From eb6c707a2570e9e1c1f48742d78e7e497b28fd93 Mon Sep 17 00:00:00 2001 From: Andres Mejia Date: Mon, 3 May 2010 23:28:13 -0400 Subject: [PATCH] Imported Upstream version 0.4.5 --- AUTHORS | 11 + BUGS | 9 + COPYING | 504 + Changelog | 711 + Clean.bat | 23 + INSTALLME | 46 + Makefile | 141 + README | 17 + TODO | 27 + applications/GPAX/GPAX.cpp | 76 + applications/GPAX/GPAX.def | 9 + applications/GPAX/GPAX.dsp | 173 + applications/GPAX/GPAX.h | 495 + applications/GPAX/GPAX.idl | 84 + applications/GPAX/GPAX.rc | 149 + applications/GPAX/GPAX.rgs | 175 + applications/GPAX/GPAXPlugin.cpp | 650 + applications/GPAX/GPAXPlugin.h | 257 + applications/GPAX/GPAX_i.c | 178 + applications/GPAX/GPAX_p.c | 602 + applications/GPAX/GPAXps.def | 11 + applications/GPAX/GPAXps.mk | 16 + applications/GPAX/StdAfx.cpp | 12 + applications/GPAX/StdAfx.h | 28 + applications/GPAX/dlldata.c | 38 + applications/GPAX/gpax.bmp | Bin 0 -> 246 bytes applications/GPAX/resource.h | 24 + applications/Makefile | 54 + applications/generators/MPEG4/MPEG4Gen.dsp | 98 + applications/generators/MPEG4/MPEG4Gen.dsw | 29 + applications/generators/MPEG4/Makefile | 59 + applications/generators/MPEG4/main.c | 1701 ++ applications/generators/MPEG4/skip.txt | 181 + applications/generators/MPEG4/templates1.txt | 1292 + applications/generators/MPEG4/templates2.txt | 592 + applications/generators/MPEG4/templates3.txt | 123 + applications/generators/MPEG4/templates4.txt | 129 + applications/generators/MPEG4/templates5.txt | 561 + applications/generators/MPEG4/templates6.txt | 242 + applications/generators/MPEG4/templates7.txt | 235 + applications/generators/Makefile | 26 + applications/generators/SVG/Makefile | 63 + applications/generators/SVG/SVGGen.dsp | 126 + applications/generators/SVG/SVGGen.dsw | 29 + .../generators/SVG/Tiny-1.2-NG/Tiny-1.2.nvdl | 35 + .../generators/SVG/Tiny-1.2-NG/Tiny-1.2.rng | 55 + .../generators/SVG/Tiny-1.2-NG/animate.rng | 334 + .../SVG/Tiny-1.2-NG/animation-element.rng | 59 + .../generators/SVG/Tiny-1.2-NG/audio.rng | 51 + .../SVG/Tiny-1.2-NG/conditional.rng | 65 + .../SVG/Tiny-1.2-NG/contenttype-attrib.rng | 22 + .../SVG/Tiny-1.2-NG/coordinate-attrib.rng | 42 + .../SVG/Tiny-1.2-NG/core-attrib.rng | 67 + .../generators/SVG/Tiny-1.2-NG/datatypes.rng | 145 + .../generators/SVG/Tiny-1.2-NG/discard.rng | 45 + .../SVG/Tiny-1.2-NG/extensibility.rng | 44 + .../SVG/Tiny-1.2-NG/extresources-attrib.rng | 22 + .../SVG/Tiny-1.2-NG/flowable-text-tiny.rng | 118 + .../SVG/Tiny-1.2-NG/focus-attrib.rng | 86 + .../generators/SVG/Tiny-1.2-NG/font-tiny.rng | 349 + .../SVG/Tiny-1.2-NG/gradient-tiny.rng | 156 + .../SVG/Tiny-1.2-NG/graphics-attrib.rng | 109 + .../generators/SVG/Tiny-1.2-NG/handler.rng | 53 + .../generators/SVG/Tiny-1.2-NG/headers.rng | 67 + .../generators/SVG/Tiny-1.2-NG/hyperlink.rng | 47 + .../generators/SVG/Tiny-1.2-NG/image.rng | 53 + .../generators/SVG/Tiny-1.2-NG/laser-ext.rng | 146 + .../SVG/Tiny-1.2-NG/media-attrib.rng | 40 + .../SVG/Tiny-1.2-NG/opacity-attrib-tiny.rng | 44 + .../SVG/Tiny-1.2-NG/paint-attrib-tiny.rng | 113 + .../generators/SVG/Tiny-1.2-NG/prefetch.rng | 60 + .../generators/SVG/Tiny-1.2-NG/script.rng | 44 + .../generators/SVG/Tiny-1.2-NG/shapes.rng | 230 + .../generators/SVG/Tiny-1.2-NG/solidcolor.rng | 65 + .../SVG/Tiny-1.2-NG/structure-tiny.rng | 257 + .../generators/SVG/Tiny-1.2-NG/text-tiny.rng | 213 + .../SVG/Tiny-1.2-NG/transform-attrib.rng | 25 + .../Tiny-1.2-NG/vectoreffects-attrib-tiny.rng | 26 + .../generators/SVG/Tiny-1.2-NG/video.rng | 80 + .../SVG/Tiny-1.2-NG/viewport-attrib-tiny.rng | 45 + .../SVG/Tiny-1.2-NG/xlink-attrib.rng | 123 + .../generators/SVG/Tiny-1.2-NG/xml-events.rng | 84 + applications/generators/SVG/html.c | 150 + applications/generators/SVG/laser.c | 266 + applications/generators/SVG/main.c | 937 + applications/generators/SVG/svggen.h | 194 + applications/generators/SVG/v1.c | 607 + applications/generators/SVG/v2.c | 462 + applications/generators/SVG/v3.c | 294 + applications/generators/X3D/Makefile | 59 + applications/generators/X3D/X3DGen.dsp | 98 + applications/generators/X3D/X3DGen.dsw | 29 + applications/generators/X3D/main.c | 1274 + applications/generators/X3D/skip.txt | 130 + applications/generators/X3D/templates_X3D.txt | 1644 ++ applications/mp42avi/Makefile | 59 + applications/mp42avi/main.c | 850 + applications/mp4box/Makefile | 73 + applications/mp4box/filedump.c | 1726 ++ applications/mp4box/fileimport.c | 1773 ++ applications/mp4box/main.c | 2739 ++ applications/mp4client/Makefile | 112 + applications/mp4client/extract.c | 676 + applications/mp4client/main.c | 1967 ++ applications/osmo4_sym/aif/osmo4_icon.bmp | Bin 0 -> 5862 bytes .../osmo4_sym/aif/osmo4_icon_mask.bmp | Bin 0 -> 414 bytes applications/osmo4_sym/aif/osmo4_menu.bmp | Bin 0 -> 3766 bytes .../osmo4_sym/aif/osmo4_menu_mask.bmp | Bin 0 -> 294 bytes applications/osmo4_sym/aif/osmo4aif.rss | 14 + applications/osmo4_sym/osmo4.cpp | 82 + applications/osmo4_sym/osmo4.h | 38 + applications/osmo4_sym/osmo4_ui.cpp | 604 + applications/osmo4_sym/osmo4_ui.h | 181 + applications/osmo4_sym/osmo4_view.cpp | 610 + applications/osmo4_sym/osmo4_view.h | 166 + applications/osmo4_sym/playlist.cpp | 527 + applications/osmo4_sym/playlist.h | 110 + applications/osmo4_sym/res/osmo4.rss | 19 + applications/osmo4_sym/res/osmo4.svg | 35 + applications/osmo4_sym/res/osmo4_caption.rss | 21 + applications/osmo4_sym/res/osmo4_gen.rss | 59 + applications/osmo4_sym/res/osmo4_reg.rss | 31 + applications/osmo4_w32/AddressBar.cpp | 182 + applications/osmo4_w32/AddressBar.h | 90 + applications/osmo4_w32/FileProps.cpp | 710 + applications/osmo4_w32/FileProps.h | 70 + applications/osmo4_w32/MainFrm.cpp | 1519 ++ applications/osmo4_w32/MainFrm.h | 223 + applications/osmo4_w32/OpenUrl.cpp | 98 + applications/osmo4_w32/OpenUrl.h | 49 + applications/osmo4_w32/Options.cpp | 1982 ++ applications/osmo4_w32/Options.h | 622 + applications/osmo4_w32/Osmo4.cpp | 1073 + applications/osmo4_w32/Osmo4.h | 114 + applications/osmo4_w32/Osmo4.rc | 998 + applications/osmo4_w32/Playlist.cpp | 941 + applications/osmo4_w32/Playlist.h | 120 + applications/osmo4_w32/Sliders.cpp | 139 + applications/osmo4_w32/Sliders.h | 54 + applications/osmo4_w32/StdAfx.cpp | 8 + applications/osmo4_w32/StdAfx.h | 26 + applications/osmo4_w32/res/Osmo4.rc2 | 13 + applications/osmo4_w32/res/error.ico | Bin 0 -> 766 bytes applications/osmo4_w32/res/maintool.bmp | Bin 0 -> 1438 bytes applications/osmo4_w32/res/message.ico | Bin 0 -> 766 bytes applications/osmo4_w32/res/osmo4.ico | Bin 0 -> 15086 bytes applications/osmo4_w32/res/pause.ico | Bin 0 -> 1078 bytes applications/osmo4_w32/res/play.ico | Bin 0 -> 1078 bytes applications/osmo4_w32/res/playlist.bmp | Bin 0 -> 958 bytes applications/osmo4_w32/res/stop.ico | Bin 0 -> 1078 bytes applications/osmo4_w32/resource.h | 322 + applications/osmo4_wce/MainFrm.cpp | 636 + applications/osmo4_wce/MainFrm.h | 151 + applications/osmo4_wce/OpenDlg.cpp | 92 + applications/osmo4_wce/OpenDlg.h | 47 + applications/osmo4_wce/Options.cpp | 1230 + applications/osmo4_wce/Options.h | 388 + applications/osmo4_wce/Osmo4.cpp | 699 + applications/osmo4_wce/Osmo4.h | 111 + applications/osmo4_wce/Osmo4.rc | 756 + applications/osmo4_wce/ProgressBar.cpp | 133 + applications/osmo4_wce/ProgressBar.h | 53 + applications/osmo4_wce/Resource.h | 170 + applications/osmo4_wce/StdAfx.cpp | 6 + applications/osmo4_wce/StdAfx.h | 40 + applications/osmo4_wce/newres.h | 28 + applications/osmo4_wce/res/Cmdbar.bmp | Bin 0 -> 886 bytes applications/osmo4_wce/res/Osmo4.ico | Bin 0 -> 1078 bytes applications/osmo4_wce/res/Osmo4.rc2 | 13 + applications/osmo4_wx/Darwin.Info.plist | 32 + .../osmo4_wx/Darwin.InfoPlist.strings | Bin 0 -> 456 bytes applications/osmo4_wx/Darwin.Osmo.icns | Bin 0 -> 38570 bytes applications/osmo4_wx/Makefile | 91 + applications/osmo4_wx/Playlist.cpp | 826 + applications/osmo4_wx/Playlist.h | 134 + applications/osmo4_wx/fileprops.cpp | 607 + applications/osmo4_wx/fileprops.h | 83 + applications/osmo4_wx/menubtn.cpp | 863 + applications/osmo4_wx/menubtn.h | 314 + applications/osmo4_wx/osmo4.ico | Bin 0 -> 15086 bytes applications/osmo4_wx/osmo4.xpm | 301 + applications/osmo4_wx/playlist.xpm | 158 + applications/osmo4_wx/resource.h | 16 + applications/osmo4_wx/toolbar.xpm | 254 + applications/osmo4_wx/wxGPACControl.cpp | 1031 + applications/osmo4_wx/wxGPACControl.h | 137 + applications/osmo4_wx/wxOsmo4.cpp | 2518 ++ applications/osmo4_wx/wxOsmo4.h | 389 + applications/osmo4_wx/wxOsmo4.rc | 72 + applications/osmophone/Osmo4.ico | Bin 0 -> 1078 bytes applications/osmophone/main.cpp | 1230 + applications/osmophone/newres.h | 41 + applications/osmophone/openfile.cpp | 399 + applications/osmophone/osmophone.rc | 411 + applications/osmophone/resource.h | 123 + applications/osmozilla/Makefile | 112 + applications/osmozilla/np_entry.cpp | 305 + applications/osmozilla/npn_gate.cpp | 215 + applications/osmozilla/npp_gate.cpp | 353 + applications/osmozilla/npplat.h | 151 + applications/osmozilla/nsIOsmozilla.h | 121 + applications/osmozilla/nsIOsmozilla.idl | 9 + applications/osmozilla/nsIOsmozilla.xpt_linux | Bin 0 -> 180 bytes applications/osmozilla/nsIOsmozilla.xpt_w32 | Bin 0 -> 180 bytes applications/osmozilla/osmozilla.cpp | 877 + applications/osmozilla/osmozilla.def | 6 + applications/osmozilla/osmozilla.h | 241 + applications/osmozilla/osmozilla.png | Bin 0 -> 121239 bytes applications/osmozilla/osmozilla.rc | 124 + applications/osmozilla/readme.txt | 8 + applications/osmozilla/resource.h | 21 + .../standalone2drender/standalone2drender.c | 172 + .../standalone2drender/standalone2drender.h | 16 + .../testapps/beng_test/BifsEngineTester.dsp | 103 + applications/testapps/beng_test/CmdLineTst.c | 89 + .../testapps/beng_test/rect-update.bt | 18 + applications/testapps/beng_test/rect.bt | 42 + applications/testapps/broadcaster/Makefile | 37 + .../testapps/broadcaster/RTP_serv_generator.c | 229 + .../testapps/broadcaster/RTP_serv_generator.h | 60 + .../broadcaster/RTP_serv_packetizer.c | 147 + .../broadcaster/RTP_serv_packetizer.h | 15 + .../testapps/broadcaster/RTP_serv_sender.c | 66 + .../testapps/broadcaster/RTP_serv_sender.h | 14 + .../testapps/broadcaster/broadcaster.c | 488 + .../testapps/broadcaster/broadcaster.dsp | 138 + .../testapps/broadcaster/broadcaster.h | 84 + .../broadcaster/broadcaster_config.cfg | 14 + applications/testapps/broadcaster/france.mp4 | Bin 0 -> 9131 bytes .../testapps/broadcaster/meteo_local.xmt | 392 + .../testapps/broadcaster/sdp_generator.c | 71 + .../testapps/broadcaster/sdp_generator.h | 29 + applications/testapps/dmbrs/dmbrs.dsp | 90 + applications/testapps/dmbrs/main.c | 250 + applications/testapps/largefile/largefile.dsp | 102 + applications/testapps/largefile/largefile.dsw | 44 + applications/testapps/largefile/main.c | 85 + .../testapps/loadcompare/LoadCompare.dsp | 112 + applications/testapps/loadcompare/Makefile | 66 + .../testapps/loadcompare/loadcompare.c | 697 + applications/testapps/mp42ts/Makefile | 64 + applications/testapps/mp42ts/main.c | 1870 ++ applications/testapps/mp42ts/mp42ts.c | 823 + applications/testapps/mp42ts/mp42ts.dsp | 99 + applications/testapps/mp42ts/mp42ts.h | 179 + applications/testapps/mp4_streamer/Makefile | 64 + .../testapps/mp4_streamer/configuration.cfg | 31 + applications/testapps/mp4_streamer/main.c | 1175 + .../testapps/mp4_streamer/mp4_streamer.dsp | 102 + applications/testapps/mpedemux/Makefile | 76 + applications/testapps/mpedemux/main.c | 105 + applications/testapps/mpedemux/mpedemux.dsp | 90 + applications/testapps/mpeg2ts/main.c | 88 + applications/testapps/mpeg2ts/mpeg2ts.dsp | 100 + applications/testapps/svg2bifs/main.c | 1046 + applications/testapps/svg2bifs/svg2bifs.dsp | 90 + applications/v4studio/V4CommandPanel.cpp | 601 + applications/v4studio/V4CommandPanel.h | 103 + applications/v4studio/V4FieldList.cpp | 452 + applications/v4studio/V4FieldList.h | 47 + applications/v4studio/V4Node.cpp | 57 + applications/v4studio/V4Node.h | 61 + applications/v4studio/V4NodePool.cpp | 76 + applications/v4studio/V4NodePool.h | 57 + applications/v4studio/V4NodePools.cpp | 121 + applications/v4studio/V4NodePools.h | 59 + applications/v4studio/V4SceneGraph.cpp | 304 + applications/v4studio/V4SceneGraph.h | 95 + applications/v4studio/V4SceneManager.cpp | 598 + applications/v4studio/V4SceneManager.h | 148 + applications/v4studio/V4Service.cpp | 205 + applications/v4studio/V4Service.h | 42 + applications/v4studio/V4StudioApp.cpp | 23 + applications/v4studio/V4StudioApp.h | 15 + applications/v4studio/V4StudioFrame.cpp | 951 + applications/v4studio/V4StudioFrame.h | 211 + applications/v4studio/V4StudioTree.cpp | 386 + applications/v4studio/V4StudioTree.h | 86 + .../v4studio/V4TimeLine/V4TimeLine.cpp | 159 + applications/v4studio/V4TimeLine/V4TimeLine.h | 84 + .../v4studio/V4TimeLine/V4TimeLineCase.cpp | 186 + .../v4studio/V4TimeLine/V4TimeLineCase.h | 68 + .../v4studio/V4TimeLine/V4TimeLineElt.cpp | 74 + .../v4studio/V4TimeLine/V4TimeLineElt.h | 48 + .../v4studio/V4TimeLine/V4TimeLineHdr.cpp | 37 + .../v4studio/V4TimeLine/V4TimeLineHdr.h | 36 + .../v4studio/V4TimeLine/V4TimeLineLine.cpp | 92 + .../v4studio/V4TimeLine/V4TimeLineLine.h | 68 + applications/v4studio/install.txt | 110 + applications/v4studio/rc/V4Studio.aps | Bin 0 -> 60860 bytes applications/v4studio/rc/V4Studio.rc | 1 + applications/v4studio/rc/appearance.bmp | Bin 0 -> 822 bytes applications/v4studio/rc/circle.bmp | Bin 0 -> 1334 bytes applications/v4studio/rc/close.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/colortransform.bmp | Bin 0 -> 310 bytes applications/v4studio/rc/copy.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/cut.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/delete.bmp | Bin 0 -> 774 bytes applications/v4studio/rc/fs.bmp | Bin 0 -> 298 bytes applications/v4studio/rc/group.bmp | Bin 0 -> 310 bytes applications/v4studio/rc/ifs2d.bmp | Bin 0 -> 1334 bytes applications/v4studio/rc/ils2d.bmp | Bin 0 -> 1334 bytes applications/v4studio/rc/image.bmp | Bin 0 -> 822 bytes applications/v4studio/rc/layer2d.bmp | Bin 0 -> 310 bytes applications/v4studio/rc/lg.bmp | Bin 0 -> 774 bytes applications/v4studio/rc/lineproperties.bmp | Bin 0 -> 1378 bytes applications/v4studio/rc/material2d.bmp | Bin 0 -> 1378 bytes applications/v4studio/rc/movie.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/new.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/open.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/orderedgroup.bmp | Bin 0 -> 310 bytes applications/v4studio/rc/paste.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/paste_use.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/preview.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/print.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/rect.bmp | Bin 0 -> 1334 bytes applications/v4studio/rc/redo.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/resource_orig.xrc | 75 + applications/v4studio/rc/rg.bmp | Bin 0 -> 774 bytes applications/v4studio/rc/save.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/shape.bmp | Bin 0 -> 310 bytes applications/v4studio/rc/sound.bmp | Bin 0 -> 774 bytes applications/v4studio/rc/t2d.bmp | Bin 0 -> 310 bytes applications/v4studio/rc/text.bmp | Bin 0 -> 298 bytes applications/v4studio/rc/tm2d.bmp | Bin 0 -> 310 bytes applications/v4studio/rc/undo.bmp | Bin 0 -> 238 bytes applications/v4studio/rc/v4.bmp | Bin 0 -> 1334 bytes applications/v4studio/rc/v4.ico | Bin 0 -> 766 bytes applications/v4studio/rc/xlineproperties.bmp | Bin 0 -> 1378 bytes applications/v4studio/safe_include.h | 7 + applications/v4studio/treeIcon1.xpm | 41 + applications/v4studio/treeIcon2.xpm | 42 + applications/v4studio/treeIcon3.xpm | 44 + applications/v4studio/treeIcon4.xpm | 44 + applications/v4studio/treeIcon5.xpm | 43 + applications/v4studio/wxGPACPanel.cpp | 371 + applications/v4studio/wxGPACPanel.h | 46 + bin/arm_ppc02_rel/install/do.bat | 4 + bin/arm_ppc02_rel/install/gpac.inf | 134 + bin/arm_ppc02_rel/install/gpac.ini | 10 + bin/arm_ppc02_rel/install/readme.txt | 6 + bin/arm_ppc03_rel/install/do.bat | 4 + bin/arm_ppc03_rel/install/gpac.inf | 141 + bin/arm_ppc03_rel/install/gpac.ini | 10 + bin/arm_ppc03_rel/install/readme.txt | 6 + bin/gcc/libgpac.so | Bin 0 -> 4376700 bytes bin/w32_rel/Osmo4.ico | Bin 0 -> 15086 bytes bin/w32_rel/nsis_install/gpac_installer.nsi | 606 + build/msevc3/GPAC.VCW | 401 + build/msevc3/Osmo4.vcp | 281 + build/msevc3/aac_in.vcp | 140 + build/msevc3/amr_dec.vcp | 2411 ++ build/msevc3/bifs_dec.vcp | 133 + build/msevc3/ctx_load.vcp | 135 + build/msevc3/dummy_in.vcp | 122 + build/msevc3/ft_font.vcp | 129 + build/msevc3/gapi.vcp | 134 + build/msevc3/img_in.vcp | 214 + build/msevc3/isom_in.vcp | 190 + build/msevc3/laser_dec.vcp | 147 + build/msevc3/libgpac.vcp | 3862 +++ build/msevc3/libgpac_dll.vcp | 104 + build/msevc3/mp3_in.vcp | 137 + build/msevc3/odf_dec.vcp | 132 + build/msevc3/ogg.vcp | 198 + build/msevc3/rtp_in.vcp | 258 + build/msevc3/saf_in.vcp | 135 + build/msevc3/soft_raster.vcp | 218 + build/msevc3/svg_in.vcp | 145 + build/msevc3/timedtext.vcp | 155 + build/msevc3/wav_out.vcp | 118 + build/msevc3/xvid_dec.vcp | 462 + build/msevc4/GPAC.VCW | 434 + build/msevc4/GPAX.VCP | 188 + build/msevc4/Osmo4.vcp | 286 + build/msevc4/aac_in.vcp | 129 + build/msevc4/amr_dec.vcp | 2305 ++ build/msevc4/bifs_dec.vcp | 135 + build/msevc4/ctx_load.vcp | 137 + build/msevc4/dummy_in.vcp | 124 + build/msevc4/ffmpeg_in.vcp | 228 + build/msevc4/ft_font.vcp | 135 + build/msevc4/gapi.vcp | 140 + build/msevc4/img_in.vcp | 234 + build/msevc4/ismacryp.vcp | 150 + build/msevc4/isom_in.vcp | 194 + build/msevc4/laser_dec.vcp | 151 + build/msevc4/libgpac.vcp | 7112 ++++++ build/msevc4/libgpac_dll.vcp | 106 + build/msevc4/mp3_in.vcp | 138 + build/msevc4/odf_dec.vcp | 133 + build/msevc4/osmophone.vcp | 183 + build/msevc4/rtp_in.vcp | 254 + build/msevc4/saf_in.vcp | 136 + build/msevc4/soft_rast.vcp | 234 + build/msevc4/svg_in.vcp | 149 + build/msevc4/timedtext.vcp | 158 + build/msevc4/wav_out.vcp | 119 + build/msevc4/xvid_dec.vcp | 462 + build/msvc6/GPAX.dsp | 172 + build/msvc6/Osmo4.dsp | 217 + build/msvc6/V4Studio.dsp | 505 + build/msvc6/aac_in.dsp | 105 + build/msvc6/ac3_in.dsp | 103 + build/msvc6/amr_dec.dsp | 982 + build/msvc6/amr_float_dec.dsp | 304 + build/msvc6/bifs_dec.dsp | 99 + build/msvc6/ctx_load.dsp | 99 + build/msvc6/dummy_in.dsp | 99 + build/msvc6/dx_hw.dsp | 143 + build/msvc6/ffmpeg_in.dsp | 113 + build/msvc6/ft_font.dsp | 105 + build/msvc6/gdip_raster.dsp | 115 + build/msvc6/gpac.dsw | 695 + build/msvc6/gpac_js.dsp | 113 + build/msvc6/img_in.dsp | 125 + build/msvc6/ismacryp.dsp | 100 + build/msvc6/isom_in.dsp | 116 + build/msvc6/laser_dec.dsp | 99 + build/msvc6/libgpac.dsp | 1404 ++ build/msvc6/libgpac_dll.dsp | 97 + build/msvc6/mp3_in.dsp | 103 + build/msvc6/mp42avi.dsp | 91 + build/msvc6/mp4box.dsp | 104 + build/msvc6/mp4client.dsp | 96 + build/msvc6/mpegts_in.dsp | 112 + build/msvc6/odf_dec.dsp | 99 + build/msvc6/ogg.dsp | 115 + build/msvc6/osmozilla.dsp | 150 + build/msvc6/raw_out.dsp | 99 + build/msvc6/rtp_in.dsp | 123 + build/msvc6/saf_in.dsp | 103 + build/msvc6/sdl_out.dsp | 116 + build/msvc6/soft_raster.dsp | 136 + build/msvc6/standalone2drender.dsp | 267 + build/msvc6/svg_in.dsp | 111 + build/msvc6/timedtext.dsp | 103 + build/msvc6/wav_out.dsp | 99 + build/msvc6/wxOsmo4.dsp | 156 + build/msvc6/xvid_dec.dsp | 101 + build/msvc8/GPAX.vcproj | 767 + build/msvc8/Osmo4.vcproj | 1012 + build/msvc8/V4Studio.vcproj | 922 + build/msvc8/aac_in.vcproj | 623 + build/msvc8/ac3_in.vcproj | 268 + build/msvc8/amr_dec.vcproj | 5447 ++++ build/msvc8/amr_float_dec.vcproj | 1565 ++ build/msvc8/bifs_dec.vcproj | 569 + build/msvc8/ctx_load.vcproj | 569 + build/msvc8/dummy_in.vcproj | 569 + build/msvc8/dx_hw.vcproj | 801 + build/msvc8/ffmpeg_in.vcproj | 661 + build/msvc8/ft_font.vcproj | 547 + build/msvc8/gdip_raster.vcproj | 699 + build/msvc8/gpac.sln | 346 + build/msvc8/img_in.vcproj | 745 + build/msvc8/ismacryp.vcproj | 569 + build/msvc8/isom_in.vcproj | 695 + build/msvc8/laser_dec.vcproj | 569 + build/msvc8/libgpac.vcproj | 7219 ++++++ build/msvc8/libgpac_dll.vcproj | 539 + build/msvc8/mp3_in.vcproj | 617 + build/msvc8/mp4box.vcproj | 639 + build/msvc8/mp4client.vcproj | 595 + build/msvc8/mpegts_in.vcproj | 584 + build/msvc8/odf_dec.vcproj | 571 + build/msvc8/ogg.vcproj | 701 + build/msvc8/osmozilla.vcproj | 766 + build/msvc8/raw_out.vcproj | 569 + build/msvc8/rtp_in.vcproj | 773 + build/msvc8/saf_in.vcproj | 574 + build/msvc8/sdl_out.vcproj | 700 + build/msvc8/soft_raster.vcproj | 813 + build/msvc8/svg_in.vcproj | 592 + build/msvc8/timedtext.vcproj | 609 + build/msvc8/wav_out.vcproj | 573 + build/msvc8/wxOsmo4.vcproj | 388 + build/msvc8/xvid_dec.vcproj | 577 + build/symbian/Icons.mk | 44 + build/symbian/aac_in.mmp | 15 + build/symbian/amr_in.mmp | 11 + build/symbian/bifs_dec.mmp | 9 + build/symbian/bld.inf | 35 + build/symbian/ctx_load.mmp | 10 + build/symbian/dummy_in.mmp | 9 + build/symbian/epoc_hw.mmp | 25 + build/symbian/ffmpeg_in.mmp | 15 + build/symbian/ft_font.mmp | 18 + build/symbian/gpac_module_symbianU.def | 5 + build/symbian/img_in.mmp | 29 + build/symbian/ismacryp.mmp | 8 + build/symbian/isom_in.mmp | 12 + build/symbian/laser_dec.mmp | 8 + build/symbian/libgpac.mmp | 318 + build/symbian/libgpac_symbianU.def | 903 + build/symbian/module_base.mmp | 24 + build/symbian/mp3_in.mmp | 22 + build/symbian/odf_dec.mmp | 9 + build/symbian/osmo4.mmp | 90 + build/symbian/rtp_in.mmp | 14 + build/symbian/saf_in.mmp | 9 + build/symbian/sis/GPAC.cfg | 26 + build/symbian/sis/backup_registration.xml | 5 + build/symbian/sis/gpac.cer | 18 + build/symbian/sis/gpac.key | 15 + build/symbian/sis/osmo4_gcce.pkg | 54 + build/symbian/sis/osmo4_thumb.pkg | 48 + build/symbian/soft_raster.mmp | 15 + build/symbian/svg_in.mmp | 10 + build/symbian/symbian_dll_entry.cpp | 7 + build/symbian/timedtext.mmp | 9 + build/symbian/xvid_dec.mmp | 34 + config.h | 9 + configure | 1810 ++ doc/CODING_STYLE | 136 + doc/INSTALL.gcc | 116 + doc/INSTALL.gpe | 100 + doc/INSTALL.symbian | 82 + doc/INSTALL.w32 | 216 + doc/INSTALL.wCE | 133 + doc/ISO 639-2 codes.txt | 484 + doc/SceneGenerators | 148 + doc/configuration.html | 650 + doc/doxyfile | 258 + doc/gpac.mp4 | Bin 0 -> 2688 bytes doc/ipmpx_syntax.bt | 237 + doc/man/gpac.1 | 447 + doc/man/mp42avi.1 | 69 + doc/man/mp4box.1 | 530 + doc/man/mp4client.1 | 243 + extra_lib/include/a52dec/a52.h | 62 + extra_lib/include/a52dec/mm_accel.h | 37 + extra_lib/include/faad/faad.h | 35 + extra_lib/include/faad/neaacdec.h | 255 + extra_lib/include/ffmpeg/avcodec.h | 2714 ++ extra_lib/include/ffmpeg/avformat.h | 549 + extra_lib/include/ffmpeg/avio.h | 212 + extra_lib/include/ffmpeg/avutil.h | 137 + extra_lib/include/ffmpeg/common.h | 408 + extra_lib/include/ffmpeg/integer.h | 49 + extra_lib/include/ffmpeg/intfloat_readwrite.h | 39 + extra_lib/include/ffmpeg/log.h | 102 + extra_lib/include/ffmpeg/mathematics.h | 51 + extra_lib/include/ffmpeg/rational.h | 79 + .../freetype/freetype/cache/ftccache.h | 304 + .../include/freetype/freetype/cache/ftccmap.h | 216 + .../freetype/freetype/cache/ftcglyph.h | 191 + .../freetype/freetype/cache/ftcimage.h | 313 + .../freetype/freetype/cache/ftcmanag.h | 244 + .../freetype/freetype/cache/ftcsbits.h | 275 + .../include/freetype/freetype/cache/ftlru.h | 208 + .../freetype/freetype/config/ftconfig.h | 340 + .../freetype/freetype/config/ftheader.h | 537 + .../freetype/freetype/config/ftmodule.h | 19 + .../freetype/freetype/config/ftoption.h | 530 + .../freetype/freetype/config/ftstdlib.h | 147 + .../include/freetype/freetype/freetype.h | 2986 +++ extra_lib/include/freetype/freetype/ftbbox.h | 89 + extra_lib/include/freetype/freetype/ftbdf.h | 200 + extra_lib/include/freetype/freetype/ftcache.h | 414 + .../include/freetype/freetype/ftchapters.h | 69 + .../include/freetype/freetype/fterrdef.h | 229 + .../include/freetype/freetype/fterrors.h | 207 + extra_lib/include/freetype/freetype/ftglyph.h | 566 + extra_lib/include/freetype/freetype/ftgzip.h | 100 + extra_lib/include/freetype/freetype/ftimage.h | 1236 + .../include/freetype/freetype/ftincrem.h | 292 + extra_lib/include/freetype/freetype/ftlist.h | 274 + extra_lib/include/freetype/freetype/ftmac.h | 128 + extra_lib/include/freetype/freetype/ftmm.h | 188 + .../include/freetype/freetype/ftmoderr.h | 153 + .../include/freetype/freetype/ftmodule.h | 314 + extra_lib/include/freetype/freetype/ftoutln.h | 472 + extra_lib/include/freetype/freetype/ftpfr.h | 172 + .../include/freetype/freetype/ftrender.h | 229 + extra_lib/include/freetype/freetype/ftsizes.h | 159 + .../include/freetype/freetype/ftsnames.h | 167 + .../include/freetype/freetype/ftstroker.h | 139 + extra_lib/include/freetype/freetype/ftsynth.h | 71 + extra_lib/include/freetype/freetype/ftsysio.h | 195 + .../include/freetype/freetype/ftsysmem.h | 202 + .../include/freetype/freetype/ftsystem.h | 309 + .../include/freetype/freetype/fttrigon.h | 315 + extra_lib/include/freetype/freetype/fttypes.h | 558 + .../include/freetype/freetype/ftwinfnt.h | 135 + extra_lib/include/freetype/freetype/ftxf86.h | 60 + .../freetype/freetype/internal/autohint.h | 205 + .../freetype/freetype/internal/bdftypes.h | 58 + .../freetype/freetype/internal/cfftypes.h | 256 + .../freetype/freetype/internal/fnttypes.h | 104 + .../freetype/freetype/internal/ftcalc.h | 77 + .../freetype/freetype/internal/ftcore.h | 185 + .../freetype/freetype/internal/ftdebug.h | 196 + .../freetype/freetype/internal/ftdriver.h | 202 + .../freetype/freetype/internal/ftexcept.h | 79 + .../freetype/freetype/internal/ftgloadr.h | 147 + .../freetype/freetype/internal/fthash.h | 502 + .../freetype/freetype/internal/ftmemory.h | 293 + .../freetype/freetype/internal/ftobject.h | 533 + .../freetype/freetype/internal/ftobjs.h | 845 + .../freetype/freetype/internal/ftserv.h | 259 + .../freetype/freetype/internal/ftstream.h | 498 + .../freetype/freetype/internal/fttrace.h | 106 + .../freetype/freetype/internal/internal.h | 48 + .../freetype/freetype/internal/pcftypes.h | 56 + .../include/freetype/freetype/internal/pfr.h | 60 + .../freetype/freetype/internal/psaux.h | 733 + .../freetype/freetype/internal/pshints.h | 626 + .../freetype/freetype/internal/psnames.h | 241 + .../freetype/internal/services/svbdf.h | 57 + .../freetype/internal/services/svgldict.h | 60 + .../freetype/internal/services/svmm.h | 68 + .../freetype/internal/services/svpfr.h | 65 + .../freetype/internal/services/svpostnm.h | 58 + .../freetype/internal/services/svpscmap.h | 113 + .../freetype/internal/services/svpsinfo.h | 55 + .../freetype/internal/services/svsfnt.h | 69 + .../freetype/internal/services/svwinfnt.h | 50 + .../freetype/internal/services/svxf86nm.h | 55 + .../include/freetype/freetype/internal/sfnt.h | 529 + .../freetype/freetype/internal/t1types.h | 200 + .../freetype/freetype/internal/t42types.h | 55 + .../freetype/freetype/internal/tttypes.h | 1678 ++ .../include/freetype/freetype/t1tables.h | 397 + .../include/freetype/freetype/ttnameid.h | 1075 + .../include/freetype/freetype/tttables.h | 674 + extra_lib/include/freetype/freetype/tttags.h | 80 + extra_lib/include/freetype/ft2build.h | 39 + extra_lib/include/jpeg/jconfig.h | 60 + extra_lib/include/jpeg/jmorecfg.h | 368 + extra_lib/include/jpeg/jpeglib.h | 1096 + extra_lib/include/js/jsapi.h | 2220 ++ extra_lib/include/js/jsautocfg.h | 50 + extra_lib/include/js/jscompat.h | 57 + extra_lib/include/js/jscpucfg.h | 262 + extra_lib/include/js/jsdbgapi.h | 406 + extra_lib/include/js/jslong.h | 437 + extra_lib/include/js/jsosdep.h | 115 + extra_lib/include/js/jsotypes.h | 202 + extra_lib/include/js/jsproto.tbl | 116 + extra_lib/include/js/jspubtd.h | 667 + extra_lib/include/js/jstypes.h | 464 + extra_lib/include/js/jsxdrapi.h | 223 + extra_lib/include/mad/mad.h | 952 + extra_lib/include/ogg/ogg.h | 202 + extra_lib/include/ogg/os_types.h | 127 + extra_lib/include/openjpeg/openjpeg.h | 911 + extra_lib/include/png/png.h | 3597 +++ extra_lib/include/png/pngconf.h | 1487 ++ extra_lib/include/theora/theora.h | 794 + extra_lib/include/vorbis/codec.h | 241 + extra_lib/include/vorbis/vorbisenc.h | 112 + extra_lib/include/vorbis/vorbisfile.h | 183 + extra_lib/include/xvid/xvid.h | 786 + extra_lib/include/zlib/zconf.h | 332 + extra_lib/include/zlib/zlib.h | 1357 + extra_lib/lib/arm_ppc02_deb/dummy | 0 extra_lib/lib/arm_ppc02_rel/dummy | 0 extra_lib/lib/arm_ppc03_deb/dummy | 0 extra_lib/lib/arm_ppc03_rel/dummy | 0 extra_lib/lib/gcc/dummy | 0 extra_lib/lib/w32_deb/dummy | 0 extra_lib/lib/w32_rel/dummy | 0 gpac.spec | 101 + include/gpac/avparse.h | 208 + include/gpac/base_coding.h | 105 + include/gpac/bifs.h | 95 + include/gpac/bifsengine.h | 138 + include/gpac/bitstream.h | 450 + include/gpac/color.h | 239 + include/gpac/compositor.h | 164 + include/gpac/config_file.h | 165 + include/gpac/constants.h | 403 + include/gpac/crypt.h | 190 + include/gpac/download.h | 277 + include/gpac/esi.h | 183 + include/gpac/events.h | 689 + include/gpac/ietf.h | 1319 + include/gpac/internal/avilib.h | 434 + include/gpac/internal/bifs_dev.h | 223 + include/gpac/internal/bifs_tables.h | 661 + include/gpac/internal/camera.h | 184 + include/gpac/internal/compositor_dev.h | 1155 + include/gpac/internal/config_static.h | 89 + include/gpac/internal/crypt_dev.h | 158 + include/gpac/internal/ietf_dev.h | 327 + include/gpac/internal/isomedia_dev.h | 3140 +++ include/gpac/internal/laser_dev.h | 337 + include/gpac/internal/media_dev.h | 148 + include/gpac/internal/mesh.h | 285 + include/gpac/internal/odf_dev.h | 364 + include/gpac/internal/ogg.h | 209 + include/gpac/internal/scenegraph_dev.h | 1033 + include/gpac/internal/swf_dev.h | 361 + include/gpac/internal/terminal_dev.h | 851 + include/gpac/internal/vobsub.h | 82 + include/gpac/ismacryp.h | 116 + include/gpac/iso639.h | 523 + include/gpac/isomedia.h | 1852 ++ include/gpac/laser.h | 88 + include/gpac/list.h | 168 + include/gpac/math.h | 1048 + include/gpac/media_tools.h | 335 + include/gpac/mediaobject.h | 149 + include/gpac/module.h | 235 + include/gpac/modules/audio_out.h | 133 + include/gpac/modules/codec.h | 231 + include/gpac/modules/font.h | 126 + include/gpac/modules/ipmp.h | 131 + include/gpac/modules/js_usr.h | 66 + include/gpac/modules/raster2d.h | 258 + include/gpac/modules/service.h | 467 + include/gpac/modules/term_ext.h | 84 + include/gpac/modules/video_out.h | 188 + include/gpac/mpeg4_odf.h | 1697 ++ include/gpac/mpegts.h | 681 + include/gpac/network.h | 403 + include/gpac/nodes_mpeg4.h | 1916 ++ include/gpac/nodes_svg.h | 491 + include/gpac/nodes_x3d.h | 1396 ++ include/gpac/nodes_xbl.h | 70 + include/gpac/options.h | 291 + include/gpac/path2d.h | 611 + include/gpac/scene_manager.h | 429 + include/gpac/scenegraph.h | 758 + include/gpac/scenegraph_svg.h | 620 + include/gpac/scenegraph_vrml.h | 623 + include/gpac/setup.h | 377 + include/gpac/svg_types.h | 887 + include/gpac/sync_layer.h | 125 + include/gpac/term_info.h | 158 + include/gpac/terminal.h | 180 + include/gpac/thread.h | 271 + include/gpac/token.h | 106 + include/gpac/tools.h | 655 + include/gpac/user.h | 111 + include/gpac/utf.h | 99 + include/gpac/xml.h | 158 + modules/Makefile | 93 + modules/aac_in/Makefile | 76 + modules/aac_in/aac_in.c | 761 + modules/aac_in/aac_in.def | 6 + modules/aac_in/faad_dec.c | 367 + modules/ac3_in/Makefile | 76 + modules/ac3_in/ac3_in.c | 664 + modules/ac3_in/ac3_in.def | 6 + modules/ac3_in/liba52_dec.c | 339 + modules/alsa/Makefile | 60 + modules/alsa/alsa.c | 371 + modules/amr_dec/Makefile | 88 + modules/amr_dec/amr_dec.c | 305 + modules/amr_dec/amr_dec.def | 6 + modules/amr_dec/amr_in.c | 573 + modules/amr_dec/amr_nb/typedefs.h | 195 + modules/amr_float_dec/Makefile | 79 + modules/amr_float_dec/amr_float_dec.c | 321 + modules/amr_float_dec/amr_float_dec.def | 6 + modules/amr_float_dec/amr_nb_ft/dummy | 0 modules/amr_float_dec/amr_wb_ft/dummy | 0 modules/bifs_dec/Makefile | 62 + modules/bifs_dec/bifs_dec.c | 187 + modules/bifs_dec/bifs_dec.def | 6 + modules/ctx_load/Makefile | 63 + modules/ctx_load/ctx_load.c | 748 + modules/ctx_load/ctx_load.def | 6 + modules/dummy_in/Makefile | 62 + modules/dummy_in/dummy_in.c | 445 + modules/dummy_in/dummy_in.def | 6 + modules/dx_hw/Makefile | 69 + modules/dx_hw/collide.cur | Bin 0 -> 2238 bytes modules/dx_hw/copy_pixels.c | 409 + modules/dx_hw/dx_2d.c | 796 + modules/dx_hw/dx_audio.c | 456 + modules/dx_hw/dx_hw.def | 6 + modules/dx_hw/dx_hw.h | 173 + modules/dx_hw/dx_hw.rc | 65 + modules/dx_hw/dx_video.c | 491 + modules/dx_hw/dx_window.c | 862 + modules/dx_hw/hand.cur | Bin 0 -> 2238 bytes modules/dx_hw/resource.h | 21 + modules/epoc_hw/epoc_aout.cpp | 361 + modules/epoc_hw/epoc_codec.cpp | 425 + modules/epoc_hw/epoc_vout.cpp | 525 + modules/ffmpeg_in/Makefile | 81 + modules/ffmpeg_in/ffmpeg_decode.c | 776 + modules/ffmpeg_in/ffmpeg_demux.c | 869 + modules/ffmpeg_in/ffmpeg_in.def | 6 + modules/ffmpeg_in/ffmpeg_in.h | 178 + modules/ffmpeg_in/ffmpeg_load.c | 55 + modules/ft_font/Makefile | 66 + modules/ft_font/ft_font.c | 595 + modules/ft_font/ft_font.def | 6 + modules/ft_font/ft_font.h | 29 + modules/gapi/gapi.cpp | 1201 + modules/gapi/gapi.def | 6 + modules/gapi/gapi.h | 102 + modules/gdip_raster/gdip_font.cpp | 393 + modules/gdip_raster/gdip_grad.cpp | 406 + modules/gdip_raster/gdip_priv.h | 213 + modules/gdip_raster/gdip_rend.cpp | 499 + modules/gdip_raster/gdip_rend.def | 6 + modules/gdip_raster/gdip_texture.cpp | 483 + modules/gpac_js/Makefile | 75 + modules/gpac_js/gpac_js.c | 353 + modules/gpac_js/gpac_js.def | 6 + modules/img_in/Makefile | 88 + modules/img_in/bmp_dec.c | 206 + modules/img_in/img_dec.c | 136 + modules/img_in/img_in.c | 364 + modules/img_in/img_in.def | 6 + modules/img_in/img_in.h | 106 + modules/img_in/jp2_dec.c | 373 + modules/img_in/jpeg_dec.c | 144 + modules/img_in/png_dec.c | 160 + modules/ismacryp/Makefile | 62 + modules/ismacryp/ismacryp.c | 353 + modules/ismacryp/ismacryp.def | 6 + modules/isom_in/Makefile | 62 + modules/isom_in/cache.c | 267 + modules/isom_in/isom_in.def | 6 + modules/isom_in/isom_in.h | 116 + modules/isom_in/load.c | 177 + modules/isom_in/read.c | 836 + modules/isom_in/read_ch.c | 210 + modules/jack/Makefile | 61 + modules/jack/jack.c | 572 + modules/laser_dec/Makefile | 62 + modules/laser_dec/laser_dec.c | 196 + modules/laser_dec/laser_dec.def | 6 + modules/mp3_in/Makefile | 77 + modules/mp3_in/mad_dec.c | 348 + modules/mp3_in/mp3_in.c | 686 + modules/mp3_in/mp3_in.def | 6 + modules/mpegts_in/Makefile | 67 + modules/mpegts_in/mpegts_in.c | 1312 + modules/mpegts_in/mpegts_in.def | 6 + modules/odf_dec/Makefile | 62 + modules/odf_dec/odf_dec.c | 311 + modules/odf_dec/odf_dec.def | 6 + modules/ogg/Makefile | 100 + modules/ogg/ogg.def | 6 + modules/ogg/ogg_in.c | 972 + modules/ogg/ogg_in.h | 63 + modules/ogg/ogg_load.c | 118 + modules/ogg/theora_dec.c | 234 + modules/ogg/vorbis_dec.c | 269 + modules/oss_audio/Makefile | 69 + modules/oss_audio/oss.c | 273 + modules/pulseaudio/Makefile | 60 + modules/pulseaudio/pulseaudio.c | 328 + modules/raw_out/Makefile | 62 + modules/raw_out/raw_out.def | 6 + modules/raw_out/raw_video.c | 157 + modules/rtp_in/Makefile | 63 + modules/rtp_in/rtp_in.c | 751 + modules/rtp_in/rtp_in.def | 6 + modules/rtp_in/rtp_in.h | 338 + modules/rtp_in/rtp_session.c | 387 + modules/rtp_in/rtp_signaling.c | 823 + modules/rtp_in/rtp_stream.c | 427 + modules/rtp_in/sdp_fetch.c | 179 + modules/rtp_in/sdp_load.c | 553 + modules/saf_in/Makefile | 63 + modules/saf_in/saf_in.c | 593 + modules/saf_in/saf_in.def | 6 + modules/sdl_out/Makefile | 72 + modules/sdl_out/audio.c | 210 + modules/sdl_out/cursors.c | 60 + modules/sdl_out/sdl_out.c | 77 + modules/sdl_out/sdl_out.def | 6 + modules/sdl_out/sdl_out.h | 79 + modules/sdl_out/video.c | 945 + modules/sdl_out/video2d.c | 28 + modules/soft_raster/Makefile | 69 + modules/soft_raster/ftgrays.c | 783 + modules/soft_raster/rast_soft.def | 6 + modules/soft_raster/rast_soft.h | 362 + modules/soft_raster/raster_565.c | 491 + modules/soft_raster/raster_argb.c | 537 + modules/soft_raster/raster_load.c | 99 + modules/soft_raster/raster_rgb.c | 444 + modules/soft_raster/stencil.c | 855 + modules/soft_raster/surface.c | 635 + modules/svg_in/Makefile | 67 + modules/svg_in/svg_in.c | 430 + modules/svg_in/svg_in.def | 6 + modules/timedtext/Makefile | 62 + modules/timedtext/timedtext.def | 6 + modules/timedtext/timedtext_dec.c | 1174 + modules/timedtext/timedtext_in.c | 382 + modules/wav_out/Makefile | 55 + modules/wav_out/wav_out.c | 486 + modules/wav_out/wav_out.def | 6 + modules/x11_out/Makefile | 98 + modules/x11_out/x11_out.c | 1547 ++ modules/x11_out/x11_out.h | 131 + modules/xvid_dec/Makefile | 69 + modules/xvid_dec/xvid_dec.c | 483 + modules/xvid_dec/xvid_dec.def | 6 + modules/xvid_dec/xvid_dec_wce.cpp | 299 + modules/xvid_dec/xvid_wce/AUTHORS | 36 + modules/xvid_dec/xvid_wce/CodecAPI.cpp | 114 + modules/xvid_dec/xvid_wce/LICENSE | 340 + modules/xvid_dec/xvid_wce/README | 29 + modules/xvid_dec/xvid_wce/ReadMe.txt | 65 + modules/xvid_dec/xvid_wce/Rules.h | 66 + modules/xvid_dec/xvid_wce/TODO | 52 + modules/xvid_dec/xvid_wce/bitstream.cpp | 1104 + modules/xvid_dec/xvid_wce/bitstream.h | 116 + modules/xvid_dec/xvid_wce/decoder.cpp | 1448 ++ modules/xvid_dec/xvid_wce/decoder.h | 269 + modules/xvid_dec/xvid_wce/font.cpp | 598 + modules/xvid_dec/xvid_wce/global.h | 355 + modules/xvid_dec/xvid_wce/gmc.cpp | 396 + modules/xvid_dec/xvid_wce/gmc.h | 45 + modules/xvid_dec/xvid_wce/idct.cpp | 210 + modules/xvid_dec/xvid_wce/image.cpp | 356 + modules/xvid_dec/xvid_wce/image.h | 37 + modules/xvid_dec/xvid_wce/interpolate8x8.cpp | 872 + modules/xvid_dec/xvid_wce/interpolate8x8.h | 107 + modules/xvid_dec/xvid_wce/mbcoding.cpp | 936 + modules/xvid_dec/xvid_wce/mbprediction.cpp | 341 + modules/xvid_dec/xvid_wce/mbprediction.h | 54 + modules/xvid_dec/xvid_wce/mem_align.cpp | 131 + modules/xvid_dec/xvid_wce/mem_align.h | 35 + modules/xvid_dec/xvid_wce/mem_transfer.cpp | 281 + modules/xvid_dec/xvid_wce/mem_transfer.h | 61 + modules/xvid_dec/xvid_wce/note | 4 + modules/xvid_dec/xvid_wce/portab.h | 166 + modules/xvid_dec/xvid_wce/qpel.inl | 146 + modules/xvid_dec/xvid_wce/qpel_tab.cpp | 103 + modules/xvid_dec/xvid_wce/quant.h | 54 + modules/xvid_dec/xvid_wce/quant_h263.cpp | 85 + modules/xvid_dec/xvid_wce/quant_matrix.cpp | 115 + modules/xvid_dec/xvid_wce/quant_matrix.h | 44 + modules/xvid_dec/xvid_wce/quant_mpeg.cpp | 98 + modules/xvid_dec/xvid_wce/reduced.cpp | 195 + modules/xvid_dec/xvid_wce/reduced.h | 54 + modules/xvid_dec/xvid_wce/vlc_codes.h | 50 + modules/xvid_dec/xvid_wce/xvid.cpp | 185 + modules/xvid_dec/xvid_wce/xvid.h | 502 + modules/xvid_dec/xvid_wce/xvid_ppc.asm | 592 + regression_tests/_mozilla_ie_action.html | 35 + regression_tests/_mozilla_ie_simple.html | 22 + regression_tests/_ppc_action.html | 35 + regression_tests/_ppc_simple.html | 25 + .../auxiliary_files/count_arabic.mp3 | Bin 0 -> 59872 bytes .../auxiliary_files/count_english.mp3 | Bin 0 -> 60060 bytes .../auxiliary_files/count_french.mp3 | Bin 0 -> 60060 bytes .../auxiliary_files/count_german.mp3 | Bin 0 -> 57365 bytes .../auxiliary_files/count_italian.mp3 | Bin 0 -> 57678 bytes .../auxiliary_files/count_spanish.mp3 | Bin 0 -> 60186 bytes .../auxiliary_files/count_video.cmp | Bin 0 -> 146688 bytes .../auxiliary_files/enst_audio.aac | Bin 0 -> 85058 bytes .../auxiliary_files/enst_video.h264 | Bin 0 -> 47679 bytes .../auxiliary_files/index2batch.xslt | 54 + .../auxiliary_files/index2html.xslt | 62 + .../auxiliary_files/index2sh.xslt | 71 + regression_tests/auxiliary_files/logo.jpg | Bin 0 -> 13781 bytes regression_tests/auxiliary_files/logo.png | Bin 0 -> 14830 bytes .../auxiliary_files/nefertiti.wrl | 649 + regression_tests/auxiliary_files/sky.jpg | Bin 0 -> 6496 bytes regression_tests/auxiliary_files/subtitle.srt | 23 + .../auxiliary_files/subtitle_fr.srt | 23 + .../auxiliary_files/svg2html.xslt | 117 + .../auxiliary_files/x3d2html.xslt | 127 + .../auxiliary_files/xmt2html.xslt | 159 + .../bifs-2D-background-background2D-bind.bt | 71 + .../bifs-2D-background-background2D-image.bt | 97 + ...bifs-2D-background-background2D-layer2D.bt | 92 + .../bifs-2D-background-background2D-movie.bt | 94 + ...s-2D-background-background2D-url-change.bt | 105 + .../bifs-2D-interactivity-discsensor.bt | 129 + .../bifs-2D-interactivity-htk-sensor.bt | 175 + .../bifs-2D-interactivity-keysensor.bt | 810 + .../bifs-2D-interactivity-mousesensor.bt | 182 + .../bifs-2D-interactivity-nested-sensors.bt | 123 + .../bifs-2D-interactivity-planesensor2D.bt | 124 + ...bifs-2D-interactivity-proximitysensor2D.bt | 89 + .../bifs-2D-interactivity-stringsensor.bt | 153 + ...fs-2D-interactivity-touchsensor-4states.bt | 103 + ...s-2D-interactivity-touchsensor-hitpoint.bt | 91 + ...ivity-touchsensor-isactive-exposedfield.bt | 99 + ...s-2D-interactivity-touchsensor-isactive.bt | 98 + ...ifs-2D-interactivity-touchsensor-isover.bt | 59 + ...-2D-interactivity-touchsensor-move_over.bt | 268 + .../bifs-2D-painting-colortransform-alpha.bt | 101 + .../bifs-2D-painting-colortransform-bitmap.bt | 109 + .../bifs-2D-painting-colortransform-color.bt | 102 + .../bifs-2D-painting-lineproperties.bt | 168 + .../bifs-2D-painting-material2D.bt | 154 + .../bifs-2D-painting-xlineproperties-cap.bt | 211 + ...ting-xlineproperties-compositetexture2D.bt | 153 + .../bifs-2D-painting-xlineproperties-dash.bt | 142 + ...D-painting-xlineproperties-imagetexture.bt | 159 + .../bifs-2D-painting-xlineproperties-join.bt | 215 + ...painting-xlineproperties-lineargradient.bt | 122 + ...painting-xlineproperties-radialgradient.bt | 124 + ...fs-2D-painting-xlineproperties-scalable.bt | 123 + ...2D-painting-xlineproperties-transparent.bt | 176 + .../bifs-2D-positioning-clipper2D.bt | 316 + .../bifs-2D-positioning-form-align-center.bt | 162 + .../bifs-2D-positioning-form-align-horiz.bt | 129 + .../bifs-2D-positioning-form-align-vert.bt | 129 + .../bifs-2D-positioning-form-spread-horiz.bt | 182 + .../bifs-2D-positioning-form-spread-vert.bt | 170 + .../bifs-2D-positioning-layer2D.bt | 129 + .../bifs-2D-positioning-layer2d-in-layer2d.bt | 111 + ...-2D-positioning-layout-horiz-ltr-nowrap.bt | 403 + ...D-positioning-layout-horiz-ltr-wrap-btt.bt | 445 + ...D-positioning-layout-horiz-ltr-wrap-ttb.bt | 434 + ...-2D-positioning-layout-horiz-rtl-nowrap.bt | 415 + ...D-positioning-layout-horiz-rtl-wrap-btt.bt | 469 + ...D-positioning-layout-horiz-rtl-wrap-ttb.bt | 457 + .../bifs-2D-positioning-layout-horiz-text.bt | 84 + ...bifs-2D-positioning-layout-scroll-child.bt | 87 + .../bifs-2D-positioning-layout-scroll-full.bt | 344 + ...D-positioning-layout-scroll-modes-horiz.bt | 349 + ...2D-positioning-layout-scroll-modes-vert.bt | 350 + ...ifs-2D-positioning-layout-scroll-on-off.bt | 218 + ...s-2D-positioning-layout-vert-btt-nowrap.bt | 340 + ...2D-positioning-layout-vert-btt-wrap-ltr.bt | 372 + ...2D-positioning-layout-vert-btt-wrap-rtl.bt | 381 + ...s-2D-positioning-layout-vert-ttb-nowrap.bt | 331 + ...2D-positioning-layout-vert-ttb-wrap-ltr.bt | 363 + ...2D-positioning-layout-vert-ttb-wrap-rtl.bt | 372 + .../bifs-2D-positioning-orderedgroup.bt | 86 + .../bifs-2D-positioning-pathlayout.bt | 164 + .../bifs-2D-positioning-transform2D.bt | 133 + .../bifs-2D-positioning-transformmatrix2D.bt | 179 + regression_tests/bifs-2D-shapes-all.bt | 267 + .../bifs-2D-shapes-indexfaceset2D.bt | 123 + .../bifs-2D-shapes-indexlineset2D.bt | 100 + regression_tests/bifs-2D-shapes-pointset2D.bt | 56 + regression_tests/bifs-2D-shapes-xcurve2D.bt | 188 + ...texturing-compositetexture2D-background.bt | 116 + ...-2D-texturing-compositetexture2D-bitmap.bt | 89 + ...exturing-compositetexture2D-transparent.bt | 131 + .../bifs-2D-texturing-gradients-text.bt | 137 + ...bifs-2D-texturing-gradients-transparent.bt | 120 + .../bifs-2D-texturing-imagetexture-shapes.bt | 282 + ...bifs-2D-texturing-lineargradient-simple.bt | 75 + ...bifs-2D-texturing-lineargradient-spread.bt | 113 + .../bifs-2D-texturing-movietexture-shapes.bt | 285 + .../bifs-2D-texturing-pixeltexture.bt | 191 + ...bifs-2D-texturing-radialgradient-simple.bt | 94 + ...bifs-2D-texturing-radialgradient-spread.bt | 118 + ...bifs-2D-texturing-texturetransform-base.bt | 234 + ...-2D-texturing-texturetransform-interact.bt | 231 + ...ring-texturetransform-transformmatrix2D.bt | 242 + regression_tests/bifs-2D-viewport-complete.bt | 715 + regression_tests/bifs-2D-viewport-simple.bt | 88 + regression_tests/bifs-3D-background-images.bt | 87 + regression_tests/bifs-3D-background.bt | 56 + .../bifs-3D-interactivity-collision-proxy.bt | 80 + .../bifs-3D-interactivity-collision.bt | 79 + .../bifs-3D-interactivity-cylindersensor.bt | 90 + .../bifs-3D-interactivity-planesensor.bt | 93 + .../bifs-3D-interactivity-proximitysensor.bt | 80 + .../bifs-3D-interactivity-spheresensor.bt | 90 + .../bifs-3D-interactivity-visibilitysensor.bt | 100 + .../bifs-3D-lighting-directionalLight.bt | 63 + regression_tests/bifs-3D-lighting-fog.bt | 56 + .../bifs-3D-lighting-pointlight.bt | 71 + .../bifs-3D-lighting-spotlight.bt | 72 + ...-positioning-billboard-viewer-alignment.bt | 62 + .../bifs-3D-positioning-billboard.bt | 61 + .../bifs-3D-positioning-gravity.bt | 71 + .../bifs-3D-positioning-layer3D-views.bt | 160 + .../bifs-3D-positioning-layer3D.bt | 186 + regression_tests/bifs-3D-positioning-lod.bt | 63 + .../bifs-3D-positioning-transform.bt | 64 + .../bifs-3D-shapes-box-transparent.bt | 118 + regression_tests/bifs-3D-shapes-box.bt | 45 + regression_tests/bifs-3D-shapes-cone.bt | 58 + regression_tests/bifs-3D-shapes-cylinder.bt | 92 + .../bifs-3D-shapes-elevationgrid.bt | 74 + regression_tests/bifs-3D-shapes-extrusion.bt | 99 + .../bifs-3D-shapes-indexedfaceset.bt | 66 + .../bifs-3D-shapes-indexedlineset.bt | 99 + .../bifs-3D-shapes-nonlineardeformer.bt | 796 + regression_tests/bifs-3D-shapes-pointset.bt | 46 + .../bifs-3D-texturing-box-transparent.bt | 73 + .../bifs-3D-texturing-box-video.bt | 184 + regression_tests/bifs-3D-texturing-box.bt | 69 + ...-3D-texturing-compositetexture3D-bitmap.bt | 113 + ...ifs-3D-texturing-compositetexture3D-box.bt | 106 + .../bifs-3D-texturing-cone-transparent.bt | 68 + regression_tests/bifs-3D-texturing-cone.bt | 81 + .../bifs-3D-texturing-cylinder-transparent.bt | 93 + .../bifs-3D-texturing-cylinder.bt | 107 + .../bifs-3D-texturing-transform-box.bt | 85 + .../bifs-3D-texturing-transform-matrix-box.bt | 91 + regression_tests/bifs-3D-viewpoint-anim.bt | 84 + .../bifs-3D-viewpoint-bind-jump.bt | 114 + regression_tests/bifs-3D-viewpoint-bind.bt | 115 + .../bifs-3D-viewpoint-ortho-bind.bt | 114 + .../bifs-bitmap-image-meter-metrics.bt | 80 + .../bifs-bitmap-image-pixel-metrics.bt | 89 + .../bifs-bitmap-image-resizing.bt | 153 + .../bifs-bitmap-movie-materialkey.bt | 101 + regression_tests/bifs-bitmap-movie.bt | 84 + .../bifs-bitmap-video-resizing.bt | 152 + .../bifs-command-animated-osmo4logo.bt | 315 + regression_tests/bifs-command-delete-index.bt | 58 + regression_tests/bifs-command-delete-node.bt | 58 + regression_tests/bifs-command-delete-route.bt | 67 + regression_tests/bifs-command-global-qp.bt | 66 + regression_tests/bifs-command-insert-index.bt | 58 + regression_tests/bifs-command-insert-node.bt | 74 + .../bifs-command-insert-nodedef.bt | 64 + regression_tests/bifs-command-insert-route.bt | 67 + .../bifs-command-multiple-replace-field.bt | 66 + .../bifs-command-multiple-replace-index.bt | 62 + .../bifs-command-node-delete-ex.bt | 125 + regression_tests/bifs-command-proto-delete.bt | 93 + regression_tests/bifs-command-proto-insert.bt | 83 + .../bifs-command-protolist-delete.bt | 93 + .../bifs-command-quantification.bt | 186 + .../bifs-command-replace-field.bt | 67 + .../bifs-command-replace-index.bt | 58 + .../bifs-command-replace-node-null.bt | 65 + regression_tests/bifs-command-replace-node.bt | 67 + .../bifs-command-replace-route.bt | 67 + .../bifs-command-replace-scene-null.bt | 37 + .../bifs-command-replace-scene.bt | 76 + .../bifs-command-route-add-children.bt | 79 + .../bifs-command-route-children.bt | 79 + .../bifs-command-route-node-exposedfield.bt | 80 + regression_tests/bifs-command-route-node.bt | 76 + .../bifs-command-route-remove-children.bt | 93 + .../bifs-externproto-forestgump-lib.bt | 463 + .../bifs-externproto-forestgump.bt | 96 + .../bifs-externproto-mfurl-lib.bt | 52 + regression_tests/bifs-externproto-mfurl.bt | 69 + regression_tests/bifs-externproto-nood-lib.bt | 109 + regression_tests/bifs-externproto-nood.bt | 70 + .../bifs-externproto-simple-lib.bt | 90 + regression_tests/bifs-externproto-simple.bt | 81 + regression_tests/bifs-game-arrange.bt | 452 + regression_tests/bifs-game-breakout.bt | 1035 + regression_tests/bifs-game-bubble.bt | 433 + regression_tests/bifs-game-minesweeper.bt | 1538 ++ regression_tests/bifs-game-othello.bt | 2836 +++ .../bifs-interpolation-colorinterpolator.bt | 63 + ...-interpolation-coordinateinterpolator2D.bt | 64 + .../bifs-interpolation-positionanimator.bt | 63 + .../bifs-interpolation-positionanimator2D.bt | 65 + ...rpolation-positioninterpolator-position.bt | 94 + ...interpolation-positioninterpolator-size.bt | 58 + ...olation-positioninterpolator2D-position.bt | 96 + ...terpolation-positioninterpolator2D-size.bt | 64 + .../bifs-interpolation-scalaranimator.bt | 66 + .../bifs-interpolation-scalarinterpolator.bt | 63 + .../bifs-interpolation-timesensor-enabled.bt | 82 + ...polation-timesensor-starttime_norestart.bt | 81 + ...erpolation-timesensor-starttime_restart.bt | 82 + .../bifs-interpolation-valuator-sftime.bt | 58 + .../bifs-linking-anchor-mp4-next.bt | 67 + .../bifs-linking-anchor-mp4-prev.bt | 67 + .../bifs-linking-anchor-viewpoint.bt | 92 + regression_tests/bifs-linking-anchor-www.bt | 67 + .../bifs-linking-animationstream.bt | 136 + .../bifs-linking-inline-direct-inline.bt | 75 + .../bifs-linking-inline-direct.bt | 51 + .../bifs-linking-inline-od-inline.bt | 76 + regression_tests/bifs-linking-inline-od.bt | 60 + .../bifs-linking-inline-rtsp-no-od.bt | 48 + regression_tests/bifs-linking-inline-rtsp.bt | 59 + .../bifs-linking-inline-segment-inline.bt | 74 + .../bifs-linking-inline-segment.bt | 41 + regression_tests/bifs-media-audiobuffer.bt | 203 + .../bifs-media-audioclip-urlchanged.bt | 145 + regression_tests/bifs-media-audioclip.bt | 133 + .../bifs-media-audiosource-mixing.bt | 194 + .../bifs-media-audiosource-urlchanged.bt | 146 + regression_tests/bifs-media-audiosource.bt | 133 + .../bifs-media-imagetexture-OD-reuse.bt | 89 + .../bifs-media-imagetexture-no-od.bt | 60 + .../bifs-media-imagetexture-object-scale.bt | 97 + .../bifs-media-imagetexture-transparent.bt | 275 + .../bifs-media-imagetexture-url-change.bt | 95 + .../bifs-media-movietexture-control.bt | 148 + .../bifs-media-movietexture-no-od.bt | 65 + .../bifs-media-movietexture-od-joinsession.bt | 93 + ...ifs-media-movietexture-od-leave-session.bt | 93 + .../bifs-media-movietexture-owns-OCR.bt | 75 + .../bifs-media-movietexture-shares-OCR.bt | 74 + .../bifs-media-movietexture-url-change.bt | 158 + .../bifs-media-sound-spatialize.bt | 77 + regression_tests/bifs-media-sound.bt | 77 + regression_tests/bifs-misc-UTF16-input.bt | 55 + regression_tests/bifs-misc-cyclic-graph.bt | 55 + .../bifs-misc-hc-proto-pathextrusion.bt | 103 + .../bifs-misc-hc-proto-planarextrusion.bt | 87 + .../bifs-misc-hc-proto-planeclipper.bt | 69 + ...ifs-misc-non-linear-parsing-conditional.bt | 69 + .../bifs-misc-non-linear-parsing-use.bt | 60 + ...-misc-srt-import-3gpp-control-share-ocr.bt | 64 + .../bifs-misc-srt-import-3gpp-control.bt | 64 + regression_tests/bifs-misc-srt-import-3gpp.bt | 64 + regression_tests/bifs-misc-srt-import.bt | 75 + regression_tests/bifs-od-remove-esd.bt | 78 + regression_tests/bifs-od-remove-od.bt | 78 + regression_tests/bifs-od-update-od.bt | 89 + regression_tests/bifs-proto-conditional.bt | 107 + regression_tests/bifs-proto-delete-def.bt | 91 + regression_tests/bifs-proto-delete-index.bt | 95 + regression_tests/bifs-proto-forestgump.bt | 478 + regression_tests/bifs-proto-mfurl.bt | 78 + regression_tests/bifs-proto-multiple.bt | 180 + regression_tests/bifs-proto-nested.bt | 132 + regression_tests/bifs-proto-route.bt | 110 + .../bifs-proto-sftime-protocode.bt | 99 + .../bifs-proto-sftime-protointerface.bt | 104 + regression_tests/bifs-proto-simple.bt | 88 + regression_tests/bifs-proto-use.bt | 93 + regression_tests/bifs-script-char-to-int.bt | 87 + regression_tests/bifs-script-child-create.bt | 61 + regression_tests/bifs-script-date.bt | 64 + regression_tests/bifs-script-event-out.bt | 85 + regression_tests/bifs-script-initialize.bt | 86 + regression_tests/bifs-script-load-url.bt | 67 + regression_tests/bifs-script-node-access.bt | 86 + regression_tests/bifs-script-node-create.bt | 80 + regression_tests/bifs-script-proto.bt | 223 + regression_tests/bifs-script-timestamp.bt | 67 + regression_tests/bifs-stream-text-switch.bt | 143 + regression_tests/bifs-text-align-horiz1.bt | 304 + regression_tests/bifs-text-align-horiz2.bt | 320 + regression_tests/bifs-text-align-horiz3.bt | 320 + regression_tests/bifs-text-align-horiz4.bt | 336 + regression_tests/bifs-text-align-vert1.bt | 200 + regression_tests/bifs-text-align-vert2.bt | 209 + regression_tests/bifs-text-align-vert3.bt | 209 + regression_tests/bifs-text-align-vert4.bt | 218 + regression_tests/bifs-text-glyph-advance.bt | 436 + regression_tests/bifs-text-length.bt | 230 + regression_tests/bifs-text-maxextend.bt | 176 + regression_tests/bifs-text-style.bt | 184 + regression_tests/bifs-text-unicode.bt | 55 + regression_tests/bifs-text-vrml-alignment.bt | 161 + .../bifs-timeline-mediacontrol-OCR.bt | 245 + .../bifs-timeline-mediacontrol-audio-speed.bt | 214 + .../bifs-timeline-mediacontrol-audio.bt | 352 + .../bifs-timeline-mediacontrol-complete.bt | 266 + ...bifs-timeline-mediacontrol-deactivation.bt | 105 + .../bifs-timeline-mediacontrol-inline-av.bt | 96 + ...s-timeline-mediacontrol-inline-segments.bt | 66 + .../bifs-timeline-mediacontrol-inline.bt | 231 + .../bifs-timeline-mediacontrol-rtsp.bt | 340 + .../bifs-timeline-mediacontrol-seg-inline.bt | 81 + .../bifs-timeline-mediacontrol-segments.bt | 154 + .../bifs-timeline-mediacontrol-video.bt | 248 + .../bifs-timeline-mediacontrol-videospeed.bt | 202 + ...ifs-timeline-mediasensor-segment-switch.bt | 226 + .../bifs-timeline-mediasensor-segment.bt | 215 + regression_tests/bifs-timeline-mediasensor.bt | 137 + regression_tests/build-navigator-sh | 11 + regression_tests/build-navigator-w32.bat | 14 + regression_tests/build-shell | 49 + regression_tests/build-w32.bat | 54 + regression_tests/index.css | 134 + regression_tests/index.xml | 373 + regression_tests/x3d-2D-Arc2d.x3dv | 23 + regression_tests/x3d-2D-ArcClose2d.x3dv | 24 + regression_tests/x3d-2D-Disk2d.x3dv | 24 + regression_tests/x3d-2D-Polyline2d.x3dv | 23 + regression_tests/x3d-2D-Polypoint2d.x3dv | 23 + regression_tests/x3d-2D-TriangleSet2d.x3dv | 24 + .../x3d-3D-IndexedTriangleFanSet.x3dv | 28 + .../x3d-3D-IndexedTriangleSet.x3dv | 27 + .../x3d-3D-IndexedTriangleStripSet.x3dv | 28 + regression_tests/x3d-3D-LineSet.x3dv | 27 + regression_tests/x3d-3D-TriangleFanSet.x3dv | 28 + regression_tests/x3d-3D-TriangleSet.x3dv | 26 + regression_tests/x3d-3D-TriangleStripSet.x3dv | 28 + regression_tests/x3d-misc-ColorRGBA.x3dv | 42 + regression_tests/x3d-misc-HatchStyle.x3dv | 31 + regression_tests/x3d-misc-KeySensor.x3dv | 73 + regression_tests/x3d-misc-StringSensor.x3dv | 83 + run_configure.sh | 21 + src/Makefile | 234 + src/bifs/arith_decoder.c | 281 + src/bifs/bifs_codec.c | 455 + src/bifs/bifs_node_tables.c | 920 + src/bifs/com_dec.c | 1162 + src/bifs/com_enc.c | 885 + src/bifs/conditional.c | 177 + src/bifs/field_decode.c | 894 + src/bifs/field_encode.c | 602 + src/bifs/memory_decoder.c | 920 + src/bifs/predictive_mffield.c | 448 + src/bifs/quant.h | 110 + src/bifs/quantize.c | 333 + src/bifs/script.h | 105 + src/bifs/script_dec.c | 778 + src/bifs/script_enc.c | 1773 ++ src/bifs/unquantize.c | 434 + src/compositor/audio_input.c | 262 + src/compositor/audio_mixer.c | 760 + src/compositor/audio_render.c | 341 + src/compositor/bindable.c | 244 + src/compositor/camera.c | 416 + src/compositor/compositor.c | 2530 ++ src/compositor/compositor_2d.c | 858 + src/compositor/compositor_3d.c | 256 + src/compositor/compositor_node_init.c | 228 + src/compositor/drawable.c | 1384 + src/compositor/drawable.h | 316 + src/compositor/events.c | 1513 ++ src/compositor/font_engine.c | 1422 ++ src/compositor/gl_inc.h | 198 + src/compositor/hardcoded_protos.c | 749 + src/compositor/mesh.c | 2195 ++ src/compositor/mesh_collide.c | 598 + src/compositor/mesh_tesselate.c | 489 + src/compositor/mpeg4_animstream.c | 196 + src/compositor/mpeg4_audio.c | 610 + src/compositor/mpeg4_background.c | 537 + src/compositor/mpeg4_background2d.c | 413 + src/compositor/mpeg4_bitmap.c | 266 + src/compositor/mpeg4_composite.c | 622 + src/compositor/mpeg4_form.c | 674 + src/compositor/mpeg4_geometry_2d.c | 683 + src/compositor/mpeg4_geometry_3d.c | 523 + src/compositor/mpeg4_geometry_ifs2d.c | 342 + src/compositor/mpeg4_geometry_ils2d.c | 308 + src/compositor/mpeg4_gradients.c | 597 + src/compositor/mpeg4_grouping.c | 826 + src/compositor/mpeg4_grouping.h | 187 + src/compositor/mpeg4_grouping_2d.c | 401 + src/compositor/mpeg4_grouping_3d.c | 409 + src/compositor/mpeg4_layer_2d.c | 379 + src/compositor/mpeg4_layer_3d.c | 710 + src/compositor/mpeg4_layout.c | 806 + src/compositor/mpeg4_lighting.c | 135 + src/compositor/mpeg4_path_layout.c | 272 + src/compositor/mpeg4_sensors.c | 1288 + src/compositor/mpeg4_sound.c | 283 + src/compositor/mpeg4_text.c | 692 + src/compositor/mpeg4_textures.c | 397 + src/compositor/mpeg4_timesensor.c | 193 + src/compositor/mpeg4_viewport.c | 568 + src/compositor/navigate.c | 663 + src/compositor/nodes_stacks.h | 301 + src/compositor/offscreen_cache.c | 805 + src/compositor/offscreen_cache.h | 58 + src/compositor/svg_base.c | 387 + src/compositor/svg_font.c | 521 + src/compositor/svg_geometry.c | 691 + src/compositor/svg_grouping.c | 1316 + src/compositor/svg_media.c | 714 + src/compositor/svg_paint_servers.c | 706 + src/compositor/svg_text.c | 1486 ++ src/compositor/texturing.c | 256 + src/compositor/texturing.h | 93 + src/compositor/texturing_gl.c | 1185 + src/compositor/visual_manager.c | 256 + src/compositor/visual_manager.h | 156 + src/compositor/visual_manager_2d.c | 778 + src/compositor/visual_manager_2d.h | 149 + src/compositor/visual_manager_2d_draw.c | 581 + src/compositor/visual_manager_3d.c | 1517 ++ src/compositor/visual_manager_3d.h | 287 + src/compositor/visual_manager_3d_gl.c | 1677 ++ src/compositor/x3d_geometry.c | 1013 + src/dir_info | 47 + src/ietf/rtcp.c | 577 + src/ietf/rtp.c | 901 + src/ietf/rtp_depacketizer.c | 1461 ++ src/ietf/rtp_packetizer.c | 575 + src/ietf/rtp_pck_3gpp.c | 770 + src/ietf/rtp_pck_mpeg12.c | 233 + src/ietf/rtp_pck_mpeg4.c | 615 + src/ietf/rtsp_command.c | 579 + src/ietf/rtsp_common.c | 367 + src/ietf/rtsp_response.c | 708 + src/ietf/rtsp_session.c | 780 + src/ietf/sdp.c | 1153 + src/isomedia/avc_ext.c | 449 + src/isomedia/box_code_3gpp.c | 1263 + src/isomedia/box_code_apple.c | 324 + src/isomedia/box_code_base.c | 7419 ++++++ src/isomedia/box_code_isma.c | 885 + src/isomedia/box_code_meta.c | 773 + src/isomedia/box_dump.c | 3251 +++ src/isomedia/box_funcs.c | 1416 ++ src/isomedia/data_map.c | 562 + src/isomedia/hint_track.c | 989 + src/isomedia/hinting.c | 982 + src/isomedia/isma_sample.c | 492 + src/isomedia/isom_intern.c | 704 + src/isomedia/isom_read.c | 2320 ++ src/isomedia/isom_store.c | 1285 + src/isomedia/isom_write.c | 3761 +++ src/isomedia/media.c | 809 + src/isomedia/media_odf.c | 516 + src/isomedia/meta.c | 561 + src/isomedia/movie_fragments.c | 778 + src/isomedia/sample_descs.c | 481 + src/isomedia/stbl_read.c | 513 + src/isomedia/stbl_write.c | 1715 ++ src/isomedia/track.c | 791 + src/isomedia/tx3g.c | 693 + src/laser/lsr_dec.c | 5235 ++++ src/laser/lsr_enc.c | 3944 +++ src/laser/lsr_tables.c | 849 + src/libgpac.def | 1235 + src/libgpac_ce.def | 1207 + src/mcrypt/cbc.c | 167 + src/mcrypt/cfb.c | 160 + src/mcrypt/ctr.c | 234 + src/mcrypt/des.c | 580 + src/mcrypt/ecb.c | 85 + src/mcrypt/g_crypt.c | 332 + src/mcrypt/ncfb.c | 321 + src/mcrypt/nofb.c | 212 + src/mcrypt/ofb.c | 163 + src/mcrypt/rijndael-128.c | 414 + src/mcrypt/rijndael-192.c | 418 + src/mcrypt/rijndael-256.c | 416 + src/mcrypt/sha1.c | 381 + src/mcrypt/stream.c | 69 + src/mcrypt/tripledes.c | 760 + src/media_tools/av_parsers.c | 2636 ++ src/media_tools/avilib.c | 2973 +++ src/media_tools/gpac_ogg.c | 1326 + src/media_tools/img.c | 646 + src/media_tools/ismacryp.c | 949 + src/media_tools/isom_hinter.c | 1489 ++ src/media_tools/isom_tools.c | 1092 + src/media_tools/media_export.c | 2000 ++ src/media_tools/media_import.c | 6181 +++++ src/media_tools/mpeg2_ps.c | 1808 ++ src/media_tools/mpeg2_ps.h | 204 + src/media_tools/mpegts.c | 1611 ++ src/media_tools/saf.c | 333 + src/media_tools/text_import.c | 1713 ++ src/media_tools/vobsub.c | 651 + src/odf/desc_private.c | 680 + src/odf/descriptors.c | 708 + src/odf/ipmpx_code.c | 2017 ++ src/odf/ipmpx_dump.c | 868 + src/odf/ipmpx_parse.c | 682 + src/odf/oci_codec.c | 422 + src/odf/odf_code.c | 3323 +++ src/odf/odf_codec.c | 653 + src/odf/odf_command.c | 647 + src/odf/odf_dump.c | 1940 ++ src/odf/odf_parse.c | 686 + src/odf/qos.c | 390 + src/odf/slc.c | 447 + src/scene_manager/encode_cbk.c | 503 + src/scene_manager/encode_isom.c | 1219 + src/scene_manager/loader_bt.c | 3447 +++ src/scene_manager/loader_isom.c | 368 + src/scene_manager/loader_qt.c | 190 + src/scene_manager/loader_svg.c | 1840 ++ src/scene_manager/loader_xmt.c | 2861 +++ src/scene_manager/scene_dump.c | 3187 +++ src/scene_manager/scene_manager.c | 525 + src/scene_manager/scene_stats.c | 612 + src/scene_manager/swf_bifs.c | 2252 ++ src/scene_manager/swf_parse.c | 2498 ++ src/scene_manager/text_to_bifs.c | 521 + src/scenegraph/base_scenegraph.c | 1974 ++ src/scenegraph/commands.c | 869 + src/scenegraph/dom_events.c | 731 + src/scenegraph/dom_smjs.c | 3655 +++ src/scenegraph/mpeg4_animators.c | 792 + src/scenegraph/mpeg4_nodes.c | 20795 ++++++++++++++++ src/scenegraph/mpeg4_valuator.c | 432 + src/scenegraph/smil_anim.c | 1462 ++ src/scenegraph/smil_timing.c | 1041 + src/scenegraph/svg_attributes.c | 5996 +++++ src/scenegraph/svg_properties.c | 971 + src/scenegraph/svg_smjs.c | 2533 ++ src/scenegraph/svg_types.c | 484 + src/scenegraph/vrml_interpolators.c | 709 + src/scenegraph/vrml_proto.c | 1231 + src/scenegraph/vrml_route.c | 360 + src/scenegraph/vrml_script.c | 302 + src/scenegraph/vrml_smjs.c | 3814 +++ src/scenegraph/vrml_tools.c | 1489 ++ src/scenegraph/x3d_nodes.c | 10628 ++++++++ src/scenegraph/xbl_process.c | 236 + src/scenegraph/xml_ns.c | 1032 + src/terminal/channel.c | 1298 + src/terminal/clock.c | 275 + src/terminal/decoder.c | 1025 + src/terminal/inline.c | 1719 ++ src/terminal/input_sensor.c | 988 + src/terminal/input_sensor.h | 101 + src/terminal/media_control.c | 443 + src/terminal/media_control.h | 108 + src/terminal/media_manager.c | 591 + src/terminal/media_memory.c | 586 + src/terminal/media_memory.h | 163 + src/terminal/media_object.c | 798 + src/terminal/media_sensor.c | 232 + src/terminal/network_service.c | 1024 + src/terminal/object_browser.c | 376 + src/terminal/object_manager.c | 1788 ++ src/terminal/svg_external.c | 228 + src/terminal/term_node_init.c | 254 + src/terminal/terminal.c | 1323 + src/utils/base_encoding.c | 179 + src/utils/bitstream.c | 846 + src/utils/color.c | 1005 + src/utils/configfile.c | 361 + src/utils/downloader.c | 1546 ++ src/utils/error.c | 434 + src/utils/gzio.cpp | 1006 + src/utils/list.c | 730 + src/utils/math.c | 2353 ++ src/utils/module.c | 193 + src/utils/module_wrap.h | 79 + src/utils/os_divers.c | 1239 + src/utils/os_module.c | 214 + src/utils/os_net.c | 1446 ++ src/utils/os_thread.c | 566 + src/utils/path2d.c | 1359 + src/utils/path2d_stroker.c | 1784 ++ src/utils/symbian_net.cpp | 1316 + src/utils/symbian_os.cpp | 839 + src/utils/token.c | 132 + src/utils/uni_bidi.c | 1484 ++ src/utils/url.c | 195 + src/utils/utf.c | 188 + src/utils/xml_parser.c | 1695 ++ src/utils/zlib_symbian_ext.h | 207 + src/utils/zutil.c | 318 + src/utils/zutil.h | 277 + 1534 files changed, 607048 insertions(+) create mode 100644 AUTHORS create mode 100644 BUGS create mode 100644 COPYING create mode 100644 Changelog create mode 100644 Clean.bat create mode 100644 INSTALLME create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 applications/GPAX/GPAX.cpp create mode 100644 applications/GPAX/GPAX.def create mode 100644 applications/GPAX/GPAX.dsp create mode 100644 applications/GPAX/GPAX.h create mode 100644 applications/GPAX/GPAX.idl create mode 100644 applications/GPAX/GPAX.rc create mode 100644 applications/GPAX/GPAX.rgs create mode 100644 applications/GPAX/GPAXPlugin.cpp create mode 100644 applications/GPAX/GPAXPlugin.h create mode 100644 applications/GPAX/GPAX_i.c create mode 100644 applications/GPAX/GPAX_p.c create mode 100644 applications/GPAX/GPAXps.def create mode 100644 applications/GPAX/GPAXps.mk create mode 100644 applications/GPAX/StdAfx.cpp create mode 100644 applications/GPAX/StdAfx.h create mode 100644 applications/GPAX/dlldata.c create mode 100644 applications/GPAX/gpax.bmp create mode 100644 applications/GPAX/resource.h create mode 100644 applications/Makefile create mode 100644 applications/generators/MPEG4/MPEG4Gen.dsp create mode 100644 applications/generators/MPEG4/MPEG4Gen.dsw create mode 100644 applications/generators/MPEG4/Makefile create mode 100644 applications/generators/MPEG4/main.c create mode 100644 applications/generators/MPEG4/skip.txt create mode 100644 applications/generators/MPEG4/templates1.txt create mode 100644 applications/generators/MPEG4/templates2.txt create mode 100644 applications/generators/MPEG4/templates3.txt create mode 100644 applications/generators/MPEG4/templates4.txt create mode 100644 applications/generators/MPEG4/templates5.txt create mode 100644 applications/generators/MPEG4/templates6.txt create mode 100644 applications/generators/MPEG4/templates7.txt create mode 100644 applications/generators/Makefile create mode 100644 applications/generators/SVG/Makefile create mode 100644 applications/generators/SVG/SVGGen.dsp create mode 100644 applications/generators/SVG/SVGGen.dsw create mode 100644 applications/generators/SVG/Tiny-1.2-NG/Tiny-1.2.nvdl create mode 100644 applications/generators/SVG/Tiny-1.2-NG/Tiny-1.2.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/animate.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/animation-element.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/audio.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/conditional.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/contenttype-attrib.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/coordinate-attrib.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/core-attrib.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/datatypes.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/discard.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/extensibility.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/extresources-attrib.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/flowable-text-tiny.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/focus-attrib.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/font-tiny.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/gradient-tiny.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/graphics-attrib.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/handler.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/headers.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/hyperlink.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/image.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/laser-ext.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/media-attrib.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/opacity-attrib-tiny.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/paint-attrib-tiny.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/prefetch.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/script.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/shapes.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/solidcolor.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/structure-tiny.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/text-tiny.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/transform-attrib.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/vectoreffects-attrib-tiny.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/video.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/viewport-attrib-tiny.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/xlink-attrib.rng create mode 100644 applications/generators/SVG/Tiny-1.2-NG/xml-events.rng create mode 100644 applications/generators/SVG/html.c create mode 100644 applications/generators/SVG/laser.c create mode 100644 applications/generators/SVG/main.c create mode 100644 applications/generators/SVG/svggen.h create mode 100644 applications/generators/SVG/v1.c create mode 100644 applications/generators/SVG/v2.c create mode 100644 applications/generators/SVG/v3.c create mode 100644 applications/generators/X3D/Makefile create mode 100644 applications/generators/X3D/X3DGen.dsp create mode 100644 applications/generators/X3D/X3DGen.dsw create mode 100644 applications/generators/X3D/main.c create mode 100644 applications/generators/X3D/skip.txt create mode 100644 applications/generators/X3D/templates_X3D.txt create mode 100644 applications/mp42avi/Makefile create mode 100644 applications/mp42avi/main.c create mode 100644 applications/mp4box/Makefile create mode 100644 applications/mp4box/filedump.c create mode 100644 applications/mp4box/fileimport.c create mode 100644 applications/mp4box/main.c create mode 100644 applications/mp4client/Makefile create mode 100644 applications/mp4client/extract.c create mode 100644 applications/mp4client/main.c create mode 100644 applications/osmo4_sym/aif/osmo4_icon.bmp create mode 100644 applications/osmo4_sym/aif/osmo4_icon_mask.bmp create mode 100644 applications/osmo4_sym/aif/osmo4_menu.bmp create mode 100644 applications/osmo4_sym/aif/osmo4_menu_mask.bmp create mode 100644 applications/osmo4_sym/aif/osmo4aif.rss create mode 100644 applications/osmo4_sym/osmo4.cpp create mode 100644 applications/osmo4_sym/osmo4.h create mode 100644 applications/osmo4_sym/osmo4_ui.cpp create mode 100644 applications/osmo4_sym/osmo4_ui.h create mode 100644 applications/osmo4_sym/osmo4_view.cpp create mode 100644 applications/osmo4_sym/osmo4_view.h create mode 100644 applications/osmo4_sym/playlist.cpp create mode 100644 applications/osmo4_sym/playlist.h create mode 100644 applications/osmo4_sym/res/osmo4.rss create mode 100644 applications/osmo4_sym/res/osmo4.svg create mode 100644 applications/osmo4_sym/res/osmo4_caption.rss create mode 100644 applications/osmo4_sym/res/osmo4_gen.rss create mode 100644 applications/osmo4_sym/res/osmo4_reg.rss create mode 100644 applications/osmo4_w32/AddressBar.cpp create mode 100644 applications/osmo4_w32/AddressBar.h create mode 100644 applications/osmo4_w32/FileProps.cpp create mode 100644 applications/osmo4_w32/FileProps.h create mode 100644 applications/osmo4_w32/MainFrm.cpp create mode 100644 applications/osmo4_w32/MainFrm.h create mode 100644 applications/osmo4_w32/OpenUrl.cpp create mode 100644 applications/osmo4_w32/OpenUrl.h create mode 100644 applications/osmo4_w32/Options.cpp create mode 100644 applications/osmo4_w32/Options.h create mode 100644 applications/osmo4_w32/Osmo4.cpp create mode 100644 applications/osmo4_w32/Osmo4.h create mode 100644 applications/osmo4_w32/Osmo4.rc create mode 100644 applications/osmo4_w32/Playlist.cpp create mode 100644 applications/osmo4_w32/Playlist.h create mode 100644 applications/osmo4_w32/Sliders.cpp create mode 100644 applications/osmo4_w32/Sliders.h create mode 100644 applications/osmo4_w32/StdAfx.cpp create mode 100644 applications/osmo4_w32/StdAfx.h create mode 100644 applications/osmo4_w32/res/Osmo4.rc2 create mode 100644 applications/osmo4_w32/res/error.ico create mode 100644 applications/osmo4_w32/res/maintool.bmp create mode 100644 applications/osmo4_w32/res/message.ico create mode 100644 applications/osmo4_w32/res/osmo4.ico create mode 100644 applications/osmo4_w32/res/pause.ico create mode 100644 applications/osmo4_w32/res/play.ico create mode 100644 applications/osmo4_w32/res/playlist.bmp create mode 100644 applications/osmo4_w32/res/stop.ico create mode 100644 applications/osmo4_w32/resource.h create mode 100644 applications/osmo4_wce/MainFrm.cpp create mode 100644 applications/osmo4_wce/MainFrm.h create mode 100644 applications/osmo4_wce/OpenDlg.cpp create mode 100644 applications/osmo4_wce/OpenDlg.h create mode 100644 applications/osmo4_wce/Options.cpp create mode 100644 applications/osmo4_wce/Options.h create mode 100644 applications/osmo4_wce/Osmo4.cpp create mode 100644 applications/osmo4_wce/Osmo4.h create mode 100644 applications/osmo4_wce/Osmo4.rc create mode 100644 applications/osmo4_wce/ProgressBar.cpp create mode 100644 applications/osmo4_wce/ProgressBar.h create mode 100644 applications/osmo4_wce/Resource.h create mode 100644 applications/osmo4_wce/StdAfx.cpp create mode 100644 applications/osmo4_wce/StdAfx.h create mode 100644 applications/osmo4_wce/newres.h create mode 100644 applications/osmo4_wce/res/Cmdbar.bmp create mode 100644 applications/osmo4_wce/res/Osmo4.ico create mode 100644 applications/osmo4_wce/res/Osmo4.rc2 create mode 100644 applications/osmo4_wx/Darwin.Info.plist create mode 100644 applications/osmo4_wx/Darwin.InfoPlist.strings create mode 100644 applications/osmo4_wx/Darwin.Osmo.icns create mode 100644 applications/osmo4_wx/Makefile create mode 100644 applications/osmo4_wx/Playlist.cpp create mode 100644 applications/osmo4_wx/Playlist.h create mode 100644 applications/osmo4_wx/fileprops.cpp create mode 100644 applications/osmo4_wx/fileprops.h create mode 100644 applications/osmo4_wx/menubtn.cpp create mode 100644 applications/osmo4_wx/menubtn.h create mode 100644 applications/osmo4_wx/osmo4.ico create mode 100644 applications/osmo4_wx/osmo4.xpm create mode 100644 applications/osmo4_wx/playlist.xpm create mode 100644 applications/osmo4_wx/resource.h create mode 100644 applications/osmo4_wx/toolbar.xpm create mode 100644 applications/osmo4_wx/wxGPACControl.cpp create mode 100644 applications/osmo4_wx/wxGPACControl.h create mode 100644 applications/osmo4_wx/wxOsmo4.cpp create mode 100644 applications/osmo4_wx/wxOsmo4.h create mode 100644 applications/osmo4_wx/wxOsmo4.rc create mode 100644 applications/osmophone/Osmo4.ico create mode 100644 applications/osmophone/main.cpp create mode 100644 applications/osmophone/newres.h create mode 100644 applications/osmophone/openfile.cpp create mode 100644 applications/osmophone/osmophone.rc create mode 100644 applications/osmophone/resource.h create mode 100644 applications/osmozilla/Makefile create mode 100644 applications/osmozilla/np_entry.cpp create mode 100644 applications/osmozilla/npn_gate.cpp create mode 100644 applications/osmozilla/npp_gate.cpp create mode 100644 applications/osmozilla/npplat.h create mode 100644 applications/osmozilla/nsIOsmozilla.h create mode 100644 applications/osmozilla/nsIOsmozilla.idl create mode 100644 applications/osmozilla/nsIOsmozilla.xpt_linux create mode 100644 applications/osmozilla/nsIOsmozilla.xpt_w32 create mode 100644 applications/osmozilla/osmozilla.cpp create mode 100644 applications/osmozilla/osmozilla.def create mode 100644 applications/osmozilla/osmozilla.h create mode 100644 applications/osmozilla/osmozilla.png create mode 100644 applications/osmozilla/osmozilla.rc create mode 100644 applications/osmozilla/readme.txt create mode 100644 applications/osmozilla/resource.h create mode 100644 applications/standalone2drender/standalone2drender.c create mode 100644 applications/standalone2drender/standalone2drender.h create mode 100644 applications/testapps/beng_test/BifsEngineTester.dsp create mode 100644 applications/testapps/beng_test/CmdLineTst.c create mode 100644 applications/testapps/beng_test/rect-update.bt create mode 100644 applications/testapps/beng_test/rect.bt create mode 100644 applications/testapps/broadcaster/Makefile create mode 100644 applications/testapps/broadcaster/RTP_serv_generator.c create mode 100644 applications/testapps/broadcaster/RTP_serv_generator.h create mode 100644 applications/testapps/broadcaster/RTP_serv_packetizer.c create mode 100644 applications/testapps/broadcaster/RTP_serv_packetizer.h create mode 100644 applications/testapps/broadcaster/RTP_serv_sender.c create mode 100644 applications/testapps/broadcaster/RTP_serv_sender.h create mode 100644 applications/testapps/broadcaster/broadcaster.c create mode 100644 applications/testapps/broadcaster/broadcaster.dsp create mode 100644 applications/testapps/broadcaster/broadcaster.h create mode 100644 applications/testapps/broadcaster/broadcaster_config.cfg create mode 100644 applications/testapps/broadcaster/france.mp4 create mode 100644 applications/testapps/broadcaster/meteo_local.xmt create mode 100644 applications/testapps/broadcaster/sdp_generator.c create mode 100644 applications/testapps/broadcaster/sdp_generator.h create mode 100644 applications/testapps/dmbrs/dmbrs.dsp create mode 100644 applications/testapps/dmbrs/main.c create mode 100644 applications/testapps/largefile/largefile.dsp create mode 100644 applications/testapps/largefile/largefile.dsw create mode 100644 applications/testapps/largefile/main.c create mode 100644 applications/testapps/loadcompare/LoadCompare.dsp create mode 100644 applications/testapps/loadcompare/Makefile create mode 100644 applications/testapps/loadcompare/loadcompare.c create mode 100644 applications/testapps/mp42ts/Makefile create mode 100644 applications/testapps/mp42ts/main.c create mode 100644 applications/testapps/mp42ts/mp42ts.c create mode 100644 applications/testapps/mp42ts/mp42ts.dsp create mode 100644 applications/testapps/mp42ts/mp42ts.h create mode 100644 applications/testapps/mp4_streamer/Makefile create mode 100644 applications/testapps/mp4_streamer/configuration.cfg create mode 100644 applications/testapps/mp4_streamer/main.c create mode 100644 applications/testapps/mp4_streamer/mp4_streamer.dsp create mode 100644 applications/testapps/mpedemux/Makefile create mode 100644 applications/testapps/mpedemux/main.c create mode 100644 applications/testapps/mpedemux/mpedemux.dsp create mode 100644 applications/testapps/mpeg2ts/main.c create mode 100644 applications/testapps/mpeg2ts/mpeg2ts.dsp create mode 100644 applications/testapps/svg2bifs/main.c create mode 100644 applications/testapps/svg2bifs/svg2bifs.dsp create mode 100644 applications/v4studio/V4CommandPanel.cpp create mode 100644 applications/v4studio/V4CommandPanel.h create mode 100644 applications/v4studio/V4FieldList.cpp create mode 100644 applications/v4studio/V4FieldList.h create mode 100644 applications/v4studio/V4Node.cpp create mode 100644 applications/v4studio/V4Node.h create mode 100644 applications/v4studio/V4NodePool.cpp create mode 100644 applications/v4studio/V4NodePool.h create mode 100644 applications/v4studio/V4NodePools.cpp create mode 100644 applications/v4studio/V4NodePools.h create mode 100644 applications/v4studio/V4SceneGraph.cpp create mode 100644 applications/v4studio/V4SceneGraph.h create mode 100644 applications/v4studio/V4SceneManager.cpp create mode 100644 applications/v4studio/V4SceneManager.h create mode 100644 applications/v4studio/V4Service.cpp create mode 100644 applications/v4studio/V4Service.h create mode 100644 applications/v4studio/V4StudioApp.cpp create mode 100644 applications/v4studio/V4StudioApp.h create mode 100644 applications/v4studio/V4StudioFrame.cpp create mode 100644 applications/v4studio/V4StudioFrame.h create mode 100644 applications/v4studio/V4StudioTree.cpp create mode 100644 applications/v4studio/V4StudioTree.h create mode 100644 applications/v4studio/V4TimeLine/V4TimeLine.cpp create mode 100644 applications/v4studio/V4TimeLine/V4TimeLine.h create mode 100644 applications/v4studio/V4TimeLine/V4TimeLineCase.cpp create mode 100644 applications/v4studio/V4TimeLine/V4TimeLineCase.h create mode 100644 applications/v4studio/V4TimeLine/V4TimeLineElt.cpp create mode 100644 applications/v4studio/V4TimeLine/V4TimeLineElt.h create mode 100644 applications/v4studio/V4TimeLine/V4TimeLineHdr.cpp create mode 100644 applications/v4studio/V4TimeLine/V4TimeLineHdr.h create mode 100644 applications/v4studio/V4TimeLine/V4TimeLineLine.cpp create mode 100644 applications/v4studio/V4TimeLine/V4TimeLineLine.h create mode 100644 applications/v4studio/install.txt create mode 100644 applications/v4studio/rc/V4Studio.aps create mode 100644 applications/v4studio/rc/V4Studio.rc create mode 100644 applications/v4studio/rc/appearance.bmp create mode 100644 applications/v4studio/rc/circle.bmp create mode 100644 applications/v4studio/rc/close.bmp create mode 100644 applications/v4studio/rc/colortransform.bmp create mode 100644 applications/v4studio/rc/copy.bmp create mode 100644 applications/v4studio/rc/cut.bmp create mode 100644 applications/v4studio/rc/delete.bmp create mode 100644 applications/v4studio/rc/fs.bmp create mode 100644 applications/v4studio/rc/group.bmp create mode 100644 applications/v4studio/rc/ifs2d.bmp create mode 100644 applications/v4studio/rc/ils2d.bmp create mode 100644 applications/v4studio/rc/image.bmp create mode 100644 applications/v4studio/rc/layer2d.bmp create mode 100644 applications/v4studio/rc/lg.bmp create mode 100644 applications/v4studio/rc/lineproperties.bmp create mode 100644 applications/v4studio/rc/material2d.bmp create mode 100644 applications/v4studio/rc/movie.bmp create mode 100644 applications/v4studio/rc/new.bmp create mode 100644 applications/v4studio/rc/open.bmp create mode 100644 applications/v4studio/rc/orderedgroup.bmp create mode 100644 applications/v4studio/rc/paste.bmp create mode 100644 applications/v4studio/rc/paste_use.bmp create mode 100644 applications/v4studio/rc/preview.bmp create mode 100644 applications/v4studio/rc/print.bmp create mode 100644 applications/v4studio/rc/rect.bmp create mode 100644 applications/v4studio/rc/redo.bmp create mode 100644 applications/v4studio/rc/resource_orig.xrc create mode 100644 applications/v4studio/rc/rg.bmp create mode 100644 applications/v4studio/rc/save.bmp create mode 100644 applications/v4studio/rc/shape.bmp create mode 100644 applications/v4studio/rc/sound.bmp create mode 100644 applications/v4studio/rc/t2d.bmp create mode 100644 applications/v4studio/rc/text.bmp create mode 100644 applications/v4studio/rc/tm2d.bmp create mode 100644 applications/v4studio/rc/undo.bmp create mode 100644 applications/v4studio/rc/v4.bmp create mode 100644 applications/v4studio/rc/v4.ico create mode 100644 applications/v4studio/rc/xlineproperties.bmp create mode 100644 applications/v4studio/safe_include.h create mode 100644 applications/v4studio/treeIcon1.xpm create mode 100644 applications/v4studio/treeIcon2.xpm create mode 100644 applications/v4studio/treeIcon3.xpm create mode 100644 applications/v4studio/treeIcon4.xpm create mode 100644 applications/v4studio/treeIcon5.xpm create mode 100644 applications/v4studio/wxGPACPanel.cpp create mode 100644 applications/v4studio/wxGPACPanel.h create mode 100644 bin/arm_ppc02_rel/install/do.bat create mode 100644 bin/arm_ppc02_rel/install/gpac.inf create mode 100644 bin/arm_ppc02_rel/install/gpac.ini create mode 100644 bin/arm_ppc02_rel/install/readme.txt create mode 100644 bin/arm_ppc03_rel/install/do.bat create mode 100644 bin/arm_ppc03_rel/install/gpac.inf create mode 100644 bin/arm_ppc03_rel/install/gpac.ini create mode 100644 bin/arm_ppc03_rel/install/readme.txt create mode 100755 bin/gcc/libgpac.so create mode 100644 bin/w32_rel/Osmo4.ico create mode 100644 bin/w32_rel/nsis_install/gpac_installer.nsi create mode 100644 build/msevc3/GPAC.VCW create mode 100644 build/msevc3/Osmo4.vcp create mode 100644 build/msevc3/aac_in.vcp create mode 100644 build/msevc3/amr_dec.vcp create mode 100644 build/msevc3/bifs_dec.vcp create mode 100644 build/msevc3/ctx_load.vcp create mode 100644 build/msevc3/dummy_in.vcp create mode 100644 build/msevc3/ft_font.vcp create mode 100644 build/msevc3/gapi.vcp create mode 100644 build/msevc3/img_in.vcp create mode 100644 build/msevc3/isom_in.vcp create mode 100644 build/msevc3/laser_dec.vcp create mode 100644 build/msevc3/libgpac.vcp create mode 100644 build/msevc3/libgpac_dll.vcp create mode 100644 build/msevc3/mp3_in.vcp create mode 100644 build/msevc3/odf_dec.vcp create mode 100644 build/msevc3/ogg.vcp create mode 100644 build/msevc3/rtp_in.vcp create mode 100644 build/msevc3/saf_in.vcp create mode 100644 build/msevc3/soft_raster.vcp create mode 100644 build/msevc3/svg_in.vcp create mode 100644 build/msevc3/timedtext.vcp create mode 100644 build/msevc3/wav_out.vcp create mode 100644 build/msevc3/xvid_dec.vcp create mode 100644 build/msevc4/GPAC.VCW create mode 100644 build/msevc4/GPAX.VCP create mode 100644 build/msevc4/Osmo4.vcp create mode 100644 build/msevc4/aac_in.vcp create mode 100644 build/msevc4/amr_dec.vcp create mode 100644 build/msevc4/bifs_dec.vcp create mode 100644 build/msevc4/ctx_load.vcp create mode 100644 build/msevc4/dummy_in.vcp create mode 100644 build/msevc4/ffmpeg_in.vcp create mode 100644 build/msevc4/ft_font.vcp create mode 100644 build/msevc4/gapi.vcp create mode 100644 build/msevc4/img_in.vcp create mode 100644 build/msevc4/ismacryp.vcp create mode 100644 build/msevc4/isom_in.vcp create mode 100644 build/msevc4/laser_dec.vcp create mode 100644 build/msevc4/libgpac.vcp create mode 100644 build/msevc4/libgpac_dll.vcp create mode 100644 build/msevc4/mp3_in.vcp create mode 100644 build/msevc4/odf_dec.vcp create mode 100644 build/msevc4/osmophone.vcp create mode 100644 build/msevc4/rtp_in.vcp create mode 100644 build/msevc4/saf_in.vcp create mode 100644 build/msevc4/soft_rast.vcp create mode 100644 build/msevc4/svg_in.vcp create mode 100644 build/msevc4/timedtext.vcp create mode 100644 build/msevc4/wav_out.vcp create mode 100644 build/msevc4/xvid_dec.vcp create mode 100644 build/msvc6/GPAX.dsp create mode 100644 build/msvc6/Osmo4.dsp create mode 100644 build/msvc6/V4Studio.dsp create mode 100644 build/msvc6/aac_in.dsp create mode 100644 build/msvc6/ac3_in.dsp create mode 100644 build/msvc6/amr_dec.dsp create mode 100644 build/msvc6/amr_float_dec.dsp create mode 100644 build/msvc6/bifs_dec.dsp create mode 100644 build/msvc6/ctx_load.dsp create mode 100644 build/msvc6/dummy_in.dsp create mode 100644 build/msvc6/dx_hw.dsp create mode 100644 build/msvc6/ffmpeg_in.dsp create mode 100644 build/msvc6/ft_font.dsp create mode 100644 build/msvc6/gdip_raster.dsp create mode 100644 build/msvc6/gpac.dsw create mode 100644 build/msvc6/gpac_js.dsp create mode 100644 build/msvc6/img_in.dsp create mode 100644 build/msvc6/ismacryp.dsp create mode 100644 build/msvc6/isom_in.dsp create mode 100644 build/msvc6/laser_dec.dsp create mode 100644 build/msvc6/libgpac.dsp create mode 100644 build/msvc6/libgpac_dll.dsp create mode 100644 build/msvc6/mp3_in.dsp create mode 100644 build/msvc6/mp42avi.dsp create mode 100644 build/msvc6/mp4box.dsp create mode 100644 build/msvc6/mp4client.dsp create mode 100644 build/msvc6/mpegts_in.dsp create mode 100644 build/msvc6/odf_dec.dsp create mode 100644 build/msvc6/ogg.dsp create mode 100644 build/msvc6/osmozilla.dsp create mode 100644 build/msvc6/raw_out.dsp create mode 100644 build/msvc6/rtp_in.dsp create mode 100644 build/msvc6/saf_in.dsp create mode 100644 build/msvc6/sdl_out.dsp create mode 100644 build/msvc6/soft_raster.dsp create mode 100644 build/msvc6/standalone2drender.dsp create mode 100644 build/msvc6/svg_in.dsp create mode 100644 build/msvc6/timedtext.dsp create mode 100644 build/msvc6/wav_out.dsp create mode 100644 build/msvc6/wxOsmo4.dsp create mode 100644 build/msvc6/xvid_dec.dsp create mode 100644 build/msvc8/GPAX.vcproj create mode 100644 build/msvc8/Osmo4.vcproj create mode 100644 build/msvc8/V4Studio.vcproj create mode 100644 build/msvc8/aac_in.vcproj create mode 100644 build/msvc8/ac3_in.vcproj create mode 100644 build/msvc8/amr_dec.vcproj create mode 100644 build/msvc8/amr_float_dec.vcproj create mode 100644 build/msvc8/bifs_dec.vcproj create mode 100644 build/msvc8/ctx_load.vcproj create mode 100644 build/msvc8/dummy_in.vcproj create mode 100644 build/msvc8/dx_hw.vcproj create mode 100644 build/msvc8/ffmpeg_in.vcproj create mode 100644 build/msvc8/ft_font.vcproj create mode 100644 build/msvc8/gdip_raster.vcproj create mode 100644 build/msvc8/gpac.sln create mode 100644 build/msvc8/img_in.vcproj create mode 100644 build/msvc8/ismacryp.vcproj create mode 100644 build/msvc8/isom_in.vcproj create mode 100644 build/msvc8/laser_dec.vcproj create mode 100644 build/msvc8/libgpac.vcproj create mode 100644 build/msvc8/libgpac_dll.vcproj create mode 100644 build/msvc8/mp3_in.vcproj create mode 100644 build/msvc8/mp4box.vcproj create mode 100644 build/msvc8/mp4client.vcproj create mode 100644 build/msvc8/mpegts_in.vcproj create mode 100644 build/msvc8/odf_dec.vcproj create mode 100644 build/msvc8/ogg.vcproj create mode 100644 build/msvc8/osmozilla.vcproj create mode 100644 build/msvc8/raw_out.vcproj create mode 100644 build/msvc8/rtp_in.vcproj create mode 100644 build/msvc8/saf_in.vcproj create mode 100644 build/msvc8/sdl_out.vcproj create mode 100644 build/msvc8/soft_raster.vcproj create mode 100644 build/msvc8/svg_in.vcproj create mode 100644 build/msvc8/timedtext.vcproj create mode 100644 build/msvc8/wav_out.vcproj create mode 100644 build/msvc8/wxOsmo4.vcproj create mode 100644 build/msvc8/xvid_dec.vcproj create mode 100644 build/symbian/Icons.mk create mode 100644 build/symbian/aac_in.mmp create mode 100644 build/symbian/amr_in.mmp create mode 100644 build/symbian/bifs_dec.mmp create mode 100644 build/symbian/bld.inf create mode 100644 build/symbian/ctx_load.mmp create mode 100644 build/symbian/dummy_in.mmp create mode 100644 build/symbian/epoc_hw.mmp create mode 100644 build/symbian/ffmpeg_in.mmp create mode 100644 build/symbian/ft_font.mmp create mode 100644 build/symbian/gpac_module_symbianU.def create mode 100644 build/symbian/img_in.mmp create mode 100644 build/symbian/ismacryp.mmp create mode 100644 build/symbian/isom_in.mmp create mode 100644 build/symbian/laser_dec.mmp create mode 100644 build/symbian/libgpac.mmp create mode 100644 build/symbian/libgpac_symbianU.def create mode 100644 build/symbian/module_base.mmp create mode 100644 build/symbian/mp3_in.mmp create mode 100644 build/symbian/odf_dec.mmp create mode 100644 build/symbian/osmo4.mmp create mode 100644 build/symbian/rtp_in.mmp create mode 100644 build/symbian/saf_in.mmp create mode 100644 build/symbian/sis/GPAC.cfg create mode 100644 build/symbian/sis/backup_registration.xml create mode 100644 build/symbian/sis/gpac.cer create mode 100644 build/symbian/sis/gpac.key create mode 100644 build/symbian/sis/osmo4_gcce.pkg create mode 100644 build/symbian/sis/osmo4_thumb.pkg create mode 100644 build/symbian/soft_raster.mmp create mode 100644 build/symbian/svg_in.mmp create mode 100644 build/symbian/symbian_dll_entry.cpp create mode 100644 build/symbian/timedtext.mmp create mode 100644 build/symbian/xvid_dec.mmp create mode 100644 config.h create mode 100755 configure create mode 100644 doc/CODING_STYLE create mode 100644 doc/INSTALL.gcc create mode 100644 doc/INSTALL.gpe create mode 100644 doc/INSTALL.symbian create mode 100644 doc/INSTALL.w32 create mode 100644 doc/INSTALL.wCE create mode 100644 doc/ISO 639-2 codes.txt create mode 100644 doc/SceneGenerators create mode 100644 doc/configuration.html create mode 100644 doc/doxyfile create mode 100644 doc/gpac.mp4 create mode 100644 doc/ipmpx_syntax.bt create mode 100644 doc/man/gpac.1 create mode 100644 doc/man/mp42avi.1 create mode 100644 doc/man/mp4box.1 create mode 100644 doc/man/mp4client.1 create mode 100644 extra_lib/include/a52dec/a52.h create mode 100644 extra_lib/include/a52dec/mm_accel.h create mode 100644 extra_lib/include/faad/faad.h create mode 100644 extra_lib/include/faad/neaacdec.h create mode 100644 extra_lib/include/ffmpeg/avcodec.h create mode 100644 extra_lib/include/ffmpeg/avformat.h create mode 100644 extra_lib/include/ffmpeg/avio.h create mode 100644 extra_lib/include/ffmpeg/avutil.h create mode 100644 extra_lib/include/ffmpeg/common.h create mode 100644 extra_lib/include/ffmpeg/integer.h create mode 100644 extra_lib/include/ffmpeg/intfloat_readwrite.h create mode 100644 extra_lib/include/ffmpeg/log.h create mode 100644 extra_lib/include/ffmpeg/mathematics.h create mode 100644 extra_lib/include/ffmpeg/rational.h create mode 100644 extra_lib/include/freetype/freetype/cache/ftccache.h create mode 100644 extra_lib/include/freetype/freetype/cache/ftccmap.h create mode 100644 extra_lib/include/freetype/freetype/cache/ftcglyph.h create mode 100644 extra_lib/include/freetype/freetype/cache/ftcimage.h create mode 100644 extra_lib/include/freetype/freetype/cache/ftcmanag.h create mode 100644 extra_lib/include/freetype/freetype/cache/ftcsbits.h create mode 100644 extra_lib/include/freetype/freetype/cache/ftlru.h create mode 100644 extra_lib/include/freetype/freetype/config/ftconfig.h create mode 100644 extra_lib/include/freetype/freetype/config/ftheader.h create mode 100644 extra_lib/include/freetype/freetype/config/ftmodule.h create mode 100644 extra_lib/include/freetype/freetype/config/ftoption.h create mode 100644 extra_lib/include/freetype/freetype/config/ftstdlib.h create mode 100644 extra_lib/include/freetype/freetype/freetype.h create mode 100644 extra_lib/include/freetype/freetype/ftbbox.h create mode 100644 extra_lib/include/freetype/freetype/ftbdf.h create mode 100644 extra_lib/include/freetype/freetype/ftcache.h create mode 100644 extra_lib/include/freetype/freetype/ftchapters.h create mode 100644 extra_lib/include/freetype/freetype/fterrdef.h create mode 100644 extra_lib/include/freetype/freetype/fterrors.h create mode 100644 extra_lib/include/freetype/freetype/ftglyph.h create mode 100644 extra_lib/include/freetype/freetype/ftgzip.h create mode 100644 extra_lib/include/freetype/freetype/ftimage.h create mode 100644 extra_lib/include/freetype/freetype/ftincrem.h create mode 100644 extra_lib/include/freetype/freetype/ftlist.h create mode 100644 extra_lib/include/freetype/freetype/ftmac.h create mode 100644 extra_lib/include/freetype/freetype/ftmm.h create mode 100644 extra_lib/include/freetype/freetype/ftmoderr.h create mode 100644 extra_lib/include/freetype/freetype/ftmodule.h create mode 100644 extra_lib/include/freetype/freetype/ftoutln.h create mode 100644 extra_lib/include/freetype/freetype/ftpfr.h create mode 100644 extra_lib/include/freetype/freetype/ftrender.h create mode 100644 extra_lib/include/freetype/freetype/ftsizes.h create mode 100644 extra_lib/include/freetype/freetype/ftsnames.h create mode 100644 extra_lib/include/freetype/freetype/ftstroker.h create mode 100644 extra_lib/include/freetype/freetype/ftsynth.h create mode 100644 extra_lib/include/freetype/freetype/ftsysio.h create mode 100644 extra_lib/include/freetype/freetype/ftsysmem.h create mode 100644 extra_lib/include/freetype/freetype/ftsystem.h create mode 100644 extra_lib/include/freetype/freetype/fttrigon.h create mode 100644 extra_lib/include/freetype/freetype/fttypes.h create mode 100644 extra_lib/include/freetype/freetype/ftwinfnt.h create mode 100644 extra_lib/include/freetype/freetype/ftxf86.h create mode 100644 extra_lib/include/freetype/freetype/internal/autohint.h create mode 100644 extra_lib/include/freetype/freetype/internal/bdftypes.h create mode 100644 extra_lib/include/freetype/freetype/internal/cfftypes.h create mode 100644 extra_lib/include/freetype/freetype/internal/fnttypes.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftcalc.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftcore.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftdebug.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftdriver.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftexcept.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftgloadr.h create mode 100644 extra_lib/include/freetype/freetype/internal/fthash.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftmemory.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftobject.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftobjs.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftserv.h create mode 100644 extra_lib/include/freetype/freetype/internal/ftstream.h create mode 100644 extra_lib/include/freetype/freetype/internal/fttrace.h create mode 100644 extra_lib/include/freetype/freetype/internal/internal.h create mode 100644 extra_lib/include/freetype/freetype/internal/pcftypes.h create mode 100644 extra_lib/include/freetype/freetype/internal/pfr.h create mode 100644 extra_lib/include/freetype/freetype/internal/psaux.h create mode 100644 extra_lib/include/freetype/freetype/internal/pshints.h create mode 100644 extra_lib/include/freetype/freetype/internal/psnames.h create mode 100644 extra_lib/include/freetype/freetype/internal/services/svbdf.h create mode 100644 extra_lib/include/freetype/freetype/internal/services/svgldict.h create mode 100644 extra_lib/include/freetype/freetype/internal/services/svmm.h create mode 100644 extra_lib/include/freetype/freetype/internal/services/svpfr.h create mode 100644 extra_lib/include/freetype/freetype/internal/services/svpostnm.h create mode 100644 extra_lib/include/freetype/freetype/internal/services/svpscmap.h create mode 100644 extra_lib/include/freetype/freetype/internal/services/svpsinfo.h create mode 100644 extra_lib/include/freetype/freetype/internal/services/svsfnt.h create mode 100644 extra_lib/include/freetype/freetype/internal/services/svwinfnt.h create mode 100644 extra_lib/include/freetype/freetype/internal/services/svxf86nm.h create mode 100644 extra_lib/include/freetype/freetype/internal/sfnt.h create mode 100644 extra_lib/include/freetype/freetype/internal/t1types.h create mode 100644 extra_lib/include/freetype/freetype/internal/t42types.h create mode 100644 extra_lib/include/freetype/freetype/internal/tttypes.h create mode 100644 extra_lib/include/freetype/freetype/t1tables.h create mode 100644 extra_lib/include/freetype/freetype/ttnameid.h create mode 100644 extra_lib/include/freetype/freetype/tttables.h create mode 100644 extra_lib/include/freetype/freetype/tttags.h create mode 100644 extra_lib/include/freetype/ft2build.h create mode 100644 extra_lib/include/jpeg/jconfig.h create mode 100644 extra_lib/include/jpeg/jmorecfg.h create mode 100644 extra_lib/include/jpeg/jpeglib.h create mode 100644 extra_lib/include/js/jsapi.h create mode 100644 extra_lib/include/js/jsautocfg.h create mode 100644 extra_lib/include/js/jscompat.h create mode 100644 extra_lib/include/js/jscpucfg.h create mode 100644 extra_lib/include/js/jsdbgapi.h create mode 100644 extra_lib/include/js/jslong.h create mode 100644 extra_lib/include/js/jsosdep.h create mode 100644 extra_lib/include/js/jsotypes.h create mode 100644 extra_lib/include/js/jsproto.tbl create mode 100644 extra_lib/include/js/jspubtd.h create mode 100644 extra_lib/include/js/jstypes.h create mode 100644 extra_lib/include/js/jsxdrapi.h create mode 100644 extra_lib/include/mad/mad.h create mode 100644 extra_lib/include/ogg/ogg.h create mode 100644 extra_lib/include/ogg/os_types.h create mode 100644 extra_lib/include/openjpeg/openjpeg.h create mode 100644 extra_lib/include/png/png.h create mode 100644 extra_lib/include/png/pngconf.h create mode 100644 extra_lib/include/theora/theora.h create mode 100644 extra_lib/include/vorbis/codec.h create mode 100644 extra_lib/include/vorbis/vorbisenc.h create mode 100644 extra_lib/include/vorbis/vorbisfile.h create mode 100644 extra_lib/include/xvid/xvid.h create mode 100644 extra_lib/include/zlib/zconf.h create mode 100644 extra_lib/include/zlib/zlib.h create mode 100644 extra_lib/lib/arm_ppc02_deb/dummy create mode 100644 extra_lib/lib/arm_ppc02_rel/dummy create mode 100644 extra_lib/lib/arm_ppc03_deb/dummy create mode 100644 extra_lib/lib/arm_ppc03_rel/dummy create mode 100644 extra_lib/lib/gcc/dummy create mode 100644 extra_lib/lib/w32_deb/dummy create mode 100644 extra_lib/lib/w32_rel/dummy create mode 100644 gpac.spec create mode 100644 include/gpac/avparse.h create mode 100644 include/gpac/base_coding.h create mode 100644 include/gpac/bifs.h create mode 100644 include/gpac/bifsengine.h create mode 100644 include/gpac/bitstream.h create mode 100644 include/gpac/color.h create mode 100644 include/gpac/compositor.h create mode 100644 include/gpac/config_file.h create mode 100644 include/gpac/constants.h create mode 100644 include/gpac/crypt.h create mode 100644 include/gpac/download.h create mode 100644 include/gpac/esi.h create mode 100644 include/gpac/events.h create mode 100644 include/gpac/ietf.h create mode 100644 include/gpac/internal/avilib.h create mode 100644 include/gpac/internal/bifs_dev.h create mode 100644 include/gpac/internal/bifs_tables.h create mode 100644 include/gpac/internal/camera.h create mode 100644 include/gpac/internal/compositor_dev.h create mode 100644 include/gpac/internal/config_static.h create mode 100644 include/gpac/internal/crypt_dev.h create mode 100644 include/gpac/internal/ietf_dev.h create mode 100644 include/gpac/internal/isomedia_dev.h create mode 100644 include/gpac/internal/laser_dev.h create mode 100644 include/gpac/internal/media_dev.h create mode 100644 include/gpac/internal/mesh.h create mode 100644 include/gpac/internal/odf_dev.h create mode 100644 include/gpac/internal/ogg.h create mode 100644 include/gpac/internal/scenegraph_dev.h create mode 100644 include/gpac/internal/swf_dev.h create mode 100644 include/gpac/internal/terminal_dev.h create mode 100644 include/gpac/internal/vobsub.h create mode 100644 include/gpac/ismacryp.h create mode 100644 include/gpac/iso639.h create mode 100644 include/gpac/isomedia.h create mode 100644 include/gpac/laser.h create mode 100644 include/gpac/list.h create mode 100644 include/gpac/math.h create mode 100644 include/gpac/media_tools.h create mode 100644 include/gpac/mediaobject.h create mode 100644 include/gpac/module.h create mode 100644 include/gpac/modules/audio_out.h create mode 100644 include/gpac/modules/codec.h create mode 100644 include/gpac/modules/font.h create mode 100644 include/gpac/modules/ipmp.h create mode 100644 include/gpac/modules/js_usr.h create mode 100644 include/gpac/modules/raster2d.h create mode 100644 include/gpac/modules/service.h create mode 100644 include/gpac/modules/term_ext.h create mode 100644 include/gpac/modules/video_out.h create mode 100644 include/gpac/mpeg4_odf.h create mode 100644 include/gpac/mpegts.h create mode 100644 include/gpac/network.h create mode 100644 include/gpac/nodes_mpeg4.h create mode 100644 include/gpac/nodes_svg.h create mode 100644 include/gpac/nodes_x3d.h create mode 100644 include/gpac/nodes_xbl.h create mode 100644 include/gpac/options.h create mode 100644 include/gpac/path2d.h create mode 100644 include/gpac/scene_manager.h create mode 100644 include/gpac/scenegraph.h create mode 100644 include/gpac/scenegraph_svg.h create mode 100644 include/gpac/scenegraph_vrml.h create mode 100644 include/gpac/setup.h create mode 100644 include/gpac/svg_types.h create mode 100644 include/gpac/sync_layer.h create mode 100644 include/gpac/term_info.h create mode 100644 include/gpac/terminal.h create mode 100644 include/gpac/thread.h create mode 100644 include/gpac/token.h create mode 100644 include/gpac/tools.h create mode 100644 include/gpac/user.h create mode 100644 include/gpac/utf.h create mode 100644 include/gpac/xml.h create mode 100644 modules/Makefile create mode 100644 modules/aac_in/Makefile create mode 100644 modules/aac_in/aac_in.c create mode 100644 modules/aac_in/aac_in.def create mode 100644 modules/aac_in/faad_dec.c create mode 100644 modules/ac3_in/Makefile create mode 100644 modules/ac3_in/ac3_in.c create mode 100644 modules/ac3_in/ac3_in.def create mode 100644 modules/ac3_in/liba52_dec.c create mode 100644 modules/alsa/Makefile create mode 100644 modules/alsa/alsa.c create mode 100644 modules/amr_dec/Makefile create mode 100644 modules/amr_dec/amr_dec.c create mode 100644 modules/amr_dec/amr_dec.def create mode 100644 modules/amr_dec/amr_in.c create mode 100644 modules/amr_dec/amr_nb/typedefs.h create mode 100644 modules/amr_float_dec/Makefile create mode 100644 modules/amr_float_dec/amr_float_dec.c create mode 100644 modules/amr_float_dec/amr_float_dec.def create mode 100644 modules/amr_float_dec/amr_nb_ft/dummy create mode 100644 modules/amr_float_dec/amr_wb_ft/dummy create mode 100644 modules/bifs_dec/Makefile create mode 100644 modules/bifs_dec/bifs_dec.c create mode 100644 modules/bifs_dec/bifs_dec.def create mode 100644 modules/ctx_load/Makefile create mode 100644 modules/ctx_load/ctx_load.c create mode 100644 modules/ctx_load/ctx_load.def create mode 100644 modules/dummy_in/Makefile create mode 100644 modules/dummy_in/dummy_in.c create mode 100644 modules/dummy_in/dummy_in.def create mode 100644 modules/dx_hw/Makefile create mode 100644 modules/dx_hw/collide.cur create mode 100644 modules/dx_hw/copy_pixels.c create mode 100644 modules/dx_hw/dx_2d.c create mode 100644 modules/dx_hw/dx_audio.c create mode 100644 modules/dx_hw/dx_hw.def create mode 100644 modules/dx_hw/dx_hw.h create mode 100644 modules/dx_hw/dx_hw.rc create mode 100644 modules/dx_hw/dx_video.c create mode 100644 modules/dx_hw/dx_window.c create mode 100644 modules/dx_hw/hand.cur create mode 100644 modules/dx_hw/resource.h create mode 100644 modules/epoc_hw/epoc_aout.cpp create mode 100644 modules/epoc_hw/epoc_codec.cpp create mode 100644 modules/epoc_hw/epoc_vout.cpp create mode 100644 modules/ffmpeg_in/Makefile create mode 100644 modules/ffmpeg_in/ffmpeg_decode.c create mode 100644 modules/ffmpeg_in/ffmpeg_demux.c create mode 100644 modules/ffmpeg_in/ffmpeg_in.def create mode 100644 modules/ffmpeg_in/ffmpeg_in.h create mode 100644 modules/ffmpeg_in/ffmpeg_load.c create mode 100644 modules/ft_font/Makefile create mode 100644 modules/ft_font/ft_font.c create mode 100644 modules/ft_font/ft_font.def create mode 100644 modules/ft_font/ft_font.h create mode 100644 modules/gapi/gapi.cpp create mode 100644 modules/gapi/gapi.def create mode 100644 modules/gapi/gapi.h create mode 100644 modules/gdip_raster/gdip_font.cpp create mode 100644 modules/gdip_raster/gdip_grad.cpp create mode 100644 modules/gdip_raster/gdip_priv.h create mode 100644 modules/gdip_raster/gdip_rend.cpp create mode 100644 modules/gdip_raster/gdip_rend.def create mode 100644 modules/gdip_raster/gdip_texture.cpp create mode 100644 modules/gpac_js/Makefile create mode 100644 modules/gpac_js/gpac_js.c create mode 100644 modules/gpac_js/gpac_js.def create mode 100644 modules/img_in/Makefile create mode 100644 modules/img_in/bmp_dec.c create mode 100644 modules/img_in/img_dec.c create mode 100644 modules/img_in/img_in.c create mode 100644 modules/img_in/img_in.def create mode 100644 modules/img_in/img_in.h create mode 100644 modules/img_in/jp2_dec.c create mode 100644 modules/img_in/jpeg_dec.c create mode 100644 modules/img_in/png_dec.c create mode 100644 modules/ismacryp/Makefile create mode 100644 modules/ismacryp/ismacryp.c create mode 100644 modules/ismacryp/ismacryp.def create mode 100644 modules/isom_in/Makefile create mode 100644 modules/isom_in/cache.c create mode 100644 modules/isom_in/isom_in.def create mode 100644 modules/isom_in/isom_in.h create mode 100644 modules/isom_in/load.c create mode 100644 modules/isom_in/read.c create mode 100644 modules/isom_in/read_ch.c create mode 100644 modules/jack/Makefile create mode 100644 modules/jack/jack.c create mode 100644 modules/laser_dec/Makefile create mode 100644 modules/laser_dec/laser_dec.c create mode 100644 modules/laser_dec/laser_dec.def create mode 100644 modules/mp3_in/Makefile create mode 100644 modules/mp3_in/mad_dec.c create mode 100644 modules/mp3_in/mp3_in.c create mode 100644 modules/mp3_in/mp3_in.def create mode 100644 modules/mpegts_in/Makefile create mode 100644 modules/mpegts_in/mpegts_in.c create mode 100644 modules/mpegts_in/mpegts_in.def create mode 100644 modules/odf_dec/Makefile create mode 100644 modules/odf_dec/odf_dec.c create mode 100644 modules/odf_dec/odf_dec.def create mode 100644 modules/ogg/Makefile create mode 100644 modules/ogg/ogg.def create mode 100644 modules/ogg/ogg_in.c create mode 100644 modules/ogg/ogg_in.h create mode 100644 modules/ogg/ogg_load.c create mode 100644 modules/ogg/theora_dec.c create mode 100644 modules/ogg/vorbis_dec.c create mode 100644 modules/oss_audio/Makefile create mode 100644 modules/oss_audio/oss.c create mode 100644 modules/pulseaudio/Makefile create mode 100644 modules/pulseaudio/pulseaudio.c create mode 100644 modules/raw_out/Makefile create mode 100644 modules/raw_out/raw_out.def create mode 100644 modules/raw_out/raw_video.c create mode 100644 modules/rtp_in/Makefile create mode 100644 modules/rtp_in/rtp_in.c create mode 100644 modules/rtp_in/rtp_in.def create mode 100644 modules/rtp_in/rtp_in.h create mode 100644 modules/rtp_in/rtp_session.c create mode 100644 modules/rtp_in/rtp_signaling.c create mode 100644 modules/rtp_in/rtp_stream.c create mode 100644 modules/rtp_in/sdp_fetch.c create mode 100644 modules/rtp_in/sdp_load.c create mode 100644 modules/saf_in/Makefile create mode 100644 modules/saf_in/saf_in.c create mode 100644 modules/saf_in/saf_in.def create mode 100644 modules/sdl_out/Makefile create mode 100644 modules/sdl_out/audio.c create mode 100644 modules/sdl_out/cursors.c create mode 100644 modules/sdl_out/sdl_out.c create mode 100644 modules/sdl_out/sdl_out.def create mode 100644 modules/sdl_out/sdl_out.h create mode 100644 modules/sdl_out/video.c create mode 100644 modules/sdl_out/video2d.c create mode 100644 modules/soft_raster/Makefile create mode 100644 modules/soft_raster/ftgrays.c create mode 100644 modules/soft_raster/rast_soft.def create mode 100644 modules/soft_raster/rast_soft.h create mode 100644 modules/soft_raster/raster_565.c create mode 100644 modules/soft_raster/raster_argb.c create mode 100644 modules/soft_raster/raster_load.c create mode 100644 modules/soft_raster/raster_rgb.c create mode 100644 modules/soft_raster/stencil.c create mode 100644 modules/soft_raster/surface.c create mode 100644 modules/svg_in/Makefile create mode 100644 modules/svg_in/svg_in.c create mode 100644 modules/svg_in/svg_in.def create mode 100644 modules/timedtext/Makefile create mode 100644 modules/timedtext/timedtext.def create mode 100644 modules/timedtext/timedtext_dec.c create mode 100644 modules/timedtext/timedtext_in.c create mode 100644 modules/wav_out/Makefile create mode 100644 modules/wav_out/wav_out.c create mode 100644 modules/wav_out/wav_out.def create mode 100644 modules/x11_out/Makefile create mode 100644 modules/x11_out/x11_out.c create mode 100644 modules/x11_out/x11_out.h create mode 100644 modules/xvid_dec/Makefile create mode 100644 modules/xvid_dec/xvid_dec.c create mode 100644 modules/xvid_dec/xvid_dec.def create mode 100644 modules/xvid_dec/xvid_dec_wce.cpp create mode 100644 modules/xvid_dec/xvid_wce/AUTHORS create mode 100644 modules/xvid_dec/xvid_wce/CodecAPI.cpp create mode 100644 modules/xvid_dec/xvid_wce/LICENSE create mode 100644 modules/xvid_dec/xvid_wce/README create mode 100644 modules/xvid_dec/xvid_wce/ReadMe.txt create mode 100644 modules/xvid_dec/xvid_wce/Rules.h create mode 100644 modules/xvid_dec/xvid_wce/TODO create mode 100644 modules/xvid_dec/xvid_wce/bitstream.cpp create mode 100644 modules/xvid_dec/xvid_wce/bitstream.h create mode 100644 modules/xvid_dec/xvid_wce/decoder.cpp create mode 100644 modules/xvid_dec/xvid_wce/decoder.h create mode 100644 modules/xvid_dec/xvid_wce/font.cpp create mode 100644 modules/xvid_dec/xvid_wce/global.h create mode 100644 modules/xvid_dec/xvid_wce/gmc.cpp create mode 100644 modules/xvid_dec/xvid_wce/gmc.h create mode 100644 modules/xvid_dec/xvid_wce/idct.cpp create mode 100644 modules/xvid_dec/xvid_wce/image.cpp create mode 100644 modules/xvid_dec/xvid_wce/image.h create mode 100644 modules/xvid_dec/xvid_wce/interpolate8x8.cpp create mode 100644 modules/xvid_dec/xvid_wce/interpolate8x8.h create mode 100644 modules/xvid_dec/xvid_wce/mbcoding.cpp create mode 100644 modules/xvid_dec/xvid_wce/mbprediction.cpp create mode 100644 modules/xvid_dec/xvid_wce/mbprediction.h create mode 100644 modules/xvid_dec/xvid_wce/mem_align.cpp create mode 100644 modules/xvid_dec/xvid_wce/mem_align.h create mode 100644 modules/xvid_dec/xvid_wce/mem_transfer.cpp create mode 100644 modules/xvid_dec/xvid_wce/mem_transfer.h create mode 100644 modules/xvid_dec/xvid_wce/note create mode 100644 modules/xvid_dec/xvid_wce/portab.h create mode 100644 modules/xvid_dec/xvid_wce/qpel.inl create mode 100644 modules/xvid_dec/xvid_wce/qpel_tab.cpp create mode 100644 modules/xvid_dec/xvid_wce/quant.h create mode 100644 modules/xvid_dec/xvid_wce/quant_h263.cpp create mode 100644 modules/xvid_dec/xvid_wce/quant_matrix.cpp create mode 100644 modules/xvid_dec/xvid_wce/quant_matrix.h create mode 100644 modules/xvid_dec/xvid_wce/quant_mpeg.cpp create mode 100644 modules/xvid_dec/xvid_wce/reduced.cpp create mode 100644 modules/xvid_dec/xvid_wce/reduced.h create mode 100644 modules/xvid_dec/xvid_wce/vlc_codes.h create mode 100644 modules/xvid_dec/xvid_wce/xvid.cpp create mode 100644 modules/xvid_dec/xvid_wce/xvid.h create mode 100644 modules/xvid_dec/xvid_wce/xvid_ppc.asm create mode 100644 regression_tests/_mozilla_ie_action.html create mode 100644 regression_tests/_mozilla_ie_simple.html create mode 100644 regression_tests/_ppc_action.html create mode 100644 regression_tests/_ppc_simple.html create mode 100644 regression_tests/auxiliary_files/count_arabic.mp3 create mode 100644 regression_tests/auxiliary_files/count_english.mp3 create mode 100644 regression_tests/auxiliary_files/count_french.mp3 create mode 100644 regression_tests/auxiliary_files/count_german.mp3 create mode 100644 regression_tests/auxiliary_files/count_italian.mp3 create mode 100644 regression_tests/auxiliary_files/count_spanish.mp3 create mode 100644 regression_tests/auxiliary_files/count_video.cmp create mode 100644 regression_tests/auxiliary_files/enst_audio.aac create mode 100644 regression_tests/auxiliary_files/enst_video.h264 create mode 100644 regression_tests/auxiliary_files/index2batch.xslt create mode 100644 regression_tests/auxiliary_files/index2html.xslt create mode 100644 regression_tests/auxiliary_files/index2sh.xslt create mode 100644 regression_tests/auxiliary_files/logo.jpg create mode 100644 regression_tests/auxiliary_files/logo.png create mode 100644 regression_tests/auxiliary_files/nefertiti.wrl create mode 100644 regression_tests/auxiliary_files/sky.jpg create mode 100644 regression_tests/auxiliary_files/subtitle.srt create mode 100644 regression_tests/auxiliary_files/subtitle_fr.srt create mode 100644 regression_tests/auxiliary_files/svg2html.xslt create mode 100644 regression_tests/auxiliary_files/x3d2html.xslt create mode 100644 regression_tests/auxiliary_files/xmt2html.xslt create mode 100644 regression_tests/bifs-2D-background-background2D-bind.bt create mode 100644 regression_tests/bifs-2D-background-background2D-image.bt create mode 100644 regression_tests/bifs-2D-background-background2D-layer2D.bt create mode 100644 regression_tests/bifs-2D-background-background2D-movie.bt create mode 100644 regression_tests/bifs-2D-background-background2D-url-change.bt create mode 100644 regression_tests/bifs-2D-interactivity-discsensor.bt create mode 100644 regression_tests/bifs-2D-interactivity-htk-sensor.bt create mode 100644 regression_tests/bifs-2D-interactivity-keysensor.bt create mode 100644 regression_tests/bifs-2D-interactivity-mousesensor.bt create mode 100644 regression_tests/bifs-2D-interactivity-nested-sensors.bt create mode 100644 regression_tests/bifs-2D-interactivity-planesensor2D.bt create mode 100644 regression_tests/bifs-2D-interactivity-proximitysensor2D.bt create mode 100644 regression_tests/bifs-2D-interactivity-stringsensor.bt create mode 100644 regression_tests/bifs-2D-interactivity-touchsensor-4states.bt create mode 100644 regression_tests/bifs-2D-interactivity-touchsensor-hitpoint.bt create mode 100644 regression_tests/bifs-2D-interactivity-touchsensor-isactive-exposedfield.bt create mode 100644 regression_tests/bifs-2D-interactivity-touchsensor-isactive.bt create mode 100644 regression_tests/bifs-2D-interactivity-touchsensor-isover.bt create mode 100644 regression_tests/bifs-2D-interactivity-touchsensor-move_over.bt create mode 100644 regression_tests/bifs-2D-painting-colortransform-alpha.bt create mode 100644 regression_tests/bifs-2D-painting-colortransform-bitmap.bt create mode 100644 regression_tests/bifs-2D-painting-colortransform-color.bt create mode 100644 regression_tests/bifs-2D-painting-lineproperties.bt create mode 100644 regression_tests/bifs-2D-painting-material2D.bt create mode 100644 regression_tests/bifs-2D-painting-xlineproperties-cap.bt create mode 100644 regression_tests/bifs-2D-painting-xlineproperties-compositetexture2D.bt create mode 100644 regression_tests/bifs-2D-painting-xlineproperties-dash.bt create mode 100644 regression_tests/bifs-2D-painting-xlineproperties-imagetexture.bt create mode 100644 regression_tests/bifs-2D-painting-xlineproperties-join.bt create mode 100644 regression_tests/bifs-2D-painting-xlineproperties-lineargradient.bt create mode 100644 regression_tests/bifs-2D-painting-xlineproperties-radialgradient.bt create mode 100644 regression_tests/bifs-2D-painting-xlineproperties-scalable.bt create mode 100644 regression_tests/bifs-2D-painting-xlineproperties-transparent.bt create mode 100644 regression_tests/bifs-2D-positioning-clipper2D.bt create mode 100644 regression_tests/bifs-2D-positioning-form-align-center.bt create mode 100644 regression_tests/bifs-2D-positioning-form-align-horiz.bt create mode 100644 regression_tests/bifs-2D-positioning-form-align-vert.bt create mode 100644 regression_tests/bifs-2D-positioning-form-spread-horiz.bt create mode 100644 regression_tests/bifs-2D-positioning-form-spread-vert.bt create mode 100644 regression_tests/bifs-2D-positioning-layer2D.bt create mode 100644 regression_tests/bifs-2D-positioning-layer2d-in-layer2d.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-horiz-ltr-nowrap.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-horiz-ltr-wrap-btt.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-horiz-ltr-wrap-ttb.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-horiz-rtl-nowrap.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-horiz-rtl-wrap-btt.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-horiz-rtl-wrap-ttb.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-horiz-text.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-scroll-child.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-scroll-full.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-scroll-modes-horiz.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-scroll-modes-vert.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-scroll-on-off.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-vert-btt-nowrap.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-vert-btt-wrap-ltr.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-vert-btt-wrap-rtl.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-vert-ttb-nowrap.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-vert-ttb-wrap-ltr.bt create mode 100644 regression_tests/bifs-2D-positioning-layout-vert-ttb-wrap-rtl.bt create mode 100644 regression_tests/bifs-2D-positioning-orderedgroup.bt create mode 100644 regression_tests/bifs-2D-positioning-pathlayout.bt create mode 100644 regression_tests/bifs-2D-positioning-transform2D.bt create mode 100644 regression_tests/bifs-2D-positioning-transformmatrix2D.bt create mode 100644 regression_tests/bifs-2D-shapes-all.bt create mode 100644 regression_tests/bifs-2D-shapes-indexfaceset2D.bt create mode 100644 regression_tests/bifs-2D-shapes-indexlineset2D.bt create mode 100644 regression_tests/bifs-2D-shapes-pointset2D.bt create mode 100644 regression_tests/bifs-2D-shapes-xcurve2D.bt create mode 100644 regression_tests/bifs-2D-texturing-compositetexture2D-background.bt create mode 100644 regression_tests/bifs-2D-texturing-compositetexture2D-bitmap.bt create mode 100644 regression_tests/bifs-2D-texturing-compositetexture2D-transparent.bt create mode 100644 regression_tests/bifs-2D-texturing-gradients-text.bt create mode 100644 regression_tests/bifs-2D-texturing-gradients-transparent.bt create mode 100644 regression_tests/bifs-2D-texturing-imagetexture-shapes.bt create mode 100644 regression_tests/bifs-2D-texturing-lineargradient-simple.bt create mode 100644 regression_tests/bifs-2D-texturing-lineargradient-spread.bt create mode 100644 regression_tests/bifs-2D-texturing-movietexture-shapes.bt create mode 100644 regression_tests/bifs-2D-texturing-pixeltexture.bt create mode 100644 regression_tests/bifs-2D-texturing-radialgradient-simple.bt create mode 100644 regression_tests/bifs-2D-texturing-radialgradient-spread.bt create mode 100644 regression_tests/bifs-2D-texturing-texturetransform-base.bt create mode 100644 regression_tests/bifs-2D-texturing-texturetransform-interact.bt create mode 100644 regression_tests/bifs-2D-texturing-texturetransform-transformmatrix2D.bt create mode 100644 regression_tests/bifs-2D-viewport-complete.bt create mode 100644 regression_tests/bifs-2D-viewport-simple.bt create mode 100644 regression_tests/bifs-3D-background-images.bt create mode 100644 regression_tests/bifs-3D-background.bt create mode 100644 regression_tests/bifs-3D-interactivity-collision-proxy.bt create mode 100644 regression_tests/bifs-3D-interactivity-collision.bt create mode 100644 regression_tests/bifs-3D-interactivity-cylindersensor.bt create mode 100644 regression_tests/bifs-3D-interactivity-planesensor.bt create mode 100644 regression_tests/bifs-3D-interactivity-proximitysensor.bt create mode 100644 regression_tests/bifs-3D-interactivity-spheresensor.bt create mode 100644 regression_tests/bifs-3D-interactivity-visibilitysensor.bt create mode 100644 regression_tests/bifs-3D-lighting-directionalLight.bt create mode 100644 regression_tests/bifs-3D-lighting-fog.bt create mode 100644 regression_tests/bifs-3D-lighting-pointlight.bt create mode 100644 regression_tests/bifs-3D-lighting-spotlight.bt create mode 100644 regression_tests/bifs-3D-positioning-billboard-viewer-alignment.bt create mode 100644 regression_tests/bifs-3D-positioning-billboard.bt create mode 100644 regression_tests/bifs-3D-positioning-gravity.bt create mode 100644 regression_tests/bifs-3D-positioning-layer3D-views.bt create mode 100644 regression_tests/bifs-3D-positioning-layer3D.bt create mode 100644 regression_tests/bifs-3D-positioning-lod.bt create mode 100644 regression_tests/bifs-3D-positioning-transform.bt create mode 100644 regression_tests/bifs-3D-shapes-box-transparent.bt create mode 100644 regression_tests/bifs-3D-shapes-box.bt create mode 100644 regression_tests/bifs-3D-shapes-cone.bt create mode 100644 regression_tests/bifs-3D-shapes-cylinder.bt create mode 100644 regression_tests/bifs-3D-shapes-elevationgrid.bt create mode 100644 regression_tests/bifs-3D-shapes-extrusion.bt create mode 100644 regression_tests/bifs-3D-shapes-indexedfaceset.bt create mode 100644 regression_tests/bifs-3D-shapes-indexedlineset.bt create mode 100644 regression_tests/bifs-3D-shapes-nonlineardeformer.bt create mode 100644 regression_tests/bifs-3D-shapes-pointset.bt create mode 100644 regression_tests/bifs-3D-texturing-box-transparent.bt create mode 100644 regression_tests/bifs-3D-texturing-box-video.bt create mode 100644 regression_tests/bifs-3D-texturing-box.bt create mode 100644 regression_tests/bifs-3D-texturing-compositetexture3D-bitmap.bt create mode 100644 regression_tests/bifs-3D-texturing-compositetexture3D-box.bt create mode 100644 regression_tests/bifs-3D-texturing-cone-transparent.bt create mode 100644 regression_tests/bifs-3D-texturing-cone.bt create mode 100644 regression_tests/bifs-3D-texturing-cylinder-transparent.bt create mode 100644 regression_tests/bifs-3D-texturing-cylinder.bt create mode 100644 regression_tests/bifs-3D-texturing-transform-box.bt create mode 100644 regression_tests/bifs-3D-texturing-transform-matrix-box.bt create mode 100644 regression_tests/bifs-3D-viewpoint-anim.bt create mode 100644 regression_tests/bifs-3D-viewpoint-bind-jump.bt create mode 100644 regression_tests/bifs-3D-viewpoint-bind.bt create mode 100644 regression_tests/bifs-3D-viewpoint-ortho-bind.bt create mode 100644 regression_tests/bifs-bitmap-image-meter-metrics.bt create mode 100644 regression_tests/bifs-bitmap-image-pixel-metrics.bt create mode 100644 regression_tests/bifs-bitmap-image-resizing.bt create mode 100644 regression_tests/bifs-bitmap-movie-materialkey.bt create mode 100644 regression_tests/bifs-bitmap-movie.bt create mode 100644 regression_tests/bifs-bitmap-video-resizing.bt create mode 100644 regression_tests/bifs-command-animated-osmo4logo.bt create mode 100644 regression_tests/bifs-command-delete-index.bt create mode 100644 regression_tests/bifs-command-delete-node.bt create mode 100644 regression_tests/bifs-command-delete-route.bt create mode 100644 regression_tests/bifs-command-global-qp.bt create mode 100644 regression_tests/bifs-command-insert-index.bt create mode 100644 regression_tests/bifs-command-insert-node.bt create mode 100644 regression_tests/bifs-command-insert-nodedef.bt create mode 100644 regression_tests/bifs-command-insert-route.bt create mode 100644 regression_tests/bifs-command-multiple-replace-field.bt create mode 100644 regression_tests/bifs-command-multiple-replace-index.bt create mode 100644 regression_tests/bifs-command-node-delete-ex.bt create mode 100644 regression_tests/bifs-command-proto-delete.bt create mode 100644 regression_tests/bifs-command-proto-insert.bt create mode 100644 regression_tests/bifs-command-protolist-delete.bt create mode 100644 regression_tests/bifs-command-quantification.bt create mode 100644 regression_tests/bifs-command-replace-field.bt create mode 100644 regression_tests/bifs-command-replace-index.bt create mode 100644 regression_tests/bifs-command-replace-node-null.bt create mode 100644 regression_tests/bifs-command-replace-node.bt create mode 100644 regression_tests/bifs-command-replace-route.bt create mode 100644 regression_tests/bifs-command-replace-scene-null.bt create mode 100644 regression_tests/bifs-command-replace-scene.bt create mode 100644 regression_tests/bifs-command-route-add-children.bt create mode 100644 regression_tests/bifs-command-route-children.bt create mode 100644 regression_tests/bifs-command-route-node-exposedfield.bt create mode 100644 regression_tests/bifs-command-route-node.bt create mode 100644 regression_tests/bifs-command-route-remove-children.bt create mode 100644 regression_tests/bifs-externproto-forestgump-lib.bt create mode 100644 regression_tests/bifs-externproto-forestgump.bt create mode 100644 regression_tests/bifs-externproto-mfurl-lib.bt create mode 100644 regression_tests/bifs-externproto-mfurl.bt create mode 100644 regression_tests/bifs-externproto-nood-lib.bt create mode 100644 regression_tests/bifs-externproto-nood.bt create mode 100644 regression_tests/bifs-externproto-simple-lib.bt create mode 100644 regression_tests/bifs-externproto-simple.bt create mode 100644 regression_tests/bifs-game-arrange.bt create mode 100644 regression_tests/bifs-game-breakout.bt create mode 100644 regression_tests/bifs-game-bubble.bt create mode 100644 regression_tests/bifs-game-minesweeper.bt create mode 100644 regression_tests/bifs-game-othello.bt create mode 100644 regression_tests/bifs-interpolation-colorinterpolator.bt create mode 100644 regression_tests/bifs-interpolation-coordinateinterpolator2D.bt create mode 100644 regression_tests/bifs-interpolation-positionanimator.bt create mode 100644 regression_tests/bifs-interpolation-positionanimator2D.bt create mode 100644 regression_tests/bifs-interpolation-positioninterpolator-position.bt create mode 100644 regression_tests/bifs-interpolation-positioninterpolator-size.bt create mode 100644 regression_tests/bifs-interpolation-positioninterpolator2D-position.bt create mode 100644 regression_tests/bifs-interpolation-positioninterpolator2D-size.bt create mode 100644 regression_tests/bifs-interpolation-scalaranimator.bt create mode 100644 regression_tests/bifs-interpolation-scalarinterpolator.bt create mode 100644 regression_tests/bifs-interpolation-timesensor-enabled.bt create mode 100644 regression_tests/bifs-interpolation-timesensor-starttime_norestart.bt create mode 100644 regression_tests/bifs-interpolation-timesensor-starttime_restart.bt create mode 100644 regression_tests/bifs-interpolation-valuator-sftime.bt create mode 100644 regression_tests/bifs-linking-anchor-mp4-next.bt create mode 100644 regression_tests/bifs-linking-anchor-mp4-prev.bt create mode 100644 regression_tests/bifs-linking-anchor-viewpoint.bt create mode 100644 regression_tests/bifs-linking-anchor-www.bt create mode 100644 regression_tests/bifs-linking-animationstream.bt create mode 100644 regression_tests/bifs-linking-inline-direct-inline.bt create mode 100644 regression_tests/bifs-linking-inline-direct.bt create mode 100644 regression_tests/bifs-linking-inline-od-inline.bt create mode 100644 regression_tests/bifs-linking-inline-od.bt create mode 100644 regression_tests/bifs-linking-inline-rtsp-no-od.bt create mode 100644 regression_tests/bifs-linking-inline-rtsp.bt create mode 100644 regression_tests/bifs-linking-inline-segment-inline.bt create mode 100644 regression_tests/bifs-linking-inline-segment.bt create mode 100644 regression_tests/bifs-media-audiobuffer.bt create mode 100644 regression_tests/bifs-media-audioclip-urlchanged.bt create mode 100644 regression_tests/bifs-media-audioclip.bt create mode 100644 regression_tests/bifs-media-audiosource-mixing.bt create mode 100644 regression_tests/bifs-media-audiosource-urlchanged.bt create mode 100644 regression_tests/bifs-media-audiosource.bt create mode 100644 regression_tests/bifs-media-imagetexture-OD-reuse.bt create mode 100644 regression_tests/bifs-media-imagetexture-no-od.bt create mode 100644 regression_tests/bifs-media-imagetexture-object-scale.bt create mode 100644 regression_tests/bifs-media-imagetexture-transparent.bt create mode 100644 regression_tests/bifs-media-imagetexture-url-change.bt create mode 100644 regression_tests/bifs-media-movietexture-control.bt create mode 100644 regression_tests/bifs-media-movietexture-no-od.bt create mode 100644 regression_tests/bifs-media-movietexture-od-joinsession.bt create mode 100644 regression_tests/bifs-media-movietexture-od-leave-session.bt create mode 100644 regression_tests/bifs-media-movietexture-owns-OCR.bt create mode 100644 regression_tests/bifs-media-movietexture-shares-OCR.bt create mode 100644 regression_tests/bifs-media-movietexture-url-change.bt create mode 100644 regression_tests/bifs-media-sound-spatialize.bt create mode 100644 regression_tests/bifs-media-sound.bt create mode 100644 regression_tests/bifs-misc-UTF16-input.bt create mode 100644 regression_tests/bifs-misc-cyclic-graph.bt create mode 100644 regression_tests/bifs-misc-hc-proto-pathextrusion.bt create mode 100644 regression_tests/bifs-misc-hc-proto-planarextrusion.bt create mode 100644 regression_tests/bifs-misc-hc-proto-planeclipper.bt create mode 100644 regression_tests/bifs-misc-non-linear-parsing-conditional.bt create mode 100644 regression_tests/bifs-misc-non-linear-parsing-use.bt create mode 100644 regression_tests/bifs-misc-srt-import-3gpp-control-share-ocr.bt create mode 100644 regression_tests/bifs-misc-srt-import-3gpp-control.bt create mode 100644 regression_tests/bifs-misc-srt-import-3gpp.bt create mode 100644 regression_tests/bifs-misc-srt-import.bt create mode 100644 regression_tests/bifs-od-remove-esd.bt create mode 100644 regression_tests/bifs-od-remove-od.bt create mode 100644 regression_tests/bifs-od-update-od.bt create mode 100644 regression_tests/bifs-proto-conditional.bt create mode 100644 regression_tests/bifs-proto-delete-def.bt create mode 100644 regression_tests/bifs-proto-delete-index.bt create mode 100644 regression_tests/bifs-proto-forestgump.bt create mode 100644 regression_tests/bifs-proto-mfurl.bt create mode 100644 regression_tests/bifs-proto-multiple.bt create mode 100644 regression_tests/bifs-proto-nested.bt create mode 100644 regression_tests/bifs-proto-route.bt create mode 100644 regression_tests/bifs-proto-sftime-protocode.bt create mode 100644 regression_tests/bifs-proto-sftime-protointerface.bt create mode 100644 regression_tests/bifs-proto-simple.bt create mode 100644 regression_tests/bifs-proto-use.bt create mode 100644 regression_tests/bifs-script-char-to-int.bt create mode 100644 regression_tests/bifs-script-child-create.bt create mode 100644 regression_tests/bifs-script-date.bt create mode 100644 regression_tests/bifs-script-event-out.bt create mode 100644 regression_tests/bifs-script-initialize.bt create mode 100644 regression_tests/bifs-script-load-url.bt create mode 100644 regression_tests/bifs-script-node-access.bt create mode 100644 regression_tests/bifs-script-node-create.bt create mode 100644 regression_tests/bifs-script-proto.bt create mode 100644 regression_tests/bifs-script-timestamp.bt create mode 100644 regression_tests/bifs-stream-text-switch.bt create mode 100644 regression_tests/bifs-text-align-horiz1.bt create mode 100644 regression_tests/bifs-text-align-horiz2.bt create mode 100644 regression_tests/bifs-text-align-horiz3.bt create mode 100644 regression_tests/bifs-text-align-horiz4.bt create mode 100644 regression_tests/bifs-text-align-vert1.bt create mode 100644 regression_tests/bifs-text-align-vert2.bt create mode 100644 regression_tests/bifs-text-align-vert3.bt create mode 100644 regression_tests/bifs-text-align-vert4.bt create mode 100644 regression_tests/bifs-text-glyph-advance.bt create mode 100644 regression_tests/bifs-text-length.bt create mode 100644 regression_tests/bifs-text-maxextend.bt create mode 100644 regression_tests/bifs-text-style.bt create mode 100644 regression_tests/bifs-text-unicode.bt create mode 100644 regression_tests/bifs-text-vrml-alignment.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-OCR.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-audio-speed.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-audio.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-complete.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-deactivation.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-inline-av.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-inline-segments.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-inline.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-rtsp.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-seg-inline.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-segments.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-video.bt create mode 100644 regression_tests/bifs-timeline-mediacontrol-videospeed.bt create mode 100644 regression_tests/bifs-timeline-mediasensor-segment-switch.bt create mode 100644 regression_tests/bifs-timeline-mediasensor-segment.bt create mode 100644 regression_tests/bifs-timeline-mediasensor.bt create mode 100755 regression_tests/build-navigator-sh create mode 100644 regression_tests/build-navigator-w32.bat create mode 100644 regression_tests/build-shell create mode 100644 regression_tests/build-w32.bat create mode 100644 regression_tests/index.css create mode 100644 regression_tests/index.xml create mode 100644 regression_tests/x3d-2D-Arc2d.x3dv create mode 100644 regression_tests/x3d-2D-ArcClose2d.x3dv create mode 100644 regression_tests/x3d-2D-Disk2d.x3dv create mode 100644 regression_tests/x3d-2D-Polyline2d.x3dv create mode 100644 regression_tests/x3d-2D-Polypoint2d.x3dv create mode 100644 regression_tests/x3d-2D-TriangleSet2d.x3dv create mode 100644 regression_tests/x3d-3D-IndexedTriangleFanSet.x3dv create mode 100644 regression_tests/x3d-3D-IndexedTriangleSet.x3dv create mode 100644 regression_tests/x3d-3D-IndexedTriangleStripSet.x3dv create mode 100644 regression_tests/x3d-3D-LineSet.x3dv create mode 100644 regression_tests/x3d-3D-TriangleFanSet.x3dv create mode 100644 regression_tests/x3d-3D-TriangleSet.x3dv create mode 100644 regression_tests/x3d-3D-TriangleStripSet.x3dv create mode 100644 regression_tests/x3d-misc-ColorRGBA.x3dv create mode 100644 regression_tests/x3d-misc-HatchStyle.x3dv create mode 100644 regression_tests/x3d-misc-KeySensor.x3dv create mode 100644 regression_tests/x3d-misc-StringSensor.x3dv create mode 100755 run_configure.sh create mode 100644 src/Makefile create mode 100644 src/bifs/arith_decoder.c create mode 100644 src/bifs/bifs_codec.c create mode 100644 src/bifs/bifs_node_tables.c create mode 100644 src/bifs/com_dec.c create mode 100644 src/bifs/com_enc.c create mode 100644 src/bifs/conditional.c create mode 100644 src/bifs/field_decode.c create mode 100644 src/bifs/field_encode.c create mode 100644 src/bifs/memory_decoder.c create mode 100644 src/bifs/predictive_mffield.c create mode 100644 src/bifs/quant.h create mode 100644 src/bifs/quantize.c create mode 100644 src/bifs/script.h create mode 100644 src/bifs/script_dec.c create mode 100644 src/bifs/script_enc.c create mode 100644 src/bifs/unquantize.c create mode 100644 src/compositor/audio_input.c create mode 100644 src/compositor/audio_mixer.c create mode 100644 src/compositor/audio_render.c create mode 100644 src/compositor/bindable.c create mode 100644 src/compositor/camera.c create mode 100644 src/compositor/compositor.c create mode 100644 src/compositor/compositor_2d.c create mode 100644 src/compositor/compositor_3d.c create mode 100644 src/compositor/compositor_node_init.c create mode 100644 src/compositor/drawable.c create mode 100644 src/compositor/drawable.h create mode 100644 src/compositor/events.c create mode 100644 src/compositor/font_engine.c create mode 100644 src/compositor/gl_inc.h create mode 100644 src/compositor/hardcoded_protos.c create mode 100644 src/compositor/mesh.c create mode 100644 src/compositor/mesh_collide.c create mode 100644 src/compositor/mesh_tesselate.c create mode 100644 src/compositor/mpeg4_animstream.c create mode 100644 src/compositor/mpeg4_audio.c create mode 100644 src/compositor/mpeg4_background.c create mode 100644 src/compositor/mpeg4_background2d.c create mode 100644 src/compositor/mpeg4_bitmap.c create mode 100644 src/compositor/mpeg4_composite.c create mode 100644 src/compositor/mpeg4_form.c create mode 100644 src/compositor/mpeg4_geometry_2d.c create mode 100644 src/compositor/mpeg4_geometry_3d.c create mode 100644 src/compositor/mpeg4_geometry_ifs2d.c create mode 100644 src/compositor/mpeg4_geometry_ils2d.c create mode 100644 src/compositor/mpeg4_gradients.c create mode 100644 src/compositor/mpeg4_grouping.c create mode 100644 src/compositor/mpeg4_grouping.h create mode 100644 src/compositor/mpeg4_grouping_2d.c create mode 100644 src/compositor/mpeg4_grouping_3d.c create mode 100644 src/compositor/mpeg4_layer_2d.c create mode 100644 src/compositor/mpeg4_layer_3d.c create mode 100644 src/compositor/mpeg4_layout.c create mode 100644 src/compositor/mpeg4_lighting.c create mode 100644 src/compositor/mpeg4_path_layout.c create mode 100644 src/compositor/mpeg4_sensors.c create mode 100644 src/compositor/mpeg4_sound.c create mode 100644 src/compositor/mpeg4_text.c create mode 100644 src/compositor/mpeg4_textures.c create mode 100644 src/compositor/mpeg4_timesensor.c create mode 100644 src/compositor/mpeg4_viewport.c create mode 100644 src/compositor/navigate.c create mode 100644 src/compositor/nodes_stacks.h create mode 100644 src/compositor/offscreen_cache.c create mode 100644 src/compositor/offscreen_cache.h create mode 100644 src/compositor/svg_base.c create mode 100644 src/compositor/svg_font.c create mode 100644 src/compositor/svg_geometry.c create mode 100644 src/compositor/svg_grouping.c create mode 100644 src/compositor/svg_media.c create mode 100644 src/compositor/svg_paint_servers.c create mode 100644 src/compositor/svg_text.c create mode 100644 src/compositor/texturing.c create mode 100644 src/compositor/texturing.h create mode 100644 src/compositor/texturing_gl.c create mode 100644 src/compositor/visual_manager.c create mode 100644 src/compositor/visual_manager.h create mode 100644 src/compositor/visual_manager_2d.c create mode 100644 src/compositor/visual_manager_2d.h create mode 100644 src/compositor/visual_manager_2d_draw.c create mode 100644 src/compositor/visual_manager_3d.c create mode 100644 src/compositor/visual_manager_3d.h create mode 100644 src/compositor/visual_manager_3d_gl.c create mode 100644 src/compositor/x3d_geometry.c create mode 100644 src/dir_info create mode 100644 src/ietf/rtcp.c create mode 100644 src/ietf/rtp.c create mode 100644 src/ietf/rtp_depacketizer.c create mode 100644 src/ietf/rtp_packetizer.c create mode 100644 src/ietf/rtp_pck_3gpp.c create mode 100644 src/ietf/rtp_pck_mpeg12.c create mode 100644 src/ietf/rtp_pck_mpeg4.c create mode 100644 src/ietf/rtsp_command.c create mode 100644 src/ietf/rtsp_common.c create mode 100644 src/ietf/rtsp_response.c create mode 100644 src/ietf/rtsp_session.c create mode 100644 src/ietf/sdp.c create mode 100644 src/isomedia/avc_ext.c create mode 100644 src/isomedia/box_code_3gpp.c create mode 100644 src/isomedia/box_code_apple.c create mode 100644 src/isomedia/box_code_base.c create mode 100644 src/isomedia/box_code_isma.c create mode 100644 src/isomedia/box_code_meta.c create mode 100644 src/isomedia/box_dump.c create mode 100644 src/isomedia/box_funcs.c create mode 100644 src/isomedia/data_map.c create mode 100644 src/isomedia/hint_track.c create mode 100644 src/isomedia/hinting.c create mode 100644 src/isomedia/isma_sample.c create mode 100644 src/isomedia/isom_intern.c create mode 100644 src/isomedia/isom_read.c create mode 100644 src/isomedia/isom_store.c create mode 100644 src/isomedia/isom_write.c create mode 100644 src/isomedia/media.c create mode 100644 src/isomedia/media_odf.c create mode 100644 src/isomedia/meta.c create mode 100644 src/isomedia/movie_fragments.c create mode 100644 src/isomedia/sample_descs.c create mode 100644 src/isomedia/stbl_read.c create mode 100644 src/isomedia/stbl_write.c create mode 100644 src/isomedia/track.c create mode 100644 src/isomedia/tx3g.c create mode 100644 src/laser/lsr_dec.c create mode 100644 src/laser/lsr_enc.c create mode 100644 src/laser/lsr_tables.c create mode 100644 src/libgpac.def create mode 100644 src/libgpac_ce.def create mode 100644 src/mcrypt/cbc.c create mode 100644 src/mcrypt/cfb.c create mode 100644 src/mcrypt/ctr.c create mode 100644 src/mcrypt/des.c create mode 100644 src/mcrypt/ecb.c create mode 100644 src/mcrypt/g_crypt.c create mode 100644 src/mcrypt/ncfb.c create mode 100644 src/mcrypt/nofb.c create mode 100644 src/mcrypt/ofb.c create mode 100644 src/mcrypt/rijndael-128.c create mode 100644 src/mcrypt/rijndael-192.c create mode 100644 src/mcrypt/rijndael-256.c create mode 100644 src/mcrypt/sha1.c create mode 100644 src/mcrypt/stream.c create mode 100644 src/mcrypt/tripledes.c create mode 100644 src/media_tools/av_parsers.c create mode 100644 src/media_tools/avilib.c create mode 100644 src/media_tools/gpac_ogg.c create mode 100644 src/media_tools/img.c create mode 100644 src/media_tools/ismacryp.c create mode 100644 src/media_tools/isom_hinter.c create mode 100644 src/media_tools/isom_tools.c create mode 100644 src/media_tools/media_export.c create mode 100644 src/media_tools/media_import.c create mode 100644 src/media_tools/mpeg2_ps.c create mode 100644 src/media_tools/mpeg2_ps.h create mode 100644 src/media_tools/mpegts.c create mode 100644 src/media_tools/saf.c create mode 100644 src/media_tools/text_import.c create mode 100644 src/media_tools/vobsub.c create mode 100644 src/odf/desc_private.c create mode 100644 src/odf/descriptors.c create mode 100644 src/odf/ipmpx_code.c create mode 100644 src/odf/ipmpx_dump.c create mode 100644 src/odf/ipmpx_parse.c create mode 100644 src/odf/oci_codec.c create mode 100644 src/odf/odf_code.c create mode 100644 src/odf/odf_codec.c create mode 100644 src/odf/odf_command.c create mode 100644 src/odf/odf_dump.c create mode 100644 src/odf/odf_parse.c create mode 100644 src/odf/qos.c create mode 100644 src/odf/slc.c create mode 100644 src/scene_manager/encode_cbk.c create mode 100644 src/scene_manager/encode_isom.c create mode 100644 src/scene_manager/loader_bt.c create mode 100644 src/scene_manager/loader_isom.c create mode 100644 src/scene_manager/loader_qt.c create mode 100644 src/scene_manager/loader_svg.c create mode 100644 src/scene_manager/loader_xmt.c create mode 100644 src/scene_manager/scene_dump.c create mode 100644 src/scene_manager/scene_manager.c create mode 100644 src/scene_manager/scene_stats.c create mode 100644 src/scene_manager/swf_bifs.c create mode 100644 src/scene_manager/swf_parse.c create mode 100644 src/scene_manager/text_to_bifs.c create mode 100644 src/scenegraph/base_scenegraph.c create mode 100644 src/scenegraph/commands.c create mode 100644 src/scenegraph/dom_events.c create mode 100644 src/scenegraph/dom_smjs.c create mode 100644 src/scenegraph/mpeg4_animators.c create mode 100644 src/scenegraph/mpeg4_nodes.c create mode 100644 src/scenegraph/mpeg4_valuator.c create mode 100644 src/scenegraph/smil_anim.c create mode 100644 src/scenegraph/smil_timing.c create mode 100644 src/scenegraph/svg_attributes.c create mode 100644 src/scenegraph/svg_properties.c create mode 100644 src/scenegraph/svg_smjs.c create mode 100644 src/scenegraph/svg_types.c create mode 100644 src/scenegraph/vrml_interpolators.c create mode 100644 src/scenegraph/vrml_proto.c create mode 100644 src/scenegraph/vrml_route.c create mode 100644 src/scenegraph/vrml_script.c create mode 100644 src/scenegraph/vrml_smjs.c create mode 100644 src/scenegraph/vrml_tools.c create mode 100644 src/scenegraph/x3d_nodes.c create mode 100644 src/scenegraph/xbl_process.c create mode 100644 src/scenegraph/xml_ns.c create mode 100644 src/terminal/channel.c create mode 100644 src/terminal/clock.c create mode 100644 src/terminal/decoder.c create mode 100644 src/terminal/inline.c create mode 100644 src/terminal/input_sensor.c create mode 100644 src/terminal/input_sensor.h create mode 100644 src/terminal/media_control.c create mode 100644 src/terminal/media_control.h create mode 100644 src/terminal/media_manager.c create mode 100644 src/terminal/media_memory.c create mode 100644 src/terminal/media_memory.h create mode 100644 src/terminal/media_object.c create mode 100644 src/terminal/media_sensor.c create mode 100644 src/terminal/network_service.c create mode 100644 src/terminal/object_browser.c create mode 100644 src/terminal/object_manager.c create mode 100644 src/terminal/svg_external.c create mode 100644 src/terminal/term_node_init.c create mode 100644 src/terminal/terminal.c create mode 100644 src/utils/base_encoding.c create mode 100644 src/utils/bitstream.c create mode 100644 src/utils/color.c create mode 100644 src/utils/configfile.c create mode 100644 src/utils/downloader.c create mode 100644 src/utils/error.c create mode 100644 src/utils/gzio.cpp create mode 100644 src/utils/list.c create mode 100644 src/utils/math.c create mode 100644 src/utils/module.c create mode 100644 src/utils/module_wrap.h create mode 100644 src/utils/os_divers.c create mode 100644 src/utils/os_module.c create mode 100644 src/utils/os_net.c create mode 100644 src/utils/os_thread.c create mode 100644 src/utils/path2d.c create mode 100644 src/utils/path2d_stroker.c create mode 100644 src/utils/symbian_net.cpp create mode 100644 src/utils/symbian_os.cpp create mode 100644 src/utils/token.c create mode 100644 src/utils/uni_bidi.c create mode 100644 src/utils/url.c create mode 100644 src/utils/utf.c create mode 100644 src/utils/xml_parser.c create mode 100644 src/utils/zlib_symbian_ext.h create mode 100644 src/utils/zutil.c create mode 100644 src/utils/zutil.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..107c2c5 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,11 @@ +Author: +======= +Jean Le Feuvre + +Contributors: +============= +Cyril Concolato contributing SVG, SMIL, ISMACryp MP4 support and V4Studio (together with some ENST people) + + +Pierre Souchay for PulseAudio and JACK audio support +Ivan Vecera () for VobSub support diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..9402234 --- /dev/null +++ b/BUGS @@ -0,0 +1,9 @@ +BUGS + +A bug tracker is available at http://sourceforge.net/projects/gpac/ +BUG REPORTS + +If you find a bug that is not in the list above, please report it at + +http://sourceforge.net/tracker/?group_id=84101&atid=571738 + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..223ede7 --- /dev/null +++ b/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Changelog b/Changelog new file mode 100644 index 0000000..86d88b2 --- /dev/null +++ b/Changelog @@ -0,0 +1,711 @@ +02/12/08: GPAC 0.4.5 + - Support for AC3 in ISO Media, AC3 decoder (liba52) RTP hinting + - Support for MediaAccessEvent (spec still under development ??) + - Added support for user extensions (global class only) in JS through modules - cf gpac_js for sample code. + - Initial support for 3GPP DIMS (file creation and dumping + playback) + * Currently only SVG as overlay is supported for DIMS files, we are not sure about the specification on this topic + * RTP hinting /playback is not supported yet + - Support for SVG foreignObject - all GPAC supported content can be played in a foreign object + - Added automatic switch between 2D and 3D context for VRML/BIFS Inline node + - Basic support for DCCI queries: + * the DCCI object is exposed in scripts as 'DCCIRoot' + * the related document can be specified in GPAC configuration file in the [General] section with the 'EnvironmentFile' key + pointing to an XML file to use as the ontology context + * DCCI's propertyFilters are NOT supported yet. + - Added support for YCbCr OpenGL texturing (only tested with GL_MESA_ycbcr_texture) + - Fixed bugs in OSS audio (100% CPU usage when no audio to play) and ALSA (broken playback) + - Initial support for YUV overlays on Win32 and Linux (Needs XVideo) + * only one overlay at the moment + * Vector graphics /images can be drawn above the overlay if the video output supports dest color keying + - Changed 3D mesh object to be slightly more compact (colors and normal) + - Unified focus model for SVG and MPEG-4. MPEG-4 focus can only be used through tab/-tab (no north/east/.. navigation) + * The MPEG-4 focus is based on the groups (2D/3D) presenting interactive features (sensors) + * keyboard mapping for sensors is: + isOver triggered when focus moves in(isOver=TRUE) and out (isOver=FALSE) + isActive triggered when ENTER key is pressed (isActive=TRUE) and released (isActive=TRUE) + direction keys with or without SHIFT to simulate mouse move + * Layout node is considered as a sensor when scroll_rate is 0, so that the user can scroll the layout + * The focus is by default on the UA (GPAC), not on the content. + - Added support for inplace text edition in SVGT and in MPEG4 + * support for clipboard paste at current caret pos (win32, wxWidgets - no WinCE/symbian yet) + * VRML/MPEG4/X3D: this is not standard, enabled with "EDITABLE" present in text->fontStyle->styles. + Added because StringSensor does not allow for in-place editing of the string + - Added support for text selection in MPEG4 (non standard) / SVGT with clipboard copy (win32, wxWidgets - no copy for text+tspan - no WinCE/symbian yet) + - Better support for SVG , added support for SVG->svg linking (ElementID and svgView) and many svg fixes + - Added support for MPEG-4 over MPEG-2 TS in mp42ts (MPEG-4 SL in MPEG-2 PES) + - Added support for MPEG-4 AAC/LOAS/LATM/ over MPEG-2 TS for import in ISO Media and in the player. + Only basic DVB config is supported for LATM + - Fixed support for AVC/H264 over MPEG-2 TS import in ISO Media and in the player. Only one PPS/SPS is allowed + - Added support for offscreen GL rendering on symbian using PBuffers + - SVG fonts should now be quite stable, but does not support kerning + - Moved all Font modules to the new glyph-based architecture + - Moved SVG text to new glyph-based renderer, added support for OpenGL drawing and textured mode + - Added support for AVC/H264 in mp4_streamer + - Added support of MPEG-4 systems over MPEG-2 + - Added extended version of "-node" switch in MP4Box, to get the list of possible nodes. For insatnce, "MP4Box -node Anchor.children" + - New glyph-based font manager + - Misc fixes in SVG rendering with OpenGL + - Renderer modules have been removed from GPAC. The new integrated 2D+3D renderer has been moved to libgpac and is now + called "compositor". Some renaming has been done in the process to clarify a bit some functions. + NOTE: EVC3 and MSVC8 projects have not been updated yet + - added support for TrackSelection (tsel) - (c) 2007 ENST & RresonateMP4 - cf MP4Box -h general + - added support for PASP in ISO files + - misc fixes in render_full module + - committed patch fixing VobSub extraction bug + - added support for http playback in ffmpeg demuxer + +31/05/07: GPAC 0.4.4 + - Added support for XMLHttpRequest for both VRML/MPEG-4 and SVG. All methods should be supported, but only GET/HEAD have been tested + - Added a basic subset of DOM Core for xml doc (XMLHttpRequest, SVG) and moved the uDOM implementation in it. + - Added support for SVG focus & navigation in 2D renderer + - new SVG scene graph implementation with much lower memory usage + - Updated LASeR to new SVG implementation and fixed many bugs in codec + current LASeR binary version should now be in sync with the latest spec (IS+COR, AMD still to be done) + - Added support for some LASeR tools: + - conditional + - clipBegin/clipEnd and syncReference on audio/video elements + - WARNING: + * other LASeR (non-SVG) v1 elements and all v2 elements are NOT supported + * LASeR save/restore and sendEvents are NOT supported + * SAF handling is likely not conformant yet + - Moved default SVG implementation to dynamic attribute allocation + - ALSA output module + - DVB support for Linux - connection URL is dvb://ChannelName, cf doc/configuration.html + - Moved ISMA decryption to run-time module for basic DRM tests + - Added support for NAT Keep Alive (RTP streaming only) + - Two new test apps: + mp4_streamer: RTP unicaster/mulitcaster + mp42ts: MPEG-2 TS sample gateway, can output to file or to RTP. Supports ISO file and SDPs (RTP only, no RTSP) as input. + - Changed default behavior in MP4Box when adding AAC-SBR with explicit signaling: full SBR Samplerate is now used in media track. + - Added JP2 and MJP2 support for mux/demux and playback - latest version of openjpeg (1.1) used for decoding (Win32 only). + Win32 user: if you don't want to install openjpeg, remove GPAC_HAS_JP2 from img_in project settings + - large code rewrite in scenegraph and 2D renderer to lower memory usage + - added support for OMA DRM2 packaging in MP4Box (doc to come) + - Symbian OS now supported. Build instructions are in doc/INSTALL.symbian. Network is NOT working on symbian yet + - Major speed improvement of XML SAX parser + - added unthreaded mode in MP4Client (-no-thread switch) to test behavior on symbian + - added fullscreen startup mode in MP4Client (-fs switch) + - better handling of iTunes tags in MP4Box (set/get) and in players (title display) + - added support for multiple RTSP sessions in an SDP description + - added support for faked broadcast mode in SDP (forbids any timeline control by player except initial play/final stop) + syntax is (in media SDP section, per stream): a=gpac-broadcast:1 + - added ffmpeg support for WinCE devices (ffmpeg for WinCE is available in gpac_extra_libs) + - added GPAC log system + + - moved key/mouse event subsystem to DOM3 model + - Fixed handling of QT V1 and V2 audio descriptions + - Misc source code reorganization for RTP (depacketizers now in libgpac, no longer in plugins) + - Fixed support for IPV6 and for multicast (IPV4 and IPV6). Some issues remain with IPV6 on Win32 XP, so IPV6 is disabled by default on Win32 + - Fixed handling of negative delays when adding/appending media tracks with MP4Box + - moved ffmpeg headers to latest CVS version (05/01/2007). + - fixes in MPEG2 TS import and demuxer + - added detection of FPS for raw AVC import when present + - fixed a display freezing bug in osmo4 for PocketPC 2003. + - added support for svg animation and svg use with external resources + - cleanup of MPEG-4 RemoteOD + support for segment identifiers at scene level + - some workarounds for iPod file producing (special UUID needed) (-ipod option, auto on for .m4a and .m4v extensions) + - added windowless mode on Win32: transparent background color is defined in the config file ( [Rendering] ColorKey ) + - major speed improvements in MPEG-4/VRML scripts and SVG uDOM regarding node creation + - and a lot more bug fixes... + +21/07/06: GPAC 0.4.2 + - commit of GPAX (GPAC ActiveX) - controller only works in IE and ActiveX control tester for now. + - API changes to Osmozilla to keep in sync with GPAX. Sample html file can be found in applications/GPAX. + - both plugin now support browser navigation (ie link to html within MPEG-4 content) + - Plugins can be used to modify the presentation from a parent HTML doc + - check sample files in regression tests for more details + - iTune tagging support, (patch from Andrew Voznytsa with slight modif). Tagging can be done with MP4Box -itags option. The tags are passed in a single string, separated by ':', formatted as 'tag_name=tag_value'. Supported tags names are: album, artist, comment, compilation, composer, created, disk, encoder, genre, name, tempo, track, tracknum, writer. + NOTE: to make sure you mp4 is importable on an iPod, you must: + - use .m4a extension + - specify the right brands: MP4Box -brand "M4A " -ab mp42 + This process is automated in MP4Box for all file with extension .m4a + - added MPEG-1/2 raw importing (extensions: .m1v and .m2v). + - cleanup of all Makefiles: + no more recursive makes for libgpac + gpac can now be compiled outside the main source tree with gcc, eg $ ~/cvs/test>../gpac/configure + - Support for VobSub import and export (.idx) thanks to a great patch from Falco ! + - initial version of MPEG-2 TS demuxer (MP4Box and client plugin) + program-based import for MPEG-2 TS streams (MP4Box -add file.ts#program=ProgName). + - initial IPV6 support + - MP42AVI is now deprecated. MP4Client can now be used to produce uncompressed bmp/raw/AVI, dumping the complete presentattion rather than just BIFS scenes (audio is currently not extracted). Usage: + MP4Client -bmp 1-2.5-3 file.mp4 take screenshots of file.mp4 aty T=1, 2.5 and 3 seconds + MP4Client -avi -fps 15.0 -size 176x144 file.mp4 produces an uncompressed AVI of resolution 176x144, framerate 15 + MP4Client -avi 4-10 file.mp4 produces an uncompressed AVI of the scene between 4 and 10 seconds + Check MP4Client man page for more details. + - added support for major brand versioning in MP4Box: "-brand GPAC:2" will set the major brand to GPAC, with a version of 2. + - regression tests (.bt and .x3dv) are now part of GPAC source tree + changed all audio and video media in regression tests. + - added 2D/3D selection param for osmozilla: use3d="true" or use3d="false" to force renderer used. + - added support for importing AMR/SMV/EVRC file missing their magic number + - moved all language handling to both ISO 639-1 (2 char code) and 639-2 (3 char code), as 639-1 is used in SVG. + - removed old XML parser, all parsing now relies on a new GPAC SAX parser (avoid dependency on libxml when unneeded). + - support for progressive loading support for XMT and X3D files. + - new svg_in module using gpac sax parser (supports progressive loading too) + - cf configuration.html or man gpac for more info on progressive loading control + - added 'define' support in BT - to use it just do: + #define symbol blab labl a + and reuse the symbol in the BT text. This may be quite buggy, but it can be usefull + - support for Scene Carousel in hinters, core and rtp reassembler. Currently only BIFS and BIFS+AV can be use the scene carousel, carouseling of + static data (eg images) is not supported. The OD data must be embedded in the IOD a la ISMA. + - added FPS and size info dumping for MPEG and AVI file import (MP4Box -info file.mpg) + - improved 3D renderer while checking the X3D conformance suite + - changed MPEG-4 SP -> AVI to add VOSH before each I-frame + - Experimental support for LASeR (encoder, decoder and decoder module). + - Far from behing complete or usable at this time, binary syntax not 100% safe and COR to standard is in edition stage... + - LASeR RAP generation in MP4Box + - added SAF (LASeR Simple Aggregation Format) support: mux and demux (MP4Box) + SAF input plugin. + Basic LASeR usage: + encoding: MP4Box -mp4 file.svg, MP4Box -saf file.svg + decoding: MP4Box -svg file.mp4, MP4Box -xsr file.mp4 (dumps to LASeRML format) + - added patch from FT R&D for simple anim mask encoding needed for FAP/BAP streams on FDT/BDP nodes. + - added patch for drift-controled interleaving (interleaves while trying to keep chunk synchronized). This is now the storage mode of MP4Box, old interleaving is possible with -old-inter option. + - added support for tight interleaving without hinting in MP4Box. + - added support for delayed concatenation (-cat file.mp4:delay=2000 test.mp4) + - added "-name" option to MP4Box (track import and general cmd line opt) for setting the track handler name + - More SVG improvements and features: + - support for SVG scripting (ecmascript through SpiderMonkey) - a good subset of microDOM is supported (!! presentation traits are missing). + - support for SVG events (DOM) + - support for SVG scene dumping + - SVG Tiny 1.2 gradients + gradient matrix (1.1 feature) + - SVG.preserveAspectRatio support + - system color paint + - basic conditional processing (switch) + - SMIL anim events (begin, end, restart) + - added PAR modification support to MP4Box (import time and file based) + - improved precision of IsoMedia file spliting + - added NHML import/export. NHML is an XML representation of the NHNT file, with add-ons and a more flexible way of integrating media. Doc to come on web site + - clean-up of 2D direct rendering mode: + * no more bounds tracking for less memory usage + * automatically mode switch to direct rendering when using slide navigation (pan&zoom) + - clean-up of soft raster: + * removed all bezier curbs handling, let gpac core handle those + * paths are now always flatten to reduce rastering times + * misc optimizations of scanline converter (no more Y-sorting, only X-sorting used) + - speed improvements in BT/WRL loader + - updated makefiles for compilation under GPE + - made FreeType plugin log unknown fonts in general cfg file to speed up font selection. + - changed module naming - all modules are now prefixed with "gm_", and module is loaded/unloaded at run-time + - changed isomedia file open API for better support of temporary directories + - simplified 2D blitter for non DirectX output: no more in-middle surface used, direct stretch, blit and yuv conversion to back buffer + - added software and hardware support for MaterialKey in 2D renderer + - Support for SP2003 and PPC2003 + - added all project files for evc4 + - added osmophone to gpac/applications, demo player for Smartphone devices (tested on SPV C500 and PocketPC) + - support for GDI drawing on WinCE to avoid weird menu behaviour in windowed mode + - added support for OpenGL-ES in 3D renderer, in DX and in GAPI plugins + * tested with Hybrid Graphics and Vincent3D implementations + * Klimt could compile but no decent results + * Result tested on PocketPC/SmartPhone 2003 and regular windows + - added LASeR XML to SVG loader + - improved SRT -> 3GPP convertion - now accepts any number of , , tags at random places. + + - fixed AVC/H264 parsing (pic size detection on interleaved stream and cropping were broken) + - fixes on X3D scene parsing and dumping + - fixed mp4 root OD creating when fragmenting file (PLs were not copied over) + - fixed bug in large MP4 file (> 4GB) writing (chunk offset table was corrupted) + - fixes in InputSensor handling. + - fixes in XMT proto+script parsing + - fixed ISOMedia track duration in case of ctts table + - fixed PAR issus at import time setting wrong track sizes for visual media. + - fixed handling of 64-bit timing in GPAC (clients and MP4Box). Impacts on a few APIs (libisomedia, scene manager and terminal). + - fixed bugs in BIFS RAP generation (BT and XMT-A loaders) + - fixed support for node insert and delete in laser + - fixed AVC import bug with multiple PPSs per stream + - fixed stts bug in file concatenation (first inserted sample had a wrong duration) + - fixed v5 and v7 MPEG-4 node templates + - fixed ttxt extraction (fontID was missing in styles) + - fixed MP4Box -add file1 -cat file2 use case (broken CTTS compute) + - fixed 3GP brand when using AVC (3GP6 shall be used) + - fixed misc issues with text import/info display on track sizes and 3GP text track importing. + - fixed bug in gradient raster (spread/pad/repeat modes were broken) + - fixed bevel stroking bug and many SVG rendering ones + - fixed bug in MP4Box track concatenation of MPEG-4 user streams + - fixed bug in meta items storage (existing items were corrupted on file rewrite) + - fixed splitx option to support end times larger than file duration + - fixed globalQP context loading issues + - fixed display bug when switching between MPEG-4 and SVG scenes + - fixed absolute URLs handling on WinCE devices + - fixed redraw bug in 2D renderer for simple video drawing (background was always repaint regardless of video transparency) + - fixed UTF8 vs Win-CP text handling (BT and XMT parsers, subs->bifs and subs->ttxt converters) + - fixed bug in unknown stsd boxes handling + - fixes for gpac compilation on 64 bits platforms + - fixed bugs related to cache in file downloader + +03/08/05: GPAC 0.4.0 RC2 + - fixed Invalid versioning of previous release (was still 0.4.0-DEV...) + - fixed MinGW compilation + - fixed MP4Box handling of full MSDOS paths (C:\) + - fixed GCC 4 compilation issues + - added MacOS X (DARWIN) compilation patches + - fixed OSS audio compilation (seems to perform perfectly on 50% of systems tested and dramatically fail on the rest...) + - fixed MP4A-LATM support at client side + - fixed X11 module in embedded mode (should work well with both Osmo4/wx and Osmozilla) + +28/07/05: GPAC 0.4.0 release + ** GPAC is now licensed under LGPL. ** + Massive code rewrite and repository reorganization in order to comply to some base coding style (cf gpac/doc/CODING_STYLE) have taken place + since previous release. Documentation is still a work in progress. + APIs are not backward compatible, but should now be in a frozen state/spelling for the most common tools (utils, MPEG-4 OD, IsoMedia and terminal APIs) + + - Fixed FAAD multichannel support with latest FAAD CVS version (bug due to FAAD inner channel creation). Latest version should + support both multichannel and AAC radios. + - added support for normal drawing (for debug purposes) in 3D renderer + - adedd support for single instance of Osmo4/w32 + - changed audio configuration options to "Number of buffers" and "TotalDuration" + - fixed old bug in audio renderer screwing up the audio from time to time + - moved ffmpeg to latest CVS version (25/07/05). THIS VERSION IS NO LONGER COMPATIBLE with previous ones used in GPAC, update + your binaries!! Also updated ffmpeg plugin to new ffmpeg API + - moved all fullscreen handling to dedicated windows, refinement of wxOsmo4. + - more work on x11 plugin (events handling, 3D support) - THIS IS EXPERIMENTAL AT THIS RELEASE LEVEL, CHECKOUT CVS FOR BETTER SUPPORT... + - fixed anamorphic video handling in GPAC, both playing (2D and 3D) and MP4Box parsers (only done for MPEG-4 Visual, not AVC). Video is + now only rescaled at blit time + - changed ';' separators to ':' seprators in MP4Box (meta options and track import options) for linux prompt compatibility. + - misc fixes in tx3g track import, tx3g bt dump and couple of issues introduced in 0.3.0->0.4.0 migration + - misc cleanup of GF_VideoOutput interface. + - started doc manager using doxygen (doxyfile added in gpac/doc). APIs may be slightly reworked during the documentation phase. + Documentation will only be produced for libgpac, eg all exported headers in gpac/include/gpac + Development APIs documentation (gpac/include/gpac/internal) will come later. + - cleaned up IsoMedia reading for more efficient and more reliable parsing + - misc fixes in MP4Box option parsing, in RTP multicast setup + - Osmozilla should now be much more stable on Win32 - Linux version to come. + + +20/06/05: GPAC 0.3.0 release + - fixed bug in 4GB file writing in interleaving mode + - fixed bug in absolute path usage in MP4Box + - added -cat file*.mpg syntax support + - added support for mass encryption in ISMACrypt drm_file (trackID="*" means all tracks get encrypted with the desired key) + - fixed compilation of wxOsmo4 with wxWidgets+Unicode + - fixed bug in 3GPP text layout when not fully contained in video + - added mapping between OD.ESD.GF_Language and MP4.MediaHeader.Language (BT/XMT <-> MP4) + - fixed bug in SDP playback without RTSP session attached (live casts) + - added language and delay selection at import time (cf MP4Box -h import) + - made -lang able to change all tracks languages by default !erase language specified at import time! + - added -lang option to MP4Box to change track language + - added -delay option to MP4Box to change initil media delay - ALTHOUGH 100% STANDARD, THIS MAY NOT BE SUPPORTED BY SOME PLAYERS + - updated configure for opengl disabling and fixed-point configuration + - misc fixes in ffmpeg decoder and MP4Box ISMA for PAR - this is disabled by default due to some unsolved crashes, cf gpac/doc/configuration.html or man gpac.s + - misc updates in MP4Box meta handling for item extraction. + - fixed MPEG-2 aac importing and info dumping + - fixed 3GPP text display bug when stoping and changing an animationStream using a 3GPP text object. + - fixed AVC/H264 HP parsing + - added BIFS track visual size info at BT/XMT encoding stage + - MOVED FFMPEG INCLUDE FILES TO LATEST CVS VERSION to support AVC/H264 HP decoding + - added meta self reference item for dual-headed files + - commit MPEG-4 LATM Audio hinting patch and added rtp aggregation. RTP reassembler NOT UPDATED YET + - fixed bugs in TeXML parser + - fixed bt/xmt to MP4 encoding for IOD with single BIFS ESD.URLString set + - fixed SVG outline handling and SVG/SMIL anim on appearance/geometry + - fixed svg_loader compilation on winCE (ARM binaries and include to follow in next release of gpac_extra_libs) + - commit of SVG fixed-point version + - Fixed MP4Box spliter crash introduced by MetaBox support. + - Merged MP21 and MP4 handling, added meta support in MP4Box (creation/extraction - cf MP4Box -h meta) + + FROM THIS DAY ON, ALL WORK ADDED TO GPAC IS COPYLEFT ENST + +15/05/05: + - Updated makefiles and configure for MacOSX support + - commit of GPAC fixed-point version - may not be completely stable yet :) + - changed all project files location for soon-to-come EVC4 and Visual .NET support + - changed 2D path object to directly support cubic and quadratic bezier (outliner do support them too), inspired from FreeType. + - MSVC Users: moved to wxWidgets 2.6.0 !! + - more fixes on CTTS in H264/AVC importer (p-frame reorder and some I/B sequences with neg POC) + - fixed last sample duration in mp4 for BIFS/OD/Text + - completely rewrote AVC CTS computing to handle b-refs properly + - fixed improper instanciation of externProto with VRML files not declaring default field values in externProtos + - reworked file splitter to support duration and size based splittings + - fixes in file concatanation + - support for "self" parameter in Anchor to replace only inline scenes on anchors + - fixed H263 raw importer (frame boundary detection and frame size) - changed default H263 frame rate to 15 fps (more used than 25 in 3GP). + - added support for new chaper format: CHAPTERX=HH:MM:SS[:ms or .ms] and CHAPTERXNAME=string + - committed AVC parser patch from bobolobo (SEI parsing and recovery points) + - added support for nero chapters in MP4 ('chpl' box), in MP4Box (-chap file.chp) and selection in players (chapters mapped to MPEG-4 SegmentDescriptors). + note regarding chp files: not sure about file syntax, currently support for (one chapter entry per line): + ZoomPlayer syntax: AddChapter (-fps for import framerate selection), AddChapterBySecond and AddChapterByTime + Regular time code … la SRT: HH:MM:SS[:ms or .ms] [Chapter Name] + SMPTE time code: HH:MM:SS;fr/fps [Chapter Name] - if fps is omitted, use '-fps' if specified or 25 fps default. + + - fixed avi packed bitstream flag removal bug with beta versions of DivX. + - fixed bugs in AVC CTTS compute + - wxOsmo4 now single-windowed on win32 with DirectX output driver (SDL one buggy). + - fixed bugs in media exporter for non-MPEG4 streams (AMR & co). + - added '-dts' option to MP4Box - dumps samp num, DTS and CTS for all tracks and performs sanity check of CTS offsets. + - added '-split time_sec' option to MP4Box - splits a simple AV IsoMedia file (no systems, only ONE VIDEO stream supported) into several movies of time_sec duration. + - added '-cat' option to MP4Box - concatenates several input file (IsoMedia or not) to a single IsoMedia file. Same options as -add. + ** Streams with different sample descriptions are imported as distinct tracks ** + - fixed bugs in OD and IPMPX parsers (ipmpDescrPtr and IPMP_ToolID) + +30/03/05: GPAC 0.2.4 release + - Added TeXML import (QT XML format for 3GPP text) + - Added SRT extraction for text tracks. + - Added SUB subtitles support (SUB->3GPP text, SUB->BIFS) + - New lifting for MP4Box - nicer progress notifications and help screens. + - changed MP4Box to OVERRIDE FILES BY DEFAULT if '-out' is not specified. + - changed MP4Box to use 0.5sec interleaving storage by default on all operations. + - added file associations for Osmo4/Win32 (not for wx version) + - added volume control and playlist restoring (Osmo4_w32 and Osmo4_wx) as well as fullscreen restoring when changing file through playlist + - added '-unhint' option in MP4Box to remove all hinting from file. + - many rewrite and fixes in WinCE version (Osmo4 and gapi), much more stable and usable player. + - changed SBR import in MP4Box: -sbr for backward compatible HE-AAC (implicit) signaling, -sbrx for non-backward compatible (explicit) signaling. + - added bandwidth signaling in SDP (b=AS:X) for some players compatibility + - added H263 and MPEG4 Visual(CMP) importers in MP4Box + - added extra SDP lines support in MP4Box - PLEASE refer to rfc2327 before complaining :) + - added hinters for QCELP (RFC 2658) and EVRC/SMV (RFC 3558). Not tested (no decoder available) - QCELP hinter works with QT6.5. + - added QCP importer/export in MP4Box and raw EVRC and SMV importers (not tested) + - added support for 3GPP2 extensions in IsoMedia (EVRC, QCELP and SMV config) + - added basic media cache for stream recording (rtp, internet radios). Only MP4 cache available, but other formats possible (plugin) + - added raw samples export and avi track to raw export in MP4Box + - More H264: import/export/rtp hinter in MP4Box, rtp reassembler + - Basic ISMACryp support in client (RTP and file) + - ISMACryp hinter in MP4Box + - ISMACryp support in MP4Box - cf "MP4Box -h crypt" for more details. Selective encryption (sample-based) is supported but not key switching (only one key at this time). + - added smaller version of libmcrypt to M4Systems library for ISMA AES-128 CTR (but kept other algos for future IPMPX usage) + - added simple converter from cubic QTVR to MP4 + - added 3GGP AMR NB and WB (float code) plugin (Fixed Point AMRWB is just so slow ....) + - moved 3GGP AMR NB decoder to 600 release (old lib should still be compatible) + - moved to latest official ffmpeg tarball for AVC B-frames decoding support (older ffmpeg versions should still work despite some API changes) + - added IPMPX base code (read/write and BT/XMT dump/parse) + - added chunk encoding in MP4Box (uses 2 input files, one with base scene, one with BIFS updates only) + - changed OGG muxing in MP4 - one single OTI (0xDD) used for all OGG streams - DSI format same as before. + - Fixed bug in CTTS computing in MPEG4 Video import caused by consecutive I frames. + - fixed MPA hinter RTP agregation mode (was always on). + - misc updates in importers, exporters and ISMACryp APIs + - cleaning of ISMA and 3GP related stuff in m4systems, forced 3GP file to be branded '3GP5' for QT compatibility... + - rewrote AMR hinter for RTP agregation, added maxptime hint param for speech rtp payloads (AMR/QCELP/EVRC/SMV). + - Fixed MediaControl/Inline URL changing + - fixed video RTP timescale in hinters for QT compatibility + - fixed language-based alternate stream selection in ODs + - cleanup of m4_config.h (removing all unneeded includes, misc cleanup for FreeBSD port) + - brought XMT-A in line with spec regarding MFString/MFURL/MFScript (parsers and dumpers) + - added support for "od://" in XMT-A urls according to XMT spec + - fixed raw aac export bug + - fixed DIV5 import issues with PLs + +05/01/05: GPAC 0.2.3 release + - new regression tests (X3D and some other features) + - MPEG-4 playback from BT/XMT now supports proper startTime/stopTime behaviour + - found a port of XVID for WinCE/ARM, removed OpenDivX from distribution (seemed no longer maintained). This XviD port is BTW much faster & more reliable than old OpenDivx + - split codec_pack in xvid_dec, img_in and mp3_in plugins + - fixed ffmpeg H264 support + - scene time for VRML/X3D now complies with spec (VRML uses absolute UTC timing since 1970) + - fixed 3D spot/point lighting bug + - added support for raw AMR file format (AMR and AMR-WB, multichannel AMR not supported) in MP4Box (import/extract) and reader plugin. + - added MP4Box '-tmp directory': forces temp file creation to given directory rather than OS-specific temp file storage. + - integrated the very nice mpeg ps parser from MPEG4IP - MP4Box now supports MPEG PS import to MP4 (ATM only one audio, one video, and only MP3 audio) + - added basic MPEG 1/2 Video RTP hinter and reassembler, checked MPEG files hinted to MP4 compatibility with QT player. + - fixed color transform and texturing issues. + - various fixes due to "dynamic scenes" in RTP stack and ESM module + - fixed copy/paste in Osmo4 address bar, mp4 on http issues, MP4Box -add pbs with IsoMedia + - Freezing TTXT format version 1.0 - documentation avail on gpac.sf.net + - Updated MP4Box documentation on gpac.sf.net to 0.2.3 version. + - Changed NetClientPluggin API (got rid of status query, status handled internally in ESM and in plugin if needed) + - Updated evc3 projects, made M4Systems READ/WRITE on CE to enable timedtext parsing (and future streaming cache, who knows ...). + - Added 3GPP text hinting in MP4Box and 3GPP text reassembler in rtp plugin. + - Bug fixes and testing of 3GP timed text with some 3GP files + - GF_InlineScene now supports MediaControl with mediaStartTime and mediaStopTime + - AAC SBR import now uses backward compatible decoder config. + - 3GPP/MPEG4 timed text decoder - supports everything except soft wrap & dynamic highlighting (karaoke) - vertical text not fully tested + - new "ttxt" format (for "timed text"): XML representation of 3GPP/MPEG4 timed text streams - importer (from ttxt and from srt) and dumper (to ttxt only) support. + - MP4Box "-ttxt" option: converts an SRT file to a TTXT one. + - Input plugin for ttxt and srt files. + - Support for "dynamic scenes", eg gpac will now generate on the fly a scene description when none is found, and allows stream selection in GUI. + - Modified Osmo4 & wxOsmo4 GUI: stream selection & subtitle adding + - Changed MP4Box "-import" to "-convert" ("-import" is kept for backward compatibility). + - Added MP4Box -nosys (removes all MPEG-4 systems streams and writes an empty IOD) + - Added MP4Box "-add": adds any supported format to an mp4 file (same input conventions as -convert). Several input can be specified (ex: -add audio1.aac -add sub1.srt video.mp4 -out full_movie.mp4) + - fixed a nasty bug in RTSP stack screwing up all mediaControl & rtsp session. + - 3D NonLinearDeformer (AFX) support (taper, twister and bender) + - Completed X3D geometry set: LineSet, (Indexed)TriangleSet, (Indexed)TriangleStripSet, (Indexed)TriangleFanSet + - Full multichannel audio mixing and resampling (and better audio speed support). Multichannel->stereo conversion (not configurable atm). + - Yet Another Announcement of a stable ffmpeg demuxer - stable with avi & mpeg, still sync issues with some QT files (same issues with ffplay). + - added SBR mode for aac importing ('-sbr' switch) in MP4Box. + - fixed: MP4Box hinters (empty tracks), SDL -> BIFS coord mapping in fullscreen, rounding segmentDescriptor.startTime pbs in seeking with mp4menu + - Added Playlist and brower-like navigation to Osmo4 and wxOsmo4 + - AAC/ADTS file & streaming input (radios) - FAAD decoder moved to aac_in plugin + - ADTS import/extraction in MP4Box + - fixed OD XMT-A for ESD.OCR_ES_ID and ESD.dependsOn_ES_ID (now use IDREFs and not binary IDs) + - integrated ogg lib in gpac (), and added ogg/vorbis and ogg/theora importers to mp4 - THIS IS NOT STANDARD AT ALL. + The current syntax is to put needed headers in the MPEG-4 DecoderSpecificInfo, with the following syntax: + u16 sizeof_header; + char *header_data; + for each header (quite similar to what is found in qtcomponents). Vorbis ObjectTypeIndication is 0xDE, theora ObjectTypeIndication is 0xDF. + Need some more testing before filing a request to mp4ra.org. + Streaming possible as MPEG-4 streams, Vorbis RTP packetizer still to be done. + +09/11/04: GPAC 0.2.2 release + - Xiph OGG demuxer: supports file, http download (not tested) and icecast servers. + - Xiph Vorbis decoder + - Xiph Theora support (should work with fluendo but I can't get any data from the server...) + - Better FFMPEG support (moved to latest ffmpeg cvs tarball). + - all file associations in client are made through mime types, and changed handling of service (mime type query if possible before loading plugin) + - network stats & changed all UIs for that. + - decoder stats & changed all UIs for that. + - nicer Osmo4/wxGTK + - shoutcast support in mp3 reader + - Install doc cleanup + - MP4Box "-single" option now work for 3GP files + - MP4Box "-rem TrackID" option to remove a track + - made 3GP hinter produce QT-compatible streams (as usual QT only accepts specific RTP timescales) + - moved M4Systems to a dynamic lib (static is still first built & used for MP4Box) + - moved all scene decoders (bifs, OD and context loader) to real plugins + - moved BT & XMT parsers from stdio to ZLIB io, in order to support GZIPed VRML & X3D (and consequently, BT and XMT-A ;). GPAC CAN NO LONGER COMPILE WITHOUT ZLIB + - massive cleanups in scenegraph & MPEG-4 node naming convention + - !!!scene graph no longer customizable without code hacking!!! + - X3D scene graph generator (decided NOT TO SUPPORT VRML 97 extensions, since they are in X3D and with a different format...) + - updated vrml tools for X3D support (Double precision coords & RGBA colors) + - culling of AABB tree against frustum at draw stage in 3D renderer - greatly speeds up large meshes & terrains rendering + - support for weird cyclic graphs in scenegraph (mainly to support encoding of conditionals). Works with nodes as well, configurable through scenegraph but this can + likely crash a renderer other than GPACs (tested with blaxxun & old GPAC versions, crash works each time:) + - lighting and transformations now properly set in 3D renderer + - improved gravity (ground detection) and added jump in walk mode (right click or 'j') + - support for streams without systems timing knowledge (non MPEG4), eg streams where timing is computed after decoding (and not given by transport layer) for simpler ogg support. + - support for multiple URL in VRML nodes (including remote script) + - '#Viewpoint' and 'URL#Viewpoint' support in Anchor and Inline - #segment_name is not supported and should not be used (not standard IMHO) + - ColorRGBA support for X3D + - texture generator (only "SPHERE-LCOAL" and "COORD" supported for now) + - XML scene dumper now supports X3D (scene dumping API has changed) + - X3D support in BT and XMT-A loader + - updated 3D renderer & ESM for X3D support + - added X3D geometry2D nodes in 3D renderer + - Anchor.activate now works with no children + - plethora of bug fixes + + +15/10/04: GPAC 0.2.1 release + - massive fixes in javascript (assignment was only 50% working), new faster, less memory-hungry implementation (and Othello reg test now passes!!) + - CreateVrmlFromScript support for VRML content. + - collision detection + - gravity (not really working) + - Layer3D fully supported (including navigation!) + - InputSensor in non-encoded scenes (bt/xmt) + - aabb tree in 3D meshes for faster picking/collision - needs more cfg options though + - user input in composite texture 2D/3D, and completed TouchSensor (hitNormal & texCoords event out) in 3D renderer + - added full blending support in 3D renderer (depth sorting) + - fixed handling of clipers (layout/form) and layer2D. + - added text change checking in spidermonkey to avoid rebuilding text + - support for usual coords in 2D renderer (window top-left in 0, 0 and decreasing y) for SVG integration + - 3D normal renormalization for GL when scaling (eg now there's a proper lighting) + - 4/3 and 16/9 aspect ratio modes now work as expected + - interactions with fullscreen SDL now happen where they should + - LinearGradient and RadialGradient in 3D renderer + - hide all scene context importing through a single ContextLoader object to get first frame and progress info for swf loading in player + - added Orbit navigation mode (orbit around the user look point) & updated interfaces + - massive cleanup of scene handling & decoders to enable run-time modules for scene decoding. APIs have changed, 2 new ones for scene decoders and media decoders + - finished BT/XMT/WRL scene handler and added support for SWF. + - added toolbar for Osmo4_w32 (_wx one to come) + - added PlanarExtrusion hardcoded proto (2D shape extruded by 2D shape) + - added bt/wrl support in MP4Client, update scene graph accordingly to use a single graph & lots of related fixes in ESM + - added texture text support in 3D renderer (graphics plugin being now loaded by main renderer) + - upgraded avilib to latest version from ogmtools (should support OpenDML 2.0) + - updated configure, Makefiles, & EVC3 project files + - textured text support (eg render to texture then blit) in 2D renderer + - ElevationGrid, Extrusion, VisibilitySensor, LOD support + - Sound support (stereo spatializer only) and reworked generic audio renderer for Sound/Sound2D support + - MPEG-4 V5 animators (not complete) + - reworked hardcoded proto mechanism to comply with VRML recommendations - some doc to come soon + - hardcoded proto PathExtrusion for any 2D shape extrusion except Bitmap and PointSet2D - eg EXTRUDED TEXT SUPPORT :) + - fixed Win32 large file support in GPAC, needs testing of AVI importer... + - cleaned up multichannel support, fixed faad channel reordering + - added background support: sky & ground dome, cube image (6 sides) + - added navigation support (WALK/FLY/EXAMINE/ + extra modes a la blaxxun) and user selection in Osmo4/wxOsmo4 and MP4Client + - added animation between viewpoints + - changed handling of empty boundable stacks according to VRML: no more default backColor, and real 3D scenes (top=Group or Layer3D) no longer may use orthographic projections + - normal smoothing for IFS. + - full lighting support (spot, point and directional) + - fog and billboard support. + - basic AVC support in MP4 (only AVCSampleEntry and child atoms) + - VRML/MPEG-4 interaction sensor support (added ProximitySensor, SphereSensor, PlaneSensor and CylinderSensors). still some work to do on TouchSensor hitNormal & hitTex + - SWF parser updates: PlaceObject2 and defineShape2.NewStyle bugs, support for soundStream + - added user interaction in 3D renderer (ray casting) in both ortho and projection modes + - frustum is now only recomputed when change in size or viewpoint + - viewport/viewpoint selection to osmo4/wxOsmo4 GUIs + - BT parser supports basic VRML (uncompressed .WRL should now be imported fine) + - plethora of bug fixes + +02/09/04: GPAC 0.2.0 release + - updated cfg file doc man pages & makefiles + - massive re-arch of renderer(s) to be able to load at run-time either 2D or 3D renderer, instead of static linking + - new 2D and 3D renderers + - re-organized development headers + - added basic file<->plugin association by file extension (cf doc) + - Osmo4/wxWidgets version in Applications/Osmo4_wx + - put zoom & pan back in place in 2D render plugin + - fixed V4Studio (upgrade to wxWidgets 2.5.2) + - MP42AVI supports 3D scenes duming + - updated templates for v5 & v6. WARNING: it seems that some nodes will be removed from v5 (AFX COR) which will invalidate v6 bitstreams + - still more fixes related to scenegraph arch changes + - reworked importers & exporters + - updated new regression test suite (no 3D yet) + - bug fixes in MediaControl, MediaSensor & inline scene control + - more fixes related to scenegraph arch changes + - cleanup of SWF importer: + * shape importing is OK, gradient so-so (wrong matrix mapping), no bitmap fill + * font & text OK. + * sprites should work + * sound work - sound stream not done yet + there will likely not be any new SWF feature supported, ActionScript & buttons are out of scope for GPAC. + - added swf support from MP4Box (cf -swf switch) + - fixed XMT-A syntax of script and proto SF/MFNode field, and BIFS/XMT UTF-16 support + - fixed avg/max rate compute on large files for AVI and MP3 importers + - Background2D texturing uses hw blitter when possible (2D renderer only) + - better inline scene & segment descriptors handling + - encoding of IOD extra descriptors now supported + - configure script fixes + - changed all 3D handling, encapsulating all GL calls in a single file + - moved all primitives in 3D renderer to use a single mesh object (rendering done through vertex arrays) + - added IFS, ILS, Sphere + - optimized AVI importer, fixed vbr mp3 in avi import + - changed scenegraph: + * all nodes now keep track of their parents in order to signal sub-tree changes for frustum culling + * no more "SimulationTime" used in scenegraph, using node modif flags instead + * updated 2D and 3D renderer accordingly + - added support for rectangular textures in GL (most graphic cards support this, at least on win32) + - added frustum culling + - added SFTime->SFString formating in valuator (current MPEG-4 COR) + - plethora of bug fixes + +11/05/04: JLF: dev version 0.1.9 + - changed source code architecture: rendering code is now in a dedicated static link outside libm4systems + - intergated draft 3D renderer: + - Most 2D features have been ported to the new renderer (missing: viewport, colorTransform and gradients) + - no user interaction, no viewpoint, no background, no lighting, NO ETC !!!! + - Box, cylinder, cone working + - texture mapping working (still images and video) + - changed video plugin API accordingly + - changed graphics API: all path handling (building & flatening , outlining & dashing) moved to authoring subproject + - changed font API: no more graphics driver used to get font outline + - cf install documentation to recompile. + - updated all node tables to COMPLETE BIFS (eg all nodes from v1 to v6) + - updated v6 template & code to final amendment version + - fixed inline scene start/stop when modifying inline URL. + - updated TODO :) + - uploaded ultra basic SWF convertor (so that it doesn't get lost on my hard drive:) + - plethora of bug fixes + +28/04/04: GPAC Release 0.1.4 + - MP4Box avi import fixes (key frames, VOL removal, n-VOP handling) + - Fixed MP4 and NHNT importers (visual tracks sizes) + - MP4Box avi extractor for visual tracks + - XMT-A parser fixes (unicode, DEF routes, proto namespaces) + - better ExternProto resolving + - added VRML addressing (eg url "protolib#protoName" and "protolib#protoID") + - added pause/resume in Layout scrolling + - fixed rendering of layout nodes (layout, Form and Layer2D) in layout nodes + - fixed port reuse bug: 2 instances of the client may now use RTP streaming (UDP or TCP) at the same time + - fixed video Profile and Levels handling (was old 2000 spec version) + - added -merge option in MP4Box (merges 2 files into a single AV file) + - Fixes in SDL surfaces handling + - better "end of presentation" support + - plethora of bug fixes + +18/04/04: JLF: GPAC Release 0.1.3 rc2 + - MP4Box default hinting is now QT compatible - b-frame hinting support + - DecodingBuffer and CompositionMemory occupancey in ODInfo + - layout text splitting (word boundaries only) + - linux install and man pages + - OCR stream support in (authoring/playing) - only tested with MP4. MediaControl supported on OCR + - MP4Box B-frame import now works with data referencing + - fixed many seeking and buffering issues + - multi-framed audio units (needed for most formats understood by ffmpeg demux) + - multiplexed input streams now properly setup + - networking under linux (http and rtp) + - fixed udp streaming on win32/winCE, rtp reordering (was never on), default udp buffer size. + - changed config file for reordering + - changed audio driver interface (retrieve the HW config on setup for SDL) + some audio resync addons + - support for console events detection in MP4Client linux (a la win32 kbhit()) + - anchor (mp4 navigation only) support in MP4Client + - uploaded linux version of extra libs + - added non-system SDL support in GCC configure script (cf --sdl-cfg) + - default font handling in freetype plugin: some text will be diplayed if at least one font is present + - better MPEG-4 video import (B-frame and packed bitstream) + - updated MP4 with full trackHeader (was old v1) and fixed MP4Box compatibility issues with 3ivx splitter + - moved all windowing architecture to the video output plugins (keyboard and mouse events, WM messages) in order to support SDL, moved old input functions to new event model + - modified jconfig.h to support both win32 and MinGW. + - updated V4Studio to work with new event/windowing architecture (work with SDL and DX) + - UDP autoconfig option (if no UDP traffic is recieved, restart with TCP) + - moved to FAAD2.1 (CVS), fixed faad2 compil on MinGW & winCE + - cleaned up install docs + - added support for SDL with software YUV->RGB and software stretching + - fixed ffmpeg audio support + - added ffmpeg JPEG support + - moved to FreeType 2.1.7 + - moved to libmad 0.15.1b + - MinGW support in all extra libs + - MinGW compilation supported - including DirectX under mingw + - added support for xvid 1.0.0 + - plethora of bug fixes + +28/01/04: GPAC Release 0.1.2 + - codec selection in Osmo4 GUI + - PocketPC installer + - OpenDivx plugin + - clean-up extra lib package, install notes and added missing libs + - FFMPEG demuxer done (synchro not good and seeking pbs). GPAC now supports all FFMPEG-supported formats. + - new NSIS script for complete install + - fixed hang on PocketPC WavOut and misc cleanups for PocketPC compilation + - H263 in 3GP files (use FFMPEG dec) + - H263 streaming (RFC 2429) + - B-frame parsing and CTS reconstruction in AVI importer (needs debug). + - muxInfo.duration parameter is now in milliseconds (was in seconds) + - '-node' options to MP4Box to get a given node syntax (fields default value and QP info) + - relative time stamps in BT ('AT D1000 ' means 'at last AU TS + 1000') + - FFMPEG decoder plugin now working with latest CVS snapshot + - MediaControl switching (several MC on single object) + - separate file downloader API and streaming client API / cleanup of plugins and ESM module + - base support for non-MPEG4 URLs in the scene (eg, url "http://whatever/resource") + - loader (local/network) for JPEG and PNG files + - loader (local/network) for MP3 files. Would be nice to add SHOUTCAST/ICECAST too... + - support for ESD URLs and non-inline OD URLs (supports any service, local file, download and streaming) + - MP4Box no longer needs SpiderMonkey + - added MatteTexture support (needs testing and debug) + - cleanup of exported headers (install in ) + - move all config info to a single include file and updated some compil macro to avoid touching the file + - persistant associations font name / file name for freetype in GPAC.cfg + - support for conditional in proto creating node interfacing with the proto + - complete XMT-A parser (script, proto, externProto and extendedUpdates) + - edit list support in MP4 reader plugin + - BT->XMT-A and XMT-A->BT in MP4Box + - support for MP4 track start offset in BT/MP4Box (edit Lists) + - BIFS random access point generation in MP4Box (sync shadow or regular RAP) for cartoons + - AMR NB support (payload parsing and 3GP codec plugin) + - plethora of bug fixes + +20/11/03: GPAC Release 0.1.1 + - 3GP rtsp streaming support + - GPAC uses latest stable extra libs: faad2 2.0rc3, FreeType 2.1.5 and mad 0.15.0b, and dev version (20031117) of xvid + - support for MediaSegment ranges ("#seg+" and "#seg1-seg2") in MediaControl/MediaSensor + - flow control for RTP over RTSP and session restart when RTSP TEARDOWN not acknowledged + - 3GPP hinting (AMR NB and H263) contribution by Andrew Voznytsa + - playback support for basic 3GPP files (that is only MPEG-4 visual and AAC audio) + - support for MPA payload format - RFC 2250 (hinting and playback) + - support for mode detection (CELP and AAC) in hinting since QT6 understands only these modes + - support for several mediaSensors per OD + - AudioBuffer + - removed DirectDraw display resolution changes when switching to fullscreen + - BIFS Extended Updates from AFX/MU amendment (integrated in GF_SceneGraph, dumping, memory decoding and encoding) + - BT parser completed - complete BIFS syntax is now supported except PMF + - SRT importer through BT (converts SRT file to a BIFS animationStream) + - better audio playback/sync + - ffmpeg plugin, decoder seems OK (MPEG1 and MPEG4 visual tested), demuxer not working yet. + - added AVI/mp3/MP4/NHNT import to mp4box + - BIFS encoder initial release (missing: PMF, BIFS extended Updates), bt parser almost done, bt->mp4 working + - exporters in MP4Box (cmp, aac, mp3, jpg, png and nhnt) + - mp4 XML reports for hint tracks and atoms in MP4Box + - merged old soft rasterizer with charcoal + freetype (using ftgrays standalone raster) - better quality, faster ... + - added BIFS stat tools in MP4Box + - lots of updates in dirty rect algo. + - changed GraphicsDriver2D API + - scene graph dumper in Osmo4 win32 + - New app MP42AVI for BIFS to video (AVI, BMP, RAW) conversion, single-frame dump support + - MP4 dumping to XMT-A and bt (BIFS, OD and OCI) - cf MP4Box + - PredictiveMFField support (only IPPPPPPP...) + - segment descriptors in MediaControl + - ported wav audio for WinCE - TEST AND FIXING NEEDEED , DOESN'T WORK ON ALL IPAQs + - support for hardcoded proto testing + - WinCE port + GAPI video renderer (for iPaq only) + - externProto support (only tested with local proto libs) + - InputSensor (keySensor, Mouse and StringSensor) + - plethora of bug fixes + +version 0.1.0: + initial release diff --git a/Clean.bat b/Clean.bat new file mode 100644 index 0000000..e63123d --- /dev/null +++ b/Clean.bat @@ -0,0 +1,23 @@ +cd src +DEL /S *.obj *.lib *.pch *.pdb *.idb *.exp *.plg *.res *.plg *.ncb *.opt *.APS *.ilk *.SUP *.sbr *.map *.vcl *.o *.dll *.exe *.a *.NCB *.OPT *.vcb *.vco *.bsc *.HLP *.hm *.LOG *.ph +cd .. + +cd modules +DEL /S *.obj *.lib *.pch *.pdb *.idb *.exp *.plg *.res *.plg *.ncb *.opt *.APS *.ilk *.SUP *.sbr *.map *.vcl *.o *.dll *.exe *.a *.NCB *.OPT *.vcb *.vco *.bsc *.HLP *.hm *.LOG *.ph +cd .. + +cd build +DEL /S *.obj *.lib *.pch *.pdb *.idb *.exp *.plg *.res *.plg *.ncb *.opt *.APS *.ilk *.SUP *.sbr *.map *.vcl *.o *.dll *.exe *.a *.NCB *.OPT *.vcb *.vco *.bsc *.HLP *.hm *.LOG *.ph +cd .. + +cd applications +DEL /S *.obj *.lib *.pch *.pdb *.idb *.exp *.plg *.res *.plg *.ncb *.opt *.APS *.ilk *.SUP *.sbr *.map *.vcl *.o *.dll *.exe *.a *.NCB *.OPT *.vcb *.vco *.bsc *.HLP *.hm *.LOG *.ph +cd .. + +cd bin +DEL /S *.obj *.lib *.pch *.pdb *.idb *.exp *.plg *.res *.plg *.ncb *.opt *.APS *.ilk *.SUP *.sbr *.map *.vcl *.o *.exe *.a *.NCB *.OPT *.vcb *.vco *.bsc *.HLP *.hm *.LOG *.ph +cd .. + + + + diff --git a/INSTALLME b/INSTALLME new file mode 100644 index 0000000..1bd5609 --- /dev/null +++ b/INSTALLME @@ -0,0 +1,46 @@ +Installation instructions for GPAC 0.4.5 - December 2008: + + +* Foreword + + GPAC may be compiled without any third party libraries, but in this case its functionalities are very +limited (no still image, no audio, no video, no text, no scripting). It is therefore recommended to download the +extra lib package available at http://sourceforge.net/projects/gpac. Compilation instructions for these libraries +are provided per library in the package. + The current extra_lib package to use with gpac 0.4.5 is gpac_extra_libs 0.4.5. + + In case you have some of these libs already installed on your system, the detailed list of dependencies is + * freetype2 from version 2.1.4 on. + * SpiderMonkey v1.7 (libjs from mozilla). Older versions should work + * libjpg version 6b + * Libpng version 1.2.33 (older versions should work) + * MAD version 0.15.1b (older versions should work) + * xvid version 1.0 (0.9.0 / .1 / .2 should also work) + * ffmpeg (latest stable API version checked was 08 February 2007 snapshot, you may need to change a few things with current versions) + * libogg 1.1, libvorbis 1.1 and libtheora 1.0 from Xiph.org (newer versions work) + * faad2, version 2.0 or above (2.6.1 working) + * liba52, version 0.7.4 + * OpenJPEG, version 1.3 + + Windows user are invited to download and install the Microsoft Platform SDK at + http://www.microsoft.com/msdownload/platformsdk/sdkupdate + If you don't have enough bandwidth (it's big) the GDIplus SDK is also included in the gpac_extra_lib package + +* Installing GPAC + +Detailed instruction for Win32 MSVC Compilation are available in gpac/doc/INSTALL.w32 + +Detailed instruction for WinCE eVC Compilation are available in gpac/doc/INSTALL.wCE + +Detailed instruction for GCC Compilation are available in gpac/doc/INSTALL.gcc + +Detailed instruction for GCC cross-compilation for familiar+GPE systems are available in gpac/doc/INSTALL.gpe + +Detailed instruction for GCCE/Symbian cross-compilation for Symbian v9.1 systems are available in gpac/doc/INSTALL.symbian + +* Configuring GPAC + +GPAC's client configuration is documented in gpac/doc/configuration.html +MP4Box documentation is available online at http://gpac.sourceforge.net + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..84ab8ae --- /dev/null +++ b/Makefile @@ -0,0 +1,141 @@ +# +# Main gpac Makefile +# +include config.mak + +vpath %.c $(SRC_PATH) + +all: lib apps mods + +lib: + $(MAKE) -C src all + +apps: + $(MAKE) -C applications all + +sggen: + $(MAKE) -C applications sggen + +mods: + $(MAKE) -C modules all + +instmoz: + $(MAKE) -C applications/osmozilla install + +depend: + $(MAKE) -C src dep + $(MAKE) -C applications dep + $(MAKE) -C modules dep + +clean: + $(MAKE) -C src clean + $(MAKE) -C applications clean + $(MAKE) -C modules clean + +distclean: + $(MAKE) -C src distclean + $(MAKE) -C applications distclean + $(MAKE) -C modules distclean + rm -f config.mak + +dep: depend + +# tar release (use 'make -k tar' on a checkouted tree) +FILE=gpac-$(shell grep "\#define GPAC_VERSION " include/gpac/tools.h | \ + cut -d "\"" -f 2 ) + +tar: + ( tar zcvf ~/$(FILE).tar.gz ../gpac --exclude CVS --exclude bin --exclude lib --exclude Obj --exclude temp --exclude amr_nb --exclude amr_nb_ft --exclude amr_wb_ft --exclude *.mak --exclude *.o --exclude *.~*) + +install: + install -d "$(DESTDIR)$(prefix)" + install -d "$(DESTDIR)$(prefix)/bin" + install $(INSTFLAGS) -m 755 bin/gcc/MP4Box "$(DESTDIR)$(prefix)/bin" + $(MAKE) -C applications install + install -d "$(DESTDIR)$(moddir)" + install bin/gcc/*.$(DYN_LIB_SUFFIX) "$(DESTDIR)$(moddir)" + rm -f $(DESTDIR)$(moddir)/libgpac.$(DYN_LIB_SUFFIX) + rm -f $(DESTDIR)$(moddir)/nposmozilla.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) + install $(INSTFLAGS) -m 755 bin/gcc/libgpac.dll $(prefix)/$(libdir) +else +ifeq ($(DEBUGBUILD),no) + $(STRIP) bin/gcc/libgpac.$(DYN_LIB_SUFFIX) +endif +ifeq ($(CONFIG_DARWIN),yes) + install -m 755 bin/gcc/libgpac.$(DYN_LIB_SUFFIX) $(DESTDIR)$(prefix)/$(libdir)/libgpac-$(VERSION).$(DYN_LIB_SUFFIX) + ln -sf libgpac-$(VERSION).$(DYN_LIB_SUFFIX) $(DESTDIR)$(prefix)/$(libdir)/libgpac.$(DYN_LIB_SUFFIX) +else + install $(INSTFLAGS) -m 755 bin/gcc/libgpac.$(DYN_LIB_SUFFIX) $(DESTDIR)$(prefix)/$(libdir)/libgpac-$(VERSION).$(DYN_LIB_SUFFIX) + ln -sf libgpac-$(VERSION).$(DYN_LIB_SUFFIX) $(DESTDIR)$(prefix)/$(libdir)/libgpac.$(DYN_LIB_SUFFIX) + ldconfig || true +endif +endif + install -d "$(DESTDIR)$(mandir)" + install -d "$(DESTDIR)$(mandir)/man1" + if [ -d doc ] ; then \ + install -m 644 doc/man/mp4box.1 $(DESTDIR)$(mandir)/man1/ ; \ + install -m 644 doc/man/mp4client.1 $(DESTDIR)$(mandir)/man1/ ; \ + install -m 644 doc/man/gpac.1 $(DESTDIR)$(mandir)/man1/ ; \ + install -d "$(DESTDIR)$(prefix)/share/gpac" ; \ + install -m 644 doc/gpac.mp4 $(DESTDIR)$(prefix)/share/gpac/ ; \ + fi + +uninstall: + $(MAKE) -C applications uninstall + rm -rf $(moddir) + rm -rf $(prefix)/$(libdir)/libgpac* + rm -rf $(prefix)/bin/MP4Box + rm -rf $(prefix)/bin/MP4Client + rm -rf $(mandir)/man1/mp4box.1 + rm -rf $(mandir)/man1/mp4client.1 + rm -rf $(mandir)/man1/gpac.1 + rm -rf $(prefix)/share/gpac + +install-lib: + mkdir -p "$(DESTDIR)$(prefix)/include/gpac" + install -m 644 $(SRC_PATH)/include/gpac/*.h "$(DESTDIR)$(prefix)/include/gpac" + mkdir -p "$(DESTDIR)$(prefix)/include/gpac/internal" + install -m 644 $(SRC_PATH)/include/gpac/internal/*.h "$(DESTDIR)$(prefix)/include/gpac/internal" + mkdir -p "$(DESTDIR)$(prefix)/include/gpac/modules" + install -m 644 $(SRC_PATH)/include/gpac/modules/*.h "$(DESTDIR)$(prefix)/include/gpac/modules" +ifeq ($(GPAC_ENST), yes) + mkdir -p "$(DESTDIR)$(prefix)/include/gpac/enst" + install -m 644 $(SRC_PATH)/include/gpac/enst/*.h "$(DESTDIR)$(prefix)/include/gpac/enst" +endif + mkdir -p "$(DESTDIR)$(prefix)/lib" + install -m 644 "./bin/gcc/libgpac_static.a" "$(DESTDIR)$(prefix)/lib" + mkdir -p "$(DESTDIR)$(prefix)/$(libdir)" + install -m 644 "./bin/gcc/libgpac_static.a" "$(DESTDIR)$(prefix)/$(libdir)" + +uninstall-lib: + rm -rf "$(prefix)/include/gpac/internal" + rm -rf "$(prefix)/include/gpac/modules" + rm -rf "$(prefix)/include/gpac/enst" + rm -rf "$(prefix)/include/gpac" + +help: + @echo "Input to GPAC make:" + @echo "depend/dep: builds dependencies (dev only)" + @echo "all (void): builds main library, programs and plugins" + @echo "lib: builds GPAC library only (libgpac.so)" + @echo "apps: builds programs only" + @echo "modules: builds modules only" + @echo "instmoz: build and local install of osmozilla" + @echo "sggen: builds scene graph generators" + @echo + @echo "clean: clean src repository" + @echo "distclean: clean src repository and host config file" + @echo "tar: create GPAC tarball" + @echo + @echo "install: install applications and modules on system" + @echo "uninstall: uninstall applications and modules" + @echo + @echo "install-lib: install gpac library (dyn and static) and headers , and " + @echo "uninstall-lib: uninstall gpac library (dyn and static) and headers" + @echo + @echo "to build libgpac documentation, go to gpac/doc and type 'doxygen'" + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/README b/README new file mode 100644 index 0000000..4addd3f --- /dev/null +++ b/README @@ -0,0 +1,17 @@ +README for GPAC version 0.4.5, December 2008. + +GPAC is a multimedia framework oriented towards rich media and distributed under the LGPL license (see COPYING). +GPAC supports many multimedia formats, from simple audiovisual containers (avi, mov, mpg) to complex +presentation formats (MPEG-4 Systems, SVG Tiny 1.2, VRML/X3D). GPAC supports scripting of presentation for MPEG4/VRML/X3D through +mozilla SpiderMonkey javascript engine. +GPAC currently supports local playback, http download and play and RTP/RTSP streaming over UDP (unicast or multicast) or TCP. +GPAC also features MP4Box, a multimedia swiss-army knife for the prompt. + +For compilation and installation instruction, check INSTALL file + +For GPAC configuration instruction, check gpac/doc/configuration.html or gpac/doc/man/gpac.1 (man gpac when installed) + +For more information, visit the GPAC website on sourceforge: + http://gpac.sourceforge.net + + diff --git a/TODO b/TODO new file mode 100644 index 0000000..3478c94 --- /dev/null +++ b/TODO @@ -0,0 +1,27 @@ +todo items as of version 0.4.5: + * more refinement for several top scenes support (display overlays & co). + +utopic roadmap +v0.5.0: + * maybe support for glyph-based texturing rather than string-based + * SMIL support ? + +v0.6.0: + * M4Flex (ex FlexMux) support + * Flexible IPMP integration + * JNI interfacing and MPEG-J APIs + * native SWF support in client ? + +v1.0.0: + * full rendering (MPEG-4 AFX ?) + +Future work on authoring tools is not still well defined. The current state has bt/vrml/XMT-A support, it could be interesting to have XMT-O support. The SWF converter +still needs lots of work to support ActionScript and co but this will likely not be developed by your servitor. Other graphics converter are more-or-less droped. +The main authoring focus should now be a better V4Studio-like app, to create simple content as well as act as a live studio. + + +droped items: +* Hardware accelerated 2D raster: from most doc on OpenGL and co, there is no waranty of any kind that backbuffer content is not altered when swaping buffers. +This is a big issue since the 2D renderer main strength is to compute diffs between frames... 2D acceleration is however available with the 3D renderer +* MatteTexture fix: given the lack of acceleration in 2D, the matteTexture in the 2D renderer will be just to slow to be usable, and therefore won't be fixed +for a while... MatteTexture with the 3D renderer will be fixed however, trying to use openGL texenv stuff and multitexturing diff --git a/applications/GPAX/GPAX.cpp b/applications/GPAX/GPAX.cpp new file mode 100644 index 0000000..97c8f11 --- /dev/null +++ b/applications/GPAX/GPAX.cpp @@ -0,0 +1,76 @@ +// GPAX.cpp : Implementation of DLL Exports. + + +// Note: Proxy/Stub Information +// To build a separate proxy/stub DLL, +// run nmake -f GPAXps.mk in the project directory. + +#include "stdafx.h" +#include "resource.h" +#include +#include "GPAX.h" + +#include "GPAX_i.c" +#include "GPAXPlugin.h" + + +CComModule _Module; + +BEGIN_OBJECT_MAP(ObjectMap) +OBJECT_ENTRY(CLSID_GPAX, CGPAXPlugin) +END_OBJECT_MAP() + +///////////////////////////////////////////////////////////////////////////// +// DLL Entry Point + +extern "C" +#ifdef _WIN32_WCE +BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) +#else +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) +#endif +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + _Module.Init(ObjectMap, (HINSTANCE) hInstance, &LIBID_GPAXLib); + DisableThreadLibraryCalls((HINSTANCE) hInstance); + } + else if (dwReason == DLL_PROCESS_DETACH) + _Module.Term(); + return TRUE; // ok +} + +///////////////////////////////////////////////////////////////////////////// +// Used to determine whether the DLL can be unloaded by OLE + +STDAPI DllCanUnloadNow(void) +{ + return (_Module.GetLockCount()==0) ? S_OK : S_FALSE; +} + +///////////////////////////////////////////////////////////////////////////// +// Returns a class factory to create an object of the requested type + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) +{ + return _Module.GetClassObject(rclsid, riid, ppv); +} + +///////////////////////////////////////////////////////////////////////////// +// DllRegisterServer - Adds entries to the system registry + +STDAPI DllRegisterServer(void) +{ + // registers object, typelib and all interfaces in typelib + return _Module.RegisterServer(TRUE); +} + +///////////////////////////////////////////////////////////////////////////// +// DllUnregisterServer - Removes entries from the system registry + +STDAPI DllUnregisterServer(void) +{ + return _Module.UnregisterServer(TRUE); +} + + diff --git a/applications/GPAX/GPAX.def b/applications/GPAX/GPAX.def new file mode 100644 index 0000000..f61476d --- /dev/null +++ b/applications/GPAX/GPAX.def @@ -0,0 +1,9 @@ +; GPAX.def : Declares the module parameters. + +LIBRARY "GPAX.dll" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/applications/GPAX/GPAX.dsp b/applications/GPAX/GPAX.dsp new file mode 100644 index 0000000..0b6fe26 --- /dev/null +++ b/applications/GPAX/GPAX.dsp @@ -0,0 +1,173 @@ +# Microsoft Developer Studio Project File - Name="GPAX" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=GPAX - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "GPAX.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "GPAX.mak" CFG="GPAX - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "GPAX - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "GPAX - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "GPAX - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/w32_deb" +# PROP Intermediate_Dir "obj/w32_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /Yu"stdafx.h" /FD /GZ /c +# ADD BASE RSC /l 0x804 /d "_DEBUG" +# ADD RSC /l 0x804 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 js32.lib zlib.lib winmm.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"../../bin/w32_deb/GPAX.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" +# Begin Custom Build - Performing registration +OutDir=.\obj/w32_deb +TargetPath=\CVS\gpac\bin\w32_deb\GPAX.dll +InputPath=\CVS\gpac\bin\w32_deb\GPAX.dll +SOURCE="$(InputPath)" + +"$(OutDir)\regsvr32.trg" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + regsvr32 /s /c "$(TargetPath)" + echo regsvr32 exec. time > "$(OutDir)\regsvr32.trg" + +# End Custom Build + +!ELSEIF "$(CFG)" == "GPAX - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "obj/w32_rel" +# PROP BASE Intermediate_Dir "obj/w32_rel" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/w32_rel" +# PROP Intermediate_Dir "obj/w32_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MD /W3 /Gm /ZI /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /Yu"stdafx.h" /FD /GZ /c +# SUBTRACT CPP /O +# ADD BASE RSC /l 0x804 /d "_DEBUG" +# ADD RSC /l 0x804 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 js32.lib zlib.lib winmm.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept /libpath:"../gpac/extra_lib/lib/w32_deb" +# ADD LINK32 js32.lib zlib.lib winmm.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"../../bin/w32_rel/GPAX.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_rel" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=copy ..\..\bin\w32_rel\GPAX.dll "C:\Program Files\GPAC" +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "GPAX - Win32 Debug" +# Name "GPAX - Win32 Release" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\GPAX.cpp +# End Source File +# Begin Source File + +SOURCE=.\GPAX.def +# End Source File +# Begin Source File + +SOURCE=.\GPAX.idl +# ADD MTL /tlb ".\GPAX.tlb" /h "GPAX.h" /iid "GPAX_i.c" /Oicf +# End Source File +# Begin Source File + +SOURCE=.\GPAX.rc +# End Source File +# Begin Source File + +SOURCE=.\GPAXPlugin.cpp +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\GPAXPlugin.h +# End Source File +# Begin Source File + +SOURCE=.\Resource.h +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\gpax.bmp +# End Source File +# Begin Source File + +SOURCE=.\GPAX.rgs +# End Source File +# Begin Source File + +SOURCE=.\GPAXProp.rgs +# End Source File +# End Group +# End Target +# End Project +# Section GPAX : {00000000-0000-0000-0000-800000800000} +# 1:21:IDS_DOCSTRINGGPAXProp:105 +# 1:12:IDD_GPAXPROP:107 +# 1:12:IDR_GPAXPROP:106 +# 1:20:IDS_HELPFILEGPAXProp:104 +# 1:17:IDS_TITLEGPAXProp:103 +# End Section diff --git a/applications/GPAX/GPAX.h b/applications/GPAX/GPAX.h new file mode 100644 index 0000000..c38c76d --- /dev/null +++ b/applications/GPAX/GPAX.h @@ -0,0 +1,495 @@ + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + + /* File created by MIDL compiler version 5.03.0286 */ +/* at Thu Jul 20 19:14:15 2006 + */ +/* Compiler settings for \CVS\gpac\applications\GPAX\GPAX.idl: + Oicf (OptLev=i2), W1, Zp8, env=Win32 (32b run), ms_ext, c_ext + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: + __declspec(uuid()), __declspec(selectany), __declspec(novtable) + DECLSPEC_UUID(), MIDL_INTERFACE() +*/ +//@@MIDL_FILE_HEADING( ) + + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 440 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __GPAX_h__ +#define __GPAX_h__ + +/* Forward Declarations */ + +#ifndef __IGPAX_FWD_DEFINED__ +#define __IGPAX_FWD_DEFINED__ +typedef interface IGPAX IGPAX; +#endif /* __IGPAX_FWD_DEFINED__ */ + + +#ifndef __IGPAXEvents_FWD_DEFINED__ +#define __IGPAXEvents_FWD_DEFINED__ +typedef interface IGPAXEvents IGPAXEvents; +#endif /* __IGPAXEvents_FWD_DEFINED__ */ + + +#ifndef __GPAX_FWD_DEFINED__ +#define __GPAX_FWD_DEFINED__ + +#ifdef __cplusplus +typedef class GPAX GPAX; +#else +typedef struct GPAX GPAX; +#endif /* __cplusplus */ + +#endif /* __GPAX_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "oaidl.h" +#include "ocidl.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t); +void __RPC_USER MIDL_user_free( void __RPC_FAR * ); + + +#ifndef __GPAXLib_LIBRARY_DEFINED__ +#define __GPAXLib_LIBRARY_DEFINED__ + +/* library GPAXLib */ +/* [helpstring][version][uuid] */ + + + +#define DISPID_SRC ( 100 ) + +#define DISPID_AutoStart ( 101 ) + +#define DISPID_PlayEvent ( 100 ) + +#define DISPID_PauseEvent ( 101 ) + +#define DISPID_StopEvent ( 102 ) + + +EXTERN_C const IID LIBID_GPAXLib; + +#ifndef __IGPAX_INTERFACE_DEFINED__ +#define __IGPAX_INTERFACE_DEFINED__ + +/* interface IGPAX */ +/* [object][oleautomation][hidden][dual][helpstring][uuid] */ + + +EXTERN_C const IID IID_IGPAX; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("E2A9A937-BB35-47E0-8942-964806299AB4") + IGPAX : public IDispatch + { + public: + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE Play( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE Pause( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE Stop( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE Update( + /* [in] */ BSTR mtype, + /* [in] */ BSTR updates) = 0; + + virtual /* [helpstring][propget][id] */ HRESULT STDMETHODCALLTYPE get_src( + /* [retval][out] */ BSTR __RPC_FAR *url) = 0; + + virtual /* [helpstring][propput][id] */ HRESULT STDMETHODCALLTYPE put_src( + /* [in] */ BSTR url) = 0; + + virtual /* [helpstring][propget][id] */ HRESULT STDMETHODCALLTYPE get_AutoStart( + /* [retval][out] */ VARIANT_BOOL __RPC_FAR *autoplay) = 0; + + virtual /* [helpstring][propput][id] */ HRESULT STDMETHODCALLTYPE put_AutoStart( + /* [in] */ VARIANT_BOOL autoplay) = 0; + + }; + +#else /* C style interface */ + + typedef struct IGPAXVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IGPAX __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IGPAX __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IGPAX __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )( + IGPAX __RPC_FAR * This, + /* [out] */ UINT __RPC_FAR *pctinfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )( + IGPAX __RPC_FAR * This, + /* [in] */ UINT iTInfo, + /* [in] */ LCID lcid, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )( + IGPAX __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, + /* [in] */ UINT cNames, + /* [in] */ LCID lcid, + /* [size_is][out] */ DISPID __RPC_FAR *rgDispId); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )( + IGPAX __RPC_FAR * This, + /* [in] */ DISPID dispIdMember, + /* [in] */ REFIID riid, + /* [in] */ LCID lcid, + /* [in] */ WORD wFlags, + /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, + /* [out] */ VARIANT __RPC_FAR *pVarResult, + /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, + /* [out] */ UINT __RPC_FAR *puArgErr); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Play )( + IGPAX __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Pause )( + IGPAX __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Stop )( + IGPAX __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Update )( + IGPAX __RPC_FAR * This, + /* [in] */ BSTR mtype, + /* [in] */ BSTR updates); + + /* [helpstring][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_src )( + IGPAX __RPC_FAR * This, + /* [retval][out] */ BSTR __RPC_FAR *url); + + /* [helpstring][propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_src )( + IGPAX __RPC_FAR * This, + /* [in] */ BSTR url); + + /* [helpstring][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_AutoStart )( + IGPAX __RPC_FAR * This, + /* [retval][out] */ VARIANT_BOOL __RPC_FAR *autoplay); + + /* [helpstring][propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_AutoStart )( + IGPAX __RPC_FAR * This, + /* [in] */ VARIANT_BOOL autoplay); + + END_INTERFACE + } IGPAXVtbl; + + interface IGPAX + { + CONST_VTBL struct IGPAXVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IGPAX_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IGPAX_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IGPAX_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IGPAX_GetTypeInfoCount(This,pctinfo) \ + (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) + +#define IGPAX_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \ + (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) + +#define IGPAX_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \ + (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) + +#define IGPAX_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \ + (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) + + +#define IGPAX_Play(This) \ + (This)->lpVtbl -> Play(This) + +#define IGPAX_Pause(This) \ + (This)->lpVtbl -> Pause(This) + +#define IGPAX_Stop(This) \ + (This)->lpVtbl -> Stop(This) + +#define IGPAX_Update(This,mtype,updates) \ + (This)->lpVtbl -> Update(This,mtype,updates) + +#define IGPAX_get_src(This,url) \ + (This)->lpVtbl -> get_src(This,url) + +#define IGPAX_put_src(This,url) \ + (This)->lpVtbl -> put_src(This,url) + +#define IGPAX_get_AutoStart(This,autoplay) \ + (This)->lpVtbl -> get_AutoStart(This,autoplay) + +#define IGPAX_put_AutoStart(This,autoplay) \ + (This)->lpVtbl -> put_AutoStart(This,autoplay) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IGPAX_Play_Proxy( + IGPAX __RPC_FAR * This); + + +void __RPC_STUB IGPAX_Play_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IGPAX_Pause_Proxy( + IGPAX __RPC_FAR * This); + + +void __RPC_STUB IGPAX_Pause_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IGPAX_Stop_Proxy( + IGPAX __RPC_FAR * This); + + +void __RPC_STUB IGPAX_Stop_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IGPAX_Update_Proxy( + IGPAX __RPC_FAR * This, + /* [in] */ BSTR mtype, + /* [in] */ BSTR updates); + + +void __RPC_STUB IGPAX_Update_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][propget][id] */ HRESULT STDMETHODCALLTYPE IGPAX_get_src_Proxy( + IGPAX __RPC_FAR * This, + /* [retval][out] */ BSTR __RPC_FAR *url); + + +void __RPC_STUB IGPAX_get_src_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][propput][id] */ HRESULT STDMETHODCALLTYPE IGPAX_put_src_Proxy( + IGPAX __RPC_FAR * This, + /* [in] */ BSTR url); + + +void __RPC_STUB IGPAX_put_src_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][propget][id] */ HRESULT STDMETHODCALLTYPE IGPAX_get_AutoStart_Proxy( + IGPAX __RPC_FAR * This, + /* [retval][out] */ VARIANT_BOOL __RPC_FAR *autoplay); + + +void __RPC_STUB IGPAX_get_AutoStart_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][propput][id] */ HRESULT STDMETHODCALLTYPE IGPAX_put_AutoStart_Proxy( + IGPAX __RPC_FAR * This, + /* [in] */ VARIANT_BOOL autoplay); + + +void __RPC_STUB IGPAX_put_AutoStart_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IGPAX_INTERFACE_DEFINED__ */ + + +#ifndef __IGPAXEvents_DISPINTERFACE_DEFINED__ +#define __IGPAXEvents_DISPINTERFACE_DEFINED__ + +/* dispinterface IGPAXEvents */ +/* [helpstring][uuid] */ + + +EXTERN_C const IID DIID_IGPAXEvents; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("1FDA32FC-4C9A-461F-B33B-0715B0343006") + IGPAXEvents : public IDispatch + { + }; + +#else /* C style interface */ + + typedef struct IGPAXEventsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IGPAXEvents __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IGPAXEvents __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IGPAXEvents __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )( + IGPAXEvents __RPC_FAR * This, + /* [out] */ UINT __RPC_FAR *pctinfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )( + IGPAXEvents __RPC_FAR * This, + /* [in] */ UINT iTInfo, + /* [in] */ LCID lcid, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )( + IGPAXEvents __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, + /* [in] */ UINT cNames, + /* [in] */ LCID lcid, + /* [size_is][out] */ DISPID __RPC_FAR *rgDispId); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )( + IGPAXEvents __RPC_FAR * This, + /* [in] */ DISPID dispIdMember, + /* [in] */ REFIID riid, + /* [in] */ LCID lcid, + /* [in] */ WORD wFlags, + /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, + /* [out] */ VARIANT __RPC_FAR *pVarResult, + /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, + /* [out] */ UINT __RPC_FAR *puArgErr); + + END_INTERFACE + } IGPAXEventsVtbl; + + interface IGPAXEvents + { + CONST_VTBL struct IGPAXEventsVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IGPAXEvents_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IGPAXEvents_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IGPAXEvents_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IGPAXEvents_GetTypeInfoCount(This,pctinfo) \ + (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) + +#define IGPAXEvents_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \ + (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) + +#define IGPAXEvents_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \ + (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) + +#define IGPAXEvents_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \ + (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + +#endif /* __IGPAXEvents_DISPINTERFACE_DEFINED__ */ + + +EXTERN_C const CLSID CLSID_GPAX; + +#ifdef __cplusplus + +class DECLSPEC_UUID("181D18E6-4DC1-4B55-B72E-BE2A10064995") +GPAX; +#endif +#endif /* __GPAXLib_LIBRARY_DEFINED__ */ + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/applications/GPAX/GPAX.idl b/applications/GPAX/GPAX.idl new file mode 100644 index 0000000..922910f --- /dev/null +++ b/applications/GPAX/GPAX.idl @@ -0,0 +1,84 @@ +// GPAX.idl : IDL source for GPAX.dll +// + +// This file will be processed by the MIDL tool to +// produce the type library (GPAX.tlb) and marshalling code. + +import "oaidl.idl"; +import "ocidl.idl"; +#include "olectl.h" + + +[ + uuid(E64FAC7F-0134-4A75-A7DA-80D53EBC56A6), + version(1.0), + helpstring("GPAX ActiveX Control") +] + +library GPAXLib +{ + importlib("stdole2.tlb"); + + // Forward declare all types defined in this typelib + interface IGPAX; + dispinterface IGPAXEvents; + + const int DISPID_SRC = 100; + const int DISPID_AutoStart = 101; + + + //IDispatch interface + [ + odl, + uuid(E2A9A937-BB35-47E0-8942-964806299AB4), + helpstring("GPAC ActiveX Control"), + dual, + hidden, + oleautomation + ] + interface IGPAX : IDispatch + { + /*functions*/ + [helpstring("Play Movie")] HRESULT Play(); + [helpstring("Pause/Resume Movie")] HRESULT Pause(); + [helpstring("Stop Movie")] HRESULT Stop(); + [helpstring("Update Scene")] HRESULT Update([in] BSTR mtype, [in] BSTR updates); + + + /*properties*/ + [id(DISPID_SRC), propget, helpstring("Get/Set the media source")] + HRESULT src([out, retval] BSTR* url); + [id(DISPID_SRC), propput, helpstring("Get/Set the media source")] + HRESULT src([in] BSTR url); + + [id(DISPID_AutoStart), propget, helpstring("Get/Set automatic playback upon load")] + HRESULT AutoStart([out, retval] VARIANT_BOOL* autoplay); + [id(DISPID_AutoStart), propput, helpstring("Get/Set automatic playback upon load")] + HRESULT AutoStart([in] VARIANT_BOOL autoplay); + }; + + + //event interface + [ + uuid(1FDA32FC-4C9A-461F-B33B-0715B0343006), + helpstring("GPAX Control Events") + ] + dispinterface IGPAXEvents + { + properties: + methods: + }; + + + //AX control + [ + uuid(181D18E6-4DC1-4B55-B72E-BE2A10064995), + helpstring("GPAC Control"), + control + ] + coclass GPAX + { + [default] interface IGPAX; + [default, source] dispinterface IGPAXEvents; + }; +}; diff --git a/applications/GPAX/GPAX.rc b/applications/GPAX/GPAX.rc new file mode 100644 index 0000000..9ba86a4 --- /dev/null +++ b/applications/GPAX/GPAX.rc @@ -0,0 +1,149 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Chinese (P.R.C.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) +#ifdef _WIN32 +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED +#pragma code_page(936) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "1 TYPELIB ""GPAX.tlb""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Chinese (P.R.C.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "ENST\0" + VALUE "FileDescription", "GPAX\0" + VALUE "FileVersion", "0.4.5\0" + VALUE "InternalName", "GPAX\0" + VALUE "LegalCopyright", "Copyright © 2007\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "GPAX.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "ENST GPAX\0" + VALUE "ProductVersion", "0.4.5\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_GPAXPLUGIN BITMAP DISCARDABLE "gpax.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// REGISTRY +// + +IDR_GPAXPLUGIN REGISTRY DISCARDABLE "GPAX.rgs" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_PROJNAME "GPAX" + IDS_TITLEGPAXProp "&GPAX" + IDS_HELPFILEGPAXProp "Help File Name" + IDS_DOCSTRINGGPAXProp "URLs setting" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +1 TYPELIB "GPAX.tlb" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/applications/GPAX/GPAX.rgs b/applications/GPAX/GPAX.rgs new file mode 100644 index 0000000..2949a85 --- /dev/null +++ b/applications/GPAX/GPAX.rgs @@ -0,0 +1,175 @@ +HKCR +{ + ForceRemove '.aac' + { + val 'Content Type' = s 'audio/aac' + } + ForceRemove '.amr' + { + val 'Content Type' = s 'audio/amr' + } + ForceRemove '.mp4' + { + val 'Content Type' = s 'application/mp4' + } + ForceRemove '.3gp' + { + val 'Content Type' = s 'video/3gpp' + } + ForceRemove '.3g2' + { + val 'Content Type' = s 'video/3gpp2' + } + ForceRemove '.wrl' + { + val 'Content Type' = s 'model/vrml' + } + ForceRemove '.x3dv' + { + val 'Content Type' = s 'model/x3d+vrml' + } + ForceRemove '.x3d' + { + val 'Content Type' = s 'model/x3d+xml' + } + ForceRemove '.svg' + { + val 'Content Type' = s 'image/svg+xml' + } + ForceRemove '.sdp' + { + val 'Content Type' = s 'application/sdp' + } + + GPAX.GPAXPlugin.1 = s 'GPAC ActiveX' + { + CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + } + GPAX.GPAXPlugin = s 'GPAC ActiveX' + { + CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + CurVer = s 'GPAX.GPAXPlugin.1' + } + NoRemove CLSID + { + ForceRemove {181D18E6-4DC1-4B55-B72E-BE2A10064995} = s 'GPAC ActiveX' + { + ProgID = s 'GPAX.GPAXPlugin.1' + VersionIndependentProgID = s 'GPAX.GPAXPlugin' + ForceRemove 'Programmable' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + ForceRemove 'Control' + ForceRemove 'Insertable' + ForceRemove 'ToolboxBitmap32' = s '%MODULE%, 101' + 'MiscStatus' = s '0' + { + '1' = s '131473' + } + 'TypeLib' = s '{E64FAC7F-0134-4A75-A7DA-80D53EBC56A6}' + 'Version' = s '1.0' + ForceRemove 'EnableFullPage' + { + ForceRemove .mp4 + ForceRemove .3gp + ForceRemove .3g2 + ForceRemove .wrl + ForceRemove .x3d + ForceRemove .x3dv + ForceRemove .svg + } + } + } + + NoRemove MIME + { + NoRemove Database + { + NoRemove 'Content Type' + { + 'application/x-gpac' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.gpac' + } + 'application/mp4' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.mp4' + } + 'application/sdp' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.sdp' + } + 'audio/aac' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.aac' + } + 'audio/amr' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.amr' + } + 'audio/mp4' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.mp4' + } + 'audio/mpeg' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.mp3' + } + 'image/svg+xml' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.svg' + } + 'model/vrml' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.wrl' + } + 'model/x3d+vrml' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.x3dv' + } + 'model/x3d+xml' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.x3d' + } + 'video/mp4' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.mp4' + } + 'video/3gpp' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.3gp' + } + 'video/3gpp2' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.3g2' + } + 'video/avi' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.avi' + } + 'video/mpeg' + { + val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' + val Extension = s '.mpg' + } + } + } + } +} diff --git a/applications/GPAX/GPAXPlugin.cpp b/applications/GPAX/GPAXPlugin.cpp new file mode 100644 index 0000000..e32b66a --- /dev/null +++ b/applications/GPAX/GPAXPlugin.cpp @@ -0,0 +1,650 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Y.XI, X. ZHAO, J. Le Feuvre + * Copyright (c) ENST 2006-200x + * All rights reserved + * + * This file is part of GPAC / ActiveX control + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "stdafx.h" +#include "GPAX.h" +#include "GPAXPlugin.h" +#include +#include + +#ifndef _WIN32_WCE +#include +#endif + +///////////////////////////////////////////////////////////////////////////// +// CGPAXPlugin + +#if 0 +static print_err(char *msg, char *title) +{ + u16 w_msg[1024], w_title[1024]; + CE_CharToWide(msg, w_msg); + CE_CharToWide(title, w_title); + ::MessageBox(NULL, w_msg, w_title, MB_OK); +} +#endif + + +void CGPAXPlugin::SetStatusText(char *msg) +{ +#ifndef _WIN32_WCE + if (m_pBrowser) { + if (msg) { + u16 w_msg[1024]; + gf_utf8_mbstowcs(w_msg, 1024, (const char **)&msg); + m_pBrowser->put_StatusText((BSTR) w_msg); + } else { + m_pBrowser->put_StatusText(L""); + } + } +#endif +} +//GPAC player Event Handler. not yet implemented, just dummies here +Bool CGPAXPlugin::EventProc(GF_Event *evt) +{ + char msg[1024]; + if (!m_term) return 0; + + switch (evt->type) { + case GF_EVENT_MESSAGE: + if (evt->message.error) { + sprintf(msg, "(GPAC) %s (%s)", evt->message.message, gf_error_to_string(evt->message.error)); + } else { + sprintf(msg, "(GPAC) %s", evt->message.message); + } + SetStatusText(msg); + break; + case GF_EVENT_PROGRESS: + if (evt->progress.done == evt->progress.total) { + SetStatusText(NULL); + } else { + char *szTitle = ""; + if (evt->progress.progress_type==0) szTitle = "Buffer "; + else if (evt->progress.progress_type==1) szTitle = "Download "; + else if (evt->progress.progress_type==2) szTitle = "Import "; + sprintf(msg, "(GPAC) %s: %02.2f", szTitle, (100.0*evt->progress.done) / evt->progress.total); + SetStatusText(msg); + } + break; + case GF_EVENT_CONNECT: + m_bIsConnected = evt->connect.is_connected; + break; + /*IGNORE any scene size, just work with the size allocated in the parent doc*/ + case GF_EVENT_SCENE_SIZE: + gf_term_set_size(m_term, m_width, m_height); + break; + /*window has been resized (full-screen plugin), resize*/ + case GF_EVENT_SIZE: + m_width = evt->size.width; + m_height = evt->size.height; + gf_term_set_size(m_term, m_width, m_height); + break; + case GF_EVENT_DBLCLICK: + gf_term_set_option(m_term, GF_OPT_FULLSCREEN, !gf_term_get_option(m_term, GF_OPT_FULLSCREEN)); + break; + case GF_EVENT_KEYDOWN: + if ((evt->key.flags & GF_KEY_MOD_ALT)) { + } else { + switch (evt->key.key_code) { + case GF_KEY_HOME: + gf_term_set_option(m_term, GF_OPT_NAVIGATION_TYPE, 1); + break; + case GF_KEY_ESCAPE: + gf_term_set_option(m_term, GF_OPT_FULLSCREEN, !gf_term_get_option(m_term, GF_OPT_FULLSCREEN)); + break; + } + } + break; + case GF_EVENT_NAVIGATE_INFO: + strcpy(msg, evt->navigate.to_url); + SetStatusText(msg); + break; + case GF_EVENT_NAVIGATE: + if (gf_term_is_supported_url(m_term, evt->navigate.to_url, 1, 1)) { + gf_term_navigate_to(m_term, evt->navigate.to_url); + return 1; + } +#ifndef _WIN32_WCE + else if (m_pBrowser) { + u32 i; + const char **sz_ptr; + u16 w_szTar[1024], w_szURL[1024]; + VARIANT target, flags; + flags.intVal = 0; + target.bstrVal = L"_SELF"; + + for (i=0; inavigate.param_count; i++) { + if (!strcmp(evt->navigate.parameters[i], "_parent")) target.bstrVal = L"_PARENT"; + else if (!strcmp(evt->navigate.parameters[i], "_blank")) target.bstrVal = L"_BLANK"; + else if (!strcmp(evt->navigate.parameters[i], "_top")) target.bstrVal = L"_TOP"; + else if (!strcmp(evt->navigate.parameters[i], "_new")) flags.intVal |= navOpenInNewWindow; + else if (!strnicmp(evt->navigate.parameters[i], "_target=", 8)) { + sz_ptr = & evt->navigate.parameters[i]+8; + gf_utf8_mbstowcs(w_szTar, 1024, (const char **)sz_ptr); + target.bstrVal = (BSTR) w_szTar; + } + } + sz_ptr = & evt->navigate.to_url; + gf_utf8_mbstowcs(w_szURL, 1024, (const char **)sz_ptr); + m_pBrowser->Navigate((BSTR) w_szURL, &flags, &target, NULL, NULL);; + return 1; + } +#endif + break; + } + return 0; +} + +Bool GPAX_EventProc(void *ptr, GF_Event *evt) +{ + CGPAXPlugin *_this = (CGPAXPlugin *)ptr; + return _this->EventProc(evt); +} + +//Read Parameters from pPropBag given by MSIE +Bool CGPAXPlugin::ReadParamString(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog, + WCHAR *name, char *buf, int bufsize) +{ + VARIANT v; + HRESULT hr; + Bool retval=0; + + v.vt = VT_EMPTY; + v.bstrVal = NULL; + hr = pPropBag->Read(name, &v, pErrorLog); + if(SUCCEEDED(hr)) + { + if(v.vt==VT_BSTR && v.bstrVal) + { +// USES_CONVERSION; +// lstrcpyn(buf,OLE2T(v.bstrVal),bufsize); + const u16 *srcp = (const u16 *) v.bstrVal; + u32 len = gf_utf8_wcstombs(buf, bufsize, &srcp); + if (len>=0) { + buf[len] = 0; + retval=1; + } + } + VariantClear(&v); + } + return retval; +} + +void CGPAXPlugin::LoadDATAUrl() +{ +#ifndef _WIN32_WCE + HRESULT hr; + + if (m_url[0]) return; + /*get parent doc*/ + CComPtr spContainer; + if (m_spClientSite->GetContainer(&spContainer) != S_OK) + return; + CComPtr spDoc = CComQIPtr(spContainer); + CComPtr spColl; + if (spDoc->get_all(&spColl) != S_OK) + return; + /*get HTML in the doc*/ + CComPtr spDisp; + CComPtr sphtmlObjects; + + CComPtr spDispObjects; + if (spColl->tags(CComVariant("OBJECT"), &spDispObjects) != S_OK) + return; + CComPtr spObjs = CComQIPtr(spDispObjects); + + /*browse all objects and find us*/ + long lCount = 0; + spObjs->get_length(&lCount); + for (long lCnt = 0; lCnt < lCount; lCnt++) { + IDispatch *an_obj= NULL; + CComVariant varEmpty; + CComVariant varName; + varName.vt = VT_I4; + varName.lVal = lCnt; + hr = spObjs->item(varName, varEmpty, &an_obj); + varName.Clear(); + varEmpty.Clear(); + if (hr != S_OK) continue; + + /*get the IHTMLObject*/ + IHTMLObjectElement* pObjectElem=NULL; + an_obj->QueryInterface(IID_IHTMLObjectElement, (void**)&pObjectElem); + if (!pObjectElem) continue; + + /*get its parent owner - it MUST be us*/ + IDispatch *disp= NULL; + pObjectElem->get_object(&disp); + if (disp != this) continue; + + BSTR data = NULL; + if ((pObjectElem->get_data(&data) == S_OK) && data) { + const u16 *srcp = (const u16 *) data; + u32 len = gf_utf8_wcstombs(m_url, MAXLEN_URL, &srcp); + if (len>=0) m_url[len] = 0; + } + SysFreeString(data); + break; + } + + if (m_url) { + UpdateURL(); + } +#endif + +} + +//Create window message fuction. when the window is created, also initialize a instance of +//GPAC player instance. +LRESULT CGPAXPlugin::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + if (m_term) return 0; + + unsigned char config_path[GF_MAX_PATH]; + char *gpac_cfg; + const char *str; + + if (m_hWnd==NULL) return 0; + + + gpac_cfg = "GPAC.cfg"; + +#if defined(_DEBUG) && !defined(_WIN32_WCE) + strcpy((char *) config_path, "D:\\cvs\\gpac\\bin\\w32_deb\\"); +#else + //Here we retrieve GPAC config file in the install diractory, which is indicated in the + //Registry + HKEY hKey = NULL; + DWORD dwSize; +#ifdef _WIN32_WCE + u16 w_path[1024]; + RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("GPAC"), 0, KEY_READ, &hKey); + DWORD dwType = REG_SZ; + dwSize = GF_MAX_PATH; + RegQueryValueEx(hKey, TEXT("InstallDir"), 0, &dwType, (LPBYTE) w_path, &dwSize); + CE_WideToChar(w_path, (char *)config_path); + RegCloseKey(hKey); +#else + RegOpenKeyEx(HKEY_CLASSES_ROOT, "GPAC", 0, KEY_READ, &hKey); + dwSize = GF_MAX_PATH; + RegQueryValueEx(hKey, "InstallDir", NULL, NULL, (unsigned char*) config_path, &dwSize); + RegCloseKey(hKey); +#endif +#endif + + //Create a structure m_user for initialize the terminal. the parameters to set: + //1)config file path + //2)Modules file path + //3)window handler + //4)EventProc + memset(&m_user, 0, sizeof(m_user)); + + m_user.config = gf_cfg_new((const char*) config_path, gpac_cfg); + if(!m_user.config) { + char cfg_file[MAX_PATH]; + /*create a blank config*/ + sprintf(cfg_file, "%s\\%s", config_path, gpac_cfg); + FILE *test = fopen(cfg_file, "wt"); + if (test) fclose(test); + m_user.config = gf_cfg_new((const char *) config_path, gpac_cfg); + if(!m_user.config) { +#ifdef _WIN32_WCE + ::MessageBox(NULL, _T("GPAC Configuration file not found"), _T("Fatal Error"), MB_OK); +#else + ::MessageBox(NULL, "GPAC Configuration file not found", "Fatal Error", MB_OK); +#endif + goto err_exit; + } + gf_cfg_set_key(m_user.config, "General", "ModulesDirectory", (const char *) config_path); + gf_cfg_set_key(m_user.config, "Compositor", "Raster2D", "GPAC 2D Raster"); + sprintf((char *) cfg_file, "%s\\cache", config_path); + gf_cfg_set_key(m_user.config, "General", "CacheDirectory", (const char *) cfg_file); + gf_cfg_set_key(m_user.config, "Network", "AutoReconfigUDP", "no"); + gf_cfg_set_key(m_user.config, "Network", "BufferLength", "0"); + gf_cfg_set_key(m_user.config, "Audio", "ForceConfig", "yes"); + gf_cfg_set_key(m_user.config, "Audio", "NumBuffers", "2"); + gf_cfg_set_key(m_user.config, "Audio", "TotalDuration", "120"); + gf_cfg_set_key(m_user.config, "Video", "DriverName", "dx_hw"); + gf_cfg_set_key(m_user.config, "Video", "UseHardwareMemory", "yes"); + +#ifdef _WIN32_WCE + strcpy((char*)cfg_file, "\\windows"); +#else + ::GetWindowsDirectory((char*)cfg_file, MAX_PATH); +#endif + if (cfg_file[strlen((char*)cfg_file)-1] != '\\') strcat((char*)cfg_file, "\\"); + strcat((char *)cfg_file, "Fonts"); + gf_cfg_set_key(m_user.config, "FontEngine", "FontDirectory", (const char *) cfg_file); + } + + str = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + m_user.modules = gf_modules_new(str, m_user.config); + if(!gf_modules_get_count(m_user.modules)) goto err_exit; + + m_user.os_window_handler = m_hWnd; + m_user.opaque = this; + m_user.EventProc = GPAX_EventProc; + + //create a terminal + m_term = gf_term_new(&m_user); + + if (!m_term) goto err_exit; + + gf_term_set_option(m_term, GF_OPT_AUDIO_VOLUME, 100); + + LoadDATAUrl(); + + RECT rc; + ::GetWindowRect(m_hWnd, &rc); + m_width = rc.right-rc.left; + m_height = rc.bottom-rc.top; + if (m_bAutoStart && strlen(m_url)) Play(); + return 0; + + //Error Processing +err_exit: + if(m_user.modules) + gf_modules_del(m_user.modules); + m_user.modules = NULL; + if(m_user.config) + gf_cfg_del(m_user.config); + m_user.config = NULL; + return 1; +} + +void CGPAXPlugin::UnloadTerm() +{ + if (m_term) { + GF_Terminal *a_term = m_term; + m_term = NULL; + gf_term_del(a_term); + } + if (m_user.modules) gf_modules_del(m_user.modules); + if (m_user.config) gf_cfg_del(m_user.config); + memset(&m_user, 0, sizeof(m_user)); +} +CGPAXPlugin::~CGPAXPlugin() +{ + UnloadTerm(); +#ifndef _WIN32_WCE + if (m_pBrowser) m_pBrowser->Release(); + m_pBrowser = NULL; +#endif +} +LRESULT CGPAXPlugin::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + UnloadTerm(); + return 0; +} + + +HRESULT CGPAXPlugin::OnDraw(ATL_DRAWINFO& di) +{ + if (m_term && m_bInitialDraw) { + m_bInitialDraw = FALSE; + if (m_bAutoStart) Play(); + } + return S_OK; +} + +// Load is called before OnCreate, but it may not be called at +// all if there are no parameters. +STDMETHODIMP CGPAXPlugin::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog) +{ + char szOpt[1024]; + // examine the / tag arguments + + m_url[0] = 0; + ReadParamString(pPropBag,pErrorLog,L"src", m_url, MAXLEN_URL); + if (!m_url[0]) + ReadParamString(pPropBag,pErrorLog,L"data", m_url, MAXLEN_URL); + + if (ReadParamString(pPropBag,pErrorLog,L"autostart", szOpt, 1024)) + m_bAutoStart = (!stricmp(szOpt, "false") || !stricmp(szOpt, "no")) ? 0 : 1; + + if (ReadParamString(pPropBag,pErrorLog,L"use3d", szOpt, 1024)) + m_bUse3D = (!stricmp(szOpt, "true") || !stricmp(szOpt, "yes")) ? 1 : 0; + + if (ReadParamString(pPropBag,pErrorLog,L"aspectratio", szOpt, 1024)) { + if (!stricmp(szOpt, "keep")) m_AR = GF_ASPECT_RATIO_KEEP; + else if (!stricmp(szOpt, "16:9")) m_AR = GF_ASPECT_RATIO_16_9; + else if (!stricmp(szOpt, "4:3")) m_AR = GF_ASPECT_RATIO_4_3; + else if (!stricmp(szOpt, "fill")) m_AR = GF_ASPECT_RATIO_FILL_SCREEN; + } + + if (ReadParamString(pPropBag,pErrorLog,L"loop", szOpt, 1024)) + m_bLoop = !stricmp(szOpt, "true") ? 0 : 1; + + UpdateURL(); + +#ifndef _WIN32_WCE + /*get the top-level container*/ + if (!m_pBrowser) { + IServiceProvider *isp, *isp2 = NULL; + if ( SUCCEEDED(m_spClientSite->QueryInterface(IID_IServiceProvider, reinterpret_cast(&isp)) ) ) { + + if (SUCCEEDED(isp->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, reinterpret_cast(&isp2)) ) ) { + isp2->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, reinterpret_cast(&m_pBrowser)); + isp2->Release(); + } + isp->Release(); + } + } + if (m_pBrowser) m_pBrowser->put_StatusText(L"GPAC Ready"); +#endif + + return IPersistPropertyBagImpl::Load(pPropBag, pErrorLog); +} + +void CGPAXPlugin::UpdateURL() +{ + /*get absolute URL*/ + if (!strlen(m_url)) return; + IMoniker* pMoniker = NULL; + LPOLESTR sDisplayName; + + if (SUCCEEDED(m_spClientSite->GetMoniker(OLEGETMONIKER_TEMPFORUSER, + OLEWHICHMK_CONTAINER, + &pMoniker) ) ) { + char parent_url[1024]; + pMoniker->GetDisplayName(NULL, NULL, &sDisplayName); + wcstombs(parent_url, sDisplayName, 300); + pMoniker->Release(); + + char *abs_url = gf_url_concatenate(parent_url, m_url); + if (abs_url) { + strcpy(m_url, abs_url); + free(abs_url); + } + } +} + +STDMETHODIMP CGPAXPlugin::Save(LPPROPERTYBAG pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties) +{ + u16 wurl[MAXLEN_URL]; + const char *sptr; + u16 len; + + VARIANT value; + if( pPropBag == NULL) return E_INVALIDARG; + + VariantInit(&value); + + V_VT(&value) = VT_BOOL; + V_BOOL(&value) = m_bAutoStart ? VARIANT_TRUE : VARIANT_FALSE; + pPropBag->Write(OLESTR("AutoStart"), &value); + VariantClear(&value); + + V_VT(&value) = VT_BSTR; + + sptr = (const char *)m_url; + len = gf_utf8_mbstowcs(wurl, MAXLEN_URL, &sptr); + V_BSTR(&value) = SysAllocStringLen(NULL, len+1); + memcpy(V_BSTR(&value) , wurl, len*sizeof(u16)); + V_BSTR(&value) [len] = 0; + + pPropBag->Write(OLESTR("src"), &value); + VariantClear(&value); + return S_OK; +} + +STDMETHODIMP CGPAXPlugin::Play() +{ + if (m_term) { + if (!m_bIsConnected) { + if (strlen(m_url)) { + gf_term_connect(m_term, m_url); + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, m_AR); + } + } else + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); //if target is connected, set it playing + } + return S_OK; +} + +STDMETHODIMP CGPAXPlugin::Pause() +{ + if(m_term) { + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE) == GF_STATE_PAUSED) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + } else { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + } + } + return S_OK; +} + +STDMETHODIMP CGPAXPlugin::Stop() +{ + if(m_term) gf_term_disconnect(m_term); //set it stop + return S_OK; +} + +STDMETHODIMP CGPAXPlugin::Update(BSTR _mtype, BSTR _updates) +{ + if (m_term) { + u16 *srcp; + u32 len; + char mtype[1024], *updates; + + srcp = (u16 *) _mtype; + len = gf_utf8_wcstombs(mtype, 1024, (const u16 **)&srcp); + mtype[len] = 0; + + srcp = (u16 *)_updates; + len = gf_utf8_wcstombs(NULL, 0, (const u16 **)&srcp); + if (len) { + updates = (char *) malloc(sizeof(char) * (len+1)); + srcp = (u16 *)_updates; + len = gf_utf8_wcstombs(updates, len, (const u16 **)&srcp); + updates[len] = 0; + gf_term_scene_update(m_term, mtype, updates); + free(updates); + } + } + return S_OK; +} + +STDMETHODIMP CGPAXPlugin::get_src(BSTR *url) +{ + u16 wurl[MAXLEN_URL]; + const char *sptr; + u16 len; + if (url==NULL) return E_POINTER; + + sptr = (const char *)m_url; + len = gf_utf8_mbstowcs(wurl, MAXLEN_URL, &sptr); + *url = SysAllocStringLen(NULL, len+1); + memcpy(*url, wurl, len*sizeof(u16)); + *url[len] = 0; + return S_OK; +} +STDMETHODIMP CGPAXPlugin::put_src(BSTR url) +{ + const u16 *srcp = (const u16 *)url; + u32 len = gf_utf8_wcstombs(m_url, MAXLEN_URL, &srcp); + m_url[len] = 0; + UpdateURL(); + return S_OK; +} + +STDMETHODIMP CGPAXPlugin::get_AutoStart(VARIANT_BOOL *as) +{ + if (as==NULL) return E_POINTER; + *as = m_bAutoStart ? VARIANT_TRUE: VARIANT_FALSE; + return S_OK; +} +STDMETHODIMP CGPAXPlugin::put_AutoStart(VARIANT_BOOL as) +{ + m_bAutoStart = (as !=VARIANT_FALSE) ? TRUE: FALSE; + return S_OK; +} + +STDMETHODIMP CGPAXPlugin::GetInterfaceSafetyOptions( + REFIID riid, + DWORD *pdwSupportedOptions, + DWORD *pdwEnabledOptions +) +{ + if( (NULL == pdwSupportedOptions) || (NULL == pdwEnabledOptions) ) + return E_POINTER; + + *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACESAFE_FOR_UNTRUSTED_CALLER; + + if ((IID_IDispatch == riid) || (IID_IGPAX == riid)) { + *pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER; + return NOERROR; + } + else if (IID_IPersistPropertyBag == riid) { + *pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA; + return NOERROR; + } + *pdwEnabledOptions = 0; + return E_NOINTERFACE; +}; + +STDMETHODIMP CGPAXPlugin::SetInterfaceSafetyOptions( + REFIID riid, + DWORD dwOptionSetMask, + DWORD dwEnabledOptions +) +{ + if ((IID_IDispatch == riid) || (IID_IGPAX == riid) ) { + if( (INTERFACESAFE_FOR_UNTRUSTED_CALLER == dwOptionSetMask) + && (INTERFACESAFE_FOR_UNTRUSTED_CALLER == dwEnabledOptions) ) { + return NOERROR; + } + return E_FAIL; + } + else if (IID_IPersistPropertyBag == riid) { + if( (INTERFACESAFE_FOR_UNTRUSTED_DATA == dwOptionSetMask) + && (INTERFACESAFE_FOR_UNTRUSTED_DATA == dwEnabledOptions) ) { + return NOERROR; + } + return E_FAIL; + } + return E_FAIL; +}; + diff --git a/applications/GPAX/GPAXPlugin.h b/applications/GPAX/GPAXPlugin.h new file mode 100644 index 0000000..ed95423 --- /dev/null +++ b/applications/GPAX/GPAXPlugin.h @@ -0,0 +1,257 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Y.XI, X. ZHAO, J. Le Feuvre + * Copyright (c) ENST 2006-200x + * All rights reserved + * + * This file is part of GPAC / ActiveX control + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __GPAXPLUGIN_H_ +#define __GPAXPLUGIN_H_ + +#define MAXLEN_URL 300 + +#include "resource.h" // main symbols +#include +#include + + + +#include +#include +#include +#include +#include + +#if (_MSC_VER >= 1300) +using namespace ATL; +#endif + + +Bool GPAX_EventProc(void *ptr, GF_Event *evt); + +///////////////////////////////////////////////////////////////////////////// +// CGPAXPlugin +class ATL_NO_VTABLE CGPAXPlugin : + public CComObjectRootEx, + public IDispatchImpl, + public CComControl, + public CComCoClass, + public IOleControlImpl, + public IOleObjectImpl, + public IOleInPlaceActiveObjectImpl, + public IViewObjectExImpl, + public IOleInPlaceObjectWindowlessImpl, + public IProvideClassInfo2Impl<&CLSID_GPAX, &DIID_IGPAXEvents, &LIBID_GPAXLib>, + + public IPersistStreamInitImpl, + public ISupportErrorInfo, + public IConnectionPointContainerImpl, + public IPersistStorageImpl, + public ISpecifyPropertyPagesImpl, + public IQuickActivateImpl, + public IDataObjectImpl, + public IPropertyNotifySinkCP, + + public IPersistPropertyBagImpl, + public IObjectSafetyImpl + +{ +public: + CGPAXPlugin() { + m_term = NULL; + m_bAutoStart = TRUE; + m_bInitialDraw = TRUE; + m_bWindowOnly = TRUE; //to declare that the control is a window control in order + //to inherit the member variable m_hWnd which contains the window handler + m_bIsConnected = 0; + m_bUse3D = 0; + m_AR = GF_ASPECT_RATIO_KEEP; + m_url[0] = 0; +#ifndef _WIN32_WCE + m_pBrowser = NULL; +#endif + memset(&m_user, 0, sizeof(m_user)); + + m_dwCurrentSafety = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA; + } + + ~CGPAXPlugin(); + + Bool EventProc(GF_Event *evt); + + + DECLARE_REGISTRY_RESOURCEID(IDR_GPAXPLUGIN) + DECLARE_PROTECT_FINAL_CONSTRUCT() +#if (_MSC_VER >= 1300) + DECLARE_OLEMISC_STATUS(OLEMISC_ACTSLIKEBUTTON | OLEMISC_ACTIVATEWHENVISIBLE) +#endif + + static LPCTSTR GetWindowClassName() { return TEXT("GPAC ActiveX"); } + + BEGIN_COM_MAP(CGPAXPlugin) + COM_INTERFACE_ENTRY(IGPAX) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IViewObjectEx) + COM_INTERFACE_ENTRY(IProvideClassInfo) + COM_INTERFACE_ENTRY(IOleControl) + COM_INTERFACE_ENTRY(IOleObject) + + COM_INTERFACE_ENTRY_IMPL(IViewObjectEx) + COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx) + COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx) + COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless) + COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless) + COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceActiveObject) + + COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject) + COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless) + +// COM_INTERFACE_ENTRY(IObjectSafety) + COM_INTERFACE_ENTRY_IID(IID_IObjectSafety, IObjectSafety) + COM_INTERFACE_ENTRY(IPersistPropertyBag) + COM_INTERFACE_ENTRY_IMPL_IID(IID_IPersist, IPersistPropertyBag) + +/* COM_INTERFACE_ENTRY(IViewObject) + COM_INTERFACE_ENTRY(IViewObject2) + COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless) + COM_INTERFACE_ENTRY(IOleInPlaceObject) + */ + + COM_INTERFACE_ENTRY(IProvideClassInfo2) + COM_INTERFACE_ENTRY(IPersistStreamInit) + COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit) + COM_INTERFACE_ENTRY(ISupportErrorInfo) + COM_INTERFACE_ENTRY(IConnectionPointContainer) + COM_INTERFACE_ENTRY(ISpecifyPropertyPages) + COM_INTERFACE_ENTRY(IQuickActivate) + COM_INTERFACE_ENTRY(IPersistStorage) + COM_INTERFACE_ENTRY(IDataObject) + + END_COM_MAP() + + BEGIN_PROP_MAP(CGPAXPlugin) + END_PROP_MAP() + + BEGIN_CONNECTION_POINT_MAP(CGPAXPlugin) + CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink) + END_CONNECTION_POINT_MAP() + + BEGIN_MSG_MAP(CGPAXPlugin) + CHAIN_MSG_MAP(CComControl) + DEFAULT_REFLECTION_HANDLER() + MESSAGE_HANDLER(WM_CREATE, OnCreate) + MESSAGE_HANDLER(WM_DESTROY, OnDestroy) + + END_MSG_MAP() + // Handler prototypes: + // LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + // LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); + // LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled); + + + + // ISupportsErrorInfo + STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid) + { + static const IID* arr[] = + { + &IID_IGPAX, + }; + for (int i=0; i + // + // + //the interface IPersistPropertyBag enable MSIE and ActiveX Control to communicate these + //properties included in tags + STDMETHODIMP Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog); + STDMETHODIMP Save(LPPROPERTYBAG, BOOL, BOOL); + + +private: + Bool ReadParamString(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog, WCHAR *name, char *buf, int bufsize); + void SetStatusText(char *msg); + void UpdateURL(); + void UnloadTerm(); + void LoadDATAUrl(); + + GF_Terminal *m_term; + GF_User m_user; + char m_url[MAXLEN_URL]; +#ifndef _WIN32_WCE + /*pointer to the parent browser if any*/ + IWebBrowser2 *m_pBrowser; +#endif + + u32 m_width, m_height, m_AR; + Bool m_bIsConnected, m_bInitialDraw, m_bAutoStart, m_bUse3D, m_bLoop; + +}; + + + +#endif //__GPAXPLUGIN_H_ diff --git a/applications/GPAX/GPAX_i.c b/applications/GPAX/GPAX_i.c new file mode 100644 index 0000000..3be5d97 --- /dev/null +++ b/applications/GPAX/GPAX_i.c @@ -0,0 +1,178 @@ + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + +/* this ALWAYS GENERATED file contains the IIDs and CLSIDs */ + +/* link this file in with the server and any clients */ + + + /* File created by MIDL compiler version 5.03.0286 */ +/* at Thu Jul 20 19:14:15 2006 + */ +/* Compiler settings for \CVS\gpac\applications\GPAX\GPAX.idl: + Oicf (OptLev=i2), W1, Zp8, env=Win32 (32b run), ms_ext, c_ext + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: + __declspec(uuid()), __declspec(selectany), __declspec(novtable) + DECLSPEC_UUID(), MIDL_INTERFACE() +*/ +//@@MIDL_FILE_HEADING( ) + +#if !defined(_M_IA64) && !defined(_M_AXP64) + +#ifdef __cplusplus +extern "C"{ +#endif + + +#include +#include + +#ifdef _MIDL_USE_GUIDDEF_ + +#ifndef INITGUID +#define INITGUID +#include +#undef INITGUID +#else +#include +#endif + +#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ + DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) + +#else // !_MIDL_USE_GUIDDEF_ + +#ifndef __IID_DEFINED__ +#define __IID_DEFINED__ + +typedef struct _IID +{ + unsigned long x; + unsigned short s1; + unsigned short s2; + unsigned char c[8]; +} IID; + +#endif // __IID_DEFINED__ + +#ifndef CLSID_DEFINED +#define CLSID_DEFINED +typedef IID CLSID; +#endif // CLSID_DEFINED + +#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ + const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} + +#endif !_MIDL_USE_GUIDDEF_ + +MIDL_DEFINE_GUID(IID, LIBID_GPAXLib,0xE64FAC7F,0x0134,0x4A75,0xA7,0xDA,0x80,0xD5,0x3E,0xBC,0x56,0xA6); + + +MIDL_DEFINE_GUID(IID, IID_IGPAX,0xE2A9A937,0xBB35,0x47E0,0x89,0x42,0x96,0x48,0x06,0x29,0x9A,0xB4); + + +MIDL_DEFINE_GUID(IID, DIID_IGPAXEvents,0x1FDA32FC,0x4C9A,0x461F,0xB3,0x3B,0x07,0x15,0xB0,0x34,0x30,0x06); + + +MIDL_DEFINE_GUID(CLSID, CLSID_GPAX,0x181D18E6,0x4DC1,0x4B55,0xB7,0x2E,0xBE,0x2A,0x10,0x06,0x49,0x95); + +#undef MIDL_DEFINE_GUID + +#ifdef __cplusplus +} +#endif + + + +#endif /* !defined(_M_IA64) && !defined(_M_AXP64)*/ + + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + +/* this ALWAYS GENERATED file contains the IIDs and CLSIDs */ + +/* link this file in with the server and any clients */ + + + /* File created by MIDL compiler version 5.03.0286 */ +/* at Thu Jul 20 19:14:15 2006 + */ +/* Compiler settings for \CVS\gpac\applications\GPAX\GPAX.idl: + Oicf (OptLev=i2), W1, Zp8, env=Win64 (32b run,appending), ms_ext, c_ext, robust + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: + __declspec(uuid()), __declspec(selectany), __declspec(novtable) + DECLSPEC_UUID(), MIDL_INTERFACE() +*/ +//@@MIDL_FILE_HEADING( ) + +#if defined(_M_IA64) || defined(_M_AXP64) + +#ifdef __cplusplus +extern "C"{ +#endif + + +#include +#include + +#ifdef _MIDL_USE_GUIDDEF_ + +#ifndef INITGUID +#define INITGUID +#include +#undef INITGUID +#else +#include +#endif + +#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ + DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) + +#else // !_MIDL_USE_GUIDDEF_ + +#ifndef __IID_DEFINED__ +#define __IID_DEFINED__ + +typedef struct _IID +{ + unsigned long x; + unsigned short s1; + unsigned short s2; + unsigned char c[8]; +} IID; + +#endif // __IID_DEFINED__ + +#ifndef CLSID_DEFINED +#define CLSID_DEFINED +typedef IID CLSID; +#endif // CLSID_DEFINED + +#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ + const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} + +#endif !_MIDL_USE_GUIDDEF_ + +MIDL_DEFINE_GUID(IID, LIBID_GPAXLib,0xE64FAC7F,0x0134,0x4A75,0xA7,0xDA,0x80,0xD5,0x3E,0xBC,0x56,0xA6); + + +MIDL_DEFINE_GUID(IID, IID_IGPAX,0xE2A9A937,0xBB35,0x47E0,0x89,0x42,0x96,0x48,0x06,0x29,0x9A,0xB4); + + +MIDL_DEFINE_GUID(IID, DIID_IGPAXEvents,0x1FDA32FC,0x4C9A,0x461F,0xB3,0x3B,0x07,0x15,0xB0,0x34,0x30,0x06); + + +MIDL_DEFINE_GUID(CLSID, CLSID_GPAX,0x181D18E6,0x4DC1,0x4B55,0xB7,0x2E,0xBE,0x2A,0x10,0x06,0x49,0x95); + +#undef MIDL_DEFINE_GUID + +#ifdef __cplusplus +} +#endif + + + +#endif /* defined(_M_IA64) || defined(_M_AXP64)*/ + diff --git a/applications/GPAX/GPAX_p.c b/applications/GPAX/GPAX_p.c new file mode 100644 index 0000000..64594b3 --- /dev/null +++ b/applications/GPAX/GPAX_p.c @@ -0,0 +1,602 @@ +/* this ALWAYS GENERATED file contains the proxy stub code */ + + +/* File created by MIDL compiler version 5.01.0164 */ +/* at Mon Jul 17 15:58:48 2006 + */ +/* Compiler settings for D:\CVS\gpac\applications\GPAX\GPAX.idl: + Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext + error checks: allocation ref bounds_check enum stub_data +*/ +//@@MIDL_FILE_HEADING( ) + +#define USE_STUBLESS_PROXY + + +/* verify that the version is high enough to compile this file*/ +#ifndef __REDQ_RPCPROXY_H_VERSION__ +#define __REQUIRED_RPCPROXY_H_VERSION__ 440 +#endif + + +#include "rpcproxy.h" +#ifndef __RPCPROXY_H_VERSION__ +#error this stub requires an updated version of +#endif // __RPCPROXY_H_VERSION__ + + +#include "GPAX.h" + +#define TYPE_FORMAT_STRING_SIZE 59 +#define PROC_FORMAT_STRING_SIZE 213 + +typedef struct _MIDL_TYPE_FORMAT_STRING + { + short Pad; + unsigned char Format[ TYPE_FORMAT_STRING_SIZE ]; + } MIDL_TYPE_FORMAT_STRING; + +typedef struct _MIDL_PROC_FORMAT_STRING + { + short Pad; + unsigned char Format[ PROC_FORMAT_STRING_SIZE ]; + } MIDL_PROC_FORMAT_STRING; + + +extern const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString; +extern const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString; + + +/* Standard interface: __MIDL_itf_GPAX_0000, ver. 0.0, + GUID={0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} */ + + +/* Object interface: IUnknown, ver. 0.0, + GUID={0x00000000,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}} */ + + +/* Object interface: IDispatch, ver. 0.0, + GUID={0x00020400,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}} */ + + +/* Object interface: IGPAX, ver. 0.0, + GUID={0xE2A9A937,0xBB35,0x47E0,{0x89,0x42,0x96,0x48,0x06,0x29,0x9A,0xB4}} */ + + +extern const MIDL_STUB_DESC Object_StubDesc; + + +extern const MIDL_SERVER_INFO IGPAX_ServerInfo; + +#pragma code_seg(".orpc") +extern const USER_MARSHAL_ROUTINE_QUADRUPLE UserMarshalRoutines[1]; + +static const MIDL_STUB_DESC Object_StubDesc = + { + 0, + NdrOleAllocate, + NdrOleFree, + 0, + 0, + 0, + 0, + 0, + __MIDL_TypeFormatString.Format, + 1, /* -error bounds_check flag */ + 0x20000, /* Ndr library version */ + 0, + 0x50100a4, /* MIDL Version 5.1.164 */ + 0, + UserMarshalRoutines, + 0, /* notify & notify_flag routine table */ + 1, /* Flags */ + 0, /* Reserved3 */ + 0, /* Reserved4 */ + 0 /* Reserved5 */ + }; + +static const unsigned short IGPAX_FormatStringOffsetTable[] = + { + (unsigned short) -1, + (unsigned short) -1, + (unsigned short) -1, + (unsigned short) -1, + 0, + 22, + 44, + 66, + 100, + 128, + 156, + 184 + }; + +static const MIDL_SERVER_INFO IGPAX_ServerInfo = + { + &Object_StubDesc, + 0, + __MIDL_ProcFormatString.Format, + &IGPAX_FormatStringOffsetTable[-3], + 0, + 0, + 0, + 0 + }; + +static const MIDL_STUBLESS_PROXY_INFO IGPAX_ProxyInfo = + { + &Object_StubDesc, + __MIDL_ProcFormatString.Format, + &IGPAX_FormatStringOffsetTable[-3], + 0, + 0, + 0 + }; + +CINTERFACE_PROXY_VTABLE(15) _IGPAXProxyVtbl = +{ + &IGPAX_ProxyInfo, + &IID_IGPAX, + IUnknown_QueryInterface_Proxy, + IUnknown_AddRef_Proxy, + IUnknown_Release_Proxy , + 0 /* (void *)-1 /* IDispatch::GetTypeInfoCount */ , + 0 /* (void *)-1 /* IDispatch::GetTypeInfo */ , + 0 /* (void *)-1 /* IDispatch::GetIDsOfNames */ , + 0 /* IDispatch_Invoke_Proxy */ , + (void *)-1 /* IGPAX::Play */ , + (void *)-1 /* IGPAX::Pause */ , + (void *)-1 /* IGPAX::Stop */ , + (void *)-1 /* IGPAX::Update */ , + (void *)-1 /* IGPAX::get_URL */ , + (void *)-1 /* IGPAX::put_URL */ , + (void *)-1 /* IGPAX::get_AutoStart */ , + (void *)-1 /* IGPAX::put_AutoStart */ +}; + + +static const PRPC_STUB_FUNCTION IGPAX_table[] = +{ + STUB_FORWARDING_FUNCTION, + STUB_FORWARDING_FUNCTION, + STUB_FORWARDING_FUNCTION, + STUB_FORWARDING_FUNCTION, + NdrStubCall2, + NdrStubCall2, + NdrStubCall2, + NdrStubCall2, + NdrStubCall2, + NdrStubCall2, + NdrStubCall2, + NdrStubCall2 +}; + +CInterfaceStubVtbl _IGPAXStubVtbl = +{ + &IID_IGPAX, + &IGPAX_ServerInfo, + 15, + &IGPAX_table[-3], + CStdStubBuffer_DELEGATING_METHODS +}; + +#pragma data_seg(".rdata") + +static const USER_MARSHAL_ROUTINE_QUADRUPLE UserMarshalRoutines[1] = + { + + { + BSTR_UserSize + ,BSTR_UserMarshal + ,BSTR_UserUnmarshal + ,BSTR_UserFree + } + + }; + + +#if !defined(__RPC_WIN32__) +#error Invalid build platform for this stub. +#endif + +#if !(TARGET_IS_NT40_OR_LATER) +#error You need a Windows NT 4.0 or later to run this stub because it uses these features: +#error -Oif or -Oicf, [wire_marshal] or [user_marshal] attribute, more than 32 methods in the interface. +#error However, your C/C++ compilation flags indicate you intend to run this app on earlier systems. +#error This app will die there with the RPC_X_WRONG_STUB_VERSION error. +#endif + + +static const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString = + { + 0, + { + + /* Procedure Play */ + + 0x33, /* FC_AUTO_HANDLE */ + 0x6c, /* Old Flags: object, Oi2 */ +/* 2 */ NdrFcLong( 0x0 ), /* 0 */ +/* 6 */ NdrFcShort( 0x7 ), /* 7 */ +#ifndef _ALPHA_ +/* 8 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ +#else + NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ +#endif +/* 10 */ NdrFcShort( 0x0 ), /* 0 */ +/* 12 */ NdrFcShort( 0x8 ), /* 8 */ +/* 14 */ 0x4, /* Oi2 Flags: has return, */ + 0x1, /* 1 */ + + /* Return value */ + +/* 16 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ +#ifndef _ALPHA_ +/* 18 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 20 */ 0x8, /* FC_LONG */ + 0x0, /* 0 */ + + /* Procedure Pause */ + +/* 22 */ 0x33, /* FC_AUTO_HANDLE */ + 0x6c, /* Old Flags: object, Oi2 */ +/* 24 */ NdrFcLong( 0x0 ), /* 0 */ +/* 28 */ NdrFcShort( 0x8 ), /* 8 */ +#ifndef _ALPHA_ +/* 30 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ +#else + NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ +#endif +/* 32 */ NdrFcShort( 0x0 ), /* 0 */ +/* 34 */ NdrFcShort( 0x8 ), /* 8 */ +/* 36 */ 0x4, /* Oi2 Flags: has return, */ + 0x1, /* 1 */ + + /* Return value */ + +/* 38 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ +#ifndef _ALPHA_ +/* 40 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 42 */ 0x8, /* FC_LONG */ + 0x0, /* 0 */ + + /* Procedure Stop */ + +/* 44 */ 0x33, /* FC_AUTO_HANDLE */ + 0x6c, /* Old Flags: object, Oi2 */ +/* 46 */ NdrFcLong( 0x0 ), /* 0 */ +/* 50 */ NdrFcShort( 0x9 ), /* 9 */ +#ifndef _ALPHA_ +/* 52 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ +#else + NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ +#endif +/* 54 */ NdrFcShort( 0x0 ), /* 0 */ +/* 56 */ NdrFcShort( 0x8 ), /* 8 */ +/* 58 */ 0x4, /* Oi2 Flags: has return, */ + 0x1, /* 1 */ + + /* Return value */ + +/* 60 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ +#ifndef _ALPHA_ +/* 62 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 64 */ 0x8, /* FC_LONG */ + 0x0, /* 0 */ + + /* Procedure Update */ + +/* 66 */ 0x33, /* FC_AUTO_HANDLE */ + 0x6c, /* Old Flags: object, Oi2 */ +/* 68 */ NdrFcLong( 0x0 ), /* 0 */ +/* 72 */ NdrFcShort( 0xa ), /* 10 */ +#ifndef _ALPHA_ +/* 74 */ NdrFcShort( 0x10 ), /* x86, MIPS, PPC Stack size/offset = 16 */ +#else + NdrFcShort( 0x20 ), /* Alpha Stack size/offset = 32 */ +#endif +/* 76 */ NdrFcShort( 0x0 ), /* 0 */ +/* 78 */ NdrFcShort( 0x8 ), /* 8 */ +/* 80 */ 0x6, /* Oi2 Flags: clt must size, has return, */ + 0x3, /* 3 */ + + /* Parameter mtype */ + +/* 82 */ NdrFcShort( 0x8b ), /* Flags: must size, must free, in, by val, */ +#ifndef _ALPHA_ +/* 84 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 86 */ NdrFcShort( 0x1a ), /* Type Offset=26 */ + + /* Parameter updates */ + +/* 88 */ NdrFcShort( 0x8b ), /* Flags: must size, must free, in, by val, */ +#ifndef _ALPHA_ +/* 90 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ +#else + NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ +#endif +/* 92 */ NdrFcShort( 0x1a ), /* Type Offset=26 */ + + /* Return value */ + +/* 94 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ +#ifndef _ALPHA_ +/* 96 */ NdrFcShort( 0xc ), /* x86, MIPS, PPC Stack size/offset = 12 */ +#else + NdrFcShort( 0x18 ), /* Alpha Stack size/offset = 24 */ +#endif +/* 98 */ 0x8, /* FC_LONG */ + 0x0, /* 0 */ + + /* Procedure get_URL */ + +/* 100 */ 0x33, /* FC_AUTO_HANDLE */ + 0x6c, /* Old Flags: object, Oi2 */ +/* 102 */ NdrFcLong( 0x0 ), /* 0 */ +/* 106 */ NdrFcShort( 0xb ), /* 11 */ +#ifndef _ALPHA_ +/* 108 */ NdrFcShort( 0xc ), /* x86, MIPS, PPC Stack size/offset = 12 */ +#else + NdrFcShort( 0x18 ), /* Alpha Stack size/offset = 24 */ +#endif +/* 110 */ NdrFcShort( 0x0 ), /* 0 */ +/* 112 */ NdrFcShort( 0x8 ), /* 8 */ +/* 114 */ 0x5, /* Oi2 Flags: srv must size, has return, */ + 0x2, /* 2 */ + + /* Parameter mrl */ + +/* 116 */ NdrFcShort( 0x2113 ), /* Flags: must size, must free, out, simple ref, srv alloc size=8 */ +#ifndef _ALPHA_ +/* 118 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 120 */ NdrFcShort( 0x2c ), /* Type Offset=44 */ + + /* Return value */ + +/* 122 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ +#ifndef _ALPHA_ +/* 124 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ +#else + NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ +#endif +/* 126 */ 0x8, /* FC_LONG */ + 0x0, /* 0 */ + + /* Procedure put_URL */ + +/* 128 */ 0x33, /* FC_AUTO_HANDLE */ + 0x6c, /* Old Flags: object, Oi2 */ +/* 130 */ NdrFcLong( 0x0 ), /* 0 */ +/* 134 */ NdrFcShort( 0xc ), /* 12 */ +#ifndef _ALPHA_ +/* 136 */ NdrFcShort( 0xc ), /* x86, MIPS, PPC Stack size/offset = 12 */ +#else + NdrFcShort( 0x18 ), /* Alpha Stack size/offset = 24 */ +#endif +/* 138 */ NdrFcShort( 0x0 ), /* 0 */ +/* 140 */ NdrFcShort( 0x8 ), /* 8 */ +/* 142 */ 0x6, /* Oi2 Flags: clt must size, has return, */ + 0x2, /* 2 */ + + /* Parameter mrl */ + +/* 144 */ NdrFcShort( 0x8b ), /* Flags: must size, must free, in, by val, */ +#ifndef _ALPHA_ +/* 146 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 148 */ NdrFcShort( 0x1a ), /* Type Offset=26 */ + + /* Return value */ + +/* 150 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ +#ifndef _ALPHA_ +/* 152 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ +#else + NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ +#endif +/* 154 */ 0x8, /* FC_LONG */ + 0x0, /* 0 */ + + /* Procedure get_AutoStart */ + +/* 156 */ 0x33, /* FC_AUTO_HANDLE */ + 0x6c, /* Old Flags: object, Oi2 */ +/* 158 */ NdrFcLong( 0x0 ), /* 0 */ +/* 162 */ NdrFcShort( 0xd ), /* 13 */ +#ifndef _ALPHA_ +/* 164 */ NdrFcShort( 0xc ), /* x86, MIPS, PPC Stack size/offset = 12 */ +#else + NdrFcShort( 0x18 ), /* Alpha Stack size/offset = 24 */ +#endif +/* 166 */ NdrFcShort( 0x0 ), /* 0 */ +/* 168 */ NdrFcShort( 0xe ), /* 14 */ +/* 170 */ 0x4, /* Oi2 Flags: has return, */ + 0x2, /* 2 */ + + /* Parameter autoplay */ + +/* 172 */ NdrFcShort( 0x2150 ), /* Flags: out, base type, simple ref, srv alloc size=8 */ +#ifndef _ALPHA_ +/* 174 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 176 */ 0x6, /* FC_SHORT */ + 0x0, /* 0 */ + + /* Return value */ + +/* 178 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ +#ifndef _ALPHA_ +/* 180 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ +#else + NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ +#endif +/* 182 */ 0x8, /* FC_LONG */ + 0x0, /* 0 */ + + /* Procedure put_AutoStart */ + +/* 184 */ 0x33, /* FC_AUTO_HANDLE */ + 0x6c, /* Old Flags: object, Oi2 */ +/* 186 */ NdrFcLong( 0x0 ), /* 0 */ +/* 190 */ NdrFcShort( 0xe ), /* 14 */ +#ifndef _ALPHA_ +/* 192 */ NdrFcShort( 0xc ), /* x86, MIPS, PPC Stack size/offset = 12 */ +#else + NdrFcShort( 0x18 ), /* Alpha Stack size/offset = 24 */ +#endif +/* 194 */ NdrFcShort( 0x6 ), /* 6 */ +/* 196 */ NdrFcShort( 0x8 ), /* 8 */ +/* 198 */ 0x4, /* Oi2 Flags: has return, */ + 0x2, /* 2 */ + + /* Parameter autoplay */ + +/* 200 */ NdrFcShort( 0x48 ), /* Flags: in, base type, */ +#ifndef _ALPHA_ +/* 202 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 204 */ 0x6, /* FC_SHORT */ + 0x0, /* 0 */ + + /* Return value */ + +/* 206 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ +#ifndef _ALPHA_ +/* 208 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ +#else + NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ +#endif +/* 210 */ 0x8, /* FC_LONG */ + 0x0, /* 0 */ + + 0x0 + } + }; + +static const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString = + { + 0, + { + NdrFcShort( 0x0 ), /* 0 */ +/* 2 */ + 0x12, 0x0, /* FC_UP */ +/* 4 */ NdrFcShort( 0xc ), /* Offset= 12 (16) */ +/* 6 */ + 0x1b, /* FC_CARRAY */ + 0x1, /* 1 */ +/* 8 */ NdrFcShort( 0x2 ), /* 2 */ +/* 10 */ 0x9, /* Corr desc: FC_ULONG */ + 0x0, /* */ +/* 12 */ NdrFcShort( 0xfffc ), /* -4 */ +/* 14 */ 0x6, /* FC_SHORT */ + 0x5b, /* FC_END */ +/* 16 */ + 0x17, /* FC_CSTRUCT */ + 0x3, /* 3 */ +/* 18 */ NdrFcShort( 0x8 ), /* 8 */ +/* 20 */ NdrFcShort( 0xfffffff2 ), /* Offset= -14 (6) */ +/* 22 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 24 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 26 */ 0xb4, /* FC_USER_MARSHAL */ + 0x83, /* 131 */ +/* 28 */ NdrFcShort( 0x0 ), /* 0 */ +/* 30 */ NdrFcShort( 0x4 ), /* 4 */ +/* 32 */ NdrFcShort( 0x0 ), /* 0 */ +/* 34 */ NdrFcShort( 0xffffffe0 ), /* Offset= -32 (2) */ +/* 36 */ + 0x11, 0x4, /* FC_RP [alloced_on_stack] */ +/* 38 */ NdrFcShort( 0x6 ), /* Offset= 6 (44) */ +/* 40 */ + 0x13, 0x0, /* FC_OP */ +/* 42 */ NdrFcShort( 0xffffffe6 ), /* Offset= -26 (16) */ +/* 44 */ 0xb4, /* FC_USER_MARSHAL */ + 0x83, /* 131 */ +/* 46 */ NdrFcShort( 0x0 ), /* 0 */ +/* 48 */ NdrFcShort( 0x4 ), /* 4 */ +/* 50 */ NdrFcShort( 0x0 ), /* 0 */ +/* 52 */ NdrFcShort( 0xfffffff4 ), /* Offset= -12 (40) */ +/* 54 */ + 0x11, 0xc, /* FC_RP [alloced_on_stack] [simple_pointer] */ +/* 56 */ 0x6, /* FC_SHORT */ + 0x5c, /* FC_PAD */ + + 0x0 + } + }; + +const CInterfaceProxyVtbl * _GPAX_ProxyVtblList[] = +{ + ( CInterfaceProxyVtbl *) &_IGPAXProxyVtbl, + 0 +}; + +const CInterfaceStubVtbl * _GPAX_StubVtblList[] = +{ + ( CInterfaceStubVtbl *) &_IGPAXStubVtbl, + 0 +}; + +PCInterfaceName const _GPAX_InterfaceNamesList[] = +{ + "IGPAX", + 0 +}; + +const IID * _GPAX_BaseIIDList[] = +{ + &IID_IDispatch, + 0 +}; + + +#define _GPAX_CHECK_IID(n) IID_GENERIC_CHECK_IID( _GPAX, pIID, n) + +int __stdcall _GPAX_IID_Lookup( const IID * pIID, int * pIndex ) +{ + + if(!_GPAX_CHECK_IID(0)) + { + *pIndex = 0; + return 1; + } + + return 0; +} + +const ExtendedProxyFileInfo GPAX_ProxyFileInfo = +{ + (PCInterfaceProxyVtblList *) & _GPAX_ProxyVtblList, + (PCInterfaceStubVtblList *) & _GPAX_StubVtblList, + (const PCInterfaceName * ) & _GPAX_InterfaceNamesList, + (const IID ** ) & _GPAX_BaseIIDList, + & _GPAX_IID_Lookup, + 1, + 2, + 0, /* table of [async_uuid] interfaces */ + 0, /* Filler1 */ + 0, /* Filler2 */ + 0 /* Filler3 */ +}; diff --git a/applications/GPAX/GPAXps.def b/applications/GPAX/GPAXps.def new file mode 100644 index 0000000..c4ab3de --- /dev/null +++ b/applications/GPAX/GPAXps.def @@ -0,0 +1,11 @@ + +LIBRARY "GPAXPS" + +DESCRIPTION 'Proxy/Stub DLL' + +EXPORTS + DllGetClassObject @1 PRIVATE + DllCanUnloadNow @2 PRIVATE + GetProxyDllInfo @3 PRIVATE + DllRegisterServer @4 PRIVATE + DllUnregisterServer @5 PRIVATE diff --git a/applications/GPAX/GPAXps.mk b/applications/GPAX/GPAXps.mk new file mode 100644 index 0000000..70cf327 --- /dev/null +++ b/applications/GPAX/GPAXps.mk @@ -0,0 +1,16 @@ + +GPAXps.dll: dlldata.obj GPAX_p.obj GPAX_i.obj + link /dll /out:GPAXps.dll /def:GPAXps.def /entry:DllMain dlldata.obj GPAX_p.obj GPAX_i.obj \ + kernel32.lib rpcndr.lib rpcns4.lib rpcrt4.lib oleaut32.lib uuid.lib \ + +.c.obj: + cl /c /Ox /DWIN32 /D_WIN32_WINNT=0x0400 /DREGISTER_PROXY_DLL \ + $< + +clean: + @del GPAXps.dll + @del GPAXps.lib + @del GPAXps.exp + @del dlldata.obj + @del GPAX_p.obj + @del GPAX_i.obj diff --git a/applications/GPAX/StdAfx.cpp b/applications/GPAX/StdAfx.cpp new file mode 100644 index 0000000..a5eea17 --- /dev/null +++ b/applications/GPAX/StdAfx.cpp @@ -0,0 +1,12 @@ +// stdafx.cpp : source file that includes just the standard includes +// stdafx.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +#ifdef _ATL_STATIC_REGISTRY +#include +#include +#endif + +#include diff --git a/applications/GPAX/StdAfx.h b/applications/GPAX/StdAfx.h new file mode 100644 index 0000000..27caa01 --- /dev/null +++ b/applications/GPAX/StdAfx.h @@ -0,0 +1,28 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, +// but are changed infrequently + +#if !defined(AFX_STDAFX_H__2CD656F1_059C_4EC4_9EAA_8FECF66BB748__INCLUDED_) +#define AFX_STDAFX_H__2CD656F1_059C_4EC4_9EAA_8FECF66BB748__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define STRICT +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif +#define _ATL_APARTMENT_THREADED + +#include +//You may derive a class from CComModule and use it if you want to override +//something, but do not change the name of _Module +extern CComModule _Module; +#include +#include + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__2CD656F1_059C_4EC4_9EAA_8FECF66BB748__INCLUDED) diff --git a/applications/GPAX/dlldata.c b/applications/GPAX/dlldata.c new file mode 100644 index 0000000..86db18b --- /dev/null +++ b/applications/GPAX/dlldata.c @@ -0,0 +1,38 @@ +/********************************************************* + DllData file -- generated by MIDL compiler + + DO NOT ALTER THIS FILE + + This file is regenerated by MIDL on every IDL file compile. + + To completely reconstruct this file, delete it and rerun MIDL + on all the IDL files in this DLL, specifying this file for the + /dlldata command line option + +*********************************************************/ + +#define PROXY_DELEGATION + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +EXTERN_PROXY_FILE( GPAX ) + + +PROXYFILE_LIST_START +/* Start of list */ + REFERENCE_PROXY_FILE( GPAX ), +/* End of list */ +PROXYFILE_LIST_END + + +DLLDATA_ROUTINES( aProxyFileList, GET_DLL_CLSID ) + +#ifdef __cplusplus +} /*extern "C" */ +#endif + +/* end of generated dlldata file */ diff --git a/applications/GPAX/gpax.bmp b/applications/GPAX/gpax.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3ff99ff3a736c68624df2285444fa06e7fb7e705 GIT binary patch literal 246 zcmZuqF%H5o47^H6WrIw#Q&!%=&R@a;>XdAx6J&}y@s&KOSx7n=Cq+!yzB^w|kx%D0 z*1EDB8T&69Y-(UC|07OHXv`|iyds3aS^}yAqOvi#%Tw{cx(nB_$-NMhCK{eV +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=MPEG4Gen - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "MPEG4Gen.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "MPEG4Gen.mak" CFG="MPEG4Gen - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "MPEG4Gen - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "MPEG4Gen - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "MPEG4Gen - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Obj/W32Rel" +# PROP Intermediate_Dir "Obj/W32Rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "MPEG4Gen - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Obj/W32Deb" +# PROP Intermediate_Dir "Obj/W32Deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "MPEG4Gen - Win32 Release" +# Name "MPEG4Gen - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\..\src\utils\list.c +# End Source File +# Begin Source File + +SOURCE=.\main.c +# End Source File +# End Group +# End Target +# End Project diff --git a/applications/generators/MPEG4/MPEG4Gen.dsw b/applications/generators/MPEG4/MPEG4Gen.dsw new file mode 100644 index 0000000..15ce429 --- /dev/null +++ b/applications/generators/MPEG4/MPEG4Gen.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "MPEG4Gen"=.\MPEG4Gen.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/applications/generators/MPEG4/Makefile b/applications/generators/MPEG4/Makefile new file mode 100644 index 0000000..30405f9 --- /dev/null +++ b/applications/generators/MPEG4/Makefile @@ -0,0 +1,59 @@ +include ../../../config.mak + +vpath %.c $(SRC_PATH)/applications/generators/MPEG4 + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= main.o ../../../src/utils/list.o ../../../src/utils/error.o + +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=MPEG4Gen$(EXE) +else +OBJS+=../../../src/utils/os_divers.o +EXT= +PROG=MPEG4Gen +endif + +SRCS := $(OBJS:.o=.c) + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) $(PROG) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/applications/generators/MPEG4/main.c b/applications/generators/MPEG4/main.c new file mode 100644 index 0000000..2b08f17 --- /dev/null +++ b/applications/generators/MPEG4/main.c @@ -0,0 +1,1701 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG4 Scene Graph Generator sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +#include + +#include + + +#define COPYRIGHT_SCENE "/*\n * GPAC - Multimedia Framework C SDK\n *\n * Copyright (c) Jean Le Feuvre 2000-2005\n * All rights reserved\n *\n * This file is part of GPAC / Scene Graph sub-project\n *\n * GPAC is free software; you can redistribute it and/or modify\n * it under the terms of the GNU Lesser General Public License as published by\n * the Free Software Foundation; either version 2, or (at your option)\n * any later version.\n *\n * GPAC is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Lesser General Public License for more details. \n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; see the file COPYING. If not, write to\n * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n */\n" +#define COPYRIGHT_BIFS "/*\n * GPAC - Multimedia Framework C SDK\n *\n * Copyright (c) Jean Le Feuvre 2000-2005\n * All rights reserved\n *\n * This file is part of GPAC / BIFS codec sub-project\n *\n * GPAC is free software; you can redistribute it and/or modify\n * it under the terms of the GNU Lesser General Public License as published by\n * the Free Software Foundation; either version 2, or (at your option)\n * any later version.\n *\n * GPAC is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Lesser General Public License for more details. \n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; see the file COPYING. If not, write to\n * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n */\n" + +static char *CurrentLine; + +void PrintUsage() +{ + printf("MPEG4Gen [-p file] template_file_v1 (template_file_v2 ...)\n" + "\nGPAC MPEG4 Scene Graph generator. Usage:\n" + "-p: listing file of nodes to exclude from tables\n" + "Template files MUST be fed in order\n" + "\n" + "Generated Files are directly updated in the GPAC distribution - do NOT try to change this\n\n" + "Written by Jean Le Feuvre - (c) 2000-2005\n" + ); +} + +//a node field +typedef struct +{ + char type[50]; + //SFxxx, MFxxx + char familly[50]; + //name + char name[1000]; + //default value + char def[100]; + //bounds + u32 hasBounds; + char b_min[20]; + char b_max[20]; + //Quant + u32 hasQuant; + char quant_type[50]; + char qt13_bits[50]; + //Anim + u32 hasAnim; + u32 AnimType; + +} BField; + +//NDTs + +//a BIFS node +typedef struct +{ + char name[1000]; + //NDT info. NDT are created in alphabetical order + GF_List *NDT; + //0: normal, 1: special + u32 codingType; + u32 version; + + GF_List *Fields; + + //coding types + u8 hasDef, hasIn, hasOut, hasDyn; + u8 hasAQInfo; + + u8 hasDefault; + + u8 skip_impl; + + char Child_NDT_Name[1000]; +} BNode; + + +void skip_sep(char *sep) +{ + //skip separaors + while (*CurrentLine && strchr(sep, *CurrentLine)) { + CurrentLine = CurrentLine + 1; + //end of line - no token + if (*CurrentLine == '\n') return; + } +} + +//note that we increment the line no matter what +u32 GetNextToken(char *token, char *sep) +{ + u32 i , j = 0; + + strcpy(token, ""); + + //skip separaors + while (*CurrentLine && strchr(sep, *CurrentLine)) { + CurrentLine = CurrentLine + 1; + j ++; + //end of line - no token + if (*CurrentLine == '\n') return 0; + } + + //copy token untill next blank + i=0; + while (1) { + //bad line + if (! *CurrentLine) { + token[i] = 0; + return 0; + } + //end of token or end of line + if (strchr(sep, *CurrentLine) || (*CurrentLine == '\n') ) { + token[i] = 0; + CurrentLine = CurrentLine + 1; + return i; + } else { + token[i] = *CurrentLine; + } + CurrentLine = CurrentLine + 1; + i++; + j++; + } + return 1; +} + + +char szFixedVal[5000]; +char *GetFixedPrintout(char *val) +{ + if (!strcmp(val, "FIX_MIN") || !strcmp(val, "FIX_MAX")) return val; + /*composite texture...*/ + if (!strcmp(val, "65535")) return "FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/"; + sprintf(szFixedVal, "FLT2FIX(%s)", val); + return szFixedVal; +} + +BField *BlankField() +{ + BField *n = malloc(sizeof(BField)); + memset(n, 0, sizeof(BField)); + return n; +} + + +BNode *BlankNode() +{ + BNode *n = malloc(sizeof(BNode)); + memset(n, 0, sizeof(BNode)); + n->NDT = gf_list_new(); + n->Fields = gf_list_new(); + return n; +} + +u8 IsNDT(GF_List *NDTs, char *famName) +{ + u32 i; + char *ndtName; + for (i=0; i\n\n"); + + //write all tags + fprintf(f, "\n\nenum {\n"); + + for (i=0; iname); + else + fprintf(f, "\tTAG_MPEG4_%s = GF_NODE_RANGE_FIRST_MPEG4", n->name); + } + fprintf(f, ",\n\tTAG_LastImplementedMPEG4\n};\n\n"); + + for (i=0; iskip_impl) continue; + + fprintf(f, "typedef struct _tag%s\n{\n", n->name); + fprintf(f, "\tBASE_NODE\n"); + + /*write children field*/ + for (j=0; jFields); j++) { + bf = gf_list_get(n->Fields, j); + if (!stricmp(bf->name, "addChildren") || !strcmp(bf->name, "removeChildren")) continue; + if (!strcmp(bf->name, "children") && stricmp(n->name, "audioBuffer")) { + fprintf(f, "\tVRML_CHILDREN\n"); + break; + } + } + for (j=0; jFields); j++) { + bf = gf_list_get(n->Fields, j); + + if (!strcmp(bf->name, "addChildren") || !strcmp(bf->name, "removeChildren")) continue; + if (!strcmp(bf->name, "children") && stricmp(n->name, "audioBuffer")) continue; + + //write remaining fields + //eventIn fields are handled as pointer to functions, called by the route manager + if (!strcmp(bf->type, "eventIn")) { + fprintf(f, "\t%s %s;\t/*eventIn*/\n", bf->familly, bf->name); + fprintf(f, "\tvoid (*on_%s)(GF_Node *pThis);\t/*eventInHandler*/\n", bf->name); + } else if (!strcmp(bf->type, "eventOut")) { + //eventOut fields are handled as an opaque stack pointing to the route manager + //this will be refined once the route is in place + //we will likely need a function such as: + // void SignalRoute(route_stack, node, par) + fprintf(f, "\t%s %s;\t/*eventOut*/\n", bf->familly, bf->name); + } else if (strstr(bf->familly, "Node")) { + //this is a POINTER to a node + if (strstr(bf->familly, "SF")) { + fprintf(f, "\tGF_Node *%s;\t/*%s*/\n", bf->name, bf->type); + } else { + //this is a POINTER to a chain + fprintf(f, "\tGF_ChildNodeItem *%s;\t/*%s*/\n", bf->name, bf->type); + } + } else { + fprintf(f, "\t%s %s;\t/*%s*/\n", bf->familly, bf->name, bf->type); + } + } + fprintf(f, "} M_%s;\n\n\n", n->name); + } + + + hasViewport = 0; + //all NDTs are defined in v1 + fprintf(f, "/*NodeDataType tags*/\nenum {\n"); + for (i=0; i not generated + if (!hasViewport) fprintf(f, ",\n\tNDT_SFViewportNode"); + fprintf(f, "\n};\n\n"); + + + fprintf(f, "/*All BIFS versions handled*/\n"); + fprintf(f, "#define GF_BIFS_NUM_VERSION\t\t%d\n\n", NumVersions); + fprintf(f, "enum {\n"); + for (i=0; iNDT); i++) { + ndt = gf_list_get(node->NDT, i); + if (!strcmp(ndt, NDTName)) return 1; + } + return 0; +} + +u32 GetBitsCount(u32 MaxVal) +{ + u32 k=0; + + while ((s32) MaxVal > ((1<version != Version) continue; + if (!IsNodeInTable(n, NDTName)) continue; + nodeCount++; + } + return nodeCount; + +} + +void WriteNDT_H(FILE *f, GF_List *BNodes, GF_List *NDTs, u32 Version) +{ + u32 i, j, first, count; + char *NDTName; + BNode *n; + + + fprintf(f, "\n\n/* NDT BIFS Version %d */\n\n", Version); + + //for all NDTs + for (i=0; i 2) { + count -= 1; + } + if (!count) continue; + + //numBits + fprintf(f, "#define %s_V%d_NUMBITS\t\t%d\n", NDTName, Version, GetBitsCount(count + ( (Version == 2) ? 1 : 0) ) ); + fprintf(f, "#define %s_V%d_Count\t%d\n\n", NDTName, Version, count); + + fprintf(f, "static const u32 %s_V%d_TypeToTag[%d] = {\n", NDTName, Version, count); + first = 1; + //browse each node. + for (j=0; jversion != Version) continue; + if (!IsNodeInTable(n, NDTName)) continue; + + if (first) { + fprintf(f, " TAG_MPEG4_%s", n->name); + first = 0; + } else { + fprintf(f, ", TAG_MPEG4_%s", n->name); + } + } + fprintf(f, "\n};\n\n"); + + } + + fprintf(f, "\nu32 NDT_V%d_GetNumBits(u32 NDT_Tag);\n", Version); + fprintf(f, "u32 NDT_V%d_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType);\n", Version); + fprintf(f, "u32 NDT_V%d_GetNodeType(u32 NDT_Tag, u32 NodeTag);\n", Version); + + fprintf(f, "\n\n"); +} + +//write the NDTs functions for v1 nodes +//all our internal handling is in TAG_MPEG4_#nodename because we need an homogeneous +//namespace for all nodes (v1, v2, v3 and v4) +//the NDT functions will perform the translation from the NDT value to the absolute +//TAG of the node +void WriteNDT_Dec(FILE *f, GF_List *BNodes, GF_List *NDTs, u32 Version) +{ + char *NDTName; + u32 i, count; + + //NodeTag complete translation + fprintf(f, "\n\n\nu32 NDT_V%d_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType)\n{\n\tif (!NodeType) return 0;\n", Version); + + //handle version + fprintf(f, "\t/* adjust according to the table version */\n"); + if (Version == 2) { + fprintf(f, "\t/* v2: 0 reserved for extensions, 1 reserved for protos */\n"); + fprintf(f, "\tif (NodeType == 1) return 0;\n"); + fprintf(f, "\tNodeType -= 2;\n"); + } else { + fprintf(f, "\t/* v%d: 0 reserved for extensions */\n", Version); + fprintf(f, "\tNodeType -= 1;\n"); + } + + fprintf(f, "\tswitch (Context_NDT_Tag) {\n"); + + for (i=0; i 2) { + count -= 1; + } + if (!count) continue; + + fprintf(f, "\tcase NDT_%s:\n\t\tif (NodeType >= %s_V%d_Count) return 0;\n", NDTName, NDTName, Version); + fprintf(f, "\t\treturn %s_V%d_TypeToTag[NodeType];\n", NDTName, Version); + } + fprintf(f, "\tdefault:\n\t\treturn 0;\n\t}\n}"); + + //NDT codec bits + fprintf(f, "\n\n\nu32 NDT_V%d_GetNumBits(u32 NDT_Tag)\n{\n\tswitch (NDT_Tag) {\n", Version); + + for (i=0; i 2) { + count -= 1; + } + if (!count) continue; + + fprintf(f, "\tcase NDT_%s:\n\t\treturn %s_V%d_NUMBITS;\n", NDTName, NDTName, Version); + } + /*all tables have 1 node in v2 for proto coding*/ + fprintf(f, "\tdefault:\n\t\treturn %d;\n\t}\n}\n\n", (Version==2) ? 1 : 0); +} + + +void WriteNDT_Enc(FILE *f, GF_List *BNodes, GF_List *NDTs, u32 Version) +{ + u32 i, count; + char *NDTName; + + fprintf(f, "u32 NDT_V%d_GetNodeType(u32 NDT_Tag, u32 NodeTag)\n{\n\tif(!NDT_Tag || !NodeTag) return 0;\n\tswitch(NDT_Tag) {\n", Version); + for (i=0; i 2) { + count -= 1; + } + if (!count) continue; + fprintf(f, "\tcase NDT_%s:\n\t\treturn ALL_GetNodeType(%s_V%d_TypeToTag, %s_V%d_Count, NodeTag, GF_BIFS_V%d);\n", NDTName, NDTName, Version, NDTName, Version, Version); + } + fprintf(f, "\tdefault:\n\t\treturn 0;\n\t}\n}\n\n"); +} + + + +void WriteNodeFields(FILE *f, BNode *n) +{ + u32 i, first; + BField *bf; + u32 NbDef, NbIn, NbOut, NbDyn, hasAQ; + + NbDef = NbIn = NbOut = NbDyn = hasAQ = 0; + for (i=0;iFields); i++) { + bf = gf_list_get(n->Fields, i); + if (!strcmp(bf->type, "field") || !strcmp(bf->type, "exposedField")) { + NbDef += 1; + } + if (!strcmp(bf->type, "eventIn") || !strcmp(bf->type, "exposedField")) { + NbIn += 1; + //check for anim + if (bf->hasAnim) NbDyn += 1; + } + if (!strcmp(bf->type, "eventOut") || !strcmp(bf->type, "exposedField")) { + NbOut += 1; + } + if (bf->hasAnim || bf->hasQuant) hasAQ = 1; + } + + n->hasAQInfo = hasAQ; + + //write the def2all table + if (NbDef) { + first = 1; + fprintf(f, "static const u16 %s_Def2All[] = { ", n->name); + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + if (strcmp(bf->type, "field") && strcmp(bf->type, "exposedField")) continue; + if (first) { + fprintf(f, "%d", i); + first = 0; + } else { + fprintf(f, ", %d", i); + } + } + fprintf(f, "};\n"); + } + //write the in2all table + if (NbIn) { + first = 1; + fprintf(f, "static const u16 %s_In2All[] = { ", n->name); + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + if (strcmp(bf->type, "eventIn") && strcmp(bf->type, "exposedField")) continue; + if (first) { + fprintf(f, "%d", i); + first = 0; + } else { + fprintf(f, ", %d", i); + } + } + fprintf(f, "};\n"); + } + //write the out2all table + if (NbOut) { + first = 1; + fprintf(f, "static const u16 %s_Out2All[] = { ", n->name); + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + if (strcmp(bf->type, "eventOut") && strcmp(bf->type, "exposedField")) continue; + if (first) { + fprintf(f, "%d", i); + first = 0; + } else { + fprintf(f, ", %d", i); + } + } + fprintf(f, "};\n"); + } + //then write the dyn2all table + if (NbDyn) { + first = 1; + fprintf(f, "static const u16 %s_Dyn2All[] = { ", n->name); + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + if (strcmp(bf->type, "eventIn") && strcmp(bf->type, "exposedField")) continue; + if (!bf->hasAnim) continue; + if (first) { + fprintf(f, "%d", i); + first = 0; + } else { + fprintf(f, ", %d", i); + } + } + fprintf(f, "};\n"); + } + + n->hasDef = NbDef; + n->hasDyn = NbDyn; + n->hasIn = NbIn; + n->hasOut = NbOut; + + + fprintf(f, "\nstatic u32 %s_get_field_count(GF_Node *node, u8 IndexMode)\n{\n", n->name); + fprintf(f, "\tswitch(IndexMode) {\n"); + + fprintf(f, "\tcase GF_SG_FIELD_CODING_IN: return %d;\n", NbIn); + fprintf(f, "\tcase GF_SG_FIELD_CODING_DEF: return %d;\n", NbDef); + fprintf(f, "\tcase GF_SG_FIELD_CODING_OUT: return %d;\n", NbOut); + fprintf(f, "\tcase GF_SG_FIELD_CODING_DYN: return %d;\n", NbDyn); + fprintf(f, "\tdefault:\n"); + fprintf(f, "\t\treturn %d;\n", gf_list_count(n->Fields)); + fprintf(f, "\t}"); + + fprintf(f, "\n}\n"); + + fprintf(f, "\nstatic GF_Err %s_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField)\n{\n", n->name); + fprintf(f, "\tswitch(IndexMode) {\n"); + + if (NbIn) { + fprintf(f, "\tcase GF_SG_FIELD_CODING_IN:\n"); + fprintf(f, "\t\t*allField = %s_In2All[inField];\n\t\treturn GF_OK;\n", n->name); + } + if (NbDef) { + fprintf(f, "\tcase GF_SG_FIELD_CODING_DEF:\n"); + fprintf(f, "\t\t*allField = %s_Def2All[inField];\n\t\treturn GF_OK;\n", n->name); + } + if (NbOut) { + fprintf(f, "\tcase GF_SG_FIELD_CODING_OUT:\n"); + fprintf(f, "\t\t*allField = %s_Out2All[inField];\n\t\treturn GF_OK;\n", n->name); + } + if (NbDyn) { + fprintf(f, "\tcase GF_SG_FIELD_CODING_DYN:\n"); + fprintf(f, "\t\t*allField = %s_Dyn2All[inField];\n\t\treturn GF_OK;\n", n->name); + } + + fprintf(f, "\tdefault:\n"); + fprintf(f, "\t\treturn GF_BAD_PARAM;\n"); + fprintf(f, "\t}"); + + fprintf(f, "\n}\n"); + + + fprintf(f, "static GF_Err %s_get_field(GF_Node *node, GF_FieldInfo *info)\n{\n\tswitch (info->fieldIndex) {\n", n->name); + for (i=0;iFields); i++) { + bf = gf_list_get(n->Fields, i); + + fprintf(f, "\tcase %d:\n", i); + + fprintf(f, "\t\tinfo->name = \"%s\";\n", bf->name); + + //skip all eventIn + if (!strcmp(bf->type, "eventIn")) { + fprintf(f, "\t\tinfo->eventType = GF_SG_EVENT_IN;\n"); + fprintf(f, "\t\tinfo->on_event_in = ((M_%s *)node)->on_%s;\n", n->name, bf->name); + } + else if (!strcmp(bf->type, "eventOut")) { + fprintf(f, "\t\tinfo->eventType = GF_SG_EVENT_OUT;\n"); + } + else if (!strcmp(bf->type, "field")) { + fprintf(f, "\t\tinfo->eventType = GF_SG_EVENT_FIELD;\n"); + } + else { + fprintf(f, "\t\tinfo->eventType = GF_SG_EVENT_EXPOSED_FIELD;\n"); + } + + if (strstr(bf->familly, "Node")) { + if (strstr(bf->familly, "MF")) { + fprintf(f, "\t\tinfo->fieldType = GF_SG_VRML_MFNODE;\n"); + } else { + fprintf(f, "\t\tinfo->fieldType = GF_SG_VRML_SFNODE;\n"); + } + //always remove the SF or MF, as all NDTs are SFXXX + fprintf(f, "\t\tinfo->NDTtype = NDT_SF%s;\n", bf->familly+2); + fprintf(f, "\t\tinfo->far_ptr = & ((M_%s *)node)->%s;\n", n->name, bf->name); + } else { + char szName[20]; + strcpy(szName, bf->familly); + strupr(szName); + //no ext type + fprintf(f, "\t\tinfo->fieldType = GF_SG_VRML_%s;\n", szName); + fprintf(f, "\t\tinfo->far_ptr = & ((M_%s *) node)->%s;\n", n->name, bf->name); + } + fprintf(f, "\t\treturn GF_OK;\n"); + } + fprintf(f, "\tdefault:\n\t\treturn GF_BAD_PARAM;\n\t}\n}\n\n"); + + fprintf(f, "\nstatic s32 %s_get_field_index_by_name(char *name)\n{\n", n->name); + for (i=0;iFields); i++) { + bf = gf_list_get(n->Fields, i); + fprintf(f, "\tif (!strcmp(\"%s\", name)) return %d;\n", bf->name, i); + } + fprintf(f, "\treturn -1;\n\t}\n"); +} + + +//write the Quantization info for each node field(Quant and BIFS-Anim info) +void WriteNodeQuant(FILE *f, BNode *n) +{ + u32 i; + BField *bf; + fprintf(f, "static Bool %s_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits)\n{\n\tswitch (FieldIndex) {\n", n->name); + + for (i=0; iFields) ; i++ ) { + bf = gf_list_get(n->Fields, i); + if (!bf->hasAnim && !bf->hasQuant) continue; + + fprintf(f, "\tcase %d:\n", i); + //Anim Type + fprintf(f, "\t\t*AType = %d;\n", bf->AnimType); + //Quant Type + fprintf(f, "\t\t*QType = %s;\n", bf->quant_type); + if (!strcmp(bf->quant_type, "13")) + fprintf(f, "\t\t*QT13_bits = %s;\n", bf->qt13_bits); + + //Bounds + if (bf->hasBounds) { + if (!strcmp(bf->b_min, "+I") || !strcmp(bf->b_min, " +I") || !strcmp(bf->b_min, "I")) { + fprintf(f, "\t\t*b_min = FIX_MAX;\n"); + } else if (!strcmp(bf->b_min, "-I")) { + fprintf(f, "\t\t*b_min = FIX_MIN;\n"); + } else { + fprintf(f, "\t\t*b_min = %s;\n", GetFixedPrintout(bf->b_min)); + } + if (!strcmp(bf->b_max, "+I") || !strcmp(bf->b_max, " +I") || !strcmp(bf->b_max, "I")) { + fprintf(f, "\t\t*b_max = FIX_MAX;\n"); + } else { + fprintf(f, "\t\t*b_max = %s;\n", GetFixedPrintout(bf->b_max)); + } + } + fprintf(f, "\t\treturn 1;\n"); + } + fprintf(f, "\tdefault:\n\t\treturn 0;\n\t}\n}\n\n"); +} + +void WriteNodeCode(GF_List *BNodes) +{ + FILE *f; + char token[20], tok[20]; + char *store; + u32 i, j, k, go; + BField *bf; + BNode *n; + + f = BeginFile("mpeg4_nodes", 1); + + fprintf(f, "#include \n\n"); + fprintf(f, "\n#include \n"); + + for (k=0; kskip_impl) continue; + + fprintf(f, "\n/*\n\t%s Node deletion\n*/\n\n", n->name); + fprintf(f, "static void %s_Del(GF_Node *node)\n{\n\tM_%s *p = (M_%s *) node;\n", n->name, n->name, n->name); + + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + //nothing on child events + if (!strcmp(bf->name, "addChildren")) continue; + if (!strcmp(bf->name, "removeChildren")) continue; + + //delete all children node + if (!strcmp(bf->name, "children") && stricmp(n->name, "audioBuffer")) { + is_parent = 1; + continue; + } + + //delete ALL fields that must be deleted: this includes eventIn and out since + //all fields are defined in the node + if (!strcmp(bf->familly, "MFInt") + || !strcmp(bf->familly, "MFFloat") + || !strcmp(bf->familly, "MFDouble") + || !strcmp(bf->familly, "MFBool") + || !strcmp(bf->familly, "MFInt32") + || !strcmp(bf->familly, "MFColor") + || !strcmp(bf->familly, "MFRotation") + || !strcmp(bf->familly, "MFString") + || !strcmp(bf->familly, "MFTime") + || !strcmp(bf->familly, "MFVec2f") + || !strcmp(bf->familly, "MFVec3f") + || !strcmp(bf->familly, "MFVec4f") + || !strcmp(bf->familly, "MFVec2d") + || !strcmp(bf->familly, "MFVec3d") + || !strcmp(bf->familly, "MFURL") + || !strcmp(bf->familly, "MFScript") + || !strcmp(bf->familly, "SFString") + || !strcmp(bf->familly, "SFURL") + || !strcmp(bf->familly, "SFImage") + ) { + char szName[500]; + strcpy(szName, bf->familly); + strlwr(szName); + fprintf(f, "\tgf_sg_%s_del(p->%s);\n", szName, bf->name); + } + else if (!strcmp(bf->familly, "SFCommandBuffer")) { + fprintf(f, "\tgf_sg_sfcommand_del(p->%s);\n", bf->name); + } + else if (strstr(bf->familly, "Node")) { + //this is a POINTER to a node + if (strstr(bf->familly, "SF")) { + fprintf(f, "\tgf_node_unregister((GF_Node *) p->%s, (GF_Node *) p);\t\n", bf->name); + } else { + //this is a POINTER to a chain + fprintf(f, "\tgf_node_unregister_children((GF_Node *) p, p->%s);\t\n", bf->name); + } + } + } + if (is_parent) fprintf(f, "\tgf_sg_vrml_parent_destroy((GF_Node *) p);\t\n"); + fprintf(f, "\tgf_node_free((GF_Node *) p);\n}\n\n"); + + //node fields + WriteNodeFields(f, n); + WriteNodeQuant(f, n); + + // + // Constructor + // + + fprintf(f, "\n\nGF_Node *%s_Create()\n{\n\tM_%s *p;\n\tGF_SAFEALLOC(p, M_%s);\n", n->name, n->name, n->name); + fprintf(f, "\tif(!p) return NULL;\n"); + fprintf(f, "\tgf_node_setup((GF_Node *)p, TAG_MPEG4_%s);\n", n->name); + + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + //setup all children node + if (!strcmp(bf->name, "children") && stricmp(n->name, "audioBuffer")) { + fprintf(f, "\tgf_sg_vrml_parent_setup((GF_Node *) p);\n"); + break; + } + else if ( strstr(bf->familly, "Node") && strncmp(bf->type, "event", 5) ) { +#if 0 + //this is a POINTER to a node + if (strstr(bf->familly, "MF")) { + //this is a POINTER to a chain + fprintf(f, "\tp->%s = gf_list_new();\t\n", bf->name); + } +#endif + } + /*special case for SFCommandBuffer: we also create a command list*/ + if (!stricmp(bf->familly, "SFCommandBuffer")) { + fprintf(f, "\tp->%s.commandList = gf_list_new();\t\n", bf->name); + } + } + + //check if we have a child node + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + if ( !strcmp(bf->name, "children") || + ( !strstr(bf->type, "event") && strstr(bf->familly, "MF") && strstr(bf->familly, "Node")) ) { + sprintf(n->Child_NDT_Name, "NDT_SF%s", bf->familly+2); + break; + } + } + + fprintf(f, "\n\t/*default field values*/\n"); + + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + + //nothing on eventIn or Out + if (!strcmp(bf->type, "eventIn")) continue; + if (!strcmp(bf->type, "eventOut")) continue; + + if (!strcmp(bf->def, "")) continue; + + //no default on nodes + if (strstr(bf->familly, "Node")) continue; + //extract default falue + + // + // SF Fields + // + + //SFBool + if (!strcmp(bf->familly, "SFBool")) { + if (!strcmp(bf->def, "1") || !strcmp(bf->def, "TRUE")) + fprintf(f, "\tp->%s = 1;\n", bf->name); + } + //SFFloat + else if (!strcmp(bf->familly, "SFFloat")) { + fprintf(f, "\tp->%s = %s;\n", bf->name, GetFixedPrintout(bf->def)); + } + //SFTime + else if (!strcmp(bf->familly, "SFTime")) { + fprintf(f, "\tp->%s = %s;\n", bf->name, bf->def); + } + //SFInt32 + else if (!strcmp(bf->familly, "SFInt32")) { + fprintf(f, "\tp->%s = %s;\n", bf->name, bf->def); + } + //SFColor + else if (!strcmp(bf->familly, "SFColor")) { + CurrentLine = bf->def; + GetNextToken(token, " "); + TranslateToken(token); + + fprintf(f, "\tp->%s.red = %s;\n", bf->name, GetFixedPrintout(token)); + GetNextToken(token, " "); + fprintf(f, "\tp->%s.green = %s;\n", bf->name, GetFixedPrintout(token)); + GetNextToken(token, " "); + fprintf(f, "\tp->%s.blue = %s;\n", bf->name, GetFixedPrintout(token)); + } + //SFVec2f + else if (!strcmp(bf->familly, "SFVec2f")) { + CurrentLine = bf->def; + GetNextToken(token, " "); + TranslateToken(token); + fprintf(f, "\tp->%s.x = %s;\n", bf->name, GetFixedPrintout(token)); + GetNextToken(token, " "); + TranslateToken(token); + fprintf(f, "\tp->%s.y = %s;\n", bf->name, GetFixedPrintout(token)); + } + //SFVec3f + else if (!strcmp(bf->familly, "SFVec3f")) { + CurrentLine = bf->def; + GetNextToken(token, " "); + TranslateToken(token); + fprintf(f, "\tp->%s.x = %s;\n", bf->name, GetFixedPrintout(token)); + + GetNextToken(token, " "); + TranslateToken(token); + fprintf(f, "\tp->%s.y = %s;\n", bf->name, GetFixedPrintout(token)); + + GetNextToken(token, " "); + TranslateToken(token); + fprintf(f, "\tp->%s.z = %s;\n", bf->name, GetFixedPrintout(token)); + } + //SFVec4f & SFRotation + else if (!strcmp(bf->familly, "SFVec4f") || !strcmp(bf->familly, "SFRotation")) { + CurrentLine = bf->def; + GetNextToken(token, " "); + TranslateToken(token); + fprintf(f, "\tp->%s.x = %s;\n", bf->name, GetFixedPrintout(token)); + + GetNextToken(token, " "); + TranslateToken(token); + fprintf(f, "\tp->%s.y = %s;\n", bf->name, GetFixedPrintout(token)); + + GetNextToken(token, " "); + TranslateToken(token); + fprintf(f, "\tp->%s.z = %s;\n", bf->name, GetFixedPrintout(token)); + + GetNextToken(token, " "); + TranslateToken(token); + fprintf(f, "\tp->%s.q = %s;\n", bf->name, GetFixedPrintout(token)); + } + //SFString + else if (!strcmp(bf->familly, "SFString")) { + fprintf(f, "\tp->%s.buffer = (char*)malloc(sizeof(char) * %d);\n", bf->name, strlen(bf->def)+1); + fprintf(f, "\tstrcpy(p->%s.buffer, \"%s\");\n", bf->name, bf->def); + } + + // + // MF Fields + // + //MFFloat + else if (!strcmp(bf->familly, "MFFloat")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, " ,")) j++; + j+=1; + fprintf(f, "\tp->%s.vals = (SFFloat*)malloc(sizeof(SFFloat)*%d);\n", bf->name, j); + fprintf(f, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, " ,")) go = 0; + TranslateToken(token); + fprintf(f, "\tp->%s.vals[%d] = %s;\n", bf->name, j, GetFixedPrintout(token)); + j+=1; + } + } + //MFVec2f + else if (!strcmp(bf->familly, "MFVec2f")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(f, "\tp->%s.vals = (SFVec2f*)malloc(sizeof(SFVec2f)*%d);\n", bf->name, j); + fprintf(f, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(f, "\tp->%s.vals[%d].x = %s;\n", bf->name, j, GetFixedPrintout(tok)); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(f, "\tp->%s.vals[%d].y = %s;\n", bf->name, j, GetFixedPrintout(tok)); + j+=1; + CurrentLine = store; + } + } + //MFVec3f + else if (!strcmp(bf->familly, "MFVec3f")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(f, "\tp->%s.vals = (SFVec3f *)malloc(sizeof(SFVec3f)*%d);\n", bf->name, j); + fprintf(f, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(f, "\tp->%s.vals[%d].x = %s;\n", bf->name, j, GetFixedPrintout(tok)); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(f, "\tp->%s.vals[%d].y = %s;\n", bf->name, j, GetFixedPrintout(tok)); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(f, "\tp->%s.vals[%d].z = %s;\n", bf->name, j, GetFixedPrintout(tok)); + j+=1; + CurrentLine = store; + } + } + //MFVec4f + else if (!strcmp(bf->familly, "MFVec4f") || !strcmp(bf->familly, "MFRotation")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(f, "\tp->%s.vals = (GF_Vec4*)malloc(sizeof(GF_Vec4)*%d);\n", bf->name, j); + fprintf(f, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(f, "\tp->%s.vals[%d].x = %s;\n", bf->name, j, GetFixedPrintout(tok)); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(f, "\tp->%s.vals[%d].y = %s;\n", bf->name, j, GetFixedPrintout(tok)); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(f, "\tp->%s.vals[%d].z = %s;\n", bf->name, j, GetFixedPrintout(tok)); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(f, "\tp->%s.vals[%d].q = %s;\n", bf->name, j, GetFixedPrintout(tok)); + j+=1; + CurrentLine = store; + } + } + //MFInt32 + else if (!strcmp(bf->familly, "MFInt32")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(f, "\tp->%s.vals = (SFInt32*)malloc(sizeof(SFInt32)*%d);\n", bf->name, j); + fprintf(f, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + fprintf(f, "\tp->%s.vals[%d] = %s;\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + //MFColor + else if (!strcmp(bf->familly, "MFColor")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(f, "\tp->%s.vals = (SFColor*)malloc(sizeof(SFColor)*%d);\n", bf->name, j); + fprintf(f, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + fprintf(f, "\tp->%s.vals[%d].red = %s;\n", bf->name, j, GetFixedPrintout(tok)); + GetNextToken(tok, " "); + fprintf(f, "\tp->%s.vals[%d].green = %s;\n", bf->name, j, GetFixedPrintout(tok)); + GetNextToken(tok, " "); + fprintf(f, "\tp->%s.vals[%d].blue = %s;\n", bf->name, j, GetFixedPrintout(tok)); + j+=1; + CurrentLine = store; + } + } + //MFString + else if (!strcmp(bf->familly, "MFString")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(f, "\tp->%s.vals = (char**)malloc(sizeof(SFString)*%d);\n", bf->name, j); + fprintf(f, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " \""); + fprintf(f, "\tp->%s.vals[%d] = (char*)malloc(sizeof(char) * %d);\n", bf->name, j, strlen(tok)+1); + fprintf(f, "\tstrcpy(p->%s.vals[%d], \"%s\");\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + //MFTime + else if (!strcmp(bf->familly, "MFTime")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(f, "\tp->%s.vals = (SFTime*)malloc(sizeof(SFTime)*%d);\n", bf->name, j); + fprintf(f, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " \""); + TranslateToken(tok); + fprintf(f, "\tp->%s.vals[%d] = %s;\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + + //other nodes + else if (!strcmp(bf->familly, "SFImage")) { + //we currently only have SFImage, with NO texture so do nothing + } + //unknown init (for debug) + else { + fprintf(f, "UNKNOWN FIELD (%s);\n", bf->familly); + + } + } + fprintf(f, "\treturn (GF_Node *)p;\n}\n\n"); + + } + + fprintf(f, "\n\n\n"); + + //creator function + fprintf(f, "GF_Node *gf_sg_mpeg4_node_new(u32 NodeTag)\n{\n\tswitch (NodeTag) {\n"); + for (i=0; iskip_impl) { + fprintf(f, "\tcase TAG_MPEG4_%s:\n\t\treturn %s_Create();\n", n->name, n->name); + } + } + fprintf(f, "\tdefault:\n\t\treturn NULL;\n\t}\n}\n\n"); + + fprintf(f, "const char *gf_sg_mpeg4_node_get_class_name(u32 NodeTag)\n{\n\tswitch (NodeTag) {\n"); + for (i=0; iskip_impl) fprintf(f, "\tcase TAG_MPEG4_%s:\n\t\treturn \"%s\";\n", n->name, n->name); + } + fprintf(f, "\tdefault:\n\t\treturn \"Unknown Node\";\n\t}\n}\n\n"); + + fprintf(f, "void gf_sg_mpeg4_node_del(GF_Node *node)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl) { + fprintf(f, "\tcase TAG_MPEG4_%s:\n\t\t%s_Del(node); return;\n", n->name, n->name); + } + } + fprintf(f, "\tdefault:\n\t\treturn;\n\t}\n}\n\n"); + + fprintf(f, "u32 gf_sg_mpeg4_node_get_field_count(GF_Node *node, u8 code_mode)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl) { + fprintf(f, "\tcase TAG_MPEG4_%s:return %s_get_field_count(node, code_mode);\n", n->name, n->name); + } + } + fprintf(f, "\tdefault:\n\t\treturn 0;\n\t}\n}\n\n"); + + fprintf(f, "GF_Err gf_sg_mpeg4_node_get_field(GF_Node *node, GF_FieldInfo *field)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl) { + fprintf(f, "\tcase TAG_MPEG4_%s: return %s_get_field(node, field);\n", n->name, n->name); + } + } + fprintf(f, "\tdefault:\n\t\treturn GF_BAD_PARAM;\n\t}\n}\n\n"); + + fprintf(f, "GF_Err gf_sg_mpeg4_node_get_field_index(GF_Node *node, u32 inField, u8 code_mode, u32 *fieldIndex)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl) { + fprintf(f, "\tcase TAG_MPEG4_%s: return %s_get_field_index(node, inField, code_mode, fieldIndex);\n", n->name, n->name); + } + } + fprintf(f, "\tdefault:\n\t\treturn GF_BAD_PARAM;\n\t}\n}\n\n"); + + fprintf(f, "Bool gf_sg_mpeg4_node_get_aq_info(GF_Node *node, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl) { + fprintf(f, "\tcase TAG_MPEG4_%s: return %s_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits);\n", n->name, n->name); + } + } + fprintf(f, "\tdefault:\n\t\treturn 0;\n\t}\n}\n\n"); + + fprintf(f, "u32 gf_sg_mpeg4_node_get_child_ndt(GF_Node *node)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl && strlen(n->Child_NDT_Name) ) { + fprintf(f, "\tcase TAG_MPEG4_%s: return %s;\n", n->name, n->Child_NDT_Name); + } + } + fprintf(f, "\tdefault:\n\t\treturn 0;\n\t}\n}\n\n"); + + + fprintf(f, "\nu32 gf_node_mpeg4_type_by_class_name(const char *node_name)\n{\n\tif(!node_name) return 0;\n"); + for (i=0; iskip_impl) continue; + fprintf(f, "\tif (!strcmp(node_name, \"%s\")) return TAG_MPEG4_%s;\n", n->name, n->name); + } + fprintf(f, "\treturn 0;\n}\n\n"); + + fprintf(f, "s32 gf_sg_mpeg4_node_get_field_index_by_name(GF_Node *node, char *name)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl) { + fprintf(f, "\tcase TAG_MPEG4_%s: return %s_get_field_index_by_name(name);\n", n->name, n->name); + } + } + fprintf(f, "\tdefault:\n\t\treturn -1;\n\t}\n}\n\n"); + + + EndFile(f, "", 1); +} + +void ParseTemplateFile(FILE *nodes, GF_List *BNodes, GF_List *NDTs, u32 version) +{ + char sLine[2000]; + char token[100]; + char *p; + BNode *n; + BField *f; + u32 j, i, k; + + //get lines one by one + n = NULL; + while (!feof(nodes)) { + fgets(sLine, 2000, nodes); + //skip comment and empty lines + if (sLine[0] == '#') continue; + if (sLine[0] == '\n') continue; + + CurrentLine = sLine; + + //parse the line till end of line + while (GetNextToken(token, " \t")) { + + //this is a new node + if (!strcmp(token, "PROTO") ) { + n = BlankNode(); + n->version = version; + gf_list_add(BNodes, n); + + //get its name + GetNextToken(n->name, " \t["); + if (!strcmp(n->name, "TimeSensor")) { + n = n; + } + + //extract the NDTs + GetNextToken(token, "\t[ %#="); + if (strcmp(token, "NDT")) { + printf("Corrupted template file\n"); + return; + } + while (1) { + GetNextToken(token, "=, \t"); + //done with NDTs + if (token[0] == '%') break; + + //update the NDT list + CheckInTable(token, NDTs); + p = malloc(strlen(token)+1); + strcpy(p, token); + gf_list_add(n->NDT, p); + } + + //extract the coding type + if (strcmp(token, "%COD")) { + printf("Corrupted template file\n"); + return; + } else { + GetNextToken(token, "= "); + if (token[0] == 'N') { + n->codingType = 0; + } else { + n->codingType = 1; + } + } + } + //this is NOT a field + else if (token[0] == ']' || token[0] == '{' || token[0] == '}' ) { + break; + } + //parse a field + else { + if (!n) { + printf("Corrupted template file\n"); + return; + } + f = BlankField(); + gf_list_add(n->Fields, f); + + //get the field type + strcpy(f->type, token); + GetNextToken(f->familly, " \t"); + GetNextToken(f->name, " \t"); + //fix for our own code :( + if (!strcmp(f->name, "tag")) strcpy(f->name, "_tag"); + + //has default + skip_sep(" \t"); + if (GetNextToken(token, "#\t")) { + j=0; + while (token[j] == ' ') j+=1; + if (token[j] == '[') j+=1; + if (token[j] == '"') j+=1; + + if (token[j] != '"' && token[j] != ']') { + strcpy(f->def, token+j); + j=1; + while (j) { + switch (f->def[strlen(f->def)-1]) { + case ' ': + case '"': + case ']': + f->def[strlen(f->def)-1] = 0; + break; + default: + j=0; + break; + } + } + } else { + strcpy(f->def, ""); + } + if (!strcmp(f->familly, "SFFloat")) { + if (!strcmp(f->def, "+I") || !strcmp(f->def, "I")) { + strcpy(f->def, "FIX_MAX"); + } else if (!strcmp(f->def, "-I")) { + strcpy(f->def, "FIX_MIN"); + } + } else if (!strcmp(f->familly, "SFTime")) { + if (!strcmp(f->def, "+I") || !strcmp(f->def, "I")) { + strcpy(f->def, "FIX_MAX"); + } else if (!strcmp(f->def, "-I")) { + strcpy(f->def, "FIX_MIN"); + } + } else if (!strcmp(f->familly, "SFInt32")) { + if (!strcmp(f->def, "+I") || !strcmp(f->def, "I")) { + strcpy(f->def, "2 << 31"); + } else if (!strcmp(f->def, "-I")) { + strcpy(f->def, "- (2 << 31)"); + } + } + } + //has other + while (GetNextToken(token, " \t#%=")) { + switch (token[0]) { + //bounds + case 'b': + f->hasBounds = 1; + GetNextToken(f->b_min, "[(,"); + GetNextToken(f->b_max, ")]"); + break; + case 'q': + f->hasQuant = 1; + GetNextToken(f->quant_type, " \t"); + if (!strcmp(f->quant_type, "13")) + GetNextToken(f->qt13_bits, " \t"); + break; + case 'a': + f->hasAnim = 1; + GetNextToken(token, " \t"); + f->AnimType = atoi(token); + break; + default: + break; + } + } + } + } + } + + + for (k=0; kFields); i++) { + f = gf_list_get(n->Fields, i); + //nothing on events + if (!strcmp(f->type, "eventIn")) continue; + if (!strcmp(f->type, "eventOut")) continue; + if (!strcmp(f->def, "")) continue; + if (strstr(f->familly, "Node")) continue; + n->hasDefault = 1; + } + } +} + + +void WriteNodeDump(FILE *f, BNode *n) +{ + BField *bf; + u32 i; + + fprintf(f, "static const char *%s_FieldName[] = {\n", n->name); + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + if (!i) { + fprintf(f, " \"%s\"", bf->name); + } else { + fprintf(f, ", \"%s\"", bf->name); + } + } + fprintf(f, "\n};\n\n"); +} + +void parse_profile(GF_List *nodes, FILE *prof) +{ + char sLine[2000]; + BNode *n; + Bool found; + u32 i; + + while (!feof(prof)) { + fgets(sLine, 2000, prof); + //skip comment and empty lines + if (sLine[0] == '#') continue; + if (sLine[0] == '\n') continue; + if (strstr(sLine, "Proximity")) + found = 0; + found = 1; + while (found) { + switch (sLine[strlen(sLine)-1]) { + case '\n': + case '\r': + case ' ': + sLine[strlen(sLine)-1] = 0; + break; + default: + found = 0; + break; + } + } + +// if (0 && !stricmp(sLine, "Appearance") || !stricmp(sLine, "Shape") || !stricmp(sLine, "Sound2D") ) { + if (0) { + printf("Warning: cannot disable node %s (required in all BIFS profiles)\n", sLine); + } else { + found = 0; + for (i=0; iname, sLine)) { + n->skip_impl = 1; + found = 1; + break; + } + } + if (!found) printf("cannot disable %s: node not found\n", sLine); + } + } +} + +int main (int argc, char **argv) +{ + FILE *nodes, *ndt_c, *ndt_h, *fskip; + GF_List *BNodes, *NDTs; + u32 i, j, nbVersion; + BNode *n; + BField *bf; + + if (argc < 2) { + PrintUsage(); + return 0; + } + + BNodes = gf_list_new(); + NDTs = gf_list_new(); + + + + fskip = NULL; + i=1; + if (argv[i][0]=='-') { + fskip = fopen(argv[i+1], "rt"); + if (!fskip) { + printf("file %s not found\n", argv[i+1]); + return 0; + } + i+=2; + } + nbVersion = 1; + for (; i<(u32)argc; i++) { + nodes = fopen(argv[i], "rt"); + //all nodes are in the same list but we keep version info + ParseTemplateFile(nodes, BNodes, NDTs, nbVersion); + + + //special case for viewport: it is present in V1 but empty + if (nbVersion==1) CheckInTable("SFViewportNode", NDTs); + nbVersion++; + fclose(nodes); + } + nbVersion--; + printf("BIFS tables parsed: %d versions\n", nbVersion); + + if (fskip) { + parse_profile(BNodes, fskip); + fclose(fskip); + } + + //write the nodes def + WriteNodesFile(BNodes, NDTs, nbVersion); + //write all nodes init stuff + WriteNodeCode(BNodes); + + //write all NDTs + ndt_h = BeginFile("NDT", 0); + ndt_c = BeginFile("NDT", 1); + + fprintf(ndt_h, "#include \n\n"); + fprintf(ndt_c, "\n\n#include \n"); + + //prepare the encoding file + fprintf(ndt_h, "\n\nu32 ALL_GetNodeType(const u32 *table, const u32 count, u32 NodeTag, u32 Version);\n\n"); + fprintf(ndt_c, "\n\nu32 ALL_GetNodeType(const u32 *table, const u32 count, u32 NodeTag, u32 Version)\n{\n\tu32 i = 0;"); + fprintf(ndt_c, "\n\twhile (iskip_impl) continue; + for (j=0; jFields); j++) { + bf = gf_list_get(n->Fields, j); + if (!strcmp(bf->name, "children")) { + fprintf(ndt_c, "\tcase TAG_MPEG4_%s:\n\t\treturn NDT_SF%s;\n", n->name, bf->familly+2); + break; + } + } + } + fprintf(ndt_c, "\tdefault:\n\t\treturn 0;\n\t}\n}\n\n"); + + EndFile(ndt_h, "NDT", 0); + EndFile(ndt_c, "", 1); + + //free NDTs + while (gf_list_count(NDTs)) { + char *tmp = gf_list_get(NDTs, 0); + free(tmp); + gf_list_rem(NDTs, 0); + } + gf_list_del(NDTs); + //free nodes + while (gf_list_count(BNodes)) { + n = gf_list_get(BNodes, 0); + gf_list_rem(BNodes, 0); + while (gf_list_count(n->NDT)) { + char *tmp = gf_list_get(n->NDT, 0); + free(tmp); + gf_list_rem(n->NDT, 0); + } + gf_list_del(n->NDT); + while (gf_list_count(n->Fields)) { + bf = gf_list_get(n->Fields, 0); + free(bf); + gf_list_rem(n->Fields, 0); + } + gf_list_del(n->Fields); + free(n); + } + gf_list_del(BNodes); + + return 0; +} + diff --git a/applications/generators/MPEG4/skip.txt b/applications/generators/MPEG4/skip.txt new file mode 100644 index 0000000..b064f06 --- /dev/null +++ b/applications/generators/MPEG4/skip.txt @@ -0,0 +1,181 @@ +#MPEG-4 v1.0 + +#Anchor +#AnimationStream +#AudioBuffer +#AudioClip +#AudioDelay +#AudioFX +#AudioMix +#AudioSource +#AudioSwitch +#Background2D +#Bitmap +#Circle +#Curve2D +#Background +#Billboard +#Box +#Color +#ColorInterpolator +#Collision +#CompositeTexture2D +#CompositeTexture3D +#Conditional +#Cone +#Coordinate2D +#Coordinate +#CoordinateInterpolator2D +#CoordinateInterpolator +#Cylinder +#CylinderSensor +#DirectionalLight +#DiscSensor +#ElevationGrid +Expression +#Extrusion +Face +FaceDefMesh +FaceDefTables +FaceDefTransform +FAP +FDP +FIT +#Fog +#FontStyle +#Form +#Group +#ImageTexture +#IndexedFaceSet +#IndexedFaceSet2D +#IndexedLineSet +#IndexedLineSet2D +#Inline +#LOD +#Layer2D +#Layer3D +#Layout +#LineProperties +#ListeningPoint +#Material2D +#Material +#MovieTexture +#NavigationInfo +#Normal +#NormalInterpolator +#OrderedGroup +#OrientationInterpolator +#PointLight +#PointSet +#PointSet2D +#PixelTexture +#PlaneSensor2D +#PlaneSensor +#PositionInterpolator2D +#PositionInterpolator +#ProximitySensor2D +#ProximitySensor +#QuantizationParameter +#Rectangle +#ScalarInterpolator +#Script +#Sound +#Sphere +#SphereSensor +#SpotLight +#Switch +#TermCap +#TextureCoordinate +#TextureTransform +#Text +#TimeSensor +#TouchSensor +#Transform2D +#Transform +#Valuator +#Viewpoint +#VisibilitySensor +Viseme +#WorldInfo + + +#MPEG-4 v2.0 +#AcousticMaterial +#AcousticScene +#ApplicationWindow +BAP +BDP +Body +BodyDefTable +BodySegmentConnectionHint +#DirectiveSound +#Hierarchical3DMesh +#MaterialKey +#PerceptualParameters + +#MPEG-4 v3.0 +#TemporalTransform +#TemporalGroup +#ServerCommand + +#MPEG-4 v4.0 +#InputSensor +#MatteTexture +#MediaBuffer +#MediaControl +#MediaSensor + +#MPEG-4 V5 +BitWrapper +#CoordinateInterpolator4D +DepthImage +FFD +Implicit +XXLFM_Appearance +XXLFM_BlendList +XXLFM_FrameList +XXLFM_LightMap +XXLFM_SurfaceMapList +XXLFM_ViewMapList +MeshGrid +#NonLinearDeformer +NurbsCurve +NurbsCurve2D +NurbsSurface +OctreeImage +XXParticles +XXParticleInitBox +XXPlanarObstacle +XXPointAttractor +PointTexture +#PositionAnimator +#PositionAnimator2D +#PositionInterpolator4D +ProceduralTexture +Quadric +SBBone +SBMuscle +SBSegment +SBSite +SBSkinnedModel +SBVCAnimation +#ScalarAnimator +SimpleTexture +SolidRep +SubdivisionSurface +SubdivSurfaceSector +WaveletSubdivisionSurface + +#MPEG-4 V6 +#Clipper2D +#ColorTransform +#Ellipse +#LinearGradient +#PathLayout +#RadialGradient +SynthesizedTexture +#TransformMatrix2D +#Viewport +#XCurve2D +#XFontStyle +#XLineProperties diff --git a/applications/generators/MPEG4/templates1.txt b/applications/generators/MPEG4/templates1.txt new file mode 100644 index 0000000..a64c34e --- /dev/null +++ b/applications/generators/MPEG4/templates1.txt @@ -0,0 +1,1292 @@ +#-- Version 1 --# +# templates for the BIFS nodes +# ============================= +# Notations I = Infinity +# %q=x Quantization method x +# 0 None +# 1 3D Position (SFVec3F) +# 2 2D Position (SFVec2F) +# 3 drawing Order +# 4 Color (SFColor) +# 5 Texture Coordinate +# 6 Angle (SFFloat 0-2PI) +# 7 Scale (SFVec2F or SFVec3F) +# 8 Interpolators keys +# 9 Normals +# 10 Rotations (SFRotation) +# 11 Object Size 3D (SFVec3F and SFFloat) +# 12 Object Size 2D +# 13 Linear Quantization (+ Nb Bits) +# 14 Index (of IndexedFaceSet,...) +# 15 Reserved +# +# %a=y Animation method for fields that can be animated +# +## OO 081498 To match BIFS's update numbering +# 0 None +# 1 Position 3D +# 2 Position 2D +# 4 Color +# 6 Angle +# 7 Float +# 8 BoundFloat (intensities, transparencies,...) +# 9 Normal +# 10 Rotation +# 11 Size 3D +# 12 Size 2D +# 13 Integer +# 14 Reserved +## 0 3D Position +## 1 2D positon +## 2 Color (SFColor) +## 3 Angle (SFFloat 0-2pi) +## 4 Normals +## 5 Scale (SFVec2F) +## 6 Rotation (SFRotation) +## 7 Object Size or Scalar (SFFloat) +# +# %b=[min,max] bounds of value +# For each scalar or vectorial value, bounds may be specified. +# This will be used to check if user-specified values are out of bounds. In +# this case, bounds specified in the templates will be used (if not infinity). +# +# %NDT=Node Data Type +# For each node, one or several Node Data Types are assigned, specifying which node sub +# types the node belongs to. Moreover, each field of type SF/MF3DNode is re assigned +# a unique correct NodeDataType according to specify the allowed values of the field +# +# %COD Type of encoding +# N Normal Syntax : The node syntax follos the generic syntax for nodes +# S Special Syntax : The node has a specific syntax +# +# +# NCT => VRML type equivalence +# +# SF/MFxxxNode => SF/MFNode +# SF/MFURL => SF/MFString +# SF/MFCommandBuffer => SF/MFString +# SF/MFScript => SF/MFString +# +# +# Modification History +# ------------------------------------------------ +# Jan 16, 2003 (MBS) AudioBuffer.length changed to exposedField +# Dec 10, 1997 (Yuval Fisher) SFTimerNode changed to SFTimeSensorNode +# Dec 10, 1997 (Yuval Fisher) SFFitNode changed to SFFITNode +# Dec 10, 1997 (Yuval Fisher) children in Form changed to exposedField +# Dec 11, 1997 (Yuval Fisher) exposedfield changed to exposedField in Form +# +# Dec 19, 1997 (Yuval Fisher - Following Alexandros' Parser errors) +# - FALSE removed from isActive field on AudioClip node +# - %b=(-I,+I) removed from set_fraction on ColorInterpolator +# - justify field value in FontStyle enclosed in [] +# - %b=(-I,+I) removed from set_fraction on ScalarInterpolator +# - removed -1 from duration_changed and FALSE from isActive in VideoObject2D +# - changed renderedFace to exposedField in Face node +# - changed renderedFace to exposedField in Body node -- just temporary anyway +# - animation type on height field of elevation grid changed from 11 to 7 +# - vector field of Normal node, transposed quantization=9 and animation=4 +# - changed eventIn to exposedField in background, fog, navigationInfo,viewpoint +# of Layer3D +# - Same as above for Composite3DTexture +# +# Dec 22, 1997 (Yuval Fisher) added Conditional back. +# March 16: +# Reordered alphabetically +# Sound field spatialize changed to exposedfield +# Sound added addChildren and deleteChildren to all nodes with children +# Changed children2D field to be children in CompositeMap +# Eliminate ucs_2 field from StreamingText +# Do the childrenLayer fields need add and remove eventIns ? +# sound field changed to source in Sound2D +# +# March 24 +# Many changes based on changes adopted in Tokyo +# added bounding boxes to Layer2D and Layer3D +# +# March 25 +# Added Animation to various nodes - Checked template with Julien. +# +# March 30 +# 0 -> FALSE and MFURL:null->[] corrections. +# +# April 13 +# Added TermCap per Joern +# +# May 12 +# add and remove ChildrenLayer in Layer3d was missing 'Layer' +# term cap had int instead of int32 +# +# May 19 +# DiscSensor center had a wrong quantization type +# May 26 +# drawOrderMin and Max were SFVec3f in Quantization Parameter +# June 8 +# Layer2D,3D Has an SFInt32 depth, converted to SFFloat +# Text should hav an MFString +# June 16 +# added to PlaneSensor: +#eventOut SFVec3f translation_changed +# +# +# to do: +# check if SFstring or SFBuffer ins AudioFX +# check children field for AudioSource +# fix FIT node bounds +# ask Eric if Face really has a URL -- try to eliminate it because it's an +# animation stream/can bounds be put on the faceDefMesh parameters why are +# these fields +# +# Oct 28 +# JS: Updated all template to correct DOC +# JCD updated Form, Layout and Valuator +# JS: To check FBA latest nodes +# November 2, 1998 (Mikael Bourges-Sevenier) AudioBuffer has MFAudioNode children not MFNode +# November 2, 1998 (Mikael Bourges-Sevenier) Face node is a SF3DNode also. +# November 2, 1998 (Mikael Bourges-Sevenier) modif I bound -> +I bound +# November 2, 1998 (Mikael Bourges-Sevenier) modif xFInt -> xFInt32 where missing +# November 2, 1998 (Mikael Bourges-Sevenier) removed childrenLayer in Layer2D +# Nov 2: JS: Integrated latest comments from Gabriel and Liam +# Nov 3: Still more bug corrections! The last ones? Added also type correspondance for +# semantic tables. +# JS: Changesd all fap values to be 0 by default +# Feb 9 99: Changed Layer2D/3D to be SF3DNode, suppresed cone anim stuff +# +# Mar 1 1999 +# group -> groups in Form plus other minor fixes +# Mar 8, 1999 +# Add speed to AudioSource +# June 15, 1999 YF +# Changed renderedFace to default [] from NULL & removed blank line +# +# Dec 20, 1999 YF +# FABs get default value of +I +# +# Aug 10, 2000 YF per Steve Woods: +# VisibilitySensor %b= missing from center and size fields +# +# July 10, 2001 YF per DCOR1 +# modify maxAngle, minAngle and diskAngle in CylinderSensor +# minAngle in DiscSensor changed to 0 +# +# Jan 2, 2002 YF per decision at meeting +# revert to capilized letter of factor, offset, sum in Valuator +# +# Aug 13, 2002 -- added activate field to Anchor per COR2 item +# modified field to exposedField in FontStyle per COR2 +# + +PROTO Anchor [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFString description "" +exposedField MFString parameter [] +exposedField MFURL url [] +eventIn SFBool activate +]{ +} + + +PROTO AnimationStream [ #%NDT=SFWorldNode,SF3DNode,SF2DNode,SFStreamingNode %COD=N +exposedField SFBool loop FALSE +exposedField SFFloat speed 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFTime startTime 0 #%b=(-I,+I) +exposedField SFTime stopTime 0 #%b=(-I,+I) +exposedField MFURL url [] +eventOut SFTime duration_changed +eventOut SFBool isActive +] { +} + + +PROTO Appearance [ #%NDT=SFWorldNode,SFAppearanceNode %COD=N +exposedField SFMaterialNode material NULL +exposedField SFTextureNode texture NULL +exposedField SFTextureTransformNode textureTransform NULL +] { +} + +PROTO AudioBuffer [ #%NDT=SFWorldNode,SFAudioNode %COD=N +exposedField SFBool loop FALSE +exposedField SFFloat pitch 1 #%b=[0,+I) #%q=0 #%a=7 +exposedField SFTime startTime 0 #%b=[0,+I) #%q=0 +exposedField SFTime stopTime 0 #%b=[0,+I) #%q=0 +exposedField MFAudioNode children [] +exposedField SFInt32 numChan 1 #%b=[0,255] #%q=13 8 +exposedField MFInt32 phaseGroup [1] +exposedField SFFloat length 0.0 #%b=[0,+I) #%q=0 +eventOut SFTime duration_changed +eventOut SFBool isActive +] { +} + +PROTO AudioClip [ #%NDT=SFWorldNode,SFAudioNode,SFStreamingNode %COD=N +exposedField SFString description "" +exposedField SFBool loop FALSE +exposedField SFFloat pitch 1.0 #%b=[0,+I) #%q=0 #%a=7 +exposedField SFTime startTime 0 #%b=(-I,+I) +exposedField SFTime stopTime 0 #%b=(-I,+I) +exposedField MFURL url [] +eventOut SFTime duration_changed +eventOut SFBool isActive +] { +} + +PROTO AudioDelay [ #%NDT=SFWorldNode,SFAudioNode %COD=N +eventIn MFAudioNode addChildren +eventIn MFAudioNode removeChildren +exposedField MFAudioNode children [] +exposedField SFTime delay 0 #%b=[0,+I) +field SFInt32 numChan 1 #%b=[0,255] #%q=13 8 +field MFInt32 phaseGroup [] #%b=[0,255] #%q=13 8 +] { +} + +PROTO AudioFX [ #%NDT=SFWorldNode,SFAudioNode %COD=N +eventIn MFAudioNode addChildren +eventIn MFAudioNode removeChildren +exposedField MFAudioNode children [] +exposedField SFString orch "" +exposedField SFString score "" +exposedField MFFloat params [] #%b=(-I,+I) #%q=0 #%a=7 +field SFInt32 numChan 1 #%b=[0,255] #%q=13 8 +field MFInt32 phaseGroup [] #%b=[0,255] #%q=13 8 +] { +} + +PROTO AudioMix [ #%NDT=SFWorldNode,SFAudioNode %COD=N +eventIn MFAudioNode addChildren +eventIn MFAudioNode removeChildren +exposedField MFAudioNode children [] +exposedField SFInt32 numInputs 1 #%b=[1,255] #%q=13 8 +exposedField MFFloat matrix [] #%b=[0,1] #%q=0 #%a=7 +field SFInt32 numChan 1 #%b=[0,255] #%q=13 8 +field MFInt32 phaseGroup [] #%b=[0,255] #%q=13 8 +] { +} + +PROTO AudioSource [ #%NDT=SFWorldNode,SFAudioNode,SFStreamingNode %COD=N +eventIn MFAudioNode addChildren +eventIn MFAudioNode removeChildren +exposedField MFAudioNode children [] +exposedField MFURL url [] +exposedField SFFloat pitch 1 #%b=[0,+I) #%q=0 #%a=7 +exposedField SFFloat speed 1 #%b=[0,+I) #%q=0 #%a=7 +exposedField SFTime startTime 0 +exposedField SFTime stopTime 0 +field SFInt32 numChan 1 #%b=[0,255] #%q=13 8 +field MFInt32 phaseGroup [] #%b=[0,255] #%q=13 8 +] { +} + +PROTO AudioSwitch [ #%NDT=SFWorldNode,SFAudioNode %COD=N +eventIn MFAudioNode addChildren +eventIn MFAudioNode removeChildren +exposedField MFAudioNode children [] +exposedField MFInt32 whichChoice [] #%b=[0,1] #%q=13 1 +field SFInt32 numChan 1 #%b=[0,255] #%q=13 8 +field MFInt32 phaseGroup [] #%b=[0,255] #%q=13 8 +] { +} + +PROTO Background [ #%NDT=SFWorldNode,SF3DNode,SFBackground3DNode %COD=N +eventIn SFBool set_bind +exposedField MFFloat groundAngle [] #%b=[0,1.5707963] #%q=6 #%a=8 +exposedField MFColor groundColor [] #%b=[0,1] #%q=4 #%a=4 +exposedField MFURL backUrl [] +exposedField MFURL bottomUrl [] +exposedField MFURL frontUrl [] +exposedField MFURL leftUrl [] +exposedField MFURL rightUrl [] +exposedField MFURL topUrl [] +exposedField MFFloat skyAngle [] #%b=[0,3.14159265] #%q=6 #%a=8 +exposedField MFColor skyColor [ 0 0 0 ] #%b=[0,1] #%q=4 #%a=4 +eventOut SFBool isBound +] { +} + +PROTO Background2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode,SFBackground2DNode %COD=N +eventIn SFBool set_bind +exposedField SFColor backColor 0 0 0 #%b=[0,1] #%q=4 #%a=4 +exposedField MFURL url [] +eventOut SFBool isBound +] { +} + +PROTO Billboard [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFVec3f axisOfRotation 0 1 0 #%q=9 #%a=9 +] { +} + +# modified JCD +PROTO Bitmap [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFVec2f scale -1 -1 #%b=[-1,+I) #%q=12 #%a=12 +] { +} + + +PROTO Box [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +field SFVec3f size 2 2 2 #%b=[0,+I) #%q=11 +] { +} + +PROTO Circle [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFFloat radius 1 #%b=[0,+I) #%q=12 #%a=7 +] { +} + +PROTO Collision [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFBool collide TRUE +field SF3DNode proxy NULL +eventOut SFTime collideTime +] { +} + +PROTO Color [ #%NDT=SFWorldNode,SFColorNode %COD=N +exposedField MFColor color [] #%b=[0,1] #%q=4 #%a=4 +] { +} + +PROTO ColorInterpolator [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +eventIn SFFloat set_fraction +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFColor keyValue [] #%b=[0,1] #%q=4 +eventOut SFColor value_changed +] { +} + +PROTO CompositeTexture2D [ #%NDT=SFWorldNode,SFTextureNode %COD=N +eventIn MF2DNode addChildren +eventIn MF2DNode removeChildren +exposedField MF2DNode children [] +exposedField SFInt32 pixelWidth -1 #%b=[0,65535] #%q=13 16 +exposedField SFInt32 pixelHeight -1 #%b=[0,65535] #%q=13 16 +exposedField SFBackground2DNode background NULL +exposedField SFViewportNode viewport NULL +field SFInt32 repeatSandT 3 #%b=[0,3] #%q=13 2 +] { +} + +PROTO CompositeTexture3D [ #%NDT=SFWorldNode,SFTextureNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFInt32 pixelWidth -1 #%b=[0,65535] #%q=13 16 +exposedField SFInt32 pixelHeight -1 #%b=[0,65535] #%q=13 16 +exposedField SFBackground3DNode background NULL +exposedField SFFogNode fog NULL +exposedField SFNavigationInfoNode navigationInfo NULL +exposedField SFViewpointNode viewpoint NULL +field SFBool repeatS TRUE +field SFBool repeatT TRUE +] { +} + + +PROTO Conditional [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +eventIn SFBool activate +eventIn SFBool reverseActivate FALSE +exposedField SFCommandBuffer buffer "" +eventOut SFBool isActive +]{ +} + +PROTO Cone [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +field SFFloat bottomRadius 1 #%b=[0,+I) #%q=11 +field SFFloat height 2 #%b=[0,+I) #%q=11 +field SFBool side TRUE +field SFBool bottom TRUE +] { +} + +PROTO Coordinate [ #%NDT=SFWorldNode,SFCoordinateNode %COD=N +exposedField MFVec3f point [] #%b=(-I,+I) #%q=1 #%a=1 +] { +} + +PROTO Coordinate2D [ #%NDT=SFWorldNode,SFCoordinate2DNode %COD=N +exposedField MFVec2f point [] #%b=(-I,+I) #%q=2 #%a=2 +] { +} + +PROTO CoordinateInterpolator [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn SFFloat set_fraction +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFVec3f keyValue [] #%b=(-I,+I) #%q=1 +eventOut MFVec3f value_changed +] { +} + +PROTO CoordinateInterpolator2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn SFFloat set_fraction +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFVec2f keyValue [] #%b=(-I,+I) #%q=2 +eventOut MFVec2f value_changed +] { +} + + +PROTO Curve2D [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFCoordinate2DNode point [] +exposedField SFFloat fineness 0.5 #%b=[0,1] #%q=0 #%a=7 +exposedField MFInt32 type [] #%b=[0,3] #%q=13 2 +] { +} + +PROTO Cylinder [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +field SFBool bottom TRUE +field SFFloat height 2 #%b=[0,+I) #%q=11 +field SFFloat radius 1 #%b=[0,+I) #%q=11 +field SFBool side TRUE +field SFBool top TRUE +] { +} + +PROTO CylinderSensor [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFBool autoOffset TRUE +exposedField SFFloat diskAngle 0.262 #%b=[0,1.5707963] #%q=6 +exposedField SFBool enabled TRUE +exposedField SFFloat maxAngle -1 #%b=[-6.2831853,6.2831853] #%q=6 +exposedField SFFloat minAngle 0 #%b=[-6.2831853,6.2831853] #%q=6 +exposedField SFFloat offset 0 #%b=[0,6.2831853] #%q=6 +eventOut SFBool isActive +eventOut SFRotation rotation_changed +eventOut SFVec3f trackPoint_changed +] { +} + +PROTO DirectionalLight [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFFloat ambientIntensity 0 #%b=[0,1] #%q=4 #%a=8 +exposedField SFColor color 1 1 1 #%b=[0,1] #%q=4 #%a=4 +exposedField SFVec3f direction 0 0 -1 #%q=9 #%a=9 +exposedField SFFloat intensity 1 #%b=[0,1] #%q=4 #%a=8 +exposedField SFBool on TRUE +] { +} + +PROTO DiscSensor [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +exposedField SFBool autoOffset TRUE +exposedField SFBool enabled TRUE +exposedField SFFloat maxAngle -1 #%b=[-6.2831853,6.2831853] #%q=6 +exposedField SFFloat minAngle 0 #%b=[-6.2831853,6.2831853] #%q=6 +exposedField SFFloat offset 0 #%b=[0,6.2831853] #%q=6 +eventOut SFBool isActive +eventOut SFFloat rotation_changed +eventOut SFVec2f trackPoint_changed +] { +} + +PROTO ElevationGrid [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn MFFloat set_height +exposedField SFColorNode color NULL +exposedField SFNormalNode normal NULL +exposedField SFTextureCoordinateNode texCoord NULL +field MFFloat height [] #%b=(-I,+I) #%q=11 #%a=7 +field SFBool ccw TRUE +field SFBool colorPerVertex TRUE +field SFFloat creaseAngle 0.0 #%b=[0,6.2831853] #%q=6 +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +field SFInt32 xDimension 0 #%b=[0,+I) #%q=11 +field SFFloat xSpacing 1.0 #%b=(0,+I) #%q=11 +field SFInt32 zDimension 0 #%b=[0,+I) #%q=11 +field SFFloat zSpacing 1.0 #%b=[0,+I) #%q=11 +] { +} + + +PROTO Expression [ #%NDT=SFWorldNode,SFExpressionNode %COD=N +exposedField SFInt32 expression_select1 0 #%b=(0,31) #%q=13 5 +exposedField SFInt32 expression_intensity1 0 #%b=(0,63) #%q=13 6 +exposedField SFInt32 expression_select2 0 #%b=(0,31) #%q=13 5 +exposedField SFInt32 expression_intensity2 0 #%b=(0,63) #%q=13 6 +exposedField SFBool init_face FALSE +exposedField SFBool expression_def FALSE +] { +} + +PROTO Extrusion [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn MFVec2f set_crossSection +eventIn MFRotation set_orientation +eventIn MFVec2f set_scale +eventIn MFVec3f set_spine +field SFBool beginCap TRUE +field SFBool ccw TRUE +field SFBool convex TRUE +field SFFloat creaseAngle 0.0 #%b=[0,6.2831853] #%q=6 +field MFVec2f crossSection [ 1 1, 1 -1, -1 -1, -1 1, 1 1 ] #%b=(-I,+I) #%q=2 +field SFBool endCap TRUE +field MFRotation orientation [0 0 1 0] #%b=(-I,+I) #%q=10 +field MFVec2f scale [1 1] #%b=[0,+I) #%q=7 +field SFBool solid TRUE +field MFVec3f spine [ 0 0 0, 0 1 0 ] #%b=(-I,+I) #%q=1 +] { +} + +PROTO Face [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +exposedField SFFAPNode fap NULL +exposedField SFFDPNode fdp NULL +exposedField SFFITNode fit NULL +exposedField SFAudioNode ttsSource NULL +exposedField MF3DNode renderedFace [] +] { +} + + +PROTO FaceDefMesh [ #%NDT=SFWorldNode,SFFaceDefMeshNode %COD=N +field SF3DNode faceSceneGraphNode NULL +field MFInt32 intervalBorders [] #%q=0 +field MFInt32 coordIndex [] #%q=0 +field MFVec3f displacements [] #%q=0 +] { +} + +PROTO FaceDefTables [ #%NDT=SFWorldNode,SFFaceDefTablesNode %COD=N +field SFInt32 fapID 1 #%b=[1, 68] #%q=13 7 +field SFInt32 highLevelSelect 1 #%b=[1, 64] #%q=13 6 +exposedField MFFaceDefMeshNode faceDefMesh [] +exposedField MFFaceDefTransformNode faceDefTransform [] +] { +} + +PROTO FaceDefTransform [ #%NDT=SFWorldNode,SFFaceDefTransformNode %COD=N +field SF3DNode faceSceneGraphNode NULL +field SFInt32 fieldId 1 +field SFRotation rotationDef 0 0 1 0 #%b=(-I,+I) #%q=10 +field SFVec3f scaleDef 1 1 1 #%q=7 +field SFVec3f translationDef 0 0 0 #%q=1 +] { +} + +PROTO FAP [ #%NDT=SFWorldNode,SFFAPNode %COD=N +exposedField SFVisemeNode viseme NULL +exposedField SFExpressionNode expression NULL +exposedField SFInt32 open_jaw +I #%b=[0, +I) #%q=0 +exposedField SFInt32 lower_t_midlip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_b_midlip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 stretch_l_corner +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 stretch_r_corner +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 lower_t_lip_lm +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 lower_t_lip_rm +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 lower_b_lip_lm +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 lower_b_lip_rm +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_l_cornerlip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_r_cornerlip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 thrust_jaw +I #%b=[0,+I) #%q=0 +exposedField SFInt32 shift_jaw +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 push_b_lip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 push_t_lip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 depress_chin +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 close_t_l_eyelid +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 close_t_r_eyelid +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 close_b_l_eyelid +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 close_b_r_eyelid +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 yaw_l_eyeball +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 yaw_r_eyeball +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 pitch_l_eyeball +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 pitch_r_eyeball +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 thrust_l_eyeball +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 thrust_r_eyeball +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 dilate_l_pupil +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 dilate_r_pupil +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_l_i_eyebrow +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_r_i_eyebrow +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_l_m_eyebrow +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_r_m_eyebrow +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_l_o_eyebrow +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_r_o_eyebrow +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 squeeze_l_eyebrow +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 squeeze_r_eyebrow +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 puff_l_cheek +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 puff_r_cheek +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 lift_l_cheek +I #%b=[0,+I) #%q=0 +exposedField SFInt32 lift_r_cheek +I #%b=[0,+I) #%q=0 +exposedField SFInt32 shift_tongue_tip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_tongue_tip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 thrust_tongue_tip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_tongue +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 tongue_roll +I #%b=[0,+I) #%q=0 +exposedField SFInt32 head_pitch +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 head_yaw +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 head_roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 lower_t_midlip_o +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_b_midlip_o +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 stretch_l_cornerlip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 stretch_r_cornerlip +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 lower_t_lip_lm_o +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 lower_t_lip_rm_o +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_b_lip_lm_o +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_b_lip_rm_o +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_l_cornerlip_o +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_r_cornerlip_o +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 stretch_l_nose +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 stretch_r_nose +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_nose +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 bend_nose +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_l_ear +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 raise_r_ear +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 pull_l_ear +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 pull_r_ear +I #%b=(-I,+I) #%q=0 +] { +} + +PROTO FDP [ #%NDT=SFWorldNode,SFFDPNode %COD=N +exposedField SFCoordinateNode featurePointsCoord NULL +exposedField SFTextureCoordinateNode textureCoord NULL +exposedField MFFaceDefTablesNode faceDefTables [] +exposedField MF3DNode faceSceneGraph [] +field SFBool useOrthoTexture FALSE +] { +} + + +PROTO FIT [ #%NDT=SFWorldNode,SFFITNode %COD=N +exposedField MFInt32 FAPs [] #%b=[-1,68] #%q=13 7 +exposedField MFInt32 Graph [] #%b=[0,68] #%q=13 7 +exposedField MFInt32 numeratorExp [] #%b=[0,15] #%q=13 4 +exposedField MFInt32 denominatorExp [] #%b=[0,15] #%q=13 4 +exposedField MFInt32 numeratorImpulse [] #%b=[0,1023] #%q=13 10 +exposedField MFInt32 numeratorTerms [] #%b=[0,10] #%q=13 4 +exposedField MFInt32 denominatorTerms [] #%b=[0,10] #%q=13 4 +exposedField MFFloat numeratorCoefs [] #%b=(-I,+I) +exposedField MFFloat denominatorCoefs [] #%b=(-I,+I) +] { +} + +PROTO Fog [ #%NDT=SFWorldNode,SF3DNode,SFFogNode %COD=N +exposedField SFColor color 1 1 1 #%b=[0,1] #%q=4 #%a=4 +exposedField SFString fogType "LINEAR" +exposedField SFFloat visibilityRange 0 #%b=[0,+I) #%q=11 #a=7 +eventIn SFBool set_bind +eventOut SFBool isBound +] { +} + + +PROTO FontStyle [ #%NDT=SFWorldNode,SFFontStyleNode %COD=N +exposedField MFString family ["SERIF"] +exposedField SFBool horizontal TRUE +exposedField MFString justify ["BEGIN"] +exposedField SFString language "" +exposedField SFBool leftToRight TRUE +exposedField SFFloat size 1.0 #%b=[0,+I) #%q=11 +exposedField SFFloat spacing 1.0 #%b=[0,+I) #%q=11 +exposedField SFString style "PLAIN" +exposedField SFBool topToBottom TRUE +] { +} + +PROTO Form [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn MF2DNode addChildren +eventIn MF2DNode removeChildren +exposedField MF2DNode children [] +exposedField SFVec2f size -1 -1 #%b=[0,+I) #%q=12 #%a=12 +exposedField MFInt32 groups [] #%b=[-1,1022] #%q=13 10 +exposedField MFString constraints [] +exposedField MFInt32 groupsIndex [] #%b=[-1,1022] #%q=13 10 +]{} + + +PROTO Group [ #%NDT=SFWorldNode,SFTopNode,SF3DNode,SF2DNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +] { +} + + +PROTO ImageTexture [ #%NDT=SFWorldNode,SFTextureNode %COD=N +exposedField MFURL url [] +field SFBool repeatS TRUE +field SFBool repeatT TRUE +] { +} + +PROTO IndexedFaceSet [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn MFInt32 set_colorIndex +eventIn MFInt32 set_coordIndex +eventIn MFInt32 set_normalIndex +eventIn MFInt32 set_texCoordIndex +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField SFNormalNode normal NULL +exposedField SFTextureCoordinateNode texCoord NULL +field SFBool ccw TRUE +field MFInt32 colorIndex [] #%b=[-1,+I) #%q=14 +field SFBool colorPerVertex TRUE +field SFBool convex TRUE +field MFInt32 coordIndex [] #%b=[-1,+I) #%q=14 +field SFFloat creaseAngle 0.0 #%b=[0,6.2831853] #%q=6 +field MFInt32 normalIndex [] #%b=[-1,+I) #%q=14 +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +field MFInt32 texCoordIndex [] #%b=[-1,+I) #%q=14 +] { +} + +PROTO IndexedFaceSet2D [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn MFInt32 set_colorIndex +eventIn MFInt32 set_coordIndex +eventIn MFInt32 set_texCoordIndex +exposedField SFColorNode color NULL +exposedField SFCoordinate2DNode coord NULL +exposedField SFTextureCoordinateNode texCoord NULL +field MFInt32 colorIndex [] #%b=[-1,+I) #%q=14 +field SFBool colorPerVertex TRUE +field SFBool convex TRUE +field MFInt32 coordIndex [] #%b=[-1,+I) #%q=14 +field MFInt32 texCoordIndex [] #%b=[-1,+I) #%q=14 +] { +} + +PROTO IndexedLineSet [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn MFInt32 set_colorIndex +eventIn MFInt32 set_coordIndex +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +field MFInt32 colorIndex [] #%b=[-1,+I) #%q=14 +field SFBool colorPerVertex TRUE +field MFInt32 coordIndex [] #%b=[-1,+I) #%q=14 +] { +} + +PROTO IndexedLineSet2D [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn MFInt32 set_colorIndex +eventIn MFInt32 set_coordIndex +exposedField SFColorNode color NULL +exposedField SFCoordinate2DNode coord NULL +field MFInt32 colorIndex [] #%b=[-1,+I) #%q=14 +field SFBool colorPerVertex TRUE +field MFInt32 coordIndex [] #%b=[-1,+I) #%q=14 +]{ +} + +PROTO Inline [ #%NDT=SFWorldNode,SF3DNode,SFStreamingNode,SF2DNode %COD=N +exposedField MFURL url [] +] { +} + + +PROTO LOD [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +exposedField MF3DNode level [] +field SFVec3f center 0 0 0 #%b=(-I,+I) #%q=1 +field MFFloat range [] #%b=[0,+I] #%q=11 +] { +} + +PROTO Layer2D [ #%NDT=SFWorldNode,SFTopNode,SF2DNode,SF3DNode %COD=N +eventIn MF2DNode addChildren +eventIn MF2DNode removeChildren +exposedField MF2DNode children [] +exposedField SFVec2f size -1 -1 #%b=(-I,+I) #%q=12 #%a=12 +exposedField SFBackground2DNode background NULL +exposedField SFViewportNode viewport NULL +] { +} + +PROTO Layer3D [ #%NDT=SFWorldNode,SFTopNode,SF2DNode,SF3DNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFVec2f size -1 -1 #%b=(-I,+I) #%q=12 #%a=12 +exposedField SFBackground3DNode background NULL +exposedField SFFogNode fog NULL +exposedField SFNavigationInfoNode navigationInfo NULL +exposedField SFViewpointNode viewpoint NULL +] { +} + + +PROTO Layout [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn MF2DNode addChildren +eventIn MF2DNode removeChildren +exposedField MF2DNode children [] +exposedField SFBool wrap FALSE +exposedField SFVec2f size -1 -1 #%b=[0,+I) #%q=12 #%a=12 +exposedField SFBool horizontal TRUE +exposedField MFString justify ["BEGIN"] +exposedField SFBool leftToRight TRUE +exposedField SFBool topToBottom TRUE +exposedField SFFloat spacing 1 #%b=[0,+I) #%q=0 #%a=7 +exposedField SFBool smoothScroll FALSE +exposedField SFBool loop FALSE +exposedField SFBool scrollVertical TRUE +exposedField SFFloat scrollRate 0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFInt32 scrollMode 0 #%b=[-1,1] #%q=13 2 +]{} + +PROTO LineProperties [ #%NDT=SFWorldNode,SFLinePropertiesNode %COD=N +exposedField SFColor lineColor 0 0 0 #%b=[0,1] #%q=4 #%a=4 +exposedField SFInt32 lineStyle 0 #%b=[0,5] #%q=13 3 +exposedField SFFloat width 1 #%b=[0,+I) #%q=12 #%a=7 +]{ +} + +PROTO ListeningPoint [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn SFBool set_bind +exposedField SFBool jump TRUE +exposedField SFRotation orientation 0 0 1 0 #%q=10 #%a=10 +exposedField SFVec3f position 0 0 10 #%b=(-I,+I) #%q=1 #%a=1 +field SFString description "" +eventOut SFTime bindTime +eventOut SFBool isBound +] { +} + +PROTO Material [ #%NDT=SFWorldNode,SFMaterialNode %COD=N +exposedField SFFloat ambientIntensity 0.2 #%b=[0,1] #%q=4 #%a=8 +exposedField SFColor diffuseColor 0.8 0.8 0.8 #%b=[0,1] #%q=4 #%a=4 +exposedField SFColor emissiveColor 0 0 0 #%b=[0,1] #%q=4 #%a=4 +exposedField SFFloat shininess 0.2 #%b=[0,1] #%q=4 #%a=8 +exposedField SFColor specularColor 0 0 0 #%b=[0,1] #%q=4 #%a=4 +exposedField SFFloat transparency 0 #%b=[0,1] #%q=4 #%a=8 +] { +} + +PROTO Material2D [ #%NDT=SFWorldNode,SFMaterialNode %COD=N +exposedField SFColor emissiveColor 0.8 0.8 0.8 #%b=[0,1] #%q=4 #%a=4 +exposedField SFBool filled FALSE +exposedField SFLinePropertiesNode lineProps NULL +exposedField SFFloat transparency 0 #%b=[0,1] #%q=4 #%a=8 +]{ +} + +PROTO MovieTexture [ #%NDT=SFWorldNode,SFTextureNode,SFStreamingNode %COD=N +exposedField SFBool loop FALSE +exposedField SFFloat speed 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFTime startTime 0 #%b=(-I,+I) +exposedField SFTime stopTime 0 #%b=(-I,+I) +exposedField MFURL url [] +field SFBool repeatS TRUE +field SFBool repeatT TRUE +eventOut SFTime duration_changed +eventOut SFBool isActive +] { +} + +PROTO NavigationInfo [ #%NDT=SFWorldNode,SF3DNode,SFNavigationInfoNode %COD=N +eventIn SFBool set_bind +exposedField MFFloat avatarSize [0.25, 1.6, 0.75] # %b=[0,+I) #%q=11 +exposedField SFBool headlight TRUE +exposedField SFFloat speed 1.0 # %b=[0,+I) #%q=0 +exposedField MFString type ["WALK", "ANY"] +exposedField SFFloat visibilityLimit 0.0 # %b=[0,+I) #%q=11 #%a=7 +eventOut SFBool isBound +]{ +} + +PROTO Normal [ #%NDT=SFWorldNode,SFNormalNode %COD=N +exposedField MFVec3f vector [] #%q=9 #%a=9 +] { +} + +PROTO NormalInterpolator [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn SFFloat set_fraction +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFVec3f keyValue [] #%b=(-I,+I) #%q=9 +eventOut MFVec3f value_changed +] { +} + +PROTO OrderedGroup [ #%NDT=SFWorldNode,SF3DNode,SF2DNode,SFTopNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField MFFloat order [] #%b=[0,+I) #%q=3 +] { +} + +PROTO OrientationInterpolator [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn SFFloat set_fraction +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFRotation keyValue [] #%b=(-I,+I) #%q=10 +eventOut SFRotation value_changed +] { +} + + +PROTO PixelTexture [ #%NDT=SFWorldNode,SFTextureNode %COD=N +exposedField SFImage image 0 0 0 # %q=0 +field SFBool repeatS TRUE +field SFBool repeatT TRUE +] { +} + +PROTO PlaneSensor [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFBool autoOffset TRUE +exposedField SFBool enabled TRUE +exposedField SFVec2f maxPosition -1 -1 #%b=(-I,+I) #%q=2 +exposedField SFVec2f minPosition 0 0 #%b=(-I,+I) #%q=2 +exposedField SFVec3f offset 0 0 0 #%b=(-I,+I) #%q=1 +eventOut SFBool isActive +eventOut SFVec3f trackPoint_changed +eventOut SFVec3f translation_changed +] { +} + +PROTO PlaneSensor2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +exposedField SFBool autoOffset TRUE +exposedField SFBool enabled TRUE +exposedField SFVec2f maxPosition 0 0 #%b=(-I,+I) #%q=2 +exposedField SFVec2f minPosition 0 0 #%b=(-I,+I) #%q=2 +exposedField SFVec2f offset 0 0 #%b=(-I,+I) #%q=12 +eventOut SFBool isActive +eventOut SFVec2f trackPoint_changed +eventOut SFVec2f translation_changed +] { +} + +PROTO PointLight [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFFloat ambientIntensity 0 #%b=[0,1] #%q=4 #%a=8 +exposedField SFVec3f attenuation 1 0 0 #%b=[0,+I) #%q=11 #%a=1 +exposedField SFColor color 1 1 1 #%b=[0,1] #%q=4 #%a=4 +exposedField SFFloat intensity 1 #%b=[0,1] #%q=4 #%a=8 +exposedField SFVec3f location 0 0 0 #%b=(-I,+I) #%q=1 #%a=1 +exposedField SFBool on TRUE +exposedField SFFloat radius 100 #%b=[0,+I) #%q=11 #%a=7 +] { +} + +PROTO PointSet [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +] { +} + +PROTO PointSet2D [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFColorNode color NULL +exposedField SFCoordinate2DNode coord NULL +]{ +} + + +PROTO PositionInterpolator [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn SFFloat set_fraction +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFVec3f keyValue [] #%b=(-I,+I) #%q=1 +eventOut SFVec3f value_changed +] { +} + +PROTO PositionInterpolator2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn SFFloat set_fraction +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFVec2f keyValue [] #%b=(-I,+I) #%q=2 +eventOut SFVec2f value_changed +] { +} + +PROTO ProximitySensor2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +exposedField SFVec2f center 0 0 #%b=[-1,+I) #%q=2 +exposedField SFVec2f size 0 0 #%b=[0,+I) #%q=12 +exposedField SFBool enabled TRUE +eventOut SFBool isActive +eventOut SFVec2f position_changed +eventOut SFFloat orientation_changed +eventOut SFTime enterTime +eventOut SFTime exitTime +] { +} + +PROTO ProximitySensor [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFVec3f center 0 0 0 #%b=(-I,+I) #%q=1 +exposedField SFVec3f size 0 0 0 #%b=[0,+I) #%q=11 +exposedField SFBool enabled TRUE +eventOut SFBool isActive +eventOut SFVec3f position_changed +eventOut SFRotation orientation_changed +eventOut SFTime enterTime +eventOut SFTime exitTime +] { +} + +PROTO QuantizationParameter [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +field SFBool isLocal FALSE +field SFBool position3DQuant FALSE +field SFVec3f position3DMin -I -I -I #%b=(-I,+I) #%q=0 +field SFVec3f position3DMax +I +I +I #%b=(-I,+I) #%q=0 +field SFInt32 position3DNbBits 16 #%b=[0,31] #%q=13 5 +field SFBool position2DQuant FALSE +field SFVec2f position2DMin -I -I #%b=(-I,+I) #%q=0 +field SFVec2f position2DMax +I +I #%b=(-I,+I) #%q=0 +field SFInt32 position2DNbBits 16 #%b=[0,31] #%q=13 5 +field SFBool drawOrderQuant FALSE +field SFFloat drawOrderMin -I #%b=(-I,+I) #%q=0 +field SFFloat drawOrderMax +I #%b=(-I,+I) #%q=0 +field SFInt32 drawOrderNbBits 8 #%b=[0,31] #%q=13 5 +field SFBool colorQuant TRUE +field SFFloat colorMin 0.0 #%b=[0,1] #%q=0 +field SFFloat colorMax 1.0 #%b=[0,1] #%q=0 +field SFInt32 colorNbBits 8 #%b=[0,31] #%q=13 5 +field SFBool textureCoordinateQuant TRUE +field SFFloat textureCoordinateMin 0 #%b=[0,1] #%q=0 +field SFFloat textureCoordinateMax 1 #%b=[0,1] #%q=0 +field SFInt32 textureCoordinateNbBits 16 #%b=[0,31] #%q=13 5 +field SFBool angleQuant TRUE +field SFFloat angleMin 0.0 #%b=[0,6.2831853] #%q=0 +field SFFloat angleMax 6.2831853 #%b=[0,6.2831853] #%q=0 +field SFInt32 angleNbBits 16 #%b=[0,31] #%q=13 5 +field SFBool scaleQuant FALSE +field SFFloat scaleMin 0.0 #%b=(-I,+I) #%q=0 +field SFFloat scaleMax +I #%b=(-I,+I) #%q=0 +field SFInt32 scaleNbBits 8 #%b=[0,31] #%q=13 5 +field SFBool keyQuant TRUE +field SFFloat keyMin 0.0 #%b=(-I,+I) #%q=0 +field SFFloat keyMax 1.0 #%b=(-I,+I) #%q=0 +field SFInt32 keyNbBits 8 #%b=[0,31] #%q=13 5 +field SFBool normalQuant TRUE +field SFInt32 normalNbBits 8 #%b=[0,31] #%q=13 5 +field SFBool sizeQuant FALSE +field SFFloat sizeMin 0 #%b=(-I,+I) #%q=0 +field SFFloat sizeMax +I #%b=(-I,+I) #%q=0 +field SFInt32 sizeNbBits 8 #%b=[0,31] #%q=13 5 +field SFBool useEfficientCoding FALSE +]{ +} + +PROTO Rectangle [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFVec2f size 2 2 #%b=[0,+I) #%q=12 #%a=2 +]{ +} + +PROTO ScalarInterpolator [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +eventIn SFFloat set_fraction +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFFloat keyValue [] #%b=(-I,+I) #%q=0 +eventOut SFFloat value_changed +]{} + +PROTO Script [#%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=S +exposedField MFScript url [] +field SFBool directOutput FALSE +field SFBool mustEvaluate FALSE +]{ +} + +PROTO Shape [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +exposedField SFAppearanceNode appearance NULL +exposedField SFGeometryNode geometry NULL +] { +} + +PROTO Sound [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFVec3f direction 0 0 1 #%b=(-I,+I) #%q=9 +exposedField SFFloat intensity 1 #%b=[0,1] #%q=4 #%a=7 +exposedField SFVec3f location 0 0 0 #%b=(-I,+I) #%q=1 #%a=1 +exposedField SFFloat maxBack 10 #%b=[0,+I) #%q=11 #%a=7 +exposedField SFFloat maxFront 10 #%b=[0,+I) #%q=11 #%a=7 +exposedField SFFloat minBack 1 #%b=[0,+I) #%q=11 #%a=7 +exposedField SFFloat minFront 1 #%b=[0,+I) #%q=11 #%a=7 +exposedField SFFloat priority 0 #%b=[0,1] #%q=4 +exposedField SFAudioNode source NULL +field SFBool spatialize TRUE +] { +} + +PROTO Sound2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +exposedField SFFloat intensity 1 #%b=[0,1] #%q=4 #%a=7 +exposedField SFVec2f location 0 0 #%b=(-I,+I) #%q=2 #%a=2 +exposedField SFAudioNode source NULL +field SFBool spatialize TRUE +]{ +} + +PROTO Sphere [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +field SFFloat radius 1 #%b=(0,+I) #%q=11 +] { +} + + +PROTO SphereSensor [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFBool autoOffset TRUE +exposedField SFBool enabled TRUE +exposedField SFRotation offset 0 1 0 0 # %b=(-I,+I) #%q=10 +eventOut SFBool isActive +eventOut SFRotation rotation_changed +eventOut SFVec3f trackPoint_changed +]{ +} + +PROTO SpotLight [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFFloat ambientIntensity 0 #%b=[0,1] #%q=4 #%a=8 +exposedField SFVec3f attenuation 1 0 0 #%b=[0,+I) #%q=11 #%a=1 +exposedField SFFloat beamWidth 1.570796 #%b=[0,1.5707963] #%q=6 #%a=8 +exposedField SFColor color 1 1 1 #%b=[0,1] #%q=4 #%a=4 +exposedField SFFloat cutOffAngle 0.785398 #%b=[0,1.5707963] #%q=6 #%a=8 +exposedField SFVec3f direction 0 0 -1 #%b=(-I,+I) #%q=9 #%a=9 +exposedField SFFloat intensity 1 #%b=[0,1] #%q=4 #%a=8 +exposedField SFVec3f location 0 0 0 #%b=(-I,+I) #%q=1 #%a=1 +exposedField SFBool on TRUE +exposedField SFFloat radius 100 #%b=[0,+I) #%q=11 #%a=7 +] { +} + +PROTO Switch [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +exposedField MF3DNode choice [] +exposedField SFInt32 whichChoice -1 #%b=[-1, 1022] #%q=13 10 +] { +} + +PROTO TermCap [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn SFTime evaluate +exposedField SFInt32 capability 0 #%b=[0,127] #%q=13 7 +eventOut SFInt32 value 0 #%b=[0,7] #%q=13 3 +] { +} + +PROTO Text [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField MFString string [] +exposedField MFFloat length [] #%b=[0,+I) #%q=11 #%a=7 +exposedField SFFontStyleNode fontStyle NULL +exposedField SFFloat maxExtent 0.0 #%b=[0,+I) #%q=11 #%a=7 +] { +} + +PROTO TextureCoordinate [ #%NDT=SFWorldNode,SFTextureCoordinateNode %COD=N +exposedField MFVec2f point [] #%b=(-I,+I) #%q=5 #%a=2 +]{ +} + +PROTO TextureTransform [ #%NDT=SFWorldNode,SFTextureTransformNode %COD=N +exposedField SFVec2f center 0 0 #%b=(-I,+I) #%q=2 #%a=2 +exposedField SFFloat rotation 0 #%b=[0,6.2831853] #%q=6 #%a=6 +exposedField SFVec2f scale 1 1 #%b=(-I,+I) #%q=7 #%a=12 +exposedField SFVec2f translation 0 0 #%b=(-I,+I) #%q=2 #%a=2 +] { +} + +PROTO TimeSensor [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +exposedField SFTime cycleInterval 1 #%b=(0,+I) +exposedField SFBool enabled TRUE +exposedField SFBool loop FALSE +exposedField SFTime startTime 0 #%b=(-I,+I) +exposedField SFTime stopTime 0 #%b=(-I,+I) +eventOut SFTime cycleTime +eventOut SFFloat fraction_changed +eventOut SFBool isActive +eventOut SFTime time +] { +} + +PROTO TouchSensor [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +exposedField SFBool enabled TRUE +eventOut SFVec3f hitNormal_changed +eventOut SFVec3f hitPoint_changed +eventOut SFVec2f hitTexCoord_changed +eventOut SFBool isActive +eventOut SFBool isOver +eventOut SFTime touchTime +] {} + +PROTO Transform [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField SFVec3f center 0 0 0 #%b=(-I,+I) #%q=1 #%a=1 +exposedField MF3DNode children [] +exposedField SFRotation rotation 0 0 1 0 #%q=10 #%a=10 +exposedField SFVec3f scale 1 1 1 #%b=(0,+I) #%q=7 #%a=11 +exposedField SFRotation scaleOrientation 0 0 1 0 #%q=10 #%a=10 +exposedField SFVec3f translation 0 0 0 #%b=(-I,+I) #%q=1 #%a=1 +] { +} + +PROTO Transform2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn MF2DNode addChildren +eventIn MF2DNode removeChildren +exposedField MF2DNode children [] +exposedField SFVec2f center 0 0 #%b=(-I,+I) #%q=2 #%a=2 +exposedField SFFloat rotationAngle 0 #%b=[0,6.2831853] #%q=6 #%a=6 +exposedField SFVec2f scale 1 1 #%b=(-I,+I) #%q=7 #%a=12 +exposedField SFFloat scaleOrientation 0 #%b=[0,6.2831853] #%q=6 #%a=6 +exposedField SFVec2f translation 0 0 #%b=(-I,+I) #%q=2 #%a=2 +] { +} + +PROTO Valuator [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +eventIn SFBool inSFBool +eventIn SFColor inSFColor +eventIn MFColor inMFColor +eventIn SFFloat inSFFloat +eventIn MFFloat inMFFloat +eventIn SFInt32 inSFInt32 +eventIn MFInt32 inMFInt32 +eventIn SFRotation inSFRotation +eventIn MFRotation inMFRotation +eventIn SFString inSFString +eventIn MFString inMFString +eventIn SFTime inSFTime +eventIn SFVec2f inSFVec2f +eventIn MFVec2f inMFVec2f +eventIn SFVec3f inSFVec3f +eventIn MFVec3f inMFVec3f +eventOut SFBool outSFBool +eventOut SFColor outSFColor +eventOut MFColor outMFColor +eventOut SFFloat outSFFloat +eventOut MFFloat outMFFloat +eventOut SFInt32 outSFInt32 +eventOut MFInt32 outMFInt32 +eventOut SFRotation outSFRotation +eventOut MFRotation outMFRotation +eventOut SFString outSFString +eventOut MFString outMFString +eventOut SFTime outSFTime +eventOut SFVec2f outSFVec2f +eventOut MFVec2f outMFVec2f +eventOut SFVec3f outSFVec3f +eventOut MFVec3f outMFVec3f +exposedField SFFloat Factor1 1.0 #%b=(-I,+I) #%q=0 +exposedField SFFloat Factor2 1.0 #%b=(-I,+I) #%q=0 +exposedField SFFloat Factor3 1.0 #%b=(-I,+I) #%q=0 +exposedField SFFloat Factor4 1.0 #%b=(-I,+I) #%q=0 +exposedField SFFloat Offset1 0.0 #%b=(-I,+I) #%q=0 +exposedField SFFloat Offset2 0.0 #%b=(-I,+I) #%q=0 +exposedField SFFloat Offset3 0.0 #%b=(-I,+I) #%q=0 +exposedField SFFloat Offset4 0.0 #%b=(-I,+I) #%q=0 +exposedField SFBool Sum FALSE +] { +} + +PROTO Viewpoint [ #%NDT=SFWorldNode,SF3DNode,SFViewpointNode %COD=N +eventIn SFBool set_bind +exposedField SFFloat fieldOfView 0.785398 #%b=[0,3.1415927] #%q=6 #%a=8 +exposedField SFBool jump TRUE +exposedField SFRotation orientation 0 0 1 0 #%q=10 #%a=10 +exposedField SFVec3f position 0 0 10 #%b=(-I,+I) #%q=1 #%a=1 +field SFString description "" +eventOut SFTime bindTime +eventOut SFBool isBound +] { +} + + +PROTO VisibilitySensor [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFVec3f center 0 0 0 # %b=(-I,+I) #%q=1 #%a=1 +exposedField SFBool enabled TRUE +exposedField SFVec3f size 0 0 0 # %b=[0,+I) #%q=11 #%a=11 +eventOut SFTime enterTime +eventOut SFTime exitTime +eventOut SFBool isActive +]{ +} + +PROTO Viseme [ #%NDT=SFWorldNode,SFVisemeNode %COD=N +exposedField SFInt32 viseme_select1 0 #%b=(0,31) #%q=13 5 +exposedField SFInt32 viseme_select2 0 #%b=(0,31) #%q=13 5 +exposedField SFInt32 viseme_blend 0 #%b=(0,63) #%q=13 6 +exposedField SFBool viseme_def FALSE +] { +} + +PROTO WorldInfo [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +field MFString info [] +field SFString title "" +] { +} diff --git a/applications/generators/MPEG4/templates2.txt b/applications/generators/MPEG4/templates2.txt new file mode 100644 index 0000000..b3c013e --- /dev/null +++ b/applications/generators/MPEG4/templates2.txt @@ -0,0 +1,592 @@ +#-- Version 2 --# +# templates for the BIFS nodes of v2 +# ================================== +# Notations I = Infinity +# %q=x Quantization method x +# 0 None +# 1 3D Position (SFVec3F) +# 2 2D Position (SFVec2F) +# 3 drawing Order +# 4 Color (SFColor) +# 5 Texture Coordinate +# 6 Angle (SFFloat 0-2PI) +# 7 Scale (SFVec2F or SFVec3F) +# 8 Interpolators keys +# 9 Normals +# 10 Rotations (SFRotation) +# 11 Object Size 3D (SFVec3F and SFFloat) +# 12 Object Size 2D +# 13 Linear Quantization (+ Nb Bits) +# 14 Index (of IndexedFaceSet,...) +# 15 Reserved +# +# %a=y Animation method for fields that can be animated +# +## OO 081498 To match BIFS's update numbering +# 0 None +# 1 Position 3D +# 2 Position 2D +# 4 Color +# 6 Angle +# 7 Float +# 8 BoundFloat (intensities, transparencies,...) +# 9 Normal +# 10 Rotation +# 11 Size 3D +# 12 Size 2D +# 13 Integer +# 14 Reserved +## 0 3D Position +## 1 2D positon +## 2 Color (SFColor) +## 3 Angle (SFFloat 0-2pi) +## 4 Normals +## 5 Scale (SFVec2F) +## 6 Rotation (SFRotation) +## 7 Object Size or Scalar (SFFloat) +# +# %b=[min,max] bounds of value +# For each scalar or vectorial value, bounds may be specified. +# This will be used to check if user-specified values are out of bounds. In +# this case, bounds specified in the templates will be used (if not infinity). +# +# %NDT=Node Data Type +# For each node, one or several Node Data Types are assigned, specifying which node sub +# types the node belongs to. Moreover, each field of type SF/MF3DNode is re assigned +# a unique correct NodeDataType according to specify the allowed values of the field +# +# %COD Type of encoding +# N Normal Syntax : The node syntax follos the generic syntax for nodes +# S Special Syntax : The node has a specific syntax +# +# +# NCT => VRML type equivalence +# +# SF/MFxxxNode => SF/MFNode +# SF/MFURL => SF/MFString +# SF/MFCommandBuffer => SF/MFString +# SF/MFScript => SF/MFString +# +# +# Modification History +# ------------------------------------------------ +# JS: Created on 990726 +# _________________ +# YF: modified 990726 +# added NDT to nodes below, but probably incorrectly. +#BodySegmentConnectionHint [ #%NDT=SFWorldNode +#MaterialKey [#%NDT=SFWorldNode +#PROTO ServerCommand [#%NDT=SFWorldNode +#Hierarchical3DMesh {#%NDT=SFWorldNode +# +# JS: modified 990726 +# Cleaned up NDTs. +# re-ordered in the right order fields and events: eventIn, exposedField, field, eventOut +# => FPDAM needs to be changed accordingly +# corrected case of fields and event types! +# re-aligned stuff with spaces so that we may conserve a kind of alignment. Please be +# careful! did not finish for BAp, too long! +# +# YF: modified 990728 +# YF: fixed typo in Hierarchical3DMesh, finishd BAP allignment +# +# YF: modified 990729 +# YF: many more typos +# +# Aug 23, 1999 (Mikael Bourges-Sevenier) added body animation nodes from Tolga +# Aug 26, 1999 (Mikael Bourges-Sevenier) added body audio nodes from Riitta +# Sep 1, 1999 (MBS) cross-checked everything. +# Dec 20, 1999 YF +# bounds of form (x,y) changed to form [a,b] e.g.: +# numInterpolatorkeys gets new range [2,+I) +# bapIds gets new range [1,296] +# BAPs set to +I default value +# added application window +# +# 23.1.2000 (Riitta) +# Fixed AcousticMaterial, AcousticScene, DirectiveSound +# to comply with the latest spec (FDIS). +# +# Feb 8, 2000 (YF) NULL -> [] in BodySegmentConnectionHint +# remove "," from Vec3f values and put [ ] around MFFloat values +# +# July 27, 2000 (YF) NDT of Hierarchical3DMech changed to SFGeometryNode +# +# August 10, 2000 (YF per Steve Wood): +# Add [] around default values of MF field in AcousticMaterial, AcousticScene +# DirectiveSound, PerceptualParameters +# ApplicationWindow parameter field default value just [] + +#PROTO AcousticMaterial [#%NDT=SFMaterialNode,SFWorldNode %COD=N +#exposedField SFFloat ambientIntensity 0.2 #%b=[0,1] #%q=4 #%a=8 +#exposedField SFColor diffuseColor 0.8, 0.8, 0.8 #%b=[0,1] #%q=4 #%a=8 +#exposedField SFColor emissiveColor 0, 0, 0 #%b=[0,1] #%q=4 #%a=8 +#exposedField SFFloat shininess 0.2 #%b=[0,1] #%q=4 #%a=8 +#exposedField SFColor specularColor 0, 0, 0 #%b=[0,1] #%q=4 #%a=8 +#exposedField SFFloat transparency 0 #%b=[0,1] #%q=4 #%a=8 +#field SFFloat reffunc 0 #%b=(-I,+I) #%q=0 +#field SFFloat transfunc 1 #%b=(-I,+I) #%q=0 +#]{ +#} + +PROTO AcousticMaterial [#%NDT=SFMaterialNode,SFWorldNode %COD=N +exposedField SFFloat ambientIntensity 0.2 #%b=[0,1] #%q=4 #%a=8 +exposedField SFColor diffuseColor 0.8 0.8 0.8 #%b=[0,1] #%q=4 #%a=8 +exposedField SFColor emissiveColor 0 0 0 #%b=[0,1] #%q=4 #%a=8 +exposedField SFFloat shininess 0.2 #%b=[0,1] #%q=4 #%a=8 +exposedField SFColor specularColor 0 0 0 #%b=[0,1] #%q=4 #%a=8 +exposedField SFFloat transparency 0 #%b=[0,1] #%q=4 #%a=8 +field MFFloat reffunc [0] #%b=(-I,+I) #%q=0 +field MFFloat transfunc [1] #%b=(-I,+I) #%q=0 +field MFFloat refFrequency [0] #%b=(0,+I) #%q=0 +field MFFloat transFrequency [0] #%b=(0,+I) #%q=0 +]{ +} + +#PROTO AcousticScene [#%NDT=SFWorldNode,SF3DNode %COD=N +#exposedField SFFloat reverbLevel 0.4 #%b=(-I,+I) #%q=0 #%a=7 +#exposedField SFTime reverbDelay 0.5 #%b=(-I,+I) #%q=0 +#field SFVec3f center 0 0 0 #%b=(-I,+I) #%q=11 #%a=11 +#field SFVec3f Size -1 -1 -1 #%b=(-I,+I) #%q=1 #%a=1 +#field MFTime reverbTime [0] #%b=(-I,+I) #%q=0 +#field MFFloat reverbFreq [1000] #%b=(0 ,+I) #%q=0 +#]{ +#} + +PROTO AcousticScene [#%NDT=SFWorldNode,SF3DNode %COD=N +field SFVec3f center 0 0 0 #%b=(-I,+I) #%q=1 +field SFVec3f Size -1 -1 -1 #%b=(-I,+I) #%q=11 +field MFTime reverbTime [0] #%b=(0 ,+I) #%q=0 +field MFFloat reverbFreq [1000] #%b=(0 ,+I) #%q=0 +exposedField SFFloat reverbLevel 0.4 #%b=(0 ,+I) #%q=0 #%a=7 +exposedField SFTime reverbDelay 0.5 #%b=(0 ,+I) #%q=0 +]{ +} + +PROTO ApplicationWindow [#%NDT=SFWorldNode,SF2DNode %COD=N +exposedField SFBool isActive FALSE +exposedField SFTime startTime 0 #%b=(-I,+I) #%q=0 +exposedField SFTime stopTime 0 #%b=(-I,+I) #%q=0 +exposedField SFString description "" +exposedField MFString parameter [] +exposedField MFURL url [] +exposedField SFVec2f size 0 0 #%b=(-I,+I) #%q=12 #%a=12 +]{ +} + +PROTO BAP [#%NDT=SFWorldNode,SFBAPNode %COD=N +exposedField SFInt32 sacroiliac_tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 sacroiliac_torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 sacroiliac_roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_hip_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_hip_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_hip_abduct +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_hip_abduct +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_hip_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_hip_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_knee_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_knee_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_knee_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_knee_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_ankle_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_ankle_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_ankle_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_ankle_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_subtalar_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_subtalar_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_midtarsal_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_midtarsal_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_metatarsal_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_metatarsal_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_sternoclavicular_abduct +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_sternoclavicular_abduct +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_sternoclavicular_rotate +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_sternoclavicular_rotate +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_acromioclavicular_abduct +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_acromioclavicular_abduct +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_acromioclavicular_rotate +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_acromioclavicular_rotate +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_shoulder_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_shoulder_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_shoulder_abduct +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_shoulder_abduct +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_shoulder_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_shoulder_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_elbow_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_elbow_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_elbow_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_elbow_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_wrist_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_wrist_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_wrist_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_wrist_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_wrist_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_wrist_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 skullbase_roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 skullbase_torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 skullbase_tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc1roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc1torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc1tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc2roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc2torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc2tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc3roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc3torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc3tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc4roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc4torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc4tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc5roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc5torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc5tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc6roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc6torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc6tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc7roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc7torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vc7tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt1roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt1torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt1tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt2roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt2torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt2tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt3roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt3torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt3tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt4roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt4torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt4tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt5roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt5torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt5tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt6roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt6torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt6tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt7roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt7torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt7tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt8roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt8torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt8tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt9roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt9torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt9tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt10roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt10torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt10tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt11roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt11torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt11tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt12roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt12torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vt12tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl1roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl1torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl1tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl2roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl2torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl2tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl3roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl3torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl3tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl4roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl4torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl4tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl5roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl5torsion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 vl5tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_pinky0_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_pinky0_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_pinky1_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_pinky1_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_pinky1_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_pinky1_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_pinky1_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_pinky1_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_pinky2_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_pinky2_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_pinky3_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_pinky3_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_ring0_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_ring0_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_ring1_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_ring1_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_ring1_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_ring1_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_ring1_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_ring1_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_ring2_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_ring2_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_ring3_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_ring3_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_middle0_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_middle0_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_middle1_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_middle1_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_middle1_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_middle1_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_middle1_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_middle1_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_middle2_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_middle2_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_middle3_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_middle3_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_index0_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_index0_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_index1_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_index1_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_index1_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_index1_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_index1_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_index1_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_index2_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_index2_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_index3_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_index3_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_thumb1_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_thumb1_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_thumb1_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_thumb1_pivot +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_thumb1_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_thumb1_twisting +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_thumb2_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_thumb2_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 l_thumb3_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 r_thumb3_flexion +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 HumanoidRoot_tr_vertical +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 HumanoidRoot_tr_lateral +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 HumanoidRoot_tr_frontal +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 HumanoidRoot_rt_body_turn +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 HumanoidRoot_rt_body_roll +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 HumanoidRoot_rt_body_tilt +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap187 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap188 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap189 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap190 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap191 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap192 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap193 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap194 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap195 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap196 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap197 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap198 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap199 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap200 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap201 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap202 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap203 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap204 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap205 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap206 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap207 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap208 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap209 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap210 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap211 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap212 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap213 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap214 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap215 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap216 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap217 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap218 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap219 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap220 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap221 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap222 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap223 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap224 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap225 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap226 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap227 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap228 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap229 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap230 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap231 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap232 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap233 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap234 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap235 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap236 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap237 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap238 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap239 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap240 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap241 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap242 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap243 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap244 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap245 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap246 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap247 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap248 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap249 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap250 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap251 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap252 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap253 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap254 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap255 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap256 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap257 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap258 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap259 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap260 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap261 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap262 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap263 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap264 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap265 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap266 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap267 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap268 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap269 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap270 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap271 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap272 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap273 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap274 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap275 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap276 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap277 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap278 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap279 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap280 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap281 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap282 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap283 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap284 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap285 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap286 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap287 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap288 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap289 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap290 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap291 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap292 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap293 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap294 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap295 +I #%b=(-I,+I) #%q=0 +exposedField SFInt32 extensionBap296 +I #%b=(-I,+I) #%q=0 +] { +} + +PROTO BDP [#%NDT=SFWorldNode,SFBDPNode %COD=N +exposedField MFBodyDefTableNode bodyDefTables [] +exposedField MF3DNode bodySceneGraph [] +] { +} + +PROTO Body [#%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +exposedField SFBDPNode bdp NULL +exposedField SFBAPNode bap NULL +exposedField MF3DNode renderedBody [] +] +{ +} + +PROTO BodyDefTable [#%NDT=SFWorldNode,SFBodyDefTableNode %COD=N +exposedField SFString bodySceneGraphNodeName "" +exposedField MFInt32 bapIDs [] #%b=[1,296] #%q=13 9 +exposedField MFInt32 vertexIds [] #%b=[0,+I) #%q=0 +exposedField MFInt32 bapCombinations [] #%b=(-I,+I) #%q=0 +exposedField MFVec3f displacements [] +exposedField SFInt32 numInterpolateKeys 2 #%b=[2,+I) #%q=0 +] { +} + +PROTO BodySegmentConnectionHint [#%NDT=SFWorldNode,SFBodySegmentConnectionHintNode %COD=N +exposedField SFString firstSegmentNodeName "" +exposedField SFString secondSegmentNodeName "" +exposedField MFInt32 firstVertexIdList [] #%b=[0,+I) #%q=0 +exposedField MFInt32 secondVertexIdList [] #%b=[0,+I) #%q=0 +] { +} + +#PROTO DirectiveSound [#%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +#exposedField SFVec3f direction 0 0 -1 #%b=(-I,+I) #%q=9 #%a=9 +#exposedField SFFloat intensity 1 #%b=(-I,+I) #%q=0 #%a=7 +#exposedField SFVec3f location 0 0 0 #%b=(-I,+I) #%q=1 #%a=1 +#exposedField SFAudioNode source NULL +#exposedField SFPerceptualParameterNode perceptualParameters NULL +#exposedField SFBool roomEffect FALSE +#exposedField SFBool spatialize TRUE +#field MFFloat angles 1 #%b=[0,3.14159265] #%q=6 #%a=8 +#field MFFloat directivity 1 #%b=(-I,+I) #%q=0 +#field SFFloat speedOfSound 340 #%b=(-I,+I) #%q=1 #%a=1 +#field SFFloat distance 100 #%b=(-I,+I) #%q=0 +#field SFBool useAirabs FALSE +#]{ +#} + +PROTO DirectiveSound [#%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFVec3f direction 0 0 -1 #%b=(-I,+I) #%q=9 #%a=9 +exposedField SFFloat intensity 1 #%b=(0,+I) #%q=0 #%a=7 +exposedField SFVec3f location 0 0 0 #%b=(-I,+I) #%q=1 #%a=1 +exposedField SFAudioNode source NULL +exposedField SFPerceptualParameterNode perceptualParameters NULL +exposedField SFBool roomEffect FALSE +exposedField SFBool spatialize TRUE +field MFFloat directivity 1 #%b=(-I,+I) #%q=0 +field MFFloat angles 1 #%b=[0,3.14159265] #%q=6 +field MFFloat frequency [] #%b=(0,+I) #%q=0 +field SFFloat speedOfSound 340 #%b=(0,+I) #%q=1 +field SFFloat distance 100 #%b=(0,+I) #%q=0 +field SFBool useAirabs FALSE +]{ +} + +PROTO Hierarchical3DMesh [#%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn SFInt32 triangleBudget 1000 #%b=(-1,+I) #%q=0 +exposedField SFFloat level 1 #%b=(-1,+I) #%q=0 +field MFURL url [] +eventOut SFBool doneLoading +]{ +} + +PROTO MaterialKey [#%NDT=SFWorldNode,SFMaterialNode %COD=N +exposedField SFBool isKeyed TRUE +exposedField SFBool isRGB TRUE +exposedField SFColor keyColor 0 0 0 #%b=[0,1] #%q=4 #%a=4 +exposedField SFFloat lowThreshold 0 #%b=[0,1] #%q=4 #%a=8 +exposedField SFFloat highThreshold 0 #%b=[0,1] #%q=4 #%a=8 +exposedField SFFloat transparency 0 #%b=[0,1] #%q=4 #%a=8 +]{ +} + +PROTO PerceptualParameters [#%NDT=SFWorldNode,SFPerceptualParameterNode %COD=N +exposedField SFFloat sourcePresence 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat sourceWarmth 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat sourceBrilliance 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat roomPresence 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat runningReverberance 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat envelopment 0.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat lateReverberance 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat heavyness 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat liveness 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField MFFloat omniDirectivity [1.0] #%b=(-I,+I) #%q=0 #%a=7 +exposedField MFFloat directFilterGains [1.0, 1.0, 1.0] #%b=(-I,+I) #%q=0 #%a=7 +exposedField MFFloat inputFilterGains [1.0, 1.0, 1.0] #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat refDistance 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat freqLow 250.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFFloat freqHigh 4000.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFTime timeLimit1 0.02 #%b=(-I,+I) #%q=0 +exposedField SFTime timeLimit2 0.04 #%b=(-I,+I) #%q=0 +exposedField SFTime timeLimit3 0.1 #%b=(-I,+I) #%q=0 +exposedField SFTime modalDensity 0.8 #%b=(-I,+I) #%q=0 +]{ +} + +#PROTO ServerCommand [#%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +#eventIn SFBool trigger FALSE +#exposedField SFBool enable FALSE +#exposedField MFURL url [ ] +#eventIn SFString command "" +#]{ +#} + + + diff --git a/applications/generators/MPEG4/templates3.txt b/applications/generators/MPEG4/templates3.txt new file mode 100644 index 0000000..e2a0f4b --- /dev/null +++ b/applications/generators/MPEG4/templates3.txt @@ -0,0 +1,123 @@ +#-- Version 3 --# +# templates for the BIFS nodes of v3 +# ================================== +# Notations I = Infinity +# %q=x Quantization method x +# 0 None +# 1 3D Position (SFVec3F) +# 2 2D Position (SFVec2F) +# 3 drawing Order +# 4 Color (SFColor) +# 5 Texture Coordinate +# 6 Angle (SFFloat 0-2PI) +# 7 Scale (SFVec2F or SFVec3F) +# 8 Interpolators keys +# 9 Normals +# 10 Rotations (SFRotation) +# 11 Object Size 3D (SFVec3F and SFFloat) +# 12 Object Size 2D +# 13 Linear Quantization (+ Nb Bits) +# 14 Index (of IndexedFaceSet,...) +# 15 Reserved +# +# %a=y Animation method for fields that can be animated +# +# 0 None +# 1 Position 3D +# 2 Position 2D +# 4 Color +# 6 Angle +# 7 Float +# 8 BoundFloat (intensities, transparencies,...) +# 9 Normal +# 10 Rotation +# 11 Size 3D +# 12 Size 2D +# 13 Integer +# 14 Reserved +## 0 3D Position +## 1 2D positon +## 2 Color (SFColor) +## 3 Angle (SFFloat 0-2pi) +## 4 Normals +## 5 Scale (SFVec2F) +## 6 Rotation (SFRotation) +## 7 Object Size or Scalar (SFFloat) +# +# %b=[min,max] bounds of value +# For each scalar or vectorial value, bounds may be specified. +# This will be used to check if user-specified values are out of bounds. In +# this case, bounds specified in the templates will be used (if not infinity). +# +# %NDT=Node Data Type +# For each node, one or several Node Data Types are assigned, specifying +# which node sub types the node belongs to. Moreover, each field of type +# SF/MF3DNode is re assigned a unique correct NodeDataType according to +# specify the allowed values of the field +# +# %COD Type of encoding +# N Normal Syntax : The node syntax follos the generic syntax for nodes +# S Special Syntax : The node has a specific syntax +# +# +# NCT => VRML type equivalence +# +# SF/MFxxxNode => SF/MFNode +# SF/MFURL => SF/MFString +# SF/MFCommandBuffer => SF/MFString +# SF/MFScript => SF/MFString +# +# +# Modification History +# ------------------------------------------------ +# 10 Aug 2000 (YF per reflector discussion): +# TemporalGroup children is now an MFTemporalNode +# +# 12 Nov 2001 (YF per reflector discussion): +# reorder fields of ServerCommand to match specification +# +# 6 Jan 02 YF -- add default values for children field of TemporalTransform +# and TemporalGroup +# +# YF: Created on Jun 5, 2000 based on w3383 output from Geneva + +PROTO TemporalTransform [#%NDT=SFWorldNode,SF2DNode,SF3DNode,SFTemporalNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField MFURL url [] +exposedField SFTime startTime -1.0 +exposedField SFTime optimalDuration -1.0 +exposedField SFBool active FALSE +exposedField SFFloat speed 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFVec2f scalability 1.0 1.0 #%b=[-1,+I) #%q=12 #%a=12 +exposedField MFInt32 stretchMode [0] #%b=[0,2] #%q=13 2 +exposedField MFInt32 shrinkMode [0] #%b=[0,1] #%q=13 1 +exposedField SFTime maxDelay 0 +eventOut SFTime actualDuration +] { +} + + +PROTO TemporalGroup [#%NDT=SFWorldNode,SF2DNode,SF3DNode,SFTemporalNode %COD=N +eventIn MFTemporalNode addChildren +eventIn MFTemporalNode removeChildren +exposedField MFTemporalNode children [] +field SFBool costart TRUE +field SFBool coend FALSE +field SFBool meet FALSE +exposedField MFFloat priority [] #%b=[0,+I) #%q=3 +eventOut SFBool isActive +eventOut SFInt32 activeChild +]{ +} + +PROTO ServerCommand [#%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn SFBool trigger FALSE +exposedField SFBool enable FALSE +exposedField MFURL url [] +exposedField SFString command "" +]{ +} + + diff --git a/applications/generators/MPEG4/templates4.txt b/applications/generators/MPEG4/templates4.txt new file mode 100644 index 0000000..94458e8 --- /dev/null +++ b/applications/generators/MPEG4/templates4.txt @@ -0,0 +1,129 @@ +#-- Version 4 --# +# templates for the BIFS nodes +# ============================= +# Notations I = Infinity +# %q=x Quantization method x +# 0 None +# 1 3D Position (SFVec3F) +# 2 2D Position (SFVec2F) +# 3 drawing Order +# 4 Color (SFColor) +# 5 Texture Coordinate +# 6 Angle (SFFloat 0-2PI) +# 7 Scale (SFVec2F or SFVec3F) +# 8 Interpolators keys +# 9 Normals +# 10 Rotations (SFRotation) +# 11 Object Size 3D (SFVec3F and SFFloat) +# 12 Object Size 2D +# 13 Linear Quantization (+ Nb Bits) +# 14 Index (of IndexedFaceSet,...) +# 15 Reserved +# +# %a=y Animation method for fields that can be animated +# +## OO 081498 To match BIFS's update numbering +# 0 None +# 1 Position 3D +# 2 Position 2D +# 4 Color +# 6 Angle +# 7 Float +# 8 BoundFloat (intensities, transparencies,...) +# 9 Normal +# 10 Rotation +# 11 Size 3D +# 12 Size 2D +# 13 Integer +# 14 Reserved +## 0 3D Position +## 1 2D positon +## 2 Color (SFColor) +## 3 Angle (SFFloat 0-2pi) +## 4 Normals +## 5 Scale (SFVec2F) +## 6 Rotation (SFRotation) +## 7 Object Size or Scalar (SFFloat) +# +# %b=[min,max] bounds of value +# For each scalar or vectorial value, bounds may be specified. +# This will be used to check if user-specified values are out of bounds. In +# this case, bounds specified in the templates will be used (if not infinity). +# +# %NDT=Node Data Type +# For each node, one or several Node Data Types are assigned, specifying which node sub +# types the node belongs to. Moreover, each field of type SF/MF3DNode is re assigned +# a unique correct NodeDataType according to specify the allowed values of the field +# +# %COD Type of encoding +# N Normal Syntax : The node syntax follos the generic syntax for nodes +# S Special Syntax : The node has a specific syntax +# +# +# NCT => VRML type equivalence +# +# SF/MFxxxNode => SF/MFNode +# SF/MFURL => SF/MFString +# SF/MFCommandBuffer => SF/MFString +# SF/MFScript => SF/MFString +# +# +# Modification History +# ------------------------------------------------ +# Jan 29, 2000 created for v4 nodes (called group 4 now) +# +# Aug 21, 2001 Changed SFCommandBuffer from SFString buffer in InputSensor +# SFURL -> MFURL in MediaControl + +PROTO InputSensor [#%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +exposedField SFBool enabled TRUE +exposedField SFCommandBuffer buffer "" +exposedField MFURL url "" +eventOut SFTime eventTime +]{ +} + +PROTO MatteTexture [#%NDT=SFWorldNode,SFTextureNode,SF2DNode,SF3DNode %COD=N +field SFTextureNode surfaceA NULL +field SFTextureNode surfaceB NULL +field SFTextureNode alphaSurface NULL +exposedField SFString operation "" +field SFBool overwrite FALSE +exposedField SFFloat fraction 0 +exposedField MFFloat parameter 0 +]{ +} + +PROTO MediaBuffer [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +exposedField SFFloat bufferSize 0.0 #%b=[0,+I) +exposedField MFURL url [] +exposedField SFTime mediaStartTime -1 #%b=(-I,+I) +exposedField SFTime mediaStopTime +I #%b=(-I,+I) +eventOut SFBool isBuffered +exposedField SFBool enabled TRUE +] { +} + +PROTO MediaControl [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +exposedField MFURL url [] +exposedField SFTime mediaStartTime -1 #%b=(-I,+I) +exposedField SFTime mediaStopTime +I #%b=(-I,+I) +exposedField SFFloat mediaSpeed 1.0 #%b=(-I,+I) +exposedField SFBool loop FALSE +exposedField SFBool preRoll TRUE +exposedField SFBool mute FALSE +exposedField SFBool enabled TRUE +eventOut SFBool isPreRolled +] { +} + +PROTO MediaSensor [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +exposedField MFURL url [] +eventOut SFTime mediaCurrentTime +eventOut SFTime streamObjectStartTime +eventOut SFTime mediaDuration +eventOut SFBool isActive +eventOut MFString info +] { +} + diff --git a/applications/generators/MPEG4/templates5.txt b/applications/generators/MPEG4/templates5.txt new file mode 100644 index 0000000..21bbdd2 --- /dev/null +++ b/applications/generators/MPEG4/templates5.txt @@ -0,0 +1,561 @@ +#-- Version 5 --# +# templates for the BIFS nodes +# ============================= +# Notations I = Infinity +# %q=x Quantization method x +# 0 None +# 1 3D Position (SFVec3F) +# 2 2D Position (SFVec2F) +# 3 drawing Order +# 4 Color (SFColor) +# 5 Texture Coordinate +# 6 Angle (SFFloat 0-2PI) +# 7 Scale (SFVec2F or SFVec3F) +# 8 Interpolators keys +# 9 Normals +# 10 Rotations (SFRotation) +# 11 Object Size 3D (SFVec3F and SFFloat) +# 12 Object Size 2D +# 13 Linear Quantization (+ Nb Bits) +# 14 Index (of IndexedFaceSet,...) +# 15 SFVec4f +# 16 Reserved +# +# %a=y Animation method for fields that can be animated +# +## OO 081498 To match BIFS's update numbering +# 0 None +# 1 Position 3D +# 2 Position 2D +# 4 Color +# 6 Angle +# 7 Float +# 8 BoundFloat (intensities, transparencies,...) +# 9 Normal +# 10 Rotation +# 11 Size 3D +# 12 Size 2D +# 13 Integer +# 14 Reserved +## 0 3D Position +## 1 2D positon +## 2 Color (SFColor) +## 3 Angle (SFFloat 0-2pi) +## 4 Normals +## 5 Scale (SFVec2F) +## 6 Rotation (SFRotation) +## 7 Object Size or Scalar (SFFloat) +# +# %b=[min,max] bounds of value +# For each scalar or vectorial value, bounds may be specified. +# This will be used to check if user-specified values are out of bounds. In +# this case, bounds specified in the templates will be used (if not infinity). +# +# %NDT=Node Data Type +# For each node, one or several Node Data Types are assigned, specifying which node sub +# types the node belongs to. Moreover, each field of type SF/MF3DNode is re assigned +# a unique correct NodeDataType according to specify the allowed values of the field +# +# %COD Type of encoding +# N Normal Syntax : The node syntax follos the generic syntax for nodes +# S Special Syntax : The node has a specific syntax +# +# +# NCT => VRML type equivalence +# +# SF/MFxxxNode => SF/MFNode +# SF/MFURL => SF/MFString +# SF/MFCommandBuffer => SF/MFString +# SF/MFScript => SF/MFString +# +# +# Modification History +# ------------------------------------------------ +# March 18, 2004 [MBS] According to 68th meeting resolutions, removed Light-Field Mapping, Solid modeling and Particle systems related nodes. +# This includes the nodes: +# - +# Aug. 1, 2003 [MBS] According to 65th meeting resolutions, removed Multi-User Worlds nodes MUxxx +# Dec. 19, 2002 [MBS] According to 63rd meeting resolutions, removed Synthetic textures nodes: ColorProfile, SynthesizedTextureXXX, GradientLinear,GradientRadial,Ellipse +# Dec. 8, 2002 [MBS,MH] OctreeImage.image changed from MFTextureNode to MFDepthImageNode. DepthImage now also belongs to SFDepthImageNode context. +# Nov. 6, 2002 [MBS] modified nodes according to study of FPDAM (w5285). +# Oct 22, 2002 [MBS] added GradientRadial.Transform field which was missing but was in the spec. +# Aug 31, 2002 [MBS] +# changed SynthesizedTextureCurve.separatingFlags to MFInt32 since MFBool doesn't exist in VRML and bounds to 0 and 1 on 1 bit. +# For profileType, changed quantizer to 13 1 instead of 13 2 since values are 0 or 1 +# Aug 7, 2002 [MBS] aligned nodes to FDPAM +# May 9, 2002 [AFX] added quantizers, syntax check with PDAM +# January 28, 2002 [MBS] added rest of AFX nodes, alpha order everything +# January 6, 2002 [MBS] added AFX nodes +# December 7, 2001 [MBS, IG] created for AMD4 (v5) nodes + +# +# AFX nodes +# + +PROTO BitWrapper [ #%NDT=SFWorldNode,SF3DNode,SF2DNode,SFGeometryNode %COD=N +field SFWorldNode node NULL +field SFInt32 type 0 +field MFURL url [] +field SFString buffer "" +]{} + +PROTO CoordinateInterpolator4D [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn SFFloat set_fraction +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFVec4f keyValue [] #%b=(-I,+I) #%q=15 +eventOut MFVec4f value_changed +] { +} + +PROTO DepthImage [ #%NDT=SFWorldNode,SF3DNode,SFDepthImageNode %COD=N +field SFDepthTextureNode diTexture NULL +field SFFloat farPlane 100 #%b=[0,+I] +field SFVec2f fieldOfView 0.785398 0.785398 #%b=[0,3.1415927] +field SFFloat nearPlane 10 #%b=[0,+I] +field SFRotation orientation 0 0 1 0 +field SFBool orthographic TRUE +field SFVec3f position 0 0 10 #%b=[-I,+I] +]{} + +PROTO FFD [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField MFVec4f controlPoint [] #%b=[-I,+I] #%q=15 #%a=15 +field SFInt32 uDimension 2 #%b=[2,257] #%q=13 8 +field MFFloat uKnot [] #%b=[-I,+I] +field SFInt32 uOrder 2 #%b=[2,33] #%q=13 5 +field SFInt32 vDimension 2 #%b=[2,257] #%q=13 8 +field MFFloat vKnot [] #%b=[-I,+I] +field SFInt32 vOrder 2 #%b=[2,33] #%q=13 5 +field SFInt32 wDimension 2 #%b=[2,257] #%q=13 8 +field MFFloat wKnot [] #%b=[-I,+I] +field SFInt32 wOrder 2 #%b=[2,33] #%q=13 5 +]{} + +PROTO Implicit [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFVec3f bboxSize 2 2 2 #%b=[0,+I] #%q=11 #%a=11 +exposedField MFFloat c [] #%b=[-I,+I] #%q=0 #%a=7 +exposedField MFInt32 densities [] #%b=[0,+I] +exposedField SFBool dual FALSE +exposedField SFBool solid FALSE +]{} + +# renamed as XX for deletion +PROTO XXLFM_Appearance [ #%NDT=SFWorldNode,SFAppearanceNode %COD=N +exposedField SFBlendListNode blendList NULL +exposedField MFLightMapNode lightMapList [] +exposedField MFTextureNode tileList [] +exposedField SFFrameListNode vertexFrameList NULL +]{} + +# renamed as XX for deletion +PROTO XXLFM_BlendList [ #%NDT=SFWorldNode,SFBlendListNode %COD=N +exposedField MFInt32 blendMode [] #%b=[0,1] #%q=13 1 +exposedField MFInt32 lightMapIndex [] #%q=14 +]{} + +# renamed as XX for deletion +PROTO XXLFM_FrameList [ #%NDT=SFWorldNode,SFFrameListNode %COD=N +exposedField MFInt32 index [ -1 ] #%q=14 +exposedField MFVec3f frame [ 1 0 0, 0 1 0, 0 0 1 ] #%b=[-1,1] #%q=1 +]{} + +# renamed as XX for deletion +PROTO XXLFM_LightMap [ #%NDT=SFWorldNode,SFLightMapNode %COD=N +exposedField SFVec3f biasRGB 0 0 0 #%b=[-1,1] #%q=7 +exposedField SFInt32 priorityLevel 0 #%b=[0,255] #%q=13 8 +exposedField SFVec3f scaleRGB 1 1 1 #%b=[-1,1] #%q=7 +exposedField SFSurfaceMapNode surfaceMapList NULL +exposedField SFViewMapNode viewMapList NULL +]{} + +# renamed as XX for deletion +PROTO XXLFM_SurfaceMapList [ #%NDT=SFWorldNode,SFSurfaceMapNode %COD=N +exposedField MFInt32 tileIndex [] #%q=14 +exposedField SFTextureCoordinateNode triangleCoordinate NULL +exposedField MFInt32 triangleIndex [] #%q=14 +exposedField MFInt32 viewMapIndex [] #%q=14 +]{} + +# renamed as XX for deletion +PROTO XXLFM_ViewMapList [ #%NDT=SFWorldNode,SFViewMapNode %COD=N +exposedField SFTextureCoordinateNode textureOrigin NULL +exposedField SFTextureCoordinateNode textureSize NULL +exposedField MFInt32 tileIndex [] #%q=14 +exposedField MFInt32 vertexIndex [] #%q=14 +]{} + +PROTO MeshGrid [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn MFInt32 set_colorIndex +eventIn MFInt32 set_coordIndex +eventIn MFInt32 set_normalIndex +eventIn MFInt32 set_texCoordIndex +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField SFInt32 displayLevel 0 #%b=[0, +I] #%q=13 32 #%a=13 +exposedField SFInt32 filterType 0 #%b=[0, 1] #%q=13 2 #%a=13 +exposedField SFCoordinateNode gridCoord NULL +exposedField SFInt32 hierarchicalLevel 0 #%b=[-1, +I] #%q=13 32 #%a=13 +exposedField MFInt32 nLevels [] #%q=7 #%a=7 +exposedField SFNormalNode normal NULL +exposedField MFInt32 nSlices [] #%q=7 #%a=7 +exposedField SFTextureCoordinateNode texCoord NULL +exposedField MFFloat vertexOffset [] #%b=[0.0, 2.0] #%q=7 #%a=7 +exposedField MFInt32 vertexLink [] #%b=[0, 3] #%q=13 2 +field MFInt32 colorIndex [] #%b=[-1, +I] #%q=14 +field MFInt32 coordIndex [] #%b=[-1, +I] #%q=14 +field MFInt32 normalIndex [] #%b=[-1, +I] #%q=14 +field SFBool solid TRUE +field MFInt32 texCoordIndex [] #%b=[-1, +I] #%q=14 +eventOut SFBool isLoading +eventOut MFInt32 nVertices +]{} + +PROTO NonLinearDeformer [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFVec3f axis 0 0 1 #%b=[0,1] +exposedField MFFloat extend [] +exposedField SFGeometryNode geometry NULL +exposedField SFFloat param 0 +exposedField SFInt32 type 0 #%b=[0,2] +]{} + +PROTO NurbsCurve [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn MFInt32 set_colorIndex +exposedField SFColorNode color NULL +exposedField MFVec4f controlPoint [] #%b=[-I,+I] #%q=15 #%a=15 +exposedField SFInt32 tessellation 0 #%b=[0,+I] +field MFInt32 colorIndex [] #%q=14 +field SFBool colorPerVertex TRUE +field MFFloat knot [] #%b=[-I,+I] +field SFInt32 order 4 #%b=[3,34] #%q=13 5 +]{} + +PROTO NurbsCurve2D [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn MFInt32 set_colorIndex +exposedField SFColorNode color NULL +exposedField MFVec3f controlPoint [] #%b=[-I,+I] #%q=2 #%a=2 +exposedField SFInt32 tessellation 0 #%b=[0,+I] +field MFInt32 colorIndex [] #%q=14 +field SFBool colorPerVertex TRUE +field MFFloat knot [] #%b=[-I,+I] +field SFInt32 order 4 #%b=[3,34] #%q=13 5 +]{} + +PROTO NurbsSurface [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +eventIn MFInt32 set_colorIndex +eventIn MFInt32 set_texColorIndex +exposedField SFColorNode color NULL +exposedField MFVec4f controlPoint [] #%b=[-I,+I] #%q=15 #%a=15 +exposedField SFTextureCoordinateNode texCoord NULL +exposedField SFInt32 uTessellation 0 #%b=[0,+I] +exposedField SFInt32 vTessellation 0 #%b=[0,+I] +field SFBool ccw TRUE +field MFInt32 colorIndex [] #%q=14 +field SFBool colorPerVertex TRUE +field SFBool solid TRUE +field MFInt32 texColorIndex [] #%q=14 +field SFInt32 uDimension 4 #%b=[3,258] #%q=13 8 +field MFFloat uKnot [] #%b=[-I,+I] +field SFInt32 uOrder 4 #%b=[3,34] #%q=13 5 +field SFInt32 vDimension 4 #%b=[3,258] #%q=13 8 +field MFFloat vKnot [] #%b=[-I,+I] +field SFInt32 vOrder 4 #%b=[3,34] #%q=13 5 +]{} + +PROTO OctreeImage [ #%NDT=SFWorldNode,SF3DNode %COD=N +field MFDepthImageNode images [] +field MFInt32 octree [] #%b=[0,255] #%q=13 8 +field SFInt32 octreeResolution 256 #%b=[1,+I] +field MFInt32 voxelImageIndex [] #%b=[0,255] #%q=13 8 +]{} + +# renamed as XX for deletion +PROTO XXParticles [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFFloat creationRate 500 +exposedField SFFloat creationRateVariation 0 +exposedField SFFloat emitAlpha 1.0 +exposedField SFColor emitColor 1 1 1 +exposedField SFColor emitColorVariation 0 0 0 +exposedField SFVec3f emitterPosition 0 3 0 +exposedField SFVec3f emitVelocity 0 0 0 +exposedField SFVec3f emitVelocityVariation 1 1 1 +exposedField SFBool enabled TRUE +exposedField SFFloat fadeAlpha 1.0 +exposedField SFColor fadeColor 0.25 0.25 0.25 +exposedField SFFloat fadeRate 0.25 +exposedField SFVec3f force 0 -9.8 0 +exposedField MFInfluenceNode influences [] +exposedField SFWorldNode init NULL +exposedField SFTime maxLifeTime 5 +exposedField SFFloat maxLifeTimeVariation 0 +exposedField SFInt32 maxParticles 500 +exposedField SFFloat minRange 1 +exposedField SFFloat maxRange -1 +exposedField SFWorldNode primitive NULL +exposedField SFInt32 primitiveType 2 +exposedField SFFloat particleRadius 0.1 +exposedField SFFloat particleRadiusRate 0 +exposedField SFFloat particleRadiusVariation 0 +]{} + +# renamed as XX for deletion +PROTO XXParticleInitBox [ #%NDT=SFWorldNode,SFParticleInitializerNode %COD=N +exposedField SFFloat falloff 0 +exposedField SFVec3f size 1 1 1 +]{} + +# renamed as XX for deletion +PROTO XXPlanarObstacle [ #%NDT=SFWorldNode,SFInfluenceNode %COD=N +exposedField SFVec3f distance 0 0 0 +exposedField SFVec3f normal 0 1 0 +exposedField SFFloat reflection 0 +exposedField SFFloat absorption 0 +]{} + +# renamed as XX for deletion +PROTO XXPointAttractor [ #%NDT=SFWorldNode,SFInfluenceNode %COD=N +exposedField SFFloat innerRadius 10 +exposedField SFFloat outerRadius 100 +exposedField SFVec3f position 0 0 0 +exposedField SFFloat rate 1 +]{} + +PROTO PointTexture [ #%NDT=SFWorldNode,SFDepthTextureNode %COD=N +field MFColor color [] +field MFInt32 depth [] #%b=[0,+I] +field SFInt32 depthNbBits 7 #%b=[0,31] #%q=13 5 +field SFInt32 height 256 #%b=[1,+I] +field SFInt32 width 256 #%b=[1,+I] +]{} + +PROTO PositionAnimator [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn SFFloat set_fraction +exposedField SFVec2f fromTo 0 1 #%q=8 +exposedField MFFloat key [] #%q=8 +exposedField MFRotation keyOrientation [] +exposedField SFInt32 keyType 0 +exposedField MFVec2f keySpline [0 0, 1 1 ] #%q=8 +exposedField MFVec3f keyValue [] #%q=4 +exposedField SFInt32 keyValueType 0 +exposedField SFVec3f offset 0 0 0 #%b=[-I,+I] #%q=1 +exposedField MFFloat weight [] #%b=[-1,1] +eventOut SFVec3f endValue +eventOut SFRotation rotation_changed +eventOut SFVec3f value_changed +]{} + +PROTO PositionAnimator2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn SFFloat set_fraction +exposedField SFVec2f fromTo 0 1 #%q=8 +exposedField MFFloat key [] #%q=8 +exposedField SFInt32 keyOrientation 0 +exposedField SFInt32 keyType 0 +exposedField MFVec2f keySpline [0 0, 1 1 ] #%q=8 +exposedField MFVec2f keyValue [] #%q=4 +exposedField SFInt32 keyValueType 0 +exposedField SFVec2f offset 0 0 #%b=[-I,+I] #%q=2 +exposedField MFFloat weight [] #%b=[-1,1] +eventOut SFVec2f endValue +eventOut SFFloat rotation_changed +eventOut SFVec2f value_changed +]{} + +PROTO PositionInterpolator4D [ #%NDT=SFWorldNode,SF3DNode %COD=N +eventIn SFFloat set_fraction +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFVec4f keyValue [] #%b=(-I,+I) #%q=15 +eventOut SFVec4f value_changed +] { +} + +PROTO ProceduralTexture [ #%NDT=SFWorldNode,SFTextureNode %COD=N +exposedField SFBool aSmooth FALSE +exposedField MFVec2f aWarpmap [0 0, 1 1] #%b=[0,1] #%q=2 +exposedField MFFloat aWeights [0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0] #%b=[-1,1] +exposedField SFBool bSmooth FALSE +exposedField MFVec2f bWarpmap [0 0, 1 1] #%b=[0, 1] #%q=2 +exposedField MFFloat bWeights [0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0] #%b=[-1,1] +exposedField SFInt32 cellWidth 4 #%b=[0,15] #%q=13 4 +exposedField SFInt32 cellHeight 4 #%b=[0,15] #%q=13 4 +exposedField MFColor color [0.3 0.698 1, 0.8 0.8 0.8, 1 1 1, 0 0 0] #%q=4 +exposedField SFFloat distortion 0 #%b=[0,1] #%q=13 16 +exposedField SFInt32 height 7 #%b=[1,15] #%q=13 4 +exposedField SFInt32 roughness 0 #%b=[0,15] #%q=13 4 +exposedField SFInt32 seed 129093 #%b=[-I,+I] +exposedField SFInt32 type 0 #%b=[0,4] #%q=13 3 +exposedField SFBool xSmooth FALSE +exposedField MFVec2f xWarpmap [] #%b=[0,1] #%q=2 +exposedField SFBool ySmooth FALSE +exposedField MFVec2f yWarpmap [] #%b=[0,1] #%q=2 +exposedField SFInt32 width 7 #%b=[1,15] #%q=13 4 +eventOut SFImage image_changed +]{} + +PROTO Quadric [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFVec3f bboxSize 2 2 2 #%b=[0,+I] #%q=11 #%a=11 +exposedField MFInt32 densities [] #%b=[0,+I] +exposedField SFBool dual FALSE +exposedField SFVec4f P0 -1 0 0 1 #%b=[-I,+I] #%q=15 #%a=15 +exposedField SFVec4f P1 1 0 0 1 #%b=[-I,+I] #%q=15 #%a=15 +exposedField SFVec4f P2 0 1 0 0 #%b=[-I,+I] #%q=15 #%a=15 +exposedField SFVec4f P3 0 0 1 0 #%b=[-I,+I] #%q=15 #%a=15 +exposedField SFVec4f P4 0 1 0 1 #%b=[-I,+I] #%q=15 #%a=15 +exposedField SFVec4f P5 0 0 1 1 #%b=[-I,+I] #%q=15 #%a=15 +exposedField SFBool solid FALSE +]{} + +PROTO SBBone [ #%NDT=SFWorldNode,SF2DNode,SF3DNode,SFSBBoneNode %COD=N +eventIn SF3DNode addChildren +eventIn SF3DNode removeChildren +exposedField SFInt32 boneID 0 #%b=[0,1023] #%q=13 10 +exposedField SFVec3f center 0 0 0 #%q=1 #%a=1 +exposedField MF3DNode children [] +exposedField SFVec3f endpoint 0 0 1 #%q=1 #%a=1 +exposedField SFInt32 falloff 1 #%b=[-1,4] #%q=13 3 +exposedField SFInt32 ikChainPosition 0 #%b=[0,3] #%q=13 2 +exposedField MFFloat ikPitchLimit [] +exposedField MFFloat ikRollLimit [] +exposedField MFFloat ikTxLimit [] +exposedField MFFloat ikTyLimit [] +exposedField MFFloat ikTzLimit [] +exposedField MFFloat ikYawLimit [] +exposedField SFRotation rotation 0 0 1 0 #%q=10 #%a=10 +exposedField SFInt32 rotationOrder 0 #%b=[0,23] #%q=13 5 +exposedField SFVec3f scale 0 0 0 #%q=7 #%a=11 +exposedField SFRotation scaleOrientation 0 0 1 0 #%q=10 #%a=10 +exposedField MFFloat sectionInner [] +exposedField MFFloat sectionOuter [] +exposedField MFFloat sectionPosition [] +exposedField MFInt32 skinCoordIndex [] #%q=14 +exposedField MFFloat skinCoordWeight [] #%b=[-1,1] +exposedField SFVec3f translation 0 0 0 #%q=1 #%a=1 +]{} + +PROTO SBMuscle [ #%NDT=SFWorldNode,SF2DNode,SF3DNode,SFSBMuscleNode %COD=N +exposedField SFInt32 falloff 1 #%b=[-1,4] #%q=13 3 +exposedField SFGeometryNode muscleCurve NULL +exposedField SFInt32 muscleID 0 #%b=[0,1023] #%q=13 10 +exposedField SFInt32 radius 1 #%q=7 #%a=11 +exposedField MFInt32 skinCoordIndex [] #%b=[0,+I] #%q=14 +exposedField MFFloat skinCoordWeight [] #%b=[-1,1] +]{} + +PROTO SBSegment [ #%NDT=SFWorldNode,SF2DNode,SF3DNode,SFSBSegmentNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField SFVec3f centerOfMass 0 0 0 #%q=1 #%a=1 +exposedField MF3DNode children [] +exposedField SFFloat mass 0 +exposedField MFVec3f momentsOfInertia [ 0 0 0, 0 0 0, 0 0 0 ] +exposedField SFString name "" +]{} + +PROTO SBSite [ #%NDT=SFWorldNode,SF2DNode,SF3DNode,SFSBSiteNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField SFVec3f center 0 0 0 #%q=1 #%a=1 +exposedField MF3DNode children [] +exposedField SFString name "" +exposedField SFRotation rotation 0 0 1 0 #%q=10 #%a=10 +exposedField SFVec3f scale 1 1 1 #%q=7 #%a=11 +exposedField SFRotation scaleOrientation 0 0 1 0 #%q=10 #%a=10 +exposedField SFVec3f translation 0 0 0 #%q=1 #%a=1 +]{} + +PROTO SBSkinnedModel [#%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +exposedField MFSBBoneNode bones [] +exposedField SFVec3f center 0 0 0 #%q=1 #%a=1 +exposedField MFSBMuscleNode muscles [] +exposedField SFString name "" +exposedField SFRotation rotation 0 0 1 0 #%q=10 #%a=10 +exposedField MFSBSegmentNode segments [] +exposedField SFVec3f scale 1 1 1 #%q=7 #%a=11 +exposedField SFRotation scaleOrientation 0 0 1 0 #%q=10 #%a=10 +exposedField MFSBSiteNode sites [] +exposedField MF3DNode skeleton [] +exposedField MF3DNode skin [] +exposedField SFCoordinateNode skinCoord NULL +exposedField SFNormalNode skinNormal NULL +exposedField SFVec3f translation 0 0 0 #%q=1 #%a=1 +exposedField SF3DNode weighsComputationSkinCoord NULL +]{} + +PROTO SBVCAnimation [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +exposedField MFURL url [] +exposedField MF3DNode virtualCharacters [] +]{} + +PROTO ScalarAnimator [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N +eventIn SFFloat set_fraction +exposedField SFVec2f fromTo 0 1 #%q=8 +exposedField MFFloat key [] #%q=8 +exposedField SFInt32 keyType 0 +exposedField MFVec2f keySpline [0 0, 1 1 ] #%q=8 +exposedField MFFloat keyValue [] #%q=0 +exposedField SFInt32 keyValueType 0 +exposedField SFFloat offset 0 +exposedField MFFloat weight [] #%b=[-1,1] +eventOut SFFloat endValue +eventOut SFFloat value_changed +]{} + +PROTO SimpleTexture [ #%NDT=SFWorldNode,SFDepthTextureNode %COD=N +field SFTextureNode depth NULL +field SFTextureNode texture NULL +]{} + +PROTO SolidRep [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFVec3f bboxSize 2 2 2 #%b=[0,+I] #%q=11 #%a=11 +exposedField MFInt32 densityList [] #%b=[0,+I] +exposedField SF3DNode solidTree NULL +]{} + +PROTO SubdivisionSurface [ #%NDT=SFWorldNode,SFGeometryNode,SFBaseMeshNode %COD=N +eventIn MFInt32 set_colorIndex +eventIn MFInt32 set_coordIndex +eventIn MFInt32 set_cornerVertexIndex +eventIn MFInt32 set_creaseEdgeIndex +eventIn MFInt32 set_creaseVertexIndex +eventIn MFInt32 set_dartVertexIndex +eventIn MFInt32 set_texCoordIndex +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField SFTextureCoordinateNode texCoord NULL +exposedField MFSubdivSurfaceSectorNode sectors [] +exposedField SFInt32 subdivisionLevel 0 #%b=[-1,+I] +exposedField SFInt32 subdivisionType 0 #%b=[0,3] #%q=13 2 +exposedField SFInt32 subdivisionSubType 0 #%b=[0,3] #%q=13 2 +field MFInt32 invisibleEdgeIndex [] #%b=[0,+I] +field SFBool ccw TRUE +field MFInt32 colorIndex [] #%b=[-1,+I] +field SFBool colorPerVertex TRUE +field SFBool convex TRUE +field MFInt32 coordIndex [] #%b=[-1,+I] +field MFInt32 cornerVertexIndex [] #%b=[-1,+I] +field MFInt32 creaseEdgeIndex [] #%b=[-1,+I] +field MFInt32 creaseVertexIndex [] #%b=[-1,+I] +field MFInt32 dartVertexIndex [] #%b=[-1,+I] +field SFBool solid TRUE +field MFInt32 texCoordIndex [] #%b=[-1,+I] +]{} + +PROTO SubdivSurfaceSector [ #%NDT=SFWorldNode,SFSubdivSurfaceSectorNode %COD=N +exposedField SFFloat flatness 0 #%b=[0,1] #%q=7 +exposedField SFVec3f normal 0 0 0 #%q=9 +exposedField SFFloat normalTension 0 #%b=[0,1] #%q=7 +exposedField SFInt32 tag 0 #%b=[0,2] #%q=13 2 +exposedField SFFloat theta 0 #%b=[0,6.2831853] #%q=6 +field SFInt32 faceIndex 0 #%q=14 +field SFInt32 vertexIndex 0 #%q=14 +]{} + +PROTO WaveletSubdivisionSurface [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFGeometryNode baseMesh NULL +exposedField SFFloat fieldOfView 0.785398 # pi/4 +exposedField SFFloat frequency 1.0 +exposedField SFInt32 quality 1 +]{} + + + diff --git a/applications/generators/MPEG4/templates6.txt b/applications/generators/MPEG4/templates6.txt new file mode 100644 index 0000000..ab82832 --- /dev/null +++ b/applications/generators/MPEG4/templates6.txt @@ -0,0 +1,242 @@ +#-- Version 6 --# +#-- Beta Advanced Text Graphics --# +# templates for the BIFS nodes +# ============================= +# Notations I = Infinity +# %q=x Quantization method x +# 0 None +# 1 3D Position (SFVec3F) +# 2 2D Position (SFVec2F) +# 3 drawing Order +# 4 Color (SFColor) +# 5 Texture Coordinate +# 6 Angle (SFFloat 0-2PI) +# 7 Scale (SFVec2F or SFVec3F) +# 8 Interpolators keys +# 9 Normals +# 10 Rotations (SFRotation) +# 11 Object Size 3D (SFVec3F and SFFloat) +# 12 Object Size 2D +# 13 Linear Quantization (+ Nb Bits) +# 14 Index (of IndexedFaceSet,...) +# 15 Reserved +# +# %a=y Animation method for fields that can be animated +# +## OO 081498 To match BIFS's update numbering +# 0 None +# 1 Position 3D +# 2 Position 2D +# 4 Color +# 6 Angle +# 7 Float +# 8 BoundFloat (intensities, transparencies,...) +# 9 Normal +# 10 Rotation +# 11 Size 3D +# 12 Size 2D +# 13 Integer +# 14 Reserved +## 0 3D Position +## 1 2D positon +## 2 Color (SFColor) +## 3 Angle (SFFloat 0-2pi) +## 4 Normals +## 5 Scale (SFVec2F) +## 6 Rotation (SFRotation) +## 7 Object Size or Scalar (SFFloat) +# +# %b=[min,max] bounds of value +# For each scalar or vectorial value, bounds may be specified. +# This will be used to check if user-specified values are out of bounds. In +# this case, bounds specified in the templates will be used (if not infinity). +# +# %NDT=Node Data Type +# For each node, one or several Node Data Types are assigned, specifying which node sub +# types the node belongs to. Moreover, each field of type SF/MF3DNode is re assigned +# a unique correct NodeDataType according to specify the allowed values of the field +# +# %COD Type of encoding +# N Normal Syntax : The node syntax follos the generic syntax for nodes +# S Special Syntax : The node has a specific syntax +# +# +# NCT => VRML type equivalence +# +# SF/MFxxxNode => SF/MFNode +# SF/MFURL => SF/MFString +# SF/MFCommandBuffer => SF/MFString +# SF/MFScript => SF/MFString +# +# +# Modification History +# ------------------------------------------------ +# July 31, 2003 [CC, ENST] updated to FPDAM2 (w5774) +# April 28, 2003 [JLF, ENST] updated to PDAM (w5645) +# January 9, 2003 [JLF, ENST] updated to WD3.0 (w5475) +# September 17, 2002 [JLF, ENST] created for AdvancedText & Graphics WD 2.0 +#NB: XFontStyle.feature* MFInt32 fields should have some quantization type ? + +PROTO Clipper2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn MF2DNode addChildren +eventIn MF2DNode removeChildren +exposedField MF2DNode children [] +exposedField SFGeometryNode geometry NULL +exposedField SFBool inside TRUE +exposedField SF2DNode transform NULL +exposedField SFBool XOR FALSE +] { +} + +PROTO ColorTransform [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFFloat mrr 1 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mrg 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mrb 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mra 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat tr 0 #%b=(-I, +I) #%q=4 #%a=7 +exposedField SFFloat mgr 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mgg 1 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mgb 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mga 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat tg 0 #%b=(-I, +I) #%q=4 #%a=7 +exposedField SFFloat mbr 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mbg 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mbb 1 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mba 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat tb 0 #%b=(-I, +I) #%q=4 #%a=7 +exposedField SFFloat mar 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mag 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mab 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat maa 1 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat ta 0 #%b=(-I, +I) #%q=4 #%a=7 +] { +} + +PROTO Ellipse [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFVec2f radius 1 1 #%b=[0,+I) #%q=12 #%a=2 +]{} + +PROTO LinearGradient [ #%NDT=SFWorldNode,SFTextureNode %COD=N +exposedField SFVec2f endPoint 1 0 #%b=(-I, +I) #%q=5 #%a=2 +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFColor keyValue [] #%b=[0,1] #%q=4 +exposedField MFFloat opacity [1] #%b=[0,1] #%q=7 +exposedField SFInt32 spreadMethod 0 #%b=[0,2] #%q=13 2 +exposedField SFVec2f startPoint 0 0 #%b=(-I, +I) #%q=5 #%a=2 +exposedField SF3DNode transform NULL +]{} + +PROTO PathLayout [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn MF2DNode addChildren +eventIn MF2DNode removeChildren +exposedField MF2DNode children [] +exposedField SFGeometryNode geometry NULL +exposedField MFInt32 alignment [0 0] #%b=[-1,1] #%q=13 2 +exposedField SFFloat pathOffset 0 #%b=[-I,I] #%q=7 #%a=7 +exposedField SFFloat spacing 1.0 #%b=[-I,I] #%q=7 #%a=7 +exposedField SFBool reverseLayout FALSE +exposedField SFInt32 wrapMode 0 #%b=[0,2] #%q=13 2 +exposedField SFBool splitText TRUE +] { +} + +PROTO RadialGradient [ #%NDT=SFWorldNode,SFTextureNode %COD=N +exposedField SFVec2f center 0.5 0.5 #%b=(-I, +I) #%q=5 #%a=2 +exposedField SFVec2f focalPoint 0 0 #%b=(-I, +I) #%q=5 #%a=2 +exposedField MFFloat key [] #%b=[0,1] #%q=8 +exposedField MFColor keyValue [] #%b=[0,1] #%q=4 +exposedField MFFloat opacity [1] #%b=[0,1] #%q=7 +exposedField SFFloat radius 0.5 #%b=[0,+I) #%q=12 #%a=7 +exposedField SFInt32 spreadMethod 0 #%b=[0,2] #%q=13 2 +exposedField SF3DNode transform NULL +]{} + +PROTO SynthesizedTexture [ #%NDT=SFWorldNode,SFTextureNode %COD=N +exposedField MFVec3f translation [] #%b=[-I,+I] #%q=1 #%a=1 +exposedField MFRotation rotation [] #%b=[-I,+I] #%q=10 #%a=10 +exposedField SFInt32 pixelWidth -1 #%b=[0,65535] #%q=13 16 +exposedField SFInt32 pixelHeight -1 #%b=[0,65535] #%q=13 16 +exposedField SFBool loop FALSE +exposedField SFFloat speed 1.0 #%b=(-I,+I) #%q=0 #%a=7 +exposedField SFTime startTime 0 #%b=(-I,+I) +exposedField SFTime stopTime 0 #%b=(-I,+I) +exposedField MFURL url [] +eventOut SFTime duration_changed +eventOut SFBool isActive +]{ +} + +PROTO TransformMatrix2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode,SFTextureTransformNode %COD=N +eventIn MF2DNode addChildren +eventIn MF2DNode removeChildren +exposedField MF2DNode children [] +exposedField SFFloat mxx 1 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat mxy 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat tx 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat myx 0 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat myy 1 #%b=(-I, +I) #%q=7 #%a=7 +exposedField SFFloat ty 0 #%b=(-I, +I) #%q=7 #%a=7 +] { +} + +PROTO Viewport [ #%NDT=SFWorldNode,SF3DNode,SF2DNode,SFViewportNode %COD=N +eventIn SFBool set_bind +exposedField SFVec2f position 0 0 #%b=(-I,+I) #%q=1 #%a=1 +exposedField SFVec2f size -1 -1 #%b=(-I,+I) #%q=12 #%a=12 +exposedField SFFloat orientation 0 #%b=[0,6.2831853] #%q=6 #%a=6 +exposedField MFInt32 alignment [0 0] #%b=[-1,1] #%q=13 3 +exposedField SFInt32 fit 0 #%b=[0,2] #%q=13 3 +field SFString description "" +eventOut SFTime bindTime +eventOut SFBool isBound + +] { +} + +PROTO XCurve2D [ #%NDT=SFWorldNode,SFGeometryNode %COD=N +exposedField SFCoordinate2DNode point [] +exposedField SFFloat fineness 0.5 #%b=[0,1] #%q=0 #%a=7 +exposedField MFInt32 type [] #%b=[0,15] #%q=13 4 +] { +} + +PROTO XFontStyle [ #%NDT=SFWorldNode,SFFontStyleNode %COD=N +exposedField MFString fontName ["SERIF"] +exposedField SFBool horizontal TRUE +exposedField MFString justify ["BEGIN"] +exposedField SFString language "" +exposedField SFBool leftToRight TRUE +exposedField SFFloat size 1.0 #%b=[0,+I) #%q=11 +exposedField SFString stretch "NORMAL" +exposedField SFFloat letterSpacing 0.0 #%b=[0,+I) #%q=11 +exposedField SFFloat wordSpacing 0.0 #%b=[0,+I) #%q=11 +exposedField SFInt32 weight 400 +exposedField SFBool fontKerning TRUE +exposedField SFString style "PLAIN" +exposedField SFBool topToBottom TRUE +exposedField MFString featureName [""] +exposedField MFInt32 featureStartOffset [] #%b=(-I, +I) +exposedField MFInt32 featureLength [] #%b=(-I, +I) +exposedField MFInt32 featureValue [] #%b=(-I, +I) +] { +} + +PROTO XLineProperties [ #%NDT=SFWorldNode,SFLinePropertiesNode %COD=N +exposedField SFColor lineColor 0 0 0 #%b=[0,1] #%q=4 #%a=4 +exposedField SFInt32 lineStyle 0 #%b=[0,5] #%q=13 3 +exposedField SFBool isCenterAligned TRUE +exposedField SFBool isScalable TRUE +exposedField SFInt32 lineCap 0 #%b=[0,2] #%q=13 3 +exposedField SFInt32 lineJoin 0 #%b=[0,2] #%q=13 3 +exposedField SFFloat miterLimit 4 #%b=[1,+I) #%q=12 +exposedField SFFloat transparency 0 #%b=[0,1] #%q=4 #%a=8 +exposedField SFFloat width 1 #%b=[0,+I) #%q=12 #%a=7 +exposedField SFFloat dashOffset 0 #%b=[0,+I) #%q=12 #%a=7 +exposedField MFFloat dashes [] #%b=[0,+I) #%q=12 #%a=7 +exposedField SFTextureNode texture NULL +exposedField SFTextureTransformNode textureTransform NULL +]{ +} diff --git a/applications/generators/MPEG4/templates7.txt b/applications/generators/MPEG4/templates7.txt new file mode 100644 index 0000000..55b4536 --- /dev/null +++ b/applications/generators/MPEG4/templates7.txt @@ -0,0 +1,235 @@ +#-- Version 7 --# +# +# Beta for AFX/AMD1 +# +# templates for the BIFS nodes +# ============================= +# Notations I = Infinity +# %q=x Quantization method x +# 0 None +# 1 3D Position (SFVec3F) +# 2 2D Position (SFVec2F) +# 3 drawing Order +# 4 Color (SFColor) +# 5 Texture Coordinate +# 6 Angle (SFFloat 0-2PI) +# 7 Scale (SFVec2F or SFVec3F) +# 8 Interpolators keys +# 9 Normals +# 10 Rotations (SFRotation) +# 11 Object Size 3D (SFVec3F and SFFloat) +# 12 Object Size 2D +# 13 Linear Quantization (+ Nb Bits) +# 14 Index (of IndexedFaceSet,...) +# 15 SFVec4f +# 16 Reserved +# +# %a=y Animation method for fields that can be animated +# +## OO 081498 To match BIFS's update numbering +# 0 None +# 1 Position 3D +# 2 Position 2D +# 4 Color +# 6 Angle +# 7 Float +# 8 BoundFloat (intensities, transparencies,...) +# 9 Normal +# 10 Rotation +# 11 Size 3D +# 12 Size 2D +# 13 Integer +# 14 Reserved +## 0 3D Position +## 1 2D positon +## 2 Color (SFColor) +## 3 Angle (SFFloat 0-2pi) +## 4 Normals +## 5 Scale (SFVec2F) +## 6 Rotation (SFRotation) +## 7 Object Size or Scalar (SFFloat) +# +# %b=[min,max] bounds of value +# For each scalar or vectorial value, bounds may be specified. +# This will be used to check if user-specified values are out of bounds. In +# this case, bounds specified in the templates will be used (if not infinity). +# +# %NDT=Node Data Type +# For each node, one or several Node Data Types are assigned, specifying which node sub +# types the node belongs to. Moreover, each field of type SF/MF3DNode is re assigned +# a unique correct NodeDataType according to specify the allowed values of the field +# +# %COD Type of encoding +# N Normal Syntax : The node syntax follos the generic syntax for nodes +# S Special Syntax : The node has a specific syntax +# +# +# NCT => VRML type equivalence +# +# SF/MFxxxNode => SF/MFNode +# SF/MFURL => SF/MFString +# SF/MFCommandBuffer => SF/MFString +# SF/MFScript => SF/MFString +# +# +# Modification History +# ------------------------------------------------ +# March 18, 2003 [MBS] According to 68th meeting resolutions, created for AFX/AMD1 nodes + +# +# AFX/AMD1 nodes +# + + +PROTO AdvancedAudioBuffer [ #%NDT=SFWorldNode,SFAudioNode %COD=N +eventIn MFAudioNode addChildren +eventIn MFAudioNode removeChildren +exposedField MFAudioNode children [] +exposedField SFBool loop FALSE +exposedField SFFloat pitch 1.0 #%b=[0,+I] #%q=0 #%a=7 +exposedField SFTime startTime 0 #%b=[0,+I] #%q=0 +exposedField SFTime stopTime 0 #%b=[0,+I] #%q=0 +exposedField SFTime startLoadTime 0 #%b=[0,+I] #%q=0 +exposedField SFTime stopLoadTime 0 #%b=[0,+I] #%q=0 +exposedField SFInt32 loadMode 0 #%b=[0,4] #%q=13 3 +exposedField SFInt32 numAccumulatedBlocks 0 #%b=[0,65535] #%q=13 16 +exposedField SFInt32 deleteBlock 0 #%b=[-65536, 0] #%q=13 17 +exposedField SFInt32 playBlock 0 #%b=[-65536, 0] #%q=13 17 +exposedField SFFloat length 0.0 #%b=[0,+I] #%q=0 +field SFInt32 numChan 1 #%b=[0,255] #%q=13 8 +field MFInt32 phaseGroup [] #%b=[0,255] #%q=13 8 +eventOut SFTime duration_changed +eventOut SFBool isActive +]{} + + +PROTO AudioChannelConfig [ #%NDT=SFWorldNode,SFAudioNode %COD=N +eventIn MFAudioNode addChildren +eventIn MFAudioNode removeChildren +exposedField MFAudioNode children [] +exposedField SFInt32 generalChannelFormat 0 #%b=[0,4] #%q=13 3 +exposedField SFInt32 fixedPreset 0 #%b=[0,15] #%q=13 4 +exposedField SFInt32 fixedPresetSubset 0 +exposedField SFInt32 fixedPresetAddInf 0 #%b=[0,2] #%q=13 3 +exposedField MFInt32 channelCoordinateSystems [] #%b=[0,6] #%q=13 3 +exposedField MFFloat channelSoundLocation [] #%b=[-I,+I] +exposedField MFInt32 channelDirectionalPattern [] #%b=[0,2] #%q=13 3 +exposedField MFVec3f channelDirection [] +exposedField SFInt32 ambResolution2D 1 #%b=[0,127] #%q=13 7 +exposedField SFInt32 ambResolution3D 0 #%b=[0,15] #%q=13 4 +exposedField SFInt32 ambEncodingConvention 0 #%b=[0,5] #%q=13 3 +exposedField SFFloat ambNfcReferenceDistance 1.5 #%b=[0,+I] +exposedField SFFloat ambSoundSpeed 340.0 #%b=[0,+I] +exposedField SFInt32 ambArrangementRule 0 #%b=[0,7] #%q=13 3 +exposedField SFInt32 ambRecombinationPreset 0 #%b=[0,15] #%q=13 4 +exposedField MFInt32 ambComponentIndex [] #%b=[0,255] #%q=13 8 +exposedField MFFloat ambBackwardMatrix [] #%b=[-I,+I] +exposedField MFInt32 ambSoundfieldResolution [] #%b=[0,127] #%q=13 7 +field SFInt32 numChannel 0 #%b=[0,255] #%q=13 8 +]{} + +PROTO DepthImageV2 [ #%NDT=SFWorldNode,SF3DNode,SFDepthImageNode %COD=N +field SFDepthTextureNode diTexture NULL +field SFFloat farPlane 100 #%b=[0,+I] +field SFVec2f fieldOfView 0.75 0.75 #%b=[0,3.1415927] +field SFFloat nearPlane 10 #%b=[0,+I] +field SFRotation orientation 0 0 1 0 +field SFBool orthographic TRUE +field SFVec3f position 0 0 10 #%b=[-I,+I] +field SFVec2f splatMinMax 0.115 0.975 #%b=[-I,+I] +]{} + + +PROTO MorphShape [ #%NDT=SFWorldNode,SF3DNode,SF2DNode, %COD=N + exposedField SF3DNode baseShape NULL + exposedField SFInt32 morphID 0 #%b=[0, 1023] #%q=13 7 + exposedField MF3DNode targetShapes [ ] + exposedField MFFloat weights [ ] +]{} + +PROTO MultiTexture [ #%NDT=SFWorldNode,SFTextureNode %COD=N + exposedField SFFloat alpha 1 #%b=[0,1] + exposedField SFColor color 1 1 1 #%b=[0,1] + exposedField MFInt32 function [] + exposedField MFInt32 mode [] + exposedField MFInt32 source [] + exposedField MFTextureNode texture [] + exposedField MFVec3f cameraVector [] + exposedField SFBool transparent FALSE +]{} + +PROTO PointTextureV2 [ #%NDT=SFWorldNode,SFDepthTextureNode %COD=N +field MFColor color [] +field MFInt32 depth [] #%b=[0,+I] +field SFInt32 depthNbBits 7 #%b=[0,31] #%q=13 5 +field SFInt32 height 256 #%b=[1,+I] +field SFNormalNode normal NULL #%b=[-I,+I] +field MFVec3f splatU [] #%b=[-I,+I] #%q=1 +field MFVec3f splatV [] #%b=[-I,+I] #%q=1 +field SFInt32 width 256 #%b=[1,+I] +]{} + + +PROTO SBVCAnimationV2 [ #%NDT=SFWorldNode,SF3DNode,SF2DNode %COD=N + exposedField MFInt32 activeUrlIndex [] + exposedField SFBool loop FALSE + exposedField SFFloat speed 1.0 #%b=(-I,+I) #%q=0 #%a=7 + exposedField SFTime startTime 0 #%b=(-I,+I) + exposedField SFTime stopTime 0 #%b=(-I,+I) + exposedField SFFloat transitionTime 0 #%b=[0,+I) #%q=0 #%a=7 + exposedField MFURL url [ ] + exposedField MF3DNode virtualCharacters [ ] + eventOut SFTime duration_changed + eventOut SFBool isActive +]{} + +PROTO SimpleTextureV2 [ #%NDT=SFWorldNode,SFDepthTextureNode %COD=N +field SFTextureNode depth NULL +field SFTextureNode normal NULL +field SFTextureNode splatU NULL +field SFTextureNode splatV NULL +field SFTextureNode texture NULL +]{} + +PROTO SurroundingSound [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFAudioNode source NULL +exposedField SFFloat intensity 1.0 #%b=[0,1] #%q=0 #%a=7 +exposedField SFFloat distance 0.0 #%b=[0,+I] #%q=0 +exposedField SFVec3f location 0.0 0.0 0.0 #%b=[-I,+I] #%q=1 #%a=1 +exposedField SFFloat distortionFactor 0.0 #%b=[-I,+I] +exposedField SFRotation orientation 0.0 0.0 1.0 0.0 #%b=[-I,+I] #%q=10 #%a=10 +exposedField SFBool isTransformable TRUE +]{} + + +PROTO Transform3DAudio [ #%NDT=SFWorldNode,SF2DNode,SF3DNode %COD=N +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFFloat thirdCenterCoordinate 0.0 #%b=[-I,+I] #%q=0 #%a=7 +exposedField SFVec3f rotationVector 0.0 0.0 1.0 #%b=[-3.14159265,3.14159265] #%q=7 #%a=11 +exposedField SFFloat thirdScaleCoordinate 0.0 #%b=[0,+I] #%q=0 #%a=7 +exposedField SFVec3f scaleOrientationVector 0.0 0.0 1.0 #%b=[-1,1] #%q=7 #%a=11 +exposedField SFFloat thirdTranslationCoordinate 0.0 #%b=[-I,+I] #%q=0 #%a=7 +exposedField SFRotation coordinateTransform 1.0 0.0 0.0 -1.5707963 #%b=[-3.14159265,3.14159265] #%q=10 +]{} + + +PROTO WideSound [ #%NDT=SFWorldNode,SF3DNode %COD=N +exposedField SFAudioNode source NULL +exposedField SFFloat intensity 1 #%b=[0,1] #%q=0 #%a=7 +exposedField SFVec3f location 0.0 0.0 0.0 #%b=[-I,+I] #%q=1 #%a=1 +exposedField SFBool spatialize TRUE +exposedField SFPerceptualParameterNode perceptualParameters NULL +exposedField SFBool roomEffect FALSE +exposedField SFInt32 shape 0 #%b=[0,4] #%q=13 4 +exposedField MFFloat size 0.0 #%b=[-I,+I] +exposedField SFVec3f direction 0.0 1.0 0.0 #%b=[-I,+I] #%q=9 #%a=9 +exposedField SFFloat density 0.5 #%b=[0,+I] +exposedField SFInt32 diffuseSelect 1 #%b=[0,+I] #%q=0 +exposedField SFFloat decorrStrength 1.0 #%b=[0,1] +field SFFloat speedOfSound 340.0 #%b=[0,+I] #%q=1 +field SFFloat distance 1000.0 #%b=[0,+I] #%q=0 +field SFBool useAirabs FALSE +]{} + diff --git a/applications/generators/Makefile b/applications/generators/Makefile new file mode 100644 index 0000000..820e257 --- /dev/null +++ b/applications/generators/Makefile @@ -0,0 +1,26 @@ +include ../../config.mak + +GENDIRS=MPEG4 X3D + +ifeq ($(HAS_LIBXML2), yes) +#sorry minGW friends, I can't get a stable libxml2 to compile ... +ifeq ($(CONFIG_WIN32), yes) +else +GENDIRS+=SVG +endif +endif + +all: sggen + +sggen: + set -e; for i in $(GENDIRS) ; do $(MAKE) -C $$i all; done + +dep: + set -e; for i in $(GENDIRS) ; do $(MAKE) -C $$i dep; done + +clean: + set -e; for i in $(GENDIRS) ; do $(MAKE) -C $$i clean; done + +distclean: + set -e; for i in $(GENDIRS) ; do $(MAKE) -C $$i distclean; done + diff --git a/applications/generators/SVG/Makefile b/applications/generators/SVG/Makefile new file mode 100644 index 0000000..a634751 --- /dev/null +++ b/applications/generators/SVG/Makefile @@ -0,0 +1,63 @@ +include ../../../config.mak + +vpath %.c $(SRC_PATH)/applications/generators/SVG + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= html.o laser.o main.o v1.o v2.o v3.o ../../../src/utils/list.o ../../../src/utils/error.o + +CFLAGS+=-g +LDFLAGS+=-g +CFLAGS+=$(XML2_CFLAGS) + +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=SVGGen$(EXE) +EXTRALIBS+=-lwsock32 -lz +else +EXT= +PROG=SVGGen +endif + +SRCS := $(OBJS:.o=.c) + +all: $(PROG) + +SVGGen$(EXE): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(XML2_LFLAGS) $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) $(PROG) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/applications/generators/SVG/SVGGen.dsp b/applications/generators/SVG/SVGGen.dsp new file mode 100644 index 0000000..8d95fcb --- /dev/null +++ b/applications/generators/SVG/SVGGen.dsp @@ -0,0 +1,126 @@ +# Microsoft Developer Studio Project File - Name="SVGGen" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=SVGGen - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "SVGGen.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "SVGGen.mak" CFG="SVGGen - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "SVGGen - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "SVGGen - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "SVGGen - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 libxml2.lib zlib.lib iconv.lib /nologo /subsystem:console /machine:I386 /libpath:"../../../extra_lib/lib/w32_release" + +!ELSEIF "$(CFG)" == "SVGGen - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include/" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 libxml2.lib zlib.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"../../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "SVGGen - Win32 Release" +# Name "SVGGen - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\..\src\utils\error.c +# End Source File +# Begin Source File + +SOURCE=.\html.c +# End Source File +# Begin Source File + +SOURCE=.\laser.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\utils\list.c +# End Source File +# Begin Source File + +SOURCE=.\main.c +# End Source File +# Begin Source File + +SOURCE=.\v1.c +# End Source File +# Begin Source File + +SOURCE=.\v2.c +# End Source File +# Begin Source File + +SOURCE=.\v3.c +# End Source File +# End Group +# Begin Source File + +SOURCE=.\svggen.h +# End Source File +# End Target +# End Project diff --git a/applications/generators/SVG/SVGGen.dsw b/applications/generators/SVG/SVGGen.dsw new file mode 100644 index 0000000..170c054 --- /dev/null +++ b/applications/generators/SVG/SVGGen.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "SVGGen"=.\SVGGen.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/applications/generators/SVG/Tiny-1.2-NG/Tiny-1.2.nvdl b/applications/generators/SVG/Tiny-1.2-NG/Tiny-1.2.nvdl new file mode 100644 index 0000000..0571331 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/Tiny-1.2.nvdl @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/Tiny-1.2.rng b/applications/generators/SVG/Tiny-1.2-NG/Tiny-1.2.rng new file mode 100644 index 0000000..90c7adc --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/Tiny-1.2.rng @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/animate.rng b/applications/generators/SVG/Tiny-1.2-NG/animate.rng new file mode 100644 index 0000000..5a49526 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/animate.rng @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + remove + freeze + + + + + + + + + + + + + + + + always + never + whenNotActive + + + + + + + + + + + + + + canSlip + locked + independent + default + + + + + + + + default + + + + + + + + + + + + + + + + + + + + canSlip + locked + independent + inherit + + + + + + + + inherit + + + + + + + + + + + + + + + + + + + + + XML + CSS + auto + + + + + + + + + + + + + + + discrete + linear + paced + spline + + + + + + + + + + + + + + + replace + sum + + + + + + + none + sum + + + + + + + + + + translate + scale + rotate + skewX + skewY + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/animation-element.rng b/applications/generators/SVG/Tiny-1.2-NG/animation-element.rng new file mode 100644 index 0000000..8ef01ea --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/animation-element.rng @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/audio.rng b/applications/generators/SVG/Tiny-1.2-NG/audio.rng new file mode 100644 index 0000000..046f0de --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/audio.rng @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/conditional.rng b/applications/generators/SVG/Tiny-1.2-NG/conditional.rng new file mode 100644 index 0000000..03449a8 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/conditional.rng @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/contenttype-attrib.rng b/applications/generators/SVG/Tiny-1.2-NG/contenttype-attrib.rng new file mode 100644 index 0000000..b72188c --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/contenttype-attrib.rng @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/coordinate-attrib.rng b/applications/generators/SVG/Tiny-1.2-NG/coordinate-attrib.rng new file mode 100644 index 0000000..325982d --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/coordinate-attrib.rng @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/core-attrib.rng b/applications/generators/SVG/Tiny-1.2-NG/core-attrib.rng new file mode 100644 index 0000000..15cbdc1 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/core-attrib.rng @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + default + preserve + + + + + + + + + + + preserve + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/datatypes.rng b/applications/generators/SVG/Tiny-1.2-NG/datatypes.rng new file mode 100644 index 0000000..c330b2a --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/datatypes.rng @@ -0,0 +1,145 @@ + + + + + + + + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + auto + self + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/discard.rng b/applications/generators/SVG/Tiny-1.2-NG/discard.rng new file mode 100644 index 0000000..18bd530 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/discard.rng @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/extensibility.rng b/applications/generators/SVG/Tiny-1.2-NG/extensibility.rng new file mode 100644 index 0000000..45309d7 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/extensibility.rng @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/extresources-attrib.rng b/applications/generators/SVG/Tiny-1.2-NG/extresources-attrib.rng new file mode 100644 index 0000000..f900756 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/extresources-attrib.rng @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/flowable-text-tiny.rng b/applications/generators/SVG/Tiny-1.2-NG/flowable-text-tiny.rng new file mode 100644 index 0000000..dc87152 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/flowable-text-tiny.rng @@ -0,0 +1,118 @@ + + + + + + + + + + + auto + before + center + after + inherit + + + + + + + auto + inherit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + auto + + + + + + + + auto + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/focus-attrib.rng b/applications/generators/SVG/Tiny-1.2-NG/focus-attrib.rng new file mode 100644 index 0000000..edd10fc --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/focus-attrib.rng @@ -0,0 +1,86 @@ + + + + + + + + + + auto + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + auto + none + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/font-tiny.rng b/applications/generators/SVG/Tiny-1.2-NG/font-tiny.rng new file mode 100644 index 0000000..824006d --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/font-tiny.rng @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/gradient-tiny.rng b/applications/generators/SVG/Tiny-1.2-NG/gradient-tiny.rng new file mode 100644 index 0000000..a8cd97e --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/gradient-tiny.rng @@ -0,0 +1,156 @@ + + + + + + + + + + + inherit + + + + + + + + inherit + + + + + + + + + + + + + + + + + + + + + + userSpaceOnUse + objectBoundingBox + + + + + + + + + + + pad + reflect + repeat + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/graphics-attrib.rng b/applications/generators/SVG/Tiny-1.2-NG/graphics-attrib.rng new file mode 100644 index 0000000..7b1028d --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/graphics-attrib.rng @@ -0,0 +1,109 @@ + + + + + + + + + + inline + block + list-item + run-in + compact + marker + table + inline-table + table-row-group + table-header-group + table-footer-group + table-row + table-column-group + table-column + table-cell + table-caption + none + inherit + + + + + + + visible + hidden + inherit + + + + + + + auto + optimizeSpeed + optimizeQuality + inherit + + + + + + + visiblePainted + visibleFill + visibleStroke + visible + painted + fill + stroke + all + none + inherit + + + + + + + auto + optimizeSpeed + crispEdges + geometricPrecision + inherit + + + + + + + auto + optimizeSpeed + optimizeLegibility + geometricPrecision + inherit + + + + + + + + + + whenStarted + always + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/handler.rng b/applications/generators/SVG/Tiny-1.2-NG/handler.rng new file mode 100644 index 0000000..f052fc9 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/handler.rng @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/headers.rng b/applications/generators/SVG/Tiny-1.2-NG/headers.rng new file mode 100644 index 0000000..a112936 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/headers.rng @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/hyperlink.rng b/applications/generators/SVG/Tiny-1.2-NG/hyperlink.rng new file mode 100644 index 0000000..a8e19e5 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/hyperlink.rng @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + _replace + _self + _parent + _top + _blank + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/image.rng b/applications/generators/SVG/Tiny-1.2-NG/image.rng new file mode 100644 index 0000000..1549ddb --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/image.rng @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/laser-ext.rng b/applications/generators/SVG/Tiny-1.2-NG/laser-ext.rng new file mode 100644 index 0000000..ed65b8a --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/laser-ext.rng @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/media-attrib.rng b/applications/generators/SVG/Tiny-1.2-NG/media-attrib.rng new file mode 100644 index 0000000..508dc45 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/media-attrib.rng @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + inherit + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/opacity-attrib-tiny.rng b/applications/generators/SVG/Tiny-1.2-NG/opacity-attrib-tiny.rng new file mode 100644 index 0000000..00fb018 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/opacity-attrib-tiny.rng @@ -0,0 +1,44 @@ + + + + + + + + + + inherit + + + + + + + + inherit + + + + + + + + + + + inherit + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/paint-attrib-tiny.rng b/applications/generators/SVG/Tiny-1.2-NG/paint-attrib-tiny.rng new file mode 100644 index 0000000..d67afaf --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/paint-attrib-tiny.rng @@ -0,0 +1,113 @@ + + + + + + + + + + inherit + + + + + + + + inherit + nonzero + evenodd + + + + + + + inherit + + + + + + + + inherit + none + + + + + + + + inherit + + + + + + + + butt + round + square + inherit + + + + + + + miter + round + bevel + inherit + + + + + + + inherit + + + + + + + + inherit + + + + + + + + inherit + + + + + + + + auto + optimizeSpeed + optimizeQuality + inherit + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/prefetch.rng b/applications/generators/SVG/Tiny-1.2-NG/prefetch.rng new file mode 100644 index 0000000..4f91049 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/prefetch.rng @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + auto + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/script.rng b/applications/generators/SVG/Tiny-1.2-NG/script.rng new file mode 100644 index 0000000..65204fb --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/script.rng @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/shapes.rng b/applications/generators/SVG/Tiny-1.2-NG/shapes.rng new file mode 100644 index 0000000..1b32e93 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/shapes.rng @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/solidcolor.rng b/applications/generators/SVG/Tiny-1.2-NG/solidcolor.rng new file mode 100644 index 0000000..3e92662 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/solidcolor.rng @@ -0,0 +1,65 @@ + + + + + + + + + + + inherit + + + + + + + + inherit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/structure-tiny.rng b/applications/generators/SVG/Tiny-1.2-NG/structure-tiny.rng new file mode 100644 index 0000000..6bc021e --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/structure-tiny.rng @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \s*(none|xMidYMid)\s*(meet)?\s* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + disable + magnify + + + + + + + 1.0 + 1.1 + 1.2 + + + + + + + none + tiny + basic + full + + + + + + + + + + + + none + + + + + + + + onLoad + onStart + + + + + + + all + forwardOnly + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/text-tiny.rng b/applications/generators/SVG/Tiny-1.2-NG/text-tiny.rng new file mode 100644 index 0000000..074bff0 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/text-tiny.rng @@ -0,0 +1,213 @@ + + + + + + + + + + + inherit + + + + + + + + inherit + + + + + + + + normal + italic + oblique + inherit + + + + + + + normal + small-caps + inherit + + + + + + + normal + bold + bolder + lighter + 100 + 200 + 300 + 400 + 500 + 600 + 700 + 800 + 900 + inherit + + + + + + + start + middle + end + inherit + + + + + + + start + center + end + inherit + + + + + + + none + underline + overline + line-through + blink + inherit + + + + + + + + + + + + + + + + + none + simple + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/transform-attrib.rng b/applications/generators/SVG/Tiny-1.2-NG/transform-attrib.rng new file mode 100644 index 0000000..fe379ae --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/transform-attrib.rng @@ -0,0 +1,25 @@ + + + + + + + + + + + none + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/vectoreffects-attrib-tiny.rng b/applications/generators/SVG/Tiny-1.2-NG/vectoreffects-attrib-tiny.rng new file mode 100644 index 0000000..ed5c9ad --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/vectoreffects-attrib-tiny.rng @@ -0,0 +1,26 @@ + + + + + + + + + + none + non-scaling-stroke + inherit + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/video.rng b/applications/generators/SVG/Tiny-1.2-NG/video.rng new file mode 100644 index 0000000..b1f885d --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/video.rng @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + geometric + pinned + pinned90 + pinned180 + pinned270 + + + + + + + none + top + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/viewport-attrib-tiny.rng b/applications/generators/SVG/Tiny-1.2-NG/viewport-attrib-tiny.rng new file mode 100644 index 0000000..91473b2 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/viewport-attrib-tiny.rng @@ -0,0 +1,45 @@ + + + + + + + + + + inherit + none + + + + + + + + inherit + + + + + + + + visible + hidden + scroll + auto + inherit + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/xlink-attrib.rng b/applications/generators/SVG/Tiny-1.2-NG/xlink-attrib.rng new file mode 100644 index 0000000..83b4fb8 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/xlink-attrib.rng @@ -0,0 +1,123 @@ + + + + + + + + + + + + simple + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + onLoad + + + + + + + + other + + + + + + + + + embed + + + + + + + + + + + + + + + + + + + new + replace + + + + + + onRequest + + + + + + + + + + + + + + + diff --git a/applications/generators/SVG/Tiny-1.2-NG/xml-events.rng b/applications/generators/SVG/Tiny-1.2-NG/xml-events.rng new file mode 100644 index 0000000..147fed2 --- /dev/null +++ b/applications/generators/SVG/Tiny-1.2-NG/xml-events.rng @@ -0,0 +1,84 @@ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + default + capture + + + + + + + continue + stop + + + + + + + perform + cancel + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
diff --git a/applications/generators/SVG/html.c b/applications/generators/SVG/html.c new file mode 100644 index 0000000..6e79378 --- /dev/null +++ b/applications/generators/SVG/html.c @@ -0,0 +1,150 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004-2005 + * All rights reserved + * + * This file is part of GPAC / SVG Scene Graph Generator sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "svggen.h" +static FILE *BeginHtml() +{ + FILE *f; + char sPath[GF_MAX_PATH]; + + sprintf(sPath, "C:%cUsers%cCyril%ccontent%csvg%cregression%cregression_table.html", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); + f = fopen(sPath, "wt"); + + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "Status of the SVG implementation in GPAC\n"); + + { + time_t rawtime; + time(&rawtime); + fprintf(f, "\n\n", asctime(gmtime(&rawtime)), GPAC_VERSION); + } + fprintf(f, "\n", COPYRIGHT); + fprintf(f, "\n"); + + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "

Status of the SVG implementation in GPAC

\n"); + return f; +} + +static void EndHtml(FILE *f) +{ + fprintf(f, "\n"); + fprintf(f, "\n"); + fclose(f); +} + +/* Generates an HTML table */ +void generate_table(GF_List *elements) +{ + u32 i, j; + u32 nbExamples = 0; + FILE *f; + f = BeginHtml(); + + + fprintf(f, "

Legend

\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "
StatusColor
Not supported
Partially supported
Fully supported
\n"); + + fprintf(f, "

SVG Tiny 1.2 Elements

\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + for (i = 0; i < gf_list_count(elements); i++) { + SVGGenElement *elt = gf_list_get(elements, i); + fprintf(f, "\n", elt->implementation_name); + fprintf(f, "\n", elt->svg_name, elt->svg_name); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + } + fprintf(f, "\n"); + fprintf(f, "
Element NameStatusObservationsExample(s)Bug(s)
%s   
\n"); + + for (i = 0; i < gf_list_count(elements); i++) { + SVGGenElement *elt = gf_list_get(elements, i); + fprintf(f, "

%s

\n", elt->implementation_name, elt->svg_name); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n"); + for (j = 0; j < gf_list_count(elt->attributes); j++) { + SVGGenAttribute *att = gf_list_get(elt->attributes, j); + if (!strcmp(att->svg_name, "textContent")) continue; + fprintf(f, "\n"); + fprintf(f, "\n",att->svg_name); + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n",++nbExamples); + fprintf(f, "\n"); + fprintf(f, "\n"); + } + fprintf(f, "\n"); + fprintf(f, "
Attribute NameStatusObservationsExample(s)Bug(s)
%s %d -   
\n"); + } + + EndHtml(f); + gf_list_del(elements); +} + diff --git a/applications/generators/SVG/laser.c b/applications/generators/SVG/laser.c new file mode 100644 index 0000000..914594c --- /dev/null +++ b/applications/generators/SVG/laser.c @@ -0,0 +1,266 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004-2005 + * All rights reserved + * + * This file is part of GPAC / SVG Scene Graph Generator sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "svggen.h" + +static char *laser_attribute_name_type_list[] = { + "target", "accumulate", "additive", "audio_level", "bandwidth", "begin", "calcMode", "children", "choice", "clipBegin", "clipEnd", "color", "color_rendering", "cx", "cy", "d", "delta", "display", "display_align", "dur", "editable", "lsr_enabled", "end", "event", "externalResourcesRequired", "fill", "fill_opacity", "fill_rule", "focusable", "font_family", "font_size", "font_style", "font_variant", "font_weight", "fullscreen", "gradientUnits", "handler", "height", "image_rendering", "keyPoints", "keySplines", "keyTimes", "line_increment", "listener_target", "mediaCharacterEncoding", "mediaContentEncodings", "mediaSize", "mediaTime", "nav_down", "nav_down_left", "nav_down_right", "nav_left", "nav_next", "nav_prev", "nav_right", "nav_up", "nav_up_left", "nav_up_right", "observer", "offset", "opacity", "overflow", "overlay", "path", "pathLength", "pointer_events", "points", "preserveAspectRatio", "r", "repeatCount", "repeatDur", "requiredExtensions", "requiredFeatures", "requiredFormats", "restart", "rotate", "rotation", "rx", "ry", "scale", "shape_rendering", "size", "solid_color", "solid_opacity", "stop_color", "stop_opacity", "stroke", "stroke_dasharray", "stroke_dashoffset", "stroke_linecap", "stroke_linejoin", "stroke_miterlimit", "stroke_opacity", "stroke_width", "svg_height", "svg_width", "syncBehavior", "syncBehaviorDefault", "syncReference", "syncTolerance", "syncToleranceDefault", "systemLanguage", "text_align", "text_anchor", "text_decoration", "text_display", "text_rendering", "textContent", "transform", "transformBehavior", "translation", "vector_effect", "viewBox", "viewport_fill", "viewport_fill_opacity", "visibility", "width", "x", "x1", "x2", "xlink_actuate", "xlink_arcrole", "xlink_href", "xlink_role", "xlink_show", "xlink_title", "xlink_type", "xml_base", "xml_lang", "y", "y1", "y2", "zoomAndPan", NULL +}; + + + +static char *laser_attribute_rare_type_list[] = { + "_class", "audio_level", "color", "color_rendering", "display", "display_align", "fill_opacity", + "fill_rule", "image_rendering", "line_increment", "pointer_events", "shape_rendering", "solid_color", + "solid_opacity", "stop_color", "stop_opacity", "stroke_dasharray", "stroke_dashoffset", "stroke_linecap", + "stroke_linejoin", "stroke_miterlimit", "stroke_opacity", "stroke_width", "text_anchor", "text_rendering", + "viewport_fill", "viewport_fill_opacity", "vector_effect", "visibility", "requiredExtensions", + "requiredFeatures", "requiredFormats", "systemLanguage", "xml_base", "xml_lang", "xml_space", + "nav_next", "nav_up", "nav_up_left", "nav_up_right", "nav_prev", "nav_down", "nav_down_left", + "nav_down_right", "nav_left", "focusable", "nav_right", "transform","text_decoration", + "extension", /*LASER EXTENSIONS SVG*/ + + "font_variant", "font_family", "font_size", "font_style", "font_weight", "xlink_title", "xlink_type", + "xlink_role", "xlink_arcrole", "xlink_actuate", "xlink_show", "end", "max", "min", + NULL +}; + + +s32 get_lsr_att_name_type(const char *name) +{ + u32 i = 0; + while (laser_attribute_name_type_list[i]) { + if (!strcmp(name, laser_attribute_name_type_list[i])) return i; + i++; + } + return -1; +} + +void generateGenericAttrib(FILE *output, SVGGenElement *elt, u32 index) +{ + int k; + for (k=0; k < generic_attributes[index].array_length; k++) { + char *att_name = generic_attributes[index].array[k]; + SVGGenAttribute *a = findAttribute(elt, att_name); + if (a) { + s32 type = get_lsr_att_name_type(att_name); + /*SMIL anim fill not updatable*/ + if ((index==6) && !strcmp(att_name, "fill")) { + type = -1; + } + fprintf(output, ", %d", type); + } + } +} + +void generate_laser_tables(GF_List *svg_elements) +{ + FILE *output; + u32 i; + u32 special_cases; + + output = BeginFile(2); + if (generation_mode == 1) fprintf(output, "\n#include \n\n"); + else if (generation_mode == 2) fprintf(output, "\n#include \n\n"); + else if (generation_mode == 3) fprintf(output, "\n#include \n\n"); + + for (i=0; iattributes); + + fprintf(output, "static const s32 %s_field_to_attrib_type[] = {\n", elt->implementation_name); + + /*core info: id, xml:id, class, xml:lang, xml:base, xml:space, externalResourcesRequired*/ + fprintf(output, "-1, -1, -1, 125, 124, -1, 24"); + if (elt->has_media_properties) generateGenericAttrib(output, elt, 2); + if (elt->has_properties) generateGenericAttrib(output, elt, 1); + if (elt->has_opacity_properties) generateGenericAttrib(output, elt, 3); + if (elt->has_focus) generateGenericAttrib(output, elt, 4); + if (elt->has_xlink) generateGenericAttrib(output, elt, 5); + if (elt->has_timing) generateGenericAttrib(output, elt, 6); + if (elt->has_sync) generateGenericAttrib(output, elt, 7); + if (elt->has_animation) generateGenericAttrib(output, elt, 8); + if (elt->has_conditional) generateGenericAttrib(output, elt, 9); + /*WATCHOUT - HARDCODED VALUES*/ + if (elt->has_transform) fprintf(output, ", 105"); + if (elt->has_xy) fprintf(output, ", 116, 129"); + + + /*svg.width and svg.height escapes*/ + special_cases = 0; + if (!strcmp(elt->svg_name, "svg")) special_cases = 1; + else if (!strcmp(elt->svg_name, "a")) special_cases = 2; + + for (j=0; jattributes, j); + s32 type = get_lsr_att_name_type(att->svg_name); + if (special_cases==1) { + if (!strcmp(att->svg_name, "width")) + type = 95; + else if (!strcmp(att->svg_name, "height")) + type = 94; + } + if ((special_cases==2) && !strcmp(att->svg_name, "target")) + type = 0; + fprintf(output, ", %d", type); + } + fprintf(output, "\n};\n\n"); + + } + fprintf(output, "s32 gf_lsr_field_to_attrib_type(GF_Node *n, u32 fieldIndex)\n{\n\tif(!n) return -2;\n\tswitch (gf_node_get_tag(n)) {\n"); + for (i=0; iimplementation_name); + fcount = gf_list_count(elt->attributes); + fprintf(output, "\t\treturn %s_field_to_attrib_type[fieldIndex];\n", elt->implementation_name); + } + fprintf(output, "\tdefault:\n\t\treturn -2;\n\t}\n}\n\n"); +} + + +void generate_laser_tables_da(GF_List *atts) +{ + FILE *output; + u32 i, count, j, count2; + + output = BeginFile(2); + + fprintf(output, "\n#include \n\n"); + fprintf(output, "\n\ns32 gf_lsr_anim_type_from_attribute(u32 tag) {\n\tswitch(tag) {\n"); + + count = gf_list_count(atts); + j=0; + while (laser_attribute_name_type_list[j]) { + for (i=0; iimplementation_name, laser_attribute_name_type_list[j])) { + fprintf(output, "\tcase TAG_SVG_ATT_%s: return %d;\n", att->implementation_name, j); + break; + } + } + if (i==count) { + //fprintf(stdout, "Warning: Ignoring %s\n", laser_attribute_name_type_list[j]); + fprintf(output, "\tcase TAG_LSR_ATT_%s: return %d;\n", laser_attribute_name_type_list[j], j); + } + j++; + } + fprintf(output, "\tdefault: return -1;\n\t}\n}\n\n"); + + fprintf(output, "\n\ns32 gf_lsr_rare_type_from_attribute(u32 tag) {\n\tswitch(tag) {\n"); + count = gf_list_count(atts); + j=0; + while (laser_attribute_rare_type_list[j]) { + for (i=0; iimplementation_name, laser_attribute_rare_type_list[j])) { + fprintf(output, "\tcase TAG_SVG_ATT_%s: return %d;\n", att->implementation_name, j); + break; + } + } + if (i==count) { + if (!strcmp(laser_attribute_rare_type_list[j], "extension")) { + fprintf(output, "\tcase TAG_SVG_ATT_syncMaster: return %d;\n", j); + fprintf(output, "\tcase TAG_SVG_ATT_focusHighlight: return %d;\n", j); + fprintf(output, "\tcase TAG_SVG_ATT_initialVisibility: return %d;\n", j); + fprintf(output, "\tcase TAG_SVG_ATT_fullscreen: return %d;\n", j); + fprintf(output, "\tcase TAG_SVG_ATT_requiredFonts: return %d;\n", j); + } else { + fprintf(stdout, "Warning: Ignoring %s\n", laser_attribute_rare_type_list[j]); + } + } + j++; + } + fprintf(output, "\tdefault: return -1;\n\t}\n}\n\n"); + + + + fprintf(output, "\n\ns32 gf_lsr_anim_type_to_attribute(u32 tag) {\n\tswitch(tag) {\n"); + j=0; + while (laser_attribute_name_type_list[j]) { + for (i=0; iimplementation_name, laser_attribute_name_type_list[j])) { + fprintf(output, "\tcase %d: return TAG_SVG_ATT_%s;\n", j, att->implementation_name); + break; + } + } + if (i==count) { + fprintf(output, "\tcase %d: return TAG_LSR_ATT_%s;\n", j, laser_attribute_name_type_list[j]); + } + j++; + } + fprintf(output, "\tdefault: return -1;\n\t}\n}\n\n"); + + fprintf(output, "\n\ns32 gf_lsr_rare_type_to_attribute(u32 tag) {\n\tswitch(tag) {\n"); + j=0; + while (laser_attribute_rare_type_list[j]) { + for (i=0; iimplementation_name, laser_attribute_rare_type_list[j])) { + fprintf(output, "\tcase %d: return TAG_SVG_ATT_%s;\n", j, att->implementation_name); + break; + } + } + j++; + } + fprintf(output, "\tdefault: return -1;\n\t}\n}\n\n"); + + + fprintf(output, "\n\nu32 gf_lsr_same_rare(SVGAllAttributes *elt_atts, SVGAllAttributes *base_atts)\n{\n"); + fprintf(output, "\tGF_FieldInfo f_elt, f_base;\n"); + + j=0; + while (laser_attribute_rare_type_list[j]) { + SVGGenAttribute *att = NULL; + if (!strcmp(laser_attribute_rare_type_list[j], "extension")) { + j++; + continue; + } + for (i=0; iimplementation_name, laser_attribute_rare_type_list[j])) + break; + att = NULL; + } + assert(att); + + fprintf(output, "\tf_elt.fieldType = f_base.fieldType = %s_datatype;\n", att->impl_type); + fprintf(output, "\tf_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_%s;\n", laser_attribute_rare_type_list[j]); + fprintf(output, "\tf_elt.far_ptr = elt_atts->%s;\n", laser_attribute_rare_type_list[j]); + fprintf(output, "\tf_base.far_ptr = base_atts->%s;\n", laser_attribute_rare_type_list[j]); + fprintf(output, "\tif (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0;\n\n"); + + j++; + } + fprintf(output, "\treturn 1;\n}\n\n"); + + fclose(output); +} \ No newline at end of file diff --git a/applications/generators/SVG/main.c b/applications/generators/SVG/main.c new file mode 100644 index 0000000..e448d93 --- /dev/null +++ b/applications/generators/SVG/main.c @@ -0,0 +1,937 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004-2005 + * All rights reserved + * + * This file is part of GPAC / SVG Scene Graph Generator sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "svggen.h" + +SVGGenAttribute *NewSVGGenAttribute() +{ + SVGGenAttribute *att; + GF_SAFEALLOC(att, SVGGenAttribute) + return att; +} + +void deleteSVGGenAttribute(SVGGenAttribute **p) +{ + xmlFree((*p)->svg_name); + xmlFree((*p)->svg_type); + free(*p); + *p = NULL; +} + +SVGGenAttrGrp *NewSVGGenAttrGrp() +{ + SVGGenAttrGrp *tmp; + GF_SAFEALLOC(tmp, SVGGenAttrGrp) + tmp->attrs = gf_list_new(); + tmp->attrgrps = gf_list_new(); + return tmp; +} + +SVGGenElement *NewSVGGenElement() +{ + SVGGenElement *elt; + GF_SAFEALLOC(elt, SVGGenElement); + if (elt) { + elt->attributes = gf_list_new(); + elt->generic_attributes = gf_list_new(); + } + return elt; +} + +void deleteSVGGenElement(SVGGenElement **p) +{ + u32 i; + xmlFree((*p)->svg_name); + for (i = 0; i < gf_list_count((*p)->attributes); i++) { + SVGGenAttribute *a = gf_list_get((*p)->attributes, i); + deleteSVGGenAttribute(&a); + } + gf_list_del((*p)->attributes); + free(*p); + *p = NULL; +} + +static GF_List *sortElements(GF_List *elements) +{ + u32 i, j; + GF_List *sorted_elements = gf_list_new(); + + for (i = 0; i< gf_list_count(elements); i++) { + u8 is_added = 0; + SVGGenElement *elt = gf_list_get(elements, i); + for (j = 0; j < gf_list_count(sorted_elements); j++) { + SVGGenElement *selt = gf_list_get(sorted_elements, j); + if (strcmp(elt->svg_name, selt->svg_name) < 0) { + gf_list_insert(sorted_elements, elt, j); + is_added = 1; + break; + } + } + if (!is_added) gf_list_add(sorted_elements, elt); + } + + gf_list_del(elements); + return sorted_elements; +} + +static GF_List *sortAttrGrp(GF_List *attgrps) +{ + u32 i, j; + GF_List *sorted_attgrps = gf_list_new(); + + for (i = 0; i< gf_list_count(attgrps); i++) { + u8 is_added = 0; + SVGGenAttrGrp *grp = gf_list_get(attgrps, i); + for (j = 0; j < gf_list_count(sorted_attgrps); j++) { + SVGGenAttrGrp *sgrp = gf_list_get(sorted_attgrps, j); + if (strcmp(grp->name, sgrp->name) < 0) { + gf_list_insert(sorted_attgrps, grp, j); + is_added = 1; + break; + } + } + if (!is_added) gf_list_add(sorted_attgrps, grp); + } + + gf_list_del(attgrps); + return sorted_attgrps; +} + +static GF_List *sortAttr(GF_List *atts) +{ + u32 i, j; + GF_List *sorted_atts = gf_list_new(); + + for (i = 0; i< gf_list_count(atts); i++) { + u8 is_added = 0; + SVGGenAttribute *att = gf_list_get(atts, i); + for (j = 0; j < gf_list_count(sorted_atts); j++) { + SVGGenAttribute *satt = gf_list_get(sorted_atts, j); + if (strcmp(att->svg_name, satt->svg_name) < 0) { + gf_list_insert(sorted_atts, att, j); + is_added = 1; + break; + } + } + if (!is_added) gf_list_add(sorted_atts, att); + } + + gf_list_del(atts); + return sorted_atts; +} + +void svgNameToImplementationName(xmlChar *svg_name, char implementation_name[50]) { + char *tmp; + strcpy(implementation_name, svg_name); + tmp = implementation_name; + while ( (tmp = strchr(tmp, '.')) ) { *tmp='_'; tmp++; } + tmp = implementation_name; + while ( (tmp = strchr(tmp, '-')) ) { *tmp='_'; tmp++; } + tmp = implementation_name; + while ( (tmp = strchr(tmp, ':')) ) { *tmp='_'; tmp++; } +} + +static Bool isGenericAttributesGroup(char *name) +{ + if (!strcmp(name, "svg.Core.attr") || + !strcmp(name, "svg.CorePreserve.attr") || + !strcmp(name, "svg.External.attr") || + !strcmp(name, "svg.Properties.attr") || + !strcmp(name, "svg.Media.attr") || + !strcmp(name, "svg.MediaClip.attr") || + !strcmp(name, "svg.Opacity.attr") || + !strcmp(name, "svg.FocusHighlight.attr") || + !strcmp(name, "svg.Focus.attr") || + !strcmp(name, "svg.AnimateCommon.attr") || + !strcmp(name, "svg.XLinkEmbed.attr") || + !strcmp(name, "svg.XLinkRequired.attr") || + !strcmp(name, "svg.XLinkReplace.attr") || +// !strcmp(name, "svg.ContentType.attr") || + !strcmp(name, "svg.AnimateTiming.attr") || + !strcmp(name, "svg.AnimateTimingNoMinMax.attr") || + !strcmp(name, "svg.AnimateBegin.attr") || + !strcmp(name, "svg.AnimateTimingNoFillNoMinMax.attr") || + !strcmp(name, "svg.AnimateSync.attr") || + !strcmp(name, "svg.AnimateSyncDefault.attr") || + !strcmp(name, "svg.AnimateAttributeCommon.attr") || + !strcmp(name, "svg.AnimateToCommon.attr") || + !strcmp(name, "svg.AnimateValueCommon.attr") || + !strcmp(name, "svg.AnimateAdditionCommon.attr") || + !strcmp(name, "svg.AnimateTypeCommon.attr") || + !strcmp(name, "svg.Conditional.attr") || +// !strcmp(name, "svg.XY.attr") || + !strcmp(name, "svg.Transform.attr")) { + return 1; + } else { + return 0; + } +} + +static Bool setGenericAttributesFlags(char *name, SVGGenElement *e) +{ + Bool ret = 1; + if (!strcmp(name, "svg.Core.attr") || + !strcmp(name, "svg.CorePreserve.attr") || + !strcmp(name, "svg.External.attr")) { + e->has_svg_generic = 1; + e->has_xml_generic = 1; + } else if (!strcmp(name, "svg.Properties.attr")) { + e->has_properties = 1; + e->has_media_properties = 1; + } else if (!strcmp(name, "svg.Media.attr")) { + e->has_media_properties = 1; + } else if (!strcmp(name, "svg.Opacity.attr")) { + e->has_opacity_properties = 1; + } else if (!strcmp(name, "svg.FocusHighlight.attr") || + !strcmp(name, "svg.Focus.attr")) { + e->has_focus = 1; + } else if (!strcmp(name, "svg.AnimateCommon.attr") || + !strcmp(name, "svg.XLinkEmbed.attr") || + !strcmp(name, "svg.XLinkRequired.attr") || + !strcmp(name, "svg.XLinkReplace.attr")) { //|| +// !strcmp(name, "svg.ContentType.attr")) { + e->has_xlink = 1; + } else if (!strcmp(name, "svg.AnimateTiming.attr") || + !strcmp(name, "svg.AnimateTimingNoMinMax.attr") || + !strcmp(name, "svg.AnimateBegin.attr") || + !strcmp(name, "svg.AnimateTimingNoFillNoMinMax.attr")) { + e->has_timing = 1; + } else if (!strcmp(name, "svg.AnimateSync.attr") || + !strcmp(name, "svg.AnimateSyncDefault.attr")) { + e->has_sync= 1; + } else if (!strcmp(name, "svg.AnimateAttributeCommon.attr") || + !strcmp(name, "svg.AnimateToCommon.attr") || + !strcmp(name, "svg.AnimateValueCommon.attr") || + !strcmp(name, "svg.AnimateAdditionCommon.attr") || + !strcmp(name, "svg.AnimateTypeCommon.attr")) { + e->has_animation = 1; + } else if (!strcmp(name, "svg.Conditional.attr")) { + e->has_conditional = 1; + } else if (!strcmp(name, "svg.Transform.attr")) { + e->has_transform = 1; + } else if (!strcmp(name, "svg.XY.attr")) { + e->has_xy = 1; + } else { + ret = 0; + } + return ret; +} + +static void flattenAttributeGroup(SVGGenAttrGrp attgrp, SVGGenElement *e, Bool all); + +static void flattenAttributeGroups(GF_List *attrgrps, SVGGenElement *e, Bool all) +{ + u32 i; + for (i = 0; i < gf_list_count(attrgrps); i ++) { + SVGGenAttrGrp *ag = gf_list_get(attrgrps, i); + flattenAttributeGroup(*ag, e, all); + } +} + +static void flattenAttributeGroup(SVGGenAttrGrp attgrp, SVGGenElement *e, Bool all) +{ + u32 i; + + if (isGenericAttributesGroup(attgrp.name) && !all) { + setGenericAttributesFlags(attgrp.name, e); + flattenAttributeGroups(attgrp.attrgrps, e, 1); + for (i = 0; i < gf_list_count(attgrp.attrs); i++) { + gf_list_add(e->generic_attributes, gf_list_get(attgrp.attrs, i)); + } + } else { + flattenAttributeGroups(attgrp.attrgrps, e, all); + for (i = 0; i < gf_list_count(attgrp.attrs); i++) { + if (all) + gf_list_add(e->generic_attributes, gf_list_get(attgrp.attrs, i)); + else + gf_list_add(e->attributes, gf_list_get(attgrp.attrs, i)); + } + } +} + +SVGGenAttribute *findAttribute(SVGGenElement *e, char *name) +{ + u32 i; + for (i = 0; i < gf_list_count(e->attributes); i++) { + SVGGenAttribute *a = gf_list_get(e->attributes, i); + if (!strcmp(a->svg_name, name)) return a; + } + for (i = 0; i < gf_list_count(e->generic_attributes); i++) { + SVGGenAttribute *a = gf_list_get(e->generic_attributes, i); + if (!strcmp(a->svg_name, name)) return a; + } + return NULL; +} + +static u32 countAttributesAllInGroup(SVGGenAttrGrp *ag) +{ + u32 i, ret = 0; + for (i = 0; i < gf_list_count(ag->attrgrps); i ++) { + SVGGenAttrGrp *agtmp = gf_list_get(ag->attrgrps, i); + ret += countAttributesAllInGroup(agtmp); + } + ret += gf_list_count(ag->attrs); + return ret; +} + +/* XML related functions */ +xmlNodeSetPtr findNodes( xmlXPathContextPtr ctxt, xmlChar * path ) +{ + xmlXPathObjectPtr res = NULL; + + if ( ctxt->node != NULL && path != NULL ) { + xmlXPathCompExprPtr comp; + + xmlDocPtr tdoc = NULL; + xmlNodePtr froot = ctxt->node; + + comp = xmlXPathCompile( path ); + if ( comp == NULL ) { + return NULL; + } + + if ( ctxt->node->doc == NULL ) { + /* if one XPaths a node from a fragment, libxml2 will + refuse the lookup. this is not very usefull for XML + scripters. thus we need to create a temporary document + to make libxml2 do it's job correctly. + */ + tdoc = xmlNewDoc( NULL ); + + /* find refnode's root node */ + while ( froot != NULL ) { + if ( froot->parent == NULL ) { + break; + } + froot = froot->parent; + } + xmlAddChild((xmlNodePtr)tdoc, froot); + + ctxt->node->doc = tdoc; + } + + res = xmlXPathCompiledEval(comp, ctxt); + + xmlXPathFreeCompExpr(comp); + + if ( tdoc != NULL ) { + /* after looking through a fragment, we need to drop the + fake document again */ + xmlSetTreeDoc(froot,NULL); + froot->doc = NULL; + tdoc->children = NULL; + tdoc->last = NULL; + froot->parent = NULL; + ctxt->node->doc = NULL; + + xmlFreeDoc( tdoc ); + } + } + if (res && res->type == XPATH_NODESET) + return res->nodesetval; + else + return NULL; +} + + +/* definition of GPAC groups of SVG attributes */ + +void setAttributeType(SVGGenAttribute *att) +{ + if (!att->svg_type) { /* if the type is not given in the RNG, we explicitely set it */ + if (!strcmp(att->svg_name, "textContent")) { + strcpy(att->impl_type, "SVG_TextContent"); + } else if (!strcmp(att->svg_name, "class")) { + strcpy(att->implementation_name, "_class"); + strcpy(att->impl_type, "SVG_String"); + } else if (!strcmp(att->svg_name, "visibility")) { + strcpy(att->impl_type, "SVG_Visibility"); + } else if (!strcmp(att->svg_name, "display")) { + strcpy(att->impl_type, "SVG_Display"); + } else if (!strcmp(att->svg_name, "stroke-linecap")) { + strcpy(att->impl_type, "SVG_StrokeLineCap"); + } else if (!strcmp(att->svg_name, "stroke-dasharray")) { + strcpy(att->impl_type, "SVG_StrokeDashArray"); + } else if (!strcmp(att->svg_name, "stroke-linejoin")) { + strcpy(att->impl_type, "SVG_StrokeLineJoin"); + } else if (!strcmp(att->svg_name, "font-style")) { + strcpy(att->impl_type, "SVG_FontStyle"); + } else if (!strcmp(att->svg_name, "font-weight")) { + strcpy(att->impl_type, "SVG_FontWeight"); + } else if (!strcmp(att->svg_name, "text-anchor")) { + strcpy(att->impl_type, "SVG_TextAnchor"); + } else if (!strcmp(att->svg_name, "fill")) { + strcpy(att->impl_type, "SMIL_Fill"); + } else if (!strcmp(att->svg_name, "fill-rule")) { + strcpy(att->impl_type, "SVG_FillRule"); + } else if (!strcmp(att->svg_name, "font-family")) { + strcpy(att->impl_type, "SVG_FontFamily"); + } else if (!strcmp(att->svg_name, "calcMode")) { + strcpy(att->impl_type, "SMIL_CalcMode"); + } else if (!strcmp(att->svg_name, "values")) { + strcpy(att->impl_type, "SMIL_AnimateValues"); + } else if (!strcmp(att->svg_name, "keyTimes")) { + strcpy(att->impl_type, "SMIL_KeyTimes"); + } else if (!strcmp(att->svg_name, "keySplines")) { + strcpy(att->impl_type, "SMIL_KeySplines"); + } else if (!strcmp(att->svg_name, "keyPoints")) { + strcpy(att->impl_type, "SMIL_KeyPoints"); + } else if (!strcmp(att->svg_name, "from") || + !strcmp(att->svg_name, "to") || + !strcmp(att->svg_name, "by")) { + strcpy(att->impl_type, "SMIL_AnimateValue"); + } else if (!strcmp(att->svg_name, "additive")) { + strcpy(att->impl_type, "SMIL_Additive"); + } else if (!strcmp(att->svg_name, "accumulate")) { + strcpy(att->impl_type, "SMIL_Accumulate"); + } else if (!strcmp(att->svg_name, "begin") || + !strcmp(att->svg_name, "end") + ) { + strcpy(att->impl_type, "SMIL_Times"); + } else if (!strcmp(att->svg_name, "clipBegin") || + !strcmp(att->svg_name, "clipEnd") + ) { + strcpy(att->impl_type, "SVG_Clock"); + } else if (!strcmp(att->svg_name, "min") || + !strcmp(att->svg_name, "max") || + !strcmp(att->svg_name, "dur") || + !strcmp(att->svg_name, "repeatDur") + ) { + strcpy(att->impl_type, "SMIL_Duration"); + } else if (!strcmp(att->svg_name, "repeat")) { + strcpy(att->impl_type, "SMIL_Repeat"); + } else if (!strcmp(att->svg_name, "restart")) { + strcpy(att->impl_type, "SMIL_Restart"); + } else if (!strcmp(att->svg_name, "repeatCount")) { + strcpy(att->impl_type, "SMIL_RepeatCount"); + } else if (!strcmp(att->svg_name, "attributeName")) { + strcpy(att->impl_type, "SMIL_AttributeName"); + } else if (!strcmp(att->svg_name, "type")) { + strcpy(att->impl_type, "SVG_TransformType"); + } else if (!strcmp(att->svg_name, "font-size")) { + strcpy(att->impl_type, "SVG_FontSize"); + } else if (!strcmp(att->svg_name, "viewBox")) { + strcpy(att->impl_type, "SVG_ViewBox"); + } else if (!strcmp(att->svg_name, "preserveAspectRatio")) { + strcpy(att->impl_type, "SVG_PreserveAspectRatio"); + } else if (!strcmp(att->svg_name, "zoomAndPan")) { + strcpy(att->impl_type, "SVG_ZoomAndPan"); + } else if (!strcmp(att->svg_name, "path")) { + strcpy(att->impl_type, "SVG_PathData"); + } else if (!strcmp(att->svg_name, "image-rendering")) { + strcpy(att->impl_type, "SVG_RenderingHint"); + } else if (!strcmp(att->svg_name, "color-rendering")) { + strcpy(att->impl_type, "SVG_RenderingHint"); + } else if (!strcmp(att->svg_name, "text-rendering")) { + strcpy(att->impl_type, "SVG_RenderingHint"); + } else if (!strcmp(att->svg_name, "shape-rendering")) { + strcpy(att->impl_type, "SVG_RenderingHint"); + } else if (!strcmp(att->svg_name, "pointer-events")) { + strcpy(att->impl_type, "SVG_PointerEvents"); + } else if (!strcmp(att->svg_name, "vector-effect")) { + strcpy(att->impl_type, "SVG_VectorEffect"); + } else if (!strcmp(att->svg_name, "vector-effect")) { + strcpy(att->impl_type, "SVG_VectorEffect"); + } else if (!strcmp(att->svg_name, "display-align")) { + strcpy(att->impl_type, "SVG_DisplayAlign"); + } else if (!strcmp(att->svg_name, "text-align")) { + strcpy(att->impl_type, "SVG_TextAlign"); + } else if (!strcmp(att->svg_name, "propagate")) { + strcpy(att->impl_type, "XMLEV_Propagate"); + } else if (!strcmp(att->svg_name, "defaultAction")) { + strcpy(att->impl_type, "XMLEV_DefaultAction"); + } else if (!strcmp(att->svg_name, "phase")) { + strcpy(att->impl_type, "XMLEV_Phase"); + } else if (!strcmp(att->svg_name, "syncBehavior")) { + strcpy(att->impl_type, "SMIL_SyncBehavior"); + } else if (!strcmp(att->svg_name, "syncBehaviorDefault")) { + strcpy(att->impl_type, "SMIL_SyncBehavior"); + } else if (!strcmp(att->svg_name, "attributeType")) { + strcpy(att->impl_type, "SMIL_AttributeType"); + } else if (!strcmp(att->svg_name, "playbackOrder")) { + strcpy(att->impl_type, "SVG_PlaybackOrder"); + } else if (!strcmp(att->svg_name, "timelineBegin")) { + strcpy(att->impl_type, "SVG_TimelineBegin"); + } else if (!strcmp(att->svg_name, "xml:space")) { + strcpy(att->impl_type, "XML_Space"); + } else if (!strcmp(att->svg_name, "snapshotTime")) { + strcpy(att->impl_type, "SVG_Clock"); + } else if (!strcmp(att->svg_name, "version")) { + strcpy(att->impl_type, "SVG_String"); + } else if (!strcmp(att->svg_name, "gradientUnits")) { + strcpy(att->impl_type, "SVG_GradientUnit"); + } else if (!strcmp(att->svg_name, "baseProfile")) { + strcpy(att->impl_type, "SVG_String"); + } else if (!strcmp(att->svg_name, "focusHighlight")) { + strcpy(att->impl_type, "SVG_FocusHighlight"); + } else if (!strcmp(att->svg_name, "initialVisibility")) { + strcpy(att->impl_type, "SVG_InitialVisibility"); + } else if (!strcmp(att->svg_name, "overlay")) { + strcpy(att->impl_type, "SVG_Overlay"); + } else if (!strcmp(att->svg_name, "transformBehavior")) { + strcpy(att->impl_type, "SVG_TransformBehavior"); + } else if (!strcmp(att->svg_name, "rotate")) { + strcpy(att->impl_type, "SVG_Rotate"); + } else if (!strcmp(att->svg_name, "font-variant")) { + strcpy(att->impl_type, "SVG_FontVariant"); + } else if (!strcmp(att->svg_name, "lsr:enabled")) { + strcpy(att->impl_type, "SVG_Boolean"); + } else if (!strcmp(att->svg_name, "spreadMethod")) { + strcpy(att->impl_type, "SVG_SpreadMethod"); + } else if (!strcmp(att->svg_name, "gradientTransform")) { + strcpy(att->impl_type, "SVG_Transform_Full"); + } else if (!strcmp(att->svg_name, "editable")) { + strcpy(att->impl_type, "SVG_Boolean"); + } else if (!strcmp(att->svg_name, "choice")) { + strcpy(att->impl_type, "LASeR_Choice"); + } else if (!strcmp(att->svg_name, "size") || + !strcmp(att->svg_name, "delta")) { + strcpy(att->impl_type, "LASeR_Size"); + } else if (!strcmp(att->svg_name, "syncReference")) { + strcpy(att->impl_type, "XMLRI"); + } else { + /* For all other attributes, we use String as default type */ + strcpy(att->impl_type, "SVG_String"); + fprintf(stdout, "Warning: using type SVG_String for attribute %s.\n", att->svg_name); + } + } else { /* for some attributes, the type given in the RNG needs to be overriden */ + if (!strcmp(att->svg_name, "color")) { + strcpy(att->impl_type, "SVG_Paint"); + } else if (!strcmp(att->svg_name, "viewport-fill")) { + strcpy(att->impl_type, "SVG_Paint"); + } else if (!strcmp(att->svg_name, "syncTolerance")) { + strcpy(att->impl_type, "SMIL_SyncTolerance"); + } else if (!strcmp(att->svg_name, "syncToleranceDefault")) { + strcpy(att->impl_type, "SMIL_SyncTolerance"); + } else if (!strcmp(att->svg_name, "transform")) { + strcpy(att->impl_type, "SVG_Transform"); + } else if (!strcmp(att->svg_name, "gradientTransform")) { + strcpy(att->impl_type, "SVG_Transform"); + } else if (!strcmp(att->svg_name, "focusable")) { + strcpy(att->impl_type, "SVG_Focusable"); + } else if (!strcmp(att->svg_name, "event") || !strcmp(att->svg_name, "ev:event")) { + strcpy(att->impl_type, "XMLEV_Event"); + } else if (!strcmp(att->svg_type, "IRI.datatype")) { + strcpy(att->impl_type, "XMLRI"); + } else if (!strcmp(att->svg_type, "IDREF.datatype")) { + strcpy(att->impl_type, "XML_IDREF"); + } else if (strstr(att->svg_type, "datatype")) { + char *tmp; + sprintf(att->impl_type, "SVG_%s", att->svg_type); + tmp = att->impl_type; + while ( (tmp = strstr(tmp, "-")) ) { *tmp='_'; tmp++; } + tmp = att->impl_type; + while ( (tmp = strstr(tmp, ".")) ) { *tmp='_'; tmp++; } + tmp = att->impl_type;; + if ( (tmp = strstr(tmp, "datatype")) ) { + tmp--; + *tmp = 0; + } + } + } +} + +void getAttributeType(xmlDocPtr doc, xmlXPathContextPtr xpathCtx, + xmlNodePtr attributeNode, SVGGenAttribute *a) +{ + + xmlNodeSetPtr refNodes; + xpathCtx->node = attributeNode; + refNodes = findNodes(xpathCtx, ".//rng:ref"); + if (refNodes->nodeNr == 0) { + //a->svg_type = xmlStrdup("0_ref_type"); + } else if (refNodes->nodeNr == 1) { + xmlNodePtr ref = refNodes->nodeTab[0]; + a->svg_type = xmlStrdup(xmlGetProp(ref, "name")); + } else { + //a->svg_type = xmlStrdup("N_ref_type"); + } +} + +void getRealAttributes(xmlDocPtr doc, xmlXPathContextPtr xpathCtx, xmlNodePtr newCtxNode, + GF_List *attributes) +{ + xmlNodeSetPtr attributeNodes; + int k; + u32 j; + + xpathCtx->node = newCtxNode; + attributeNodes = findNodes(xpathCtx, ".//rng:attribute"); + for (k = 0; k < attributeNodes->nodeNr; k++) { + Bool already_exists = 0; + xmlNodePtr attributeNode = attributeNodes->nodeTab[k]; + if (attributeNode->type == XML_ELEMENT_NODE) { + SVGGenAttribute *a = NewSVGGenAttribute(); + a->svg_name = xmlGetProp(attributeNode, "name"); + a->optional = xmlStrEqual(attributeNode->parent->name, "optional"); + svgNameToImplementationName(a->svg_name, a->implementation_name); + getAttributeType(doc, xpathCtx, attributeNode, a); + setAttributeType(a); + for (j=0;jsvg_name, a->svg_name)) { + already_exists = 1; + break; + } + } + if (already_exists) { + deleteSVGGenAttribute(&a); + } else { + //fprintf(stdout, "Adding attribute %s to element %s\n",a->svg_name, e->svg_name); + gf_list_add(attributes, a); + } + } + } +} + +SVGGenAttrGrp *getOneGlobalAttrGrp(xmlDocPtr doc, xmlXPathContextPtr xpathCtx, xmlChar *name) +{ + SVGGenAttrGrp *attgrp = NULL; + xmlNodeSetPtr attrGrpDefNodes; + xmlChar *expr; + u32 j; + int i, l; + + /* attributes group already resolved */ + for (j = 0; j < gf_list_count(globalAttrGrp); j++) { + SVGGenAttrGrp *attgrp = gf_list_get(globalAttrGrp, j); + if (!strcmp(attgrp->name, name)) { + return attgrp; + } + } + + /* new attributes group */ + expr = xmlStrdup("//rng:define[@name=\""); + expr = xmlStrcat(expr, name); + expr = xmlStrcat(expr, "\" and not(rng:empty) and not(rng:notAllowed)]"); + attrGrpDefNodes = findNodes(xpathCtx, expr); + if (!attrGrpDefNodes->nodeNr) { + fprintf(stdout, "Warning: found 0 non-empty or allowed definition for the Group of Attributes: %s\n", name); + return NULL; + } + attgrp = NewSVGGenAttrGrp(); + attgrp->name = strdup(name); + svgNameToImplementationName(attgrp->name, attgrp->imp_name); + gf_list_add(globalAttrGrp, attgrp); + + for (i = 0; i < attrGrpDefNodes->nodeNr; i++) { + xmlNodePtr attrGrp = attrGrpDefNodes->nodeTab[i]; + getRealAttributes(doc, xpathCtx, attrGrp, attgrp->attrs); + + { + xmlNodeSetPtr refNodes; + xpathCtx->node = attrGrp; + refNodes = findNodes(xpathCtx, ".//rng:ref"); + for (l = 0; l < refNodes->nodeNr; l++) { + xmlNodePtr ref = refNodes->nodeTab[l]; + xmlChar *rname = xmlGetProp(ref, "name"); + if (xmlStrstr(rname, ".attr")) { + SVGGenAttrGrp *g2 = getOneGlobalAttrGrp(doc, xpathCtx, rname); + if (g2) { + gf_list_add(attgrp->attrgrps, g2); + } + } + } + } + } + return attgrp; +} + +void getAllGlobalAttrGrp(xmlDocPtr doc, xmlXPathContextPtr xpathCtx) +{ + xmlNodeSetPtr elementNodes = findNodes(xpathCtx, "//rng:define"); + int k; + for (k = 0; k < elementNodes->nodeNr; k++) { + xmlNodePtr elementNode = elementNodes->nodeTab[k]; + if (elementNode->type == XML_ELEMENT_NODE) { + xmlChar *name = NULL; + name = xmlGetProp(elementNode, "name"); + if (xmlStrstr(name, ".attr")) { + getOneGlobalAttrGrp(doc, xpathCtx, name); + } + } + } +} + +GF_List *getElements(xmlDocPtr doc, xmlXPathContextPtr xpathCtx) +{ + xmlChar *expr; + GF_List *elements = gf_list_new(); + xmlNodeSetPtr ATNodes; + xmlNodeSetPtr refNodes, elementNodes = findNodes(xpathCtx, "//rng:element"); + int k, j; + u32 i; + + for (k = 0; k < elementNodes->nodeNr; k++) { + xmlNodePtr elementNode = elementNodes->nodeTab[k]; + if (elementNode->type == XML_ELEMENT_NODE) { + SVGGenElement *e = NewSVGGenElement(); + e->svg_name = xmlStrdup(xmlGetProp(elementNode, "name")); + //fprintf(stdout, "\n\tElement %s\n", e->svg_name); + + svgNameToImplementationName(e->svg_name, e->implementation_name); + + /* getting the */ + expr = xmlStrdup("//rng:define[@name=\""); + if (!xmlStrcmp(e->svg_name, "polygon") || !xmlStrcmp(e->svg_name, "polyline")) { + expr = xmlStrcat(expr, "polyCommon"); + } else { + expr = xmlStrcat(expr, e->svg_name); + } + expr = xmlStrcat(expr, ".AT\"]"); + ATNodes = findNodes(xpathCtx, expr); + if (ATNodes->nodeNr) { + + /* dealing with attributes defined in groups of attributes */ + xpathCtx->node = ATNodes->nodeTab[0]; + refNodes = findNodes(xpathCtx, ".//rng:ref"); + for (j = 0; j nodeNr; j++) { + xmlNodePtr refNode = refNodes->nodeTab[j]; + char *name = xmlGetProp(refNode, "name"); + for (i = 0; i < gf_list_count(globalAttrGrp); i++) { + SVGGenAttrGrp *a = gf_list_get(globalAttrGrp, i); + if (!strcmp(a->name, name)) { + if (isGenericAttributesGroup(a->name)) { + setGenericAttributesFlags(a->name, e); + flattenAttributeGroup(*a, e, 1); + } else { + flattenAttributeGroup(*a, e, 0); + } + break; + } + } + } + + /* dealing with attributes defined directly here */ + getRealAttributes(doc, xpathCtx, ATNodes->nodeTab[0], e->attributes); + } + + /* checking if this element is already present in the list of possible elements + and if not, adding it */ + { + Bool found = 0; + for (i=0;isvg_name, e->svg_name)) { + found = 1; + break; + } + } + if (!found) gf_list_add(elements, e); + } + } + } + return elements; +} + +/*type: 0: header, 1: source*/ +FILE *BeginFile(u32 type) +{ + FILE *f; + + char sPath[GF_MAX_PATH]; + + if (!type) { +#ifdef LOCAL_SVG_NODES + sprintf(sPath, "nodes_svg.h", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); +#else + if (generation_mode == 1) + sprintf(sPath, "..%c..%c..%c..%cinclude%cgpac%cnodes_svg_sa.h", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); + else if (generation_mode == 2) + sprintf(sPath, "..%c..%c..%c..%cinclude%cgpac%cnodes_svg_sani.h", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); + else if (generation_mode == 3) + sprintf(sPath, "..%c..%c..%c..%cinclude%cgpac%cnodes_svg_da.h", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); + +#endif + } else if (type==1) { +#ifdef LOCAL_SVG_NODES + sprintf(sPath, "svg_nodes.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); +#else + if (generation_mode == 1) + sprintf(sPath, "..%c..%c..%c..%csrc%cscenegraph%csvg_nodes_sa.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); + else if (generation_mode == 2) + sprintf(sPath, "..%c..%c..%c..%csrc%cscenegraph%csvg_nodes_sani.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); + else if (generation_mode == 3) + sprintf(sPath, "..%c..%c..%c..%csrc%cscenegraph%csvg_nodes_da.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); +#endif + } else { +#ifdef LOCAL_SVG_NODES + sprintf(sPath, "lsr_tables.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); +#else + if (generation_mode == 1) + sprintf(sPath, "..%c..%c..%c..%csrc%claser%clsr_tables_sa.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); + else if (generation_mode == 2) + sprintf(sPath, "..%c..%c..%c..%csrc%claser%clsr_tables_sani.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); + else if (generation_mode == 3) + sprintf(sPath, "..%c..%c..%c..%csrc%claser%clsr_tables.c", GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR); + +#endif + } + + f = fopen(sPath, "wt"); + fprintf(f, "%s\n", COPYRIGHT); + + { + time_t rawtime; + time(&rawtime); + fprintf(f, "\n/*\n\tDO NOT MOFIFY - File generated on GMT %s\n\tBY SVGGen for GPAC Version %s\n*/\n\n", asctime(gmtime(&rawtime)), GPAC_VERSION); + } + + if (!type) { + if (generation_mode == 1) { + fprintf(f, "#ifndef _GF_SVG_SA_NODES_H\n"); + fprintf(f, "#define _GF_SVG_SA_NODES_H\n\n"); + } else if (generation_mode == 2) { + fprintf(f, "#ifndef _GF_SVG_SANI_NODES_H\n"); + fprintf(f, "#define _GF_SVG_SANI_NODES_H\n\n"); + } else if (generation_mode == 3) { + fprintf(f, "#ifndef _GF_SVG_NODES_H\n"); + fprintf(f, "#define _GF_SVG_NODES_H\n\n"); + } + fprintf(f, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"); + } + return f; +} + +void EndFile(FILE *f, u32 type) +{ + if (!type) { + fprintf(f, "#ifdef __cplusplus\n}\n#endif\n\n"); + if (generation_mode == 1) fprintf(f, "\n\n#endif\t\t/*_GF_SVG_SA_NODES_H*/\n\n"); + if (generation_mode == 2) fprintf(f, "\n\n#endif\t\t/*_GF_SVG_SANI_NODES_H*/\n\n"); + if (generation_mode == 3) fprintf(f, "\n\n#endif\t\t/*_GF_SVG_NODES_H*/\n\n"); + } else { + fprintf(f, "\n"); + } + fclose(f); +} + +void generateAttributes(FILE *output, GF_List *attributes, Bool inDefine) +{ + u32 i; + for (i = 0; iimpl_type, att->implementation_name); + else + fprintf(output, "\t%s %s; \\\n", att->impl_type, att->implementation_name); + else + fprintf(output, "\t%s %s;\n", att->impl_type, att->implementation_name); + } +} + +/* +u32 generateAttributesGroupInfo(FILE *output, char * elt_imp_name, SVGGenAttrGrp *attgrp, u32 i) +{ + u32 att_index = i; + u32 k; + for (k=0; kattrgrps); k++) { + SVGGenAttrGrp *ag = gf_list_get(attgrp->attrgrps, k); + att_index = generateAttributesGroupInfo(output, elt_imp_name, ag, att_index); + } + for (k=0; kattrs); k++) { + SVGGenAttribute *at = gf_list_get(attgrp->attrs, k); + generateAttributeInfo(output, elt_imp_name, at, att_index++); + } + return att_index; +} +*/ + +void replaceIncludes(xmlDocPtr doc, xmlXPathContextPtr xpathCtx) +{ + int k; + xmlNodeSetPtr nodes; + xmlXPathObjectPtr xpathObj; + + /* Get all the RNG elements */ + xpathObj = xmlXPathEvalExpression("//rng:include", xpathCtx); + if(xpathObj == NULL || xpathObj->type != XPATH_NODESET) return; + + nodes = xpathObj->nodesetval; + + for (k = 0; k < nodes->nodeNr; k++) { + xmlNodePtr node = nodes->nodeTab[k]; + if (node->type == XML_ELEMENT_NODE) { + xmlChar *href; + xmlDocPtr sub_doc; + + href = xmlGetNoNsProp(node, "href"); + sub_doc = xmlParseFile(href); + xmlReplaceNode(nodes->nodeTab[k], xmlDocGetRootElement(sub_doc)); + } + } + xmlXPathFreeObject(xpathObj); +} + +int main(int argc, char **argv) +{ + xmlDocPtr doc = NULL; + xmlXPathContextPtr xpathCtx = NULL; + GF_List *svg_elements = NULL; + + xmlInitParser(); + LIBXML_TEST_VERSION + + doc = xmlParseFile(argv[1]); + if (!doc) { + printf("error: could not parse file %s\n", argv[1]); + return -1; + } + + xpathCtx = xmlXPathNewContext(doc); + if(xpathCtx == NULL) { + fprintf(stderr,"Error: unable to create new XPath context\n"); + xmlFreeDoc(doc); + return(-1); + } + xmlXPathRegisterNs(xpathCtx, RNG_PREFIX, RNG_NS); + xmlXPathRegisterNs(xpathCtx, RNGA_PREFIX, RNGA_NS); + xmlXPathRegisterNs(xpathCtx, SVGA_PREFIX, SVGA_NS); + + replaceIncludes(doc, xpathCtx); + xmlSaveFile("completerng_props.xml", doc); + + globalAttrGrp = gf_list_new(); + getAllGlobalAttrGrp(doc, xpathCtx); + + svg_elements = getElements(doc, xpathCtx); + svg_elements = sortElements(svg_elements); + + if (argv[2] && !strcmp(argv[2], "-html")) { + generate_table(svg_elements); + } else { + if (generation_mode == 1) generateSVGCode_V1(svg_elements); + if (generation_mode == 2) generateSVGCode_V2(svg_elements); + if (generation_mode == 3) generateSVGCode_V3(svg_elements); + } + + xmlXPathFreeContext(xpathCtx); + //xmlFreeDoc(doc); + + xmlCleanupParser(); + return 0; +} + + diff --git a/applications/generators/SVG/svggen.h b/applications/generators/SVG/svggen.h new file mode 100644 index 0000000..8913f31 --- /dev/null +++ b/applications/generators/SVG/svggen.h @@ -0,0 +1,194 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004-2005 + * All rights reserved + * + * This file is part of GPAC / SVG Scene Graph Generator sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef _SVGGEN_H_ +#define _SVGGEN_H_ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* if defined generates .c/.h directly in the appropriate GPAC source folders */ +#undef LOCAL_SVG_NODES + +/* + Modes for generating SVG code + - 1 means static allocation of attributes (including properties, use Tiny-1.2-NG) + - 2 means static allocation of attributes (only useful properties on nodes, use Tiny-1.2-NG-noproperties) + - 3 means dynamic allocation of attributes (including properties) +*/ +static u32 generation_mode = 3; + +#define RNG_NS "http://relaxng.org/ns/structure/1.0" +#define RNGA_NS "http://relaxng.org/ns/compatibility/annotations/1.0" +#define SVGA_NS "http://www.w3.org/2005/02/svg-annotations" + +#define RNG_PREFIX "rng" +#define RNGA_PREFIX "rnga" +#define SVGA_PREFIX "svg" + +#define COPYRIGHT "/*\n * GPAC - Multimedia Framework C SDK\n *\n * Authors: Cyril Concolato - Jean Le Feuvre\n * Copyright (c)2004-200X ENST - All rights reserved\n *\n * This file is part of GPAC / SVG Scene Graph sub-project\n *\n * GPAC is free software; you can redistribute it and/or modify\n * it under the terms of the GNU Lesser General Public License as published by\n * the Free Software Foundation; either version 2, or (at your option)\n * any later version.\n *\n * GPAC is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Lesser General Public License for more details. \n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; see the file COPYING. If not, write to\n * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n */\n" + + +/* + type declarations +*/ + +typedef struct +{ + xmlChar *svg_name; + char implementation_name[50]; + + Bool has_svg_generic; + Bool has_xml_generic; + Bool has_media_properties; + Bool has_properties; + Bool has_opacity_properties; + Bool has_focus; + Bool has_xlink; + Bool has_timing; + Bool has_sync; + Bool has_animation; + Bool has_conditional; + Bool has_transform; + Bool has_xy; + + GF_List *attributes; + GF_List *generic_attributes; + + u32 nb_atts; +} SVGGenElement; + +typedef struct { + xmlChar *svg_name; + char implementation_name[50]; + xmlChar *svg_type; + char impl_type[50]; + u8 animatable; + u8 inheritable; + Bool optional; + xmlChar *default_value; + u32 index; +} SVGGenAttribute; +SVGGenAttribute *NewSVGGenAttribute(); + +typedef struct { + char *name; + char imp_name[50]; + GF_List *attrs; + GF_List *attrgrps; +} SVGGenAttrGrp; + + + +/******************************************* + * Structures needed for static allocation * + *******************************************/ + +static GF_List *globalAttrGrp; + +/* SVG Generic */ +static char *core[] = { "id", "class", "xml:id", "xml:base", "xml:lang", "xml:space", "externalResourceRequired" }; + +/* Media Properties */ +static char *media_properties[] = { + "audio-level", "display", "image-rendering", "pointer-events", "shape-rendering", "text-rendering", + "viewport-fill", "viewport-fill-opacity", "visibility" +}; + +/* others */ +static char *other_properties[] = { + "color", "color-rendering", "display-align", "fill", "fill-opacity", "fill-rule", + "font-family", "font-size", "font-style", "font-weight", "line-increment", + "solid-color", "solid-opacity", "stop-color", "stop-opacity", + "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", + "stroke-opacity", "stroke-width", "text-align", "text-anchor", "vector-effect" +}; + +/* only opacity on image */ +static char *opacity_properties[] = { + "opacity" +}; + +/* Focus */ +static char *focus[] = { + "focusHighlight", "focusable", "nav-down", "nav-down-left", "nav-down-right", + "nav-left", "nav-next", "nav-prev", "nav-right", "nav-up", "nav-up-left", "nav-up-right" +}; + +/* Xlink */ +static char *xlink[] = { + "xlink:href", "xlink:show", "xlink:title", "xlink:actuate", "xlink:role", "xlink:arcrole", "xlink:type" +}; + +/* Timing */ +static char *timing[] = { + "begin", "end", "dur", "repeatCount", "repeatDur", "restart", "min", "max", "fill", "clipBegin", "clipEnd" +}; + +/* Sync */ +static char *sync[] = { + "syncBehavior", "syncBehaviorDefault", "syncTolerance", "syncToleranceDefault", "syncMaster", "syncReference" +}; + +/* Animation */ +static char *anim[] = { + "attributeName", "attributeType", "to", "from", "by", "values", + "type", "calcMode", "keySplines", "keyTimes", "accumulate", "additive", "lsr:enabled" +}; + +/* Conditional Processing */ +static char *conditional[] = { + "requiredExtensions", "requiredFeatures", "requiredFonts", "requiredFormats", "systemLanguage" +}; + +typedef struct { + int array_length; + char **array; // mapping of constructs to the RNG definition +} _atts; + +static _atts generic_attributes[] = { + { 7, core }, + { 26, other_properties }, + { 9, media_properties }, + { 1, opacity_properties }, + { 12, focus }, + { 7, xlink }, + { 11, timing }, + { 6, sync }, + { 13, anim }, + { 5, conditional} +}; + +FILE *BeginFile(u32 type); +void EndFile(FILE *f, u32 type); + + +#endif // _SVGGEN_H_ diff --git a/applications/generators/SVG/v1.c b/applications/generators/SVG/v1.c new file mode 100644 index 0000000..785866e --- /dev/null +++ b/applications/generators/SVG/v1.c @@ -0,0 +1,607 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004-2005 + * All rights reserved + * + * This file is part of GPAC / SVG Scene Graph Generator sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "svggen.h" + +void generateNode(FILE *output, SVGGenElement* svg_elt) +{ + fprintf(output, "typedef struct _tagSVG_SA_%sElement\n{\n", svg_elt->implementation_name); + + if (svg_elt->has_transform) { + fprintf(output, "\tTRANSFORMABLE_SVG_ELEMENT\n"); + } else { + fprintf(output, "\tBASE_SVG_ELEMENT\n"); + } + + if (!strcmp(svg_elt->implementation_name, "conditional")) { + fprintf(output, "\tSVGCommandBuffer updates;\n"); + } + + generateAttributes(output, svg_elt->attributes, 0); + + /*special case for handler node*/ + if (!strcmp(svg_elt->implementation_name, "handler")) { + fprintf(output, "\tvoid (*handle_event)(GF_Node *hdl, GF_DOM_Event *event);\n"); + } + fprintf(output, "} SVG_SA_%sElement;\n\n\n", svg_elt->implementation_name); +} + + +void generateAttributeInfo(FILE *output, char * elt_imp_name, SVGGenAttribute *att, u32 i) +{ + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"%s\";\n", att->svg_name); + fprintf(output, "\t\t\tinfo->fieldType = %s_datatype;\n", att->impl_type); + fprintf(output, "\t\t\tinfo->far_ptr = & ((SVG_SA_%sElement *)node)->%s;\n", elt_imp_name, att->implementation_name); + fprintf(output, "\t\t\treturn GF_OK;\n"); +} + +u32 generateCoreInfo(FILE *output, SVGGenElement *elt, u32 start) +{ + u32 i = start; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"id\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_ID_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = gf_node_get_name_address(node);\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"xml:id\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_ID_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = gf_node_get_name_address(node);\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"class\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_String_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVG_SA_Element *)node)->core->_class;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"xml:lang\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_LanguageID_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVG_SA_Element *)node)->core->lang;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"xml:base\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_String_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVG_SA_Element *)node)->core->base;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"xml:space\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = XML_Space_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVG_SA_Element *)node)->core->space;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"externalResourcesRequired\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_Boolean_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVG_SA_Element *)node)->core->eRR;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + + return i; +} + +void generateAttributeInfoFlat(FILE *output, char *pointer, char *name, char *type, u32 i) +{ + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"%s\";\n", name); + fprintf(output, "\t\t\tinfo->fieldType = %s_datatype;\n", type); + fprintf(output, "\t\t\tinfo->far_ptr = &%s;\n", pointer); + fprintf(output, "\t\t\treturn GF_OK;\n"); +} + +u32 generateTransformInfo(FILE *output, SVGGenElement *elt, u32 start) +{ + u32 i = start; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"transform\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_Transform_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVGTransformableElement *)node)->transform;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + return i; +} + +u32 generateMotionTransformInfo(FILE *output, SVGGenElement *elt, u32 start) +{ + u32 i = start; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"motionTransform\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_Motion_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = ((SVGTransformableElement *)node)->motionTransform;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + return i; +} + +u32 generateXYInfo(FILE *output, SVGGenElement *elt, u32 start) +{ + u32 i = start; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"x\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_Coordinate_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVGTransformableElement *)node)->xy.x;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"y\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_Coordinate_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVGTransformableElement *)node)->xy.y;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + return i; +} + +u32 generateGenericInfo(FILE *output, SVGGenElement *elt, u32 index, char *pointer_root, u32 start) +{ + u32 i = start; + int k; + for (k=0; k < generic_attributes[index].array_length; k++) { + char *att_name = generic_attributes[index].array[k]; + SVGGenAttribute *a = findAttribute(elt, att_name); + if (a) { + char pointer[500]; + if (strstr(att_name, "xlink:")) { + sprintf(pointer, "%s%s", pointer_root, att_name+6); + } else if (strstr(att_name, "xml:")) { + sprintf(pointer, "%s%s", pointer_root, att_name+4); + } else { + char imp_name[50]; + svgNameToImplementationName(att_name, imp_name); + sprintf(pointer, "%s%s", pointer_root, imp_name); + } + generateAttributeInfoFlat(output, pointer, a->svg_name, a->impl_type, i); + i++; + } + } + return i; +} + +u32 generateIndexInfo(FILE *output, SVGGenElement *elt, u32 index, u32 start) +{ + u32 i = start; + int k; + for (k=0; k < generic_attributes[index].array_length; k++) { + char *att_name = generic_attributes[index].array[k]; + SVGGenAttribute *a = findAttribute(elt, att_name); + if (a) { + fprintf(output, "\tif(!strcmp(\"%s\", name)) return %d;\n", att_name, i); + i++; + } + } + return i; +} + +void generateNodeImpl(FILE *output, SVGGenElement* svg_elt) +{ + u32 i; + + /***************************************************/ + /* Constructor */ + /***************************************************/ + fprintf(output, "void *gf_svg_new_%s()\n{\n\tSVG_SA_%sElement *p;\n", svg_elt->implementation_name,svg_elt->implementation_name); + fprintf(output, "\tGF_SAFEALLOC(p, SVG_SA_%sElement);\n\tif (!p) return NULL;\n\tgf_node_setup((GF_Node *)p, TAG_SVG_%s);\n\tgf_sg_parent_setup((GF_Node *) p);\n",svg_elt->implementation_name,svg_elt->implementation_name); + + fprintf(output, "\tgf_svg_sa_init_core((SVG_SA_Element *)p);\n"); + if (svg_elt->has_properties || + svg_elt->has_media_properties || + svg_elt->has_opacity_properties) { + fprintf(output, "\tgf_svg_sa_init_properties((SVG_SA_Element *)p);\n"); + } + if (svg_elt->has_focus) { + fprintf(output, "\tgf_svg_sa_init_focus((SVG_SA_Element *)p);\n"); + } + if (svg_elt->has_xlink) { + fprintf(output, "\tgf_svg_sa_init_xlink((SVG_SA_Element *)p);\n"); + } + if (svg_elt->has_timing) { + fprintf(output, "\tgf_svg_sa_init_timing((SVG_SA_Element *)p);\n"); + } + if (svg_elt->has_sync) { + fprintf(output, "\tgf_svg_sa_init_sync((SVG_SA_Element *)p);\n"); + } + if (svg_elt->has_animation){ + fprintf(output, "\tgf_svg_sa_init_anim((SVG_SA_Element *)p);\n"); + } + if (svg_elt->has_conditional) { + fprintf(output, "\tgf_svg_sa_init_conditional((SVG_SA_Element *)p);\n"); + } + + if (svg_elt->has_transform) { + fprintf(output, "\tgf_mx2d_init(p->transform.mat);\n"); + } + + if (!strcmp(svg_elt->implementation_name, "conditional")) { + fprintf(output, "\tgf_svg_sa_init_lsr_conditional(&p->updates);\n"); + fprintf(output, "\tgf_svg_sa_init_timing((SVG_SA_Element *)p);\n"); + + } + + for (i = 0; i < gf_list_count(svg_elt->attributes); i++) { + SVGGenAttribute *att = gf_list_get(svg_elt->attributes, i); + /* Initialization of complex types */ + if ( !strcmp("SVG_Points", att->impl_type) || + !strcmp("SVG_Coordinates", att->impl_type) || + !strcmp("SMIL_KeyPoints", att->impl_type)) { + fprintf(output, "\tp->%s = gf_list_new();\n", att->implementation_name); + } else if (!strcmp("SVG_PathData", att->impl_type) && !strcmp(svg_elt->svg_name, "animateMotion")) { + fprintf(output, "#ifdef USE_GF_PATH\n"); + fprintf(output, "\tgf_path_reset(&p->path);\n"); + fprintf(output, "#else\n"); + fprintf(output, "\tp->path.commands = gf_list_new();\n"); + fprintf(output, "\tp->path.points = gf_list_new();\n"); + fprintf(output, "#endif\n"); + } else if (!strcmp("SVG_PathData", att->impl_type)) { + fprintf(output, "#ifdef USE_GF_PATH\n"); + fprintf(output, "\tgf_path_reset(&p->d);\n"); + fprintf(output, "#else\n"); + fprintf(output, "\tp->d.commands = gf_list_new();\n"); + fprintf(output, "\tp->d.points = gf_list_new();\n"); + fprintf(output, "#endif\n"); + } else if (!strcmp(att->svg_name, "lsr:enabled")) { + fprintf(output, "\tp->lsr_enabled = 1;\n"); + } + } + /*some default values*/ + if (!strcmp(svg_elt->svg_name, "svg")) { + fprintf(output, "\tp->width.type = SVG_NUMBER_PERCENTAGE;\n"); + fprintf(output, "\tp->width.value = INT2FIX(100);\n"); + fprintf(output, "\tp->height.type = SVG_NUMBER_PERCENTAGE;\n"); + fprintf(output, "\tp->height.value = INT2FIX(100);\n"); + } + else if (!strcmp(svg_elt->svg_name, "solidColor")) { + fprintf(output, "\tp->properties->solid_opacity.value = FIX_ONE;\n"); + } + else if (!strcmp(svg_elt->svg_name, "stop")) { + fprintf(output, "\tp->properties->stop_opacity.value = FIX_ONE;\n"); + } + else if (!strcmp(svg_elt->svg_name, "linearGradient")) { + fprintf(output, "\tp->x2.value = FIX_ONE;\n"); + fprintf(output, "\tgf_mx2d_init(p->gradientTransform.mat);\n"); + } + else if (!strcmp(svg_elt->svg_name, "radialGradient")) { + fprintf(output, "\tp->cx.value = FIX_ONE/2;\n"); + fprintf(output, "\tp->cy.value = FIX_ONE/2;\n"); + fprintf(output, "\tp->r.value = FIX_ONE/2;\n"); + fprintf(output, "\tgf_mx2d_init(p->gradientTransform.mat);\n"); + fprintf(output, "\tp->fx.value = FIX_ONE/2;\n"); + fprintf(output, "\tp->fy.value = FIX_ONE/2;\n"); + } + else if (!strcmp(svg_elt->svg_name, "video") || !strcmp(svg_elt->svg_name, "audio") || !strcmp(svg_elt->svg_name, "animation")) { + fprintf(output, "\tp->timing->dur.type = SMIL_DURATION_MEDIA;\n"); + } + fprintf(output, "\treturn p;\n}\n\n"); + + /***************************************************/ + /* Destructor */ + /***************************************************/ + fprintf(output, "static void gf_svg_sa_%s_del(GF_Node *node)\n{\n", svg_elt->implementation_name); + fprintf(output, "\tSVG_SA_%sElement *p = (SVG_SA_%sElement *)node;\n", svg_elt->implementation_name, svg_elt->implementation_name); + + fprintf(output, "\tgf_svg_sa_reset_base_element((SVG_SA_Element *)p);\n"); + + if (!strcmp(svg_elt->implementation_name, "conditional")) { + fprintf(output, "\tgf_svg_sa_reset_lsr_conditional(&p->updates);\n"); + } + else if (!strcmp(svg_elt->implementation_name, "a")) { + fprintf(output, "\tif (p->target) free(p->target);\n"); + } + + for (i = 0; i < gf_list_count(svg_elt->attributes); i++) { + SVGGenAttribute *att = gf_list_get(svg_elt->attributes, i); + if (!strcmp("SMIL_KeyPoints", att->impl_type)) { + fprintf(output, "\tgf_smil_delete_key_types(p->%s);\n", att->implementation_name); + } else if (!strcmp("SVG_Coordinates", att->impl_type)) { + fprintf(output, "\tgf_svg_delete_coordinates(p->%s);\n", att->implementation_name); + } else if (!strcmp("SVG_Points", att->impl_type)) { + fprintf(output, "\tgf_svg_delete_points(p->%s);\n", att->implementation_name); + } else if (!strcmp("SVG_PathData", att->impl_type)) { + if (!strcmp(svg_elt->svg_name, "animateMotion")) { + fprintf(output, "\tgf_svg_reset_path(p->path);\n"); + } else { + fprintf(output, "\tgf_svg_reset_path(p->d);\n"); + } + } else if (!strcmp("XMLRI", att->impl_type)) { + fprintf(output, "\tgf_svg_reset_iri(node->sgprivate->scenegraph, &p->%s);\n", att->implementation_name); + } else if (!strcmp("SVG_FontFamily", att->impl_type)) { + fprintf(output, "\tif (p->%s.value) free(p->%s.value);\n", att->implementation_name, att->implementation_name); + } else if (!strcmp("SVG_String", att->impl_type) || !strcmp("SVG_ContentType", att->impl_type)) { + fprintf(output, "\tfree(p->%s);\n", att->implementation_name); + } + } + if (svg_elt->has_transform) { + fprintf(output, "\tif (p->motionTransform) free(p->motionTransform);\n"); + } + + fprintf(output, "\tgf_sg_parent_reset((GF_Node *) p);\n"); + fprintf(output, "\tgf_node_free((GF_Node *)p);\n"); + fprintf(output, "}\n\n"); + + /***************************************************/ + /* Attribute Access */ + /***************************************************/ + fprintf(output, "static GF_Err gf_svg_sa_%s_get_attribute(GF_Node *node, GF_FieldInfo *info)\n{\n", svg_elt->implementation_name); + fprintf(output, "\tswitch (info->fieldIndex) {\n"); + svg_elt->nb_atts = 0; + svg_elt->nb_atts = generateCoreInfo(output, svg_elt, svg_elt->nb_atts); + + if (svg_elt->has_media_properties) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 2, "((SVG_SA_Element *)node)->properties->", svg_elt->nb_atts); + if (svg_elt->has_properties) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 1, "((SVG_SA_Element *)node)->properties->", svg_elt->nb_atts); + if (svg_elt->has_opacity_properties) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 3, "((SVG_SA_Element *)node)->properties->", svg_elt->nb_atts); + if (svg_elt->has_focus) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 4, "((SVG_SA_Element *)node)->focus->", svg_elt->nb_atts); + if (svg_elt->has_xlink) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 5, "((SVG_SA_Element *)node)->xlink->", svg_elt->nb_atts); + if (svg_elt->has_timing) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 6, "((SVG_SA_Element *)node)->timing->", svg_elt->nb_atts); + if (svg_elt->has_sync) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 7, "((SVG_SA_Element *)node)->sync->", svg_elt->nb_atts); + if (svg_elt->has_animation) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 8, "((SVG_SA_Element *)node)->anim->", svg_elt->nb_atts); + if (svg_elt->has_conditional) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 9, "((SVG_SA_Element *)node)->conditional->", svg_elt->nb_atts); + if (svg_elt->has_transform) { + svg_elt->nb_atts = generateTransformInfo(output, svg_elt, svg_elt->nb_atts); + svg_elt->nb_atts = generateMotionTransformInfo(output, svg_elt, svg_elt->nb_atts); + } + if (svg_elt->has_xy) + svg_elt->nb_atts = generateXYInfo(output, svg_elt, svg_elt->nb_atts); + + for (i = 0; i < gf_list_count(svg_elt->attributes); i++) { + SVGGenAttribute *att = gf_list_get(svg_elt->attributes, i); + generateAttributeInfo(output, svg_elt->implementation_name, att, svg_elt->nb_atts++); + } + fprintf(output, "\t\tdefault: return GF_BAD_PARAM;\n\t}\n}\n\n"); + + /***************************************************/ + /* gf_svg_sa_%s_get_attribute_index_from_name */ + /***************************************************/ + fprintf(output, "s32 gf_svg_sa_%s_get_attribute_index_from_name(char *name)\n{\n", svg_elt->implementation_name); + { + u32 att_index = 0; + fprintf(output, "\tif(!strcmp(\"id\", name)) return %d;\n", att_index); + att_index++; + fprintf(output, "\tif(!strcmp(\"xml:id\", name)) return %d;\n", att_index); + att_index++; + fprintf(output, "\tif(!strcmp(\"class\", name)) return %d;\n", att_index); + att_index++; + fprintf(output, "\tif(!strcmp(\"xml:lang\", name)) return %d;\n", att_index); + att_index++; + fprintf(output, "\tif(!strcmp(\"xml:base\", name)) return %d;\n", att_index); + att_index++; + fprintf(output, "\tif(!strcmp(\"xml:space\", name)) return %d;\n", att_index); + att_index++; + fprintf(output, "\tif(!strcmp(\"externalResourcesRequired\", name)) return %d;\n", att_index); + att_index++; + if (svg_elt->has_media_properties) + att_index = generateIndexInfo(output, svg_elt, 2, att_index); + if (svg_elt->has_properties) + att_index = generateIndexInfo(output, svg_elt, 1, att_index); + if (svg_elt->has_opacity_properties) + att_index = generateIndexInfo(output, svg_elt, 3, att_index); + if (svg_elt->has_focus) + att_index = generateIndexInfo(output, svg_elt, 4, att_index); + if (svg_elt->has_xlink) + att_index = generateIndexInfo(output, svg_elt, 5, att_index); + if (svg_elt->has_timing) + att_index = generateIndexInfo(output, svg_elt, 6, att_index); + if (svg_elt->has_sync) + att_index = generateIndexInfo(output, svg_elt, 7, att_index); + if (svg_elt->has_animation) + att_index = generateIndexInfo(output, svg_elt, 8, att_index); + if (svg_elt->has_conditional) + att_index = generateIndexInfo(output, svg_elt, 9, att_index); + if (svg_elt->has_transform) { + fprintf(output, "\tif(!strcmp(\"transform\", name)) return %d;\n", att_index); + att_index++; + /*motionTransform*/ + fprintf(output, "\tif(!strcmp(\"motionTransform\", name)) return %d;\n", att_index); + att_index++; + } + if (svg_elt->has_xy) { + fprintf(output, "\tif(!strcmp(\"x\", name)) return %d;\n", att_index); + att_index++; + fprintf(output, "\tif(!strcmp(\"y\", name)) return %d;\n", att_index); + att_index++; + } + + for (i = 0; i < gf_list_count(svg_elt->attributes); i++) { + SVGGenAttribute *att = gf_list_get(svg_elt->attributes, i); + fprintf(output, "\tif(!strcmp(\"%s\", name)) return %d;\n", att->svg_name, att_index); + att_index++; + } + } + fprintf(output, "\treturn -1;\n}\n\n"); +} + +void generateSVGCode_V1(GF_List *svg_elements) +{ + FILE *output; + u32 i; + + /***************************************************/ + /***************************************************/ + /*************** Creating .h file ******************/ + /***************************************************/ + /***************************************************/ + output = BeginFile(0); + fprintf(output, "#include \n\n\n"); + fprintf(output, "/* Definition of SVG element internal tags */\n"); + fprintf(output, "/* TAG names are made of \"TAG_SVG\" + SVG element name (with - replaced by _) */\n"); + + /* write all tags */ + fprintf(output, "enum {\n"); + for (i=0; iimplementation_name); + } else { + fprintf(output, ",\n\tTAG_SVG_%s", elt->implementation_name); + } + } + + fprintf(output, ",\n\t/*undefined elements (when parsing) use this tag*/\n\tTAG_SVG_UndefinedElement\n};\n\n"); + + fprintf(output, "/******************************************\n"); + fprintf(output, "* SVG Elements structure definitions *\n"); + fprintf(output, "*******************************************/\n"); + for (i=0; i\n\n"); + + fprintf(output, "#ifndef GPAC_DISABLE_SVG\n\n"); + fprintf(output, "#include \n\n"); + fprintf(output, "#ifdef GPAC_ENABLE_SVG_SA\n\n"); + for (i=0; iimplementation_name,elt->implementation_name); + } + fprintf(output, "\t\tdefault: return NULL;\n\t}\n}\n\n"); + + /***************************************************/ + /* void gf_svg_sa_element_del(SVG_SA_Element *elt) */ + /***************************************************/ + fprintf(output, "void gf_svg_sa_element_del(SVG_SA_Element *elt)\n{\n"); + fprintf(output, "\tGF_Node *node = (GF_Node *)elt;\n"); + fprintf(output, "\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iimplementation_name, elt->implementation_name); + } + fprintf(output, "\t\tdefault: return;\n\t}\n}\n\n"); + + /***************************************************/ + /* u32 gf_svg_sa_get_attribute_count(SVG_SA_Element *elt) */ + /***************************************************/ + fprintf(output, "u32 gf_svg_sa_get_attribute_count(GF_Node *node)\n{\n"); + fprintf(output, "\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iimplementation_name, elt->nb_atts); + } + fprintf(output, "\t\tdefault: return 0;\n\t}\n}\n\n"); + + /***********************************************************************/ + /* GF_Err gf_svg_sa_get_attribute_info(GF_Node *node, GF_FieldInfo *info) */ + /***********************************************************************/ + fprintf(output, "GF_Err gf_svg_sa_get_attribute_info(GF_Node *node, GF_FieldInfo *info)\n{\n"); + fprintf(output, "\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iimplementation_name, elt->implementation_name); + } + fprintf(output, "\t\tdefault: return GF_BAD_PARAM;\n\t}\n}\n\n"); + + /****************************************************************/ + /* u32 gf_svg_sa_node_type_by_class_name(const char *element_name) */ + /****************************************************************/ + fprintf(output, "u32 gf_svg_sa_node_type_by_class_name(const char *element_name)\n{\n\tif (!element_name) return TAG_UndefinedNode;\n"); + for (i=0; isvg_name, elt->implementation_name); + } + fprintf(output, "\treturn TAG_UndefinedNode;\n}\n\n"); + + + /***************************************************/ + /* const char *gf_svg_sa_get_element_name(u32 tag) */ + /***************************************************/ + fprintf(output, "const char *gf_svg_sa_get_element_name(u32 tag)\n{\n\tswitch(tag) {\n"); + for (i=0; iimplementation_name, elt->svg_name); + } + fprintf(output, "\tdefault: return \"UndefinedNode\";\n\t}\n}\n\n"); + + /***************************************************/ + /* const char *gf_svg_sa_get_attribute_index_by_name(u32 tag) */ + /***************************************************/ + fprintf(output, "s32 gf_svg_sa_get_attribute_index_by_name(GF_Node *node, char *name)\n{\n\tswitch(node->sgprivate->tag) {\n"); + for (i=0; iimplementation_name, elt->implementation_name); + } + fprintf(output, "\tdefault: return -1;\n\t}\n}\n\n"); + + /***************************************************/ + /* Bool gf_svg_is_element_transformable(u32 tag) */ + /***************************************************/ + fprintf(output, "Bool gf_svg_is_element_transformable(u32 tag)\n{\n\tswitch(tag) {\n"); + for (i=0; iimplementation_name); + if (elt->has_transform) fprintf(output, "return 1;\n"); + else fprintf(output, "return 0;\n"); + } + fprintf(output, "\tdefault: return 0;\n\t}\n}\n"); + + fprintf(output, "#endif /*GPAC_ENABLE_SVG_SA*/\n"); + fprintf(output, "#endif /*GPAC_DISABLE_SVG*/\n\n"); + EndFile(output, 1); + + generate_laser_tables(svg_elements); +} + diff --git a/applications/generators/SVG/v2.c b/applications/generators/SVG/v2.c new file mode 100644 index 0000000..8c549e6 --- /dev/null +++ b/applications/generators/SVG/v2.c @@ -0,0 +1,462 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004-2005 + * All rights reserved + * + * This file is part of GPAC / SVG Scene Graph Generator sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "svggen.h" + +void generateAttributes2(FILE *output, GF_List *attributes) +{ + u32 i; + for (i = 0; iimplementation_name, "transform")) continue; + fprintf(output, "\t%s %s;\n", att->impl_type, att->implementation_name); + } +} + +void generateNode2(FILE *output, SVGGenElement* svg_elt) +{ + fprintf(output, "typedef struct _tagSVG_SANI_%sElement\n{\n", svg_elt->implementation_name); + + if (svg_elt->has_transform) { + fprintf(output, "\tTRANSFORMABLE_SVG_SANI_ELEMENT\n"); + } else { + fprintf(output, "\tBASE_SVG_SANI_ELEMENT\n"); + } + + if (!strcmp(svg_elt->implementation_name, "conditional")) { + fprintf(output, "\tSVGCommandBuffer updates;\n"); + } + + generateAttributes2(output, svg_elt->attributes); + + /*special case for handler node*/ + if (!strcmp(svg_elt->implementation_name, "handler")) { + fprintf(output, "\tvoid (*handle_event)(GF_Node *hdl, GF_DOM_Event *event);\n"); + } + fprintf(output, "} SVG_SANI_%sElement;\n\n\n", svg_elt->implementation_name); +} + +void generateAttributeInfo2(FILE *output, char * elt_imp_name, SVGGenAttribute *att, u32 i) +{ + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"%s\";\n", att->svg_name); + fprintf(output, "\t\t\tinfo->fieldType = %s_datatype;\n", att->impl_type); + fprintf(output, "\t\t\tinfo->far_ptr = & ((SVG_SANI_%sElement *)node)->%s;\n", elt_imp_name, att->implementation_name); + fprintf(output, "\t\t\treturn GF_OK;\n"); +} + +u32 generateTransformInfo2(FILE *output, SVGGenElement *elt, u32 start) +{ + u32 i = start; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"transform\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_Transform_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVG_SANI_TransformableElement *)node)->transform;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + return i; +} + +u32 generateMotionTransformInfo2(FILE *output, SVGGenElement *elt, u32 start) +{ + u32 i = start; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"motionTransform\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_Transform_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = ((SVG_SANI_TransformableElement *)node)->motionTransform;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + return i; +} + +u32 generateXYInfo2(FILE *output, SVGGenElement *elt, u32 start) +{ + u32 i = start; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"x\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_Coordinate_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVG_SANI_TransformableElement *)node)->xy.x;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + + fprintf(output, "\t\tcase %d:\n", i); + fprintf(output, "\t\t\tinfo->name = \"y\";\n"); + fprintf(output, "\t\t\tinfo->fieldType = SVG_Coordinate_datatype;\n"); + fprintf(output, "\t\t\tinfo->far_ptr = &((SVG_SANI_TransformableElement *)node)->xy.y;\n"); + fprintf(output, "\t\t\treturn GF_OK;\n"); + i++; + return i; +} + +void generateNodeImpl2(FILE *output, SVGGenElement* svg_elt) +{ + u32 i; + + /* Constructor */ + fprintf(output, "void *gf_svg_sani_new_%s()\n{\n\tSVG_SANI_%sElement *p;\n", svg_elt->implementation_name,svg_elt->implementation_name); + fprintf(output, "\tGF_SAFEALLOC(p, SVG_SANI_%sElement);\n\tif (!p) return NULL;\n\tgf_node_setup((GF_Node *)p, TAG_SVG_SANI_%s);\n\tgf_sg_parent_setup((GF_Node *) p);\n",svg_elt->implementation_name,svg_elt->implementation_name); + + fprintf(output, "\tgf_svg_sani_init_core((SVG_SANI_Element *)p);\n"); + if (svg_elt->has_focus) { + fprintf(output, "\tgf_svg_sani_init_focus((SVG_SANI_Element *)p);\n"); + } + if (svg_elt->has_xlink) { + fprintf(output, "\tgf_svg_sani_init_xlink((SVG_SANI_Element *)p);\n"); + } + if (svg_elt->has_timing) { + fprintf(output, "\tgf_svg_sani_init_timing((SVG_SANI_Element *)p);\n"); + } + if (svg_elt->has_sync) { + fprintf(output, "\tgf_svg_sani_init_sync((SVG_SANI_Element *)p);\n"); + } + if (svg_elt->has_animation){ + fprintf(output, "\tgf_svg_sani_init_anim((SVG_SANI_Element *)p);\n"); + } + if (svg_elt->has_conditional) { + fprintf(output, "\tgf_svg_sani_init_conditional((SVG_SANI_Element *)p);\n"); + } + + if (svg_elt->has_transform) { + fprintf(output, "\tgf_mx2d_init(p->transform.mat);\n"); + } + + if (!strcmp(svg_elt->implementation_name, "conditional")) { + fprintf(output, "\tgf_svg_sa_init_lsr_conditional(&p->updates);\n"); + fprintf(output, "\tgf_svg_sani_init_timing((SVG_SANI_Element *)p);\n"); + + } + + for (i = 0; i < gf_list_count(svg_elt->attributes); i++) { + SVGGenAttribute *att = gf_list_get(svg_elt->attributes, i); + + /* forcing initialization of old-properties */ + if (!strcmp("audio-level", att->svg_name)) { + fprintf(output, "\tp->audio_level.type = SVG_NUMBER_VALUE;\n"); + fprintf(output, "\tp->audio_level.value = FIX_ONE;\n"); + } else if (!strcmp("display", att->svg_name)) { + fprintf(output, "\tp->display = SVG_DISPLAY_INLINE;\n"); + } else if (!strcmp("display-align", att->svg_name)) { + fprintf(output, "\tp->display_align = SVG_DISPLAYALIGN_AUTO;\n"); + } else if (!strcmp("fill", att->svg_name)) { + fprintf(output, "\tp->fill.type = SVG_PAINT_COLOR;\n"); + fprintf(output, "\tp->fill.color.type = SVG_COLOR_RGBCOLOR;\n"); + } else if (!strcmp("fill-opacity", att->svg_name)) { + fprintf(output, "\tp->fill_opacity.type = SVG_NUMBER_VALUE;\n"); + fprintf(output, "\tp->fill_opacity.value = FIX_ONE;\n"); + } else if (!strcmp("fill-rule", att->svg_name)) { + fprintf(output, "\tp->fill_rule = SVG_FILLRULE_NONZERO;\n"); + } else if (!strcmp("font-family", att->svg_name)) { + fprintf(output, "\tp->font_family.type = SVG_FONTFAMILY_VALUE;\n"); + fprintf(output, "\tp->font_family.value = strdup(\"Arial\");\n"); + } else if (!strcmp("font-size", att->svg_name)) { + fprintf(output, "\tp->font_size.type = SVG_NUMBER_VALUE;\n"); + fprintf(output, "\tp->font_size.value = 12*FIX_ONE;\n"); + } else if (!strcmp("font-style", att->svg_name)) { + fprintf(output, "\tp->font_style = SVG_FONTSTYLE_NORMAL;\n"); + } else if (!strcmp("font-variant", att->svg_name)) { + fprintf(output, "\tp->font_variant = SVG_FONTVARIANT_NORMAL;\n"); + } else if (!strcmp("font-weight", att->svg_name)) { + fprintf(output, "\tp->font_weight = SVG_FONTWEIGHT_NORMAL;\n"); + } else if (!strcmp("line-increment", att->svg_name)) { + fprintf(output, "\tp->line_increment.type = SVG_NUMBER_AUTO;\n"); + fprintf(output, "\tp->line_increment.value = FIX_ONE;\n"); + } else if (!strcmp("opacity", att->svg_name)) { + fprintf(output, "\tp->opacity.type = SVG_NUMBER_VALUE;\n"); + fprintf(output, "\tp->opacity.value = FIX_ONE;\n"); + } else if (!strcmp("solid-color", att->svg_name)) { + fprintf(output, "\tp->solid_color.type = SVG_PAINT_COLOR;\n"); + fprintf(output, "\tp->solid_color.color.type = SVG_COLOR_RGBCOLOR;\n"); + } else if (!strcmp("solid-opacity", att->svg_name)) { + fprintf(output, "\tp->solid_opacity.type = SVG_NUMBER_VALUE;\n"); + fprintf(output, "\tp->solid_opacity.value = FIX_ONE;\n"); + } else if (!strcmp("solid-color", att->svg_name)) { + fprintf(output, "\tp->stop_color.type = SVG_PAINT_COLOR;\n"); + fprintf(output, "\tp->stop_color.color.type = SVG_COLOR_RGBCOLOR;\n"); + } else if (!strcmp("stop-opacity", att->svg_name)) { + fprintf(output, "\tp->stop_opacity.type = SVG_NUMBER_VALUE;\n"); + fprintf(output, "\tp->stop_opacity.value = FIX_ONE;\n"); + } else if (!strcmp("stroke", att->svg_name)) { + fprintf(output, "\tp->stroke.type = SVG_PAINT_NONE;\n"); + fprintf(output, "\tp->stroke.color.type = SVG_COLOR_RGBCOLOR;\n"); + } else if (!strcmp("stroke-dasharray", att->svg_name)) { + fprintf(output, "\tp->stroke_dasharray.type = SVG_STROKEDASHARRAY_NONE;\n"); + } else if (!strcmp("stroke-dashoffset", att->svg_name)) { + fprintf(output, "\tp->stroke_dashoffset.type = SVG_NUMBER_VALUE;\n"); + } else if (!strcmp("stroke-linecap", att->svg_name)) { + fprintf(output, "\tp->stroke_linecap = SVG_STROKELINECAP_BUTT;\n"); + } else if (!strcmp("stroke-linejoin", att->svg_name)) { + fprintf(output, "\tp->stroke_linejoin = SVG_STROKELINEJOIN_MITER;\n"); + } else if (!strcmp("stroke-miterlimit", att->svg_name)) { + fprintf(output, "\tp->stroke_miterlimit.type = SVG_NUMBER_VALUE;\n"); + fprintf(output, "\tp->stroke_miterlimit.value = 4*FIX_ONE;\n"); + } else if (!strcmp("stroke-opacity", att->svg_name)) { + fprintf(output, "\tp->stroke_opacity.type = SVG_NUMBER_VALUE;\n"); + fprintf(output, "\tp->stroke_opacity.value = FIX_ONE;\n"); + } else if (!strcmp("stroke-width", att->svg_name)) { + fprintf(output, "\tp->stroke_width.type = SVG_NUMBER_VALUE;\n"); + fprintf(output, "\tp->stroke_width.value = FIX_ONE;\n"); + } else if (!strcmp("text-align", att->svg_name)) { + fprintf(output, "\tp->text_align = SVG_TEXTALIGN_START;\n"); + } else if (!strcmp("text-anchor", att->svg_name)) { + fprintf(output, "\tp->text_anchor = SVG_TEXTANCHOR_START;\n"); + } else if (!strcmp("vector-effect", att->svg_name)) { + fprintf(output, "\tp->vector_effect = SVG_VECTOREFFECT_NONE;\n"); + } else if (!strcmp("viewport-fill", att->svg_name)) { + fprintf(output, "\tp->viewport_fill.type = SVG_PAINT_NONE;\n"); + } else if (!strcmp("viewport-fill-opacity", att->svg_name)) { + fprintf(output, "\tp->viewport_fill_opacity.type = SVG_NUMBER_VALUE;\n"); + fprintf(output, "\tp->viewport_fill_opacity.value = FIX_ONE;\n"); + } else if (!strcmp("visibility", att->svg_name)) { + fprintf(output, "\tp->visibility = SVG_VISIBILITY_VISIBLE;\n"); + } + + /* Initialization of complex types */ + if ( !strcmp("SVG_Points", att->impl_type) || + !strcmp("SVG_Coordinates", att->impl_type) || + !strcmp("SMIL_KeyPoints", att->impl_type)) { + fprintf(output, "\tp->%s = gf_list_new();\n", att->implementation_name); + } else if (!strcmp("SVG_PathData", att->impl_type) && !strcmp(svg_elt->svg_name, "animateMotion")) { + fprintf(output, "#ifdef USE_GF_PATH\n"); + fprintf(output, "\tgf_path_reset(&p->path);\n"); + fprintf(output, "#else\n"); + fprintf(output, "\tp->path.commands = gf_list_new();\n"); + fprintf(output, "\tp->path.points = gf_list_new();\n"); + fprintf(output, "#endif\n"); + } else if (!strcmp("SVG_PathData", att->impl_type)) { + fprintf(output, "#ifdef USE_GF_PATH\n"); + fprintf(output, "\tgf_path_reset(&p->d);\n"); + fprintf(output, "#else\n"); + fprintf(output, "\tp->d.commands = gf_list_new();\n"); + fprintf(output, "\tp->d.points = gf_list_new();\n"); + fprintf(output, "#endif\n"); + } else if (!strcmp(att->svg_name, "lsr:enabled")) { + fprintf(output, "\tp->lsr_enabled = 1;\n"); + } + } + /*some default values*/ + if (!strcmp(svg_elt->svg_name, "svg")) { + fprintf(output, "\tp->width.type = SVG_NUMBER_PERCENTAGE;\n"); + fprintf(output, "\tp->width.value = INT2FIX(100);\n"); + fprintf(output, "\tp->height.type = SVG_NUMBER_PERCENTAGE;\n"); + fprintf(output, "\tp->height.value = INT2FIX(100);\n"); + } + else if (!strcmp(svg_elt->svg_name, "linearGradient")) { + fprintf(output, "\tp->x2.value = FIX_ONE;\n"); + fprintf(output, "\tgf_mx2d_init(p->gradientTransform.mat);\n"); + } + else if (!strcmp(svg_elt->svg_name, "radialGradient")) { + fprintf(output, "\tp->cx.value = FIX_ONE/2;\n"); + fprintf(output, "\tp->cy.value = FIX_ONE/2;\n"); + fprintf(output, "\tp->r.value = FIX_ONE/2;\n"); + fprintf(output, "\tgf_mx2d_init(p->gradientTransform.mat);\n"); + fprintf(output, "\tp->fx.value = FIX_ONE/2;\n"); + fprintf(output, "\tp->fy.value = FIX_ONE/2;\n"); + } + else if (!strcmp(svg_elt->svg_name, "video") || !strcmp(svg_elt->svg_name, "audio") || !strcmp(svg_elt->svg_name, "animation")) { + fprintf(output, "\tp->timing->dur.type = SMIL_DURATION_MEDIA;\n"); + } + fprintf(output, "\treturn p;\n}\n\n"); + + /* Destructor */ + fprintf(output, "static void gf_svg_sani_%s_del(GF_Node *node)\n{\n", svg_elt->implementation_name); + fprintf(output, "\tSVG_SANI_%sElement *p = (SVG_SANI_%sElement *)node;\n", svg_elt->implementation_name, svg_elt->implementation_name); + fprintf(output, "\tgf_svg_sani_reset_base_element((SVG_SANI_Element *)p);\n"); + + if (!strcmp(svg_elt->implementation_name, "conditional")) { + fprintf(output, "\tgf_svg_sa_reset_lsr_conditional(&p->updates);\n"); + } + else if (!strcmp(svg_elt->implementation_name, "a")) { + fprintf(output, "\tif (p->target) free(p->target);\n"); + } + + for (i = 0; i < gf_list_count(svg_elt->attributes); i++) { + SVGGenAttribute *att = gf_list_get(svg_elt->attributes, i); + if (!strcmp("SMIL_KeyPoints", att->impl_type)) { + fprintf(output, "\tgf_smil_delete_key_types(p->%s);\n", att->implementation_name); + } else if (!strcmp("SVG_Coordinates", att->impl_type)) { + fprintf(output, "\tgf_svg_delete_coordinates(p->%s);\n", att->implementation_name); + } else if (!strcmp("SVG_Points", att->impl_type)) { + fprintf(output, "\tgf_svg_delete_points(p->%s);\n", att->implementation_name); + } else if (!strcmp("SVG_PathData", att->impl_type)) { + if (!strcmp(svg_elt->svg_name, "animateMotion")) { + fprintf(output, "\tgf_svg_reset_path(p->path);\n"); + } else { + fprintf(output, "\tgf_svg_reset_path(p->d);\n"); + } + } else if (!strcmp("XMLRI", att->impl_type)) { + fprintf(output, "\tgf_svg_reset_iri(node->sgprivate->scenegraph, &p->%s);\n", att->implementation_name); + } else if (!strcmp("SVG_FontFamily", att->impl_type)) { + fprintf(output, "\tif (p->%s.value) free(p->%s.value);\n", att->implementation_name, att->implementation_name); + } else if (!strcmp("SVG_String", att->impl_type) || !strcmp("SVG_ContentType", att->impl_type)) { + fprintf(output, "\tfree(p->%s);\n", att->implementation_name); + } + } + if (svg_elt->has_transform) { + fprintf(output, "\tif (p->motionTransform) free(p->motionTransform);\n"); + } + + fprintf(output, "\tgf_sg_parent_reset((GF_Node *) p);\n"); + fprintf(output, "\tgf_node_free((GF_Node *)p);\n"); + fprintf(output, "}\n\n"); + + /* Attribute Access */ + fprintf(output, "static GF_Err gf_svg_sani_%s_get_attribute(GF_Node *node, GF_FieldInfo *info)\n{\n", svg_elt->implementation_name); + fprintf(output, "\tswitch (info->fieldIndex) {\n"); + svg_elt->nb_atts = 0; + svg_elt->nb_atts = generateCoreInfo(output, svg_elt, svg_elt->nb_atts); + + if (svg_elt->has_focus) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 4, "((SVG_SANI_Element *)node)->focus->", svg_elt->nb_atts); + if (svg_elt->has_xlink) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 5, "((SVG_SANI_Element *)node)->xlink->", svg_elt->nb_atts); + if (svg_elt->has_timing) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 6, "((SVG_SANI_Element *)node)->timing->", svg_elt->nb_atts); + if (svg_elt->has_sync) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 7, "((SVG_SANI_Element *)node)->sync->", svg_elt->nb_atts); + if (svg_elt->has_animation) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 8, "((SVG_SANI_Element *)node)->anim->", svg_elt->nb_atts); + if (svg_elt->has_conditional) + svg_elt->nb_atts = generateGenericInfo(output, svg_elt, 9, "((SVG_SANI_Element *)node)->conditional->", svg_elt->nb_atts); + if (svg_elt->has_transform) { + svg_elt->nb_atts = generateTransformInfo2(output, svg_elt, svg_elt->nb_atts); + svg_elt->nb_atts = generateMotionTransformInfo2(output, svg_elt, svg_elt->nb_atts); + } + if (svg_elt->has_xy) + svg_elt->nb_atts = generateXYInfo2(output, svg_elt, svg_elt->nb_atts); + + for (i = 0; i < gf_list_count(svg_elt->attributes); i++) { + SVGGenAttribute *att = gf_list_get(svg_elt->attributes, i); + generateAttributeInfo2(output, svg_elt->implementation_name, att, svg_elt->nb_atts++); + } + fprintf(output, "\t\tdefault: return GF_BAD_PARAM;\n\t}\n}\n\n"); + +} + +void generateSVGCode_V2(GF_List *svg_elements) +{ + FILE *output; + u32 i; + + output = BeginFile(0); + fprintf(output, "#include \n\n\n"); + fprintf(output, "/* Definition of SVG 2 Alternate element internal tags */\n"); + fprintf(output, "/* TAG names are made of \"TAG_SVG_SANI_\" + SVG element name (with - replaced by _) */\n"); + + /* write all tags */ + fprintf(output, "enum {\n"); + for (i=0; iimplementation_name); + } else { + fprintf(output, ",\n\tTAG_SVG_SANI_%s", elt->implementation_name); + } + } + + fprintf(output, ",\n\t/*undefined elements (when parsing) use this tag*/\n\tTAG_SVG_SANI_UndefinedElement\n};\n\n"); + + fprintf(output, "/******************************************\n"); + fprintf(output, "* SVG_SANI_ Elements structure definitions *\n"); + fprintf(output, "*******************************************/\n"); + for (i=0; i\n\n"); + + fprintf(output, "#ifndef GPAC_DISABLE_SVG\n\n"); + fprintf(output, "#include \n\n"); + for (i=0; iimplementation_name,elt->implementation_name); + } + fprintf(output, "\t\tdefault: return NULL;\n\t}\n}\n\n"); + + fprintf(output, "void gf_svg_sani_element_del(SVG_SANI_Element *elt)\n{\n"); + fprintf(output, "\tGF_Node *node = (GF_Node *)elt;\n"); + fprintf(output, "\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iimplementation_name, elt->implementation_name); + } + fprintf(output, "\t\tdefault: return;\n\t}\n}\n\n"); + + fprintf(output, "u32 gf_svg_sani_get_attribute_count(GF_Node *node)\n{\n"); + fprintf(output, "\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iimplementation_name, elt->nb_atts); + } + fprintf(output, "\t\tdefault: return 0;\n\t}\n}\n\n"); + + fprintf(output, "GF_Err gf_svg_sani_get_attribute_info(GF_Node *node, GF_FieldInfo *info)\n{\n"); + fprintf(output, "\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iimplementation_name, elt->implementation_name); + } + fprintf(output, "\t\tdefault: return GF_BAD_PARAM;\n\t}\n}\n\n"); + + fprintf(output, "u32 gf_svg_sani_type_by_class_name(const char *element_name)\n{\n\tif (!element_name) return TAG_UndefinedNode;\n"); + for (i=0; isvg_name, elt->implementation_name); + } + fprintf(output, "\treturn TAG_UndefinedNode;\n}\n\n"); + + fprintf(output, "const char *gf_svg_sani_get_element_name(u32 tag)\n{\n\tswitch(tag) {\n"); + for (i=0; iimplementation_name, elt->svg_name); + } + fprintf(output, "\tdefault: return \"UndefinedNode\";\n\t}\n}\n\n"); + + fprintf(output, "Bool gf_svg_sani_is_element_transformable(u32 tag)\n{\n\tswitch(tag) {\n"); + for (i=0; iimplementation_name); + if (elt->has_transform) fprintf(output, "return 1;\n"); + else fprintf(output, "return 0;\n"); + } + fprintf(output, "\tdefault: return 0;\n\t}\n}\n"); + + fprintf(output, "#endif /*GPAC_DISABLE_SVG*/\n\n"); + EndFile(output, 1); + + generate_laser_tables(svg_elements); +} diff --git a/applications/generators/SVG/v3.c b/applications/generators/SVG/v3.c new file mode 100644 index 0000000..3316889 --- /dev/null +++ b/applications/generators/SVG/v3.c @@ -0,0 +1,294 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004-2005 + * All rights reserved + * + * This file is part of GPAC / SVG Scene Graph Generator sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "svggen.h" + + +void buildGlobalAttributeList(GF_List *svg_elements, GF_List *all_atts) +{ + u32 i, j, k; + Bool added = 0; + + for (i=0; igeneric_attributes); j++) { + SVGGenAttribute *att = gf_list_get(elt->generic_attributes, j); + added = 0; + if (!strcmp(att->impl_type, "SMIL_Fill")) { + strcpy(att->implementation_name, "smil_fill"); + } else if (!strcmp(att->impl_type, "SVG_TransformType")) { + strcpy(att->implementation_name, "transform_type"); + } + for (k = 0; k < gf_list_count(all_atts); k++) { + SVGGenAttribute *a = gf_list_get(all_atts, k); + if (!strcmp(a->implementation_name, att->implementation_name) + && !strcmp(a->impl_type, att->impl_type)) { + added = 1; + break; + } + } + if (!added) { + gf_list_add(all_atts, att); + } + } + for (j = 0; j < gf_list_count(elt->attributes); j++) { + SVGGenAttribute *att = gf_list_get(elt->attributes, j); + added = 0; + if (!strcmp(elt->svg_name, "text")) { + if (!strcmp(att->implementation_name, "x")) { + strcpy(att->implementation_name, "text_x"); + } else if (!strcmp(att->implementation_name, "y")) { + strcpy(att->implementation_name, "text_y"); + } else if (!strcmp(att->implementation_name, "rotate")) { + strcpy(att->implementation_name, "text_rotate"); + } + } else if (!strcmp(elt->svg_name, "listener") && !strcmp(att->implementation_name, "target")) { + strcpy(att->implementation_name, "listener_target"); + } else if (!strcmp(elt->svg_name, "a") && !strcmp(att->implementation_name, "target")) { + strcpy(att->impl_type, "SVG_String"); + } else if (!strcmp(elt->svg_name, "cursorManager")) { + if (!strcmp(att->implementation_name, "x")) { + strcpy(att->implementation_name, "cursorManager_x"); + } else if (!strcmp(att->implementation_name, "y")) { + strcpy(att->implementation_name, "cursorManager_y"); + } + } + for (k = 0; k < gf_list_count(all_atts); k++) { + SVGGenAttribute *a = gf_list_get(all_atts, k); + if (!strcmp(a->implementation_name, att->implementation_name) + && !strcmp(a->impl_type, att->impl_type)) { + added = 1; + break; + } + } + if (!added) { + gf_list_add(all_atts, att); + } + } + } + /*motionTransform is not parsed in rng*/ + { + SVGGenAttribute *att = NewSVGGenAttribute(); + strcpy(att->implementation_name, "motionTransform"); + strcpy(att->impl_type, "SVG_Motion"); + att->svg_name = "motionTransform"; + att->svg_type = "SVG_Motion"; + gf_list_add(all_atts, att); + } +} + +void generateSVGCode_V3(GF_List *svg_elements) +{ + FILE *output; + u32 i; + GF_List *all_atts = gf_list_new(); + + buildGlobalAttributeList(svg_elements, all_atts); + + /***************************************************/ + /***************************************************/ + /*************** Creating .h file ******************/ + /***************************************************/ + /***************************************************/ + output = BeginFile(0); + fprintf(output, "#include \n\n\n"); + + /* Generation of ELEMENT tags */ + fprintf(output, "/* Definition of SVG 3 Alternate element internal tags */\n"); + fprintf(output, "/* TAG names are made of \"TAG_SVG\" + SVG element name (with - replaced by _) */\n"); + fprintf(output, "enum {\n"); + for (i=0; iimplementation_name); + } else { + fprintf(output, ",\n\tTAG_SVG_%s", elt->implementation_name); + } + } + fprintf(output, ",\n\t/*undefined elements (when parsing) use this tag*/\n\tTAG_SVG_UndefinedElement\n};\n\n"); + + /* Generation of ATTRIBUTE tags */ + fprintf(output, "/* Definition of SVG 3 attribute internal tags - %d defined */\n", gf_list_count(all_atts)); + fprintf(output, "/* TAG names are made of \"TAG_SVG_ATT_\" + SVG attribute name (with - replaced by _) */\n"); + fprintf(output, "enum {\n"); + + for (i=0; iimplementation_name); + else fprintf(output, "\tTAG_SVG_ATT_%s = TAG_SVG_ATT_RANGE_FIRST,\n", att->implementation_name); + } + fprintf(output, "\t/*undefined attributes (when parsing) use this tag*/\n\tTAG_SVG_ATT_Unknown\n};\n\n"); + + /* Generation of the flatten structure pointing to all possible attributes in SVG */ + fprintf(output, "struct _all_atts {\n"); + for (i=0; iimpl_type, att->implementation_name); + } + fprintf(output, "};\n"); + + EndFile(output, 0); + + /***************************************************/ + /***************************************************/ + /*************** Creating .c file ******************/ + /***************************************************/ + /***************************************************/ + output = BeginFile(1); + fprintf(output, "#ifndef GPAC_DISABLE_SVG\n\n"); + fprintf(output, "#include \n\n"); + fprintf(output, "#include \n\n"); + + + /****************************************************************/ + /* u32 gf_svg_get_attribute_tag(u32 element_tag, const char *attribute_name) */ + /****************************************************************/ + fprintf(output, "u32 gf_svg_get_attribute_tag(u32 element_tag, const char *attribute_name)\n{\n\tif (!attribute_name) return TAG_SVG_ATT_Unknown;\n"); + for (i=0; iimpl_type, "SMIL_Fill")) continue; + if (!strcmp(att->impl_type, "SVG_ContentType")) continue; + if (!strcmp(att->implementation_name, "text_x")) continue; + if (!strcmp(att->implementation_name, "text_y")) continue; + if (!strcmp(att->implementation_name, "text_rotate")) continue; + if (!strcmp(att->implementation_name, "cursorManager_x")) continue; + if (!strcmp(att->implementation_name, "cursorManager_y")) continue; + + if (!strcmp(att->svg_name, "x") || !strcmp(att->svg_name, "y")) { + fprintf(output, "\tif (!stricmp(attribute_name, \"%s\")) {\n", att->svg_name); + fprintf(output, "\t\tif (element_tag == TAG_SVG_text) return TAG_SVG_ATT_text_%s;\n", att->implementation_name); + fprintf(output, "\t\telse if (element_tag == TAG_SVG_cursorManager) return TAG_SVG_ATT_cursorManager_%s;\n", att->svg_name, att->implementation_name); + fprintf(output, "\t\telse return TAG_SVG_ATT_%s;\n", att->svg_name, att->implementation_name); + fprintf(output, "\t}\n"); + } else if (!strcmp(att->svg_name, "rotate")) { + fprintf(output, "\tif (!stricmp(attribute_name, \"%s\")) {\n", att->svg_name); + fprintf(output, "\t\tif (element_tag == TAG_SVG_text) return TAG_SVG_ATT_text_%s;\n", att->implementation_name); + fprintf(output, "\t\telse return TAG_SVG_ATT_%s;\n", att->svg_name, att->implementation_name); + fprintf(output, "\t}\n"); + } else if (!strcmp(att->svg_name, "type")) { + fprintf(output, "\tif (!stricmp(attribute_name, \"%s\")) {\n", att->svg_name); + fprintf(output, "\t\tif (element_tag == TAG_SVG_animateTransform) return TAG_SVG_ATT_transform_type;\n"); + fprintf(output, "\t\telse return TAG_SVG_ATT_%s;\n", att->svg_name, att->implementation_name); + fprintf(output, "\t}\n"); + } else if (!strcmp(att->svg_name, "fill")) { + fprintf(output, "\tif (!stricmp(attribute_name, \"%s\")) {\n", att->svg_name); + fprintf(output, "\t\tif (element_tag == TAG_SVG_animate || element_tag == TAG_SVG_animateColor || element_tag == TAG_SVG_animateMotion || element_tag == TAG_SVG_animateTransform || element_tag == TAG_SVG_animation || element_tag == TAG_SVG_audio || element_tag == TAG_SVG_video || element_tag == TAG_SVG_set) return TAG_SVG_ATT_smil_fill;\n", att->svg_name, att->implementation_name); + fprintf(output, "\t\telse return TAG_SVG_ATT_%s;\n", att->svg_name, att->implementation_name); + fprintf(output, "\t}\n"); + } else { + fprintf(output, "\tif (!stricmp(attribute_name, \"%s\")) return TAG_SVG_ATT_%s;\n", att->svg_name, att->implementation_name); + } + } + fprintf(output, "\treturn TAG_SVG_ATT_Unknown;\n}\n\n"); + + /****************************************************************/ + /* u32 gf_svg_get_attribute_type(u32 tag) */ + /****************************************************************/ + fprintf(output, "u32 gf_svg_get_attribute_type(u32 tag)\n{\n"); + fprintf(output, "\tswitch(tag) {\n"); + for (i=0; iimplementation_name, att->impl_type); + } + fprintf(output, "\t\tdefault: return SVG_Unknown_datatype;\n"); + fprintf(output, "\t}\n"); + fprintf(output, "\treturn TAG_SVG_ATT_Unknown;\n}\n\n"); + + /****************************************************************/ + /* const char* gf_svg_get_attribute_name(u32 tag) */ + /****************************************************************/ + fprintf(output, "const char*gf_svg_get_attribute_name(u32 tag)\n{\n"); + fprintf(output, "\tswitch(tag) {\n"); + for (i=0; iimplementation_name, att->svg_name); + } + fprintf(output, "\t\tdefault: return \"unknown\";\n"); + fprintf(output, "\t}\n"); + fprintf(output, "}\n\n"); + + /***************************************************/ + /* SVGAttribute *gf_svg_create_attribute(GF_Node *node, u32 tag) */ + /***************************************************/ + fprintf(output, "SVGAttribute *gf_svg_create_attribute(GF_Node *node, u32 tag)\n{\n\tswitch(tag) {\n"); + for (i=0; iimplementation_name, att->impl_type); + } + fprintf(output, "\tdefault: return NULL;\n\t}\n}\n\n"); + + /****************************************************************/ + /* void gf_svg_flatten_attributes(SVG_Element *e, SVGAllAttributes *all_atts) */ + /****************************************************************/ + fprintf(output, "void gf_svg_flatten_attributes(SVG_Element *e, SVGAllAttributes *all_atts)\n"); + + fprintf(output, "{\n"); + fprintf(output, "\tSVGAttribute *att;\n"); + fprintf(output, "\tmemset(all_atts, 0, sizeof(SVGAllAttributes));\n"); + fprintf(output, "\tif (e->sgprivate->tag <= GF_NODE_FIRST_DOM_NODE_TAG) return;\n"); + fprintf(output, "\tatt = e->attributes;\n"); + fprintf(output, "\twhile (att) {\n"); + fprintf(output, "\t\tswitch(att->tag) {\n"); + for (i=0; i%s = (%s *)att->data; break;\n", att->implementation_name, att->implementation_name, att->impl_type); + } + fprintf(output, "\t\t}\n"); + fprintf(output, "\tatt = att->next;\n"); + fprintf(output, "\t}\n"); + fprintf(output, "}\n"); + fprintf(output, "\n"); + + /****************************************************************/ + /* u32 gf_svg_get_element_tag(const char *element_name) */ + /****************************************************************/ + fprintf(output, "u32 gf_svg_get_element_tag(const char *element_name)\n{\n\tif (!element_name) return TAG_UndefinedNode;\n"); + for (i=0; isvg_name, elt->implementation_name); + } + fprintf(output, "\treturn TAG_UndefinedNode;\n}\n\n"); + + /***************************************************/ + /* const char *gf_svg_get_element_name(u32 tag) */ + /***************************************************/ + fprintf(output, "const char *gf_svg_get_element_name(u32 tag)\n{\n\tswitch(tag) {\n"); + for (i=0; iimplementation_name, elt->svg_name); + } + fprintf(output, "\tdefault: return \"TAG_SVG_UndefinedNode\";\n\t}\n}\n\n"); + + fprintf(output, "#endif /*GPAC_DISABLE_SVG*/\n\n"); + EndFile(output, 1); + + + generate_laser_tables_da(all_atts); + + gf_list_del(all_atts); + +} + diff --git a/applications/generators/X3D/Makefile b/applications/generators/X3D/Makefile new file mode 100644 index 0000000..224779c --- /dev/null +++ b/applications/generators/X3D/Makefile @@ -0,0 +1,59 @@ +include ../../../config.mak + +vpath %.c $(SRC_PATH)/applications/generators/X3D + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= main.o ../../../src/utils/list.o ../../../src/utils/error.o + +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=X3DGen$(EXE) +else +OBJS+=../../../src/utils/os_divers.o +EXT= +PROG=X3DGen +endif + +SRCS := $(OBJS:.o=.c) + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) $(PROG) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/applications/generators/X3D/X3DGen.dsp b/applications/generators/X3D/X3DGen.dsp new file mode 100644 index 0000000..b16b8dd --- /dev/null +++ b/applications/generators/X3D/X3DGen.dsp @@ -0,0 +1,98 @@ +# Microsoft Developer Studio Project File - Name="X3DGen" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=X3DGen - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "X3DGen.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "X3DGen.mak" CFG="X3DGen - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "X3DGen - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "X3DGen - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "X3DGen - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "X3DGen - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Obj/W32Deb" +# PROP Intermediate_Dir "Obj/W32Deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "X3DGen - Win32 Release" +# Name "X3DGen - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\..\src\utils\list.c +# End Source File +# Begin Source File + +SOURCE=.\main.c +# End Source File +# End Group +# End Target +# End Project diff --git a/applications/generators/X3D/X3DGen.dsw b/applications/generators/X3D/X3DGen.dsw new file mode 100644 index 0000000..2c265c5 --- /dev/null +++ b/applications/generators/X3D/X3DGen.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "X3DGen"=.\X3DGen.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/applications/generators/X3D/main.c b/applications/generators/X3D/main.c new file mode 100644 index 0000000..023876f --- /dev/null +++ b/applications/generators/X3D/main.c @@ -0,0 +1,1274 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / X3D Scene Graph Generator sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +#include +#include + +#define COPYRIGHT "/*\n * GPAC - Multimedia Framework C SDK\n *\n * Copyright (c) Jean Le Feuvre 2000-2005\n * All rights reserved\n *\n * This file is part of GPAC / X3D Scene Graph sub-project\n *\n * GPAC is free software; you can redistribute it and/or modify\n * it under the terms of the GNU Lesser General Public License as published by\n * the Free Software Foundation; either version 2, or (at your option)\n * any later version.\n *\n * GPAC is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Lesser General Public License for more details. \n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; see the file COPYING. If not, write to\n * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n */\n" + +static char *CurrentLine; + +void PrintUsage() +{ + printf("X3DGen [skip_file]\n" + "\nGPAC X3D Scene Graph generator\n" + "\n" + "\nskip_file: txt file with list of nodes to leave unimplemented" + "Generated Files are directly updated in the GPAC distribution - do NOT try to change this\n\n" + "Written by Jean Le Feuvre - (c) 2000-2005\n" + ); +} + +//a node field +typedef struct +{ + char type[50]; + //SFxxx, MFxxx + char familly[50]; + //name + char name[1000]; + //default value + char def[100]; + //bounds + u32 hasBounds; + char b_min[20]; + char b_max[20]; +} X3DField; + +//NDTs + +//a BIFS node +typedef struct +{ + char name[1000]; + //NDT info. NDT are created in alphabetical order + GF_List *NDT; + GF_List *Fields; + u8 hasDefault; + char Child_NDT_Name[1000]; + u8 skip_impl; + +} X3DNode; + + +void skip_sep(char *sep) +{ + //skip separaors + while (*CurrentLine && strchr(sep, *CurrentLine)) { + CurrentLine = CurrentLine + 1; + //end of line - no token + if (*CurrentLine == '\n') return; + } +} + +//note that we increment the line no matter what +u32 GetNextToken(char *token, char *sep) +{ + u32 i , j = 0; + + strcpy(token, ""); + + //skip separaors + while (*CurrentLine && strchr(sep, *CurrentLine)) { + CurrentLine = CurrentLine + 1; + j ++; + //end of line - no token + if (*CurrentLine == '\n') return 0; + } + + //copy token untill next blank + i=0; + while (1) { + //bad line + if (! *CurrentLine) { + token[i] = 0; + return 0; + } + //end of token or end of line + if (strchr(sep, *CurrentLine) || (*CurrentLine == '\n') ) { + token[i] = 0; + CurrentLine = CurrentLine + 1; + return i; + } else { + token[i] = *CurrentLine; + } + CurrentLine = CurrentLine + 1; + i++; + j++; + } + return 1; +} + +X3DField *BlankField() +{ + X3DField *n = malloc(sizeof(X3DField)); + memset(n, 0, sizeof(X3DField)); + return n; +} + + +X3DNode *BlankNode() +{ + X3DNode *n = malloc(sizeof(X3DNode)); + memset(n, 0, sizeof(X3DNode)); + n->NDT = gf_list_new(); + n->Fields = gf_list_new(); + return n; +} + +u8 IsNDT(GF_List *NDTs, char *famName) +{ + u32 i; + char *ndtName; + for (i=0; i\n\n"); + + //write all tags + fprintf(f, "\n\nenum {\n"); + + for (i=0; iname); + else + fprintf(f, "\tTAG_X3D_%s = GF_NODE_RANGE_FIRST_X3D", n->name); + } + fprintf(f, ",\n\tTAG_LastImplementedX3D\n};\n\n"); + + for (i=0; iskip_impl) continue; + fprintf(f, "typedef struct _tagX3D%s\n{\n", n->name); + fprintf(f, "\tBASE_NODE\n"); + + /*write children field*/ + for (j=0; jFields); j++) { + bf = gf_list_get(n->Fields, j); + if (!stricmp(bf->name, "addChildren") || !strcmp(bf->name, "removeChildren")) continue; + if (strcmp(bf->type, "eventOut") && !strcmp(bf->name, "children")) { + fprintf(f, "\tVRML_CHILDREN\n"); + break; + } + } + for (j=0; jFields); j++) { + bf = gf_list_get(n->Fields, j); + + if (!strcmp(bf->name, "addChildren") || !strcmp(bf->name, "removeChildren")) continue; + if (strcmp(bf->type, "eventOut") && !strcmp(bf->name, "children")) continue; + + if (strstr(bf->familly, "Node")) { + //this is a POINTER to a node + if (strstr(bf->familly, "SF")) { + fprintf(f, "\tGF_Node *%s;\t/*%s*/\n", bf->name, bf->type); + } else { + //this is a POINTER to a chain + fprintf(f, "\tGF_ChildNodeItem *%s;\t/*%s*/\n", bf->name, bf->type); + } + } else { + fprintf(f, "\t%s %s;\t/*%s*/\n", bf->familly, bf->name, bf->type); + } + if (!strcmp(bf->type, "eventIn")) + fprintf(f, "\tvoid (*on_%s)(GF_Node *pThis);\t/*eventInHandler*/\n", bf->name); + } + fprintf(f, "} X_%s;\n\n\n", n->name); + } + + EndFile(f, 0); + +} + +void WriteNodeFields(FILE *f, X3DNode *n) +{ + u32 i; + X3DField *bf; + + fprintf(f, "\nstatic u32 %s_get_field_count(GF_Node *node, u8 dummy)\n{\n\treturn %d;\n}\n\n", n->name, gf_list_count(n->Fields)); + fprintf(f, "static GF_Err %s_get_field(GF_Node *node, GF_FieldInfo *info)\n{\n\tswitch (info->fieldIndex) {\n", n->name); + for (i=0;iFields); i++) { + bf = gf_list_get(n->Fields, i); + + fprintf(f, "\tcase %d:\n", i); + + fprintf(f, "\t\tinfo->name = \"%s\";\n", bf->name); + + //skip all eventIn + if (!strcmp(bf->type, "eventIn")) { + fprintf(f, "\t\tinfo->eventType = GF_SG_EVENT_IN;\n"); + fprintf(f, "\t\tinfo->on_event_in = ((X_%s *)node)->on_%s;\n", n->name, bf->name); + } + else if (!strcmp(bf->type, "eventOut")) { + fprintf(f, "\t\tinfo->eventType = GF_SG_EVENT_OUT;\n"); + } + else if (!strcmp(bf->type, "field")) { + fprintf(f, "\t\tinfo->eventType = GF_SG_EVENT_FIELD;\n"); + } + else { + fprintf(f, "\t\tinfo->eventType = GF_SG_EVENT_EXPOSED_FIELD;\n"); + } + + if (strstr(bf->familly, "Node")) { + if (strstr(bf->familly, "MF")) { + fprintf(f, "\t\tinfo->fieldType = GF_SG_VRML_MFNODE;\n"); + } else { + fprintf(f, "\t\tinfo->fieldType = GF_SG_VRML_SFNODE;\n"); + } + //always remove the SF or MF, as all NDTs are SFXXX + fprintf(f, "\t\tinfo->NDTtype = NDT_SF%s;\n", bf->familly+2); + fprintf(f, "\t\tinfo->far_ptr = & ((X_%s *)node)->%s;\n", n->name, bf->name); + } else { + char szName[20]; + strcpy(szName, bf->familly); + strupr(szName); + //no ext type + fprintf(f, "\t\tinfo->fieldType = GF_SG_VRML_%s;\n", szName); + fprintf(f, "\t\tinfo->far_ptr = & ((X_%s *) node)->%s;\n", n->name, bf->name); + } + fprintf(f, "\t\treturn GF_OK;\n"); + } + fprintf(f, "\tdefault:\n\t\treturn GF_BAD_PARAM;\n\t}\n}\n\n"); + + fprintf(f, "\nstatic s32 %s_get_field_index_by_name(char *name)\n{\n", n->name); + for (i=0;iFields); i++) { + bf = gf_list_get(n->Fields, i); + fprintf(f, "\tif (!strcmp(\"%s\", name)) return %d;\n", bf->name, i); + } + fprintf(f, "\treturn -1;\n\t}\n"); +} + +void WriteNodeCode(GF_List *BNodes, FILE *vrml_code) +{ + char token[20], tok[20]; + char *store; + u32 i, j, k, go; + X3DField *bf; + X3DNode *n; + + fprintf(vrml_code, "\n#include \n"); + fprintf(vrml_code, "\n#include \n"); + fprintf(vrml_code, "\n/*for NDT tag definitions*/\n#include \n"); + + for (k=0; kskip_impl) continue; + fprintf(vrml_code, "\n/*\n\t%s Node deletion\n*/\n\n", n->name); + fprintf(vrml_code, "static void %s_Del(GF_Node *node)\n{\n\tX_%s *p = (X_%s *) node;\n", n->name, n->name, n->name); + + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + //nothing on child events + if (!strcmp(bf->name, "addChildren")) continue; + if (!strcmp(bf->name, "removeChildren")) continue; + + //delete all children node + if (strcmp(bf->type, "eventOut") && !strcmp(bf->name, "children")) { + is_parent = 1; + continue; + } + + //delete ALL fields that must be deleted: this includes eventIn and out since + //all fields are defined in the node + if (!strcmp(bf->familly, "MFInt") + || !strcmp(bf->familly, "MFFloat") + || !strcmp(bf->familly, "MFDouble") + || !strcmp(bf->familly, "MFBool") + || !strcmp(bf->familly, "MFInt32") + || !strcmp(bf->familly, "MFColor") + || !strcmp(bf->familly, "MFRotation") + || !strcmp(bf->familly, "MFString") + || !strcmp(bf->familly, "MFTime") + || !strcmp(bf->familly, "MFVec2f") + || !strcmp(bf->familly, "MFVec3f") + || !strcmp(bf->familly, "MFVec4f") + || !strcmp(bf->familly, "MFVec2d") + || !strcmp(bf->familly, "MFVec3d") + || !strcmp(bf->familly, "MFURL") + || !strcmp(bf->familly, "MFScript") + || !strcmp(bf->familly, "SFString") + || !strcmp(bf->familly, "SFURL") + || !strcmp(bf->familly, "SFImage") + + ) { + char szName[500]; + strcpy(szName, bf->familly); + strlwr(szName); + fprintf(vrml_code, "\tgf_sg_%s_del(p->%s);\n", szName, bf->name); + } else if (strstr(bf->familly, "Node")) { + //this is a POINTER to a node + if (strstr(bf->familly, "SF")) { + fprintf(vrml_code, "\tgf_node_unregister((GF_Node *) p->%s, node);\t\n", bf->name); + } else { + //this is a POINTER to a chain + fprintf(vrml_code, "\tgf_node_unregister_children(node, p->%s);\t\n", bf->name); + } + } + } + if (is_parent) + fprintf(vrml_code, "\tgf_sg_vrml_parent_destroy(node);\t\n"); + /*avoids gcc warnings in case no field to delete*/ + fprintf(vrml_code, "\tgf_node_free((GF_Node *)p);\n}\n\n"); + + //node fields + WriteNodeFields(vrml_code, n); + + // + // Constructor + // + + fprintf(vrml_code, "\n\nstatic GF_Node *%s_Create()\n{\n\tX_%s *p;\n\tGF_SAFEALLOC(p, X_%s);\n", n->name, n->name, n->name); + fprintf(vrml_code, "\tif(!p) return NULL;\n"); + fprintf(vrml_code, "\tgf_node_setup((GF_Node *)p, TAG_X3D_%s);\n", n->name); + + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + //setup all children node + if (strcmp(bf->type, "eventOut") && !strcmp(bf->name, "children")) { + fprintf(vrml_code, "\tgf_sg_vrml_parent_setup((GF_Node *) p);\n"); + break; + } + else if ( strstr(bf->familly, "Node") && strncmp(bf->type, "event", 5) ) { + //this is a POINTER to a node + if (strstr(bf->familly, "MF")) { + //this is a POINTER to a chain + //fprintf(vrml_code, "\tp->%s = gf_list_new();\t\n", bf->name); + } + } + /*special case for SFCommandBuffer: we also create a command list*/ + if (!stricmp(bf->familly, "SFCommandBuffer")) { + fprintf(vrml_code, "\tp->%s.commandList = gf_list_new();\t\n", bf->name); + } + } + + fprintf(vrml_code, "\n\t/*default field values*/\n"); + + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + + //nothing on eventIn or Out + if (!strcmp(bf->type, "eventIn")) continue; + if (!strcmp(bf->type, "eventOut")) continue; + + if (!strcmp(bf->def, "")) continue; + + //no default on nodes + if (strstr(bf->familly, "Node")) continue; + //extract default falue + + // + // SF Fields + // + + //SFBool + if (!strcmp(bf->familly, "SFBool")) { + if (!strcmp(bf->def, "1") || !strcmp(bf->def, "TRUE")) + fprintf(vrml_code, "\tp->%s = 1;\n", bf->name); + } + //SFFloat + else if (!strcmp(bf->familly, "SFFloat")) { + fprintf(vrml_code, "\tp->%s = FLT2FIX(%s);\n", bf->name, bf->def); + } + //SFDouble + else if (!strcmp(bf->familly, "SFDouble")) { + fprintf(vrml_code, "\tp->%s = (SFDouble) %s;\n", bf->name, bf->def); + } + //SFTime + else if (!strcmp(bf->familly, "SFTime")) { + fprintf(vrml_code, "\tp->%s = %s;\n", bf->name, bf->def); + } + //SFInt32 + else if (!strcmp(bf->familly, "SFInt32")) { + fprintf(vrml_code, "\tp->%s = %s;\n", bf->name, bf->def); + } + //SFColor + else if (!strcmp(bf->familly, "SFColor")) { + CurrentLine = bf->def; + GetNextToken(token, " "); + TranslateToken(token); + + fprintf(vrml_code, "\tp->%s.red = FLT2FIX(%s);\n", bf->name, token); + GetNextToken(token, " "); + fprintf(vrml_code, "\tp->%s.green = FLT2FIX(%s);\n", bf->name, token); + GetNextToken(token, " "); + fprintf(vrml_code, "\tp->%s.blue = FLT2FIX(%s);\n", bf->name, token); + } + //SFVec2f + else if (!strcmp(bf->familly, "SFVec2f")) { + CurrentLine = bf->def; + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.x = FLT2FIX(%s);\n", bf->name, token); + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.y = FLT2FIX(%s);\n", bf->name, token); + } + //SFVec2d + else if (!strcmp(bf->familly, "SFVec2d")) { + CurrentLine = bf->def; + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.x = (SFDouble) %s;\n", bf->name, token); + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.y = (SFDouble) %s;\n", bf->name, token); + } + //SFVec3f + else if (!strcmp(bf->familly, "SFVec3f")) { + CurrentLine = bf->def; + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.x = FLT2FIX(%s);\n", bf->name, token); + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.y = FLT2FIX(%s);\n", bf->name, token); + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.z = FLT2FIX(%s);\n", bf->name, token); + } + //SFVec3d + else if (!strcmp(bf->familly, "SFVec3d")) { + CurrentLine = bf->def; + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.x = (SFDouble) %s;\n", bf->name, token); + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.y = (SFDouble) %s;\n", bf->name, token); + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.z = (SFDouble) %s;\n", bf->name, token); + } + //SFVec4f & SFRotation + else if (!strcmp(bf->familly, "SFVec4f") || !strcmp(bf->familly, "SFRotation")) { + CurrentLine = bf->def; + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.x = FLT2FIX(%s);\n", bf->name, token); + + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.y = FLT2FIX(%s);\n", bf->name, token); + + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.z = FLT2FIX(%s);\n", bf->name, token); + + GetNextToken(token, " "); + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.q = FLT2FIX(%s);\n", bf->name, token); + } + //SFString + else if (!strcmp(bf->familly, "SFString")) { + fprintf(vrml_code, "\tp->%s.buffer = (char*) malloc(sizeof(char) * %d);\n", bf->name, strlen(bf->def)+1); + fprintf(vrml_code, "\tstrcpy(p->%s.buffer, \"%s\");\n", bf->name, bf->def); + } + + // + // MF Fields + // + //MFFloat + else if (!strcmp(bf->familly, "MFFloat")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, " ,")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (SFFloat *)malloc(sizeof(SFFloat)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, " ,")) go = 0; + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.vals[%d] = FLT2FIX(%s);\n", bf->name, j, token); + j+=1; + } + } + //MFDouble + else if (!strcmp(bf->familly, "MFDouble")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, " ,")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (SFFloat*)malloc(sizeof(SFFloat)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, " ,")) go = 0; + TranslateToken(token); + fprintf(vrml_code, "\tp->%s.vals[%d] = (SFDouble) %s;\n", bf->name, j, token); + j+=1; + } + } + //MFVec2f + else if (!strcmp(bf->familly, "MFVec2f")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (SFVec2f*) malloc(sizeof(SFVec2f)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].x = FLT2FIX(%s);\n", bf->name, j, tok); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].y = FLT2FIX(%s);\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + //MFVec2d + else if (!strcmp(bf->familly, "MFVec2d")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (SFVec2f*)malloc(sizeof(SFVec2f)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].x = (SFDouble) %s;\n", bf->name, j, tok); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].y = (SFDouble) %s;\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + //MFVec3f + else if (!strcmp(bf->familly, "MFVec3f")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (SFVec3f*)malloc(sizeof(SFVec3f)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].x = FLT2FIX(%s);\n", bf->name, j, tok); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].y = FLT2FIX(%s);\n", bf->name, j, tok); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].z = FLT2FIX(%s);\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + //MFVec3d + else if (!strcmp(bf->familly, "MFVec3d")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (SFVec2f*)malloc(sizeof(SFVec3f)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].x = (SFDouble) %s;\n", bf->name, j, tok); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].y = (SFDouble) %s;\n", bf->name, j, tok); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].z = (SFDouble) %s;\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + //MFVec4f & MFRotation + else if (!strcmp(bf->familly, "MFVec4f") || !strcmp(bf->familly, "MFRotation")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (GF_Vec4*)malloc(sizeof(GF_Vec4)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].x = FLT2FIX(%s);\n", bf->name, j, tok); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].y = FLT2FIX(%s);\n", bf->name, j, tok); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].z = FLT2FIX(%s);\n", bf->name, j, tok); + GetNextToken(tok, " "); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d].q = FLT2FIX(%s);\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + //MFInt32 + else if (!strcmp(bf->familly, "MFInt32")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (SFInt32*)malloc(sizeof(SFInt32)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + fprintf(vrml_code, "\tp->%s.vals[%d] = %s;\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + //MFColor + else if (!strcmp(bf->familly, "MFColor")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (SFColor*)malloc(sizeof(SFColor)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " "); + fprintf(vrml_code, "\tp->%s.vals[%d].red = FLT2FIX(%s);\n", bf->name, j, tok); + GetNextToken(tok, " "); + fprintf(vrml_code, "\tp->%s.vals[%d].green = FLT2FIX(%s);\n", bf->name, j, tok); + GetNextToken(tok, " "); + fprintf(vrml_code, "\tp->%s.vals[%d].blue = FLT2FIX(%s);\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + //MFString + else if (!strcmp(bf->familly, "MFString")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (char**)malloc(sizeof(SFString)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " \""); + fprintf(vrml_code, "\tp->%s.vals[%d] = (char*)malloc(sizeof(char) * %d);\n", bf->name, j, strlen(tok)+1); + fprintf(vrml_code, "\tstrcpy(p->%s.vals[%d], \"%s\");\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + //MFTime + else if (!strcmp(bf->familly, "MFTime")) { + j = 0; + CurrentLine = bf->def; + while (GetNextToken(token, ",")) j++; + j+=1; + fprintf(vrml_code, "\tp->%s.vals = (SFTime*)malloc(sizeof(SFTime)*%d);\n", bf->name, j); + fprintf(vrml_code, "\tp->%s.count = %d;\n", bf->name, j); + j = 0; + go = 1; + CurrentLine = bf->def; + while (go) { + if (!GetNextToken(token, ",")) go = 0; + store = CurrentLine; + CurrentLine = token; + GetNextToken(tok, " \""); + TranslateToken(tok); + fprintf(vrml_code, "\tp->%s.vals[%d] = %s;\n", bf->name, j, tok); + j+=1; + CurrentLine = store; + } + } + + //other nodes + else if (!strcmp(bf->familly, "SFImage")) { + //we currently only have SFImage, with NO texture so do nothing + } + //unknown init (for debug) + else { + fprintf(vrml_code, "UNKNOWN FIELD (%s);\n", bf->familly); + + } + } + fprintf(vrml_code, "\treturn (GF_Node *)p;\n}\n\n"); + + } + + fprintf(vrml_code, "\n\n\n"); + + //creator function + fprintf(vrml_code, "GF_Node *gf_sg_x3d_node_new(u32 NodeTag)\n{\n\tswitch (NodeTag) {\n"); + for (i=0; iskip_impl) fprintf(vrml_code, "\tcase TAG_X3D_%s:\n\t\treturn %s_Create();\n", n->name, n->name); + } + fprintf(vrml_code, "\tdefault:\n\t\treturn NULL;\n\t}\n}\n\n"); + + fprintf(vrml_code, "const char *gf_sg_x3d_node_get_class_name(u32 NodeTag)\n{\n\tswitch (NodeTag) {\n"); + for (i=0; iskip_impl) fprintf(vrml_code, "\tcase TAG_X3D_%s:\n\t\treturn \"%s\";\n", n->name, n->name); + } + fprintf(vrml_code, "\tdefault:\n\t\treturn \"Unknown Node\";\n\t}\n}\n\n"); + + fprintf(vrml_code, "void gf_sg_x3d_node_del(GF_Node *node)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl) fprintf(vrml_code, "\tcase TAG_X3D_%s:\n\t\t%s_Del(node); return;\n", n->name, n->name); + } + fprintf(vrml_code, "\tdefault:\n\t\treturn;\n\t}\n}\n\n"); + + fprintf(vrml_code, "u32 gf_sg_x3d_node_get_field_count(GF_Node *node)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl) fprintf(vrml_code, "\tcase TAG_X3D_%s:return %s_get_field_count(node, 0);\n", n->name, n->name); + } + fprintf(vrml_code, "\tdefault:\n\t\treturn 0;\n\t}\n}\n\n"); + + fprintf(vrml_code, "GF_Err gf_sg_x3d_node_get_field(GF_Node *node, GF_FieldInfo *field)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl) fprintf(vrml_code, "\tcase TAG_X3D_%s: return %s_get_field(node, field);\n", n->name, n->name); + } + fprintf(vrml_code, "\tdefault:\n\t\treturn GF_BAD_PARAM;\n\t}\n}\n\n"); + + fprintf(vrml_code, "\nu32 gf_node_x3d_type_by_class_name(const char *node_name)\n{\n\tif(!node_name) return 0;\n"); + for (i=0; iskip_impl) fprintf(vrml_code, "\tif (!strcmp(node_name, \"%s\")) return TAG_X3D_%s;\n", n->name, n->name); + } + fprintf(vrml_code, "\treturn 0;\n}\n\n"); + + fprintf(vrml_code, "s32 gf_sg_x3d_node_get_field_index_by_name(GF_Node *node, char *name)\n{\n\tswitch (node->sgprivate->tag) {\n"); + for (i=0; iskip_impl) { + fprintf(vrml_code, "\tcase TAG_X3D_%s: return %s_get_field_index_by_name(name);\n", n->name, n->name); + } + } + fprintf(vrml_code, "\tdefault:\n\t\treturn -1;\n\t}\n}\n\n"); + +} + + +static u32 IsNodeInTable(X3DNode *node, char *NDTName) +{ + u32 i; + char *ndt; + + for (i=0; iNDT); i++) { + ndt = gf_list_get(node->NDT, i); + if (!strcmp(ndt, NDTName)) return 1; + } + return 0; +} + +static u32 GetNDTCount(char *NDTName, GF_List *XNodes) +{ + u32 i, nodeCount; + X3DNode *n; + nodeCount = 0; + for (i=0; iname); + first = 0; + } else { + fprintf(f, ", TAG_X3D_%s", n->name); + } + } + fprintf(f, "\n};\n\n"); + } + + //NodeTag complete translation + fprintf(f, "\n\n\nBool gf_x3d_get_node_type(u32 NDT_Tag, u32 NodeTag)\n{\n\tconst u32 *types;\n\tu32 count, i;\n\tif (!NodeTag) return 0;\n\ttypes = NULL; count = 0;\n"); + + fprintf(f, "\tswitch (NDT_Tag) {\n"); + for (i=0; iname, " \t["); + + //extract the NDTs + GetNextToken(token, "\t[ %#="); + if (strcmp(token, "NDT")) { + printf("Corrupted template file\n"); + return; + } + while (1) { + GetNextToken(token, "=, \t"); + //done with NDTs + if (!token[0]) break; + + //update the NDT list + CheckInTable(token, NDTs); + p = malloc(strlen(token)+1); + strcpy(p, token); + gf_list_add(n->NDT, p); + } + } + //this is NOT a field + else if (token[0] == ']' || token[0] == '{' || token[0] == '}' ) { + break; + } + //parse a field + else { + if (!n) { + printf("Corrupted template file\n"); + return; + } + f = BlankField(); + gf_list_add(n->Fields, f); + + //get the field type + strcpy(f->type, token); + GetNextToken(f->familly, " \t"); + GetNextToken(f->name, " \t"); + //fix for our own code :( + if (!strcmp(f->name, "tag")) strcpy(f->name, "_tag"); + + //has default + skip_sep(" \t"); + if (GetNextToken(token, "#\t")) { + j=0; + while (token[j] == ' ') j+=1; + if (token[j] == '[') j+=1; + if (token[j] == '"') j+=1; + + if (token[j] != '"' && token[j] != ']') { + strcpy(f->def, token+j); + j=1; + while (j) { + switch (f->def[strlen(f->def)-1]) { + case ' ': + case '"': + case ']': + f->def[strlen(f->def)-1] = 0; + break; + default: + j=0; + break; + } + } + } else { + strcpy(f->def, ""); + } + if (!strcmp(f->familly, "SFFloat")) { + if (!strcmp(f->def, "+I") || !strcmp(f->def, "I")) { + strcpy(f->def, "GF_MAX_FLOAT"); + } else if (!strcmp(f->def, "-I")) { + strcpy(f->def, "GF_MIN_FLOAT"); + } + } else if (!strcmp(f->familly, "SFTime")) { + if (!strcmp(f->def, "+I") || !strcmp(f->def, "I")) { + strcpy(f->def, "GF_MAX_FLOAT"); + } else if (!strcmp(f->def, "-I")) { + strcpy(f->def, "GF_MIN_FLOAT"); + } + } else if (!strcmp(f->familly, "SFInt32")) { + if (!strcmp(f->def, "+I") || !strcmp(f->def, "I")) { + strcpy(f->def, "2 << 31"); + } else if (!strcmp(f->def, "-I")) { + strcpy(f->def, "- (2 << 31)"); + } + } + } + //has other + while (GetNextToken(token, " \t#%=")) { + switch (token[0]) { + //bounds + case 'b': + case 'q': + case 'a': + printf("Corrupted X3D template file (quantization/animation not allowed)\n"); + gf_list_del_item(n->Fields, f); + free(f); + return; + default: + break; + } + } + /*we ignore these*/ + if (!stricmp(f->name, "bboxCenter") || !stricmp(f->name, "bboxSize")) { + gf_list_del_item(n->Fields, f); + free(f); + } + } + } + } + + + for (k=0; kFields); i++) { + f = gf_list_get(n->Fields, i); + //nothing on events + if (!strcmp(f->type, "eventIn")) continue; + if (!strcmp(f->type, "eventOut")) continue; + if (!strcmp(f->def, "")) continue; + if (strstr(f->familly, "Node")) continue; + n->hasDefault = 1; + } + } +} + + +void WriteNodeDump(FILE *f, X3DNode *n) +{ + X3DField *bf; + u32 i; + + fprintf(f, "static const char *%s_FieldName[] = {\n", n->name); + for (i=0; iFields); i++) { + bf = gf_list_get(n->Fields, i); + if (!i) { + fprintf(f, " \"%s\"", bf->name); + } else { + fprintf(f, ", \"%s\"", bf->name); + } + } + fprintf(f, "\n};\n\n"); +} + + +void parse_profile(GF_List *nodes, FILE *prof) +{ + char sLine[2000]; + X3DNode *n; + Bool found; + u32 i; + + while (!feof(prof)) { + fgets(sLine, 2000, prof); + //skip comment and empty lines + if (sLine[0] == '#') continue; + if (sLine[0] == '\n') continue; + if (strstr(sLine, "Proximity")) + found = 0; + found = 1; + while (found) { + switch (sLine[strlen(sLine)-1]) { + case '\n': + case '\r': + case ' ': + sLine[strlen(sLine)-1] = 0; + break; + default: + found = 0; + break; + } + } + + if (0) { + printf("Warning: cannot disable node %s (required in all BIFS profiles)\n", sLine); + } else { + found = 0; + for (i=0; iname, sLine)) { + n->skip_impl = 1; + found = 1; + break; + } + } + if (!found) printf("cannot disable %s: node not found\n", sLine); + } + } +} + +int main (int argc, char **argv) +{ + FILE *nodes, *pf; + GF_List *XNodes, *NDTs; + X3DNode *n; + X3DField *bf; + u32 nb_nodes, nb_imp; + + nodes = fopen("templates_X3D.txt", "rt"); + if (!nodes) { + fprintf(stdout, "cannot open \"templates_X3D.txt\" - aborting\n"); + return 0; + } + + XNodes = gf_list_new(); + NDTs = gf_list_new(); + //all nodes are in the same list but we keep version info + ParseTemplateFile(nodes, XNodes, NDTs); + fclose(nodes); + + if (argc>1) { + pf = fopen(argv[1], "rt"); + if (!pf) fprintf(stdout, "Cannot open profile file %s\n", argv[1]); + else { + parse_profile(XNodes, pf); + fclose(pf); + } + } + + //write the nodes def + WriteNodesFile(XNodes, NDTs); + + nodes = BeginFile(1); + + //write all nodes init stuff + WriteNodeCode(XNodes, nodes); + + WriteNDT(nodes, XNodes, NDTs); + + EndFile(nodes, 1); + + //free NDTs + while (gf_list_count(NDTs)) { + char *tmp = gf_list_get(NDTs, 0); + free(tmp); + gf_list_rem(NDTs, 0); + } + gf_list_del(NDTs); + + nb_nodes = gf_list_count(XNodes); + nb_imp = 0; + //free nodes + while (gf_list_count(XNodes)) { + n = gf_list_get(XNodes, 0); + if (!n->skip_impl) nb_imp++; + gf_list_rem(XNodes, 0); + while (gf_list_count(n->NDT)) { + char *tmp = gf_list_get(n->NDT, 0); + free(tmp); + gf_list_rem(n->NDT, 0); + } + gf_list_del(n->NDT); + while (gf_list_count(n->Fields)) { + bf = gf_list_get(n->Fields, 0); + free(bf); + gf_list_rem(n->Fields, 0); + } + gf_list_del(n->Fields); + free(n); + } + gf_list_del(XNodes); + + fprintf(stdout, "Generation done: %d nodes implemented (%d nodes total)\n", nb_imp, nb_nodes); + return 0; +} + diff --git a/applications/generators/X3D/skip.txt b/applications/generators/X3D/skip.txt new file mode 100644 index 0000000..62cae25 --- /dev/null +++ b/applications/generators/X3D/skip.txt @@ -0,0 +1,130 @@ +#X3D defined nodes + +#Anchor +#Appearance +#Arc2D +#ArcClose2D +#AudioClip +#Background +#Billboard +#BooleanFilter +#BooleanSequencer +#BooleanToggle +#BooleanTrigger +#Box +#Circle2D +#Collision +#Color +#ColorInterpolator +#ColorRGBA +#Cone +#Contour2D +#ContourPolyline2D +#Coordinate +#CoordinateDouble +#Coordinate2D +#CoordinateInterpolator +#CoordinateInterpolator2D +#Cylinder +#CylinderSensor +#DirectionalLight +#Disk2D +#ElevationGrid +EspduTransform +#Extrusion +#FillProperties +#Fog +#FontStyle +GeoCoordinate +GeoElevationGrid +GeoLocation +GeoLOD +GeoMetadata +GeoOrigin +GeoPositionInterpolator +GeoTouchSensor +GeoViewpoint +#Group +HAnimDisplacer +HAnimHumanoid +HAnimJoint +HAnimSegment +HAnimSite +#ImageTexture +#IndexedFaceSet +#IndexedLineSet +#IndexedTriangleFanSet +#IndexedTriangleSet +#IndexedTriangleStripSet +#Inline +#IntegerSequencer +#IntegerTrigger +#KeySensor +#LineProperties +#LineSet +LoadSensor +#LOD +#Material +#MetadataDouble +#MetadataFloat +#MetadataInteger +#MetadataSet +#MetadataString +#MovieTexture +#MultiTexture +#MultiTextureCoordinate +#MultiTextureTransform +#NavigationInfo +#Normal +#NormalInterpolator +NurbsCurve +NurbsCurve2D +NurbsOrientationInterpolator +NurbsPatchSurface +NurbsPositionInterpolator +NurbsSet +NurbsSurfaceInterpolator +NurbsSweptSurface +NurbsSwungSurface +NurbsTextureCoordinate +NurbsTrimmedSurface +#OrientationInterpolator +#PixelTexture +#PlaneSensor +#PointLight +#PointSet +#Polyline2D +#Polypoint2D +#PositionInterpolator +#PositionInterpolator2D +#ProximitySensor +ReceiverPdu +#Rectangle2D +#ScalarInterpolator +#Script +#Shape +SignalPdu +#Sound +#Sphere +#SphereSensor +#SpotLight +#StaticGroup +#StringSensor +#Switch +#Text +#TextureBackground +#TextureCoordinate +#TextureCoordinateGenerator +#TextureTransform +#TimeSensor +#TimeTrigger +#TouchSensor +#Transform +TransmitterPdu +#TriangleFanSet +#TriangleSet +#TriangleSet2D +#TriangleStripSet +#Viewpoint +#VisibilitySensor +#WorldInfo diff --git a/applications/generators/X3D/templates_X3D.txt b/applications/generators/X3D/templates_X3D.txt new file mode 100644 index 0000000..5f235da --- /dev/null +++ b/applications/generators/X3D/templates_X3D.txt @@ -0,0 +1,1644 @@ +# GPAC X3D template file +# X3D nodes in GPAC are designed so that they match their MPEG-4 counterparts in order to perform type casting, whenever possible ('#X3D extensions' signaled). +# ommented fields are fields where X3D spec, DTD and XSD disagree +# + +#NOT AN MPEG4 extensions because of activate eventIn in mpeg4 +PROTO Anchor [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFString description "" +exposedField MFString parameter [] +exposedField MFURL url [] +exposedField SFMetadataNode metadata NULL +]{ +} + +PROTO Appearance [ #%NDT=SFWorldNode,SFAppearanceNode +exposedField SFMaterialNode material NULL +exposedField SFTextureNode texture NULL +exposedField SFTextureTransformNode textureTransform NULL +#X3D extensions +exposedField SFFillPropertiesNode fillProperties NULL +exposedField SFX3DLinePropertiesNode lineProperties NULL +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Arc2D [ #%NDT=SFWorldNode,SFGeometryNode +field SFFloat endAngle 1.5707963 +field SFFloat radius 1 +field SFFloat startAngle 0 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO ArcClose2D [ #%NDT=SFWorldNode,SFGeometryNode +field SFString closureType "PIE" +field SFFloat endAngle 1.5707963 +field SFFloat radius 1 +field SFFloat startAngle 0 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO AudioClip [ #%NDT=SFWorldNode,SFAudioNode,SFStreamingNode +exposedField SFString description "" +exposedField SFBool loop FALSE +exposedField SFFloat pitch 1.0 +exposedField SFTime startTime 0 +exposedField SFTime stopTime 0 +exposedField MFURL url [] +eventOut SFTime duration_changed +eventOut SFBool isActive +#X3D extensions +exposedField SFMetadataNode metadata NULL +exposedField SFTime pauseTime 0 +exposedField SFTime resumeTime 0 +eventOut SFTime elapsedTime +eventOut SFBool isPaused +] { +} + +PROTO Background [ #%NDT=SFWorldNode,SF3DNode,SFBackground3DNode +eventIn SFBool set_bind +exposedField MFFloat groundAngle [] +exposedField MFColor groundColor [] +exposedField MFURL backUrl [] +exposedField MFURL bottomUrl [] +exposedField MFURL frontUrl [] +exposedField MFURL leftUrl [] +exposedField MFURL rightUrl [] +exposedField MFURL topUrl [] +exposedField MFFloat skyAngle [] +exposedField MFColor skyColor [ 0 0 0 ] +eventOut SFBool isBound +#X3D extensions +exposedField SFMetadataNode metadata NULL +eventOut SFTime bindTime +] { +} + +PROTO Billboard [ #%NDT=SFWorldNode,SF3DNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFVec3f axisOfRotation 0 1 0 +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO BooleanFilter [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFBool set_boolean +eventOut SFBool inputFalse +eventOut SFBool inputNegate +eventOut SFBool inputTrue +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO BooleanSequencer [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFBool next +eventIn SFBool previous +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFBool keyValue [] +eventOut SFBool value_changed +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO BooleanToggle [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFBool set_boolean +exposedField SFBool toggle FALSE +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO BooleanTrigger [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFTime set_triggerTime +eventOut SFBool triggerTrue +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Box [ #%NDT=SFWorldNode,SFGeometryNode +field SFVec3f size 2 2 2 +#X3D extensions +exposedField SFMetadataNode metadata NULL +#field SFBool solid TRUE +] { +} + +PROTO Circle2D [ #%NDT=SFWorldNode,SFGeometryNode +exposedField SFFloat radius 1 +exposedField SFMetadataNode metadata NULL +] { +} + +#note the MPEG-4 version uses "collide" instead of 'enabled" +PROTO Collision [ #%NDT=SFWorldNode,SF3DNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFBool enabled TRUE +field SF3DNode proxy NULL +eventOut SFTime collideTime +eventOut SFBool isActive +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Color [ #%NDT=SFWorldNode,SFColorNode +exposedField MFColor color [] +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO ColorInterpolator [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFColor keyValue [] +eventOut SFColor value_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO ColorRGBA [ #%NDT=SFWorldNode,SFColorNode +exposedField MFColorRGBA color [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Cone [ #%NDT=SFWorldNode,SFGeometryNode +field SFFloat bottomRadius 1 +field SFFloat height 2 +field SFBool side TRUE +field SFBool bottom TRUE +#X3D extensions +exposedField SFMetadataNode metadata NULL +#field SFBool solid TRUE +] { +} + +PROTO Contour2D [#%NDT=SFWorldNode,SFNurbsControlCurveNode +eventIn MFNurbsControlCurveNode addChildren +eventIn MFNurbsControlCurveNode removeChildren +exposedField MFNurbsControlCurveNode children [] +exposedField SFMetadataNode metadata NULL +]{ +} + +PROTO ContourPolyline2D #%NDT=SFWorldNode,SFNurbsControlCurveNode + exposedField MFVec2f point [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Coordinate [ #%NDT=SFWorldNode,SFCoordinateNode +exposedField MFVec3f point [] +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} +PROTO CoordinateDouble [ #%NDT=SFWorldNode,SFCoordinateNode +exposedField MFVec3d point [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Coordinate2D [ #%NDT=SFWorldNode,SFCoordinate2DNode +exposedField MFVec2f point [] +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO CoordinateInterpolator [ #%NDT=SFWorldNode,SF3DNode +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFVec3f keyValue [] +eventOut MFVec3f value_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO CoordinateInterpolator2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFVec2f keyValue [] +eventOut MFVec2f value_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Cylinder [ #%NDT=SFWorldNode,SFGeometryNode +field SFBool bottom TRUE +field SFFloat height 2 +field SFFloat radius 1 +field SFBool side TRUE +field SFBool top TRUE +#X3D extensions +exposedField SFMetadataNode metadata NULL +#field SFBool solid TRUE +] { +} + +PROTO CylinderSensor [ #%NDT=SFWorldNode,SF3DNode +exposedField SFBool autoOffset TRUE +exposedField SFFloat diskAngle 0.2617 +exposedField SFBool enabled TRUE +exposedField SFFloat maxAngle -1 +exposedField SFFloat minAngle 0 +exposedField SFFloat offset 0 +eventOut SFBool isActive +eventOut SFRotation rotation_changed +eventOut SFVec3f trackPoint_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +exposedField SFString description "" +eventOut SFBool isOver +] { +} + +PROTO DirectionalLight [ #%NDT=SFWorldNode,SF3DNode +exposedField SFFloat ambientIntensity 0 +exposedField SFColor color 1 1 1 +exposedField SFVec3f direction 0 0 -1 +exposedField SFFloat intensity 1 +exposedField SFBool on TRUE +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Disk2D [#%NDT=SFWorldNode,SFGeometryNode +field SFFloat innerRadius 0 +field SFFloat outerRadius 1 +#field SFBool solid FALSE +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO ElevationGrid [ #%NDT=SFWorldNode,SFGeometryNode +eventIn MFFloat set_height +exposedField SFColorNode color NULL +exposedField SFNormalNode normal NULL +exposedField SFTextureCoordinateNode texCoord NULL +field MFFloat height [] +field SFBool ccw TRUE +field SFBool colorPerVertex TRUE +field SFFloat creaseAngle 0.0 +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +field SFInt32 xDimension 0 +field SFFloat xSpacing 1.0 +field SFInt32 zDimension 0 +field SFFloat zSpacing 1.0 +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO EspduTransform [#%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +eventIn SFFloat set_articulationParameterValue0 +eventIn SFFloat set_articulationParameterValue1 +eventIn SFFloat set_articulationParameterValue2 +eventIn SFFloat set_articulationParameterValue3 +eventIn SFFloat set_articulationParameterValue4 +eventIn SFFloat set_articulationParameterValue5 +eventIn SFFloat set_articulationParameterValue6 +eventIn SFFloat set_articulationParameterValue7 +exposedField SFString address "localhost" +exposedField SFInt32 applicationID 1 +exposedField SFInt32 articulationParameterCount 0 +exposedField MFInt32 articulationParameterDesignatorArray [] +exposedField MFInt32 articulationParameterChangeIndicatorArray [] +exposedField MFInt32 articulationParameterIdPartAttachedToArray [] +exposedField MFInt32 articulationParameterTypeArray [] +exposedField MFFloat articulationParameterArray [] +exposedField SFVec3f center 0 0 0 +exposedField MF3DNode children [] +exposedField SFInt32 collisionType 0 +exposedField SFInt32 deadReckoning 0 +exposedField SFVec3f detonationLocation 0 0 0 +exposedField SFVec3f detonationRelativeLocation 0 0 0 +exposedField SFInt32 detonationResult 0 +exposedField SFInt32 entityCategory 0 +exposedField SFInt32 entityCountry 0 +exposedField SFInt32 entityDomain 0 +exposedField SFInt32 entityExtra 0 +exposedField SFInt32 entityID 0 +exposedField SFInt32 entityKind 0 +exposedField SFInt32 entitySpecific 0 +exposedField SFInt32 entitySubCategory 0 +exposedField SFInt32 eventApplicationID 1 +exposedField SFInt32 eventEntityID 0 +exposedField SFInt32 eventNumber 0 +exposedField SFInt32 eventSiteID 0 +exposedField SFBool fired1 FALSE +exposedField SFBool fired2 FALSE +exposedField SFInt32 fireMissionIndex 0 +exposedField SFFloat firingRange 0.0 +exposedField SFInt32 firingRate 0 +exposedField SFInt32 forceID 0 +exposedField SFInt32 fuse 0 +exposedField SFVec3f linearVelocity 0 0 0 +exposedField SFVec3f linearAcceleration 0 0 0 +exposedField SFString marking "" +exposedField SFString multicastRelayHost "" +exposedField SFInt32 multicastRelayPort 0 +exposedField SFInt32 munitionApplicationID 1 +exposedField SFVec3f munitionEndPoint 0 0 0 +exposedField SFInt32 munitionEntityID 0 +exposedField SFInt32 munitionQuantity 0 +exposedField SFInt32 munitionSiteID 0 +exposedField SFVec3f munitionStartPoint 0 0 0 +exposedField SFString networkMode "standAlone" +exposedField SFInt32 port 0 +exposedField SFTime readInterval 0.1 +exposedField SFRotation rotation 0 0 1 0 +exposedField SFVec3f scale 1 1 1 +exposedField SFRotation scaleOrientation 0 0 1 0 +exposedField SFInt32 siteID 0 +exposedField SFVec3f translation 0 0 0 +exposedField SFInt32 warhead 0 +exposedField SFTime writeInterval 1.0 +field SFBool rtpHeaderExpected FALSE +eventOut SFFloat articulationParameterValue0_changed 0.0 +eventOut SFFloat articulationParameterValue1_changed 0.0 +eventOut SFFloat articulationParameterValue2_changed 0.0 +eventOut SFFloat articulationParameterValue3_changed 0.0 +eventOut SFFloat articulationParameterValue4_changed 0.0 +eventOut SFFloat articulationParameterValue5_changed 0.0 +eventOut SFFloat articulationParameterValue6_changed 0.0 +eventOut SFFloat articulationParameterValue7_changed 0.0 +eventOut SFTime collideTime 0 +eventOut SFTime detonateTime 0 +eventOut SFTime firedTime 0 +eventOut SFBool isActive FALSE +eventOut SFBool isCollided FALSE +eventOut SFBool isDetonated FALSE +eventOut SFBool isNetworkReader FALSE +eventOut SFBool isNetworkWriter FALSE +eventOut SFBool isRtpHeaderHeard FALSE +eventOut SFBool isStandAlone FALSE +eventOut SFTime timestamp 0 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Extrusion [ #%NDT=SFWorldNode,SFGeometryNode +eventIn MFVec2f set_crossSection +eventIn MFRotation set_orientation +eventIn MFVec2f set_scale +eventIn MFVec3f set_spine +field SFBool beginCap TRUE +field SFBool ccw TRUE +field SFBool convex TRUE +field SFFloat creaseAngle 0.0 +field MFVec2f crossSection [ 1 1, 1 -1, -1 -1, -1 1, 1 1 ] +field SFBool endCap TRUE +field MFRotation orientation [0 0 1 0] +field MFVec2f scale [1 1] +field SFBool solid TRUE +field MFVec3f spine [ 0 0 0, 0 1 0 ] +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO FillProperties [ #%NDT=SFWorldNode,SFFillPropertiesNode +exposedField SFBool filled TRUE +exposedField SFColor hatchColor 1 1 1 +exposedField SFBool hatched TRUE +exposedField SFInt32 hatchStyle 1 +] { +} + +PROTO Fog [ #%NDT=SFWorldNode,SF3DNode,SFFogNode +exposedField SFColor color 1 1 1 +exposedField SFString fogType "LINEAR" +exposedField SFFloat visibilityRange 0 +eventIn SFBool set_bind +eventOut SFBool isBound +#X3D extensions +exposedField SFMetadataNode metadata NULL +eventOut SFTime bindTime +] { +} + + +PROTO FontStyle [ #%NDT=SFWorldNode,SFFontStyleNode +exposedField MFString family ["SERIF"] +exposedField SFBool horizontal TRUE +exposedField MFString justify ["BEGIN"] +exposedField SFString language "" +exposedField SFBool leftToRight TRUE +exposedField SFFloat size 1.0 +exposedField SFFloat spacing 1.0 +exposedField SFString style "PLAIN" +exposedField SFBool topToBottom TRUE +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO GeoCoordinate [#%NDT=SFWorldNode,SFCoordinateNode +exposedField MFVec3d point [] +field SFGeoOriginNode geoOrigin NULL +field MFString geoSystem ["GD", "WE"] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO GeoElevationGrid [#%NDT=SFWorldNode,SFGeometryNode +eventIn MFDouble set_height +exposedField SFColorNode color NULL +exposedField SFNormalNode normal NULL +exposedField SFTextureCoordinateNode texCoord NULL +exposedField SFFloat yScale 1.0 +field SFBool ccw TRUE +field SFBool colorPerVertex TRUE +field SFFloat creaseAngle 0.0 +field SFString geoGridOrigin "0 0 0" +field SFGeoOriginNode geoOrigin NULL +field MFString geoSystem ["GD", "WE"] +field MFDouble height [] +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +field SFInt32 xDimension 0 +field SFDouble xSpacing 1.0 +field SFInt32 zDimension 0 +field SFDouble zSpacing 1.0 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO GeoLocation [#%NDT=SFWorldNode,SF3DNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFVec3d geoCoords 0 0 0 +field SFGeoOriginNode geoOrigin NULL +field MFString geoSystem ["GD", "WE"] +exposedField SFMetadataNode metadata NULL +]{ +} + +#addChildren and removeChildren are commented, it looks like a bug in X3D spec +PROTO GeoLOD [#%NDT=SFWorldNode,SF3DNode +# eventIn MF3DNode addChildren +# eventIn MF3DNode removeChildren +field SFVec3d center 0 0 0 +field MFURL child1Url [] +field MFURL child2Url [] +field MFURL child3Url [] +field MFURL child4Url [] +field SFGeoOriginNode geoOrigin NULL +field MFString geoSystem ["GD","WE"] +field SFFloat range 10 +field MFURL rootUrl [] +field MF3DNode rootNode [] +eventOut MF3DNode children +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO GeoMetadata [#%NDT=SFWorldNode,SF3DNode,SF2DNode +exposedField MF3DNode data [] +exposedField MFString summary [] +exposedField MFURL url [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO GeoOrigin [%#NDT=SFGeoOriginNode +exposedField SFVec3d geoCoords 0 0 0 +exposedField MFString geoSystem ["GD","WE"] +field SFBool rotateYUp FALSE +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO GeoPositionInterpolator [ #%NDT=SFWorldNode,SF3DNode +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFVec3d keyValue [] +field SFGeoOriginNode geoOrigin NULL +field MFString geoSystem ["GD","WE"] +eventOut SFVec3d geovalue_changed +eventOut SFVec3f value_changed +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO GeoTouchSensor [ #%NDT=SFWorldNode,SF2DNode,SF3DNode +exposedField SFBool enabled TRUE +field SFGeoOriginNode geoOrigin NULL +field MFString geoSystem ["GD","WE"] +eventOut SFVec3f hitNormal_changed +eventOut SFVec3f hitPoint_changed +eventOut SFVec2f hitTexCoord_changed +eventOut SFVec3d hitGeoCoord_changed +eventOut SFBool isActive +eventOut SFBool isOver +eventOut SFTime touchTime +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO GeoViewpoint [#%NDT=SFWorldNode,SF3DNode,SFViewpointNode +eventIn SFBool set_bind +eventIn SFString set_orientation +eventIn SFString set_position +exposedField SFString description "" +exposedField SFFloat fieldOfView 0.785398 +exposedField SFBool headlight TRUE +exposedField SFBool jump TRUE +exposedField MFString navType ["EXAMINE","ANY"] +eventOut SFTime bindTime +eventOut SFBool isBound +field SFGeoOriginNode geoOrigin NULL +field MFString geoSystem ["GD","WE"] +field SFRotation orientation 0 0 1 0 +field SFVec3d position 0 0 100000 +field SFFloat speedFactor 1.0 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Group [ #%NDT=SFWorldNode,SFTopNode,SF3DNode,SF2DNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO HAnimDisplacer [#%NDT=SFWorldNode,SFHAnimDisplacerNode +exposedField MFInt32 coordIndex [] +exposedField MFVec3f displacements [] +exposedField SFString name "" +exposedField SFFloat weight 0.0 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO HAnimHumanoid [#%NDT=SFWorldNode,SF3DNode +exposedField SFVec3f center 0 0 0 +exposedField MFString info [] +exposedField MFHAnimNode joints [] +exposedField SFString name "" +exposedField SFRotation rotation 0 0 1 0 +exposedField SFVec3f scale 1 1 1 +exposedField SFRotation scaleOrientation 0 0 1 0 +exposedField MFHAnimNode segments [] +exposedField MFHAnimNode sites [] +exposedField MFHAnimNode skeleton [] +exposedField MF3DNode skin [] +exposedField SFCoordinateNode skinCoord NULL +exposedField SFNormalNode skinNormal NULL +exposedField SFVec3f translation 0 0 0 +exposedField SFString version "" +exposedField MFViewpointNode viewpoints [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO HAnimJoint [#%NDT=SFWorldNode,SFHAnimNode +eventIn MFHAnimNode addChildren +eventIn MFHAnimNode removeChildren +exposedField MFHAnimNode children [] +exposedField SFVec3f center 0 0 0 +exposedField MFHAnimDisplacerNode displacers [] +exposedField SFRotation limitOrientation 0 0 1 0 +exposedField MFFloat llimit [] +exposedField SFString name "" +exposedField SFRotation rotation 0 0 1 0 +exposedField SFVec3f scale 1 1 1 +exposedField SFRotation scaleOrientation 0 0 1 0 +exposedField MFInt32 skinCoordIndex [] +exposedField MFFloat skinCoordWeight [] +exposedField MFFloat stiffness [0 0 0] +exposedField SFVec3f translation 0 0 0 +exposedField MFFloat ulimit [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO HAnimSegment [#%NDT=SFWorldNode,SFHAnimNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFVec3f centerOfMass 0 0 0 +exposedField SFCoordinateNode coord NULL +exposedField MFHAnimDisplacerNode displacers [] +exposedField SFFloat mass 0 +exposedField MFFloat momentsOfInertia [0 0 0 0 0 0 0 0 0] +exposedField SFString name "" +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO HAnimSite [#%NDT=SFWorldNode,SFHAnimNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFVec3f center 0 0 0 +exposedField SFString name "" +exposedField SFRotation rotation 0 0 1 0 +exposedField SFVec3f scale 1 1 1 +exposedField SFRotation scaleOrientation 0 0 1 0 +exposedField SFVec3f translation 0 0 0 +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO ImageTexture [ #%NDT=SFWorldNode,SFTextureNode +exposedField MFURL url [] +field SFBool repeatS TRUE +field SFBool repeatT TRUE +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO IndexedFaceSet [ #%NDT=SFWorldNode,SFGeometryNode +eventIn MFInt32 set_colorIndex +eventIn MFInt32 set_coordIndex +eventIn MFInt32 set_normalIndex +eventIn MFInt32 set_texCoordIndex +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField SFNormalNode normal NULL +exposedField SFTextureCoordinateNode texCoord NULL +field SFBool ccw TRUE +field MFInt32 colorIndex [] +field SFBool colorPerVertex TRUE +field SFBool convex TRUE +field MFInt32 coordIndex [] +field SFFloat creaseAngle 0.0 +field MFInt32 normalIndex [] +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +field MFInt32 texCoordIndex [] +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO IndexedLineSet [ #%NDT=SFWorldNode,SFGeometryNode +eventIn MFInt32 set_colorIndex +eventIn MFInt32 set_coordIndex +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +field MFInt32 colorIndex [] +field SFBool colorPerVertex TRUE +field MFInt32 coordIndex [] +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO IndexedTriangleFanSet [ #%NDT=SFWorldNode,SFGeometryNode +eventIn MFInt32 set_index +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField SFNormalNode normal NULL +exposedField SFTextureCoordinateNode texCoord NULL +field SFBool ccw TRUE +field SFBool colorPerVertex TRUE +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +field MFInt32 index [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO IndexedTriangleSet [ #%NDT=SFWorldNode,SFGeometryNode +eventIn MFInt32 set_index +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField SFNormalNode normal NULL +exposedField SFTextureCoordinateNode texCoord NULL +field SFBool ccw TRUE +field SFBool colorPerVertex TRUE +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +field MFInt32 index [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO IndexedTriangleStripSet [ #%NDT=SFWorldNode,SFGeometryNode +eventIn MFInt32 set_index +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField SFFloat creaseAngle 0 +exposedField SFNormalNode normal NULL +exposedField SFTextureCoordinateNode texCoord NULL +field SFBool ccw TRUE +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +field MFInt32 index [] +exposedField SFMetadataNode metadata NULL +] { +} + + + +PROTO Inline [ #%NDT=SFWorldNode,SF3DNode,SFStreamingNode,SF2DNode +exposedField MFURL url [] +#X3D extensions +exposedField SFMetadataNode metadata NULL +exposedField SFBool load TRUE +] { +} + + +PROTO IntegerSequencer [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFBool next +eventIn SFBool previous +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFInt32 keyValue [] +eventOut SFInt32 value_changed +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO IntegerTrigger [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFBool set_boolean +exposedField SFInt32 integerKey -1 +eventOut SFInt32 triggerValue +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO KeySensor [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +exposedField SFBool enabled TRUE +eventOut SFInt32 actionKeyPress +eventOut SFInt32 actionKeyRelease +eventOut SFBool altKey +eventOut SFBool controlKey +eventOut SFBool isActive +eventOut SFString keyPress +eventOut SFString keyRelease +eventOut SFBool shiftKey +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO LineProperties [ #%NDT=SFWorldNode,SFX3DLinePropertiesNode +exposedField SFBool applied TRUE +exposedField SFInt32 linetype 1 +exposedField SFFloat linewidthScaleFactor 0 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO LineSet [ #%NDT=SFWorldNode,SFGeometryNode +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField MFInt32 vertexCount [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO LoadSensor [ #%NDT=SFWorldNode,SFStreamingNode +exposedField SFBool enabled TRUE +exposedField SFTime timeOut 0 +exposedField MFStreamingNode watchList [] +eventOut SFBool isActive +eventOut SFBool isLoaded +eventOut SFTime loadTime +eventOut SFFloat progress +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO LOD [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +field SFVec3f center 0 0 0 +field MFFloat range [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Material [ #%NDT=SFWorldNode,SFMaterialNode +exposedField SFFloat ambientIntensity 0.2 +exposedField SFColor diffuseColor 0.8 0.8 0.8 +exposedField SFColor emissiveColor 0 0 0 +exposedField SFFloat shininess 0.2 +exposedField SFColor specularColor 0 0 0 +exposedField SFFloat transparency 0 +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + + + +PROTO MetadataDouble [ #%NDT=SFWorldNode,SFMetadataNode +exposedField SFString name "" +exposedField SFString reference "" +exposedField MFDouble value [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO MetadataFloat[ #%NDT=SFWorldNode,SFMetadataNode +exposedField SFString name "" +exposedField SFString reference "" +exposedField MFFloat value [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO MetadataInteger [ #%NDT=SFWorldNode,SFMetadataNode +exposedField SFString name "" +exposedField SFString reference "" +exposedField MFInt32 value [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO MetadataSet [ #%NDT=SFWorldNode,SFMetadataNode +exposedField SFString name "" +exposedField SFString reference "" +exposedField MFMetadataNode value [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO MetadataString [ #%NDT=SFWorldNode,SFMetadataNode +exposedField SFString name "" +exposedField SFString reference "" +exposedField MFString value [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO MovieTexture [ #%NDT=SFWorldNode,SFTextureNode,SFStreamingNode +exposedField SFBool loop FALSE +exposedField SFFloat speed 1.0 +exposedField SFTime startTime 0 +exposedField SFTime stopTime 0 +exposedField MFURL url [] +field SFBool repeatS TRUE +field SFBool repeatT TRUE +eventOut SFTime duration_changed +eventOut SFBool isActive +#X3D extensions +exposedField SFMetadataNode metadata NULL +exposedField SFTime resumeTime 0 +exposedField SFTime pauseTime 0 +eventOut SFTime elapsedTime +eventOut SFBool isPaused +] { +} + +PROTO MultiTexture [ #%NDT=SFWorldNode,SFTextureNode +exposedField SFFloat alpha 1 +exposedField SFColor color 1 1 1 +exposedField MFString function [] +exposedField MFString mode [] +exposedField MFString source [] +exposedField MFTextureNode texture [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO MultiTextureCoordinate [ #%NDT=SFWorldNode,SFTextureCoordinateNode +MultiTextureCoordinate MFTextureCoordinateNode texCoord NULL +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO MultiTextureTransform [ #%NDT=SFWorldNode,SFTextureTransformNode +exposedField MFTextureTransformNode textureTransform [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO NavigationInfo [ #%NDT=SFWorldNode,SF3DNode,SFNavigationInfoNode +eventIn SFBool set_bind +exposedField MFFloat avatarSize [0.25, 1.6, 0.75] +exposedField SFBool headlight TRUE +exposedField SFFloat speed 1.0 +exposedField MFString type ["WALK", "ANY"] +exposedField SFFloat visibilityLimit 0.0 +eventOut SFBool isBound +#X3D extensions +exposedField SFMetadataNode metadata NULL +exposedField MFString transitionType ["WALK", "ANY"] +eventOut SFTime bindTime +]{ +} + +PROTO Normal [ #%NDT=SFWorldNode,SFNormalNode +exposedField MFVec3f vector [] +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO NormalInterpolator [ #%NDT=SFWorldNode,SF3DNode +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFVec3f keyValue [] +eventOut MFVec3f value_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO NurbsCurve [ #%NDT=SFWorldNode,SFGeometryNode,SFNurbsCurveNode +exposedField MFVec3f controlPoint [] +exposedField SFInt32 tessellation 0 +exposedField MFDouble weight [] +field SFBool closed FALSE +field MFFloat knot [] +field SFInt32 order 3 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO NurbsCurve2D [ #%NDT=SFWorldNode,SFNurbsControlCurveNode +exposedField MFVec2f controlPoint [] +exposedField SFInt32 tessellation 0 +exposedField MFFloat weight [] +field MFFloat knot [] +field SFInt32 order 3 +field SFBool closed FALSE +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO NurbsOrientationInterpolator [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFFloat set_fraction +exposedField SFCoordinateNode controlPoints NULL +exposedField MFDouble knot [] +exposedField SFInt32 order 3 +exposedField MFDouble weight [] +eventOut SFRotation value_changed +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO NurbsPatchSurface [ #%NDT=SFWorldNode,SFGeometryNode,SFNurbsSurfaceNode +exposedField SFCoordinateNode controlPoint NULL +exposedField SFTextureCoordinateNode texCoord NULL +exposedField SFInt32 uTessellation 0 +exposedField SFInt32 vTessellation 0 +exposedField MFDouble weight [] +field SFBool solid TRUE +field SFBool uClosed FALSE +field SFInt32 uDimension 0 +field MFDouble uKnot [] +field SFInt32 uOrder 3 +field SFBool vClosed FALSE +field SFInt32 vDimension 0 +field MFDouble vKnot [] +field SFInt32 vOrder 3 +exposedField SFMetadataNode metadata NULL +] { + } + +PROTO NurbsPositionInterpolator [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFFloat set_fraction +exposedField SFCoordinateNode controlPoints NULL +exposedField MFDouble knot [] +exposedField SFInt32 order 3 +exposedField MFDouble weight [] +eventOut SFVec3f value_changed +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO NurbsSet [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn MFNurbsSurfaceNode addGeometry +eventIn MFNurbsSurfaceNode removeGeometry +exposedField MFNurbsSurfaceNode geometry [] +exposedField SFFloat tessellationScale 1.0 +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO NurbsSurfaceInterpolator [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFVec2f set_fraction +exposedField SFCoordinateNode controlPoints NULL +exposedField MFDouble weight [] +eventOut SFVec3f position_changed +eventOut SFVec3f normal_changed +field SFInt32 uDimension 0 +field MFDouble uKnot [] +field SFInt32 uOrder 3 +field SFInt32 vDimension 0 +field MFDouble vKnot [] +field SFInt32 vOrder 3 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO NurbsSweptSurface [ #%NDT=SFWorldNode,SFGeometryNode,SFNurbsSurfaceNode +exposedField SFNurbsControlCurveNode crossSectionCurve NULL +exposedField SFNurbsCurveNode trajectoryCurve NULL +field SFBool ccw TRUE +field SFBool solid TRUE +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO NurbsSwungSurface [ #%NDT=SFWorldNode,SFGeometryNode,SFNurbsSurfaceNode +exposedField SFNurbsControlCurveNode profileCurve NULL +exposedField SFNurbsControlCurveNode trajectoryCurve NULL +field SFBool ccw TRUE +field SFBool solid TRUE +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO NurbsTextureCoordinate [ #%NDT=SFWorldNode,SFTextureCoordinateNode +exposedField MFVec2f controlPoint [] +exposedField MFFloat weight [] +field SFInt32 uDimension 0 +field MFDouble uKnot [] +field SFInt32 uOrder 3 +field SFInt32 vDimension 0 +field MFDouble vKnot [] +field SFInt32 vOrder 3 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO NurbsTrimmedSurface [ #%NDT=SFWorldNode,SFGeometryNode,SFNurbsSurfaceNode +eventIn MFNurbsControlCurveNode addTrimmingContour +eventIn MFNurbsControlCurveNode removeTrimmingContour +exposedField MFNurbsControlCurveNode trimmingContour [] +exposedField SFCoordinateNode controlPoint NULL +exposedField SFTextureCoordinateNode texCoord NULL +exposedField SFInt32 uTessellation 0 +exposedField SFInt32 vTessellation 0 +exposedField MFDouble weight [] +field SFBool solid TRUE +field SFBool uClosed FALSE +field SFInt32 uDimension 0 +field MFDouble uKnot [] +field SFInt32 uOrder 3 +field SFBool vClosed FALSE +field SFInt32 vDimension 0 +field MFDouble vKnot [] +field SFInt32 vOrder 3 +exposedField SFMetadataNode metadata NULL +] { +} + + + +PROTO OrientationInterpolator [ #%NDT=SFWorldNode,SF3DNode +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFRotation keyValue [] +eventOut SFRotation value_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO PixelTexture [ #%NDT=SFWorldNode,SFTextureNode +exposedField SFImage image 0 0 0 +field SFBool repeatS TRUE +field SFBool repeatT TRUE +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO PlaneSensor [ #%NDT=SFWorldNode,SF3DNode +exposedField SFBool autoOffset TRUE +exposedField SFBool enabled TRUE +exposedField SFVec2f maxPosition -1 -1 +exposedField SFVec2f minPosition 0 0 +exposedField SFVec3f offset 0 0 0 +eventOut SFBool isActive +eventOut SFVec3f trackPoint_changed +eventOut SFVec3f translation_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +exposedField SFString description "" +eventOut SFBool isOver +] { +} + +PROTO PointLight [ #%NDT=SFWorldNode,SF3DNode +exposedField SFFloat ambientIntensity 0 +exposedField SFVec3f attenuation 1 0 0 +exposedField SFColor color 1 1 1 +exposedField SFFloat intensity 1 +exposedField SFVec3f location 0 0 0 +exposedField SFBool on TRUE +exposedField SFFloat radius 100 +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO PointSet [ #%NDT=SFWorldNode,SFGeometryNode +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Polyline2D [#%NDT=SFWorldNode,SFGeometryNode +exposedField MFVec2f lineSegments [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Polypoint2D [#%NDT=SFWorldNode,SFGeometryNode +exposedField MFVec2f point [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO PositionInterpolator [ #%NDT=SFWorldNode,SF3DNode +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFVec3f keyValue [] +eventOut SFVec3f value_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO PositionInterpolator2D [ #%NDT=SFWorldNode,SF2DNode,SF3DNode +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFVec2f keyValue [] +eventOut SFVec2f value_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO ProximitySensor [ #%NDT=SFWorldNode,SF3DNode +exposedField SFVec3f center 0 0 0 +exposedField SFVec3f size 0 0 0 +exposedField SFBool enabled TRUE +eventOut SFBool isActive +eventOut SFVec3f position_changed +eventOut SFRotation orientation_changed +eventOut SFTime enterTime +eventOut SFTime exitTime +#X3D extensions +exposedField SFMetadataNode metadata NULL +eventOut SFVec3f centerOfRotation_changed +] { +} + +PROTO ReceiverPdu [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +exposedField SFString address "localhost" +exposedField SFInt32 applicationID 1 +exposedField SFInt32 entityID 0 +exposedField SFString multicastRelayHost "" +exposedField SFInt32 multicastRelayPort 0 +exposedField SFString networkMode "standAlone" +exposedField SFInt32 port 0 +exposedField SFInt32 radioID 0 +exposedField SFFloat readInterval 0.1 +exposedField SFFloat receivedPower 0.0 +exposedField SFInt32 receiverState 0 +exposedField SFBool rtpHeaderExpected +exposedField SFInt32 siteID 0 +exposedField SFInt32 transmitterApplicationID 1 +exposedField SFInt32 transmitterEntityID 0 +exposedField SFInt32 transmitterRadioID 0 +exposedField SFInt32 transmitterSiteID 0 +exposedField SFInt32 whichGeometry 1 +exposedField SFFloat writeInterval 1.0 +eventOut SFBool isActive FALSE +eventOut SFBool isNetworkReader FALSE +eventOut SFBool isNetworkWriter FALSE +eventOut SFBool isRtpHeaderHeard FALSE +eventOut SFBool isStandAlone FALSE +eventOut SFTime timestamp 0 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Rectangle2D [ #%NDT=SFWorldNode,SFGeometryNode +field SFVec2f size 2 2 +#X3D extensions +exposedField SFMetadataNode metadata NULL +#exposedField SFBool filled TRUE +]{ +} + +PROTO ScalarInterpolator [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFFloat set_fraction +exposedField MFFloat key [] +exposedField MFFloat keyValue [] +eventOut SFFloat value_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +]{} + +PROTO Script [#%NDT=SFWorldNode,SF3DNode,SF2DNode +exposedField MFScript url [] +field SFBool directOutput FALSE +field SFBool mustEvaluate FALSE +#X3D extensions +exposedField SFMetadataNode metadata NULL +]{ +} + +PROTO Shape [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +exposedField SFAppearanceNode appearance NULL +exposedField SFGeometryNode geometry NULL +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO SignalPdu [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +exposedField SFString address "localhost" +exposedField SFInt32 applicationID 1 +exposedField MFInt32 data [] +exposedField SFInt32 dataLength 0 +exposedField SFInt32 encodingScheme 0 +exposedField SFInt32 entityID 0 +exposedField SFString multicastRelayHost "" +exposedField SFInt32 multicastRelayPort 0 +exposedField SFString networkMode "standAlone" +exposedField SFInt32 port 0 +exposedField SFInt32 radioID 0 +exposedField SFFloat readInterval 0.1 +exposedField SFBool rtpHeaderExpected FALSE +exposedField SFInt32 sampleRate 0 +exposedField SFInt32 samples 0 +exposedField SFInt32 siteID 0 +exposedField SFInt32 tdlType 0 +exposedField SFInt32 whichGeometry 1 +exposedField SFFloat writeInterval 1.0 +eventOut SFBool isActive +eventOut SFBool isNetworkReader +eventOut SFBool isNetworkWriter +eventOut SFBool isRtpHeaderHeard +eventOut SFBool isStandAlone +eventOut SFTime timestamp +exposedField SFMetadataNode metadata NULL +] { +} + + + +PROTO Sound [ #%NDT=SFWorldNode,SF3DNode +exposedField SFVec3f direction 0 0 1 +exposedField SFFloat intensity 1 +exposedField SFVec3f location 0 0 0 +exposedField SFFloat maxBack 10 +exposedField SFFloat maxFront 10 +exposedField SFFloat minBack 1 +exposedField SFFloat minFront 1 +exposedField SFFloat priority 0 +exposedField SFAudioNode source NULL +field SFBool spatialize TRUE +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO Sphere [ #%NDT=SFWorldNode,SFGeometryNode +field SFFloat radius 1 +#X3D extensions +exposedField SFMetadataNode metadata NULL +#field SFBool solid TRUE +] { +} + + +PROTO SphereSensor [ #%NDT=SFWorldNode,SF3DNode +exposedField SFBool autoOffset TRUE +exposedField SFBool enabled TRUE +exposedField SFRotation offset 0 1 0 0 +eventOut SFBool isActive +eventOut SFRotation rotation_changed +eventOut SFVec3f trackPoint_changed +#X3D extensions +exposedField SFMetadataNode metadata NULL +exposedField SFString description "" +eventOut SFBool isOver +]{ +} + +PROTO SpotLight [ #%NDT=SFWorldNode,SF3DNode +exposedField SFFloat ambientIntensity 0 +exposedField SFVec3f attenuation 1 0 0 +exposedField SFFloat beamWidth 1.570796 +exposedField SFColor color 1 1 1 +exposedField SFFloat cutOffAngle 0.785398 +exposedField SFVec3f direction 0 0 -1 +exposedField SFFloat intensity 1 +exposedField SFVec3f location 0 0 0 +exposedField SFBool on TRUE +exposedField SFFloat radius 100 +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO StaticGroup [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +field MF3DNode children [] +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO StringSensor [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +exposedField SFBool deletionAllowed TRUE +exposedField SFBool enabled TRUE +eventOut SFString enteredText +eventOut SFString finalText +eventOut SFBool isActive +exposedField SFMetadataNode metadata NULL +] { +} + + + +PROTO Switch [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField MF3DNode children [] +exposedField SFInt32 whichChoice -1 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Text [ #%NDT=SFWorldNode,SFGeometryNode +exposedField MFString string [] +exposedField MFFloat length [] +exposedField SFFontStyleNode fontStyle NULL +exposedField SFFloat maxExtent 0.0 +#X3D extensions +exposedField SFMetadataNode metadata NULL +#field SFBool solid FALSE +] { +} + +PROTO TextureBackground [ #%NDT=SFWorldNode,SF3DNode,SFBackground3DNode +eventIn SFBool set_bind +exposedField MFFloat groundAngle [] +exposedField MFColor groundColor [] +exposedField SFTextureNode backTexture NULL +exposedField SFTextureNode bottomTexture NULL +exposedField SFTextureNode frontTexture NULL +exposedField SFTextureNode leftTexture NULL +exposedField SFTextureNode rightTexture NULL +exposedField SFTextureNode topTexture NULL +exposedField MFFloat skyAngle [] +exposedField MFColor skyColor 0 0 0 +exposedField MFFloat transparency 0 +exposedField SFTime bindTime +exposedField SFBool isBound +exposedField SFMetadataNode metadata NULL +] { +} + + + +PROTO TextureCoordinate [ #%NDT=SFWorldNode,SFTextureCoordinateNode +exposedField MFVec2f point [] +#X3D extensions +exposedField SFMetadataNode metadata NULL +]{ +} + +PROTO TextureCoordinateGenerator [ #%NDT=SFWorldNode,SFTextureCoordinateNode +exposedField SFString mode "SPHERE" +TextureCoordinateGenerator MFFloat parameter [] +exposedField SFMetadataNode metadata NULL +] { +} + + + +PROTO TextureTransform [ #%NDT=SFWorldNode,SFTextureTransformNode +exposedField SFVec2f center 0 0 +exposedField SFFloat rotation 0 +exposedField SFVec2f scale 1 1 +exposedField SFVec2f translation 0 0 +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO TimeSensor [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +exposedField SFTime cycleInterval 1 +exposedField SFBool enabled TRUE +exposedField SFBool loop FALSE +exposedField SFTime startTime 0 +exposedField SFTime stopTime 0 +eventOut SFTime cycleTime +eventOut SFFloat fraction_changed +eventOut SFBool isActive +eventOut SFTime time +#X3D extensions +exposedField SFMetadataNode metadata NULL +exposedField SFTime pauseTime 0 +exposedField SFTime resumeTime 0 +eventOut SFTime elapsedTime +eventOut SFBool isPaused +] { +} + +PROTO TimeTrigger [ #%NDT=SFWorldNode,SF3DNode,SF2DNode +eventIn SFBool set_boolean +eventOut SFTime triggerTime +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO TouchSensor [ #%NDT=SFWorldNode,SF2DNode,SF3DNode +exposedField SFBool enabled TRUE +eventOut SFVec3f hitNormal_changed +eventOut SFVec3f hitPoint_changed +eventOut SFVec2f hitTexCoord_changed +eventOut SFBool isActive +eventOut SFBool isOver +eventOut SFTime touchTime +#X3D extensions +exposedField SFMetadataNode metadata NULL +exposedField SFString description "" +] {} + +PROTO Transform [ #%NDT=SFWorldNode,SF3DNode +eventIn MF3DNode addChildren +eventIn MF3DNode removeChildren +exposedField SFVec3f center 0 0 0 +exposedField MF3DNode children [] +exposedField SFRotation rotation 0 0 1 0 +exposedField SFVec3f scale 1 1 1 +exposedField SFRotation scaleOrientation 0 0 1 0 +exposedField SFVec3f translation 0 0 0 +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO TransmitterPdu [ #%NDT=SFWorldNode,SF2DNode,SF3DNode +exposedField SFString address "localhost" +exposedField SFVec3f antennaLocation 0 0 0 +exposedField SFInt32 antennaPatternLength 0 +exposedField SFInt32 antennaPatternType 0 +exposedField SFInt32 applicationID 1 +exposedField SFInt32 cryptoKeyID 0 +exposedField SFInt32 cryptoSystem 0 +exposedField SFInt32 entityID 0 +exposedField SFInt32 frequency 0 +exposedField SFInt32 inputSource 0 +exposedField SFInt32 lengthOfModulationParameters 0 +exposedField SFInt32 modulationTypeDetail 0 +exposedField SFInt32 modulationTypeMajor 0 +exposedField SFInt32 modulationTypeSpreadSpectrum 0 +exposedField SFInt32 modulationTypeSystem 0 +exposedField SFString multicastRelayHost "" +exposedField SFInt32 multicastRelayPort 0 +exposedField SFString networkMode "standAlone" +exposedField SFInt32 port 0 +exposedField SFFloat power 0.0 +exposedField SFInt32 radioEntityTypeCategory 0 +exposedField SFInt32 radioEntityTypeCountry 0 +exposedField SFInt32 radioEntityTypeDomain 0 +exposedField SFInt32 radioEntityTypeKind 0 +exposedField SFInt32 radioEntityTypeNomenclature 0 +exposedField SFInt32 radioEntityTypeNomenclatureVersion 0 +exposedField SFInt32 radioID 0 +exposedField SFFloat readInterval 0.1 +exposedField SFVec3f relativeAntennaLocation 0 0 0 +exposedField SFBool rtpHeaderExpected FALSE +exposedField SFInt32 siteID 0 +exposedField SFFloat transmitFrequencyBandwidth 0.0 +exposedField SFInt32 transmitState 0 +exposedField SFInt32 whichGeometry 1 +exposedField SFFloat writeInterval 1.0 +eventOut SFBool isActive FALSE +eventOut SFBool isNetworkReader FALSE +eventOut SFBool isNetworkWriter FALSE +eventOut SFBool isRtpHeaderHeard FALSE +eventOut SFBool isStandAlone FALSE +eventOut SFTime timestamp 0 +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO TriangleFanSet [ #%NDT=SFWorldNode,SFGeometryNode +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField MFInt32 fanCount [] +exposedField SFNormalNode normal NULL +exposedField SFTextureCoordinateNode texCoord NULL +field SFBool ccw TRUE +field SFBool colorPerVertex TRUE +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +exposedField SFMetadataNode metadata NULL +] { +} + + +PROTO TriangleSet [ #%NDT=SFWorldNode,SFGeometryNode +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField SFNormalNode normal NULL +exposedField SFTextureCoordinateNode texCoord NULL +field SFBool ccw TRUE +field SFBool colorPerVertex TRUE +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO TriangleSet2D [ #%NDT=SFWorldNode,SFGeometryNode +exposedField MFVec2f vertices [] +#field SFBool solid FALSE +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO TriangleStripSet [ #%NDT=SFWorldNode,SFGeometryNode +exposedField SFColorNode color NULL +exposedField SFCoordinateNode coord NULL +exposedField SFNormalNode normal NULL +exposedField MFInt32 stripCount [] +exposedField SFTextureCoordinateNode texCoord NULL +field SFBool ccw TRUE +field SFBool colorPerVertex TRUE +field SFBool normalPerVertex TRUE +field SFBool solid TRUE +exposedField SFMetadataNode metadata NULL +] { +} + +PROTO Viewpoint [ #%NDT=SFWorldNode,SF3DNode,SFViewpointNode +eventIn SFBool set_bind +exposedField SFFloat fieldOfView 0.785398 +exposedField SFBool jump TRUE +exposedField SFRotation orientation 0 0 1 0 +exposedField SFVec3f position 0 0 10 +field SFString description "" +eventOut SFTime bindTime +eventOut SFBool isBound +#X3D extensions +exposedField SFMetadataNode metadata NULL +exposedField SFVec3f centerOfRotation 0 0 0 +] { +} + + +PROTO VisibilitySensor [ #%NDT=SFWorldNode,SF3DNode +exposedField SFVec3f center 0 0 0 +exposedField SFBool enabled TRUE +exposedField SFVec3f size 0 0 0 +eventOut SFTime enterTime +eventOut SFTime exitTime +eventOut SFBool isActive +#X3D extensions +exposedField SFMetadataNode metadata NULL +]{ +} + +PROTO WorldInfo [ #%NDT=SFWorldNode,SF2DNode,SF3DNode +field MFString info [] +field SFString title "" +#X3D extensions +exposedField SFMetadataNode metadata NULL +] { +} diff --git a/applications/mp42avi/Makefile b/applications/mp42avi/Makefile new file mode 100644 index 0000000..77295e4 --- /dev/null +++ b/applications/mp42avi/Makefile @@ -0,0 +1,59 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/applications/mp42avi + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= main.o + + +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=MP42Avi$(EXE) +else +EXT= +PROG=MP42Avi +endif + +SRCS := $(OBJS:.o=.c) + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) -L../../bin/$(TARGET_BIN_DIR) -lgpac -lz + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(PROG) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/applications/mp42avi/main.c b/applications/mp42avi/main.c new file mode 100644 index 0000000..fa75d56 --- /dev/null +++ b/applications/mp42avi/main.c @@ -0,0 +1,850 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / command-line mp4 toolbox + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#define GPAC_CFG_FILE "GPAC.cfg" +#else +#include +typedef struct tagBITMAPFILEHEADER +{ + u16 bfType; + u32 bfSize; + u16 bfReserved1; + u16 bfReserved2; + u32 bfOffBits; +} BITMAPFILEHEADER; + +typedef struct tagBITMAPINFOHEADER{ + u32 biSize; + s32 biWidth; + s32 biHeight; + u16 biPlanes; + u16 biBitCount; + u32 biCompression; + u32 biSizeImage; + s32 biXPelsPerMeter; + s32 biYPelsPerMeter; + u32 biClrUsed; + u32 biClrImportant; +} BITMAPINFOHEADER; + +#define BI_RGB 0L + +#define GPAC_CFG_FILE ".gpacrc" +#endif + +#include +#include + +void PrintVersion() +{ + printf ("MP42AVI - GPAC version %s\n", GPAC_FULL_VERSION); +} + +void PrintUsage() +{ + printf ("MP42AVI [option] input\n" + "Dumps BIFS media frames as AVI, BMP or raw\n\n" + "Options\n" + "-fps Framerate: specifies extraction framerate - if not set computed from track length\n" + "-size WxH: forces output BIFS to the given resolution\n" + "-raw [frame]: uses raw format for output - only dumps one frame if specified\n" + "-bmp [frame]: uses BMP format for output - only dumps one frame if specified\n" + "-outpath path: specifies where to dump frames/movie\n" + "\n" + "Note: when dumping a frame, either the frame number can be specified or the frame time\n" + "in the format hh:mm:ss:xFz where hh, mm, ss are hours, minutes, seconds, x the number\n" + "of the frame in the seconds and z the frame rate used to express the time\n" + "\n" + "-cfg: specifies path to GPAC config file (GPAC.cfg)\n" + "-v: prints version\n" + "-h: prints this message\n" + "\nWritten by Jean Le Feuvre - (c) 2000-2005\n"); +} + + +typedef struct +{ + GF_Compositor *sr; + GF_SceneGraph *sg; + GF_BifsDecoder *bifs; + GF_ISOFile *file; + + u32 track; + u64 duration, cts; +} BIFSVID; + +void node_init(void *cbk, GF_Node *node) +{ + BIFSVID *b2v = cbk; + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_Conditional: + case TAG_MPEG4_QuantizationParameter: + break; + default: + if (b2v->sr) gf_sc_on_node_init(b2v->sr, node); + break; + } +} + +void node_modif(void *cbk, GF_Node *node) +{ + BIFSVID *b2v = cbk; + if (b2v->sr) gf_sc_invalidate(b2v->sr, node); +} + +Double get_scene_time(void *cbk) +{ + Double res; + BIFSVID *b2v = cbk; + res = (Double) (s64) b2v->cts; + res /= (Double) (s64) b2v->duration; + return res; +} + +void write_bmp(GF_VideoSurface *fb, char *rad_name, u32 img_num) +{ + char str[GF_MAX_PATH]; + BITMAPFILEHEADER fh; + BITMAPINFOHEADER fi; + FILE *fout; + u32 j, i; + char *ptr; + + if (img_num<10) { + sprintf(str, "%s_00%d.bmp", rad_name, img_num); + } else if (img_num<100) { + sprintf(str, "%s_0%d.bmp", rad_name, img_num); + } else { + sprintf(str, "%s_%d.bmp", rad_name, img_num); + } + + fout = fopen(str, "wb"); + if (!fout) return; + + memset(&fh, 0, sizeof(fh)); + fh.bfType = 19778; + fh.bfOffBits = 14 + 40; + + memset(&fi, 0, sizeof(char)*40); + fi.biSize = sizeof(char)*40; + fi.biWidth = fb->width; + fi.biHeight = fb->height; + fi.biPlanes = 1; + fi.biBitCount = 24; + fi.biCompression = BI_RGB; + fi.biSizeImage = fb->pitch * fb->height; + + /*NOT ALIGNED!!*/ + fwrite(&fh.bfType, 2, 1, fout); + fwrite(&fh.bfSize, 4, 1, fout); + fwrite(&fh.bfReserved1, 2, 1, fout); + fwrite(&fh.bfReserved2, 2, 1, fout); + fwrite(&fh.bfOffBits, 4, 1, fout); + + fwrite(&fi, 1, 40, fout); + + for (j=fb->height; j>0; j--) { + ptr = fb->video_buffer + (j-1)*fb->pitch; + //fwrite(ptr, 1, fb->width * 3, fout); + for (i=0;iwidth; i++) { + fputc(ptr[2], fout); + fputc(ptr[1], fout); + fputc(ptr[0], fout); + ptr+=3; + } + } + + fclose(fout); +} + + +void write_raw(GF_VideoSurface *fb, char *rad_name, u32 img_num) +{ + char str[GF_MAX_PATH]; + FILE *fout; + if (img_num<10) { + sprintf(str, "%s_00%d.raw", rad_name, img_num); + } else if (img_num<100) { + sprintf(str, "%s_0%d.raw", rad_name, img_num); + } else { + sprintf(str, "%s_%d.raw", rad_name, img_num); + } + + fout = fopen(str, "wb"); + if (!fout) return; + fwrite(fb->video_buffer , fb->height*fb->pitch, 1, fout); + fclose(fout); +} + +void dump_frame(BIFSVID b2v, char *conv_buf, char *out_path, u32 dump_type, avi_t *avi_out, u32 frameNum) +{ + u32 k; + GF_VideoSurface fb; + + /*lock it*/ + gf_sc_get_screen_buffer(b2v.sr, &fb); + /*export frame*/ + switch (dump_type) { + case 0: + /*reverse frame*/ + for (k=0; kdependsOnESID && (esd->decoderConfig->streamType == GF_STREAM_SCENE)) break; + gf_odf_desc_del((GF_Descriptor *) esd); + esd = NULL; + } + if (!esd) { + printf("no bifs track found\n"); + goto err_exit; + } + + es_id = (u16) gf_isom_get_track_id(file, track_number+1); + e = gf_bifs_decoder_configure_stream(b2v.bifs, es_id, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, esd->decoderConfig->objectTypeIndication); + if (e) { + printf("BIFS init error %s\n", gf_error_to_string(e)); + gf_odf_desc_del((GF_Descriptor *) esd); + esd = NULL; + goto err_exit; + } + + { + GF_ISOSample *samp = gf_isom_get_sample(file, track_number+1, 1, &di); + b2v.cts = samp->DTS + samp->CTS_Offset; + /*apply command*/ + gf_bifs_decode_au(b2v.bifs, es_id, samp->data, samp->dataLength, ((Double)(s64)b2v.cts)/1000.0); + gf_isom_sample_del(&samp); + } + + b2v.duration = gf_isom_get_media_duration(file, track_number+1); + + gf_odf_desc_del((GF_Descriptor *) esd); + + } + gf_sc_set_scene(b2v.sr, b2v.sg); + + if (!width || !height) { + gf_sg_get_scene_size_info(b2v.sg, &width, &height); + } + /*we work in RGB24, and we must make sure the pitch is %4*/ + if ((width*3)%4) { + printf("Adjusting width (%d) to have a stride multiple of 4\n", width); + while ((width*3)%4) width--; + } + gf_sc_set_size(b2v.sr, width, height); + gf_sc_get_screen_buffer(b2v.sr, &fb); + width = fb.width; + height = fb.height; + gf_sc_release_screen_buffer(b2v.sr, &fb); + + GF_SAFEALLOC(rendered_frames, nb_viewpoints*sizeof(char *)); + for (viewpoint_index = 1; viewpoint_index <= nb_viewpoints; viewpoint_index++) { + GF_SAFEALLOC(rendered_frames[viewpoint_index-1], fb.width*fb.height*3); + gf_sc_set_viewpoint(b2v.sr, viewpoint_index, NULL); + gf_sc_draw_frame(b2v.sr); + /*needed for background2D !!*/ + gf_sc_draw_frame(b2v.sr); + strcpy(out_path, ""); + if (out_dir) { + strcat(out_path, out_dir); + if (out_path[strlen(out_path)-1] != '\\') strcat(out_path, "\\"); + } + strcat(out_path, rad_name); + strcat(out_path, "_view"); + gf_sc_get_screen_buffer(b2v.sr, &fb); + write_bmp(&fb, out_path, viewpoint_index); + memcpy(rendered_frames[viewpoint_index-1], fb.video_buffer, fb.width*fb.height*3); + gf_sc_release_screen_buffer(b2v.sr, &fb); + } + + if (width != 800 || height != 480) { + printf("Wrong scene dimension, cannot produce output\n"); + goto err_exit; + } else { + u32 x, y; + GF_VideoSurface out_fb; + u32 bpp = 3; + out_fb.width = 800; + out_fb.height = 480; + out_fb.pitch = 800*bpp; + out_fb.pixel_format = GF_PIXEL_RGB_24; + out_fb.is_hardware_memory = 0; + GF_SAFEALLOC(out_fb.video_buffer, out_fb.pitch*out_fb.height) +#if 1 + for (y=0; ydependsOnESID && (esd->decoderConfig->streamType == GF_STREAM_SCENE)) break; + gf_odf_desc_del((GF_Descriptor *) esd); + esd = NULL; + } + if (!esd) { + printf("no bifs track found\n"); + goto err_exit; + } + + b2v.duration = gf_isom_get_media_duration(file, i+1); + timescale = gf_isom_get_media_timescale(file, i+1); + es_id = (u16) gf_isom_get_track_id(file, i+1); + e = gf_bifs_decoder_configure_stream(b2v.bifs, es_id, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, esd->decoderConfig->objectTypeIndication); + if (e) { + printf("BIFS init error %s\n", gf_error_to_string(e)); + gf_odf_desc_del((GF_Descriptor *) esd); + esd = NULL; + goto err_exit; + } + if (dump_time>=0) dump_time = dump_time *1000 / timescale; + + gf_sc_set_scene(b2v.sr, b2v.sg); + count = gf_isom_get_sample_count(file, i+1); + + reset_fps = 0; + if (!fps) { + fps = (Float) (count * timescale); + fps /= (Double) (s64) b2v.duration; + printf("Estimated BIFS FrameRate %g\n", fps); + reset_fps = 1; + } + + if (!width || !height) { + gf_sg_get_scene_size_info(b2v.sg, &width, &height); + } + /*we work in RGB24, and we must make sure the pitch is %4*/ + if ((width*3)%4) { + printf("Adjusting width (%d) to have a stride multiple of 4\n", width); + while ((width*3)%4) width--; + } + + gf_sc_set_size(b2v.sr, width, height); + gf_sc_draw_frame(b2v.sr); + + gf_sc_get_screen_buffer(b2v.sr, &fb); + width = fb.width; + height = fb.height; + if (avi_out) { + AVI_set_video(avi_out, width, height, fps, comp); + conv_buf = malloc(sizeof(char) * width * height * 3); + } + printf("Dumping at BIFS resolution %d x %d\n\n", width, height); + gf_sc_release_screen_buffer(b2v.sr, &fb); + + cur_time = 0; + + duration = (u32)(timescale / fps); + if (reset_fps) fps = 0; + + frameNum = 1; + first_dump = 1; + for (j=0; jDTS + samp->CTS_Offset; + /*apply command*/ + gf_bifs_decode_au(b2v.bifs, es_id, samp->data, samp->dataLength, ((Double)(s64)b2v.cts)/1000.0); + gf_isom_sample_del(&samp); + + if ((frameID>=0) && (j<(u32)frameID)) continue; + if ((dump_time>=0) && ((u32) dump_time>b2v.cts)) continue; + /*render frame*/ + gf_sc_draw_frame(b2v.sr); + /*needed for background2D !!*/ + if (first_dump) { + gf_sc_draw_frame(b2v.sr); + first_dump = 0; + } + + if (fps) { + if (cur_time > b2v.cts) continue; + + while (1) { + printf("dumped frame time %f (frame %d - sample %d)\r", ((Float)cur_time)/timescale, frameNum, j+1); + dump_frame(b2v, conv_buf, config_path, dump_type, avi_out, frameNum); + frameNum++; + cur_time += duration; + if (cur_time > b2v.cts) break; + } + } else { + dump_frame(b2v, conv_buf, config_path, dump_type, avi_out, (frameID>=0) ? frameID : frameNum); + if (frameID>=0 || dump_time>=0) break; + frameNum++; + printf("dumped frame %d / %d\r", j+1, count); + } + + } + gf_odf_desc_del((GF_Descriptor *) esd); + + /*destroy everything*/ + gf_bifs_decoder_del(b2v.bifs); + gf_sg_del(b2v.sg); + gf_sc_set_scene(b2v.sr, NULL); + gf_sc_del(b2v.sr); + +err_exit: + if (avi_out) AVI_close(avi_out); + if (conv_buf) free(conv_buf); + if (user.modules) gf_modules_del(user.modules); + if (needs_raw) gf_cfg_set_key(user.config, "Video", "DriverName", old_driv); + gf_cfg_del(user.config); +} + +int main (int argc, char **argv) +{ + Double fps_dump; + u32 i; + char rad[500]; + s32 frameID, h, m, s, f; + Float fps; + u32 dump_type; + s32 dump_time; + u32 dump_w, dump_h; + Bool copy; + char szConfigFile[4096]; + char *dump_out; + char *inName, *arg; + GF_ISOFile *file; + + if (argc < 2) { + PrintUsage(); + return 0; + } + + dump_type = 0; + fps_dump = 0.0f; + dump_w = dump_h = 0; + dump_out = NULL; + inName = NULL; + frameID = -1; + dump_time = -1; + szConfigFile[0] = 0; + + for (i = 1; i < (u32) argc ; i++) { + arg = argv[i]; + if (arg[0] != '-') { + inName = arg; + break; + } + if (!stricmp(arg, "-h")) { + PrintUsage(); + return 0; + } else if (!stricmp(arg, "-version")) { + PrintVersion(); + return 0; + } else if (!stricmp(arg, "-size")) { + sscanf(argv[i+1], "%dx%d", &dump_w, &dump_h); + i++; + } else if (!stricmp(arg, "-raw")) { + dump_type = 2; + if ((i+1<(u32)argc) && (argv[i+1][0]!='-')) { + if (strstr(argv[i+1], "T")) { + if (strstr(argv[i+1], "F")) { + sscanf(argv[i+1], "T%d:%d:%d:%dF%f", &h, &m, &s, &f, &fps); + dump_time = (s32) ((3600*h + 60*m + s)*1000 + 1000*f/fps); + } else { + sscanf(argv[i+1], "T%d:%d:%d", &h, &m, &s); + dump_time = (s32) ((3600*h + 60*m + s)*1000); + } + } else { + frameID = atoi(argv[i+1]); + } + i++; + } + } else if (!stricmp(arg, "-bmp")) { + dump_type = 1; + if ((i+1<(u32)argc) && (argv[i+1][0]!='-')) { + if (strstr(argv[i+1], "T")) { + if (strstr(argv[i+1], "F")) { + sscanf(argv[i+1], "T%d:%d:%d:%dF%f", &h, &m, &s, &f, &fps); + dump_time = (s32) ((3600*h + 60*m + s)*1000 + 1000*f/fps); + } else { + sscanf(argv[i+1], "T%d:%d:%d", &h, &m, &s); + dump_time = (s32) ((3600*h + 60*m + s)*1000); + } + } else { + frameID = atoi(argv[i+1]); + } + i++; + } + } else if (!stricmp(arg, "-3d")) { + dump_type = 3; + } else if (!stricmp(arg, "-outpath")) { + dump_out = argv[i+1]; + i++; + } else if (!stricmp(arg, "-fps")) { + fps_dump = atof(argv[i+1]); + i++; + } else if (!stricmp(arg, "-copy")) { + copy = 1; + } else if (!stricmp(arg, "-cfg")) { + strcpy(szConfigFile, argv[i+1]); + i += 1; + } else { + PrintUsage(); + return (0); + } + } + if (!inName) { + PrintUsage(); + return 0; + } + gf_sys_init(); + + file = gf_isom_open(inName, GF_ISOM_OPEN_READ, NULL); + if (!file) { + printf("Error opening file: %s\n", gf_error_to_string(gf_isom_last_error(NULL))); + return 0; + } + + if (dump_out) { + arg = strrchr(inName, GF_PATH_SEPARATOR); + if (arg) { + strcpy(rad, arg + 1); + } else { + strcpy(rad, inName); + } + } else { + strcpy(rad, inName); + } + while (rad[strlen(rad)-1] != '.') rad[strlen(rad)-1] = 0; + rad[strlen(rad)-1] = 0; + if (dump_type == 3) { + bifs3d_viewpoints_merger(file, szConfigFile, dump_w, dump_h, rad, dump_type, dump_out, fps_dump, frameID, dump_time); + } + else bifs_to_vid(file, szConfigFile, dump_w, dump_h, rad, dump_type, dump_out, fps_dump, frameID, dump_time); + printf("\ndone\n"); + gf_isom_delete(file); + return 0; + +} + diff --git a/applications/mp4box/Makefile b/applications/mp4box/Makefile new file mode 100644 index 0000000..d3c03fa --- /dev/null +++ b/applications/mp4box/Makefile @@ -0,0 +1,73 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/applications/mp4box + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#file format is read-only +ifeq ($(GPACREADONLY), yes) +CFLAGS+= -DGPAC_READ_ONLY +endif + +ifeq ($(DISABLE_SVG), yes) +CFLAGS+=-DGPAC_DISABLE_SVG +endif + +#common obj +OBJS= main.o filedump.o fileimport.o + +LINKFLAGS=-L../../bin/gcc +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=MP4Box$(EXE) +#LINKFLAGS+=-lgpac_static -lz $(EXTRALIBS) +LINKFLAGS+=-lgpac +else +EXT= +PROG=MP4Box +#LINKFLAGS+=-lgpac_static $(EXTRALIBS) $(GPAC_SH_FLAGS) -lz +LINKFLAGS+=-lgpac -lz $(OGL_LIBS) +endif + + +SRCS := $(OBJS:.o=.c) + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) $(LINKFLAGS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(PROG) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/applications/mp4box/filedump.c b/applications/mp4box/filedump.c new file mode 100644 index 0000000..ce852cb --- /dev/null +++ b/applications/mp4box/filedump.c @@ -0,0 +1,1726 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / mp4box application + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +/*for asctime and gmtime*/ +#include +/*ISO 639 languages*/ +#include +#include + + +extern u32 swf_flags; +extern Float swf_flatten_angle; +extern u32 get_file_type_by_ext(char *inName); + +void scene_coding_log(void *cbk, u32 log_level, u32 log_tool, const char *fmt, va_list vlist); + +#ifndef GPAC_READ_ONLY +GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double force_fps, u32 frames_per_sample); +#endif + +void PrintLanguages() +{ + u32 i=0; + fprintf(stdout, "Supported ISO 639 languages and codes:\n\n"); + while (GF_ISO639_Lang[i]) { + if (!GF_ISO639_Lang[i+2][0]) { + i+=3; + continue; + } + fprintf(stdout, "%s (%s - %s)\n", GF_ISO639_Lang[i], GF_ISO639_Lang[i+1], GF_ISO639_Lang[i+2]); + i+=3; + } +} + +static const char *GetLanguage(char *lcode) +{ + u32 i=0; + if ((lcode[0]=='u') && (lcode[1]=='n') && (lcode[2]=='d')) return "Undetermined"; + while (GF_ISO639_Lang[i]) { + if (GF_ISO639_Lang[i+2][0] && strstr(GF_ISO639_Lang[i+1], lcode)) return GF_ISO639_Lang[i]; + i+=3; + } + return "Unknown"; +} + +const char *GetLanguageCode(char *lang) +{ + u32 i; + Bool check_2cc = 0; + i = strlen(lang); + if (i==3) return lang; + if (i==2) check_2cc = 1; + + i=0; + while (GF_ISO639_Lang[i]) { + if (GF_ISO639_Lang[i+2][0]) { + if (check_2cc) { + if (!stricmp(GF_ISO639_Lang[i+2], lang) ) return GF_ISO639_Lang[i+1]; + } else if (!stricmp(GF_ISO639_Lang[i], lang)) return GF_ISO639_Lang[i+1]; + } + i+=3; + } + return "und"; +} + +#ifndef GPAC_READ_ONLY + +GF_Err dump_cover_art(GF_ISOFile *file, char *inName) +{ + const char *tag; + char szName[1024]; + FILE *t; + u32 tag_len; + GF_Err e = gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_COVER_ART, &tag, &tag_len); + if (e!=GF_OK) { + if (e==GF_URL_ERROR) { + fprintf(stdout, "No cover art found\n"); + return GF_OK; + } + return e; + } + + sprintf(szName, "%s.%s", inName, (tag_len>>31) ? "png" : "jpg"); + t = fopen(szName, "wb"); + fwrite(tag, tag_len & 0x7FFFFFFF, 1, t); + + fclose(t); + return GF_OK; +} + +GF_Err set_cover_art(GF_ISOFile *file, char *inName) +{ + GF_Err e; + char *tag, *ext; + FILE *t; + u32 tag_len; + t = fopen(inName, "rb"); + fseek(t, 0, SEEK_END); + tag_len = ftell(t); + fseek(t, 0, SEEK_SET); + tag = malloc(sizeof(char) * tag_len); + fread(tag, tag_len, 1, t); + fclose(t); + + ext = strrchr(inName, '.'); + if (!stricmp(ext, ".png")) tag_len |= 0x80000000; + e = gf_isom_apple_set_tag(file, GF_ISOM_ITUNE_COVER_ART, tag, tag_len); + free(tag); + return e; +} + +GF_Err dump_file_text(char *file, char *inName, u32 dump_mode, Bool do_log) +{ + GF_Err e; + GF_SceneManager *ctx; + GF_SceneGraph *sg; + GF_SceneLoader load; + u32 ftype; + u32 prev_level = gf_log_get_level(); + u32 prev_tools = gf_log_get_tools(); + gf_log_cbk prev_logs = NULL; + FILE *logs = NULL; + e = GF_OK; + + sg = gf_sg_new(); + ctx = gf_sm_new(sg); + memset(&load, 0, sizeof(GF_SceneLoader)); + load.fileName = file; + load.ctx = ctx; + load.swf_import_flags = swf_flags; + load.swf_flatten_limit = swf_flatten_angle; + + ftype = get_file_type_by_ext(file); + if (ftype == 1) { + load.isom = gf_isom_open(file, GF_ISOM_OPEN_READ, NULL); + if (!load.isom) { + e = gf_isom_last_error(NULL); + fprintf(stdout, "Error opening file: %s\n", gf_error_to_string(e)); + gf_sm_del(ctx); + gf_sg_del(sg); + return e; + } + } + /*SAF*/ + else if (ftype==6) { + load.isom = gf_isom_open("saf_conv", GF_ISOM_WRITE_EDIT, NULL); + if (load.isom) e = import_file(load.isom, file, 0, 0, 0); + else e = gf_isom_last_error(NULL); + + if (e) { + fprintf(stdout, "Error importing file: %s\n", gf_error_to_string(e)); + gf_sm_del(ctx); + gf_sg_del(sg); + if (load.isom) gf_isom_delete(load.isom); + return e; + } + } + + if (do_log) { + char szLog[GF_MAX_PATH]; + sprintf(szLog, "%s_dec.logs", inName); + logs = fopen(szLog, "wt"); + + gf_log_set_tools(GF_LOG_CODING); + gf_log_set_level(GF_LOG_DEBUG); + prev_logs = gf_log_set_callback(logs, scene_coding_log); + } + e = gf_sm_load_init(&load); + if (!e) e = gf_sm_load_run(&load); + gf_sm_load_done(&load); + if (logs) { + gf_log_set_tools(prev_tools); + gf_log_set_level(prev_level); + gf_log_set_callback(NULL, prev_logs); + fclose(logs); + } + if (!e) { + u32 count = gf_list_count(ctx->streams); + if (count) + fprintf(stdout, "Scene loaded - dumping %d systems streams\n", count); + else + fprintf(stdout, "Scene loaded - dumping root scene\n"); + + e = gf_sm_dump(ctx, inName, dump_mode); + } + + gf_sm_del(ctx); + gf_sg_del(sg); + if (e) fprintf(stdout, "Error loading scene: %s\n", gf_error_to_string(e)); + if (load.isom) gf_isom_delete(load.isom); + return e; +} + +static void dump_stats(FILE *dump, GF_SceneStatistics *stats) +{ + u32 i; + s32 created, count, draw_created, draw_count, deleted, draw_deleted; + created = count = draw_created = draw_count = deleted = draw_deleted = 0; + + fprintf(dump, "\n"); + fprintf(dump, "\n", gf_list_count(stats->node_stats)); + for (i=0; inode_stats); i++) { + GF_NodeStats *ptr = gf_list_get(stats->node_stats, i); + fprintf(dump, "\n", ptr->name); + + switch (ptr->tag) { + case TAG_MPEG4_Bitmap: + case TAG_MPEG4_Background2D: + case TAG_MPEG4_Background: + case TAG_MPEG4_Box: + case TAG_MPEG4_Circle: + case TAG_MPEG4_CompositeTexture2D: + case TAG_MPEG4_CompositeTexture3D: + case TAG_MPEG4_Cylinder: + case TAG_MPEG4_Cone: + case TAG_MPEG4_Curve2D: + case TAG_MPEG4_Extrusion: + case TAG_MPEG4_ElevationGrid: + case TAG_MPEG4_IndexedFaceSet2D: + case TAG_MPEG4_IndexedFaceSet: + case TAG_MPEG4_IndexedLineSet2D: + case TAG_MPEG4_IndexedLineSet: + case TAG_MPEG4_PointSet2D: + case TAG_MPEG4_PointSet: + case TAG_MPEG4_Rectangle: + case TAG_MPEG4_Sphere: + case TAG_MPEG4_Text: + case TAG_MPEG4_Ellipse: + case TAG_MPEG4_XCurve2D: + draw_count += ptr->nb_created + ptr->nb_used - ptr->nb_del; + draw_deleted += ptr->nb_del; + draw_created += ptr->nb_created; + break; + } + fprintf(dump, "\n", ptr->nb_created, ptr->nb_used, ptr->nb_del); + count += ptr->nb_created + ptr->nb_used; + deleted += ptr->nb_del; + created += ptr->nb_created; + fprintf(dump, "\n"); + } + if (i) { + fprintf(dump, "\n", count, created, deleted, stats->nb_svg_attributes); + fprintf(dump, "\n", draw_count, draw_created, draw_deleted); + } + fprintf(dump, "\n"); + + created = count = deleted = 0; + if (gf_list_count(stats->proto_stats)) { + fprintf(dump, "\n", gf_list_count(stats->proto_stats)); + for (i=0; iproto_stats); i++) { + GF_NodeStats *ptr = gf_list_get(stats->proto_stats, i); + fprintf(dump, "\n", ptr->name); + fprintf(dump, "\n", ptr->nb_created, ptr->nb_used, ptr->nb_del); + count += ptr->nb_created + ptr->nb_used; + deleted += ptr->nb_del; + created += ptr->nb_created; + fprintf(dump, "\n"); + } + if (i) fprintf(dump, "\n", count, created, deleted); + fprintf(dump, "\n"); + } + fprintf(dump, "\n", FIX2FLT( stats->min_fixed) , FIX2FLT( stats->max_fixed )); + fprintf(dump, "\n", stats->scale_int_res_2d, stats->scale_frac_res_2d, stats->int_res_2d, stats->frac_res_2d); + fprintf(dump, "\n"); + fprintf(dump, "\n"); + fprintf(dump, "\n", stats->count_2d, stats->rem_2d); + if (stats->count_2d) { + fprintf(dump, "\n", FIX2FLT( stats->min_2d.x) , FIX2FLT( stats->min_2d.y ), FIX2FLT( stats->max_2d.x ), FIX2FLT( stats->max_2d.y ) ); + } + fprintf(dump, "\n"); + + fprintf(dump, "\n"); + fprintf(dump, "", stats->count_3d, stats->rem_3d); + if (stats->count_3d) { + fprintf(dump, "\n", FIX2FLT( stats->min_3d.x ), FIX2FLT( stats->min_3d.y ), FIX2FLT( stats->min_3d.z ), FIX2FLT( stats->max_3d.x ), FIX2FLT( stats->max_3d.y ), FIX2FLT( stats->max_3d.z ) ); + } + fprintf(dump, "\n"); + + fprintf(dump, "\n"); + fprintf(dump, "", stats->count_color, stats->rem_color); + fprintf(dump, "\n"); + + fprintf(dump, "\n"); + fprintf(dump, "", stats->count_float, stats->rem_float); + fprintf(dump, "\n"); + + fprintf(dump, "\n"); + fprintf(dump, "", stats->count_2f); + fprintf(dump, "\n"); + fprintf(dump, "\n"); + fprintf(dump, "", stats->count_3f); + fprintf(dump, "\n"); +} + + +static void ReorderAU(GF_List *sample_list, GF_AUContext *au) +{ + u32 i; + for (i=0; itiming_sec > au->timing_sec) + /*set bifs first*/ + || ((ptr->timing_sec == au->timing_sec) && (ptr->owner->streamType < au->owner->streamType)) + ) { + gf_list_insert(sample_list, au, i); + return; + } + } + gf_list_add(sample_list, au); +} + +void dump_scene_stats(char *file, char *inName, u32 stat_level) +{ + GF_Err e; + FILE *dump; + Bool close; + u32 i, j, count; + char szBuf[1024]; + GF_SceneManager *ctx; + GF_SceneLoader load; + GF_StatManager *sm; + GF_List *sample_list; + GF_SceneGraph *scene_graph; + + dump = NULL; + sm = NULL; + sample_list = NULL; + + close = 0; + + scene_graph = gf_sg_new(); + ctx = gf_sm_new(scene_graph); + memset(&load, 0, sizeof(GF_SceneLoader)); + load.fileName = file; + load.ctx = ctx; + + if (get_file_type_by_ext(file) == 1) { + load.isom = gf_isom_open(file, GF_ISOM_OPEN_READ, NULL); + if (!load.isom) { + fprintf(stdout, "Cannot open file: %s\n", gf_error_to_string(gf_isom_last_error(NULL))); + gf_sm_del(ctx); + gf_sg_del(scene_graph); + return; + } + } + + e = gf_sm_load_init(&load); + if (!e) e = gf_sm_load_run(&load); + gf_sm_load_done(&load); + if (e) goto exit; + + if (inName) { + strcpy(szBuf, inName); + strcat(szBuf, "_stat.xml"); + dump = fopen(szBuf, "wt"); + close = 1; + } else { + dump = stdout; + close = 0; + } + + fprintf(stdout, "Analysing Scene\n"); + + fprintf(dump, "\n"); + fprintf(dump, "\n"); + fprintf(dump, "\n", file, (stat_level==1) ? "full scene" : ((stat_level==2) ? "AccessUnit based" : "SceneGraph after each AU")); + + sm = gf_sm_stats_new(); + + /*stat level 1: complete scene stat*/ + if (stat_level == 1) { + e = gf_sm_stats_for_scene(sm, ctx); + if (!e) dump_stats(dump, gf_sm_stats_get(sm) ); + goto exit; + } + /*re_order all BIFS-AUs*/ + sample_list = gf_list_new(); + /*configure all systems streams we're dumping*/ + for (i=0; istreams); i++) { + GF_StreamContext *sc = gf_list_get(ctx->streams, i); + if (sc->streamType != GF_STREAM_SCENE) continue; + for (j=0; jAUs); j++) { + GF_AUContext *au = gf_list_get(sc->AUs, j); + ReorderAU(sample_list, au); + } + } + + count = gf_list_count(sample_list); + for (i=0; icommands); j++) { + GF_Command *com = gf_list_get(au->commands, j); + /*stat level 2 - get command stats*/ + if (stat_level==2) { + e = gf_sm_stats_for_command(sm, com); + if (e) goto exit; + } + /*stat level 3 - apply command*/ + if (stat_level==3) gf_sg_command_apply(scene_graph, com, 0); + } + /*stat level 3: get graph stat*/ + if (stat_level==3) { + e = gf_sm_stats_for_graph(sm, scene_graph); + if (e) goto exit; + } + if (stat_level==2) { + fprintf(dump, "\n", au->owner->ESID, LLD_CAST au->timing); + } else { + fprintf(dump, "\n", au->owner->ESID, LLD_CAST au->timing); + } + /*dump stats*/ + dump_stats(dump, gf_sm_stats_get(sm) ); + /*reset stats*/ + gf_sm_stats_reset(sm); + if (stat_level==2) { + fprintf(dump, "\n"); + } else { + fprintf(dump, "\n"); + } + + gf_set_progress("Analysing AU", i+1, count); + } + + +exit: + if (sample_list) gf_list_del(sample_list); + if (sm) gf_sm_stats_del(sm); + gf_sm_del(ctx); + gf_sg_del(scene_graph); + if (e) { + fprintf(stdout, "%s\n", gf_error_to_string(e)); + } else { + fprintf(dump, "\n"); + } + if (dump && close) fclose(dump); + fprintf(stdout, "done\n"); +} +#endif + +void PrintFixed(Fixed val, Bool add_space) +{ + if (add_space) fprintf(stdout, " "); + if (val==FIX_MIN) fprintf(stdout, "-I"); + else if (val==FIX_MAX) fprintf(stdout, "+I"); + else fprintf(stdout, "%g", FIX2FLT(val)); +} + +void PrintNodeSFField(u32 type, void *far_ptr) +{ + if (!far_ptr) return; + switch (type) { + case GF_SG_VRML_SFBOOL: + fprintf(stdout, "%s", (*(SFBool *)far_ptr) ? "TRUE" : "FALSE"); + break; + case GF_SG_VRML_SFINT32: + fprintf(stdout, "%d", (*(SFInt32 *)far_ptr)); + break; + case GF_SG_VRML_SFFLOAT: + PrintFixed((*(SFFloat *)far_ptr), 0); + break; + case GF_SG_VRML_SFTIME: + fprintf(stdout, "%g", (*(SFTime *)far_ptr)); + break; + case GF_SG_VRML_SFVEC2F: + PrintFixed(((SFVec2f *)far_ptr)->x, 0); + PrintFixed(((SFVec2f *)far_ptr)->y, 1); + break; + case GF_SG_VRML_SFVEC3F: + PrintFixed(((SFVec3f *)far_ptr)->x, 0); + PrintFixed(((SFVec3f *)far_ptr)->y, 1); + PrintFixed(((SFVec3f *)far_ptr)->z, 1); + break; + case GF_SG_VRML_SFROTATION: + PrintFixed(((SFRotation *)far_ptr)->x, 0); + PrintFixed(((SFRotation *)far_ptr)->y, 1); + PrintFixed(((SFRotation *)far_ptr)->z, 1); + PrintFixed(((SFRotation *)far_ptr)->q, 1); + break; + case GF_SG_VRML_SFCOLOR: + PrintFixed(((SFColor *)far_ptr)->red, 0); + PrintFixed(((SFColor *)far_ptr)->green, 1); + PrintFixed(((SFColor *)far_ptr)->blue, 1); + break; + case GF_SG_VRML_SFSTRING: + if (((SFString*)far_ptr)->buffer) + fprintf(stdout, "\"%s\"", ((SFString*)far_ptr)->buffer); + else + fprintf(stdout, "NULL"); + break; + } +} + +static Bool node_in_table_by_tag(u32 tag, u32 NDTType) +{ + if (!tag) return 0; + if (tag==TAG_ProtoNode) return 1; + else if (tag<=GF_NODE_RANGE_LAST_MPEG4) { + u32 i; + + for (i=0;icount; i++) { + if (i) fprintf(stdout, " "); + gf_sg_vrml_mf_get_item(f.far_ptr, f.fieldType, &ptr, i); + PrintNodeSFField(sftype, ptr); + } + fprintf(stdout, "]"); + } + if (gf_bifs_get_aq_info(node, i, &qt, &at, &bmin, &bmax, &nbBits)) { + if (qt) { + fprintf(stdout, " #QP=%d", qt); + if (qt==13) fprintf(stdout, " NbBits=%d", nbBits); + if (bmin && bmax) { + fprintf(stdout, " Bounds=["); + PrintFixed(bmin, 0); + fprintf(stdout, ","); + PrintFixed(bmax, 0); + fprintf(stdout, "]"); + } + } + } + fprintf(stdout, "\n"); + } + fprintf(stdout, "}\n\n"); + + gf_node_unregister(node, NULL); + gf_sg_del(sg); +} + +void PrintBuiltInNodes(u32 graph_type) +{ + GF_Node *node; + GF_SceneGraph *sg; + u32 i, nb_in, nb_not_in, start_tag, end_tag; + + if (graph_type==1) { + start_tag = GF_NODE_RANGE_FIRST_X3D; + end_tag = TAG_LastImplementedX3D; + } else if (graph_type==2) { + start_tag = GF_NODE_RANGE_FIRST_SVG; + end_tag = GF_NODE_RANGE_LAST_SVG; + } else { + start_tag = GF_NODE_RANGE_FIRST_MPEG4; + end_tag = TAG_LastImplementedMPEG4; + } + nb_in = nb_not_in = 0; + sg = gf_sg_new(); + + if (graph_type==1) { + fprintf(stdout, "Available X3D nodes in this build (dumping):\n"); + } else if (graph_type==2) { + fprintf(stdout, "Available SVG nodes in this build (dumping and LASeR coding):\n"); + } else { + fprintf(stdout, "Available MPEG-4 nodes in this build (encoding/decoding/dumping):\n"); + } + for (i=start_tag; i\n"); + fprintf(dump, "\n"); + fprintf(dump, "\n"); + + for (i=0; i\n", gf_isom_get_track_id(file, i+1)); + for (j=0; j\n"); + } + fprintf(dump, "\n"); + if (inName) fclose(dump); +} + +void dump_file_ts(GF_ISOFile *file, char *inName) +{ + u32 i, j, k, count; + Bool has_error; + FILE *dump; + char szBuf[1024]; + + if (inName) { + strcpy(szBuf, inName); + strcat(szBuf, "_ts.txt"); + dump = fopen(szBuf, "wt"); + } else { + dump = stdout; + } + + has_error = 0; + for (i=0; iDTS; + cts = dts + (s32) samp->CTS_Offset; + gf_isom_sample_del(&samp); + + fprintf(dump, "Sample %d - DTS "LLD" - CTS "LLD"", j+1, LLD_CAST dts, LLD_CAST cts); + if (ctsDTS; + acts = adts + (s32) samp->CTS_Offset; + + if (adts==dts) { fprintf(dump, " #SAME DTS USED!!!"); has_error = 1; } + if (acts==cts) { fprintf(dump, " #SAME CTS USED!!! "); has_error = 1; } + + gf_isom_sample_del(&samp); + } + } + + fprintf(dump, "\n"); + gf_set_progress("Analysing Track Timing", j+1, count); + } + fprintf(dump, "\n\n"); + gf_set_progress("Analysing Track Timing", count, count); + } + if (inName) fclose(dump); + if (has_error) fprintf(stdout, "\tFile has CTTS table errors\n"); +} + +void dump_file_ismacryp(GF_ISOFile *file, char *inName) +{ + u32 i, j; + FILE *dump; + char szBuf[1024]; + + if (inName) { + strcpy(szBuf, inName); + strcat(szBuf, "_ismacryp.xml"); + dump = fopen(szBuf, "wt"); + } else { + dump = stdout; + } + + fprintf(dump, "\n"); + fprintf(dump, "\n"); + fprintf(dump, "\n"); + + + for (i=0; i\n", gf_isom_get_track_id(file, i+1)); + for (j=0; j\n"); + } + fprintf(dump, "\n"); + if (inName) fclose(dump); +} + + +void dump_timed_text_track(GF_ISOFile *file, u32 trackID, char *inName, Bool is_convert, u32 dump_type) +{ + FILE *dump; + GF_Err e; + u32 track; + char szBuf[1024]; + + track = gf_isom_get_track_by_id(file, trackID); + if (!track) { + fprintf(stdout, "Cannot find track ID %d\n", trackID); + return; + } + + if (gf_isom_get_media_type(file, track) != GF_ISOM_MEDIA_TEXT) { + fprintf(stdout, "Track ID %d is not a 3GPP text track\n", trackID); + return; + } + + if (inName) { + if (is_convert) + sprintf(szBuf, "%s.%s", inName, (dump_type==2) ? "svg" : ((dump_type==1) ? "srt" : "ttxt") ) ; + else + sprintf(szBuf, "%s_%d_text.%s", inName, trackID, (dump_type==2) ? "svg" : ((dump_type==1) ? "srt" : "ttxt") ); + dump = fopen(szBuf, "wt"); + } else { + dump = stdout; + } + e = gf_isom_text_dump(file, track, dump, dump_type); + if (inName) fclose(dump); + + if (e) fprintf(stdout, "Conversion failed (%s)\n", gf_error_to_string(e)); + else fprintf(stdout, "Conversion done\n"); +} + + +void DumpSDP(GF_ISOFile *file, char *inName) +{ + const char *sdp; + u32 size, i; + FILE *dump; + char szBuf[1024]; + + if (inName) { + char *ext; + strcpy(szBuf, inName); + ext = strchr(szBuf, '.'); + if (ext) ext[0] = 0; + strcat(szBuf, "_sdp.txt"); + dump = fopen(szBuf, "wt"); + } else { + dump = stdout; + fprintf(dump, "* File SDP content *\n\n"); + } + //get the movie SDP + gf_isom_sdp_get(file, &sdp, &size); + fprintf(dump, "%s", sdp); + fprintf(dump, "\r\n"); + + //then tracks + for (i=0; i365) { + y++; + d-=365; + if (y%4) d--; + } + sprintf(szDur, "%d Years %d Days, %02d:%02d:%02d.%03d", y, d, h, m, s, ms); + } + + } + return szDur; +} + +static char *format_date(u64 time, char *szTime) +{ + time_t now; + if (!time) { + strcpy(szTime, "UNKNOWN DATE"); + } else { + time -= 2082758400; + now = (u32) time; + sprintf(szTime, "GMT %s", asctime(gmtime(&now)) ); + } + return szTime; +} + +static void DumpMetaItem(GF_ISOFile *file, Bool root_meta, u32 tk_num, char *name) +{ + u32 i, count, brand, primary_id; + brand = gf_isom_get_meta_type(file, root_meta, tk_num); + if (!brand) return; + + count = gf_isom_get_meta_item_count(file, root_meta, tk_num); + primary_id = gf_isom_get_meta_primary_item_id(file, root_meta, tk_num); + fprintf(stdout, "%s type: \"%s\" - %d resource item(s)\n", name, gf_4cc_to_str(brand), (count+(primary_id>0))); + switch (gf_isom_has_meta_xml(file, root_meta, tk_num)) { + case 1: fprintf(stdout, "Meta has XML resource\n"); break; + case 2: fprintf(stdout, "Meta has BinaryXML resource\n"); break; + } + if (primary_id) { + fprintf(stdout, "Primary Item - ID %d\n", primary_id); + } + for (i=0; idecoderConfig->streamType); + if (st) { + fprintf(stdout, "MPEG-4 Config%s%s Stream - ObjectTypeIndication 0x%02x\n", + full_dump ? "\n\t" : ": ", st, esd->decoderConfig->objectTypeIndication); + } else { + fprintf(stdout, "MPEG-4 Config%sStream Type 0x%02x - ObjectTypeIndication 0x%02x\n", + full_dump ? "\n\t" : ": ", esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); + } + if (esd->decoderConfig->streamType==GF_STREAM_VISUAL) { + u32 w, h; + w = h = 0; + if (esd->decoderConfig->objectTypeIndication==0x20) { + GF_M4VDecSpecInfo dsi; + gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + if (full_dump) fprintf(stdout, "\t"); + w = dsi.width; + h = dsi.height; + if (w && h) { + fprintf(stdout, "MPEG-4 Visual Size %d x %d - %s\n", dsi.width, dsi.height, gf_m4v_get_profile_name(dsi.VideoPL)); + if (dsi.par_den && dsi.par_num) { + u32 tw, th; + gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); + fprintf(stdout, "Pixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", dsi.par_num, dsi.par_den, tw, th); + } + } + } else if (esd->decoderConfig->objectTypeIndication==0x21) { + GF_AVCConfig *avccfg; + GF_AVCConfigSlot *slc; + s32 par_n, par_d; + + gf_isom_get_visual_info(file, trackNum, 1, &w, &h); + if (full_dump) fprintf(stdout, "\t"); + fprintf(stdout, "AVC/H264 Video - Visual Size %d x %d - ", w, h); + avccfg = gf_isom_avc_config_get(file, trackNum, 1); + if (!avccfg) { + fprintf(stdout, "\n\n\tNon-compliant AVC track: SPS/PPS not found in sample description\n"); + } else { + fprintf(stdout, "Profile %s @ Level %g\n", gf_avc_get_profile_name(avccfg->AVCProfileIndication), ((Double)avccfg->AVCLevelIndication)/10.0 ); + fprintf(stdout, "NAL Unit length bits: %d\n", 8*avccfg->nal_unit_size); + +#ifndef GPAC_READ_ONLY + slc = gf_list_get(avccfg->sequenceParameterSets, 0); + gf_avc_get_sps_info(slc->data, slc->size, NULL, NULL, &par_n, &par_d); + if ((par_n>0) && (par_d>0)) { + u32 tw, th; + gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); + fprintf(stdout, "Pixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th); + } +#endif + gf_odf_avc_cfg_del(avccfg); + } + } + /*OGG media*/ + else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_MEDIA_OGG) { + char *szName; + gf_isom_get_visual_info(file, trackNum, 1, &w, &h); + if (full_dump) fprintf(stdout, "\t"); + if (!strnicmp(&esd->decoderConfig->decoderSpecificInfo->data[3], "theora", 6)) szName = "Theora"; + else szName = "Unknown"; + fprintf(stdout, "Ogg/%s video / GPAC Mux - Visual Size %d x %d\n", szName, w, h); + } + if (!w || !h) { + gf_isom_get_visual_info(file, trackNum, 1, &w, &h); + if (full_dump) fprintf(stdout, "\t"); + fprintf(stdout, "Visual Size %d x %d\n", w, h); + } + } else if (esd->decoderConfig->streamType==GF_STREAM_AUDIO) { + GF_M4ADecSpecInfo a_cfg; + GF_Err e; + u32 oti, is_mp2 = 0; + switch (esd->decoderConfig->objectTypeIndication) { + case 0x66: + case 0x67: + case 0x68: + is_mp2 = 1; + case 0x40: + e = gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg); + if (full_dump) fprintf(stdout, "\t"); + if (e) fprintf(stdout, "Corrupted AAC Config\n"); + else { + fprintf(stdout, "MPEG-%d Audio %s - %d Channel(s) - SampleRate %d", is_mp2 ? 2 : 4, gf_m4a_object_type_name(a_cfg.base_object_type), a_cfg.nb_chan, a_cfg.base_sr); + if (a_cfg.has_sbr) fprintf(stdout, " - SBR SampleRate %d", a_cfg.sbr_sr); + fprintf(stdout, "\n"); + } + break; + case 0x69: + case 0x6B: + if (msub_type == GF_ISOM_SUBTYPE_MPEG4_CRYP) { + fprintf(stdout, "MPEG-1/2 Audio - %d Channels - SampleRate %d\n", nb_ch, sr); + } else { + GF_ISOSample *samp = gf_isom_get_sample(file, trackNum, 1, &oti); + oti = GF_4CC((u8)samp->data[0], (u8)samp->data[1], (u8)samp->data[2], (u8)samp->data[3]); + if (full_dump) fprintf(stdout, "\t"); + fprintf(stdout, "%s Audio - %d Channel(s) - SampleRate %d - Layer %d\n", + gf_mp3_version_name(oti), + gf_mp3_num_channels(oti), + gf_mp3_sampling_rate(oti), + gf_mp3_layer(oti) + ); + gf_isom_sample_del(&samp); + } + break; + /*OGG media*/ + case GPAC_OTI_MEDIA_OGG: + { + char *szName; + if (full_dump) fprintf(stdout, "\t"); + if (!strnicmp(&esd->decoderConfig->decoderSpecificInfo->data[3], "vorbis", 6)) szName = "Vorbis"; + else if (!strnicmp(&esd->decoderConfig->decoderSpecificInfo->data[2], "Speex", 5)) szName = "Speex"; + else if (!strnicmp(&esd->decoderConfig->decoderSpecificInfo->data[2], "Flac", 4)) szName = "Flac"; + else szName = "Unknown"; + fprintf(stdout, "Ogg/%s audio / GPAC Mux - Sample Rate %d - %d channel(s)\n", szName, sr, nb_ch); + } + break; + case 0xA0: fprintf(stdout, "EVRC Audio - Sample Rate 8000 - 1 channel\n"); break; + case 0xA1: fprintf(stdout, "SMV Audio - Sample Rate 8000 - 1 channel\n"); break; + case 0xE1: fprintf(stdout, "QCELP Audio - Sample Rate 8000 - 1 channel\n"); break; + /*packetVideo hack for EVRC...*/ + case 0xD1: + if (esd->decoderConfig->decoderSpecificInfo && (esd->decoderConfig->decoderSpecificInfo->dataLength==8) + && !strnicmp(esd->decoderConfig->decoderSpecificInfo->data, "pvmm", 4)) { + if (full_dump) fprintf(stdout, "\t"); + fprintf(stdout, "EVRC Audio (PacketVideo Mux) - Sample Rate 8000 - 1 channel\n"); + } + break; + } + } + else if (esd->decoderConfig->streamType==GF_STREAM_SCENE) { + if (esd->decoderConfig->objectTypeIndication<=6) { + GF_BIFSConfig *b_cfg = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication); + fprintf(stdout, "BIFS Scene description - %s stream\n", b_cfg->elementaryMasks ? "Animation" : "Command"); + if (full_dump && !b_cfg->elementaryMasks) { + fprintf(stdout, "\tWidth %d Height %d Pixel Metrics %s\n", b_cfg->pixelWidth, b_cfg->pixelHeight, b_cfg->pixelMetrics ? "yes" : "no"); + } + gf_odf_desc_del((GF_Descriptor *)b_cfg); + } else if (esd->decoderConfig->objectTypeIndication==0x09) { + GF_LASERConfig l_cfg; + gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &l_cfg); + fprintf(stdout, "LASER Stream - %s\n", l_cfg.newSceneIndicator ? "Full Scene" : "Scene Segment"); + } + } + + /*sync is only valid if we open all tracks to take care of default MP4 sync..*/ + if (!full_dump) { + if (!esd->OCRESID || (esd->OCRESID == esd->ESID)) + fprintf(stdout, "Self-synchronized\n"); + else + fprintf(stdout, "Synchronized on stream %d\n", esd->OCRESID); + } else { + fprintf(stdout, "\tDecoding Buffer size %d - Average bitrate %d kbps - Max Bitrate %d kbps\n", esd->decoderConfig->bufferSizeDB, esd->decoderConfig->avgBitrate/1024, esd->decoderConfig->maxBitrate/1024); + if (esd->dependsOnESID) + fprintf(stdout, "\tDepends on stream %d for decoding\n", esd->dependsOnESID); + else + fprintf(stdout, "\tNo stream dependencies for decoding\n"); + + fprintf(stdout, "\tStreamPriority %d\n", esd->streamPriority); + if (esd->URLString) fprintf(stdout, "\tRemote Data Source %s\n", esd->URLString); + } + gf_odf_desc_del((GF_Descriptor *) esd); + + /*ISMACryp*/ + if (msub_type == GF_ISOM_SUBTYPE_MPEG4_CRYP) { + const char *scheme_URI, *KMS_URI; + u32 scheme_type, version; + u32 IV_size; + Bool use_sel_enc; + + if (gf_isom_is_ismacryp_media(file, trackNum, 1)) { + gf_isom_get_ismacryp_info(file, trackNum, 1, NULL, &scheme_type, &version, &scheme_URI, &KMS_URI, &use_sel_enc, &IV_size, NULL); + fprintf(stdout, "\n*Encrypted stream - ISMA scheme %s (version %d)\n", gf_4cc_to_str(scheme_type), version); + if (scheme_URI) fprintf(stdout, "scheme location: %s\n", scheme_URI); + if (KMS_URI) { + if (!strnicmp(KMS_URI, "(key)", 5)) fprintf(stdout, "KMS location: key in file\n"); + else fprintf(stdout, "KMS location: %s\n", KMS_URI); + } + fprintf(stdout, "Selective Encryption: %s\n", use_sel_enc ? "Yes" : "No"); + if (IV_size) fprintf(stdout, "Initialization Vector size: %d bits\n", IV_size*8); + } else if (gf_isom_is_omadrm_media(file, trackNum, 1)) { + const char *textHdrs; + u32 enc_type, hdr_len; + u64 orig_len; + fprintf(stdout, "\n*Encrypted stream - OMA DRM\n"); + gf_isom_get_omadrm_info(file, trackNum, 1, NULL, NULL, NULL, &scheme_URI, &KMS_URI, &textHdrs, &hdr_len, &orig_len, &enc_type, &use_sel_enc, &IV_size, NULL); + fprintf(stdout, "Rights Issuer: %s\n", KMS_URI); + fprintf(stdout, "Content ID: %s\n", scheme_URI); + if (textHdrs) { + u32 i, offset; + const char *start = textHdrs; + fprintf(stdout, "OMA Textual Headers:\n"); + i=offset=0; + while (i1) ? "s" :""); + for (i=0; icompressor_name, udesc->width, udesc->height); + } else if (mtype==GF_ISOM_MEDIA_AUDIO) { + fprintf(stdout, "Audio Track - Sample Rate %d - %d channel(s)\n", udesc->samplerate, udesc->nb_channels); + } else { + fprintf(stdout, "Unknown media type\n"); + } + fprintf(stdout, "\tVendor code \"%s\" - Version %d - revision %d\n", gf_4cc_to_str(udesc->vendor_code), udesc->version, udesc->revision); + if (udesc->extension_buf) { + fprintf(stdout, "\tCodec configuration data size: %d bytes\n", udesc->extension_buf_size); + free(udesc->extension_buf); + } + free(udesc); + } else { + fprintf(stdout, "Unknown track type\n"); + } + } + + DumpMetaItem(file, 0, trackNum, "Track Meta"); + + gf_isom_get_track_switch_group_count(file, trackNum, &alt_group, &nb_groups); + if (alt_group) { + fprintf(stdout, "Alternate Group ID %d\n", alt_group); + for (i=0; iDTS+samp->CTS_Offset; + size += samp->dataLength; + rate += samp->dataLength; + if (samp->DTS - time_slice>ts) { + if (max_rate < rate) max_rate = rate; + rate = 0; + time_slice = samp->DTS; + } + gf_isom_sample_del(&samp); + } + fprintf(stdout, "\nComputed info from media:\n"); + scale = 1000; + scale /= ts; + dur = (u64) (scale * (s64)dur); + fprintf(stdout, "\tTotal size %d bytes - Total samples duration %d ms\n", size, (u32) dur); + if (!dur) { + fprintf(stdout, "\n"); + return; + } + rate = (u32) (size * 8 / (dur/1000)); + max_rate *= 8; + if (rate >= 1500) { + rate /= 1024; + max_rate /= 1024; + fprintf(stdout, "\tAverage rate %d kbps - Max Rate %d kbps\n", rate, max_rate); + } else { + fprintf(stdout, "\tAverage rate %d bps - Max Rate %d bps\n", rate, max_rate); + } + fprintf(stdout, "\n"); + + count = gf_isom_get_chapter_count(file, trackNum); + if (count) { + char szDur[20]; + const char *name; + u64 time; + fprintf(stdout, "\nChapters:\n"); + for (j=0; j0) && (tag <= (sizeof(ID3v1Genres)/sizeof(const char *)) )) { + return ID3v1Genres[tag-1]; + } + return "Unknown"; +} +u32 id3_get_genre_tag(const char *name) +{ + u32 i, count = sizeof(ID3v1Genres)/sizeof(const char *); + for (i=0; itag == GF_ODF_IOD_TAG) { + fprintf(stdout, "File has root IOD\n"); + fprintf(stdout, "Scene PL 0x%02x - Graphics PL 0x%02x - OD PL 0x%02x\n", iod->scene_profileAndLevel, iod->graphics_profileAndLevel, iod->OD_profileAndLevel); + fprintf(stdout, "Visual PL: %s (0x%02x)\n", gf_m4v_get_profile_name(iod->visual_profileAndLevel), iod->visual_profileAndLevel); + fprintf(stdout, "Audio PL: %s (0x%02x)\n", gf_m4a_get_profile_name(iod->audio_profileAndLevel), iod->audio_profileAndLevel); + //fprintf(stdout, "inline profiles included %s\n", iod->inlineProfileFlag ? "yes" : "no"); + } else { + fprintf(stdout, "File has root OD\n"); + } + if (!gf_list_count(iod->ESDescriptors)) fprintf(stdout, "No streams included in root OD\n"); + gf_odf_desc_del((GF_Descriptor *) iod); + } else { + fprintf(stdout, "File has no MPEG4 IOD/OD\n"); + } + if (gf_isom_is_JPEG2000(file)) fprintf(stdout, "File is JPEG 2000\n"); + + count = gf_isom_get_copyright_count(file); + if (count) { + const char *lang, *note; + fprintf(stdout, "\nCopyrights:\n"); + for (i=0; i>31) fprintf(stdout, "\tCover Art: PNG File\n"); + else fprintf(stdout, "\tCover Art: JPEG File\n"); + } + } + + fprintf(stdout, "\n"); + for (i=0; iuser; + + switch (evt_type) { + case GF_M2TS_EVT_PAT_FOUND: + fprintf(stdout, "Initial PAT found - %d programs\n", gf_list_count(ts->programs) ); + break; + case GF_M2TS_EVT_PAT_UPDATE: + fprintf(stdout, "PAT updated - %d programs\n", gf_list_count(ts->programs) ); + break; + case GF_M2TS_EVT_PAT_REPEAT: + dumper->has_seen_pat = 1; +// fprintf(stdout, "Repeated PAT found - %d programs\n", gf_list_count(ts->programs) ); + break; + case GF_M2TS_EVT_PMT_FOUND: + prog = (GF_M2TS_Program*)par; + count = gf_list_count(prog->streams); + fprintf(stdout, "Program number %d found - %d streams:\n", prog->number, count); + for (i=0; istreams, i); + if (es->pid == prog->pmt_pid) fprintf(stdout, "\tPID %d: Program Map Table\n", es->pid); + else { + GF_M2TS_PES *pes = (GF_M2TS_PES *)es; + fprintf(stdout, "\tPID %d: %s ", pes->pid, gf_m2ts_get_stream_name(pes->stream_type) ); + if (pes->mpeg4_es_id) fprintf(stdout, " - MPEG-4 ES ID %d", pes->mpeg4_es_id); + fprintf(stdout, "\n"); + } + } + break; + case GF_M2TS_EVT_PMT_UPDATE: + fprintf(stdout, "Program list updated - %d streams\n", gf_list_count( ((GF_M2TS_Program*)par)->streams) ); + break; + case GF_M2TS_EVT_PMT_REPEAT: +// fprintf(stdout, "Repeated Program list found - %d streams\n", gf_list_count( ((GF_M2TS_Program*)par)->streams) ); + break; + case GF_M2TS_EVT_SDT_FOUND: + count = gf_list_count(ts->SDTs) ; + fprintf(stdout, "Program Description found - %d desc:\n", count); + for (i=0; iSDTs, i); + fprintf(stdout, "\tServiceID %d - Provider %s - Name %s\n", sdt->service_id, sdt->provider, sdt->service); + } + break; + case GF_M2TS_EVT_SDT_UPDATE: + count = gf_list_count(ts->SDTs) ; + fprintf(stdout, "Program Description updated - %d desc\n", count); + for (i=0; iSDTs, i); + fprintf(stdout, "\tServiceID %d - Provider %s - Name %s\n", sdt->service_id, sdt->provider, sdt->service); + } + break; + case GF_M2TS_EVT_SDT_REPEAT: +// fprintf(stdout, "Repeated Program Description - %d desc\n", gf_list_count(ts->SDTs) ); + break; + case GF_M2TS_EVT_PES_PCK: + pck = par; + //fprintf(stdout, "PES(%d): DTS "LLD" PTS" LLD" RAP %d size %d\n", pck->stream->pid, pck->DTS, pck->PTS, pck->rap, pck->data_len); + if (dumper->pes_out && (dumper->dump_pid == pck->stream->pid)) { + fwrite(pck->data, pck->data_len, 1, dumper->pes_out); + } + break; + case GF_M2TS_EVT_SL_PCK: +#if 0 + { + GF_M2TS_SL_PCK *sl_pck = par; + if (dumper->pes_out && (dumper->dump_pid == sl_pck->stream->pid)) { + GF_SLHeader header; + u32 header_len; + if (sl_pck->stream->mpeg4_es_id) { + GF_ESD *esd = ((GF_M2TS_PES*)sl_pck->stream)->esd; + if (!dumper->is_info_dumped) { + if (esd->decoderConfig->decoderSpecificInfo) fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, 1, dumper->pes_out_info); + dumper->is_info_dumped = 1; + fprintf(dumper->pes_out_nhml, "pes_out_nhml, "timeScale=\"%d\" ", esd->slConfig->timestampResolution); + fprintf(dumper->pes_out_nhml, "streamType=\"%d\" ", esd->decoderConfig->streamType); + fprintf(dumper->pes_out_nhml, "objectTypeIndication=\"%d\" ", esd->decoderConfig->objectTypeIndication); + if (esd->decoderConfig->decoderSpecificInfo) fprintf(dumper->pes_out_nhml, "specificInfoFile=\"%s\" ", dumper->info); + fprintf(dumper->pes_out_nhml, "baseMediaFile=\"%s\" ", dumper->dump); + fprintf(dumper->pes_out_nhml, "inRootOD=\"yes\">\n"); + } + gf_sl_depacketize(esd->slConfig, &header, sl_pck->data, sl_pck->data_len, &header_len); + fwrite(sl_pck->data+header_len, sl_pck->data_len-header_len, 1, dumper->pes_out); + fprintf(dumper->pes_out_nhml, "\n", LLD_CAST header.decodingTimeStamp, sl_pck->data_len-header_len, (header.randomAccessPointFlag?"yes":"no")); + } + } + } +#endif + break; + } +} + +void dump_mpeg2_ts(char *mpeg2ts_file, char *pes_out_name) +{ + char data[188]; + GF_M2TS_Dump dumper; + u32 size, fsize, fdone; + GF_M2TS_Demuxer *ts; + + FILE *src = fopen(mpeg2ts_file, "rb"); + + ts = gf_m2ts_demux_new(); + ts->on_event = on_m2ts_dump_event; + memset(&dumper, 0, sizeof(GF_M2TS_Dump)); + ts->user = &dumper; + + fseek(src, 0, SEEK_END); + fsize = ftell(src); + fseek(src, 0, SEEK_SET); + fdone = 0; + + if (pes_out_name) { + char *pid = strrchr(pes_out_name, '#'); + if (pid) { + dumper.dump_pid = atoi(pid+1); + pid[0] = 0; + sprintf(dumper.dump, "%s_%d.media", pes_out_name, dumper.dump_pid); + dumper.pes_out = fopen(dumper.dump, "wb"); + sprintf(dumper.nhml, "%s_%d.nhml", pes_out_name, dumper.dump_pid); + dumper.pes_out_nhml = fopen(dumper.nhml, "wt"); + sprintf(dumper.info, "%s_%d.info", pes_out_name, dumper.dump_pid); + dumper.pes_out_info = fopen(dumper.info, "wb"); + pid[0] = '#'; + } + } + + while (!feof(src)) { + size = fread(data, 1, 188, src); + if (size<188) break; + + gf_m2ts_process_data(ts, data, size); + if (dumper.has_seen_pat) break; + } + + gf_m2ts_reset_parsers(ts); + gf_f64_seek(src, 0, SEEK_SET); + fdone = 0; + while (!feof(src)) { + size = fread(data, 1, 188, src); + if (size<188) break; + + gf_m2ts_process_data(ts, data, size); + + fdone += size; + gf_set_progress("MPEG-2 TS Parsing", fdone, fsize); + } + + fclose(src); + gf_m2ts_demux_del(ts); + if (dumper.pes_out) fclose(dumper.pes_out); + if (dumper.pes_out_nhml) { + if (dumper.is_info_dumped) fprintf(dumper.pes_out_nhml, "\n"); + fclose(dumper.pes_out_nhml); + fclose(dumper.pes_out_info); + } +} diff --git a/applications/mp4box/fileimport.c b/applications/mp4box/fileimport.c new file mode 100644 index 0000000..86bc31a --- /dev/null +++ b/applications/mp4box/fileimport.c @@ -0,0 +1,1773 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / mp4box application + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include +#include +#include + + +#ifndef GPAC_READ_ONLY + +extern u32 swf_flags; +extern Float swf_flatten_angle; + +const char *GetLanguageCode(char *lang); +void scene_coding_log(void *cbk, u32 log_level, u32 log_tool, const char *fmt, va_list vlist); + +void convert_file_info(char *inName, u32 trackID) +{ + GF_Err e; + u32 i; + Bool found; + GF_MediaImporter import; + memset(&import, 0, sizeof(GF_MediaImporter)); + import.trackID = trackID; + import.in_name = inName; + import.flags = GF_IMPORT_PROBE_ONLY; + e = gf_media_import(&import); + if (e) { + fprintf(stdout, "Error probing file %s: %s\n", inName, gf_error_to_string(e)); + return; + } + if (trackID) { + fprintf(stdout, "Import probing results for track %s#%d:\n", inName, trackID); + } else { + fprintf(stdout, "Import probing results for %s:\n", inName); + if (!import.nb_tracks) { + fprintf(stdout, "File has no selectable tracks\n"); + return; + } + fprintf(stdout, "File has %d tracks\n", import.nb_tracks); + } + found = 0; + for (i=0; i> 16, import.tk_info[i].video_info.par & 0xFFFF); + fprintf(stdout, "\n"); + } + else if ((import.tk_info[i].type==GF_ISOM_MEDIA_AUDIO) && import.tk_info[i].audio_info.sample_rate) { + fprintf(stdout, "Source: %s - SampleRate %d - %d channels\n", gf_4cc_to_str(import.tk_info[i].media_type), import.tk_info[i].audio_info.sample_rate, import.tk_info[i].audio_info.nb_channels); + } else { + fprintf(stdout, "Source: %s\n", gf_4cc_to_str(import.tk_info[i].media_type)); + } + + + fprintf(stdout, "\nImport Capabilities:\n"); + if (import.tk_info[i].flags & GF_IMPORT_USE_DATAREF) fprintf(stdout, "\tCan use data referencing\n"); + if (import.tk_info[i].flags & GF_IMPORT_NO_FRAME_DROP) fprintf(stdout, "\tCan use fixed FPS import\n"); + if (import.tk_info[i].flags & GF_IMPORT_FORCE_PACKED) fprintf(stdout, "\tCan force packed bitstream import\n"); + if (import.tk_info[i].flags & GF_IMPORT_OVERRIDE_FPS) fprintf(stdout, "\tCan override source frame rate\n"); + if (import.tk_info[i].flags & (GF_IMPORT_SBR_IMPLICIT|GF_IMPORT_SBR_EXPLICIT)) fprintf(stdout, "\tCan use AAC-SBR signaling\n"); + if (import.tk_info[i].flags & GF_IMPORT_FORCE_MPEG4) fprintf(stdout, "\tCan force MPEG-4 Systems signaling\n"); + if (import.tk_info[i].flags & GF_IMPORT_3GPP_AGGREGATION) fprintf(stdout, "\tCan use 3GPP frame aggregation\n"); + if (import.tk_info[i].flags & GF_IMPORT_NO_DURATION) fprintf(stdout, "\tCannot use duration-based import\n"); + + found = 1; + break; + } + fprintf(stdout, "\n"); + if (!found && trackID) fprintf(stdout, "Cannot find track %d in file\n", trackID); +} + +GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double force_fps, u32 frames_per_sample) +{ + u32 track_id, i, timescale, track; + s32 par_d, par_n, prog_id, delay; + Bool do_audio, do_video, do_all, disable; + u32 group; + const char *szLan; + GF_Err e; + GF_MediaImporter import; + char *ext, szName[1000], *handler_name; + + memset(&import, 0, sizeof(GF_MediaImporter)); + + strcpy(szName, inName); + ext = strrchr(inName, '.'); + if (!ext) { + fprintf(stdout, "Unknown input file type\n"); + return GF_BAD_PARAM; + } + + disable = 0; + szLan = NULL; + delay = 0; + group = 0; + par_d = par_n = -2; + /*use ':' as separator, but beware DOS paths...*/ + ext = strchr(szName, ':'); + if (ext && ext[1]=='\\') ext = strchr(szName+2, ':'); + + handler_name = NULL; + while (ext) { + char *ext2 = strchr(ext+1, ':'); + if (ext2 && !strncmp(ext2, "://", 3)) ext2 = strchr(ext2+1, ':'); + if (ext2 && !strncmp(ext2, ":\\", 2)) ext2 = strchr(ext2+1, ':'); + if (ext2) ext2[0] = 0; + + /*all extensions for track-based importing*/ + if (!strnicmp(ext+1, "lang=", 5)) szLan = GetLanguageCode(ext+6); + else if (!strnicmp(ext+1, "delay=", 6)) delay = atoi(ext+7); + else if (!strnicmp(ext+1, "fps=", 4)) { + if (!strcmp(ext+5, "auto")) force_fps = 10000.0; + else force_fps = atof(ext+5); + } + else if (!stricmp(ext+1, "dref")) import_flags |= GF_IMPORT_USE_DATAREF; + else if (!stricmp(ext+1, "nodrop")) import_flags |= GF_IMPORT_NO_FRAME_DROP; + else if (!stricmp(ext+1, "packed")) import_flags |= GF_IMPORT_FORCE_PACKED; + else if (!stricmp(ext+1, "sbr")) import_flags |= GF_IMPORT_SBR_IMPLICIT; + else if (!stricmp(ext+1, "sbrx")) import_flags |= GF_IMPORT_SBR_EXPLICIT; + else if (!stricmp(ext+1, "mpeg4")) import_flags |= GF_IMPORT_FORCE_MPEG4; + else if (!strnicmp(ext+1, "agg=", 4)) frames_per_sample = atoi(ext+5); + else if (!strnicmp(ext+1, "dur=", 4)) import.duration = (u32) (atof(ext+5) * 1000); + else if (!strnicmp(ext+1, "par=", 4)) { + if (!stricmp(ext+5, "none")) { + par_n = par_d = -1; + } else { + if (ext2) ext2[0] = ':'; + if (ext2) ext2 = strchr(ext2+1, ':'); + if (ext2) ext2[0] = 0; + sscanf(ext+5, "%d:%d", &par_n, &par_d); + } + } + else if (!strnicmp(ext+1, "name=", 5)) handler_name = strdup(ext+6); + else if (!strnicmp(ext+1, "font=", 5)) import.fontName = strdup(ext+6); + else if (!strnicmp(ext+1, "size=", 5)) import.fontSize = atoi(ext+6); + else if (!strnicmp(ext+1, "fmt=", 4)) import.streamFormat = strdup(ext+5); + else if (!strnicmp(ext+1, "disable", 7)) disable = 1; + else if (!strnicmp(ext+1, "group=", 6)) { + group = atoi(ext+7); + if (!group) group = gf_isom_get_next_alternate_group_id(dest); + } + + + /*unrecognized, assume name has colon in it*/ + else { + ext = ext2; + continue; + } + + if (ext2) ext2[0] = ':'; + ext2 = ext+1; + ext[0] = 0; + ext = strchr(ext+1, ':'); + } + + /*check duration import (old syntax)*/ + ext = strrchr(szName, '%'); + if (ext) { + import.duration = (u32) (atof(ext+1) * 1000); + ext[0] = 0; + } + + /*select switches for av containers import*/ + do_audio = do_video = 0; + track_id = prog_id = 0; + do_all = 1; + ext = strrchr(szName, '#'); + if (ext) ext[0] = 0; + + import.in_name = szName; + import.flags = GF_IMPORT_PROBE_ONLY; + e = gf_media_import(&import); + if (e) goto exit; + + if (ext) { + ext++; + if (!strnicmp(ext, "audio", 5)) do_audio = 1; + else if (!strnicmp(ext, "video", 5)) do_video = 1; + else if (!strnicmp(ext, "trackID=", 8)) track_id = atoi(&ext[8]); + else if (!strnicmp(ext, "PID=", 4)) track_id = atoi(&ext[4]); + else if (!strnicmp(ext, "program=", 8)) { + for (i=0; i0) { + gf_isom_append_edit_segment(import.dest, i+1, (timescale*delay)/1000, 0, GF_ISOM_EDIT_EMPTY); + gf_isom_append_edit_segment(import.dest, i+1, tk_dur, 0, GF_ISOM_EDIT_NORMAL); + } else { + u64 to_skip = (timescale*(-delay))/1000; + if (to_skip=0) && (par_d>=0)) { + e = gf_media_change_par(import.dest, i+1, par_n, par_d); + } + if (handler_name) gf_isom_set_handler_name(import.dest, i+1, handler_name); + } + } else { + for (i=0; i0) { + gf_isom_append_edit_segment(import.dest, track, (timescale*delay)/1000, 0, GF_ISOM_EDIT_EMPTY); + gf_isom_append_edit_segment(import.dest, track, tk_dur, 0, GF_ISOM_EDIT_NORMAL); + } else { + u64 to_skip = (timescale*(-delay))/1000; + if (to_skip=-1) && (par_d>=-1)) { + e = gf_media_change_par(import.dest, track, par_n, par_d); + } + if (handler_name) gf_isom_set_handler_name(import.dest, track, handler_name); + + if (group) { + gf_isom_set_alternate_group_id(import.dest, track, group); + } + } + if (track_id) fprintf(stdout, "WARNING: Track ID %d not found in file\n", track_id); + else if (do_video) fprintf(stdout, "WARNING: Video track not found\n"); + else if (do_audio) fprintf(stdout, "WARNING: Audio track not found\n"); + } + +exit: + if (handler_name) free(handler_name); + if (import.fontName) free(import.fontName); + if (import.streamFormat) free(import.streamFormat); + return e; +} + +typedef struct +{ + u32 tk; + Bool has_non_raps; + u32 last_sample; + u32 sample_count; + u32 time_scale; + u64 firstDTS, lastDTS; + u32 dst_tk; + /*set if media can be duplicated at split boundaries - only used for text tracks and provate tracks, this assumes all + samples are RAP*/ + Bool can_duplicate; + /*controls import by time rather than by sample (otherwise we would have to remove much more samples video vs audio for example*/ + Bool first_sample_done; + u32 stop_state; +} TKInfo; + +GF_Err split_isomedia_file(GF_ISOFile *mp4, Double split_dur, u32 split_size_kb, char *inName, Double InterleavingTime, Double chunk_start_time, const char *tmpdir, char *outfile) +{ + u32 i, count, nb_tk, needs_rap_sync, cur_file, conv_type, nb_tk_done, nb_samp, nb_done, di; + Double max_dur, cur_file_time; + Bool do_add, all_duplicatable, size_exceeded, chunk_extraction; + GF_ISOFile *dest; + GF_ISOSample *samp; + GF_Err e; + TKInfo *tks, *tki; + char *ext, szName[1000], szFile[1000]; + Double chunk_start = (Double) chunk_start_time; + + chunk_extraction = (chunk_start>=0) ? 1 : 0; + + + strcpy(szName, inName); + ext = strrchr(szName, '.'); + if (ext) ext[0] = 0; + ext = strrchr(inName, '.'); + + dest = NULL; + + conv_type = 0; + switch (gf_isom_guess_specification(mp4)) { + case GF_4CC('I','S','M','A'): conv_type = 1; break; + case GF_ISOM_BRAND_3GP4: + case GF_ISOM_BRAND_3GP5: + case GF_ISOM_BRAND_3GP6: + case GF_ISOM_BRAND_3GG6: + case GF_ISOM_BRAND_3G2A: + conv_type = 2; + break; + } + if (!stricmp(ext, ".3gp") || !stricmp(ext, ".3g2")) conv_type = 2; + + count = gf_isom_get_track_count(mp4); + tks = (TKInfo *)malloc(sizeof(TKInfo)*count); + memset(tks, 0, sizeof(TKInfo)*count); + + e = GF_OK; + max_dur = 0; + nb_tk = 0; + all_duplicatable = 1; + needs_rap_sync = 0; + nb_samp = 0; + for (i=0; i1) { + break; + } + continue; + case GF_ISOM_MEDIA_HINT: + case GF_ISOM_MEDIA_SCENE: + case GF_ISOM_MEDIA_OCR: + case GF_ISOM_MEDIA_OD: + case GF_ISOM_MEDIA_OCI: + case GF_ISOM_MEDIA_IPMP: + case GF_ISOM_MEDIA_MPEGJ: + case GF_ISOM_MEDIA_MPEG7: + case GF_ISOM_MEDIA_FLASH: + fprintf(stdout, "WARNING: Track ID %d (type %s) not handled by spliter - skipping\n", gf_isom_get_track_id(mp4, i+1), gf_4cc_to_str(mtype)); + continue; + default: + /*for all other track types, only split if more than one sample*/ + if (gf_isom_get_sample_count(mp4, i+1)==1) { + fprintf(stdout, "WARNING: Track ID %d (type %s) not handled by spliter - skipping\n", gf_isom_get_track_id(mp4, i+1), gf_4cc_to_str(mtype)); + continue; + } + tks[nb_tk].can_duplicate = 1; + } + + tks[nb_tk].sample_count = gf_isom_get_sample_count(mp4, i+1); + nb_samp += tks[nb_tk].sample_count; + tks[nb_tk].last_sample = 0; + tks[nb_tk].firstDTS = 0; + tks[nb_tk].time_scale = gf_isom_get_media_timescale(mp4, i+1); + tks[nb_tk].has_non_raps = gf_isom_has_sync_points(mp4, i+1); + /*seen that on some 3gp files from nokia ...*/ + if (mtype==GF_ISOM_MEDIA_AUDIO) tks[nb_tk].has_non_raps = 0; + + dur = (Double) (s64) gf_isom_get_media_duration(mp4, i+1); + dur /= tks[nb_tk].time_scale; + if (max_dur=max_dur) { + fprintf(stdout, "Input file (%f) shorter than requested split start offset (%f)\n", max_dur, chunk_start); + free(tks); + return GF_NOT_SUPPORTED; + } + if (max_dur<=split_dur) { + fprintf(stdout, "Input file (%f) shorter than requested split duration (%f)\n", max_dur, split_dur); + free(tks); + return GF_NOT_SUPPORTED; + } + if (needs_rap_sync) { + tki = &tks[needs_rap_sync-1]; + if ((gf_isom_get_sync_point_count(mp4, tki->tk)==1) && (chunk_start != 0.0f)) { + fprintf(stdout, "Not enough Random Access points in input file - cannot split\n"); + free(tks); + return GF_NOT_SUPPORTED; + } + } + split_size_kb *= 1024; + cur_file_time = 0; + + if (chunk_start>0) { + if (needs_rap_sync) { + u32 sample_num; + Double start; + tki = &tks[needs_rap_sync-1]; + + start = (Double) (s64) gf_isom_get_sample_dts(mp4, tki->tk, tki->sample_count); + start /= tki->time_scale; + if (startstop_state = 2; + needs_rap_sync = 0; + } else { + e = gf_isom_get_sample_for_media_time(mp4, tki->tk, (u64) (chunk_start*tki->time_scale), &di, GF_ISOM_SEARCH_SYNC_BACKWARD, &samp, &sample_num); + if (e!=GF_OK) { + fprintf(stdout, "Cannot locate RAP in track ID %d for chunk extraction from %02.2f sec\n", gf_isom_get_track_id(mp4, tki->tk), chunk_start); + free(tks); + return GF_NOT_SUPPORTED; + } + start = (Double) (s64) samp->DTS; + start /= tki->time_scale; + gf_isom_sample_del(&samp); + fprintf(stdout, "Adjusting chunk start time to previous random access at %02.2f sec\n", start); + split_dur += (chunk_start - start); + chunk_start = start; + } + } + /*sync all tracks*/ + for (i=0; ilast_samplesample_count) { + Double time; + u64 dts; + dts = gf_isom_get_sample_dts(mp4, tki->tk, tki->last_sample+1); + time = (Double) (s64) dts; + time /= tki->time_scale; + if (time>=chunk_start) { + /*rewind one sample (text tracks & co)*/ + if (tki->can_duplicate && tki->last_sample) { + tki->last_sample--; + tki->firstDTS = (u64) (chunk_start*tki->time_scale); + } else { + tki->firstDTS = dts; + } + break; + } + tki->last_sample++; + } + } + cur_file_time = chunk_start; + } else { + chunk_start = 0; + } + + dest = NULL; + nb_done = 0; + nb_tk_done = 0; + cur_file = 0; + while (nb_tk_donestop_state==2) continue; + + e = gf_isom_clone_track(mp4, tki->tk, dest, 0, &tki->dst_tk); + if (e) { + fprintf(stdout, "Error cloning track %d\n", tki->tk); + goto err_exit; + } + /*use non-packet CTS offsets (faster add/remove)*/ + if (gf_isom_has_time_offset(mp4, tki->tk)) { + gf_isom_set_cts_packing(dest, tki->dst_tk, 1); + } + gf_isom_remove_edit_segments(dest, tki->dst_tk); + } + do_add = 1; + is_last = 0; + last_rap_sample_time = 0; + file_split_dur = split_dur; + + size_exceeded = 0; + max_dts = 0; + while (do_add) { + Double time; + u32 nb_over; + /*perfom basic de-interleaving to make sure we're not importing too much of a given track*/ + u32 nb_add = 0; + /*add one sample of each track*/ + for (i=0; istop_state) + continue; + if (tki->last_sample==tki->sample_count) + continue; + + /*get sample info, see if we need to check it (basic de-interleaver)*/ + dts = gf_isom_get_sample_dts(mp4, tki->tk, tki->last_sample+1); + + /*reinsertion (timed text)*/ + if (dts < tki->firstDTS) { + samp = gf_isom_get_sample(mp4, tki->tk, tki->last_sample+1, &di); + samp->DTS = 0; + e = gf_isom_add_sample(dest, tki->dst_tk, di, samp); + gf_isom_sample_del(&samp); + tki->last_sample += 1; + dts = gf_isom_get_sample_dts(mp4, tki->tk, tki->last_sample+1); + } + dts -= tki->firstDTS; + + + t = (Double) (s64) dts; + t /= tki->time_scale; + if (tki->first_sample_done) { + if (t>max_dts) continue; + } else { + /*here's the trick: only take care of a/v media for deinterleaving, and ad other media + only if thir dts is less than the max AV dts found. Otherwise with some text streams we will end up importing + too much video and corrupting the last sync point indication*/ + if (!tki->can_duplicate && (t>max_dts)) max_dts = t; + tki->first_sample_done = 1; + } + samp = gf_isom_get_sample(mp4, tki->tk, tki->last_sample+1, &di); + samp->DTS -= tki->firstDTS; + + nb_add += 1; + + if (tki->has_non_raps && samp->IsRAP) { + GF_ISOSample *next_rap; + u32 next_rap_num, sdi; + last_rap_sample_time = (Double) (s64) samp->DTS; + last_rap_sample_time /= tki->time_scale; + e = gf_isom_get_sample_for_media_time(mp4, tki->tk, samp->DTS+tki->firstDTS+2, &sdi, GF_ISOM_SEARCH_SYNC_FORWARD, &next_rap, &next_rap_num); + if (e==GF_EOS) is_last = 1; + if (next_rap) { + if (!next_rap->IsRAP) + is_last = 1; + gf_isom_sample_del(&next_rap); + } + } + tki->lastDTS = samp->DTS; + e = gf_isom_add_sample(dest, tki->dst_tk, di, samp); + gf_isom_sample_del(&samp); + tki->last_sample += 1; + gf_set_progress("Splitting", nb_done, nb_samp); + nb_done++; + if (e) { + fprintf(stdout, "Error cloning track %d sample %d\n", tki->tk, tki->last_sample); + goto err_exit; + } + } + + /*test by size/duration*/ + nb_over = 0; + + /*test by file size: same as duration test, only dynamically increment import duration*/ + if (split_size_kb) { + u64 est_size = gf_isom_estimate_size(dest); + /*while below desired size keep importing*/ + if (est_sizestop_state) { + nb_over++; + continue; + } + time = (Double) (s64) tki->lastDTS; + time /= tki->time_scale; + if (size_exceeded || (tki->last_sample==tki->sample_count) || (!tki->can_duplicate && (time>file_split_dur)) ) { + nb_over++; + tki->stop_state = 1; + if (tki->last_samplesample_count) is_last = 0; + if ((!tki->can_duplicate || all_duplicatable) && (tki->last_sample==tki->sample_count)) is_last = 1; + } + /*special tracks (not audio, not video)*/ + else if (tki->can_duplicate) { + u64 dts = gf_isom_get_sample_dts(mp4, tki->tk, tki->last_sample+1); + time = (Double) (s64) (dts - tki->firstDTS); + time /= tki->time_scale; + if (time>file_split_dur) { + nb_over++; + tki->stop_state = 1; + } + } + if (!nb_add && (!max_dts || (tki->lastDTS <= 1 + (u64) (tki->time_scale*max_dts) ))) + tki->first_sample_done = 0; + } + if (nb_over==nb_tk) do_add = 0; + } + + /*remove samples - first figure out smallest duration*/ + file_split_dur = (Double) GF_MAX_FLOAT; + for (i=0; istop_state==2) || (!is_last && (tki->sample_count == tki->last_sample)) ) { + if (tki->has_non_raps) last_rap_sample_time = 0; + continue; + } + + if (tki->lastDTS) { + time = (Double) (s64) tki->lastDTS; + time /= tki->time_scale; + if ((!tki->can_duplicate || all_duplicatable) && timesplit_dur) && !chunk_start) { + /*if larger than last RAP, rewind till it*/ + if (last_rap_sample_time && (last_rap_sample_timedst_tk); + if (!last_samp) break; + + dts = gf_isom_get_sample_dts(dest, tki->dst_tk, last_samp); + time = (Double) (s64) dts; + time /= tki->time_scale; + /*done*/ + if (tki->last_sample==tki->sample_count) { + if (!chunk_extraction && !tki->can_duplicate) { + tki->stop_state=2; + break; + } + } + if (time /*+ (Double) GF_EPSILON_FLOAT*/ < file_split_dur) break; + + gf_isom_remove_sample(dest, tki->dst_tk, last_samp); + tki->last_sample--; + assert(tki->last_sample); + nb_done--; + gf_set_progress("Splitting", nb_done, nb_samp); + } + if (tki->last_samplesample_count) { + u64 dts; + tki->stop_state = 0; + dts = gf_isom_get_sample_dts(mp4, tki->tk, tki->last_sample+1); + time = (Double) (s64) (dts - tki->firstDTS); + time /= tki->time_scale; + /*re-insert prev sample*/ + if (tki->can_duplicate && (time>file_split_dur) ) { + tki->last_sample--; + dts = gf_isom_get_sample_dts(mp4, tki->tk, tki->last_sample+1); + tki->firstDTS += (u64) (file_split_dur*tki->time_scale); + gf_isom_set_last_sample_duration(dest, tki->dst_tk, (u32) (tki->firstDTS - dts) ); + } else { + tki->firstDTS = dts; + } + tki->first_sample_done = 0; + } else { + nb_tk_done++; + } + + } + } + + if (chunk_extraction) { + fprintf(stdout, "Extracting chunk %s - duration %02.2f seconds\n", szFile, file_split_dur); + } else { + fprintf(stdout, "Storing split-file %s - duration %02.2f seconds\n", szFile, file_split_dur); + } + + /*repack CTSs*/ + for (i=0; istop_state == 2) continue; + if (!gf_isom_get_sample_count(dest, tki->dst_tk)) { + gf_isom_remove_track(dest, tki->dst_tk); + continue; + } + if (gf_isom_has_time_offset(mp4, tki->tk)) { + gf_isom_set_cts_packing(dest, tki->dst_tk, 0); + } + if (is_last && tki->can_duplicate) { + gf_isom_set_last_sample_duration(dest, tki->dst_tk, gf_isom_get_sample_duration(mp4, tki->tk, tki->sample_count)); + } + + /*rewrite edit list*/ + new_track_dur = gf_isom_get_track_duration(dest, tki->dst_tk); + count = gf_isom_get_edit_segment_count(mp4, tki->tk); + if (count>2) { + fprintf(stdout, "Warning: %d edit segments - not supported while splitting (max 2) - ignoring extra\n", count); + count=2; + } + for (j=0; jtk, j+1, &editTime, &segDur, &MediaTime, &mode); + if (!j && (mode!=GF_ISOM_EDIT_EMPTY) ) { + fprintf(stdout, "Warning: Edit list doesn't look like a track delay scheme - ignoring\n"); + break; + } + if (mode==GF_ISOM_EDIT_NORMAL) { + segDur = new_track_dur; + } + gf_isom_set_edit_segment(dest, tki->dst_tk, editTime, segDur, MediaTime, mode); + } + } + /*check chapters*/ + do_add = 1; + for (i=0; icur_file_time+file_split_dur) break; + max_dts-=cur_file_time; + chap_time = (u64) (max_dts*1000); + gf_isom_add_chapter(dest, 0, chap_time, name); + /*add prev*/ + if (do_add && i) { + gf_isom_get_chapter(mp4, 0, i, &chap_time, (const char **) &name); + gf_isom_add_chapter(dest, 0, 0, name); + do_add = 0; + } + } + cur_file_time += file_split_dur; + + if (conv_type==1) gf_media_make_isma(dest, 1, 0, 0); + else if (conv_type==2) gf_media_make_3gpp(dest); + if (InterleavingTime) { + gf_isom_make_interleave(dest, InterleavingTime); + } else { + gf_isom_set_storage_mode(dest, GF_ISOM_STORE_STREAMABLE); + } + + gf_isom_clone_pl_indications(mp4, dest); + e = gf_isom_close(dest); + dest = NULL; + if (e) fprintf(stdout, "Error storing file %s\n", gf_error_to_string(e)); + if (is_last || chunk_extraction) break; + cur_file++; + } + gf_set_progress("Splitting", nb_samp, nb_samp); +err_exit: + if (dest) gf_isom_delete(dest); + free(tks); + return e; +} + +GF_Err cat_multiple_files(GF_ISOFile *dest, char *fileName, u32 import_flags, Double force_fps, u32 frames_per_sample, char *tmp_dir, Bool force_cat); + +GF_Err cat_isomedia_file(GF_ISOFile *dest, char *fileName, u32 import_flags, Double force_fps, u32 frames_per_sample, char *tmp_dir, Bool force_cat) +{ + u32 i, j, count, nb_tracks, nb_samp, nb_done; + GF_ISOFile *orig; + GF_Err e; + Float ts_scale; + Double dest_orig_dur; + u32 dst_tk, tk_id, mtype; + u64 insert_dts; + GF_ISOSample *samp; + + if (strchr(fileName, '*')) return cat_multiple_files(dest, fileName, import_flags, force_fps, frames_per_sample, tmp_dir, force_cat); + + e = GF_OK; + if (!gf_isom_probe_file(fileName)) { + orig = gf_isom_open("temp", GF_ISOM_WRITE_EDIT, tmp_dir); + e = import_file(orig, fileName, import_flags, force_fps, frames_per_sample); + if (e) return e; + } else { + orig = gf_isom_open(fileName, GF_ISOM_OPEN_READ, NULL); + } + + nb_samp = 0; + nb_tracks = gf_isom_get_track_count(orig); + for (i=0; i1)) { + insert_dts = 2*gf_isom_get_sample_dts(dest, dst_tk, count) - gf_isom_get_sample_dts(dest, dst_tk, count-1); + } else { + insert_dts = dest_track_dur_before_cat; + if (!count) insert_dts = 0; + } + + ts_scale = (Float) gf_isom_get_media_timescale(dest, dst_tk); + ts_scale /= gf_isom_get_media_timescale(orig, i+1); + + /*if not a new track, see if we can merge the edit list - this is a crude test that only checks + we have the same edit types*/ + if (nb_edits && (nb_edits == gf_isom_get_edit_segment_count(dest, dst_tk)) ) { + u64 editTime, segmentDuration, mediaTime, dst_editTime, dst_segmentDuration, dst_mediaTime; + u8 dst_editMode, editMode; + u32 j; + merge_edits = 1; + for (j=0; jDTS; + samp->DTS = (u64) (ts_scale * (s64)samp->DTS) + insert_dts; + samp->CTS_Offset = (u32) (samp->CTS_Offset * ts_scale); + + if (gf_isom_is_self_contained(orig, i+1, di)) { + e = gf_isom_add_sample(dest, dst_tk, di, samp); + } else { + u64 offset; + GF_ISOSample *s = gf_isom_get_sample_info(orig, i+1, j+1, &di, &offset); + e = gf_isom_add_sample_reference(dest, dst_tk, di, samp, offset); + gf_isom_sample_del(&s); + } + gf_isom_sample_del(&samp); + if (e) goto err_exit; + gf_set_progress("Appending", nb_done, nb_samp); + nb_done++; + } + /*scene description and text: compute last sample duration based on original media duration*/ + if (!use_ts_dur) { + insert_dts = gf_isom_get_media_duration(orig, i+1) - last_DTS; + gf_isom_set_last_sample_duration(dest, dst_tk, (u32) insert_dts); + } + + if (merge_edits) { + /*get the first edit normal mode and add the new track dur*/ + for (j=nb_edits; j>0; j--) { + u64 editTime, segmentDuration, mediaTime; + u8 editMode; + gf_isom_get_edit_segment(dest, dst_tk, j, &editTime, &segmentDuration, &mediaTime, &editMode); + + if (editMode==GF_ISOM_EDIT_NORMAL) { + Double dur = (Double) (s64) gf_isom_get_media_duration(orig, i+1); + /*convert to dst time scale*/ + dur *= ts_scale; + + /*convert to track time scale*/ + ts_scale = (Float) gf_isom_get_timescale(dest); + ts_scale /= (Float) gf_isom_get_media_timescale(dest, dst_tk); + dur *= ts_scale; + + segmentDuration += (u64) (s64) dur; + gf_isom_modify_edit_segment(dest, dst_tk, j, segmentDuration, mediaTime, editMode); + break; + } + } + } else { + u64 editTime, segmentDuration, mediaTime, edit_offset; + Double t; + u8 editMode; + u32 j, count; + + count = gf_isom_get_edit_segment_count(dest, dst_tk); + gf_isom_get_edit_segment(dest, dst_tk, count, &editTime, &segmentDuration, &mediaTime, &editMode); + + + /*convert to dst time scale*/ + ts_scale = (Float) gf_isom_get_timescale(dest); + ts_scale /= (Float) gf_isom_get_timescale(orig); + + edit_offset = editTime + segmentDuration; + count = gf_isom_get_edit_segment_count(orig, i+1); + for (j=0; jszRad1); + if (strnicmp(szName, cat_enum->szRad1, len_rad1)) return 0; + if (strlen(cat_enum->szRad2) && !strstr(szName + len_rad1, cat_enum->szRad2) ) return 0; + + strcpy(szFileName, szName); + strcat(szFileName, cat_enum->szOpt); + + e = cat_isomedia_file(cat_enum->dest, szFileName, cat_enum->import_flags, cat_enum->force_fps, cat_enum->frames_per_sample, cat_enum->tmp_dir, cat_enum->force_cat); + if (e) return 1; + return 0; +} + +GF_Err cat_multiple_files(GF_ISOFile *dest, char *fileName, u32 import_flags, Double force_fps, u32 frames_per_sample, char *tmp_dir, Bool force_cat) +{ + CATEnum cat_enum; + char *sep; + + cat_enum.dest = dest; + cat_enum.import_flags = import_flags; + cat_enum.force_fps = force_fps; + cat_enum.frames_per_sample = frames_per_sample; + cat_enum.tmp_dir = tmp_dir; + cat_enum.force_cat = force_cat; + + strcpy(cat_enum.szPath, fileName); + sep = strrchr(cat_enum.szPath, GF_PATH_SEPARATOR); + if (!sep) sep = strrchr(cat_enum.szPath, '/'); + if (!sep) { + strcpy(cat_enum.szPath, "."); + strcpy(cat_enum.szRad1, fileName); + } else { + strcpy(cat_enum.szRad1, sep+1); + sep[0] = 0; + } + sep = strchr(cat_enum.szRad1, '*'); + strcpy(cat_enum.szRad2, sep+1); + sep[0] = 0; + sep = strchr(cat_enum.szRad2, '%'); + if (!sep) sep = strchr(cat_enum.szRad2, '#'); + if (!sep) sep = strchr(cat_enum.szRad2, ':'); + strcpy(cat_enum.szOpt, ""); + if (sep) { + strcpy(cat_enum.szOpt, sep); + sep[0] = 0; + } + return gf_enum_directory(cat_enum.szPath, 0, cat_enumerate, &cat_enum, NULL); +} + + +/* + MPEG-4 encoding +*/ + +GF_Err EncodeFile(char *in, GF_ISOFile *mp4, GF_SMEncodeOptions *opts, FILE *logs) +{ + GF_Err e; + GF_SceneLoader load; + GF_SceneManager *ctx; + GF_SceneGraph *sg; + GF_StatManager *statsman = NULL; + + sg = gf_sg_new(); + ctx = gf_sm_new(sg); + memset(&load, 0, sizeof(GF_SceneLoader)); + load.fileName = in; + load.ctx = ctx; + load.swf_import_flags = swf_flags; + load.swf_flatten_limit = swf_flatten_angle; + /*since we're encoding we must get MPEG4 nodes only*/ + load.flags = GF_SM_LOAD_MPEG4_STRICT; + e = gf_sm_load_init(&load); + if (e<0) { + gf_sm_load_done(&load); + fprintf(stdout, "Cannot load context %s - %s\n", in, gf_error_to_string(e)); + goto err_exit; + } + e = gf_sm_load_run(&load); + gf_sm_load_done(&load); + + if (opts->auto_quant) { + fprintf(stdout, "Analysing Scene for Automatic Quantization\n"); + statsman = gf_sm_stats_new(); + e = gf_sm_stats_for_scene(statsman, ctx); + if (!e) { + GF_SceneStatistics *stats = gf_sm_stats_get(statsman); + /*LASeR*/ + if (opts->auto_quant==1) { + if (opts->resolution > (s32)stats->frac_res_2d) { + fprintf(stdout, " Given resolution %d is (unnecessarily) too high, using %d instead.\n", opts->resolution, stats->frac_res_2d); + opts->resolution = stats->frac_res_2d; + } else if (stats->int_res_2d + opts->resolution <= 0) { + fprintf(stdout, " Given resolution %d is too low, using %d instead.\n", opts->resolution, stats->int_res_2d - 1); + opts->resolution = 1 - stats->int_res_2d; + } + opts->coord_bits = stats->int_res_2d + opts->resolution; + fprintf(stdout, " Coordinates & Lengths encoded using "); + if (opts->resolution < 0) fprintf(stdout, "only the %d most significant bits (of %d).\n", opts->coord_bits, stats->int_res_2d); + else fprintf(stdout, "a %d.%d representation\n", stats->int_res_2d, opts->resolution); + + fprintf(stdout, " Matrix Scale & Skew Coefficients "); + if (opts->coord_bits - 8 < stats->scale_int_res_2d) { + opts->scale_bits = stats->scale_int_res_2d - opts->coord_bits + 8; + fprintf(stdout, "encoded using a %d.8 representation\n", stats->scale_int_res_2d); + } else { + opts->scale_bits = 0; + fprintf(stdout, "encoded using a %d.8 representation\n", opts->coord_bits - 8); + } + } + /*BIFS*/ + else if (stats->base_layer) { + GF_AUContext *au; + GF_CommandField *inf; + M_QuantizationParameter *qp; + GF_Command *com = gf_sg_command_new(ctx->scene_graph, GF_SG_GLOBAL_QUANTIZER); + qp = (M_QuantizationParameter *) gf_node_new(ctx->scene_graph, TAG_MPEG4_QuantizationParameter); + + inf = gf_sg_command_field_new(com); + inf->new_node = (GF_Node *)qp; + inf->field_ptr = &inf->new_node; + inf->fieldType = GF_SG_VRML_SFNODE; + gf_node_register(inf->new_node, NULL); + au = gf_list_get(stats->base_layer->AUs, 0); + gf_list_insert(au->commands, com, 0); + qp->useEfficientCoding = 1; + qp->textureCoordinateQuant = 0; + if ((stats->count_2f+stats->count_2d) && opts->resolution) { + qp->position2DMin = stats->min_2d; + qp->position2DMax = stats->max_2d; + qp->position2DNbBits = opts->resolution; + qp->position2DQuant = 1; + } + if ((stats->count_3f+stats->count_3d) && opts->resolution) { + qp->position3DMin = stats->min_3d; + qp->position3DMax = stats->max_3d; + qp->position3DQuant = opts->resolution; + qp->position3DQuant = 1; + qp->textureCoordinateQuant = 1; + } + if (0 && stats->count_float && opts->resolution) { + qp->scaleMin = stats->min_fixed; + qp->scaleMax = stats->max_fixed; + qp->scaleNbBits = 2*opts->resolution; + qp->scaleQuant = 1; + } + } + } + } + + if (e) { + fprintf(stdout, "Error loading file %s\n", gf_error_to_string(e)); + goto err_exit; + } else { + u32 prev_level = gf_log_get_level(); + u32 prev_tools = gf_log_get_tools(); + gf_log_cbk prev_logs = NULL; + if (logs) { + gf_log_set_tools(GF_LOG_CODING); + gf_log_set_level(GF_LOG_DEBUG); + prev_logs = gf_log_set_callback(logs, scene_coding_log); + } + e = gf_sm_encode_to_file(ctx, mp4, opts); + if (logs) { + gf_log_set_tools(prev_tools); + gf_log_set_level(prev_level); + gf_log_set_callback(NULL, prev_logs); + } + } + + gf_isom_set_brand_info(mp4, GF_ISOM_BRAND_MP42, 1); + gf_isom_modify_alternate_brand(mp4, GF_ISOM_BRAND_ISOM, 1); + +err_exit: + if (statsman) gf_sm_stats_del(statsman); + gf_sm_del(ctx); + gf_sg_del(sg); + return e; +} + +/* + MPEG-4 chunk encoding +*/ + +static u32 GetNbBits(u32 MaxVal) +{ + u32 k=0; + while ((s32) MaxVal > ((1<streams starting at AU index 1 (0 is SceneReplace from previous context) */ + bifsenc = gf_bifs_encoder_new(ctx->scene_graph); + e = GF_OK; + + iod = (GF_InitialObjectDescriptor *) ctx->root_od; + /*if no iod check we only have one bifs*/ + if (!iod) { + count = 0; + for (i=0; istreams); i++) { + sc = gf_list_get(ctx->streams, i); + if (sc->streamType == GF_STREAM_OD) count++; + } + if (!iod && count>1) return GF_NOT_SUPPORTED; + } + + count = gf_list_count(ctx->streams); + + for (i=0; istreams); i++) { + u32 nbb; + GF_StreamContext *sc = gf_list_get(ctx->streams, i); + esd = NULL; + if (sc->streamType != GF_STREAM_SCENE) continue; + + delete_desc = 0; + esd = NULL; + is_in_iod = 1; + if (iod) { + is_in_iod = 0; + for (j=0; jESDescriptors); j++) { + esd = gf_list_get(iod->ESDescriptors, j); + if (esd->decoderConfig && esd->decoderConfig->streamType == GF_STREAM_SCENE) { + if (!sc->ESID) sc->ESID = esd->ESID; + if (sc->ESID == esd->ESID) { + is_in_iod = 1; + break; + } + } + /*special BIFS direct import from NHNT*/ + else if (gf_list_count(iod->ESDescriptors)==1) { + sc->ESID = esd->ESID; + is_in_iod = 1; + break; + } + esd = NULL; + } + } + + if (!esd) { + delete_desc = 1; + esd = gf_odf_desc_esd_new(2); + gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = NULL; + esd->ESID = sc->ESID; + esd->decoderConfig->streamType = GF_STREAM_SCENE; + } + + /*should NOT happen (means inputctx is not properly setup)*/ + if (!esd->decoderConfig->decoderSpecificInfo) { + bcfg = (GF_BIFSConfig*)gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); + delete_bcfg = 1; + } + /*regular retrieve from ctx*/ + else if (esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_BIFS_CFG_TAG) { + bcfg = (GF_BIFSConfig *)esd->decoderConfig->decoderSpecificInfo; + delete_bcfg = 0; + } + /*should not happen either (unless loading from MP4 in which case BIFSc is not decoded)*/ + else { + bcfg = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication); + delete_bcfg = 1; + } + /*NO CHANGE TO BIFSC otherwise the generated update will not match the input context*/ + nbb = GetNbBits(ctx->max_node_id); + if (bcfg->nodeIDbitsmax_route_id); + if (bcfg->routeIDbitsmax_proto_id); + if (bcfg->protoIDbitsESID, bcfg, encode_names, 0); + if (delete_bcfg) gf_odf_desc_del((GF_Descriptor *)bcfg); + + /*setup MP4 track*/ + if (!esd->slConfig) esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + if (sc->timeScale) esd->slConfig->timestampResolution = sc->timeScale; + if (!esd->slConfig->timestampResolution) esd->slConfig->timestampResolution = 1000; + esd->ESID = sc->ESID; + gf_bifs_encoder_get_config(bifsenc, sc->ESID, &data, &data_len); + + if (esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + esd->decoderConfig->decoderSpecificInfo->data = data; + esd->decoderConfig->decoderSpecificInfo->dataLength = data_len; + esd->decoderConfig->objectTypeIndication = gf_bifs_encoder_get_version(bifsenc, sc->ESID); + + for (j=1; jAUs); j++) { + char *data; + u32 data_len; + au = gf_list_get(sc->AUs, j); + e = gf_bifs_encode_au(bifsenc, sc->ESID, au->commands, &data, &data_len); + if (data) { + sprintf(szName, "%s%02d.bifs", szRad, j); + f = fopen(szName, "wb"); + fwrite(data, data_len, 1, f); + fclose(f); + free(data); + } + } + } + gf_bifs_encoder_del(bifsenc); + return e; +} + + +/** + * @chunkFile BT chunk to be encoded + * @bifs output file name for the BIFS data + * @inputContext initial BT upon which the chunk is based (shall not be NULL) + * @outputContext: file name to dump the context after applying the new chunk to the input context + can be NULL, without .bt + * @logFile: can be NULL + */ +GF_Err EncodeFileChunk(char *chunkFile, char *bifs, char *inputContext, char *outputContext, const char *tmpdir) +{ + GF_Err e; + GF_SceneGraph *sg; + GF_SceneManager *ctx; + GF_SceneLoader load; + + /*Step 1: create context and load input*/ + sg = gf_sg_new(); + ctx = gf_sm_new(sg); + memset(&load, 0, sizeof(GF_SceneLoader)); + load.fileName = inputContext; + load.ctx = ctx; + /*since we're encoding we must get MPEG4 nodes only*/ + load.flags = GF_SM_LOAD_MPEG4_STRICT; + e = gf_sm_load_init(&load); + if (!e) e = gf_sm_load_run(&load); + gf_sm_load_done(&load); + if (e) { + fprintf(stdout, "Cannot load context %s - %s\n", inputContext, gf_error_to_string(e)); + goto exit; + } + + /* Step 2: make sure we have only ONE RAP for each stream*/ + e = gf_sm_make_random_access(ctx); + if (e) goto exit; + + /*Step 3: loading the chunk into the context*/ + memset(&load, 0, sizeof(GF_SceneLoader)); + load.fileName = chunkFile; + load.ctx = ctx; + load.flags = GF_SM_LOAD_MPEG4_STRICT | GF_SM_LOAD_CONTEXT_READY; + e = gf_sm_load_init(&load); + if (!e) e = gf_sm_load_run(&load); + gf_sm_load_done(&load); + if (e) { + fprintf(stdout, "Cannot load chunk context %s - %s\n", chunkFile, gf_error_to_string(e)); + goto exit; + } + fprintf(stdout, "Context and chunks loaded\n"); + + /* Assumes that the first AU contains only one command a SceneReplace and + that is not part of the current chunk */ + /* Last argument is a callback to pass the encoded AUs: not needed here + Saving is not handled correctly */ + e = EncodeBIFSChunk(ctx, bifs, NULL); + if (e) goto exit; + + + if (outputContext) { + u32 d_mode, do_enc; + char szF[GF_MAX_PATH], *ext; + + /*make random access for storage*/ + e = gf_sm_make_random_access(ctx); + if (e) goto exit; + + /*check if we dump to BT, XMT or encode to MP4*/ + strcpy(szF, outputContext); + ext = strrchr(szF, '.'); + d_mode = GF_SM_DUMP_BT; + do_enc = 0; + if (ext) { + if (!stricmp(ext, ".xmt") || !stricmp(ext, ".xmta")) d_mode = GF_SM_DUMP_XMTA; + else if (!stricmp(ext, ".mp4")) do_enc = 1; + ext[0] = 0; + } + + if (do_enc) { + GF_ISOFile *mp4; + strcat(szF, ".mp4"); + mp4 = gf_isom_open(szF, GF_ISOM_WRITE_EDIT, tmpdir); + e = gf_sm_encode_to_file(ctx, mp4, NULL); + if (e) gf_isom_delete(mp4); + else gf_isom_close(mp4); + } + else e = gf_sm_dump(ctx, szF, d_mode); + } + +exit: + if (ctx) { + sg = ctx->scene_graph; + gf_sm_del(ctx); + gf_sg_del(sg); + } + return e; +} + +#include + +void sax_node_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + char szCheck[100]; + GF_List *imports = sax_cbck; + GF_XMLAttribute *att; + u32 i=0; + + /*do not process hyperlinks*/ + if (!strcmp(node_name, "a") || !strcmp(node_name, "Anchor")) return; + + for (i=0; iname, "xlink:href") && stricmp(att->name, "url")) continue; + if (att->value[0]=='#') continue; + if (!strnicmp(att->value, "od:", 3)) continue; + sprintf(szCheck, "%d", atoi(att->value)); + if (!strcmp(szCheck, att->value)) continue; + gf_list_add(imports, strdup(att->value) ); + } +} + +GF_ISOFile *package_file(char *file_name, char *fcc, const char *tmpdir) +{ + GF_ISOFile *file = NULL; + GF_Err e; + GF_SAXParser *sax; + GF_List *imports; + Bool ascii; + char *isom_src = NULL; + u32 i, count, mtype; + char *type; + + type = gf_xml_get_root_type(file_name, &e); + if (!type) { + fprintf(stdout, "Cannot process XML file %s: %s\n", file_name, gf_error_to_string(e) ); + return NULL; + } + + imports = gf_list_new(); + sax = gf_xml_sax_new(sax_node_start, NULL, NULL, imports); + e = gf_xml_sax_parse_file(sax, file_name, NULL); + ascii = !gf_xml_sax_binary_file(sax); + gf_xml_sax_del(sax); + if (e<0) goto exit; + e = GF_OK; + + if (fcc) { + mtype = GF_4CC(fcc[0],fcc[1],fcc[2],fcc[3]); + } else { + mtype = 0; + if (!stricmp(type, "svg")) mtype = ascii ? GF_4CC('s','v','g',' ') : GF_4CC('s','v','g','z'); + else if (!stricmp(type, "smil")) mtype = ascii ? GF_4CC('s','m','i','l') : GF_4CC('s','m','l','z'); + else if (!stricmp(type, "x3d")) mtype = ascii ? GF_4CC('x','3','d',' ') : GF_4CC('x','3','d','z') ; + else if (!stricmp(type, "xmt-a")) mtype = ascii ? GF_4CC('x','m','t','a') : GF_4CC('x','m','t','z'); + } + if (!mtype) { + fprintf(stdout, "Missing 4CC code for meta name - please use ABCD:fileName\n"); + e = GF_BAD_PARAM; + goto exit; + } + + + count = gf_list_count(imports); + for (i=0; i +#include +/*RTP packetizer flags*/ +#include +#include + +#define BUFFSIZE 8192 + +/*in fileimport.c*/ +#ifndef GPAC_READ_ONLY +void convert_file_info(char *inName, u32 trackID); +GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double force_fps, u32 frames_per_sample); +GF_Err split_isomedia_file(GF_ISOFile *mp4, Double split_dur, u32 split_size_kb, char *inName, Double InterleavingTime, Double chunk_start, const char *tmpdir, char *outfile); +GF_Err cat_isomedia_file(GF_ISOFile *mp4, char *fileName, u32 import_flags, Double force_fps, u32 frames_per_sample, char *tmp_dir, Bool force_cat); + +GF_Err EncodeFile(char *in, GF_ISOFile *mp4, GF_SMEncodeOptions *opts, FILE *logs); +GF_Err EncodeFileChunk(char *chunkFile, char *bifs, char *inputContext, char *outputContext, const char *tmpdir); + +GF_ISOFile *package_file(char *file_name, char *fcc, const char *tmpdir); +GF_Err dump_cover_art(GF_ISOFile *file, char *inName); +u32 id3_get_genre_tag(const char *name); +#endif + +/*in filedump.c*/ +#ifndef GPAC_READ_ONLY +GF_Err dump_file_text(char *file, char *inName, u32 dump_mode, Bool do_log); +void dump_scene_stats(char *file, char *inName, u32 stat_level); +#endif +void PrintNode(const char *name, u32 graph_type); +void PrintBuiltInNodes(u32 graph_type); +void dump_file_mp4(GF_ISOFile *file, char *inName); +void dump_file_rtp(GF_ISOFile *file, char *inName); +void dump_file_ts(GF_ISOFile *file, char *inName); +void dump_file_ismacryp(GF_ISOFile *file, char *inName); +void dump_timed_text_track(GF_ISOFile *file, u32 trackID, char *inName, Bool is_convert, u32 dump_type); +void DumpSDP(GF_ISOFile *file, char *inName); +void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump); +void DumpMovieInfo(GF_ISOFile *file); +void PrintLanguages(); +const char *GetLanguageCode(char *lang); +void dump_mpeg2_ts(char *mpeg2ts_in, char *pes_out_name); + + +Bool quiet = 0; + +/*some global vars for swf import :(*/ +u32 swf_flags = 0; +Float swf_flatten_angle = 0; +s32 laser_resolution = 0; + + +typedef struct { u32 code; const char *name; const char *comment; } itunes_tag; +static const itunes_tag itags[] = { + {GF_ISOM_ITUNE_ALBUM_ARTIST, "album_artist", "usage: album_artist=album artist"}, + {GF_ISOM_ITUNE_ALBUM, "album", "usage: album=name" }, + {GF_ISOM_ITUNE_TRACKNUMBER, "tracknum", "usage: track=x/N"}, + {GF_ISOM_ITUNE_TRACK, "track", "usage: track=name"}, + {GF_ISOM_ITUNE_ARTIST, "artist", "usage: artist=name"}, + {GF_ISOM_ITUNE_COMMENT, "comment", "usage: comment=any comment"}, + {GF_ISOM_ITUNE_COMPILATION, "compilation", "usage: compilation=yes,no"}, + {GF_ISOM_ITUNE_COMPOSER, "composer", "usage: composer=name"}, + {GF_ISOM_ITUNE_CREATED, "created", ""}, + {GF_ISOM_ITUNE_DISK, "disk", "usage: disk=x/N"}, + {GF_ISOM_ITUNE_TOOL, "tool", "usage: tool=name"}, + {GF_ISOM_ITUNE_GENRE, "genre", "usage: genre=name"}, + {GF_ISOM_ITUNE_NAME, "name", "usage: name=name"}, + {GF_ISOM_ITUNE_TEMPO, "tempo", "usage: tempo=integer"}, + {GF_ISOM_ITUNE_WRITER, "writer", "usage: writer=name"}, + {GF_ISOM_ITUNE_GROUP, "group", "usage: group=name"}, + {GF_ISOM_ITUNE_COVER_ART, "cover", "usage: covber=file.jpg,file.png"}, + {GF_ISOM_ITUNE_ENCODER, "encoder", "usage: encoder=name"}, + {GF_ISOM_ITUNE_GAPELESS, "gapeless", "usage: artist=yes,no"}, +}; + +u32 nb_itunes_tags = sizeof(itags) / sizeof(itunes_tag); + + +void PrintVersion() +{ + fprintf(stdout, "MP4Box - GPAC version " GPAC_FULL_VERSION "\n" +#ifdef GPAC_FIXED_POINT + "GPAC compiled in fixed-point version\n" +#endif +#ifdef GPAC_READ_ONLY + "GPAC compiled in read-only version\n" +#endif + "GPAC Copyright: (c) Jean Le Feuvre 2000-2005\n\t\t(c) ENST 2005-200X\n"); +} + +void PrintGeneralUsage() +{ + fprintf(stdout, "General Options:\n" + " -inter time_in_ms interleaves file data (track chunks of time_in_ms)\n" + " * Note 1: Interleaving is 0.5s by default\n" + " * Note 2: Performs drift checking accross tracks\n" + " * Note 3: a value of 0 disables interleaving\n" + " -old-inter time same as -inter but doesn't perform drift checking\n" + " -tight: performs tight interleaving (sample based) of the file\n" + " * Note: reduces disk seek but increases file size\n" + " -flat stores file with all media data first, non-interleaved\n" + " -frag time_in_ms fragments file (track fragments of time_in_ms)\n" + " * Note: Always disables interleaving\n" + " -out filename specifies output file name\n" + " * Note: By default input (MP4,3GP) file is overwritten\n" + " -tmp dirname specifies directory for temporary file creation\n" + " * Note: Default temp dir is OS-dependent\n" + " -no-sys removes all MPEG-4 Systems info except IOD (profiles)\n" + " * Note: Set by default whith '-add' and '-cat'\n" + " -no-iod removes InitialObjectDescriptor from file\n" + " -isma rewrites the file as an ISMA 1.0 AV file\n" + " -ismax same as \'-isma\' and removes all clock references\n" + " -3gp rewrites as 3GPP(2) file (no more MPEG-4 Systems Info)\n" + " * Note 1: some tracks may be removed in the process\n" + " * Note 2: always on for *.3gp *.3g2 *.3gpp\n" + " -ipod rewrites the file for iPod\n" + " -brand ABCD[:v] sets major brand of file, with optional version\n" + " -ab ABCD adds given brand to file's alternate brand list\n" + " -rb ABCD removes given brand from file's alternate brand list\n" + " -cprt string adds copyright string to movie\n" + " -chap file adds chapter information contained in file\n" + " -rem trackID: removes track from file\n" + " -enable trackID: enables track\n" + " -disable trackID: disables track\n" + " -new: forces creation of a new destination file\n" + " -rem trackID: removes track from file\n" + " -lang [tkID=]LAN: sets track language. LAN is the ISO 639-2 code (eng, und)\n" + " -delay tkID=TIME: sets track start delay in ms.\n" + " -par tkID=PAR: sets visual track pixel aspect ratio (PAR=N:D or \"none\")\n" + " -name tkID=NAME: sets track handler name\n" + " * NAME can indicate a UTF-8 file (\"file://file name\"\n" + " -itags tag1[:tag2]: sets iTunes tags to file - more info: MP4Box -tag-list.\n" + " -split time_sec splits in files of time_sec max duration\n" + " * Note: this removes all MPEG-4 Systems media\n" + " -split-size size splits in files of max filesize kB.\n" + " * Note: this removes all MPEG-4 Systems media\n" + " -split-chunk S:E extracts a new file from Start to End (in seconds)\n" + " * Note: this removes all MPEG-4 Systems media\n" + " -group-add fmt creates a new grouping information in the file. Format is\n" + " a colon-separated list of following options:\n" + " refTrack=ID: ID of the track used as a group reference.\n" + " If not set, the track will belong to the same group as the previous trackID specified.\n" + " If 0 or no previous track specified, a new alternate group will be created\n" + " switchID=ID: ID of the switch group to create.\n" + " If 0, a new ID will be computed for you\n" + " If <0, disables SwitchGroup\n" + " criteria=string: list of space-separated 4CCs.\n" + " trackID=ID: ID of the track to add to this group.\n" + "\n" + " *WARNING* Options modify state as they are parsed:\n" + " trackID=1:criteria=lang:trackID=2\n" + " is different from:\n" + " criteria=lang:trackID=1:trackID=2\n" + "\n" + " -group-rem-track ID removes track from its group\n" + " -group-rem ID removes the track's group\n" + " -group-clean removes all group information from all tracks\n" + " -ref id:XXXX:refID adds a reference of type 4CC from track ID to track refID\n" + "\n"); +} + +void PrintFormats() +{ + fprintf(stdout, "Suppported raw formats and file extensions:\n" + " NHNT .media .nhnt .info\n" + " NHML .nhml (opt: .media .info)\n" + " MPEG-1-2 Video .m1v .m2v\n" + " MPEG-4 Video .cmp .m4v\n" + " H263 Video .263 .h263\n" + " AVC/H264 Video .h264 .h26L .264 .26L\n" + " JPEG Images .jpg .jpeg\n" + " PNG Images .png\n" + " MPEG 1-2 Audio .mp3, .m1a, .m2a\n" + " ADTS-AAC Audio .aac\n" + " AMR(WB) Audio .amr .awb\n" + " EVRC Audio .evc\n" + " SMV Audio .smv\n" + "\n" + "Supported containers and file extensions:\n" + " AVI .avi\n" + " MPEG-2 PS .mpg .mpeg .vob .vcd .svcd\n" + " MPEG-2 TS .ts .m2t\n" + " QCP .qcp\n" + " OGG .ogg\n" + " ISO-Media files no extension checking\n" + "\n" + "Supported text formats:\n" + " SRT Subtitles .srt\n" + " SUB Subtitles .sub\n" + " GPAC Timed Text .ttxt\n" + " QuickTime TeXML Text .xml (cf QT documentation)\n" + "\n" + "Supported Scene formats:\n" + " MPEG-4 XMT-A .xmt .xmta .xmt.gz .xmta.gz\n" + " MPEG-4 BT .bt .bt.gz\n" + " VRML .wrl .wrl.gz\n" + " X3D-XML .x3d .x3d.gz\n" + " X3D-VRML .x3dv .x3dv.gz\n" + " MacroMedia Flash .swf (very limitted import support only)\n" + "\n" + ); +} + +void PrintImportUsage() +{ + fprintf(stdout, "Importing Options\n" + "\nFile importing syntax:\n" + " \"#video\" \"#audio\": base import for most AV files\n" + " \"#trackID=ID\": track import for IsoMedia and other files\n" + " \"#pid=ID\": stream import from MPEG-2 TS\n" + " \":dur=D\": imports only the first D seconds\n" + " \":lang=LAN\": sets imported media language code\n" + " \":delay=delay_ms\": sets imported media initial delay in ms\n" + " \":par=PAR\": sets visual pixel aspect ratio (PAR=Num:Den)\n" + " \":name=NAME\": sets track handler name\n" + " \":disable\": imported track(s) will be disabled\n" + " \":group=G\": adds the track as part of the G alternate group.\n" + " * If G is 0, the first available GroupID will be picked.\n" + " \":fps=VAL\": same as -fps option\n" + " \":agg=VAL\": same as -agg option\n" + " \":par=VAL\": same as -par option\n" + " \":dref\": same as -dref option\n" + " \":nodrop\": same as -nodrop option\n" + " \":packed\": same as -packed option\n" + " \":sbr\": same as -sbr option\n" + " \":sbrx\": same as -sbrx option\n" + " \":mpeg4\": same as -mpeg4 option\n" + " \":font=name\": specifies font name for text import (default \"Serif\")\n" + " \":size=s\": specifies font size for text import (default 18)\n" + "\n" + " -add file: add file tracks to (new) output file\n" + " -cat file: concatenates file samples to (new) output file\n" + " * Note: creates tracks if needed\n" + " -force-cat: skips media configuration check when concatenating file\n" + " !!! THIS MAY BREAK THE CONCATENATED TRACK(S) !!!\n" + " -keep-sys: keeps all MPEG-4 Systems info when using '-add' / 'cat'\n" + " -keep-all: keeps all existing tracks when using '-add'\n" + " * Note: only used when adding IsoMedia files\n" + "\n" + "All the following options can be specified as default or for each track.\n" + "When specified by track the syntax is \":opt\" or \":opt=val\".\n\n" + " -dref: keeps media data in original file\n" + " -no-drop: forces constant FPS when importing AVI video\n" + " -packed: * forces packed bitstream when importing raw ASP\n" + " -sbr: backward compatible signaling of AAC-SBR\n" + " -sbrx: non-backward compatible signaling of AAC-SBR\n" + " * Note: SBR AAC cannot be detected at import time\n" + " -fps FPS: forces frame rate for video and SUB subtitles import\n" + " * For raw H263 import, default FPS is 15\n" + " * For all other imports, default FPS is 25\n" + " -- THIS IS IGNORED FOR IsoMedia IMPORT --\n" + " -mpeg4: forces MPEG-4 sample descriptions when possible (3GPP2)\n" + " For AAC, forces MPEG-4 AAC signaling even if MPEG-2\n" + " -agg N: aggregates N audio frames in 1 sample (3GP media only)\n" + " * Note: Maximum value is 15 - Disabled by default\n" + "\n" + ); +} + +void PrintEncodeUsage() +{ + fprintf(stdout, "MPEG-4 Scene Encoding Options\n" + " -mp4: specify input file is for encoding.\n" + " -def: encode DEF names\n" + " -sync time_in_ms: forces BIFS sync sample generation every time_in_ms\n" + " * Note: cannot be used with -shadow\n" + " -shadow time_ms: forces BIFS sync shadow sample generation every time_ms.\n" + " * Note: cannot be used with -sync\n" + " -log: generates scene codec log file if available\n" + " -ms file: specifies file for track importing\n" + "\nChunk Processing\n" + " -ctx-in file: specifies initial context (MP4/BT/XMT)\n" + " * Note: input file must be a commands-only file\n" + " -ctx-out file: specifies storage of updated context (MP4/BT/XMT)\n" + "\n" + "LASeR Encoding options\n" + " -resolution res: resolution factor (-8 to 7, default 0)\n" + " all coords are multiplied by 2^res before truncation\n" + " -coord-bits bits: bits used for encoding truncated coordinates\n" + " (0 to 31, default 12)\n" + " -scale-bits bits: extra bits used for encoding truncated scales\n" + " (0 to 4, default 0)\n" + " -auto-quant res: resolution is given as if using -resolution\n" + " but coord-bits and scale-bits are infered\n" + ); +} + +void PrintEncryptUsage() +{ + fprintf(stdout, "ISMA Encryption/Decryption Options\n" + " -crypt drm_file: crypts a specific track using ISMA AES CTR 128\n" + " -decrypt [drm_file] decrypts a specific track using ISMA AES CTR 128\n" + " * Note: drm_file can be omitted if keys are in file\n" + " -set-kms kms_uri changes KMS location for all tracks or a given one.\n" + " * to adress a track, use \'tkID=kms_uri\'\n" + "\n" + "DRM file syntax for GPAC ISMACryp:\n" + " File is XML and shall start with xml header\n" + " File root is an \"ISMACryp\" element\n" + " File is a list of \"ISMACrypTrack\" elements\n" + "\n" + "ISMACrypTrack attributes are\n" + " TrackID: ID of track to en/decrypt\n" + " key: AES-128 key formatted (hex string \'0x\'+32 chars)\n" + " salt: CTR IV salt key (64 bits) (hex string \'0x\'+16 chars)\n" + "\nEncryption only attributes:\n" + " Scheme_URI: URI of scheme used\n" + " KMS_URI: URI of key management system\n" + " * Note: \'self\' writes key and salt in the file\n" + " selectiveType: selective encryption type - understood values are:\n" + " \"None\": all samples encrypted (default)\n" + " \"RAP\": only encrypts random access units\n" + " \"Non-RAP\": only encrypts non-random access units\n" + " \"Rand\": random selection is performed\n" + " \"X\": Encrypts every first sample out of X (uint)\n" + " \"RandX\": Encrypts one random sample out of X (uint)\n" + "\n" + " ipmpType: IPMP Signaling Type: None, IPMP, IPMPX\n" + " ipmpDescriptorID: IPMP_Descriptor ID to use if IPMP(X) is used\n" + " * If not set MP4Box will generate one for you\n" + "\n" + ); +} + +void PrintHintUsage() +{ + fprintf(stdout, "Hinting Options\n" + " -hint: hints the file for RTP/RTSP\n" + " -mtu size: specifies RTP MTU (max size) in bytes. Default size is 1450\n" + " * Note: this includes the RTP header (12 bytes)\n" + " -copy: copies media data to hint track rather than reference\n" + " * Note: speeds up server but takes much more space\n" + " -multi [maxptime]: enables frame concatenation in RTP packets if possible\n" + " maxptime: max packet duration in ms (optional, default 100ms)\n" + " -rate ck_rate: specifies rtp rate in Hz when no default for payload\n" + " * Note: default value is 90000 (MPEG rtp rates)\n" + " -mpeg4: forces MPEG-4 generic payload whenever possible\n" + " -latm: forces MPG4-LATM transport for AAC streams\n" + " -static: enables static RTP payload IDs whenever possible\n" + " * By default, dynamic payloads are always used\n" + "\n" + "MPEG-4 Generic Payload Options\n" + " -ocr: forces all streams to be synchronized\n" + " * Most RTSP servers only support synchronized streams\n" + " -rap: signals random access points in RTP packets\n" + " -ts: signals AU Time Stamps in RTP packets\n" + " -size: signals AU size in RTP packets\n" + " -idx: signals AU sequence numbers in RTP packets\n" + " -iod: prevents systems tracks embedding in IOD\n" + " * Note: shouldn't be used with -isma option\n" + "\n" + " -add-sdp string: adds sdp string to (hint) track (\"-add-sdp tkID:string\")\n" + " or movie. This will take care of SDP lines ordering\n" + " -unhint: removes all hinting information.\n" + "\n"); +} +void PrintExtractUsage() +{ + fprintf(stdout, "Extracting Options\n" + " -raw TrackID: extracts track in raw format when supported\n" + " -raws TrackID: extract each track sample to a file\n" + " * Note: \"TrackID:N\" extracts Nth sample\n" + " -nhnt TrackID: extracts track in nhnt format\n" + " -nhml TrackID: extracts track in nhml format (XML nhnt).\n" + " * Note: \"-nhml +TrackID\" for full dump\n" + " -single TrackID: extracts track to a new mp4 file\n" + " -avi TrackID: extracts visual track to an avi file\n" + " -qcp TrackID: same as \'-raw\' but defaults to QCP file for EVRC/SMV\n" + " -aviraw TK: extracts AVI track in raw format\n" + " $TK can be one of \"video\" \"audio\" \"audioN\"\n" + " -saf: remux file to SAF multiplex\n" + " * Note: can be used when encoding scene descriptions\n" + "\n"); +} +void PrintDumpUsage() +{ + fprintf(stdout, "Dumping Options\n" + " -std: dumps to stdout instead of file\n" + " -info [trackID] prints movie info / track info if trackID specified\n" + " * Note: for non IsoMedia files, gets import options\n" + " -bt: scene to bt format - removes unknown MPEG4 nodes\n" + " -xmt: scene to XMT-A format - removes unknown MPEG4 nodes\n" + " -wrl: scene VRML format - removes unknown VRML nodes\n" + " -x3d: scene to X3D/XML format - removes unknown X3D nodes\n" + " -x3dv: scene to X3D/VRML format - removes unknown X3D nodes\n" + " -lsr: scene to LASeR format\n" + " -diso: scene IsoMedia file boxes in XML output\n" + " -drtp: rtp hint samples structure to XML output\n" + " -dts: prints sample timing to text output\n" + " -sdp: dumps SDP description of hinted file\n" + " -dcr: ISMACryp samples structure to XML output\n" + "\n" +#ifndef GPAC_READ_ONLY + " -ttxt: Converts input subtitle to GPAC TTXT format\n" +#endif + " -ttxt TrackID: Dumps Text track to GPAC TTXT format\n" +#ifndef GPAC_READ_ONLY + " -srt: Converts input subtitle to SRT format\n" +#endif + " -srt TrackID: Dumps Text track to SRT format\n" + "\n" + " -stat: generates node/field statistics for scene\n" + " -stats: generates node/field statistics per MPEG-4 Access Unit\n" + " -statx: generates node/field statistics for scene after each AU\n" + "\n" + " -hash: generates SHA-1 Hash of the input file\n" + "\n"); +} + +void PrintMetaUsage() +{ + fprintf(stdout, "Meta handling Options\n" + " -set-meta args: sets given meta type - syntax: \"ABCD[:tk=ID]\"\n" + " * ABCD: four char meta type (NULL or 0 to remove meta)\n" + " * [:tk=ID]: if not set use root (file) meta\n" + " if ID is 0 use moov meta\n" + " if ID is not 0 use track meta\n" + " -add-item args: adds resource to meta\n" + " * syntax: file_path + options (\':\' separated):\n" + " tk=ID: meta adressing (file, moov, track)\n" + " name=str: item name\n" + " mime=mtype: item mime type\n" + " encoding=enctype: item content-encoding type\n" + " * file_path \"this\" or \"self\": item is the file itself\n" + " -rem-item args: removes resource from meta - syntax: item_ID[:tk=ID]\n" + " -set-primary args: sets item as primary for meta - syntax: item_ID[:tk=ID]\n" + " -set-xml args: sets meta XML data\n" + " * syntax: xml_file_path[:tk=ID][:binary]\n" + " -rem-xml [tk=ID]: removes meta XML data\n" + " -dump-xml args: dumps meta XML to file - syntax file_path[:tk=ID]\n" + " -dump-item args: dumps item to file - syntax item_ID[:tk=ID][:path=fileName]\n" + " -package: packages input XML file into an ISO container\n" + " * all media referenced except hyperlinks are added to file\n" + "\n"); +} + +void PrintSWFUsage() +{ + fprintf(stdout, + "SWF Importer Options\n" + "\n" + "MP4Box can import simple Macromedia Flash files (\".SWF\")\n" + "You can specify a SWF input file with \'-bt\', \'xmt\' and \'-mp4\' options\n" + "\n" + " -global: all SWF defines are placed in first scene replace\n" + " * Note: By default SWF defines are sent when needed\n" + " -no-ctrl: uses a single stream for movie control and dictionary\n" + " * Note: this will disable ActionScript\n" + " -no-text: removes all SWF text\n" + " -no-font: removes all embedded SWF Fonts (terminal fonts used)\n" + " -no-line: removes all lines from SWF shapes\n" + " -no-grad: removes all gradients from swf shapes\n" + " -quad: uses quadratic bezier curves instead of cubic ones\n" + " -xlp: support for lines transparency and scalability\n" + " -flatten ang: complementary angle below which 2 lines are merged\n" + " * Note: angle \'0\' means no flattening\n" + "\n" + ); +} + +void PrintUsage() +{ + fprintf (stdout, "MP4Box [option] input [option]\n" +#ifndef GPAC_READ_ONLY + " -h general: general options help\n" + " -h hint: hinting options help\n" + " -h import: import options help\n" + " -h encode: encode options help\n" + " -h meta: meta handling options help\n" +#else + "READ-ONLY VERSION\n" +#endif + " -h extract: extraction options help\n" + " -h dump: dump options help\n" + " -h swf: Flash (SWF) options help\n" + " -h crypt: ISMA E&A options help\n" + " -h format: supported formats help\n" + "\n" + " -nodes: lists supported MPEG4 nodes\n" + " -node NodeName: gets MPEG4 node syntax and QP info\n" + " -xnodes: lists supported X3D nodes\n" + " -xnode NodeName: gets X3D node syntax\n" + " -snodes: lists supported SVG nodes\n" + " -snode NodeName: gets SVG node syntax\n" + " -languages: lists supported ISO 639 languages\n" + "\n" + "-quiet: quiet mode\n" + " -v: verbose mode\n" + " -version: gets build version\n" + ); +} + + +void scene_coding_log(void *cbk, u32 log_level, u32 log_tool, const char *fmt, va_list vlist) +{ + FILE *logs = cbk; + if (log_tool != GF_LOG_CODING) return; + vfprintf(logs, fmt, vlist); + fflush(logs); +} + +#ifndef GPAC_READ_ONLY + +/* + MP4 File Hinting +*/ + +void SetupClockReferences(GF_ISOFile *file) +{ + u32 i, count, ocr_id; + count = gf_isom_get_track_count(file); + if (count==1) return; + ocr_id = 0; + for (i=0; iOCRESID = ocr_id; + gf_isom_change_mpeg4_description(file, i+1, 1, esd); + gf_odf_desc_del((GF_Descriptor *) esd); + } + } +} + +/*base RTP payload type used (you can specify your own types if needed)*/ +#define BASE_PAYT 96 + +GF_Err HintFile(GF_ISOFile *file, u32 MTUSize, u32 max_ptime, u32 rtp_rate, u32 base_flags, Bool copy_data, Bool interleave, Bool regular_iod) +{ + GF_ESD *esd; + GF_InitialObjectDescriptor *iod; + u32 i, val, res, streamType; + u32 sl_mode, prev_ocr, single_ocr, nb_done, tot_bw, bw, flags, spec_type; + GF_Err e; + char szPayload[30]; + GF_RTPHinter *hinter; + Bool copy, has_iod, single_av; + u8 init_payt = BASE_PAYT; + u32 iod_mode, mtype; + u32 media_group = 0; + u8 media_prio = 0; + + tot_bw = 0; + prev_ocr = 0; + single_ocr = 1; + + has_iod = 1; + iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(file); + if (!iod) has_iod = 0; + else { + if (!gf_list_count(iod->ESDescriptors)) has_iod = 0; + gf_odf_desc_del((GF_Descriptor *) iod); + } + + spec_type = gf_isom_guess_specification(file); + single_av = gf_isom_is_single_av(file); + + /*first make sure we use a systems track as base OCR*/ + for (i=0; idecoderConfig->streamType; + if (!prev_ocr) { + prev_ocr = esd->OCRESID; + if (!esd->OCRESID) prev_ocr = esd->ESID; + } else if (esd->OCRESID && prev_ocr != esd->OCRESID) { + single_ocr = 0; + } + /*OD MUST BE WITHOUT REFERENCES*/ + if (streamType==1) copy = 1; + } + gf_odf_desc_del((GF_Descriptor *) esd); + + if (!regular_iod && gf_isom_is_track_in_root_od(file, i+1)) { + /*single AU - check if base64 would fit in ESD (consider 33% overhead of base64), otherwise stream*/ + if (gf_isom_get_sample_count(file, i+1)==1) { + GF_ISOSample *samp = gf_isom_get_sample(file, i+1, 1, &val); + if (streamType) { + res = gf_hinter_can_embbed_data(samp->data, samp->dataLength, streamType); + } else { + /*not a system track, we shall hint it*/ + res = 0; + } + if (samp) gf_isom_sample_del(&samp); + if (res) continue; + } + } + if (interleave) sl_mode |= GP_RTP_PCK_USE_INTERLEAVING; + + hinter = gf_hinter_track_new(file, i+1, MTUSize, max_ptime, rtp_rate, sl_mode, init_payt, copy, media_group, media_prio, &e); + + if (!hinter) { + if (e) { + fprintf(stdout, "Cannot create hinter (%s)\n", gf_error_to_string(e)); + if (!nb_done) return e; + } + continue; + } + bw = gf_hinter_track_get_bandwidth(hinter); + tot_bw += bw; + flags = gf_hinter_track_get_flags(hinter); + gf_hinter_track_get_payload_name(hinter, szPayload); + fprintf(stdout, "Hinting track ID %d - Type \"%s:%s\" (%s) - BW %d kbps\n", gf_isom_get_track_id(file, i+1), gf_4cc_to_str(mtype), gf_4cc_to_str(mtype), szPayload, bw); + if (flags & GP_RTP_PCK_AUTO_CAROUSEL) fprintf(stdout, "\tMPEG-4 Systems stream carousel enabled\n"); +/* + if (flags & GP_RTP_PCK_FORCE_MPEG4) fprintf(stdout, "\tMPEG4 transport forced\n"); + if (flags & GP_RTP_PCK_USE_MULTI) fprintf(stdout, "\tRTP aggregation enabled\n"); +*/ + e = gf_hinter_track_process(hinter); + + if (!e) e = gf_hinter_track_finalize(hinter, has_iod); + gf_hinter_track_del(hinter); + + if (e) { + fprintf(stdout, "Error while hinting (%s)\n", gf_error_to_string(e)); + if (!nb_done) return e; + } + init_payt++; + nb_done ++; + } + + if (has_iod) { + iod_mode = GF_SDP_IOD_ISMA; + if (regular_iod) iod_mode = GF_SDP_IOD_REGULAR; + } else { + iod_mode = GF_SDP_IOD_NONE; + } + gf_hinter_finalize(file, iod_mode, tot_bw); + + if (!single_ocr) + fprintf(stdout, "Warning: at least 2 timelines found in the file\nThis may not be supported by servers/players\n\n"); + + return GF_OK; +} + + + +static void check_media_profile(GF_ISOFile *file, u32 track) +{ + u8 PL; + GF_M4ADecSpecInfo dsi; + GF_ESD *esd = gf_isom_get_esd(file, track, 1); + if (!esd) return; + + switch (esd->decoderConfig->streamType) { + case 0x04: + PL = gf_isom_get_pl_indication(file, GF_ISOM_PL_VISUAL); + if (esd->decoderConfig->objectTypeIndication==0x20) { + GF_M4VDecSpecInfo dsi; + gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + if (dsi.VideoPL > PL) gf_isom_set_pl_indication(file, GF_ISOM_PL_VISUAL, dsi.VideoPL); + } else if (esd->decoderConfig->objectTypeIndication==0x21) { + gf_isom_set_pl_indication(file, GF_ISOM_PL_VISUAL, 0x15); + } else if (!PL) { + gf_isom_set_pl_indication(file, GF_ISOM_PL_VISUAL, 0xFE); + } + break; + case 0x05: + PL = gf_isom_get_pl_indication(file, GF_ISOM_PL_AUDIO); + switch (esd->decoderConfig->objectTypeIndication) { + case 0x66: case 0x67: case 0x68: case 0x40: + gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + if (dsi.audioPL > PL) gf_isom_set_pl_indication(file, GF_ISOM_PL_AUDIO, dsi.audioPL); + break; + default: + if (!PL) gf_isom_set_pl_indication(file, GF_ISOM_PL_AUDIO, 0xFE); + } + break; + } + gf_odf_desc_del((GF_Descriptor *) esd); +} + +void remove_systems_tracks(GF_ISOFile *file) +{ + u32 i, count; + + count = gf_isom_get_track_count(file); + if (count==1) return; + + /*force PL rewrite*/ + gf_isom_set_pl_indication(file, GF_ISOM_PL_VISUAL, 0); + gf_isom_set_pl_indication(file, GF_ISOM_PL_AUDIO, 0); + gf_isom_set_pl_indication(file, GF_ISOM_PL_OD, 1); /*the lib always remove IOD when no profiles are specified..*/ + + for (i=0; imime_type[0] = 0; + meta->enc_type[0] = 0; + meta->szName[0] = 0; + meta->szPath[0] = 0; + meta->trackID = 0; + meta->root_meta = 1; + + if (!opts) return 0; + while (1) { + if (!opts || !opts[0]) return ret; + if (opts[0]==':') opts += 1; + strcpy(szSlot, opts); + next = strchr(szSlot, ':'); + /*use ':' as separator, but beware DOS paths...*/ + if (next && next[1]=='\\') next = strchr(szSlot+2, ':'); + if (next) next[0] = 0; + + if (!strnicmp(szSlot, "tk=", 3)) { + sscanf(szSlot, "tk=%d", &meta->trackID); + meta->root_meta = 0; + ret = 1; + } + else if (!strnicmp(szSlot, "name=", 5)) { strcpy(meta->szName, szSlot+5); ret = 1; } + else if (!strnicmp(szSlot, "path=", 5)) { strcpy(meta->szPath, szSlot+5); ret = 1; } + else if (!strnicmp(szSlot, "mime=", 5)) { strcpy(meta->mime_type, szSlot+5); ret = 1; } + else if (!strnicmp(szSlot, "encoding=", 9)) { strcpy(meta->enc_type, szSlot+9); ret = 1; } + else if (!strnicmp(szSlot, "dref", 4)) { meta->use_dref = 1; ret = 1; } + else if (!stricmp(szSlot, "binary")) { + if (meta->act_type==4) meta->act_type=5; + ret = 1; + } + else if (!strchr(szSlot, '=')) { + switch (meta->act_type) { + case 0: + if (!stricmp(szSlot, "null") || !stricmp(szSlot, "0")) meta->meta_4cc = 0; + else meta->meta_4cc = GF_4CC(szSlot[0], szSlot[1], szSlot[2], szSlot[3]); + ret = 1; + break; + case 1: + case 4: + case 7: + strcpy(meta->szPath, szSlot); + ret = 1; + break; + case 2: + case 3: + case 8: + meta->item_id = atoi(szSlot); + ret = 1; + break; + } + } + opts += strlen(szSlot); + } + return ret; +} +#endif + + + + +typedef struct +{ + /*0: set tsel param - 1 remove tsel - 2 remove all tsel info in alternate group - 3 remove all tsel info in file*/ + u32 act_type; + u32 trackID; + + u32 refTrackID; + u32 criteria[30]; + u32 nb_criteria; + Bool is_switchGroup; + u32 switchGroupID; +} TSELAction; + +static Bool parse_tsel_args(TSELAction *tsel_list, char *opts, u32 *nb_tsel_act) +{ + u32 act; + u32 refTrackID = 0; + Bool has_switch_id; + u32 switch_id = 0; + u32 criteria[30]; + u32 nb_criteria = 0; + TSELAction *tsel_act; + char szSlot[1024], *next; + + has_switch_id = 0; + act = tsel_list[*nb_tsel_act].act_type; + + + if (!opts) return 0; + while (1) { + if (!opts || !opts[0]) return 1; + if (opts[0]==':') opts += 1; + strcpy(szSlot, opts); + next = strchr(szSlot, ':'); + /*use ':' as separator, but beware DOS paths...*/ + if (next && next[1]=='\\') next = strchr(szSlot+2, ':'); + if (next) next[0] = 0; + + + if (!strnicmp(szSlot, "ref=", 4)) refTrackID = atoi(szSlot+4); + else if (!strnicmp(szSlot, "switchID=", 9)) { + if (atoi(szSlot+9)<0) { + switch_id = 0; + has_switch_id = 0; + } else { + switch_id = atoi(szSlot+9); + has_switch_id = 1; + } + } + else if (!strnicmp(szSlot, "switchID", 8)) { + switch_id = 0; + has_switch_id = 1; + } + else if (!strnicmp(szSlot, "criteria=", 9)) { + u32 j=9; + nb_criteria = 0; + while (j+3act_type = act; + tsel_act->trackID = strchr(szSlot, '=') ? atoi(szSlot+8) : atoi(szSlot); + tsel_act->refTrackID = refTrackID; + tsel_act->switchGroupID = switch_id; + tsel_act->is_switchGroup = has_switch_id; + tsel_act->nb_criteria = nb_criteria; + memcpy(tsel_act->criteria, criteria, sizeof(u32)*nb_criteria); + + if (!refTrackID) + refTrackID = tsel_act->trackID; + + (*nb_tsel_act) ++; + } + opts += strlen(szSlot); + } + return 1; +} + + +#define CHECK_NEXT_ARG if (i+1==(u32)argc) { fprintf(stdout, "Missing arg - please check usage\n"); return 1; } + +#define CHECK_META_OPS CHECK_NEXT_ARG if (nb_meta_act>=MAX_CUMUL_OPS) { fprintf(stdout, "Sorry - no more than %d meta operations allowed\n", MAX_CUMUL_OPS); return 1; } + + +typedef struct +{ + /* + 0: rem track + 1: set track language + 2: set track delay + 3: set track KMS URI + 4: set visual track PAR if possible + 5: set track handler name + 6: enables track + 7: disables track + 8: referenceTrack + */ + u32 act_type; + /*track ID*/ + u32 trackID; + char lang[4]; + s32 delay_ms; + const char *kms; + const char *hdl_name; + s32 par_num, par_den; +} TrackAction; + +enum +{ + GF_ISOM_CONV_TYPE_ISMA = 1, + GF_ISOM_CONV_TYPE_ISMA_EX, + GF_ISOM_CONV_TYPE_3GPP, + GF_ISOM_CONV_TYPE_IPOD, + GF_ISOM_CONV_TYPE_PSP +}; + +int main(int argc, char **argv) +{ + char outfile[5000]; + GF_Err e; + GF_SMEncodeOptions opts; + Double InterleavingTime, split_duration, split_start, import_fps; + SDPLine sdp_lines[MAX_CUMUL_OPS]; + MetaAction metas[MAX_CUMUL_OPS]; + char *szFilesToCat[MAX_CUMUL_OPS]; + char *szTracksToAdd[MAX_CUMUL_OPS]; + TrackAction tracks[MAX_CUMUL_OPS]; + TSELAction tsel_acts[MAX_CUMUL_OPS]; + u32 brand_add[MAX_CUMUL_OPS], brand_rem[MAX_CUMUL_OPS]; + u32 i, MTUSize, stat_level, hint_flags, info_track_id, import_flags, nb_add, nb_cat, ismaCrypt, agg_samples, nb_sdp_ex, max_ptime, raw_sample_num, split_size, nb_meta_act, nb_track_act, rtp_rate, major_brand, nb_alt_brand_add, nb_alt_brand_rem, old_interleave, car_dur, minor_version, conv_type, nb_tsel_acts; + Bool HintIt, needSave, FullInter, Frag, HintInter, dump_std, dump_rtp, dump_mode, regular_iod, trackID, HintCopy, remove_sys_tracks, remove_hint, force_new, keep_sys_tracks, remove_root_od, import_subtitle; + Bool print_sdp, print_info, open_edit, track_dump_type, dump_isom, dump_cr, force_ocr, encode, do_log, do_flat, dump_srt, dump_ttxt, x3d_info, chunk_mode, dump_ts, do_saf, dump_m2ts, dump_cart, do_hash, verbose, force_cat; + char *inName, *outName, *arg, *mediaSource, *tmpdir, *input_ctx, *output_ctx, *drm_file, *avi2raw, *cprt, *chap_file, *pes_dump, *itunes_tags, *pack_file, *raw_cat; + GF_ISOFile *file; + + + if (argc < 2) { + PrintUsage(); + return 1; + } + + nb_tsel_acts = nb_add = nb_cat = nb_track_act = nb_sdp_ex = max_ptime = raw_sample_num = nb_meta_act = rtp_rate = major_brand = nb_alt_brand_add = nb_alt_brand_rem = car_dur = minor_version = 0; + e = GF_OK; + split_duration = 0.0; + split_start = -1.0; + InterleavingTime = 0.5; + import_fps = 0; + import_flags = 0; + split_size = 0; + MTUSize = 1450; + HintCopy = FullInter = HintInter = encode = do_log = old_interleave = do_saf = do_hash = verbose = 0; + chunk_mode = dump_mode = Frag = force_ocr = remove_sys_tracks = agg_samples = remove_hint = keep_sys_tracks = remove_root_od = 0; + x3d_info = conv_type = HintIt = needSave = print_sdp = print_info = regular_iod = dump_std = open_edit = dump_isom = dump_rtp = dump_cr = dump_srt = dump_ttxt = force_new = dump_ts = dump_m2ts = dump_cart = import_subtitle = force_cat = 0; + track_dump_type = 0; + ismaCrypt = 0; + file = NULL; + itunes_tags = pes_dump = NULL; + memset(&opts, 0, sizeof(opts)); + + trackID = stat_level = hint_flags = 0; + info_track_id = 0; + do_flat = 0; + inName = outName = mediaSource = input_ctx = output_ctx = drm_file = avi2raw = cprt = chap_file = pack_file = raw_cat = NULL; + + swf_flags = GF_SM_SWF_SPLIT_TIMELINE; + swf_flatten_angle = 0.0f; + tmpdir = NULL; + + /*parse our args*/ + for (i = 1; i < (u32) argc ; i++) { + arg = argv[i]; + /*main file*/ +// if (isalnum(arg[0]) || (arg[0]=='/') || (arg[0]=='.') || (arg[0]=='\\') ) { + if (arg[0] != '-') { + if (inName) { fprintf(stdout, "Error - 2 input names specified, please check usage\n"); return 1; } + inName = arg; + } + else if (!stricmp(arg, "-?")) { PrintUsage(); return 0; } + else if (!stricmp(arg, "-version")) { PrintVersion(); return 0; } + else if (!stricmp(arg, "-sdp")) print_sdp = 1; + else if (!stricmp(arg, "-quiet")) quiet = 1; + else if (!stricmp(arg, "-info")) { + print_info = 1; + if ((i+1<(u32) argc) && (sscanf(argv[i+1], "%d", &info_track_id)==1)) { + char szTk[20]; + sprintf(szTk, "%d", info_track_id); + if (!strcmp(szTk, argv[i+1])) i++; + else info_track_id=0; + } else { + info_track_id=0; + } + } + else if (!stricmp(arg, "-raw")) { + CHECK_NEXT_ARG + track_dump_type = GF_EXPORT_NATIVE; + trackID = atoi(argv[i+1]); + i++; + } + else if (!stricmp(arg, "-qcp")) { + CHECK_NEXT_ARG + track_dump_type = GF_EXPORT_NATIVE | GF_EXPORT_USE_QCP; + trackID = atoi(argv[i+1]); + i++; + } + else if (!stricmp(arg, "-aviraw")) { + CHECK_NEXT_ARG + if (argv[i+1] && !stricmp(argv[i+1], "video")) trackID = 1; + else if (argv[i+1] && !stricmp(argv[i+1], "audio")) { + if (strlen(argv[i+1])==5) trackID = 2; + else trackID = 1 + atoi(argv[i+1] + 5); + } + else { fprintf(stdout, "Usage: \"-aviraw video\" or \"-aviraw audio\"\n"); return 1; } + track_dump_type = GF_EXPORT_AVI_NATIVE; + i++; + } + else if (!stricmp(arg, "-raws")) { + CHECK_NEXT_ARG + track_dump_type = GF_EXPORT_RAW_SAMPLES; + if (strchr(argv[i+1], ':')) { + sscanf(argv[i+1], "%d:%d", &trackID, &raw_sample_num); + } else { + trackID = atoi(argv[i+1]); + } + i++; + } + else if (!stricmp(arg, "-nhnt")) { + CHECK_NEXT_ARG + track_dump_type = GF_EXPORT_NHNT; + trackID = atoi(argv[i+1]); + i++; + } + else if (!stricmp(arg, "-nhml")) { + CHECK_NEXT_ARG + track_dump_type = GF_EXPORT_NHML; + if (argv[i+1][0]=='+') { + track_dump_type |= GF_EXPORT_NHML_FULL; + trackID = atoi(argv[i+1] + 1); + } else { + trackID = atoi(argv[i+1]); + } + i++; + } + else if (!stricmp(arg, "-avi")) { + CHECK_NEXT_ARG + track_dump_type = GF_EXPORT_AVI; + trackID = atoi(argv[i+1]); + i++; + } + else if (!stricmp(arg, "-node")) { CHECK_NEXT_ARG PrintNode(argv[i+1], 0); return (0); } + else if (!stricmp(arg, "-xnode")) { CHECK_NEXT_ARG PrintNode(argv[i+1], 1); return (0); } + else if (!stricmp(arg, "-snode")) { CHECK_NEXT_ARG PrintNode(argv[i+1], 2); return (0); } + else if (!stricmp(arg, "-nodes")) { PrintBuiltInNodes(0); return (0); } + else if (!stricmp(arg, "-xnodes")) { PrintBuiltInNodes(1); return (0); } + else if (!stricmp(arg, "-snodes")) { PrintBuiltInNodes(2); return (0); } + else if (!stricmp(arg, "-std")) dump_std = 1; + else if (!stricmp(arg, "-bt")) dump_mode = 1 + GF_SM_DUMP_BT; + else if (!stricmp(arg, "-xmt")) dump_mode = 1 + GF_SM_DUMP_XMTA; + else if (!stricmp(arg, "-wrl")) dump_mode = 1 + GF_SM_DUMP_VRML; + else if (!stricmp(arg, "-x3dv")) dump_mode = 1 + GF_SM_DUMP_X3D_VRML; + else if (!stricmp(arg, "-x3d")) dump_mode = 1 + GF_SM_DUMP_X3D_XML; + else if (!stricmp(arg, "-lsr")) dump_mode = 1 + GF_SM_DUMP_LASER; + else if (!stricmp(arg, "-svg")) dump_mode = 1 + GF_SM_DUMP_SVG; + else if (!stricmp(arg, "-stat")) stat_level = 1; + else if (!stricmp(arg, "-stats")) stat_level = 2; + else if (!stricmp(arg, "-statx")) stat_level = 3; + else if (!stricmp(arg, "-diso")) dump_isom = 1; + else if (!stricmp(arg, "-dump-cover")) dump_cart = 1; + else if (!stricmp(arg, "-hash")) do_hash = 1; + + else if (!stricmp(arg, "-dmp4")) { + dump_isom = 1; + fprintf(stdout, "WARNING: \"-dmp4\" is deprecated - use \"-diso\" option\n"); + } + else if (!stricmp(arg, "-drtp")) dump_rtp = 1; + else if (!stricmp(arg, "-dts")) dump_ts = 1; + else if (!stricmp(arg, "-dcr")) dump_cr = 1; + else if (!stricmp(arg, "-ttxt") || !stricmp(arg, "-srt")) { + if ((i+1<(u32) argc) && (sscanf(argv[i+1], "%d", &trackID)==1)) { + char szTk[20]; + sprintf(szTk, "%d", trackID); + if (!strcmp(szTk, argv[i+1])) i++; + else trackID=0; + } else { + trackID = 0; + } +#ifdef GPAC_READ_ONLY + if (trackID) { fprintf(stdout, "Error: Read-Only version - subtitle conversion not available\n"); return 1; } +#endif + if (!stricmp(arg, "-ttxt")) dump_ttxt = 1; + else dump_srt = 1; + import_subtitle = 1; + } else if (!stricmp(arg, "-dm2ts")) { + dump_m2ts = 1; + if ( ((i+1<(u32) argc) && inName) || (i+2<(u32) argc) ) { + if (argv[i+1][0] != '-') pes_dump = argv[i+1]; + i++; + } + } + +#ifndef GPAC_READ_ONLY + /*SWF importer options*/ + else if (!stricmp(arg, "-global")) swf_flags |= GF_SM_SWF_STATIC_DICT; + else if (!stricmp(arg, "-no-ctrl")) swf_flags &= ~GF_SM_SWF_SPLIT_TIMELINE; + else if (!stricmp(arg, "-no-text")) swf_flags |= GF_SM_SWF_NO_TEXT; + else if (!stricmp(arg, "-no-font")) swf_flags |= GF_SM_SWF_NO_FONT; + else if (!stricmp(arg, "-no-line")) swf_flags |= GF_SM_SWF_NO_LINE; + else if (!stricmp(arg, "-no-grad")) swf_flags |= GF_SM_SWF_NO_GRADIENT; + else if (!stricmp(arg, "-quad")) swf_flags |= GF_SM_SWF_QUAD_CURVE; + else if (!stricmp(arg, "-xlp")) swf_flags |= GF_SM_SWF_SCALABLE_LINE; + else if (!stricmp(arg, "-ic2d")) swf_flags |= GF_SM_SWF_USE_IC2D; + else if (!stricmp(arg, "-same-app")) swf_flags |= GF_SM_SWF_REUSE_APPEARANCE; + else if (!stricmp(arg, "-flatten")) { + CHECK_NEXT_ARG + swf_flatten_angle = (Float) atof(argv[i+1]); + i++; + } + else if (!stricmp(arg, "-isma")) { conv_type = GF_ISOM_CONV_TYPE_ISMA; open_edit = 1; } + else if (!stricmp(arg, "-3gp")) { conv_type = GF_ISOM_CONV_TYPE_3GPP; open_edit = 1; } + else if (!stricmp(arg, "-ipod")) { conv_type = GF_ISOM_CONV_TYPE_IPOD; open_edit = 1; } + else if (!stricmp(arg, "-ismax")) { conv_type = GF_ISOM_CONV_TYPE_ISMA_EX; open_edit = 1; } + + else if (!stricmp(arg, "-no-sys") || !stricmp(arg, "-nosys")) { remove_sys_tracks = 1; open_edit = 1; } + else if (!stricmp(arg, "-no-iod")) { remove_root_od = 1; open_edit = 1; } + else if (!stricmp(arg, "-out")) { CHECK_NEXT_ARG outName = argv[i+1]; i++; } + else if (!stricmp(arg, "-tmp")) { + CHECK_NEXT_ARG tmpdir = argv[i+1]; i++; + } + else if (!stricmp(arg, "-cprt")) { CHECK_NEXT_ARG cprt = argv[i+1]; i++; open_edit = 1; } + else if (!stricmp(arg, "-chap")) { CHECK_NEXT_ARG chap_file = argv[i+1]; i++; open_edit = 1; } + else if (!stricmp(arg, "-inter") || !stricmp(arg, "-old-inter")) { + CHECK_NEXT_ARG + InterleavingTime = atof(argv[i+1]) / 1000; + open_edit = 1; + needSave = 1; + if (!stricmp(arg, "-old-inter")) old_interleave = 1; + i++; + } else if (!stricmp(arg, "-frag")) { + CHECK_NEXT_ARG + InterleavingTime = atof(argv[i+1]) / 1000; + open_edit = 1; + needSave = 1; + i++; + Frag = 1; + } + else if (!stricmp(arg, "-itags")) { CHECK_NEXT_ARG itunes_tags = argv[i+1]; i++; open_edit = 1; } + else if (!stricmp(arg, "-hint")) { open_edit = 1; HintIt = 1; } + else if (!stricmp(arg, "-unhint")) { open_edit = 1; remove_hint = 1; } + else if (!stricmp(arg, "-copy")) HintCopy = 1; + else if (!stricmp(arg, "-tight")) { + FullInter = 1; + open_edit = 1; + needSave = 1; + } else if (!stricmp(arg, "-ocr")) force_ocr = 1; + else if (!stricmp(arg, "-latm")) hint_flags |= GP_RTP_PCK_USE_LATM_AAC; + else if (!stricmp(arg, "-rap")) hint_flags |= GP_RTP_PCK_SIGNAL_RAP; + else if (!stricmp(arg, "-ts")) hint_flags |= GP_RTP_PCK_SIGNAL_TS; + else if (!stricmp(arg, "-size")) hint_flags |= GP_RTP_PCK_SIGNAL_SIZE; + else if (!stricmp(arg, "-idx")) hint_flags |= GP_RTP_PCK_SIGNAL_AU_IDX; + else if (!stricmp(arg, "-static")) hint_flags |= GP_RTP_PCK_USE_STATIC_ID; + else if (!stricmp(arg, "-multi")) { + hint_flags |= GP_RTP_PCK_USE_MULTI; + if ((i+1<(u32) argc) && (sscanf(argv[i+1], "%d", &max_ptime)==1)) { + char szPt[20]; + sprintf(szPt, "%d", max_ptime); + if (!strcmp(szPt, argv[i+1])) i++; + else max_ptime=0; + } + } + else if (!stricmp(arg, "-mpeg4")) { + hint_flags |= GP_RTP_PCK_FORCE_MPEG4; + import_flags |= GF_IMPORT_FORCE_MPEG4; + } + else if (!stricmp(arg, "-mtu")) { CHECK_NEXT_ARG MTUSize = atoi(argv[i+1]); i++; } + else if (!stricmp(arg, "-cardur")) { CHECK_NEXT_ARG car_dur = atoi(argv[i+1]); i++; } + else if (!stricmp(arg, "-rate")) { CHECK_NEXT_ARG rtp_rate = atoi(argv[i+1]); i++; } + else if (!stricmp(arg, "-add-sdp") || !stricmp(arg, "-sdp_ex")) { + char *id; + CHECK_NEXT_ARG + if (nb_sdp_ex>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d extra SDP lines allowed\n", MAX_CUMUL_OPS); + return 1; + } + id = strchr(argv[i+1], ':'); + if (id) { + id[0] = 0; + if (sscanf(argv[i+1], "%d", &sdp_lines[0].trackID)==1) { + id[0] = ':'; + sdp_lines[nb_sdp_ex].line = id+1; + } else { + id[0] = ':'; + sdp_lines[nb_sdp_ex].line = argv[i+1]; + sdp_lines[nb_sdp_ex].trackID = 0; + } + } else { + sdp_lines[nb_sdp_ex].line = argv[i+1]; + sdp_lines[nb_sdp_ex].trackID = 0; + } + open_edit = 1; + nb_sdp_ex++; + i++; + } + else if (!stricmp(arg, "-single")) { + CHECK_NEXT_ARG + track_dump_type = GF_EXPORT_MP4; + trackID = atoi(argv[i+1]); + i++; + } + else if (!stricmp(arg, "-iod")) regular_iod = 1; + else if (!stricmp(arg, "-flat")) do_flat = 1; + else if (!stricmp(arg, "-new")) force_new = 1; + else if (!stricmp(arg, "-add") || !stricmp(arg, "-import") || !stricmp(arg, "-convert")) { + CHECK_NEXT_ARG + if (!stricmp(arg, "-import")) fprintf(stdout, "\tWARNING: \"-import\" is deprecated - use \"-add\"\n"); + else if (!stricmp(arg, "-convert")) fprintf(stdout, "\tWARNING: \"-convert\" is deprecated - use \"-add\"\n"); + if (nb_add>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d add operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + szTracksToAdd[nb_add] = argv[i+1]; + nb_add++; + i++; + } + else if (!stricmp(arg, "-cat")) { + CHECK_NEXT_ARG + if (nb_cat>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d cat operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + szFilesToCat[nb_cat] = argv[i+1]; + nb_cat++; + i++; + } + else if (!stricmp(arg, "-force-cat")) force_cat = 1; + else if (!stricmp(arg, "-raw-cat")) { + CHECK_NEXT_ARG + raw_cat = argv[i+1]; + i++; + } + else if (!stricmp(arg, "-rem") || !stricmp(arg, "-disable") || !stricmp(arg, "-enable")) { + CHECK_NEXT_ARG + if (nb_track_act>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d track operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + if (!stricmp(arg, "-enable")) tracks[nb_track_act].act_type = 6; + else if (!stricmp(arg, "-disable")) tracks[nb_track_act].act_type = 7; + else tracks[nb_track_act].act_type = 0; + tracks[nb_track_act].trackID = atoi(argv[i+1]); + open_edit = 1; + nb_track_act++; + i++; + } + else if (!stricmp(arg, "-par")) { + char szTK[20], *ext; + CHECK_NEXT_ARG + if (nb_track_act>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d track operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + tracks[nb_track_act].act_type = 4; + strcpy(szTK, argv[i+1]); + ext = strchr(szTK, '='); + if (!ext) { + fprintf(stdout, "Bad format for track par - expecting ID=PAR_NUM:PAR_DEN got %s\n", argv[i+1]); + return 1; + } + if (!stricmp(ext+1, "none")) { + tracks[nb_track_act].par_num = tracks[nb_track_act].par_den = -1; + } else { + sscanf(ext+1, "%d:%d", &tracks[nb_track_act].par_num, &tracks[nb_track_act].par_den); + } + ext[0] = 0; + tracks[nb_track_act].trackID = atoi(szTK); + open_edit = 1; + nb_track_act++; + i++; + } + else if (!stricmp(arg, "-lang")) { + char szTK[20], *ext; + CHECK_NEXT_ARG + if (nb_track_act>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d track operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + tracks[nb_track_act].act_type = 1; + tracks[nb_track_act].lang[3] = 0; + tracks[nb_track_act].trackID = 0; + strcpy(szTK, argv[i+1]); + ext = strchr(szTK, '='); + if (!strnicmp(argv[i+1], "all=", 4)) { + strncpy(tracks[nb_track_act].lang, argv[i+1]+1, 3); + } else if (!ext) { + strncpy(tracks[nb_track_act].lang, argv[i+1], 3); + } else { + strncpy(tracks[nb_track_act].lang, ext+1, 3); + ext[0] = 0; + tracks[nb_track_act].trackID = atoi(szTK); + ext[0] = '='; + } + open_edit = 1; + nb_track_act++; + i++; + } + else if (!stricmp(arg, "-delay")) { + char szTK[20], *ext; + CHECK_NEXT_ARG + if (nb_track_act>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d track operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + strcpy(szTK, argv[i+1]); + ext = strchr(szTK, '='); + if (!ext) { + fprintf(stdout, "Bad format for track delay - expecting ID=DLAY got %s\n", argv[i+1]); + return 1; + } + tracks[nb_track_act].act_type = 2; + tracks[nb_track_act].delay_ms = atoi(ext+1); + ext[0] = 0; + tracks[nb_track_act].trackID = atoi(szTK); + open_edit = 1; + nb_track_act++; + i++; + } + else if (!stricmp(arg, "-ref")) { + char *szTK, *ext; + CHECK_NEXT_ARG + if (nb_track_act>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d track operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + szTK = argv[i+1]; + ext = strchr(szTK, ':'); + if (!ext) { + fprintf(stdout, "Bad format for track reference - expecting ID:XXXX:refID got %s\n", argv[i+1]); + return 1; + } + tracks[nb_track_act].act_type = 8; + ext[0] = 0; tracks[nb_track_act].trackID = atoi(szTK); ext[0] = ':'; szTK = ext+1; + ext = strchr(szTK, ':'); + if (!ext) { + fprintf(stdout, "Bad format for track reference - expecting ID:XXXX:refID got %s\n", argv[i+1]); + return 1; + } + ext[0] = 0; + strncpy(tracks[nb_track_act].lang, szTK, 4); + ext[0] = ':'; + tracks[nb_track_act].delay_ms = (s32) atoi(ext+1); + open_edit = 1; + nb_track_act++; + i++; + } + else if (!stricmp(arg, "-name")) { + char szTK[GF_MAX_PATH], *ext; + CHECK_NEXT_ARG + if (nb_track_act>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d track operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + strcpy(szTK, argv[i+1]); + ext = strchr(szTK, '='); + if (!ext) { + fprintf(stdout, "Bad format for track delay - expecting ID=DLAY got %s\n", argv[i+1]); + return 1; + } + tracks[nb_track_act].act_type = 5; + tracks[nb_track_act].hdl_name = ext+1; + ext[0] = 0; + tracks[nb_track_act].trackID = atoi(szTK); + ext[0] = '='; + open_edit = 1; + nb_track_act++; + i++; + } + else if (!stricmp(arg, "-dref")) import_flags |= GF_IMPORT_USE_DATAREF; + else if (!stricmp(arg, "-no-drop") || !stricmp(arg, "-nodrop")) import_flags |= GF_IMPORT_NO_FRAME_DROP; + else if (!stricmp(arg, "-packed")) import_flags |= GF_IMPORT_FORCE_PACKED; + else if (!stricmp(arg, "-sbr")) import_flags |= GF_IMPORT_SBR_IMPLICIT; + else if (!stricmp(arg, "-sbrx")) import_flags |= GF_IMPORT_SBR_EXPLICIT; + else if (!stricmp(arg, "-fps")) { + CHECK_NEXT_ARG + if (!strcmp(argv[i+1], "auto")) import_fps = 10000.0; + else import_fps = atof(argv[i+1]); + i++; + } + else if (!stricmp(arg, "-agg")) { CHECK_NEXT_ARG agg_samples = atoi(argv[i+1]); i++; } + else if (!stricmp(arg, "-keep-sys") || !stricmp(arg, "-keepsys")) keep_sys_tracks = 1; + else if (!stricmp(arg, "-keep-all") || !stricmp(arg, "-keepall")) import_flags |= GF_IMPORT_KEEP_ALL_TRACKS; + else if (!stricmp(arg, "-ms")) { CHECK_NEXT_ARG mediaSource = argv[i+1]; i++; } + else if (!stricmp(arg, "-mp4")) { encode = 1; open_edit = 1; } + else if (!stricmp(arg, "-saf")) { do_saf = 1; } + else if (!stricmp(arg, "-log")) do_log = 1; + else if (!stricmp(arg, "-def")) opts.flags |= GF_SM_ENCODE_USE_NAMES; + else if (!stricmp(arg, "-sync")) { + CHECK_NEXT_ARG + opts.flags |= GF_SM_ENCODE_RAP_INBAND; + opts.rap_freq = atoi(argv[i+1]); + i++; + } else if (!stricmp(arg, "-shadow")) { + CHECK_NEXT_ARG + opts.flags &= ~GF_SM_ENCODE_RAP_INBAND; + opts.flags |= GF_SM_ENCODE_RAP_SHADOW; + opts.rap_freq = atoi(argv[i+1]); + i++; + } else if (!stricmp(arg, "-carousel")) { + CHECK_NEXT_ARG + opts.flags &= ~(GF_SM_ENCODE_RAP_INBAND | GF_SM_ENCODE_RAP_SHADOW); + opts.rap_freq = atoi(argv[i+1]); + i++; + } + /*LASeR options*/ + else if (!stricmp(arg, "-resolution")) { + CHECK_NEXT_ARG + opts.resolution = atoi(argv[i+1]); + i++; + } + else if (!stricmp(arg, "-auto-quant")) { + CHECK_NEXT_ARG + opts.resolution = atoi(argv[i+1]); + opts.auto_quant = 1; + i++; + } + else if (!stricmp(arg, "-coord-bits")) { + CHECK_NEXT_ARG + opts.coord_bits = atoi(argv[i+1]); + i++; + } + else if (!stricmp(arg, "-scale-bits")) { + CHECK_NEXT_ARG + opts.scale_bits = atoi(argv[i+1]); + i++; + } + else if (!stricmp(arg, "-global-quant")) { + CHECK_NEXT_ARG + opts.resolution = atoi(argv[i+1]); + opts.auto_quant = 2; + i++; + } + /*chunk encoding*/ + else if (!stricmp(arg, "-ctx-out") || !stricmp(arg, "-outctx")) { CHECK_NEXT_ARG output_ctx = argv[i+1]; i++; } + else if (!stricmp(arg, "-ctx-in") || !stricmp(arg, "-inctx")) { + CHECK_NEXT_ARG + chunk_mode = 1; + input_ctx = argv[i+1]; + i++; + } + else if (!strcmp(arg, "-crypt")) { + CHECK_NEXT_ARG + ismaCrypt = 1; + drm_file = argv[i+1]; + open_edit = 1; + i += 1; + } + else if (!strcmp(arg, "-decrypt")) { + CHECK_NEXT_ARG + ismaCrypt = 2; + if (get_file_type_by_ext(argv[i+1])!=1) { + drm_file = argv[i+1]; + i += 1; + } + open_edit = 1; + } + else if (!stricmp(arg, "-set-kms")) { + char szTK[20], *ext; + CHECK_NEXT_ARG + if (nb_track_act>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d track operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + strncpy(szTK, argv[i+1], 19); + ext = strchr(szTK, '='); + tracks[nb_track_act].act_type = 3; + tracks[nb_track_act].trackID = 0; + if (!strnicmp(argv[i+1], "all=", 4)) { + tracks[nb_track_act].kms = argv[i+1] + 4; + } else if (!ext) { + tracks[nb_track_act].kms = argv[i+1]; + } else { + tracks[nb_track_act].kms = ext+1; + ext[0] = 0; + tracks[nb_track_act].trackID = atoi(szTK); + ext[0] = '='; + } + open_edit = 1; + nb_track_act++; + i++; + } + else if (!stricmp(arg, "-split")) { CHECK_NEXT_ARG split_duration = atof(argv[i+1]); i++; split_size = 0; } + else if (!stricmp(arg, "-split-size") || !stricmp(arg, "-splits")) { CHECK_NEXT_ARG split_size = atoi(argv[i+1]); i++; split_duration = 0; } + else if (!stricmp(arg, "-split-chunk") || !stricmp(arg, "-splitx")) { + CHECK_NEXT_ARG + if (!strstr(argv[i+1], ":")) { + fprintf(stdout, "Chunk extraction usage: \"-splitx start->end\" expressed in seconds\n"); + return 1; + } + sscanf(argv[i+1], "%lf:%lf", &split_start, &split_duration); + split_duration -= split_start; + split_size = 0; + i++; + } + /*meta*/ + else if (!stricmp(arg, "-set-meta")) { + CHECK_META_OPS + metas[nb_meta_act].act_type = 0; + parse_meta_args(&metas[nb_meta_act], argv[i+1]); + nb_meta_act++; + open_edit = 1; + i++; + } + else if (!stricmp(arg, "-add-item")) { + CHECK_META_OPS + metas[nb_meta_act].act_type = 1; + parse_meta_args(&metas[nb_meta_act], argv[i+1]); + nb_meta_act++; + open_edit = 1; + i++; + } + else if (!stricmp(arg, "-rem-item")) { + CHECK_META_OPS + metas[nb_meta_act].act_type = 2; + parse_meta_args(&metas[nb_meta_act], argv[i+1]); + nb_meta_act++; + open_edit = 1; + i++; + } + else if (!stricmp(arg, "-set-primary")) { + CHECK_META_OPS + metas[nb_meta_act].act_type = 3; + parse_meta_args(&metas[nb_meta_act], argv[i+1]); + nb_meta_act++; + open_edit = 1; + i++; + } + else if (!stricmp(arg, "-set-xml")) { + CHECK_META_OPS + metas[nb_meta_act].act_type = 4; + parse_meta_args(&metas[nb_meta_act], argv[i+1]); + nb_meta_act++; + open_edit = 1; + i++; + } + else if (!stricmp(arg, "-rem-xml")) { + CHECK_META_OPS + metas[nb_meta_act].act_type = 6; + if (parse_meta_args(&metas[nb_meta_act], argv[i+1])) i++; + nb_meta_act++; + open_edit = 1; + } + else if (!stricmp(arg, "-dump-xml")) { + CHECK_META_OPS + metas[nb_meta_act].act_type = 7; + parse_meta_args(&metas[nb_meta_act], argv[i+1]); + nb_meta_act++; + i++; + } + else if (!stricmp(arg, "-dump-item")) { + CHECK_META_OPS + metas[nb_meta_act].act_type = 8; + parse_meta_args(&metas[nb_meta_act], argv[i+1]); + nb_meta_act++; + i++; + } + else if (!stricmp(arg, "-group-add") || !stricmp(arg, "-group-rem-track") || !stricmp(arg, "-group-rem")) { + tsel_acts[nb_tsel_acts].act_type = !stricmp(arg, "-group-rem") ? 2 : ( !stricmp(arg, "-group-rem-track") ? 1 : 0 ); + if (parse_tsel_args(tsel_acts, argv[i+1], &nb_tsel_acts)==0) { + fprintf(stdout, "Invalid group syntax - check usage\n"); + return 1; + } + open_edit=1; + i++; + } + else if (!stricmp(arg, "-group-clean")) { + tsel_acts[nb_tsel_acts].act_type = 3; + nb_tsel_acts++; + open_edit=1; + } + + else if (!stricmp(arg, "-package")) { + CHECK_NEXT_ARG + pack_file = argv[i+1]; + i++; + } + + else if (!stricmp(arg, "-brand")) { + char *b = argv[i+1]; + CHECK_NEXT_ARG + major_brand = GF_4CC(b[0], b[1], b[2], b[3]); + open_edit = 1; + if (b[4]==':') minor_version = atoi(b+5); + i++; + } + else if (!stricmp(arg, "-ab")) { + char *b = argv[i+1]; + CHECK_NEXT_ARG + if (nb_alt_brand_add>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d brand remove operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + brand_add[nb_alt_brand_add] = GF_4CC(b[0], b[1], b[2], b[3]); + nb_alt_brand_add++; + open_edit = 1; + i++; + } + else if (!stricmp(arg, "-rb")) { + char *b = argv[i+1]; + CHECK_NEXT_ARG + if (nb_alt_brand_rem>=MAX_CUMUL_OPS) { + fprintf(stdout, "Sorry - no more than %d brand remove operations allowed\n", MAX_CUMUL_OPS); + return 1; + } + brand_rem[nb_alt_brand_rem] = GF_4CC(b[0], b[1], b[2], b[3]); + nb_alt_brand_rem++; + open_edit = 1; + i++; + } +#endif + else if (!stricmp(arg, "-languages")) { + PrintLanguages(); + return 0; + } + else if (!stricmp(arg, "-h")) { + if (i+1== (u32) argc) PrintUsage(); + else if (!strcmp(argv[i+1], "extract")) PrintExtractUsage(); + else if (!strcmp(argv[i+1], "dump")) PrintDumpUsage(); + else if (!strcmp(argv[i+1], "swf")) PrintSWFUsage(); +#ifndef GPAC_READ_ONLY + else if (!strcmp(argv[i+1], "general")) PrintGeneralUsage(); + else if (!strcmp(argv[i+1], "hint")) PrintHintUsage(); + else if (!strcmp(argv[i+1], "import")) PrintImportUsage(); + else if (!strcmp(argv[i+1], "format")) PrintFormats(); + else if (!strcmp(argv[i+1], "encode")) PrintEncodeUsage(); + else if (!strcmp(argv[i+1], "crypt")) PrintEncryptUsage(); + else if (!strcmp(argv[i+1], "meta")) PrintMetaUsage(); +#endif + else if (!strcmp(argv[i+1], "all")) { +#ifndef GPAC_READ_ONLY + PrintGeneralUsage(); + PrintHintUsage(); + PrintImportUsage(); + PrintFormats(); + PrintEncodeUsage(); + PrintEncryptUsage(); + PrintMetaUsage(); +#endif + PrintExtractUsage(); + PrintDumpUsage(); + PrintSWFUsage(); + } + else PrintUsage(); + return 0; + } + else if (!stricmp(arg, "-v")) verbose++; + else if (!stricmp(arg, "-tag-list")) { + fprintf(stdout, "Supported iTunes tag modifiers:\n"); + for (i=0; i= to_copy) break; + } + fclose(fin); + fclose(fout); + return 0; + } + +// gf_log_set_level((verbose>1) ? GF_LOG_DEBUG : (verbose ? GF_LOG_INFO : GF_LOG_WARNING) ); + gf_log_set_level(verbose ? GF_LOG_DEBUG : GF_LOG_INFO); + gf_log_set_tools(GF_LOG_CONTAINER|GF_LOG_SCENE|GF_LOG_PARSER|GF_LOG_AUTHOR|GF_LOG_CODING); + if (quiet) { + gf_log_set_level(0); + gf_set_progress_callback(NULL, progress_quiet); + + } + /*init libgpac*/ + gf_sys_init(); + + + if (do_saf && !encode) { + switch (get_file_type_by_ext(inName)) { + case 2: case 3: case 4: + encode = 1; + break; + } + } + + if (dump_mode == 1 + GF_SM_DUMP_SVG) { + if (strstr(inName, ".srt") || strstr(inName, ".ttxt")) import_subtitle = 2; + } + + if (import_subtitle && !trackID) { +#ifndef GPAC_READ_ONLY + GF_MediaImporter import; + file = gf_isom_open("ttxt_convert", GF_ISOM_OPEN_WRITE, NULL); + memset(&import, 0, sizeof(GF_MediaImporter)); + import.dest = file; + import.in_name = inName; + e = gf_media_import(&import); + if (e) { + fprintf(stdout, "Error importing %s: %s\n", inName, gf_error_to_string(e)); + gf_isom_delete(file); + gf_delete_file("ttxt_convert"); + return 1; + } + strcpy(outfile, outName ? outName : inName); + if (strchr(outfile, '.')) { + while (outfile[strlen(outfile)-1] != '.') outfile[strlen(outfile)-1] = 0; + outfile[strlen(outfile)-1] = 0; + } + dump_timed_text_track(file, gf_isom_get_track_id(file, 1), dump_std ? NULL : outfile, 1, (import_subtitle==2) ? 2 : dump_srt); + gf_isom_delete(file); + gf_delete_file("ttxt_convert"); + if (e) { + fprintf(stdout, "Error converting %s: %s\n", inName, gf_error_to_string(e)); + return 1; + } + return 0; +#else + fprintf(stdout, "Error: read-only version\n"); + return 1; +#endif + } +#ifndef GPAC_READ_ONLY + if (nb_add) { + u8 open_mode = GF_ISOM_OPEN_EDIT; + if (force_new) { + open_mode = (do_flat) ? GF_ISOM_OPEN_WRITE : GF_ISOM_WRITE_EDIT; + } else { + FILE *test = fopen(inName, "rb"); + if (!test) { + open_mode = (do_flat) ? GF_ISOM_OPEN_WRITE : GF_ISOM_WRITE_EDIT; + if (!outName) outName = inName; + } + else fclose(test); + } + + open_edit = 1; + file = gf_isom_open(inName, open_mode, tmpdir); + if (!file) { + fprintf(stdout, "Cannot open destination file %s: %s\n", inName, gf_error_to_string(gf_isom_last_error(NULL)) ); + return 1; + } + for (i=0; i0) ? GF_ISOM_OPEN_READ_DUMP : GF_ISOM_OPEN_READ) ), tmpdir); + if (!file) { + if (open_edit && nb_meta_act) { + file = gf_isom_open(inName, GF_ISOM_WRITE_EDIT, tmpdir); + if (!outName && file) outName = inName; + } + + if (!file) { + fprintf(stdout, "Error opening file %s: %s\n", inName, gf_error_to_string(gf_isom_last_error(NULL))); + return 1; + } + } + break; + /*allowed for bt<->xmt*/ + case 2: + case 3: + /*allowed for svg->lsr**/ + case 4: + /*allowed for swf->bt, swf->xmt*/ + case 5: + break; + /*used for .saf / .lsr dump*/ + case 6: + if ((dump_mode==1+GF_SM_DUMP_LASER) || (dump_mode==1+GF_SM_DUMP_SVG)) { + break; + } + + default: + if (!open_edit && file_exists && !gf_isom_probe_file(inName) && track_dump_type) { + } +#ifndef GPAC_READ_ONLY + else if (!open_edit && file_exists /* && !gf_isom_probe_file(inName) */ && !dump_mode) { + if (dump_m2ts) { + dump_mpeg2_ts(inName, pes_dump); + } else { + convert_file_info(inName, info_track_id); + } + return 0; + } +#endif + else if (open_edit) { + file = gf_isom_open(inName, GF_ISOM_WRITE_EDIT, tmpdir); + if (!outName && file) outName = inName; + } else if (!file_exists) { + fprintf(stdout, "Error creating file %s: %s\n", inName, gf_error_to_string(GF_URL_ERROR)); + return 1; + } else { + fprintf(stdout, "Cannot open %s - extension not supported\n", inName); + return 1; + } + } + } + + strcpy(outfile, outName ? outName : inName); + if (strrchr(outfile, '.')) { + char *szExt = strrchr(outfile, '.'); + + /*turn on 3GP saving*/ + if (!stricmp(szExt, ".3gp") || !stricmp(szExt, ".3gpp") || !stricmp(szExt, ".3g2")) + conv_type = GF_ISOM_CONV_TYPE_3GPP; + else if (!stricmp(szExt, ".m4a") || !stricmp(szExt, ".m4v")) + conv_type = GF_ISOM_CONV_TYPE_IPOD; + else if (!stricmp(szExt, ".psp")) + conv_type = GF_ISOM_CONV_TYPE_PSP; + + while (outfile[strlen(outfile)-1] != '.') outfile[strlen(outfile)-1] = 0; + outfile[strlen(outfile)-1] = 0; + } + +#ifndef GPAC_READ_ONLY + if (track_dump_type & GF_EXPORT_AVI_NATIVE) { + char szFile[1024]; + GF_MediaExporter mdump; + memset(&mdump, 0, sizeof(mdump)); + mdump.in_name = inName; + mdump.flags = GF_EXPORT_AVI_NATIVE; + mdump.trackID = trackID; + if (trackID>2) { + sprintf(szFile, "%s_audio%d", outfile, trackID-1); + } else { + sprintf(szFile, "%s_%s", outfile, (trackID==1) ? "video" : "audio"); + } + mdump.out_name = szFile; + e = gf_media_export(&mdump); + if (e) goto err_exit; + return 0; + } + if (!open_edit && !gf_isom_probe_file(inName) && track_dump_type) { + GF_MediaExporter mdump; + memset(&mdump, 0, sizeof(mdump)); + mdump.in_name = inName; + mdump.flags = track_dump_type; + mdump.trackID = trackID; + mdump.out_name = outfile; + e = gf_media_export(&mdump); + if (e) goto err_exit; + return 0; + } + + if (dump_mode) { + e = dump_file_text(inName, dump_std ? NULL : outfile, dump_mode-1, do_log); + if (e) goto err_exit; + } + if (stat_level) dump_scene_stats(inName, dump_std ? NULL : outfile, stat_level); +#endif + if (!HintIt && print_sdp) DumpSDP(file, dump_std ? NULL : outfile); + if (print_info) { + if (info_track_id) DumpTrackInfo(file, info_track_id, 1); + else DumpMovieInfo(file); + } + if (dump_isom) dump_file_mp4(file, dump_std ? NULL : outfile); + if (dump_rtp) dump_file_rtp(file, dump_std ? NULL : outfile); + if (dump_ts) dump_file_ts(file, dump_std ? NULL : outfile); + if (dump_cr) dump_file_ismacryp(file, dump_std ? NULL : outfile); + if ((dump_ttxt || dump_srt) && trackID) dump_timed_text_track(file, trackID, dump_std ? NULL : outfile, 0, dump_srt); + if (do_hash) { + u8 hash[20]; + e = gf_media_get_file_hash(inName, hash); + if (e) goto err_exit; + fprintf(stdout, "File %s hash (SHA-1): ", inName); + for (i=0; i<20; i++) fprintf(stdout, "%02X", hash[i]); + fprintf(stdout, "\n"); + } + if (dump_cart) dump_cover_art(file, outfile); + +#ifndef GPAC_READ_ONLY + if (split_duration || split_size) { + split_isomedia_file(file, split_duration, split_size, inName, InterleavingTime, split_start, tmpdir, outName); + /*never save file when splitting is desired*/ + open_edit = 0; + } + if (do_saf || track_dump_type) { + char szFile[1024]; + GF_MediaExporter mdump; + memset(&mdump, 0, sizeof(mdump)); + mdump.file = file; + mdump.flags = do_saf ? GF_EXPORT_SAF : track_dump_type; + if (!do_saf) { + mdump.trackID = trackID; + mdump.sample_num = raw_sample_num; + if (outName) { + mdump.out_name = outName; + mdump.flags |= GF_EXPORT_MERGE; + } else { + sprintf(szFile, "%s_track%d", outfile, trackID); + mdump.out_name = szFile; + } + } else { + mdump.out_name = outfile; + } + e = gf_media_export(&mdump); + if (e) goto err_exit; + } +#endif + + for (i=0; itrackID) tk = gf_isom_get_track_by_id(file, meta->trackID); + + switch (meta->act_type) { +#ifndef GPAC_READ_ONLY + case 0: + /*note: we don't handle file brand modification, this is an author stuff and cannot be guessed from meta type*/ + e = gf_isom_set_meta_type(file, meta->root_meta, tk, meta->meta_4cc); + gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO2, 1); + needSave = 1; + break; + case 1: + self_ref = !stricmp(meta->szPath, "NULL") || !stricmp(meta->szPath, "this") || !stricmp(meta->szPath, "self"); + e = gf_isom_add_meta_item(file, meta->root_meta, tk, self_ref, self_ref ? NULL : meta->szPath, + strlen(meta->szName) ? meta->szName : NULL, + strlen(meta->mime_type) ? meta->mime_type : NULL, + strlen(meta->enc_type) ? meta->enc_type : NULL, + meta->use_dref ? meta->szPath : NULL, NULL); + needSave = 1; + break; + case 2: + e = gf_isom_remove_meta_item(file, meta->root_meta, tk, meta->item_id); + needSave = 1; + break; + case 3: + e = gf_isom_set_meta_primary_item(file, meta->root_meta, tk, meta->item_id); + needSave = 1; + break; + case 4: + case 5: + e = gf_isom_set_meta_xml(file, meta->root_meta, tk, meta->szPath, (meta->act_type==5) ? 1 : 0); + needSave = 1; + break; + case 6: + e = gf_isom_remove_meta_xml(file, meta->root_meta, tk); + needSave = 1; + break; + case 8: + e = gf_isom_extract_meta_item(file, meta->root_meta, tk, meta->item_id, strlen(meta->szPath) ? meta->szPath : NULL); + break; +#endif + case 7: + e = gf_isom_extract_meta_xml(file, meta->root_meta, tk, meta->szPath, NULL); + break; + } + if (e) goto err_exit; + } + if (!open_edit) { + if (file) gf_isom_delete(file); + gf_sys_close(); + return 0; + } + +#ifndef GPAC_READ_ONLY + + for (i=0; itrackID ? gf_isom_get_track_by_id(file, tka->trackID) : 0; + u32 timescale = gf_isom_get_timescale(file); + switch (tka->act_type) { + case 0: + e = gf_isom_remove_track(file, track); + if (e) { + fprintf(stdout, "Error Removing track ID %d: %s\n", tka->trackID, gf_error_to_string(e)); + } else { + fprintf(stdout, "Removing track ID %d\n", tka->trackID); + } + needSave = 1; + break; + case 1: + for (i=0; ilang)); + if (e) goto err_exit; + needSave = 1; + } + needSave = 1; + break; + case 2: + if (tka->delay_ms) { + u64 tk_dur; + gf_isom_remove_edit_segments(file, track); + tk_dur = gf_isom_get_track_duration(file, track); + if (tka->delay_ms>0) { + gf_isom_append_edit_segment(file, track, (timescale*tka->delay_ms)/1000, 0, GF_ISOM_EDIT_EMPTY); + gf_isom_append_edit_segment(file, track, tk_dur, 0, GF_ISOM_EDIT_NORMAL); + needSave = 1; + } else { + u64 to_skip = (timescale*(-tka->delay_ms))/1000; + if (to_skipdelay_ms)*gf_isom_get_media_timescale(file, track) / 1000; + gf_isom_append_edit_segment(file, track, tk_dur-to_skip, seg_dur, GF_ISOM_EDIT_NORMAL); + } else { + fprintf(stdout, "Warning: request negative delay longer than track duration - ignoring\n"); + } + } + } else if (gf_isom_get_edit_segment_count(file, track)) { + gf_isom_remove_edit_segments(file, track); + needSave = 1; + } + break; + case 3: + for (i=0; ikms); + if (e) goto err_exit; + needSave = 1; + } + break; + case 4: + e = gf_media_change_par(file, track, tka->par_num, tka->par_den); + needSave = 1; + break; + case 5: + e = gf_isom_set_handler_name(file, track, tka->hdl_name); + needSave = 1; + break; + case 6: + if (!gf_isom_is_track_enabled(file, track)) { + e = gf_isom_set_track_enabled(file, track, 1); + needSave = 1; + } + break; + case 7: + if (gf_isom_is_track_enabled(file, track)) { + e = gf_isom_set_track_enabled(file, track, 0); + needSave = 1; + } + break; + case 8: + e = gf_isom_set_track_reference(file, track, GF_4CC(tka->lang[0], tka->lang[1], tka->lang[2], tka->lang[3]), (u32) tka->delay_ms); + needSave = 1; + break; + } + if (e) goto err_exit; + } + + if (itunes_tags) { + char *tags = itunes_tags; + + while (tags) { + char *val; + char *sep = strchr(tags, ':'); + u32 tlen, itag = 0; + if (sep) { + while (sep) { + for (itag=0; itag.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/applications/mp4client/extract.c b/applications/mp4client/extract.c new file mode 100644 index 0000000..291f2b6 --- /dev/null +++ b/applications/mp4client/extract.c @@ -0,0 +1,676 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / command-line client + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#ifndef GPAC_READ_ONLY + + +#ifdef WIN32 +#include +#else +typedef struct tagBITMAPFILEHEADER +{ + u16 bfType; + u32 bfSize; + u16 bfReserved1; + u16 bfReserved2; + u32 bfOffBits; +} BITMAPFILEHEADER; + +typedef struct tagBITMAPINFOHEADER{ + u32 biSize; + s32 biWidth; + s32 biHeight; + u16 biPlanes; + u16 biBitCount; + u32 biCompression; + u32 biSizeImage; + s32 biXPelsPerMeter; + s32 biYPelsPerMeter; + u32 biClrUsed; + u32 biClrImportant; +} BITMAPINFOHEADER; + +#define BI_RGB 0L + +#endif + + +#include +#include +#include + +extern Bool is_connected; +extern GF_Terminal *term; +extern u32 Duration; +extern GF_Err last_error; + +static GFINLINE u8 colmask(s32 a, s32 n) +{ + s32 mask = (1 << n) - 1; + return (u8) (a & (0xff & ~mask)) | ((-((a >> n) & 1)) & mask); +} + +static u32 put_pixel(FILE *fout, u32 type, u32 pf, char *ptr) +{ + u16 col; + switch (pf) { + case GF_PIXEL_RGB_32: + case GF_PIXEL_ARGB: + fputc(ptr[0], fout); + fputc(ptr[1], fout); + fputc(ptr[2], fout); + return 4; + + case GF_PIXEL_BGR_32: + case GF_PIXEL_RGBA: + fputc(ptr[3], fout); + fputc(ptr[2], fout); + fputc(ptr[1], fout); + return 4; + + case GF_PIXEL_RGB_24: + fputc(ptr[2], fout); + fputc(ptr[1], fout); + fputc(ptr[0], fout); + return 3; + + case GF_PIXEL_BGR_24: + fputc(ptr[2], fout); + fputc(ptr[1], fout); + fputc(ptr[0], fout); + return 3; + case GF_PIXEL_RGB_565: + col = * (u16 *)ptr; + fputc(colmask(col << 3, 3), fout); + fputc(colmask(col >> (5 - 2), 2), fout); + fputc(colmask(col >> (11 - 3), 3), fout); + return 2; + + case GF_PIXEL_RGB_555: + col = * (u16 *)ptr; + fputc(colmask(col << 3, 3), fout); + fputc(colmask(col >> (5 - 3), 3), fout); + fputc(colmask(col >> (10 - 3), 3), fout); + return 2; + /* this is used to write the byte depthbuffer in greyscale when dumping depth*/ + case GF_PIXEL_GREYSCALE: + /* bmp always needs 3 pixels */ + fputc(ptr[0], fout); + fputc(ptr[0], fout); + fputc(ptr[0], fout); + /* if printing the characters corresponding to the float depth buffer: */ + /* + { + u32 i=0; + while (ptr[i]!='\0') { + fputc(ptr[i], fout); + i++; + } + fputc('\b', fout); + } + */ + return 1; + + + case 0: + fputc(ptr[0], fout); + return 1; + } + return 0; +} + +void write_bmp(GF_VideoSurface *fb, char *rad_name, u32 img_num) +{ + char str[GF_MAX_PATH]; + BITMAPFILEHEADER fh; + BITMAPINFOHEADER fi; + FILE *fout; + u32 j, i; + char *ptr, *prev; + + prev = strrchr(rad_name, '.'); + if (prev) prev[0] = '\0'; + + if (fb->pixel_format==GF_PIXEL_GREYSCALE) sprintf(str, "%s_%d_depth.bmp", rad_name, img_num); + else sprintf(str, "%s_%d.bmp", rad_name, img_num); + + fout = fopen(str, "wb"); + if (!fout) return; + + memset(&fh, 0, sizeof(fh)); + fh.bfType = 19778; + fh.bfOffBits = 14 + 40; + + memset(&fi, 0, sizeof(char)*40); + fi.biSize = sizeof(char)*40; + fi.biWidth = fb->width; + fi.biHeight = fb->height; + fi.biPlanes = 1; + if (fb->pixel_format==GF_PIXEL_GREYSCALE) fi.biBitCount = 24; + else fi.biBitCount = 24; + fi.biCompression = BI_RGB; + fi.biSizeImage = fb->pitch * fb->height; + + /*NOT ALIGNED!!*/ + fwrite(&fh.bfType, 2, 1, fout); + fwrite(&fh.bfSize, 4, 1, fout); + fwrite(&fh.bfReserved1, 2, 1, fout); + fwrite(&fh.bfReserved2, 2, 1, fout); + fwrite(&fh.bfOffBits, 4, 1, fout); + + fwrite(&fi, 1, 40, fout); + + for (j=fb->height; j>0; j--) { + ptr = fb->video_buffer + (j-1)*fb->pitch; + for (i=0;iwidth; i++) { + u32 res = put_pixel(fout, 0, fb->pixel_format, ptr); + assert(res); + ptr += res; + } + } + fclose(fout); +} + +/*writes onto a file the content of the framebuffer in *fb interpreted as the byte depthbuffer */ +/*it's also possible to write a float depthbuffer by passing the floats to strings and writing chars in putpixel - see comments*/ +void write_depthfile(GF_VideoSurface *fb, char *rad_name, u32 img_num) +{ + FILE *fout; + u32 i, j; + char val; + unsigned char *depth; + + depth = (unsigned char *) fb->video_buffer; + + fout = fopen("dump_depth", "wb"); + if (!fout) return; + for (j=0; jheight; j++) { + for (i=0;iwidth; i++) { + +#ifdef GPAC_USE_TINYGL + val = fputc(depth[2*i+j*fb->width*sizeof(unsigned short)], fout); + val = fputc(depth[2*i+j*fb->width*sizeof(unsigned short) + 1], fout); +#else + val = fputc(depth[i+j*fb->width], fout); +#endif + } + } + fclose(fout); +} + +void write_texture_file(GF_VideoSurface *fb, char *rad_name, u32 img_num, u32 dump_mode) +{ + + FILE *fout; + u32 i, j; + char val; + unsigned char *buf; + + buf = (unsigned char *) fb->video_buffer; + + if (dump_mode==6) fout = fopen("dump_rgbds", "wb"); + else if (dump_mode==9) fout = fopen("dump_rgbd", "wb"); + else return; + + if (!fout) return; + for (j=0; jheight; j++) { + for (i=0;iwidth*4; i++) { + val = fputc(buf[i+j*fb->pitch], fout); + } + } + fclose(fout); +} + + +void write_raw(GF_VideoSurface *fb, char *rad_name, u32 img_num) +{ + u32 j, i; + char *ptr, *prev; + char str[GF_MAX_PATH]; + FILE *fout; + prev = strrchr(rad_name, '.'); + if (prev) prev[0] = '\0'; + if (img_num<10) { + sprintf(str, "%s_00%d.raw", rad_name, img_num); + } else if (img_num<100) { + sprintf(str, "%s_0%d.raw", rad_name, img_num); + } else { + sprintf(str, "%s_%d.raw", rad_name, img_num); + } + + fout = fopen(str, "wb"); + if (!fout) return; + + + for (j=0;jheight; j++) { + ptr = fb->video_buffer + j*fb->pitch; + for (i=0;iwidth; i++) { + u32 res = put_pixel(fout, 0, fb->pixel_format, ptr); + assert(res); + ptr += res; + } + } + fclose(fout); +} + + +/* creates a .bmp format greyscale image of the byte depthbuffer and a binary with only the content of the depthbuffer */ +void dump_depth (GF_Terminal *term, char *rad_name, u32 dump_type, u32 frameNum, char *conv_buf, avi_t *avi_out) +{ + GF_Err e; + u32 i, k; + GF_VideoSurface fb; + + /*lock it*/ + e = gf_sc_get_screen_buffer(term->compositor, &fb, 1); + if (e) fprintf(stdout, "Error grabbing depth buffer: %s\n", gf_error_to_string(e)); + else fprintf(stdout, "OK\n"); + /*export frame*/ + switch (dump_type) { + case 1: + case 8: + /*reverse frame*/ + for (k=0; k> 8/*(11 - 3)*/, 3); + dst[1] = colmask(src_16 >> 3/*(5 - 2)*/, 2); + dst[0] = colmask(src_16 << 3, 3); + src+=2; + break; + case GF_PIXEL_RGB_555: + src_16 = * (u16 *)src; + dst[2] = colmask(src_16 >> 7/*(10 - 3)*/, 3); + dst[1] = colmask(src_16 >> 2/*(5 - 3)*/, 3); + dst[0] = colmask(src_16 << 3, 3); + src+=2; + break; + /*for depth .avi*/ + case GF_PIXEL_GREYSCALE: + dst[0] = src[0]; + dst[1] = src[0]; + dst[2] = src[0]; + src+=1; + break; + } + dst += 3; + } + } + if (AVI_write_frame(avi_out, conv_buf, fb.height*fb.width*3, 1) <0) + printf("Error writing frame\n"); + break; + case 2: + write_bmp(&fb, rad_name, frameNum); + break; + case 3: + write_raw(&fb, rad_name, frameNum); + break; + case 4: + write_depthfile(&fb, rad_name, frameNum); + break; + case 7: + write_bmp(&fb, rad_name, frameNum); + break; + + } + /*unlock it*/ + /*in -depth -avi mode, do not release it yet*/ + if (dump_type!=8) gf_sc_release_screen_buffer(term->compositor, &fb); +} + +void dump_frame(GF_Terminal *term, char *rad_name, u32 dump_type, u32 frameNum, char *conv_buf, avi_t *avi_out) +{ + GF_Err e = GF_OK; + u32 i, k, out_size; + GF_VideoSurface fb; + + /*lock it*/ + if (dump_type==5 || dump_type==6) e = gf_sc_get_screen_buffer(term->compositor, &fb, 2); + else if (dump_type== 9 || dump_type==10) e = gf_sc_get_screen_buffer(term->compositor, &fb, 3); + else e = gf_sc_get_screen_buffer(term->compositor, &fb, 0); + if (e) fprintf(stdout, "Error grabbing frame buffer: %s\n", gf_error_to_string(e)); + + if (dump_type!=5 && dump_type!= 10) { + out_size = fb.height*fb.width*3; + } else { + out_size = fb.height*fb.width*4; + } + /*export frame*/ + switch (dump_type) { + case 1: + case 5: + case 10: + case 8: + /*reverse frame*/ + for (k=0; k> 8/*(11 - 3)*/, 3); + dst[1] = colmask(src_16 >> 3/*(5 - 2)*/, 2); + dst[0] = colmask(src_16 << 3, 3); + src+=2; + dst+=3; + } + break; + case GF_PIXEL_RGB_555: + for (i=0;i> 7/*(10 - 3)*/, 3); + dst[1] = colmask(src_16 >> 2/*(5 - 3)*/, 3); + dst[0] = colmask(src_16 << 3, 3); + src+=2; + dst +=3; + } + break; + } + } + if (dump_type!=5 && dump_type!= 10) { + if (AVI_write_frame(avi_out, conv_buf, out_size, 1) <0) + printf("Error writing frame\n"); + } else { + if (AVI_write_frame(avi_out, conv_buf, out_size, 1) <0) + printf("Error writing frame\n"); + } + break; + case 2: + write_bmp(&fb, rad_name, frameNum); + break; + case 6: + case 9: + write_texture_file(&fb, rad_name, frameNum, dump_type); + break; + + case 3: + write_raw(&fb, rad_name, frameNum); + break; + } + /*unlock it*/ + gf_sc_release_screen_buffer(term->compositor, &fb); +} + +Bool dump_file(char *url, u32 dump_mode, Double fps, u32 width, u32 height, Float scale, u32 *times, u32 nb_times) +{ + GF_Err e; + u32 i = 0; + GF_VideoSurface fb; + char szPath[GF_MAX_PATH]; + char *prev=NULL; + + prev = strstr(url, "://"); + if (prev) { + prev = strrchr(url, '/'); + if (prev) prev++; + } + + if (!prev) prev = url; + strcpy(szPath, prev); + prev = strrchr(szPath, '.'); + if (prev) prev[0] = 0; + + fprintf(stdout, "Opening URL %s\n", url); + /*connect in pause mode*/ + gf_term_connect_from_time(term, url, 0, 1); + + while (!term->compositor->scene + || term->compositor->msg_type + || (gf_term_get_option(term, GF_OPT_PLAY_STATE) == GF_STATE_STEP_PAUSE) + ) { + if (last_error) return 1; + gf_term_process_flush(term); + gf_sleep(10); + } + + if (width && height) { + gf_term_set_size(term, width, height); + gf_term_process_flush(term); + } + + e = gf_sc_get_screen_buffer(term->compositor, &fb, 0); + if (e != GF_OK) { + fprintf(stdout, "Error grabbing screen buffer: %s\n", gf_error_to_string(e)); + return 0; + } + width = fb.width; + height = fb.height; + gf_sc_release_screen_buffer(term->compositor, &fb); + + if (scale != 1) { + width = (u32)(width * scale); + height = (u32)(height * scale); + gf_term_set_size(term, width, height); + gf_term_process_flush(term); + } + + /*we work in RGB24, and we must make sure the pitch is %4*/ + if ((width*3)%4) { + fprintf(stdout, "Adjusting width (%d) to have a stride multiple of 4\n", width); + while ((width*3)%4) width--; + + gf_term_set_size(term, width, height); + gf_term_process_flush(term); + + gf_sc_get_screen_buffer(term->compositor, &fb, 0); + width = fb.width; + height = fb.height; + gf_sc_release_screen_buffer(term->compositor, &fb); + } + + if (dump_mode==1 || dump_mode==5 || dump_mode==8 || dump_mode==10) { + u32 time, prev_time, nb_frames, dump_dur; + char *conv_buf; + avi_t *avi_out = NULL; + avi_t *depth_avi_out = NULL; + char szPath_depth[GF_MAX_PATH]; + char comp[5]; + strcpy(szPath_depth, szPath); + strcat(szPath, ".avi"); + avi_out = AVI_open_output_file(szPath); + if (!avi_out) { + fprintf(stdout, "Error creating AVI file %s\n", szPath); + return 1; + } + if (dump_mode==8) { + strcat(szPath_depth, "_depth.avi"); + depth_avi_out = AVI_open_output_file(szPath_depth); + if (!depth_avi_out) { + fprintf(stdout, "Error creating AVI file %s\n", szPath); + return 1; + } + } + + if (!fps) fps = 25.0; + time = prev_time = 0; + nb_frames = 0; + + if (nb_times==2) { + prev_time = times[0]; + dump_dur = times[1] - times[0]; + } else { + dump_dur = times[0] ? times[0] : Duration; + } + if (!dump_dur) { + fprintf(stdout, "Warning: file has no duration, defaulting to 1 sec\n"); + dump_dur = 1000; + } + + comp[0] = comp[1] = comp[2] = comp[3] = comp[4] = 0; + AVI_set_video(avi_out, width, height, fps, comp); + if (dump_mode==8) AVI_set_video(depth_avi_out, width, height, fps, comp); + if (dump_mode != 5 && dump_mode!=10) conv_buf = malloc(sizeof(char) * width * height * 3); + else conv_buf = malloc(sizeof(char) * width * height * 4); + /*step to first frame*/ + if (prev_time) gf_term_step_clocks(term, prev_time); + + while (time < dump_dur) { + while ((gf_term_get_option(term, GF_OPT_PLAY_STATE) == GF_STATE_STEP_PAUSE)) { + gf_term_process_flush(term); + } + fprintf(stdout, "Dumping %02d/100\r", (u32) ((100.0*prev_time)/dump_dur) ); + + if (dump_mode==8) { + /*we'll dump both buffers at once*/ + gf_mx_p(term->compositor->mx); + dump_depth(term, szPath_depth, dump_mode, i+1, conv_buf, depth_avi_out); + dump_frame(term, szPath, dump_mode, i+1, conv_buf, avi_out); + gf_mx_v(term->compositor->mx); + + } + else dump_frame(term, szPath, dump_mode, i+1, conv_buf, avi_out); + + nb_frames++; + time = (u32) (nb_frames*1000/fps); + gf_term_step_clocks(term, time - prev_time); + prev_time = time; + } + AVI_close(avi_out); + if (dump_mode==8) AVI_close(depth_avi_out); + free(conv_buf); + fprintf(stdout, "AVI Extraction 100/100\n"); + } else { + if (times[0]) gf_term_step_clocks(term, times[0]); + + for (i=0; i +#include +#include +#include +#include + +/*ISO 639 languages*/ +#include + +#ifndef WIN32 +#include +#include +#else +/*for GetModuleFileName*/ +#include +#endif + +/*local prototypes*/ +void PrintWorldInfo(GF_Terminal *term); +void ViewOD(GF_Terminal *term, u32 OD_ID, u32 number); +void PrintODList(GF_Terminal *term); +void ViewODs(GF_Terminal *term, Bool show_timing); +void PrintGPACConfig(); + +static Bool not_threaded = 0; +static Bool no_audio = 0; +static Bool no_regulation = 0; +Bool is_connected = 0; +Bool startup_file = 0; +GF_User user; +GF_Terminal *term; +u64 Duration; +GF_Err last_error = GF_OK; + +static Bool request_next_playlist_item = 0; + +static GF_Config *cfg_file; +static Bool display_rti = 0; +static Bool Run; +static Bool CanSeek = 0; +static u32 Volume=100; +static char the_url[GF_MAX_PATH]; +static Bool no_mime_check = 1; +static Bool be_quiet = 0; +static u32 log_time_start = 0; + +static u32 forced_width=0; +static u32 forced_height=0; + +/*windowless options*/ +u32 align_mode = 0; +u32 init_w = 0; +u32 init_h = 0; +u32 last_x, last_y; +Bool right_down = 0; + +#ifndef GPAC_READ_ONLY +void dump_frame(GF_Terminal *term, char *rad_path, u32 dump_type, u32 frameNum); +Bool dump_file(char *the_url, u32 dump_mode, Double fps, u32 width, u32 height, Float scale, u32 *times, u32 nb_times); +#endif + +void PrintUsage() +{ + fprintf(stdout, "Usage MP4Client [options] [filename]\n" + "\t-c fileName: user-defined configuration file\n" + "\t-rti fileName: logs run-time info (FPS, CPU, Mem usage) to file\n" + "\t-rtix fileName: same as -rti but driven by GPAC logs\n" + "\t-quiet: removes script message, buffering and downloading status\n" + "\t-log-file file: sets output log file.\n" + "\t-log-level lev: sets log level. Possible values are:\n" + "\t \"error\" : logs only error messages\n" + "\t \"warning\" : logs error+warning messages\n" + "\t \"info\" : logs error+warning+info messages\n" + "\t \"debug\" : logs all messages\n" + "\n" + "\t-log-tools lt: sets tool(s) to log. List of \':\'-separated values:\n" + "\t \"core\" : libgpac core\n" + "\t \"coding\" : bitstream formats (audio, video, scene)\n" + "\t \"container\" : container formats (ISO File, MPEG-2 TS, AVI, ...)\n" + "\t \"network\" : network data exept RTP trafic\n" + "\t \"rtp\" : rtp trafic\n" + "\t \"author\" : authoring tools (hint, import, export)\n" + "\t \"sync\" : terminal sync layer\n" + "\t \"codec\" : terminal codec messages\n" + "\t \"parser\" : scene parsers (svg, xmt, bt) and other\n" + "\t \"media\" : terminal media object management\n" + "\t \"scene\" : scene graph and scene manager\n" + "\t \"script\" : scripting engine messages\n" + "\t \"interact\" : interaction engine (events, scripts, etc)\n" + "\t \"compose\" : composition engine (2D, 3D, etc)\n" + "\t \"service\" : network service management\n" + "\t \"mmio\" : Audio/Video HW I/O management\n" + "\t \"none\" : no tool logged\n" + "\t \"all\" : all tools logged\n" + "\n" + "\t-size WxH: specifies visual size (default: scene size)\n" + "\t-scale s: scales the visual size (default: 1)\n" + "\t-no-thread: disables thread usage (except for audio)\n" + "\t-no-audio: disables audio \n" + "\t-no-wnd: uses windowless mode (Win32 only)\n" + "\t-align vh: specifies v and h alignment for windowless mode\n" + " possible v values: t(op), m(iddle), b(ottom)\n" + " possible h values: l(eft), m(iddle), r(ight)\n" + " default alignment is top-left\n" + "Dumper Options:\n" + "\t-bmp [times]: dumps given frames to bmp\n" + "\t-raw [times]: dumps given frames to bmp\n" + "\t-avi [times]: dumps given file to raw avi\n" + "\t-rgbds: dumps the RGBDS pixel format texture\n" + " with -avi [times]: dumps an rgbds-format .avi\n" + "\t-rgbd: dumps the RGBD pixel format texture\n" + " with -avi [times]: dumps an rgbd-format .avi\n" + "\t-depth: dumps depthmap (z-buffer) frames\n" + " with -avi [times]: dumps depthmap in grayscale .avi\n" + " with -bmp: dumps depthmap in grayscale .bmp\n" + "\t-fps FPS: specifies frame rate for AVI dumping (default: 25.0)\n" + "\t-2d: uses 2D compositor\n" + "\t-3d: uses 3D compositor\n" + "\t-fill: uses fill aspect ratio for dumping (default: none)\n" + "\t-show: show window while dumping (default: no)\n" + "MP4Client - GPAC command line player and dumper - version %s\n" + "GPAC Written by Jean Le Feuvre (c) 2001-2005 - ENST (c) 2005-200X\n", + + GPAC_FULL_VERSION + ); +} + +void PrintHelp() +{ + fprintf(stdout, "MP4Client command keys:\n" + "\to: connect to the specified URL\n" + "\tO: connect to the specified URL in playlist mode\n" + "\tN: switch to the next URL in the playlist (works with return key as well)\n" + "\tr: restart current presentation\n" + "\tp: play/pause the presentation\n" + "\ts: step one frame ahead\n" + "\tz: seek into presentation\n" + "\tt: print current timing\n" + "\n" + "\tw: view world info\n" + "\tv: view Object Descriptor list\n" + "\ti: view Object Descriptor info (by ID)\n" + "\tj: view Object Descriptor info (by number)\n" + "\tb: view media objects timing and buffering info\n" + "\tm: view media objects buffering and memory info\n" + "\td: dumps scene graph\n" + "\n" + "\tC: Enable Streaming Cache\n" + "\tS: Stops Streaming Cache and save to file\n" + "\tA: Aborts Streaming Cache\n" + "\n" + "\tk: turns stress mode on/off\n" + "\tn: changes navigation mode\n" + "\tx: reset to last active viewpoint\n" + "\n" + "\t2: restart using 2D compositor\n" + "\t3: restart using 3D compositor\n" + "\n" + "\t4: forces 4/3 Aspect Ratio\n" + "\t5: forces 16/9 Aspect Ratio\n" + "\t6: forces no Aspect Ratio (always fill screen)\n" + "\t7: forces original Aspect Ratio (default)\n" + "\n" + "\tL: changes to new log level. CF MP4Client usage for possible values\n" + "\tT: select new tools to log. CF MP4Client usage for possible values\n" + "\n" + "\tl: list available modules\n" + "\tc: prints some GPAC configuration info\n" + "\tR: toggles run-time info display on/off\n" + "\tq: exit the application\n" + "\th: print this message\n" + "\n" + "MP4Client - GPAC command line player - version %s\n" + "GPAC Written by Jean Le Feuvre (c) 2001-2005 - ENST (c) 2005-200X\n", + + GPAC_FULL_VERSION + ); +} + + + +GF_Config *create_default_config(char *file_path, char *file_name) +{ + GF_Config *cfg; + char szPath[GF_MAX_PATH]; + FILE *f; + sprintf(szPath, "%s%c%s", file_path, GF_PATH_SEPARATOR, file_name); + f = fopen(szPath, "wt"); + fprintf(stdout, "create %s: %s\n", szPath, (f==NULL) ? "Error" : "OK"); + if (!f) return NULL; + fclose(f); + + cfg = gf_cfg_new(file_path, file_name); + if (!cfg) return NULL; + +#ifdef GPAC_MODULES_PATH + fprintf(stdout, "Using module directory %s \n", GPAC_MODULES_PATH); + strcpy(szPath, GPAC_MODULES_PATH); +#elif defined(WIN32) + strcpy(szPath, file_path); +#else + fprintf(stdout, "Please enter full path to GPAC modules directory:\n"); + scanf("%s", szPath); +#endif + gf_cfg_set_key(cfg, "General", "ModulesDirectory", szPath); + gf_cfg_set_key(cfg, "Audio", "ForceConfig", "yes"); + gf_cfg_set_key(cfg, "Audio", "NumBuffers", "2"); + gf_cfg_set_key(cfg, "Audio", "TotalDuration", "120"); + gf_cfg_set_key(cfg, "Audio", "DisableNotification", "no"); + gf_cfg_set_key(cfg, "FontEngine", "FontReader", "ft_font"); + +#ifdef WIN32 + GetWindowsDirectory((char*)szPath, MAX_PATH); + if (szPath[strlen((char*)szPath)-1] != '\\') strcat((char*)szPath, "\\"); + strcat((char *)szPath, "Fonts"); +#elif defined(__DARWIN__) || defined(__APPLE__) + fprintf(stdout, "Please enter full path to a TrueType font directory (.ttf, .ttc) - enter to default:\n"); + scanf("%s", szPath); +#else + /*these fonts seems installed by default on many systems...*/ + gf_cfg_set_key(cfg, "FontEngine", "FontSerif", "Bitstream Vera Serif"); + gf_cfg_set_key(cfg, "FontEngine", "FontSans", "Bitstream Vera Sans"); + gf_cfg_set_key(cfg, "FontEngine", "FontFixed", "Bitstream Vera Monospace"); + strcpy(szPath, "/usr/share/fonts/truetype/"); +#endif + fprintf(stdout, "Using default font directory %s\n", szPath); + gf_cfg_set_key(cfg, "FontEngine", "FontDirectory", szPath); + +#ifdef WIN32 +/* fprintf(stdout, "Please enter full path to a cache directory for HTTP downloads:\n"); + scanf("%s", szPath); +*/ + GetWindowsDirectory((char*)szPath, MAX_PATH); + if (szPath[strlen((char*)szPath)-1] != '\\') strcat((char*)szPath, "\\"); + strcat((char *)szPath, "Temp"); + + gf_cfg_set_key(cfg, "General", "CacheDirectory", szPath); + fprintf(stdout, "Using default cache directory %s\n", szPath); +#else + fprintf(stdout, "Using /tmp as a cache directory for HTTP downloads:\n"); + gf_cfg_set_key(cfg, "General", "CacheDirectory", "/tmp"); +#endif + + gf_cfg_set_key(cfg, "Downloader", "CleanCache", "yes"); + gf_cfg_set_key(cfg, "Compositor", "AntiAlias", "All"); + gf_cfg_set_key(cfg, "Compositor", "FrameRate", "30"); + /*use power-of-2 emulation*/ + gf_cfg_set_key(cfg, "Compositor", "EmulatePOW2", "yes"); +#ifdef WIN32 + gf_cfg_set_key(cfg, "Compositor", "ScalableZoom", "yes"); + gf_cfg_set_key(cfg, "Video", "DriverName", "DirectX Video Output"); +#else +#ifdef __DARWIN__ + gf_cfg_set_key(cfg, "Video", "DriverName", "SDL Video Output"); + /*SDL not so fast with scalable zoom*/ + gf_cfg_set_key(cfg, "Compositor", "ScalableZoom", "no"); +#else + gf_cfg_set_key(cfg, "Video", "DriverName", "X11 Video Output"); + /*x11 only supports scalable zoom*/ + gf_cfg_set_key(cfg, "Compositor", "ScalableZoom", "yes"); + gf_cfg_set_key(cfg, "Audio", "DriverName", "SDL Audio Output"); +#endif +#endif + gf_cfg_set_key(cfg, "Video", "SwitchResolution", "no"); + gf_cfg_set_key(cfg, "Network", "AutoReconfigUDP", "yes"); + gf_cfg_set_key(cfg, "Network", "UDPTimeout", "10000"); + gf_cfg_set_key(cfg, "Network", "BufferLength", "3000"); +#ifdef GPAC_TRISCOPE_MODE + gf_cfg_set_key(cfg, "Compositor", "OGLDepthBuffGain", "5"); +#endif + + /*store and reload*/ + gf_cfg_del(cfg); + return gf_cfg_new(file_path, file_name); +} + +static void PrintTime(u64 time) +{ + u32 ms, h, m, s; + h = (u32) (time / 1000 / 3600); + m = (u32) (time / 1000 / 60 - h*60); + s = (u32) (time / 1000 - h*3600 - m*60); + ms = (u32) (time - (h*3600 + m*60 + s) * 1000); + fprintf(stdout, "%02d:%02d:%02d.%02d", h, m, s, ms); +} + + +static u32 rti_update_time_ms = 200; +static FILE *rti_logs = NULL; +static u64 memory_at_gpac_startup = 0; + +static void UpdateRTInfo(const char *legend) +{ + GF_SystemRTInfo rti; + + /*refresh every second*/ + if (!display_rti && !rti_logs) return; + if (!gf_sys_get_rti(rti_update_time_ms, &rti, 0) && !legend) + return; + + if (display_rti) { + char szMsg[1024]; + GF_Event evt; + + if (!rti.process_memory) rti.process_memory = (u32) (memory_at_gpac_startup-rti.physical_memory_avail); + if (!rti.gpac_memory) rti.gpac_memory = (u32) (memory_at_gpac_startup-rti.physical_memory_avail); + sprintf(szMsg, "FPS %02.2f - CPU %02d (%02d) - Mem %d kB", + gf_term_get_framerate(term, 0), rti.total_cpu_usage, rti.process_cpu_usage, (u32) (rti.gpac_memory / 1024) ); + + evt.type = GF_EVENT_SET_CAPTION; + evt.caption.caption = szMsg; + gf_term_user_event(term, &evt); + } + if (rti_logs) { + fprintf(rti_logs, "% 8d\t% 8d\t% 8d\t% 4d\t% 8d\t%s", + gf_sys_clock(), + gf_term_get_time_in_ms(term), + rti.total_cpu_usage, + (u32) gf_term_get_framerate(term, 0), + (u32) (rti.gpac_memory / 1024), + legend ? legend : "" + ); + if (!legend) fprintf(rti_logs, "\n"); + } +} + +static void ResetCaption() +{ + GF_Event event; + if (display_rti) return; + event.type = GF_EVENT_SET_CAPTION; + if (is_connected) { + char szName[1024]; + NetInfoCommand com; + + event.caption.caption = NULL; + /*get any service info*/ + if (!startup_file && gf_term_get_service_info(term, gf_term_get_root_object(term), &com) == GF_OK) { + strcpy(szName, ""); + if (com.track_info) { + char szBuf[10]; + sprintf(szBuf, "%02d ", (u32) (com.track_info>>16) ); + strcat(szName, szBuf); + } + if (com.artist) { strcat(szName, com.artist); strcat(szName, " "); } + if (com.name) { strcat(szName, com.name); strcat(szName, " "); } + if (com.album) { strcat(szName, "("); strcat(szName, com.album); strcat(szName, ")"); } + + if (strlen(szName)) event.caption.caption = szName; + } + if (!event.caption.caption) { + char *str = strrchr(the_url, '\\'); + if (!str) str = strrchr(the_url, '/'); + event.caption.caption = str ? str+1 : the_url; + } + } else { + event.caption.caption = "GPAC MP4Client " GPAC_FULL_VERSION; + } + gf_term_user_event(term, &event); +} + +#ifdef WIN32 +u32 get_sys_col(int idx) +{ + u32 res; + DWORD val = GetSysColor(idx); + res = (val)&0xFF; res<<=8; + res |= (val>>8)&0xFF; res<<=8; + res |= (val>>16)&0xFF; + return res; +} +#endif + +Bool GPAC_EventProc(void *ptr, GF_Event *evt) +{ + if (!term) return 0; + + switch (evt->type) { + case GF_EVENT_DURATION: + Duration = 1000; + Duration = (u64) (((s64) Duration) * evt->duration.duration); + CanSeek = evt->duration.can_seek; + break; + case GF_EVENT_MESSAGE: + { + const char *servName; + if (!evt->message.service || !strcmp(evt->message.service, the_url)) { + servName = "main service"; + } else { + servName = evt->message.service; + } + if (!evt->message.message) return 0; + if (evt->message.error==GF_SCRIPT_INFO) { + fprintf(stdout, "%s\n", evt->message.message); + } else if (evt->message.error) { + if (!is_connected) last_error = evt->message.error; + fprintf(stdout, "%s (%s): %s\n", evt->message.message, servName, gf_error_to_string(evt->message.error)); + } else if (!be_quiet) + fprintf(stdout, "(%s) %s\r", servName, evt->message.message); + } + break; + case GF_EVENT_PROGRESS: + { + char *szTitle = ""; + if (evt->progress.progress_type==0) szTitle = "Buffer "; + else if (evt->progress.progress_type==1) szTitle = "Download "; + else if (evt->progress.progress_type==2) szTitle = "Import "; + gf_set_progress(szTitle, evt->progress.done, evt->progress.total); + } + break; + + + case GF_EVENT_DBLCLICK: + gf_term_set_option(term, GF_OPT_FULLSCREEN, !gf_term_get_option(term, GF_OPT_FULLSCREEN)); + return 0; + + case GF_EVENT_MOUSEDOWN: + if (evt->mouse.button==GF_MOUSE_RIGHT) { + right_down = 1; + last_x = evt->mouse.x; + last_y = evt->mouse.y; + } + return 0; + case GF_EVENT_MOUSEUP: + if (evt->mouse.button==GF_MOUSE_RIGHT) { + right_down = 0; + last_x = evt->mouse.x; + last_y = evt->mouse.y; + } + return 0; + case GF_EVENT_MOUSEMOVE: + if (right_down && (user.init_flags & GF_TERM_WINDOWLESS) ) { + GF_Event move; + move.move.x = evt->mouse.x - last_x; + move.move.y = evt->mouse.y - last_y; + move.type = GF_EVENT_MOVE; + move.move.relative = 1; + gf_term_user_event(term, &move); + } + return 0; + + /*we use CTRL and not ALT for keys, since windows shortcuts keypressed with ALT*/ + case GF_EVENT_KEYDOWN: + if ((evt->key.flags & GF_KEY_MOD_ALT)) { + switch (evt->key.key_code) { + case GF_KEY_LEFT: + if (Duration>=2000) { + s32 res = gf_term_get_time_in_ms(term) - (s32) (5*Duration/100); + if (res<0) res=0; + fprintf(stdout, "seeking to %.2f %% (", 100.0*(s64)res / (s64)Duration); + PrintTime(res); + fprintf(stdout, ")\n"); + gf_term_play_from_time(term, res, 0); + } + break; + case GF_KEY_RIGHT: + if (Duration>=2000) { + u32 res = gf_term_get_time_in_ms(term) + (s32) (5*Duration/100); + if (res>=Duration) res = 0; + fprintf(stdout, "seeking to %.2f %% (", 100.0*(s64)res / (s64)Duration); + PrintTime(res); + fprintf(stdout, ")\n"); + gf_term_play_from_time(term, res, 0); + } + break; + /*these 2 are likely not supported by most audio ouput modules*/ + case GF_KEY_UP: + if (Volume!=100) { Volume = MIN(Volume + 5, 100); gf_term_set_option(term, GF_OPT_AUDIO_VOLUME, Volume); } + break; + case GF_KEY_DOWN: + if (Volume) { Volume = (Volume > 5) ? (Volume-5) : 0; gf_term_set_option(term, GF_OPT_AUDIO_VOLUME, Volume); } + break; + case GF_KEY_PAGEDOWN: + if (Volume!=100) { Volume = MIN(Volume + 5, 100); gf_term_set_option(term, GF_OPT_AUDIO_VOLUME, Volume); } + break; + } + } else { + switch (evt->key.key_code) { + case GF_KEY_HOME: + gf_term_set_option(term, GF_OPT_NAVIGATION_TYPE, 1); + break; + case GF_KEY_ESCAPE: + gf_term_set_option(term, GF_OPT_FULLSCREEN, !gf_term_get_option(term, GF_OPT_FULLSCREEN)); + break; + case GF_KEY_PAGEDOWN: + request_next_playlist_item = 1; + break; + } + } + + if (!(evt->key.flags & GF_KEY_MOD_CTRL)) return 0; + switch (evt->key.key_code) { + case GF_KEY_F: + fprintf(stdout, "Rendering rate: %f FPS\n", gf_term_get_framerate(term, 0)); + break; + case GF_KEY_T: + fprintf(stdout, "Scene Time: %f \n", gf_term_get_time_in_ms(term)/1000.0); + break; + case GF_KEY_D: + gf_term_set_option(term, GF_OPT_DIRECT_DRAW, !gf_term_get_option(term, GF_OPT_DIRECT_DRAW) ); + break; + case GF_KEY_4: gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3); break; + case GF_KEY_5: gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9); break; + case GF_KEY_6: gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN); break; + case GF_KEY_7: gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP); break; + case GF_KEY_P: + gf_term_set_option(term, GF_OPT_PLAY_STATE, (gf_term_get_option(term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) ? GF_STATE_PLAYING : GF_STATE_PAUSED); + break; + case GF_KEY_S: + gf_term_set_option(term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE); + break; + case GF_KEY_B: + if (is_connected) ViewODs(term, 1); + break; + case GF_KEY_M: + if (is_connected) ViewODs(term, 0); + break; + } + break; + + case GF_EVENT_CONNECT: + if (evt->connect.is_connected) { + is_connected = 1; + fprintf(stdout, "Service Connected\n"); + } else if (is_connected) { + fprintf(stdout, "Service %s\n", is_connected ? "Disconnected" : "Connection Failed"); + is_connected = 0; + Duration = 0; + } + if (init_w && init_h) { + gf_term_set_size(term, init_w, init_h); + } + ResetCaption(); + break; + case GF_EVENT_SIZE: + if (user.init_flags & GF_TERM_WINDOWLESS) { + GF_Event move; + move.type = GF_EVENT_MOVE; + move.move.align_x = align_mode & 0xFF; + move.move.align_y = (align_mode>>8) & 0xFF; + move.move.relative = 2; + gf_term_user_event(term, &move); + } + break; + case GF_EVENT_SCENE_SIZE: + if (forced_width && forced_height) { + GF_Event size; + size.type = GF_EVENT_SIZE; + size.size.width = forced_width; + size.size.height = forced_height; + gf_term_user_event(term, &size); + } + break; + + case GF_EVENT_METADATA: + ResetCaption(); + break; + + case GF_EVENT_QUIT: + Run = 0; + break; + case GF_EVENT_DISCONNECT: + gf_term_disconnect(term); + break; + case GF_EVENT_MIGRATE: + { + const char *str = gf_cfg_get_key(cfg_file, "Network", "SessionMigration"); + if (!str || !strcmp(str, "no")) + gf_cfg_set_key(cfg_file, "Network", "SessionMigration", "yes"); + + fprintf(stdout, "Migrating session %s\n", the_url); + gf_term_disconnect(term); + } + break; + case GF_EVENT_NAVIGATE_INFO: + if (evt->navigate.to_url) fprintf(stdout, "Go to URL: \"%s\"\r", evt->navigate.to_url); + break; + case GF_EVENT_NAVIGATE: + if (gf_term_is_supported_url(term, evt->navigate.to_url, 1, no_mime_check)) { + strcpy(the_url, evt->navigate.to_url); + fprintf(stdout, "Navigating to URL %s\n", the_url); + gf_term_navigate_to(term, evt->navigate.to_url); + return 1; + } else { + fprintf(stdout, "Navigation destination not supported\nGo to URL: %s\n", evt->navigate.to_url); + } + break; + case GF_EVENT_SET_CAPTION: + gf_term_user_event(term, evt); + break; + case GF_EVENT_AUTHORIZATION: + if (!strlen(evt->auth.user)) { + fprintf(stdout, "Authorization required for site %s\n", evt->auth.site_url); + fprintf(stdout, "login: "); + scanf("%s", evt->auth.user); + } else { + fprintf(stdout, "Authorization required for %s@%s\n", evt->auth.user, evt->auth.site_url); + } + fprintf(stdout, "password: "); + gf_prompt_set_echo_off(1); + scanf("%s", evt->auth.password); + gf_prompt_set_echo_off(0); + return 1; + case GF_EVENT_SYS_COLORS: +#ifdef WIN32 + evt->sys_cols.sys_colors[0] = get_sys_col(COLOR_ACTIVEBORDER); + evt->sys_cols.sys_colors[1] = get_sys_col(COLOR_ACTIVECAPTION); + evt->sys_cols.sys_colors[2] = get_sys_col(COLOR_APPWORKSPACE); + evt->sys_cols.sys_colors[3] = get_sys_col(COLOR_BACKGROUND); + evt->sys_cols.sys_colors[4] = get_sys_col(COLOR_BTNFACE); + evt->sys_cols.sys_colors[5] = get_sys_col(COLOR_BTNHIGHLIGHT); + evt->sys_cols.sys_colors[6] = get_sys_col(COLOR_BTNSHADOW); + evt->sys_cols.sys_colors[7] = get_sys_col(COLOR_BTNTEXT); + evt->sys_cols.sys_colors[8] = get_sys_col(COLOR_CAPTIONTEXT); + evt->sys_cols.sys_colors[9] = get_sys_col(COLOR_GRAYTEXT); + evt->sys_cols.sys_colors[10] = get_sys_col(COLOR_HIGHLIGHT); + evt->sys_cols.sys_colors[11] = get_sys_col(COLOR_HIGHLIGHTTEXT); + evt->sys_cols.sys_colors[12] = get_sys_col(COLOR_INACTIVEBORDER); + evt->sys_cols.sys_colors[13] = get_sys_col(COLOR_INACTIVECAPTION); + evt->sys_cols.sys_colors[14] = get_sys_col(COLOR_INACTIVECAPTIONTEXT); + evt->sys_cols.sys_colors[15] = get_sys_col(COLOR_INFOBK); + evt->sys_cols.sys_colors[16] = get_sys_col(COLOR_INFOTEXT); + evt->sys_cols.sys_colors[17] = get_sys_col(COLOR_MENU); + evt->sys_cols.sys_colors[18] = get_sys_col(COLOR_MENUTEXT); + evt->sys_cols.sys_colors[19] = get_sys_col(COLOR_SCROLLBAR); + evt->sys_cols.sys_colors[20] = get_sys_col(COLOR_3DDKSHADOW); + evt->sys_cols.sys_colors[21] = get_sys_col(COLOR_3DFACE); + evt->sys_cols.sys_colors[22] = get_sys_col(COLOR_3DHIGHLIGHT); + evt->sys_cols.sys_colors[23] = get_sys_col(COLOR_3DLIGHT); + evt->sys_cols.sys_colors[24] = get_sys_col(COLOR_3DSHADOW); + evt->sys_cols.sys_colors[25] = get_sys_col(COLOR_WINDOW); + evt->sys_cols.sys_colors[26] = get_sys_col(COLOR_WINDOWFRAME); + evt->sys_cols.sys_colors[27] = get_sys_col(COLOR_WINDOWTEXT); + return 1; +#else + memset(evt->sys_cols.sys_colors, 0, sizeof(u32)*28); + return 1; +#endif + break; + } + return 0; +} + +GF_Config *loadconfigfile(char *filepath) +{ + GF_Config *cfg; + char *cfg_dir; + char szPath[GF_MAX_PATH]; + + if (filepath) { + cfg_dir = strrchr(szPath, '\\'); + if (!cfg_dir) cfg_dir = strrchr(szPath, '/'); + if (cfg_dir) { + char c = cfg_dir[0]; + cfg_dir[0] = 0; + cfg = gf_cfg_new(cfg_dir, cfg_dir+1); + cfg_dir[0] = c; + if (cfg) goto success; + } + } + +#ifdef WIN32 + GetModuleFileNameA(NULL, szPath, GF_MAX_PATH); + cfg_dir = strrchr(szPath, '\\'); + if (cfg_dir) cfg_dir[1] = 0; + + cfg = gf_cfg_new(szPath, "GPAC.cfg"); + if (cfg) goto success; + strcpy(szPath, "."); + cfg = gf_cfg_new(szPath, "GPAC.cfg"); + if (cfg) goto success; + strcpy(szPath, "."); + cfg = gf_cfg_new(szPath, "GPAC.cfg"); + if (cfg) goto success; + + GetModuleFileNameA(NULL, szPath, GF_MAX_PATH); + cfg_dir = strrchr(szPath, '\\'); + if (cfg_dir) cfg_dir[1] = 0; + cfg = create_default_config(szPath, "GPAC.cfg"); +#else + /*linux*/ + cfg_dir = getenv("HOME"); + if (cfg_dir) { + strcpy(szPath, cfg_dir); + } else { + fprintf(stdout, "WARNING: HOME env var not set - using current directory for config file\n"); + strcpy(szPath, "."); + } + cfg = gf_cfg_new(szPath, ".gpacrc"); + if (cfg) goto success; + fprintf(stdout, "GPAC config file not found in %s - creating new file\n", szPath); + cfg = create_default_config(szPath, ".gpacrc"); +#endif + if (!cfg) { + fprintf(stdout, "cannot create config file in %s directory\n", szPath); + return NULL; + } + success: + fprintf(stdout, "Using config file in %s directory\n", szPath); + return cfg; +} + +void list_modules(GF_ModuleManager *modules) +{ + u32 i; + fprintf(stdout, "\rAvailable modules:\n"); + for (i=0; isimulation_time)) { + Run = 0; + } + continue; + } + c = gf_prompt_get_char(); + +force_input: + switch (c) { + case 'q': + Run = 0; + break; + case 'Q': + str = gf_cfg_get_key(cfg_file, "Network", "SessionMigration"); + if (!str || !strcmp(str, "no")) + gf_cfg_set_key(cfg_file, "Network", "SessionMigration", "yes"); + fprintf(stdout, "Migrating session %s\n", the_url); + gf_term_disconnect(term); + break; + case 'o': + startup_file = 0; + gf_term_disconnect(term); + fprintf(stdout, "Enter the absolute URL\n"); + scanf("%s", the_url); + if (rti_file) init_rti_logs(rti_file, the_url, use_rtix); + gf_term_connect(term, the_url); + break; + case 'O': + gf_term_disconnect(term); + fprintf(stdout, "Enter the absolute URL to the playlist\n"); + scanf("%s", the_url); + playlist = fopen(the_url, "rt"); + if (playlist) { + fscanf(playlist, "%s", the_url); + fprintf(stdout, "Opening URL %s\n", the_url); + gf_term_connect(term, the_url); + } + break; + case '\n': + case 'N': + if (playlist) { + gf_term_disconnect(term); + + if (fscanf(playlist, "%s", the_url) == EOF) { + fprintf(stdout, "No more items - exiting\n"); + Run = 0; + } else { + fprintf(stdout, "Opening URL %s\n", the_url); + gf_term_connect(term, the_url); + } + } + break; + case 'P': + if (playlist) { + u32 count; + gf_term_disconnect(term); + scanf("%d", &count); + while (count) { + fscanf(playlist, "%s", the_url); + count--; + } + fprintf(stdout, "Opening URL %s\n", the_url); + gf_term_connect(term, the_url); + } + break; + case 'r': + if (is_connected) { + gf_term_disconnect(term); + gf_term_connect(term, startup_file ? gf_cfg_get_key(cfg_file, "General", "StartupFile") : the_url); + } + break; + + case 'D': + if (is_connected) gf_term_disconnect(term); + break; + + case 'p': + if (is_connected) { + Bool is_pause = gf_term_get_option(term, GF_OPT_PLAY_STATE); + fprintf(stdout, "[Status: %s]\n", is_pause ? "Playing" : "Paused"); + gf_term_set_option(term, GF_OPT_PLAY_STATE, is_pause ? GF_STATE_PLAYING : GF_STATE_PAUSED); + } + break; + case 's': + if (is_connected) { + gf_term_set_option(term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE); + fprintf(stdout, "Step time: "); + PrintTime(gf_term_get_time_in_ms(term)); + fprintf(stdout, "\n"); + } + break; + + case 'z': + if (!CanSeek || (Duration<=2000)) { + fprintf(stdout, "scene not seekable\n"); + } else { + Double res; + s32 seekTo; + fprintf(stdout, "Duration: "); + PrintTime(Duration); + res = gf_term_get_time_in_ms(term); + res *= 100; res /= (s64)Duration; + fprintf(stdout, " (current %.2f %%)\nEnter Seek percentage:\n", res); + if (scanf("%d", &seekTo) == 1) { + if (seekTo > 100) seekTo = 100; + res = (Double)(s64)Duration; res /= 100; res *= seekTo; + gf_term_play_from_time(term, (u64) (s64) res, 0); + } + } + break; + + case 't': + { + if (is_connected) { + fprintf(stdout, "Current Time: "); + PrintTime(gf_term_get_time_in_ms(term)); + fprintf(stdout, " - Duration: "); + PrintTime(Duration); + fprintf(stdout, "\n"); + } + } + break; + case 'w': + if (is_connected) PrintWorldInfo(term); + break; + case 'v': + if (is_connected) PrintODList(term); + break; + case 'i': + if (is_connected) { + u32 ID; + fprintf(stdout, "Enter OD ID (0 for main OD): "); + scanf("%d", &ID); + ViewOD(term, ID, (u32)-1); + } + break; + case 'j': + if (is_connected) { + u32 num; + fprintf(stdout, "Enter OD number (0 for main OD): "); + scanf("%d", &num); + ViewOD(term, (u32)-1, num); + } + break; + case 'b': + if (is_connected) ViewODs(term, 1); + break; + + case 'm': + if (is_connected) ViewODs(term, 0); + break; + + case 'l': + list_modules(user.modules); + break; + + case 'n': + if (is_connected) set_navigation(); + break; + case 'x': + if (is_connected) gf_term_set_option(term, GF_OPT_NAVIGATION_TYPE, 0); + break; + + case 'd': + if (is_connected) { + char file[GF_MAX_PATH], *sExt; + GF_Err e; + Bool xml_dump, std_out; + fprintf(stdout, "Enter file radical name (+\'.x\' for XML dumping) - \"std\" for stdout: "); + scanf("%s", file); + sExt = strrchr(file, '.'); + xml_dump = 0; + if (sExt) { + if (!stricmp(sExt, ".x")) xml_dump = 1; + sExt[0] = 0; + } + std_out = strnicmp(file, "std", 3) ? 0 : 1; + e = gf_term_dump_scene(term, std_out ? NULL : file, xml_dump, 0, NULL); + fprintf(stdout, "Dump done (%s)\n", gf_error_to_string(e)); + } + break; + + case 'c': + PrintGPACConfig(); + break; + case '3': + { + Bool use_3d = !gf_term_get_option(term, GF_OPT_USE_OPENGL); + if (gf_term_set_option(term, GF_OPT_USE_OPENGL, use_3d)==GF_OK) { + fprintf(stdout, "Using %s for 2D drawing\n", use_3d ? "OpenGL" : "2D rasterizer"); + } + } + break; + case 'k': + { + Bool opt = gf_term_get_option(term, GF_OPT_STRESS_MODE); + opt = !opt; + fprintf(stdout, "Turning stress mode %s\n", opt ? "on" : "off"); + gf_term_set_option(term, GF_OPT_STRESS_MODE, opt); + } + break; + case '4': gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3); break; + case '5': gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9); break; + case '6': gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN); break; + case '7': gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP); break; + + case 'C': + switch (gf_term_get_option(term, GF_OPT_MEDIA_CACHE)) { + case GF_MEDIA_CACHE_DISABLED: gf_term_set_option(term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_ENABLED); break; + case GF_MEDIA_CACHE_ENABLED: gf_term_set_option(term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISABLED); break; + case GF_MEDIA_CACHE_RUNNING: fprintf(stdout, "Streaming Cache is running - please stop it first\n"); continue; + } + switch (gf_term_get_option(term, GF_OPT_MEDIA_CACHE)) { + case GF_MEDIA_CACHE_ENABLED: fprintf(stdout, "Streaming Cache Enabled\n"); break; + case GF_MEDIA_CACHE_DISABLED: fprintf(stdout, "Streaming Cache Disabled\n"); break; + case GF_MEDIA_CACHE_RUNNING: fprintf(stdout, "Streaming Cache Running\n"); break; + } + break; + case 'S': + case 'A': + if (gf_term_get_option(term, GF_OPT_MEDIA_CACHE)==GF_MEDIA_CACHE_RUNNING) { + gf_term_set_option(term, GF_OPT_MEDIA_CACHE, (c=='S') ? GF_MEDIA_CACHE_DISABLED : GF_MEDIA_CACHE_DISCARD); + fprintf(stdout, "Streaming Cache stoped\n"); + } else { + fprintf(stdout, "Streaming Cache not running\n"); + } + break; + case 'R': + display_rti = !display_rti; + ResetCaption(); + break; + + case 'u': + { + GF_Err e; + char szCom[8192]; + fprintf(stdout, "Enter command to send:\n"); + fflush(stdin); + szCom[0] = 0; + scanf("%[^\t\n]", szCom); + e = gf_term_scene_update(term, NULL, szCom); + if (e) fprintf(stdout, "Processing command failed: %s\n", gf_error_to_string(e)); + } + break; + + case 'L': + { + char szLog[1024]; + fprintf(stdout, "Enter new log level:\n"); + scanf("%s", szLog); + gf_log_set_level(parse_log_level(szLog)); + } + break; + case 'T': + { + char szLog[1024]; + fprintf(stdout, "Enter new log tools:\n"); + scanf("%s", szLog); + gf_log_set_tools(parse_log_tools(szLog)); + } + break; + case 'g': + { + GF_SystemRTInfo rti; + gf_sys_get_rti(rti_update_time_ms, &rti, 0); + fprintf(stdout, "GPAC allocated memory "LLD"\n", rti.gpac_memory); + } + break; + case 'M': + { + u32 size; + fprintf(stdout, "Enter new video cache memory in kBytes (current %d):\n", gf_term_get_option(term, GF_OPT_VIDEO_CACHE_SIZE)); + scanf("%d", &size); + gf_term_set_option(term, GF_OPT_VIDEO_CACHE_SIZE, size); + } + break; + + case 'E': + gf_term_set_option(term, GF_OPT_RELOAD_CONFIG, 1); + break; + + case 'h': + PrintHelp(); + break; + default: + break; + } + } + + gf_term_disconnect(term); + if (rti_file) UpdateRTInfo("Disconnected\n"); + + fprintf(stdout, "Deleting terminal... "); + if (playlist) fclose(playlist); + gf_term_del(term); + fprintf(stdout, "OK\n"); + + fprintf(stdout, "Unloading modules... "); + gf_modules_del(user.modules); + fprintf(stdout, "OK\n"); + gf_cfg_del(cfg_file); + gf_sys_close(); + if (rti_logs) fclose(rti_logs); + if (logfile) fclose(logfile); + return 0; +} + + + + +void PrintWorldInfo(GF_Terminal *term) +{ + u32 i; + const char *title; + GF_List *descs; + descs = gf_list_new(); + title = gf_term_get_world_info(term, NULL, descs); + if (!title && !gf_list_count(descs)) { + fprintf(stdout, "No World Info available\n"); + } else { + fprintf(stdout, "\t%s\n", title ? title : "No title available"); + for (i=0; iobjectDescriptorID); + + count = gf_term_get_object_count(term, root_odm); + for (i=0; iobjectDescriptorID, + (odi.od_type==GF_STREAM_VISUAL) ? "Video" : (odi.od_type==GF_STREAM_AUDIO) ? "Audio" : "Systems"); + } +} + +void ViewOD(GF_Terminal *term, u32 OD_ID, u32 number) +{ + ODInfo odi; + u32 i, j, count, d_enum,id; + GF_Err e; + char code[5]; + NetStatCommand com; + GF_ObjectManager *odm, *root_odm = gf_term_get_root_object(term); + if (!root_odm) return; + + odm = NULL; + if ((!OD_ID && (number == (u32)(-1))) || + ((OD_ID == (u32)(-1)) && !number)) { + odm = root_odm; + if ((gf_term_get_object_info(term, odm, &odi) != GF_OK)) odm=NULL; + } else { + count = gf_term_get_object_count(term, root_odm); + for (i=0; iobjectDescriptorID == OD_ID)) break; + else if (i == (u32)(number-1)) break; + } + odm = NULL; + } + } + if (!odm) { + if (number == (u32)-1) fprintf(stdout, "cannot find OD with ID %d\n", OD_ID); + else fprintf(stdout, "cannot find OD with number %d\n", number); + return; + } + if (!odi.od) { + if (number == (u32)-1) fprintf(stdout, "Object %d not attached yet\n", OD_ID); + else fprintf(stdout, "Object #%d not attached yet\n", number); + return; + } + + if (!odi.od) { + fprintf(stdout, "Service not attached\n"); + return; + } + + if (odi.od->tag==GF_ODF_IOD_TAG) { + fprintf(stdout, "InitialObjectDescriptor %d\n", odi.od->objectDescriptorID); + fprintf(stdout, "Profiles and Levels: Scene %x - Graphics %x - Visual %x - Audio %x - OD %x\n", + odi.scene_pl, odi.graphics_pl, odi.visual_pl, odi.audio_pl, odi.OD_pl); + fprintf(stdout, "Inline Profile Flag %d\n", odi.inline_pl); + } else { + fprintf(stdout, "ObjectDescriptor %d\n", odi.od->objectDescriptorID); + } + + fprintf(stdout, "Object Duration: "); + if (odi.duration) { + PrintTime((u32) (odi.duration*1000)); + } else { + fprintf(stdout, "unknown"); + } + fprintf(stdout, "\n"); + + if (odi.owns_service) { + fprintf(stdout, "Service Handler: %s\n", odi.service_handler); + fprintf(stdout, "Service URL: %s\n", odi.service_url); + } + if (odi.codec_name) { + Float avg_dec_time; + switch (odi.od_type) { + case GF_STREAM_VISUAL: + fprintf(stdout, "Video Object: Width %d - Height %d\r\n", odi.width, odi.height); + fprintf(stdout, "Media Codec: %s\n", odi.codec_name); + if (odi.par) fprintf(stdout, "Pixel Aspect Ratio: %d:%d\n", (odi.par>>16)&0xFF, (odi.par)&0xFF); + break; + case GF_STREAM_AUDIO: + fprintf(stdout, "Audio Object: Sample Rate %d - %d channels\r\n", odi.sample_rate, odi.num_channels); + fprintf(stdout, "Media Codec: %s\n", odi.codec_name); + break; + case GF_STREAM_SCENE: + case GF_STREAM_PRIVATE_SCENE: + if (odi.width && odi.height) { + fprintf(stdout, "Scene Description - Width %d - Height %d\n", odi.width, odi.height); + } else { + fprintf(stdout, "Scene Description - no size specified\n"); + } + fprintf(stdout, "Scene Codec: %s\n", odi.codec_name); + break; + case GF_STREAM_TEXT: + if (odi.width && odi.height) { + fprintf(stdout, "Text Object: Width %d - Height %d\n", odi.width, odi.height); + } else { + fprintf(stdout, "Text Object: No size specified\n"); + } + fprintf(stdout, "Text Codec %s\n", odi.codec_name); + break; + } + + avg_dec_time = 0; + if (odi.nb_dec_frames) { + avg_dec_time = (Float) odi.total_dec_time; + avg_dec_time /= odi.nb_dec_frames; + } + fprintf(stdout, "\tBitrate over last second: %d kbps\n\tMax bitrate over one second: %d kbps\n\tAverage Decoding Time %.2f ms (%d max)\n\tTotal decoded frames %d\n", + (u32) odi.avg_bitrate/1024, odi.max_bitrate/1024, avg_dec_time, odi.max_dec_time, odi.nb_dec_frames); + } + if (odi.protection) fprintf(stdout, "Encrypted Media%s\n", (odi.protection==2) ? " NOT UNLOCKED" : ""); + + count = gf_list_count(odi.od->ESDescriptors); + fprintf(stdout, "%d streams in OD\n", count); + for (i=0; iESDescriptors, i); + + fprintf(stdout, "\nStream ID %d - Clock ID %d\n", esd->ESID, esd->OCRESID); + if (esd->dependsOnESID) fprintf(stdout, "\tDepends on Stream ID %d for decoding\n", esd->dependsOnESID); + + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: fprintf(stdout, "\tOD Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_OCR: fprintf(stdout, "\tOCR Stream\n"); break; + case GF_STREAM_SCENE: fprintf(stdout, "\tScene Description Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_VISUAL: + fprintf(stdout, "\tVisual Stream - media type: "); + switch (esd->decoderConfig->objectTypeIndication) { + case 0x20: fprintf(stdout, "MPEG-4\n"); break; + case 0x60: fprintf(stdout, "MPEG-2 Simple Profile\n"); break; + case 0x61: fprintf(stdout, "MPEG-2 Main Profile\n"); break; + case 0x62: fprintf(stdout, "MPEG-2 SNR Profile\n"); break; + case 0x63: fprintf(stdout, "MPEG-2 Spatial Profile\n"); break; + case 0x64: fprintf(stdout, "MPEG-2 High Profile\n"); break; + case 0x65: fprintf(stdout, "MPEG-2 422 Profile\n"); break; + case 0x6A: fprintf(stdout, "MPEG-1\n"); break; + case 0x6C: fprintf(stdout, "JPEG\n"); break; + case 0x6D: fprintf(stdout, "PNG\n"); break; + case 0x80: + memcpy(code, esd->decoderConfig->decoderSpecificInfo->data, 4); + code[4] = 0; + fprintf(stdout, "GPAC Intern (%s)\n", code); + break; + default: + fprintf(stdout, "Private Type (0x%x)\n", esd->decoderConfig->objectTypeIndication); + break; + } + break; + + case GF_STREAM_AUDIO: + fprintf(stdout, "\tAudio Stream - media type: "); + switch (esd->decoderConfig->objectTypeIndication) { + case 0x40: fprintf(stdout, "MPEG-4\n"); break; + case 0x66: fprintf(stdout, "MPEG-2 AAC Main Profile\n"); break; + case 0x67: fprintf(stdout, "MPEG-2 AAC LowComplexity Profile\n"); break; + case 0x68: fprintf(stdout, "MPEG-2 AAC Scalable Sampling Rate Profile\n"); break; + case 0x69: fprintf(stdout, "MPEG-2 Audio\n"); break; + case 0x6B: fprintf(stdout, "MPEG-1 Audio\n"); break; + case 0xA0: fprintf(stdout, "EVRC Audio\n"); break; + case 0xA1: fprintf(stdout, "SMV Audio\n"); break; + case 0xE1: fprintf(stdout, "QCELP Audio\n"); break; + case 0x80: + memcpy(code, esd->decoderConfig->decoderSpecificInfo->data, 4); + code[4] = 0; + fprintf(stdout, "GPAC Intern (%s)\n", code); + break; + default: + fprintf(stdout, "Private Type (0x%x)\n", esd->decoderConfig->objectTypeIndication); + break; + } + break; + case GF_STREAM_MPEG7: fprintf(stdout, "\tMPEG-7 Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_IPMP: fprintf(stdout, "\tIPMP Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_OCI: fprintf(stdout, "\tOCI Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_MPEGJ: fprintf(stdout, "\tMPEGJ Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_INTERACT: fprintf(stdout, "\tUser Interaction Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_TEXT: fprintf(stdout, "\tStreaming Text Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + default: fprintf(stdout, "Unknown Stream\r\n"); break; + } + + fprintf(stdout, "\tBuffer Size %d\n\tAverage Bitrate %d bps\n\tMaximum Bitrate %d bps\n", esd->decoderConfig->bufferSizeDB, esd->decoderConfig->avgBitrate, esd->decoderConfig->maxBitrate); + if (esd->slConfig->predefined==SLPredef_SkipSL) { + fprintf(stdout, "\tNot using MPEG-4 Synchronization Layer\n"); + } else { + fprintf(stdout, "\tStream Clock Resolution %d\n", esd->slConfig->timestampResolution); + } + if (esd->URLString) fprintf(stdout, "\tStream Location: %s\n", esd->URLString); + + /*check language*/ + if (esd->langDesc) { + u32 i=0; + char lan[4], *szLang; + lan[0] = esd->langDesc->langCode>>16; + lan[1] = (esd->langDesc->langCode>>8)&0xFF; + lan[2] = (esd->langDesc->langCode)&0xFF; + lan[3] = 0; + + if ((lan[0]=='u') && (lan[1]=='n') && (lan[2]=='d')) szLang = "Undetermined"; + else { + szLang = lan; + while (GF_ISO639_Lang[i]) { + if (GF_ISO639_Lang[i+2][0] && strstr(GF_ISO639_Lang[i+1], lan)) { + szLang = (char*) GF_ISO639_Lang[i]; + break; + } + i+=3; + } + } + fprintf(stdout, "\tStream Language: %s\n", szLang); + } + } + fprintf(stdout, "\n"); + /*check OCI (not everything interests us) - FIXME: support for unicode*/ + count = gf_list_count(odi.od->OCIDescriptors); + if (count) { + fprintf(stdout, "%d Object Content Information descriptors in OD\n", count); + for (i=0; iOCIDescriptors, i); + switch (desc->tag) { + case GF_ODF_SEGMENT_TAG: + { + GF_Segment *sd = (GF_Segment *) desc; + fprintf(stdout, "Segment Descriptor: Name: %s - start time %g sec - duration %g sec\n", sd->SegmentName, sd->startTime, sd->Duration); + } + break; + case GF_ODF_CC_NAME_TAG: + { + GF_CC_Name *ccn = (GF_CC_Name *)desc; + fprintf(stdout, "Content Creators:\n"); + for (j=0; jContentCreators); j++) { + GF_ContentCreatorInfo *ci = (GF_ContentCreatorInfo *) gf_list_get(ccn->ContentCreators, j); + if (!ci->isUTF8) continue; + fprintf(stdout, "\t%s\n", ci->contentCreatorName); + } + } + break; + + case GF_ODF_SHORT_TEXT_TAG: + { + GF_ShortTextual *std = (GF_ShortTextual *)desc; + fprintf(stdout, "Description:\n\tEvent: %s\n\t%s\n", std->eventName, std->eventText); + } + break; + default: + break; + } + } + fprintf(stdout, "\n"); + } + + switch (odi.status) { + case 0: fprintf(stdout, "Stoped - "); break; + case 1: fprintf(stdout, "Playing - "); break; + case 2: fprintf(stdout, "Paused - "); break; + case 3: fprintf(stdout, "Not setup yet\n"); return; + default: fprintf(stdout, "Setup Failed\n"); return; + } + if (odi.buffer>=0) fprintf(stdout, "Buffer: %d ms - ", odi.buffer); + else fprintf(stdout, "Not buffering - "); + fprintf(stdout, "Clock drift: %d ms\n", odi.clock_drift); + if (odi.db_unit_count) fprintf(stdout, "%d AU in DB\n", odi.db_unit_count); + if (odi.cb_max_count) fprintf(stdout, "Composition Buffer: %d CU (%d max)\n", odi.cb_unit_count, odi.cb_max_count); + fprintf(stdout, "\n"); + + if (odi.owns_service) { + const char *url; + u32 done, total, bps; + d_enum = 0; + while (gf_term_get_download_info(term, odm, &d_enum, &url, NULL, &done, &total, &bps)) { + if (d_enum==1) fprintf(stdout, "Current Downloads in service:\n"); + if (done && total) { + fprintf(stdout, "%s: %d / %d bytes (%.2f %%) - %.2f kBps\n", url, done, total, (100.0f*done)/total, ((Float)bps)/1024.0f); + } else { + fprintf(stdout, "%s: %.2f kbps\n", url, ((Float)8*bps)/1024.0f); + } + } + if (!d_enum) fprintf(stdout, "No Downloads in service\n"); + fprintf(stdout, "\n"); + } + d_enum = 0; + while (gf_term_get_channel_net_info(term, odm, &d_enum, &id, &com, &e)) { + if (e) continue; + if (!com.bw_down && !com.bw_up) continue; + + fprintf(stdout, "Stream ID %d statistics:\n", id); + if (com.multiplex_port) { + fprintf(stdout, "\tMultiplex Port %d - multiplex ID %d\n", com.multiplex_port, com.port); + } else { + fprintf(stdout, "\tPort %d\n", com.port); + } + fprintf(stdout, "\tPacket Loss Percentage: %.4f\n", com.pck_loss_percentage); + fprintf(stdout, "\tDown Bandwidth: %d bps\n", com.bw_down); + if (com.bw_up) fprintf(stdout, "\tUp Bandwidth: %d bps\n", com.bw_up); + if (com.ctrl_port) { + if (com.multiplex_port) { + fprintf(stdout, "\tControl Multiplex Port: %d - Control Multiplex ID %d\n", com.multiplex_port, com.ctrl_port); + } else { + fprintf(stdout, "\tControl Port: %d\n", com.ctrl_port); + } + fprintf(stdout, "\tDown Bandwidth: %d bps\n", com.ctrl_bw_down); + fprintf(stdout, "\tUp Bandwidth: %d bps\n", com.ctrl_bw_up); + } + fprintf(stdout, "\n"); + } +} + +void PrintODTiming(GF_Terminal *term, GF_ObjectManager *odm) +{ + ODInfo odi; + if (!odm) return; + + if (gf_term_get_object_info(term, odm, &odi) != GF_OK) return; + if (!odi.od) { + fprintf(stdout, "Service not attached\n"); + return; + } + + fprintf(stdout, "OD %d: ", odi.od->objectDescriptorID); + switch (odi.status) { + case 1: fprintf(stdout, "Playing - "); break; + case 2: fprintf(stdout, "Paused - "); break; + default: fprintf(stdout, "Stoped - "); break; + } + if (odi.buffer>=0) fprintf(stdout, "Buffer: %d ms - ", odi.buffer); + else fprintf(stdout, "Not buffering - "); + fprintf(stdout, "Clock drift: %d ms", odi.clock_drift); + fprintf(stdout, " - time: "); + PrintTime((u32) (odi.current_time*1000)); + fprintf(stdout, "\n"); +} + +void PrintODBuffer(GF_Terminal *term, GF_ObjectManager *odm) +{ + Float avg_dec_time; + ODInfo odi; + if (!odm) return; + + if (gf_term_get_object_info(term, odm, &odi) != GF_OK) return; + if (!odi.od) { + fprintf(stdout, "Service not attached\n"); + return; + } + + fprintf(stdout, "OD %d: ", odi.od->objectDescriptorID); + switch (odi.status) { + case 1: fprintf(stdout, "Playing"); break; + case 2: fprintf(stdout, "Paused"); break; + default: fprintf(stdout, "Stoped"); break; + } + if (odi.buffer>=0) fprintf(stdout, " - Buffer: %d ms", odi.buffer); + if (odi.db_unit_count) fprintf(stdout, " - DB: %d AU", odi.db_unit_count); + if (odi.cb_max_count) fprintf(stdout, " - CB: %d/%d CUs", odi.cb_unit_count, odi.cb_max_count); + + fprintf(stdout, "\n * %d decoded frames - %d dropped frames\n", odi.nb_dec_frames, odi.nb_droped); + avg_dec_time = 0; + if (odi.nb_dec_frames) { avg_dec_time = (Float) odi.total_dec_time; avg_dec_time /= odi.nb_dec_frames; } + fprintf(stdout, " * Avg Bitrate %d kbps (%d max) - Avg Decoding Time %.2f ms (%d max)\n", + (u32) odi.avg_bitrate/1024, odi.max_bitrate/1024, avg_dec_time, odi.max_dec_time); +} + +void ViewODs(GF_Terminal *term, Bool show_timing) +{ + u32 i, count; + GF_ObjectManager *odm, *root_odm = gf_term_get_root_object(term); + if (!root_odm) return; + + if (show_timing) { + PrintODTiming(term, root_odm); + } else { + PrintODBuffer(term, root_odm); + } + count = gf_term_get_object_count(term, root_odm); + for (i=0; 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 literal 0 HcmV?d00001 diff --git a/applications/osmo4_sym/aif/osmo4_icon_mask.bmp b/applications/osmo4_sym/aif/osmo4_icon_mask.bmp new file mode 100644 index 0000000000000000000000000000000000000000..cff9766729ff720fff5aed99d283b0aaf8a8f720 GIT binary patch literal 414 zcmY+=J&MCH5Cz~T3~^)d1;V;bvBj2KC6GRM$PxAiGNkY=au%5sdky6V3TX@xo_#a2 z7b1{8^fUI%98XsV8+)!h&d+5!l#4SNKgzU+%OdMYfJpG7X4GbaGb7;jyax^{V>>!; z(6lr+*wl3TMpdala2J{)+(;wiBfZn4hT@I#EmnV*{XjF%he*HPbhc++=7h>*zUGWp z<8Fe^zAI6RFLRWq*#G5WM&92ObMZkm&vXc0^PaliYaO}m_gdFnW2<$pb^rH)d0Kzg OK4rAFmmozpmvMj6lcPug literal 0 HcmV?d00001 diff --git a/applications/osmo4_sym/aif/osmo4_menu.bmp b/applications/osmo4_sym/aif/osmo4_menu.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f44d9b1afae601d554d0dabd8dd2d03bcd587ae1 GIT binary patch literal 3766 zcmc&$drXs86#s5j=-N_iX$32fsi26;qrv(B2?Fwn3fdYUxD}LOn=+inSTxZ{mbgC_ z6aGOH6U}1W7&--fp%YvL)V6lj#YB+0Mpqb=hbx6n8fn>nwyrB$YhizQ^L=0Lckeyt zcV73LlfJ%zO*w}M-x$2Hc*F4$DimLes=$6rgzI%K;PdxK9h1WE?>C=6uPH0LoSFH( zPM2DTDXP=?VDc1;Jl|1R12`00G#rlfFImKYyqNqpCtu`L-yW_`i z*zNWYO5q>A&GxFe_&Omi6!iy#@u09!BaxH~1mSc#{S)WFBq}OaD*a~T#yeA|YH^H4 zyDgLbFfuaYTLeGw?RLk`oi_=&PssDsRIOV5drnRcgTa_SeR^sTL_~b* z9O2>Nvu4eL6cC}JqQYc)nUc~15*n?!s;cpS;CFNk1O;6tq$@X9YqQyFYipr7bOv&C zbaZTN>>}rgiHVsvZ{Ff&-Q7z|zS6X8SpOQlT|rRVc? zU0v3(v9avz>=`p=KwAJNBqS_dx^&sHWg?MCC=@PUyf`W<3Pyn=%FD|Es8;s{1gJnR zm)B1Uzq8ZAVqGP~`1RNAU@$y992y!L5)uN<6B83xu3VXxmX@BLzIyfQl#~?60#m>n zFadrcEJ%uq?&FSfxplq0FT9H2Q~#4E9})6^!FXsg{RQgY-d=bZMuw^YU$bV-+O=!* z^77WLTbGrUwQAKWSOKEVoH=vJk|nOor`7h+XgWf4)z$YWj$crKEB!1rRpTNZ92|t_ zVSHE~Is~(n$=qmSu^LR$(=WMKJSRTw z9~M>*(j!OS=nuN8sw%h__Rq-3faVH?V$YsEa=9FH;3EX)&Yg<@ZE0z7gA^Ad9d*XX zPXb>cc#VJP&^e@>YKCc3)1j(2yl z;uBx5e?h0?3K$O_{Mjotq=5e+h1Xpy?%u5fIiG*sV)0%Gec~g9<>lQ#0_AX;pFHu> zQm9m_`1p8Sd!y0I?&HS;Or{3p`}e=O!S9Oc6CcNQIuo6K3H2vAxo&7^oNrOvQ4Wys z5CxtW-UTu|Y!iu4g}XvRuJ`neBlMKwL-;s*`0zbK?x2{iUw^@Dwz#Q}9Xo~)3=0dx z7iFiRp~1anHowfxZFbh*JJr?RJ?}c~2focVnxB7-5Oe|0laelKwe4_wM@I*05y}A? z0c0fPoVd6+i^YO1ovt%6u@Q&SvM7}rXB~1&_V&aNe2`eJuOyNNbRlT0==5f(^kQ;y zPC!5adI2;X7-+(#O$*q*{Y$CzW?&#{F8Z%lrBa3b|2E?IfB*17Gdk)xdQ`(=)&1Ll zBC8!Iq>M)Uj7IyMki8UjilQDO)!;x#$kpmpq*r@edc*4O_wIB4?* R^WL>d;lG#L#5X=Z{y!0U=#~Hg literal 0 HcmV?d00001 diff --git a/applications/osmo4_sym/aif/osmo4_menu_mask.bmp b/applications/osmo4_sym/aif/osmo4_menu_mask.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4f02c6757c8b1e4ff3a55180707705cd6b2eb08f GIT binary patch literal 294 zcmYMvF%H5o3 + +RESOURCE AIF_DATA + { + + app_uid = 0x1000AC00; + num_icons = 2; + embeddability = KAppNotEmbeddable; + newfile = KAppDoesNotSupportNewFile; + } + +// End of File diff --git a/applications/osmo4_sym/osmo4.cpp b/applications/osmo4_sym/osmo4.cpp new file mode 100644 index 0000000..07e5001 --- /dev/null +++ b/applications/osmo4_sym/osmo4.cpp @@ -0,0 +1,82 @@ +#include "osmo4.h" +#include "osmo4_ui.h" + + +EXPORT_C CApaApplication* NewApplication() + { + return new COsmo4Application; + } + + +#ifdef EKA2 + +#include + +GLDEF_C TInt E32Main() +{ + return EikStart::RunApplication( NewApplication ); +} + +#else + + +GLDEF_C TInt E32Dll( TDllReason /*aReason*/ ) +{ + return KErrNone; +} + +#endif + + +#if defined(__SERIES60_3X__) +const TUid KUidOsmo4App = { 0xf01f9075 }; +#else +const TUid KUidOsmo4App = { 0x1000AC00 }; +#endif + + +CApaDocument* COsmo4Application::CreateDocumentL() +{ + return (static_cast ( COsmo4Document::NewL( *this ) ) ); +} + +TUid COsmo4Application::AppDllUid() const +{ + return KUidOsmo4App; +} + + +COsmo4Document* COsmo4Document::NewL( CEikApplication& aApp ) +{ + COsmo4Document* self = NewLC( aApp ); + CleanupStack::Pop( self ); + return self; +} + +COsmo4Document* COsmo4Document::NewLC( CEikApplication& aApp ) +{ + COsmo4Document* self = + new ( ELeave ) COsmo4Document( aApp ); + + CleanupStack::PushL( self ); + self->ConstructL(); + return self; +} +void COsmo4Document::ConstructL() +{ +} + +COsmo4Document::COsmo4Document( CEikApplication& aApp ) + : CAknDocument( aApp ) +{ +} + +COsmo4Document::~COsmo4Document() +{ +} + +CEikAppUi* COsmo4Document::CreateAppUiL() +{ + return ( static_cast ( new ( ELeave ) COsmo4AppUi ) ); +} + diff --git a/applications/osmo4_sym/osmo4.h b/applications/osmo4_sym/osmo4.h new file mode 100644 index 0000000..e01c614 --- /dev/null +++ b/applications/osmo4_sym/osmo4.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2004, Nokia. All rights reserved */ + + +#ifndef __OSMO4_H__ +#define __OSMO4_H__ + +// INCLUDES +#include +#include + +class COsmo4Application : public CAknApplication +{ + public: + TUid AppDllUid() const; + + protected: + CApaDocument* CreateDocumentL(); + }; + + +class COsmo4Document : public CAknDocument + { + public: + static COsmo4Document* NewL( CEikApplication& aApp ); + static COsmo4Document* NewLC( CEikApplication& aApp ); + virtual ~COsmo4Document(); + + public: + CEikAppUi* CreateAppUiL(); + + private: + void ConstructL(); + COsmo4Document( CEikApplication& aApp ); + + }; + +#endif // __OSMO4_H__ + diff --git a/applications/osmo4_sym/osmo4_ui.cpp b/applications/osmo4_sym/osmo4_ui.cpp new file mode 100644 index 0000000..5a51f3c --- /dev/null +++ b/applications/osmo4_sym/osmo4_ui.cpp @@ -0,0 +1,604 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2006-200X + * Authors: Jean Le Feuvre + * All rights reserved + * + * This file is part of GPAC / Symbian GUI player + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +// INCLUDE FILES +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#include "osmo4_ui.h" +#include "osmo4_view.h" +#include "playlist.h" + +#include +#include + + + +// ============================ MEMBER FUNCTIONS =============================== + + +// ----------------------------------------------------------------------------- +// Cosmo4AppUi::ConstructL() +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void COsmo4AppUi::ConstructL() +{ + // Initialise app UI with standard value. + BaseConstructL(CAknAppUi::EAknEnableSkin); + + /*Create display*/ + iAppView = COsmo4AppView::NewL( ClientRect() ); + AddToStackL(iAppView); + + /*create playlist*/ +#ifndef GPAC_GUI_ONLY + iPlaylist = CPlaylist::NewL( ClientRect(), iAppView->GetUser() ); + + iPlaylist->MakeVisible(EFalse); +#endif + + iAppView->MakeVisible(ETrue); + view_mode = 0; + + m_title = NULL; + + //StatusPane ()->SwitchLayoutL ( R_AVKON_STATUS_PANE_LAYOUT_SMALL ); + + nb_keys = 0; + CaptureKeys(1); + + + + CCommandLineArguments *args = CCommandLineArguments::NewL(); +#ifndef GPAC_GUI_ONLY + if (args->Count() > 1) { + TPtrC url = args->Arg(1); +#if defined(_UNICODE) + char szURL[1024]; + u16 szURLUTF16[1024]; + size_t len; + len = url.Size(); + memcpy(szURLUTF16, url.Ptr(), sizeof(u8)*len); + szURLUTF16[len/2] = 0; + const u16 *sptr = szURLUTF16; + len = gf_utf8_wcstombs(szURL, 512, &sptr); + if (len != (size_t) -1) { + szURL[len] = 0; + iAppView->Connect((const char *)szURL); + } +#else + iAppView->Connect((const char *)url.Ptr()); +#endif + } +#endif + delete args; +} + +// ----------------------------------------------------------------------------- +// COsmo4AppUi::COsmo4AppUi() +// C++ default constructor can NOT contain any code, that might leave. +// ----------------------------------------------------------------------------- +// +COsmo4AppUi::COsmo4AppUi() + { + // No implementation required + } + +// ----------------------------------------------------------------------------- +// COsmo4AppUi::~COsmo4AppUi() +// Destructor. +// ----------------------------------------------------------------------------- +// +COsmo4AppUi::~COsmo4AppUi() +{ + CaptureKeys(0); + + switch (view_mode) { + case 0: + if (iAppView) RemoveFromStack(iAppView); + break; + case 1: + if (iPlaylist) RemoveFromStack(iPlaylist); + break; + } + if (iAppView) delete iAppView; + if (iPlaylist) delete iPlaylist; + if (m_title) free(m_title); + m_title = NULL; +} + + +void COsmo4AppUi::CaptureKey(TInt32 code, TInt32 scancode) +{ + RWindowGroup iWG = CCoeEnv::Static()->RootWin(); + if (nb_keys>=MAX_KEY_CAP) return; + keys[nb_keys].key_cap = iWG.CaptureKey(code, 0, 0); + keys[nb_keys].key_cap_ud = iWG.CaptureKeyUpAndDowns(scancode, 0, 0); + nb_keys++; +} +/* +possible meaning for key codes: +EStdKeyYes -Call +EStdKeyNo -End +EStdKeyApplication0 -Apps key +EStdKeyDevice0 -Left softkey +EStdKeyDevice1 -Right softkey +EStdKeyDevice2 -Power +EStdKeyDevice3 -Button press +EStdKeyDevice4 -Flip - Open +EStdKeyDevice5 -Flip - Close +EStdKeyDevice6 -Side key + +EStdKeyDeviceD -Jog Dial forward +EStdKeyDeviceE -Jog Dial back +*/ +void COsmo4AppUi::CaptureKeys(int do_capture) +{ + if (do_capture) { + CaptureKey(EKeyIncVolume, EStdKeyIncVolume); + CaptureKey(EKeyDecVolume, EStdKeyDecVolume); + } else { + RWindowGroup iWG = CCoeEnv::Static()->RootWin(); + for (int i=0; iShutdown(); + Exit(); + break; + /*PLAYLIST commands*/ + case EOsmo4PlayListAdd: + iPlaylist->PlaylistAct(Osmo4PLAdd); + break; + case EOsmo4PlayListRem: + iPlaylist->PlaylistAct(Osmo4PLRem); + break; + case EOsmo4PlayListMoveUp: + iPlaylist->PlaylistAct(Osmo4PLMoveUp); + break; + case EOsmo4PlayListMoveDown: + iPlaylist->PlaylistAct(Osmo4PLMoveDown); + break; + case EOsmo4PlayListClear: + iPlaylist->PlaylistAct(Osmo4PLClear); + break; + case EOsmo4PlayListMode: + iPlaylist->PlaylistAct(Osmo4PLToggleMode); + break; + case EOsmo4PlayListAllFiles: + iPlaylist->PlaylistAct(Osmo4PLToggleAllFiles); + break; + + /*FILE menu command*/ + case EOsmo4PlayListView: + TogglePlaylist(); + break; + case EOsmo4OpenURL: + break; + case EOsmo4Fullscreen: + break; + case EOsmo4ViewMaxSize: + { + CEikStatusPane* statusPane = StatusPane(); + if (statusPane->IsVisible()) statusPane->MakeVisible(EFalse); + else statusPane->MakeVisible(ETrue); + } + break; + case EOsmo4AROriginal: + gf_term_set_option(iAppView->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP); + break; + case EOsmo4ARFillScreen: + gf_term_set_option(iAppView->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN); + break; + case EOsmo4AR4_3: + gf_term_set_option(iAppView->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3); + break; + case EOsmo4AR16_9: + gf_term_set_option(iAppView->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9); + break; + + case EOsmo4NavReset: + gf_term_set_option(iAppView->m_term, GF_OPT_NAVIGATION_TYPE, 0); + break; + case EOsmo4NavNone: + gf_term_set_option(iAppView->m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_NONE); + break; + case EOsmo4NavSlide: + e = gf_term_set_option(iAppView->m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_SLIDE); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot set navigation: %s", gf_error_to_string(e) )); + } + break; + case EOsmo4NavWalk: + gf_term_set_option(iAppView->m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_WALK); + break; + case EOsmo4NavFly: + gf_term_set_option(iAppView->m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_FLY); + break; + case EOsmo4NavExamine: + gf_term_set_option(iAppView->m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_EXAMINE); + break; + case EOsmo4NavHeadlight: + gf_term_set_option(iAppView->m_term, GF_OPT_HEADLIGHT, !gf_term_get_option(iAppView->m_term, GF_OPT_HEADLIGHT) ); + break; + case EOsmo4CollideNone: + gf_term_set_option(iAppView->m_term, GF_OPT_COLLISION, GF_COLLISION_NONE); + break; + case EOsmo4CollideSimple: + gf_term_set_option(iAppView->m_term, GF_OPT_COLLISION, GF_COLLISION_NORMAL); + break; + case EOsmo4CollideDisp: + gf_term_set_option(iAppView->m_term, GF_OPT_COLLISION, GF_COLLISION_DISPLACEMENT); + break; + case EOsmo4NavGravity: + gf_term_set_option(iAppView->m_term, GF_OPT_GRAVITY, !gf_term_get_option(iAppView->m_term, GF_OPT_GRAVITY)); + break; + case EOsmo4ViewRTI: + iAppView->show_rti = !iAppView->show_rti; + break; + + case EOsmo4OptEnableLogs: + { + const char *opt = gf_cfg_get_key(iAppView->m_user.config, "General", "LogLevel"); + if (opt && !stricmp(opt, "debug")) { + gf_cfg_set_key(iAppView->m_user.config, "General", "LogLevel", "error"); + } else { + gf_cfg_set_key(iAppView->m_user.config, "General", "LogLevel", "debug"); + } + iAppView->SetupLogs(); + } + break; + case EOsmo4OptOpenGL: + { + const char *opt = gf_cfg_get_key(iAppView->m_user.config, "Compositor", "ForceOpenGL"); + Bool use_gl = (opt && !strcmp(opt, "yes")) ? 1 : 0; + gf_cfg_set_key(iAppView->m_user.config, "Compositor", "ForceOpenGL", use_gl ? "no" : "yes"); + gf_term_set_option(iAppView->m_term, GF_OPT_USE_OPENGL, !use_gl); + } + break; + case EOsmo4OptDirectDraw: + { + const char *opt = gf_cfg_get_key(iAppView->m_user.config, "Compositor", "DirectDraw"); + Bool use_dd = (opt && !strcmp(opt, "yes")) ? 1 : 0; + gf_cfg_set_key(iAppView->m_user.config, "Compositor", "DirectDraw", use_dd ? "no" : "yes"); + gf_term_set_option(iAppView->m_term, GF_OPT_DIRECT_DRAW, !use_dd); + } + break; + case EOsmo4OptXMLProgressive: + { + const char *opt = gf_cfg_get_key(iAppView->m_user.config, "SAXLoader", "Progressive"); + Bool use_prog = (opt && !strcmp(opt, "yes")) ? 1 : 0; + gf_cfg_set_key(iAppView->m_user.config, "SAXLoader", "Progressive", use_prog ? "no" : "yes"); + gf_cfg_set_key(iAppView->m_user.config, "SAXLoader", "MaxDuration", "100"); + } + break; + + default: + if ((aCommand>=EOsmo4OpenRecentFirst) && (aCommand<=EOsmo4OpenRecentLast)) { + const char *sOpt = gf_cfg_get_key_name(iAppView->m_user.config, "RecentFiles", aCommand - EOsmo4OpenRecentFirst); + if (sOpt) iAppView->Connect(sOpt); + } else { + iAppView->MessageBox("Unandled command - panic", "Osmo4"); + Panic( EOsmo4Ui ); + } + break; + } +#endif +} + + +// ----------------------------------------------------------------------------- +// Called by the framework when the application status pane +// size is changed. Passes the new client rectangle to the +// AppView +// ----------------------------------------------------------------------------- +// +void COsmo4AppUi::HandleStatusPaneSizeChange() +{ + iAppView->SetRect( ClientRect() ); +#ifndef GPAC_GUI_ONLY + iPlaylist->SetRect( ClientRect() ); +#endif +} + +void COsmo4AppUi::TogglePlaylist() +{ + CEikButtonGroupContainer* cba= CEikButtonGroupContainer::Current(); + +#ifndef GPAC_GUI_ONLY + switch (view_mode) { + case 0: + RemoveFromStack(iAppView); + iAppView->ShowHide(0); + AddToStackL(iPlaylist); + if (cba) { + cba->SetCommandSetL(R_AVKON_SOFTKEYS_OPTIONS_BACK); + cba->DrawDeferred(); + } + view_was_max = StatusPane()->IsVisible() ? 0 : 1; + if (view_was_max) StatusPane()->MakeVisible(ETrue); + iPlaylist->ShowHide(1); + view_mode = 1; + break; + case 1: + RemoveFromStack(iPlaylist); + iPlaylist->ShowHide(0); + AddToStackL(iAppView); + if (cba) { + cba->SetCommandSetL(R_AVKON_SOFTKEYS_OPTIONS_EXIT); + cba->DrawDeferred(); + } + iAppView->ShowHide(1); + if (view_was_max) StatusPane()->MakeVisible(EFalse); + view_was_max = 0; + view_mode = 0; + break; + } +#endif +} + +void COsmo4AppUi::PlayURL(const char *url) +{ + if (view_mode) { + TogglePlaylist(); + } + if (url) { + char *sep; + iAppView->Connect(url); + sep = strrchr(url, '\\'); + SetTitle(sep ? sep+1 : url); + } +} + +void COsmo4AppUi::SetTitleInfo(const char *title) +{ +#if 1 + CEikStatusPane* statusPane = StatusPane(); + CAknTitlePane *iTitlePane = (CAknTitlePane*) statusPane->ControlL(TUid::Uid(EEikStatusPaneUidTitle)); + + if (!title) title = "Osmo4"; + + HBufC *htitle = HBufC::NewL( strlen(title)+1); + htitle->Des().Copy( TPtrC8(( TText8* ) title) ); + iTitlePane->SetText(htitle); +#endif +} + +void COsmo4AppUi::SetTitle(const char *title, int store_it) +{ + if (store_it) { + if (m_title) free(m_title); + m_title = NULL; + if (title) m_title = strdup(title); + } + SetTitleInfo(title ? title : m_title); +} + +void COsmo4AppUi::SetInfo(const char *info) +{ + if (view_mode) return; + if (info) { + char szTitle[200]; + sprintf(szTitle, "%s\n%s", info, m_title ? m_title : "Osmo4"); + SetTitleInfo(szTitle); + } else { + SetTitleInfo(m_title); + } +} + + +#define DECLARE_MENU_ITEM(__text, __com, __check, __res, has_sep) \ + item.iText = __text; \ + item.iCommandId = __com; \ + item.iFlags = has_sep ? EEikMenuItemSeparatorAfter : 0; \ + if (__check) item.iFlags |= EEikMenuItemCheckBox | EEikMenuItemSymbolOn; \ + item.iCascadeId = __res; \ + aMenuPane->AddMenuItemL(item); + + +void COsmo4AppUi::DynInitMenuPaneL(TInt aResourceId, CEikMenuPane* aMenuPane) +{ + CEikMenuPaneItem::SData item; + + if (aResourceId==R_OSMO4_MENU) { + + aMenuPane->Reset(); + + if (view_mode==1) { +#ifndef GPAC_GUI_ONLY + Bool is_file = iPlaylist->SelectionIsFile(); + Bool in_pl = (is_file && iPlaylist->IsInPlaylist()) ? 1 : 0; + + if (iPlaylist->PlaylistMode()) { + DECLARE_MENU_ITEM(_L("Up"), EOsmo4PlayListMoveUp, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Down"), EOsmo4PlayListMoveDown, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Remove"), EOsmo4PlayListRem, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Clear"), EOsmo4PlayListClear, 0, 0, 1); + } else if (!in_pl) { + DECLARE_MENU_ITEM(_L("Add to PlayList"), EOsmo4PlayListAdd, 0, 0, 1); + } else if (is_file) { + DECLARE_MENU_ITEM(_L("Remove from Playlist"), EOsmo4PlayListRem, 0, 0, 1); + } + + if (! iPlaylist->PlaylistMode()) { + DECLARE_MENU_ITEM(iPlaylist->ViewAllFiles() ? _L("View known files") : _L("View all files"), EOsmo4PlayListAllFiles, 0, 0, 1); + } else { + DECLARE_MENU_ITEM(_L("Sort"), 0, 0, R_OSMO4_SM1, 1); + } + DECLARE_MENU_ITEM(iPlaylist->PlaylistMode() ? _L("Browse") : _L("Playlist"), EOsmo4PlayListMode, 0, 0, 0); +#endif + } else { + /*open*/ + DECLARE_MENU_ITEM(_L("File"), 0, 0, R_OSMO4_SM1, 0); + DECLARE_MENU_ITEM(_L("View"), 0, 0, R_OSMO4_SM2, 0); + DECLARE_MENU_ITEM(_L("Options"), 0, 0, R_OSMO4_SM3, 0); + //DECLARE_MENU_ITEM(_L("Exit"), EEikCmdExit, 0, 0, 0); + } + smenu_id = 0; + return; + } + else if (aResourceId==R_OSMO4_SM1) { + aMenuPane->Reset(); + /*sort menu*/ + if (view_mode==1) { + } + /*file menu*/ + else { + DECLARE_MENU_ITEM(_L("Open local"), EOsmo4PlayListView, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Open URL"), EOsmo4OpenURL, 0, 0, 1); +#ifndef GPAC_GUI_ONLY + if (gf_cfg_get_key_name(iAppView->m_user.config, "RecentFiles", 0) != NULL) { + DECLARE_MENU_ITEM(_L("Recent"), 0, 0, R_OSMO4_SSM1, 0); + } +#endif + } + smenu_id = 1; + return; + } + /*not used*/ + if (view_mode==1) return; + + /*View menu*/ + if (aResourceId==R_OSMO4_SM2) { + aMenuPane->Reset(); +#ifndef GPAC_GUI_ONLY + /*content view menu*/ + if (gf_term_get_option(iAppView->m_term, GF_OPT_NAVIGATION_TYPE) != GF_NAVIGATE_TYPE_NONE) { + DECLARE_MENU_ITEM(_L("Navigate"), 0, 0, R_OSMO4_SSM1, 1); + } +#endif + DECLARE_MENU_ITEM(_L("Fullscreen"), EOsmo4Fullscreen, 0, 0, 0); + /*don't allow content AR modification by user*/ + //DECLARE_MENU_ITEM(_L("Aspect Ratio"), 0, 0, R_OSMO4_SSM2, 1); + DECLARE_MENU_ITEM(_L("Maximize size"), EOsmo4ViewMaxSize, (StatusPane()->IsVisible() ? 0 : 1), 0, 1); + DECLARE_MENU_ITEM(_L("CPU Usage"), EOsmo4ViewRTI, iAppView->show_rti, 0, 0); + smenu_id = 2; + return; + } + /*Option menu*/ + if (aResourceId==R_OSMO4_SM3) { +#ifndef GPAC_GUI_ONLY + const char *opt = gf_cfg_get_key(iAppView->m_user.config, "Compositor", "ForceOpenGL"); + DECLARE_MENU_ITEM(_L("Use 2D OpenGL"), EOsmo4OptOpenGL, (opt && !strcmp(opt, "yes")) ? 1 : 0, 0, 0); + opt = gf_cfg_get_key(iAppView->m_user.config, "Compositor", "DirectDraw"); + DECLARE_MENU_ITEM(_L("Direct Draw"), EOsmo4OptDirectDraw, (opt && !strcmp(opt, "yes")) ? 1 : 0, 0, 0); + opt = gf_cfg_get_key(iAppView->m_user.config, "SAXLoader", "Progressive"); + DECLARE_MENU_ITEM(_L("Progressive XML"), EOsmo4OptXMLProgressive, (opt && !strcmp(opt, "yes")) ? 1 : 0, 0, 0); + +#endif + + DECLARE_MENU_ITEM(_L("Enable Logs"), EOsmo4OptEnableLogs, iAppView->do_log, 0, 0); + return; + } + + if (aResourceId==R_OSMO4_SSM1) { + aMenuPane->Reset(); + if (smenu_id == 1) { + u32 i = 0; +#ifndef GPAC_GUI_ONLY + while (1) { + const char *opt = gf_cfg_get_key_name(iAppView->m_user.config, "RecentFiles", i); + if (!opt) break; + const char *sep = strrchr(opt, '\\'); + if (!sep) sep = strrchr(opt, '/'); + if (!sep) sep = opt; + else sep += 1; + item.iText.Copy( TPtrC8(( TText8* ) sep) ); + item.iCommandId = EOsmo4OpenRecentFirst + i; + item.iFlags = 0; item.iCascadeId = 0; + aMenuPane->AddMenuItemL(item); + i++; + if (i>=10) break; + } + if (!i) { + DECLARE_MENU_ITEM(_L("_"), 0, 0, 0, 0); + } +#endif + } else if (smenu_id == 2) { + DECLARE_MENU_ITEM(_L("Reset"), EOsmo4NavReset, 0, 0, 1); + DECLARE_MENU_ITEM(_L("None"), EOsmo4NavNone, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Slide"), EOsmo4NavSlide, 0, 0, 0); + +#ifndef GPAC_GUI_ONLY + if (gf_term_get_option(iAppView->m_term, GF_OPT_NAVIGATION_TYPE) == GF_NAVIGATE_TYPE_3D) { + DECLARE_MENU_ITEM(_L("Walk"), EOsmo4NavWalk, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Fly"), EOsmo4NavFly, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Examine"), EOsmo4NavExamine, 0, 0, 1); + DECLARE_MENU_ITEM(_L("Headlight"), EOsmo4NavHeadlight, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Gravity"), EOsmo4NavGravity, 0, 0, 0); + } +#endif + } + return; + } + + if (aResourceId==R_OSMO4_SSM2) { + aMenuPane->Reset(); + DECLARE_MENU_ITEM(_L("Keep Original"), EOsmo4AROriginal, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Fill Screen"), EOsmo4ARFillScreen, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Ratio 4-3"), EOsmo4AR4_3, 0, 0, 0); + DECLARE_MENU_ITEM(_L("Ratio 16-9"), EOsmo4AR16_9, 0, 0, 0); + return; + } +} + diff --git a/applications/osmo4_sym/osmo4_ui.h b/applications/osmo4_sym/osmo4_ui.h new file mode 100644 index 0000000..06b3578 --- /dev/null +++ b/applications/osmo4_sym/osmo4_ui.h @@ -0,0 +1,181 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2006-200X + * Authors: Jean Le Feuvre + * All rights reserved + * + * This file is part of GPAC / Symbian GUI player + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __OSMO4_UI_H__ +#define __OSMO4_UI_H__ + +// INCLUDES +#include + + +// FORWARD DECLARATIONS +class COsmo4AppView; +class CPlaylist; + + +// osmo4 enumerate command codes +enum TOsmo4Ids +{ + /*Playlist commands*/ + EOsmo4PlayListView = 0x6001, + EOsmo4PlayListAdd, + EOsmo4PlayListRem, + EOsmo4PlayListMode, + EOsmo4PlayListMoveUp, + EOsmo4PlayListMoveDown, + EOsmo4PlayListClear, + EOsmo4PlayListAllFiles, + /*file commands*/ + EOsmo4OpenURL, + EOsmo4OpenRecentFirst, + EOsmo4OpenRecentLast = EOsmo4OpenRecentFirst + 10, + /*view commands*/ + EOsmo4Fullscreen, + EOsmo4ViewMaxSize, + EOsmo4AROriginal, + EOsmo4ARFillScreen, + EOsmo4AR4_3, + EOsmo4AR16_9, + EOsmo4NavReset, + EOsmo4NavNone, + EOsmo4NavSlide, + EOsmo4NavWalk, + EOsmo4NavFly, + EOsmo4NavExamine, + EOsmo4NavHeadlight, + EOsmo4NavGravity, + EOsmo4CollideNone, + EOsmo4CollideSimple, + EOsmo4CollideDisp, + EOsmo4ViewRTI, + + /*option commands*/ + EOsmo4OptEnableLogs, + EOsmo4OptOpenGL, + EOsmo4OptDirectDraw, + EOsmo4OptXMLProgressive, + + +}; + + +/** osmo4 application panic codes */ +enum TOsmo4Panics +{ + EOsmo4Ui = 1 + // add further panics here +}; + +inline void Panic(TOsmo4Panics aReason) +{ + _LIT(applicationName,"Osmo4"); + User::Panic(applicationName, aReason); +} + + +#define MAX_KEY_CAP 10 +typedef struct +{ + TInt32 key_cap; + TInt32 key_cap_ud; +} KeyCapInfo; + +// CLASS DECLARATION +/** +* COsmo4AppUi application UI class. +* Interacts with the user through the UI and request message processing +* from the handler class +*/ +class COsmo4AppUi : public CAknAppUi + { + public: // Constructors and destructor + + /** + * ConstructL. + * 2nd phase constructor. + */ + void ConstructL(); + + /** + * COsmo4AppUi. + * C++ default constructor. This needs to be public due to + * the way the framework constructs the AppUi + */ + COsmo4AppUi(); + + /** + * ~COsmo4AppUi. + * Virtual Destructor. + */ + virtual ~COsmo4AppUi(); + + private: // Functions from base classes + + /** + * From CEikAppUi, HandleCommandL. + * Takes care of command handling. + * @param aCommand Command to be handled. + */ + void HandleCommandL( TInt aCommand ); + + + /** + * HandleStatusPaneSizeChange. + * Called by the framework when the application status pane + * size is changed. + */ + + void HandleStatusPaneSizeChange(); + virtual void DynInitMenuPaneL(TInt aResourceId, CEikMenuPane* aMenuPane); + +public: + void PlayURL(const char *); + void SetTitle(const char *title, TBool store_it = ETrue); + void SetInfo(const char *); + +private: + void TogglePlaylist(); + void SetTitleInfo(const char *); + void HandleForegroundEventL(TBool aForeground); + void CaptureKeys(int do_capture); + void CaptureKey(TInt32 code, TInt32 scancode); + +private: + COsmo4AppView* iAppView; + CPlaylist *iPlaylist; + int view_was_max; + int smenu_id; + char *m_title; + /*current view mode*/ + int view_mode; + + KeyCapInfo keys[MAX_KEY_CAP]; + int nb_keys; + }; + +#endif // __OSMO4_UI_H__ + +// End of File + diff --git a/applications/osmo4_sym/osmo4_view.cpp b/applications/osmo4_sym/osmo4_view.cpp new file mode 100644 index 0000000..1acb350 --- /dev/null +++ b/applications/osmo4_sym/osmo4_view.cpp @@ -0,0 +1,610 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2006-200X + * Authors: Jean Le Feuvre + * All rights reserved + * + * This file is part of GPAC / Symbian GUI player + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +// INCLUDE FILES +#include +#include + +#include "osmo4_view.h" +#include "osmo4_ui.h" + +#include +/*for initial setup*/ +#include + + +#if defined(__SERIES60_3X__) +#define GPAC_CFG_DIR "\\private\\F01F9075\\" +#define GPAC_MODULES_DIR "\\sys\\bin\\" +#else +#define GPAC_CFG_DIR "\\system\\apps\\Osmo4\\" +#define GPAC_MODULES_DIR GPAC_CFG_DIR +#endif + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// Cosmo4AppView::NewL() +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +COsmo4AppView* COsmo4AppView::NewL( const TRect& aRect ) +{ + COsmo4AppView* self = COsmo4AppView::NewLC( aRect ); + CleanupStack::Pop( self ); + return self; +} + +// ----------------------------------------------------------------------------- +// COsmo4AppView::NewLC() +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +COsmo4AppView* COsmo4AppView::NewLC( const TRect& aRect ) +{ + COsmo4AppView* self = new ( ELeave ) COsmo4AppView; + CleanupStack::PushL( self ); + self->ConstructL( aRect ); + return self; +} + + + +// ----------------------------------------------------------------------------- +// COsmo4AppView::COsmo4AppView() +// C++ default constructor can NOT contain any code, that might leave. +// ----------------------------------------------------------------------------- +// +COsmo4AppView::COsmo4AppView() +{ + // No implementation required + m_pTimer = NULL; +#ifndef GPAC_GUI_ONLY + memset(&m_user, 0, sizeof(GF_User)); + m_term = NULL; + m_mx = NULL; + memset(&m_rti, 0, sizeof(GF_SystemRTInfo)); +#endif + last_title_update = 0; + show_rti = 0; +#if defined(__SERIES60_3X__) + selector = NULL; + target = NULL; +#endif +} + + + +// ----------------------------------------------------------------------------- +// COsmo4AppView::~COsmo4AppView() +// Destructor. +// ----------------------------------------------------------------------------- +// +COsmo4AppView::~COsmo4AppView() +{ + Shutdown(); +#ifndef GPAC_GUI_ONLY + if (m_mx) gf_mx_del(m_mx); +#endif + +#if defined(__SERIES60_3X__) + if (selector) delete selector; + //if (target) delete target; +#endif +} + +void COsmo4AppView::Shutdown() +{ +// MessageBox("Osmo4 shutdown request", ""); + if (m_pTimer) { + m_pTimer->Cancel(); + delete m_pTimer; + m_pTimer = NULL; + } +#ifndef GPAC_GUI_ONLY + if (m_term) { + GF_Terminal *t = m_term; + m_term = NULL; + gf_term_del(t); + } + if (m_user.config) { + gf_cfg_del(m_user.config); + m_user.config = NULL; + } + if (m_user.modules) { + gf_modules_del(m_user.modules); + m_user.modules = NULL; + } +#endif +// MessageBox("Osmo4 shutdown OK", ""); +} + +void COsmo4AppView::MessageBox(const char *text, const char *title) +{ + HBufC *msg1, *msg2; + TInt length = User::StringLength( ( TUint8* ) text) + 1; + msg1 = HBufC::NewL( length ); + msg1->Des().Copy( TPtrC8(( TText8* ) text) ); + + length = User::StringLength( ( TUint8* ) title) + 1; + msg2 = HBufC::NewL( length ); + msg2->Des().Copy( TPtrC8(( TText8* ) title) ); + + CEikonEnv::Static()->InfoWinL(*msg2, *msg1); + delete msg1; + delete msg2; +} + +TInt myTick(TAny* aObject) +{ + return ((COsmo4AppView*)aObject)->OnTick(); +} + +TInt COsmo4AppView::OnTick() +{ +#ifndef GPAC_GUI_ONLY + if (m_term) gf_term_process_step(m_term); + + /*check RTI display*/ + if (show_rti && gf_sys_get_rti(500, &m_rti, 0)) DisplayRTI(); +#endif + /*never stop...*/ + return 1; +} + +void COsmo4AppView::DisplayRTI() +{ +#ifndef GPAC_GUI_ONLY + COsmo4AppUi *app = (COsmo4AppUi *) CEikonEnv::Static()->AppUi(); + char szInfo[20]; + sprintf(szInfo, "CPU %02d FPS %02.2f", m_rti.process_cpu_usage, gf_term_get_framerate(m_term, 0)); + app->SetInfo(szInfo); +#endif +} + + +//GPAC log function +static void on_gpac_log(void *cbk, u32 ll, u32 lm, const char *fmt, va_list list) +{ + char szMsg[2048]; + COsmo4AppView *app = (COsmo4AppView *)cbk; + +#ifndef GPAC_GUI_ONLY + gf_mx_p(app->m_mx); + if (app->do_log) { + FILE *logs = fopen("\\data\\gpac_logs.txt", "a+t"); + if (logs) { + vfprintf(logs, fmt, list); + fclose(logs); + } + } else { + vsprintf(szMsg, fmt, list); + app->MessageBox(szMsg, "Error:"); + } + gf_mx_v(app->m_mx); +#endif +} + +static Bool GPAC_EventProc(void *ptr, GF_Event *evt) +{ + COsmo4AppView *app = (COsmo4AppView *)ptr; + return app->EventProc(evt); +} + +Bool COsmo4AppView::EventProc(GF_Event *evt) +{ + TRect r; + +#ifndef GPAC_GUI_ONLY + switch (evt->type) { + case GF_EVENT_MESSAGE: + if (!evt->message.message) return 0; + if (evt->message.error) { + char err[1024]; + sprintf(err, "Error: %s", gf_error_to_string(evt->message.error)); + MessageBox(evt->message.message, err); + } else { + MessageBox(evt->message.message, "Info"); + } + break; + case GF_EVENT_SCENE_SIZE: + r = Rect(); + gf_term_set_size(m_term, r.Width(), r.Height()); + break; + } +#endif + return 0; +} + +void COsmo4AppView::SetupLogs() +{ + const char *opt; + +#ifndef GPAC_GUI_ONLY + gf_mx_p(m_mx); + if (do_log) { + gf_log_set_level(0); + do_log = 0; + } + /*setup GPAC logs: log all errors*/ + opt = gf_cfg_get_key(m_user.config, "General", "LogLevel"); + if ((opt && !stricmp(opt, "debug")) /*|| 1*/) { + FILE *logs = fopen("\\data\\gpac_logs.txt", "wt"); + if (!logs) { + MessageBox("Cannot open log file - disabling logs", "Warning !"); + } else { + MessageBox("Debug log enabled in \\data\\gpac_logs.txt", "Info"); + fclose(logs); + do_log = 1; + gf_log_set_level(GF_LOG_DEBUG); + gf_log_set_tools(0xFFFFFFFF); + } + } + if (!do_log) { + gf_log_set_level(GF_LOG_ERROR); + gf_log_set_tools(0xFFFFFFFF); + } + + gf_log_set_callback(this, on_gpac_log); + gf_mx_v(m_mx); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Osmo4 logs initialized\n")); +#endif +} + + +static void Osmo4_progress_cbk(void *usr, char *title, u32 done, u32 total) +{ +#ifndef GPAC_GUI_ONLY + COsmo4AppView *view = (COsmo4AppView *) usr; + COsmo4AppUi *app = (COsmo4AppUi *) CEikonEnv::Static()->AppUi(); + + if (done==total) { + app->SetInfo(NULL); + } else if (view->last_title_update + 500 < gf_sys_clock()) { + char szName[1024]; + view->last_title_update = gf_sys_clock(); + sprintf(szName, "%s %02d %%", title, (done*100 / total) ); + app->SetInfo(szName); + } +#endif +} + +// ----------------------------------------------------------------------------- +// COsmo4AppView::ConstructL() +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void COsmo4AppView::ConstructL( const TRect& aRect ) +{ + const char *opt; + Bool first_launch = 0; + +#if defined(__SERIES60_3X__) + selector = CRemConInterfaceSelector::NewL(); + target = CRemConCoreApiTarget::NewL(*selector, *this); + selector->OpenTargetL(); +#endif + + // Create a window for this application view + CreateWindowL(); + // Set the windows size + SetRect( aRect ); + //draw + ActivateL(); + +#ifndef GPAC_GUI_ONLY + m_window = Window(); + m_session = CEikonEnv::Static()->WsSession(); + + m_mx = gf_mx_new("Osmo4"); + + //load config file + m_user.config = gf_cfg_new(GPAC_CFG_DIR, "GPAC.cfg"); + if (!m_user.config) { + first_launch = 1; + FILE *ft = fopen(GPAC_CFG_DIR"GPAC.cfg", "wt"); + if (!ft) { + MessageBox("Cannot create GPAC Config file", "Fatal Error"); + User::Leave(KErrGeneral); + } else { + fclose(ft); + } + m_user.config = gf_cfg_new(GPAC_CFG_DIR, "GPAC.cfg"); + if (!m_user.config) { + MessageBox("GPAC Configuration file not found", "Fatal Error"); + User::Leave(KErrGeneral); + } + } + + SetupLogs(); + gf_set_progress_callback(this, Osmo4_progress_cbk); + + opt = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + if (!opt) first_launch = 2; + + if (first_launch) { + /*hardcode module directory*/ + gf_cfg_set_key(m_user.config, "General", "ModulesDirectory", GPAC_MODULES_DIR); + /*hardcode cache directory*/ + gf_cfg_set_key(m_user.config, "General", "CacheDirectory", GPAC_CFG_DIR"cache"); + gf_cfg_set_key(m_user.config, "Downloader", "CleanCache", "yes"); + /*startup file*/ + gf_cfg_set_key(m_user.config, "General", "StartupFile", GPAC_CFG_DIR"gpac.mp4"); + /*setup UDP traffic autodetect*/ + gf_cfg_set_key(m_user.config, "Network", "AutoReconfigUDP", "yes"); + gf_cfg_set_key(m_user.config, "Network", "UDPTimeout", "10000"); + gf_cfg_set_key(m_user.config, "Network", "BufferLength", "3000"); + + gf_cfg_set_key(m_user.config, "Compositor", "TextureTextMode", "Default"); + + + /*save cfg and reload*/ + gf_cfg_del(m_user.config); + m_user.config = gf_cfg_new(GPAC_CFG_DIR, "GPAC.cfg"); + if (!m_user.config) { + MessageBox("Cannot save initial GPAC Config file", "Fatal Error"); + User::Leave(KErrGeneral); + } + + + MessageBox("Osmo4", "Thank you for Installing"); + } + + /*load modules*/ + opt = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + m_user.modules = gf_modules_new(opt, m_user.config); + if (!m_user.modules || !gf_modules_get_count(m_user.modules)) { + MessageBox(m_user.modules ? "No modules available" : "Cannot create module manager", "Fatal Error"); + if (m_user.modules) gf_modules_del(m_user.modules); + gf_cfg_del(m_user.config); + User::Leave(KErrGeneral); + } + + if (first_launch) { + /*first launch, register all files ext*/ + for (u32 i=0; iCanHandleURL(ifce, "test.test"); + gf_modules_close_interface((GF_BaseInterface *)ifce); + } + } + } + + /*we don't thread the terminal, ie appart from the audio renderer, media decoding and visual rendering is + handled by the app process*/ + m_user.init_flags = GF_TERM_NO_VISUAL_THREAD | GF_TERM_NO_REGULATION; + m_user.EventProc = GPAC_EventProc; + m_user.opaque = this; + m_user.os_window_handler = (void *) &m_window; + m_user.os_display = (void *) &m_session; + + m_term = gf_term_new(&m_user); + if (!m_term) { + MessageBox("Cannot load GPAC terminal", "Fatal Error"); + gf_modules_del(m_user.modules); + gf_cfg_del(m_user.config); + User::Leave(KErrGeneral); + } + //MessageBox("GPAC terminal loaded", "Success !"); + + /*ok set output size*/ + TSize s = m_window.Size(); + gf_term_set_size(m_term, s.iWidth, s.iHeight); + + + /*start our callback (every ms)*/ + const TInt KTickInterval = 33000; + m_pTimer = CPeriodic::NewL(CActive::EPriorityStandard); + m_pTimer->Start(KTickInterval, KTickInterval, TCallBack(myTick, this)); + + opt = gf_cfg_get_key(m_user.config, "General", "StartupFile"); + if (opt) gf_term_connect(m_term, opt); + +#endif + +} + + +// ----------------------------------------------------------------------------- +// COsmo4AppView::Draw() +// Draws the display. +// ----------------------------------------------------------------------------- +// +void COsmo4AppView::Draw( const TRect& /*aRect*/ ) const +{ +#ifndef GPAC_GUI_ONLY + if (!m_term) { + CWindowGc& gc = SystemGc(); + TRgb black(0,0,0); + gc.SetBrushColor(black); + gc.Clear(); + } else { + /*FIXME - this is just to force a screen flush, needs rework*/ + gf_term_set_option(m_term, GF_OPT_FREEZE_DISPLAY, 0); + } +#else + CWindowGc& gc = SystemGc(); + TRgb black(0,0,0); + TRect rect = Rect(); + gc.SetBrushColor(black); + gc.Clear(rect); +#endif +} + +void COsmo4AppView::ShowHide(Bool show) +{ +#ifndef GPAC_GUI_ONLY + if (show) { + MakeVisible(ETrue); + if (m_term) { + gf_term_set_option(m_term, GF_OPT_VISIBLE, 1); + DrawDeferred(); + } + } else { + MakeVisible(EFalse); + if (m_term) gf_term_set_option(m_term, GF_OPT_VISIBLE, 0); + } +#else + MakeVisible(ETrue); +#endif +} + +// ----------------------------------------------------------------------------- +// COsmo4AppView::SizeChanged() +// Called by framework when the view size is changed. +// ----------------------------------------------------------------------------- +// +void COsmo4AppView::SizeChanged() +{ +#ifndef GPAC_GUI_ONLY + if (m_term) { + TSize s = m_window.Size(); + gf_term_set_size(m_term, s.iWidth, s.iHeight); + } +#endif + DrawNow(); +} + +void COsmo4AppView::Connect(const char *url) +{ +#ifndef GPAC_GUI_ONLY + char the_url[1024]; + /*copy before removing from recent files*/ + strcpy(the_url, url); + gf_cfg_set_key(m_user.config, "RecentFiles", the_url, NULL); + gf_cfg_insert_key(m_user.config, "RecentFiles", the_url, "", 0); + u32 count = gf_cfg_get_key_count(m_user.config, "RecentFiles"); + if (count > 10) gf_cfg_set_key(m_user.config, "RecentFiles", gf_cfg_get_key_name(m_user.config, "RecentFiles", count-1), NULL); + + if (m_term) gf_term_connect(m_term, the_url); +#endif +} + + +TKeyResponse COsmo4AppView::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType) +{ + GF_Event evt; + u32 ret; + + evt.key.hw_code = aKeyEvent.iScanCode; + evt.key.flags = 0; + switch (aType) { + case EEventKeyUp: + evt.type = GF_EVENT_KEYUP; + break; + case EEventKeyDown: + case EEventKey: + evt.type = GF_EVENT_KEYDOWN; + break; + default: + return EKeyWasNotConsumed; + } + + switch (aKeyEvent.iCode) { + case EKeyLeftArrow: evt.key.key_code = GF_KEY_LEFT; break; + case EKeyRightArrow: evt.key.key_code = GF_KEY_RIGHT; break; + case EKeyUpArrow: evt.key.key_code = GF_KEY_UP; break; + case EKeyDownArrow: evt.key.key_code = GF_KEY_DOWN; break; + case EKeyIncVolume: evt.key.key_code = GF_KEY_VOLUMEUP; break; + case EKeyDecVolume: evt.key.key_code = GF_KEY_VOLUMEDOWN; break; + default: + switch (aKeyEvent.iScanCode) { + case EStdKeyIncVolume: evt.key.key_code = GF_KEY_VOLUMEUP; break; + case EStdKeyDecVolume: evt.key.key_code = GF_KEY_VOLUMEDOWN; break; + default: + return EKeyWasNotConsumed; + } + } +#ifndef GPAC_GUI_ONLY + ret = gf_term_user_event(m_term, &evt); + /*generate a key up*/ + if (aType==EEventKey) { + evt.type = GF_EVENT_KEYUP; + ret += gf_term_user_event(m_term, &evt); + } +#else + ret = 0; +#endif + return ret ? EKeyWasConsumed : EKeyWasNotConsumed; +} + +#if defined(__SERIES60_3X__) +void COsmo4AppView::MrccatoCommand(TRemConCoreApiOperationId aOperationId, TRemConCoreApiButtonAction aButtonAct) +{ + GF_Event e; + switch (aOperationId) { +/* + TRequestStatus status; + case ERemConCoreApiPausePlayFunction: + case ERemConCoreApiStop: + case ERemConCoreApiRewind: + case ERemConCoreApiForward: + case ERemConCoreApiFastForward: + case ERemConCoreApiBackward: + switch (aButtonAct) { + case ERemConCoreApiButtonPress: + break; + case ERemConCoreApiButtonRelease: + break; + case ERemConCoreApiButtonClick: + break; + default: + break; + } +*/ + case ERemConCoreApiVolumeUp: + case ERemConCoreApiVolumeDown: +#ifndef GPAC_GUI_ONLY + e.key.hw_code = 0; + e.key.flags = 0; + e.key.key_code = (aOperationId==ERemConCoreApiVolumeUp) ? GF_KEY_VOLUMEUP : GF_KEY_VOLUMEDOWN; + switch (aButtonAct) { + case ERemConCoreApiButtonPress: + e.type = GF_EVENT_KEYDOWN; + gf_term_user_event(m_term, &e); + break; + case ERemConCoreApiButtonRelease: + e.type = GF_EVENT_KEYUP; + gf_term_user_event(m_term, &e); + break; + default: + e.type = GF_EVENT_KEYDOWN; + gf_term_user_event(m_term, &e); + e.type = GF_EVENT_KEYUP; + gf_term_user_event(m_term, &e); + break; + } +#endif + break; + default: + break; + } +} + +#endif diff --git a/applications/osmo4_sym/osmo4_view.h b/applications/osmo4_sym/osmo4_view.h new file mode 100644 index 0000000..c0b3fd1 --- /dev/null +++ b/applications/osmo4_sym/osmo4_view.h @@ -0,0 +1,166 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2006-200X + * Authors: Jean Le Feuvre + * All rights reserved + * + * This file is part of GPAC / Symbian GUI player + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef __OSMO4_VIEW_H__ +#define __OSMO4_VIEW_H__ + +// INCLUDES +#include + +#if defined(__SERIES60_3X__) +#include +#include +#include +#endif + + +#include +#include + + +// CLASS DECLARATION +class COsmo4AppView : public CCoeControl +#if defined(__SERIES60_3X__) + ,MRemConCoreApiTargetObserver +#endif + + { + public: // New methods + + /** + * NewL. + * Two-phased constructor. + * Create a COsmo4AppView object, which will draw itself to aRect. + * @param aRect The rectangle this view will be drawn to. + * @return a pointer to the created instance of COsmo4AppView. + */ + static COsmo4AppView* NewL( const TRect& aRect ); + + /** + * NewLC. + * Two-phased constructor. + * Create a COsmo4AppView object, which will draw itself + * to aRect. + * @param aRect Rectangle this view will be drawn to. + * @return A pointer to the created instance of COsmo4AppView. + */ + static COsmo4AppView* NewLC( const TRect& aRect ); + + /** + * ~COsmo4AppView + * Virtual Destructor. + */ + virtual ~COsmo4AppView(); + + public: // Functions from base classes + + /** + * From CCoeControl, Draw + * Draw this COsmo4AppView to the screen. + * @param aRect the rectangle of this view that needs updating + */ + void Draw( const TRect& aRect ) const; + + /** + * From CoeControl, SizeChanged. + * Called by framework when the view size is changed. + */ + virtual void SizeChanged(); + +#ifndef GPAC_GUI_ONLY + GF_User *GetUser() { return &m_user; } +#else + GF_User *GetUser() { return NULL; } +#endif + void SetupLogs(); + void MessageBox(const char *text, const char *title); + + virtual TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType); + +#if defined(__SERIES60_3X__) + void MrccatoCommand(TRemConCoreApiOperationId aOperationId, TRemConCoreApiButtonAction aButtonAct); +#endif + + TInt OnTick(); + + void Shutdown(); + + void Connect(const char *url); + void ShowHide(Bool show); + Bool EventProc(GF_Event *evt); + +#ifndef GPAC_GUI_ONLY + GF_Terminal *m_term; +#endif + + private: // Constructors + + /** + * ConstructL + * 2nd phase constructor. + * Perform the second phase construction of a + * COsmo4AppView object. + * @param aRect The rectangle this view will be drawn to. + */ + void ConstructL(const TRect& aRect); + + void DisplayRTI(); + + /** + * COsmo4AppView. + * C++ default constructor. + */ + COsmo4AppView(); + + CPeriodic *m_pTimer; + + RWindow m_window; + RWsSession m_session; +#ifndef GPAC_GUI_ONLY + GF_SystemRTInfo m_rti; +#endif + +#if defined(__SERIES60_3X__) + CRemConInterfaceSelector *selector; + CRemConCoreApiTarget *target; +#endif + + +public: + u32 last_title_update; + Bool do_log; + Bool show_rti; +#ifndef GPAC_GUI_ONLY + GF_Mutex *m_mx; + GF_User m_user; +#endif + }; + + +#endif // __OSMO4_VIEW_H__ + +// End of File + diff --git a/applications/osmo4_sym/playlist.cpp b/applications/osmo4_sym/playlist.cpp new file mode 100644 index 0000000..26c7070 --- /dev/null +++ b/applications/osmo4_sym/playlist.cpp @@ -0,0 +1,527 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2006-200X + * Authors: Jean Le Feuvre + * All rights reserved + * + * This file is part of GPAC / Symbian GUI player + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + + +#include + +// INCLUDE FILES +#include "osmo4_ui.h" +#include "playlist.h" + +#ifdef USE_SKIN +#include +#include +#include // skin +#include //skin +#endif + + +CPlaylist::CPlaylist() +{ + playlist_mode = 0; + view_all_files = 0; +} +CPlaylist::~CPlaylist() +{ + delete iListBox; + delete iBackGround; +} + +CPlaylist* CPlaylist::NewL( const TRect& aRect, GF_User *user) +{ + CPlaylist* self = CPlaylist::NewLC( aRect, user); + CleanupStack::Pop( self ); + return self; +} +CPlaylist* CPlaylist::NewLC( const TRect& aRect, GF_User *user) +{ + CPlaylist* self = new ( ELeave ) CPlaylist; + CleanupStack::PushL( self ); + self->ConstructL( aRect, user); + return self; +} + +void CPlaylist::ConstructL(const TRect& aRect, GF_User *user) +{ + CreateWindowL(); + +#ifdef USE_SKIN + iListBox = new (ELeave) CAknSingleStyleListBox(); +#else + iListBox = new (ELeave) CEikTextListBox(); +#endif + iListBox->ConstructL(this); + iListBox->SetContainerWindowL(*this); + iListBox->SetListBoxObserver(this); + + CDesCArray* textArray = new (ELeave) CDesCArrayFlat(16); + iListBox->Model()->SetItemTextArray( textArray ); + iListBox->Model()->SetOwnershipType( ELbmOwnsItemArray ); + + // Creates scrollbar. + iListBox->CreateScrollBarFrameL( ETrue ); + iListBox->ScrollBarFrame()->SetScrollBarVisibilityL(CEikScrollBarFrame::EAuto, CEikScrollBarFrame::EAuto); + //iListBox->ActivateL(); + + iListBox->SetFocus(ETrue); + + SetRect(aRect); + ActivateL(); + MakeVisible(EFalse); + + strcpy(szCurrentDir, ""); + +#ifndef GPAC_GUI_ONLY + m_user = user; + + strcpy(ext_list, ""); + u32 count = gf_cfg_get_key_count(user->config, "MimeTypes"); + for (u32 i=0; iconfig, "MimeTypes", i); + const char *opt = gf_cfg_get_key(user->config, "MimeTypes", sMime); + strcpy(szKeyList, opt+1); + sKey = strrchr(szKeyList, '\"'); + if (!sKey) continue; + sKey[0] = 0; + strcat(ext_list, szKeyList); + strcat(ext_list, " "); + } + + const char *opt = gf_cfg_get_key(m_user->config, "General", "LastWorkingDir"); + if (opt) strcpy(szCurrentDir, opt); +#endif + +} + +void CPlaylist::SizeChanged() +{ + iListBox->SetRect( Rect() ); +} + +void CPlaylist::Draw(const TRect& aRect) const +{ +#ifdef USE_SKIN + CWindowGc& gc = SystemGc(); + MAknsSkinInstance* skin = AknsUtils::SkinInstance(); + MAknsControlContext* cc = AknsDrawUtils::ControlContext( this ); + AknsDrawUtils::Background( skin, cc, this, gc, aRect ); +#endif + +} + +#ifdef USE_SKIN +TTypeUid::Ptr CPlaylist::MopSupplyObject(TTypeUid aId) +{ + if(aId.iUid == MAknsControlContext::ETypeId && iBackGround) { + return MAknsControlContext::SupplyMopObject( aId, iBackGround); + } + return CCoeControl::MopSupplyObject( aId ); +} +#endif + + +TInt CPlaylist::CountComponentControls() const +{ + return 1; +} +CCoeControl* CPlaylist::ComponentControl(TInt aIndex) const +{ + switch (aIndex) { + case 0: return iListBox; + default: return NULL; + } +} + +TKeyResponse CPlaylist::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType) +{ + if (aType != EEventKey) return iListBox->OfferKeyEventL(aKeyEvent, aType); + + switch (aKeyEvent.iScanCode) { + case EStdKeyEnter: + HandleSelection(); + return EKeyWasConsumed; + default: + return iListBox->OfferKeyEventL(aKeyEvent, aType); + } +} +void CPlaylist::HandleListBoxEventL(CEikListBox* aListBox, TListBoxEvent aEventType ) +{ + if (aEventType == MEikListBoxObserver::EEventItemClicked || + aEventType == MEikListBoxObserver::EEventEnterKeyPressed) + + HandleSelection(); +} + +void CPlaylist::ShowHide(Bool show) +{ + if (show) { + RefreshPlaylist(); + MakeVisible(ETrue); + DrawNow(); + } else { + /*cleanup*/ + ResetView(); + MakeVisible(EFalse); + ((COsmo4AppUi *) CEikonEnv::Static()->AppUi())->SetTitle(NULL, 0); + } +} + + +void CPlaylist::FlushItemList() +{ + iListBox->HandleItemAdditionL(); + iListBox->SetCurrentItemIndexAndDraw(0); +} + +void CPlaylist::ResetView() +{ + CDesCArray* array = static_cast(iListBox->Model()->ItemTextArray()); + array->Reset(); + iListBox->Reset(); +} + +void CPlaylist::AddItem(const char *name, int is_directory) +{ + TBuf<100> tmp; + char szName[100]; + CDesCArray* array = static_cast(iListBox->Model()->ItemTextArray()); + + if (is_directory) { +#ifdef USE_SKIN + sprintf(szName, "\t+ %s\t\t", name); +#else + sprintf(szName, "+ %s", name); +#endif + } else { +#ifdef USE_SKIN + sprintf(szName, "\t%s\t\t", name); +#else + strcpy(szName, name); +#endif + } + tmp.SetLength(strlen(szName)+1); + tmp.Copy( TPtrC8(( TText8* ) szName) ); + tmp.ZeroTerminate(); + array->AppendL(tmp); +} + +static Bool enum_dirs(void *cbk, char *name, char *path) +{ + CPlaylist *of = (CPlaylist *)cbk; + of->AddItem(name, 1); + return 0; +} + +static Bool enum_files(void *cbk, char *name, char *path) +{ + CPlaylist *of = (CPlaylist *)cbk; + of->AddItem(name, 0); + return 0; +} + + +void CPlaylist::ScanDirectory(const char *dir) +{ + ResetView(); + + if (!dir || !strlen(dir)) { + RFs iFs; + TDriveList aList; + iFs.Connect(); + iFs.DriveList(aList); + for (TInt i=0;iAppUi())->SetTitle(szCurrentDir, 0); + +} + +void CPlaylist::GetSelectionName(char *szName) +{ + CDesCArray* array = static_cast(iListBox->Model()->ItemTextArray()); + TInt idx = iListBox->CurrentItemIndex(); + +#ifndef GPAC_GUI_ONLY + +#if defined(_UNICODE) + size_t len; + /*handle terminating zero !!*/ + u16 szNameUTF16[100]; + len = (*array)[idx].Size(); + memcpy(szNameUTF16, (*array)[idx].Ptr(), sizeof(u8)*len); + szNameUTF16[len/2] = 0; + const u16 *sptr = szNameUTF16; + + /*skip initial '\t'*/ +#ifdef USE_SKIN + sptr += 1; +#endif + + len = gf_utf8_wcstombs(szName, 512, &sptr); + szName[len] = 0; + + +#else + + char *src = (*array)[idx]).Ptr(); + /*skip initial '\t'*/ +#ifdef USE_SKIN + src += 1; +#endif + strcpy(szName, (const char *) src) ; +#endif + + /*remove trailing "\t\t"*/ +#ifdef USE_SKIN + len = strlen(szName); + szName[len-2] = 0; +#endif + +#else + szName[0] = 0; +#endif + +} + +void CPlaylist::HandleSelection() +{ + char szName[100]; + GetSelectionName(szName); + + /*sub-directory*/ + if ((szName[0] == '+') && (szName[1] == ' ')) { + /*browse up*/ + if ((szName[2] == '.') && (szName[3] == '.')) { + char *prev = strrchr(szCurrentDir, '\\'); + if (prev) { + prev[0] = 0; + ScanDirectory(szCurrentDir); + } else { + ScanDirectory(NULL); + } + } else { + strcat(szCurrentDir, "\\"); + strcat(szCurrentDir, szName+2); + ScanDirectory(szCurrentDir); + } + } else if (szName[1] == ':') { + ScanDirectory(szName); + } else { + char szURL[1024]; + COsmo4AppUi *app = (COsmo4AppUi *) CEikonEnv::Static()->AppUi(); + if (playlist_mode) { + TInt idx = iListBox->CurrentItemIndex(); +#ifndef GPAC_GUI_ONLY + const char *url = gf_cfg_get_key_name(m_user->config, "Playlist", idx); + if (url) app->PlayURL(url); +#endif + } else { + gf_cfg_set_key(m_user->config, "General", "LastWorkingDir", (const char *) szCurrentDir); + sprintf(szURL, "%s\\%s", szCurrentDir, szName); + app->PlayURL(szURL); + } + } +} + +Bool CPlaylist::SelectionIsFile() +{ + char szName[100]; + GetSelectionName(szName); + if ((szName[0] == '+') && (szName[1] == ' ')) return 0; + else if (szName[1] == ':') return 0; + return 1; +} + +Bool CPlaylist::IsInPlaylist() +{ + char szURL[1024]; + char szName[100]; + GetSelectionName(szName); + if ((szName[0] == '+') && (szName[1] == ' ')) return 0; + else if (szName[1] == ':') return 0; + + /*remove from playlist*/ + sprintf(szURL, "%s\\%s", szCurrentDir, szName); +#ifndef GPAC_GUI_ONLY + const char *opt = gf_cfg_get_key(m_user->config, "Playlist", szURL); + if (opt) return 1; +#endif + return 0; +} + +static Bool dir_add_files(void *cbk, char *name, char *path) +{ + CPlaylist *pl = (CPlaylist *)cbk; + +#if 0 + if (!bViewUnknownTypes && extension_list) { + char *ext = strrchr(name, '.'); + if (!ext || !strstr(extension_list, ext+1)) return 0; + } +#endif + +#ifndef GPAC_GUI_ONLY + gf_cfg_set_key(pl->m_user->config, "Playlist", path, ""); +#endif + + return 0; +} + + +void CPlaylist::RefreshPlaylist() +{ + if (playlist_mode) { +#ifndef GPAC_GUI_ONLY + u32 count = gf_cfg_get_key_count(m_user->config, "Playlist"); + ResetView(); + for (u32 i=0; iconfig, "Playlist", i); + const char *sep = strrchr(opt, '\\'); + if (!sep) sep = strrchr(opt, '/'); + AddItem(sep ? (sep+1) : opt, 0); + } + if (!count) AddItem("[empty]", 0); +#endif + FlushItemList(); + + ((COsmo4AppUi *) CEikonEnv::Static()->AppUi())->SetTitle("Playlist", 0); + } else { + ScanDirectory(szCurrentDir); + } +} + + +void CPlaylist::PlaylistAct(Osmo4_PLActions act) +{ + char szURL[1024]; + char szName[100]; + CDesCArray*array; + TInt idx; + TInt count; + + if (act==Osmo4PLClear) { + while (1) { +#ifndef GPAC_GUI_ONLY + const char *opt = gf_cfg_get_key_name(m_user->config, "Playlist", 0); + if (!opt) break; + gf_cfg_set_key(m_user->config, "Playlist", opt, NULL); +#endif + } + RefreshPlaylist(); + return; + } else if (act == Osmo4PLToggleMode) { + playlist_mode = !playlist_mode; + RefreshPlaylist(); + return; + } else if (act == Osmo4PLToggleAllFiles) { + view_all_files = !view_all_files; + RefreshPlaylist(); + return; + } else if (act == Osmo4PLAdd) { +#ifndef GPAC_GUI_ONLY + GetSelectionName(szName); + if ((szName[0] == '+') && (szName[1] == ' ')) { + if ((szName[2] != '.') && (szName[3] != '.')) { + sprintf(szURL, "%s\\%s", szCurrentDir, szName+2); + gf_enum_directory(szURL, 0, dir_add_files, this, view_all_files ? NULL : ext_list); + } + } else if (szName[1] == ':') { + gf_enum_directory(szName, 0, dir_add_files, this, view_all_files ? NULL : ext_list); + } else { + sprintf(szURL, "%s\\%s", szCurrentDir, szName); + gf_cfg_set_key(m_user->config, "Playlist", szURL, ""); + } +#endif + return; + } + + GetSelectionName(szName); + if ((szName[0] == '+') && (szName[1] == ' ')) return; + else if (szName[1] == ':') return; + + switch (act) { + /*remove from playlist*/ + case Osmo4PLRem: +#ifndef GPAC_GUI_ONLY + sprintf(szURL, "%s\\%s", szCurrentDir, szName); + gf_cfg_set_key(m_user->config, "Playlist", szURL, NULL); +#endif + RefreshPlaylist(); + break; + /*move up*/ + case Osmo4PLMoveUp: + array = static_cast(iListBox->Model()->ItemTextArray()); + count = array->Count(); + idx = iListBox->CurrentItemIndex(); + sprintf(szURL, "%s\\%s", szCurrentDir, szName); +#ifndef GPAC_GUI_ONLY + gf_cfg_set_key(m_user->config, "Playlist", szURL, NULL); + gf_cfg_insert_key(m_user->config, "Playlist", szURL, "", idx-1); +#endif + RefreshPlaylist(); + if (idx>1) iListBox->SetCurrentItemIndexAndDraw(idx-1); + break; + /*move down*/ + case Osmo4PLMoveDown: + array = static_cast(iListBox->Model()->ItemTextArray()); + count = array->Count(); + idx = iListBox->CurrentItemIndex(); + sprintf(szURL, "%s\\%s", szCurrentDir, szName); +#ifndef GPAC_GUI_ONLY + gf_cfg_set_key(m_user->config, "Playlist", szURL, NULL); + gf_cfg_insert_key(m_user->config, "Playlist", szURL, "", idx+1); +#endif + RefreshPlaylist(); + if (idxSetCurrentItemIndexAndDraw(idx+1); + break; + default: + break; + } +} + diff --git a/applications/osmo4_sym/playlist.h b/applications/osmo4_sym/playlist.h new file mode 100644 index 0000000..6e34852 --- /dev/null +++ b/applications/osmo4_sym/playlist.h @@ -0,0 +1,110 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2006-200X + * Authors: Jean Le Feuvre + * All rights reserved + * + * This file is part of GPAC / Symbian GUI player + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __osmo4playlist_H__ +#define __osmo4playlist_H__ + +#include +#include +#include + +#define USE_SKIN + +#ifdef USE_SKIN +class MAknsControlContext; // for skins support +#endif + + +#include + +class CEikTextListBox; //For list box + +enum Osmo4_PLActions +{ + Osmo4PLAdd = 0, + Osmo4PLRem, + Osmo4PLClear, + Osmo4PLMoveUp, + Osmo4PLMoveDown, + Osmo4PLToggleMode, + Osmo4PLToggleAllFiles, +}; + +class CPlaylist : public CCoeControl,MEikListBoxObserver +{ +public: + static CPlaylist* NewL( const TRect& aRect, GF_User *user); + static CPlaylist* NewLC( const TRect& aRect, GF_User *user); + virtual ~CPlaylist(); + void SizeChanged(); + + virtual TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType); + + void HandleListBoxEventL(CEikListBox* aListBox, TListBoxEvent aEventType ); + + void AddItem(const char *name, int is_directory); + TInt CountComponentControls() const; + CCoeControl* ComponentControl(TInt aIndex) const; + void Draw(const TRect& aRect) const; + + void ShowHide(Bool show); + Bool SelectionIsFile(); + Bool IsInPlaylist(); + Bool PlaylistMode() { return playlist_mode; } + Bool ViewAllFiles() { return view_all_files; } + + void PlaylistAct(Osmo4_PLActions act); + +#ifndef GPAC_GUI_ONLY + GF_User *m_user; +#endif + +private: + void ConstructL(const TRect& aRect, GF_User *user); + CPlaylist(); + + +#ifdef USE_SKIN + TTypeUid::Ptr MopSupplyObject(TTypeUid aId); + MAknsControlContext* iBackGround; +#endif + + + void ResetView(); + void FlushItemList(); + void ScanDirectory(const char *dir); + void HandleSelection(); + void GetSelectionName(char *name); + void RefreshPlaylist(); + + char szCurrentDir[1024]; + CEikTextListBox* iListBox; + Bool playlist_mode; + Bool view_all_files; + char ext_list[4096]; +}; + +#endif //__osmo4playlist_H__ + diff --git a/applications/osmo4_sym/res/osmo4.rss b/applications/osmo4_sym/res/osmo4.rss new file mode 100644 index 0000000..b73da36 --- /dev/null +++ b/applications/osmo4_sym/res/osmo4.rss @@ -0,0 +1,19 @@ +#include +#include "osmo4_gen.rss" + +rls_string STRING_osmo_caption_string "Osmo4" + +RESOURCE LOCALISABLE_APP_INFO r_osmo4_localisable_app_info + { + short_caption = STRING_osmo_caption_string; + caption_and_icon = + CAPTION_AND_ICON_INFO + { + caption = STRING_osmo_caption_string; + + number_of_icons = 1; + icon_file = "\\resource\\apps\\osmo4_aif.mif"; + }; + } + +// End of File diff --git a/applications/osmo4_sym/res/osmo4.svg b/applications/osmo4_sym/res/osmo4.svg new file mode 100644 index 0000000..a8fa8b0 --- /dev/null +++ b/applications/osmo4_sym/res/osmo4.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/osmo4_sym/res/osmo4_caption.rss b/applications/osmo4_sym/res/osmo4_caption.rss new file mode 100644 index 0000000..6a6a0a0 --- /dev/null +++ b/applications/osmo4_sym/res/osmo4_caption.rss @@ -0,0 +1,21 @@ +/* Copyright (c) 2004, Nokia. All rights reserved */ + + +// INCLUDES +#include + + +// RESOURCE DEFINITIONS +// ----------------------------------------------------------------------------- +// +// Caption data for Osmo4 +// +// ----------------------------------------------------------------------------- +// +RESOURCE CAPTION_DATA + { + caption="Osmo4"; + shortcaption= "Osmo4"; + } + +// End of File diff --git a/applications/osmo4_sym/res/osmo4_gen.rss b/applications/osmo4_sym/res/osmo4_gen.rss new file mode 100644 index 0000000..a1cb490 --- /dev/null +++ b/applications/osmo4_sym/res/osmo4_gen.rss @@ -0,0 +1,59 @@ +/* Copyright (c) 2004, Nokia. All rights reserved */ + +// RESOURCE IDENTIFIER +NAME OSMO // 4 letter ID + + +// INCLUDES +#include +#include +#include + +RESOURCE RSS_SIGNATURE + { + } + +RESOURCE TBUF r_default_document_name + { + buf="OSMO"; + } + +RESOURCE EIK_APP_INFO + { + menubar = r_Osmo4_menubar; + cba = R_AVKON_SOFTKEYS_OPTIONS_EXIT; + } + + +RESOURCE MENU_BAR r_Osmo4_menubar + { + titles = + { + MENU_TITLE { menu_pane = r_Osmo4_menu; } + }; + } + + +RESOURCE MENU_PANE r_Osmo4_menu + { + } + +RESOURCE MENU_PANE r_osmo4_sm1 + { + } + +RESOURCE MENU_PANE r_osmo4_sm2 + { + } + +RESOURCE MENU_PANE r_osmo4_sm3 + { + } + +RESOURCE MENU_PANE r_osmo4_ssm1 + { + } +RESOURCE MENU_PANE r_osmo4_ssm2 + { + } + diff --git a/applications/osmo4_sym/res/osmo4_reg.rss b/applications/osmo4_sym/res/osmo4_reg.rss new file mode 100644 index 0000000..eba4e8a --- /dev/null +++ b/applications/osmo4_sym/res/osmo4_reg.rss @@ -0,0 +1,31 @@ +/* +* ============================================================================== +* Name : osmo4_reg.rss +* Part of : osmo4 +* Interface : +* Description : +* Version : +* +* Copyright (c) 2005-2006 Nokia Corporation. +* This material, including documentation and any related +* computer programs, is protected by copyright controlled by +* Nokia Corporation. +* ============================================================================== +*/ + +#include +#include + +UID2 KUidAppRegistrationResourceFile +UID3 0xF01F9075 + +RESOURCE APP_REGISTRATION_INFO + { + app_file="Osmo4"; + localisable_resource_file = "\\resource\\apps\\Osmo4"; + localisable_resource_id = R_OSMO4_LOCALISABLE_APP_INFO; + + embeddability=KAppNotEmbeddable; + newfile=KAppDoesNotSupportNewFile; + } + diff --git a/applications/osmo4_w32/AddressBar.cpp b/applications/osmo4_w32/AddressBar.cpp new file mode 100644 index 0000000..7832fb6 --- /dev/null +++ b/applications/osmo4_w32/AddressBar.cpp @@ -0,0 +1,182 @@ +// AddressBar.cpp : implementation file +// + +#include "stdafx.h" +#include "osmo4.h" +#include "MainFrm.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// AddressBar dialog + +IMPLEMENT_DYNAMIC(CInitDialogBar, CDialogBar) + +BEGIN_MESSAGE_MAP(CInitDialogBar, CDialogBar) +END_MESSAGE_MAP() + + +CInitDialogBar::CInitDialogBar() +{ +} + +CInitDialogBar::~CInitDialogBar() +{ +} + +BOOL CInitDialogBar::Create(CWnd * pParentWnd, LPCTSTR lpszTemplateName, UINT nStyle, UINT nID) +{ + if(!CDialogBar::Create(pParentWnd, lpszTemplateName, nStyle, nID)) + return FALSE; + + if (!OnInitDialog()) return FALSE; + + return TRUE; +} + +BOOL CInitDialogBar::Create(CWnd * pParentWnd, UINT nIDTemplate, UINT nStyle, UINT nID) +{ + if(!Create(pParentWnd, MAKEINTRESOURCE(nIDTemplate), nStyle, nID)) return FALSE; + + if(!OnInitDialog()) return FALSE; + return TRUE; +} + + +BOOL CInitDialogBar::OnInitDialog() +{ + UpdateData(FALSE); + return TRUE; +} + +void CInitDialogBar::DoDataExchange(CDataExchange* pDX) +{ + CDialogBar::DoDataExchange(pDX); +} + + + +IMPLEMENT_DYNAMIC(AddressBar, CInitDialogBar) + + +BEGIN_MESSAGE_MAP(AddressBar, CInitDialogBar) + //{{AFX_MSG_MAP(AddressBar) + ON_WM_SIZE() + ON_WM_CLOSE() + ON_CBN_SELENDOK(IDC_ADDRESS, OnSelendOK) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +AddressBar::AddressBar () : CInitDialogBar() +{ +} + +BOOL AddressBar::OnInitDialog() +{ + CInitDialogBar::OnInitDialog(); + + return TRUE; +} + + +void AddressBar::DoDataExchange(CDataExchange* pDX) +{ + CInitDialogBar::DoDataExchange(pDX); + //{{AFX_DATA_MAP(AddressBar) + DDX_Control(pDX, IDC_DUMTXT, m_Title); + DDX_Control(pDX, IDC_ADDRESS, m_Address); + //}}AFX_DATA_MAP +} + + +///////////////////////////////////////////////////////////////////////////// +// AddressBar message handlers + +void AddressBar::OnSize(UINT nType, int cx, int cy) +{ + u32 w; + POINT pt; + //CDialog::OnSize(nType, cx, cy); + + if (!m_Address.m_hWnd) return; + RECT rc; + m_Title.GetClientRect(&rc); + w = rc.right - rc.left; + m_Address.GetWindowRect(&rc); + pt.x = rc.left; + pt.y = rc.top; + ScreenToClient(&pt); + rc.right = cx - pt.x; + m_Address.SetWindowPos(this, 0, 0, rc.right, rc.bottom, SWP_NOZORDER | SWP_NOMOVE); + +} + +void AddressBar::OnClose() +{ +} + +void AddressBar::ReloadURLs() +{ + Osmo4 *gpac = GetApp(); + u32 i=0; + + while (m_Address.GetCount()) m_Address.DeleteString(0); + while (1) { + const char *sOpt = gf_cfg_get_key_name(gpac->m_user.config, "RecentFiles", i); + if (!sOpt) return; + m_Address.AddString(sOpt); + i++; + } +} + +void AddressBar::SelectionReady() +{ + void UpdateLastFiles(GF_Config *cfg, const char *URL); + + CString URL; + int sel = m_Address.GetCurSel(); + if (sel == CB_ERR) { + m_Address.GetWindowText(URL); + } else { + m_Address.GetLBText(sel, URL); + } + if (!URL.GetLength()) return; + Osmo4 *gpac = GetApp(); + Playlist *pl = ((CMainFrame*)gpac->m_pMainWnd)->m_pPlayList; + /*don't store local files*/ + if (URL.Find("://", 0)>0) { + UpdateLastFiles(gpac->m_user.config, URL); + ReloadURLs(); + } + pl->Truncate(); + pl->QueueURL(URL); + pl->RefreshList(); + pl->PlayNext(); +} + +void AddressBar::OnSelendOK() +{ + SelectionReady(); +} + +BOOL AddressBar::PreTranslateMessage(MSG* pMsg) +{ + if (pMsg->message == WM_KEYDOWN) { + switch (pMsg->wParam) { + case VK_RETURN: + ::TranslateMessage(pMsg); + ::DispatchMessage(pMsg); + SelectionReady(); + return TRUE; + default: + break; + } + } + return CInitDialogBar::PreTranslateMessage(pMsg); +} + diff --git a/applications/osmo4_w32/AddressBar.h b/applications/osmo4_w32/AddressBar.h new file mode 100644 index 0000000..2048fe9 --- /dev/null +++ b/applications/osmo4_w32/AddressBar.h @@ -0,0 +1,90 @@ +#if !defined(AFX_ADDRESSBAR_H__B0764C99_5CC2_4412_8B1F_22E71BAD70F0__INCLUDED_) +#define AFX_ADDRESSBAR_H__B0764C99_5CC2_4412_8B1F_22E71BAD70F0__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// AddressBar.h : header file +// + + +///////////////////////////////////////////////////////////////////////////// +// AddressBar dialog + +class CInitDialogBar : public CDialogBar +{ + DECLARE_DYNAMIC(CInitDialogBar) + +// Construction +public: + CInitDialogBar(); + virtual ~CInitDialogBar(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CInitDialogBar) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +public: + virtual BOOL Create(CWnd * pParentWnd, UINT nIDTemplate, UINT nStyle, UINT nID); + virtual BOOL Create(CWnd * pParentWnd, LPCTSTR lpszTemplateName, UINT nStyle, UINT nID); + +protected: + virtual BOOL OnInitDialog(); + +protected: + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// +// AddressBar dialog + +class AddressBar : public CInitDialogBar +{ + + DECLARE_DYNAMIC(AddressBar) + + // Construction +public: + AddressBar(); + +// Dialog Data + //{{AFX_DATA(AddressBar) + enum { IDD = IDD_NAVBAR }; + CStatic m_Title; + CComboBox m_Address; + //}}AFX_DATA + + void ReloadURLs(); + void SelectionReady(); + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(AddressBar) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL PreTranslateMessage(MSG* pMsg); + //virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(AddressBar) + virtual BOOL OnInitDialog(); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnClose(); + afx_msg void OnSelendOK(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ADDRESSBAR_H__B0764C99_5CC2_4412_8B1F_22E71BAD70F0__INCLUDED_) diff --git a/applications/osmo4_w32/FileProps.cpp b/applications/osmo4_w32/FileProps.cpp new file mode 100644 index 0000000..bf913d8 --- /dev/null +++ b/applications/osmo4_w32/FileProps.cpp @@ -0,0 +1,710 @@ +// FileProps.cpp : implementation file +// + +#include "stdafx.h" +#include "osmo4.h" +#include "FileProps.h" +#include "MainFrm.h" + +/*ISO 639 languages*/ +#include + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CFileProps dialog + + +CFileProps::CFileProps(CWnd* pParent /*=NULL*/) + : CDialog(CFileProps::IDD, pParent) +{ + //{{AFX_DATA_INIT(CFileProps) + //}}AFX_DATA_INIT +} + + +void CFileProps::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CFileProps) + DDX_Control(pDX, IDC_VIEWSEL, m_ViewSel); + DDX_Control(pDX, IDC_ODINFO, m_ODInfo); + DDX_Control(pDX, IDC_ODTREE, m_ODTree); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CFileProps, CDialog) + //{{AFX_MSG_MAP(CFileProps) + ON_NOTIFY(TVN_SELCHANGED, IDC_ODTREE, OnSelchangedOdtree) + ON_BN_CLICKED(IDC_WORLD, OnWorld) + ON_BN_CLICKED(IDC_VIEWSG, OnViewsg) + ON_WM_TIMER() + ON_WM_CLOSE() + ON_WM_DESTROY() + ON_NOTIFY(TCN_SELCHANGE, IDC_VIEWSEL, OnSelchangeViewsel) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CFileProps message handlers + + +#define FP_TIMER_ID 20 + +BOOL CFileProps::OnInitDialog() +{ + CDialog::OnInitDialog(); + + char sText[5000]; + sprintf(sText, "%s Properties", ((CMainFrame*)GetApp()->m_pMainWnd)->m_pPlayList->GetDisplayName()); + + SetWindowText(sText); + current_odm = NULL; + + m_ViewSel.InsertItem(0, "General"); + m_ViewSel.InsertItem(1, "Streams"); + m_ViewSel.InsertItem(2, "Playback"); + m_ViewSel.InsertItem(3, "Network"); + + m_ODTree.SetIndent(0); + RewriteODTree(); + SetTimer(FP_TIMER_ID, 500, NULL); + + return TRUE; +} + + +void CFileProps::WriteInlineTree(GF_ObjectManager *root_od, HTREEITEM parent) +{ + Osmo4 *gpac = GetApp(); + + /*browse all ODs*/ + u32 count = gf_term_get_object_count(gpac->m_term, root_od); + + for (u32 i=0; im_term, root_od, i); + if (!odm) return; + HTREEITEM item = m_ODTree.InsertItem("Object Descriptor", 0, 0, parent); + m_ODTree.SetItemData(item, (DWORD) odm); + /*if inline propagate*/ + switch (gf_term_object_subscene_type(gpac->m_term, odm)) { + case 1: + m_ODTree.SetItemText(item, "Root Scene"); + WriteInlineTree(odm, item); + break; + case 2: + m_ODTree.SetItemText(item, "Inline Scene"); + WriteInlineTree(odm, item); + break; + case 3: + m_ODTree.SetItemText(item, "Extern Proto Lib"); + WriteInlineTree(odm, item); + break; + default: + break; + } + } +} + +void CFileProps::RewriteODTree() +{ + Osmo4 *gpac = GetApp(); + + m_ODTree.DeleteAllItems(); + + GF_ObjectManager *root_odm = gf_term_get_root_object(gpac->m_term); + if (!root_odm) return; + + HTREEITEM root = m_ODTree.InsertItem("Root OD", 0, 0); + m_ODTree.SetItemData(root, (DWORD) root_odm); + + m_ODTree.SetItemText(root, "Root Scene"); + WriteInlineTree(root_odm, root); +} + +void CFileProps::OnSelchangedOdtree(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; + *pResult = 0; + + HTREEITEM item = m_ODTree.GetSelectedItem(); + GF_ObjectManager *odm = (GF_ObjectManager *) m_ODTree.GetItemData(item); + if (!odm) return; + + SetInfo(odm); +} + + +void CFileProps::OnClose() +{ + KillTimer(FP_TIMER_ID); + DestroyWindow(); +} + +void CFileProps::OnDestroy() +{ + CDialog::OnDestroy(); + delete this; + ((CMainFrame *)GetApp()->m_pMainWnd)->m_pProps = NULL; +} + +void CFileProps::OnSelchangeViewsel(NMHDR* pNMHDR, LRESULT* pResult) +{ + SetInfo(current_odm); + *pResult = 0; +} + +void CFileProps::SetInfo(GF_ObjectManager *odm) +{ + current_odm = odm; + switch (m_ViewSel.GetCurSel()) { + case 3: SetNetworkInfo(); break; + case 2: SetDecoderInfo(); break; + case 1: SetStreamsInfo(); break; + default: SetGeneralInfo(); break; + } +} + +void CFileProps::OnTimer(UINT nIDEvent) +{ + if (nIDEvent == FP_TIMER_ID) { + switch (m_ViewSel.GetCurSel()) { + case 3: SetNetworkInfo(); break; + case 2: SetDecoderInfo(); break; + } + } + + CDialog::OnTimer(nIDEvent); +} + +void CFileProps::SetGeneralInfo() +{ + char info[10000]; + char buf[1000]; + ODInfo odi; + GF_ObjectManager *odm; + u32 h, m, s, i, j; + + Osmo4 *gpac = GetApp(); + odm = current_odm; + + strcpy(info, ""); + if (!odm || gf_term_get_object_info(gpac->m_term, odm, &odi) != GF_OK) return; + + if (!odi.od) { + strcat(info, odi.service_url); + m_ODInfo.SetWindowText(info); + return; + } + sprintf(buf, "%sObject Descriptor ID %d\r\n", (odi.has_profiles) ? "Initial " : "", odi.od->objectDescriptorID); + strcat(info, buf); + if (odi.duration) { + h = (u32) (odi.duration / 3600); + m = (u32) (odi.duration / 60) - h*60; + s = (u32) (odi.duration) - h*3600 - m*60; + sprintf(buf, "Duration %02d:%02d:%02d\r\n", h, m, s); + strcat(info, buf); + } else { + strcat(info, "Unknown duration\r\n"); + } + if (odi.owns_service) { + strcat(info, "Service Handler: "); + strcat(info, odi.service_handler); + strcat(info, "\r\n"); + strcat(info, "Service URL: "); + strcat(info, odi.service_url); + strcat(info, "\r\n"); + } + + if (odi.od->URLString) { + strcat(info, "Remote OD - URL: "); + strcat(info, odi.od->URLString); + strcat(info, "\r\n"); + } + /*get OD content info*/ + if (odi.codec_name) { + switch (odi.od_type) { + case GF_STREAM_VISUAL: + sprintf(buf, "Video Object: Width %d - Height %d\r\n", odi.width, odi.height); + strcat(info, buf); + strcat(info, "Media Codec "); + strcat(info, odi.codec_name); + strcat(info, "\r\n"); + if (odi.par) { + sprintf(buf, "Pixel Aspect Ratio: %d:%d\r\n", (odi.par>>16)&0xFF, (odi.par)&0xFF); + strcat(info, buf); + } + break; + case GF_STREAM_AUDIO: + sprintf(buf, "Audio Object: Sample Rate %d - %d channels\r\n", odi.sample_rate, odi.num_channels); + strcat(info, buf); + strcat(info, "Media Codec "); + strcat(info, odi.codec_name); + strcat(info, "\r\n"); + break; + case GF_STREAM_PRIVATE_SCENE: + case GF_STREAM_SCENE: + if (odi.width && odi.height) { + sprintf(buf, "Scene Description: Width %d - Height %d\r\n", odi.width, odi.height); + } else { + sprintf(buf, "Scene Description: No size specified\r\n"); + } + strcat(info, buf); + strcat(info, "Scene Codec "); + strcat(info, odi.codec_name); + strcat(info, "\r\n"); + break; + case GF_STREAM_TEXT: + if (odi.width && odi.height) { + sprintf(buf, "Text Object: Width %d - Height %d\r\n", odi.width, odi.height); + } else { + sprintf(buf, "Text Object: No size specified\r\n"); + } + strcat(info, buf); + strcat(info, "Text Codec "); + strcat(info, odi.codec_name); + strcat(info, "\r\n"); + break; + } + } + if (odi.protection) { + strcat(info, "Encrypted Media"); + if (odi.protection==2) strcat(info, " NOT UNLOCKED"); + strcat(info, "\r\n"); + } + + if (!gf_list_count(odi.od->OCIDescriptors)) { + m_ODInfo.SetWindowText(info); + return; + } + + strcat(info, "\r\nObject Content Information:\r\n"); + + /*check OCI (not everything interests us) - FIXME: support for unicode*/ + for (i=0; iOCIDescriptors); i++) { + GF_Descriptor *desc = (GF_Descriptor *) gf_list_get(odi.od->OCIDescriptors, i); + switch (desc->tag) { + case GF_ODF_SEGMENT_TAG: + { + GF_Segment *sd = (GF_Segment *) desc; + strcat(info, "\r\nSegment Descriptor:\r\n"); + sprintf(buf, "Name: %s - start time %g sec - duration %g sec\r\n", sd->SegmentName, sd->startTime, sd->Duration); + strcat(info, buf); + } + break; + case GF_ODF_CC_NAME_TAG: + { + GF_CC_Name *ccn = (GF_CC_Name *)desc; + strcat(info, "\r\nContent Creators:\r\n"); + for (j=0; jContentCreators); j++) { + GF_ContentCreatorInfo *ci = (GF_ContentCreatorInfo *) gf_list_get(ccn->ContentCreators, j); + if (!ci->isUTF8) continue; + strcat(info, "\t"); + strcat(info, ci->contentCreatorName); + strcat(info, "\r\n"); + } + } + break; + + case GF_ODF_SHORT_TEXT_TAG: + { + GF_ShortTextual *std = (GF_ShortTextual *)desc; + strcat(info, "\r\n"); + strcat(info, std->eventName); + strcat(info, ": "); + strcat(info, std->eventText); + strcat(info, "\r\n"); + } + break; + /*todo*/ + case GF_ODF_CC_DATE_TAG: + break; + default: + break; + } + + } + + m_ODInfo.SetWindowText(info); +} + +void CFileProps::OnWorld() +{ + CString wit; + const char *str; + GF_List *descs; + Osmo4 *gpac = GetApp(); + + descs = gf_list_new(); + str = gf_term_get_world_info(gpac->m_term, current_odm, descs); + if (!str) { + MessageBox("No World Info available", "Sorry!"); + return; + } + + wit = ""; + for (u32 i=0; iszAppPath); + strcat(szOutFile, "scene_dump"); + + GF_Err e = gf_term_dump_scene(gpac->m_term, (char *) szOutFile, gpac->m_ViewXMTA, 0, current_odm); + + if (e) { + MessageBox(gf_error_to_string(e), "Error while dumping"); + } else { + ShellExecute(NULL, "open", szOutFile, NULL, NULL, SW_SHOWNORMAL); + } +} + +void CFileProps::SetDecoderInfo() +{ + ODInfo odi; + char buf[1000], info[2000]; + u32 h, m, s; + Osmo4 *gpac = GetApp(); + + sprintf(info, ""); + m_ODInfo.SetWindowText(""); + + if (!current_odm || gf_term_get_object_info(gpac->m_term, current_odm, &odi)) return; + if (!odi.od) return; + + strcat(info, "Status: "); + switch (odi.status) { + case 0: + case 1: + case 2: + h = (u32) (odi.current_time / 3600); + m = (u32) (odi.current_time / 60) - h*60; + s = (u32) (odi.current_time) - h*3600 - m*60; + sprintf(buf, "%s\r\nObject Time: %02d:%02d:%02d\r\n", (odi.status==0) ? "Stopped" : (odi.status==1) ? "Playing" : "Paused", h, m, s); + strcat(info, buf); + break; + case 3: + strcat(info, "Not Setup"); + m_ODInfo.SetWindowText(info); + return; + default: + strcat(info, "Setup Failed"); + m_ODInfo.SetWindowText(info); + return; + } + /*get clock drift*/ + sprintf(buf, "Clock drift: %d ms\r\n", odi.clock_drift); + strcat(info, buf); + /*get buffering*/ + if (odi.buffer>=0) { + sprintf(buf, "Buffering Time: %d ms\r\n", odi.buffer); + strcat(info, buf); + } else if (odi.buffer==-1) { + strcat(info, "Not buffering\r\n"); + } else { + strcat(info, "Not Playing\r\n"); + } + /*get DB occupation*/ + if (odi.buffer>=0) { + sprintf(buf, "Decoding Buffer: %d Access Units\r\n", odi.db_unit_count); + strcat(info, buf); + } + /*get CB occupation*/ + if (odi.cb_max_count) { + sprintf(buf, "Composition Memory: %d/%d Units\r\n", odi.cb_unit_count, odi.cb_max_count); + strcat(info, buf); + } + + Float avg_dec_time = 0; + if (odi.nb_dec_frames) { + avg_dec_time = (Float) odi.total_dec_time; + avg_dec_time /= odi.nb_dec_frames; + } + sprintf(buf, "Bitrate over last second: %d kbps\r\nMax bitrate over one second: %d kbps\r\nAverage Decoding Time %.2f ms (%d max)\r\nTotal decoded frames %d - %d dropped\r\n", + (u32) odi.avg_bitrate/1024, odi.max_bitrate/1024, avg_dec_time, odi.max_dec_time, odi.nb_dec_frames, odi.nb_droped); + strcat(info, buf); + + m_ODInfo.SetWindowText(info); +} + + +void CFileProps::SetStreamsInfo() +{ + u32 i, count; + char info[10000]; + char buf[1000], code[5]; + ODInfo odi; + GF_ObjectManager *odm; + Bool is_media; + + Osmo4 *gpac = GetApp(); + odm = current_odm; + + strcpy(info, ""); + m_ODInfo.SetWindowText(""); + + if (!odm || gf_term_get_object_info(gpac->m_term, odm, &odi) != GF_OK) return; + if (!odi.od) return; + + + if (odi.has_profiles) { + strcat(info, "MPEG-4 Profiles and Levels:\r\n"); + sprintf(buf, "\tOD Profile@Level %d\r\n", odi.OD_pl); + strcat(info, buf); + sprintf(buf, "\tScene Profile@Level %d\r\n", odi.scene_pl); + strcat(info, buf); + sprintf(buf, "\tGraphics Profile@Level %d\r\n", odi.graphics_pl); + strcat(info, buf); + sprintf(buf, "\tAudio Profile@Level %d\r\n", odi.audio_pl); + strcat(info, buf); + sprintf(buf, "\tVisual Profile@Level %d\r\n", odi.visual_pl); + strcat(info, buf); + sprintf(buf, "\tInline Content Profiled %s\r\n", odi.inline_pl ? "yes" : "no"); + strcat(info, buf); + strcat(info, "\r\n"); + } + is_media = 0; + count = gf_list_count(odi.od->ESDescriptors); + + for (i=0; iESDescriptors, i); + + sprintf(buf, "\t** Stream ID %d - Clock ID %d **\r\n", esd->ESID, esd->OCRESID); + strcat(info, buf); + if (esd->dependsOnESID) { + sprintf(buf, "Depends on Stream ID %d for decoding\r\n", esd->dependsOnESID); + strcat(info, buf); + } + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: + sprintf(buf, "OD Stream - version %d\r\n", esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + case GF_STREAM_OCR: + sprintf(buf, "OCR Stream\r\n"); + strcat(info, buf); + break; + case GF_STREAM_SCENE: + sprintf(buf, "Scene Description Stream - version %d\r\n", esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + case GF_STREAM_PRIVATE_SCENE: + sprintf(buf, "GPAC Private Scene Description Stream\r\n"); + strcat(info, buf); + break; + case GF_STREAM_VISUAL: + is_media = 1; + strcat(info, "Visual Stream - media type: "); + switch (esd->decoderConfig->objectTypeIndication) { + case 0x20: strcat(info, "MPEG-4\r\n"); break; + case 0x60: strcat(info, "MPEG-2 Simple Profile\r\n"); break; + case 0x61: strcat(info, "MPEG-2 Main Profile\r\n"); break; + case 0x62: strcat(info, "MPEG-2 SNR Profile\r\n"); break; + case 0x63: strcat(info, "MPEG-2 Spatial Profile\r\n"); break; + case 0x64: strcat(info, "MPEG-2 High Profile\r\n"); break; + case 0x65: strcat(info, "MPEG-2 422 Profile\r\n"); break; + case 0x6A: strcat(info, "MPEG-1\r\n"); break; + case 0x6C: strcat(info, "JPEG\r\n"); break; + case 0x6D: strcat(info, "PNG\r\n"); break; + case 0x80: + memcpy(code, esd->decoderConfig->decoderSpecificInfo->data, 4); + code[4] = 0; + sprintf(buf, "GPAC Intern (%s)\r\n", code); + strcat(info, buf); + break; + default: + sprintf(buf, "Private/Unknown (0x%x)\r\n", esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + } + break; + + case GF_STREAM_AUDIO: + is_media = 1; + strcat(info, "Audio Stream - media type: "); + switch (esd->decoderConfig->objectTypeIndication) { + case 0x40: strcat(info, "MPEG-4\r\n"); break; + case 0x66: strcat(info, "MPEG-2 AAC Main Profile\r\n"); break; + case 0x67: strcat(info, "MPEG-2 AAC LowComplexity Profile\r\n"); break; + case 0x68: strcat(info, "MPEG-2 AAC Scalable Sampling Rate Profile\r\n"); break; + case 0x69: strcat(info, "MPEG-2 Audio\r\n"); break; + case 0x6B: strcat(info, "MPEG-1 Audio\r\n"); break; + case 0xA0: strcat(info, "EVRC Audio\r\n"); break; + case 0xA1: strcat(info, "SMV Audio\r\n"); break; + case 0xE1: strcat(info, "QCELP Audio\r\n"); break; + case 0x80: + memcpy(code, esd->decoderConfig->decoderSpecificInfo->data, 4); + code[4] = 0; + sprintf(buf, "GPAC Intern (%s)\r\n", code); + strcat(info, buf); + break; + default: + sprintf(buf, "Private/Unknown (0x%x)\r\n", esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + } + break; + case GF_STREAM_MPEG7: + sprintf(buf, "MPEG-7 Stream - version %d\r\n", esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + case GF_STREAM_IPMP: + sprintf(buf, "IPMP Stream - version %d\r\n", esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + case GF_STREAM_OCI: + sprintf(buf, "OCI Stream - version %d\r\n", esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + case GF_STREAM_MPEGJ: + sprintf(buf, "MPEGJ Stream - version %d\r\n", esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + case GF_STREAM_INTERACT: + sprintf(buf, "User Interaction Stream - version %d\r\n", esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + case GF_STREAM_TEXT: + sprintf(buf, "3GPP/MPEG-4 Timed Text - version %d\r\n", esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + default: + sprintf(buf, "Private/Unknown (StreamType 0x%x OTI 0x%x)\r\n", esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); + strcat(info, buf); + break; + } + + sprintf(buf, "Buffer Size %d\r\nAverage Bitrate %d bps\r\nMaximum Bitrate %d bps\r\n", esd->decoderConfig->bufferSizeDB, esd->decoderConfig->avgBitrate, esd->decoderConfig->maxBitrate); + strcat(info, buf); + if (esd->slConfig->predefined==SLPredef_SkipSL) { + sprintf(buf, "Not using MPEG-4 Synchronization Layer\r\n"); + } else { + sprintf(buf, "Stream Clock Resolution %d\r\n", esd->slConfig->timestampResolution); + } + strcat(info, buf); + if (esd->URLString) { + sprintf(buf, "Stream Location: %s\r\n", esd->URLString); + strcat(info, buf); + } + + /*check language*/ + if (esd->langDesc) { + u32 i=0; + char lan[4], *szLang; + lan[0] = esd->langDesc->langCode>>16; + lan[1] = (esd->langDesc->langCode>>8)&0xFF; + lan[2] = (esd->langDesc->langCode)&0xFF; + lan[3] = 0; + + if ((lan[0]=='u') && (lan[1]=='n') && (lan[2]=='d')) szLang = "Undetermined"; + else { + szLang = lan; + while (GF_ISO639_Lang[i]) { + if (GF_ISO639_Lang[i+2][0] && strstr(GF_ISO639_Lang[i+1], lan)) { + szLang = (char*) GF_ISO639_Lang[i]; + break; + } + i+=3; + } + } + sprintf(buf, "Stream Language: %s\r\n", szLang); + strcat(info, buf); + } + strcat(info, "\r\n"); + } + + m_ODInfo.SetWindowText(info); +} + +void CFileProps::SetNetworkInfo() +{ + char info[10000]; + char buf[10000]; + u32 id; + NetStatCommand com; + ODInfo odi; + u32 d_enum, nb_streams; + GF_Err e; + GF_ObjectManager *odm; + Osmo4 *gpac = GetApp(); + odm = current_odm; + + strcpy(info, ""); + m_ODInfo.SetWindowText(""); + + if (!odm || gf_term_get_object_info(gpac->m_term, odm, &odi) != GF_OK) return; + if (!odi.od) return; + + if (odi.owns_service) { + const char *url, *path; + u32 done, total, bps; + strcpy(info, "Current Downloads in service:\r\n"); + d_enum = 0; + while (gf_term_get_download_info(gpac->m_term, odm, &d_enum, &url, &path, &done, &total, &bps)) { + if (total && done) { + sprintf(buf, "%s %s: %d / %d bytes (%.2f %%) - %.2f kBps\r\n", url, path, done, total, (100.0f*done)/total, ((Float)bps)/1024); + } else { + sprintf(buf, "%s %s: %.2f kbps\r\n", url, path, ((Float)bps*8)/1024); + } + strcat(info, buf); + } + if (!d_enum) strcpy(info, "No Downloads in service\r\n"); + strcat(info, "\r\n"); + } + + d_enum = 0; + nb_streams = 0; + while (gf_term_get_channel_net_info(gpac->m_term, odm, &d_enum, &id, &com, &e)) { + if (e) continue; + if (!com.bw_down && !com.bw_up) continue; + nb_streams ++; + + sprintf(buf, "Stream ID %d statistics:\r\n", id); + strcat(info, buf); + if (com.multiplex_port) { + sprintf(buf, "\tMultiplex Port %d - multiplex ID %d\r\n", com.multiplex_port, com.port); + } else { + sprintf(buf, "\tPort %d\r\n", com.port); + } + strcat(info, buf); + sprintf(buf, "\tPacket Loss Percentage: %.4f\r\n", com.pck_loss_percentage); + strcat(info, buf); + sprintf(buf, "\tDown Bandwidth: %.3f kbps\r\n", ((Float)com.bw_down) / 1024); + strcat(info, buf); + if (com.bw_up) { + sprintf(buf, "\tUp Bandwidth: %d bps\r\n", com.bw_up); + strcat(info, buf); + } + if (com.ctrl_port) { + if (com.multiplex_port) { + sprintf(buf, "\tControl Multiplex Port: %d - Control Multiplex ID %d\r\n", com.multiplex_port, com.ctrl_port); + } else { + sprintf(buf, "\tControl Port: %d\r\n", com.ctrl_port); + } + strcat(info, buf); + sprintf(buf, "\tControl Down Bandwidth: %d bps\r\n", com.ctrl_bw_down); + strcat(info, buf); + sprintf(buf, "\tControl Up Bandwidth: %d bps\r\n", com.ctrl_bw_up); + strcat(info, buf); + } + strcat(info, "\r\n"); + } + if (!nb_streams) strcat(info, "No network streams in this object\r\n"); + + m_ODInfo.SetWindowText(info); +} diff --git a/applications/osmo4_w32/FileProps.h b/applications/osmo4_w32/FileProps.h new file mode 100644 index 0000000..ff0fd27 --- /dev/null +++ b/applications/osmo4_w32/FileProps.h @@ -0,0 +1,70 @@ +#if !defined(AFX_FILEPROPS_H__CA4484B4_0301_4BE1_8736_551250121C3F__INCLUDED_) +#define AFX_FILEPROPS_H__CA4484B4_0301_4BE1_8736_551250121C3F__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// FileProps.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CFileProps dialog + +class CFileProps : public CDialog +{ +// Construction +public: + CFileProps(CWnd* pParent = NULL); // standard constructor + BOOL Create(CWnd * pParent) + { + return CDialog::Create( CFileProps::IDD, pParent); + } + +// Dialog Data + //{{AFX_DATA(CFileProps) + enum { IDD = IDD_PROPERTIES }; + CTabCtrl m_ViewSel; + CEdit m_ODInfo; + CTreeCtrl m_ODTree; + //}}AFX_DATA + + + void RewriteODTree(); + void WriteInlineTree(GF_ObjectManager *root_od, HTREEITEM parent); + void SetInfo(GF_ObjectManager *odm); + +private: + GF_ObjectManager *current_odm; + void SetGeneralInfo(); + void SetStreamsInfo(); + void SetDecoderInfo(); + void SetNetworkInfo(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CFileProps) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CFileProps) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangedOdtree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnWorld(); + afx_msg void OnViewsg(); + afx_msg void OnTimer(UINT nIDEvent); + afx_msg void OnClose(); + afx_msg void OnDestroy(); + afx_msg void OnSelchangeViewsel(NMHDR* pNMHDR, LRESULT* pResult); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_FILEPROPS_H__CA4484B4_0301_4BE1_8736_551250121C3F__INCLUDED_) diff --git a/applications/osmo4_w32/MainFrm.cpp b/applications/osmo4_w32/MainFrm.cpp new file mode 100644 index 0000000..f8134cd --- /dev/null +++ b/applications/osmo4_w32/MainFrm.cpp @@ -0,0 +1,1519 @@ +// MainFrm.cpp : implementation of the CMainFrame class +// + +#include "stdafx.h" +#include "Osmo4.h" + +#include "MainFrm.h" +#include "resource.h" + +#include + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CChildView + +CChildView::CChildView() +{ +} + +CChildView::~CChildView() +{ + /*since the wndproc is overwritten by the terminal, we detach the handle otherwise we get a nice assertion + failure from windows*/ + HWND hWnd = Detach(); + ::PostMessage(hWnd, WM_QUIT, 0, 0); +} + + +BEGIN_MESSAGE_MAP(CChildView,CWnd ) + //{{AFX_MSG_MAP(CChildView) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CChildView message handlers + +BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) +{ + cs.dwExStyle = 0; + cs.style &= ~WS_BORDER; + + cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, + ::LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_WINDOW+1), NULL); + + return TRUE; +} + + + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame + +IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd) + +BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) + //{{AFX_MSG_MAP(CMainFrame) + ON_WM_CREATE() + ON_WM_SETFOCUS() + ON_WM_INITMENUPOPUP() + ON_WM_SIZE() + ON_WM_MOVE() + ON_MESSAGE(WM_SETSIZE,OnSetSize) + ON_MESSAGE(WM_NAVIGATE,OnNavigate) + ON_MESSAGE(WM_OPENURL, Open) + ON_MESSAGE(WM_NEWINSTANCE, NewInstanceOpened) + + ON_WM_LBUTTONDOWN() + ON_WM_LBUTTONDBLCLK() + ON_WM_LBUTTONUP() + ON_WM_CHAR() + ON_WM_SYSKEYDOWN() + ON_WM_SYSKEYUP() + ON_WM_KEYDOWN() + ON_WM_KEYUP() + ON_WM_DROPFILES() + ON_MESSAGE(WM_CONSOLEMSG, OnConsoleMessage) + ON_COMMAND(ID_VIEW_ORIGINAL, OnViewOriginal) + ON_COMMAND(ID_VIEW_FULLSCREEN, OnViewFullscreen) + ON_COMMAND(ID_AR_KEEP, OnArKeep) + ON_COMMAND(ID_AR_FILL, OnArFill) + ON_COMMAND(ID_AR_43, OnAr43) + ON_COMMAND(ID_AR_169, OnAr169) + ON_UPDATE_COMMAND_UI(ID_AR_169, OnUpdateAr169) + ON_UPDATE_COMMAND_UI(ID_AR_43, OnUpdateAr43) + ON_UPDATE_COMMAND_UI(ID_AR_FILL, OnUpdateArFill) + ON_UPDATE_COMMAND_UI(ID_AR_KEEP, OnUpdateArKeep) + ON_COMMAND(ID_NAVIGATE_NONE, OnNavigateNone) + ON_COMMAND(ID_NAVIGATE_WALK, OnNavigateWalk) + ON_COMMAND(ID_NAVIGATE_FLY, OnNavigateFly) + ON_COMMAND(ID_NAVIGATE_EXAM, OnNavigateExam) + ON_COMMAND(ID_NAVIGATE_SLIDE, OnNavigateSlide) + ON_COMMAND(ID_NAVIGATE_PAN, OnNavigatePan) + ON_COMMAND(ID_NAVIGATE_ORBIT, OnNavigateOrbit) + ON_COMMAND(ID_NAVIGATE_GAME, OnNavigateGame) + ON_COMMAND(ID_NAVIGATE_VR, OnNavigateVR) + ON_COMMAND(ID_NAV_RESET, OnNavigateReset) + ON_COMMAND(ID_SHORTCUTS, OnShortcuts) + ON_COMMAND(IDD_CONFIGURE, OnConfigure) + ON_COMMAND(ID_FILE_PROP, OnFileProp) + ON_COMMAND(ID_VIEW_PL, OnViewPlaylist) + ON_UPDATE_COMMAND_UI(ID_FILE_PROP, OnUpdateFileProp) + ON_UPDATE_COMMAND_UI(ID_NAVIGATE_NONE, OnUpdateNavigate) + ON_COMMAND(ID_REC_ENABLE, OnCacheEnable) + ON_UPDATE_COMMAND_UI(ID_REC_ENABLE, OnUpdateCacheEnable) + ON_COMMAND(ID_REC_STOP, OnCacheStop) + ON_COMMAND(ID_REC_ABORT, OnCacheAbort) + ON_UPDATE_COMMAND_UI(ID_REC_STOP, OnUpdateCacheStop) + ON_COMMAND(ID_COLLIDE_DISP, OnCollideDisp) + ON_UPDATE_COMMAND_UI(ID_COLLIDE_DISP, OnUpdateCollideDisp) + ON_COMMAND(ID_COLLIDE_NONE, OnCollideNone) + ON_UPDATE_COMMAND_UI(ID_COLLIDE_NONE, OnUpdateCollideNone) + ON_COMMAND(ID_COLLIDE_REG, OnCollideReg) + ON_UPDATE_COMMAND_UI(ID_COLLIDE_REG, OnUpdateCollideReg) + ON_COMMAND(ID_HEADLIGHT, OnHeadlight) + ON_UPDATE_COMMAND_UI(ID_HEADLIGHT, OnUpdateHeadlight) + ON_COMMAND(ID_GRAVITY, OnGravity) + ON_UPDATE_COMMAND_UI(ID_GRAVITY, OnUpdateGravity) + ON_COMMAND(ID_NAV_INFO, OnNavInfo) + ON_COMMAND(ID_NAV_NEXT, OnNavNext) + ON_COMMAND(ID_NAV_PREV, OnNavPrev) + ON_UPDATE_COMMAND_UI(ID_NAV_NEXT, OnUpdateNavNext) + ON_UPDATE_COMMAND_UI(ID_NAV_PREV, OnUpdateNavPrev) + ON_COMMAND(ID_CLEAR_NAV, OnClearNav) + ON_UPDATE_COMMAND_UI(ID_VIEW_PL, OnUpdateViewPlaylist) + ON_COMMAND(ID_PLAYLIST_LOOP, OnPlaylistLoop) + ON_UPDATE_COMMAND_UI(ID_PLAYLIST_LOOP, OnUpdatePlaylistLoop) + ON_COMMAND(ID_ADD_SUBTITLE, OnAddSubtitle) + ON_UPDATE_COMMAND_UI(ID_REC_ABORT, OnUpdateCacheStop) + ON_UPDATE_COMMAND_UI(ID_NAVIGATE_WALK, OnUpdateNavigate) + ON_UPDATE_COMMAND_UI(ID_NAVIGATE_FLY, OnUpdateNavigate) + ON_UPDATE_COMMAND_UI(ID_NAVIGATE_EXAM, OnUpdateNavigate) + ON_UPDATE_COMMAND_UI(ID_NAVIGATE_PAN, OnUpdateNavigate) + ON_UPDATE_COMMAND_UI(ID_NAVIGATE_SLIDE, OnUpdateNavigate) + ON_UPDATE_COMMAND_UI(ID_NAVIGATE_ORBIT, OnUpdateNavigate) + ON_UPDATE_COMMAND_UI(ID_NAVIGATE_VR, OnUpdateNavigate) + ON_UPDATE_COMMAND_UI(ID_NAVIGATE_GAME, OnUpdateNavigate) + ON_COMMAND(ID_FILE_EXIT, OnFileExit) + ON_COMMAND(ID_VIEW_CPU, OnViewCPU) + ON_UPDATE_COMMAND_UI(ID_VIEW_CPU, OnUpdateViewCPU) + + ON_COMMAND(ID_FILE_COPY, OnFileCopy) + ON_UPDATE_COMMAND_UI(ID_FILE_COPY, OnUpdateFileCopy) + ON_COMMAND(ID_FILE_PASTE, OnFilePaste) + ON_UPDATE_COMMAND_UI(ID_FILE_PASTE, OnUpdateFilePaste) + + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame construction/destruction + +CMainFrame::CMainFrame() +{ + m_icoerror = AfxGetApp()->LoadIcon(IDI_ERR); + m_icomessage = AfxGetApp()->LoadIcon(IDI_MESSAGE); + m_bFullScreen = m_bRestoreFS = 0; + m_aspect_ratio = GF_ASPECT_RATIO_KEEP; + m_pProps = NULL; + m_pOpt = NULL; + m_pPlayList = NULL; + m_pWndView = new CChildView(); + m_bInitShow = TRUE; + m_bStartupFile = TRUE; + m_num_chapters = 0; + m_chapters_start = NULL; + m_last_prog = -1; + m_timer_on = 0; + m_show_rti = 0; + nb_viewpoints = 0; +} + +CMainFrame::~CMainFrame() +{ + if (m_chapters_start) free(m_chapters_start); + if (m_pProps != NULL) m_pProps->DestroyWindow(); + if (m_pOpt != NULL) m_pOpt->DestroyWindow(); + if (m_pPlayList != NULL) delete m_pPlayList; + delete m_pWndView; +} + + + + +#define RTI_TIMER 22 +#define RTI_REFRESH_MS 500 + +void CALLBACK EXPORT RTInfoTimer(HWND , UINT , UINT nID , DWORD ) +{ + char szMsg[100]; + GF_SystemRTInfo rti; + if (nID != RTI_TIMER) return; + Osmo4 *app = GetApp(); + CMainFrame *pFrame = (CMainFrame *) app->m_pMainWnd; + /*shutdown*/ + if (!pFrame) return; + + if (pFrame->m_show_rti && !pFrame->m_timer_on) { + if (!gf_sys_get_rti(RTI_REFRESH_MS, &rti, 0)) return; + if (!rti.gpac_memory) rti.gpac_memory = rti.process_memory ? rti.process_memory : rti.physical_memory; + + if (pFrame->m_show_rti && !pFrame->m_timer_on) { + sprintf(szMsg, "FPS %02.2f - CPU %02d (%02d) - Mem %d kB", + gf_term_get_framerate(app->m_term, 0), rti.total_cpu_usage, rti.process_cpu_usage, rti.gpac_memory/1024); + pFrame->m_wndStatusBar.SetPaneText(1, szMsg); + } + } + + u32 ms = gf_term_get_time_in_ms(app->m_term); + u32 h = ms / 1000 / 3600; + u32 m = ms / 1000 / 60 - h*60; + u32 s = ms / 1000 - h*3600 - m*60; + + sprintf(szMsg, "%02d:%02d.%02d", h, m, s); + pFrame->m_wndStatusBar.SetPaneText(0, szMsg); +} + +static UINT status_indics[] = +{ + ID_TIMER, + ID_SEPARATOR, // status line indicator +}; + + + +int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + UINT buttonArray[50]; + TBBUTTONINFO bi; + u32 *ba; + if (CFrameWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + // create a view to occupy the client area of the frame + if (!m_pWndView->CreateEx(0, NULL, NULL, WS_CHILD, 0, 0, 300, 200, m_hWnd, NULL, NULL)) + { + TRACE0("Failed to create view window\n"); + return -1; + } + m_pPlayList = new Playlist(); + m_pPlayList->Create(); + m_pPlayList->ShowWindow(SW_HIDE); + + + if (!m_wndToolBar.CreateEx(this, WS_CHILD | CBRS_TOP | CBRS_FLYBY) || + !m_wndToolBar.LoadBitmap(IDR_MAINTOOLS)) + { + TRACE0("Failed to create toolbar\n"); + return -1; // fail to create + } + + ba = &buttonArray[0]; + *ba = ID_FILEOPEN; ba++; + *ba = ID_SEPARATOR; ba++; + *ba = ID_NAV_PREV; ba++; + *ba = ID_NAV_NEXT; ba++; + *ba = ID_SEPARATOR; ba++; + *ba = ID_FILE_PLAY; ba++; + *ba = ID_FILE_STEP; ba++; + *ba = ID_FILE_STOP; ba++; + *ba = ID_SEPARATOR; ba++; + *ba = ID_FILE_PROP; ba++; + *ba = ID_SEPARATOR; ba++; + *ba = ID_FILE_PROP; ba++; + *ba = ID_SWITCH_RENDER; + m_wndToolBar.SetButtons(buttonArray, 13); + m_wndToolBar.SetButtonInfo(0, ID_FILEOPEN, TBBS_BUTTON, 0); + m_wndToolBar.SetButtonInfo(1, ID_SEPARATOR, TBBS_SEPARATOR, 0); + m_wndToolBar.SetButtonInfo(2, ID_NAV_PREV, TBBS_DROPDOWN, 1); + m_wndToolBar.SetButtonInfo(3, ID_NAV_NEXT, TBBS_DROPDOWN, 2); + m_wndToolBar.SetButtonInfo(4, ID_SEPARATOR, TBBS_SEPARATOR, 0); + m_wndToolBar.SetButtonInfo(5, ID_FILE_PLAY, TBBS_BUTTON, 3); + m_wndToolBar.SetButtonInfo(6, ID_FILE_STEP, TBBS_BUTTON, 5); + m_wndToolBar.SetButtonInfo(7, ID_FILE_STOP, TBBS_BUTTON, 6); + m_wndToolBar.SetButtonInfo(8, ID_SEPARATOR, TBBS_SEPARATOR, 0); + m_wndToolBar.SetButtonInfo(9, ID_FILE_PROP, TBBS_BUTTON, 7); + m_wndToolBar.SetButtonInfo(10, ID_SEPARATOR, TBBS_SEPARATOR, 0); + m_wndToolBar.SetButtonInfo(11, IDD_CONFIGURE, TBBS_BUTTON, 8); + m_wndToolBar.SetButtonInfo(12, ID_SWITCH_RENDER, TBBS_BUTTON, 9); + + CToolBarCtrl &ctrl = m_wndToolBar.GetToolBarCtrl(); + ctrl.SetStyle(TBSTYLE_FLAT | TBSTYLE_DROPDOWN); + ctrl.SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); + + memset(&bi, 0, sizeof(bi)); + bi.cbSize = sizeof(bi); + ctrl.GetButtonInfo(2, &bi); + bi.fsStyle |= TBSTYLE_DROPDOWN; + ctrl.SetButtonInfo(ID_NAV_PREV, &bi); + + memset(&bi, 0, sizeof(bi)); + bi.cbSize = sizeof(bi); + ctrl.GetButtonInfo(3, &bi); + bi.fsStyle |= TBSTYLE_DROPDOWN; + ctrl.SetButtonInfo(ID_NAV_NEXT, &bi); + + if (!m_wndStatusBar.Create(this) || + !m_wndStatusBar.SetIndicators(status_indics, + sizeof(status_indics)/sizeof(UINT))) + { + TRACE0("Failed to create status bar\n"); + return -1; // fail to create + } + + if (!m_Address.Create(this, IDD_NAVBAR, WS_CHILD | CBRS_TOP | CBRS_FLYBY | CBRS_SIZE_DYNAMIC, IDD_NAVBAR) ) { + return -1; // fail to create + } + + if (!m_Sliders.Create(IDD_SLIDERS, this) ) { + return -1; // fail to create + } + + m_wndStatusBar.SetPaneInfo(0, ID_TIMER, SBPS_NORMAL, 60); + m_wndStatusBar.SetPaneInfo(1, ID_SEPARATOR, SBPS_STRETCH, 0); + SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), TRUE); + SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), FALSE); + + SetTimer(RTI_TIMER, RTI_REFRESH_MS, RTInfoTimer); + return 0; +} + + +BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + if( !CFrameWnd::PreCreateWindow(cs) ) + return FALSE; + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + cs.dwExStyle &= ~WS_EX_CLIENTEDGE; + cs.lpszClass = AfxRegisterWndClass(0); + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame diagnostics + +#ifdef _DEBUG +void CMainFrame::AssertValid() const +{ + CFrameWnd::AssertValid(); +} + +void CMainFrame::Dump(CDumpContext& dc) const +{ + CFrameWnd::Dump(dc); +} + +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame message handlers +void CMainFrame::OnSetFocus(CWnd* pOldWnd) +{ + m_pWndView->SetFocus(); + if (m_bRestoreFS==1) { + m_bRestoreFS=2; + } + else if (m_bRestoreFS==2) { + m_bRestoreFS = 0; + SetFullscreen(); + } +} + +BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) +{ + // let the view have first crack at the command + if (m_pWndView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) + return TRUE; + + // otherwise, do default handling + return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); +} + + +void CMainFrame::OnSize(UINT nType, int cx, int cy) +{ + RECT rc2; + u32 tool_h, slide_h, add_h, stat_h; + + if (m_bInitShow) { + CFrameWnd::OnSize(nType, cx, cy); + return; + } + m_wndToolBar.GetClientRect(&rc2); + tool_h = rc2.bottom - rc2.top; + m_Address.GetClientRect(&rc2); + add_h = rc2.bottom - rc2.top; + m_Sliders.GetClientRect(&rc2); + slide_h = rc2.bottom - rc2.top; + m_wndStatusBar.GetClientRect(&rc2); + stat_h = rc2.bottom - rc2.top; + if ((u32) cy <= tool_h+add_h+slide_h+stat_h) { + OnSetSize(cx, 1); + return; + } + + CFrameWnd::OnSize(nType, cx, cy); + cy -= tool_h + add_h + slide_h + stat_h; + + m_Address.SetWindowPos(this, 0, 0, cx, add_h, SWP_SHOWWINDOW | SWP_NOMOVE); + + m_pWndView->ShowWindow(SW_SHOW); + m_pWndView->SetWindowPos(this, 0, add_h + tool_h, cx, cy, SWP_NOZORDER); + + m_Sliders.SetWindowPos(this, 0, add_h + tool_h + cy, cx, slide_h, SWP_NOZORDER|SWP_SHOWWINDOW); + /*and resize term*/ + gf_term_set_size(GetApp()->m_term, cx, cy); +} + + +LONG CMainFrame::OnSetSize(WPARAM wParam, LPARAM lParam) +{ + UINT width, height; + width = wParam; + height = lParam; + if (m_bInitShow) { + m_wndToolBar.UpdateWindow(); + m_wndToolBar.ShowWindow(SW_SHOW); + m_Address.UpdateWindow(); + m_Address.ShowWindow(SW_SHOW); + m_Sliders.UpdateWindow(); + m_Sliders.ShowWindow(SW_SHOW); + m_Sliders.m_PosSlider.EnableWindow(FALSE); + m_pWndView->ShowWindow(SW_SHOW); + ShowWindow(SW_SHOW); + m_bInitShow = FALSE; + } + + RECT winRect; + winRect.left = 0; + winRect.right = width; + winRect.top = 0; + winRect.bottom = height; + AdjustWindowRectEx(&winRect, GetStyle(), TRUE, GetExStyle()); + winRect.bottom -= winRect.top; + winRect.right -= winRect.left; + winRect.left = winRect.top = 0; + + RECT rc2; + m_Address.GetClientRect(&rc2); + winRect.bottom += rc2.bottom; + m_wndToolBar.GetClientRect(&rc2); + winRect.bottom += rc2.bottom; + m_Sliders.GetClientRect(&rc2); + winRect.bottom += rc2.bottom; + m_wndStatusBar.GetClientRect(&rc2); + winRect.bottom += rc2.bottom; + + GetWindowRect(&rc2); + rc2.bottom -= rc2.top; + rc2.right -= rc2.left; + if ((rc2.right != winRect.right) || (rc2.bottom != winRect.bottom)) { + SetWindowPos(NULL, 0, 0, winRect.right, winRect.bottom, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW); + } else { + /*just resize term*/ + gf_term_set_size(GetApp()->m_term, width, height); + } + return 0; +} + +void CMainFrame::OnMove(int x, int y) +{ + CFrameWnd::OnMove(x, y); + RECT rc; + + m_wndToolBar.GetClientRect(&rc); + m_wndToolBar.SetWindowPos(this, x, y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); + y += rc.bottom - rc.top; + m_Address.SetWindowPos(this, x, y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); + m_Address.GetClientRect(&rc); + y += rc.bottom - rc.top; + m_pWndView->SetWindowPos(this, x, y, 0, 0, SWP_NOSIZE); + m_pWndView->GetClientRect(&rc); + y += rc.bottom; + m_Sliders.SetWindowPos(this, x, y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); +} + + +#define PROGRESS_TIMER 20 +#define PROGRESS_REFRESH_MS 100 + +void CALLBACK EXPORT ProgressTimer(HWND , UINT , UINT nID , DWORD ) +{ + u32 now; + if (nID != PROGRESS_TIMER) return; + Osmo4 *app = GetApp(); + CMainFrame *pFrame = (CMainFrame *) app->m_pMainWnd; + /*shutdown*/ + if (!pFrame) return; + + now = gf_term_get_time_in_ms(app->m_term); + if (!now) return; + + if (app->can_seek && !pFrame->m_Sliders.m_grabbed) { + if (now >= app->max_duration + 100) { + if (gf_term_get_option(app->m_term, GF_OPT_IS_FINISHED)) { + pFrame->m_pPlayList->PlayNext(); + } + /*if no IsOver go on forever*/ + } else { + if (!app->m_reset) + pFrame->m_Sliders.m_PosSlider.SetPos(now); + } + } +} + +void CMainFrame::SetProgTimer(Bool bOn) +{ + if (bOn) + SetTimer(PROGRESS_TIMER, PROGRESS_REFRESH_MS, ProgressTimer); + else + KillTimer(PROGRESS_TIMER); +} + + +LONG CMainFrame::Open(WPARAM wParam, LPARAM lParam) +{ + Bool do_pause; + Osmo4 *app = GetApp(); + CString txt, url; + m_bStartupFile = FALSE; + txt = "Osmo4 - "; + txt += m_pPlayList->GetDisplayName(); + + url = m_pPlayList->GetURL(); + m_Address.m_Address.SetWindowText(url); + SetWindowText(txt); + if (app->start_mode==1) do_pause = 1; + else if (app->start_mode==2) do_pause = 0; + else do_pause = /*!app->m_AutoPlay*/0; + gf_term_connect_from_time(app->m_term, (LPCSTR) url, app->m_reconnect_time, do_pause); + app->m_reconnect_time = 0; + app->start_mode = 0; + app->UpdatePlayButton(); + nb_viewpoints = 0; + return 1; +} + +LONG CMainFrame::NewInstanceOpened(WPARAM wParam, LPARAM lParam) +{ + Bool queue_only = 0; + char *url = (char *) static_gpac_get_url(); + if (!strnicmp(url, "-queue ", 7)) { + queue_only = 1; + url += 7; + } + m_pPlayList->QueueURL(url); + m_pPlayList->RefreshList(); + if (!queue_only) m_pPlayList->PlayNext(); + return 1; +} + + +void CMainFrame::ForwardMessage() +{ + const MSG *msg = GetCurrentMessage(); + m_pWndView->SendMessage(msg->message, msg->wParam, msg->lParam); +} +void CMainFrame::OnSysKeyUp(UINT , UINT , UINT ) { ForwardMessage(); } +void CMainFrame::OnSysKeyDown(UINT , UINT , UINT ) { ForwardMessage(); } +void CMainFrame::OnChar(UINT , UINT , UINT ) { ForwardMessage(); } +void CMainFrame::OnKeyDown(UINT , UINT , UINT ) { ForwardMessage(); } +void CMainFrame::OnKeyUp(UINT , UINT , UINT ) { ForwardMessage(); } +void CMainFrame::OnLButtonDown(UINT , CPoint ) { ForwardMessage(); } +void CMainFrame::OnLButtonDblClk(UINT , CPoint ) { ForwardMessage(); } +void CMainFrame::OnLButtonUp(UINT , CPoint ) { ForwardMessage(); } + +void CMainFrame::OnDropFiles(HDROP hDropInfo) +{ + u32 i, count; + Osmo4 *app = GetApp(); + char fileName[MAX_PATH]; + + count = ::DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0); + if (!count) return; + + /*if playing and sub d&d, open sub in current presentation*/ + if (app->m_isopen && (count==1)) { + ::DragQueryFile(hDropInfo, 0, fileName, MAX_PATH); + char *ext = strrchr(fileName, '.'); + if (ext && ( !stricmp(ext, ".srt") || !stricmp(ext, ".sub") || !stricmp(ext, ".ttxt") || !stricmp(ext, ".xml") ) ) { + AddSubtitle(fileName, 1); + return; + } + } + +/* if (count==1) + m_pPlayList->Truncate(); + else +*/ m_pPlayList->Clear(); + + for (i=0; iQueueURL(fileName); + } + m_pPlayList->RefreshList(); + m_pPlayList->PlayNext(); +} + +void CALLBACK EXPORT ConsoleTimer(HWND , UINT , UINT , DWORD ) +{ + CMainFrame *pFrame = (CMainFrame *) GetApp()->m_pMainWnd; + + pFrame->m_wndStatusBar.GetStatusBarCtrl().SetIcon(2, NULL); + pFrame->KillTimer(pFrame->m_timer_on); + pFrame->m_timer_on = 0; + pFrame->m_wndStatusBar.SetPaneText(1, "Ready"); +} + +#define CONSOLE_DISPLAY_TIME 1000 + +LONG CMainFrame::OnConsoleMessage(WPARAM wParam, LPARAM lParam) +{ + if (m_timer_on) KillTimer(m_timer_on); + + if (console_err>=0) { + m_wndStatusBar.GetStatusBarCtrl().SetIcon(2, m_icomessage); + m_wndStatusBar.SetPaneText(1, console_message); + } else { + char msg[5000]; + m_wndStatusBar.GetStatusBarCtrl().SetIcon(2, m_icoerror); + sprintf(msg, "%s (%s)", console_message, console_service); + m_wndStatusBar.SetPaneText(1, msg); + } + m_timer_on = SetTimer(10, wParam ? wParam : CONSOLE_DISPLAY_TIME, ConsoleTimer); + return 0; +} + +BOOL CMainFrame::DestroyWindow() +{ + if (GetApp()->m_isopen) KillTimer(PROGRESS_TIMER); + /*signal close to prevent callbacks but don't close, this is done in ExitInstance (otherwise there's a + deadlock happening not sure why yet)*/ +// GetApp()->m_open = 0; + return CFrameWnd::DestroyWindow(); +} + + +void CMainFrame::OnViewOriginal() +{ + Osmo4 *gpac = GetApp(); + gf_term_set_option(gpac->m_term, GF_OPT_ORIGINAL_VIEW, 1); + OnSetSize(gpac->orig_width, gpac->orig_height); +} + +void CMainFrame::SetFullscreen() +{ + Osmo4 *gpac = GetApp(); + if (!m_bFullScreen) { +// GetWindowRect(&backup_wnd_rc); + if (gf_term_set_option(gpac->m_term, GF_OPT_FULLSCREEN, 1) == GF_OK) + m_bFullScreen = 1; + } else { + if (gf_term_set_option(gpac->m_term, GF_OPT_FULLSCREEN, 0) == GF_OK) + m_bFullScreen = 0; +// SetWindowPos(NULL, backup_wnd_rc.left, backup_wnd_rc.top, backup_wnd_rc.right-backup_wnd_rc.left, backup_wnd_rc.bottom-backup_wnd_rc.top, SWP_NOZORDER); + } +} + +void CMainFrame::OnViewFullscreen() +{ + SetFullscreen(); +} + +void CMainFrame::OnArKeep() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP); + m_aspect_ratio = GF_ASPECT_RATIO_KEEP; +} + +void CMainFrame::OnArFill() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN); + m_aspect_ratio = GF_ASPECT_RATIO_FILL_SCREEN; +} + +void CMainFrame::OnAr43() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3); + m_aspect_ratio = GF_ASPECT_RATIO_4_3; +} + +void CMainFrame::OnAr169() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9); + m_aspect_ratio = GF_ASPECT_RATIO_16_9; +} + +void CMainFrame::OnUpdateAr169(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(m_aspect_ratio == GF_ASPECT_RATIO_16_9); +} + +void CMainFrame::OnUpdateAr43(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(m_aspect_ratio == GF_ASPECT_RATIO_4_3); +} + +void CMainFrame::OnUpdateArFill(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(m_aspect_ratio == GF_ASPECT_RATIO_FILL_SCREEN); +} + +void CMainFrame::OnUpdateArKeep(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(m_aspect_ratio == GF_ASPECT_RATIO_KEEP); +} + +void CMainFrame::OnUpdateNavigate(CCmdUI* pCmdUI) +{ + BOOL enable; + Osmo4 *app = GetApp(); + pCmdUI->Enable(FALSE); + if (!app->m_isopen) return; + + u32 type = gf_term_get_option(app->m_term, GF_OPT_NAVIGATION_TYPE); + enable = type ? TRUE : FALSE; + + if (pCmdUI->m_nID==ID_NAV_RESET) { + pCmdUI->Enable(TRUE); + return; + } + + u32 mode = gf_term_get_option(app->m_term, GF_OPT_NAVIGATION); + /*common 2D/3D modes*/ + if (pCmdUI->m_nID==ID_NAVIGATE_NONE) { pCmdUI->Enable(enable); pCmdUI->SetCheck(mode ? 0 : 1); } + else if (pCmdUI->m_nID==ID_NAVIGATE_EXAM) { pCmdUI->Enable(enable); pCmdUI->SetCheck((mode==GF_NAVIGATE_EXAMINE) ? 1 : 0); } + else if (pCmdUI->m_nID==ID_NAVIGATE_SLIDE) { pCmdUI->Enable(enable); pCmdUI->SetCheck((mode==GF_NAVIGATE_SLIDE) ? 1 : 0); } + + if (type==GF_NAVIGATE_TYPE_2D) return; + pCmdUI->Enable(enable); + if (pCmdUI->m_nID==ID_NAVIGATE_WALK) pCmdUI->SetCheck((mode==GF_NAVIGATE_WALK) ? 1 : 0); + else if (pCmdUI->m_nID==ID_NAVIGATE_FLY) pCmdUI->SetCheck((mode==GF_NAVIGATE_FLY) ? 1 : 0); + else if (pCmdUI->m_nID==ID_NAVIGATE_PAN) pCmdUI->SetCheck((mode==GF_NAVIGATE_PAN) ? 1 : 0); + else if (pCmdUI->m_nID==ID_NAVIGATE_VR) pCmdUI->SetCheck((mode==GF_NAVIGATE_VR) ? 1 : 0); + else if (pCmdUI->m_nID==ID_NAVIGATE_GAME) pCmdUI->SetCheck((mode==GF_NAVIGATE_GAME) ? 1 : 0); +} + + +void CMainFrame::SetNavigate(u32 mode) +{ + Osmo4 *app = GetApp(); + gf_term_set_option(app->m_term, GF_OPT_NAVIGATION, mode); +} +void CMainFrame::OnNavigateNone() { SetNavigate(GF_NAVIGATE_NONE); } +void CMainFrame::OnNavigateWalk() { SetNavigate(GF_NAVIGATE_WALK); } +void CMainFrame::OnNavigateFly() { SetNavigate(GF_NAVIGATE_FLY); } +void CMainFrame::OnNavigateExam() { SetNavigate(GF_NAVIGATE_EXAMINE); } +void CMainFrame::OnNavigateSlide() { SetNavigate(GF_NAVIGATE_SLIDE); } +void CMainFrame::OnNavigatePan() { SetNavigate(GF_NAVIGATE_PAN); } +void CMainFrame::OnNavigateOrbit() { SetNavigate(GF_NAVIGATE_ORBIT); } +void CMainFrame::OnNavigateVR() { SetNavigate(GF_NAVIGATE_VR); } +void CMainFrame::OnNavigateGame() { SetNavigate(GF_NAVIGATE_GAME); } + +void CMainFrame::OnNavigateReset() +{ + Osmo4 *app = GetApp(); + gf_term_set_option(app->m_term, GF_OPT_NAVIGATION_TYPE, 0); +} + + +LONG CMainFrame::OnNavigate(WPARAM /*wParam*/, LPARAM /*lParam*/) +{ + Osmo4 *gpac = GetApp(); + + /*this is a migrate instruction, just disconnect the player*/ + if (gpac->m_navigate_url.IsEmpty() ) { + gf_term_disconnect(gpac->m_term); + return 0; + } + + if (gf_term_is_supported_url(gpac->m_term, gpac->m_navigate_url, 1, gpac->m_NoMimeFetch)) { + char *str = gf_url_concatenate(m_pPlayList->GetURL(), gpac->m_navigate_url); + if (str) { + m_pPlayList->Truncate(); + m_pPlayList->QueueURL(str); + free(str); + m_pPlayList->RefreshList(); + m_pPlayList->PlayNext(); + return 0; + } + } + + if (m_bFullScreen) { + SetFullscreen(); + m_bRestoreFS = 1; + } + + console_message = gpac->m_navigate_url; + console_err = GF_OK; + PostMessage(WM_CONSOLEMSG); + ShellExecute(NULL, "open", (LPCSTR) gpac->m_navigate_url, NULL, NULL, SW_SHOWNORMAL); + + return 0; +} + +void CMainFrame::OnFileProp() +{ + if (!m_pProps) { + m_pProps = new CFileProps(this); + m_pProps->Create(this); + } + m_pProps->ShowWindow(SW_SHOW); +} + +void CMainFrame::OnUpdateFileProp(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(GetApp()->m_isopen); +} + +void CMainFrame::OnConfigure() +{ + if (!m_pOpt) { + m_pOpt = new COptions(this); + m_pOpt->Create(this); + } + m_pOpt->ShowWindow(SW_SHOW); +} + +void CMainFrame::OnShortcuts() +{ + MessageBox( + "Open File: Ctrl + O\n" + "Open URL: Ctrl + U\n" + "Reload File: Ctrl + R\n" + "Pause/Resume File: Ctrl + P\n" + "Step by Step: Ctrl + S\n" + "Seek +5%: Alt + left arrow\n" + "Seek -5%: Alt + right arrow\n" + "Seek +1min: Alt + up arrow\n" + "Seek -1min: Alt + down arrow\n" + "Fullscreen On/Off: Alt+Return or Escape\n" + "\n" + "Show Properties: Ctrl + I\n" + "Show Playlist: Ctrl + V\n" + "Next Playlist Item: Ctrl + right arrow\n" + "Previous Playlist Item: Ctrl + left arrow\n" + "\n" + "Aspect Ratio Normal: Ctrl + 1\n" + "Aspect Ratio Fill: Ctrl + 2\n" + "Aspect Ratio 4/3: Ctrl + 3\n" + "Aspect Ratio 16/9: Ctrl + 4\n" + + + , "Shortcuts Available on Osmo4", MB_OK); +} + +void CMainFrame::OnNavInfo() +{ + MessageBox( + "* Walk & Fly modes:\n" + "\tH move: H pan - V move: Z-translate - V move+CTRL or Wheel: V pan - Right Click (Walk only): Jump\n" + "\tleft/right: H pan - left/right+CTRL: H translate - up/down: Z-translate - up/down+CTRL: V pan\n" + "* Pan mode:\n" + "\tH move: H pan - V move: V pan - V move+CTRL or Wheel: Z-translate\n" + "\tleft/right: H pan - left/right+CTRL: H translate - up/down: V pan - up/down+CTRL: Z-translate\n" + "* Slide mode:\n" + "\tH move: H translate - V move: V translate - V move+CTRL or Wheel: Z-translate\n" + "\tleft/right: H translate - left/right+CTRL: H pan - up/down: V translate - up/down+CTRL: Z-translate\n" + "* Examine & Orbit mode:\n" + "\tH move: Y-Axis rotate - H move+CTRL: Z-Axis rotate - V move: X-Axis rotate - V move+CTRL or Wheel: Z-translate\n" + "\tleft/right: Y-Axis rotate - left/right+CTRL: H translate - up/down: X-Axis rotate - up/down+CTRL: Y-translate\n" + "* VR mode:\n" + "\tH move: H pan - V move: V pan - V move+CTRL or Wheel: Camera Zoom\n" + "\tleft/right: H pan - up/down: V pan - up/down+CTRL: Camera Zoom\n" + "* Game mode (press END to escape):\n" + "\tH move: H pan - V move: V pan\n" + "\tleft/right: H translate - up/down: Z-translate\n" + "\n" + "* All 3D modes: CTRL+PGUP/PGDOWN will zoom in/out camera (field of view) \n" + + "\n" + "*Slide Mode in 2D:\n" + "\tH move: H translate - V move: V translate - V move+CTRL: zoom\n" + "\tleft/right: H translate - up/down: V translate - up/down+CTRL: zoom\n" + "*Examine Mode in 2D (3D renderer only):\n" + "\tH move: Y-Axis rotate - V move: X-Axis rotate\n" + "\tleft/right: Y-Axis rotate - up/down: X-Axis rotate\n" + + "\n" + "HOME: reset navigation to last viewpoint (2D or 3D navigation)\n" + "SHIFT key in all modes: fast movement\n" + + , "3D navigation keys (\'H\'orizontal and \'V\'ertical) used in GPAC", MB_OK); +} + + + +void CMainFrame::BuildViewList() +{ + Osmo4 *app = GetApp(); + if (!app->m_isopen) return; + + /*THIS IS HARCODED FROM THE MENU LAYOUT */ + CMenu *pMenu = GetMenu()->GetSubMenu(1)->GetSubMenu(0); + while (pMenu->GetMenuItemCount()) pMenu->DeleteMenu(0, MF_BYPOSITION); + + s32 id = ID_VP_0; + nb_viewpoints = 0; + while (1) { + const char *szName = NULL; + Bool bound; + GF_Err e = gf_term_get_viewpoint(app->m_term, nb_viewpoints+1, &szName, &bound); + if (e) break; + if (szName) { + pMenu->AppendMenu(MF_ENABLED, id+nb_viewpoints, szName); + } else { + char szLabel[1024]; + sprintf(szLabel, "Viewpoint #%d", nb_viewpoints+1); + pMenu->AppendMenu(MF_ENABLED, id+nb_viewpoints, szLabel); + } + nb_viewpoints++; + if (nb_viewpoints==ID_VP_19-ID_VP_0) break; + } +} + + +void CMainFrame::BuildStreamList(Bool reset_only) +{ + u32 nb_subs; + CMenu *pSelect; + Osmo4 *app = GetApp(); + + pSelect = GetMenu()->GetSubMenu(2)->GetSubMenu(0); + /*THIS IS HARCODED FROM THE MENU LAYOUT */ + CMenu *pMenu = pSelect->GetSubMenu(0); + while (pMenu->GetMenuItemCount()) pMenu->DeleteMenu(0, MF_BYPOSITION); + pMenu = pSelect->GetSubMenu(1); + while (pMenu->GetMenuItemCount()) pMenu->DeleteMenu(0, MF_BYPOSITION); + pMenu = pSelect->GetSubMenu(2); + while (pMenu->GetMenuItemCount()) pMenu->DeleteMenu(0, MF_BYPOSITION); + + if (reset_only) { + m_bFirstStreamQuery = 1; + return; + } + if (!app->m_isopen || !gf_term_get_option(app->m_term, GF_OPT_CAN_SELECT_STREAMS)) return; + + GF_ObjectManager *root_od = gf_term_get_root_object(app->m_term); + if (!root_od) return; + u32 count = gf_term_get_object_count(app->m_term, root_od); + nb_subs = 0; + + for (u32 i=0; im_term, root_od, i); + if (!odm) return; + + if (gf_term_get_object_info(app->m_term, odm, &info) != GF_OK) break; + if (info.owns_service) { + char *szName = (char *)strrchr(info.service_url, '\\'); + if (!szName) szName = (char *)strrchr(info.service_url, '/'); + if (!szName) szName = (char *) info.service_url; + else szName += 1; + strcpy(szLabel, szName); + szName = strrchr(szLabel, '.'); + if (szName) szName[0] = 0; + } + + switch (info.od_type) { + case GF_STREAM_AUDIO: + pMenu = pSelect->GetSubMenu(0); + if (!info.owns_service) { + if (info.lang) { + sprintf(szLabel, "Language %s (ID %d)", gf_4cc_to_str(info.lang), info.od->objectDescriptorID); + } else { + sprintf(szLabel, "ID %d", info.od->objectDescriptorID); + } + } + pMenu->AppendMenu(MF_ENABLED, ID_SELOBJ_0 + i, szLabel); + break; + case GF_STREAM_VISUAL: + pMenu = pSelect->GetSubMenu(1); + if (!info.owns_service) sprintf(szLabel, "ID %d", info.od->objectDescriptorID); + pMenu->AppendMenu(MF_ENABLED, ID_SELOBJ_0 + i, szLabel); + break; + case GF_STREAM_TEXT: + nb_subs ++; + pMenu = pSelect->GetSubMenu(2); + if (!info.owns_service) { + if (info.lang) { + sprintf(szLabel, "Language %s (ID %d)", gf_4cc_to_str(info.lang), info.od->objectDescriptorID); + } else { + sprintf(szLabel, "ID %d", info.od->objectDescriptorID); + } + } + pMenu->AppendMenu(MF_ENABLED, ID_SELOBJ_0 + i, szLabel); + break; + } + } + if (m_bFirstStreamQuery) { + m_bFirstStreamQuery = 0; + if (!nb_subs && app->m_LookForSubtitles) LookForSubtitles(); + } + +} + +BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam) +{ + int ID = LOWORD(wParam); + Osmo4 *app = GetApp(); + + if ( (ID>=ID_VP_0) && (ID<=ID_VP_0+nb_viewpoints)) { + ID -= ID_VP_0; + gf_term_set_viewpoint(app->m_term, ID+1, NULL); + return TRUE; + } + if ( (ID>=ID_NAV_PREV_0) && (ID<=ID_NAV_PREV_9)) { + ID -= ID_NAV_PREV_0; + s32 prev = m_pPlayList->m_cur_entry - ID; + if (prev>=0) { + m_pPlayList->m_cur_entry = prev; + m_pPlayList->PlayPrev(); + } + return TRUE; + } + if ( (ID>=ID_NAV_NEXT_0) && (ID<=ID_NAV_NEXT_9)) { + ID -= ID_NAV_NEXT_0; + u32 next = m_pPlayList->m_cur_entry + ID; + if (next < gf_list_count(m_pPlayList->m_entries) ) { + m_pPlayList->m_cur_entry = next; + m_pPlayList->PlayNext(); + } + return TRUE; + } + if ( (ID>=ID_SELOBJ_0) && (ID<=ID_SELOBJ_29)) { + ID -= ID_SELOBJ_0; + GF_ObjectManager *root_od = gf_term_get_root_object(app->m_term); + if (!root_od) return TRUE; + GF_ObjectManager *odm = gf_term_get_object(app->m_term, root_od, ID); + gf_term_select_object(app->m_term, odm); + return TRUE; + } + if ( (ID>=ID_SETCHAP_FIRST) && (ID<=ID_SETCHAP_LAST)) { + ID -= ID_SETCHAP_FIRST; + gf_term_play_from_time(app->m_term, (u32) (1000*m_chapters_start[ID]), 0); + return TRUE; + } + return CFrameWnd::OnCommand(wParam, lParam); +} + +void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT ID, BOOL bSys) +{ + Osmo4 *app = GetApp(); + /*viewport list*/ + if (pPopupMenu->GetMenuItemID(0)==ID_VP_0) { + for (int i=0; im_term, i+1, &szName, &bound); + pPopupMenu->EnableMenuItem(i, MF_BYPOSITION); + if (bound) pPopupMenu->CheckMenuItem(i, MF_BYPOSITION | MF_CHECKED); + } + return; + } + /*navigation*/ + if ((pPopupMenu->GetMenuItemID(0)==ID_NAV_PREV_0) || (pPopupMenu->GetMenuItemID(0)==ID_NAV_NEXT_0)) { + int count = pPopupMenu->GetMenuItemCount(); + for (int i=0; iEnableMenuItem(i, MF_BYPOSITION); + } + return; + } + /*stream selection*/ + if (pPopupMenu->m_hMenu == GetMenu()->GetSubMenu(2)->m_hMenu) { + if (!app->m_isopen || !gf_term_get_option(app->m_term, GF_OPT_CAN_SELECT_STREAMS)) { + pPopupMenu->EnableMenuItem(0, MF_BYPOSITION | MF_DISABLED | MF_GRAYED); + } else { + pPopupMenu->EnableMenuItem(0, MF_BYPOSITION | MF_ENABLED); + } + } + if ((pPopupMenu->GetMenuItemID(0)>=ID_SELOBJ_0) && (pPopupMenu->GetMenuItemID(0)<=ID_SELOBJ_29)) { + GF_ObjectManager *root_od = gf_term_get_root_object(app->m_term); + if (!root_od) return; + + int count = pPopupMenu->GetMenuItemCount(); + for (int i=0; iGetMenuItemID(i) - ID_SELOBJ_0; + GF_ObjectManager *odm = gf_term_get_object(app->m_term, root_od, id); + if (!odm) { + pPopupMenu->EnableMenuItem(i, MF_DISABLED | MF_BYPOSITION); + } else { + ODInfo info; + + gf_term_get_object_info(app->m_term, odm, &info); + pPopupMenu->EnableMenuItem(i, MF_BYPOSITION); + pPopupMenu->CheckMenuItem(i, MF_BYPOSITION | (info.status ? MF_CHECKED : MF_UNCHECKED) ); + } + } + return; + } + /*chapters*/ + if (pPopupMenu->m_hMenu == GetMenu()->GetSubMenu(2)->m_hMenu) { + if (!app->m_isopen || !m_num_chapters) { + pPopupMenu->EnableMenuItem(1, MF_BYPOSITION | MF_DISABLED | MF_GRAYED); + } else { + pPopupMenu->EnableMenuItem(1, MF_BYPOSITION | MF_ENABLED); + } + } + if ((pPopupMenu->GetMenuItemID(0)>=ID_SETCHAP_FIRST) && (pPopupMenu->GetMenuItemID(0)<=ID_SETCHAP_LAST)) { + Double now = gf_term_get_time_in_ms(app->m_term); + now /= 1000; + + int count = pPopupMenu->GetMenuItemCount(); + for (int i=0; iGetMenuItemID(i) - ID_SETCHAP_FIRST; + pPopupMenu->EnableMenuItem(i, MF_BYPOSITION); + + Bool is_current = 0; + if (m_chapters_start[id]<=now) { + if (id+1now) is_current = 1; + } else { + is_current = 1; + } + } + pPopupMenu->CheckMenuItem(i, MF_BYPOSITION | (is_current ? MF_CHECKED : MF_UNCHECKED)); + } + return; + } + /*default*/ + CFrameWnd::OnInitMenuPopup(pPopupMenu, ID, bSys); +} + +void CMainFrame::OnCollideDisp() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_COLLISION, GF_COLLISION_DISPLACEMENT); +} + +void CMainFrame::OnUpdateCollideDisp(CCmdUI* pCmdUI) +{ + Osmo4 *gpac = GetApp(); + pCmdUI->Enable(gpac->m_isopen); + pCmdUI->SetCheck( (gf_term_get_option(gpac->m_term, GF_OPT_COLLISION) == GF_COLLISION_DISPLACEMENT) ? 1 : 0); +} + +void CMainFrame::OnCollideNone() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_COLLISION, GF_COLLISION_NONE); +} + +void CMainFrame::OnUpdateCollideNone(CCmdUI* pCmdUI) +{ + Osmo4 *gpac = GetApp(); + pCmdUI->Enable(gpac->m_isopen); + pCmdUI->SetCheck( (gf_term_get_option(gpac->m_term, GF_OPT_COLLISION) == GF_COLLISION_NONE) ? 1 : 0); +} + +void CMainFrame::OnCollideReg() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_COLLISION, GF_COLLISION_NORMAL); +} + +void CMainFrame::OnUpdateCollideReg(CCmdUI* pCmdUI) +{ + Osmo4 *gpac = GetApp(); + pCmdUI->Enable(gpac->m_isopen); + pCmdUI->SetCheck( (gf_term_get_option(gpac->m_term, GF_OPT_COLLISION) == GF_COLLISION_NORMAL) ? 1 : 0); +} + +void CMainFrame::OnHeadlight() +{ + Osmo4 *app = GetApp(); + Bool val = !gf_term_get_option(app->m_term, GF_OPT_HEADLIGHT); + gf_term_set_option(app->m_term, GF_OPT_HEADLIGHT, val); +} + +void CMainFrame::OnUpdateHeadlight(CCmdUI* pCmdUI) +{ + Osmo4 *app = GetApp(); + pCmdUI->Enable(FALSE); + if (!app->m_isopen) return; + u32 type = gf_term_get_option(app->m_term, GF_OPT_NAVIGATION_TYPE); + if (type!=GF_NAVIGATE_TYPE_3D) return; + + pCmdUI->Enable(TRUE); + pCmdUI->SetCheck(gf_term_get_option(app->m_term, GF_OPT_HEADLIGHT) ? 1 : 0); +} + +void CMainFrame::OnGravity() +{ + Osmo4 *app = GetApp(); + Bool val = gf_term_get_option(app->m_term, GF_OPT_GRAVITY) ? 0 : 1; + gf_term_set_option(app->m_term, GF_OPT_GRAVITY, val); +} + +void CMainFrame::OnUpdateGravity(CCmdUI* pCmdUI) +{ + Osmo4 *app = GetApp(); + pCmdUI->Enable(FALSE); + if (!app->m_isopen) return; + u32 type = gf_term_get_option(app->m_term, GF_OPT_NAVIGATION_TYPE); + if (type!=GF_NAVIGATE_TYPE_3D) return; + type = gf_term_get_option(app->m_term, GF_OPT_NAVIGATION); + if (type != GF_NAVIGATE_WALK) return; + pCmdUI->Enable(TRUE); + pCmdUI->SetCheck(gf_term_get_option(app->m_term, GF_OPT_GRAVITY) ? 1 : 0); +} + + +BOOL CMainFrame::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) +{ + + if (((LPNMHDR)lParam)->code == TBN_DROPDOWN) { + RECT rc; + s32 i, count, start; + POINT pt; + CMenu *pPopup = new CMenu(); + pPopup->CreatePopupMenu(); + + m_wndToolBar.GetWindowRect(&rc); + pt.y = rc.bottom; + pt.x = rc.left; + m_wndToolBar.GetToolBarCtrl().GetItemRect(0, &rc); + pt.x += (rc.right - rc.left); + m_wndToolBar.GetToolBarCtrl().GetItemRect(1, &rc); + pt.x += (rc.right - rc.left); + + count = gf_list_count(m_pPlayList->m_entries); + if ( ((LPNMTOOLBAR)lParam)->iItem == ID_NAV_PREV) { + start = m_pPlayList->m_cur_entry - 1; + for (i=0; i<10; i++) { + if (start - i < 0) break; + if (start - i >= count) break; + PLEntry *ple = (PLEntry *) gf_list_get(m_pPlayList->m_entries, start - i); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_NAV_PREV_0 + i, ple->m_disp_name); + } + } else { + start = m_pPlayList->m_cur_entry + 1; + for (i=0; i<10; i++) { + if (start + i >= count) break; + PLEntry *ple = (PLEntry *) gf_list_get(m_pPlayList->m_entries, start + i); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_NAV_NEXT_0 + i, ple->m_disp_name); + } + m_wndToolBar.GetToolBarCtrl().GetItemRect(2, &rc); + pt.x += (rc.right - rc.left); + } + pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this); + delete pPopup; + + return FALSE; + } + return CFrameWnd::OnNotify(wParam, lParam, pResult); +} + +void CMainFrame::OnNavNext() +{ + Osmo4 *app = GetApp(); + /*don't play if last could trigger playlist loop*/ + if ((m_pPlayList->m_cur_entry<0) || (gf_list_count(m_pPlayList->m_entries) == 1 + (u32) m_pPlayList->m_cur_entry)) return; + m_pPlayList->PlayNext(); +} + +void CMainFrame::OnUpdateNavNext(CCmdUI* pCmdUI) +{ + if (m_pPlayList->m_cur_entry<0) pCmdUI->Enable(FALSE); + else if ((u32) m_pPlayList->m_cur_entry + 1 == gf_list_count(m_pPlayList->m_entries) ) pCmdUI->Enable(FALSE); + else pCmdUI->Enable(TRUE); +} + +void CMainFrame::OnNavPrev() +{ + Osmo4 *app = GetApp(); + if (m_pPlayList->m_cur_entry<=0) return; + m_pPlayList->PlayPrev(); +} + +void CMainFrame::OnUpdateNavPrev(CCmdUI* pCmdUI) +{ + if (m_pPlayList->m_cur_entry<=0) pCmdUI->Enable(FALSE); + else pCmdUI->Enable(TRUE); +} + + +void CMainFrame::OnClearNav() +{ + m_pPlayList->ClearButPlaying(); +} + +void CMainFrame::OnViewPlaylist() +{ + m_pPlayList->ShowWindow(m_pPlayList->IsWindowVisible() ? SW_HIDE : SW_SHOW); +} + +void CMainFrame::OnUpdateViewPlaylist(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(m_pPlayList->IsWindowVisible() ? 1 : 0); +} +void CMainFrame::OnPlaylistLoop() +{ + GetApp()->m_Loop = !GetApp()->m_Loop; +} + +void CMainFrame::OnUpdatePlaylistLoop(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(GetApp()->m_Loop ? 1 : 0); +} + +void CMainFrame::OnAddSubtitle() +{ + CFileDialog fd(TRUE,NULL,NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, "All Subtitles|*.srt;*.sub;*.ttxt;*.xml|SRT Subtitles|*.srt|SUB Subtitles|*.sub|3GPP TimedText|*.ttxt|QuckTime TeXML|*.xml|"); + if (fd.DoModal() != IDOK) return; + + AddSubtitle(fd.GetPathName(), 1); +} + +void CMainFrame::AddSubtitle(const char *fileName, Bool auto_play) +{ + gf_term_add_object(GetApp()->m_term, fileName, auto_play); +} + +static Bool subs_enum_dir_item(void *cbck, char *item_name, char *item_path) +{ + CMainFrame *_this = (CMainFrame *)cbck; + _this->AddSubtitle(item_path, 0); + return 0; +} + +void CMainFrame::LookForSubtitles() +{ + char dir[GF_MAX_PATH]; + CString url = m_pPlayList->GetURL(); + strcpy(dir, url); + char *sep = strrchr(dir, '\\'); + if (!sep) ::GetCurrentDirectory(GF_MAX_PATH, dir); + else sep[0] = 0; + + gf_enum_directory(dir, 0, subs_enum_dir_item, this, "ttxt;srt"); +} + +void CMainFrame::OnCacheEnable() +{ + Osmo4 *app = GetApp(); + u32 state = gf_term_get_option(app->m_term, GF_OPT_MEDIA_CACHE); + if (state==GF_MEDIA_CACHE_DISABLED) { + gf_term_set_option(app->m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_ENABLED); + } else if (state==GF_MEDIA_CACHE_DISABLED) { + gf_term_set_option(app->m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISABLED); + } +} + +void CMainFrame::OnUpdateCacheEnable(CCmdUI* pCmdUI) +{ + Osmo4 *app = GetApp(); + u32 state = gf_term_get_option(app->m_term, GF_OPT_MEDIA_CACHE); + switch (state) { + case GF_MEDIA_CACHE_ENABLED: + pCmdUI->SetText("Enabled"); + pCmdUI->Enable(TRUE); + break; + case GF_MEDIA_CACHE_RUNNING: + pCmdUI->SetText("Running"); + pCmdUI->Enable(FALSE); + break; + case GF_MEDIA_CACHE_DISABLED: + pCmdUI->SetText("Disabled"); + break; + } +} + +void CMainFrame::OnUpdateCacheStop(CCmdUI* pCmdUI) +{ + Osmo4 *app = GetApp(); + u32 state = gf_term_get_option(app->m_term, GF_OPT_MEDIA_CACHE); + pCmdUI->Enable( (state==GF_MEDIA_CACHE_RUNNING) ? TRUE : FALSE); +} + +void CMainFrame::OnCacheStop() +{ + Osmo4 *app = GetApp(); + gf_term_set_option(app->m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISABLED); +} +void CMainFrame::OnCacheAbort() +{ + Osmo4 *app = GetApp(); + gf_term_set_option(app->m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISCARD); +} + +void CMainFrame::OnFileExit() +{ + DestroyWindow(); +} + + +void CMainFrame::BuildChapterList(Bool reset_only) +{ + CMenu *pChaps; + ODInfo odi; + NetInfoCommand com; + Osmo4 *app = GetApp(); + + /*THIS IS HARCODED FROM THE MENU LAYOUT */ + pChaps = GetMenu()->GetSubMenu(2)->GetSubMenu(1); + while (pChaps->GetMenuItemCount()) pChaps->DeleteMenu(0, MF_BYPOSITION); + + if (m_chapters_start) free(m_chapters_start); + m_chapters_start = NULL; + m_num_chapters = 0; + if (reset_only) return; + + GF_ObjectManager *root_od = gf_term_get_root_object(app->m_term); + if (!root_od) return; + if (gf_term_get_object_info(app->m_term, root_od, &odi) != GF_OK) return; + + u32 count = gf_list_count(odi.od->OCIDescriptors); + m_num_chapters = 0; + for (u32 i=0; iOCIDescriptors, i); + if (seg->tag != GF_ODF_SEGMENT_TAG) continue; + + if (seg->SegmentName && strlen((const char *)seg->SegmentName)) { + strcpy(szLabel, (const char *) seg->SegmentName); + } else { + sprintf(szLabel, "Chapter #%02d", m_num_chapters+1); + } + pChaps->AppendMenu(MF_ENABLED, ID_SETCHAP_FIRST + m_num_chapters, szLabel); + + m_chapters_start = (Double *) realloc(m_chapters_start, sizeof(Double)*(m_num_chapters+1)); + m_chapters_start[m_num_chapters] = seg->startTime; + m_num_chapters++; + } + + /*get any service info*/ + if (!m_bStartupFile && gf_term_get_service_info(app->m_term, root_od, &com) == GF_OK) { + CString title(""); + if (com.track_info) { title.Format("%02d ", (u32) (com.track_info>>16) ); } + if (com.artist) { title += com.artist; title += " "; } + if (com.name) { title += com.name; title += " "; } + if (com.album) { title += "("; title += com.album; title += ")"; } + + if (title.GetLength()) SetWindowText(title); + } +} + +void CMainFrame::OnViewCPU() +{ + m_show_rti = !m_show_rti; +} + +void CMainFrame::OnUpdateViewCPU(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(TRUE); + pCmdUI->SetCheck(m_show_rti); +} + + +void CMainFrame::OnFileCopy() +{ + u32 len; + const char *text = gf_term_get_text_selection(GetApp()->m_term, 0); + if (!text) return; + + if (!IsClipboardFormatAvailable(CF_TEXT)) return; + if (!OpenClipboard()) return; + EmptyClipboard(); + + len = strlen(text); + if (!len) return; + + HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (len + 1) * sizeof(char)); + LPTSTR lptstrCopy = (char *) GlobalLock(hglbCopy); + memcpy(lptstrCopy, text, len * sizeof(char)); + lptstrCopy[len] = 0; + GlobalUnlock(hglbCopy); + SetClipboardData(CF_TEXT, hglbCopy); + CloseClipboard(); +} + +void CMainFrame::OnUpdateFileCopy(CCmdUI* pCmdUI) +{ + Osmo4 *app = GetApp(); + if (IsClipboardFormatAvailable(CF_TEXT) + && app->m_term + && (gf_term_get_text_selection(app->m_term, 1)!=NULL) + ) { + pCmdUI->Enable(TRUE); + } else { + pCmdUI->Enable(FALSE); + } +} + + +void CMainFrame::OnFilePaste() +{ + if (!IsClipboardFormatAvailable(CF_TEXT)) return; + if (!OpenClipboard()) return; + + HGLOBAL hglbCopy = GetClipboardData(CF_TEXT); + if (hglbCopy) { + LPTSTR lptstrCopy = (char *) GlobalLock(hglbCopy); + gf_term_paste_text(GetApp()->m_term, lptstrCopy, 0); + GlobalUnlock(hglbCopy); + } + CloseClipboard(); +} + +void CMainFrame::OnUpdateFilePaste(CCmdUI* pCmdUI) +{ + Osmo4 *app = GetApp(); + if (IsClipboardFormatAvailable(CF_TEXT) + && app->m_term + && (gf_term_paste_text(app->m_term, NULL, 1)==GF_OK) + ) { + pCmdUI->Enable(TRUE); + } else { + pCmdUI->Enable(FALSE); + } +} + + diff --git a/applications/osmo4_w32/MainFrm.h b/applications/osmo4_w32/MainFrm.h new file mode 100644 index 0000000..7eba87a --- /dev/null +++ b/applications/osmo4_w32/MainFrm.h @@ -0,0 +1,223 @@ +// MainFrm.h : interface of the CMainFrame class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_MAINFRM_H__3666B63B_D886_4F0B_9953_A2AF09E3C15A__INCLUDED_) +#define AFX_MAINFRM_H__3666B63B_D886_4F0B_9953_A2AF09E3C15A__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include +#include +#include +#include + +#include "FileProps.h" +#include "Options.h" +#include "AddressBar.h" +#include "Sliders.h" +#include "Playlist.h" + + +class CChildView : public CWnd +{ +// Construction +public: + CChildView(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CChildView) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CChildView(); + + // Generated message map functions +protected: + //{{AFX_MSG(CChildView) + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +class CMainFrame : public CFrameWnd +{ + +public: + CMainFrame(); +protected: + DECLARE_DYNAMIC(CMainFrame) + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMainFrame) + public: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo); + virtual BOOL DestroyWindow(); + protected: + virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CMainFrame(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +public: + CStatusBar m_wndStatusBar; + CToolBar m_wndToolBar; + Sliders m_Sliders; + AddressBar m_Address; + CFileProps *m_pProps; + COptions *m_pOpt; + Playlist *m_pPlayList; + CChildView *m_pWndView; + Bool m_bFullScreen, m_bRestoreFS; + u32 m_timer_on; + CString console_message; + CString console_service; + GF_Err console_err; + u32 m_aspect_ratio; + RECT backup_wnd_rc; + Bool m_bFirstStreamQuery; + /*filter progress events to avoid killing importers with status bar text display...*/ + s32 m_last_prog; + Bool m_show_rti; + Bool m_bStartupFile; + +public: + void SetFullscreen(); + void BuildViewList(); + void BuildStreamList(Bool reset_ony); + void BuildChapterList(Bool reset_ony); + void SetProgTimer(Bool bOn); + void AddSubtitle(const char *fileName, Bool auto_play); + +private: + void ForwardMessage(); + HICON m_icoerror, m_icomessage; + s32 nb_viewpoints; + Bool m_bInitShow; + + void SetNavigate(u32 mode); + void LookForSubtitles(); + + Double *m_chapters_start; + u32 m_num_chapters; + + +// Generated message map functions +protected: + //{{AFX_MSG(CMainFrame) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSetFocus(CWnd *pOldWnd); + afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu); + afx_msg BOOL OnCommand(WPARAM wParam, LPARAM lParam); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnMove(int x, int y); + afx_msg LONG OnSetSize(WPARAM wParam, LPARAM lParam); + afx_msg LONG OnNavigate(WPARAM wParam, LPARAM lParam); + afx_msg LONG Open(WPARAM wParam, LPARAM lParam); + afx_msg LONG NewInstanceOpened(WPARAM wParam, LPARAM lParam); + + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnChar( UINT nChar, UINT nRepCnt, UINT nFlags ); + afx_msg void OnSysKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags ); + afx_msg void OnSysKeyUp( UINT nChar, UINT nRepCnt, UINT nFlags ); + afx_msg void OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags ); + afx_msg void OnKeyUp( UINT nChar, UINT nRepCnt, UINT nFlags ); + afx_msg void OnDropFiles(HDROP hDropInfo); + afx_msg LONG OnConsoleMessage(WPARAM wParam, LPARAM lParam); + afx_msg void OnViewOriginal(); + afx_msg void OnViewFullscreen(); + afx_msg void OnArKeep(); + afx_msg void OnArFill(); + afx_msg void OnAr43(); + afx_msg void OnAr169(); + afx_msg void OnUpdateAr169(CCmdUI* pCmdUI); + afx_msg void OnUpdateAr43(CCmdUI* pCmdUI); + afx_msg void OnUpdateArFill(CCmdUI* pCmdUI); + afx_msg void OnUpdateArKeep(CCmdUI* pCmdUI); + afx_msg void OnNavigateNone(); + afx_msg void OnNavigateWalk(); + afx_msg void OnNavigateFly(); + afx_msg void OnNavigateExam(); + afx_msg void OnNavigateSlide(); + afx_msg void OnNavigatePan(); + afx_msg void OnNavigateOrbit(); + afx_msg void OnNavigateGame(); + afx_msg void OnNavigateVR(); + afx_msg void OnNavigateReset(); + afx_msg void OnShortcuts(); + afx_msg void OnConfigure(); + afx_msg void OnFileProp(); + afx_msg void OnViewPlaylist(); + afx_msg void OnUpdateFileProp(CCmdUI* pCmdUI); + afx_msg void OnUpdateNavigate(CCmdUI* pCmdUI); + afx_msg void OnCacheEnable(); + afx_msg void OnUpdateCacheEnable(CCmdUI* pCmdUI); + afx_msg void OnCacheStop(); + afx_msg void OnCacheAbort(); + afx_msg void OnUpdateCacheStop(CCmdUI* pCmdUI); + afx_msg void OnCollideDisp(); + afx_msg void OnUpdateCollideDisp(CCmdUI* pCmdUI); + afx_msg void OnCollideNone(); + afx_msg void OnUpdateCollideNone(CCmdUI* pCmdUI); + afx_msg void OnCollideReg(); + afx_msg void OnUpdateCollideReg(CCmdUI* pCmdUI); + afx_msg void OnHeadlight(); + afx_msg void OnUpdateHeadlight(CCmdUI* pCmdUI); + afx_msg void OnGravity(); + afx_msg void OnUpdateGravity(CCmdUI* pCmdUI); + afx_msg void OnNavInfo(); + afx_msg void OnNavNext(); + afx_msg void OnNavPrev(); + afx_msg void OnUpdateNavNext(CCmdUI* pCmdUI); + afx_msg void OnUpdateNavPrev(CCmdUI* pCmdUI); + afx_msg void OnClearNav(); + afx_msg void OnUpdateViewPlaylist(CCmdUI* pCmdUI); + afx_msg void OnPlaylistLoop(); + afx_msg void OnUpdatePlaylistLoop(CCmdUI* pCmdUI); + afx_msg void OnAddSubtitle(); + afx_msg void OnFileExit(); + afx_msg void OnViewCPU(); + afx_msg void OnUpdateViewCPU(CCmdUI* pCmdUI); + afx_msg void OnFileCopy(); + afx_msg void OnUpdateFileCopy(CCmdUI* pCmdUI); + afx_msg void OnFilePaste(); + afx_msg void OnUpdateFilePaste(CCmdUI* pCmdUI); + + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAINFRM_H__3666B63B_D886_4F0B_9953_A2AF09E3C15A__INCLUDED_) diff --git a/applications/osmo4_w32/OpenUrl.cpp b/applications/osmo4_w32/OpenUrl.cpp new file mode 100644 index 0000000..37f6279 --- /dev/null +++ b/applications/osmo4_w32/OpenUrl.cpp @@ -0,0 +1,98 @@ +// OpenUrl.cpp : implementation file +// + +#include "stdafx.h" +#include "Osmo4.h" +#include "OpenUrl.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// COpenUrl dialog + + +COpenUrl::COpenUrl(CWnd* pParent /*=NULL*/) + : CDialog(COpenUrl::IDD, pParent) +{ + //{{AFX_DATA_INIT(COpenUrl) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void COpenUrl::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COpenUrl) + DDX_Control(pDX, IDC_COMBOURL, m_URLs); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COpenUrl, CDialog) + //{{AFX_MSG_MAP(COpenUrl) + ON_BN_CLICKED(IDC_BUTGO, OnButgo) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + + + +#define MAX_LAST_FILES 20 +void UpdateLastFiles(GF_Config *cfg, const char *URL) +{ + u32 nb_entries; + gf_cfg_set_key(cfg, "RecentFiles", URL, NULL); + gf_cfg_insert_key(cfg, "RecentFiles", URL, "", 0); + /*remove last entry if needed*/ + nb_entries = gf_cfg_get_key_count(cfg, "RecentFiles"); + if (nb_entries>MAX_LAST_FILES) { + gf_cfg_set_key(cfg, "RecentFiles", gf_cfg_get_key_name(cfg, "RecentFiles", nb_entries-1), NULL); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// COpenUrl message handlers + +void COpenUrl::OnButgo() +{ + CString URL; + int sel = m_URLs.GetCurSel(); + if (sel == CB_ERR) { + m_URLs.GetWindowText(URL); + } else { + m_URLs.GetLBText(sel, URL); + } + if (!URL.GetLength()) { + EndDialog(IDCANCEL); + return; + } + + Osmo4 *gpac = GetApp(); + + m_url = URL; + UpdateLastFiles(gpac->m_user.config, (const char *) URL); + EndDialog(IDOK); +} + +BOOL COpenUrl::OnInitDialog() +{ + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + u32 i=0; + + while (m_URLs.GetCount()) m_URLs.DeleteString(0); + while (1) { + const char *sOpt = gf_cfg_get_key_name(gpac->m_user.config, "RecentFiles", i); + if (!sOpt) break; + m_URLs.AddString(sOpt); + i++; + } + return TRUE; +} diff --git a/applications/osmo4_w32/OpenUrl.h b/applications/osmo4_w32/OpenUrl.h new file mode 100644 index 0000000..4a4bd65 --- /dev/null +++ b/applications/osmo4_w32/OpenUrl.h @@ -0,0 +1,49 @@ +#if !defined(AFX_OPENURL_H__ADB51A74_305E_4183_8D44_03EEB83D2BFA__INCLUDED_) +#define AFX_OPENURL_H__ADB51A74_305E_4183_8D44_03EEB83D2BFA__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// OpenUrl.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// COpenUrl dialog + +class COpenUrl : public CDialog +{ +// Construction +public: + COpenUrl(CWnd* pParent = NULL); // standard constructor + CString m_url; + +// Dialog Data + //{{AFX_DATA(COpenUrl) + enum { IDD = IDD_OPENFILE }; + CComboBox m_URLs; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COpenUrl) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COpenUrl) + afx_msg void OnBrowse(); + afx_msg void OnButgo(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_OPENURL_H__ADB51A74_305E_4183_8D44_03EEB83D2BFA__INCLUDED_) diff --git a/applications/osmo4_w32/Options.cpp b/applications/osmo4_w32/Options.cpp new file mode 100644 index 0000000..61b4d92 --- /dev/null +++ b/applications/osmo4_w32/Options.cpp @@ -0,0 +1,1982 @@ +// Options.cpp : implementation file +// + +#include "stdafx.h" +#include "Osmo4.h" +#include "MainFrm.h" + +#include +#include +#include +#include +#include + +#include + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// COptions dialog + + +COptions::COptions(CWnd* pParent /*=NULL*/) + : CDialog(COptions::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptions) + //}}AFX_DATA_INIT +} + + +void COptions::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptions) + DDX_Control(pDX, IDC_SELECT, m_Selector); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptions, CDialog) + //{{AFX_MSG_MAP(COptions) + ON_BN_CLICKED(IDC_SAVEOPT, OnSaveopt) + ON_WM_CLOSE() + ON_WM_DESTROY() + ON_CBN_SELCHANGE(IDC_SELECT, OnSelchangeSelect) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +BOOL COptions::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_general.Create(IDD_OPT_GEN, this); + m_systems.Create(IDD_OPT_SYSTEMS, this); + m_render.Create(IDD_OPT_RENDER, this); + m_render2d.Create(IDD_OPT_RENDER2D, this); + m_render3d.Create(IDD_OPT_RENDER3D, this); + m_decoder.Create(IDD_OPT_DECODER, this); + m_audio.Create(IDD_OPT_AUDIO, this); + m_video.Create(IDD_OPT_VIDEO, this); + m_http.Create(IDD_OPT_HTTP, this); + m_font.Create(IDD_OPT_FONT, this); + m_stream.Create(IDD_OPT_STREAM, this); + m_cache.Create(IDD_OPT_MCACHE, this); + m_files.Create(IDD_OPT_FILETYPES, this); + m_logs.Create(IDD_OPT_LOGS, this); + + m_Selector.AddString("General"); + m_Selector.AddString("MPEG-4 Systems"); + m_Selector.AddString("Media Decoders"); + m_Selector.AddString("Compositor"); + m_Selector.AddString("2D Drawing"); + m_Selector.AddString("3D Drawing"); + m_Selector.AddString("Video Output"); + m_Selector.AddString("Audio Output"); + m_Selector.AddString("Text Engine"); + m_Selector.AddString("File Download"); + m_Selector.AddString("Real-Time Streaming"); + m_Selector.AddString("Streaming Cache"); + m_Selector.AddString("File Types"); + m_Selector.AddString("Log System"); + + HideAll(); + + const char *sOpt = gf_cfg_get_key(GetApp()->m_user.config, "General", "ConfigPanel"); + u32 sel = sOpt ? atoi(sOpt) : 0; + if (sel>13) sel=13; + m_Selector.SetCurSel(sel); + m_general.ShowWindow(SW_SHOW); + + OnSelchangeSelect(); + + return TRUE; +} + + +///////////////////////////////////////////////////////////////////////////// +// COptions message handlers + +void COptions::HideAll() +{ + m_general.ShowWindow(SW_HIDE); + m_systems.ShowWindow(SW_HIDE); + m_render.ShowWindow(SW_HIDE); + m_render2d.ShowWindow(SW_HIDE); + m_render3d.ShowWindow(SW_HIDE); + m_audio.ShowWindow(SW_HIDE); + m_video.ShowWindow(SW_HIDE); + m_http.ShowWindow(SW_HIDE); + m_font.ShowWindow(SW_HIDE); + m_stream.ShowWindow(SW_HIDE); + m_decoder.ShowWindow(SW_HIDE); + m_cache.ShowWindow(SW_HIDE); + m_files.ShowWindow(SW_HIDE); + m_files.ShowWindow(SW_HIDE); + m_logs.ShowWindow(SW_HIDE); +} + +void COptions::OnSelchangeSelect() +{ + HideAll(); + switch (m_Selector.GetCurSel()) { + case 0: m_general.ShowWindow(SW_SHOW); break; + case 1: m_systems.ShowWindow(SW_SHOW); break; + case 2: m_decoder.ShowWindow(SW_SHOW); break; + case 3: m_render.ShowWindow(SW_SHOW); break; + case 4: m_render2d.ShowWindow(SW_SHOW); break; + case 5: m_render3d.ShowWindow(SW_SHOW); break; + case 6: m_video.ShowWindow(SW_SHOW); break; + case 7: m_audio.ShowWindow(SW_SHOW); break; + case 8: m_font.ShowWindow(SW_SHOW); break; + case 9: m_http.ShowWindow(SW_SHOW); break; + case 10: m_stream.ShowWindow(SW_SHOW); break; + case 11: m_cache.ShowWindow(SW_SHOW); break; + case 12: m_files.ShowWindow(SW_SHOW); break; + case 13: m_logs.ShowWindow(SW_SHOW); break; + } +} + +void COptions::OnSaveopt() +{ + m_general.SaveOptions(); + m_systems.SaveOptions(); + m_decoder.SaveOptions(); + m_render.SaveOptions(); + m_render2d.SaveOptions(); + m_render3d.SaveOptions(); + m_audio.SaveOptions(); + m_video.SaveOptions(); + m_http.SaveOptions(); + m_font.SaveOptions(); + m_stream.SaveOptions(); + m_cache.SaveOptions(); + m_logs.SaveOptions(); + + Osmo4 *gpac = GetApp(); + gf_term_set_option(gpac->m_term, GF_OPT_RELOAD_CONFIG, 1); + m_render2d.SetYUV(); +} + +void COptions::OnClose() +{ + char str[20]; + sprintf(str, "%d", m_Selector.GetCurSel()); + gf_cfg_set_key(GetApp()->m_user.config, "General", "ConfigPanel", str); + + DestroyWindow(); +} + +void COptions::OnDestroy() +{ + CDialog::OnDestroy(); + delete this; + ((CMainFrame *)GetApp()->m_pMainWnd)->m_pOpt = NULL; +} + + +COptGen::COptGen(CWnd* pParent /*=NULL*/) + : CDialog(COptGen::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptGen) + //}}AFX_DATA_INIT +} + + +void COptGen::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptGen) + DDX_Control(pDX, IDC_LOOKFORSUB, m_LookForSubs); + DDX_Control(pDX, IDC_DUMP_XMT, m_ViewXMT); + DDX_Control(pDX, IDC_NO_CONSOLE, m_NoConsole); + DDX_Control(pDX, IDC_LOOP, m_Loop); + DDX_Control(pDX, IDC_SINGLE_INSTANCE, m_SingleInstance); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptGen, CDialog) + //{{AFX_MSG_MAP(COptGen) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptGen message handlers + + + +BOOL COptGen::OnInitDialog() +{ + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "Loop"); + m_Loop.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "LookForSubtitles"); + m_LookForSubs.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "ConsoleOff"); + m_NoConsole.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "ViewXMT"); + m_ViewXMT.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "SingleInstance"); + m_SingleInstance.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + return TRUE; +} + +void COptGen::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + + gpac->m_Loop = m_Loop.GetCheck(); + gf_cfg_set_key(gpac->m_user.config, "General", "Loop", gpac->m_Loop ? "yes" : "no"); + gpac->m_LookForSubtitles = m_LookForSubs.GetCheck(); + gf_cfg_set_key(gpac->m_user.config, "General", "LookForSubtitles", gpac->m_LookForSubtitles ? "yes" : "no"); + gpac->m_NoConsole = m_NoConsole.GetCheck(); + gf_cfg_set_key(gpac->m_user.config, "General", "ConsoleOff", gpac->m_NoConsole ? "yes" : "no"); + gpac->m_ViewXMTA = m_ViewXMT.GetCheck(); + gf_cfg_set_key(gpac->m_user.config, "General", "ViewXMT", gpac->m_ViewXMTA ? "yes" : "no"); + gpac->m_SingleInstance = m_SingleInstance.GetCheck(); + gf_cfg_set_key(gpac->m_user.config, "General", "SingleInstance", gpac->m_SingleInstance ? "yes" : "no"); +} + +COptSystems::COptSystems(CWnd* pParent /*=NULL*/) + : CDialog(COptSystems::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptSystems) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void COptSystems::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptSystems) + DDX_Control(pDX, IDC_FORCE_DURATION, m_ForceDuration); + DDX_Control(pDX, IDC_DEC_THREAD, m_Threading); + DDX_Control(pDX, IDC_BIFSDROP, m_BifsAlwaysDrawn); + DDX_Control(pDX, IDC_LANG, m_Lang); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptSystems, CDialog) + //{{AFX_MSG_MAP(COptSystems) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptSystems message handlers + + + + + +BOOL COptSystems::OnInitDialog() +{ + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "Language3CC"); + if (!sOpt) sOpt = "eng"; + s32 select = 0; + while (m_Lang.GetCount()) m_Lang.DeleteString(0); + s32 i = 0; + while (GF_ISO639_Lang[i]) { + /*only use common languages (having both 2- and 3-char code names)*/ + if (GF_ISO639_Lang[i+2][0]) { + m_Lang.AddString(GF_ISO639_Lang[i]); + if (sOpt && !stricmp(sOpt, GF_ISO639_Lang[i+1])) select = m_Lang.GetCount() - 1; + } + i += 3; + } + m_Lang.SetCurSel(select); + + + /*system config*/ + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "ThreadingPolicy"); + select = 0; + while (m_Threading.GetCount()) m_Threading.DeleteString(0); + m_Threading.AddString("Single Thread"); + m_Threading.AddString("Mutli Thread"); + if (sOpt && !stricmp(sOpt, "Multi")) select = 1; + m_Threading.AddString("Free"); + if (sOpt && !stricmp(sOpt, "Free")) select = 2; + m_Threading.SetCurSel(select); + + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "ForceSingleClock"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_ForceDuration.SetCheck(1); + } else { + m_ForceDuration.SetCheck(0); + } + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "AlwaysDrawBIFS"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_BifsAlwaysDrawn.SetCheck(1); + } else { + m_BifsAlwaysDrawn.SetCheck(0); + } + + + return TRUE; +} + + +void COptSystems::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + + s32 sel = m_Lang.GetCurSel(); + u32 i=0; + while (GF_ISO639_Lang[i]) { + /*only use common languages (having both 2- and 3-char code names)*/ + if (GF_ISO639_Lang[i+2][0]) { + if (!sel) break; + sel--; + } + i+=3; + } + gf_cfg_set_key(gpac->m_user.config, "Systems", "LanguageName", GF_ISO639_Lang[i]); + gf_cfg_set_key(gpac->m_user.config, "Systems", "Language3CC", GF_ISO639_Lang[i+1]); + gf_cfg_set_key(gpac->m_user.config, "Systems", "Language2CC", GF_ISO639_Lang[i+2]); + + sel = m_Threading.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Systems", "ThreadingPolicy", (sel==0) ? "Single" : ( (sel==1) ? "Multi" : "Free")); + gf_cfg_set_key(gpac->m_user.config, "Systems", "ForceSingleClock", m_ForceDuration.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Systems", "AlwaysDrawBIFS", m_BifsAlwaysDrawn.GetCheck() ? "yes" : "no"); +} + + +OptDecoder::OptDecoder(CWnd* pParent /*=NULL*/) + : CDialog(OptDecoder::IDD, pParent) +{ + //{{AFX_DATA_INIT(OptDecoder) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void OptDecoder::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(OptDecoder) + DDX_Control(pDX, IDC_VIDEC_LIST, m_Video); + DDX_Control(pDX, IDC_AUDEC_LIST, m_Audio); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(OptDecoder, CDialog) + //{{AFX_MSG_MAP(OptDecoder) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// OptDecoder message handlers + + + +BOOL OptDecoder::OnInitDialog() +{ + u32 i; + const char *sOpt; + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + + /*audio dec enum*/ + while (m_Audio.GetCount()) m_Audio.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "DefAudioDec"); + u32 count = gf_modules_get_count(gpac->m_user.modules); + GF_BaseDecoder *ifce; + s32 select = 0; + s32 to_sel = 0; + for (i=0; im_user.modules, i, GF_MEDIA_DECODER_INTERFACE); + if (!ifce) continue; + + if (ifce->CanHandleStream(ifce, GF_STREAM_AUDIO, 0, NULL, 0, 0)) { + if (sOpt && !stricmp(ifce->module_name, sOpt)) select = to_sel; + m_Audio.AddString(ifce->module_name); + to_sel++; + } + gf_modules_close_interface((GF_BaseInterface *)ifce); + } + m_Audio.SetCurSel(select); + + /*video dec enum*/ + while (m_Video.GetCount()) m_Video.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "DefVideoDec"); + count = gf_modules_get_count(gpac->m_user.modules); + select = 0; + to_sel = 0; + for (i=0; im_user.modules, i, GF_MEDIA_DECODER_INTERFACE); + if (!ifce) continue; + + if (ifce->CanHandleStream(ifce, GF_STREAM_VISUAL, 0, NULL, 0, 0)) { + if (sOpt && !stricmp(ifce->module_name, sOpt)) select = to_sel; + m_Video.AddString(ifce->module_name); + to_sel++; + } + gf_modules_close_interface((GF_BaseInterface *)ifce); + } + m_Video.SetCurSel(select); + return TRUE; +} + +void OptDecoder::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + char str[100]; + m_Audio.GetWindowText(str, 100); + gf_cfg_set_key(gpac->m_user.config, "Systems", "DefAudioDec", str); + m_Video.GetWindowText(str, 100); + gf_cfg_set_key(gpac->m_user.config, "Systems", "DefVideoDec", str); + +} + +COptRender::COptRender(CWnd* pParent /*=NULL*/) + : CDialog(COptRender::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptRender) + //}}AFX_DATA_INIT +} + + +void COptRender::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptRender) + DDX_Control(pDX, IDC_DRAW_BOUNDS, m_DrawBounds); + DDX_Control(pDX, IDC_GD_LIST, m_Graphics); + DDX_Control(pDX, IDC_USE_RENDER3D, m_Use3DRender); + DDX_Control(pDX, IDC_AA_LIST, m_AntiAlias); + DDX_Control(pDX, IDC_FORCE_SIZE, m_ForceSize); + DDX_Control(pDX, IDC_FAST_RENDER, m_HighSpeed); + DDX_Control(pDX, IDC_BIFS_RATE, m_BIFSRate); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptRender, CDialog) + //{{AFX_MSG_MAP(COptRender) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptRender message handlers + + + +#define NUM_RATES 11 +static char *BIFSRates[11] = +{ + "5.0", + "7.5", + "10.0", + "12.5", + "15.0", + "24.0", + "25.0", + "30.0", + "50.0", + "60.0", + "100.0", +}; + + + +BOOL COptRender::OnInitDialog() +{ + s32 i; + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "ForceOpenGL"); + m_Use3DRender.SetCheck( (sOpt && !strcmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "ForceSceneSize"); + m_ForceSize.SetCheck( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "FrameRate"); + if (!sOpt) sOpt = "30.0"; + s32 select = 0; + while (m_BIFSRate.GetCount()) m_BIFSRate.DeleteString(0); + for (i = 0; im_user.config, "Compositor", "HighSpeed"); + m_HighSpeed.SetCheck( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "AntiAlias"); + while (m_AntiAlias.GetCount()) m_AntiAlias.DeleteString(0); + + m_AntiAlias.AddString("None"); + m_AntiAlias.AddString("Text only"); + m_AntiAlias.AddString("Complete"); + select = 2; + if (sOpt && !stricmp(sOpt, "Text")) select = 1; + else if (sOpt && !stricmp(sOpt, "None")) select = 0; + m_AntiAlias.SetCurSel(select); + + /*graphics driver enum*/ + while (m_Graphics.GetCount()) m_Graphics.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "Raster2D"); + s32 count = gf_modules_get_count(gpac->m_user.modules); + GF_BaseInterface *ifce; + select = 0; + u32 to_sel = 0; + for (i=0; im_user.modules, i, GF_RASTER_2D_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(ifce->module_name, sOpt)) select = to_sel; + m_Graphics.AddString(ifce->module_name); + gf_modules_close_interface(ifce); + to_sel++; + } + m_Graphics.SetCurSel(select); + + + m_DrawBounds.AddString("None"); + m_DrawBounds.AddString("Box/Rect"); + m_DrawBounds.AddString("AABB Tree"); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "BoundingVolume"); + if (sOpt && !stricmp(sOpt, "Box")) m_DrawBounds.SetCurSel(1); + else if (sOpt && !stricmp(sOpt, "AABB")) m_DrawBounds.SetCurSel(2); + else m_DrawBounds.SetCurSel(0); + + return TRUE; +} + + +Bool COptRender::SaveOptions() +{ + char str[50]; + Osmo4 *gpac = GetApp(); + + gf_cfg_set_key(gpac->m_user.config, "Compositor", "HighSpeed", m_HighSpeed.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "ForceSceneSize", m_ForceSize.GetCheck() ? "yes" : "no"); + + s32 sel = m_BIFSRate.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "FrameRate", BIFSRates[sel]); + + sel = m_AntiAlias.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "AntiAlias", (sel==0) ? "None" : ( (sel==1) ? "Text" : "All")); + + sel = m_DrawBounds.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "BoundingVolume", (sel==2) ? "AABB" : (sel==1) ? "Box" : "None"); + + m_Graphics.GetWindowText(str, 50); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "Raster2D", str); + + gf_cfg_set_key(gpac->m_user.config, "Compositor", "ForceOpenGL", m_Use3DRender.GetCheck() ? "yes" : "no"); + return 0; +} + + +COptRender2D::COptRender2D(CWnd* pParent /*=NULL*/) + : CDialog(COptRender2D::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptRender2D) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void COptRender2D::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptRender2D) + DDX_Control(pDX, IDC_FORMAT_YUV, m_YUVFormat); + DDX_Control(pDX, IDC_YUV, m_NoYUV); + DDX_Control(pDX, IDC_ZOOM_SCALABLE, m_Scalable); + DDX_Control(pDX, IDC_DIRECTRENDER, m_DirectRender); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptRender2D, CDialog) + //{{AFX_MSG_MAP(COptRender2D) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptRender2D message handlers + +BOOL COptRender2D::OnInitDialog() +{ + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "DirectDraw"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_DirectRender.SetCheck(1); + } else { + m_DirectRender.SetCheck(0); + } + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "ScalableZoom"); + if (sOpt && !stricmp(sOpt, "no")) { + m_Scalable.SetCheck(0); + } else { + m_Scalable.SetCheck(1); + } + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "DisableYUV"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_NoYUV.SetCheck(1); + } else { + m_NoYUV.SetCheck(0); + } + + SetYUV(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void COptRender2D::SetYUV() +{ + Osmo4 *gpac = GetApp(); + u32 yuv_format = gf_term_get_option(gpac->m_term, GF_OPT_YUV_FORMAT); + if (!yuv_format) { + m_YUVFormat.SetWindowText("(No YUV used)"); + } else { + char str[100]; + sprintf(str, "(%s used)", gf_4cc_to_str(yuv_format)); + m_YUVFormat.SetWindowText(str); + } +} + +void COptRender2D::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "DirectDraw", m_DirectRender.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "ScalableZoom", m_Scalable.GetCheck() ? "yes" : "no"); + + gf_cfg_set_key(gpac->m_user.config, "Compositor", "DisableYUV", m_NoYUV.GetCheck() ? "yes" : "no"); +} + + +COptRender3D::COptRender3D(CWnd* pParent /*=NULL*/) + : CDialog(COptRender3D::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptRender3D) + //}}AFX_DATA_INIT +} + + +void COptRender3D::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptRender3D) + DDX_Control(pDX, IDC_BITMAP_USE_PIXEL, m_BitmapPixels); + DDX_Control(pDX, IDC_DISABLE_TX_RECT, m_DisableTXRect); + DDX_Control(pDX, IDC_RASTER_OUTLINE, m_RasterOutlines); + DDX_Control(pDX, IDC_EMUL_POW2, m_EmulPow2); + DDX_Control(pDX, IDC_DISABLE_POLY_AA, m_PolyAA); + DDX_Control(pDX, IDC_DRAW_NORMALS, m_DrawNormals); + DDX_Control(pDX, IDC_BACK_CULL, m_BackCull); + DDX_Control(pDX, IDC_DRAW_MODE, m_Wireframe); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptRender3D, CDialog) + //{{AFX_MSG_MAP(COptRender3D) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptRender3D message handlers + + +BOOL COptRender3D::OnInitDialog() +{ + CDialog::OnInitDialog(); + + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + m_DrawNormals.AddString("Never"); + m_DrawNormals.AddString("Per Face"); + m_DrawNormals.AddString("Per Vertex"); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "DrawNormals"); + if (sOpt && !stricmp(sOpt, "PerFace")) m_DrawNormals.SetCurSel(1); + else if (sOpt && !stricmp(sOpt, "PerVertex")) m_DrawNormals.SetCurSel(2); + else m_DrawNormals.SetCurSel(0); + + m_BackCull.AddString("Off"); + m_BackCull.AddString("On"); + m_BackCull.AddString("Alpha"); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "BackFaceCulling"); + if (sOpt && !stricmp(sOpt, "Off")) m_BackCull.SetCurSel(0); + else if (sOpt && !stricmp(sOpt, "Alpha")) m_BackCull.SetCurSel(2); + else m_BackCull.SetCurSel(1); + + m_Wireframe.AddString("Solid"); + m_Wireframe.AddString("Wireframe"); + m_Wireframe.AddString("Both"); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "Wireframe"); + if (sOpt && !stricmp(sOpt, "WireOnly")) m_Wireframe.SetCurSel(1); + else if (sOpt && !stricmp(sOpt, "WireOnSolid")) m_Wireframe.SetCurSel(2); + else m_Wireframe.SetCurSel(0); + + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "RasterOutlines"); + m_RasterOutlines.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "EmulatePOW2"); + m_EmulPow2.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "PolygonAA"); + m_PolyAA.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "BitmapCopyPixels"); + m_BitmapPixels.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "DisableRectExt"); + m_DisableTXRect.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void COptRender3D::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + + u32 sel = m_DrawNormals.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "DrawNormals", (sel==2) ? "PerVertex" : (sel==1) ? "PerFace" : "Never"); + sel = m_BackCull.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "BackFaceCulling", (sel==2) ? "Alpha" : (sel==1) ? "On" : "Off"); + sel = m_Wireframe.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "Wireframe", (sel==2) ? "WireOnSolid" : (sel==1) ? "WireOnly" : "WireNone"); + + gf_cfg_set_key(gpac->m_user.config, "Compositor", "RasterOutlines", m_RasterOutlines.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "EmulatePOW2", m_EmulPow2.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "PolygonAA", m_PolyAA.GetCheck() ? "yes" : "no"); + + gf_cfg_set_key(gpac->m_user.config, "Compositor", "DisableRectExt", m_DisableTXRect.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "BitmapCopyPixels", m_BitmapPixels.GetCheck() ? "yes" : "no"); +} + +COptVideo::COptVideo(CWnd* pParent /*=NULL*/) + : CDialog(COptVideo::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptVideo) + //}}AFX_DATA_INIT +} + + +void COptVideo::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptVideo) + DDX_Control(pDX, IDC_SWITCH_RES, m_SwitchRes); + DDX_Control(pDX, IDC_VIDEO_LIST, m_Videos); + DDX_Control(pDX, IDC_HWMEMORY, m_UseHWMemory); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptVideo, CDialog) + //{{AFX_MSG_MAP(COptVideo) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptVideo message handlers + +BOOL COptVideo::OnInitDialog() +{ + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Video", "SwitchResolution"); + m_SwitchRes.SetCheck(sOpt && !stricmp(sOpt, "yes") ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Video", "UseHardwareMemory"); + m_UseHWMemory.SetCheck(sOpt && !stricmp(sOpt, "yes") ? 1 : 0); + + + u32 count = gf_modules_get_count(gpac->m_user.modules); + GF_BaseInterface *ifce; + s32 to_sel = 0; + s32 select = 0; + /*video drivers enum*/ + while (m_Videos.GetCount()) m_Videos.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Video", "DriverName"); + + for (u32 i=0; im_user.modules, i, GF_VIDEO_OUTPUT_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(ifce->module_name, sOpt)) select = to_sel; + m_Videos.AddString(ifce->module_name); + gf_modules_close_interface(ifce); + to_sel++; + } + m_Videos.SetCurSel(select); + + return TRUE; + +} + +void COptVideo::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + char str[50]; + + gf_cfg_set_key(gpac->m_user.config, "Video", "SwitchResolution", m_SwitchRes.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Video", "UseHardwareMemory", m_UseHWMemory.GetCheck() ? "yes" : "no"); + m_Videos.GetWindowText(str, 50); + gf_cfg_set_key(gpac->m_user.config, "Video", "DriverName", str); +} + + +COptAudio::COptAudio(CWnd* pParent /*=NULL*/) + : CDialog(COptAudio::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptAudio) + //}}AFX_DATA_INIT +} + + +void COptAudio::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptAudio) + DDX_Control(pDX, IDC_AUDIO_NOTIFS, m_Notifs); + DDX_Control(pDX, IDC_DRIVER_LIST, m_DriverList); + DDX_Control(pDX, IDC_AUDIO_RESYNC, m_AudioResync); + DDX_Control(pDX, IDC_AUDIO_MULTICH, m_AudioMultiCH); + DDX_Control(pDX, IDC_AUDIO_FPS, m_AudioDur); + DDX_Control(pDX, IDC_SPIN_FPS, m_SpinFPS); + DDX_Control(pDX, IDC_FORCE_AUDIO, m_ForceConfig); + DDX_Control(pDX, IDC_SPIN_AUDIO, m_AudioSpin); + DDX_Control(pDX, IDC_EDIT_AUDIO, m_AudioEdit); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptAudio, CDialog) + //{{AFX_MSG_MAP(COptAudio) + ON_BN_CLICKED(IDC_FORCE_AUDIO, OnForceAudio) + ON_CBN_SELCHANGE(IDC_DRIVER_LIST, OnSelchangeDriverList) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptAudio message handlers + +BOOL COptAudio::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_AudioSpin.SetBuddy(& m_AudioEdit); + m_SpinFPS.SetBuddy(& m_AudioDur); + m_SpinFPS.SetRange(0, 2000); + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "ForceConfig"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_ForceConfig.SetCheck(1); + } else { + m_ForceConfig.SetCheck(0); + } + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "NumBuffers"); + if (sOpt) { + m_AudioEdit.SetWindowText(sOpt); + } else { + m_AudioEdit.SetWindowText("2"); + } + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "TotalDuration"); + if (sOpt) { + m_AudioDur.SetWindowText(sOpt); + } else { + m_AudioDur.SetWindowText("120"); + } + + OnForceAudio(); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "NoResync"); + m_AudioResync.SetCheck( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "DisableMultiChannel"); + m_AudioMultiCH.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + /*driver enum*/ + while (m_DriverList.GetCount()) m_DriverList.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "DriverName"); + u32 count = gf_modules_get_count(gpac->m_user.modules); + GF_BaseInterface *ifce; + s32 select = 0; + s32 to_sel = 0; + for (u32 i=0; im_user.modules, i, GF_AUDIO_OUTPUT_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(ifce->module_name, sOpt)) select = to_sel; + m_DriverList.AddString(ifce->module_name); + gf_modules_close_interface(ifce); + to_sel++; + } + m_DriverList.SetCurSel(select); + + m_Notifs.ShowWindow(SW_HIDE); + if (sOpt && strstr(sOpt, "DirectSound")) m_Notifs.ShowWindow(SW_SHOW); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "DisableNotification"); + if (sOpt && !stricmp(sOpt, "yes")) + m_Notifs.SetCheck(1); + else + m_Notifs.SetCheck(0); + + return TRUE; +} + + +void COptAudio::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + char str[50]; + + gf_cfg_set_key(gpac->m_user.config, "Audio", "ForceConfig", m_ForceConfig.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Audio", "NoResync", m_AudioResync.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Audio", "DisableMultiChannel", m_AudioMultiCH.GetCheck() ? "yes" : "no"); + + m_AudioEdit.GetWindowText(str, 20); + gf_cfg_set_key(gpac->m_user.config, "Audio", "NumBuffers", str); + m_AudioDur.GetWindowText(str, 20); + gf_cfg_set_key(gpac->m_user.config, "Audio", "TotalDuration", str); + + m_DriverList.GetWindowText(str, 50); + gf_cfg_set_key(gpac->m_user.config, "Audio", "DriverName", str); + + if (strstr(str, "DirectSound")) { + gf_cfg_set_key(gpac->m_user.config, "Audio", "DisableNotification", m_Notifs.GetCheck() ? "yes" : "no"); + } + +} + +void COptAudio::OnForceAudio() +{ + BOOL en = m_ForceConfig.GetCheck(); + + m_AudioSpin.EnableWindow(en); + m_AudioEdit.EnableWindow(en); + m_SpinFPS.EnableWindow(en); + m_AudioDur.EnableWindow(en); +} + +void COptAudio::OnSelchangeDriverList() +{ + char str[50]; + m_DriverList.GetWindowText(str, 50); + if (strstr(str, "DirectSound")) { + m_Notifs.ShowWindow(SW_SHOW); + } else { + m_Notifs.ShowWindow(SW_HIDE); + } +} + + + + +COptFont::COptFont(CWnd* pParent /*=NULL*/) + : CDialog(COptFont::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptFont) + //}}AFX_DATA_INIT +} + + +void COptFont::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptFont) + DDX_Control(pDX, IDC_TEXTURE_MODE, m_TextureModes); + DDX_Control(pDX, IDC_FONT_LIST, m_Fonts); + DDX_Control(pDX, IDC_BROWSE_FONT, m_BrowseFont); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptFont, CDialog) + //{{AFX_MSG_MAP(COptFont) + ON_BN_CLICKED(IDC_BROWSE_FONT, OnBrowseFont) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptFont message handlers + +BOOL COptFont::OnInitDialog() +{ + u32 i; + GF_BaseInterface *ifce; + + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + /*video drivers enum*/ + while (m_Fonts.GetCount()) m_Fonts.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "FontEngine", "FontReader"); + s32 to_sel = 0; + s32 select = 0; + u32 count = gf_modules_get_count(gpac->m_user.modules); + for (i=0; im_user.modules, i, GF_FONT_READER_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(ifce->module_name, sOpt)) select = to_sel; + m_Fonts.AddString(ifce->module_name); + gf_modules_close_interface(ifce); + to_sel++; + } + m_Fonts.SetCurSel(select); + + + sOpt = gf_cfg_get_key(gpac->m_user.config, "FontEngine", "FontDirectory"); + if (sOpt) m_BrowseFont.SetWindowText(sOpt); + + /*text texturing modes*/ + while (m_TextureModes.GetCount()) m_TextureModes.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "TextureTextMode"); + m_TextureModes.AddString("Default"); + m_TextureModes.AddString("Never"); + m_TextureModes.AddString("Always"); + if (sOpt && !stricmp(sOpt, "3D")) m_TextureModes.SetCurSel(1); + else if (sOpt && !stricmp(sOpt, "Always")) m_TextureModes.SetCurSel(2); + else m_TextureModes.SetCurSel(0); + + return TRUE; +} + + + +static char szCacheDir[MAX_PATH]; + +static int CALLBACK LocCbck(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) +{ + char dir[MAX_PATH]; + if (uMsg == BFFM_INITIALIZED) { + strcpy(dir, szCacheDir); + SendMessage(hwnd, BFFM_SETSELECTION, TRUE,(LPARAM) dir); + } + return 0; +} + +void COptFont::OnBrowseFont() +{ + BROWSEINFO brw; + LPMALLOC pMalloc; + LPITEMIDLIST ret; + char dir[MAX_PATH]; + + if (NOERROR == ::SHGetMalloc(&pMalloc) ) { + + m_BrowseFont.GetWindowText(szCacheDir, MAX_PATH); + + memset(&brw, 0, sizeof(BROWSEINFO)); + brw.hwndOwner = this->GetSafeHwnd(); + brw.pszDisplayName = dir; + brw.lpszTitle = "Select Font Directory..."; + brw.ulFlags = 0L; + brw.lpfn = LocCbck; + + ret = SHBrowseForFolder(&brw); + if (ret != NULL) { + if (::SHGetPathFromIDList(ret, dir)) { + m_BrowseFont.SetWindowText(dir); + } + pMalloc->Free(ret); + } + pMalloc->Release(); + } +} + + +void COptFont::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + char str[MAX_PATH]; + + m_Fonts.GetWindowText(str, 50); + gf_cfg_set_key(gpac->m_user.config, "FontEngine", "FontReader", str); + m_BrowseFont.GetWindowText(str, 50); + gf_cfg_set_key(gpac->m_user.config, "FontEngine", "FontDirectory", str); + switch (m_TextureModes.GetCurSel()) { + case 2: gf_cfg_set_key(gpac->m_user.config, "Compositor", "TextureTextMode", "Always"); break; + case 1: gf_cfg_set_key(gpac->m_user.config, "Compositor", "TextureTextMode", "Never"); break; + default: gf_cfg_set_key(gpac->m_user.config, "Compositor", "TextureTextMode", "Default"); break; + } +} + + +COptHTTP::COptHTTP(CWnd* pParent /*=NULL*/) + : CDialog(COptHTTP::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptHTTP) + //}}AFX_DATA_INIT +} + + +void COptHTTP::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptHTTP) + DDX_Control(pDX, IDC_HTTP_PROXY, m_ProxyName); + DDX_Control(pDX, IDC_HTTP_USE_PROXY, m_useProxy); + DDX_Control(pDX, IDC_SAX_DELAY, m_SAXDuration); + DDX_Control(pDX, IDC_SAX_PROGRESSIVE, m_Progressive); + DDX_Control(pDX, IDC_RESTART_CACHE, m_RestartFile); + DDX_Control(pDX, IDC_CLEAN_CACHE, m_CleanCache); + DDX_Control(pDX, IDC_BROWSE_CACHE, m_CacheDir); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptHTTP, CDialog) + //{{AFX_MSG_MAP(COptHTTP) + ON_BN_CLICKED(IDC_BROWSE_CACHE, OnBrowseCache) + ON_BN_CLICKED(IDC_SAX_PROGRESSIVE, OnSaxProgressive) + ON_BN_CLICKED(IDC_HTTP_USE_PROXY, OnUseProxy) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptHTTP message handlers + + +void COptHTTP::OnBrowseCache() +{ + BROWSEINFO brw; + LPMALLOC pMalloc; + LPITEMIDLIST ret; + char dir[MAX_PATH]; + + if (NOERROR == ::SHGetMalloc(&pMalloc) ) { + + m_CacheDir.GetWindowText(szCacheDir, MAX_PATH); + + memset(&brw, 0, sizeof(BROWSEINFO)); + brw.hwndOwner = this->GetSafeHwnd(); + brw.pszDisplayName = dir; + brw.lpszTitle = "Select HTTP Cache Directory..."; + brw.ulFlags = 0L; + brw.lpfn = LocCbck; + + ret = SHBrowseForFolder(&brw); + if (ret != NULL) { + if (::SHGetPathFromIDList(ret, dir)) { + m_CacheDir.SetWindowText(dir); + } + pMalloc->Free(ret); + } + pMalloc->Release(); + } +} + +BOOL COptHTTP::OnInitDialog() +{ + char proxy[GF_MAX_PATH]; + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "CacheDirectory"); + if (sOpt) m_CacheDir.SetWindowText(sOpt); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Downloader", "CleanCache"); + m_CleanCache.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Downloader", "RestartFiles"); + m_RestartFile.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "SAXLoader", "Progressive"); + m_Progressive.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + OnSaxProgressive(); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "SAXLoader", "MaxDuration"); + if (sOpt) { + m_SAXDuration.SetWindowText(sOpt); + } else { + m_SAXDuration.SetWindowText("0"); + } + //if (m_Progressive.GetCheck()) m_SAXDuration.EnableWindow(1); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "HTTPProxy", "Enabled"); + m_useProxy.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + OnUseProxy(); + strcpy(proxy, ""); + sOpt = gf_cfg_get_key(gpac->m_user.config, "HTTPProxy", "Name"); + if (sOpt) { + strcpy(proxy, sOpt); + sOpt = gf_cfg_get_key(gpac->m_user.config, "HTTPProxy", "Port"); + if (sOpt) { + strcat(proxy, ":"); + strcat(proxy, sOpt); + } + } + m_ProxyName.SetWindowText(proxy); + return TRUE; +} + +void COptHTTP::OnSaxProgressive() +{ + if (m_Progressive.GetCheck()) { + m_SAXDuration.EnableWindow(1); + } else { + m_SAXDuration.EnableWindow(0); + } +} + + +void COptHTTP::OnUseProxy() +{ + if (m_useProxy.GetCheck()) { + m_ProxyName.EnableWindow(1); + } else { + m_ProxyName.EnableWindow(0); + } +} + +void COptHTTP::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + + gf_cfg_set_key(gpac->m_user.config, "Downloader", "CleanCache", m_CleanCache.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Downloader", "RestartFiles", m_RestartFile.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "SAXLoader", "Progressive", m_Progressive.GetCheck() ? "yes" : "no"); + + m_SAXDuration.GetWindowText(szCacheDir, MAX_PATH); + gf_cfg_set_key(gpac->m_user.config, "SAXLoader", "MaxDuration", szCacheDir); + + gf_cfg_set_key(gpac->m_user.config, "HTTPProxy", "Enabled", m_useProxy.GetCheck() ? "yes" : "no"); + m_ProxyName.GetWindowText(szCacheDir, MAX_PATH); + char *sep = strrchr(szCacheDir, ':'); + if (sep) { + sep[0] = 0; + gf_cfg_set_key(gpac->m_user.config, "HTTPProxy", "Name", szCacheDir); + sep[0] = ':'; + gf_cfg_set_key(gpac->m_user.config, "HTTPProxy", "Port", sep+1); + } else { + gf_cfg_set_key(gpac->m_user.config, "HTTPProxy", "Name", szCacheDir); + gf_cfg_set_key(gpac->m_user.config, "HTTPProxy", "Port", NULL); + } + m_CacheDir.GetWindowText(szCacheDir, MAX_PATH); + gf_cfg_set_key(gpac->m_user.config, "General", "CacheDirectory", szCacheDir); +} + + +COptStream::COptStream(CWnd* pParent /*=NULL*/) + : CDialog(COptStream::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptStream) + //}}AFX_DATA_INIT +} + + +void COptStream::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptStream) + DDX_Control(pDX, IDC_REBUFFER_LEN, m_RebufferLen); + DDX_Control(pDX, IDC_REBUFFER, m_Rebuffer); + DDX_Control(pDX, IDC_BUFFER, m_Buffer); + DDX_Control(pDX, IDC_TIMEOUT, m_Timeout); + DDX_Control(pDX, IDC_REORDER, m_Reorder); + DDX_Control(pDX, IDC_RTSP, m_UseRTSP); + DDX_Control(pDX, IDC_PORT, m_Port); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptStream, CDialog) + //{{AFX_MSG_MAP(COptStream) + ON_CBN_SELCHANGE(IDC_PORT, OnSelchangePort) + ON_BN_CLICKED(IDC_RTSP, OnRtsp) + ON_BN_CLICKED(IDC_REBUFFER, OnRebuffer) + ON_EN_UPDATE(IDC_REBUFFER_LEN, OnUpdateRebufferLen) + ON_EN_UPDATE(IDC_BUFFER, OnUpdateBuffer) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptStream message handlers + +BOOL COptStream::OnInitDialog() +{ + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + while (m_Port.GetCount()) m_Port.DeleteString(0); + m_Port.AddString("554 (RTSP standard)"); + m_Port.AddString("7070 (RTSP ext)"); + m_Port.AddString("80 (RTSP / HTTP tunnel)"); + m_Port.AddString("8080 (RTSP / HTTP tunnel)"); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Streaming", "DefaultPort"); + u32 port = 554; + Bool force_rtsp = 0;; + if (sOpt) port = atoi(sOpt); + switch (port) { + case 8080: + m_Port.SetCurSel(3); + force_rtsp = 1; + break; + case 80: + m_Port.SetCurSel(2); + force_rtsp = 1; + break; + case 7070: + m_Port.SetCurSel(1); + break; + default: + m_Port.SetCurSel(0); + break; + } + + Bool use_rtsp = 0; + sOpt = gf_cfg_get_key(gpac->m_user.config, "Streaming", "RTPoverRTSP"); + if (sOpt && !stricmp(sOpt, "yes")) use_rtsp = 1; + + if (force_rtsp) { + m_UseRTSP.SetCheck(1); + m_UseRTSP.EnableWindow(0); + m_Reorder.SetCheck(0); + m_Reorder.EnableWindow(0); + } else { + m_UseRTSP.SetCheck(use_rtsp); + m_UseRTSP.EnableWindow(1); + m_Reorder.EnableWindow(1); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Streaming", "ReorderSize"); + if (sOpt && !stricmp(sOpt, "0")) { + m_Reorder.SetCheck(0); + } else { + m_Reorder.SetCheck(1); + } + } + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Streaming", "RTSPTimeout"); + if (sOpt) { + m_Timeout.SetWindowText(sOpt); + } else { + m_Timeout.SetWindowText("30000"); + } + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Network", "BufferLength"); + if (sOpt) { + m_Buffer.SetWindowText(sOpt); + } else { + m_Buffer.SetWindowText("3000"); + } + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Network", "RebufferLength"); + u32 buf_len = 0; + if (sOpt) buf_len = atoi(sOpt); + if (buf_len) { + m_RebufferLen.SetWindowText(sOpt); + m_Rebuffer.SetCheck(1); + m_RebufferLen.EnableWindow(1); + } else { + m_RebufferLen.SetWindowText("0"); + m_Rebuffer.SetCheck(0); + m_RebufferLen.EnableWindow(0); + } + + return TRUE; +} + + +void COptStream::OnSelchangePort() +{ + s32 sel = m_Port.GetCurSel(); + switch (sel) { + case 3: + case 2: + m_UseRTSP.SetCheck(1); + m_UseRTSP.EnableWindow(0); + m_Reorder.SetCheck(0); + m_Reorder.EnableWindow(0); + break; + case 1: + default: + m_UseRTSP.SetCheck(0); + m_UseRTSP.EnableWindow(1); + m_Reorder.SetCheck(1); + m_Reorder.EnableWindow(1); + break; + } +} + +void COptStream::OnRtsp() +{ + if (m_UseRTSP.GetCheck()) { + m_Reorder.SetCheck(0); + m_Reorder.EnableWindow(0); + } else { + m_Reorder.SetCheck(1); + m_Reorder.EnableWindow(1); + } + +} + +void COptStream::CheckRebuffer() +{ + char str[50]; + s32 buf, rebuf; + m_Buffer.GetWindowText(str, 50); + buf = atoi(str); + m_RebufferLen.GetWindowText(str, 50); + rebuf = atoi(str); + if (rebuf*2 > buf) { + rebuf = buf/2; + sprintf(str, "%d", rebuf); + m_RebufferLen.SetWindowText(str); + } +} + +void COptStream::OnRebuffer() +{ + if (!m_Rebuffer.GetCheck()) { + m_RebufferLen.EnableWindow(0); + } else { + m_RebufferLen.EnableWindow(1); + CheckRebuffer(); + } +} + +void COptStream::OnUpdateRebufferLen() +{ + CheckRebuffer(); +} + +void COptStream::OnUpdateBuffer() +{ + CheckRebuffer(); +} + +void COptStream::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + Bool force_rtsp = 0; + s32 sel = m_Port.GetCurSel(); + switch (sel) { + case 3: + gf_cfg_set_key(gpac->m_user.config, "Streaming", "DefaultPort", "8080"); + force_rtsp = 1; + break; + case 2: + gf_cfg_set_key(gpac->m_user.config, "Streaming", "DefaultPort", "80"); + force_rtsp = 1; + break; + case 1: + gf_cfg_set_key(gpac->m_user.config, "Streaming", "DefaultPort", "7070"); + break; + default: + gf_cfg_set_key(gpac->m_user.config, "Streaming", "DefaultPort", "554"); + break; + } + + if (force_rtsp) { + gf_cfg_set_key(gpac->m_user.config, "Streaming", "RTPoverRTSP", "yes"); + } else { + gf_cfg_set_key(gpac->m_user.config, "Streaming", "RTPoverRTSP", m_UseRTSP.GetCheck() ? "yes" : "no"); + if (!m_UseRTSP.GetCheck()) gf_cfg_set_key(gpac->m_user.config, "Streaming", "ReorderSize", m_Reorder.GetCheck() ? "30" : "0"); + } + + char str[50]; + + m_Timeout.GetWindowText(str, 50); + gf_cfg_set_key(gpac->m_user.config, "Streaming", "RTSPTimeout", str); + + m_Buffer.GetWindowText(str, 50); + gf_cfg_set_key(gpac->m_user.config, "Network", "BufferLength", str); + if (m_Rebuffer.GetCheck()) { + m_RebufferLen.GetWindowText(str, 50); + gf_cfg_set_key(gpac->m_user.config, "Network", "RebufferLength", str); + } else { + gf_cfg_set_key(gpac->m_user.config, "Network", "RebufferLength", "0"); + } +} + + + + +COptMCache::COptMCache(CWnd* pParent /*=NULL*/) + : CDialog(COptMCache::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptMCache) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void COptMCache::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptMCache) + DDX_Control(pDX, IDC_BASEPRES, m_BaseName); + DDX_Control(pDX, IDC_MCACHE_USENAME, m_UseBase); + DDX_Control(pDX, IDC_MCACHE_OVERWRITE, m_Overwrite); + DDX_Control(pDX, IDC_BROWSE_MCACHE, m_RecDir); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptMCache, CDialog) + //{{AFX_MSG_MAP(COptMCache) + ON_BN_CLICKED(IDC_BROWSE_MCACHE, OnBrowseMcache) + ON_BN_CLICKED(IDC_MCACHE_USENAME, OnMcacheUsename) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptMCache message handlers + +void COptMCache::OnBrowseMcache() +{ + BROWSEINFO brw; + LPMALLOC pMalloc; + LPITEMIDLIST ret; + char dir[MAX_PATH]; + + if (NOERROR == ::SHGetMalloc(&pMalloc) ) { + + m_RecDir.GetWindowText(szCacheDir, MAX_PATH); + + memset(&brw, 0, sizeof(BROWSEINFO)); + brw.hwndOwner = this->GetSafeHwnd(); + brw.pszDisplayName = dir; + brw.lpszTitle = "Select HTTP Cache Directory..."; + brw.ulFlags = 0L; + brw.lpfn = LocCbck; + + ret = SHBrowseForFolder(&brw); + if (ret != NULL) { + if (::SHGetPathFromIDList(ret, dir)) { + m_RecDir.SetWindowText(dir); + } + pMalloc->Free(ret); + } + pMalloc->Release(); + } +} + +BOOL COptMCache::OnInitDialog() +{ + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "StreamingCache", "RecordDirectory"); + if (!sOpt) sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "CacheDirectory"); + if (sOpt) m_RecDir.SetWindowText(sOpt); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "StreamingCache", "KeepExistingFiles"); + m_Overwrite.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 0 : 1); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "StreamingCache", "BaseFileName"); + if (sOpt) { + m_UseBase.SetCheck(1); + m_BaseName.EnableWindow(TRUE); + m_BaseName.SetWindowText(sOpt); + } else { + m_UseBase.SetCheck(0); + m_BaseName.EnableWindow(FALSE); + m_BaseName.SetWindowText("uses service URL"); + } + return TRUE; +} + +void COptMCache::OnMcacheUsename() +{ + if (m_UseBase.GetCheck()) { + m_BaseName.EnableWindow(TRUE); + m_BaseName.SetWindowText("record"); + } else { + m_BaseName.EnableWindow(FALSE); + m_BaseName.SetWindowText("uses service URL"); + } +} + +void COptMCache::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + + gf_cfg_set_key(gpac->m_user.config, "StreamingCache", "KeepExistingFiles", m_Overwrite.GetCheck() ? "no" : "yes"); + if (m_UseBase.GetCheck()) { + m_BaseName.GetWindowText(szCacheDir, MAX_PATH); + gf_cfg_set_key(gpac->m_user.config, "StreamingCache", "BaseFileName", szCacheDir); + } else { + gf_cfg_set_key(gpac->m_user.config, "StreamingCache", "BaseFileName", NULL); + } + m_RecDir.GetWindowText(szCacheDir, MAX_PATH); + gf_cfg_set_key(gpac->m_user.config, "StreamingCache", "RecordDirectory", szCacheDir); +} + + +OptFiles::OptFiles(CWnd* pParent /*=NULL*/) + : CDialog(OptFiles::IDD, pParent) +{ + //{{AFX_DATA_INIT(OptFiles) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void OptFiles::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(OptFiles) + DDX_Control(pDX, IDC_ASSOCIATE, m_DoAssociate); + DDX_Control(pDX, IDC_FILES_PLUG, m_PlugName); + DDX_Control(pDX, IDC_FILES_MIMES, m_mimes); + DDX_Control(pDX, IDC_FILES_EXT, m_extensions); + DDX_Control(pDX, IDC_FILELIST, m_FileDescs); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(OptFiles, CDialog) + //{{AFX_MSG_MAP(OptFiles) + ON_CBN_SELCHANGE(IDC_FILELIST, OnSelchangeFilelist) + ON_BN_CLICKED(IDC_ASSOCIATE, OnAssociate) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// OptFiles message handlers + +BOOL OptFiles::OnInitDialog() +{ + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + u32 count, i; + + while (m_FileDescs.GetCount()) m_FileDescs.DeleteString(0); + count = gf_cfg_get_key_count(gpac->m_user.config, "MimeTypes"); + for (i=0; im_user.config, "MimeTypes", i); + if (!sMime) continue; + sOpt = gf_cfg_get_key(gpac->m_user.config, "MimeTypes", sMime); + if (!sOpt) continue; + sKey = (char *) strstr(sOpt, "\" \""); + if (!sKey) continue; + strcpy(sDesc, sKey+3); + sKey = strchr(sDesc, '\"'); + if (!sKey) continue; + sKey[0] = 0; + m_FileDescs.AddString(sDesc); + } + m_FileDescs.SetCurSel(0); + SetSelection(0); + return TRUE; +} + +void OptFiles::OnSelchangeFilelist() +{ + SetSelection(m_FileDescs.GetCurSel()); +} + +void OptFiles::SetSelection(u32 sel) +{ + Osmo4 *gpac = GetApp(); + char *sMime, *sKey, sDesc[200], sText[200]; + sMime = (char *) gf_cfg_get_key_name(gpac->m_user.config, "MimeTypes", sel); + sprintf(sText, "Mime Type: %s", sMime); + m_mimes.SetWindowText(sText); + strcpy(cur_mime, sMime); + sMime = (char *) gf_cfg_get_key(gpac->m_user.config, "MimeTypes", sMime); + strcpy(sDesc, sMime+1); + sKey = strchr(sDesc, '\"'); + sKey[0] = 0; + sprintf(sText, "Extensions: %s", sDesc); + strcpy(cur_ext, sDesc); + m_extensions.SetWindowText(sText); + sKey = strrchr(sMime, '\"'); + sprintf(sText, "Module: %s", sKey+2); + m_PlugName.SetWindowText(sText); + + Bool has_asso, need_asso, go = 1; + sKey = cur_ext; + need_asso = has_asso = 0; + + HKEY hKey; + DWORD dwSize; + while (go) { + Bool ok; + char szExt[50], szReg[60], c; + char *tmp = strchr(sKey, ' '); + if (!tmp) { go = 0;} + else { c = tmp[0]; tmp[0] = 0; } + sprintf(szExt, ".%s", sKey); + sprintf(szReg, "GPAC\\%s", sKey); + if (tmp) { tmp[0] = c; tmp += 1; } + + if (RegOpenKeyEx(HKEY_CLASSES_ROOT, szExt, 0, KEY_READ, &hKey ) == ERROR_SUCCESS) { + dwSize = 200; + ok = 1; + if (RegQueryValueEx(hKey, "", NULL, NULL,(unsigned char*) sDesc, &dwSize) != ERROR_SUCCESS) ok = 0; + RegCloseKey(hKey); + if (ok && !stricmp((char *)sDesc, szReg)) has_asso = 1; + else need_asso = 1; + } else need_asso = 1; + sKey = tmp; + + } + m_DoAssociate.SetCheck(has_asso); + if (need_asso && has_asso) + OnAssociate(); +} + + +void OptFiles::OnAssociate() +{ + char *sKey, sDesc[200]; + unsigned char szApp[MAX_PATH]; + unsigned char szIco[MAX_PATH]; + + strcpy((char *) szApp, GetApp()->szAppPath); + strcpy((char *) szIco, (const char *) szApp); + strcat((char *) szIco, "Osmo4.ico"); + strcat((char *) szApp, "Osmo4.exe \"%L\""); + + if (m_DoAssociate.GetCheck()) { + Bool go = 1; + sKey = cur_ext; + + HKEY hKey; + DWORD dwSize; + while (go) { + Bool ok; + char szExt[50], szReg[60], szOld[80], szPath[1024], c; + char *tmp = strchr(sKey, ' '); + if (!tmp) { go = 0;} + else { c = tmp[0]; tmp[0] = 0; } + sprintf(szExt, ".%s", sKey); + sprintf(szReg, "GPAC\\%s", sKey); + if (tmp) { tmp[0] = c; tmp += 1; } + + RegOpenKeyEx(HKEY_CLASSES_ROOT, szExt, 0, 0, &hKey ); + dwSize = 200; + ok = 1; + if (RegQueryValueEx(hKey, "", NULL, NULL,(unsigned char*) sDesc, &dwSize) != ERROR_SUCCESS) ok = 0; + RegCloseKey(hKey); + strcpy(szOld, ""); + if (ok && stricmp((char *)sDesc, szReg)) strcpy(szOld, sDesc); + + strcpy(szPath, szReg); strcat(szPath, "\\DefaultIcon"); + RegCreateKeyEx(HKEY_CLASSES_ROOT, szPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwSize); + RegSetValueEx(hKey, "", 0, REG_SZ, szIco, strlen((const char *) szIco)+1); + RegCloseKey(hKey); + + strcpy(szPath, szReg); strcat(szPath, "\\Shell\\open\\command"); + RegCreateKeyEx(HKEY_CLASSES_ROOT, szPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwSize); + RegSetValueEx(hKey, "", 0, REG_SZ, szApp, strlen((const char *) szApp)+1); + RegCloseKey(hKey); + + if (strlen(szOld)) { + strcpy(szPath, szReg); strcat(szPath, "\\Backup"); + RegCreateKeyEx(HKEY_CLASSES_ROOT, szPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwSize); + RegSetValueEx(hKey, "", 0, REG_SZ, (unsigned char *) szOld, strlen((const char *) szIco)+1); + RegCloseKey(hKey); + } + + RegCreateKeyEx(HKEY_CLASSES_ROOT, szExt, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwSize); + RegSetValueEx(hKey, "", 0, REG_SZ, (const unsigned char *) szReg, strlen(szReg)+1); + RegCloseKey(hKey); + + sKey = tmp; + } + } else { + Bool go = 1; + sKey = cur_ext; + + HKEY hKey; + DWORD dwSize; + while (go) { + Bool ok; + char szExt[50], szReg[60], szPath[1024], c; + char *tmp = strchr(sKey, ' '); + if (!tmp) { go = 0;} + else { c = tmp[0]; tmp[0] = 0; } + sprintf(szExt, ".%s", sKey); + sprintf(szReg, "GPAC\\%s", sKey); + if (tmp) { tmp[0] = c; tmp += 1; } + + strcpy(szPath, szReg); strcat(szPath, "\\Backup"); + RegOpenKeyEx(HKEY_CLASSES_ROOT, szPath, 0, 0, &hKey ); + dwSize = 200; + ok = 1; + if (RegQueryValueEx(hKey, "", NULL, NULL,(unsigned char*) sDesc, &dwSize) != ERROR_SUCCESS) ok = 0; + RegCloseKey(hKey); + if (ok && strlen((char *)sDesc)) { + RegCreateKeyEx(HKEY_CLASSES_ROOT, szExt, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwSize); + RegSetValueEx(hKey, "", 0, REG_SZ, (unsigned char*) sDesc, strlen((const char *) sDesc)+1); + RegCloseKey(hKey); + } + + RegOpenKeyEx(HKEY_CLASSES_ROOT, szReg, 0, 0, &hKey ); + RegDeleteKey(hKey, "Backup"); + RegDeleteKey(hKey, "DefaultIcon"); + RegDeleteKey(hKey, "Shell\\open\\command"); + RegDeleteKey(hKey, "Shell\\open"); + RegDeleteKey(hKey, "Shell"); + RegCloseKey(hKey); + RegDeleteKey(HKEY_CLASSES_ROOT, szReg); + + sKey = tmp; + } + } +} + +COptLogs::COptLogs(CWnd* pParent /*=NULL*/) + : CDialog(COptLogs::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptLogs) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void COptLogs::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptLogs) + DDX_Control(pDX, IDC_TOOL_SYNC, m_sync); + DDX_Control(pDX, IDC_TOOL_SCRIPT, m_script); + DDX_Control(pDX, IDC_TOOL_SCENE, m_scene); + DDX_Control(pDX, IDC_TOOL_RTP, m_rtp); + DDX_Control(pDX, IDC_TOOL_RENDER, m_render); + DDX_Control(pDX, IDC_TOOL_PARSER, m_parser); + DDX_Control(pDX, IDC_TOOL_NET, m_net); + DDX_Control(pDX, IDC_TOOL_MMIO, m_mmio); + DDX_Control(pDX, IDC_TOOL_MEDIA, m_media); + DDX_Control(pDX, IDC_TOOL_CORE, m_core); + DDX_Control(pDX, IDC_TOOL_CONTAINER, m_container); + DDX_Control(pDX, IDC_TOOL_COMPOSE, m_compose); + DDX_Control(pDX, IDC_TOOL_CODING, m_coding); + DDX_Control(pDX, IDC_TOOL_CODEC, m_codec); + DDX_Control(pDX, IDC_TOOL_AUTHOR, m_author); + DDX_Control(pDX, IDC_LOG_LEVEL, m_Level); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptLogs, CDialog) + //{{AFX_MSG_MAP(COptLogs) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptLogs message handlers + +BOOL COptLogs::OnInitDialog() +{ + CDialog::OnInitDialog(); + + Osmo4 *gpac = GetApp(); + switch (gpac->m_log_level) { + case GF_LOG_ERROR: m_Level.SetCurSel(1); break; + case GF_LOG_WARNING: m_Level.SetCurSel(2); break; + case GF_LOG_INFO: m_Level.SetCurSel(3); break; + case GF_LOG_DEBUG: m_Level.SetCurSel(4); break; + default: m_Level.SetCurSel(0); break; + } + + m_sync.SetCheck(gpac->m_log_tools & GF_LOG_SYNC); + m_script.SetCheck(gpac->m_log_tools & GF_LOG_SCRIPT); + m_scene.SetCheck(gpac->m_log_tools & GF_LOG_SCENE); + m_rtp.SetCheck(gpac->m_log_tools & GF_LOG_RTP); + m_render.SetCheck(gpac->m_log_tools & GF_LOG_COMPOSE); + m_parser.SetCheck(gpac->m_log_tools & GF_LOG_PARSER); + m_net.SetCheck(gpac->m_log_tools & GF_LOG_NETWORK); + m_mmio.SetCheck(gpac->m_log_tools & GF_LOG_MMIO); + m_media.SetCheck(gpac->m_log_tools & GF_LOG_MEDIA); + m_core.SetCheck(gpac->m_log_tools & GF_LOG_CORE); + m_container.SetCheck(gpac->m_log_tools & GF_LOG_CONTAINER); + m_compose.SetCheck(gpac->m_log_tools & GF_LOG_INTERACT); + m_coding.SetCheck(gpac->m_log_tools & GF_LOG_CODING); + m_codec.SetCheck(gpac->m_log_tools & GF_LOG_CODEC); + m_author.SetCheck(gpac->m_log_tools & GF_LOG_AUTHOR); + + return TRUE; +} + +void COptLogs::SaveOptions() +{ + Osmo4 *gpac = GetApp(); + CString str = ""; + u32 flags = 0; + + switch (m_Level.GetCurSel()) { + case 1: + gf_cfg_set_key(gpac->m_user.config, "General", "LogLevel", "error"); + gpac->m_log_level = GF_LOG_ERROR; + break; + case 2: + gf_cfg_set_key(gpac->m_user.config, "General", "LogLevel", "warning"); + gpac->m_log_level = GF_LOG_WARNING; + break; + case 3: + gf_cfg_set_key(gpac->m_user.config, "General", "LogLevel", "info"); + gpac->m_log_level = GF_LOG_INFO; + break; + case 4: + gf_cfg_set_key(gpac->m_user.config, "General", "LogLevel", "debug"); + gpac->m_log_level = GF_LOG_DEBUG; + break; + default: + gf_cfg_set_key(gpac->m_user.config, "General", "LogLevel", "none"); + gpac->m_log_level = 0; + break; + } + gf_log_set_level(gpac->m_log_level); + + + if (m_sync.GetCheck()) { flags |= GF_LOG_SYNC; str +="sync:"; } + if (m_script.GetCheck()) { flags |= GF_LOG_SCRIPT; str +="script:"; } + if (m_scene.GetCheck()) { flags |= GF_LOG_SCENE; str +="scene:"; } + if (m_rtp.GetCheck()) { flags |= GF_LOG_RTP; str +="rtp:"; } + if (m_render.GetCheck()) { flags |= GF_LOG_COMPOSE; str +="compose:"; } + if (m_parser.GetCheck()) { flags |= GF_LOG_PARSER; str +="parser:"; } + if (m_net.GetCheck()) { flags |= GF_LOG_NETWORK; str +="network:"; } + if (m_mmio.GetCheck()) { flags |= GF_LOG_MMIO; str +="mmio:"; } + if (m_media.GetCheck()) { flags |= GF_LOG_MEDIA; str +="media:"; } + if (m_core.GetCheck()) { flags |= GF_LOG_CORE; str +="core:"; } + if (m_container.GetCheck()) { flags |= GF_LOG_CONTAINER; str +="container:"; } + if (m_compose.GetCheck()) { flags |= GF_LOG_INTERACT; str +="interact:"; } + if (m_coding.GetCheck()) { flags |= GF_LOG_CODING; str +="coding:"; } + if (m_codec.GetCheck()) { flags |= GF_LOG_CODEC; str +="codec:"; } + if (m_author.GetCheck()) { flags |= GF_LOG_AUTHOR; str +="author:"; } + + gf_cfg_set_key(gpac->m_user.config, "General", "LogTools", str); + gpac->m_log_tools = flags; + gf_log_set_tools(gpac->m_log_tools); +} diff --git a/applications/osmo4_w32/Options.h b/applications/osmo4_w32/Options.h new file mode 100644 index 0000000..b9e3df8 --- /dev/null +++ b/applications/osmo4_w32/Options.h @@ -0,0 +1,622 @@ +#if !defined(AFX_OPTIONS_H__5C839953_58C0_4D9D_89CE_2820C7686C1B__INCLUDED_) +#define AFX_OPTIONS_H__5C839953_58C0_4D9D_89CE_2820C7686C1B__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// Options.h : header file +// + + +class COptAudio : public CDialog +{ +// Construction +public: + COptAudio(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptAudio) + enum { IDD = IDD_OPT_AUDIO }; + CButton m_Notifs; + CComboBox m_DriverList; + CButton m_AudioResync; + CButton m_AudioMultiCH; + CEdit m_AudioDur; + CSpinButtonCtrl m_SpinFPS; + CButton m_ForceConfig; + CSpinButtonCtrl m_AudioSpin; + CEdit m_AudioEdit; + //}}AFX_DATA + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptAudio) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptAudio) + virtual BOOL OnInitDialog(); + afx_msg void OnForceAudio(); + afx_msg void OnSelchangeDriverList(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// +// OptDecoder dialog + +class OptDecoder : public CDialog +{ +// Construction +public: + OptDecoder(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(OptDecoder) + enum { IDD = IDD_OPT_DECODER }; + CComboBox m_Video; + CComboBox m_Audio; + //}}AFX_DATA + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(OptDecoder) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(OptDecoder) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class OptFiles : public CDialog +{ +// Construction +public: + OptFiles(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(OptFiles) + enum { IDD = IDD_OPT_FILETYPES }; + CButton m_DoAssociate; + CStatic m_PlugName; + CStatic m_mimes; + CStatic m_extensions; + CComboBox m_FileDescs; + //}}AFX_DATA + + void SetSelection(u32 sel); + char cur_ext[200], cur_mime[200]; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(OptFiles) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(OptFiles) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangeFilelist(); + afx_msg void OnAssociate(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// +// COptFont dialog + +class COptFont : public CDialog +{ +// Construction +public: + COptFont(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptFont) + enum { IDD = IDD_OPT_FONT }; + CComboBox m_TextureModes; + CComboBox m_Fonts; + CButton m_BrowseFont; + //}}AFX_DATA + + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptFont) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptFont) + virtual BOOL OnInitDialog(); + afx_msg void OnBrowseFont(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// +// COptGen dialog + +class COptGen : public CDialog +{ +// Construction +public: + COptGen(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptGen) + enum { IDD = IDD_OPT_GEN }; + CButton m_LookForSubs; + CButton m_ViewXMT; + CButton m_NoConsole; + CButton m_Loop; + CButton m_SingleInstance; + //}}AFX_DATA + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptGen) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptGen) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +///////////////////////////////////////////////////////////////////////////// +// COptHTTP dialog + +class COptHTTP : public CDialog +{ +// Construction +public: + COptHTTP(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptHTTP) + enum { IDD = IDD_OPT_HTTP }; + CEdit m_ProxyName; + CButton m_useProxy; + CEdit m_SAXDuration; + CButton m_Progressive; + CButton m_RestartFile; + CButton m_CleanCache; + CButton m_CacheDir; + //}}AFX_DATA + + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptHTTP) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptHTTP) + afx_msg void OnBrowseCache(); + virtual BOOL OnInitDialog(); + afx_msg void OnSaxProgressive(); + afx_msg void OnUseProxy(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +class COptMCache : public CDialog +{ +// Construction +public: + COptMCache(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptMCache) + enum { IDD = IDD_OPT_MCACHE }; + CEdit m_BaseName; + CButton m_UseBase; + CButton m_Overwrite; + CButton m_RecDir; + //}}AFX_DATA + + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptMCache) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptMCache) + virtual BOOL OnInitDialog(); + afx_msg void OnBrowseMcache(); + afx_msg void OnMcacheUsename(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class COptRender : public CDialog +{ +// Construction +public: + COptRender(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptRender) + enum { IDD = IDD_OPT_RENDER }; + CComboBox m_DrawBounds; + CComboBox m_Graphics; + CButton m_Use3DRender; + CComboBox m_AntiAlias; + CButton m_ForceSize; + CButton m_HighSpeed; + CComboBox m_BIFSRate; + //}}AFX_DATA + + + Bool SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptRender) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptRender) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class COptRender2D : public CDialog +{ +// Construction +public: + COptRender2D(CWnd* pParent = NULL); // standard constructor + + void SaveOptions(); + void SetYUV(); + +// Dialog Data + //{{AFX_DATA(COptRender2D) + enum { IDD = IDD_OPT_RENDER2D }; + CStatic m_YUVFormat; + CButton m_NoYUV; + CButton m_Scalable; + CButton m_DirectRender; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptRender2D) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptRender2D) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class COptRender3D : public CDialog +{ +// Construction +public: + COptRender3D(CWnd* pParent = NULL); // standard constructor + + void SaveOptions(); + +// Dialog Data + //{{AFX_DATA(COptRender3D) + enum { IDD = IDD_OPT_RENDER3D }; + CButton m_BitmapPixels; + CButton m_DisableTXRect; + CButton m_RasterOutlines; + CButton m_EmulPow2; + CButton m_PolyAA; + CComboBox m_BackCull; + CComboBox m_DrawNormals; + CComboBox m_Wireframe; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptRender3D) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptRender3D) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class COptStream : public CDialog +{ +// Construction +public: + COptStream(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptStream) + enum { IDD = IDD_OPT_STREAM }; + CEdit m_RebufferLen; + CButton m_Rebuffer; + CEdit m_Buffer; + CEdit m_Timeout; + CButton m_Reorder; + CButton m_UseRTSP; + CComboBox m_Port; + //}}AFX_DATA + + + void SaveOptions(); + + void CheckRebuffer(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptStream) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptStream) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangePort(); + afx_msg void OnRtsp(); + afx_msg void OnRebuffer(); + afx_msg void OnUpdateRebufferLen(); + afx_msg void OnUpdateBuffer(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class COptSystems : public CDialog +{ +// Construction +public: + COptSystems(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptSystems) + enum { IDD = IDD_OPT_SYSTEMS }; + CButton m_ForceDuration; + CComboBox m_Threading; + CButton m_BifsAlwaysDrawn; + CComboBox m_Lang; + //}}AFX_DATA + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptSystems) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptSystems) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class COptVideo : public CDialog +{ +// Construction +public: + COptVideo(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptVideo) + enum { IDD = IDD_OPT_VIDEO }; + CButton m_SwitchRes; + CButton m_UseHWMemory; + CComboBox m_Videos; + //}}AFX_DATA + + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptVideo) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptVideo) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// +// COptLogs dialog + +class COptLogs : public CDialog +{ +// Construction +public: + COptLogs(CWnd* pParent = NULL); // standard constructor + void SaveOptions(); + +// Dialog Data + //{{AFX_DATA(COptLogs) + enum { IDD = IDD_OPT_LOGS }; + CButton m_sync; + CButton m_script; + CButton m_scene; + CButton m_rtp; + CButton m_render; + CButton m_parser; + CButton m_net; + CButton m_mmio; + CButton m_media; + CButton m_core; + CButton m_container; + CButton m_compose; + CButton m_coding; + CButton m_codec; + CButton m_author; + CComboBox m_Level; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptLogs) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptLogs) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + + +///////////////////////////////////////////////////////////////////////////// +// COptions dialog + +class COptions : public CDialog +{ +// Construction +public: + COptions(CWnd* pParent = NULL); // standard constructor + BOOL Create(CWnd * pParent) + { + return CDialog::Create( COptions::IDD, pParent); + } + +// Dialog Data + //{{AFX_DATA(COptions) + enum { IDD = IDD_OPTIONS }; + CComboBox m_Selector; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptions) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + COptGen m_general; + COptSystems m_systems; + COptRender m_render; + COptRender2D m_render2d; + COptRender3D m_render3d; + COptAudio m_audio; + OptDecoder m_decoder; + COptVideo m_video; + COptHTTP m_http; + COptFont m_font; + COptStream m_stream; + COptMCache m_cache; + OptFiles m_files; + COptLogs m_logs; + + void HideAll(); + + // Generated message map functions + //{{AFX_MSG(COptions) + virtual BOOL OnInitDialog(); + afx_msg void OnSaveopt(); + afx_msg void OnClose(); + afx_msg void OnDestroy(); + afx_msg void OnSelchangeSelect(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_OPTIONS_H__5C839953_58C0_4D9D_89CE_2820C7686C1B__INCLUDED_) diff --git a/applications/osmo4_w32/Osmo4.cpp b/applications/osmo4_w32/Osmo4.cpp new file mode 100644 index 0000000..0b39e19 --- /dev/null +++ b/applications/osmo4_w32/Osmo4.cpp @@ -0,0 +1,1073 @@ +// GPAC.cpp : Defines the class behaviors for the application. +// + +#include "stdafx.h" +#include "Osmo4.h" +#include + +#include "MainFrm.h" +#include "OpenUrl.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// Osmo4 + +BEGIN_MESSAGE_MAP(Osmo4, CWinApp) + //{{AFX_MSG_MAP(Osmo4) + ON_COMMAND(ID_FILEOPEN, OnOpenFile) + ON_COMMAND(ID_FILE_STEP, OnFileStep) + ON_COMMAND(ID_OPEN_URL, OnOpenUrl) + ON_COMMAND(ID_FILE_RELOAD, OnFileReload) + ON_COMMAND(ID_CONFIG_RELOAD, OnConfigReload) + ON_COMMAND(ID_FILE_PLAY, OnFilePlay) + ON_UPDATE_COMMAND_UI(ID_FILE_PLAY, OnUpdateFilePlay) + ON_UPDATE_COMMAND_UI(ID_FILE_STEP, OnUpdateFileStep) + ON_COMMAND(ID_FILE_STOP, OnFileStop) + ON_UPDATE_COMMAND_UI(ID_FILE_STOP, OnUpdateFileStop) + ON_COMMAND(ID_SWITCH_RENDER, OnSwitchRender) + ON_UPDATE_COMMAND_UI(ID_FILE_RELOAD, OnUpdateFileStop) + ON_COMMAND(ID_H_ABOUT, OnAbout) + ON_COMMAND(ID_FILE_MIGRATE, OnFileMigrate) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// Osmo4 construction + +Osmo4::Osmo4() +{ +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only Osmo4 object + +Osmo4 theApp; + + + +class UserPassDialog : public CDialog +{ +// Construction +public: + UserPassDialog(CWnd* pParent = NULL); // standard constructor + + Bool GetPassword(const char *site_url, char *user, char *password); + +// Dialog Data + //{{AFX_DATA(UserPassDialog) + enum { IDD = IDD_PASSWD }; + CStatic m_SiteURL; + CEdit m_User; + CEdit m_Pass; + //}}AFX_DATA + + void SetSelection(u32 sel); + char cur_ext[200], cur_mime[200]; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(UserPassDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + const char *m_site_url; + char *m_user, *m_password; + + // Generated message map functions + //{{AFX_MSG(UserPassDialog) + virtual BOOL OnInitDialog(); + afx_msg void OnClose(); + //}}AFX_MSG +}; + +UserPassDialog::UserPassDialog(CWnd* pParent /*=NULL*/) + : CDialog(UserPassDialog::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptStream) + //}}AFX_DATA_INIT +} + +void UserPassDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(UserPassDialog) + DDX_Control(pDX, IDC_TXT_SITE, m_SiteURL); + DDX_Control(pDX, IDC_EDIT_USER, m_User); + DDX_Control(pDX, IDC_EDIT_PASSWORD, m_Pass); + //}}AFX_DATA_MAP +} + +BOOL UserPassDialog::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_SiteURL.SetWindowText(m_site_url); + m_User.SetWindowText(m_user); + m_Pass.SetWindowText(""); + return TRUE; +} + +void UserPassDialog::OnClose() +{ + m_User.GetWindowText(m_user, 50); + m_Pass.GetWindowText(m_password, 50); +} + +Bool UserPassDialog::GetPassword(const char *site_url, char *user, char *password) +{ + m_site_url = site_url; + m_user = user; + if (DoModal() != IDOK) return 0; + return 1; +} + + +u32 get_sys_col(int idx) +{ + u32 res; + DWORD val = GetSysColor(idx); + res = (val)&0xFF; res<<=8; + res |= (val>>8)&0xFF; res<<=8; + res |= (val>>16)&0xFF; + return res; +} + +static void Osmo4_progress_cbk(void *usr, char *title, u32 done, u32 total) +{ + if (!total) return; + CMainFrame *pFrame = (CMainFrame *) ((Osmo4 *) usr)->m_pMainWnd; + s32 prog = (s32) ( (100 * (u64)done) / total); + if (pFrame->m_last_prog < prog) { + pFrame->console_err = GF_OK; + pFrame->m_last_prog = prog; + pFrame->console_message.Format("%s %02d %%", title, prog); + pFrame->PostMessage(WM_CONSOLEMSG, 0, 0); + if (done==total) pFrame->m_last_prog = -1; + } +} + +#define W32_MIN_WIDTH 120 + +static void log_msg(char *msg) +{ + ::MessageBox(NULL, msg, "GPAC", MB_OK); +} +Bool Osmo4_EventProc(void *priv, GF_Event *evt) +{ + u32 dur; + Osmo4 *gpac = (Osmo4 *) priv; + CMainFrame *pFrame = (CMainFrame *) gpac->m_pMainWnd; + /*shutdown*/ + if (!pFrame) return 0; + + switch (evt->type) { + case GF_EVENT_DURATION: + dur = (u32) (1000 * evt->duration.duration); + //if (dur<1100) dur = 0; + pFrame->m_pPlayList->SetDuration((u32) evt->duration.duration ); + gpac->max_duration = dur; + gpac->can_seek = evt->duration.can_seek; + if (!gpac->can_seek) { + pFrame->m_Sliders.m_PosSlider.EnableWindow(FALSE); + } else { + pFrame->m_Sliders.m_PosSlider.EnableWindow(TRUE); + pFrame->m_Sliders.m_PosSlider.SetRangeMin(0); + pFrame->m_Sliders.m_PosSlider.SetRangeMax(dur); + } + break; + + case GF_EVENT_MESSAGE: + if (!evt->message.service || !strcmp(evt->message.service, (LPCSTR) pFrame->m_pPlayList->GetURL() )) { + pFrame->console_service = "main service"; + } else { + pFrame->console_service = evt->message.service; + } + if (evt->message.error!=GF_OK) { + if (evt->message.errorm_NoConsole) { + pFrame->console_err = evt->message.error; + pFrame->console_message = evt->message.message; + gpac->m_pMainWnd->PostMessage(WM_CONSOLEMSG, 0, 0); + + /*any error before connection confirm is a service connection error*/ + if (!gpac->m_isopen) pFrame->m_pPlayList->SetDead(); + } + return 0; + } + if (gpac->m_NoConsole) return 0; + + /*process user message*/ + pFrame->console_err = GF_OK; + pFrame->console_message = evt->message.message; + gpac->m_pMainWnd->PostMessage(WM_CONSOLEMSG, 0, 0); + break; + case GF_EVENT_PROGRESS: + char *szType; + if (evt->progress.progress_type==0) szType = "Buffer "; + else if (evt->progress.progress_type==1) szType = "Download "; + else if (evt->progress.progress_type==2) szType = "Import "; + gf_set_progress(szType, evt->progress.done, evt->progress.total); + break; + case GF_EVENT_NAVIGATE_INFO: + pFrame->console_message = evt->navigate.to_url; + gpac->m_pMainWnd->PostMessage(WM_CONSOLEMSG, 1000, 0); + break; + + case GF_EVENT_SCENE_SIZE: + if (evt->size.width && evt->size.height) { + gpac->orig_width = evt->size.width; + gpac->orig_height = evt->size.height; + if (gpac->m_term && !pFrame->m_bFullScreen) + pFrame->PostMessage(WM_SETSIZE, evt->size.width, evt->size.height); + } + break; + /*don't resize on win32 msg notif*/ + case GF_EVENT_SIZE: + if (gpac->m_term && !pFrame->m_bFullScreen && gpac->orig_width && (evt->size.width < W32_MIN_WIDTH) ) + pFrame->PostMessage(WM_SETSIZE, W32_MIN_WIDTH, (W32_MIN_WIDTH*gpac->orig_height) / gpac->orig_width); + break; + + case GF_EVENT_CONNECT: + if (pFrame->m_bStartupFile) return 0; + + pFrame->BuildStreamList(1); + if (evt->connect.is_connected) { + pFrame->BuildChapterList(0); + gpac->m_isopen = 1; + } else { + gpac->max_duration = 0; + gpac->m_isopen = 0; + pFrame->BuildChapterList(1); + } + pFrame->m_wndToolBar.SetButtonInfo(5, ID_FILE_PLAY, TBBS_BUTTON, gpac->m_isopen ? 4 : 3); + pFrame->m_Sliders.m_PosSlider.SetPos(0); + pFrame->SetProgTimer(1); + if (!pFrame->m_bFullScreen) { + pFrame->SetFocus(); + pFrame->SetForegroundWindow(); + } + break; + + case GF_EVENT_QUIT: + pFrame->PostMessage(WM_CLOSE, 0L, 0L); + break; + case GF_EVENT_MIGRATE: + { + const char *str = gf_cfg_get_key(gpac->m_user.config, "Network", "SessionMigration"); + if (!str || !strcmp(str, "no")) + gf_cfg_set_key(gpac->m_user.config, "Network", "SessionMigration", "yes"); + + gpac->m_navigate_url = ""; + pFrame->PostMessage(WM_NAVIGATE, NULL, NULL); + } + break; + case GF_EVENT_KEYDOWN: + if (gpac->can_seek && evt->key.flags & GF_KEY_MOD_ALT) { + s32 res; + switch (evt->key.key_code) { + case GF_KEY_LEFT: + res = gf_term_get_time_in_ms(gpac->m_term) - 5*gpac->max_duration/100; + if (res<0) res=0; + gpac->PlayFromTime(res); + break; + case GF_KEY_RIGHT: + res = gf_term_get_time_in_ms(gpac->m_term) + 5*gpac->max_duration/100; + if ((u32) res>=gpac->max_duration) res = 0; + gpac->PlayFromTime(res); + break; + case GF_KEY_DOWN: + res = gf_term_get_time_in_ms(gpac->m_term) - 60000; + if (res<0) res=0; + gpac->PlayFromTime(res); + break; + case GF_KEY_UP: + res = gf_term_get_time_in_ms(gpac->m_term) + 60000; + if ((u32) res>=gpac->max_duration) res = 0; + gpac->PlayFromTime(res); + break; + } + } else if (evt->key.flags & GF_KEY_MOD_CTRL) { + switch (evt->key.key_code) { + case GF_KEY_LEFT: + pFrame->m_pPlayList->PlayPrev(); + break; + case GF_KEY_RIGHT: + pFrame->m_pPlayList->PlayNext(); + break; + } + } else { + switch (evt->key.key_code) { + case GF_KEY_HOME: + gf_term_set_option(gpac->m_term, GF_OPT_NAVIGATION_TYPE, 1); + break; + case GF_KEY_ESCAPE: + pFrame->PostMessage(WM_COMMAND, ID_VIEW_FULLSCREEN); + break; + } + } + break; + case GF_EVENT_NAVIGATE: + /*fixme - a proper browser would require checking mime type & co*/ + /*store URL since it may be destroyed, and post message*/ + gpac->m_navigate_url = evt->navigate.to_url; + pFrame->PostMessage(WM_NAVIGATE, NULL, NULL); + return 1; + case GF_EVENT_VIEWPOINTS: + pFrame->BuildViewList(); + return 0; + case GF_EVENT_STREAMLIST: + pFrame->BuildStreamList(0); + return 0; + case GF_EVENT_SET_CAPTION: + pFrame->SetWindowText(evt->caption.caption); + break; + case GF_EVENT_DBLCLICK: + pFrame->PostMessage(WM_COMMAND, ID_VIEW_FULLSCREEN); + return 0; + case GF_EVENT_AUTHORIZATION: + { + UserPassDialog passdlg; + return passdlg.GetPassword(evt->auth.site_url, evt->auth.user, evt->auth.password); + } + + case GF_EVENT_SYS_COLORS: + evt->sys_cols.sys_colors[0] = get_sys_col(COLOR_ACTIVEBORDER); + evt->sys_cols.sys_colors[1] = get_sys_col(COLOR_ACTIVECAPTION); + evt->sys_cols.sys_colors[2] = get_sys_col(COLOR_APPWORKSPACE); + evt->sys_cols.sys_colors[3] = get_sys_col(COLOR_BACKGROUND); + evt->sys_cols.sys_colors[4] = get_sys_col(COLOR_BTNFACE); + evt->sys_cols.sys_colors[5] = get_sys_col(COLOR_BTNHIGHLIGHT); + evt->sys_cols.sys_colors[6] = get_sys_col(COLOR_BTNSHADOW); + evt->sys_cols.sys_colors[7] = get_sys_col(COLOR_BTNTEXT); + evt->sys_cols.sys_colors[8] = get_sys_col(COLOR_CAPTIONTEXT); + evt->sys_cols.sys_colors[9] = get_sys_col(COLOR_GRAYTEXT); + evt->sys_cols.sys_colors[10] = get_sys_col(COLOR_HIGHLIGHT); + evt->sys_cols.sys_colors[11] = get_sys_col(COLOR_HIGHLIGHTTEXT); + evt->sys_cols.sys_colors[12] = get_sys_col(COLOR_INACTIVEBORDER); + evt->sys_cols.sys_colors[13] = get_sys_col(COLOR_INACTIVECAPTION); + evt->sys_cols.sys_colors[14] = get_sys_col(COLOR_INACTIVECAPTIONTEXT); + evt->sys_cols.sys_colors[15] = get_sys_col(COLOR_INFOBK); + evt->sys_cols.sys_colors[16] = get_sys_col(COLOR_INFOTEXT); + evt->sys_cols.sys_colors[17] = get_sys_col(COLOR_MENU); + evt->sys_cols.sys_colors[18] = get_sys_col(COLOR_MENUTEXT); + evt->sys_cols.sys_colors[19] = get_sys_col(COLOR_SCROLLBAR); + evt->sys_cols.sys_colors[20] = get_sys_col(COLOR_3DDKSHADOW); + evt->sys_cols.sys_colors[21] = get_sys_col(COLOR_3DFACE); + evt->sys_cols.sys_colors[22] = get_sys_col(COLOR_3DHIGHLIGHT); + evt->sys_cols.sys_colors[23] = get_sys_col(COLOR_3DLIGHT); + evt->sys_cols.sys_colors[24] = get_sys_col(COLOR_3DSHADOW); + evt->sys_cols.sys_colors[25] = get_sys_col(COLOR_WINDOW); + evt->sys_cols.sys_colors[26] = get_sys_col(COLOR_WINDOWFRAME); + evt->sys_cols.sys_colors[27] = get_sys_col(COLOR_WINDOWTEXT); + return 1; + } + return 0; +} + + +/*here's the trick: use a storage section shared among all processes for the wnd handle and for the command line +NOTE: this has to be static memory of course, don't try to alloc anything there...*/ +#pragma comment(linker, "/SECTION:.shr,RWS") +#pragma data_seg(".shr") +HWND static_gpac_hwnd = NULL; +char static_szCmdLine[MAX_PATH] = ""; +#pragma data_seg() + +const char *static_gpac_get_url() +{ + return (const char *) static_szCmdLine; +} + +static void osmo4_do_log(void *cbk, u32 level, u32 tool, const char *fmt, va_list list) +{ + FILE *logs = (FILE *) cbk; + vfprintf(logs, fmt, list); + fflush(logs); +} + +BOOL Osmo4::InitInstance() +{ + CCommandLineInfo cmdInfo; + + m_logs = NULL; + + m_term = NULL; + + memset(&m_user, 0, sizeof(GF_User)); + + strcpy((char *) szAppPath, AfxGetApp()->m_pszHelpFilePath); + while (szAppPath[strlen((char *) szAppPath)-1] != '\\') szAppPath[strlen((char *) szAppPath)-1] = 0; + if (szAppPath[strlen((char *) szAppPath)-1] != '\\') strcat(szAppPath, "\\"); + + /*setup user*/ + memset(&m_user, 0, sizeof(GF_User)); + + Bool first_launch = 0; + /*init config and modules*/ + m_user.config = gf_cfg_new((const char *) szAppPath, "GPAC.cfg"); + if (!m_user.config) { + first_launch = 1; + /*create blank config file in the exe dir*/ + unsigned char config_file[MAX_PATH]; + strcpy((char *) config_file, (const char *) szAppPath); + strcat((char *) config_file, "GPAC.cfg"); + FILE *ft = fopen((const char *) config_file, "wt"); + fclose(ft); + m_user.config = gf_cfg_new((const char *) szAppPath, "GPAC.cfg"); + if (!m_user.config) { + MessageBox(NULL, "GPAC Configuration file not found", "Fatal Error", MB_OK); + m_pMainWnd->PostMessage(WM_CLOSE); + } + } + const char *opt = gf_cfg_get_key(m_user.config, "General", "SingleInstance"); + m_SingleInstance = (opt && !stricmp(opt, "yes")) ? 1 : 0; + + m_hMutex = NULL; + if (m_SingleInstance) { + m_hMutex = CreateMutex(NULL, FALSE, "Osmo4_GPAC_INSTANCE"); + if ( GetLastError() == ERROR_ALREADY_EXISTS ) { + char szDIR[1024]; + if (m_hMutex) CloseHandle(m_hMutex); + m_hMutex = NULL; + + if (!static_gpac_hwnd || !IsWindow(static_gpac_hwnd) ) { + ::MessageBox(NULL, "Osmo4 ghost process detected", "Error at last shutdown" , MB_OK); + } else { + ::SetForegroundWindow(static_gpac_hwnd); + + if (m_lpCmdLine && strlen(m_lpCmdLine)) { + DWORD res; + u32 len; + char *the_url, *cmd; + GetCurrentDirectory(1024, szDIR); + if (szDIR[strlen(szDIR)-1] != '\\') strcat(szDIR, "\\"); + cmd = (char *)(const char *) m_lpCmdLine; + strcpy(static_szCmdLine, ""); + if (cmd[0]=='"') cmd+=1; + + if (!strnicmp(cmd, "-queue ", 7)) { + strcat(static_szCmdLine, "-queue "); + cmd += 7; + } + the_url = gf_url_concatenate(szDIR, cmd); + if (!the_url) { + strcat(static_szCmdLine, cmd); + } else { + strcat(static_szCmdLine, the_url); + free(the_url); + } + while ( (len = strlen(static_szCmdLine)) ) { + char s = static_szCmdLine[len-1]; + if ((s==' ') || (s=='"')) static_szCmdLine[len-1]=0; + else break; + } + ::SendMessageTimeout(static_gpac_hwnd, WM_NEWINSTANCE, 0, 0, 0, 1000, &res); + } + } + + return FALSE; + } + } + +#if 0 + // Standard initialization +#ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL +#else + Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif + +#endif + + SetRegistryKey(_T("GPAC")); + CMainFrame* pFrame = new CMainFrame; + m_pMainWnd = pFrame; + pFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL, NULL); + m_pMainWnd->DragAcceptFiles(); + + if (m_SingleInstance) static_gpac_hwnd = m_pMainWnd->m_hWnd; + + const char *str = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + m_user.modules = gf_modules_new(str, m_user.config); + if (!m_user.modules) { + const char *sOpt; + /*inital launch*/ + m_user.modules = gf_modules_new(szAppPath, m_user.config); + if (m_user.modules) { + unsigned char str_path[MAX_PATH]; + gf_cfg_set_key(m_user.config, "General", "ModulesDirectory", (const char *) szAppPath); + + sOpt = gf_cfg_get_key(m_user.config, "Compositor", "Raster2D"); + if (!sOpt) gf_cfg_set_key(m_user.config, "Compositor", "Raster2D", "GPAC 2D Raster"); + + sOpt = gf_cfg_get_key(m_user.config, "General", "CacheDirectory"); + if (!sOpt) { + sprintf((char *) str_path, "%scache", szAppPath); + gf_cfg_set_key(m_user.config, "General", "CacheDirectory", (const char *) str_path); + } + /*setup UDP traffic autodetect*/ + gf_cfg_set_key(m_user.config, "Network", "AutoReconfigUDP", "yes"); + gf_cfg_set_key(m_user.config, "Network", "UDPTimeout", "10000"); + gf_cfg_set_key(m_user.config, "Network", "BufferLength", "3000"); + + /*first launch, register all files ext*/ + u32 i; + for (i=0; iCanHandleURL(ifce, "test.test"); + gf_modules_close_interface((GF_BaseInterface *)ifce); + } + } + + sprintf((char *) str_path, "%sgpac.mp4", szAppPath); + gf_cfg_set_key(m_user.config, "General", "StartupFile", (const char *) str_path); + } + + /*check audio config on windows, force config*/ + sOpt = gf_cfg_get_key(m_user.config, "Audio", "ForceConfig"); + if (!sOpt) { + gf_cfg_set_key(m_user.config, "Audio", "ForceConfig", "yes"); + gf_cfg_set_key(m_user.config, "Audio", "NumBuffers", "2"); + gf_cfg_set_key(m_user.config, "Audio", "TotalDuration", "120"); + } + /*check video config */ + sOpt = gf_cfg_get_key(m_user.config, "Video", "UseHardwareMemory"); + if (!sOpt) gf_cfg_set_key(m_user.config, "Video", "UseHardwareMemory", "yes"); + + /*by default use GDIplus, much faster than freetype on font loading*/ + gf_cfg_set_key(m_user.config, "FontEngine", "FontReader", "gdip_rend"); + + } + if (! gf_modules_get_count(m_user.modules) ) { + MessageBox(NULL, "No modules available - system cannot work", "Fatal Error", MB_OK); + m_pMainWnd->PostMessage(WM_CLOSE); + } + + /*setup font dir*/ + str = gf_cfg_get_key(m_user.config, "FontEngine", "FontDirectory"); + if (!str) { + char szFtPath[MAX_PATH]; + ::GetWindowsDirectory((char*)szFtPath, MAX_PATH); + if (szFtPath[strlen((char*)szFtPath)-1] != '\\') strcat((char*)szFtPath, "\\"); + strcat((char *)szFtPath, "Fonts"); + gf_cfg_set_key(m_user.config, "FontEngine", "FontDirectory", (const char *) szFtPath); + } + + /*check video driver, if none or raw_out use dx_hw by default*/ + str = gf_cfg_get_key(m_user.config, "Video", "DriverName"); + if (!str || !stricmp(str, "raw_out")) { + gf_cfg_set_key(m_user.config, "Video", "DriverName", "dx_hw"); + } + + /*reset session migration*/ + str = gf_cfg_get_key(m_user.config, "Network", "SessionMigration"); + if (str && !strcmp(str, "yes")) + gf_cfg_set_key(m_user.config, "Network", "SessionMigration", "no"); + + /*check log file*/ + str = gf_cfg_get_key(m_user.config, "General", "LogFile"); + if (str) { + m_logs = fopen(str, "wt"); + gf_log_set_callback(m_logs, osmo4_do_log); + } + else m_logs = NULL; + + /*set log level*/ + m_log_level = 0; + str = gf_cfg_get_key(m_user.config, "General", "LogLevel"); + if (str) { + if (!stricmp(str, "debug")) m_log_level = GF_LOG_DEBUG; + else if (!stricmp(str, "info")) m_log_level = GF_LOG_INFO; + else if (!stricmp(str, "warning")) m_log_level = GF_LOG_WARNING; + else if (!stricmp(str, "error")) m_log_level = GF_LOG_ERROR; + gf_log_set_level(m_log_level); + } + + /*set log tools*/ + m_log_tools = 0; + str = gf_cfg_get_key(m_user.config, "General", "LogTools"); + if (str) { + char *sep; + char *val = (char *) str; + while (val) { + sep = strchr(val, ':'); + if (sep) sep[0] = 0; + if (!stricmp(val, "core")) m_log_tools |= GF_LOG_CODING; + else if (!stricmp(val, "coding")) m_log_tools |= GF_LOG_CODING; + else if (!stricmp(val, "container")) m_log_tools |= GF_LOG_CONTAINER; + else if (!stricmp(val, "network")) m_log_tools |= GF_LOG_NETWORK; + else if (!stricmp(val, "rtp")) m_log_tools |= GF_LOG_RTP; + else if (!stricmp(val, "author")) m_log_tools |= GF_LOG_AUTHOR; + else if (!stricmp(val, "sync")) m_log_tools |= GF_LOG_SYNC; + else if (!stricmp(val, "codec")) m_log_tools |= GF_LOG_CODEC; + else if (!stricmp(val, "parser")) m_log_tools |= GF_LOG_PARSER; + else if (!stricmp(val, "media")) m_log_tools |= GF_LOG_MEDIA; + else if (!stricmp(val, "scene")) m_log_tools |= GF_LOG_SCENE; + else if (!stricmp(val, "script")) m_log_tools |= GF_LOG_SCRIPT; + else if (!stricmp(val, "interact")) m_log_tools |= GF_LOG_INTERACT; + else if (!stricmp(val, "compose")) m_log_tools |= GF_LOG_COMPOSE; + else if (!stricmp(val, "mmio")) m_log_tools |= GF_LOG_MMIO; + else if (!stricmp(val, "none")) m_log_tools = 0; + else if (!stricmp(val, "all")) m_log_tools = 0xFFFFFFFF; + if (!sep) break; + sep[0] = ':'; + val = sep+1; + } + gf_log_set_tools(m_log_tools); + } + + gf_sys_init(); + + m_user.opaque = this; + m_user.os_window_handler = pFrame->m_pWndView->m_hWnd; + m_user.EventProc = Osmo4_EventProc; + + m_reset = 0; + orig_width = 320; + orig_height = 240; + + gf_set_progress_callback(this, Osmo4_progress_cbk); + + m_term = gf_term_new(&m_user); + if (! m_term) { + MessageBox(NULL, "Cannot load GPAC Terminal", "Fatal Error", MB_OK); + m_pMainWnd->PostMessage(WM_CLOSE); + return TRUE; + } + SetOptions(); + UpdateRenderSwitch(); + + pFrame->SendMessage(WM_SETSIZE, orig_width, orig_height); + pFrame->m_Address.ReloadURLs(); + + pFrame->m_Sliders.SetVolume(); + + m_reconnect_time = 0; + + + ParseCommandLine(cmdInfo); + + start_mode = 0; + + if (! cmdInfo.m_strFileName.IsEmpty()) { + pFrame->m_pPlayList->QueueURL(cmdInfo.m_strFileName); + pFrame->m_pPlayList->RefreshList(); + pFrame->m_pPlayList->PlayNext(); + } else { + char sPL[MAX_PATH]; + strcpy((char *) sPL, szAppPath); + strcat(sPL, "gpac_pl.m3u"); + pFrame->m_pPlayList->OpenPlayList(sPL); + const char *sOpt = gf_cfg_get_key(GetApp()->m_user.config, "General", "PLEntry"); + if (sOpt) { + s32 count = (s32)gf_list_count(pFrame->m_pPlayList->m_entries); + pFrame->m_pPlayList->m_cur_entry = atoi(sOpt); + if (pFrame->m_pPlayList->m_cur_entry>=count) + pFrame->m_pPlayList->m_cur_entry = count-1; + } else { + pFrame->m_pPlayList->m_cur_entry = -1; + } +#if 0 + if (pFrame->m_pPlayList->m_cur_entry>=0) { + start_mode = 1; + pFrame->m_pPlayList->Play(); + } +#endif + + sOpt = gf_cfg_get_key(GetApp()->m_user.config, "General", "StartupFile"); + if (sOpt) gf_term_connect(m_term, sOpt); + } + pFrame->SetFocus(); + pFrame->SetForegroundWindow(); + return TRUE; +} + +int Osmo4::ExitInstance() +{ + if (m_term) gf_term_del(m_term); + if (m_user.modules) gf_modules_del(m_user.modules); + if (m_user.config) gf_cfg_del(m_user.config); + gf_sys_close(); + /*last instance*/ + if (m_hMutex) { + CloseHandle(m_hMutex); + static_gpac_hwnd = NULL; + } + if (m_logs) fclose(m_logs); + return CWinApp::ExitInstance(); +} + + + +///////////////////////////////////////////////////////////////////////////// +// Osmo4 message handlers + + + + + +///////////////////////////////////////////////////////////////////////////// +// CAboutDlg dialog used for App About + +class CAboutDlg : public CDialog +{ +public: + CAboutDlg(); + +// Dialog Data + //{{AFX_DATA(CAboutDlg) + enum { IDD = IDD_ABOUTBOX }; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAboutDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + //{{AFX_MSG(CAboutDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnGogpac(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) +{ + //{{AFX_DATA_INIT(CAboutDlg) + //}}AFX_DATA_INIT +} + +void CAboutDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAboutDlg) + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) + //{{AFX_MSG_MAP(CAboutDlg) + ON_BN_CLICKED(IDC_GOGPAC, OnGogpac) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +void Osmo4::OnAbout() +{ + CAboutDlg aboutDlg; + aboutDlg.DoModal(); +} + +BOOL CAboutDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + CString str = "GPAC/Osmo4 - version " GPAC_FULL_VERSION; + SetWindowText(str); + return TRUE; +} + +void CAboutDlg::OnGogpac() +{ + ShellExecute(NULL, "open", "http://gpac.sourceforge.net", NULL, NULL, SW_SHOWNORMAL); +} + +///////////////////////////////////////////////////////////////////////////// +// Osmo4 message handlers + + +void Osmo4::SetOptions() +{ + const char *sOpt = gf_cfg_get_key(m_user.config, "General", "Loop"); + m_Loop = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(m_user.config, "General", "LookForSubtitles"); + m_LookForSubtitles = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(m_user.config, "General", "ConsoleOff"); + m_NoConsole = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(m_user.config, "General", "ViewXMT"); + m_ViewXMTA = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(m_user.config, "General", "NoMIMETypeFetch"); + m_NoMimeFetch = (!sOpt || !stricmp(sOpt, "yes")) ? 1 : 0; +} + + +void Osmo4::OnOpenUrl() +{ + COpenUrl url; + if (url.DoModal() != IDOK) return; + + CMainFrame *pFrame = (CMainFrame *) m_pMainWnd; + pFrame->m_pPlayList->Truncate(); + pFrame->m_pPlayList->QueueURL(url.m_url); + pFrame->m_pPlayList->RefreshList(); + pFrame->m_pPlayList->PlayNext(); +} + + +CString Osmo4::GetFileFilter() +{ + u32 keyCount, i; + CString sFiles; + CString sExts; + CString supportedFiles; + + /*force MP4 and 3GP files at beginning to make sure they are selected (Win32 bug with too large filters)*/ + supportedFiles = "All Known Files|*.m3u;*.pls;*.mp4;*.3gp;*.3g2"; + + sExts = ""; + sFiles = ""; + keyCount = gf_cfg_get_key_count(m_user.config, "MimeTypes"); + for (i=0; i=0) continue; + /*if same extensions for # mime types skip (don't polluate the file list)*/ + if (sExts.Find(szKeyList)>=0) continue; + + sExts += szKeyList; + sExts += " "; + sFiles += sDesc; + sFiles += "|"; + + first = 1; + + sOpt = CString(szKeyList); + while (1) { + + int pos = sOpt.Find(' '); + CString ext = (pos==-1) ? sOpt : sOpt.Left(pos); + /*WATCHOUT: we do have some "double" ext , eg .wrl.gz - these are NOT supported by windows*/ + if (ext.Find(".")<0) { + if (!first) { + sFiles += ";"; + } else { + first = 0; + } + sFiles += "*."; + sFiles += ext; + + CString sext = ext; + sext += ";"; + if (supportedFiles.Find(sext)<0) { + supportedFiles += ";*."; + supportedFiles += ext; + } + } + + if (sOpt==ext) break; + CString rem; + rem.Format("%s ", (LPCTSTR) ext); + sOpt.Replace((LPCTSTR) rem, ""); + } + sFiles += "|"; + } + supportedFiles += "|"; + supportedFiles += sFiles; + supportedFiles += "M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|All Files |*.*|"; + return supportedFiles; +} + +void Osmo4::OnOpenFile() +{ + CString sFiles = GetFileFilter(); + u32 nb_items; + + /*looks like there's a bug here, main filter isn't used correctly while the others are*/ + CFileDialog fd(TRUE,NULL,NULL, OFN_ALLOWMULTISELECT | OFN_HIDEREADONLY | OFN_FILEMUSTEXIST , sFiles); + fd.m_ofn.nMaxFile = 25000; + fd.m_ofn.lpstrFile = (char *) malloc(sizeof(char) * fd.m_ofn.nMaxFile); + fd.m_ofn.lpstrFile[0] = 0; + + if (fd.DoModal()!=IDOK) { + free(fd.m_ofn.lpstrFile); + return; + } + + CMainFrame *pFrame = (CMainFrame *) m_pMainWnd; + + nb_items = 0; + POSITION pos = fd.GetStartPosition(); + while (pos) { + CString file = fd.GetNextPathName(pos); + nb_items++; + } + /*if several items, act as playlist (replace playlist), otherwise as browser (lost all "next" context)*/ + if (nb_items==1) + pFrame->m_pPlayList->Truncate(); + else + pFrame->m_pPlayList->Clear(); + + pos = fd.GetStartPosition(); + while (pos) { + CString file = fd.GetNextPathName(pos); + pFrame->m_pPlayList->QueueURL(file); + } + free(fd.m_ofn.lpstrFile); + pFrame->m_pPlayList->RefreshList(); + pFrame->m_pPlayList->PlayNext(); +} + + +void Osmo4::Pause() +{ + if (!m_isopen) return; + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PLAYING) ? GF_STATE_PAUSED : GF_STATE_PLAYING); +} + +void Osmo4::OnMainPause() +{ + Pause(); +} + +void Osmo4::OnFileStep() +{ + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE); + ((CMainFrame *) m_pMainWnd)->m_wndToolBar.SetButtonInfo(5, ID_FILE_PLAY, TBBS_BUTTON, 3); +} +void Osmo4::OnUpdateFileStep(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(m_isopen && !m_reset); +} + +void Osmo4::PlayFromTime(u32 time) +{ + Bool do_pause; + if (start_mode==1) do_pause = 1; + else if (start_mode==2) do_pause = 0; + else do_pause = /*!m_AutoPlay*/0; + gf_term_play_from_time(m_term, time, do_pause); + m_reset = 0; +} + + +void Osmo4::OnFileReload() +{ + gf_term_disconnect(m_term); + m_pMainWnd->PostMessage(WM_OPENURL); +} + +void Osmo4::OnFileMigrate() +{ + const char *str = gf_cfg_get_key(m_user.config, "Network", "SessionMigration"); + if (!str || !strcmp(str, "no")) + gf_cfg_set_key(m_user.config, "Network", "SessionMigration", "yes"); + + m_navigate_url = ""; + m_pMainWnd->PostMessage(WM_NAVIGATE, NULL, NULL); +} + +void Osmo4::OnConfigReload() +{ + gf_term_set_option(m_term, GF_OPT_RELOAD_CONFIG, 1); +} + +void Osmo4::UpdatePlayButton(Bool force_play) +{ + if (!force_play && gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PLAYING) { + ((CMainFrame *) m_pMainWnd)->m_wndToolBar.SetButtonInfo(5, ID_FILE_PLAY, TBBS_BUTTON, 4); + } else { + ((CMainFrame *) m_pMainWnd)->m_wndToolBar.SetButtonInfo(5, ID_FILE_PLAY, TBBS_BUTTON, 3); + } +} + +void Osmo4::OnFilePlay() +{ + if (m_isopen) { + if (m_reset) { + m_reset = 0; + PlayFromTime(0); + ((CMainFrame *)m_pMainWnd)->SetProgTimer(1); + } else { + Pause(); + } + UpdatePlayButton(); + } else { + ((CMainFrame *) m_pMainWnd)->m_pPlayList->Play(); + } +} + +void Osmo4::OnUpdateFilePlay(CCmdUI* pCmdUI) +{ + if (m_isopen) { + pCmdUI->Enable(TRUE); + if (pCmdUI->m_nID==ID_FILE_PLAY) { + if (!m_isopen) { + pCmdUI->SetText("Play/Pause\tCtrl+P"); + } else if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PLAYING) { + pCmdUI->SetText("Pause\tCtrl+P"); + } else { + pCmdUI->SetText("Resume\tCtrl+P"); + } + } + } else { + pCmdUI->Enable(((CMainFrame *)m_pMainWnd)->m_pPlayList->HasValidEntries() ); + pCmdUI->SetText("Play\tCtrl+P"); + } +} + +void Osmo4::OnFileStop() +{ + CMainFrame *pFrame = (CMainFrame *) m_pMainWnd; + if (m_reset) return; + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PLAYING) Pause(); + m_reset = 1; + pFrame->m_Sliders.m_PosSlider.SetPos(0); + pFrame->SetProgTimer(0); + pFrame->m_wndToolBar.SetButtonInfo(5, ID_FILE_PLAY, TBBS_BUTTON, 3); + start_mode = 2; +} + +void Osmo4::OnUpdateFileStop(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(m_isopen); +} + +void Osmo4::OnSwitchRender() +{ + const char *opt = gf_cfg_get_key(m_user.config, "Compositor", "ForceOpenGL"); + Bool use_gl = (opt && !stricmp(opt, "yes")) ? 1 : 0; + gf_cfg_set_key(m_user.config, "Compositor", "ForceOpenGL", use_gl ? "no" : "yes"); + + gf_term_set_option(m_term, GF_OPT_USE_OPENGL, !use_gl); + + UpdateRenderSwitch(); +} + +void Osmo4::UpdateRenderSwitch() +{ + const char *opt = gf_cfg_get_key(m_user.config, "Compositor", "ForceOpenGL"); + if (opt && !stricmp(opt, "no")) + ((CMainFrame *) m_pMainWnd)->m_wndToolBar.SetButtonInfo(12, ID_SWITCH_RENDER, TBBS_BUTTON, 10); + else + ((CMainFrame *) m_pMainWnd)->m_wndToolBar.SetButtonInfo(12, ID_SWITCH_RENDER, TBBS_BUTTON, 9); +} diff --git a/applications/osmo4_w32/Osmo4.h b/applications/osmo4_w32/Osmo4.h new file mode 100644 index 0000000..a7a0583 --- /dev/null +++ b/applications/osmo4_w32/Osmo4.h @@ -0,0 +1,114 @@ +// GPAC.h : main header file for the GPAC application +// + +#if !defined(AFX_GPAC_H__8B06A368_E142_47E3_ABE7_0B459FC0E853__INCLUDED_) +#define AFX_GPAC_H__8B06A368_E142_47E3_ABE7_0B459FC0E853__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols + +///////////////////////////////////////////////////////////////////////////// +// Osmo4: +// See GPAC.cpp for the implementation of this class +// + + +/*GPAC terminal*/ +#include +/*GPAC terminal info (OD browsing)*/ +#include + +enum { + WM_SCENE_DONE = WM_USER + 1, + WM_NAVIGATE, + WM_SETSIZE, + WM_OPENURL, + WM_RESTARTURL, + WM_CONSOLEMSG, + WM_NEWINSTANCE, +}; + +const char *static_gpac_get_url(); + +class Osmo4 : public CWinApp +{ +public: + Osmo4(); + + GF_Terminal *m_term; + GF_User m_user; + + Bool m_isopen, m_reset; + u32 max_duration; + Bool can_seek; + u32 orig_width,orig_height, m_reconnect_time; + + CString m_navigate_url; + void Pause(); + void PlayFromTime(u32 time); + + void SetOptions(); + void UpdateRenderSwitch(); + void UpdatePlayButton(Bool force_play = 0); + + /*general options*/ + Bool m_Loop, m_LookForSubtitles, m_NoConsole, m_ViewXMTA, m_SingleInstance, m_NoMimeFetch; + u32 start_mode; + + CString GetFileFilter(); + + char szAppPath[GF_MAX_PATH]; + + FILE *m_logs; + u32 m_log_level, m_log_tools; + + HANDLE m_hMutex; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(Osmo4) + public: + virtual BOOL InitInstance(); + virtual int ExitInstance(); + //}}AFX_VIRTUAL + +// Implementation + +public: + //{{AFX_MSG(Osmo4) + afx_msg void OnOpenFile(); + afx_msg void OnMainPause(); + afx_msg void OnFileStep(); + afx_msg void OnOpenUrl(); + afx_msg void OnFileReload(); + afx_msg void OnFileMigrate(); + afx_msg void OnConfigReload(); + afx_msg void OnFilePlay(); + afx_msg void OnUpdateFilePlay(CCmdUI* pCmdUI); + afx_msg void OnUpdateFileStep(CCmdUI* pCmdUI); + afx_msg void OnFileStop(); + afx_msg void OnUpdateFileStop(CCmdUI* pCmdUI); + afx_msg void OnSwitchRender(); + afx_msg void OnAbout(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +inline Osmo4 *GetApp() { return (Osmo4 *)AfxGetApp(); } + + + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GPAC_H__8B06A368_E142_47E3_ABE7_0B459FC0E853__INCLUDED_) + \ No newline at end of file diff --git a/applications/osmo4_w32/Osmo4.rc b/applications/osmo4_w32/Osmo4.rc new file mode 100644 index 0000000..a3247d1 --- /dev/null +++ b/applications/osmo4_w32/Osmo4.rc @@ -0,0 +1,998 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINFRAME MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Open File\tCtrl+O", ID_FILEOPEN + MENUITEM "Open &URL\tCtrl+U", ID_OPEN_URL + MENUITEM SEPARATOR + MENUITEM "File Propert&ies\tCtrl+I", ID_FILE_PROP + POPUP "Streaming Cache" + BEGIN + MENUITEM "&Enable", ID_REC_ENABLE + MENUITEM "&Stop and Save", ID_REC_STOP + MENUITEM "&Abort", ID_REC_ABORT + END + MENUITEM SEPARATOR + MENUITEM "Copy", ID_FILE_COPY + MENUITEM "Paste", ID_FILE_PASTE + MENUITEM SEPARATOR + MENUITEM "Exit", ID_FILE_EXIT + END + POPUP "View" + BEGIN + POPUP "Viewpoint" + BEGIN + MENUITEM "", ID_VIEWPORT_EMPTY + END + POPUP "&Navigation" + BEGIN + MENUITEM "Headlight", ID_HEADLIGHT + MENUITEM SEPARATOR + MENUITEM "&None", ID_NAVIGATE_NONE + MENUITEM "&Walk", ID_NAVIGATE_WALK + MENUITEM "&Fly", ID_NAVIGATE_FLY + MENUITEM "&Examine", ID_NAVIGATE_EXAM + MENUITEM "&Pan", ID_NAVIGATE_PAN + MENUITEM "&Slide", ID_NAVIGATE_SLIDE + MENUITEM "&Orbit", ID_NAVIGATE_ORBIT + MENUITEM "&VR", ID_NAVIGATE_VR + MENUITEM "&Game", ID_NAVIGATE_GAME + MENUITEM SEPARATOR + POPUP "Collision" + BEGIN + MENUITEM "Off", ID_COLLIDE_NONE + MENUITEM "Regular", ID_COLLIDE_REG + MENUITEM "Displacement", ID_COLLIDE_DISP + END + MENUITEM "Gravity", ID_GRAVITY + MENUITEM SEPARATOR + MENUITEM "&Reset", ID_NAV_RESET + END + MENUITEM SEPARATOR + MENUITEM "&Fullscreen", ID_VIEW_FULLSCREEN + MENUITEM "Original &Aspect", ID_VIEW_ORIGINAL + POPUP "Aspect &Ratio" + BEGIN + MENUITEM "&Keep Original", ID_AR_KEEP + MENUITEM "&Fill Screen", ID_AR_FILL + MENUITEM "Ratio 4/3", ID_AR_43 + MENUITEM "Ratio 16/9", ID_AR_169 + END + MENUITEM SEPARATOR + MENUITEM "Resource Usage", ID_VIEW_CPU + MENUITEM SEPARATOR + MENUITEM "&Options", IDD_CONFIGURE + END + POPUP "Play" + BEGIN + POPUP "Stream &Selection" + BEGIN + POPUP "Audio" + BEGIN + MENUITEM "", ID_AUDIO_EMPTY + END + POPUP "Video" + BEGIN + MENUITEM "", ID_VIDEO_EMPTY + END + POPUP "Subtitle" + BEGIN + MENUITEM "", ID_SUBS_EMPTY + END + MENUITEM SEPARATOR + MENUITEM "Add Subtitle", ID_ADD_SUBTITLE + END + POPUP "&Chapters" + BEGIN + MENUITEM "", ID_SETCHAP_FIRST + END + MENUITEM SEPARATOR + MENUITEM "Playlist\tCtrl+L", ID_VIEW_PL, CHECKED + MENUITEM "&Loop Playlist", ID_PLAYLIST_LOOP + MENUITEM SEPARATOR + MENUITEM "Play/Pause\tCtrl+P", ID_FILE_PLAY + MENUITEM "Step-by-Step\tCtrl+S", ID_FILE_STEP + MENUITEM "Stop", ID_FILE_STOP + MENUITEM SEPARATOR + MENUITEM "Reload File\tCtrl+R", ID_FILE_RELOAD + MENUITEM SEPARATOR + MENUITEM "Clear History", ID_CLEAR_NAV + MENUITEM "Reload Config", ID_CONFIG_RELOAD + END + POPUP "?" + BEGIN + MENUITEM "Shortcut List", ID_SHORTCUTS + MENUITEM "Navigation Keys", ID_NAV_INFO + MENUITEM SEPARATOR + MENUITEM "&About ...", ID_H_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "I", ID_FILE_PROP, VIRTKEY, CONTROL, NOINVERT + "L", ID_VIEW_PL, VIRTKEY, CONTROL, NOINVERT + "M", ID_FILE_MIGRATE, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILEOPEN, VIRTKEY, CONTROL, NOINVERT + "P", ID_FILE_PLAY, VIRTKEY, CONTROL, NOINVERT + "R", ID_FILE_RELOAD, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_STEP, VIRTKEY, CONTROL, NOINVERT + "U", ID_OPEN_URL, VIRTKEY, CONTROL, NOINVERT + VK_NUMPAD1, ID_AR_KEEP, VIRTKEY, CONTROL, NOINVERT + VK_NUMPAD2, ID_AR_FILL, VIRTKEY, CONTROL, NOINVERT + VK_NUMPAD3, ID_AR_43, VIRTKEY, CONTROL, NOINVERT + VK_NUMPAD4, ID_AR_169, VIRTKEY, CONTROL, NOINVERT + VK_RETURN, ID_VIEW_FULLSCREEN, VIRTKEY, ALT, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOGEX 0, 0, 209, 137 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CLIENTEDGE +CAPTION "Osmo4 / GPAC version X.X.X" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,8,4,20,20 + CTEXT "Osmo4 Player - GPAC Multimedia Framework",IDC_STATIC,31, + 10,150,10,SS_NOPREFIX + CTEXT "(c) Jean Le Feuvre 2000-2005 - (c) ENST 2005-200X\nAll Rights Reserved", + IDC_STATIC,4,64,201,18 + CTEXT "This program is free software and may be distributed according to the terms of the GNU Lesser General Public License", + IDC_STATIC,4,26,200,18 + PUSHBUTTON "http://gpac.sourceforge.net",IDC_GOGPAC,43,47,121,13, + BS_FLAT,WS_EX_STATICEDGE + GROUPBOX "With Many Thanks To:",IDC_STATIC,3,82,203,53 + CTEXT "The FreeType Project\nMozilla SpiderMonkey (JavaScript support)\n\nZLIB, the PNG Group, the I.J.G.\nFFMPEG, FAAD, XVID, MAD", + IDC_STATIC,9,92,189,41 +END + +IDD_PASSWD DIALOG DISCARDABLE 0, 0, 134, 71 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Enter user name and password" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,4,56,50,12 + PUSHBUTTON "Cancel",IDCANCEL,80,56,50,12 + LTEXT "Static__________________________",IDC_TXT_SITE,25,4,105, + 8 + LTEXT "Login",IDC_STATIC,7,21,18,8 + LTEXT "Password",IDC_STATIC,7,37,32,8 + EDITTEXT IDC_EDIT_USER,47,18,55,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_PASSWORD,47,34,55,14,ES_PASSWORD | + ES_AUTOHSCROLL + LTEXT "Site",IDC_STATIC,7,4,13,8 +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "\0" + VALUE "FileDescription", "Osmo4-GPAC\0" + VALUE "FileVersion", "0.4.5\0" + VALUE "InternalName", "Osmo4\0" + VALUE "LegalCopyright", "Copyright (C) 2005\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "Osmo4.EXE\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Osmo4-GPAC\0" + VALUE "ProductVersion", "0.4.5\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + BOTTOMMARGIN, 136 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_MAINTOOLS TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_FILEOPEN + BUTTON ID_NAV_PREV + BUTTON ID_NAV_NEXT + BUTTON ID_FILE_PLAY + BUTTON ID_FILE_PLAY + BUTTON ID_FILE_STEP + BUTTON ID_FILE_STOP + BUTTON ID_FILE_PROPS + BUTTON IDD_CONFIGURE + BUTTON ID_SWITCH_RENDER + BUTTON ID_SWITCH_RENDER +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_MAINTOOLS BITMAP DISCARDABLE "res\\maintool.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDR_MAINFRAME "Osmo4" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + AFX_IDS_APP_TITLE "Osmo4" + AFX_IDS_IDLEMESSAGE "Ready" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_EXIT "Quit the application; prompts to save documents\nExit" + ID_H_ABOUT "Display program information, version number and copyright\nAbout" + ID_PLAYLIST_LOOP "Restarts playlist from begining when playlist is over" + ID_FILEOPEN "Opens local file" + ID_VIEW_ORIGINAL "restore Original Aspect of presentation" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCSIZE "Change the window size" + AFX_IDS_SCMOVE "Change the window position" + AFX_IDS_SCMINIMIZE "Reduce the window to an icon" + AFX_IDS_SCMAXIMIZE "Enlarge the window to full size" + AFX_IDS_SCNEXTWINDOW "Switch to the next document window" + AFX_IDS_SCPREVWINDOW "Switch to the previous document window" + AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCRESTORE "Restore the window to normal size" + AFX_IDS_SCTASKLIST "Activate Task List" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_STOP "Stops current presentation" + ID_SWITCH_RENDER "Switch between 2D and 3D renderers" + ID_COLLIDE_NONE "Turns collision detection off" + ID_COLLIDE_REG "Turns collision detection on" + ID_COLLIDE_DISP "Collision with camera displacement" + ID_HEADLIGHT "Turns headlight on/off" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_CLEAR_NAV "Clears navigation history" + ID_TIMER " " + ID_FPS " " + ID_VIEW_PL "View navigation history as a playlist" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_FULLSCREEN "Move to Full Screen mode (Esc to exit)" + ID_AR_KEEP "Keep Aspect Ratio of presentation" + ID_SHORTCUTS "List of available shortcuts" + ID_FILE_PROP "Show presentation properties" + ID_FILE_STEP "Step one frame into presentation" + IDD_CONFIGURE "Configure Player" + ID_VIEW_SCALABLE "Uses vectorial zooming when resizing the window" + ID_OPEN_URL "Open remote presentation" + ID_FILE_RELOAD "Reload current presentation" + ID_FILE_PLAY "Play/Pause presentation" + ID_NAVIGATE_NONE "Disable navigation" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_NAVIGATE_WALK "Turn walk navigation on" + ID_AR_FILL "Ignores Aspect Ratio and always fill screen" + ID_AR_43 "Forces Aspect Ratio of 4/3" + ID_AR_169 "Forces Aspect Ratio of 16/9" + ID_NAV_RESET "Restore last viewpoint" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_NAVIGATE_VR "QT-VR like navigation" + ID_REC_ENABLE "Enable recording of streaming data" + ID_REC_STOP "Stops recording and save to file" + ID_REC_ABORT "Stops recording and discard data" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_COPY "Copy selected text to clipboard" + ID_FILE_PASTE "Paste clipboard" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPENFILE DIALOG DISCARDABLE 0, 0, 301, 23 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Select Location" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_COMBOURL,3,5,273,67,CBS_DROPDOWN | CBS_AUTOHSCROLL | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDC_BUTGO,279,5,19,13 +END + +IDD_OPTIONS DIALOG DISCARDABLE 0, 0, 174, 106 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Osmo4 Options" +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Apply",IDC_SAVEOPT,147,2,26,12 + COMBOBOX IDC_SELECT,42,2,99,173,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Category",IDC_STATIC,7,4,29,8 +END + +IDD_OPT_GEN DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Loop At End",IDC_LOOP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,4,55,10 + CONTROL "Look for subtitles",IDC_LOOKFORSUB,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,42,69,10 + CONTROL "Disable console messages",IDC_NO_CONSOLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,17,99,10 + CONTROL "View Graph in XMT-A format",IDC_DUMP_XMT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,30,105,10 + CONTROL "Single Instance",IDC_SINGLE_INSTANCE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,54,65,10 +END + +IDD_OPT_RENDER DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Rendering Frame Rate",IDC_STATIC,5,4,72,8 + COMBOBOX IDC_BIFS_RATE,81,2,84,55,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Fast Rendering",IDC_FAST_RENDER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,6,49,64,10 + CONTROL "Force Scene Size",IDC_FORCE_SIZE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,96,49,72,10 + LTEXT "Anti-Aliasing Level",IDC_STATIC,7,20,58,8 + COMBOBOX IDC_AA_LIST,81,17,84,46,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Use 3D Renderer",IDC_USE_RENDER3D,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,6,63,71,10 + LTEXT "2D Rasterizer",IDC_STATIC,7,35,44,8 + COMBOBOX IDC_GD_LIST,81,33,84,44,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_DRAW_BOUNDS,109,60,56,44,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Bounds",IDC_STATIC,82,64,25,8 +END + +IDD_OPT_AUDIO DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_CONTROL | WS_CHILD | WS_THICKFRAME +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Spin1",IDC_SPIN_AUDIO,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ARROWKEYS,27,15,11,14 + EDITTEXT IDC_EDIT_AUDIO,5,15,19,14,ES_READONLY | ES_NUMBER + CONTROL "Force Audio Config",IDC_FORCE_AUDIO,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,6,3,73,10 + LTEXT "buffers",IDC_STATIC,43,18,23,8 + CONTROL "Spin1",IDC_SPIN_FPS,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ARROWKEYS,104,15,11,14 + EDITTEXT IDC_AUDIO_FPS,77,15,24,14,ES_NUMBER + LTEXT "Audio Driver",IDC_STATIC,7,63,40,8 + CONTROL "No Resynchronization",IDC_AUDIO_RESYNC,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,46,82,10 + COMBOBOX IDC_DRIVER_LIST,63,60,103,62,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Disable Notifications",IDC_AUDIO_NOTIFS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,89,47,79,10 + CONTROL "Disable Multichannel",IDC_AUDIO_MULTICH,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,34,81,10 + LTEXT "ms total length",IDC_STATIC,118,18,49,8 +END + +IDD_OPT_VIDEO DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Video Driver",IDC_STATIC,6,7,40,8 + COMBOBOX IDC_VIDEO_LIST,55,5,111,44,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Change video resolution in fullscreen",IDC_SWITCH_RES, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,29,131,10 + CONTROL "Use Hardware Video Memory in 2D mode",IDC_HWMEMORY, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,44,143,10 +END + +IDD_OPT_HTTP DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "...",IDC_BROWSE_CACHE,57,4,109,12 + CONTROL "Clean cache at exit",IDC_CLEAN_CACHE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,19,77,10 + CONTROL "Always redownload incomplete cached files", + IDC_RESTART_CACHE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 3,30,153,10 + LTEXT "Cache Directory",IDC_STATIC,5,5,52,8 + CONTROL "XML progressive load",IDC_SAX_PROGRESSIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,42,84,10 + EDITTEXT IDC_SAX_DELAY,142,41,22,12,ES_CENTER | ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "TimeSlice (ms)",IDC_STATIC,91,43,46,8 + EDITTEXT IDC_HTTP_PROXY,54,58,111,12,ES_AUTOHSCROLL + CONTROL "Use proxy",IDC_HTTP_USE_PROXY,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,59,47,10 +END + +IDD_OPT_FONT DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Font Engine",IDC_STATIC,6,11,39,8 + COMBOBOX IDC_FONT_LIST,60,8,105,44,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "...",IDC_BROWSE_FONT,2,40,164,12 + LTEXT "System Font Directory",IDC_STATIC,46,29,70,8 + COMBOBOX IDC_TEXTURE_MODE,101,58,64,44,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Text Texturing Mode",IDC_STATIC,7,60,69,8 +END + +IDD_OPT_SYSTEMS DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_LANG,75,4,92,58,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Prefered Language for stream selection",IDC_STATIC,3,3, + 61,17 + LTEXT "Decoder Threading",IDC_STATIC,4,28,62,8 + COMBOBOX IDC_DEC_THREAD,75,25,92,57,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Always draw late BIFS frames",IDC_BIFSDROP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,43,109,10 + CONTROL "Force Single Timeline",IDC_FORCE_DURATION,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,58,83,10 +END + +IDD_OPT_STREAM DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Default Port",IDC_STATIC,6,6,40,8 + COMBOBOX IDC_PORT,52,3,113,61,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "RTP over RTSP",IDC_RTSP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,19,68,10 + CONTROL "use RTP reordering",IDC_REORDER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,81,19,76,10 + LTEXT "milliseconds before control timeout",IDC_STATIC,38,34, + 108,8 + EDITTEXT IDC_TIMEOUT,3,32,30,12,ES_CENTER | ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "milliseconds of Media Buffering ",IDC_STATIC,38,48,100, + 8 + EDITTEXT IDC_BUFFER,3,47,30,12,ES_CENTER | ES_AUTOHSCROLL | + ES_NUMBER + EDITTEXT IDC_REBUFFER_LEN,83,60,30,12,ES_CENTER | ES_AUTOHSCROLL | + ES_NUMBER + CONTROL "Rebuffer if less than",IDC_REBUFFER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,63,79,10 + LTEXT "milliseconds",IDC_STATIC,117,63,41,8 +END + +IDD_PROPERTIES DIALOGEX 0, 0, 338, 150 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Presentation Properties" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "Tree1",IDC_ODTREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | + TVS_SHOWSELALWAYS | TVS_TRACKSELECT | TVS_SINGLEEXPAND | + WS_BORDER | WS_TABSTOP,2,2,120,114 + EDITTEXT IDC_ODINFO,123,17,213,130,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_NOHIDESEL | ES_OEMCONVERT | + ES_READONLY | WS_VSCROLL | WS_HSCROLL, + WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE + PUSHBUTTON "Get World Info",IDC_WORLD,2,118,119,13 + PUSHBUTTON "View Scene Graph",IDC_VIEWSG,2,134,119,13 + CONTROL "Tab1",IDC_VIEWSEL,"SysTabControl32",TCS_BUTTONS,124,2, + 208,14 +END + +IDD_OPT_DECODER DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Prefered Audio Module",-1,46,5,69,8 + COMBOBOX IDC_AUDEC_LIST,26,17,111,56,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Prefered Video Module",-1,48,40,69,8 + COMBOBOX IDC_VIDEC_LIST,25,52,113,55,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP +END + +IDD_OPT_RENDER2D DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Disable YUV Hardware",IDC_YUV,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,5,54,89,10 + LTEXT "Static",IDC_FORMAT_YUV,97,57,67,8 + CONTROL "Direct Rendering",IDC_DIRECTRENDER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,32,72,10 + CONTROL "Scalable Zoom",IDC_ZOOM_SCALABLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,10,77,10 +END + +IDD_OPT_RENDER3D DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Use OpenGL Outlines",IDC_RASTER_OUTLINE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,2,84,10 + CONTROL "Emulate power-of-two textures for video",IDC_EMUL_POW2, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,45,140,10 + CONTROL "Polygon Anti-Aliasing",IDC_DISABLE_POLY_AA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,90,2,78,10 + CONTROL "Disable rectangular texture extensions", + IDC_DISABLE_TX_RECT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,6,55,135,10 + CONTROL "Bitmap node uses direct pixel copy", + IDC_BITMAP_USE_PIXEL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,6,65,125,10 + LTEXT "Draw Normals",IDC_STATIC,4,17,45,8 + COMBOBOX IDC_DRAW_NORMALS,4,25,48,44,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Backface Cull",IDC_STATIC,57,17,55,8 + COMBOBOX IDC_BACK_CULL,57,25,47,44,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Draw Mode",IDC_STATIC,113,17,55,8 + COMBOBOX IDC_DRAW_MODE,111,25,53,44,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP +END + +IDD_SLIDERS DIALOG DISCARDABLE 0, 0, 218, 18 +STYLE WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Slider1",ID_SLIDER,"msctls_trackbar32",TBS_BOTH | + TBS_NOTICKS | WS_TABSTOP,0,3,185,12 + CONTROL "Slider1",ID_AUDIO_VOL,"msctls_trackbar32",TBS_BOTH | + TBS_NOTICKS | WS_BORDER | WS_TABSTOP,187,3,30,13 +END + +IDD_NAVBAR DIALOGEX 0, 0, 279, 15 +STYLE WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + COMBOBOX IDC_ADDRESS,29,1,130,196,CBS_DROPDOWN | CBS_AUTOHSCROLL | + WS_VSCROLL | WS_TABSTOP,WS_EX_ACCEPTFILES + LTEXT "Address",IDC_DUMTXT,1,4,26,8 +END + +IDD_PLAYLIST DIALOGEX 0, 0, 186, 54 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Osmo4 Playlist" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "List4",IDC_FILELIST,"SysListView32",LVS_REPORT | + WS_BORDER | WS_TABSTOP,1,0,182,51 +END + +IDD_OPT_MCACHE DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "...",IDC_BROWSE_MCACHE,41,2,126,12 + CONTROL "Overwrite existing files",IDC_MCACHE_OVERWRITE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,18,85,10 + CONTROL "Use filename",IDC_MCACHE_USENAME,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,31,57,10 + LTEXT "Record To",IDC_STATIC,4,4,35,8 + EDITTEXT IDC_BASEPRES,81,31,82,12,ES_CENTER | ES_AUTOHSCROLL | + ES_NUMBER +END + +IDD_OPT_FILETYPES DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_FILELIST,59,6,108,61,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Supported Files",IDC_STATIC,6,8,50,8 + LTEXT "Extension",IDC_FILES_EXT,7,25,154,8 + LTEXT "Mime Type",IDC_FILES_MIMES,7,37,156,8 + CONTROL "Associate with Osmo4",IDC_ASSOCIATE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,60,84,10 + LTEXT "Plugin",IDC_FILES_PLUG,8,49,154,8 +END + +IDD_OPT_LOGS DIALOG DISCARDABLE 0, 20, 169, 76 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Level",IDC_STATIC,6,7,18,8 + COMBOBOX IDC_LOG_LEVEL,30,4,64,74,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "core",IDC_TOOL_CORE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,23,30,10 + CONTROL "coding",IDC_TOOL_CODING,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,33,37,10 + CONTROL "container",IDC_TOOL_CONTAINER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,43,45,10 + CONTROL "network",IDC_TOOL_NET,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,53,41,10 + CONTROL "rtp",IDC_TOOL_RTP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 3,63,24,10 + CONTROL "author",IDC_TOOL_AUTHOR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,48,24,36,10 + CONTROL "sync",IDC_TOOL_SYNC,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,48,34,31,10 + CONTROL "codec",IDC_TOOL_CODEC,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,48,44,36,10 + CONTROL "parser",IDC_TOOL_PARSER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,48,54,35,10 + CONTROL "media",IDC_TOOL_MEDIA,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,48,64,35,10 + CONTROL "scene",IDC_TOOL_SCENE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,86,24,35,10 + CONTROL "script",IDC_TOOL_SCRIPT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,86,34,33,10 + CONTROL "compose",IDC_TOOL_COMPOSE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,86,44,45,10 + CONTROL "render",IDC_TOOL_RENDER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,86,54,36,10 + CONTROL "mmio",IDC_TOOL_MMIO,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,86,64,32,10 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_OPT_VIDEO, DIALOG + BEGIN + RIGHTMARGIN, 168 + END + + IDD_OPT_FONT, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 168 + BOTTOMMARGIN, 74 + END + + IDD_OPT_SYSTEMS, DIALOG + BEGIN + RIGHTMARGIN, 167 + TOPMARGIN, 1 + BOTTOMMARGIN, 75 + END + + IDD_OPT_STREAM, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 167 + TOPMARGIN, 1 + END + + IDD_PROPERTIES, DIALOG + BEGIN + RIGHTMARGIN, 335 + TOPMARGIN, 1 + END + + IDD_OPT_DECODER, DIALOG + BEGIN + RIGHTMARGIN, 168 + END + + IDD_OPT_RENDER3D, DIALOG + BEGIN + RIGHTMARGIN, 168 + END + + IDD_NAVBAR, DIALOG + BEGIN + RIGHTMARGIN, 167 + END + + IDD_OPT_FILETYPES, DIALOG + BEGIN + RIGHTMARGIN, 168 + END + + IDD_OPT_LOGS, DIALOG + BEGIN + RIGHTMARGIN, 168 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_PLAYLIST TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_PL_OPEN + BUTTON ID_PL_SAVE + BUTTON ID_PL_ADD_FILE + BUTTON ID_PL_REM_FILE + BUTTON ID_PL_UP + BUTTON ID_PL_DOWN + BUTTON ID_PL_SORT_FILE +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_PLAYLIST BITMAP DISCARDABLE "res\\playlist.bmp" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif //_WIN32\r\n" + "#include ""res\\Osmo4.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "res\\osmo4.ico" +IDI_PLAY ICON DISCARDABLE "res\\play.ico" +IDI_STOP ICON DISCARDABLE "res\\stop.ico" +IDI_PAUSE ICON DISCARDABLE "res\\pause.ico" +IDI_MESSAGE ICON DISCARDABLE "res\\message.ico" +IDI_ERR ICON DISCARDABLE "res\\error.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_OPT_RENDER DLGINIT +BEGIN + IDC_BIFS_RATE, 0x403, 4, 0 +0x2e35, 0x0030, + IDC_BIFS_RATE, 0x403, 4, 0 +0x2e37, 0x0035, + IDC_BIFS_RATE, 0x403, 5, 0 +0x3031, 0x302e, "\000" + IDC_BIFS_RATE, 0x403, 5, 0 +0x3231, 0x352e, "\000" + IDC_BIFS_RATE, 0x403, 5, 0 +0x3531, 0x302e, "\000" + IDC_BIFS_RATE, 0x403, 5, 0 +0x3432, 0x302e, "\000" + IDC_BIFS_RATE, 0x403, 5, 0 +0x3532, 0x302e, "\000" + IDC_BIFS_RATE, 0x403, 5, 0 +0x3033, 0x302e, "\000" + 0 +END + +IDD_OPT_SYSTEMS DLGINIT +BEGIN + IDC_LANG, 0x403, 8, 0 +0x6e45, 0x6c67, 0x7369, 0x0068, + IDC_LANG, 0x403, 7, 0 +0x7246, 0x6e65, 0x6863, "\000" + IDC_LANG, 0x403, 7, 0 +0x6547, 0x6d72, 0x6e61, "\000" + IDC_LANG, 0x403, 8, 0 +0x7449, 0x6c61, 0x6169, 0x006e, + IDC_LANG, 0x403, 8, 0 +0x7053, 0x6e61, 0x7369, 0x0068, + IDC_LANG, 0x403, 9, 0 +0x6843, 0x6e69, 0x6565, 0x6573, "\000" + IDC_LANG, 0x403, 10, 0 +0x614a, 0x6170, 0x656e, 0x7365, 0x0065, + IDC_DEC_THREAD, 0x403, 14, 0 +0x6953, 0x676e, 0x656c, 0x5420, 0x7268, 0x6165, 0x0064, + IDC_DEC_THREAD, 0x403, 13, 0 +0x754d, 0x6c74, 0x2069, 0x6854, 0x6572, 0x6461, "\000" + IDC_DEC_THREAD, 0x403, 5, 0 +0x7246, 0x6565, "\000" + 0 +END + +IDD_OPT_LOGS DLGINIT +BEGIN + IDC_LOG_LEVEL, 0x403, 9, 0 +0x6944, 0x6173, 0x6c62, 0x6465, "\000" + IDC_LOG_LEVEL, 0x403, 6, 0 +0x7245, 0x6f72, 0x0072, + IDC_LOG_LEVEL, 0x403, 8, 0 +0x6157, 0x6e72, 0x6e69, 0x0067, + IDC_LOG_LEVEL, 0x403, 5, 0 +0x6e49, 0x6f66, "\000" + IDC_LOG_LEVEL, 0x403, 6, 0 +0x6544, 0x7562, 0x0067, + 0 +END + +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +#include "res\Osmo4.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/applications/osmo4_w32/Playlist.cpp b/applications/osmo4_w32/Playlist.cpp new file mode 100644 index 0000000..b2d648f --- /dev/null +++ b/applications/osmo4_w32/Playlist.cpp @@ -0,0 +1,941 @@ +// Playlist.cpp : implementation file +// + +#include "stdafx.h" +#include "osmo4.h" +#include "MainFrm.h" +#include "OpenURL.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// Playlist dialog + +PLEntry::PLEntry(CString url, char *path) +{ + if (!path || strrchr(url, '\\') || strstr(url, "://")) { + m_url = strdup(url); + } else { + char szPath[MAX_PATH]; + strcpy(szPath, path); + strcat(szPath, url); + m_url = strdup(szPath); + } + char *str = (char *) strrchr(url, '\\'); + if (!str) str = (char *) strrchr(url, '/'); + if (str && strlen(str+1)) { + m_disp_name = strdup(str+1); + str = strrchr(m_disp_name, '.'); + if (str) str[0] = 0; + } else { + str = (char *) strstr(url, "://"); + if (str) { + str += 3; + m_disp_name = strdup(str); + } else { + m_disp_name = strdup(url); + str = strrchr(m_disp_name, '.'); + if (str) str[0] = 0; + } + } + m_duration = 0; + m_bIsDead = 0; + m_bIsPlaying = 0; + m_bIsSelected = 0; +} + +PLEntry::~PLEntry() +{ + if (m_url) free(m_url); + if (m_disp_name) free(m_disp_name); + +} + + +static char szCacheDir[MAX_PATH]; + +Playlist::Playlist() + : CDialog(Playlist::IDD, NULL) +{ + //{{AFX_DATA_INIT(Playlist) + //}}AFX_DATA_INIT + + m_entries = gf_list_new(); + m_cur_entry = -1; + m_all_dead_entries=-1; + GetCurrentDirectory(MAX_PATH, szCacheDir); +} + +Playlist::~Playlist() +{ + Clear(); + gf_list_del(m_entries); +} + +void Playlist::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(Playlist) + DDX_Control(pDX, IDC_FILELIST, m_FileList); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(Playlist, CDialog) + //{{AFX_MSG_MAP(Playlist) + ON_WM_SIZE() + ON_COMMAND(ID_PL_ADD_FILE, OnPlAddFile) + ON_COMMAND(ID_PL_REM_FILE, OnPlRemFile) + ON_COMMAND(ID_PL_UP, OnSelUp) + ON_COMMAND(ID_PL_DOWN, OnSelDown) + ON_COMMAND(ID_PL_SAVE, OnPlSave) + ON_WM_DROPFILES() + ON_WM_CLOSE() + ON_WM_DESTROY() + ON_COMMAND(ID_PL_REM_DEAD, OnPlRemDead) + ON_COMMAND(ID_PL_REM_ALL, OnPlRemAll) + ON_COMMAND(ID_PL_ADD_DIR, OnPlAddDir) + ON_COMMAND(ID_PL_ADD_DIR_REC, OnPlAddDirRec) + ON_COMMAND(ID_PL_ADD_URL, OnPlAddUrl) + ON_COMMAND(ID_PL_OPEN, OnPlOpen) + ON_COMMAND(ID_PL_PLAY, OnPlPlay) + ON_COMMAND(ID_PL_SEL_REV, OnReverseSelection) + ON_COMMAND(ID_PL_SORT_REV, OnReverseList) + ON_COMMAND(ID_PL_RANDOM, OnRandomize) + ON_COMMAND(ID_PL_SORT_TITLE, OnSortTitle) + ON_COMMAND(ID_PL_SORT_FILE, OnSortFile) + ON_COMMAND(ID_PL_SORT_DUR, OnSortDuration) + ON_NOTIFY(NM_RCLICK, IDC_FILELIST, OnRclickFilelist) + ON_NOTIFY(NM_DBLCLK, IDC_FILELIST, OnDblclkFilelist) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// Playlist message handlers + +BOOL Playlist::OnInitDialog() +{ + UINT buttonArray[50]; + TBBUTTONINFO bi; + u32 *ba; + CDialog::OnInitDialog(); + + + SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), TRUE); + SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), FALSE); + + if (!m_toolBar.CreateEx(this, WS_CHILD | CBRS_TOP | CBRS_FLYBY) || + !m_toolBar.LoadBitmap(IDR_PLAYLIST)) + { + TRACE0("Failed to create toolbar\n"); + return 0; + } + + ba = &buttonArray[0]; + *ba = ID_PL_OPEN; ba++; + *ba = ID_PL_SAVE; ba++; + *ba = ID_SEPARATOR; ba++; + *ba = ID_PL_ADD_FILE; ba++; + *ba = ID_PL_REM_FILE; ba++; + *ba = ID_SEPARATOR; ba++; + *ba = ID_PL_UP; ba++; + *ba = ID_PL_DOWN; ba++; + *ba = ID_SEPARATOR; ba++; + *ba = ID_PL_SORT_FILE; ba++; + m_toolBar.SetButtons(buttonArray, 9); + m_toolBar.SetButtonInfo(0, ID_PL_OPEN, TBBS_BUTTON, 0); + m_toolBar.SetButtonInfo(1, ID_PL_SAVE, TBBS_BUTTON, 1); + m_toolBar.SetButtonInfo(2, ID_SEPARATOR, TBBS_SEPARATOR, 0); + m_toolBar.SetButtonInfo(3, ID_PL_ADD_FILE, TBBS_DROPDOWN | TBBS_BUTTON, 2); + m_toolBar.SetButtonInfo(4, ID_PL_REM_FILE, TBBS_DROPDOWN | TBBS_BUTTON, 3); + m_toolBar.SetButtonInfo(5, ID_SEPARATOR, TBBS_SEPARATOR, 0); + m_toolBar.SetButtonInfo(6, ID_PL_UP, TBBS_BUTTON, 4); + m_toolBar.SetButtonInfo(7, ID_PL_DOWN, TBBS_BUTTON, 5); +// m_toolBar.SetButtonInfo(8, ID_SEPARATOR, TBBS_SEPARATOR, 0); + m_toolBar.SetButtonInfo(8, ID_PL_SORT_FILE, TBBS_DROPDOWN | TBBS_BUTTON, 6); + + CToolBarCtrl &ctrl = m_toolBar.GetToolBarCtrl(); + ctrl.SetStyle(TBSTYLE_FLAT | TBSTYLE_DROPDOWN); + ctrl.SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); + + memset(&bi, 0, sizeof(bi)); + bi.cbSize = sizeof(bi); + ctrl.GetButtonInfo(3, &bi); + bi.fsStyle |= TBSTYLE_DROPDOWN; + ctrl.SetButtonInfo(ID_PL_ADD_FILE, &bi); + + memset(&bi, 0, sizeof(bi)); + bi.cbSize = sizeof(bi); + ctrl.GetButtonInfo(4, &bi); + bi.fsStyle |= TBBS_DROPDOWN | TBSTYLE_DROPDOWN; + ctrl.SetButtonInfo(ID_PL_REM_FILE, &bi); + + memset(&bi, 0, sizeof(bi)); + bi.cbSize = sizeof(bi); + ctrl.GetButtonInfo(9, &bi); + bi.fsStyle |= TBSTYLE_DROPDOWN; + ctrl.SetButtonInfo(ID_PL_SORT_FILE, &bi); + + m_FileList.InsertColumn(0, "", LVCFMT_LEFT, 30, 0); + m_FileList.InsertColumn(1, "Title", LVCFMT_LEFT, 200, 1); + m_FileList.InsertColumn(2, "Duration", LVCFMT_LEFT, 200, 2); + + + m_toolBar.UpdateWindow(); + m_toolBar.ShowWindow(SW_SHOW); + + SetWindowPos(NULL, 0, 0, 400, 600, SWP_NOZORDER | SWP_NOMOVE); + + PostMessage(WM_NULL); + DragAcceptFiles(); + + return TRUE; +} + +void Playlist::OnSize(UINT nType, int cx, int cy) +{ + u32 tool_h; + CDialog::OnSize(nType, cx, cy); + RECT rc; + if (!m_toolBar.m_hWnd) return; + if (!m_FileList.m_hWnd) return; + + m_toolBar.GetClientRect(&rc); + tool_h = rc.bottom - rc.top; + m_toolBar.SetWindowPos(this, 0, 0, cx, tool_h, SWP_NOZORDER); + m_FileList.SetWindowPos(this, 0, tool_h, cx, cy-tool_h, SWP_NOZORDER); + + m_FileList.SetExtendedStyle(m_FileList.GetExtendedStyle() | LVS_EX_FULLROWSELECT); + + m_FileList.SetColumnWidth(0, 30); + m_FileList.SetColumnWidth(2, 60); + m_FileList.SetColumnWidth(1, cx-95); + +} + +void Playlist::OnDropFiles(HDROP hDropInfo) +{ + u32 i, count; + Osmo4 *app = GetApp(); + char fileName[MAX_PATH]; + count = ::DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0); + if (!count) return; + for (i=0; icode == TBN_DROPDOWN) { + RECT rc; + POINT pt; + CMenu *pPopup = new CMenu(); + pPopup->CreatePopupMenu(); + + m_toolBar.GetWindowRect(&rc); + pt.y = rc.bottom; + pt.x = rc.left; + + if ( ((LPNMTOOLBAR)lParam)->iItem == ID_PL_ADD_FILE) { + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_ADD_DIR, "Directory..."); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_ADD_DIR_REC, "Directory and subfolders..."); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_ADD_URL, "URL..."); + m_toolBar.GetToolBarCtrl().GetItemRect(3, &rc); + pt.x += rc.left; + } else if ( ((LPNMTOOLBAR)lParam)->iItem == ID_PL_REM_FILE) { + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_REM_ALL, "Clear"); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_REM_DEAD, "Remove dead entries"); + + m_toolBar.GetToolBarCtrl().GetItemRect(4, &rc); + pt.x += rc.left; + } else if ( ((LPNMTOOLBAR)lParam)->iItem == ID_PL_SORT_FILE) { + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_TITLE, "Sort Files by title"); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_FILE, "Sort Files by filename"); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_DUR, "Sort Files by duration"); + pPopup->AppendMenu(MF_SEPARATOR,0); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_REV, "Reverse Playlist"); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_RANDOM, "Shuffle Playlist"); + + m_toolBar.GetToolBarCtrl().GetItemRect(8, &rc); + pt.x += rc.left; + } + pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this); + delete pPopup; + + return FALSE; + } + return CDialog::OnNotify(wParam, lParam, pResult); +} + +void Playlist::Clear() +{ + m_FileList.DeleteAllItems(); + while (gf_list_count(m_entries)) { + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, 0); + gf_list_rem(m_entries, 0); + delete ple; + } + m_cur_entry = -1; +} + +void Playlist::ClearButPlaying() +{ + PLEntry *p; + if (m_cur_entry>=0) p = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (p) gf_list_rem(m_entries, m_cur_entry); + + Clear(); + if (p) { + gf_list_add(m_entries, p); + m_cur_entry = 0; + } + RefreshList(); +} + +void Playlist::UpdateEntry(u32 i) +{ + char szText[20]; + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, i); + if (i+1<10) sprintf(szText, "00%d", i+1); + else if (i+1<100) sprintf(szText, "0%d", i+1); + else sprintf(szText, "%d", i+1); + m_FileList.SetItem(i, 0, LVIF_TEXT, szText, 0, 0, 0, 0); + + CString str; + if (ple->m_bIsDead) { str = "!! DEAD !! "; str += ple->m_disp_name; } + else if (ple->m_bIsPlaying) { str = ">> "; str += ple->m_disp_name; str += " >>"; } + else str = ple->m_disp_name; + m_FileList.SetItem(i, 1, LVIF_TEXT, str, 0, 0, 0, 0); + + if (ple->m_duration) { + u32 h = (u32) (ple->m_duration / 3600); + u32 m = (u32) (ple->m_duration / 60) - h*60; + u32 s = (u32) (ple->m_duration) - h*3600 - m*60; + sprintf(szText, "%02d:%02d:%02d", h, m, s); + m_FileList.SetItem(i, 2, LVIF_TEXT, szText, 0, 0, 0, 0); + } else { + m_FileList.SetItem(i, 2, LVIF_TEXT, "Unknown", 0, 0, 0, 0); + } + +} + +void Playlist::RefreshList() +{ + u32 i, top_idx; + char szPath[GF_MAX_PATH]; + + top_idx = m_FileList.GetTopIndex(); + m_FileList.DeleteAllItems(); + + for (i=0; im_bIsPlaying) m_cur_entry = i; + + if (ple->m_bIsSelected) { + m_FileList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED); + ple->m_bIsSelected = 0; + } + } + + if (m_cur_entry >= (s32)gf_list_count(m_entries)) m_cur_entry = gf_list_count(m_entries); + else { + s32 last_idx = top_idx + m_FileList.GetCountPerPage(); + m_FileList.EnsureVisible(top_idx, 0); + if (gf_list_count(m_entries)<(u32) last_idx) last_idx = gf_list_count(m_entries); + m_FileList.EnsureVisible(last_idx, 1); + } + + + strcpy((char *) szPath, GetApp()->szAppPath); + strcat(szPath, "gpac_pl.m3u"); + Save(szPath, 1); +} + +void Playlist::OnPlAddFile() +{ + Osmo4 *app = GetApp(); + CString sFiles = app->GetFileFilter(); + + CFileDialog fd(TRUE,NULL,NULL, OFN_ALLOWMULTISELECT | OFN_HIDEREADONLY | OFN_FILEMUSTEXIST , sFiles); + fd.m_ofn.nMaxFile = 25000; + fd.m_ofn.lpstrFile = (char *) malloc(sizeof(char) * fd.m_ofn.nMaxFile); + fd.m_ofn.lpstrFile[0] = 0; + + if (fd.DoModal() == IDOK) { + s32 cur = m_FileList.GetItemCount(); + POSITION pos = fd.GetStartPosition(); + while (pos) { + QueueURL(fd.GetNextPathName(pos)); + } + } + free(fd.m_ofn.lpstrFile); + m_all_dead_entries=-1; + RefreshList(); +} + +void Playlist::OnClose() +{ + ShowWindow(SW_HIDE); +} + +void Playlist::OnPlRemFile() +{ + if (!m_FileList.GetSelectedCount()) return; + POSITION pos = m_FileList.GetFirstSelectedItemPosition(); + while (pos != NULL) { + int nItem = m_FileList.GetNextSelectedItem(pos); + PLEntry *ple = (PLEntry *) m_FileList.GetItemData(nItem); + gf_list_del_item(m_entries, ple); + delete ple; + } + m_all_dead_entries=-1; + RefreshList(); +} + +void Playlist::OnSelUp() +{ + s32 i; + if (!m_FileList.GetSelectedCount()) return; + POSITION pos = m_FileList.GetFirstSelectedItemPosition(); + int nItem = m_FileList.GetNextSelectedItem(pos); + if (nItem==0) return; + + pos = m_FileList.GetFirstSelectedItemPosition(); + while (pos != NULL) { + nItem = m_FileList.GetNextSelectedItem(pos); + PLEntry *ple = (PLEntry *) m_FileList.GetItemData(nItem); + i = gf_list_del_item(m_entries, ple); + assert(i>=1); + gf_list_insert(m_entries, ple, i-1); + ple->m_bIsSelected = 1; + } + RefreshList(); +} + +void Playlist::OnSelDown() +{ + s32 i, nItem; + if (!m_FileList.GetSelectedCount()) return; + POSITION pos = m_FileList.GetFirstSelectedItemPosition(); + while (pos != NULL) nItem = m_FileList.GetNextSelectedItem(pos); + + if ((u32) nItem + 1 == gf_list_count(m_entries)) return; + + pos = m_FileList.GetFirstSelectedItemPosition(); + while (pos != NULL) { + nItem = m_FileList.GetNextSelectedItem(pos); + PLEntry *ple = (PLEntry *) m_FileList.GetItemData(nItem); + i = gf_list_del_item(m_entries, ple); + gf_list_insert(m_entries, ple, i+1); + ple->m_bIsSelected = 1; + } + RefreshList(); +} + +void Playlist::OnPlRemAll() +{ + Clear(); + RefreshList(); + m_cur_entry = -1; +} + +void Playlist::OnPlRemDead() +{ + for (u32 i=0; im_bIsDead) continue; + gf_list_rem(m_entries, i); + i--; + delete ple; + } + m_all_dead_entries=-1; + RefreshList(); +} + + +static int CALLBACK LocCbck(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) +{ + char dir[MAX_PATH]; + if (uMsg == BFFM_INITIALIZED) { + strcpy(dir, szCacheDir); + SendMessage(hwnd, BFFM_SETSELECTION, TRUE,(LPARAM) dir); + } + return 0; +} +static Bool pl_enum_dir_item(void *cbck, char *item_name, char *item_path) +{ + Osmo4 *gpac = GetApp(); + Playlist *_this = (Playlist *)cbck; + + if (gf_term_is_supported_url(gpac->m_term, item_name, 0, 1)) { + _this->QueueURL(item_path); + } + return 0; +} + +static Bool pl_enum_dir_dirs(void *cbck, char *item_name, char *item_path) +{ + gf_enum_directory(item_path, 0, pl_enum_dir_item, cbck, NULL); + gf_enum_directory(item_path, 1, pl_enum_dir_dirs, cbck, NULL); + return 0; +} + + +void Playlist::AddDir(Bool do_recurse) +{ + BROWSEINFO brw; + LPMALLOC pMalloc; + LPITEMIDLIST ret; + char dir[MAX_PATH]; + + Bool res = 0; + if (NOERROR == ::SHGetMalloc(&pMalloc) ) { + memset(&brw, 0, sizeof(BROWSEINFO)); + brw.hwndOwner = this->GetSafeHwnd(); + brw.pszDisplayName = dir; + brw.lpszTitle = "Select Directory..."; + brw.ulFlags = 0L; + brw.lpfn = LocCbck; + + ret = SHBrowseForFolder(&brw); + if (ret != NULL) { + if (::SHGetPathFromIDList(ret, dir)) res = 1; + pMalloc->Free(ret); + } + pMalloc->Release(); + } + if (!res) return; + strcpy(szCacheDir, dir); + + gf_enum_directory(dir, 0, pl_enum_dir_item, this, NULL); + if (do_recurse) gf_enum_directory(dir, 1, pl_enum_dir_dirs, this, NULL); + m_all_dead_entries=-1; + RefreshList(); +} +void Playlist::OnPlAddDir() +{ + AddDir(0); +} +void Playlist::OnPlAddDirRec() +{ + AddDir(1); +} + +void Playlist::OnPlAddUrl() +{ + COpenUrl url; + if (url.DoModal() != IDOK) return; + PLEntry *ple = new PLEntry(url.m_url); + gf_list_add(m_entries, ple); + m_all_dead_entries=-1; + RefreshList(); +} + +void Playlist::OnPlSave() +{ + Bool save_m3u; + char szPath[GF_MAX_PATH]; + if (!gf_list_count(m_entries)) return; + CFileDialog fd(FALSE,NULL,NULL, OFN_OVERWRITEPROMPT, "M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|"); + if (fd.DoModal() != IDOK) return; + + strcpy(szPath, fd.GetPathName()); + strlwr(szPath); + save_m3u = (fd.m_ofn.nFilterIndex==1) ? 1 : 0; + if (save_m3u) { + if (!strstr(szPath, ".m3u")) { + strcpy(szPath, fd.GetPathName()); + strcat(szPath, ".m3u"); + } else { + strcpy(szPath, fd.GetPathName()); + } + } else { + if (!strstr(szPath, ".pls")) { + strcpy(szPath, fd.GetPathName()); + strcat(szPath, ".pls"); + } else { + strcpy(szPath, fd.GetPathName()); + } + } + Save(szPath, save_m3u); +} + +void Playlist::Save(char *szPath, Bool save_m3u) +{ + FILE *out = fopen(szPath, "wt"); + if (!save_m3u) + fprintf(out, "[playlist]\nNumberOfEntries=%d\n", gf_list_count(m_entries)); + + for (u32 i=0; im_url); + } else { + fprintf(out, "File%d=%s\n", i+1, ple->m_url); + fprintf(out, "Title%d=%s\n", i+1, ple->m_disp_name); + fprintf(out, "Length%d=%d\n", i+1, ple->m_duration ? ple->m_duration : -1); + + } + } + if (!save_m3u) fprintf(out, "Version=2\n"); + + fprintf(out, "\n"); + fclose(out); +} + +void Playlist::OnPlOpen() +{ + CFileDialog fd(TRUE,NULL,NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, "M3U & PLS Playlists|*.m3u;*.pls|M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|"); + if (fd.DoModal() != IDOK) return; + + Clear(); + OpenPlayList(fd.GetPathName()); + m_cur_entry = 0; + Play(); +} + +void Playlist::OpenPlayList(CString fileName) +{ + FILE *pl; + PLEntry *ple; + Bool load_m3u, go; + char szLine[GF_MAX_PATH], *sep; + char szPath[GF_MAX_PATH]; + + strcpy(szPath, fileName); + sep = strrchr(szPath, '\\'); + if (sep) sep[1] = 0; + else szPath[0] = 0; + + pl = fopen(fileName, "rt"); + if (!pl) return; + ple = NULL; + load_m3u = 1; + while (!feof(pl)) { + fgets(szLine, GF_MAX_PATH, pl); + go = 1; + while (go) { + switch (szLine[strlen(szLine)-1]) { + case '\n': + case '\r': + case ' ': + szLine[strlen(szLine)-1] = 0; + break; + default: + go = 0; + break; + } + } + if (!strlen(szLine)) continue; + if (!stricmp(szLine, "[playlist]")) { + load_m3u = 0; + } else if (load_m3u) { + ple = new PLEntry(szLine, szPath); + gf_list_add(m_entries, ple); + } else if (!strnicmp(szLine, "file", 4)) { + char *st = strchr(szLine, '='); + if (!st) ple = NULL; + else { + ple = new PLEntry(st + 1, szPath); + gf_list_add(m_entries, ple); + } + } else if (ple && !strnicmp(szLine, "Length", 6)) { + char *st = strchr(szLine, '='); + s32 d = atoi(st + 1); + if (d>0) ple->m_duration = d; + } else if (ple && !strnicmp(szLine, "Title", 5)) { + char *st = strchr(szLine, '='); + free(ple->m_disp_name); + ple->m_disp_name = strdup(st + 6); + } + } + fclose(pl); + m_all_dead_entries=-1; + m_cur_entry = -1; + RefreshList(); +} + + +void Playlist::OnRclickFilelist(NMHDR* pNMHDR, LRESULT* pResult) +{ + if (!m_FileList.GetItemCount()) return; + + CMenu *pPopup = new CMenu(); + pPopup->CreatePopupMenu(); + + if (m_FileList.GetSelectedCount()==1) { + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_PLAY, "Play"); + pPopup->AppendMenu(MF_SEPARATOR, 0, ""); + } + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SEL_REV, "Inverse Selection"); + if (m_FileList.GetSelectedCount()) pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_REM_FILE, "Remove File(s)"); + if (m_FileList.GetItemCount()>1) { + pPopup->AppendMenu(MF_SEPARATOR, 0, ""); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_TITLE, "Sort By Title"); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_FILE, "Sort By File Name"); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_DUR, "Sort By Duration"); + pPopup->AppendMenu(MF_SEPARATOR, 0, ""); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_REV, "Reverse List"); + pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_RANDOM, "Randomize"); + } + + POINT pt; + GetCursorPos(&pt); + pPopup->TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this); + delete pPopup; + + *pResult = 0; +} + +void Playlist::OnReverseSelection() +{ + u32 i; + POSITION pos = m_FileList.GetFirstSelectedItemPosition(); + while (pos != NULL) { + int nItem = m_FileList.GetNextSelectedItem(pos); + PLEntry *ple = (PLEntry *) m_FileList.GetItemData(nItem); + ple->m_bIsSelected = 1; + } + + for (i=0; im_bIsSelected = !ple->m_bIsSelected; + } + RefreshList(); +} + +void Playlist::OnReverseList() +{ + u32 count = gf_list_count(m_entries); + u32 hcount = count / 2; + count--; + for (u32 i=0; i1) { + u32 pos = gf_rand() % (gf_list_count(m_entries)-1); + PLEntry *ple = (PLEntry *)gf_list_get(m_entries, pos); + gf_list_rem(m_entries, pos); + gf_list_add(new_entries, ple); + } + PLEntry *ple = (PLEntry *)gf_list_get(m_entries, 0); + gf_list_rem(m_entries, 0); + gf_list_add(new_entries, ple); + + gf_list_del(m_entries); + m_entries = new_entries; + m_cur_entry = -1; + RefreshList(); +} + +void Playlist::Sort(u32 type) +{ + u32 i, j, smallest; + if (gf_list_count(m_entries)<=1) return; + + for (i=0; im_url, ple2->m_url); + break; + case 1: + test = stricmp(ple1->m_disp_name, ple2->m_disp_name); + break; + case 2: + test = ple1->m_duration - ple2->m_duration; + break; + } + if (test<0) smallest = j; + } + PLEntry *ple = (PLEntry *)gf_list_get(m_entries, smallest); + gf_list_rem(m_entries, smallest); + gf_list_insert(m_entries, ple, i); + } + m_cur_entry = -1; + RefreshList(); +} + +void Playlist::OnSortFile() { Sort(0); } +void Playlist::OnSortTitle() { Sort(1); } +void Playlist::OnSortDuration() { Sort(2); } + + +Bool Playlist::HasValidEntries() +{ + u32 nb_dead = 0; + if (m_all_dead_entries==-1) { + for (u32 i=0; im_bIsPlaying = 0; + if (ple->m_bIsDead) nb_dead ++; + } + m_all_dead_entries = (nb_dead==gf_list_count(m_entries)) ? 1 : 0; + } + if (m_all_dead_entries==1) return 0; + return 1; +} + +void Playlist::RefreshCurrent() +{ + if (m_cur_entry==-1) return; + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple && ple->m_bIsPlaying) { + ple->m_bIsPlaying = 0; + UpdateEntry(m_cur_entry); + } +} + +void Playlist::Play() +{ + PLEntry *ple; + + if (!HasValidEntries()) return; + + RefreshCurrent(); + if (m_cur_entry==-1) m_cur_entry = 0; + + if (m_cur_entry >= (s32)gf_list_count(m_entries)) { + if (!GetApp()->m_Loop) return; + m_cur_entry = 0; + } + + ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + assert(ple); + if (ple->m_bIsDead) { + m_cur_entry++; + Play(); + } else { + char szPLE[20]; + ple->m_bIsPlaying = 1; + UpdateEntry(m_cur_entry); + sprintf(szPLE, "%d", m_cur_entry); + gf_cfg_set_key(GetApp()->m_user.config, "General", "PLEntry", szPLE); + GetApp()->m_pMainWnd->PostMessage(WM_OPENURL); + } +} + +void Playlist::OnDblclkFilelist(NMHDR* pNMHDR, LRESULT* pResult) +{ + POSITION pos = m_FileList.GetFirstSelectedItemPosition(); + RefreshCurrent(); + m_cur_entry = m_FileList.GetNextSelectedItem(pos); + Play(); + *pResult = 0; +} + +void Playlist::OnPlPlay() +{ + POSITION pos = m_FileList.GetFirstSelectedItemPosition(); + + RefreshCurrent(); + m_cur_entry = m_FileList.GetNextSelectedItem(pos); + Play(); +} + +void Playlist::Truncate() +{ + while (m_cur_entry+1 < (s32)gf_list_count(m_entries)) { + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry+1); + gf_list_rem(m_entries, m_cur_entry+1); + delete ple; + } + RefreshList(); +} + + +void Playlist::QueueURL(CString filename) +{ + char *ext = (char *) strrchr(filename, '.'); + if (ext && (!stricmp(ext, ".m3u") || !stricmp(ext, ".pls")) ) { + OpenPlayList(filename); + } else { + PLEntry *ple = new PLEntry(filename); + gf_list_add(m_entries, ple); + } + m_all_dead_entries=-1; +} + +void Playlist::PlayNext() +{ + RefreshCurrent(); + if (1+m_cur_entry < (s32)gf_list_count(m_entries)) { + m_cur_entry++; + Play(); + } +} + +void Playlist::PlayPrev() +{ + RefreshCurrent(); + if (m_cur_entry>0) { + m_cur_entry--; + Play(); + } +} + +void Playlist::SetDead() +{ + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) { + ple->m_bIsDead = 1; + UpdateEntry(m_cur_entry); + m_all_dead_entries=-1; + if (ple->m_bIsPlaying) PlayNext(); + } +} +void Playlist::SetDuration(u32 duration) +{ + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) { + ple->m_duration = duration; + UpdateEntry(m_cur_entry); + } +} + +CString Playlist::GetDisplayName() +{ + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) return CString(ple->m_disp_name); + return CString(""); +} + +CString Playlist::GetURL() +{ + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) return CString(ple->m_url); + return CString(""); +} + diff --git a/applications/osmo4_w32/Playlist.h b/applications/osmo4_w32/Playlist.h new file mode 100644 index 0000000..bd899e2 --- /dev/null +++ b/applications/osmo4_w32/Playlist.h @@ -0,0 +1,120 @@ +#if !defined(AFX_PLAYLIST_H__EA74376A_83DF_435E_8484_A15BF5B77A32__INCLUDED_) +#define AFX_PLAYLIST_H__EA74376A_83DF_435E_8484_A15BF5B77A32__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// Playlist.h : header file +// + + +class PLEntry +{ +public: + PLEntry(CString url, char *path = NULL); + ~PLEntry(); + + char *m_url; + char *m_disp_name; + u32 m_duration; + + Bool m_bIsSelected; + Bool m_bIsDead; + Bool m_bIsPlaying; +}; + + +///////////////////////////////////////////////////////////////////////////// +// Playlist dialog + +class Playlist : public CDialog +{ +// Construction +public: + Playlist(); + virtual ~Playlist(); + + virtual Bool Create() { + /*use desktop window to enable playlist behind player*/ + return CDialog::Create(IDD_PLAYLIST, GetDesktopWindow()); + } + + CToolBar m_toolBar; + GF_List *m_entries; + + void Clear(); + void ClearButPlaying(); + void RefreshList(); + void AddDir(Bool do_recurse); + void Truncate(); + void SetDead(); + void SetDuration(u32 duration); + + void Play(); + void PlayNext(); + void PlayPrev(); + Bool HasValidEntries(); + CString GetDisplayName(); + CString GetURL(); + + void OpenPlayList(CString fileName); + + void QueueURL(CString filename); + s32 m_cur_entry; + +// Dialog Data + //{{AFX_DATA(Playlist) + enum { IDD = IDD_PLAYLIST}; + CListCtrl m_FileList; + //}}AFX_DATA + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(Playlist) + protected: + virtual void DoDataExchange(CDataExchange* pDX); + virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult); + //}}AFX_VIRTUAL + +// Implementation +protected: + s32 m_all_dead_entries; + void UpdateEntry(u32 idx); + void RefreshCurrent(); + void Sort(u32 type); + void Save(char *szPath, Bool save_m3u); + + // Generated message map functions + //{{AFX_MSG(Playlist) + virtual BOOL OnInitDialog() ; + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnDropFiles(HDROP hDropInfo); + afx_msg void OnPlAddFile(); + afx_msg void OnPlRemFile(); + afx_msg void OnSelUp(); + afx_msg void OnSelDown(); + afx_msg void OnPlSave(); + afx_msg void OnClose(); + afx_msg void OnPlRemDead(); + afx_msg void OnPlRemAll(); + afx_msg void OnPlAddDir(); + afx_msg void OnPlAddDirRec(); + afx_msg void OnPlAddUrl(); + afx_msg void OnPlOpen(); + afx_msg void OnReverseSelection(); + afx_msg void OnReverseList(); + afx_msg void OnRandomize(); + afx_msg void OnSortTitle(); + afx_msg void OnSortFile(); + afx_msg void OnSortDuration(); + afx_msg void OnPlPlay(); + afx_msg void OnRclickFilelist(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDblclkFilelist(NMHDR* pNMHDR, LRESULT* pResult); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PLAYLIST_H__EA74376A_83DF_435E_8484_A15BF5B77A32__INCLUDED_) diff --git a/applications/osmo4_w32/Sliders.cpp b/applications/osmo4_w32/Sliders.cpp new file mode 100644 index 0000000..07574c4 --- /dev/null +++ b/applications/osmo4_w32/Sliders.cpp @@ -0,0 +1,139 @@ +// Sliders.cpp : implementation file +// + +#include "stdafx.h" +#include "osmo4.h" +#include "Sliders.h" +#include + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// Sliders dialog + + +Sliders::Sliders(CWnd* pParent /*=NULL*/) + : CDialog(Sliders::IDD, pParent) +{ + //{{AFX_DATA_INIT(Sliders) + //}}AFX_DATA_INIT + + m_grabbed = 0; +} + + +void Sliders::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(Sliders) + DDX_Control(pDX, ID_AUDIO_VOL, m_AudioVol); + DDX_Control(pDX, ID_SLIDER, m_PosSlider); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(Sliders, CDialog) + //{{AFX_MSG_MAP(Sliders) + ON_WM_HSCROLL() + ON_WM_SIZE() + ON_WM_CLOSE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// Sliders message handlers + + +void Sliders::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + + Osmo4 *app = GetApp(); + if (pScrollBar->GetDlgCtrlID() == ID_SLIDER) { + switch (nSBCode) { + case TB_LINEUP: + case TB_LINEDOWN: + case TB_PAGEUP: + case TB_PAGEDOWN: + case TB_THUMBPOSITION: + case TB_THUMBTRACK: + case TB_TOP: + case TB_BOTTOM: + m_grabbed = 1; + break; + case TB_ENDTRACK: + if (!app->can_seek || !app->m_isopen) { + m_PosSlider.SetPos(0); + } else { + u32 seek_to = m_PosSlider.GetPos(); + app->PlayFromTime(seek_to); + } + m_grabbed = 0; + break; + } + } + if (pScrollBar->GetDlgCtrlID() == ID_AUDIO_VOL) { + u32 vol = m_AudioVol.GetPos(); + gf_term_set_option(app->m_term, GF_OPT_AUDIO_VOLUME, vol); + } + CDialog::OnHScroll(nSBCode, nPos, pScrollBar); +} + +void Sliders::OnSize(UINT nType, int cx, int cy) +{ + CDialog::OnSize(nType, cx, cy); + + if (!m_PosSlider.m_hWnd) return; + RECT rc, rc2; + + u32 tw = 40; + //m_PosSlider.GetClientRect(&rc); + //rc.right = rc.left + cx; + //m_PosSlider.SetWindowPos(this, rc.left, rc.top, rc.right, rc.bottom, SWP_NOZORDER | SWP_NOMOVE); + + m_PosSlider.GetClientRect(&rc); + rc.right = rc.left + cx - tw; + m_PosSlider.SetWindowPos(this, rc.left, rc.top, rc.right, rc.bottom, SWP_NOZORDER | SWP_NOMOVE); + + m_AudioVol.GetClientRect(&rc2); + rc2.top = rc2.bottom = cy/2; + rc2.top -= cy/3; + rc2.bottom += cy/3; + rc2.left = rc.right; + rc2.right = rc.right+tw; + m_AudioVol.MoveWindow(&rc2); + +} + +/*we sure don't want to close this window*/ +void Sliders::OnClose() +{ + u32 i = 0; + return; +} + +BOOL Sliders::PreTranslateMessage(MSG* pMsg) +{ + if (pMsg->message == WM_KEYDOWN) { + GetApp()->m_pMainWnd->SetFocus(); + GetApp()->m_pMainWnd->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam); + return TRUE; + } + return CDialog::PreTranslateMessage(pMsg); +} + + +BOOL Sliders::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_AudioVol.SetRange(0, 100); + return TRUE; +} + +void Sliders::SetVolume() +{ + m_AudioVol.SetPos(gf_term_get_option(GetApp()->m_term, GF_OPT_AUDIO_VOLUME)); +} diff --git a/applications/osmo4_w32/Sliders.h b/applications/osmo4_w32/Sliders.h new file mode 100644 index 0000000..ce97047 --- /dev/null +++ b/applications/osmo4_w32/Sliders.h @@ -0,0 +1,54 @@ +#if !defined(AFX_SLIDERS_H__3542255E_1376_4FB7_91E7_B4841BB4F173__INCLUDED_) +#define AFX_SLIDERS_H__3542255E_1376_4FB7_91E7_B4841BB4F173__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// Sliders.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// Sliders dialog + +class Sliders : public CDialog +{ +// Construction +public: + Sliders(CWnd* pParent = NULL); // standard constructor + + void SetVolume(); + Bool m_grabbed; + +// Dialog Data + //{{AFX_DATA(Sliders) + enum { IDD = IDD_SLIDERS }; + CSliderCtrl m_AudioVol; + CSliderCtrl m_PosSlider; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(Sliders) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(Sliders) + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnClose(); + virtual BOOL OnInitDialog(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_SLIDERS_H__3542255E_1376_4FB7_91E7_B4841BB4F173__INCLUDED_) diff --git a/applications/osmo4_w32/StdAfx.cpp b/applications/osmo4_w32/StdAfx.cpp new file mode 100644 index 0000000..cdfba52 --- /dev/null +++ b/applications/osmo4_w32/StdAfx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// GPAC.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + + + diff --git a/applications/osmo4_w32/StdAfx.h b/applications/osmo4_w32/StdAfx.h new file mode 100644 index 0000000..2209b7e --- /dev/null +++ b/applications/osmo4_w32/StdAfx.h @@ -0,0 +1,26 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__1EEB44C5_1152_4872_8CA7_BD2994085EDC__INCLUDED_) +#define AFX_STDAFX_H__1EEB44C5_1152_4872_8CA7_BD2994085EDC__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__1EEB44C5_1152_4872_8CA7_BD2994085EDC__INCLUDED_) diff --git a/applications/osmo4_w32/res/Osmo4.rc2 b/applications/osmo4_w32/res/Osmo4.rc2 new file mode 100644 index 0000000..b593608 --- /dev/null +++ b/applications/osmo4_w32/res/Osmo4.rc2 @@ -0,0 +1,13 @@ +// +// Osmo4.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/applications/osmo4_w32/res/error.ico b/applications/osmo4_w32/res/error.ico new file mode 100644 index 0000000000000000000000000000000000000000..8a2d46cccd645a3ffde4476766fb4cbef301a5d0 GIT binary patch literal 766 zcmdUtF$w}P5JlgrAX{l=Yf5jSH#0}D4SN#VX1$4EV`-;_?D%6KZoopz{gXeF$x8x( zK&5bQNz`=$E7gtcd>X4KlqV^cV zA@^xRZXxmyhPr9P07KIbu{M+#N?e>BS))Jjv(8NI#@rq(?8(}Cw)#PPGp{R7@@B)Lq#-3yMy^BuM0y*^7Yi_1c_6&Q3+(NuuRb$JM zr4Lzh`u_3#Brczr-!OhKUNIiz5i@Lc-(PN~g9siu9718nQl@Ew=El_@xn8-#z>g+O zcDDeuiO#|4@5Z%2VhIkNdS4juVXpS@)&z46Y+B{`CA4Euf&=|h$^@sBj!7-qXsU{I zm3rfcrFwh?wnHx53a(rQ*2Ot|H% z*_bZ4eAaUz+wMf}DZq^9e&ezLEJfoMatpY~7u5h%HPat;fNFk@4Q_xvo_@8y?g{W8 zZmqA$$m2FQ#6jqPfT%;=~Cqag%4_rLWm5U(#F$%cL&@(ixccnFUEc*f-Jy$`LBXQF{CM3c*} z!DZ+DgSDsyI<%{zJb10H3Su}J^` literal 0 HcmV?d00001 diff --git a/applications/osmo4_w32/res/osmo4.ico b/applications/osmo4_w32/res/osmo4.ico new file mode 100644 index 0000000000000000000000000000000000000000..36ff66711771065305f263aee09bda915091c6b0 GIT binary patch literal 15086 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 Ih+0ATC&$&)D*ylh literal 0 HcmV?d00001 diff --git a/applications/osmo4_w32/res/pause.ico b/applications/osmo4_w32/res/pause.ico new file mode 100644 index 0000000000000000000000000000000000000000..7fab3531842d5177d069e52f04a883c6ade58364 GIT binary patch literal 1078 zcmd6mO-{ow5QSe_q_VJRj*u1VE#OwL!#g${$_MZvy5tC5Ak6n1tOnC85E5dNnK%Ek zl4ohqM$xvE*RYRofzCqvtYxZM3%n3 zWM?ATth0@c<6x5ajoCeR7$&^$%uean!95~g1rWCUrDG~?u!y;zTg9Ei$6$c@se-qZLz-L<&*)1%A`S8Cs%)q*w9&$eJa*kUjr7QZy*hoLko|js=np^5ruYFcT zbFRrB(K_dje_bLo4#P|r!;1UcIiEYF9=LveD9s4zw%C+g)E jRZog~t9{rHXEJhsJqR6VKBxLtIBc64?7;cPAk2i57?sr{ zYpkusoV)qX)uiA9juAU$bq_zT6V~^Q5VU3Nrf{F?mldvA@y+x63Do>KSN@~I_It2n>1-Ww=r#f?viR8qwyj-s@H{9KfVQ-ZVl L^ZljK^e_DY4LCYJ literal 0 HcmV?d00001 diff --git a/applications/osmo4_w32/res/stop.ico b/applications/osmo4_w32/res/stop.ico new file mode 100644 index 0000000000000000000000000000000000000000..52e1a3b6ac8b5d228d1b3e16570ebda9cedbccf1 GIT binary patch literal 1078 zcmd6mF>b;@5JkTs5+GTDBe;sVMRF_X*pebeT5jV5>>(&~1d1q|_t&g37B136`DS+J z&z~8#n3XEc)HE9zpEKzNzu9zrTS?DzX-lrb-mpqfoKm3nF#;k=2%Y03RNJfi7E-g+GuG`i1|JP*n| c56;fv4VSfa=3TeEZ(Bf`vQp!E_xYLr0IvM^sQ>@~ literal 0 HcmV?d00001 diff --git a/applications/osmo4_w32/resource.h b/applications/osmo4_w32/resource.h new file mode 100644 index 0000000..9330426 --- /dev/null +++ b/applications/osmo4_w32/resource.h @@ -0,0 +1,322 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Osmo4.rc +// +#define IDD_ABOUTBOX 100 +#define IDR_MAINFRAME 128 +#define IDR_GPACTYPE 129 +#define IDD_CONTROL 132 +#define IDI_PLAY 141 +#define IDI_STOP 142 +#define IDI_PAUSE 143 +#define IDI_MESSAGE 144 +#define IDI_ERR 145 +#define IDD_OPTIONS 152 +#define IDD_OPT_GEN 154 +#define IDD_OPT_RENDER 155 +#define IDD_OPT_AUDIO 157 +#define IDD_OPT_VIDEO 158 +#define IDD_OPT_HTTP 159 +#define IDD_OPT_FONT 161 +#define IDD_OPT_SYSTEMS 162 +#define IDD_OPT_STREAM 164 +#define IDD_PROPERTIES 166 +#define IDD_OPT_DECODER 167 +#define IDD_OPT_RENDER2D 168 +#define IDD_OPT_RENDER3D 169 +#define IDR_MAINTOOLS 170 +#define ID_MAINSLIDER 171 +#define IDD_SLIDERS 173 +#define IDD_NAVBAR 176 +#define IDD_PLAYLIST 177 +#define IDR_PLAYLIST 178 +#define IDD_OPT_FILETYPES 179 +#define IDR_MENUPL 182 +#define IDD_PASSWD 188 +#define IDC_FILES_MIMES 1000 +#define IDC_FILES_PLUG 1001 +#define ID_AUDIO_VOL 1002 +#define IDC_TXT_SITE 1003 +#define IDC_EDIT_USER 1004 +#define IDC_EDIT_PASSWORD 1005 +#define IDC_PLAY 1006 +#define IDC_STOP 1007 +#define IDC_COMBOURL 1008 +#define IDC_BROWSE 1009 +#define IDC_BUTGO 1010 +#define IDC_SPIN_OPT 1011 +#define IDC_OPT_SET_NAME 1012 +#define IDC_LANG 1013 +#define IDC_LOOP 1014 +#define IDC_AUTOSTART 1015 +#define IDC_FILEASSOC 1016 +#define IDC_NO_CONSOLE 1017 +#define IDC_BIFS_RATE 1018 +#define IDC_DEC_THREAD 1019 +#define IDC_DIRECTRENDER 1020 +#define IDC_HWMEMORY 1021 +#define IDC_BIFSDROP 1023 +#define IDC_SPIN_AUDIO 1024 +#define IDC_EDIT_AUDIO 1025 +#define IDC_FORCE_AUDIO 1026 +#define IDC_SPIN_FPS 1027 +#define IDC_AUDIO_FPS 1028 +#define IDC_AUDIO_MULTICH 1029 +#define IDC_GD_LIST 1031 +#define IDC_FAST_RENDER 1032 +#define IDC_FORCE_DURATION 1033 +#define IDC_YUV 1034 +#define IDC_AUDIO_RESYNC 1035 +#define IDC_AUDIO_NOTIFS 1036 +#define IDC_STOPATEND 1037 +#define IDC_CLEAN_CACHE 1038 +#define IDC_RESTART_CACHE 1039 +#define IDC_NOTIFY_PROG 1040 +#define IDC_BROWSE_CACHE 1041 +#define IDC_LOOKFORSUB 1042 +#define IDD_OPT_MCACHE 1043 +#define IDC_DRIVER_LIST 1044 +#define IDC_AA_LIST 1045 +#define IDC_ZOOM_SCALABLE 1046 +#define IDD_OPENFILE 1047 +#define IDC_SINGLE_INSTANCE 1048 +#define IDC_VIDEO_LIST 1049 +#define IDC_FONT_LIST 1050 +#define IDC_BROWSE_FONT 1051 +#define IDC_USE_FONT 1052 +#define IDC_SAVEOPT 1053 +#define IDC_PORT 1054 +#define IDC_RTSP 1055 +#define IDC_TIMEOUT 1056 +#define IDC_BUFFER 1057 +#define IDC_REBUFFER_LEN 1058 +#define IDC_REBUFFER 1059 +#define IDC_ODTREE 1060 +#define IDC_VIEWSG 1061 +#define IDC_FORCE_SIZE 1062 +#define IDC_USE_RENDER3D 1063 +#define IDC_ODINFO 1064 +#define IDC_WORLD 1065 +#define IDC_GOGPAC 1066 +#define IDC_GOOSMO4 1067 +#define IDC_DUMP_XMT 1068 +#define IDC_OBJECT_TIME 1069 +#define IDC_FORMAT_YUV 1070 +#define IDC_DRAW_BOUNDS 1071 +#define IDC_REORDER 1072 +#define IDC_TEXTURE_MODE 1073 +#define IDC_SWITCH_RES 1074 +#define IDC_AUDEC_LIST 1075 +#define IDC_VIDEC_LIST 1076 +#define IDC_ASSOCIATE 1077 +#define IDC_RASTER_OUTLINE 1078 +#define IDC_EMUL_POW2 1079 +#define IDC_DISABLE_POLY_AA 1080 +#define IDC_WIRE_NONE 1081 +#define IDC_WIRE_ONLY 1082 +#define IDC_WIRE_BOTH 1083 +#define IDC_DISABLE_TX_RECT 1084 +#define IDC_BITMAP_USE_PIXEL 1085 +#define IDC_TEXTURE_TEXT 1086 +#define IDC_SLIDER 1087 +#define ID_SLIDER 1088 +#define IDC_SELECT 1089 +#define IDC_NO_BACKCULL 1090 +#define IDC_FILES_EXT 1091 +#define IDC_VIEWSEL 1094 +#define IDC_ADDRESS 1096 +#define IDC_DUMTXT 1097 +#define IDC_FILELIST 1109 +#define IDC_BROWSE_MCACHE 1110 +#define IDC_MCACHE_OVERWRITE 1111 +#define IDC_MCACHE_USENAME 1112 +#define IDC_BASEPRES 1113 +#define ID_FILE_EXIT 1114 +#define ID_H_ABOUT 1115 +#define ID_PLAYLIST_LOOP 1116 +#define ID_FILEOPEN 1117 +#define ID_VIEW_CONTROL 1118 +#define ID_VIEW_ORIGINAL 1119 +#define ID_VIEW_FULLSCREEN 1120 +#define ID_AR_KEEP 1121 +#define IDC_DRAW_NORMALS 1122 +#define IDC_BACK_CULL 1123 +#define IDC_DRAW_MODE 1124 +#define ID_SHORTCUTS 1125 +#define ID_FILE_RESTART 1126 +#define ID_OPT_QUALITY 1127 +#define ID_FILE_PROP 1128 +#define ID_FILE_STEP 1129 +#define IDD_CONFIGURE 1130 +#define ID_VIEW_SCALABLE 1131 +#define ID_OPEN_URL 1132 +#define ID_FILE_RELOAD 1133 +#define ID_FILE_PLAY 1134 +#define ID_NAVIGATE_NONE 1135 +#define ID_NAVIGATE_WALK 1136 +#define ID_NAVIGATE_FLY 1137 +#define ID_NAVIGATE_EXAM 1138 +#define ID_NAVIGATE_PAN 1139 +#define ID_NAVIGATE_SLIDE 1140 +#define ID_NAVIGATE_GAME 1141 +#define ID_AR_FILL 1142 +#define ID_AR_43 1143 +#define ID_AR_169 1144 +#define ID_FILE_MIGRATE 1145 +#define ID_NAV_RESET 1151 +#define ID_FILE_STOP 1152 +#define ID_FILE_PREV 1155 +#define ID_FILE_NEXT 1156 +#define ID_FILE_PROPS 1157 +#define ID_SWITCH_RENDER 1158 +#define ID_RELOAD_TERMINAL 1159 +#define ID_VIEW_PLAYLIST 1160 +#define ID_NAVIGATE_ORBIT 1161 +#define ID_COLLIDE_NONE 1162 +#define ID_COLLIDE_REG 1163 +#define ID_COLLIDE_DISP 1164 +#define ID_GRAVITY 1165 +#define ID_HEADLIGHT 1166 +#define ID_NAV_INFO 1167 +#define ID_NAV_PREV 1168 +#define ID_NAV_NEXT 1169 +#define ID_CLEAR_NAV 1170 +#define ID_TIMER 1171 +#define ID_FPS 1172 +#define ID_VIEWPORT_EMPTY 1173 +#define ID_PL_REM_ALL 1174 +#define ID_PL_REM_DEAD 1175 +#define ID_PL_ADD_DIR_REC 1176 +#define ID_PL_ADD_FILE 1177 +#define ID_PL_REM_FILE 1178 +#define ID_PL_OPEN 1179 +#define ID_PL_SAVE 1180 +#define ID_PL_UP 1181 +#define ID_PL_DOWN 1182 +#define ID_VIEW_PL 1183 +#define ID_PL_ADD_DIR 1184 +#define ID_PL_ADD_URL 1185 +#define ID_PL_PLAY 1186 +#define ID_PL_SEL_REV 1187 +#define ID_PL_SORT_TITLE 1188 +#define ID_PL_SORT_FILE 1189 +#define ID_PL_SORT_DUR 1190 +#define ID_PL_SORT_REV 1191 +#define ID_PL_RANDOM 1192 +#define ID_ADD_SUBTITLE 1193 +#define ID_NAVIGATE_VR 1194 +#define ID_REC_ENABLE 1195 +#define ID_REC_STOP 1196 +#define ID_REC_ABORT 1197 +#define ID_AUDIO_EMPTY 1198 +#define ID_VIDEO_EMPTY 1199 +#define ID_SUBS_EMPTY 1200 +#define ID_VIEW_CPU 1201 +#define IDC_SAX_PROGRESSIVE 1202 +#define IDC_SAX_DELAY 1203 +#define IDC_HTTP_PROXY 1204 +#define IDC_HTTP_USE_PROXY 1205 +#define IDC_LOG_LEVEL 1210 +#define IDC_TOOL_CORE 1211 +#define IDC_TOOL_CODING 1212 +#define IDC_TOOL_CONTAINER 1213 +#define IDC_TOOL_NET 1214 +#define IDC_TOOL_RTP 1215 +#define IDC_TOOL_AUTHOR 1216 +#define IDC_TOOL_CODEC 1217 +#define IDC_TOOL_PARSER 1218 +#define IDC_TOOL_MEDIA 1219 +#define IDC_TOOL_SCENE 1220 +#define IDC_TOOL_SCRIPT 1221 +#define IDC_TOOL_COMPOSE 1222 +#define IDC_TOOL_RENDER 1223 +#define IDC_TOOL_MMIO 1224 +#define IDC_TOOL_SYNC 1225 +#define IDD_OPT_LOGS 1226 +#define ID_VP_0 1300 +#define ID_VP_1 1301 +#define ID_VP_2 1302 +#define ID_VP_3 1303 +#define ID_VP_4 1304 +#define ID_VP_5 1305 +#define ID_VP_6 1306 +#define ID_VP_7 1307 +#define ID_VP_8 1308 +#define ID_VP_9 1309 +#define ID_VP_10 1310 +#define ID_VP_11 1311 +#define ID_VP_12 1312 +#define ID_VP_13 1313 +#define ID_VP_14 1314 +#define ID_VP_15 1315 +#define ID_VP_16 1316 +#define ID_VP_17 1317 +#define ID_VP_18 1318 +#define ID_VP_19 1319 +#define ID_NAV_PREV_0 1320 +#define ID_NAV_PREV_1 1321 +#define ID_NAV_PREV_2 1322 +#define ID_NAV_PREV_3 1323 +#define ID_NAV_PREV_4 1324 +#define ID_NAV_PREV_5 1325 +#define ID_NAV_PREV_6 1326 +#define ID_NAV_PREV_7 1327 +#define ID_NAV_PREV_8 1328 +#define ID_NAV_PREV_9 1329 +#define ID_NAV_NEXT_0 1330 +#define ID_NAV_NEXT_1 1331 +#define ID_NAV_NEXT_2 1332 +#define ID_NAV_NEXT_3 1333 +#define ID_NAV_NEXT_4 1334 +#define ID_NAV_NEXT_5 1335 +#define ID_NAV_NEXT_6 1336 +#define ID_NAV_NEXT_7 1337 +#define ID_NAV_NEXT_8 1338 +#define ID_NAV_NEXT_9 1339 +#define ID_SELOBJ_0 1340 +#define ID_SELOBJ_1 1341 +#define ID_SELOBJ_2 1342 +#define ID_SELOBJ_3 1343 +#define ID_SELOBJ_4 1344 +#define ID_SELOBJ_5 1345 +#define ID_SELOBJ_6 1346 +#define ID_SELOBJ_7 1347 +#define ID_SELOBJ_8 1348 +#define ID_SELOBJ_9 1349 +#define ID_SELOBJ_10 1350 +#define ID_SELOBJ_11 1351 +#define ID_SELOBJ_12 1352 +#define ID_SELOBJ_13 1353 +#define ID_SELOBJ_14 1354 +#define ID_SELOBJ_15 1355 +#define ID_SELOBJ_16 1356 +#define ID_SELOBJ_17 1357 +#define ID_SELOBJ_18 1358 +#define ID_SELOBJ_19 1359 +#define ID_SELOBJ_20 1360 +#define ID_SELOBJ_21 1361 +#define ID_SELOBJ_22 1362 +#define ID_SELOBJ_23 1363 +#define ID_SELOBJ_24 1364 +#define ID_SELOBJ_25 1365 +#define ID_SELOBJ_26 1366 +#define ID_SELOBJ_27 1367 +#define ID_SELOBJ_28 1368 +#define ID_SELOBJ_29 1369 +#define ID_SETCHAP_FIRST 2000 +#define ID_SETCHAP_LAST 2200 +#define ID_FILE_COPY 32961 +#define ID_FILE_PASTE 32962 +#define ID_CONFIG_RELOAD 32963 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 189 +#define _APS_NEXT_COMMAND_VALUE 32964 +#define _APS_NEXT_CONTROL_VALUE 1131 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/applications/osmo4_wce/MainFrm.cpp b/applications/osmo4_wce/MainFrm.cpp new file mode 100644 index 0000000..27a5621 --- /dev/null +++ b/applications/osmo4_wce/MainFrm.cpp @@ -0,0 +1,636 @@ +// MainFrm.cpp : implementation of the CMainFrame class +// + +#include "stdafx.h" +#include "Osmo4.h" + +#include +#include + +#include "MainFrm.h" +#include + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + + + +CChildView::CChildView() +{ +} + +CChildView::~CChildView() +{ + /*since the wndproc is overwritten by the terminal, we detach the handle otherwise we get a nice assertion + failure from windows*/ + HWND hWnd = Detach(); + ::PostMessage(hWnd, WM_QUIT, 0, 0); +} + + +BEGIN_MESSAGE_MAP(CChildView,CWnd ) + //{{AFX_MSG_MAP(CChildView) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CChildView message handlers + +BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) +{ + if (!CWnd::PreCreateWindow(cs)) + return FALSE; + + cs.style &= ~WS_BORDER; + cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, + NULL, HBRUSH(COLOR_WINDOW+1), NULL); + + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame + +IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd) + +BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) + //{{AFX_MSG_MAP(CMainFrame) + ON_WM_CREATE() + ON_WM_SETFOCUS() + ON_COMMAND(ID_APP_EXIT, OnAppExit) + ON_MESSAGE(WM_OPENURL, Open) + ON_MESSAGE(WM_SETTINGCHANGE, OnSIPChange) + ON_MESSAGE(WM_SETSIZE,OnSetSize) + ON_MESSAGE(WM_NAVIGATE,OnNavigate) + ON_WM_SIZE() + ON_COMMAND(ID_FILE_STEP, OnFileStep) + ON_UPDATE_COMMAND_UI(ID_FILE_STEP, OnUpdateFileStep) + ON_COMMAND(ID_FILE_PAUSE, OnFilePause) + ON_UPDATE_COMMAND_UI(ID_FILE_PAUSE, OnUpdateFilePause) + ON_COMMAND(ID_FILE_STOP, OnFileStop) + ON_UPDATE_COMMAND_UI(ID_FILE_STOP, OnUpdateFileStop) + ON_COMMAND(ID_VIEW_FULLSCREEN, OnViewFullscreen) + ON_UPDATE_COMMAND_UI(ID_VIEW_FULLSCREEN, OnUpdateViewFullscreen) + ON_WM_CLOSE() + ON_COMMAND(ID_VIEW_FIT, OnViewFit) + ON_UPDATE_COMMAND_UI(ID_VIEW_FIT, OnUpdateViewFit) + ON_COMMAND(ID_VIEW_AR_ORIG, OnViewArOrig) + ON_COMMAND(ID_VIEW_AR_FILL, OnViewArFill) + ON_COMMAND(ID_VIEW_AR_43, OnViewAr43) + ON_COMMAND(ID_VIEW_AR_169, OnViewAr169) + ON_COMMAND(ID_NAV_NONE, OnNavNone) + ON_COMMAND(ID_NAV_SLIDE, OnNavSlide) + ON_COMMAND(ID_NAV_RESET, OnNaveReset) + ON_COMMAND_RANGE(ID_NAV_NONE, ID_NAV_EXAMINE, OnSetNavigation) + ON_WM_KEYDOWN() + ON_WM_KEYUP() + ON_COMMAND(ID_VIEW_TIMING, OnViewTiming) + ON_UPDATE_COMMAND_UI(ID_VIEW_TIMING, OnUpdateViewTiming) + ON_WM_INITMENUPOPUP() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame construction/destruction + +CMainFrame::CMainFrame() +{ + GXOpenInput(); + m_view_timing = 0; + m_restore_fs = 0; +} + +CMainFrame::~CMainFrame() +{ + GXCloseInput(); +} + +void CMainFrame::OnSetFocus(CWnd* pOldWnd) +{ + if (m_restore_fs) { + m_restore_fs = 0; + GetApp()->ShowTaskBar(0); + OnViewFullscreen(); + } +} + +int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + COsmo4 *app = GetApp(); + + if (CFrameWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + // create a view to occupy the client area of the frame + if (!m_wndView.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW | WS_BORDER, + CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL)) + { + TRACE0("Failed to create view window\n"); + return -1; + } + m_wndView.ShowWindow(SW_HIDE); + + + if (!m_dumbWnd.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW | WS_BORDER, + CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL)) + { + TRACE0("Failed to create dumb window\n"); + return -1; + } + m_dumbWnd.SetWindowPos(this, 0, 0, app->m_screen_width, app->m_screen_height-app->m_menu_height, 0L); + m_dumbWnd.ShowWindow(SW_HIDE); + + if (!m_progBar.Create(IDD_CONTROL , this) ) { + TRACE0("Failed to create status bar\n"); + return -1; // fail to create + } + m_progBar.UpdateWindow(); + m_progBar.SetWindowPos(this, 0, 0, app->m_screen_width, app->m_menu_height, 0L); + m_progBar.ShowWindow(SW_SHOWNORMAL); + + +// m_wndCommandBar.m_bShowSharedNewButton = FALSE; + + if (!m_wndCommandBar.Create(this) + || !m_wndCommandBar.InsertMenuBar(IDR_MENU) + || !m_wndCommandBar.AddAdornments() + || !m_wndCommandBar.LoadBitmap(IDR_MAINFRAME) + ) + { + TRACE0("Failed to create CommandBar\n"); + return -1; // fail to create + } + + CToolBarCtrl & toolBar = m_wndCommandBar.GetToolBarCtrl(); + TBBUTTON tb; + memset(&tb, 0, sizeof(tb)); + tb.idCommand = ID_OPEN_FILE; tb.iBitmap = 0; tb.fsStyle = TBSTYLE_BUTTON; toolBar.AddButtons(1, &tb); + tb.idCommand = 0; tb.iBitmap = 0; tb.fsStyle = TBSTYLE_SEP; toolBar.AddButtons(1, &tb); + tb.idCommand = ID_FILE_PAUSE; tb.iBitmap = 1; tb.fsStyle = TBSTYLE_BUTTON; toolBar.AddButtons(1, &tb); + tb.idCommand = ID_FILE_STEP; tb.iBitmap = 2; tb.fsStyle = TBSTYLE_BUTTON; toolBar.AddButtons(1, &tb); + tb.idCommand = ID_FILE_STOP; tb.iBitmap = 3; tb.fsStyle = TBSTYLE_BUTTON; toolBar.AddButtons(1, &tb); + tb.idCommand = 0; tb.iBitmap = 0; tb.fsStyle = TBSTYLE_SEP; toolBar.AddButtons(1, &tb); + + SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), TRUE); + SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), FALSE); + SetWindowPos(NULL, 0, 0, app->m_screen_width, app->m_screen_height, 0L); + + SetWindowText(_T("Osmo4")); + return 0; +} + +void CMainFrame::SetPauseButton(Bool force_play_button) +{ + CToolBarCtrl & toolBar = m_wndCommandBar.GetToolBarCtrl(); + TBBUTTON tb; + memset(&tb, 0, sizeof(tb)); + tb.idCommand = ID_FILE_PAUSE; tb.fsStyle = TBSTYLE_BUTTON; + + if (force_play_button || GetApp()->m_stoped || gf_term_get_option(GetApp()->m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) { + tb.iBitmap = 4; + } else { + tb.iBitmap = 1; + } + toolBar.DeleteButton(5); + toolBar.InsertButton(5, &tb); +} + + +BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + if( !CFrameWnd::PreCreateWindow(cs) ) + return FALSE; + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + + cs.lpszClass = AfxRegisterWndClass(0); + return TRUE; +} + + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame diagnostics + +/* +#ifdef _DEBUG +void CMainFrame::AssertValid() const +{ + CFrameWnd::AssertValid(); +} + +void CMainFrame::Dump(CDumpContext& dc) const +{ + CFrameWnd::Dump(dc); +} + +#endif //_DEBUG +*/ + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame message handlers +BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) +{ + // let the view have first crack at the command + if (m_wndView.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) + return TRUE; + + // otherwise, do default handling + return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); +} + +#define PROGRESS_TIMER 20 +#define PROGRESS_REFRESH_MS 500 + +void CALLBACK EXPORT ProgressTimer(HWND , UINT , UINT nID , DWORD ) +{ + if (nID != PROGRESS_TIMER) return; + ((CMainFrame *) GetApp()->m_pMainWnd)->UpdateTime(); +} + +void CMainFrame::UpdateTime() +{ + u32 now; + + COsmo4 *app = GetApp(); + if (!app->m_open || app->m_stoped) return; + now = gf_term_get_time_in_ms(app->m_term); + if (!now) return; + + if (app->m_can_seek && (now>=app->m_duration + 100)) { + if (gf_term_get_option(app->m_term, GF_OPT_IS_FINISHED)) { + if (app->m_Loop && m_full_screen) { + gf_term_play_from_time(app->m_term, 0, 0); + } else { + OnFileStop(); + if (app->m_Loop) OnFilePause(); + } + return; + } + } + + if (!m_full_screen) m_progBar.SetPosition(now); +} + +void CMainFrame::CloseURL() +{ + COsmo4 *app = GetApp(); + if (!app->m_open) return; + if (m_view_timing) KillTimer(PROGRESS_TIMER); + gf_term_disconnect(app->m_term); + app->m_open = 0; + app->m_can_seek = 0; + app->m_duration = (u32) -1; + m_progBar.m_prev_time = 0; + m_progBar.SetPosition(0); +} + +void CMainFrame::OnAppExit() +{ + CloseURL(); + PostMessage(WM_QUIT); +} + +void CMainFrame::OnSize(UINT nType, int cx, int cy) +{ + COsmo4 *app = GetApp(); + u32 disp_w, disp_h, c_w, c_h, x, y; + + if (m_full_screen) return; + + disp_w = app->m_screen_width; + disp_h = app->m_screen_height; + CFrameWnd::OnSize(nType, disp_w, disp_h); + + x = y = 0; + disp_h -= app->m_menu_height; + + if (m_view_timing) { + disp_h -= app->m_menu_height; + y = app->m_menu_height; + m_progBar.SetWindowPos(this, 0, 0, app->m_screen_width, app->m_menu_height, 0L); + m_progBar.ShowWindow(SW_SHOWNORMAL); + } else { + m_progBar.ShowWindow(SW_HIDE); + } + m_dumbWnd.SetWindowPos(this, 0, y, disp_w, disp_h, 0L); + m_dumbWnd.ShowWindow(SW_SHOW); + + if (m_view_timing) + SetTimer(PROGRESS_TIMER, PROGRESS_REFRESH_MS, ProgressTimer); + + if (!app->m_scene_width || !app->m_scene_height) { + m_wndView.SetWindowPos(this, 0, y, disp_w, disp_h, SWP_SHOWWINDOW); + gf_term_set_size(app->m_term, disp_w, disp_h); + return; + } + + if (!app->m_fit_screen && (app->m_scene_width < disp_w) && (app->m_scene_height < disp_h)) { + c_w = app->m_scene_width; + c_h = app->m_scene_height; + x = (disp_w - c_w) / 2; + y = (disp_h - c_h) / 2; + } else { + c_w = disp_w; + c_h = disp_h; + } + m_wndView.SetWindowPos(this, x, y, c_w, c_h, SWP_SHOWWINDOW | SWP_NOZORDER); + gf_term_set_size(app->m_term, c_w, c_h); +} + + +void CMainFrame::OnViewFullscreen() +{ + COsmo4 *app = GetApp(); + if (!app->m_open) return; + u32 disp_w = app->m_screen_width; + u32 disp_h = app->m_screen_height; + + Bool is_full_screen = !m_full_screen; + + /*prevent resize messages*/ + m_full_screen = 1; + + HWND hWnd = GetSafeHwnd(); + ::SetForegroundWindow(hWnd); + ::CommandBar_Show(m_wndCommandBar.GetSafeHwnd(), is_full_screen ? FALSE : TRUE); + SHFullScreen(hWnd, SHFS_HIDESTARTICON | SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON); + + if (is_full_screen) { + m_dumbWnd.ShowWindow(SW_HIDE); + + ::MoveWindow(m_hWnd, 0, 0, disp_w, disp_h, 0); + m_wndView.GetWindowRect(&m_view_rc); + m_wndView.SetWindowPos(this, 0, 0, disp_w, disp_h, SWP_NOZORDER); + gf_term_set_option(app->m_term, GF_OPT_FULLSCREEN, is_full_screen); + m_full_screen = 1; + } else { + gf_term_set_option(app->m_term, GF_OPT_FULLSCREEN, is_full_screen); + m_full_screen = 0; + OnSetSize(0,0); + m_dumbWnd.ShowWindow(SW_SHOW); + gf_term_set_option(app->m_term, GF_OPT_REFRESH, 0); + } +} + + +void CMainFrame::OnUpdateViewFullscreen(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(GetApp()->m_open ? TRUE : FALSE); +} + +LONG CMainFrame::OnSetSize(WPARAM wParam, LPARAM lParam) +{ + RECT rc; + if (m_full_screen) return 0; + GetWindowRect(&rc); + SetWindowPos(NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top, SWP_NOZORDER | SWP_NOMOVE); + return 1; +} + +LONG CMainFrame::Open(WPARAM wParam, LPARAM lParam) +{ + COsmo4 *app = GetApp(); + CloseURL(); + char filename[5000]; + CE_WideToChar((u16 *) (LPCTSTR) app->m_filename, filename); + app->m_stoped = 0; + + if (app->m_reconnect_time) { + gf_term_connect_from_time(app->m_term, filename, app->m_reconnect_time, 0); + app->m_reconnect_time = 0; + } else { + gf_term_connect(app->m_term, filename); + } + app->SetBacklightState(1); + return 1; +} + + + + +LONG CMainFrame::OnNavigate(WPARAM /*wParam*/, LPARAM /*lParam*/) +{ + COsmo4 *app = GetApp(); + char to_url[MAX_PATH]; + CE_WideToChar((u16 *) (LPCTSTR) app->m_navigate_url, to_url); + + if (gf_term_is_supported_url(app->m_term, to_url, 1, app->m_no_mime_fetch)) { + char fileName[MAX_PATH]; + TCHAR w_to_url[MAX_PATH]; + CE_WideToChar((u16 *) (LPCTSTR) app->m_filename, fileName); + char *str = gf_url_concatenate(fileName, to_url); + if (!str) str = strdup(to_url); + CE_CharToWide(str, (u16 *)w_to_url); + free(str); + app->m_filename = w_to_url; + Open(0, 0); + } else { + SHELLEXECUTEINFO info; + console_message = app->m_navigate_url; + console_err = GF_OK; + PostMessage(WM_CONSOLEMSG); + + + if (m_full_screen) { + OnViewFullscreen(); + app->ShowTaskBar(1); + m_restore_fs = 1; + } + + memset(&info, 0, sizeof(SHELLEXECUTEINFO)); + info.cbSize = sizeof(SHELLEXECUTEINFO); + info.lpVerb = L"open"; + info.fMask = SEE_MASK_NOCLOSEPROCESS; + info.lpFile = L"iexplore"; + info.lpParameters = (LPCTSTR) app->m_navigate_url; + info.nShow = SW_SHOWNORMAL; + ShellExecuteEx(&info); + } + return 1; +} + +void CMainFrame::OnFilePause() +{ + COsmo4 *app = GetApp(); + if (app->m_stoped) { + char filename[5000]; + CE_WideToChar((u16 *) (LPCTSTR) app->m_filename, filename); + app->m_stoped = 0; + gf_term_connect(app->m_term, filename); + app->SetBacklightState(1); + + if (m_view_timing) + SetTimer(PROGRESS_TIMER, PROGRESS_REFRESH_MS, ProgressTimer); + + SetPauseButton(); + } else { + app->Pause(); + } +} +void CMainFrame::OnUpdateFilePause(CCmdUI* pCmdUI) +{ + COsmo4 *app = GetApp(); + pCmdUI->Enable((app->m_open || app->m_stoped) ? TRUE : FALSE); +} +void CMainFrame::OnFileStop() +{ + COsmo4 *app = GetApp(); + if (!app->m_open) return; + if (m_full_screen) OnViewFullscreen(); + app->m_stoped = 1; + if (m_view_timing) KillTimer(PROGRESS_TIMER); + gf_term_disconnect(app->m_term); + m_progBar.SetPosition(0); + app->SetBacklightState(0); + SetPauseButton(); +} + +void CMainFrame::OnUpdateFileStop(CCmdUI* pCmdUI) +{ + pCmdUI->Enable( GetApp()->m_open ? TRUE : FALSE); +} + +void CMainFrame::OnFileStep() +{ + COsmo4 *app = GetApp(); + gf_term_set_option(app->m_term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE); + app->SetBacklightState(0); + SetPauseButton(1); +} +void CMainFrame::OnUpdateFileStep(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(GetApp()->m_open ? TRUE : FALSE); +} + +void CMainFrame::OnClose() +{ + PostMessage(WM_DESTROY); +} + +LONG CMainFrame::OnSIPChange(WPARAM wParam, LPARAM lParam) +{ + if (wParam == SPI_SETSIPINFO) GetApp()->ShowTaskBar(0); + return 1; +} + +void CMainFrame::OnViewFit() +{ + COsmo4 *app = GetApp(); + app->m_fit_screen = !app->m_fit_screen; + if (app->m_open) OnSetSize(0, 0); +} + +void CMainFrame::OnUpdateViewFit(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(GetApp()->m_fit_screen ? TRUE : FALSE); +} + +void CMainFrame::OnViewArOrig() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP); +} +void CMainFrame::OnViewArFill() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN); +} +void CMainFrame::OnViewAr43() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3); +} +void CMainFrame::OnViewAr169() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9); +} + +void CMainFrame::OnNavNone() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_NONE); +} + +void CMainFrame::OnNavSlide() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_SLIDE); +} + +void CMainFrame::OnNaveReset() +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_NAVIGATION_TYPE, 0); +} + +void CMainFrame::ForwardMessage() +{ + const MSG *msg = GetCurrentMessage(); + m_wndView.SendMessage(msg->message, msg->wParam, msg->lParam); +} +void CMainFrame::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + ForwardMessage(); +} +void CMainFrame::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + ForwardMessage(); +} + + +void CMainFrame::OnViewTiming() +{ + if (m_full_screen) return; + if (m_view_timing) KillTimer(PROGRESS_TIMER); + m_view_timing = !m_view_timing; + OnSetSize(0, 0); +} + +void CMainFrame::OnUpdateViewTiming(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(m_view_timing ? TRUE : FALSE); +} + +void CMainFrame::OnSetNavigation(UINT nID) +{ + gf_term_set_option(GetApp()->m_term, GF_OPT_NAVIGATION, nID - ID_NAV_NONE); +} + + +void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) +{ + COsmo4 *app = GetApp(); + CFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu); + + u32 opt = gf_term_get_option(GetApp()->m_term, GF_OPT_ASPECT_RATIO); + CheckMenuItem(pPopupMenu->m_hMenu, ID_VIEW_AR_ORIG, MF_BYCOMMAND| (opt==GF_ASPECT_RATIO_KEEP) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(pPopupMenu->m_hMenu, ID_VIEW_AR_FILL, MF_BYCOMMAND| (opt==GF_ASPECT_RATIO_FILL_SCREEN) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(pPopupMenu->m_hMenu, ID_VIEW_AR_43, MF_BYCOMMAND| (opt==GF_ASPECT_RATIO_4_3) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(pPopupMenu->m_hMenu, ID_VIEW_AR_169, MF_BYCOMMAND| (opt==GF_ASPECT_RATIO_16_9) ? MF_CHECKED : MF_UNCHECKED); + + CheckMenuItem(pPopupMenu->m_hMenu, ID_VIEW_FIT, MF_BYCOMMAND| app->m_fit_screen ? MF_CHECKED : MF_UNCHECKED); + + u32 type; + if (!app->m_open) type = GF_NAVIGATE_TYPE_NONE; + else type = gf_term_get_option(app->m_term, GF_OPT_NAVIGATION_TYPE); + + EnableMenuItem(pPopupMenu->m_hMenu, ID_NAV_NONE, MF_BYCOMMAND | ((type==GF_NAVIGATE_TYPE_NONE) ? MF_GRAYED : MF_ENABLED) ); + EnableMenuItem(pPopupMenu->m_hMenu, ID_NAV_SLIDE, MF_BYCOMMAND | ((type==GF_NAVIGATE_TYPE_NONE) ? MF_GRAYED : MF_ENABLED) ); + EnableMenuItem(pPopupMenu->m_hMenu, ID_NAV_RESET, MF_BYCOMMAND | ((type==GF_NAVIGATE_TYPE_NONE) ? MF_GRAYED : MF_ENABLED) ); + + EnableMenuItem(pPopupMenu->m_hMenu, ID_NAV_WALK, MF_BYCOMMAND | ( (type!=GF_NAVIGATE_TYPE_3D) ? MF_GRAYED : MF_ENABLED) ); + EnableMenuItem(pPopupMenu->m_hMenu, ID_NAV_FLY, MF_BYCOMMAND | ((type!=GF_NAVIGATE_TYPE_3D) ? MF_GRAYED : MF_ENABLED) ); + EnableMenuItem(pPopupMenu->m_hMenu, ID_NAV_EXAMINE, MF_BYCOMMAND | ((type!=GF_NAVIGATE_TYPE_3D) ? MF_GRAYED : MF_ENABLED) ); + EnableMenuItem(pPopupMenu->m_hMenu, ID_COLLIDE_OFF, MF_BYCOMMAND | ((type!=GF_NAVIGATE_TYPE_3D) ? MF_GRAYED : MF_ENABLED) ); + EnableMenuItem(pPopupMenu->m_hMenu, ID_COLLIDE_REG, MF_BYCOMMAND | ((type!=GF_NAVIGATE_TYPE_3D) ? MF_GRAYED : MF_ENABLED) ); + EnableMenuItem(pPopupMenu->m_hMenu, ID_COLLIDE_DISP, MF_BYCOMMAND | ((type!=GF_NAVIGATE_TYPE_3D) ? MF_GRAYED : MF_ENABLED) ); + EnableMenuItem(pPopupMenu->m_hMenu, ID_NAV_GRAVITY, MF_BYCOMMAND | ((type!=GF_NAVIGATE_TYPE_3D) ? MF_GRAYED : MF_ENABLED) ); + + if (type==GF_NAVIGATE_TYPE_NONE) { + u32 mode = gf_term_get_option(app->m_term, GF_OPT_NAVIGATION); + CheckMenuItem(pPopupMenu->m_hMenu, ID_NAV_NONE, MF_BYCOMMAND | ( (mode==GF_NAVIGATE_NONE) ? MF_CHECKED : MF_UNCHECKED) ); + CheckMenuItem(pPopupMenu->m_hMenu, ID_NAV_SLIDE, MF_BYCOMMAND | ( (mode==GF_NAVIGATE_SLIDE) ? MF_CHECKED : MF_UNCHECKED) ); + CheckMenuItem(pPopupMenu->m_hMenu, ID_NAV_WALK, MF_BYCOMMAND | ( (mode==GF_NAVIGATE_WALK) ? MF_CHECKED : MF_UNCHECKED) ); + CheckMenuItem(pPopupMenu->m_hMenu, ID_NAV_FLY, MF_BYCOMMAND | ((mode==GF_NAVIGATE_FLY) ? MF_CHECKED : MF_UNCHECKED) ); + CheckMenuItem(pPopupMenu->m_hMenu, ID_NAV_EXAMINE, MF_BYCOMMAND | ((mode==GF_NAVIGATE_EXAMINE) ? MF_CHECKED : MF_UNCHECKED) ); + } +} diff --git a/applications/osmo4_wce/MainFrm.h b/applications/osmo4_wce/MainFrm.h new file mode 100644 index 0000000..7684b9f --- /dev/null +++ b/applications/osmo4_wce/MainFrm.h @@ -0,0 +1,151 @@ +// MainFrm.h : interface of the CMainFrame class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_MAINFRM_H__1DEE4BC7_6B56_48A8_BDD7_5DC14EF6AD3E__INCLUDED_) +#define AFX_MAINFRM_H__1DEE4BC7_6B56_48A8_BDD7_5DC14EF6AD3E__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#include "ProgressBar.h" + + +class CChildView : public CWnd +{ +// Construction +public: + CChildView(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CChildView) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CChildView(); + // Generated message map functions +protected: + //{{AFX_MSG(CChildView) + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class CMainFrame : public CFrameWnd +{ +public: + CMainFrame(); + + + +protected: + DECLARE_DYNAMIC(CMainFrame) + +// Attributes +public: + + ProgressBar m_progBar; + Bool m_full_screen, m_restore_fs, m_view_timing; + u32 m_timer_on; + CString console_message; + GF_Err console_err; + u32 m_aspect_ratio; + +// Operations +public: + void SetPauseButton(Bool force_play_button = 0); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMainFrame) + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + afx_msg void OnSetFocus(CWnd *pOldWnd); + virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CMainFrame(); + +/* +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif +*/ + +protected: // control bar embedded members + +#if (_MSC_VER >= 1300) + CCommandBar m_wndCommandBar; +#else + CCeCommandBar m_wndCommandBar; +#endif + + void CloseURL(); + void ForwardMessage(); + +private: + RECT m_view_rc; + +public: + /*m_dumbWnd is used to clean the screen...*/ + CChildView m_wndView, m_dumbWnd; + void UpdateTime(); + +// Generated message map functions +protected: + //{{AFX_MSG(CMainFrame) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnAppExit(); + afx_msg LONG Open(WPARAM wParam, LPARAM lParam); + afx_msg LONG OnSIPChange(WPARAM wParam, LPARAM lParam); + afx_msg LONG OnSetSize(WPARAM wParam, LPARAM lParam); + afx_msg LONG OnNavigate(WPARAM wParam, LPARAM lParam); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnFileStep(); + afx_msg void OnUpdateFileStep(CCmdUI* pCmdUI); + afx_msg void OnFilePause(); + afx_msg void OnUpdateFilePause(CCmdUI* pCmdUI); + afx_msg void OnFileStop(); + afx_msg void OnUpdateFileStop(CCmdUI* pCmdUI); + afx_msg void OnViewFullscreen(); + afx_msg void OnUpdateViewFullscreen(CCmdUI* pCmdUI); + afx_msg void OnClose(); + afx_msg void OnViewFit(); + afx_msg void OnUpdateViewFit(CCmdUI* pCmdUI); + afx_msg void OnViewArOrig(); + afx_msg void OnViewArFill(); + afx_msg void OnViewAr43(); + afx_msg void OnViewAr169(); + afx_msg void OnNavNone(); + afx_msg void OnNavSlide(); + afx_msg void OnNaveReset(); + afx_msg void OnSetNavigation(UINT nID); + + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnViewTiming(); + afx_msg void OnUpdateViewTiming(CCmdUI* pCmdUI); + afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAINFRM_H__1DEE4BC7_6B56_48A8_BDD7_5DC14EF6AD3E__INCLUDED_) diff --git a/applications/osmo4_wce/OpenDlg.cpp b/applications/osmo4_wce/OpenDlg.cpp new file mode 100644 index 0000000..6a58e7c --- /dev/null +++ b/applications/osmo4_wce/OpenDlg.cpp @@ -0,0 +1,92 @@ +// OpenDlg.cpp : implementation file +// +#include "stdafx.h" +#include "resource.h" +#include "OpenDlg.h" +#include "Osmo4.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// OpenDlg dialog + + +OpenDlg::OpenDlg(CWnd* pParent /*=NULL*/) + : CDialog(OpenDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(OpenDlg) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void OpenDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(OpenDlg) + DDX_Control(pDX, IDC_FILELIST, m_URLs); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(OpenDlg, CDialog) + //{{AFX_MSG_MAP(OpenDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +void OpenDlg::OnOK() +{ + CString URL; + char szUrl[5000]; + + int sel = m_URLs.GetCurSel(); + if (sel == CB_ERR) { + m_URLs.GetWindowText(URL); + } else { + m_URLs.GetLBText(sel, URL); + } + if (!URL.GetLength()) { + EndDialog(IDCANCEL); + return; + } + COsmo4 *app = GetApp(); + u32 nb_entries; + + app->m_filename = URL; + + CE_WideToChar((unsigned short *) (LPCTSTR) URL, szUrl); + + gf_cfg_set_key(app->m_user.config, "RecentFiles", szUrl, NULL); + gf_cfg_insert_key(app->m_user.config, "RecentFiles", szUrl, "", 0); + /*remove last entry if needed*/ + nb_entries = gf_cfg_get_key_count(app->m_user.config, "RecentFiles"); + if (nb_entries>20) { + gf_cfg_set_key(app->m_user.config, "RecentFiles", gf_cfg_get_key_name(app->m_user.config, "RecentFiles", nb_entries-1), NULL); + } + EndDialog(IDOK); +} + +BOOL OpenDlg::OnInitDialog() +{ + TCHAR w_str[5000]; + CDialog::OnInitDialog(); + COsmo4 *app = GetApp(); + const char *sOpt; + u32 i=0; + + while (m_URLs.GetCount()) m_URLs.DeleteString(0); + while (1) { + sOpt = gf_cfg_get_key_name(app->m_user.config, "RecentFiles", i); + if (!sOpt) break; + CE_CharToWide((char *) sOpt, (u16 *)w_str); + m_URLs.AddString(w_str); + i++; + } + + SetFocus(); + return TRUE; +} diff --git a/applications/osmo4_wce/OpenDlg.h b/applications/osmo4_wce/OpenDlg.h new file mode 100644 index 0000000..efc8724 --- /dev/null +++ b/applications/osmo4_wce/OpenDlg.h @@ -0,0 +1,47 @@ +#if !defined(AFX_OPENDLG_H__DD903B38_EA45_4251_A8C9_4E4B08BECCCC__INCLUDED_) +#define AFX_OPENDLG_H__DD903B38_EA45_4251_A8C9_4E4B08BECCCC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// OpenDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// OpenDlg dialog + +class OpenDlg : public CDialog +{ +// Construction +public: + OpenDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(OpenDlg) + enum { IDD = IDD_OPENFILE }; + CComboBox m_URLs; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(OpenDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(OpenDlg) + virtual void OnOK(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_OPENDLG_H__DD903B38_EA45_4251_A8C9_4E4B08BECCCC__INCLUDED_) diff --git a/applications/osmo4_wce/Options.cpp b/applications/osmo4_wce/Options.cpp new file mode 100644 index 0000000..70bd376 --- /dev/null +++ b/applications/osmo4_wce/Options.cpp @@ -0,0 +1,1230 @@ +// Options.cpp : implementation file +// + +#include "stdafx.h" +#include "Osmo4.h" +#include +#include +#include +#include +#include +#include + +#include "Options.h" +#include + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// COptions dialog + + +COptions::COptions(CWnd* pParent /*=NULL*/) + : CDialog(COptions::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptions) + //}}AFX_DATA_INIT + +} + + +void COptions::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptions) + DDX_Control(pDX, IDC_COMBOSEL, m_Selection); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptions, CDialog) + //{{AFX_MSG_MAP(COptions) + ON_BN_CLICKED(IDC_SAVEOPT, OnSaveopt) + ON_CBN_SELCHANGE(IDC_COMBOSEL, OnSelchangeCombosel) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptions message handlers + + +void COptions::OnSelchangeCombosel() +{ + HideAll(); + switch (m_Selection.GetCurSel()) { + case 0: m_general.ShowWindow(SW_SHOW); break; + case 1: m_systems.ShowWindow(SW_SHOW); break; + case 2: m_decoder.ShowWindow(SW_SHOW); break; + case 3: m_render.ShowWindow(SW_SHOW); break; + case 4: m_render3D.ShowWindow(SW_SHOW); break; + case 5: m_audio.ShowWindow(SW_SHOW); break; + case 6: m_font.ShowWindow(SW_SHOW); break; + case 7: m_http.ShowWindow(SW_SHOW); break; + case 8: m_stream.ShowWindow(SW_SHOW); break; + } +} + +void COptions::HideAll() +{ + m_general.ShowWindow(SW_HIDE); + m_systems.ShowWindow(SW_HIDE); + m_render.ShowWindow(SW_HIDE); + m_render3D.ShowWindow(SW_HIDE); + m_audio.ShowWindow(SW_HIDE); + m_http.ShowWindow(SW_HIDE); + m_font.ShowWindow(SW_HIDE); + m_stream.ShowWindow(SW_HIDE); + m_decoder.ShowWindow(SW_HIDE); +} + +BOOL COptions::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_general.Create(IDD_OPT_GEN, this); + m_systems.Create(IDD_OPT_SYSTEMS, this); + m_decoder.Create(IDD_OPT_DECODER, this); + m_render.Create(IDD_OPT_RENDER, this); + m_render3D.Create(IDD_OPT_RENDER3D, this); + m_audio.Create(IDD_OPT_AUDIO, this); + m_http.Create(IDD_OPT_HTTP, this); + m_font.Create(IDD_OPT_FONT, this); + m_stream.Create(IDD_OPT_STREAM, this); + + m_Selection.AddString(_T("General")); + m_Selection.AddString(_T("MPEG-4 Systems")); + m_Selection.AddString(_T("Decoders")); + m_Selection.AddString(_T("Compositor")); + m_Selection.AddString(_T("3D Rendering")); + m_Selection.AddString(_T("Audio")); + m_Selection.AddString(_T("Text")); + m_Selection.AddString(_T("Download")); + m_Selection.AddString(_T("Streaming")); + HideAll(); + + const char *sOpt = gf_cfg_get_key(GetApp()->m_user.config, "General", "ConfigPanel"); + u32 sel = sOpt ? atoi(sOpt) : 0; + if (sel>8) sel=8; + m_Selection.SetCurSel(sel); + OnSelchangeCombosel(); + + SetFocus(); + return TRUE; +} + +void COptions::OnSaveopt() +{ + m_general.SaveOptions(); + m_systems.SaveOptions(); + m_render.SaveOptions(); + m_render3D.SaveOptions(); + m_audio.SaveOptions(); + m_http.SaveOptions(); + m_font.SaveOptions(); + m_stream.SaveOptions(); + m_decoder.SaveOptions(); + + COsmo4 *gpac = GetApp(); + gf_term_set_option(gpac->m_term, GF_OPT_RELOAD_CONFIG, 1); +} + +void COptions::OnOK() +{ + char str[20]; + sprintf(str, "%d", m_Selection.GetCurSel()); + gf_cfg_set_key(GetApp()->m_user.config, "General", "ConfigPanel", str); + + EndDialog(IDCANCEL); +} + + + +COptAudio::COptAudio(CWnd* pParent /*=NULL*/) + : CDialog(COptAudio::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptAudio) + //}}AFX_DATA_INIT +} + + +void COptAudio::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptAudio) + DDX_Control(pDX, IDC_DRIVER_LIST, m_DriverList); + DDX_Control(pDX, IDC_AUDIO_RESYNC, m_AudioResync); + DDX_Control(pDX, IDC_AUDIO_DUR, m_AudioDur); + DDX_Control(pDX, IDC_SPIN_DUR, m_SpinDur); + DDX_Control(pDX, IDC_FORCE_AUDIO, m_ForceConfig); + DDX_Control(pDX, IDC_SPIN_AUDIO, m_AudioSpin); + DDX_Control(pDX, IDC_EDIT_AUDIO, m_AudioEdit); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptAudio, CDialog) + //{{AFX_MSG_MAP(COptAudio) + ON_BN_CLICKED(IDC_FORCE_AUDIO, OnForceAudio) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptAudio message handlers + +BOOL COptAudio::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_AudioSpin.SetBuddy(& m_AudioEdit); + m_SpinDur.SetBuddy(& m_AudioDur); + m_SpinDur.SetRange(0, 1000); + + COsmo4 *gpac = GetApp(); + const char *sOpt; + TCHAR wTmp[500]; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "ForceConfig"); + m_ForceConfig.SetCheck( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "NumBuffers"); + if (sOpt) { + CE_CharToWide((char *)sOpt, (u16 *)wTmp); + m_AudioEdit.SetWindowText(wTmp); + } else { + m_AudioEdit.SetWindowText(_T("2")); + } + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "TotalDuration"); + if (sOpt) { + CE_CharToWide((char *)sOpt, (u16 *)wTmp); + m_AudioDur.SetWindowText(wTmp); + } else { + m_AudioDur.SetWindowText(_T("200")); + } + + OnForceAudio(); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "NoResync"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_AudioResync.SetCheck(1); + } else { + m_AudioResync.SetCheck(0); + } + + /*driver enum*/ + while (m_DriverList.GetCount()) m_DriverList.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Audio", "DriverName"); + u32 count = gf_modules_get_count(gpac->m_user.modules); + GF_BaseInterface *ifce; + s32 select = 0; + s32 to_sel = 0; + for (u32 i=0; im_user.modules, i, GF_AUDIO_OUTPUT_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + CE_CharToWide((char *) ((GF_BaseInterface *)ifce)->module_name, (u16 *)wTmp); + m_DriverList.AddString(wTmp); + gf_modules_close_interface(ifce); + to_sel++; + } + m_DriverList.SetCurSel(select); + + + return TRUE; +} + + +void COptAudio::SaveOptions() +{ + COsmo4 *gpac = GetApp(); + TCHAR wstr[50]; + char str[50]; + + gf_cfg_set_key(gpac->m_user.config, "Audio", "ForceConfig", m_ForceConfig.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Audio", "NoResync", m_AudioResync.GetCheck() ? "yes" : "no"); + + m_AudioEdit.GetWindowText(wstr, 20); + CE_WideToChar((u16 *)wstr, str); + gf_cfg_set_key(gpac->m_user.config, "Audio", "NumBuffers", str); + m_AudioDur.GetWindowText(wstr, 20); + CE_WideToChar((u16 *)wstr, str); + gf_cfg_set_key(gpac->m_user.config, "Audio", "TotalDuration", str); + + m_DriverList.GetWindowText(wstr, 50); + CE_WideToChar((u16 *)wstr, str); + gf_cfg_set_key(gpac->m_user.config, "Audio", "DriverName", str); + +} + +void COptAudio::OnForceAudio() +{ + BOOL en = m_ForceConfig.GetCheck(); + + m_AudioSpin.EnableWindow(en); + m_AudioEdit.EnableWindow(en); + m_SpinDur.EnableWindow(en); + m_AudioDur.EnableWindow(en); +} + + +COptDecoder::COptDecoder(CWnd* pParent /*=NULL*/) + : CDialog(COptDecoder::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptDecoder) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void COptDecoder::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptDecoder) + DDX_Control(pDX, IDC_VIDEC_LIST, m_Video); + DDX_Control(pDX, IDC_AUDEC_LIST, m_Audio); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptDecoder, CDialog) + //{{AFX_MSG_MAP(COptDecoder) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptDecoder message handlers + +BOOL COptDecoder::OnInitDialog() +{ + u32 i; + CDialog::OnInitDialog(); + + COsmo4 *gpac = GetApp(); + const char *sOpt; + + /*audio dec enum*/ + while (m_Audio.GetCount()) m_Audio.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "DefAudioDec"); + u32 count = gf_modules_get_count(gpac->m_user.modules); + GF_BaseDecoder *ifce; + s32 select = 0; + s32 to_sel = 0; + for (i=0; im_user.modules, i, GF_MEDIA_DECODER_INTERFACE); + if (!ifce) continue; + if (ifce->CanHandleStream(ifce, GF_STREAM_AUDIO, 0, NULL, 0, 0)) { + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + TCHAR wzTmp[500]; + CE_CharToWide((char *) ifce->module_name, (u16 *)wzTmp); + m_Audio.AddString(wzTmp); + to_sel++; + } + gf_modules_close_interface((GF_BaseInterface *) ifce); + } + m_Audio.SetCurSel(select); + + /*audio dec enum*/ + while (m_Video.GetCount()) m_Video.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "DefVideoDec"); + count = gf_modules_get_count(gpac->m_user.modules); + select = 0; + to_sel = 0; + for (i=0; im_user.modules, i, GF_MEDIA_DECODER_INTERFACE); + if (!ifce) continue; + if (ifce->CanHandleStream(ifce, GF_STREAM_VISUAL, 0, NULL, 0, 0)) { + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + TCHAR wzTmp[500]; + CE_CharToWide((char *) ifce->module_name, (u16 *)wzTmp); + m_Video.AddString(wzTmp); + to_sel++; + } + gf_modules_close_interface((GF_BaseInterface *) ifce); + } + m_Video.SetCurSel(select); + + return TRUE; +} + +void COptDecoder::SaveOptions() +{ + COsmo4 *gpac = GetApp(); + TCHAR wstr[100]; + char str[100]; + + m_Audio.GetWindowText(wstr, 50); + CE_WideToChar((u16 *)wstr, str); + gf_cfg_set_key(gpac->m_user.config, "Systems", "DefAudioDec", str); + m_Video.GetWindowText(wstr, 50); + CE_WideToChar((u16 *)wstr, str); + gf_cfg_set_key(gpac->m_user.config, "Systems", "DefVideoDec", str); +} + + + +COptFont::COptFont(CWnd* pParent /*=NULL*/) + : CDialog(COptFont::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptFont) + //}}AFX_DATA_INIT +} + + +void COptFont::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptFont) + DDX_Control(pDX, IDC_USE_TEXTURE, m_UseTexture); + DDX_Control(pDX, IDC_FONT_LIST, m_Fonts); + DDX_Control(pDX, IDC_BROWSE_FONT, m_BrowseFont); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptFont, CDialog) + //{{AFX_MSG_MAP(COptFont) + ON_BN_CLICKED(IDC_BROWSE_FONT, OnBrowseFont) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptFont message handlers + +BOOL COptFont::OnInitDialog() +{ + u32 i; + GF_BaseInterface *ifce; + + CDialog::OnInitDialog(); + + COsmo4 *gpac = GetApp(); + TCHAR wTmp[500]; + const char *sOpt; + + /*video drivers enum*/ + while (m_Fonts.GetCount()) m_Fonts.DeleteString(0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "FontEngine", "DriverName"); + s32 to_sel = 0; + s32 select = 0; + u32 count = gf_modules_get_count(gpac->m_user.modules); + for (i=0; im_user.modules, i, GF_FONT_READER_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + CE_CharToWide((char *) ifce->module_name, (u16 *)wTmp); + m_Fonts.AddString(wTmp); + gf_modules_close_interface(ifce); + to_sel++; + } + m_Fonts.SetCurSel(select); + + + sOpt = gf_cfg_get_key(gpac->m_user.config, "FontEngine", "FontDirectory"); + CE_CharToWide((char *)sOpt, (u16 *)wTmp); + if (sOpt) m_BrowseFont.SetWindowText(wTmp); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "TextureTextMode"); + m_UseTexture.SetCheck( (!sOpt || stricmp(sOpt, "Never")) ? 1 : 0); + + return TRUE; +} + +void COptFont::OnBrowseFont() +{ + +} + + +void COptFont::SaveOptions() +{ + COsmo4 *gpac = GetApp(); + char str[MAX_PATH]; + TCHAR wstr[MAX_PATH]; + + m_Fonts.GetWindowText(wstr, 50); + CE_WideToChar((u16 *)wstr, str); + gf_cfg_set_key(gpac->m_user.config, "FontEngine", "FontReader", str); + m_BrowseFont.GetWindowText(wstr, 50); + CE_WideToChar((u16 *)wstr, str); + gf_cfg_set_key(gpac->m_user.config, "FontEngine", "FontDirectory", str); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "TextureTextMode", m_UseTexture.GetCheck() ? "Default" : "Never"); +} + + + +COptGen::COptGen(CWnd* pParent /*=NULL*/) + : CDialog(COptGen::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptGen) + //}}AFX_DATA_INIT +} + + +void COptGen::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptGen) + DDX_Control(pDX, IDC_NO_BACKLIGHT, m_NoBacklight); + DDX_Control(pDX, IDC_FILL_SCREEN, m_Fill); + DDX_Control(pDX, IDC_LOOP, m_Loop); + DDX_Control(pDX, IDC_ENABLE_LOGS, m_Logs); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptGen, CDialog) + //{{AFX_MSG_MAP(COptGen) + ON_BN_CLICKED(IDC_FILEASSOC, OnFileassoc) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptGen message handlers + + + +BOOL COptGen::OnInitDialog() +{ + CDialog::OnInitDialog(); + COsmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "Loop"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_Loop.SetCheck(1); + } else { + m_Loop.SetCheck(0); + } + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "FillScreen"); + m_Fill.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "DisableBackLight"); + m_NoBacklight.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "LogLevel"); + m_Logs.SetCheck((sOpt && !stricmp(sOpt, "debug")) ? 1 : 0); + return TRUE; +} + +void COptGen::SaveOptions() +{ + COsmo4 *gpac = GetApp(); + + gpac->m_Loop = m_Loop.GetCheck(); + gf_cfg_set_key(gpac->m_user.config, "General", "Loop", gpac->m_Loop ? "yes" : "no"); + gpac->m_fit_screen = m_Fill.GetCheck(); + gf_cfg_set_key(gpac->m_user.config, "General", "FillScreen", gpac->m_fit_screen ? "yes" : "no"); + gpac->m_disable_backlight = m_NoBacklight.GetCheck(); + gf_cfg_set_key(gpac->m_user.config, "General", "DisableBackLight", gpac->m_disable_backlight ? "yes" : "no"); + + gpac->EnableLogs(m_Logs.GetCheck() ? 1 : 0); +} + +void COptGen::OnFileassoc() +{ + HKEY hSection; + TCHAR szDir[MAX_PATH]; + char szTemp[MAX_PATH]; + TCHAR cmd[MAX_PATH]; + DWORD ioSize = MAX_PATH; + DWORD dwDisp; + + RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Osmo4"), 0, KEY_READ, &hSection); + + GetModuleFileName(NULL, szDir, MAX_PATH); + + while (szDir[strlen((char *) szDir)-1] != (TCHAR) '\\') szDir[strlen((char *) szDir)-1] = 0; + if (!hSection) + RegCreateKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Osmo4"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hSection, &dwDisp); + + CE_WideToChar((u16 *)szDir, szTemp); + /*overwrite install dir with current path*/ + RegSetValueEx(hSection, _T("Install_Dir"), 0, REG_SZ, (const unsigned char *) szTemp, strlen(szTemp)+1); + RegCloseKey(hSection); + + + /*overwrite .mp4 file associations */ + RegCreateKeyEx(HKEY_CLASSES_ROOT, _T("mp4file\\DefaultIcon"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hSection, &dwDisp); + wcscpy(cmd, szDir); + wcscat(cmd, _T("Osmo4.ico") ); + CE_WideToChar((u16 *)cmd, szTemp); + + RegSetValueEx(hSection, _T(""), 0, REG_SZ, (const unsigned char *) szTemp, strlen((const char *) szTemp)+1); + RegCloseKey(hSection); + + RegCreateKeyEx(HKEY_CLASSES_ROOT, _T("mp4file\\Shell\\open\\command"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hSection, &dwDisp); + wcscpy(cmd, szDir); + wcscat(cmd, _T("Osmo4.exe \"%L\"") ); + CE_WideToChar((u16 *)cmd, szTemp); + RegSetValueEx(hSection, _T(""), 0, REG_SZ, (const unsigned char *) szTemp, strlen(szTemp)+1); + RegCloseKey(hSection); + + RegCreateKeyEx(HKEY_CLASSES_ROOT, _T(".mp4"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hSection, &dwDisp); + RegSetValueEx(hSection, _T(""), 0, REG_SZ, (const unsigned char *) "mp4file", strlen("mp4file")+1); + RegCloseKey(hSection); +} + + + +COptHTTP::COptHTTP(CWnd* pParent /*=NULL*/) + : CDialog(COptHTTP::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptHTTP) + //}}AFX_DATA_INIT +} + + +void COptHTTP::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptHTTP) + DDX_Control(pDX, IDC_RESTART_CACHE, m_RestartFile); + DDX_Control(pDX, IDC_CLEAN_CACHE, m_CleanCache); + DDX_Control(pDX, IDC_BROWSE_CACHE, m_CacheDir); + DDX_Control(pDX, IDC_SAX_PROGRESSIVE, m_Progressive); + DDX_Control(pDX, IDC_SAX_DURATION, m_SaxDuration); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptHTTP, CDialog) + //{{AFX_MSG_MAP(COptHTTP) + ON_BN_CLICKED(IDC_BROWSE_CACHE, OnBrowseCache) + ON_BN_CLICKED(IDC_SAX_PROGRESSIVE, OnProgressive) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptHTTP message handlers + + +void COptHTTP::OnBrowseCache() +{ + +} + +BOOL COptHTTP::OnInitDialog() +{ + CDialog::OnInitDialog(); + + COsmo4 *gpac = GetApp(); + TCHAR wTmp[500]; + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Downloader", "CleanCache"); + m_CleanCache.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Downloader", "RestartFiles"); + m_RestartFile.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(gpac->m_user.config, "SAXLoader", "Progressive"); + m_Progressive.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "SAXLoader", "MaxDuration"); + if (sOpt) { + CE_CharToWide((char *) sOpt, (u16 *)wTmp); + m_SaxDuration.SetWindowText(wTmp); + } else { + m_SaxDuration.SetWindowText( _T("30") ); + } + + sOpt = gf_cfg_get_key(gpac->m_user.config, "General", "CacheDirectory"); + CE_CharToWide((char *) sOpt, (u16 *)wTmp); + if (sOpt) m_CacheDir.SetWindowText(wTmp); + + OnProgressive(); + return TRUE; +} + +void COptHTTP::OnProgressive() +{ + m_SaxDuration.EnableWindow( m_Progressive.GetCheck() ? TRUE : FALSE ); +} + +void COptHTTP::SaveOptions() +{ + TCHAR wTmp[500]; + char szCacheDir[500]; + COsmo4 *gpac = GetApp(); + + gf_cfg_set_key(gpac->m_user.config, "Downloader", "CleanCache", m_CleanCache.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Downloader", "RestartFiles", m_RestartFile.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "SAXLoader", "Progressive", m_Progressive.GetCheck() ? "yes" : "no"); + + m_SaxDuration.GetWindowText(wTmp, MAX_PATH); + CE_WideToChar((u16 *)wTmp, szCacheDir); + gf_cfg_set_key(gpac->m_user.config, "SAXLoader", "MaxDuration", szCacheDir); + + m_CacheDir.GetWindowText(wTmp, MAX_PATH); + CE_WideToChar((u16 *)wTmp, szCacheDir); + gf_cfg_set_key(gpac->m_user.config, "General", "CacheDirectory", szCacheDir); +} + + + +COptRender::COptRender(CWnd* pParent /*=NULL*/) + : CDialog(COptRender::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptRender) + //}}AFX_DATA_INIT +} + + +void COptRender::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptRender) + DDX_Control(pDX, IDC_AA_LIST, m_Antialias); + DDX_Control(pDX, IDC_FORCE_SIZE, m_ForceSize); + DDX_Control(pDX, IDC_FAST_RENDER, m_HighSpeed); + DDX_Control(pDX, IDC_ZOOM_SCALABLE, m_Scalable); + DDX_Control(pDX, IDC_DIRECTRENDER, m_DirectRender); + DDX_Control(pDX, IDC_BIFS_RATE, m_BIFSRate); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptRender, CDialog) + //{{AFX_MSG_MAP(COptRender) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptRender message handlers + + + +#define NUM_RATES 11 +static char *BIFSRates[11] = +{ + "5.0", + "7.5", + "10.0", + "12.5", + "15.0", + "24.0", + "25.0", + "30.0", + "50.0", + "60.0", + "100.0", +}; + + + +BOOL COptRender::OnInitDialog() +{ + CDialog::OnInitDialog(); + + COsmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "DirectDraw"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_DirectRender.SetCheck(1); + } else { + m_DirectRender.SetCheck(0); + } + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "ScalableZoom"); + if (sOpt && !stricmp(sOpt, "no")) { + m_Scalable.SetCheck(0); + } else { + m_Scalable.SetCheck(1); + } + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "ForceSceneSize"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_ForceSize.SetCheck(1); + } else { + m_ForceSize.SetCheck(0); + } + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "FrameRate"); + if (!sOpt) sOpt = "30.0"; + s32 select = 0; + while (m_BIFSRate.GetCount()) m_BIFSRate.DeleteString(0); + for (s32 i = 0; im_user.config, "Compositor", "HighSpeed"); + m_HighSpeed.SetCheck((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "AntiAlias"); + while (m_Antialias.GetCount()) m_Antialias.DeleteString(0); + + m_Antialias.AddString(_T("None")); + m_Antialias.AddString(_T("Text only")); + m_Antialias.AddString(_T("Complete")); + select = 2; + if (sOpt && !stricmp(sOpt, "Text")) select = 1; + else if (sOpt && !stricmp(sOpt, "None")) select = 0; + m_Antialias.SetCurSel(select); + + return TRUE; +} + + +void COptRender::SaveOptions() +{ + COsmo4 *gpac = GetApp(); + + gf_cfg_set_key(gpac->m_user.config, "Compositor", "DirectDraw", m_DirectRender.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "ScalableZoom", m_Scalable.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "HighSpeed", m_HighSpeed.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "ForceSceneSize", m_ForceSize.GetCheck() ? "yes" : "no"); + + s32 sel = m_BIFSRate.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "FrameRate", BIFSRates[sel]); + + sel = m_Antialias.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "AntiAlias", (sel==0) ? "None" : ( (sel==1) ? "Text" : "All")); +} + + + + +COptRender3D::COptRender3D(CWnd* pParent /*=NULL*/) + : CDialog(COptRender3D::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptRender) + //}}AFX_DATA_INIT +} + + +void COptRender3D::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptRender) + DDX_Control(pDX, IDC_WIRE_MODE, m_WireMode); + DDX_Control(pDX, IDC_DRAW_NORMALS, m_DrawNormals); + DDX_Control(pDX, IDC_USE_3D_REN, m_Use3DRender); + DDX_Control(pDX, IDC_NO_BACKCULL, m_NoBackFace); + DDX_Control(pDX, IDC_EMULATE_POW2, m_EmulatePOW2); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptRender3D, CDialog) + //{{AFX_MSG_MAP(COptRender3D) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +BOOL COptRender3D::OnInitDialog() +{ + CDialog::OnInitDialog(); + + COsmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "ForceOpenGL"); + m_Use3DRender.SetCheck( (sOpt && !strcmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "BackFaceCulling"); + m_NoBackFace.SetCheck( (sOpt && !stricmp(sOpt, "Off")) ? 1 : 0); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "EmulatePOW2"); + m_EmulatePOW2.SetCheck( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + m_WireMode.ResetContent(); + m_WireMode.AddString(_T("Solid Draw")); + m_WireMode.AddString(_T("Wireframe")); + m_WireMode.AddString(_T("Both")); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "Wireframe"); + if (sOpt && !stricmp(sOpt, "WireOnly")) m_WireMode.SetCurSel(1); + else if (sOpt && !stricmp(sOpt, "WireOnSolid")) m_WireMode.SetCurSel(2); + else m_WireMode.SetCurSel(0); + + + m_DrawNormals.ResetContent(); + m_DrawNormals.AddString(_T("Never")); + m_DrawNormals.AddString(_T("Per Face")); + m_DrawNormals.AddString(_T("Per Vertex")); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Compositor", "DrawNormals"); + if (sOpt && !stricmp(sOpt, "PerFace")) m_DrawNormals.SetCurSel(1); + else if (sOpt && !stricmp(sOpt, "PerVertex")) m_DrawNormals.SetCurSel(2); + else m_DrawNormals.SetCurSel(0); + + return TRUE; +} + + +void COptRender3D::SaveOptions() +{ + COsmo4 *gpac = GetApp(); + + u32 sel = m_DrawNormals.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "DrawNormals", (sel==2) ? "PerVertex" : (sel==1) ? "PerFace" : "Never"); + + sel = m_WireMode.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "Wireframe", (sel==2) ? "WireOnSolid" : (sel==1) ? "WireOnly" : "WireNone"); + + gf_cfg_set_key(gpac->m_user.config, "Compositor", "BackFaceCulling", m_NoBackFace.GetCheck() ? "Off" : "On"); + gf_cfg_set_key(gpac->m_user.config, "Compositor", "EmulatePOW2", m_EmulatePOW2.GetCheck() ? "yes" : "no"); +} + + +COptStream::COptStream(CWnd* pParent /*=NULL*/) + : CDialog(COptStream::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptStream) + //}}AFX_DATA_INIT +} + + +void COptStream::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptStream) + DDX_Control(pDX, IDC_REBUFFER_LEN, m_RebufferLen); + DDX_Control(pDX, IDC_REBUFFER, m_Rebuffer); + DDX_Control(pDX, IDC_BUFFER, m_Buffer); + DDX_Control(pDX, IDC_TIMEOUT, m_Timeout); + DDX_Control(pDX, IDC_REORDER, m_Reorder); + DDX_Control(pDX, IDC_RTSP, m_UseRTSP); + DDX_Control(pDX, IDC_PORT, m_Port); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptStream, CDialog) + //{{AFX_MSG_MAP(COptStream) + ON_CBN_SELCHANGE(IDC_PORT, OnSelchangePort) + ON_BN_CLICKED(IDC_RTSP, OnRtsp) + ON_BN_CLICKED(IDC_REBUFFER, OnRebuffer) + ON_EN_UPDATE(IDC_REBUFFER_LEN, OnUpdateRebufferLen) + ON_EN_UPDATE(IDC_BUFFER, OnUpdateBuffer) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptStream message handlers + +BOOL COptStream::OnInitDialog() +{ + CDialog::OnInitDialog(); + + COsmo4 *gpac = GetApp(); + TCHAR wTmp[500]; + const char *sOpt; + + while (m_Port.GetCount()) m_Port.DeleteString(0); + m_Port.AddString(_T("554 (RTSP standard)")); + m_Port.AddString(_T("7070 (RTSP ext)")); + m_Port.AddString(_T("80 (RTSP / HTTP tunnel)")); + m_Port.AddString(_T("8080 (RTSP / HTTP tunnel)")); + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Streaming", "DefaultPort"); + u32 port = 554; + Bool force_rtsp = 0;; + if (sOpt) port = atoi(sOpt); + switch (port) { + case 8080: + m_Port.SetCurSel(3); + force_rtsp = 1; + break; + case 80: + m_Port.SetCurSel(2); + force_rtsp = 1; + break; + case 7070: + m_Port.SetCurSel(1); + break; + default: + m_Port.SetCurSel(0); + break; + } + + Bool use_rtsp = 0; + sOpt = gf_cfg_get_key(gpac->m_user.config, "Streaming", "RTPoverRTSP"); + if (sOpt && !stricmp(sOpt, "yes")) use_rtsp = 1; + + if (force_rtsp) { + m_UseRTSP.SetCheck(1); + m_UseRTSP.EnableWindow(0); + m_Reorder.SetCheck(0); + m_Reorder.EnableWindow(0); + } else { + m_UseRTSP.SetCheck(use_rtsp); + m_UseRTSP.EnableWindow(1); + m_Reorder.EnableWindow(1); + sOpt = gf_cfg_get_key(gpac->m_user.config, "Streaming", "ReorderSize"); + if (sOpt && !stricmp(sOpt, "0")) { + m_Reorder.SetCheck(0); + } else { + m_Reorder.SetCheck(1); + } + } + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Streaming", "RTSPTimeout"); + if (sOpt) { + CE_CharToWide((char *) sOpt, (u16 *)wTmp); + m_Timeout.SetWindowText(wTmp); + } else { + m_Timeout.SetWindowText(_T("30000")); + } + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Network", "BufferLength"); + if (sOpt) { + CE_CharToWide((char *) sOpt, (u16 *)wTmp); + m_Buffer.SetWindowText(wTmp); + } else { + m_Buffer.SetWindowText(_T("3000")); + } + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Network", "RebufferLength"); + u32 buf_len = 0; + if (sOpt) buf_len = atoi(sOpt); + if (buf_len) { + CE_CharToWide((char *) sOpt, (u16 *)wTmp); + m_RebufferLen.SetWindowText(wTmp); + m_Rebuffer.SetCheck(1); + m_RebufferLen.EnableWindow(1); + } else { + m_RebufferLen.SetWindowText(_T("0")); + m_Rebuffer.SetCheck(0); + m_RebufferLen.EnableWindow(0); + } + + return TRUE; +} + + +void COptStream::OnSelchangePort() +{ + s32 sel = m_Port.GetCurSel(); + switch (sel) { + case 3: + case 2: + m_UseRTSP.SetCheck(1); + m_UseRTSP.EnableWindow(0); + m_Reorder.SetCheck(0); + m_Reorder.EnableWindow(0); + break; + case 1: + default: + m_UseRTSP.SetCheck(0); + m_UseRTSP.EnableWindow(1); + m_Reorder.SetCheck(1); + m_Reorder.EnableWindow(1); + break; + } +} + +void COptStream::OnRtsp() +{ + if (m_UseRTSP.GetCheck()) { + m_Reorder.SetCheck(0); + m_Reorder.EnableWindow(0); + } else { + m_Reorder.SetCheck(1); + m_Reorder.EnableWindow(1); + } + +} + +void COptStream::CheckRebuffer() +{ + TCHAR wstr[50]; + char str[50]; + s32 buf, rebuf; + m_Buffer.GetWindowText(wstr, 50); + CE_WideToChar((u16 *)wstr, str); + buf = atoi(str); + m_RebufferLen.GetWindowText(wstr, 50); + CE_WideToChar((u16 *)wstr, str); + rebuf = atoi(str); + if (rebuf*2 > buf) { + rebuf = buf/2; + wsprintf(wstr, _T("%d"), rebuf); + m_RebufferLen.SetWindowText(wstr); + } +} + +void COptStream::OnRebuffer() +{ + if (!m_Rebuffer.GetCheck()) { + m_RebufferLen.EnableWindow(0); + } else { + m_RebufferLen.EnableWindow(1); + CheckRebuffer(); + } +} + +void COptStream::OnUpdateRebufferLen() +{ + CheckRebuffer(); +} + +void COptStream::OnUpdateBuffer() +{ + CheckRebuffer(); +} + +void COptStream::SaveOptions() +{ + COsmo4 *gpac = GetApp(); + Bool force_rtsp = 0; + s32 sel = m_Port.GetCurSel(); + switch (sel) { + case 3: + gf_cfg_set_key(gpac->m_user.config, "Streaming", "DefaultPort", "8080"); + force_rtsp = 1; + break; + case 2: + gf_cfg_set_key(gpac->m_user.config, "Streaming", "DefaultPort", "80"); + force_rtsp = 1; + break; + case 1: + gf_cfg_set_key(gpac->m_user.config, "Streaming", "DefaultPort", "7070"); + break; + default: + gf_cfg_set_key(gpac->m_user.config, "Streaming", "DefaultPort", "554"); + break; + } + + if (force_rtsp) { + gf_cfg_set_key(gpac->m_user.config, "Streaming", "RTPoverRTSP", "yes"); + } else { + gf_cfg_set_key(gpac->m_user.config, "Streaming", "RTPoverRTSP", m_UseRTSP.GetCheck() ? "yes" : "no"); + if (!m_UseRTSP.GetCheck()) gf_cfg_set_key(gpac->m_user.config, "Streaming", "ReorderSize", m_Reorder.GetCheck() ? "30" : "0"); + } + + TCHAR wstr[50]; + char str[50]; + + m_Timeout.GetWindowText(wstr, 50); + CE_WideToChar((u16 *)wstr, str); + gf_cfg_set_key(gpac->m_user.config, "Streaming", "RTSPTimeout", str); + + m_Buffer.GetWindowText(wstr, 50); + CE_WideToChar((u16 *)wstr, str); + gf_cfg_set_key(gpac->m_user.config, "Network", "BufferLength", str); + if (m_Rebuffer.GetCheck()) { + m_RebufferLen.GetWindowText(wstr, 50); + CE_WideToChar((u16 *)wstr, str); + gf_cfg_set_key(gpac->m_user.config, "Network", "RebufferLength", str); + } else { + gf_cfg_set_key(gpac->m_user.config, "Network", "RebufferLength", "0"); + } +} + + + +COptSystems::COptSystems(CWnd* pParent /*=NULL*/) + : CDialog(COptSystems::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptSystems) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void COptSystems::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptSystems) + DDX_Control(pDX, IDC_FORCE_DURATION, m_ForceDuration); + DDX_Control(pDX, IDC_DEC_THREAD, m_Threading); + DDX_Control(pDX, IDC_BIFSDROP, m_BifsAlwaysDrawn); + DDX_Control(pDX, IDC_LANG, m_Lang); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptSystems, CDialog) + //{{AFX_MSG_MAP(COptSystems) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptSystems message handlers + + + +BOOL COptSystems::OnInitDialog() +{ + CDialog::OnInitDialog(); + + COsmo4 *gpac = GetApp(); + const char *sOpt; + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "Language3CC"); + if (!sOpt) sOpt = "eng"; + s32 select = 0; + while (m_Lang.GetCount()) m_Lang.DeleteString(0); + u32 i=0; + while (GF_ISO639_Lang[i]) { + TCHAR szTmp[100]; + /*only use common languages (having both 2- and 3-char code names)*/ + if (GF_ISO639_Lang[i+2][0]) { + CE_CharToWide( (char *)GF_ISO639_Lang[i], (u16 *)szTmp); + m_Lang.AddString(szTmp); + if (sOpt && !stricmp(sOpt, GF_ISO639_Lang[i+1])) select = m_Lang.GetCount() - 1; + } + i+=3; + } + m_Lang.SetCurSel(select); + + + /*system config*/ + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "ThreadingPolicy"); + select = 0; + while (m_Threading.GetCount()) m_Threading.DeleteString(0); + m_Threading.AddString(_T("Single Thread")); + m_Threading.AddString(_T("Mutli Thread")); + if (sOpt && !stricmp(sOpt, "Multi")) select = 1; + m_Threading.AddString(_T("Free")); + if (sOpt && !stricmp(sOpt, "Free")) select = 2; + m_Threading.SetCurSel(select); + + + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "ForceSingleClock"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_ForceDuration.SetCheck(1); + } else { + m_ForceDuration.SetCheck(0); + } + sOpt = gf_cfg_get_key(gpac->m_user.config, "Systems", "AlwaysDrawBIFS"); + if (sOpt && !stricmp(sOpt, "yes")) { + m_BifsAlwaysDrawn.SetCheck(1); + } else { + m_BifsAlwaysDrawn.SetCheck(0); + } + + + return TRUE; +} + + +void COptSystems::SaveOptions() +{ + COsmo4 *gpac = GetApp(); + + s32 sel = m_Lang.GetCurSel(); + u32 i=0; + while (GF_ISO639_Lang[i]) { + /*only use common languages (having both 2- and 3-char code names)*/ + if (GF_ISO639_Lang[i+2][0]) { + if (!sel) break; + sel--; + } + i+=3; + } + gf_cfg_set_key(gpac->m_user.config, "Systems", "LanguageName", GF_ISO639_Lang[i]); + gf_cfg_set_key(gpac->m_user.config, "Systems", "Language3CC", GF_ISO639_Lang[i+1]); + gf_cfg_set_key(gpac->m_user.config, "Systems", "Language2CC", GF_ISO639_Lang[i+2]); + + sel = m_Threading.GetCurSel(); + gf_cfg_set_key(gpac->m_user.config, "Systems", "ThreadingPolicy", (sel==0) ? "Single" : ( (sel==1) ? "Multi" : "Free")); + + /*reset duration flag*/ + gpac->m_duration = (u32) -1; + gf_cfg_set_key(gpac->m_user.config, "Systems", "ForceSingleClock", m_ForceDuration.GetCheck() ? "yes" : "no"); + gf_cfg_set_key(gpac->m_user.config, "Systems", "AlwaysDrawBIFS", m_BifsAlwaysDrawn.GetCheck() ? "yes" : "no"); + +} + diff --git a/applications/osmo4_wce/Options.h b/applications/osmo4_wce/Options.h new file mode 100644 index 0000000..89abecd --- /dev/null +++ b/applications/osmo4_wce/Options.h @@ -0,0 +1,388 @@ +#if !defined(AFX_OPTIONS_H__5C839953_58C0_4D9D_89CE_2820C7686C1B__INCLUDED_) +#define AFX_OPTIONS_H__5C839953_58C0_4D9D_89CE_2820C7686C1B__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// Options.h : header file +// + + +class COptSystems : public CDialog +{ +// Construction +public: + COptSystems(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptSystems) + enum { IDD = IDD_OPT_SYSTEMS }; + CButton m_ForceDuration; + CComboBox m_Threading; + CButton m_BifsAlwaysDrawn; + CComboBox m_Lang; + //}}AFX_DATA + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptSystems) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptSystems) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class COptStream : public CDialog +{ +// Construction +public: + COptStream(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptStream) + enum { IDD = IDD_OPT_STREAM }; + CEdit m_RebufferLen; + CButton m_Rebuffer; + CEdit m_Buffer; + CEdit m_Timeout; + CButton m_Reorder; + CButton m_UseRTSP; + CComboBox m_Port; + //}}AFX_DATA + + + void SaveOptions(); + + void CheckRebuffer(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptStream) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptStream) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangePort(); + afx_msg void OnRtsp(); + afx_msg void OnRebuffer(); + afx_msg void OnUpdateRebufferLen(); + afx_msg void OnUpdateBuffer(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; +class COptRender : public CDialog +{ +// Construction +public: + COptRender(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptRender) + enum { IDD = IDD_OPT_RENDER }; + CComboBox m_Antialias; + CButton m_ForceSize; + CButton m_HighSpeed; + CButton m_Scalable; + CButton m_DirectRender; + CComboBox m_BIFSRate; + //}}AFX_DATA + + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptRender) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptRender) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; +class COptHTTP : public CDialog +{ +// Construction +public: + COptHTTP(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptHTTP) + enum { IDD = IDD_OPT_HTTP }; + CButton m_RestartFile; + CButton m_Progressive; + CButton m_CleanCache; + CButton m_CacheDir; + CEdit m_SaxDuration; + //}}AFX_DATA + + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptHTTP) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptHTTP) + afx_msg void OnBrowseCache(); + afx_msg void OnProgressive(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; +class COptGen : public CDialog +{ +// Construction +public: + COptGen(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptGen) + enum { IDD = IDD_OPT_GEN }; + CButton m_NoBacklight; + CButton m_Fill; + CButton m_Loop; + CButton m_Logs; + //}}AFX_DATA + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptGen) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptGen) + virtual BOOL OnInitDialog(); + afx_msg void OnFileassoc(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; +class COptFont : public CDialog +{ +// Construction +public: + COptFont(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptFont) + enum { IDD = IDD_OPT_FONT }; + CButton m_UseTexture; + CComboBox m_Fonts; + CButton m_BrowseFont; + //}}AFX_DATA + + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptFont) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptFont) + virtual BOOL OnInitDialog(); + afx_msg void OnBrowseFont(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; +class COptDecoder : public CDialog +{ +// Construction +public: + COptDecoder(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptDecoder) + enum { IDD = IDD_OPT_DECODER }; + CComboBox m_Video; + CComboBox m_Audio; + //}}AFX_DATA + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptDecoder) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptDecoder) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +class COptAudio : public CDialog +{ +// Construction +public: + COptAudio(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptAudio) + enum { IDD = IDD_OPT_AUDIO }; + CComboBox m_DriverList; + CButton m_AudioResync; + CEdit m_AudioDur; + CSpinButtonCtrl m_SpinDur; + CButton m_ForceConfig; + CSpinButtonCtrl m_AudioSpin; + CEdit m_AudioEdit; + //}}AFX_DATA + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptAudio) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptAudio) + virtual BOOL OnInitDialog(); + afx_msg void OnForceAudio(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +class COptRender3D : public CDialog +{ +// Construction +public: + COptRender3D(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptRender) + enum { IDD = IDD_OPT_RENDER3D }; + CComboBox m_WireMode; + CComboBox m_DrawNormals; + CButton m_Use3DRender; + CButton m_NoBackFace; + CButton m_EmulatePOW2; + //}}AFX_DATA + + void SaveOptions(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptRender) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptRender) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// +// COptions dialog + +class COptions : public CDialog +{ +// Construction +public: + COptions(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptions) + enum { IDD = IDD_OPTIONS }; + CComboBox m_Selection; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptions) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + COptGen m_general; + COptSystems m_systems; + COptRender m_render; + COptRender3D m_render3D; + COptAudio m_audio; + COptHTTP m_http; + COptFont m_font; + COptStream m_stream; + COptDecoder m_decoder; + + + void HideAll(); + + // Generated message map functions + //{{AFX_MSG(COptions) + virtual BOOL OnInitDialog(); + virtual void OnOK(); + afx_msg void OnSaveopt(); + afx_msg void OnSelchangeCombosel(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_OPTIONS_H__5C839953_58C0_4D9D_89CE_2820C7686C1B__INCLUDED_) diff --git a/applications/osmo4_wce/Osmo4.cpp b/applications/osmo4_wce/Osmo4.cpp new file mode 100644 index 0000000..5b02d14 --- /dev/null +++ b/applications/osmo4_wce/Osmo4.cpp @@ -0,0 +1,699 @@ +// Osmo4.cpp : Defines the class behaviors for the application. +// + +#include "stdafx.h" +#include "Osmo4.h" + +#include +#include +#include "MainFrm.h" +#include "OpenDlg.h" +#include "Options.h" +#include + + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// COsmo4 + +BEGIN_MESSAGE_MAP(COsmo4, CWinApp) + //{{AFX_MSG_MAP(COsmo4) + ON_COMMAND(ID_APP_ABOUT, OnAppAbout) + ON_COMMAND(IDD_CONFIGURE, OnConfigure) + ON_COMMAND(ID_OPEN_FILE, OnOpenFile) + ON_COMMAND(ID_OPEN_URL, OnOpenUrl) + ON_COMMAND(ID_SHORTCUTS, OnShortcuts) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + + + + + +Bool Osmo4CE_EventProc(void *priv, GF_Event *event) +{ + u32 dur; + COsmo4 *app = (COsmo4 *) priv; + CMainFrame *pFrame = (CMainFrame *) app->m_pMainWnd; + /*shutdown*/ + if (!pFrame) return 0; + + switch (event->type) { + case GF_EVENT_MESSAGE: + if (event->message.error!=GF_OK) { + if (event->message.errorconsole_err = event->message.error; + pFrame->console_message = event->message.message; + pFrame->PostMessage(WM_CONSOLEMSG, 0, 0); + } + return 0; + } + if (1) return 0; + /*process user message*/ + pFrame->console_err = GF_OK; + pFrame->console_message = event->message.message; + pFrame->PostMessage(WM_CONSOLEMSG, 0, 0); + break; + case GF_EVENT_SIZE: + break; + case GF_EVENT_SCENE_SIZE: + app->m_scene_width = event->size.width; + app->m_scene_height = event->size.height; + if (!pFrame->m_full_screen) + pFrame->PostMessage(WM_SETSIZE, event->size.width, event->size.height); + break; + case GF_EVENT_CONNECT: + app->m_open = event->connect.is_connected; + break; + case GF_EVENT_DURATION: + dur = (u32) (1000 * event->duration.duration); + if (dur<2000) dur = 0; + app->m_duration = dur; + app->m_can_seek = event->duration.can_seek && dur; + pFrame->m_progBar.m_range_invalidated = 1; + /*by default, don't display timing if not seekable and vice-versa*/ + if (app->m_can_seek != pFrame->m_view_timing) { + pFrame->m_view_timing = app->m_can_seek; + if (!pFrame->m_full_screen) + pFrame->PostMessage(WM_SETSIZE, 0, 0); + } + break; + case GF_EVENT_NAVIGATE: + /*store URL since it may be destroyed, and post message*/ + app->m_navigate_url = event->navigate.to_url; + pFrame->PostMessage(WM_NAVIGATE, NULL, NULL); + return 1; + case GF_EVENT_QUIT: + pFrame->PostMessage(WM_CLOSE, 0L, 0L); + break; + /*ipaq keys*/ + case GF_EVENT_KEYDOWN: + switch (event->key.key_code) { + case GF_KEY_F1: + pFrame->PostMessage(WM_COMMAND, ID_FILE_OPEN); + break; + case GF_KEY_F2: + pFrame->PostMessage(WM_QUIT); + break; + case GF_KEY_F3: + pFrame->PostMessage(WM_COMMAND, ID_FILE_RESTART); + break; + case GF_KEY_F5: + pFrame->PostMessage(WM_COMMAND, ID_VIEW_FULLSCREEN); + break; + case GF_KEY_ENTER: + pFrame->PostMessage(WM_COMMAND, ID_FILE_PAUSE); + break; + case GF_KEY_LEFT: + if (app->m_duration>=2000) { + s32 res = gf_term_get_time_in_ms(app->m_term) - 5*app->m_duration/100; + if (res<0) res=0; + gf_term_play_from_time(app->m_term, res, 0); + } + break; + case GF_KEY_RIGHT: + if (app->m_duration>=2000) { + u32 res = gf_term_get_time_in_ms(app->m_term) + 5*app->m_duration/100; + if (res>=app->m_duration) res = 0; + gf_term_play_from_time(app->m_term, res, 0); + } + break; + case GF_KEY_UP: + if (app->m_duration>=2000) pFrame->PostMessage(WM_COMMAND, ID_FILE_STEP); + break; + case GF_KEY_DOWN: + gf_term_set_option(app->m_term, GF_OPT_REFRESH, 0); + break; + } + break; + case GF_EVENT_DBLCLICK: + pFrame->PostMessage(WM_COMMAND, ID_VIEW_FULLSCREEN); + return 0; + } + + return 0; +} + +COsmo4::COsmo4() + : CWinApp() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance + m_logs = NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only COsmo4 object + +COsmo4 theApp; + +static void osmo4_do_log(void *cbk, u32 level, u32 tool, const char *fmt, va_list list) +{ + FILE *logs = (FILE *) cbk; + if (logs) { + vfprintf(logs, fmt, list); + fflush(logs); + } +} + +void COsmo4::EnableLogs(Bool turn_on) +{ + if (turn_on) { + m_logs = fopen("\\gpac_logs.txt", "wt"); + if (!m_logs) { + MessageBox(NULL, _T("Couldn't open log files at file system root"), _T("Disabling logs"), MB_OK); + turn_on = 0; + } else { + gf_log_set_level(GF_LOG_DEBUG); +// gf_log_set_tools(0xFFFFFFFF); + gf_log_set_tools(GF_LOG_CORE|GF_LOG_NETWORK|GF_LOG_RTP|GF_LOG_SYNC|GF_LOG_CODEC|GF_LOG_MEDIA); + gf_log_set_callback(m_logs, osmo4_do_log); + gf_cfg_set_key(m_user.config, "General", "LogLevel", "debug"); + } + } + if (!turn_on) { + if (m_logs) { + fclose(m_logs); + m_logs = 0; + } + gf_log_set_level(0); + gf_log_set_tools(0); + gf_log_set_callback(NULL, NULL); + gf_cfg_set_key(m_user.config, "General", "LogLevel", "none"); + } +} + +///////////////////////////////////////////////////////////////////////////// +// COsmo4 initialization + +BOOL COsmo4::InitInstance() +{ + if (!AfxSocketInit()) + { + AfxMessageBox(IDP_SOCKETS_INIT_FAILED); + return FALSE; + } + + gf_sys_init(); + + SetRegistryKey(_T("GPAC")); + + m_prev_batt_bl = m_prev_ac_bl = 0; + + m_screen_width = GetSystemMetrics(SM_CXSCREEN); + m_screen_height = GetSystemMetrics(SM_CYSCREEN); + m_menu_height = GetSystemMetrics(SM_CYMENU); + m_scene_width = m_scene_height = 0; + + CMainFrame* pFrame = new CMainFrame; + m_pMainWnd = pFrame; + + pFrame->LoadFrame(IDR_MAINFRAME, WS_VISIBLE, NULL, NULL); + + pFrame->ShowWindow(m_nCmdShow); + pFrame->UpdateWindow(); + + TCHAR w_config_path[MAX_PATH]; + char config_path[MAX_PATH]; + GetModuleFileName(NULL, w_config_path, MAX_PATH); + CE_WideToChar((u16 *) w_config_path, (char *) config_path); + + while (config_path[strlen((char *) config_path)-1] != '\\') config_path[strlen((char *) config_path)-1] = 0; + + /*setup user*/ + memset(&m_user, 0, sizeof(GF_User)); + + /*init config and plugins*/ + m_user.config = gf_cfg_new((const char *) config_path, "GPAC.cfg"); + if (!m_user.config) { + /*create blank config file in the exe dir*/ + unsigned char config_file[MAX_PATH]; + strcpy((char *) config_file, (const char *) config_path); + strcat((char *) config_file, "GPAC.cfg"); + FILE *ft = fopen((const char *) config_file, "wt"); + fclose(ft); + m_user.config = gf_cfg_new((const char *) config_path, "GPAC.cfg"); + if (!m_user.config) { + MessageBox(NULL, _T("GPAC Configuration file not found"), _T("Fatal Error"), MB_OK); + m_pMainWnd->PostMessage(WM_CLOSE); + } + } + + const char *str = gf_cfg_get_key(m_user.config, "General", "LogLevel"); + EnableLogs((str && !strcmp(str, "debug")) ? 1 : 0); + + + str = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + m_user.modules = gf_modules_new(str, m_user.config); + if (!m_user.modules) { + unsigned char str_path[MAX_PATH]; + const char *sOpt; + /*inital launch*/ + m_user.modules = gf_modules_new(config_path, m_user.config); + if (m_user.modules) { + gf_cfg_set_key(m_user.config, "General", "ModulesDirectory", (const char *) config_path); + + sOpt = gf_cfg_get_key(m_user.config, "Compositor", "Raster2D"); + if (!sOpt) gf_cfg_set_key(m_user.config, "Compositor", "Raster2D", "GPAC 2D Raster"); + + + sOpt = gf_cfg_get_key(m_user.config, "General", "CacheDirectory"); + if (!sOpt) { + sprintf((char *) str_path, "%scache", config_path); + gf_cfg_set_key(m_user.config, "General", "CacheDirectory", (const char *) str_path); + } + /*setup UDP traffic autodetect*/ + gf_cfg_set_key(m_user.config, "Network", "AutoReconfigUDP", "yes"); + gf_cfg_set_key(m_user.config, "Network", "UDPTimeout", "10000"); + gf_cfg_set_key(m_user.config, "Network", "BufferLength", "3000"); + + + /*first launch, register all files ext*/ + u32 i; + for (i=0; iCanHandleURL(ifce, "test.test"); + gf_modules_close_interface((GF_BaseInterface *)ifce); + } + } + } + + /*check audio config on windows, force config*/ + sOpt = gf_cfg_get_key(m_user.config, "Audio", "ForceConfig"); + if (!sOpt) { + gf_cfg_set_key(m_user.config, "Audio", "ForceConfig", "yes"); + gf_cfg_set_key(m_user.config, "Audio", "NumBuffers", "2"); + gf_cfg_set_key(m_user.config, "Audio", "TotalDuration", "200"); + } + /*by default use GDIplus, much faster than freetype on font loading*/ + gf_cfg_set_key(m_user.config, "FontEngine", "FontReader", "ft_font"); + + sprintf((char *) str_path, "%sgpac.mp4", config_path); + gf_cfg_set_key(m_user.config, "General", "StartupFile", (const char *) str_path); + + ::MessageBox(NULL, _T("Osmo4/GPAC Setup complete"), _T("Initial launch"), MB_OK); + } + if (! gf_modules_get_count(m_user.modules) ) { + MessageBox(NULL, _T("No plugins available - system cannot work"), _T("Fatal Error"), MB_OK); + m_pMainWnd->PostMessage(WM_QUIT); + } + + /*setup font dir*/ + str = gf_cfg_get_key(m_user.config, "FontEngine", "FontDirectory"); + if (!str || !strlen(str) ) { + strcpy((char *) config_path, "\\Windows"); + gf_cfg_set_key(m_user.config, "FontEngine", "FontDirectory", (const char *) config_path); + } + /*work with iPaq's default fonts ...*/ + str = gf_cfg_get_key(m_user.config, "FontEngine", "FontSerif"); + if (!str) gf_cfg_set_key(m_user.config, "FontEngine", "FontSerif", "Tahoma"); + str = gf_cfg_get_key(m_user.config, "FontEngine", "FontSans"); + if (!str) gf_cfg_set_key(m_user.config, "FontEngine", "FontSans", "Frutiger"); + str = gf_cfg_get_key(m_user.config, "FontEngine", "FontFixed"); + if (!str) gf_cfg_set_key(m_user.config, "FontEngine", "FontFixed", "Courier New"); + + /*check video driver, if none or raw_out use dx_hw by default*/ + str = gf_cfg_get_key(m_user.config, "Video", "DriverName"); + if (!str || !stricmp(str, "raw_out")) { + gf_cfg_set_key(m_user.config, "Video", "DriverName", "gapi"); + } + + m_user.config = m_user.config; + m_user.modules = m_user.modules; + m_user.EventProc = Osmo4CE_EventProc; + m_user.opaque = this; + m_user.os_window_handler = pFrame->m_wndView.m_hWnd; + + + m_term = gf_term_new(&m_user); + if (! m_term) { + MessageBox(NULL, _T("Cannot load MPEG-4 Terminal"), _T("Fatal Error"), MB_OK); + m_pMainWnd->PostMessage(WM_QUIT); + } + + m_stoped = 0; + m_open = 0; + m_can_seek = 0; + m_DoResume = 0; + SetOptions(); + pFrame->SendMessage(WM_SETSIZE, 0, 0); + ShowTaskBar(0); + + CCommandLineInfo cmdInfo; + ParseCommandLine(cmdInfo); + + if (! cmdInfo.m_strFileName.IsEmpty()) { + m_filename = cmdInfo.m_strFileName; + m_pMainWnd->PostMessage(WM_OPENURL); + } else { + str = gf_cfg_get_key(m_user.config, "General", "StartupFile"); + if (str) gf_term_connect(m_term, str); + } + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// COsmo4 message handlers + + + + + +///////////////////////////////////////////////////////////////////////////// +// CAboutDlg dialog used for App About + +class CAboutDlg : public CDialog +{ +public: + CAboutDlg(); + +// Dialog Data + //{{AFX_DATA(CAboutDlg) + enum { IDD = IDD_ABOUTBOX }; + CStatic m_AbtTxt; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAboutDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + //{{AFX_MSG(CAboutDlg) + virtual BOOL OnInitDialog(); // Added for WCE apps + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) +{ + //{{AFX_DATA_INIT(CAboutDlg) + //}}AFX_DATA_INIT +} + +void CAboutDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAboutDlg) + DDX_Control(pDX, IDC_ABT_TEXT, m_AbtTxt); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) + //{{AFX_MSG_MAP(CAboutDlg) + // No message handlers + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COsmo4 commands +// Added for WCE apps + +BOOL CAboutDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + CString str = _T("Osmo4 Player\nGPAC V"); + str += _T(GPAC_VERSION); + str += _T(" (build"); + str += _T(GPAC_BUILD_NUMBER); + str += _T(")"); + m_AbtTxt.SetWindowText(str); + return TRUE; +} + + + +void COsmo4::OnAppAbout() +{ + CAboutDlg aboutDlg; + ShowTaskBar(1); + aboutDlg.DoModal(); + ShowTaskBar(0); +} + + +int COsmo4::ExitInstance() +{ + gf_term_del(m_term); + gf_modules_del(m_user.modules); + gf_cfg_del(m_user.config); + ShowTaskBar(1); + gf_sys_close(); + if (m_logs) fclose(m_logs); + return CWinApp::ExitInstance(); +} + +void COsmo4::SetOptions() +{ + const char *sOpt = gf_cfg_get_key(m_user.config, "General", "Loop"); + m_Loop = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(m_user.config, "General", "FillScreen"); + m_fit_screen = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(m_user.config, "General", "DisableBackLight"); + m_disable_backlight = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP); + sOpt = gf_cfg_get_key(m_user.config, "General", "NoMIMETypeFetch"); + m_no_mime_fetch = (!sOpt || !stricmp(sOpt, "yes")) ? 1 : 0; + + //gf_term_set_option(m_term, GF_OPT_AUDIO_VOLUME, 100); +} + +void COsmo4::Pause() +{ + if (!m_open) return; + + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PLAYING) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + SetBacklightState(0); + } else { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + SetBacklightState(1); + } + ((CMainFrame*)m_pMainWnd)->SetPauseButton(); +} + + +void COsmo4::OnConfigure() +{ + COptions dlg; + + ShowTaskBar(1); + dlg.DoModal(); + ShowTaskBar(0); +} + +void COsmo4::ShowTaskBar(Bool showIt, Bool pause_only) +{ + if (showIt) { + m_DoResume = 0; + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PLAYING) { + m_DoResume = 1; + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + } + gf_term_set_option(m_term, GF_OPT_FREEZE_DISPLAY, 1); + if (!pause_only) { + SHFullScreen(GetForegroundWindow(), SHFS_HIDESTARTICON | SHFS_SHOWTASKBAR| SHFS_SHOWSIPBUTTON); + ::ShowWindow(::FindWindow(_T("HHTaskbar"),NULL), SW_SHOWNA); + } + SetBacklightState(0); + } else { + if (!pause_only) { + SHFullScreen(GetForegroundWindow(), SHFS_HIDESTARTICON | SHFS_HIDETASKBAR| SHFS_HIDESIPBUTTON); + ::ShowWindow(::FindWindow(_T("HHTaskbar"),NULL), SW_HIDE); + } + gf_term_set_option(m_term, GF_OPT_FREEZE_DISPLAY, 0); + gf_term_set_option(m_term, GF_OPT_REFRESH, 0); + if (m_DoResume) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + SetBacklightState(1); + m_DoResume = 0; + } + m_pMainWnd->SetFocus(); + } +} + + +CString COsmo4::GetFileFilter() +{ + u32 keyCount, i; + CString sFiles; + CString sExts; + CString supportedFiles; + + return CString("All Files |*.*|"); + + supportedFiles = "All Known Files|*.m3u;*.pls"; + + sExts = ""; + sFiles = ""; + keyCount = gf_cfg_get_key_count(m_user.config, "MimeTypes"); + for (i=0; i + name*/ + strcpy(szKeyList, opt+1); + sKey = strrchr(szKeyList, '\"'); + if (!sKey) continue; + sKey[0] = 0; + /*get description*/ + sKey = strrchr(szKeyList, '\"'); + if (!sKey) continue; + strcpy(sDesc, sKey+1); + sKey[0] = 0; + sKey = strrchr(szKeyList, '\"'); + if (!sKey) continue; + sKey[0] = 0; + + CE_CharToWide(sDesc, (unsigned short *)swDesc); + CE_CharToWide(szKeyList, (unsigned short *)swKeyList); + + /*if same description for # mime types skip (means an old mime syntax)*/ + if (sFiles.Find((LPCTSTR) swDesc)>=0) continue; + /*if same extensions for # mime types skip (don't polluate the file list)*/ + if (sExts.Find((LPCTSTR) swKeyList)>=0) continue; + + sExts += (LPCTSTR) swKeyList; + sExts += " "; + sFiles += (LPCTSTR) swDesc; + sFiles += "|"; + + first = 1; + + sOpt = CString(szKeyList); + while (1) { + + int pos = sOpt.Find(' '); + CString ext = (pos==-1) ? sOpt : sOpt.Left(pos); + /*WATCHOUT: we do have some "double" ext , eg .wrl.gz - these are NOT supported by windows*/ + if (ext.Find(_T("."))<0) { + if (!first) { + sFiles += ";"; + } else { + first = 0; + } + sFiles += "*."; + sFiles += ext; + + CString sext = ext; + sext += ";"; + if (supportedFiles.Find(sext)<0) { + supportedFiles += ";*."; + supportedFiles += ext; + } + } + + if (sOpt==ext) break; + CString rem; + rem.Format(_T("%s "), (LPCTSTR) ext); + sOpt.Replace((LPCTSTR) rem, _T("")); + } + sFiles += "|"; + } + supportedFiles += "|"; + supportedFiles += sFiles; + //supportedFiles += "M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|All Files |*.*|"; + supportedFiles += "All Files |*.*|"; + return supportedFiles; +} + +void COsmo4::OnOpenFile() +{ + Bool res; + CFileDialog fd(TRUE,NULL,_T("\\"),OFN_HIDEREADONLY, GetFileFilter()); + + ShowTaskBar(1); + res = 0; + if (fd.DoModal()==IDOK) { + res = 1; + m_filename = fd.GetPathName(); + m_DoResume = 0;/*done by term*/ + } + ShowTaskBar(0); + if (res) m_pMainWnd->PostMessage(WM_OPENURL); +} + +void COsmo4::OnOpenUrl() +{ + OpenDlg dlg; + Bool res; + ShowTaskBar(1, 1); + res = 0; + if (dlg.DoModal() == IDOK) { + res = 1; + m_DoResume = 0;/*done by term*/ + } + ShowTaskBar(0, 1); + if (res) m_pMainWnd->PostMessage(WM_OPENURL); +} + +void COsmo4::SetBacklightState(Bool disable) +{ + HKEY hKey = 0; + DWORD dwSize; + DWORD dwValue; + HANDLE hBL; + + if (!m_disable_backlight) return; + + if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("ControlPanel\\Backlight"), 0, 0, &hKey ) != ERROR_SUCCESS) return; + + if (disable) { + dwSize = 4; + RegQueryValueEx(hKey, _T("BatteryTimeout"), NULL, NULL,(unsigned char*) &m_prev_batt_bl, &dwSize); + dwSize = 4; + RegQueryValueEx(hKey, _T("ACTimeout"), NULL, NULL, (unsigned char*) &m_prev_ac_bl,&dwSize); + dwSize = 4; + dwValue = 0xefff ; + RegSetValueEx(hKey, _T("BatteryTimeout"), NULL, REG_DWORD, (unsigned char *)&dwValue, dwSize); + dwSize = 4; + dwValue = 0xefff ; + RegSetValueEx( hKey, _T("ACTimeout"), NULL, REG_DWORD, (unsigned char *)&dwValue, dwSize); + } else { + if (m_prev_batt_bl) { + dwSize = 4; + RegSetValueEx(hKey, _T("BatteryTimeout"), NULL, REG_DWORD, (unsigned char *)&m_prev_batt_bl, dwSize); + } + if (m_prev_ac_bl) { + dwSize = 4; + RegSetValueEx(hKey, _T("ACTimeout"), NULL, REG_DWORD,(unsigned char *)&m_prev_ac_bl, dwSize); + } + } + RegCloseKey(hKey); + hBL = CreateEvent(NULL, FALSE, FALSE, _T("BackLightChangeEvent")); + if (hBL) { + SetEvent(hBL); + CloseHandle(hBL); + } +} + +void COsmo4::OnShortcuts() +{ + ShowTaskBar(1); + + MessageBox(NULL, + _T("Double Click: Fullscreen on/off\n"), + + _T("Osmo4 Shortcuts"), + MB_OK); + + ShowTaskBar(0); +} + diff --git a/applications/osmo4_wce/Osmo4.h b/applications/osmo4_wce/Osmo4.h new file mode 100644 index 0000000..8c842d3 --- /dev/null +++ b/applications/osmo4_wce/Osmo4.h @@ -0,0 +1,111 @@ +// Osmo4.h : main header file for the OSMO4 application +// + +#if !defined(AFX_OSMO4_H__7E4A02D1_F77D_4E97_9E10_032054B29E33__INCLUDED_) +#define AFX_OSMO4_H__7E4A02D1_F77D_4E97_9E10_032054B29E33__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols + +///////////////////////////////////////////////////////////////////////////// +// COsmo4: +// See Osmo4.cpp for the implementation of this class +// + +/*MPEG4 term*/ +#include + +enum { + WM_SCENE_DONE = WM_USER + 1, + WM_NAVIGATE, + WM_SETSIZE, + WM_OPENURL, + WM_CONSOLEMSG, +}; + + +#define IPAQ_TRANSLATE_KEY(vk) (LOWORD(vk) != 0x5b ? LOWORD(vk) : vk ) +/*navigation pad keys*/ +#define VK_IPAQ_LEFT 0x25 +#define VK_IPAQ_UP 0x26 +#define VK_IPAQ_RIGHT 0x27 +#define VK_IPAQ_DOWN 0x28 +/*"enter" key*/ +#define VK_IPAQ_START 0x86 +/*ipaq keys from left to right*/ +#define VK_IPAQ_A 0xC1 +#define VK_IPAQ_B 0xC2 +#define VK_IPAQ_C 0xC3 +#define VK_IPAQ_D 0xC4 +/*record button*/ +#define VK_IPAQ_E 0xC5 + +class COsmo4 : public CWinApp +{ +public: + COsmo4(); + + GF_Terminal *m_term; + GF_User m_user; + CString m_filename; + + u32 m_duration; + CString m_navigate_url; + Bool m_Loop, m_fit_screen, m_can_seek, m_open, m_disable_backlight, m_stoped, m_no_mime_fetch; + void Pause(); + + u32 m_scene_width, m_scene_height, m_reconnect_time; + u32 m_screen_width, m_screen_height, m_menu_height; + /*task bar on/off*/ + void ShowTaskBar(Bool showIt, Bool pause_only = 0); + + CString GetFileFilter(); + + void SetBacklightState(Bool disable); + void EnableLogs(Bool turn_on); + +private: + Bool m_DoResume; + + void SetOptions(); + + /*power management*/ + u32 m_prev_batt_bl, m_prev_ac_bl; + + FILE *m_logs; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COsmo4) + public: + virtual BOOL InitInstance(); + virtual int ExitInstance(); + //}}AFX_VIRTUAL + +// Implementation + + //{{AFX_MSG(COsmo4) + afx_msg void OnAppAbout(); + afx_msg void OnConfigure(); + afx_msg void OnOpenFile(); + afx_msg void OnOpenUrl(); + afx_msg void OnShortcuts(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +inline COsmo4 *GetApp() { return (COsmo4 *)AfxGetApp(); } + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_OSMO4_H__7E4A02D1_F77D_4E97_9E10_032054B29E33__INCLUDED_) diff --git a/applications/osmo4_wce/Osmo4.rc b/applications/osmo4_wce/Osmo4.rc new file mode 100644 index 0000000..487a60a --- /dev/null +++ b/applications/osmo4_wce/Osmo4.rc @@ -0,0 +1,756 @@ +//Microsoft eMbedded Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include "newres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "res\\Osmo4.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Cmdbar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 16 +BEGIN + BUTTON ID_OPEN_FILE + BUTTON ID_FILE_PAUSE + BUTTON ID_FILE_STEP + BUTTON ID_FILE_STOP + BUTTON ID_VIEW_FULLSCREEN +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "Q", ID_APP_EXIT, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + "V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT + "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT + "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 140, 153 +STYLE WS_POPUP | WS_CAPTION +EXSTYLE 0x80000000L +CAPTION "About Osmo4" +FONT 8, "System" +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,15,3,21,20 + CTEXT "Osmo4 player\nGPAC MPEG-4 SDK",IDC_ABT_TEXT,44,4,64,17 + CTEXT "This program is free software and may be distributed according to the terms of the GNU Lesser General Public License", + IDC_STATIC,3,24,131,24 + CTEXT "GPAC Copyright (C) 2000 - 2005 by Jean Le Feuvre All Rights Reserved http://gpac.sourceforge.net", + IDC_STATIC,4,52,129,26 + LTEXT "GPAC (Osmo4 (C) 2002-2005)",IDC_STATIC,11,100,109,8 + LTEXT "Mozilla SpiderMonkey (JavaScript)",IDC_STATIC,11,110, + 109,8 + LTEXT "The FreeType Project",IDC_STATIC,11,120,70,8 + LTEXT "The PNG Group, The I.J.G.",IDC_STATIC,12,130,87,8 + LTEXT "XVID, FAAD, MAD, 3GPP",IDC_STATIC,13,139,80,8 + LTEXT "------------ With many thanks to: -------------", + IDC_STATIC,0,91,139,8 +END + +IDD_OPENFILE DIALOG DISCARDABLE 0, 0, 133, 116 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Select file location" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "OK",IDOK,92,33,27,14 + COMBOBOX IDC_FILELIST,4,17,127,52,CBS_DROPDOWN | CBS_AUTOHSCROLL | + CBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Cancel",IDCANCEL,14,34,27,14 + LTEXT "Enter path to remote file",IDC_STATIC,25,6,80,8 +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "\0" + VALUE "FileDescription", "Osmo4-GPAC\0" + VALUE "FileVersion", "0.4.5\0" + VALUE "InternalName", "Osmo4\0" + VALUE "LegalCopyright", "Copyright (C) ENST 2005-2007\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "Osmo4.EXE\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Osmo4-0.4.5\0" + VALUE "ProductVersion", "0.4.5\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + RIGHTMARGIN, 138 + TOPMARGIN, 1 + END + + IDD_OPENFILE, DIALOG + BEGIN + LEFTMARGIN, 2 + TOPMARGIN, 2 + BOTTOMMARGIN, 113 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menubar +// + +IDR_MENU MENU DISCARDABLE +BEGIN + POPUP "File" + BEGIN + MENUITEM "Open", ID_OPEN_FILE + MENUITEM "Open URL", ID_OPEN_URL + MENUITEM SEPARATOR + MENUITEM "Exit", ID_APP_EXIT + END + POPUP "View" + BEGIN + POPUP "Viewpoint" + BEGIN + MENUITEM "", ID_VIEWPORT_EMPTY + END + POPUP "Navigation" + BEGIN + MENUITEM "Reset", ID_NAV_RESET + MENUITEM SEPARATOR + MENUITEM "None", ID_NAV_NONE + MENUITEM "Walk", ID_NAV_WALK + MENUITEM "Fly", ID_NAV_FLY + MENUITEM "Examine", ID_NAV_EXAMINE + MENUITEM "Slide", ID_NAV_SLIDE + MENUITEM SEPARATOR + MENUITEM "Headlight", ID_NAV_HEADLIGHT + MENUITEM "Gravity", ID_NAV_GRAVITY + POPUP "Collision" + BEGIN + MENUITEM "Off", ID_COLLIDE_OFF + MENUITEM "Regular", ID_COLLIDE_REG + MENUITEM "Displacement", ID_COLLIDE_DISP + END + END + MENUITEM SEPARATOR + MENUITEM "Fullscreen", ID_VIEW_FULLSCREEN + POPUP "Aspect Ratio" + BEGIN + MENUITEM "Keep Original", ID_VIEW_AR_ORIG + MENUITEM "Fill Screen", ID_VIEW_AR_FILL + MENUITEM "Ratio 4/3", ID_VIEW_AR_43 + MENUITEM "Ratio 16/9", ID_VIEW_AR_169 + MENUITEM SEPARATOR + MENUITEM "Fit Screen", ID_VIEW_FIT + END + MENUITEM SEPARATOR + MENUITEM "Time Control", ID_VIEW_TIMING + MENUITEM SEPARATOR + MENUITEM "Settings", IDD_CONFIGURE + END + POPUP "?" + BEGIN + MENUITEM "Shortcuts", ID_SHORTCUTS + MENUITEM "About ...", ID_APP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Data +// + +IDR_MENU SHMENUBAR DISCARDABLE +BEGIN + IDR_MENU, 3, + I_IMAGENONE, ID_FILE, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_FILE, 0, 0, + I_IMAGENONE, ID_VIEW, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_VIEW, 0, 1, + I_IMAGENONE, ID_MENUITEM32789, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_MENUITEM32790, 0, 2, +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_EDIT "File" + IDS_TOOL "Tools" + IDP_SOCKETS_INIT_FAILED "Windows sockets initialization failed." +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDR_MAINFRAME "Osmo4\n\nOsmo4\n\n\nOsmo4.Document\nOsmo4 Document" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + AFX_IDS_IDLEMESSAGE "Ready" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_APP_ABOUT "Display program information, version number and copyright\nAbout" + ID_APP_EXIT "Quit the application; prompts to save documents\nExit" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_NEXT_PANE "Switch to the next window pane\nNext Pane" + ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_WINDOW_SPLIT "Split the active window into panes\nSplit" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDIT_CLEAR "Erase the selection\nErase" + ID_EDIT_CLEAR_ALL "Erase everything\nErase All" + ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" + ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" + ID_EDIT_FIND "Find the specified text\nFind" + ID_EDIT_PASTE "Insert Clipboard contents\nPaste" + ID_EDIT_REPEAT "Repeat the last action\nRepeat" + ID_EDIT_REPLACE "Replace specific text with different text\nReplace" + ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" + ID_EDIT_UNDO "Undo the last action\nUndo" + ID_EDIT_REDO "Redo the previously undone action\nRedo" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCTASKLIST "Activate Task List" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_NEW "New" + IDS_FILE "File" + IDS_MHELP "Help" + IDS_SAVE "Save" + IDS_CUT "Cut" + IDS_COPY "Copy" + IDS_PASTE "Paste" + IDS_ABOUT "About" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_INDICATOR_CAPS "CAP" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_FILE "File" + ID_FILE_RESTART "Restart presentation" + IDS_CAP_MENUITEM32790 "?" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_VIEW "View" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONTROL DIALOG DISCARDABLE 0, 0, 161, 15 +STYLE DS_SETFOREGROUND | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_STATICEDGE +FONT 8, "System" +BEGIN + CONTROL "Slider1",IDC_SLIDER,"msctls_trackbar32",TBS_BOTH | + TBS_NOTICKS | WS_TABSTOP,0,1,101,12 + LTEXT "00:00 FPS 30.00",IDC_TIME,102,3,56,10 +END + +IDD_OPTIONS DIALOG DISCARDABLE 0, 0, 134, 121 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Osmo4 Options" +FONT 8, "System" +BEGIN + PUSHBUTTON "Apply",IDC_SAVEOPT,108,3,24,14 + COMBOBOX IDC_COMBOSEL,3,5,99,80,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "-------------------------------------------------------", + IDC_STATIC,0,17,133,8 +END + +IDD_OPT_GEN DIALOG DISCARDABLE 0, 120, 133, 121 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "System" +BEGIN + CONTROL "Loop at end",IDC_LOOP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,6,28,54,10 + PUSHBUTTON "Make Default MPEG-4 Player",IDC_FILEASSOC,5,90,124,20, + BS_MULTILINE + CONTROL "Stretch display to fill screen",IDC_FILL_SCREEN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,6,45,104,10 + CONTROL "Disable Backlight while playing",IDC_NO_BACKLIGHT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,61,111,10 + CONTROL "Enable logs",IDC_ENABLE_LOGS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,6,74,52,10 +END + +IDD_OPT_RENDER DIALOG DISCARDABLE 0, 20, 133, 121 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "Rendering Frame Rate",IDC_STATIC,27,27,73,8 + COMBOBOX IDC_BIFS_RATE,29,39,68,47,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Direct Rendering",IDC_DIRECTRENDER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,1,64,69,10 + CONTROL "Scalable Zoom",IDC_ZOOM_SCALABLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,70,64,61,10 + CONTROL "Fast Rendering",IDC_FAST_RENDER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,70,79,60,10 + CONTROL "Force Scene Size",IDC_FORCE_SIZE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,1,79,70,10 + LTEXT "Anti-Aliasing",IDC_STATIC,6,103,40,8 + COMBOBOX IDC_AA_LIST,56,99,71,47,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP +END + +IDD_OPT_SYSTEMS DIALOG DISCARDABLE 0, 20, 133, 121 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "Prefered Language for stream selection",IDC_STATIC,3,46, + 64,19 + COMBOBOX IDC_LANG,69,48,60,72,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Decoder Threading",IDC_STATIC,4,75,62,8 + COMBOBOX IDC_DEC_THREAD,69,72,60,30,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Always draw late BIFS frames",IDC_BIFSDROP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,91,111,10 + CONTROL "Force Single Timeline",IDC_FORCE_DURATION,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,106,82,10 +END + +IDD_OPT_VIDEO DIALOG DISCARDABLE 0, 20, 133, 121 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "Graphics Driver",IDC_STATIC,5,54,50,8 + LTEXT "Anti-Aliasing",IDC_STATIC,6,77,40,8 + LTEXT "Video Driver",IDC_STATIC,5,98,40,8 + COMBOBOX IDC_GD_LIST,56,53,71,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_AA_LIST,56,73,71,47,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_VIDEO_LIST,56,95,71,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_OPT_AUDIO DIALOG DISCARDABLE 0, 20, 133, 121 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "System" +BEGIN + CONTROL "Force Audio Config",IDC_FORCE_AUDIO,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,33,77,10 + LTEXT "Number of buffers",IDC_STATIC,5,51,60,8 + LTEXT "Total Duration in ms",IDC_STATIC,4,69,64,8 + EDITTEXT IDC_EDIT_AUDIO,68,48,34,14,ES_AUTOHSCROLL | ES_READONLY + CONTROL "Spin1",IDC_SPIN_AUDIO,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ARROWKEYS | UDS_HORZ,105,49,19,12 + EDITTEXT IDC_AUDIO_DUR,68,67,34,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_DUR,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ARROWKEYS | UDS_HORZ,105,68,19,12 + CONTROL "Disable Audio Resync",IDC_AUDIO_RESYNC,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,85,84,10 + LTEXT "Default Plugin",IDC_STATIC,5,103,45,8 + COMBOBOX IDC_DRIVER_LIST,52,100,73,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_OPT_FONT DIALOG DISCARDABLE 0, 20, 133, 119 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "Font Engine",IDC_STATIC,35,33,39,8 + COMBOBOX IDC_FONT_LIST,12,45,98,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + LTEXT "Systems Font Directory",IDC_STATIC,19,87,76,8 + PUSHBUTTON "...",IDC_BROWSE_FONT,6,98,115,12 + CONTROL "Draw text through texturing",IDC_USE_TEXTURE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,68,107,10 +END + +IDD_OPT_HTTP DIALOG DISCARDABLE 0, 20, 133, 121 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "Cache Directory",IDC_STATIC,35,9,53,8 + PUSHBUTTON "...",IDC_BROWSE_CACHE,7,22,113,14 + CONTROL "Remove temp files",IDC_CLEAN_CACHE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,43,75,10 + CONTROL "Always redownload incomplete cached files", + IDC_RESTART_CACHE,"Button",BS_AUTOCHECKBOX | + BS_MULTILINE | WS_TABSTOP,7,54,102,22 + CONTROL "XML Progressive Load",IDC_SAX_PROGRESSIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,77,85,10 + EDITTEXT IDC_SAX_DURATION,27,89,35,14,ES_CENTER | ES_AUTOHSCROLL | + ES_NUMBER +END + +IDD_OPT_STREAM DIALOG DISCARDABLE 0, 20, 133, 121 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "Default RTSP Port",IDC_STATIC,33,33,58,8 + COMBOBOX IDC_PORT,15,42,95,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "RTP over RTSP",IDC_RTSP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,1,58,64,10 + CONTROL "RTP Reordering",IDC_REORDER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,66,58,66,10 + EDITTEXT IDC_TIMEOUT,1,72,35,14,ES_CENTER | ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms before control timeout",IDC_STATIC,44,75,83,8 + EDITTEXT IDC_BUFFER,1,87,35,14,ES_CENTER | ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms of Media Buffering ",IDC_STATIC,44,90,72,8 + CONTROL "Rebuffer if less than",IDC_REBUFFER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,2,106,78,10 + EDITTEXT IDC_REBUFFER_LEN,81,105,35,12,ES_CENTER | ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms",IDC_STATIC,121,108,10,8 +END + +IDD_OPT_DECODER DIALOG DISCARDABLE 0, 20, 133, 121 +STYLE DS_MODALFRAME | DS_CONTROL | WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "Prefered Audio Output",-1,33,9,70,8 + LTEXT "Prefered Video Output",-1,33,44,80,8 + COMBOBOX IDC_AUDEC_LIST,11,22,109,54,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_VIDEC_LIST,12,57,109,47,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP +END + +IDD_OPT_RENDER3D DIALOG DISCARDABLE 0, 0, 133, 121 +STYLE DS_MODALFRAME | WS_CHILD +FONT 8, "System" +BEGIN + CONTROL "Use 3D Renderer",IDC_USE_3D_REN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,29,121,10 + CONTROL "Disable Backface Culling",IDC_NO_BACKCULL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,41,92,10 + CONTROL "Emulate pow2 textures for video",IDC_EMULATE_POW2, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,1,97,121,10 + LTEXT "Drawing Mode",IDC_STATIC,5,56,46,8 + COMBOBOX IDC_WIRE_MODE,61,54,61,35,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + LTEXT "Draw Normals",IDC_STATIC,5,73,45,8 + COMBOBOX IDC_DRAW_NORMALS,62,72,61,35,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_CONTROL, DIALOG + BEGIN + RIGHTMARGIN, 160 + BOTTOMMARGIN, 14 + END + + IDD_OPTIONS, DIALOG + BEGIN + RIGHTMARGIN, 133 + BOTTOMMARGIN, 120 + END + + IDD_OPT_GEN, DIALOG + BEGIN + RIGHTMARGIN, 132 + BOTTOMMARGIN, 120 + END + + IDD_OPT_RENDER, DIALOG + BEGIN + RIGHTMARGIN, 132 + BOTTOMMARGIN, 120 + END + + IDD_OPT_SYSTEMS, DIALOG + BEGIN + RIGHTMARGIN, 132 + BOTTOMMARGIN, 120 + END + + IDD_OPT_VIDEO, DIALOG + BEGIN + RIGHTMARGIN, 132 + BOTTOMMARGIN, 120 + END + + IDD_OPT_AUDIO, DIALOG + BEGIN + RIGHTMARGIN, 132 + BOTTOMMARGIN, 120 + END + + IDD_OPT_FONT, DIALOG + BEGIN + RIGHTMARGIN, 132 + BOTTOMMARGIN, 118 + END + + IDD_OPT_HTTP, DIALOG + BEGIN + RIGHTMARGIN, 132 + BOTTOMMARGIN, 120 + END + + IDD_OPT_STREAM, DIALOG + BEGIN + RIGHTMARGIN, 132 + BOTTOMMARGIN, 120 + END + + IDD_OPT_DECODER, DIALOG + BEGIN + RIGHTMARGIN, 132 + BOTTOMMARGIN, 120 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "#include ""newres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif //_WIN32\r\n" + "#include ""res\\Osmo4.rc2"" // non-Microsoft eMbedded Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#include ""wceres.rc"" // WCE-specific components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_OPT_RENDER DLGINIT +BEGIN + IDC_BIFS_RATE, 0x403, 8, 0 +0x0035, 0x002e, 0x0030, 0x0000, + IDC_BIFS_RATE, 0x403, 8, 0 +0x0037, 0x002e, 0x0035, 0x0000, + IDC_BIFS_RATE, 0x403, 10, 0 +0x0031, 0x0030, 0x002e, 0x0030, 0x0000, + IDC_BIFS_RATE, 0x403, 10, 0 +0x0031, 0x0032, 0x002e, 0x0035, 0x0000, + IDC_BIFS_RATE, 0x403, 10, 0 +0x0031, 0x0035, 0x002e, 0x0030, 0x0000, + IDC_BIFS_RATE, 0x403, 10, 0 +0x0032, 0x0034, 0x002e, 0x0030, 0x0000, + IDC_BIFS_RATE, 0x403, 10, 0 +0x0032, 0x0035, 0x002e, 0x0030, 0x0000, + IDC_BIFS_RATE, 0x403, 10, 0 +0x0033, 0x0030, 0x002e, 0x0030, 0x0000, + 0 +END + +IDD_OPT_SYSTEMS DLGINIT +BEGIN + IDC_DEC_THREAD, 0x403, 28, 0 +0x0053, 0x0069, 0x006e, 0x0067, 0x006c, 0x0065, 0x0020, 0x0054, 0x0068, +0x0072, 0x0065, 0x0061, 0x0064, 0x0000, + IDC_DEC_THREAD, 0x403, 26, 0 +0x004d, 0x0075, 0x0074, 0x006c, 0x0069, 0x0020, 0x0054, 0x0068, 0x0072, +0x0065, 0x0061, 0x0064, 0x0000, + IDC_DEC_THREAD, 0x403, 10, 0 +0x0046, 0x0072, 0x0065, 0x0065, 0x0000, + 0 +END + +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +#include "res\Osmo4.rc2" // non-Microsoft eMbedded Visual C++ edited resources +#include "afxres.rc" // Standard components +//#include "wceres.rc" // WCE-specific components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/applications/osmo4_wce/ProgressBar.cpp b/applications/osmo4_wce/ProgressBar.cpp new file mode 100644 index 0000000..313e31c --- /dev/null +++ b/applications/osmo4_wce/ProgressBar.cpp @@ -0,0 +1,133 @@ +// ProgressBar.cpp : implementation file +// + +#include "stdafx.h" +#include "Osmo4.h" +#include "ProgressBar.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// ProgressBar dialog + + +ProgressBar::ProgressBar(CWnd* pParent /*=NULL*/) + : CDialog(ProgressBar::IDD, pParent) +{ + //{{AFX_DATA_INIT(ProgressBar) + //}}AFX_DATA_INIT + + m_grabbed = 0; + m_range_invalidated = 0; +} + + +void ProgressBar::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(ProgressBar) + DDX_Control(pDX, IDC_TIME, m_Time); + DDX_Control(pDX, IDC_SLIDER, m_Slider); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(ProgressBar, CDialog) + //{{AFX_MSG_MAP(ProgressBar) + ON_WM_SIZE() + ON_WM_HSCROLL() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// ProgressBar message handlers +#define TEXT_RIGHT_PAD 2 +void ProgressBar::OnSize(UINT nType, int cx, int cy) +{ + u32 tw; + CDialog::OnSize(nType, cx, cy); + + if (!m_Slider.m_hWnd) return; + + RECT rc; + m_Time.GetClientRect(&rc); + tw = rc.right-rc.left; + rc.left = cx - tw - TEXT_RIGHT_PAD; + rc.right = cx - TEXT_RIGHT_PAD; + m_Time.MoveWindow(&rc); + m_Slider.GetClientRect(&rc); + rc.left = 0; + rc.right = cx - tw - TEXT_RIGHT_PAD; + m_Slider.MoveWindow(&rc); +} + +void ProgressBar::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + COsmo4 *app = GetApp(); + + if (pScrollBar->GetDlgCtrlID() == IDC_SLIDER) { + if (!app->m_can_seek) return; + switch (nSBCode) { + case TB_LINEUP: + case TB_LINEDOWN: + case TB_PAGEUP: + case TB_PAGEDOWN: + case TB_THUMBPOSITION: + case TB_THUMBTRACK: + case TB_TOP: + case TB_BOTTOM: + m_grabbed = 1; + break; + case TB_ENDTRACK: + if (!app->m_open) { + SetPosition(0); + } else { + u32 seek_to = m_Slider.GetPos(); + gf_term_play_from_time(app->m_term, seek_to, 0); + } + m_grabbed = 0; + return; + } + } + CDialog::OnHScroll(nSBCode, nPos, pScrollBar); +} + +void ProgressBar::SetPosition(u32 now) +{ + TCHAR swText[20]; + u32 nb_s, nb_m; + COsmo4 *app = GetApp(); + + if (m_range_invalidated) { + if (app->m_can_seek) { + m_Slider.SetRangeMin(0); + m_Slider.SetRangeMax(app->m_duration); + m_Slider.ShowWindow(SW_SHOWNORMAL); + m_Slider.EnableWindow(TRUE); + } else { + m_Slider.ShowWindow(SW_SHOWNORMAL); + m_Slider.EnableWindow(FALSE); + + } + m_range_invalidated = 0; + } + if (now==m_prev_time) return; + + if (nowm_term, 0); + m_prev_time = now; + } + nb_s = now/1000; + nb_m = nb_s/60; + nb_s -= nb_m*60; + wsprintf(swText, _T("%02d:%02d FPS %02.2f"), nb_m, nb_s, m_FPS); + m_Time.SetWindowText(swText); + + if (!m_grabbed) m_Slider.SetPos(now); +} diff --git a/applications/osmo4_wce/ProgressBar.h b/applications/osmo4_wce/ProgressBar.h new file mode 100644 index 0000000..fbd2c57 --- /dev/null +++ b/applications/osmo4_wce/ProgressBar.h @@ -0,0 +1,53 @@ +#if !defined(AFX_PROGRESSBAR_H__85FCAC2B_9C83_4C83_8E66_D712FC88B221__INCLUDED_) +#define AFX_PROGRESSBAR_H__85FCAC2B_9C83_4C83_8E66_D712FC88B221__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// ProgressBar.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// ProgressBar dialog + +class ProgressBar : public CDialog +{ +// Construction +public: + ProgressBar(CWnd* pParent = NULL); // standard constructor + + Bool m_grabbed, m_range_invalidated; + Double m_FPS; + u32 m_prev_time; + void SetPosition(u32 time_ms); + +// Dialog Data + //{{AFX_DATA(ProgressBar) + enum { IDD = IDD_CONTROL }; + CStatic m_Time; + CSliderCtrl m_Slider; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ProgressBar) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(ProgressBar) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PROGRESSBAR_H__85FCAC2B_9C83_4C83_8E66_D712FC88B221__INCLUDED_) diff --git a/applications/osmo4_wce/Resource.h b/applications/osmo4_wce/Resource.h new file mode 100644 index 0000000..588802a --- /dev/null +++ b/applications/osmo4_wce/Resource.h @@ -0,0 +1,170 @@ +//{{NO_DEPENDENCIES}} +// Microsoft eMbedded Visual C++ generated include file. +// Used by Osmo4.rc +// +#define IDD_ABOUTBOX 100 +#define IDM_MENU 101 +#define IDS_EDIT 102 +#define IDS_TOOL 103 +#define IDP_SOCKETS_INIT_FAILED 104 +#define IDR_MAIN_EDIT 105 +#define IDR_MAIN_TOOL 106 +#define IDI_PAUSE 120 +#define IDD_OPT_HTTP 121 +#define IDR_MAINFRAME 128 +#define IDD_OPT_AUDIO 129 +#define IDD_OPENFILE 130 +#define IDD_CONTROL 131 +#define IDD_OPTIONS 132 +#define IDD_OPT_GEN 133 +#define IDD_OPT_RENDER 134 +#define IDD_OPT_SYSTEMS 135 +#define IDD_OPT_VIDEO 136 +#define IDI_PLAY 137 +#define IDD_OPT_FONT 138 +#define IDI_STOP 139 +#define IDD_OPT_STREAM 140 +#define IDR_MENU 141 +#define IDD_OPT_RENDER3D 142 +#define IDD_OPT_DECODER 143 +#define IDC_FILELIST 1000 +#define IDC_BROWSE 1001 +#define IDC_SEEK 1002 +#define IDC_PLAY 1003 +#define IDC_STOP 1004 +#define IDC_OPT_SET_NAME 1005 +#define IDC_SAVEOPT 1006 +#define IDC_LOOP 1007 +#define IDC_AUTOSTART 1008 +#define IDC_NO_CONSOLE 1009 +#define IDC_FILEASSOC 1010 +#define IDC_BIFS_RATE 1011 +#define IDC_DIRECTRENDER 1012 +#define IDC_ZOOM_SCALABLE 1013 +#define IDC_FAST_RENDER 1014 +#define IDC_FORCE_SIZE 1015 +#define IDC_COLOR 1016 +#define IDC_LANG 1017 +#define IDC_DEC_THREAD 1018 +#define IDC_BIFSDROP 1019 +#define IDC_FORCE_DURATION 1020 +#define IDC_SLIDER 1021 +#define IDC_GD_LIST 1022 +#define IDC_AA_LIST 1023 +#define IDC_VIDEO_LIST 1024 +#define IDC_FORCE_AUDIO 1025 +#define IDC_EDIT_AUDIO 1026 +#define IDC_SPIN_AUDIO 1027 +#define IDC_AUDIO_FPS 1028 +#define IDC_SPIN_DUR 1029 +#define IDC_AUDIO_RESYNC 1030 +#define IDC_DRIVER_LIST 1031 +#define IDC_FONT_LIST 1032 +#define IDC_BROWSE_FONT 1033 +#define IDC_BROWSE_CACHE 1034 +#define IDC_CLEAN_CACHE 1035 +#define IDC_RESTART_CACHE 1036 +#define IDC_PORT 1037 +#define IDC_RTSP 1038 +#define IDC_REORDER 1039 +#define IDC_TIMEOUT 1040 +#define IDC_BUFFER 1041 +#define IDC_REBUFFER 1042 +#define IDC_REBUFFER_LEN 1043 +#define IDC_VERSION 1044 +#define IDC_AUDEC_LIST 1046 +#define IDC_VIDEC_LIST 1047 +#define IDC_COMBOSEL 1048 +#define IDC_TIME 1049 +#define IDC_ABT_TEXT 1050 +#define IDC_CTRL_PLAY 1051 +#define IDC_USE_TEXTURE 1052 +#define IDC_USE_3D_REN 1053 +#define IDC_NO_BACKCULL 1054 +#define IDC_CTRL_STOP 1055 +#define IDC_EMULATE_POW2 1056 +#define IDC_WIRE_MODE 1057 +#define IDC_SAX_DURATION 1058 +#define IDC_DRAW_NORMALS 1059 +#define IDC_SPIN_OPT 1060 +#define IDC_AUDIO_DUR 1061 +#define IDC_SPIN_FPS 1062 +#define IDC_FILL_SCREEN 1063 +#define IDC_VIEW_FPS 1064 +#define IDC_NO_BACKLIGHT 1065 +#define IDC_SAX_PROGRESSIVE 1066 +#define IDC_ENABLE_LOGS 1067 +#define IDS_CAP_FILE 32772 +#define ID_FILE_RESTART 32773 +#define ID_FPS_DISP 32774 +#define IDD_CONFIGURE 32775 +#define ID_VIEW_CONTROL 32776 +#define ID_FILE_PAUSE 32777 +#define ID_FILE_STEP 32778 +#define ID_FILE_STOP 32779 +#define ID_VIEW_FULLSCREEN 32780 +#define ID_FILE 32781 +#define ID_MENUITEM32789 32782 +#define IDS_CAP_MENUITEM32790 32783 +#define ID_VIEW 32784 +#define IDS_CAP_VIEW 32785 +#define ID_OPEN_FILE 32786 +#define ID_OPEN_URL 32787 +#define ID_SHORTCUTS 32788 +#define ID_VIEW_FIT 32789 +#define ID_NAV_NONE 32806 +#define ID_NAV_SLIDE 32807 +#define ID_NAV_RESET 32808 +#define ID_NAV_WALK 32810 +#define ID_NAV_FLY 32811 +#define ID_NAV_EXAMINE 32812 +#define ID_NAV_GRAVITY 32813 +#define ID_COLLIDE_OFF 32815 +#define ID_COLLIDE_REG 32816 +#define ID_COLLIDE_DISP 32817 +#define ID_VIEWPORT_EMPTY 32818 +#define ID_NAV_HEADLIGHT 32819 +#define ID_VP_0 32820 +#define ID_VP_1 32821 +#define ID_VP_2 32822 +#define ID_VP_3 32823 +#define ID_VP_4 32824 +#define ID_VP_5 32825 +#define ID_VP_6 32826 +#define ID_VP_7 32827 +#define ID_VP_8 32828 +#define ID_VP_9 32829 +#define ID_VP_10 32830 +#define ID_VP_11 32831 +#define ID_VP_12 32832 +#define ID_VP_13 32833 +#define ID_VP_14 32834 +#define ID_VP_15 32835 +#define ID_VP_16 32836 +#define ID_VP_17 32837 +#define ID_VP_18 32838 +#define ID_VP_19 32839 +#define ID_VIEW_AR_ORIG 32890 +#define ID_VIEW_AR_FILL 32891 +#define ID_VIEW_AR_43 32892 +#define ID_VIEW_AR_169 32893 +#define ID_VIEW_TIMING 32894 +#define IDS_NEW 65000 +#define IDS_FILE 65001 +#define IDS_MHELP 65002 +#define IDS_SAVE 65003 +#define IDS_CUT 65004 +#define IDS_COPY 65005 +#define IDS_PASTE 65006 +#define IDS_ABOUT 65007 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 143 +#define _APS_NEXT_COMMAND_VALUE 32820 +#define _APS_NEXT_CONTROL_VALUE 1059 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/applications/osmo4_wce/StdAfx.cpp b/applications/osmo4_wce/StdAfx.cpp new file mode 100644 index 0000000..e6caa79 --- /dev/null +++ b/applications/osmo4_wce/StdAfx.cpp @@ -0,0 +1,6 @@ +// stdafx.cpp : source file that includes just the standard includes +// Osmo4.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + diff --git a/applications/osmo4_wce/StdAfx.h b/applications/osmo4_wce/StdAfx.h new file mode 100644 index 0000000..c78f502 --- /dev/null +++ b/applications/osmo4_wce/StdAfx.h @@ -0,0 +1,40 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__C81DFFE9_41A3_474E_A044_3A730DB4FFD6__INCLUDED_) +#define AFX_STDAFX_H__C81DFFE9_41A3_474E_A044_3A730DB4FFD6__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#if (_WIN32_WCE <= 200) +#error This project does not support MFCCE 2.00 or earlier, because it requires CControlBar, available only in MFCCE 2.01 or later +#endif + +#if (_WIN32_WCE <= 211) +#error This project can not be built for H/PC Pro 2.11 or earlier platforms. +#endif + + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions + +#if defined(_AFXDLL) +#include // MFC support for Internet Explorer 4 Common Controls +#endif + +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + +#include // MFC socket extensions + +//{{AFX_INSERT_LOCATION}} +// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__C81DFFE9_41A3_474E_A044_3A730DB4FFD6__INCLUDED_) diff --git a/applications/osmo4_wce/newres.h b/applications/osmo4_wce/newres.h new file mode 100644 index 0000000..31c3a43 --- /dev/null +++ b/applications/osmo4_wce/newres.h @@ -0,0 +1,28 @@ +#ifndef __NEWRES_H__ +#define __NEWRES_H__ + +#define SHMENUBAR RCDATA +#if !(defined(_WIN32_WCE_PSPC) && (_WIN32_WCE >= 300)) + #undef HDS_HORZ + #undef HDS_BUTTONS + #undef HDS_HIDDEN + + #include + // for MenuBar + #define I_IMAGENONE (-2) + #define NOMENU 0xFFFF + #define IDS_SHNEW 1 + #define IDM_SHAREDNEW 10 + #define IDM_SHAREDNEWDEFAULT 11 + + // for Tab Control + #define TCS_SCROLLOPPOSITE 0x0001 // assumes multiline tab + #define TCS_BOTTOM 0x0002 + #define TCS_RIGHT 0x0002 + #define TCS_VERTICAL 0x0080 + #define TCS_MULTISELECT 0x0004 // allow multi-select in button mode + #define TCS_FLATBUTTONS 0x0008 +#endif //_WIN32_WCE_PSPC + + +#endif //__NEWRES_H__ diff --git a/applications/osmo4_wce/res/Cmdbar.bmp b/applications/osmo4_wce/res/Cmdbar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..90764e4ac4815fd667742681ca767037578c6fa9 GIT binary patch literal 886 zcmcJNK@!3s5CpNbDmnTL-{IXq_ykUgM{fG8{0w_`2~Z|gIk|=n%ghb~@&1_3!D1rc z7*EEPaS9hQQE)H%oI>CUDfx@cG%U-)TR^raz8#=!ENbY0f2Lsm7@m0sf zZRR{wpR2wci_r6_D?hT)D1mZb4ASt!4~;*Md}q5Tzn@+G&JUfEZ~8m_1+V_uS@M-% s=)He~8$U|E<6GJBg`$s;-<;Kr?{poS;4}s1>lMTo4)XecJD_dgFMk0!djJ3c literal 0 HcmV?d00001 diff --git a/applications/osmo4_wce/res/Osmo4.ico b/applications/osmo4_wce/res/Osmo4.ico new file mode 100644 index 0000000000000000000000000000000000000000..96d9091d07264eb11ae7347b213d673a6961cc51 GIT binary patch literal 1078 zcmb7DJ8Hu~5Ph-@LXD+1l?8#|E2OfI6vzR*;6h!ZGKN5|BBLBc>QcINz`V0$q6QufMM=!G7K_ej6W!;qG=kQ z3Rw4twbID^$t7`%vJL4JaOP%H2Tw2uhff|#6jmk=R}oC-pqt^)$ZM3LFDn=s!Y);I z414{`&SE!Z(fHjywNw7txIflM7~;H(`_(t@I~2I5Ucc9);BW8Va)p-wyTFqZBKfB% z!BHF(9MN<@p2K#AfOf#zF1OYs_6V#Fz+w-~cI71RmZP*YNxC)3x&>Ns1@w_$P12|2 zsu}lHm-f{z@7IULwmq)e?P0O*_OrBmnx^Wdp6aa`n&oD4wD!Cpm*lwT4fWe(;1`_x z%>+q9<$rrFs92m)xP2o`)Me6v%%PUme?CK<>!l_tbj4wg+LH9C?Q) + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + Osmo4 + CFBundleGetInfoString + Osmo4 0.4.2 + CFBundleIconFile + Osmo + CFBundleIdentifier + org.gpac.Osmo4 + CFBundleInfoDictionaryVersion + 0.4.2 + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.4.2 + CFBundleSignature + Omo4 + CFBundleVersion + 0.4.2 + NSHumanReadableCopyright + Copyright (c) 2003-2005 gpac + NSMainNibFile + NSMainNibFile + NSPrincipalClass + NSApplication + + diff --git a/applications/osmo4_wx/Darwin.InfoPlist.strings b/applications/osmo4_wx/Darwin.InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..2efc670b7db95be4b0ebd929b71de20bda6a0430 GIT binary patch literal 456 zcmZ{g$qK?i5Jcls zij2nEX(?A*t#y?zkJ_1-=s;zsm}rOr{}H!<+|oYPA9SR;L48S86__Qyp8D$QgxW*r zYXUza8g)2jO;Ja1n&j;qy`vS~1p55Pi9;p9Rje>$*|=$N>;l#R!ZCB6NBFw=pWHzG z(5t-I!_}TypB_RyKBupvvDG3A>ACFEdOMlO(U2`fRAbHX;j#}`i*zS>tk-t(I)$%1 E0HUT#W&i*H literal 0 HcmV?d00001 diff --git a/applications/osmo4_wx/Darwin.Osmo.icns b/applications/osmo4_wx/Darwin.Osmo.icns new file mode 100644 index 0000000000000000000000000000000000000000..a598061aa6d15577ad67469469bec642644b3eca GIT binary patch literal 38570 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 literal 0 HcmV?d00001 diff --git a/applications/osmo4_wx/Makefile b/applications/osmo4_wx/Makefile new file mode 100644 index 0000000..a0841d7 --- /dev/null +++ b/applications/osmo4_wx/Makefile @@ -0,0 +1,91 @@ +include ../../config.mak + +vpath %.cpp $(SRC_PATH)/applications/osmo4_wx + +CFLAGS= $(CPPFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +ifeq ($(GPACREADONLY), yes) +CFLAGS+=-DGPAC_READ_ONLY +endif + +#common obj +OBJS= wxOsmo4.o wxGPACControl.o fileprops.o Playlist.o menubtn.o + +ifeq ($(BUILD_INSTALL), yes) +INSTALL_FLAGS=-DGPAC_MODULES_PATH=\"$(moddir)\" +else +INSTALL_FLAGS= +endif + +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=Osmo4$(EXE) +else +EXT= +PROG=Osmo4 +endif + + +SRCS := $(OBJS:.o=.cpp) + +all: $(PROG) + +Osmo4$(EXE): $(OBJS) + $(CC) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) -L../../bin/gcc -lgpac $(WX_LFLAGS) -lz + +%.o: %.cpp + $(CXX) $(CFLAGS) $(INSTALL_FLAGS) $(WX_CFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJS) ../../bin/gcc/$(PROG) + +install: +ifeq ($(CONFIG_DARWIN),yes) + mkdir -p $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/MacOS + mkdir -p $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/Resources/English.lproj + cp ./Darwin.Info.plist \ + $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/Info.plist + cp ./Darwin.InfoPlist.strings \ + $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/Resources/English.lproj/InfoPlist.strings + cp ./Darwin.Osmo.icns \ + $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/Resources/Osmo.icns + install -m 755 $(INSTFLAGS) ../../bin/gcc/Osmo4 \ + $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/MacOS + echo -n 'APPLOsm4' > $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/PkgInfo +else + rm -f wxOsmo4.o + $(MAKE) -override BUILD_INSTALL=yes all + mkdir -p $(DESTDIR)$(prefix)/bin + install -m 755 $(INSTFLAGS) ../../bin/gcc/Osmo4 "$(DESTDIR)$(prefix)/bin" +endif + +uninstall: + rm -rf $(DESTDIR)$(prefix)/bin/Osmo4 + +dep: + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif + diff --git a/applications/osmo4_wx/Playlist.cpp b/applications/osmo4_wx/Playlist.cpp new file mode 100644 index 0000000..6147702 --- /dev/null +++ b/applications/osmo4_wx/Playlist.cpp @@ -0,0 +1,826 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#include "wxOsmo4.h" +#include "Playlist.h" + + +#include "playlist.xpm" + +PLEntry::PLEntry(wxString url) +{ + m_url = strdup(url.mb_str(wxConvUTF8)); + Bool is_remote = 0;; + wxCharBuffer the_url = (const char *) url.mb_str(wxConvUTF8); + const char *_url = strstr(the_url, "://"); + if (_url) {_url += 3; is_remote = 1; } + else _url = (const char *) the_url; + + char *str = (char*)strrchr(_url, '\\'); + if (!str) str = (char*)strrchr(_url, '/'); + if (str && strlen(str+1)) { + m_disp_name = strdup(str+1); + str = strrchr(m_disp_name, '.'); + if (str) str[0] = 0; + } else { + m_disp_name = strdup(_url); + if (!is_remote) { + str = strrchr(m_disp_name, '.'); + if (str) str[0] = 0; + } + } + m_duration = 0; + m_bIsDead = 0; + m_bIsPlaying = 0; + m_bIsSelected = 0; +} + +PLEntry::~PLEntry() +{ + if (m_url) free(m_url); + if (m_disp_name) free(m_disp_name); + +} + +wxPlaylist::wxPlaylist(wxWindow *parent) + : wxFrame(parent, -1, wxString(_T("Osmo4 Playlist")), wxDefaultPosition, wxDefaultSize, + wxCLOSE_BOX | wxSYSTEM_MENU | wxCAPTION | wxRESIZE_BORDER) +{ + + m_pApp = (wxOsmo4Frame *)parent; + + m_pOpen = new wxBitmap(pl_open); + m_pSave = new wxBitmap(pl_save); + m_pAdd = new wxBitmap(pl_add); + m_pRem = new wxBitmap(pl_rem); + m_pUp = new wxBitmap(pl_up); + m_pDown = new wxBitmap(pl_down); + m_pSort = new wxBitmap(pl_sort); + + m_pToolBar = CreateToolBar(wxTB_HORIZONTAL); + m_pAddBut = new wxMenuButton(m_pToolBar, ID_PL_ADD_FILE, *m_pAdd); + wxMenu *menu = new wxMenu(); + menu->Append(ID_PL_ADD_URL, wxT("&Url")); + menu->Append(ID_PL_ADD_DIR, wxT("&Directory")); + menu->Append(ID_PL_ADD_DIR_REC, wxT("&Directory and subfolders")); + m_pAddBut->AssignMenu(menu); + m_pAddBut->SetToolTip(wxString(wxT("Add Files"))); + + m_pRemBut = new wxMenuButton(m_pToolBar, ID_PL_REM_FILE, *m_pRem); + menu = new wxMenu(); + menu->Append(ID_PL_REM_ALL, wxT("&Clear")); + menu->Append(ID_PL_REM_DEAD, wxT("&Remove dead entries")); + m_pRemBut->AssignMenu(menu); + m_pRemBut->SetToolTip(wxString(wxT("Remove Selected Files"))); + + m_pSortBut = new wxMenuButton(m_pToolBar, ID_PL_SORT_FILE, *m_pSort); + menu = new wxMenu(); + menu->Append(ID_PL_SORT_TITLE, wxT("&Sort by Title")); + menu->Append(ID_PL_SORT_FILE, wxT("&Sort by file name")); + menu->Append(ID_PL_SORT_DUR, wxT("&Sort by Duration")); + menu->AppendSeparator(); + menu->Append(ID_PL_REVERSE, wxT("&Reverse")); + menu->Append(ID_PL_RANDOMIZE, wxT("&Randomize")); + m_pSortBut->AssignMenu(menu); + m_pSortBut->SetToolTip(wxString(wxT("Sort Playlist by filename"))); + + m_pToolBar->AddTool(ID_PL_OPEN, wxT(""), *m_pOpen, wxT("Open Playlist")); + m_pToolBar->AddTool(ID_PL_SAVE, wxT(""), *m_pSave, wxT("Save Playlist")); + m_pToolBar->AddSeparator(); + m_pToolBar->AddControl(m_pAddBut); + m_pToolBar->AddControl(m_pRemBut); + m_pToolBar->AddSeparator(); + m_pToolBar->AddTool(ID_PL_UP, wxT(""), *m_pUp, wxT("Moves Selected Files Up")); + m_pToolBar->AddTool(ID_PL_DOWN, wxT(""), *m_pDown, wxT("Moves Selected Files Down")); + m_pToolBar->AddSeparator(); + m_pToolBar->AddControl(m_pSortBut); + m_pToolBar->Realize(); + + m_FileList = new wxListCtrl(this, ID_FILE_LIST, wxDefaultPosition, wxDefaultSize, wxLC_REPORT); + + m_FileList->InsertColumn(0, wxT(""), wxLIST_FORMAT_LEFT, 1); + m_FileList->InsertColumn(1, wxT("Title"), wxLIST_FORMAT_LEFT, 1); + m_FileList->InsertColumn(2, wxT("Duration"), wxLIST_FORMAT_LEFT, 1); + + m_entries = gf_list_new(); + m_cur_entry = -1; + m_all_dead_entries = -1; + + SetSize(220, 380); + Centre(); +} + +wxPlaylist::~wxPlaylist() +{ + Clear(); + gf_list_del(m_entries); + delete m_pAddBut; + delete m_pRemBut; + delete m_pSortBut; + delete m_pOpen; + delete m_pSave; + delete m_pAdd; + delete m_pRem; + delete m_pUp; + delete m_pDown; + delete m_pSort; +} + + +BEGIN_EVENT_TABLE(wxPlaylist, wxWindow) + EVT_CLOSE(wxPlaylist::OnClose) + EVT_SIZE(wxPlaylist::OnSize) + EVT_TOOL(ID_PL_ADD_FILE, wxPlaylist::OnAddFile) + EVT_TOOL(ID_PL_ADD_URL, wxPlaylist::OnAddURL) + EVT_TOOL(ID_PL_ADD_DIR, wxPlaylist::OnAddDir) + EVT_TOOL(ID_PL_ADD_DIR_REC, wxPlaylist::OnAddDirRec) + EVT_TOOL(ID_PL_REM_FILE, wxPlaylist::OnRemFile) + EVT_TOOL(ID_PL_REM_ALL, wxPlaylist::OnRemAll) + EVT_TOOL(ID_PL_REM_DEAD, wxPlaylist::OnRemDead) + EVT_TOOL(ID_PL_UP, wxPlaylist::OnSelUp) + EVT_TOOL(ID_PL_DOWN, wxPlaylist::OnSelDown) + EVT_TOOL(ID_PL_SAVE, wxPlaylist::OnSave) + EVT_TOOL(ID_PL_OPEN, wxPlaylist::OnOpen) + EVT_MENU(ID_PL_PLAY, wxPlaylist::OnPlay) + EVT_MENU(ID_PL_RANDOMIZE, wxPlaylist::OnRandomize) + EVT_MENU(ID_PL_REVERSE, wxPlaylist::OnReverseList) + EVT_MENU(ID_PL_SEL_REV, wxPlaylist::OnReverseSelection) + EVT_MENU(ID_PL_SORT_TITLE, wxPlaylist::OnSortTitle) + EVT_MENU(ID_PL_SORT_FILE, wxPlaylist::OnSortFile) + EVT_MENU(ID_PL_SORT_DUR, wxPlaylist::OnSortDuration) + EVT_LIST_ITEM_ACTIVATED(ID_FILE_LIST, wxPlaylist::OnItemActivate) + EVT_LIST_ITEM_RIGHT_CLICK(ID_FILE_LIST, wxPlaylist::OnRightClick) +END_EVENT_TABLE() + +void wxPlaylist::OnClose(wxCloseEvent &event) +{ + if (event.CanVeto()) { + event.Veto(); + Hide(); + } +} + +void wxPlaylist::OnSize(wxSizeEvent &event) +{ + wxSize s = event.GetSize(); + m_FileList->SetSize(0, 0, s.GetWidth()-2, s.GetHeight()); + m_FileList->SetColumnWidth(0, 30); + m_FileList->SetColumnWidth(2, 60); + m_FileList->SetColumnWidth(1, s.GetWidth()-96); +} + +void wxPlaylist::Clear() +{ + m_FileList->DeleteAllItems(); + while (gf_list_count(m_entries)) { + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, 0); + gf_list_rem(m_entries, 0); + delete ple; + } + m_cur_entry = -1; +} + +void wxPlaylist::ClearButPlaying() +{ + PLEntry *p = NULL; + if (m_cur_entry >= 0) { + p = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + gf_list_rem(m_entries, m_cur_entry); + } + Clear(); + if (p) { + gf_list_add(m_entries, p); + m_cur_entry = 0; + } + RefreshList(); +} + +void wxPlaylist::UpdateEntry(u32 idx) +{ + char szText[20]; + + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, idx); + if (idx+1<10) sprintf(szText, "00%d", idx+1); + else if (idx+1<100) sprintf(szText, "0%d", idx+1); + else sprintf(szText, "%d", idx+1); + m_FileList->SetItem(idx, 0, wxString(szText, wxConvUTF8)); + + wxString str; + if (ple->m_bIsDead) str = wxT("!! ") + wxString(ple->m_disp_name, wxConvUTF8) + wxT(" (DEAD)!!)"); + else if (ple->m_bIsPlaying) str = wxT(">> ") + wxString(ple->m_disp_name, wxConvUTF8) + wxT(" >>"); + else str = wxString(ple->m_disp_name, wxConvUTF8); + m_FileList->SetItem(idx, 1, str); + + if (ple->m_duration) { + u32 h = (u32) (ple->m_duration / 3600); + u32 m = (u32) (ple->m_duration / 60) - h*60; + u32 s = (u32) (ple->m_duration) - h*3600 - m*60; + m_FileList->SetItem(idx, 2, wxString::Format(wxT("%02d:%02d:%02d"), h, m, s) ); + } else { + m_FileList->SetItem(idx, 2, wxT("Unknown")); + } +} + +void wxPlaylist::RefreshList() +{ + u32 i, top_idx; + char szPath[GF_MAX_PATH]; + Bool first_sel; + + top_idx = m_FileList->GetTopItem(); + m_FileList->DeleteAllItems(); + + first_sel = 0; + for (i=0; iInsertItem(i, wxT("")); + /*cast for 64-bit compilation*/ + m_FileList->SetItemData(i, (unsigned long) ple); + UpdateEntry(i); + + if (ple->m_bIsPlaying) m_cur_entry = i; + + if (ple->m_bIsSelected) { + m_FileList->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED | wxLIST_MASK_STATE); + ple->m_bIsSelected = 0; + /*ensure first item of selection is visible*/ + if (!first_sel) { + first_sel = 1; + top_idx = i; + } + } + } + + if (m_cur_entry >= (s32) gf_list_count(m_entries)-1) m_cur_entry = gf_list_count(m_entries)-1; + else { + s32 last_idx = top_idx + m_FileList->GetCountPerPage(); + m_FileList->EnsureVisible(top_idx); + if (gf_list_count(m_entries)<1+ (u32) last_idx) last_idx = gf_list_count(m_entries)-1; + m_FileList->EnsureVisible(last_idx); + } + + strcpy((char *) szPath, m_pApp->szAppPath); +#ifdef WIN32 + strcat(szPath, "gpac_pl.m3u"); +#else + strcat(szPath, ".gpac_pl.m3u"); +#endif + Save(szPath, 1); +} + +void wxPlaylist::OnAddFile(wxCommandEvent &WXUNUSED(event)) +{ + wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), m_pApp->GetFileFilter(), wxOPEN | wxCHANGE_DIR | /*wxHIDE_READONLY |*/ wxMULTIPLE); + + if (dlg.ShowModal() == wxID_OK) { + wxArrayString stra; + dlg.GetPaths(stra); + for (u32 i=0; im_pApp->m_term, item_name, 0, 1)) { + PLEntry *ple = new PLEntry(wxString(item_path, wxConvUTF8) ); + gf_list_add(_this->m_entries, ple); + } + return 0; +} + +static Bool pl_enum_dir_dirs(void *cbck, char *item_name, char *item_path) +{ + gf_enum_directory(item_path, 0, pl_enum_dir_item, cbck, NULL); + gf_enum_directory(item_path, 1, pl_enum_dir_dirs, cbck, NULL); + return 0; +} + + +void wxPlaylist::AddDir(Bool do_recurse) +{ + wxDirDialog dlg(this); + dlg.SetPath(wxString(szCacheDir, wxConvUTF8) ); + if (dlg.ShowModal() != wxID_OK) return; + + strcpy(szCacheDir, dlg.GetPath().mb_str(wxConvUTF8)); + gf_enum_directory(szCacheDir, 0, pl_enum_dir_item, this, NULL); + if (do_recurse) gf_enum_directory(szCacheDir, 1, pl_enum_dir_dirs, this, NULL); + m_all_dead_entries = -1; + RefreshList(); +} +void wxPlaylist::OnAddDir(wxCommandEvent &WXUNUSED(event)) +{ + AddDir(0); +} +void wxPlaylist::OnAddDirRec(wxCommandEvent &WXUNUSED(event)) +{ + AddDir(1); +} + +void wxPlaylist::OnAddURL(wxCommandEvent &WXUNUSED(event)) +{ + OpenURLDlg dlg(this, m_pApp->m_user.config); + if (dlg.ShowModal() != wxID_OK) return; + PLEntry *ple = new PLEntry(dlg.m_urlVal); + gf_list_add(m_entries, ple); + m_all_dead_entries = -1; + RefreshList(); +} + +void wxPlaylist::OnRemFile(wxCommandEvent &WXUNUSED(event)) +{ + if (!m_FileList->GetSelectedItemCount()) return; + + long item = -1; + for (;;) { + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) break; + PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item); + gf_list_del_item(m_entries, ple); + delete ple; + } + RefreshList(); +} + +void wxPlaylist::OnRemAll(wxCommandEvent &WXUNUSED(event)) +{ + Clear(); + RefreshList(); + m_cur_entry = -1; + m_all_dead_entries = 1; +} + +void wxPlaylist::OnRemDead(wxCommandEvent &WXUNUSED(event)) +{ + for (u32 i=0; im_bIsDead) continue; + gf_list_rem(m_entries, i); + i--; + delete ple; + } + m_all_dead_entries = gf_list_count(m_entries) ? 0 : 1; + RefreshList(); +} + + +void wxPlaylist::OnSelUp(wxCommandEvent &WXUNUSED(event)) +{ + s32 i; + if (!m_FileList->GetSelectedItemCount()) return; + long item = -1; + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item <= 0) return; + + item = -1; + for (;;) { + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) break; + PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item); + i = gf_list_del_item(m_entries, ple); + assert(i>=1); + gf_list_insert(m_entries, ple, i-1); + ple->m_bIsSelected = 1; + } + RefreshList(); +} + +void wxPlaylist::OnSelDown(wxCommandEvent &WXUNUSED(event)) +{ + s32 i; + + if (!m_FileList->GetSelectedItemCount()) return; + long item = -1; + for (;;) { + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) break; + } + if ((u32) item + 1 == gf_list_count(m_entries)) return; + + item = -1; + for (;;) { + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) break; + PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item); + i = gf_list_del_item(m_entries, ple); + assert(i>=1); + gf_list_insert(m_entries, ple, i+1); + ple->m_bIsSelected = 1; + } + RefreshList(); +} + + + +void wxPlaylist::OnSave(wxCommandEvent & WXUNUSED(event)) +{ + Bool save_m3u; + char szPath[GF_MAX_PATH]; + if (!gf_list_count(m_entries)) return; + + wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), wxT("M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|"), wxSAVE | wxCHANGE_DIR | wxOVERWRITE_PROMPT); + if (dlg.ShowModal() != wxID_OK) return; + + strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8)); + strlwr(szPath); + save_m3u = (dlg.GetFilterIndex()==0) ? 1 : 0; + if (save_m3u) { + if (!strstr(szPath, ".m3u")) { + strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8)); + strcat(szPath, ".m3u"); + } else { + strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8)); + } + } else { + if (!strstr(szPath, ".pls")) { + strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8)); + strcat(szPath, ".pls"); + } else { + strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8)); + } + } + Save(szPath, save_m3u); +} + +void wxPlaylist::Save(char *szPath, Bool save_m3u) +{ + FILE *out = fopen(szPath, "wt"); + if (!save_m3u) + fprintf(out, "[playlist]\nNumberOfEntries=%d\n", gf_list_count(m_entries)); + + for (u32 i=0; im_url); + } else { + fprintf(out, "File%d=%s\n", i+1, ple->m_url); + fprintf(out, "Title%d=%s\n", i+1, ple->m_disp_name); + if (ple->m_duration) fprintf(out, "Length%d=%d\n", i+1, ple->m_duration); + else fprintf(out, "Length%d=-1\n", i+1); + } + } + if (!save_m3u) fprintf(out, "Version=2\n"); + + fprintf(out, "\n"); + fclose(out); +} + +void wxPlaylist::OnOpen(wxCommandEvent & WXUNUSED(event)) +{ + wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), wxT("M3U & PLS Playlists|*.m3u;*.pls|M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|"), wxOPEN | wxCHANGE_DIR/* | wxHIDE_READONLY*/); + if (dlg.ShowModal() != wxID_OK) return; + + Clear(); + OpenPlaylist(dlg.GetPath()); + m_cur_entry = 0; + Play(); +} + +void wxPlaylist::OpenPlaylist(wxString filename) +{ + FILE *pl; + PLEntry *ple; + Bool load_m3u, go; + char szLine[GF_MAX_PATH]; + pl = fopen(filename.mb_str(wxConvUTF8) , "rt"); + if (!pl) return; + + ple = NULL; + load_m3u = 1; + while (!feof(pl)) { + fgets(szLine, GF_MAX_PATH, pl); + go = 1; + while (go) { + switch (szLine[strlen(szLine)-1]) { + case '\n': + case '\r': + case ' ': + szLine[strlen(szLine)-1] = 0; + break; + default: + go = 0; + break; + } + } + if (!strlen(szLine)) continue; + if (!stricmp(szLine, "[playlist]")) { + load_m3u = 0; + } else if (load_m3u) { + ple = new PLEntry(wxString(szLine, wxConvUTF8) ); + gf_list_add(m_entries, ple); + } else if (!strnicmp(szLine, "file", 4)) { + char *st = strchr(szLine, '='); + if (!st) ple = NULL; + else { + ple = new PLEntry(wxString(st + 1, wxConvUTF8) ); + gf_list_add(m_entries, ple); + } + } else if (ple && !strnicmp(szLine, "Length", 6)) { + char *st = strchr(szLine, '='); + s32 d = atoi(st + 1); + if (d>0) ple->m_duration = d; + } else if (ple && !strnicmp(szLine, "Title", 5)) { + char *st = strchr(szLine, '='); + free(ple->m_disp_name); + ple->m_disp_name = strdup(st + 6); + } + } + fclose(pl); + m_all_dead_entries = -1; + m_cur_entry = -1; + RefreshList(); +} + +void wxPlaylist::OnRightClick(wxListEvent & event) +{ + if (!m_FileList->GetItemCount()) return; + + wxMenu *popup = new wxMenu(); + + if (m_FileList->GetSelectedItemCount()==1) { + popup->Append(ID_PL_PLAY, wxT("Play")); + popup->AppendSeparator(); + } + popup->Append(ID_PL_SEL_REV, wxT("Inverse Selection")); + if (m_FileList->GetSelectedItemCount()) popup->Append(ID_PL_REM_FILE, wxT("Remove File(s)")); + if (m_FileList->GetItemCount()>1) { + popup->AppendSeparator(); + popup->Append(ID_PL_SORT_TITLE, wxT("Sort By Title")); + popup->Append(ID_PL_SORT_FILE, wxT("Sort By File Name")); + popup->Append(ID_PL_SORT_DUR, wxT("Sort By Duration")); + popup->AppendSeparator(); + popup->Append(ID_PL_REVERSE, wxT("Reverse List")); + popup->Append(ID_PL_RANDOMIZE, wxT("Randomize")); + } + + PopupMenu(popup, event.GetPoint()); + delete popup; +} + +void wxPlaylist::OnReverseSelection(wxCommandEvent &WXUNUSED(event) ) +{ + u32 i; + long item = -1; + for (;;) { + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) break; + PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item); + ple->m_bIsSelected = 1; + } + for (i=0; im_bIsSelected = !ple->m_bIsSelected; + } + RefreshList(); +} + +void wxPlaylist::OnReverseList(wxCommandEvent &WXUNUSED(event) ) +{ + u32 count = gf_list_count(m_entries); + u32 hcount = count / 2; + count--; + for (u32 i=0; i1) { + u32 pos = gf_rand() % (gf_list_count(m_entries)-1); + PLEntry *ple = (PLEntry *)gf_list_get(m_entries, pos); + gf_list_rem(m_entries, pos); + gf_list_add(new_entries, ple); + } + PLEntry *ple = (PLEntry *)gf_list_get(m_entries, 0); + gf_list_rem(m_entries, 0); + gf_list_add(new_entries, ple); + + gf_list_del(m_entries); + m_entries = new_entries; + m_cur_entry = -1; + RefreshList(); +} + +void wxPlaylist::Sort(u32 type) +{ + u32 i, j, smallest; + + for (i=0; im_url, ple2->m_url); + break; + case 1: + test = stricmp(ple1->m_disp_name, ple2->m_disp_name); + break; + case 2: + test = ple1->m_duration - ple2->m_duration; + break; + } + if (test<0) smallest = j; + } + PLEntry *ple = (PLEntry *)gf_list_get(m_entries, smallest); + gf_list_rem(m_entries, smallest); + gf_list_insert(m_entries, ple, i); + } + m_cur_entry = -1; + RefreshList(); +} + +void wxPlaylist::OnSortFile(wxCommandEvent &WXUNUSED(event) ) { Sort(0); } +void wxPlaylist::OnSortTitle(wxCommandEvent &WXUNUSED(event) ) { Sort(1); } +void wxPlaylist::OnSortDuration(wxCommandEvent &WXUNUSED(event) ) { Sort(2); } + +void wxPlaylist::RefreshCurrent() +{ + PLEntry *ple; + if (m_cur_entry<0) return; + ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple && ple->m_bIsPlaying) { + ple->m_bIsPlaying = 0; + UpdateEntry(m_cur_entry); + } +} + +Bool wxPlaylist::HasValidEntries() +{ + u32 nb_dead = 0; + if (m_all_dead_entries==-1) { + for (u32 i=0; im_bIsPlaying = 0; + if (ple->m_bIsDead) nb_dead ++; + } + m_all_dead_entries = (nb_dead==gf_list_count(m_entries)) ? 1 : 0; + } + return !m_all_dead_entries; +} + +void wxPlaylist::Play() +{ + PLEntry *ple; + + if (!HasValidEntries()) return; + + RefreshCurrent(); + + if (m_cur_entry >= (s32)gf_list_count(m_entries)) { + if (!m_pApp->m_loop) return; + m_cur_entry = 0; + } + + ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (!ple || ple->m_bIsDead) { + m_cur_entry++; + Play(); + } else { + char szPLE[20]; + ple->m_bIsPlaying = 1; + UpdateEntry(m_cur_entry); + sprintf(szPLE, "%d", m_cur_entry); + gf_cfg_set_key(m_pApp->m_user.config, "General", "PLEntry", szPLE); + m_pApp->DoConnect(); + } +} + +void wxPlaylist::OnItemActivate(wxListEvent &WXUNUSED(event) ) +{ + long item = m_FileList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item==-1) return; + RefreshCurrent(); + m_cur_entry = item; + Play(); +} + + +void wxPlaylist::OnPlay(wxCommandEvent &WXUNUSED(event)) +{ + long item = m_FileList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item==-1) return; + + RefreshCurrent(); + m_cur_entry = item; + Play(); +} + +void wxPlaylist::Truncate() +{ + if (m_cur_entry<0) return; + while ((u32) m_cur_entry+1 < gf_list_count(m_entries)) { + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry+1); + gf_list_rem(m_entries, m_cur_entry+1); + delete ple; + } + RefreshList(); +} + +void wxPlaylist::QueueURL(wxString filename) +{ + char *ext = (char*)strrchr(filename.mb_str(wxConvUTF8), '.'); + if (ext && (!stricmp(ext, ".m3u") || !stricmp(ext, ".pls")) ) { + OpenPlaylist(filename); + } else { + PLEntry *ple = new PLEntry(filename); + gf_list_add(m_entries, ple); + } +} + +void wxPlaylist::PlayNext() +{ + RefreshCurrent(); + if (1+m_cur_entry < (s32)gf_list_count(m_entries)) { + m_cur_entry++; + Play(); + } +} + +void wxPlaylist::PlayPrev() +{ + RefreshCurrent(); + if (m_cur_entry>0) { + m_cur_entry--; + Play(); + } +} + +void wxPlaylist::SetDead() +{ + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) { + ple->m_bIsDead = 1; + UpdateEntry(m_cur_entry); + if (ple->m_bIsPlaying) PlayNext(); + m_all_dead_entries = -1; + } +} +void wxPlaylist::SetDuration(u32 duration) +{ + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) { + ple->m_duration = duration; + UpdateEntry(m_cur_entry); + } +} + +wxString wxPlaylist::GetDisplayName() +{ + if (m_cur_entry>=0) { + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) return wxString(wxString(ple->m_disp_name, wxConvUTF8) ); + } + return wxT(""); +} + +wxString wxPlaylist::GetURL() +{ + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) return wxString(ple->m_url, wxConvUTF8); + return wxT(""); +} + diff --git a/applications/osmo4_wx/Playlist.h b/applications/osmo4_wx/Playlist.h new file mode 100644 index 0000000..0ebb230 --- /dev/null +++ b/applications/osmo4_wx/Playlist.h @@ -0,0 +1,134 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#ifndef _PLAYLIST_H +#define _PLAYLIST_H + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "menubtn.h" + +enum +{ + ID_FILE_LIST = 1000, +}; + +class wxOsmo4Frame; + +class PLEntry +{ +public: + PLEntry(wxString url); + ~PLEntry(); + + char *m_url; + char *m_disp_name; + u32 m_duration; + + Bool m_bIsSelected; + Bool m_bIsDead; + Bool m_bIsPlaying; +}; + + +class wxPlaylist : public wxFrame +{ +public: + wxPlaylist(wxWindow *parent); + virtual ~wxPlaylist(); + + void Clear(); + void ClearButPlaying(); + void RefreshList(); + + void Truncate(); + void QueueURL(wxString filename); + void Play(); + void PlayNext(); + void PlayPrev(); + void SetDead(); + void SetDuration(u32 duration); + Bool HasValidEntries(); + void OpenPlaylist(wxString fileName); + + /*for current entry played*/ + wxString GetDisplayName(); + wxString GetURL(); + + s32 m_cur_entry; + GF_List *m_entries; + + wxOsmo4Frame *m_pApp; + +private: + DECLARE_EVENT_TABLE() + + void OnClose(wxCloseEvent &event); + void OnSize(wxSizeEvent &event); + void OnAddFile(wxCommandEvent &event); + void OnAddURL(wxCommandEvent &event); + void OnAddDir(wxCommandEvent &event); + void OnAddDirRec(wxCommandEvent &event); + void OnRemFile(wxCommandEvent &event); + void OnRemAll(wxCommandEvent &event); + void OnRemDead(wxCommandEvent &event); + void OnSelUp(wxCommandEvent &event); + void OnSelDown(wxCommandEvent &event); + void OnSave(wxCommandEvent &event); + void OnOpen(wxCommandEvent &event); + void OnRightClick(wxListEvent & event); + void OnReverseSelection(wxCommandEvent &event); + void OnReverseList(wxCommandEvent &event); + void OnRandomize(wxCommandEvent &event); + void OnSortFile(wxCommandEvent &event); + void OnSortTitle(wxCommandEvent &event); + void OnSortDuration(wxCommandEvent &event); + void OnItemActivate(wxListEvent &event); + void OnPlay(wxCommandEvent &event); + + + void Sort(u32 type); + void UpdateEntry(u32 idx); + void RefreshCurrent(); + void Save(char *szPath, Bool save_m3u); + + wxBitmap *m_pOpen, *m_pSave, *m_pAdd, *m_pRem, *m_pUp, *m_pDown, *m_pSort; + wxMenuButton *m_pAddBut, *m_pRemBut, *m_pSortBut; + wxToolBar *m_pToolBar; + wxListCtrl *m_FileList; + char szCacheDir[GF_MAX_PATH]; + s32 m_all_dead_entries; + + void AddDir(Bool do_recurse); +}; + + + +#endif + diff --git a/applications/osmo4_wx/fileprops.cpp b/applications/osmo4_wx/fileprops.cpp new file mode 100644 index 0000000..5645e82 --- /dev/null +++ b/applications/osmo4_wx/fileprops.cpp @@ -0,0 +1,607 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#include "fileprops.h" +#include "wxOsmo4.h" +#include "Playlist.h" +#include +#include +#include +#include +/*ISO 639 languages*/ +#include + + +wxFileProps::wxFileProps(wxWindow *parent) + : wxDialog(parent, -1, wxString(_T("File Properties"))) +{ + + m_pApp = (wxOsmo4Frame *)parent; + SetSize(540, 260); + assert(m_pApp->m_pPlayList); + + m_pTreeView = new wxTreeCtrl(this, ID_TREE_VIEW, wxPoint(4, 2), wxSize(200, 180), wxTR_DEFAULT_STYLE | wxSUNKEN_BORDER); + + new wxStaticText(this, 0, _T("Information"), wxPoint(210, 2), wxSize(60, 20)); + m_pViewSel = new wxComboBox(this, ID_VIEW_SEL, _T(""), wxPoint(280, 2), wxSize(120, 24), 0, NULL, wxCB_READONLY); + m_pViewSel->Append(wxT("General")); + m_pViewSel->Append(wxT("Streams")); + m_pViewSel->Append(wxT("Playback")); + m_pViewSel->Append(wxT("Network")); + m_pViewSel->SetSelection(0); + + m_pViewInfo = new wxTextCtrl(this, -1, wxT(""), wxPoint(210, 30), wxSize(320, 200), wxTE_MULTILINE | wxTE_READONLY | wxHSCROLL | wxSUNKEN_BORDER); + +#ifdef WIN32 + m_pViewInfo->SetBackgroundColour(wxColour(wxT("LIGHT GREY"))); +#endif + + m_pViewWI = new wxButton(this, ID_VIEW_WI, wxT("View World Info"), wxPoint(4, 174), wxSize(200, 40)); + m_pViewSG = new wxButton(this, ID_VIEW_SG, wxT("View Scene Graph"), wxPoint(4, 220), wxSize(200, 40)); + + + wxString str = m_pApp->m_pPlayList->GetDisplayName(); + str += wxT(" Properties"); + SetTitle(str); + + m_pTimer = new wxTimer(); + m_pTimer->SetOwner(this, ID_OD_TIMER); + m_pTimer->Start(500, 0); + RewriteODTree(); + +} + +wxFileProps::~wxFileProps() +{ + m_pTimer->Stop(); + delete m_pTimer; +} + + +BEGIN_EVENT_TABLE(wxFileProps, wxDialog) + EVT_TREE_ITEM_ACTIVATED(ID_TREE_VIEW, wxFileProps::OnSetSelection) + EVT_TREE_SEL_CHANGED(ID_TREE_VIEW, wxFileProps::OnSetSelection) + EVT_TREE_ITEM_EXPANDED(ID_TREE_VIEW, wxFileProps::OnSetSelection) + EVT_TREE_ITEM_COLLAPSED(ID_TREE_VIEW, wxFileProps::OnSetSelection) + EVT_TIMER(ID_OD_TIMER, wxFileProps::OnTimer) + EVT_BUTTON(ID_VIEW_SG, wxFileProps::OnViewSG) + EVT_BUTTON(ID_VIEW_WI, wxFileProps::OnViewWorld) + EVT_COMBOBOX(ID_VIEW_SEL, wxFileProps::OnSelectInfo) +END_EVENT_TABLE() + +void wxFileProps::RewriteODTree() +{ + GF_ObjectManager *root_odm = gf_term_get_root_object(m_pApp->m_term); + if (!root_odm) return; + + m_pTreeView->DeleteAllItems(); + ODTreeData *root = new ODTreeData(root_odm); + m_pTreeView->AddRoot(wxT("Root OD"), -1, -1, root); + wxTreeItemId rootId = m_pTreeView->GetRootItem(); + + WriteInlineTree(root); + SetInfo(root_odm); +} + +void wxFileProps::WriteInlineTree(ODTreeData *root) +{ + /*browse all ODs*/ + u32 count = gf_term_get_object_count(m_pApp->m_term, root->m_pODMan); + + for (u32 i=0; im_term, root->m_pODMan, i); + if (!odm) return; + ODTreeData *odd = new ODTreeData(odm); + m_pTreeView->AppendItem(root->GetId(), wxT("Object Descriptor"), -1, -1, odd); + + /*if inline propagate*/ + switch (gf_term_object_subscene_type(m_pApp->m_term, odm)) { + case 1: + m_pTreeView->SetItemText(odd->GetId(), wxT("Root Scene")); + WriteInlineTree(odd); + break; + case 2: + m_pTreeView->SetItemText(odd->GetId(), wxT("Inline Scene")); + WriteInlineTree(odd); + break; + case 3: + m_pTreeView->SetItemText(odd->GetId(), wxT("Extern Proto Lib")); + break; + default: + break; + } + } +} + +void wxFileProps::OnSetSelection(wxTreeEvent& event) +{ + ODTreeData *odd = (ODTreeData *) m_pTreeView->GetItemData(event.GetItem()); + SetInfo(odd->m_pODMan); +} + +void wxFileProps::SetInfo(GF_ObjectManager *odm) +{ + m_current_odm = odm; + + switch (m_pViewSel->GetSelection()) { + case 3: SetNetworkInfo(); break; + case 2: SetDecoderInfo(); break; + case 1: SetStreamsInfo(); break; + default: SetGeneralInfo(); break; + } +} + +void wxFileProps::OnTimer(wxTimerEvent& WXUNUSED(event)) +{ + switch (m_pViewSel->GetSelection()) { + case 2: SetDecoderInfo(); break; + } +} +void wxFileProps::OnSelectInfo(wxCommandEvent & WXUNUSED(event) ) +{ + SetInfo(m_current_odm); +} + +void wxFileProps::SetGeneralInfo() +{ + wxString info; + ODInfo odi; + u32 h, m, s; + u32 i, j; + + info = wxT(""); + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + + if (!m_current_odm || gf_term_get_object_info(m_pApp->m_term, m_current_odm, &odi) != GF_OK) return; + + if (odi.has_profiles) info += wxT("Initial "); + info += wxString::Format(wxT("Object Descriptor ID %d\n"), odi.od->objectDescriptorID); + if (odi.duration) { + h = (u32) (odi.duration / 3600); + m = (u32) (odi.duration / 60) - h*60; + s = (u32) (odi.duration) - h*3600 - m*60; + info += wxString::Format(wxT("Duration %02d:%02d:%02d\n"), h, m, s); + } else { + info += wxT("Unknown duration\n"); + } + + if (odi.owns_service) { + info += wxT("Service Handler: ") + wxString(odi.service_handler, wxConvUTF8) + wxT("\n"); + info += wxT("Service URL: ") + wxString(odi.service_url, wxConvUTF8) + wxT("\n"); + } + + if (odi.od->URLString) { + info += wxT("Remote OD - URL: ") + wxString(odi.od->URLString, wxConvUTF8) + wxT("\n"); + } + + if (odi.codec_name) { + switch (odi.od_type) { + case GF_STREAM_VISUAL: + info += wxString::Format(wxT("Video Object: Width %d - Height %d\n"), odi.width, odi.height); + info += wxT("Media Codec ") + wxString(odi.codec_name, wxConvUTF8) + wxT("\n"); + break; + case GF_STREAM_AUDIO: + info += wxString::Format(wxT("Audio Object: Sample Rate %d - %d channels\n"), odi.sample_rate, odi.num_channels); + info += wxT("Media Codec ") + wxString(odi.codec_name, wxConvUTF8) + wxT("\n"); + break; + case GF_STREAM_PRIVATE_SCENE: + case GF_STREAM_SCENE: + if (odi.width && odi.height) { + info += wxString::Format(wxT("Scene Description: Width %d - Height %d\n"), odi.width, odi.height); + } else { + info += wxT("Scene Description: No size specified\n"); + } + info += wxT("Scene Codec ") + wxString(odi.codec_name, wxConvUTF8) + wxT("\n"); + break; + case GF_STREAM_TEXT: + if (odi.width && odi.height) { + info += wxString::Format(wxT("Text Object: Width %d - Height %d\n"), odi.width, odi.height); + } else { + info += wxString::Format(wxT("Text Object: No size specified\n")); + } + info += wxT("Text Codec ") + wxString(odi.codec_name, wxConvUTF8) + wxT("\n"); + break; + } + } + if (odi.protection==2) info += wxT("Encrypted Media NOT UNLOCKED"); + else if (odi.protection==1) info += wxT("Encrypted Media"); + + if (!gf_list_count(odi.od->OCIDescriptors)) { + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + return; + } + + info += wxT("\nObject Content Information:\n"); + + /*check OCI (not everything interests us) - FIXME: support for unicode*/ + for (i=0; iOCIDescriptors); i++) { + GF_Descriptor *desc = (GF_Descriptor *) gf_list_get(odi.od->OCIDescriptors, i); + switch (desc->tag) { + case GF_ODF_SEGMENT_TAG: + { + GF_Segment *sd = (GF_Segment *) desc; + info += wxT("\nSegment Descriptor:\nName: ") + wxString((char *) sd->SegmentName, wxConvUTF8); + info += wxString::Format(wxT(" - start time %g sec - duration %g sec\n"), sd->startTime, sd->Duration); + } + break; + case GF_ODF_CC_NAME_TAG: + { + GF_CC_Name *ccn = (GF_CC_Name *)desc; + info += wxT("\nContent Creators:\n"); + for (j=0; jContentCreators); j++) { + GF_ContentCreatorInfo *ci = (GF_ContentCreatorInfo *) gf_list_get(ccn->ContentCreators, j); + if (!ci->isUTF8) continue; + info += wxT("\t") + wxString(ci->contentCreatorName, wxConvUTF8) + wxT("\n"); + } + } + break; + + case GF_ODF_SHORT_TEXT_TAG: + { + GF_ShortTextual *std = (GF_ShortTextual *)desc; + info += wxT("\n") + wxString(std->eventName, wxConvUTF8) + wxT(": ") + wxString(std->eventText, wxConvUTF8) + wxT("\n"); + } + break; + /*todo*/ + case GF_ODF_CC_DATE_TAG: + break; + default: + break; + } + + } + + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); +} + +void wxFileProps::SetStreamsInfo() +{ + u32 i, count; + wxString info; + ODInfo odi; + char code[5]; + + info = wxT(""); + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + + if (!m_current_odm || gf_term_get_object_info(m_pApp->m_term, m_current_odm, &odi) != GF_OK) return; + + if (odi.has_profiles) { + info += wxString::Format(wxT("\tOD Profile@Level %d\n"), odi.OD_pl); + info += wxString::Format(wxT("\tScene Profile@Level %d\n"), odi.scene_pl); + info += wxString::Format(wxT("\tGraphics Profile@Level %d\n"), odi.graphics_pl); + info += wxString::Format(wxT("\tAudio Profile@Level %d\n"), odi.audio_pl); + info += wxString::Format(wxT("\tVisual Profile@Level %d\n"), odi.scene_pl); + if (odi.inline_pl) info += wxT("\tInline Content use same profiles\n"); + info += wxT("\n"); + } + + count = gf_list_count(odi.od->ESDescriptors); + + for (i=0; iESDescriptors, i); + + info += wxString::Format(wxT("Stream ID %d - Clock ID %d\n"), esd->ESID, esd->OCRESID); + if (esd->dependsOnESID) { + info += wxString::Format(wxT("\tDepends on Stream ID %d for decoding\n"), esd->dependsOnESID); + } + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: + info += wxString::Format(wxT("\tOD Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_OCR: + info += wxT("\tObject Clock Reference Stream\n"); + break; + case GF_STREAM_SCENE: + info += wxString::Format(wxT("\tScene Description Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_PRIVATE_SCENE: + info += wxString::Format(wxT("\tGPAC Private Scene Description Stream\n")); + break; + case GF_STREAM_VISUAL: + info += wxT("\tVisual Stream - media type: "); + switch (esd->decoderConfig->objectTypeIndication) { + case 0x20: info += wxT("MPEG-4\n"); break; + case 0x60: info += wxT("MPEG-2 Simple Profile\n"); break; + case 0x61: info += wxT("MPEG-2 Main Profile\n"); break; + case 0x62: info += wxT("MPEG-2 SNR Profile\n"); break; + case 0x63: info += wxT("MPEG-2 Spatial Profile\n"); break; + case 0x64: info += wxT("MPEG-2 High Profile\n"); break; + case 0x65: info += wxT("MPEG-2 422 Profile\n"); break; + case 0x6A: info += wxT("MPEG-1\n"); break; + case 0x6C: info += wxT("JPEG\n"); break; + case 0x6D: info += wxT("PNG\n"); break; + case 0x80: + memcpy(code, esd->decoderConfig->decoderSpecificInfo->data, 4); + code[4] = 0; + info += wxT("GPAC Intern (") + wxString(code, wxConvUTF8) + wxT(")\n"); + break; + default: + info += wxString::Format(wxT("Private/Unknown Type (0x%x)\n"), esd->decoderConfig->objectTypeIndication); + break; + } + break; + + case GF_STREAM_AUDIO: + info += wxT("\tAudio Stream - media type: "); + switch (esd->decoderConfig->objectTypeIndication) { + case 0x40: info += wxT("MPEG-4\n"); break; + case 0x66: info += wxT("MPEG-2 AAC Main Profile\n"); break; + case 0x67: info += wxT("MPEG-2 AAC LowComplexity Profile\n"); break; + case 0x68: info += wxT("MPEG-2 AAC Scalable Sampling Rate Profile\n"); break; + case 0x69: info += wxT("MPEG-2 Audio\n"); break; + case 0x6B: info += wxT("MPEG-1 Audio\n"); break; + case 0xA0: info += wxT("EVRC Audio\n"); break; + case 0xA1: info += wxT("SMV Audio\n"); break; + case 0xE1: info += wxT("QCELP Audio\n"); break; + case 0x80: + memcpy(code, esd->decoderConfig->decoderSpecificInfo->data, 4); + code[4] = 0; + info += wxT("GPAC Intern (") + wxString(code, wxConvUTF8) + wxT(")\n"); + break; + default: + info += wxString::Format(wxT("Private/Unknown Type (0x%x)\n"), esd->decoderConfig->objectTypeIndication); + break; + } + break; + case GF_STREAM_MPEG7: + info += wxString::Format(wxT("\tMPEG-7 Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_IPMP: + info += wxString::Format(wxT("\tIPMP Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_OCI: + info += wxString::Format(wxT("\tOCI Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_MPEGJ: + info += wxString::Format(wxT("\tMPEGJ Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_INTERACT: + info += wxString::Format(wxT("\tUser Interaction Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + default: + info += wxT("Private/Unknown\n"); + break; + } + + info += wxString::Format(wxT("\tBuffer Size %d\n\tAverage Bitrate %d bps\n\tMaximum Bitrate %d bps\n"), esd->decoderConfig->bufferSizeDB, esd->decoderConfig->avgBitrate, esd->decoderConfig->maxBitrate); + if (esd->slConfig->predefined==SLPredef_SkipSL) { + info += wxString::Format(wxT("\tNot using MPEG-4 Synchronization Layer\n")); + } else { + info += wxString::Format(wxT("\tStream Clock Resolution %d\n"), esd->slConfig->timestampResolution); + } + if (esd->URLString) + info += wxT("\tStream Location: ") + wxString(esd->URLString, wxConvUTF8) + wxT("\n"); + + /*check language*/ + if (esd->langDesc) { + u32 i=0; + char lan[4], *szLang; + lan[0] = esd->langDesc->langCode>>16; + lan[1] = (esd->langDesc->langCode>>8)&0xFF; + lan[2] = (esd->langDesc->langCode)&0xFF; + lan[3] = 0; + + if ((lan[0]=='u') && (lan[1]=='n') && (lan[2]=='d')) szLang = "Undetermined"; + else { + szLang = lan; + while (GF_ISO639_Lang[i]) { + if (GF_ISO639_Lang[i+2][0] && strstr(GF_ISO639_Lang[i+1], lan)) { + szLang = (char*) GF_ISO639_Lang[i]; + break; + } + i+=3; + } + } + info += wxString::Format(wxT("\tStream Language: %s\n"), szLang); + } + + } + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); +} + + +void wxFileProps::SetDecoderInfo() +{ + ODInfo odi; + wxString info; + u32 h, m, s; + + if (!m_current_odm || gf_term_get_object_info(m_pApp->m_term, m_current_odm, &odi)) { + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + return; + } + + info = wxT("Status: "); + switch (odi.status) { + case 0: + case 1: + case 2: + h = (u32) (odi.current_time / 3600); + m = (u32) (odi.current_time / 60) - h*60; + s = (u32) (odi.current_time) - h*3600 - m*60; + if (odi.status==0) info += wxT("Stopped"); + else if (odi.status==1) info += wxT("Playing"); + else info += wxT("Paused"); + info += wxString::Format(wxT("\nObject Time: %02d:%02d:%02d\n"), h, m, s); + break; + case 3: + info += wxT("Not Setup\n"); + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + return; + default: + info += wxT("Setup Failed\n"); + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + return; + } + /*get clock drift*/ + info += wxString::Format(wxT("Clock drift: %d ms\n"), odi.clock_drift); + /*get buffering*/ + if (odi.buffer>=0) info += wxString::Format(wxT("Buffering Time: %d ms\n"), odi.buffer); + else if (odi.buffer==-1) info += wxT("Not buffering\n"); + else info += wxT("Not Playing\n"); + + /*get DB occupation*/ + if (odi.buffer>=0) info += wxString::Format(wxT("Decoding Buffer: %d Access Units\n"), odi.db_unit_count); + /*get CB occupation*/ + if (odi.cb_max_count) + info += wxString::Format(wxT("Composition Memory: %d/%d Units\n"), odi.cb_unit_count, odi.cb_max_count); + + Float avg_dec_time = 0; + if (odi.nb_dec_frames) { + avg_dec_time = (Float) odi.total_dec_time; + avg_dec_time /= odi.nb_dec_frames; + } + info += wxString::Format(wxT("Average Bitrate %d kbps (%d max)\nAverage Decoding Time %.2f ms (%d max)\nTotal decoded frames %d - %d dropped\n"), + (u32) odi.avg_bitrate/1024, odi.max_bitrate/1024, avg_dec_time, odi.max_dec_time, odi.nb_dec_frames, odi.nb_droped); + + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); +} + +void wxFileProps::SetNetworkInfo() +{ + wxString info; + u32 id; + NetStatCommand com; + ODInfo odi; + u32 d_enum; + GF_Err e; + + info = wxT(""); + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(wxT("")); + + if (!m_current_odm || gf_term_get_object_info(m_pApp->m_term, m_current_odm, &odi) != GF_OK) return; + + if (odi.owns_service) { + const char *url, *path; + u32 done, total, bps; + info = wxT("Current Downloads in service:\n"); + d_enum = 0; + while (gf_term_get_download_info(m_pApp->m_term, m_current_odm, &d_enum, &url, &path, &done, &total, &bps)) { + info += wxString(url, wxConvUTF8); + if (total) { + info += wxString::Format(wxT(": %d / %d bytes (%.2f %%) - %.2f kBps\n"), done, total, (100.0*done)/total, ((Double)bps)/1024); + } else { + info += wxString::Format(wxT(": %.2f kBps\n"), ((Double)bps)/1024); + } + } + if (!d_enum) info = wxT("No Downloads in service\n"); + info += wxT("\n"); + } + + d_enum = 0; + while (gf_term_get_channel_net_info(m_pApp->m_term, m_current_odm, &d_enum, &id, &com, &e)) { + if (e) continue; + if (!com.bw_down && !com.bw_up) continue; + + info += wxString::Format(wxT("Stream ID %d statistics:\n"), id); + if (com.multiplex_port) { + info += wxString::Format(wxT("\tMultiplex Port %d - multiplex ID %d\n"), com.multiplex_port, com.port); + } else { + info += wxString::Format(wxT("\tPort %d\n"), com.port); + } + info += wxString::Format(wxT("\tPacket Loss Percentage: %.4f\n"), com.pck_loss_percentage); + info += wxString::Format(wxT("\tDown Bandwidth: %.3f bps\n"), ((Float)com.bw_down)/1024); + if (com.bw_up) info += wxString::Format(wxT("\tUp Bandwidth: %d bps\n"), com.bw_up); + if (com.ctrl_port) { + if (com.multiplex_port) { + info += wxString::Format(wxT("\tControl Multiplex Port: %d - Control Multiplex ID %d\n"), com.multiplex_port, com.ctrl_port); + } else { + info += wxString::Format(wxT("\tControl Port: %d\n"), com.ctrl_port); + } + info += wxString::Format(wxT("\tControl Down Bandwidth: %d bps\n"), com.ctrl_bw_down); + info += wxString::Format(wxT("\tControl Up Bandwidth: %d bps\n"), com.ctrl_bw_up); + } + info += wxT("\n"); + } + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); +} + + +void wxFileProps::OnViewWorld(wxCommandEvent &WXUNUSED(event)) +{ + wxString wit; + const char *str; + GF_List *descs; + descs = gf_list_new(); + str = gf_term_get_world_info(m_pApp->m_term, m_current_odm, descs); + + if (!str) { + wxMessageDialog(this, wxT("No World Info available"), wxT("Sorry!"), wxOK).ShowModal(); + return; + } + + wit = wxT(""); + for (u32 i=0; gf_list_count(descs); i++) { + const char *d = (const char *) gf_list_get(descs, i); + wit += wxString(d, wxConvUTF8); + wit += wxT("\n"); + } + wxMessageDialog(this, wit, wxString(str, wxConvUTF8), wxOK).ShowModal(); + gf_list_del(descs); +} + +void wxFileProps::OnViewSG(wxCommandEvent &WXUNUSED(event)) +{ + const char *sOpt; + Bool dump_xmt; + wxFileName out_file; + char szOutFile[GF_MAX_PATH]; + wxString fname; + + sOpt = gf_cfg_get_key(m_pApp->m_user.config, "General", "CacheDirectory"); + out_file.AssignDir(wxString(sOpt, wxConvUTF8) ); + + sOpt = gf_cfg_get_key(m_pApp->m_user.config, "General", "ViewXMT"); + out_file.SetFullName(wxT("scene_dump")); + if (sOpt && !stricmp(sOpt, "yes")) { + dump_xmt = 1; + } else { + dump_xmt = 0; + } + strcpy(szOutFile, out_file.GetFullName().mb_str(wxConvUTF8)); + + GF_Err e = gf_term_dump_scene(m_pApp->m_term, szOutFile, dump_xmt, 0, m_current_odm); + if (e) { + wxMessageDialog dlg(this, wxString(gf_error_to_string(e), wxConvUTF8), wxT("Error while dumping"), wxOK); + dlg.ShowModal(); + } else { + wxString cmd = get_pref_browser(m_pApp->m_user.config); + cmd += wxT(" "); + cmd += wxString(szOutFile, wxConvUTF8); + wxExecute(cmd); + } +} diff --git a/applications/osmo4_wx/fileprops.h b/applications/osmo4_wx/fileprops.h new file mode 100644 index 0000000..a1ba51b --- /dev/null +++ b/applications/osmo4_wx/fileprops.h @@ -0,0 +1,83 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#ifndef _FILEPROPS_H +#define _FILEPROPS_H + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include + +#include + +/*abstract class for all items in the tree*/ +class ODTreeData : public wxTreeItemData +{ +public: + ODTreeData(GF_ObjectManager *odm) : wxTreeItemData(), m_pODMan(odm) {} + GF_ObjectManager *m_pODMan; +}; + + +class wxOsmo4Frame; +class wxFileProps : public wxDialog +{ +public: + wxFileProps(wxWindow *parent); + virtual ~wxFileProps(); + +private: + DECLARE_EVENT_TABLE() + + wxOsmo4Frame *m_pApp; + + wxTreeCtrl *m_pTreeView; + wxTextCtrl *m_pViewInfo; + wxComboBox *m_pViewSel; + wxButton *m_pViewWI, *m_pViewSG; + wxTimer *m_pTimer; + + GF_ObjectManager *m_current_odm; + + void RewriteODTree(); + void SetGeneralInfo(); + void SetStreamsInfo(); + void SetDecoderInfo(); + void SetNetworkInfo(); + void WriteInlineTree(ODTreeData *pRoot); + void OnSetSelection(wxTreeEvent &event); + void OnSelectInfo(wxCommandEvent &event); + void OnTimer(wxTimerEvent &event); + void OnViewWorld(wxCommandEvent &event); + void OnViewSG(wxCommandEvent &event); + void SetInfo(GF_ObjectManager *odm); +}; + +#endif + diff --git a/applications/osmo4_wx/menubtn.cpp b/applications/osmo4_wx/menubtn.cpp new file mode 100644 index 0000000..a13e58c --- /dev/null +++ b/applications/osmo4_wx/menubtn.cpp @@ -0,0 +1,863 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wxMenuButton +// Purpose: A button with a dropdown wxMenu +// Author: John Labenski +// Modified by: +// Created: 11/05/2002 +// RCS-ID: +// Copyright: (c) John Labenki +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) + #pragma implementation "menubtn.h" +#endif + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/control.h" + #include "wx/menu.h" + #include "wx/settings.h" + #include "wx/bitmap.h" + #include "wx/pen.h" + #include "wx/dc.h" +#endif // WX_PRECOMP + +#include +#include +#include +#include + +#include "menubtn.h" + + + +// ========================================================================== +// wxCustomButton +// ========================================================================== +IMPLEMENT_DYNAMIC_CLASS( wxCustomButton, wxControl ) + +BEGIN_EVENT_TABLE(wxCustomButton,wxControl) + EVT_MOUSE_EVENTS ( wxCustomButton::OnMouseEvents ) + EVT_PAINT ( wxCustomButton::OnPaint ) + EVT_SIZE ( wxCustomButton::OnSize ) +END_EVENT_TABLE() + +wxCustomButton::~wxCustomButton() +{ + if (HasCapture()) ReleaseMouse(); + if (m_timer) delete m_timer; +} + +void wxCustomButton::Init() +{ + m_focused = FALSE; + m_labelMargin = wxSize(4,4); + m_bitmapMargin = wxSize(2,2); + m_down = 0; + m_timer = NULL; + m_eventType = 0; + m_button_style = wxCUSTBUT_TOGGLE|wxCUSTBUT_BOTTOM; +} + +bool wxCustomButton::Create(wxWindow* parent, wxWindowID id, + const wxString& label, const wxBitmap &bitmap, + const wxPoint& pos, const wxSize& size, + long style, const wxValidator& val, + const wxString& name) +{ + if (!wxControl::Create(parent,id,pos,size,wxNO_BORDER|wxCLIP_CHILDREN,val,name)) + return FALSE; + + wxControl::SetLabel(label); + wxControl::SetBackgroundColour(parent->GetBackgroundColour()); + wxControl::SetForegroundColour(parent->GetForegroundColour()); + wxControl::SetFont(parent->GetFont()); + + if (bitmap.Ok()) m_bmpLabel = bitmap; + + if (!SetButtonStyle(style)) return FALSE; + + wxSize bestSize = DoGetBestSize(); + SetSize(wxSize(size.x<0 ? bestSize.x:size.x, size.y<0 ? bestSize.y:size.y)); +#if (wxMINOR_VERSION<8) + SetBestSize(GetSize()); +#else + SetInitialSize(GetSize()); +#endif + + CalcLayout(TRUE); + return TRUE; +} + +void wxCustomButton::SetValue(bool depressed) +{ + wxCHECK_RET(!(m_button_style & wxCUSTBUT_NOTOGGLE), wxT("can't set button state")); + m_down = depressed ? 1 : 0; + Refresh(FALSE); +} + +bool wxCustomButton::SetButtonStyle(long style) +{ + int n_styles = 0; + if ((style & wxCUSTBUT_LEFT) != 0) n_styles++; + if ((style & wxCUSTBUT_RIGHT) != 0) n_styles++; + if ((style & wxCUSTBUT_TOP) != 0) n_styles++; + if ((style & wxCUSTBUT_BOTTOM) != 0) n_styles++; + wxCHECK_MSG(n_styles < 2, FALSE, wxT("Only one wxCustomButton label position allowed")); + + n_styles = 0; + if ((style & wxCUSTBUT_NOTOGGLE) != 0) n_styles++; + if ((style & wxCUSTBUT_BUTTON) != 0) n_styles++; + if ((style & wxCUSTBUT_TOGGLE) != 0) n_styles++; + if ((style & wxCUSTBUT_BUT_DCLICK_TOG) != 0) n_styles++; + if ((style & wxCUSTBUT_TOG_DCLICK_BUT) != 0) n_styles++; + wxCHECK_MSG(n_styles < 2, FALSE, wxT("Only one wxCustomButton style allowed")); + + m_button_style = style; + + if ((m_button_style & wxCUSTBUT_BUTTON) != 0) + m_down = 0; + + CalcLayout(TRUE); + return TRUE; +} + +void wxCustomButton::SetLabel( const wxString &label ) +{ + wxControl::SetLabel(label); + CalcLayout(TRUE); +} + +// sequence of events in GTK is up, dclick, up. + +void wxCustomButton::OnMouseEvents(wxMouseEvent& event) +{ + if (m_button_style & wxCUSTBUT_NOTOGGLE) return; + + if (event.LeftDown() || event.RightDown()) + { + if (!HasCapture()) + CaptureMouse(); // keep depressed until up + + m_down++; + Redraw(); + } + else if (event.LeftDClick() || event.RightDClick()) + { + m_down++; // GTK eats second down event + Redraw(); + } + else if (event.LeftUp()) + { + if (HasCapture()) + ReleaseMouse(); + + m_eventType = wxEVT_LEFT_UP; + +#if (wxMINOR_VERSION<8) + if (wxRect(wxPoint(0,0), GetSize()).Inside(event.GetPosition())) +#else + if (wxRect(wxPoint(0,0), GetSize()).Contains(event.GetPosition())) +#endif + { + if ((m_button_style & wxCUSTBUT_BUTTON) && (m_down > 0)) + { + m_down = 0; + Redraw(); + SendEvent(); + return; + } + else + { + if (!m_timer) + { + m_timer = new wxTimer(this, m_down+1); + m_timer->Start(200, TRUE); + } + else + { + m_eventType = wxEVT_LEFT_DCLICK; + } + + if ((m_button_style & wxCUSTBUT_TOGGLE) && + (m_button_style & wxCUSTBUT_TOG_DCLICK_BUT)) m_down++; + } + } + + Redraw(); + } + else if (event.RightUp()) + { + if (HasCapture()) + ReleaseMouse(); + + m_eventType = wxEVT_RIGHT_UP; + +#if (wxMINOR_VERSION<8) + if (wxRect(wxPoint(0,0), GetSize()).Inside(event.GetPosition())) +#else + if (wxRect(wxPoint(0,0), GetSize()).Contains(event.GetPosition())) +#endif + { + if ((m_button_style & wxCUSTBUT_BUTTON) && (m_down > 0)) + { + m_down = 0; + Redraw(); + SendEvent(); + return; + } + else + { + m_down++; + + if (!m_timer) + { + m_timer = new wxTimer(this, m_down); + m_timer->Start(250, TRUE); + } + else + { + m_eventType = wxEVT_RIGHT_DCLICK; + } + } + } + + Redraw(); + } + else if (event.Entering()) + { + m_focused = TRUE; + if ((event.LeftIsDown() || event.RightIsDown()) && HasCapture()) + m_down++; + + Redraw(); + } + else if (event.Leaving()) + { + m_focused = FALSE; + if ((event.LeftIsDown() || event.RightIsDown()) && HasCapture()) + m_down--; + + Redraw(); + } +} + + + +void wxCustomButton::SendEvent() +{ + if (((m_button_style & wxCUSTBUT_TOGGLE) && (m_eventType == wxEVT_LEFT_UP)) || + ((m_button_style & wxCUSTBUT_BUT_DCLICK_TOG) && (m_eventType == wxEVT_LEFT_DCLICK)) || + ((m_button_style & wxCUSTBUT_TOG_DCLICK_BUT) && (m_eventType == wxEVT_LEFT_UP))) + { + wxCommandEvent eventOut(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, GetId()); + eventOut.SetInt(m_down%2 ? 1 : 0); + eventOut.SetExtraLong(m_eventType); + eventOut.SetEventObject(this); + GetEventHandler()->ProcessEvent(eventOut); + } + else + { + wxCommandEvent eventOut(wxEVT_COMMAND_BUTTON_CLICKED, GetId()); + eventOut.SetInt(0); + eventOut.SetExtraLong(m_eventType); + eventOut.SetEventObject(this); + GetEventHandler()->ProcessEvent(eventOut); + } +} + +wxBitmap wxCustomButton::CreateBitmapDisabled(const wxBitmap &bitmap) const +{ + wxCHECK_MSG(bitmap.Ok(), wxNullBitmap, wxT("invalid bitmap")); + + unsigned char br = GetBackgroundColour().Red(); + unsigned char bg = GetBackgroundColour().Green(); + unsigned char bb = GetBackgroundColour().Blue(); + + wxImage image = bitmap.ConvertToImage(); + int pos, width = image.GetWidth(), height = image.GetHeight(); + unsigned char *img_data = image.GetData(); + + for (int j=0; j lh ? bh : lh; + if (has_bitmap && has_label) lw -= wxMin(m_labelMargin.x, m_bitmapMargin.x); + return wxSize(lw+bw, h); + } + + int w = bw > lw ? bw : lw; + if (has_bitmap && has_label) lh -= wxMin(m_labelMargin.y, m_bitmapMargin.y); + return wxSize(w, lh+bh); +} + +void wxCustomButton::CalcLayout(bool refresh) +{ + int w, h; + GetSize(&w,&h); + + int bw = 0, bh = 0; + int lw = 0, lh = 0; + + if (m_bmpLabel.Ok()) // assume they're all the same size + { + bw = m_bmpLabel.GetWidth(); + bh = m_bmpLabel.GetHeight(); + } + wxString label = GetLabel(); + if (!label.IsEmpty()) + { + GetTextExtent(label, &lw, &lh); + } + + // Center the label or bitmap if only one or the other + if (!m_bmpLabel.Ok()) + { + m_bitmapPos = wxPoint(0,0); + m_labelPos = wxPoint((w-lw)/2, (h-lh)/2); + } + else if (label.IsEmpty()) + { + m_bitmapPos = wxPoint((w-bw)/2, (h-bh)/2); + m_labelPos = wxPoint(0,0); + } + else if (m_button_style & wxCUSTBUT_LEFT) + { + int mid_margin = wxMax(m_labelMargin.x, m_bitmapMargin.x); + m_labelPos = wxPoint((w - (bw+lw+m_labelMargin.x+m_bitmapMargin.x+mid_margin))/2 + m_labelMargin.x, (h - lh)/2); + m_bitmapPos = wxPoint(m_labelPos.x + lw + mid_margin, (h - bh)/2); + } + else if (m_button_style & wxCUSTBUT_RIGHT) + { + int mid_margin = wxMax(m_labelMargin.x, m_bitmapMargin.x); + m_bitmapPos = wxPoint((w - (bw+lw+m_labelMargin.x+m_bitmapMargin.x+mid_margin))/2 + m_bitmapMargin.x, (h - bh)/2); + m_labelPos = wxPoint(m_bitmapPos.x + bw + mid_margin, (h - lh)/2); + } + else if (m_button_style & wxCUSTBUT_TOP) + { + int mid_margin = wxMax(m_labelMargin.y, m_bitmapMargin.y); + m_labelPos = wxPoint((w - lw)/2, (h - (bh+lh+m_labelMargin.y+m_bitmapMargin.y+mid_margin))/2 + m_labelMargin.y); + m_bitmapPos = wxPoint((w - bw)/2, m_labelPos.y + lh + mid_margin); + } + else // if (m_button_style & wxCUSTBUT_BOTTOM) DEFAULT + { + int mid_margin = wxMax(m_labelMargin.y, m_bitmapMargin.y); + m_bitmapPos = wxPoint((w - bw)/2, (h - (bh+lh+m_labelMargin.y+m_bitmapMargin.y+mid_margin))/2 + m_bitmapMargin.y); + m_labelPos = wxPoint((w - lw)/2, m_bitmapPos.y + bh + mid_margin); + } + + if (refresh) Refresh(FALSE); +} + + +/* XPM */ +static const char *down_arrow_xpm_data[] = { +/* columns rows colors chars-per-pixel */ +"5 3 2 1", +" c None", +"a c Black", +/* pixels */ +"aaaaa", +" aaa ", +" a "}; + +static wxBitmap s_dropdownBitmap; // all buttons share the same bitmap + +enum +{ + IDD_DROPDOWN_BUTTON = 100 +}; + +//----------------------------------------------------------------------------- +// wxMenuButtonEvents +//----------------------------------------------------------------------------- + +DEFINE_LOCAL_EVENT_TYPE(wxEVT_MENUBUTTON_OPEN) + +// ========================================================================== +// MenuDropButton +// ========================================================================== + +class MenuDropButton : public wxCustomButton +{ +public: + MenuDropButton( wxWindow *parent, wxWindowID id, long style) : wxCustomButton() + { + if (!s_dropdownBitmap.Ok()) + s_dropdownBitmap = wxBitmap(down_arrow_xpm_data); + + Create( parent, id, wxEmptyString, s_dropdownBitmap, wxDefaultPosition, + wxSize(wxMENUBUTTON_DROP_WIDTH, wxMENUBUTTON_DROP_HEIGHT), style); + } + + virtual void Paint( wxDC &dc ) + { + wxCustomButton *labelBut = ((wxMenuButton*)GetParent())->GetLabelButton(); + + // pretend that both buttons have focus (for flat style) + if (labelBut) + { + wxPoint p = GetParent()->ScreenToClient(wxGetMousePosition()); + +#if (wxMINOR_VERSION<8) + if (GetRect().Inside(p) || labelBut->GetRect().Inside(p)) +#else + if (GetRect().Contains(p) || labelBut->GetRect().Contains(p)) +#endif + { + m_focused = TRUE; + + if (!labelBut->GetFocused()) + labelBut->SetFocused(TRUE); + } + else + { + m_focused = FALSE; + + if (labelBut->GetFocused()) + labelBut->SetFocused(FALSE); + } + } + + wxCustomButton::Paint(dc); + } +}; + +// ========================================================================== +// MenuLabelButton +// ========================================================================== + +class MenuLabelButton : public wxCustomButton +{ +public: + MenuLabelButton( wxWindow* parent, wxWindowID id, + const wxString &label, + const wxBitmap &bitmap, + long style ) : wxCustomButton() + { + Create(parent, id, label, bitmap, wxDefaultPosition, wxDefaultSize, style); + } + + virtual void Paint( wxDC &dc ) + { + wxCustomButton *dropBut = ((wxMenuButton*)GetParent())->GetDropDownButton(); + + // pretend that both buttons have focus (for flat style) + if (dropBut) + { + wxPoint p = GetParent()->ScreenToClient(wxGetMousePosition()); + +#if (wxMINOR_VERSION<8) + if (GetRect().Inside(p) || dropBut->GetRect().Inside(p)) +#else + if (GetRect().Contains(p) || dropBut->GetRect().Contains(p)) +#endif + { + m_focused = TRUE; + + if (!dropBut->GetFocused()) + dropBut->SetFocused(TRUE); + } + else + { + m_focused = FALSE; + + if (dropBut->GetFocused()) + dropBut->SetFocused(FALSE); + } + } + + wxCustomButton::Paint(dc); + } +}; + +// ========================================================================== +// wxMenuButton +// ========================================================================== + +IMPLEMENT_DYNAMIC_CLASS( wxMenuButton, wxControl ) + +BEGIN_EVENT_TABLE(wxMenuButton,wxControl) + EVT_BUTTON(wxID_ANY, wxMenuButton::OnButton) + +#ifdef __WXMSW__ + EVT_MENU(wxID_ANY, wxMenuButton::OnMenu) +#endif +END_EVENT_TABLE() + +wxMenuButton::~wxMenuButton() +{ + AssignMenu(NULL, TRUE); +} + +void wxMenuButton::Init() +{ + m_labelButton = NULL; + m_dropdownButton = NULL; + m_menu = NULL; + m_menu_static = FALSE; + m_style = 0; +} + +bool wxMenuButton::Create( wxWindow* parent, wxWindowID id, + const wxString &label, + const wxBitmap &bitmap, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& val, + const wxString& name) +{ + m_style = style; + + long flat = style & wxMENUBUT_FLAT; + + wxControl::Create(parent,id,pos,size,wxNO_BORDER|wxCLIP_CHILDREN,val,name); + wxControl::SetLabel(label); + SetBackgroundColour(parent->GetBackgroundColour()); + SetForegroundColour(parent->GetForegroundColour()); + SetFont(parent->GetFont()); + + m_labelButton = new MenuLabelButton(this, id, label, bitmap, wxCUSTBUT_BUTTON|flat); + m_dropdownButton = new MenuDropButton(this, IDD_DROPDOWN_BUTTON, wxCUSTBUT_BUTTON|flat); + + wxSize bestSize = DoGetBestSize(); + SetSize( wxSize(size.x < 0 ? bestSize.x : size.x, + size.y < 0 ? bestSize.y : size.y) ); + +#if (wxMINOR_VERSION<8) + SetBestSize(GetSize()); +#else + SetInitialSize(GetSize()); +#endif + + return TRUE; +} + +#ifdef __WXMSW__ +// FIXME - I think there was a patch to fix this +void wxMenuButton::OnMenu( wxCommandEvent &event ) +{ + event.Skip(); + wxMenuItem *mi = m_menu->FindItem(event.GetId()); + if (mi && (mi->GetKind() == wxITEM_RADIO)) + m_menu->Check(event.GetId(), TRUE); +} +#endif // __WXMSW__ + +void wxMenuButton::OnButton( wxCommandEvent &event) +{ + int win_id = event.GetId(); + + if (win_id == IDD_DROPDOWN_BUTTON) + { + wxNotifyEvent mevent(wxEVT_MENUBUTTON_OPEN, GetId()); + mevent.SetEventObject(this); + if (GetEventHandler()->ProcessEvent(mevent) && !mevent.IsAllowed()) + return; + + if (!m_menu) + return; + + PopupMenu(m_menu, wxPoint(0, GetSize().y)); + + m_labelButton->Refresh(FALSE); + m_dropdownButton->Refresh(FALSE); + } + else if (win_id == m_labelButton->GetId()) + { + + wxCommandEvent cevent(wxEVT_COMMAND_MENU_SELECTED, win_id); + cevent.SetEventObject(this); + cevent.SetId(win_id); + GetParent()->GetEventHandler()->ProcessEvent(cevent); + + if (!m_menu) return; + + const wxMenuItemList &items = m_menu->GetMenuItems(); + int first_radio_id = -1; + int checked_id = -1; + bool check_next = FALSE; + + // find the next available radio item to check + for (wxMenuItemList::Node *node = items.GetFirst(); node; node = node->GetNext()) + { + wxMenuItem *mi = (wxMenuItem*)node->GetData(); + if (mi && (mi->GetKind() == wxITEM_RADIO)) + { + if (first_radio_id == -1) + first_radio_id = mi->GetId(); + + if (check_next) + { + check_next = FALSE; + checked_id = mi->GetId(); + break; + } + else if (mi->IsChecked()) + check_next = TRUE; + } + } + // the last item was checked, go back to the first + if (check_next && (first_radio_id != -1)) + checked_id = first_radio_id; + + if (checked_id != -1) + { + m_menu->Check(checked_id, TRUE); + + wxCommandEvent mevent( wxEVT_COMMAND_MENU_SELECTED, checked_id); + mevent.SetEventObject( m_menu ); + mevent.SetInt(1); + GetEventHandler()->ProcessEvent(mevent); + } + } +} + +int wxMenuButton::GetSelection() const +{ + wxCHECK_MSG(m_menu != NULL, wxNOT_FOUND, wxT("No attached menu in wxMenuButton::GetSelection")); + + const wxMenuItemList &items = m_menu->GetMenuItems(); + + for (wxMenuItemList::Node *node = items.GetFirst(); node; node = node->GetNext()) + { + wxMenuItem *mi = (wxMenuItem*)node->GetData(); + if (mi && (mi->GetKind() == wxITEM_RADIO)) + { + if (mi->IsChecked()) + return mi->GetId(); + } + } + + return wxNOT_FOUND; +} + +void wxMenuButton::AssignMenu(wxMenu *menu, bool static_menu) +{ + if (!m_menu_static && m_menu) + delete m_menu; + + m_menu = menu; + m_menu_static = static_menu; +} + +void wxMenuButton::SetToolTip(const wxString &tip) +{ + wxWindow::SetToolTip(tip); + ((wxWindow*)m_labelButton)->SetToolTip(tip); + ((wxWindow*)m_dropdownButton)->SetToolTip(tip); +} +void wxMenuButton::SetToolTip(wxToolTip *tip) +{ + wxWindow::SetToolTip(tip); + ((wxWindow*)m_labelButton)->SetToolTip(tip); + ((wxWindow*)m_dropdownButton)->SetToolTip(tip); +} + +void wxMenuButton::DoSetSize(int x, int y, int width, int height, int sizeFlags) +{ + wxSize curSize( GetSize() ); + wxSize bestSize( DoGetBestSize() ); + + if (width == -1) + width = curSize.GetWidth(); + if (width < 10) + width = bestSize.GetWidth(); + + if (height == -1) + height = curSize.GetHeight(); + if (height < 5) + height = bestSize.GetHeight(); + + wxWindow::DoSetSize(x, y, width, height, sizeFlags); + + if (m_labelButton) + m_labelButton->SetSize(0, 0, width - wxMENUBUTTON_DROP_WIDTH, height); + if (m_dropdownButton) + m_dropdownButton->SetSize(width-wxMENUBUTTON_DROP_WIDTH, 0, wxMENUBUTTON_DROP_WIDTH, height); +} + +wxSize wxMenuButton::DoGetBestSize() +{ + if (!m_labelButton || !m_dropdownButton) + return wxSize(wxMENUBUTTON_DROP_WIDTH+wxMENUBUTTON_DROP_HEIGHT, wxMENUBUTTON_DROP_HEIGHT); + + wxSize size = m_labelButton->GetBestSize(); + size.x += wxMENUBUTTON_DROP_WIDTH; + return size; +} diff --git a/applications/osmo4_wx/menubtn.h b/applications/osmo4_wx/menubtn.h new file mode 100644 index 0000000..441c7b7 --- /dev/null +++ b/applications/osmo4_wx/menubtn.h @@ -0,0 +1,314 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wxMenuButton +// Purpose: A button with a dropdown wxMenu +// Author: John Labenski +// Modified by: +// Created: 11/05/2002 +// Copyright: (c) John Labenski +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + +/* + +wxMenuButton is a button that drops down an assigned wxMenu + +Create the button with either a text or bitmap label. + Create a new wxMenu and call AssignMenu and thats it. When you press the + dropdown button the menu appears. When you press the label button the next + wxITEM_RADIO (ie wxMenuItem::GetKind) in the menu is selected round robin. + If there are no radio items then it really just acts like a menubar, though + this is probably not too useful. The events sent in this case are EVT_MENUs + either generated by the menu when you click on it or created when you click + on the label to select the next radio item. +*/ + +#ifndef _WX_MENUBTN_H_ +#define _WX_MENUBTN_H_ + +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) + #pragma interface "menubtn.h" +#endif + +class wxMenu; +class wxBitmap; +class wxCustomButton; + +//----------------------------------------------------------------------------- +// wxCustomButton styles +//----------------------------------------------------------------------------- + +enum wxCustomButton_Style +{ + // Position of the label, use only one + wxCUSTBUT_LEFT = 0x0001, + wxCUSTBUT_RIGHT = 0x0002, + wxCUSTBUT_TOP = 0x0004, + wxCUSTBUT_BOTTOM = 0x0008, + // Button style, use only one + wxCUSTBUT_NOTOGGLE = 0x0100, + wxCUSTBUT_BUTTON = 0x0200, + wxCUSTBUT_TOGGLE = 0x0400, + wxCUSTBUT_BUT_DCLICK_TOG = 0x0800, + wxCUSTBUT_TOG_DCLICK_BUT = 0x1000, + // drawing styles + wxCUSTBUT_FLAT = 0x2000 // flat, mouseover raises if not depressed +}; + +//----------------------------------------------------------------------------- +// wxCustomButton +//----------------------------------------------------------------------------- + +class WXDLLEXPORT wxCustomButton : public wxControl +{ +public: + + wxCustomButton() : wxControl() { Init(); } + + // wxToggleButton or wxButton compatible constructor (also wxTextCtrl) + wxCustomButton(wxWindow* parent, wxWindowID id, + const wxString& label, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxCUSTBUT_TOGGLE, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxCustomButton")) + : wxControl() + { + Init(); + Create(parent,id,label,wxNullBitmap,pos,size,style,val,name); + } + + // wxBitmapButton compatible constructor + wxCustomButton(wxWindow *parent, wxWindowID id, + const wxBitmap& bitmap, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxCUSTBUT_TOGGLE, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxCustomButton")) + : wxControl() + { + Init(); + Create(parent,id,wxEmptyString,bitmap,pos,size,style,val,name); + } + + // Native constructor + wxCustomButton(wxWindow *parent, wxWindowID id, + const wxString& label, const wxBitmap& bitmap, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxCUSTBUT_TOGGLE|wxCUSTBUT_BOTTOM, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxCustomButton")) + : wxControl() + { + Init(); + Create(parent,id,label,bitmap,pos,size,style,val,name); + } + + virtual ~wxCustomButton(); + + bool Create(wxWindow* parent, + wxWindowID id, + const wxString& label, + const wxBitmap &bitmap, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = 0, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxCustomButton")); + + bool GetValue() const { return m_down%2 != 0; } + void SetValue( bool depressed ); + + // Use combinations of wxCustomButton_Style(s) + long GetButtonStyle() const { return m_button_style; } + bool SetButtonStyle( long style ); + + // Set the text label, wxEmptyString for none + void SetLabel( const wxString &label ); + + // set the bitmaps, ONLY this Label bitmap is used for calculating control size + // all bitmaps will be centered accordingly in any case + // call SetSet(GetBestSize()) if you change their size and want the control to resize appropriately + void SetBitmapLabel(const wxBitmap& bitmap); + void SetBitmapSelected(const wxBitmap& sel) { m_bmpSelected = sel; CalcLayout(TRUE); }; + void SetBitmapFocus(const wxBitmap& focus) { m_bmpFocus = focus; CalcLayout(TRUE); }; + void SetBitmapDisabled(const wxBitmap& disabled) { m_bmpDisabled = disabled; CalcLayout(TRUE); }; + // wxBitmapButton compatibility + void SetLabel(const wxBitmap& bitmap) { SetBitmapLabel(bitmap); } + + // retrieve the bitmaps + const wxBitmap& GetBitmapLabel() const { return m_bmpLabel; } + const wxBitmap& GetBitmapSelected() const { return m_bmpSelected; } + const wxBitmap& GetBitmapFocus() const { return m_bmpFocus; } + const wxBitmap& GetBitmapDisabled() const { return m_bmpDisabled; } + + // Creates a "disabled" bitmap by dithering it with the background colour + wxBitmap CreateBitmapDisabled(const wxBitmap &bitmap) const; + + // set/get the margins (in pixels) around the label and bitmap + // if fit = TRUE then resize the button to fit + void SetMargins(const wxSize &margin, bool fit = FALSE); + + // set/get the margins around the text label + // the inter bitmap/label margin is the max of either margin, not the sum + void SetLabelMargin(const wxSize &margin, bool fit = FALSE); + wxSize GetLabelMargin() const { return m_labelMargin; } + // set/get the margins around the bitmap + // the inter bitmap/label margin is the max of either margin, not the sum + void SetBitmapMargin(const wxSize &margin, bool fit = FALSE); + wxSize GetBitmapMargin() const { return m_bitmapMargin; } + + // can be used to activate the focused behavior (see MenuButton) + void SetFocused(bool focused) { m_focused = focused; Refresh(FALSE); } + bool GetFocused() const { return m_focused; } + +protected: + void OnPaint(wxPaintEvent &event); + void Redraw(); + virtual void Paint( wxDC &dc ); + + virtual wxSize DoGetBestSize() const; + + virtual void SendEvent(); + + void OnMouseEvents(wxMouseEvent &event); + + void OnSize( wxSizeEvent &event ); + + virtual void CalcLayout(bool refresh); + + long m_down; // toggle state if m_down%2 then depressed + bool m_focused; // mouse in window + long m_button_style; + + // the bitmaps for various states + wxBitmap m_bmpLabel, + m_bmpSelected, + m_bmpFocus, + m_bmpDisabled; + + // the margins around the label/bitmap + wxSize m_labelMargin, + m_bitmapMargin; + + wxPoint m_bitmapPos, + m_labelPos; + + wxTimer *m_timer; + + wxEventType m_eventType; // store the mouse event type + +private: + void Init(); + DECLARE_DYNAMIC_CLASS(wxCustomButton) + DECLARE_EVENT_TABLE() +}; + +//----------------------------------------------------------------------------- +// wxMenuButton styles +//----------------------------------------------------------------------------- + +#define wxMENUBUTTON_DROP_WIDTH 10 +#define wxMENUBUTTON_DROP_HEIGHT 22 + +enum wxMenuButton_Styles +{ + wxMENUBUT_FLAT = wxCUSTBUT_FLAT +}; + +//----------------------------------------------------------------------------- +// wxMenuButton +//----------------------------------------------------------------------------- + +class wxMenuButton : public wxControl +{ +public: + + wxMenuButton() : wxControl() { Init(); } + + // Use this constructor if you need one compatible with a wxBitmapButton + // setup the button later with AssignMenu + wxMenuButton( wxWindow* parent, wxWindowID id, + const wxBitmap &bitmap, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = 0, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxMenuButton")) + : wxControl() + { + Init(); + Create(parent,id,wxEmptyString,bitmap,pos,size,style,val,name); + } + + virtual ~wxMenuButton(); + + bool Create( wxWindow* parent, + wxWindowID id, + const wxString &label, + const wxBitmap &bitmap, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxNO_BORDER, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxMenuButton")); + + // Gets the id of the first selected radio item or wxNOT_FOUND (-1) if none + int GetSelection() const; + + // This menu will be displayed when the dropdown button is pressed. + // if static_menu is FALSE it will be deleted when the buttton is destroyed. + void AssignMenu(wxMenu *menu, bool static_menu = FALSE); + + wxMenu *GetMenu() const { return m_menu; } + + // get a pointer to the label button, for turning it into a toggle perhaps + wxCustomButton *GetLabelButton() const { return m_labelButton; } + wxCustomButton *GetDropDownButton() const { return m_dropdownButton; } + + void SetToolTip(const wxString &tip); + void SetToolTip(wxToolTip *tip); + +protected: + void OnButton(wxCommandEvent &event); + + virtual void DoSetSize(int x, int y, int width, int height, + int sizeFlags = wxSIZE_AUTO); + + virtual wxSize DoGetBestSize(); + +// FIXME! - in MSW the radio items don't check themselves +#ifdef __WXMSW__ + void OnMenu( wxCommandEvent &event ); +#endif + + wxCustomButton *m_labelButton; + wxCustomButton *m_dropdownButton; + + wxMenu *m_menu; + bool m_menu_static; + long m_style; + +private: + void Init(); + DECLARE_DYNAMIC_CLASS(wxMenuButton) + DECLARE_EVENT_TABLE() +}; + +//----------------------------------------------------------------------------- +// wxMenuButtonEvents +// +// EVT_MENUBUTTON_OPEN(id, fn) - menu is about to be opened, (dis)(en)able items +// or call Veto() to stop menu from popping up +// this is a wxNotifyEvent +//----------------------------------------------------------------------------- + +BEGIN_DECLARE_EVENT_TYPES() + DECLARE_LOCAL_EVENT_TYPE( wxEVT_MENUBUTTON_OPEN, 0 ) +END_DECLARE_EVENT_TYPES() + +#define EVT_MENUBUTTON_OPEN(id, fn) DECLARE_EVENT_TABLE_ENTRY(wxEVT_MENUBUTTON_OPEN, id, wxID_ANY, (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) (wxNotifyEventFunction) & fn, (wxObject *) NULL ), + +#endif // _WX_MENUBTN_H_ diff --git a/applications/osmo4_wx/osmo4.ico b/applications/osmo4_wx/osmo4.ico new file mode 100644 index 0000000000000000000000000000000000000000..36ff66711771065305f263aee09bda915091c6b0 GIT binary patch literal 15086 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 Ih+0ATC&$&)D*ylh literal 0 HcmV?d00001 diff --git a/applications/osmo4_wx/osmo4.xpm b/applications/osmo4_wx/osmo4.xpm new file mode 100644 index 0000000..b1a4cd6 --- /dev/null +++ b/applications/osmo4_wx/osmo4.xpm @@ -0,0 +1,301 @@ +/* XPM */ +static char * osmo4[] = { +"32 32 266 2", +" c None", +". c #990909", +"+ c #A10505", +"@ c #AB0404", +"# c #AF0202", +"$ c #B10303", +"% c #AE0202", +"& c #A90505", +"* c #A10606", +"= c #990E0E", +"- c #990D0D", +"; c #A80303", +"> c #BD0000", +", c #D40000", +"' c #DE0000", +") c #E20000", +"! c #E10000", +"~ c #DC0000", +"{ c #CD0000", +"] c #B80101", +"^ c #A40909", +"/ c #A20505", +"( c #BD0101", +"_ c #E00000", +": c #CB0000", +"< c #A80000", +"[ c #7E0101", +"} c #6B0202", +"| c #670202", +"1 c #720202", +"2 c #920000", +"3 c #B70000", +"4 c #D70000", +"5 c #B80303", +"6 c #9D0B0B", +"7 c #980909", +"8 c #B00202", +"9 c #DB0000", +"0 c #B50909", +"a c #8A1313", +"b c #1C0505", +"c c #070505", +"d c #080808", +"e c #0A0A0A", +"f c #090909", +"g c #070707", +"h c #0C0404", +"i c #5B1414", +"j c #A70F0F", +"k c #CC0303", +"l c #CC0000", +"m c #AB0505", +"n c #950D0D", +"o c #B60101", +"p c #DD0000", +"q c #CF0202", +"r c #9C1414", +"s c #141414", +"t c #0C0C0C", +"u c #0F0F0F", +"v c #111111", +"w c #101010", +"x c #0E0E0E", +"y c #B70707", +"z c #DF0000", +"A c #D50000", +"B c #A90606", +"C c #970E0E", +"D c #B30202", +"E c #C50505", +"F c #812121", +"G c #191919", +"H c #161616", +"I c #181818", +"J c #171717", +"K c #151515", +"L c #AF0A0A", +"M c #D70101", +"N c #D60000", +"O c #AD0606", +"P c #AA0303", +"Q c #DA0000", +"R c #C80505", +"S c #8E1E1E", +"T c #1D1D1D", +"U c #1F1F1F", +"V c #202020", +"W c #B00909", +"X c #CE0000", +"Y c #9F0A0A", +"Z c #9B0808", +"` c #D20000", +" . c #D80101", +".. c #8B1B1B", +"+. c #2D2D2D", +"@. c #272727", +"#. c #262626", +"$. c #252525", +"%. c #BB0606", +"&. c #BB0303", +"*. c #AF0303", +"=. c #AF0B0B", +"-. c #303030", +";. c #2B2B2B", +">. c #323232", +",. c #CD0202", +"'. c #A60A0A", +"). c #9E0909", +"!. c #CF0000", +"~. c #CE0404", +"{. c #292929", +"]. c #B10808", +"^. c #A70303", +"/. c #B60A0A", +"(. c #961010", +"_. c #B90000", +":. c #9C1212", +"<. c #1A1A1A", +"[. c #222222", +"}. c #1C1C1C", +"|. c #C20202", +"1. c #A30606", +"2. c #C60000", +"3. c #2A2A2A", +"4. c #3B3B3B", +"5. c #444444", +"6. c #434343", +"7. c #3A3A3A", +"8. c #1E1E1E", +"9. c #131313", +"0. c #BB0A0A", +"a. c #AD0707", +"b. c #9A1515", +"c. c #D30000", +"d. c #353535", +"e. c #484848", +"f. c #5F5F5F", +"g. c #6C6C6C", +"h. c #646464", +"i. c #565656", +"j. c #343434", +"k. c #212121", +"l. c #121212", +"m. c #B30505", +"n. c #B10404", +"o. c #9A1111", +"p. c #424242", +"q. c #555555", +"r. c #6B6B6B", +"s. c #757575", +"t. c #6E6E6E", +"u. c #606060", +"v. c #4E4E4E", +"w. c #3F3F3F", +"x. c #2C2C2C", +"y. c #B10909", +"z. c #B60707", +"A. c #9B1515", +"B. c #D00000", +"C. c #494949", +"D. c #595959", +"E. c #676767", +"F. c #696969", +"G. c #616161", +"H. c #525252", +"I. c #454545", +"J. c #B20707", +"K. c #363636", +"L. c #626262", +"M. c #5B5B5B", +"N. c #505050", +"O. c #282828", +"P. c #B80B0B", +"Q. c #AC0707", +"R. c #AA1111", +"S. c #0D0D0D", +"T. c #585858", +"U. c #545454", +"V. c #4B4B4B", +"W. c #BF0303", +"X. c #9F0B0B", +"Y. c #B90707", +"Z. c #242424", +"`. c #313131", +" + c #3E3E3E", +".+ c #474747", +"++ c #4D4D4D", +"@+ c #4F4F4F", +"#+ c #4C4C4C", +"$+ c #3D3D3D", +"%+ c #D90000", +"&+ c #8E1010", +"*+ c #A00808", +"=+ c #D00303", +"-+ c #3C3C3C", +";+ c #0B0B0B", +">+ c #AE0808", +",+ c #C10101", +"'+ c #BB0202", +")+ c #BA0A0A", +"!+ c #1B1B1B", +"~+ c #2F2F2F", +"{+ c #C10303", +"]+ c #A10A0A", +"^+ c #9F0707", +"/+ c #D80000", +"(+ c #BE0303", +"_+ c #821C1C", +":+ c #C20404", +"<+ c #232323", +"[+ c #A90C0C", +"}+ c #970C0C", +"|+ c #960E0E", +"1+ c #BE0101", +"2+ c #C60505", +"3+ c #131212", +"4+ c #B00B0B", +"5+ c #CE0101", +"6+ c #A80707", +"7+ c #990B0B", +"8+ c #C40404", +"9+ c #AD0909", +"0+ c #D00101", +"a+ c #B00505", +"b+ c #7B1C1C", +"c+ c #960C0C", +"d+ c #BC0202", +"e+ c #BC0808", +"f+ c #962525", +"g+ c #482E2E", +"h+ c #1E1616", +"i+ c #141010", +"j+ c #2F2020", +"k+ c #703636", +"l+ c #A81212", +"m+ c #A90707", +"n+ c #8E1313", +"o+ c #D30606", +"p+ c #B91E1E", +"q+ c #901919", +"r+ c #661010", +"s+ c #540F0F", +"t+ c #4F0D0D", +"u+ c #5A0F0F", +"v+ c #7A1515", +"w+ c #A21E1E", +"x+ c #C51212", +"y+ c #D60101", +"z+ c #980D0D", +"A+ c #930C0C", +"B+ c #B30404", +"C+ c #C60202", +"D+ c #7B1919", +"E+ c #8C1515", +"F+ c #9F0909", +"G+ c #B20303", +"H+ c #C30000", +"I+ c #CA0000", +"J+ c #C90000", +"K+ c #BC0101", +"L+ c #AA0505", +"M+ c #8F1111", +"N+ c #7F1E1E", +"O+ c #732626", +"P+ c #762A2A", +"Q+ c #6E2828", +" ", +" . + @ # $ % & * = ", +" - ; > , ' ) ) ! ) ! ~ { ] ^ ", +" / ( ' _ : < [ } | 1 2 3 4 ) , 5 6 ", +" 7 8 9 ' 0 a b c d e e f g h i j k ) l m ", +" n o p q r s t u v v w t x y z A B ", +" C D ) E F G H I G J K L M N O ", +" P Q R S T U U T V W ' X Y ", +" Z ` ... +.@.#.$. %.! &. ", +" *.) =. -.;.>. ,.4 '. ", +" ).!.~. {. ].) ( ", +" ^.z /. $. A ` (. ", +" _.! :. K K <.V [.}.K K |.z 1. ", +" 2.9 v <.3.4.5.6.7.;.8.9.J 0.) a. ", +" b.!.c. w }.d.e.f.g.h.i.5.j.k.l. m._ n. ", +" o.` !. u J 3.p.q.r.s.t.u.v.w.x.T w y._ z. ", +" A.B.` u k.j.C.D.E.g.F.G.H.I.j.$.9.l. m._ J. ", +" : 4 s v #.K.C.q.u.h.L.M.N.I.K.O.J x P.) Q. ", +" > z R. S.9.#.d.5.v.i.D.T.U.V.p.j.{.G t W._ X. ", +" % ) Y. l.9.Z.`. +.+++N.@+#+I.$+>.@.I t { %+&+ ", +" *+, =+ x w V ;.K.$+6.I.I.p.-+K.;.[.H ;+ >+z ,+ ", +" '+_ )+ x S.!+Z.~+d.7.-+-+7.d.~+#.8.9.;+ {+_ ]+ ", +" ^+/+, e s }.$.;.~+`.`.-.;.#.U J S.w z.' (+_+ ", +" ] ) :+ t S.K T [.@.O.O.@.<+U I v ;+ [+` ~ }+ ", +" |+1+) 2+ e t 9.I }.T 8.}.G K u e 3+ 4+5+' 6+ ", +" 7+l ) 8+ e ;+u 9.K K 9.v S.f w 9+0+) a+b+ ", +" c+d+) N e+f+g+h+;+f e f i+j+k+l+|.~ /+m+ ", +" n+D N ! o+p+q+r+s+t+u+v+w+x+y+! X z+ ", +" A+B+X _ ! ~ /+N %+' ) ~ C+Y D+ ", +" E+F+G+H+I+l J+K+L+M+N+ ", +" O+P+Q+ ", +" "}; diff --git a/applications/osmo4_wx/playlist.xpm b/applications/osmo4_wx/playlist.xpm new file mode 100644 index 0000000..6ec3810 --- /dev/null +++ b/applications/osmo4_wx/playlist.xpm @@ -0,0 +1,158 @@ +/* XPM */ +static char* pl_open[] = { +"16 16 5 1", +" c #000000", +"! c #808000", +"# c #C0C0C0", +"$ c #FFFF00", +"% c #FFFFFF", +"################", +"################", +"######### ####", +"######## ### # #", +"############# #", +"# ######## #", +" %$% #####", +" $%$%$%$%$ #####", +" %$%$%$%$% #####", +" $%$% #", +" %$% !!!!!!!!! #", +" $% !!!!!!!!! ##", +" % !!!!!!!!! ###", +" !!!!!!!!! ####", +" #####", +"################"}; + +/* XPM */ +static char* pl_save[] = { +"16 16 3 1", +" c #000000", +"! c #808000", +"# c #C0C0C0", +"################", +"# #", +"# ! ######## # #", +"# ! ######## #", +"# ! ######## ! #", +"# ! ######## ! #", +"# ! ######## ! #", +"# ! ######## ! #", +"# !! !! #", +"# !!!!!!!!!!!! #", +"# !! ! #", +"# !! ## ! #", +"# !! ## ! #", +"# !! ## ! #", +"## #", +"################"}; + +/* XPM */ +static char* pl_add[] = { +"16 16 2 1", +" c #000000", +"! c #C0C0C0", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!! !!!", +"!! !!!", +"!! !!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static char* pl_rem[] = { +"16 16 2 1", +" c #000000", +"! c #C0C0C0", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!! !!!", +"!! !!!", +"!! !!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static char* pl_up[] = { +"16 16 2 1", +" c #000000", +"! c #C0C0C0", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!! !!!!!!!!", +"!!!!!! !!!!!!!", +"!!!!! !!!!!!", +"!!!! !!!!!", +"!!! !!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static char* pl_down[] = { +"16 16 2 1", +" c #000000", +"! c #C0C0C0", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!! !!!!", +"!!!! !!!!!", +"!!!!! !!!!!!", +"!!!!!! !!!!!!!", +"!!!!!!! !!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static char* pl_sort[] = { +"16 16 2 1", +" c #000000", +"! c #C0C0C0", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!! !!!!! !!!!!", +"!!!! !!!! !!!!", +"!!!! !!! !!!", +"!!!! !!!!! !!!!!", +"!!!! !!!!! !!!!!", +"!!!! !!!!! !!!!!", +"!!!! !!!!! !!!!!", +"!! !!! !!!!!", +"!!! !!!! !!!!!", +"!!!! !!!!! !!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + diff --git a/applications/osmo4_wx/resource.h b/applications/osmo4_wx/resource.h new file mode 100644 index 0000000..2d49449 --- /dev/null +++ b/applications/osmo4_wx/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by wxOsmo4.rc +// +#define IDI_ICON1 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/applications/osmo4_wx/toolbar.xpm b/applications/osmo4_wx/toolbar.xpm new file mode 100644 index 0000000..d9cdc79 --- /dev/null +++ b/applications/osmo4_wx/toolbar.xpm @@ -0,0 +1,254 @@ +/* XPM */ +static char* tool_open_file[] = { +"16 16 2 1", +" c #000000", +"! c none", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!! !!!!!!!", +"!!!!!! !!!!!!", +"!!!!! !!!!!", +"!!!! !!!!", +"!!! !!!", +"!! !!", +"!! !!", +"!!!!!!!!!!!!!!!!", +"!! !!", +"!! !!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static char* tool_prev[] = { +"16 16 3 1", +" c #000000", +"! c none", +"# c #FF0000", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!! !!", +"!!!!!!!!!! !!", +"!!!!!!!! ## !!", +"!!!!!! #### !!", +"!!!! ###### !!", +"!!! ######## !!", +"!!!! ##### !!", +"!!!!!! ### !!", +"!!!!!!!! # !!", +"!!!!!!!!!! !!", +"!!!!!!!!!!!! !!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static char* tool_next[] = { +"16 16 3 1", +" c #000000", +"! c none", +"# c #FF0000", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!! !!!!!!!!!!!!", +"!! !!!!!!!!!!", +"!! ## !!!!!!!!", +"!! #### !!!!!!", +"!! ###### !!!!", +"!! ######## !!!", +"!! ###### !!!!", +"!! #### !!!!!!", +"!! ## !!!!!!!!", +"!! !!!!!!!!!!", +"!! !!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static char* tool_play[] = { +"16 16 2 1", +" c #000000", +"! c none", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!! !!!!!!!!!!!!", +"!! !!!!!!!!!!", +"!! !!!!!!!!", +"!! !!!!!!", +"!! !!!!", +"!! !!", +"!! !!!!", +"!! !!!!!!", +"!! !!!!!!!!", +"!! !!!!!!!!!!", +"!! !!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static char* tool_pause[] = { +"16 16 3 1", +" c #000000", +"! c #808080", +"# c none", +"################", +"################", +"################", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"################", +"################"}; + + +/* XPM */ +static char* tool_step[] = { +"16 16 2 1", +" c #000000", +"! c none", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!! !!!!!!!!!!!", +"!! !!!!!!!!!", +"!! !!!!!!!", +"!!!! !!!!!", +"!!!!!! !!!", +"!!!!!!!! !!", +"!!!!!! !!!", +"!!!! !!!!!", +"!! !!!!!!!", +"!! !!!!!!!!!", +"!! !!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + + +/* XPM */ +static char* tool_stop[] = { +"16 16 2 1", +" c #000000", +"! c none", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static char* tool_info[] = { +"16 16 3 1", +" c #000000", +"! c none", +"# c #0000FF", +"!!!!!!!!!!!!!!!!", +"!!!!! !!!!!", +"!!! !!!", +"!! ###### !!", +"! ####!!#### !", +"! ####!!#### !", +" ############ ", +" #####!!##### ", +" ######!!##### ", +" #####!!##### ", +" #####!!##### ", +"! ####!!#### !", +"! #####!!### !", +"!! ###### !!", +"!!! !!!", +"!!!!! !!!!!"}; + + +/* XPM */ +static char* tool_config[] = { +"16 16 3 1", +" c #000000", +"! c #808080", +"# c none", +"################", +"################", +"## ## ######", +"## ## # ## # ##", +"## ## #", +"## #!# ####### #", +"## # #!# ## ## #", +"## #!# #!# ! # #", +"## # #!# ## ## #", +"## #!# #!##### #", +"## # #!# ## ## #", +"## #!# #!# ! # #", +"## # #!# ## ## #", +"## #", +"################", +"################"}; + +/* XPM */ +static char* tool_sw_2d[] = { +"16 16 4 1", +" c #FF0000", +". c #C0C0C0", +"+ c #0000FF", +"@ c #000000", +" .............. ", +". ..++..++++.. .", +".. +..+..+..+ ..", +"... ..+..+.. ...", +".... +...+. +...", +"...+. +..+ .+...", +"...+++ .+ ++....", +"....... .......", +"....@@ @@ @.....", +"...@. .@.@ .....", +"...@ ....@. ....", +"... ..@@.@.. ...", +".. @...@.@.@. ..", +". ..@@@@@@@@.. .", +" .............. ", +"................"}; + +/* XPM */ +static char* tool_sw_3d[] = { +"16 16 4 1", +" c #FFFFFF", +". c #C0C0C0", +"+ c #0000FF", +"@ c #000000", +" ", +"................", +"....++..++++....", +"...+..+..+..+...", +"......+..+..+...", +"....++...+..+...", +"...+..+..+..+...", +"...++++.++++....", +"................", +"....@@@@@@@.....", +"...@...@.@......", +"...@.....@......", +"...@..@@.@......", +"...@...@.@.@....", +"....@@@@@@@@....", +"................"}; + diff --git a/applications/osmo4_wx/wxGPACControl.cpp b/applications/osmo4_wx/wxGPACControl.cpp new file mode 100644 index 0000000..59c788c --- /dev/null +++ b/applications/osmo4_wx/wxGPACControl.cpp @@ -0,0 +1,1031 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#include "wxOsmo4.h" +#include +#include +#include +#include +#include +#include + +#include + +#include "wxGPACControl.h" + + +#define NUM_RATES 11 +static char *BIFSRates[11] = +{ + "5.0", + "7.5", + "10.0", + "12.5", + "15.0", + "24.0", + "25.0", + "30.0", + "50.0", + "60.0", + "100.0" +}; + +void wxGPACControl::SetYUVLabel() +{ + u32 yuv_format = gf_term_get_option(m_pApp->m_term, GF_OPT_YUV_FORMAT); + if (!yuv_format) { + m_yuvtxt->SetLabel(wxT("(No YUV used)")); + } else { + char str[100]; + sprintf(str, "(%s used)", gf_4cc_to_str(yuv_format)); + m_yuvtxt->SetLabel(wxString(str, wxConvUTF8) ); + } +} + +wxGPACControl::wxGPACControl(wxWindow *parent) + : wxDialog(parent, -1, wxString(wxT("GPAC Control Panel"))) +{ + const char *sOpt; + SetSize(320, 240); + u32 i; + wxBoxSizer *bs; + Centre(); + + m_pApp = (wxOsmo4Frame *)parent; + + s_main = new wxBoxSizer(wxVERTICAL); + + s_header = new wxBoxSizer(wxHORIZONTAL); + //s_header->Add(new wxStaticText(this, 0, wxT("Category"), wxDefaultPosition, wxSize(60, 20)), wxALIGN_CENTER); + m_select = new wxComboBox(this, ID_SELECT, wxT(""), wxDefaultPosition, wxSize(120, 30), 0, NULL, wxCB_READONLY); + s_header->Add(m_select, 2, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_header->Add( new wxButton(this, ID_APPLY, wxT("Apply"), wxDefaultPosition, +#ifdef WIN32 + wxSize(40, 20) +#else + wxSize(40, 30) +#endif + ), + 1, wxALIGN_TOP|wxALIGN_RIGHT|wxADJUST_MINSIZE); + s_main->Add(s_header, 0, wxEXPAND, 0); + + /*general section*/ + s_general = new wxBoxSizer(wxVERTICAL); + m_loop = new wxCheckBox(this, 0, wxT("Loop at End"), wxPoint(10, 40), wxSize(140, 20)); + s_general->Add(m_loop); + m_lookforsubs = new wxCheckBox(this, 0, wxT("Look for Subtitles"), wxPoint(180, 40), wxSize(140, 20)); + s_general->Add(m_lookforsubs); + m_noconsole = new wxCheckBox(this, 0, wxT("Disable console messages"), wxPoint(10, 80), wxSize(180, 20)); + s_general->Add(m_noconsole); + m_viewxmt = new wxCheckBox(this, 0, wxT("View graph in XMT-A format"), wxPoint(10, 120), wxSize(180, 20)); + s_general->Add(m_viewxmt); + s_main->Add(s_general, 0, wxEXPAND, 0); + + /*MPEG-4 systems*/ + s_mpeg4 = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Prefered Stream Language")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_lang = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_lang, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_mpeg4->Add(bs, 0, wxALL|wxEXPAND, 2); + + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Decoder Threading Mode")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_thread = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_thread, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_mpeg4->Add(bs, 0, wxALL|wxEXPAND, 2); + m_bifsalwaysdrawn = new wxCheckBox(this, 0, wxT("Always draw late BIFS frames")); + s_mpeg4->Add(m_bifsalwaysdrawn); + m_singletime = new wxCheckBox(this, 0, wxT("Force Single Timeline")); + s_mpeg4->Add(m_singletime); + s_main->Add(s_mpeg4, 0, wxEXPAND, 0); + + /*media decoders*/ + s_mdec = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Prefered Audio Output")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_decaudio = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_decaudio, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_mdec->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Prefered Video Output")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_decvideo = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_decvideo, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_mdec->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_mdec, 0, wxEXPAND, 0); + + /*Rendering*/ + s_rend = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Target Frame Rate")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_fps = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_fps, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Anti-Aliasing")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_aa = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_aa, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Graphics Driver")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_graph = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_graph, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_draw_bounds = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(new wxStaticText(this, 0, wxT("Bounds")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + bs->Add(m_draw_bounds, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend->Add(bs, 0, wxALL|wxEXPAND, 2); + m_fast = new wxCheckBox(this, 0, wxT("Fast Rendering")); + m_force_size = new wxCheckBox(this, 0, wxT("Force Scene Size")); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(m_fast, wxALIGN_CENTER | wxADJUST_MINSIZE); + bs->Add(m_force_size, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend->Add(bs, 0, wxALL|wxEXPAND, 2); + m_use3D = new wxCheckBox(this, 0, wxT("Use 3D Renderer")); + s_rend->Add(m_use3D, 0, wxALL|wxEXPAND, 2); + m_bWas3D = m_use3D->GetValue(); + s_main->Add(s_rend, 0, wxEXPAND, 0); + + /*Render 2D*/ + s_rend2d = new wxBoxSizer(wxVERTICAL); + m_direct = new wxCheckBox(this, 0, wxT("Direct Rendering")); + s_rend2d->Add(m_direct, 0, wxALL|wxEXPAND, 2); + m_scalable = new wxCheckBox(this, 0, wxT("Scalable Zoom")); + s_rend2d->Add(m_scalable, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_noyuv = new wxCheckBox(this, 0, wxT("Disable YUV hardware")); + bs->Add(m_noyuv, wxALIGN_CENTER | wxADJUST_MINSIZE); + m_yuvtxt = new wxStaticText(this, 0, wxT("(No YUV used)"), wxDefaultPosition, wxSize(60, 20), wxALIGN_LEFT); + bs->Add(m_yuvtxt, wxALIGN_CENTER|wxADJUST_MINSIZE); + s_rend2d->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_rend2d, 0, wxEXPAND, 0); + + /*Render 3D*/ + s_rend3d = new wxBoxSizer(wxVERTICAL); + m_raster_outlines = new wxCheckBox(this, 0, wxT("Use OpenGL Raster outlines")); + s_rend3d->Add(m_raster_outlines, 0, wxALL|wxEXPAND, 2); + m_polyaa = new wxCheckBox(this, 0, wxT("Enable polygon anti-aliasing")); + s_rend3d->Add(m_polyaa, 0, wxALL|wxEXPAND, 2); + m_nobackcull = new wxCheckBox(this, 0, wxT("Disable backface culling")); + s_rend3d->Add(m_nobackcull, 0, wxALL|wxEXPAND, 2); + + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Wireframe mode")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_wire = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_wire, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend3d->Add(bs, 0, wxALL|wxEXPAND, 2); + + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Draw Normals")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_normals = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_normals, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend3d->Add(bs, 0, wxALL|wxEXPAND, 2); + + m_emulpow2 = new wxCheckBox(this, 0, wxT("Emulate power-of-two textures for video")); + s_rend3d->Add(m_emulpow2, 0, wxALL|wxEXPAND, 2); + m_norectext = new wxCheckBox(this, 0, wxT("Disable rectangular texture extensions")); + s_rend3d->Add(m_norectext, 0, wxALL|wxEXPAND, 2); + m_copypixels = new wxCheckBox(this, 0, wxT("Bitmap node uses direct pixel copy")); + s_rend3d->Add(m_copypixels, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_rend3d, 0, wxEXPAND, 0); + + /*video*/ + s_video = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Video Driver")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_video = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_video , wxALIGN_CENTER | wxADJUST_MINSIZE); + s_video->Add(bs, 0, wxALL|wxEXPAND, 2); + m_switchres = new wxCheckBox(this, 0, wxT("Change video resolution in fullscreen")); + s_video->Add(m_switchres, 0, wxALL|wxEXPAND, 2); + m_usehwmem = new wxCheckBox(this, 0, wxT("Use hardware memory in 2D mode")); + s_video->Add(m_usehwmem, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_video, 0, wxEXPAND, 0); + + + /*audio*/ + s_audio = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Audio Driver")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_audio = new wxComboBox(this, ID_AUDIO_DRIVER, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_audio, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_audio->Add(bs, 0, wxALL|wxEXPAND, 2); + m_forcecfg = new wxCheckBox(this, ID_FORCE_AUDIO, wxT("Force Audio Config")); + m_forcecfg->SetValue(1); + s_audio->Add(m_forcecfg, 0, wxALL|wxEXPAND, 2); + + + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Number of buffers")), wxALIGN_CENTER|wxADJUST_MINSIZE); + m_nbbuf = new wxSpinCtrl(this, -1, wxT(""), wxDefaultPosition, wxSize(20, 20), wxSP_WRAP | wxSP_ARROW_KEYS, 1, 30, 15); + m_nbbuf->SetValue(8); + bs->Add(m_nbbuf, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_audio->Add(bs, 0, wxALL|wxEXPAND, 2); + + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Total length in ms")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_buflen = new wxSpinCtrl(this, -1, wxT(""), wxDefaultPosition, wxSize(20, 20), wxSP_WRAP | wxSP_ARROW_KEYS, 1, 1000); + m_buflen->SetValue(400); + bs->Add(m_buflen, wxALIGN_CENTER | wxADJUST_MINSIZE|wxLEFT,10); + s_audio->Add(bs, 0, wxALL|wxEXPAND, 2); + + m_noresync = new wxCheckBox(this, -1, wxT("Disable Resynchronization")); + s_audio->Add(m_noresync); + m_nomulitch = new wxCheckBox(this, -1, wxT("Disable Multichannel")); + s_audio->Add(m_nomulitch); +#ifdef WIN32 + m_notifs = new wxCheckBox(this, -1, wxT("Disable DirectSound Notifications")); + s_audio->Add(m_notifs); +#endif + s_main->Add(s_audio, 0, wxEXPAND, 0); + + /*font*/ + s_font = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Font Engine")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_font = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_font, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_font->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("System Font Directory")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_fontdir = new wxButton(this, ID_FONT_DIR, wxT("..."), wxDefaultPosition, wxDefaultSize); + bs->Add(m_fontdir, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_font->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Text Texturing Mode")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_texturemode = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_texturemode, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_font->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_font, 0, wxEXPAND, 0); + + /*download*/ + s_dnld = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Cache Directory")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_cachedir = new wxButton(this, ID_CACHE_DIR, wxT("...")); + bs->Add(m_cachedir, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_dnld->Add(bs, 0, wxALL|wxEXPAND, 2); + m_cleancache = new wxCheckBox(this, -1, wxT("Remove temp files on exit")); + s_dnld->Add(m_cleancache); + m_restartcache = new wxCheckBox(this, -1, wxT("Always redownload incomplete cached files")); + s_dnld->Add(m_restartcache); + bs = new wxBoxSizer(wxHORIZONTAL); + m_progressive = new wxCheckBox(this, ID_PROGRESSIVE, wxT("XML progressive load")); + bs->Add(m_progressive, wxALIGN_CENTER | wxADJUST_MINSIZE); + m_sax_duration = new wxTextCtrl(this, 0, wxT(""), wxPoint(10, 120), wxSize(60, 20)); + bs->Add(m_sax_duration, wxALIGN_CENTER | wxADJUST_MINSIZE); + bs->Add(new wxStaticText(this, 0, wxT("max load slice (ms)")), wxADJUST_MINSIZE | wxALIGN_CENTER); + s_dnld->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_use_proxy = new wxCheckBox(this, ID_USE_PROXY, wxT("Use proxy")); + bs->Add(m_use_proxy, wxALIGN_CENTER | wxADJUST_MINSIZE); + m_proxy_name = new wxTextCtrl(this, 0, wxT(""), wxPoint(10, 120), wxSize(60, 20)); + bs->Add(m_proxy_name, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_dnld->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_dnld, 0, wxEXPAND, 0); + + /*streaming*/ + s_stream = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Default RTSP port")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_port = new wxComboBox(this, ID_RTSP_PORT, wxT(""), wxPoint(160, 40), wxSize(140, 30), 0, NULL, wxCB_READONLY); + bs->Add(m_port, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_stream->Add(bs, 0, wxALL|wxEXPAND, 2); + m_rtsp = new wxCheckBox(this, ID_RTP_OVER_RTSP, wxT("RTP over RTSP"), wxPoint(10, 80), wxSize(140, 20)); + m_reorder = new wxCheckBox(this, -1, wxT("use RTP reordering"), wxPoint(160, 80), wxSize(130, 20)); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(m_rtsp, wxALIGN_CENTER | wxADJUST_MINSIZE); + bs->Add(m_reorder, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_stream->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_timeout = new wxTextCtrl(this, 0, wxT(""), wxPoint(10, 120), wxSize(60, 20)); + bs->Add(new wxStaticText(this, 0, wxT("Control Timeout (ms): ")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + bs->Add(m_timeout, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_stream->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_buffer = new wxTextCtrl(this, 0, wxT(""), wxPoint(10, 150), wxSize(60, 20)); + bs->Add(new wxStaticText(this, 0, wxT("Media Buffering (ms): ")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + bs->Add(m_buffer, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_stream->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_dorebuffer = new wxCheckBox(this, ID_RTSP_REBUFFER, wxT("Rebuffer if below")); + bs->Add(m_dorebuffer, wxALIGN_CENTER | wxADJUST_MINSIZE); + m_rebuffer = new wxTextCtrl(this, 0, wxT(""), wxPoint(200, 180), wxSize(60, 20)); + bs->Add(m_rebuffer, wxALIGN_CENTER | wxADJUST_MINSIZE); + m_rebuffer->Disable(); + s_stream->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_stream, 0, wxEXPAND, 0); + + /*streaming cache*/ + s_rec = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Record To: ")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_recdir = new wxButton(this, ID_RECORD_DIR, wxT("...")); + bs->Add(m_recdir, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rec->Add(bs, 0, wxALL|wxEXPAND, 2); + m_overwrite = new wxCheckBox(this, -1, wxT("Overwrite existing files")); + s_rec->Add(m_overwrite); + bs = new wxBoxSizer(wxHORIZONTAL); + m_usename = new wxCheckBox(this, ID_USE_FILENAME, wxT("Use filename")); + m_recfile = new wxTextCtrl(this, 0, wxT("")); + bs->Add(m_usename, wxALIGN_CENTER | wxADJUST_MINSIZE); + bs->Add(m_recfile, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rec->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_rec, 0, wxEXPAND, 0); + + /*load options*/ + GF_Config *cfg = m_pApp->m_user.config; + /*general*/ + sOpt = gf_cfg_get_key(cfg, "General", "Loop"); + m_loop->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "General", "LookForSubtitles"); + m_lookforsubs->SetValue((sOpt && !stricmp(sOpt, "no")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "General", "ConsoleOff"); + m_noconsole->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "General", "ViewXMT"); + m_viewxmt->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + /*systems config*/ + sOpt = gf_cfg_get_key(cfg, "Systems", "Language3CC"); + if (!sOpt) sOpt = "eng"; + u32 select = 0; + i=0; + while (GF_ISO639_Lang[i]) { + /*only use common languages (having both 2- and 3-char code names)*/ + if (GF_ISO639_Lang[i+2][0]) { + m_lang->Append(wxString(GF_ISO639_Lang[i], wxConvUTF8) ); + if (sOpt && !stricmp(sOpt, GF_ISO639_Lang[i+1])) select = m_lang->GetCount() - 1; + } + i+=3; + } + m_lang->SetSelection(select); + sOpt = gf_cfg_get_key(cfg, "Systems", "ThreadingPolicy"); + select = 0; + m_thread->Append(wxT("Single Thread")); + m_thread->Append(wxT("Mutli Thread")); + if (sOpt && !stricmp(sOpt, "Multi")) select = 1; + m_thread->Append(wxT("Free")); + if (sOpt && !stricmp(sOpt, "Free")) select = 2; + m_thread->SetSelection(select); + sOpt = gf_cfg_get_key(cfg, "Systems", "ForceSingleClock"); + m_singletime->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Systems", "AlwaysDrawBIFS"); + m_bifsalwaysdrawn->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + + /*audio dec enum*/ + sOpt = gf_cfg_get_key(cfg, "Systems", "DefAudioDec"); + u32 count = gf_modules_get_count(m_pApp->m_user.modules); + GF_BaseDecoder *ifc_d; + select = 0; + s32 to_sel = 0; + for (i=0; im_user.modules, i, GF_MEDIA_DECODER_INTERFACE); + if (!ifc_d) continue; + if (ifc_d->CanHandleStream(ifc_d, GF_STREAM_AUDIO, 0, NULL, 0, 0)) { + if (sOpt && !stricmp(ifc_d->module_name, sOpt)) select = to_sel; + m_decaudio->Append(wxString(ifc_d->module_name, wxConvUTF8) ); + to_sel++; + } + gf_modules_close_interface((GF_BaseInterface *) ifc_d); + } + m_decaudio->SetSelection(select); + + /*video dec enum*/ + sOpt = gf_cfg_get_key(cfg, "Systems", "DefVideoDec"); + select = to_sel = 0; + for (i=0; im_user.modules, i, GF_MEDIA_DECODER_INTERFACE); + if (!ifc_d) continue; + if (ifc_d->CanHandleStream(ifc_d, GF_STREAM_VISUAL, 0, NULL, 0, 0)) { + if (sOpt && !stricmp(ifc_d->module_name, sOpt)) select = to_sel; + m_decvideo->Append(wxString(ifc_d->module_name, wxConvUTF8) ); + to_sel++; + } + gf_modules_close_interface((GF_BaseInterface *) ifc_d); + } + m_decvideo->SetSelection(select); + + /*rendering FIXME*/ + m_bWas3D = 0; + m_use3D->SetValue(m_bWas3D ? 1 : 0); + + sOpt = gf_cfg_get_key(cfg, "Compositor", "ForceSceneSize"); + m_force_size->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(cfg, "Compositor", "FrameRate"); + if (!sOpt) sOpt = "30.0"; + select = 0; + for (i = 0; iAppend(wxString(BIFSRates[i], wxConvUTF8) ); + if (sOpt && !stricmp(sOpt, BIFSRates[i]) ) select = i; + } + m_fps->SetSelection(select); + + sOpt = gf_cfg_get_key(cfg, "Compositor", "HighSpeed"); + m_fast->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(cfg, "Compositor", "AntiAlias"); + m_aa->Append(wxT("None")); + m_aa->Append(wxT("Text only")); + m_aa->Append(wxT("Complete")); + select = 2; + if (sOpt && !stricmp(sOpt, "Text")) select = 1; + else if (sOpt && !stricmp(sOpt, "None")) select = 0; + m_aa->SetSelection(select); + + sOpt = gf_cfg_get_key(cfg, "Compositor", "BoundingVolume"); + m_draw_bounds->Append(wxT("None")); + m_draw_bounds->Append(wxT("Box/Rect")); + m_draw_bounds->Append(wxT("AABB Tree")); + select = 0; + if (sOpt && !stricmp(sOpt, "Box")) select = 1; + else if (sOpt && !stricmp(sOpt, "AABB")) select = 2; + m_draw_bounds->SetSelection(select); + + /*render2d*/ + sOpt = gf_cfg_get_key(cfg, "Compositor", "DirectDraw"); + m_direct->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "ScalableZoom"); + m_scalable->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "DisableYUV"); + m_noyuv->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + SetYUVLabel(); + + /*graphics driver enum*/ + sOpt = gf_cfg_get_key(cfg, "Compositor", "Raster2D"); + GF_BaseInterface *ifce; + select = to_sel = 0; + for (i=0; im_user.modules, i, GF_RASTER_2D_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + m_graph->Append(wxString(((GF_BaseInterface *)ifce)->module_name, wxConvUTF8) ); + gf_modules_close_interface(ifce); + to_sel++; + } + m_graph->SetSelection(select); + + /*render3d*/ + sOpt = gf_cfg_get_key(cfg, "Compositor", "RasterOutlines"); + m_raster_outlines->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "EmulatePOW2"); + m_emulpow2->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "PolygonAA"); + m_polyaa->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "BackFaceCulling"); + m_nobackcull->SetValue((sOpt && !stricmp(sOpt, "Off")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "Wireframe"); + sOpt = gf_cfg_get_key(cfg, "Compositor", "BitmapCopyPixels"); + m_copypixels->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "DisableRectExt"); + m_norectext->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + m_wire->Append(wxT("No Wireframe")); + m_wire->Append(wxT("Wireframe Only")); + m_wire->Append(wxT("Solid and Wireframe")); + sOpt = gf_cfg_get_key(cfg, "Compositor", "Wireframe"); + if (sOpt && !stricmp(sOpt, "WireOnly")) m_wire->SetSelection(1); + else if (sOpt && !stricmp(sOpt, "WireOnSolid")) m_wire->SetSelection(2); + else m_wire->SetSelection(0); + m_normals->Append(wxT("Never")); + m_normals->Append(wxT("Per Face")); + m_normals->Append(wxT("Per Vertex")); + sOpt = gf_cfg_get_key(cfg, "Compositor", "DrawNormals"); + if (sOpt && !stricmp(sOpt, "PerFace")) m_normals->SetSelection(1); + else if (sOpt && !stricmp(sOpt, "PerVertex")) m_normals->SetSelection(2); + else m_normals->SetSelection(0); + + /*video*/ + sOpt = gf_cfg_get_key(cfg, "Video", "SwitchResolution"); + m_switchres->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Video", "UseHardwareMemory"); + m_usehwmem->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Video", "DriverName"); + select = to_sel = 0; + for (i=0; im_user.modules, i, GF_VIDEO_OUTPUT_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + m_video->Append(wxString(((GF_BaseInterface *)ifce)->module_name, wxConvUTF8) ); + gf_modules_close_interface(ifce); + to_sel++; + } + m_video->SetSelection(select); + + /*audio*/ + sOpt = gf_cfg_get_key(cfg, "Audio", "ForceConfig"); + m_forcecfg->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Audio", "NumBuffers"); + m_nbbuf->SetValue( sOpt ? wxString(sOpt, wxConvUTF8) : wxT("2")); + sOpt = gf_cfg_get_key(cfg, "Audio", "TotalDuration"); + m_buflen->SetValue( sOpt ? wxString(sOpt, wxConvUTF8) : wxT("120")); + wxCommandEvent event; + ForceAudio(event); + sOpt = gf_cfg_get_key(cfg, "Audio", "NoResync"); + m_noresync->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Audio", "DisableMultiChannel"); + m_nomulitch->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + /*driver enum*/ + sOpt = gf_cfg_get_key(cfg, "Audio", "DriverName"); + select = to_sel = 0; + for (i=0; im_user.modules, i, GF_AUDIO_OUTPUT_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + m_audio->Append(wxString(((GF_BaseInterface *)ifce)->module_name, wxConvUTF8) ); + gf_modules_close_interface(ifce); + to_sel++; + } + m_audio->SetSelection(select); +#ifdef WIN32 + sOpt = gf_cfg_get_key(cfg, "Audio", "DisableNotification"); + m_notifs->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + wxCommandEvent audevt; + OnSetAudioDriver(audevt); +#endif + + /*font*/ + sOpt = gf_cfg_get_key(cfg, "FontEngine", "FontReader"); + to_sel = select = 0; + for (i=0; im_user.modules, i, GF_FONT_READER_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + m_font->Append(wxString(((GF_BaseInterface *)ifce)->module_name, wxConvUTF8) ); + gf_modules_close_interface(ifce); + to_sel++; + } + m_font->SetSelection(select); + sOpt = gf_cfg_get_key(cfg, "FontEngine", "FontDirectory"); + if (sOpt) m_fontdir->SetLabel(wxString(sOpt, wxConvUTF8) ); + sOpt = gf_cfg_get_key(cfg, "Compositor", "TextureTextMode"); + m_texturemode->Append(wxT("Default")); + m_texturemode->Append(wxT("Never")); + m_texturemode->Append(wxT("Always")); + if (sOpt && !stricmp(sOpt, "Always")) m_texturemode->SetSelection(2); + else if (sOpt && !stricmp(sOpt, "3D")) m_texturemode->SetSelection(1); + else m_texturemode->SetSelection(0); + + /*downloader*/ + sOpt = gf_cfg_get_key(cfg, "General", "CacheDirectory"); + if (sOpt) m_cachedir->SetLabel(wxString(sOpt, wxConvUTF8) ); + sOpt = gf_cfg_get_key(cfg, "Downloader", "CleanCache"); + m_cleancache->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Downloader", "RestartFiles"); + m_restartcache->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "SAXLoader", "Progressive"); + m_progressive->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "SAXLoader", "MaxDuration"); + m_sax_duration->SetValue(sOpt ? wxString(sOpt, wxConvUTF8) : wxT("30")); + if (! m_progressive->GetValue()) m_sax_duration->Enable(0); + + sOpt = gf_cfg_get_key(cfg, "HTTPProxy", "Enabled"); + m_use_proxy->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + char szProxy[GF_MAX_PATH]; + strcpy(szProxy, ""); + sOpt = gf_cfg_get_key(cfg, "HTTPProxy", "Name"); + if (sOpt) { + strcat(szProxy, sOpt); + sOpt = gf_cfg_get_key(cfg, "HTTPProxy", "Port"); + if (sOpt) { + strcat(szProxy, ":"); + strcat(szProxy, sOpt); + } + } else { + m_use_proxy->SetValue(0); + } + m_proxy_name->SetValue( wxString((char *)szProxy, wxConvUTF8) ); + if (! m_use_proxy->GetValue()) m_proxy_name->Enable(0); + + /*streaming*/ + m_port->Append(wxT("554 (RTSP standard)")); + m_port->Append(wxT("7070 (RTSP ext)")); + m_port->Append(wxT("80 (RTSP / HTTP tunnel)")); + m_port->Append(wxT("8080 (RTSP / HTTP tunnel)")); + sOpt = gf_cfg_get_key(cfg, "Streaming", "DefaultPort"); + u32 port = 554; + Bool force_rtsp = 0; + if (sOpt) port = atoi(sOpt); + switch (port) { + case 8080: + m_port->SetSelection(3); + force_rtsp = 1; + break; + case 80: + m_port->SetSelection(2); + force_rtsp = 1; + break; + case 7070: + m_port->SetSelection(1); + break; + default: + m_port->SetSelection(0); + break; + } + + Bool use_rtsp = 0; + sOpt = gf_cfg_get_key(cfg, "Streaming", "RTPoverRTSP"); + if (sOpt && !stricmp(sOpt, "yes")) use_rtsp = 1; + + if (force_rtsp) { + m_rtsp->SetValue(1); + m_rtsp->Enable(0); + m_reorder->SetValue(0); + m_reorder->Enable(0); + } else { + m_rtsp->SetValue(use_rtsp ? 1 : 0); + m_rtsp->Enable(1); + m_reorder->Enable(1); + sOpt = gf_cfg_get_key(cfg, "Streaming", "ReorderSize"); + m_reorder->SetValue( (sOpt && !stricmp(sOpt, "0")) ? 1 : 0); + } + sOpt = gf_cfg_get_key(cfg, "Streaming", "RTSPTimeout"); + m_timeout->SetValue(sOpt ? wxString(sOpt, wxConvUTF8) : wxT("30000")); + sOpt = gf_cfg_get_key(cfg, "Network", "BufferLength"); + m_buffer->SetValue(sOpt ? wxString(sOpt, wxConvUTF8) : wxT("3000")); + sOpt = gf_cfg_get_key(cfg, "Network", "RebufferLength"); + u32 buf_len = 0; + if (sOpt) buf_len = atoi(sOpt); + if (buf_len) { + m_dorebuffer->SetValue(1); + m_rebuffer->SetValue(wxString(sOpt, wxConvUTF8)); + m_rebuffer->Enable(1); + } else { + m_dorebuffer->SetValue(0); + m_rebuffer->SetValue(wxT("0")); + m_rebuffer->Enable(0); + } + + RTPoverRTSP(event); + + sOpt = gf_cfg_get_key(cfg, "StreamingCache", "RecordDirectory"); + if (!sOpt) sOpt = gf_cfg_get_key(cfg, "General", "CacheDirectory"); + if (sOpt) m_recdir->SetLabel(wxString(sOpt, wxConvUTF8)); + sOpt = gf_cfg_get_key(cfg, "StreamingCache", "KeepExistingFiles"); + m_overwrite->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 0 : 1); + + sOpt = gf_cfg_get_key(cfg, "StreamingCache", "BaseFileName"); + if (sOpt) { + m_usename->SetValue(1); + m_recfile->Enable(1); + m_recfile->SetValue(wxString(sOpt, wxConvUTF8)); + } else { + m_usename->SetValue(0); + m_recfile->Enable(0); + m_recfile->SetValue(wxT("uses service URL")); + } + + m_select->Append(wxT("General")); + m_select->Append(wxT("MPEG-4 Systems")); + m_select->Append(wxT("Media Decoders")); + m_select->Append(wxT("Compositor")); + m_select->Append(wxT("Renderer 2D")); + m_select->Append(wxT("Renderer 3D")); + m_select->Append(wxT("Video Output")); + m_select->Append(wxT("Audio Output")); + m_select->Append(wxT("Text Engine")); + m_select->Append(wxT("File Download")); + m_select->Append(wxT("Real-Time Streaming")); + m_select->Append(wxT("Streaming Cache")); + + sOpt = gf_cfg_get_key(cfg, "General", "ConfigPanel"); + m_sel = sOpt ? atoi(sOpt) : 0; + if (m_sel>11) m_sel=11; + m_select->SetSelection(m_sel); + + DoSelect(); +} + +BEGIN_EVENT_TABLE(wxGPACControl, wxDialog) + EVT_BUTTON(ID_APPLY, wxGPACControl::Apply) + EVT_COMBOBOX(ID_SELECT, wxGPACControl::OnSetSelection) + EVT_CHECKBOX(ID_FORCE_AUDIO, wxGPACControl::ForceAudio) + EVT_COMBOBOX(ID_AUDIO_DRIVER, wxGPACControl::OnSetAudioDriver) + EVT_BUTTON(ID_FONT_DIR, wxGPACControl::FontDir) + EVT_BUTTON(ID_CACHE_DIR, wxGPACControl::CacheDir) + EVT_CHECKBOX(ID_PROGRESSIVE, wxGPACControl::OnProgressive) + EVT_CHECKBOX(ID_USE_PROXY, wxGPACControl::OnUseProxy) + EVT_CHECKBOX(ID_RTP_OVER_RTSP, wxGPACControl::RTPoverRTSP) + EVT_CHECKBOX(ID_RTSP_REBUFFER, wxGPACControl::Rebuffer) + EVT_COMBOBOX(ID_RTSP_PORT, wxGPACControl::OnSetRTSPPort) + EVT_CHECKBOX(ID_USE_FILENAME, wxGPACControl::OnUseFileName) + EVT_BUTTON(ID_RECORD_DIR, wxGPACControl::OnRecDir) +END_EVENT_TABLE() + + +wxGPACControl::~wxGPACControl() +{ + char str[20]; + sprintf(str, "%d", m_sel); + gf_cfg_set_key(m_pApp->m_user.config, "General", "ConfigPanel", str); +} + + +void wxGPACControl::DoSelect() +{ + + /*hide everything*/ + s_main->Show(s_general, false); + s_main->Show(s_mpeg4, false); + s_main->Show(s_mdec, false); + s_main->Show(s_rend, false); + s_main->Show(s_rend2d, false); + s_main->Show(s_rend3d, false); + s_main->Show(s_video, false); + s_main->Show(s_audio, false); + s_main->Show(s_font, false); + s_main->Show(s_dnld, false); + s_main->Show(s_stream, false); + s_main->Show(s_rec, false); + switch (m_sel) { + case 0: s_main->Show(s_general, true); break; + case 1: s_main->Show(s_mpeg4, true); break; + case 2: s_main->Show(s_mdec, true); break; + case 3: s_main->Show(s_rend, true); break; + case 4: s_main->Show(s_rend2d, true); break; + case 5: s_main->Show(s_rend3d, true); break; + case 6: s_main->Show(s_video, true); break; + case 7: s_main->Show(s_audio, true); break; + case 8: s_main->Show(s_font, true); break; + case 9: s_main->Show(s_dnld, true); break; + case 10: s_main->Show(s_stream, true); break; + case 11: s_main->Show(s_rec, true); break; + } + SetSizer(s_main); + s_main->Fit(this); + //s_main->Layout(); + return; + +} + +void wxGPACControl::OnSetSelection(wxCommandEvent &WXUNUSED(event)) +{ + m_sel = m_select->GetSelection(); + DoSelect(); +} + +void wxGPACControl::FontDir(wxCommandEvent &WXUNUSED(event)) +{ + wxDirDialog dlg(this); + dlg.SetPath(m_fontdir->GetLabel()); + if (dlg.ShowModal() == wxID_OK) { + m_fontdir->SetLabel(dlg.GetPath()); + } +} +void wxGPACControl::CacheDir(wxCommandEvent &WXUNUSED(event)) +{ + wxDirDialog dlg(this); + dlg.SetPath(m_cachedir->GetLabel()); + if (dlg.ShowModal() == wxID_OK) { + m_cachedir->SetLabel(dlg.GetPath()); + } +} + +void wxGPACControl::OnProgressive(wxCommandEvent &WXUNUSED(event)) +{ + m_sax_duration->Enable(m_progressive->GetValue() ? 1 : 0); +} + +void wxGPACControl::OnUseProxy(wxCommandEvent &WXUNUSED(event)) +{ + m_proxy_name->Enable(m_use_proxy->GetValue() ? 1 : 0); +} + +void wxGPACControl::RTPoverRTSP(wxCommandEvent &WXUNUSED(event)) +{ + m_reorder->Enable(m_rtsp->GetValue() ? 0 : 1); +} + +void wxGPACControl::Rebuffer(wxCommandEvent &WXUNUSED(event)) +{ + if (m_dorebuffer->GetValue()) { + m_rebuffer->Enable(); + } else { + m_rebuffer->Disable(); + } +} + +void wxGPACControl::OnSetRTSPPort(wxCommandEvent &WXUNUSED(event)) +{ + if (m_port->GetSelection() > 1) { + m_rtsp->Enable(0); + m_reorder->Enable(0); + } else { + m_rtsp->Enable(1); + m_reorder->Enable(1); + } +} + +void wxGPACControl::OnRecDir(wxCommandEvent &WXUNUSED(event)) +{ + wxDirDialog dlg(this); + dlg.SetPath(m_recdir->GetLabel()); + if (dlg.ShowModal() == wxID_OK) { + m_recdir->SetLabel(dlg.GetPath()); + } +} + +void wxGPACControl::OnUseFileName(wxCommandEvent &WXUNUSED(event)) +{ + if (m_usename->GetValue()) { + m_recfile->Enable(); + m_recfile->SetValue(wxT("record")); + } else { + m_recfile->Disable(); + m_recfile->SetValue(wxT("uses service URL")); + } +} + +void wxGPACControl::ForceAudio(wxCommandEvent &WXUNUSED(event)) +{ + if (m_forcecfg->GetValue()) { + m_nbbuf->Enable(); + m_buflen->Enable(); + } else { + m_nbbuf->Disable(); + m_buflen->Disable(); + } +} + +void wxGPACControl::OnSetAudioDriver(wxCommandEvent &WXUNUSED(event)) +{ +#ifdef WIN32 + if (strstr(m_audio->GetStringSelection().mb_str(wxConvUTF8), "DirectSound")) { + m_notifs->Enable(1); + } else { + m_notifs->Enable(0); + } +#endif +} + + + +void wxGPACControl::Apply(wxCommandEvent &WXUNUSED(event)) +{ + /*save options*/ + GF_Config *cfg = m_pApp->m_user.config; + + m_pApp->m_loop = m_loop->GetValue() ? 1 : 0; + gf_cfg_set_key(cfg, "General", "Loop", m_loop->GetValue() ? "yes" : "no"); + m_pApp->m_lookforsubs = m_lookforsubs->GetValue() ? 1 : 0; + gf_cfg_set_key(cfg, "General", "LookForSubtitles", m_lookforsubs->GetValue() ? "yes" : "no"); + m_pApp->m_console_off = m_noconsole->GetValue() ? 1 : 0; + gf_cfg_set_key(cfg, "General", "ConsoleOff", m_noconsole->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "General", "ViewXMT", m_viewxmt->GetValue() ? "yes" : "no"); + + s32 sel = m_lang->GetSelection(); + u32 i=0; + while (GF_ISO639_Lang[i]) { + /*only use common languages (having both 2- and 3-char code names)*/ + if (GF_ISO639_Lang[i+2][0]) { + if (!sel) break; + sel--; + } + i+=3; + } + gf_cfg_set_key(cfg, "Systems", "LanguageName", GF_ISO639_Lang[i]); + gf_cfg_set_key(cfg, "Systems", "Language3CC", GF_ISO639_Lang[i+1]); + gf_cfg_set_key(cfg, "Systems", "Language2CC", GF_ISO639_Lang[i+2]); + + + sel = m_thread->GetSelection(); + gf_cfg_set_key(cfg, "Systems", "ThreadingPolicy", (sel==0) ? "Single" : ( (sel==1) ? "Multi" : "Free")); + gf_cfg_set_key(cfg, "Systems", "ForceSingleClock", m_singletime->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Systems", "AlwaysDrawBIFS", m_bifsalwaysdrawn->GetValue() ? "yes" : "no"); + + gf_cfg_set_key(cfg, "Systems", "DefAudioDec", m_decaudio->GetStringSelection().mb_str(wxConvUTF8)); + gf_cfg_set_key(cfg, "Systems", "DefVideoDec", m_decvideo->GetStringSelection().mb_str(wxConvUTF8)); + + + gf_cfg_set_key(cfg, "Compositor", "HighSpeed", m_fast->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "ForceSceneSize", m_force_size->GetValue() ? "yes" : "no"); + + gf_cfg_set_key(cfg, "Compositor", "FrameRate", BIFSRates[m_fps->GetSelection()]); + sel = m_aa->GetSelection(); + gf_cfg_set_key(cfg, "Compositor", "AntiAlias", (sel==0) ? "None" : ( (sel==1) ? "Text" : "All")); + sel = m_draw_bounds->GetSelection(); + gf_cfg_set_key(cfg, "Compositor", "BoundingVolume", (sel==2) ? "AABB" : (sel==1) ? "Box" : "None"); + + Bool is_3D = m_use3D->GetValue() ? 1 : 0; + if (m_bWas3D != is_3D) { + /*FIXME*/ + } + gf_cfg_set_key(cfg, "Compositor", "Raster2D", m_graph->GetStringSelection().mb_str(wxConvUTF8)); + + gf_cfg_set_key(cfg, "Compositor", "DirectDraw", m_direct->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "ScalableZoom", m_scalable->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "DisableYUV", m_noyuv->GetValue() ? "yes" : "no"); + + gf_cfg_set_key(cfg, "Compositor", "RasterOutlines", m_raster_outlines->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "EmulatePOW2", m_emulpow2->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "PolygonAA", m_polyaa->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "DisableRectExt", m_norectext->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "BitmapCopyPixels", m_copypixels->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "BackFaceCulling", m_nobackcull->GetValue() ? "Off" : "On"); + + sel = m_wire->GetSelection(); + gf_cfg_set_key(cfg, "Compositor", "Wireframe", (sel==2) ? "WireOnSolid" : ( (sel==1) ? "WireOnly" : "WireNone" ) ); + sel = m_normals->GetSelection(); + gf_cfg_set_key(cfg, "Compositor", "DrawNormals", (sel==2) ? "PerVertex" : ( (sel==1) ? "PerFace" : "Never" ) ); + + gf_cfg_set_key(cfg, "Video", "SwitchResolution", m_switchres->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Video", "UseHardwareMemory", m_usehwmem->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Video", "DriverName", m_video->GetStringSelection().mb_str(wxConvUTF8)); + + + gf_cfg_set_key(cfg, "Audio", "ForceConfig", m_forcecfg->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Audio", "NoResync", m_noresync->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Audio", "DisableMultiChannel", m_nomulitch->GetValue() ? "yes" : "no"); + + gf_cfg_set_key(cfg, "Audio", "NumBuffers", wxString::Format(wxT("%d"), m_nbbuf->GetValue()).mb_str(wxConvUTF8) ); + gf_cfg_set_key(cfg, "Audio", "TotalDuration", wxString::Format(wxT("%d"), m_buflen->GetValue()).mb_str(wxConvUTF8) ); + gf_cfg_set_key(cfg, "Audio", "DriverName", m_audio->GetStringSelection().mb_str(wxConvUTF8)); +#ifdef WIN32 + if (m_notifs->IsEnabled()) + gf_cfg_set_key(cfg, "Audio", "DisableNotification", m_notifs->GetValue() ? "yes" : "no"); +#endif + + gf_cfg_set_key(cfg, "FontEngine", "FontReader", m_font->GetStringSelection().mb_str(wxConvUTF8)); + gf_cfg_set_key(cfg, "FontEngine", "FontDirectory", m_fontdir->GetLabel().mb_str(wxConvUTF8)); + switch (m_texturemode->GetSelection()) { + case 2: gf_cfg_set_key(cfg, "Compositor", "TextureTextMode", "Always"); break; + case 1: gf_cfg_set_key(cfg, "Compositor", "TextureTextMode", "Never"); break; + default: gf_cfg_set_key(cfg, "Compositor", "TextureTextMode", "Default"); break; + } + + gf_cfg_set_key(cfg, "Downloader", "CleanCache", m_cleancache->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Downloader", "RestartFiles", m_restartcache->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "SAXLoader", "Progressive", m_progressive->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "SAXLoader", "MaxDuration", m_sax_duration->GetLabel().mb_str(wxConvUTF8)); + gf_cfg_set_key(cfg, "General", "CacheDirectory", m_cachedir->GetLabel().mb_str(wxConvUTF8)); + + + Bool force_rtsp = 0; + switch (m_port->GetSelection()) { + case 3: + gf_cfg_set_key(cfg, "Streaming", "DefaultPort", "8080"); + force_rtsp = 1; + break; + case 2: + gf_cfg_set_key(cfg, "Streaming", "DefaultPort", "80"); + force_rtsp = 1; + break; + case 1: + gf_cfg_set_key(cfg, "Streaming", "DefaultPort", "7070"); + break; + default: + gf_cfg_set_key(cfg, "Streaming", "DefaultPort", "554"); + break; + } + + if (force_rtsp) { + gf_cfg_set_key(cfg, "Streaming", "RTPoverRTSP", "yes"); + } else { + gf_cfg_set_key(cfg, "Streaming", "RTPoverRTSP", m_rtsp->GetValue() ? "yes" : "no"); + if (!m_rtsp->GetValue()) gf_cfg_set_key(cfg, "Streaming", "ReorderSize", m_dorebuffer->GetValue() ? "30" : "0"); + } + + gf_cfg_set_key(cfg, "Streaming", "RTSPTimeout", m_timeout->GetValue().mb_str(wxConvUTF8)); + gf_cfg_set_key(cfg, "Network", "BufferLength", m_buffer->GetValue().mb_str(wxConvUTF8)); + if (m_dorebuffer->GetValue()) { + gf_cfg_set_key(cfg, "Network", "RebufferLength", m_rebuffer->GetValue().mb_str(wxConvUTF8)); + } else { + gf_cfg_set_key(cfg, "Network", "RebufferLength", "0"); + } + + gf_cfg_set_key(cfg, "StreamingCache", "KeepExistingFiles", m_overwrite->GetValue() ? "no" : "yes"); + if (m_usename->GetValue()) { + gf_cfg_set_key(cfg, "StreamingCache", "BaseFileName", m_recfile->GetValue().mb_str(wxConvUTF8)); + } else { + gf_cfg_set_key(cfg, "StreamingCache", "BaseFileName", NULL); + } + gf_cfg_set_key(cfg, "StreamingCache", "RecordDirectory", m_recdir->GetLabel().mb_str(wxConvUTF8)); + + + gf_term_set_option(m_pApp->m_term, GF_OPT_RELOAD_CONFIG, 1); +} + diff --git a/applications/osmo4_wx/wxGPACControl.h b/applications/osmo4_wx/wxGPACControl.h new file mode 100644 index 0000000..043d902 --- /dev/null +++ b/applications/osmo4_wx/wxGPACControl.h @@ -0,0 +1,137 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#ifndef _OPTIONS_H +#define _OPTIONS_H + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include +#include + +enum +{ + ID_SELECT = 1000, + ID_APPLY, + + ID_MAKE_DEF, + ID_FORCE_AUDIO, + ID_AUDIO_DRIVER, + ID_FONT_DIR, + ID_CACHE_DIR, + ID_PROGRESSIVE, + ID_RTSP_PORT, + ID_RTP_OVER_RTSP, + ID_RTSP_REBUFFER, + ID_RECORD_DIR, + ID_USE_FILENAME, + ID_USE_PROXY, +}; + +class wxOsmo4Frame; +class wxGPACControl : public wxDialog +{ +public: + wxGPACControl(wxWindow *parent); + virtual ~wxGPACControl(); + +private: + DECLARE_EVENT_TABLE() + + wxOsmo4Frame *m_pApp; + + wxComboBox *m_select; + Bool m_bWas3D; + + void Apply(wxCommandEvent &event); + void OnSetSelection(wxCommandEvent &event); + void ForceAudio(wxCommandEvent &event); + void OnSetAudioDriver(wxCommandEvent &event); + void FontDir(wxCommandEvent &event); + void CacheDir(wxCommandEvent &event); + void OnProgressive(wxCommandEvent &event); + void OnUseProxy(wxCommandEvent &event); + void RTPoverRTSP(wxCommandEvent &event); + void Rebuffer(wxCommandEvent &event); + void OnSetRTSPPort(wxCommandEvent &event); + void OnUseFileName(wxCommandEvent &event); + void OnRecDir(wxCommandEvent &event); + void DoSelect(); + s32 m_sel; + void SetYUVLabel(); + + wxBoxSizer *s_header, *s_main, *s_general, *s_mpeg4, *s_mdec, *s_rend, *s_rend2d, *s_rend3d, *s_audio, *s_video, *s_font, *s_dnld, *s_stream, *s_rec; + + /*general section*/ + wxCheckBox *m_loop, *m_lookforsubs, *m_noconsole, *m_viewxmt; + /*MPEG-4 systems*/ + wxCheckBox *m_bifsalwaysdrawn, *m_singletime; + wxComboBox *m_lang, *m_thread; + /*media decoders*/ + wxComboBox *m_decaudio, *m_decvideo; + /*Rendering*/ + wxComboBox *m_fps, *m_aa, *m_draw_bounds; + wxCheckBox *m_use3D, *m_fast, *m_force_size; + /*Renderer 2D*/ + wxComboBox *m_graph; + wxCheckBox *m_noyuv, *m_direct, *m_scalable; + wxStaticText *m_yuvtxt; + /*Renderer 3D*/ + wxCheckBox *m_raster_outlines, *m_polyaa, *m_nobackcull, *m_emulpow2, *m_norectext, *m_copypixels; + wxComboBox *m_wire, *m_normals; + /*video*/ + wxComboBox *m_video; + wxCheckBox *m_switchres, *m_usehwmem; + /*audio*/ + wxSpinCtrl *m_nbbuf, *m_buflen; + wxComboBox *m_audio; + wxCheckBox *m_forcecfg, *m_noresync, *m_nomulitch; +#ifdef WIN32 + wxCheckBox *m_notifs; +#endif + /*font*/ + wxComboBox *m_font; + wxButton *m_fontdir; + wxComboBox *m_texturemode; + /*file download*/ + wxButton *m_cachedir; + wxCheckBox *m_cleancache, *m_restartcache, *m_progressive, *m_use_proxy; + wxTextCtrl *m_sax_duration, *m_proxy_name; + /*streaming*/ + wxComboBox *m_port; + wxCheckBox *m_rtsp, *m_reorder, *m_dorebuffer; + wxTextCtrl *m_timeout, *m_buffer, *m_rebuffer; + /*file recorder*/ + wxButton *m_recdir; + wxCheckBox *m_overwrite, *m_usename; + wxTextCtrl *m_recfile; +}; + +#endif + diff --git a/applications/osmo4_wx/wxOsmo4.cpp b/applications/osmo4_wx/wxOsmo4.cpp new file mode 100644 index 0000000..888f7d1 --- /dev/null +++ b/applications/osmo4_wx/wxOsmo4.cpp @@ -0,0 +1,2518 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + + +#include "wxOsmo4.h" +#include "wxGPACControl.h" +#include "fileprops.h" +#include "wx/image.h" +#include +#include +#include +#include + + +IMPLEMENT_APP(wxOsmo4App) + +#include "osmo4.xpm" + +#include +#include +#include + +#include "toolbar.xpm" + +#include "Playlist.h" + +#ifdef WIN32 +#define FRAME_H 140 +#else +#define FRAME_H 110 +#endif + + +wxString get_pref_browser(GF_Config *cfg) +{ + const char *sOpt = gf_cfg_get_key(cfg, "General", "Browser"); + if (sOpt) return wxString(sOpt, wxConvUTF8); +#ifdef __WXMAC__ + return wxT("safari"); +#else +#ifdef WIN32 + return wxT("explorer.exe"); +#else + return wxT("mozilla"); +#endif +#endif +} + + +IMPLEMENT_DYNAMIC_CLASS(wxGPACEvent, wxEvent ) + +wxGPACEvent::wxGPACEvent(wxWindow* win) +{ + SetEventType(GPAC_EVENT); + SetEventObject(win); + gpac_evt.type = 0; + to_url = wxT(""); +} +wxEvent *wxGPACEvent::Clone() const +{ + wxGPACEvent *evt = new wxGPACEvent((wxWindow *) m_eventObject); + evt->to_url = to_url; + evt->gpac_evt = gpac_evt; + return evt; +} + + + +/*open file dlg*/ +BEGIN_EVENT_TABLE(OpenURLDlg, wxDialog) + EVT_BUTTON(ID_URL_GO, OpenURLDlg::OnGo) +END_EVENT_TABLE() + +OpenURLDlg::OpenURLDlg(wxWindow *parent, GF_Config *cfg) + : wxDialog(parent, -1, wxString(wxT("Enter remote presentation location"))) +{ +#ifndef WIN32 + SetSize(430, 35); +#else + SetSize(430, 55); +#endif + Centre(); + m_url = new wxComboBox(this, -1, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_DROPDOWN); + m_url->SetSize(0, 2, 340, 18, wxSIZE_AUTO); + m_go = new wxButton(this, ID_URL_GO, wxT("Go !")); +#ifndef WIN32 + m_go->SetSize(344, 2, 20, 18, wxSIZE_AUTO); +#else + m_go->SetSize(364, 2, 30, 18, wxSIZE_AUTO); +#endif + m_urlVal = wxT(""); + + m_cfg = cfg; + + const char *sOpt; + u32 i=0; + + while (1) { + sOpt = gf_cfg_get_key_name(m_cfg, "RecentFiles", i); + if (!sOpt) break; + m_url->Append(wxString(sOpt, wxConvUTF8) ); + i++; + } +} + +#define MAX_LAST_FILES 20 +void UpdateLastFiles(GF_Config *cfg, const char *URL) +{ + u32 nb_entries; + gf_cfg_set_key(cfg, "RecentFiles", URL, NULL); + gf_cfg_insert_key(cfg, "RecentFiles", URL, "", 0); + /*remove last entry if needed*/ + nb_entries = gf_cfg_get_key_count(cfg, "RecentFiles"); + if (nb_entries>MAX_LAST_FILES) { + gf_cfg_set_key(cfg, "RecentFiles", gf_cfg_get_key_name(cfg, "RecentFiles", nb_entries-1), NULL); + } +} + +void OpenURLDlg::OnGo(wxCommandEvent& event) +{ + m_urlVal = m_url->GetValue(); + UpdateLastFiles(m_cfg, m_urlVal.mb_str(wxConvUTF8)); + EndModal(wxID_OK); +} +/*end open file dlg*/ + +#ifdef WIN32 +u32 get_sys_col(int idx) +{ + u32 res; + DWORD val = GetSysColor(idx); + res = (val)&0xFF; res<<=8; + res |= (val>>8)&0xFF; res<<=8; + res |= (val>>16)&0xFF; + return res; +} +#endif + +static void wxOsmo4_progress_cbk(void *usr, char *title, u32 done, u32 total) +{ + if (!total) return; + wxOsmo4Frame *app = (wxOsmo4Frame *)usr; + s32 prog = (s32) ( (100 * (u64)done) / total); + if (app->m_last_prog < prog) { + app->m_last_prog = prog; + + if (prog<100) { + /*appears to crash wxWidgets / X11 when refreshing the text too often*/ + if (app->m_LastStatusTime + 200 > gf_sys_clock()) return; + char msg[1024]; + sprintf(msg, "%s %02d %%)", title, prog); + //app->SetStatus(wxString(msg, wxConvUTF8)); + } else { + app->SetStatus(wxT("Ready")); + app->m_last_prog = -1; + } + } +} + +Bool GPAC_EventProc(void *ptr, GF_Event *evt) +{ + wxCommandEvent event; + wxOsmo4Frame *app = (wxOsmo4Frame *)ptr; + + switch (evt->type) { + case GF_EVENT_DURATION: + app->m_duration = (u32) (evt->duration.duration*1000); + app->m_can_seek = evt->duration.can_seek; + if (app->m_duration<1100) app->m_can_seek = 0; + app->m_pProg->Enable(app->m_can_seek ? 1 : 0); + app->m_pPlayList->SetDuration((u32) evt->duration.duration); + break; + case GF_EVENT_MESSAGE: + { + const char *servName; + if (!evt->message.service || !strcmp(evt->message.service, app->m_pPlayList->GetURL().mb_str(wxConvUTF8))) { + servName = "main service"; + } else { + servName = evt->message.service; + } + if (!evt->message.message) return 0; + + if (evt->message.error) { + app->SetStatus(wxString(evt->message.message, wxConvUTF8) + wxT(" (") + wxString(servName, wxConvUTF8) + wxT(")") ); + if (!app->m_connected) app->m_pPlayList->SetDead(); + } + else if (!app->m_console_off) { + if (strstr(evt->message.message, "100 %")) { + app->SetStatus(wxT("")); + } else { + app->SetStatus(wxString(evt->message.message, wxConvUTF8) ); + } + } + + /*log*/ + if (evt->message.error) + ::wxLogMessage(wxString(evt->message.message, wxConvUTF8) + wxT(" (") + wxString(servName, wxConvUTF8) + wxT(") ") + wxString(gf_error_to_string(evt->message.error), wxConvUTF8) ); + else + ::wxLogMessage(wxString(evt->message.message, wxConvUTF8) + wxT(" (") + wxString(servName, wxConvUTF8) + wxT(")")); + } + break; + case GF_EVENT_PROGRESS: + { + char *sTitle; + if (evt->progress.progress_type==0) sTitle = "Buffer"; + else if (evt->progress.progress_type==1) sTitle = "Download"; + else if (evt->progress.progress_type==2) sTitle = "Import"; + gf_set_progress(sTitle, evt->progress.done, evt->progress.total); + } + break; + case GF_EVENT_KEYDOWN: + if (app->m_can_seek && (evt->key.flags & GF_KEY_MOD_ALT)) { + s32 res; + switch (evt->key.key_code) { + case GF_KEY_LEFT: + res = gf_term_get_time_in_ms(app->m_term) - 5*app->m_duration/100; + if (res<0) res=0; + gf_term_play_from_time(app->m_term, res, 0); + break; + case GF_KEY_RIGHT: + res = gf_term_get_time_in_ms(app->m_term) + 5*app->m_duration/100; + if ((u32) res>=app->m_duration) res = 0; + gf_term_play_from_time(app->m_term, res, 0); + break; + case GF_KEY_DOWN: + res = gf_term_get_time_in_ms(app->m_term) - 60000; + if (res<0) res=0; + gf_term_play_from_time(app->m_term, res, 0); + break; + case GF_KEY_UP: + res = gf_term_get_time_in_ms(app->m_term) + 60000; + if ((u32) res>=app->m_duration) res = 0; + gf_term_play_from_time(app->m_term, res, 0); + break; + } + } else if (evt->key.flags & GF_KEY_MOD_CTRL) { + switch (evt->key.key_code) { + case GF_KEY_LEFT: + app->m_pPlayList->PlayPrev(); + break; + case GF_KEY_RIGHT: + app->m_pPlayList->PlayNext(); + break; + } + } else { + switch (evt->key.key_code) { + case GF_KEY_HOME: + gf_term_set_option(app->m_term, GF_OPT_NAVIGATION_TYPE, 1); + break; + case GF_KEY_ESCAPE: + if (gf_term_get_option(app->m_term, GF_OPT_FULLSCREEN)) + gf_term_set_option(app->m_term, GF_OPT_FULLSCREEN, 0); + break; + default: + { + wxGPACEvent wxevt(app); + wxevt.gpac_evt = *evt; + app->AddPendingEvent(wxevt); + } + break; + } + } + break; + + case GF_EVENT_CONNECT: + { + wxGPACEvent wxevt(app); + wxevt.gpac_evt.type = GF_EVENT_CONNECT; + wxevt.gpac_evt.connect.is_connected = evt->connect.is_connected; + if (!evt->connect.is_connected) app->m_duration = 0; + app->AddPendingEvent(wxevt); + } + break; + case GF_EVENT_NAVIGATE: + { + wxGPACEvent wxevt(app); + wxevt.to_url = wxString(evt->navigate.to_url, wxConvUTF8); + wxevt.gpac_evt.type = evt->type; + app->AddPendingEvent(wxevt); + } + return 1; + case GF_EVENT_SET_CAPTION: + { + wxGPACEvent wxevt(app); + wxevt.to_url = wxString(evt->caption.caption, wxConvUTF8); + wxevt.gpac_evt.type = evt->type; + app->AddPendingEvent(wxevt); + } + return 1; + + case GF_EVENT_QUIT: + case GF_EVENT_VIEWPOINTS: + case GF_EVENT_STREAMLIST: + case GF_EVENT_SCENE_SIZE: +// case GF_EVENT_SIZE: + { + wxGPACEvent wxevt(app); + wxevt.gpac_evt = *evt; + app->AddPendingEvent(wxevt); + } + break; + case GF_EVENT_DBLCLICK: + gf_term_set_option(app->m_term, GF_OPT_FULLSCREEN, !gf_term_get_option(app->m_term, GF_OPT_FULLSCREEN)); + return 0; + case GF_EVENT_MOUSEDOWN: + if (!gf_term_get_option(app->m_term, GF_OPT_FULLSCREEN)) { +#ifdef __WXGTK__ + app->m_pVisual->SetFocus(); +#else + app->m_pView->SetFocus(); +#endif + } + break; + case GF_EVENT_SYS_COLORS: +#ifdef WIN32 + evt->sys_cols.sys_colors[0] = get_sys_col(COLOR_ACTIVEBORDER); + evt->sys_cols.sys_colors[1] = get_sys_col(COLOR_ACTIVECAPTION); + evt->sys_cols.sys_colors[2] = get_sys_col(COLOR_APPWORKSPACE); + evt->sys_cols.sys_colors[3] = get_sys_col(COLOR_BACKGROUND); + evt->sys_cols.sys_colors[4] = get_sys_col(COLOR_BTNFACE); + evt->sys_cols.sys_colors[5] = get_sys_col(COLOR_BTNHIGHLIGHT); + evt->sys_cols.sys_colors[6] = get_sys_col(COLOR_BTNSHADOW); + evt->sys_cols.sys_colors[7] = get_sys_col(COLOR_BTNTEXT); + evt->sys_cols.sys_colors[8] = get_sys_col(COLOR_CAPTIONTEXT); + evt->sys_cols.sys_colors[9] = get_sys_col(COLOR_GRAYTEXT); + evt->sys_cols.sys_colors[10] = get_sys_col(COLOR_HIGHLIGHT); + evt->sys_cols.sys_colors[11] = get_sys_col(COLOR_HIGHLIGHTTEXT); + evt->sys_cols.sys_colors[12] = get_sys_col(COLOR_INACTIVEBORDER); + evt->sys_cols.sys_colors[13] = get_sys_col(COLOR_INACTIVECAPTION); + evt->sys_cols.sys_colors[14] = get_sys_col(COLOR_INACTIVECAPTIONTEXT); + evt->sys_cols.sys_colors[15] = get_sys_col(COLOR_INFOBK); + evt->sys_cols.sys_colors[16] = get_sys_col(COLOR_INFOTEXT); + evt->sys_cols.sys_colors[17] = get_sys_col(COLOR_MENU); + evt->sys_cols.sys_colors[18] = get_sys_col(COLOR_MENUTEXT); + evt->sys_cols.sys_colors[19] = get_sys_col(COLOR_SCROLLBAR); + evt->sys_cols.sys_colors[20] = get_sys_col(COLOR_3DDKSHADOW); + evt->sys_cols.sys_colors[21] = get_sys_col(COLOR_3DFACE); + evt->sys_cols.sys_colors[22] = get_sys_col(COLOR_3DHIGHLIGHT); + evt->sys_cols.sys_colors[23] = get_sys_col(COLOR_3DLIGHT); + evt->sys_cols.sys_colors[24] = get_sys_col(COLOR_3DSHADOW); + evt->sys_cols.sys_colors[25] = get_sys_col(COLOR_WINDOW); + evt->sys_cols.sys_colors[26] = get_sys_col(COLOR_WINDOWFRAME); + evt->sys_cols.sys_colors[27] = get_sys_col(COLOR_WINDOWTEXT); + return 1; +#else + memset(evt->sys_cols.sys_colors, 0, sizeof(u32)*28); + return 1; +#endif + } + return 0; +} + + + +bool wxOsmo4App::OnInit() +{ +#ifdef __WXGTK__ + XSynchronize((Display *) wxGetDisplay(), 1); +#endif + wxFrame *frame = new wxOsmo4Frame(); + frame->Show(TRUE); + SetTopWindow(frame); + return true; +} + + +class myDropfiles : public wxFileDropTarget +{ +public: + myDropfiles() : wxFileDropTarget() {} + virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames); + wxOsmo4Frame *m_pMain; +}; + +bool myDropfiles::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) +{ + u32 count = filenames.GetCount(); + + if (count==1) { + const char *ext = strrchr(filenames.Item(0).mb_str(wxConvUTF8) , '.'); + /*if playing and sub d&d, open sub in current presentation*/ + if (m_pMain->m_connected && ext && ( !stricmp(ext, ".srt") || !stricmp(ext, ".sub") || !stricmp(ext, ".ttxt") || !stricmp(ext, ".xml") ) ) { + m_pMain->AddSubtitle(filenames.Item(0).mb_str(wxConvUTF8) , 1); + return TRUE; + } + } + + for (u32 i=0; im_pPlayList->QueueURL(filenames.Item(i)); + + m_pMain->m_pPlayList->RefreshList(); + m_pMain->m_pPlayList->PlayNext(); + return TRUE; +} + +bool GPACLogs::OnFrameClose(wxFrame *frame) +{ + Show(FALSE); + return 0; +} + +void wxOsmo4Frame::ShowViewWindow(Bool do_show) +{ + m_pView->Show(do_show ? 1 : 0); +#ifdef __WXGTK__ + //m_pView->Show(0); +#endif +} + +#ifdef __WXGTK__ +extern "C" { +#ifdef __WXGTK20__ + int gdk_x11_drawable_get_xid( void * ); + void *gdk_x11_drawable_get_xdisplay( void * ); +#endif + void *gtk_widget_get_parent_window( void * ); +} +#endif + + +void wxOsmo4Frame::CheckVideoOut() +{ + const char *sOpt = gf_cfg_get_key(m_user.config, "Video", "DriverName"); + void *os_handle = NULL; + void *os_display = NULL; + /*build a child window for embed display*/ + if (sOpt && stricmp(sOpt, "SDL Video Output")) { + if (m_user.os_window_handler) return; + m_bExternalView = 0; + +#ifdef __WXGTK__ + GtkWidget* widget = m_pVisual->GetHandle(); + +#ifdef __WXGTK20__ + os_handle = (void *) gdk_x11_drawable_get_xid(gtk_widget_get_parent_window(widget)); +#else + os_handle = (void *)*(int *)( (char *)gtk_widget_get_parent_window(widget) + 2 * sizeof(void *) ); +#endif + +#elif defined (WIN32) + os_handle = m_pView->GetHandle(); +#endif + if (os_handle) { + m_user.os_window_handler = os_handle; + m_user.os_display = os_display; + ShowViewWindow(1); + m_pView->SetSize(320, 240); + SetSize(wxSize(320, 240+FRAME_H)); + SetWindowStyle(wxDEFAULT_FRAME_STYLE); + DoLayout(320, 240); + return; + } + } + /*we're using SDL, don't use SDL hack*/ + m_bExternalView = 1; + m_user.os_window_handler = 0; + m_user.os_display = NULL; + SetSize(wxSize(320,FRAME_H)); + m_pView->SetSize(0, 0); + ShowViewWindow(0); + DoLayout(); + SetWindowStyle(wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER)); +} + +static void wxOsmo4_do_log(void *cbk, u32 level, u32 tool, const char *fmt, va_list list) +{ + wxOsmo4Frame *osmo = (wxOsmo4Frame *)cbk; + + if (osmo->m_logs) { + vfprintf(osmo->m_logs, fmt, list); + fflush(osmo->m_logs); + } else { + ::wxVLogMessage(wxString(fmt, wxConvUTF8), list); + } +} + + +Bool wxOsmo4Frame::LoadTerminal() +{ + m_term = NULL; + memset(&m_user, 0, sizeof(GF_User)); + + /*locate exec dir for cfg file*/ + wxPathList pathList; + wxString currentDir(wxGetCwd()); + wxString abs_gpac_path = wxT(""); + const char *gpac_cfg; + + ::wxLogMessage(wxT("Looking for GPAC configuration file")); + +#if defined(__WXMAC__) && !defined(__DARWIN__) + // On Mac, the current directory is the relevant one when the application starts. + abs_gpac_path = wxGetCwd(); + gpac_cfg = "GPAC.cfg"; +#else + +#ifdef WIN32 + wxOsmo4App &app = wxGetApp(); + gpac_cfg = "GPAC.cfg"; + /*locate exe*/ + if (wxIsAbsolutePath(app.argv[0])) { + abs_gpac_path = wxPathOnly(app.argv[0]); + } else { + if (currentDir.Last() != wxFILE_SEP_PATH) currentDir += wxFILE_SEP_PATH; + abs_gpac_path = currentDir + app.argv[0]; + if (wxFileExists(abs_gpac_path)) { + abs_gpac_path = wxPathOnly(abs_gpac_path); + } else { + abs_gpac_path = wxT(""); + pathList.AddEnvList(wxT("PATH")); + abs_gpac_path = pathList.FindAbsoluteValidPath(app.argv[0]); + if (!abs_gpac_path.IsEmpty()) { + abs_gpac_path = wxPathOnly(abs_gpac_path); + } else { + /*ask user*/ + wxDirDialog dlg(NULL, wxT("Locate GPAC config file directory")); + if ( dlg.ShowModal() != wxID_OK ) return 0; + abs_gpac_path = dlg.GetPath(); + } + } + } +#else + gpac_cfg = ".gpacrc"; + char *cfg_dir = getenv("HOME"); + if (cfg_dir) { + abs_gpac_path = wxString(cfg_dir, wxConvUTF8); + } else { + /*ask user*/ + wxDirDialog dlg(NULL, wxT("Locate GPAC config file directory")); + if ( dlg.ShowModal() != wxID_OK ) return 0; + abs_gpac_path = dlg.GetPath(); + } +#endif + +#endif + + /*load config*/ + m_user.config = gf_cfg_new(abs_gpac_path.mb_str(wxConvUTF8), gpac_cfg); + + if (!m_user.config) { + unsigned char config_file[GF_MAX_PATH]; + strcpy((char *) config_file, (const char *) abs_gpac_path.mb_str(wxConvUTF8)); + if (config_file[strlen((char *) config_file)-1] != GF_PATH_SEPARATOR) { + char szSep[2]; + szSep[0] = GF_PATH_SEPARATOR; + szSep[1] = 0; + strcat((char *) config_file, (const char *)szSep); + } + strcat((char *) config_file, gpac_cfg); + FILE *ft = fopen((const char *) config_file, "wt"); + if (!ft) { + wxMessageDialog(NULL, wxT("Cannot create blank config file"), wxT("Init error"), wxOK).ShowModal(); + return 0; + } + fclose(ft); + m_user.config = gf_cfg_new(abs_gpac_path.mb_str(wxConvUTF8), gpac_cfg); + if (!m_user.config) { + wxMessageDialog(NULL, wxT("Cannot open GPAC configuration file"), wxT("Init error"), wxOK); + return 0; + } + } + strcpy(szAppPath, abs_gpac_path.mb_str(wxConvUTF8)); + if (szAppPath[strlen(szAppPath)] != GF_PATH_SEPARATOR) + sprintf(szAppPath, "%s%c", szAppPath, GF_PATH_SEPARATOR); + + + /*check log file*/ + const char *str = gf_cfg_get_key(m_user.config, "General", "LogFile"); + if (str) m_logs = fopen(str, "wt"); + gf_log_set_callback(this, wxOsmo4_do_log); + + /*set log level*/ + m_log_level = 0; + str = gf_cfg_get_key(m_user.config, "General", "LogLevel"); + if (str) { + if (!stricmp(str, "debug")) m_log_level = GF_LOG_DEBUG; + else if (!stricmp(str, "info")) m_log_level = GF_LOG_INFO; + else if (!stricmp(str, "warning")) m_log_level = GF_LOG_WARNING; + else if (!stricmp(str, "error")) m_log_level = GF_LOG_ERROR; + gf_log_set_level(m_log_level); + } + + /*set log tools*/ + m_log_tools = 0; + str = gf_cfg_get_key(m_user.config, "General", "LogTools"); + if (str) { + char *sep; + char *val = (char *) str; + while (val) { + sep = strchr(val, ':'); + if (sep) sep[0] = 0; + if (!stricmp(val, "core")) m_log_tools |= GF_LOG_CODING; + else if (!stricmp(val, "coding")) m_log_tools |= GF_LOG_CODING; + else if (!stricmp(val, "container")) m_log_tools |= GF_LOG_CONTAINER; + else if (!stricmp(val, "network")) m_log_tools |= GF_LOG_NETWORK; + else if (!stricmp(val, "rtp")) m_log_tools |= GF_LOG_RTP; + else if (!stricmp(val, "author")) m_log_tools |= GF_LOG_AUTHOR; + else if (!stricmp(val, "sync")) m_log_tools |= GF_LOG_SYNC; + else if (!stricmp(val, "codec")) m_log_tools |= GF_LOG_CODEC; + else if (!stricmp(val, "parser")) m_log_tools |= GF_LOG_PARSER; + else if (!stricmp(val, "media")) m_log_tools |= GF_LOG_MEDIA; + else if (!stricmp(val, "scene")) m_log_tools |= GF_LOG_SCENE; + else if (!stricmp(val, "script")) m_log_tools |= GF_LOG_SCRIPT; + else if (!stricmp(val, "interact")) m_log_tools |= GF_LOG_INTERACT; + else if (!stricmp(val, "compose")) m_log_tools |= GF_LOG_COMPOSE; + else if (!stricmp(val, "mmio")) m_log_tools |= GF_LOG_MMIO; + else if (!stricmp(val, "none")) m_log_tools = 0; + else if (!stricmp(val, "all")) m_log_tools = 0xFFFFFFFF; + if (!sep) break; + sep[0] = ':'; + val = sep+1; + } + gf_log_set_tools(m_log_tools); + } + + gf_sys_init(); + + ::wxLogMessage(wxT("GPAC configuration file opened - looking for modules")); + str = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + Bool first_launch = 0; + if (!str) { + first_launch = 1; +#ifdef GPAC_MODULES_PATH + str = GPAC_MODULES_PATH; +#else + str = abs_gpac_path.mb_str(wxConvUTF8); +#endif + } + + + m_user.modules = gf_modules_new(str, m_user.config); + /*initial launch*/ + if (first_launch || !gf_modules_get_count(m_user.modules)) { + const char *sOpt; + wxDirDialog dlg(NULL, wxT("Locate GPAC modules directory")); + if (!gf_modules_get_count(m_user.modules)) { + if (m_user.modules) gf_modules_del(m_user.modules); + m_user.modules = NULL; + if ( dlg.ShowModal() != wxID_OK ) return false; + str = dlg.GetPath().mb_str(wxConvUTF8); + + m_user.modules = gf_modules_new(str, m_user.config); + if (!m_user.modules || !gf_modules_get_count(m_user.modules) ) { + wxMessageDialog(NULL, wxT("Cannot find any modules for GPAC"), wxT("Init error"), wxOK); + gf_cfg_del(m_user.config); + m_user.config = NULL; + return 0; + } + } + + u32 i; + for (i=0; iCanHandleURL(ifce, "test.test"); + gf_modules_close_interface((GF_BaseInterface *) ifce); + } + } + + gf_cfg_set_key(m_user.config, "General", "ModulesDirectory", (const char *) str); + + /*setup UDP traffic autodetect*/ + gf_cfg_set_key(m_user.config, "Network", "AutoReconfigUDP", "yes"); + gf_cfg_set_key(m_user.config, "Network", "UDPTimeout", "10000"); + gf_cfg_set_key(m_user.config, "Network", "BufferLength", "3000"); + + /*check audio config on windows, force config*/ + sOpt = gf_cfg_get_key(m_user.config, "Audio", "ForceConfig"); + if (!sOpt) { + gf_cfg_set_key(m_user.config, "Audio", "ForceConfig", "yes"); + gf_cfg_set_key(m_user.config, "Audio", "NumBuffers", "2"); + gf_cfg_set_key(m_user.config, "Audio", "TotalDuration", "120"); + } + +#ifdef WIN32 + unsigned char str_path[MAX_PATH]; + sOpt = gf_cfg_get_key(m_user.config, "Compositor", "Raster2D"); + if (!sOpt) gf_cfg_set_key(m_user.config, "Compositor", "Raster2D", "gdip_rend"); + sOpt = gf_cfg_get_key(m_user.config, "General", "CacheDirectory"); + if (!sOpt) { + sprintf((char *) str_path, "%scache", abs_gpac_path.mb_str(wxConvUTF8)); + gf_cfg_set_key(m_user.config, "General", "CacheDirectory", (const char *) str_path); + } + /*by default use GDIplus, much faster than freetype on font loading*/ + gf_cfg_set_key(m_user.config, "FontEngine", "FontReader", "gdip_rend"); + gf_cfg_set_key(m_user.config, "Video", "DriverName", "DirectX Video Output"); + + sOpt = gf_cfg_get_key(m_user.config, "General", "StartupFile"); + if (!sOpt) { + sprintf((char *) str_path, "%sgpac.mp4", abs_gpac_path.mb_str(wxConvUTF8)); + gf_cfg_set_key(m_user.config, "General", "StartupFile", (const char *) str_path); + } +#else + +#if defined(__DARWIN__) || defined(__APPLE__) + wxDirDialog dlg3(NULL, wxT("Please specify a cache directory for GPAC")); + dlg3.SetPath(wxT("/tmp")); + if ( dlg3.ShowModal() == wxID_OK ) + gf_cfg_set_key(m_user.config, "General", "CacheDirectory", (const char *) dlg3.GetPath().mb_str(wxConvUTF8) ); + + wxDirDialog dlg2(NULL, wxT("Please locate a TrueType font repository on your system for text support")); + dlg2.SetPath(wxT("/usr/share/fonts/truetype")); + if ( dlg2.ShowModal() == wxID_OK ) + gf_cfg_set_key(m_user.config, "FontEngine", "FontDirectory", (const char *) dlg2.GetPath().mb_str(wxConvUTF8) ); + + gf_cfg_set_key(m_user.config, "Video", "DriverName", "SDL Video Output"); + gf_cfg_set_key(m_user.config, "Compositor", "ScalableZoom", "no"); +#else + gf_cfg_set_key(m_user.config, "FontEngine", "FontDirectory", "/usr/share/fonts/truetype/"); + /*these fonts seems installed by default on many systems...*/ + gf_cfg_set_key(m_user.config, "FontEngine", "FontSerif", "Bitstream Vera Serif"); + gf_cfg_set_key(m_user.config, "FontEngine", "FontSans", "Bitstream Vera Sans"); + gf_cfg_set_key(m_user.config, "FontEngine", "FontFixed", "Bitstream Vera Monospace"); + + + gf_cfg_set_key(m_user.config, "General", "CacheDirectory", "/tmp"); + gf_cfg_set_key(m_user.config, "Video", "DriverName", "X11 Video Output"); + gf_cfg_set_key(m_user.config, "Compositor", "ScalableZoom", "yes"); + gf_cfg_set_key(m_user.config, "Audio", "DriverName", "SDL Audio Output"); +#endif + + sOpt = gf_cfg_get_key(m_user.config, "General", "StartupFile"); + if (!sOpt) { + FILE *test; + test = fopen("/usr/local/share/gpac/gpac.mp4", "rb"); + if (test) { + gf_cfg_set_key(m_user.config, "General", "StartupFile", "/usr/local/share/gpac/gpac.mp4"); + fclose(test); + } else { + test = fopen("/usr/share/gpac/gpac.mp4", "rb"); + if (test) { + gf_cfg_set_key(m_user.config, "General", "StartupFile", "/usr/share/gpac/gpac.mp4"); + fclose(test); + } + } + } + +#endif + } + if (! gf_modules_get_count(m_user.modules) ) { + wxMessageDialog(NULL, wxT("No modules available - system cannot work"), wxT("Fatal Error"), wxOK).ShowModal(); + gf_modules_del(m_user.modules); + gf_cfg_del(m_user.config); + m_user.config = NULL; + return 0; + } + + ::wxLogMessage(wxT("%d modules found:"), gf_modules_get_count(m_user.modules)); + for (u32 i=0; im_pMain = this; + SetDropTarget(droptarget); + m_pLogs = new GPACLogs(this); + m_bGrabbed = 0; + + /*new menu bar*/ + wxMenuBar *b = new wxMenuBar(); + /*file*/ + wxMenu *menu = new wxMenu(); + menu->Append(FILE_OPEN, wxT("&Open File\tCtrl+O"), wxT("Open local presentation")); + menu->Append(FILE_OPEN_URL, wxT("&Open URL\tCtrl+U"), wxT("Open remote presentation")); + menu->AppendSeparator(); + menu->Append(FILE_PROPERTIES, wxT("&Properties\tCtrl+I"), wxT("Show presentation properties")); + menu->Enable(FILE_PROPERTIES, 0); + wxMenu *smenu = new wxMenu(); + smenu->Append(ID_MCACHE_ENABLE, wxT("&Enable"), wxT("Turns Recorder On/Off")); + smenu->Append(ID_MCACHE_STOP, wxT("&Stop"), wxT("Stops recording and saves")); + smenu->Append(ID_MCACHE_ABORT, wxT("&Abort"), wxT("Stops recording and discards")); + menu->Append(0, wxT("&Streaming Cache"), smenu); + menu->AppendSeparator(); + menu->Append(FILE_COPY, wxT("&Copy\tCtrl+C"), wxT("Copy selected text")); + menu->Append(FILE_PASTE, wxT("&Paste\tCtrl+V"), wxT("Copy selected text")); + menu->AppendSeparator(); + menu->Append(FILE_QUIT, wxT("E&xit"), wxT("Quit the application")); + b->Append(menu, wxT("&File")); + /*view*/ + menu = new wxMenu(); + vp_list = new wxMenu(); + menu->Append(0, wxT("&Viewpoint"), vp_list); + smenu = new wxMenu(); + smenu->Append(ID_HEADLIGHT, wxT("Headlight"), wxT("Turns headlight on/off"), wxITEM_CHECK); + smenu->AppendSeparator(); + smenu->Append(ID_NAVIGATE_NONE, wxT("None"), wxT("Disables Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_WALK, wxT("Walk"), wxT("Walk Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_FLY, wxT("Fly"), wxT("Fly Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_EXAMINE, wxT("Examine"), wxT("Examine Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_PAN, wxT("Pan"), wxT("Pan Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_SLIDE, wxT("Slide"), wxT("Slide Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_ORBIT, wxT("Orbit"), wxT("Orbit Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_GAME, wxT("Game"), wxT("Game Navigation"), wxITEM_CHECK); + smenu->AppendSeparator(); + wxMenu *ssmenu = new wxMenu(); + ssmenu->Append(ID_COLLIDE_NONE, wxT("None"), wxT("No Collision detection"), wxITEM_CHECK); + ssmenu->Append(ID_COLLIDE_REG, wxT("Regular"), wxT("Regular Collision detection"), wxITEM_CHECK); + ssmenu->Append(ID_COLLIDE_DISP, wxT("Displacement"), wxT("Collision detecion with camera displacement"), wxITEM_CHECK); + smenu->Append(0, wxT("&Collision"), ssmenu); + smenu->Append(ID_GRAVITY, wxT("Gravity"), wxT("Turns gravity on/off"), wxITEM_CHECK); + smenu->AppendSeparator(); + smenu->Append(ID_NAVIGATE_RESET, wxT("Reset"), wxT("Reset Navigation")); + + menu->Append(0, wxT("&Navigation"), smenu); + menu->AppendSeparator(); + menu->Append(VIEW_FULLSCREEN, wxT("&Fullscreen"), wxT("Toggles Fullscreen"), wxITEM_CHECK); + menu->Append(VIEW_ORIGINAL, wxT("&Original Size"), wxT("Restore original size")); + smenu = new wxMenu(); + smenu->Append(VIEW_AR_KEEP, wxT("Keep Original\tCtrl+1"), wxT("Keep original aspect ratio"), wxITEM_CHECK); + smenu->Append(VIEW_AR_FILL, wxT("Fill Screen\tCtrl+2"), wxT("Stretch presentation to fill screen"), wxITEM_CHECK); + smenu->Append(VIEW_AR_43, wxT("Ratio 4/3\tCtrl+3"), wxT("Force aspect ratio to 4/3"), wxITEM_CHECK); + smenu->Append(VIEW_AR_169, wxT("Ratio 16/9\tCtrl+4"), wxT("Force aspect ratio to 16/9"), wxITEM_CHECK); + menu->Append(0, wxT("&Aspect Ratio"), smenu); + smenu->Check(VIEW_AR_KEEP, 1); + menu->AppendSeparator(); + menu->Append(VIEW_OPTIONS, wxT("&Options"), wxT("View Options")); + menu->AppendSeparator(); + menu->Append(VIEW_RTI, wxT("&Resource Usage"), wxT("View Resource Usage"), wxITEM_CHECK); + menu->Append(VIEW_LOGS, wxT("&Logs"), wxT("View GPAC logs")); + b->Append(menu, wxT("&View")); + + /*play*/ + menu = new wxMenu(); + sel_menu = new wxMenu(); + sel_menu->Append(0, wxT("&Audio"), new wxMenu()); + sel_menu->Append(0, wxT("&Video"), new wxMenu()); + sel_menu->Append(0, wxT("&Subtitles"), new wxMenu()); + sel_menu->AppendSeparator(); + sel_menu->Append(ID_ADD_SUB, wxT("&Add Subtitle"), wxT("Adds subtitle")); + menu->Append(ID_STREAM_MENU, wxT("&Streams Selection"), sel_menu); + chap_menu = new wxMenu(); + menu->Append(ID_CHAPTER_MENU, wxT("&Chapters"), chap_menu); + + menu->AppendSeparator(); + menu->Append(VIEW_PLAYLIST, wxT("&Playlist\tCtrl+L"), wxT("Show navigation history as playlist"), wxITEM_CHECK); + menu->Append(ID_CLEAR_NAV, wxT("&Clear History"), wxT("Clear navigation history")); + menu->AppendSeparator(); + menu->Append(FILE_PLAY, wxT("&Play/Pause\tCtrl+P"), wxT("Play/Pause/Resume Presentation")); + menu->Append(FILE_STEP, wxT("&Step-by-Step\tCtrl+S"), wxT("Play/Pause/Resume Presentation")); + menu->Append(FILE_STOP, wxT("&Stop"), wxT("Stop Presentation")); + menu->AppendSeparator(); + menu->Append(FILE_RELOAD_CONFIG, wxT("&Reload Config\tCtrl+R"), wxT("Reload Configuration File")); + menu->Append(FILE_RELOAD, wxT("&Reload File\tCtrl+R"), wxT("Reload Presentation")); + b->Append(menu, wxT("&Play")); + + menu = new wxMenu(); + menu->Append(APP_SHORTCUTS, wxT("&Shortcuts"), wxT("Show keyboard shortcuts")); + menu->Append(APP_NAV_KEYS, wxT("&Navigation Keys"), wxT("Show navigation keys")); + menu->AppendSeparator(); + menu->Append(APP_ABOUT, wxT("&About"), wxT("Display information and copyright")); + b->Append(menu, wxT("&?")); + + SetMenuBar(b); + + m_pStatusbar = CreateStatusBar(1, 0, -1, wxT("statusBar")); + ws[0] = 60; + ws[1] = 70; + ws[2] = -1; + m_pStatusbar->SetFieldsCount(3, ws); + + SetStatusBarPane(2); + wxColour foreCol = m_pStatusbar->GetBackgroundColour(); + SetBackgroundColour(foreCol); + + + m_pTimer = new wxTimer(); + m_pTimer->SetOwner(this, ID_CTRL_TIMER); + m_bGrabbed = 0; + + /*create toolbar*/ + m_pToolBar = CreateToolBar(wxTB_FLAT|wxTB_HORIZONTAL); + m_pOpenFile = new wxBitmap(tool_open_file); + m_pPrev = new wxBitmap(tool_prev); + m_pNext = new wxBitmap(tool_next); + m_pPlay = new wxBitmap(tool_play); + m_pPause = new wxBitmap(tool_pause); + m_pStep = new wxBitmap(tool_step); + m_pStop = new wxBitmap(tool_stop); + m_pInfo = new wxBitmap(tool_info); + m_pConfig = new wxBitmap(tool_config); + m_pSW2D = new wxBitmap(tool_sw_2d); + m_pSW3D = new wxBitmap(tool_sw_3d); + + m_pToolBar->AddTool(FILE_OPEN, wxT(""), *m_pOpenFile, wxT("Open File")); + m_pToolBar->AddSeparator(); + m_pPrevBut = new wxMenuButton(m_pToolBar, FILE_PREV, *m_pPrev); + m_pPrevBut->SetToolTip(wxT("Previous Location")); + m_pToolBar->AddControl(m_pPrevBut); + m_pNextBut = new wxMenuButton(m_pToolBar, FILE_NEXT, *m_pNext); + m_pNextBut->SetToolTip(wxT("Next Location")); + m_pToolBar->AddControl(m_pNextBut); + + m_pToolBar->AddSeparator(); + m_pToolBar->AddTool(FILE_PLAY, wxT(""), *m_pPlay, wxT("Play/Pause File")); + m_pToolBar->AddTool(FILE_STEP, wxT(""), *m_pStep, wxT("Step-by-Step Mode")); + m_pToolBar->AddTool(FILE_STOP, wxT(""), *m_pStop, wxT("Stop File")); + m_pToolBar->AddSeparator(); + m_pToolBar->AddTool(FILE_PROPERTIES, wxT(""), *m_pInfo, wxT("Show File Information")); + m_pToolBar->AddSeparator(); + m_pToolBar->AddTool(VIEW_OPTIONS, wxT(""), *m_pConfig, wxT("GPAC Configuration")); + m_pToolBar->AddTool(SWITCH_RENDER, wxT(""), *m_pSW3D, wxT("Switch 2D/3D Renderers")); + + m_pToolBar->Realize(); + + m_Address = new wxMyComboBox(this, ID_ADDRESS, wxT(""), wxPoint(50, 0), wxSize(80, 20)); + wxStaticText *add_text = new wxStaticText(this, -1, wxT("URL"), wxPoint(0, 0), wxSize(40, 20)); + add_text->SetBackgroundColour(foreCol); + + m_pAddBar = new wxBoxSizer(wxHORIZONTAL); + m_pAddBar->Add(add_text, 0, wxALIGN_TOP); + m_pAddBar->Add(m_Address, 2, wxALIGN_CENTER|wxEXPAND|wxADJUST_MINSIZE); + m_pAddBar->SetMinSize(80, 32); + m_pAddBar->Layout(); + + m_pProg = new wxSlider(this, ID_SLIDER, 0, 0, 1000, wxPoint(0, 22), wxSize(80, 22), wxSL_HORIZONTAL|wxSUNKEN_BORDER); + m_pProg->Enable(0); + m_pProg->Show(); + m_pProg->SetBackgroundColour(foreCol); + + m_pView = new wxWindow(this, -1, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); + m_pView->SetBackgroundColour(wxColour(wxT("BLACK"))); +#ifdef __WXGTK__ + m_pVisual = new wxWindow(m_pView, -1, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); + m_pVisual->SetBackgroundColour(wxColour(wxT("BLACK"))); +#endif + + m_pPlayList = new wxPlaylist(this); + m_pPlayList->SetIcon(wxIcon(osmo4)); + m_pPlayList->Hide(); + Raise(); + Show(); + + m_connected = 0; + if (!LoadTerminal()) { + Close(TRUE); + return; + } + + + + if (m_bExternalView) SetWindowStyle(wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER)); + DoLayout(320, 240); + UpdateRenderSwitch(); + + const char *sOpt = gf_cfg_get_key(m_user.config, "General", "ConsoleOff"); + m_console_off = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(m_user.config, "General", "Loop"); + m_loop = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(m_user.config, "General", "LookForSubtitles"); + m_lookforsubs = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + gf_term_set_option(m_term, GF_OPT_AUDIO_VOLUME, 100); + + ReloadURLs(); + Raise(); + m_pStatusbar->SetStatusText(wxT("Ready"), 2); + m_LastStatusTime = 0; + + m_pPrevBut->Refresh(); + m_pNextBut->Refresh(); + + wxOsmo4App &app = wxGetApp(); + if (app.argc>1) { + m_pPlayList->QueueURL(wxString(app.argv[1])); + m_pPlayList->RefreshList(); + m_pPlayList->PlayNext(); + } else { + char sPL[GF_MAX_PATH]; + strcpy((char *) sPL, szAppPath); +#ifdef WIN32 + strcat(sPL, "gpac_pl.m3u"); +#else + strcat(sPL, ".gpac_pl.m3u"); +#endif + m_pPlayList->OpenPlaylist(wxString(sPL, wxConvUTF8) ); + const char *sOpt = gf_cfg_get_key(m_user.config, "General", "PLEntry"); + if (sOpt) { + m_pPlayList->m_cur_entry = atoi(sOpt); + if (m_pPlayList->m_cur_entry>=(s32)gf_list_count(m_pPlayList->m_entries)) + m_pPlayList->m_cur_entry = -1; + } + + sOpt = gf_cfg_get_key(m_user.config, "General", "StartupFile"); + if (sOpt) { + gf_term_connect(m_term, sOpt); + m_bStartupFile = 1; + } + } + + sOpt = gf_cfg_get_key(m_user.config, "Audio", "DriverName"); + + if (!strcmp(sOpt, "No Audio Output Available")) { + ::wxLogMessage(wxT("WARNING: no audio output availble - make sure no other program is locking the sound card")); + SetStatus(wxT("No audio ouput available")); + + } else { + SetStatus(wxT("Ready")); + } +} + +wxOsmo4Frame::~wxOsmo4Frame() +{ + vp_list = NULL; + sel_menu = NULL; + + if (m_user.modules) gf_modules_del(m_user.modules); + gf_sys_close(); + if (m_user.config) gf_cfg_del(m_user.config); + + if (m_chapters_start) free(m_chapters_start); + if (m_pView) delete m_pView; + + //m_pToolBar->RemoveTool(FILE_PREV); + //m_pToolBar->RemoveTool(FILE_NEXT); + + delete m_pPrevBut; + delete m_pNextBut; + delete m_pPlayList; + delete m_pTimer; + delete m_pOpenFile; + delete m_pPrev; + delete m_pNext; + delete m_pPlay; + delete m_pPause; + delete m_pStep; + delete m_pStop; + delete m_pInfo; + delete m_pConfig; + delete m_pSW2D; + delete m_pSW3D; +} + + +BEGIN_EVENT_TABLE(wxOsmo4Frame, wxFrame) + EVT_CLOSE(wxOsmo4Frame::OnCloseApp) + EVT_MENU(FILE_OPEN, wxOsmo4Frame::OnFileOpen) + EVT_MENU(FILE_OPEN_URL, wxOsmo4Frame::OnFileOpenURL) + EVT_MENU(FILE_RELOAD_CONFIG, wxOsmo4Frame::OnFileReloadConfig) + EVT_MENU(FILE_RELOAD, wxOsmo4Frame::OnFileReload) + EVT_MENU(FILE_PROPERTIES, wxOsmo4Frame::OnFileProperties) + EVT_MENU(FILE_QUIT, wxOsmo4Frame::OnFileQuit) + EVT_MENU(VIEW_FULLSCREEN, wxOsmo4Frame::OnFullScreen) + EVT_MENU(VIEW_OPTIONS, wxOsmo4Frame::OnOptions) + EVT_MENU(VIEW_AR_KEEP, wxOsmo4Frame::OnViewARKeep) + EVT_MENU(VIEW_AR_FILL, wxOsmo4Frame::OnViewARFill) + EVT_MENU(VIEW_AR_169, wxOsmo4Frame::OnViewAR169) + EVT_MENU(VIEW_AR_43, wxOsmo4Frame::OnViewAR43) + EVT_MENU(VIEW_ORIGINAL, wxOsmo4Frame::OnViewOriginal) + EVT_MENU(VIEW_PLAYLIST, wxOsmo4Frame::OnPlaylist) + EVT_UPDATE_UI(VIEW_PLAYLIST, wxOsmo4Frame::OnUpdatePlayList) + EVT_MENU(FILE_COPY, wxOsmo4Frame::OnFileCopy) + EVT_UPDATE_UI(FILE_COPY, wxOsmo4Frame::OnUpdateFileCopy) + EVT_MENU(FILE_PASTE, wxOsmo4Frame::OnFilePaste) + EVT_UPDATE_UI(FILE_PASTE, wxOsmo4Frame::OnUpdateFilePaste) + + EVT_MENU(ID_CLEAR_NAV, wxOsmo4Frame::OnClearNav) + EVT_UPDATE_UI(ID_STREAM_MENU, wxOsmo4Frame::OnUpdateStreamMenu) + EVT_UPDATE_UI(ID_CHAPTER_MENU, wxOsmo4Frame::OnUpdateChapterMenu) + EVT_MENU(ID_ADD_SUB, wxOsmo4Frame::OnAddSub) + + EVT_MENU(ID_MCACHE_ENABLE, wxOsmo4Frame::OnCacheEnable) + EVT_UPDATE_UI(ID_MCACHE_ENABLE, wxOsmo4Frame::OnUpdateCacheEnable) + EVT_MENU(ID_MCACHE_STOP, wxOsmo4Frame::OnCacheStop) + EVT_MENU(ID_MCACHE_ABORT, wxOsmo4Frame::OnCacheAbort) + EVT_UPDATE_UI(ID_MCACHE_STOP, wxOsmo4Frame::OnUpdateCacheAbort) + EVT_UPDATE_UI(ID_MCACHE_ABORT, wxOsmo4Frame::OnUpdateCacheAbort) + + + EVT_MENU(APP_SHORTCUTS, wxOsmo4Frame::OnShortcuts) + EVT_MENU(APP_NAV_KEYS, wxOsmo4Frame::OnNavInfo) + EVT_MENU(APP_ABOUT, wxOsmo4Frame::OnAbout) + EVT_GPACEVENT(wxOsmo4Frame::OnGPACEvent) + EVT_TIMER(ID_CTRL_TIMER, wxOsmo4Frame::OnTimer) + EVT_COMMAND_SCROLL(ID_SLIDER, wxOsmo4Frame::OnSlide) + EVT_MENU(VIEW_LOGS, wxOsmo4Frame::OnLogs) + EVT_MENU(VIEW_RTI, wxOsmo4Frame::OnRTI) + + EVT_MENUBUTTON_OPEN(FILE_PREV, wxOsmo4Frame::OnFilePrevOpen) + EVT_MENUBUTTON_OPEN(FILE_NEXT, wxOsmo4Frame::OnFileNextOpen) + EVT_MENU(FILE_PREV, wxOsmo4Frame::OnNavPrev) + EVT_UPDATE_UI(FILE_PREV, wxOsmo4Frame::OnUpdateNavPrev) + EVT_MENU_RANGE(ID_NAV_PREV_0, ID_NAV_PREV_9, wxOsmo4Frame::OnNavPrevMenu) + EVT_MENU(FILE_NEXT, wxOsmo4Frame::OnNavNext) + EVT_UPDATE_UI(FILE_NEXT, wxOsmo4Frame::OnUpdateNavNext) + EVT_MENU_RANGE(ID_NAV_NEXT_0, ID_NAV_NEXT_9, wxOsmo4Frame::OnNavNextMenu) + + EVT_TOOL(FILE_PLAY, wxOsmo4Frame::OnFilePlay) + EVT_TOOL(FILE_STEP, wxOsmo4Frame::OnFileStep) + EVT_TOOL(FILE_STOP, wxOsmo4Frame::OnFileStop) + EVT_TOOL(SWITCH_RENDER, wxOsmo4Frame::OnRenderSwitch) + + EVT_COMBOBOX(ID_ADDRESS, wxOsmo4Frame::OnURLSelect) + + EVT_MENU_RANGE(ID_SELSTREAM_0, ID_SELSTREAM_9, wxOsmo4Frame::OnStreamSel) + EVT_UPDATE_UI_RANGE(ID_SELSTREAM_0, ID_SELSTREAM_9, wxOsmo4Frame::OnUpdateStreamSel) + + EVT_MENU_RANGE(ID_SETCHAP_FIRST, ID_SETCHAP_LAST, wxOsmo4Frame::OnChapterSel) + EVT_UPDATE_UI_RANGE(ID_SETCHAP_FIRST, ID_SETCHAP_LAST, wxOsmo4Frame::OnUpdateChapterSel) + + EVT_MENU_RANGE(ID_VIEWPOINT_FIRST, ID_VIEWPOINT_LAST, wxOsmo4Frame::OnViewport) + EVT_UPDATE_UI_RANGE(ID_VIEWPOINT_FIRST, ID_VIEWPOINT_LAST, wxOsmo4Frame::OnUpdateViewport) + + EVT_MENU_RANGE(ID_NAVIGATE_NONE, ID_NAVIGATE_GAME, wxOsmo4Frame::OnNavigate) + EVT_UPDATE_UI_RANGE(ID_NAVIGATE_NONE, ID_NAVIGATE_GAME, wxOsmo4Frame::OnUpdateNavigation) + EVT_MENU(ID_NAVIGATE_RESET, wxOsmo4Frame::OnNavigateReset) + + EVT_MENU_RANGE(ID_COLLIDE_NONE, ID_COLLIDE_DISP, wxOsmo4Frame::OnCollide) + EVT_UPDATE_UI_RANGE(ID_COLLIDE_NONE, ID_COLLIDE_DISP, wxOsmo4Frame::OnUpdateCollide) + + EVT_MENU(ID_HEADLIGHT, wxOsmo4Frame::OnHeadlight) + EVT_UPDATE_UI(ID_HEADLIGHT, wxOsmo4Frame::OnUpdateHeadlight) + EVT_MENU(ID_GRAVITY, wxOsmo4Frame::OnGravity) + EVT_UPDATE_UI(ID_GRAVITY, wxOsmo4Frame::OnUpdateGravity) + + EVT_UPDATE_UI(FILE_PROPERTIES, wxOsmo4Frame::OnUpdateNeedsConnect) + EVT_UPDATE_UI(FILE_RELOAD, wxOsmo4Frame::OnUpdateNeedsConnect) + EVT_UPDATE_UI(FILE_PLAY, wxOsmo4Frame::OnUpdatePlay) + EVT_UPDATE_UI(FILE_STOP, wxOsmo4Frame::OnUpdateNeedsConnect) + EVT_UPDATE_UI(FILE_STEP, wxOsmo4Frame::OnUpdateNeedsConnect) + EVT_UPDATE_UI(VIEW_ORIGINAL, wxOsmo4Frame::OnUpdateNeedsConnect) + EVT_UPDATE_UI(VIEW_FULLSCREEN, wxOsmo4Frame::OnUpdateFullScreen) + EVT_UPDATE_UI(VIEW_AR_KEEP, wxOsmo4Frame::OnUpdateAR) + EVT_UPDATE_UI(VIEW_AR_FILL, wxOsmo4Frame::OnUpdateAR) + EVT_UPDATE_UI(VIEW_AR_169, wxOsmo4Frame::OnUpdateAR) + EVT_UPDATE_UI(VIEW_AR_43, wxOsmo4Frame::OnUpdateAR) + + EVT_SIZE(wxOsmo4Frame::OnSize) +END_EVENT_TABLE() + +void wxOsmo4Frame::DoLayout(u32 v_width, u32 v_height) +{ + wxPoint pos; + if (!m_Address || !m_pProg) return; + + int t_h = m_pToolBar->GetSize().y; + int a_h = m_pAddBar->GetSize().y; + int p_h = m_pProg->GetSize().y; + + if (m_bExternalView) { + if (v_width && v_height) { + m_orig_width = v_width; + m_orig_height = v_height; + } + SetClientSize(320, a_h+p_h+t_h); + m_pAddBar->SetDimension(0,0, 320, a_h); + m_pProg->SetSize(0, t_h+a_h, 320, p_h, 0); + return; + } + + if (v_width && v_height) { + m_orig_width = v_width; + m_orig_height = v_height; + v_height += a_h + p_h + t_h; + SetClientSize(v_width, v_height); + m_pView->SetSize(0, a_h+t_h, v_width, v_height, 0); + m_pAddBar->SetDimension(0, t_h, v_width, a_h); + m_pProg->SetSize(0, v_height - p_h, v_width, p_h, 0); + } + wxSize s = GetClientSize(); + s.y -= a_h + p_h + t_h; + if (m_pView) { + m_pView->SetSize(0, a_h+t_h, s.x, s.y, 0); + m_pAddBar->SetDimension(0, 0, s.x, a_h); + m_pAddBar->SetDimension(0, 0, s.x, a_h); + m_pAddBar->Layout(); + m_pProg->SetSize(0, s.y+t_h+a_h, s.x, p_h, 0); + if (m_term) gf_term_set_size(m_term, s.x, s.y); + } +} + +void wxOsmo4Frame::OnSize(wxSizeEvent &event) +{ + DoLayout(); +} + +void wxOsmo4Frame::OnCloseApp(wxCloseEvent &WXUNUSED(event)) +{ + if (m_term) gf_term_del(m_term); + m_term = NULL; + Destroy(); +} + + +wxString wxOsmo4Frame::GetFileFilter() +{ + u32 keyCount, i; + wxString sFiles, sSupportedFiles, sExts; + + /*force MP4 and 3GP files at beginning to make sure they are selected (Win32 bug with too large filters)*/ + sSupportedFiles = wxT("All Known Files|*.m3u;*.pls;*.mp4;*.3gp;*.3g2"); + sExts = wxT(""); + sFiles = wxT(""); + keyCount = gf_cfg_get_key_count(m_user.config, "MimeTypes"); + for (i=0; i=0) continue; + /*if same extensions for # mime types skip (don't polluate the file list)*/ + if (sExts.Find(wxString(szKeyList, wxConvUTF8) )>=0) continue; + + sExts += wxString(szKeyList, wxConvUTF8); + sExts += wxT(" "); + sFiles += wxString(sDesc, wxConvUTF8); + sFiles += wxT("|"); + + wxString sOpt = wxString(szKeyList, wxConvUTF8); + while (1) { + wxString ext = sOpt.BeforeFirst(' '); + if (ext.Find('.')<0) { + if (first) first = 0; + else sFiles += wxT(";"); + sFiles += wxT("*."); + sFiles += ext; + wxString sext = ext; + sext += wxT(";"); + if (sSupportedFiles.Find(sext)<0) { + sSupportedFiles += wxT(";*."); + sSupportedFiles += ext; + } + } + if (sOpt==ext) break; + wxString rem = ext + wxT(" "); + sOpt.Replace(rem, wxT(""), TRUE); + } + sFiles += wxT("|"); + } + sSupportedFiles += wxT("|"); + sSupportedFiles += sFiles; + sSupportedFiles += wxT("M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|All Files|*.*||"); + return sSupportedFiles; +} + +void wxOsmo4Frame::OnFileOpen(wxCommandEvent & WXUNUSED(event)) +{ + wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), GetFileFilter(), wxOPEN | wxMULTIPLE | wxCHANGE_DIR /*| wxHIDE_READONLY*/); + + if (dlg.ShowModal() != wxID_OK) return; + + wxArrayString stra; + dlg.GetPaths(stra); + if (stra.GetCount() == 1) { + m_pPlayList->Truncate(); + } else { + m_pPlayList->Clear(); + } + for (u32 i=0; iQueueURL(stra[i]); + + m_pPlayList->RefreshList(); + m_pPlayList->PlayNext(); +} + +void wxOsmo4Frame::OnFileOpenURL(wxCommandEvent & WXUNUSED(event)) +{ + OpenURLDlg dlg(this, m_user.config); + if (dlg.ShowModal()==wxID_OK) { + m_pPlayList->Truncate(); + m_pPlayList->QueueURL(dlg.m_urlVal); + m_pPlayList->RefreshList(); + m_pPlayList->PlayNext(); + } +} + +void wxOsmo4Frame::OnFileProperties(wxCommandEvent & WXUNUSED(event)) +{ + wxFileProps dlg(this); + dlg.SetIcon(wxIcon(osmo4)); + dlg.ShowModal(); +} + +void wxOsmo4Frame::OnFileReload(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_disconnect(m_term); + m_connected = 0; + DoConnect(); +} + +void wxOsmo4Frame::OnFileReloadConfig(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_RELOAD_CONFIG, 1); +} + +void wxOsmo4Frame::OnFileQuit(wxCommandEvent & WXUNUSED(event)) +{ + Close(FALSE); +} + +void wxOsmo4Frame::OnViewOriginal(wxCommandEvent & WXUNUSED(event)) +{ + if (!m_bExternalView) { + DoLayout(m_orig_width, m_orig_height); + } else { + gf_term_set_option(m_term, GF_OPT_ORIGINAL_VIEW, 1); + } +} + +void wxOsmo4Frame::OnOptions(wxCommandEvent & WXUNUSED(event)) +{ + wxGPACControl dlg(this); + dlg.SetIcon(wxIcon(osmo4)); + dlg.ShowModal(); +} + +void wxOsmo4Frame::DoConnect() +{ + //if (m_connected) { gf_term_disconnect(m_term); m_connected = 0; } + + wxString url = m_pPlayList->GetURL(); + m_Address->SetValue(url); +#ifdef __WXGTK__ + m_pVisual->SetFocus(); +#else + m_pView->SetFocus(); +#endif + wxString txt = wxT("Osmo4 - "); + txt += m_pPlayList->GetDisplayName(); + SetTitle(txt); + m_bStartupFile = 0; + gf_term_connect(m_term, url.mb_str(wxConvUTF8)); +} + +void wxOsmo4Frame::OnLogs(wxCommandEvent & WXUNUSED(event)) +{ + m_pLogs->Show(); +} + +void wxOsmo4Frame::OnUpdateNeedsConnect(wxUpdateUIEvent &event) +{ + event.Enable(m_connected ? 1 : 0); +} + +void wxOsmo4Frame::OnUpdatePlay(wxUpdateUIEvent &event) +{ + event.Enable( (m_connected || m_pPlayList->HasValidEntries()) ? 1 : 0); +} + +void wxOsmo4Frame::OnUpdateFullScreen(wxUpdateUIEvent &event) +{ + if (m_connected) { + event.Enable(1); + event.Check(gf_term_get_option(m_term, GF_OPT_FULLSCREEN) ? 1 : 0); + } else { + event.Enable(0); + } +} + +void wxOsmo4Frame::OnFullScreen(wxCommandEvent & WXUNUSED(event)) +{ + Bool isFS = gf_term_get_option(m_term, GF_OPT_FULLSCREEN) ? 1 : 0; + gf_term_set_option(m_term, GF_OPT_FULLSCREEN, isFS ? 0 : 1); +} + +void wxOsmo4Frame::OnViewARKeep(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP); +} +void wxOsmo4Frame::OnViewARFill(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN); +} +void wxOsmo4Frame::OnViewAR169(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9); +} +void wxOsmo4Frame::OnViewAR43(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3); +} + +void wxOsmo4Frame::OnUpdateAR(wxUpdateUIEvent &event) +{ + if (!m_connected) { + event.Enable(0); + return; + } + event.Enable(1); + u32 val = gf_term_get_option(m_term, GF_OPT_ASPECT_RATIO); + if ((event.GetId() == VIEW_AR_FILL) && (val==GF_ASPECT_RATIO_FILL_SCREEN)) + event.Check(1); + else if ((event.GetId() == VIEW_AR_KEEP) && (val==GF_ASPECT_RATIO_KEEP)) + event.Check(1); + else if ((event.GetId() == VIEW_AR_169) && (val==GF_ASPECT_RATIO_16_9)) + event.Check(1); + else if ((event.GetId() == VIEW_AR_43) && (val==GF_ASPECT_RATIO_4_3)) + event.Check(1); + else event.Check(0); +} + +void wxOsmo4Frame::OnShortcuts(wxCommandEvent & WXUNUSED(event)) +{ + wxMessageDialog dlg(this, + wxT("Shortcuts with focus on main frame:\n") + wxT("Open File: Ctrl + O\n") + wxT("Show File Information: Ctrl + I\n") + wxT("Reload File: Ctrl + R\n") + wxT("Pause/Resume File: Ctrl + P\n") + wxT("Step by Step: Ctrl + S\n") + wxT("Fullscreen On/Off: Alt + Return\n") + wxT("View Playlist: Ctrl + L\n") + wxT("Aspect Ratio Normal: Ctrl + 1\n") + wxT("Aspect Ratio Fill: Ctrl + 2\n") + wxT("Aspect Ratio 4/3: Ctrl + 3\n") + wxT("Aspect Ratio 16/9: Ctrl + 4\n") + wxT("\n") + wxT("Shortcuts with focus on video frame:\n") + wxT("Seek +5% into presentation: Alt + right arrow\n") + wxT("Seek -5% into presentation: Alt + left arrow\n") + wxT("Seek +1min into presentation: Alt + up arrow\n") + wxT("Seek -1min into presentation: Alt + down arrow\n") + wxT("Next Playlist Entry: Ctrl + right arrow\n") + wxT("Prev Playlist Entry: Ctrl + left arrow\n") + + , wxT("Shortcuts Available on Osmo4") + , wxOK); + + dlg.ShowModal(); +} + +void wxOsmo4Frame::OnNavInfo(wxCommandEvent & WXUNUSED(event)) +{ + wxMessageDialog dlg(this, + wxT("* Walk & Fly modes:\n") + wxT("\tH move: H pan - V move: Z-translate - V move+CTRL or Wheel: V pan - Right Click (Walk only): Jump\n") + wxT("\tleft/right: H pan - left/right+CTRL: H translate - up/down: Z-translate - up/down+CTRL: V pan\n") + wxT("* Pan mode:\n") + wxT("\tH move: H pan - V move: V pan - V move+CTRL or Wheel: Z-translate\n") + wxT("\tleft/right: H pan - left/right+CTRL: H translate - up/down: V pan - up/down+CTRL: Z-translate\n") + wxT("* Slide mode:\n") + wxT("\tH move: H translate - V move: V translate - V move+CTRL or Wheel: Z-translate\n") + wxT("\tleft/right: H translate - left/right+CTRL: H pan - up/down: V translate - up/down+CTRL: Z-translate\n") + wxT("* Examine & Orbit mode:\n") + wxT("\tH move: Y-Axis rotate - H move+CTRL: No move - V move: X-Axis rotate - V move+CTRL or Wheel: Z-translate\n") + wxT("\tleft/right: Y-Axis rotate - left/right+CTRL: H translate - up/down: X-Axis rotate - up/down+CTRL: Y-translate\n") + wxT("* VR mode:\n") + wxT("\tH move: H pan - V move: V pan - V move+CTRL or Wheel: Camera Zoom\n") + wxT("\tleft/right: H pan - up/down: V pan - up/down+CTRL: Camera Zoom\n") + wxT("* Game mode (press END to escape):\n") + wxT("\tH move: H pan - V move: V pan\n") + wxT("\tleft/right: H translate - up/down: Z-translate\n") + wxT("\n") + wxT("* All 3D modes: CTRL+PGUP/PGDOWN will zoom in/out camera (field of view) \n") + wxT("\n") + wxT("*Slide Mode in 2D:\n") + wxT("\tH move: H translate - V move: V translate - V move+CTRL: zoom\n") + wxT("\tleft/right: H translate - up/down: V translate - up/down+CTRL: zoom\n") + wxT("*Examine Mode in 2D (3D renderer only):\n") + wxT("\tH move: Y-Axis rotate - V move: X-Axis rotate\n") + wxT("\tleft/right: Y-Axis rotate - up/down: X-Axis rotate\n") + wxT("\n") + wxT("HOME: reset navigation to last viewpoint (2D or 3D navigation)\n") + wxT("SHIFT key in all modes: fast movement\n") + + , wxT("3D navigation keys (\'H\'orizontal and \'V\'ertical) used in GPAC") + , wxOK ); + dlg.ShowModal(); +} + + +/*open file dlg*/ +class AboutDlg : public wxDialog { +public: + AboutDlg(wxWindow *parent); + +private: + wxStaticText *m_info; + wxButton *m_close; + void OnClose(wxCommandEvent& event); + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(AboutDlg, wxDialog) + EVT_BUTTON(ID_ABOUT_CLOSE, AboutDlg::OnClose) +END_EVENT_TABLE() + +AboutDlg::AboutDlg(wxWindow *parent) + : wxDialog(parent, -1, wxString(wxT("GPAC/Osmo4 V ")wxT(GPAC_FULL_VERSION))) +{ + SetSize(220, 320); + Centre(); + wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); + + m_info = new wxStaticText(this, -1, wxT("http://gpac.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); + sizer->Add(m_info, 1, wxEXPAND|wxADJUST_MINSIZE, 0); + m_close = new wxButton(this, ID_ABOUT_CLOSE, wxT("Close"), wxDefaultPosition, wxSize(120, 20)); + sizer->Add(m_close, 0, wxEXPAND, 0); + + SetIcon(wxIcon(osmo4)); + m_info->SetLabel( + wxT("Osmo4 Player\n") + wxT("GPAC Multimedia Framework\n") + wxT("\n") + wxT("This program is free software and may\n") + wxT("be distributed according to the terms\n") + wxT("of the GNU Lesser General Public License\n") + wxT("\n") + wxT("Copyright (c) Jean Le Feuvre 2000-2005\n") + wxT("(c) ENST 2005-200X\n") + wxT("All Rights Reserved\n") + wxT("http://gpac.sourceforge.net\n") + wxT("\n") + wxT(" ** With Many Thanks To ** \n\n") + wxT("Mozilla SpiderMonkey (JavaScript)\n") + wxT("The FreeType Project\n") + wxT("The PNG Group, The I.J.G.\n") + wxT("FFMPEG, FAAD, XVID, MAD\n") + ); + + SetSizer(sizer); + sizer->Fit(this); +} +void AboutDlg::OnClose(wxCommandEvent& WXUNUSED(event)) +{ + Close(FALSE); +} + +void wxOsmo4Frame::OnAbout(wxCommandEvent & WXUNUSED(event)) +{ + AboutDlg dlg(this); + dlg.ShowModal(); +} + + +void wxOsmo4Frame::OnGPACEvent(wxGPACEvent &event) +{ + wxString cmd; + wxCommandEvent evt; + if (!m_term) return; + + switch (event.gpac_evt.type) { + case GF_EVENT_NAVIGATE: + if (gf_term_is_supported_url(m_term, event.to_url.mb_str(wxConvUTF8), 1, 0)) { + char *str = gf_url_concatenate(m_pPlayList->GetURL().mb_str(wxConvUTF8), event.to_url.mb_str(wxConvUTF8)); + if (str) { + m_pPlayList->Truncate(); + m_pPlayList->QueueURL(wxString(str, wxConvUTF8)); + m_pPlayList->RefreshList(); + free(str); + m_pPlayList->PlayNext(); + } + return; + } + cmd = get_pref_browser(m_user.config); + cmd += wxT(" "); + cmd += event.to_url; + wxExecute(cmd); + break; + case GF_EVENT_QUIT: + Close(TRUE); + break; + case GF_EVENT_SET_CAPTION: + SetTitle(event.to_url); + break; + case GF_EVENT_CONNECT: + BuildStreamList(0); + ConnectAcknowledged(event.gpac_evt.connect.is_connected); + break; + case GF_EVENT_KEYDOWN: + if (!(event.gpac_evt.key.flags & GF_KEY_MOD_CTRL)) return; + switch (event.gpac_evt.key.key_code) { + case GF_KEY_R: + gf_term_set_option(m_term, GF_OPT_REFRESH, 1); + break; + case GF_KEY_P: + OnFilePlay(evt); + break; + case GF_KEY_S: + OnFileStep(evt); + break; + } + break; + case GF_EVENT_SCENE_SIZE: + m_orig_width = event.gpac_evt.size.width; + m_orig_height = event.gpac_evt.size.height; + case GF_EVENT_SIZE: + if (! gf_term_get_option(m_term, GF_OPT_FULLSCREEN)) { + DoLayout(event.gpac_evt.size.width, event.gpac_evt.size.height); + } + break; + case GF_EVENT_VIEWPOINTS: + BuildViewList(); + break; + case GF_EVENT_STREAMLIST: + BuildStreamList(0); + break; + } +} + + +static wxString format_time(u32 duration, u32 timescale) +{ + u32 h, m, s; + Float time = duration; + time /= timescale; + time *= 1000; + h = (u32) (time / 1000 / 3600); + m = (u32) (time / 1000 / 60 - h*60); + s = (u32) (time / 1000 - h*3600 - m*60); + return wxString::Format(wxT("%02d:%02d:%02d"), h, m, s); +} + +void wxOsmo4Frame::SetStatus(wxString str) +{ + //m_pStatusbar->SetStatusText(str, 2); + m_LastStatusTime = gf_sys_clock(); +} + +#define RTI_REFRESH_MS 500 +void wxOsmo4Frame::OnRTI(wxCommandEvent & event) +{ + m_bViewRTI = event.IsChecked(); + if (m_bViewRTI) { + if (!m_pTimer->IsRunning()) m_pTimer->Start(RTI_REFRESH_MS, 0); + } else if (!m_connected && m_pTimer->IsRunning()) { + m_LastStatusTime = 0; + m_pStatusbar->SetStatusText(wxT("Ready"), 2); + m_pTimer->Stop(); + } +} + +void wxOsmo4Frame::OnTimer(wxTimerEvent& WXUNUSED(event)) +{ + wxString str; + u32 now; + if (m_LastStatusTime) { + now = gf_sys_clock(); + if (now > 1000+m_LastStatusTime) { + m_LastStatusTime = 0; + m_pStatusbar->SetStatusText(wxT("Ready"), 2); + } + } + + if (m_bViewRTI) { + GF_SystemRTInfo rti; + if (!gf_sys_get_rti(RTI_REFRESH_MS, &rti, 0)) return; + if (rti.gpac_memory) rti.process_memory = rti.gpac_memory; + + str = wxString::Format(wxT("CPU %02d (%02d) - Mem %d kB" ), + rti.total_cpu_usage, rti.process_cpu_usage, rti.gpac_memory/1024); + + m_pStatusbar->SetStatusText(str, 2); + } + if (!m_connected) return; + + now = gf_term_get_time_in_ms(m_term); + if (!now) return; + + if (!m_duration) { + str = format_time(now, 1000); + m_pStatusbar->SetStatusText(str); + str = wxString::Format(wxT("FPS %.2f"), gf_term_get_framerate(m_term, 0)); + m_pStatusbar->SetStatusText(str, 1); + return; + } +#ifdef __WXGTK__ + if (m_bGrabbed) { + u32 now = gf_sys_clock() - m_last_grab_time; + if (now>200) { + m_bGrabbed = 0; + Double res = (Double) m_last_grab_pos; + res /= 1000; + res *= m_duration; + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + m_bToReset = 0; + } + gf_term_play_from_time(m_term, (u32) res, 0); + return; + } + } +#endif + + if (!m_bGrabbed) { + if ((now >= m_duration + 500) && gf_term_get_option(m_term, GF_OPT_IS_FINISHED)) { + m_pPlayList->PlayNext(); + } else { + Double val = now * 1000; + val /= m_duration; + m_pProg->SetValue((val<=1000) ? (u32) val : 1000); + + if (0) { + str = format_time(m_duration-now, 1000); + } else { + str = format_time(now, 1000); + } + m_pStatusbar->SetStatusText(str); + str = wxString::Format(wxT("FPS %.2f"), gf_term_get_framerate(m_term, 0)); + m_pStatusbar->SetStatusText(str, 1); + } + } +} + +void wxOsmo4Frame::ConnectAcknowledged(Bool bOk) +{ + if (bOk) { + m_pTimer->Start(RTI_REFRESH_MS, 0); + m_connected = 1; + m_bToReset = 0; + UpdatePlay(); + BuildChapterList(0); + } else { + BuildChapterList(1); + if (!m_connected) { + UpdatePlay(); + m_pTimer->Stop(); + //m_pProg->Enable(0); + } + } +} + +void wxOsmo4Frame::OnFilePlay(wxCommandEvent & WXUNUSED(event)) +{ + wxCommandEvent evt; + if (m_connected) { + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + if (m_bToReset) { + m_pTimer->Start(100, 0); + gf_term_play_from_time(m_term, 0, 0); + } + m_bToReset = 0; + UpdatePlay(); + } else { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + UpdatePlay(); + } + } else { + m_pPlayList->Play(); + } +} + +void wxOsmo4Frame::OnFileStep(wxCommandEvent & WXUNUSED(event)) +{ + wxCommandEvent evt; + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE); + UpdatePlay(); +} + +void wxOsmo4Frame::OnFileStop(wxCommandEvent &WXUNUSED(event)) +{ + Stop(); +} + +void wxOsmo4Frame::Stop() +{ + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PLAYING) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + } + m_bToReset = 1; + m_pTimer->Stop(); + m_pProg->SetValue(0); + UpdatePlay(); +} + +void wxOsmo4Frame::OnSlide(wxScrollEvent &event) +{ + if (!m_duration) return; + + /*wxSlider on GTK is buggy, so track a release timeout*/ +#ifdef __WXGTK__ + m_last_grab_time = gf_sys_clock(); + m_bGrabbed = 1; + m_last_grab_pos = event.GetPosition(); + Double now = (Double) m_last_grab_pos; + now /= 1000; + now *= m_duration; + wxString str = format_time((u32) (now), 1000); + m_pStatusbar->SetStatusText(str); + if (!m_pTimer->IsRunning()) m_pTimer->Start(100, 0); +#else + s32 type = event.GetEventType(); + if (type == wxEVT_SCROLL_THUMBTRACK) { + m_bGrabbed = 1; + Double now = (Double) event.GetPosition(); + now /= 1000; + now *= m_duration; + wxString str = format_time((u32) (now), 1000); + m_pStatusbar->SetStatusText(str); + } + else if (m_bGrabbed){ + m_bGrabbed = 0; + Double res = (Double) m_pProg->GetValue(); + res /= 1000; + res *= m_duration; + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + m_bToReset = 0; + if (!m_pTimer->IsRunning()) m_pTimer->Start(100, 0); + } + gf_term_play_from_time(m_term, (u32) res, 0); + } +#endif +} + + +void wxOsmo4Frame::BuildViewList() +{ + if (!vp_list || !m_connected) return; + + while (vp_list->GetMenuItemCount()) { + wxMenuItem* it = vp_list->FindItemByPosition(0); + vp_list->Delete(it); + } + + s32 id = ID_VIEWPOINT_FIRST; + nb_viewpoints = 0; + while (1) { + const char *szName; + Bool bound; + GF_Err e = gf_term_get_viewpoint(m_term, nb_viewpoints+1, &szName, &bound); + if (e) break; + if (szName) { + vp_list->AppendCheckItem(id+nb_viewpoints, wxString(szName, wxConvUTF8) ); + } else { + vp_list->AppendCheckItem(id+nb_viewpoints, wxString::Format(wxT("Viewpoint #%d"), nb_viewpoints+1) ); + } + nb_viewpoints++; + } +} + +void wxOsmo4Frame::OnViewport(wxCommandEvent & event) +{ + u32 ID = event.GetId() - ID_VIEWPOINT_FIRST; + gf_term_set_viewpoint(m_term, ID+1, NULL); +} + +void wxOsmo4Frame::OnUpdateViewport(wxUpdateUIEvent & event) +{ + u32 ID = event.GetId() - ID_VIEWPOINT_FIRST; + const char *szName; + Bool bound; + gf_term_get_viewpoint(m_term, ID+1, &szName, &bound); + event.Enable(1); + if (bound) event.Check(1); +} + +void wxOsmo4Frame::OnNavigate(wxCommandEvent & event) +{ + switch (event.GetId()) { + case ID_NAVIGATE_NONE: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_NONE); break; + case ID_NAVIGATE_WALK: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_WALK); break; + case ID_NAVIGATE_FLY: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_FLY); break; + case ID_NAVIGATE_EXAMINE: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_EXAMINE); break; + case ID_NAVIGATE_PAN: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_PAN); break; + case ID_NAVIGATE_SLIDE: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_SLIDE); break; + case ID_NAVIGATE_ORBIT: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_ORBIT); break; + case ID_NAVIGATE_GAME: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_GAME); break; + } +} +void wxOsmo4Frame::OnNavigateReset(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_NAVIGATION_TYPE, 0); +} +void wxOsmo4Frame::OnUpdateNavigation(wxUpdateUIEvent & event) +{ + u32 ID = event.GetId(); + event.Enable(0); + if (!m_connected) return; + u32 type = gf_term_get_option(m_term, GF_OPT_NAVIGATION_TYPE); + bool enable = type ? 1 : 0; + + u32 mode = gf_term_get_option(m_term, GF_OPT_NAVIGATION); + /*common 2D/3D modes*/ + if (ID==ID_NAVIGATE_NONE) { event.Enable(enable); event.Check(mode ? 0 : 1); } + else if (ID==ID_NAVIGATE_EXAMINE) { event.Enable(enable); event.Check((mode==GF_NAVIGATE_EXAMINE) ? 1 : 0); } + else if (ID==ID_NAVIGATE_SLIDE) { event.Enable(enable); event.Check((mode==GF_NAVIGATE_SLIDE) ? 1 : 0); } + + if (type==GF_NAVIGATE_TYPE_2D) return; + event.Enable(enable); + if (ID==ID_NAVIGATE_WALK) event.Check((mode==GF_NAVIGATE_WALK) ? 1 : 0); + else if (ID==ID_NAVIGATE_FLY) event.Check((mode==GF_NAVIGATE_FLY) ? 1 : 0); + else if (ID==ID_NAVIGATE_PAN) event.Check((mode==GF_NAVIGATE_PAN) ? 1 : 0); + else if (ID==ID_NAVIGATE_ORBIT) event.Check((mode==GF_NAVIGATE_ORBIT) ? 1 : 0); + else if (ID==ID_NAVIGATE_GAME) event.Check((mode==GF_NAVIGATE_GAME) ? 1 : 0); +} + +void wxOsmo4Frame::OnRenderSwitch(wxCommandEvent &WXUNUSED(event)) +{ + const char *opt = gf_cfg_get_key(m_user.config, "Compositor", "ForceOpenGL"); + Bool use_gl = (opt && !stricmp(opt, "yes")) ? 1 : 0; + gf_cfg_set_key(m_user.config, "Compositor", "ForceOpenGL", use_gl ? "no" : "yes"); + + gf_term_set_option(m_term, GF_OPT_USE_OPENGL, !use_gl); + + UpdateRenderSwitch(); +} + +void wxOsmo4Frame::UpdateRenderSwitch() +{ + const char *opt = gf_cfg_get_key(m_user.config, "Compositor", "ForceOpenGL"); + m_pToolBar->RemoveTool(SWITCH_RENDER); + if (opt && !stricmp(opt, "yes")) + m_pToolBar->InsertTool(12, SWITCH_RENDER, *m_pSW2D, wxNullBitmap, FALSE, NULL, wxT("2D Rasterizer")); + else + m_pToolBar->InsertTool(12, SWITCH_RENDER, *m_pSW3D, wxNullBitmap, FALSE, NULL, wxT("OpenGL Rendering")); + +#ifdef WIN32 + /*there's a display bug with the menubtn, remove and reinsert*/ + m_pToolBar->RemoveTool(FILE_PREV); + m_pToolBar->RemoveTool(FILE_NEXT); + m_pToolBar->InsertControl(2, m_pPrevBut); + m_pToolBar->InsertControl(3, m_pNextBut); +#endif + m_pToolBar->Realize(); +} + +void wxOsmo4Frame::UpdatePlay() +{ + m_pToolBar->RemoveTool(FILE_PLAY); + if (m_connected) { + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) + m_pToolBar->InsertTool(5, FILE_PLAY, *m_pPlay, wxNullBitmap, FALSE, NULL, wxT("Pause File")); + else + m_pToolBar->InsertTool(5, FILE_PLAY, *m_pPause, wxNullBitmap, FALSE, NULL, wxT("Play File")); + } else { + m_pToolBar->InsertTool(5, FILE_PLAY, *m_pPlay, wxNullBitmap, FALSE, NULL, wxT("Pause File")); + } + +#ifdef WIN32 + /*there's a display bug with the menubtn, remove and reinsert*/ + m_pToolBar->RemoveTool(FILE_PREV); + m_pToolBar->RemoveTool(FILE_NEXT); + m_pToolBar->InsertControl(2, m_pPrevBut); + m_pToolBar->InsertControl(3, m_pNextBut); +#endif + m_pToolBar->Realize(); +} + +void wxOsmo4Frame::OnCollide(wxCommandEvent & event) +{ + u32 ID = event.GetId(); + if (ID==ID_COLLIDE_NONE) gf_term_set_option(m_term, GF_OPT_COLLISION, GF_COLLISION_NONE); + else if (ID==ID_COLLIDE_REG) gf_term_set_option(m_term, GF_OPT_COLLISION, GF_COLLISION_NORMAL); + else if (ID==ID_COLLIDE_DISP) gf_term_set_option(m_term, GF_OPT_COLLISION, GF_COLLISION_DISPLACEMENT); +} +void wxOsmo4Frame::OnUpdateCollide(wxUpdateUIEvent & event) +{ + u32 ID = event.GetId(); + event.Enable(0); + if (!m_connected) return; + event.Enable(1); + u32 mode = gf_term_get_option(m_term, GF_OPT_COLLISION); + if (ID==ID_COLLIDE_NONE) { event.Check((mode==GF_COLLISION_NONE) ? 1 : 0); } + else if (ID==ID_COLLIDE_REG) { event.Check((mode==GF_COLLISION_NORMAL) ? 1 : 0); } + else if (ID==ID_COLLIDE_DISP) { event.Check((mode==GF_COLLISION_DISPLACEMENT) ? 1 : 0); } +} + +void wxOsmo4Frame::OnHeadlight(wxCommandEvent &WXUNUSED(event)) +{ + Bool val = !gf_term_get_option(m_term, GF_OPT_HEADLIGHT); + gf_term_set_option(m_term, GF_OPT_HEADLIGHT, val); +} +void wxOsmo4Frame::OnUpdateHeadlight(wxUpdateUIEvent & event) +{ + event.Enable(0); + if (!m_connected) return; + u32 type = gf_term_get_option(m_term, GF_OPT_NAVIGATION_TYPE); + if (type!=GF_NAVIGATE_TYPE_3D) return; + + event.Enable(1); + event.Check(gf_term_get_option(m_term, GF_OPT_HEADLIGHT) ? 1 : 0); +} +void wxOsmo4Frame::OnGravity(wxCommandEvent & WXUNUSED(event)) +{ + Bool val = gf_term_get_option(m_term, GF_OPT_GRAVITY) ? 0 : 1; + gf_term_set_option(m_term, GF_OPT_GRAVITY, val); +} +void wxOsmo4Frame::OnUpdateGravity(wxUpdateUIEvent & event) +{ + event.Enable(0); + if (!m_connected) return; + u32 type = gf_term_get_option(m_term, GF_OPT_NAVIGATION_TYPE); + if (type!=GF_NAVIGATE_TYPE_3D) return; + type = gf_term_get_option(m_term, GF_OPT_NAVIGATION); + if (type != GF_NAVIGATE_WALK) return; + event.Enable(1); + event.Check(gf_term_get_option(m_term, GF_OPT_GRAVITY) ? 1 : 0); +} + + +BEGIN_EVENT_TABLE(wxMyComboBox, wxComboBox) + EVT_KEY_UP(wxMyComboBox::OnKeyUp) +END_EVENT_TABLE() + +void wxMyComboBox::OnKeyUp(wxKeyEvent &event) +{ + if (event.GetKeyCode()==WXK_RETURN) { + event.Skip(); + wxCommandEvent evt; + evt.SetEventType(wxEVT_COMMAND_COMBOBOX_SELECTED); + evt.SetEventObject(this); + evt.SetId(GetId()); + GetParent()->AddPendingEvent(evt); + } +} + + +void wxOsmo4Frame::ReloadURLs() +{ + const char *sOpt; + u32 i=0; + + m_Address->Clear(); + while (1) { + sOpt = gf_cfg_get_key_name(m_user.config, "RecentFiles", i); + if (!sOpt) break; + m_Address->Append(wxString(sOpt, wxConvUTF8) ); + i++; + } +} + +void wxOsmo4Frame::SelectionReady() +{ + wxString urlVal = m_Address->GetValue(); + if (urlVal.Find(wxT("://"))>0) { + UpdateLastFiles(m_user.config, urlVal.mb_str(wxConvUTF8)); + ReloadURLs(); + } + m_pPlayList->Truncate(); + m_pPlayList->QueueURL(urlVal); + m_pPlayList->RefreshList(); + m_pPlayList->PlayNext(); +} + +void wxOsmo4Frame::OnURLSelect(wxCommandEvent &WXUNUSED(event)) +{ + SelectionReady(); +} + +void wxOsmo4Frame::OnPlaylist(wxCommandEvent &WXUNUSED(event)) +{ + assert(m_pPlayList); + m_pPlayList->Show(m_pPlayList->IsShown() ? 0 : 1); +} + +void wxOsmo4Frame::OnUpdatePlayList(wxUpdateUIEvent & event) +{ + event.Enable(1); + event.Check(m_pPlayList->IsShown() ? 1 : 0); +} + +void wxOsmo4Frame::OnFilePrevOpen(wxNotifyEvent & event) +{ + u32 count = gf_list_count(m_pPlayList->m_entries); + u32 start = m_pPlayList->m_cur_entry - 1; + wxMenu *popup = new wxMenu(); + + for (u32 i=0; i<10; i++) { + if (start - i < 0) break; + if (start - i >= count) break; + PLEntry *ple = (PLEntry *) gf_list_get(m_pPlayList->m_entries, start - i); + popup->Append(ID_NAV_PREV_0 + i, wxString(ple->m_disp_name, wxConvUTF8) ); + } + m_pPrevBut->AssignMenu(popup); +} + +void wxOsmo4Frame::OnFileNextOpen(wxNotifyEvent & event) +{ + u32 count = gf_list_count(m_pPlayList->m_entries); + wxMenu *popup = new wxMenu(); + u32 start = m_pPlayList->m_cur_entry + 1; + for (u32 i=0; i<10; i++) { + if (start + i >= count) break; + PLEntry *ple = (PLEntry *) gf_list_get(m_pPlayList->m_entries, start + i); + popup->Append(ID_NAV_NEXT_0 + i, wxString(ple->m_disp_name, wxConvUTF8) ); + } + m_pNextBut->AssignMenu(popup); +} + +void wxOsmo4Frame::OnNavPrev(wxCommandEvent &WXUNUSED(event)) +{ + if (m_pPlayList->m_cur_entry<=0) return; + m_pPlayList->PlayPrev(); +} +void wxOsmo4Frame::OnUpdateNavPrev(wxUpdateUIEvent & event) +{ + if (m_pPlayList->m_cur_entry<=0) event.Enable(0); + else event.Enable(TRUE); +} +void wxOsmo4Frame::OnNavPrevMenu(wxCommandEvent &event) +{ + u32 ID = event.GetId() - ID_NAV_PREV_0; + s32 prev = m_pPlayList->m_cur_entry - ID; + if (prev>=0) { + m_pPlayList->m_cur_entry = prev; + m_pPlayList->PlayPrev(); + } +} +void wxOsmo4Frame::OnNavNext(wxCommandEvent &WXUNUSED(event)) +{ + /*don't play if last could trigger playlist loop*/ + if ((m_pPlayList->m_cur_entry<0) || (gf_list_count(m_pPlayList->m_entries) == 1 + (u32) m_pPlayList->m_cur_entry)) return; + m_pPlayList->PlayNext(); +} +void wxOsmo4Frame::OnUpdateNavNext(wxUpdateUIEvent & event) +{ + if (m_pPlayList->m_cur_entry<0) event.Enable(0); + else if ((u32) m_pPlayList->m_cur_entry + 1 == gf_list_count(m_pPlayList->m_entries) ) event.Enable(0); + else event.Enable(1); +} + +void wxOsmo4Frame::OnNavNextMenu(wxCommandEvent &event) +{ + u32 ID = event.GetId() - ID_NAV_NEXT_0; + s32 next = m_pPlayList->m_cur_entry + ID; + if (next < (s32) gf_list_count(m_pPlayList->m_entries) ) { + m_pPlayList->m_cur_entry = next; + m_pPlayList->PlayNext(); + } +} + +void wxOsmo4Frame::OnClearNav(wxCommandEvent &WXUNUSED(event)) +{ + m_pPlayList->ClearButPlaying(); +} + + +void wxOsmo4Frame::BuildStreamList(Bool reset_only) +{ + u32 nb_subs; + wxMenu *pMenu; + + pMenu = sel_menu->FindItemByPosition(0)->GetSubMenu(); + while (pMenu->GetMenuItemCount()) { + wxMenuItem* it = pMenu->FindItemByPosition(0); + pMenu->Delete(it); + } + pMenu = sel_menu->FindItemByPosition(1)->GetSubMenu(); + while (pMenu->GetMenuItemCount()) { + wxMenuItem* it = pMenu->FindItemByPosition(0); + pMenu->Delete(it); + } + pMenu = sel_menu->FindItemByPosition(2)->GetSubMenu(); + while (pMenu->GetMenuItemCount()) { + wxMenuItem* it = pMenu->FindItemByPosition(0); + pMenu->Delete(it); + } + + if (reset_only) { + m_bFirstStreamListBuild = 1; + return; + } + + if (!gf_term_get_option(m_term, GF_OPT_CAN_SELECT_STREAMS)) return; + + nb_subs = 0; + GF_ObjectManager *root_od = gf_term_get_root_object(m_term); + if (!root_od) return; + u32 count = gf_term_get_object_count(m_term, root_od); + + for (u32 i=0; iFindItemByPosition(0)->GetSubMenu(); + if (!info.owns_service) sprintf(szLabel, "Audio #%d", pMenu->GetMenuItemCount() + 1); + pMenu->AppendCheckItem(ID_SELSTREAM_0 +i, wxString(szLabel, wxConvUTF8)); + break; + case GF_STREAM_VISUAL: + pMenu = sel_menu->FindItemByPosition(1)->GetSubMenu(); + if (!info.owns_service) sprintf(szLabel, "Video #%d", pMenu->GetMenuItemCount() + 1); + pMenu->AppendCheckItem(ID_SELSTREAM_0 +i, wxString(szLabel, wxConvUTF8)); + break; + case GF_STREAM_TEXT: + nb_subs ++; + pMenu = sel_menu->FindItemByPosition(2)->GetSubMenu(); + if (!info.owns_service) sprintf(szLabel, "Subtitle #%d", pMenu->GetMenuItemCount() + 1); + pMenu->AppendCheckItem(ID_SELSTREAM_0 +i, wxString(szLabel, wxConvUTF8)); + break; + } + } + if (m_bFirstStreamListBuild) { + m_bFirstStreamListBuild = 0; + if (!nb_subs && m_lookforsubs) LookForSubtitles(); + } +} + +void wxOsmo4Frame::OnStreamSel(wxCommandEvent & event) +{ + GF_ObjectManager *root_od = gf_term_get_root_object(m_term); + if (!root_od) return; + u32 ID = event.GetId() - ID_SELSTREAM_0; + GF_ObjectManager *odm = gf_term_get_object(m_term, root_od, ID); + gf_term_select_object(m_term, odm); +} + +void wxOsmo4Frame::OnUpdateStreamSel(wxUpdateUIEvent & event) +{ + GF_ObjectManager *root_od = gf_term_get_root_object(m_term); + if (!root_od) return; + u32 ID = event.GetId() - ID_SELSTREAM_0; + + GF_ObjectManager *odm = gf_term_get_object(m_term, root_od, ID); + if (!odm) return; + + ODInfo info; + gf_term_get_object_info(m_term, odm, &info); + event.Enable(1); + event.Check(info.status ? 1 : 0); +} + +void wxOsmo4Frame::OnUpdateStreamMenu(wxUpdateUIEvent & event) +{ + if (!m_connected || !gf_term_get_option(m_term, GF_OPT_CAN_SELECT_STREAMS)) { + event.Enable(0); + } else { + event.Enable(1); + } +} + +void wxOsmo4Frame::OnAddSub(wxCommandEvent &WXUNUSED(event)) +{ + wxFileDialog dlg(this, wxT("Add Subtitle"), wxT(""), wxT(""), wxT("All Subtitles|*.srt;*.ttxt|SRT Subtitles|*.srt|3GPP TimedText|*.ttxt|"), wxOPEN | wxCHANGE_DIR /* | wxHIDE_READONLY*/); + + if (dlg.ShowModal() == wxID_OK) { + AddSubtitle(dlg.GetPath().mb_str(wxConvUTF8), 1); + } + +} + +void wxOsmo4Frame::AddSubtitle(const char *fileName, Bool auto_play) +{ + gf_term_add_object(m_term, fileName, auto_play); +} + +static Bool subs_enum_dir_item(void *cbck, char *item_name, char *item_path) +{ + wxOsmo4Frame *_this = (wxOsmo4Frame*)cbck; + _this->AddSubtitle(item_path, 0); + return 0; +} + +void wxOsmo4Frame::LookForSubtitles() +{ + char dir[GF_MAX_PATH]; + const char *url = m_pPlayList->GetURL().mb_str(wxConvUTF8); + strcpy(dir, url); + char *sep = strrchr(dir, '\\'); + if (!sep) strcpy(dir, ::wxGetCwd().mb_str(wxConvUTF8)); + else sep[0] = 0; + + gf_enum_directory(dir, 0, subs_enum_dir_item, this, "ttxt;srt"); +} + +void wxOsmo4Frame::OnCacheEnable(wxCommandEvent &WXUNUSED(event)) +{ + u32 state = gf_term_get_option(m_term, GF_OPT_MEDIA_CACHE); + if (state==GF_MEDIA_CACHE_DISABLED) { + gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_ENABLED); + } else if (state==GF_MEDIA_CACHE_DISABLED) { + gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISABLED); + } +} + +void wxOsmo4Frame::OnCacheStop(wxCommandEvent &WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISABLED); +} + +void wxOsmo4Frame::OnCacheAbort(wxCommandEvent &WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISCARD); +} + +void wxOsmo4Frame::OnUpdateCacheEnable(wxUpdateUIEvent & event) +{ + u32 state = gf_term_get_option(m_term, GF_OPT_MEDIA_CACHE); + switch (state) { + case GF_MEDIA_CACHE_ENABLED: + event.Enable(1); + event.SetText(wxT("Enabled")); + break; + case GF_MEDIA_CACHE_RUNNING: + event.SetText(wxT("Running")); + event.Enable(0); + break; + case GF_MEDIA_CACHE_DISABLED: + event.SetText(wxT("Disabled")); + break; + } +} + +void wxOsmo4Frame::OnUpdateCacheAbort(wxUpdateUIEvent & event) +{ + u32 state = gf_term_get_option(m_term, GF_OPT_MEDIA_CACHE); + event.Enable( (state==GF_MEDIA_CACHE_RUNNING) ? 1 : 0); +} + + + +void wxOsmo4Frame::BuildChapterList(Bool reset_only) +{ + ODInfo odi; + + while (chap_menu->GetMenuItemCount()) { + wxMenuItem* it = chap_menu->FindItemByPosition(0); + chap_menu->Delete(it); + } + if (m_chapters_start) free(m_chapters_start); + m_chapters_start = NULL; + m_num_chapters = 0; + if (reset_only) return; + + GF_ObjectManager *root_od = gf_term_get_root_object(m_term); + if (!root_od) return; + if (gf_term_get_object_info(m_term, root_od, &odi) != GF_OK) return; + + u32 count = gf_list_count(odi.od->OCIDescriptors); + m_num_chapters = 0; + for (u32 i=0; iOCIDescriptors, i); + if (seg->tag != GF_ODF_SEGMENT_TAG) continue; + + if (seg->SegmentName && strlen((const char *)seg->SegmentName)) { + strcpy(szLabel, (const char *) seg->SegmentName); + } else { + sprintf(szLabel, "Chapter %02d", m_num_chapters+1); + } + chap_menu->AppendCheckItem(ID_SETCHAP_FIRST + m_num_chapters, wxString(szLabel, wxConvUTF8)); + + m_chapters_start = (Double *) realloc(m_chapters_start, sizeof(Double)*(m_num_chapters+1)); + m_chapters_start[m_num_chapters] = seg->startTime; + m_num_chapters++; + } + + /*get any service info*/ + NetInfoCommand com; + if (!m_bStartupFile && gf_term_get_service_info(m_term, root_od, &com) == GF_OK) { + wxString title = wxT(""); + if (com.track_info) { title.Format(wxT("%02d "), (u32) (com.track_info>>16) ); } + if (com.artist) { title.Append(wxString(com.artist, wxConvUTF8)); title += wxT(" "); } + if (com.name) { title.Append(wxString(com.name, wxConvUTF8)); title += wxT(" "); } + if (com.album) { title += wxT("("); title.Append(wxString(com.album, wxConvUTF8)); title += wxT(")"); } + + if (title.length()) SetTitle(title); + } + +} + +void wxOsmo4Frame::OnChapterSel(wxCommandEvent & event) +{ + GF_ObjectManager *root_od = gf_term_get_root_object(m_term); + if (!root_od) return; + u32 ID = event.GetId() - ID_SETCHAP_FIRST; + gf_term_play_from_time(m_term, (u32) (1000*m_chapters_start[ID]), 0); +} + +void wxOsmo4Frame::OnUpdateChapterSel(wxUpdateUIEvent & event) +{ + Double now; + Bool is_current; + u32 ID = event.GetId() - ID_SETCHAP_FIRST; + + now = gf_term_get_time_in_ms(m_term); + now /= 1000; + + is_current = 0; + if (m_chapters_start[ID]<=now) { + if (ID+1now) is_current = 1; + } else { + is_current = 1; + } + } + event.Enable(1); + event.Check(is_current ? 1 : 0); +} + +void wxOsmo4Frame::OnUpdateChapterMenu(wxUpdateUIEvent & event) +{ + if (!m_connected || !m_num_chapters) { + event.Enable(0); + } else { + event.Enable(1); + } +} + +void wxOsmo4Frame::OnFileCopy(wxCommandEvent &event) +{ + const char *text = gf_term_get_text_selection(m_term, 0); + if (!text) return; + if (!wxTheClipboard->Open()) return; + + wxTheClipboard->SetData( new wxTextDataObject( wxString(text, wxConvUTF8)) ); + wxTheClipboard->Close(); +} + +void wxOsmo4Frame::OnUpdateFileCopy(wxUpdateUIEvent &event) +{ + if (gf_term_get_text_selection(m_term, 1)!=NULL) { + event.Enable(1); + } else { + event.Enable(0); + } +} + +void wxOsmo4Frame::OnFilePaste(wxCommandEvent &event) +{ + if (!wxTheClipboard->Open()) return; + if (wxTheClipboard->IsSupported( wxDF_TEXT )) { + wxTextDataObject data; + wxTheClipboard->GetData(data); + gf_term_paste_text(m_term, data.GetText().mb_str(wxConvUTF8), 0); + } + wxTheClipboard->Close(); +} + +void wxOsmo4Frame::OnUpdateFilePaste(wxUpdateUIEvent &event) +{ + Bool ok = 0; + if (wxTheClipboard->Open()) { + if (wxTheClipboard->IsSupported( wxDF_TEXT )) { + if (gf_term_paste_text(m_term, NULL, 1)==GF_OK) { + ok = 1; + } + } + wxTheClipboard->Close(); + } + event.Enable(ok ? 1 : 0); +} + diff --git a/applications/osmo4_wx/wxOsmo4.h b/applications/osmo4_wx/wxOsmo4.h new file mode 100644 index 0000000..68c35f2 --- /dev/null +++ b/applications/osmo4_wx/wxOsmo4.h @@ -0,0 +1,389 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#ifndef _WXOSMO4_H +#define _WXOSMO4_H + +/* +we need to force X to work in sync mode when we use embedded view... +include first to avoid Bool type redef between X11 and gpac +*/ +#ifdef __WXGTK__ +#include +#endif + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +#include +#include +#include +#include "menubtn.h" + +/*include gpac AFTER wx in case we override malloc/realloc/free for mem tracking*/ +#include +#include + +class wxOsmo4App : public wxApp +{ +public: + virtual bool OnInit(); +}; + +DECLARE_APP(wxOsmo4App) + +class wxOsmo4Frame; +class wxPlaylist; + +class GPACLogs : public wxLogWindow { +public: + GPACLogs(wxFrame *parent) : wxLogWindow(parent, wxT("GPAC Logs"), FALSE, FALSE) { + m_pMain = (wxOsmo4Frame *) parent; + } + virtual bool OnFrameClose(wxFrame *frame); + +private: + wxOsmo4Frame *m_pMain; +}; + +#define MAX_VIEWPOINTS 50 + +enum { + // Menu commands + FILE_OPEN = wxID_HIGHEST+1, + FILE_OPEN_URL, + FILE_RELOAD, + FILE_RELOAD_CONFIG, + FILE_PLAY, + FILE_STEP, + FILE_STOP, + FILE_PREV, + FILE_NEXT, + FILE_PROPERTIES, + FILE_COPY, + FILE_PASTE, + TERM_RELOAD, + FILE_QUIT, + VIEW_FULLSCREEN, + VIEW_ORIGINAL, + VIEW_AR_KEEP, + VIEW_AR_FILL, + VIEW_AR_43, + VIEW_AR_169, + VIEW_OPTIONS, + VIEW_LOGS, + VIEW_RTI, + VIEW_PLAYLIST, + SWITCH_RENDER, + APP_SHORTCUTS, + APP_NAV_KEYS, + APP_ABOUT, + ID_ADDRESS, + ID_URL_GO, + ID_ABOUT_CLOSE, + ID_CLEAR_NAV, + ID_STREAM_MENU, + ID_CHAPTER_MENU, + ID_ADD_SUB, + + ID_MCACHE_ENABLE, + ID_MCACHE_STOP, + ID_MCACHE_ABORT, + + ID_CTRL_TIMER, + ID_SLIDER, + + ID_TREE_VIEW, + ID_OD_TIMER, + ID_VIEW_SG, + ID_VIEW_WI, + ID_VIEW_SEL, + + + ID_HEADLIGHT, + ID_NAVIGATE_NONE, + ID_NAVIGATE_WALK, + ID_NAVIGATE_FLY, + ID_NAVIGATE_EXAMINE, + ID_NAVIGATE_SLIDE, + ID_NAVIGATE_PAN, + ID_NAVIGATE_ORBIT, + ID_NAVIGATE_GAME, + ID_NAVIGATE_RESET, + + ID_COLLIDE_NONE, + ID_COLLIDE_REG, + ID_COLLIDE_DISP, + ID_GRAVITY, + + ID_PL_OPEN, + ID_PL_SAVE, + ID_PL_ADD_FILE, + ID_PL_ADD_URL, + ID_PL_ADD_DIR, + ID_PL_ADD_DIR_REC, + ID_PL_REM_FILE, + ID_PL_REM_ALL, + ID_PL_REM_DEAD, + ID_PL_UP, + ID_PL_DOWN, + ID_PL_RANDOMIZE, + ID_PL_REVERSE, + ID_PL_SEL_REV, + ID_PL_SORT_TITLE, + ID_PL_SORT_FILE, + ID_PL_SORT_DUR, + ID_PL_PLAY, + + + /*reserve IDs for viewpoint menu*/ + ID_VIEWPOINT_FIRST, + ID_VIEWPOINT_LAST = ID_VIEWPOINT_FIRST + MAX_VIEWPOINTS, + + /*reserve IDs for navigation menus*/ + ID_NAV_PREV_0, + ID_NAV_PREV_9 = ID_NAV_PREV_0 + 10, + ID_NAV_NEXT_0, + ID_NAV_NEXT_9 = ID_NAV_NEXT_0 + 10, + /*reserve IDs for stream selection menus*/ + ID_SELSTREAM_0, + ID_SELSTREAM_9 = ID_SELSTREAM_0 + 10, + + /*reserve IDs for chapter selection menus*/ + ID_SETCHAP_FIRST, + ID_SETCHAP_LAST = ID_SELSTREAM_0 + 200, +}; + +wxString get_pref_browser(GF_Config *cfg); + +class wxGPACEvent : public wxEvent +{ +public: + wxGPACEvent( wxWindow* win = (wxWindow*) NULL ); + void CopyObject( wxObject& obj ) const; + virtual wxEvent *Clone() const; + + wxString to_url; + GF_Event gpac_evt; + + DECLARE_DYNAMIC_CLASS(wxGPACEvent) +}; +typedef void (wxEvtHandler::*GPACEventFunction)(wxGPACEvent&); +DEFINE_EVENT_TYPE(GPAC_EVENT) + +#define EVT_GPACEVENT(func) DECLARE_EVENT_TABLE_ENTRY(GPAC_EVENT, -1, -1, (wxObjectEventFunction) (wxEventFunction) (GPACEventFunction) & func, (wxObject*) NULL), + +class OpenURLDlg : public wxDialog { +public: + OpenURLDlg(wxWindow *parent, GF_Config *cfg); + wxString m_urlVal; +private: + wxButton *m_go; + wxComboBox *m_url; + GF_Config *m_cfg; + void OnGo(wxCommandEvent& event); + DECLARE_EVENT_TABLE() +}; + +class wxMyComboBox : public wxComboBox +{ +public: + wxMyComboBox(wxWindow* parent, wxWindowID id, const wxString& value = wxT(""), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize) + : wxComboBox(parent, id, value, pos, size, 0, NULL, wxCB_DROPDOWN) + {} + +private: + DECLARE_EVENT_TABLE() + + void OnKeyUp(wxKeyEvent &event); +}; + +class wxOsmo4Frame : public wxFrame { +public: + wxOsmo4Frame(); + virtual ~wxOsmo4Frame(); + + char szAppPath[GF_MAX_PATH]; + + u32 m_duration; + wxString the_next_url; + GF_Terminal *m_term; + GF_User m_user; + Bool m_connected, m_can_seek, m_console_off, m_loop, m_lookforsubs; + + void DoConnect(); + + void ConnectAcknowledged(Bool bOk); + void SetStatus(wxString str); + + void OnFilePlay(wxCommandEvent &event); + void OnFileStep(wxCommandEvent &event); + void OnFileStop(wxCommandEvent &event); + wxString GetFileFilter(); + + void BuildViewList(); + void BuildStreamList(Bool reset_only); + void BuildChapterList(Bool reset_only); + + void AddSubtitle(const char *fileName, Bool auto_play); + + wxWindow *m_pView; + +#ifdef __WXGTK__ + u32 m_last_grab_time, m_last_grab_pos; + wxWindow *m_pVisual; +#endif + wxSlider *m_pProg; + wxPlaylist *m_pPlayList; + + void DoLayout(u32 v_width = 0, u32 v_height = 0); + s32 m_last_prog; + + FILE *m_logs; + u32 m_log_level, m_log_tools; + u32 m_LastStatusTime; + +protected: + +private: + DECLARE_EVENT_TABLE() + + void OnCloseApp(wxCloseEvent &event); + void OnSize(wxSizeEvent &event); + + void OnFileOpen(wxCommandEvent &event); + void OnFileOpenURL(wxCommandEvent &event); + void OnFileReload(wxCommandEvent &event); + void OnFileReloadConfig(wxCommandEvent & event); + void OnFileProperties(wxCommandEvent &event); + void OnFileQuit(wxCommandEvent &event); + void OnFullScreen(wxCommandEvent &event); + void OnOptions(wxCommandEvent &event); + void OnViewARKeep(wxCommandEvent &event); + void OnViewARFill(wxCommandEvent &event); + void OnViewAR169(wxCommandEvent &event); + void OnViewAR43(wxCommandEvent &event); + void OnViewOriginal(wxCommandEvent &event); + void OnPlaylist(wxCommandEvent &event); + void OnShortcuts(wxCommandEvent &event); + void OnNavInfo(wxCommandEvent &event); + void OnAddSub(wxCommandEvent &event); + void OnAbout(wxCommandEvent &event); + Bool LoadTerminal(); + void OnGPACEvent(wxGPACEvent &event); + void OnTimer(wxTimerEvent& event); + void OnSlide(wxScrollEvent &event); + void OnRelease(wxScrollEvent &event); + void OnLogs(wxCommandEvent & event); + void OnRTI(wxCommandEvent & event); + void OnUpdatePlay(wxUpdateUIEvent &event); + void OnUpdateNeedsConnect(wxUpdateUIEvent &event); + void OnUpdateFullScreen(wxUpdateUIEvent &event); + void OnUpdateAR(wxUpdateUIEvent &event); + void OnViewport(wxCommandEvent & event); + void OnUpdateViewport(wxUpdateUIEvent & event); + void OnNavigate(wxCommandEvent & event); + void OnNavigateReset(wxCommandEvent & event); + void OnUpdateNavigation(wxUpdateUIEvent & event); + void OnRenderSwitch(wxCommandEvent &event); + void OnCollide(wxCommandEvent & event); + void OnUpdateCollide(wxUpdateUIEvent & event); + void OnHeadlight(wxCommandEvent & event); + void OnUpdateHeadlight(wxUpdateUIEvent & event); + void OnGravity(wxCommandEvent & event); + void OnUpdateGravity(wxUpdateUIEvent & event); + void OnURLSelect(wxCommandEvent &event); + void OnUpdatePlayList(wxUpdateUIEvent & event); + void OnFilePrevOpen(wxNotifyEvent & event); + void OnFileNextOpen(wxNotifyEvent & event); + void OnNavPrev(wxCommandEvent &event); + void OnUpdateNavPrev(wxUpdateUIEvent & event); + void OnNavPrevMenu(wxCommandEvent &event); + void OnNavNext(wxCommandEvent &event); + void OnUpdateNavNext(wxUpdateUIEvent & event); + void OnNavNextMenu(wxCommandEvent &event); + void OnClearNav(wxCommandEvent &event); + void OnStreamSel(wxCommandEvent &event); + void OnUpdateStreamSel(wxUpdateUIEvent & event); + void OnUpdateStreamMenu(wxUpdateUIEvent & event); + void OnChapterSel(wxCommandEvent &event); + void OnUpdateChapterSel(wxUpdateUIEvent & event); + void OnUpdateChapterMenu(wxUpdateUIEvent & event); + + void SelectionReady(); + void ReloadURLs(); + void LookForSubtitles(); + + void OnCacheEnable(wxCommandEvent &event); + void OnCacheStop(wxCommandEvent &event); + void OnCacheAbort(wxCommandEvent &event); + void OnUpdateCacheEnable(wxUpdateUIEvent & event); + void OnUpdateCacheAbort(wxUpdateUIEvent & event); + + void OnFileCopy(wxCommandEvent &event); + void OnUpdateFileCopy(wxUpdateUIEvent &event); + void OnFilePaste(wxCommandEvent &event); + void OnUpdateFilePaste(wxUpdateUIEvent &event); + + + void CheckVideoOut(); + + wxMenuBar* m_pMenubar; + wxStatusBar* m_pStatusbar; + wxTimer *m_pTimer; + GPACLogs *m_pLogs; + wxBoxSizer *m_pAddBar; + + Bool m_bGrabbed, m_bToReset, m_bFirstStreamListBuild; + wxBitmap *m_pOpenFile, *m_pPrev, *m_pNext, *m_pPlay, *m_pPause, *m_pStep, *m_pStop, *m_pInfo, *m_pConfig, *m_pSW2D, *m_pSW3D; + wxMenuButton *m_pPrevBut, *m_pNextBut; + wxToolBar *m_pToolBar; + wxMyComboBox *m_Address; + + wxMenu *vp_list; + wxMenu *sel_menu; + wxMenu *chap_menu; + void Stop(); + + s32 nb_viewpoints; + + void UpdateRenderSwitch(); + void UpdatePlay(); + + u32 m_orig_width, m_orig_height; + + u32 m_num_chapters; + Double *m_chapters_start; + Bool m_bExternalView, m_bViewRTI, m_bStartupFile; + + void ShowViewWindow(Bool do_show); +}; + + +#endif + diff --git a/applications/osmo4_wx/wxOsmo4.rc b/applications/osmo4_wx/wxOsmo4.rc new file mode 100644 index 0000000..e7d6408 --- /dev/null +++ b/applications/osmo4_wx/wxOsmo4.rc @@ -0,0 +1,72 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON DISCARDABLE "osmo4.ico" +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/applications/osmophone/Osmo4.ico b/applications/osmophone/Osmo4.ico new file mode 100644 index 0000000000000000000000000000000000000000..d544ffce61cc278ca5f2b433f70dc50a3c3435b4 GIT binary patch literal 1078 zcma)5J8s)R5Pd5f;IOfFJ8oqH0Rmkim3*WCIe>>yA*xB1od^WTRVXM2p}LeR9Y8Si zW=W@V;gM!`-u(ANz+r{D_AvUh0zPx~e#RRIJlp{r+G_F>+2NiNP{zJbN+&(4^Fm1# z!!U3)$CUq=Cez6LnaPAvYeGz9YRi`N-yqTiQl(48p@}7sT*#%NU4)|_uF3%tN@sfs z=yE{gGo&F|z#*WT+emb(LODZ7(=(v!=Yyb8Vh|0TE`Dfa#n>6-c-(H&sYGikZL7qt z_Yyrdj&E4XO8Nyefx*N!RNf!hyE^ttPmUMEj3=J!wT4-b8N#I5A3YV2Z1$4 +#include +/*for initial setup*/ +#include + +#include +#include +#include +#include +#include "resource.h" + +#include + +#define WM_LOADTERM WM_USER + 1 +#define STATE_TIMER_ID 20 +#define STATE_TIMER_DUR 1000 +#define GPAC_TIMER_ID 21 +#define GPAC_TIMER_DUR 33 + + +Bool gf_file_dialog(HINSTANCE inst, HWND parent, char *url, const char *ext_list, GF_Config *cfg); +void set_backlight_state(Bool disable); +void refresh_recent_files(); +void do_layout(Bool notif_size); + +static HWND g_hwnd = NULL; +static HWND g_hwnd_disp = NULL; +static HWND g_hwnd_menu1 = NULL; +static HWND g_hwnd_menu2 = NULL; +static HWND g_hwnd_status = NULL; +static HINSTANCE g_hinst = NULL; +static Bool is_ppc = 0; + +static Bool is_connected = 0; +static Bool navigation_on = 0; +static Bool playlist_navigation_on = 1; + +static u32 Duration; +static Bool CanSeek = 0; +static u32 Volume=100; +static char the_url[GF_MAX_PATH] = ""; +static Bool NavigateTo = 0; +static char the_next_url[GF_MAX_PATH]; +static GF_Terminal *term; +static GF_User user; +static u32 disp_w = 0; +static u32 disp_h = 0; +static u32 screen_w = 0; +static u32 screen_h = 0; +static u32 menu_h = 0; +static u32 caption_h = 0; +static Bool backlight_off = 0; +static u32 prev_batt_bl, prev_ac_bl; +static Bool show_status = 1; +static Bool reset_status = 1; +static u32 last_state_time = 0; +static Bool loop = 0; +static Bool full_screen = 0; +static Bool force_2d_gl = 0; +static Bool ctrl_mod_down = 0; +static Bool view_cpu = 0; +static Bool menu_switched = 0; +static Bool use_low_fps = 0; +static Bool use_svg_prog = 0; + +static Bool log_rti = 0; +static FILE *rti_file = NULL; +static u32 rti_update_time_ms = 200; + +static u32 playlist_act = 0; + +void set_status(char *state) +{ + if (show_status && g_hwnd_status) { + TCHAR wstate[1024]; + CE_CharToWide(state, (u16 *) wstate); + SendMessage(g_hwnd_status, WM_SETTEXT, 0, (LPARAM) wstate); + last_state_time = GetTickCount(); + } +} + +void update_state_info() +{ + TCHAR wstate[1024]; + Double FPS; + u32 time, m, s; + if (!show_status) return; + if (last_state_time) { + if (GetTickCount() > last_state_time + 1000) { + last_state_time = 0; + reset_status = 1; + } + else return; + } + if (!term) return; + if (!is_connected && reset_status) { + SendMessage(g_hwnd_status, WM_SETTEXT, 0, (LPARAM) TEXT("Ready") ); + reset_status = 0; + return; + } + + FPS = gf_term_get_framerate(term, 0); + time = gf_term_get_time_in_ms(term) / 1000; + m = time/60; + s = time - m*60; + if (view_cpu) { + GF_SystemRTInfo rti; + if (!gf_sys_get_rti(STATE_TIMER_DUR, &rti, 0)) return; + wsprintf(wstate, TEXT("T %02d:%02d : FPS %02.2f : CPU %02d"), m, s, FPS, rti.total_cpu_usage); + } else { + wsprintf(wstate, TEXT("T %02d:%02d : FPS %02.2f"), m, s, FPS); + } + SendMessage(g_hwnd_status, WM_SETTEXT, 0, (LPARAM) wstate); +} + + +static u32 prev_pos = 0; +void cbk_on_progress(void *_title, u32 done, u32 total) +{ +#if 0 + char szMsg[1024]; + u32 pos = (u32) ((u64) done * 100)/total; + if (pos 10) gf_cfg_set_key(user.config, "RecentFiles", gf_cfg_get_key_name(user.config, "RecentFiles", count-1), NULL); + + setup_logs(); + + gf_term_connect(term, the_url); +} + +void switch_playlist(Bool play_prev) +{ + char szPLE[20]; + u32 idx = 0; + u32 count; + const char *ple = gf_cfg_get_key(user.config, "General", "PLEntry"); + if (ple) idx = atoi(ple); + + count = gf_cfg_get_key_count(user.config, "Playlist"); + if (!count) return; + /*not the first launch*/ + if (strlen(the_url)) { + if (!idx && play_prev) return; + if (play_prev) idx--; + else idx++; + if (idx>=count) return; + } else { + if (idx>=count) idx=0; + } + + ple = gf_cfg_get_key_name(user.config, "Playlist", idx); + if (!ple) return; + + sprintf(szPLE, "%d", idx); + gf_cfg_set_key(user.config, "General", "PLEntry", szPLE); + + strcpy(the_url, ple); + do_open_file(); +} + + +Bool GPAC_EventProc(void *ptr, GF_Event *evt) +{ + switch (evt->type) { + case GF_EVENT_DURATION: + Duration = (u32) (evt->duration.duration*1000); + CanSeek = evt->duration.can_seek; + break; + case GF_EVENT_MESSAGE: + { + if (!evt->message.message) return 0; + set_status((char *) evt->message.message); + } + break; + case GF_EVENT_PROGRESS: + { + char *szTitle = ""; + if (evt->progress.progress_type==0) szTitle = "Buffer "; + else if (evt->progress.progress_type==1) szTitle = "Download "; + else if (evt->progress.progress_type==2) szTitle = "Import "; + cbk_on_progress(szTitle, evt->progress.done, evt->progress.total); + } + break; + + case GF_EVENT_SIZE: + break; + case GF_EVENT_SCENE_SIZE: + do_layout(1); + break; + case GF_EVENT_CONNECT: + if (evt->connect.is_connected) { + is_connected = 1; + if (!backlight_off) set_backlight_state(1); + refresh_recent_files(); + navigation_on = (gf_term_get_option(term, GF_OPT_NAVIGATION)==GF_NAVIGATE_NONE) ? 0 : 1; + } else { + navigation_on = 0; + is_connected = 0; + Duration = 0; + } + break; + case GF_EVENT_QUIT: + break; + case GF_EVENT_KEYDOWN: + switch (evt->key.key_code) { + case GF_KEY_ENTER: + if (full_screen) set_full_screen(); + break; + case GF_KEY_1: + ctrl_mod_down = !ctrl_mod_down; + evt->key.key_code = GF_KEY_CONTROL; + evt->type = ctrl_mod_down ? GF_EVENT_KEYDOWN : GF_EVENT_KEYUP; + gf_term_user_event(term, evt); + break; + case GF_KEY_MEDIAPREVIOUSTRACK: + playlist_act = 2; + break; + case GF_KEY_MEDIANEXTTRACK: + playlist_act = 1; + break; + } + break; + case GF_EVENT_NAVIGATE: + if (gf_term_is_supported_url(term, evt->navigate.to_url, 1, 1)) { + gf_term_navigate_to(term, evt->navigate.to_url); + return 1; + } else { + u16 dst[1024]; + SHELLEXECUTEINFO info; + +/* + if (full_screen) gf_term_set_option(term, GF_OPT_FULLSCREEN, 0); + full_screen = 0; +*/ + memset(&info, 0, sizeof(SHELLEXECUTEINFO)); + info.cbSize = sizeof(SHELLEXECUTEINFO); + info.lpVerb = L"open"; + info.fMask = SEE_MASK_NOCLOSEPROCESS; + info.lpFile = L"iexplore"; + CE_CharToWide((char *) evt->navigate.to_url, dst); + info.lpParameters = (LPCTSTR) dst; + info.nShow = SW_SHOWNORMAL; + ShellExecuteEx(&info); + } + return 1; + } + return 0; +} + +#define TERM_NOT_THREADED + +Bool LoadTerminal() +{ + /*by default use current dir*/ + strcpy(the_url, "."); + + setup_logs(); + + g_hwnd_disp = CreateWindow(TEXT("STATIC"), NULL, WS_CHILD | WS_VISIBLE , 0, 0, disp_w, disp_h, g_hwnd, NULL, g_hinst, NULL); + + user.EventProc = GPAC_EventProc; + /*dummy in this case (global vars) but MUST be non-NULL*/ + user.opaque = user.modules; + user.os_window_handler = g_hwnd_disp; +#ifdef TERM_NOT_THREADED + user.init_flags = GF_TERM_NO_VISUAL_THREAD | GF_TERM_NO_REGULATION; +#endif + + term = gf_term_new(&user); + if (!term) { + gf_modules_del(user.modules); + gf_cfg_del(user.config); + memset(&user, 0, sizeof(GF_User)); + return 0; + } + +#ifdef TERM_NOT_THREADED + ::SetTimer(g_hwnd, GPAC_TIMER_ID, GPAC_TIMER_DUR, NULL); +#endif + + const char *str = gf_cfg_get_key(user.config, "General", "StartupFile"); + if (str) { + do_layout(1); + strcpy(the_url, str); + gf_term_connect(term, str); + } + return 1; +} + +void do_layout(Bool notif_size) +{ + u32 w, h; + if (full_screen) { + w = screen_w; + h = screen_h; + ::ShowWindow(g_hwnd_status, SW_HIDE); + ::ShowWindow(g_hwnd_menu1, SW_HIDE); + ::ShowWindow(g_hwnd_menu2, SW_HIDE); + + SHFullScreen(g_hwnd, SHFS_HIDESIPBUTTON); + ::MoveWindow(g_hwnd, 0, 0, screen_w, screen_h, 1); + ::MoveWindow(g_hwnd_disp, 0, 0, screen_w, screen_h, 1); + SetForegroundWindow(g_hwnd_disp); + } else { + ::ShowWindow(g_hwnd_menu1, menu_switched ? SW_HIDE : SW_SHOW); + ::ShowWindow(g_hwnd_menu2, menu_switched ? SW_SHOW : SW_HIDE); + if (show_status) { + ::MoveWindow(g_hwnd, 0, 0, disp_w, disp_h, 1); + ::ShowWindow(g_hwnd_status, SW_SHOW); + ::MoveWindow(g_hwnd_status, 0, 0, disp_w, caption_h, 1); + ::MoveWindow(g_hwnd_disp, 0, caption_h, disp_w, disp_h - caption_h, 1); + w = disp_w; + h = disp_h - caption_h; + } else { + ::ShowWindow(g_hwnd_status, SW_HIDE); + ::MoveWindow(g_hwnd, 0, caption_h, disp_w, disp_h, 1); + ::MoveWindow(g_hwnd_disp, 0, 0, disp_w, disp_h, 1); + w = disp_w; + h = disp_h; + } + } + if (notif_size && term) gf_term_set_size(term, w, h); +} + + +void set_backlight_state(Bool disable) +{ + HKEY hKey = 0; + DWORD dwSize; + DWORD dwValue; + HANDLE hBL; + + + if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("ControlPanel\\Backlight"), 0, 0, &hKey ) != ERROR_SUCCESS) return; + + if (disable) { + dwSize = 4; + RegQueryValueEx(hKey, _T("BatteryTimeout"), NULL, NULL,(unsigned char*) &prev_batt_bl, &dwSize); + dwSize = 4; + RegQueryValueEx(hKey, _T("ACTimeout"), NULL, NULL, (unsigned char*) &prev_ac_bl,&dwSize); + dwSize = 4; + dwValue = 0xefff ; + RegSetValueEx(hKey, _T("BatteryTimeout"), NULL, REG_DWORD, (unsigned char *)&dwValue, dwSize); + dwSize = 4; + dwValue = 0xefff ; + RegSetValueEx( hKey, _T("ACTimeout"), NULL, REG_DWORD, (unsigned char *)&dwValue, dwSize); + backlight_off = 1; + } else { + if (prev_batt_bl) { + dwSize = 4; + RegSetValueEx(hKey, _T("BatteryTimeout"), NULL, REG_DWORD, (unsigned char *)&prev_batt_bl, dwSize); + } + if (prev_ac_bl) { + dwSize = 4; + RegSetValueEx(hKey, _T("ACTimeout"), NULL, REG_DWORD,(unsigned char *)&prev_ac_bl, dwSize); + } + backlight_off = 0; + } + RegCloseKey(hKey); + hBL = CreateEvent(NULL, FALSE, FALSE, _T("BackLightChangeEvent")); + if (hBL) { + SetEvent(hBL); + CloseHandle(hBL); + } +} + +static Bool do_resume = 0; +static Bool prev_backlight_state; +void freeze_display(Bool do_freeze) +{ + if (do_freeze) { + prev_backlight_state = backlight_off; + do_resume = 0; + if (0 && is_connected && gf_term_get_option(term, GF_OPT_PLAY_STATE)==GF_STATE_PLAYING) { + do_resume= 1; + gf_term_set_option(term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + } + /*freeze display*/ + gf_term_set_option(term, GF_OPT_FREEZE_DISPLAY, 1); + + set_backlight_state(0); + gf_sleep(100); + } else { + if (prev_backlight_state) set_backlight_state(1); + gf_term_set_option(term, GF_OPT_FREEZE_DISPLAY, 0); + + if (do_resume) { + gf_term_set_option(term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + set_backlight_state(1); + } + } +} + +static void show_taskbar(Bool show_it) +{ + HWND wnd; + if (!is_ppc) return; + + wnd = GetForegroundWindow(); + wnd = g_hwnd; + if (show_it) { + SHFullScreen(wnd, SHFS_SHOWSTARTICON | SHFS_SHOWTASKBAR| SHFS_SHOWSIPBUTTON); + ::ShowWindow(::FindWindow(_T("HHTaskbar"),NULL), SW_SHOWNA); + } else { + ::ShowWindow(::FindWindow(_T("HHTaskbar"),NULL), SW_HIDE); + SHFullScreen(wnd, SHFS_HIDESTARTICON | SHFS_HIDETASKBAR| SHFS_HIDESIPBUTTON); + } +} + +void refresh_recent_files() +{ + u32 count = gf_cfg_get_key_count(user.config, "RecentFiles"); + + HMENU hMenu = (HMENU)SendMessage(g_hwnd_menu1, SHCMBM_GETSUBMENU, 0, ID_MENU_FILE); + /*pos is hardcoded*/ + hMenu = GetSubMenu(hMenu, 2); + + while (RemoveMenu(hMenu, 0, MF_BYPOSITION)) {} + + for (u32 i=0; iCanHandleURL(ifce, "test.test"); + gf_modules_close_interface((GF_BaseInterface *)ifce); + } + } + } + + str = gf_cfg_get_key(user.config, "General", "Loop"); + loop = (str && !stricmp(str, "yes")) ? 1 : 0; + + str = gf_cfg_get_key(user.config, "SAXLoader", "Progressive"); + use_svg_prog = (str && !strcmp(str, "yes")) ? 1 : 0; + + str = gf_cfg_get_key(user.config, "General", "RTIRefreshPeriod"); + if (str) { + rti_update_time_ms = atoi(str); + } else { + gf_cfg_set_key(user.config, "General", "RTIRefreshPeriod", "200"); + } + + + if (is_ppc) GXOpenInput(); + + if (InitInstance(nShowCmd)) { + SetForegroundWindow(g_hwnd); + show_taskbar(0); + + force_2d_gl = gf_term_get_option(term, GF_OPT_USE_OPENGL); + + while (GetMessage(&msg, NULL, 0,0) == TRUE) { + TranslateMessage (&msg); + DispatchMessage (&msg); + + if (playlist_act) { + switch_playlist(playlist_act-1); + playlist_act = 0; + } + } + show_taskbar(1); + } + if (is_ppc) GXCloseInput(); + + /*and destroy*/ + if (term) gf_term_del(term); + if (user.modules) gf_modules_del(user.modules); + if (user.config) gf_cfg_del(user.config); + + if (backlight_off) set_backlight_state(0); + + gf_sys_close(); + if (rti_file) fclose(rti_file); + return 0; +} diff --git a/applications/osmophone/newres.h b/applications/osmophone/newres.h new file mode 100644 index 0000000..57f429f --- /dev/null +++ b/applications/osmophone/newres.h @@ -0,0 +1,41 @@ +#ifndef __NEWRES_H__ +#define __NEWRES_H__ + +#if !defined(UNDER_CE) +#define UNDER_CE _WIN32_WCE +#endif + +#if defined(_WIN32_WCE) + #if !defined(WCEOLE_ENABLE_DIALOGEX) + #define DIALOGEX DIALOG DISCARDABLE + #endif + #include + #define SHMENUBAR RCDATA + #if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300) + #include + #define AFXCE_IDR_SCRATCH_SHMENU 28700 + #else + #define I_IMAGENONE (-2) + #define NOMENU 0xFFFF + #define IDS_SHNEW 1 + + #define IDM_SHAREDNEW 10 + #define IDM_SHAREDNEWDEFAULT 11 + #endif // _WIN32_WCE_PSPC + #define AFXCE_IDD_SAVEMODIFIEDDLG 28701 +#endif // _WIN32_WCE + +#ifdef RC_INVOKED +#ifndef _INC_WINDOWS +#define _INC_WINDOWS + #include "winuser.h" // extract from windows header + #include "winver.h" +#endif +#endif + +#ifdef IDC_STATIC +#undef IDC_STATIC +#endif +#define IDC_STATIC (-1) + +#endif //__NEWRES_H__ diff --git a/applications/osmophone/openfile.cpp b/applications/osmophone/openfile.cpp new file mode 100644 index 0000000..a87b222 --- /dev/null +++ b/applications/osmophone/openfile.cpp @@ -0,0 +1,399 @@ +#include +#include + +#include +#include "resource.h" + +static HINSTANCE g_hInst = NULL; +static HMENU g_hMenuView; +static HWND g_hWndMenuBar; +static HWND hDirTxt; +static HWND the_wnd; +static HWND hList; +static TCHAR w_current_dir[GF_MAX_PATH] = _T("\\"); +static u8 current_dir[GF_MAX_PATH] = "\\"; +static const char *extension_list = NULL; +static char *out_url = NULL; +Bool bViewUnknownTypes = 0; +Bool playlist_mode = 0; +GF_Config *cfg; + + +static void refresh_menu_states() +{ + if (playlist_mode) { + EnableMenuItem(g_hMenuView, IDM_OF_PL_UP, MF_BYCOMMAND|MF_ENABLED); + EnableMenuItem(g_hMenuView, IDM_OF_PL_DOWN, MF_BYCOMMAND|MF_ENABLED ); + } else { + EnableMenuItem(g_hMenuView, IDM_OF_VIEW_ALL, MF_BYCOMMAND| (extension_list ? MF_ENABLED : MF_GRAYED) ); + CheckMenuItem(g_hMenuView, IDM_OF_VIEW_ALL, MF_BYCOMMAND| (bViewUnknownTypes ? MF_CHECKED : MF_UNCHECKED) ); + } + CheckMenuItem(g_hMenuView, IDM_OF_PLAYLIST, MF_BYCOMMAND| (playlist_mode ? MF_CHECKED : MF_UNCHECKED) ); +} + +static void switch_menu_pl() +{ + DeleteMenu(g_hMenuView, IDM_OF_VIEW_ALL, MF_BYCOMMAND); + DeleteMenu(g_hMenuView, IDM_OF_PL_UP, MF_BYCOMMAND); + DeleteMenu(g_hMenuView, IDM_OF_PL_DOWN, MF_BYCOMMAND); + DeleteMenu(g_hMenuView, IDM_OF_PL_CLEAR, MF_BYCOMMAND); + + if (playlist_mode) { + InsertMenu(g_hMenuView, 0, MF_BYPOSITION, IDM_OF_PL_CLEAR, _T("Clear")); + InsertMenu(g_hMenuView, 0, MF_BYPOSITION, IDM_OF_PL_DOWN, _T("Move Down") ); + InsertMenu(g_hMenuView, 0, MF_BYPOSITION, IDM_OF_PL_UP, _T("Move Up") ); + } else { + InsertMenu(g_hMenuView, 0, MF_BYPOSITION, IDM_OF_VIEW_ALL, _T("All Unknown Files") ); + } + TBBUTTONINFO tbbi; + tbbi.cbSize = sizeof(tbbi); + tbbi.dwMask = TBIF_TEXT; + tbbi.pszText = playlist_mode ? _T("Remove") : _T("Add"); + SendMessage(g_hWndMenuBar, TB_SETBUTTONINFO, IDM_OF_PL_ACT, (LPARAM)&tbbi); + refresh_menu_states(); +} + + +Bool enum_dirs(void *cbk, char *name, char *path) +{ + TCHAR w_name[GF_MAX_PATH], w_str_name[GF_MAX_PATH]; + + CE_CharToWide(name, (u16 *) w_name); + wcscpy(w_str_name, _T("+ ")); + wcscat(w_str_name, w_name); + int iRes = SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) w_str_name); + SendMessage(hList, LB_SETITEMDATA, iRes, (LPARAM) 1); + return 0; +} + +Bool enum_files(void *cbk, char *name, char *path) +{ + TCHAR w_name[GF_MAX_PATH]; + + if (!bViewUnknownTypes && extension_list) { + char *ext = strrchr(name, '.'); + if (!ext || !strstr(extension_list, ext+1)) return 0; + } + CE_CharToWide(name, (u16 *) w_name); + SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) w_name); + return 0; +} + + +void set_directory(TCHAR *dir) +{ + SendMessage(hList, LB_RESETCONTENT, 0, 0); + + CE_WideToChar((u16 *) dir, (char *) current_dir); + wcscpy(w_current_dir, dir); + SetWindowText(hDirTxt, w_current_dir); + + if (strcmp((const char *) current_dir, "\\")) { + int iRes = SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) _T("+ ..") ); + SendMessage(hList, LB_SETITEMDATA, iRes, (LPARAM) 1); + } + + /*enum directories*/ + gf_enum_directory((const char *) current_dir, 1, enum_dirs, NULL, NULL); + /*enum files*/ + gf_enum_directory((char *) current_dir, 0, enum_files, NULL, NULL); + SendMessage(hList, LB_SETCURSEL, 0, 0); + SetFocus(hList); +} + + + +void refresh_playlist() +{ + TCHAR w_name[GF_MAX_PATH]; + u32 count, i; + + SetWindowText(hDirTxt, _T("Playlist")); + SendMessage(hList, LB_RESETCONTENT, 0, 0); + + count = gf_cfg_get_key_count(cfg, "Playlist"); + for (i=0; i=count) i=0; + SendMessage(hList, LB_SETCURSEL, i, 0); + SetFocus(hList); +} + +void playlist_act(u32 act_type) +{ + u32 idx, count; + char entry[MAX_PATH]; + const char *url; + + /*reset all*/ + if (act_type == 3) { + count = gf_cfg_get_key_count(cfg, "Playlist"); + while (count) { + url = gf_cfg_get_key_name(cfg, "Playlist", 0); + gf_cfg_set_key(cfg, "Playlist", url, NULL); + count--; + } + refresh_playlist(); + return; + } + count = SendMessage(hList, LB_GETSELCOUNT, 0, 0); + if (!count) return; + idx = SendMessage(hList, LB_GETCURSEL, 0, 0); + + if ((act_type==1) && !idx) return; + else if ((act_type==2) && (idx+1==count)) return; + + url = gf_cfg_get_key_name(cfg, "Playlist", idx); + if (!url) return; + strcpy(entry, url); + /*remove from playlist*/ + gf_cfg_set_key(cfg, "Playlist", url, NULL); + switch (act_type) { + /*remove*/ + case 0: + if (idx+1==count) idx--; + break; + /*up*/ + case 1: + gf_cfg_insert_key(cfg, "Playlist", entry, "", idx-1); + idx--; + break; + /*down*/ + case 2: + gf_cfg_insert_key(cfg, "Playlist", entry, "", idx+1); + idx++; + break; + } + refresh_playlist(); + SendMessage(hList, LB_SETCURSEL, idx, 0); + SetFocus(hList); +} + +Bool add_files(void *cbk, char *name, char *path) +{ + if (!bViewUnknownTypes && extension_list) { + char *ext = strrchr(name, '.'); + if (!ext || !strstr(extension_list, ext+1)) return 0; + } + gf_cfg_set_key(cfg, "Playlist", path, ""); + return 0; +} + +void process_list_change(HWND hWnd, Bool add_to_pl) +{ + TCHAR sTxt[GF_MAX_PATH]; + if (!SendMessage(hList, LB_GETSELCOUNT, 0, 0)) return; + + u32 idx = SendMessage(hList, LB_GETCURSEL, 0, 0); + SendMessage(hList, LB_GETTEXT, idx, (LPARAM)(LPCTSTR) sTxt); + + DWORD param = SendMessage(hList, LB_GETITEMDATA, idx, 0); + if (param==1) { + if (!wcscmp(sTxt, _T("+ ..") ) ) { + if (add_to_pl) return; + current_dir[strlen((const char *) current_dir)-1] = 0; + char *b = strrchr((const char *) current_dir, '\\'); + if (b) b[1] = 0; + else b[0] = '\\'; + CE_CharToWide((char *) current_dir, (u16 *) w_current_dir); + set_directory(w_current_dir); + } else { + if (add_to_pl) { + char dir[MAX_PATH]; + TCHAR wdir[MAX_PATH]; + wcscpy(wdir, w_current_dir); + wcscat(wdir, sTxt+2); + wcscat(wdir, _T("\\")); + CE_WideToChar((u16 *) wdir, (char *) dir); + gf_enum_directory(dir, 0, add_files, NULL, NULL); + } else { + wcscat(w_current_dir, sTxt+2); + wcscat(w_current_dir, _T("\\")); + CE_WideToChar((u16 *) w_current_dir, (char *) current_dir); + set_directory(w_current_dir); + } + } + } else { + char szTxt[1024]; + CE_WideToChar((u16 *) sTxt, (char *) szTxt); + strcpy((char *) out_url, (const char *) current_dir); + strcat(out_url, szTxt); + if (add_to_pl) { + gf_cfg_set_key(cfg, "Playlist", out_url, ""); + strcpy(out_url, ""); + } else { + if (playlist_mode) { + const char *file; + char szPLE[20]; + sprintf(szPLE, "%d", idx); + gf_cfg_set_key(cfg, "General", "PLEntry", szPLE); + file = gf_cfg_get_key_name(cfg, "Playlist", idx); + strcpy(out_url, file); + } + gf_cfg_set_key(cfg, "General", "LastWorkingDir", (const char *) current_dir); + EndDialog(hWnd, 1); + } + } +} + +BOOL InitFileDialog(const HWND hWnd) +{ + TCHAR psz[80]; + ZeroMemory(psz, sizeof(psz)); + SHINITDLGINFO sid; + ZeroMemory(&sid, sizeof(sid)); + sid.dwMask = SHIDIM_FLAGS; + sid.dwFlags = SHIDIF_SIZEDLGFULLSCREEN; + sid.hDlg = hWnd; + + if (FALSE == SHInitDialog(&sid)) + return FALSE; + + SHMENUBARINFO mbi; + ZeroMemory(&mbi, sizeof(SHMENUBARINFO)); + mbi.cbSize = sizeof(SHMENUBARINFO); + mbi.hwndParent = hWnd; + mbi.nToolBarId = IDR_MENU_OPEN; + mbi.hInstRes = g_hInst; + + if (FALSE == SHCreateMenuBar(&mbi)) + { + return FALSE; + } + g_hWndMenuBar = mbi.hwndMB; + + ShowWindow(g_hWndMenuBar, SW_SHOW); + + the_wnd = hWnd; + + hDirTxt = GetDlgItem(hWnd, IDC_DIRNAME); + hList = GetDlgItem(hWnd, IDC_FILELIST); + g_hMenuView = (HMENU)SendMessage(g_hWndMenuBar, SHCMBM_GETSUBMENU, 0, ID_OF_VIEW); + + RECT rc; + GetClientRect(hWnd, &rc); + u32 caption_h = GetSystemMetrics(SM_CYCAPTION) - 3; + MoveWindow(hDirTxt, 0, 0, rc.right - rc.left, caption_h, 1); + MoveWindow(hList, 0, caption_h, rc.right - rc.left, rc.bottom - rc.top - caption_h, 1); + + if (playlist_mode) { + refresh_playlist(); + } else { + if (!strcmp((const char *) current_dir, "\\")) { + char *opt = (char *) gf_cfg_get_key(cfg, "General", "LastWorkingDir"); + if (opt) CE_CharToWide(opt, (u16 *) w_current_dir); + } + set_directory(w_current_dir); + } + switch_menu_pl(); + return TRUE; +} + +BOOL CALLBACK FileDialogProc(const HWND hWnd, const UINT Msg, const WPARAM wParam, const LPARAM lParam) +{ + BOOL bProcessedMsg = TRUE; + + switch (Msg) { + case WM_INITDIALOG: + if (FALSE == InitFileDialog(hWnd)) + EndDialog(hWnd, -1); + break; + + case WM_ACTIVATE: + if (WA_INACTIVE != LOWORD(wParam)) SetFocus(hWnd); + break; + + case WM_CLOSE: + EndDialog(hWnd, 0); + break; + + case WM_COMMAND: + if (LOWORD(wParam) == IDC_FILELIST) { + if (HIWORD(wParam) == LBN_DBLCLK) { + process_list_change(hWnd, 0); + } else { + bProcessedMsg = FALSE; + } + } else { + switch (LOWORD(wParam)) { + case IDOK: + process_list_change(hWnd, 0); + break; + case IDCANCEL: + EndDialog(hWnd, 0); + break; + case IDM_OF_VIEW_ALL: + bViewUnknownTypes = !bViewUnknownTypes; + refresh_menu_states(); + set_directory(w_current_dir); + break; + case IDM_OF_PLAYLIST: + playlist_mode = !playlist_mode; + if (playlist_mode) refresh_playlist(); + else set_directory(w_current_dir); + switch_menu_pl(); + break; + case IDM_OF_PL_ACT: + if (playlist_mode) { + playlist_act(0); + } else { + process_list_change(hWnd, 1); + } + break; + case IDM_OF_PL_UP: + playlist_act(1); + break; + case IDM_OF_PL_DOWN: + playlist_act(2); + break; + case IDM_OF_PL_CLEAR: + playlist_act(3); + break; + default: + bProcessedMsg = FALSE; + break; + } + } + break; + case WM_KEYDOWN: + switch (wParam) { + case VK_LEFT: + case '1': + playlist_act(1); + break; + case VK_RIGHT: + case '2': + playlist_act(2); + break; + default: + bProcessedMsg = FALSE; + break; + } + break; + + default: + bProcessedMsg = FALSE; + } + + return bProcessedMsg; +} + +Bool gf_file_dialog(HINSTANCE inst, HWND parent, char *url, const char *ext_list, GF_Config *gpac_cfg) +{ + extension_list = ext_list; + out_url = url; + g_hInst = inst; + cfg = gpac_cfg; + int iResult = DialogBox(inst, MAKEINTRESOURCE(IDD_FILEDIALOG), parent,(DLGPROC)FileDialogProc); + if (iResult>0) return 1; + return 0; +} + diff --git a/applications/osmophone/osmophone.rc b/applications/osmophone/osmophone.rc new file mode 100644 index 0000000..47272df --- /dev/null +++ b/applications/osmophone/osmophone.rc @@ -0,0 +1,411 @@ +//Microsoft eMbedded Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "newres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON ICON DISCARDABLE "Osmo4.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""newres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Data +// + +IDM_MAIN_MENU1 SHMENUBAR DISCARDABLE +BEGIN + IDM_MAIN_MENU1, 2, + I_IMAGENONE, ID_MENU_FILE, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_FILE, 0, 0, + I_IMAGENONE, IDM_MENU_VIEW, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_VIEW, 0, 1, +END + +IDR_ABOUT_MENU SHMENUBAR DISCARDABLE +BEGIN + IDR_ABOUT_MENU, 1, + I_IMAGENONE, IDM_ABOUT_OK, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, IDS_CAP_OK, + 0, NOMENU, +END + +IDM_MAIN_MENU2 SHMENUBAR DISCARDABLE +BEGIN + IDM_MAIN_MENU2, 2, + I_IMAGENONE, ID_OPTION, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_OPTION, 0, 0, + I_IMAGENONE, ID_MENUITEM40071, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_MENUITEM40072, 0, 1, +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menubar +// + +IDM_MAIN_MENU1 MENU DISCARDABLE +BEGIN + POPUP "File" + BEGIN + MENUITEM "Open", IDM_FILE_OPEN + MENUITEM "Open URL", IDM_FILE_OPEN_URL + POPUP "Recent" + BEGIN + MENUITEM "FILE 1", IDM_OPEN_FILE1 + MENUITEM "FILE 2", IDM_OPEN_FILE2 + MENUITEM "FILE 3", IDM_OPEN_FILE3 + MENUITEM "FILE 4", IDM_OPEN_FILE4 + MENUITEM "FILE 5", IDM_OPEN_FILE5 + MENUITEM "FILE 6", IDM_OPEN_FILE6 + MENUITEM "FILE 7", IDM_OPEN_FILE7 + MENUITEM "FILE 8 ", IDM_OPEN_FILE8 + MENUITEM "FILE 9", IDM_OPEN_FILE9 + END + MENUITEM SEPARATOR + MENUITEM "Pause", IDM_FILE_PAUSE + MENUITEM "Copy/Paste", ID_FILE_CUT_PASTE + MENUITEM SEPARATOR + MENUITEM "Exit", IDM_ITEM_QUIT + END + POPUP "View" + BEGIN + POPUP "Navigate" + BEGIN + MENUITEM "Reset", IDM_NAV_RESET + MENUITEM SEPARATOR + MENUITEM "None", IDM_NAV_NONE + MENUITEM "Slide", IDM_NAV_SLIDE + MENUITEM "Walk", IDM_NAV_WALK + MENUITEM "Fly", IDM_NAV_FLY + MENUITEM "Examine", IDM_NAV_EXAMINE + MENUITEM SEPARATOR + MENUITEM "Headlight", IDM_NAV_HEADLIGHT + MENUITEM "Gravity", IDM_NAV_GRAVITY + POPUP "Collision" + BEGIN + MENUITEM "Disabled", IDM_NAV_COL_NONE + MENUITEM "Regular", IDM_NAV_COL_REG + MENUITEM "Displacement", IDM_NAV_COL_DISP + END + END + MENUITEM SEPARATOR + MENUITEM "Fullscreen", IDM_VIEW_FS + POPUP "Aspect Ratio" + BEGIN + MENUITEM "Keep Original", IDM_VIEW_AR_NONE + MENUITEM "Fill Screen", IDM_VIEW_AR_FILL + MENUITEM "Ratio 4/3", IDM_VIEW_AR_4_3 + MENUITEM "Ratio 16/9", IDM_VIEW_AR_16_9 + END + MENUITEM SEPARATOR + MENUITEM "Options", IDM_MENU_SWITCH + END +END + +IDR_ABOUT_MENU MENU DISCARDABLE +BEGIN + MENUITEM "OK", IDM_ABOUT_OK +END + +IDM_MAIN_MENU2 MENU DISCARDABLE +BEGIN + POPUP "Options" + BEGIN + MENUITEM "15.0 FPS", IDM_VIEW_LOW_RATE + MENUITEM "Direct Draw", IDM_VIEW_DIRECT + MENUITEM SEPARATOR + MENUITEM "Status Bar", IDM_VIEW_STATUS + MENUITEM "CPU usage", IDM_VIEW_CPU + MENUITEM "Log RTI", IDM_FILE_LOG_RTI + MENUITEM SEPARATOR + MENUITEM "Close", IDM_MENU_SWITCH + END + POPUP "?" + BEGIN + MENUITEM "2D OpenGL", IDM_VIEW_FORCEGL + MENUITEM "Progressive SVG ", IDM_VIEW_SVG_LOAD + MENUITEM SEPARATOR + MENUITEM "Disable Playlist", IDS_CAP_DISABLE_PLAYLIST + MENUITEM "About", IDM_VIEW_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CLASSNAME "Osmophone" + IDS_WINDOWNAME "Osmo4" + IDS_APPNAME "Osmophone.exe" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_QUIT "Exit" + IDS_CAP_FILE "File" + IDS_CAP_EXIT "View" + IDS_CAP_VIEW "View" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_OK "OK" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_NAVIGATE "Navigate" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_OPTION "Options" + IDS_CAP_MENUITEM40072 "?" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_DISABLE_PLAYLIST "Copy/Paste" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Data +// + +IDR_MENU_OPEN SHMENUBAR DISCARDABLE +BEGIN + IDR_MENU_OPEN, 2, + I_IMAGENONE, IDM_OF_PL_ACT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, + IDS_CAP_ADD, 0, NOMENU, + I_IMAGENONE, ID_OF_VIEW, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_VIEW, 0, 1, +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menubar +// + +IDR_MENU_OPEN MENU DISCARDABLE +BEGIN + MENUITEM "Add", IDM_OF_PL_ACT + POPUP "View" + BEGIN + MENUITEM "All Unknown Files", IDM_OF_VIEW_ALL + MENUITEM "Move Up", IDM_OF_PL_UP + MENUITEM "Move Down", IDM_OF_PL_DOWN + MENUITEM "Clear", IDM_OF_PL_CLEAR + MENUITEM SEPARATOR + MENUITEM "Playlist Mode", IDM_OF_PLAYLIST + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_APPABOUT, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 93 + BOTTOMMARGIN, 71 + END + + IDD_FILEDIALOG, DIALOG + BEGIN + RIGHTMARGIN, 103 + BOTTOMMARGIN, 85 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_APPABOUT DIALOG DISCARDABLE 0, 0, 94, 74 +STYLE DS_SETFOREGROUND | WS_POPUP | WS_CAPTION +CAPTION "About Osmophone" +FONT 8, "System" +BEGIN + ICON IDI_ICON,IDC_STATIC,6,4,20,20 + LTEXT "Osmo4/GPAC\n""0.4.5"" (build ""33"")",IDC_STATIC,33, + 6,46,17 + CTEXT "Copyright (c) 2007 ENST\nAll Rights Reserved\n\nLicensed under LGPL", + IDC_STATIC,2,28,87,39 +END + +IDD_FILEDIALOG DIALOG DISCARDABLE 0, 0, 104, 86 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Select File" +FONT 8, "System" +BEGIN + EDITTEXT IDC_DIRNAME,1,3,100,12,ES_AUTOHSCROLL | ES_READONLY + LISTBOX IDC_FILELIST,1,18,102,65,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040c04b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "ENST\0" + VALUE "FileDescription", "osmophone\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "osmophone\0" + VALUE "LegalCopyright", "Copyright © 2008\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "osmophone.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "ENST GPAC\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x40c, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_FILE "+" + IDS_CAP_VIEW "View" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_OK "OK" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_SELECT "File" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_MENUITEM40092 "Add" + IDS_CAP_ADD "Add" +END + +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/applications/osmophone/resource.h b/applications/osmophone/resource.h new file mode 100644 index 0000000..9744529 --- /dev/null +++ b/applications/osmophone/resource.h @@ -0,0 +1,123 @@ +//{{NO_DEPENDENCIES}} +// Microsoft eMbedded Visual C++ generated include file. +// Used by osmophone.rc +// +#define IDS_CLASSNAME 1 +#define IDS_WINDOWNAME 4 +#define IDS_APPNAME 5 +#define IDI_ICON 101 +#define IDM_MENU 102 +#define IDM_MAIN_MENU 102 +#define IDM_MAIN_MENU1 102 +#define IDD_ABOUTBOX 103 +#define IDD_APPABOUT 104 +#define IDD_OPENFILE 105 +#define IDD_FILEDIALOG 105 +#define IDR_MENU_OPEN 106 +#define IDR_ABOUT_MENU 107 +#define IDM_MAIN_MENU2 108 +#define IDM_MAIN_MENUITEM1 401 +#define IDM_MAIN_MENUITEM2 402 +#define IDS_MAIN_MENUITEM1 410 +#define IDS_MAIN_MENUITEM2 412 +#define IDC_EDIT1 1001 +#define IDC_DIRNAME 1001 +#define IDC_LIST1 1002 +#define IDC_FILELIST 1002 +#define IDC_LIST2 1003 +#define IDM_FILE_EXIT 40002 +#define IDM_HELP_ABOUT 40003 +#define IDM_GRAB 40004 +#define IDM_RELEASE 40005 +#define ID_FILE_SHOW_WMKEYDOWN 40006 +#define ID_FILE_SHOW_WMKEYUP 40007 +#define ID_FILE_SHOW_WMCHAR 40008 +#define ID_FILE_SHOW_WMMOUSEMOVE 40009 +#define ID_FILE_SHOW_WMLBUTTONDOWN 40010 +#define ID_FILE_SHOW_WMLBUTTONDBLCLK 40011 +#define ID_FILE_SHOW_WMLBUTTONUP 40012 +#define ID_FILE_SHOW_WMRBUTTONDOWN 40013 +#define ID_FILE_SHOW_WMRBUTTONUP 40014 +#define ID_FILE_SHOW_WMRBUTTONDBLCLK 40015 +#define IDM_ITEM_QUIT 40016 +#define IDS_CAP_QUIT 40018 +#define ID_MENU_FILE 40019 +#define IDS_CAP_FILE 40021 +#define IDM_FILE_OPEN 40022 +#define IDS_CAP_EXIT 40023 +#define IDM_MENU_VIEW 40024 +#define IDS_CAP_VIEW 40025 +#define IDM_FILE_OPEN_URL 40026 +#define IDM_OPEN_FILE1 40030 +#define IDM_OPEN_FILE2 40031 +#define IDM_OPEN_FILE3 40032 +#define IDM_OPEN_FILE4 40033 +#define IDM_OPEN_FILE5 40034 +#define IDM_OPEN_FILE6 40035 +#define IDM_OPEN_FILE7 40036 +#define IDM_OPEN_FILE8 40037 +#define IDM_OPEN_FILE9 40038 +#define IDM_OPEN_FILE10 40039 +#define ID_VIEW 40040 +#define IDM_OF_VIEW_ALL 40041 +#define IDS_CAP_OK 40044 +#define IDS_CAP_MENUITEM40045 40046 +#define ID_SELECT 40047 +#define IDS_CAP_SELECT 40049 +#define IDM_OF_PL_ADD 40050 +#define IDM_OF_PLAYLIST 40051 +#define IDM_OF_PL_REM 40052 +#define IDM_VIEW_FS 40053 +#define IDM_VIEW_ABOUT 40054 +#define IDM_ABOUT_OK 40055 +#define IDM_VIEW_STATUS 40057 +#define IDM_VIEW_FORCEGL 40058 +#define ID_NAVIGATE 40059 +#define IDS_CAP_NAVIGATE 40061 +#define IDM_NAV_NONE 40062 +#define IDM_NAV_SLIDE 40063 +#define IDM_SELECT 40064 +#define IDM_NAV_RESET 40065 +#define IDM_MENU_SWITCH 40066 +#define ID_OPTION 40067 +#define IDS_CAP_OPTION 40069 +#define ID_MENUITEM40071 40071 +#define IDS_CAP_MENUITEM40072 40073 +#define IDM_NAV_WALK 40074 +#define IDM_NAV_FLY 40075 +#define IDM_NAV_EXAMINE 40076 +#define IDM_NAV_HEADLIGHT 40077 +#define IDM_NAV_GRAVITY 40078 +#define IDM_NAV_COL_NONE 40079 +#define IDM_NAV_COL_REG 40080 +#define IDM_NAV_COL_DISP 40081 +#define IDM_VIEW_AR_NONE 40082 +#define IDM_VIEW_AR_FILL 40083 +#define IDM_VIEW_AR_4_3 40084 +#define IDM_VIEW_AR_16_9 40085 +#define IDM_OF_PL_UP 40088 +#define IDM_OF_PL_DOWN 40089 +#define ID_OF_VIEW 40090 +#define IDM_OF_PL_ACT 40091 +#define IDS_CAP_MENUITEM40092 40093 +#define IDS_CAP_ADD 40095 +#define IDM_OF_PL_CLEAR 40096 +#define IDM_FILE_LOG_RTI 40097 +#define IDM_VIEW_CPU 40098 +#define IDM_FILE_PAUSE 40099 +#define IDM_VIEW_LOW_RATE 40100 +#define IDM_VIEW_DIRECT 40101 +#define IDM_VIEW_SVG_LOAD 40102 +#define IDS_CAP_DISABLE_PLAYLIST 40103 +#define ID_FILE_CUT_PASTE 40104 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 111 +#define _APS_NEXT_COMMAND_VALUE 40105 +#define _APS_NEXT_CONTROL_VALUE 1004 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/applications/osmozilla/Makefile b/applications/osmozilla/Makefile new file mode 100644 index 0000000..f7a866c --- /dev/null +++ b/applications/osmozilla/Makefile @@ -0,0 +1,112 @@ +include ../../config.mak + +vpath %.cpp $(SRC_PATH)/applications/osmozilla + +ifeq ($(CONFIG_WIN32),yes) +USER_NAME=root +else +USER_NAME=$(shell whoami) +ifeq ($(USER_NAME), root) +else +MOZILLA_DIR=local +endif +endif + +CFLAGS=$(CPPFLAGS) $(XUL_CFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +ifeq ($(CONFIG_WIN32),yes) +CFLAGS+=-DXP_WIN +else +ifeq ($(CONFIG_DARWIN),yes) +CFLAGS+=-DXP_MAC +else +CFLAGS+=-DXP_UNIX -DMOZ_X11 +endif +endif + +CFLAGS+=-DNPBASIC_EXPORTS -DMOZILLA_STRICT_API -DXPCOM_GLUE + + +LINKLIBS=-L../../bin/gcc -lgpac + +OBJS=osmozilla.o npp_gate.o np_entry.o npn_gate.o + +SRCS := $(OBJS:.o=.cpp) + + +LIB=nposmozilla.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LINKLIBS+=-lwinmm -lgdi32 +LDFLAGS+=--export-all-symbols +endif + +all: $(LIB) + +$(LIB): $(OBJS) +ifeq ($(CONFIG_WIN32),yes) + windres osmozilla.rc osmoz.o + $(CXX) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) osmoz.o $(LINKLIBS) + cp $(SRC_PATH)/applications/osmozilla/nsIOsmozilla.xpt_w32 ../../bin/gcc/nposmozilla.xpt + chmod +w ../../bin/gcc/nposmozilla.xpt +else + $(CXX) $(SHFLAGS) $(LDFLAGS) $(OBJS) $(LINKLIBS) -o ../../bin/gcc/$@ + cp $(SRC_PATH)/applications/osmozilla/nsIOsmozilla.xpt_linux ../../bin/gcc/nposmozilla.xpt + chmod +w ../../bin/gcc/nposmozilla.xpt +endif + @echo $(USER_ROOT) + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) ../../bin/gcc/nposmozilla.xpt +ifeq ($(CONFIG_WIN32),yes) + rm -f osmoz.o +endif + +install: +ifeq ($(MOZILLA_DIR), local) +ifeq ($(USER_NAME), root) + @echo "*** Root cannot install local mozilla plugins! ***" + @echo "*** Exit root mode and reinstall mozilla plugin! ***" +else + $(MAKE) $(LIB) + install -D -m 755 ../../bin/gcc/$(LIB) "$(HOME)/.mozilla/plugins/$(LIB)" + install -D -m 755 ../../bin/gcc/nposmozilla.xpt "$(HOME)/.mozilla/components/nposmozilla.xpt" +endif +else + install -D -m 755 ../../bin/gcc/$(LIB) "$(MOZILLA_DIR)/components/$(LIB)" + install -D -m 755 ../../bin/gcc/nposmozilla.xpt "$(MOZILLA_DIR)/components/nposmozilla.xpt" +endif + +uninstall: +ifeq ($(MOZILLA_DIR), local) +ifeq ($(USER_NAME), root) +else + rm -rf "$(HOME)/.mozilla/plugins/$(LIB)" + rm -rf "$(HOME)/.mozilla/components/nposmozilla.xpt" +endif +else + rm -rf "$(MOZILLA_DIR)/components/$(LIB)" + rm -rf "$(MOZILLA_DIR)/components/nposmozilla.xpt" +endif + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + diff --git a/applications/osmozilla/np_entry.cpp b/applications/osmozilla/np_entry.cpp new file mode 100644 index 0000000..c3df107 --- /dev/null +++ b/applications/osmozilla/np_entry.cpp @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +////////////////////////////////////////////////////////////// +// +// Main plugin entry point implementation -- exports from the +// plugin library +// +#include "npplat.h" + +#include "osmozilla.h" + +NPNetscapeFuncs NPNFuncs; + +NPError OSCALL NP_Shutdown() +{ + NS_PluginShutdown(); + return NPERR_NO_ERROR; +} + +static NPError fillPluginFunctionTable(NPPluginFuncs* aNPPFuncs) +{ + if(aNPPFuncs == NULL) + return NPERR_INVALID_FUNCTABLE_ERROR; + + // Set up the plugin function table that Netscape will use to + // call us. Netscape needs to know about our version and size + // and have a UniversalProcPointer for every function we implement. + + aNPPFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; +#ifdef XP_MAC + aNPPFuncs->newp = NewNPP_NewProc(Private_New); + aNPPFuncs->destroy = NewNPP_DestroyProc(Private_Destroy); + aNPPFuncs->setwindow = NewNPP_SetWindowProc(Private_SetWindow); + aNPPFuncs->newstream = NewNPP_NewStreamProc(Private_NewStream); + aNPPFuncs->destroystream = NewNPP_DestroyStreamProc(Private_DestroyStream); + aNPPFuncs->asfile = NewNPP_StreamAsFileProc(Private_StreamAsFile); + aNPPFuncs->writeready = NewNPP_WriteReadyProc(Private_WriteReady); + aNPPFuncs->write = NewNPP_WriteProc(Private_Write); + aNPPFuncs->print = NewNPP_PrintProc(Private_Print); + aNPPFuncs->event = NewNPP_HandleEventProc(Private_HandleEvent); + aNPPFuncs->urlnotify = NewNPP_URLNotifyProc(Private_URLNotify); + aNPPFuncs->getvalue = NewNPP_GetValueProc(Private_GetValue); + aNPPFuncs->setvalue = NewNPP_SetValueProc(Private_SetValue); +#else + aNPPFuncs->newp = NPP_New; + aNPPFuncs->destroy = NPP_Destroy; + aNPPFuncs->setwindow = NPP_SetWindow; + aNPPFuncs->newstream = NPP_NewStream; + aNPPFuncs->destroystream = NPP_DestroyStream; + aNPPFuncs->asfile = NPP_StreamAsFile; + aNPPFuncs->writeready = NPP_WriteReady; + aNPPFuncs->write = NPP_Write; + aNPPFuncs->print = NPP_Print; + aNPPFuncs->event = NPP_HandleEvent; + aNPPFuncs->urlnotify = NPP_URLNotify; + aNPPFuncs->getvalue = NPP_GetValue; + aNPPFuncs->setvalue = NPP_SetValue; +#endif +#ifdef OJI + aNPPFuncs->javaClass = NULL; +#endif + + return NPERR_NO_ERROR; +} + +static NPError fillNetscapeFunctionTable(NPNetscapeFuncs* aNPNFuncs) +{ + if(aNPNFuncs == NULL) + return NPERR_INVALID_FUNCTABLE_ERROR; + + if(HIBYTE(aNPNFuncs->version) > NP_VERSION_MAJOR) + return NPERR_INCOMPATIBLE_VERSION_ERROR; + + if(aNPNFuncs->size < sizeof(NPNetscapeFuncs)) + return NPERR_INVALID_FUNCTABLE_ERROR; + + NPNFuncs.size = aNPNFuncs->size; + NPNFuncs.version = aNPNFuncs->version; + NPNFuncs.geturlnotify = aNPNFuncs->geturlnotify; + NPNFuncs.geturl = aNPNFuncs->geturl; + NPNFuncs.posturlnotify = aNPNFuncs->posturlnotify; + NPNFuncs.posturl = aNPNFuncs->posturl; + NPNFuncs.requestread = aNPNFuncs->requestread; + NPNFuncs.newstream = aNPNFuncs->newstream; + NPNFuncs.write = aNPNFuncs->write; + NPNFuncs.destroystream = aNPNFuncs->destroystream; + NPNFuncs.status = aNPNFuncs->status; + NPNFuncs.uagent = aNPNFuncs->uagent; + NPNFuncs.memalloc = aNPNFuncs->memalloc; + NPNFuncs.memfree = aNPNFuncs->memfree; + NPNFuncs.memflush = aNPNFuncs->memflush; + NPNFuncs.reloadplugins = aNPNFuncs->reloadplugins; +#ifdef OJI + NPNFuncs.getJavaEnv = aNPNFuncs->getJavaEnv; + NPNFuncs.getJavaPeer = aNPNFuncs->getJavaPeer; +#endif + NPNFuncs.getvalue = aNPNFuncs->getvalue; + NPNFuncs.setvalue = aNPNFuncs->setvalue; + NPNFuncs.invalidaterect = aNPNFuncs->invalidaterect; + NPNFuncs.invalidateregion = aNPNFuncs->invalidateregion; + NPNFuncs.forceredraw = aNPNFuncs->forceredraw; + + return NPERR_NO_ERROR; +} + +#ifdef XP_WIN + +NPError OSCALL NP_Initialize(NPNetscapeFuncs* aNPNFuncs) +{ + NPError rv = fillNetscapeFunctionTable(aNPNFuncs); + if(rv != NPERR_NO_ERROR) + return rv; + + return NS_PluginInitialize(); +} + +NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* aNPPFuncs) +{ + return fillPluginFunctionTable(aNPPFuncs); +} + +#endif + + +#ifdef XP_UNIX + +NPError OSCALL NP_Initialize(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs) +{ + NPError rv = fillNetscapeFunctionTable(aNPNFuncs); + if(rv != NPERR_NO_ERROR) + return rv; + + rv = fillPluginFunctionTable(aNPPFuncs); + if(rv != NPERR_NO_ERROR) + return rv; + + return NS_PluginInitialize(); +} + +char * NP_GetMIMEDescription(void) +{ + return NPP_GetMIMEDescription(); +} + +NPError NP_GetValue(void *future, NPPVariable aVariable, void *aValue) +{ + return NS_PluginGetValue(aVariable, aValue); +} + +#endif + + +#ifdef XP_MAC + + +#if !TARGET_API_MAC_CARBON +QDGlobals* gQDPtr; // Pointer to Netscape's QuickDraw globals +#endif + +short gResFile; // Refnum of the plugin's resource file + +NPError Private_Initialize(void) +{ + NPError rv = NS_PluginInitialize(); + return rv; +} + +void Private_Shutdown(void) +{ + NS_PluginShutdown(); + __destroy_global_chain(); +} + +void SetUpQD(void); + +void SetUpQD(void) +{ + ProcessSerialNumber PSN; + FSSpec myFSSpec; + Str63 name; + ProcessInfoRec infoRec; + OSErr result = noErr; + CFragConnectionID connID; + Str255 errName; + + // Memorize the plugin¹s resource file refnum for later use. + gResFile = CurResFile(); + +#if !TARGET_API_MAC_CARBON + // Ask the system if CFM is available. + long response; + OSErr err = Gestalt(gestaltCFMAttr, &response); + Boolean hasCFM = BitTst(&response, 31-gestaltCFMPresent); + + if (hasCFM) { + // GetProcessInformation takes a process serial number and + // will give us back the name and FSSpec of the application. + // See the Process Manager in IM. + infoRec.processInfoLength = sizeof(ProcessInfoRec); + infoRec.processName = name; + infoRec.processAppSpec = &myFSSpec; + + PSN.highLongOfPSN = 0; + PSN.lowLongOfPSN = kCurrentProcess; + + result = GetProcessInformation(&PSN, &infoRec); + } + else + // If no CFM installed, assume it must be a 68K app. + result = -1; + + if (result == noErr) { + // Now that we know the app name and FSSpec, we can call GetDiskFragment + // to get a connID to use in a subsequent call to FindSymbol (it will also + // return the address of ³main² in app, which we ignore). If GetDiskFragment + // returns an error, we assume the app must be 68K. + Ptr mainAddr; + result = GetDiskFragment(infoRec.processAppSpec, 0L, 0L, infoRec.processName, + kReferenceCFrag, &connID, (Ptr*)&mainAddr, errName); + } + + if (result == noErr) { + // The app is a PPC code fragment, so call FindSymbol + // to get the exported ³qd² symbol so we can access its + // QuickDraw globals. + CFragSymbolClass symClass; + result = FindSymbol(connID, "\pqd", (Ptr*)&gQDPtr, &symClass); + } + else { + // The app is 68K, so use its A5 to compute the address + // of its QuickDraw globals. + gQDPtr = (QDGlobals*)(*((long*)SetCurrentA5()) - (sizeof(QDGlobals) - sizeof(GrafPtr))); + } +#endif /* !TARGET_API_MAC_CARBON */ +} + +NPError main(NPNetscapeFuncs* nsTable, NPPluginFuncs* pluginFuncs, NPP_ShutdownUPP* unloadUpp); + +#if !TARGET_API_MAC_CARBON +#pragma export on +#if GENERATINGCFM +RoutineDescriptor mainRD = BUILD_ROUTINE_DESCRIPTOR(uppNPP_MainEntryProcInfo, main); +#endif +#pragma export off +#endif /* !TARGET_API_MAC_CARBON */ + + +NPError main(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs, NPP_ShutdownUPP* aUnloadUpp) +{ + NPError rv = NPERR_NO_ERROR; + + if (aUnloadUpp == NULL) + rv = NPERR_INVALID_FUNCTABLE_ERROR; + + if (rv == NPERR_NO_ERROR) + rv = fillNetscapeFunctionTable(aNPNFuncs); + + if (rv == NPERR_NO_ERROR) { + // defer static constructors until the global functions are initialized. + __InitCode__(); + rv = fillPluginFunctionTable(aNPPFuncs); + } + + *aUnloadUpp = NewNPP_ShutdownProc(Private_Shutdown); + SetUpQD(); + rv = Private_Initialize(); + + return rv; +} +#endif //XP_MAC diff --git a/applications/osmozilla/npn_gate.cpp b/applications/osmozilla/npn_gate.cpp new file mode 100644 index 0000000..13a0c7c --- /dev/null +++ b/applications/osmozilla/npn_gate.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +//////////////////////////////////////////////////////////// +// +// Implementation of Netscape entry points (NPN_*) +// +#include "npplat.h" + +extern NPNetscapeFuncs NPNFuncs; + +void NPN_Version(int* plugin_major, int* plugin_minor, int* netscape_major, int* netscape_minor) +{ + *plugin_major = NP_VERSION_MAJOR; + *plugin_minor = NP_VERSION_MINOR; + *netscape_major = HIBYTE(NPNFuncs.version); + *netscape_minor = LOBYTE(NPNFuncs.version); +} + +NPError NPN_GetURLNotify(NPP instance, const char *url, const char *target, void* notifyData) +{ + int navMinorVers = NPNFuncs.version & 0xFF; + NPError rv = NPERR_NO_ERROR; + + if( navMinorVers >= NPVERS_HAS_NOTIFICATION ) + rv = CallNPN_GetURLNotifyProc(NPNFuncs.geturlnotify, instance, url, target, notifyData); + else + rv = NPERR_INCOMPATIBLE_VERSION_ERROR; + + return rv; +} + +NPError NPN_GetURL(NPP instance, const char *url, const char *target) +{ + NPError rv = CallNPN_GetURLProc(NPNFuncs.geturl, instance, url, target); + return rv; +} + +NPError NPN_PostURLNotify(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file, void* notifyData) +{ + int navMinorVers = NPNFuncs.version & 0xFF; + NPError rv = NPERR_NO_ERROR; + + if( navMinorVers >= NPVERS_HAS_NOTIFICATION ) + rv = CallNPN_PostURLNotifyProc(NPNFuncs.posturlnotify, instance, url, window, len, buf, file, notifyData); + else + rv = NPERR_INCOMPATIBLE_VERSION_ERROR; + + return rv; +} + +NPError NPN_PostURL(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file) +{ + NPError rv = CallNPN_PostURLProc(NPNFuncs.posturl, instance, url, window, len, buf, file); + return rv; +} + +NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList) +{ + NPError rv = CallNPN_RequestReadProc(NPNFuncs.requestread, stream, rangeList); + return rv; +} + +NPError NPN_NewStream(NPP instance, NPMIMEType type, const char* target, NPStream** stream) +{ + int navMinorVersion = NPNFuncs.version & 0xFF; + + NPError rv = NPERR_NO_ERROR; + + if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT ) + rv = CallNPN_NewStreamProc(NPNFuncs.newstream, instance, type, target, stream); + else + rv = NPERR_INCOMPATIBLE_VERSION_ERROR; + + return rv; +} + +int32 NPN_Write(NPP instance, NPStream *stream, int32 len, void *buffer) +{ + int navMinorVersion = NPNFuncs.version & 0xFF; + int32 rv = 0; + + if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT ) + rv = CallNPN_WriteProc(NPNFuncs.write, instance, stream, len, buffer); + else + rv = -1; + + return rv; +} + +NPError NPN_DestroyStream(NPP instance, NPStream* stream, NPError reason) +{ + int navMinorVersion = NPNFuncs.version & 0xFF; + NPError rv = NPERR_NO_ERROR; + + if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT ) + rv = CallNPN_DestroyStreamProc(NPNFuncs.destroystream, instance, stream, reason); + else + rv = NPERR_INCOMPATIBLE_VERSION_ERROR; + + return rv; +} + +void NPN_Status(NPP instance, const char *message) +{ + CallNPN_StatusProc(NPNFuncs.status, instance, message); +} + +const char* NPN_UserAgent(NPP instance) +{ + const char * rv = NULL; + rv = CallNPN_UserAgentProc(NPNFuncs.uagent, instance); + return rv; +} + +void* NPN_MemAlloc(uint32 size) +{ + void * rv = NULL; + rv = CallNPN_MemAllocProc(NPNFuncs.memalloc, size); + return rv; +} + +void NPN_MemFree(void* ptr) +{ + CallNPN_MemFreeProc(NPNFuncs.memfree, ptr); +} + +uint32 NPN_MemFlush(uint32 size) +{ + uint32 rv = CallNPN_MemFlushProc(NPNFuncs.memflush, size); + return rv; +} + +void NPN_ReloadPlugins(NPBool reloadPages) +{ + CallNPN_ReloadPluginsProc(NPNFuncs.reloadplugins, reloadPages); +} + +#ifdef OJI +JRIEnv* NPN_GetJavaEnv(void) +{ + JRIEnv * rv = NULL; + rv = CallNPN_GetJavaEnvProc(NPNFuncs.getJavaEnv); + return rv; +} + +jref NPN_GetJavaPeer(NPP instance) +{ + jref rv; + rv = CallNPN_GetJavaPeerProc(NPNFuncs.getJavaPeer, instance); + return rv; +} +#endif + +NPError NPN_GetValue(NPP instance, NPNVariable variable, void *value) +{ + NPError rv = CallNPN_GetValueProc(NPNFuncs.getvalue, instance, variable, value); + return rv; +} + +NPError NPN_SetValue(NPP instance, NPPVariable variable, void *value) +{ + NPError rv = CallNPN_SetValueProc(NPNFuncs.setvalue, instance, variable, value); + return rv; +} + +void NPN_InvalidateRect(NPP instance, NPRect *invalidRect) +{ + CallNPN_InvalidateRectProc(NPNFuncs.invalidaterect, instance, invalidRect); +} + +void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion) +{ + CallNPN_InvalidateRegionProc(NPNFuncs.invalidateregion, instance, invalidRegion); +} + +void NPN_ForceRedraw(NPP instance) +{ + CallNPN_ForceRedrawProc(NPNFuncs.forceredraw, instance); +} diff --git a/applications/osmozilla/npp_gate.cpp b/applications/osmozilla/npp_gate.cpp new file mode 100644 index 0000000..179dba8 --- /dev/null +++ b/applications/osmozilla/npp_gate.cpp @@ -0,0 +1,353 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "osmozilla.h" + + +// here the plugin creates a plugin instance object which +// will be associated with this newly created NPP instance and +// will do all the neccessary job +NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved) +{ + if(instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPError rv = NPERR_NO_ERROR; + + // create a new plugin instance object + // initialization will be done when the associated window is ready + nsPluginCreateData ds; + + ds.instance = instance; + ds.type = pluginType; + ds.mode = mode; + ds.argc = argc; + ds.argn = argn; + ds.argv = argv; + ds.saved = saved; + + nsPluginInstanceBase * plugin = NS_NewPluginInstance(&ds); + if(plugin == NULL) + return NPERR_OUT_OF_MEMORY_ERROR; + + // associate the plugin instance object with NPP instance + instance->pdata = (void *)plugin; + return rv; +} + +// here is the place to clean up and destroy the nsPluginInstance object +NPError NPP_Destroy (NPP instance, NPSavedData** save) +{ + if(instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPError rv = NPERR_NO_ERROR; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin != NULL) { + plugin->shut(); + NS_DestroyPluginInstance(plugin); + } + return rv; +} + +// during this call we know when the plugin window is ready or +// is about to be destroyed so we can do some gui specific +// initialization and shutdown +NPError NPP_SetWindow (NPP instance, NPWindow* pNPWindow) +{ + if(instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPError rv = NPERR_NO_ERROR; + + if(pNPWindow == NULL) + return NPERR_GENERIC_ERROR; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + + if(plugin == NULL) + return NPERR_GENERIC_ERROR; + + // window just created + if(!plugin->isInitialized() && (pNPWindow->window != NULL)) { + if(!plugin->init(pNPWindow)) { + NS_DestroyPluginInstance(plugin); + return NPERR_MODULE_LOAD_FAILED_ERROR; + } + } + + // window goes away + if((pNPWindow->window == NULL) && plugin->isInitialized()) + return plugin->SetWindow(pNPWindow); + + // window resized? + if(plugin->isInitialized() && (pNPWindow->window != NULL)) + return plugin->SetWindow(pNPWindow); + + // this should not happen, nothing to do + if((pNPWindow->window == NULL) && !plugin->isInitialized()) + return plugin->SetWindow(pNPWindow); + + return rv; +} + +NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype) +{ + if(instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin == NULL) + return NPERR_GENERIC_ERROR; + + NPError rv = plugin->NewStream(type, stream, seekable, stype); + return rv; +} + +int32 NPP_WriteReady (NPP instance, NPStream *stream) +{ + if(instance == NULL) + return 0x0fffffff; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin == NULL) + return 0x0fffffff; + + int32 rv = plugin->WriteReady(stream); + return rv; +} + +int32 NPP_Write (NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer) +{ + if(instance == NULL) + return len; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin == NULL) + return len; + + int32 rv = plugin->Write(stream, offset, len, buffer); + return rv; +} + +NPError NPP_DestroyStream (NPP instance, NPStream *stream, NPError reason) +{ + if(instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin == NULL) + return NPERR_GENERIC_ERROR; + + NPError rv = plugin->DestroyStream(stream, reason); + return rv; +} + +void NPP_StreamAsFile (NPP instance, NPStream* stream, const char* fname) +{ + if(instance == NULL) + return; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin == NULL) + return; + + plugin->StreamAsFile(stream, fname); +} + +void NPP_Print (NPP instance, NPPrint* printInfo) +{ + if(instance == NULL) + return; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin == NULL) + return; + plugin->Print(printInfo); +} + +void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) +{ + if(instance == NULL) + return; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin == NULL) + return; + + plugin->URLNotify(url, reason, notifyData); +} + +NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) +{ + if(instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin == NULL) + return NPERR_GENERIC_ERROR; + + NPError rv = plugin->GetValue(variable, value); + return rv; +} + +NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) +{ + if(instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin == NULL) + return NPERR_GENERIC_ERROR; + + NPError rv = plugin->SetValue(variable, value); + return rv; +} + +int16 NPP_HandleEvent(NPP instance, void* event) +{ + if(instance == NULL) + return 0; + + nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; + if(plugin == NULL) + return 0; + + uint16 rv = plugin->HandleEvent(event); + return rv; +} + +#ifdef OJI +jref NPP_GetJavaClass (void) +{ + return NULL; +} +#endif + +/**************************************************/ +/* */ +/* Mac */ +/* */ +/**************************************************/ + +// Mac needs these wrappers, see npplat.h for more info + +#ifdef XP_MAC + +NPError Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved) +{ + NPError rv = NPP_New(pluginType, instance, mode, argc, argn, argv, saved); + return rv; +} + +NPError Private_Destroy(NPP instance, NPSavedData** save) +{ + NPError rv = NPP_Destroy(instance, save); + return rv; +} + +NPError Private_SetWindow(NPP instance, NPWindow* window) +{ + NPError rv = NPP_SetWindow(instance, window); + return rv; +} + +NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype) +{ + NPError rv = NPP_NewStream(instance, type, stream, seekable, stype); + return rv; +} + +int32 Private_WriteReady(NPP instance, NPStream* stream) +{ + int32 rv = NPP_WriteReady(instance, stream); + return rv; +} + +int32 Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer) +{ + int32 rv = NPP_Write(instance, stream, offset, len, buffer); + return rv; +} + +void Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname) +{ + NPP_StreamAsFile(instance, stream, fname); +} + + +NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason) +{ + NPError rv = NPP_DestroyStream(instance, stream, reason); + return rv; +} + +int16 Private_HandleEvent(NPP instance, void* event) +{ + int16 rv = NPP_HandleEvent(instance, event); + return rv; +} + +void Private_Print(NPP instance, NPPrint* platformPrint) +{ + NPP_Print(instance, platformPrint); +} + +void Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) +{ + NPP_URLNotify(instance, url, reason, notifyData); +} + +jref Private_GetJavaClass(void) +{ + return NULL; +} + +NPError Private_GetValue(NPP instance, NPPVariable variable, void *result) +{ + NPError rv = NPP_GetValue(instance, variable, result); + return rv; +} + +NPError Private_SetValue(NPP instance, NPNVariable variable, void *value) +{ + NPError rv = NPP_SetValue(instance, variable, value); + return rv; +} + +#endif //XP_MAC diff --git a/applications/osmozilla/npplat.h b/applications/osmozilla/npplat.h new file mode 100644 index 0000000..d80dd88 --- /dev/null +++ b/applications/osmozilla/npplat.h @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _NPPLAT_H_ +#define _NPPLAT_H_ + +/**************************************************/ +/* */ +/* Windows */ +/* */ +/**************************************************/ + +#ifdef WIN32 +#include +#endif + +#include "npapi.h" +#include "npupp.h" + +/**************************************************/ +/* */ +/* Unix */ +/* */ +/**************************************************/ +#ifdef XP_UNIX +#include +#endif //XP_UNIX + +/**************************************************/ +/* */ +/* Mac */ +/* */ +/**************************************************/ +#ifdef XP_MAC + +#include +#include +#include +#include +#include +#include + +#include "jri.h" + +// The Mixed Mode procInfos defined in npupp.h assume Think C- +// style calling conventions. These conventions are used by +// Metrowerks with the exception of pointer return types, which +// in Metrowerks 68K are returned in A0, instead of the standard +// D0. Thus, since NPN_MemAlloc and NPN_UserAgent return pointers, +// Mixed Mode will return the values to a 68K plugin in D0, but +// a 68K plugin compiled by Metrowerks will expect the result in +// A0. The following pragma forces Metrowerks to use D0 instead. +// +#ifdef __MWERKS__ +#ifndef powerc +#pragma pointers_in_D0 +#endif +#endif + +#ifdef __MWERKS__ +#ifndef powerc +#pragma pointers_in_A0 +#endif +#endif + +// The following fix for static initializers (which fixes a preious +// incompatibility with some parts of PowerPlant, was submitted by +// Jan Ulbrich. +#ifdef __MWERKS__ + #ifdef __cplusplus + extern "C" { + #endif + #ifndef powerc + extern void __InitCode__(void); + #else + extern void __sinit(void); + #define __InitCode__ __sinit + #endif + extern void __destroy_global_chain(void); + #ifdef __cplusplus + } + #endif // __cplusplus +#endif // __MWERKS__ + +// Wrapper functions for all calls from Netscape to the plugin. +// These functions let the plugin developer just create the APIs +// as documented and defined in npapi.h, without needing to +// install those functions in the function table or worry about +// setting up globals for 68K plugins. +NPError Private_Initialize(void); +void Private_Shutdown(void); +NPError Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved); +NPError Private_Destroy(NPP instance, NPSavedData** save); +NPError Private_SetWindow(NPP instance, NPWindow* window); +NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype); +NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason); +int32 Private_WriteReady(NPP instance, NPStream* stream); +int32 Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer); +void Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname); +void Private_Print(NPP instance, NPPrint* platformPrint); +int16 Private_HandleEvent(NPP instance, void* event); +void Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData); +jref Private_GetJavaClass(void); +NPError Private_GetValue(NPP instance, NPPVariable variable, void *result); +NPError Private_SetValue(NPP instance, NPNVariable variable, void *value); + +#endif //XP_MAC + +#ifndef HIBYTE +#define HIBYTE(i) (i >> 8) +#endif + +#ifndef LOBYTE +#define LOBYTE(i) (i & 0xff) +#endif + +#endif //_NPPLAT_H_ diff --git a/applications/osmozilla/nsIOsmozilla.h b/applications/osmozilla/nsIOsmozilla.h new file mode 100644 index 0000000..fdd4ccc --- /dev/null +++ b/applications/osmozilla/nsIOsmozilla.h @@ -0,0 +1,121 @@ +/* + * DO NOT EDIT. THIS FILE IS GENERATED FROM nsIOsmozilla.idl + */ + +#ifndef __gen_nsIOsmozilla_h__ +#define __gen_nsIOsmozilla_h__ + + +#ifndef __gen_nsISupports_h__ +#include "nsISupports.h" +#endif + +/* For IDL files that don't want to include root IDL files. */ +#ifndef NS_NO_VTABLE +#define NS_NO_VTABLE +#endif + +/* starting interface: nsIOsmozilla */ +#define NS_IOSMOZILLA_IID_STR "d2d536a0-b6fc-11d5-9d10-0060b0fbd80b" + +#define NS_IOSMOZILLA_IID \ + {0xd2d536a0, 0xb6fc, 0x11d5, \ + { 0x9d, 0x10, 0x00, 0x60, 0xb0, 0xfb, 0xd8, 0x0b }} + +class NS_NO_VTABLE nsIOsmozilla : public nsISupports { + public: + + NS_DEFINE_STATIC_IID_ACCESSOR(NS_IOSMOZILLA_IID) + + /* void Pause (); */ + NS_IMETHOD Pause(void) = 0; + + /* void Play (); */ + NS_IMETHOD Play(void) = 0; + + /* void Stop (); */ + NS_IMETHOD Stop(void) = 0; + + /* void Update (in string type, in string commands); */ + NS_IMETHOD Update(const char *type, const char *commands) = 0; + +}; + +/* Use this macro when declaring classes that implement this interface. */ +#define NS_DECL_NSIOSMOZILLA \ + NS_IMETHOD Pause(void); \ + NS_IMETHOD Play(void); \ + NS_IMETHOD Stop(void); \ + NS_IMETHOD Update(const char *type, const char *commands); + +/* Use this macro to declare functions that forward the behavior of this interface to another object. */ +#define NS_FORWARD_NSIOSMOZILLA(_to) \ + NS_IMETHOD Pause(void) { return _to Pause(); } \ + NS_IMETHOD Play(void) { return _to Play(); } \ + NS_IMETHOD Stop(void) { return _to Stop(); } \ + NS_IMETHOD Update(const char *type, const char *commands) { return _to Update(type, commands); } + +/* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */ +#define NS_FORWARD_SAFE_NSIOSMOZILLA(_to) \ + NS_IMETHOD Pause(void) { return !_to ? NS_ERROR_NULL_POINTER : _to->Pause(); } \ + NS_IMETHOD Play(void) { return !_to ? NS_ERROR_NULL_POINTER : _to->Play(); } \ + NS_IMETHOD Stop(void) { return !_to ? NS_ERROR_NULL_POINTER : _to->Stop(); } \ + NS_IMETHOD Update(const char *type, const char *commands) { return !_to ? NS_ERROR_NULL_POINTER : _to->Update(type, commands); } + +#if 0 +/* Use the code below as a template for the implementation class for this interface. */ + +/* Header file */ +class nsOsmozilla : public nsIOsmozilla +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOSMOZILLA + + nsOsmozilla(); + virtual ~nsOsmozilla(); + /* additional members */ +}; + +/* Implementation file */ +NS_IMPL_ISUPPORTS1(nsOsmozilla, nsIOsmozilla) + +nsOsmozilla::nsOsmozilla() +{ + /* member initializers and constructor code */ +} + +nsOsmozilla::~nsOsmozilla() +{ + /* destructor code */ +} + +/* void Pause (); */ +NS_IMETHODIMP nsOsmozilla::Pause() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void Play (); */ +NS_IMETHODIMP nsOsmozilla::Play() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void Stop (); */ +NS_IMETHODIMP nsOsmozilla::Stop() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void Update (in string type, in string commands); */ +NS_IMETHODIMP nsOsmozilla::Update(const char *type, const char *commands) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* End of implementation class template. */ +#endif + + +#endif /* __gen_nsIOsmozilla_h__ */ diff --git a/applications/osmozilla/nsIOsmozilla.idl b/applications/osmozilla/nsIOsmozilla.idl new file mode 100644 index 0000000..338fb96 --- /dev/null +++ b/applications/osmozilla/nsIOsmozilla.idl @@ -0,0 +1,9 @@ +#include "nsISupports.idl" + +[scriptable, uuid(d2d536a0-b6fc-11d5-9d10-0060b0fbd8bn)] +interface nsIOsmozilla : nsISupports { + void Pause(); + void Play(); + void Stop(); + void Update(in string type, in string commands); +}; diff --git a/applications/osmozilla/nsIOsmozilla.xpt_linux b/applications/osmozilla/nsIOsmozilla.xpt_linux new file mode 100644 index 0000000000000000000000000000000000000000..1c96fbb3cbf5e69ed61129a611f127121875f92a GIT binary patch literal 180 zcmazDaQ64*3aKne^~p@)<&t7#VqjumVAul0N-XaA`q7eo;v=1CZ@soSR>jnUj+U(#`@>-XaA`q7eo;v=1CZ@soSR>jnUj+U(#`@> +#include +#include +#include +#include + +#include "osmozilla.h" + +#include + +nsIServiceManager *gServiceManager = NULL; + + +#define GPAC_PLUGIN_MIMETYPES \ + "audio/mpeg:mp2,mp3,mpga,mpega:MP3 Music;" \ + "audio/x-mpeg:mp2,mp3,mpga,mpega:MP3 Music;" \ + "audio/amr:amr,awb:AMR Audio;" \ + "audio/mp4:mp4,mpg4,mpeg4,m4a:MPEG-4 Audio;" \ + "audio/aac:aac:MPEG-4 AAC Music;" \ + "audio/aacp:aac:MPEG-4 AACPlus Music;" \ + "audio/basic:snd,au:Basic Audio;" \ + "audio/x-wav:wav:WAV Audio;" \ + "audio/3gpp:3gp,3gpp:3GPP/MMS Music;" \ + "audio/3gpp2:3g2,3gp2:3GPP2/MMS Music;" \ + "video/mpeg:mpg,mpeg,mpe,mpv2:MPEG Video;" \ + "video/x-mpeg:mpg,mpeg,mpe,mpv2:MPEG Video;" \ + "video/mpeg-system:mpg,mpeg,mpe,vob,mpv2:MPEG Video;" \ + "video/x-mpeg-system:mpg,mpeg,mpe,vob,mpv2:MPEG Video;" \ + "video/avi:avi:AVI Video;" \ + "video/quicktime:mov,qt:QuickTime Movies;" \ + "video/x-ms-asf:asf,asx:Windows Media Video;" \ + "video/x-ms-wmv:wmv:Windows Media;" \ + "video/mp4:mp4,mpg4:MPEG-4 Video;" \ + "video/3gpp:3gp,3gpp:3GPP/MMS Video;" \ + "video/3gpp2:3g2,3gp2:3GPP2/MMS Video;" \ + "image/jpeg:jpeg,jpg:JPEG Images;" \ + "image/png:png:PNG Images;" \ + "image/bmp:bmp:MS Bitmap Images;" \ + "image/svg+xml:svg,svg.gz,svgz:SVG Document;" \ + "image/x-svgm:svgm:SVGM Document;" \ + "x-subtitle/srt:srt:SRT SubTitles;" \ + "x-subtitle/sub:sub:SUB SubTitles;" \ + "x-subtitle/ttxt:ttxt:GPAC 3GPP TimedText;" \ + "model/vrml:wrl,wrl.gz:VRML World;" \ + "model/x3d+vrml:x3dv,x3dv.gz,x3dvz:X3D/VRML World;" \ + "model/x3d+xml:x3d,x3d.gz,x3dz:X3D/XML World;" \ + "application/ogg:ogg:Ogg Media;" \ + "application/x-ogg:ogg:Ogg Media;" \ + "application/x-bt:bt,bt.gz,btz:MPEG-4 Text (BT);" \ + "application/x-xmt:xmt,xmt.gz,xmtz:MPEG-4 Text (XMT);" \ + "application/mp4:mp4,mpg4:MPEG-4 Movies;" \ + "application/sdp:sdp:Streaming Media Session;" \ + /* explicit plugin call */ \ + "application/x-gpac::GPAC plugin;" \ + + +char* NPP_GetMIMEDescription(void) +{ + return GPAC_PLUGIN_MIMETYPES; +} + +///////////////////////////////////// +// general initialization and shutdown +// +NPError NS_PluginInitialize() +{ + // this is probably a good place to get the service manager + // note that Mozilla will add reference, so do not forget to release + nsISupports *sm = NULL; + + NPN_GetValue(NULL, NPNVserviceManager, &sm); + + // Mozilla returns nsIServiceManager so we can use it directly; doing QI on + // nsISupports here can still be more appropriate in case something is changed + // in the future so we don't need to do casting of any sort. + if (sm) { + sm->QueryInterface(NS_GET_IID(nsIServiceManager), (void **) &gServiceManager); + NS_RELEASE(sm); + } + return NPERR_NO_ERROR; +} + +void NS_PluginShutdown() +{ + // we should release the service manager + NS_IF_RELEASE(gServiceManager); + gServiceManager = NULL; +} + +// get values per plugin +NPError NS_PluginGetValue(NPPVariable aVariable, void *aValue) +{ + NPError err = NPERR_NO_ERROR; + switch (aVariable) { + case NPPVpluginNameString: + *((char **)aValue) = "Osmozilla"; + break; + case NPPVpluginDescriptionString: + *((char **)aValue) = "GPAC Plugin " GPAC_FULL_VERSION " for Mozilla. For more information go to GPAC website"; + break; + default: + err = NPERR_INVALID_PARAM; + break; + } + return err; +} + +///////////////////////////////////////////////////////////// +// +// construction and destruction of our plugin instance object +// +nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct) +{ + if(!aCreateDataStruct) return NULL; + nsOsmozillaInstance * plugin = new nsOsmozillaInstance(aCreateDataStruct); + return plugin; +} + +void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin) +{ + if(aPlugin) delete (nsOsmozillaInstance *)aPlugin; +} + +//////////////////////////////////////// +// +// nsOsmozillaInstance class implementation +// +nsOsmozillaInstance::nsOsmozillaInstance(nsPluginCreateData * aCreateDataStruct) : nsPluginInstanceBase(), + mInstance(aCreateDataStruct->instance) +{ +#ifdef XP_UNIX + mWindow = 0L; + mFontInfo = NULL; + mXtwidget = NULL; +#endif + +#ifdef XP_WIN + m_hWnd = NULL; +#endif + + mScriptablePeer = NULL; + mInitialized = 0; + + + m_szURL = NULL; + m_term = NULL; + m_bIsConnected = 0; + + m_argc=aCreateDataStruct->argc; + m_argv=aCreateDataStruct->argv; + m_argn=aCreateDataStruct->argn; + + SetOptions(); +} + +void nsOsmozillaInstance::SetOptions() +{ + m_bLoop = 0; + m_bAutoStart = 1; + m_bUse3D = 0; + + /*options sent from plugin*/ + for(int i=0;ipdata = NULL; + mInstance = NULL; + } + mInitialized = FALSE; + if (mScriptablePeer != NULL) { + mScriptablePeer->SetInstance(NULL); + NS_IF_RELEASE(mScriptablePeer); + } +} + +static void osmozilla_do_log(void *cbk, u32 level, u32 tool, const char *fmt, va_list list) +{ + FILE *logs = (FILE *) cbk; + vfprintf(logs, fmt, list); + fflush(logs); +} + +NPBool nsOsmozillaInstance::init(NPWindow* aWindow) +{ + unsigned char config_path[GF_MAX_PATH]; + char *gpac_cfg; + const char *str; + + if(aWindow == NULL) return FALSE; + +#ifdef XP_WIN + gpac_cfg = "GPAC.cfg"; +#ifdef _DEBUG +//#if 0 + strcpy((char *) config_path, "C:\\CVS\\gpac\\bin\\w32_deb"); +#else + HKEY hKey = NULL; + DWORD dwSize; + RegOpenKeyEx(HKEY_CLASSES_ROOT, "GPAC", 0, KEY_READ, &hKey); + dwSize = GF_MAX_PATH; + RegQueryValueEx(hKey, "InstallDir", NULL, NULL,(unsigned char*) config_path, &dwSize); + RegCloseKey(hKey); +#endif + +#endif /*XP_WIN*/ + +#ifdef XP_UNIX + gpac_cfg = ".gpacrc"; + strcpy((char *) config_path, getenv("HOME")); +#endif + + memset(&m_user, 0, sizeof(m_user)); + m_user.config = gf_cfg_new((const char *) config_path, gpac_cfg); + /*need to have a valid cfg file for now*/ + if (!m_user.config) goto err_exit; + + str = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + m_user.modules = gf_modules_new(str, m_user.config); + if (!gf_modules_get_count(m_user.modules)) goto err_exit; + + m_user.opaque = this; + + m_disable_mime = 1; + str = gf_cfg_get_key(m_user.config, "General", "NoMIMETypeFetch"); + if (str && !strcmp(str, "no")) m_disable_mime = 0; + + if (SetWindow(aWindow)) mInitialized = TRUE; + + /*check log file*/ + str = gf_cfg_get_key(m_user.config, "General", "LogFile"); + if (str) { + m_logs = fopen(str, "wt"); + gf_log_set_callback(m_logs, osmozilla_do_log); + } + else m_logs = NULL; + + /*set log level*/ + m_log_level = 0; + str = gf_cfg_get_key(m_user.config, "General", "LogLevel"); + if (str) { + if (!stricmp(str, "debug")) m_log_level = GF_LOG_DEBUG; + else if (!stricmp(str, "info")) m_log_level = GF_LOG_INFO; + else if (!stricmp(str, "warning")) m_log_level = GF_LOG_WARNING; + else if (!stricmp(str, "error")) m_log_level = GF_LOG_ERROR; + gf_log_set_level(m_log_level); + } + if (m_log_level && !m_logs) m_logs = stdout; + + /*set log tools*/ + m_log_tools = 0; + str = gf_cfg_get_key(m_user.config, "General", "LogTools"); + if (str) { + char *sep; + char *val = (char *) str; + while (val) { + sep = strchr(val, ':'); + if (sep) sep[0] = 0; + if (!stricmp(val, "core")) m_log_tools |= GF_LOG_CODING; + else if (!stricmp(val, "coding")) m_log_tools |= GF_LOG_CODING; + else if (!stricmp(val, "container")) m_log_tools |= GF_LOG_CONTAINER; + else if (!stricmp(val, "network")) m_log_tools |= GF_LOG_NETWORK; + else if (!stricmp(val, "rtp")) m_log_tools |= GF_LOG_RTP; + else if (!stricmp(val, "author")) m_log_tools |= GF_LOG_AUTHOR; + else if (!stricmp(val, "sync")) m_log_tools |= GF_LOG_SYNC; + else if (!stricmp(val, "codec")) m_log_tools |= GF_LOG_CODEC; + else if (!stricmp(val, "parser")) m_log_tools |= GF_LOG_PARSER; + else if (!stricmp(val, "media")) m_log_tools |= GF_LOG_MEDIA; + else if (!stricmp(val, "scene")) m_log_tools |= GF_LOG_SCENE; + else if (!stricmp(val, "script")) m_log_tools |= GF_LOG_SCRIPT; + else if (!stricmp(val, "interact")) m_log_tools |= GF_LOG_INTERACT; + else if (!stricmp(val, "compose")) m_log_tools |= GF_LOG_COMPOSE; + else if (!stricmp(val, "mmio")) m_log_tools |= GF_LOG_MMIO; + else if (!stricmp(val, "none")) m_log_tools = 0; + else if (!stricmp(val, "all")) m_log_tools = 0xFFFFFFFF; + if (!sep) break; + sep[0] = ':'; + val = sep+1; + } + gf_log_set_tools(m_log_tools); + } + return mInitialized; + +err_exit: + +#ifdef WIN32 + MessageBox(NULL, "GPAC CONFIGURATION FILE NOT FOUND OR INVALID - PLEASE LAUNCH OSMO4 FIRST", "OSMOZILLA FATAL ERROR", MB_OK); +#else + fprintf(stdout, "OSMOZILLA FATAL ERROR\nGPAC CONFIGURATION FILE NOT FOUND OR INVALID\nPLEASE LAUNCH OSMO4 or MP4Client FIRST\n"); +#endif + if (m_user.modules) gf_modules_del(m_user.modules); + m_user.modules = NULL; + if (m_user.config) gf_cfg_del(m_user.config); + m_user.config = NULL; + return FALSE; +} + +void nsOsmozillaInstance::shut() +{ + if (m_szURL) free(m_szURL); + m_szURL = NULL; + if (m_term) { + GF_Terminal *a_term = m_term; + m_term = NULL; + gf_term_del(a_term); + } + if (m_user.modules) gf_modules_del(m_user.modules); + if (m_user.config) gf_cfg_del(m_user.config); + memset(&m_user, 0, sizeof(m_user)); +} + +const char * nsOsmozillaInstance::getVersion() +{ + return NPN_UserAgent(mInstance); +} + +NPError nsOsmozillaInstance::GetValue(NPPVariable aVariable, void *aValue) +{ + NPError rv = NPERR_NO_ERROR; + + switch (aVariable) { + case NPPVpluginScriptableInstance: + { + nsIOsmozilla *scriptablePeer = getScriptablePeer(); + if (scriptablePeer) { + *(nsISupports **) aValue = scriptablePeer; + } else + rv = NPERR_OUT_OF_MEMORY_ERROR; + } + break; + + + case NPPVpluginScriptableIID: + { + static nsIID scriptableIID = NS_IOSMOZILLA_IID; + nsIID *ptr = (nsIID *) NPN_MemAlloc(sizeof(nsIID)); + if (ptr) { + *ptr = scriptableIID; + *(nsIID **) aValue = ptr; + } else + rv = NPERR_OUT_OF_MEMORY_ERROR; + } + break; + + default: + break; + } + return rv; +} + +Bool nsOsmozillaInstance::EventProc(GF_Event *evt) +{ + char msg[1024]; + + if (!m_term) return 0; + + switch (evt->type) { + case GF_EVENT_MESSAGE: + if (!evt->message.message) return 0; + if (evt->message.error) + sprintf((char *)msg, "GPAC: %s (%s)", evt->message.message, gf_error_to_string(evt->message.error)); + else + sprintf((char *)msg, "GPAC: %s", evt->message.message); + + NPN_Status(mInstance, msg); + break; + case GF_EVENT_PROGRESS: + if (evt->progress.done == evt->progress.total) { + NPN_Status(mInstance, ""); + } else { + char *szTitle = ""; + if (evt->progress.progress_type==0) szTitle = "Buffer "; + else if (evt->progress.progress_type==1) szTitle = "Download "; + else if (evt->progress.progress_type==2) szTitle = "Import "; + sprintf(msg, "(GPAC) %s: %02.2f", szTitle, (100.0*evt->progress.done) / evt->progress.total); + NPN_Status(mInstance, msg); + } + break; + + /*IGNORE any scene size, just work with the size allocated in the parent doc*/ + case GF_EVENT_SCENE_SIZE: + gf_term_set_size(m_term, m_width, m_height); + break; + /*window has been resized (full-screen plugin), resize*/ + case GF_EVENT_SIZE: + m_width = evt->size.width; + m_height = evt->size.height; + gf_term_set_size(m_term, m_width, m_height); + break; + case GF_EVENT_CONNECT: + m_bIsConnected = evt->connect.is_connected; + break; + case GF_EVENT_DURATION: + m_bCanSeek = evt->duration.can_seek; + m_Duration = evt->duration.duration; + break; + case GF_EVENT_DBLCLICK: + gf_term_set_option(m_term, GF_OPT_FULLSCREEN, !gf_term_get_option(m_term, GF_OPT_FULLSCREEN)); + break; + case GF_EVENT_KEYDOWN: + if ((evt->key.flags & GF_KEY_MOD_ALT)) { + } else { + switch (evt->key.key_code) { + case GF_KEY_HOME: + gf_term_set_option(m_term, GF_OPT_NAVIGATION_TYPE, 1); + break; + case GF_KEY_ESCAPE: + gf_term_set_option(m_term, GF_OPT_FULLSCREEN, !gf_term_get_option(m_term, GF_OPT_FULLSCREEN)); + break; + } + } + break; + case GF_EVENT_NAVIGATE_INFO: + strcpy(msg, evt->navigate.to_url); + NPN_Status(mInstance, msg); + break; + case GF_EVENT_NAVIGATE: + if (gf_term_is_supported_url(m_term, evt->navigate.to_url, 1, m_disable_mime)) { + gf_term_navigate_to(m_term, evt->navigate.to_url); + return 1; + } else { + u32 i; + char *target = "_self"; + + for (i=0; inavigate.param_count; i++) { + if (!strcmp(evt->navigate.parameters[i], "_parent")) target = "_parent"; + else if (!strcmp(evt->navigate.parameters[i], "_blank")) target = "_blank"; + else if (!strcmp(evt->navigate.parameters[i], "_top")) target = "_top"; + else if (!strcmp(evt->navigate.parameters[i], "_new")) target = "_new"; + else if (!strnicmp(evt->navigate.parameters[i], "_target=", 8)) target = (char *) evt->navigate.parameters[i]+8; + } + NPN_GetURL(mInstance, evt->navigate.to_url, target); + return 1; + } + break; + } + return 0; +} + +Bool Osmozilla_EventProc(void *priv, GF_Event *evt) +{ + nsOsmozillaInstance *gpac = (nsOsmozillaInstance *) priv; + return gpac->EventProc(evt); +} + +NPError nsOsmozillaInstance::SetWindow(NPWindow* aWindow) +{ + if (mInitialized) { + m_width = aWindow->width; + m_height = aWindow->height; + if (m_bIsConnected) gf_term_set_size(m_term, m_width, m_height); + return TRUE; + } + + if(aWindow == NULL) return FALSE; + if (!aWindow->width || !aWindow->height) return FALSE; + + m_width = aWindow->width; + m_height = aWindow->height; + + m_user.EventProc = Osmozilla_EventProc; + +#ifdef XP_WIN + m_user.os_window_handler = aWindow->window; +#endif + +#ifdef XP_UNIX + m_user.os_window_handler = aWindow->window; + /*HACK - although we don't use the display in the X11 plugin, this is used to signal that + the user is mozilla and prevent some X11 calls crashing the browser in file playing mode + (eg, "firefox myfile.mp4" )*/ + m_user.os_display =((NPSetWindowCallbackStruct *)aWindow->ws_info)->display; + XSynchronize((Display *) m_user.os_display, True); + m_user.os_window_handler = aWindow->window; +#endif + + m_prev_time = 0; + m_url_changed = 0; + + m_term = gf_term_new(&m_user); + if (! m_term) return FALSE; + + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, aspect_ratio); + mInitialized = TRUE; + + /*stream not ready*/ + if (!m_szURL || !m_bAutoStart) return TRUE; + + /*connect from 0 and pause if not autoplay*/ + gf_term_connect(m_term, m_szURL); + return TRUE; +} + + + +NPError nsOsmozillaInstance::NewStream(NPMIMEType type, NPStream * stream, + NPBool seekable, uint16 * stype) +{ + if (m_szURL) free(m_szURL); + m_szURL = strdup((const char *)stream->url); + + /*connect from 0 and pause if not autoplay*/ + if (m_bAutoStart) + gf_term_connect(m_term, m_szURL); + + /*we handle data fetching ourselves*/ + *stype = NP_SEEK; + return NPERR_NO_ERROR; +} + +NPError nsOsmozillaInstance::DestroyStream(NPStream * stream, NPError reason) +{ + if (0 && m_szURL) { + gf_term_disconnect(m_term); + free(m_szURL); + m_szURL = NULL; + } + return NPERR_NO_ERROR; +} + +uint16 nsOsmozillaInstance::HandleEvent(void* event) +{ + fprintf(stdout, "event !\n"); + return false; +} + +void nsOsmozillaInstance::Pause() +{ +fprintf(stdout, "pause\n"); + if (m_term) { + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE) == GF_STATE_PAUSED) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + } else { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + } + } +} + +void nsOsmozillaInstance::Play() +{ + if (!m_bIsConnected) { + if (m_szURL) gf_term_connect(m_term, (const char *) m_szURL); + } else { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + } +} + +void nsOsmozillaInstance::Stop() +{ + gf_term_disconnect(m_term); +} + +#ifdef XP_WIN +PBITMAPINFO CreateBitmapInfoStruct(GF_VideoSurface *pfb) +{ + PBITMAPINFO pbmi; + WORD cClrBits; + + cClrBits = 32; + + pbmi = (PBITMAPINFO) LocalAlloc(LPTR, + sizeof(BITMAPINFOHEADER)); + + pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmi->bmiHeader.biWidth = pfb->width; + pbmi->bmiHeader.biHeight = 1; + pbmi->bmiHeader.biPlanes = 1; + pbmi->bmiHeader.biBitCount = cClrBits; + + pbmi->bmiHeader.biCompression = BI_RGB; + pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 + * pbmi->bmiHeader.biHeight; + pbmi->bmiHeader.biClrImportant = 0; + return pbmi; +} +#endif + +void nsOsmozillaInstance::Print(NPPrint* printInfo) +{ + if (printInfo->mode == NP_EMBED) + { + NPEmbedPrint *ep = (NPEmbedPrint *)printInfo; +#ifdef XP_MACOS + /* + ep->platformPrint contains a THPrint reference on MacOS + */ + } +#endif // XP_MACOS +#ifdef XP_UNIX + /* + ep->platformPrint contains a NPPrintCallbackStruct on Unix and + the plug-in location and size in the NPWindow are in page coordinates (720/ inch), but the printer requires point coordinates (72/inch) + */ +#endif // XP_UNIX +#ifdef XP_WIN + /* + The coordinates for the window rectangle are in TWIPS format. + This means that you need to convert the x-y coordinates using the Windows API call DPtoLP when you output text + */ + HDC pDC = (HDC)printInfo->print.embedPrint.platformPrint; + GF_VideoSurface fb; + u32 xsrc, ysrc; + u16 src_16; + char *src; + /*lock the source buffer */ + gf_term_get_screen_buffer(m_term, &fb); + BITMAPINFO *infoSrc = CreateBitmapInfoStruct(&fb); + float deltay = (float)printInfo->print.embedPrint.window.height/(float)fb.height; + int ysuiv = 0; + char *ligne = (char *) LocalAlloc(GMEM_FIXED, fb.width*4); + for (ysrc=0; ysrc> 8) & 0xf8; + dst[2] += dst[2]>>5; + dst[1] = (src_16 >> 3) & 0xfc; + dst[1] += dst[1]>>6; + dst[0] = (src_16 << 3) & 0xf8; + dst[0] += dst[0]>>5; + src+=2; + break; + case GF_PIXEL_RGB_555: + src_16 = * (u16 *)src; + dst[2] = (src_16 >> 7) & 0xf8; + dst[2] += dst[2]>>5; + dst[1] = (src_16 >> 2) & 0xf8; + dst[1] += dst[1]>>5; + dst[0] = (src_16 << 3) & 0xf8; + dst[0] += dst[0]>>5; + src+=2; + break; + } + dst += 4; + } + int ycrt = ysuiv; + ysuiv = (u32) ( ((float)ysrc+1.0)*deltay); + int delta = ysuiv-ycrt; + StretchDIBits( + pDC, printInfo->print.embedPrint.window.x, ycrt+printInfo->print.embedPrint.window.y, printInfo->print.embedPrint.window.width, + delta, + 0, 0, fb.width, 1, + ligne, infoSrc, DIB_RGB_COLORS, SRCCOPY); + } + + /*unlock GPAC frame buffer */ + gf_term_release_screen_buffer(m_term, &fb); + /* free temporary objects */ + GlobalFree(ligne); + LocalFree(infoSrc); +#endif // XP_WIN + } else if (printInfo->mode == NP_FULL) + { + NPFullPrint *ep = (NPFullPrint *)printInfo; + // TODO present the print dialog and manage the print + } +} + +#include +void nsOsmozillaInstance::Update(const char *type, const char *commands) +{ + if (m_term) { + GF_Err e = gf_term_scene_update(m_term, (char *) type, (char *) commands); + if (e) { + char szMsg[1024]; + sprintf((char *)szMsg, "GPAC: Error applying update (%s)", gf_error_to_string(e) ); + NPN_Status(mInstance, szMsg); + } + } +} + + +// Scriptability related code + +nsOsmozillaPeer *nsOsmozillaInstance::getScriptablePeer() +{ +fprintf(stdout, "get peer\n"); + if (!mScriptablePeer) { + mScriptablePeer = new nsOsmozillaPeer(this); + if (!mScriptablePeer) return NULL; + NS_ADDREF(mScriptablePeer); + } + NS_ADDREF(mScriptablePeer); + return mScriptablePeer; +} + +static NS_DEFINE_IID(kIZillaPluginIID, NS_IOSMOZILLA_IID); +static NS_DEFINE_IID(kIClassInfoIID, NS_ICLASSINFO_IID); +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); + +nsOsmozillaPeer::nsOsmozillaPeer(nsOsmozillaInstance * aPlugin) +{ + mPlugin=aPlugin; + mRefCnt = 0; +} + +nsOsmozillaPeer::~nsOsmozillaPeer() +{ +} + // Notice that we expose our claim to implement nsIClassInfo. +//NS_IMPL_ISUPPORTS2(nsOsmozillaPeer, nsITestPlugin, nsIClassInfo) + + // the following method will be callable from JavaScript +NS_IMETHODIMP nsOsmozillaPeer::Pause() { mPlugin->Pause(); return NS_OK; } +NS_IMETHODIMP nsOsmozillaPeer::Play() { mPlugin->Play(); return NS_OK; } +NS_IMETHODIMP nsOsmozillaPeer::Stop() { mPlugin->Stop(); return NS_OK; } + +NS_IMETHODIMP nsOsmozillaPeer::Update(const char *type, const char *commands) +{ + mPlugin->Update(type, commands); + return NS_OK; +} + +void nsOsmozillaPeer::SetInstance(nsOsmozillaInstance * plugin) +{ + mPlugin = plugin; +} + +NS_IMETHODIMP_(nsrefcnt) nsOsmozillaPeer::AddRef() +{ + ++mRefCnt; + return mRefCnt; +} + +NS_IMETHODIMP_(nsrefcnt) nsOsmozillaPeer::Release() +{ + --mRefCnt; + if (mRefCnt == 0) { + delete this; + return 0; + } + return mRefCnt; +} + +// here nsOsmozillaPeer should return three interfaces it can be asked for by their iid's +// static casts are necessary to ensure that correct pointer is returned +NS_IMETHODIMP nsOsmozillaPeer::QueryInterface(const nsIID & aIID, + void **aInstancePtr) +{ + if (!aInstancePtr) + return NS_ERROR_NULL_POINTER; + + if (aIID.Equals(kIZillaPluginIID)) { + *aInstancePtr = NS_STATIC_CAST(nsIOsmozilla *, this); + AddRef(); + return NS_OK; + } + + if (aIID.Equals(kIClassInfoIID)) { + *aInstancePtr = NS_STATIC_CAST(nsIClassInfo *, this); + AddRef(); + return NS_OK; + } + + if (aIID.Equals(kISupportsIID)) { + *aInstancePtr = NS_STATIC_CAST(nsISupports *, (NS_STATIC_CAST (nsIOsmozilla *, this))); + AddRef(); + return NS_OK; + } + return NS_NOINTERFACE; +} + diff --git a/applications/osmozilla/osmozilla.def b/applications/osmozilla/osmozilla.def new file mode 100644 index 0000000..5d5bbb1 --- /dev/null +++ b/applications/osmozilla/osmozilla.def @@ -0,0 +1,6 @@ +LIBRARY nposmozilla + +EXPORTS + NP_GetEntryPoints @1 + NP_Initialize @2 + NP_Shutdown @3 diff --git a/applications/osmozilla/osmozilla.h b/applications/osmozilla/osmozilla.h new file mode 100644 index 0000000..d00619b --- /dev/null +++ b/applications/osmozilla/osmozilla.h @@ -0,0 +1,241 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __PLUGIN_H__ +#define __PLUGIN_H__ + +#include + +#include "npplat.h" +#include "nsIOsmozilla.h" + +#include + +#ifndef WIN32 +#include +#include +#include +#endif + +class nsOsmozillaPeer; + +struct nsPluginCreateData +{ + NPP instance; + NPMIMEType type; + uint16 mode; + int16 argc; + char** argn; + char** argv; + NPSavedData* saved; +}; + +class nsPluginInstanceBase +{ +public: + // these three methods must be implemented in the derived + // class platform specific way + virtual NPBool init(NPWindow* aWindow) = 0; + virtual void shut() = 0; + virtual NPBool isInitialized() = 0; + + // implement all or part of those methods in the derived + // class as needed + virtual NPError SetWindow(NPWindow* pNPWindow) { return NPERR_NO_ERROR; } + virtual NPError NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype) { return NPERR_NO_ERROR; } + virtual NPError DestroyStream(NPStream *stream, NPError reason) { return NPERR_NO_ERROR; } + virtual void StreamAsFile(NPStream* stream, const char* fname) { return; } + virtual int32 WriteReady(NPStream *stream) { return 0x0fffffff; } + virtual int32 Write(NPStream *stream, int32 offset, + int32 len, void *buffer) { return len; } + virtual void Print(NPPrint* printInfo) { return; } + virtual uint16 HandleEvent(void* event) { return 0; } + virtual void URLNotify(const char* url, NPReason reason, + void* notifyData) { return; } + virtual NPError GetValue(NPPVariable variable, void *value) { return NPERR_NO_ERROR; } + virtual NPError SetValue(NPNVariable variable, void *value) { return NPERR_NO_ERROR; } +}; + +// functions that should be implemented for each specific plugin + +// creation and destruction of the object of the derived class +nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct); +void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin); + +// global plugin initialization and shutdown +NPError NS_PluginInitialize(); +void NS_PluginShutdown(); + +// global to get plugins name & description +NPError NS_PluginGetValue(NPPVariable aVariable, void *aValue); +char* NPP_GetMIMEDescription(void); + + +//#define NO_GPAC + +class nsOsmozillaInstance : public nsPluginInstanceBase +{ +public: + + nsOsmozillaInstance(nsPluginCreateData * aCreateDataStruct); + virtual ~nsOsmozillaInstance(); + + NPBool init(NPWindow* aWindow); + void shut(); + NPBool isInitialized() {return mInitialized;} + + + NPError SetWindow(NPWindow* aWindow); + NPError NewStream(NPMIMEType type, NPStream * stream, NPBool seekable,uint16 * stype); + NPError DestroyStream(NPStream * stream, NPError reason); + NPError GetValue(NPPVariable aVariable, void *aValue); + virtual uint16 HandleEvent(void* event); + + + nsOsmozillaPeer * getScriptablePeer(); + + // locals + const char * getVersion(); + + int m_argc; + char **m_argv; + char **m_argn; + nsOsmozillaPeer *mScriptablePeer; + + void Pause(); + void Play(); + void Stop(); + void Update(const char *type, const char *commands); + void Print(NPPrint* printInfo); + Bool EventProc(GF_Event *evt); + + +private: + NPP mInstance; + NPBool mInitialized; + +#ifdef XP_WIN + HWND m_hWnd; +#endif +#ifdef XP_UNIX + Window mWindow; + Display *mDisplay; + XFontStruct *mFontInfo; + Widget mXtwidget; +#endif + +#ifdef XP_MACOSX + NPWindow *window; +#endif + + /*general options*/ + Bool m_bLoop, m_bAutoStart, m_bIsConnected; + + GF_Terminal *m_term; + GF_User m_user; + + char *m_szURL; + + Bool m_isopen, m_paused, m_url_changed, m_bUse3D, m_disable_mime; + u32 max_duration, m_log_level, m_log_tools, aspect_ratio; + FILE *m_logs; + Bool m_bCanSeek; + Double m_Duration; + uint32 m_height, m_width; + unsigned char *m_navigate_url; + u32 current_time_ms, m_prev_time; + Float current_FPS; + + void SetOptions(); +}; + +// We must implement nsIClassInfo because it signals the + // Mozilla Security Manager to allow calls from JavaScript. + // helper class to implement all necessary nsIClassInfo method stubs + // and to set flags used by the security system + +class nsClassInfoMixin : public nsIClassInfo + { + // These flags are used by the DOM and security systems to signal that + // JavaScript callers are allowed to call this object's scritable methods. + NS_IMETHOD GetFlags(PRUint32 *aFlags) + {*aFlags = nsIClassInfo::PLUGIN_OBJECT | nsIClassInfo::DOM_OBJECT; + return NS_OK;} + + NS_IMETHOD GetImplementationLanguage(PRUint32 *aImplementationLanguage) + {*aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS; + return NS_OK;} + + // The rest of the methods can safely return error codes... + NS_IMETHOD GetInterfaces(PRUint32 *count, nsIID * **array) + {return NS_ERROR_NOT_IMPLEMENTED;} + NS_IMETHOD GetHelperForLanguage(PRUint32 language, nsISupports **_retval) + {return NS_ERROR_NOT_IMPLEMENTED;} + NS_IMETHOD GetContractID(char * *aContractID) + {return NS_ERROR_NOT_IMPLEMENTED;} + NS_IMETHOD GetClassDescription(char * *aClassDescription) + {return NS_ERROR_NOT_IMPLEMENTED;} + NS_IMETHOD GetClassID(nsCID * *aClassID) + {return NS_ERROR_NOT_IMPLEMENTED;} + NS_IMETHOD GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) + {return NS_ERROR_NOT_IMPLEMENTED;} + }; + + +class nsOsmozillaPeer : public nsIOsmozilla , public nsClassInfoMixin +{ +public: + nsOsmozillaPeer(nsOsmozillaInstance * aPlugin); + virtual ~nsOsmozillaPeer(); + + // methods from nsISupports + NS_IMETHOD QueryInterface(const nsIID & aIID, void **aInstancePtr); + NS_IMETHOD_(nsrefcnt) AddRef(); + NS_IMETHOD_(nsrefcnt) Release(); + +public: + NS_DECL_NSIOSMOZILLA + void SetInstance(nsOsmozillaInstance * plugin); + +protected: + nsrefcnt mRefCnt; + nsOsmozillaInstance * mPlugin; +}; + + +#endif // __PLUGIN_H__ diff --git a/applications/osmozilla/osmozilla.png b/applications/osmozilla/osmozilla.png new file mode 100644 index 0000000000000000000000000000000000000000..6f66a17b1edda041995dfdee6d4baae64b040a71 GIT binary patch literal 121239 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 Gxb=Tf0re~Z literal 0 HcmV?d00001 diff --git a/applications/osmozilla/osmozilla.rc b/applications/osmozilla/osmozilla.rc new file mode 100644 index 0000000..18be328 --- /dev/null +++ b/applications/osmozilla/osmozilla.rc @@ -0,0 +1,124 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winresrc.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1252) +#endif //_WIN32 + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", " \0" + VALUE "FileDescription", "Osmozilla allows playback of many media and rich media files. For more information, visit gpac.sourceforge.net\0" + VALUE "FileExtents", "*|aac|wrl,wrl.gz|x3dv,x3dv.gz,x3dvz|x3d,x3d.gz,x3dz|svg,svg.gz,svgz|mpg,mpeg,mp2,mpa,mpe,mpv2|asf,wma,wmv,asx,asr|avi|mp4,mpg4|mp4|m4a|3gp,3gpp|3gp,3gpp|3g2,3gp2|3g2,3gp2|mp2,mp3,mpga,mpega|ogg|sdp\0" + VALUE "FileOpenName", "GPAC Plugin|AAC Music|VRML World|X3D/VRML World|X3D/XML World|SVG Document|MPEG Video|WindowsMedia Movies|AVI Movies|MPEG-4 Videos|MPEG-4 Movies|MPEG-4 Music|3GPP Movies|3GPP Music|3GPP2 Movies|3GPP2 Music|MP3 Music|OGG Movies|SDP Session\0" + VALUE "FileVersion", "0.4.5\0" + VALUE "InternalName", "nposmozilla\0" + VALUE "LegalCopyright", "Copyright © GPAC 2005-2007\0" + VALUE "LegalTrademarks", "\0" + VALUE "MIMEType", "application/x-gpac|audio/aac|model/vrml|model/x3d+vrml|model/x3d+xml|image/svg+xml|video/mpeg|video/x-ms-asf|video/avi|video/mp4|application/mp4|audio/mp4|video/3gpp|audio/3gpp|video/3gpp2|audio/3gpp2|audio/mpeg|application/ogg|application/sdp\0" + VALUE "OriginalFilename", "nposmozilla.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Osmozilla - GPAC Plugin for Mozilla\0" + VALUE "ProductVersion", "0.4.5\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // !_MAC + +#endif // Neutral resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""winresrc.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/applications/osmozilla/readme.txt b/applications/osmozilla/readme.txt new file mode 100644 index 0000000..7a61b5c --- /dev/null +++ b/applications/osmozilla/readme.txt @@ -0,0 +1,8 @@ +to regenerate plugin interface from IDL: + +xpidl -m header -I path_to\gecko-sdk\xpcom\idl nsIOsmozilla.idl +xpidl -m typelib -I path_to\gecko-sdk\xpcom\idl nsIOsmozilla.idl +xpt_link nposmozilla.xpt nsIOsmozilla.xpt + +This MUST be done for win32 and linux OSs independently, an .xpt file generated on one OS is not compatible with another OS... +Please keep the w32 file "nsIOsmozilla.xpt_w32" and the linux one "nsIOsmozilla.xpt_linux" to bear with makefiles... \ No newline at end of file diff --git a/applications/osmozilla/resource.h b/applications/osmozilla/resource.h new file mode 100644 index 0000000..8d3b519 --- /dev/null +++ b/applications/osmozilla/resource.h @@ -0,0 +1,21 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by osmozilla.rc +// +#define IDD_MAIN 101 +#define IDC_BUTTON_GO 1002 +#define IDC_STATIC_UA 1003 +#define IDC_BUTTON1 1005 +#define IDC_BUTTON_DONT 1005 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1006 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/applications/standalone2drender/standalone2drender.c b/applications/standalone2drender/standalone2drender.c new file mode 100644 index 0000000..e6870dd --- /dev/null +++ b/applications/standalone2drender/standalone2drender.c @@ -0,0 +1,172 @@ +/* + * GPAC Multimedia Framework + * + * Authors: Cyril Concolato + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / standalone 2D rendering lib (render2D + FT + RAW OUT + soft raster ) + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "standalone2drender.h" +#include +#include +#include "render2d.h" +#include "stacks2d.h" +#include "visualsurface2d.h" +#include "ft_font.h" + +void SR_ResetFrameRate(GF_Compositor *); +GF_Raster2D *EVG_LoadRenderer(); +GF_VideoOutput *NewRawVideoOutput(); +GF_VisualRenderer *NewVisualRenderer(); +GF_Err R2D_GetSurfaceAccess(VisualSurface2D *surf); +void R2D_ReleaseSurfaceAccess(VisualSurface2D *surf); +Bool R2D_SupportsFormat(VisualSurface2D *surf, u32 pixel_format); +void R2D_DrawBitmap(VisualSurface2D *surf, struct _gf_sc_texture_handler *txh, GF_IRect *clip, GF_Rect *unclip, u8 alpha, u32 *col_key, GF_ColorMatrix *cmat); +GF_FontRaster *FT_Load(); +void FT_Delete(GF_FontRaster *); + +static GF_Err SA2DR_InitFontEngine(GF_FontRaster *dr) +{ + FTBuilder *ftpriv = (FTBuilder *)dr->priv; + + /*inits freetype*/ + if (FT_Init_FreeType(&ftpriv->library) ) return GF_IO_ERR; + + /*remove the final delimiter*/ +#if defined(WIN32) +#pragma message("using C:\\WINDOWS\\Fonts directory for fonts") + ftpriv->font_dir = strdup("C:\\WINDOWS\\Fonts"); +#else if defined(_WIN32_WCE) +#pragma message("using \\Windows directory for fonts") + ftpriv->font_dir = strdup("\\Windows"); +#endif + strcpy(ftpriv->font_serif,"Arial"); + strcpy(ftpriv->font_sans,"Times New Roman"); + strcpy(ftpriv->font_fixed,"Courier New"); + return GF_OK; +} + +static void SA2DR_SetFontEngine(GF_Compositor *sr) +{ + GF_FontRaster *ifce = FT_Load(); + + /*cannot init font engine*/ + if (SA2DR_InitFontEngine(ifce) != GF_OK) { + FT_Delete(ifce); + return; + } + sr->font_engine = ifce; +} + +static GF_Err SA2DR_LoadRenderer(GF_VisualRenderer *vr, GF_Compositor *compositor) +{ + Render2D *sr; + if (vr->user_priv) return GF_BAD_PARAM; + + sr = malloc(sizeof(Render2D)); + if (!sr) return GF_OUT_OF_MEM; + memset(sr, 0, sizeof(Render2D)); + + sr->compositor = compositor; + + sr->strike_bank = gf_list_new(); + sr->surfaces_2D = gf_list_new(); + + sr->top_effect = malloc(sizeof(RenderEffect2D)); + memset(sr->top_effect, 0, sizeof(RenderEffect2D)); + sr->top_effect->sensors = gf_list_new(); + sr->sensors = gf_list_new(); + + /*and create main surface*/ + sr->surface = NewVisualSurface2D(); + sr->surface->GetSurfaceAccess = R2D_GetSurfaceAccess; + sr->surface->ReleaseSurfaceAccess = R2D_ReleaseSurfaceAccess; + + sr->surface->DrawBitmap = R2D_DrawBitmap; + sr->surface->SupportsFormat = R2D_SupportsFormat; + sr->surface->render = sr; + gf_list_add(sr->surfaces_2D, sr->surface); + + sr->zoom = sr->scale_x = sr->scale_y = 1.0; + vr->user_priv = sr; + + /*load options*/ + //sr->top_effect->trav_flags |= TF_RENDER_DIRECT; + sr->scalable_zoom = 1; + sr->enable_yuv_hw = 0; + return GF_OK; +} + +GF_Compositor *SR_NewStandaloneRenderer() +{ + GF_Compositor *tmp; + GF_SAFEALLOC(tmp, GF_Compositor) + tmp->user, GF_User) + + tmp->visual_renderer = NewVisualRenderer(); + tmp->aspect_ratio = GF_ASPECT_RATIO_FILL_SCREEN; + + memset(&cfg, 0, sizeof(cfg)); + cfg.double_buffered = 1; + + tmp->video_out = NewRawVideoOutput(); + tmp->video_out->evt_cbk_hdl = tmp; + tmp->video_out->on_event = NULL; + + tmp->r2d = EVG_LoadRenderer(); + + /*and init*/ + if (SA2DR_LoadRenderer(tmp->visual_renderer, tmp) != GF_OK) { + tmp->video_out->Shutdown(tmp->video_out); + free(tmp); + return NULL; + } + + tmp->mx = gf_mx_new(""Renderer"); + tmp->textures = gf_list_new(); + tmp->frame_rate = 30.0; + tmp->frame_duration = 33; + tmp->time_nodes = gf_list_new(); + tmp->events = gf_list_new(); + tmp->ev_mx = gf_mx_new("Events"); + + SR_ResetFrameRate(tmp); + + /*set font engine if any*/ + SA2DR_SetFontEngine(tmp); + + tmp->extra_scenes = gf_list_new(); + tmp->interaction_level = GF_INTERACT_NORMAL | GF_INTERACT_INPUT_SENSOR; + tmp->antiAlias = GF_ANTIALIAS_FULL; + return tmp; +} + +void SR_DeleteStandaloneRenderer(GF_Compositor *tmp) +{ + tmp->visual_renderer->UnloadRenderer(tmp->visual_renderer); + free(tmp->visual_renderer); + DeleteVideoOutput(tmp->video_out); + EVG_ShutdownRenderer(tmp->r2d); + ft_shutdown_font_engine(tmp->font_engine); + FT_Delete(tmp->font_engine); + free(tmp->user); + free(tmp); +} + diff --git a/applications/standalone2drender/standalone2drender.h b/applications/standalone2drender/standalone2drender.h new file mode 100644 index 0000000..a7c2b66 --- /dev/null +++ b/applications/standalone2drender/standalone2drender.h @@ -0,0 +1,16 @@ +#ifndef _STANDALONE2DRENDER_H +#define _STANDALONE2DRENDER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +GF_Compositor *SR_NewStandaloneRenderer(); + +#ifdef __cplusplus +} +#endif + +#endif //_STANDALONE2DRENDER_H \ No newline at end of file diff --git a/applications/testapps/beng_test/BifsEngineTester.dsp b/applications/testapps/beng_test/BifsEngineTester.dsp new file mode 100644 index 0000000..37329d7 --- /dev/null +++ b/applications/testapps/beng_test/BifsEngineTester.dsp @@ -0,0 +1,103 @@ +# Microsoft Developer Studio Project File - Name="BifsEngineTester" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=BifsEngineTester - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "BifsEngineTester.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "BifsEngineTester.mak" CFG="BifsEngineTester - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "BifsEngineTester - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "BifsEngineTester - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "BifsEngineTester - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../../../bin/w32_rel/BifsEngineTester.exe" + +!ELSEIF "$(CFG)" == "BifsEngineTester - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /out:"../../../bin/w32_deb/BifsEngineTester.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "BifsEngineTester - Win32 Release" +# Name "BifsEngineTester - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CmdLineTst.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/applications/testapps/beng_test/CmdLineTst.c b/applications/testapps/beng_test/CmdLineTst.c new file mode 100644 index 0000000..bd94426 --- /dev/null +++ b/applications/testapps/beng_test/CmdLineTst.c @@ -0,0 +1,89 @@ +#include + +GF_Err SampleCallBack(void *calling_object, char *data, u32 size, u32 ts) +{ + fprintf(stdout, "Received at time %d, buffer %d bytes long.\n", ts, size); + return GF_OK; +} + +int main(int argc, char **argv) +{ + int i; + GF_BifsEngine *codec1 = NULL; + GF_BifsEngine * codec2 = NULL; + if (0) { + char *config; + u32 config_size; + char update[] = "\n AT \n 500 \n { \n REPLACE \n M.emissiveColor BY 1 0 0 } \n"; + + codec1 = gf_beng_init(NULL, argv[1]); + gf_beng_get_stream_config(codec1, &config, &config_size); + fprintf(stdout, "EncodedBifsConfig size is %d \n", config_size); + + gf_beng_encode_context(codec1, SampleCallBack); + gf_beng_save_context(codec1, "initial_context.mp4"); + gf_beng_encode_from_string(codec1, (char *) update, SampleCallBack); + gf_beng_save_context(codec1, "non_aggregated_context.mp4"); + gf_beng_aggregate_context(codec1); + gf_beng_save_context(codec1, "aggregated_context.mp4"); + gf_beng_terminate(codec1); + } else if (1) { + char *config; + u32 config_size; + char scene[] = "OrderedGroup {children [Background2D {backColor 1 1 1}Shape {appearance Appearance {material DEF M Material2D {emissiveColor 0 0 1 filled TRUE } } geometry Rectangle { size 100 75 } } ] }"; + char update[] = "\n AT \n 500 \n { \n REPLACE \n M.emissiveColor BY 1 0 0 \n REPLACE \n M.filled BY FALSE} \n"; + + codec1 = gf_beng_init_from_string(NULL, scene, 200, 200, 1); + gf_beng_get_stream_config(codec1, &config, &config_size); + fprintf(stdout, "EncodedBifsConfig size is %d \n", config_size); + + gf_beng_encode_context(codec1, SampleCallBack); + gf_beng_save_context(codec1, "initial_context.mp4"); + gf_beng_encode_from_string(codec1, (char *) update, SampleCallBack); + gf_beng_save_context(codec1, "non_aggregated_context.mp4"); + gf_beng_aggregate_context(codec1); + gf_beng_save_context(codec1, "aggregated_context.mp4"); + gf_beng_terminate(codec1); + } else { + + for (i = 0; i <10; i++) { + char context_rootname[] = "rect_context"; + char in_context[100], + bt_out_na_context[100], bt_out_agg_context[100], + mp4_out_na_context[100], mp4_out_agg_context[100]; + char update[1000];// = "REPLACE M.emissiveColor BY 1 1 0"; + char timed_update[1000]; + + sprintf(update, "REPLACE M.emissiveColor BY %f 0 0", i/10.0f); + + if (i != 0) { + sprintf(in_context, "na_%s_%i.bt", context_rootname, i); + } else { + strcpy(in_context, "rect.bt"); + } + + codec2 = gf_beng_init(NULL, in_context); + + sprintf(timed_update, "AT %i { %s }", 1000 + i, update); + + gf_beng_encode_from_string(codec2, timed_update, SampleCallBack); + + sprintf(mp4_out_na_context, "na_%s_%i.mp4", context_rootname, i+1); + sprintf(bt_out_na_context, "na_%s_%i.bt", context_rootname, i+1); + sprintf(mp4_out_agg_context, "agg_%s_%i.mp4", context_rootname, i+1); + sprintf(bt_out_agg_context, "agg_%s_%i.bt", context_rootname, i+1); + + gf_beng_save_context(codec2, mp4_out_na_context); + gf_beng_save_context(codec2, bt_out_na_context); + gf_beng_aggregate_context(codec2); + gf_beng_save_context(codec2, mp4_out_agg_context); + gf_beng_save_context(codec2, bt_out_agg_context); + + gf_beng_terminate(codec2); + } + fprintf(stdout, "Done.\n"); + } + return 0; +} + + diff --git a/applications/testapps/beng_test/rect-update.bt b/applications/testapps/beng_test/rect-update.bt new file mode 100644 index 0000000..cae8ca4 --- /dev/null +++ b/applications/testapps/beng_test/rect-update.bt @@ -0,0 +1,18 @@ +AT 1000 { + REPLACE REC.size BY 50 50 +} +AT 2000 { + REPLACE M.emissiveColor BY 1 1 1 +} +AT 3000 { + REPLACE M.filled BY FALSE +} +AT 4000 { + REPLACE REC.size BY 30 20 +} +AT 5000 { + DELETE REC +} +AT 6000 { + DELETE M +} diff --git a/applications/testapps/beng_test/rect.bt b/applications/testapps/beng_test/rect.bt new file mode 100644 index 0000000..9058a32 --- /dev/null +++ b/applications/testapps/beng_test/rect.bt @@ -0,0 +1,42 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + nodeIdBits 10 + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + Shape { + appearance Appearance { + material DEF M Material2D { + emissiveColor 0.5 0.6 0.4 + filled TRUE + } + } + geometry DEF REC Rectangle { + size 50 100 + } + } + ] +} diff --git a/applications/testapps/broadcaster/Makefile b/applications/testapps/broadcaster/Makefile new file mode 100644 index 0000000..0efdbe7 --- /dev/null +++ b/applications/testapps/broadcaster/Makefile @@ -0,0 +1,37 @@ +include ../../../config.mak + +vpath %.c $(SRC_PATH)/applications/testapps/broadcaster + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +SOURCES= +APPNAME=broadcaster + + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS=RTP_serv_generator.o RTP_serv_packetizer.o RTP_serv_sender.o broadcaster.o sdp_generator.o +LIBS=-lgpac + +all: broadcaster + +broadcaster: $(OBJS) + @$(CC) -o $(APPNAME) $(OBJS) $(LIBS) + +.c.o: + @$(CC) -g -c $(CFLAGS) $*.c + + +clean: + -rm -f $(OBJS) $(APPNAME) *~ + + diff --git a/applications/testapps/broadcaster/RTP_serv_generator.c b/applications/testapps/broadcaster/RTP_serv_generator.c new file mode 100644 index 0000000..35f7271 --- /dev/null +++ b/applications/testapps/broadcaster/RTP_serv_generator.c @@ -0,0 +1,229 @@ +/* + RTP Server + Projet MIX + Module de generation de l'horloge +*/ + +#include "RTP_serv_generator.h" + +/*Inutile car plus de variables globales*/ +#define VERIFY_INITIALIZED if (!initialized) { printf("Error : SampleCallBack Called before initialization\n"); exit(255);} +int initialized = 0; // Il n'y a plus de variables globales donc ce flag est inutile. + +char in_context[100]; +char update[1000];// = "REPLACE M.emissiveColor BY 1 1 0"; +char timed_update[1000]; + +extern GF_Err SampleCallBack(void *calling_object, char *data, u32 size, u64 ts); + +PNC_CallbackData * PNC_Init_SceneGenerator(GF_RTPChannel * p_chan, GF_RTPHeader * p_hdr, char * default_scene, int socketPort) { + PNC_CallbackData * data = malloc(sizeof(PNC_CallbackData)); + int * i ; + + data->chan = p_chan; + data->hdr=p_hdr; + + /*Charger la scene initiale == le contexte*/ + data->codec= gf_beng_init(data, default_scene); + fprintf(stdout, "[carrousel] : command socket: listening on port %d\n", socketPort); + data->socket = gf_sk_new(GF_SOCK_TYPE_UDP); // on cree le socket UDP de commande + fprintf(stdout, "[carrousel] : socket bind: %d\n", gf_sk_bind(data->socket, NULL, socketPort, NULL, 0, 0)); + // fprintf(stdout, "[carrousel] : socket connect: %d\n", gf_sk_connect(data->socket, "127.0.0.1", socketPort, NULL)); + // fprintf(stdout, "[carrousel] : socket listen: %d\n", gf_sk_listen(data->socket, 1)); + fprintf(stdout, "[carrousel] : gf_sk_set_block_mode: %d\n", gf_sk_set_block_mode(data->socket,0)); + + data->extension = malloc(sizeof(PNC_CallbackExt)); + ((PNC_CallbackExt * )data->extension)->i = 0; + ((PNC_CallbackExt * )data->extension)->lastTS = 0; + i = & ((PNC_CallbackExt * )data->extension)->i; + + initialized = 1; // ici on est initialisé (flag) + + return data; +} + +void PNC_SendInitScene(PNC_CallbackData * data){ + + // on peut gerer l'update de la scene par defaut. + /// TODO: Il va falloir gerer le mecanisme Carousel + data->RAP=1; //On demande que RAP soit positionné dans le SL + data->SAUN_inc=1; //On demande que SAUN soit incrementé car + //on ne pourra pas faire les prochaines mises a jour sans ... + fprintf(stdout, "[gpaclib] : "); + gf_beng_encode_context(data->codec, SampleCallBack); +} + +void PNC_Close_SceneGenerator(PNC_CallbackData * data) { + // terminer + if (data->extension) free(data->extension); + gf_beng_terminate(data->codec); + gf_rtp_del(data->chan); + PNC_ClosePacketizer(data); + free(data); +} + +/* fonction callcak appellee quand la compilation du BT est faite */ +GF_Err SampleCallBack(void *calling_object, char *au, u32 size, u64 ts) +{ + PNC_CallbackData * data ; + VERIFY_INITIALIZED; + + data = (PNC_CallbackData *) calling_object; + + ///*the sample object*/ + ////typedef struct tagM4Sample + //{ + // /*data size*/ + // u32 dataLength; + // /*data with padding if requested*/ + // char *data; + // /*decoding time*/ + // u32 DTS; + // /*relative offset for composition if needed*/ + // u32 CTS_Offset; + // /*Random Access Point flag - 1 is regular RAP (read/write) , 2 is SyncShadow (read mode only)*/ + // u8 IsRAP; + //} M4Sample; + PNC_ProcessData(data, au, size, ts); // on passe la main au packetizer ... + + return GF_OK; +} + +GF_Err PNC_RAP(PNC_CallbackData * data) { + /// envoyer le RAP initial; + data->RAP = 1; + gf_beng_aggregate_context(data->codec); + gf_beng_encode_context(data->codec, SampleCallBack); + data->RAP = 0; + return GF_OK; +} + +int PNC_int_isFinished(char * bsBuffer, u32 bsBufferSize) { + /* Fonction interne, sorte de parseur de directives + * Non exportée dans le .h */ + char * buff = malloc(sizeof(char)*bsBufferSize+5); + char * sstr; + int retour=(int)PNC_RET_RTP_STREAM_NOOP; + + *buff = 0; + + if (! buff) printf("ERREUR 1 %s %d \n", __FILE__, __LINE__); + if (! bsBuffer) printf("ERREUR 2 %s %d \n", __FILE__, __LINE__); + + memcpy(buff, bsBuffer, bsBufferSize); + buff[bsBufferSize]=0; + if ( *buff != '\0') + { + sstr = strstr(buff, "#_RTP_STREAM_"); // On cherche le prefixe des directives ... + + if (sstr) + { + //On en tient une ;) + sstr+=13; //On mange le prefixe + // printf("strStr $%s$ $%s$ %d\n",sstr ,PNC_STR_RTP_STREAM_RAP, strcmp(sstr, PNC_STR_RTP_STREAM_RAP) ); + if (strcmp(sstr, PNC_STR_RTP_STREAM_SEND_CRITICAL)==0) retour= (int)PNC_RET_RTP_STREAM_SEND_CRITICAL; + + if (strcmp(sstr, PNC_STR_RTP_STREAM_SEND)==0) retour= (int)PNC_RET_RTP_STREAM_SEND; + if (strcmp(sstr, PNC_STR_RTP_STREAM_RAP)==0) retour= (int)PNC_RET_RTP_STREAM_RAP; + if (strcmp(sstr, PNC_STR_RTP_STREAM_RAP_RESET)==0) retour= (int)PNC_RET_RTP_STREAM_RAP_RESET; + + free(buff); return retour; + } + } + free(buff); + return (int)PNC_RET_RTP_STREAM_NOOP; +} + +GF_Err PNC_processBIFSGenerator(PNC_CallbackData * data) { + unsigned char buffer[66000]; + u32 byteRead=0; + GF_BitStream * bs = gf_bs_new(NULL, 0,GF_BITSTREAM_WRITE); // dernier champ = mode ?!? no doc + unsigned char *bsBuffer; + // char bsCommande[66000]; + // struct timespec heure; + u32 bsSize=0; + int retour=0; + GF_Err e; + /* ici, nous allons attendre (bloquant) sur le socket, et quand on recoit + * des données, on les range. quand on recoit un ordre, on l'execute sur les données. + */ + + while (!(retour = PNC_int_isFinished((char *) buffer, byteRead))) + { + e = gf_sk_receive(data->socket, buffer, 66000, 0, & byteRead); + /* le receive semble pas bloquant alors que l'API l'annonce */ + switch (e) + { + case GF_IP_NETWORK_EMPTY: + gf_bs_del(bs); + return GF_OK; + case GF_OK: + break; + default: + fprintf(stdout, "[carrousel] : erreur de socket : %d\n", e); + gf_bs_del(bs); + return e; + } + // if (byteRead > 0) printf("Octets recus: %d \n", byteRead); + gf_bs_write_data(bs, buffer, byteRead); + } + + gf_bs_write_data(bs, (unsigned char *) "\0", 1); // on met le terminateur de chaine + gf_bs_get_content(bs, &bsBuffer, &bsSize); + //update=malloc(sizeof(char) * (bsSize + 20)); + //trash = clock_gettime(CLOCK_REALTIME, &heure); + //offset = heure.tv_sec*1000 + heure.tv_nsec/1000/1000; + //sprintf(update, "AT %d {%s}", offset, bsBuffer); + //printf("%f\n",(float)offset/1000.0); + /* les données sont pretes*/ + + // mutex + while(gf_mx_try_lock(data->carrousel_mutex) == 0) + { + gf_sleep(1); + } + + fprintf(stdout, "[carrousel] : received -> "); + switch (retour) + { + case PNC_RET_RTP_STREAM_SEND: + fprintf(stdout, "RTP STREAM SEND\n"); + /* on encode la chaine, qui est bien NULL Terminated */ + // sprintf(bsCommande, "AT 1{%s}", bsBuffer); + // fprintf(stdout, "COMMANDE : %s\n", bsCommande); + fprintf(stdout, "[gpaclib] : "); + e = gf_beng_encode_from_string(data->codec, (char *) bsBuffer, SampleCallBack); + fprintf(stdout, "beng result %d\n",e); + break; + + case PNC_RET_RTP_STREAM_SEND_CRITICAL: + fprintf(stdout, "RTP STREAM SEND CRITICAL\n"); + // sprintf(bsCommande, "AT 1{%s}", bsBuffer); + // fprintf(stdout, "COMMANDE : %s\n", bsCommande); + data->SAUN_inc=1; + fprintf(stdout, "[gpaclib] : "); + gf_beng_encode_from_string(data->codec, (char *) bsBuffer, SampleCallBack); + break; + + case PNC_RET_RTP_STREAM_RAP: + data->RAP=1; + data->RAPsent++; + fprintf(stdout, "RTP STREAM RAP\n"); + fprintf(stdout, "[gpaclib] : "); + gf_beng_aggregate_context(data->codec); + gf_beng_encode_context(data->codec, SampleCallBack); + break; + case PNC_RET_RTP_STREAM_RAP_RESET: + data->RAP=1; //On demande que RAP soit positionné dans le SL + data->RAPsent++; + fprintf(stdout, "RTP STREAM RAP\n"); + data->SAUN_inc=1; // On demande l'augmentation du SAUN + fprintf(stdout, "[gpaclib] : "); + gf_beng_aggregate_context(data->codec); + gf_beng_encode_context(data->codec, SampleCallBack); + break; + } + // unlocking mutex + gf_mx_v(data->carrousel_mutex); + return GF_OK; +} diff --git a/applications/testapps/broadcaster/RTP_serv_generator.h b/applications/testapps/broadcaster/RTP_serv_generator.h new file mode 100644 index 0000000..fd9777c --- /dev/null +++ b/applications/testapps/broadcaster/RTP_serv_generator.h @@ -0,0 +1,60 @@ +#include +#if 0 +#include +#endif +#include +#include // Pour les sockets +#include +#include + +#include "RTP_serv_packetizer.h" +#include +#ifndef __RTP_SERV_CLOCK +#define __RTP_SERV_CLOCK + + +#define PNC_RET_RTP_STREAM_NOOP 0 +#define PNC_RET_RTP_STREAM_SEND 1 +#define PNC_RET_RTP_STREAM_SEND_CRITICAL 2 +#define PNC_RET_RTP_STREAM_RAP 3 +#define PNC_RET_RTP_STREAM_RAP_RESET 4 + +#define PNC_STR_RTP_STREAM_SEND "SEND\n" +#define PNC_STR_RTP_STREAM_SEND_CRITICAL "SEND_CRITICAL\n" +#define PNC_STR_RTP_STREAM_RAP "RAP\n" +#define PNC_STR_RTP_STREAM_RAP_RESET "RAP_RESET\n" + +/*Le type passe pour le callback (permet la reentrance)*/ +typedef struct tmp_PNC_CallbackData { + GF_RTPChannel * chan; + GF_RTPHeader * hdr; + char * formatedPacket; + int formatedPacketLength; + GP_RTPPacketizer *rtpBuilder; + void * codec; + GF_Socket *socket; /// Socket pour recevoir les demandes de mise à jour. + GF_Socket *feedback_socket; // socket pour envoyer les données de retour débit à l'interface + void * extension; + int RAP; + int RAPsent; + int SAUN_inc; // On incremente le SequAUNumber de l'entete SL + GF_Mutex *carrousel_mutex; +} PNC_CallbackData; + + +typedef struct tmp_PNC_CallbackExt { + int i; + int lastTS; +} PNC_CallbackExt; + + +/*Les fonctions exportees*/ +extern GF_Err PNC_RAP(PNC_CallbackData * data); +extern PNC_CallbackData* PNC_Init_SceneGenerator(GF_RTPChannel * p_chan, GF_RTPHeader * p_hdr, char * default_scene, int socketPort); +extern GF_Err PNC_processBIFSGenerator(PNC_CallbackData*); +extern void PNC_Close_SceneGenerator(PNC_CallbackData*); + +extern void PNC_SendInitScene(PNC_CallbackData * data); + + +#endif diff --git a/applications/testapps/broadcaster/RTP_serv_packetizer.c b/applications/testapps/broadcaster/RTP_serv_packetizer.c new file mode 100644 index 0000000..eb93207 --- /dev/null +++ b/applications/testapps/broadcaster/RTP_serv_packetizer.c @@ -0,0 +1,147 @@ +/* +RTP Server +Projet MIX + +Generation des paquets RTP +*/ + + +/* Ici on va prendre un buffer et le préparer pour l'envoyer, cad le passer + a RTPBuilder pour qu'il fasse les entetes, qu'il splitte + Ensuite on concatene l'entete avec le contenu et on envoie. + +*/ + + + +#include "RTP_serv_packetizer.h" +#include "RTP_serv_sender.h" + +#include +#include +#include + + +void OnNewPacket(void *cbk, GF_RTPHeader *header){ + // + + // Ici on remet le packet courant à vide. + ((PNC_CallbackData *)cbk)->formatedPacketLength = 0; + +} +void OnPacketDone(void *cbk, GF_RTPHeader *header) { + // + printf("OK Paquet généré ->>>"); + //Ici on peut proceder à l'envoi du packet courant. + + + PNC_SendRTP(((PNC_CallbackData *)cbk), ((PNC_CallbackData *)cbk)->formatedPacket, ((PNC_CallbackData *)cbk)->formatedPacketLength); + + ((PNC_CallbackData *)cbk)->formatedPacketLength = 0; + +} + +void OnData(void *cbk, char *data, u32 data_size, Bool is_head){ + + memcpy( ((PNC_CallbackData *)cbk)->formatedPacket+((PNC_CallbackData *)cbk)->formatedPacketLength, data, data_size); + ((PNC_CallbackData *)cbk)->formatedPacketLength+=data_size; + + // Ici on concatene ce qu'on vient de recevoir au packet courant. + +} + +void PNC_InitPacketiser(PNC_CallbackData * data, char *sdp_fmt){ + + // char sdp_fmt[5000]; + GP_RTPPacketizer *p; + GF_SLConfig sl; + memset(&sl, 0, sizeof(sl)); + sl.useTimestampsFlag = 1; + sl.useRandomAccessPointFlag = 1; + sl.timestampResolution = 1000; + sl.AUSeqNumLength = 16; // Peut etre descendu a 7 pour gagner 1 octet ... + // PIPOCANAJA + + + p = gf_rtp_builder_new(GF_RTP_PAYT_MPEG4, + &sl, + GP_RTP_PCK_SIGNAL_RAP | GP_RTP_PCK_SIGNAL_AU_IDX, + data, + OnNewPacket, + OnPacketDone, + NULL, + OnData); + + // M4RTP_InitBuilder(p, 96, 1488, 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, NULL); + gf_rtp_builder_init(p, 96, 1470, 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, NULL); + + gf_rtp_builder_format_sdp(p, "mpeg4-generic", sdp_fmt, NULL, 0); + + // fprintf(stdout, "%s\n", sdp_fmt); + p->rtp_header.Version=2; + p->rtp_header.SSRC=rand();//RandomNumber identifiant la source + + data->hdr=& p->rtp_header; + data->rtpBuilder=p; + data->formatedPacket = malloc(2000); //buffer pour les paquets + data->formatedPacketLength = 0; +} + +void PNC_ClosePacketizer(PNC_CallbackData *data) +{ + free(data->formatedPacket); + gf_rtp_builder_del(data->rtpBuilder); +} + +GF_Err PNC_ProcessData(PNC_CallbackData * data, char *au, u32 size, u64 ts) { + /* Prepare le paquet en mettant à jour le hdr qui contiendra le numero de sequence courant, + le timestamp correct ...*/ + /*PacketID represente le numero du paquet dans le cas d'une fragmentation en plusieurs paquet RTP + d'une data*/ + + // ici on met le timestamp pour que les modifications arrivent bien a etre affiches + data->hdr->TimeStamp = (u32) gf_sys_clock(); // ts; + + //Dans samp->IsRAP + // /*Random Access Point flag - 1 is regular RAP (read/write) , 2 is SyncShadow (read mode only)*/ + // u8 IsRAP; + + if (! data->rtpBuilder) { + printf(" data->rtpBuilder Null\n\n");exit(-1);} + + // ici on met le timestamp pour que les modifications arrivent bien a etre affiches + data->rtpBuilder->sl_header.compositionTimeStamp = (u32) gf_sys_clock(); // ts; + + data->rtpBuilder->sl_header.randomAccessPointFlag = data->RAP; + + if (data->SAUN_inc) // Si on a demandé d'incrémenter, on incrémente :) + data->rtpBuilder->sl_header.AU_sequenceNumber ++ ; + + data->RAP=0; + data->SAUN_inc=0; + + data->rtpBuilder->sl_header.paddingBits = 0; + + + gf_rtp_builder_process(data->rtpBuilder, au, size, 1, size, 0, 0); + +#if 0 + + while (remaining_length > RTP_packet_overflow) { + data->hdr->SequenceNumber++; + + PNC_SendRTP(data, current_data, RTP_packet_overflow); + + current_data += RTP_packet_overflow; + remaining_length -= RTP_packet_overflow; + } + +#endif + + + + // data->hdr->SequenceNumber++; + //PNC_SendRTP(data, current_data, remaining_length); + + return GF_OK; +} diff --git a/applications/testapps/broadcaster/RTP_serv_packetizer.h b/applications/testapps/broadcaster/RTP_serv_packetizer.h new file mode 100644 index 0000000..5c9611a --- /dev/null +++ b/applications/testapps/broadcaster/RTP_serv_packetizer.h @@ -0,0 +1,15 @@ +#ifndef __RTP_SERV_PACKETISER +#define __RTP_SERV_PACKETISER + + +#include +#include "gpac/bifsengine.h" // Pour M4Sample +#include "RTP_serv_generator.h" + +/*Les fonctions exportees*/ +void PNC_InitPacketiser(PNC_CallbackData * data, char *sdp_fmt); +GF_Err PNC_ProcessData(PNC_CallbackData * data, char *au, u32 size, u64 ts); +void PNC_ClosePacketizer(PNC_CallbackData *data); + + +#endif diff --git a/applications/testapps/broadcaster/RTP_serv_sender.c b/applications/testapps/broadcaster/RTP_serv_sender.c new file mode 100644 index 0000000..2d7f2ce --- /dev/null +++ b/applications/testapps/broadcaster/RTP_serv_sender.c @@ -0,0 +1,66 @@ +#include "RTP_serv_sender.h" +#include +#include + + +GF_Err PNC_InitRTP(GF_RTPChannel **chan, char * dest, int port){ + GF_Err res; + GF_RTSPTransport tr; + + *chan = gf_rtp_new(); + printf("[carrousel] : RTP_SetupPorts=%d\n", gf_rtp_set_ports(*chan, 0)); + + tr.destination = dest; + tr.IsUnicast = gf_sk_is_multicast_address(dest) ? 0 : 1; + tr.Profile="RTP/AVP";//RTSP_PROFILE_RTP_AVP; + tr.IsRecord = 0; + tr.Append = 0; + tr.source = "0.0.0.0"; + tr.SSRC=rand(); + + tr.port_first = port; + tr.port_last = port+1; + if (tr.IsUnicast) { + tr.client_port_first = port; + tr.client_port_last = port+1; + } else { + tr.source = dest; + } + res = gf_rtp_setup_transport(*chan, &tr, dest); + printf("[carrousel] : RTP_SetupTransport=%d\n", res); + if (res !=0) return res; + + res = gf_rtp_initialize(*chan, 0, 1, 1500, 0, 0, NULL); + printf("[carrousel] : RTP_Initialize=%d\n", res); + if (res !=0) return res; + return GF_OK; +} + + +GF_Err PNC_SendRTP(PNC_CallbackData * data, char * payload, int payloadSize){ + GF_Err e; + unsigned char feedback_buffer[250]; // buffer pour envoyer le nombre de byte envoyé + + if (!data->hdr->TimeStamp) + data->hdr->TimeStamp = ((PNC_CallbackExt * )data->extension)->lastTS; + + ((PNC_CallbackExt * )data->extension)->lastTS = data->hdr->TimeStamp; + + e = gf_rtp_send_packet(data->chan, data->hdr, 0, 0, payload, payloadSize); + + fprintf(stdout, " SendPacket : %d, TimeStamp RTP = %d\n", e, data->hdr->TimeStamp); + + // sending feedback bytes + memset(feedback_buffer, 0, sizeof(feedback_buffer)); + sprintf((char *) feedback_buffer, "DataSent=%d\nRAPsent=%d\n", payloadSize, data->RAPsent); + // fprintf(stdout, "DataSent=%d\nRAPsent=%d\n", payloadSize, data->RAPsent); + e = gf_sk_send(data->feedback_socket, feedback_buffer, strlen((char *) feedback_buffer)); + fprintf(stdout, "[carrousel] : sent feedback data %d byte, return %d\n", payloadSize, e); + + return GF_OK; +} + +GF_Err PNC_CloseRTP(GF_RTPChannel *chan){ + gf_rtp_del(chan); + return GF_OK; +} diff --git a/applications/testapps/broadcaster/RTP_serv_sender.h b/applications/testapps/broadcaster/RTP_serv_sender.h new file mode 100644 index 0000000..381c755 --- /dev/null +++ b/applications/testapps/broadcaster/RTP_serv_sender.h @@ -0,0 +1,14 @@ +#ifndef __RTP_SERV_SENDER +#define __RTP_SERV_SENDER + +#include /// Pour GF_Err ... +#include "RTP_serv_generator.h" + +extern void test_RTP_serv_send(); + +extern GF_Err PNC_InitRTP(GF_RTPChannel **chan, char * dest, int port); +extern GF_Err PNC_SendRTP(PNC_CallbackData * data, char * payload, int payloadSize); +extern GF_Err PNC_CloseRTP(GF_RTPChannel *chan); + + +#endif diff --git a/applications/testapps/broadcaster/broadcaster.c b/applications/testapps/broadcaster/broadcaster.c new file mode 100644 index 0000000..7dd18ef --- /dev/null +++ b/applications/testapps/broadcaster/broadcaster.c @@ -0,0 +1,488 @@ +#include "broadcaster.h" + + +extern GF_Err SampleCallBack(void *calling_object, char *data, u32 size, u64 ts); + +/* fonction de gestion de la ligne de commande */ +void command_line_parsing(int* argc, char** argv, int* tcp_port, char *config_file, int *config_flag) +{ + int argument, counter; + char value[MAX_BUF]; + + // cas de tous parametres necessaires + if ((*argc) == 3) + { + /* parsing des parametres de la ligne de commande */ + argument=-1; + for(counter = 0; counter < ((*argc) - 2); counter = counter+2) + { + argument = server_command_line(argv[counter+1], argv[counter+2], value, argument); + if (argument == 0) (*tcp_port) = atoi(value); + // if (argument == 1) (*udp_port) = atoi(value); + if (argument == 2) + { + strcpy(config_file, value); + (*config_flag) = 1; + } + } + } + else + { + print_usage(); + exit(0); + } +} + +/* fonction pour la gestion de base de la ligne de commande */ +int server_command_line(char *arg_a, char *arg_b, char *value, int argument) +{ + char flag; + sscanf(arg_a, "-%c", &flag); + strcpy(value, arg_b); + switch (flag) + { + case 'p': + argument = 0; + break; + /* + case 'u': + argument = 1; + break; + */ + case 'f': + argument = 2; + break; + default: + print_usage(); + argument = -1; + break; + } + return argument; +} + +/* gestion usage */ +void print_usage(void) +{ + fprintf(stdout, "[broadcaster] usage : ./broadcaster [-p tcp_port] [-f fichier_config]\n"); + fprintf(stdout, "[broadcaster] usage : il faut specifier un fichier de configuration ou un port TCP pour l'interface GUI\n"); +} + +/* fonction pour envoyer les messages RAP */ +u32 RAP_send(void *par) +{ + RAP_Input *input = par; + PNC_CallbackData *data = input->data; + u32 *timer; + + input->status = 1; + while(input->status==1) { + // mutex avec le thread qui envoie les RAP pour l'envoi avec carrousel + while(gf_mx_try_lock(input->carrousel_mutex) == 0) + { + gf_sleep(1); + } + // locking + // gf_mx_p(input->carrousel_mutex); + // ici il faut integrer la fonction du carrousel pour envoyer RAP + /* envoi de RAP */ + timer = input->RAPtimer; + data->RAPsent++; + fprintf(stdout, "[broadcaster] : RAP %d seconds\n", *(input->RAPtimer)); // *timer); + data->RAP=1; //On demande que RAP soit positionné dans le SL + fprintf(stdout, "[gpaclib] : "); + gf_beng_aggregate_context(data->codec); + gf_beng_encode_context(data->codec, SampleCallBack); + // unlocking + gf_mx_v(input->carrousel_mutex); + // gf_sleep(input->RAPtimer); + gf_sleep(*timer*1000); + } + input->status = 2; + return GF_OK; +} + +/* gestion tcp pour interface gui */ +u32 tcp_server(void *par) +{ + TCP_Input *input = par; + u32 *timer = input->RAPtimer; + char buffer[MAX_BUF]; + unsigned char temp[MAX_BUF]; + FILE *fp; + u32 byte_read; + int ret; + GF_Config *gf_config_file; + GF_Socket *TCP_socket; + GF_Socket *conn_socket; + GF_Err e; + + input->status = 1; + + TCP_socket = gf_sk_new(GF_SOCK_TYPE_TCP); + e = gf_sk_bind(TCP_socket, NULL, input->port, NULL, 0, 0); + e = gf_sk_listen(TCP_socket, 1); + e = gf_sk_set_block_mode(TCP_socket, 0); + e = gf_sk_server_mode(TCP_socket, 0); + + while(input->status == 1) { + memset(buffer, 0, sizeof(buffer)); + e = gf_sk_accept(TCP_socket, &conn_socket); + if (e == GF_OK) { + memset(buffer, 0, sizeof(buffer)); + e = gf_sk_receive(conn_socket, buffer, MAX_BUF, 0, &byte_read); + } + + switch (e) { + case GF_IP_NETWORK_EMPTY: + gf_sleep(33); + continue; + case GF_OK: + break; + default: + fprintf(stdout, "[broadcaster] : Error with TCP socket : %d\n", e); + exit(1); + break; + } + + if((*(input->config_flag)) == 0) { + + u32 num_retry; + + /* waiting for the configuration info */ + fp = fopen("temp.cfg", "w+"); + if (!fp) { + fprintf(stdout, "[broadcaster] : Error opening temp file for the configuration\n"); + exit(1); + } + ret = fwrite(buffer, 1, byte_read, fp); + fclose(fp); + + /* parsing config info */ + gf_config_file = gf_cfg_new(".", "temp.cfg"); + input->config->scene_init_file = gf_cfg_get_key(gf_config_file, MAIN_SECTION, SCENE_INIT); + input->config->rap_timer = gf_cfg_get_key(gf_config_file, MAIN_SECTION, RAP_TIMER); + input->config->config_input_port = gf_cfg_get_key(gf_config_file, MAIN_SECTION, PORT_CONFIG); + input->config->modif_input_port = gf_cfg_get_key(gf_config_file, MAIN_SECTION, PORT_MODIF); + + input->config->dest_ip = gf_cfg_get_key(gf_config_file, DEST_SECTION, DEST_ADDRESS); + input->config->dest_port = gf_cfg_get_key(gf_config_file, DEST_SECTION, PORT_OUTPUT); + + input->config->feedback_ip = gf_cfg_get_key(gf_config_file, FEEDBACK_SECTION, IP_FEEDBACK); + input->config->feedback_port = gf_cfg_get_key(gf_config_file, FEEDBACK_SECTION, PORT_FEEDBACK); + + /* Acknowledging the configuration */ + gf_sk_send(conn_socket, "OK\n", 3); + + memset(temp, 0, sizeof(temp)); + fp = fopen(input->config->scene_init_file, "w+"); + if (!fp) { + fprintf(stdout, "[broadcaster] : Error opening temp file for the initial scene\n"); + exit(1); + } + num_retry=10; + + while (1) { + + GF_Err e = gf_sk_receive(conn_socket, temp, sizeof(temp), 0, &byte_read); + + if (e == GF_OK) { + fwrite(temp, 1, byte_read, fp); + + } else if (e==GF_IP_NETWORK_EMPTY) { + + num_retry--; + + if (!num_retry) + + break; + + gf_sleep(1); + + } else + + break; + } + fclose(fp); + *(input->config_flag) = 1; + } + /* we only wait now for the config updates */ + if ( (*(input->config_flag)) == 1) { + ret = sscanf(buffer, "DelaiMax=%d\n", timer); + fprintf(stdout, "[broadcaster] : RAP timer changed, now : %d\n", *timer); + } + gf_sk_del(conn_socket); + } + + input->status = 2; + return GF_OK; +} + +u8 get_a_char(); +Bool has_input(); + +int main (int argc, char** argv) +{ + GF_Err e; + Bool run; + int tcp_port; + u32 config_flag; // pour savoir s'il faut lire la configuration du fichier ou de l'interface + char config_file[MAX_BUF]; + + TCP_Input *tcp_conf; + RAP_Input *rap_conf; + CONF_Data *conf; + + /* parametres de configuration */ + GF_Config *gf_config_file; + GF_Thread *tcp_thread; + GF_Thread *rap_thread; + GF_Err th_err_tcp; + GF_Err th_err_rap; + GF_Err res; + + GF_Socket *UDP_feedback_socket; + + PNC_CallbackData * data; + GF_RTPChannel * chan; + GF_RTPHeader hdr; + u32 timer; + + GF_Mutex *carrousel_mutex; // objet en mutex + char sdp_fmt[5000]; + + /* init gpac lib */ + gf_sys_init(); + + /* allocation structures */ + GF_SAFEALLOC(tcp_conf, TCP_Input) + GF_SAFEALLOC(rap_conf, RAP_Input) + GF_SAFEALLOC(conf, CONF_Data) + + /* gestion la ligne de commande */ + tcp_port = config_flag = 0; + command_line_parsing(&argc, argv, &tcp_port, config_file, (int *) &config_flag); + // fprintf(stdout, "[broadcaster] : tcp:%d, udp:%d, config_flag:%d\n", tcp_port, udp_port, config_flag); + + /* controle si les deux parametres necessaires ont ete specifies */ + tcp_conf->config_flag = &config_flag; + + gf_config_file = NULL; + + /* controle pour savoir ou il faut prendre la configuration */ + if(config_flag == 1) + { + /* ici il faut lire du fichier de configuration */ + gf_config_file = gf_cfg_new(NULL, config_file); + conf->scene_init_file = gf_cfg_get_key(gf_config_file, MAIN_SECTION, SCENE_INIT); + conf->rap_timer = gf_cfg_get_key(gf_config_file, MAIN_SECTION, RAP_TIMER); + conf->config_input_port = gf_cfg_get_key(gf_config_file, MAIN_SECTION, PORT_CONFIG); + tcp_port = atoi(conf->config_input_port); + conf->modif_input_port = gf_cfg_get_key(gf_config_file, MAIN_SECTION, PORT_MODIF); + + conf->dest_ip = gf_cfg_get_key(gf_config_file, DEST_SECTION, DEST_ADDRESS); + conf->dest_port = gf_cfg_get_key(gf_config_file, DEST_SECTION, PORT_OUTPUT); + + conf->feedback_ip = gf_cfg_get_key(gf_config_file, FEEDBACK_SECTION, IP_FEEDBACK); + conf->feedback_port = gf_cfg_get_key(gf_config_file, FEEDBACK_SECTION, PORT_FEEDBACK); + } + + // thread pour ecouter les données de l'interface tcp (et configuration si config_flag == 0) + + // il faut definir le poineur ici pour que apres tout ça marche bien + tcp_conf->RAPtimer = &timer; + tcp_conf->port = tcp_port; + tcp_conf->config = conf; // dans le cas ou on prend la configuration de l'interface ici on peut modifier les donnees + tcp_thread = gf_th_new("TCPInterface"); + th_err_tcp = gf_th_run(tcp_thread, tcp_server, tcp_conf); + + if(config_flag == 0) + fprintf(stdout, "[broadcast] : en attente de la configuration de l'interface...\n"); + + // ici il faut attendre que les donnees arrivent de l'interface + while(config_flag == 0) + { + gf_sleep(1000); + } + fprintf(stdout, "[broadcast] : configuration OK, init serveur\n"); + + // setting the timer RAP + // cette partie change dans le cas ou on prend la configuratoin de l'interface + timer = atoi(conf->rap_timer); + + // a ce point la nous avons la configuration du serveur + // initialisation carrousel + // Initialisation de la connexion + // ici il faut donner un adresse de broadcast + res = PNC_InitRTP(&chan, (char *)conf->dest_ip, atoi(conf->dest_port)); + if (res != 0) + { + printf("[carrousel] : erreur d'initialisation RTP -> M4Err = %d\n", res); + exit(1); + } + + // objet mutex + carrousel_mutex = gf_mx_new("Carrousel"); + + // recupere l'objet data, necessaire pour toutes operations du carrousel + data = PNC_Init_SceneGenerator(chan, &hdr, (char *) conf->scene_init_file, atoi(conf->modif_input_port)); // la generation de scene + data->carrousel_mutex = carrousel_mutex; // ici on passe l'objet mutex à l'objet data + data->RAPsent = 1; + + // preparation du socket pour envoyer le données de débit à l'interface + UDP_feedback_socket = gf_sk_new(GF_SOCK_TYPE_UDP); + e = gf_sk_bind(UDP_feedback_socket, NULL, (u16) atoi(conf->feedback_port), (char *) conf->feedback_ip, (u16) atoi(conf->feedback_port), 0); + // fprintf(stdout, "[broadcaster] : bind udp for feedback : %d, port : %d\n", e, atoi(port_feedback)); + //e = gf_sk_connect(UDP_feedback_socket, (char *) conf->feedback_ip, (u16) atoi(conf->feedback_port, NULL)); + // fprintf(stdout, "[broadcaster] : connect udp feedback socket : %d, host : %s\n", e, ip_feedback); + gf_sk_set_block_mode(UDP_feedback_socket, 0); + data->feedback_socket = UDP_feedback_socket; + + PNC_InitPacketiser(data, sdp_fmt); // le packetiser, qui initialise le RTPBuilder, les headers, etc. + PNC_SendInitScene(data); // ici on envoie le RAP initial + + // ouvrir thread boucle pour envoi rap + rap_conf->RAPtimer = &timer; + rap_conf->carrousel_mutex = carrousel_mutex; // ici on passe l'objet mutex + rap_conf->data = data; + rap_thread = gf_th_new("RAPGenerator"); + th_err_rap = gf_th_run(rap_thread, RAP_send, rap_conf); + + // generation du fichier SDP + sdp_generator(data, conf->dest_ip, sdp_fmt); + + // udp boucle pour les données de modification + run = 1; + while (run) { + + // cette fonction est modifié par rapport à la fonction du carrousel normale + // car elle integre le control sur la variable mutex pour envoyer en exclusion + // avec le thread des RAP + GF_Err e = PNC_processBIFSGenerator(data); + if (e) break; + + if (has_input()) { + char c = get_a_char(); + switch (c) { + case 'q': + run = 0; + break; + } + } + gf_sleep(10); + } + rap_conf->status = 0; + while (rap_conf->status != 2) + gf_sleep(0); + + tcp_conf->status = 0; + while (tcp_conf->status != 2) + gf_sleep(0); + + /* nettoyage final */ + PNC_Close_SceneGenerator(data); + + free(tcp_conf); + free(rap_conf); + free(conf); + fprintf(stdout, "[broadcaster] : structures freed\n"); + + if (gf_config_file) + gf_cfg_del(gf_config_file); + gf_delete_file("temp.cfg"); + + gf_th_del(tcp_thread); + gf_th_del(rap_thread); + + gf_mx_del(carrousel_mutex); + gf_sys_close(); + return 0; +} + /*seems OK under mingw also*/ +#ifdef WIN32 +#include +#include +Bool has_input() +{ + return kbhit(); +} +u8 get_a_char() +{ + return getchar(); +} +void set_echo_off(Bool echo_off) +{ + DWORD flags; + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(hStdin, &flags); + if (echo_off) flags &= ~ENABLE_ECHO_INPUT; + else flags |= ENABLE_ECHO_INPUT; + SetConsoleMode(hStdin, flags); +} +#else +/*linux kbhit/getchar- borrowed on debian mailing lists, (author Mike Brownlow)*/ +#include + +static struct termios t_orig, t_new; +static s32 ch_peek = -1; + +void init_keyboard() +{ + tcgetattr(0, &t_orig); + t_new = t_orig; + t_new.c_lflag &= ~ICANON; + t_new.c_lflag &= ~ECHO; + t_new.c_lflag &= ~ISIG; + t_new.c_cc[VMIN] = 1; + t_new.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t_new); +} +void close_keyboard(Bool new_line) +{ + tcsetattr(0,TCSANOW, &t_orig); + if (new_line) fprintf(stdout, "\n"); +} + +void set_echo_off(Bool echo_off) +{ + init_keyboard(); + if (echo_off) t_orig.c_lflag &= ~ECHO; + else t_orig.c_lflag |= ECHO; + close_keyboard(0); +} + +Bool has_input() +{ + u8 ch; + s32 nread; + + init_keyboard(); + if (ch_peek != -1) return 1; + t_new.c_cc[VMIN]=0; + tcsetattr(0, TCSANOW, &t_new); + nread = read(0, &ch, 1); + t_new.c_cc[VMIN]=1; + tcsetattr(0, TCSANOW, &t_new); + if(nread == 1) { + ch_peek = ch; + return 1; + } + close_keyboard(0); + return 0; +} + +u8 get_a_char() +{ + u8 ch; + if (ch_peek != -1) { + ch = ch_peek; + ch_peek = -1; + close_keyboard(1); + return ch; + } + read(0,&ch,1); + close_keyboard(1); + return ch; +} + +#endif + diff --git a/applications/testapps/broadcaster/broadcaster.dsp b/applications/testapps/broadcaster/broadcaster.dsp new file mode 100644 index 0000000..a0890b7 --- /dev/null +++ b/applications/testapps/broadcaster/broadcaster.dsp @@ -0,0 +1,138 @@ +# Microsoft Developer Studio Project File - Name="broadcaster" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=broadcaster - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "broadcaster.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "broadcaster.mak" CFG="broadcaster - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "broadcaster - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "broadcaster - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "broadcaster - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "broadcaster - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 zlib.lib winmm.lib ws2_32.lib user32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../../bin/w32_deb/broadcaster.exe" /pdbtype:sept /libpath:"../../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "broadcaster - Win32 Release" +# Name "broadcaster - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\broadcaster.c +# End Source File +# Begin Source File + +SOURCE=.\RTP_serv_generator.c +# End Source File +# Begin Source File + +SOURCE=.\RTP_serv_packetizer.c +# End Source File +# Begin Source File + +SOURCE=.\RTP_serv_sender.c +# End Source File +# Begin Source File + +SOURCE=.\sdp_generator.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\broadcaster.h +# End Source File +# Begin Source File + +SOURCE=.\RTP_serv_generator.h +# End Source File +# Begin Source File + +SOURCE=.\RTP_serv_packetizer.h +# End Source File +# Begin Source File + +SOURCE=.\RTP_serv_sender.h +# End Source File +# Begin Source File + +SOURCE=.\sdp_generator.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/applications/testapps/broadcaster/broadcaster.h b/applications/testapps/broadcaster/broadcaster.h new file mode 100644 index 0000000..d999d10 --- /dev/null +++ b/applications/testapps/broadcaster/broadcaster.h @@ -0,0 +1,84 @@ +/* includes default */ +#include +#include +#include + +/* includes pour gpac library*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* include pour carrousel BIFS */ +/* pour les envois RTP */ +#include "RTP_serv_sender.h" +/* pour la mise en paquets */ +#include "RTP_serv_packetizer.h" +/* le module applicatif */ +#include "RTP_serv_generator.h" + +/* include pour SDP generation */ +#include "sdp_generator.h" + +/* definitions */ +#define MAX_BUF 4096 + +/* données de configuration */ +#define MAIN_SECTION "Broadcaster" +#define SCENE_INIT "InitialScene" +#define RAP_TIMER "RAPPeriod" +#define PORT_CONFIG "ConfigPort" +#define PORT_MODIF "SceneUpdatePort" + +#define DEST_SECTION "Destination" +#define DEST_ADDRESS "IP" +#define PORT_OUTPUT "Port" + +#define FEEDBACK_SECTION "Feedback" +#define IP_FEEDBACK "IP" +#define PORT_FEEDBACK "Port" + +/* structure pour les données de configuration du serveur */ +typedef struct config_data +{ + const char *rap_timer; + const char *scene_init_file; + const char *modif_input_port; + const char *config_input_port; + + const char *feedback_ip; + const char *feedback_port; + + const char *dest_ip; + const char *dest_port; +} CONF_Data; + +typedef struct tcp_input +{ + u16 port; // port sur laquelle ouvrir le serveur + u32 *config_flag; // pour savoir si le serveur tcp doit attendre donnees de configuration + // GF_Socket *socket; // socket tcp pour l'interface + u32 *RAPtimer; + CONF_Data *config; + u32 status; +} TCP_Input; + +typedef struct rap_input +{ + GF_Mutex *carrousel_mutex; + u32 *RAPtimer; + PNC_CallbackData *data; + u32 status; +} RAP_Input; + +void command_line_parsing(int* argc, char** argv, int *tcp_port, char *config_file, int *config_flag); +int server_command_line(char *arg_a, char *arg_b, char *value, int argument); +u32 tcp_server(void *par); +u32 RAP_send(void *par); +void print_usage(void); diff --git a/applications/testapps/broadcaster/broadcaster_config.cfg b/applications/testapps/broadcaster/broadcaster_config.cfg new file mode 100644 index 0000000..c132771 --- /dev/null +++ b/applications/testapps/broadcaster/broadcaster_config.cfg @@ -0,0 +1,14 @@ +[Broadcaster] +InitialScene=meteo_local.xmt +RAPPeriod=2 +ConfigPort=5000 +SceneUpdatePort=8000 + +[Destination] +#IP=233.64.133.10 +IP=137.194.232.99 +Port=7000 + +[Feedback] +IP=137.194.232.99 +Port=5757 diff --git a/applications/testapps/broadcaster/france.mp4 b/applications/testapps/broadcaster/france.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..fbfea0a411a299aeca7614fd5f4ad2d4e49424f0 GIT binary patch literal 9131 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 literal 0 HcmV?d00001 diff --git a/applications/testapps/broadcaster/meteo_local.xmt b/applications/testapps/broadcaster/meteo_local.xmt new file mode 100644 index 0000000..1feaa3a --- /dev/null +++ b/applications/testapps/broadcaster/meteo_local.xmt @@ -0,0 +1,392 @@ + +
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/applications/testapps/broadcaster/sdp_generator.c b/applications/testapps/broadcaster/sdp_generator.c new file mode 100644 index 0000000..01066bc --- /dev/null +++ b/applications/testapps/broadcaster/sdp_generator.c @@ -0,0 +1,71 @@ +#include "sdp_generator.h" + +int sdp_generator(PNC_CallbackData * data, char *ip_dest, char *sdp_fmt) +{ + GF_BifsEngine *codec; + GF_ESD *esd; + u32 size,size64; + char *buffer; + char buf64[5000]; + FILE *fp; + int ret; + char temp[5000]; + u16 port; + u32 socket_type; + + // fonctions necessaires pour recuperer les informations necessaires a la construction du fichier + gf_sk_get_local_info(data->chan->rtp, &port, &socket_type); + + // fprintf(stdout, "%s --------- %d\n", ip_adresse, port); + fp = fopen("broadcaster.sdp", "w+"); + if(fp == NULL) + { + fprintf(stdout, "[broadcaster] : erreur, probleme a ouvrir file temp pour scene initiale\n"); + exit(1); + } + // ecriture du fichier SDP + ret = fwrite("v=0\n", 1, 4, fp); + + sprintf(temp, "o=GpacBroadcaster 3326096807 1117107880000 IN IP%d %s\n", gf_net_is_ipv6(ip_dest) ? 6 : 4, ip_dest); + ret = fwrite(temp, 1, strlen(temp), fp); + + ret = fwrite("s=MPEG4Broadcaster\n", 1, 19, fp); + + sprintf(temp, "c=IN IP%d %s\n", gf_net_is_ipv6(ip_dest) ? 6 : 4, ip_dest); + ret = fwrite(temp, 1, strlen(temp), fp); + + ret = fwrite("t=0 0\n", 1, 6, fp); + + // GF_BIFSEngine + codec = (GF_BifsEngine *) data->codec; + buffer = NULL; + size = 0; + gf_odf_desc_write((GF_Descriptor *) codec->ctx->root_od, &buffer, &size); + esd = gf_list_get(codec->ctx->root_od->ESDescriptors, 0); + + //encode in Base64 the iod + size64 = gf_base64_encode((unsigned char *) buffer, size, (unsigned char *) buf64, 2000); + // size64 = gf_base64_encode(buffer, size, buf64, 2000); + buf64[size64] = 0; + free(buffer); + + // fprintf(stdout, "a=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"\n", buf64); + sprintf(temp, "a=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"\n", buf64); + ret = fwrite(temp, 1, strlen(temp), fp); + + sprintf(temp, "m=application %d RTP/AVP 96\n", port); + ret = fwrite(temp, 1, strlen(temp), fp); + + ret = fwrite("a=rtpmap:96 mpeg4-generic/1000\n", 1, 31, fp); + + sprintf(temp, "a=mpeg4-esid:%d\n", esd->ESID); + ret = fwrite(temp, 1, strlen(temp), fp); + + // fprintf(stdout, "%s\n", sdp_fmt); + sprintf(temp, "%s\n", sdp_fmt); + ret = fwrite(temp, 1, strlen(temp), fp); + + fclose(fp); + fprintf(stdout, "[sdp generator] : fichier SDP generater in broadcaster.sdp\n"); + return GF_OK; +} diff --git a/applications/testapps/broadcaster/sdp_generator.h b/applications/testapps/broadcaster/sdp_generator.h new file mode 100644 index 0000000..9ce41fd --- /dev/null +++ b/applications/testapps/broadcaster/sdp_generator.h @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include +#include +#include +#include "RTP_serv_generator.h" + +/* definitions des structures */ +struct __tag_bifs_engine +{ + GF_SceneGraph *sg; + GF_SceneManager *ctx; + GF_SceneLoader load; + void *calling_object; + GF_StreamContext *sc; + + GF_BifsEncoder *bifsenc; + u32 stream_ts_res; + /* TODO: maybe the currentAUCount should be a GF_List of u32 + to capture the number of AU per input BIFS stream */ + u32 currentAUCount; + + char encoded_bifs_config[20]; + u32 encoded_bifs_config_size; +}; + +int sdp_generator(PNC_CallbackData * data, char *ip_dest, char *sdp_fmt); diff --git a/applications/testapps/dmbrs/dmbrs.dsp b/applications/testapps/dmbrs/dmbrs.dsp new file mode 100644 index 0000000..8ae463f --- /dev/null +++ b/applications/testapps/dmbrs/dmbrs.dsp @@ -0,0 +1,90 @@ +# Microsoft Developer Studio Project File - Name="dmbrs" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=dmbrs - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dmbrs.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dmbrs.mak" CFG="dmbrs - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dmbrs - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "dmbrs - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dmbrs - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 zlib.lib winmm.lib /nologo /subsystem:console /machine:I386 /out:"../../../bin/w32_rel/dmbrs.exe" /libpath:"../../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "dmbrs - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 zlib.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../../bin/w32_deb/dmbrs.exe" /pdbtype:sept /libpath:"../../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "dmbrs - Win32 Release" +# Name "dmbrs - Win32 Debug" +# Begin Source File + +SOURCE=.\main.c +# End Source File +# End Target +# End Project diff --git a/applications/testapps/dmbrs/main.c b/applications/testapps/dmbrs/main.c new file mode 100644 index 0000000..0755e4e --- /dev/null +++ b/applications/testapps/dmbrs/main.c @@ -0,0 +1,250 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / DMB application + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include + +void save_ts(char *filename, unsigned char *data) +{ + FILE *ts_out = fopen(filename,"a+b"); + fwrite(data, 1, 188, ts_out); + fclose(ts_out); +} +void save_rs_0(char *filename, unsigned char *data) +{ + FILE *rs_out = fopen(filename,"a+b"); + fwrite(data, 1, 204, rs_out); + fclose(rs_out); +} + +void RS_Interleaver(GF_BitStream *bs, char *out_name) +{ + u8 *tmp; + u8 ts0[204], ts1[204], ts2[204], ts3[204], ts4[204], ts5[204], ts6[204], ts7[204], ts8[204], ts9[204], ts10[204], ts11[204]; + u8 *ts[12] = { ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ts8, ts9, ts10, ts11 }; + u8 rs[204]; + u32 i; + u64 bs_data; + u32 k; + + memset(ts[0], 0xFF, 204); + ts[0][0] = 0x47; + memset(ts[1], 0xFF, 204); + ts[1][0] = 0x47; + memset(ts[2], 0xFF, 204); + ts[2][0] = 0x47; + memset(ts[3], 0xFF, 204); + ts[3][0] = 0x47; + memset(ts[4], 0xFF, 204); + ts[4][0] = 0x47; + memset(ts[5], 0xFF, 204); + ts[5][0] = 0x47; + memset(ts[6], 0xFF, 204); + ts[6][0] = 0x47; + memset(ts[7], 0xFF, 204); + ts[7][0] = 0x47; + memset(ts[8], 0xFF, 204); + ts[8][0] = 0x47; + memset(ts[9], 0xFF, 204); + ts[9][0] = 0x47; + memset(ts[10], 0xFF, 204); + ts[10][0] = 0x47; + + k = 11; + bs_data = gf_bs_available(bs); + while (bs_data > 188 || k > 0){ + + gf_bs_read_data(bs, ts[11], 188); + if (bs_data == 0) { + memset(ts[11], 0xFF, 204); + ts[11][0] = 0x47; + k--; + } + bs_data = gf_bs_available(bs); + + for (i=0; i<(17*12); i+=12){ // 1 paquet RS + + rs[i] = ts[11][i]; + if (rs[0] != 0x47){ + printf ("error ts sync byte"); + } + rs[i+1] = ts[10][i+1]; + rs[i+2] = ts[9][i+2]; + rs[i+3] = ts[8][i+3]; + rs[i+4] = ts[7][i+4]; + rs[i+5] = ts[6][i+5]; + rs[i+6] = ts[5][i+6]; + rs[i+7] = ts[4][i+7]; + rs[i+8] = ts[3][i+8]; + rs[i+9] = ts[2][i+9]; + rs[i+10] = ts[1][i+10]; + rs[i+11] = ts[0][i+11]; + } + + if (rs[0] != 0x47) { + printf("error in output TS\n"); + }else{ + save_rs_0(out_name, rs); + } + + tmp = ts[0]; + ts[0] = ts[1]; + ts[1] = ts[2]; + ts[2] = ts[3]; + ts[3] = ts[4]; + ts[4] = ts[5]; + ts[5] = ts[6]; + ts[6] = ts[7]; + ts[7] = ts[8]; + ts[8] = ts[9]; + ts[9] = ts[10]; + ts[10] = ts[11]; + ts[11] = tmp; + } +} + +void RS_Deinterleaver(GF_BitStream *bs, char *out_name) +{ + u8 rs0[204], rs1[204], rs2[204], rs3[204], rs4[204], rs5[204], rs6[204], rs7[204], rs8[204], rs9[204], rs10[204], rs11[204]; + u8 *rs[12] = { rs0, rs1, rs2, rs3, rs4, rs5, rs6, rs7, rs8, rs9, rs10, rs11 }; + u8 *tmp; + u8 buf[204]; + u32 i; + u64 bs_data; + u32 k = 0; + + memset(rs[0], 0, 204); + memset(rs[1], 0, 204); + memset(rs[2], 0, 204); + memset(rs[3], 0, 204); + memset(rs[4], 0, 204); + memset(rs[5], 0, 204); + memset(rs[6], 0, 204); + memset(rs[7], 0, 204); + memset(rs[8], 0, 204); + memset(rs[9], 0, 204); + memset(rs[10], 0, 204); + memset(rs[11], 0, 204); + + bs_data = gf_bs_available(bs); + while (bs_data > 204){ + u64 pos; + k++; +// printf("TS Packet Number: %d\r", k); + + pos = gf_bs_get_position(bs); + gf_bs_read_data(bs, buf, 204); + bs_data = gf_bs_available(bs); + + while ((buf[0] != 0x47) && (bs_data > 0)) { + printf("error in input TS %d\n", k); + //return; + pos++; + gf_bs_seek(bs, pos); + gf_bs_read_data(bs, buf, 204); + bs_data = gf_bs_available(bs); + } + + for (i=0; i<(17*12); i+=12){ // 1 paquet + rs[0][i] = buf[i]; + rs[1][i+1] = buf[i+1]; + rs[2][i+2] = buf[i+2]; + rs[3][i+3] = buf[i+3]; + rs[4][i+4] = buf[i+4]; + rs[5][i+5] = buf[i+5]; + rs[6][i+6] = buf[i+6]; + rs[7][i+7] = buf[i+7]; + rs[8][i+8] = buf[i+8]; + rs[9][i+9] = buf[i+9]; + rs[10][i+10] = buf[i+10]; + rs[11][i+11] = buf[i+11]; + } + if (k >= 12){ + if (rs[11][0] != 0x47) { + printf("error in output TS\n"); + }else{ + save_ts(out_name, rs[11]); + } + } + tmp = rs[11]; + rs[11] = rs[10]; + rs[10] = rs[9]; + rs[9] = rs[8]; + rs[8] = rs[7]; + rs[7] = rs[6]; + rs[6] = rs[5]; + rs[5] = rs[4]; + rs[4] = rs[3]; + rs[3] = rs[2]; + rs[2] = rs[1]; + rs[1] = rs[0]; + rs[0] = tmp; + } +} + +void main(int argc, char **argv) +{ + FILE *in; + GF_BitStream *bs; + + /* generation d'un TS aléatoire */ +/* + if ((in=fopen(argv[1], "wb")) == NULL) { + printf( "Impossible d'ouvrir %s en lecture.\n", argv[1]); + } + { + char buffer[188]; + u32 j, i, nb_packets = 300; + for (i = 0; i < nb_packets; i++) { + buffer[0] = 0x47; + for (j = 1; j <188; j++) { + buffer[j] = rand();//j; + } + fwrite(buffer, 1, 188, in); + } + } + fclose(in); + if ((in=fopen(argv[1], "rb")) == NULL) { + printf( "Impossible d'ouvrir %s en lecture.\n", argv[1]); + } + + bs = gf_bs_from_file(in, GF_BITSTREAM_READ); + if (bs == NULL) return; + + RS_Interleaver(bs, argv[2]); + fclose(in); + gf_bs_del(bs); +*/ + + + if ((in=fopen(argv[1], "rb")) == NULL) { + printf( "Impossible d'ouvrir %s en lecture.\n", argv[1]); + } + + bs = gf_bs_from_file(in, GF_BITSTREAM_READ); + if (bs == NULL) return; + + RS_Deinterleaver(bs, argv[2]); + fclose(in); + gf_bs_del(bs); + +} \ No newline at end of file diff --git a/applications/testapps/largefile/largefile.dsp b/applications/testapps/largefile/largefile.dsp new file mode 100644 index 0000000..e6b0c21 --- /dev/null +++ b/applications/testapps/largefile/largefile.dsp @@ -0,0 +1,102 @@ +# Microsoft Developer Studio Project File - Name="largefile" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=largefile - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "largefile.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "largefile.mak" CFG="largefile - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "largefile - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "largefile - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "largefile - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 winmm.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "largefile - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 winmm.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "largefile - Win32 Release" +# Name "largefile - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\main.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/applications/testapps/largefile/largefile.dsw b/applications/testapps/largefile/largefile.dsw new file mode 100644 index 0000000..12e531a --- /dev/null +++ b/applications/testapps/largefile/largefile.dsw @@ -0,0 +1,44 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "largefile"=.\largefile.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac + End Project Dependency +}}} + +############################################################################### + +Project: "libgpac"=..\..\..\build\msvc6\libgpac.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/applications/testapps/largefile/main.c b/applications/testapps/largefile/main.c new file mode 100644 index 0000000..8337f36 --- /dev/null +++ b/applications/testapps/largefile/main.c @@ -0,0 +1,85 @@ +#include + +void PrintUsage() +{ + fprintf(stdout, + "Usage: largefile [options]\n" + "Option is one of:\n" + "-flat test file writing in flat mode (moov at end)\n" + "-inter test file writing in interleaved mode (moov at begin)\n" + "-size size specifies target media size in GB. Default is 5.0 GB\n" + "" + ); +} +#define TEST_FILE_NAME "largefile.mp4" + +int main(int argc, char **argv) +{ + GF_ISOFile *movie; + GF_ESD *esd; + GF_Err e; + Double gb_size = 5.0; + u8 store_mode; + u32 track, di, i, nb_samp; + GF_ISOSample *samp; + + store_mode = GF_ISOM_OPEN_WRITE; + for (i=1; idecoderConfig->streamType = 4; + gf_isom_new_mpeg4_description(movie, track, esd, NULL, NULL, &di); + + samp = gf_isom_sample_new(); + samp->dataLength = 1024*1024; + samp->data = malloc(sizeof(char)*samp->dataLength); + memset(samp->data, 0, sizeof(char)*samp->dataLength); + + for (i=0; iDTS % 25) samp->IsRAP = 0; + else samp->IsRAP = 1; + e = gf_isom_add_sample(movie, track, di, samp); + samp->DTS += 1; + + fprintf(stdout, "Writing sample %d / %d \r", i+1, nb_samp); + if (e) break; + } + gf_isom_sample_del(&samp); + + if (e) { + fprintf(stdout, "\nError writing sample %d\n", i); + gf_isom_delete(movie); + return 1; + } + + fprintf(stdout, "\nDone writing samples\n"); + e = gf_isom_close(movie); + if (e) { + fprintf(stdout, "Error writing file\n"); + return 1; + } + return 0; +} + + diff --git a/applications/testapps/loadcompare/LoadCompare.dsp b/applications/testapps/loadcompare/LoadCompare.dsp new file mode 100644 index 0000000..5eed27f --- /dev/null +++ b/applications/testapps/loadcompare/LoadCompare.dsp @@ -0,0 +1,112 @@ +# Microsoft Developer Studio Project File - Name="LoadCompare" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=LoadCompare - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "LoadCompare.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "LoadCompare.mak" CFG="LoadCompare - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "LoadCompare - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "LoadCompare - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "LoadCompare - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "LoadCompare___Win32_Release" +# PROP BASE Intermediate_Dir "LoadCompare___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/loadcompare_rel" +# PROP Intermediate_Dir "obj/loadcompare_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /I "../../../extra_lib/include/zlib" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 zlib.lib winmm.lib libxml2.lib /nologo /subsystem:console /machine:I386 /out:"../../../bin/w32_rel/LoadCompare.exe" /libpath:"../../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "LoadCompare - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "LoadCompare___Win32_Debug" +# PROP BASE Intermediate_Dir "LoadCompare___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/loadcompare_deb" +# PROP Intermediate_Dir "obj/loadcompare_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /I "../../../extra_lib/include/zlib" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 zlib.lib winmm.lib libxml2.lib /nologo /subsystem:console /pdb:"obj/loadcompare_deb//LoadCompare.pdb" /debug /machine:I386 /out:"../../../bin/w32_deb/LoadCompare.exe" /pdbtype:sept /libpath:"../../../extra_lib/lib/w32_deb" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "LoadCompare - Win32 Release" +# Name "LoadCompare - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\loadcompare.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\modules\svg_loader\lsr_parser.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\modules\svg_loader\svg_parser.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/applications/testapps/loadcompare/Makefile b/applications/testapps/loadcompare/Makefile new file mode 100644 index 0000000..11184a9 --- /dev/null +++ b/applications/testapps/loadcompare/Makefile @@ -0,0 +1,66 @@ +include ../../../config.mak + +vpath %.c $(SRC_PATH)/applications/testapps/loadcompare + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +CFLAGS+=$(XML2_CFLAGS) + +#common obj +OBJS= loadcompare.o ../../../modules/svg_loader/svg_parser.o ../../../modules/svg_loader/lsr_parser.o + +LINKFLAGS=-L../../../bin/gcc +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=LoadCompare$(EXE) +#LINKFLAGS+=-lgpac_static -lz $(EXTRALIBS) +LINKFLAGS+=-lgpac +else +EXT= +PROG=LoadCompare +#LINKFLAGS+=-lgpac_static $(EXTRALIBS) $(GPAC_SH_FLAGS) -lz +LINKFLAGS+=-lgpac -lz $(XML2_LFLAGS) +endif + + +SRCS := $(OBJS:.o=.c) + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o ../../../bin/gcc/$@ $(OBJS) $(LINKFLAGS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../../bin/gcc/$(PROG) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/applications/testapps/loadcompare/loadcompare.c b/applications/testapps/loadcompare/loadcompare.c new file mode 100644 index 0000000..35e9d8a --- /dev/null +++ b/applications/testapps/loadcompare/loadcompare.c @@ -0,0 +1,697 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2000-2006 + * All rights reserved + * + * This file is part of GPAC / load&compare application + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include +#include + +enum { + SVG = 0, + XMT = 1, +}; + +typedef struct { + char filename[100]; + u32 size; + u32 gpacxml_loadtime; + u32 libxml_loadtime; + u32 gz_size; + u32 gpacxml_gz_loadtime; + u32 libxml_gz_loadtime; + u32 track_size; + u32 track_loadtime; + u32 decoded_size; + u32 decoded_loadtime; +} LoadData; + +typedef struct { + FILE *out; + u32 type; + u32 nbloads; + u32 verbose; + Bool regenerate; + Bool spread_repeat; + u32 repeat_index; + GF_List *data; +} GF_LoadCompare; + +GF_Err load_mp4(GF_LoadCompare *lc, GF_ISOFile *mp4, u32 *loadtime) +{ + GF_Err e = GF_OK; + GF_SceneLoader load; + GF_SceneGraph *sg; + u32 i, starttime, endtime; + u32 nb; + if (lc->spread_repeat) nb = 1; + else nb = lc->nbloads ; + + *loadtime = 0; + for (i = 0; i< nb; i++) { + memset(&load, 0, sizeof(GF_SceneLoader)); + sg = gf_sg_new(); + load.ctx = gf_sm_new(sg); + + load.isom = mp4; + starttime = gf_sys_clock(); + + e = gf_sm_load_init(&load); + if (e) { + fprintf(stderr, "Error loading MP4 file\n"); + } else { + e = gf_sm_load_run(&load); + if (e) { + fprintf(stderr, "Error loading MP4 file\n"); + } else { + endtime = gf_sys_clock(); + *loadtime += endtime-starttime; + } + gf_sm_load_done(&load); + } + gf_sm_del(load.ctx); + gf_sg_del(sg); + } + return e; +} + +void load_progress(void *cbk, u32 done, u32 total) { + fprintf(stdout, "%d/%d\r", done, total); +} + +GF_Err gpacctx_load_file(GF_LoadCompare *lc, char *item_path, u32 *loadtime) +{ + GF_Err e = GF_OK; + GF_SceneLoader load; + GF_SceneGraph *sg; + u32 i, starttime, endtime; + + u32 nb; + if (lc->spread_repeat) nb = 1; + else nb = lc->nbloads ; + + *loadtime = 0; + + for (i = 0; idataLength; + gf_isom_sample_del(&samp); + } + return e; +} + +GF_Err encode_laser(GF_LoadCompare *lc, char *item_path, GF_ISOFile *mp4, GF_SMEncodeOptions *opts) +{ + GF_Err e = GF_OK; + GF_SceneLoader load; + GF_SceneManager *ctx; + GF_SceneGraph *sg; + GF_StatManager *statsman = NULL; + + memset(&load, 0, sizeof(GF_SceneLoader)); + sg = gf_sg_new(); + ctx = gf_sm_new(sg); + load.ctx = ctx; + load.fileName = item_path; + + e = gf_sm_load_init(&load); + if (e) { + fprintf(stderr, "Error loading file %s\n", item_path); + } else { + e = gf_sm_load_run(&load); + if (e) { + fprintf(stderr, "Error loading file %s\n", item_path); + } else { + if (opts->auto_qant) { + if (lc->verbose) fprintf(stdout, "Analysing Scene for Automatic Quantization\n"); + statsman = gf_sm_stats_new(); + e = gf_sm_stats_for_scene(statsman, ctx); + if (!e) { + GF_SceneStatistics *stats = gf_sm_stats_get(statsman); + if (opts->resolution > (s32)stats->frac_res_2d) { + if (lc->verbose) fprintf(stdout, " Given resolution %d is (unnecessarily) too high, using %d instead.\n", opts->resolution, stats->frac_res_2d); + opts->resolution = stats->frac_res_2d; + } else if (stats->int_res_2d + opts->resolution <= 0) { + if (lc->verbose) fprintf(stdout, " Given resolution %d is too low, using %d instead.\n", opts->resolution, stats->int_res_2d - 1); + opts->resolution = 1 - stats->int_res_2d; + } + opts->coord_bits = stats->int_res_2d + opts->resolution; + if (lc->verbose) fprintf(stdout, " Coordinates & Lengths encoded using "); + if (opts->resolution < 0) { + if (lc->verbose) fprintf(stdout, "only the %d most significant bits (of %d).\n", opts->coord_bits, stats->int_res_2d); + } else { + if (lc->verbose) fprintf(stdout, "a %d.%d representation\n", stats->int_res_2d, opts->resolution); + } + + if (lc->verbose) fprintf(stdout, " Matrix Scale & Skew Coefficients "); + if (opts->coord_bits < stats->scale_int_res_2d) { + opts->scale_bits = stats->scale_int_res_2d - opts->coord_bits; + if (lc->verbose) fprintf(stdout, "encoded using a %d.8 representation\n", stats->scale_int_res_2d); + } else { + opts->scale_bits = 0; + if (lc->verbose) fprintf(stdout, "not encoded.\n"); + } + } + gf_sm_stats_del(statsman); + } + + e = gf_sm_encode_to_file(ctx, mp4, opts); + if (e) { + fprintf(stderr, "Error while encoding mp4 file\n"); + } else { + e = gf_isom_set_brand_info(mp4, GF_ISOM_BRAND_MP42, 1); + if (!e) e = gf_isom_modify_alternate_brand(mp4, GF_ISOM_BRAND_ISOM, 1); + } + + gf_sm_load_done(&load); + } + } + gf_sm_del(ctx); + gf_sg_del(sg); + + return e; +} + +GF_Err create_laser_mp4(GF_LoadCompare *lc, char *item_name, char *item_path, u32 *size) +{ + char mp4_path[100], *ext; + GF_Err e = GF_OK; + GF_ISOFile *mp4; + + *size = 0; + + strcpy(mp4_path, item_name); + ext = strrchr(mp4_path, '.'); + strcpy(ext, ".mp4"); + mp4 = gf_isom_open(mp4_path, GF_ISOM_WRITE_EDIT, NULL); + if (!mp4) { + if (lc->verbose) fprintf(stdout, "Could not open file %s for writing\n", mp4_path); + e = GF_IO_ERR; + } else { + GF_SMEncodeOptions opts; + memset(&opts, 0, sizeof(GF_SMEncodeOptions)); + opts.auto_qant = 1; + opts.resolution = 8; + e = encode_laser(lc, item_path, mp4, &opts); + if (e) { + if (lc->verbose) fprintf(stdout, "Could not encode MP4 file from %s\n", item_path); + gf_isom_delete(mp4); + } else { + gf_isom_close(mp4); + + mp4 = gf_isom_open(mp4_path, GF_ISOM_OPEN_READ, NULL); + if (!mp4) { + if (lc->verbose) fprintf(stdout, "Could not open file %s for reading\n", mp4_path); + e = GF_IO_ERR; + } else { + e = get_laser_track_size(mp4, size); + if (e) { + if (lc->verbose) fprintf(stdout, "Could not get MP4 file size\n"); + } + gf_isom_close(mp4); + } + } + } + return e; +} + + +GF_Err get_mp4_loadtime(GF_LoadCompare *lc, char *item_name, char *item_path, u32 *loadtime) +{ + char mp4_path[100], *ext; + GF_Err e = GF_OK; + GF_ISOFile *mp4; + + *loadtime = 0; + + strcpy(mp4_path, item_name); + ext = strrchr(mp4_path, '.'); + strcpy(ext, ".mp4"); + mp4 = gf_isom_open(mp4_path, GF_ISOM_OPEN_READ, NULL); + if (!mp4) { + if (lc->verbose) fprintf(stdout, "Could not open file %s for reading\n", mp4_path); + e = GF_IO_ERR; + } else { + e = load_mp4(lc, mp4, loadtime); + if (e) { + if (lc->verbose) fprintf(stdout, "Could not get MP4 file load time\n"); + } + } + gf_isom_close(mp4); + return e; +} + +GF_Err decode_svg(GF_LoadCompare *lc, char *item_name, char *item_path, char *svg_out_path) +{ + GF_SceneManager *ctx; + GF_SceneGraph *sg; + GF_SceneLoader load; + GF_ISOFile *mp4; + GF_Err e = GF_OK; + char mp4_path[256]; + char *ext; + + strcpy(mp4_path, item_name); + ext = strrchr(mp4_path, '.'); + strcpy(ext, ".mp4"); + mp4 = gf_isom_open(mp4_path, GF_ISOM_OPEN_READ, NULL); + if (!mp4) { + if (lc->verbose) fprintf(stdout, "Could not open file %s\n", mp4_path); + e = GF_IO_ERR; + } else { + sg = gf_sg_new(); + ctx = gf_sm_new(sg); + memset(&load, 0, sizeof(GF_SceneLoader)); + load.isom = mp4; + load.ctx = ctx; + e = gf_sm_load_init(&load); + if (e) { + fprintf(stderr, "Error loading MP4 file\n"); + } else { + e = gf_sm_load_run(&load); + if (e) { + fprintf(stderr, "Error loading MP4 file\n"); + } else { + gf_sm_load_done(&load); + + ext = strrchr(svg_out_path, '.'); + ext[0] = 0; + e = gf_sm_dump(ctx, svg_out_path, GF_SM_DUMP_SVG); + if (e) { + fprintf(stderr, "Error dumping SVG from MP4 file\n"); + } + } + } + gf_sm_del(ctx); + gf_sg_del(sg); + gf_isom_close(mp4); + } + return e; +} + +GF_Err libxml_load_svg(GF_LoadCompare *lc, char *item_path, u32 *loadtime) +{ + GF_Err e = GF_OK; + GF_SceneGraph *sg; + u32 i, starttime, endtime; + void *p; + + u32 nb; + if (lc->spread_repeat) nb = 1; + else nb = lc->nbloads ; + + *loadtime = 0; + + for (i = 0; iverbose) fprintf(stdout, "LibXML single parsing: %d\n", endtime-starttime); + *loadtime += endtime-starttime; + + gf_sg_del(sg); + } + return e; +} + +GF_Err get_size(GF_LoadCompare *lc, char *item_name, char *item_path, u32 *size) +{ + GF_Err e = GF_OK; + FILE *file = NULL; + + *size = 0; + + file = fopen(item_path, "rt"); + if (!file) { + if (lc->verbose) fprintf(stdout, "Could not open file %s\n", item_path); + e = GF_IO_ERR; + } else { + fseek(file, 0, SEEK_END); + *size = (u32)ftell(file); + fclose(file); + if (*size == 0) { + if (lc->verbose) fprintf(stdout, "File %s has a size of 0\n", item_path); + e = GF_IO_ERR; + } + } + return e; +} + +GF_Err get_decoded_svg_loadtime_and_size(GF_LoadCompare *lc, char *item_name, char *item_path, u32 *loadtime, u32 *size) +{ + GF_Err e = GF_OK; + char svg_out_name[256]; + char *ext; + + strcpy(svg_out_name, item_name); + ext = strrchr(svg_out_name, '.'); + strcpy(ext, "_out.svg"); + + *size = 0; + *loadtime = 0; + + e = decode_svg(lc, item_name, item_path, svg_out_name); + if (!e) { + e = get_size(lc, svg_out_name, svg_out_name, size); + if (e) { + return e; + } + e = gpacctx_load_file(lc, svg_out_name, loadtime); + } + return e; +} + +GF_Err create_gz_file(GF_LoadCompare *lc, char *item_name, char *item_path, u32 *size) +{ + char buffer[100]; + char gz_path[256]; + GF_Err e = GF_OK; + FILE *file = NULL; + void *gz = NULL; + u32 read; + + *size = 0; + + strcpy(gz_path, item_name); + strcat(gz_path, "z"); + gz = gzopen(gz_path, "wb"); + file = fopen(item_path, "rt"); + + if (!gz || !file) { + if (lc->verbose) fprintf(stdout, "Could not open file %s or %s\n", item_path, gz_path); + e = GF_IO_ERR; + } else { + while ((read = fread(buffer, 1, 100, file))) gzwrite(gz, buffer, read); + fclose(file); + gzclose(gz); + file = fopen(gz_path, "rb"); + fseek(file, 0, SEEK_END); + *size = (u32)ftell(file); + fclose(file); + if (*size == 0) { + if (lc->verbose) fprintf(stdout, "File %s has a size of 0\n", gz_path); + e = GF_IO_ERR; + } + } + return e; +} + +GF_Err get_gz_loadtime(GF_LoadCompare *lc, char *item_name, char *item_path, u32 *loadtime, Bool useLibXML) +{ + char gz_path[256]; + GF_Err e = GF_OK; + *loadtime = 0; + + strcpy(gz_path, item_name); + strcat(gz_path, "z"); + + if (useLibXML) { + e = libxml_load_svg(lc, gz_path, loadtime); + } else { + e = gpacctx_load_file(lc, gz_path, loadtime); + } + return e; +} + +void print_load_data(GF_LoadCompare *lc, LoadData *ld) +{ + if (lc->verbose) fprintf(stdout, "Processing %s\n", ld->filename); + fprintf(lc->out, "%s\t", ld->filename); + + if (lc->verbose) fprintf(stdout, "File Size %d\n", ld->size); + fprintf(lc->out, "%d\t", ld->size); + + if (lc->verbose) fprintf(stdout, "GPAC XML Load Time %d\n", ld->gpacxml_loadtime); + fprintf(lc->out, "%d\t", ld->gpacxml_loadtime); + + if (lc->verbose) fprintf(stdout, "LibXML Load Time %d \n", ld->libxml_loadtime); + fprintf(lc->out, "%d\t", ld->libxml_loadtime); + + if (lc->verbose) fprintf(stdout, "GZ Size %d\n", ld->gz_size); + fprintf(lc->out, "%d\t", ld->gz_size); + + if (lc->verbose) fprintf(stdout, "GZ Load Time %d\n", ld->gpacxml_gz_loadtime); + fprintf(lc->out, "%d\t", ld->gpacxml_gz_loadtime); + + if (lc->verbose) fprintf(stdout, "LibXML GZ Load Time %d\n", ld->libxml_gz_loadtime); + fprintf(lc->out, "%d\t", ld->libxml_gz_loadtime); + + if (lc->verbose) fprintf(stdout, "MP4 Track Size %d\n", ld->track_size); + fprintf(lc->out, "%d\t", ld->track_size); + + if (lc->verbose) fprintf(stdout, "MP4 Track Load Time %d\n", ld->track_loadtime); + fprintf(lc->out, "%d\t", ld->track_loadtime); + + if (lc->verbose) fprintf(stdout, "Decoded Size %d\n", ld->decoded_size); + fprintf(lc->out, "%d\t", ld->decoded_size); + + if (lc->verbose) fprintf(stdout, "Decoded Load Time %d \n", ld->decoded_loadtime); + fprintf(lc->out, "%d\t", ld->decoded_loadtime); + + if (lc->verbose) fprintf(stdout, "Done %s\n", ld->filename); + fprintf(lc->out, "\n"); + fflush(lc->out); +} + +Bool loadcompare_one(void *cbck, char *item_name, char *item_path) +{ + GF_Err e; + GF_LoadCompare *lc = cbck; + u32 loadtime; + LoadData *ld; + + if (lc->repeat_index == 0) { + GF_SAFEALLOC(ld, sizeof(LoadData)); + gf_list_add(lc->data, ld); + strcpy(ld->filename, item_name); + + e = get_size(lc, item_name, item_path, &ld->size); + if (e) return 1; + + e = create_gz_file(lc, item_name, item_path, &ld->gz_size); + if (e) return 1; + + e = create_laser_mp4(lc, item_name, item_path, &ld->track_size); + if (e) return 1; + + } else { + LoadData *tmp; + u32 pos = 0; + ld = NULL; + while (tmp = gf_list_enum(lc->data, &pos)) { + if (!strcmp(tmp->filename, item_name)) { + ld = tmp; + break; + } + } + if (ld == NULL) return 1; + } + + + if (lc->type == SVG) { + /* GPAC XML loader */ + e = gpacctx_load_file(lc, item_path, &loadtime); + if (e) return 1; + ld->gpacxml_loadtime += loadtime; + + e = get_gz_loadtime(lc, item_name, item_path, &loadtime, 0); + if (e) return 1; + ld->gpacxml_gz_loadtime += loadtime; + + /* LibXML and LibXML GZ loadings */ + e = libxml_load_svg(lc, item_path, &loadtime); + if (e) return 1; + ld->libxml_loadtime += loadtime; + + e = get_gz_loadtime(lc, item_name, item_path, &loadtime, 1); + if (e) return 1; + ld->libxml_gz_loadtime += loadtime; + + /* MP4 Loading */ + e = get_mp4_loadtime(lc, item_name, item_path, &loadtime); + if (e) return 1; + ld->track_loadtime += loadtime; + +/* e = get_decoded_svg_loadtime_and_size(lc, item_name, item_path, &loadtime, &ld->decoded_size); + if (e) return 1; + ld->decoded_loadtime += loadtime;*/ + + } else if (lc->type == XMT) { + e = gpacctx_load_file(lc, item_path, &loadtime); + if (e) return 1; + ld->gpacxml_loadtime += loadtime; + } + + if (!lc->spread_repeat) { + print_load_data(lc, ld); + free(ld); + } + return 0; +} + +void usage() +{ + fprintf(stdout, "Compare LASeR and SVG encoding size and loading time\n"); + fprintf(stdout, "usage: (-out output_result) (-single input.svg | -dir dir) (-nloads X) (-verbose X)\n"); + fprintf(stdout, "defaults are: stdout, dir=. and X = 1"); +} + +int main(int argc, char **argv) +{ + u32 i; + char *arg; + GF_LoadCompare lc; + Bool single = 0; + char *out = NULL; + char in[256] = "."; + + fprintf(stdout, "LASeR and SVG Comparison tool\n"); + + memset(&lc, 0, sizeof(GF_LoadCompare)); + lc.nbloads = 1; + lc.out = stdout; + + for (i = 1; i < (u32) argc ; i++) { + arg = argv[i]; + if (!stricmp(arg, "-out")) { + out = argv[i+1]; + i++; + } else if (!stricmp(arg, "-single")) { + single = 1; + strcpy(in, argv[i+1]); + i++; + } else if (!stricmp(arg, "-dir")) { + strcpy(in, argv[i+1]); + i++; + } else if (!stricmp(arg, "-nloads")) { + lc.nbloads = (u32)atoi(argv[i+1]); + i++; + } else if (!stricmp(arg, "-regenerate")) { + lc.regenerate = 1; + } else if (!stricmp(arg, "-xmt")) { + lc.type = XMT; + } else if (!stricmp(arg, "-svg")) { + lc.type = SVG; + } else if (!stricmp(arg, "-spread_repeat")) { + lc.spread_repeat = 1; + } else if (!stricmp(arg, "-verbose")) { + lc.verbose = (u32)atoi(argv[i+1]); + i++; + } else { + usage(); + return -1; + } + } + + gf_sys_init(); + if (out) lc.out = fopen(out, "wt"); + if (!lc.out) { + fprintf(stderr, "Cannot open output file %s\n", out); + return -1; + } + + if (lc.type == SVG) { + fprintf(lc.out,"File Name\tSVG Size\tSVG Load Time\tLibXML Load Time\tSVGZ Size\tSVGZ Load Time\tLibXML GZ Load Time\tMP4 Size\tMP4 Load Time\tDecoded SVG Size\tDecoded SVG Load Time\n"); + } else if (lc.type == XMT) { + fprintf(lc.out,"File Name\tXMT Size\tXMT Load Time\tBT Size\tBT Load Time\n"); + } + + lc.data = gf_list_new(); + + if (single) { + LoadData *ld; + char *tmp = strrchr(in, GF_PATH_SEPARATOR); + loadcompare_one(&lc, tmp+1, in); + ld = gf_list_get(lc.data, 0); + print_load_data(&lc, ld); + free(ld); + } else { + if (lc.spread_repeat) { + for (lc.repeat_index = 0; lc.repeat_index < lc.nbloads; lc.repeat_index ++) { + if (lc.verbose) fprintf(stdout, "Loop %d\n", lc.repeat_index); + if (lc.type == SVG) { + gf_enum_directory(in, 0, loadcompare_one, &lc, "svg"); + } else if (lc.type == XMT) { + gf_enum_directory(in, 0, loadcompare_one, &lc, "xmt"); + } + } + for (i=0; i.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/applications/testapps/mp42ts/main.c b/applications/testapps/mp42ts/main.c new file mode 100644 index 0000000..dd8c6ad --- /dev/null +++ b/applications/testapps/mp42ts/main.c @@ -0,0 +1,1870 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2000-200X + * All rights reserved + * + * This file is part of GPAC / mp42ts application + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include +#include +#include +#include "mp42ts.h" + +void usage() +{ + fprintf(stderr, "usage: mp42ts [options] dst\n" + "With options being: \n" + "-prog=FILE specifies an input file used for a TS service\n" + " * currently only supports ISO files and SDP files\n" + " * option can be used several times, once for each program\n" + "-rate=R specifies target rate in kbits/sec of the multiplex\n" + " If not set, transport stream will be of variable bitrate\n" + "-mpeg4 forces usage of MPEG-4 signaling (use of IOD and SL Config)\n" + "\n" + "dst can be a file, an RTP or a UDP destination (unicast/multicast)\n" + ); +} + +static GFINLINE void m2ts_dump_time(M2TS_Time *time, char *name) +{ + fprintf(stdout, "%s: %d%03d\n", name, time->sec, time->nanosec/1000000); +} +static GFINLINE Bool m2ts_time_less(M2TS_Time *a, M2TS_Time *b) { + if (a->sec>b->sec) return 0; + if (a->sec==b->sec) return (a->nanosecnanosec) ? 1 : 0; + return 1; +} +static GFINLINE Bool m2ts_time_less_or_equal(M2TS_Time *a, M2TS_Time *b) { + if (a->sec>b->sec) return 0; + if (a->sec==b->sec) return (a->nanosec>b->nanosec) ? 0 : 1; + return 1; +} + +static GFINLINE void m2ts_time_inc(M2TS_Time *time, u32 delta_inc_num, u32 delta_inc_den) +{ + u64 n_sec; + u32 sec; + + /*couldn't compute bitrate - we need to have more info*/ + if (!delta_inc_den) return; + + sec = delta_inc_num / delta_inc_den; + + if (sec) { + time->sec += sec; + sec *= delta_inc_den; + delta_inc_num = delta_inc_num % sec; + } + /*move to nanosec - 0x3B9ACA00 = 1000*1000*1000 */ + n_sec = delta_inc_num; + n_sec *= 0x3B9ACA00; + n_sec /= delta_inc_den; + time->nanosec += (u32) n_sec; + while (time->nanosec >= 0x3B9ACA00) { + time->nanosec -= 0x3B9ACA00; + time->sec ++; + } +} + +/************************************ + * Section-related functions + ************************************/ +void m2ts_mux_table_update(M2TS_Mux_Stream *stream, u8 table_id, u16 table_id_extension, + u8 *table_payload, u32 table_payload_length, + Bool use_syntax_indicator, Bool private_indicator, + Bool use_checksum) +{ + u32 overhead_size; + u32 offset; + u32 section_number, nb_sections; + M2TS_Mux_Table *table, *prev_table; + u32 maxSectionLength; + M2TS_Mux_Section *section, *prev_sec; + GF_BitStream *bs; + + /* check if there is already a table with that id */ + prev_table = NULL; + table = stream->tables; + while (table) { + if (table->table_id == table_id) { + /* if yes, we need to flush the table and increase the version number */ + M2TS_Mux_Section *sec = table->section; + while (sec) { + M2TS_Mux_Section *sec2 = sec->next; + free(sec->data); + free(sec); + sec = sec2; + } + table->version_number = (table->version_number + 1)%0x1F; + break; + } + prev_table = table; + table = table->next; + } + + if (!table) { + /* if no, the table is created */ + GF_SAFEALLOC(table, M2TS_Mux_Table); + table->table_id = table_id; + if (prev_table) prev_table->next = table; + else stream->tables = table; + } + + if (!table_payload_length) return; + + switch (table_id) { + case GF_M2TS_TABLE_ID_PMT: + case GF_M2TS_TABLE_ID_PAT: + case GF_M2TS_TABLE_ID_SDT_ACTUAL: + case GF_M2TS_TABLE_ID_SDT_OTHER: + case GF_M2TS_TABLE_ID_BAT: + maxSectionLength = 1024; + break; + case GF_M2TS_TABLE_ID_MPEG4_BIFS: + case GF_M2TS_TABLE_ID_MPEG4_OD: + maxSectionLength = 4096; + break; + default: + fprintf(stderr, "Cannot create sections for table with id %d\n", table_id); + return; + } + + overhead_size = SECTION_HEADER_LENGTH; + if (use_syntax_indicator) overhead_size += SECTION_ADDITIONAL_HEADER_LENGTH + CRC_LENGTH; + + section_number = 0; + nb_sections = 1; + while (nb_sections*(maxSectionLength - overhead_size) 1) + fprintf(stdout,"Warning: last section number for PMT shall be 0 !!\n"); + break; + default: + break; + } + + prev_sec = NULL; + offset = 0; + while (offset < table_payload_length) { + u32 remain; + GF_SAFEALLOC(section, M2TS_Mux_Section); + + remain = table_payload_length - offset; + if (remain > maxSectionLength - overhead_size) { + section->length = maxSectionLength; + } else { + section->length = remain + overhead_size; + } + + bs = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE); + + /* first header (not included in section length */ + gf_bs_write_int(bs, table_id, 8); + gf_bs_write_int(bs, use_syntax_indicator, 1); + gf_bs_write_int(bs, private_indicator, 1); + gf_bs_write_int(bs, 3, 2); /* reserved bits are all set */ + gf_bs_write_int(bs, section->length - SECTION_HEADER_LENGTH, 12); + + if (use_syntax_indicator) { + /* second header */ + gf_bs_write_int(bs, table_id_extension, 16); + gf_bs_write_int(bs, 3, 2); /* reserved bits are all set */ + gf_bs_write_int(bs, table->version_number, 5); + gf_bs_write_int(bs, 1, 1); /* current_next_indicator = 1: we don't send version in advance */ + gf_bs_write_int(bs, section_number, 8); + section_number++; + gf_bs_write_int(bs, nb_sections-1, 8); + } + + gf_bs_write_data(bs, table_payload + offset, section->length - overhead_size); + offset += section->length - overhead_size; + + if (use_syntax_indicator) { + /* place holder for CRC */ + gf_bs_write_u32(bs, 0); + } + + gf_bs_get_content(bs, (char**) §ion->data, §ion->length); + gf_bs_del(bs); + + if (use_syntax_indicator) { + u32 CRC; + CRC = gf_crc_32(section->data,section->length-CRC_LENGTH); + section->data[section->length-4] = (CRC >> 24) & 0xFF; + section->data[section->length-3] = (CRC >> 16) & 0xFF; + section->data[section->length-2] = (CRC >> 8) & 0xFF; + section->data[section->length-1] = CRC & 0xFF; + } + + if (prev_sec) prev_sec->next = section; + else table->section = section; + prev_sec = section; + } + stream->current_table = stream->tables; + stream->current_section = stream->current_table->section; + stream->current_section_offset = 0; +} + +void m2ts_mux_table_update_mpeg4(M2TS_Mux_Stream *stream, u8 table_id, u16 table_id_extension, + u8 *table_payload, u32 table_payload_length, + Bool use_syntax_indicator, Bool private_indicator, + Bool use_checksum) +{ + GF_SLHeader hdr; + u32 overhead_size; + u32 offset, sl_size; + u32 section_number, nb_sections; + M2TS_Mux_Table *table, *prev_table; + /*max section length for MPEG-4 BIFS and OD*/ + u32 maxSectionLength = 4096; + M2TS_Mux_Section *section, *prev_sec; + GF_BitStream *bs; + + /* check if there is already a table with that id */ + prev_table = NULL; + table = stream->tables; + while (table) { + if (table->table_id == table_id) { + /* if yes, we need to flush the table and increase the version number */ + M2TS_Mux_Section *sec = table->section; + while (sec) { + M2TS_Mux_Section *sec2 = sec->next; + free(sec->data); + free(sec); + sec = sec2; + } + table->version_number = (table->version_number + 1)%0x1F; + break; + } + prev_table = table; + table = table->next; + } + + if (!table) { + /* if no, the table is created */ + GF_SAFEALLOC(table, M2TS_Mux_Table); + table->table_id = table_id; + if (prev_table) prev_table->next = table; + else stream->tables = table; + } + + if (!table_payload_length) return; + + overhead_size = SECTION_HEADER_LENGTH; + if (use_syntax_indicator) overhead_size += SECTION_ADDITIONAL_HEADER_LENGTH + CRC_LENGTH; + + section_number = 0; + nb_sections = 1; + hdr = stream->sl_header; + sl_size = gf_sl_get_header_size(&stream->ifce->sl_config, &hdr); + /*SL-packetized data doesn't fit in one section, we must repacketize*/ + if (sl_size + table_payload_length > maxSectionLength - overhead_size) { + nb_sections = 0; + offset = 0; + hdr.accessUnitEndFlag = 0; + while (offsetifce->sl_config, &hdr); + /*remove start flag*/ + hdr.accessUnitStartFlag = 0; + /*fill each section but beware of last packet*/ + offset += maxSectionLength - overhead_size - sl_size; + nb_sections++; + } + } + prev_sec = NULL; + offset = 0; + hdr = stream->sl_header; + while (offset < table_payload_length) { + u32 remain; + char *slhdr; + u32 slhdr_size; + GF_SAFEALLOC(section, M2TS_Mux_Section); + + hdr.accessUnitEndFlag = (section_number+1==nb_sections) ? stream->sl_header.accessUnitEndFlag : 0; + gf_sl_packetize(&stream->ifce->sl_config, &hdr, NULL, 0, &slhdr, &slhdr_size); + hdr.accessUnitStartFlag = 0; + + remain = table_payload_length - offset; + if (remain > maxSectionLength - overhead_size - slhdr_size) { + section->length = maxSectionLength; + } else { + section->length = remain + overhead_size + slhdr_size; + } + sl_size = section->length - overhead_size - slhdr_size; + + bs = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE); + + /* first header (not included in section length */ + gf_bs_write_int(bs, table_id, 8); + gf_bs_write_int(bs, use_syntax_indicator, 1); + gf_bs_write_int(bs, private_indicator, 1); + gf_bs_write_int(bs, 3, 2); /* reserved bits are all set */ + gf_bs_write_int(bs, section->length - SECTION_HEADER_LENGTH, 12); + + if (use_syntax_indicator) { + /* second header */ + gf_bs_write_int(bs, table_id_extension, 16); + gf_bs_write_int(bs, 3, 2); /* reserved bits are all set */ + gf_bs_write_int(bs, table->version_number, 5); + gf_bs_write_int(bs, 1, 1); /* current_next_indicator = 1: we don't send version in advance */ + gf_bs_write_int(bs, section_number, 8); + section_number++; + gf_bs_write_int(bs, nb_sections-1, 8); + } + + /*write sl header*/ + gf_bs_write_data(bs, slhdr, slhdr_size); + free(slhdr); + /*write sl data*/ + gf_bs_write_data(bs, table_payload + offset, sl_size); + offset += sl_size; + + if (use_syntax_indicator) { + /* place holder for CRC */ + gf_bs_write_u32(bs, 0); + } + + gf_bs_get_content(bs, (char**) §ion->data, §ion->length); + gf_bs_del(bs); + + if (use_syntax_indicator) { + u32 CRC; + CRC = gf_crc_32(section->data,section->length-CRC_LENGTH); + section->data[section->length-4] = (CRC >> 24) & 0xFF; + section->data[section->length-3] = (CRC >> 16) & 0xFF; + section->data[section->length-2] = (CRC >> 8) & 0xFF; + section->data[section->length-1] = CRC & 0xFF; + } + + if (prev_sec) prev_sec->next = section; + else table->section = section; + prev_sec = section; + } + stream->current_table = stream->tables; + stream->current_section = stream->current_table->section; + stream->current_section_offset = 0; +} + +void m2ts_mux_table_update_bitrate(M2TS_Mux *mux, M2TS_Mux_Stream *stream) +{ + M2TS_Mux_Table *table; + + /*update PMT*/ + if (stream->table_needs_update) + stream->process(mux, stream); + + stream->bit_rate = 0; + table = stream->tables; + while (table) { + M2TS_Mux_Section *section = table->section; + while (section) { + stream->bit_rate += section->length; + section = section->next; + } + table = table->next; + } + stream->bit_rate *= 8; + if (!stream->refresh_rate_ms) stream->refresh_rate_ms = 500; + stream->bit_rate *= 1000; + stream->bit_rate /= stream->refresh_rate_ms; +} + +/* length of adaptation_field_length; */ +#define ADAPTATION_LENGTH_LENGTH 1 +/* discontinuty flag, random access flag ... */ +#define ADAPTATION_FLAGS_LENGTH 1 +/* length of encoded pcr */ +#define PCR_LENGTH 6 + +static u32 m2ts_add_adaptation(GF_BitStream *bs, + Bool has_pcr, u64 time, + Bool is_rap, + u32 padding_length) +{ + u32 adaptation_length; + + adaptation_length = ADAPTATION_FLAGS_LENGTH + (has_pcr?PCR_LENGTH:0) + padding_length; + + gf_bs_write_int(bs, adaptation_length, 8); + gf_bs_write_int(bs, 0, 1); // discontinuity indicator + gf_bs_write_int(bs, is_rap, 1); // random access indicator + gf_bs_write_int(bs, 0, 1); // es priority indicator + gf_bs_write_int(bs, has_pcr, 1); // PCR_flag + gf_bs_write_int(bs, 0, 1); // OPCR flag + gf_bs_write_int(bs, 0, 1); // splicing point flag + gf_bs_write_int(bs, 0, 1); // transport private data flag + gf_bs_write_int(bs, 0, 1); // adaptation field extension flag + if (has_pcr) { + u64 PCR_base, PCR_ext; + PCR_base = time; + gf_bs_write_long_int(bs, PCR_base, 33); + gf_bs_write_int(bs, 0, 6); // reserved + PCR_ext = 0; + gf_bs_write_long_int(bs, PCR_ext, 9); + } + while (padding_length) { + gf_bs_write_u8(bs, 0xff); // stuffing byte + padding_length--; + } + + return adaptation_length + ADAPTATION_LENGTH_LENGTH; +} + +void m2ts_mux_table_get_next_packet(M2TS_Mux_Stream *stream, u8 *packet) +{ + GF_BitStream *bs; + M2TS_Mux_Table *table; + M2TS_Mux_Section *section; + u32 payload_length, padding_length; + u8 adaptation_field_control; + + table = stream->current_table; + assert(table); + + section = stream->current_section; + assert(section); + + bs = gf_bs_new(packet, 188, GF_BITSTREAM_WRITE); + + gf_bs_write_int(bs, 0x47, 8); // sync + gf_bs_write_int(bs, 0, 1); // error indicator + if (stream->current_section_offset == 0) { + gf_bs_write_int(bs, 1, 1); // payload start indicator + } else { + /* No section concatenation yet!!!*/ + gf_bs_write_int(bs, 0, 1); // payload start indicator + } + + if (!stream->current_section_offset) payload_length = 183; + else payload_length = 184; + + if (section->length - stream->current_section_offset >= payload_length) { + padding_length = 0; + adaptation_field_control = M2TS_ADAPTATION_NONE; + } else { + /* in all the following cases, we write an adaptation field */ + adaptation_field_control = M2TS_ADAPTATION_AND_PAYLOAD; + /* we need at least 2 bytes for adaptation field headers (no pcr) */ + payload_length -= 2; + if (section->length - stream->current_section_offset >= payload_length) { + padding_length = 0; + } else { + padding_length = payload_length - section->length + stream->current_section_offset; + payload_length -= padding_length; + } + } + assert(payload_length + stream->current_section_offset <= section->length); + + gf_bs_write_int(bs, 0, 1); /*priority indicator*/ + gf_bs_write_int(bs, stream->pid, 13); /*pid*/ + gf_bs_write_int(bs, 0, 2); /*scrambling indicator*/ + gf_bs_write_int(bs, adaptation_field_control, 2); /*we do not use adaptation field for sections */ + gf_bs_write_int(bs, stream->continuity_counter, 4); /*continuity counter*/ + if (stream->continuity_counter < 15) stream->continuity_counter++; + else stream->continuity_counter=0; + + if (adaptation_field_control != M2TS_ADAPTATION_NONE) + m2ts_add_adaptation(bs, 0, 0, 0, padding_length); + + /*pointer field*/ + if (!stream->current_section_offset) { + /* no concatenations of sections in ts packets, so start address is 0 */ + gf_bs_write_u8(bs, 0); + } + gf_bs_del(bs); + + memcpy(packet+188-payload_length, section->data + stream->current_section_offset, payload_length); + stream->current_section_offset += payload_length; + + //m2ts_time_inc(stream->time, 1504/*188*8*/, muxer->bit_rate); + + if (stream->current_section_offset == section->length) { + stream->current_section_offset = 0; + stream->current_section = stream->current_section->next; + if (!stream->current_section) { + stream->current_table = stream->current_table->next; + /*carousel table*/ + if (!stream->current_table && stream->refresh_rate_ms) { + stream->current_table = stream->tables; + /*update ES time*/ + m2ts_time_inc(&stream->time, stream->refresh_rate_ms, 1000); + } + if (stream->current_table) stream->current_section = stream->current_table->section; + } + } + +} + + + +Bool m2ts_stream_process_pat(M2TS_Mux *muxer, M2TS_Mux_Stream *stream) +{ + if (stream->table_needs_update) { /* generate table payload */ + M2TS_Mux_Program *prog; + GF_BitStream *bs; + u8 *payload; + u32 size; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + prog = muxer->programs; + while (prog) { + gf_bs_write_u16(bs, prog->number); + gf_bs_write_int(bs, 0x7, 3); /*reserved*/ + gf_bs_write_int(bs, prog->pmt->pid, 13); /*reserved*/ + prog = prog->next; + } + gf_bs_get_content(bs, (char**)&payload, &size); + gf_bs_del(bs); + m2ts_mux_table_update(stream, GF_M2TS_TABLE_ID_PAT, muxer->ts_id, payload, size, 1, 0, 0); + stream->table_needs_update = 0; + free(payload); + } + return 1; +} + +Bool m2ts_stream_process_pmt(M2TS_Mux *muxer, M2TS_Mux_Stream *stream) +{ + if (stream->table_needs_update) { /* generate table payload */ + M2TS_Mux_Stream *es; + u8 *payload; + u32 length; + GF_BitStream *bs; + + bs = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, 0x7, 3); // reserved + gf_bs_write_int(bs, stream->program->pcr->pid, 13); + gf_bs_write_int(bs, 0xF, 4); // reserved + + if (!stream->program->iod) { + gf_bs_write_int(bs, 0, 12); // program info length =0 + } else { + u32 len; + GF_BitStream *bs_iod; + char *iod_data; + u32 iod_data_len; + + bs_iod = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE); + gf_odf_write_descriptor(bs_iod, stream->program->iod); + gf_bs_get_content(bs_iod, &iod_data, &iod_data_len); + gf_bs_del(bs_iod); + + len = iod_data_len + 4; + gf_bs_write_int(bs, len, 12); // program info length + + gf_bs_write_int(bs, GF_M2TS_MPEG4_IOD_DESCRIPTOR, 8); + len = iod_data_len + 2; + gf_bs_write_int(bs, len, 8); + + /* Scope_of_IOD_label : + 0x10 iod unique a l'intérieur de programme + 0x11 iod unoque dans le flux ts */ + gf_bs_write_int(bs, 2, 8); + + gf_bs_write_int(bs, 2, 8); // IOD_label + + gf_bs_write_data(bs, iod_data, iod_data_len); + free(iod_data); + } + es = stream->program->streams; + while (es) { + gf_bs_write_int(bs, es->mpeg2_stream_type, 8); + gf_bs_write_int(bs, 0x7, 3); // reserved + gf_bs_write_int(bs, es->pid, 13); + gf_bs_write_int(bs, 0xF, 4); // reserved + + /* Second Loop Descriptor */ + if (stream->program->iod) { + gf_bs_write_int(bs, 4, 12); // ES info length = 4 :only SL Descriptor + gf_bs_write_int(bs, GF_M2TS_MPEG4_SL_DESCRIPTOR, 8); + gf_bs_write_int(bs, 2, 8); + gf_bs_write_int(bs, es->ifce->stream_id, 16); // mpeg4_esid + } else { + gf_bs_write_int(bs, 0, 12); + } + es = es->next; + } + + gf_bs_get_content(bs, (char**)&payload, &length); + gf_bs_del(bs); + + m2ts_mux_table_update(stream, GF_M2TS_TABLE_ID_PMT, stream->program->number, payload, length, 1, 0, 0); + stream->table_needs_update = 0; + free(payload); + } + return 1; +} + +Bool m2ts_stream_process_stream(M2TS_Mux *muxer, M2TS_Mux_Stream *stream) +{ + if (stream->mpeg2_stream_type==GF_M2TS_SYSTEMS_MPEG4_SECTIONS) { + /*section not completely sent yet*/ + if (stream->current_section) return 1; + } + else if (stream->pck_offset < stream->pck.data_len) { + /*PES packet not completely sent yet*/ + return 1; + } + + /*PULL mode*/ + if (stream->ifce->caps & GF_ESI_AU_PULL_CAP) { + if (stream->pck.data_len) { + /*discard packet data if we use SL over PES*/ + if (stream->mpeg2_stream_type==GF_M2TS_SYSTEMS_MPEG4_PES) free(stream->pck.data); + /*release data*/ + stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_RELEASE, NULL); + } + stream->pck_offset = 0; + stream->pck.data_len = 0; + + /*EOS*/ + if (stream->ifce->caps & GF_ESI_STREAM_IS_OVER) return 0; + stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_PULL, &stream->pck); + } else { + M2TS_Packet *pck; + /*flush input pipe*/ + stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_FLUSH, NULL); + gf_mx_p(stream->mx); + /*discard first packet*/ + if (stream->pck_offset) { + assert(stream->pck_first); + pck = stream->pck_first; + stream->pck_first = pck->next; + free(pck->data); + free(pck); + } + stream->pck_offset = 0; + stream->pck.data_len = 0; + + /*fill pck*/ + pck = stream->pck_first; + if (!pck) { + gf_mx_v(stream->mx); + return 0; + } + stream->pck.cts = pck->cts; + stream->pck.data = pck->data; + stream->pck.data_len = pck->data_len; + stream->pck.dts = pck->dts; + stream->pck.flags = pck->flags; + gf_mx_v(stream->mx); + } + if (!(stream->pck.flags & GF_ESI_DATA_HAS_DTS)) + stream->pck.dts = stream->pck.cts; + + /*!! watchout !!*/ + if (stream->ts_scale) { + stream->pck.cts = (u64) (stream->ts_scale * (s64) stream->pck.cts); + stream->pck.dts = (u64) (stream->ts_scale * (s64) stream->pck.dts); + } + + /*SL-encapsultaion*/ + switch (stream->mpeg2_stream_type) { + case GF_M2TS_SYSTEMS_MPEG4_SECTIONS: + /*update SL config*/ + stream->sl_header.accessUnitStartFlag = (stream->pck.flags & GF_ESI_DATA_AU_START) ? 1 : 0; + stream->sl_header.accessUnitEndFlag = (stream->pck.flags & GF_ESI_DATA_AU_END) ? 1 : 0; + stream->sl_header.accessUnitLength += stream->pck.data_len; + stream->sl_header.randomAccessPointFlag = (stream->pck.flags & GF_ESI_DATA_AU_RAP) ? 1: 0; + stream->sl_header.compositionTimeStampFlag = (stream->pck.flags & GF_ESI_DATA_HAS_CTS) ? 1 : 0; + stream->sl_header.compositionTimeStamp = stream->pck.cts; + stream->sl_header.decodingTimeStampFlag = (stream->pck.flags & GF_ESI_DATA_HAS_DTS) ? 1: 0; + stream->sl_header.decodingTimeStamp = stream->pck.dts; + + m2ts_mux_table_update_mpeg4(stream, stream->table_id, muxer->ts_id, stream->pck.data, stream->pck.data_len, 1, 0, 0); + break; + case GF_M2TS_SYSTEMS_MPEG4_PES: + { + char *src_data; + u32 src_data_len; + + /*update SL config*/ + stream->sl_header.accessUnitStartFlag = (stream->pck.flags & GF_ESI_DATA_AU_START) ? 1 : 0; + stream->sl_header.accessUnitEndFlag = (stream->pck.flags & GF_ESI_DATA_AU_END) ? 1 : 0; + stream->sl_header.accessUnitLength += stream->pck.data_len; + stream->sl_header.randomAccessPointFlag = (stream->pck.flags & GF_ESI_DATA_AU_RAP) ? 1: 0; + stream->sl_header.compositionTimeStampFlag = (stream->pck.flags & GF_ESI_DATA_HAS_CTS) ? 1 : 0; + stream->sl_header.compositionTimeStamp = stream->pck.cts; + stream->sl_header.decodingTimeStampFlag = (stream->pck.flags & GF_ESI_DATA_HAS_DTS) ? 1: 0; + stream->sl_header.decodingTimeStamp = stream->pck.dts; + + src_data = stream->pck.data; + src_data_len = stream->pck.data_len; + stream->pck.data_len = 0; + stream->pck.data = NULL; + + gf_sl_packetize(&stream->ifce->sl_config, &stream->sl_header, src_data, src_data_len, &stream->pck.data, &stream->pck.data_len); + + /*discard src data*/ + if (!(stream->ifce->caps & GF_ESI_AU_PULL_CAP)) { + free(src_data); + stream->pck_first->data = stream->pck.data; + stream->pck_first->data_len = stream->pck.data_len; + } + } + break; + } + + + /*initializing the PCR*/ + if (!stream->program->pcr_init) { + if (stream==stream->program->pcr) { + stream->program->pcr_init_ts_time = muxer->time; + stream->program->pcr_init_time = stream->pck.dts; + stream->program->pcr_init = 1; + } else { + /*don't send until PCR is initialized*/ + return 0; + } + } + + /*move to current time in TS unit*/ + stream->time = stream->program->pcr_init_ts_time; + m2ts_time_inc(&stream->time, (u32) (stream->pck.dts - stream->program->pcr_init_time), 90000); + + /*compute bitrate if needed*/ + if (!stream->bit_rate) { + if (!stream->last_br_time) { + stream->last_br_time = stream->pck.dts + 1; + stream->bytes_since_last_time = stream->pck.data_len; + } else { + if (stream->pck.dts - stream->last_br_time - 1 >= 90000) { + u64 r = 8*stream->bytes_since_last_time; + r*=90000; + stream->bit_rate = (u32) (r / (stream->pck.dts - stream->last_br_time - 1)); + stream->program->mux->needs_reconfig = 1; + } else { + stream->bytes_since_last_time += stream->pck.data_len; + } + } + } + return 1; +} + +static u32 m2ts_stream_get_pes_header_length(M2TS_Mux_Stream *stream) +{ + u32 hdr_len; + /*not the AU start*/ + if (stream->pck_offset || !(stream->pck.flags & GF_ESI_DATA_AU_START) ) return 0; + hdr_len = 9; + if (stream->pck.flags & GF_ESI_DATA_HAS_CTS) hdr_len += 5; + if (stream->pck.flags & GF_ESI_DATA_HAS_DTS) hdr_len += 5; + return hdr_len; +} + +u32 m2ts_stream_add_pes_header(GF_BitStream *bs, M2TS_Mux_Stream *stream) +{ + u32 pes_len; + Bool use_pts, use_dts; + + gf_bs_write_int(bs, 0x1, 24);//packet start code + gf_bs_write_u8(bs, stream->mpeg2_stream_id);// stream id + + use_pts = (stream->pck.flags & GF_ESI_DATA_HAS_CTS) ? 1 : 0; + use_dts = (stream->pck.flags & GF_ESI_DATA_HAS_DTS) ? 1 : 0; + + pes_len = stream->pck.data_len + 3; // 3 = header size + if (use_pts) pes_len += 5; + if (use_dts) pes_len += 5; + gf_bs_write_int(bs, pes_len, 16); // pes packet length + + gf_bs_write_int(bs, 0x2, 2); // reserved + gf_bs_write_int(bs, 0x0, 2); // scrambling + gf_bs_write_int(bs, 0x0, 1); // priority + gf_bs_write_int(bs, 0x1, 1); // alignment indicator + gf_bs_write_int(bs, 0x0, 1); // copyright + gf_bs_write_int(bs, 0x0, 1); // original or copy + + gf_bs_write_int(bs, use_pts, 1); + gf_bs_write_int(bs, use_dts, 1); + gf_bs_write_int(bs, 0x0, 6); //6 flags = 0 (ESCR, ES_rate, DSM_trick, additional_copy, PES_CRC, PES_extension) + + gf_bs_write_int(bs, use_dts*5+use_pts*5, 8); + + if (use_pts) { + u64 t; + gf_bs_write_int(bs, use_dts ? 0x3 : 0x2, 4); // reserved '0011' || '0010' + t = ((stream->pck.cts >> 30) & 0x7); + gf_bs_write_long_int(bs, t, 3); + gf_bs_write_int(bs, 1, 1); // marker bit + t = ((stream->pck.cts >> 15) & 0x7fff); + gf_bs_write_long_int(bs, t, 15); + gf_bs_write_int(bs, 1, 1); // marker bit + t = stream->pck.cts & 0x7fff; + gf_bs_write_long_int(bs, t, 15); + gf_bs_write_int(bs, 1, 1); // marker bit + } + + if (use_dts) { + u64 t; + gf_bs_write_int(bs, 0x1, 4); // reserved '0001' + t = ((stream->pck.dts >> 30) & 0x7); + gf_bs_write_long_int(bs, t, 3); + gf_bs_write_int(bs, 1, 1); // marker bit + t = ((stream->pck.dts >> 15) & 0x7fff); + gf_bs_write_long_int(bs, t, 15); + gf_bs_write_int(bs, 1, 1); // marker bit + t = stream->pck.dts & 0x7fff; + gf_bs_write_long_int(bs, t, 15); + gf_bs_write_int(bs, 1, 1); // marker bit + } + return pes_len+4; // 4 = start code + stream_id +} + +void m2ts_mux_pes_get_next_packet(M2TS_Mux_Stream *stream, u8 *packet) +{ + GF_BitStream *bs; + Bool is_rap, needs_pcr; + u32 remain, adaptation_field_control, payload_length, padding_length, hdr_len; + + assert(stream->pid); + bs = gf_bs_new(packet, 188, GF_BITSTREAM_WRITE); + + hdr_len = m2ts_stream_get_pes_header_length(stream); + remain = stream->pck.data_len - stream->pck_offset; + + needs_pcr = (hdr_len && (stream==stream->program->pcr) ) ? 1 : 0; + adaptation_field_control = M2TS_ADAPTATION_NONE; + payload_length = 184 - hdr_len; + padding_length = 0; + + if (needs_pcr) { + adaptation_field_control = M2TS_ADAPTATION_AND_PAYLOAD; + /*AF headers + PCR*/ + payload_length -= 8; + } else if (remain<184) { + /*AF headers*/ + payload_length -= 2; + adaptation_field_control = M2TS_ADAPTATION_AND_PAYLOAD; + } + if (remain>=payload_length) { + padding_length = 0; + } else { + padding_length = payload_length - remain; + payload_length -= padding_length; + } + + gf_bs_write_int(bs, 0x47, 8); // sync byte + gf_bs_write_int(bs, 0, 1); // error indicator + gf_bs_write_int(bs, hdr_len ? 1 : 0, 1); // start ind + gf_bs_write_int(bs, 0, 1); // transport priority + gf_bs_write_int(bs, stream->pid, 13); // pid + gf_bs_write_int(bs, 0, 2); // scrambling + gf_bs_write_int(bs, adaptation_field_control, 2); // we do not use adaptation field for sections + gf_bs_write_int(bs, stream->continuity_counter, 4); // continuity counter + if (stream->continuity_counter < 15) stream->continuity_counter++; + else stream->continuity_counter=0; + + is_rap = (hdr_len && (stream->pck.flags & GF_ESI_DATA_AU_RAP) ) ? 1 : 0; + + if (adaptation_field_control != M2TS_ADAPTATION_NONE) { + /*FIXME - WE NEED A REAL PCR, NOT THE DTS*/ + m2ts_add_adaptation(bs, needs_pcr, stream->pck.dts, is_rap, padding_length); + } + + /*FIXME - we need proper packetization here in case we're not fed with full AUs*/ + if (hdr_len) m2ts_stream_add_pes_header(bs, stream); + + gf_bs_del(bs); + + memcpy(packet+188-payload_length, stream->pck.data + stream->pck_offset, payload_length); + stream->pck_offset += payload_length; + + assert(stream->pck_offset <= stream->pck.data_len); + m2ts_time_inc(&stream->time, payload_length, stream->bit_rate); +} + + +M2TS_Mux_Stream *m2ts_stream_new(u32 pid) { + M2TS_Mux_Stream *stream; + + GF_SAFEALLOC(stream, M2TS_Mux_Stream); + stream->pid = pid; + stream->process = m2ts_stream_process_stream; + + return stream; +} + +GF_Err m2ts_output_ctrl(GF_ESInterface *_self, u32 ctrl_type, void *param) +{ + GF_ESIPacket *esi_pck; + M2TS_Packet *pck; + M2TS_Mux_Stream *stream = (M2TS_Mux_Stream *)_self->output_udta; + switch (ctrl_type) { + case GF_ESI_OUTPUT_DATA_DISPATCH: + GF_SAFEALLOC(pck, M2TS_Packet); + esi_pck = (GF_ESIPacket *)param; + pck->data_len = esi_pck->data_len; + pck->data = malloc(sizeof(char)*pck->data_len); + memcpy(pck->data, esi_pck->data, pck->data_len); + pck->flags = esi_pck->flags; + pck->cts = esi_pck->cts; + pck->dts = esi_pck->dts; + gf_mx_p(stream->mx); + if (!stream->pck_first) { + stream->pck_first = stream->pck_last = pck; + } else { + stream->pck_last->next = pck; + stream->pck_last = pck; + } + gf_mx_v(stream->mx); + break; + } + return GF_OK; +} + + + +M2TS_Mux_Stream *m2ts_program_stream_add(M2TS_Mux_Program *program, struct __elementary_stream_ifce *ifce, u32 pid, Bool is_pcr) +{ + M2TS_Mux_Stream *stream, *st; + + stream = m2ts_stream_new(pid); + stream->ifce = ifce; + stream->pid = pid; + stream->program = program; + if (is_pcr) program->pcr = stream; + if (program->streams) { + st = program->streams; + while (st->next) st = st->next; + st->next = stream; + } else { + program->streams = stream; + } + if (program->pmt) program->pmt->table_needs_update = 1; + stream->bit_rate = ifce->bit_rate; + + switch (ifce->stream_type) { + case GF_STREAM_VISUAL: + /*just pick first valid stream_id in visual range*/ + stream->mpeg2_stream_id = 0xE0; + switch (ifce->object_type_indication) { + case 0x20: + stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG4; + break; + case 0x21: + stream->mpeg2_stream_type = GF_M2TS_VIDEO_H264; + break; + case 0x6A: + stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG1; + break; + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG2; + break; + /*JPEG/PNG carried in MPEG-4 PES*/ + case 0x6C: + case 0x6D: + stream->mpeg2_stream_type = GF_M2TS_SYSTEMS_MPEG4_PES; + stream->mpeg2_stream_id = 0xFA; + break; + default: + break; + } + break; + case GF_STREAM_AUDIO: + switch (ifce->object_type_indication) { + case 0x6B: + stream->mpeg2_stream_type = GF_M2TS_AUDIO_MPEG1; + break; + case 0x69: + stream->mpeg2_stream_type = GF_M2TS_AUDIO_MPEG2; + break; + case 0x40: + stream->mpeg2_stream_type = GF_M2TS_AUDIO_AAC; + break; + } + /*just pick first valid stream_id in audio range*/ + stream->mpeg2_stream_id = 0xC0; + break; + case GF_STREAM_SCENE: + case GF_STREAM_OD: + stream->mpeg2_stream_type = GF_M2TS_SYSTEMS_MPEG4_SECTIONS; + stream->mpeg2_stream_id = 0xFA; + stream->table_id = (ifce->stream_type==GF_STREAM_OD) ? GF_M2TS_TABLE_ID_MPEG4_OD : GF_M2TS_TABLE_ID_MPEG4_BIFS; + break; + } + + /*override signaling for all streams except BIFS/OD, to use MPEG-4 PES*/ + if (program->mux->mpeg4_signaling) { + if (stream->mpeg2_stream_type != GF_M2TS_SYSTEMS_MPEG4_SECTIONS) { + stream->mpeg2_stream_type = GF_M2TS_SYSTEMS_MPEG4_PES; + stream->mpeg2_stream_id = 0xFA;/*ISO/IEC14496-1_SL-packetized_stream*/ + } + } + + stream->ifce->output_ctrl = m2ts_output_ctrl; + stream->ifce->output_udta = stream; + stream->mx = gf_mx_new("M2TS PID"); + if (ifce->timescale != 90000) stream->ts_scale = 90000.0 / ifce->timescale; + return stream; +} + +#define M2TS_PSI_REFRESH_RATE 200 + +M2TS_Mux_Program *m2ts_mux_program_add(M2TS_Mux *muxer, u32 program_number, u32 pmt_pid) +{ + M2TS_Mux_Program *program; + + GF_SAFEALLOC(program, M2TS_Mux_Program); + program->mux = muxer; + program->number = program_number; + if (muxer->programs) { + M2TS_Mux_Program *p = muxer->programs; + while (p->next) p = p->next; + p->next = program; + } else { + muxer->programs = program; + } + program->pmt = m2ts_stream_new(pmt_pid); + program->pmt->program = program; + muxer->pat->table_needs_update = 1; + program->pmt->process = m2ts_stream_process_pmt; + program->pmt->refresh_rate_ms = M2TS_PSI_REFRESH_RATE; + return program; +} + +M2TS_Mux *m2ts_mux_new(u32 mux_rate, Bool real_time) +{ + GF_BitStream *bs; + M2TS_Mux *muxer; + GF_SAFEALLOC(muxer, M2TS_Mux); + muxer->pat = m2ts_stream_new(GF_M2TS_PID_PAT); + muxer->pat->process = m2ts_stream_process_pat; + muxer->pat->refresh_rate_ms = M2TS_PSI_REFRESH_RATE; + muxer->real_time = real_time; + muxer->bit_rate = mux_rate; + if (mux_rate) muxer->fixed_rate = 1; + + /*format NULL packet*/ + bs = gf_bs_new(muxer->null_pck, 188, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, 0x47, 8); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 0x1FFF, 13); + gf_bs_write_int(bs, 0, 2); + gf_bs_write_int(bs, 1, 2); + gf_bs_write_int(bs, 0, 4); + gf_bs_del(bs); + return muxer; +} + +void m2ts_mux_stream_del(M2TS_Mux_Stream *st) +{ + while (st->tables) { + M2TS_Mux_Table *tab = st->tables->next; + while (st->tables->section) { + M2TS_Mux_Section *sec = st->tables->section->next; + free(st->tables->section->data); + free(st->tables->section); + st->tables->section = sec; + } + free(st->tables); + st->tables = tab; + } + while (st->pck_first) { + M2TS_Packet *pck = st->pck_first; + st->pck_first = pck->next; + free(pck->data); + free(pck); + } + if (st->mx) gf_mx_del(st->mx); + free(st); +} + +void m2ts_mux_program_del(M2TS_Mux_Program *prog) +{ + while (prog->streams) { + M2TS_Mux_Stream *st = prog->streams->next; + m2ts_mux_stream_del(prog->streams); + prog->streams = st; + } + m2ts_mux_stream_del(prog->pmt); + free(prog); +} + +void m2ts_mux_del(M2TS_Mux *mux) +{ + while (mux->programs) { + M2TS_Mux_Program *p = mux->programs->next; + m2ts_mux_program_del(mux->programs); + mux->programs = p; + } + m2ts_mux_stream_del(mux->pat); + free(mux); +} + +void m2ts_mux_update_config(M2TS_Mux *mux, Bool reset_time) +{ + M2TS_Mux_Program *prog; + + if (!mux->fixed_rate) { + mux->bit_rate = 0; + + /*get PAT bitrate*/ + m2ts_mux_table_update_bitrate(mux, mux->pat); + mux->bit_rate += mux->pat->bit_rate; + } + + prog = mux->programs; + while (prog) { + M2TS_Mux_Stream *stream = prog->streams; + while (stream) { + /*!! WATCHOUT - this is raw bitrate without PES header overhead !!*/ + if (!mux->fixed_rate) { + mux->bit_rate += stream->bit_rate; + /*update PCR every 100ms - we need at least 8 bytes without padding*/ + if (stream == prog->pcr) mux->bit_rate += 8*8*10; + } + + /*reset mux time*/ + if (reset_time) stream->time.sec = stream->time.nanosec = 0; + stream = stream->next; + } + /*get PMT bitrate*/ + if (!mux->fixed_rate) { + m2ts_mux_table_update_bitrate(mux, prog->pmt); + mux->bit_rate += prog->pmt->bit_rate; + } + prog = prog->next; + } + /*reset mux time*/ + if (reset_time) { + mux->time.sec = mux->time.nanosec = 0; + mux->init_sys_time = 0; + } +} + +u32 gf_m2ts_get_sys_clock(M2TS_Mux *muxer) +{ + return gf_sys_clock() - muxer->init_sys_time; +} +u32 gf_m2ts_get_ts_clock(M2TS_Mux *muxer) +{ + u32 now, init; + init = muxer->init_ts_time.sec*1000 + muxer->init_ts_time.nanosec/1000000; + now = muxer->time.sec*1000 + muxer->time.nanosec/1000000; + return now-init; +} + + +const char *m2ts_mux_process(M2TS_Mux *muxer, u32 *status) +{ + M2TS_Mux_Program *program; + M2TS_Mux_Stream *stream, *stream_to_process; + M2TS_Time time; + u32 now, nb_streams, nb_streams_done; + char *ret; + Bool res; + + nb_streams = nb_streams_done = 0; + *status = GF_M2TS_STATE_IDLE; + + now = gf_sys_clock(); + + if (muxer->real_time) { + if (!muxer->init_sys_time) { + muxer->init_sys_time = now; + muxer->init_ts_time = muxer->time; + } else { + u32 diff = now - muxer->init_sys_time; + M2TS_Time now = muxer->init_ts_time; + m2ts_time_inc(&now, diff, 1000); + + if (m2ts_time_less(&now, &muxer->time)) + return NULL; + } + } + + stream_to_process = NULL; + time = muxer->time; + + if (muxer->needs_reconfig) { + m2ts_mux_update_config(muxer, 0); + muxer->needs_reconfig = 0; + } + + res = muxer->pat->process(muxer, muxer->pat); + if (res && m2ts_time_less_or_equal(&muxer->pat->time, &time) ) { + time = muxer->pat->time; + stream_to_process = muxer->pat; + /*force sending the PAT regardless of other streams*/ + goto send_pck; + } + + program = muxer->programs; + while (program) { + res = program->pmt->process(muxer, program->pmt); + if (res && m2ts_time_less(&program->pmt->time, &time) ) { + time = program->pmt->time; + stream_to_process = program->pmt; + /*force sending the PMT regardless of other streams*/ + goto send_pck; + } + stream = program->streams; + while (stream) { + nb_streams ++; + res = stream->process(muxer, stream); + if (res) { + if (m2ts_time_less(&stream->time, &time)) { + time = stream->time; + stream_to_process = stream; + } + } else { + if (stream->ifce->caps & GF_ESI_STREAM_IS_OVER) nb_streams_done ++; + } + stream = stream->next; + } + program = program->next; + } + +send_pck: + + ret = NULL; + if (!stream_to_process) { + if (nb_streams && (nb_streams==nb_streams_done)) { + *status = GF_M2TS_STATE_EOS; + } else { + *status = GF_M2TS_STATE_PADDING; + } + /* padding packets ?? */ + if (muxer->fixed_rate) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Inserting empty packet at %d:%d\n", time.sec, time.nanosec)); + ret = muxer->null_pck; + muxer->tot_pad_sent++; + } + /*we still need to increase the mux time, even though we're not fixed-rate*/ + else { + m2ts_time_inc(&muxer->time, 1504/*188*8*/, muxer->bit_rate); + } + } else { + if (stream_to_process->tables) { + m2ts_mux_table_get_next_packet(stream_to_process, muxer->dst_pck); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Sending table packet from PID %d at %d:%d\n", stream_to_process->pid, time.sec, time.nanosec)); + } else { + m2ts_mux_pes_get_next_packet(stream_to_process, muxer->dst_pck); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Sending PES packet from PID %d at %d:%d\n", stream_to_process->pid, time.sec, time.nanosec)); + } + ret = muxer->dst_pck; + *status = GF_M2TS_STATE_DATA; + } + if (ret) { + muxer->tot_pck_sent++; + /*increment time*/ + m2ts_time_inc(&muxer->time, 1504/*188*8*/, muxer->bit_rate); + + if (muxer->real_time) { + muxer->pck_sent++; + if (now - muxer->last_br_time > 500) { + u64 size = 8*188*muxer->pck_sent*1000; + muxer->avg_br = (u32) (size/(now - muxer->last_br_time)); + muxer->last_br_time = now; + muxer->pck_sent=0; + } + } + } + return ret; +} + +typedef struct +{ + GF_ISOFile *mp4; + u32 track, sample_number, sample_count; + GF_ISOSample *sample; + /*refresh rate for images*/ + u32 refresh_rate_ms, nb_repeat_last; +} GF_ESIMP4; + +static GF_Err mp4_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) +{ + GF_ESIMP4 *priv = (GF_ESIMP4 *)ifce->input_udta; + if (!priv) return GF_BAD_PARAM; + + switch (act_type) { + case GF_ESI_INPUT_DATA_FLUSH: + return GF_OK; + case GF_ESI_INPUT_DATA_PULL: + { + GF_ESIPacket *pck = (GF_ESIPacket *)param; + if (!priv->sample) { + priv->sample = gf_isom_get_sample(priv->mp4, priv->track, priv->sample_number+1, NULL); + } + if (!priv->sample) return GF_IO_ERR; + pck->data_len = priv->sample->dataLength; + pck->data = priv->sample->data; + pck->flags = GF_ESI_DATA_AU_START | GF_ESI_DATA_AU_END | GF_ESI_DATA_HAS_CTS; + pck->cts = priv->sample->DTS; + + if (priv->nb_repeat_last) { + pck->cts += priv->nb_repeat_last*ifce->timescale * priv->refresh_rate_ms / 1000; + } + + if (priv->sample->CTS_Offset) { + pck->dts = pck->cts; + pck->cts += priv->sample->CTS_Offset; + pck->flags |= GF_ESI_DATA_HAS_DTS; + } + if (priv->sample->IsRAP) pck->flags |= GF_ESI_DATA_AU_RAP; + } + return GF_OK; + case GF_ESI_INPUT_DATA_RELEASE: + if (priv->sample) { + gf_isom_sample_del(&priv->sample); + priv->sample_number++; + if (priv->sample_number==priv->sample_count) { + if (priv->refresh_rate_ms) { + priv->nb_repeat_last++; + priv->sample_number--; + } else { + ifce->caps |= GF_ESI_STREAM_IS_OVER; + } + } + } + return GF_OK; + case GF_ESI_INPUT_DESTROY: + free(priv); + ifce->input_udta = NULL; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + +static void fill_isom_es_ifce(GF_ESInterface *ifce, GF_ISOFile *mp4, u32 track_num) +{ + GF_ESIMP4 *priv; + char _lan[4]; + GF_DecoderConfig *dcd; + u64 avg_rate; + + GF_SAFEALLOC(priv, GF_ESIMP4); + + priv->mp4 = mp4; + priv->track = track_num; + priv->sample_count = gf_isom_get_sample_count(mp4, track_num); + + memset(ifce, 0, sizeof(GF_ESInterface)); + ifce->caps = GF_ESI_AU_PULL_CAP; + ifce->stream_id = gf_isom_get_track_id(mp4, track_num); + dcd = gf_isom_get_decoder_config(mp4, track_num, 1); + ifce->stream_type = dcd->streamType; + ifce->object_type_indication = dcd->objectTypeIndication; + gf_odf_desc_del((GF_Descriptor *)dcd); + gf_isom_get_media_language(mp4, track_num, _lan); + ifce->lang = GF_4CC(_lan[0],_lan[1],_lan[2],' '); + + ifce->timescale = gf_isom_get_media_timescale(mp4, track_num); + ifce->duration = gf_isom_get_media_timescale(mp4, track_num); + avg_rate = gf_isom_get_media_data_size(mp4, track_num); + avg_rate *= ifce->timescale * 8; + avg_rate /= gf_isom_get_media_duration(mp4, track_num); + + if (gf_isom_has_time_offset(mp4, track_num)) ifce->caps |= GF_ESI_SIGNAL_DTS; + + /*turn on image repeat*/ + switch (ifce->object_type_indication) { + case 0x6C: + case 0x6D: + //priv->refresh_rate_ms = 500; + break; + } + ifce->bit_rate = (u32) avg_rate; + ifce->duration = (Double) (s64) gf_isom_get_media_duration(mp4, track_num); + ifce->duration /= ifce->timescale; + + ifce->input_ctrl = mp4_input_ctrl; + ifce->input_udta = priv; +} + +typedef struct +{ + /*RTP channel*/ + GF_RTPChannel *rtp_ch; + + /*depacketizer*/ + GF_RTPDepacketizer *depacketizer; + + GF_ESIPacket pck; + + GF_ESInterface *ifce; +} GF_ESIRTP; + +static GF_Err rtp_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) +{ + u32 size, PayloadStart; + GF_Err e; + GF_RTPHeader hdr; + char buffer[8000]; + GF_ESIRTP *rtp = (GF_ESIRTP*)ifce->input_udta; + + if (!ifce->input_udta) return GF_BAD_PARAM; + + switch (act_type) { + case GF_ESI_INPUT_DATA_FLUSH: + /*flush rtp channel*/ + while (1) { + size = gf_rtp_read_rtp(rtp->rtp_ch, buffer, 8000); + if (!size) break; + e = gf_rtp_decode_rtp(rtp->rtp_ch, buffer, size, &hdr, &PayloadStart); + if (e) return e; + gf_rtp_depacketizer_process(rtp->depacketizer, &hdr, buffer + PayloadStart, size - PayloadStart); + } + /*flush rtcp channel*/ + while (1) { + size = gf_rtp_read_rtcp(rtp->rtp_ch, buffer, 8000); + if (!size) break; + e = gf_rtp_decode_rtcp(rtp->rtp_ch, buffer, size); + if (e == GF_EOS) ifce->caps |= GF_ESI_STREAM_IS_OVER; + } + return GF_OK; + case GF_ESI_INPUT_DESTROY: + gf_rtp_depacketizer_del(rtp->depacketizer); + gf_rtp_del(rtp->rtp_ch); + free(rtp); + ifce->input_udta = NULL; + return GF_OK; + } + return GF_OK; +} + +static void rtp_sl_packet_cbk(void *udta, char *payload, u32 size, GF_SLHeader *hdr, GF_Err e) +{ + GF_ESIRTP *rtp = (GF_ESIRTP*)udta; + rtp->pck.data = payload; + rtp->pck.data_len = size; + rtp->pck.dts = hdr->decodingTimeStamp; + rtp->pck.cts = hdr->compositionTimeStamp; + rtp->pck.flags = 0; + if (hdr->compositionTimeStampFlag) rtp->pck.flags |= GF_ESI_DATA_HAS_CTS; + if (hdr->decodingTimeStampFlag) rtp->pck.flags |= GF_ESI_DATA_HAS_DTS; + if (hdr->randomAccessPointFlag) rtp->pck.flags |= GF_ESI_DATA_AU_RAP; + if (hdr->accessUnitStartFlag) rtp->pck.flags |= GF_ESI_DATA_AU_START; + if (hdr->accessUnitEndFlag) rtp->pck.flags |= GF_ESI_DATA_AU_END; + rtp->ifce->output_ctrl(rtp->ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &rtp->pck); +} + +void fill_rtp_es_ifce(GF_ESInterface *ifce, GF_SDPMedia *media, GF_SDPInfo *sdp) +{ + u32 i; + GF_Err e; + GF_X_Attribute*att; + GF_ESIRTP *rtp; + GF_RTPMap*map; + GF_SDPConnection *conn; + GF_RTSPTransport trans; + + /*check connection*/ + conn = sdp->c_connection; + if (!conn) conn = (GF_SDPConnection*)gf_list_get(media->Connections, 0); + + /*check payload type*/ + map = (GF_RTPMap*)gf_list_get(media->RTPMaps, 0); + GF_SAFEALLOC(rtp, GF_ESIRTP); + + memset(ifce, 0, sizeof(GF_ESInterface)); + rtp->rtp_ch = gf_rtp_new(); + i=0; + while ((att = (GF_X_Attribute*)gf_list_enum(media->Attributes, &i))) { + if (!stricmp(att->Name, "mpeg4-esid") && att->Value) ifce->stream_id = atoi(att->Value); + } + + memset(&trans, 0, sizeof(GF_RTSPTransport)); + trans.Profile = media->Profile; + trans.source = conn ? conn->host : sdp->o_address; + trans.IsUnicast = gf_sk_is_multicast_address(trans.source) ? 0 : 1; + if (!trans.IsUnicast) { + trans.port_first = media->PortNumber; + trans.port_last = media->PortNumber + 1; + trans.TTL = conn ? conn->TTL : 0; + } else { + trans.client_port_first = media->PortNumber; + trans.client_port_last = media->PortNumber + 1; + } + + if (gf_rtp_setup_transport(rtp->rtp_ch, &trans, NULL) != GF_OK) { + gf_rtp_del(rtp->rtp_ch); + fprintf(stdout, "Cannot initialize RTP transport\n"); + return; + } + /*setup depacketizer*/ + rtp->depacketizer = gf_rtp_depacketizer_new(media, rtp_sl_packet_cbk, rtp); + if (!rtp->depacketizer) { + gf_rtp_del(rtp->rtp_ch); + fprintf(stdout, "Cannot create RTP depacketizer\n"); + return; + } + /*setup channel*/ + gf_rtp_setup_payload(rtp->rtp_ch, map); + ifce->input_udta = rtp; + ifce->input_ctrl = rtp_input_ctrl; + rtp->ifce = ifce; + + ifce->object_type_indication = rtp->depacketizer->sl_map.ObjectTypeIndication; + ifce->stream_type = rtp->depacketizer->sl_map.StreamType; + ifce->timescale = gf_rtp_get_clockrate(rtp->rtp_ch); + + /*DTS signaling is only supported for MPEG-4 visual*/ + if (rtp->depacketizer->sl_map.DTSDeltaLength) ifce->caps |= GF_ESI_SIGNAL_DTS; + + gf_rtp_depacketizer_reset(rtp->depacketizer, 1); + e = gf_rtp_initialize(rtp->rtp_ch, 0x100000ul, 0, 0, 10, 200, NULL); + if (e!=GF_OK) { + gf_rtp_del(rtp->rtp_ch); + fprintf(stdout, "Cannot initialize RTP channel: %s\n", gf_error_to_string(e)); + return; + } + fprintf(stdout, "RTP interface initialized\n"); +} + +#define MAX_MUX_SRC_PROG 100 +typedef struct +{ + GF_ISOFile *mp4; + u32 nb_streams, pcr_idx; + GF_ESInterface streams[40]; + GF_Descriptor *iod; +} M2TSProgram; + +Bool open_program(M2TSProgram *prog, const char *src) +{ + GF_SDPInfo *sdp; + u32 i; + GF_Err e; + + memset(prog, 0, sizeof(M2TSProgram)); + + /* Open ISO file */ + if (gf_isom_probe_file(src)) { + u32 nb_tracks; + u32 first_audio = 0; + prog->mp4 = gf_isom_open(src, GF_ISOM_OPEN_READ, 0); + prog->nb_streams = 0; + /*on MPEG-2 TS, carry 3GPP timed text as MPEG-4 Part17*/ + gf_isom_text_set_streaming_mode(prog->mp4, 1); + nb_tracks = gf_isom_get_track_count(prog->mp4); + for (i=0; imp4, i+1) == GF_ISOM_MEDIA_HINT) + continue; + fill_isom_es_ifce(&prog->streams[i], prog->mp4, i+1); + prog->nb_streams++; + /*get first visual stream as PCR*/ + if (!prog->pcr_idx && + (gf_isom_get_media_type(prog->mp4, i+1) == GF_ISOM_MEDIA_VISUAL) && + (gf_isom_get_sample_count(prog->mp4, i+1)>1) ) { + prog->pcr_idx = i+1; + } + if (!first_audio && (gf_isom_get_media_type(prog->mp4, i+1) == GF_ISOM_MEDIA_AUDIO) ) { + first_audio = i+1; + } + prog->streams[i].sl_config.timestampResolution = 90000; //prog->streams[i].timescale; + prog->streams[i].sl_config.useRandomAccessPointFlag = 1; + prog->streams[i].sl_config.useAccessUnitStartFlag = 1; + prog->streams[i].sl_config.useAccessUnitEndFlag = 1; + prog->streams[i].sl_config.useTimestampsFlag = 1; + prog->streams[i].sl_config.timestampLength = 33; + prog->streams[i].sl_config.tag = GF_ODF_SLC_TAG; + gf_isom_set_extraction_slc(prog->mp4, i+1, 1, &prog->streams[i].sl_config); + } + /* WARNING: the returned IOD may be different from the one actually in the file + because it rewrites the SL config according to SL extraction settings. */ + prog->iod = gf_isom_get_root_od(prog->mp4); + /*if no visual PCR found, use first audio*/ + if (!prog->pcr_idx) prog->pcr_idx = first_audio; + if (prog->pcr_idx) prog->pcr_idx-=1; + return 1; + } + + /*open SDP file*/ + if (strstr(src, ".sdp")) { + char *sdp_buf; + u32 sdp_size; + FILE *_sdp = fopen(src, "rt"); + if (!_sdp) { + fprintf(stderr, "Error opening %s - no such file\n", src); + return 0; + } + fseek(_sdp, 0, SEEK_END); + sdp_size = ftell(_sdp); + fseek(_sdp, 0, SEEK_SET); + sdp_buf = (char*)malloc(sizeof(char)*sdp_size); + memset(sdp_buf, 0, sizeof(char)*sdp_size); + fread(sdp_buf, sdp_size, 1, _sdp); + fclose(_sdp); + + sdp = gf_sdp_info_new(); + e = gf_sdp_info_parse(sdp, sdp_buf, sdp_size); + free(sdp_buf); + if (e) { + fprintf(stderr, "Error opening %s : %s\n", src, gf_error_to_string(e)); + gf_sdp_info_del(sdp); + return 0; + } + prog->nb_streams = gf_list_count(sdp->media_desc); + for (i=0; inb_streams; i++) { + GF_SDPMedia *media = gf_list_get(sdp->media_desc, i); + fill_rtp_es_ifce(&prog->streams[i], media, sdp); + if (!prog->pcr_idx && (prog->streams[i].stream_type == GF_STREAM_VISUAL)) { + prog->pcr_idx = i+1; + } + } + if (prog->pcr_idx) prog->pcr_idx-=1; + gf_sdp_info_del(sdp); + return 2; + } else { + fprintf(stderr, "Error opening %s - not a supported input media, skipping.\n", src); + return 0; + } +} + +int main(int argc, char **argv) +{ + const char *ts_pck; + GF_Err e; + u32 res, run_time; + Bool real_time, mpeg4_signaling; + M2TS_Mux *muxer; + u32 i, j, mux_rate, nb_progs, cur_pid; + char *ts_out = NULL; +FILE *ts_file; + GF_Socket *ts_udp; + GF_RTPChannel *ts_rtp; + GF_RTSPTransport tr; + GF_RTPHeader hdr; + u16 port = 1234; + u32 output_type; + M2TSProgram progs[MAX_MUX_SRC_PROG]; + + real_time=0; + output_type = 0; + ts_file = NULL; + ts_udp = NULL; + ts_rtp = NULL; + ts_out = NULL; + nb_progs = 0; + mux_rate = 0; + run_time = 0; + mpeg4_signaling = 0; + for (i=1; impeg4_signaling = mpeg4_signaling; + /* Open mpeg2ts file*/ + switch(output_type) { + case 0: + ts_file = fopen(ts_out, "wb"); + if (!ts_file ) { + fprintf(stderr, "Error opening %s\n", ts_out); + goto exit; + } + break; + case 1: + ts_udp = gf_sk_new(GF_SOCK_TYPE_UDP); + if (gf_sk_is_multicast_address((char *)ts_out)) { + e = gf_sk_setup_multicast(ts_udp, (char *)ts_out, port, 0, 0, NULL); + } else { + e = gf_sk_bind(ts_udp, NULL, port, (char *)ts_out, port, GF_SOCK_REUSE_PORT); + } + if (e) { + fprintf(stdout, "Error inhitializing UDP socket: %s\n", gf_error_to_string(e)); + goto exit; + } + break; + case 2: + ts_rtp = gf_rtp_new(); + gf_rtp_set_ports(ts_rtp, port); + tr.IsUnicast = gf_sk_is_multicast_address((char *)ts_out) ? 0 : 1; + tr.Profile="RTP/AVP"; + tr.destination = (char *)ts_out; + tr.source = "0.0.0.0"; + tr.IsRecord = 0; + tr.Append = 0; + tr.SSRC = rand(); + tr.port_first = port; + tr.port_last = port+1; + if (tr.IsUnicast) { + tr.client_port_first = port; + tr.client_port_last = port+1; + } else { + tr.source = (char *)ts_out; + } + res = gf_rtp_setup_transport(ts_rtp, &tr, (char *)ts_out); + if (res !=0) { + fprintf(stdout, "Cannot setup RTP transport info\n"); + goto exit; + } + res = gf_rtp_initialize(ts_rtp, 0, 1, 1500, 0, 0, NULL); + if (res !=0) { + fprintf(stdout, "Cannot initialize RTP sockets\n"); + goto exit; + } + memset(&hdr, 0, sizeof(GF_RTPHeader)); + hdr.Version = 2; + hdr.PayloadType = 33; /*MP2T*/ + hdr.SSRC = tr.SSRC; + hdr.Marker = 0; + break; + } + + cur_pid = 100; + for (i=0; impeg4_signaling) program->iod = progs[i].iod; + for (j=0; j=GF_M2TS_STATE_PADDING) break; + } + break; + case 1: + while ((ts_pck = m2ts_mux_process(muxer, &status)) != NULL) { + e = gf_sk_send(ts_udp, (char*)ts_pck, 188); + if (e) + fprintf(stdout, "Error %s sending UDP packet\n", gf_error_to_string(e)); + if (status>=GF_M2TS_STATE_PADDING) break; + } + break; + case 2: + while ((ts_pck = m2ts_mux_process(muxer, &status)) != NULL) { + hdr.SequenceNumber++; + /*muxer clock at 90k*/ + ts = muxer->time.sec*90000 + muxer->time.nanosec*9/100000; + /*FIXME - better discontinuity check*/ + hdr.Marker = (ts < hdr.TimeStamp) ? 1 : 0; + hdr.TimeStamp = ts; + e = gf_rtp_send_packet(ts_rtp, &hdr, 0, 0, (char*)ts_pck, 188); + if (e) + fprintf(stdout, "Error %s sending RTP packet\n", gf_error_to_string(e)); + if (status>=GF_M2TS_STATE_PADDING) break; + } + break; + } + if (real_time) { + /*abort*/ + if (gf_prompt_has_input()) { + char c = gf_prompt_get_char(); + if (c == 'q') break; + } + fprintf(stdout, "M2TS: time %d - TS time %d - avg bitrate %d\r", gf_m2ts_get_sys_clock(muxer), gf_m2ts_get_ts_clock(muxer), muxer->avg_br); + } else if (run_time) { + if (gf_m2ts_get_ts_clock(muxer) > run_time) { + fprintf(stdout, "Stoping multiplex at %d ms (requested runtime %d ms)\n", gf_m2ts_get_ts_clock(muxer), run_time); + break; + } + } else if (status==GF_M2TS_STATE_EOS) { + break; + } + } + + +exit: + if (ts_file) fclose(ts_file); + if (ts_udp) gf_sk_del(ts_udp); + if (ts_rtp) gf_rtp_del(ts_rtp); + if (ts_out) free(ts_out); + m2ts_mux_del(muxer); + + for (i=0; i +#include +#include +#include +#include "mp42ts.h" + +#if 0 +/************************************************************ + * SL-Packetized Stream related functions + ************************************************************/ +void config_sample_hdr(M2TS_mux_stream *stream) +{ + GF_SAFEALLOC(stream->SLHeader, GF_SLHeader); + if (stream->sample){ + stream->SLHeader->accessUnitStartFlag = 1; + stream->SLHeader->accessUnitEndFlag = 1; + stream->SLHeader->accessUnitLength = stream->sample->dataLength; + stream->SLHeader->randomAccessPointFlag = stream->sample->IsRAP; + stream->SLHeader->compositionTimeStampFlag = 1; + stream->SLHeader->compositionTimeStamp = (stream->sample->CTS_Offset+stream->sample->DTS)*90000/1000; + stream->SLHeader->decodingTimeStampFlag = 1; + stream->SLHeader->decodingTimeStamp = stream->sample->DTS*90000/1000; + printf("SL Header: DTS "LLD", CTS "LLD"\n", stream->SLHeader->decodingTimeStamp, stream->SLHeader->compositionTimeStamp); + } +} + +void SLpacket_in_pes(M2TS_mux_stream *stream, char *Packet, u32 Size) +{ + config_sample_hdr(stream); + gf_sl_packetize(stream->SLConfig, stream->SLHeader, stream->sample->data, stream->sample->dataLength, &stream->sl_packet, &stream->sl_packet_len); +} + +GF_List *CreateTSPacketsFromSLPacket(M2TS_mux_stream *stream, char *SLPacket, u32 SL_Size) +{ + u8 table_id; + u16 table_id_extension; + MP42TS_Buffer *B; + GF_List *sections, *ts_packet; + + if (stream->MP4_type == GF_ISOM_MEDIA_SCENE){ + table_id = GF_M2TS_TABLE_ID_MPEG4_BIFS; + }else if (stream->MP4_type == GF_ISOM_MEDIA_OD){ + table_id = GF_M2TS_TABLE_ID_MPEG4_OD; + } + table_id_extension = 0; + + GF_SAFEALLOC(B, MP42TS_Buffer); + B->length = SL_Size; + B->data = SLPacket; + sections = CreateSections(B, table_id, table_id_extension, stream->SL_section_version_number); + B = EncodeSections(sections); + ts_packet = CreateTSPacketsFromSections(B, stream->mpeg2_es_pid, &stream->continuity_counter); + + free(B); + stream->SL_section_version_number ++; + if (stream->SL_section_version_number > 32) stream->SL_section_version_number = 0; + + return ts_packet; +} + +MP42TS_Buffer *get_iod(GF_BitStream *bs) +{ + GF_Err e; + GF_InitialObjectDescriptor *iod; + u32 DescSize =0; + GF_BitStream *bs_out; + MP42TS_Buffer *B; + iod = (GF_InitialObjectDescriptor *)gf_odf_new_iod(); + + e = gf_odf_read_iod(bs, iod, DescSize); + + bs_out = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE); + e = gf_odf_write_iod(bs_out, iod); + + GF_SAFEALLOC(B, MP42TS_Buffer); + B->data = malloc(DescSize); + gf_bs_get_content(bs_out, &B->data, &B->length); + gf_bs_del(bs); + + return B; +} + +/********************************************************************************** + * PSI related functions + **********************************************************************************/ + +/*************************** PMT *****************************/ +void InitializePMT(GF_M2TS_Program *program) +{ + u32 i; + + program->pcr_pid = 0; + for (i = 0; istreams); i++) { + M2TS_mux_stream *stream = (M2TS_mux_stream *)gf_list_get(program->streams, i); + if (stream->MP2_type == GF_M2TS_VIDEO_MPEG2) { + program->pcr_pid = stream->mpeg2_es_pid; + } else if (!program->pcr_pid) { + program->pcr_pid = stream->mpeg2_es_pid; + } + } +} + +MP42TS_Buffer *EncodePMT(GF_M2TS_Program *program, MP42TS_Buffer *iod) +{ + u32 i; + MP42TS_Buffer *B; + return B; +} + +void CreateTSPacketsForPMTs(M2TS_muxer *muxer) +{ + u32 i, j, nb_progs; + GF_M2TS_Program *program; + M2TS_mux_stream *stream; + MP42TS_Buffer *B; + GF_List *sections; + Bool use_iod = 0; + MP42TS_Buffer *B_iod; + + muxer->pmt_ts_packet = gf_list_new(); + nb_progs = gf_list_count(muxer->pat_table); + for (i= 0; ipat_table,i); + program->streams = gf_list_new(); + + for (j = 0; j < gf_list_count(muxer->streams); j++) { + stream = gf_list_get(muxer->streams, j); + stream->mpeg2_es_pid = 100*(i+1)+10*(j+1); + gf_list_add(program->streams, stream); + } + + InitializePMT(program); + + if (use_iod) { + GF_ObjectDescriptor *iod; + GF_BitStream *bs_iod; + + /* iod */ + iod = (GF_ObjectDescriptor *)gf_isom_get_root_od(muxer->mp4_in); + /* Mise à jour des SLConfigDescriptor à la façon MPEG-2 (timestamp_length = 33 ...) */ + if (iod->tag == GF_ODF_OD_TAG || iod->tag == GF_ODF_IOD_TAG) { + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *)iod; + u32 i, count = gf_list_count(od->ESDescriptors); + for (i = 0; i < count; i++ ){ + GF_ESD * esd = gf_list_get(od->ESDescriptors, i); + esd->slConfig->AUDuration = 0; + esd->slConfig->AULength = 0; + esd->slConfig->AUSeqNumLength = 0; + esd->slConfig->CUDuration = 0; + esd->slConfig->degradationPriorityLength = 0; + esd->slConfig->durationFlag = 0; + esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0; + esd->slConfig->instantBitrateLength = 32; + esd->slConfig->OCRLength = 0; + esd->slConfig->OCRResolution = 90000; + esd->slConfig->packetSeqNumLength = 0; + esd->slConfig->predefined = 0; + esd->slConfig->startCTS = 0; + esd->slConfig->startDTS = 0; + esd->slConfig->timeScale = 0; + esd->slConfig->timestampLength = 33; + esd->slConfig->timestampResolution = 90000; + esd->slConfig->useAccessUnitEndFlag = 1; + esd->slConfig->useAccessUnitStartFlag = 1; + esd->slConfig->useIdleFlag = 1; + esd->slConfig->usePaddingFlag = 0; + esd->slConfig->useRandomAccessPointFlag = 0; + esd->slConfig->useTimestampsFlag = 1; + } + } + bs_iod = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE); + + gf_odf_write_od(bs_iod, iod); + gf_odf_desc_del((GF_Descriptor *)iod); + + GF_SAFEALLOC(B_iod, MP42TS_Buffer); + gf_bs_get_content(bs_iod, &B_iod->data, &B_iod->length); + gf_bs_del(bs_iod); + } + + B = EncodePMT(program, (use_iod ? B_iod: NULL)); + sections = CreateSections(B, GF_M2TS_TABLE_ID_PMT, program->number, 1); + B = EncodeSections(sections); + gf_list_add(muxer->pmt_ts_packet, CreateTSPacketsFromSections(B, program->pmt_pid, NULL)); + } +} + + +/*************************** SDT *****************************/ +MP42TS_Buffer *encodeSDT(GF_List *prog_list, char *provider_name, u32 provider_name_lenth, char *service_name, u32 service_name_lenth) +{ + u32 i, j, k, CRC; + GF_M2TS_Program *program; + MP42TS_Buffer *B; + u16 desc_lenth; + u32 nb_program = gf_list_count(prog_list); + GF_SAFEALLOC(B, MP42TS_Buffer); + B->length = nb_program * (10 + service_name_lenth + provider_name_lenth) + 7; // un seul descripteur: service desc 0x48 + B->data = malloc(B->length); + + B->data[0] = 0; // original_network_id (2 byte) + B->data[1] = 0x1; + B->data[2] = 0; // reserved + // program loop + for (k= 0; k < gf_list_count(prog_list); k++){ + program = gf_list_get(prog_list,k); + B->data[3 + k*(10 + provider_name_lenth + service_name_lenth)] = (program->number >> 8) & 0xff; // service_id + B->data[4 + k*(10 + provider_name_lenth + service_name_lenth)] = program->number & 0xff; // service_id + B->data[5 + k*(10 + provider_name_lenth + service_name_lenth)] = 0; // EIT + + desc_lenth = provider_name_lenth + service_name_lenth + 5; // desc_loop_lenth + B->data[6 + k*(10 + provider_name_lenth + service_name_lenth)] = ((desc_lenth >> 8) & 0xf) | 0x80; // dscriptor loop length : 1 desc // 0x4 status = running + B->data[7 + k*(10 + provider_name_lenth + service_name_lenth)] = desc_lenth & 0xff;// dscriptor loop length + // descriptor loop + B->data[8 + k*(10 + provider_name_lenth + service_name_lenth)] = 0x48; // desct tag + B->data[9 + k*(10 + provider_name_lenth + service_name_lenth)] = service_name_lenth + provider_name_lenth + 3; // desc_lenth + B->data[10 + k*(10 + provider_name_lenth + service_name_lenth)] = 0x1; // srvice type : 0x1 = digital TV + B->data[11 + k*(10 + provider_name_lenth + service_name_lenth)] = provider_name_lenth; + for (i = 0; i < provider_name_lenth; i++){ + B->data[12 + i + k*(10 + provider_name_lenth + service_name_lenth)] = provider_name[i]; + } + B->data[12 + i + k*(10 + provider_name_lenth + service_name_lenth)] = service_name_lenth; + for (j = 0; j < service_name_lenth; j++){ + B->data[13+i+j + k*(10 + provider_name_lenth + service_name_lenth)] = service_name[j]; + } + // descriptor loop end + } + // crc // + CRC = gf_crc_32(B->data,B->length-4); + B->data[B->length-4] = (CRC >> 24) & 0xFF; + B->data[B->length-3] = (CRC >> 16) & 0xFF; + B->data[B->length-2] = (CRC >> 8) & 0xFF; + B->data[B->length-1] = CRC & 0xFF; + + return B; +} + +void CreateTSPacketsForSDT(M2TS_muxer *muxer, char *provider_name, char *service_name) +{ + MP42TS_Buffer *B; + GF_List *sections; + B = encodeSDT(muxer->pat_table, provider_name, 4, service_name, 8); + sections = CreateSections(B, GF_M2TS_TABLE_ID_SDT_ACTUAL, 1, 1); + B = EncodeSections(sections); + muxer->sdt_ts_packet = CreateTSPacketsFromSections(B, 0x11, NULL); +} + + +/************************************************************************************* + * PES related functions + ************************************************************************************/ +void add_pes_header(GF_BitStream *bs, M2TS_mux_stream *stream) +{ + u32 pes_len; + u32 pes_header_data_length; + Bool use_pts, use_dts; + + use_pts = 1; + use_dts = 1; + pes_header_data_length = 10; + + if (stream->muxer->log_level == LOG_PES) fprintf(stdout, "PID %d - Starting PES for AU %d - DTS "LLD" - PTS "LLD" - RAP %d\n", stream->mpeg2_es_pid, stream->sample_number, stream->sample->DTS, stream->sample->DTS+stream->sample->CTS_Offset, stream->sample->IsRAP); + gf_bs_write_int(bs, 0x1, 24);//packet start code + gf_bs_write_int(bs, stream->mpeg2_pes_streamid, 8);// stream id + + pes_len = stream->sample->dataLength + 13; // 13 = header size (including PTS/DTS) + gf_bs_write_int(bs, pes_len, 16); // pes packet length + + gf_bs_write_int(bs, 0x2, 2); // reserved + gf_bs_write_int(bs, 0x0, 2); // scrambling + gf_bs_write_int(bs, 0x0, 1); // priority + gf_bs_write_int(bs, 0x1, 1); // alignment indicator + gf_bs_write_int(bs, 0x0, 1); // copyright + gf_bs_write_int(bs, 0x0, 1); // original or copy + + gf_bs_write_int(bs, use_pts, 1); + gf_bs_write_int(bs, use_dts, 1); + gf_bs_write_int(bs, 0x0, 6); //6 flags = 0 (ESCR, ES_rate, DSM_trick, additional_copy, PES_CRC, PES_extension) + + gf_bs_write_int(bs, pes_header_data_length, 8); + + if (use_pts && use_dts){ + u64 cts, dts, t; + + cts = stream->sample->DTS + stream->sample->CTS_Offset; + + gf_bs_write_int(bs, 0x2, 4); // reserved '0010' + t = ((cts >> 30) & 0x7); + gf_bs_write_long_int(bs, t, 3); + gf_bs_write_int(bs, 1, 1); // marker bit + t = ((cts >> 15) & 0x7fff); + gf_bs_write_long_int(bs, t, 15); + gf_bs_write_int(bs, 1, 1); // marker bit + t = cts & 0x7fff; + gf_bs_write_long_int(bs, t, 15); + gf_bs_write_int(bs, 1, 1); // marker bit + + dts = stream->sample->DTS; + + gf_bs_write_int(bs, 0x1, 4); // reserved '0001' + t = ((dts >> 30) & 0x7); + gf_bs_write_long_int(bs, t, 3); + gf_bs_write_int(bs, 1, 1); // marker bit + t = ((dts >> 15) & 0x7fff); + gf_bs_write_long_int(bs, t, 15); + gf_bs_write_int(bs, 1, 1); // marker bit + t = dts & 0x7fff; + gf_bs_write_long_int(bs, t, 15); + gf_bs_write_int(bs, 1, 1); // marker bit + } + +} + +u8 *encode_ts(M2TS_muxer *muxer, M2TS_mux_stream *stream) +{ + GF_BitStream *bs; + u8 *ts_data; + u32 ts_data_len; + + /* length of the header from start of pes header to start of payload (including PTS/DTS) */ + u32 pes_header_length = 19; + u32 remain; + Bool PUSI; + u32 adaptation_length; + u32 payload_length; + Bool is_rap; + + bs = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE); + + if (stream->nb_bytes_written == 0){ + PUSI = 1; + } else { + PUSI = 0; + } + + gf_bs_write_int(bs, 0x47, 8); // sync byte + gf_bs_write_int(bs, 0, 1); // error indicator + gf_bs_write_int(bs, PUSI, 1); // start ind + gf_bs_write_int(bs, 0, 1); // transport priority + gf_bs_write_int(bs, stream->mpeg2_es_pid, 13); // pid + gf_bs_write_int(bs, 0, 2); // scrambling + + remain = stream->sample->dataLength + pes_header_length*PUSI - stream->nb_bytes_written; + + if (stream->MP2_type == GF_M2TS_VIDEO_MPEG2 && stream->sample->IsRAP) is_rap=1; + else is_rap = 0; + + adaptation_length = add_adaptation(bs, &stream->continuity_counter, + stream->PCR, + stream->stream_time, + is_rap, + remain); + + payload_length = 184 - adaptation_length; + + if (PUSI){ + u32 pes_payload_length; + + add_pes_header(bs, stream); + pes_payload_length = payload_length - pes_header_length; + gf_bs_write_data(bs, stream->sample->data, pes_payload_length); + stream->nb_bytes_written += pes_payload_length; +// fprintf(stdout, "TS payload length %d - total %d\n", pes_payload_length, stream->nb_bytes_written); + } else { + gf_bs_write_data(bs, stream->sample->data+stream->nb_bytes_written, payload_length); + stream->nb_bytes_written += payload_length; +// fprintf(stdout, "TS payload length %d - total %d \n", payload_length, stream->nb_bytes_written); + } + gf_bs_get_content(bs, &ts_data, &ts_data_len); + gf_bs_del(bs); + return ts_data; +} + +void create_one_ts(M2TS_muxer *muxer, M2TS_mux_stream *stream) +{ + u8 *data; + + if (!stream->sample || stream->nb_bytes_written >= stream->sample->dataLength){ + u32 tmp; + /* TODO free sample ? */ + stream->sample = NULL; + stream->sample_number ++; + stream->sample = gf_isom_get_sample(muxer->mp4_in, stream->track_number, stream->sample_number, &tmp); + if (stream->sample) stream->stream_time = stream->sample->DTS; + stream->nb_bytes_written = 0; + } + + if (stream->sample) { + data = encode_ts(muxer, stream); + fwrite(data, 1, 188, muxer->ts_out); + //fwrite(data+4, 1, 184, stream->pes_out); + } +} + +void get_mux_stream_sample(GF_ISOFile *mp4_in, M2TS_mux_stream *stream); + +void initialize_muxer(M2TS_muxer *muxer) +{ + M2TS_mux_stream *stream; + u32 i, nb_track; + + muxer->muxer_time = 0; + muxer->streams = gf_list_new(); + muxer->pmt_ts_packet = gf_list_new(); + + nb_track = gf_isom_get_track_count(muxer->mp4_in); + for (i=1; i<=nb_track; i++) { + char out[500]; + GF_SAFEALLOC(stream, M2TS_mux_stream); + stream->muxer = muxer; + stream->track_number = i; + stream->mpeg4_es_id = gf_isom_get_track_id(muxer->mp4_in, i); + stream->MP4_type = gf_isom_get_media_type(muxer->mp4_in, i); + stream->nb_samples = gf_isom_get_sample_count(muxer->mp4_in, i); + if (0) { + sprintf(out, "out%i.pes", i); + stream->pes_out = fopen(out, "wb"); + } + + switch (stream->MP4_type) { + case GF_ISOM_MEDIA_VISUAL: + if (muxer->use_sl) { + stream->MP2_type = GF_M2TS_SYSTEMS_MPEG4_PES; + } else { + stream->MP2_type = GF_M2TS_VIDEO_MPEG2; + } + stream->mpeg2_pes_streamid = 0xE0 + i; + break; + case GF_ISOM_MEDIA_AUDIO: + if (muxer->use_sl) { + stream->MP2_type = GF_M2TS_SYSTEMS_MPEG4_PES; + } else { + stream->MP2_type = GF_M2TS_AUDIO_MPEG2; + } + stream->mpeg2_pes_streamid = 0xC0 + i; + break; + case GF_ISOM_MEDIA_OD: + stream->MP2_type = GF_M2TS_SYSTEMS_MPEG4_SECTIONS; + break; + case GF_ISOM_MEDIA_SCENE: + stream->MP2_type = GF_M2TS_SYSTEMS_MPEG4_SECTIONS; + break; + default: + fprintf(stderr, "Track type %d not supported in MPEG-2\n", stream->MP4_type); + } + + if (stream->MP2_type == GF_M2TS_SYSTEMS_MPEG4_PES || + stream->MP2_type == GF_M2TS_SYSTEMS_MPEG4_SECTIONS) { + stream->SLConfig = (GF_SLConfig *)gf_odf_desc_new(GF_ODF_SLC_TAG); + stream->SLConfig->AUDuration = 0; + stream->SLConfig->AULength = 0; + stream->SLConfig->AUSeqNumLength = 0; + stream->SLConfig->CUDuration = 0; + stream->SLConfig->degradationPriorityLength = 0; + stream->SLConfig->durationFlag = 0; + stream->SLConfig->hasRandomAccessUnitsOnlyFlag = 0; + stream->SLConfig->instantBitrateLength = 32; + stream->SLConfig->OCRLength = 0; + stream->SLConfig->OCRResolution = 90000; + stream->SLConfig->packetSeqNumLength = 0; + stream->SLConfig->predefined = 0; + stream->SLConfig->startCTS = 0; + stream->SLConfig->startDTS = 0; + stream->SLConfig->timeScale = 0; + stream->SLConfig->timestampLength = 33; + stream->SLConfig->timestampResolution = 90000; + stream->SLConfig->useAccessUnitEndFlag = 1; + stream->SLConfig->useAccessUnitStartFlag = 1; + stream->SLConfig->useIdleFlag = 1; + stream->SLConfig->usePaddingFlag = 0; + stream->SLConfig->useRandomAccessPointFlag = 0; + stream->SLConfig->useTimestampsFlag = 1; + } + if (stream->MP2_type == GF_M2TS_SYSTEMS_MPEG4_PES){ + stream->SL_in_pes = 1; + } else if (stream->MP2_type == GF_M2TS_SYSTEMS_MPEG4_SECTIONS){ + stream->SL_in_section = 1; + stream->sl_section_ts_packets = gf_list_new(); + } + gf_list_add(muxer->streams, stream); + } +} + +/********************************************************************************* + * Muxer general functions + ********************************************************************************/ +M2TS_mux_stream *get_current_stream(M2TS_muxer *muxer) +{ + M2TS_mux_stream *stream; + M2TS_mux_stream *res; + u32 i, count; + u64 inf_time = 0xffffffffffffffff; + + count = gf_list_count(muxer->streams); + for(i = 0; i < count; i++) { + stream = gf_list_get(muxer->streams, i); + if (stream->stream_time < inf_time) { + res = stream; + inf_time = stream->stream_time; + } + } + + if (res->sample_number > res->nb_samples) muxer->end = 1; + return stream; +} + +M2TS_mux_stream *compare_muxer_time_with_stream_time(M2TS_muxer *muxer) +{ + M2TS_mux_stream *stream; + u32 i, k, count; + u64 inf_time = 0xffffffffffffffff; + u32 stream_with_inf_time = 0; + + k=0; + count = gf_list_count(muxer->streams); + for(i = 0; i < count; i++) { + stream = gf_list_get(muxer->streams, i); + if (stream->sample) { + if (stream->stream_time < inf_time) { + inf_time = stream->stream_time; + stream_with_inf_time = i; + } + } else { + u32 SI_index = (u32)(muxer->TS_Rate * muxer->SI_interval / 188); + if (stream->SL_in_section && stream->repeat_section && + muxer->insert_SI == SI_index){ + // par exemple pour qu'une image soit retransmise + stream->nb_bytes_written = 0; + update_muxer(muxer, muxer->mp4_in, stream); + } + k++; + } + } + + if (k == i) muxer->end = 1; + stream = gf_list_get(muxer->streams, stream_with_inf_time); + muxer->muxer_time = stream->stream_time; + return stream; +} + +void M2TS_OnEvent_muxer(M2TS_muxer *muxer, u32 evt_type, void *par) +{ + M2TS_mux_stream *stream; + + switch (evt_type) { + case GF_M2TS_EVT_PAT: + { + u8 *data; + u32 i, count; + count = gf_list_count(muxer->pat_ts_packet); + for (i = 0; i < count; i++) { + data = gf_list_get(muxer->pat_ts_packet, i); + fwrite(data, 1, 188, muxer->ts_out); + } + } + break; + + case GF_M2TS_EVT_SDT: + { + u8 *data; + u32 i, count; + count = gf_list_count(muxer->sdt_ts_packet); + for (i = 0; i < count; i++) { + data = gf_list_get(muxer->sdt_ts_packet, i); + fwrite(data, 1, 188, muxer->ts_out); + } + } + break; + + case GF_M2TS_EVT_PMT: + { + u32 i, j, count1, count2; + u8 *data; + GF_List *ts_packet; + + count1 = gf_list_count(muxer->pmt_ts_packet); + for (i = 0; i < count1; i++) { + ts_packet = gf_list_get(muxer->pmt_ts_packet, i); + count2 = gf_list_count(ts_packet); + for (j = 0; jts_out); + } + } + muxer->insert_SI = 0; + } + break; + + case GF_M2TS_EVT_ES: + stream = get_current_stream(muxer); //compare_muxer_time_with_stream_time(muxer); + if (muxer->end) return; + create_one_ts(muxer, stream); + break; + +/* + case GF_M2TS_EVT_SL_SECTION: + + stream = par; + if (!stream->sl_packet && stream->sample){ + // AU ---> SL : 1 sample = 1paquet SL + if (stream->SL_in_section){ + static u32 writeSL = 1; + u32 count; + // créer une section ou plus pour ce paquet SL => list de paquet TS + printf("sample number %d: DTS "LLD", CTS Offset %d\n", stream->sample_number, stream->sample->DTS, stream->sample->CTS_Offset); + + config_sample_hdr(stream); + gf_sl_packetize(stream->SLConfig, stream->SLHeader, stream->sample->data, stream->sample->dataLength, &stream->sl_packet, &stream->sl_packet_len); + + + writeSL++; + stream->sl_section_ts_packets = CreateTSPacketsFromSLPacket(stream, stream->sl_packet, stream->sl_packet_len); + + count = gf_list_count(stream->sl_section_ts_packets); + printf("sl_packet_length %d ,nb ts packet %d \n", stream->sl_packet_len, count); + + }else if (stream->SL_in_pes && (stream->nb_bytes_written == 0)){ + config_sample_hdr(stream); + gf_sl_packetize(stream->SLConfig, stream->SLHeader, stream->sample->data, stream->sample->dataLength, &stream->sample->data, &stream->sample->dataLength); + + } + + } + if (stream->SL_in_section && stream->sl_packet){ + + unsigned char *data; + static nb_ts_sec = 0; + data = gf_list_get(stream->sl_section_ts_packets, 0); + fwrite(data, 1, 188, muxer->ts_out); + nb_ts_sec ++; + gf_list_rem(stream->sl_section_ts_packets, 0); + + + if (gf_list_count(stream->sl_section_ts_packets) == 0){ + + printf("nb ts write %d\n\n", nb_ts_sec); + nb_ts_sec = 0; + + stream->sl_packet = NULL; + stream->sl_packet_len = 0; + stream->nb_bytes_written = 0; + stream->sample->dataLength = 0; + free(stream->sample->data); + update_muxer(muxer, muxer->mp4_in, stream); + } + + }else if (stream->SL_in_pes && stream->sample->data){ + + create_one_ts(muxer, stream); + if (stream->nb_bytes_written == 0){ + stream->sl_packet = NULL; + stream->sl_packet_len = 0; + } + } + + + break; + + case GF_M2TS_EVT_DEMUX_DATA: + { + GF_BitStream *mp2_bs; + u32 pid_found = 0; + u64 pos, has_data; + mp2_bs = par; + + has_data = gf_bs_available(mp2_bs); + while (has_data > 0) { + u32 i = 0; + unsigned char *data; + u32 data_size = 188; + data = malloc(data_size); + // read one ts packet + gf_bs_read_data(mp2_bs, data, data_size); + has_data = gf_bs_available(mp2_bs); + pos = gf_bs_get_position(mp2_bs); + if (data[0] != 0x47){ + u32 k = 0; + i++; + printf("Sync byte error %d\r", i); + while (data[0] != 0x47 && has_data){ + data = &data[1]; + data[187] = gf_bs_read_u8(mp2_bs); + k++; + } + }else{ + + // gf_m2ts_process_data(ts, data, data_size); + m2ts_write_ts_packet(muxer, data, &pid_found); + } + pos = gf_bs_get_position(mp2_bs); + if (pid_found == 1) break; + } + } + break;*/ + } +} + + +/*Events used by the MPEGTS muxer*/ +enum +{ + GF_M2TS_EVT_PAT = 0, + GF_M2TS_EVT_PMT, + GF_M2TS_EVT_SDT, + GF_M2TS_EVT_ES, + GF_M2TS_EVT_SL_SECTION, + /* Data from demuxer */ + GF_M2TS_EVT_DEMUX_DATA, +}; + +typedef struct +{ + u8 *data; + u32 length; +} MP42TS_Buffer; + +typedef struct M2TS_mux_stream +{ + u32 track_number; + u32 mpeg2_es_pid; + u32 mpeg2_pes_streamid; + u32 mpeg4_es_id; + + GF_ISOSample *sample; + u32 sample_number; + u32 nb_samples; + u32 nb_bytes_written; + u32 continuity_counter; + + GF_List *packets; + + + GF_SLConfig *SLConfig; + GF_SLHeader *SLHeader; + Bool SL_in_pes; + u8 *sl_packet; + u32 sl_packet_len; + Bool SL_in_section; + Bool repeat_section; + u32 SL_section_version_number; + GF_List *sl_section_ts_packets; + + GF_M2TS_PES *PES; + + u8 is_time_initialized; + u64 stream_time; + + u32 MP2_type; + u32 MP4_type; + + FILE *pes_out; + + Bool PCR; + + struct M2TS_muxer *muxer; +} M2TS_mux_stream; + +typedef struct M2TS_muxer +{ + u32 TS_Rate; // bps + float PAT_interval; // s + float PMT_interval; // s + float SI_interval; //s + + u32 send_pat, send_pmt; + + /* ~ PCR */ + u64 muxer_time; + + /* M2TS_mux_stream List*/ + GF_List *streams; + + GF_List *pat_table; /* List of GF_M2TS_Program */ + + GF_List *pat_ts_packet; /* List of Encoded TS packets corresponding to PAT */ + GF_List *sdt_ts_packet; /* List of Encoded TS packets corresponding to SDT */ + GF_List *pmt_ts_packet; /* List of List of Encoded TS packets corresponding to each PMT */ + + /*user callback - MUST NOT BE NULL*/ + void (*on_event)(struct M2TS_muxer *muxer, u32 evt_type, void *par); + /*private user data*/ + void *user; + + GF_ISOFile *mp4_in; + FILE *ts_out; + + u32 insert_SI; + Bool end; + + Bool use_sl; + + u32 log_level; +} M2TS_muxer; + +typedef struct +{ + u32 *pid; + u32 length; +} M2TS_pid_list; +#endif diff --git a/applications/testapps/mp42ts/mp42ts.dsp b/applications/testapps/mp42ts/mp42ts.dsp new file mode 100644 index 0000000..50df482 --- /dev/null +++ b/applications/testapps/mp42ts/mp42ts.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="mp42ts" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=mp42ts - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mp42ts.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mp42ts.mak" CFG="mp42ts - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mp42ts - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "mp42ts - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mp42ts - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 /nologo /subsystem:console /machine:I386 /out:"../../../bin/w32_rel/mp42ts.exe" /libpath:"../../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "mp42ts - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /out:"../../../bin/w32_deb/mp42ts.exe" /pdbtype:sept /libpath:"../../../extra_lib/lib/w32_deb" +# SUBTRACT LINK32 /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "mp42ts - Win32 Release" +# Name "mp42ts - Win32 Debug" +# Begin Source File + +SOURCE=.\main.c +# End Source File +# Begin Source File + +SOURCE=.\mp42ts.c +# End Source File +# Begin Source File + +SOURCE=.\mp42ts.h +# End Source File +# End Target +# End Project diff --git a/applications/testapps/mp42ts/mp42ts.h b/applications/testapps/mp42ts/mp42ts.h new file mode 100644 index 0000000..18085d2 --- /dev/null +++ b/applications/testapps/mp42ts/mp42ts.h @@ -0,0 +1,179 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2000-200X + * All rights reserved + * + * This file is part of GPAC / mp42ts application + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include +#include +#include +#include +#include +#include + + +typedef struct __m2ts_mux_program M2TS_Mux_Program; +typedef struct __m2ts_mux M2TS_Mux; + +enum { + LOG_NO_LOG = 0, + LOG_PES = 1, + LOG_SECTION = 2, + LOG_TS = 3 +}; + + +typedef struct __m2ts_section { + struct __m2ts_section *next; + u8 *data; + u32 length; +} M2TS_Mux_Section; + +typedef struct __m2ts_table { + struct __m2ts_table *next; + u8 table_id; + u8 version_number; + struct __m2ts_section *section; +} M2TS_Mux_Table; + +typedef struct +{ + u32 sec; + u32 nanosec; +} M2TS_Time; + + +typedef struct __m2ts_mux_pck +{ + struct __m2ts_mux_pck *next; + char *data; + u32 data_len; + u32 flags; + u64 cts, dts; +} M2TS_Packet; + + +typedef struct __m2ts_mux_stream { + struct __m2ts_mux_stream *next; + + u32 pid; + u8 continuity_counter; + struct __m2ts_mux_program *program; + + /*average stream bit-rate in bit/sec*/ + u32 bit_rate; + + /*multiplexer time - NOT THE PCR*/ + M2TS_Time time; + + /*table tools*/ + M2TS_Mux_Table *tables; + /*total table sizes for bitrate estimation (PMT/PAT/...)*/ + u32 total_table_size; + /* used for on-the-fly packetization of sections */ + M2TS_Mux_Table *current_table; + M2TS_Mux_Section *current_section; + u32 current_section_offset; + u32 refresh_rate_ms; + Bool table_needs_update; + + Bool (*process)(struct __m2ts_mux *muxer, struct __m2ts_mux_stream *stream); + + /*PES tools*/ + void *pes_packetizer; + u32 mpeg2_stream_type; + u32 mpeg2_stream_id; + + GF_ESIPacket pck; + u32 pck_offset; + + struct __elementary_stream_ifce *ifce; + Double ts_scale; + + /*packet fifo*/ + M2TS_Packet *pck_first, *pck_last; + GF_Mutex *mx; + /*avg bitrate compute*/ + u64 last_br_time; + u32 bytes_since_last_time; + + /*MPEG-4 over MPEG-2*/ + u8 table_id; + GF_SLHeader sl_header; + //GF_SLConfig sl_config; +} M2TS_Mux_Stream; + + +struct __m2ts_mux_program { + struct __m2ts_mux_program *next; + + struct __m2ts_mux *mux; + u16 number; + /*all streams but PMT*/ + M2TS_Mux_Stream *streams; + /*PMT*/ + M2TS_Mux_Stream *pmt; + /*pointer to PCR stream*/ + M2TS_Mux_Stream *pcr; + + Bool pcr_init; + /*TS time at pcr init*/ + M2TS_Time pcr_init_ts_time; + u64 pcr_init_time; + + GF_Descriptor *iod; +}; + +struct __m2ts_mux { + M2TS_Mux_Program *programs; + M2TS_Mux_Stream *pat; + + u16 ts_id; + + Bool needs_reconfig; + Bool real_time; + /*if set bit-rate won't be re-estimated*/ + Bool fixed_rate; + + /*output bit-rate in bit/sec*/ + u32 bit_rate; + + char dst_pck[188], null_pck[188]; + + /*multiplexer time in micro-sec*/ + M2TS_Time time, init_ts_time; + u32 init_sys_time; + + Bool eos_found; + u32 pck_sent, last_br_time, avg_br; + u64 tot_pck_sent, tot_pad_sent; + + Bool mpeg4_signaling; +}; + + +enum +{ + GF_M2TS_STATE_IDLE, + GF_M2TS_STATE_DATA, + GF_M2TS_STATE_PADDING, + GF_M2TS_STATE_EOS, +}; + diff --git a/applications/testapps/mp4_streamer/Makefile b/applications/testapps/mp4_streamer/Makefile new file mode 100644 index 0000000..2628096 --- /dev/null +++ b/applications/testapps/mp4_streamer/Makefile @@ -0,0 +1,64 @@ +include ../../../config.mak + +vpath %.c $(SRC_PATH)/applications/testapps/mp4_streamer + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= main.o + +LINKFLAGS=-L../../../bin/gcc +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=mp4_streamer$(EXE) +#LINKFLAGS+=-lgpac_static -lz $(EXTRALIBS) +LINKFLAGS+=-lgpac +else +EXT= +PROG=mp4_streamer +#LINKFLAGS+=-lgpac_static $(EXTRALIBS) $(GPAC_SH_FLAGS) -lz $(OGL_LIBS) +LINKFLAGS+=-lgpac $(OGL_LIBS) +endif + + +SRCS := $(OBJS:.o=.c) + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o ../../../bin/gcc/$@ $(OBJS) $(LINKFLAGS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../../bin/gcc/$(PROG) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/applications/testapps/mp4_streamer/configuration.cfg b/applications/testapps/mp4_streamer/configuration.cfg new file mode 100644 index 0000000..1e5b8da --- /dev/null +++ b/applications/testapps/mp4_streamer/configuration.cfg @@ -0,0 +1,31 @@ +#---------------------------------------------------------------------- +# Configuration File of the mp4 streamer +#---------------------------------------------------------------------- +# Usage: time in ms, bit rate in kbps, port numbers must be even +#---------------------------------------------------------------------- +[GLOBAL] +nbSession=2 +burst_mode=yes +off_duration=500 +burst_duration=500 +burst_bitrate=8000 + +#an IPV4 multicast address +IP_dest=233.64.133.10 +#IPV4 loopback (localhost) +#IP_dest=127.0.0.1 +#IPV6 multicast loopback +#IP_dest=FF01::1 + +#IPV6 loopback +#IP_dest=::1 + +[SESSION1] +port=7000 +file=tnt.mp4 +looping=1 + +[SESSION2] +port=7002 +file=av.mp4 +looping=1 diff --git a/applications/testapps/mp4_streamer/main.c b/applications/testapps/mp4_streamer/main.c new file mode 100644 index 0000000..63a31a8 --- /dev/null +++ b/applications/testapps/mp4_streamer/main.c @@ -0,0 +1,1175 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato / Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / mp4 simple streamer application + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include + +GP_RTPPacketizer *gf_rtp_packetizer_create_and_init_from_file(GF_ISOFile *file, + u32 TrackNum, + void *cbk_obj, + void (*OnNewPacket)(void *cbk, GF_RTPHeader *header), + void (*OnPacketDone)(void *cbk, GF_RTPHeader *header), + void (*OnDataReference)(void *cbk, u32 payload_size, u32 offset_from_orig), + void (*OnData)(void *cbk, char *data, u32 data_size, Bool is_head), + u32 Path_MTU, + u32 max_ptime, + u32 default_rtp_rate, + u32 flags, + u8 PayloadID, + Bool copy_media, + u32 InterleaveGroupID, + u8 InterleaveGroupPriority); +void gf_hinter_format_ttxt_sdp(GP_RTPPacketizer *builder, char *payload_name, char *sdpLine, GF_ISOFile *file, u32 track); + +//------------------------------------------------------------ +// Define +//------------------------------------------------------------ +#define PATHFILE "." +#define RTP_HEADER_SIZE 12 // in bytes (octets) +#define BASE_PAYT 96 + +//------------------------------------------------------------ +// Typedef +//------------------------------------------------------------ +enum +{ + LOG_NONE = 0, + LOG_BURST, + LOG_AU, + LOG_PACKET, +}; + +typedef struct +{ + u32 log_level; + u32 path_mtu; + struct __tag_rtp_session *session; + u8 payt; + + Bool burst_mode; + + /*burst mode configuration*/ + u32 burstDuration; + u32 burstBitRate; + u32 burstSize; + u32 offDuration; + u32 averageBitRate; + u32 cycleDuration; + u32 nbBurstSent; +} Streamer; + +typedef struct __tag_rtp_pck +{ + struct __tag_rtp_pck *next; + + GF_RTPHeader header; + char *payload; + u32 payload_len; +} RTP_Packet; + +typedef struct __tag_rtp_stream +{ + struct __tag_rtp_stream *next; + + u32 current_au; + u32 nb_aus; + + u32 port; + GF_RTPChannel *channel; + GP_RTPPacketizer *packetizer; + + GF_ISOSample *au; // the AU + u32 track; + u32 sample_duration; + u32 sample_desc_index; + /*normalized DTS in micro-sec*/ + u64 microsec_dts; + + /* The previous packet which could not be sent in previous burst*/ + RTP_Packet *pck_queue; + /* The current packet being formed */ + RTP_Packet packet; + u32 ts_offset, microsec_ts_offset; + u32 next_ts; + + Double ts_scale, microsec_ts_scale; + Bool process_burst; + /*NALU size for H264/AVC*/ + u32 avc_nalu_size; + + struct __tag_rtp_session *session; +} RTP_Stream; + + +typedef struct __tag_rtp_session +{ + struct __tag_rtp_session *next; + + u32 id; + u32 minBurstSize; // max size to fill the burst with the media bitRate + u32 looping; // 1: play the media in a loop, 0 play once + char *filename; + GF_ISOFile *mp4File; + u32 nbBurstSent; // pour cette session + u32 timelineOrigin; // time when the first burts was sent. this time <=> (CTS=0) + u32 nextBurstTime; + u32 dataLengthInBurst; // total bytes filled in current burst + s32 drift; + Bool force_mpeg4_generic; + + /*list of streams in session*/ + RTP_Stream *stream; + + /*to sync looping sessions with tracks of # length*/ + u32 duration; + + Streamer *streamer; +} RTP_Session; + + +static void rtp_flush_channel(RTP_Stream *rtp) +{ + u32 currentPacketSize; + RTP_Packet *pck = rtp->pck_queue; + while (pck) { + RTP_Packet *tmp = pck; + + gf_rtp_send_packet(rtp->channel, &pck->header, 0, 0, pck->payload, pck->payload_len); + currentPacketSize = (pck->payload_len + RTP_HEADER_SIZE); + rtp->session->dataLengthInBurst+= currentPacketSize; + if (rtp->session->streamer->log_level == LOG_PACKET) fprintf(stdout, " RTP SN %u - TS %u - M %u - Size %u\n", pck->header.SequenceNumber, pck->header.TimeStamp, pck->header.Marker, currentPacketSize); + + pck = pck->next; + free(tmp->payload); + free(tmp); + } + rtp->pck_queue = NULL; +} + + +/* + * callback functions, called by the RTP packetiser + */ + +/* + * The RTP packetizer is starting a new RTP packet and is giving the header + */ +static void burst_on_pck_new(void *cbk, GF_RTPHeader *header) +{ + RTP_Stream *rtp = cbk; + if (!header) return; + memcpy(&rtp->packet.header, header, sizeof(GF_RTPHeader)); +} /* OnNewPacket */ + +/* + * The RTP packetiser is done with the current RTP packet + * the header may have changed since the beginning of the packet (OnNewPacket) + * + */ +static void burst_on_pck_done(void *cbk, GF_RTPHeader *header) +{ + GF_Err e; + s64 burst_time, rtp_ts; + RTP_Stream *rtp = cbk; + u32 currentPacketSize; // in bits + + + currentPacketSize = (rtp->packet.payload_len + RTP_HEADER_SIZE); + burst_time = (s64) rtp->packetizer->sl_config.timestampResolution * (rtp->session->nextBurstTime + rtp->session->streamer->cycleDuration); + rtp_ts = (s64) rtp->next_ts*1000; + + if (rtp->session->dataLengthInBurst + currentPacketSize < rtp->session->streamer->burstSize + && (burst_time - rtp_ts > 0) ) { + + e = gf_rtp_send_packet(rtp->channel, header, 0, 0, rtp->packet.payload, rtp->packet.payload_len); + if (e) + fprintf(stdout, "Error %s sending RTP packet\n", gf_error_to_string(e)); + rtp->session->dataLengthInBurst += currentPacketSize; + free(rtp->packet.payload); + rtp->packet.payload = NULL; + rtp->packet.payload_len = 0; + + if (rtp->session->streamer->log_level == LOG_PACKET) fprintf(stdout, " RTP SN %u - TS %u - M %u - Size %u\n", rtp->packet.header.SequenceNumber, rtp->packet.header.TimeStamp, rtp->packet.header.Marker, currentPacketSize); + + } else { + RTP_Packet *pck; + if (rtp->session->dataLengthInBurst + currentPacketSize > rtp->session->streamer->burstSize) { + if (rtp->session->streamer->log_level >= LOG_BURST) + fprintf(stdout, " Packet (TS %u) delayed due to buffer overflow\n", rtp->next_ts); + } else { + if (rtp->session->streamer->log_level==LOG_PACKET) + fprintf(stdout, " Packet (TS %u) delayed to avoid drift\n", rtp->next_ts); + } + + GF_SAFEALLOC(pck, RTP_Packet); + memcpy(&pck->header, header, sizeof(GF_RTPHeader)); + pck->payload = rtp->packet.payload; + pck->payload_len = rtp->packet.payload_len; + rtp->packet.payload = NULL; + rtp->packet.payload_len = 0; + rtp->process_burst = 0; + if (rtp->pck_queue) { + RTP_Packet *first = rtp->pck_queue; + while (first->next) first = first->next; + first->next = pck; + } else { + rtp->pck_queue = pck; + } + } + +} /* OnPacketdone */ + +/* + * The RTP packetiser has added data to the current RTP packet + */ +static void on_pck_data(void *cbk, char *data, u32 data_size, Bool is_head) +{ + RTP_Stream *rtp = cbk; + if (!data ||!data_size) return; + + if (!rtp->packet.payload_len) { + rtp->packet.payload = malloc(data_size); + memcpy(rtp->packet.payload, data, data_size); + rtp->packet.payload_len = data_size; + } else { + rtp->packet.payload = realloc(rtp->packet.payload, rtp->packet.payload_len + data_size); + if (!is_head) { + memcpy(rtp->packet.payload+rtp->packet.payload_len, data, data_size); + } else { + memmove(rtp->packet.payload+data_size, rtp->packet.payload, rtp->packet.payload_len); + memcpy(rtp->packet.payload, data, data_size); + } + rtp->packet.payload_len += data_size; + } + +} /* OnData */ + +/* + * The RTP packetizer is starting a new RTP packet and is giving the header + */ +static void on_pck_new(void *cbk, GF_RTPHeader *header) +{ + RTP_Stream *rtp = cbk; + if (!header) return; + memcpy(&rtp->packet.header, header, sizeof(GF_RTPHeader)); +} + +/* + * The RTP packetiser is done with the current RTP packet + * the header may have changed since the beginning of the packet (OnNewPacket) + * + */ +static void on_pck_done(void *cbk, GF_RTPHeader *header) +{ + RTP_Stream *rtp = cbk; + GF_Err e = gf_rtp_send_packet(rtp->channel, header, 0, 0, rtp->packet.payload, rtp->packet.payload_len); + if (e) + fprintf(stdout, "Error %s sending RTP packet\n", gf_error_to_string(e)); + free(rtp->packet.payload); + + if (rtp->session->streamer->log_level == LOG_PACKET) + fprintf(stdout, " RTP SN %u - TS %u - M %u - Size %u\n", rtp->packet.header.SequenceNumber, rtp->packet.header.TimeStamp, rtp->packet.header.Marker, rtp->packet.payload_len + RTP_HEADER_SIZE); + + rtp->packet.payload = NULL; + rtp->packet.payload_len = 0; +} + + +GF_Err rtp_init_packetizer(RTP_Stream *rtp, char *dest_ip) +{ + u32 flags = 0; + + if (rtp->session->force_mpeg4_generic) flags = GP_RTP_PCK_SIGNAL_RAP | GP_RTP_PCK_FORCE_MPEG4; + + if (rtp->session->streamer->burst_mode) { + rtp->packetizer = gf_rtp_packetizer_create_and_init_from_file(rtp->session->mp4File, rtp->track, rtp, + burst_on_pck_new, burst_on_pck_done, NULL, on_pck_data, + rtp->session->streamer->path_mtu, 0, 0, flags, + rtp->session->streamer->payt, 0, 0, 0); + } else { + rtp->packetizer = gf_rtp_packetizer_create_and_init_from_file(rtp->session->mp4File, rtp->track, rtp, + on_pck_new, on_pck_done, NULL, on_pck_data, + rtp->session->streamer->path_mtu, 0, 0, flags, + rtp->session->streamer->payt, 0, 0, 0); + } + + rtp->session->streamer->payt++; + + rtp->ts_scale = rtp->packetizer->sl_config.timestampResolution; + rtp->ts_scale /= gf_isom_get_media_timescale(rtp->session->mp4File, rtp->track); + + rtp->microsec_ts_scale = 1000000; + rtp->microsec_ts_scale /= gf_isom_get_media_timescale(rtp->session->mp4File, rtp->track); + return GF_OK; +} + +GF_Err rtp_setup_sdp(RTP_Session *session, char *dest_ip) +{ + RTP_Stream *rtp; + FILE *sdp_out; + char filename[30]; + char mediaName[30], payloadName[30]; + char sdpLine[20000]; + + sprintf(filename, "session%d.sdp", session->id); + sdp_out = fopen(filename, "wt"); + if (!sdp_out) return GF_IO_ERR; + + sprintf(sdpLine, "v=0"); + fprintf(sdp_out, "%s\n", sdpLine); + sprintf(sdpLine, "o=MP4Streamer 3357474383 1148485440000 IN IP%d %s", gf_net_is_ipv6(dest_ip) ? 6 : 4, dest_ip); + fprintf(sdp_out, "%s\n", sdpLine); + sprintf(sdpLine, "s=livesession"); + fprintf(sdp_out, "%s\n", sdpLine); + sprintf(sdpLine, "i=This is an MP4 time-sliced Streaming demo"); + fprintf(sdp_out, "%s\n", sdpLine); + sprintf(sdpLine, "u=http://gpac.sourceforge.net"); + fprintf(sdp_out, "%s\n", sdpLine); + sprintf(sdpLine, "e=admin@"); + fprintf(sdp_out, "%s\n", sdpLine); + sprintf(sdpLine, "c=IN IP%d %s", gf_net_is_ipv6(dest_ip) ? 6 : 4, dest_ip); + fprintf(sdp_out, "%s\n", sdpLine); + sprintf(sdpLine, "t=0 0"); + fprintf(sdp_out, "%s\n", sdpLine); + sprintf(sdpLine, "a=x-copyright: Streamed with GPAC (C)2000-200X - http://gpac.sourceforge.net\n"); + fprintf(sdp_out, "%s\n", sdpLine); + + rtp = session->stream; + while (rtp) { + + gf_rtp_builder_get_payload_name(rtp->packetizer, payloadName, mediaName); + + sprintf(sdpLine, "m=%s %d RTP/%s %d", mediaName, rtp->port, rtp->packetizer->slMap.IV_length ? "SAVP" : "AVP", rtp->packetizer->PayloadType); + fprintf(sdp_out, "%s\n", sdpLine); + sprintf(sdpLine, "a=rtpmap:%d %s/%d", rtp->packetizer->PayloadType, payloadName, rtp->packetizer->sl_config.timestampResolution); + fprintf(sdp_out, "%s\n", sdpLine); + + if (gf_isom_get_media_type(rtp->session->mp4File, rtp->track) == GF_ISOM_MEDIA_VISUAL) { + u32 w, h; + w = h = 0; + gf_isom_get_visual_info(rtp->session->mp4File, rtp->track, 1, &w, &h); + if (rtp->packetizer->rtp_payt == GF_RTP_PAYT_H263) { + sprintf(sdpLine, "a=cliprect:0,0,%d,%d", h, w); + } + /*extensions for some mobile phones*/ + sprintf(sdpLine, "a=framesize:%d %d-%d", rtp->packetizer->PayloadType, w, h); + } + /*AMR*/ + if ((rtp->packetizer->rtp_payt == GF_RTP_PAYT_AMR) || (rtp->packetizer->rtp_payt == GF_RTP_PAYT_AMR_WB)) { + sprintf(sdpLine, "a=fmtp:%d octet-align", rtp->packetizer->PayloadType); + fprintf(sdp_out, "%s\n", sdpLine); + } + /*Text*/ + else if (rtp->packetizer->rtp_payt == GF_RTP_PAYT_3GPP_TEXT) { + gf_hinter_format_ttxt_sdp(rtp->packetizer, payloadName, sdpLine, rtp->session->mp4File, rtp->track); + fprintf(sdp_out, "%s\n", sdpLine); + } + /*EVRC/SMV in non header-free mode*/ + else if ((rtp->packetizer->rtp_payt == GF_RTP_PAYT_EVRC_SMV) && (rtp->packetizer->auh_size>1)) { + sprintf(sdpLine, "a=fmtp:%d maxptime=%d", rtp->packetizer->PayloadType, rtp->packetizer->auh_size*20); + fprintf(sdp_out, "%s\n", sdpLine); + } + /*H264/AVC*/ + else if (rtp->packetizer->rtp_payt == GF_RTP_PAYT_H264_AVC) { + GF_AVCConfig *avcc = gf_isom_avc_config_get(rtp->session->mp4File, rtp->track, 1); + sprintf(sdpLine, "a=fmtp:%d profile-level-id=%02X%02X%02X; packetization-mode=1", rtp->packetizer->PayloadType, avcc->AVCProfileIndication, avcc->profile_compatibility, avcc->AVCLevelIndication); + if (gf_list_count(avcc->pictureParameterSets) || gf_list_count(avcc->sequenceParameterSets)) { + u32 i, count, b64s; + char b64[200]; + strcat(sdpLine, "; sprop-parameter-sets="); + count = gf_list_count(avcc->sequenceParameterSets); + for (i=0; isequenceParameterSets, i); + b64s = gf_base64_encode(sl->data, sl->size, b64, 200); + b64[b64s]=0; + strcat(sdpLine, b64); + if (i+1pictureParameterSets); + for (i=0; ipictureParameterSets, i); + b64s = gf_base64_encode(sl->data, sl->size, b64, 200); + b64[b64s]=0; + strcat(sdpLine, b64); + if (i+1avc_nalu_size = avcc->nal_unit_size; + gf_odf_avc_cfg_del(avcc); + } + /*MPEG-4 decoder config*/ + else if (rtp->packetizer->rtp_payt==GF_RTP_PAYT_MPEG4) { + GF_DecoderConfig *dcd; + dcd = gf_isom_get_decoder_config(rtp->session->mp4File, rtp->track, 1); + + if (dcd && dcd->decoderSpecificInfo && dcd->decoderSpecificInfo->data) { + gf_rtp_builder_format_sdp(rtp->packetizer, payloadName, sdpLine, dcd->decoderSpecificInfo->data, dcd->decoderSpecificInfo->dataLength); + } else { + gf_rtp_builder_format_sdp(rtp->packetizer, payloadName, sdpLine, NULL, 0); + } + if (dcd) gf_odf_desc_del((GF_Descriptor *)dcd); + + if (rtp->packetizer->slMap.IV_length) { + const char *kms; + gf_isom_get_ismacryp_info(rtp->session->mp4File, rtp->track, 1, NULL, NULL, NULL, NULL, &kms, NULL, NULL, NULL); + if (!strnicmp(kms, "(key)", 5) || !strnicmp(kms, "(ipmp)", 6) || !strnicmp(kms, "(uri)", 5)) { + strcat(sdpLine, "; ISMACrypKey="); + } else { + strcat(sdpLine, "; ISMACrypKey=(uri)"); + } + strcat(sdpLine, kms); + } + + fprintf(sdp_out, "%s\n", sdpLine); + } + /*MPEG-4 Audio LATM*/ + else if (rtp->packetizer->rtp_payt==GF_RTP_PAYT_LATM) { + GF_DecoderConfig *dcd; + GF_BitStream *bs; + char *config_bytes; + u32 config_size; + + /* form config string */ + bs = gf_bs_new(NULL, 32, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, 0, 1); /* AudioMuxVersion */ + gf_bs_write_int(bs, 1, 1); /* all streams same time */ + gf_bs_write_int(bs, 0, 6); /* numSubFrames */ + gf_bs_write_int(bs, 0, 4); /* numPrograms */ + gf_bs_write_int(bs, 0, 3); /* numLayer */ + + /* audio-specific config */ + dcd = gf_isom_get_decoder_config(rtp->session->mp4File, rtp->track, 1); + if (dcd) { + gf_bs_write_data(bs, dcd->decoderSpecificInfo->data, dcd->decoderSpecificInfo->dataLength); + gf_odf_desc_del((GF_Descriptor *)dcd); + } + + /* other data */ + gf_bs_write_int(bs, 0, 3); /* frameLengthType */ + gf_bs_write_int(bs, 0xff, 8); /* latmBufferFullness */ + gf_bs_write_int(bs, 0, 1); /* otherDataPresent */ + gf_bs_write_int(bs, 0, 1); /* crcCheckPresent */ + gf_bs_get_content(bs, &config_bytes, &config_size); + gf_bs_del(bs); + + gf_rtp_builder_format_sdp(rtp->packetizer, payloadName, sdpLine, config_bytes, config_size); + fprintf(sdp_out, "%s\n", sdpLine); + free(config_bytes); + } + rtp = rtp->next; + } + fprintf(sdp_out, "\n"); + + fclose(sdp_out); + + return GF_OK; +} /* rtp_init_packetizer */ + +GF_Err rtp_init_channel(RTP_Stream *rtp, u32 path_mtu, char * dest, int port) +{ + GF_RTSPTransport tr; + GF_Err res; + + rtp->channel = gf_rtp_new(); + gf_rtp_set_ports(rtp->channel, 0); + + tr.IsUnicast = gf_sk_is_multicast_address(dest) ? 0 : 1; + tr.Profile="RTP/AVP"; + tr.destination = dest; + tr.source = "0.0.0.0"; + tr.IsRecord = 0; + tr.Append = 0; + tr.SSRC = rand(); + + tr.port_first = port; + tr.port_last = port+1; + if (tr.IsUnicast) { + tr.client_port_first = port; + tr.client_port_last = port+1; + } else { + tr.source = dest; + } + + res = gf_rtp_setup_transport(rtp->channel, &tr, dest); + if (res !=0) { + fprintf(stdout, "Cannot setup RTP transport info\n"); + return res; + } + + res = gf_rtp_initialize(rtp->channel, 0, 1, 1500, 0, 0, NULL); + if (res !=0) { + fprintf(stdout, "Cannot initialize RTP sockets\n"); + return res; + } + + return GF_OK; +} /* rtp_init_channel */ + +// --------------------------------------------------------------------------------------------------- + +/* + * paquetization of a burst + * process the AUs till the burst size is reached. + * + */ + +void burst_process_session(RTP_Session *session) +{ + RTP_Stream *rtp, *to_send; + Bool first = 1; + u32 time; + + time = gf_sys_clock(); + if (!session->timelineOrigin) session->timelineOrigin = time; + + + rtp = session->stream; + while (rtp) { + rtp->process_burst = 1; + rtp = rtp->next; + } + + rtp = NULL; + while (1) { + u64 min_ts = (u64) -1; + to_send = NULL; + + /*for each stream, locate next time*/ + rtp = session->stream; + while (rtp) { + /*channel is no longer active in current burst*/ + if (!rtp->process_burst) { + rtp = rtp->next; + continue; + } + /*flush prev stream if needed*/ + rtp_flush_channel(rtp); + + /*load next AU*/ + if (!rtp->au) { + if (rtp->current_au >= rtp->nb_aus) { + if (!session->looping) { + rtp->process_burst = 0; + rtp = rtp->next; + continue; + } + rtp->process_burst = 1; + rtp->ts_offset = rtp->next_ts; + rtp->microsec_ts_offset = (u32) (rtp->next_ts*(1000000.0/rtp->packetizer->sl_config.timestampResolution)) + session->timelineOrigin; + rtp->current_au = 0; + } + if (rtp->current_au + 1==rtp->nb_aus) { + rtp->current_au = rtp->current_au; + } + + rtp->au = gf_isom_get_sample(session->mp4File, rtp->track, rtp->current_au + 1, &rtp->sample_desc_index); + rtp->current_au ++; + if (rtp->au) { + u64 ts; + rtp->sample_duration = gf_isom_get_sample_duration(session->mp4File, rtp->track, rtp->current_au); + rtp->sample_duration = (u32)(rtp->sample_duration*rtp->ts_scale); + + rtp->microsec_dts = (u64) (rtp->microsec_ts_scale * (s64) (rtp->au->DTS)) + rtp->microsec_ts_offset + session->timelineOrigin; + ts = (u64) (rtp->ts_scale * (s64) (rtp->au->DTS)); + + rtp->packetizer->sl_header.decodingTimeStamp = ts + rtp->ts_offset; + + ts = (u64) (rtp->ts_scale * (s64) (rtp->au->DTS+rtp->au->CTS_Offset)); + rtp->packetizer->sl_header.compositionTimeStamp = ts + rtp->ts_offset; + + rtp->packetizer->sl_header.randomAccessPointFlag = rtp->au->IsRAP; + } + } + if (rtp->au) { + if (min_ts > rtp->microsec_dts) { + min_ts = rtp->microsec_dts; + to_send = rtp; + } + } + rtp = rtp->next; + } + + /*burst is full or no packet to write due to timing*/ + if (!to_send) break; + + rtp = to_send; + /*compute drift*/ + if (first) { + first = 0; + session->drift = (s32) ((s64)(time - session->timelineOrigin) - ((s64) rtp->next_ts*1000/rtp->packetizer->sl_config.timestampResolution)); + if (session->streamer->log_level >= LOG_BURST) fprintf(stdout, "Time %u - Burst %u - Session %u (Time %u) - TS %d - Drift %d ms\n", time, session->streamer->nbBurstSent, session->id, time - session->timelineOrigin, rtp->next_ts, session->drift); + else { + fprintf(stdout, "Time %u - Burst %u - Session %u (Time %u) - TS %d - Drift %d ms\r", time, session->streamer->nbBurstSent, session->id, time - session->timelineOrigin, rtp->next_ts, session->drift); + fflush(stdout); + } + + } + + if (session->streamer->log_level >= LOG_AU) fprintf(stdout, "Sess %d - stream %d - Processing AU %d - DTS "LLD" - CTS "LLD"\n", session->id, rtp->track, rtp->current_au, rtp->packetizer->sl_header.decodingTimeStamp, rtp->packetizer->sl_header.compositionTimeStamp); + + /*unpack nal units*/ + if (rtp->avc_nalu_size) { + u32 v, size; + u32 remain = rtp->au->dataLength; + char *ptr = rtp->au->data; + + rtp->packetizer->sl_header.accessUnitStartFlag = 1; + rtp->packetizer->sl_header.accessUnitEndFlag = 0; + while (remain) { + size = 0; + v = rtp->avc_nalu_size; + while (v) { + size |= (u8) *ptr; + ptr++; + remain--; + v-=1; + if (v) size<<=8; + } + remain -= size; + rtp->packetizer->sl_header.accessUnitEndFlag = remain ? 0 : 1; + gf_rtp_builder_process(rtp->packetizer, ptr, size, (u8) !remain, rtp->au->dataLength, rtp->sample_duration, (u8) rtp->sample_desc_index ); + ptr += size; + rtp->packetizer->sl_header.accessUnitStartFlag = 0; + } + } else { + gf_rtp_builder_process(rtp->packetizer, rtp->au->data, rtp->au->dataLength, (u8) 1, rtp->au->dataLength, rtp->sample_duration, (u8) rtp->sample_desc_index); + } + + rtp->next_ts = (u32)(rtp->packetizer->sl_header.decodingTimeStamp + rtp->sample_duration); + /*OK delete sample*/ + gf_isom_sample_del(&rtp->au); + } + + if (session->streamer->log_level >= LOG_BURST) + fprintf(stdout, " Actual Burst Size %d bytes - Actual Bit Rate %d kbps\n", session->dataLengthInBurst, 8*session->dataLengthInBurst/(session->streamer->burstDuration+session->streamer->offDuration)); + session->nbBurstSent++; + session->streamer->nbBurstSent++; + +} + + +void process_sessions(Streamer *streamer) +{ + RTP_Session *session; + RTP_Stream *rtp, *to_send; + u32 time; + s32 diff; + u64 min_ts; + + time = gf_sys_clock(); + /*browse all sessions and locate most mature stream*/ + to_send = NULL; + min_ts = (u64) -1; + session = streamer->session; + while (session) { + /*init session timeline - all sessions are sync'ed for packet scheduling purposes*/ + if (!session->timelineOrigin) session->timelineOrigin = time*1000; + rtp = session->stream; + while (rtp) { + /*load next AU*/ + if (!rtp->au) { + if (rtp->current_au >= rtp->nb_aus) { + Double scale; + if (!rtp->session->looping) { + rtp = rtp->next; + continue; + } + /*increment ts offset*/ + scale = rtp->packetizer->sl_config.timestampResolution/1000.0; + rtp->ts_offset += (u32) (session->duration * scale); + rtp->microsec_ts_offset = (u32) (rtp->ts_offset*(1000000.0/rtp->packetizer->sl_config.timestampResolution)) + session->timelineOrigin; + rtp->current_au = 0; + } + if (rtp->current_au + 1==rtp->nb_aus) { + rtp->current_au = rtp->current_au; + } + + rtp->au = gf_isom_get_sample(rtp->session->mp4File, rtp->track, rtp->current_au + 1, &rtp->sample_desc_index); + rtp->current_au ++; + if (rtp->au) { + u64 ts; + rtp->sample_duration = gf_isom_get_sample_duration(rtp->session->mp4File, rtp->track, rtp->current_au); + rtp->sample_duration = (u32)(rtp->sample_duration*rtp->ts_scale); + + rtp->microsec_dts = (u64) (rtp->microsec_ts_scale * (s64) (rtp->au->DTS)) + rtp->microsec_ts_offset + session->timelineOrigin; + + ts = (u64) (rtp->ts_scale * (s64) (rtp->au->DTS)); + rtp->packetizer->sl_header.decodingTimeStamp = ts + rtp->ts_offset; + + ts = (u64) (rtp->ts_scale * (s64) (rtp->au->DTS+rtp->au->CTS_Offset)); + rtp->packetizer->sl_header.compositionTimeStamp = ts + rtp->ts_offset; + + rtp->packetizer->sl_header.randomAccessPointFlag = rtp->au->IsRAP; + } + } + + /*check timing*/ + if (rtp->au) { + if (min_ts > rtp->microsec_dts) { + min_ts = rtp->microsec_dts; + to_send = rtp; + } + } + + rtp = rtp->next; + } + session = session->next; + } + + /*no input data ...*/ + if( !to_send) return; + min_ts /= 1000; + + /*sleep until TS is mature*/ + while (1) { + diff = (u32) (min_ts) - gf_sys_clock(); + if (diff > 2) { + //fprintf(stdout, "RTP session %d stream %d - sleeping %d ms\n", to_send->session->id, to_send->track, diff); + gf_sleep(1); + } else { + if (diff<0) fprintf(stdout, "WARNING: RTP session %d stream %d - sending packet %d ms too late\n", to_send->session->id, to_send->track, -diff); + break; + } + } + + /*send packets*/ + + /*unpack nal units*/ + if (to_send->avc_nalu_size) { + u32 v, size; + u32 remain = to_send->au->dataLength; + char *ptr = to_send->au->data; + + to_send->packetizer->sl_header.accessUnitStartFlag = 1; + to_send->packetizer->sl_header.accessUnitEndFlag = 0; + while (remain) { + size = 0; + v = to_send->avc_nalu_size; + while (v) { + size |= (u8) *ptr; + ptr++; + remain--; + v-=1; + if (v) size<<=8; + } + remain -= size; + to_send->packetizer->sl_header.accessUnitEndFlag = remain ? 0 : 1; + gf_rtp_builder_process(to_send->packetizer, ptr, size, (u8) !remain, to_send->au->dataLength, to_send->sample_duration, (u8) to_send->sample_desc_index ); + ptr += size; + to_send->packetizer->sl_header.accessUnitStartFlag = 0; + } + } else { + gf_rtp_builder_process(to_send->packetizer, to_send->au->data, to_send->au->dataLength, (u8) 1, to_send->au->dataLength, to_send->sample_duration, (u8) to_send->sample_desc_index); + } + /*delete sample*/ + gf_isom_sample_del(&to_send->au); + +} + + + +// --------------------------------------------------------------------------------------------------- + + +u16 check_next_port(Streamer *streamer, u16 first_port) +{ + RTP_Session *session = streamer->session; + while (session) { + RTP_Stream *rtp = session->stream; + while (rtp) { + if (rtp->port==first_port) { + return check_next_port(streamer, (u16) (first_port+2) ); + } + rtp = rtp->next; + } + session = session->next; + } + return first_port; +} + +/* + * configuration: + * retrieves the parameters from the configuration file + * + */ + +GF_Err configuration(Streamer *streamer, char *cfg_file, char *src_file, char *ip_dest, u16 port, Bool loop, Bool force_mpeg4) +{ + GF_Err e = GF_OK; + RTP_Session *session, *last_sess; + u32 nb_sessions; + const char *opt = NULL; + const char *dest_ip; + GF_Config *configFile = NULL; + u32 i, j; + + if (cfg_file) { + fprintf(stdout, "Configuration file: %s \n", cfg_file); + + configFile = gf_cfg_new(PATHFILE, cfg_file); + if (!configFile) { + fprintf(stderr, "ERROR: could not open the file\n"); + return GF_IO_ERR; + } + + opt = gf_cfg_get_key(configFile, "GLOBAL", "nbSession"); + nb_sessions = opt ? atoi(opt) : 0; + fprintf(stdout, " Number of sessions: %u \n", nb_sessions); + + opt = gf_cfg_get_key(configFile, "GLOBAL", "IP_dest"); + dest_ip = opt ? opt : "127.0.0.1"; + fprintf(stdout, " Destination IP: %s \n", dest_ip); + + + opt = gf_cfg_get_key(configFile, "GLOBAL", "path_mtu"); + if (opt) streamer->path_mtu = atoi(opt); + if (!streamer->path_mtu) streamer->path_mtu = 1450; + fprintf(stdout, " Path MTU: %u bytes \n", streamer->path_mtu); + + /*configure burst mode*/ + opt = gf_cfg_get_key(configFile, "GLOBAL", "burst_mode"); + streamer->burst_mode = (opt && !strcmp(opt, "yes")) ? 1 : 0; + if (streamer->burst_mode) { + opt = gf_cfg_get_key(configFile, "GLOBAL", "off_duration"); + streamer->offDuration = opt ? atoi(opt) : 0; + fprintf(stdout, " Offtime duration: %u ms \n", streamer->offDuration); + + opt = gf_cfg_get_key(configFile, "GLOBAL", "burst_duration"); + streamer->burstDuration = opt ? atoi(opt) : 1000; + fprintf(stdout, " Burst duration: %u ms \n", streamer->burstDuration); + + opt = gf_cfg_get_key(configFile, "GLOBAL", "burst_bitrate"); + streamer->burstBitRate = opt ? atoi(opt) : 500; + fprintf(stdout, " Burst bit rate: %u kbps \n", streamer->burstBitRate); + + streamer->burstSize = streamer->burstBitRate*1024*streamer->burstDuration/1000/8; + streamer->averageBitRate = (streamer->burstDuration * streamer->burstBitRate)/(streamer->offDuration + streamer->burstDuration); + + fprintf(stdout, " Burst mode enabled - burst size: %d bytes - average bit rate %d kbps\n", streamer->burstSize, streamer->averageBitRate); + } + } else { + fprintf(stdout, " Path MTU: %u bytes \n", streamer->path_mtu); + dest_ip = ip_dest; + fprintf(stdout, " Destination IP: %s Port %d\n", dest_ip, port); + nb_sessions = 1; + } + + streamer->payt = BASE_PAYT; + + last_sess = NULL; + for(i=0; inext = session; + } else { + streamer->session = session; + } + last_sess = session; + + session->streamer = streamer; + session->id = i+1; + session->mp4File = file; + + if (cfg_file) { + opt = gf_cfg_get_key(configFile, sessionName, "port"); + first_port = opt ? atoi(opt) : 7000+4*i; + + opt = gf_cfg_get_key(configFile, sessionName, "looping"); + session->looping = opt ? atoi(opt) : 1; + + opt = gf_cfg_get_key(configFile, sessionName, "force_mpeg4-generic"); + session->force_mpeg4_generic = opt ? atoi(opt) : 0; + + opt = gf_cfg_get_key(configFile, sessionName, "path_mtu"); + sess_path_mtu = opt ? atoi(opt) : 0; + if (!opt) sess_path_mtu = streamer->path_mtu; + + opt = gf_cfg_get_key(configFile, sessionName, "IP_dest"); + sess_dest_ip = (char *) (opt ? opt : dest_ip); + } else { + sess_path_mtu = streamer->path_mtu; + sess_dest_ip = (char *) dest_ip; + session->looping = loop; + session->force_mpeg4_generic = force_mpeg4; + first_port = port; + } + + sess_data_size = 0; + prev_stream = NULL; + nb_tracks = gf_isom_get_track_count(session->mp4File); + for (j=0;jmp4File, j+1)) { + case GF_ISOM_MEDIA_VISUAL: + case GF_ISOM_MEDIA_AUDIO: + case GF_ISOM_MEDIA_TEXT: + case GF_ISOM_MEDIA_OD: + case GF_ISOM_MEDIA_SCENE: + break; + default: + continue; + } + + GF_SAFEALLOC(rtp, RTP_Stream); + if (prev_stream) prev_stream->next = rtp; + else session->stream = rtp; + prev_stream = rtp; + + rtp->session = session; + rtp->track = j+1; + + rtp->nb_aus = gf_isom_get_sample_count(session->mp4File, rtp->track); + mediaTimescale = gf_isom_get_media_timescale(session->mp4File, rtp->track); + mediaDuration = (u32)(gf_isom_get_media_duration(session->mp4File, rtp->track)*1000/mediaTimescale); // ms + mediaSize = (u32)gf_isom_get_media_data_size(session->mp4File, rtp->track); + + sess_data_size += mediaSize; + if (mediaDuration > session->duration) session->duration = mediaDuration; + + rtp->port = check_next_port(streamer, first_port); + first_port = rtp->port+2; + + e = rtp_init_channel(rtp, sess_path_mtu+12, sess_dest_ip, rtp->port); + if (e) { + fprintf(stderr, "Could not initialize RTP Channel: %s\n", gf_error_to_string(e)); + goto exit; + } + e = rtp_init_packetizer(rtp, sess_dest_ip); + if (e) { + fprintf(stderr, "Could not initialize Packetizer: %s\n", gf_error_to_string(e)); + goto exit; + } + } + rtp_setup_sdp(session, sess_dest_ip); + + if (streamer->burst_mode) { + session->minBurstSize = (sess_data_size * (streamer->burstDuration + streamer->offDuration)) / session->duration; // bps * ms ==> bits + fprintf(stdout, "Session %u: %d ms - avg bitrate %d kbps - Min Burst Size %d bytes\n", session->id, session->duration, sess_data_size/session->duration, session->minBurstSize); + } + } + + /*configure burst*/ + if (streamer->burst_mode) { + streamer->cycleDuration = 0; + session = streamer->session; + while (session) { + session->nextBurstTime = streamer->cycleDuration; + streamer->cycleDuration += streamer->burstDuration; + session = session->next; + } + streamer->cycleDuration += streamer->offDuration; + } + + fprintf(stdout, "\n"); + +exit: + if (cfg_file) gf_cfg_del(configFile); + return e; +} /* configuration */ + +// --------------------------------------------------------------------------------------------------- + + +void usage() +{ + fprintf(stdout, "Usage: MP4Streamer [options] file\n" + "with options being one fo the following:\n" + "-cfg: file is a configuration file for all sessions\n" + "\n" + "Options used for simple streaming:\n" + "-port=NUM: port to use. Default is 7000\n" + "-mtu=NUM: network path MTU to use. Default is 1450\n" + "-dst=ADD: destination IP address. Default is 127.0.0.1\n" + "-mpeg4: forces usage of mpeg4-generic payload format. Default is off\n" + "-noloop: disables session looping. Default is off\n" + ); +} + +Bool check_exit() +{ + if (gf_prompt_has_input()) { + char c; + c = (char) gf_prompt_get_char(); + if (c=='q') return 1; + } + return 0; +} + +int main(int argc, char **argv) +{ + Streamer streamer; /* Streamer metadata (from cfg file) */ + char *cfg = NULL; + RTP_Session *session; + u32 i; + s32 sleepDuration; + u32 time; + char *ip_dest = "127.0.0.1"; + u16 port = 7000; + Bool loop = 1; + Bool force_mpeg4 = 0; + char *file_name = NULL; + + + if (argc < 2) { + usage(argv[0]); + return 0; + } + + gf_log_set_tools(0xFFFFFFFF); + gf_log_set_level(GF_LOG_ERROR); + + memset(&streamer, 0, sizeof(Streamer)); + streamer.log_level = LOG_BURST; + streamer.path_mtu = 1450; + + for (i=1; i<(u32) argc; i++) { + char *arg = argv[i]; + if (arg[0]=='-') { + if (!stricmp(arg, "-q") || !stricmp(arg, "--quiet")) streamer.log_level = LOG_NONE; + else if (!strnicmp(arg, "-v=", 3)) { + if (!stricmp(arg+3, "au")) streamer.log_level = LOG_AU; + else if (!stricmp(arg+3, "rtp")) streamer.log_level = LOG_PACKET; + } + else if (!strnicmp(arg, "-cfg=", 5)) cfg = arg+5; + else if (!strnicmp(arg, "-port=", 6)) port = atoi(arg+6); + else if (!strnicmp(arg, "-mtu=", 5)) streamer.path_mtu = atoi(arg+5); + else if (!strnicmp(arg, "-dst=", 5)) ip_dest = arg+5; + else if (!stricmp(arg, "-noloop")) loop = 0; + else if (!stricmp(arg, "-mpeg4")) force_mpeg4 = 1; + } else { + file_name = arg; + } + } + + if (!cfg && !file_name) { + usage(argv[0]); + return 0; + } + + fprintf(stdout, "Time-sliced MP4 Streamer - (c) ENST 2006-200X\n"); + fprintf(stdout, "Developed within the Multimedia group\n"); + fprintf(stdout, "with the help of the following students: \n"); + fprintf(stdout, " MIX 2006: Anh-Vu BUI and Xiangfeng LIU \n"); + fprintf(stdout, "\n"); + + if (!cfg) fprintf(stdout, "Configuring streaming of file %s to %s:%d - loop %s\n", file_name, ip_dest, port, loop?"enabled":"disabled"); + /* + * Initialization + * + */ + gf_sys_init(); + if (configuration(&streamer, cfg, file_name, ip_dest, port, loop, force_mpeg4) != GF_OK) { + goto err_exit; + } + + if (streamer.burst_mode) { + session = streamer.session; + while (1) { + burst_process_session(session); + session->dataLengthInBurst = 0; + session->nextBurstTime += streamer.cycleDuration; + session = session->next; + if (!session) session = streamer.session; + + time = gf_sys_clock(); + sleepDuration = session->nextBurstTime - (time - session->timelineOrigin); + if (sleepDuration > 0) { + gf_sleep(sleepDuration); + } + + if (check_exit()) break; + } + } else { + while (1) { + process_sessions(&streamer); + if (check_exit()) break; + } + } + +err_exit: + /* + * Desallocation + * + */ + session = streamer.session; + while (session) { + RTP_Session *_s = session; + RTP_Stream *rtp = session->stream; + while (rtp) { + RTP_Stream *tmp = rtp; + rtp_flush_channel(rtp); + if (rtp->au) gf_isom_sample_del(&rtp->au); + if (rtp->channel) gf_rtp_del(rtp->channel); + if (rtp->packetizer) gf_rtp_builder_del(rtp->packetizer); + if (rtp->packet.payload) free(rtp->packet.payload); + rtp = rtp->next; + free(tmp); + } + if (session->mp4File) gf_isom_close(session->mp4File); + session = session->next; + free(_s); + } + gf_sys_close(); + fprintf(stdout, "done\n"); + return GF_OK; + +} /* end main */ + diff --git a/applications/testapps/mp4_streamer/mp4_streamer.dsp b/applications/testapps/mp4_streamer/mp4_streamer.dsp new file mode 100644 index 0000000..4a05bb3 --- /dev/null +++ b/applications/testapps/mp4_streamer/mp4_streamer.dsp @@ -0,0 +1,102 @@ +# Microsoft Developer Studio Project File - Name="mp4_streamer" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=mp4_streamer - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mp4_streamer.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mp4_streamer.mak" CFG="mp4_streamer - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mp4_streamer - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "mp4_streamer - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mp4_streamer - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 /nologo /subsystem:console /machine:I386 /out:"../../../bin/w32_rel/mp4_streamer.exe" /libpath:"../../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "mp4_streamer - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /out:"../../../bin/w32_deb/mp4_streamer.exe" /pdbtype:sept /libpath:"../../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "mp4_streamer - Win32 Release" +# Name "mp4_streamer - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\main.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/applications/testapps/mpedemux/Makefile b/applications/testapps/mpedemux/Makefile new file mode 100644 index 0000000..75aadd4 --- /dev/null +++ b/applications/testapps/mpedemux/Makefile @@ -0,0 +1,76 @@ +include ../../../config.mak + +vpath %.c $(SRC_PATH)/applications/test_apps/mpedemux + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#file format is read-only +ifeq ($(GPACREADONLY), yes) +CFLAGS+= -DGPAC_READ_ONLY +endif + +ifeq ($(DISABLE_SVG), yes) +CFLAGS+=-DGPAC_DISABLE_SVG +endif + +#common obj +OBJS= main.o + +LINKFLAGS=-L../../../bin/gcc +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=mpedemux$(EXE) +LINKFLAGS+=-lgpac_static -lz $(EXTRALIBS) +#LINKFLAGS+=-lgpac +else +EXT= +PROG=mpedemux +LINKFLAGS+=-lgpac_static $(EXTRALIBS) $(GPAC_SH_FLAGS) -lz +#LINKFLAGS+=-lgpac -lz +endif + + +SRCS := $(OBJS:.o=.c) + +all: LIBGPAC $(PROG) + +LIBGPAC: + $(MAKE) -C ../../../src + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o ../../../bin/gcc/$@ $(OBJS) $(LINKFLAGS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../../bin/gcc/$(PROG) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/applications/testapps/mpedemux/main.c b/applications/testapps/mpedemux/main.c new file mode 100644 index 0000000..b606e91 --- /dev/null +++ b/applications/testapps/mpedemux/main.c @@ -0,0 +1,105 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2007-200X + * All rights reserved + * + * This file is part of GPAC / mpedemux application + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include + +typedef struct +{ + FILE *ts_file; + GF_M2TS_Demuxer *ts_demux; +} MPEDemux; + + +static void mpedemux_on_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *param) +{ + MPEDemux *mpedemux= (MPEDemux *) ts->user; + switch (evt_type) { + case GF_M2TS_EVT_PAT_FOUND: + /* called when the first PAT is fully parsed */ + break; + case GF_M2TS_EVT_SDT_FOUND: + /* called when the first SDT is fully parsed */ + break; + case GF_M2TS_EVT_PMT_FOUND: + /* called when a first PMT is fully parsed */ + break; + case GF_M2TS_EVT_INT_FOUND: + /* called when a first INT is fully parsed */ + /* TODO: create socket for each target in the IP platform */ + break; + case GF_M2TS_EVT_PAT_UPDATE: + case GF_M2TS_EVT_PMT_UPDATE: + case GF_M2TS_EVT_SDT_UPDATE: + /* called when a new version of the table is parsed */ + break; + case GF_M2TS_EVT_PES_PCK: + /* called when a PES packet is parsed */ + break; + case GF_M2TS_EVT_SL_PCK: + /* called when an MPEG-4 SL-packet is parsed */ + break; + case GF_M2TS_EVT_IP_DATAGRAM: + /* called when an IP packet is parsed + TODO: send this packet on the right socket */ + break; + } +} + +static void usage() +{ + fprintf(stdout, "mpedemux input.ts\n"); +} + +int main(int argc, char **argv) +{ + u8 data[188]; + u32 size; + MPEDemux *mpedemux; + + if (argc < 2) { + usage(); + return GF_OK; + } + + gf_log_set_level(GF_LOG_ERROR); + gf_log_set_tools(GF_LOG_CONTAINER); + + GF_SAFEALLOC(mpedemux, MPEDemux); + mpedemux->ts_demux = gf_m2ts_demux_new(); + mpedemux->ts_demux->on_event = mpedemux_on_event; + mpedemux->ts_demux->user = mpedemux; + + mpedemux->ts_file = fopen(argv[1], "rb"); + + while (1) { + /*read chunks by chunks*/ + size = fread(data, 1, 188, mpedemux->ts_file); + if (!size) break; + /*process chunk*/ + gf_m2ts_process_data(mpedemux->ts_demux, data, size); + } + + gf_m2ts_demux_del(mpedemux->ts_demux); + free(mpedemux); + return GF_OK; +} diff --git a/applications/testapps/mpedemux/mpedemux.dsp b/applications/testapps/mpedemux/mpedemux.dsp new file mode 100644 index 0000000..5aae5e6 --- /dev/null +++ b/applications/testapps/mpedemux/mpedemux.dsp @@ -0,0 +1,90 @@ +# Microsoft Developer Studio Project File - Name="mpedemux" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=mpedemux - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mpedemux.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mpedemux.mak" CFG="mpedemux - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mpedemux - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "mpedemux - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mpedemux - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 zlib.lib winmm.lib ws2_32.lib js32.lib /nologo /subsystem:console /machine:I386 /out:"../../../bin/w32_rel/mpedemux.exe" /libpath:"../../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "mpedemux - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 zlib.lib winmm.lib ws2_32.lib js32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../../bin/w32_deb/mpedemux.exe" /pdbtype:sept /libpath:"../../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "mpedemux - Win32 Release" +# Name "mpedemux - Win32 Debug" +# Begin Source File + +SOURCE=.\main.c +# End Source File +# End Target +# End Project diff --git a/applications/testapps/mpeg2ts/main.c b/applications/testapps/mpeg2ts/main.c new file mode 100644 index 0000000..ae79bfc --- /dev/null +++ b/applications/testapps/mpeg2ts/main.c @@ -0,0 +1,88 @@ +#include + +u32 dump_pid = 130; +FILE *dest = NULL; +Bool has_seen_pat = 0; + +void on_m2ts_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) +{ + GF_M2TS_PES_PCK *pck; + switch (evt_type) { + case GF_M2TS_EVT_PAT_FOUND: + fprintf(stdout, "Service connected (PAT found)\n"); + break; + case GF_M2TS_EVT_PAT_REPEAT: + has_seen_pat = 1; + break; + case GF_M2TS_EVT_PAT_UPDATE: + fprintf(stdout, "Service connected (PAT found)\n"); + break; + case GF_M2TS_EVT_PMT_FOUND: + fprintf(stdout, "Program list found - %d streams\n", gf_list_count( ((GF_M2TS_Program*)par)->streams) ); + break; + case GF_M2TS_EVT_PMT_UPDATE: + fprintf(stdout, "Program list updated - %d streams\n", gf_list_count( ((GF_M2TS_Program*)par)->streams) ); + break; + case GF_M2TS_EVT_SDT_FOUND: + fprintf(stdout, "Program Description found - %d desc\n", gf_list_count(ts->SDTs) ); + break; + case GF_M2TS_EVT_SDT_UPDATE: + fprintf(stdout, "Program Description updated - %d desc\n", gf_list_count(ts->SDTs) ); + break; + case GF_M2TS_EVT_PES_PCK: + pck = par; + if (dest && (dump_pid == pck->stream->pid)) { + fwrite(pck->data, pck->data_len, 1, dest); + } + + //fprintf(stdout, "PES(%d): DTS "LLD" PTS" LLD" RAP %d size %d\n", pck->stream->pid, pck->DTS, pck->PTS, pck->rap, pck->data_len); + break; + } +} + +int main(int argc, char **argv) +{ + char data[188]; + u32 size, fsize, fdone; + GF_M2TS_Demuxer *ts; + + FILE *src = fopen(argv[1], "rb"); + ts = gf_m2ts_demux_new(); + ts->on_event = on_m2ts_event; + + fseek(src, 0, SEEK_END); + fsize = ftell(src); + fseek(src, 0, SEEK_SET); + fdone = 0; + + while (!feof(src)) { + size = fread(data, 1, 188, src); + if (size<188) break; + + gf_m2ts_process_data(ts, data, size); + if (has_seen_pat) break; + } + + dest = fopen("pes.mp3", "wb"); + gf_m2ts_reset_parsers(ts); + gf_f64_seek(src, 0, SEEK_SET); + fdone = 0; + while (!feof(src)) { + size = fread(data, 1, 188, src); + if (size<188) break; + + gf_m2ts_process_data(ts, data, size); + + fdone += size; + gf_set_progress("MPEG-2 TS Parsing", fdone, fsize); + } + gf_set_progress("MPEG-2 TS Parsing", fsize, fsize); + + fclose(src); + gf_m2ts_demux_del(ts); + if (dest) fclose(dest); + return 0; +} + + + diff --git a/applications/testapps/mpeg2ts/mpeg2ts.dsp b/applications/testapps/mpeg2ts/mpeg2ts.dsp new file mode 100644 index 0000000..656390f --- /dev/null +++ b/applications/testapps/mpeg2ts/mpeg2ts.dsp @@ -0,0 +1,100 @@ +# Microsoft Developer Studio Project File - Name="mpeg2ts" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=mpeg2ts - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mpeg2ts.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mpeg2ts.mak" CFG="mpeg2ts - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mpeg2ts - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "mpeg2ts - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mpeg2ts - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "mpeg2ts - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 zlib.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "mpeg2ts - Win32 Release" +# Name "mpeg2ts - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\main.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/applications/testapps/svg2bifs/main.c b/applications/testapps/svg2bifs/main.c new file mode 100644 index 0000000..c97ef07 --- /dev/null +++ b/applications/testapps/svg2bifs/main.c @@ -0,0 +1,1046 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2000-200X + * All rights reserved + * + * This file is part of GPAC / svg2bifs application + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +typedef struct { + GF_SAXParser *sax_parser; + + GF_SceneGraph *svg_sg; + GF_Node *svg_parent; + SVGAllAttributes all_atts; + SVGPropertiesPointers svg_props; + + GF_SceneGraph *bifs_sg; + GF_Node *bifs_parent; + GF_Node *bifs_text_node; + + Bool force_transform; + +} SVG2BIFS_Converter; + + +typedef struct { + /* Stage of the resolving: + 0: resolving attributes which depends on the target: from, to, by, values, type + 1: resolving begin times + 2: resolving end times */ + u32 resolve_stage; + /* Animation element being defered */ + SVG_Element *animation_elt; + /* anim parent*/ + SVG_Element *anim_parent; + /* target animated element*/ + SVG_Element *target; + /* id of the target element when unresolved*/ + char *target_id; + + /* attributes which cannot be parsed until the type of the target attribute is known */ + char *type; /* only for animateTransform */ + char *to; + char *from; + char *by; + char *values; +} SVG_DeferedAnimation; + + +static GF_Node *create_appearance(SVGPropertiesPointers *svg_props, GF_SceneGraph *sg) +{ + M_Appearance *app; + M_Material2D *mat; + M_XLineProperties *xlp; + M_RadialGradient *rg; + M_LinearGradient *lg; + + app = (M_Appearance *)gf_node_new(sg, TAG_MPEG4_Appearance); + + app->material = gf_node_new(sg, TAG_MPEG4_Material2D); + mat = (M_Material2D *)app->material; + gf_node_register((GF_Node*)mat, (GF_Node*)app); + + if (svg_props->fill->type == SVG_PAINT_NONE) { + mat->filled = 0; + } else { + mat->filled = 1; + if (svg_props->fill->type == SVG_PAINT_COLOR) { + if (svg_props->fill->color.type == SVG_COLOR_RGBCOLOR) { + mat->emissiveColor.red = svg_props->fill->color.red; + mat->emissiveColor.green = svg_props->fill->color.green; + mat->emissiveColor.blue = svg_props->fill->color.blue; + } else if (svg_props->fill->color.type == SVG_COLOR_CURRENTCOLOR) { + mat->emissiveColor.red = svg_props->color->color.red; + mat->emissiveColor.green = svg_props->color->color.green; + mat->emissiveColor.blue = svg_props->color->color.blue; + } else { + /* WARNING */ + mat->emissiveColor.red = 0; + mat->emissiveColor.green = 0; + mat->emissiveColor.blue = 0; + } + } else { // SVG_PAINT_URI + /* TODO: gradient or solidcolor */ + } + } + + mat->transparency = FIX_ONE - svg_props->fill_opacity->value; + + if (svg_props->stroke->type != SVG_PAINT_NONE && + svg_props->stroke_width->value != 0) { + mat->lineProps = gf_node_new(sg, TAG_MPEG4_XLineProperties); + xlp = (M_XLineProperties *)mat->lineProps; + gf_node_register((GF_Node*)xlp, (GF_Node*)mat); + + xlp->width = svg_props->stroke_width->value; + + if (svg_props->stroke->type == SVG_PAINT_COLOR) { + if (svg_props->stroke->color.type == SVG_COLOR_RGBCOLOR) { + xlp->lineColor.red = svg_props->stroke->color.red; + xlp->lineColor.green = svg_props->stroke->color.green; + xlp->lineColor.blue = svg_props->stroke->color.blue; + } else if (svg_props->stroke->color.type == SVG_COLOR_CURRENTCOLOR) { + xlp->lineColor.red = svg_props->color->color.red; + xlp->lineColor.green = svg_props->color->color.green; + xlp->lineColor.blue = svg_props->color->color.blue; + } else { + /* WARNING */ + xlp->lineColor.red = 0; + xlp->lineColor.green = 0; + xlp->lineColor.blue = 0; + } + } else { // SVG_PAINT_URI + /* TODO: xlp->texture = ... */ + } + xlp->transparency = FIX_ONE - svg_props->stroke_opacity->value; + xlp->lineCap = *svg_props->stroke_linecap; + xlp->lineJoin = *svg_props->stroke_linejoin; + if (svg_props->stroke_dasharray->type == SVG_STROKEDASHARRAY_ARRAY) { + u32 i; + xlp->lineStyle = 6; + gf_sg_vrml_mf_alloc(&xlp->dashes, GF_SG_VRML_MFFLOAT, svg_props->stroke_dasharray->array.count); + for (i = 0; i < svg_props->stroke_dasharray->array.count; i++) { + xlp->dashes.vals[i] = svg_props->stroke_dasharray->array.vals[i] / svg_props->stroke_width->value; + } + } + xlp->miterLimit = svg_props->stroke_miterlimit->value; + } + + return (GF_Node*)app; +} + + + +static GF_Node *add_transform_matrix(SVG2BIFS_Converter *converter, GF_Node *node) +{ + M_TransformMatrix2D *tr = (M_TransformMatrix2D*)gf_node_new(converter->bifs_sg, TAG_MPEG4_TransformMatrix2D); + gf_node_register((GF_Node *)tr, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, (GF_Node *)tr); + if (converter->all_atts.transform) { + SVG_Transform *svg_tr = converter->all_atts.transform; + tr->mxx = svg_tr->mat.m[0]; + tr->mxy = svg_tr->mat.m[1]; + tr->tx = svg_tr->mat.m[2]; + tr->myx = svg_tr->mat.m[3]; + tr->myy = svg_tr->mat.m[4]; + tr->ty = svg_tr->mat.m[5]; + } + return (GF_Node *)tr; + +} + +static GF_Node *add_transform2d(SVG2BIFS_Converter *converter, GF_Node *node) +{ + M_Transform2D *tr = (M_Transform2D*)gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); + gf_node_register((GF_Node *)tr, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, (GF_Node *)tr); + return (GF_Node *)tr; +} + +static void svg_parse_animation(GF_SceneGraph *sg, SVG_DeferedAnimation *anim) +{ + GF_FieldInfo info; + u32 tag; + u8 anim_value_type = 0; + + if (anim->resolve_stage==0) { + /* Stage 0: parsing the animation attribute values + for that we need to resolve the target first */ + if (!anim->target) + anim->target = (SVG_Element *) gf_sg_find_node_by_name(sg, anim->target_id + 1); + + if (!anim->target) { + /* the target is still not known stay in stage 0 */ + return; + } else { + XMLRI *iri; + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_XLINK_ATT_href, 1, 0, &info); + iri = (XMLRI *)info.far_ptr; + iri->type = XMLRI_ELEMENTID; + iri->target = anim->target; + gf_node_register_iri(sg, iri); + } + + tag = gf_node_get_tag((GF_Node *)anim->animation_elt); + /* get the attribute name attribute if specified */ + if (anim->type && (tag== TAG_SVG_animateTransform) ) { + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_transform_type, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->type, 0); + switch(*(SVG_TransformType *) info.far_ptr) { + case SVG_TRANSFORM_TRANSLATE: + anim_value_type = SVG_Transform_Translate_datatype; + break; + case SVG_TRANSFORM_SCALE: + anim_value_type = SVG_Transform_Scale_datatype; + break; + case SVG_TRANSFORM_ROTATE: + anim_value_type = SVG_Transform_Rotate_datatype; + break; + case SVG_TRANSFORM_SKEWX: + anim_value_type = SVG_Transform_SkewX_datatype; + break; + case SVG_TRANSFORM_SKEWY: + anim_value_type = SVG_Transform_SkewY_datatype; + break; + case SVG_TRANSFORM_MATRIX: + anim_value_type = SVG_Transform_datatype; + break; + default: + fprintf(stdout, "unknown datatype for animate transform"); + return; + } + } + else if (gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_attributeName, 0, 0, &info) == GF_OK) { + gf_node_get_attribute_by_name((GF_Node *)anim->target, ((SMIL_AttributeName *)info.far_ptr)->name, 0, 1, 1, &info); + anim_value_type = info.fieldType; + } else { + if (tag == TAG_SVG_animateMotion) { + anim_value_type = SVG_Motion_datatype; + } else if (tag == TAG_SVG_discard) { + /* there is no value to parse in discard, we can jump to the next stage */ + anim->resolve_stage = 1; + svg_parse_animation(sg, anim); + return; + } else { + fprintf(stdout, "Missing attributeName attribute on %s", gf_node_get_name((GF_Node *)anim->animation_elt)); + return; + } + } + + if (anim->to) { + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_to, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->to, anim_value_type); + } + if (anim->from) { + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_from, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->from, anim_value_type); + } + if (anim->by) { + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_by, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->by, anim_value_type); + } + if (anim->values) { + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_values, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->values, anim_value_type); + } + anim->resolve_stage = 1; + } +} + + +static void svg2bifs_node_start(void *sax_cbck, const char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + u32 i; + SVG2BIFS_Converter *converter = (SVG2BIFS_Converter *)sax_cbck; + SVGPropertiesPointers *backup_props; + char *id_string = NULL; + u32 tag; + SVG_Element *elt; + SVG_DeferedAnimation *anim = NULL; + + tag = gf_xml_get_element_tag(name, 0); + elt = (SVG_Element*)gf_node_new(converter->svg_sg, tag); + if (!gf_sg_get_root_node(converter->svg_sg)) { + gf_node_register((GF_Node *)elt, NULL); + gf_sg_set_root_node(converter->svg_sg, (GF_Node *)elt); + } else { + gf_node_register((GF_Node *)elt, converter->svg_parent); + //gf_node_list_add_child(&((GF_ParentNode*)converter->svg_parent)->children, (GF_Node *)elt); + } + +// fprintf(stdout, "Converting %s\n", gf_node_get_class_name((GF_Node *)elt)); +// if (converter->bifs_parent) fprintf(stdout, "%s\n", gf_node_get_class_name(converter->bifs_parent)); + + if (gf_svg_is_animation_tag(tag)) { + GF_SAFEALLOC(anim, SVG_DeferedAnimation); + /*default anim target is parent node*/ + anim->animation_elt = elt; + if (converter->svg_parent) { + anim->target = anim->anim_parent = (SVG_Element*) converter->svg_parent; + } + } + + for (i=0; ivalue || !strlen(att->value)) continue; + + if (!stricmp(att->name, "style")) { + gf_svg_parse_style((GF_Node *)elt, att->value); + } else if (!stricmp(att->name, "id") || !stricmp(att->name, "xml:id")) { + gf_svg_parse_element_id((GF_Node *)elt, att->value, 0); + id_string = att->value; + } else if (anim && !stricmp(att->name, "to")) { + anim->to = strdup(att->value); + } else if (anim && !stricmp(att->name, "from")) { + anim->from = strdup(att->value); + } else if (anim && !stricmp(att->name, "by")) { + anim->by = strdup(att->value); + } else if (anim && !stricmp(att->name, "values")) { + anim->values = strdup(att->value); + } else if (anim && (tag == TAG_SVG_animateTransform) && !stricmp(att->name, "type")) { + anim->type = strdup(att->value); + } else { + GF_FieldInfo info; + if (gf_node_get_field_by_name((GF_Node *)elt, att->name, &info)==GF_OK) { + gf_svg_parse_attribute((GF_Node *)elt, &info, att->value, 0); + } else { + fprintf(stdout, "Skipping attribute %s\n", att->name); + } + } + } + + if (anim) { + svg_parse_animation(converter->svg_sg, anim); + } + + memset(&converter->all_atts, 0, sizeof(SVGAllAttributes)); + gf_svg_flatten_attributes(elt, &converter->all_atts); + + backup_props = malloc(sizeof(SVGPropertiesPointers)); + memcpy(backup_props, &converter->svg_props, sizeof(SVGPropertiesPointers)); + gf_node_set_private((GF_Node *)elt, backup_props); + + gf_svg_apply_inheritance(&converter->all_atts, &converter->svg_props); + + fprintf(stdout, "START\t%s\t%s\t%s", converter->svg_parent ? gf_node_get_class_name(converter->svg_parent) : "none", converter->bifs_parent ? gf_node_get_class_name(converter->bifs_parent) : "none", name); + converter->svg_parent = (GF_Node *)elt; + if (!gf_sg_get_root_node(converter->bifs_sg)) { + if (tag == TAG_SVG_svg) { + GF_Node *node, *child; + + converter->bifs_sg->usePixelMetrics = 1; + if (converter->all_atts.width && converter->all_atts.width->type == SVG_NUMBER_VALUE) { + converter->bifs_sg->width = FIX2INT(converter->all_atts.width->value); + } else { + converter->bifs_sg->width = 320; + } + if (converter->all_atts.height && converter->all_atts.height->type == SVG_NUMBER_VALUE) { + converter->bifs_sg->height = FIX2INT(converter->all_atts.height->value); + } else { + converter->bifs_sg->height = 200; + } + + node = gf_node_new(converter->bifs_sg, TAG_MPEG4_OrderedGroup); + gf_node_register(node, NULL); + gf_sg_set_root_node(converter->bifs_sg, node); + + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_QuantizationParameter); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + { + M_QuantizationParameter *qp = (M_QuantizationParameter *)child; + qp->useEfficientCoding = 1; + } + + /* SVG to BIFS coordinate transformation */ + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Viewport); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + { + M_Viewport *vp = (M_Viewport*)child; + if (converter->all_atts.viewBox) { + vp->size.x = converter->all_atts.viewBox->width; + vp->size.y = converter->all_atts.viewBox->height; + vp->position.x = converter->all_atts.viewBox->x+converter->all_atts.viewBox->width/2; + vp->position.y = -(converter->all_atts.viewBox->y+converter->all_atts.viewBox->height/2); + } else { + vp->size.x = INT2FIX(converter->bifs_sg->width); + vp->size.y = INT2FIX(converter->bifs_sg->height); + vp->position.x = INT2FIX(converter->bifs_sg->width)/2; + vp->position.y = -INT2FIX(converter->bifs_sg->height)/2; + } + } + + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Background2D); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + { + M_Background2D *b = (M_Background2D *)child; + b->backColor.red = FIX_ONE; + b->backColor.green = FIX_ONE; + b->backColor.blue = FIX_ONE; + } + + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + { + M_Transform2D *tr = (M_Transform2D *)node; + tr->scale.y = -FIX_ONE; + } + converter->bifs_parent = node; + } + } else { + GF_Node *node, *child; + + node = converter->bifs_parent; + + switch(tag) { + case TAG_SVG_g: + { + if (converter->all_atts.transform) { + node = add_transform_matrix(converter, node); + converter->bifs_parent = node; + } else { + M_Group *g = (M_Group*)gf_node_new(converter->bifs_sg, TAG_MPEG4_Group); + gf_node_register((GF_Node *)g, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, (GF_Node *)g); + node = (GF_Node *)g; + converter->bifs_parent = node; + } + } + break; + case TAG_SVG_rect: + { + Bool is_parent_set = 0; + if (converter->all_atts.transform) { + node = add_transform_matrix(converter, node); + converter->bifs_parent = node; + is_parent_set = 1; + } + if (converter->force_transform) { + node = add_transform2d(converter, node); + if (!is_parent_set) { + converter->bifs_parent = node; + is_parent_set = 1; + } + } + if (converter->all_atts.x || converter->all_atts.y) { + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + if (!is_parent_set) { + converter->bifs_parent = node; + is_parent_set = 1; + } + { + M_Transform2D *tr = (M_Transform2D *)node; + if (converter->all_atts.x) tr->translation.x = converter->all_atts.x->value + (converter->all_atts.width?converter->all_atts.width->value/2:0); + if (converter->all_atts.y) tr->translation.y = converter->all_atts.y->value + (converter->all_atts.height?converter->all_atts.height->value/2:0); + } + } + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + if (!is_parent_set) converter->bifs_parent = node; + { + M_Shape *shape = (M_Shape *)node; + shape->geometry = gf_node_new(converter->bifs_sg, TAG_MPEG4_Rectangle); + gf_node_register(shape->geometry, (GF_Node *)shape); + { + M_Rectangle *rect = (M_Rectangle *)shape->geometry; + if (converter->all_atts.width) rect->size.x = converter->all_atts.width->value; + if (converter->all_atts.height) rect->size.y = converter->all_atts.height->value; + } + + shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); + gf_node_register(shape->appearance, (GF_Node *)shape); + } + } + break; + case TAG_SVG_path: + { + Bool is_parent_set = 0; + if (converter->all_atts.transform) { + node = add_transform_matrix(converter, node); + converter->bifs_parent = node; + is_parent_set = 1; + } + if (converter->force_transform) { + node = add_transform2d(converter, node); + if (!is_parent_set) { + converter->bifs_parent = node; + is_parent_set = 1; + } + } + if (converter->all_atts.x || converter->all_atts.y) { + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + if (!is_parent_set) { + converter->bifs_parent = node; + is_parent_set = 1; + } + { + M_Transform2D *tr = (M_Transform2D *)node; + if (converter->all_atts.x) tr->translation.x = converter->all_atts.x->value; + if (converter->all_atts.y) tr->translation.y = converter->all_atts.y->value; + } + } + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + if (!is_parent_set) converter->bifs_parent = node; + { + M_Shape *shape = (M_Shape *)node; + shape->geometry = gf_node_new(converter->bifs_sg, TAG_MPEG4_XCurve2D); + gf_node_register(shape->geometry, (GF_Node *)shape); + if (converter->all_atts.d) { + M_Coordinate2D *c2d; + M_XCurve2D *xc = (M_XCurve2D *)shape->geometry; + u32 i, j, c, k; + + xc->point = gf_node_new(converter->bifs_sg, TAG_MPEG4_Coordinate2D); + c2d = (M_Coordinate2D *)xc->point; + gf_node_register(xc->point, (GF_Node *)xc); + + gf_sg_vrml_mf_alloc(&c2d->point, GF_SG_VRML_MFVEC2F, converter->all_atts.d->n_points); + gf_sg_vrml_mf_alloc(&xc->type, GF_SG_VRML_MFINT32, converter->all_atts.d->n_points); + + c = 0; + k = 0; + j = 0; + c2d->point.vals[k] = converter->all_atts.d->points[0]; + k++; + xc->type.vals[0] = 0; + for (i = 1; i < converter->all_atts.d->n_points; ) { + switch(converter->all_atts.d->tags[i]) { + case GF_PATH_CURVE_ON: + c2d->point.vals[k] = converter->all_atts.d->points[i]; + k++; + + if (i-1 == converter->all_atts.d->contours[c]) { + xc->type.vals[j] = 0; + c++; + } else { + xc->type.vals[j] = 1; + } + i++; + break; + case GF_PATH_CURVE_CUBIC: + c2d->point.vals[k] = converter->all_atts.d->points[i]; + c2d->point.vals[k+1] = converter->all_atts.d->points[i+1]; + c2d->point.vals[k+2] = converter->all_atts.d->points[i+2]; + k+=3; + + xc->type.vals[j] = 2; + if (converter->all_atts.d->tags[i+2]==GF_PATH_CLOSE) { + j++; + xc->type.vals[j] = 6; + } + i+=3; + break; + case GF_PATH_CLOSE: + xc->type.vals[j] = 6; + i++; + break; + case GF_PATH_CURVE_CONIC: + c2d->point.vals[k] = converter->all_atts.d->points[i]; + c2d->point.vals[k+1] = converter->all_atts.d->points[i+1]; + k+=2; + + xc->type.vals[j] = 7; + if (converter->all_atts.d->tags[i+1]==GF_PATH_CLOSE) { + j++; + xc->type.vals[j] = 6; + } + i+=2; + break; + } + j++; + } + xc->type.count = j; + c2d->point.count = k; + } + + shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); + gf_node_register(shape->appearance, (GF_Node *)shape); + } + } + break; + case TAG_SVG_polyline: + { + Bool is_parent_set = 0; + if (converter->all_atts.transform) { + node = add_transform_matrix(converter, node); + converter->bifs_parent = node; + is_parent_set = 1; + } + if (converter->force_transform) { + node = add_transform2d(converter, node); + if (!is_parent_set) { + converter->bifs_parent = node; + is_parent_set = 1; + } + } + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + if (!is_parent_set) converter->bifs_parent = node; + { + M_Shape *shape = (M_Shape *)node; + shape->geometry = gf_node_new(converter->bifs_sg, TAG_MPEG4_IndexedFaceSet2D); + gf_node_register(shape->geometry, (GF_Node *)shape); + if (converter->all_atts.points) { + M_Coordinate2D *c2d; + M_IndexedFaceSet2D *ifs = (M_IndexedFaceSet2D *)shape->geometry; + u32 i; + + ifs->coord = gf_node_new(converter->bifs_sg, TAG_MPEG4_Coordinate2D); + c2d = (M_Coordinate2D *)ifs->coord; + gf_node_register(ifs->coord, (GF_Node *)ifs); + + gf_sg_vrml_mf_alloc(&c2d->point, GF_SG_VRML_MFVEC2F, gf_list_count(*converter->all_atts.points)); + for (i = 0; i < gf_list_count(*converter->all_atts.points); i++) { + SVG_Point *p = (SVG_Point *)gf_list_get(*converter->all_atts.points, i); + c2d->point.vals[i].x = p->x; + c2d->point.vals[i].y = p->y; + } + } + + shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); + gf_node_register(shape->appearance, (GF_Node *)shape); + } + } + break; + case TAG_SVG_text: + { + Bool is_parent_set = 0; + if (converter->all_atts.transform) { + node = add_transform_matrix(converter, node); + converter->bifs_parent = node; + is_parent_set = 1; + } + if (converter->force_transform) { + node = add_transform2d(converter, node); + if (!is_parent_set) { + converter->bifs_parent = node; + is_parent_set = 1; + } + } + + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + { + M_Transform2D *tr = (M_Transform2D *)child; + if (converter->all_atts.text_x) tr->translation.x = ((SVG_Coordinate *)gf_list_get(*converter->all_atts.text_x, 0))->value; + if (converter->all_atts.text_y) tr->translation.y = ((SVG_Coordinate *)gf_list_get(*converter->all_atts.text_y, 0))->value; + tr->scale.y = -FIX_ONE; + } + node = child; + child = NULL; + if (!is_parent_set) { + converter->bifs_parent = node; + is_parent_set = 1; + } + + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + if (!is_parent_set) converter->bifs_parent = node; + { + M_FontStyle *fs; + M_Text *text; + M_Shape *shape = (M_Shape *)node; + text = (M_Text *)gf_node_new(converter->bifs_sg, TAG_MPEG4_Text); + shape->geometry = (GF_Node *)text; + converter->bifs_text_node = shape->geometry; + gf_node_register(shape->geometry, (GF_Node *)shape); + + fs = (M_FontStyle *)gf_node_new(converter->bifs_sg, TAG_MPEG4_XFontStyle); + gf_node_register((GF_Node *)fs, (GF_Node*)text); + text->fontStyle = (GF_Node *)fs; + + gf_sg_vrml_mf_alloc(&fs->family, GF_SG_VRML_MFSTRING, 1); + fs->family.vals[0] = strdup(converter->svg_props.font_family->value); + fs->size = converter->svg_props.font_size->value; + + shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); + gf_node_register(shape->appearance, (GF_Node *)shape); + } + } + break; + case TAG_SVG_ellipse: + case TAG_SVG_circle: + { + Bool is_parent_set = 0; + if (converter->all_atts.transform) { + node = add_transform_matrix(converter, node); + converter->bifs_parent = node; + is_parent_set = 1; + } + if (converter->force_transform) { + node = add_transform2d(converter, node); + if (!is_parent_set) { + converter->bifs_parent = node; + is_parent_set = 1; + } + } + if (converter->all_atts.cx || converter->all_atts.cy) { + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + { + M_Transform2D *tr = (M_Transform2D *)child; + if (converter->all_atts.cx) tr->translation.x = converter->all_atts.cx->value; + if (converter->all_atts.cy) tr->translation.y = converter->all_atts.cy->value; + } + node = child; + child = NULL; + if (!is_parent_set) { + converter->bifs_parent = node; + is_parent_set = 1; + } + } + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + if (!is_parent_set) converter->bifs_parent = node; + { + M_Shape *shape = (M_Shape *)node; + if (tag == TAG_SVG_ellipse) { + M_Ellipse *e = (M_Ellipse *)gf_node_new(converter->bifs_sg, TAG_MPEG4_Ellipse); + shape->geometry = (GF_Node *)e; + e->radius.x = converter->all_atts.rx->value; + e->radius.y = converter->all_atts.ry->value; + } else { + M_Circle *c = (M_Circle *)gf_node_new(converter->bifs_sg, TAG_MPEG4_Circle); + shape->geometry = (GF_Node *)c; + c->radius = converter->all_atts.r->value; + } + gf_node_register(shape->geometry, (GF_Node *)shape); + + shape->appearance = create_appearance(&converter->svg_props, converter->bifs_sg); + gf_node_register(shape->appearance, (GF_Node *)shape); + } + } + break; + + case TAG_SVG_defs: + { + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Switch); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + { + M_Switch *sw = (M_Switch *)node; + sw->whichChoice = -1; + } + converter->bifs_parent = node; + } + break; + case TAG_SVG_solidColor: + { + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Shape); + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + converter->bifs_parent = node; + } + break; + case TAG_SVG_animateTransform: + { + GF_Node *child_ts; + if (!gf_node_get_id(node)) { + gf_node_set_id(node, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL); + } + + child_ts = gf_node_new(converter->bifs_sg, TAG_MPEG4_TimeSensor); + if (!gf_node_get_id(child_ts)) { + gf_node_set_id(child_ts, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL); + } + gf_node_register(child_ts, node); + gf_node_list_add_child(&((GF_ParentNode *)node)->children, child_ts); + { + M_TimeSensor *ts = (M_TimeSensor *)child_ts; + if (converter->all_atts.dur) { + ts->cycleInterval = converter->all_atts.dur->clock_value; + } + if (converter->all_atts.repeatCount && converter->all_atts.repeatCount->type == SMIL_REPEATCOUNT_INDEFINITE) { + ts->loop = 1; + } + } + + if (converter->all_atts.transform_type) { + GF_FieldInfo fromField, toField; + + switch (*converter->all_atts.transform_type) { + case SVG_TRANSFORM_ROTATE: + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_PositionInterpolator2D); + if (!gf_node_get_id(child)) { + gf_node_set_id(child, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL); + } + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode *)node)->children, child); + + gf_node_get_field_by_name(child_ts, "fraction_changed", &fromField); + gf_node_get_field_by_name(child, "set_fraction", &toField); + gf_sg_route_new(converter->bifs_sg, child_ts, fromField.fieldIndex, child, toField.fieldIndex); + + gf_node_get_field_by_name(child, "value_changed", &fromField); + gf_node_get_field_by_name(node, "rotationAngle", &toField); + gf_sg_route_new(converter->bifs_sg, child, fromField.fieldIndex, node, toField.fieldIndex); + { + M_PositionInterpolator2D *pi2d = (M_PositionInterpolator2D *)child; + if (converter->all_atts.keyTimes) { + SFFloat *g; + u32 count, i; + count = gf_list_count(*converter->all_atts.keyTimes); + for (i = 0; i < count; i++) { + Fixed *f = gf_list_get(*converter->all_atts.keyTimes, i); + gf_sg_vrml_mf_append(&pi2d->key, GF_SG_VRML_MFFLOAT, &g); + *g = *f; + } + } + if (converter->all_atts.values) { + SFVec2f *g; + u32 count, i; + count = gf_list_count(converter->all_atts.values->values); + for (i = 0; i < count; i++) { + SVG_Point_Angle *p; + p = gf_list_get(converter->all_atts.values->values, i); + gf_sg_vrml_mf_append(&pi2d->keyValue, GF_SG_VRML_MFVEC2F, &g); + g->x = p->x; + g->y = p->y; + } + } + } + + + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_ScalarInterpolator); + if (!gf_node_get_id(child)) { + gf_node_set_id(child, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL); + } + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode *)node)->children, child); + + gf_node_get_field_by_name(child_ts, "fraction_changed", &fromField); + gf_node_get_field_by_name(child, "set_fraction", &toField); + gf_sg_route_new(converter->bifs_sg, child_ts, fromField.fieldIndex, child, toField.fieldIndex); + + gf_node_get_field_by_name(child, "value_changed", &fromField); + gf_node_get_field_by_name(node, "center", &toField); + gf_sg_route_new(converter->bifs_sg, child, fromField.fieldIndex, node, toField.fieldIndex); + + { + M_ScalarInterpolator *si = (M_ScalarInterpolator *)child; + if (converter->all_atts.keyTimes) { + SFFloat *g; + u32 count, i; + count = gf_list_count(*converter->all_atts.keyTimes); + for (i = 0; i < count; i++) { + Fixed *f = gf_list_get(*converter->all_atts.keyTimes, i); + gf_sg_vrml_mf_append(&si->key, GF_SG_VRML_MFFLOAT, &g); + *g = *f; + } + } + if (converter->all_atts.values) { + SFFloat *g; + u32 count, i; + count = gf_list_count(converter->all_atts.values->values); + for (i = 0; i < count; i++) { + SVG_Point_Angle *p; + p = gf_list_get(converter->all_atts.values->values, i); + gf_sg_vrml_mf_append(&si->keyValue, GF_SG_VRML_MFFLOAT, &g); + *g = p->angle; + } + } + } + + break; + + case SVG_TRANSFORM_SCALE: + case SVG_TRANSFORM_TRANSLATE: + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_PositionInterpolator2D); + if (!gf_node_get_id(child)) { + gf_node_set_id(child, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL); + } + gf_node_register(child, node); + gf_node_list_add_child(&((GF_ParentNode *)node)->children, child); + + gf_node_get_field_by_name(child_ts, "fraction_changed", &fromField); + gf_node_get_field_by_name(child, "set_fraction", &toField); + gf_sg_route_new(converter->bifs_sg, child_ts, fromField.fieldIndex, child, toField.fieldIndex); + + gf_node_get_field_by_name(child, "value_changed", &fromField); + if (*converter->all_atts.transform_type == SVG_TRANSFORM_SCALE) + gf_node_get_field_by_name(node, "scale", &toField); + else + gf_node_get_field_by_name(node, "translation", &toField); + + gf_sg_route_new(converter->bifs_sg, child, fromField.fieldIndex, node, toField.fieldIndex); + { + M_PositionInterpolator2D *pi2d = (M_PositionInterpolator2D *)child; + if (converter->all_atts.keyTimes) { + SFFloat *g; + u32 count, i; + count = gf_list_count(*converter->all_atts.keyTimes); + for (i = 0; i < count; i++) { + Fixed *f = gf_list_get(*converter->all_atts.keyTimes, i); + gf_sg_vrml_mf_append(&pi2d->key, GF_SG_VRML_MFFLOAT, &g); + *g = *f; + } + } + if (converter->all_atts.values) { + SFVec2f *g; + u32 count, i; + count = gf_list_count(converter->all_atts.values->values); + for (i = 0; i < count; i++) { + SVG_Point *p; + p = gf_list_get(converter->all_atts.values->values, i); + gf_sg_vrml_mf_append(&pi2d->keyValue, GF_SG_VRML_MFVEC2F, &g); + g->x = p->x; + g->y = p->y; + } + } + } + break; + default: + fprintf(stdout, "Warning: transformation type not supported \n"); + } + } + //converter->bifs_parent = node; + } + break; + default: + { + fprintf(stdout, "Warning: element %s not supported \n", gf_node_get_class_name((GF_Node *)elt)); + child = gf_node_new(converter->bifs_sg, TAG_MPEG4_Transform2D); + gf_node_register(child, node); + //gf_node_list_add_child(&((GF_ParentNode*)node)->children, child); + node = child; + child = NULL; + converter->bifs_parent = node; + } + break; + } + + if (id_string) + gf_node_set_id(converter->bifs_parent, gf_sg_get_next_available_node_id(converter->bifs_sg), NULL);//gf_node_get_name((GF_Node *)elt)); + + } + fprintf(stdout, "\t%s\n", converter->bifs_parent ? gf_node_get_class_name(converter->bifs_parent) : "none"); +} + +static void svg2bifs_node_end(void *sax_cbck, const char *name, const char *name_space) +{ + SVG2BIFS_Converter *converter = (SVG2BIFS_Converter *)sax_cbck; + GF_Node *parent; + + SVGPropertiesPointers *backup_props = gf_node_get_private(converter->svg_parent); + memcpy(&converter->svg_props, backup_props, sizeof(SVGPropertiesPointers)); +// free(backup_props); + gf_node_set_private(converter->svg_parent, NULL); + + if (!(gf_node_get_tag(converter->svg_parent) == TAG_SVG_animateTransform)) + converter->bifs_parent = gf_node_get_parent(converter->bifs_parent, 0); + parent = gf_node_get_parent(converter->svg_parent, 0); + gf_node_unregister(converter->svg_parent, parent); + if (!parent) gf_sg_set_root_node(converter->svg_sg, NULL); + converter->svg_parent = parent; + converter->bifs_text_node = NULL; + + fprintf(stdout, "END:\t%s\t%s\n", converter->svg_parent ? gf_node_get_class_name(converter->svg_parent) : "none", converter->bifs_parent ? gf_node_get_class_name(converter->bifs_parent) : "none"); +} + +static void svg2bifs_text_content(void *sax_cbck, const char *text_content, Bool is_cdata) +{ + SVG2BIFS_Converter *converter = (SVG2BIFS_Converter *)sax_cbck; + if (converter->bifs_text_node) { + M_Text *text = (M_Text *)converter->bifs_text_node; + gf_sg_vrml_mf_alloc(&text->string, GF_SG_VRML_MFSTRING, 1); + text->string.vals[0] = strdup(text_content); + } +} + +int main(int argc, char **argv) +{ + SVG2BIFS_Converter *converter; + GF_SceneDumper *dump; + char *tmp; + + gf_sys_init(); + + GF_SAFEALLOC(converter, SVG2BIFS_Converter); + + converter->sax_parser = gf_xml_sax_new(svg2bifs_node_start, svg2bifs_node_end, svg2bifs_text_content, converter); + converter->force_transform = 1; + + converter->svg_sg = gf_sg_new(); + gf_svg_properties_init_pointers(&converter->svg_props); + + converter->bifs_sg = gf_sg_new(); + + fprintf(stdout, "Parsing SVG File\n"); + gf_xml_sax_parse_file(converter->sax_parser, argv[1], NULL); + + fprintf(stdout, "Dumping BIFS scenegraph\n"); + tmp = strchr(argv[1], '.'); + tmp[0] = 0; + dump = gf_sm_dumper_new(converter->bifs_sg, argv[1], ' ', GF_SM_DUMP_XMTA); + tmp[0] = '.'; + + gf_sm_dump_graph(dump, 1, 0); + gf_sm_dumper_del(dump); + + gf_svg_properties_reset_pointers(&converter->svg_props); + + gf_sg_del(converter->svg_sg); +// gf_sg_del(converter->bifs_sg); + + gf_xml_sax_del(converter->sax_parser); + + free(converter); +} \ No newline at end of file diff --git a/applications/testapps/svg2bifs/svg2bifs.dsp b/applications/testapps/svg2bifs/svg2bifs.dsp new file mode 100644 index 0000000..066739a --- /dev/null +++ b/applications/testapps/svg2bifs/svg2bifs.dsp @@ -0,0 +1,90 @@ +# Microsoft Developer Studio Project File - Name="svg2bifs" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=svg2bifs - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "svg2bifs.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "svg2bifs.mak" CFG="svg2bifs - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "svg2bifs - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "svg2bifs - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "svg2bifs - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 /nologo /subsystem:console /machine:I386 /out:"../../../bin/w32_rel/svg2bifs.exe" /libpath:"../../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "svg2bifs - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /out:"../../../bin/w32_deb/svg2bifs.exe" /pdbtype:sept /libpath:"../../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "svg2bifs - Win32 Release" +# Name "svg2bifs - Win32 Debug" +# Begin Source File + +SOURCE=.\main.c +# End Source File +# End Target +# End Project diff --git a/applications/v4studio/V4CommandPanel.cpp b/applications/v4studio/V4CommandPanel.cpp new file mode 100644 index 0000000..9bd1703 --- /dev/null +++ b/applications/v4studio/V4CommandPanel.cpp @@ -0,0 +1,601 @@ +/* + V4CommandPanel.cpp + + Panel allowing to choose an action for a given frame in the time line + +*/ + +#include "safe_include.h" + +#include "V4CommandPanel.h" +#include "V4StudioFrame.h" + +#include "V4Node.h" + + + +// !! order matters !!, index will become the command id +// when command has no string, it is not available +// see v4_scenegraph_vrml.h +wxString cmdNames[] = { wxT(""), // GF_SG_SCENE_REPLACE + wxT("Replace Node"), // GF_SG_NODE_REPLACE + wxT("Replace Field"), // GF_SG_FIELD_REPLACE + wxT(""), // GF_SG_INDEXED_REPLACE + wxT(""), // GF_SG_ROUTE_REPLACE + wxT("Delete Node"), // GF_SG_NODE_DELETE + wxT(""), // GF_SG_INDEXED_DELETE + wxT(""), // GF_SG_ROUTE_DELETE + wxT("Insert Node"), // GF_SG_NODE_INSERT + wxT("") // GF_SG_INDEXED_INSERT + // TODO : to complete + }; + + +// Events table +BEGIN_EVENT_TABLE(V4CommandPanel, wxPanel) + EVT_PAINT(V4CommandPanel::OnPaint) + EVT_COMBOBOX(cmbCommandID, V4CommandPanel::OnCommandCombo) + EVT_COMBOBOX(cmbFieldID, V4CommandPanel::OnFieldCombo) + EVT_BUTTON(CreateID, V4CommandPanel::OnCreateCommand) +END_EVENT_TABLE() + + + +// Constructor +V4CommandPanel::V4CommandPanel(V4StudioFrame * parent_) + : wxPanel(parent_, -1), + // tabs + tabs(this, -1), + tabView(&tabs, -1), + tabAdd(&tabs, -1), + + // add Page + cmbCommands(&tabAdd, cmbCommandID, ""), + lblCommands(&tabAdd, -1, "Action :"), + cmbNodes(&tabAdd, -1, ""), + lblNodes(&tabAdd, -1, "Node :"), + cmbFields(&tabAdd ,cmbFieldID, ""), + lblFields(&tabAdd, -1, "Field :"), + txtField(&tabAdd, -1, ""), + btnCreate(&tabAdd, CreateID, "Create"), + + // view Page + cmbListCommands(&tabView, -1), + txtDesc(&tabView, -1), + btnDelete(&tabView, -1, "Delete") +{ + parent = parent_; + + // disposition + + // global + sizerTabs = new wxBoxSizer(wxHORIZONTAL); + sizerTabs->Add(&tabs, 1, wxEXPAND); + + tabs.AddPage(&tabView, "View"); + tabs.AddPage(&tabAdd, "Add"); + + + // Add Page + sizerAdd = new wxBoxSizer(wxVERTICAL); // up = combobox, middle = differs with the command, bottom = buttons + + sizerU = new wxBoxSizer(wxHORIZONTAL); // up + sizerM = new wxBoxSizer(wxVERTICAL); // middle + sizerD = new wxBoxSizer(wxHORIZONTAL); // bottom + + szSelectNode = new wxBoxSizer(wxHORIZONTAL); + szSelectField = new wxBoxSizer(wxHORIZONTAL); + szTxtField = new wxBoxSizer(wxHORIZONTAL); + + sizerAdd->Add(sizerU, 1, wxEXPAND); + sizerAdd->Add(sizerM, 3, wxEXPAND); + sizerAdd->Add(sizerD, 1, wxEXPAND); + + sizerU->Add(&lblCommands, 1, wxEXPAND | wxALL, 5); + sizerU->Add(&cmbCommands, 3, wxEXPAND | wxALL, 2); + + szSelectField->Add(&lblFields, 1, wxEXPAND | wxALL, 5); + szSelectField->Add(&cmbFields, 2, wxEXPAND | wxALL, 2); + + szSelectNode->Add(&lblNodes, 1, wxEXPAND | wxALL, 5); + szSelectNode->Add(&cmbNodes, 2, wxEXPAND | wxALL, 2); + + szTxtField->Add(&txtField, 1, wxEXPAND | wxALL, 5); + + sizerD->Add(&btnCreate, 1, wxEXPAND | wxALL, 3); + + tabAdd.SetSizer(sizerAdd); + + + + // View Page + sizerView = new wxBoxSizer(wxVERTICAL); + sizerUp = new wxBoxSizer(wxHORIZONTAL); + sizerDown = new wxBoxSizer(wxHORIZONTAL); + + sizerView->Add(sizerUp, 0, wxEXPAND); + sizerView->Add(sizerDown, 4, wxEXPAND); + + sizerUp->Add(&cmbListCommands, 2, wxEXPAND | wxALL, 2); + sizerUp->Add(&btnDelete, 1, wxEXPAND | wxALL, 2); + + sizerDown->Add(&txtDesc, 1, wxEXPAND); + + tabView.SetSizer(sizerView); + + + + // global + SetSizer(sizerTabs); + + //Layout(); + + ShowFieldSizer(false); + ShowNodeSizer(false); + txtField.Show(false); +} + +// Destructor +V4CommandPanel::~V4CommandPanel() { + delete szSelectNode; + delete szSelectField; + delete szTxtField; +}; + +/************************/ +/* Events */ +/************************/ + +// OnPaint event +void V4CommandPanel::OnPaint(wxPaintEvent& event) { + wxPaintDC dc(this); + + dc.BeginDrawing(); + + // draws a seperator line on the left border + int w,h; + this->GetSize(&w, &h); + dc.DrawLine(0, 0, 0, h); + + dc.EndDrawing(); +} + + +// OnCommandCombo -- command combo box selection has changed +void V4CommandPanel::OnCommandCombo(wxCommandEvent &event) { + // retrieves the command id + u32 command = (u32) cmbCommands.GetClientData(cmbCommands.GetSelection()); + + // retrieves the current node + GF_Node * node = GetCurrentNode(); + + if (! IsCommandValidForNode(command, node)) { + cmbCommands.SetSelection(-1); + command = (u32) -1; + } + + // shows or hides components + switch (command) { + case GF_SG_FIELD_REPLACE: { + ShowNodeSizer(false); + ShowFieldSizer(true); + break; + } + + case GF_SG_NODE_REPLACE: + case GF_SG_NODE_INSERT: { + ShowFieldSizer(false); + ShowNodeSizer(true); + break; + } + + case GF_SG_NODE_DELETE: + default : { + ShowFieldSizer(false); + ShowNodeSizer(false); + } + } +} + + +// OnFieldCombo -- field combo box selection has changed +void V4CommandPanel::OnFieldCombo(wxCommandEvent &event) { + + // if no selection exits + u32 sel = cmbFields.GetSelection(); + if (sel == wxNOT_FOUND) return; + + + // gets selected node in the timeline + GF_Node * node = GetCurrentNode(); + if (!node) return; + + GF_FieldInfo field; + gf_node_get_field(node, sel, &field); + + // show or hides elements depending on the type of the field we will modify + if ( (field.fieldType == GF_SG_VRML_SFNODE) || (field.fieldType == GF_SG_VRML_MFNODE) ) { + sizerM->Detach(szTxtField); + txtField.Show(false); + ShowNodeSizer(true); + } else { + sizerM->Detach(szTxtField); + sizerM->Add(szTxtField, 2, wxEXPAND); + txtField.Show(true); + } + +} + + +// OnCreateCommand -- creates a command with the option from the UI +void V4CommandPanel::OnCreateCommand(wxCommandEvent &event) { + + // verifies that there is a valid command value on cmbCommands + u32 sel = cmbCommands.GetSelection(); + if (sel == wxNOT_FOUND) return; + + // gets data from the UI + u32 tag = (u32) cmbCommands.GetClientData(sel); + GF_Node * node = GetCurrentNode(); + + // Creates the new command - REQUIRES registering the node + GF_Command * c = gf_sg_command_new(parent->GetV4SceneManager()->GetSceneGraph(), tag); + gf_node_register(node, NULL); + c->node = node; // !! the node have to be registered if command is validated + + bool succeed = false;; + + // performs various initialization depending on the command tag + switch (tag) { + case GF_SG_NODE_REPLACE: { + // TODO : not implemented yet because il would even replace the nodes in the dictionnary + break; + } + + case GF_SG_FIELD_REPLACE: { + // verifies that a valid field is selected + u32 selF = cmbFields.GetSelection(); + if (selF == wxNOT_FOUND) break; + + // gets the field + GF_FieldInfo field; + gf_node_get_field(node, (u32)cmbFields.GetClientData(selF), &field); + + GF_CommandField * cmdField = gf_sg_command_field_new(c); // if failure, will be freed with freeing the command + cmdField->fieldIndex = field.fieldIndex; + cmdField->fieldType = field.fieldType; + + // fills the GF_CommandField structures with data depending on the field type + switch (field.fieldType) { + case GF_SG_VRML_SFNODE: { + // get the node we will use as a replacement + u32 selN = cmbNodes.GetSelection(); + if (selN == wxNOT_FOUND) break; + + cmdField->new_node = (GF_Node *) cmbNodes.GetClientData(selN); + // TODO : why is the 2nd line necessary ? + cmdField->field_ptr = &cmdField->new_node; + + succeed = true; + break; + } + + case GF_SG_VRML_MFNODE: { + break; + } + + // field is not a node field + default: { + + wxString s = txtField.GetValue(); + if (s.IsEmpty()) break; + + GF_FieldInfo dummy; + dummy.far_ptr = gf_sg_vrml_field_pointer_new(field.fieldType); + dummy.fieldType = field.fieldType; + parent->GetFieldView()->SetFieldValue(dummy, &s, 0); // TODO : check the zero + + cmdField->field_ptr = dummy.far_ptr; + + succeed = true; + + break; + } + + } + + break; + } + + case GF_SG_NODE_INSERT: { + break; + } + + case GF_SG_NODE_DELETE: { + break; + } + } + + if (!succeed) { + gf_sg_command_del(c); + return; + } + + + V4SceneManager * sm = parent->GetV4SceneManager(); + + // The command is created, now searching for an AU to put it in + GF_StreamContext * ctx = sm->GetBifsStream(); + u32 count = gf_list_count (ctx->AUs); + GF_AUContext * au = NULL; + + for (u32 i = 0; i < count; i++) { + au = (GF_AUContext *) gf_list_get(ctx->AUs, i); + // V4Studio only uses timing + // TODO : FPS is defined in V4SceneGraph.h, should change and become a property somewhere + // TODO : units is a constant + if ( au->timing == sm->GetUnits() / sm->GetFrameRate() * frame ) break; + au = NULL; + } + + // creates new AU at the right time if none found + if (!au) au = gf_sm_stream_au_new(ctx, sm->GetUnits() / sm->GetFrameRate() * frame, 0, false); + + // adds command to AU and to the cell + gf_list_add(au->commands, (void *) c); + parent->GetTimeLine()->AddCommand(c); + + //gf_sg_command_apply(parent->GetV4Scene()->GetSceneGraph(), c, 0); + + +/* + // checks whether there is already a command for this object + GF_List * chain = parent->timeLine->GetCommands(); + + // if we can fuse the two commands together, we do it + if (gf_list_count(chain) > 0) { + GF_Command * old; + old = (GF_Command *) gf_list_get(chain, gf_list_count(chain) - 1); + if ( (old->tag = + } +*/ + + +} + + +/************************/ +/* Refresh */ +/************************/ + +// refresh -- refresh the node and the command combo boxes +void V4CommandPanel::Refresh(u32 frame_) { + + frame = frame_; + + // update the GUI + // cmbCommands updates in turns cmbNodes and/or cmbFields + RefreshCommands(); + + // update the list of the existing command for the cell currently selected + RefreshListCommands(); +} + + +// RefreshListCommands -- +void V4CommandPanel::RefreshListCommands() { + // retrieves pointer to the node + GF_Node * node = GetCurrentNode(); + if (!node) return; + + cmbListCommands.Clear(); + + GF_Command * c; + int i=0; + + while ( c = parent->GetTimeLine()->GetCommand(i) ) { + cmbListCommands.Append(wxString(cmdNames[c->tag]), (void *) i); + i++; + } +} + + +// RefreshCommands -- lists the available commands for the selected node +void V4CommandPanel::RefreshCommands() { + + // retrieves pointer to the node + GF_Node * node = GetCurrentNode(); + if (!node) return; + + u32 oldSel = cmbCommands.GetSelection(); + + // deletes all items from the combo box + cmbCommands.Clear(); + + // Prints the different possible commands, associates them with their id + for (int i=0; iGetV4SceneManager()->pools.pool(gf_node_get_tag(node)); + + // fills the combo box + PopulateNodes(pool, node); + + break; + } + + case GF_SG_FIELD_REPLACE: { + // checks if the field combo box has a valid selection + u32 index = cmbFields.GetSelection(); + if ( index == wxNOT_FOUND ) break; + + // get the field selected + GF_FieldInfo field; + gf_node_get_field(node, index, &field); + + // if field is not a node field then exits + if ( (field.fieldType != GF_SG_VRML_SFNODE) && (field.fieldType != GF_SG_VRML_MFNODE) ) break; + + // get the pool corresponding to that field + V4NodePool &pool = parent->GetV4SceneManager()->pools.poolFromFieldName(field.name); + + // fills the combo box + PopulateNodes(pool, node); + + break; + } + + case GF_SG_NODE_INSERT: { + // we add a child, that is to say we can add any generic node, we use "shape" to find that pool + V4NodePool &pool = parent->GetV4SceneManager()->pools.pool(TAG_MPEG4_Shape); + + // fills the combo box + PopulateNodes(pool, node); + + break; + } + + default: { + return; + } + } + +} + + +// RefreshFields -- lists the fields this nodes has +void V4CommandPanel::RefreshFields() { + + // retrieves pointer to the node + GF_Node * node = GetCurrentNode(); + if (!node) return; + + // clears the combo box + cmbFields.Clear(); + + int count = gf_node_get_field_count(node); + GF_FieldInfo field; + + for (u32 i = 0; i < count; i++) { + gf_node_get_field(node, i, &field); + cmbFields.Append(field.name, (void *) i); + } + + // TODO : are there some nodes with no fields ? + cmbFields.SetSelection(0); + + OnFieldCombo(wxCommandEvent()); + +} + + +// ShowNodeSizer -- shows or hides controls in the szSelectNode sizer +void V4CommandPanel::ShowNodeSizer(bool show) { + cmbNodes.Show(show); + lblNodes.Show(show); + + // always detaches to avoid accumulation of objects + sizerM->Detach(szSelectNode); + + if (show) { + sizerM->Add(szSelectNode, 1, wxEXPAND); + RefreshNodes(); + } + + tabAdd.Layout(); +} + + + +// ShowFieldSizer -- shows or hides controls in the szSelectField sizer +void V4CommandPanel::ShowFieldSizer(bool show) { + cmbFields.Show(show); + lblFields.Show(show); + txtField.Show(false); // always hide, RefreshField may bring it back + + // always detaches to avoid accumulation of objects + sizerM->Detach(szSelectField); + sizerM->Detach(szTxtField); + + if (show) { + // here order matters, RefreshFields may add szSelectNode, so we have to had ourselves before that + sizerM->Add(szSelectField, 1, wxEXPAND); + RefreshFields(); + } + + tabAdd.Layout(); +} + + +/************************/ +/* Utils */ +/************************/ + +// IsCommandValidForNode -- tells if we can apply this command to that node +bool V4CommandPanel::IsCommandValidForNode(u32 command, GF_Node * node) { + switch (command) { + case GF_SG_FIELD_REPLACE: + case GF_SG_NODE_DELETE: + case GF_SG_NODE_REPLACE: { + return true; + break; + } + + case GF_SG_NODE_INSERT: { + // to insert a child, node must have a GF_SG_VRML_MFNODE field, other children are not "inserted" + return parent->GetV4SceneManager()->pools.NodeHasField(node, GF_SG_VRML_MFNODE); + break; + } + + } + + return false; +} + + +// GetCurrentNode -- returns the node currently selected in the timeline +GF_Node * V4CommandPanel::GetCurrentNode() { + u32 id = parent->GetTimeLine()->GetSelectedID(); + if (!id) return NULL; + + return gf_sg_find_node(parent->GetV4SceneManager()->GetSceneGraph(), id); +} + + + +// PopulateNodes -- fills the cmdNodes with nodes from pool, do not add the specified node if found +void V4CommandPanel::PopulateNodes(V4NodePool& pool, GF_Node * node) { + for (u32 i = 0; i != pool.GetNodesCount(); i++) + if (pool.at(i).GetNode() != node) + cmbNodes.Append(pool.at(i).GetName(), (void *) pool.at(i).GetNode()); +} \ No newline at end of file diff --git a/applications/v4studio/V4CommandPanel.h b/applications/v4studio/V4CommandPanel.h new file mode 100644 index 0000000..1f169e1 --- /dev/null +++ b/applications/v4studio/V4CommandPanel.h @@ -0,0 +1,103 @@ +/* + V4CommandPanel.h + + Header file for CommandPanel, the control alloing to create commands from the timeline +*/ + +#ifndef _V4CommandPanel_h_ +#define _V4CommandPanel_h_ + +#include "safe_include.h" +#include +#include + +// list of all commands +#include + + +#define cmbCommandID wxID_HIGHEST+1 +#define cmbFieldID wxID_HIGHEST+2 +#define CreateID wxID_HIGHEST+3 + + +class V4StudioFrame; +class V4NodePool; + + +class V4CommandPanel : public wxPanel { + public: + // Constructor Destructor + V4CommandPanel(V4StudioFrame * parent); + ~V4CommandPanel(); + + // Update + void Refresh(u32 frame); + + private: + + u32 frame; + + // controls + + wxNotebook tabs; + wxPanel tabView; + wxPanel tabAdd; + + // add page + wxComboBox cmbCommands; + wxStaticText lblCommands; + + wxComboBox cmbNodes; + wxStaticText lblNodes; + + wxComboBox cmbFields; + wxStaticText lblFields; + + wxButton btnCreate; + + wxTextCtrl txtField; + + + // view page + wxComboBox cmbListCommands; + wxTextCtrl txtDesc; + wxButton btnDelete; + + //sizers + // global + wxBoxSizer * sizerTabs; + // Add page + wxBoxSizer * sizerAdd, * sizerU, * sizerM, * sizerD, * szSelectNode, * szSelectField, * szTxtField; + // View page + wxBoxSizer * sizerView, * sizerUp, * sizerDown; + + + // UI -- show or hides part of the control + void ShowNodeSizer(bool show); + void ShowFieldSizer(bool show); + + // Refreshing of various comboboxes + void RefreshCommands(); + void RefreshNodes(); + void RefreshFields(); + void RefreshListCommands(); + + // fills the cmdNodes with nodes from pool, do not add the specified node if found + void PopulateNodes(V4NodePool& pool, GF_Node * node); + + // events + DECLARE_EVENT_TABLE() + void OnPaint(wxPaintEvent& event); + void OnCommandCombo(wxCommandEvent &event); + void OnFieldCombo(wxCommandEvent &event); + void OnCreateCommand(wxCommandEvent &event); + + bool IsCommandValidForNode(u32 command, GF_Node * node); // tells if we can apply this command to that node + GF_Node * GetCurrentNode(); // returns the node currently selected in the timeline + + // All the component of the V4Studio Main Frame have a pointer to that Main Frame, called parent. + V4StudioFrame * parent; + +}; + +#endif \ No newline at end of file diff --git a/applications/v4studio/V4FieldList.cpp b/applications/v4studio/V4FieldList.cpp new file mode 100644 index 0000000..952d953 --- /dev/null +++ b/applications/v4studio/V4FieldList.cpp @@ -0,0 +1,452 @@ +#include "safe_include.h" + +// For compilers that supports precompilation , includes "wx/wx.h" +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include +#include + +#include "V4FieldList.h" + +#include "V4StudioFrame.h" + +BEGIN_EVENT_TABLE(V4FieldList, wxGrid) + EVT_GRID_CELL_CHANGE(V4FieldList::OnCellChanged) + EVT_GRID_CELL_LEFT_CLICK(V4FieldList::OnCellLeftClick) + EVT_GRID_CELL_RIGHT_CLICK(V4FieldList::OnCellRightClick) + EVT_GRID_CELL_LEFT_DCLICK(V4FieldList::OnCellLeftDClick) +END_EVENT_TABLE() + +class Position { +public : + Position(u32 fi, s32 fp) : fieldIndex(fi), fieldPosition(fp) {} + u32 fieldIndex; + s32 fieldPosition; +}; + +V4FieldList::V4FieldList(wxWindow *parent_, wxSize size) : wxGrid(parent_, -1, wxDefaultPosition, size) { + positions = gf_list_new(); + CreateGrid(0,0); + SetRowLabelSize(0); + SetColLabelSize(20); + SetColLabelValue(0, wxString(" ")); + SetColLabelValue(1, wxString("Field Name")); + SetColLabelValue(2, wxString("Field Value")); + InsertCols(0,3); + AutoSizeColumns(true); + EnableGridLines(true); + + parent = (V4StudioFrame *) parent_; +} + +V4FieldList::~V4FieldList() +{ + s32 i; + for (i=gf_list_count(positions)-1; i>=0; i--) { + Position *p = (Position *)gf_list_get(positions, i); + delete p; + p = NULL; + gf_list_rem(positions, i); + } + gf_list_del(positions); +} + +void V4FieldList::Create() +{ + GF_Node *node = m_pNode; + + if (!node) return; + + DeleteCols(0,3); + u32 nbRows = GetNumberRows(); + if (nbRows) DeleteRows(0,nbRows); + InsertCols(0,3); + + s32 i; + for (i=gf_list_count(positions)-1; i>=0; i--) { + Position *p = (Position *)gf_list_get(positions, i); + delete p; + p = NULL; + gf_list_rem(positions, i); + } + + u32 pos = 0; // counts rows added + + // adds a row to modify DefName + InsertRows(pos, 1); + SetCellValue(pos, 1, wxString("Name")); + const char * defName = gf_node_get_name(node); + if (defName) SetCellValue(pos, 2, wxString(defName)); + gf_list_add(positions, new Position(-1, -1)); + // enables modifying the DefName + for (i=0; i<2; i++) SetReadOnly(pos, i, TRUE); + SetReadOnly(pos, 2, FALSE); + pos++; + + // adds all fields + GF_FieldInfo field; + u32 count = gf_node_get_field_count(node); + u32 j; + + for (j=0; jc_str(), "%f %f %f", &(((SFColor *)ptr)->red), &(((SFColor *)ptr)->green), &(((SFColor *)ptr)->blue)); + break; + case GF_SG_VRML_SFVEC2F: + sscanf(value->c_str(), "%f %f", &(((SFVec2f *)ptr)->x), &(((SFVec2f *)ptr)->y)); + break; + case GF_SG_VRML_SFFLOAT: + sscanf(value->c_str(), "%f", (SFFloat *)ptr); + break; + case GF_SG_VRML_SFINT32: + sscanf(value->c_str(), "%d", (SFInt32 *)ptr); + break; + case GF_SG_VRML_SFBOOL: + if (!stricmp(value->c_str(), "true")) *((SFBool *)ptr) = 1; + else *((SFBool *)ptr) = 0; + break; + case GF_SG_VRML_SFSTRING: + if (((SFString *)ptr)->buffer) free(((SFString *)ptr)->buffer); + ((SFString *)ptr)->buffer = strdup(value->c_str()); + break; + default: + break; + } +} + +void V4FieldList::GetFieldValue(GF_FieldInfo f, wxString *s, int pos) +{ + void *ptr = NULL; + s32 type = -1; + if (gf_sg_vrml_is_sf_field(f.fieldType)) { + type = f.fieldType; + ptr = f.far_ptr; + } else { + if (pos < 0) return; + gf_sg_vrml_mf_get_item(f.far_ptr, f.fieldType, &ptr, pos); + type = gf_sg_vrml_get_sf_type(f.fieldType); + } + switch (type) { + case GF_SG_VRML_SFBOOL: + if (*(SFBool *)ptr == 0) s->Printf("false"); + else s->Printf("true"); + break; + case GF_SG_VRML_SFINT32: + s->Printf("%d", *(SFInt32 *)ptr); + break; + case GF_SG_VRML_SFCOLOR: + s->Printf("%.2f %.2f %.2f", ((SFColor *)ptr)->red, ((SFColor *)ptr)->green, ((SFColor *)ptr)->blue); + break; + case GF_SG_VRML_SFVEC2F: + s->Printf("%.2f %.2f", ((SFVec2f *)ptr)->x, ((SFVec2f *)ptr)->y); + break; + case GF_SG_VRML_SFFLOAT: + s->Printf("%.2f", *(SFFloat *)ptr); + break; + case GF_SG_VRML_SFSTRING: + s->Printf("%s", ((SFString *)ptr)->buffer); + break; + case GF_SG_VRML_SFSCRIPT: + s->Printf("%s", ((SFScript *)ptr)->script_text); + break; + default: + break; + } + gf_node_dirty_set(m_pNode, 0, 1); +} + +// OnCellChanged -- user has validated the changes made to a field, updates the node with the new value +void V4FieldList::OnCellChanged(wxGridEvent &evt) +{ + u32 row; + GF_FieldInfo field; + wxString value; + + if (!m_pNode || evt.GetCol() == 0) { + evt.Skip(); + return; + } + + row = evt.GetRow(); + value = GetCellValue(row, 2); + Position *pSelected = (Position *)gf_list_get(positions, row); + if (!pSelected) { + evt.Skip(); + return; + } + + // defName requires special treatment + if (pSelected->fieldIndex == (u32) -1) { + u32 id = gf_node_get_id(m_pNode); + if (!id) id = gf_sg_get_next_available_node_id(parent->GetV4SceneManager()->GetSceneGraph()); + gf_node_set_id(m_pNode, id, value.c_str()); + parent->GetV4SceneManager()->pools.Add(m_pNode); + } else { + // modifies any kind of field + gf_node_get_field(m_pNode, pSelected->fieldIndex, &field); + if (gf_sg_vrml_is_sf_field(field.fieldType)) { + SetFieldValue(field, &value, -1); + } else { + if (pSelected->fieldPosition >= 0) { + SetFieldValue(field, &value, pSelected->fieldPosition); + } else { + GenMFField *mf = (GenMFField *)field.far_ptr; + void *ptr; + gf_sg_vrml_mf_insert(mf, field.fieldType, &ptr, mf->count); + SetFieldValue(field, &value, mf->count-1); + SetCellValue(row, 2, ""); + wxString sign = GetCellValue(row, 0); + switch (sign.GetChar(0)) { + case '-': + { + u32 insertPosition = row+mf->count+1; + InsertRows(insertPosition-1, 1); + gf_list_insert(positions, new Position(pSelected->fieldIndex, mf->count-1), insertPosition); + wxString tmp; + tmp << '['; tmp << (mf->count-1); tmp << ']'; + SetCellValue(insertPosition-1, 1, tmp); + wxString buf; + GetFieldValue(field, &buf, mf->count-1); + SetCellValue(insertPosition-1, 2, buf); + } + break; + case '+': + break; + case ' ': + SetCellValue(row, 0, "+"); + break; + } + } + } + } + gf_node_dirty_set(m_pNode, 0, 1); + V4StudioFrame *parent = (V4StudioFrame *)this->GetParent(); + parent->Update(); +} + +void V4FieldList::SetLog(wxString s) +{ + V4StudioFrame *parent = (V4StudioFrame *)this->GetParent(); + parent->GetStatusBar()->SetStatusText(s); +} + +void V4FieldList::SetCellFieldValue(GF_FieldInfo field, u32 pos) +{ + wxString buf; + if (gf_sg_vrml_is_sf_field(field.fieldType)) { + SetCellValue(pos,1,wxString(field.name)); + GetFieldValue(field, &buf, -1); + SetCellValue(pos,2,buf); + } else { + GenMFField *mf = (GenMFField *)field.far_ptr; + if (mf->count > 0 ) { + SetCellValue(pos,0,"+"); + } else { + SetCellValue(pos,0," "); + } + SetCellValue(pos,1,field.name); + } +} + +// OnCellLeftClick -- draws columns for multivalued fields +void V4FieldList::OnCellLeftClick(wxGridEvent &evt) +{ + u32 col = evt.GetCol(); + u32 row = evt.GetRow(); + GF_FieldInfo field; + GF_Node *node = m_pNode; + + if (col != 0) { + evt.Skip(); + return; + } + + Position *pSelected = (Position *)gf_list_get(positions, row); + if (!pSelected) { + evt.Skip(); + return; + } + + gf_node_get_field(node, pSelected->fieldIndex, &field); + if (gf_sg_vrml_is_sf_field(field.fieldType) || field.fieldType == GF_SG_VRML_MFNODE) { + evt.Skip(); + return; + } + + wxString value = GetCellValue(row, col); + char c = value.GetChar(0); + GenMFField *mf = (GenMFField *)field.far_ptr; + if (c == ' ') { + evt.Skip(); + return; + } else if (c == '+') { + value.SetChar(0, '-'); + SetCellValue(row,0,value); + InsertRows(row+1, mf->count); + for (u32 j = 0; jcount; j++) { + Position *p = new Position(pSelected->fieldIndex, j); + gf_list_insert(positions, p, row+1+j); + wxString tmp; + tmp << '['; tmp << j; tmp << ']'; + SetCellValue(row+1+j, 1, tmp); + wxString buf; + GetFieldValue(field, &buf, j); + SetCellValue(row+1+j, 2, buf); + } + } else if (c == '-') { + value.SetChar(0, '+'); + SetCellValue(row,0,value); + DeleteRows(row+1, mf->count); + for (s32 j = mf->count-1; j>=0; j--) { + Position *p = (Position *)gf_list_get(positions,row+1+j); + delete p; + p = NULL; + gf_list_rem(positions, row+1+j); + } + } + evt.Skip(); +} + +void V4FieldList::OnCellRightClick(wxGridEvent &evt) +{ + int row = evt.GetRow(); + GF_FieldInfo field; + GF_Node *node = m_pNode; + + Position *pSelected = (Position *)gf_list_get(positions,row); + if (!pSelected) { + evt.Skip(); + return; + } + + gf_node_get_field(node, pSelected->fieldIndex, &field); + if (gf_sg_vrml_is_sf_field(field.fieldType)) { + evt.Skip(); + return; + } + + GenMFField *mf = (GenMFField *)field.far_ptr; + if (pSelected->fieldPosition == -1) { + evt.Skip(); + return; + } + + InsertRows(row, 1); + gf_list_insert(positions, new Position(pSelected->fieldIndex, pSelected->fieldPosition), row); + void *ptr; + gf_sg_vrml_mf_insert(mf, field.fieldType, &ptr, pSelected->fieldPosition); + SetCellValue(row, 2, ""); + for (u32 i=pSelected->fieldPosition; icount; i++) { + wxString tmp; + tmp << '['; tmp << i; tmp << ']'; + SetCellValue(row+i, 1, tmp); + } +} + +// OnCellLeftDClick -- Edit a given field +void V4FieldList::OnCellLeftDClick(wxGridEvent &evt) +{ + int row = evt.GetRow(); + GF_FieldInfo field; + GF_Node *node = m_pNode; + + Position *pSelected = (Position *)gf_list_get(positions,row); + if (!pSelected) { + evt.Skip(); + return; + } + + // editing the DEF name is NOT modifying a field + // we just need to modify the string itself + if (pSelected->fieldIndex == (u32) -1) { + evt.Skip(); + return; + } + + gf_node_get_field(node, pSelected->fieldIndex, &field); + u32 type; + void *ptr; + if (gf_sg_vrml_is_sf_field(field.fieldType)) { + type = field.fieldType; + ptr = field.far_ptr; + } else { + if (pSelected->fieldPosition < 0) { + evt.Skip(); + return; + } + gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &ptr, pSelected->fieldPosition); + type = gf_sg_vrml_get_sf_type(field.fieldType); + } + + if (type== GF_SG_VRML_SFCOLOR) { + wxColourDialog cd(this); + if (cd.ShowModal() == wxID_OK) + { + wxColourData retData = cd.GetColourData(); + wxColour col = retData.GetColour(); + ((SFColor *)ptr)->red = col.Red()/255.0f; + ((SFColor *)ptr)->green = col.Green()/255.0f; + ((SFColor *)ptr)->blue = col.Blue()/255.0f; + } else { + return; + } + } else if (!stricmp(field.name,"family")) { + wxFontDialog fd(this); + if (fd.ShowModal() == wxID_OK) { + wxFontData retData = fd.GetFontData(); + wxFont font = retData.GetChosenFont(); + wxString name = font.GetFaceName(); + if (((SFString *)ptr)->buffer) free(((SFString *)ptr)->buffer); + ((SFString *)ptr)->buffer = strdup(name.c_str()); + } else { + return; + } + + } else { + evt.Skip(); + return; + } + V4StudioFrame *parent = (V4StudioFrame *)this->GetParent(); + parent->Update(); +} \ No newline at end of file diff --git a/applications/v4studio/V4FieldList.h b/applications/v4studio/V4FieldList.h new file mode 100644 index 0000000..d4142c5 --- /dev/null +++ b/applications/v4studio/V4FieldList.h @@ -0,0 +1,47 @@ +#ifndef _V4_FIELD_LIST_H +#define _V4_FIELD_LIST_H + +#include "safe_include.h" +#include + +#include + + +class V4StudioFrame; + + +class V4FieldList: public wxGrid { + +public: + V4FieldList(wxWindow *parent, wxSize size); + ~V4FieldList(); + + void SetNode(GF_Node *node) { m_pNode = node; } + + void Create(); + void GetFieldValue(GF_FieldInfo f, wxString *s, int pos); + void SetFieldValue(GF_FieldInfo f, wxString *s, int pos); + + void OnCellChanged(wxGridEvent &evt); + void OnCellLeftClick(wxGridEvent &evt); + void OnCellRightClick(wxGridEvent &evt); + void OnCellLeftDClick(wxGridEvent &evt); +protected: + DECLARE_EVENT_TABLE() + +private: + void SetLog(wxString s); + + void InsertOneFieldRow(u32 position, GF_FieldInfo field); + + void SetCellFieldValue(GF_FieldInfo field, u32 pos); + + GF_Node * m_pNode; + GF_List *positions; + + // All the component of the V4Studio Main Frame have a pointer to that Main Frame, called parent. + V4StudioFrame * parent; + +}; + +#endif diff --git a/applications/v4studio/V4Node.cpp b/applications/v4studio/V4Node.cpp new file mode 100644 index 0000000..75ca8f9 --- /dev/null +++ b/applications/v4studio/V4Node.cpp @@ -0,0 +1,57 @@ +/* + V4Node.cpp + + Implements V4Node class +*/ + +#include "V4Node.h" +#include + + +// Constructor +V4Node::V4Node(const u32 _NodeID, GF_Node * _node) : NodeID(_NodeID), node(_node) { + _ASSERT(_node); // no NULL node +} + + +// GetID -- +u32 V4Node::GetID() const { + return NodeID; +} + + +// GetNode -- +GF_Node * V4Node::GetNode() const { + return node; +} + + +// IsAliveAt -- check all aparitions +bool V4Node::IsAliveAt(const u32 frame) { + std::list::iterator i; + + for (i = lifeSpan.begin(); i != lifeSpan.end(); i++) + if ( ((*i).from >= frame) && ((*i).to <= frame) ) return true; + + return false; +} + + +// Appear +void V4Node::Appear(const u32 startFrame, const u32 stopFrame) { + // TODO : + return; +} + + +// Disappear +void V4Node::Disappear(const u32 startFrame, const u32 stopFrame) { + // TODO : + return; +} + + +// GetName -- +wxString V4Node::GetName() { + return wxString(gf_node_get_name(node)); +} \ No newline at end of file diff --git a/applications/v4studio/V4Node.h b/applications/v4studio/V4Node.h new file mode 100644 index 0000000..815bbf2 --- /dev/null +++ b/applications/v4studio/V4Node.h @@ -0,0 +1,61 @@ +/* + V4Node.h + + Represents a node with additionnal attributes for V4Studio, such as lifespan +*/ + +#ifndef _V4Node_ +#define _V4Node_ + +#include "safe_include.h" +#include // GF_Node structure + +#include + +#include + + +struct segment { + u32 from, to; +}; + + +class V4Node { + public: + // constructor destructor + V4Node(const u32 NodeID, GF_Node * node=NULL); // node may be modified, don't use const GF_Node * + + // accesses to members + u32 GetID() const; + GF_Node * GetNode() const; + + + // computes whether the node is alive at the given frame + bool IsAliveAt(const u32 frame); + + // Add an apparition, inclusive + void Appear(const u32 startFrame, const u32 stopFrame); + + // Remove frames from lifeSpan, inclusive + void Disappear(const u32 startFrame, const u32 stopFrame); + + + // (to save workspace) tells whether the node should appear on the timeline + bool isOnTimeLine; + + // retrieves the node _DEF_ name (node has a Node ID) + wxString GetName(); + + + + private: + // various accesses to the node + u32 NodeID; + GF_Node * node; + + // timeLine + std::list lifeSpan; // each element of the list is one use of this node, first value is start frame, second is end frame, inclusive + +}; + +#endif \ No newline at end of file diff --git a/applications/v4studio/V4NodePool.cpp b/applications/v4studio/V4NodePool.cpp new file mode 100644 index 0000000..f78f6fc --- /dev/null +++ b/applications/v4studio/V4NodePool.cpp @@ -0,0 +1,76 @@ +/* + V4NodePool.cpp + +*/ +#include "safe_include.h" +#include + +#include "V4NodePool.h" + + +// FindNode -- search for a node with the specified NodeID or pointer +u32 V4NodePool::FindNode(const u32 NodeID, const GF_Node * node) { + + if ( (NodeID == 0) && (node == NULL) ) return (u32) -1; + + for (int i = 0; i < Nodes.size(); i++) + if ( (NodeID == Nodes.at(i).GetID()) || (node == Nodes.at(i).GetNode()) ) return i; + + return (u32) -1; +} + + +// AddNode -- +V4Node& V4NodePool::AddNode(GF_Node * _node) { + // checks that the node is not already in the pool + u32 id = gf_node_get_id(_node); + u32 i = FindNode(id); + if (i != (u32) -1) return Nodes.at(i); + + // add a new entry into the pool + V4Node node(id, _node); + Nodes.push_back(node); + count++; + return Nodes.back(); +} + + +// DeleteNode -- Deletes a node from the pool +void V4NodePool::DeleteNode(const u32 _NodeID, const GF_Node * _node) { + u32 i = FindNode(_NodeID, _node); + std::vector::iterator iter = Nodes.begin(); + + for (u32 j=0; j +#include "V4Node.h" + +using namespace std; + +class V4NodePool { + + public: + + // Constructor + V4NodePool() { count = 0; } + + // Adds a node to the pool + V4Node& AddNode(GF_Node * node=NULL); // node may be modified, don't use const GF_Node * + + // Deletes a node from the pool + void DeleteNode(const u32 NodeID, const GF_Node * node=NULL); + + // retrieves a node from the pool + V4Node& GetV4Node(const u32 NodeID, const GF_Node * node=NULL); + + // returns the numbre of nodes + u32 GetNodesCount() const; + + // returns the nth element in the vector + V4Node& at(u32 n); + + // clear the pool + void Clear(); + + // returns count + u32 GetCount() const; + + + private: + vector Nodes; + + // find the V4Node with the specified attributes + u32 FindNode(const u32 NodeID, const GF_Node * node=NULL); + + // counts the nodes added to that pool (used in autonaming nodes) + u32 count; + +}; + +#endif \ No newline at end of file diff --git a/applications/v4studio/V4NodePools.cpp b/applications/v4studio/V4NodePools.cpp new file mode 100644 index 0000000..2168dba --- /dev/null +++ b/applications/v4studio/V4NodePools.cpp @@ -0,0 +1,121 @@ +/* + V4NodePools.cpp + + Implements V4NodePools class + +*/ + +#include "V4NodePools.h" +#include "V4NodePool.h" + +V4NodePool dummy; + +// Constructor +V4NodePools::V4NodePools() { + pools.resize(DICT_LAST); + unPooled = 0; +} + + +// pool -- returns the pool of object for the given tag +V4NodePool& V4NodePools::pool(const u32 tag) +{ + u32 n = poolN(tag); + if ((u32) -1 == n) return dummy; + return pools.at(n); +} + + +// pool -- returns the NUMBER of the pool for the given tag +u32 V4NodePools::poolN(const u32 tag) { + + switch (tag) { + + // objects that can be children of a switch + case TAG_MPEG4_OrderedGroup: + case TAG_MPEG4_Shape: { + return DICT_GEN; + break; + } + + // objects that can be a geometry + case TAG_MPEG4_Rectangle: + case TAG_MPEG4_IndexedFaceSet2D: + case TAG_MPEG4_Circle: { + return DICT_GEOMETRY; + break; + } + + // objects that can be an appearance + case TAG_MPEG4_Appearance: { + return DICT_APPEARANCE; + break; + } + + // objects that can be a texture + case TAG_MPEG4_MovieTexture: + case TAG_MPEG4_ImageTexture: { + return DICT_TEXTURE; + break; + } + + // objects that can be a material + case TAG_MPEG4_Material2D: { + return DICT_MATERIAL; + break; + } + + } + + return (u32) -1; +} + + +// poolNFromFieldName -- Get the correct pool from a field name +V4NodePool& V4NodePools::poolFromFieldName(const char * name) { + + if (!strcmp("appearance", name)) return pools.at(DICT_APPEARANCE); + if (!strcmp("geometry", name)) return pools.at(DICT_GEOMETRY); + if (!strcmp("material", name)) return pools.at(DICT_MATERIAL); + if (!strcmp("texture", name)) return pools.at(DICT_TEXTURE); + + // throws an exception if not found + return dummy; +} + + +// Clear -- clears all the pool +void V4NodePools::Clear() { + for (u32 i = 0; i < pools.size(); i++) + pools.at(i).Clear(); +} + + +// Add -- Adds a node to the correct pool +void V4NodePools::Add(GF_Node * node) { + u32 tag = gf_node_get_tag(node); + if (poolN(tag) == (u32) -1) return; + pool(tag).AddNode(node); +} + + +// NodeHasField -- tells if nodes as field of the given type +bool V4NodePools::NodeHasField(GF_Node * node, u32 fieldType) { + u32 count = gf_node_get_field_count(node); + GF_FieldInfo field; + + // cycles through all fields and checks field type + for (u32 i = 0; i < count; i++) { + gf_node_get_field(node, i, &field); + if (field.fieldType == fieldType) return true; + } + + return false; +} + + +// returns the total number of nodes created for that type of node +u32 V4NodePools::GetCount(const u32 tag) { + if (poolN(tag) == (u32) -1) return unPooled++; + return pool(tag).GetCount(); +} \ No newline at end of file diff --git a/applications/v4studio/V4NodePools.h b/applications/v4studio/V4NodePools.h new file mode 100644 index 0000000..a9f0107 --- /dev/null +++ b/applications/v4studio/V4NodePools.h @@ -0,0 +1,59 @@ +/* + V4NodePools + + List all the Node pools for each possible node tag + +*/ + +#include "safe_include.h" +#include +#include + +#include + +#include "V4NodePool.h" + + +using namespace std; + + +enum { + DICT_GEN = 0, + DICT_GEOMETRY, + DICT_APPEARANCE, + DICT_TEXTURE, + DICT_MATERIAL, + + DICT_LAST // last element, gives the actual number of pool +}; + + +class V4NodePools { + + public: + // Constructor + V4NodePools(); + + // gets the correct pool from the given tag + V4NodePool& pool(const u32 tag); + V4NodePool& poolFromFieldName(const char * name); + u32 poolN(const u32 tag); + + // clears all the pools + void Clear(); + + // Adds a node to the correct pool + void Add(GF_Node * node); + + // tells if nodes as field of the given type + bool NodeHasField(GF_Node * node, u32 fieldType); + + // returns the total number of nodes created for that type of node + u32 GetCount(const u32 tag); + + + private: + vector pools; + unsigned int unPooled; + +}; \ No newline at end of file diff --git a/applications/v4studio/V4SceneGraph.cpp b/applications/v4studio/V4SceneGraph.cpp new file mode 100644 index 0000000..03dd1fd --- /dev/null +++ b/applications/v4studio/V4SceneGraph.cpp @@ -0,0 +1,304 @@ +#include "V4SceneGraph.h" +#include "V4StudioFrame.h" + +#include +#include +#include + +#include "V4Service.h" + +V4SceneGraph::V4SceneGraph(V4StudioFrame *parent) : frame(parent) +{ + m_pSm = NULL; + m_pSg = NULL; + m_pSr = NULL; + m_pIs = NULL; + m_pOriginal_mp4 = NULL; + m_bEncodeNames = 0; + m_term = parent->GetGPACPanel()->m_term; +} + +V4SceneGraph::~V4SceneGraph() +{ + // TODO : pquoi fallait il commenter cette ligne ? + //if (m_pSm) gf_sm_del(m_pSm); + if (m_pOriginal_mp4) free(m_pOriginal_mp4); + if (m_pService) delete m_pService; +} + +void V4SceneGraph::SetSceneSize(int w, int h) +{ + gf_sg_set_scene_size_info(m_pSg, w,h, 1); + /*reassign scene graph to update scene size in renderer*/ + gf_sr_set_scene(m_pSr, m_pSg); +} + +void V4SceneGraph::GetSceneSize(wxSize &size) +{ + gf_sg_get_scene_size_info(m_pSg, (u32 *)&(size.x), (u32 *)&(size.y)); +} + +GF_Node *V4SceneGraph::SetTopNode(u32 tag) +{ + GF_Node * root = NewNode(tag); + gf_sg_set_root_node(m_pSg, root); + return (GF_Node *)root; +} + + +GF_Node *V4SceneGraph::NewNode(u32 tag) +{ + GF_Node *n = gf_node_new(m_pSg, tag); + if (!n) return NULL; + gf_node_init(n); + return n; +} + +void V4SceneGraph::SaveFile(const char *path) +{ + char rad_name[5000]; + strcpy(rad_name, "dump"); + gf_sm_dump(m_pSm, rad_name, 0); + GF_ISOFile *mp4 = gf_isom_open(path, GF_ISOM_WRITE_EDIT, NULL); + m_pSm->max_node_id = gf_sg_get_max_node_id(m_pSm->scene_graph); + gf_sm_encode_to_file(m_pSm, mp4, "c:\\log.txt", NULL, GF_SM_LOAD_MPEG4_STRICT, 0); + gf_isom_close(mp4); +} + +void V4SceneGraph::LoadNew() +{ + if (m_pSm) { + gf_sr_set_scene(m_pSr, NULL); + gf_sm_del(m_pSm); + m_pSm = NULL; + gf_sg_del(m_pSg); + m_pSg = NULL; + } + if (m_pOriginal_mp4) free(m_pOriginal_mp4); + m_pOriginal_mp4 = NULL; + + m_pIs = gf_is_new(NULL); + m_pSg = m_pIs->graph; + gf_sg_set_init_callback(m_pSg, v4s_node_init, this); + gf_sr_set_scene(m_pSr, m_pSg); + + m_pSm = gf_sm_new(m_pSg); + /* Create a BIFS stream with one AU with on ReplaceScene */ + GF_StreamContext *sc = gf_sm_stream_new(m_pSm, 1, GF_STREAM_SCENE, 0); + GF_AUContext *au = gf_sm_stream_au_new(sc, 0, 0, 1); + GF_Command *command = gf_sg_command_new(m_pSg, GF_SG_SCENE_REPLACE); + gf_list_add(au->commands, command); + + // reinitializes the node pool + frame->pools.Clear(); +} + +void V4SceneGraph::LoadFileOld(const char *path) +{ + m_pService = new V4Service(path); + gf_term_attach_service(m_term, m_pService->m_pNetClient); + m_pIs = m_term->root_scene; + m_pSg = m_pIs->graph; + gf_term_play_from_time(m_term, 0); + while(!m_pIs->graph_attached) {} + // CreateDictionnary crashes because the root node in m_pSg is not set. + CreateDictionnary(); +} + +void V4SceneGraph::LoadFile(const char *path) +{ + GF_SceneLoader load; + if (m_pSm) { + gf_sr_set_scene(m_pSr, NULL); + gf_sm_del(m_pSm); + gf_sg_del(m_pSg); + } + + // initializes a new scene + // We need an GF_InlineScene, a SceneManager and an GF_ObjectManager + m_pIs = gf_is_new(NULL); + m_pSg = m_pIs->graph; + m_pSm = gf_sm_new(m_pSg); + + m_pIs->root_od = gf_odm_new(); + + m_pIs->root_od->parentscene = NULL; + m_pIs->root_od->subscene = m_pIs; + m_pIs->root_od->term = m_term; + + m_term->root_scene = m_pIs; + + // TODO : what's the use of this ? + if (m_pOriginal_mp4) free(m_pOriginal_mp4); + m_pOriginal_mp4 = NULL; + + /* Loading of a file (BT, MP4 ...) and modification of the SceneManager */ + memset(&load, 0, sizeof(GF_SceneLoader)); + load.fileName = path; + load.ctx = m_pSm; + load.cbk = this; + if (strstr(path, ".mp4") || strstr(path, ".MP4") ) load.isom = gf_isom_open(path, GF_ISOM_OPEN_READ, NULL); + gf_sm_load_init(&load); + gf_sm_load_run(&load); + gf_sm_load_done(&load); + if (load.isom) gf_isom_delete(load.isom); + + /* SceneManager should be initialized and filled correctly */ + + gf_sg_set_scene_size_info(m_pSg, m_pSm->scene_width, m_pSm->scene_height, m_pSm->is_pixel_metrics); + + // TODO : replace with GetBifsStream + GF_StreamContext *sc = (GF_StreamContext *) gf_list_get(m_pSm->streams,0); + + if (sc->streamType == 3) { + GF_AUContext *au = (GF_AUContext *) gf_list_get(sc->AUs,0); + GF_Command *c = (GF_Command *) gf_list_get(au->commands,0); + gf_sg_command_apply(m_pSg, c, 0); + /* This is a patch to solve the save pb: + When ApplyCommand is made on a Scene Replace Command + The command node is set to NULL + When we save a BIFS stream whose first command is of this kind, + the file saver thinks the bifs commands should come from an NHNT file + This is a temporary patch */ + if (c->tag == GF_SG_SCENE_REPLACE) { c->node = m_pSg->RootNode; } + } + gf_sr_set_scene(m_pSr, m_pSg); + + // retrieves all the node from the tree and adds them to the node pool + GF_Node * root = gf_sg_get_root_node(m_pSg); + CreateDictionnary(); +} + +GF_Node *V4SceneGraph::CopyNode(GF_Node *node, GF_Node *parent, bool copy) +{ + if (copy) return CloneNodeForEditing(m_pSg, node); + u32 nodeID = gf_node_get_id(node); + if (!nodeID) { + nodeID = gf_sg_get_next_available_node_id(m_pSg); + gf_node_set_id(node, nodeID, NULL); + gf_node_register(node, parent); + } + return node; +} + +void V4SceneGraph::LoadCommand(u32 commandNumber) +{ + GF_StreamContext *sc = NULL; + for (u32 i=0; istreams); i++) { + sc = (GF_StreamContext *) gf_list_get(m_pSm->streams, i); + if (sc->streamType == GF_STREAM_SCENE) break; + sc = NULL; + } + assert(sc); + GF_AUContext *au = (GF_AUContext *) gf_list_get(sc->AUs,0); + if (commandNumber < gf_list_count(au->commands)) { + for (u32 j=0; j<=commandNumber; j++) { + GF_Command *c = (GF_Command *) gf_list_get(au->commands, j); + gf_sg_command_apply(m_pSg, c, 0); + } + } +} + + + +extern "C" { + +GF_Node *CloneNodeForEditing(GF_SceneGraph *inScene, GF_Node *orig) //, GF_Node *cloned_parent) +{ + u32 i, j, count; + GF_Node *node, *child, *tmp; + GF_List *list, *list2; + GF_FieldInfo field_orig, field; + + /*this is not a mistake*/ + if (!orig) return NULL; + + /*check for DEF/USE + if (orig->sgprivate->NodeID) { + node = gf_sg_find_node(inScene, orig->sgprivate->NodeID); + //node already created, USE + if (node) { + gf_node_register(node, cloned_parent); + return node; + } + } + */ + /*create a node*/ +/* + if (orig->sgprivate->tag == TAG_MPEG4_ProtoNode) { + proto_node = ((GF_ProtoInstance *)orig)->proto_interface; + //create the instance but don't load the code -c we MUST wait for ISed routes to be cloned before + node = gf_sg_proto_create_node(inScene, proto_node, (GF_ProtoInstance *) orig); + } else { +*/ + node = gf_node_new(inScene, gf_node_get_tag(orig)); +// } + + count = gf_node_get_field_count(orig); + + /*copy each field*/ + for (i=0; isgprivate->NodeID) { + Node_SetID(node, orig->sgprivate->NodeID); + gf_node_register(node, cloned_parent); + }*/ + + /*init node before creating ISed routes so the eventIn handler are in place*/ + if (gf_node_get_tag(node) != TAG_ProtoNode) gf_node_init(node); + + return node; +} + +} + + +// GetBifsStream -- gets the first stream with id = 3 +GF_StreamContext * V4SceneGraph::GetBifsStream() { + GF_StreamContext * ctx = NULL; + u32 count = gf_list_count(m_pSm->streams); + + // cycle trough each stream + while (count--) { + ctx = (GF_StreamContext *) gf_list_get(m_pSm->streams, count); + if (ctx->streamType == 3) break; + } + + if (ctx == NULL) return NULL; + + // if we went through the loop without finding any BIFS stream return null + if (ctx->streamType != 3) return NULL; + + return ctx; +} + + diff --git a/applications/v4studio/V4SceneGraph.h b/applications/v4studio/V4SceneGraph.h new file mode 100644 index 0000000..cc6dc07 --- /dev/null +++ b/applications/v4studio/V4SceneGraph.h @@ -0,0 +1,95 @@ +#ifndef _V4SCENEGRAPH_H +#define _V4SCENEGRAPH_H + +#include "safe_include.h" +#include +#include +#include // MPEG4CLIENT + + +#include + + +#define DICTNAME "__V4dictionnary" + + +#define FPS 25 + + +class V4StudioFrame; +class V4Service; + +extern "C" { + GF_Node *CloneNodeForEditing(GF_SceneGraph *inScene, GF_Node *orig); + int GetNextNodeID(GF_SceneGraph *sg); +} + +class V4SceneGraph { +public: + // Constructor / Desctructor + V4SceneGraph(V4StudioFrame *parent); + ~V4SceneGraph(); + + + /* file management functions */ + + void SaveFile(const char * path); + void LoadNew(); + void LoadFile(const char *path); + void LoadFileOld(const char *path); + + + /* Node management functions */ + + GF_Node *NewNode(u32 tag); + GF_Node *CopyNode(GF_Node *node, GF_Node *parent, bool copy); + GF_Node *SetTopNode(u32 tag); // TODO : will destroy dictionnary + + /* Various */ + + void LoadCommand(u32 commandNumber); // ? + GF_StreamContext * GetBifsStream(); // Get the first stream with id = 3 + + + /* access to private members */ + + // SceneSize + void SetSceneSize(int w, int h); + void GetSceneSize(wxSize &size); + + // Renderer (owned by the terminal) + void SetRenderer(GF_Compositor *sr) { m_pSr = sr; } + GF_Compositor *GetSceneCompositor() { return m_pSr; } + + // GF_InlineScene + // TODO : Should be modified ? + LPINLINESCENE GetInlineScene() { return m_pIs; } + + // SceneManager + // TODO : Should be modified ? + GF_SceneManager * GetSceneManager() { return m_pSm; } + + // SceneGraph + GF_SceneGraph *GetSceneGraph() { return m_pSg; } + GF_Node *GetRootNode() { return gf_sg_get_root_node(m_pSg); } + +protected: + + // self created + LPINLINESCENE m_pIs; + GF_SceneGraph * m_pSg; + GF_SceneManager *m_pSm; + GF_Node * dictionnary; + + // from other objects + GF_Compositor *m_pSr; + GF_Terminal *m_term; + V4StudioFrame * frame; + + char *m_pOriginal_mp4; + Bool m_bEncodeNames; + V4Service *m_pService; +}; + +#endif + diff --git a/applications/v4studio/V4SceneManager.cpp b/applications/v4studio/V4SceneManager.cpp new file mode 100644 index 0000000..ea31f89 --- /dev/null +++ b/applications/v4studio/V4SceneManager.cpp @@ -0,0 +1,598 @@ +#include "V4SceneManager.h" +#include "V4StudioFrame.h" + +#include +#include +#include +#include + + +V4SceneManager::V4SceneManager(V4StudioFrame *parent) : frame(parent) +{ + m_pSm = NULL; + m_pIs = NULL; + units = 1000; // used to calculate AU timings +} + +V4SceneManager::~V4SceneManager() +{ + GF_SceneGraph *tmp = m_pIs->graph; + + /* Deletion of the SceneManager + This is done before deleting the GF_InlineScene in the GF_Terminal in the GPAC Panel + but for that we need to register the root node one more time to avoid the deletion of the scene graph + But this is temporary, this register of the root node should be done at loading time. + */ + if (m_pSm) { + gf_node_register(m_pSm->scene_graph->RootNode, NULL); + gf_sm_del(m_pSm); + m_pSm = NULL; + } + + /* Destruction of the GPAC panel and of the associated service and terminal */ + delete m_gpac_panel; + m_gpac_panel = NULL; + +} + +void V4SceneManager::LoadCommon() +{ + /* Creation and initialization of the structure InlineScene, including the SceneGraph */ + m_pIs = gf_inline_new(NULL); + + /* Creation of the GPAC Panel which includes the creation of the terminal */ + m_gpac_panel = new wxGPACPanel(this, NULL); + + /* Connection of the InlineScene to the terminal */ + GF_Terminal *term = m_gpac_panel->GetMPEG4Terminal(); + term->root_scene = m_pIs; + +/* + gf_sc_set_scene(term->compositor, m_pIs->graph); + m_pIs->graph_attached = 1; +*/ + + /* Creation of a SceneManager to manipulate the scene and streams */ + m_pSm = gf_sm_new(m_pIs->graph); + + /* Creation of main ObjectManager to manipulate the media */ + m_pIs->root_od = gf_odm_new(); + m_pIs->root_od->parentscene = NULL; + m_pIs->root_od->subscene = m_pIs; + m_pIs->root_od->term = term; +} + +void V4SceneManager::LoadNew() +{ + LoadCommon(); + + /* Create a BIFS stream with one AU with one ReplaceScene */ + GF_StreamContext *sc = gf_sm_stream_new(m_pSm, 1, GF_STREAM_SCENE, 0); + GF_AUContext *au = gf_sm_stream_au_new(sc, 0, 0, 1); + GF_Command *command = gf_sg_command_new(m_pIs->graph, GF_SG_SCENE_REPLACE); + gf_list_add(au->commands, command); + + SetLength(50); + SetFrameRate(25); + + // reinitializes the node pool + pools.Clear(); +} + +void V4SceneManager::LoadFile(const char *path) +{ + LoadCommon(); + GF_Terminal *term = m_gpac_panel->GetMPEG4Terminal(); + + /* Loading of a file (BT, MP4 ...) and modification of the SceneManager */ + GF_SceneLoader load; + memset(&load, 0, sizeof(GF_SceneLoader)); + load.fileName = path; + load.ctx = m_pSm; + if (strstr(path, ".mp4") || strstr(path, ".MP4") ) load.isom = gf_isom_open(path, GF_ISOM_OPEN_READ, NULL); + gf_sm_load_init(&load); + gf_sm_load_run(&load); + gf_sm_load_done(&load); + if (load.isom) gf_isom_delete(load.isom); + + /* SceneManager should be initialized and filled correctly */ + + gf_sg_set_scene_size_info(m_pIs->graph, m_pSm->scene_width, m_pSm->scene_height, m_pSm->is_pixel_metrics); + + // TODO : replace with GetBifsStream + GF_StreamContext *sc = (GF_StreamContext *) gf_list_get(m_pSm->streams,0); + + if (sc->streamType == 3) { + GF_AUContext *au = (GF_AUContext *) gf_list_get(sc->AUs,0); + GF_Command *c = (GF_Command *) gf_list_get(au->commands,0); + //gf_sg_command_apply(m_pIs->graph, c, 0); + /* This is a patch to solve the save pb: + When ApplyCommand is made on a Scene Replace Command + The command node is set to NULL + When we save a BIFS stream whose first command is of this kind, + the file saver thinks the bifs commands should come from an NHNT file + This is a temporary patch */ + if (c->tag == GF_SG_SCENE_REPLACE) { c->node = m_pIs->graph->RootNode; } + } + + gf_sc_set_scene(term->compositor, m_pIs->graph); + m_pIs->graph_attached = 1; + + // TODO : read actual values from file + SetLength(50); + SetFrameRate(25); + + // retrieves all the node from the tree and adds them to the node pool + GF_Node * root = gf_sg_get_root_node(m_pIs->graph); +// CreateDictionnary(); +} + +void V4SceneManager::SaveFile(const char *path) +{ + GF_SMEncodeOptions opts; + char rad_name[5000]; + + /* First dump the scene in a BT file */ + strcpy(rad_name, "dump"); + m_pSm->scene_graph->RootNode = NULL; + gf_sm_dump(m_pSm, rad_name, 0); + + /* Then reload properly (overriding the current SceneManager)*/ + GF_SceneLoader load; + memset(&load, 0, sizeof(GF_SceneLoader)); + load.fileName = "dump.bt"; + load.ctx = m_pSm; + gf_sm_load_init(&load); + gf_sm_load_run(&load); + gf_sm_load_done(&load); + + /* Finally encode the file */ + GF_ISOFile *mp4 = gf_isom_open(path, GF_ISOM_WRITE_EDIT, NULL); + m_pSm->max_node_id = gf_sg_get_max_node_id(m_pSm->scene_graph); + memset(&opts, 0, sizeof(opts)); + opts.flags = GF_SM_LOAD_MPEG4_STRICT; + gf_sm_encode_to_file(m_pSm, mp4, &opts); + gf_isom_set_brand_info(mp4, GF_ISOM_BRAND_MP42, 1); + gf_isom_modify_alternate_brand(mp4, GF_ISOM_BRAND_ISOM, 1); + gf_isom_close(mp4); +} + +void V4SceneManager::SetSceneSize(int w, int h) +{ + gf_sg_set_scene_size_info(m_pIs->graph, w,h, 1); + /*reassign scene graph to update scene size in renderer*/ + gf_sc_set_scene(m_gpac_panel->GetMPEG4Terminal()->compositor, m_pIs->graph); + m_pIs->graph_attached = 1; +} + +void V4SceneManager::GetSceneSize(wxSize &size) +{ + gf_sg_get_scene_size_info(m_pIs->graph, (u32 *)&(size.x), (u32 *)&(size.y)); +} + +GF_Node *V4SceneManager::SetTopNode(u32 tag) +{ + GF_Node *root = NewNode(tag); + gf_sg_set_root_node(m_pIs->graph, root); + GF_StreamContext *sc = (GF_StreamContext *) gf_list_get(m_pSm->streams,0); + if (sc->streamType == 3) { + GF_AUContext *au = (GF_AUContext *) gf_list_get(sc->AUs,0); + GF_Command *c = (GF_Command *) gf_list_get(au->commands,0); + /* This is a patch to solve the save pb: + When ApplyCommand is made on a Scene Replace Command + The command node is set to NULL + When we save a BIFS stream whose first command is of this kind, + the file saver thinks the bifs commands should come from an NHNT file + This is a temporary patch */ + if (c->tag == GF_SG_SCENE_REPLACE) { + c->node = m_pIs->graph->RootNode; + gf_node_register(m_pIs->graph->RootNode, NULL); + } + } + return root; +} + +GF_Node *V4SceneManager::NewNode(u32 tag) +{ + GF_Node *n = gf_node_new(m_pIs->graph, tag); + if (!n) return NULL; + gf_node_init(n); + return n; +} + +GF_Node *V4SceneManager::CopyNode(GF_Node *node, GF_Node *parent, bool copy) +{ + if (copy) return CloneNodeForEditing(m_pIs->graph, node); + u32 nodeID = gf_node_get_id(node); + if (!nodeID) { + nodeID = gf_sg_get_next_available_node_id(m_pIs->graph); + gf_node_set_id(node, nodeID, NULL); + gf_node_register(node, parent); + } + return node; +} + +extern "C" { + +GF_Node *CloneNodeForEditing(GF_SceneGraph *inScene, GF_Node *orig) //, GF_Node *cloned_parent) +{ + u32 i, j, count; + GF_Node *node, *child, *tmp; + GF_ChildNodeItem *list, *list2; + GF_FieldInfo field_orig, field; + + /*this is not a mistake*/ + if (!orig) return NULL; + + /*check for DEF/USE + if (orig->sgprivate->NodeID) { + node = gf_sg_find_node(inScene, orig->sgprivate->NodeID); + //node already created, USE + if (node) { + gf_node_register(node, cloned_parent); + return node; + } + } + */ + /*create a node*/ +/* + if (orig->sgprivate->tag == TAG_MPEG4_ProtoNode) { + proto_node = ((GF_ProtoInstance *)orig)->proto_interface; + //create the instance but don't load the code -c we MUST wait for ISed routes to be cloned before + node = gf_sg_proto_create_node(inScene, proto_node, (GF_ProtoInstance *) orig); + } else { +*/ + node = gf_node_new(inScene, gf_node_get_tag(orig)); +// } + + count = gf_node_get_field_count(orig); + + /*copy each field*/ + for (i=0; isgprivate->NodeID) { + Node_SetID(node, orig->sgprivate->NodeID); + gf_node_register(node, cloned_parent); + }*/ + + /*init node before creating ISed routes so the eventIn handler are in place*/ + if (gf_node_get_tag(node) != TAG_ProtoNode) gf_node_init(node); + + return node; +} + +} // extern "C" + + +// GetBifsStream -- gets the first stream with id = 3 +GF_StreamContext * V4SceneManager::GetBifsStream() { + GF_StreamContext * ctx = NULL; + u32 count = gf_list_count(m_pSm->streams); + + // cycle trough each stream + while (count--) { + ctx = (GF_StreamContext *) gf_list_get(m_pSm->streams, count); + if (ctx->streamType == 3) break; + } + + if (ctx == NULL) return NULL; + + // if we went through the loop without finding any BIFS stream return null + if (ctx->streamType != 3) return NULL; + + return ctx; +} + + +// CreateDictionnary -- Create the root node of the dictionnary and populates it +void V4SceneManager::CreateDictionnary() { + GF_Node * root = gf_sg_get_root_node(m_pIs->graph); + dictionnary = NewNode(TAG_MPEG4_Switch); + + // Insert the dictionnary in the scene and gives it a name + gf_node_insert_child(root, dictionnary, 0); + gf_node_register(dictionnary, root); + gf_node_set_id(dictionnary, gf_sg_get_next_available_node_id(m_pIs->graph), DICTNAME); + + // makes the dictionnary invisible + // the number 1 field is the "whichChoice", setting it to -1 makes it display nothing + GF_FieldInfo field; + gf_node_get_field(dictionnary, 1, &field); + frame->GetFieldView()->SetFieldValue(field, &wxString("-1"), -1); // TODO : maybe put the method SetFieldValue somewhere more accessible + + // populates dictionnary with the root node of the scene + AddToDictionnary(root); +} + + +// AddToDictionnary -- adds a node to the dictionnary, increases its reference count, the node must already have an ID +// WARNING : does not add the node to the pool if it has a parent in the dictionnary +void V4SceneManager::AddToDictionnary(GF_Node * node) { + + // checks if nodes has a parent with an id + // if true then there is already a reference to this node + if (HasDefParent(node)) return; + + AddRecursive(node); +} + + +// AddRecursive -- recursively adds items with an ID to the dictionnary +void V4SceneManager::AddRecursive(GF_Node * node, bool parentAdded) { + + // skips empty nodes + if (!node) return; + + // skips the dictionnary + const char * c = gf_node_get_name(node); + if ( (c != NULL) && (!strcmp(c, DICTNAME)) ) return; + + // if node as an id adds it to the dictionnary and the node pool + u32 id = gf_node_get_id(node); + if (id) { + pools.Add(node); + // children of added node are not added to the dictionnary + if (!parentAdded) AddEffective(node); + parentAdded = true; + } + + GF_FieldInfo field; + GF_ChildNodeItem * list; + int count = gf_node_get_field_count(node); + + // tests all fields, if a field is a node then adds it and process its children recursively + for (int i=0; iGetTimeLine()->SetLength(length_); +} \ No newline at end of file diff --git a/applications/v4studio/V4SceneManager.h b/applications/v4studio/V4SceneManager.h new file mode 100644 index 0000000..3cb4209 --- /dev/null +++ b/applications/v4studio/V4SceneManager.h @@ -0,0 +1,148 @@ +#ifndef _V4SceneManager_H +#define _V4SceneManager_H + +#include "safe_include.h" +#include +#include +#include // MPEG4CLIENT +#include "wxGPACPanel.h" +#include "V4NodePools.h" + + +#include + + +#define DICTNAME "__V4dictionnary" + + +class V4StudioFrame; + +extern "C" { + GF_Node *CloneNodeForEditing(GF_SceneGraph *inScene, GF_Node *orig); + int GetNextNodeID(GF_SceneGraph *sg); +} + +class V4SceneManager { +public: + // Constructor / Desctructor + V4SceneManager(V4StudioFrame *parent); + ~V4SceneManager(); + + // reusable nodes, sorted by tag + V4NodePools pools; + + /* file management functions */ + + void SaveFile(const char * path); + + void LoadCommon(); + void LoadNew(); + void LoadFile(const char *path); + + /* Node management functions */ + + GF_Node *NewNode(u32 tag); + GF_Node *CopyNode(GF_Node *node, GF_Node *parent, bool copy); + GF_Node *SetTopNode(u32 tag); // TODO : will destroy dictionnary + + void CreateIDandAddToPool(GF_Node *node); + + /* Dictionnary functions */ + + // adds a node to the dictionnary, increases its reference count, the node must already have an ID + void AddToDictionnary(GF_Node * node); + + // removes a node from the dictionnary = DELETES it's ID + void RemoveFromDictionnary(GF_Node * node); + + GF_Node * GetDictionnary() const; + + + /* Various */ + + void LoadCommand(u32 commandNumber); // ? + GF_StreamContext * GetBifsStream(); // Get the first stream with id = 3 + + + /* access to private members */ + + // SceneSize + void SetSceneSize(int w, int h); + void GetSceneSize(wxSize &size); + + // FrameRate + void SetFrameRate(const u32 framerate_) { frameRate = framerate_; } + u32 GetFrameRate() const { return frameRate; } + + // accesses Length + void SetLength(const u32 length_); + u32 GetLength() const { return length; } + + // accesses units + void SetUnits(u32 units_) { units = units_; } + u32 GetUnits() const { return units; } + + + + GF_Compositor *GetSceneCompositor() { return m_gpac_panel->GetSceneCompositor(); } + + // GF_InlineScene + GF_InlineScene *GetInlineScene() { return m_pIs; } + + // SceneGraph + GF_SceneGraph *GetSceneGraph() { return m_pIs->graph; } + GF_Node *GetRootNode() { return gf_sg_get_root_node(m_pIs->graph); } + + wxGPACPanel *GetGPACPanel() { return m_gpac_panel; } + V4StudioFrame *GetV4StudioFrame() { return frame; } + + // TODO : terminal + private: + + /* dictionnary function */ + + // initializes the dictionnary node + void CreateDictionnary(); + + // Finds all node with an ID from the given node and adds them to the node pool and the dictionnary + void AddRecursive(GF_Node * node, bool parentAdded = false); + + // calls a proper function to add the node according to its type + void AddEffective(GF_Node * node); + + // Create a dummy node of type tag and it to the specified field (or reimplace if the field is single valued) + GF_Node * CreateDummyNode(const u32 tag, GF_Node * parent, const u32 fieldIndex); + + // Sets the first node as a child of the second using the given field + void MakeChild(GF_Node * child, GF_Node * parent, const u32 fieldIndex); + + // Checks is specified node as a parent with an ID + GF_Node * V4SceneManager::HasDefParent(GF_Node * node); + + // adds a node to the dictionnary + void AddGen(GF_Node * node); + void AddGeometry(GF_Node * node); + void AddAppearance(GF_Node * node); + void AddTexture(GF_Node * node); + void AddMaterial(GF_Node * node); + + +protected: + + // self created + LPINLINESCENE m_pIs; + GF_SceneManager *m_pSm; + GF_Node *dictionnary; + wxGPACPanel *m_gpac_panel; + + // from other objects + V4StudioFrame *frame; + + u8 frameRate; + u32 length; // in frames! + u32 units; // = 1000 + +}; + +#endif + diff --git a/applications/v4studio/V4Service.cpp b/applications/v4studio/V4Service.cpp new file mode 100644 index 0000000..823e9ee --- /dev/null +++ b/applications/v4studio/V4Service.cpp @@ -0,0 +1,205 @@ +#include "V4Service.h" + +#include + +#define V4_PRIVATE_SCENE_OTI 0xFF + +extern "C" { + + +V4Channel *V4Service::V4_GetChannel(V4Service *v4service, LPNETCHANNEL ch) +{ + u32 i; + for (i=0; ichannels); i++) { + V4Channel *v4c = (V4Channel *)gf_list_get(v4service->channels, i); + if (v4c->ch && v4c->ch==ch) return v4c; + } + return NULL; +} + +Bool V4Service::V4_RemoveChannel(V4Service *v4service, LPNETCHANNEL ch) +{ + u32 i; + for (i=0; ichannels); i++) { + V4Channel *v4c = (V4Channel *)gf_list_get(v4service->channels, i); + if (v4c->ch && v4c->ch==ch) { + gf_list_rem(v4service->channels, i); + free(v4c); + return 1; + } + } + return 0; +} + +static Bool V4_CanHandleURL(GF_InputService *plug, const char *url) +{ + return 1; +} + +static GF_Err V4_ConnectService (GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + V4Service *v4serv = (V4Service *)plug->priv; + v4serv->SetService(serv); + gf_term_on_connect(v4serv->GetService(), NULL, GF_OK); + return GF_OK; +} + +static GF_Err V4_CloseService(GF_InputService *plug) +{ + V4Service *v4serv = (V4Service *)plug->priv; + gf_term_on_disconnect(v4serv->GetService(), NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *V4_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + V4Service *v4service= (V4Service *) plug->priv; + GF_ESD *esd; + GF_InitialObjectDescriptor *iod = (GF_InitialObjectDescriptor *) gf_odf_desc_new(GF_ODF_IOD_TAG); + iod->scene_profileAndLevel = 1; + iod->graphics_profileAndLevel = 1; + iod->OD_profileAndLevel = 1; + iod->audio_profileAndLevel = 0xFE; + iod->visual_profileAndLevel = 0xFE; + iod->objectDescriptorID = 1; + + esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = 1000; + esd->slConfig->useTimestampsFlag = 1; + esd->ESID = 0xFFFE; + esd->decoderConfig->streamType = GF_STREAM_PRIVATE_SCENE; + esd->decoderConfig->objectTypeIndication = 0x01; //V4_PRIVATE_SCENE_OTI; + if (v4service->GetPath()) { + esd->decoderConfig->decoderSpecificInfo->dataLength = strlen(v4service->GetPath()) + 1; + esd->decoderConfig->decoderSpecificInfo->data = strdup(v4service->GetPath()); + } + gf_list_add(iod->ESDescriptors, esd); + return (GF_Descriptor *)iod; +} + +static GF_Err V4_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ESID; + V4Service *v4serv = (V4Service *)plug->priv; + + sscanf(url, "ES_ID=%d", &ESID); + if (!ESID) { + gf_term_on_connect(v4serv->GetService(), channel, GF_STREAM_NOT_FOUND); + } else { + V4Channel *v4c = (V4Channel *)malloc(sizeof(V4Channel)); + memset(v4c, 0, sizeof(V4Channel)); + v4c->ch = channel; + v4c->ESID = ESID; + gf_list_add(v4serv->GetChannels(), v4c); + gf_term_on_connect(v4serv->GetService(), channel, GF_OK); + } + return GF_OK; +} + +static GF_Err V4_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + V4Service *v4serv = (V4Service *)plug->priv; + Bool had_ch; + + had_ch = v4serv->V4_RemoveChannel(v4serv, channel); + gf_term_on_disconnect(v4serv->GetService(), channel, had_ch ? GF_OK : GF_STREAM_NOT_FOUND); + return GF_OK; +} + +static GF_Err V4_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + V4Service *v4serv = (V4Service *)plug->priv; + V4Channel *v4c; + + if (!com->base.on_channel) return GF_OK; + + v4c = v4serv->V4_GetChannel(v4serv, com->base.on_channel); + if (!v4c) return GF_STREAM_NOT_FOUND; + + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: return GF_OK; + case GF_NET_CHAN_INTERACTIVE: return GF_OK; + case GF_NET_CHAN_SET_PADDING: return GF_NOT_SUPPORTED; + case GF_NET_CHAN_BUFFER: + com->buffer.max = com->buffer.min = 0; + return GF_OK; + case GF_NET_CHAN_DURATION: + /*this is not made for updates, use undefined duration*/ + com->duration.duration = 0; + return GF_OK; + case GF_NET_CHAN_PLAY: + v4c->start = (u32) (1000 * com->play.start_range); + v4c->end = (u32) (1000 * com->play.end_range); + return GF_OK; + case GF_NET_CHAN_STOP: + return GF_OK; + case GF_NET_CHAN_CONFIG: return GF_OK; + case GF_NET_CHAN_GET_DSI: + com->get_dsi.dsi = NULL; + com->get_dsi.dsi_len = 0; + return GF_OK; + } + return GF_OK; +} + +static GF_Err V4_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + V4Channel *v4c; + V4Service *v4serv = (V4Service *)plug->priv; + v4c = v4serv->V4_GetChannel(v4serv, channel); + if (!v4c) return GF_STREAM_NOT_FOUND; + + memset(out_sl_hdr, 0, sizeof(GF_SLHeader)); + out_sl_hdr->compositionTimeStampFlag = 1; + out_sl_hdr->compositionTimeStamp = v4c->start; + out_sl_hdr->accessUnitStartFlag = 1; + *sl_compressed = 0; + *out_reception_status = GF_OK; + *is_new_data = 1; + return GF_OK; return GF_OK; +} + +static GF_Err V4_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) +{ + return GF_OK; +} + +static Bool V4_CanHandleURLInService(GF_InputService *plug, const char *url) +{ + return 0; +} + +} // extern "C" + +V4Service::V4Service(const char *path) +{ + + m_pNetClient = (GF_InputService *)malloc(sizeof(GF_InputService)); + m_pNetClient->priv = this; + + m_pNetClient->CanHandleURL = V4_CanHandleURL; + + m_pNetClient->ConnectService = V4_ConnectService; + m_pNetClient->CloseService = V4_CloseService; + + m_pNetClient->ConnectChannel = V4_ConnectChannel; + m_pNetClient->DisconnectChannel = V4_DisconnectChannel; + + m_pNetClient->GetServiceDescriptor = V4_GetServiceDesc; + m_pNetClient->ServiceCommand = V4_ServiceCommand; + + m_pNetClient->ChannelGetSLP = V4_ChannelGetSLP; + m_pNetClient->ChannelReleaseSLP = V4_ChannelReleaseSLP; + + m_pNetClient->CanHandleURLInService = V4_CanHandleURLInService; + + channels = gf_list_new(); + if (path) m_path = strdup(path); +} + +V4Service::~V4Service() +{ + if (m_path) free(m_path); + gf_list_del(channels); + free(m_pNetClient); +} \ No newline at end of file diff --git a/applications/v4studio/V4Service.h b/applications/v4studio/V4Service.h new file mode 100644 index 0000000..183a367 --- /dev/null +++ b/applications/v4studio/V4Service.h @@ -0,0 +1,42 @@ +#include "safe_include.h" +#include + +typedef struct +{ + u32 ESID; + LPNETCHANNEL ch; + u32 start, end; +} V4Channel; + + +class V4Service { +public: + + V4Service(const char*path); + ~V4Service(); + + void SetService(GF_ClientService *s) { m_pService = s; } + GF_ClientService *GetService() { return m_pService; } + + void SetPath(char *path) { m_path = path; } + char *GetPath() { return m_path; } + + GF_List* GetChannels() { return channels; } + V4Channel *V4_GetChannel(V4Service *v4service, LPNETCHANNEL ch); + Bool V4_RemoveChannel(V4Service *v4service, LPNETCHANNEL ch); + + GF_InputService *GetServiceInterface() { return m_pNetClient; } + +protected: + + /* the interface given to the GPAC Core */ + GF_InputService *m_pNetClient; + + /* The actual GPAC Core service returned by the interface */ + GF_ClientService *m_pService; + /* All the channels of the current service */ + GF_List *channels; + + /* The path to the currently load file */ + char *m_path; +}; \ No newline at end of file diff --git a/applications/v4studio/V4StudioApp.cpp b/applications/v4studio/V4StudioApp.cpp new file mode 100644 index 0000000..32fef26 --- /dev/null +++ b/applications/v4studio/V4StudioApp.cpp @@ -0,0 +1,23 @@ +#include "safe_include.h" + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "V4StudioApp.h" +#include "V4StudioFrame.h" +#include +//#include + +IMPLEMENT_APP(V4StudioApp) + +bool V4StudioApp::OnInit() +{ + wxFrame *frame = new V4StudioFrame(); + frame->Show(TRUE); + SetTopWindow(frame); + return true; +} + diff --git a/applications/v4studio/V4StudioApp.h b/applications/v4studio/V4StudioApp.h new file mode 100644 index 0000000..fe70266 --- /dev/null +++ b/applications/v4studio/V4StudioApp.h @@ -0,0 +1,15 @@ +#ifndef _V4STUDIO_APP_H +#define _V4STUDIO_APP_H + +class V4StudioApp : public wxApp +{ +public: + virtual bool OnInit(); +protected: +private: +}; + +DECLARE_APP(V4StudioApp) + +#endif + diff --git a/applications/v4studio/V4StudioFrame.cpp b/applications/v4studio/V4StudioFrame.cpp new file mode 100644 index 0000000..d51a78e --- /dev/null +++ b/applications/v4studio/V4StudioFrame.cpp @@ -0,0 +1,951 @@ +#include "safe_include.h" + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "V4StudioFrame.h" +#include "wx/xrc/xmlres.h" + + +#include +#include // GF_Terminal +#include + +V4StudioFrame::V4StudioFrame(): + wxFrame((wxFrame *) NULL, -1, "V4Studio", wxPoint(50, 50), wxSize(800, 700)) +{ + + m_pV4sm = NULL; + + m_selection = NULL; + m_parentSelection = NULL; + m_clipboardNode = NULL; + + m_frame = 0; // we start at frame=0 + editDict = false; + + // Creates components and places them on the form + fieldView = new V4FieldList(this, wxSize(100,250)); + treeView = new V4StudioTree(this, wxSize(100,250), fieldView); + timeLine = new V4TimeLine(this); + timeLine->SetSize(500, 100); + cmdPanel = new V4CommandPanel(this); + cmdPanel->SetSize(100, 100); + + /*new m_pFileMenu bar*/ + wxMenuBar *b = new wxMenuBar(); + /*file*/ + m_pFileMenu = new wxMenu(); + m_pFileMenu->Append(MENU_FILE_NEW, "&New\tCtrl+N", "Create a new document"); + m_pFileMenu->Append(MENU_FILE_OPEN, "&Open...\tCtrl+O", "Open an existing document"); + m_pFileMenu->Append(MENU_FILE_SAVE, "&Save\tCtrl+S", "Save the active document"); + m_pFileMenu->Append(MENU_FILE_CLOSE, "&Close\tCtrl+X", "Close the active document"); + m_pFileMenu->AppendSeparator(); + m_pFileMenu->Append(CHANGE_SIZE_DIALOG, "&Size\tCtrl+Z", "Change scene size"); + m_pFileMenu->Append(CHANGE_FRAMERATE, "&FrameRate\tCtrl+F", "Change FrameRate"); + m_pFileMenu->Append(CHANGE_LENGTH, "&Length\tCtrl+L", "Change Length"); + m_pFileMenu->AppendSeparator(); + m_pFileMenu->Append(MENU_FILE_QUIT, "E&xit", "Quit the application; prompts to save documents"); + b->Append(m_pFileMenu, "&File"); + SetMenuBar(b); + + /*file*/ + m_pToolsMenu = new wxMenu(); + m_pToolsMenu->Append(MENU_TOOL_SHOW_LOWLEVEL, "&Low-Level\tCtrl+L", "Shows the low-level toolbar"); + m_pToolsMenu->Append(MENU_TOOL_SHOW_HIGHLEVEL, "&High-Level\tCtrl+H", "Shows the high-level toolbar"); + b->Append(m_pToolsMenu, "&Tools"); + SetMenuBar(b); + + m_pStatusbar = CreateStatusBar(); + + // Main Toolbar + m_pMainToolbar = new wxToolBar(this, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL|wxTB_FLAT); + m_pMainToolbar->AddTool(TOOL_FILE_NEW, _("New"), wxBitmap (wxT("rc\\new.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Open a new file"), wxT("")); + m_pMainToolbar->AddTool(TOOL_FILE_OPEN, _("Open"), wxBitmap (wxT("rc\\open.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Open an existing file (BT/XMT/MP4)"), wxT("")); + m_pMainToolbar->AddTool(TOOL_FILE_SAVE, _("Save"), wxBitmap (wxT("rc\\save.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Save to a file"), wxT("")); + m_pMainToolbar->AddTool(TOOL_FILE_CLOSE, _("Close"), wxBitmap (wxT("rc\\close.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Close the file"), wxT("")); + m_pMainToolbar->AddSeparator(); + m_pMainToolbar->AddTool(TOOL_FILE_PREVIEW, _("Preview"), wxBitmap (wxT("rc\\preview.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Preview in player"), wxT("")); + m_pMainToolbar->AddSeparator(); + m_pMainToolbar->AddTool(TOOL_EDIT_CUT, _("Cut"), wxBitmap (wxT("rc\\cut.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Cut"), wxT("")); + m_pMainToolbar->AddTool(TOOL_EDIT_COPY, _("Copy"), wxBitmap (wxT("rc\\copy.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Copy"), wxT("")); + m_pMainToolbar->AddTool(TOOL_EDIT_PASTE, _("Paste"), wxBitmap (wxT("rc\\paste.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Paste"), wxT("")); + m_pMainToolbar->AddTool(TOOL_EDIT_PASTE_USE, _("PastePlus"), wxBitmap (wxT("rc\\paste_use.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Paste a USE"), wxT("")); + m_pMainToolbar->AddTool(TOOL_EDIT_DELETE, _("Delete"), wxBitmap (wxT("rc\\delete.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Delete"), wxT("")); + m_pMainToolbar->AddSeparator(); + m_pMainToolbar->AddTool(TOOL_EDIT_UNDO, _("Undo"), wxBitmap (wxT("rc\\undo.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Undo"), wxT("")); + m_pMainToolbar->AddTool(TOOL_EDIT_REDO, _("Redo"), wxBitmap (wxT("rc\\redo.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Redo"), wxT("")); + m_pMainToolbar->AddSeparator(); + + m_pMainToolbar->AddSeparator(); + + m_pMainToolbar->AddTool(TOOL_ADD_TO_TL, _("AddToTL"), wxBitmap( wxT("rc\\paste.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Add to timeLine"), wxT("")); + m_pMainToolbar->AddTool(TOOL_NEXT_FRAME, _("NextFrame"), wxBitmap( wxT("rc\\redo.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Next frame"), wxT("")); + m_pMainToolbar->AddTool(TOOL_VIEW_DICT, _("ViewDict"), wxBitmap( wxT("rc\\open.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("View Dictionnary"), wxT("")); + + /* Not Implemented yet */ + m_pMainToolbar->EnableTool(TOOL_FILE_PREVIEW, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_UNDO, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_REDO, false); + m_pMainToolbar->EnableTool(TOOL_NEXT_FRAME, false); + + /* Not available when starting V4Studio */ + /* Nothing to save */ + m_pFileMenu->Enable(MENU_FILE_SAVE, false); + m_pFileMenu->Enable(MENU_FILE_CLOSE, false); + m_pFileMenu->Enable(CHANGE_SIZE_DIALOG, false); + m_pFileMenu->Enable(CHANGE_LENGTH, false); + m_pFileMenu->Enable(CHANGE_FRAMERATE, false); + m_pMainToolbar->EnableTool(TOOL_FILE_SAVE, false); + m_pMainToolbar->EnableTool(TOOL_FILE_CLOSE, false); + + /* Nothing to edit */ + m_pMainToolbar->EnableTool(TOOL_EDIT_CUT, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_COPY, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE_USE, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_DELETE, false); + + m_pMainToolbar->EnableTool(TOOL_ADD_TO_TL, false); + m_pMainToolbar->EnableTool(TOOL_VIEW_DICT, false); + m_pMainToolbar->Realize(); + SetToolBar(m_pMainToolbar); + + m_uSelectedNodeToolBar = 0; + // Node Creation Toolbar + m_pLowLevelNodeToolbar = new wxToolBar(this, -1, wxDefaultPosition, wxDefaultSize, wxTB_VERTICAL|wxTB_FLAT); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_ORDEREDGROUP, _("OrderedGroup"), wxBitmap (wxT("rc\\orderedgroup.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create an OrderedGroup"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_LAYER2D, _("Layer2D"), wxBitmap (wxT("rc\\layer2d.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a Layer2D"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_TRANSFORM2D, _("Transform2D"), wxBitmap (wxT("rc\\t2d.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a Tranform2D"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_TRANSFORMMATRIX2D, _("TransformMatrix2D"), wxBitmap (wxT("rc\\tm2d.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a TranformMatrix2D"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_COLOR_TRANSFORM, _("ColorTransform"), wxBitmap (wxT("rc\\colortransform.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a ColorTransform"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_SHAPE, _("Shape"), wxBitmap (wxT("rc\\shape.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a Shape"), wxT("")); + m_pLowLevelNodeToolbar->AddSeparator(); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_APPEARANCE, _("Appearance"), wxBitmap (wxT("rc\\appearance.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create an Appearance"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_MATERIAL2D, _("Material2D"), wxBitmap (wxT("rc\\material2d.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a Material2D"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_LINEPROPS, _("LineProps"), wxBitmap (wxT("rc\\lineproperties.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a LineProperties"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_LINEAR_GRADIENT, _("LinearGradient"), wxBitmap (wxT("rc\\lg.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a Linear Gradient"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_RADIAL_GRADIENT, _("RadialGradient"), wxBitmap (wxT("rc\\rg.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a Radial Gradient"), wxT("")); + m_pLowLevelNodeToolbar->AddSeparator(); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_RECT, _("Rectangle"), wxBitmap (wxT("rc\\rect.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a Rectangle"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_CIRCLE, _("Circle"), wxBitmap (wxT("rc\\circle.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a Rectangle"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_IFS2D, _("IndexedFaceSet2D"), wxBitmap (wxT("rc\\ifs2d.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create an IndexedFaceSet2D"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_ILS2D, _("IndexedLineSet2D"), wxBitmap (wxT("rc\\ils2d.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create an IndexedLineSet2D"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_XLINEPROPS, _("XLineProps"), wxBitmap (wxT("rc\\xlineproperties.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create an XLineProperties"), wxT("")); + m_pLowLevelNodeToolbar->AddSeparator(); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_TEXT, _("Text"), wxBitmap (wxT("rc\\text.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a Text"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_FONTSTYLE, _("FontStyle"), wxBitmap (wxT("rc\\fs.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Create a FontStyle"), wxT("")); + m_pLowLevelNodeToolbar->AddSeparator(); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_BACKGROUND2D, _("Background2D"), wxBitmap (wxT("rc\\image.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Add a Background2D"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_MOVIE, _("Movie"), wxBitmap (wxT("rc\\movie.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Add a Movie"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_IMAGE, _("Image"), wxBitmap (wxT("rc\\image.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Add an Image"), wxT("")); + m_pLowLevelNodeToolbar->AddTool(TOOL_NEW_SOUND, _("Sound"), wxBitmap (wxT("rc\\sound.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Add a Sound"), wxT("")); + m_pLowLevelNodeToolbar->Realize(); + +/* + m_pHighLevelNodeToolbar = new wxToolBar(this, -1, wxDefaultPosition, wxDefaultSize, wxTB_VERTICAL|wxTB_FLAT); + m_pHighLevelNodeToolbar->AddTool(TOOL_NEW_2DSCENE, _("OrderedGroup"), wxBitmap (wxT("rc\\orderedgroup.bmp"), wxBITMAP_TYPE_ANY), wxNullBitmap, wxITEM_NORMAL, _("Creates a 2D scene"), wxT("")); + m_pHighLevelNodeToolbar->Realize(); +*/ + set_properties(); + do_layout(); + UpdateToolBar(); +} + +V4StudioFrame::~V4StudioFrame() +{ +} + + +/* Functions that changes scene parameters */ + +void V4StudioFrame::OnChangeFrameRate(wxCommandEvent &event) { + if (!m_pV4sm) return; + + u32 l = m_pV4sm->GetFrameRate(); + wxString sFR; + sFR.Printf("%d", l); + + wxTextEntryDialog dialog(this, _T("Enter scene framerate"), _T("one positive integer number"), sFR, wxOK | wxCANCEL); + + if (dialog.ShowModal() != wxID_OK) { + dialog.Destroy(); + return; + } + + sscanf(dialog.GetValue(), "%d", &l); + m_pV4sm->SetFrameRate(l); + + dialog.Destroy(); + SceneGraphChanged(); +} + + +void V4StudioFrame::OnChangeLength(wxCommandEvent &event) { + if (!m_pV4sm) return; + u32 l = m_pV4sm->GetLength(); + wxString sLength; + sLength.Printf("%d", l); + + wxTextEntryDialog dialog(this, _T("Enter scene length"), _T("one positive integer number"), sLength, wxOK | wxCANCEL); + + if (dialog.ShowModal() != wxID_OK) { + dialog.Destroy(); + return; + } + + sscanf(dialog.GetValue(), "%d", &l); + m_pV4sm->SetLength(l); + + dialog.Destroy(); + SceneGraphChanged(); +} + +void V4StudioFrame::OnChangeSize(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + u32 w, h; + wxSize sceneSize; + m_pV4sm->GetSceneSize(sceneSize); + w = sceneSize.GetX(); + h = sceneSize.GetY(); + wxString sSize; + sSize.Printf("%d %d", w, h); + wxTextEntryDialog dialog(this, + _T("Enter the scene width and height"), + _T("Please enter two integer numbers"), + sSize, + wxOK | wxCANCEL); + + while (dialog.ShowModal() != wxID_OK && w == 0 && h == 0) {} + sscanf(dialog.GetValue(), "%d %d", &w, &h); + m_pV4sm->SetSceneSize(w,h); + dialog.Destroy(); + SceneGraphChanged(); +} + +// Dispatchs the new scene graph to all the sub components who need it. +void V4StudioFrame::SceneGraphChanged() +{ + m_parentSelection = NULL; + m_selection = (!m_pV4sm ? NULL : m_pV4sm->GetRootNode()); + Layout(); + Update(); +} + +void V4StudioFrame::set_properties() +{ + SetTitle(_("V4Studio")); + wxIcon _icon; + _icon.CopyFromBitmap(wxBitmap(_("rc\\v4.bmp"), wxBITMAP_TYPE_ANY)); + SetIcon(_icon); + SetSize(wxSize(500, 500)); +} + + +void V4StudioFrame::do_layout() +{ + wxBoxSizer * sizer_6 = new wxBoxSizer(wxVERTICAL); // top = treeview + fieldview; bottom = timeline + command panel + wxBoxSizer * sizer_7 = new wxBoxSizer(wxHORIZONTAL); // right = timeline, left = command panel + wxBoxSizer * sizer_8 = new wxBoxSizer(wxHORIZONTAL); // right = treeview; left = fieldview + + sizer_6->Add(sizer_8, 2, wxEXPAND, 0); // height of treeview twice bigger thant height of timeline + sizer_6->Add(sizer_7, 1, wxEXPAND, 0); + + sizer_7->Add(timeLine, 3, wxEXPAND, 0); + sizer_7->Add(cmdPanel, 1, wxEXPAND, 0); + + sizer_8->Add(treeView, 1, wxEXPAND, 0); + sizer_8->Add(fieldView, 1, wxEXPAND, 0); + + wxBoxSizer* sizer_9 = new wxBoxSizer(wxHORIZONTAL); + + sizer_9->Add(m_pLowLevelNodeToolbar, 0, wxEXPAND, 0); +/* + if (m_uSelectedNodeToolBar == 0) sizer_9->Add(m_pLowLevelNodeToolbar, 0, wxEXPAND, 0); + else sizer_9->Add(m_pHighLevelNodeToolbar, 0, wxEXPAND, 0); +*/ + + sizer_9->Add(sizer_6, 1, wxEXPAND, 0); + + SetSizer(sizer_9); + Layout(); + + cmdPanel->Layout(); +} + + +BEGIN_EVENT_TABLE(V4StudioFrame, wxFrame) + // m_pFileMenu events + EVT_MENU(MENU_FILE_NEW, V4StudioFrame::OnNew) + EVT_MENU(MENU_FILE_OPEN, V4StudioFrame::OnFileOpen) + EVT_MENU(MENU_FILE_SAVE, V4StudioFrame::OnSave) + EVT_MENU(MENU_FILE_CLOSE, V4StudioFrame::OnClose) + EVT_MENU(MENU_FILE_QUIT, V4StudioFrame::OnQuit) + EVT_MENU(CHANGE_SIZE_DIALOG, V4StudioFrame::OnChangeSize) + EVT_MENU(CHANGE_FRAMERATE, V4StudioFrame::OnChangeFrameRate) + EVT_MENU(CHANGE_LENGTH, V4StudioFrame::OnChangeLength) + + // m_pToolsMenu events + EVT_MENU(MENU_TOOL_SHOW_LOWLEVEL, V4StudioFrame::OnLowLevelTools) + EVT_MENU(MENU_TOOL_SHOW_HIGHLEVEL, V4StudioFrame::OnHighLevelTools) + + // edit toolbar events + EVT_TOOL(TOOL_FILE_NEW, V4StudioFrame::OnNew) + EVT_TOOL(TOOL_FILE_OPEN, V4StudioFrame::OnFileOpen) + EVT_TOOL(TOOL_FILE_SAVE, V4StudioFrame::OnSave) + EVT_TOOL(TOOL_FILE_CLOSE, V4StudioFrame::OnClose) + EVT_TOOL(TOOL_EDIT_CUT, V4StudioFrame::OnEditCut) + EVT_TOOL(TOOL_EDIT_COPY, V4StudioFrame::OnEditCopy) + EVT_TOOL(TOOL_EDIT_PASTE, V4StudioFrame::OnEditPaste) + EVT_TOOL(TOOL_EDIT_PASTE_USE, V4StudioFrame::OnEditPasteUse) + EVT_TOOL(TOOL_EDIT_DELETE, V4StudioFrame::OnEditDelete) + EVT_TOOL(TOOL_ADD_TO_TL, V4StudioFrame::OnAddToTimeLine) + EVT_TOOL(TOOL_NEXT_FRAME, V4StudioFrame::NextFrame) + EVT_TOOL(TOOL_VIEW_DICT, V4StudioFrame::SwitchView) + + // new toolbar events + EVT_TOOL(TOOL_NEW_ORDEREDGROUP, V4StudioFrame::OnNewOrderedGroup) + EVT_TOOL(TOOL_NEW_LAYER2D, V4StudioFrame::OnNewLayer2D) + EVT_TOOL(TOOL_NEW_TRANSFORM2D, V4StudioFrame::OnNewTransform2D) + EVT_TOOL(TOOL_NEW_TRANSFORMMATRIX2D, V4StudioFrame::OnNewTransformMatrix2D) + EVT_TOOL(TOOL_NEW_COLOR_TRANSFORM, V4StudioFrame::OnNewColorTransform) + EVT_TOOL(TOOL_NEW_SHAPE, V4StudioFrame::OnNewShape) + EVT_TOOL(TOOL_NEW_APPEARANCE, V4StudioFrame::OnNewAppearance) + EVT_TOOL(TOOL_NEW_LINEAR_GRADIENT, V4StudioFrame::OnNewLinearGradient) + EVT_TOOL(TOOL_NEW_RADIAL_GRADIENT, V4StudioFrame::OnNewRadialGradient) + EVT_TOOL(TOOL_NEW_MATERIAL2D, V4StudioFrame::OnNewMaterial2D) + EVT_TOOL(TOOL_NEW_LINEPROPS, V4StudioFrame::OnNewLineProps) + EVT_TOOL(TOOL_NEW_XLINEPROPS, V4StudioFrame::OnNewXLineProps) + EVT_TOOL(TOOL_NEW_RECT, V4StudioFrame::OnNewRect) + EVT_TOOL(TOOL_NEW_CIRCLE, V4StudioFrame::OnNewCircle) + EVT_TOOL(TOOL_NEW_TEXT, V4StudioFrame::OnNewText) + EVT_TOOL(TOOL_NEW_FONTSTYLE, V4StudioFrame::OnNewFontStyle) + EVT_TOOL(TOOL_NEW_SOUND, V4StudioFrame::OnNewSound) + EVT_TOOL(TOOL_NEW_BACKGROUND2D, V4StudioFrame::OnNewBackground2D) + EVT_TOOL(TOOL_NEW_IMAGE, V4StudioFrame::OnNewImageTexture) + EVT_TOOL(TOOL_NEW_MOVIE, V4StudioFrame::OnNewMovieTexture) +END_EVENT_TABLE() + +// Creates New Scene +void V4StudioFrame::OnNew(wxCommandEvent &event) +{ + m_pV4sm = new V4SceneManager(this); + m_pV4sm->LoadNew(); + + m_pMainToolbar->EnableTool(TOOL_ADD_TO_TL, true); + m_pMainToolbar->EnableTool(TOOL_VIEW_DICT, true); + m_pMainToolbar->EnableTool(TOOL_FILE_SAVE, true); + m_pFileMenu->Enable(MENU_FILE_SAVE, true); + m_pMainToolbar->EnableTool(TOOL_FILE_CLOSE, true); + m_pFileMenu->Enable(MENU_FILE_CLOSE, true); + m_pMainToolbar->EnableTool(TOOL_FILE_OPEN, false); + m_pFileMenu->Enable(MENU_FILE_OPEN, false); + m_pMainToolbar->EnableTool(TOOL_FILE_NEW, false); + m_pFileMenu->Enable(MENU_FILE_NEW, false); + m_pFileMenu->Enable(CHANGE_SIZE_DIALOG, true); + m_pFileMenu->Enable(CHANGE_LENGTH, true); + m_pFileMenu->Enable(CHANGE_FRAMERATE, true); + + Layout(); + SetStatusText("New scene", 0); + SceneGraphChanged(); + + OnChangeSize(event); +} + +// Opens an existing file +void V4StudioFrame::OnFileOpen(wxCommandEvent &event) +{ + wxFileDialog *dlg = new wxFileDialog(this, "Open a bt scene", + "", "", "BT Files (*.bt)|*.bt|MP4 Files (*.mp4)|*.mp4|XMT Files (*.xmt)|*.xmt|SWF Files (*.swf)|*.swf|All files (*.*)|*.*", + wxOPEN, wxDefaultPosition); + if ( dlg->ShowModal() == wxID_OK ) { + if (m_pV4sm) delete m_pV4sm; + m_pV4sm = new V4SceneManager(this); + m_pV4sm->LoadFile(dlg->GetPath().c_str()); + + m_pMainToolbar->EnableTool(TOOL_ADD_TO_TL, true); + m_pMainToolbar->EnableTool(TOOL_VIEW_DICT, true); + m_pMainToolbar->EnableTool(TOOL_FILE_SAVE, true); + m_pFileMenu->Enable(MENU_FILE_SAVE, true); + m_pMainToolbar->EnableTool(TOOL_FILE_CLOSE, true); + m_pFileMenu->Enable(MENU_FILE_CLOSE, true); + m_pMainToolbar->EnableTool(TOOL_FILE_OPEN, false); + m_pFileMenu->Enable(MENU_FILE_OPEN, false); + m_pMainToolbar->EnableTool(TOOL_FILE_NEW, false); + m_pFileMenu->Enable(MENU_FILE_NEW, true); + m_pFileMenu->Enable(CHANGE_SIZE_DIALOG, true); + m_pFileMenu->Enable(CHANGE_LENGTH, true); + m_pFileMenu->Enable(CHANGE_FRAMERATE, true); + + Layout(); + SetStatusText(dlg->GetFilename(), 0); + SceneGraphChanged(); + } + dlg->Destroy(); +} + +void V4StudioFrame::OnSave(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + wxFileDialog *dlg = new wxFileDialog(this, "Save the scene to an mp4 file", + "", "", "MPEG-4 Files (*.mp4)|*.mp4|All files (*.*)|*.*", + wxSAVE, wxDefaultPosition); + if ( dlg->ShowModal() == wxID_OK ) + { + m_pV4sm->SaveFile(dlg->GetPath().c_str()); + SetStatusText(dlg->GetFilename(), 0); + } + dlg->Destroy(); +} + +void V4StudioFrame::OnClose(wxCommandEvent &event) +{ + if (m_pV4sm) delete m_pV4sm; + m_pV4sm = NULL; + + m_pMainToolbar->EnableTool(TOOL_ADD_TO_TL, false); + m_pMainToolbar->EnableTool(TOOL_VIEW_DICT, false); + m_pMainToolbar->EnableTool(TOOL_FILE_SAVE, false); + m_pFileMenu->Enable(MENU_FILE_SAVE, false); + m_pMainToolbar->EnableTool(TOOL_FILE_CLOSE, false); + m_pFileMenu->Enable(MENU_FILE_CLOSE, false); + m_pMainToolbar->EnableTool(TOOL_FILE_OPEN, true); + m_pFileMenu->Enable(MENU_FILE_OPEN, true); + m_pMainToolbar->EnableTool(TOOL_FILE_NEW, true); + m_pFileMenu->Enable(MENU_FILE_NEW, true); + m_pFileMenu->Enable(CHANGE_SIZE_DIALOG, false); + m_pFileMenu->Enable(CHANGE_LENGTH, false); + m_pFileMenu->Enable(CHANGE_FRAMERATE, false); + + m_clipboardNode = NULL; + m_clipboardParentNode = NULL; + m_pMainToolbar->EnableTool(TOOL_EDIT_CUT, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_COPY, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE_USE, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_DELETE, false); + Layout(); + SceneGraphChanged(); +} + +// Quits application +// TODO : memory leaks ! +void V4StudioFrame::OnQuit(wxCommandEvent &event) +{ + Close(FALSE); +} + +/**********************************************/ +/* Tools Menu methods */ +/**********************************************/ +void V4StudioFrame::OnLowLevelTools(wxCommandEvent &event) +{ + m_uSelectedNodeToolBar = 0; + do_layout(); +} + +void V4StudioFrame::OnHighLevelTools(wxCommandEvent &event) +{ + m_uSelectedNodeToolBar = 1; + do_layout(); +} + +/**********************************************/ +/* Functions to add components to the scene */ +/**********************************************/ +void V4StudioFrame::OnNewOrderedGroup(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *top = m_pV4sm->GetRootNode(); + if (!top) { + top = m_pV4sm->SetTopNode(TAG_MPEG4_OrderedGroup); + m_selection = top; + m_parentSelection = NULL; + } else { + GF_Node *og = m_pV4sm->NewNode(TAG_MPEG4_OrderedGroup); + gf_node_insert_child(m_selection, og, -1); + gf_node_register(og, m_selection); + m_parentSelection = m_selection; + m_selection = og; + } + Update(); +} + +void V4StudioFrame::OnNewLayer2D(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *top = m_pV4sm->GetRootNode(); + if (!top) { + top = m_pV4sm->SetTopNode(TAG_MPEG4_Layer2D); + m_selection = top; + m_parentSelection = NULL; + } else { + GF_Node *l2d = m_pV4sm->NewNode(TAG_MPEG4_Layer2D); + gf_node_insert_child(m_selection, l2d, -1); + gf_node_register(l2d, m_selection); + m_parentSelection = m_selection; + m_selection = l2d; + } + Update(); +} + +void V4StudioFrame::OnNewText(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *text = m_pV4sm->NewNode(TAG_MPEG4_Text); + M_Shape *shape = (M_Shape *)m_selection; + shape->geometry = text; + gf_node_register(text, m_selection); + m_parentSelection = m_selection; + m_selection = text; + Update(); +} + +void V4StudioFrame::OnNewFontStyle(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *fs = m_pV4sm->NewNode(TAG_MPEG4_FontStyle); + M_Text *text = (M_Text *)m_selection; + text->fontStyle = fs; + gf_node_register(fs, m_selection); + m_selection = fs; + m_parentSelection = (GF_Node *)text; + Update(); +} + +void V4StudioFrame::OnNewRect(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *rect = m_pV4sm->NewNode(TAG_MPEG4_Rectangle); + M_Shape *shape = (M_Shape *)m_selection; + shape->geometry = rect; + gf_node_register(rect, m_selection); + m_parentSelection = m_selection; + m_selection = rect; + Update(); +} + +void V4StudioFrame::OnNewCircle(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *circle = m_pV4sm->NewNode(TAG_MPEG4_Circle); + M_Shape *shape = (M_Shape *)m_selection; + shape->geometry = circle; + gf_node_register(circle, m_selection); + ((M_Circle *) circle)->radius = 75; + m_parentSelection = m_selection; + m_selection = circle; + Update(); +} + +void V4StudioFrame::OnNewTransform2D(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *t2d = m_pV4sm->NewNode(TAG_MPEG4_Transform2D); + if (!m_selection) m_selection = m_pV4sm->GetRootNode(); + gf_node_insert_child(m_selection, t2d, -1); + gf_node_register(t2d, m_selection); + m_parentSelection = m_selection; + m_selection = t2d; + Update(); +} + +void V4StudioFrame::OnNewColorTransform(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *ct = m_pV4sm->NewNode(TAG_MPEG4_ColorTransform); + if (!m_selection) m_selection = m_pV4sm->GetRootNode(); + gf_node_insert_child(m_selection, ct, -1); + gf_node_register(ct, m_selection); + m_parentSelection = m_selection; + m_selection = ct; + Update(); +} + +void V4StudioFrame::OnNewTransformMatrix2D(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *tm2d = m_pV4sm->NewNode(TAG_MPEG4_TransformMatrix2D); + if (!m_selection) m_selection = m_pV4sm->GetRootNode(); + gf_node_insert_child(m_selection, tm2d, -1); + gf_node_register(tm2d, m_selection); + m_parentSelection = m_selection; + m_selection = (GF_Node *)tm2d; + Update(); +} + +void V4StudioFrame::OnNewShape(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *shape = m_pV4sm->NewNode(TAG_MPEG4_Shape); + gf_node_insert_child(m_selection, shape, -1); + gf_node_register( shape, m_selection); + m_parentSelection = m_selection; + m_selection = (GF_Node *)shape; + Update(); +} + +void V4StudioFrame::OnNewAppearance(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *app = m_pV4sm->NewNode(TAG_MPEG4_Appearance); + M_Shape *shape = (M_Shape *)m_selection; + shape->appearance = app; + gf_node_register(app, m_selection); + m_parentSelection = m_selection; + m_selection = app; + Update(); +} + +void V4StudioFrame::OnNewLinearGradient(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *lg = m_pV4sm->NewNode(TAG_MPEG4_LinearGradient); + M_Appearance *app = (M_Appearance *)m_selection; + app->texture = lg; + gf_node_register(lg, m_selection); + m_parentSelection = m_selection; + m_selection = (GF_Node *)lg; + Update(); +} + +void V4StudioFrame::OnNewRadialGradient(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *rg = m_pV4sm->NewNode(TAG_MPEG4_RadialGradient); + M_Appearance *app = (M_Appearance *)m_selection; + app->texture = rg; + gf_node_register(rg, m_selection); + m_parentSelection = m_selection; + m_selection = rg; + Update(); +} + + +void V4StudioFrame::OnNewMaterial2D(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *mat2d = m_pV4sm->NewNode(TAG_MPEG4_Material2D); + M_Appearance *app = (M_Appearance *)m_selection; + app->material = mat2d; + gf_node_register(mat2d, m_selection); + m_parentSelection = m_selection; + + m_selection = mat2d; + Update(); +} + +void V4StudioFrame::OnNewLineProps(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *lp = m_pV4sm->NewNode(TAG_MPEG4_LineProperties); + M_Material2D *mat2d= (M_Material2D *)m_selection; + mat2d->lineProps = lp; + gf_node_register(lp, m_selection); + m_parentSelection = m_selection; + m_selection = lp; + Update(); +} + +void V4StudioFrame::OnNewXLineProps(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *lp = m_pV4sm->NewNode(TAG_MPEG4_XLineProperties); + M_Material2D *mat2d= (M_Material2D *)m_selection; + mat2d->lineProps = lp; + gf_node_register(lp, m_selection); + m_parentSelection = m_selection; + m_selection = lp; + Update(); +} + +void V4StudioFrame::OnNewSound(wxCommandEvent &event) +{ +} + +void V4StudioFrame::OnNewBackground2D(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *b2d = m_pV4sm->NewNode(TAG_MPEG4_Background2D); + gf_node_insert_child(m_selection, b2d, -1); + gf_node_register(b2d, m_selection); + m_parentSelection = m_selection; + m_selection = b2d; + Update(); +} + +void V4StudioFrame::OnNewImageTexture(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *it = m_pV4sm->NewNode(TAG_MPEG4_ImageTexture); + M_Appearance *app = (M_Appearance *) m_selection; + app->texture = it; + gf_node_register(it, m_selection); + m_parentSelection = m_selection; + m_selection = it; + Update(); +} + +void V4StudioFrame::OnNewMovieTexture(wxCommandEvent &event) +{ + if (!m_pV4sm) return; + GF_Node *mt = m_pV4sm->NewNode(TAG_MPEG4_MovieTexture); + M_Appearance *app = (M_Appearance *)m_selection; + app->texture = mt; + gf_node_register(mt, m_selection); + m_parentSelection = m_selection; + m_selection = mt; + Update(); +} + +void V4StudioFrame::UpdateSelection(GF_Node *node, GF_Node *parent) +{ + SetSelection(node); + SetParentSelection(parent); + m_pMainToolbar->EnableTool(TOOL_EDIT_CUT, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_COPY, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_DELETE, true); + UpdateToolBar(); + fieldView->SetNode(node); + fieldView->Create(); +} + + +// Update display +void V4StudioFrame::Update() +{ + if (m_pV4sm){ + if (m_pV4sm->GetGPACPanel()) m_pV4sm->GetGPACPanel()->Update(); + treeView->Refresh(m_pV4sm->GetRootNode()); + } else { + treeView->Refresh(NULL); + } + UpdateToolBar(); +} + +void V4StudioFrame::UpdateToolBar() +{ + bool enableGeometry = false, + enableAppearance = false, + enableMaterial = false, + enableGroupNode = false, + enableLineProps = false, + enableFontStyle = false, + enable2DNode = false, + enableTexture = false, + enableTopNode = false, + enableBackground = false; + + // a scene must have been created to enable the controls + if (m_pV4sm && m_pV4sm->GetInlineScene()) { + if (m_selection != NULL) { + u32 tag = gf_node_get_tag(m_selection); + GF_Node * node = m_selection; + enableBackground = true; + switch (tag) { + case TAG_MPEG4_Switch: + case TAG_MPEG4_OrderedGroup: + case TAG_MPEG4_Layer2D: + case TAG_MPEG4_Transform2D: + case TAG_MPEG4_TransformMatrix2D: + case TAG_MPEG4_ColorTransform: + enableGroupNode = true; + enable2DNode = true; + break; + case TAG_MPEG4_Shape: + if (!((M_Shape *)node)->geometry) enableGeometry = true; + if (!((M_Shape *)node)->appearance) enableAppearance = true; + break; + case TAG_MPEG4_Appearance: + if (!((M_Appearance *)node)->material) enableMaterial = true; + if (!((M_Appearance *)node)->texture) enableTexture = true; + break; + case TAG_MPEG4_Material2D: + if (!((M_Material2D *)node)->lineProps) enableLineProps = true; + break; + case TAG_MPEG4_Text: + if (!((M_Text *)node)->fontStyle) enableFontStyle = true; + break; + case TAG_MPEG4_Rectangle: + case TAG_MPEG4_Circle: + case TAG_MPEG4_ImageTexture: + case TAG_MPEG4_MovieTexture: + case TAG_MPEG4_Background2D: + break; + } + } else { + enableTopNode = true; + } + } + + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_ORDEREDGROUP, enableGroupNode || enable2DNode || enableTopNode); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_LAYER2D, enableGroupNode || enable2DNode || enableTopNode); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_TRANSFORM2D, enableGroupNode || enable2DNode); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_COLOR_TRANSFORM, enableGroupNode || enable2DNode); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_TRANSFORMMATRIX2D, enableGroupNode || enable2DNode); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_SHAPE, enable2DNode); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_RECT, enableGeometry); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_CIRCLE, enableGeometry); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_IFS2D, enableGeometry); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_ILS2D, enableGeometry); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_TEXT, enableGeometry); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_MOVIE, enableTexture); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_IMAGE, enableTexture); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_SOUND, enable2DNode); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_APPEARANCE, enableAppearance); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_MATERIAL2D, enableMaterial); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_LINEAR_GRADIENT, enableTexture); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_RADIAL_GRADIENT, enableTexture); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_LINEPROPS, enableLineProps); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_XLINEPROPS, enableLineProps); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_FONTSTYLE, enableFontStyle); + m_pLowLevelNodeToolbar->EnableTool(TOOL_NEW_BACKGROUND2D, enableBackground); +} + +// Cut and paste functions +void V4StudioFrame::OnEditCut(wxCommandEvent &WXUNUSED(event)) +{ + if (!m_selection) return; + + m_clipboardNode = m_selection; + m_clipboardParentNode = m_parentSelection; + gf_node_remove_child(m_parentSelection, m_selection); + m_pMainToolbar->EnableTool(TOOL_EDIT_CUT, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_COPY, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE_USE, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_DELETE, false); + + Update(); +} + +void V4StudioFrame::OnEditDelete(wxCommandEvent &WXUNUSED(event)) +{ + if (!m_selection) return; + + gf_node_remove_child(m_parentSelection, m_selection); + m_clipboardNode = NULL; + m_clipboardParentNode = NULL; + m_pMainToolbar->EnableTool(TOOL_EDIT_CUT, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_COPY, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE_USE, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_DELETE, false); + + Update(); +} + +void V4StudioFrame::OnEditCopy(wxCommandEvent &WXUNUSED(event)) +{ + if (!m_selection) return; + + m_clipboardNode = m_selection; + m_clipboardParentNode = m_parentSelection; + m_pMainToolbar->EnableTool(TOOL_EDIT_CUT, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_COPY, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE_USE, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_DELETE, false); + + Update(); +} + +void V4StudioFrame::OnEditPaste(wxCommandEvent &WXUNUSED(event)) +{ + if (!m_pV4sm) return; + if (m_clipboardNode == NULL) return; + + GF_Node *copy = m_pV4sm->CopyNode(m_clipboardNode, m_parentSelection, true); + gf_node_insert_child(m_selection, copy, -1); + m_pMainToolbar->EnableTool(TOOL_EDIT_CUT, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_COPY, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE_USE, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_DELETE, false); + + Update(); +} + +void V4StudioFrame::OnEditPasteUse(wxCommandEvent &WXUNUSED(event)) +{ + if (!m_pV4sm) return; + if (m_clipboardNode == NULL) return; + + GF_Node *copy = m_pV4sm->CopyNode(m_clipboardNode, m_parentSelection, false); + gf_node_register(copy, m_selection); + gf_node_insert_child(m_selection, copy, -1); + m_pMainToolbar->EnableTool(TOOL_EDIT_CUT, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_COPY, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE, true); + m_pMainToolbar->EnableTool(TOOL_EDIT_PASTE_USE, false); + m_pMainToolbar->EnableTool(TOOL_EDIT_DELETE, false); + + Update(); +} + +// OnAddToTimeLine -- adds a node in the timeline (meaning creating a node ID and adding it to the dictionnary and the pool) +void V4StudioFrame::OnAddToTimeLine(wxCommandEvent &event) { + if (!m_pV4sm) return; + if (!m_selection) return; // don't add nothing + + // if object has no node id then create one + if (! gf_node_get_id(m_selection) ) + m_pV4sm->CreateIDandAddToPool(m_selection); + + // Adds the node to the dictionnary +// m_pV4sm->AddToDictionnary(m_selection); + + // calls timeline function to add the line + char c[50]; + strcpy(c, gf_node_get_name(m_selection)); + timeLine->AddLine( gf_node_get_id(m_selection), wxString(c) ); +} + + +// NextFrame -- goto next frame +void V4StudioFrame::NextFrame(wxCommandEvent &event) { + //gf_term_play_from_time(gpacPanel->GetMPEG4Terminal(), 1000); + m_selection = NULL; + Update(); +} + + +// SwitchView -- switches between scene editing and dictionnary editing +void V4StudioFrame::SwitchView(wxCommandEvent &event) { + SetEditDict(!editDict); +} + + + +/****** Access Functions ******/ + +// changes time to the frame specified, implies playing the scene up to that point +void V4StudioFrame::SetFrame(unsigned long _frame) { + // TODO : + m_frame = _frame; +} + + +// SetEditDict -- edit the dictionnary or not +void V4StudioFrame::SetEditDict(bool _editDict) { + + // does nothing if no scene + if (!m_pV4sm) return; + if ( ! m_pV4sm->GetSceneGraph() ) return; + + editDict = _editDict; + + if (editDict) treeView->Refresh(m_pV4sm->GetDictionnary()); + else treeView->Refresh(m_pV4sm->GetRootNode()); +} + + +// GetEditDict -- Access to edit dict +bool V4StudioFrame::GetEditDict() const { + return editDict; +} diff --git a/applications/v4studio/V4StudioFrame.h b/applications/v4studio/V4StudioFrame.h new file mode 100644 index 0000000..f0c79b0 --- /dev/null +++ b/applications/v4studio/V4StudioFrame.h @@ -0,0 +1,211 @@ +#ifndef V4STUDIO_FRAME_H +#define V4STUDIO_FRAME_H + +#include "safe_include.h" // include m4_tools.h + +#include +#include +#include + +// UI +#include "V4StudioTree.h" +#include "V4FieldList.h" +#include "V4TimeLine\V4TimeLine.h" +#include "V4CommandPanel.h" + +// Scene data +#include "V4SceneManager.h" + +enum { + // File Menu commands + MENU_FILE_NEW, + MENU_FILE_OPEN, + MENU_FILE_SAVE, + MENU_FILE_CLOSE, + MENU_FILE_QUIT, + + // Tools Menu commands + MENU_TOOL_SHOW_LOWLEVEL, + MENU_TOOL_SHOW_HIGHLEVEL, + + // Generic Toolbar commands + TOOL_FILE_NEW, + TOOL_FILE_OPEN, + TOOL_FILE_SAVE, + TOOL_FILE_CLOSE, + TOOL_FILE_PREVIEW, + TOOL_EDIT_CUT, + TOOL_EDIT_COPY, + TOOL_EDIT_PASTE, + TOOL_EDIT_PASTE_USE, + TOOL_EDIT_DELETE, + TOOL_EDIT_UNDO, + TOOL_EDIT_REDO, + TOOL_FRAME_COMBO, + TOOL_ADD_TO_TL, + TOOL_NEXT_FRAME, + TOOL_VIEW_DICT, + + CHANGE_SIZE_DIALOG, + CHANGE_FRAMERATE, + CHANGE_LENGTH, + + /* High level node creation */ + TOOL_NEW_2DSCENE, + + /* Low level node creations */ + // 2D node Toolbar commands + TOOL_NEW_ORDEREDGROUP, + TOOL_NEW_LAYER2D, + TOOL_NEW_TRANSFORM2D, + TOOL_NEW_TRANSFORMMATRIX2D, + TOOL_NEW_COLOR_TRANSFORM, + TOOL_NEW_SHAPE, + + // Appearance and sub-appearance Toolbar commands + TOOL_NEW_APPEARANCE, + TOOL_NEW_MATERIAL2D, + TOOL_NEW_LINEPROPS, + TOOL_NEW_XLINEPROPS, + TOOL_NEW_FONTSTYLE, + TOOL_NEW_LINEAR_GRADIENT, + TOOL_NEW_RADIAL_GRADIENT, + + // Geometry Toolbar commands + TOOL_NEW_RECT, + TOOL_NEW_CIRCLE, + TOOL_NEW_IFS2D, + TOOL_NEW_ILS2D, + TOOL_NEW_TEXT, + + // Media Toolbar commands + TOOL_NEW_BACKGROUND2D, + TOOL_NEW_MOVIE, + TOOL_NEW_IMAGE, + TOOL_NEW_SOUND +}; + +class V4StudioFrame: public wxFrame { +public: + + // Constructor / Desctructor + V4StudioFrame(); + ~V4StudioFrame(); + + /* generic UI functions */ + + /* File Menu functions */ + void OnNew(wxCommandEvent &event); + void OnFileOpen(wxCommandEvent &event); + void OnSave(wxCommandEvent &event); + void OnClose(wxCommandEvent &event); + void OnQuit(wxCommandEvent &event); + void OnChangeSize(wxCommandEvent &event); + void OnChangeFrameRate(wxCommandEvent &event); + void OnChangeLength(wxCommandEvent &event); + + /* Tools Menu functions */ + void OnLowLevelTools(wxCommandEvent &event); + void OnHighLevelTools(wxCommandEvent &event); + + /* edition toolbar functions */ + void OnEditCut(wxCommandEvent &event); + void OnEditCopy(wxCommandEvent &event); + void OnEditPaste(wxCommandEvent &event); + void OnEditPasteUse(wxCommandEvent &event); + void OnEditDelete(wxCommandEvent &event); + void OnAddToTimeLine(wxCommandEvent &event); + void OnCombo(wxCommandEvent& event); + void NextFrame(wxCommandEvent &event); + void SwitchView(wxCommandEvent &event); + + /* new components toolbar functions */ + void OnNewLayer2D(wxCommandEvent &event); + void OnNewOrderedGroup(wxCommandEvent &event); + void OnNewTransform2D(wxCommandEvent &event); + void OnNewTransformMatrix2D(wxCommandEvent &event); + void OnNewColorTransform(wxCommandEvent &event); + void OnNewShape(wxCommandEvent &event); + void OnNewAppearance(wxCommandEvent &event); + void OnNewMaterial2D(wxCommandEvent &event); + void OnNewLinearGradient(wxCommandEvent &event); + void OnNewRadialGradient(wxCommandEvent &event); + void OnNewLineProps(wxCommandEvent &event); + void OnNewXLineProps(wxCommandEvent &event); + void OnNewText(wxCommandEvent &event); + void OnNewFontStyle(wxCommandEvent &event); + void OnNewRect(wxCommandEvent &event); + void OnNewCircle(wxCommandEvent &event); + void OnNewSound(wxCommandEvent &event); + void OnNewBackground2D(wxCommandEvent &event); + void OnNewImageTexture(wxCommandEvent &event); + void OnNewMovieTexture(wxCommandEvent &event); + + + /* display update functions */ + void Update(); + void UpdateSelection(GF_Node *node, GF_Node *parent); + void SetSelection(GF_Node *node) { m_selection = node; } + void SetParentSelection(GF_Node *node) { m_parentSelection = node; } + void UpdateToolBar(); + void SceneGraphChanged(); + + + /* access to private members */ + + // UI objects + V4FieldList * GetFieldView() { return fieldView; } + V4StudioTree * GetTreeView() { return treeView; } + V4CommandPanel * GetCmdPanel() { return cmdPanel; } + V4TimeLine * GetTimeLine() { return timeLine; } + V4SceneManager * GetV4SceneManager() { return m_pV4sm; } + + // variables + void SetFrame(unsigned long _frame); // changes time to the frame specified, implies playing the scene up to that point + void SetEditDict(bool editDict); // edit the dictionnary or not + bool GetEditDict() const; + + +private: + void set_properties(); + void do_layout(); + +protected: + + DECLARE_EVENT_TABLE() + + // select, cut and paste members + GF_Node *m_selection; + GF_Node *m_parentSelection; + GF_Node *m_clipboardNode; + GF_Node *m_clipboardParentNode; + + // scene data + V4SceneManager *m_pV4sm; + + // UI objects + V4StudioTree *treeView; + V4FieldList *fieldView; + V4TimeLine *timeLine; + V4CommandPanel *cmdPanel; + + wxMenu *m_pFileMenu; + wxMenu *m_pToolsMenu; + wxStatusBar *m_pStatusbar; + // The Main bar that contains the new, load, save, cut, copy ... + wxToolBar *m_pMainToolbar; + + // The tool bar to create all the MPEG-4 nodes + wxToolBar *m_pLowLevelNodeToolbar; + wxToolBar *m_pHighLevelNodeToolbar; + u32 m_uSelectedNodeToolBar; + + // editing states machine + bool editDict; // true if we are editing the dictionnary + unsigned long m_frame; // frame currently selected + +}; + + +#endif + diff --git a/applications/v4studio/V4StudioTree.cpp b/applications/v4studio/V4StudioTree.cpp new file mode 100644 index 0000000..4e06364 --- /dev/null +++ b/applications/v4studio/V4StudioTree.cpp @@ -0,0 +1,386 @@ +#include "safe_include.h" + +// For compilers that supports precompilation , includes "wx/wx.h" +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "V4StudioTree.h" +#include "V4StudioFrame.h" + +#include "V4SceneManager.h" // DICTNAME constant + +#include "wx/image.h" +#include "wx/imaglist.h" +#include "wx/treectrl.h" + +#include + +#include "treeIcon1.xpm" +#include "treeIcon2.xpm" +#include "treeIcon3.xpm" +#include "treeIcon4.xpm" +#include "treeIcon5.xpm" + +#include + + +BEGIN_EVENT_TABLE(V4StudioTree, wxTreeCtrl) + EVT_TREE_SEL_CHANGED(-1, V4StudioTree::OnSelChanged) + EVT_TREE_ITEM_RIGHT_CLICK(-1, V4StudioTree::OnItemRightClick) + EVT_TREE_BEGIN_DRAG(-1, V4StudioTree::OnBeginDrag) + EVT_TREE_END_DRAG(-1, V4StudioTree::OnEndDrag) +END_EVENT_TABLE() + +GF_Err V4StudioTreeItemData::GetField(GF_FieldInfo *f) +{ + if (!f || fieldIndex < 0) return GF_BAD_PARAM; + return gf_node_get_field(parent, fieldIndex, f); +} + +V4StudioTree::V4StudioTree(wxWindow *parent_, wxSize size,V4FieldList *fieldView) : + wxTreeCtrl(parent_, -1, wxDefaultPosition, size, wxTR_DEFAULT_STYLE) +{ + CreateImageList(); + AddRoot("Scene", V4StudioTree::TreeCtrlIcon_Folder, V4StudioTree::TreeCtrlIcon_FolderOpened); + m_transformNode = NULL; + m_selectedNode = NULL; + + parent = (V4StudioFrame *) parent_; +} + + +// Refresh -- redraws the tree starting at the specified node +void V4StudioTree::Refresh(GF_Node * node) +{ + + // check if we have to display the dictionnary or not + if (parent->GetEditDict()) + node = parent->GetV4SceneManager()->GetDictionnary(); + + + wxTreeItemId rootId = GetRootItem(); + CollapseAndReset(rootId); + if (node) { + AddNodesToItem(rootId, node, -1, -1); + Expand(rootId); + } +} + +void V4StudioTree::AddNodesToItem(wxTreeItemId parentItemId, GF_Node * node, s32 fieldIndex, s32 position) +{ + GF_FieldInfo field; + u32 count, i, j; + char *name; + + if (!node) return; + + // displays the name and the defname it exists + u32 s = strlen(gf_node_get_class_name(node)); + + if (gf_node_get_name(node)) { + s += strlen(gf_node_get_name(node)); + name = new char[s+4]; + strcpy(name, gf_node_get_class_name(node)); + strcat(name, " - "); + strcat(name, gf_node_get_name(node)); + } else { + name = new char[s+1]; + strcpy(name, gf_node_get_class_name(node)); + } + + GF_Node * parent = NULL; + + V4StudioTreeItemData *parentItemData = (V4StudioTreeItemData *)GetItemData(parentItemId); + if (parentItemData != NULL) parent = parentItemData->GetNode(); + + V4StudioTreeItemData * currentItemData = new V4StudioTreeItemData(node, parent, fieldIndex, position); + wxTreeItemId nodeItemId; + if (position == -1) nodeItemId = AppendItem(parentItemId, wxString(name), -1, -1, currentItemData); + else nodeItemId = InsertItem(parentItemId, position, wxString(name), -1, -1, currentItemData); + + delete [] name; + name = NULL; + + count = gf_node_get_field_count(node); + for (i=0;iAdd(icons[i]); + else images->Add(wxBitmap(wxBitmap(icons[i]).ConvertToImage().Rescale(size, size))); + } + AssignImageList(images); +} + +void V4StudioTree::OnSelChanged(wxTreeEvent& event) +{ + wxKeyEvent kevt = event.GetKeyEvent(); + + wxTreeItemId itemId = event.GetItem(); + m_selectedItem = itemId; + if (itemId.IsOk()) { + V4StudioTreeItemData *itemData = (V4StudioTreeItemData *)GetItemData(itemId); + if (!itemData) { + event.Skip(); + return; + } + GF_Node *itemNode = itemData->GetNode(); + GF_Node *itemParentNode = itemData->GetNodeParent(); + if (!itemNode) { + event.Skip(); + return; + } + V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent(); + mainFrame->UpdateSelection(itemNode, itemParentNode); + } +} + +void V4StudioTree::SetSelectedItem(GF_Node *node) +{ + if (m_selectedNode == node) return; + wxTreeItemId rootId = GetRootItem(); + wxTreeItemId itemId = FindNodeItem(rootId, node); + m_transformNode = FindTransformNode(itemId); + itemId = GetItemParent(itemId); + if (itemId.IsOk()) { + V4StudioTreeItemData *data = (V4StudioTreeItemData *)GetItemData(itemId); + if (data != NULL) { + V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent(); + GF_Node *itemNode = data->GetNode(); + m_selectedNode = itemNode; + GF_Node *itemParentNode = data->GetNodeParent(); + mainFrame->UpdateSelection(itemNode, itemParentNode); + } + SelectItem(itemId); + } +} + +wxTreeItemId V4StudioTree::FindNodeItem(wxTreeItemId itemId, GF_Node *node) +{ + void* cookie; + V4StudioTreeItemData *data = (V4StudioTreeItemData *)GetItemData(itemId); + if (data != NULL) { + if (data->GetNode() == node) return itemId; + } + wxTreeItemId ret = (wxTreeItemId)0l; + wxTreeItemId child = GetFirstChild(itemId, cookie); + while (child.IsOk()) { + wxTreeItemId ret = FindNodeItem(child, node); + if (ret.IsOk()) return ret; + child = GetNextChild(itemId, cookie); + } + return ret; +} + +void V4StudioTree::OnItemRightClick(wxTreeEvent &event) +{ + wxTreeItemId itemId = event.GetItem(); +} + +void V4StudioTree::OnBeginDrag(wxTreeEvent& event) +{ + // need to explicitly allow drag + if ( event.GetItem() != GetRootItem() ){ + m_draggedItem = event.GetItem(); + V4StudioTreeItemData *draggedData = (V4StudioTreeItemData *)GetItemData(m_draggedItem); + event.Allow(); + } else { + wxLogMessage(wxT("OnBeginDrag: this item can't be dragged.")); + } +} + +void V4StudioTree::OnEndDrag(wxTreeEvent& event) +{ + wxTreeItemId itemSrc = m_draggedItem, itemDst = event.GetItem(), dstParentItem = GetItemParent(itemDst); + m_draggedItem = (wxTreeItemId)0l; + + V4StudioTreeItemData *srcData = (V4StudioTreeItemData *)GetItemData(itemSrc); + GF_FieldInfo srcField; + srcData->GetField(&srcField); + // Removal of the src item from its parent field + switch (srcField.fieldType) { + case GF_SG_VRML_SFNODE: + if (* (GF_Node **) srcField.far_ptr) {} + break; + case GF_SG_VRML_MFNODE: + { + GF_ChildNodeItem *nodes = (* (GF_ChildNodeItem **) srcField.far_ptr); + gf_node_list_del_child_idx(&nodes, srcData->GetPosition()); + } + break; + default: + break; + } + + GF_Node *srcNode = srcData->GetNode(); + GF_FieldInfo dstField; + V4StudioTreeItemData *dstData = (V4StudioTreeItemData *)GetItemData(itemDst); + dstData->GetField(&dstField); + // Addition of the src item prior to the dest item + switch (dstField.fieldType) { + case GF_SG_VRML_SFNODE: + if (* (GF_Node **) dstField.far_ptr) {} + break; + case GF_SG_VRML_MFNODE: + { + GF_ChildNodeItem *nodes = (* (GF_ChildNodeItem **) dstField.far_ptr); + gf_node_list_insert_child(&nodes, srcNode, dstData->GetPosition()); + gf_node_dirty_set(dstData->GetNode(), 0, 1); + } + break; + default: + break; + } + + GF_Node *dstParentNode = dstData->GetNodeParent(); + + Delete(itemSrc); + AddNodesToItem(dstParentItem, srcNode, dstData->GetFieldIndex(), dstData->GetPosition()); + V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent(); + mainFrame->UpdateSelection(srcNode,dstData->GetNodeParent()); + mainFrame->Update(); +} + +GF_Node *V4StudioTree::FindTransformNode(wxTreeItemId itemId) +{ + GF_Node *transformNode = NULL; + while (true) { + if (itemId.IsOk()) { + V4StudioTreeItemData *data = (V4StudioTreeItemData *)GetItemData(itemId); + if (data != NULL) { + GF_Node *itemNode = data->GetNode(); + u32 tag = gf_node_get_tag(itemNode); + if (tag == TAG_MPEG4_Transform2D || tag == TAG_MPEG4_TransformMatrix2D) { + transformNode = itemNode; + break; + } + } else { + break; + } + } else { + break; + } + itemId = GetItemParent(itemId); + } + return transformNode; +} + +void V4StudioTree::Translate(int dX, int dY) +{ + if (m_transformNode) { + GF_FieldInfo field; + u32 tag = gf_node_get_tag(m_transformNode); + if (tag == TAG_MPEG4_Transform2D){ + gf_node_get_field(m_transformNode, 7, &field); + ((SFVec2f *)field.far_ptr)->x += dX; + ((SFVec2f *)field.far_ptr)->y += dY; + } else { + gf_node_get_field(m_transformNode, 5, &field); + *((SFFloat *)field.far_ptr) += dX; + gf_node_get_field(m_transformNode, 8, &field); + *((SFFloat *)field.far_ptr) += dY; + } + V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent(); + mainFrame->GetFieldView()->SetNode(m_transformNode); + mainFrame->GetFieldView()->Create(); + } +} + +void V4StudioTree::Scale(int dX, int dY) +{ + if (m_transformNode && (dX || dY)) { + GF_FieldInfo field; + u32 tag = gf_node_get_tag(m_transformNode); + if (tag == TAG_MPEG4_Transform2D){ + gf_node_get_field(m_transformNode, 5, &field); + if (dX) ((SFVec2f *)field.far_ptr)->x *= (dX>0?1.01:0.99); + if (dY) ((SFVec2f *)field.far_ptr)->y *= (dY>0?1.01:0.99); + } else { + gf_node_get_field(m_transformNode, 3, &field); + if (dX) *((SFFloat *)field.far_ptr) *= (dX>0?1.01:0.99); + gf_node_get_field(m_transformNode, 7, &field); + if (dY) *((SFFloat *)field.far_ptr) *= (dY>0?1.01:0.99); + } + V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent(); + mainFrame->GetFieldView()->SetNode(m_transformNode); + mainFrame->GetFieldView()->Create(); + } +} + + +void V4StudioTree::Rotate(int dX, int dY) +{ + /*quick and dirty of course...*/ + dY *= -1; + if (m_transformNode && (dX || dY)) { + u32 tag = gf_node_get_tag(m_transformNode); + if (tag == TAG_MPEG4_Transform2D){ + Double ang; + ang = atan2(dY, dX) / 100; + ((M_Transform2D *) m_transformNode)->rotationAngle += ang; + } else { + ((M_TransformMatrix2D *) m_transformNode)->mxy += dX; + ((M_TransformMatrix2D *) m_transformNode)->myx += dY; + } + V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent(); + mainFrame->GetFieldView()->SetNode(m_transformNode); + mainFrame->GetFieldView()->Create(); + } +} + + diff --git a/applications/v4studio/V4StudioTree.h b/applications/v4studio/V4StudioTree.h new file mode 100644 index 0000000..845af74 --- /dev/null +++ b/applications/v4studio/V4StudioTree.h @@ -0,0 +1,86 @@ +#ifndef _V4STUDIO_TREE_H +#define _V4STUDIO_TREE_H + +#include "safe_include.h" +#include +#include "V4FieldList.h" + +#include + + +class V4StudioFrame; + + +class V4StudioTreeItemData : public wxTreeItemData { +public: + V4StudioTreeItemData(GF_Node *n, GF_Node *p, s32 fi, s32 pos = -1) : node(n), parent(p), fieldIndex(fi), position(pos) {} + +public: + GF_Node *GetNode() { return node; } + void SetNode(GF_Node *n) { node = n; } + GF_Err GetField(GF_FieldInfo *f); + + GF_Node *GetNodeParent() { return parent; } + void SetNodeParent(GF_Node *n) { parent = n; } + s32 GetPosition() { return position; } + void SetPosition(s32 pos) { position = pos; } + void SetFieldIndex(s32 i) { fieldIndex = i; } + s32 GetFieldIndex() { return fieldIndex; } + +private: + GF_Node *node; + GF_Node *parent; + s32 fieldIndex; + s32 position; + +}; + +class V4StudioTree: public wxTreeCtrl { + +public: + enum + { + TreeCtrlIcon_File, + TreeCtrlIcon_FileSelected, + TreeCtrlIcon_Folder, + TreeCtrlIcon_FolderSelected, + TreeCtrlIcon_FolderOpened + }; + + V4StudioTree(wxWindow *parent, wxSize size, V4FieldList *fieldView); + + void CreateImageList(int size = 16); + + void Refresh(GF_Node * node); + + void OnSelChanged(wxTreeEvent& event); + void OnItemRightClick(wxTreeEvent &event); + void ShowMenu(wxTreeItemId id, const wxPoint& pt); + void OnBeginDrag(wxTreeEvent& event); + void OnEndDrag(wxTreeEvent& event); + + void SetSelectedItem(GF_Node *node); + void Translate(int dX, int dY); + void Scale(int dX, int dY); + void Rotate(int dX, int dY); + wxTreeItemId FindNodeItem(wxTreeItemId itemId, GF_Node *node); + GF_Node *FindTransformNode(wxTreeItemId itemId); + +protected: + DECLARE_EVENT_TABLE() + +private: + void AddNodesToItem(wxTreeItemId parentItemId, GF_Node * node, s32 fieldIndex, s32 position); + + wxTreeItemId m_selectedItem; // item being dragged right now + wxTreeItemId m_draggedItem; // item being dragged right now + + GF_Node * m_selectedNode; + GF_Node * m_transformNode; + + // All the component of the V4Studio Main Frame have a pointer to that Main Frame, called parent. + V4StudioFrame * parent; + +}; + +#endif diff --git a/applications/v4studio/V4TimeLine/V4TimeLine.cpp b/applications/v4studio/V4TimeLine/V4TimeLine.cpp new file mode 100644 index 0000000..ac5acbe --- /dev/null +++ b/applications/v4studio/V4TimeLine/V4TimeLine.cpp @@ -0,0 +1,159 @@ +/* + V4TimeLine.cpp + + Implémentation de la classe V4TimeLine +*/ + +#include "V4TimeLine.h" +#include "V4TimeLineCase.h" +#include "..\V4StudioFrame.h" + + +// Constructeur +V4TimeLine::V4TimeLine(wxWindow * parent_, unsigned int timeLineLength) : wxScrolledWindow(parent_), sizer(NULL) { + + // position en X de la grille + Offset = 100; + Length = timeLineLength; + clicked = NULL; + + parent = (V4StudioFrame *) parent_; + + sizer = new wxBoxSizer(wxVERTICAL); + + // indispensable pour avoir des barres de défilement + SetScrollRate( 5, 5 ); + + hdr = new V4TimeLineHdr(this); + sizer->Add(hdr); + + SetSizer(sizer); + FitInside(); +} + + +// Destructeur +V4TimeLine::~V4TimeLine() { +} + + +// retourne la position en X du debut de la grille +int V4TimeLine::GetOffset() { + return Offset; +} + + +// returns Length attribute +unsigned int V4TimeLine::GetLength() { + return Length; +} + + +// Adds a line to the timeline +void V4TimeLine::AddLine(u32 NodeID, wxString eltName) { + // verifies that we have no line with the same NodeID + + for (int i = 0; i < lines.size(); i++) { + if (lines.at(i)->GetNodeID() == NodeID) + return; + } + + // adds the line at the end of the list, in the next position + // note that we already have the hearder line in position 0 + lines.push_back( + new V4TimeLineElt(this, lines.size()+1, NodeID, eltName, hdr->GetFrame()) + ); + + // redraws the component + sizer->Add(lines.back()); + Layout(); +} + + +// called when time changed, update display (background colors) +void V4TimeLine::SetFrame(u32 _frame, V4TimeLineCell * newClicked) { + + // updates lines display + if (_frame != hdr->GetFrame()) { + hdr->SetFrame(_frame); + + // TODO : apply all commands from previous frames. + // maybe by adding a NextFrame() method + + for (int i = 0; i < lines.size(); i++) + lines.at(i)->SetFrame(_frame); + } + + + // updates clicked state on clicked cells + + // removes state from old + if (clicked != NULL) { + u32 state = clicked->GetState(); + state &= ~CELL_STATE_CLICKED; + clicked->SetState(state); + } + + clicked = newClicked; + + // adds state to new + if (clicked != NULL) { + u32 state = clicked->GetState(); + state |= CELL_STATE_CLICKED; + clicked->SetState(state); + } + + + // updates command panel + ((V4StudioFrame *) GetParent())->GetCmdPanel()->Refresh(_frame); + +} + + +// GetSelectedID -- returns the ID of the node whose timeline is selected +u32 V4TimeLine::GetSelectedID() const { + + // if nothing is selected then returns null nodeID + if (!clicked) return 0; + + // gets the line from the cell + u32 pos = clicked->GetParent()->GetPos(); + + // gets the node id (!! the header line is first) + if (pos) return lines.at(pos-1)->GetNodeID(); + else return 0; // if we clicked on a header cell +} + + +// AddCommand -- +void V4TimeLine::AddCommand(GF_Command * c) { + clicked->AddCommand(c); +} + + +// DeleteCommand -- +void V4TimeLine::DeleteCommand(u32 n) { + clicked->DeleteCommand(n); +} + + +// GetCommand -- +GF_Command * V4TimeLine::GetCommand(u32 n) { + return clicked->GetCommand(n); +} + + +// SetLength -- +void V4TimeLine::SetLength(const unsigned int length_) { + hdr->SetLength(length_); + + // if the selected frames will be deleted, we select the last cell + if (hdr->GetFrame() > length_) SetFrame(length_); + + for (int i = 0; i < lines.size(); i++) + lines.at(i)->SetLength(length_); + + Length = length_; + + FitInside(); +} \ No newline at end of file diff --git a/applications/v4studio/V4TimeLine/V4TimeLine.h b/applications/v4studio/V4TimeLine/V4TimeLine.h new file mode 100644 index 0000000..98908a8 --- /dev/null +++ b/applications/v4studio/V4TimeLine/V4TimeLine.h @@ -0,0 +1,84 @@ +/* + V4TimeLine.h + + V4Studio component allowing to edit timings + The component draw sub components whose type is V4TimeLineElt +*/ + +#ifndef _V4TimeLine_ +#define _V4TimeLine_ + +#include "../safe_include.h" + +// wxWidgets includes +#include +#include + +// Own includes +#include "V4TimeLineElt.h" +#include "V4TimeLineHdr.h" + +// STL includes +#include + +class V4TimeLineCell; +class V4StudioFrame; + + + +class V4TimeLine : public wxScrolledWindow { + public: + // Constructeur -- timeLineLength est compté en images + V4TimeLine(wxWindow * parent, unsigned int timeLineLength = 50); + + // Desctructeur + ~V4TimeLine(); + + void AddLine(u32 NodeID, wxString eltName); // Adds a line to the timeline + // TODO : add an additionnal parameter to specify a pointer to the element represented by the line + // TODO : use node pointer instead of id ? + + void Clear(); // clears all the line in the timeline + // TODO : check if everything is destroyed correctly + + // retourne la position en X du debut de la grille + int GetOffset(); + + // access to Length + unsigned int GetLength(); + void SetLength(const unsigned int length_); + + // called when time changed, update display (background colors) + void SetFrame(u32 _frame, V4TimeLineCell * newClicked = NULL); + + // returns the ID of the node whose timeline is selected + u32 GetSelectedID() const; + + // Deletes a command from the current cell + void DeleteCommand(u32 n); + + // Adds a command to the current cell + void AddCommand(GF_Command *); + + // Retrieves the list of commands for the current cell + GF_Command * GetCommand(u32 n); + + // pointer to the parent + V4StudioFrame * parent; + + + private: + // composants graphiques + std::vector lines; + V4TimeLineHdr * hdr; + wxBoxSizer * sizer; + + // attributs divers + int Offset; // X position of the grid + u32 Length; // length (in frames) of the scene + V4TimeLineCell * clicked; // cell currently "clicked" + + +}; + +#endif \ No newline at end of file diff --git a/applications/v4studio/V4TimeLine/V4TimeLineCase.cpp b/applications/v4studio/V4TimeLine/V4TimeLineCase.cpp new file mode 100644 index 0000000..375e97d --- /dev/null +++ b/applications/v4studio/V4TimeLine/V4TimeLineCase.cpp @@ -0,0 +1,186 @@ +/* + V4TimeLineCase.cpp + +*/ + +#include "V4TimeLineCase.h" +#include "V4TimeLineLine.h" +#include "V4TimeLine.h" + +#include "..\V4StudioFrame.h" +#include "..\V4SceneManager.h" + + +BEGIN_EVENT_TABLE(V4TimeLineCell, wxWindow) + EVT_PAINT(V4TimeLineCell::OnPaint) + EVT_LEFT_UP(V4TimeLineCell::OnLeftUp) +END_EVENT_TABLE() + + +// Constructor +V4TimeLineCell::V4TimeLineCell(V4TimeLineLine * parent_, unsigned int _num, unsigned int _type) : wxWindow(parent_, -1), num(_num), state(0), type(_type) { + + parent = parent_; + commands = gf_list_new(); + + SetSize(parent_->GetOffset() + _num * 20, 0, -1, -1); + + // line, timeline, frame + v4sf = (V4StudioFrame *) parent->GetParent()->GetParent(); + +} + + +// destructor +V4TimeLineCell::~V4TimeLineCell() { + gf_list_del(commands); +} + + +// OnPaint +void V4TimeLineCell::OnPaint(wxPaintEvent & event) { + wxPaintDC dc(this); + + dc.BeginDrawing(); + + int w, h; + GetSize(&w, &h); + + // draws the background + dc.SetPen(*wxTRANSPARENT_PEN); + + // finds a color according to the parity + unsigned char r,g,b; + if ( ((V4TimeLineLine *)GetParent())->GetPos() % 2 ) { r = 220; g = 220; b = 255; } + else { r = 200; g = 200; b = 255; }; + + // if we are selected (ie current frame) adds green + if (state & CELL_STATE_SELECTED) + g += 35; + + wxColour c(r,g,b); + + wxBrush br(c); + dc.SetBrush( br ); + + dc.DrawRectangle(0,0,w,h); + + dc.SetBrush(*wxWHITE_BRUSH); + + // draws the vertical borders + dc.SetPen(*wxLIGHT_GREY_PEN); + dc.DrawLine(0,0,0,h); + + dc.SetPen(*wxBLACK_PEN); + + // draws state specific information + if (state & CELL_STATE_CLICKED) dc.DrawCircle(w/2,h/2,10); + if (state & CELL_STATE_COMMAND) dc.DrawRectangle(2, 2, w-4, h-4); + + // draws frame number + wxString s; + if ( (type == CELL_TYPE_HDR) && (! (num % 10)) ) { + s = s.Format(wxT("%d"), num); + // centers text + int wt, ht; + dc.GetTextExtent(s, &wt, &ht); + dc.DrawText(s, (w-wt)/2, (h-ht)/2); + } + + dc.EndDrawing(); +} + + +// OnLeftUp -- tells the application that the user switched frame +void V4TimeLineCell::OnLeftUp(wxMouseEvent & event) { + + ((V4TimeLine *) GetParent()->GetParent())->SetFrame(num, this); + + V4SceneManager * sg = v4sf->GetV4SceneManager(); + + // don't trigger any more reaction if this is a cell in the first row + //if (type == CELL_TYPE_HDR) return; + + Refresh(); + v4sf->Update(); +} + + +// SetState -- Changes cell state +void V4TimeLineCell::SetState(unsigned int _state) { + state = _state; + + // if cell is clicked but not selected, can't be clicked anymore + if ( (state & CELL_STATE_CLICKED) && !(state & CELL_STATE_SELECTED) ) + state &= ~CELL_STATE_CLICKED; + + Refresh(); +} + + +// GetState -- Retrieves cells state +unsigned int V4TimeLineCell::GetState() const { + return state; +} + + +// AddCommand -- +void V4TimeLineCell::AddCommand(GF_Command *c) { + gf_list_add(commands, c); + SetState( GetState() | CELL_STATE_COMMAND ); +} + +// DeleteCommand -- Deletes ONE command +void V4TimeLineCell::DeleteCommand(const u32 n) { + // TODO : +} + + +// DeleteCommand -- destroys all the commands from this cell (for instance when shortening the scene) +// ONLY works for NORMAL cells +void V4TimeLineCell::DeleteCommands() { + + // deletes the commands from the au + + // useful objects + V4SceneManager *sm = v4sf->GetV4SceneManager(); + GF_StreamContext *ctx = sm->GetBifsStream(); + GF_AUContext * au = NULL; + GF_Node * myNode = gf_sg_find_node( sm->GetSceneGraph(), ((V4TimeLineElt *) parent)->GetNodeID() ); + + u32 myTiming = sm->GetUnits() / sm->GetFrameRate() * num; + + // first, locates the AU + for (int i = 0; i < gf_list_count(ctx->AUs); i++) { + au = (GF_AUContext *) gf_list_get(ctx->AUs, i); + if (au->timing == myTiming) { + // then the commands for this node and deletes it + for (int j = 0; j < gf_list_count(au->commands); j++) { + GF_Command * c = (GF_Command *)gf_list_get(au->commands, j); + if ( c->node == myNode ) { + gf_sg_command_del(c); + gf_list_rem(au->commands, j); + break; + } + } + } + } + + // deletes the command in our chain + gf_list_reset(commands); + + SetState( GetState() ^ CELL_STATE_COMMAND ); +} + + +// GetCommand -- allow listing of the command for this cell +GF_Command * V4TimeLineCell::GetCommand(u32 n) { + if (gf_list_count(commands) < n) return NULL; + else return (GF_Command *) gf_list_get(commands, n); +} + + +// GetParent -- returns a pointer to parent (used to retrieve line number and node ID) +V4TimeLineLine * V4TimeLineCell::GetParent() const { + return parent; +} diff --git a/applications/v4studio/V4TimeLine/V4TimeLineCase.h b/applications/v4studio/V4TimeLine/V4TimeLineCase.h new file mode 100644 index 0000000..159ab22 --- /dev/null +++ b/applications/v4studio/V4TimeLine/V4TimeLineCase.h @@ -0,0 +1,68 @@ +/* + V4TimeLineCase.h + + // TODO : description +*/ + +#ifndef _V4TimeLineCell_ +#define _V4TimeLineCell_ + + +#include + +#include + + +class V4TimeLineLine; + +// differents types of a cell -- differs with the line type +#define CELL_TYPE_HDR 0 +#define CELL_TYPE_NORMAL 1 + +// different states of a cell +#define CELL_STATE_CLICKED 1 +#define CELL_STATE_SELECTED 2 +#define CELL_STATE_COMMAND 4 + +class V4StudioFrame; + +class V4TimeLineCell: public wxWindow { + + public: + // Constructor + V4TimeLineCell(V4TimeLineLine * parent, unsigned int num=0, unsigned int type=CELL_TYPE_NORMAL); + + // Destructor + virtual ~V4TimeLineCell(); + + // Changes cell state + void SetState(unsigned int _state); + unsigned int GetState() const; + + // returns a pointer to parent (used to retrieve line number and node ID) + V4TimeLineLine * GetParent() const; + + // manipulates the commands list + void AddCommand(GF_Command *); + void DeleteCommands(); + void DeleteCommand(const u32 n); + GF_Command * GetCommand(u32 n); + + private: + unsigned int state; // possible values not yet defined + unsigned int type; // type of cell + + unsigned int num; + + DECLARE_EVENT_TABLE() + + V4StudioFrame * v4sf; + V4TimeLineLine * parent; + + GF_List * commands; // actions to perform for this object (row) when entering the frame (col) + + void OnLeftUp(wxMouseEvent &); + void OnPaint(wxPaintEvent &); +}; + +#endif \ No newline at end of file diff --git a/applications/v4studio/V4TimeLine/V4TimeLineElt.cpp b/applications/v4studio/V4TimeLine/V4TimeLineElt.cpp new file mode 100644 index 0000000..2247c62 --- /dev/null +++ b/applications/v4studio/V4TimeLine/V4TimeLineElt.cpp @@ -0,0 +1,74 @@ +/* + V4TimeLineElt.cpp + + Implements V4TimeLineElt class +*/ + +#include "V4TimeLineElt.h" +#include "V4TimeLine.h" +#include "V4TimeLineCase.h" + +#include "..\V4StudioFrame.h" +#include "..\V4SceneManager.h" // FPS constant + + +// Constructor +V4TimeLineElt::V4TimeLineElt(V4TimeLine * parent, unsigned int pos, unsigned long _NodeID, wxString eltName, unsigned long frame) : +V4TimeLineLine(parent, pos, frame), txt(NULL), lbl(NULL) { + // creates sub-controls + + NodeID = _NodeID; + + sizer = new wxBoxSizer(wxHORIZONTAL); + SetSizer(sizer); + + lbl = new wxStaticText(this, -1, eltName); + lbl->SetSize(0,0,parent->GetOffset(), 20); + + + sizer->Add(lbl, 0, wxALL, 0); + + CreateLine(CELL_TYPE_NORMAL); + + + // locates the existing commands for a node + GF_StreamContext * stream = parent->parent->GetV4SceneManager()->GetBifsStream(); + + // tries all access units + for (i=0; i < gf_list_count(stream->AUs); i++) { + GF_AUContext * au = (GF_AUContext *) gf_list_get(stream->AUs, i); + + // and all commands in each au + for (u32 j=0; j < gf_list_count(au->commands); j++) { + GF_Command * c = (GF_Command *) gf_list_get(au->commands, j); + + // and checks whether they are appled to this node + if (gf_node_get_id(c->node) == NodeID) { + // TODO : the timing unit might not be 1 ms + u32 cellNum = au->timing / 1000 * parent->parent->GetV4SceneManager()->GetFrameRate(); + cells.at(cellNum)->SetState(CELL_STATE_COMMAND); + cells.at(cellNum)->AddCommand(c); + } + } + + } + +} + + +// Destructor +V4TimeLineElt::~V4TimeLineElt() { + +} + + +// returns NodeID +u32 V4TimeLineElt::GetNodeID() { + return NodeID; +} + + +// +unsigned char V4TimeLineElt::GetType() { + return CELL_TYPE_NORMAL; +} \ No newline at end of file diff --git a/applications/v4studio/V4TimeLine/V4TimeLineElt.h b/applications/v4studio/V4TimeLine/V4TimeLineElt.h new file mode 100644 index 0000000..a2c77af --- /dev/null +++ b/applications/v4studio/V4TimeLine/V4TimeLineElt.h @@ -0,0 +1,48 @@ +/* + V4TimeLineElt.h + + Class representing one line in the timeline + + A line consists in a label, giving the name of the object it represents, and an owner drawn zone where are represented the events +*/ + +#ifndef _V4TimeLineElt_ +#define _V4TimeLineElt_ + +#include "../safe_include.h" + +// wxWidgets includes +#include + +// Own includes +#include "V4TimeLineLine.h" + + +// include GPAC +//#include "../safe_include.h" // definition des types de données + + +class V4TimeLine; + + +class V4TimeLineElt : public V4TimeLineLine { + public: + // Constructor + V4TimeLineElt(V4TimeLine * parent, unsigned int pos, unsigned long NodeID, wxString eltName, unsigned long frame=0); + + // Destructor + ~V4TimeLineElt(); + + // inherited + virtual unsigned char GetType(); + + // returns NodeID + u32 GetNodeID(); + + private: + wxTextCtrl * txt; + wxStaticText * lbl; + unsigned long NodeID; +}; + +#endif \ No newline at end of file diff --git a/applications/v4studio/V4TimeLine/V4TimeLineHdr.cpp b/applications/v4studio/V4TimeLine/V4TimeLineHdr.cpp new file mode 100644 index 0000000..890b174 --- /dev/null +++ b/applications/v4studio/V4TimeLine/V4TimeLineHdr.cpp @@ -0,0 +1,37 @@ +/* + V4TimeLineHdr.cpp + + Implements V4TimeLineHdr class. +*/ + +#include "V4TimeLineHdr.h" +#include "V4TimeLine.h" +#include + +BEGIN_EVENT_TABLE(V4TimeLineHdr, wxWindow) +END_EVENT_TABLE() + + +// Constructor +V4TimeLineHdr::V4TimeLineHdr(V4TimeLine * parent, unsigned int pos) : V4TimeLineLine(parent, pos) { + + // Creates default line + sizer = new wxBoxSizer(wxHORIZONTAL); + SetSizer(sizer); + lbl = new wxStaticText(this, -1, wxT("Object")); + lbl->SetSize(0,0,parent->GetOffset(), 20); + sizer->Add(lbl, 0, wxALL, 0); + + CreateLine(CELL_TYPE_HDR); + +} + + +// Desctructor +V4TimeLineHdr::~V4TimeLineHdr() {}; + + +// +unsigned char V4TimeLineHdr::GetType() { + return CELL_TYPE_HDR; +} \ No newline at end of file diff --git a/applications/v4studio/V4TimeLine/V4TimeLineHdr.h b/applications/v4studio/V4TimeLine/V4TimeLineHdr.h new file mode 100644 index 0000000..068fff5 --- /dev/null +++ b/applications/v4studio/V4TimeLine/V4TimeLineHdr.h @@ -0,0 +1,36 @@ +/* + V4TimeLineHdr.h + + Defines the class for printing hearder information on the timeline (frame numbers...) +*/ + +#ifndef _V4TimeLineHdr_ +#define _V4TimeLineHdr_ + +#include "../safe_include.h" + +#include +#include "V4TimeLineLine.h" // parent class + +class V4TimeLine; + + +class V4TimeLineHdr : public V4TimeLineLine { + public: + // Constructor + V4TimeLineHdr(V4TimeLine * parent, unsigned int pos=0); + + // Destructor + ~V4TimeLineHdr(); + + // inherited + virtual unsigned char GetType(); + + private: + + wxStaticText * lbl; // holds the title string + + DECLARE_EVENT_TABLE(); +}; + +#endif diff --git a/applications/v4studio/V4TimeLine/V4TimeLineLine.cpp b/applications/v4studio/V4TimeLine/V4TimeLineLine.cpp new file mode 100644 index 0000000..2c72bef --- /dev/null +++ b/applications/v4studio/V4TimeLine/V4TimeLineLine.cpp @@ -0,0 +1,92 @@ +/* + V4TimeLineLine.cpp + + Implements V4TimeLineLine class + +*/ + +#include "V4TimeLineLine.h" +#include "V4TimeLine.h" + +// Constructor +V4TimeLineLine::V4TimeLineLine(V4TimeLine * parent, unsigned int _pos, unsigned long frame) : wxWindow((wxWindow *) parent, -1), pos(_pos) { +} + + +// retrieves X position of the grid +unsigned int V4TimeLineLine::GetOffset() const { + return ( (V4TimeLine *)GetParent() )->GetOffset(); +} + + +// returns the number of frame in the scene +unsigned int V4TimeLineLine::GetLength() const { + return ( (V4TimeLine *)GetParent() )->GetLength(); +} + + +// returns the position of the line in the timeLine +unsigned int V4TimeLineLine::GetPos() const { + return pos; +} + + +// Update diplay to match new time +void V4TimeLineLine::SetFrame(unsigned long _frame) { + std::vector::iterator iter; + + int i = 0; + + // uptades the needed cells + + for (iter = cells.begin(); iter != cells.end(); iter++) { + // newly selected cell, adds selected state + if (i == _frame) { + unsigned int state = (*iter)->GetState(); + state |= CELL_STATE_SELECTED; + (*iter)->SetState(state); + } + + // old selected cell, deletes selected state + if ( (i == frame) && (_frame != frame) ) { + unsigned int state = (*iter)->GetState(); + state &= ~CELL_STATE_SELECTED; + (*iter)->SetState(state); + } + + i++; + } + + frame = _frame; +} + + +// Retrieves frame +unsigned long V4TimeLineLine::GetFrame() const { + return frame; +} + + +// +void V4TimeLineLine::SetLength(const unsigned int length_) { + int L = GetLength(); + + V4TimeLine * line = (V4TimeLine *) GetParent(); + SetSizeHints(line->GetOffset() + 20 * length_, -1); + line->Layout(); + + if (L <= length_) { + + for (int i = L; i < length_; i++) { + cells.push_back(new V4TimeLineCell(this,i, GetType())); + sizer->Add(cells.back(), 1, wxALL, 0); + } + } else { + for (int i = L-1; i >= length_; i--) { + if (cells.back()->GetCommand(0)) cells.back()->DeleteCommands(); + delete cells.back(); + cells.pop_back(); + } + } + +} \ No newline at end of file diff --git a/applications/v4studio/V4TimeLine/V4TimeLineLine.h b/applications/v4studio/V4TimeLine/V4TimeLineLine.h new file mode 100644 index 0000000..07e70a2 --- /dev/null +++ b/applications/v4studio/V4TimeLine/V4TimeLineLine.h @@ -0,0 +1,68 @@ +/* + V4TimeLineLine.h + + Defines generic behaviour for a line in the timeline + There are two child classes: V4TimeLineElt and V4TimeLineHdr + +*/ + +#ifndef _V4TimeLineLine_ +#define _V4TimeLineLine_ + +#include "../safe_include.h" + +#include +#include +#include "V4TimeLineCase.h" + + +#define LINE_TYPE_HDR 0 +#define LINE_TYPE_ELT 1 + +class V4TimeLine; + + +// macro that creates the sizer and the grid for a line +#define CreateLine(CELL_TYPE) \ + SetSizeHints(parent->GetOffset() + 20 * GetLength(), -1); \ + \ + for (u32 i=0; iAdd(cells.back(), 1, wxALL, 0); \ + } \ + SetFrame(frame); + + +class V4TimeLineLine : public wxWindow { + public: + // Constructor + V4TimeLineLine(V4TimeLine * parent, unsigned int _pos, unsigned long frame=0); + + // retrieves the X positon of the grid + unsigned int GetOffset() const; + + // returns the Y position of the line + unsigned int GetPos() const; + + // accesses the length of the grid + unsigned int GetLength() const; + void SetLength(const unsigned int length_); + + // Update display to match new time + void SetFrame(unsigned long _frame); + unsigned long GetFrame() const; + + virtual unsigned char GetType() PURE; + + + protected: + std::vector cells; // cases of the grid + wxBoxSizer * sizer; + + unsigned int pos; // Y position of the line in the grid + + unsigned long frame; // current frame + +}; + +#endif \ No newline at end of file diff --git a/applications/v4studio/install.txt b/applications/v4studio/install.txt new file mode 100644 index 0000000..a728b49 --- /dev/null +++ b/applications/v4studio/install.txt @@ -0,0 +1,110 @@ +What is V4Studio? +------------------ + +This piece of software is an authoring tool to design and encode MPEG-4 content +at large (audio, video, still picture, vector graphics and text). The current +version has no support for audio, video or still picture. But if you edit an +existing mp4 files that contains some media, they will remain in the output mp4. + +This tool was design based on the rendering system of the gpac project but the +GUI is written using wxWindows (www.wxwindows.org). + +The current version allows you to open an mp4 file or a bt file, to edit the +graphical elements (vector graphics, text) of the initial BIFS scene. You can +then save the result to an mp4 file. Editing is very limited for the moment, you +can: +- add nodes within a node depending on the context. +- delete nodes +- change some field values (float, int, string, colours, points) +- move objects. + +Problems: +--------- +Since it is in a version early stage, this software has known bugs (fields list +view, node drag and drop, node deletions) and may have some more unknown ones. + +How to install V4Studio? +-------------------------- +I'm using Windows 2000 and I haven't tried it yet on other platforms. Please +report any problem. Only the Debug version has been tested. + +On Windows: +Get the source from the gpac project http://gpac.sourceforge.net + +Compiling: +--------- +Open the GPAC.dsw file. Set the V4Studio project active and build it. It will +build also libgpac.dll. + +The current version uses a hard-coded path to a configuration file GPAC.cfg. +Before compiling, you should change that path to your path. In the file +wxGPACPanel.cpp, change: + strcpy(config_path, "E:\\MesProjets\\gpac\\gpac\\bin\\Debug"); +into: + strcpy(config_path, \\gpac\\bin\\w32_deb"); + +To link, it needs the js32.lib. Place it under gpac/extra_lib/lib/w32_deb. + +To link, you also need some elements from wxWidgets, version 2.5.2. You need to change the path +to the wxWidgets include and library directories, in tools->options, Directories tab +Add to include directories: +\wxWidgets\include +\wxWidgets\contrib\include +\wxWidgets\lib\mswd + +Add to library directories: +\wxWidgets\lib +\wxWidgets\contrib\lib + +The following libs from wxWidgets are required and should be present in V4Studio project linker settings: +wxmsw25d_html.lib wxbase25d.lib wxmsw25d_core.lib wxmsw25d_adv.lib wxbase25d_xml.lib wxexpatd.lib wxmsw25d_xrc.lib +(remove the 'd' for release versions) + + +Once this is done, you should be able to compile V4Studio.exe. + +Running: +-------- +To run the tool, you should have a valid GPAC.cfg file. If you don't have the +GPAC.CFG file, here a default content, where you should change +------------------------------------ +[General] +ModulesDirectory=\gpac\bin\Debug\ +CacheDirectory=\gpac\bin\Debug\cache + +[Rendering] +DriverName=gdip_rend +AntiAlias=All + +[Audio] +ForceConfig=yes +NumBuffers=8 +TotalDuration=400 +DriverName=dx_hw + +[FontEngine] +DriverName=gdip_rend +FontDirectory=C:\WINNT\Fonts + +[Video] +DriverName=dx_hw + +[System] +ThreadingPolicy=Free +Priority=normal +----------------------------------------- + +You need also at least the plugin for the Rendering section (here gdip_rend), +the Video section (here dx_hw), the audio section (here dx_hw). So you should +also compile the dll for the 2 projects gdip_rend, and dx_hw. Then put the dll +in the modules Directory. + +To run the tool outside VC++, copy the V4Studio.exe file in a PATH and copy the +rc directory into the same PATH. The program should run without error messages. + +If GPAC has been compiled with ECMAScript support, you will +also have to copy the js32.dll into the execution directory (or any directory in +the machine PATH) + +Other platforms: +To be tested ... \ No newline at end of file diff --git a/applications/v4studio/rc/V4Studio.aps b/applications/v4studio/rc/V4Studio.aps new file mode 100644 index 0000000000000000000000000000000000000000..1e237e05be0c95a0c22ebcb899e1ae251689191c GIT binary patch literal 60860 zcmeIb3!Gh5bw9oWfg}(K_yk1>C`xJxb070kAi2+(o7}l~xQ|RisY5a|fkgd|Wj zBcP!Mh+<2%_5BvA1#NvlqE=Abs%>d&l~$UNfJ5RhS~Rgz?*IE;YwvT;y>}*wR)79K zpI=UL=G^tzYp=6kYwhO|5t)y>(Xbmoo31WEFTvd}Lz2d`(U{@rb7#6Aa?@Us>?^Js z*)TeG)ioPej$E~JhId8c$1jH}G5U2|T0sZos<{ZudOg=i%Ot8{)q@&HY%T@oebOqk7EM@{>V% zsYn&&ry|41>6DfxFyM-&wa7_G-T6porZRKJ{>}%V<?y`i0*A_1gb=*MHsgns@!zw#)1Z z*we^IeuiUs%29cg+j22~7UOO-rpli*a;u1cRn+tqxa~T6KAqH3k51M=(J(XQng7@F zX*Oc#J3b%xGjN|eA#(C!l%{tB_zrGVDM{jd~c8W$Jty)Rs7dwz=r^jiz^dT{^iXkIYO& z!_1M{zvetTc|v3+?%BA{5|ImVXK~+#%s5kK{A9kII(IQ*osct8+vnrSd+`+iJdJ#_ zPePu~g$OjOno!gJd1^aS9`_;KkAUmTxS2c1gs0~`9n-(#iMZA44kg5F)g#;~Pz#M+0c<`q0`HB#%3X_Rn9@bZ^hA znJsyBiUx~@IZMv_74vE_WIbYucQS0zEZCcQPm7!f^SP)oA=?|f@iJg=7k@`#kJsmJ z{5{sKjQXe1_!83oJWIHzkOxe}L$EK5VK-u)fCkV6+E62E=5N98Q-M1J0-n|z02(?# z$8jS7FSZHO*QW~X6WhHom!j$2o=2y&-?0npdasNub1HwtY)_i+k zflprEZZs_z+`V90Fo?3MD8+GVt*U|H65Wa2&_NfXl z!mem~x98EREqOFc14YBkfQ9(A|kf8bFJ;U7|)_j`r2=eK_9zpxaw(~fzW93l=_NWiF z$mdctz1#C>c1s@3(UfQo^K0lQ{SxW>>tH?3r2LJs9X*fIutn@CEe&3^IlWu^N^@HB z=(M&xI$LJ`#_EwNIRQJf8$WqJg8ORR8*%sIUWEHR_e2X6!1M5%{RJAR_Urf)f0460 z=Q*DM&KVlOM&s1&=+L@*H=d|RPqQxZCj1C)`iJxt+l>?Rh_+^G|1PXW!^o=&{&9@N zUq{cc(^~S2BMq&6+cST|`QO)(P`5%z;I_q#=M$+hnXZF<(d$5Z*Wi81Ap;TcJJPeCkdz*tQshs z`oLdvxCs4buQ_ky^`;<_Hn`#Fr+<#ueRHOT8!hzdVU6tckUleuYdRRw&um2KEih#n z-SnFl)=2G-@@E$Zm}A1!FUsEn2ZDYBKc*<^k>R}E=EU+@Ve zo^#$dGrJg1aAX&Qu1^b*&FXWednz#PAFGdeM^nt(lsrj?};oWlRn;(>i4mITdJq@|@p@v-dKtuXJ-H;3K!3*-f z>tT@_KO?g0ZjmJzb&n6qLwmvhAo%YE|1R*4gMSFTb=(E;Uk3gd_%8(iIpBxf`|k(; zo#4L?{Qcm+5WIqpzkpxBFW}cv{Xy9a{s+N-FZg$Xe;oWn;IE^U7r=iR_+#L|5d7!J zpd14K9`HW|{s+MSY4G0z{(HcG7x-@k|0?h=0e>9)4e&R>-vECD{0;Co!2dM! z!N0rlZyf&yl^;(1{d>Uw5cnSe|EIxE>Gy#9E^yxn?p5Gk0`55E?*;#Z;J+9AyTCsV z{vq(!!CwIXW#Es2|3dJe1AfT8|9`#8B#Nb)Vxq+@Eq_T0{oaGt( z4e+Di)M$Xe0saQ`L3TZi?D`C{>uzM%II?$e5Oo9md%*t?_#XiOr@?;@_@^m8vKRah zg8yFd?*jig_=mthP4R);hva_n-wFQfz~2x43&B5K0zK5&3;qYee=qoVfgdT|fG;;q z0zK3?1pYnXe+c{!fdA9rN8(JAKo6nv-;euF+}Gjm$9*C0=@RHCKlzF5-@jkJ_r34Q zx4!i)`N~(mBA@)^C*_`d?vZ!C^PO_nU3bYF-tY#w@x~iv{rdH?YSk)PwrrU!S+Ybf zzx;BE$K&$Sm%db<|NQ668E2d!hkntJ_rLqysK*niGl-W}3GwR#f2j(ZeISo<9f)4w zJuo?W`0(S8&Dy{3(MJy`9DoLg;9jMB=12GLQP_nAXt0SQXs}9Fu%Y~n$iS)LR{g-O zin#Yu^7QX$>42sWU6SLyUx#GlAPd7b+e!9qj%_D*E7w$iCZGiH$TNN5FMj?~b`n;N z*W#IG)A20xT0HYfUXA02gYbUr zsU3#xEW&znrYbAL7<1wXZZ?w+ut$74r#9*kVDYquXUJG!6d?Fo^c2h^-8xEF|>J84G^+`C8N#{!V> z^XL;h4#+U;y zJk_2)DKpi)X7P0P^+{uxNvj#yVGZZPKdEwOM#dGQ5&3lFb7Gdey>Ak=uZ~7PWQR*b zhM^sWyL}8hS_Ti-b^R(f^vot3%A9Jpq0iLUInftA(HBM6&&uM7zUYa*=&#fljT$L7 zD~E5^*0s^tv*)nE+uU`tgO$E#&&@YaHq*B_7<5v%Y-tHUC!i~Jj)%ScIk3?dKf=zL zGiRHFm43tI;YowH;F>e%aHBCtF{&RoITqwR$GoC@CN=zM~{wHe;(;BB|vrhMBPq#xe8?J$&r)NMe^ZMg9GmD16r z;qFCdLl>)@)-l^?Uz97nxbnfS_VL?jzw?<*Hj_EiY%@8Fb+o*it@OpMzLaA5XF~r^ zl~*UmZ!y+&V*K{R`0f8IVfarV9^E zAA4M5W!tttj9=-*g_rh|08a2m`}oV)PKIr^na@$dzfGGtn>GyBY??}#wb?uLx=nT{ zJV6@=^hULHw03|1=5Lp2s!OS_uV5vbiv%i~N>hGoCAANR4w zZ_60X9mE`0Ei!6*bldstCfmszYPOxv1?Gh9JTdnf3`w4t`#drC`76zRHVv2(z(gWu zCH+PCpa&lK>7FJS__p8&&?4{MhgseI``9>79%$UYe{%2Mef#$AJF@R~%w`^FJki** zcXDzs{%~^hKw}c{{(XQC>SX7vSx-2AcAvn2k>1Gz2ailPCe;=mki9g;d-v*8D>?Sw zjtj7wYMrg62k=B>B$r%vvE4q}MSB{xsoy~#=@9Nc=xesy&gZG9-=^&}lXlx}JGL>o z8%OLp;hv2np1!LAQ@FixWY>;eTeol9c4YPu{BG=;!1M4{KDX{TynP!Id>1U`F4)Lj zWS_wE$Zfm$oJ~n#${lYyPWZk(`wIpL-DK`G z+s(7}1*Xrh*-BrGGBEWuaiY(ORXQiu44qgr^#A;tp(96*Sg+Gepd8q*4Y2*$4?7p7 z9y}mN(0SaqpMUmEP98ZZa%At`!;=RO9yvJq_#_wo$mEen5qj2RkgKiF5zJ{e8vFLo zQZpzA9#h#6@|fAh$7juIOdg!vx6c?3Jhp$h%0sQ#>eX~Gs?3-osCTH!Rr6L+; zD33fo%XBs}G&)SWu1Hb^fH`o$bW__m0c`YUU+EFF?~kx;_wBrUdi)`@_fMVI+ULaL zJ6zMZ!!wn>TmMe=XJXoRcwz#yUuNysc1@CvK`iWAZUrqAF1KyRFbpmj&Jx-;O~5?znwYI?fnX=IPnH{-7e}7k8wTrAA>jQJp`Iikx~%&2 zFr?nPSkqD1FKf9c=x?~b<+&}9v21K8; z>3V!JNj3ZKc&*y?xtuj?ZFJlf6z{ zJ9NAjlb8NE+vpSaM2B=2`C(uEb2i}#bTj>vV*dB(NV{3Yee`kK3PxxL(BI@pZ3^et z9Pb*TbDQiZb7?p3-_`g|!p@hfT!DF0o>mmtV`*O7H5a_6&uC*=L`P9j5c8qoYHvxZ(=A`s%A?&6+iW^=w#1G$C((^PA<}ShM$$k9b3mEJr~R$-@J1D=*qPl){k9w!3|^M7c9E`8K+&ka(sMj-Kw=W%!h#WLJ$jgN2Gl-e|M?V8PFBb%?^1jV-XbUB9g zE7t+eY*@c}(}uO<4!QY;jX-W)vpx|A<`PCbIyOGCY0X9+J$m`pZ9{9;k8apHK0k6G z>7|!MSv17PvGq`LsMZb#QmKJg$Yc7Ov3gQ3bC4!mtNvi3ZX6^yJH;sS+ zeG!GPA0OMaaMQJdN;r%ViTH3K=fhyDKy_%izmNq+UZg;^zdTebR|Zl=Y<|WVALs!% zKj0WBlnMidD|1=wq=vR&Vz`0Sa)(WDY!FzFk%YWBU@MhVnOb34s+Pmf{Fel1>BLrR z4djGN7tW~-WEV;>WdXjF!qKM}DOkx>YvoD~hu+d2XiHdjNzRPS;&+=%EL&JlqO0HDP6wB2@sSk0zLb2sqf39+6xjcY~UK!z|4&+PlP(6fJ zzIsVHli02<%a#ro)Ad@dTw(^t&{`8!2~lxQa{`q~l`^>^nG&4N1ZE&)N@8CpQR!4> zDOq}G7Xa5%X@b2l3@W3TOKIWD@Wdq0E4f;|qA_P_rGcsT7xFcPLE@JzOu1C6lo>J7 zx~!;yT&Yf&d;S?_FjcRze8v(mw;E4suuwy31S$#JOVrX7avg9dcdP>LPnG&|b!F*< zY4RwKS*DmJ_0kdz)y)%w5Gr3Nnb;m2x$MFiM=veB#j~WqhcU#t`qoA<9Gy zRtjaNb3BHVoQWvqmQxP)n~y1+D`g2LWVM3*2FXl84;+;H&6>xo!++8@% znNX$B*RRsL<<*Knv1cB}d*nKWtGQw>qa3~T5fB6ll@yVZb&AL>&t>YU%m|gnsn6u8 zmQsT#_{x^SSxj#c$Q~W0h5Jn3zw5WP)vWWsOsf$o-{d%sB@6pndZsXfGZBp z;52E%slmZ2=~ayKa8MaM8)K$2r5ogT+ki|0TV z+X%QiiRzSz2&#*yE_q!9)lF2l+#Esm5Y;2ML{Pm%^~$Y^8bC``7%Z+JE+wy5TnhWW z0jA{*3RZJyGg4^gh|I_iL)O!^3fffSvOMb*s-kQa*oXs_lRq+4VVSn{+q&}dM#bcr zEZrTl(;%iucT8?Kh>6i1mpcq%I&>%GPJ@^P-AUPH5Z}JL6GxFV;yhoxyG!0|5Z}4G z8wYSGjj!9?BX2PX?E=7F`C|vFHYxcN2dX-0cE~9f4FZ@m@}~||g|hNi2dYLnd7A@O zrM&!^1F2I_hy1w%sZ>u)-tItZ)f1O@7>Jq&Fd^@BAhqgA%DWs$t$I4;-43KyJzer1 z2U4q^Zh5bQD2>IkHln>9@_t3ZGL(zVmEM?qKruz%N~SvGFBDS=FfsY4@t{F*4QDDY zA2Y59E+Kzue6BT3CFMS2@`z5kUlD_qawb=;F3qu!}XDX=n zM0Cri6_H!+5ItCO!4SDhuAC=FuY6V!gT)j@=2P-Hh1DtnPRr*NMyr_8hAE$sFIb2a zQ#PNKFDh8h4Gd?TP08ovOYm<7P@gri&`Ii$2NVO#O2+|)r~?-6LB(ZZ_$y^b4`f`v zuE>G%GRKyXZz!e;hsAh0<(rBL{16SA^;~5Ij7b&c zqhKyX2h8-pJ7R&&3(`C;{}Hm&B{6hDexT@FB~{I(m-eYq0((*(QG9hpHB&Ca`SKPI zv`+a?3)2#5m+Y}H@VvYy#;AK_ui}Bjzo?{?JZd43GH{U`2?eN$b-(gtbIY1cF9fG$ zQc1o%z^vF?W#kDZXoWC-zcgb z@_!UPP=?dvBi9hH_J>STf1#LdCdK8)NsQc|8& zk`@Gdt%mNBpDLP|mVAqM%g>ZXZzEgm%hcE&K;<4etoZ6+xm?UusZinP7b_vrdl#4vpjyFRIU~B)>tKzGF$Oh$_L9;Eo4EkE}1h08NDtQ z+AXIkb+9~S-N24ykIbDRgF}_!@=|msaCSR?=Tn8ETy$}4ft+QCYQ2hjo2}&VYE|@& z`^jjuB%FH~YJld4#HbjIaHigHdQBqZcK z#h`Y%hR3lcWxf(jCG-ggQdUMfJLUWtQp#6`Q>AJFN%LZZ>S%9Ro8$5lB8Ih4ASo_X zNY$>aKzHw@Dwd`h^~M?Bj*jj$_qnj-)mFfrW)tZ17#_`n7Lv=~?yUTdQnH0At*fPw zzI}I2Ualk*@1Rt6=jC@zQUnR7=VC>cG7-xO->1W2qqY-1P|P!@ikZM(!WoJ?e18F^ z2W5sC1g9wB(4mEaYlIJp;8jbRrdA6+Qm5jfGTRg1G{RZxQal^7h?pMfc4D+~46Hf4 zq#j3beO=dM>F9tz(c4VYz77a+c||iJn=WRG+Pm#YCFGTk=z7Ybz@%K-O!5Ld<+5f% zRA84}?uf2e9SZE0Ma?8Hutyd*6QTlpC8b2PSos2)T&8{u9#O_)eFQ{iJ<>$S;3ef0 zh4RB*1470nub6Bul?|~8=~FB$H6jW{CZ#`u_fk8h;IW~IE?MG{&9b{?sS+T#nDQ^s zBSl5RhN5DiQD<|Cp^%#MK4KmC1X93(2CI#plfi)AM$F3<0kJ9Gyi|-(D%7%tRI#j$ zbPSGAUGb1KkTU-z1_x-FBGrG1;Nvo+czOVqtmvRDS5$xCzW9bENNFXVOiHVli^cI;uxU2SOS)1qhng277j+nGK3jLpZeAFKCxW>18f*&|W;~2*{+B-bn zjkE!oly`b$Fr=;Ao$@YEs1>ZD#WA?{ZjZzpWm47)h{LCTkH^z$rz#Z=T(Vxo;a9)c z(_kT0RY&QS_jw8&*B}ev;(k!^nwzxMLD7e!`ynGCH=8MO`LI&5mGWR73TuZoj!OO! zA4esZM|$*!@g(IQrD-#b{)_oGsx|zGj|H?yNrNNtmjRvuY|t0IhlJc0Aq34m2hB}l zQtl5J!j>)$x8mag-nVpdI2E4=uo0sb$9T(Uq6Al)FzE7GMPs~@(IwDGeAN);L3PO! zXdV7WF@q?Jnga>63|})QKjfW2i|~M=>X;`mNlAH7F&NR$<|?5}nds=0uSX~;4hhiT z)TB>zbjdfA>PjsJ7Y5U1=hG(8DtyzZ=u@?jdgO1Fq)t-{tylg|Y0m1jP*d{vN@dY# zA*JP8N=j9iX40mo8@EAXW#rpRMEaxLX3}`-@GMAK`HqsD?Q4;mlYdaEdd;QW&|nG! zzdm*I@{dY%$U;*eD1k=npQ2=oTU(U`TCImH)}S|+!1%;J8^0T-?nuCTem6uxHPjf* zGqf`QX5nG((QC*CMkUeNBj0Z#`Svx@nU{x^lFBa+`MWygKRh;Ig-8AaMcU+1$PKT2 zPlONz=*i1olcEC8_JK%&m3Gs+n$iaA*3?_ zNjVZ?EJ&yP+@aECOdbtj-tE7e5E`&se$j-qus!nB4C#lPQNftTMHj~x$jp-fV>r5m znGDWuS!LuW_!&YBBRVS&T0vt(;!MDIQqX^9b6|APnk!LfV;z z6+QnXsSec2eli$Ek%jYR4x4u4+`z@;B8N<+%M}cD^w-@e2-?vFC&_>tPvt0XOcG59 zR6~sJq6j1v9hosrNM{Sm@g}9~B%w7Ou41|aMnqoe;BEv=%|)k+&xMEL7@NwNfnJ)9p< z8|NY_$}JWTyaNV<53TNli?1lR24qzQ4KK6Z#aWcsD>>@Ax=4%i1`Fw0UuJN|X|qEq zsKjiHEayon%56$Omo8tiIUSZ3BmI$*e0$44rbAKQs1)BYlVU0qWv5bngHH;|7_Oq+ zu9R9o=3Bz3EUu#5p+p!4bM9PBMY+=grP8SIR`@NZqU=(N&lA^)4Z^+IsICGARMoP` zwPq+B^zpMTrQ}bP;%Yf~ynMM^39OnF#?ax5{Ano8&2TgoJ%@X%a)l+*p)YFUWi_f| zNr#$G16*1f5XNe-*AGfLMEJ@JfDmh(t#3LCeT2m9^fSs)+OP+IgNp%h*qA68;+ z`7lZhrbJT3NM16ZV@3I>XDO9iyRGCv`6|lCJP)#UplpLkM(iodUwWdgvk1C2$`C_Q zy3aF2T@x*9V2Oj_e$Oy~j-{*4CO!%3Cp@)=X%F{`K6DT0f92^$%Vn)!M*%26Mfqz_ zH>W2`M+GU$Cj+{5uYxgb2teH`%BMU-*brL{D#~X(DQMMA@1lIxQz|u#-lwvD(Amm- zQ9kGC#agAAP6e5-%@16Vj>UZ+lsU?9XEB{Bi~UZmf>XG#9oK|gQ7BU;HnM@>->+3 zw6t_snC5>{Y^fYf@FD@_Aw^bG%W`Q92v8+7u=3A}O|Pis)Kv&NM$8iVmu6y+=FAWI zS0(lp%V|s#P%DUJPRMtafRO_C+RT6X9+3HAow1I|e<}bq22%VC%N_*>Fyg@~J6M1p znvlJUtggTaeNrA(AX~^|ec(R*4WBZPKFWRtuxvmjcgtgrF^vU*kLz#W&FqyQ8jP+v zC#+!WClyhlBslg@C^Urhp=*CYAvfb=YA58NB6B4SjF_&1@va{!vY&$~0w4d7f^db= zy%h}S{#apHT|pQ0Ckh~2a}_tCqiKXJ`>9g0)j^GmwF2WRKl4bJREmzvVUPA|Xp$*0 zAxAths;1I%@^htO?H1Qv20c+0j;Es(-xZA`VXzV$`PP>$po0*E;E>78lS6_JkU)S_ zPL_duxXz22&#NeNm0(?WD;Yqu*?fi)=xw?60>Eo~d4>{#1!5FKPogOElu!uV^XDKnduu8+Bh7{#FO0)GE>W>8?pQn_*N@_*aQG_Ww zTM1Dg&ceita*mSfrA&V=vosnEF}*-(Z4{Wcb1hUAqo4?8#lbZeE=^vj99RPrw(pFy zDCa35Qi4Ys%AtzF6_>Wn;8H4Rc}hXJnxLd`U=dqS1O@ zsI+j9;)$KF+=w*16Rh z8l&~YishU_U`+EUpmMe12P?T{5j++@tW>7= z*c!#c^~0KX`WTAGi@jR$!IGMM3@>$^;(QWQ0A6dIA_p)`g7W8Av*crVxAjV)+lXXy zK5;&V_uHU^unaThvAAucqS+T?-ua%6T(4*yFC*G#%$Ad-oaU-Su7yj)yE~I|lcDNx zt1#lI$b`JckXg>yIAmO2YsgAph>Xd0Ll*NPvV)(sVl-|T5Ah^><#mQrm5Aw)n?09p zVj!|xZt+Nt0A{I9vP*6a!y2knUhnzbAQu^v@&?1YmoeUi>@ci*8^gxsHYdSe$FMQ^ zBg4A)F>Hst(XazydSKzxPD8@{rpr_m)3vu7%Eez$Y`5HDSQo!xyW~#8y5ux$r|dGU zi{G$Gd6Qww#cY^&SZaNjp|!?B0j$SQLNqmsM74E*|^DNc89U}V<$xA3wLE5452O$F-!93CG15ycm|Y%{>q!~VJA zG4Wg-f*+<)(zE_=MW^bua%lPKyB6ga0VOp5*0Fj@DWx(e1mM(CmiDXuNAc)1!$i7` zOV$f|S_$wpv*nWRK%x6vl!l^*QkcT`+q*bCP{fA_nO@l8kk-Lr4ht0JBp`Lot&6fF z-63-nhm{KfE+(gWJ`VHid#2-Zx@QcL32rQgM1&@?WW%}Xq&&mWelrEu%Xi8diem=Y zD3rC1AA$1||K9+;oqeW&F+PUU{bHW(=8f-7P9kr{q~mAsV{| zbGo3aBb}CKdpRuawlt(8oss8wIZPuKH# zH|dbc$^}Y9M(HSon>Y#IFefkeqNC-vBa@excmgYw_F1_BMqdKE#4hx5r=Z5wX-OT1F5aVE_6PXsB=6`+H@Bstt76CXpIdm zY$gyor9Qp*YN>LB9cUY1fJ$j}T9wAp^q{^_S+>P_B{iX$s{M*qzsCz=`&>|T%lHci zH*3w_0q!#Pu23#si3s-(2A{%n4Dj{EV%j*4LN=?!Ff-cukQiFu9 zgIeOayp4{@??+WLluyb=#p(+is#7+3q1X}X=B2RJZ`=ouV7p~A zu+>^*@WjfqY%#nl>xV95vemG(d+-v`-wtYbT(%j_rKpNY$_<8W+7x%=OsO`xUMNK< zk`szDwM5&en-$+w2|Lg(-J<9;EalR`vtSIIQ@mAa9JgvFp9$a43lUAyA1RS7TCnL!Bg8(MH!7)G(Ef>U zVc1mdR03-d7FWT>4OK9$5-?;ig{}-!v~yX^lq;>JdFp^Su}cZ9vk0n8z9?@pDhqD5 zzg)R8h>t$(nf|Z}Jh$EB^iM~y#T8KER>0{Fwfj=`Erp4H|Z3?T+=$|XSS}!ei zyC40?^xKud1)xP;sNsj{txbG~QU_BU$Z8@(C|=~9N@gaukhvbCDDN`aE@UKyjQsAI zlC2KI<67Ke#;Zi4UPQGk-w~s$?%=ubhF;Y+w^}Y3_`DR~jp)N~R3c(&>X3yUkLqGb4 zI^|&L#Z3B|QP6BOIo!;s2aJTA!uTl~xnh6L9rs{02D9j&ByAxox*bKbu8yjGYR2Im0W=e)!k%kjl`Y(p%w5{Lfqz2?) zoAGXp8^sb<Dz~+s{J`k#*&zV}$5%h2_p(v`9$YhzTglq~XGrAIbfM;ozkP zZIdSV0mIXHH4_dR!5r8K35~{&jHK;$Gv)sng)KI^3W%&-?x7|Y6F-HdA2+dS9c2yv zNr>*N6#TME3=_>1jv)=ZSqkIFYyZOGuc*7N^K5*da>#I`fQ9RSq9RJRLPJa4(~iIh z$|W7D(smLl(QqWx`-m_&E^>MnRPB@2RxIL z9R*DRUk`fwqq4#mLouA3@o~?EVf-0 zWp)sYP64@TD^$sqa+)LI93Q_xv165-?y%T_8Yb6>%nf7Emmc>0<&IH#hNJnd#5Bh; zFNz!v_Bmr_?93*L9>RpxcSUT5*R02KlCw_XxFQURsSxi;Vr=}`h8!s1bcIr1P+CZg z$#aa@m%|JIM}1kITnC8^ae1yWzwFF31i z-N@!mYu!;`{csi{6<3~*gRg+HIGr($%L@#PDLkGp<4w1`(4h<2tapHVcvcuCaHG7g zhhaIzYvUX+qPfnQBuygB-@RC*g0_RpsBq+sMu_H(2YC^V<})-^XoYy77x5S)&sx*2 zAxY(=OGIil$SV$|R$zI7HVxQ|Y}lyNiham9xeLoDXp*jk zNp`#te;K%)PKbS}%Yv9JHhkEi#wBH_X!fNI6$}I*1bk}acV1eQnV3>EE7 z@%%69g0Su&ksj$Y6n!JF9I8UUVQm&3hCA5L##!)!5jAHbqn9WXO9ElRSSO0aQp2_u zi2;#13Q*XwiSb}CqR?8T>)Gsz;Bb>T&j%w#Ekop$k~qLJB!sWu!vn-L0E&Xj(qzG# z;S@2&bHW!bm1PHB3TKHCAEZ6rX2?)Tntp7=LKH0cJx{w2-AbMqJcqh z!IG@;G+Rp;F{@;NVR*GMq_XTO1#T&cbWN}G!Lxe4l(m8+(z#h{#IX1RPuFz4;WaBR z;#-mfzMFgD1w1p1zbzA6IqU@&@E9>NSyC*QRyLPJ3wXF#aanaZGBhC98wx|{fny(& zEr!&cBq4L0hmSFQ#3AJQV?;IgRuepijA%CT*j(ub=8=~-Iz0A~0BbFURY7dpVQhe3X9RP9&2I;Phuq>L37mX7jXb-|r;Z+Z;kv7GDirfL9l=q2bmGAxDWOzO`aIK&l;)H~+2`qIB={9kQFDp>zT2qW z-v}f8Hf%GmTal+OaIp)v14NC)$wlWZ=rQTCid;CifUI+P=l*UPDHj`#29$qZD;C1C=yiK57(>OgCk54^K>E81;Tx z`F$aTyDJ}$(6nL%MC8jSjM(HQAy@v|(9t%XdGbj^v$JjuKnKrQW6TkKSQkHISj&lE zwhM&M8bM#q73B*qOscOBr;qv30p=@CWwYeK zsb*!jU()rKHE+LU^tSq^IlSA5k@BdS`{jsC<~?nN*1NBWq>uv9!QPmB&5&&yHsU-A zj_ho}+_`g_w-34i{zwj1w{M6HfHh0cEEt@Q$=@5gt$@VkTSghm<(As)w#%lZeA_6j zW)PPtRB&9KZ^Lmi9HVhG#iH2!BR(Ml$FLq5W{B~)HX>QYaVVaX5a&^C4qd6CyOFcC z9=?ct->AiMsjroo}?%7~ZOkNx*szL+UnAlzY79K^}rEC-d*<=$PyKplgVZ z3%U&MD4j&EL!PAvgAvU$SqdyE7e)CN$g=~EaHgY)<>feF>p1~aDvNpQ79lUk_UPvZ z9QD$aJc$MJynu(DuI5OR3*_v8tliRPdglT;C!qV|w3=DE7O=bL-fpmr>Aq%q_X2kO z9Ni!8+RW0kK+X+VbPGTm{e@xD6qcs2Zyec~ z=wipt&_(q63Mmdv#zxqyGi)W5wo;;pHY+Zjs$y*u_~N7N%NY*NR;6CG@HpE8y*R_y zi&*JsXcRN_;1rD&R4M#mfl6SfutWB9O^H_2B(@4WB+e^AIZ?G%Vk3*g;y?xMGj(EZ zlA*HDzN)w`OiDUj9g`<~A#7`QC>+~}LauzhSg~Y$+_UW79q0FUJ8V9ctCUS4EVAqg zuoWCwhL`VU&r{z9BO^ZURqS~h5_>~U#}S?`t++nS-Pv1r^K5CuVL7k%_x|Sny?>arS2w2m8Lv<$4!8w1zBTb&W4gIF1FqTE!1w z6%Dj-{Hxiob$C0~G%?2BtfH{EYzRXN7G08ESw#<)UG8Dw+|mG7snz?iT98Et>m`d0 zkJTDp2W;^l@Yp&AhD>ZPkIPoET;1T1D|ukHA&cc@y7?luiic(gIGhfy;c;3GddP-G za=B8qlAU8i4y8Ej4W%&hR1J`DRki%ZM%gP?&KeH*pzdSr4;!XlS?btUvooy7`qByx z0&$V!U@V7TR>;+Oc!<7#d?mZXRZPaWH2|wZe7GmUi}k%TdJlzn`Tr%*{sz7cbED#;v)n=~7SbzJYLdO>79xg< zFlwjdPWF{s$OC08Im+lMFnUw1$7H;X zluq@tpiu5=+1y6H0*x;{LfGx=Ln8L#vc|Kof?g@st(x7?&bn93^XNQJAz#R8pGEua ztt|cUpqApM5()O=TN&tYr!Xks+WQ;YInY|9cxF8g_PH^nW?h?(g8PRp*kSa)U6X;L zcT-4pjsCEmpc-R`-m4Rkv<=2~gLV>5%D1=J-cEzRM_Q6S`c{!8ow4s^pT3opE7*H? z$<6J=YPnwPAEuekxZ-+CJJS*@B6jp1xwV~+6_|xIR-U_Z`}%ecB$gYYS}AX6Cv*A} z>wh(eS8*_*^&PG(45U+OtRTnxkMK0~R)#^geuIN7R^%Dw(1Yi}*;mdw#yIlOjBd`y zoo!?!I?AZMT7t)@w*;>aVesFw<&HLDvA#TqtrPW%7R^qcwB8aN8)Qog1UGm{}a<@Bc-<-mtCu;5;Y%tv^x ze=D5_QD{Rp$`k%usr3qqiXOY)F~;GmRyun8DbKK)9{^}&NW*J|mkjk_b3fir&lTZT z6_+^`;c`D1#glEY6 zrKn1uYQys@F7POwz5g`ms6bIyk>gm3^IeDGccNyBjBH+;U$bd^Gu7vq-`R>Pu5_5yt2tt@ar|0-jzJk-q+r}Y z{R$MQ2?OIYU#Uq06C7UwRnsO3f8zoL`S}fq>f}5xsQml}z%F@-g8cjjz;1r;3DjN# zd-$~{fGCPe?&VjW2s^!2a(0-oQ*0Gyg$X;&MmS_Z*r_(k!2-fgw=vH90`4DMyV2n< zHU_8dYK{(!T#L+S{7@sIphX8Qk_uXEP@+>oiwruT&yKJb7gRuJ9iT~u20aQ|I-o0v z9y+L&4%iNczBxck2XqP1EeGh*0mrhTOHSD7wUYgD!cMVO?28k2nvJj{PS~k7DvJ$v zx{a~xO}bNVHM`uKR*sr@3<35jXwq@=Tfc%P9H*uh6g1fwP+6j&iAKkBse&dMLl#8^ z?X__%>VSgw+US0l6tvgI0jTKTQVq*~3}c{gOW5hPlAT(@PO(+&&k}Z;jj$_A*r}$y zSi(*>?ZXmw%4zrYnoS!`Jhn0ao(MpbjuTX`QqY9sc;w$#&}8Gd)K`hD-n7nz#aeR( zU05tKSI~v+l9dX&u-&o>dX8*%VSCuk1?a-|vS$m>g=?VM0A1MC?3#{m9Uw;Am8TFEI?gm&4S zT*W>XVW-&$dsl>=YNPB=5q7$bu@gnuDYu$EC&H#3HhZGCMA+2BcLXL3HvRAsf!8UF zdcalypAfj&VABs<#BVX!5)NBzZ#CHT!(QCi8*KVv$L<>pHvMp}=njKTKkNX$&0weB zO7=#eAL@g~zlz-u!cM;tPJ9AJfmQk_`x%6teq)^bB<%EC&5p#{aaY{15&10&8WmfW z;jM#e%40V(oOFPuJ9j-RXsTm(G`w+8O>^v`eyf6}ICfjZ=LXgE#&&G@*8ojzeDwom=oAxnnvKvCChSxjm3JHLbQ`0aOS)5THND!Ab*oG~ zP7?T#f+ii`xcabyCLABR`iO!iyE{Qo5MfO;K67=if+iW?xq_z$s=YQobOpB#puIM} zbOkRCpuILebp^K#pnGk6>k4ifVW-zhdS!&2Vyozd5q6r5(BmTPR2!v(McC;!Mt6#^ zQ*Jd~r?F9&+n8#W+H2$B zwknTv-p7sxNUwGg^F9j719!#Q}4 zxrY;RgB5%kuZBs&0Nu-JIIe39P-w3o!2&ef@EKxpo)#Ff$}>daOf90gQQVMd8DgDi z!uPhI@o~#eH06&%ESBo~xY3&LgGyLT(ySAj^~W*2mK$n*D>UyvRJ0u;8>Ryq`pM84 z#S9R>;A-wD)*Vm=Cfe|MA~@@q2G;LQJqMow(}_nyMvZa03`p*Yp;M0`8>BH@*ykxa zpD$pgO&>NL;kbD;hxuF%C#s~ev*iULy;iAbd=zLgIW33_XCnnH)CTJT`mpRLvh5^+ zZ71^`58_VOZS5iha~0N$JqcSK(zC?(sesN z)Nx?R)k{4GQwcXt1h7F(W#N{Amw66+%O`_{w#_oQFzX_4l#zI)rPv#!eRe0mNY9w0 ztlKo|(*zDO(dBB~Y8DrG;lja60m1BwZa*Xe9y3F@&Cu*U5POMXv0l@r&4|UtPs%F0 zJC3jFV<8b~8h6yefhYlqnFt6zdWOKVwq&!sC>6=vX;i#rYlJKVh!9S?O9s#2C6ECU z;I;<|G90SKRDoLX;vE%V)g2u0VX-rly1}Wpq-mASM1t&Bf{r7krplhnjf8pou-HJ| zMTX7>-^4RD7CUrhS;&p}Q-%&ce+6cZH*NT3%eB+2Snk8k7}}=av7*WLpeq@h+OkTG zPpNI6qh%}0?Bk%e2-hS-?fwWIYbk@%?TrEHScouidZ_MqDJlnEWl4kqvsB@l4>BMr z(d$4`#}X(D{dt-S+JWAy^?^Ai6;N$G8KSVM=vEHW2ZNU zG_eB6ulyhrs%tsIgJE(Tk5Bvz6`6m+y~}V10?XWC>;%Aj>{H92}t1~a^!J%U$u%21WHkCZsl{Bir0p8*PYNIC0c~ODjlqi(&DGtqH~` z5ptb{#2iaG?>_=eb=WrRdRYeL>9*DCvI5q0`mZFB1?^E$zp@J!<{zf8+IZ0F6xqq6 z7>tf$rBY;f9c1*7{s&Qd)9H(B5R2>vhT$<);*WneLs4YEFr-^}9>-(0kL_NOeMPe9 zdk)|jN1WVZWGDdg^-&2t&H-6x4AcM&TU!~jzC=1(h5jTXn4g>u zf8EE{H;s&rt#%(+2OCIRw{f~YkAgp3;8F3{X&BMJdmDJ_HFIw3cirKl;_AQ=CWu9^ zhP&p@%)&Zcg`WQg6y7oX+KOKA5DL~Pdh}yRqH$QAJ;n(6H>*o!)k0o&sQrPYl=u);x+XxA4rP;n7OVR+| zt`D*bXpak!3J0%E-a5SJHITRlX%#!B#0t`lZ#%Utw()#ZYln`WZiAQ)s37gCbllst z+KA!nO-Q$Kv}MP7%R$vA&*fxO$YIT+MzlM}mQ+DH^+BWQWo<%AT?4I-EiDbbSh~<{ zXwz#IbYl6s?wC4-8Vy5Q1^>!)quV5gdaXpt(GDCv{WB`5iu7-n<*hYai%`lLT7L>C znPt2cwc)?^(BnuO)*D*=4WKs~ts$%M2~t{)hSk28wf1BoGq2@~)_$t8x?0N@>-8or ztH&2Z89gJ`s9#1py`tlj&YH#g(4G#fA%P{2wd(j}F=Yp!Z5`!r>J%{hx)C*Fv#Vvt zCpnAsEWopxN>47YTZ8EnwoJz-ErS-56xx}o^M~HsI>_FFwubo>m5wv%Q6{fIPB0&)(rW~5(;pxo zSQe?-D#${=2c;b!d4QcsBfV={&IeKQs`$Rs&Xf1dlMl>8 z0D0d$dH=kZ;1|ZvhnCw1gRg^6MZ$(cbbAUIYM6@3 zBR!@e4PvDfRuWGYN)Kh2nqr%erV7L*!)&VCwbw9M^UG72r=r{GwE>0c%AH-qsT7+> zCojP2NQIp!oW%%5ky{3*(NH+s9tSOBB~-man`AOE*N@}Bn!3i>S6nr+ zVRY=Ot=opyj9xRgdHmw|!k&fQSFKq;viADXu}fSJd+WA^n^;F#PI-%*dm3d%>xA`~ zqjjPSbb5nfy_o@s*W8)*6wZz5-N4xxtm#9j>GUCKmj}>Zlu*0<)p2GRE(uY$;buMN z%_~ZOHsB|r($s!t=&81bquh&5_Hx}jMdGDg92qr@+;;zUD)~6!#_5fSaJA~a_|)m- zH+!?KVX6CO4a+pKByN>`VAgc9r}A%Ay(aw_$Nbq||5KEqHI6sUnJ$iIk9R8l|3<@7 zf4m5;R@uqZr<2|61Gk2y-q&ha%0?ngA^W=+51P8XM7?4S+N2j{&wYkZ6C}C*?9 zFhx9@pEX@PQQx{(p&v% z?~!Yeru@DZ#ePu3P$n`5S8JT#eW8zYUK^cp)-adZ9_R7;@+z`Tr?;w1>V!R3L>u7ba)8Uj#fUO*G8uv zi!VKv9?>3&ifGf}R+T{=P;haz>M%F$bvUPuP93&kuJKsqHSEWzh&CNQuQI5^^V{NJ zJ;kO(mkwA9fGesab+~WIv2V{@xI!AkK&!d=FC|QaRTDX{CSsH6piT h-ZAvozTPH-b0(t?MR~r_#>4*7?T1v5yHME$qc9>9EW1Iep}C=EH`sOSbIL_rjEA&BgUz3tTeh}r|^aOTYOe&2J3S?4Kmkr)M9 z7i-S=Jq^+m=jeUf6nokS5ToQe>wDPT$<~YPXk}M3z6Jso^@ljVpVPHOyhgOj=%_S$ z%8dRxG8E}&LpK$dscOS}hJ7bEX!AarchkC^OCGw)xK^4HQ=gGQQzEjl3y)ev*xQI- ze1L{J&ejm!MrS#FYly8j;^#K*@qQZjaITv63VPRa!_BRg#@#>n zpd-IZk*k!eg{~l8@+$73TW}#mx!haELx;Ca>q_31L5gJ6Lalho$V6~Hcb4;D5l^ys zA%1LZOsi#CVbkncQK^VpSOmSal@iP0el|}tc{!KY>AZF7+ZzK;GCNM;F%B1Tb0N=! z&Ej1eAExthDxd%2^_T(|NHqz~kzK>(n#b^pY99}-o2#3sYcF;{+!g5gY zX?#lMi{j!Pe~16IskdsgBE<~qrVDtU!N?rmNbH~W?Vwp7uE~rllE?5;9w{}SS4nuK z%wV{BUV8}x_9tD!cjZ_GVx)IZ(m*v*T33yu?Ui42u>D3 zQuH4uiy$fb;mIOMie7NC2$G^VnJj{&5Z(GR21(J$Ocp^>bb^ybkQANtWDz7qzsO_} zBt^g6WDz8V4eY+Q9>%2TB1{%RQgmS^iy$ewSd&GN6kWi{B1noZ>SPflg@xqPnZ(x9 zSak6xiy$fbKqiYIDf*Bmiy$fb;3kV8Df%!ciy$dHAfK*EXOdelV;}lt5hO)_2$MyS z6#a2b7C}<<2Qyg&NnwzWI6+dFkWZgbSEVzlt^2cl#0iqZ0`d_jND7O}N1PxjY$P9X zf~2syEyM|uqT9~S&e+}E9eaCwV}E~t93CEyqobp7e0)4kPEN+@>FGE-I~x}l7vuW+ zdfeXLj=Q_Naese59v&XXG_j~#)pQqn}mwcu8{(auP@lSip5mNvF literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/close.bmp b/applications/v4studio/rc/close.bmp new file mode 100644 index 0000000000000000000000000000000000000000..aeedd7d54e9f51bb54492b9e584d18140974071b GIT binary patch literal 238 zcmZvVF%m){3`Ez_nP2KLES>QN_TD9x{E{PagK#3po&q0bw-PVx3NP>s z4?3aJ9{NvQV#_@ZrBv@-wN@H4w&q+FWUUA(KNz_<+3^mxVr}`Y*v!B@0~;eI%|T;+ WfDi61?iX$A#SqH$V)4tCy{Nx&|3)OM0 zNq)Yntg=xh9>?d*{N;9@OyT!0-tjiv)Cz3AyzP{vxy7<$~OeTT!c7JQf6T89- z%?kmn<${;(P#uFN<0A#FuUo(qgT{&F@ekspii W=d@ZzPG(Cb=QBs}_=7thzxV(viB<;y literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/cut.bmp b/applications/v4studio/rc/cut.bmp new file mode 100644 index 0000000000000000000000000000000000000000..15554338a01bc4d8f0a501c32237e84200e64583 GIT binary patch literal 238 zcmZvU!3}^Q5Cqr6gr}Ba8OD(CvKJo2^+{<|WDB@(K8*2TclNjo+kV075Ef~cCTYMZ z2hQ$)mH}PlY>+FWlp?l4?H9GSBD!~uE%28ISaR-wrPxe$a^ z3z}k}8a(PT6+_I#ss$p5)ly7b@GC~Mg&4(XT5vf7Ssd;QV$6pthFgSe6fzsG63D<5 bM-~CHaVdr=Pty0+l literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/group.bmp b/applications/v4studio/rc/group.bmp new file mode 100644 index 0000000000000000000000000000000000000000..08175bda244e0f5698a2dc8c3cc322d99fae43ca GIT binary patch literal 310 zcmb`BF%H5o6hmE!txMq)oS+Bj-n%eSq%PUQjk4xu>HAI*V#kU79Y;#Mtn(n_iQeHA zPH@l#l`6cAosoZ$B(_rO1I4P=%8ZNVt=7UpRz%2l;z9o7IR_$ty^UnQ8{HSTz^tNo2jzFSDIDS1`>4%3WFn%sEbh;9Dzh#mcrl&B)MF|PjzEG1#8bCYkwnW$S&yzT zI0A|K5EKSSAWVr`j9DxJ{@z4)PAi)6f)C<+ER3zCllZl6ZI06YaARhYR z2qf5)c<6^Ckl-NVp&yPwg2Oq2emDY&N~6(8#^bR}CKH)Xr!t$(WImtEVzH3raw)6T zO4jSOY&IL&@Aq;z9OQUB%IS2H^Z6{7%SEo&>(_p5({Cz1|DQhIw_VqLw86ekv)fgB zSG8NJ2{my@?|1Ev_Au6d@7mjc+qL}9{9S+iZT+@y4dZQUO>gtR23z~@R*erU+w{BQ EH_2KZCjbBd literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/ils2d.bmp b/applications/v4studio/rc/ils2d.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1ff4094df7ac0fd67a709cacb6d5d731308fe640 GIT binary patch literal 1334 zcmb`_F|OJ`5P;!%QX)l)o)o3z1oR``0YV*#3(!u(1vmi*Fpa0dqHxOvI04esoB+{t zg2_KNCa+A9wZ6rkUC$1?{`LFgs|@Yqi^!b2A5U-cZ5((L`T2b$+)qupF*pJVA*e7o z0*RWaFgOBTzwTQyt2qcIOm(e={i7G~6a0C)npu*q?B&ukI!4XK* zA5j<_fkgc|g~1U>umG#ua5;S-QJbJJI0A{<7=^(RNYrL2430peHc(-31QNBW3WFn% zAQAOcB|cn1pQz1O7#x8_9f-o<2qfx|6b45iQ3t0mI0A_}OohP_Nbmyn)UH$|FV1Rn+glbo+k{m83Qx9=C0tpgO z4{Fa0C)8&Jx7o2qdb#zrV|DHk0{$E{nxNmdmBARx4Sr*Rt7c zWV_wUZnu;DelN%4QBJ3moX=;uTrP6GUgdVX$>Z^O#4FG@&w1lb|9Wqn*f)nU&Ha9uG7hix-D@UEy0&S%DN%o@hv|u- T!}L(={T&(-e;S$=`tffN_D# zmjC}5fU^IQ0GP`F6vxy86oerp4G2M?EE<5Thf5)gK-h3Cm{PbzfLd@#QCS=S{ogit literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/layer2d.bmp b/applications/v4studio/rc/layer2d.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9d8e28b8d033c89295d8d97979967d22be1c6b20 GIT binary patch literal 310 zcmY+8K?=hl5Ji8WkU%zVPSF$e0NLj*u4*6~Aqxw=Om?&NX7&Ze*u&8RX)d`!;^`jqkYl0rV2j_xYb?A4MXEQtkD#773+YYggp7@QU|#I!M# vmoc0sZ(vxAUEyDpLhBN96*QFUA3oyK6VfZB?Osx^x+h9O%vZ5`M;@V6Z}e2SNVGQy1r z=gbh$kU?XC@-Eo;1M8Ji9Jtiz`yn};>rw?YIDFp)4{;I3{k~vxvZXG$aiXIYi|={g E3w(AS?EnA( literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/movie.bmp b/applications/v4studio/rc/movie.bmp new file mode 100644 index 0000000000000000000000000000000000000000..312f6db7612b31a003c9b731103772745f116032 GIT binary patch literal 238 zcmZ?rea8R+Wk5;;i20$Ik%0v)Q2``{xFJ{oECM4M7#M(vp`ign0ZAZ};lP0dKpG_R xAB-6O{|B*v7zjZ^AOZ^bIXZvg3Zl_)d6)(m4OIu1N7Dy02QClQ2LP%1FJ%A# literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/new.bmp b/applications/v4studio/rc/new.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d66feb2384ad4c626a078c995c1a3e49af78ec3c GIT binary patch literal 238 zcmbu2yA6Oa5JQcGL>b0l4tjP;1(ew=nJ8j|<6WY3bU0`GK9aK^RUId`!4(ELX-1{H u>6c`QQqJ8_T9s0iQsQ8(Wab^@`3kuPA_p->w)Xt4Pkz>06 literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/open.bmp b/applications/v4studio/rc/open.bmp new file mode 100644 index 0000000000000000000000000000000000000000..bbf93fe03364e63f3e18a3507a0e85ab8ea2f87d GIT binary patch literal 238 zcmXv{u@M3>3=;=;U&0ua+_3>YyQsonW~6N3OvKTr2(k@|B{{YeKb~(CUb3rr5zpcQ zCn>O}mP9XDa6kd~GO1FGG0<95V`X5i$V8neN$dxz4&8XX3!AZr-;ApY^eS-9oTLj~ Y^Hcc9z2|$wrXTtLH=Rc2n(WoWFRhqJ00000 literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/orderedgroup.bmp b/applications/v4studio/rc/orderedgroup.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9d8e28b8d033c89295d8d97979967d22be1c6b20 GIT binary patch literal 310 zcmY+8K?=hl5Ji8WkU%zVPSF$e0NLj*u4*6~Aqxw=Om?&NX7&Ze*u&8RX)d`!;^`jqkYl0rV2j_xYb?A4MXEQtk$83OZ*I;# co}I%%@f6>ra3+S=gC69}eM%GII+SAI4J;x>B>(^b literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/paste_use.bmp b/applications/v4studio/rc/paste_use.bmp new file mode 100644 index 0000000000000000000000000000000000000000..066ee7cc989458b85cb51527618433bf21757ab9 GIT binary patch literal 238 zcmYL?I}So25Jj(viI`VtT!qUphJ?znl~;)+CGNmixdnS0R^%4&4461@@0m{+&XpI>trgm!^{dt_LmX>iDJ4&YS&G!bl@h&=!QCID qhCceRU6yBS@kgJS^Xz0SrR3!78)syru}pf=XYX6u(44!I^!Nh0mPVNX literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/preview.bmp b/applications/v4studio/rc/preview.bmp new file mode 100644 index 0000000000000000000000000000000000000000..da1f4dbc4be6c2dd8fb9d7cb71cc48a4a7c139ca GIT binary patch literal 238 zcmZvVu?@p83pv2yNo4mlT?N_pr2ISnd1NHDM5O^cFcMc59&^> z)PfrwsOeYM05)2ipmU4fJIss+o(=cdQCu+!%TlZiKDH!a!aP2;C|UGpLD9M;zmsLJ XIp;lzIUm!ckI6HO{IjaYcZ_-fA4^nl literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/print.bmp b/applications/v4studio/rc/print.bmp new file mode 100644 index 0000000000000000000000000000000000000000..00319b55bb23c3c69cc051144d80e5275747c53e GIT binary patch literal 238 zcmZur!3_d23^Ni^o@EUFY`~vgA|YX<`*bfpod|VizM>j+f}Fx~Q7! zgtHKM7yg3|DAD^Sp<0YF#C}+nSj(MC$5#Ss27*aN<{l9dp7pzrGpff7&!z1W7RjCyO8{ z`oYN}NQ!=WvIvr*7o04Dr07j1iy$dPx4w))QgkwtMUWJo;A9abMJGL31WC~!GFb#k z(H}Qi1W927yRWT>F)6wTlSPmeU6{!tNQy4jWDz7q7jUu&lA?<`Sp-R8A^CJBvGp_- zUHr)+NQyp?$s$OKKBUPaNQyqV$s$OKKFrA?ND42=r>oML7~~^PkQ8pnr%$M>(wWrOJuDw_f~2s3e8dTo!lLpKCrAn#$w!Fae}1ic6xd`&d$!pZnqoz{eBz{hjDRnF)lAJ$JN!L; ve?J}`9>(M2<9K>{8qd$qJQ3^NitoWUKaQYXHtaK{cvyw!_%p*Ri9bz(b-vmXx}r)(2faS + + + + + + + Create a new document + + + + Open an existing document + + + + Save the active document + + + + + Quit the application; prompts to save documents + + + + + + + Undo the last action + + + + Redo the previously undone action + + + + + Cut the selection and put it on the Clipboard + + + + Copy the selection and put it on the Clipboard + + + + Insert Clipboard contents + + + + Delete the selection + + + + + + + Show or hide the Scene Tree + + + + Show or hide the GF_Route View + + + + Show or hide the Field View + + + + + + + Display program information, version number and copyright + + + + diff --git a/applications/v4studio/rc/rg.bmp b/applications/v4studio/rc/rg.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5bfe4f1c1efcc826e548fc6b50bdc740fd827d15 GIT binary patch literal 774 zcmZ?rWn*Rl12Z700mK48%n!tj3=%++f#CuZ1A`DZ1cL>D#61RuvkV46w3opFh}JR$ zVj_qXL>WXqT;&{wL`(!R0jLIUGDHNZvWKA%69L8HS|D}-xp2i*3@ykAq8*|ItKvk4 zxyT5p7flP?aG(@Wr6YV3`7rsq)3!X>4ws(){6V0MX5FKAjcylGXqN-n#XsS=YhW0WogZT{L^_i_GT%*k@+ph E1NJ~MtpET3 literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/shape.bmp b/applications/v4studio/rc/shape.bmp new file mode 100644 index 0000000000000000000000000000000000000000..8672ae8513413fe082b311db96063c9170b1e90b GIT binary patch literal 310 zcmY+8F%AMD5JiW?Zkxr{3wQz#VDDYDaZPCG@CqKoik53x;hPzva2&dAeKYfB?GRw>S&yH;*V|>V(1Uu$ zM|{A8?Wkr2pT=hKP1%fYIk$mIv{DMA7HV{>RSDCCD5>iX|H;fH@#z`Lk>^m$j`rTU iR_D%N-&_Ya?(2RV|M;@r&wG*6;;ui5C*UV5An`Y#=2|-d literal 0 HcmV?d00001 diff --git a/applications/v4studio/rc/text.bmp b/applications/v4studio/rc/text.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2decce39882ecc43b1e9a7ff73e1bfc49222d99d GIT binary patch literal 298 zcmYk0%?&~^41@h7q<7%J&lHF;xU)-65>JF+u@$Sdh|5JU$lQVNJQ3!n}6e%=pgD_<}*2+Yz5YBOr`1t>vyt3|(LMK@8j3+#3B&Dlo ygm>;xkr)m_l%iTMX5l^3fyO9s)fg6#}AJ2s6x1u~4 z9D#%oR2UqAM7^jmI0A{9tS~qNiCR!$a0C*yiNfFrB#8FaXdQt>C8IDn0*OjcVQ>Tz zm9)a(2qfx56b45iQ6Hx;I06YaV0Y`Q(*_cC2nvHEkf?)E7#x8_9hSo22qfwN6$VEj zQHQE9I06Y4BA?2{`x>-~I(&t}5lGa9C=8B3qAp2ca0C){aSDSYkf_U47#x8F6Ue7d zr80@WCT(51!r%xb>PJu*9Dzjr914RYkfTzG~`1ZjzEF|@~IoDQ>jd{ue?k? z#Nh}eSb%(p!x2cZDESbFBamPt@*xgKAi?HrK^%@iqRL<}kl}DBqtQsln^%PNzTX^^5+eru$cUk=LuR z$n$0YJ9}UDE0rHzw{DtMg&BT2JpJt-M81iv*KO0Z+w<_cd1(5=1SbDnHMi2On7{2_ unB8sL=Jr5;-F44W$!u2-!us^PSC+0o!+O + +#endif + diff --git a/applications/v4studio/treeIcon1.xpm b/applications/v4studio/treeIcon1.xpm new file mode 100644 index 0000000..0263fad --- /dev/null +++ b/applications/v4studio/treeIcon1.xpm @@ -0,0 +1,41 @@ +/* XPM */ +static char *icon1_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 2 1", +/* colors */ +". c #000000", +"# c #ffffff", +/* pixels */ +"################################", +"################################", +"####................############", +"####................############", +"####..############....##########", +"####..############....##########", +"####..##..##..####..##..########", +"####..##..##..####..##..########", +"####..############........######", +"####..############........######", +"####..##..##..##########..######", +"####..##..##..##########..######", +"####..##################..######", +"###...##################..######", +"####..##..##..##..##..##..######", +"####..##..##..##..##..##..######", +"####..##################..######", +"####..##################..######", +"####..##..##..##..##..##..######", +"####..##..##..##..##..##..######", +"####..##################..######", +"####..##################..######", +"####..##..##..##..##..##..######", +"####..##..##..##..##..##..######", +"####..##################..######", +"####..##################..######", +"####......................######", +"####......................######", +"################################", +"################################", +"################################", +"################################" +}; diff --git a/applications/v4studio/treeIcon2.xpm b/applications/v4studio/treeIcon2.xpm new file mode 100644 index 0000000..d3d76ec --- /dev/null +++ b/applications/v4studio/treeIcon2.xpm @@ -0,0 +1,42 @@ +/* XPM */ +static char *icon2_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 3 1", +/* colors */ +". c #000000", +"# c #ff0000", +"a c None", +/* pixels */ +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaa................aaaaaaaaaaaa", +"aaaa................aaaaaaaaaaaa", +"aaaa..############....aaaaaaaaaa", +"aaaa..############....aaaaaaaaaa", +"aaaa..##..##..####..aa..aaaaaaaa", +"aaaa..##..##..####..aa..aaaaaaaa", +"aaaa..############........aaaaaa", +"aaaa..############........aaaaaa", +"aaaa..##..##..##########..aaaaaa", +"aaaa..##..##..##########..aaaaaa", +"aaaa..##################..aaaaaa", +"aaaa..##################..aaaaaa", +"aaaa..##..##..##..##..##..aaaaaa", +"aaaa..##..##..##..##..##..aaaaaa", +"aaaa..##################..aaaaaa", +"aaaa..##################..aaaaaa", +"aaaa..##..##..##..##..##..aaaaaa", +"aaaa..##..##..##..##..##..aaaaaa", +"aaaa..##################..aaaaaa", +"aaaa..##################..aaaaaa", +"aaaa..##..##..##..##..##..aaaaaa", +"aaaa..##..##..##..##..##..aaaaaa", +"aaaa..##################..aaaaaa", +"aaaa..##################..aaaaaa", +"aaaa......................aaaaaa", +"aaaa......................aaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +}; diff --git a/applications/v4studio/treeIcon3.xpm b/applications/v4studio/treeIcon3.xpm new file mode 100644 index 0000000..0dba5be --- /dev/null +++ b/applications/v4studio/treeIcon3.xpm @@ -0,0 +1,44 @@ +/* XPM */ +static char *icon3_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 5 1", +/* colors */ +". c #000000", +"# c #c0c0c0", +"a c #808080", +"b c #ffff00", +"c c None", +/* pixels */ +"cccccccccccccccccccccccccccccccc", +"cccccccccccccccccccccccccccccccc", +"ccccccaaaaaaaaaacccccccccccccccc", +"ccccccaaaaaaaaaacccccccccccccccc", +"ccccaabb##bb##bbaacccccccccccccc", +"ccccaabb##bb##bbaacccccccccccccc", +"ccaabb##bb##bb##bbaaaaaaaaaaaacc", +"ccaabb##bb##bb##bbaaaaaaaaaaaacc", +"ccaaccccccccccccccccccccccccaa..", +"ccaaccccccccccccccccccccccccaa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaaaaaaaaaaaaaaaaaaaaaaaaaaaa..", +"ccaaaaaaaaaaaaaaaaaaaaaaaaaaaa..", +"cccc............................", +"cccc............................", +"cccccccccccccccccccccccccccccccc", +"cccccccccccccccccccccccccccccccc", +"cccccccccccccccccccccccccccccccc", +"cccccccccccccccccccccccccccccccc" +}; diff --git a/applications/v4studio/treeIcon4.xpm b/applications/v4studio/treeIcon4.xpm new file mode 100644 index 0000000..f4cd561 --- /dev/null +++ b/applications/v4studio/treeIcon4.xpm @@ -0,0 +1,44 @@ +/* XPM */ +static char *icon4_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 5 1", +/* colors */ +". c #000000", +"# c #c0c0c0", +"a c #808080", +"b c #ff0000", +"c c #ffffff", +/* pixels */ +"cccccccccccccccccccccccccccccccc", +"cccccccccccccccccccccccccccccccc", +"ccccccaaaaaaaaaacccccccccccccccc", +"ccccccaaaaaaaaaacccccccccccccccc", +"ccccaabb##bb##bbaacccccccccccccc", +"ccccaabb##bb##bbaacccccccccccccc", +"ccaabb##bb##bb##bbaaaaaaaaaaaacc", +"ccaabb##bb##bb##bbaaaaaaaaaaaacc", +"ccaaccccccccccccccccccccccccaa..", +"ccaaccccccccccccccccccccccccaa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaacc##bb##bb##bb##bb##bb##aa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaaccbb##bb##bb##bb##bb##bbaa..", +"ccaaaaaaaaaaaaaaaaaaaaaaaaaaaa..", +"ccaaaaaaaaaaaaaaaaaaaaaaaaaaaa..", +"cccc............................", +"cccc............................", +"cccccccccccccccccccccccccccccccc", +"cccccccccccccccccccccccccccccccc", +"cccccccccccccccccccccccccccccccc", +"cccccccccccccccccccccccccccccccc" +}; diff --git a/applications/v4studio/treeIcon5.xpm b/applications/v4studio/treeIcon5.xpm new file mode 100644 index 0000000..63820ef --- /dev/null +++ b/applications/v4studio/treeIcon5.xpm @@ -0,0 +1,43 @@ +/* XPM */ +static char *icon5_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 4 1", +/* colors */ +". c #000000", +"# c #808000", +"a c None", +"b c #ffff00", +/* pixels */ +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aa..........aaaaaaaaaaaaaaaaaaaa", +"aa..........aaaaaaaaaaaaaaaaaaaa", +"aa..bbbbbb................aaaaaa", +"aa..bbbbbb................aaaaaa", +"aa..bbbbbbbbbbbbbbbbbbbb..aaaaaa", +"aa..bbbbbbbbbbbbbbbbbbbb..aaaaaa", +"aa..bbbbbbbbbbbbbbbbbbbb..aaaaaa", +"aa..bbbbbbbbbbbbbbbbbbbb..aaaaaa", +"aa..bbbb......................aa", +"aa..bbbb......................aa", +"aa..bbb.####################..aa", +"aa..bbb.####################..aa", +"aa..bb..####################.aaa", +"aa..bb..####################.aaa", +"aa..b.####################..aaaa", +"aa..b.####################..aaaa", +"aa....####################.aaaaa", +"aa....####################.aaaaa", +"aa..####################..aaaaaa", +"aa..####################..aaaaaa", +"aa......................aaaaaaaa", +"aa......................aaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +}; diff --git a/applications/v4studio/wxGPACPanel.cpp b/applications/v4studio/wxGPACPanel.cpp new file mode 100644 index 0000000..a1ae41b --- /dev/null +++ b/applications/v4studio/wxGPACPanel.cpp @@ -0,0 +1,371 @@ +#include "safe_include.h" +#include +#include + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "wxGPACPanel.h" +#include "V4StudioFrame.h" +#include "V4StudioApp.h" +#include "V4Service.h" + + +bool GPACInit(void *application, GF_Terminal **term, GF_User *user, bool quiet); + +wxGPACPanel::wxGPACPanel(V4SceneManager *parent, const char *path) +{ + this->parent = parent; + m_term = NULL; + m_pService = NULL; + + // Initializes the MPEG-4 terminal + GPACInit(this, &m_term, &m_user, true); + if (!m_term) return; + + // Setting all the variables needed for picking of objects. + m_iDragging = 0; + m_transformMode = 0; + dragX = dragY = 0; + picked = NULL; + + if (path) { + m_pService = new V4Service(path); + gf_term_attach_service(m_term, m_pService->GetServiceInterface()); + } +} + +wxGPACPanel::~wxGPACPanel() +{ + if (m_pService) delete m_pService; + if (m_term) gf_term_del(m_term); + if (m_user.modules) gf_modules_del(m_user.modules); + if (m_user.config) gf_cfg_del(m_user.config); +} + + +Bool V4S_EventProc(void *par, GF_Event *evt) +{ + wxGPACPanel *panel = (wxGPACPanel *)par; + if (!panel->GetMPEG4Terminal()) return 0; + + switch (evt->type) { + case GF_EVENT_REFRESH: + gf_term_set_option(panel->GetMPEG4Terminal(), GF_OPT_REFRESH, 0); + break; + case GF_EVENT_SCENE_SIZE: + case GF_EVENT_SIZE: + gf_sc_set_size(panel->GetSceneCompositor(), evt->size.width, evt->size.height); + panel->Update(); + break; + case GF_EVENT_MOUSEDOWN: + if (evt->mouse.button==GF_MOUSE_LEFT) { + panel->picked = gf_sc_pick_node(panel->GetSceneCompositor(), evt->mouse.x, evt->mouse.y); + panel->m_iDragging ++; + if (panel->picked) { + panel->GetV4SceneManager()->GetV4StudioFrame()->GetTreeView()->SetSelectedItem(panel->picked); + panel->dragX = evt->mouse.x; + panel->dragY = evt->mouse.y; + panel->m_transformMode = 0; + } + } + else if (evt->mouse.button==GF_MOUSE_MIDDLE) { + panel->m_iDragging ++; + panel->picked = gf_sc_pick_node(panel->GetSceneCompositor(), evt->mouse.x, evt->mouse.y); + if (panel->picked) { + panel->GetV4SceneManager()->GetV4StudioFrame()->GetTreeView()->SetSelectedItem(panel->picked); + panel->dragX = evt->mouse.x; + panel->dragY = evt->mouse.y; + panel->m_transformMode = 2; + } + } + else if (evt->mouse.button==GF_MOUSE_RIGHT) { + panel->m_iDragging ++; + panel->picked = gf_sc_pick_node(panel->GetSceneCompositor(), evt->mouse.x, evt->mouse.y); + if (panel->picked) { + panel->GetV4SceneManager()->GetV4StudioFrame()->GetTreeView()->SetSelectedItem(panel->picked); + panel->dragX = evt->mouse.x; + panel->dragY = evt->mouse.y; + panel->m_transformMode = 1; + } + } + break; + case GF_EVENT_MOUSEUP: + if (evt->mouse.button==GF_MOUSE_LEFT) { + panel->m_iDragging --; + if (panel->picked) { + int dX = evt->mouse.x - panel->dragX; + int dY = evt->mouse.y - panel->dragY; + panel->dragX = evt->mouse.x; + panel->dragY = evt->mouse.y; + wxString pos; + pos << dX; + pos << ','; + pos << dY; + panel->GetV4SceneManager()->GetV4StudioFrame()->GetTreeView()->Translate(dX,dY); + panel->GetV4SceneManager()->GetV4StudioFrame()->GetStatusBar()->SetStatusText(pos); + } + } + else if (evt->mouse.button==GF_MOUSE_MIDDLE) { + panel->m_iDragging --; + if (panel->picked) { + int dX = evt->mouse.x - panel->dragX; + int dY = evt->mouse.y - panel->dragY; + panel->dragX = evt->mouse.x; + panel->dragY = evt->mouse.y; + wxString pos; + pos << dX; + pos << ','; + pos << dY; + panel->GetV4SceneManager()->GetV4StudioFrame()->GetTreeView()->Rotate(dX,dY); + panel->GetV4SceneManager()->GetV4StudioFrame()->GetStatusBar()->SetStatusText(pos); + } + } + else if (evt->mouse.button==GF_MOUSE_RIGHT) { + panel->m_iDragging --; + if (panel->picked) { + int dX = evt->mouse.x - panel->dragX; + int dY = evt->mouse.y - panel->dragY; + panel->dragX = evt->mouse.x; + panel->dragY = evt->mouse.y; + wxString pos; + pos << dX; + pos << ','; + pos << dY; + panel->GetV4SceneManager()->GetV4StudioFrame()->GetTreeView()->Scale(dX,dY); + panel->GetV4SceneManager()->GetV4StudioFrame()->GetStatusBar()->SetStatusText(pos); + } + } + break; + case GF_EVENT_MOUSEMOVE: + { + wxString pos; + if (panel->picked && panel->m_iDragging) { + int dX = evt->mouse.x - panel->dragX; + int dY = evt->mouse.y - panel->dragY; + panel->dragX = evt->mouse.x; + panel->dragY = evt->mouse.y; + pos << dX; + pos << ','; + pos << dY; + switch (panel->m_transformMode) { + case 0: + panel->GetV4SceneManager()->GetV4StudioFrame()->GetTreeView()->Translate(dX,dY); + break; + case 1: + panel->GetV4SceneManager()->GetV4StudioFrame()->GetTreeView()->Scale(dX,dY); + break; + case 2: + panel->GetV4SceneManager()->GetV4StudioFrame()->GetTreeView()->Rotate(dX,dY); + break; + } + } else { + pos << evt->mouse.x; + pos << ','; + pos << evt->mouse.y; + } + panel->GetV4SceneManager()->GetV4StudioFrame()->GetStatusBar()->SetStatusText(pos); + } + break; + case GF_EVENT_QUIT: + panel->GetV4SceneManager()->GetV4StudioFrame()->Close(TRUE); + break; + } + return 0; +} + +void wxGPACPanel::Update() +{ + if (m_term) { + //gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE); + gf_sc_invalidate(m_term->compositor, NULL); + gf_sc_draw_frame(m_term->compositor); + } +} + +bool GPACInit(void *application, GF_Terminal **term, GF_User *user, bool quiet) +{ + memset(user, 0, sizeof(GF_User)); + + /*locate exec dir for cfg file*/ + wxPathList pathList; + wxString currentDir(wxGetCwd()); + wxString abs_gpac_path = ""; + const char *gpac_cfg; + + if (!quiet) ::wxLogMessage("Looking for GPAC configuration file"); + +#if defined(__WXMAC__) && !defined(__DARWIN__) + // On Mac, the current directory is the relevant one when the application starts. + abs_gpac_path = wxGetCwd(); + gpac_cfg = "GPAC.cfg"; +#else + +#ifdef WIN32 + V4StudioApp &app = wxGetApp(); + gpac_cfg = "GPAC.cfg"; + /*locate exe*/ + if (wxIsAbsolutePath(app.argv[0])) { + abs_gpac_path = wxPathOnly(app.argv[0]); + } else { + if (currentDir.Last() != wxFILE_SEP_PATH) currentDir += wxFILE_SEP_PATH; + abs_gpac_path = currentDir + app.argv[0]; + if (wxFileExists(abs_gpac_path)) { + abs_gpac_path = wxPathOnly(abs_gpac_path); + } else { + abs_gpac_path = ""; + pathList.AddEnvList(wxT("PATH")); + abs_gpac_path = pathList.FindAbsoluteValidPath(app.argv[0]); + if (!abs_gpac_path.IsEmpty()) { + abs_gpac_path = wxPathOnly(abs_gpac_path); + } else { + /*ask user*/ + wxDirDialog dlg(NULL, "Locate GPAC config file directory"); + if ( dlg.ShowModal() != wxID_OK ) return 0; + abs_gpac_path = dlg.GetPath(); + } + } + } +#else + gpac_cfg = ".gpacrc"; + char *cfg_dir = getenv("HOME"); + if (cfg_dir) { + abs_gpac_path = cfg_dir; + } else { + /*ask user*/ + wxDirDialog dlg(NULL, "Locate GPAC config file directory"); + if ( dlg.ShowModal() != wxID_OK ) return 0; + abs_gpac_path = dlg.GetPath(); + } + +#endif + +#endif + + /*load config*/ + user->config = gf_cfg_new(abs_gpac_path.c_str(), gpac_cfg); + + if (!user->config) { + unsigned char config_file[GF_MAX_PATH]; + strcpy((char *) config_file, (const char *) abs_gpac_path.c_str()); + if (config_file[strlen((char *) config_file)-1] != GF_PATH_SEPARATOR) { + char szSep[2]; + szSep[0] = GF_PATH_SEPARATOR; + szSep[1] = 0; + strcat((char *) config_file, (const char *)szSep); + } + strcat((char *) config_file, gpac_cfg); + FILE *ft = fopen((const char *) config_file, "wt"); + if (!ft) { + wxMessageDialog(NULL, "Cannot create blank config file", "Init error", wxOK).ShowModal(); + return 0; + } + fclose(ft); + user->config = gf_cfg_new(abs_gpac_path.c_str(), gpac_cfg); + if (!user->config) { + wxMessageDialog(NULL, "Cannot open GPAC configuration file", "Init error", wxOK); + return 0; + } + } + if (!quiet) ::wxLogMessage("GPAC configuration file opened - looking for modules"); + const char *str = gf_cfg_get_key(user->config, "General", "ModulesDirectory"); + Bool first_launch = 0; + if (!str) { + first_launch = 1; +#ifdef GPAC_MODULES_PATH + str = GPAC_MODULES_PATH; +#else + str = abs_gpac_path.c_str(); +#endif + } + user->modules = gf_modules_new(str, user->config); + + /*initial launch*/ + if (first_launch || !gf_modules_get_count(user->modules)) { + const char *sOpt; + wxDirDialog dlg(NULL, "Locate GPAC modules directory"); + if (!gf_modules_get_count(user->modules)) { + gf_modules_del(user->modules); + user->modules = NULL; + if ( dlg.ShowModal() != wxID_OK ) return false; + str = dlg.GetPath().c_str();; + + user->modules = gf_modules_new(str, user->config); + if (!user->modules || !gf_modules_get_count(user->modules) ) { + wxMessageDialog(NULL, "Cannot find any modules for GPAC", "Init error", wxOK); + gf_cfg_del(user->config); + return 0; + } + } + + gf_cfg_set_key(user->config, "General", "ModulesDirectory", (const char *) str); + + /*check audio config on windows, force config*/ + sOpt = gf_cfg_get_key(user->config, "Audio", "ForceConfig"); + if (!sOpt) { + gf_cfg_set_key(user->config, "Audio", "ForceConfig", "yes"); + gf_cfg_set_key(user->config, "Audio", "NumBuffers", "2"); + gf_cfg_set_key(user->config, "Audio", "TotalDuration", "120"); + } + +#ifdef WIN32 + sOpt = gf_cfg_get_key(user->config, "Compositor", "Raster2D"); + if (!sOpt) gf_cfg_set_key(user->config, "Compositor", "Raster2D", "gdip_rend"); + sOpt = gf_cfg_get_key(user->config, "General", "CacheDirectory"); + if (!sOpt) { + unsigned char str_path[MAX_PATH]; + sprintf((char *) str_path, "%scache", abs_gpac_path.c_str()); + gf_cfg_set_key(user->config, "General", "CacheDirectory", (const char *) str_path); + } + /*by default use GDIplus, much faster than freetype on font loading*/ + gf_cfg_set_key(user->config, "FontEngine", "FontReader", "gdip_rend"); + gf_cfg_set_key(user->config, "Video", "DriverName", "DirectX Video Output"); +#else + wxDirDialog dlg3(NULL, "Please specify a cache directory for GPAC"); + dlg3.SetPath("/tmp"); + if ( dlg3.ShowModal() == wxID_OK ) + gf_cfg_set_key(user->config, "General", "CacheDirectory", (const char *) dlg3.GetPath().c_str() ); + + wxDirDialog dlg2(NULL, "Please locate a TrueType font repository on your system for text support"); + dlg2.SetPath("/usr/share/fonts/truetype"); + if ( dlg2.ShowModal() == wxID_OK ) + gf_cfg_set_key(user->config, "FontEngine", "FontDirectory", (const char *) dlg2.GetPath().c_str() ); + + gf_cfg_set_key(user->config, "Video", "DriverName", "SDL Video Output"); +#endif + } + if (! gf_modules_get_count(user->modules) ) { + wxMessageDialog(NULL, "No modules available - system cannot work", "Fatal Error", wxOK).ShowModal(); + gf_modules_del(user->modules); + gf_cfg_del(user->config); + return 0; + } + + if (!quiet) ::wxLogMessage("%d modules found:", gf_modules_get_count(user->modules)); + for (u32 i=0; imodules); i++) { + if (!quiet) ::wxLogMessage("\t%s", gf_modules_get_file_name(user->modules, i)); + } + + if (!quiet) ::wxLogMessage("Starting GPAC Terminal"); + /*now load terminal*/ + user->opaque = application; + user->EventProc = V4S_EventProc; +// user->os_window_handler = ((wxGPACPanel *)application)->GetV4SceneManager()->GetV4StudioFrame()->GetHandle(); + + // Forces the renderer to not use a thread and do the rendering by itfe + gf_cfg_set_key(user->config, "Systems", "NoVisualThread", "No"); + + *term = gf_term_new(user); + if (!*term) { + wxMessageDialog(NULL, "Fatal Error", "Cannot load GPAC Terminal", wxOK).ShowModal(); + return 0; + } else { + if (!quiet) ::wxLogMessage("GPAC Terminal started"); + } + + return 1; +} + diff --git a/applications/v4studio/wxGPACPanel.h b/applications/v4studio/wxGPACPanel.h new file mode 100644 index 0000000..ea46034 --- /dev/null +++ b/applications/v4studio/wxGPACPanel.h @@ -0,0 +1,46 @@ +#ifndef _GPACPANEL_H +#define _GPACPANEL_H + + +#include "safe_include.h" +#include +#include +/*for service connection...*/ +#include + +class V4SceneManager; +class V4Service; + +class wxGPACPanel +{ + +public : + // Constructor / Desctructor + wxGPACPanel(V4SceneManager *parent, const char *path); + ~wxGPACPanel(); + + // Access to private members + GF_Terminal *GetMPEG4Terminal() { return m_term; } + GF_Compositor *GetSceneCompositor() { return m_term->compositor; } + V4SceneManager *GetV4SceneManager() { return parent; } + // + void Update(); + + // Variables used for drag, drop, move, ... picking ... + s32 m_iDragging; + u32 m_transformMode; // 0 Move, 1 Scale, 2 Rotate + GF_Node *picked; + u32 dragX, dragY; + +private: + void TranslateCoordinates(long x, long y, int &sceneX, int &sceneY); + + V4SceneManager *parent; + + GF_Terminal *m_term; + GF_User m_user; + + V4Service *m_pService; +}; + +#endif diff --git a/bin/arm_ppc02_rel/install/do.bat b/bin/arm_ppc02_rel/install/do.bat new file mode 100644 index 0000000..18c79f4 --- /dev/null +++ b/bin/arm_ppc02_rel/install/do.bat @@ -0,0 +1,4 @@ +CabWiz gpac.inf +ezsetup -l english -i gpac.ini -r readme.txt -e ../../../COPYING -o GPAC_0.4.2_Setup_arm_ppc02.exe +del *.cab +del *.dat diff --git a/bin/arm_ppc02_rel/install/gpac.inf b/bin/arm_ppc02_rel/install/gpac.inf new file mode 100644 index 0000000..231ee09 --- /dev/null +++ b/bin/arm_ppc02_rel/install/gpac.inf @@ -0,0 +1,134 @@ +[Version] +Signature = "$Windows NT$" +Provider = "GPAC 0.4.2" +CESignature = "$Windows CE$" + +[CEStrings] +;Osmo4 for PocketPC install, Osmophone for Smartphone install +AppName = Osmo4 +InstallDir = \Program Files\GPAC + +[Strings] +;Osmo4.exe for PocketPC install, Osmophone.exe for Smartphone install +ExeName = Osmo4.exe + +[DefaultInstall] +CopyFiles = BinFiles , Exefiles +AddReg = GPACReg +CEShortcuts = Shortcut , ProgLink +CESelfRegister=GPAX.dll + +[SourceDisksNames] + +1 =, "Common Files",, .. +2 =, "Extra Files",, . + +[SourceDisksFiles] +libgpac.dll = 1 +%ExeName% = 1 +GPAX.dll = 1 + +;comment this one if not using SpiderMonkey +js.dll = 1 + +;uncomment if your device doesn't have GX installed (I think most do) +;gx.dll = 2 + +;all gpac modules - comment the ones you haven't compiled +gm_aac_in.dll = 1 +gm_amr_dec.dll = 1 +;gm_amr_float_dec.dll = 1 +gm_bifs_dec.dll = 1 +gm_ctx_load.dll = 1 +gm_dummy_in.dll = 1 +;gm_ffmpeg_in.dll = 1 +gm_ft_font.dll = 1 +gm_gapi.dll = 1 +gm_img_in.dll = 1 +gm_isom_in.dll = 1 +gm_mp3_in.dll = 1 +gm_odf_dec.dll = 1 +gm_render2d.dll = 1 +gm_rtp_in.dll = 1 +gm_timedtext.dll = 1 +gm_soft_raster.dll = 1 +gm_wav_out.dll = 1 +gm_xvid_dec.dll = 1 +;gm_ogg_xiph.dll = 1 + +;these 2 go together +;gm_render3d.dll = 1 +;libGLES_CM.dll = 1 + +;these 2 go together +gm_svg_loader.dll = 1 +libxml2.dll = 1 + +;================================================== + + + +; Ouput directories for files & shortcuts + +[DestinationDirs] +BinFiles = 0, %CE2% +Exefiles = 0, %InstallDir% +Shortcut = 0, %CE17% +ProgLink = 0, %CE11% +DefaultDestDir = 0, %InstallDir% + +[BinFiles] +;"gx.dll" +"libgpac.dll" +"GPAX.dll" +"js.dll" +"libGLES_CM.dll" +;"libxml2.dll" + + +[Exefiles] +"gm_aac_in.dll" +"gm_amr_dec.dll" +;"gm_amr_float_dec.dll" +"gm_bifs_dec.dll" +"gm_ctx_load.dll" +"gm_dummy_in.dll" +;"gm_ffmpeg_in.dll" +"gm_ft_font.dll" +"gm_gapi.dll" +"gm_img_in.dll" +"gm_isom_in.dll" +"gm_mp3_in.dll" +"gm_odf_dec.dll" +"gm_render2d.dll" +"gm_rtp_in.dll" +"gm_soft_raster.dll" +"gm_timedtext.dll" +"gm_wav_out.dll" +"gm_xvid_dec.dll" +;"gm_ogg_xiph.dll" + +"gm_render3d.dll" + +;"gm_svg_loader.dll" + +[GPACReg] +;GPAC cfg file location +HKCR,GPAC,InstallDir,0x00000000,%InstallDir% + +;UDP socket overwrite +HKLM,Comm\Afd,DgramBuffer,0x00010001,16 +;GPAC cfg file association +HKCR,.cfg,,0x00000000,txtfile +;MP4 file association +HKCR,.mp4,,0x00000000,mp4file +HKCR,mp4file\Shell\Open\Command,,0x00000000,"""%InstallDir%\%ExeName%""" """%%L""" +;Icon number is bin ID of ressource in app +HKCR,mp4file\DefaultIcon,,0x00000000,"%InstallDir%\%ExeName%,-128" + +[Shortcut] +"%AppName%", 0, "%ExeName%" + +[ProgLink] +"AppName", 0, "%ExeName%" + diff --git a/bin/arm_ppc02_rel/install/gpac.ini b/bin/arm_ppc02_rel/install/gpac.ini new file mode 100644 index 0000000..92ba500 --- /dev/null +++ b/bin/arm_ppc02_rel/install/gpac.ini @@ -0,0 +1,10 @@ +[CEAppManager] +Version = 1.0 +Component = GPAC for PocketPC 2002 + +[GPAC for PocketPC 2002] +Description = GPAC Osmo4 +Uninstall = GPAC Osmo4 +IconFile = ..\..\w32_rel\Osmo4.ico +IconIndex = 0 +CabFiles = GPAC.cab diff --git a/bin/arm_ppc02_rel/install/readme.txt b/bin/arm_ppc02_rel/install/readme.txt new file mode 100644 index 0000000..056b17b --- /dev/null +++ b/bin/arm_ppc02_rel/install/readme.txt @@ -0,0 +1,6 @@ +This will install Osmo4 / GPAC version 0.4.2 for PocketPC/SmartPhone 2002 + +GPAC is an open source MPEG-4 framework developped by ENST and available at: + http://gpac.sourceforge.net + +WARNING: THIS RELEASE COMES WITH NO WARRANTY, AND MAY EVEN DAMAGE YOUR HANDHELD DEVICE. PLEASE READ CAREFULLY THE LICENSE HEREJOIN diff --git a/bin/arm_ppc03_rel/install/do.bat b/bin/arm_ppc03_rel/install/do.bat new file mode 100644 index 0000000..783f2ff --- /dev/null +++ b/bin/arm_ppc03_rel/install/do.bat @@ -0,0 +1,4 @@ +CabWiz gpac.inf +ezsetup -l english -i gpac.ini -r readme.txt -e ../../../COPYING -o GPAC_0.4.5_Setup_arm_ppc03.exe +del *.cab +del *.dat diff --git a/bin/arm_ppc03_rel/install/gpac.inf b/bin/arm_ppc03_rel/install/gpac.inf new file mode 100644 index 0000000..b4fa5a0 --- /dev/null +++ b/bin/arm_ppc03_rel/install/gpac.inf @@ -0,0 +1,141 @@ +[Version] +Signature = "$Windows NT$" +Provider = "GPAC 0.4.5" +CESignature = "$Windows CE$" + +[CEStrings] +;Osmo4 for PocketPC install, Osmophone for Smartphone install +AppName = Osmophone +InstallDir = \Program Files\GPAC + +[Strings] +;Osmo4.exe for PocketPC install, Osmophone.exe for Smartphone install +ExeName = Osmophone.exe + +[DefaultInstall] +CopyFiles = BinFiles , Exefiles +AddReg = GPACReg +CEShortcuts = Shortcut , ProgLink +CESelfRegister=GPAX.dll + + +[SourceDisksNames] + +1 =, "Common Files",, .. +2 =, "Extra Files",, . +3 =, "Doc Files",, ..\..\..\doc\ + +[SourceDisksFiles] +libgpac.dll = 1 +%ExeName% = 1 +Osmo4.exe = 1 +GPAX.dll = 1 +gpac.mp4 = 3 + +;comment this one if not using OpenGL +libGLES_CM.dll = 1 + +;comment this one if not using SpiderMonkey +js.dll = 1 + +;uncomment if your device doesn't have GX installed (I think most do) +;gx.dll = 2 + +;all gpac modules - comment the ones you haven't compiled +gm_aac_in.dll = 1 +gm_amr_dec.dll = 1 +;gm_amr_float_dec.dll = 1 +gm_bifs_dec.dll = 1 +gm_ctx_load.dll = 1 +gm_dummy_in.dll = 1 +gm_ffmpeg_in.dll = 1 +gm_ft_font.dll = 1 +gm_gapi.dll = 1 +gm_img_in.dll = 1 +gm_ismacryp.dll = 1 +gm_isom_in.dll = 1 +gm_laser_dec.dll = 1 +gm_mp3_in.dll = 1 +gm_odf_dec.dll = 1 +gm_rtp_in.dll = 1 +gm_timedtext.dll = 1 +gm_soft_raster.dll = 1 +gm_svg_in.dll = 1 +gm_wav_out.dll = 1 +gm_xvid_dec.dll = 1 +;gm_ogg_xiph.dll = 1 + + +;ENST Proprietary modules +gm_upnp.dll = 1 + +;================================================== + + + +; Ouput directories for files & shortcuts + +[DestinationDirs] +BinFiles = 0, %CE2% +Exefiles = 0, %InstallDir% +Shortcut = 0, %CE17% +ProgLink = 0, %CE11% +DefaultDestDir = 0, %InstallDir% + +[BinFiles] +;"gx.dll" +"libgpac.dll" +"GPAX.dll" +"js.dll" +"libGLES_CM.dll" +;"libxml2.dll" + +[Exefiles] +"%ExeName%" +"Osmo4.exe" +"gpac.mp4" +"gm_aac_in.dll" +"gm_amr_dec.dll" +;"gm_amr_float_dec.dll" +"gm_bifs_dec.dll" +"gm_ctx_load.dll" +"gm_dummy_in.dll" +"gm_ffmpeg_in.dll" +"gm_ft_font.dll" +"gm_gapi.dll" +"gm_img_in.dll" +"gm_ismacryp.dll" +"gm_isom_in.dll" +"gm_laser_dec.dll" +"gm_mp3_in.dll" +"gm_odf_dec.dll" +"gm_rtp_in.dll" +"gm_soft_raster.dll" +"gm_svg_in.dll" +"gm_timedtext.dll" +"gm_wav_out.dll" +"gm_xvid_dec.dll" +;"gm_ogg_xiph.dll" + +"gm_upnp.dll" + +[GPACReg] +;GPAC cfg file location +HKCR,GPAC,InstallDir,0x00000000,%InstallDir% + +;GPAC cfg file association +HKCR,.cfg,,0x00000000,txtfile + +;MP4 file association +HKCR,.mp4,,0x00000000,mp4file +HKCR,mp4file\Shell\Open\Command,,0x00000000,"""%InstallDir%\%ExeName%""" """%%L""" + +;Icon number is bin ID of ressource in app +HKCR,mp4file\DefaultIcon,,0x00000000,"%InstallDir%\%ExeName%,-128" + +[Shortcut] +"%AppName%", 0, "%ExeName%" + +[ProgLink] +"%AppName%", 0, "%ExeName%" + diff --git a/bin/arm_ppc03_rel/install/gpac.ini b/bin/arm_ppc03_rel/install/gpac.ini new file mode 100644 index 0000000..830a9c1 --- /dev/null +++ b/bin/arm_ppc03_rel/install/gpac.ini @@ -0,0 +1,10 @@ +[CEAppManager] +Version = 1.0 +Component = GPAC for PocketPC 2003 + +[GPAC for PocketPC 2003] +Description = GPAC PocketPC MPEG-4 Player +Uninstall = GPAC Osmo4 +IconFile = ..\..\w32_rel\Osmo4.ico +IconIndex = 0 +CabFiles = GPAC.cab diff --git a/bin/arm_ppc03_rel/install/readme.txt b/bin/arm_ppc03_rel/install/readme.txt new file mode 100644 index 0000000..91f5956 --- /dev/null +++ b/bin/arm_ppc03_rel/install/readme.txt @@ -0,0 +1,6 @@ +This will install GPAC version 0.4.5 for ARM PocketPC/SmartPhones 2003 Platforms + +GPAC is an open source MPEG-4 framework developped by ENST and available at: + http://gpac.sourceforge.net + +WARNING: THIS RELEASE COMES WITH NO WARRANTY, AND MAY EVEN DAMAGE YOUR HANDHELD DEVICE. PLEASE READ CAREFULLY THE LICENSE HEREJOIN diff --git a/bin/gcc/libgpac.so b/bin/gcc/libgpac.so new file mode 100755 index 0000000000000000000000000000000000000000..64634a20a33a91ae2a37262612fd82fc31dfc766 GIT binary patch literal 4376700 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

neUk?inOo2g=zI(tC#NA0?CCkWJr_ZC!iSKTpO) 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

CLM0&j*|JZRR#s|d6sOI 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�{?!2Y<8Fkc<8 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

@5sL3k^*CDFBFVp!jA+qjmj;No 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℘G08+5aJtW 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

wHNvg;MWnJ)Q!qU@z{Zw{J&sLu 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%0AZE?z`~m?pys=f>QW=O(qk+}e{oew9x+M8 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

t(ZL|?b~H|>hF`U=k(qSOOK^uc3k<10UHuY{ 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

    ?pdsH`S*7^laTQl7G!s7!N96CZgQ}^27pMf0 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%0A4dVC)P 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�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
    c5QxB2HQ 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%0D-=3$yEg(Kb{fQy4=AwYJ7@n!?I z1Bs#yj>+{jox6(i59t=UPOMfpYHzXYuuUa5MKHdmw7MZN@({YYOSJkT8tlNoWeOhs zbH^tkA-08WE7QzLW*piUGc{7*G||{b{u;D+)NpFhkY6LWlH!A6GK1#YxwzJJ#8qp_ zQN3IA*JUu5C?cs8Lwi`)DL&jRNwBjdESc^vVLR-I=X*pGjCdl?nOw8XfT8rP1H7~7 z-7JV*6+DHV%~8)ugQipGrF0`Niqc&)Z~;n(Hxvz@h|-8CdiJEQ$16*YL2TwP0~H)S zxG_q#{AFYF1s__qRH$)JN-v|faZ8^%SLt!St=3xI$4AvYOHRwnr)fkL68Nz>r47m# zzt1(z!X2HTd%NcRcyzmP5){F|o%uc~dcfyypYGqze2*BjG^;${Ak5?*#Bw>#w!OxS%4p^95`De!jLmmntmR#W6~3^?g(Xmi)r zXvrD-1!=Rv!Tah)Vf*8UzX7afSlmUkXL{!TkE0rpB;v1VzqplcF+IS{MkGP|v1*Bn%4D0K?#x1gJxBeHzdg zGD4DlhFjrUc9z?dVF|7Xu2Yfbpl2vZ{TS3Dp#c&>dmW<=0?mM^JI=7+gkf=sQ1!M9 z-kdCDyK9*YTmD^d7`7q|1FwjJb!|_!kZSwddEia(@{RV{sBXp~4vVpZg6_?D+~I|C z^Cy*C&*7U;W$lqXf2))PsaB`A*@gO5KN<7Y}QRpVN)P-6etlXj# z;D5uo2VFx+e*#zrI_3ye*VfQhZV}JFgJrrky1pfP2MhE-^p5F5&hMC_Q^Sd2 zN%wY~m{sZ3+*9m@ zYVIj*CkX(90M%SGjOiGThtgaWE;j z3#7SGjyUgq!RYdSXFOKS9&YX39d+;b)@E^Jt5gcyFd^2_eL62~?tU|NfhV?4j zPyr1Wht+2bVIn)(mK#Fo^w99!W<8JIvJ!^r0vNsR)57T6&V|vpB^W)s`O@s8mu4T~ zz$&CpNPPy^X!6-e{csghmsovryb`w0m-{JW!hUfVe@EM+8SL{7JE}rk+EW=xw#s|Hd93pQhKt>3+VxFcBp>4 zL-pziFE>~_#SOD)IAcmuRmaLAb;508l;p6xn3HwI*Xw0oa-5!4oM=bSCK;kRDn(7z z!$VZ|b7A*w6+C{SjyZ~*{~WX#^et_L_VrcNkPauq%GaHN&cdl!4d`}ZOSE_zULr*F z*YKV&;fNL6A1$69ErujKRGv@fMx0-*)dN3ErO zsCBi^ewH=C&q(PHnXz|oFe1Uw#v&RGa*UTHkTqef8ZJ)25N`zp8RM=YOcNP{FCs4R zvqk5`@=R<$0v(uvbx{>1*ppBKqXR|I@O@0_ec*jZqU7<|#4);7XaFB?1Ox>$?Ps`P z!FoD(LM+ZWb6&k03)syW`%-Q+CXp}*wxQhPS>!8-0VF}S(l`O61N!bwb!<6Bu70{^ z8Z$%8PzTqOCf>%LZDpEK#j91rmvN8%kb@Ye_JLqTA{^K)!tJ<$-J%wRsHH!moZ}

    nK$9lQ!?t~9G(Aql` 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

    imA)|YRtKOKLh7I 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 LA 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 literal 0 HcmV?d00001 diff --git a/bin/w32_rel/Osmo4.ico b/bin/w32_rel/Osmo4.ico new file mode 100644 index 0000000000000000000000000000000000000000..36ff66711771065305f263aee09bda915091c6b0 GIT binary patch literal 15086 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 Ih+0ATC&$&)D*ylh literal 0 HcmV?d00001 diff --git a/bin/w32_rel/nsis_install/gpac_installer.nsi b/bin/w32_rel/nsis_install/gpac_installer.nsi new file mode 100644 index 0000000..1122e56 --- /dev/null +++ b/bin/w32_rel/nsis_install/gpac_installer.nsi @@ -0,0 +1,606 @@ +BGGradient + +XPStyle on +WindowIcon on +Icon "..\Osmo4.ico" +UninstallIcon "..\Osmo4.ico" + +!define GPAC_VERSION 0.4.5 +!define /date RELDATE "%Y%m%d" + +Name "GPAC Framework ${GPAC_VERSION}" +OutFile "GPAC.Framework.Setup-${RELDATE}.exe" + +InstallDir $PROGRAMFILES\GPAC +InstallDirRegKey HKLM SOFTWARE\GPAC "Install_Dir" + + +LicenseText "GPAC Licence" +LicenseData "..\..\..\COPYING" + +DirText "This will install the GPAC Framework on your computer. Choose a directory" + +InstType Normal + + +ComponentText "This will install the GPAC Framework on your computer. Select which optional things you want installed." + +Function InsertGDIPLUS + Push $R0 + Push $R1 + ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion + StrCmp $R0 "" 0 lbl_winnt + + ;NOT NT + ReadRegStr $R0 HKLM SOFTWARE\Microsoft\Windows\CurrentVersion VersionNumber + + StrCpy $R1 $R0 1 + ; win95, NOT SUPPORTED + StrCmp $R1 '4' 0 lbl_err_95 + StrCpy $R1 $R0 3 + StrCmp $R1 '4.0' lbl_err_95 + ;winME or 98 otherwise + StrCmp $R1 '4.9' lbl_add lbl_add + +lbl_err_nt: + MessageBox MB_OK "Microsoft GDI+ cannot be installed on NT 3 Systems" + Goto lbl_done + +lbl_err_95: + MessageBox MB_OK "Microsoft GDI+ cannot be installed on Windows 95 and older Systems" + Goto lbl_done + +lbl_winnt: + StrCpy $R1 $R0 1 + StrCmp $R1 '3' lbl_err_nt + StrCmp $R1 '4' lbl_add + StrCpy $R1 $R0 3 + StrCmp $R1 '5.0' lbl_add ;2000 + StrCmp $R1 '5.1' lbl_xp ;XP + StrCmp $R1 '5.2' lbl_done ;.NET server + +lbl_add: + File ".\Gdiplus.dll" + +lbl_xp: + File "..\gm_gdip_raster.dll" + +lbl_done: +FunctionEnd + +;osmo4 install +Section "Osmo4/GPAC Player" + SectionIn RO + SetOutPath $INSTDIR + + File /oname=ReadMe.txt "..\..\..\README" + File /oname=License.txt "..\..\..\COPYING" + File /oname=Changelog.txt "..\..\..\Changelog" + File "..\..\..\doc\configuration.html" + File "..\..\..\doc\gpac.mp4" + + File "..\Osmo4.exe" + File "..\Osmo4.ico" + File "..\libgpac.dll" + File "..\gm_dummy_in.dll" + File "..\gm_dx_hw.dll" + File "..\js32.dll" + + ;create default cache + SetOutPath $INSTDIR\cache + + SetOutPath $INSTDIR + + + WriteRegStr HKLM SOFTWARE\GPAC "Install_Dir" "$INSTDIR" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Osmo4" "DisplayName" "Osmo4/GPAC (remove only)" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Osmo4" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteUninstaller "uninstall.exe" + +SectionEnd + +SubSection "GPAC Plugins" + + +; +; 2 install modes, normal one and full one + +Section "MPEG-4 BIFS Decoder" + SectionIn 1 + File "..\gm_bifs_dec.dll" +SectionEnd + +Section "MPEG-4 ODF Decoder" + SectionIn 1 + File "..\gm_odf_dec.dll" +SectionEnd + +Section "MPEG-4 LASeR Decoder" + SectionIn 1 + File "..\gm_laser_dec.dll" +SectionEnd + +Section "MPEG-4 SAF Demultiplexer" + SectionIn 1 + File "..\gm_saf_in.dll" +SectionEnd + +Section "Generic Scene Description File Loader" + SectionIn 1 + File "..\gm_ctx_load.dll" +SectionEnd + +Section "Image Package (PNG, JPEG, BMP)" + SectionIn 1 + File "..\gm_img_in.dll" +SectionEnd + +Section "AAC Audio support (FAAD decoder, AAC files and Radios)" + SectionIn 1 + File "..\gm_aac_in.dll" +SectionEnd + +Section "MP3 Audio support (MAD decoder, MP3 files and Radios)" + SectionIn 1 + File "..\gm_mp3_in.dll" +SectionEnd + +Section "AC3 Audio support (A52 decoder, AC3 files and Radios)" + SectionIn 1 + File "..\gm_ac3_in.dll" +SectionEnd + +Section "FFMPEG Reader and Decoder" + SectionIn 1 + File "..\gm_ffmpeg_in.dll" + File "..\avcodec-51.dll" + File "..\avformat-51.dll" + File "..\avutil-49.dll" +SectionEnd + +Section "XviD Video Decoder" + SectionIn 1 + File "..\gm_xvid_dec.dll" +SectionEnd + +Section "3GPP AMR NB & WB Speech Decoder" + SectionIn 1 + File "..\gm_amr_float_dec.dll" +SectionEnd + +Section "Subtitle & TimedText Support" + SectionIn 1 + File "..\gm_timedtext.dll" +SectionEnd + +Section "MP4 and 3GPP File Reader" + SectionIn 1 + File "..\gm_isom_in.dll" +SectionEnd + +Section "MPEG-2 TS Reader" + SectionIn 1 + File "..\gm_mpegts_in.dll" +SectionEnd + +Section "Real-Time Streaming (RTP/RTSP/RTP) Support" + SectionIn 1 + File "..\gm_rtp_in.dll" +SectionEnd + +Section "Progressive SVG Support" + SectionIn 1 + File "..\gm_svg_in.dll" +SectionEnd + +Section "GDI+ Rasterizer" + SectionIn 1 + call InsertGDIPLUS +SectionEnd + +Section "GPAC 2D Rasterizer" + SectionIn 1 + File "..\gm_soft_raster.dll" +SectionEnd + +Section "FreeType Font Outliner" + SectionIn 1 + File "..\gm_ft_font.dll" +SectionEnd + +Section "Windows MME Audio Output" + SectionIn 1 + File "..\gm_wav_out.dll" +SectionEnd + +Section "Xiph Ogg Reader - Vorbis and Theora Decoders" + SectionIn 1 + File "..\gm_ogg_xiph.dll" +SectionEnd + + +SubSectionEnd + +SubSection "Osmo4 Shortcuts" + +Section "Add Start Menu Shortcuts" + SectionIn 1 + CreateDirectory "$SMPROGRAMS\Osmo4" + CreateShortCut "$SMPROGRAMS\Osmo4\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 + CreateShortCut "$SMPROGRAMS\Osmo4\Osmo4.lnk" "$INSTDIR\Osmo4.exe" "" "$INSTDIR\Osmo4.exe" 0 + CreateShortCut "$SMPROGRAMS\Osmo4\Readme.lnk" "$INSTDIR\ReadMe.txt" + CreateShortCut "$SMPROGRAMS\Osmo4\License.lnk" "$INSTDIR\License.txt" + CreateShortCut "$SMPROGRAMS\Osmo4\History.lnk" "$INSTDIR\changelog.txt" + CreateShortCut "$SMPROGRAMS\Osmo4\Configuration Info.lnk" "$INSTDIR\configuration.html" +SectionEnd + +Section "Add shortcut to QuickLaunch" + SectionIn 1 + CreateShortCut "$QUICKLAUNCH\Osmo4.lnk" "$INSTDIR\Osmo4.exe" "" "$INSTDIR\Osmo4.exe" 0 +SectionEnd + +Section "Add shortcut to Desktop" + SectionIn 1 + CreateShortCut "$DESKTOP\Osmo4.lnk" "$INSTDIR\Osmo4.exe" "" "$INSTDIR\Osmo4.exe" 0 +SectionEnd + +!define SHCNE_ASSOCCHANGED 0x08000000 +!define SHCNF_IDLIST 0 + +Section "Make Osmo4 the default MPEG-4 Player" + SectionIn 1 + ;write file association + WriteRegStr HKCR GPAC\mp4\DefaultIcon "" "$INSTDIR\Osmo4.ico, 0" + WriteRegStr HKCR GPAC\mp4\Shell\open\command "" '$INSTDIR\Osmo4.exe "%L" ' + WriteRegStr HKCR .mp4 "" "GPAC\mp4" + + !system 'shell32.dll::SHChangeNotify(i, i, i, i) v (${SHCNE_ASSOCCHANGED}, ${SHCNF_IDLIST}, 0, 0)' + +SectionEnd + +Section "Associate 3GPP files (3GP) with Osmo4" + SectionIn 1 + ;write file association + WriteRegStr HKCR GPAC\3gp\DefaultIcon "" "$INSTDIR\Osmo4.ico, 0" + WriteRegStr HKCR GPAC\3gp\Shell\open\command "" '$INSTDIR\Osmo4.exe "%L" ' + WriteRegStr HKCR .3gp "" "GPAC\3gp" + !system 'shell32.dll::SHChangeNotify(i, i, i, i) v (${SHCNE_ASSOCCHANGED}, ${SHCNF_IDLIST}, 0, 0)' +SectionEnd + +Section "Associate 3GPP2 files (3G2) with Osmo4" + SectionIn 1 + ;write file association + WriteRegStr HKCR GPAC\3g2\DefaultIcon "" "$INSTDIR\Osmo4.ico, 0" + WriteRegStr HKCR GPAC\3g2\Shell\open\command "" '$INSTDIR\Osmo4.exe "%L" ' + WriteRegStr HKCR .3g2 "" "GPAC\3g2" + !system 'shell32.dll::SHChangeNotify(i, i, i, i) v (${SHCNE_ASSOCCHANGED}, ${SHCNF_IDLIST}, 0, 0)' +SectionEnd + +SubSectionEnd + + +Section "MP4Box (Command-line MPEG-4 tool)" + SectionIn 1 + SetOutPath $INSTDIR + File "..\MP4Box.exe" + + Push $INSTDIR + Call AddToPath +SectionEnd + + +!define HK_MOZ "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" + +Section "Osmozilla (GPAC Plugin for Mozilla)" + SectionIn 1 + SetOutPath $INSTDIR\mozilla + File "..\nposmozilla.dll" + File "..\nposmozilla.xpt" + + WriteRegStr HKCR GPAC "InstallDir" "$INSTDIR" + WriteRegStr HKLM "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "Path" "$INSTDIR\mozilla\nposmozilla.dll" + WriteRegStr HKLM "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "XPTPath" "$INSTDIR\mozilla\nposmozilla.xpt" + WriteRegStr HKLM "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "Version" "${GPAC_VERSION}" + WriteRegStr HKLM "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "Vendor" "GPAC" + WriteRegStr HKLM "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "Description" "GPAC plugin" + WriteRegStr HKLM "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "ProductName" "Osmozilla" + + +SectionEnd + + +Section "GPAX (GPAC ActiveX Control)" + SectionIn 1 + SetOutPath $INSTDIR + File "..\GPAX.dll" + WriteRegStr HKCR GPAC "InstallDir" "$INSTDIR" + RegDLL "$INSTDIR\GPAX.dll" +SectionEnd + + +Section "MP4Client (GPAC Command-line client/grabber)" + SectionIn 1 + SetOutPath $INSTDIR + File "..\MP4Client.exe" +SectionEnd + + +; Function .onInstSuccess +; MessageBox MB_YESNO "GPAC Framework installation complete. Do you want to launch the Osmo4 player?" IDNO NoLaunch +; Exec $INSTDIR\Osmo4.exe +; NoLaunch: +; FunctionEnd + + + + + +; uninstall stuff + +UninstallText "This will uninstall OSMO4/GPAC from your computer. Hit next to continue." + +; special uninstall section. +Section "Uninstall" + ; remove registry keys + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Osmo4" + DeleteRegKey HKLM SOFTWARE\GPAC + DeleteRegKey HKCR mp4file\DefaultIcon + DeleteRegKey HKCR mp4file\shell + DeleteRegKey HKCR .mp4 + DeleteRegKey HKCR btfile\shell + DeleteRegKey HKCR .bt + + Delete $INSTDIR\cache\*.* + RMDir "$INSTDIR\cache" + Delete $INSTDIR\mozilla\*.* + RMDir "$INSTDIR\mozilla" + Delete $INSTDIR\*.* + RMDir "$INSTDIR" + Delete "$SMPROGRAMS\Osmo4\*.*" + RMDir "$SMPROGRAMS\Osmo4" + Delete "$QUICKLAUNCH\Osmo4.lnk" + Delete "$DESKTOP\Osmo4.lnk" + UnRegDLL "$INSTDIR\GPAX.dll" + Push $INSTDIR + Call un.RemoveFromPath + +SectionEnd + +;path modif functions +!verbose 3 +!include "WinMessages.NSH" +!verbose 4 + +; AddToPath - Adds the given dir to the search path. +; Input - head of the stack +; Note - Win9x systems requires reboot + +Function AddToPath + Exch $0 + Push $1 + Push $2 + Push $3 + + # don't add if the path doesn't exist + IfFileExists $0 "" AddToPath_done + + ReadEnvStr $1 PATH + Push "$1;" + Push "$0;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + Push "$1;" + Push "$0\;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + GetFullPathName /SHORT $3 $0 + Push "$1;" + Push "$3;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + Push "$1;" + Push "$3\;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + + Call IsNT + Pop $1 + StrCmp $1 1 AddToPath_NT + ; Not on NT + StrCpy $1 $WINDIR 2 + FileOpen $1 "$1\autoexec.bat" a + FileSeek $1 -1 END + FileReadByte $1 $2 + IntCmp $2 26 0 +2 +2 # DOS EOF + FileSeek $1 -1 END # write over EOF + FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" + FileClose $1 + SetRebootFlag true + Goto AddToPath_done + + AddToPath_NT: + ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" + StrCpy $2 $1 1 -1 # copy last char + StrCmp $2 ";" 0 +2 # if last char == ; + StrCpy $1 $1 -1 # remove last char + StrCmp $1 "" AddToPath_NTdoIt + StrCpy $0 "$1;$0" + AddToPath_NTdoIt: + WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $0 + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + AddToPath_done: + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + +; RemoveFromPath - Remove a given dir from the path +; Input: head of the stack + +Function un.RemoveFromPath + Exch $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + + IntFmt $6 "%c" 26 # DOS EOF + + Call un.IsNT + Pop $1 + StrCmp $1 1 unRemoveFromPath_NT + ; Not on NT + StrCpy $1 $WINDIR 2 + FileOpen $1 "$1\autoexec.bat" r + GetTempFileName $4 + FileOpen $2 $4 w + GetFullPathName /SHORT $0 $0 + StrCpy $0 "SET PATH=%PATH%;$0" + Goto unRemoveFromPath_dosLoop + + unRemoveFromPath_dosLoop: + FileRead $1 $3 + StrCpy $5 $3 1 -1 # read last char + StrCmp $5 $6 0 +2 # if DOS EOF + StrCpy $3 $3 -1 # remove DOS EOF so we can compare + StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "" unRemoveFromPath_dosLoopEnd + FileWrite $2 $3 + Goto unRemoveFromPath_dosLoop + unRemoveFromPath_dosLoopRemoveLine: + SetRebootFlag true + Goto unRemoveFromPath_dosLoop + + unRemoveFromPath_dosLoopEnd: + FileClose $2 + FileClose $1 + StrCpy $1 $WINDIR 2 + Delete "$1\autoexec.bat" + CopyFiles /SILENT $4 "$1\autoexec.bat" + Delete $4 + Goto unRemoveFromPath_done + + unRemoveFromPath_NT: + ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" + StrCpy $5 $1 1 -1 # copy last char + StrCmp $5 ";" +2 # if last char != ; + StrCpy $1 "$1;" # append ; + Push $1 + Push "$0;" + Call un.StrStr ; Find `$0;` in $1 + Pop $2 ; pos of our dir + StrCmp $2 "" unRemoveFromPath_done + ; else, it is in path + # $0 - path to add + # $1 - path var + StrLen $3 "$0;" + StrLen $4 $2 + StrCpy $5 $1 -$4 # $5 is now the part before the path to remove + StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove + StrCpy $3 $5$6 + + StrCpy $5 $3 1 -1 # copy last char + StrCmp $5 ";" 0 +2 # if last char == ; + StrCpy $3 $3 -1 # remove last char + + WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $3 + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + unRemoveFromPath_done: + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + +########################################### +# Utility Functions # +########################################### + +; IsNT +; no input +; output, top of the stack = 1 if NT or 0 if not +; +; Usage: +; Call IsNT +; Pop $R0 +; ($R0 at this point is 1 or 0) + +!macro IsNT un +Function ${un}IsNT + Push $0 + ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion + StrCmp $0 "" 0 IsNT_yes + ; we are not NT. + Pop $0 + Push 0 + Return + + IsNT_yes: + ; NT!!! + Pop $0 + Push 1 +FunctionEnd +!macroend +!insertmacro IsNT "" +!insertmacro IsNT "un." + +; StrStr +; input, top of stack = string to search for +; top of stack-1 = string to search in +; output, top of stack (replaces with the portion of the string remaining) +; modifies no other variables. +; +; Usage: +; Push "this is a long ass string" +; Push "ass" +; Call StrStr +; Pop $R0 +; ($R0 at this point is "ass string") + +!macro StrStr un +Function ${un}StrStr +Exch $R1 ; st=haystack,old$R1, $R1=needle + Exch ; st=old$R1,haystack + Exch $R2 ; st=old$R1,old$R2, $R2=haystack + Push $R3 + Push $R4 + Push $R5 + StrLen $R3 $R1 + StrCpy $R4 0 + ; $R1=needle + ; $R2=haystack + ; $R3=len(needle) + ; $R4=cnt + ; $R5=tmp + loop: + StrCpy $R5 $R2 $R3 $R4 + StrCmp $R5 $R1 done + StrCmp $R5 "" done + IntOp $R4 $R4 + 1 + Goto loop +done: + StrCpy $R1 $R2 "" $R4 + Pop $R5 + Pop $R4 + Pop $R3 + Pop $R2 + Exch $R1 +FunctionEnd +!macroend +!insertmacro StrStr "" +!insertmacro StrStr "un." + diff --git a/build/msevc3/GPAC.VCW b/build/msevc3/GPAC.VCW new file mode 100644 index 0000000..271722c --- /dev/null +++ b/build/msevc3/GPAC.VCW @@ -0,0 +1,401 @@ +Microsoft eMbedded Visual Tools Workspace File, Format Version 3.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Osmo4"=.\Osmo4.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "aac_in"=.\aac_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "amr_dec"=.\amr_dec.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "bifs_dec"=.\bifs_dec.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ctx_load"=.\ctx_load.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "dummy_in"=.\dummy_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ft_font"=.\ft_font.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "gapi"=.\gapi.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "img_in"=.\img_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "isom_in"=.\isom_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "laser_dec"=.\laser_dec.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "libgpac"=.\libgpac.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "libgpac_dll"=.\libgpac_dll.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac + End Project Dependency +}}} + +############################################################################### + +Project: "mp3_in"=.\mp3_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "odf_dec"=.\odf_dec.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ogg"=.\ogg.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "render2d"=.\render2d.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "render3d"=.\render3d.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "rtp_in"=.\rtp_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "saf_in"=.\saf_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "soft_raster"=.\soft_raster.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "svg_in"=.\svg_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "svg_loader"=.\svg_loader.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "timedtext"=.\timedtext.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "wav_out"=.\wav_out.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "xvid_dec"=.\xvid_dec.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/build/msevc3/Osmo4.vcp b/build/msevc3/Osmo4.vcp new file mode 100644 index 0000000..402ee94 --- /dev/null +++ b/build/msevc3/Osmo4.vcp @@ -0,0 +1,281 @@ +# Microsoft eMbedded Visual Tools Project File - Name="Osmo4" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Application" 0x8501 + +CFG=Osmo4 - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Osmo4.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Osmo4.vcn" CFG="Osmo4 - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Osmo4 - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Application") +!MESSAGE "Osmo4 - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Osmo4 - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 2 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/osmo4_rel" +# PROP Intermediate_Dir "obj/osmo4_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "_AFXDLL" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "_AFXDLL" /r +# ADD BASE CPP /nologo /W3 /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_AFXDLL" /Yu"stdafx.h" /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_AFXDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 gx.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/Osmo4.exe" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "Osmo4 - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 2 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/osmo4_deb" +# PROP Intermediate_Dir "obj/osmo4_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "_AFXDLL" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "_AFXDLL" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /Yu"stdafx.h" /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include" /D "DEBUG" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /FR /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 gx.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /out:"../../bin/arm_ppc02_deb/Osmo4.exe" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "Osmo4 - Win32 (WCE ARM) Release" +# Name "Osmo4 - Win32 (WCE ARM) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\MainFrm.cpp +DEP_CPP_MAINF=\ + "..\..\Applications\Osmo4_wce\MainFrm.h"\ + "..\..\Applications\Osmo4_wce\Osmo4.h"\ + "..\..\Applications\Osmo4_wce\ProgressBar.h"\ + "..\..\Applications\Osmo4_wce\StdAfx.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_MAINF=\ + ".\x.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\OpenDlg.cpp +DEP_CPP_OPEND=\ + "..\..\Applications\Osmo4_wce\OpenDlg.h"\ + "..\..\Applications\Osmo4_wce\Osmo4.h"\ + "..\..\Applications\Osmo4_wce\StdAfx.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\Options.cpp +DEP_CPP_OPTIO=\ + "..\..\Applications\Osmo4_wce\Options.h"\ + "..\..\Applications\Osmo4_wce\Osmo4.h"\ + "..\..\Applications\Osmo4_wce\StdAfx.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_OPTIO=\ + ".\x.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\Osmo4.cpp +DEP_CPP_OSMO4=\ + "..\..\Applications\Osmo4_wce\MainFrm.h"\ + "..\..\Applications\Osmo4_wce\OpenDlg.h"\ + "..\..\Applications\Osmo4_wce\Options.h"\ + "..\..\Applications\Osmo4_wce\Osmo4.h"\ + "..\..\Applications\Osmo4_wce\ProgressBar.h"\ + "..\..\Applications\Osmo4_wce\StdAfx.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_OSMO4=\ + ".\x.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\Osmo4.rc +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\ProgressBar.cpp +DEP_CPP_PROGR=\ + "..\..\Applications\Osmo4_wce\Osmo4.h"\ + "..\..\Applications\Osmo4_wce\ProgressBar.h"\ + "..\..\Applications\Osmo4_wce\StdAfx.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\StdAfx.cpp +DEP_CPP_STDAF=\ + "..\..\Applications\Osmo4_wce\StdAfx.h"\ + +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\Applications\Osmo4_wce\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=..\..\Applications\Osmo4_wce\newres.h +# End Source File +# Begin Source File + +SOURCE=..\..\Applications\Osmo4_wce\OpenDlg.h +# End Source File +# Begin Source File + +SOURCE=..\..\Applications\Osmo4_wce\Options.h +# End Source File +# Begin Source File + +SOURCE=..\..\Applications\Osmo4_wce\Osmo4.h +# End Source File +# Begin Source File + +SOURCE=..\..\Applications\Osmo4_wce\ProgressBar.h +# End Source File +# Begin Source File + +SOURCE=..\..\Applications\Osmo4_wce\Resource.h +# End Source File +# Begin Source File + +SOURCE=..\..\Applications\Osmo4_wce\StdAfx.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\res\Cmdbar.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\res\Osmo4.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/build/msevc3/aac_in.vcp b/build/msevc3/aac_in.vcp new file mode 100644 index 0000000..d83f089 --- /dev/null +++ b/build/msevc3/aac_in.vcp @@ -0,0 +1,140 @@ +# Microsoft eMbedded Visual Tools Project File - Name="aac_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=aac_in - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "aac_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "aac_in.vcn" CFG="aac_in - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "aac_in - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "aac_in - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "aac_in - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/aac_in_rel" +# PROP Intermediate_Dir "obj/aac_in_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "AAC_IN_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /I "../../extra_lib/include/faad" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "GPAC_HAS_FAAD" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 libfaad2.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_aac_in.dll" /libpath:"../../extra_lib/lib/arm_ppc02_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "aac_in - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/aac_in_deb" +# PROP Intermediate_Dir "obj/aac_in_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "AAC_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/faad" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GPAC_HAS_FAAD" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 libfaad2.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_aac_in.dll" /libpath:"../../extra_lib/lib/arm_ppc02_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "aac_in - Win32 (WCE ARM) Release" +# Name "aac_in - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\aac_in\aac_in.c +DEP_CPP_AAC_I=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\aac_in\aac_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\aac_in\faad_dec.c +DEP_CPP_FAAD_=\ + "..\..\extra_lib\include\faad\faad.h"\ + "..\..\extra_lib\include\faad\neaacdec.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc3/amr_dec.vcp b/build/msevc3/amr_dec.vcp new file mode 100644 index 0000000..022ff38 --- /dev/null +++ b/build/msevc3/amr_dec.vcp @@ -0,0 +1,2411 @@ +# Microsoft eMbedded Visual Tools Project File - Name="amr_dec" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=amr_dec - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "amr_dec.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "amr_dec.vcn" CFG="amr_dec - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "amr_dec - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "amr_dec - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "amr_dec - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/amr_dec_rel" +# PROP Intermediate_Dir "obj/amr_dec_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "AMR_DEC_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "__MSDOS__" /D "MMS_IO" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_amr_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "amr_dec - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/amr_dec_deb" +# PROP Intermediate_Dir "obj/amr_dec_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "AMR_DEC_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "__MSDOS__" /D "MMS_IO" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_amr_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "amr_dec - Win32 (WCE ARM) Release" +# Name "amr_dec - Win32 (WCE ARM) Debug" +# Begin Group "amr_nb" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\a_refl.c +DEP_CPP_A_REF=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\a_refl.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\a_refl.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\agc.c +DEP_CPP_AGC_C=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\agc.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\agc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\autocorr.c +DEP_CPP_AUTOC=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\autocorr.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\autocorr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\az_lsp.c +DEP_CPP_AZ_LS=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\az_lsp.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\grid.tab"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\az_lsp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\b_cn_cod.c +DEP_CPP_B_CN_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\b_cn_cod.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\window.tab"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\b_cn_cod.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\basic_op.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\basicop2.c +DEP_CPP_BASIC=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bgnscd.c +DEP_CPP_BGNSC=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bgnscd.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bgnscd.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bits2prm.c +DEP_CPP_BITS2=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\bits2prm.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bits2prm.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c1035pf.c +DEP_CPP_C1035=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c1035pf.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gray.tab"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\s10_8pf.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c1035pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_11pf.c +DEP_CPP_C2_11=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c2_11pf.h"\ + "..\..\modules\amr_dec\amr_nb\c2_11pf.tab"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_11pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_9pf.c +DEP_CPP_C2_9P=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c2_9pf.h"\ + "..\..\modules\amr_dec\amr_nb\c2_9pf.tab"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_9pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c3_14pf.c +DEP_CPP_C3_14=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c3_14pf.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c3_14pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c4_17pf.c +DEP_CPP_C4_17=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c4_17pf.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gray.tab"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c4_17pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c8_31pf.c +DEP_CPP_C8_31=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c8_31pf.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\s10_8pf.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c8_31pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c_g_aver.c +DEP_CPP_C_G_A=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c_g_aver.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c_g_aver.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_cor.c +DEP_CPP_CALC_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_cor.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_cor.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_en.c +DEP_CPP_CALC_E=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_en.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_en.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cbsearch.c +DEP_CPP_CBSEA=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c1035pf.h"\ + "..\..\modules\amr_dec\amr_nb\c2_11pf.h"\ + "..\..\modules\amr_dec\amr_nb\c2_9pf.h"\ + "..\..\modules\amr_dec\amr_nb\c3_14pf.h"\ + "..\..\modules\amr_dec\amr_nb\c4_17pf.h"\ + "..\..\modules\amr_dec\amr_nb\c8_31pf.h"\ + "..\..\modules\amr_dec\amr_nb\cbsearch.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cbsearch.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cl_ltp.c +DEP_CPP_CL_LT=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cl_ltp.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\convolve.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag3.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag6.h"\ + "..\..\modules\amr_dec\amr_nb\g_pitch.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_fr.h"\ + "..\..\modules\amr_dec\amr_nb\pred_lt.h"\ + "..\..\modules\amr_dec\amr_nb\q_gain_p.h"\ + "..\..\modules\amr_dec\amr_nb\ton_stab.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cl_ltp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cnst.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cnst_vad.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cod_amr.c +DEP_CPP_COD_A=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cbsearch.h"\ + "..\..\modules\amr_dec\amr_nb\cl_ltp.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\cod_amr.h"\ + "..\..\modules\amr_dec\amr_nb\convolve.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\g_adapt.h"\ + "..\..\modules\amr_dec\amr_nb\gain_q.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\levinson.h"\ + "..\..\modules\amr_dec\amr_nb\lpc.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ol_ltp.h"\ + "..\..\modules\amr_dec\amr_nb\p_ol_wgh.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_fr.h"\ + "..\..\modules\amr_dec\amr_nb\pre_big.h"\ + "..\..\modules\amr_dec\amr_nb\pred_lt.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\qua_gain.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\spreproc.h"\ + "..\..\modules\amr_dec\amr_nb\spstproc.h"\ + "..\..\modules\amr_dec\amr_nb\ton_stab.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cod_amr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\convolve.c +DEP_CPP_CONVO=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\convolve.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\convolve.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\copy.c +DEP_CPP_COPY_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\copy.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cor_h.c +DEP_CPP_COR_H=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cor_h.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\count.c +DEP_CPP_COUNT=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\count.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d1035pf.c +DEP_CPP_D1035=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d1035pf.h"\ + "..\..\modules\amr_dec\amr_nb\gray.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d1035pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_11pf.c +DEP_CPP_D2_11=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d2_11pf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_11pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_9pf.c +DEP_CPP_D2_9P=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c2_9pf.tab"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d2_9pf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_9pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d3_14pf.c +DEP_CPP_D3_14=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d3_14pf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d3_14pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d4_17pf.c +DEP_CPP_D4_17=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d4_17pf.h"\ + "..\..\modules\amr_dec\amr_nb\gray.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d4_17pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d8_31pf.c +DEP_CPP_D8_31=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d8_31pf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d8_31pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_c.c +DEP_CPP_D_GAI=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_gain_c.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_c.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_p.c +DEP_CPP_D_GAIN=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_gain_p.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_p.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_homing.c +DEP_CPP_D_HOM=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\bits2prm.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\d_homing.h"\ + "..\..\modules\amr_dec\amr_nb\d_homing.tab"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_homing.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf.c +DEP_CPP_D_PLS=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_5.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf_3.c +DEP_CPP_D_PLSF=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_3.tab"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf_5.c +DEP_CPP_D_PLSF_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_5.tab"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_amr.c +DEP_CPP_DEC_A=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\agc.h"\ + "..\..\modules\amr_dec\amr_nb\b_cn_cod.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bgnscd.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\c_g_aver.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d1035pf.h"\ + "..\..\modules\amr_dec\amr_nb\d2_11pf.h"\ + "..\..\modules\amr_dec\amr_nb\d2_9pf.h"\ + "..\..\modules\amr_dec\amr_nb\d3_14pf.h"\ + "..\..\modules\amr_dec\amr_nb\d4_17pf.h"\ + "..\..\modules\amr_dec\amr_nb\d8_31pf.h"\ + "..\..\modules\amr_dec\amr_nb\d_gain_c.h"\ + "..\..\modules\amr_dec\amr_nb\d_gain_p.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\dec_amr.h"\ + "..\..\modules\amr_dec\amr_nb\dec_gain.h"\ + "..\..\modules\amr_dec\amr_nb\dec_lag3.h"\ + "..\..\modules\amr_dec\amr_nb\dec_lag6.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_dec.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\ec_gains.h"\ + "..\..\modules\amr_dec\amr_nb\ex_ctrl.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\int_lpc.h"\ + "..\..\modules\amr_dec\amr_nb\int_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.tab"\ + "..\..\modules\amr_dec\amr_nb\lsp_avg.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ph_disp.h"\ + "..\..\modules\amr_dec\amr_nb\pred_lt.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_amr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_gain.c +DEP_CPP_DEC_G=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dec_gain.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\qgain475.tab"\ + "..\..\modules\amr_dec\amr_nb\qua_gain.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_gain.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag3.c +DEP_CPP_DEC_L=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dec_lag3.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag3.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag6.c +DEP_CPP_DEC_LA=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dec_lag6.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag6.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_dec.c +DEP_CPP_DTX_D=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\a_refl.h"\ + "..\..\modules\amr_dec\amr_nb\b_cn_cod.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c_g_aver.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_dec.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.tab"\ + "..\..\modules\amr_dec\amr_nb\lsp_az.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_5.tab"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_dec.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_enc.c +DEP_CPP_DTX_E=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.tab"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_enc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\e_homing.c +DEP_CPP_E_HOM=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\e_homing.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\e_homing.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ec_gains.c +DEP_CPP_EC_GA=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\ec_gains.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ec_gains.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\enc_lag3.c +DEP_CPP_ENC_L=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag3.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\enc_lag3.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\enc_lag6.c +DEP_CPP_ENC_LA=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag6.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\enc_lag6.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ex_ctrl.c +DEP_CPP_EX_CT=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\ex_ctrl.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ex_ctrl.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\frame.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_adapt.c +DEP_CPP_G_ADA=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\g_adapt.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_adapt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_code.c +DEP_CPP_G_COD=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\g_code.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_code.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_pitch.c +DEP_CPP_G_PIT=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\g_pitch.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_pitch.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gain_q.c +DEP_CPP_GAIN_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_en.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\g_adapt.h"\ + "..\..\modules\amr_dec\amr_nb\g_code.h"\ + "..\..\modules\amr_dec\amr_nb\gain_q.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_gain_c.h"\ + "..\..\modules\amr_dec\amr_nb\qgain475.h"\ + "..\..\modules\amr_dec\amr_nb\qgain795.h"\ + "..\..\modules\amr_dec\amr_nb\qua_gain.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gain_q.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gc_pred.c +DEP_CPP_GC_PR=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gc_pred.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gmed_n.c +DEP_CPP_GMED_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gmed_n.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\hp_max.c +DEP_CPP_HP_MA=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\hp_max.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\hp_max.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lpc.c +DEP_CPP_INT_L=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\int_lpc.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_az.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lpc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lsf.c +DEP_CPP_INT_LS=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\int_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inter_36.c +DEP_CPP_INTER=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inter_36.h"\ + "..\..\modules\amr_dec\amr_nb\inter_36.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inter_36.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inv_sqrt.c +DEP_CPP_INV_S=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inv_sqrt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lag_wind.c +DEP_CPP_LAG_W=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lag_wind.h"\ + "..\..\modules\amr_dec\amr_nb\lag_wind.tab"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lag_wind.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\levinson.c +DEP_CPP_LEVIN=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\levinson.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\levinson.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lflg_upd.c +DEP_CPP_LFLG_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\log2.c +DEP_CPP_LOG2_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\log2.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\log2.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lpc.c +DEP_CPP_LPC_C=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\autocorr.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lag_wind.h"\ + "..\..\modules\amr_dec\amr_nb\levinson.h"\ + "..\..\modules\amr_dec\amr_nb\lpc.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\window.tab"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lpc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsfwt.c +DEP_CPP_LSFWT=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsfwt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsfwt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp.c +DEP_CPP_LSP_C=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\az_lsp.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\int_lpc.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.tab"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_avg.c +DEP_CPP_LSP_A=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_avg.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_5.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_avg.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_az.c +DEP_CPP_LSP_AZ=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_az.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_az.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_lsf.c +DEP_CPP_LSP_L=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_lsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\mac_32.c +DEP_CPP_MAC_3=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mac_32.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\mac_32.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\mode.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\n_proc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\n_proc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ol_ltp.c +DEP_CPP_OL_LT=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ol_ltp.h"\ + "..\..\modules\amr_dec\amr_nb\p_ol_wgh.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_ol.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ol_ltp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\oper_32b.c +DEP_CPP_OPER_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\oper_32b.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\p_ol_wgh.c +DEP_CPP_P_OL_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_cor.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\corrwght.tab"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\hp_max.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\p_ol_wgh.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\p_ol_wgh.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ph_disp.c +DEP_CPP_PH_DI=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ph_disp.h"\ + "..\..\modules\amr_dec\amr_nb\ph_disp.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ph_disp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_fr.c +DEP_CPP_PITCH=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\convolve.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag3.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag6.h"\ + "..\..\modules\amr_dec\amr_nb\inter_36.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_fr.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_fr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_ol.c +DEP_CPP_PITCH_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_cor.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\hp_max.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_ol.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_ol.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\post_pro.c +DEP_CPP_POST_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\post_pro.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\post_pro.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pow2.c +DEP_CPP_POW2_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pow2.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_big.c +DEP_CPP_PRE_B=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pre_big.h"\ + "..\..\modules\amr_dec\amr_nb\residu.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\weight_a.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_big.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_proc.c +DEP_CPP_PRE_P=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pre_proc.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_proc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pred_lt.c +DEP_CPP_PRED_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\pred_lt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pred_lt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\preemph.c +DEP_CPP_PREEM=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\preemph.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\preemph.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\prm2bits.c +DEP_CPP_PRM2B=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\prm2bits.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\prm2bits.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pstfilt.c +DEP_CPP_PSTFI=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\agc.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\preemph.h"\ + "..\..\modules\amr_dec\amr_nb\pstfilt.h"\ + "..\..\modules\amr_dec\amr_nb\residu.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\weight_a.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pstfilt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_c.c +DEP_CPP_Q_GAI=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\q_gain_c.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_c.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_p.c +DEP_CPP_Q_GAIN=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\q_gain_p.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_p.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf.c +DEP_CPP_Q_PLS=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf_3.c +DEP_CPP_Q_PLSF=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsfwt.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_3.tab"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf_5.c +DEP_CPP_Q_PLSF_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsfwt.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_5.tab"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain475.c +DEP_CPP_QGAIN=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mac_32.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\qgain475.h"\ + "..\..\modules\amr_dec\amr_nb\qgain475.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain475.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain795.c +DEP_CPP_QGAIN7=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_en.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\g_adapt.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mac_32.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\q_gain_p.h"\ + "..\..\modules\amr_dec\amr_nb\qgain795.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain795.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qua_gain.c +DEP_CPP_QUA_G=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\qua_gain.h"\ + "..\..\modules\amr_dec\amr_nb\qua_gain.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qua_gain.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\r_fft.c +DEP_CPP_R_FFT=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\reorder.c +DEP_CPP_REORD=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\reorder.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\residu.c +DEP_CPP_RESID=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\residu.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\residu.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\s10_8pf.c +DEP_CPP_S10_8=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\s10_8pf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\s10_8pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_sign.c +DEP_CPP_SET_S=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_sign.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_zero.c +DEP_CPP_SET_Z=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_zero.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sid_sync.c +DEP_CPP_SID_S=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\sid_sync.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sid_sync.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_dec.c +DEP_CPP_SP_DE=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\agc.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bgnscd.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\bits2prm.h"\ + "..\..\modules\amr_dec\amr_nb\c_g_aver.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\dec_amr.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_dec.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\ec_gains.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_avg.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ph_disp.h"\ + "..\..\modules\amr_dec\amr_nb\post_pro.h"\ + "..\..\modules\amr_dec\amr_nb\preemph.h"\ + "..\..\modules\amr_dec\amr_nb\pstfilt.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\sp_dec.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_dec.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_enc.c +DEP_CPP_SP_EN=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\cl_ltp.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\cod_amr.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\g_adapt.h"\ + "..\..\modules\amr_dec\amr_nb\gain_q.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\levinson.h"\ + "..\..\modules\amr_dec\amr_nb\lpc.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\p_ol_wgh.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_fr.h"\ + "..\..\modules\amr_dec\amr_nb\pre_proc.h"\ + "..\..\modules\amr_dec\amr_nb\prm2bits.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\sp_enc.h"\ + "..\..\modules\amr_dec\amr_nb\ton_stab.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_enc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spreproc.c +DEP_CPP_SPREP=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\residu.h"\ + "..\..\modules\amr_dec\amr_nb\spreproc.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\weight_a.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spreproc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spstproc.c +DEP_CPP_SPSTP=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\spstproc.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spstproc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sqrt_l.c +DEP_CPP_SQRT_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sqrt_l.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\strfunc.c +DEP_CPP_STRFU=\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\strfunc.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\strfunc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\syn_filt.c +DEP_CPP_SYN_F=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\syn_filt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ton_stab.c +DEP_CPP_TON_S=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\ton_stab.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ton_stab.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\typedef.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\typedefs.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad1.c +DEP_CPP_VAD1_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad1.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad2.c +DEP_CPP_VAD2_=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad2.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vadname.c +DEP_CPP_VADNA=\ + "..\..\modules\amr_dec\amr_nb\vadname.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vadname.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\weight_a.c +DEP_CPP_WEIGH=\ + "..\..\include\gpac\setup.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\weight_a.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\weight_a.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_dec.c +DEP_CPP_AMR_D=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\amr_dec\amr_nb\agc.h"\ + "..\..\modules\amr_dec\amr_nb\bgnscd.h"\ + "..\..\modules\amr_dec\amr_nb\c_g_aver.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\d_homing.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\dec_amr.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_dec.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\ec_gains.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_avg.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ph_disp.h"\ + "..\..\modules\amr_dec\amr_nb\post_pro.h"\ + "..\..\modules\amr_dec\amr_nb\preemph.h"\ + "..\..\modules\amr_dec\amr_nb\pstfilt.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\sp_dec.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_dec.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_in.c +DEP_CPP_AMR_I=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc3/bifs_dec.vcp b/build/msevc3/bifs_dec.vcp new file mode 100644 index 0000000..6686062 --- /dev/null +++ b/build/msevc3/bifs_dec.vcp @@ -0,0 +1,133 @@ +# Microsoft eMbedded Visual Tools Project File - Name="bifs_dec" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=bifs_dec - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "bifs_dec.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "bifs_dec.vcn" CFG="bifs_dec - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "bifs_dec - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "bifs_dec - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "bifs_dec - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/bifs_dec_rel" +# PROP Intermediate_Dir "obj/bifs_dec_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "BIFS_DEC_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_bifs_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "bifs_dec - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/bifs_dec_deb" +# PROP Intermediate_Dir "obj/bifs_dec_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "BIFS_DEC_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_bifs_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "bifs_dec - Win32 (WCE ARM) Release" +# Name "bifs_dec - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\bifs_dec\bifs_dec.c +DEP_CPP_BIFS_=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\bifs_dec\bifs_dec.def +# End Source File +# End Target +# End Project diff --git a/build/msevc3/ctx_load.vcp b/build/msevc3/ctx_load.vcp new file mode 100644 index 0000000..4057d46 --- /dev/null +++ b/build/msevc3/ctx_load.vcp @@ -0,0 +1,135 @@ +# Microsoft eMbedded Visual Tools Project File - Name="ctx_load" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=ctx_load - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ctx_load.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ctx_load.vcn" CFG="ctx_load - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ctx_load - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "ctx_load - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ctx_load - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ctx_load_rel" +# PROP Intermediate_Dir "obj/ctx_load_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "CTX_LOAD_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_ctx_load.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "ctx_load - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ctx_load_deb" +# PROP Intermediate_Dir "obj/ctx_load_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "CTX_LOAD_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_ctx_load.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "ctx_load - Win32 (WCE ARM) Release" +# Name "ctx_load - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\ctx_load\ctx_load.c +DEP_CPP_CTX_L=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ctx_load\ctx_load.def +# End Source File +# End Target +# End Project diff --git a/build/msevc3/dummy_in.vcp b/build/msevc3/dummy_in.vcp new file mode 100644 index 0000000..11d09ba --- /dev/null +++ b/build/msevc3/dummy_in.vcp @@ -0,0 +1,122 @@ +# Microsoft eMbedded Visual Tools Project File - Name="dummy_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=dummy_in - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dummy_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dummy_in.vcn" CFG="dummy_in - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dummy_in - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "dummy_in - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dummy_in - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/dum_in_rel" +# PROP Intermediate_Dir "obj/dum_in_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "DUMMY_IN_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_dummy_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "dummy_in - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/dum_in_deb" +# PROP Intermediate_Dir "obj/dum_in_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "DUMMY_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_dummy_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "dummy_in - Win32 (WCE ARM) Release" +# Name "dummy_in - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\dummy_in\dummy_in.c +DEP_CPP_DUMMY=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dummy_in\dummy_in.def +# End Source File +# End Target +# End Project diff --git a/build/msevc3/ft_font.vcp b/build/msevc3/ft_font.vcp new file mode 100644 index 0000000..b248ec4 --- /dev/null +++ b/build/msevc3/ft_font.vcp @@ -0,0 +1,129 @@ +# Microsoft eMbedded Visual Tools Project File - Name="ft_font" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=ft_font - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ft_font.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ft_font.vcn" CFG="ft_font - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ft_font - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "ft_font - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ft_font - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/freetype_rel" +# PROP Intermediate_Dir "obj/freetype_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "FT_FONT_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /I "../../extra_lib/include/freetype" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 freetype.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_ft_font.dll" /libpath:"../../extra_lib/lib/arm_ppc02_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "ft_font - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/freetype_deb" +# PROP Intermediate_Dir "obj/freetype_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "FT_FONT_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/freetype" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 freetype.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_ft_font.dll" /libpath:"../../extra_lib/lib/arm_ppc02_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "ft_font - Win32 (WCE ARM) Release" +# Name "ft_font - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\ft_font\ft_font.c +DEP_CPP_FT_FO=\ + "..\..\extra_lib\include\freetype\freetype\config\ftheader.h"\ + "..\..\extra_lib\include\freetype\ft2build.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\modules\ft_font\ft_font.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ft_font\ft_font.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ft_font\ft_font.h +# End Source File +# End Target +# End Project diff --git a/build/msevc3/gapi.vcp b/build/msevc3/gapi.vcp new file mode 100644 index 0000000..0d95280 --- /dev/null +++ b/build/msevc3/gapi.vcp @@ -0,0 +1,134 @@ +# Microsoft eMbedded Visual Tools Project File - Name="gapi" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=gapi - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "gapi.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "gapi.vcn" CFG="gapi - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "gapi - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "gapi - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "gapi - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/gapi_rel" +# PROP Intermediate_Dir "obj/gapi_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /I "../../extra_lib/include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 gx.lib commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 gx.lib libGLES_CM.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc02_rel/gm_gapi.dll" /libpath:"../../extra_lib/lib/arm_ppc02_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "gapi - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/gapi_deb" +# PROP Intermediate_Dir "obj/gapi_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 gx.lib commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 gx.lib libGLES_CM.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_gapi.dll" /libpath:"../../extra_lib/lib/arm_ppc02_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "gapi - Win32 (WCE ARM) Release" +# Name "gapi - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\gapi\gapi.cpp +DEP_CPP_GAPI_=\ + "..\..\extra_lib\include\GLES\egl.h"\ + "..\..\extra_lib\include\GLES\gl.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\gapi\gapi.h"\ + {$(INCLUDE)}"aygshell.h"\ + {$(INCLUDE)}"gx.h"\ + {$(INCLUDE)}"sipapi.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\gapi\gapi.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\gapi\gapi.h +# End Source File +# End Target +# End Project diff --git a/build/msevc3/img_in.vcp b/build/msevc3/img_in.vcp new file mode 100644 index 0000000..cdaf67b --- /dev/null +++ b/build/msevc3/img_in.vcp @@ -0,0 +1,214 @@ +# Microsoft eMbedded Visual Tools Project File - Name="img_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=img_in - Win32 (WCE ARM) Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "img_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "img_in.vcn" CFG="img_in - Win32 (WCE ARM) Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "img_in - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "img_in - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "img_in - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/img_in_rel" +# PROP Intermediate_Dir "obj/img_in_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "IMG_IN_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /I "../../extra_lib/include/png" /I "../../extra_lib/include/zlib" /I "../../extra_lib/include/jpeg" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "GPAC_HAS_PNG" /D "GPAC_HAS_JPEG" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 libpng.lib zlib.lib libjpeg.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_img_in.dll" /libpath:"../../extra_lib/lib/arm_ppc02_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "img_in - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/img_in_deb" +# PROP Intermediate_Dir "obj/img_in_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "IMG_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/png" /I "../../extra_lib/include/zlib" /I "../../extra_lib/include/jpeg" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GPAC_HAS_PNG" /D "GPAC_HAS_JPEG" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 libpng.lib zlib.lib libjpeg.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_img_in.dll" /libpath:"../../extra_lib/lib/arm_ppc02_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "img_in - Win32 (WCE ARM) Release" +# Name "img_in - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\img_in\bmp_dec.c +DEP_CPP_BMP_D=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_dec.c +DEP_CPP_IMG_D=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_in.c +DEP_CPP_IMG_I=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\jpeg_dec.c +DEP_CPP_JPEG_=\ + "..\..\extra_lib\include\jpeg\jconfig.h"\ + "..\..\extra_lib\include\jpeg\jmorecfg.h"\ + "..\..\extra_lib\include\jpeg\jpeglib.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +NODEP_CPP_JPEG_=\ + "..\..\extra_lib\include\jpeg\jerror.h"\ + "..\..\extra_lib\include\jpeg\jpegint.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\png_dec.c +DEP_CPP_PNG_D=\ + "..\..\extra_lib\include\png\png.h"\ + "..\..\extra_lib\include\png\pngconf.h"\ + "..\..\extra_lib\include\zlib\zconf.h"\ + "..\..\extra_lib\include\zlib\zlib.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +NODEP_CPP_PNG_D=\ + "..\..\extra_lib\include\png\alloc.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc3/isom_in.vcp b/build/msevc3/isom_in.vcp new file mode 100644 index 0000000..43972db --- /dev/null +++ b/build/msevc3/isom_in.vcp @@ -0,0 +1,190 @@ +# Microsoft eMbedded Visual Tools Project File - Name="isom_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=isom_in - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "isom_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "isom_in.vcn" CFG="isom_in - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "isom_in - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "isom_in - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "isom_in - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/isom_in_rel" +# PROP Intermediate_Dir "obj/isom_in_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "isom_in_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_isom_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "isom_in - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/isom_in_deb" +# PROP Intermediate_Dir "obj/isom_in_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "isom_in_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_isom_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "isom_in - Win32 (WCE ARM) Release" +# Name "isom_in - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\isom_in\cache.c +DEP_CPP_CACHE=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\isom_in\isom_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\isom_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\isom_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\load.c +DEP_CPP_LOAD_=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\isom_in\isom_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\read.c +DEP_CPP_READ_=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\isom_in\isom_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\read_ch.c +DEP_CPP_READ_C=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\isom_in\isom_in.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc3/laser_dec.vcp b/build/msevc3/laser_dec.vcp new file mode 100644 index 0000000..0bc122b --- /dev/null +++ b/build/msevc3/laser_dec.vcp @@ -0,0 +1,147 @@ +# Microsoft eMbedded Visual Tools Project File - Name="laser_dec" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=laser_dec - Win32 (WCE ARM) Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "laser_dec.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "laser_dec.vcn" CFG="laser_dec - Win32 (WCE ARM) Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "laser_dec - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "laser_dec - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "laser_dec - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/laser_dec_rel" +# PROP Intermediate_Dir "obj/laser_dec_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /GX- /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "LASER_DEC_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /GX- /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /YX /Oxs /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_laser_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "laser_dec - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/laser_dec_deb" +# PROP Intermediate_Dir "obj/laser_dec_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /GX- /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "LASER_DEC_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /GX- /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_laser_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "laser_dec - Win32 (WCE ARM) Release" +# Name "laser_dec - Win32 (WCE ARM) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\laser_dec\laser_dec.c +DEP_CPP_LASER=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\laser_dec\laser_dec.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msevc3/libgpac.vcp b/build/msevc3/libgpac.vcp new file mode 100644 index 0000000..a5f7f90 --- /dev/null +++ b/build/msevc3/libgpac.vcp @@ -0,0 +1,3862 @@ +# Microsoft eMbedded Visual Tools Project File - Name="libgpac" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Static Library" 0x8504 + +CFG=libgpac - Win32 (WCE ARM) Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libgpac.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libgpac.vcn" CFG="libgpac - Win32 (WCE ARM) Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libgpac - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Static Library") +!MESSAGE "libgpac - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe + +!IF "$(CFG)" == "libgpac - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/libgpac_rel" +# PROP Intermediate_Dir "obj/libgpac_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "NDEBUG" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /I "../../extra_lib/include/zlib" /I "../../extra_lib/include/js" /D "NDEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /D "GPAC_HAS_SPIDERMONKEY" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX /Yc /Yu +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ELSEIF "$(CFG)" == "libgpac - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/libgpac_deb" +# PROP Intermediate_Dir "obj/libgpac_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/zlib" /I "../../extra_lib/include/js" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /D "GPAC_HAS_SPIDERMONKEY" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX /Yc /Yu +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ENDIF + +# Begin Target + +# Name "libgpac - Win32 (WCE ARM) Release" +# Name "libgpac - Win32 (WCE ARM) Debug" +# Begin Group "utils" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\utils\base_encoding.c +DEP_CPP_BASE_=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\bitstream.c +DEP_CPP_BITST=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\color.c +DEP_CPP_COLOR=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\configfile.c +DEP_CPP_CONFI=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\downloader.c +DEP_CPP_DOWNL=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\error.c +DEP_CPP_ERROR=\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\list.c +DEP_CPP_LIST_=\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\math.c +DEP_CPP_MATH_=\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\setup.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\module.c +DEP_CPP_MODUL=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\utils\module_wrap.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\module_wrap.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_divers.c +DEP_CPP_OS_DI=\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_module.c +DEP_CPP_OS_MO=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\utils\module_wrap.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_net.c +DEP_CPP_OS_NE=\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_thread.c +DEP_CPP_OS_TH=\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\path2d.c +DEP_CPP_PATH2=\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\path2d_stroker.c +DEP_CPP_PATH2D=\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\token.c +DEP_CPP_TOKEN=\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\url.c +DEP_CPP_URL_C=\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\utf.c +DEP_CPP_UTF_C=\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\xml_parser.c +DEP_CPP_XML_P=\ + "..\..\extra_lib\include\zlib\zconf.h"\ + "..\..\extra_lib\include\zlib\zlib.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +# End Source File +# End Group +# Begin Group "odf" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\odf\desc_private.c +DEP_CPP_DESC_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\descriptors.c +DEP_CPP_DESCR=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\ipmpx_code.c +DEP_CPP_IPMPX=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\ipmpx_dump.c +DEP_CPP_IPMPX_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\ipmpx_parse.c +DEP_CPP_IPMPX_P=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\oci_codec.c +DEP_CPP_OCI_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_code.c +DEP_CPP_ODF_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_codec.c +DEP_CPP_ODF_CO=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_command.c +DEP_CPP_ODF_COM=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\odf_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_dump.c +DEP_CPP_ODF_D=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_parse.c +DEP_CPP_ODF_P=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\qos.c +DEP_CPP_QOS_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\slc.c +DEP_CPP_SLC_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# End Group +# Begin Group "bifs" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\bifs\arith_decoder.c +DEP_CPP_ARITH=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\bifs_codec.c +DEP_CPP_BIFS_=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\bifs_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\bifs_node_tables.c +DEP_CPP_BIFS_N=\ + "..\..\include\gpac\internal\bifs_tables.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\bifs_tables.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\com_dec.c +DEP_CPP_COM_D=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_COM_D=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\com_enc.c +DEP_CPP_COM_E=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_COM_E=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\conditional.c +DEP_CPP_CONDI=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_CONDI=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\field_decode.c +DEP_CPP_FIELD=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + "..\..\src\bifs\script.h"\ + +NODEP_CPP_FIELD=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\field_encode.c +DEP_CPP_FIELD_=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\bifs_tables.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + "..\..\src\bifs\script.h"\ + +NODEP_CPP_FIELD_=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\memory_decoder.c +DEP_CPP_MEMOR=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_MEMOR=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\predictive_mffield.c +DEP_CPP_PREDI=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\quant.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\quantize.c +DEP_CPP_QUANT=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\script.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\script_dec.c +DEP_CPP_SCRIP=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\script.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\script_enc.c +DEP_CPP_SCRIPT=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\script.h"\ + +NODEP_CPP_SCRIPT=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\unquantize.c +DEP_CPP_UNQUA=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +# End Source File +# End Group +# Begin Group "isomedia" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\isomedia\avc_ext.c +DEP_CPP_AVC_E=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_3gpp.c +DEP_CPP_BOX_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_apple.c +DEP_CPP_BOX_CO=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_base.c +DEP_CPP_BOX_COD=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_isma.c +DEP_CPP_BOX_CODE=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_meta.c +DEP_CPP_BOX_CODE_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_dump.c +DEP_CPP_BOX_D=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_funcs.c +DEP_CPP_BOX_F=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\data_map.c +DEP_CPP_DATA_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\hint_track.c +DEP_CPP_HINT_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\hinting.c +DEP_CPP_HINTI=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isma_sample.c +DEP_CPP_ISMA_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_intern.c +DEP_CPP_ISOM_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_read.c +DEP_CPP_ISOM_R=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_store.c +DEP_CPP_ISOM_S=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_write.c +DEP_CPP_ISOM_W=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\isomedia_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\media.c +DEP_CPP_MEDIA=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\media_odf.c +DEP_CPP_MEDIA_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\meta.c +DEP_CPP_META_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\movie_fragments.c +DEP_CPP_MOVIE=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\sample_descs.c +DEP_CPP_SAMPL=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\stbl_read.c +DEP_CPP_STBL_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\stbl_write.c +DEP_CPP_STBL_W=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\track.c +DEP_CPP_TRACK=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\tx3g.c +DEP_CPP_TX3G_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# End Group +# Begin Group "ietf" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\include\gpac\internal\ietf_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtcp.c +DEP_CPP_RTCP_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp.c +DEP_CPP_RTP_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_packetizer.c +DEP_CPP_RTP_P=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_pck_3gpp.c +DEP_CPP_RTP_PC=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_pck_mpeg12.c +DEP_CPP_RTP_PCK=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_pck_mpeg4.c +DEP_CPP_RTP_PCK_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_command.c +DEP_CPP_RTSP_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_common.c +DEP_CPP_RTSP_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_response.c +DEP_CPP_RTSP_R=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_session.c +DEP_CPP_RTSP_S=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\sdp.c +DEP_CPP_SDP_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# End Group +# Begin Group "scenegraph" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\scenegraph\base_scenegraph.c +DEP_CPP_BASE_S=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BASE_S=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\commands.c +DEP_CPP_COMMA=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_COMMA=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\dom_events.c +DEP_CPP_DOM_E=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_DOM_E=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\mpeg4_animators.c +DEP_CPP_MPEG4=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MPEG4=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\mpeg4_nodes.c +DEP_CPP_MPEG4_=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MPEG4_=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\mpeg4_valuator.c +DEP_CPP_MPEG4_V=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MPEG4_V=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\scenegraph_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\smil_anim.c +DEP_CPP_SMIL_=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SMIL_=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\smil_timing.c +DEP_CPP_SMIL_T=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SMIL_T=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_attributes.c +DEP_CPP_SVG_A=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SVG_A=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_nodes.c +DEP_CPP_SVG_N=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SVG_N=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_properties.c +DEP_CPP_SVG_P=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SVG_P=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_smjs.c +DEP_CPP_SVG_S=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SVG_S=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_tools.c +DEP_CPP_SVG_T=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SVG_T=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_types.c +DEP_CPP_SVG_TY=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SVG_TY=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_interpolators.c +DEP_CPP_VRML_=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_proto.c +DEP_CPP_VRML_P=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_P=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_route.c +DEP_CPP_VRML_R=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_R=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_script.c +DEP_CPP_VRML_S=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_S=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_smjs.c +DEP_CPP_VRML_SM=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_SM=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_tools.c +DEP_CPP_VRML_T=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_T=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\x3d_nodes.c +DEP_CPP_X3D_N=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_X3D_N=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# End Group +# Begin Group "media_tools" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\media_tools\av_parsers.c +DEP_CPP_AV_PA=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\internal\ogg.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\avilib.c +DEP_CPP_AVILI=\ + "..\..\include\gpac\internal\avilib.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\avilib.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\gpac_ogg.c +DEP_CPP_GPAC_=\ + "..\..\include\gpac\internal\ogg.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\ismacryp.c +DEP_CPP_ISMAC=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\ismacryp.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\xml.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\ismacryp.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\isom_hinter.c +DEP_CPP_ISOM_H=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\isom_tools.c +DEP_CPP_ISOM_T=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\media_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\media_export.c +DEP_CPP_MEDIA_E=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\avilib.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\internal\ogg.h"\ + "..\..\include\gpac\internal\vobsub.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\mpegts.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\media_import.c +DEP_CPP_MEDIA_I=\ + "..\..\extra_lib\include\zlib\zconf.h"\ + "..\..\extra_lib\include\zlib\zlib.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\avilib.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\internal\ogg.h"\ + "..\..\include\gpac\internal\vobsub.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\mpegts.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\xml.h"\ + "..\..\src\media_tools\mpeg2_ps.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\mpeg2_ps.c +DEP_CPP_MPEG2=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\media_tools\mpeg2_ps.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\mpeg2_ps.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\ogg.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\text_import.c +DEP_CPP_TEXT_=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +# End Source File +# End Group +# Begin Group "scene_manager" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\scene_manager\encode_cbk.c +DEP_CPP_ENCOD=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bifsengine.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\encode_isom.c +DEP_CPP_ENCODE=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ENCODE=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_bt.c +DEP_CPP_LOADE=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\extra_lib\include\zlib\zconf.h"\ + "..\..\extra_lib\include\zlib\zlib.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + +NODEP_CPP_LOADE=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_isom.c +DEP_CPP_LOADER=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_qt.c +DEP_CPP_LOADER_=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_svg.c +DEP_CPP_LOADER_S=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_LOADER_S=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_xmt.c +DEP_CPP_LOADER_X=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_LOADER_X=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\scene_dump.c +DEP_CPP_SCENE=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_SCENE=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\scene_manager.c +DEP_CPP_SCENE_=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_SCENE_=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\scene_stats.c +DEP_CPP_SCENE_S=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SCENE_S=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\swf_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\swf_parse.c +DEP_CPP_SWF_P=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\swf_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\swf_shape.c +DEP_CPP_SWF_S=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\swf_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\text_to_bifs.c +DEP_CPP_TEXT_T=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +# End Source File +# End Group +# Begin Group "mcrypt" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\mcrypt\cbc.c +DEP_CPP_CBC_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\cfb.c +DEP_CPP_CFB_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\crypt_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ctr.c +DEP_CPP_CTR_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\des.c +DEP_CPP_DES_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ecb.c +DEP_CPP_ECB_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\g_crypt.c +DEP_CPP_G_CRY=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ncfb.c +DEP_CPP_NCFB_=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\nofb.c +DEP_CPP_NOFB_=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ofb.c +DEP_CPP_OFB_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE="..\..\src\mcrypt\rijndael-128.c" +DEP_CPP_RIJND=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE="..\..\src\mcrypt\rijndael-192.c" +DEP_CPP_RIJNDA=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE="..\..\src\mcrypt\rijndael-256.c" +DEP_CPP_RIJNDAE=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\stream.c +DEP_CPP_STREA=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\tripledes.c +DEP_CPP_TRIPL=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# End Group +# Begin Group "terminal" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\terminal\channel.c +DEP_CPP_CHANN=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\ismacryp.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_memory.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\clock.c +DEP_CPP_CLOCK=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\decoder.c +DEP_CPP_DECOD=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\renderer_dev.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\input_sensor.h"\ + "..\..\src\terminal\media_control.h"\ + "..\..\src\terminal\media_memory.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\inline.c +DEP_CPP_INLIN=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\input_sensor.c +DEP_CPP_INPUT=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\src\terminal\input_sensor.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\input_sensor.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_control.c +DEP_CPP_MEDIA_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_control.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_manager.c +DEP_CPP_MEDIA_M=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_memory.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_memory.c +DEP_CPP_MEDIA_ME=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + "..\..\src\terminal\media_memory.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_memory.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_object.c +DEP_CPP_MEDIA_O=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + "..\..\src\terminal\media_memory.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_sensor.c +DEP_CPP_MEDIA_S=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\network_service.c +DEP_CPP_NETWO=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\object_browser.c +DEP_CPP_OBJEC=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\term_info.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_memory.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\object_manager.c +DEP_CPP_OBJECT=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\input_sensor.h"\ + "..\..\src\terminal\media_control.h"\ + "..\..\src\terminal\media_memory.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\svg_external.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\term_node_init.c +DEP_CPP_TERM_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\input_sensor.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\terminal.c +DEP_CPP_TERMI=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\renderer_dev.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\terminal_dev.h +# End Source File +# End Group +# Begin Group "renderer" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\renderer\audio_input.c +DEP_CPP_AUDIO=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\renderer_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\renderer\audio_mixer.c +DEP_CPP_AUDIO_=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\renderer_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\renderer\audio_render.c +DEP_CPP_AUDIO_R=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\renderer_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\renderer\audio_stacks.c +DEP_CPP_AUDIO_S=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\renderer_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\renderer\common_stacks.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\renderer\base_textures.c +DEP_CPP_BASE_T=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\renderer_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\renderer\common_stacks.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\renderer\common_stacks.c +DEP_CPP_COMMO=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\renderer_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\renderer\common_stacks.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\renderer\common_stacks.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\renderer\renderer.c +DEP_CPP_RENDE=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\renderer_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\renderer_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\renderer\texturing.c +DEP_CPP_TEXTU=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\renderer_dev.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\renderer.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# End Group +# Begin Group "laser" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\include\gpac\internal\laser_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\laser\lsr_dec.c +DEP_CPP_LSR_D=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\laser_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_LSR_D=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\laser\lsr_enc.c +DEP_CPP_LSR_E=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\laser_dev.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\laser\lsr_tables.c +DEP_CPP_LSR_T=\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# End Group +# Begin Group "include" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\include\gpac\modules\audio_out.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\avparse.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\base_coding.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\bifs.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\bifsengine.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\bitstream.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\codec.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\config.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\constants.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\crypt.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\download.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\font.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\ietf.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\isomedia.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\list.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\math.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\media_tools.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\mediaobject.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\module.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\mpeg4_odf.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\network.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\nodes_mpeg4.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\nodes_svg.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\nodes_x3d.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\options.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\path2d.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\raster2d.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\renderer.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scene_manager.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scenegraph.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scenegraph_svg.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scenegraph_vrml.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\service.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\setup.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\terminal.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\thread.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\token.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\tools.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\user.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\utf.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\video_out.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\xml.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\yuv.h +# End Source File +# End Group +# End Target +# End Project diff --git a/build/msevc3/libgpac_dll.vcp b/build/msevc3/libgpac_dll.vcp new file mode 100644 index 0000000..8478959 --- /dev/null +++ b/build/msevc3/libgpac_dll.vcp @@ -0,0 +1,104 @@ +# Microsoft eMbedded Visual Tools Project File - Name="libgpac_dll" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=libgpac_dll - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libgpac_dll.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libgpac_dll.vcn" CFG="libgpac_dll - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libgpac_dll - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "libgpac_dll - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libgpac_dll - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/libgpac_dll_deb" +# PROP Intermediate_Dir "obj/libgpac_dll_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "LIBGPAC_DLL_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "LIBGPAC_DLL_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 zlib.lib js.lib winsock.lib toolhelp.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc02_deb/libgpac.dll" /libpath:"../../extra_lib/lib/arm_ppc02_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "libgpac_dll - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/libgpac_dll_rel" +# PROP Intermediate_Dir "obj/libgpac_dll_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /YX /Oxs /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 zlib.lib js.lib winsock.lib toolhelp.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc02_rel/libgpac.dll" /libpath:"../../extra_lib/lib/arm_ppc02_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "libgpac_dll - Win32 (WCE ARM) Debug" +# Name "libgpac_dll - Win32 (WCE ARM) Release" +# Begin Source File + +SOURCE=..\..\src\libgpac_ce.def +# End Source File +# End Target +# End Project diff --git a/build/msevc3/mp3_in.vcp b/build/msevc3/mp3_in.vcp new file mode 100644 index 0000000..0b11f2f --- /dev/null +++ b/build/msevc3/mp3_in.vcp @@ -0,0 +1,137 @@ +# Microsoft eMbedded Visual Tools Project File - Name="mp3_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=mp3_in - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mp3_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mp3_in.vcn" CFG="mp3_in - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mp3_in - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "mp3_in - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mp3_in - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/mp3_in_rel" +# PROP Intermediate_Dir "obj/mp3_in_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "MP3_IN_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /I "../../extra_lib/include/mad" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "GPAC_HAS_MAD" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 libmad.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_mp3_in.dll" /libpath:"../../extra_lib/lib/arm_ppc02_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "mp3_in - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/mp3_in_deb" +# PROP Intermediate_Dir "obj/mp3_in_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "MP3_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/mad" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GPAC_HAS_MAD" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 libmad.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_mp3_in.dll" /libpath:"../../extra_lib/lib/arm_ppc02_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "mp3_in - Win32 (WCE ARM) Release" +# Name "mp3_in - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\mp3_in\mad_dec.c +DEP_CPP_MAD_D=\ + "..\..\extra_lib\include\mad\mad.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\mp3_in\mp3_in.c +DEP_CPP_MP3_I=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\mp3_in\mp3_in.def +# End Source File +# End Target +# End Project diff --git a/build/msevc3/odf_dec.vcp b/build/msevc3/odf_dec.vcp new file mode 100644 index 0000000..8e3b2a6 --- /dev/null +++ b/build/msevc3/odf_dec.vcp @@ -0,0 +1,132 @@ +# Microsoft eMbedded Visual Tools Project File - Name="odf_dec" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=odf_dec - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "odf_dec.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "odf_dec.vcn" CFG="odf_dec - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "odf_dec - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "odf_dec - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "odf_dec - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/odf_dec_rel" +# PROP Intermediate_Dir "obj/odf_dec_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "odf_dec_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_odf_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "odf_dec - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/odf_dec_deb" +# PROP Intermediate_Dir "obj/odf_dec_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "odf_dec_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_odf_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "odf_dec - Win32 (WCE ARM) Release" +# Name "odf_dec - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\odf_dec\odf_dec.c +DEP_CPP_ODF_D=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\odf_dec\odf_dec.def +# End Source File +# End Target +# End Project diff --git a/build/msevc3/ogg.vcp b/build/msevc3/ogg.vcp new file mode 100644 index 0000000..1164623 --- /dev/null +++ b/build/msevc3/ogg.vcp @@ -0,0 +1,198 @@ +# Microsoft eMbedded Visual Tools Project File - Name="ogg" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=ogg - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ogg.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ogg.vcn" CFG="ogg - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ogg - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "ogg - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ogg - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ogg_in_rel" +# PROP Intermediate_Dir "obj/ogg_in_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "OGG_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /I "../../extra_lib/include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "GPAC_HAS_VORBIS" /D "GPAC_HAS_THEORA" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 ogg_static.lib vorbis_static.lib theora_static.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_ogg_xiph.dll" /libpath:"../../extra_lib/lib/arm_ppc02_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "ogg - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ogg_in_deb" +# PROP Intermediate_Dir "obj/ogg_in_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "OGG_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GPAC_HAS_VORBIS" /D "GPAC_HAS_THEORA" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 ogg_static.lib vorbis_static.lib theora_static.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_ogg_xiph.dll" /libpath:"../../extra_lib/lib/arm_ppc02_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "ogg - Win32 (WCE ARM) Release" +# Name "ogg - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\ogg\ogg.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ogg\ogg_in.c +DEP_CPP_OGG_I=\ + "..\..\extra_lib\include\ogg\ogg.h"\ + "..\..\extra_lib\include\ogg\os_types.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\ogg\ogg_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ogg\ogg_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ogg\ogg_load.c +DEP_CPP_OGG_L=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\ogg\ogg_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ogg\theora_dec.c +DEP_CPP_THEOR=\ + "..\..\extra_lib\include\ogg\ogg.h"\ + "..\..\extra_lib\include\ogg\os_types.h"\ + "..\..\extra_lib\include\theora\theora.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\ogg\ogg_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ogg\vorbis_dec.c +DEP_CPP_VORBI=\ + "..\..\extra_lib\include\ogg\ogg.h"\ + "..\..\extra_lib\include\ogg\os_types.h"\ + "..\..\extra_lib\include\vorbis\codec.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\ogg\ogg_in.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc3/rtp_in.vcp b/build/msevc3/rtp_in.vcp new file mode 100644 index 0000000..a6af2c9 --- /dev/null +++ b/build/msevc3/rtp_in.vcp @@ -0,0 +1,258 @@ +# Microsoft eMbedded Visual Tools Project File - Name="rtp_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=rtp_in - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rtp_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "rtp_in.vcn" CFG="rtp_in - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rtp_in - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "rtp_in - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rtp_in - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/rtp_in_rel" +# PROP Intermediate_Dir "obj/rtp_in_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "RTP_IN_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_rtp_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "rtp_in - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/rtp_in_deb" +# PROP Intermediate_Dir "obj/rtp_in_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "RTP_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_rtp_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "rtp_in - Win32 (WCE ARM) Release" +# Name "rtp_in - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_in.c +DEP_CPP_RTP_I=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_payloads.c +DEP_CPP_RTP_P=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_session.c +DEP_CPP_RTP_S=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_signaling.c +DEP_CPP_RTP_SI=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_stream.c +DEP_CPP_RTP_ST=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\sdp_fetch.c +DEP_CPP_SDP_F=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\sdp_load.c +DEP_CPP_SDP_L=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc3/saf_in.vcp b/build/msevc3/saf_in.vcp new file mode 100644 index 0000000..d59edbb --- /dev/null +++ b/build/msevc3/saf_in.vcp @@ -0,0 +1,135 @@ +# Microsoft eMbedded Visual Tools Project File - Name="saf_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=saf_in - Win32 (WCE ARM) Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "saf_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "saf_in.vcn" CFG="saf_in - Win32 (WCE ARM) Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "saf_in - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "saf_in - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "saf_in - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/saf_in_rel" +# PROP Intermediate_Dir "obj/saf_in_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /GX- /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "SAF_IN_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /GX- /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "SAF_IN_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_saf_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "saf_in - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/saf_in_deb" +# PROP Intermediate_Dir "obj/saf_in_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /GX- /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "SAF_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /GX- /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "SAF_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_saf_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "saf_in - Win32 (WCE ARM) Release" +# Name "saf_in - Win32 (WCE ARM) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\saf_in\saf_in.c +DEP_CPP_SAF_I=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\saf_in\saf_in.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msevc3/soft_raster.vcp b/build/msevc3/soft_raster.vcp new file mode 100644 index 0000000..c31c47b --- /dev/null +++ b/build/msevc3/soft_raster.vcp @@ -0,0 +1,218 @@ +# Microsoft eMbedded Visual Tools Project File - Name="soft_raster" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=soft_raster - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "soft_raster.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "soft_raster.vcn" CFG="soft_raster - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "soft_raster - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "soft_raster - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "soft_raster - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/soft_rast_rel" +# PROP Intermediate_Dir "obj/soft_rast_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "soft_raster_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_soft_raster.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "soft_raster - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/soft_rast_deb" +# PROP Intermediate_Dir "obj/soft_rast_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "soft_raster_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_soft_raster.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "soft_raster - Win32 (WCE ARM) Release" +# Name "soft_raster - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\soft_raster\ftgrays.c +DEP_CPP_FTGRA=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\rast_soft.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\rast_soft.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_565.c +DEP_CPP_RASTE=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_argb.c +DEP_CPP_RASTER=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_load.c +DEP_CPP_RASTER_=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_rgb.c +DEP_CPP_RASTER_R=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\stencil.c +DEP_CPP_STENC=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +NODEP_CPP_STENC=\ + "..\..\include\gpac\yuv.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\surface.c +DEP_CPP_SURFA=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc3/svg_in.vcp b/build/msevc3/svg_in.vcp new file mode 100644 index 0000000..4dda385 --- /dev/null +++ b/build/msevc3/svg_in.vcp @@ -0,0 +1,145 @@ +# Microsoft eMbedded Visual Tools Project File - Name="svg_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=svg_in - Win32 (WCE ARM) Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "svg_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "svg_in.vcn" CFG="svg_in - Win32 (WCE ARM) Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "svg_in - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "svg_in - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "svg_in - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/svg_in_rel" +# PROP Intermediate_Dir "obj/svg_in_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "SVG_IN_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /I "../../include" /I "../../extra_lib/include/zlib" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "SVG_IN_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 zlib.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_svg_in.dll" /libpath:"../../extra_lib/lib/arm_ppc02_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "svg_in - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/svg_in_deb" +# PROP Intermediate_Dir "obj/svg_in_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "SVG_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/zlib" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "SVG_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 zlib.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_svg_in.dll" /libpath:"../../extra_lib/lib/arm_ppc02_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "svg_in - Win32 (WCE ARM) Release" +# Name "svg_in - Win32 (WCE ARM) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\svg_in\svg_in.c +DEP_CPP_SVG_I=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\svg_in\svg_in.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msevc3/timedtext.vcp b/build/msevc3/timedtext.vcp new file mode 100644 index 0000000..3bdaf2a --- /dev/null +++ b/build/msevc3/timedtext.vcp @@ -0,0 +1,155 @@ +# Microsoft eMbedded Visual Tools Project File - Name="timedtext" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=timedtext - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "timedtext.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "timedtext.vcn" CFG="timedtext - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "timedtext - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "timedtext - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "timedtext - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ttxt_rel" +# PROP Intermediate_Dir "obj/ttxt_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "TIMEDTEXT_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_timedtext.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "timedtext - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ttxt_deb" +# PROP Intermediate_Dir "obj/ttxt_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "TIMEDTEXT_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_timedtext.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "timedtext - Win32 (WCE ARM) Release" +# Name "timedtext - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\timedtext\timedtext.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\timedtext\timedtext_dec.c +DEP_CPP_TIMED=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\timedtext\timedtext_in.c +DEP_CPP_TIMEDT=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc3/wav_out.vcp b/build/msevc3/wav_out.vcp new file mode 100644 index 0000000..866fee0 --- /dev/null +++ b/build/msevc3/wav_out.vcp @@ -0,0 +1,118 @@ +# Microsoft eMbedded Visual Tools Project File - Name="wav_out" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=wav_out - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "wav_out.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "wav_out.vcn" CFG="wav_out - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "wav_out - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "wav_out - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "wav_out - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/wav_out_rel" +# PROP Intermediate_Dir "obj/wav_out_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "wav_out_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x100000,0x10000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_wav_out.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "wav_out - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/wav_out_deb" +# PROP Intermediate_Dir "obj/wav_out_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "wav_out_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_wav_out.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "wav_out - Win32 (WCE ARM) Release" +# Name "wav_out - Win32 (WCE ARM) Debug" +# Begin Source File + +SOURCE=..\..\modules\wav_out\wav_out.c +DEP_CPP_WAV_O=\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\wav_out\wav_out.def +# End Source File +# End Target +# End Project diff --git a/build/msevc3/xvid_dec.vcp b/build/msevc3/xvid_dec.vcp new file mode 100644 index 0000000..0b495a2 --- /dev/null +++ b/build/msevc3/xvid_dec.vcp @@ -0,0 +1,462 @@ +# Microsoft eMbedded Visual Tools Project File - Name="xvid_dec" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARM) Dynamic-Link Library" 0x8502 + +CFG=xvid_dec - Win32 (WCE ARM) Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "xvid_dec.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "xvid_dec.vcn" CFG="xvid_dec - Win32 (WCE ARM) Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "xvid_dec - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE "xvid_dec - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "xvid_dec - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/xvid_dec_rel" +# PROP Intermediate_Dir "obj/xvid_dec_rel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "XVID_DEC_EXPORTS" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /O2 /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /Oxs /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_rel/gm_xvid_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "xvid_dec - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/xvid_dec_deb" +# PROP Intermediate_Dir "obj/xvid_dec_deb" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "XVID_DEC_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc02_deb/gm_xvid_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "xvid_dec - Win32 (WCE ARM) Release" +# Name "xvid_dec - Win32 (WCE ARM) Debug" +# Begin Group "xvid_wce" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\bitstream.cpp +DEP_CPP_BITST=\ + "..\..\modules\xvid_dec\xvid_wce\bitstream.h"\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant_matrix.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\bitstream.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\CodecAPI.cpp +DEP_CPP_CODEC=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\decoder.cpp +DEP_CPP_DECOD=\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\gmc.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mbprediction.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_align.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant_matrix.h"\ + "..\..\modules\xvid_dec\xvid_wce\reduced.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\decoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\font.cpp +DEP_CPP_FONT_=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\global.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\gmc.cpp +DEP_CPP_GMC_C=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\gmc.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\gmc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\idct.cpp +DEP_CPP_IDCT_=\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\image.cpp +DEP_CPP_IMAGE=\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_align.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\reduced.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\image.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\interpolate8x8.cpp +DEP_CPP_INTER=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mbcoding.cpp +DEP_CPP_MBCOD=\ + "..\..\modules\xvid_dec\xvid_wce\bitstream.h"\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mbprediction.cpp +DEP_CPP_MBPRE=\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mbprediction.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mbprediction.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mem_align.cpp +DEP_CPP_MEM_A=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_align.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mem_align.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mem_transfer.cpp +DEP_CPP_MEM_T=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mem_transfer.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\portab.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\qpel.inl +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\qpel_tab.cpp +DEP_CPP_QPEL_=\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\qpel.inl"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\quant.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\quant_h263.cpp +DEP_CPP_QUANT=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\quant_matrix.cpp +DEP_CPP_QUANT_=\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant_matrix.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\quant_matrix.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\quant_mpeg.cpp +DEP_CPP_QUANT_M=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant_matrix.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\reduced.cpp +DEP_CPP_REDUC=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\reduced.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\reduced.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\Rules.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\vlc_codes.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\xvid.cpp +DEP_CPP_XVID_=\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant.h"\ + "..\..\modules\xvid_dec\xvid_wce\reduced.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\xvid.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\xvid_ppc.asm + +!IF "$(CFG)" == "xvid_dec - Win32 (WCE ARM) Release" + +# Begin Custom Build +IntDir=.\obj/xvid_dec_rel +InputPath=..\..\modules\xvid_dec\xvid_wce\xvid_ppc.asm +InputName=xvid_ppc + +"$(IntDir)\$(InputName).obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + armasm -CPU StrongARM1 -o $(IntDir)\$(InputName).obj $(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "xvid_dec - Win32 (WCE ARM) Debug" + +# Begin Custom Build +IntDir=.\obj/xvid_dec_deb +InputPath=..\..\modules\xvid_dec\xvid_wce\xvid_ppc.asm +InputName=xvid_ppc + +"$(IntDir)\$(InputName).obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + armasm -CPU StrongARM1 -o $(IntDir)\$(InputName).obj $(InputPath) + +# End Custom Build + +!ENDIF + +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_dec.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_dec_wce.cpp +DEP_CPP_XVID_D=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc4/GPAC.VCW b/build/msevc4/GPAC.VCW new file mode 100644 index 0000000..6ad6850 --- /dev/null +++ b/build/msevc4/GPAC.VCW @@ -0,0 +1,434 @@ +Microsoft eMbedded Visual Tools Workspace File, Format Version 4.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "GPAX"=.\GPAX.VCP - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "Osmo4"=.\Osmo4.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "aac_in"=.\aac_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ac3_in"=.\ac3_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "amr_dec"=.\amr_dec.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "bifs_dec"=.\bifs_dec.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ctx_load"=.\ctx_load.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "dummy_in"=.\dummy_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ffmpeg_in"=.\ffmpeg_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ft_font"=.\ft_font.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "gapi"=.\gapi.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "gpac_js"=.\gpac_js.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "img_in"=.\img_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ismacryp"=.\ismacryp.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "isom_in"=.\isom_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "laser_dec"=.\laser_dec.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "libgpac"=.\libgpac.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "libgpac_dll"=.\libgpac_dll.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac + End Project Dependency +}}} + +############################################################################### + +Project: "mp3_in"=.\mp3_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "odf_dec"=.\odf_dec.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "osmophone"=.\osmophone.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "rtp_in"=.\rtp_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "saf_in"=.\saf_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "soft_rast"=.\soft_rast.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "svg_in"=.\svg_in.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "timedtext"=.\timedtext.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "wav_out"=.\wav_out.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "xvid_dec"=.\xvid_dec.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/build/msevc4/GPAX.VCP b/build/msevc4/GPAX.VCP new file mode 100644 index 0000000..3fc489f --- /dev/null +++ b/build/msevc4/GPAX.VCP @@ -0,0 +1,188 @@ +# Microsoft eMbedded Visual Tools Project File - Name="GPAX" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=GPAX - Win32 (WCE ARMV4) Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "GPAX.VCN". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "GPAX.VCN" CFG="GPAX - Win32 (WCE ARMV4) Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "GPAX - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "GPAX - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "GPAX - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/gpax_rel" +# PROP Intermediate_Dir "obj/gpax_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "GPAX_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "_ATL_STATIC_REGISTRY" /YX /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/GPAX.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "GPAX - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/gpax_deb" +# PROP Intermediate_Dir "obj/gpax_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GPAX_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GPAX_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_deb/GPAX.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "GPAX - Win32 (WCE ARMV4) Release" +# Name "GPAX - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\applications\GPAX\gpax.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.cpp +DEP_CPP_GPAX_=\ + "..\..\applications\GPAX\GPAX.h"\ + "..\..\applications\GPAX\GPAX_i.c"\ + "..\..\applications\GPAX\GPAXPlugin.h"\ + "..\..\applications\GPAX\StdAfx.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + {$(INCLUDE)}"guiddef.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.def +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.idl +# ADD MTL /tlb ".\GPAX.tlb" +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.rc +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.rgs +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAXPlugin.cpp +DEP_CPP_GPAXP=\ + "..\..\applications\GPAX\GPAX.h"\ + "..\..\applications\GPAX\GPAXPlugin.h"\ + "..\..\applications\GPAX\StdAfx.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAXPlugin.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\resource.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\StdAfx.cpp +DEP_CPP_STDAF=\ + "..\..\applications\GPAX\StdAfx.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\StdAfx.h +# End Source File +# End Target +# End Project diff --git a/build/msevc4/Osmo4.vcp b/build/msevc4/Osmo4.vcp new file mode 100644 index 0000000..656e4b4 --- /dev/null +++ b/build/msevc4/Osmo4.vcp @@ -0,0 +1,286 @@ +# Microsoft eMbedded Visual Tools Project File - Name="Osmo4" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Application" 0xa301 + +CFG=Osmo4 - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Osmo4.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Osmo4.vcn" CFG="Osmo4 - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Osmo4 - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Application") +!MESSAGE "Osmo4 - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Osmo4 - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/osmo4_rel" +# PROP Intermediate_Dir "obj/osmo4_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_AFXDLL" /YX /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib aygshell.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"WinMainCRTStartup" /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 gx.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/Osmo4.exe" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "Osmo4 - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 1 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/osmo4_deb" +# PROP Intermediate_Dir "obj/osmo4_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib aygshell.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"WinMainCRTStartup" /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 gx.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_deb/Osmo4.exe" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "Osmo4 - Win32 (WCE ARMV4) Release" +# Name "Osmo4 - Win32 (WCE ARMV4) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\MainFrm.cpp +DEP_CPP_MAINF=\ + "..\..\applications\osmo4_wce\MainFrm.h"\ + "..\..\applications\osmo4_wce\Osmo4.h"\ + "..\..\applications\osmo4_wce\ProgressBar.h"\ + "..\..\applications\osmo4_wce\StdAfx.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + {$(INCLUDE)}"gx.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\OpenDlg.cpp +DEP_CPP_OPEND=\ + "..\..\applications\osmo4_wce\OpenDlg.h"\ + "..\..\applications\osmo4_wce\Osmo4.h"\ + "..\..\applications\osmo4_wce\StdAfx.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\Options.cpp +DEP_CPP_OPTIO=\ + "..\..\applications\osmo4_wce\Options.h"\ + "..\..\applications\osmo4_wce\Osmo4.h"\ + "..\..\applications\osmo4_wce\StdAfx.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\iso639.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + {$(INCLUDE)}"gx.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\Osmo4.cpp +DEP_CPP_OSMO4=\ + "..\..\applications\osmo4_wce\MainFrm.h"\ + "..\..\applications\osmo4_wce\OpenDlg.h"\ + "..\..\applications\osmo4_wce\Options.h"\ + "..\..\applications\osmo4_wce\Osmo4.h"\ + "..\..\applications\osmo4_wce\ProgressBar.h"\ + "..\..\applications\osmo4_wce\StdAfx.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + {$(INCLUDE)}"gx.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\Osmo4.rc +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\ProgressBar.cpp +DEP_CPP_PROGR=\ + "..\..\applications\osmo4_wce\Osmo4.h"\ + "..\..\applications\osmo4_wce\ProgressBar.h"\ + "..\..\applications\osmo4_wce\StdAfx.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\StdAfx.cpp +DEP_CPP_STDAF=\ + "..\..\applications\osmo4_wce\StdAfx.h"\ + +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\newres.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\OpenDlg.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\Options.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\Osmo4.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\ProgressBar.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\resource.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\StdAfx.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\res\Cmdbar.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wce\res\Osmo4.ico +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\include\gpac\sync_layer.h +# End Source File +# End Target +# End Project diff --git a/build/msevc4/aac_in.vcp b/build/msevc4/aac_in.vcp new file mode 100644 index 0000000..56b45a1 --- /dev/null +++ b/build/msevc4/aac_in.vcp @@ -0,0 +1,129 @@ +# Microsoft eMbedded Visual Tools Project File - Name="aac_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=aac_in - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "aac_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "aac_in.vcn" CFG="aac_in - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "aac_in - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "aac_in - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "aac_in - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/aac_in_rel" +# PROP Intermediate_Dir "obj/aac_in_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "AAC_IN_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /I "../../extra_lib/include/faad" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "GPAC_HAS_FAAD" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 libfaad2.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/gm_aac_in.dll" /libpath:"../../extra_lib/lib/arm_ppc03_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "aac_in - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/aac_in_deb" +# PROP Intermediate_Dir "obj/aac_in_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "AAC_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/faad" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GPAC_HAS_FAAD" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 libfaad2.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_deb/gm_aac_in.dll" /libpath:"../../extra_lib/lib/arm_ppc03_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "aac_in - Win32 (WCE ARMV4) Release" +# Name "aac_in - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\aac_in\aac_in.c +DEP_CPP_AAC_I=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\aac_in\aac_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\aac_in\faad_dec.c +# End Source File +# End Target +# End Project diff --git a/build/msevc4/amr_dec.vcp b/build/msevc4/amr_dec.vcp new file mode 100644 index 0000000..52ac844 --- /dev/null +++ b/build/msevc4/amr_dec.vcp @@ -0,0 +1,2305 @@ +# Microsoft eMbedded Visual Tools Project File - Name="amr_dec" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=amr_dec - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "amr_dec.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "amr_dec.vcn" CFG="amr_dec - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "amr_dec - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "amr_dec - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "amr_dec - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/amr_dec_rel" +# PROP Intermediate_Dir "obj/amr_dec_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "AMR_DEC_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "__MSDOS__" /D "MMS_IO" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_amr_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "amr_dec - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/amr_dec_deb" +# PROP Intermediate_Dir "obj/amr_dec_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "AMR_DEC_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "__MSDOS__" /D "MMS_IO" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_amr_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "amr_dec - Win32 (WCE ARMV4) Release" +# Name "amr_dec - Win32 (WCE ARMV4) Debug" +# Begin Group "AMR_NB" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\a_refl.c +DEP_CPP_A_REF=\ + "..\..\modules\amr_dec\amr_nb\a_refl.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\a_refl.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\agc.c +DEP_CPP_AGC_C=\ + "..\..\modules\amr_dec\amr_nb\agc.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\agc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\autocorr.c +DEP_CPP_AUTOC=\ + "..\..\modules\amr_dec\amr_nb\autocorr.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\autocorr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\az_lsp.c +DEP_CPP_AZ_LS=\ + "..\..\modules\amr_dec\amr_nb\az_lsp.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\grid.tab"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\az_lsp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\b_cn_cod.c +DEP_CPP_B_CN_=\ + "..\..\modules\amr_dec\amr_nb\b_cn_cod.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\window.tab"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\b_cn_cod.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\basic_op.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\basicop2.c +DEP_CPP_BASIC=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bgnscd.c +DEP_CPP_BGNSC=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bgnscd.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bgnscd.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bits2prm.c +DEP_CPP_BITS2=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\bits2prm.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bits2prm.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c1035pf.c +DEP_CPP_C1035=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c1035pf.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gray.tab"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\s10_8pf.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c1035pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_11pf.c +DEP_CPP_C2_11=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c2_11pf.h"\ + "..\..\modules\amr_dec\amr_nb\c2_11pf.tab"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_11pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_9pf.c +DEP_CPP_C2_9P=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c2_9pf.h"\ + "..\..\modules\amr_dec\amr_nb\c2_9pf.tab"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_9pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c3_14pf.c +DEP_CPP_C3_14=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c3_14pf.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c3_14pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c4_17pf.c +DEP_CPP_C4_17=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c4_17pf.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gray.tab"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c4_17pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c8_31pf.c +DEP_CPP_C8_31=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c8_31pf.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\s10_8pf.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c8_31pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c_g_aver.c +DEP_CPP_C_G_A=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c_g_aver.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c_g_aver.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_cor.c +DEP_CPP_CALC_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_cor.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_cor.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_en.c +DEP_CPP_CALC_E=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_en.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_en.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cbsearch.c +DEP_CPP_CBSEA=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c1035pf.h"\ + "..\..\modules\amr_dec\amr_nb\c2_11pf.h"\ + "..\..\modules\amr_dec\amr_nb\c2_9pf.h"\ + "..\..\modules\amr_dec\amr_nb\c3_14pf.h"\ + "..\..\modules\amr_dec\amr_nb\c4_17pf.h"\ + "..\..\modules\amr_dec\amr_nb\c8_31pf.h"\ + "..\..\modules\amr_dec\amr_nb\cbsearch.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cbsearch.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cl_ltp.c +DEP_CPP_CL_LT=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cl_ltp.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\convolve.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag3.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag6.h"\ + "..\..\modules\amr_dec\amr_nb\g_pitch.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_fr.h"\ + "..\..\modules\amr_dec\amr_nb\pred_lt.h"\ + "..\..\modules\amr_dec\amr_nb\q_gain_p.h"\ + "..\..\modules\amr_dec\amr_nb\ton_stab.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cl_ltp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cnst.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cnst_vad.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cod_amr.c +DEP_CPP_COD_A=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cbsearch.h"\ + "..\..\modules\amr_dec\amr_nb\cl_ltp.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\cod_amr.h"\ + "..\..\modules\amr_dec\amr_nb\convolve.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\g_adapt.h"\ + "..\..\modules\amr_dec\amr_nb\gain_q.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\levinson.h"\ + "..\..\modules\amr_dec\amr_nb\lpc.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ol_ltp.h"\ + "..\..\modules\amr_dec\amr_nb\p_ol_wgh.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_fr.h"\ + "..\..\modules\amr_dec\amr_nb\pre_big.h"\ + "..\..\modules\amr_dec\amr_nb\pred_lt.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\qua_gain.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\spreproc.h"\ + "..\..\modules\amr_dec\amr_nb\spstproc.h"\ + "..\..\modules\amr_dec\amr_nb\ton_stab.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cod_amr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\convolve.c +DEP_CPP_CONVO=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\convolve.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\convolve.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\copy.c +DEP_CPP_COPY_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\copy.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cor_h.c +DEP_CPP_COR_H=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cor_h.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cor_h.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\count.c +DEP_CPP_COUNT=\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\count.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d1035pf.c +DEP_CPP_D1035=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d1035pf.h"\ + "..\..\modules\amr_dec\amr_nb\gray.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d1035pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_11pf.c +DEP_CPP_D2_11=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d2_11pf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_11pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_9pf.c +DEP_CPP_D2_9P=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c2_9pf.tab"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d2_9pf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_9pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d3_14pf.c +DEP_CPP_D3_14=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d3_14pf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d3_14pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d4_17pf.c +DEP_CPP_D4_17=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d4_17pf.h"\ + "..\..\modules\amr_dec\amr_nb\gray.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d4_17pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d8_31pf.c +DEP_CPP_D8_31=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d8_31pf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d8_31pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_c.c +DEP_CPP_D_GAI=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_gain_c.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_c.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_p.c +DEP_CPP_D_GAIN=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_gain_p.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_p.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_homing.c +DEP_CPP_D_HOM=\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\bits2prm.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\d_homing.h"\ + "..\..\modules\amr_dec\amr_nb\d_homing.tab"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_homing.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf.c +DEP_CPP_D_PLS=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_5.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf_3.c +DEP_CPP_D_PLSF=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_3.tab"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf_5.c +DEP_CPP_D_PLSF_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_5.tab"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_amr.c +DEP_CPP_DEC_A=\ + "..\..\modules\amr_dec\amr_nb\agc.h"\ + "..\..\modules\amr_dec\amr_nb\b_cn_cod.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bgnscd.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\c_g_aver.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d1035pf.h"\ + "..\..\modules\amr_dec\amr_nb\d2_11pf.h"\ + "..\..\modules\amr_dec\amr_nb\d2_9pf.h"\ + "..\..\modules\amr_dec\amr_nb\d3_14pf.h"\ + "..\..\modules\amr_dec\amr_nb\d4_17pf.h"\ + "..\..\modules\amr_dec\amr_nb\d8_31pf.h"\ + "..\..\modules\amr_dec\amr_nb\d_gain_c.h"\ + "..\..\modules\amr_dec\amr_nb\d_gain_p.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\dec_amr.h"\ + "..\..\modules\amr_dec\amr_nb\dec_gain.h"\ + "..\..\modules\amr_dec\amr_nb\dec_lag3.h"\ + "..\..\modules\amr_dec\amr_nb\dec_lag6.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_dec.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\ec_gains.h"\ + "..\..\modules\amr_dec\amr_nb\ex_ctrl.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\int_lpc.h"\ + "..\..\modules\amr_dec\amr_nb\int_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.tab"\ + "..\..\modules\amr_dec\amr_nb\lsp_avg.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ph_disp.h"\ + "..\..\modules\amr_dec\amr_nb\pred_lt.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_amr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_gain.c +DEP_CPP_DEC_G=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dec_gain.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\qgain475.tab"\ + "..\..\modules\amr_dec\amr_nb\qua_gain.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_gain.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag3.c +DEP_CPP_DEC_L=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dec_lag3.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag3.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag6.c +DEP_CPP_DEC_LA=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dec_lag6.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag6.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_dec.c +DEP_CPP_DTX_D=\ + "..\..\modules\amr_dec\amr_nb\a_refl.h"\ + "..\..\modules\amr_dec\amr_nb\b_cn_cod.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\c_g_aver.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_dec.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.tab"\ + "..\..\modules\amr_dec\amr_nb\lsp_az.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_5.tab"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_dec.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_enc.c +DEP_CPP_DTX_E=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.tab"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_enc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\e_homing.c +DEP_CPP_E_HOM=\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\e_homing.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\e_homing.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ec_gains.c +DEP_CPP_EC_GA=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\ec_gains.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ec_gains.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\enc_lag3.c +DEP_CPP_ENC_L=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag3.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\enc_lag3.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\enc_lag6.c +DEP_CPP_ENC_LA=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag6.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\enc_lag6.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ex_ctrl.c +DEP_CPP_EX_CT=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\ex_ctrl.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ex_ctrl.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\frame.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_adapt.c +DEP_CPP_G_ADA=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\g_adapt.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_adapt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_code.c +DEP_CPP_G_COD=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\g_code.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_code.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_pitch.c +DEP_CPP_G_PIT=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\g_pitch.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_pitch.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gain_q.c +DEP_CPP_GAIN_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_en.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\g_adapt.h"\ + "..\..\modules\amr_dec\amr_nb\g_code.h"\ + "..\..\modules\amr_dec\amr_nb\gain_q.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_gain_c.h"\ + "..\..\modules\amr_dec\amr_nb\qgain475.h"\ + "..\..\modules\amr_dec\amr_nb\qgain795.h"\ + "..\..\modules\amr_dec\amr_nb\qua_gain.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gain_q.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gc_pred.c +DEP_CPP_GC_PR=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gc_pred.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gmed_n.c +DEP_CPP_GMED_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gmed_n.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\hp_max.c +DEP_CPP_HP_MA=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\hp_max.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\hp_max.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lpc.c +DEP_CPP_INT_L=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\int_lpc.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_az.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lpc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lsf.c +DEP_CPP_INT_LS=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\int_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inter_36.c +DEP_CPP_INTER=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inter_36.h"\ + "..\..\modules\amr_dec\amr_nb\inter_36.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inter_36.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inv_sqrt.c +DEP_CPP_INV_S=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inv_sqrt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lag_wind.c +DEP_CPP_LAG_W=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lag_wind.h"\ + "..\..\modules\amr_dec\amr_nb\lag_wind.tab"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lag_wind.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\levinson.c +DEP_CPP_LEVIN=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\levinson.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\levinson.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lflg_upd.c +DEP_CPP_LFLG_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\log2.c +DEP_CPP_LOG2_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\log2.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\log2.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lpc.c +DEP_CPP_LPC_C=\ + "..\..\modules\amr_dec\amr_nb\autocorr.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lag_wind.h"\ + "..\..\modules\amr_dec\amr_nb\levinson.h"\ + "..\..\modules\amr_dec\amr_nb\lpc.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\window.tab"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lpc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsfwt.c +DEP_CPP_LSFWT=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsfwt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsfwt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp.c +DEP_CPP_LSP_C=\ + "..\..\modules\amr_dec\amr_nb\az_lsp.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\int_lpc.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.tab"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_avg.c +DEP_CPP_LSP_A=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_avg.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_5.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_avg.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_az.c +DEP_CPP_LSP_AZ=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_az.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_az.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_lsf.c +DEP_CPP_LSP_L=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_lsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\mac_32.c +DEP_CPP_MAC_3=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mac_32.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\mac_32.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\mode.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\n_proc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\n_proc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ol_ltp.c +DEP_CPP_OL_LT=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ol_ltp.h"\ + "..\..\modules\amr_dec\amr_nb\p_ol_wgh.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_ol.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ol_ltp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\oper_32b.c +DEP_CPP_OPER_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\oper_32b.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\p_ol_wgh.c +DEP_CPP_P_OL_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_cor.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\corrwght.tab"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gmed_n.h"\ + "..\..\modules\amr_dec\amr_nb\hp_max.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\p_ol_wgh.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\p_ol_wgh.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ph_disp.c +DEP_CPP_PH_DI=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ph_disp.h"\ + "..\..\modules\amr_dec\amr_nb\ph_disp.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ph_disp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_fr.c +DEP_CPP_PITCH=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\convolve.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag3.h"\ + "..\..\modules\amr_dec\amr_nb\enc_lag6.h"\ + "..\..\modules\amr_dec\amr_nb\inter_36.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_fr.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_fr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_ol.c +DEP_CPP_PITCH_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_cor.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\hp_max.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_ol.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_ol.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\post_pro.c +DEP_CPP_POST_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\post_pro.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\post_pro.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pow2.c +DEP_CPP_POW2_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pow2.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_big.c +DEP_CPP_PRE_B=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pre_big.h"\ + "..\..\modules\amr_dec\amr_nb\residu.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\weight_a.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_big.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_proc.c +DEP_CPP_PRE_P=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pre_proc.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_proc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pred_lt.c +DEP_CPP_PRED_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\pred_lt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pred_lt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\preemph.c +DEP_CPP_PREEM=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\preemph.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\preemph.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\prm2bits.c +DEP_CPP_PRM2B=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\prm2bits.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\prm2bits.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pstfilt.c +DEP_CPP_PSTFI=\ + "..\..\modules\amr_dec\amr_nb\agc.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\preemph.h"\ + "..\..\modules\amr_dec\amr_nb\pstfilt.h"\ + "..\..\modules\amr_dec\amr_nb\residu.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\weight_a.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pstfilt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_c.c +DEP_CPP_Q_GAI=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\q_gain_c.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_c.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_p.c +DEP_CPP_Q_GAIN=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\q_gain_p.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_p.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf.c +DEP_CPP_Q_PLS=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf_3.c +DEP_CPP_Q_PLSF=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsfwt.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_3.tab"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf_5.c +DEP_CPP_Q_PLSF_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\lsfwt.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_lsf.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf_5.tab"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain475.c +DEP_CPP_QGAIN=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mac_32.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\qgain475.h"\ + "..\..\modules\amr_dec\amr_nb\qgain475.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain475.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain795.c +DEP_CPP_QGAIN7=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\calc_en.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\g_adapt.h"\ + "..\..\modules\amr_dec\amr_nb\gains.tab"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\mac_32.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\q_gain_p.h"\ + "..\..\modules\amr_dec\amr_nb\qgain795.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain795.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qua_gain.c +DEP_CPP_QUA_G=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\qua_gain.h"\ + "..\..\modules\amr_dec\amr_nb\qua_gain.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qua_gain.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\r_fft.c +DEP_CPP_R_FFT=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\reorder.c +DEP_CPP_REORD=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\reorder.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\reorder.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\residu.c +DEP_CPP_RESID=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\residu.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\residu.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\s10_8pf.c +DEP_CPP_S10_8=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\s10_8pf.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\s10_8pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_sign.c +DEP_CPP_SET_S=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\inv_sqrt.h"\ + "..\..\modules\amr_dec\amr_nb\set_sign.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_sign.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_zero.c +DEP_CPP_SET_Z=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_zero.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sid_sync.c +DEP_CPP_SID_S=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\sid_sync.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sid_sync.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_dec.c +DEP_CPP_SP_DE=\ + "..\..\modules\amr_dec\amr_nb\agc.h"\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bgnscd.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\bits2prm.h"\ + "..\..\modules\amr_dec\amr_nb\c_g_aver.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\dec_amr.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_dec.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\ec_gains.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_avg.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ph_disp.h"\ + "..\..\modules\amr_dec\amr_nb\post_pro.h"\ + "..\..\modules\amr_dec\amr_nb\preemph.h"\ + "..\..\modules\amr_dec\amr_nb\pstfilt.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\sp_dec.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_dec.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_enc.c +DEP_CPP_SP_EN=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\bitno.tab"\ + "..\..\modules\amr_dec\amr_nb\cl_ltp.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\cod_amr.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\g_adapt.h"\ + "..\..\modules\amr_dec\amr_nb\gain_q.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\levinson.h"\ + "..\..\modules\amr_dec\amr_nb\lpc.h"\ + "..\..\modules\amr_dec\amr_nb\lsp.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\p_ol_wgh.h"\ + "..\..\modules\amr_dec\amr_nb\pitch_fr.h"\ + "..\..\modules\amr_dec\amr_nb\pre_proc.h"\ + "..\..\modules\amr_dec\amr_nb\prm2bits.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\sp_enc.h"\ + "..\..\modules\amr_dec\amr_nb\ton_stab.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_enc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spreproc.c +DEP_CPP_SPREP=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\residu.h"\ + "..\..\modules\amr_dec\amr_nb\spreproc.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\weight_a.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spreproc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spstproc.c +DEP_CPP_SPSTP=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\spstproc.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spstproc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sqrt_l.c +DEP_CPP_SQRT_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.h"\ + "..\..\modules\amr_dec\amr_nb\sqrt_l.tab"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sqrt_l.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\strfunc.c +DEP_CPP_STRFU=\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\strfunc.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\strfunc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\syn_filt.c +DEP_CPP_SYN_F=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\syn_filt.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\syn_filt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ton_stab.c +DEP_CPP_TON_S=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\copy.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\set_zero.h"\ + "..\..\modules\amr_dec\amr_nb\ton_stab.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ton_stab.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\typedef.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\typedefs.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad1.c +DEP_CPP_VAD1_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst_vad.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad.h"\ + "..\..\modules\amr_dec\amr_nb\vad1.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad1.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad2.c +DEP_CPP_VAD2_=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\log2.h"\ + "..\..\modules\amr_dec\amr_nb\oper_32b.h"\ + "..\..\modules\amr_dec\amr_nb\pow2.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\vad2.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad2.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vadname.c +DEP_CPP_VADNA=\ + "..\..\modules\amr_dec\amr_nb\vadname.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vadname.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\weight_a.c +DEP_CPP_WEIGH=\ + "..\..\modules\amr_dec\amr_nb\basic_op.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\count.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + "..\..\modules\amr_dec\amr_nb\weight_a.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\weight_a.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_dec.c +DEP_CPP_AMR_D=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\amr_dec\amr_nb\agc.h"\ + "..\..\modules\amr_dec\amr_nb\bgnscd.h"\ + "..\..\modules\amr_dec\amr_nb\c_g_aver.h"\ + "..\..\modules\amr_dec\amr_nb\cnst.h"\ + "..\..\modules\amr_dec\amr_nb\d_homing.h"\ + "..\..\modules\amr_dec\amr_nb\d_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\dec_amr.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_dec.h"\ + "..\..\modules\amr_dec\amr_nb\dtx_enc.h"\ + "..\..\modules\amr_dec\amr_nb\ec_gains.h"\ + "..\..\modules\amr_dec\amr_nb\gc_pred.h"\ + "..\..\modules\amr_dec\amr_nb\lsp_avg.h"\ + "..\..\modules\amr_dec\amr_nb\mode.h"\ + "..\..\modules\amr_dec\amr_nb\ph_disp.h"\ + "..\..\modules\amr_dec\amr_nb\post_pro.h"\ + "..\..\modules\amr_dec\amr_nb\preemph.h"\ + "..\..\modules\amr_dec\amr_nb\pstfilt.h"\ + "..\..\modules\amr_dec\amr_nb\q_plsf.h"\ + "..\..\modules\amr_dec\amr_nb\sp_dec.h"\ + "..\..\modules\amr_dec\amr_nb\typedef.h"\ + "..\..\modules\amr_dec\amr_nb\typedefs.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_dec.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_in.c +DEP_CPP_AMR_I=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc4/bifs_dec.vcp b/build/msevc4/bifs_dec.vcp new file mode 100644 index 0000000..1ab3bba --- /dev/null +++ b/build/msevc4/bifs_dec.vcp @@ -0,0 +1,135 @@ +# Microsoft eMbedded Visual Tools Project File - Name="bifs_dec" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=bifs_dec - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "bifs_dec.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "bifs_dec.vcn" CFG="bifs_dec - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "bifs_dec - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "bifs_dec - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "bifs_dec - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/bifs_dec_rel" +# PROP Intermediate_Dir "obj/bifs_dec_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "BIFS_DEC_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_bifs_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "bifs_dec - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/bifs_dec_deb" +# PROP Intermediate_Dir "obj/bifs_dec_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "BIFS_DEC_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_bifs_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "bifs_dec - Win32 (WCE ARMV4) Release" +# Name "bifs_dec - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\bifs_dec\bifs_dec.c +DEP_CPP_BIFS_=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\bifs_dec\bifs_dec.def +# End Source File +# End Target +# End Project diff --git a/build/msevc4/ctx_load.vcp b/build/msevc4/ctx_load.vcp new file mode 100644 index 0000000..55c7725 --- /dev/null +++ b/build/msevc4/ctx_load.vcp @@ -0,0 +1,137 @@ +# Microsoft eMbedded Visual Tools Project File - Name="ctx_load" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=ctx_load - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ctx_load.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ctx_load.vcn" CFG="ctx_load - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ctx_load - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "ctx_load - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ctx_load - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ctx_load_rel" +# PROP Intermediate_Dir "obj/ctx_load_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "CTX_LOAD_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_ctx_load.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "ctx_load - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ctx_load_deb" +# PROP Intermediate_Dir "obj/ctx_load_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "CTX_LOAD_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_ctx_load.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "ctx_load - Win32 (WCE ARMV4) Release" +# Name "ctx_load - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\ctx_load\ctx_load.c +DEP_CPP_CTX_L=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ctx_load\ctx_load.def +# End Source File +# End Target +# End Project diff --git a/build/msevc4/dummy_in.vcp b/build/msevc4/dummy_in.vcp new file mode 100644 index 0000000..51e841d --- /dev/null +++ b/build/msevc4/dummy_in.vcp @@ -0,0 +1,124 @@ +# Microsoft eMbedded Visual Tools Project File - Name="dummy_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=dummy_in - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dummy_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dummy_in.vcn" CFG="dummy_in - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dummy_in - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "dummy_in - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dummy_in - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/dumm_in_rel" +# PROP Intermediate_Dir "obj/dumm_in_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "DUMMY_IN_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_dummy_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "dummy_in - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/dumm_in_deb" +# PROP Intermediate_Dir "obj/dumm_in_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "DUMMY_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /FR /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_dummy_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "dummy_in - Win32 (WCE ARMV4) Release" +# Name "dummy_in - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\dummy_in\dummy_in.c +DEP_CPP_DUMMY=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\xml.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dummy_in\dummy_in.def +# End Source File +# End Target +# End Project diff --git a/build/msevc4/ffmpeg_in.vcp b/build/msevc4/ffmpeg_in.vcp new file mode 100644 index 0000000..b20f0e3 --- /dev/null +++ b/build/msevc4/ffmpeg_in.vcp @@ -0,0 +1,228 @@ +# Microsoft eMbedded Visual Tools Project File - Name="ffmpeg_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=ffmpeg_in - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ffmpeg_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ffmpeg_in.vcn" CFG="ffmpeg_in - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ffmpeg_in - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "ffmpeg_in - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ffmpeg_in - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ffmpeg_in_rel" +# PROP Intermediate_Dir "obj/ffmpeg_in_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "FFMPEG_IN_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /I "../../include" /I "../../extra_lib/include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "FFMPEG_IN_EXPORTS" /M$(CECrtMT) /c +# SUBTRACT CPP /O /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 ffmpeg.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/gm_ffmpeg_in.dll" /libpath:"../../extra_lib/lib/arm_ppc03_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "ffmpeg_in - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ffmpeg_in_deb" +# PROP Intermediate_Dir "obj/ffmpeg_in_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "FFMPEG_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 ffmpeg.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_deb/gm_ffmpeg_in.dll" /libpath:"../../extra_lib/lib/arm_ppc03_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "ffmpeg_in - Win32 (WCE ARMV4) Release" +# Name "ffmpeg_in - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\ffmpeg_in\ffmpeg_decode.c +DEP_CPP_FFMPE=\ + "..\..\extra_lib\include\ffmpeg\avcodec.h"\ + "..\..\extra_lib\include\ffmpeg\avformat.h"\ + "..\..\extra_lib\include\ffmpeg\avio.h"\ + "..\..\extra_lib\include\ffmpeg\avutil.h"\ + "..\..\extra_lib\include\ffmpeg\common.h"\ + "..\..\extra_lib\include\ffmpeg\integer.h"\ + "..\..\extra_lib\include\ffmpeg\intfloat_readwrite.h"\ + "..\..\extra_lib\include\ffmpeg\log.h"\ + "..\..\extra_lib\include\ffmpeg\mathematics.h"\ + "..\..\extra_lib\include\ffmpeg\rational.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\ffmpeg_in\ffmpeg_in.h"\ + +NODEP_CPP_FFMPE=\ + "..\..\extra_lib\include\ffmpeg\berrno.h"\ + "..\..\extra_lib\include\ffmpeg\config.h"\ + "..\..\extra_lib\include\ffmpeg\internal.h"\ + "..\..\extra_lib\include\ffmpeg\os_support.h"\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ffmpeg_in\ffmpeg_demux.c +DEP_CPP_FFMPEG=\ + "..\..\extra_lib\include\ffmpeg\avcodec.h"\ + "..\..\extra_lib\include\ffmpeg\avformat.h"\ + "..\..\extra_lib\include\ffmpeg\avio.h"\ + "..\..\extra_lib\include\ffmpeg\avutil.h"\ + "..\..\extra_lib\include\ffmpeg\common.h"\ + "..\..\extra_lib\include\ffmpeg\integer.h"\ + "..\..\extra_lib\include\ffmpeg\intfloat_readwrite.h"\ + "..\..\extra_lib\include\ffmpeg\log.h"\ + "..\..\extra_lib\include\ffmpeg\mathematics.h"\ + "..\..\extra_lib\include\ffmpeg\rational.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\ffmpeg_in\ffmpeg_in.h"\ + +NODEP_CPP_FFMPEG=\ + "..\..\extra_lib\include\ffmpeg\berrno.h"\ + "..\..\extra_lib\include\ffmpeg\config.h"\ + "..\..\extra_lib\include\ffmpeg\internal.h"\ + "..\..\extra_lib\include\ffmpeg\os_support.h"\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ffmpeg_in\ffmpeg_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ffmpeg_in\ffmpeg_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ffmpeg_in\ffmpeg_load.c +DEP_CPP_FFMPEG_=\ + "..\..\extra_lib\include\ffmpeg\avcodec.h"\ + "..\..\extra_lib\include\ffmpeg\avformat.h"\ + "..\..\extra_lib\include\ffmpeg\avio.h"\ + "..\..\extra_lib\include\ffmpeg\avutil.h"\ + "..\..\extra_lib\include\ffmpeg\common.h"\ + "..\..\extra_lib\include\ffmpeg\integer.h"\ + "..\..\extra_lib\include\ffmpeg\intfloat_readwrite.h"\ + "..\..\extra_lib\include\ffmpeg\log.h"\ + "..\..\extra_lib\include\ffmpeg\mathematics.h"\ + "..\..\extra_lib\include\ffmpeg\rational.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\ffmpeg_in\ffmpeg_in.h"\ + +NODEP_CPP_FFMPEG_=\ + "..\..\extra_lib\include\ffmpeg\berrno.h"\ + "..\..\extra_lib\include\ffmpeg\config.h"\ + "..\..\extra_lib\include\ffmpeg\internal.h"\ + "..\..\extra_lib\include\ffmpeg\os_support.h"\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc4/ft_font.vcp b/build/msevc4/ft_font.vcp new file mode 100644 index 0000000..44bbf74 --- /dev/null +++ b/build/msevc4/ft_font.vcp @@ -0,0 +1,135 @@ +# Microsoft eMbedded Visual Tools Project File - Name="ft_font" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=ft_font - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ft_font.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ft_font.vcn" CFG="ft_font - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ft_font - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "ft_font - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ft_font - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ft_font_rel" +# PROP Intermediate_Dir "obj/ft_font_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "FT_FONT_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /I "../../extra_lib/include/freetype" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 freetype.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_ft_font.dll" /libpath:"../../extra_lib/lib/arm_ppc03_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "ft_font - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ft_font_deb" +# PROP Intermediate_Dir "obj/ft_font_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "FT_FONT_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/freetype" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 freetype.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_ft_font.dll" /libpath:"../../extra_lib/lib/arm_ppc03_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "ft_font - Win32 (WCE ARMV4) Release" +# Name "ft_font - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\ft_font\ft_font.c +DEP_CPP_FT_FO=\ + "..\..\extra_lib\include\freetype\freetype\config\ftheader.h"\ + "..\..\extra_lib\include\freetype\ft2build.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + +NODEP_CPP_FT_FO=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ft_font\ft_font.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ft_font\ft_font.h +# End Source File +# End Target +# End Project diff --git a/build/msevc4/gapi.vcp b/build/msevc4/gapi.vcp new file mode 100644 index 0000000..230326e --- /dev/null +++ b/build/msevc4/gapi.vcp @@ -0,0 +1,140 @@ +# Microsoft eMbedded Visual Tools Project File - Name="gapi" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=gapi - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "gapi.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "gapi.vcn" CFG="gapi - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "gapi - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "gapi - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "gapi - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/gapi_rel" +# PROP Intermediate_Dir "obj/gapi_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "GAPI_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /I "../../extra_lib/include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "GPAC_HAVE_CONFIG_H" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 gx.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_gapi.dll" /libpath:"../../extra_lib/lib/arm_ppc03_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "gapi - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/gapi_deb" +# PROP Intermediate_Dir "obj/gapi_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GAPI_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GPAC_HAVE_CONFIG_H" /FR /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 gx.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_gapi.dll" /libpath:"../../extra_lib/lib/arm_ppc03_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "gapi - Win32 (WCE ARMV4) Release" +# Name "gapi - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\gapi\gapi.cpp +DEP_CPP_GAPI_=\ + "..\..\extra_lib\include\GLES\egl.h"\ + "..\..\extra_lib\include\GLES\gl.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\gapi\gapi.h"\ + {$(INCLUDE)}"aygshell.h"\ + {$(INCLUDE)}"gx.h"\ + {$(INCLUDE)}"sipapi.h"\ + {$(INCLUDE)}"winuserm.h"\ + +NODEP_CPP_GAPI_=\ + "..\..\include\gpac\config.h"\ + "C:\Program Files\Windows CE Tools\wce420\POCKET PC 2003\Include\ARMV4\vibrate.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\gapi\gapi.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\gapi\gapi.h +# End Source File +# End Target +# End Project diff --git a/build/msevc4/img_in.vcp b/build/msevc4/img_in.vcp new file mode 100644 index 0000000..c21616e --- /dev/null +++ b/build/msevc4/img_in.vcp @@ -0,0 +1,234 @@ +# Microsoft eMbedded Visual Tools Project File - Name="img_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=img_in - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "img_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "img_in.vcn" CFG="img_in - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "img_in - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "img_in - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "img_in - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/img_in_rel" +# PROP Intermediate_Dir "obj/img_in_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "IMG_IN_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /I "../../extra_lib/include/openjpeg" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "GPAC_HAS_JPEG" /D "GPAC_HAS_PNG" /D "GPAC_HAS_JP2" /D "OPJ_STATIC" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 LibOpenJPEG.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_img_in.dll" /libpath:"../../extra_lib/lib/arm_ppc03_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "img_in - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/img_in_deb" +# PROP Intermediate_Dir "obj/img_in_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "IMG_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/png" /I "../../extra_lib/include/zlib" /I "../../extra_lib/include/jpeg" /I "../../extra_lib/include/openjpeg" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GPAC_HAS_JPEG" /D "GPAC_HAS_PNG" /D "GPAC_HAS_JP2" /D "OPJ_STATIC" /FR /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 LibOpenJPEGd.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_img_in.dll" /libpath:"../../extra_lib/lib/arm_ppc03_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "img_in - Win32 (WCE ARMV4) Release" +# Name "img_in - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\img_in\bmp_dec.c +DEP_CPP_BMP_D=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_dec.c +DEP_CPP_IMG_D=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_in.c +DEP_CPP_IMG_I=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\jp2_dec.c +DEP_CPP_JP2_D=\ + "..\..\extra_lib\include\openjpeg\openjpeg.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\jpeg_dec.c +DEP_CPP_JPEG_=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\png_dec.c +DEP_CPP_PNG_D=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\img_in\img_in.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc4/ismacryp.vcp b/build/msevc4/ismacryp.vcp new file mode 100644 index 0000000..16ddaab --- /dev/null +++ b/build/msevc4/ismacryp.vcp @@ -0,0 +1,150 @@ +# Microsoft eMbedded Visual Tools Project File - Name="ismacryp" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=ismacryp - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ismacryp.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ismacryp.vcn" CFG="ismacryp - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ismacryp - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "ismacryp - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ismacryp - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ismacryp_rel" +# PROP Intermediate_Dir "obj/ismacryp_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "ISMACRYP_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "ISMACRYP_EXPORTS" /YX /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/gm_ismacryp.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "ismacryp - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ismacryp_deb" +# PROP Intermediate_Dir "obj/ismacryp_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "ISMACRYP_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "ISMACRYP_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/gm_ismacryp.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "ismacryp - Win32 (WCE ARMV4) Release" +# Name "ismacryp - Win32 (WCE ARMV4) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\ismacryp\ismacryp.c +DEP_CPP_ISMAC=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\ismacryp.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ismacryp\ismacryp.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msevc4/isom_in.vcp b/build/msevc4/isom_in.vcp new file mode 100644 index 0000000..fdc2d4b --- /dev/null +++ b/build/msevc4/isom_in.vcp @@ -0,0 +1,194 @@ +# Microsoft eMbedded Visual Tools Project File - Name="isom_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=isom_in - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "isom_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "isom_in.vcn" CFG="isom_in - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "isom_in - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "isom_in - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "isom_in - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/isom_in_deb" +# PROP Intermediate_Dir "obj/isom_in_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "ISOM_IN_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/gm_isom_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "isom_in - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/isom_in_deb" +# PROP Intermediate_Dir "obj/isom_in_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "ISOM_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_deb/gm_isom_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "isom_in - Win32 (WCE ARMV4) Release" +# Name "isom_in - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\isom_in\cache.c +DEP_CPP_CACHE=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\isom_in\isom_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\isom_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\isom_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\load.c +DEP_CPP_LOAD_=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\isom_in\isom_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\read.c +DEP_CPP_READ_=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\isom_in\isom_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\read_ch.c +DEP_CPP_READ_C=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\isom_in\isom_in.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc4/laser_dec.vcp b/build/msevc4/laser_dec.vcp new file mode 100644 index 0000000..79423ec --- /dev/null +++ b/build/msevc4/laser_dec.vcp @@ -0,0 +1,151 @@ +# Microsoft eMbedded Visual Tools Project File - Name="laser_dec" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=laser_dec - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "laser_dec.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "laser_dec.vcn" CFG="laser_dec - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "laser_dec - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "laser_dec - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "laser_dec - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/laser_dec_rel" +# PROP Intermediate_Dir "obj/laser_dec_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "LASER_DEC_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /YX /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/gm_laser_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "laser_dec - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/laser_dec_deb" +# PROP Intermediate_Dir "obj/laser_dec_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "LASER_DEC_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_deb/gm_laser_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "laser_dec - Win32 (WCE ARMV4) Release" +# Name "laser_dec - Win32 (WCE ARMV4) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\laser_dec\laser_dec.c +DEP_CPP_LASER=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_svg_da.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\laser_dec\laser_dec.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msevc4/libgpac.vcp b/build/msevc4/libgpac.vcp new file mode 100644 index 0000000..499b85e --- /dev/null +++ b/build/msevc4/libgpac.vcp @@ -0,0 +1,7112 @@ +# Microsoft eMbedded Visual Tools Project File - Name="libgpac" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Static Library" 0xa304 + +CFG=libgpac - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libgpac.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libgpac.vcn" CFG="libgpac - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libgpac - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Static Library") +!MESSAGE "libgpac - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe + +!IF "$(CFG)" == "libgpac - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/libgpac_rel" +# PROP Intermediate_Dir "obj/libgpac_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "NDEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /I "../../extra_lib/include/zlib" /I "../../extra_lib/include/js" /I "../../extra_lib/include" /I "../../extra_lib/include/jpeg" /I "../../extra_lib/include/png" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "NDEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /D "GPAC_HAVE_CONFIG_H" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ELSEIF "$(CFG)" == "libgpac - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/libgpac_deb" +# PROP Intermediate_Dir "obj/libgpac_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/zlib" /I "../../extra_lib/include/js" /I "../../extra_lib/include" /I "../../extra_lib/include/jpeg" /I "../../extra_lib/include/png" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /D "GPAC_HAVE_CONFIG_H" /FR /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ENDIF + +# Begin Target + +# Name "libgpac - Win32 (WCE ARMV4) Release" +# Name "libgpac - Win32 (WCE ARMV4) Debug" +# Begin Group "utils" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\utils\base_encoding.c +DEP_CPP_BASE_=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BASE_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\bitstream.c +DEP_CPP_BITST=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BITST=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\color.c +DEP_CPP_COLOR=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_COLOR=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\configfile.c +DEP_CPP_CONFI=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_CONFI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\downloader.c +DEP_CPP_DOWNL=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_DOWNL=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\error.c +DEP_CPP_ERROR=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ERROR=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\list.c +DEP_CPP_LIST_=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_LIST_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\math.c +DEP_CPP_MATH_=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\setup.h"\ + +NODEP_CPP_MATH_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\module.c +DEP_CPP_MODUL=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\utils\module_wrap.h"\ + +NODEP_CPP_MODUL=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\module_wrap.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_divers.c +DEP_CPP_OS_DI=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_OS_DI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_module.c +DEP_CPP_OS_MO=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\utils\module_wrap.h"\ + +NODEP_CPP_OS_MO=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_net.c +DEP_CPP_OS_NE=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_OS_NE=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_thread.c +DEP_CPP_OS_TH=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_OS_TH=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\path2d.c +DEP_CPP_PATH2=\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_PATH2=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\path2d_stroker.c +DEP_CPP_PATH2D=\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_PATH2D=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\token.c +DEP_CPP_TOKEN=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_TOKEN=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\uni_bidi.c +DEP_CPP_UNI_B=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + +NODEP_CPP_UNI_B=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\url.c +DEP_CPP_URL_C=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_URL_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\utf.c +DEP_CPP_UTF_C=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + +NODEP_CPP_UTF_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\xml_parser.c +DEP_CPP_XML_P=\ + "..\..\extra_lib\include\zlib\zconf.h"\ + "..\..\extra_lib\include\zlib\zlib.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_XML_P=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Group +# Begin Group "odf" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\odf\desc_private.c +DEP_CPP_DESC_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_DESC_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\descriptors.c +DEP_CPP_DESCR=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_DESCR=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\ipmpx_code.c +DEP_CPP_IPMPX=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_IPMPX=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\ipmpx_parse.c +DEP_CPP_IPMPX_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_IPMPX_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\oci_codec.c +DEP_CPP_OCI_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_OCI_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_code.c +DEP_CPP_ODF_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + +NODEP_CPP_ODF_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_codec.c +DEP_CPP_ODF_CO=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ODF_CO=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_command.c +DEP_CPP_ODF_COM=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ODF_COM=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\odf_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_parse.c +DEP_CPP_ODF_P=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ODF_P=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\qos.c +DEP_CPP_QOS_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_QOS_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\slc.c +DEP_CPP_SLC_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SLC_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Group +# Begin Group "bifs" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\bifs\arith_decoder.c +DEP_CPP_ARITH=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_ARITH=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\bifs_codec.c +DEP_CPP_BIFS_=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BIFS_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\bifs_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\bifs_node_tables.c +DEP_CPP_BIFS_N=\ + "..\..\include\gpac\internal\bifs_tables.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BIFS_N=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\bifs_tables.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\com_dec.c +DEP_CPP_COM_D=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_COM_D=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\com_enc.c +DEP_CPP_COM_E=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_COM_E=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\conditional.c +DEP_CPP_CONDI=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_CONDI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\field_decode.c +DEP_CPP_FIELD=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + "..\..\src\bifs\script.h"\ + +NODEP_CPP_FIELD=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\field_encode.c +DEP_CPP_FIELD_=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\bifs_tables.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + "..\..\src\bifs\script.h"\ + +NODEP_CPP_FIELD_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\memory_decoder.c +DEP_CPP_MEMOR=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_MEMOR=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\predictive_mffield.c +DEP_CPP_PREDI=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_PREDI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\quant.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\quantize.c +DEP_CPP_QUANT=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_QUANT=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\script.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\script_dec.c +DEP_CPP_SCRIP=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\script.h"\ + +NODEP_CPP_SCRIP=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\script_enc.c +DEP_CPP_SCRIPT=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\script.h"\ + +NODEP_CPP_SCRIPT=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\unquantize.c +DEP_CPP_UNQUA=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_UNQUA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Group +# Begin Group "isomedia" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\isomedia\avc_ext.c +DEP_CPP_AVC_E=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_AVC_E=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_3gpp.c +DEP_CPP_BOX_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BOX_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_apple.c +DEP_CPP_BOX_CO=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BOX_CO=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_base.c +DEP_CPP_BOX_COD=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BOX_COD=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_isma.c +DEP_CPP_BOX_CODE=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BOX_CODE=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_meta.c +DEP_CPP_BOX_CODE_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BOX_CODE_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_funcs.c +DEP_CPP_BOX_F=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BOX_F=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\data_map.c +DEP_CPP_DATA_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_DATA_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\hint_track.c +DEP_CPP_HINT_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_HINT_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\hinting.c +DEP_CPP_HINTI=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_HINTI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isma_sample.c +DEP_CPP_ISMA_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ISMA_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_intern.c +DEP_CPP_ISOM_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ISOM_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_read.c +DEP_CPP_ISOM_R=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ISOM_R=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_store.c +DEP_CPP_ISOM_S=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ISOM_S=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_write.c +DEP_CPP_ISOM_W=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ISOM_W=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\isomedia_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\media.c +DEP_CPP_MEDIA=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MEDIA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\media_odf.c +DEP_CPP_MEDIA_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MEDIA_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\meta.c +DEP_CPP_META_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_META_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\movie_fragments.c +DEP_CPP_MOVIE=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MOVIE=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\sample_descs.c +DEP_CPP_SAMPL=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SAMPL=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\stbl_read.c +DEP_CPP_STBL_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_STBL_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\stbl_write.c +DEP_CPP_STBL_W=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_STBL_W=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\track.c +DEP_CPP_TRACK=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_TRACK=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\tx3g.c +DEP_CPP_TX3G_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_TX3G_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Group +# Begin Group "ietf" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\include\gpac\internal\ietf_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtcp.c +DEP_CPP_RTCP_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTCP_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp.c +DEP_CPP_RTP_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTP_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_depacketizer.c +DEP_CPP_RTP_D=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\esi.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTP_D=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_packetizer.c +DEP_CPP_RTP_P=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTP_P=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_pck_3gpp.c +DEP_CPP_RTP_PC=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTP_PC=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_pck_mpeg12.c +DEP_CPP_RTP_PCK=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTP_PCK=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_pck_mpeg4.c +DEP_CPP_RTP_PCK_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTP_PCK_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_command.c +DEP_CPP_RTSP_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTSP_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_common.c +DEP_CPP_RTSP_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTSP_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_response.c +DEP_CPP_RTSP_R=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTSP_R=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_session.c +DEP_CPP_RTSP_S=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ietf_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RTSP_S=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\sdp.c +DEP_CPP_SDP_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SDP_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Group +# Begin Group "scenegraph" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\scenegraph\base_scenegraph.c +DEP_CPP_BASE_S=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_BASE_S=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\commands.c +DEP_CPP_COMMA=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\laser_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_COMMA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\dom_events.c +DEP_CPP_DOM_E=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_DOM_E=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\dom_smjs.c +DEP_CPP_DOM_S=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_DOM_S=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\mpeg4_animators.c +DEP_CPP_MPEG4=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MPEG4=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\mpeg4_nodes.c +DEP_CPP_MPEG4_=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MPEG4_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\mpeg4_valuator.c +DEP_CPP_MPEG4_V=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MPEG4_V=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\scenegraph_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\smil_anim.c +DEP_CPP_SMIL_=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SMIL_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\smil_timing.c +DEP_CPP_SMIL_T=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SMIL_T=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_attributes.c +DEP_CPP_SVG_A=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SVG_A=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_properties.c +DEP_CPP_SVG_P=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SVG_P=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_smjs.c +DEP_CPP_SVG_S=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SVG_S=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_types.c +DEP_CPP_SVG_T=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SVG_T=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_interpolators.c +DEP_CPP_VRML_=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_proto.c +DEP_CPP_VRML_P=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_P=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_route.c +DEP_CPP_VRML_R=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_R=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_script.c +DEP_CPP_VRML_S=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_S=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_smjs.c +DEP_CPP_VRML_SM=\ + "..\..\extra_lib\include\js\jsapi.h"\ + "..\..\extra_lib\include\js\jsautocfg.h"\ + "..\..\extra_lib\include\js\jscompat.h"\ + "..\..\extra_lib\include\js\jscpucfg.h"\ + "..\..\extra_lib\include\js\jslong.h"\ + "..\..\extra_lib\include\js\jsosdep.h"\ + "..\..\extra_lib\include\js\jsotypes.h"\ + "..\..\extra_lib\include\js\jspubtd.h"\ + "..\..\extra_lib\include\js\jstypes.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\js_usr.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_VRML_SM=\ + "..\..\extra_lib\include\js\jsstddef.h"\ + "..\..\extra_lib\include\js\platform.h"\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_tools.c +DEP_CPP_VRML_T=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_VRML_T=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\x3d_nodes.c +DEP_CPP_X3D_N=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_X3D_N=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\xbl_process.c +DEP_CPP_XBL_P=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_xbl.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_XBL_P=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\xml_ns.c +DEP_CPP_XML_N=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_XML_N=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Group +# Begin Group "media_tools" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\media_tools\av_parsers.c +DEP_CPP_AV_PA=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\internal\ogg.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_AV_PA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\avilib.c +DEP_CPP_AVILI=\ + "..\..\include\gpac\internal\avilib.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_AVILI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\avilib.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\gpac_ogg.c +DEP_CPP_GPAC_=\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\ogg.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_GPAC_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\img.c +DEP_CPP_IMG_C=\ + "..\..\extra_lib\include\jpeg\jconfig.h"\ + "..\..\extra_lib\include\jpeg\jmorecfg.h"\ + "..\..\extra_lib\include\jpeg\jpeglib.h"\ + "..\..\extra_lib\include\png\png.h"\ + "..\..\extra_lib\include\png\pngconf.h"\ + "..\..\extra_lib\include\zlib\zconf.h"\ + "..\..\extra_lib\include\zlib\zlib.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_IMG_C=\ + "..\..\extra_lib\include\jpeg\jerror.h"\ + "..\..\extra_lib\include\jpeg\jpegint.h"\ + "..\..\extra_lib\include\png\alloc.h"\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\ismacryp.c +DEP_CPP_ISMAC=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\ismacryp.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_ISMAC=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\isom_hinter.c +DEP_CPP_ISOM_H=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ISOM_H=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\isom_tools.c +DEP_CPP_ISOM_T=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ISOM_T=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\media_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\media_export.c +DEP_CPP_MEDIA_E=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\avilib.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\internal\ogg.h"\ + "..\..\include\gpac\internal\vobsub.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\mpegts.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MEDIA_E=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\media_import.c +DEP_CPP_MEDIA_I=\ + "..\..\extra_lib\include\zlib\zconf.h"\ + "..\..\extra_lib\include\zlib\zlib.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\avilib.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\internal\odf_dev.h"\ + "..\..\include\gpac\internal\ogg.h"\ + "..\..\include\gpac\internal\vobsub.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\mpegts.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\xml.h"\ + "..\..\src\media_tools\mpeg2_ps.h"\ + +NODEP_CPP_MEDIA_I=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\mpeg2_ps.c +DEP_CPP_MPEG2=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\media_tools\mpeg2_ps.h"\ + +NODEP_CPP_MPEG2=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\mpeg2_ps.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\text_import.c +DEP_CPP_TEXT_=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\token.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_TEXT_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\vobsub.h +# End Source File +# End Group +# Begin Group "scene_manager" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\scene_manager\encode_cbk.c +DEP_CPP_ENCOD=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bifsengine.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ENCOD=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\encode_isom.c +DEP_CPP_ENCODE=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ENCODE=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_bt.c +DEP_CPP_LOADE=\ + "..\..\extra_lib\include\zlib\zconf.h"\ + "..\..\extra_lib\include\zlib\zlib.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + +NODEP_CPP_LOADE=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_isom.c +DEP_CPP_LOADER=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_LOADER=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_qt.c +DEP_CPP_LOADER_=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_LOADER_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_svg.c +DEP_CPP_LOADER_S=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\laser_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_LOADER_S=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_xmt.c +DEP_CPP_LOADER_X=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_LOADER_X=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\scene_dump.c +DEP_CPP_SCENE=\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\bifs_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\src\bifs\quant.h"\ + +NODEP_CPP_SCENE=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\scene_manager.c +DEP_CPP_SCENE_=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bifs.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_SCENE_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\scene_stats.c +DEP_CPP_SCENE_S=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SCENE_S=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\swf_bifs.c +DEP_CPP_SWF_B=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\internal\swf_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_SWF_B=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\swf_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\swf_parse.c +DEP_CPP_SWF_P=\ + "..\..\extra_lib\include\zlib\zconf.h"\ + "..\..\extra_lib\include\zlib\zlib.h"\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\swf_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SWF_P=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\text_to_bifs.c +DEP_CPP_TEXT_T=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\media_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_TEXT_T=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Group +# Begin Group "mcrypt" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\mcrypt\cbc.c +DEP_CPP_CBC_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_CBC_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\cfb.c +DEP_CPP_CFB_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_CFB_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\crypt_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ctr.c +DEP_CPP_CTR_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_CTR_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\des.c +DEP_CPP_DES_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_DES_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ecb.c +DEP_CPP_ECB_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_ECB_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\g_crypt.c +DEP_CPP_G_CRY=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_G_CRY=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ncfb.c +DEP_CPP_NCFB_=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_NCFB_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\nofb.c +DEP_CPP_NOFB_=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_NOFB_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ofb.c +DEP_CPP_OFB_C=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_OFB_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE="..\..\src\mcrypt\rijndael-128.c" +DEP_CPP_RIJND=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RIJND=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE="..\..\src\mcrypt\rijndael-192.c" +DEP_CPP_RIJNDA=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RIJNDA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE="..\..\src\mcrypt\rijndael-256.c" +DEP_CPP_RIJNDAE=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_RIJNDAE=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\sha1.c +DEP_CPP_SHA1_=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_SHA1_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\stream.c +DEP_CPP_STREA=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_STREA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\tripledes.c +DEP_CPP_TRIPL=\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\crypt_dev.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_TRIPL=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Group +# Begin Group "terminal" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\terminal\channel.c +DEP_CPP_CHANN=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + "..\..\src\terminal\media_memory.h"\ + +NODEP_CPP_CHANN=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\clock.c +DEP_CPP_CLOCK=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_CLOCK=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\decoder.c +DEP_CPP_DECOD=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\input_sensor.h"\ + "..\..\src\terminal\media_control.h"\ + "..\..\src\terminal\media_memory.h"\ + +NODEP_CPP_DECOD=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\inline.c +DEP_CPP_INLIN=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + +NODEP_CPP_INLIN=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\input_sensor.c +DEP_CPP_INPUT=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\src\terminal\input_sensor.h"\ + +NODEP_CPP_INPUT=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\input_sensor.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_control.c +DEP_CPP_MEDIA_C=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + +NODEP_CPP_MEDIA_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_control.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_manager.c +DEP_CPP_MEDIA_M=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_memory.h"\ + +NODEP_CPP_MEDIA_M=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_memory.c +DEP_CPP_MEDIA_ME=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + "..\..\src\terminal\media_memory.h"\ + +NODEP_CPP_MEDIA_ME=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_memory.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_object.c +DEP_CPP_MEDIA_O=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + "..\..\src\terminal\media_memory.h"\ + +NODEP_CPP_MEDIA_O=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_sensor.c +DEP_CPP_MEDIA_S=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_control.h"\ + +NODEP_CPP_MEDIA_S=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\network_service.c +DEP_CPP_NETWO=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_NETWO=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\object_browser.c +DEP_CPP_OBJEC=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\term_info.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\media_memory.h"\ + +NODEP_CPP_OBJEC=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\object_manager.c +DEP_CPP_OBJECT=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\input_sensor.h"\ + "..\..\src\terminal\media_control.h"\ + "..\..\src\terminal\media_memory.h"\ + +NODEP_CPP_OBJECT=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\svg_external.c +DEP_CPP_SVG_E=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_SVG_E=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\term_node_init.c +DEP_CPP_TERM_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\terminal\input_sensor.h"\ + +NODEP_CPP_TERM_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\terminal.c +DEP_CPP_TERMI=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\term_ext.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\xml.h"\ + +NODEP_CPP_TERMI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\terminal_dev.h +# End Source File +# End Group +# Begin Group "laser" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\include\gpac\internal\laser_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\laser\lsr_dec.c +DEP_CPP_LSR_D=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\laser_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_LSR_D=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\laser\lsr_enc.c +DEP_CPP_LSR_E=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\laser_dev.h"\ + "..\..\include\gpac\internal\scenegraph_dev.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_LSR_E=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\laser\lsr_tables.c +DEP_CPP_LSR_T=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\laser_dev.h"\ + "..\..\include\gpac\laser.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_LSR_T=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Group +# Begin Group "compositor" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\compositor\audio_input.c +DEP_CPP_AUDIO=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_AUDIO=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\audio_mixer.c +DEP_CPP_AUDIO_=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_AUDIO_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\audio_render.c +DEP_CPP_AUDIO_R=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +NODEP_CPP_AUDIO_R=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\bindable.c +DEP_CPP_BINDA=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + +NODEP_CPP_BINDA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\camera.c +DEP_CPP_CAMER=\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_CAMER=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\camera.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\color.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\compositor.c +DEP_CPP_COMPO=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_COMPO=\ + "..\..\include\gpac\config.h"\ + "..\..\src\src\compositor\triscope_renoir\triscope_renoir.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\compositor_2d.c +DEP_CPP_COMPOS=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_COMPOS=\ + "..\..\include\gpac\config.h"\ + "..\..\src\src\compositor\triscope_renoir\triscope_renoir.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\compositor_3d.c +DEP_CPP_COMPOSI=\ + "..\..\..\TinyGL\include\GL\oscontext.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_COMPOSI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\compositor_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\compositor_node_init.c +DEP_CPP_COMPOSIT=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + +NODEP_CPP_COMPOSIT=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\drawable.c +DEP_CPP_DRAWA=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_DRAWA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\drawable.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\events.c +DEP_CPP_EVENT=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_EVENT=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\font_engine.c +DEP_CPP_FONT_=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_FONT_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\gl_inc.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\hardcoded_protos.c +DEP_CPP_HARDC=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\offscreen_cache.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_HARDC=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mesh.c +DEP_CPP_MESH_=\ + "..\..\..\TinyGL\include\GL\gl.h"\ + "..\..\extra_lib\include\GLES\egl.h"\ + "..\..\extra_lib\include\GLES\gl.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\compositor\gl_inc.h"\ + +NODEP_CPP_MESH_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\mesh.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mesh_collide.c +DEP_CPP_MESH_C=\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +NODEP_CPP_MESH_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mesh_tesselate.c +DEP_CPP_MESH_T=\ + "..\..\..\TinyGL\include\GL\gl.h"\ + "..\..\extra_lib\include\GLES\egl.h"\ + "..\..\extra_lib\include\GLES\gl.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\src\compositor\gl_inc.h"\ + +NODEP_CPP_MESH_T=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_animstream.c +DEP_CPP_MPEG4_A=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + +NODEP_CPP_MPEG4_A=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_audio.c +DEP_CPP_MPEG4_AU=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + +NODEP_CPP_MPEG4_AU=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_background.c +DEP_CPP_MPEG4_B=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_B=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_background2d.c +DEP_CPP_MPEG4_BA=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_BA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_bitmap.c +DEP_CPP_MPEG4_BI=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_BI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_composite.c +DEP_CPP_MPEG4_C=\ + "..\..\..\TinyGL\include\GL\oscontext.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_C=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_form.c +DEP_CPP_MPEG4_F=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_F=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_geometry_2d.c +DEP_CPP_MPEG4_G=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_G=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_geometry_3d.c +DEP_CPP_MPEG4_GE=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_GE=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_geometry_ifs2d.c +DEP_CPP_MPEG4_GEO=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_GEO=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_geometry_ils2d.c +DEP_CPP_MPEG4_GEOM=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_GEOM=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_gradients.c +DEP_CPP_MPEG4_GR=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + +NODEP_CPP_MPEG4_GR=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_grouping.c +DEP_CPP_MPEG4_GRO=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_GRO=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_grouping.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_grouping_2d.c +DEP_CPP_MPEG4_GROU=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_GROU=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_grouping_3d.c +DEP_CPP_MPEG4_GROUP=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_GROUP=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_layer_2d.c +DEP_CPP_MPEG4_L=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_L=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_layer_3d.c +DEP_CPP_MPEG4_LA=\ + "..\..\..\TinyGL\include\GL\oscontext.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_LA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_layout.c +DEP_CPP_MPEG4_LAY=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_LAY=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_lighting.c +DEP_CPP_MPEG4_LI=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_LI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_path_layout.c +DEP_CPP_MPEG4_P=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_P=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_sensors.c +DEP_CPP_MPEG4_S=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_S=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_sound.c +DEP_CPP_MPEG4_SO=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_SO=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_text.c +DEP_CPP_MPEG4_T=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_T=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_textures.c +DEP_CPP_MPEG4_TE=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\texturing.h"\ + +NODEP_CPP_MPEG4_TE=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_timesensor.c +DEP_CPP_MPEG4_TI=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + +NODEP_CPP_MPEG4_TI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_viewport.c +DEP_CPP_MPEG4_VI=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_MPEG4_VI=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\navigate.c +DEP_CPP_NAVIG=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_NAVIG=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\nodes_stacks.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\offscreen_cache.c +DEP_CPP_OFFSC=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\offscreen_cache.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_OFFSC=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\offscreen_cache.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_base.c +DEP_CPP_SVG_B=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_SVG_B=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_font.c +DEP_CPP_SVG_F=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_SVG_F=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_geometry.c +DEP_CPP_SVG_G=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_SVG_G=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_grouping.c +DEP_CPP_SVG_GR=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\mpeg4_grouping.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\offscreen_cache.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_SVG_GR=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_media.c +DEP_CPP_SVG_M=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_SVG_M=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_paint_servers.c +DEP_CPP_SVG_PA=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_SVG_PA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_text.c +DEP_CPP_SVG_TE=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_SVG_TE=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\texturing.c +DEP_CPP_TEXTU=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + +NODEP_CPP_TEXTU=\ + "..\..\include\gpac\config.h"\ + "..\..\src\src\compositor\triscope_renoir\triscope_renoir.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\texturing.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\texturing_gl.c +DEP_CPP_TEXTUR=\ + "..\..\..\TinyGL\include\GL\gl.h"\ + "..\..\extra_lib\include\GLES\egl.h"\ + "..\..\extra_lib\include\GLES\gl.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\gl_inc.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_TEXTUR=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager.c +DEP_CPP_VISUA=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_VISUA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_2d.c +DEP_CPP_VISUAL=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_VISUAL=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_2d.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_2d_draw.c +DEP_CPP_VISUAL_=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_VISUAL_=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_3d.c +DEP_CPP_VISUAL_M=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\texturing.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_VISUAL_M=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_3d.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_3d_gl.c +DEP_CPP_VISUAL_MA=\ + "..\..\..\TinyGL\include\GL\gl.h"\ + "..\..\extra_lib\include\GLES\egl.h"\ + "..\..\extra_lib\include\GLES\gl.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\gl_inc.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_VISUAL_MA=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\x3d_geometry.c +DEP_CPP_X3D_G=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\compositor.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\camera.h"\ + "..\..\include\gpac\internal\compositor_dev.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\internal\mesh.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\modules\font.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\modules\video_out.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\nodes_svg.h"\ + "..\..\include\gpac\nodes_x3d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_svg.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\svg_types.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\src\compositor\drawable.h"\ + "..\..\src\compositor\nodes_stacks.h"\ + "..\..\src\compositor\visual_manager.h"\ + "..\..\src\compositor\visual_manager_2d.h"\ + "..\..\src\compositor\visual_manager_3d.h"\ + +NODEP_CPP_X3D_G=\ + "..\..\include\gpac\config.h"\ + +# End Source File +# End Group +# Begin Group "include" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\include\gpac\modules\audio_out.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\avparse.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\base_coding.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\bifs.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\bifsengine.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\bitstream.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\codec.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\compositor.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\config.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\config_file.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\config_static.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\constants.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\crypt.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\download.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\esi.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\events.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\font.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\ietf.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\ipmp.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\ismacryp.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\isomedia.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\laser.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\list.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\math.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\media_tools.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\mediaobject.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\module.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\mpeg4_odf.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\mpegts.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\network.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\nodes_mpeg4.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\nodes_svg.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\nodes_x3d.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\options.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\path2d.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\raster2d.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\renderer.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scene_manager.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scenegraph.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scenegraph_svg.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scenegraph_vrml.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\service.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\setup.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\svg_types.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\sync_layer.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\term_info.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\terminal.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\thread.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\token.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\tools.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\user.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\utf.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\video_out.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\xml.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\yuv.h +# End Source File +# End Group +# End Target +# End Project diff --git a/build/msevc4/libgpac_dll.vcp b/build/msevc4/libgpac_dll.vcp new file mode 100644 index 0000000..eb4ffc4 --- /dev/null +++ b/build/msevc4/libgpac_dll.vcp @@ -0,0 +1,106 @@ +# Microsoft eMbedded Visual Tools Project File - Name="libgpac_dll" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=libgpac_dll - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libgpac_dll.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libgpac_dll.vcn" CFG="libgpac_dll - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libgpac_dll - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "libgpac_dll - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libgpac_dll - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/libgpac_dll_rel" +# PROP Intermediate_Dir "obj/libgpac_dll_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "LIBGPAC_DLL_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/libgpac.dll" /libpath:"../../extra_lib/lib/arm_ppc03_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "libgpac_dll - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/libgpac_dll_deb" +# PROP Intermediate_Dir "obj/libgpac_dll_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "LIBGPAC_DLL_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/libgpac.dll" /libpath:"../../extra_lib/lib/arm_ppc03_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "libgpac_dll - Win32 (WCE ARMV4) Release" +# Name "libgpac_dll - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\src\libgpac_ce.def +# End Source File +# End Target +# End Project diff --git a/build/msevc4/mp3_in.vcp b/build/msevc4/mp3_in.vcp new file mode 100644 index 0000000..827c504 --- /dev/null +++ b/build/msevc4/mp3_in.vcp @@ -0,0 +1,138 @@ +# Microsoft eMbedded Visual Tools Project File - Name="mp3_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=mp3_in - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mp3_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mp3_in.vcn" CFG="mp3_in - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mp3_in - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "mp3_in - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mp3_in - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/mp3_in_rel" +# PROP Intermediate_Dir "obj/mp3_in_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "MP3_IN_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /I "../../extra_lib/include/mad" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "GPAC_HAS_MAD" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 libmad.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_mp3_in.dll" /libpath:"../../extra_lib/lib/arm_ppc03_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "mp3_in - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/mp3_in_deb" +# PROP Intermediate_Dir "obj/mp3_in_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "MP3_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/mad" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "GPAC_HAS_MAD" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 libmad.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_mp3_in.dll" /libpath:"../../extra_lib/lib/arm_ppc03_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "mp3_in - Win32 (WCE ARMV4) Release" +# Name "mp3_in - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\mp3_in\mad_dec.c +DEP_CPP_MAD_D=\ + "..\..\extra_lib\include\mad\mad.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\mp3_in\mp3_in.c +DEP_CPP_MP3_I=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\mp3_in\mp3_in.def +# End Source File +# End Target +# End Project diff --git a/build/msevc4/odf_dec.vcp b/build/msevc4/odf_dec.vcp new file mode 100644 index 0000000..65b852f --- /dev/null +++ b/build/msevc4/odf_dec.vcp @@ -0,0 +1,133 @@ +# Microsoft eMbedded Visual Tools Project File - Name="odf_dec" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=odf_dec - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "odf_dec.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "odf_dec.vcn" CFG="odf_dec - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "odf_dec - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "odf_dec - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "odf_dec - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/odf_dec_rel" +# PROP Intermediate_Dir "obj/odf_dec_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "ODF_DEC_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_odf_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "odf_dec - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/odf_dec_deb" +# PROP Intermediate_Dir "obj/odf_dec_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "ODF_DEC_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_odf_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "odf_dec - Win32 (WCE ARMV4) Release" +# Name "odf_dec - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\odf_dec\odf_dec.c +DEP_CPP_ODF_D=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\odf_dec\odf_dec.def +# End Source File +# End Target +# End Project diff --git a/build/msevc4/osmophone.vcp b/build/msevc4/osmophone.vcp new file mode 100644 index 0000000..7f0e9b1 --- /dev/null +++ b/build/msevc4/osmophone.vcp @@ -0,0 +1,183 @@ +# Microsoft eMbedded Visual Tools Project File - Name="osmophone" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Application" 0xa301 + +CFG=osmophone - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "osmophone.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "osmophone.vcn" CFG="osmophone - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "osmophone - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Application") +!MESSAGE "osmophone - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "osmophone - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/osmophone_rel" +# PROP Intermediate_Dir "obj/osmophone_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /YX /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"WinMainCRTStartup" /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 aygshell.lib commctrl.lib gx.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"WinMainCRTStartup" /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/osmophone.exe" /libpath:"../../extra_lib/lib/arm_ppc03_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "osmophone - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/osmophone_deb" +# PROP Intermediate_Dir "obj/osmophone_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /FR /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"WinMainCRTStartup" /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 aygshell.lib commctrl.lib gx.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"WinMainCRTStartup" /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_deb/osmophone.exe" /libpath:"../../extra_lib/lib/arm_ppc03_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "osmophone - Win32 (WCE ARMV4) Release" +# Name "osmophone - Win32 (WCE ARMV4) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\applications\osmophone\main.cpp +DEP_CPP_MAIN_=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\options.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + {$(INCLUDE)}"aygshell.h"\ + {$(INCLUDE)}"gx.h"\ + {$(INCLUDE)}"sipapi.h"\ + {$(INCLUDE)}"winuserm.h"\ + +NODEP_CPP_MAIN_=\ + "..\..\include\gpac\config.h"\ + "C:\Program Files\Windows CE Tools\wce420\POCKET PC 2003\Include\ARMV4\vibrate.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmophone\openfile.cpp +DEP_CPP_OPENF=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\internal\config.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + {$(INCLUDE)}"aygshell.h"\ + {$(INCLUDE)}"sipapi.h"\ + {$(INCLUDE)}"winuserm.h"\ + +NODEP_CPP_OPENF=\ + "..\..\include\gpac\config.h"\ + "C:\Program Files\Windows CE Tools\wce420\POCKET PC 2003\Include\ARMV4\vibrate.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmophone\osmophone.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\applications\osmophone\newres.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmophone\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=..\..\applications\osmophone\Osmo4.ico +# End Source File +# Begin Source File + +SOURCE=.\Osmo4.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/build/msevc4/rtp_in.vcp b/build/msevc4/rtp_in.vcp new file mode 100644 index 0000000..7adc46b --- /dev/null +++ b/build/msevc4/rtp_in.vcp @@ -0,0 +1,254 @@ +# Microsoft eMbedded Visual Tools Project File - Name="rtp_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=rtp_in - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rtp_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "rtp_in.vcn" CFG="rtp_in - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rtp_in - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "rtp_in - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rtp_in - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/rtp_in_rel" +# PROP Intermediate_Dir "obj/rtp_in_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "RTP_IN_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_rtp_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "rtp_in - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/rtp_in_deb" +# PROP Intermediate_Dir "obj/rtp_in_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "RTP_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_rtp_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "rtp_in - Win32 (WCE ARMV4) Release" +# Name "rtp_in - Win32 (WCE ARMV4) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_in.c +DEP_CPP_RTP_I=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_session.c +DEP_CPP_RTP_S=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_signaling.c +DEP_CPP_RTP_SI=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_stream.c +DEP_CPP_RTP_ST=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\sdp_fetch.c +DEP_CPP_SDP_F=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\sdp_load.c +DEP_CPP_SDP_L=\ + "..\..\include\gpac\base_coding.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\ietf.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\network.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\rtp_in\rtp_in.h"\ + +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_in.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msevc4/saf_in.vcp b/build/msevc4/saf_in.vcp new file mode 100644 index 0000000..7f6d370 --- /dev/null +++ b/build/msevc4/saf_in.vcp @@ -0,0 +1,136 @@ +# Microsoft eMbedded Visual Tools Project File - Name="saf_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=saf_in - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "saf_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "saf_in.vcn" CFG="saf_in - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "saf_in - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "saf_in - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "saf_in - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/saf_in_rel" +# PROP Intermediate_Dir "obj/saf_in_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "SAF_IN_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "SAF_IN_EXPORTS" /YX /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/gm_saf_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "saf_in - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/saf_in_deb" +# PROP Intermediate_Dir "obj/saf_in_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "SAF_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "SAF_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_deb/gm_saf_in.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "saf_in - Win32 (WCE ARMV4) Release" +# Name "saf_in - Win32 (WCE ARMV4) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\saf_in\saf_in.c +DEP_CPP_SAF_I=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\saf_in\saf_in.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msevc4/soft_rast.vcp b/build/msevc4/soft_rast.vcp new file mode 100644 index 0000000..4aa25b1 --- /dev/null +++ b/build/msevc4/soft_rast.vcp @@ -0,0 +1,234 @@ +# Microsoft eMbedded Visual Tools Project File - Name="soft_rast" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=soft_rast - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "soft_rast.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "soft_rast.vcn" CFG="soft_rast - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "soft_rast - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "soft_rast - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "soft_rast - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/soft_rast_rel" +# PROP Intermediate_Dir "obj/soft_rast_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "SOFT_RAST_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_soft_raster.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "soft_rast - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/soft_rast_deb" +# PROP Intermediate_Dir "obj/soft_rast_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "SOFT_RAST_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /FR /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_soft_raster.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "soft_rast - Win32 (WCE ARMV4) Release" +# Name "soft_rast - Win32 (WCE ARMV4) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\soft_raster\ftgrays.c +DEP_CPP_FTGRA=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\rast_soft.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_565.c +DEP_CPP_RASTE=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_argb.c +DEP_CPP_RASTER=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_load.c +DEP_CPP_RASTER_=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_rgb.c +DEP_CPP_RASTER_R=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\stencil.c +DEP_CPP_STENC=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\surface.c +DEP_CPP_SURFA=\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\raster2d.h"\ + "..\..\include\gpac\path2d.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\soft_raster\rast_soft.h"\ + +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\modules\soft_raster\rast_soft.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msevc4/svg_in.vcp b/build/msevc4/svg_in.vcp new file mode 100644 index 0000000..ac6d461 --- /dev/null +++ b/build/msevc4/svg_in.vcp @@ -0,0 +1,149 @@ +# Microsoft eMbedded Visual Tools Project File - Name="svg_in" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=svg_in - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "svg_in.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "svg_in.vcn" CFG="svg_in - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "svg_in - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "svg_in - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "svg_in - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/svg_in_rel" +# PROP Intermediate_Dir "obj/svg_in_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "SVG_IN_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /I "../../extra_lib/include/zlib" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "SVG_IN_EXPORTS" /YX /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 zlib.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_svg_in.dll" /libpath:"../../extra_lib/lib/arm_ppc03_rel" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "svg_in - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/svg_in_deb" +# PROP Intermediate_Dir "obj/svg_in_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "SVG_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /I "../../extra_lib/include/zlib" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "SVG_IN_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 zlib.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_svg_in.dll" /libpath:"../../extra_lib/lib/arm_ppc03_deb" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "svg_in - Win32 (WCE ARMV4) Release" +# Name "svg_in - Win32 (WCE ARMV4) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\svg_in\svg_in.c +DEP_CPP_SVG_I=\ + "..\..\extra_lib\include\zlib\zconf.h"\ + "..\..\extra_lib\include\zlib\zlib.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\color.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\events.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\ipmp.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\scene_manager.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\svg_in\svg_in.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msevc4/timedtext.vcp b/build/msevc4/timedtext.vcp new file mode 100644 index 0000000..42c49e3 --- /dev/null +++ b/build/msevc4/timedtext.vcp @@ -0,0 +1,158 @@ +# Microsoft eMbedded Visual Tools Project File - Name="timedtext" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=timedtext - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "timedtext.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "timedtext.vcn" CFG="timedtext - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "timedtext - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "timedtext - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "timedtext - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ttxt_rel" +# PROP Intermediate_Dir "obj/ttxt_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "TIMEDTEXT_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /out:"../../bin/arm_ppc03_rel/gm_timedtext.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "timedtext - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ttxt_deb" +# PROP Intermediate_Dir "obj/ttxt_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "TIMEDTEXT_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /out:"../../bin/arm_ppc03_deb/gm_timedtext.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "timedtext - Win32 (WCE ARMV4) Release" +# Name "timedtext - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\timedtext\timedtext.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\timedtext\timedtext_dec.c +DEP_CPP_TIMED=\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\crypt.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\internal\isomedia_dev.h"\ + "..\..\include\gpac\internal\terminal_dev.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\math.h"\ + "..\..\include\gpac\mediaobject.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\nodes_mpeg4.h"\ + "..\..\include\gpac\scenegraph.h"\ + "..\..\include\gpac\scenegraph_vrml.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\terminal.h"\ + "..\..\include\gpac\thread.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\include\gpac\user.h"\ + "..\..\include\gpac\utf.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\timedtext\timedtext_in.c +DEP_CPP_TIMEDT=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\download.h"\ + "..\..\include\gpac\isomedia.h"\ + "..\..\include\gpac\list.h"\ + "..\..\include\gpac\media_tools.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\modules\service.h"\ + "..\..\include\gpac\mpeg4_odf.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\sync_layer.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msevc4/wav_out.vcp b/build/msevc4/wav_out.vcp new file mode 100644 index 0000000..43214c9 --- /dev/null +++ b/build/msevc4/wav_out.vcp @@ -0,0 +1,119 @@ +# Microsoft eMbedded Visual Tools Project File - Name="wav_out" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=wav_out - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "wav_out.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "wav_out.vcn" CFG="wav_out - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "wav_out - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "wav_out - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "wav_out - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/wav_out_rel" +# PROP Intermediate_Dir "obj/wav_out_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "WAV_OUT_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/gm_wav_out.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "wav_out - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/wav_out_deb" +# PROP Intermediate_Dir "obj/wav_out_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "WAV_OUT_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_deb/gm_wav_out.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "wav_out - Win32 (WCE ARMV4) Release" +# Name "wav_out - Win32 (WCE ARMV4) Debug" +# Begin Source File + +SOURCE=..\..\modules\wav_out\wav_out.c +DEP_CPP_WAV_O=\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\audio_out.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\wav_out\wav_out.def +# End Source File +# End Target +# End Project diff --git a/build/msevc4/xvid_dec.vcp b/build/msevc4/xvid_dec.vcp new file mode 100644 index 0000000..83e7430 --- /dev/null +++ b/build/msevc4/xvid_dec.vcp @@ -0,0 +1,462 @@ +# Microsoft eMbedded Visual Tools Project File - Name="xvid_dec" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Dynamic-Link Library" 0xa302 + +CFG=xvid_dec - Win32 (WCE ARMV4) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "xvid_dec.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "xvid_dec.vcn" CFG="xvid_dec - Win32 (WCE ARMV4) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "xvid_dec - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE "xvid_dec - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 +CPP=clarm.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "xvid_dec - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/xvid_dec_rel" +# PROP Intermediate_Dir "obj/xvid_dec_rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /D "XVID_DEC_EXPORTS" /YX /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /Oxt /I "../../include" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_USRDLL" /M$(CECrtMT) /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_rel/gm_xvid_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "xvid_dec - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/xvid_dec_deb" +# PROP Intermediate_Dir "obj/xvid_dec_deb" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /r +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /D "XVID_DEC_EXPORTS" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "../../include" /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_USRDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 commctrl.lib coredll.lib /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 /nologo /base:"0x00100000" /stack:0x10000,0x1000 /entry:"_DllMainCRTStartup" /dll /debug /nodefaultlib:"$(CENoDefaultLib)" /out:"../../bin/arm_ppc03_deb/gm_xvid_dec.dll" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "xvid_dec - Win32 (WCE ARMV4) Release" +# Name "xvid_dec - Win32 (WCE ARMV4) Debug" +# Begin Group "xvid_wce" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\bitstream.cpp +DEP_CPP_BITST=\ + "..\..\modules\xvid_dec\xvid_wce\bitstream.h"\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant_matrix.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\bitstream.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\CodecAPI.cpp +DEP_CPP_CODEC=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\decoder.cpp +DEP_CPP_DECOD=\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\gmc.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mbprediction.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_align.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant_matrix.h"\ + "..\..\modules\xvid_dec\xvid_wce\reduced.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\decoder.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\font.cpp +DEP_CPP_FONT_=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\global.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\gmc.cpp +DEP_CPP_GMC_C=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\gmc.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\gmc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\idct.cpp +DEP_CPP_IDCT_=\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\image.cpp +DEP_CPP_IMAGE=\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_align.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\reduced.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\image.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\interpolate8x8.cpp +DEP_CPP_INTER=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mbcoding.cpp +DEP_CPP_MBCOD=\ + "..\..\modules\xvid_dec\xvid_wce\bitstream.h"\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mbprediction.cpp +DEP_CPP_MBPRE=\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mbprediction.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mbprediction.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mem_align.cpp +DEP_CPP_MEM_A=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_align.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mem_align.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mem_transfer.cpp +DEP_CPP_MEM_T=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\mem_transfer.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\portab.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\qpel.inl +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\qpel_tab.cpp +DEP_CPP_QPEL_=\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\qpel.inl"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\quant.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\quant_h263.cpp +DEP_CPP_QUANT=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\quant_matrix.cpp +DEP_CPP_QUANT_=\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant_matrix.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\quant_matrix.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\quant_mpeg.cpp +DEP_CPP_QUANT_M=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant_matrix.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\reduced.cpp +DEP_CPP_REDUC=\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\reduced.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\reduced.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\Rules.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\vlc_codes.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\xvid.cpp +DEP_CPP_XVID_=\ + "..\..\modules\xvid_dec\xvid_wce\decoder.h"\ + "..\..\modules\xvid_dec\xvid_wce\global.h"\ + "..\..\modules\xvid_dec\xvid_wce\image.h"\ + "..\..\modules\xvid_dec\xvid_wce\interpolate8x8.h"\ + "..\..\modules\xvid_dec\xvid_wce\mem_transfer.h"\ + "..\..\modules\xvid_dec\xvid_wce\portab.h"\ + "..\..\modules\xvid_dec\xvid_wce\quant.h"\ + "..\..\modules\xvid_dec\xvid_wce\reduced.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\vlc_codes.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\xvid.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_wce\xvid_ppc.asm + +!IF "$(CFG)" == "xvid_dec - Win32 (WCE ARMV4) Release" + +# Begin Custom Build +IntDir=.\obj/xvid_dec_rel +InputPath=..\..\modules\xvid_dec\xvid_wce\xvid_ppc.asm +InputName=xvid_ppc + +"$(IntDir)\$(InputName).obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + armasm -CPU StrongARM1 -o $(IntDir)\$(InputName).obj $(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "xvid_dec - Win32 (WCE ARMV4) Debug" + +# Begin Custom Build +IntDir=.\obj/xvid_dec_deb +InputPath=..\..\modules\xvid_dec\xvid_wce\xvid_ppc.asm +InputName=xvid_ppc + +"$(IntDir)\$(InputName).obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + armasm -CPU StrongARM1 -o $(IntDir)\$(InputName).obj $(InputPath) + +# End Custom Build + +!ENDIF + +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_dec.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_dec_wce.cpp +DEP_CPP_XVID_D=\ + "..\..\include\gpac\avparse.h"\ + "..\..\include\gpac\bitstream.h"\ + "..\..\include\gpac\config_file.h"\ + "..\..\include\gpac\constants.h"\ + "..\..\include\gpac\internal\config_static.h"\ + "..\..\include\gpac\module.h"\ + "..\..\include\gpac\modules\codec.h"\ + "..\..\include\gpac\setup.h"\ + "..\..\include\gpac\tools.h"\ + "..\..\modules\xvid_dec\xvid_wce\Rules.h"\ + "..\..\modules\xvid_dec\xvid_wce\xvid.h"\ + +# End Source File +# End Target +# End Project diff --git a/build/msvc6/GPAX.dsp b/build/msvc6/GPAX.dsp new file mode 100644 index 0000000..7b4231e --- /dev/null +++ b/build/msvc6/GPAX.dsp @@ -0,0 +1,172 @@ +# Microsoft Developer Studio Project File - Name="GPAX" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=GPAX - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "GPAX.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "GPAX.mak" CFG="GPAX - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "GPAX - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "GPAX - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "GPAX - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/w32_deb" +# PROP Intermediate_Dir "obj/w32_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x804 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 js32.lib zlib.lib winmm.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"../../bin/w32_deb/GPAX.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" +# Begin Custom Build - Performing registration +OutDir=.\obj/w32_deb +TargetPath=\CVS\gpac\bin\w32_deb\GPAX.dll +InputPath=\CVS\gpac\bin\w32_deb\GPAX.dll +SOURCE="$(InputPath)" + +"$(OutDir)\regsvr32.trg" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + regsvr32 /s /c "$(TargetPath)" + echo regsvr32 exec. time > "$(OutDir)\regsvr32.trg" + +# End Custom Build + +!ELSEIF "$(CFG)" == "GPAX - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "obj/w32_rel" +# PROP BASE Intermediate_Dir "obj/w32_rel" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/w32_rel" +# PROP Intermediate_Dir "obj/w32_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MD /W3 /Gm /ZI /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x804 /d "_DEBUG" +# ADD RSC /l 0x804 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 js32.lib zlib.lib winmm.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept /libpath:"../gpac/extra_lib/lib/w32_deb" +# ADD LINK32 /nologo /subsystem:windows /dll /debug /machine:I386 /out:"../../bin/w32_rel/GPAX.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_rel" +# Begin Custom Build - Performing registration +OutDir=.\obj/w32_rel +TargetPath=\CVS\gpac\bin\w32_rel\GPAX.dll +InputPath=\CVS\gpac\bin\w32_rel\GPAX.dll +SOURCE="$(InputPath)" + +"$(OutDir)\regsvr32.trg" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + regsvr32 /s /c "$(TargetPath)" + echo regsvr32 exec. time > "$(OutDir)\regsvr32.trg" + +# End Custom Build +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=copy ..\..\bin\w32_rel\GPAX.dll "C:\Program Files\GPAC" +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "GPAX - Win32 Debug" +# Name "GPAX - Win32 Release" +# Begin Source File + +SOURCE=..\..\applications\GPAX\gpax.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.def +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.idl +# ADD MTL /tlb ".\GPAX.tlb" +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.rc +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAX.rgs +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAXPlugin.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\GPAXPlugin.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\resource.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\StdAfx.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\GPAX\StdAfx.h +# End Source File +# End Target +# End Project +# Section GPAX : {00000000-0000-0000-0000-800000800000} +# 1:21:IDS_DOCSTRINGGPAXProp:105 +# 1:12:IDD_GPAXPROP:107 +# 1:12:IDR_GPAXPROP:106 +# 1:20:IDS_HELPFILEGPAXProp:104 +# 1:17:IDS_TITLEGPAXProp:103 +# End Section diff --git a/build/msvc6/Osmo4.dsp b/build/msvc6/Osmo4.dsp new file mode 100644 index 0000000..9968288 --- /dev/null +++ b/build/msvc6/Osmo4.dsp @@ -0,0 +1,217 @@ +# Microsoft Developer Studio Project File - Name="Osmo4" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=Osmo4 - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Osmo4.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Osmo4.mak" CFG="Osmo4 - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Osmo4 - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "Osmo4 - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Osmo4 - Win32 Release" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/osmo4_w32_rel" +# PROP Intermediate_Dir "obj/osmo4_w32_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" /d "_AFXDLL" +# ADD RSC /l 0x40c /d "NDEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386 +# ADD LINK32 /nologo /stack:0x800000 /subsystem:windows /machine:I386 /out:"../../bin/w32_rel/Osmo4.exe" + +!ELSEIF "$(CFG)" == "Osmo4 - Win32 Debug" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/osmo4_w32_deb" +# PROP Intermediate_Dir "obj/osmo4_w32_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /FR /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" /d "_AFXDLL" +# ADD RSC /l 0x40c /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:windows /debug /machine:I386 /out:"../../bin/w32_deb/Osmo4.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "Osmo4 - Win32 Release" +# Name "Osmo4 - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\AddressBar.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\FileProps.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\MainFrm.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\OpenUrl.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\Options.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\Osmo4.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\Osmo4.rc +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\Playlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\Sliders.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\StdAfx.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\AddressBar.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\FileProps.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\OpenUrl.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\Options.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\Osmo4.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\Playlist.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\Resource.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\Sliders.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\StdAfx.h +# End Source File +# End Group +# Begin Group "res" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\res\error.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\res\maintool.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\res\message.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\res\osmo4.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\res\pause.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\res\play.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\res\playlist.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_w32\res\stop.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/build/msvc6/V4Studio.dsp b/build/msvc6/V4Studio.dsp new file mode 100644 index 0000000..51dcc02 --- /dev/null +++ b/build/msvc6/V4Studio.dsp @@ -0,0 +1,505 @@ +# Microsoft Developer Studio Project File - Name="V4Studio" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=V4Studio - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "V4Studio.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "V4Studio.mak" CFG="V4Studio - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "V4Studio - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "V4Studio - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "V4Studio - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/v4studio_rel" +# PROP Intermediate_Dir "obj/v4studio_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "__WINDOWS__" /D "__WXMSW__" /D "__WXDEBUG__" /D WXDEBUG=1 /D "__WIN95__" /D "__WIN32__" /D WINVER=0x0400 /D "STRICT" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /i "E:\MesProjets\wxWindows\include" /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 wxbase26.lib wxmsw26_core.lib wxmsw26_adv.lib ws2_32.lib shell32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib ole32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib winmm.lib /nologo /subsystem:windows /machine:I386 /out:"../../bin/w32_rel/V4Studio.exe" +# SUBTRACT LINK32 /map + +!ELSEIF "$(CFG)" == "V4Studio - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/v4studio_deb" +# PROP Intermediate_Dir "obj/v4studio_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../M4Systems/SceneGraph" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "__WINDOWS__" /D "__WXMSW__" /D "__WXDEBUG__" /D WXDEBUG=1 /D "__WIN95__" /D "__WIN32__" /D WINVER=0x0400 /D "STRICT" /FR /FD /GZ /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /fo"rc\V4Studio.res" /i "C:\Projets\wxWindows\include" /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /machine:IX86 +# ADD LINK32 wxbase26d.lib wxmsw26d_core.lib wxmsw26d_adv.lib comctl32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib rpcrt4.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../../bin/w32_deb/V4Studio.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "V4Studio - Win32 Release" +# Name "V4Studio - Win32 Debug" +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\appearance.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\blank.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\bullseye.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\cdrom.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\circle.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\close.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\colortransform.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\computer.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\copy.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\cross.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\cut.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\delete.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\drive.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\file1.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\floppy.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\folder1.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\folder2.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\fs.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\group.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\hand.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\ifs2d.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\ils2d.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\image.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\layer2d.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\lg.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\lineproperties.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\magnif1.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\material2d.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\movie.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\new.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\noentry.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\open.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\orderedgroup.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\paste.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\paste_use.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\pbrush.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\pencil.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\pntleft.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\pntright.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\preview.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\print.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\query.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\RCa01312 +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\rect.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\redo.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\removble.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\resource_orig.xrc +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\rg.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\rightarr.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\roller.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\save.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\shape.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\sound.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\t2d.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\text.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\tm2d.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\undo.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\v4.bmp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\v4.ico +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\V4Studio.rc + +!IF "$(CFG)" == "V4Studio - Win32 Release" + +!ELSEIF "$(CFG)" == "V4Studio - Win32 Debug" + +# ADD BASE RSC /l 0x40c /i "\CVS\gpac\applications\v4studio\rc" /i "\gpac2\applications\v4studio\rc" +# SUBTRACT BASE RSC /i "C:\Projets\wxWindows\include" +# ADD RSC /l 0x40c /i "\CVS\gpac\applications\v4studio\rc" /i "\gpac2\applications\v4studio\rc" +# SUBTRACT RSC /i "C:\Projets\wxWindows\include" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\wx\msw\watch1.cur +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\rc\xlineproperties.bmp +# End Source File +# End Group +# Begin Group "UI" + +# PROP Default_Filter "" +# Begin Group "TimeLine" + +# PROP Default_Filter "*.h" +# Begin Source File + +SOURCE=..\..\applications\v4studio\safe_include.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4TimeLine\V4TimeLine.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4TimeLine\V4TimeLine.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4TimeLine\V4TimeLineCase.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4TimeLine\V4TimeLineCase.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4TimeLine\V4TimeLineElt.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4TimeLine\V4TimeLineElt.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4TimeLine\V4TimeLineHdr.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4TimeLine\V4TimeLineHdr.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4TimeLine\V4TimeLineLine.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4TimeLine\V4TimeLineLine.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4CommandPanel.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4CommandPanel.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4FieldList.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4FieldList.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4StudioFrame.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4StudioFrame.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4StudioTree.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4StudioTree.h +# End Source File +# End Group +# Begin Group "SceneManagment" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4Node.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4Node.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4NodePool.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4NodePool.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4NodePools.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4NodePools.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4SceneManager.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4SceneManager.h +# End Source File +# End Group +# Begin Group "GPACTerminal" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4Service.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4Service.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\wxGPACPanel.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\wxGPACPanel.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4StudioApp.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\v4studio\V4StudioApp.h +# End Source File +# End Target +# End Project diff --git a/build/msvc6/aac_in.dsp b/build/msvc6/aac_in.dsp new file mode 100644 index 0000000..2b5dba6 --- /dev/null +++ b/build/msvc6/aac_in.dsp @@ -0,0 +1,105 @@ +# Microsoft Developer Studio Project File - Name="aac_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=aac_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "aac_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "aac_in.mak" CFG="aac_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "aac_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "aac_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "aac_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/aac_in_rel" +# PROP Intermediate_Dir "obj/aac_in_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AAC_IN_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include/faad" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_FAAD" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 libfaad.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_aac_in.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "aac_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/aac_in_deb" +# PROP Intermediate_Dir "obj/aac_in_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AAC_IN_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include/faad" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_FAAD" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 libfaad.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_aac_in.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "aac_in - Win32 Release" +# Name "aac_in - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\aac_in\aac_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\aac_in\aac_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\aac_in\faad_dec.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/ac3_in.dsp b/build/msvc6/ac3_in.dsp new file mode 100644 index 0000000..3c62863 --- /dev/null +++ b/build/msvc6/ac3_in.dsp @@ -0,0 +1,103 @@ +# Microsoft Developer Studio Project File - Name="ac3_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ac3_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ac3_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ac3_in.mak" CFG="ac3_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ac3_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ac3_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ac3_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AC3_IN_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_LIBA52" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 liba52.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_ac3_in.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "ac3_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AC3_IN_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_LIBA52" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 liba52.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_ac3_in.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "ac3_in - Win32 Release" +# Name "ac3_in - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\ac3_in\ac3_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ac3_in\ac3_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ac3_in\liba52_dec.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/amr_dec.dsp b/build/msvc6/amr_dec.dsp new file mode 100644 index 0000000..51809cb --- /dev/null +++ b/build/msvc6/amr_dec.dsp @@ -0,0 +1,982 @@ +# Microsoft Developer Studio Project File - Name="amr_dec" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=amr_dec - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "amr_dec.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "amr_dec.mak" CFG="amr_dec - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "amr_dec - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "amr_dec - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "amr_dec - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/amr_dec_rel" +# PROP Intermediate_Dir "obj/amr_dec_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "amr_dec_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMS_IO" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /stack:0x800000 /dll /machine:I386 /out:"../../bin/w32_rel/gm_amr_dec.dll" +# SUBTRACT LINK32 /verbose /profile /incremental:yes /debug /nodefaultlib + +!ELSEIF "$(CFG)" == "amr_dec - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/amr_dec_deb" +# PROP Intermediate_Dir "obj/amr_dec_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "amr_dec_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMS_IO" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_amr_dec.dll" +# SUBTRACT LINK32 /verbose /profile /pdb:none /incremental:no + +!ENDIF + +# Begin Target + +# Name "amr_dec - Win32 Release" +# Name "amr_dec - Win32 Debug" +# Begin Group "amr_nb_dec" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\a_refl.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\a_refl.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\agc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\agc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\autocorr.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\autocorr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\az_lsp.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\az_lsp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\b_cn_cod.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\b_cn_cod.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\basic_op.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\basicop2.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bgnscd.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bgnscd.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bits2prm.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\bits2prm.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c1035pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c1035pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_11pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_11pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_9pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c2_9pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c3_14pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c3_14pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c4_17pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c4_17pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c8_31pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c8_31pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c_g_aver.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\c_g_aver.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_cor.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_cor.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_en.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\calc_en.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cbsearch.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cbsearch.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cl_ltp.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cl_ltp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cnst.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cnst_vad.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cod_amr.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cod_amr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\convolve.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\convolve.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\copy.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\copy.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cor_h.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\cor_h.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\count.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\count.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d1035pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d1035pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_11pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_11pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_9pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d2_9pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d3_14pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d3_14pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d4_17pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d4_17pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d8_31pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d8_31pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_c.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_c.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_p.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_gain_p.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_homing.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_homing.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf_3.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\d_plsf_5.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_amr.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_amr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_gain.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_gain.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag3.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag3.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag6.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dec_lag6.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_dec.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_enc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\dtx_enc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ec_gains.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\enc_lag6.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ex_ctrl.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ex_ctrl.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\frame.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_adapt.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_adapt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_code.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_code.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_pitch.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\g_pitch.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gain_q.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gain_q.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gc_pred.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gc_pred.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gmed_n.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\gmed_n.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\hp_max.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\hp_max.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lpc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lpc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lsf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\int_lsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inter_36.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inter_36.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inv_sqrt.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\inv_sqrt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lag_wind.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lag_wind.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\levinson.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\levinson.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lflg_upd.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\log2.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\log2.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lpc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lpc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsfwt.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsfwt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_avg.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_avg.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_az.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_az.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_lsf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\lsp_lsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\mac_32.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\mac_32.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\mode.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\n_proc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\n_proc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ol_ltp.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ol_ltp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\oper_32b.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\oper_32b.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\p_ol_wgh.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\p_ol_wgh.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ph_disp.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ph_disp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_fr.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_fr.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_ol.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pitch_ol.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\post_pro.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\post_pro.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pow2.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pow2.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_big.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_big.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_proc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pre_proc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pred_lt.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pred_lt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\preemph.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\preemph.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\prm2bits.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\prm2bits.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pstfilt.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\pstfilt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_c.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_c.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_p.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_gain_p.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf_3.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\q_plsf_5.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain475.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain475.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain795.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qgain795.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qua_gain.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\qua_gain.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\r_fft.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\reorder.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\reorder.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\residu.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\residu.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\s10_8pf.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\s10_8pf.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_sign.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_sign.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_zero.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\set_zero.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sid_sync.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sid_sync.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_dec.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_enc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sp_enc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spreproc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spreproc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spstproc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\spstproc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sqrt_l.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\sqrt_l.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\strfunc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\strfunc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\syn_filt.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\syn_filt.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ton_stab.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\ton_stab.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\typedef.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\typedefs.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad1.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad1.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad2.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vad2.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vadname.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\vadname.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\weight_a.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\weight_a.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_dec.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_nb\enc_lag3.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/amr_float_dec.dsp b/build/msvc6/amr_float_dec.dsp new file mode 100644 index 0000000..67a2164 --- /dev/null +++ b/build/msvc6/amr_float_dec.dsp @@ -0,0 +1,304 @@ +# Microsoft Developer Studio Project File - Name="amr_float_dec" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=amr_float_dec - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "amr_float_dec.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "amr_float_dec.mak" CFG="amr_float_dec - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "amr_float_dec - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "amr_float_dec - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "amr_float_dec - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/amr_float_dec_rel" +# PROP Intermediate_Dir "obj/amr_float_dec_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AMR_FLOAT_DEC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_AMR_FT" /D "GPAC_HAS_AMR_FT_WB" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_amr_float_dec.dll" + +!ELSEIF "$(CFG)" == "amr_float_dec - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/amr_float_dec_deb" +# PROP Intermediate_Dir "obj/amr_float_dec_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "AMR_FLOAT_DEC_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_AMR_FT" /D "GPAC_HAS_AMR_FT_WB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_amr_float_dec.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "amr_float_dec - Win32 Release" +# Name "amr_float_dec - Win32 Debug" +# Begin Group "amr_nb_ft" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\interf_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\interf_dec.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\interf_enc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\interf_enc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\interf_rom.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\rom_dec.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\rom_enc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\sp_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\sp_dec.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\sp_enc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\sp_enc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_nb_ft\typedef.h +# End Source File +# End Group +# Begin Group "amr_wb_ft" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_acelp.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_acelp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_dtx.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_dtx.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_gain.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_gain.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_if.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_if.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_lpc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_lpc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_main.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_main.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_rom.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_util.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\dec_util.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_acelp.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_acelp.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_dtx.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_dtx.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_gain.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_gain.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_if.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_if.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_lpc.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_lpc.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_main.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_main.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_rom.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_util.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\enc_util.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\if_rom.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\if_rom.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\typedef.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_wb_ft\typedefs.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_float_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_float_dec\amr_float_dec.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\amr_dec\amr_in.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/bifs_dec.dsp b/build/msvc6/bifs_dec.dsp new file mode 100644 index 0000000..eb0a7e7 --- /dev/null +++ b/build/msvc6/bifs_dec.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="bifs_dec" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=bifs_dec - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "bifs_dec.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "bifs_dec.mak" CFG="bifs_dec - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "bifs_dec - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "bifs_dec - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "bifs_dec - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/bifs_dec_rel" +# PROP Intermediate_Dir "obj/bifs_dec_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "BIFS_DEC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_bifs_dec.dll" + +!ELSEIF "$(CFG)" == "bifs_dec - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/bifs_dec_deb" +# PROP Intermediate_Dir "obj/bifs_dec_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "BIFS_DEC_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_bifs_dec.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "bifs_dec - Win32 Release" +# Name "bifs_dec - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\bifs_dec\bifs_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\bifs_dec\bifs_dec.def +# End Source File +# End Target +# End Project diff --git a/build/msvc6/ctx_load.dsp b/build/msvc6/ctx_load.dsp new file mode 100644 index 0000000..ee240f4 --- /dev/null +++ b/build/msvc6/ctx_load.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="ctx_load" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ctx_load - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ctx_load.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ctx_load.mak" CFG="ctx_load - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ctx_load - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ctx_load - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ctx_load - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ctx_load_rel" +# PROP Intermediate_Dir "obj/ctx_load_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CTX_LOAD_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_ctx_load.dll" + +!ELSEIF "$(CFG)" == "ctx_load - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ctx_load_deb" +# PROP Intermediate_Dir "obj/ctx_load_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CTX_LOAD_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_ctx_load.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "ctx_load - Win32 Release" +# Name "ctx_load - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\ctx_load\ctx_load.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ctx_load\ctx_load.def +# End Source File +# End Target +# End Project diff --git a/build/msvc6/dummy_in.dsp b/build/msvc6/dummy_in.dsp new file mode 100644 index 0000000..28386a1 --- /dev/null +++ b/build/msvc6/dummy_in.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="dummy_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=dummy_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dummy_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dummy_in.mak" CFG="dummy_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dummy_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "dummy_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dummy_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/dymmy_in_rel" +# PROP Intermediate_Dir "obj/dymmy_in_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "dummy_in_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_dummy_in.dll" + +!ELSEIF "$(CFG)" == "dummy_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/dymmy_in_deb" +# PROP Intermediate_Dir "obj/dymmy_in_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "dummy_in_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_dummy_in.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "dummy_in - Win32 Release" +# Name "dummy_in - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\dummy_in\dummy_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dummy_in\dummy_in.def +# End Source File +# End Target +# End Project diff --git a/build/msvc6/dx_hw.dsp b/build/msvc6/dx_hw.dsp new file mode 100644 index 0000000..4741dc6 --- /dev/null +++ b/build/msvc6/dx_hw.dsp @@ -0,0 +1,143 @@ +# Microsoft Developer Studio Project File - Name="dx_hw" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=dx_hw - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dx_hw.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dx_hw.mak" CFG="dx_hw - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dx_hw - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "dx_hw - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dx_hw - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/dx_hw_rel" +# PROP Intermediate_Dir "obj/dx_hw_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DX_HW_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAVE_CONFIG_H" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 opengl32.lib glu32.lib dsound.lib dxguid.lib ddraw.lib ole32.lib user32.lib gdi32.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_dx_hw.dll" + +!ELSEIF "$(CFG)" == "dx_hw - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/dx_hw_deb" +# PROP Intermediate_Dir "obj/dx_hw_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DX_HW_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAVE_CONFIG_H" /FR /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 msimg32.lib opengl32.lib dsound.lib dxguid.lib ddraw.lib ole32.lib user32.lib gdi32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_dx_hw.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "dx_hw - Win32 Release" +# Name "dx_hw - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\dx_hw\collide.cur +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\res\collide.cur +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\copy_pixels.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\dx_2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\dx_audio.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\dx_hw.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\dx_hw.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\dx_hw.rc +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\dx_video.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\dx_window.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\hand.cur +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\res\hand.cur +# End Source File +# Begin Source File + +SOURCE=..\..\modules\dx_hw\resource.h +# End Source File +# End Target +# End Project diff --git a/build/msvc6/ffmpeg_in.dsp b/build/msvc6/ffmpeg_in.dsp new file mode 100644 index 0000000..a05c3be --- /dev/null +++ b/build/msvc6/ffmpeg_in.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ffmpeg_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ffmpeg_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ffmpeg_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ffmpeg_in.mak" CFG="ffmpeg_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ffmpeg_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ffmpeg_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ffmpeg_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ffmpeg_in_rel" +# PROP Intermediate_Dir "obj/ffmpeg_in_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FFMPEG_IN_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 avcodec-51.lib avformat-51.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_ffmpeg_in.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "ffmpeg_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ffmpeg_in_deb" +# PROP Intermediate_Dir "obj/ffmpeg_in_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FFMPEG_IN_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 avcodec-51.lib avformat-51.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_ffmpeg_in.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" +# SUBTRACT LINK32 /incremental:no /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "ffmpeg_in - Win32 Release" +# Name "ffmpeg_in - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\ffmpeg_in\ffmpeg_decode.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ffmpeg_in\ffmpeg_demux.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ffmpeg_in\ffmpeg_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ffmpeg_in\ffmpeg_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ffmpeg_in\ffmpeg_load.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/ft_font.dsp b/build/msvc6/ft_font.dsp new file mode 100644 index 0000000..2df9bbe --- /dev/null +++ b/build/msvc6/ft_font.dsp @@ -0,0 +1,105 @@ +# Microsoft Developer Studio Project File - Name="ft_font" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ft_font - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ft_font.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ft_font.mak" CFG="ft_font - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ft_font - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ft_font - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ft_font - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ft_font_rel" +# PROP Intermediate_Dir "obj/ft_font_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FT_FONT_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include/freetype" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 freetype.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_ft_font.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "ft_font - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ft_font_deb" +# PROP Intermediate_Dir "obj/ft_font_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FT_FONT_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include/freetype" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 freetype.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_ft_font.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "ft_font - Win32 Release" +# Name "ft_font - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\ft_font\ft_font.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ft_font\ft_font.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ft_font\ft_font.h +# End Source File +# End Target +# End Project diff --git a/build/msvc6/gdip_raster.dsp b/build/msvc6/gdip_raster.dsp new file mode 100644 index 0000000..54d3da2 --- /dev/null +++ b/build/msvc6/gdip_raster.dsp @@ -0,0 +1,115 @@ +# Microsoft Developer Studio Project File - Name="gdip_raster" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=gdip_raster - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "gdip_raster.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "gdip_raster.mak" CFG="gdip_raster - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "gdip_raster - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "gdip_raster - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "gdip_raster - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/gdip_rel" +# PROP Intermediate_Dir "obj/gdip_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GDIP_REND_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 gdiplus.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_gdip_raster.dll" + +!ELSEIF "$(CFG)" == "gdip_raster - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/gdip_deb" +# PROP Intermediate_Dir "obj/gdip_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GDIP_REND_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 gdiplus.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_gdip_raster.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "gdip_raster - Win32 Release" +# Name "gdip_raster - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\gdip_raster\gdip_font.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\modules\gdip_raster\gdip_grad.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\modules\gdip_raster\gdip_priv.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\gdip_raster\gdip_rend.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\modules\gdip_raster\gdip_rend.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\gdip_raster\gdip_texture.cpp +# End Source File +# End Target +# End Project diff --git a/build/msvc6/gpac.dsw b/build/msvc6/gpac.dsw new file mode 100644 index 0000000..88c4dc9 --- /dev/null +++ b/build/msvc6/gpac.dsw @@ -0,0 +1,695 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "GPAX"=.\GPAX.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "Osmo4"=.\Osmo4.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency + Begin Project Dependency + Project_Dep_Name aac_in + End Project Dependency + Begin Project Dependency + Project_Dep_Name bifs_dec + End Project Dependency + Begin Project Dependency + Project_Dep_Name ctx_load + End Project Dependency + Begin Project Dependency + Project_Dep_Name dummy_in + End Project Dependency + Begin Project Dependency + Project_Dep_Name dx_hw + End Project Dependency + Begin Project Dependency + Project_Dep_Name ft_font + End Project Dependency + Begin Project Dependency + Project_Dep_Name gdip_raster + End Project Dependency + Begin Project Dependency + Project_Dep_Name img_in + End Project Dependency + Begin Project Dependency + Project_Dep_Name isom_in + End Project Dependency + Begin Project Dependency + Project_Dep_Name mp3_in + End Project Dependency + Begin Project Dependency + Project_Dep_Name odf_dec + End Project Dependency + Begin Project Dependency + Project_Dep_Name rtp_in + End Project Dependency + Begin Project Dependency + Project_Dep_Name soft_raster + End Project Dependency + Begin Project Dependency + Project_Dep_Name wav_out + End Project Dependency + Begin Project Dependency + Project_Dep_Name xvid_dec + End Project Dependency + Begin Project Dependency + Project_Dep_Name svg_in + End Project Dependency + Begin Project Dependency + Project_Dep_Name mpegts_in + End Project Dependency + Begin Project Dependency + Project_Dep_Name saf_in + End Project Dependency + Begin Project Dependency + Project_Dep_Name timedtext + End Project Dependency + Begin Project Dependency + Project_Dep_Name ffmpeg_in + End Project Dependency + Begin Project Dependency + Project_Dep_Name ismacryp + End Project Dependency + Begin Project Dependency + Project_Dep_Name laser_dec + End Project Dependency +}}} + +############################################################################### + +Project: "aac_in"=.\aac_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ac3_in"=.\ac3_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "amr_dec"=.\amr_dec.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "amr_float_dec"=.\amr_float_dec.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "bifs_dec"=.\bifs_dec.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "broadcaster"=..\..\applications\testapps\broadcaster\broadcaster.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ctx_load"=.\ctx_load.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "dummy_in"=.\dummy_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "dx_hw"=.\dx_hw.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ffmpeg_in"=.\ffmpeg_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ft_font"=.\ft_font.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "gdip_raster"=.\gdip_raster.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "gpac_js"=.\gpac_js.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "img_in"=.\img_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ismacryp"=.\ismacryp.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "isom_in"=.\isom_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "laser_dec"=.\laser_dec.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "libgpac"=.\libgpac.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "libgpac_dll"=.\libgpac_dll.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac + End Project Dependency +}}} + +############################################################################### + +Project: "mp3_in"=.\mp3_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "mp42ts"=..\..\applications\testapps\mp42ts\mp42ts.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "mp4_streamer"=..\..\applications\testapps\mp4_streamer\mp4_streamer.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "mp4box"=.\mp4box.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac + End Project Dependency +}}} + +############################################################################### + +Project: "mp4client"=.\mp4client.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "mpegts_in"=.\mpegts_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "odf_dec"=.\odf_dec.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "ogg"=.\ogg.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "osmozilla"=.\osmozilla.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "raw_out"=.\raw_out.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "rtp_in"=.\rtp_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "saf_in"=.\saf_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "sdl_out"=.\sdl_out.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "soft_raster"=.\soft_raster.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "svg2bifs"=..\..\applications\testapps\svg2bifs\svg2bifs.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac + End Project Dependency +}}} + +############################################################################### + +Project: "svg_in"=.\svg_in.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "timedtext"=.\timedtext.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "wav_out"=.\wav_out.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "wxOsmo4"=.\wxOsmo4.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Project: "xvid_dec"=.\xvid_dec.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libgpac_dll + End Project Dependency +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/build/msvc6/gpac_js.dsp b/build/msvc6/gpac_js.dsp new file mode 100644 index 0000000..47778d0 --- /dev/null +++ b/build/msvc6/gpac_js.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="gpac_js" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=gpac_js - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "gpac_js.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "gpac_js.mak" CFG="gpac_js - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "gpac_js - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "gpac_js - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "gpac_js - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "gpac_js___Win32_Release" +# PROP BASE Intermediate_Dir "gpac_js___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/gpac_js_rel" +# PROP Intermediate_Dir "obj/gpac_js_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_JS_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include/js" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_SPIDERMONKEY" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_gpac_js.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "gpac_js - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "gpac_js___Win32_Debug" +# PROP BASE Intermediate_Dir "gpac_js___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/gpac_js_deb" +# PROP Intermediate_Dir "obj/gpac_js_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_JS_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include/js" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_SPIDERMONKEY" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_gpac_js.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "gpac_js - Win32 Release" +# Name "gpac_js - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\gpac_js\gpac_js.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\gpac_js\gpac_js.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msvc6/img_in.dsp b/build/msvc6/img_in.dsp new file mode 100644 index 0000000..2a4799e --- /dev/null +++ b/build/msvc6/img_in.dsp @@ -0,0 +1,125 @@ +# Microsoft Developer Studio Project File - Name="img_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=img_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "img_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "img_in.mak" CFG="img_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "img_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "img_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "img_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/img_in_rel" +# PROP Intermediate_Dir "obj/img_in_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IMG_IN_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include/openjpeg" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_JP2" /D "OPJ_STATIC" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 LibOpenJPEG.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_img_in.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "img_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/img_in_deb" +# PROP Intermediate_Dir "obj/img_in_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IMG_IN_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include/openjpeg" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_JP2" /D "OPJ_STATIC" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 LibOpenJPEGd.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_img_in.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "img_in - Win32 Release" +# Name "img_in - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\img_in\bmp_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\img_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\jp2_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\jpeg_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\img_in\png_dec.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/ismacryp.dsp b/build/msvc6/ismacryp.dsp new file mode 100644 index 0000000..fbc91d4 --- /dev/null +++ b/build/msvc6/ismacryp.dsp @@ -0,0 +1,100 @@ +# Microsoft Developer Studio Project File - Name="ismacryp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ismacryp - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ismacryp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ismacryp.mak" CFG="ismacryp - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ismacryp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ismacryp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ismacryp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ismacryp___Win32_Release" +# PROP BASE Intermediate_Dir "ismacryp___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ismacryp_rel" +# PROP Intermediate_Dir "obj/ismacryp_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISMACRYP_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_ismacryp.dll" + +!ELSEIF "$(CFG)" == "ismacryp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ismacryp___Win32_Debug" +# PROP BASE Intermediate_Dir "ismacryp___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ismacryp_deb" +# PROP Intermediate_Dir "obj/ismacryp_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISMACRYP_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_ismacryp.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "ismacryp - Win32 Release" +# Name "ismacryp - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\ismacryp\ismacryp.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ismacryp\ismacryp.def +# End Source File +# End Target +# End Project diff --git a/build/msvc6/isom_in.dsp b/build/msvc6/isom_in.dsp new file mode 100644 index 0000000..d367b82 --- /dev/null +++ b/build/msvc6/isom_in.dsp @@ -0,0 +1,116 @@ +# Microsoft Developer Studio Project File - Name="isom_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=isom_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "isom_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "isom_in.mak" CFG="isom_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "isom_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "isom_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "isom_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/isom_in_rel" +# PROP Intermediate_Dir "obj/isom_in_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MP4_IO_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# SUBTRACT CPP /Fr +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_isom_in.dll" + +!ELSEIF "$(CFG)" == "isom_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/isom_in_deb" +# PROP Intermediate_Dir "obj/isom_in_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MP4_IO_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_LIBA52" /FR /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_isom_in.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "isom_in - Win32 Release" +# Name "isom_in - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\isom_in\cache.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\isom_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\isom_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\load.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\read.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\isom_in\read_ch.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/laser_dec.dsp b/build/msvc6/laser_dec.dsp new file mode 100644 index 0000000..23c1505 --- /dev/null +++ b/build/msvc6/laser_dec.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="laser_dec" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=laser_dec - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "laser_dec.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "laser_dec.mak" CFG="laser_dec - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "laser_dec - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "laser_dec - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "laser_dec - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/laser_dec_rel" +# PROP Intermediate_Dir "obj/laser_dec_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LASER_DEC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_laser_dec.dll" + +!ELSEIF "$(CFG)" == "laser_dec - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/laser_dec_deb" +# PROP Intermediate_Dir "obj/laser_dec_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LASER_DEC_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_laser_dec.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "laser_dec - Win32 Release" +# Name "laser_dec - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\laser_dec\laser_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\laser_dec\laser_dec.def +# End Source File +# End Target +# End Project diff --git a/build/msvc6/libgpac.dsp b/build/msvc6/libgpac.dsp new file mode 100644 index 0000000..7b98bc1 --- /dev/null +++ b/build/msvc6/libgpac.dsp @@ -0,0 +1,1404 @@ +# Microsoft Developer Studio Project File - Name="libgpac" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=libgpac - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libgpac.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libgpac.mak" CFG="libgpac - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libgpac - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libgpac - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libgpac - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/libgpac_rel" +# PROP Intermediate_Dir "obj/libgpac_rel" +# PROP Target_Dir "" +MTL=midl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O1 /I "../../include" /I "../../extra_lib/include/zlib" /I "../../extra_lib/include/js" /I "../../extra_lib/include/jpeg" /I "../../extra_lib/include/png" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "GPAC_HAVE_CONFIG_H" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"../../bin/w32_rel/libgpac_static.lib" + +!ELSEIF "$(CFG)" == "libgpac - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/libgpac_deb" +# PROP Intermediate_Dir "obj/libgpac_deb" +# PROP Target_Dir "" +MTL=midl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /GX /ZI /Od /I "../../include" /I "../../extra_lib/include/zlib" /I "../../extra_lib/include/js" /I "../../extra_lib/include/jpeg" /I "../../extra_lib/include/png" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "GPAC_HAVE_CONFIG_H" /FD /GZ /c +# SUBTRACT CPP /Fr /YX +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"../../bin/w32_deb/libgpac_static.lib" + +!ENDIF + +# Begin Target + +# Name "libgpac - Win32 Release" +# Name "libgpac - Win32 Debug" +# Begin Group "utils" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\utils\base_encoding.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\bitstream.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\color.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\configfile.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\downloader.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\error.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\list.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\math.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\module.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\module_wrap.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_divers.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_module.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_net.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\os_thread.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\path2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\path2d_stroker.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\token.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\uni_bidi.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\url.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\utf.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\utils\xml_parser.c +# End Source File +# End Group +# Begin Group "odf" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\odf\desc_private.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\descriptors.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\ipmpx_code.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\ipmpx_dump.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\ipmpx_parse.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\oci_codec.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_code.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_codec.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_command.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\odf_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_dump.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\odf_parse.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\qos.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\odf\slc.c +# End Source File +# End Group +# Begin Group "bifs" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\bifs\arith_decoder.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\bifs_codec.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\bifs_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\bifs_node_tables.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\bifs_tables.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\com_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\com_enc.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\conditional.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\field_decode.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\field_encode.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\memory_decoder.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\predictive_mffield.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\quant.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\quantize.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\script.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\script_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\script_enc.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\bifs\unquantize.c +# End Source File +# End Group +# Begin Group "isomedia" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\isomedia\avc_ext.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_3gpp.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_apple.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_base.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_isma.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_code_meta.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_dump.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\box_funcs.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\data_map.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\hint_track.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\hinting.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isma_sample.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_intern.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_read.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_store.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\isom_write.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\isomedia_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\media.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\media_odf.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\meta.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\movie_fragments.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\sample_descs.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\stbl_read.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\stbl_write.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\track.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\isomedia\tx3g.c +# End Source File +# End Group +# Begin Group "ietf" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\include\gpac\internal\ietf_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtcp.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_depacketizer.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_packetizer.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_pck_3gpp.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_pck_mpeg12.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtp_pck_mpeg4.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_command.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_common.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_response.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\rtsp_session.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\ietf\sdp.c +# End Source File +# End Group +# Begin Group "scenegraph" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\scenegraph\base_scenegraph.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\commands.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\dom_events.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\dom_smjs.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\mpeg4_animators.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\mpeg4_nodes.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\mpeg4_valuator.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\scenegraph_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\smil_anim.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\smil_timing.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_attributes.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_properties.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_smjs.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\svg_types.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_interpolators.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_proto.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_route.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_script.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_smjs.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\vrml_tools.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\x3d_nodes.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\xbl_process.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scenegraph\xml_ns.c +# End Source File +# End Group +# Begin Group "media_tools" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\media_tools\av_parsers.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\avilib.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\avilib.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\gpac_ogg.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\img.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\ismacryp.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\isom_hinter.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\isom_tools.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\media_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\media_export.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\media_import.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\mpeg2_ps.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\mpeg2_ps.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\mpegts.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\ogg.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\saf.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\text_import.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\media_tools\vobsub.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\vobsub.h +# End Source File +# End Group +# Begin Group "scene_manager" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\scene_manager\encode_cbk.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\encode_isom.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_bt.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_isom.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_qt.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_svg.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\loader_xmt.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\scene_dump.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\scene_manager.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\scene_stats.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\swf_bifs.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\swf_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\swf_parse.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\scene_manager\text_to_bifs.c +# End Source File +# End Group +# Begin Group "mcrypt" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\mcrypt\cbc.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\cfb.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\crypt_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ctr.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\des.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ecb.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\g_crypt.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ncfb.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\nofb.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\ofb.c +# End Source File +# Begin Source File + +SOURCE="..\..\src\mcrypt\rijndael-128.c" +# End Source File +# Begin Source File + +SOURCE="..\..\src\mcrypt\rijndael-192.c" +# End Source File +# Begin Source File + +SOURCE="..\..\src\mcrypt\rijndael-256.c" +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\sha1.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\stream.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\mcrypt\tripledes.c +# End Source File +# End Group +# Begin Group "terminal" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\terminal\channel.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\clock.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\decoder.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\inline.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\input_sensor.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\input_sensor.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_control.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_control.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_manager.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_memory.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_memory.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_object.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\media_sensor.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\network_service.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\object_browser.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\object_manager.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\svg_external.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\term_node_init.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\terminal\terminal.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\terminal_dev.h +# End Source File +# End Group +# Begin Group "compositor" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\src\compositor\audio_input.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\audio_mixer.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\audio_render.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\bindable.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\camera.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\camera.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\compositor.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\compositor_2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\compositor_3d.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\compositor_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\compositor_node_init.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\drawable.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\drawable.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\events.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\font_engine.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\gl_inc.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\hardcoded_protos.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mesh.c +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\mesh.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mesh_collide.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mesh_tesselate.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_animstream.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_audio.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_background.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_background2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_bitmap.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_composite.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_form.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_geometry_2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_geometry_3d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_geometry_ifs2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_geometry_ils2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_gradients.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_grouping.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_grouping.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_grouping_2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_grouping_3d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_layer_2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_layer_3d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_layout.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_lighting.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_path_layout.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_sensors.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_sound.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_text.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_textures.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_timesensor.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\mpeg4_viewport.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\navigate.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\nodes_stacks.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\offscreen_cache.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\offscreen_cache.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_base.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_font.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_geometry.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_grouping.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_media.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_paint_servers.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\svg_text.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\texturing.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\texturing.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\texturing_gl.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_2d.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_2d_draw.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_3d.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_3d.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\visual_manager_3d_gl.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\compositor\x3d_geometry.c +# End Source File +# End Group +# Begin Group "laser" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\include\gpac\internal\laser_dev.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\laser\lsr_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\laser\lsr_enc.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\laser\lsr_tables.c +# End Source File +# End Group +# Begin Group "include" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\include\gpac\modules\audio_out.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\avparse.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\base_coding.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\bifs.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\bifsengine.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\bitstream.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\codec.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\color.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\compositor.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\config_file.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\internal\config_static.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\constants.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\crypt.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\download.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\esi.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\events.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\font.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\ietf.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\ipmp.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\ismacryp.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\isomedia.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\js_usr.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\laser.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\list.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\math.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\media_tools.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\mediaobject.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\module.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\mpeg4_odf.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\mpegts.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\network.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\nodes_mpeg4.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\nodes_svg.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\nodes_x3d.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\nodes_xbl.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\options.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\path2d.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\raster2d.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scene_manager.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scenegraph.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scenegraph_svg.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\scenegraph_vrml.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\service.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\setup.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\svg_types.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\sync_layer.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\term_ext.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\term_info.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\terminal.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\thread.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\token.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\tools.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\user.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\utf.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\modules\video_out.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\xml.h +# End Source File +# Begin Source File + +SOURCE=..\..\include\gpac\yuv.h +# End Source File +# End Group +# End Target +# End Project diff --git a/build/msvc6/libgpac_dll.dsp b/build/msvc6/libgpac_dll.dsp new file mode 100644 index 0000000..c5d9b52 --- /dev/null +++ b/build/msvc6/libgpac_dll.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="libgpac_dll" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=libgpac_dll - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libgpac_dll.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libgpac_dll.mak" CFG="libgpac_dll - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libgpac_dll - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "libgpac_dll - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libgpac_dll - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "libgpac_dll___Win32_Release" +# PROP BASE Intermediate_Dir "libgpac_dll___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/libgpac_dll_rel" +# PROP Intermediate_Dir "obj/libgpac_dll_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBGPAC_DLL_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/libgpac.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "libgpac_dll - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "libgpac_dll___Win32_Debug" +# PROP BASE Intermediate_Dir "libgpac_dll___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/libgpac_dll_deb" +# PROP Intermediate_Dir "obj/libgpac_dll_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBGPAC_DLL_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/libgpac.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "libgpac_dll - Win32 Release" +# Name "libgpac_dll - Win32 Debug" +# Begin Source File + +SOURCE=..\..\src\libgpac.def +# End Source File +# End Target +# End Project diff --git a/build/msvc6/mp3_in.dsp b/build/msvc6/mp3_in.dsp new file mode 100644 index 0000000..4c5bd36 --- /dev/null +++ b/build/msvc6/mp3_in.dsp @@ -0,0 +1,103 @@ +# Microsoft Developer Studio Project File - Name="mp3_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mp3_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mp3_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mp3_in.mak" CFG="mp3_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mp3_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mp3_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mp3_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/mp3_in_rel" +# PROP Intermediate_Dir "obj/mp3_in_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MP3_IN_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include/mad" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_MAD" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 libmad.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_mp3_in.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "mp3_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/mp3_in_deb" +# PROP Intermediate_Dir "obj/mp3_in_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MP3_IN_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include/mad" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_MAD" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 libmad.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_mp3_in.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "mp3_in - Win32 Release" +# Name "mp3_in - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\mp3_in\mad_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\mp3_in\mp3_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\mp3_in\mp3_in.def +# End Source File +# End Target +# End Project diff --git a/build/msvc6/mp42avi.dsp b/build/msvc6/mp42avi.dsp new file mode 100644 index 0000000..ee41683 --- /dev/null +++ b/build/msvc6/mp42avi.dsp @@ -0,0 +1,91 @@ +# Microsoft Developer Studio Project File - Name="mp42avi" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=mp42avi - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mp42avi.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mp42avi.mak" CFG="mp42avi - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mp42avi - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "mp42avi - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mp42avi - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/mp42avi_rel" +# PROP Intermediate_Dir "obj/mp42avi_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 /nologo /subsystem:console /machine:I386 /out:"../../bin/w32_rel/MP42Avi.exe" +# SUBTRACT LINK32 /profile /pdb:none /incremental:yes /map /debug /nodefaultlib /force + +!ELSEIF "$(CFG)" == "mp42avi - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/mp42avi_deb" +# PROP Intermediate_Dir "obj/mp42avi_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:console /profile /map /debug /machine:I386 /out:"../../bin/w32_deb/MP42Avi.exe" + +!ENDIF + +# Begin Target + +# Name "mp42avi - Win32 Release" +# Name "mp42avi - Win32 Debug" +# Begin Source File + +SOURCE=..\..\applications\mp42avi\main.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/mp4box.dsp b/build/msvc6/mp4box.dsp new file mode 100644 index 0000000..b7a0ae7 --- /dev/null +++ b/build/msvc6/mp4box.dsp @@ -0,0 +1,104 @@ +# Microsoft Developer Studio Project File - Name="mp4box" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=mp4box - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mp4box.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mp4box.mak" CFG="mp4box - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mp4box - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "mp4box - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mp4box - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/mp4box_rel" +# PROP Intermediate_Dir "obj/mp4box_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O1 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 /nologo /subsystem:console /machine:I386 /out:"../../bin/w32_rel/MP4Box.exe" /libpath:"../../extra_lib/lib/w32_rel" +# SUBTRACT LINK32 /map +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=copy ..\..\bin\w32_rel\MP4Box.exe "C:\Program Files\GPAC" +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mp4box - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/mp4box_deb" +# PROP Intermediate_Dir "obj/mp4box_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/w32_deb/MP4Box.exe" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "mp4box - Win32 Release" +# Name "mp4box - Win32 Debug" +# Begin Source File + +SOURCE=..\..\applications\mp4box\filedump.c +# End Source File +# Begin Source File + +SOURCE=..\..\applications\mp4box\fileimport.c +# End Source File +# Begin Source File + +SOURCE=..\..\applications\mp4box\main.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/mp4client.dsp b/build/msvc6/mp4client.dsp new file mode 100644 index 0000000..27fcd75 --- /dev/null +++ b/build/msvc6/mp4client.dsp @@ -0,0 +1,96 @@ +# Microsoft Developer Studio Project File - Name="mp4client" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=mp4client - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mp4client.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mp4client.mak" CFG="mp4client - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mp4client - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "mp4client - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mp4client - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/mp4client_rel" +# PROP Intermediate_Dir "obj/mp4client_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 User32.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/w32_rel/MP4Client.exe" + +!ELSEIF "$(CFG)" == "mp4client - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/mp4client_deb" +# PROP Intermediate_Dir "obj/mp4client_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Ot /I "../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 User32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/w32_deb/MP4Client.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "mp4client - Win32 Release" +# Name "mp4client - Win32 Debug" +# Begin Source File + +SOURCE=..\..\applications\mp4client\extract.c +# End Source File +# Begin Source File + +SOURCE=..\..\applications\mp4client\main.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/mpegts_in.dsp b/build/msvc6/mpegts_in.dsp new file mode 100644 index 0000000..25ff656 --- /dev/null +++ b/build/msvc6/mpegts_in.dsp @@ -0,0 +1,112 @@ +# Microsoft Developer Studio Project File - Name="mpegts_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mpegts_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mpegts_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mpegts_in.mak" CFG="mpegts_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mpegts_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mpegts_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mpegts_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "mpegts_in___Win32_Release" +# PROP BASE Intermediate_Dir "mpegts_in___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/mpegts_in_rel" +# PROP Intermediate_Dir "obj/mpegts_in_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MPEGTS_IN_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_mpegts_in.dll" + +!ELSEIF "$(CFG)" == "mpegts_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "mpegts_in___Win32_Debug" +# PROP BASE Intermediate_Dir "mpegts_in___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/mpegts_in_deb" +# PROP Intermediate_Dir "obj/mpegts_in_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MPEGTS_IN_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_mpegts_in.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "mpegts_in - Win32 Release" +# Name "mpegts_in - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\mpegts_in\mpegts_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\mpegts_in\mpegts_in.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msvc6/odf_dec.dsp b/build/msvc6/odf_dec.dsp new file mode 100644 index 0000000..1cdeaf5 --- /dev/null +++ b/build/msvc6/odf_dec.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="odf_dec" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=odf_dec - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "odf_dec.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "odf_dec.mak" CFG="odf_dec - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "odf_dec - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "odf_dec - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "odf_dec - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/odf_dec_rel" +# PROP Intermediate_Dir "obj/odf_dec_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OD_DEC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_odf_dec.dll" + +!ELSEIF "$(CFG)" == "odf_dec - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/odf_dec_deb" +# PROP Intermediate_Dir "obj/odf_dec_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OD_DEC_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_odf_dec.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "odf_dec - Win32 Release" +# Name "odf_dec - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\odf_dec\odf_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\odf_dec\odf_dec.def +# End Source File +# End Target +# End Project diff --git a/build/msvc6/ogg.dsp b/build/msvc6/ogg.dsp new file mode 100644 index 0000000..4a418f8 --- /dev/null +++ b/build/msvc6/ogg.dsp @@ -0,0 +1,115 @@ +# Microsoft Developer Studio Project File - Name="ogg" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ogg - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ogg.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ogg.mak" CFG="ogg - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ogg - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ogg - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ogg - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ogg_rel" +# PROP Intermediate_Dir "obj/ogg_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OGG_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_VORBIS" /D "GPAC_HAS_THEORA" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 ogg_static.lib vorbis_static.lib theora_static.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_ogg_xiph.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "ogg - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ogg_deb" +# PROP Intermediate_Dir "obj/ogg_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OGG_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GPAC_HAS_VORBIS" /D "GPAC_HAS_THEORA" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 vorbis_static.lib theora_static.lib ogg_static.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_ogg_xiph.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "ogg - Win32 Release" +# Name "ogg - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\ogg\ogg.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ogg\ogg_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ogg\ogg_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ogg\ogg_load.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ogg\theora_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ogg\vorbis_dec.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/osmozilla.dsp b/build/msvc6/osmozilla.dsp new file mode 100644 index 0000000..f1df122 --- /dev/null +++ b/build/msvc6/osmozilla.dsp @@ -0,0 +1,150 @@ +# Microsoft Developer Studio Project File - Name="osmozilla" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=osmozilla - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "osmozilla.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "osmozilla.mak" CFG="osmozilla - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "osmozilla - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "osmozilla - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "osmozilla - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/osmozilla_rel" +# PROP Intermediate_Dir "obj/osmozilla_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OSMOZILLA_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /I "..\..\extra_lib\include\gecko-sdk\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NPBASIC_EXPORTS" /D "MOZILLA_STRICT_API" /D "XP_WIN" /D "_X86_" /D "XPCOM_GLUE" /D WINVER=0x0400 /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 advapi32.lib user32.lib gdi32.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/nposmozilla.dll" +# SUBTRACT LINK32 /map +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=copy ..\..\applications\osmozilla\nsIOsmozilla.xpt_w32 ..\..\bin\w32_rel\nposmozilla.xpt copy ..\..\bin\w32_rel\nposmozilla.dll "C:\Program Files\GPAC\mozilla" copy ..\..\bin\w32_rel\nposmozilla.xpt "C:\Program Files\GPAC\mozilla" +# End Special Build Tool + +!ELSEIF "$(CFG)" == "osmozilla - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/osmozilla_deb" +# PROP Intermediate_Dir "obj/osmozilla_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OSMOZILLA_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "..\..\extra_lib\include\gecko-sdk\include" /D "WIN32" /D "XP_WIN32" /D "XP_WIN" /D "_X86_" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NPBASIC_EXPORTS" /D "MOZILLA_STRICT_API" /D "XPCOM_GLUE" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 advapi32.lib user32.lib gdi32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/nposmozilla.dll" /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=copy ..\..\bin\w32_deb\nposmozilla.dll "C:\Program Files\GPAC\mozilla" +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "osmozilla - Win32 Release" +# Name "osmozilla - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\applications\osmozilla\np_entry.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmozilla\npn_gate.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmozilla\npp_gate.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmozilla\osmozilla.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmozilla\osmozilla.def +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmozilla\osmozilla.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\applications\osmozilla\npplat.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmozilla\nsIOsmozilla.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmozilla\osmozilla.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msvc6/raw_out.dsp b/build/msvc6/raw_out.dsp new file mode 100644 index 0000000..d3b1d42 --- /dev/null +++ b/build/msvc6/raw_out.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="raw_out" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=raw_out - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "raw_out.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "raw_out.mak" CFG="raw_out - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "raw_out - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "raw_out - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "raw_out - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/raw_out_rel" +# PROP Intermediate_Dir "obj/raw_out_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RAW_OUT_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_raw_out.dll" + +!ELSEIF "$(CFG)" == "raw_out - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/raw_out_deb" +# PROP Intermediate_Dir "obj/raw_out_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RAW_OUT_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_raw_out.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "raw_out - Win32 Release" +# Name "raw_out - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\raw_out\raw_out.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\raw_out\raw_video.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/rtp_in.dsp b/build/msvc6/rtp_in.dsp new file mode 100644 index 0000000..bb8c50f --- /dev/null +++ b/build/msvc6/rtp_in.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="rtp_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=rtp_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rtp_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "rtp_in.mak" CFG="rtp_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rtp_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "rtp_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rtp_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/rtp_in_rel" +# PROP Intermediate_Dir "obj/rtp_in_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RTP_IN_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_rtp_in.dll" + +!ELSEIF "$(CFG)" == "rtp_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/rtp_in_deb" +# PROP Intermediate_Dir "obj/rtp_in_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RTP_IN_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_rtp_in.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "rtp_in - Win32 Release" +# Name "rtp_in - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_in.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_in.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_session.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_signaling.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\rtp_stream.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\sdp_fetch.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\rtp_in\sdp_load.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/saf_in.dsp b/build/msvc6/saf_in.dsp new file mode 100644 index 0000000..e612a6c --- /dev/null +++ b/build/msvc6/saf_in.dsp @@ -0,0 +1,103 @@ +# Microsoft Developer Studio Project File - Name="saf_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=saf_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "saf_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "saf_in.mak" CFG="saf_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "saf_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "saf_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "saf_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/saf_in_rel" +# PROP Intermediate_Dir "obj/saf_in_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SAF_IN_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_saf_in.dll" + +!ELSEIF "$(CFG)" == "saf_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/saf_in_deb" +# PROP Intermediate_Dir "obj/saf_in_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SAF_IN_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_saf_in.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "saf_in - Win32 Release" +# Name "saf_in - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\saf_in\saf_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\saf_in\saf_in.def +# End Source File +# End Group +# End Target +# End Project diff --git a/build/msvc6/sdl_out.dsp b/build/msvc6/sdl_out.dsp new file mode 100644 index 0000000..8ef584a --- /dev/null +++ b/build/msvc6/sdl_out.dsp @@ -0,0 +1,116 @@ +# Microsoft Developer Studio Project File - Name="sdl_out" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=sdl_out - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sdl_out.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sdl_out.mak" CFG="sdl_out - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sdl_out - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "sdl_out - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sdl_out - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/sdl_out_rel" +# PROP Intermediate_Dir "obj/sdl_out_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SDL_OUT_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 SDL.lib user32.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_sdl_out.dll" + +!ELSEIF "$(CFG)" == "sdl_out - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/sdl_out_deb" +# PROP Intermediate_Dir "obj/sdl_out_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SDL_OUT_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 SDL.lib user32.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_sdl_out.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "sdl_out - Win32 Release" +# Name "sdl_out - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\sdl_out\audio.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\sdl_out\cursors.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\sdl_out\sdl_out.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\sdl_out\sdl_out.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\sdl_out\sdl_out.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\sdl_out\video.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/soft_raster.dsp b/build/msvc6/soft_raster.dsp new file mode 100644 index 0000000..5872056 --- /dev/null +++ b/build/msvc6/soft_raster.dsp @@ -0,0 +1,136 @@ +# Microsoft Developer Studio Project File - Name="soft_raster" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=soft_raster - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "soft_raster.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "soft_raster.mak" CFG="soft_raster - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "soft_raster - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "soft_raster - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "soft_raster - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/soft_raster_rel" +# PROP Intermediate_Dir "obj/soft_raster_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_soft_raster.dll" + +!ELSEIF "$(CFG)" == "soft_raster - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/soft_raster_deb" +# PROP Intermediate_Dir "obj/soft_raster_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_soft_raster.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "soft_raster - Win32 Release" +# Name "soft_raster - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\soft_raster\ftgrays.c + +!IF "$(CFG)" == "soft_raster - Win32 Release" + +!ELSEIF "$(CFG)" == "soft_raster - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\rast_soft.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\rast_soft.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_565.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_argb.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_load.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_rgb.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\stencil.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\surface.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/standalone2drender.dsp b/build/msvc6/standalone2drender.dsp new file mode 100644 index 0000000..ec9cf4f --- /dev/null +++ b/build/msvc6/standalone2drender.dsp @@ -0,0 +1,267 @@ +# Microsoft Developer Studio Project File - Name="standalone2drender" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=standalone2drender - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "standalone2drender.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "standalone2drender.mak" CFG="standalone2drender - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "standalone2drender - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "standalone2drender - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "standalone2drender - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/sar2d_rel" +# PROP Intermediate_Dir "obj/sar2d_rel" +# PROP Target_Dir "" +F90=df.exe +MTL=midl.exe +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include/freetype" /I "../../modules/m4_rend" /I "../../modules/render2d" /I "../../modules/ft_font" /I "../../modules/raw_out" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "GPAC_STANDALONE_RENDER_2D" /D "DANAE" /FR /YX /FD /c +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"../../bin/w32_rel/gpac_sar2d.lib" + +!ELSEIF "$(CFG)" == "standalone2drender - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/sar2d_deb" +# PROP Intermediate_Dir "obj/sar2d_deb" +# PROP Target_Dir "" +F90=df.exe +MTL=midl.exe +LINK32=link.exe -lib +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include/freetype" /I "../../modules/m4_rend" /I "../../modules/render2d" /I "../../modules/ft_font" /I "../../modules/raw_out" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "GPAC_STANDALONE_RENDER_2D" /D "DANAE" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"../../bin/w32_deb/gpac_sar2d.lib" + +!ENDIF + +# Begin Target + +# Name "standalone2drender - Win32 Release" +# Name "standalone2drender - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\render2d\background2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\drawable.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\form.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\ft_font\ft_font.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\ftgrays.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\geometry_stacks.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\grouping.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\grouping_stacks.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\ifs2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\ils2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\layout.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\path_layout.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_565.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_argb.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_load.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\raster_rgb.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\raw_out\raw_video.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\render2d.c + +!IF "$(CFG)" == "standalone2drender - Win32 Release" + +# ADD CPP /FAcs + +!ELSEIF "$(CFG)" == "standalone2drender - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\render2d_nodes.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\sensor_stacks.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\sound.c +# End Source File +# Begin Source File + +SOURCE=..\..\applications\standalone2drender\standalone2drender.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\stencil.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\surface.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\svg_base.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\svg_media.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\svg_stacks.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\svg_text.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\text.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\texture_stacks.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\viewport.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\visualsurface2d.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\visualsurface2d_draw.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\modules\render2d\drawable.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\grouping.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\soft_raster\rast_soft.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\render2d.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\stacks2d.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\svg\svg_stacks.h +# End Source File +# Begin Source File + +SOURCE=..\..\modules\render2d\visualsurface2d.h +# End Source File +# End Group +# End Target +# End Project diff --git a/build/msvc6/svg_in.dsp b/build/msvc6/svg_in.dsp new file mode 100644 index 0000000..12b757e --- /dev/null +++ b/build/msvc6/svg_in.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="svg_in" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=svg_in - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "svg_in.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "svg_in.mak" CFG="svg_in - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "svg_in - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "svg_in - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "svg_in - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/svg_in_rel" +# PROP Intermediate_Dir "obj/svg_in_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SVG_IN_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include/zlib" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 zlib.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_svg_in.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "svg_in - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/svg_in_deb" +# PROP Intermediate_Dir "obj/svg_in_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SVG_IN_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include/zlib" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 zlib.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_svg_in.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "svg_in - Win32 Release" +# Name "svg_in - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\modules\svg_in\svg_in.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\svg_in\svg_in.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/msvc6/timedtext.dsp b/build/msvc6/timedtext.dsp new file mode 100644 index 0000000..5e35033 --- /dev/null +++ b/build/msvc6/timedtext.dsp @@ -0,0 +1,103 @@ +# Microsoft Developer Studio Project File - Name="timedtext" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=timedtext - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "timedtext.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "timedtext.mak" CFG="timedtext - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "timedtext - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "timedtext - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "timedtext - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/ttxt_rel" +# PROP Intermediate_Dir "obj/ttxt_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TIMEDTEXT_DEC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_timedtext.dll" + +!ELSEIF "$(CFG)" == "timedtext - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/ttxt_deb" +# PROP Intermediate_Dir "obj/ttxt_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TIMEDTEXT_DEC_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_timedtext.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "timedtext - Win32 Release" +# Name "timedtext - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\timedtext\timedtext.def +# End Source File +# Begin Source File + +SOURCE=..\..\modules\timedtext\timedtext_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\timedtext\timedtext_in.c +# End Source File +# End Target +# End Project diff --git a/build/msvc6/wav_out.dsp b/build/msvc6/wav_out.dsp new file mode 100644 index 0000000..9ddec56 --- /dev/null +++ b/build/msvc6/wav_out.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="wav_out" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=wav_out - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "wav_out.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "wav_out.mak" CFG="wav_out - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "wav_out - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "wav_out - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "wav_out - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/wav_out_rel" +# PROP Intermediate_Dir "obj/wav_out_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WAV_AUDIO_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 winmm.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_wav_out.dll" + +!ELSEIF "$(CFG)" == "wav_out - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/wav_out_deb" +# PROP Intermediate_Dir "obj/wav_out_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WAV_AUDIO_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 winmm.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_wav_out.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "wav_out - Win32 Release" +# Name "wav_out - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\wav_out\wav_out.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\wav_out\wav_out.def +# End Source File +# End Target +# End Project diff --git a/build/msvc6/wxOsmo4.dsp b/build/msvc6/wxOsmo4.dsp new file mode 100644 index 0000000..3128357 --- /dev/null +++ b/build/msvc6/wxOsmo4.dsp @@ -0,0 +1,156 @@ +# Microsoft Developer Studio Project File - Name="wxOsmo4" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=wxOsmo4 - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "wxOsmo4.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "wxOsmo4.mak" CFG="wxOsmo4 - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "wxOsmo4 - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "wxOsmo4 - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "wxOsmo4 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/osmo4_wx_rel" +# PROP Intermediate_Dir "obj/osmo4_wx_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 wxbase26.lib wxmsw26_core.lib comctl32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib rpcrt4.lib /nologo /subsystem:windows /machine:I386 /out:"../../bin/w32_rel/wxOsmo4.exe" + +!ELSEIF "$(CFG)" == "wxOsmo4 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/osmo4_wx_deb" +# PROP Intermediate_Dir "obj/osmo4_wx_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 wxbase26d.lib wxmsw26d_core.lib comctl32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib rpcrt4.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../../bin/w32_deb/wxOsmo4.exe" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "wxOsmo4 - Win32 Release" +# Name "wxOsmo4 - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\fileprops.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\menubtn.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\Playlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\wxGPACControl.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\wxOsmo4.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\wxOsmo4.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\fileprops.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\menubtn.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\Playlist.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\resource.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\wxGPACControl.h +# End Source File +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\wxOsmo4.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=..\..\applications\osmo4_wx\osmo4.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/build/msvc6/xvid_dec.dsp b/build/msvc6/xvid_dec.dsp new file mode 100644 index 0000000..863c985 --- /dev/null +++ b/build/msvc6/xvid_dec.dsp @@ -0,0 +1,101 @@ +# Microsoft Developer Studio Project File - Name="xvid_dec" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=xvid_dec - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "xvid_dec.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "xvid_dec.mak" CFG="xvid_dec - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "xvid_dec - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "xvid_dec - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "xvid_dec - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "obj/xvid_dec_rel" +# PROP Intermediate_Dir "obj/xvid_dec_rel" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XVID_DEC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../include" /I "../../extra_lib/include/xvid" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 libxvidcore.lib /nologo /dll /machine:I386 /out:"../../bin/w32_rel/gm_xvid_dec.dll" /libpath:"../../extra_lib/lib/w32_rel" + +!ELSEIF "$(CFG)" == "xvid_dec - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "obj/xvid_dec_deb" +# PROP Intermediate_Dir "obj/xvid_dec_deb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XVID_DEC_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "../../extra_lib/include/xvid" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 libxvidcore.lib /nologo /dll /debug /machine:I386 /out:"../../bin/w32_deb/gm_xvid_dec.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" + +!ENDIF + +# Begin Target + +# Name "xvid_dec - Win32 Release" +# Name "xvid_dec - Win32 Debug" +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_dec.c +# End Source File +# Begin Source File + +SOURCE=..\..\modules\xvid_dec\xvid_dec.def +# End Source File +# End Target +# End Project diff --git a/build/msvc8/GPAX.vcproj b/build/msvc8/GPAX.vcproj new file mode 100644 index 0000000..9651193 --- /dev/null +++ b/build/msvc8/GPAX.vcproj @@ -0,0 +1,767 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/Osmo4.vcproj b/build/msvc8/Osmo4.vcproj new file mode 100644 index 0000000..10910be --- /dev/null +++ b/build/msvc8/Osmo4.vcproj @@ -0,0 +1,1012 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/V4Studio.vcproj b/build/msvc8/V4Studio.vcproj new file mode 100644 index 0000000..3db5ff4 --- /dev/null +++ b/build/msvc8/V4Studio.vcproj @@ -0,0 +1,922 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/aac_in.vcproj b/build/msvc8/aac_in.vcproj new file mode 100644 index 0000000..c0e8707 --- /dev/null +++ b/build/msvc8/aac_in.vcproj @@ -0,0 +1,623 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/ac3_in.vcproj b/build/msvc8/ac3_in.vcproj new file mode 100644 index 0000000..eff70ae --- /dev/null +++ b/build/msvc8/ac3_in.vcproj @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/amr_dec.vcproj b/build/msvc8/amr_dec.vcproj new file mode 100644 index 0000000..9c50fac --- /dev/null +++ b/build/msvc8/amr_dec.vcproj @@ -0,0 +1,5447 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/amr_float_dec.vcproj b/build/msvc8/amr_float_dec.vcproj new file mode 100644 index 0000000..c3c306b --- /dev/null +++ b/build/msvc8/amr_float_dec.vcproj @@ -0,0 +1,1565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/bifs_dec.vcproj b/build/msvc8/bifs_dec.vcproj new file mode 100644 index 0000000..c2df3ae --- /dev/null +++ b/build/msvc8/bifs_dec.vcproj @@ -0,0 +1,569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/ctx_load.vcproj b/build/msvc8/ctx_load.vcproj new file mode 100644 index 0000000..06dd17b --- /dev/null +++ b/build/msvc8/ctx_load.vcproj @@ -0,0 +1,569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/dummy_in.vcproj b/build/msvc8/dummy_in.vcproj new file mode 100644 index 0000000..d0d5bcc --- /dev/null +++ b/build/msvc8/dummy_in.vcproj @@ -0,0 +1,569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/dx_hw.vcproj b/build/msvc8/dx_hw.vcproj new file mode 100644 index 0000000..1176b6d --- /dev/null +++ b/build/msvc8/dx_hw.vcproj @@ -0,0 +1,801 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/ffmpeg_in.vcproj b/build/msvc8/ffmpeg_in.vcproj new file mode 100644 index 0000000..5a0761b --- /dev/null +++ b/build/msvc8/ffmpeg_in.vcproj @@ -0,0 +1,661 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/ft_font.vcproj b/build/msvc8/ft_font.vcproj new file mode 100644 index 0000000..56b5c94 --- /dev/null +++ b/build/msvc8/ft_font.vcproj @@ -0,0 +1,547 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/gdip_raster.vcproj b/build/msvc8/gdip_raster.vcproj new file mode 100644 index 0000000..33fa10e --- /dev/null +++ b/build/msvc8/gdip_raster.vcproj @@ -0,0 +1,699 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/gpac.sln b/build/msvc8/gpac.sln new file mode 100644 index 0000000..bc7ca77 --- /dev/null +++ b/build/msvc8/gpac.sln @@ -0,0 +1,346 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPAX", "GPAX.vcproj", "{72486240-A124-496E-A67A-E76FEC7E99BE}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Osmo4", "Osmo4.vcproj", "{C79C2D73-06E9-4622-92CE-F166B1B51792}" + ProjectSection(ProjectDependencies) = postProject + {A86E6EF8-3683-4AD0-8878-7A0D51326088} = {A86E6EF8-3683-4AD0-8878-7A0D51326088} + {3B4108E4-C8FD-4D82-AF4A-F928CCF36A60} = {3B4108E4-C8FD-4D82-AF4A-F928CCF36A60} + {464697D2-BA44-446F-8223-1EB3969ED1A8} = {464697D2-BA44-446F-8223-1EB3969ED1A8} + {73CF10D0-DEC8-4D4B-AAB8-45864694CB8B} = {73CF10D0-DEC8-4D4B-AAB8-45864694CB8B} + {2B6AB6CD-E7D0-4706-BB4A-DDD7AE1A510D} = {2B6AB6CD-E7D0-4706-BB4A-DDD7AE1A510D} + {5D3983BF-143F-49C3-9284-89778282AEFE} = {5D3983BF-143F-49C3-9284-89778282AEFE} + {9F0660BC-4582-4D4E-BA83-E2BE5F6BDD45} = {9F0660BC-4582-4D4E-BA83-E2BE5F6BDD45} + {879E26B2-48DB-47B3-B82F-893696286E6F} = {879E26B2-48DB-47B3-B82F-893696286E6F} + {402A8794-B724-44A3-B608-84B600ECA387} = {402A8794-B724-44A3-B608-84B600ECA387} + {073E2381-4FDE-4C4D-A117-C489EB17C48B} = {073E2381-4FDE-4C4D-A117-C489EB17C48B} + {1F7CD37F-DC9A-4AC7-881B-36B263A644C7} = {1F7CD37F-DC9A-4AC7-881B-36B263A644C7} + {4AC7F65D-0DC0-49F9-8B96-AD05F11FF38E} = {4AC7F65D-0DC0-49F9-8B96-AD05F11FF38E} + {FC8F1F56-078E-470D-B252-68EDDE88AA9D} = {FC8F1F56-078E-470D-B252-68EDDE88AA9D} + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + {E2DBD151-9523-4E1A-9478-F3F825668F6E} = {E2DBD151-9523-4E1A-9478-F3F825668F6E} + {CAA5A551-006A-4119-8115-FB14692E719F} = {CAA5A551-006A-4119-8115-FB14692E719F} + {01183543-B182-4E32-9FD6-FC15C847316C} = {01183543-B182-4E32-9FD6-FC15C847316C} + {BAE0C03A-56E0-4D37-89F5-62DD03BFC013} = {BAE0C03A-56E0-4D37-89F5-62DD03BFC013} + {B38E812D-9823-48E7-BE5F-BF09B0AD4165} = {B38E812D-9823-48E7-BE5F-BF09B0AD4165} + {042D3628-67F3-4B5C-8BC0-CD9AFA416974} = {042D3628-67F3-4B5C-8BC0-CD9AFA416974} + {71071F01-C813-4384-BE38-0F4020BCC0EE} = {71071F01-C813-4384-BE38-0F4020BCC0EE} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aac_in", "aac_in.vcproj", "{1F7CD37F-DC9A-4AC7-881B-36B263A644C7}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "amr_dec", "amr_dec.vcproj", "{5ED5A1C8-DB34-4089-8E53-EFB225754D02}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "amr_float_dec", "amr_float_dec.vcproj", "{5D3983BF-143F-49C3-9284-89778282AEFE}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bifs_dec", "bifs_dec.vcproj", "{73CF10D0-DEC8-4D4B-AAB8-45864694CB8B}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ctx_load", "ctx_load.vcproj", "{BAE0C03A-56E0-4D37-89F5-62DD03BFC013}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dummy_in", "dummy_in.vcproj", "{073E2381-4FDE-4C4D-A117-C489EB17C48B}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dx_hw", "dx_hw.vcproj", "{B64736BD-9245-4F40-961D-EB9822265764}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ffmpeg_in", "ffmpeg_in.vcproj", "{9FECAB79-BC70-4EEA-A23B-B64159F904B3}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ft_font", "ft_font.vcproj", "{042D3628-67F3-4B5C-8BC0-CD9AFA416974}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gdip_raster", "gdip_raster.vcproj", "{CAA5A551-006A-4119-8115-FB14692E719F}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "img_in", "img_in.vcproj", "{9F0660BC-4582-4D4E-BA83-E2BE5F6BDD45}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "isom_in", "isom_in.vcproj", "{FC8F1F56-078E-470D-B252-68EDDE88AA9D}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "laser_dec", "laser_dec.vcproj", "{879E26B2-48DB-47B3-B82F-893696286E6F}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgpac", "libgpac.vcproj", "{233014D5-F6E5-419D-8757-FA9CE7337088}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgpac_dll", "libgpac_dll.vcproj", "{D3540754-E0CF-4604-AC11-82DE9BD4D814}" + ProjectSection(ProjectDependencies) = postProject + {233014D5-F6E5-419D-8757-FA9CE7337088} = {233014D5-F6E5-419D-8757-FA9CE7337088} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp3_in", "mp3_in.vcproj", "{4AC7F65D-0DC0-49F9-8B96-AD05F11FF38E}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4box", "mp4box.vcproj", "{48E2C7C6-52EB-46FB-8722-00A62F46F497}" + ProjectSection(ProjectDependencies) = postProject + {233014D5-F6E5-419D-8757-FA9CE7337088} = {233014D5-F6E5-419D-8757-FA9CE7337088} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4client", "mp4client.vcproj", "{A35D99BF-D72D-4F56-99A1-97249B22BCE2}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mpegts_in", "mpegts_in.vcproj", "{B38E812D-9823-48E7-BE5F-BF09B0AD4165}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "odf_dec", "odf_dec.vcproj", "{A86E6EF8-3683-4AD0-8878-7A0D51326088}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ogg", "ogg.vcproj", "{3B4108E4-C8FD-4D82-AF4A-F928CCF36A60}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "osmozilla", "osmozilla.vcproj", "{A0288B75-0D95-4106-A3A7-779A891E8FEF}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "raw_out", "raw_out.vcproj", "{8162BADA-2FB6-4A71-B998-ABAFAF28E5A8}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtp_in", "rtp_in.vcproj", "{E2DBD151-9523-4E1A-9478-F3F825668F6E}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "saf_in", "saf_in.vcproj", "{2B6AB6CD-E7D0-4706-BB4A-DDD7AE1A510D}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_out", "sdl_out.vcproj", "{F346A9A7-CF8D-4409-8776-1D4244F96EF8}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "soft_raster", "soft_raster.vcproj", "{71071F01-C813-4384-BE38-0F4020BCC0EE}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "svg_in", "svg_in.vcproj", "{402A8794-B724-44A3-B608-84B600ECA387}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "timedtext", "timedtext.vcproj", "{464697D2-BA44-446F-8223-1EB3969ED1A8}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wav_out", "wav_out.vcproj", "{01183543-B182-4E32-9FD6-FC15C847316C}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xvid_dec", "xvid_dec.vcproj", "{4258687D-4905-46A6-9407-08F4F8A81CC0}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ismacryp", "ismacryp.vcproj", "{E08DA5C2-9D97-4CD3-BB13-6FD6BA4458D6}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ac3_in", "ac3_in.vcproj", "{1DE72B1E-0DD6-4AFD-95E9-D23ACAB31812}" + ProjectSection(ProjectDependencies) = postProject + {D3540754-E0CF-4604-AC11-82DE9BD4D814} = {D3540754-E0CF-4604-AC11-82DE9BD4D814} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {72486240-A124-496E-A67A-E76FEC7E99BE}.Debug|Win32.ActiveCfg = Debug|Win32 + {72486240-A124-496E-A67A-E76FEC7E99BE}.Debug|Win32.Build.0 = Debug|Win32 + {72486240-A124-496E-A67A-E76FEC7E99BE}.Release|Win32.ActiveCfg = Release|Win32 + {72486240-A124-496E-A67A-E76FEC7E99BE}.Release|Win32.Build.0 = Release|Win32 + {C79C2D73-06E9-4622-92CE-F166B1B51792}.Debug|Win32.ActiveCfg = Debug|Win32 + {C79C2D73-06E9-4622-92CE-F166B1B51792}.Debug|Win32.Build.0 = Debug|Win32 + {C79C2D73-06E9-4622-92CE-F166B1B51792}.Release|Win32.ActiveCfg = Release|Win32 + {C79C2D73-06E9-4622-92CE-F166B1B51792}.Release|Win32.Build.0 = Release|Win32 + {1F7CD37F-DC9A-4AC7-881B-36B263A644C7}.Debug|Win32.ActiveCfg = Debug|Win32 + {1F7CD37F-DC9A-4AC7-881B-36B263A644C7}.Debug|Win32.Build.0 = Debug|Win32 + {1F7CD37F-DC9A-4AC7-881B-36B263A644C7}.Release|Win32.ActiveCfg = Release|Win32 + {1F7CD37F-DC9A-4AC7-881B-36B263A644C7}.Release|Win32.Build.0 = Release|Win32 + {5ED5A1C8-DB34-4089-8E53-EFB225754D02}.Debug|Win32.ActiveCfg = Debug|Win32 + {5ED5A1C8-DB34-4089-8E53-EFB225754D02}.Debug|Win32.Build.0 = Debug|Win32 + {5ED5A1C8-DB34-4089-8E53-EFB225754D02}.Release|Win32.ActiveCfg = Release|Win32 + {5ED5A1C8-DB34-4089-8E53-EFB225754D02}.Release|Win32.Build.0 = Release|Win32 + {5D3983BF-143F-49C3-9284-89778282AEFE}.Debug|Win32.ActiveCfg = Debug|Win32 + {5D3983BF-143F-49C3-9284-89778282AEFE}.Debug|Win32.Build.0 = Debug|Win32 + {5D3983BF-143F-49C3-9284-89778282AEFE}.Release|Win32.ActiveCfg = Release|Win32 + {5D3983BF-143F-49C3-9284-89778282AEFE}.Release|Win32.Build.0 = Release|Win32 + {73CF10D0-DEC8-4D4B-AAB8-45864694CB8B}.Debug|Win32.ActiveCfg = Debug|Win32 + {73CF10D0-DEC8-4D4B-AAB8-45864694CB8B}.Debug|Win32.Build.0 = Debug|Win32 + {73CF10D0-DEC8-4D4B-AAB8-45864694CB8B}.Release|Win32.ActiveCfg = Release|Win32 + {73CF10D0-DEC8-4D4B-AAB8-45864694CB8B}.Release|Win32.Build.0 = Release|Win32 + {BAE0C03A-56E0-4D37-89F5-62DD03BFC013}.Debug|Win32.ActiveCfg = Debug|Win32 + {BAE0C03A-56E0-4D37-89F5-62DD03BFC013}.Debug|Win32.Build.0 = Debug|Win32 + {BAE0C03A-56E0-4D37-89F5-62DD03BFC013}.Release|Win32.ActiveCfg = Release|Win32 + {BAE0C03A-56E0-4D37-89F5-62DD03BFC013}.Release|Win32.Build.0 = Release|Win32 + {073E2381-4FDE-4C4D-A117-C489EB17C48B}.Debug|Win32.ActiveCfg = Debug|Win32 + {073E2381-4FDE-4C4D-A117-C489EB17C48B}.Debug|Win32.Build.0 = Debug|Win32 + {073E2381-4FDE-4C4D-A117-C489EB17C48B}.Release|Win32.ActiveCfg = Release|Win32 + {073E2381-4FDE-4C4D-A117-C489EB17C48B}.Release|Win32.Build.0 = Release|Win32 + {B64736BD-9245-4F40-961D-EB9822265764}.Debug|Win32.ActiveCfg = Debug|Win32 + {B64736BD-9245-4F40-961D-EB9822265764}.Debug|Win32.Build.0 = Debug|Win32 + {B64736BD-9245-4F40-961D-EB9822265764}.Release|Win32.ActiveCfg = Release|Win32 + {B64736BD-9245-4F40-961D-EB9822265764}.Release|Win32.Build.0 = Release|Win32 + {9FECAB79-BC70-4EEA-A23B-B64159F904B3}.Debug|Win32.ActiveCfg = Debug|Win32 + {9FECAB79-BC70-4EEA-A23B-B64159F904B3}.Debug|Win32.Build.0 = Debug|Win32 + {9FECAB79-BC70-4EEA-A23B-B64159F904B3}.Release|Win32.ActiveCfg = Release|Win32 + {9FECAB79-BC70-4EEA-A23B-B64159F904B3}.Release|Win32.Build.0 = Release|Win32 + {042D3628-67F3-4B5C-8BC0-CD9AFA416974}.Debug|Win32.ActiveCfg = Debug|Win32 + {042D3628-67F3-4B5C-8BC0-CD9AFA416974}.Debug|Win32.Build.0 = Debug|Win32 + {042D3628-67F3-4B5C-8BC0-CD9AFA416974}.Release|Win32.ActiveCfg = Release|Win32 + {042D3628-67F3-4B5C-8BC0-CD9AFA416974}.Release|Win32.Build.0 = Release|Win32 + {CAA5A551-006A-4119-8115-FB14692E719F}.Debug|Win32.ActiveCfg = Debug|Win32 + {CAA5A551-006A-4119-8115-FB14692E719F}.Debug|Win32.Build.0 = Debug|Win32 + {CAA5A551-006A-4119-8115-FB14692E719F}.Release|Win32.ActiveCfg = Release|Win32 + {CAA5A551-006A-4119-8115-FB14692E719F}.Release|Win32.Build.0 = Release|Win32 + {9F0660BC-4582-4D4E-BA83-E2BE5F6BDD45}.Debug|Win32.ActiveCfg = Debug|Win32 + {9F0660BC-4582-4D4E-BA83-E2BE5F6BDD45}.Debug|Win32.Build.0 = Debug|Win32 + {9F0660BC-4582-4D4E-BA83-E2BE5F6BDD45}.Release|Win32.ActiveCfg = Release|Win32 + {9F0660BC-4582-4D4E-BA83-E2BE5F6BDD45}.Release|Win32.Build.0 = Release|Win32 + {FC8F1F56-078E-470D-B252-68EDDE88AA9D}.Debug|Win32.ActiveCfg = Debug|Win32 + {FC8F1F56-078E-470D-B252-68EDDE88AA9D}.Debug|Win32.Build.0 = Debug|Win32 + {FC8F1F56-078E-470D-B252-68EDDE88AA9D}.Release|Win32.ActiveCfg = Release|Win32 + {FC8F1F56-078E-470D-B252-68EDDE88AA9D}.Release|Win32.Build.0 = Release|Win32 + {879E26B2-48DB-47B3-B82F-893696286E6F}.Debug|Win32.ActiveCfg = Debug|Win32 + {879E26B2-48DB-47B3-B82F-893696286E6F}.Debug|Win32.Build.0 = Debug|Win32 + {879E26B2-48DB-47B3-B82F-893696286E6F}.Release|Win32.ActiveCfg = Release|Win32 + {879E26B2-48DB-47B3-B82F-893696286E6F}.Release|Win32.Build.0 = Release|Win32 + {233014D5-F6E5-419D-8757-FA9CE7337088}.Debug|Win32.ActiveCfg = Debug|Win32 + {233014D5-F6E5-419D-8757-FA9CE7337088}.Debug|Win32.Build.0 = Debug|Win32 + {233014D5-F6E5-419D-8757-FA9CE7337088}.Release|Win32.ActiveCfg = Release|Win32 + {233014D5-F6E5-419D-8757-FA9CE7337088}.Release|Win32.Build.0 = Release|Win32 + {D3540754-E0CF-4604-AC11-82DE9BD4D814}.Debug|Win32.ActiveCfg = Debug|Win32 + {D3540754-E0CF-4604-AC11-82DE9BD4D814}.Debug|Win32.Build.0 = Debug|Win32 + {D3540754-E0CF-4604-AC11-82DE9BD4D814}.Release|Win32.ActiveCfg = Release|Win32 + {D3540754-E0CF-4604-AC11-82DE9BD4D814}.Release|Win32.Build.0 = Release|Win32 + {4AC7F65D-0DC0-49F9-8B96-AD05F11FF38E}.Debug|Win32.ActiveCfg = Debug|Win32 + {4AC7F65D-0DC0-49F9-8B96-AD05F11FF38E}.Debug|Win32.Build.0 = Debug|Win32 + {4AC7F65D-0DC0-49F9-8B96-AD05F11FF38E}.Release|Win32.ActiveCfg = Release|Win32 + {4AC7F65D-0DC0-49F9-8B96-AD05F11FF38E}.Release|Win32.Build.0 = Release|Win32 + {48E2C7C6-52EB-46FB-8722-00A62F46F497}.Debug|Win32.ActiveCfg = Debug|Win32 + {48E2C7C6-52EB-46FB-8722-00A62F46F497}.Debug|Win32.Build.0 = Debug|Win32 + {48E2C7C6-52EB-46FB-8722-00A62F46F497}.Release|Win32.ActiveCfg = Release|Win32 + {48E2C7C6-52EB-46FB-8722-00A62F46F497}.Release|Win32.Build.0 = Release|Win32 + {A35D99BF-D72D-4F56-99A1-97249B22BCE2}.Debug|Win32.ActiveCfg = Debug|Win32 + {A35D99BF-D72D-4F56-99A1-97249B22BCE2}.Debug|Win32.Build.0 = Debug|Win32 + {A35D99BF-D72D-4F56-99A1-97249B22BCE2}.Release|Win32.ActiveCfg = Release|Win32 + {A35D99BF-D72D-4F56-99A1-97249B22BCE2}.Release|Win32.Build.0 = Release|Win32 + {B38E812D-9823-48E7-BE5F-BF09B0AD4165}.Debug|Win32.ActiveCfg = Debug|Win32 + {B38E812D-9823-48E7-BE5F-BF09B0AD4165}.Debug|Win32.Build.0 = Debug|Win32 + {B38E812D-9823-48E7-BE5F-BF09B0AD4165}.Release|Win32.ActiveCfg = Release|Win32 + {B38E812D-9823-48E7-BE5F-BF09B0AD4165}.Release|Win32.Build.0 = Release|Win32 + {A86E6EF8-3683-4AD0-8878-7A0D51326088}.Debug|Win32.ActiveCfg = Debug|Win32 + {A86E6EF8-3683-4AD0-8878-7A0D51326088}.Debug|Win32.Build.0 = Debug|Win32 + {A86E6EF8-3683-4AD0-8878-7A0D51326088}.Release|Win32.ActiveCfg = Release|Win32 + {A86E6EF8-3683-4AD0-8878-7A0D51326088}.Release|Win32.Build.0 = Release|Win32 + {3B4108E4-C8FD-4D82-AF4A-F928CCF36A60}.Debug|Win32.ActiveCfg = Debug|Win32 + {3B4108E4-C8FD-4D82-AF4A-F928CCF36A60}.Debug|Win32.Build.0 = Debug|Win32 + {3B4108E4-C8FD-4D82-AF4A-F928CCF36A60}.Release|Win32.ActiveCfg = Release|Win32 + {3B4108E4-C8FD-4D82-AF4A-F928CCF36A60}.Release|Win32.Build.0 = Release|Win32 + {A0288B75-0D95-4106-A3A7-779A891E8FEF}.Debug|Win32.ActiveCfg = Debug|Win32 + {A0288B75-0D95-4106-A3A7-779A891E8FEF}.Debug|Win32.Build.0 = Debug|Win32 + {A0288B75-0D95-4106-A3A7-779A891E8FEF}.Release|Win32.ActiveCfg = Release|Win32 + {A0288B75-0D95-4106-A3A7-779A891E8FEF}.Release|Win32.Build.0 = Release|Win32 + {8162BADA-2FB6-4A71-B998-ABAFAF28E5A8}.Debug|Win32.ActiveCfg = Debug|Win32 + {8162BADA-2FB6-4A71-B998-ABAFAF28E5A8}.Debug|Win32.Build.0 = Debug|Win32 + {8162BADA-2FB6-4A71-B998-ABAFAF28E5A8}.Release|Win32.ActiveCfg = Release|Win32 + {8162BADA-2FB6-4A71-B998-ABAFAF28E5A8}.Release|Win32.Build.0 = Release|Win32 + {E2DBD151-9523-4E1A-9478-F3F825668F6E}.Debug|Win32.ActiveCfg = Debug|Win32 + {E2DBD151-9523-4E1A-9478-F3F825668F6E}.Debug|Win32.Build.0 = Debug|Win32 + {E2DBD151-9523-4E1A-9478-F3F825668F6E}.Release|Win32.ActiveCfg = Release|Win32 + {E2DBD151-9523-4E1A-9478-F3F825668F6E}.Release|Win32.Build.0 = Release|Win32 + {2B6AB6CD-E7D0-4706-BB4A-DDD7AE1A510D}.Debug|Win32.ActiveCfg = Debug|Win32 + {2B6AB6CD-E7D0-4706-BB4A-DDD7AE1A510D}.Debug|Win32.Build.0 = Debug|Win32 + {2B6AB6CD-E7D0-4706-BB4A-DDD7AE1A510D}.Release|Win32.ActiveCfg = Release|Win32 + {2B6AB6CD-E7D0-4706-BB4A-DDD7AE1A510D}.Release|Win32.Build.0 = Release|Win32 + {F346A9A7-CF8D-4409-8776-1D4244F96EF8}.Debug|Win32.ActiveCfg = Debug|Win32 + {F346A9A7-CF8D-4409-8776-1D4244F96EF8}.Debug|Win32.Build.0 = Debug|Win32 + {F346A9A7-CF8D-4409-8776-1D4244F96EF8}.Release|Win32.ActiveCfg = Release|Win32 + {F346A9A7-CF8D-4409-8776-1D4244F96EF8}.Release|Win32.Build.0 = Release|Win32 + {71071F01-C813-4384-BE38-0F4020BCC0EE}.Debug|Win32.ActiveCfg = Debug|Win32 + {71071F01-C813-4384-BE38-0F4020BCC0EE}.Debug|Win32.Build.0 = Debug|Win32 + {71071F01-C813-4384-BE38-0F4020BCC0EE}.Release|Win32.ActiveCfg = Release|Win32 + {71071F01-C813-4384-BE38-0F4020BCC0EE}.Release|Win32.Build.0 = Release|Win32 + {402A8794-B724-44A3-B608-84B600ECA387}.Debug|Win32.ActiveCfg = Debug|Win32 + {402A8794-B724-44A3-B608-84B600ECA387}.Debug|Win32.Build.0 = Debug|Win32 + {402A8794-B724-44A3-B608-84B600ECA387}.Release|Win32.ActiveCfg = Release|Win32 + {402A8794-B724-44A3-B608-84B600ECA387}.Release|Win32.Build.0 = Release|Win32 + {464697D2-BA44-446F-8223-1EB3969ED1A8}.Debug|Win32.ActiveCfg = Debug|Win32 + {464697D2-BA44-446F-8223-1EB3969ED1A8}.Debug|Win32.Build.0 = Debug|Win32 + {464697D2-BA44-446F-8223-1EB3969ED1A8}.Release|Win32.ActiveCfg = Release|Win32 + {464697D2-BA44-446F-8223-1EB3969ED1A8}.Release|Win32.Build.0 = Release|Win32 + {01183543-B182-4E32-9FD6-FC15C847316C}.Debug|Win32.ActiveCfg = Debug|Win32 + {01183543-B182-4E32-9FD6-FC15C847316C}.Debug|Win32.Build.0 = Debug|Win32 + {01183543-B182-4E32-9FD6-FC15C847316C}.Release|Win32.ActiveCfg = Release|Win32 + {01183543-B182-4E32-9FD6-FC15C847316C}.Release|Win32.Build.0 = Release|Win32 + {4258687D-4905-46A6-9407-08F4F8A81CC0}.Debug|Win32.ActiveCfg = Debug|Win32 + {4258687D-4905-46A6-9407-08F4F8A81CC0}.Debug|Win32.Build.0 = Debug|Win32 + {4258687D-4905-46A6-9407-08F4F8A81CC0}.Release|Win32.ActiveCfg = Release|Win32 + {4258687D-4905-46A6-9407-08F4F8A81CC0}.Release|Win32.Build.0 = Release|Win32 + {E08DA5C2-9D97-4CD3-BB13-6FD6BA4458D6}.Debug|Win32.ActiveCfg = Debug|Win32 + {E08DA5C2-9D97-4CD3-BB13-6FD6BA4458D6}.Debug|Win32.Build.0 = Debug|Win32 + {E08DA5C2-9D97-4CD3-BB13-6FD6BA4458D6}.Release|Win32.ActiveCfg = Release|Win32 + {E08DA5C2-9D97-4CD3-BB13-6FD6BA4458D6}.Release|Win32.Build.0 = Release|Win32 + {1DE72B1E-0DD6-4AFD-95E9-D23ACAB31812}.Debug|Win32.ActiveCfg = Debug|Win32 + {1DE72B1E-0DD6-4AFD-95E9-D23ACAB31812}.Debug|Win32.Build.0 = Debug|Win32 + {1DE72B1E-0DD6-4AFD-95E9-D23ACAB31812}.Release|Win32.ActiveCfg = Release|Win32 + {1DE72B1E-0DD6-4AFD-95E9-D23ACAB31812}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/build/msvc8/img_in.vcproj b/build/msvc8/img_in.vcproj new file mode 100644 index 0000000..a3032e5 --- /dev/null +++ b/build/msvc8/img_in.vcproj @@ -0,0 +1,745 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/ismacryp.vcproj b/build/msvc8/ismacryp.vcproj new file mode 100644 index 0000000..14ba581 --- /dev/null +++ b/build/msvc8/ismacryp.vcproj @@ -0,0 +1,569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/isom_in.vcproj b/build/msvc8/isom_in.vcproj new file mode 100644 index 0000000..70bc06f --- /dev/null +++ b/build/msvc8/isom_in.vcproj @@ -0,0 +1,695 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/laser_dec.vcproj b/build/msvc8/laser_dec.vcproj new file mode 100644 index 0000000..c494869 --- /dev/null +++ b/build/msvc8/laser_dec.vcproj @@ -0,0 +1,569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/libgpac.vcproj b/build/msvc8/libgpac.vcproj new file mode 100644 index 0000000..0a27efa --- /dev/null +++ b/build/msvc8/libgpac.vcproj @@ -0,0 +1,7219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/libgpac_dll.vcproj b/build/msvc8/libgpac_dll.vcproj new file mode 100644 index 0000000..540b085 --- /dev/null +++ b/build/msvc8/libgpac_dll.vcproj @@ -0,0 +1,539 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/mp3_in.vcproj b/build/msvc8/mp3_in.vcproj new file mode 100644 index 0000000..80719dc --- /dev/null +++ b/build/msvc8/mp3_in.vcproj @@ -0,0 +1,617 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/mp4box.vcproj b/build/msvc8/mp4box.vcproj new file mode 100644 index 0000000..d6bbbbc --- /dev/null +++ b/build/msvc8/mp4box.vcproj @@ -0,0 +1,639 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/mp4client.vcproj b/build/msvc8/mp4client.vcproj new file mode 100644 index 0000000..a6d2239 --- /dev/null +++ b/build/msvc8/mp4client.vcproj @@ -0,0 +1,595 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/mpegts_in.vcproj b/build/msvc8/mpegts_in.vcproj new file mode 100644 index 0000000..8f2ec4f --- /dev/null +++ b/build/msvc8/mpegts_in.vcproj @@ -0,0 +1,584 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/odf_dec.vcproj b/build/msvc8/odf_dec.vcproj new file mode 100644 index 0000000..808017b --- /dev/null +++ b/build/msvc8/odf_dec.vcproj @@ -0,0 +1,571 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/ogg.vcproj b/build/msvc8/ogg.vcproj new file mode 100644 index 0000000..5eb6cd2 --- /dev/null +++ b/build/msvc8/ogg.vcproj @@ -0,0 +1,701 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/osmozilla.vcproj b/build/msvc8/osmozilla.vcproj new file mode 100644 index 0000000..ed9b0b6 --- /dev/null +++ b/build/msvc8/osmozilla.vcproj @@ -0,0 +1,766 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/raw_out.vcproj b/build/msvc8/raw_out.vcproj new file mode 100644 index 0000000..bf72878 --- /dev/null +++ b/build/msvc8/raw_out.vcproj @@ -0,0 +1,569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/rtp_in.vcproj b/build/msvc8/rtp_in.vcproj new file mode 100644 index 0000000..2809ce9 --- /dev/null +++ b/build/msvc8/rtp_in.vcproj @@ -0,0 +1,773 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/saf_in.vcproj b/build/msvc8/saf_in.vcproj new file mode 100644 index 0000000..8067cb7 --- /dev/null +++ b/build/msvc8/saf_in.vcproj @@ -0,0 +1,574 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/sdl_out.vcproj b/build/msvc8/sdl_out.vcproj new file mode 100644 index 0000000..e2571c2 --- /dev/null +++ b/build/msvc8/sdl_out.vcproj @@ -0,0 +1,700 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/soft_raster.vcproj b/build/msvc8/soft_raster.vcproj new file mode 100644 index 0000000..008a684 --- /dev/null +++ b/build/msvc8/soft_raster.vcproj @@ -0,0 +1,813 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/svg_in.vcproj b/build/msvc8/svg_in.vcproj new file mode 100644 index 0000000..22ec71e --- /dev/null +++ b/build/msvc8/svg_in.vcproj @@ -0,0 +1,592 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/timedtext.vcproj b/build/msvc8/timedtext.vcproj new file mode 100644 index 0000000..5f44f77 --- /dev/null +++ b/build/msvc8/timedtext.vcproj @@ -0,0 +1,609 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/wav_out.vcproj b/build/msvc8/wav_out.vcproj new file mode 100644 index 0000000..13573be --- /dev/null +++ b/build/msvc8/wav_out.vcproj @@ -0,0 +1,573 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/wxOsmo4.vcproj b/build/msvc8/wxOsmo4.vcproj new file mode 100644 index 0000000..64ce65b --- /dev/null +++ b/build/msvc8/wxOsmo4.vcproj @@ -0,0 +1,388 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/msvc8/xvid_dec.vcproj b/build/msvc8/xvid_dec.vcproj new file mode 100644 index 0000000..cb9a9ad --- /dev/null +++ b/build/msvc8/xvid_dec.vcproj @@ -0,0 +1,577 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/symbian/Icons.mk b/build/symbian/Icons.mk new file mode 100644 index 0000000..d428775 --- /dev/null +++ b/build/symbian/Icons.mk @@ -0,0 +1,44 @@ +ifeq (WINS,$(findstring WINS, $(PLATFORM))) +ZDIR=$(EPOCROOT)epoc32\release\$(PLATFORM)\$(CFG)\Z +else +ZDIR=$(EPOCROOT)epoc32\data\z +endif + +ifeq (THUMB,$(findstring THUMB, $(PLATFORM))) +OPT= +TARGETDIR=$(ZDIR)\system\apps\Osmo4 +ICONTARGETFILENAME=$(TARGETDIR)\Osmo4.aif +else +TARGETDIR=$(ZDIR)\resource\apps +ICONTARGETFILENAME=$(TARGETDIR)\osmo4_aif.mif +OPT=/X +endif + + +ICONDIR=..\..\applications\osmo4_sym\res + +do_nothing : + @rem do_nothing + +MAKMAKE : do_nothing + +BLD : do_nothing + +CLEAN : do_nothing + +LIB : do_nothing + +CLEANLIB : do_nothing + +RESOURCE : + mifconv $(ICONTARGETFILENAME) $(OPT) $(ICONDIR)\osmo4.svg + +FREEZE : do_nothing + +SAVESPACE : do_nothing + +RELEASABLES : + @echo $(ICONTARGETFILENAME) + +FINAL : do_nothing + diff --git a/build/symbian/aac_in.mmp b/build/symbian/aac_in.mmp new file mode 100644 index 0000000..b9e4f9f --- /dev/null +++ b/build/symbian/aac_in.mmp @@ -0,0 +1,15 @@ +TARGET gm_aac_in.dll + +//uncomment to try with faad support - currently crashing ... +//MACRO GPAC_HAS_FAAD +//STATICLIBRARY libfaad.lib +//SYSTEMINCLUDE ..\..\extra_lib\include\faad + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\aac_in +SOURCE aac_in.c +SOURCE faad_dec.c + diff --git a/build/symbian/amr_in.mmp b/build/symbian/amr_in.mmp new file mode 100644 index 0000000..79cbe03 --- /dev/null +++ b/build/symbian/amr_in.mmp @@ -0,0 +1,11 @@ +TARGET gm_amr_in.dll + +//only compile demux part +MACRO GPAC_AMR_IN_STANDALONE + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\amr_dec +SOURCE amr_in.c diff --git a/build/symbian/bifs_dec.mmp b/build/symbian/bifs_dec.mmp new file mode 100644 index 0000000..7910afa --- /dev/null +++ b/build/symbian/bifs_dec.mmp @@ -0,0 +1,9 @@ +TARGET gm_bifs_dec.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\bifs_dec +SOURCE bifs_dec.c + diff --git a/build/symbian/bld.inf b/build/symbian/bld.inf new file mode 100644 index 0000000..b192b95 --- /dev/null +++ b/build/symbian/bld.inf @@ -0,0 +1,35 @@ +// bld.inf +PRJ_MMPFILES + +//-- osmo4 icon +gnumakefile icons.mk + +//-- libgpac & osmo4 +libgpac.mmp +osmo4.mmp + +//-- all modules -- +aac_in.mmp +amr_in.mmp +bifs_dec.mmp +ctx_load.mmp +dummy_in.mmp +epoc_hw.mmp +ffmpeg_in.mmp +ft_font.mmp +img_in.mmp +ismacryp.mmp +isom_in.mmp +laser_dec.mmp +mp3_in.mmp +odf_dec.mmp +rtp_in.mmp +saf_in.mmp +soft_raster.mmp +svg_in.mmp +timedtext.mmp +xvid_dec.mmp + + +PRJ_PLATFORMS +GCCE diff --git a/build/symbian/ctx_load.mmp b/build/symbian/ctx_load.mmp new file mode 100644 index 0000000..dbf1c4a --- /dev/null +++ b/build/symbian/ctx_load.mmp @@ -0,0 +1,10 @@ +TARGET gm_ctx_load.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\ctx_load +SOURCE ctx_load.c + +EXPORTUNFROZEN diff --git a/build/symbian/dummy_in.mmp b/build/symbian/dummy_in.mmp new file mode 100644 index 0000000..6cf2f91 --- /dev/null +++ b/build/symbian/dummy_in.mmp @@ -0,0 +1,9 @@ +TARGET gm_dummy_in.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\dummy_in +SOURCE dummy_in.c + diff --git a/build/symbian/epoc_hw.mmp b/build/symbian/epoc_hw.mmp new file mode 100644 index 0000000..4315d4a --- /dev/null +++ b/build/symbian/epoc_hw.mmp @@ -0,0 +1,25 @@ +TARGET gm_epoc_hw.dll + +//OpenGL libs +#if 1 +MACRO GPAC_USE_OGL_ES +LIBRARY libgles_cm.lib +#endif + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\epoc_hw +SOURCE epoc_vout.cpp +SOURCE epoc_aout.cpp +SOURCE epoc_codec.cpp + +LIBRARY ws32.lib +//video libs +LIBRARY fbscli.lib +//audio libs +LIBRARY MediaClientAudioStream.lib +//codec libs +LIBRARY mmfserverbaseclasses.lib + diff --git a/build/symbian/ffmpeg_in.mmp b/build/symbian/ffmpeg_in.mmp new file mode 100644 index 0000000..32a1b5d --- /dev/null +++ b/build/symbian/ffmpeg_in.mmp @@ -0,0 +1,15 @@ +TARGET gm_ffmpeg_in.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\ffmpeg_in +SOURCE ffmpeg_decode.c +SOURCE ffmpeg_demux.c +SOURCE ffmpeg_load.c + +SYSTEMINCLUDE ..\..\extra_lib\include +STATICLIBRARY libffmpeg.lib +LIBRARY ezlib.lib + diff --git a/build/symbian/ft_font.mmp b/build/symbian/ft_font.mmp new file mode 100644 index 0000000..f3663dc --- /dev/null +++ b/build/symbian/ft_font.mmp @@ -0,0 +1,18 @@ +TARGET gm_ft_font.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\ft_font +SOURCE ft_font.c + +SYSTEMINCLUDE ..\..\extra_lib\include\freetype + +LIBRARY ezlib.lib +STATICLIBRARY libfreetype.lib + +#if defined(GCCE) +#else +LIBRARY egcc.lib +#endif diff --git a/build/symbian/gpac_module_symbianU.def b/build/symbian/gpac_module_symbianU.def new file mode 100644 index 0000000..94f861c --- /dev/null +++ b/build/symbian/gpac_module_symbianU.def @@ -0,0 +1,5 @@ +EXPORTS + QueryInterface @ 1 NONAME ; CODE + LoadInterface @ 2 NONAME ; CODE + ShutdownInterface @ 3 NONAME ; CODE + diff --git a/build/symbian/img_in.mmp b/build/symbian/img_in.mmp new file mode 100644 index 0000000..7891184 --- /dev/null +++ b/build/symbian/img_in.mmp @@ -0,0 +1,29 @@ +TARGET gm_img_in.dll + +//enable if you have libopenjpeg +#if 0 +MACRO GPAC_HAS_JP2 +SYSTEMINCLUDE ..\..\extra_lib\include\openjpeg +STATICLIBRARY libopenjpeg.lib + +//THIS MUST BE SET BEFORE EPOC LIBS OTHERWISE LINKER IS SCREWED !! +#if defined(GCCE) +#else +LIBRARY egcc.lib +#endif + +#endif + + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\img_in +SOURCE img_dec.c +SOURCE img_in.c +SOURCE bmp_dec.c +SOURCE png_dec.c +SOURCE jpeg_dec.c +SOURCE jp2_dec.c + diff --git a/build/symbian/ismacryp.mmp b/build/symbian/ismacryp.mmp new file mode 100644 index 0000000..19fce18 --- /dev/null +++ b/build/symbian/ismacryp.mmp @@ -0,0 +1,8 @@ +TARGET gm_ismacryp.dll +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\ismacryp +SOURCE ismacryp.c + diff --git a/build/symbian/isom_in.mmp b/build/symbian/isom_in.mmp new file mode 100644 index 0000000..f798efb --- /dev/null +++ b/build/symbian/isom_in.mmp @@ -0,0 +1,12 @@ +TARGET gm_isom_in.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\isom_in +SOURCE cache.c +SOURCE load.c +SOURCE read.c +SOURCE read_ch.c + diff --git a/build/symbian/laser_dec.mmp b/build/symbian/laser_dec.mmp new file mode 100644 index 0000000..50e319e --- /dev/null +++ b/build/symbian/laser_dec.mmp @@ -0,0 +1,8 @@ +TARGET gm_laser_dec.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\laser_dec +SOURCE laser_dec.c diff --git a/build/symbian/libgpac.mmp b/build/symbian/libgpac.mmp new file mode 100644 index 0000000..64c9af2 --- /dev/null +++ b/build/symbian/libgpac.mmp @@ -0,0 +1,318 @@ +TARGET libgpac.dll +TARGETTYPE DLL +CAPABILITY NetworkServices +UID 0x1000008d 0xf01f9073 + +//allow writable static data +EPOCALLOWDLLDATA + +//comment if you don't have JS +#if 1 +MACRO GPAC_HAS_SPIDERMONKEY +MACRO XP_PC +SYSTEMINCLUDE ..\..\extra_lib\include\js +STATICLIBRARY libjs.lib +#endif + +//comment if you don't have libpng +#if 1 +MACRO GPAC_HAS_PNG +SYSTEMINCLUDE ..\..\extra_lib\include\png +STATICLIBRARY libpng.lib +#endif + + +//comment if you don't have libjpeg +#if 1 +MACRO GPAC_HAS_JPEG +SYSTEMINCLUDE ..\..\extra_lib\include\jpeg +STATICLIBRARY libjpeg.lib +#endif + +//comment if you don't have OpenGL ES +#if 1 +MACRO GPAC_USE_OGL_ES +LIBRARY libgles_cm.lib +#else +MACRO GPAC_DISABLE_3D +#endif + + +//Common tools +SOURCEPATH ..\..\src\utils +SOURCE base_encoding.c +SOURCE bitstream.c +SOURCE color.c +SOURCE configfile.c +SOURCE downloader.c +SOURCE error.c +SOURCE list.c +SOURCE math.c +SOURCE module.c +SOURCE path2d.c +SOURCE path2d_stroker.c +SOURCE token.c +SOURCE uni_bidi.c +SOURCE url.c +SOURCE utf.c +SOURCE xml_parser.c +// zlib symbian on sybian SDKs doesn't come with gzio +SOURCE gzio.cpp +//SOURCE zutil.c +// symbian-specific stuff +SOURCE symbian_os.cpp +SOURCE symbian_net.cpp + +//IETF tools +SOURCEPATH ..\..\src\ietf +SOURCE rtcp.c +SOURCE rtp.c +SOURCE rtp_packetizer.c +SOURCE rtp_depacketizer.c +SOURCE rtp_pck_3gpp.c +SOURCE rtp_pck_mpeg12.c +SOURCE rtp_pck_mpeg4.c +SOURCE rtsp_command.c +SOURCE rtsp_common.c +SOURCE rtsp_response.c +SOURCE rtsp_session.c +SOURCE sdp.c + +//BIFS +SOURCEPATH ..\..\src\bifs +SOURCE arith_decoder.c +SOURCE bifs_codec.c +SOURCE bifs_node_tables.c +SOURCE com_dec.c +SOURCE com_enc.c +SOURCE conditional.c +SOURCE field_decode.c +SOURCE field_encode.c +SOURCE memory_decoder.c +SOURCE predictive_mffield.c +SOURCE quantize.c +SOURCE script_dec.c +SOURCE script_enc.c +SOURCE unquantize.c + +//LASeR +SOURCEPATH ..\..\src\laser +SOURCE lsr_enc.c +SOURCE lsr_dec.c +SOURCE lsr_tables.c + +//IsoMedia +SOURCEPATH ..\..\src\isomedia +SOURCE avc_ext.c +SOURCE box_code_3gpp.c +SOURCE box_code_apple.c +SOURCE box_code_base.c +SOURCE box_code_isma.c +SOURCE box_code_meta.c +SOURCE box_dump.c +SOURCE box_funcs.c +SOURCE data_map.c +SOURCE hint_track.c +SOURCE hinting.c +SOURCE isma_sample.c +SOURCE isom_intern.c +SOURCE isom_read.c +SOURCE isom_store.c +SOURCE isom_write.c +SOURCE media.c +SOURCE media_odf.c +SOURCE meta.c +SOURCE movie_fragments.c +SOURCE sample_descs.c +SOURCE stbl_read.c +SOURCE stbl_write.c +SOURCE track.c +SOURCE tx3g.c + +//OD Framework +SOURCEPATH ..\..\src\odf +SOURCE desc_private.c +SOURCE descriptors.c +SOURCE ipmpx_code.c +SOURCE ipmpx_dump.c +SOURCE ipmpx_parse.c +SOURCE oci_codec.c +SOURCE odf_code.c +SOURCE odf_codec.c +SOURCE odf_command.c +SOURCE odf_dump.c +SOURCE odf_parse.c +SOURCE qos.c +SOURCE slc.c + +//scenegraph +SOURCEPATH ..\..\src\scenegraph +SOURCE base_scenegraph.c +SOURCE mpeg4_animators.c +SOURCE commands.c +SOURCE mpeg4_nodes.c +SOURCE mpeg4_valuator.c +SOURCE vrml_interpolators.c +SOURCE vrml_proto.c +SOURCE vrml_route.c +SOURCE vrml_script.c +SOURCE vrml_smjs.c +SOURCE vrml_tools.c +SOURCE x3d_nodes.c +SOURCE smil_anim.c +SOURCE smil_timing.c +SOURCE dom_events.c +SOURCE dom_smjs.c +SOURCE svg_attributes.c +SOURCE svg_types.c +SOURCE svg_smjs.c +SOURCE svg_properties.c +SOURCE xbl_process.c +SOURCE xml_ns.c + +//mcrypt +SOURCEPATH ..\..\src\mcrypt +SOURCE cbc.c +SOURCE cfb.c +SOURCE ctr.c +SOURCE des.c +SOURCE ecb.c +SOURCE g_crypt.c +SOURCE ncfb.c +SOURCE nofb.c +SOURCE ofb.c +SOURCE rijndael-128.c +SOURCE rijndael-192.c +SOURCE rijndael-256.c +SOURCE stream.c +SOURCE tripledes.c +SOURCE sha1.c + +//media tools +SOURCEPATH ..\..\src\media_tools +SOURCE av_parsers.c +SOURCE avilib.c +SOURCE gpac_ogg.c +SOURCE img.c +SOURCE ismacryp.c +SOURCE isom_hinter.c +SOURCE isom_tools.c +SOURCE media_export.c +SOURCE media_import.c +SOURCE mpeg2_ps.c +SOURCE text_import.c +SOURCE saf.c +SOURCE mpegts.c +SOURCE vobsub.c + + +//scene manager +SOURCEPATH ..\..\src\scene_manager +SOURCE loader_bt.c +SOURCE loader_isom.c +SOURCE loader_qt.c +SOURCE loader_xmt.c +SOURCE scene_dump.c +SOURCE scene_manager.c +SOURCE scene_stats.c +SOURCE swf_bifs.c +SOURCE swf_parse.c +SOURCE text_to_bifs.c +SOURCE encode_cbk.c +SOURCE encode_isom.c +SOURCE loader_svg.c + +//terminal +SOURCEPATH ..\..\src\terminal +SOURCE channel.c +SOURCE clock.c +SOURCE decoder.c +SOURCE term_node_init.c +SOURCE inline.c +SOURCE input_sensor.c +SOURCE media_control.c +SOURCE media_manager.c +SOURCE media_memory.c +SOURCE media_object.c +SOURCE media_sensor.c +SOURCE network_service.c +SOURCE object_browser.c +SOURCE object_manager.c +SOURCE terminal.c +SOURCE svg_external.c + +//compositor +SOURCEPATH ..\..\src\compositor +SOURCE audio_input.c +SOURCE audio_mixer.c +SOURCE audio_render.c +SOURCE bindable.c +SOURCE camera.c +SOURCE compositor.c +SOURCE compositor_2d.c +SOURCE compositor_3d.c +SOURCE compositor_node_init.c +SOURCE drawable.c +SOURCE events.c +SOURCE font_engine.c +SOURCE hardcoded_protos.c +SOURCE mesh.c +SOURCE mesh_collide.c +SOURCE mesh_tesselate.c +SOURCE mpeg4_animstream.c +SOURCE mpeg4_audio.c +SOURCE mpeg4_background.c +SOURCE mpeg4_background2d.c +SOURCE mpeg4_bitmap.c +SOURCE mpeg4_composite.c +SOURCE mpeg4_form.c +SOURCE mpeg4_geometry_2d.c +SOURCE mpeg4_geometry_3d.c +SOURCE mpeg4_geometry_ifs2d.c +SOURCE mpeg4_geometry_ils2d.c +SOURCE mpeg4_gradients.c +SOURCE mpeg4_grouping.c +SOURCE mpeg4_grouping_2d.c +SOURCE mpeg4_grouping_3d.c +SOURCE mpeg4_layer_2d.c +SOURCE mpeg4_layer_3d.c +SOURCE mpeg4_layout.c +SOURCE mpeg4_lighting.c +SOURCE mpeg4_path_layout.c +SOURCE mpeg4_sensors.c +SOURCE mpeg4_sound.c +SOURCE mpeg4_text.c +SOURCE mpeg4_textures.c +SOURCE mpeg4_timesensor.c +SOURCE mpeg4_viewport.c +SOURCE navigate.c +SOURCE offscreen_cache.c +SOURCE svg_base.c +SOURCE svg_font.c +SOURCE svg_geometry.c +SOURCE svg_grouping.c +SOURCE svg_media.c +SOURCE svg_paint_servers.c +SOURCE svg_text.c +SOURCE texturing.c +SOURCE texturing_gl.c +SOURCE visual_manager.c +SOURCE visual_manager_2d.c +SOURCE visual_manager_2d_draw.c +SOURCE visual_manager_3d.c +SOURCE visual_manager_3d_gl.c +SOURCE x3d_geometry.c + +SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\..\include + +LIBRARY euser.lib estlib.lib ezlib.lib hal.lib + +#if defined(WINS) + deffile ..\..\build\symbian\libgpac_symbian.def +#else if defined(ARM) + deffile ..\..\build\symbian\libgpac_symbian.def +#endif + +//EXPORTUNFROZEN +// nostrictdef + diff --git a/build/symbian/libgpac_symbianU.def b/build/symbian/libgpac_symbianU.def new file mode 100644 index 0000000..3bd8506 --- /dev/null +++ b/build/symbian/libgpac_symbianU.def @@ -0,0 +1,903 @@ +EXPORTS + AVI_close @ 1 NONAME + AVI_open_output_file @ 2 NONAME + AVI_set_video @ 3 NONAME + AVI_write_frame @ 4 NONAME + gf_4cc_to_str @ 5 NONAME + gf_acos @ 6 NONAME + gf_angle_diff @ 7 NONAME + gf_asin @ 8 NONAME + gf_atan2 @ 9 NONAME + gf_avc_get_profile_name @ 10 NONAME + gf_avc_get_sps_info @ 11 NONAME + gf_base16_decode @ 12 NONAME + gf_base16_encode @ 13 NONAME + gf_base64_decode @ 14 NONAME + gf_base64_encode @ 15 NONAME + gf_bbox_equal @ 16 NONAME + gf_bbox_from_rect @ 17 NONAME + gf_bbox_get_vertices @ 18 NONAME + gf_bbox_grow_point @ 19 NONAME + gf_bbox_plane_relation @ 20 NONAME + gf_bbox_point_inside @ 21 NONAME + gf_bbox_refresh @ 22 NONAME + gf_bbox_union @ 23 NONAME + gf_beng_aggregate_context @ 24 NONAME + gf_beng_encode_context @ 25 NONAME + gf_beng_encode_from_file @ 26 NONAME + gf_beng_encode_from_string @ 27 NONAME + gf_beng_get_stream_config @ 28 NONAME + gf_beng_init @ 29 NONAME + gf_beng_init_from_context @ 30 NONAME + gf_beng_init_from_string @ 31 NONAME + gf_beng_save_context @ 32 NONAME + gf_beng_terminate @ 33 NONAME + gf_bifs_decode_au @ 34 NONAME + gf_bifs_decode_command_list @ 35 NONAME + gf_bifs_decoder_configure_stream @ 36 NONAME + gf_bifs_decoder_del @ 37 NONAME + gf_bifs_decoder_ignore_size_info @ 38 NONAME + gf_bifs_decoder_new @ 39 NONAME + gf_bifs_decoder_remove_stream @ 40 NONAME + gf_bifs_encode_au @ 41 NONAME + gf_bifs_encoder_del @ 42 NONAME + gf_bifs_encoder_get_config @ 43 NONAME + gf_bifs_encoder_get_rap @ 44 NONAME + gf_bifs_encoder_get_version @ 45 NONAME + gf_bifs_encoder_new @ 46 NONAME + gf_bifs_encoder_new_stream @ 47 NONAME + gf_bifs_get_aq_info @ 48 NONAME + gf_bs_align @ 49 NONAME + gf_bs_available @ 50 NONAME + gf_bs_bits_available @ 51 NONAME + gf_bs_del @ 52 NONAME + gf_bs_from_file @ 53 NONAME + gf_bs_get_bit_offset @ 54 NONAME + gf_bs_get_bit_position @ 55 NONAME + gf_bs_get_content @ 56 NONAME + gf_bs_get_position @ 57 NONAME + gf_bs_get_refreshed_size @ 58 NONAME + gf_bs_get_size @ 59 NONAME + gf_bs_new @ 60 NONAME + gf_bs_peek_bits @ 61 NONAME + gf_bs_read_bit @ 62 NONAME + gf_bs_read_data @ 63 NONAME + gf_bs_read_double @ 64 NONAME + gf_bs_read_float @ 65 NONAME + gf_bs_read_int @ 66 NONAME + gf_bs_read_long_int @ 67 NONAME + gf_bs_read_u16 @ 68 NONAME + gf_bs_read_u16_le @ 69 NONAME + gf_bs_read_u24 @ 70 NONAME + gf_bs_read_u32 @ 71 NONAME + gf_bs_read_u32_le @ 72 NONAME + gf_bs_read_u64 @ 73 NONAME + gf_bs_read_u8 @ 74 NONAME + gf_bs_rewind_bits @ 75 NONAME + gf_bs_seek @ 76 NONAME + gf_bs_set_eos_callback @ 77 NONAME + gf_bs_skip_bytes @ 78 NONAME + gf_bs_write_data @ 79 NONAME + gf_bs_write_double @ 80 NONAME + gf_bs_write_float @ 81 NONAME + gf_bs_write_int @ 82 NONAME + gf_bs_write_long_int @ 83 NONAME + gf_bs_write_u16 @ 84 NONAME + gf_bs_write_u16_le @ 85 NONAME + gf_bs_write_u24 @ 86 NONAME + gf_bs_write_u32 @ 87 NONAME + gf_bs_write_u32_le @ 88 NONAME + gf_bs_write_u64 @ 89 NONAME + gf_bs_write_u8 @ 90 NONAME + gf_ceil @ 91 NONAME + gf_cfg_del @ 92 NONAME + gf_cfg_del_section @ 93 NONAME + gf_cfg_get_key @ 94 NONAME + gf_cfg_get_key_count @ 95 NONAME + gf_cfg_get_key_name @ 96 NONAME + gf_cfg_get_section_count @ 97 NONAME + gf_cfg_get_section_name @ 98 NONAME + gf_cfg_insert_key @ 99 NONAME + gf_cfg_new @ 100 NONAME + gf_cfg_save @ 101 NONAME + gf_cfg_set_key @ 102 NONAME + gf_closest_point_to_line @ 103 NONAME + gf_cmx_apply @ 104 NONAME + gf_cmx_apply_fixed @ 105 NONAME + gf_cmx_copy @ 106 NONAME + gf_cmx_init @ 107 NONAME + gf_cmx_multiply @ 108 NONAME + gf_cmx_set @ 109 NONAME + gf_cmx_set_all @ 110 NONAME + gf_cos @ 111 NONAME + gf_crc_32 @ 112 NONAME + gf_crypt_close @ 113 NONAME + gf_crypt_decrypt @ 114 NONAME + gf_crypt_encrypt @ 115 NONAME + gf_crypt_init @ 116 NONAME + gf_crypt_open @ 117 NONAME + gf_crypt_set_state @ 118 NONAME + gf_delete_file @ 119 NONAME + gf_divfix @ 120 NONAME + gf_dm_sess_fetch_data @ 121 NONAME + gf_dm_sess_get_cache_name @ 122 NONAME + gf_dm_sess_get_stats @ 123 NONAME + gf_dom_event_fire @ 124 NONAME + gf_dom_event_fire_ex @ 125 NONAME + gf_dom_listener_build @ 126 NONAME + gf_dom_listener_count @ 127 NONAME + gf_dom_listener_get @ 128 NONAME + gf_enum_directory @ 129 NONAME + gf_error_to_string @ 130 NONAME + gf_f64_open @ 131 NONAME + gf_f64_seek @ 132 NONAME + gf_f64_tell @ 133 NONAME + gf_floor @ 134 NONAME + gf_get_next_pow2 @ 135 NONAME + gf_get_user_name @ 136 NONAME + gf_hinter_can_embbed_data @ 137 NONAME + gf_hinter_finalize @ 138 NONAME + gf_hinter_track_del @ 139 NONAME + gf_hinter_track_finalize @ 140 NONAME + gf_hinter_track_get_bandwidth @ 141 NONAME + gf_hinter_track_get_flags @ 142 NONAME + gf_hinter_track_get_payload_name @ 143 NONAME + gf_hinter_track_new @ 144 NONAME + gf_hinter_track_process @ 145 NONAME + gf_img_jpeg_dec @ 146 NONAME + gf_img_parse @ 147 NONAME + gf_img_png_dec @ 148 NONAME + gf_inline_attach_to_compositor @ 149 NONAME + gf_inline_default_scene_viewpoint @ 150 NONAME + gf_inline_disconnect @ 151 NONAME + gf_inline_find_odm @ 152 NONAME + gf_inline_force_scene_size @ 153 NONAME + gf_inline_get_proto_lib @ 154 NONAME + gf_inline_get_time @ 155 NONAME + gf_inline_process_anchor @ 156 NONAME + gf_inline_register_extra_graph @ 157 NONAME + gf_inline_set_duration @ 158 NONAME + gf_inline_setup_object @ 159 NONAME + gf_invfix @ 160 NONAME + gf_ismacryp_crypt_file @ 161 NONAME + gf_ismacryp_decrypt_file @ 162 NONAME + gf_ismacryp_decrypt_track @ 163 NONAME + gf_ismacryp_encrypt_track @ 164 NONAME + gf_ismacryp_gpac_get_info @ 165 NONAME + gf_ismacryp_mpeg4ip_get_info @ 166 NONAME + gf_isom_3gp_config_get @ 167 NONAME + gf_isom_3gp_config_new @ 168 NONAME + gf_isom_ac3_config_new @ 169 NONAME + gf_isom_add_sample @ 170 NONAME + gf_isom_add_track_to_root_od @ 171 NONAME + gf_isom_apple_get_tag @ 172 NONAME + gf_isom_avc_config_get @ 173 NONAME + gf_isom_check_data_reference @ 174 NONAME + gf_isom_close @ 175 NONAME + gf_isom_delete @ 176 NONAME + gf_isom_delete_text_sample @ 177 NONAME + gf_isom_dump @ 178 NONAME + gf_isom_dump_hint_sample @ 179 NONAME + gf_isom_find_od_for_track @ 180 NONAME + gf_isom_get_alternate_brand @ 181 NONAME + gf_isom_get_audio_info @ 182 NONAME + gf_isom_get_brand_info @ 183 NONAME + gf_isom_get_chapter @ 184 NONAME + gf_isom_get_chapter_count @ 185 NONAME + gf_isom_get_copyright @ 186 NONAME + gf_isom_get_copyright_count @ 187 NONAME + gf_isom_get_creation_time @ 188 NONAME + gf_isom_get_data_reference @ 189 NONAME + gf_isom_get_decoder_config @ 190 NONAME + gf_isom_get_duration @ 191 NONAME + gf_isom_get_edit_segment @ 192 NONAME + gf_isom_get_edit_segment_count @ 193 NONAME + gf_isom_get_esd @ 194 NONAME + gf_isom_get_filename @ 195 NONAME + gf_isom_get_fragment_defaults @ 196 NONAME + gf_isom_get_generic_sample_description @ 197 NONAME + gf_isom_get_handler_name @ 198 NONAME + gf_isom_get_ismacryp_info @ 199 NONAME + gf_isom_get_ismacryp_sample @ 200 NONAME + gf_isom_get_max_chunk_duration @ 201 NONAME + gf_isom_get_media_duration @ 202 NONAME + gf_isom_get_media_language @ 203 NONAME + gf_isom_get_media_subtype @ 204 NONAME + gf_isom_get_media_time @ 205 NONAME + gf_isom_get_media_timescale @ 206 NONAME + gf_isom_get_media_type @ 207 NONAME + gf_isom_get_meta_item_by_id @ 208 NONAME + gf_isom_get_meta_item_count @ 209 NONAME + gf_isom_get_meta_item_info @ 210 NONAME + gf_isom_get_meta_type @ 211 NONAME + gf_isom_get_missing_bytes @ 212 NONAME + gf_isom_get_mode @ 213 NONAME + gf_isom_get_mpeg4_subtype @ 214 NONAME + gf_isom_get_omadrm_info @ 215 NONAME + gf_isom_get_payt_count @ 216 NONAME + gf_isom_get_payt_info @ 217 NONAME + gf_isom_get_pixel_aspect_ratio @ 218 NONAME + gf_isom_get_pl_indication @ 219 NONAME + gf_isom_get_reference @ 220 NONAME + gf_isom_get_reference_count @ 221 NONAME + gf_isom_get_root_od @ 222 NONAME + gf_isom_get_sample @ 223 NONAME + gf_isom_get_sample_count @ 224 NONAME + gf_isom_get_sample_description_count @ 225 NONAME + gf_isom_get_sample_description_index @ 226 NONAME + gf_isom_get_sample_dts @ 227 NONAME + gf_isom_get_sample_duration @ 228 NONAME + gf_isom_get_sample_for_media_time @ 229 NONAME + gf_isom_get_sample_for_movie_time @ 230 NONAME + gf_isom_get_sample_fragment_count @ 231 NONAME + gf_isom_get_sample_fragment_size @ 232 NONAME + gf_isom_get_sample_info @ 233 NONAME + gf_isom_get_sync_point_count @ 234 NONAME + gf_isom_get_timed_meta_data_info @ 235 NONAME + gf_isom_get_timescale @ 236 NONAME + gf_isom_get_track_by_id @ 237 NONAME + gf_isom_get_track_count @ 238 NONAME + gf_isom_get_track_duration @ 239 NONAME + gf_isom_get_track_id @ 240 NONAME + gf_isom_get_track_layout_info @ 241 NONAME + gf_isom_get_track_switch_group_count @ 242 NONAME + gf_isom_get_track_switch_parameter @ 243 NONAME + gf_isom_get_user_data @ 244 NONAME + gf_isom_get_user_data_count @ 245 NONAME + gf_isom_get_visual_info @ 246 NONAME + gf_isom_get_watermark @ 247 NONAME + gf_isom_guess_specification @ 248 NONAME + gf_isom_has_meta_xml @ 249 NONAME + gf_isom_has_movie @ 250 NONAME + gf_isom_has_padding_bits @ 251 NONAME + gf_isom_has_root_od @ 252 NONAME + gf_isom_has_sample_dependency @ 253 NONAME + gf_isom_has_sync_points @ 254 NONAME + gf_isom_has_sync_shadows @ 255 NONAME + gf_isom_has_time_offset @ 256 NONAME + gf_isom_is_JPEG2000 @ 257 NONAME + gf_isom_is_fragmented @ 258 NONAME + gf_isom_is_ismacryp_media @ 259 NONAME + gf_isom_is_media_encrypted @ 260 NONAME + gf_isom_is_omadrm_media @ 261 NONAME + gf_isom_is_self_contained @ 262 NONAME + gf_isom_is_single_av @ 263 NONAME + gf_isom_is_track_enabled @ 264 NONAME + gf_isom_is_track_encrypted @ 265 NONAME + gf_isom_is_track_fragmented @ 266 NONAME + gf_isom_is_track_in_root_od @ 267 NONAME + gf_isom_ismacryp_delete_sample @ 268 NONAME + gf_isom_last_error @ 269 NONAME + gf_isom_modify_cts_offset @ 270 NONAME + gf_isom_new_mpeg4_description @ 271 NONAME + gf_isom_new_track @ 272 NONAME + gf_isom_next_hint_packet @ 273 NONAME + gf_isom_open @ 274 NONAME + gf_isom_open_progressive @ 275 NONAME + gf_isom_parse_texte_sample @ 276 NONAME + gf_isom_probe_file @ 277 NONAME + gf_isom_refresh_fragmented @ 278 NONAME + gf_isom_reset_hint_reader @ 279 NONAME + gf_isom_reset_switch_parameters @ 280 NONAME + gf_isom_reset_track_switch_parameter @ 281 NONAME + gf_isom_sample_del @ 282 NONAME + gf_isom_sample_new @ 283 NONAME + gf_isom_sdp_get @ 284 NONAME + gf_isom_sdp_track_get @ 285 NONAME + gf_isom_set_JPEG2000 @ 286 NONAME + gf_isom_set_alternate_group_id @ 287 NONAME + gf_isom_set_default_sync_track @ 288 NONAME + gf_isom_set_sample_padding @ 289 NONAME + gf_isom_set_track_enabled @ 290 NONAME + gf_isom_set_track_switch_parameter @ 291 NONAME + gf_isom_text_dump @ 292 NONAME + gf_isom_text_set_streaming_mode @ 293 NONAME + gf_laser_decode_au @ 294 NONAME + gf_laser_decoder_configure_stream @ 295 NONAME + gf_laser_decoder_del @ 296 NONAME + gf_laser_decoder_new @ 297 NONAME + gf_laser_decoder_remove_stream @ 298 NONAME + gf_laser_decoder_set_clock @ 299 NONAME + gf_list_add @ 300 NONAME + gf_list_count @ 301 NONAME + gf_list_del @ 302 NONAME + gf_list_del_item @ 303 NONAME + gf_list_enum @ 304 NONAME + gf_list_find @ 305 NONAME + gf_list_get @ 306 NONAME + gf_list_insert @ 307 NONAME + gf_list_last @ 308 NONAME + gf_list_new @ 309 NONAME + gf_list_rem @ 310 NONAME + gf_list_rem_last @ 311 NONAME + gf_list_reset @ 312 NONAME + gf_log @ 313 NONAME + gf_log_get_level @ 314 NONAME + gf_log_get_tools @ 315 NONAME + gf_log_lt @ 316 NONAME + gf_log_set_callback @ 317 NONAME + gf_log_set_level @ 318 NONAME + gf_log_set_tools @ 319 NONAME + gf_m2ts_decode_mjd_date @ 320 NONAME + gf_m4a_get_config @ 321 NONAME + gf_m4a_get_profile_name @ 322 NONAME + gf_m4a_object_type_name @ 323 NONAME + gf_m4a_parse_config @ 324 NONAME + gf_m4a_write_config @ 325 NONAME + gf_m4v_get_config @ 326 NONAME + gf_m4v_get_object_start @ 327 NONAME + gf_m4v_get_profile_name @ 328 NONAME + gf_m4v_is_valid_object_type @ 329 NONAME + gf_m4v_parse_config @ 330 NONAME + gf_m4v_parse_frame @ 331 NONAME + gf_m4v_parser_del @ 332 NONAME + gf_m4v_parser_new @ 333 NONAME + gf_m4v_rewrite_pl @ 334 NONAME + gf_media_change_par @ 335 NONAME + gf_media_export @ 336 NONAME + gf_media_fragment_file @ 337 NONAME + gf_media_get_file_hash @ 338 NONAME + gf_media_import @ 339 NONAME + gf_media_import_chapters @ 340 NONAME + gf_media_make_3gpp @ 341 NONAME + gf_media_make_isma @ 342 NONAME + gf_media_map_esd @ 343 NONAME + gf_mo_adjust_clock @ 344 NONAME + gf_mo_fetch_data @ 345 NONAME + gf_mo_get_audio_info @ 346 NONAME + gf_mo_get_duration @ 347 NONAME + gf_mo_get_flags @ 348 NONAME + gf_mo_get_last_frame_time @ 349 NONAME + gf_mo_get_loop @ 350 NONAME + gf_mo_get_object_time @ 351 NONAME + gf_mo_get_scenegraph @ 352 NONAME + gf_mo_get_speed @ 353 NONAME + gf_mo_get_visual_info @ 354 NONAME + gf_mo_has_audio @ 355 NONAME + gf_mo_is_done @ 356 NONAME + gf_mo_is_muted @ 357 NONAME + gf_mo_pause @ 358 NONAME + gf_mo_play @ 359 NONAME + gf_mo_register @ 360 NONAME + gf_mo_release_data @ 361 NONAME + gf_mo_restart @ 362 NONAME + gf_mo_resume @ 363 NONAME + gf_mo_set_flag @ 364 NONAME + gf_mo_set_speed @ 365 NONAME + gf_mo_should_deactivate @ 366 NONAME + gf_mo_stop @ 367 NONAME + gf_mo_url_changed @ 368 NONAME + gf_modules_close_interface @ 369 NONAME + gf_modules_del @ 370 NONAME + gf_modules_get_config @ 371 NONAME + gf_modules_get_count @ 372 NONAME + gf_modules_get_file_name @ 373 NONAME + gf_modules_get_option @ 374 NONAME + gf_modules_load_interface @ 375 NONAME + gf_modules_load_interface_by_name @ 376 NONAME + gf_modules_new @ 377 NONAME + gf_modules_refresh @ 378 NONAME + gf_modules_set_option @ 379 NONAME + gf_mp3_frame_size @ 380 NONAME + gf_mp3_get_next_header @ 381 NONAME + gf_mp3_get_next_header_mem @ 382 NONAME + gf_mp3_layer @ 383 NONAME + gf_mp3_num_channels @ 384 NONAME + gf_mp3_object_type_indication @ 385 NONAME + gf_mp3_sampling_rate @ 386 NONAME + gf_mp3_version @ 387 NONAME + gf_mp3_version_name @ 388 NONAME + gf_mp3_window_size @ 389 NONAME + gf_muldiv @ 390 NONAME + gf_mulfix @ 391 NONAME + gf_mx2d_add_matrix @ 392 NONAME + gf_mx2d_add_rotation @ 393 NONAME + gf_mx2d_add_scale @ 394 NONAME + gf_mx2d_add_scale_at @ 395 NONAME + gf_mx2d_add_skew @ 396 NONAME + gf_mx2d_add_skew_x @ 397 NONAME + gf_mx2d_add_skew_y @ 398 NONAME + gf_mx2d_add_translation @ 399 NONAME + gf_mx2d_apply_coords @ 400 NONAME + gf_mx2d_apply_point @ 401 NONAME + gf_mx2d_apply_rect @ 402 NONAME + gf_mx2d_from_mx @ 403 NONAME + gf_mx2d_inverse @ 404 NONAME + gf_mx2d_pre_multiply @ 405 NONAME + gf_mx_add_matrix @ 406 NONAME + gf_mx_add_matrix_2d @ 407 NONAME + gf_mx_add_matrix_4x4 @ 408 NONAME + gf_mx_add_rotation @ 409 NONAME + gf_mx_add_scale @ 410 NONAME + gf_mx_add_translation @ 411 NONAME + gf_mx_apply_bbox @ 412 NONAME + gf_mx_apply_bbox_sphere @ 413 NONAME + gf_mx_apply_plane @ 414 NONAME + gf_mx_apply_ray @ 415 NONAME + gf_mx_apply_rect @ 416 NONAME + gf_mx_apply_vec @ 417 NONAME + gf_mx_apply_vec_4x4 @ 418 NONAME + gf_mx_decompose @ 419 NONAME + gf_mx_del @ 420 NONAME + gf_mx_equal @ 421 NONAME + gf_mx_from_mx2d @ 422 NONAME + gf_mx_inverse @ 423 NONAME + gf_mx_inverse_4x4 @ 424 NONAME + gf_mx_lookat @ 425 NONAME + gf_mx_new @ 426 NONAME + gf_mx_ortho @ 427 NONAME + gf_mx_p @ 428 NONAME + gf_mx_perspective @ 429 NONAME + gf_mx_rotate_vector @ 430 NONAME + gf_mx_rotation_matrix_from_vectors @ 431 NONAME + gf_mx_try_lock @ 432 NONAME + gf_mx_v @ 433 NONAME + gf_node_allow_cyclic_traverse @ 434 NONAME + gf_node_changed @ 435 NONAME + gf_node_clone @ 436 NONAME + gf_node_dirty_clear @ 437 NONAME + gf_node_dirty_get @ 438 NONAME + gf_node_dirty_parents @ 439 NONAME + gf_node_dirty_reset @ 440 NONAME + gf_node_dirty_set @ 441 NONAME + gf_node_event_out_str @ 442 NONAME + gf_node_get_attribute_by_tag @ 443 NONAME + gf_node_get_class_name @ 444 NONAME + gf_node_get_field @ 445 NONAME + gf_node_get_field_count @ 446 NONAME + gf_node_get_graph @ 447 NONAME + gf_node_get_id @ 448 NONAME + gf_node_get_name @ 449 NONAME + gf_node_get_name_and_id @ 450 NONAME + gf_node_get_parent @ 451 NONAME + gf_node_get_parent_count @ 452 NONAME + gf_node_get_private @ 453 NONAME + gf_node_get_proto @ 454 NONAME + gf_node_get_proto_parent @ 455 NONAME + gf_node_get_proto_root @ 456 NONAME + gf_node_get_scene_time @ 457 NONAME + gf_node_get_tag @ 458 NONAME + gf_node_init @ 459 NONAME + gf_node_is_proto_root @ 460 NONAME + gf_node_list_add_child @ 461 NONAME + gf_node_list_add_child_last @ 462 NONAME + gf_node_list_del_child @ 463 NONAME + gf_node_list_del_child_idx @ 464 NONAME + gf_node_list_find_child @ 465 NONAME + gf_node_list_get_child @ 466 NONAME + gf_node_list_get_count @ 467 NONAME + gf_node_list_insert_child @ 468 NONAME + gf_node_new @ 469 NONAME + gf_node_register @ 470 NONAME + gf_node_register_iri @ 471 NONAME + gf_node_remove_id @ 472 NONAME + gf_node_set_callback_function @ 473 NONAME + gf_node_set_id @ 474 NONAME + gf_node_set_private @ 475 NONAME + gf_node_store_embedded_data @ 476 NONAME + gf_node_traverse @ 477 NONAME + gf_node_traverse_children @ 478 NONAME + gf_node_unregister @ 479 NONAME + gf_node_unregister_children @ 480 NONAME + gf_oci_codec_add_event @ 481 NONAME + gf_oci_codec_decode @ 482 NONAME + gf_oci_codec_del @ 483 NONAME + gf_oci_codec_encode @ 484 NONAME + gf_oci_codec_get_event @ 485 NONAME + gf_oci_codec_new @ 486 NONAME + gf_oci_dump_au @ 487 NONAME + gf_oci_dump_event @ 488 NONAME + gf_oci_event_add_desc @ 489 NONAME + gf_oci_event_del @ 490 NONAME + gf_oci_event_get_desc @ 491 NONAME + gf_oci_event_get_desc_count @ 492 NONAME + gf_oci_event_get_duration @ 493 NONAME + gf_oci_event_get_id @ 494 NONAME + gf_oci_event_get_start_time @ 495 NONAME + gf_oci_event_new @ 496 NONAME + gf_oci_event_rem_desc @ 497 NONAME + gf_oci_event_set_duration @ 498 NONAME + gf_oci_event_set_start_time @ 499 NONAME + gf_odf_avc_cfg_del @ 500 NONAME + gf_odf_avc_cfg_new @ 501 NONAME + gf_odf_avc_cfg_read @ 502 NONAME + gf_odf_avc_cfg_write @ 503 NONAME + gf_odf_codec_add_com @ 504 NONAME + gf_odf_codec_decode @ 505 NONAME + gf_odf_codec_del @ 506 NONAME + gf_odf_codec_encode @ 507 NONAME + gf_odf_codec_get_au @ 508 NONAME + gf_odf_codec_get_com @ 509 NONAME + gf_odf_codec_new @ 510 NONAME + gf_odf_codec_set_au @ 511 NONAME + gf_odf_com_del @ 512 NONAME + gf_odf_com_new @ 513 NONAME + gf_odf_desc_add_desc @ 514 NONAME + gf_odf_desc_copy @ 515 NONAME + gf_odf_desc_del @ 516 NONAME + gf_odf_desc_esd_new @ 517 NONAME + gf_odf_desc_list_del @ 518 NONAME + gf_odf_desc_list_read @ 519 NONAME + gf_odf_desc_list_size @ 520 NONAME + gf_odf_desc_list_write @ 521 NONAME + gf_odf_desc_new @ 522 NONAME + gf_odf_desc_read @ 523 NONAME + gf_odf_desc_size @ 524 NONAME + gf_odf_desc_write @ 525 NONAME + gf_odf_dump_au @ 526 NONAME + gf_odf_dump_com @ 527 NONAME + gf_odf_dump_com_list @ 528 NONAME + gf_odf_dump_desc @ 529 NONAME + gf_odf_encode_ui_config @ 530 NONAME + gf_odf_get_bifs_config @ 531 NONAME + gf_odf_get_laser_config @ 532 NONAME + gf_odf_get_text_config @ 533 NONAME + gf_odf_get_ui_config @ 534 NONAME + gf_odf_parse_descriptor @ 535 NONAME + gf_odf_qos_add_qualif @ 536 NONAME + gf_odf_qos_del @ 537 NONAME + gf_odf_qos_new @ 538 NONAME + gf_odf_slc_set_pref @ 539 NONAME + gf_odf_stream_type_by_name @ 540 NONAME + gf_odf_stream_type_name @ 541 NONAME + gf_odm_disconnect @ 542 NONAME + gf_odm_new @ 543 NONAME + gf_odm_remove_es @ 544 NONAME + gf_odm_setup_es @ 545 NONAME + gf_odm_setup_object @ 546 NONAME + gf_path_add_arc @ 547 NONAME + gf_path_add_arc_to @ 548 NONAME + gf_path_add_bezier @ 549 NONAME + gf_path_add_cubic_to @ 550 NONAME + gf_path_add_cubic_to_vec @ 551 NONAME + gf_path_add_ellipse @ 552 NONAME + gf_path_add_line_to @ 553 NONAME + gf_path_add_line_to_vec @ 554 NONAME + gf_path_add_move_to @ 555 NONAME + gf_path_add_move_to_vec @ 556 NONAME + gf_path_add_quadratic_to @ 557 NONAME + gf_path_add_quadratic_to_vec @ 558 NONAME + gf_path_add_rect @ 559 NONAME + gf_path_add_rect_center @ 560 NONAME + gf_path_add_svg_arc_to @ 561 NONAME + gf_path_clone @ 562 NONAME + gf_path_close @ 563 NONAME + gf_path_del @ 564 NONAME + gf_path_flatten @ 565 NONAME + gf_path_get_bounds @ 566 NONAME + gf_path_get_control_bounds @ 567 NONAME + gf_path_get_flatten @ 568 NONAME + gf_path_get_outline @ 569 NONAME + gf_path_is_empty @ 570 NONAME + gf_path_iterator_del @ 571 NONAME + gf_path_iterator_get_length @ 572 NONAME + gf_path_iterator_get_transform @ 573 NONAME + gf_path_iterator_new @ 574 NONAME + gf_path_new @ 575 NONAME + gf_path_point_over @ 576 NONAME + gf_path_reset @ 577 NONAME + gf_plane_exists_intersection @ 578 NONAME + gf_plane_get_distance @ 579 NONAME + gf_plane_get_p_vertex_idx @ 580 NONAME + gf_plane_intersect_line @ 581 NONAME + gf_plane_intersect_plane @ 582 NONAME + gf_plane_intersect_planes @ 583 NONAME + gf_polygone2d_get_convexity @ 584 NONAME + gf_quat_from_axis_cos @ 585 NONAME + gf_quat_from_matrix @ 586 NONAME + gf_quat_from_rotation @ 587 NONAME + gf_quat_get_inv @ 588 NONAME + gf_quat_multiply @ 589 NONAME + gf_quat_rotate @ 590 NONAME + gf_quat_slerp @ 591 NONAME + gf_quat_to_rotation @ 592 NONAME + gf_rand @ 593 NONAME + gf_rand_init @ 594 NONAME + gf_ray @ 595 NONAME + gf_ray_hit_box @ 596 NONAME + gf_ray_hit_sphere @ 597 NONAME + gf_ray_hit_triangle @ 598 NONAME + gf_ray_hit_triangle_backcull @ 599 NONAME + gf_rect_center @ 600 NONAME + gf_rect_equal @ 601 NONAME + gf_rect_from_bbox @ 602 NONAME + gf_rect_overlaps @ 603 NONAME + gf_rect_pixelize @ 604 NONAME + gf_rect_union @ 605 NONAME + gf_rtp_builder_del @ 606 NONAME + gf_rtp_builder_format_sdp @ 607 NONAME + gf_rtp_builder_get_payload_name @ 608 NONAME + gf_rtp_builder_init @ 609 NONAME + gf_rtp_builder_new @ 610 NONAME + gf_rtp_builder_process @ 611 NONAME + gf_rtp_decode_rtcp @ 612 NONAME + gf_rtp_decode_rtp @ 613 NONAME + gf_rtp_del @ 614 NONAME + gf_rtp_depacketizer_del @ 615 NONAME + gf_rtp_depacketizer_get_slconfig @ 616 NONAME + gf_rtp_depacketizer_new @ 617 NONAME + gf_rtp_depacketizer_process @ 618 NONAME + gf_rtp_depacketizer_reset @ 619 NONAME + gf_rtp_enable_nat_keepalive @ 620 NONAME + gf_rtp_get_clockrate @ 621 NONAME + gf_rtp_get_current_time @ 622 NONAME + gf_rtp_get_hight_interleave_id @ 623 NONAME + gf_rtp_get_local_ssrc @ 624 NONAME + gf_rtp_get_loss @ 625 NONAME + gf_rtp_get_low_interleave_id @ 626 NONAME + gf_rtp_get_ports @ 627 NONAME + gf_rtp_get_tcp_bytes_sent @ 628 NONAME + gf_rtp_get_transport @ 629 NONAME + gf_rtp_initialize @ 630 NONAME + gf_rtp_is_active @ 631 NONAME + gf_rtp_is_interleaved @ 632 NONAME + gf_rtp_is_unicast @ 633 NONAME + gf_rtp_new @ 634 NONAME + gf_rtp_read_rtcp @ 635 NONAME + gf_rtp_read_rtp @ 636 NONAME + gf_rtp_reset_buffers @ 637 NONAME + gf_rtp_send_bye @ 638 NONAME + gf_rtp_send_packet @ 639 NONAME + gf_rtp_send_rtcp_report @ 640 NONAME + gf_rtp_set_info_rtcp @ 641 NONAME + gf_rtp_set_info_rtp @ 642 NONAME + gf_rtp_set_ports @ 643 NONAME + gf_rtp_setup_payload @ 644 NONAME + gf_rtp_setup_transport @ 645 NONAME + gf_rtsp_command_del @ 646 NONAME + gf_rtsp_command_new @ 647 NONAME + gf_rtsp_command_reset @ 648 NONAME + gf_rtsp_generate_session_id @ 649 NONAME + gf_rtsp_get_command @ 650 NONAME + gf_rtsp_get_last_request @ 651 NONAME + gf_rtsp_get_last_session_id @ 652 NONAME + gf_rtsp_get_next_interleave_id @ 653 NONAME + gf_rtsp_get_remote_address @ 654 NONAME + gf_rtsp_get_response @ 655 NONAME + gf_rtsp_get_server_name @ 656 NONAME + gf_rtsp_get_service_name @ 657 NONAME + gf_rtsp_get_session_ip @ 658 NONAME + gf_rtsp_get_session_port @ 659 NONAME + gf_rtsp_get_session_state @ 660 NONAME + gf_rtsp_is_my_session @ 661 NONAME + gf_rtsp_load_service_name @ 662 NONAME + gf_rtsp_nc_to_string @ 663 NONAME + gf_rtsp_range_del @ 664 NONAME + gf_rtsp_range_new @ 665 NONAME + gf_rtsp_range_parse @ 666 NONAME + gf_rtsp_register_interleave @ 667 NONAME + gf_rtsp_reset_aggregation @ 668 NONAME + gf_rtsp_response_del @ 669 NONAME + gf_rtsp_response_new @ 670 NONAME + gf_rtsp_response_reset @ 671 NONAME + gf_rtsp_send_command @ 672 NONAME + gf_rtsp_send_response @ 673 NONAME + gf_rtsp_session_del @ 674 NONAME + gf_rtsp_session_new @ 675 NONAME + gf_rtsp_session_new_server @ 676 NONAME + gf_rtsp_session_read @ 677 NONAME + gf_rtsp_session_reset @ 678 NONAME + gf_rtsp_set_buffer_size @ 679 NONAME + gf_rtsp_set_interleave_callback @ 680 NONAME + gf_rtsp_set_mobile_ip @ 681 NONAME + gf_rtsp_transport_clone @ 682 NONAME + gf_rtsp_transport_del @ 683 NONAME + gf_rtsp_unregister_interleave @ 684 NONAME + gf_sc_audio_check_url @ 685 NONAME + gf_sc_audio_open @ 686 NONAME + gf_sc_audio_register @ 687 NONAME + gf_sc_audio_restart @ 688 NONAME + gf_sc_audio_setup @ 689 NONAME + gf_sc_audio_stop @ 690 NONAME + gf_sc_audio_unregister @ 691 NONAME + gf_sc_get_compositor @ 692 NONAME + gf_sc_invalidate @ 693 NONAME + gf_sc_lock @ 694 NONAME + gf_sc_reset_graphics @ 695 NONAME + gf_sc_svg_convert_length_to_display @ 696 NONAME + gf_sc_texture_check_url_change @ 697 NONAME + gf_sc_texture_destroy @ 698 NONAME + gf_sc_texture_get_handler @ 699 NONAME + gf_sc_texture_play @ 700 NONAME + gf_sc_texture_play_from_to @ 701 NONAME + gf_sc_texture_release_stream @ 702 NONAME + gf_sc_texture_restart @ 703 NONAME + gf_sc_texture_setup @ 704 NONAME + gf_sc_texture_stop @ 705 NONAME + gf_sc_texture_update_frame @ 706 NONAME + gf_sdp_conn_del @ 707 NONAME + gf_sdp_conn_new @ 708 NONAME + gf_sdp_fmtp_del @ 709 NONAME + gf_sdp_fmtp_new @ 710 NONAME + gf_sdp_info_check @ 711 NONAME + gf_sdp_info_del @ 712 NONAME + gf_sdp_info_new @ 713 NONAME + gf_sdp_info_parse @ 714 NONAME + gf_sdp_info_reset @ 715 NONAME + gf_sdp_info_write @ 716 NONAME + gf_sdp_media_del @ 717 NONAME + gf_sdp_media_new @ 718 NONAME + gf_sema_del @ 719 NONAME + gf_sema_new @ 720 NONAME + gf_sema_notify @ 721 NONAME + gf_sema_wait @ 722 NONAME + gf_sema_wait_for @ 723 NONAME + gf_set_progress @ 724 NONAME + gf_set_progress_callback @ 725 NONAME + gf_sg_activate_routes @ 726 NONAME + gf_sg_command_apply @ 727 NONAME + gf_sg_command_apply_list @ 728 NONAME + gf_sg_command_del @ 729 NONAME + gf_sg_del @ 730 NONAME + gf_sg_find_node @ 731 NONAME + gf_sg_find_node_by_name @ 732 NONAME + gf_sg_get_next_available_node_id @ 733 NONAME + gf_sg_get_root_node @ 734 NONAME + gf_sg_get_scene_size_info @ 735 NONAME + gf_sg_mfurl_del @ 736 NONAME + gf_sg_new_subscene @ 737 NONAME + gf_sg_node_get_tag_by_class_name @ 738 NONAME + gf_sg_proto_get_extern_url @ 739 NONAME + gf_sg_reset @ 740 NONAME + gf_sg_route_new @ 741 NONAME + gf_sg_set_node_callback @ 742 NONAME + gf_sg_set_proto_loader @ 743 NONAME + gf_sg_set_root_node @ 744 NONAME + gf_sg_set_scene_size_info @ 745 NONAME + gf_sg_set_scene_time_callback @ 746 NONAME + gf_sg_sfcolor_to_rgba @ 747 NONAME + gf_sg_sfrotation_interpolate @ 748 NONAME + gf_sg_use_pixel_metrics @ 749 NONAME + gf_sg_vrml_field_copy @ 750 NONAME + gf_sg_vrml_field_equal @ 751 NONAME + gf_sg_vrml_mf_alloc @ 752 NONAME + gf_sg_vrml_mf_append @ 753 NONAME + gf_sg_vrml_mf_reset @ 754 NONAME + gf_sin @ 755 NONAME + gf_sk_is_multicast_address @ 756 NONAME + gf_sl_depacketize @ 757 NONAME + gf_sl_get_header_size @ 758 NONAME + gf_sl_packetize @ 759 NONAME + gf_sleep @ 760 NONAME + gf_sm_del @ 761 NONAME + gf_sm_dump @ 762 NONAME + gf_sm_dump_command_list @ 763 NONAME + gf_sm_dump_graph @ 764 NONAME + gf_sm_dumper_del @ 765 NONAME + gf_sm_dumper_new @ 766 NONAME + gf_sm_encode_to_file @ 767 NONAME + gf_sm_import_bifs_subtitle @ 768 NONAME + gf_sm_load_done @ 769 NONAME + gf_sm_load_init @ 770 NONAME + gf_sm_load_run @ 771 NONAME + gf_sm_load_string @ 772 NONAME + gf_sm_make_random_access @ 773 NONAME + gf_sm_new @ 774 NONAME + gf_sm_reset @ 775 NONAME + gf_sm_stats_del @ 776 NONAME + gf_sm_stats_for_command @ 777 NONAME + gf_sm_stats_for_graph @ 778 NONAME + gf_sm_stats_for_scene @ 779 NONAME + gf_sm_stats_get @ 780 NONAME + gf_sm_stats_new @ 781 NONAME + gf_sm_stats_reset @ 782 NONAME + gf_sm_stream_au_new @ 783 NONAME + gf_sm_stream_del @ 784 NONAME + gf_sm_stream_new @ 785 NONAME + gf_smil_set_media_duration @ 786 NONAME + gf_smil_timing_init_runtime_info @ 787 NONAME + gf_smil_timing_insert_clock @ 788 NONAME + gf_smil_timing_is_active @ 789 NONAME + gf_sqrt @ 790 NONAME + gf_stretch_bits @ 791 NONAME + gf_svg_apply_animations @ 792 NONAME + gf_svg_apply_inheritance @ 793 NONAME + gf_svg_attributes_copy @ 794 NONAME + gf_svg_flatten_attributes @ 795 NONAME + gf_svg_path_build @ 796 NONAME + gf_svg_properties_init_pointers @ 797 NONAME + gf_svg_properties_reset_pointers @ 798 NONAME + gf_sys_clock @ 799 NONAME + gf_sys_close @ 800 NONAME + gf_sys_get_battery_state @ 801 NONAME + gf_sys_get_rti @ 802 NONAME + gf_sys_init @ 803 NONAME + gf_tan @ 804 NONAME + gf_temp_file_new @ 805 NONAME + gf_term_add_media @ 806 NONAME + gf_term_add_object @ 807 NONAME + gf_term_check_extension @ 808 NONAME + gf_term_connect @ 809 NONAME + gf_term_connect_from_time @ 810 NONAME + gf_term_del @ 811 NONAME + gf_term_disconnect @ 812 NONAME + gf_term_download_del @ 813 NONAME + gf_term_download_new @ 814 NONAME + gf_term_download_update_stats @ 815 NONAME + gf_term_get_framerate @ 816 NONAME + gf_term_get_mfurl_from_xlink @ 817 NONAME + gf_term_get_option @ 818 NONAME + gf_term_get_service_interface @ 819 NONAME + gf_term_get_service_url @ 820 NONAME + gf_term_get_simulation_frame_rate @ 821 NONAME + gf_term_get_time_in_ms @ 822 NONAME + gf_term_get_url @ 823 NONAME + gf_term_get_viewpoint @ 824 NONAME + gf_term_navigate_to @ 825 NONAME + gf_term_new @ 826 NONAME + gf_term_node_callback @ 827 NONAME + gf_term_on_command @ 828 NONAME + gf_term_on_connect @ 829 NONAME + gf_term_on_disconnect @ 830 NONAME + gf_term_on_message @ 831 NONAME + gf_term_on_sl_packet @ 832 NONAME + gf_term_play_from_time @ 833 NONAME + gf_term_process_flush @ 834 NONAME + gf_term_process_step @ 835 NONAME + gf_term_register_mime_type @ 836 NONAME + gf_term_scene_update @ 837 NONAME + gf_term_set_option @ 838 NONAME + gf_term_set_simulation_frame_rate @ 839 NONAME + gf_term_set_size @ 840 NONAME + gf_term_set_viewpoint @ 841 NONAME + gf_term_user_event @ 842 NONAME + gf_th_del @ 843 NONAME + gf_th_id @ 844 NONAME + gf_th_new @ 845 NONAME + gf_th_run @ 846 NONAME + gf_th_set_priority @ 847 NONAME + gf_th_status @ 848 NONAME + gf_th_stop @ 849 NONAME + gf_token_find @ 850 NONAME + gf_token_get @ 851 NONAME + gf_token_get_line @ 852 NONAME + gf_url_to_fs_path @ 853 NONAME + gf_utc_time_since_1970 @ 854 NONAME + gf_utf8_is_right_to_left @ 855 NONAME + gf_utf8_mbstowcs @ 856 NONAME + gf_utf8_reorder_bidi @ 857 NONAME + gf_utf8_wcslen @ 858 NONAME + gf_utf8_wcstombs @ 859 NONAME + gf_v2d_from_polar @ 860 NONAME + gf_v2d_len @ 861 NONAME + gf_v2d_polarize @ 862 NONAME + gf_vec_cross @ 863 NONAME + gf_vec_dot @ 864 NONAME + gf_vec_len @ 865 NONAME + gf_vec_lensq @ 866 NONAME + gf_vec_norm @ 867 NONAME + gf_vec_scale @ 868 NONAME + gf_vorbis_check_frame @ 869 NONAME + gf_vorbis_parse_header @ 870 NONAME + gf_xml_dom_del @ 871 NONAME + gf_xml_dom_detach_root @ 872 NONAME + gf_xml_dom_get_error @ 873 NONAME + gf_xml_dom_get_line @ 874 NONAME + gf_xml_dom_get_root @ 875 NONAME + gf_xml_dom_new @ 876 NONAME + gf_xml_dom_node_del @ 877 NONAME + gf_xml_dom_parse @ 878 NONAME + gf_xml_dom_serialize @ 879 NONAME + gf_xml_get_root_type @ 880 NONAME + gf_xml_sax_binary_file @ 881 NONAME + gf_xml_sax_del @ 882 NONAME + gf_xml_sax_get_error @ 883 NONAME + gf_xml_sax_get_file_pos @ 884 NONAME + gf_xml_sax_get_file_size @ 885 NONAME + gf_xml_sax_get_line @ 886 NONAME + gf_xml_sax_get_node_end_pos @ 887 NONAME + gf_xml_sax_get_node_start_pos @ 888 NONAME + gf_xml_sax_init @ 889 NONAME + gf_xml_sax_new @ 890 NONAME + gf_xml_sax_parse @ 891 NONAME + gf_xml_sax_parse_file @ 892 NONAME + gf_xml_sax_peek_node @ 893 NONAME + gf_xml_sax_suspend @ 894 NONAME + gzclose @ 895 NONAME + gzdopen @ 896 NONAME + gzeof @ 897 NONAME + gzopen @ 898 NONAME + gzread @ 899 NONAME + my_str_lwr @ 900 NONAME + my_str_upr @ 901 NONAME + diff --git a/build/symbian/module_base.mmp b/build/symbian/module_base.mmp new file mode 100644 index 0000000..ef0aacf --- /dev/null +++ b/build/symbian/module_base.mmp @@ -0,0 +1,24 @@ +TARGETTYPE DLL +CAPABILITY NetworkServices +UID 0x10000080 0x10000081 +//allow writable static data +EPOCALLOWDLLDATA + +//Symbian 8 needs E32Dll proc +#if defined(THUMB) +SOURCEPATH . +SOURCE symbian_dll_entry.cpp +#endif + +SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\..\include +LIBRARY euser.lib estlib.lib libgpac.lib + +#if defined(WINS) + deffile ..\..\build\symbian\gpac_module_symbian.def +#else if defined(ARM) + deffile ..\..\build\symbian\gpac_module_symbian.def +#endif + +//EXPORTUNFROZEN +// nostrictdef + diff --git a/build/symbian/mp3_in.mmp b/build/symbian/mp3_in.mmp new file mode 100644 index 0000000..e610b63 --- /dev/null +++ b/build/symbian/mp3_in.mmp @@ -0,0 +1,22 @@ +TARGET gm_mp3_in.dll + + +//comment if you don't have libmad +MACRO GPAC_HAS_MAD +MACRO FPM_ARM +STATICLIBRARY libmad.lib +SYSTEMINCLUDE ..\..\extra_lib\include\mad + + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\mp3_in +SOURCE mp3_in.c +SOURCE mad_dec.c + +#if defined(GCCE) +#else +LIBRARY egcc.lib +#endif diff --git a/build/symbian/odf_dec.mmp b/build/symbian/odf_dec.mmp new file mode 100644 index 0000000..57745a9 --- /dev/null +++ b/build/symbian/odf_dec.mmp @@ -0,0 +1,9 @@ +TARGET gm_odf_dec.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\odf_dec +SOURCE odf_dec.c + diff --git a/build/symbian/osmo4.mmp b/build/symbian/osmo4.mmp new file mode 100644 index 0000000..82d8ba9 --- /dev/null +++ b/build/symbian/osmo4.mmp @@ -0,0 +1,90 @@ + +/*config for symbian 9.x*/ +#if defined(GCCE) + +TARGET Osmo4.exe +TARGETTYPE EXE +SECUREID 0xf01f9075 +UID 0 0xf01f9075 +VENDORID 0 + +CAPABILITY NetworkServices +EPOCSTACKSIZE 0x8000 +/*up to 8M for gpac - this should be tuned per device !!*/ +EPOCHEAPSIZE 0x4000 0x800000 + +#else +/*config for symbian 8.x*/ + +TARGET Osmo4.app +TARGETTYPE app +UID 0x100039CE 0x1000AC00 +TARGETPATH \system\apps\Osmo4 + +EPOCALLOWDLLDATA + +#endif + +//MACRO GPAC_GUI_ONLY + +/*source code*/ +SOURCEPATH ..\..\applications\osmo4_sym +SOURCE osmo4.cpp +SOURCE osmo4_view.cpp +SOURCE osmo4_ui.cpp +SOURCE playlist.cpp + + + +/*resource files*/ +SOURCEPATH ..\..\applications\osmo4_sym\res + +#if defined(GCCE) + +START RESOURCE Osmo4.rss +HEADER +TARGETPATH resource\apps +END //RESOURCE + +START RESOURCE Osmo4_reg.rss +#ifdef WINSCW +TARGETPATH \private\10003a3f\apps +#else +TARGETPATH \private\10003a3f\import\apps +#endif +END //RESOURCE + +#else + +RESOURCE Osmo4_gen.rss +RESOURCE Osmo4_caption.rss + +#endif + + +USERINCLUDE ..\..\applications\osmo4_sym ..\..\applications\osmo4_sym\res + +SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\..\include + +LIBRARY euser.lib apparc.lib cone.lib eikcore.lib avkon.lib efsrv.lib estlib.lib ws32.lib bafl.lib eikcoctl.lib + +//gpac core lib +LIBRARY libgpac.lib + +//when using skin support +LIBRARY aknskins.lib + +#if defined(GCCE) +//hardware key listener stuff +LIBRARY remconcoreapi.lib remconinterfacebase.lib +#endif + +LANG SC + +#if defined(GCCE) +#else +AIF Osmo4.aif ..\..\applications\osmo4_sym\aif osmo4aif.rss c12 osmo4_icon.bmp osmo4_icon_mask.bmp osmo4_menu.bmp osmo4_menu_mask.bmp +#endif + + +// End of File diff --git a/build/symbian/rtp_in.mmp b/build/symbian/rtp_in.mmp new file mode 100644 index 0000000..f9b26c1 --- /dev/null +++ b/build/symbian/rtp_in.mmp @@ -0,0 +1,14 @@ +TARGET gm_rtp_in.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\rtp_in +SOURCE rtp_in.c +SOURCE rtp_session.c +SOURCE rtp_signaling.c +SOURCE rtp_stream.c +SOURCE sdp_fetch.c +SOURCE sdp_load.c + diff --git a/build/symbian/saf_in.mmp b/build/symbian/saf_in.mmp new file mode 100644 index 0000000..20e2a45 --- /dev/null +++ b/build/symbian/saf_in.mmp @@ -0,0 +1,9 @@ +TARGET gm_saf_in.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\saf_in +SOURCE saf_in.c + diff --git a/build/symbian/sis/GPAC.cfg b/build/symbian/sis/GPAC.cfg new file mode 100644 index 0000000..33c0f9c --- /dev/null +++ b/build/symbian/sis/GPAC.cfg @@ -0,0 +1,26 @@ +[General] + +[SymbianDLLs] +gm_aac_in.dll=yes +gm_amr_in.dll=yes +gm_bifs_dec.dll=yes +gm_ctx_load.dll=yes +gm_dummy_in.dll=yes +gm_epoc_hw.dll=yes +gm_ffmpeg_in.dll=yes +gm_ft_font.dll=yes +gm_img_in.dll=yes +gm_ismacryp.dll=yes +gm_isom_in.dll=yes +gm_laser_dec.dll=yes +gm_mp3_in.dll=yes +gm_odf_dec.dll=yes +gm_rtp_in.dll=yes +gm_saf_in.dll=yes +gm_soft_raster.dll=yes +gm_svg_in.dll=yes +gm_timedtext.dll=yes +gm_xvid_dec.dll=yes + +[FontEngine] +FontDirectory=Z:\resource\Fonts diff --git a/build/symbian/sis/backup_registration.xml b/build/symbian/sis/backup_registration.xml new file mode 100644 index 0000000..7f7f2d0 --- /dev/null +++ b/build/symbian/sis/backup_registration.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/build/symbian/sis/gpac.cer b/build/symbian/sis/gpac.cer new file mode 100644 index 0000000..6505cb4 --- /dev/null +++ b/build/symbian/sis/gpac.cer @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC8TCCArGgAwIBAgIJALotnRibD/q8MAkGByqGSM44BAMwHDELMAkGA1UEBhMC +RlIxDTALBgNVBAoTBEVOU1QwHhcNMDgwMTA5MDk1ODMyWhcNMTgwMTA2MDk1ODMy +WjAcMQswCQYDVQQGEwJGUjENMAsGA1UEChMERU5TVDCCAbcwggEsBgcqhkjOOAQB +MIIBHwKBgQD8NiO0JTmjTjCNpeUsR28xkuI1ovrdrGsgMKA+x4Y1D8jY6NBFlYO4 +GaPXCWchzEla6iu35bhdXlCatrokMB4FDk4pTOG/Wtx70RCer2A3ltHbit672/br +1Ghlg0jpxgztcQSMZeSEzX2pp0Hvo4aFLbhjMx2XqY2CwdyQ/XqN0wIVAKMBlvbu +Uk1ee0PwlbIirRsW9btfAoGBANYTR+sHCbDLADj80UB1UojCCYnWDuZBpGsqSrr4 +13JAn4MEeYuhIj0TGUW08gsFVXBVRnYEbwOdNl4NYH3oL7gxr7pn8BZknY39xxW+ +5fcyuMxw7+SNn8enw9Eso2pkwhhHhe4nhOlvOrvK6XA+dKHj7oQbIkqaZQ8iCU0s +M+a6A4GEAAKBgDwMWunf+ZnUlO0UE+f33+ZFbwHNiaBF0hKA+JrggZNL5+IgOQ2o +1+nbrZE3Iyfh0FrAsqYow9lMzXhtNoR5Hzk0LFUL3ZLQ7srZ1kSwUhu5A0Kkfpkg +lMjc/rzp2qlPARlMLg0Qb3Z5RHAFY/9BJpuk9XsZox6w9QskjhBWzBs5o30wezAd +BgNVHQ4EFgQU39NHanijACvhGtxSNx5bCUz6g4EwTAYDVR0jBEUwQ4AU39NHanij +ACvhGtxSNx5bCUz6g4GhIKQeMBwxCzAJBgNVBAYTAkZSMQ0wCwYDVQQKEwRFTlNU +ggkAui2dGJsP+rwwDAYDVR0TBAUwAwEB/zAJBgcqhkjOOAQDAy8AMCwCFAvs9AOw +8SaBmbjmVpd5KCEVj3qzAhQWmEA0EA9udo7ft2obyPN6YuIu/A== +-----END CERTIFICATE----- diff --git a/build/symbian/sis/gpac.key b/build/symbian/sis/gpac.key new file mode 100644 index 0000000..b026426 --- /dev/null +++ b/build/symbian/sis/gpac.key @@ -0,0 +1,15 @@ +-----BEGIN DSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,F9E9529C03B12703 + +XxBWvC122vJe3OiL0R+6tV6v4NKn0WB1rMYG8yW523PFmAXB9SgquJjXWLqPwsFl +tXnk4g2jDDCgJBbFmb2DJ4/n2G2Gmh1+QpG2ul8R6MWwdwYu9+amNMvi7JddkRlS +SZbJF79np4oiJp/f4FVkjlpXHFvPkFWhR21tEe1HNQ9US54JDHVwBcqvSUOm6qHI +oQaz1uvISz9S4H7/4itYGcFk0bguX8nftFJJRWWEPlJ8pi2q8TpPb6K89PHZrfBZ +ylhppnmb4cAZytq3KseVn9ku6twDNYczCG3GyrcreqOAYE6OSJcFHBDygYUGJWhL +lD6KmG5Q5hniYSXm9ZjTd4mx07PE24GfH0Rdi3fw634wOV2g/04wmhtYn1214Ytw +cFxyU/b5VMLBJ7O8kVUpjKzdsHosfQ1Uk2RIjrXrclKFhCgL9ZOUznFvn+C9HUDF +qCiwM/+t8fRYQKcMm6ZfYBnk2q28xWm2SYbd7O5u78+hyd5Hj1+N+YDlIyLkrn4V +38O6brMyUR9D9J9SAqTJzKj66yGtTX8dYOqTYqpYwhyLTtjKdJqcShr6qwasVFGB +H3Z1ypU+Psaa3e/7xyoWLA== +-----END DSA PRIVATE KEY----- diff --git a/build/symbian/sis/osmo4_gcce.pkg b/build/symbian/sis/osmo4_gcce.pkg new file mode 100644 index 0000000..7d383ac --- /dev/null +++ b/build/symbian/sis/osmo4_gcce.pkg @@ -0,0 +1,54 @@ +; Osmo4.pkg +; +;Language - standard language definitions +&EN + +; standard SIS file header +#{"Osmo4"},(0xF01F9075),0,43,20061002 + +;Localised Vendor name +%{"ENST-EN"} + +;Unique Vendor name +:"ENST" + +;Supports Series 60 v 3.0 +[0x101F7961], 0, 0, 0, {"Series60ProductID"} + +;Files to install +"Epoc32\release\GCCE\UREL\Osmo4.exe" -"c:\sys\bin\Osmo4.exe" +"Epoc32\release\GCCE\UREL\libgpac.dll" -"c:\sys\bin\libgpac.dll" +"Epoc32\Data\z\resource\apps\Osmo4.rsc" -"c:\resource\apps\Osmo4.rsc" +"Epoc32\Data\z\private\10003a3f\import\apps\Osmo4_reg.rsc" -"c:\private\10003a3f\import\apps\Osmo4_reg.rsc" +"Epoc32\Data\z\resource\apps\Osmo4_aif.mif" -"c:\resource\apps\Osmo4_aif.mif" +;config file +"GPAC.cfg" -"c:\private\F01F9075\GPAC.cfg" +;startup file +"..\..\..\doc\gpac.mp4" -"c:\private\F01F9075\gpac.mp4" +;cache setup +"" -"c:\private\F01F9075\cache\", FN +;modules: on S60 3rd, librairies shall be in /sys/bin to load ... +"Epoc32\release\GCCE\UREL\gm_aac_in.dll" -"c:\sys\bin\gm_aac_in.dll" +"Epoc32\release\GCCE\UREL\gm_amr_in.dll" -"c:\sys\bin\gm_amr_in.dll" +"Epoc32\release\GCCE\UREL\gm_bifs_dec.dll" -"c:\sys\bin\gm_bifs_dec.dll" +"Epoc32\release\GCCE\UREL\gm_ctx_load.dll" -"c:\sys\bin\gm_ctx_load.dll" +"Epoc32\release\GCCE\UREL\gm_dummy_in.dll" -"c:\sys\bin\gm_dummy_in.dll" +"Epoc32\release\GCCE\UREL\gm_epoc_hw.dll" -"c:\sys\bin\gm_epoc_hw.dll" +"Epoc32\release\GCCE\UREL\gm_ffmpeg_in.dll" -"c:\sys\bin\gm_ffmpeg_in.dll" +"Epoc32\release\GCCE\UREL\gm_ft_font.dll" -"c:\sys\bin\gm_ft_font.dll" +"Epoc32\release\GCCE\UREL\gm_img_in.dll" -"c:\sys\bin\gm_img_in.dll" +"Epoc32\release\GCCE\UREL\gm_ismacryp.dll" -"c:\sys\bin\gm_ismacryp.dll" +"Epoc32\release\GCCE\UREL\gm_isom_in.dll" -"c:\sys\bin\gm_isom_in.dll" +"Epoc32\release\GCCE\UREL\gm_laser_dec.dll" -"c:\sys\bin\gm_laser_dec.dll" +"Epoc32\release\GCCE\UREL\gm_mp3_in.dll" -"c:\sys\bin\gm_mp3_in.dll" +"Epoc32\release\GCCE\UREL\gm_odf_dec.dll" -"c:\sys\bin\gm_odf_dec.dll" +"Epoc32\release\GCCE\UREL\gm_rtp_in.dll" -"c:\sys\bin\gm_rtp_in.dll" +"Epoc32\release\GCCE\UREL\gm_saf_in.dll" -"c:\sys\bin\gm_saf_in.dll" +"Epoc32\release\GCCE\UREL\gm_soft_raster.dll" -"c:\sys\bin\gm_soft_raster.dll" +"Epoc32\release\GCCE\UREL\gm_svg_in.dll" -"c:\sys\bin\gm_svg_in.dll" +"Epoc32\release\GCCE\UREL\gm_timedtext.dll" -"c:\sys\bin\gm_timedtext.dll" +"Epoc32\release\GCCE\UREL\gm_xvid_dec.dll" -"c:\sys\bin\gm_xvid_dec.dll" + +;required for application to be covered by backup/restore facility +;"backup_registration.xml" -"c:\private\F01F9075\backup_registration.xml" + diff --git a/build/symbian/sis/osmo4_thumb.pkg b/build/symbian/sis/osmo4_thumb.pkg new file mode 100644 index 0000000..4ef762e --- /dev/null +++ b/build/symbian/sis/osmo4_thumb.pkg @@ -0,0 +1,48 @@ + +; Osmo4.pkg +; +;Language - standard language definitions +&EN + +; standard SIS file header +#{"Osmo4"},(0x1000AC00),0,4,5 + +;Supports Series 60 v2.0 +(0x101F7960), 0, 0, 0, {"Series60ProductID"} + +;GUI +"Epoc32\release\thumb\urel\Osmo4.APP" -"!:\system\apps\Osmo4\Osmo4.app" +"Epoc32\data\z\system\apps\Osmo4\Osmo4_gen.rsc" -"!:\system\apps\Osmo4\Osmo4.rsc" +"Epoc32\data\z\system\apps\Osmo4\Osmo4_caption.rsc" -"!:\system\apps\Osmo4\Osmo4_caption.rsc" +"Epoc32\data\z\system\apps\Osmo4\Osmo4.aif" -"!:\system\apps\Osmo4\Osmo4.aif" + +;GPAC lib, default cfg file, startup file & cache +"Epoc32\release\thumb\urel\libgpac.dll" -"!:\system\apps\Osmo4\libgpac.dll" +"GPAC.cfg" -"!:\system\apps\Osmo4\GPAC.cfg" +"..\..\..\doc\gpac.mp4" -"!:\system\apps\Osmo4\gpac.mp4" +"" -"!:\system\apps\Osmo4\cache\", FN + +;modules: +"Epoc32\release\thumb\urel\gm_aac_in.dll" -"!:\system\apps\Osmo4\gm_aac_in.dll" +"Epoc32\release\thumb\urel\gm_amr_in.dll" -"!:\system\apps\Osmo4\gm_amr_in.dll" +"Epoc32\release\thumb\urel\gm_bifs_dec.dll" -"!:\system\apps\Osmo4\gm_bifs_dec.dll" +"Epoc32\release\thumb\urel\gm_ctx_load.dll" -"!:\system\apps\Osmo4\gm_ctx_load.dll" +"Epoc32\release\thumb\urel\gm_dummy_in.dll" -"!:\system\apps\Osmo4\gm_dummy_in.dll" +"Epoc32\release\thumb\urel\gm_epoc_hw.dll" -"!:\system\apps\Osmo4\gm_epoc_hw.dll" +;"Epoc32\release\thumb\urel\gm_ffmpeg_in.dll" -"!:\system\apps\Osmo4\gm_ffmpeg_in.dll" +"Epoc32\release\thumb\urel\gm_ft_font.dll" -"!:\system\apps\Osmo4\gm_ft_font.dll" +"Epoc32\release\thumb\urel\gm_img_in.dll" -"!:\system\apps\Osmo4\gm_img_in.dll" +"Epoc32\release\thumb\urel\gm_ismacryp.dll" -"!:\system\apps\Osmo4\gm_ismacryp.dll" +"Epoc32\release\thumb\urel\gm_isom_in.dll" -"!:\system\apps\Osmo4\gm_isom_in.dll" +"Epoc32\release\thumb\urel\gm_laser_dec.dll" -"!:\system\apps\Osmo4\gm_laser_dec.dll" +"Epoc32\release\thumb\urel\gm_mp3_in.dll" -"!:\system\apps\Osmo4\gm_mp3_in.dll" +"Epoc32\release\thumb\urel\gm_odf_dec.dll" -"!:\system\apps\Osmo4\gm_odf_dec.dll" +"Epoc32\release\thumb\urel\gm_render2d.dll" -"!:\system\apps\Osmo4\gm_render2d.dll" +"Epoc32\release\thumb\urel\gm_render3d.dll" -"!:\system\apps\Osmo4\gm_render3d.dll" +"Epoc32\release\thumb\urel\gm_rtp_in.dll" -"!:\system\apps\Osmo4\gm_rtp_in.dll" +"Epoc32\release\thumb\urel\gm_saf_in.dll" -"!:\system\apps\Osmo4\gm_saf_in.dll" +"Epoc32\release\thumb\urel\gm_soft_raster.dll" -"!:\system\apps\Osmo4\gm_soft_raster.dll" +"Epoc32\release\thumb\urel\gm_svg_in.dll" -"!:\system\apps\Osmo4\gm_svg_in.dll" +"Epoc32\release\thumb\urel\gm_timedtext.dll" -"!:\system\apps\Osmo4\gm_timedtext.dll" +"Epoc32\release\thumb\urel\gm_xvid_dec.dll" -"!:\system\apps\Osmo4\gm_xvid_dec.dll" + diff --git a/build/symbian/soft_raster.mmp b/build/symbian/soft_raster.mmp new file mode 100644 index 0000000..14faf2c --- /dev/null +++ b/build/symbian/soft_raster.mmp @@ -0,0 +1,15 @@ +TARGET gm_soft_raster.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\soft_raster +SOURCE ftgrays.c +SOURCE raster_load.c +SOURCE raster_565.c +SOURCE raster_argb.c +SOURCE raster_rgb.c +SOURCE stencil.c +SOURCE surface.c + diff --git a/build/symbian/svg_in.mmp b/build/symbian/svg_in.mmp new file mode 100644 index 0000000..6919c2d --- /dev/null +++ b/build/symbian/svg_in.mmp @@ -0,0 +1,10 @@ +TARGET gm_svg_in.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\svg_in +SOURCE svg_in.c + +LIBRARY ezlib.lib diff --git a/build/symbian/symbian_dll_entry.cpp b/build/symbian/symbian_dll_entry.cpp new file mode 100644 index 0000000..f5b7278 --- /dev/null +++ b/build/symbian/symbian_dll_entry.cpp @@ -0,0 +1,7 @@ + +#include +#include + +#ifndef EKA2 +GLDEF_C s32 E32Dll(TDllReason /*aReason*/) { return 0; } +#endif diff --git a/build/symbian/timedtext.mmp b/build/symbian/timedtext.mmp new file mode 100644 index 0000000..8ffd2cb --- /dev/null +++ b/build/symbian/timedtext.mmp @@ -0,0 +1,9 @@ +TARGET gm_timedtext.dll + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\timedtext +SOURCE timedtext_in.c +SOURCE timedtext_dec.c diff --git a/build/symbian/xvid_dec.mmp b/build/symbian/xvid_dec.mmp new file mode 100644 index 0000000..c16ed12 --- /dev/null +++ b/build/symbian/xvid_dec.mmp @@ -0,0 +1,34 @@ +TARGET gm_xvid_dec.dll + +MACRO __MARM__ +MACRO __MARM_ARMI__ + +//common config of all modules +#include ".\module_base.mmp" + +//source +SOURCEPATH ..\..\modules\xvid_dec +SOURCE xvid_dec_wce.cpp +SOURCEPATH ..\..\modules\xvid_dec\xvid_wce +SOURCE bitstream.cpp +SOURCE CodecAPI.cpp +SOURCE decoder.cpp +SOURCE font.cpp +SOURCE gmc.cpp +SOURCE idct.cpp +SOURCE image.cpp +SOURCE interpolate8x8.cpp +SOURCE mbcoding.cpp +SOURCE mbprediction.cpp +SOURCE mem_align.cpp +SOURCE mem_transfer.cpp +SOURCE qpel_tab.cpp +SOURCE quant_h263.cpp +SOURCE quant_matrix.cpp +SOURCE quant_mpeg.cpp +SOURCE reduced.cpp +SOURCE xvid.cpp +//SOURCE xvid_ppc.asm + + +SYSTEMINCLUDE ..\..\modules\xvid_dec\xvid_wce diff --git a/config.h b/config.h new file mode 100644 index 0000000..6fc2775 --- /dev/null +++ b/config.h @@ -0,0 +1,9 @@ +/* Automatically generated by configure */ +#ifndef GF_CONFIG_H +#define GF_CONFIG_H +#define GPAC_CONFIG_LINUX +#define GPAC_HAS_SPIDERMONKEY +#define GPAC_HAS_JPEG +#define GPAC_HAS_PNG +#define GPAC_HAS_IPV6 +#endif diff --git a/configure b/configure new file mode 100755 index 0000000..3dce906 --- /dev/null +++ b/configure @@ -0,0 +1,1810 @@ +#!/bin/sh +# +# GPAC MPEG-4 SDK configure script (c) 2003-2005 Jean Le Feuvre +# inspired from ffmpeg configure by Fabrice Bellard (c) 2000-2002 +#set -v +# set temporary file name +if test ! -z "$TMPDIR" ; then + TMPDIR1="${TMPDIR}" +elif test ! -z "$TEMPDIR" ; then + TMPDIR1="${TEMPDIR}" +else + TMPDIR1="/tmp" +fi + +TMPC="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.c" +TMPH="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.h" +TMPCPP="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.cpp" +TMPE="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}" +TMPO="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.o" +TMPS="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.S" + +# default parameters +DESTDIR="" +prefix="/usr/local" +mandir="" +cross_prefix="" +dxsdk_path="" +moz_path="local" +cc_orig="gcc" +cc="${cc_orig}" +ar="ar" +ranlib="ranlib" +make="make" +strip="strip" +cpu=`uname -m` +debuginfo="no" +sdl_path="" +sdl_local="no" +sdl_static="no" +xulsdk_path="/usr/lib/xulrunner/sdk/include" +xul_flags="" +libdir="lib" + +#GPAC module config +js_flags="XP_UNIX" +js_lib="-ljs" +iso_fragments="yes" +make_readonly="no" +has_mingw_directx="no" +has_js="no" +has_ft="no" +has_jpeg="no" +has_png="no" +has_xvid="no" +has_mad="no" +has_faad="no" +has_ffmpeg="no" +has_amr_nb_fixed="no" +has_amr_nb="no" +has_amr_wb="no" +has_ogg="no" +has_vorbis="no" +has_theora="no" +has_a52="no" +has_pulseaudio="no" +has_oss_audio="no" +has_alsa="no" +has_jack="no" +has_x11="no" +has_x11_shm="no" +has_x11_xv="no" +disable_svg="no" +no_gcc_opt="no" +use_fixed_point="no" +use_memory_tracking="no" +has_opengl="no" +disable_opengl="no" +has_tinygl="no" +enable_tinygl="no" +has_ssl="no" +has_ipv6="no" +has_dvb4linux="no" +has_xmlrpc="no" +has_openjpeg="no" +gprof_build="no" +PIC_CFLAGS="" +want_pic="no" +has_joystick="no" +has_xul="no" +enable_joystick="no" +enable_renoir="no" + +win32="no" +mingw32="no" +cygwin="no" +linux="no" +freebsd="no" +darwin="no" +alt_macosx_dir="" +Mac_Applications="" +extralibs="-lm" +bigendian="no" +SHFLAGS=-shared +need_inet_aton="no" +LDFLAGS="$LDFLAGS -Wl --warn-common" +CFLAGS="" +CPPFLAGS="" +GPAC_SH_FLAGS=-lpthread +DYN_LIB_SUFFIX="so" +X11_PATH="/usr/X11R6" +OSS_CFLAGS="" +OSS_LDFLAGS="" +INSTFLAGS="" +is_64="no" + +#Configure Usage +if test x"$1" = x"-h" -o x"$1" = x"--help" ; then +cat << EOF + +Usage: configure [options] +Options: [defaults in brackets after descriptions] + +EOF +echo "GPAC configuration options:" +echo " --help print this message" +echo " --prefix=PREFIX install in PREFIX [$prefix]" +echo " --mandir=DIR man documentation in DIR [PREFIX/man]" +echo "" +echo " --source-path=PATH path of source code [$source_path]" +echo " --cross_prefix=PREFIX use PREFIX for compile tools [$cross_prefix]" +echo " --cc=CC use C compiler CC [$cc]" +echo " --make=MAKE use specified make [$make]" +echo " --extra-cflags=ECFLAGS add ECFLAGS to CFLAGS [$CFLAGS]" +echo " --extra-ldflags=ELDFLAGS add ELDFLAGS to LDFLAGS [$LDFLAGS]" +echo " --extra-libs=ELIBS add ELIBS [$ELIBS]" +echo " --cpu=CPU force cpu to CPU [$cpu]" +echo " --sdl-cfg=SDL_PATH specify path to sdl-config for local install [$sdl_path]" +echo " --enable-sdl-static use static SDL linking [default=no]" +echo " --X11-path=X11_PATH specify path for X11 includes and libraries [$X11_PATH]" +echo " --dxsdk-path=DX_PATH specify directX SDK for MinGW [$dxsdk_path]" +echo " --xulsdk-path=XUL_PATH specify Mozilla XUL (Gecko) SDK include path [$xulsdk_path]" +echo " --mozdir=MOZ_PATH specify mozilla main directory path for system install" +echo "" +echo " --enable-debug produce debug version" +echo " --enable-gprof enable profiling" +echo " --enable-pic enable Position Independant Code for shared objects" +echo " --strip enable strip" +echo " --track-memory enable tracking of all memory allocated by gpac" +echo " --disable-fragments disable movie fragments in ISO media support" +echo " --disable-opt disable GCC optimizations" +echo " --disable-ipv6 disable IPV6 support" +echo " --disable-wx disable wxWidgets support" +echo " --disable-oss-audio disable OSS audio" +echo " --disable-x11-shm disable X11 shared memory support" +echo " --disable-x11-xv disable X11 Xvideo support" +echo " --disable-fragments disable movie fragments in ISO media support" +echo " --enable-readonly force ISO media to be read-only" +echo " --disable-svg disable SVG support" +echo " --enable-fixed-point enable fixed-point math (THIS MODIFIES GPAC MATH.H HEADER)" +echo " --force-fixed-point force fixed-point math without changing gpac math.h header" +echo " --disable-opengl disable OpenGL support" +echo " --enable-tinygl enable TinyGL support" +echo " --enable-joystick enable joystick support" +echo " --enable-renoir enable renoir 3D support" +echo " --disable-ssl disable OpenSSL support" +echo " --enable-amr-nb-fixed enable AMR NB fixed-point decoder" +echo " --enable-amr-nb enable AMR NB library" +echo " --enable-amr-wb enable AMR WB library" +echo " --enable-amr enable both AMR NB and WB libraries" +echo "" +echo "Extra libraries configuration. You can turn a libray off or force using the local version in gpac/extra_lib/" +echo " --use-js=OPT force SpiderMonkey ECMAScript OPT=[no,local]" +echo " --use-ft=OPT force FreeType OPT=[no,local]" +echo " --use-jpeg=OPT force JPEG OPT=[no,local]" +echo " --use-png=OPT force PNG OPT=[no,local]" +echo " --use-faad=OPT force FAAD OPT=[no,local]" +echo " --use-mad=OPT force MAD OPT=[no,local]" +echo " --use-xvid=OPT force XVID OPT=[no,local]" +echo " --use-ffmpeg=OPT force FFMPEG OPT=[no,local]" +echo " --use-ogg=OPT force OGG OPT=[no,system,local]" +echo " --use-vorbis=OPT force vorbis OPT=[no,system,local]" +echo " --use-theora=OPT force theora OPT=[no,system,local]" +echo " --use-openjpeg=OPT force openjpeg OPT=[no,system,local]" +echo "" +echo "NOTE: The object files are build at the place where configure is launched" +exit 1 +fi + +for opt do + case "$opt" in + --prefix=*) prefix=`echo $opt | cut -d '=' -f 2` + ;; + --mandir=*) mandir=`echo $opt | cut -d '=' -f 2` + ;; + --source-path=*) source_path=`echo $opt | cut -d '=' -f 2` + ;; + --cross-prefix=*) cross_prefix=`echo $opt | cut -d '=' -f 2` + ;; + --cc=*) cc=`echo $opt | cut -d '=' -f 2` + ;; + --make=*) make=`echo $opt | cut -d '=' -f 2` + ;; + --extra-cflags=*) CFLAGS="$CFLAGS ${opt#--extra-cflags=}" + ;; + --extra-ldflags=*) LDFLAGS="$LDFLAGS ${opt#--extra-ldflags=}" + ;; + --extra-libs=*) extralibs=${opt#--extra-libs=} + ;; + --cpu=*) cpu=`echo $opt | cut -d '=' -f 2` + ;; + esac +done + +case "$cpu" in + i386|i486|i586|i686|i86pc|BePC) + cpu="x86" + ;; + x86_64|amd64) + cpu="x86" + is_64="yes" + canon_arch="`cc -dumpmachine | sed -e 's,\([^-]*\)-.*,\1,'`" + if [ x"$canon_arch" = x"x86_64" -o x"$canon_arch" = x"amd64" ]; then + if [ -z "`echo $CFLAGS | grep -- -m32`" ]; then + cpu="x86_64" + libdir="lib64" + #that's a bit crude... + PIC_CFLAGS="-fPIC -DPIC" + want_pic="yes" + fi + fi + ;; + armv4l|arm) + cpu="armv4l" + ;; + alpha) + cpu="alpha" + ;; + "Power Macintosh"|ppc) + cpu="powerpc" + ;; + mips) + cpu="mips" + ;; + sh4|sh4a|sh) + cpu="sh4" + ;; + *) + cpu="unknown" + ;; +esac + +# Checking for CFLAGS +if test -z "$CFLAGS"; then + CFLAGS="" +fi + +if test "$win32" = "yes" ; then + cross_prefix="" +fi + +if test "$mingw32" = "yes" ; then + cross_prefix="" +fi + +cc="${cross_prefix}${cc}" +#for ccache +cc="${cc}" +ar="${cross_prefix}${ar}" +ranlib="${cross_prefix}${ranlib}" +strip="${cross_prefix}${strip}" + + +# find source path +source_path="`echo $0 | sed -e 's#/configure##'`" +source_path_used="yes" +if test -z "$source_path" -o "$source_path" = "." ; then + source_path=`pwd` + source_path_used="no" +else + source_path="`cd \"$source_path\"; pwd`" +fi +# OS specific +targetos=`uname -s` +case $targetos in +BeOS) +js_flags=-DXP_BEOS +xul_flags=-DXP_BEOS +prefix="/boot/home/config" +CFLAGS="$CFLAGS -DPIC -fomit-frame-pointer" +# 3 gcc releases known for BeOS, each with ugly bugs +gcc_version="$($cc -v 2>&1 | grep version | cut -d ' ' -f3-)" +case "$gcc_version" in +2.9-beos-991026*|2.9-beos-000224*) echo "R5/GG gcc" +;; +*20010315*) echo "BeBits gcc" +CFLAGS="$CFLAGS -fno-expensive-optimizations" +;; +esac + +SHFLAGS=-nostart +# no need for libm, but the inet stuff +# Check for BONE +if (echo $BEINCLUDES|grep 'headers/be/bone' >/dev/null); then +extralibs="-lbind -lsocket" +else +need_inet_aton="yes" +extralibs="-lnet" +fi ;; + +SunOS) +make="gmake" +LDFLAGS="" +need_inet_aton="yes" +extralibs="$extralibs -lsocket -lnsl" +;; + +FreeBSD) +make="gmake" +LDFLAGS="$LDFLAGS -export-dynamic" +CFLAGS="$CFLAGS -pthread" +GPAC_SH_FLAGS=-pthread +freebsd="yes" +js_flags="-DXP_UNIX -I/usr/include/js" +;; + +BSD/OS) +extralibs="-lpoll -lgnugetopt -lm" +make="gmake" +;; + +Darwin) +js_flags=-DXP_MAC +xul_flags=-DXP_MAC +if test -d /sw/bin ; then + alt_macosx_dir="/sw" + CFLAGS="-I/sw/include $CFLAGS" + LDFLAGS="-L/sw/lib $LDFLAGS" +elif test -d /opt/local/bin ; then + alt_macosx_dir="/opt/local" + CFLAGS="-I/opt/local/include $CFLAGS" + LDFLAGS="-L/opt/local/lib $LDFLAGS" +fi +cc="cc" +Mac_Applications="/Applications" +SHFLAGS="-dynamiclib" +DYN_LIB_SUFFIX="dylib" +extralibs="" +GPAC_SH_FLAGS="" +strip="strip -x" +LDFLAGS="$LDFLAGS -read_only_relocs warning" +darwin="yes" +gcc_version=`$cc -v 2>&1 | grep version | cut -d ' ' -f3` +case "$gcc_version" in +*2.95*) +CFLAGS="$CFLAGS -no-cpp-precomp -pipe -fomit-frame-pointer" +;; +3.*) +CFLAGS="$CFLAGS -no-cpp-precomp -pipe -fomit-frame-pointer -mdynamic-no-pic -fno-common" +;; +4.*) +CFLAGS="$CFLAGS -no-cpp-precomp -pipe -fomit-frame-pointer -fno-common" +;; +esac +;; + +MINGW32*) +js_flags="-DXP_PC -D_declspec=__declspec" +mingw32="yes" +win32="yes" +extralibs="$extralibs -lws2_32 -lwinmm" +;; + +CYGWIN*) +js_flags=-DXP_PC +extralibs="$extralibs -lws2_32 -lwinmm" +cygwin="yes" +win32="yes" +;; + +Linux) +js_flags="-DXP_UNIX -I/usr/include/js" +#OSS_LDFLAGS="-laoss" +linux="yes" +case "$cpu" in +sh4) +CFLAGS="$CFLAGS -isystem $prefix/include" +#LDFLAGS="$LDFLAGS -L$prefix/lib" +;; +esac +;; +*) ;; +esac + +# Defines directory for binaries and libs (ex. for TinyGL) +target_bin_dir="" +if test "$cross_prefix" = "" ; then +target_bin_dir=`${cc} -v 2>&1 | sed -n '2p' | awk ' {print $2}'`-${cc_orig} +else +target_bin_dir=${cross_prefix}${cc_orig} +fi + +#if test "$source_path_used" = "yes" ; then + mkdir -p extra_lib + mkdir -p extra_lib/lib + mkdir -p extra_lib/lib/gcc +#fi + +#OK check for all local & systems lib +local_inc=$source_path/extra_lib/include +local_lib=extra_lib/lib/gcc + + +#check GCC flags support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF +if $cc -o $TMPO $TMPC -fno-strict-aliasing 2> /dev/null ; then +CFLAGS="$CFLAGS -fno-strict-aliasing" +fi +CPPFLAGS="$CFLAGS" +if $cc -o $TMPO $TMPC -lz -Wno-pointer-sign 2> /dev/null ; then +CFLAGS="$CFLAGS -Wno-pointer-sign" +fi + +#look for zlib +cat > $TMPC << EOF +#include +int main( void ) { if (zlibVersion() != ZLIB_VERSION) { puts("zlib version differs !!!"); return 1; } return 0; } +EOF +has_zlib="no" +if $cc -o $TMPO $TMPC -lz 2> /dev/null ; then +has_zlib="system" +elif $cc -o $TMPO $TMPC -I$local_inc/zlib -L$local_lib -lz 2> /dev/null ; then +has_zlib="local" +else + echo "error: zlib not found on system or in local libs" + exit 1 +fi + + +#check dlopen +cat > $TMPC << EOF +#include +int main( void ) { return (int) dlopen("foo", 0); } +EOF + +if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +dlopen="yes" +elif $cc -o $TMPE $TMPC -ldl > /dev/null 2>&1 ; then +GPAC_SH_FLAGS="$GPAC_SH_FLAGS -ldl" +else + if test "$win32" = "no"; then + echo "error: dlopen not found on system" + exit 1 + fi +fi + + +#look for spidermonkey JS support + +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF + +#regular spidermonkey +if $cc -o $TMPO $TMPC $js_flags -ljs 2> /dev/null ; then +js_inc="/usr/include" +has_js="system" +elif test "$alt_macosx_dir" != "" ; then + if cc -o $TMPO $TMPC -DXP_UNIX -I$alt_macosx_dir/include/js -L$alt_macosx_dir/lib -ljs 2> /dev/null ; then + has_js="system" + js_flags="-DXP_UNIX -I$alt_macosx_dir/include/js" + js_lib="-L$alt_macosx_dir/lib -ljs" + js_inc="$alt_macosx_dir/include/js" + fi +else +#debian spidermonkey (smjs) +js_flags="-DXP_UNIX -I/usr/include/smjs" +js_inc="/usr/include/smjs" +if $cc -o $TMPO $TMPC $js_flags -lsmjs 2> /dev/null ; then +has_js="system" +js_lib="-lsmjs" +else +#debian spidermonkey (mozjs) +js_flags="-DXP_UNIX -I/usr/include/mozjs" +js_inc="/usr/include/mozjs" +if $cc -o $TMPO $TMPC $js_flags -lmozjs 2> /dev/null ; then +has_js="system" +js_lib="-lmozjs" +else +#try local +js_inc="$local_inc/js" +js_flags="-DXP_UNIX -I$local_inc/js" +if $cc -o $TMPO $TMPC $js_flags -L$local_lib -ljs 2> /dev/null ; then +has_js="local" +fi +fi +fi +fi + + +if test "$has_js" != "no" ; then + +#WARNING: there is a bug in MOZJS packages, the MOZILLA_1_8_BRANCH macro is not signaled, there is no way of knowing +#if the lib has been compiled with or without the macro. We currently just decide that if the macro is present +#in the header, it was enabled in the build +if grep "MOZILLA_1_8_BRANCH" $js_inc/jsapi.h > /dev/null 2>&1 ; then +js_flags="-DMOZILLA_1_8_BRANCH $js_flags" +echo "WARNING: Turning on MOZILLA_1_8_BRANCH SpiderMonkey macro" +echo "If you have troubles with scripts in GPAC, disable this macro and recompile" +fi + +fi + +#end JS test + + +#look for freetype support +cat > $TMPC << EOF +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_OUTLINE_H +int main( void ) { return 0; } +EOF +echo $cross_prefix +if test "$cross_prefix" != "" ; then + if test "`which $prefix/bin/freetype-config`" != ""; then + ft_cflags="`$prefix/bin/freetype-config --cflags`" + ft_lflags="`$prefix/bin/freetype-config --libs`" + if $cc -o $TMPO $TMPC $ft_cflags $ft_lflags 2> /dev/null ; then + has_ft="system" + else + ft_cflags="-I$local_inc/freetype" + ft_lflags="-L$local_lib -lfreetype" + if $cc -o $TMPO $TMPC $ft_cflags $ft_lflags 2> /dev/null ; then + has_ft="local" + fi + fi + fi +else + if test "`which freetype-config`" != ""; then + ft_cflags="`freetype-config --cflags`" + ft_lflags="`freetype-config --libs`" + if $cc -o $TMPO $TMPC $ft_cflags $ft_lflags 2> /dev/null ; then + has_ft="system" + else + ft_cflags="-I$local_inc/freetype" + ft_lflags="-L$local_lib -lfreetype" + if $cc -o $TMPO $TMPC $ft_cflags $ft_lflags 2> /dev/null ; then + has_ft="local" + fi + fi + fi +fi +#end freetype test + + + +#look for OpenSSL support +cat > $TMPC << EOF +#include +#include +#include +#include +int main( void ) { return 0; } +EOF + +if test "$win32" = "yes" ; then +LINK_SSL="-lssleay32 -leay32" +else +LINK_SSL="-lssl -lcrypto" +fi + +if $cc -o $TMPO $TMPC $LINK_SSL 2> /dev/null ; then +has_ssl="yes" +fi + + +#look for JPEG support +cat > $TMPC << EOF +#include +#include +int main( void ) { return 0; } +EOF + +if test "$cross_prefix" != "" ; then + if test "`which $prefix/bin/jpeg-config`" != ""; then + jpeg_cflags="`$prefix/bin/jpeg-config --cflags`" + jpeg_lflags="`$prefix/bin/jpeg-config --libs`" + if $cc -o $TMPO $TMPC $jpeg_cflags $jpeg_lflags 2> /dev/null ; then + has_jpeg="system" + else + jpeg_cflags="-I$local_inc/jpeg" + jpeg_lflags="-L$local_lib -ljpeg" + if $cc -o $TMPO $TMPC $jpeg_cflags $jpeg_lflags 2> /dev/null ; then + has_jpeg="local" + fi + fi + fi +else + if $cc -o $TMPO $TMPC -ljpeg 2> /dev/null ; then + has_jpeg="system" + elif test "$alt_macosx_dir" != "" ; then + if cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -ljpeg 2> /dev/null ; then + has_jpeg="system" + fi + elif $cc -o $TMPO $TMPC -I$local_inc/jpeg -L$local_lib -ljpeg 2> /dev/null ; then + has_jpeg="local" + fi +fi + +#look for OpenJPEG support +cat > $TMPC << EOF +#include +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC -lopenjpeg 2> /dev/null ; then +has_openjpeg="system" +elif test "$alt_macosx_dir" != "" ; then + if cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -ljpeg 2> /dev/null ; then + has_openjpeg="system" + fi +elif $cc -o $TMPO $TMPC -I$local_inc/openjpeg -L$local_lib -lopenjpeg 2> /dev/null ; then +has_openjpeg="local" +fi + +#look for PNG support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF + +if test "$cross_prefix" != "" ; then + png_cflags="-I$prefix/include" + png_lflags="-L$prefix/lib -lpng -lz" + echo $png_cflags + echo $png_lflags + if $cc -o $TMPO $TMPC $png_cflags $png_lflags 2> /dev/null ; then + has_png="system" + else + png_cflags="-I$local_inc/png" + png_lflags="-L$local_lib -lpng" + if $cc -o $TMPO $TMPC $png_cflags $png_lflags 2> /dev/null ; then + has_png="local" + fi + fi +else + if $cc -o $TMPO $TMPC -lpng -lz 2> /dev/null ; then + has_png="system" + elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lpng 2> /dev/null ; then + has_png="system" + fi + elif $cc -o $TMPO $TMPC -I$local_inc/png -L$local_lib -lpng 2> /dev/null ; then + has_png="local" + fi +fi + +#look for MAD support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC -lmad 2> /dev/null ; then +has_mad="system" +elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lmad 2> /dev/null ; then + has_mad="system" + fi +elif $cc -o $TMPO $TMPC -I$local_inc/mad -L$local_lib -lmad 2> /dev/null ; then +has_mad="local" +fi + +#look for A52DEC support +cat > $TMPC << EOF +#define uint32_t unsigned int +#define uint8_t unsigned char +#include +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC -la52 2> /dev/null ; then +has_a52="system" +elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -la52 2> /dev/null ; then + has_a52="system" + fi +elif $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -la52 2> /dev/null ; then +has_a52="local" +fi + +#look for XVID support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC -lxvidcore 2> /dev/null ; then +has_xvid="system" +elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lxvidcore 2> /dev/null ; then + has_xvid="system" + fi +elif $cc -o $TMPO $TMPC -I$local_inc/xvid -L$local_lib -lxvidcore 2> /dev/null ; then +has_xvid="local" +fi + +#look for FAAD support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC -lfaad -lm 2> /dev/null ; then +has_faad="system" +elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lfaad 2> /dev/null ; then + has_faad="system" + fi +elif $cc -o $TMPO $TMPC -I$local_inc/faad -L$local_lib -lfaad -lm 2> /dev/null ; then +has_faad="local" +fi + + +#look for FFMPEG support +cat > $TMPC << EOF +#include +#include +int main( void ) { return 0; } +EOF + +if test "$cross_prefix" != "" ; then + echo "HELLO" + echo "HELLO" + echo "HELLO" + if $cc -o $TMPO $TMPC -I$prefix/include -L$prefix/lib -lz -lavcodec -lavformat -lavutil 2> /dev/null ; then + echo "HELLO" + echo "HELLO" + echo "HELLO" + has_ffmpeg="system" + else + if $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -lz -lavcodec -lavformat 2> /dev/null ; then + has_ffmpeg="local" + fi + fi +else + if $cc -o $TMPO $TMPC -lz -lavcodec -lavformat 2> /dev/null ; then + has_ffmpeg="system" + elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lz -lavcodec -lavformat 2> /dev/null ; then + has_ffmpeg="system" + fi + elif $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -lz -lavcodec -lavformat 2> /dev/null ; then + has_ffmpeg="local" + fi +fi + +#look for vorbis support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC -lvorbis 2> /dev/null ; then +has_vorbis="system" +elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lvorbis 2> /dev/null ; then + has_vorbis="system" + fi +elif $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -lvorbis -lm 2> /dev/null ; then +has_vorbis="local" +fi + +#look for theora support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC -ltheora 2> /dev/null ; then +has_theora="system" +elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -ltheora -logg 2> /dev/null ; then + has_theora="system" + fi +elif $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -ltheora -logg -lm 2> /dev/null ; then +has_theora="local" +fi + +#look for OGG support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC -logg 2> /dev/null ; then +has_ogg="system" +elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -logg 2> /dev/null ; then + has_ogg="system" + fi +elif $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -logg -lm 2> /dev/null ; then +has_ogg="local" +else +has_vorbis=no +has_theora=no +fi + + +#look for OSS support + +if test "$darwin" = "yes" ; then + +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF +if $cc -o $TMPO $TMPC -DLIBOSS_INTERNAL -I$alt_macosx_dir/include/ -I$alt_macosx_dir/include/liboss -L$alt_macosx_dir/lib -loss 2> /dev/null ; then +has_oss_audio="SYS" +OSS_CFLAGS="-DLIBOSS_INTERNAL -I$alt_macosx_dir/include/ -I$alt_macosx_dir/include/liboss" +OSS_LDFLAGS="-L$alt_macosx_dir/lib -loss" +fi + +else + +cat > $TMPC << EOF +#include +#include +#include +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC 2> /dev/null ; then +has_oss_audio="SYS" +else +cat > $TMPC << EOF +#include +#include +#include +#include +int main( void ) { return 0; } +EOF +if $cc -o $TMPO $TMPC 2> /dev/null ; then +has_oss_audio="YES" +fi +fi + +fi + + +#look for wxWidgets support +has_wx="no" +wx_too_old="no" + +if test "`which wx-config`" != "" ; then + +wx_version=`wx-config --version | sed 's/[^0-9]//g'` + +if test "$wx_version" -lt 250 ; then +wx_too_old="yes" +has_wx="yes" +else +if test "$wx_version" -lt 260 ; then +has_wx="yes" +wx_cflags=`wx-config --cppflags` +wx_lflags=`wx-config --libs` +else +has_wx="yes" +wx_cflags=`wx-config --cppflags core, base` +wx_lflags=`wx-config --libs core, base` +fi + +if test "$darwin" = "yes" ; then +wx_lflags="-Wl,-bind_at_load $wx_lflags -lstdc++" #10.4 needs it, not sure about 10.3 +fi + +fi + +cat > $TMPCPP << EOF +#include +int main( void ) { return 0; } +EOF + +if $cc $wx_cflags -o $TMPO $TMPCPP $wx_lflags > /dev/null 2>&1 ; then +wx_version=`wx-config --version | sed 's/[^0-9]//g'` +if test "$wx_version" -lt 254 ; then +wx_too_old="yes" +else +has_wx="yes" +fi +fi + +fi +#end wx test + + +# look for IPv6 +cat > $TMPC << EOF +#include +#include +#include +#include +int main( void ) { + struct sockaddr_storage saddr; + struct ipv6_mreq mreq6; + getaddrinfo(0,0,0,0); + getnameinfo(0,0,0,0,0,0,0); + IN6_IS_ADDR_MULTICAST(0); +} +EOF + +if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +has_ipv6="yes" +fi + +# look for DVB4linux +cat > $TMPC << EOF +#include +#include +int main( void ) { +} +EOF + +if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +has_dvb4linux="yes" +fi + +# look for XMLRPC +cat > $TMPC << EOF +#include +#include +#include +int main( void ) { +} +EOF + +if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +has_xmlrpc="yes" +fi + +# look for alsa +cat > $TMPC << EOF +#include +int main( void ) { +} +EOF + +if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +has_alsa="yes" +fi + +# look for pulseaudio +cat > $TMPC << EOF +#include +int main( void ) { +} +EOF +if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +has_pulseaudio="yes" +fi + +#look for jack +cat > $TMPC << EOF +#include +int main( void ) { +} +EOF +if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +has_jack="yes" +fi + + +for opt do + case "$opt" in + --sdl-cfg=*) sdl_path=`echo $opt | cut -d '=' -f 2`; sdl_local="yes" + ;; + --enable-sdl-static=*) sdl_static="yes" + ;; + --enable-jack=*) has_jack="yes" + ;; + --X11-path=*) X11_PATH=`echo $opt | cut -d '=' -f 2` + ;; + --dxsdk-path=*) dxsdk_path=`echo $opt | cut -d '=' -f 2` + ;; + --xulsdk-path=*) xulsdk_path=`echo $opt | cut -d '=' -f 2` + ;; + --mozdir=*) moz_path=`echo $opt | cut -d '=' -f 2` + ;; + --enable-amr-nb-fixed) has_amr_nb_fixed="yes" + ;; + --disable-pulseaudio=*) has_pulseaudio="no" + ;; + --enable-amr-nb) has_amr_nb="yes" + ;; + --enable-amr-wb) has_amr_wb="yes" + ;; + --enable-amr) has_amr_wb="yes"; has_amr_nb="yes" + ;; + --disable-oggvorbis) has_oggvorbis="no" + ;; + --disable-jack=*) has_jack="no" + ;; + --enable-debug) debuginfo="yes"; no_gcc_opt="yes" + ;; + --enable-gprof) gprof_build="yes"; + ;; + --enable-pic) PIC_CFLAGS="-fPIC -DPIC"; want_pic="yes"; + ;; + --disable-opt) no_gcc_opt="yes" + ;; + --disable-ipv6) has_ipv6="no" + ;; + --disable-wx) has_wx="no" + ;; + --disable-fragments) iso_fragments="no" + ;; + --enable-readonly) make_readonly="yes" + ;; + --disable-oss-audio) has_oss_audio="no" + ;; + --disable-x11-shm) has_x11_shm="no" + ;; + --disable-x11-xv) has_x11_xv="no" + ;; + --disable-svg) disable_svg="yes" + ;; + --enable-fixed-point) use_fixed_point="yes" + ;; + --force-fixed-point) use_fixed_point="forced" + ;; + --strip) INSTFLAGS="-s $INSTFLAGS" + ;; + --track-memory) use_memory_tracking="yes" + ;; + --disable-opengl) disable_opengl="yes" + ;; + --enable-tinygl) enable_tinygl="yes" + ;; + --disable-ssl) has_ssl="no" + ;; + --use-faad=*) has_faad=${opt#--use-faad=} + ;; + --use-js=*) has_js=${opt#--use-js=} + ;; + --use-ft=*) has_ft=${opt#--use-ft=} + ;; + --use-mad=*) has_mad=${opt#--use-mad=} + ;; + --use-a52=*) has_a52=${opt#--use-a52=} + ;; + --use-xvid=*) has_xvid=${opt#--use-xvid=} + ;; + --use-jpeg=*) has_jpeg=${opt#--use-jpeg=} + ;; + --use-ffmpeg=*) has_ffmpeg=${opt#--use-ffmpeg=} + ;; + --use-png=*) tmp_has_png=${opt#--use-png=} +if test "$tmp_has_png" = "system" ; then + if test "$has_png" != "system" ; then + if test "$cross_prefix" != "" ; then + echo + echo "WARNING: PNG has been forced to system, but we are cross-compiling, it will have to be on target" + echo + else + echo + echo "WARNING!! : PNG has been forced to system even though it hasn't been found in this host" + echo + fi + fi +fi +has_png=$tmp_has_png + ;; + --use-ogg=*) has_ogg=${opt#--use-ogg=} + ;; + --use-vorbis=*) has_vorbis=${opt#--use-vorbis=} + ;; + --use-theora=*) has_theora=${opt#--use-theora=} + ;; + --use-openjpeg=*) has_openjpeg=${opt#--use-openjpeg=} + ;; + --enable-joystick) enable_joystick="yes" + ;; + --enable-renoir) enable_renoir="yes" + ;; + --enable-pulseaudio=*) has_pulseaudio="yes" + ;; + esac +done + +#look for OpenGL support or for TinyGL support + +LINK3D="" +INCL3D="" + +cat > $TMPC << EOF +#include +#include +int main( void ) { return 0; } +EOF + +if test "$disable_opengl" = "no" ; then + if test "$win32" = "yes" ; then + LINK3D="-lopengl32 -lglu32" + elif test "$darwin" = "yes" ; then + LINK3D="-lgl -lglu" + else + LINK3D="-lGL -lglut" + fi + if $cc -o $TMPO $TMPC $LINK3D 2> /dev/null ; then + has_opengl="yes" + elif $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib 2> /dev/null ; then + has_opengl="yes" + INCL3D="-I$X11_PATH/include" + LINK3D="-L$X11_PATH/lib $LINK3D" + fi + if test "$has_opengl" = "no" ; then + LINK3D="" + fi +fi + +cat > $TMPC << EOF +#include +int main( void ) { int a ; a = TINYGL ; } +EOF +#if $cc $CFLAGS -o $TMPO $TMPC 2> /dev/null ; then +# has_tinygl="system" +# LINK3D="-lTinyGL" +if test "$enable_tinygl" = "yes" ;then + if $cc -o $TMPO $TMPC -I${source_path}/../../TinyGL/include -L${source_path}/../../TinyGL/lib/$target_bin_dir -lTinyGL 2> /dev/null ; then + has_tinygl="yes" + has_opengl="yes" + LINK3D="-L${source_path}/../../TinyGL/lib/$target_bin_dir -lTinyGL" + INCL3D="-I${source_path}/../../TinyGL/include" +fi +fi + + +#look for GECKO support +cat > $TMPCPP << EOF +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPCPP -I$xulsdk_path 2> /dev/null ; then +has_xul="system" +xul_flags="-I$xulsdk_path $xul_flags" +else +if $cc -o $TMPO $TMPCPP $xul_flags -I$local_inc/gecko-sdk/include 2> /dev/null ; then +has_xul="local" +xul_flags="-I$local_inc/gecko-sdk/include $xul_flags" +fi +fi + + +#look for joystick support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF +if test "$enable_joystick" = "yes" ;then + if $cc -o $TMPO $TMPC 2> /dev/null ; then + has_joystick="yes" + fi +fi + +#look for X11 shared memory support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF +if $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib 2> /dev/null ; then +has_x11="yes" + +#look for X11 shared memory support +cat > $TMPC << EOF +#include +#include +#include +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib 2> /dev/null ; then +has_x11_shm="yes" +fi + +#look for XVideo support +cat > $TMPC << EOF +#include +#include +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib 2> /dev/null ; then +has_x11_xv="yes" +fi + +fi + + +#GCC opt +if test "$no_gcc_opt" = "no"; then +CFLAGS="-O3 $CFLAGS" +fi + +#look for DX support +dx_path="system" +has_mingw_directx="no" +if test "$win32" = "yes" ; then + +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF + +if $cc -o $TMPO $TMPC 2> /dev/null ; then +has_mingw_directx="yes" +else +dx_path="$dxsdk_path" +if $cc -o $TMPO $TMPC -I$dxsdk_path/include -L$dxsdk_path/lib -lddraw 2> /dev/null ; then +has_mingw_directx="yes" +fi +fi + +fi + + +#look for SDL support +sdl_too_old=no +has_sdl=no +sdl_config="sdl-config" +if test "$sdl_local" = "yes"; then +sdl_config="$sdl_path/sdl-config" +sdl_static="yes" +fi + +if test "`which $sdl_config`" != ""; then + +cat > $TMPC << EOF +#include +#undef main +int main( void ) { return SDL_Init (SDL_INIT_VIDEO); } +EOF + +if test "$sdl_static" = "yes"; then +sdl_lib_flags=`$sdl_config --static-libs` +else +sdl_lib_flags=`$sdl_config --libs` +fi +sdl_cflags=`$sdl_config --cflags` + +if $cc -o $TMPO $sdl_cflags $TMPC $sdl_lib_flags > /dev/null 2>&1 ; then +_sdlversion=`$sdl_config --version | sed 's/[^0-9]//g'` +if test "$_sdlversion" -lt 121 ; then +sdl_too_old=yes +else +has_sdl=yes +fi +fi + +fi +#end SDL check + + +if test -z "$cross_prefix" ; then + +# big/little endian test +cat > $TMPC << EOF +#include +int main(int argc, char ** argv){ + volatile uint32_t i=0x01234567; + return (*((uint8_t*)(&i))) == 0x67; +} +EOF + +if $cc -o $TMPO $TMPC 2>/dev/null ; then +$TMPO && bigendian="yes" +else +echo big/little endian test failed +fi + +else + +# if cross compiling, cannot launch a program, so make a static guess +if test "$cpu" = "powerpc" -o "$cpu" = "mips" ; then + bigendian="yes" +fi + +fi + +if test x"$mandir" = x""; then +mandir="${prefix}/man" +fi + +version=`grep '#define GPAC_VERSION ' $source_path/include/gpac/tools.h | cut -d '"' -f 2` +echo "" +echo "** System Configuration" +echo "Install prefix: $prefix" +echo "Source path: $source_path" +echo "C compiler: $cc" +echo "make: $make" +echo "CPU: $cpu" +echo "Big Endian: $bigendian" +if test $cpu = "mips"; then +echo "MMI enabled: $mmi" +fi +echo "" +echo "** GPAC $version Core Configuration **" +echo "debug version: $debuginfo" +echo "GProf enabled: $gprof_build" +echo "Memory tracking enabled: $use_memory_tracking" +echo "read-only version: $make_readonly" +echo "fixed-point version: $use_fixed_point" +echo "IPV6 Support: $has_ipv6" +echo "IsoMedia MovieFragments support: $iso_fragments" +echo "SVG Support disabled: $disable_svg" +echo "" + +echo "** Detected libraries **" +echo "zlib: $has_zlib" + +if test "$win32" != "yes" ; then +if test "$has_oss_audio" = "no"; then +echo "OSS Audio: no" +else +echo "OSS Audio: yes" +fi +echo "ALSA Audio: $has_alsa" +echo "Jack Audio: $has_jack" +echo "PulseAudio Audio: $has_pulseaudio" +echo "X11 Shared Memory support: $has_x11_shm (path: $X11_PATH)" +echo "X11 XVideo support: $has_x11_xv" +fi + +echo "SDL Support: $has_sdl" +if test "$sdl_too_old" = "yes" ; then +echo "SDL Version too old - please upgrade for SDL support" +fi +echo "OpenGL support: $has_opengl" +echo "TinyGL support: $has_tinygl" +echo "OpenSSL support: $has_ssl" + +echo "Mozilla XUL/GECKO support: $has_xul" + +echo "Joystick support: $has_joystick" +echo "Renoir enabled: $enable_renoir" + +if test "$win32" = "yes" ; then +echo "DirectX Support: $has_mingw_directx" +fi +if test "$linux" = "yes" ; then +echo "DVB Support: $has_dvb4linux" +fi + +echo "XMLPRC Support: $has_xmlrpc" + +if test "$wx_too_old" = "yes" ; then +has_wx="no" +echo "wxWidgets Version too old - please upgrade to 2.6.0 for wxWidgets support" +fi +if test "$has_wx" = "yes" ; then +echo "wxWidgets support: Version $wx_version" +else +echo "wxWidgets support: no" +fi + + +echo "" +echo "** Extra Libraries used **" +echo "SpiderMonkey: $has_js" +echo "FreeType: $has_ft" +echo "JPEG: $has_jpeg" +echo "OpenJPEG: $has_openjpeg" +echo "PNG: $has_png" +echo "MAD: $has_mad" +echo "FAAD: $has_faad" +echo "XVID: $has_xvid" +echo "FFMPEG: $has_ffmpeg" +echo "Xiph OGG: $has_ogg" +if test "$has_ogg" = "no"; then +has_ogg="no" +else +echo "Xiph Vorbis: $has_vorbis" +echo "Xiph Theora: $has_theora" +fi +echo "A52 (AC3): $has_a52" + +echo "" +if test "$has_amr_nb_fixed" = "yes" ; then + echo "*** AMR NB FIXED-POINT NOTICE ***" + echo "Make sure you have downloaded TS26.073 from:" + echo "http://www.3gpp.org/ftp/Specs/archive/26_series/26.073/26073-*.zip" + echo "or through gpac_extra_libs and extracted src to modules/amr_dec/amr_nb" + echo "without overwriting typedefs.h file" + echo "" +fi + +echo "" +if test "$has_amr_nb" = "yes" ; then + echo "*** AMR NB NOTICE ***" + echo "Make sure you have downloaded TS26.104 from:" + echo "http://www.3gpp.org/ftp/Specs/archive/26_series/26.104/26104-*.zip" + echo "or through gpac_extra_libs and extracted src to modules/amr_float_dec/amr_nb_ft" + echo "without overwriting typedefs.h file" + echo "" +fi + + +echo "" +if test "$has_amr_wb" = "yes" ; then + echo "*** AMR WB NOTICE ***" + echo "Make sure you have downloaded TS26.204 from:" + echo "http://www.3gpp.org/ftp/Specs/archive/26_series/26.204/26204-*.zip" + echo "or through gpac_extra_libs and extracted src to modules/amr_float_dec/amr_wb_ft" + echo "without overwriting typedefs.h file" + echo "" +fi + +if test "$use_memory_tracking" = "yes"; then +echo "!! WARNING: GPAC Memory tracking is enabled !!" +echo "!! This may corrupt third-party tools using libgpac !!" +echo "!! Use at your own risk, and only for GPAC benchmarking !!" +echo "" +fi + +#needs gmon for win32 gprof +if test "$gprof_build" = "yes"; then +if test "$win32" = "yes"; then +extralibs="$extralibs -lgmon" +fi +fi + + +#overwrite fixed-point in math.h +if test "$use_fixed_point" = "yes"; then +if grep "#define GPAC_NO_FIXED_POINT" $source_path/include/gpac/math.h > /dev/null 2>&1 ; then +sed -e 's/#define GPAC_NO_FIXED_POINT/#define GPAC_FIXED_POINT/' $source_path/include/gpac/math.h >$TMPH +cp -f $TMPH $source_path/include/gpac/math.h +fi +elif test "$use_fixed_point" = "forced"; then +CFLAGS="$CFLAGS -DGPAC_FIXED_POINT" +else +if grep "#define GPAC_FIXED_POINT" $source_path/include/gpac/math.h > /dev/null 2>&1 ; then +sed -e 's/#define GPAC_FIXED_POINT/#define GPAC_NO_FIXED_POINT/' $source_path/include/gpac/math.h >$TMPH +cp -f $TMPH $source_path/include/gpac/math.h +fi +fi + +TMPH="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.h" + +echo "Creating config.mak" +echo "# Automatically generated by configure - do not modify" > config.mak +echo "/* Automatically generated by configure */" > $TMPH + +echo "#ifndef GF_CONFIG_H" >> $TMPH +echo "#define GF_CONFIG_H" >> $TMPH + +echo "prefix=$prefix" >> config.mak +echo "DESTDIR=$DESTDIR" >> config.mak +echo "moddir=$prefix/$libdir/gpac" >> config.mak +echo "moddir_path=$prefix/$libdir/gpac" >> config.mak +echo "mandir=$mandir" >> config.mak +echo "tinygl_target_bin_dir=$target_bin_dir" >> config.mak +echo "MAKE=$make" >> config.mak +echo "CC=$cc" >> config.mak +echo "AR=$ar" >> config.mak +echo "RANLIB=$ranlib" >> config.mak +echo "STRIP=$strip" >> config.mak +echo "OPTFLAGS=$CFLAGS" >> config.mak +echo "CPPFLAGS=$CPPFLAGS" >> config.mak +echo "PIC_CFLAGS=$PIC_CFLAGS" >> config.mak +echo "WANT_PIC=$want_pic" >> config.mak +echo "LDFLAGS=$LDFLAGS" >> config.mak +echo "SHFLAGS=$SHFLAGS" >> config.mak +echo "libdir=$libdir" >> config.mak + + +#for cross-compilation +if test "$cross_prefix" != "" ; then +echo "CROSS_COMPILING=yes" >> config.mak +fi + +if test "$cpu" = "x86" ; then + echo "TARGET_ARCH_X86=yes" >> config.mak +elif test "$cpu" = "armv4l" ; then +echo "CONFIG_PULSEAUDIO=$has_pulseaudio" >> config.mak + echo "TARGET_ARCH_ARMV4L=yes" >> config.mak +elif test "$cpu" = "alpha" ; then + echo "TARGET_ARCH_ALPHA=yes" >> config.mak +elif test "$cpu" = "sparc64" ; then + echo "TARGET_ARCH_SPARC64=yes" >> config.mak +elif test "$cpu" = "powerpc" ; then + echo "TARGET_ARCH_POWERPC=yes" >> config.mak +elif test "$cpu" = "mips" ; then + echo "TARGET_ARCH_MIPS=yes" >> config.mak +fi + + +if test "$bigendian" = "yes" ; then + echo "IS_BIGENDIAN=yes" >> config.mak + echo "#define GPAC_BIG_ENDIAN" >> $TMPH +fi +echo "EXTRALIBS=$extralibs" >> config.mak +echo "VERSION=$version" >>config.mak + +if test "$win32" = "yes" ; then + echo "CONFIG_WIN32=yes" >> config.mak + echo "CONFIG_OS=CONFIG_WIN32" >> config.mak + echo "#define GPAC_CONFIG_WIN32" >> $TMPH +elif test "$linux" = "yes" ; then + echo "CONFIG_LINUX=yes" >> config.mak + echo "CONFIG_OS=CONFIG_LINUX" >> config.mak + echo "#define GPAC_CONFIG_LINUX" >> $TMPH +elif test "$freebsd" = "yes" ; then + echo "CONFIG_FREEBSD=yes" >> config.mak + echo "CONFIG_OS=CONFIG_FREEBSD" >> config.mak + echo "#define GPAC_CONFIG_FREEBSD" >> $TMPH +elif test "$darwin" = "yes" ; then + echo "CONFIG_DARWIN=yes" >> config.mak + echo "CONFIG_OS=CONFIG_DARWIN" >> config.mak + echo "#define GPAC_CONFIG_DARWIN" >> $TMPH + echo "mac_apps=$Mac_Applications" >> config.mak +else + echo "CONFIG_OS=CONFIG_GEN" >> config.mak + echo "#define GPAC_CONFIG_GENERIC" >> $TMPH +fi + +if test "$win32" = "no" ; then + echo "GPAC_SH_FLAGS=$GPAC_SH_FLAGS" >> config.mak + echo "DYN_LIB_SUFFIX=$DYN_LIB_SUFFIX" >> config.mak +else + echo "DYN_LIB_SUFFIX=dll" >> config.mak +fi + + +echo "INSTFLAGS=$INSTFLAGS" >> config.mak + +echo "CONFIG_JS=$has_js" >> config.mak +if test "$has_js" = "no" ; then + has_js="no" +else +echo "JS_FLAGS=$js_flags" >> config.mak +echo "JS_LIBS=$js_lib" >> config.mak +echo "#define GPAC_HAS_SPIDERMONKEY" >> $TMPH +fi +echo "CONFIG_ZLIB=$has_zlib" >> config.mak +echo "CONFIG_FT=$has_ft" >> config.mak + +echo "CONFIG_JPEG=$has_jpeg" >> config.mak +if test "$has_jpeg" != "no" ; then +echo "#define GPAC_HAS_JPEG" >> $TMPH +fi + +echo "CONFIG_PNG=$has_png" >> config.mak +if test "$has_png" != "no" ; then +echo "#define GPAC_HAS_PNG" >> $TMPH +fi + +echo "CONFIG_JP2=$has_openjpeg" >> config.mak +echo "CONFIG_FAAD=$has_faad" >> config.mak +echo "CONFIG_MAD=$has_mad" >> config.mak +echo "CONFIG_XVID=$has_xvid" >> config.mak +echo "CONFIG_OGG=$has_ogg" >> config.mak +echo "CONFIG_VORBIS=$has_vorbis" >> config.mak +echo "CONFIG_THEORA=$has_theora" >> config.mak +echo "CONFIG_FFMPEG=$has_ffmpeg" >> config.mak +echo "CONFIG_OSS_AUDIO=$has_oss_audio" >> config.mak +echo "CONFIG_ALSA=$has_alsa" >> config.mak +echo "CONFIG_JACK=$has_jack" >> config.mak +echo "CONFIG_A52=$has_a52" >> config.mak + +echo "DISABLE_SVG=$disable_svg" >> config.mak +echo "GPAC_USE_TINYGL=$has_tinygl" >> config.mak +echo "OGL_INCLS=$INCL3D" >> config.mak +if test "$disable_svg" = "yes" ; then +echo "#define GPAC_DISABLE_SVG" >> $TMPH +fi +echo "HAS_OPENGL=$has_opengl" >> config.mak +echo "ENABLE_JOYSTICK=$has_joystick" >> config.mak +echo "TRISCOPE_MODE=$enable_renoir" >> config.mak + +if test "$has_opengl" = "yes" ; then + echo "OGL_LIBS=$LINK3D" >> config.mak +else + echo "#define GPAC_DISABLE_3D" >> $TMPH +fi + +if test "$has_tinygl" = "yes" ; then +echo "#define GPAC_USE_TINYGL" >> $TMPH +fi + +echo "HAS_OPENSSL=$has_ssl" >> config.mak +if test "$has_ssl" = "yes" ; then + echo "SSL_LIBS=$LINK_SSL" >> config.mak + echo "#define GPAC_HAS_SSL" >> $TMPH +fi + +echo "CONFIG_SDL=$has_sdl" >> config.mak +if test "$has_sdl" = "yes" ; then + echo "SDL_CFLAGS=$sdl_cflags" >> config.mak + echo "SDL_LIBS=$sdl_lib_flags" >> config.mak +fi +if test "$has_ft" = "no" ; then + has_ft="no" +else + echo "FT_CFLAGS=$ft_cflags" >> config.mak + echo "FT_LIBS=$ft_lflags" >> config.mak +fi +echo "CONFIG_AMR_NB=$has_amr_nb_fixed" >> config.mak +echo "CONFIG_AMR_NB_FT=$has_amr_nb" >> config.mak +echo "CONFIG_AMR_WB_FT=$has_amr_wb" >> config.mak +echo "DEBUGBUILD=$debuginfo" >> config.mak +echo "GPROFBUILD=$gprof_build" >> config.mak + +echo "GPACREADONLY=$make_readonly" >> config.mak +if test "$make_readonly" = "yes" ; then + echo "#define GPAC_READ_ONLY" >> $TMPH +fi +echo "ISOFRAGMENTS=$iso_fragments" >> config.mak +if test "$iso_fragments" = "no" ; then + echo "#define GPAC_ISOM_NO_FRAGMENTS" >> $TMPH +fi + +echo "CONFIG_IPV6=$has_ipv6" >> config.mak +if test "$has_ipv6" = "yes" ; then + echo "#define GPAC_HAS_IPV6" >> $TMPH +fi + +if test "$win32" = "yes" ; then +echo "CONFIG_DIRECTX=$has_mingw_directx" >> config.mak +if test "$has_mingw_directx" = "yes" ; then +echo "DX_PATH=$dx_path" >> config.mak +fi +fi + +echo "USE_WXWIDGETS=$has_wx" >> config.mak +if test "$has_wx" = "yes"; then +echo "WX_CFLAGS=$wx_cflags" >> config.mak +echo "WX_LFLAGS=$wx_lflags" >> config.mak +fi + +echo "MOZILLA_DIR=$moz_path" >> config.mak + +echo "CONFIG_XUL=$has_xul" >> config.mak +if test "$has_xul" != "no"; then +echo "XUL_CFLAGS=$xul_flags" >> config.mak +fi + +echo "LINUX_DVB=$has_dvb4linux" >> config.mak + +echo "XMLRPC_INC=$has_xmlrpc" >> config.mak + +if test "$has_oss_audio" != "no"; then +echo "OSS_INC_TYPE=$has_oss_audio" >> config.mak +echo "OSS_CFLAGS=$OSS_CFLAGS" >> config.mak +echo "OSS_LDFLAGS=$OSS_LDFLAGS" >> config.mak +fi + +echo "CONFIG_X11=$has_x11" >> config.mak +if test "$has_x11_shm" = "yes"; then +echo "USE_X11_SHM=$has_x11_shm" >> config.mak +fi +if test "$has_x11_xv" = "yes"; then +echo "USE_X11_XV=$has_x11_xv" >> config.mak +fi + +if test "$is_64" = "yes"; then +echo "X11_LIB_PATH=$X11_PATH/lib64" >> config.mak +else +echo "X11_LIB_PATH=$X11_PATH/lib" >> config.mak +fi +echo "X11_INC_PATH=$X11_PATH/include" >> config.mak + +#overwrite memory tracking in setup.h +if test "$use_memory_tracking" = "yes"; then +if grep "#define GPAC_MEMORY_TRACKING 0" $source_path/include/gpac/setup.h > /dev/null 2>&1 ; then +sed -e 's/#define GPAC_MEMORY_TRACKING 0/#define GPAC_MEMORY_TRACKING 1/' $source_path/include/gpac/setup.h >$TMPH +cp -f $TMPH $source_path/include/gpac/setup.h +fi +else +if grep "#define GPAC_MEMORY_TRACKING 1" $source_path/include/gpac/setup.h > /dev/null 2>&1 ; then +sed -e 's/#define GPAC_MEMORY_TRACKING 1/#define GPAC_MEMORY_TRACKING 0/' $source_path/include/gpac/setup.h >$TMPH +cp -f $TMPH $source_path/include/gpac/setup.h +fi +fi + + +GPAC_ENST_INC=no +GPAC_ENST=no +enst_dir="`ls $source_path/src/ | grep enst`" +if test "$enst_dir" = "enst"; then +echo "GPAC Proprietary Extensions enabled" +GPAC_ENST_INC=yes +#we need libiconv for eit & co +cat > $TMPC << EOF +#include +int main( void ) { +} +EOF + +if $cc -o $TMPE $TMPC -liconv > /dev/null 2>&1 ; then +GPAC_ENST=yes +echo "LIBGPAC_ENST=`cd src; ls enst/*.c | sed -e 's/\.c/.o/' | tr -s '\r\n' ' ' ; cd ..`" >> config.mak +else +echo "Couldn't find libiconv - disabling GPAC ENST extensions" +GPAC_ENST="no" +fi +fi +echo "GPAC_ENST=$GPAC_ENST" >> config.mak +echo "GPAC_ENST_INC=$GPAC_ENST" >> config.mak + + +# build tree in object directory if source path is different from current one +if test "$source_path_used" = "yes" ; then + + echo "Creating compilation tree image" + SRC_DIRS="src src/utils src/isomedia src/ietf src/odf src/bifs src/scenegraph src/terminal src/mcrypt src/media_tools src/scene_manager src/compositor src/laser" + + if test "$enable_renoir" = "yes" ; then + SRC_DIRS+=" src/compositor/triscope_renoir" + fi + + APP_DIRS="applications/mp4box applications/mp4client applications/mp4box applications/osmozilla applications/osmo4_wx applications/testapps/mp4_streamer applications/testapps/mp42ts" + + for dir in $SRC_DIRS ; do + mkdir -p $dir + done + ln -sf $source_path/Makefile Makefile + ln -sf $source_path/src/Makefile src/Makefile + ln -sf $source_path/src/libgpac.def src/libgpac.def + + #if test "$enable_renoir" = "yes" ; then + #should be done according to target + #cp $source_path/src/compositor/triscope_renoir/*.a src/compositor/triscope_renoir + #fi + + mkdir -p applications + ln -sf $source_path/applications/Makefile applications/Makefile + mkdir -p applications/testapps + + for dir in $APP_DIRS ; do + mkdir -p $dir + ln -sf $source_path/$dir/Makefile $dir/Makefile + done + + MOD_DIRS="modules modules/aac_in modules/ac3_in modules/alsa modules/amr_dec modules/amr_float_dec modules/bifs_dec modules/ctx_load modules/dummy_in modules/dx_hw modules/ffmpeg_in modules/ft_font modules/gpac_js modules/img_in modules/ismacryp modules/isom_in modules/jack modules/laser_dec modules/mp3_in modules/mpegts_in modules/odf_dec modules/ogg modules/oss_audio modules/pulseaudio modules/raw_out modules/rtp_in modules/saf_in modules/sdl_out modules/soft_raster modules/svg_in modules/timedtext modules/wav_out modules/x11_out modules/xvid_dec" + cur_dir="`pwd`" + for dir in $MOD_DIRS ; do + mkdir -p $dir + ln -sf $source_path/$dir/Makefile $dir/Makefile + cd $source_path/$dir + if ls *.def > /dev/null 2>&1; then + def_file="`ls *.def`" + cd $cur_dir + ln -sf $source_path/$dir/$def_file $dir/$def_file + else + cd $cur_dir + fi + done + if test "$has_mingw_directx" = "yes"; then + ln -sf $source_path/modules/dx_hw/hand.cur modules/dx_hw/hand.cur + ln -sf $source_path/modules/dx_hw/collide.cur modules/dx_hw/collide.cur + fi + + cd $cur_dir + + echo "SRC_LOCAL_PATH=no" >> config.mak +else + echo "SRC_LOCAL_PATH=yes" >> config.mak +fi + +echo "SRC_PATH=$source_path" >> config.mak +echo "LOCAL_INC_PATH=$local_inc" >> config.mak + + +echo "#endif" >> $TMPH + +# Do not overwrite config.h if unchanged to avoid superfluous rebuilds. +if ! cmp -s $TMPH config.h; then + rm -f config.h + mv -f $TMPH config.h +else + echo "config.h is unchanged" +fi + +rm -f $TMPO $TMPC $TMPS $TMPCPP $TMPH + +if [ ! -d "./bin" ]; then + mkdir ./bin +fi +if [ ! -d "./bin/gcc" ]; then + mkdir ./bin/gcc +fi +if [ ! -d "./bin/gcc/temp" ]; then + mkdir ./bin/gcc/temp +fi + +echo '%.opic : %.c' >> config.mak +echo ' $(CC) $(CFLAGS) $(PIC_CFLAGS) -c $< -o $@' >> config.mak + +echo "Done - type 'make help' for make info, 'make' to build" diff --git a/doc/CODING_STYLE b/doc/CODING_STYLE new file mode 100644 index 0000000..58f944b --- /dev/null +++ b/doc/CODING_STYLE @@ -0,0 +1,136 @@ +GPAC coding styles as of 0.4.0 + +Foreword: + As of 0.4.0, GPAC is being licensed under the LGPL. This brought the concern of refining coding conventions inside the framework so as + to extract the core functionlaities of GPAC to third-party developers. + The number of modules present in GPAC having been developed for many years without any specific coding styles, the entire GPAC framework + has been rewrote in a NON-BACKWARD COMPATIBLE WAY. + +Introduction + coding styles only concern the GPAC library (M4Systems in GPAC<=0.3.0, libgpac now), and, although recommended, are not mandatory for plugins + or applications development. + +1 Exported symbols + + 1.0 Informative note + The GF_ or gf_ stands for "gpac framework" + + 1.1 typedef of structures - typedef of base types - typedef of functions + +All symbols defined within libgpac library and exported for application development purposes shall begin be named GF_* +with the first character of "*" being in capital, regardless of their functionality. +For example, GF_Thread (structure), GF_Err (redefined type), GF_LineCap (enum typedef) + +All structure typedefs, whether defined (declaration exported through ehaders) or not (declaration intern to the GPAC core lib), must follow +this principle. +Furthermore, typedef of non exported structure shall only apply to the structure itself, not a pointer to it. + typedef struct _my_gpac_thingy *GF_LPMYGPACSTUFF; /*this is FORBIDDEN*/ + typedef struct _my_gpac_thingy GF_MyGPACStuff; /*this is OK*/ +This will allow easy exporting of structure declaration if needed at some point in the development. + + + 1.2 constants + +All constants (whether #defines or enums) shall be in capital letters and begin with "GF_" and use "_" for word separation +For example, error code GF_BAD_PARAM. + +Enums names shall refer to the main module they are used in through a keyword right after the GF_ . +For example, + * enums refering to file format (isomedia) will be prefixed by GF_ISOM_ + * enums refering to MPEG-4 OD tools (odf) will be prefixed by GF_ODF_ + + 1.3 functions + +All exported functions and pointer to functions shall begin with "gf_". The name shall refer to the main module they are sued in +through a keyword right after the gf_ when reasonable (gf_isom_, gf_odf_, gf_term_), or to the tool they refer to (gf_bs_, gf_url, ...) + +2 Miscalleanous + +All exported functions' names shall be in lower case exclusively. +All exported functions' parameters should be in lower case exclusively. +Exported structures may use case in any fashion as long as they respect the rules expressed in section 1 above. +Exported structures member should preferably all be lower case - this may not be feasible for scene graph nodes & like... + +All exported header files shall be in lower case +All source files within the gpac core shall be in lower case + +All comments should be C-like ones (/**/) not C++ ones (//) +All exported headers documentation should be written with dowygen syntax +All constructor-like functions should be of style gf_zzz_new +All destructor like functions should be of style gf_zzz_del + +3 Rearchitecture of the GPAC repository + + This recaps the changes between gpac 0.3.0 and gpac 0.4.0 as far as the core architecture is concerned. + + + The former M4Systems library is now simply libGPAC (eg libgpac.lib, libgpac.dll, libgpac.so, libgpac.a) + Currently no decision has been taken regarding splitting of this library in smaller packages, this still needs discussion. + + directory architecture: + /gpac + /gpac/applications/ + former Applications/, misc renames + /gpac/applications/generators: former SceneGenerators + /gpac/applications/testapps: added for apps testing some features of gpac (currently only BIFSEngine tester) + /gpac/bin + rename of directories: + arm_debug -> arm_ppc02_deb + arm_release -> arm_ppc02_rel + Debug -> w32_deb + Release -> w32_rel + /gpac/build: former IDEs directory, with project files for MS eVC*/VC* + /gpac/doc + /gpac/doc/libgpac: future place for doc of all exported functions from libgpac + /gpac/extra_lib + /gpac/extra_lib/include + unchanged + /gpac/extra_lib/lib + rename of directories: + arm_debug -> arm_ppc02_deb + arm_release -> arm_ppc02_rel + w32_debug -> w32_deb + w32_release -> w32_rel + + + /gpac/src: former M4Systems directory, source for the gpac core lib + /gpac/src/utils: + former Tools - function names: gf_* + /gpac/src/isomedia: + former MP4 - code for isomedia - function names: gf_isom_* + /gpac/src/odf: + former OD - code for MPEG-4 OD Framework - function names: gf_odf_* + /gpac/src/ietf: + former IETF - code for IETF SDP/RTP/RTCP/RTSP protocols - function names: (or gf_sdp_*, gf_rtp_*, gf_rtsp_*) + moved packetizers in this place since payloads are quite related to IETF... + /gpac/src/bifs: + former BIFS - code for BIFS encoding and decoding - exported function names: gf_bifs_* + /gpac/src/terminal: + former ESM - code for terminal module - exported function names: gf_term_* + /gpac/src/mcrypt: + unchaged - function names: gf_crypt_* + /gpac/src/renderer: + former renderer - function names: gf_sr_* + /gpac/src/scenegraph: + former SceneGraph - function names: gf_sg_* for all common code (base nodes and graph handling), + gf_sg_vrml_*: MPEG4, VRML, X3D related + gf_sg_svg_*: SVG related (maybe laser-related) + + /gpac/src/media_tools: + former authoring on media import/export and IsoMedia file stuff. function names: gf_media_*, gf_hinter_ + + /gpac/src/scene_manager: + former authoring on scene manager. function names: gf_sm_* + + + /gpac/include + /gpac/include/gpac: + splitting of former m4_tools.h into dedicated headers, and for some other modules too (media parsers, ...) + + /gpac/include/gpac/internal: + former intern , no big changes + + /gpac/modules: + former Plugins directory. + + \ No newline at end of file diff --git a/doc/INSTALL.gcc b/doc/INSTALL.gcc new file mode 100644 index 0000000..7026a5e --- /dev/null +++ b/doc/INSTALL.gcc @@ -0,0 +1,116 @@ +Installation instructions for GPAC 0.4.5 on GCC-powered platforms +last modified: December 2008 + +0 Foreword + The output directory for all plugins and applications is gpac/bin/gcc + + As of 0.2.2, GPAC cannot be compiled without ZLIB. You'd better make sure it is installed on your system (zlib is provided in gpac_extra_libs package) + +I Extra lib installation + It is recommended to install (source or package) all extra libs needed by gpac not installed on your system. + Libraries found on the system are indicated as an output of the configure script + + +II GPAC compilation + + II.1 SDL Support + GPAC can use SDL for audio/video output. If SDL is not installed on your system, you may indicate configure to build with a local copy: + --sdl-cfg=path/to/local/sdlcfg/ + (make sure to update the local sdl-cfg according to your needs) + + If you can't get SDL and don't have OSS or WAV audio nor DirectX or X11 video support on your system, you won't be able to play any presentation with GPAC, + but you can still use MP4Box. + + II.2 wxWidgets Support + GPAC comes with a GUI player called Osmo4. To compile this player you will need wxWidgets 2.6.0 (2.5.2 should work) installed on your system. + Both unicode and ANSI versions of wxWidgets should be supported. + If you don't use wxWidgets, you can always use GPAC command-line player MP4Client. + + II.3 MinGW DirectX support + When building GPAC under MinGW, it is also possible to compile the DirectX plugin. You will need the MinGW versions of DX libs + (available at http://alleg.sourceforge.net/files/). Get dx70_mgw or dx80_mgw (dx8 is provided in gpac_extra_libs package) + copy the archive content in your MSys tree (for ex, /usr/local/DirectX) and configure gpac with the option --dxsdk-path=/usr/local/DirectX + ** you must keep include and lib folders under the same directory for the configure script to detect DirectX ** + + II.4 Building GPAC + go to root of gpac distribution + ./configure (--help for options) - you may need to "chmod +x" this file... + make + + any fixes to configure are welcome :) + + II.5 Installing GPAC + get root + type "make install" in gpac/ + + This will install MP4Client, Osmo4 if configured, MP4Box and all plugins as well as GPAC documentation man: MP4Box(1), MP4Client(1) and GPAC(1). + type "make uninstall" to remove gpac from your system + + II.6 Installing GPAC SDK + get root + type "make install-lib" in gpac/ + + This will install gpac base headers (), gpac development headers ( and libgpac_static - the static version of libgpac shared library. + type "make uninstall-lib" to remove gpac from your system + There is no documentation regarding headers/SDK for now, you will have to rely on function descriptions in each header. + +III Running GPAC + + III.1 MP4Client + MP4Client is a command-line interface to GPAC. Note that the player cannot work without video support + + You need a GPAC configuration file to run MP4Client, and you will need it each time. + + First launch of MP4Client + go to gpac/bin/gcc if not using the install. + type MP4Client + the prompt will ask for + 1- GPAC plugin dir: enter the path from / to gpac/bin/gcc. This is skipped when using the install version of MP4Client (the plugin path is hardcoded to + the plugins install location on the system) + 2- Font directory: enter the path to a truetype font directory on your system (note that if you don't have compiled with freetype any directory will do) + 3- cache directory: any directory with write access + You now have a valid config file for GPAC, more info on this try "man GPAC" or check gpac/doc/configuration.html. + + The config file is called ".gpacrc" and is located in the user home directory. You may run the client with a different config file by using the "-c" option. + + *If you don't see any output window, check the config file doesn't use gm_raw_out.so as a video renderer (or simply remove gm_raw_out.so). + + MINGW USERS: there are known and terrible bugs with MSys rxvt stdio buffering, do NOT use it to run MP4Client unless you want to understand + these bugs. Use w32 CMD.exe instead. Other GPAC apps are no pb for MSys rxvt. + + III.2 Osmo4 + Osmo4 is the GUI frontend to GPAC. If you have installed Osmo4 on your system, the first launch of the player should ask you to locate a + directory with TrueType fonts and a cache directory for internet downloads if no configuration file is found. + + III.3 MP4Box + MP4Box is a tool to encode, decode and manipulate MPEG-4 systems data. It does not need a configuration file. + Help for MP4Box is available on GPAC web site, with man MP4Box (except on MinGW) and with 'MP4Box -h' + + III.4 Osmozilla + Osmozilla is GPAC plugin for Mozilla-based browsers. It is by default installed to the user mozilla directory ~/.mozilla + To install the plugin on the system + * get root + * ./configure --mozdir=/path/to/mozilla + for example --mozdir=/usr/lib/mozilla-firefox + * make -C applications/osmozilla install + + * OR you may copy by hand bin/gcc/nposmozilla.so to /usr/lib/moz**/plugins and bin/gcc/nposmozilla.xpt to /usr/lib/moz**/components + +IV Configuration + + IV.0 Foreword + All configuration information is described in gpac/doc/configuration.html, or man gpac. + + IV.1 OpenGL + OpenGL is badly known for performing quite poorly as far as high data rate texturing is involved. This is a big issue when displaying a typical movie and you will likely find the GPAC 3D Rendering very slow on your system. If your GPU supports non power of 2 texturing or rectangular texturing (most Win32 drivers do) + you shouldn't have any problem with video. Otherwise here are some tips to configure GPAC on your system: + 1- set the "BitmapCopyPixels" option on: some cards perform rather well at direct pixel transfer. If no improvement, set it off. + 2- set the "BitmapCopyPixels" option off and the "EmulatePOW2" option on. This will trick the GL texturing by using only Power Of 2 textures when converting from YUV to RGB. + 3- If this does not improve video playback, you're only chance is through discussion forums & co to gather info about your system, your GL implementation and how to fine-tune it. + +V Misc + + There is a demo 2D authoring tool called V4Studio. No makefiles available yet but should compile without pbs (only needs libgpac + and wxWidgets ). It is not usable to design content but is a funny toy. + + diff --git a/doc/INSTALL.gpe b/doc/INSTALL.gpe new file mode 100644 index 0000000..b767b32 --- /dev/null +++ b/doc/INSTALL.gpe @@ -0,0 +1,100 @@ +Installation instructions for GPAC 0.4.5 on Familiar+GPE platforms +last modified: December 2008 + +0 Foreword + This file is about installing the GPAC framework on an arm device running Linux familiar (cf http://www.handhelds.org) + + Compilation has only been tested for familiar+GPE platforms ( http://gpe.handhelds.org). + The GPE version enables GPAC to use X11 video output directly, including shared memory extensions. + + The output directory for all plugins and applications is gpac/bin/gcc + + As of 0.2.2, GPAC cannot be compiled without ZLIB. You'd better make sure it is installed on your system (zlib is provided in gpac_extra_libs package) + + To install the arm cross-compilation environement on your linux system, refer to: + * http://www.handhelds.org + * we're currently using the following tool-chain for Familiar+GPE compil http://www.roebling.de/embedded.html, with X11 and GTK+ support, + already compiled. + + Do not forget to update your environment variables according to your toolchain. + +I Extra lib installation + It is recommended to install all extra libs needed by gpac not installed on your system. + +II GPAC compilation + + II.1 SDL Support + GPAC can use SDL for audio/video output. If SDL is not installed on your system, you may indicate configure to build with a local copy: + --sdl-cfg=path/to/local/sdlcfg/ + (make sure to update the local sdl-cfg according to your needs) + + If you can't get SDL and don't have OSS audio nor X11 video support on your system, you won't be able to play any presentation with GPAC, + but you can still use MP4Box. + + II.2 wxWidgets Support + GPAC comes with a GUI player called Osmo4. To compile this player you will need wxWidgets 2.6.0 (2.5.2 should work) installed on your system. + NOTE: Osmo4+wxWidgets is quite slow on familiar+GPE, and is therefore not recommended at the time being. + You can always use GPAC command-line player MP4Client. + + II.3 OpenGL ES support + OpenGL ES is not yet supported on Familiar-GPE. However, GPAC renderer supports OpenGL ES API. Porting the 3D module only requires modifying the X11 output (modules/x11_out) for + OpenGL ES. + + II.4 Building GPAC + go to root of gpac distribution. Note: you may need to "chmod +x" the configure file. + (./configure --help for options) + ./configure --prefix=/usr/local/arm/3.3.2 --cpu=armv4l --enable-fixed-point + Note: this assume the cross-compilation tool-chain is located in /usr/local/arm/3.3.2 + Note: you may also specify any other option supported by configure. It is not recommended to use the floating-point version of GPAC for ARM-based architectures. + make + + II.5 Installing GPAC + copy all files to your device, possibly creating a dedicated directory for modules + edit or create ~/.gpacrc to have at least the following lines: + [General] + ModulesDirectory=AbsolutePathToTheModules + + For more information on GPAC configuration file, cf man GPAC of gpac/doc/configuration.html + +III Running GPAC + + III.1 MP4Client + MP4Client is a command-line interface to GPAC. Note that the player cannot work without video support (so you'd better get SDL) + + You need a GPAC configuration file to run MP4Client, and you will need it each time. + + First launch of MP4Client + go to gpac/bin/gcc if not using the install. + type MP4Client + the prompt will ask for + 1- GPAC plugin dir: enter the path from / to gpac/bin/gcc. This is skipped when using the install version of MP4Client (the plugin path is hardcoded to + the plugins install location on the system) + 2- Font directory: enter the path to a truetype font directory on your system (note that if you don't have compiled with freetype any directory will do) + 3- cache directory: any directory with write access + You now have a valid config file for GPAC, more info on this try "man GPAC" or check gpac/doc/configuration.html. + + The config file is called ".gpacrc" and is located in the user home directory. You may run the client with a different config file by using the "-c" option. + + *If you don't see any output window, check the config file doesn't use raw_out.so as a video renderer (or simply remove raw_out.so). + + III.2 Osmo4 + Osmo4 is the GUI frontend to GPAC. If you have installed Osmo4 on your system, the first launch of the player should ask you to locate a + directory with TrueType fonts and a cache directory for internet downloads if no configuration file is found. + + III.3 MP4Box + MP4Box is a tool to encode, decode and manipulate MPEG-4 systems data. It does not need a configuration file. + Help for MP4Box is available on GPAC web site, with man MP4Box and with 'MP4Box -h' + +IV Configuration + + IV.0 Foreword + All configuration information is described in gpac/doc/configuration.html, or man gpac. + + IV.1 OpenGL + OpenGL is badly known for performing quite poorly as far as high data rate texturing is involved. This is a big issue when displaying a typical movie and you will likely find the GPAC 3D Renderer very slow on your system. If your GPU supports non power of 2 texturing or rectangular texturing (most Win32 drivers do) + you shouldn't have any problem with video. Otherwise here are some tips to configure GPAC on your system: + 1- set the "BitmapCopyPixels" option on: some cards perform rather well at direct pixel transfer. If no improvement, set it off. + 2- set the "BitmapCopyPixels" option off and the "EmulatePOW2" option on. This will trick the GL texturing by using only Power Of 2 textures when converting from YUV to RGB. + 3- If this does not improve video playback, you're only chance is through discussion forums & co to gather info about your system, your GL implementation and how to fine-tune it. + + diff --git a/doc/INSTALL.symbian b/doc/INSTALL.symbian new file mode 100644 index 0000000..0f77545 --- /dev/null +++ b/doc/INSTALL.symbian @@ -0,0 +1,82 @@ +Installation instructions for GPAC 0.4.5 on Symbian 9.1 (S60 3rd Edition) platform +last modified: December 2008 + +0 Foreword + Compilation has only been tested with GCCE & Nokia S60 SDK + In order to fully compile, you must get: + - the complete S60 3rd edition SDK (maintainance release) + - the MMF SDK update + + IMPORTANT NOTE: + You must install the SDK and the code you will compile with it (be it GPAC or anything else) + !! ON THE SAME LOGICAL DRIVE !! + If you do not do so, compilation will simply fail due to some misbehavior of the SDK's environment variables. + + + What is currently supported on symbian: + * all GPAC core, with audio and video output + * MP4 demux, AAC demux, MP3 demux, AMR demux + * native audio media codec from the phone through MMF: AAC and AMR + * MPEG-4 ASP video decoding through xvid + * JPEG and PNG decoding through libjpeg and libpng + * MP3 decoding through MAD + * TrueType fonts through FreeType + * Scripting with JS32 (not fully tested yet) + * FFMPEG (not really stable, random crashes) + + What is currently NOT supported on symbian: + * networking + + TIP: + Global compiler options are located in SDKRoot\Epoc32\tools\compilation_config\ - you may edit the default C++ rules to remove some + warnings during C compil. + + Side notes: + several SDKs may coexist on the drive. + * To see the list of SDKs, at DOS prompt, c:\devices + * To change the default SDK, at DOS prompt, c:\devices -setdefault @sdk_name + you may need to exit prompt and start a new one in order to refresh environment variables. + GPAC should compile on EKA2 (Symbian OS v8.0b, v8.1b). + GPAC CANNOT COMPILE ON EKA1 (Symbian OS v6.1, v7.0, v7.0s, v8.0a v8.1a) because EKA1 does not support writable static data in DLLs + +I Extra lib installation + Please follow the instructions in gpac_extra_lib/00_README_FIRST + +II GPAC compilation + + Go to gpac/build/symbian + + II.1 Configuring extra libs + * If you don't have libjs, libpng or libjpeg for symbian, comment indicated lines in libgpac.mmp (change #if 1 into #if 0) + * If you don't have OpenGL ES for symbian, comment indicated lines in libgpac.mmp (change #if 1 into #if 0) + * If you don't have freetype for symbian, comment ft_font.mmp in file bld.inf + * If you don't have ffmpeg for symbian, comment ffmpeg_in.mmp in file bld.inf + * If you don't have libmad for symbian, comment indicated lines in mp3_in.mmp + * If you don't have libopenjpeg (JPEG 2000) for symbian, comment indicated lines in img_in.mmp (change #if 1 into #if 0) + + II.2 Compiling GPAC + cd gpac/build/symbian + bldmake bldfiles + * for GCCE + abld build gcce urel + * for thumb + abld build thumb urel + cd sis + set EPOCROOT=\path\to\epoc\root\ + * for GCCE + makesis -d%EPOCROOT% osmo4_gcce.pkg + signsis osmo4_gcce.SIS osmo4_gcce.SIS gpac.cer gpac.key password + * for THUMB + makesis -d%EPOCROOT% osmo4_thumb.pkg + + Note: If you need to sign the SIS with your own certificate, you may generate one with openssl: + openssl req -new -x509 -nodes -sha1 -days 3650 -key gpac.key > gpac.cer + +You will get a .SIS package that installs properly on a symbian device. +NOTES: +* The app and plugins are all installed in \sys\bin on the device. DO NOT change this path, since it won't work otherwise (due to symbian 9.1 caged data stuff) +* If you create/succeed to compile a new plugin for GPAC, you must: + - edit build/symbian/sis/osmo4_gcce.pkg to add your plugin in the installer + - edit build/symbian/sis/GPAC.cfg to instruct the player of this new plugin (enumeration of DLLs on Symbian 9.1 is just not possible without a good amount of $$) + + diff --git a/doc/INSTALL.w32 b/doc/INSTALL.w32 new file mode 100644 index 0000000..464f870 --- /dev/null +++ b/doc/INSTALL.w32 @@ -0,0 +1,216 @@ +Installation instructions for GPAC 0.4.5 on windows platform +last modified: December 2008 + +0 Foreword + Compilation has only been tested with MSVC 6 and VS2005 (VC8). + + The output directory for all plugins and applications is + gpac/bin/w32_deb in debug mode + gpac/bin/w32_rel in release mode + + As of 0.2.2, GPAC cannot be compiled without ZLIB. You'd better make sure it is installed locally or on your system (zlib is provided in gpac_extra_libs package) + +I Extra lib installation + + It is recommended to download and compile all extra libs needed by gpac on windows. Please read carefull the ReadMe file included in the gpac_extra_libs package + + +II GPAC compilation + + open the GPAC workspace: + gpac/build/msvc6/GPAC.dsw with MSVC6 + gpac/build/msvc8/gpac.sln with VS2005 + + NEVER ATTEMPT TO LOAD A PROJECT OUTSIDE THIS WORKSPACE, AS DEPENDENCY RULES WILL BE BROKEN + + II.1 Recompiling GPAC + libgpac is the core library of the GPAC framework used by all GPAC applications. It is available as a static library and as a dynamic one + Set the "libgpac_dll" project as the active one + If you have not installed the SpiderMonkey (JavaScript, libjs), JPEG or PNG libraries, remove the indicated macros in the file gpac/include/gpac/internal/config_static.h + recompile (libgpac_dll compilation will fail if zlib is not found) + + Note: If you wish to build the fixed-point version of GPAC (not recommended), you will have to modify by hand the file gpac/include/gpac/math.h + and replace the line + #define GPAC_NO_FIXED_POINT + by the line + #define GPAC_FIXED_POINT + + !!Do not attempts to enable fixed-point compilation in any other way!! + + II.2 Recompiling MP4Box + MP4Box is GPAC command-line tool for content authoring. + Set the "MP4Box" project as the active one + Recompile + + II.3 Plugins check + Before compiling other applications in GPAC you must make sure the projects are configured with the right libraries + + * aac_in + If you have not installed faad2 library, remove the GPAC_HAS_FAAD preprocessor variable from the aac_in project settings. + Note that if no aac decoder is installed for gpac you may as well remove the "aac_in" project from the workspace (DEL). + + * ac3_in + If you have not installed liba52 library, remove the GPAC_HAS_LIBA52 preprocessor variable from the ac3_in project settings. + Note that if no ac3 decoder is installed for gpac you may as well remove the "ac3_in" project from the workspace (DEL). + + * mp3_in + If you have not installed mad library, remove the GPAC_HAS_MAD preprocessor variable from the mp3_in project settings. + Note that if no mp3 decoder is installed for gpac you may as well remove the "mp3_in" project from the workspace (DEL). + + * xvid_dec + if you have not installed xvid library, remove the "xvid_dec" project from the workspace (DEL). + + * img_in: + If you have not installed libopenjpeg, remove the GPAC_HAS_JP2 preprocessor variable from the img_in project settings + and the relevant library from the link settings + If you have not installed any of the above libraries, you may remove the "img_in" project from the workspace (DEL). + + * amr_float_dec + This plugin handles speech coded data using AMR Narrow Band and Wide Band formats. It uses two decoders from the 3GPP consortium available at: + AMR Narrowband decoder: http://www.3gpp.org/ftp/Specs/archive/26_series/26.104/26104-700.zip + AMR Wideband decoder: http://www.3gpp.org/ftp/Specs/archive/26_series/26.204/26204-710.zip + + (The same versions are included in gpac_extra_libs/AMR_NB_FT and gpac_extra_libs/AMR_WB_FT ) + + To compile this plugin, copy the source code to modules/amr_float_dec/amr_nb_ft and modules/amr_float_dec/amr_wb_ft respectively, WITHOUT OVERWRITING typedefs.h files. + You may as well compile with only one of these libraries installed: + If you have not installed AMR NB, remove the GPAC_HAS_AMR_FT preprocessor variable from the amr_float_dec project settings + If you have not installed AMR WB, remove the GPAC_HAS_AMR_FT_WB preprocessor variable from the amr_float_dec project settings + Otherwise, remove the "amr_float_dec" project from the workspace (DEL). + + * amr_dec + This plugin handles speech coded data using AMR Narrow Band format with a fixed-point decoder (suited for embedded platforms). + It uses the decoder of the 3GPP consortium (TS26.073) available from http://www.3gpp.org/ftp/Specs/archive/26_series/26.073/26073-700.zip. + It is the same version included in gpac_extra_libs/AMR_NB with a slight header modifcation. + To compile this plugin, copy the source code to modules/amr_dec/amr_nb WITHOUT OVERWRITING typedefs.h file. + Otherwise, remove the "amr_dec" project from the workspace (DEL). Note you usually won't need this plugin, the float version of the decoders being much faster. + + * ffmpeg_in + To install ffmpeg libraries, cf gpac_extra_libs/ReadMe + if you have not installed ffmpeg libraries (avcodec.lib/dll, avformat.lib/dll, avutil.lib/dll), remove the "ffmpeg_in" project from the workspace (DEL). + + * ft_font + if you have not installed freetype, simply remove the "ft_font" project from the workspace (DEL). + + * gdip_rend + To install GDIPLus, either get Microsft PlatformSDK (http://www.microsoft.com/msdownload/platformsdk/sdkupdate/) or read gpac_extra_libs/GDIPlus/install.txt + if you have not installed GDIPlus or WindowsSDK, remove the "gdip_rend" project from the workspace (DEL). + + * ogg + if you have not installed libogg library, remove the "ogg" project from the workspace (DEL). + if you have not installed libvorbis library, remove the GPAC_HAS_VORBIS preprocessor variable from the ogg project settings + if you have not installed libtheora library, remove the GPAC_HAS_THEORA preprocessor variable from the ogg project settings + Note that if you have only installed libogg you can also remove the "ogg" project from the workspace, it won't be any usefull. + + * sdl_out: + GPAC can use SDL library for audio (all clients) and video (MP4Client andwxOsmo4) output. + You will need to get SDL 1.2 for windows. Get it at http://www.libsdl.org/download-1.2.php. You need the DEVELOPMENT LIBRARY SDL-devel-1.2.7-VC6.zip + Unpack and setup your path or VisualC++ for SDL include and lib directories (you may need to restart VisualC++) + You will need to modify your VC settings to look for libxml headers and library directories. + If you don't want to install libSDL or don't plan to use MP4Client or wxOsmo4, simply remove the "sdl_out" project from the workspace (DEL). + NOTE: SDL is not needed at all on windows, you will still have audio and video support without SDL. The SDL plugin is much slower than the directX plugin + when dealing with video since it uses software YUV to RGB conversion. + + NOTE: if you have not installed GDIPlus nor freetype, you won't have text support at all in GPAC. + + + II.4 Recompiling Osmo4 + Osmo4 is the GUI MPEG-4 player of GPAC for the windows platform. + Set Osmo4 as the active project + recompile (enabled plugins will be recompiled in the process) + + II.5 Recompiling MP4Client + MP4Client is the command MPEG-4 player of GPAC. + Set MP4Client as the active project + recompile (enabled plugins will be recompiled in the process) + + II.6 Recompiling Osmo4/wxWidgets + Osmo4 / wxWidgets is the cross-platform GUI MPEG-4 player of GPAC. You must have wxWidgets 2.6.0 installed and configured on your system (this may be tricky. Please + make sure you can recompile some sample wxWidgets applications from wxWidgets distribution first). + Set wxOsmo4 as the active project + recompile (some plugins may be recompiled in the process). You may need to change the linker settings, depending on your wxWidgets version and config + + II.7 Recompiling Osmozilla + Osmozilla is GPAC plugin for Mozilla-based browsers, enabling embedding all GPAC supported contents in a web page. Recompile it only if you think it may + be helpful. + * Get the gecko sdk (http://ftp.mozilla.org/pub/mozilla.org/mozilla/releases/mozilla1.7.13/gecko-sdk-i586-pc-msvc-1.7.13.zip or in the gpac_extra_libs package) + * Extract it to gpac/extra_lib/include/gecko-sdk + * Set Osmozilla as the active project and recompile + + II.7 Recompiling GPAX + GPAX is GPAC ActiveX control, only usable as an Internet Explorer plugin for now, enabling to embed all GPAC supported contents in a web page. Recompile it only if you think it may + be helpful (Set as the active project and recompile) + +III Launching the applications + + If you have compiled libgpac with SpiderMonkey (JavaScript) enabled, copy the JS32.dll file in the output directory. + + Some applications need to locate the GPAC configuration file called GPAC.cfg. This file is automatically generated by Osmo4 in its directory. It is recommended +to first launch Osmo4 to get a working config file. + For more information regarding the config file, cf gpac/doc/configuration.html + + You can launch Osmo4 as is, the application should start and be operational right away. + + You can use MP4Box as is. + + MP4Client needs GPAC configuration file to run. It will by default search for the file in the current directory, and some hardcoded directories that will likely not work. + If the config file is not found, a new one is created in the current directory. You can pass a given config file as a parameter (MP4Client -c config_path). + You can also modify the hardcoded path in MP4Client (gpac/Applications/MP4Client/main.c) to point to gpac output directory and recompile. + + + Osmozilla cannot be launched without a mozilla-based web browser. You should not try to install it in any other way than with the GPAC installer + (cf below). Once installed, you may try any test in an html page viewed with your browser, for example: + + + + To make sure your browser has loaded the osmozilla plugin and to check the mime types supported, try typing "about:plugins" in your browser address bar. + + GPAX cannot be launched without Internet Explorer. You should not try to install it in any other way than with the GPAC installer + (cf below). Once installed, you may try any test in an html page viewed with your browser, for instance: + + + + + +IV Configuration + + IV.0 Foreword + All configuration information is described in gpac/doc/configuration.html, or man gpac. + + IV.1 OpenGL + OpenGL is badly known for performing quite poorly as far as high data rate texturing is involved. This is a big issue when displaying a typical movie and you + will likely find the GPAC 3D Renderer very slow on your system. If your GPU supports non power of 2 texturing or rectangular texturing (most Win32 drivers do) + you shouldn't have any problem with video. Otherwise here are some tips to configure GPAC on your system: + 1- set the "BitmapCopyPixels" option on: some cards perform rather well at direct pixel transfer. If no improvement, set it off. + 2- set the "BitmapCopyPixels" option off and the "EmulatePOW2" option on. This will trick the GL texturing by using only Power Of 2 textures when converting + from YUV to RGB. + 3- If this does not improve video playback, you're only chance is through discussion forums & co to gather info about your system, your GL implementation + and how to fine-tune it. + +V Misc + + V.1 Scene Generators + In gpac/applications/generators you will find the code generators for MPEG-4, X3D and SVG scene graphs used in gpac. + If you want to modify the set of nodes understood by GPAC in encoding/decoding/rendering, you will need those. + The Scene generators can be recompiled without dependencies to extra libraries nor libgpac library. + To recompile them, open the related project files (.dsp) and recompile. + To use them, cf gpac/doc/SceneGenerators + + VI.2 V4Studio + V4Studio is a very simple authoring tool for 2D content. It is not usable but may interest developers. + For Installation instructions cf gpac/Applications/V4Studio/install + + VI.3 GPAC Installer + The GPAC installer uses the NSIS installing system. The installation script is gpac/bin/w32_rel/install/GPAC_Install.nsi + - Before building the installer + * get NullSoft installer (nsis.sourceforge.net), at least version 2.0 + * make sure the js32.dll is copied in the gpac/bin/w32_rel dir if you have compiled GPAC with SpiderMonkey support, otherwise comment + out "..\js32.dll" line from the install script + * copy gdiplus.dll to gpac/bin/w32_rel/install + * make sure avcodec.dll and avformat.dll are copied in gpac/bin/w32_rel if you have built ffmpeg plugin + * make sure iconv.dll, libxml2.dll, zlib1.dll in gpac/bin/w32_rel if you have compiled libxml2 + + - Building GPAC installer + The installer includes Osmo4, MP4Box, Osmozilla and all modules except SDL_out and raw_out. To install, right click on "GPAC_Install.nsi" and compile + For other configurations, modify the script "GPAC_Install.nsi" to comment out unwanted features + diff --git a/doc/INSTALL.wCE b/doc/INSTALL.wCE new file mode 100644 index 0000000..619e4f2 --- /dev/null +++ b/doc/INSTALL.wCE @@ -0,0 +1,133 @@ +Installation instructions for GPAC 0.4.5 on windows CE platform +last modified: Mai 2007 + +0 Foreword + + ** GPAC IS NO LONGER MAINTAINED ON PocketPC 2002/eVC3 PLATFORMS ** + + Compilation has only been tested with evc4 and ARM platforms + + GPAC 0.4.5 should be stable on most devices running PocketPC/SmartPhone 2003. It has know bugs with higher versions + of the Windows Mobile OS, but should still be stable. + + The output directory for all plugins and applications for Windows PocketPC 2003 is + gpac/bin/arm_ppc03_deb in debug mode + gpac/bin/arm_ppc03_rel in release mode + + As of 0.2.2, GPAC cannot be compiled without ZLIB. You'd better make sure it is installed locally or on your system (zlib is provided in gpac_extra_libs package) + +I Extra lib installation + It is recommended to download and compile all extra libs needed by gpac on winCE. + In case you don't do so, you will need to to modify project settings. + + +II GPAC compilation + open /build/msevc4/GPAC.dsw (GPAC workspace) with MS eVC4 + + NEVER ATTEMPT TO LOAD A PROJECT OUTSIDE THIS WORKSPACE + + II.1 Setting up libgpac + libgpac is the core library of the GPAC framework used by all GPAC applications + + If you have not installed the SpiderMonkey (JavaScript, libjs), JPEG or PNG libraries, remove the indicated macros in the file gpac/include/gpac/internal/config_static.h + + If you are compiling for PocketPC 2003 OS, and wish to use the Intel GPP library, uncomment the desired macro in + the file gpac/include/gpac/internal/config_static.h + + 3D Rendering + GPAC can use OpenGL ES for 3D rendering on embedded devices, but you will need development files of OpenGL ES. + GPAC has been tested with 2 implementations of OpenGL ES: + * HybridGraphics one (cf http://www.hybrid.fi/), by far the most efficient one tested, available for evc4/PockePC 2003 only + * Vincent3D (aka ogl-es, cf http://ogl-es.sourceforge.net/), slower but with good visual results + + To setup OpenGL ES, get one of these libraries, configure the IDE to locate the include and lib files (or copy them in gpac/extra_lib/ which is setup by default) + The GL ES lib shall be named "libGLES_CM.lib" or "libGLES_CL.lib" for the OpenGL-ES Light profile, and the GL ES include files shall be located in a "GLES" directory, the include path pointing to its parent directory. + + NOTE 1: The Klimt library (cf http://studierstube.org/klimt/), which is not 100% OpenGL_ES compliant, may be used but needs some code rewrite here and there, + mainly in gapi video output. + + NOTE 2: If you do not plan to support 3D rendering, uncomment the related macro in the file gpac/include/gpac/internal/config_static.h + + + II.2 Checking Plugins + + Before compiling GPAC plugins, you must make sure the projects are configured with the right libraries + * aac_in + If you have not installed faad2 library, remove the GPAC_HAS_FAAD preprocessor variable from the aac_in project settings. + Note that if no aac decoder is installed for gpac you may as well remove the "aac_in" project from the workspace (DEL). + + * ac3_in + If you have not installed liba52 library, remove the GPAC_HAS_LIBA52 preprocessor variable from the ac3_in project settings. + Note that if no ac3 decoder is installed for gpac you may as well remove the "ac3_in" project from the workspace (DEL). + + * mp3_in + If you have not installed mad library, remove the GPAC_HAS_MAD preprocessor variable from the mp3_in project settings. + Note that if no mp3 decoder is installed for gpac you may as well remove the "mp3_in" project from the workspace (DEL). + + * amr_nb + This plugin handles speech coded data using AMR Narrow Band format. It uses the decoder of the 3GPP consortium (TS26.073) available + from http://www.3gpp.org/ftp/Specs/archive/26_series/26.073/26073-700.zip. + It is the same version included in gpac_extra_libs/AMR_NB with a slight header modifcation. + To compile this plugin, copy the source code to modules/amr_dec/amr_nb WITHOUT OVERWRITING typedefs.h file. + Otherwise, remove the "amr_dec" project from the workspace (DEL). + + * img_in: + If you have not installed libopenjpeg, remove the GPAC_HAS_JP2 preprocessor variable from the img_in project settings + and the relevant library from the link settings + If none of the above lib is installed, you can simply remove the img_in project from the workspace (DEL). + + * ft_font + if you have not installed freetype, remove the project dependecies to freetype in project Osmo4 and MP4Client + NOTE: if you have not installed freetype, you won't have text support at all in GPAC. + + * ogg + if you have not installed libogg library, remove the "ogg" project from the workspace (DEL). + if you have not installed libvorbis library, remove the GPAC_HAS_VORBIS preprocessor variable from the ogg project settings + if you have not installed libtheora library, remove the GPAC_HAS_THEORA preprocessor variable from the ogg project settings + Note that if you have only installed libogg you can also remove the "ogg" project from the workspace, it won't be any usefull. + + * gapi: + you must make sure you have installed the GAPI SDK for windows (also known as GX) before compiling - cf gpac_extra_libs/ReadMe + + II.3 Recompiling + select the target platform (PocketPC or Smartphone) + Rebuild all (Build->Batch Build->Rebuild All) + + Note: + Osmo4 build will fail for smartphone devices, the GPAC GUI available on this platform is Osmophone + + II.4 Launching Osmo4 or Osmophone + Osmo4/Osmophone will automatically create a config file in the Osmo4/Osmophone executable directory if needed. You must copy all plugins to this + directory (recommended), or edit by hand the config file "ModulesDirectory" key to point to the modules directory, otherwise Osmo4/Osmophone will not + launch. You can then launch Osmo4/Osmophone (cf Misc section below) + +III GPAC Installer for WinCE + + The GPAC installer uses MS installer and EZSetup to get an Osmo4/Osmophone installer on PocketPC 2003 Systems + Install scripts are located in gpac/bin/arm_ppc02_rel/install and gpac/bin/arm_ppc03_rel/install + THIS INSTALLER IS ONLY INTENDED FOR STRONGARM BASED DEVICES + + * compile GPAC for PocketPC 2003 + * make sure the js.dll is copied in the release build directory if you have compiled GPAC with SpiderMonkey support, otherwise comment lines "js.dll" in gpac.inf + * make sure the libGLES_CM.dll (or libGLES_CL.dll ) is copied in the release build directory if you have compiled with OpenGL ES support, otherwise comment lines "libGLES_CM.dll" and "gm_render3d.dll" in gpac.inf + * make sure the libxml2.dll is copied in the release build directory if you have compiled the svg loader, otherwise comment lines "libxml2.dll" in gpac.inf + * remove any lines in gpac.inf that refer to plugins you have not built. + * if you have built the smartphone version, change AppName and ExeName accordingly in gpac.inf + * get MS PocketPC Cabwizard (search for Cabwiz.exe in your ActiveSync distribution) + * copy cabwiz.exe, cabwiz.ddf and MAKECAB.exe in the install directory + * if needed copy gx.dll in the install directory (download from microsoft, or use gpac extra libs version) + * get Ezsetup from http://www.eskimo.com/~scottlu/win/, and copy it in this directory + * run do.bat file + +IV Misc + + IV.1 Note on Video + As of 0.2.3, the OpenDivx plugin is no longer included in GPAC. A new, modified version of XviD 1.0 for ARM is included (cf gpac/modules/xvid_dec/xvid_wce/ReadMe.txt) + + IV.2 Note on Network + Ipaq devices under WindowsCE below Windows CE .NET 4.1 (and maybe other devices) have a small default UDP buffer size, and this buffer size cannot + be changed through WinSock (software) calls. To increase it, you must manually edit the registry and set (add a DWORD key if not present): + HKEY_LOCAL_MACHINE\Comm\Afd\DgramBuffer 16 + 16 is the maximum value possible, DON'T TRY TO SPECIFY MORE. + The device MUST be reseted (soft reset, cf your manual) for the parameter to be taken into account. + diff --git a/doc/ISO 639-2 codes.txt b/doc/ISO 639-2 codes.txt new file mode 100644 index 0000000..e3b3665 --- /dev/null +++ b/doc/ISO 639-2 codes.txt @@ -0,0 +1,484 @@ +"Abkhazian","abk", "ab" +"Achinese","ace", "" +"Acoli","ach", "" +"Adangme","ada", "" +"Adygei","ady", "" +"Adyghe","ady", "" +"Afar","aar", "aa" +"Afrihili","afh", "" +"Afrikaans","afr", "af" +"Afro-Asiatic (Other)","afa", "" +"Akan","aka", "" +"Akkadian","akk", "" +"Albanian","alb/sqi", "sq" +"Aleut","ale", "" +"Algonquian languages","alg", "" +"Altaic (Other)","tut", "" +"Amharic","amh", "am" +"Apache languages","apa", "" +"Arabic","ara", "ar" +"Aragonese","arg", "" +"Aramaic","arc", "" +"Arapaho","arp", "" +"Araucanian","arn", "" +"Arawak","arw", "" +"Armenian","arm/hye", "hy" +"Artificial (Other)","art", "" +"Assamese","asmAsturian","ast", "as" +"Athapascan languages","ath", "" +"Australian languages","aus", "" +"Austronesian (Other)","map", "" +"Avaric","ava", "" +"Avestan","ave", "" +"Awadhi","awa", "" +"Aymara","aym", "ay" +"Azerbaijani","aze", "az" +"Bable","ast", "" +"Balinese","ban", "" +"Baltic (Other)","bat", "" +"Baluchi","bal", "" +"Bambara","bam", "" +"Bamileke languages","bai", "" +"Banda","bad", "" +"Bantu (Other)","bnt", "" +"Basa","bas", "" +"Bashkir","bak", "ba" +"Basque","baq/eus", "eu" +"Batak (Indonesia)","btk", "" +"Beja","bej", "" +"Belarusian","bel", "be" +"Bemba","bem", "" +"Bengali","ben", "bn" +"Berber (Other)","ber", "" +"Bhojpuri","bho", "" +"Bihari","bih", "bh" +"Bikol","bik", "" +"Bini","bin", "" +"Bislama","bis", "bi" +"Bokmål, Norwegian","nob", "" +"Bosnian","bos", "" +"Braj","bra", "" +"Breton","bre", "br" +"Buginese","bug", "" +"Bulgarian","bul", "bg" +"Buriat","bua", "" +"Burmese","bur/mya", "my" +"Caddo","cad", "" +"Carib","car", "" +"Castilian","spa", "" +"Catalan","cat", "ca" +"Caucasian (Other)","cau", "" +"Cebuano","ceb", "" +"Celtic (Other)","cel", "" +"Central American Indian (Other)","cai", "" +"Chagatai","chg", "" +"Chamic languages","cmc", "" +"Chamorro","cha", "" +"Chechen","che", "" +"Cherokee","chr" , "" +"Chewa","nya", "" +"Cheyenne","chy", "" +"Chibcha","chb", "" +"Chichewa","nya", "" +"Chinese","chi/zho", "zh" +"Chinook jargon","chn", "" +"Chipewyan","chp", "" +"Choctaw","cho", "" +"Chuang","zha", "" +"Church Slavic (Slavonic)","chu", "" +"Chuukese","chk", "" +"Chuvash","chv", "" +"Coptic","cop", "" +"Cornish","cor", "" +"Corsican","cos", "co" +"Cree","cre" , "" +"Creek","mus" , "" +"Creoles and pidgins(Other)","crp", "" +"Creoles and pidgins, English-based (Other)","cpe", "" +"Creoles and pidgins, French-based (Other)","cpf", "" +"Creoles and pidgins, Portuguese-based (Other)","cpp", "" +"Crimean Tatar","crh", "" +"Crimean Turkish","crh", "" +"Croatian","scr/hrv", "hr" +"Cushitic (Other)","cus", "" +"Czech","cze/ces", "cs" +"Dakota","dak", "" +"Danish","dan", "da" +"Dargwa","dar", "" +"Dayak","day", "" +"Delaware","del", "" +"Dinka","din", "" +"Divehi","div" , "" +"Dogri","doi", "" +"Dogrib","dgr" , "" +"Dravidian (Other)","dra", "" +"Duala","dua", "" +"Dutch","dut/nld", "nl" +"Dutch, Middle (ca. 1050-1350)","dum", "" +"Dyula","dyu", "" +"Dzongkha","dzo", "dz" +"Efik","efi", "" +"Egyptian (Ancient)","egy", "" +"Ekajuk","eka", "" +"Elamite","elx", "" +"English","eng", "en" +"English, Middle (1100-1500)","enm", "" +"English, Old (ca.450-1100)","ang", "" +"Erzya","myv", "" +"Esperanto","epo", "eo" +"Estonian","est", "et" +"Ewe","ewe", "" +"Ewondo","ewo", "" +"Fang","fan", "" +"Fanti","fat" , "" +"Faroese","fao", "fo" +"Fijian","fij", "fj" +"Finnish","fin", "fi" +"Finno-Ugrian (Other)","fiu", "" +"Fon","fon", "" +"French","fre/fra", "fr" +"French, Middle (ca.1400-1600)","frm", "" +"French, Old (842-ca.1400)","fro", "" +"Frisian","fry", "fy" +"Friulian","fur" , "" +"Fulah","ful", "" +"Ga","gaa", "" +"Gaelic","gla", "" +"Gallegan","glg", "gl" +"Ganda","lug", "" +"Gayo","gay", "" +"Gbaya","gba" , "" +"Geez","gez", "" +"Georgian","geo/kat", "ka" +"German","ger/deu", "de" +"German, Low","nds" , "" +"German, Middle High (ca.1050-1500)","gmh", "" +"German, Old High (ca.750-1050)","goh", "" +"Germanic (Other)","gem", "" +"Gikuyu","kik", "" +"Gilbertese","gil", "" +"Gondi","gon", "" +"Gorontalo","gor", "" +"Gothic","got", "" +"Grebo","grb", "" +"Greek, Ancient (to 1453)","grc", "" +"Greek, Modern (1453-)","gre/ell", "el" +"Guarani","grn", "gn" +"Gujarati","guj", "gu" +"Gwich´in","gwi", "" +"Haida","hai", "" +"Haitian","hat", "" +"Haitian Creole","hat", "" +"Hausa","hau", "ha" +"Hawaiian","haw", "" +"Hebrew","heb", "he" +"Herero","her", "" +"Hiligaynon","hil", "" +"Himachali","him", "" +"Hindi","hin", "hi" +"Hiri Motu","hmo", "" +"Hittite","hit", "" +"Hmong","hmn", "" +"Hungarian","hun", "hu" +"Hupa","hup", "" +"Iban","iba", "" +"Icelandic","ice/isl", "is" +"Ido","ido", "" +"Igbo","ibo" , "" +"Ijo","ijo", "" +"Iloko","ilo", "" +"Inari Sami","smn", "" +"Indic (Other)","inc", "" +"Indo-European (Other)","ine", "" +"Indonesian","ind", "id" +"Ingush","inh", "" +"Interlingua (International Auxiliary Language Association)","ina", "ia" +"Interlingue","ile", "" +"Inuktitut","iku", "iu" +"Inupiaq","ipk", "ik" +"Iranian (Other)","ira", "" +"Irish","gle", "ga" +"Irish, Middle (900-1200)","mga", "" +"Irish, Old (to 900)","sga", "" +"Iroquoian languages","iro", "" +"Italian","ita", "it" +"Japanese","jpn", "ja" +"Javanese","jav", "jv" +"Judeo-Arabic","jrb", "" +"Judeo-Persian","jpr", "" +"Kabardian","kbd", "" +"Kabyle","kab", "" +"Kachin","kac", "" +"Kalaallisut","kal", "kl" +"Kalmyk","xal", "" +"Kamba","kam", "" +"Kannada","kan", "kn" +"Kanuri","kau", "" +"Karachay-Balkar","krc", "" +"Kara-Kalpak","kaa", "" +"Karen","kar", "" +"Kashmiri","kas", "ks" +"Kashubian","csb", "" +"Kawi","kaw", "" +"Kazakh","kaz", "kk" +"Khasi","kha", "" +"Khmer","khm", "km" +"Khoisan (Other)","khi", "" +"Khotanese","kho", "" +"Kikuyu","kik", "" +"Kimbundu","kmb", "" +"Kinyarwanda","kin", "rw" +"Kirghiz","kir", "ky" +"Komi","kom", "" +"Kongo","kon" , "" +"Konkani","kok" , "" +"Korean","kor", "ko" +"Kosraean","kos" , "" +"Kpelle","kpe" , "" +"Kru","kro" , "" +"Kuanyama","kua", "" +"Kumyk","kum" , "" +"Kurdish","kur", "ku" +"Kurukh","kru" , "" +"Kutenai","kut", "" +"Kwanyama","kua", "" +"Ladino","lad" , "" +"Lahnda","lah" , "" +"Lamba","lam" , "" +"Lao","lao", "lo" +"Latin","lat", "la" +"Latvian","lav", "lv" +"Letzeburgesch","ltz", "" +"Lezghian (lezLimburgan - limLimburger - limlimburgish)","lim", "" +"Lingala","lin", "ln" +"Lithuanian","lit", "lt" +"Low German","nds", "" +"Low Saxon","nds", "" +"Lozi","loz" , "" +"Luba-Katanga","lub" , "" +"Luba-Lulua","lua" , "" +"Luiseno","lui", "" +"Lule Sami","smj", "" +"Lunda","lun" , "" +"Luo (Kenya and Tanzania)","luo", "" +"Luxembourgish","ltz", "" +"Lushai","lus" , "" +"Macedonian","mac/mkd", "mk" +"Madurese","mad" , "" +"Magahi","mag" , "" +"Maithili","mai", "" +"Makasar","mak", "" +"Malagasy","mlg", "mg" +"Malay","may/msa", "ms" +"Malayalam","mal", "" +"Maltese","mlt", "ml" +"Manchu","mnc", "" +"Mandar","mdr", "" +"Mandingo","man", "" +"Manipuri","mni" , "" +"Manobo languages","mno" , "" +"Manx","glv", "" +"Maori","mao/mri", "mi" +"Marathi","mar", "mr" +"Mari","chm" , "" +"Marshallese","mah", "" +"Marwari","mwr" , "" +"Masai","mas" , "" +"Mayan languages","myn" , "" +"Mende","men" , "" +"Micmac","mic" , "" +"Minangkabau","min" , "" +"Miscellaneous languages","mis" , "" +"Mohawk","moh" , "" +"Moksha","mdf", "" +"Moldavian","mol", "mo" +"Mon-Khmer (Other)","mkh" , "" +"Mongo","lol" , "" +"Mongolian","mon", "mn" +"Mossi","mos" , "" +"Multiple languages","mul" , "" +"Munda languages","mun" , "" +"Nahuatl","nah" , "" +"Nauru","nau", "na" +"Navaho","nav", "" +"Navajo","nav", "" +"Ndebele, North","nde", "" +"Ndebele, South","nbl", "" +"Ndonga, ndoNeapolitan","nap", "" +"Nepali","nep", "ne" +"Newari","new" , "" +"Nias","nia" , "" +"Niger-Kordofanian (Other)","nic", "" +"Nilo-Saharan (Other)","ssa" , "" +"Niuean","niu" , "" +"Nogai","nog", "" +"Norse, Old","non", "" +"North American Indian (Other)","nai" , "" +"Northern Sami","sme", "" +"North Ndebele","nde", "" +"Norwegian","nor", "no" +"Norwegian Bokmål","nob", "" +"Norwegian Nynorsk","nno", "" +"Nubian languages","nub" , "" +"Nyamwezi","nym" , "" +"Nyanja","nya", "" +"Nyankole","nyn", "" +"Nynorsk, Norwegian","nno" , "" +"Nyoro","nyo" , "" +"Nzima","nzi" , "" +"Occitan (post 1500)","oci", "oc" +"Ojibwa","oji" , "" +"Old Bulgarian","chu", "" +"Old Church Slavonic","chu", "" +"Old Slavonic","chu", "" +"Oriya","ori", "or" +"Oromo","orm", "om" +"Osage","osa", "" +"Ossetian - Ossetic","oss", "" +"Otomian languages","oto", "" +"Pahlavi","pal" , "" +"Palauan","pau", "" +"Pali","pli", "" +"Pampanga","pam", "" +"Pangasinan","pag", "" +"Panjabi","pan", "pa" +"Papiamento","pap" , "" +"Papuan (Other)","paa" , "" +"Persian","per/fas", "fa" +"Persian, Old (ca.600-400)","peo" , "" +"Philippine (Other)","phi" , "" +"Phoenician","phn" , "" +"Pohnpeian","pon" , "" +"Polish","pol", "pl" +"Portuguese","por", "pt" +"Prakrit languages","pra", "" +"Provençal","oci", "" +"Provençal, Old (to 1500)","pro" , "" +"Pushto","pus", "ps" +"Quechua","que", "qu" +"Raeto-Romance","roh", "rm" +"Rajasthani","raj", "" +"Rapanui","rap", "" +"Rarotongan","rar", "" +"Romance (Other)","roa", "" +"Romanian","rum/ron", "ro" +"Romany","rom" , "" +"Rundi","run", "rn" +"Russian","rus", "ru" +"Salishan languages","sal" , "" +"Samaritan Aramaic","sam" , "" +"Sami languages (Other)","smi" , "" +"Samoan","smo", "sm" +"Sandawe","sad" , "" +"Sango","sag", "sg" +"Sanskrit","san", "sa" +"Santali","sat", "" +"Sardinian","srd", "" +"Sasak","sas" , "" +"Saxon, Low","nds", "" +"Scots","sco", "" +"Scottish Gaelic","gla" , "" +"Selkup","sel" , "" +"Semitic (Other)","sem" , "" +"Serbian","srp", "sr" +"Serbo-Croatian", "scr", "sh" +"Serer","srr" , "" +"Shan","shn" , "" +"Shona","sna", "sn" +"Sichuan Yi","iii", "" +"Sidamo","sid" , "" +"Sign languages","sgn" , "" +"Siksika","bla" , "" +"Sindhi","snd", "sd" +"Sinhalese","sin", "si" +"Sino-Tibetan (Other)","sit", "" +"Siouan languages","sio", "" +"Skolt Sami","sms" , "" +"Slave (Athapascan)","den" , "" +"Slavic (Other)","sla" , "" +"Slovak","slo", "sk" +"Slovenian","slv", "sl" +"Sogdian","sog" , "" +"Somali","som", "so" +"Songhai","son" , "" +"Soninke","snk" , "" +"Sorbian languages","wen" , "" +"Sotho, Northern","nso" , "" +"Sotho, Southern","sot", "st" +"South American Indian (Other)","sai" , "" +"Southern Sami","sma", "" +"South Ndebele","nbl", "" +"Spanish","spa", "es" +"Sukuma","suk", "" +"Sumerian","sux" , "" +"Sundanese","sun", "su" +"Susu","sus" , "" +"Swahili","swa", "sw" +"Swati","ssw", "ss" +"Swedish","swe", "sv" +"Syriac","syr", "" +"Tagalog","tgl", "tl" +"Tahitian","tah", "" +"Tai (Other)","tai" , "" +"Tajik","tgk", "tg" +"Tamashek","tmh" , "" +"Tamil","tam", "ta" +"Tatar","tat", "tt" +"Telugu","tel", "te" +"Tereno","ter" , "" +"Tetum","tet" , "" +"Thai","tha", "th" +"Tibetan","tib", "bo" +"Tigre","tig" , "" +"Tigrinya","tir", "ti" +"Timne","tem" , "" +"Tiv","tiv" , "" +"Tlingit","tli" , "" +"Tok Pisin","tpi" , "" +"Tokelau","tkl" , "" +"Tonga (Nyasa)","tog" , "to" +"Tonga (Tonga Islands)","ton", "" +"Tsimshian","tsi" , "" +"Tsonga","tso", "ts" +"Tswana","tsn", "tn" +"Tumbuka","tum" , "" +"Tupi languages","tup", "" +"Turkish","tur", "tr" +"Turkish, Ottoman (1500-1928)","ota" , "" +"Turkmen","tuk", "tk" +"Tuvalu","tvl", "" +"Tuvinian","tyv" , "" +"Twi","twi", "tw" +"Udmurt (udmUgaritic)","uga" , "" +"Uighur","uig", "ug" +"Ukrainian","ukr", "uk" +"Umbundu","umb" , "" +"Undetermined","und", "" +"Urdu","urd", "ur" +"Uzbek","uzb", "uz" +"Vai","vai", "" +"Venda","ven" , "" +"Vietnamese","vie", "vi" +"Volapük","vol", "vo" +"Votic","vot", "" +"Wakashan languages","wak" , "" +"Walamo","wal" , "" +"Walloon","wln", "" +"Waray","war", "" +"Washo","was" , "" +"Welsh","wel", "cy" +"Wolof","wol", "wo" +"Xhosa","xho", "xh" +"Yakut","sah" , "" +"Yao","yao" , "" +"Yapese","yap", "" +"Yiddish","yid", "yi" +"Yoruba","yor", "yo" +"Yupik languages","ypk" , "" +"Zande","znd" , "" +"Zapotec","zap" , "" +"Zenaga","zen" , "" +"Zhuang","zha", "za" +"Zulu","zul", "zu" +"Zuni","zun", "" + diff --git a/doc/SceneGenerators b/doc/SceneGenerators new file mode 100644 index 0000000..5229e70 --- /dev/null +++ b/doc/SceneGenerators @@ -0,0 +1,148 @@ + GPAC Scene Graph generator documentation - v0.4.0 + + Last Modified: July 2005 + + + +0 - Foreword + + + +The sets (MPEG4, X3D and SVG) of nodes handled by the GPAC are genrated through "Scene Generators" applications: + +applications/generators/MPEG4 for MPEG4 along with the official MPEG-4 template files needed to generate nodes and their encoding tables. + +applications/generators/X3D for X3D along with the X3D template file needed to generate nodes. + +applications/generators/SVG for SVG along with the SVG template. + + + +MPEG-4 Template files are numbered by versions of amendments to the MPEG-4 systems standard. + +X3D and SVG templates do not have versionning. + + + +1 - Regenerating the scene graph + + + +First recompile the appropriated scene generator application. You don't need to recompile GPAC to recompile it. + +The generators DIRECTLY overwrites source code files in the GPAC distribution, you MUST NOT try to run it from a different + +location than gpac/applications/generators/* + + + +For MPEG-4 you must provide MPEG4Gen with the set of template files. For example, if you're planning to use only nodes defined in the + +first version of the standard (1998) described in templates1.txt file, just type: + + + +MPEG4Gen templates1.txt + + + +Template files MUST be fed in order, and versions cannot be skipped: you SHALL NOT try to generate version1 and version3 without version2. + +To generate a scene graph handling v1 to v3 of the BIFS system, type: + +MPEG4Gen templates1.txt templates2.txt templates3.txt + + + +For X3D, simply run X3DGen, it will automatically load the "templates_X3D.txt" file + +For SVG, simply run SVGGen completesvgt12rng.xml + + + + + +2 - Customizing the scene graph + + + +As of 0.2.2, all nodes in current gpac version are supported by renderers and cannot be removed. You will therefore have to REMOVE some code in the renderers + +and the scene graph in order to recompile GPAC. You should therefore not try to customize the scene graph unless you know what you're doing. + + + + 2.1 - Customizing the MPEG-4 scene graph + + + +As said above, it is not possible to skip a BIFS version when regenerating the scene graph since this will break binary encoding of the nodes. + +However MPEG4Gen allows you to specify which nodes should be supported or not in the scene graph. This is currently specified + +with a simple text file where unwanted nodes are listed one by line ('#' acting as a line comment). The file is specified by + +the "-p " switch + + + +For example, generating a scene graph for BIFS V1 without support for the BIFS audio nodes will be: + +MPEG4Gen -p skip_audio.txt templates1.txt + + + +and the content of skip_audio.txt file will be + + + +AudioBuffer + +AudioClip + +AudioDelay + +AudioFX + +AudioMix + +AudioSwitch + +AudioSource + + + + 2.2 - Customizing the X3D scene graph + + + +X3DGen uses the same mechanism as MPEG4Gen for node skinpping, eg: X3DGen skipfile + + + + 2.3 - Customizing the SVG scene graph + + + +This is undocumented and probably not supported + + + +3 - Advanced Manupulations + + + + It is possible to develop custom templates. The resulting encoding/decoding will not be compliant with MPEG-4 BIFS but it can + +be interesting to see how a single-version mechanism with only the desired nodes reduces applications and bitstreams sizes + +To write your own templates, you must: + + follow the syntax of regular templates + + make sure the SFWorldNode type is defined and used by all nodes (needed for BIFS updates) + + make sure at least one node will be of type SFTopNode (needed for BIFS Scene Replace command) + + + diff --git a/doc/configuration.html b/doc/configuration.html new file mode 100644 index 0000000..edea454 --- /dev/null +++ b/doc/configuration.html @@ -0,0 +1,650 @@ + + + + + + GPAC Configuration documentation + + +

    +
    +GPAC Configuration file documentation
    Version 0.4.5
    +
    +Last Modified $Date: 2008/12/02 18:04:42 $ +

    + +

    + +
    Overview +

    +Some applications in the GPAC framework use a configuration file shared among modules and reloadable at run time. Modules may use the configuration file as well (to avoid multiple config files). This doc attempts to provide explanations for the different options. +

    +The config file is based on the win32 .ini file model, thus is ordered by sections and keys. +
    A section is declared as SectionName. Defined sections are: +
    +General +RecentFiles +Systems +Compositor +Audio +Video +Network +FontEngine +Downloader +HTTPProxy +Streaming +MimeTypes +StreamingCache +SAXLoader +XviD +FFMPEG +ISOReader +DVB +ALSA + +

    +

    +A key is declared as keyName=value. The key value is not interpreted and always handled as ASCII text. +

    +
      +
    • +On Windows plateforms, this config file is called "GPAC.cfg" and is usually located in C:\\Program Files\\GPAC. Note that Osmo4 will always create a config file in its own directory when none is found. +

      +
    • +
    • +On Windows CE plateform, this config file is called "GPAC.cfg" and is usually located in \\Program Files\\GPAC. Note that Osmo4 / CE will always create a config file in its own directory when none is found. +

      +
    • +
    • +On GNU/Linux plateforms, this config file is called ".gpacrc" and is always located at the root of the user home directory (for ex, /home/jean/.gpacrc). +

      +
    • +
    +
    + +

    Note on module names: +
    Module names as given in the config file are names exported by each interface and not name of the physical library file (.dll, .so, ...). The physical file name can however be used to identify a module - it will then be replaced by the module name. + +

    +

    + + +Section "General" Back to top +

    +The General section of the config file holds player specific options. This section is not used by GPAC core engine. +

    +ModulesDirectory [value: path to directory] +

    +Specifies the path to modules directory. The MPEG-4 Systems engine cannot be loaded without modules. This option is used by GPAC clients on all platforms. +

    +CacheDirectory [value: path to storage directory] +

    +Specifies location of temp files. The user must have write access to this location. Although not used by applications, this is used by several modules. +

    + +

    +

    +

    + +Options defined for Osmo4 (Windows version and wxWidgets version):

    +Loop [value: "yes" "no"] +

    +Specifies whether the presentation has to be restarted when done playing. +

    +ConsoleOff [value: "yes" "no"] +

    +Specifies whether application messages (script, buffering, download progress) are displayed in the console or not. +

    +SingleInstance [value: "yes", "no"] +

    +Specifies if the player should be a single instance application or not (Osmo4-Win32 only). +

    +LookForSubtitles [value: "yes" "no"] +

    +Specifies if Osmo4 shall look for subtitle files when opening a presentation. +

    +ViewXMT [value: "yes" "no"] +

    +Specifies if scene dumping shall be done in XML (XMT, X3D) or in VRML-like syntax (BT, WRL). +

    +ConfigPanel [value: positive integer] +

    +Specifies the latest config panel selected by user. +

    +NoMIMETypeFetch [value: "yes", "no"] +

    +Specifies if the player has to check for mime type when following hyperlinks, or only follow links of known extensions. +

    +Loop [value: "yes", "no"] +

    +Specifies if the playlist shall be restarted when playback is over. +

    +PLEntry [value: positive integer] +

    +Indicates active playlist entry when player was last closed. Playlist backup is: +

    +
      +
    • "gpac_pl.m3u" on win32 platforms, stored in same directory as application
    • +
    • ".gpac_pl.m3u" on other platforms, stored in user home directory
    • +
    +FillScreen [value: "yes", "no"] +

    +Specifies if the display area shall fill all available space on screen. WindowsMobile/Symbian only +

    +DisableBackLight [value: "yes", "no"] +

    +Specifies if the screen backlighting shall be disabled while playing. WindowsMobile/Symbian only +

    +LastWorkingDir [value: "yes", "no"] +

    +Specifies the directory of the last local file opened. Smartphone Windows only +

    +Browser [value: string] +

    +Specifies prefered browser for WWW anchors and scene graph viewing - Only used by Osmo4/wxWidgets. +

    +StartupFile [value: filename] +

    +Specifies file to load upon startup of osmo4/MP4Client. If not specified, no file is loaded. +

    + +LogFile [value: filename] +

    +Specifies where to output GPAC's log. By default, the logs are written to stdout. Note that GPAC may be compiled without log support. This is not used by MP4Client. +

    +LogLevel [value: "none", "error", "warning", "info", "debug"] +

    +Specifies the level of the log. By default, no log is performed. +

    +LogTools [value: a ':'-separated list of the following values] +

    +Specifies which set of tools from GPAC should be logged. Available tools are: +

      +
    • core : libgpac core
    • +
    • coding : bitstream formats (audio, video, scene)
    • +
    • container : container formats (ISO File, MPEG-2 TS, AVI, ...)
    • +
    • network : network data exept RTP trafic
    • +
    • rtp : rtp, rtcp and rtsp trafic
    • +
    • author : authoring tools (hint, import, export)
    • +
    • sync : terminal sync layer
    • +
    • codec : terminal codec messages
    • +
    • parser : scene parsers (svg, xmt, bt) and other
    • +
    • media : terminal media object management
    • +
    • scene : scene graph and scene manager
    • +
    • script : scripting engine messages
    • +
    • compose : composition engine (events, etc)
    • +
    • render : renderng engine (2D, 3D, etc)
    • +
    • service : network service management
    • +
    • mmio : Audio/Video HW I/O management
    • +
    +

    + + + +Section "RecentFiles" Back to top +

    +The "RecentFiles" section of the config file holds last accessed files (hardcoded to no more than 20) in the last access order. The keys are +the file names and no value is used. This section is only used by GUI clients (osmo4/wxOsmo4) +

    + + +Section "Systems" Back to top +

    +The "Systems" section of the config file holds all configuration options for the MPEG-4 Systems engine. +

    +LanguageName [value: string] +

    +Specifies the user prefered language in readable english. This is used to select streams in case of alternate content in an audio object. +

    +Language3CC [value: 3-char code from ISO 639-2] +

    +Specifies the user prefered language as expressed in ISO 639-2. This is used to select streams in case of alternate content in an audio object. +

    +Language2CC [value: 2-char code from ISO 639-1] +

    +Specifies the user prefered language as expressed in ISO 639-1. This is used to select streams in case of alternate content in an audio object. +

    +AlwaysDrawBIFS [value: "yes" "no"] +

    +If set, late BIFS frame will still be drawn. If not set, the scene won't be redrawn untill BIFS streams are back in sync. This is by default off to keep better sync, but may be usefull when testing heavy content or slow renderers. +

    +ForceSingleClock [value: "yes" "no"] +

    +One big problem with MP4 files is that the notion of "duration" has been unclear for a long time, and most content available (audio-video files) specifiy a +wrong BIFS duration. In such a case the movie cannot be controled/seeked into. Another problem with ISMA streaming is that BIFS/OD don't use the same clock as +audio/video, thus seeking the main timeline does not seek AV media. Setting the ForceSingleClock will handle both cases by using a single timeline for all media +streams and setting the duration to the one of the longest stream. +

    +ThreadingPolicy [value: "Free" "Single" "Multi"] +

    +Specifies how media decoders are to be threaded. "Free" lets decoders decide of their threading, "Single" means that all decoders are managed in a single thread performing scheduling and priority +handling and "Multi" means that each decoder runs in its own thread. +

    +Priority [value: "low" "normal" "high" "real-time"] +

    +Specifies the priority of the decoders (priority is applied to decoder thread(s) regardless of threading mode). +

    +NoVisualThread [value: "yes" "no"] +

    +Specifies whether the visual rendering is done in the main codec manager or in a dedicated thread. +

    +DefAudioDec , DefVideoDec and DefImageDec [value: string] +

    +Specifies which module to use by default for audio/video/image decoding. The string is the name of the module to be used (same considerations as other modules, cf introduction). +

    +codec_XX_XX [value: string] +

    +Allows to specify default media module (audio/video) per stream type and object type. This is usefull if you have more than one decoder for a given type (ex, XviD and FFMPEG for MPEG-4 visual SP). The syntax is: +
    codec_AA_BB=modulename
    +where AA is the hexadecimal MPEG-4 streamType value for the codec (04=visual, 05=audio) and BB is the hexadecimal MPEG-4 objectTypeIndication of the media (0x20 = MPEG-4 video, 0x40=MPEG-4 Audio, ...). +The string is the name of the module to be used (same considerations as other modules, cf introduction). +

    + + +Section "Compositor" Back to top +

    +The "Compositor" section of the config file holds all configuration options for the compositor (logical rendering engine). +

    +Raster2D [value: string] +

    +Specifies the 2D rasterizer to use for vectorial drawing. Same as above, this module cannot be reloaded during a presentation.

    +FrameRate [value: float] +

    +Specifies the simulation frame-rate of the presentation - this value is also used by the MPEG-4 Systems engine to determine when a BIFS frame is mature for decoding.

    +AntiAlias [value: "None" "All" "Text"] +

    +Specifies antialiasing settings - whether the setting is applied or not depends on the graphics module / graphic card. +

    +
      +
    • "None": no anti-aliasing
    • +
    • "Text": anti-aliasing for text only
    • +
    • "All": complete anti-aliasing
    • +
    +HighSpeed [value: "yes" "no"] +

    +Specifies whether rendering should target speed or quality - whether the setting is applied or not depends on the renderer, graphics module / graphic card.

    +ForceSceneSize [value: "yes" "no"] +

    +Forces the scene to resize to the biggest bitmap available if no size info is given in the BIFS configuration.

    +StressMode [value: "yes" "no"] +

    +Specifies that the renderer runs in worst case scenario, recomputing display lists and reloading textures (sending them to graphics card) at each frame even when no change has occured.

    +BoundingVolume [value: "None" "Box" "AABB"] +

    +Specifies whether the bounding volume of an object shall be drawn or not. Note that the 2D renderer only uses rectangles as bounding volumes. The "AABB" value is used by the +3D renderer only, and specifies the object bounding-box tree shall be drawn.

    +StressMode [value: "yes" "no"] +

    +Specifies whether the renderer shall redraw the scene and reload texture at each simulation tick or whenever needed +

    +ColorKey [value: unsigned hexadecimal integer, formated as AARRGGBB] +

    +Specifies the color key to use for windowless rendering. GPAC currently doesn't support true alpha blitting to desktop due to limitations in most windowing toolkit, it therefore uses color keying mechanism. The alpha part of the key is used for global transparency of GPAC's output, if supported. +

    +DirectDraw [value: "yes" "no"] +

    +Specifies whether direct rendering should be used or not. In direct rendering mode, the screen is completely redrawn at each frame. In indirect rendering +object positioning is tracked from frame to frame and dirty rectangles info is collected in order to redraw the minimal amount of the screen buffer. Whether +the setting is applied or not depends on the graphics module (currently all modules handle both mode).

    +ScalableZoom [value: "yes" "no"] +

    +Specifies whether scalable zoom should be used or not. When scalable zoom is enabled, resizing the output window will also recompute all vectorial objects. Otherwise only the final buffer is stretched.

    +DisableYUV [value: "yes" "no"] +

    +Disables YUV hardware support (YUV hardware support may not be available for the current video output module).

    +RasterOutlines [value: "yes" "no"] +

    +Specifies that outlining shall be done through OpenGL pen width rather than vectorial outlining.

    +PolygonAA [value: "yes" "no"] +

    +Specifies whether polygon antialiasing should be used in full antialiasing mode. If not set, only lines and points antialiasing are used.

    +DisableBackFaceCulling [value: "yes" "no"] +

    +Specifies whether backface culling shall be disable or not. If not set, backface culling is performed.

    +Wireframe [value: "WireNone" "WireOnly" "WireOnSolid"] +

    +Specifies wireframe drawing options:

    +
      +
    • "WireNone": objects are drawn as solid
    • +
    • "WireOnly": objects are drawn as wireframe only
    • +
    • "WireOnSolid": objects are drawn as solid and wireframe is then drawn
    • +
    +DisableRectExt [value: "yes" "no"] +

    +Specifies whether OpenGL rectangular texture extension (GL_EXT_texture_rectangle or GL_NV_texture_rectangle) shall be used or not.

    +
      +
    • "yes": textures whose dimensions are not power of two are rescaled except if hardware support non-power-of-two textures (GL_ARB_texture_non_power_of_two) natively.
    • +
    • "no": if extension is available, textures are used without rescaling. Note that in this case texture transformations are disabled.
    • +
    +EmulatePOW2 [value: "yes" "no"] +

    +Enables power of 2 emulation. Ignored if openGL rectangular texture extension is enabled.

    +
      +
    • "yes": video texture is not resized but emulated with padding. This usually speeds up video mapping on shapes but disables texture transformations.
    • +
    • "no": video is resized to a power of 2 texture when mapping to a shape.
    • +
    +DisableGLUScale [value: "yes" "no"] +

    +Disables usage of gluScaleImage, which may be slower but nicer than GPAC's software stretch routines.

    +BitmapCopyPixels [value: "yes" "no"] +

    +Specifies whether the bitmap node should be drawn with regular openGL texturing ("no") or through pixel copy routines ("yes"). This greatly impacts video rendering through +bitmap depending on the graphics card. Some cards will have faster pixel copy routines that texture transfer ones, some won't. +

    +TextureTextMode (value: "Default", "Never", "Always"] +

    +Specifies whether text shall be drawn to a texture and then rendered or directly rendered. Using textured text can improve text rendering in 3D and also improve text-on-video like content. Default value will use texturing for OpenGL rendering.

    +OpenGLExtensions [value: string] +

    +Read-only option listing the OpenGL extensions supported by the GL driver. Only valid after the 3D renderer has been used. +

    + + +

    + + +Section "Audio" Back to top +

    The "Audio" section of the config file holds all configuration options for the audio rendering engine and hardware.

    +DriverName [value: string] +

    +Specifies the driver to use for audio rendering. This driver cannot be reloaded at run-time, the complete system must be restarted.

    +ForceConfig [value: "yes" "no"] +

    +Forces a given sound card configuration to be used. If not set the sound card will be setup to use 2 audio buffers of 1024 samples each. +This may not work properly on some audio cards due to hardware latency, therefore forcing the config may be very usefull.

    +NumBuffers [value: positive integer, 0 forbidden] +

    +When config is forced, specifies the number of audio buffers to allocate (audio buffers are played in ring).

    +TotalDuration [value: positive integer, 0 forbidden] +

    +When config is forced, specifies the total audio buffer size in milliseconds. Be aware that the longer the audio buffer is, the longer the audio latency will be when pausing an +audio object. The quality of fast forward audio playback will also be degradated when using large audio buffers.

    +NoResync [value: "yes" "no"] +

    +Disables audio resynchronization: audio data is never dropped but may get out of sync.

    +DisableMultiChannel [value: "yes" "no"] +

    +Disables audio multichannel output and always downmix to stereo. This may be usefull if the multichannel output behaves weirdly.

    +DisableNotification [value: "yes" "no"] +

    +Disables usage of audio buffer notifications when supported (currently only DirectSound supports it). If DirectSound audio sounds weird try without notifications.

    +Volume [value: integer (0-100)] +

    +Default audio volume used when launching GPAC.

    +Pan [value: integer (0-100)] +

    +Default audio stereo balance used when launching GPAC - 0 for full left, 100 for full right, 50 for balanced.

    + +

    + + +Section "Video" Back to top +

    The "Video" section of the config file holds all configuration options for the video renderer and hardware.

    +DriverName [value: string] +

    +Specifies the driver to use for video memory access. This driver cannot be reloaded at run-time, the complete system must be restarted.

    +SwitchResolution [value: "yes" "no"] +

    +Specifies fullscreen resolution mode. If enabled, selects smallest video resolution larger than scene size, otherwise use current video resolution.

    +UseHardwareMemory [value: "yes" "no"] +

    +Only valid for 2D renderer. Specifies if main video backbuffer is on hardware or system memory. Depending on the scene type, this may drastically change the playback speed.

    +DisableColorKeying [value: "yes" "no"] +

    +Only valid for 2D renderer. Specifies if partial overlays should be disabled. If not disabled, hardware color keying for overlays is tested and used if present. Otherwise, only the top-most video with no overlapping objects will be drawn using overlays. Default value is "no".

    +UseGLDoubleBuffering [value: "yes" "no"] +

    +Specifies if OpenGL double buffering shall be used. Default is "no".

    +GLNbBitsPerComponent [value: positive integer] +

    +Specifies the number of bits per color component. Default is 5.

    +GLNbBitsDepth [value: positive integer] +

    +Specifies the number of bits for the depth buffer. Default is 16.

    +X113DOffscreenMode [value: "Window" "VisibleWindow" "Pixmap"] +

    +Specifies the type of OpenGL offscreen rendering in X11. Default mode is "Pixmap".

    +
      +
    • "Window": A hidden window is used to perform offscreen rendering. Depending on your video driver and X11 configuration, this may not work.
    • +
    • "VisibleWindow": A visible window is used to perform offscreen rendering. This can be usefull while debugging.
    • +
    • "Pixmap": An X11 Pixmap is used to perform offscreen rendering. Depending on your video driver and X11 configuration, this may not work and can even crash the player.
    • +
    + + +

    + +Section "Network" Back to top +

    The "Network" section of the config file holds all configuration options for the network used by modules and systems engine.

    +AutoReconfigUDP [value: "yes" "no"] +

    +Specifies if network manager shall reconnect a scene if UDP is not present.

    +DataTimeout [value: positive integer] +

    +Specifies timeout in ms befor initial media buffering aborts. Default terminal value is 20000 (20 seconds).

    +UDPNotAvailable [value: "yes" "no"] +

    +Specifies if UDP traffic is not available. This is automatically set by GPAC if AutoReconfigUDP is set.

    +UDPTimeout [value: positive integer] +

    +Specifies timeout in ms for initial UDP detection. Once a UDP packet is recieved the timeout is ignored.

    +BufferLength [value: positive integer] +

    +Specifies the length of the decoding buffer in milliseconds. The client will wait for the buffer to be filled before starting decoding. A module may decide to use a different value based on protocol and network jitters.

    +RebufferLength [value: positive integer] +

    +Specifies rebuffering length of the decoding buffer in milliseconds. Whenever the decoding buffer fullness is less than this value, the object clock is paused and +the stream rebuffered till BufferLength. Therefore a value of 0 means no rebuffering. A module may decide to use a different value based on protocol and network jitters. +

    +MobileIP [value: IP Address] +

    +Specifies a Mobile IP interface overriding the default IP. If set, all sockets will be locally bound to this IP address.

    +

    +DefaultMCastInterface [value: IP Address] +

    +Specifies a default IP interface for Multicast overriding the default IP.If not set, the multicast will be setup using the default IP.

    +

    + + +

    + +Section "FontEngine" Back to top +

    The "FontEngine" section of the config file holds all configuration options for the font handling. The GPAC rendering module handles text through vectorial outline, allowing graphics module development without having to +integrate text rendering which is always heavy work.

    +FontReader [value: string] +

    +Specifies the module to use for font handling. This module cannot be reloaded at run-time, GPAC must be restarted.

    +FontDirectory (value: path to TrueType (*.ttf, *.ttc) font directory] +

    +Specifies the directory where fonts are located - currently only one directory can be specified (however nothing stops a font module from using a private directory). +
    Note: +The freetype module will scan the entire sub-directories for fonts. +

    +FontSerif [value: string]}: specifies default SERIF font.
    +FontSans [value: string]}: specifies default SANS font.
    +FontFixed [value: string]}: specifies default fixed font.
    +
    +Note:
    +The FreeType module uses this section to cache familly names to font file name associations. + +

    + +Section "Downloader" Back to top +

    The "Downloader" section of the config file holds all configuration options for file downloading and caching.

    +CleanCache [value: "yes" "no"] +

    +Specifies whether downloaded files shall be removed once used.

    +RestartFiles [value: "yes" "no"] +

    +Specifies whether incompletely downloaded files shall be removed before redownloading.

    +MaxRate [value: positive integer] +

    +Specifies a maximum data rate in kilo bits per seconds for file downloading. This is used for simulation purposes. A value of 0 means no rate restriction.

    +UserAgent [value: string] +

    +Specifies an alternate user agent (default one is "GPAC $VERSION").

    + +

    + +Section "HTTPProxy" Back to top +

    The "HTTPProxy" section of the config file holds configuration option for HTTP proxy adressing. Currently only one proxy can be enabled, and no URI selection is done.

    +Enabled [value: "yes" "no"] +

    +Specifies whether the proxy should be used or not when downloading files.

    +Name [value: string] +

    +Specifies the proxy name (IP address or resolved name) without protocol identifier (eg, no "http://"). If not present, the proxy is disabled.

    +Port [value: positive integer] +

    +Specifies the port to use with the proxy. If no port is specified, the default HTTP port (80) is used.

    + +

    + +Section "Streaming" Back to top +

    The "Streaming" section of the config file holds all configuration options for real-time streaming using IETF SDP/RTSP/RTP/RTCP protocols.

    +DefaultPort [value: unsigned short] +

    +Specifies the default port to use when connecting to a server (ignored if a port is specified in the url) if the port is 80 or 8080 (HTTP), the client will connect to the +RTSP server through an HTTP tunnel, and transport will take place on the RTSP connection.

    +ReorderSize [value: positive integer] +

    +Size of the RTP reordering buffer - 0 means no reordering. Ignored when transport takes place on the RTSP connection. The bigger this value, the longer the reordering delay will be.

    +RTPoverRTSP [value: "yes" "no" "OnlyCritical"] +

    +Specifies whether RTP packets should be carried on the RTSP connection (TCP or UDP), or carried on UDP. If the connection port is an HTTP port, this value is assumed to be true. If set to OnlyCritical, transport will take place on TCP only if a critical media (eg, neither audio nor video) is found in the session.

    +RTSPTimeout [value: positive integer] +

    +Specifies connection timeout with the server: an RTSP request is considered as failed when the timeout expires.

    +ForceFirstPort [value: positive integer] +

    +Specifies first port for RTP channels. If not set, the default first port used by GPAC is 7040.

    +NATKeepAlive [value: positive integer] +

    +Specifies the maximum inactivity period in milliseconds for RTP sockets. If no data is received after this period, an empty RTP packet will be sent in order to keep any NAT alive. If 0 (default), disables NAT keep-alive packets.

    +ForceMulticastIP [value: IP4 or IP6 address] +

    +Forces the specified multicast address to be used instead of the regular unicast. Note that some servers may not support multicast initiation by the client.

    +ForceMulticastTTL [value: Positive integer] +

    +Indicates the TTL to use when the client initiates the multicast. Default value is 127.

    +FirstPacketDrop [value: positive integer] +

    +Specifies the sequence number of the first RTP packet to be droped - 0 means no packet drop. Used for packet drop simulation tests.

    +PacketDropFrequency [value: positive integer] +

    +Specifies the frequency at which SL packets are droped. If value is 20, one packet every 20 recieved packets will be droped. Used for packet drop simulation tests.

    + +

    + + +Section "MimeTypes" Back to top +

    This section is used to keep MIME types and file associations for GPAC modules. +
    The format of the key is: +
    mimeType="fileExt1 filexExt2 .. filexExtN" "MimeType description" ModuleName +
    +The description is used for GUI purposes (open file dialogs). You may modify the file extension list to support your own extensions. +MIME Type is always checked when processing a remote ressource (eg http file) in order to load the appropriated modules. +If MIME type is not available, provided extensions are first checked, then all input modules are queried. +

    + +

    + + +Section "StreamingCache" Back to top +

    The "StreamingCache" section of the config file holds all configuration options for the streaming cache. Streaming cache allows for recording of +live sources such as RTP/RTSP sessions and internet radios. This is currently just an experimental feature in GPAC.

    +RecordDirectory [value: path] +

    +Specifies path for recorded files. Cached data is written directly to disk (no re-interleaving or similar processes). +If not specified, the default cache directory (cf General) is used.

    +BaseFileName [value: string] +

    +Specifies the base name for recorded files. If not present in configuration file, the service name (URL) is used.

    +KeepExistingFiles [value: "yes" "no"] +

    +Specifies if cached files with same name should be kept or not. If not, an integer number is added to the cached file name, the higest number for the latest file.

    + +

    + + +Section "SAXLoader" Back to top +

    The "SAXLoader" section of the config file holds all configuration options for XML SAX parsing of SVG, XMT and X3D files.

    +Progressive [value: "yes" "no" "DOM"] +

    Specifies XML parsing mode used by different file loaders using the SAX parser.

    +
      +
    • "yes": SAX parsing is used with progressive loading of the document.
    • +
    • "no": SAX parsing is used, document will first be completely downloaded.
    • +
    • "DOM": DOM parsing is used, document will first be completely downloaded. Only supported by libXML2 plugin, otherwise handled as "no".
    • +
    +MaxDuration [value positive integer] +

    +Specifies the maximum amount of time the SAX parser should spent loading a portion of the document. Only used with SAX Progressive mode

    + +

    + + +Section "XviD" Back to top +

    The "XviD" section of the config file holds all configuration options for the XviD codec.

    +PostProc [value: "FilmEffect" "Deblock_Y" "Deblock_UV"] +

    +Specifies filters to apply when decoding video. The string is a list of filters separated with a space character.

    +
      +
    • "FilmEffect": xvid 1.0.0 filmEffect.
    • +
    • "Deblock_Y": Y plane deblocking filter.
    • +
    • "Deblock_UV": UV plane deblocking filter.
    • +
    +Threaded [value "yes" "no"] +

    Specifies whether the decoder should run in its own thread or not.

    + +

    + + +Section "FFMPEG" Back to top +

    The "FFMPEG" section of the config file holds all configuration options for the FFMPEG demuxer and decoder.

    +DataBufferMS [value: positive integer] +

    +Specifies the amount of video/audio data (in milliseconds) to be bufferer before starting decoding. For developpers only.

    +IOBufferSize [value: positive integer] +

    +Specifies the size (in bytes) of the buffer used to fecth data from network (http playback only). Default size is 8192 bytes.

    + +

    + + +Section "ISOReader" Back to top +

    The "ISOReader" section of the config file holds all configuration options for the ISO Media File demuxer.

    +IgnoreMPEG-4ForBrands [value: Full 4CC or 4CC pattern (abc* ab*)] +

    +Ignores all MPEG-4 systems tracks and IOD for files showing the listed brands in their compatible brand list.

    + +

    + + +Section "DVB" Back to top +

    The "DVB" section of the config file holds all configuration options for DVB playback on GNU/Linux systems.

    +ChannelsFile [value: Absolute file path] +

    +Specifies the DVB channels configuration file as produced by dvbtools' scan util.

    + +

    + + +Section "ALSA" Back to top +

    The "ALSA" section of the config file holds all configuration options of the ALSA audio output module on GNU/Linux systems.

    +DeviceName [value: string] +

    +Specifies the ALSA device to use. Default device is "hw:0,0".

    + +

    + + +Section "StreamingText" Back to top +

    The "StreamingText" section of the config file holds all configuration options for the 3GPP/MPEG-4 Streaming Text decoder.

    +UseTexturing [value: "yes" "no"] +

    +Specifies whether the text shall be drawn using an intermediate texture or not.

    + + +

    + + + diff --git a/doc/doxyfile b/doc/doxyfile new file mode 100644 index 0000000..0dc2d7b --- /dev/null +++ b/doc/doxyfile @@ -0,0 +1,258 @@ +# Doxyfile 1.4.3 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = libgpac +PROJECT_NUMBER = 0.4.5 +OUTPUT_DIRECTORY = . +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = YES +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = NO +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = ../include +STRIP_FROM_INC_PATH = ../include +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = NO +DISTRIBUTE_GROUP_DOC = NO +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = NO +SORT_MEMBER_DOCS = NO +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ../include/gpac \ + ../include/gpac/internal +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +USE_HTAGS = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/doc/gpac.mp4 b/doc/gpac.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..ac25dd75d0dcb6b4f404cf2b2c28a7ee50f93b88 GIT binary patch literal 2688 zcmZuz3s6&M7XEK8#AvX~QwhW+3gwX$7LyM zE^d9G$U=x6X(KB%;|#A+B4r0HwDNL9XB}IaDU0IZxZSlf%kGw4XU~6gvu2$=Gw1&2 zeCPbCck+4SFOLCaqBorog0qdCBA=LgeCGqjX}nL0f6|Po=b!$#3JaK$R;O z6shtHQl)~t!tHwmJJeO$N*$g5z+|k_LrzGQ$=3kv1g#I_Yt1?w!-8pt)S4>=jP0Q; z6_sEYP!@_l3kJ`Gv!eHl@so7G25dy~;1Lu?tVOGqv5zH~B}iuzc$=dsizRRxw8QH| zd?>HMsUVm_phiOM2p#d2#4*4$)jka8vVMuss^=!H+AflyDE+J-ow_y~j2W8(7n zd0-VKE6;VzK4 z(-Y-oOVGvHsi|)sJDmd@#8hr~gJOKErOMs|KUH8u5t z)9I1{Ov0?Safy2Uc}8sj`bBWovrmB#CldH?0$t+^up?ki^#X7>du=HAyFYRco*(gr z=x4_*xWM7(Z3xAIsxbC%iA4Glw8dm0`a=Ho!bBej*sllJ4!L1JLg60)gf$wSG&a4% z#5uKdD9m>2C*k!q@9s1wm##72{8hKa)WU@6A?blcQZVjxUIhKTF0Tfmi%;RCu+G66 zAcy@k8$4kTk6gGp6B7n^1-6aY1AD^-#diarApsyQ!@Ko;xP)JD5d9ODh;@gW8v&EB zkjsN5D}PS@+gD3v$t$2sU{3;ur{cm~#>X*QnfytJVOm`9={a~V&cU^q`S{>S#>X*Q znf$Mq{v5f?A%i-MII^9tkC!A;>^c(l2W*e@^b6-${p2@xjSshftv>)t?qEM z+ygL&#-T5s*&=)FA@u69IjR06s(jPh1wl!3I1H%M;mv z^U){y_vLQ4ede%rYaN$IxvZY%SdqlqyfuZQM4hD@a?M6{Wml=toX{}>m%CIX^$uJO zjqehC)|8NWxvwcXy}z%kd>|($Ys321{=xL#*6b@+y_@^`Grf|&)=cqLwR|1?-@!G_ zxsx-aZ~eYKHFmT%C{oO-7oQ>HNs@IZo(y&bT^OxkRRq1ckdeo_9rX7tcbD?mtzvfg zs)EFI+xOmGpV-GFwv*vmU-I_wz@-iP**&l$6#zq%2YeMWrX;pI@C?%zIE4dowon*3Y z`Y*58B0MhPio~O-Sm_rvyOh=%qOp~l+?bs?>78zA7(da$Ds10~P#0e@zj(-C)EMN3YNJMnguMBr^~F(8 z*U?e&>g=J_mQ6A-H->)89kXvfp}B!1b+le - GPAC (c) 2000-2005 +. +.SH "SEE ALSO" +.LP +MP4Client(1), MP4Box(1) diff --git a/doc/man/mp42avi.1 b/doc/man/mp42avi.1 new file mode 100644 index 0000000..5f4adca --- /dev/null +++ b/doc/man/mp42avi.1 @@ -0,0 +1,69 @@ +.TH "MP42AVI" 1 "March 2005" "MP42AVI" "GPAC" +. +.SH NAME +.LP +MP42AVI \- MPEG-4 BIFS to Video converter +.SH "SYNOPSIS" +.LP +.B MP42AVI +.RI [options] \ file\ +.br +. +.SH "DESCRIPTION" +.LP +MP42AVI is a tool to convert a pure MPEG-4 BIFS presentation (no audio, no image, no video) to an uncompressed sequence of images or an uncompressed RGB avi file. +. +. +.SH OPTIONS +.P +.TP +.B \-fps Framerate +specifies extraction framerate. If not set the framerate is computed from the BIFS track duration. +.TP +.B \-size WxH +forces BIFS scene size to the given resolution +.TP +.B \-raw [frame] +uses raw format for output. If frame is specified, only the given frame is dumped. Otherwise, all samples are dumped into raw files. +.TP +.B \-bmp [frame] +uses BMP format for output. If frame is specified, only the given frame is dumped. Otherwise, all samples are dumped into BMP files. +.TP +.B \-outpath path +specifies output directory to dump frames/movie. +.TP +.B \-cfg +specifies path to GPAC config file. +.TP +.B \-v +prints build version. +.TP +.B \-h +prints this message +. +.SH SPECIAL CONSIDERATIONS +.TP +.B Frame Dumping +When dumping a frame, either the frame number (sample number) can be specified or the frame time +in the format hh:mm:ss:xFz where hh, mm, ss are hours, minutes, seconds, x the number +of the frame in the seconds and z the frame rate used to express the time +.TP +.br +.B Input Files +The current version of MP42AVI is only designed to handle pure BIFS presentation. If the input BIFS scene uses audio or visual media, these are ignored during the dump. It may even crash MP42AVI. +.TP +.B GPAC Config File +MP42AVI needs the GPAC configuration file in order to locate the different plugins used for rasterization. You must therefore have launched successfully MP4Client before trying to use MP42AVI +. +.SH "FILES" +.LP +.B GPAC Configuration File: +~/.gpacrc +. +.SH "AUTHORS" +.LP +Jean Le Feuvre - GPAC (c) 2000-2005 +. +.SH "SEE ALSO" +.LP +MP4Client(1), MP4Box(1), GPAC(1) diff --git a/doc/man/mp4box.1 b/doc/man/mp4box.1 new file mode 100644 index 0000000..b0647a8 --- /dev/null +++ b/doc/man/mp4box.1 @@ -0,0 +1,530 @@ +.TH "MP4Box" 1 "June 2005" "MP4Box" "GPAC" +. +.SH NAME +.LP +MP4Box \- MPEG-4 Systems Toolbox +.SH "SYNOPSIS" +.LP +.B MP4Box +.RI [options] \ file \ [options] +.br +. +.SH "DESCRIPTION" +.LP +MP4Box is a multi-purpose command line tool to create and edit MPEG-4 Systems presentations and manipulate ISO-media files (MP4, 3GP, MOV). +.LP +MP4Box supports file conversion from various raw formats and IsoMedia/AVI/MPEG-PS/OGG containers, file hinting for RTP streaming for QuickTime compatible streaming servers, file interleaving, file fragmentation and track extraction. +.LP +MP4Box also provides dump tools used to inspect file layout, RTP hint tracks, SDP information, scene composition. It may also be used to convert to and from BT/XMT-A/VRML/X3D. +.LP +MP4Box also features MPEG-4 Systems encoders and decoders for BIFS and OD tools. +.LP +MP4Box doesn't expect any particular order in options at prompt. +. +.SH GETTING HELP WITH MP4Box +.TP +.B \-h [type] +prints help screen. "type" can be one of "general", "hint", "import", "encode", "extract", "dump", "swf", "meta", "crypt", "format" for specific help. +.TP +.B \-version +prints version of MP4Box. +.TP +.B \-nodes +lists supported MPEG-4 Systems nodes in MP4Box current build. +.TP +.B \-node NodeName +prints the node syntax. Each field is listed as +.br +.I eventType fieldType fieldName defaultValue +.br +and field quantization information and quantization bounds if any. +. +. +.SH GENERAL OPTIONS +.P +.TP +.B \-inter duration +interleaves media data in chunks of the maximum specified duration (expressed in milliseconds) and prepare file for HTTP/FTP streaming. By default MP4Box always stores with 0.5 second interleaving. An interleaving value of '0' stores without interleaving (meta-data then track after track). +.br +.B NOTE: Track Edit Lists are ignored when performing interleaving. +.TP +.B \-flat +stores file with all media data first, non interleaved. If used when creating a new file, no temporary file is created (faster storage). +.TP +.B \-frag duration +fragments file using ISO-Media movie fragments. Tracks will be fragmented so that no track run exceeds the specified duration (expressed in milliseconds). Disables interleaving. +.TP +.B -out\ \ +specifies output file name. +.TP +.B -tmp\ \ +specifies driectory for temporary storage. If not set, temporary storage is OS-dependent. +.TP +.B \-nosys +removes all MPEG-4 Systems information from the file but keeps an empty IOD for MPEG-4 Profile@Level indications. +.TP +.B \-isma +rewrites the file as an ISMA 1.0 Audio/Video file (all systems info rewritten) with proper clock references. +.TP +.B \-ismax +rewrites the file as an ISMA 1.0 Audio/Video file (all systems info rewritten) with ISMA clock references (streams not explicetly synchronized). +.br +.B NOTE: Seeking may be broken with some players (in ISMA, audio and video streams do not depend on main scene clock). +.TP +.B \-3gp +rewrites the file as a 3GP file (no more MPEG-4 Systems specific info). This option is turned on by default when input file extension is .3gp or .3g2. +.TP +.B -cprt string +adds string copyright notice to file. +.TP +.B \-chap chap_file +adds chapter information contained in chap_file to movie. For more details on chapter file syntax, cf http://gpac.sourceforge.net/auth_mp4box.php. +.TP +.B -rem trackID +removes given track from file - can be specified multiple times. +.TP +.B -new +forces creation of a new destination file. +.TP +.B -lang [tkID=]LAN +sets the language of all tracks or the given track. LAN is the ISO 639-2 3 character code. +.TP +.B -delay [tkID=]delay_ms +sets the delay in milliseconds of all tracks or the given track. LAN is the ISO 639-2 3 character code. +.TP +.B \-split time_in_seconds +splits in files of desired maximum duration. This will remove all MPEG-4 Systems media. +.TP +.B \-splits size_in_kilobytes +splits in files of desired maximum size. This will remove all MPEG-4 Systems media. +.TP +.B \-splitx start:end +extracts a new file from specified start to end times (in seconds). This will remove all MPEG-4 Systems media. +.B When input file is an ISO-Media file (QT, MP4, 3GP), if no output is specified THE INPUT FILE IS OVERWRITTEN. +. +. +.SH HINTING OPTIONS +.TP +.B \-hint +hint the file for RTP\/RTSP sessions. Payload type is automatically detected and configured unless forced through one of MPEG-4 Generic RTP payload. +.TP +.B \-mtu size +specifies Maximum Transmission Unit size in bytes (eg maximum RTP packet size). Default size is 1500 bytes (Ethernet MTU). This must be choosen carefully: specifying too large packets will result in undesired packet fragmentation at UDP layer while specifying too small packets will result in RTP header overhead. +.TP +.B \-copy +forces hinted data to be copied to the hint track instead of simply referenced. This speeds up RTP packet construction at the server side but results in much bigger files. +.TP +.B \-tight +performs sample-based interleaving of media tracks and hint tracks. This should reduce disk seeks at server side (depending on server implementation) but results in a bigger file. +.TP +.B \-multi [maxptime] +enables Access Units concatenation in RTP packets if possible. maxptime is optional and specifies the maximum packet duration in milliseconds (default 100). +.TP +.B \-rate ck_rate +specifies the rtp rate in Hz when no default rate for payload. Default value is 90000 (MPEG rtp rates). +.TP +.B \-mpeg4 +forces usage of the MPEG-4 generic payload whenever possible. Media tracks without a mapping to MPEG-4 Systems cannot use this. +.TP +.B \-latm +forces usage of the LATM payload for AAC audio. +.TP +.B \-static +enables usage of static RTP payload IDs for streams with official payload IDs. By default MP4Box always uses dynamic payload IDs for maximum interoperability, some players having troubles with static ones. +. +.P +.B MPEG-4 Generic Payload Options (Experts only) +.IP +.B \-ocr +forces all media tracks in the file to be served synchronized. This is needed because most streaming servers don't support desynchronized tracks in a single file. Be extremelly carefull when designing MPEG-4 interactive presentations for streaming since you will have to take care of the streaming server capabilities... MP4Box generates warnings when the file timeline can be ambiguously interpreted by the server. +.IP +.B \-rap +signals Access Units random access flag in RTP packets. This is usually only needed for streaming of MPEG-4 Systems streams. +.IP +.B \-ts +signals Access Units Time Stamps (CTS and DTS) in RTP packets. +.IP +.B \-size +signals Access Units size in RTP packets. +.IP +.B \-idx +signals Access Units indexes (sequence numbers) in RTP packets. +.IP +.B \-multi +enables Access Units concatenation in RTP packets (-ts, -size and -idx are selected if needed). +.IP +.B \-iod +prevents system tracks embedding in IOD (ISMA-like IOD) when generating in SDP. MP4Box automatically detects ambiguous (ISMA/non-ISMA) files but nobody's perfect. This shouldn't be used with -isma option. +. +.TP +.B \-sdp_ex string +adds string to movie SDP or track SDP (tkID:string, where tkID is the OD of the hint track or its media track). This takes care of SDP line reordering, but not of SDP content validity. +.TP +.B \-unhint +removes all hint tracks and other hinting info from the file. +. +.SH IMPORT OPTIONS +.TP +.B \-add +adds all src_file tracks to input file, creating it if not exisiting. Up to 20 cumulated -add operations can be used. Supported syntaxes are: +.br +file#video: imports first video track from src_file. +.br +file#audio: imports first audio track from src_file. +.br +file#trackID=ID or file#ID: imports given trackfrom src_file. To get a listing of tracks in input file, use -info [ID] +.br +[;lang=LAN]: specifies language of imported media. +.br +[;delay=delay_ms]: specifies initial delay in milliseconds of imported media. +.TP +.B \-cat +concatenates all src_file tracks to input file, creating it if not exisiting. Media samples are added at the end of existing compatible tracks. If no compatible track is found for a media it is created. Up to 20 cumulated -cat operations can be used. Syntax is the same as -add. +.TP +.B \-keepsys +by default all MPEG-4 systems media are removed with -add and -cat. This option will avoid removing them from final file. +.TP +. +.TP +.B \-dref +keeps media data in original file an only imports meta-data (frame timing, size and random access). +.br +.TP +.B NOTE +Data referencing may fail with some AVI because it requires the framed data (eg an MP4 sample) to be continuous in the original file, which is not always the case depending on the original interleaving. +.TP +.B \-nodrop +forces constant FPS when importing AVI video. By default non coded frames (n-vop) are removed at import time, resulting in a variable frame-rate media. +.TP +.B \-packed +for CMP/M4V (raw MPEG-4 Visual), forces packed-bitstream mode (removes all n-vops and import at constant frame rate). +.TP +.B \-sbr +imports AAC as AAC-SBR, with backward compatible signaling (non AAC-SBR decoders should be able to play it). +.TP +.B \-sbrx +imports AAC as AAC-SBR, with non-backward compatible signaling (non AAC-SBR decoders will not be able to play it). +.TP +.B \-fps FrameRate +overrides the input video frame rate or specifies it for SUB subtitles. +.TP +.B \-mpeg4 +forces using MPEG-4 sample descriptions rather than 3GP ones (3GP2 QCELP/EVRC/SMV audio only). +.TP +.B \-agg N +aggregates N audio frames in 1 sample (3GP media only). Maximum possible value is 15, and default value is 1 (no aggregation). +. +.SH ENCODING OPTIONS +.TP +.B \-mp4 +specifies input file is for encoding. Supported inputs are BT/XMT-A/WRL/SWF files. Output file name is by default the input file name without extensions plus ".mp4" extension. +.TP +.B \-def +encodes DEF'ed nodes and routes with their textual names. +.TP +.B \-log +generates BIFS encoder log file. +.TP +.B \-ms file +specifies file for track importing - by default FILE.mp4 is used when encoding FILE.bt (in-place rewrite). This option is only needed if you don't provide +a proper muxInfo per stream in the BT/XMT file. +.TP +.B \-sync time +forces BIFS random access point generation every time milliseconds. Cannot be used with -shadow. WARNING: this may result in weird behavior of your presentation since a BIFS random access point restarts all media currently running in the scene (EXPERTS ONLY - DANGEROUS). Cannot be used with -shadow. +.TP +.B \-shadow time +forces BIFS shadow random access points generation every time milliseconds. Shadow samples are random access points that can be used instead of non random access points when seeking. WARNING: this may be not supported by some players (EXPERTS ONLY - DANGEROUS). Cannot be used with -sync. +.TP +.B \-inctx file +specifies initial context (MP4/BT/XMTA) for chunk processing. Input file must then be a command-only file: no IOD, and no implicit commands (commands without 'AT'). +.TP +.B \-outctx file +specifies output file of updated context (MP4/BT/XMTA) in chunk processing mode. This is optional, chunk processing doesn't need to store the final context. +. +.SH EXTRACTING OPTIONS +.TP +.B \-raw TrackID +extracts given track in native format when supported. +.TP +.B \-raws TrackID +extract each track sample to a file. Note: 'TrackID:N' extracts Nth sample of the track. +.TP +.B \-nhnt TrackID +extracts given track in NHNT format. All track types except ObjectDescriptors tracks can be exported. +.TP +.B \-single TrackID +extracts given track to a new mp4 file with a single track. +.TP +.B \-avi TrackID +extracts visual track to an avi file. +.TP +.B \-qcp TrackID +same as '-raw' but defaults to QCP file format for AVRC and SMV audio codecs. +.TP +.B \-aviraw TK +extracts AVI track to its raw format. TK can be one of "video", "audio" or "audioN" for multi-track avi files (cf '-info'). +. +.SH DUMP OPTIONS +.TP +.B \-info [TrackID] +prints movie and tracks information. If TrackID specified, dumps only extended track info. If input file is not an IsoMedia file, lists known tracks for import. +.TP +.B \-bt +dumps complete scene in a BT file. This will remove unknown MPEG4 nodes. +.TP +.B \-xmt +dumps complete scene in an XMT-A file. This will remove unknown MPEG4 nodes. +.TP +.B \-wrl +dumps complete scene in an VRML97 WRL file. This will remove unknown VRML97 nodes. +.TP +.B \-x3d +dumps complete scene in an X3D XML file. This will remove unknown X3D nodes. +.TP +.B \-x3dv +dumps complete scene in an X3D Text (VRML) file. This will remove unknown X3D nodes. +.TP +.B \-diso +creates XML image of the file atoms. +.TP +.B \-drtp +creates XML image of all hint tracks samples of a hinted mp4 file. +.TP +.B \-dcr +creates XML image of all ISMACryp tracks samples of an mp4 file. +.TP +.B \-sdp +creates SDP file associated with a hinted mp4 file. +.TP +.B \-ttxt +converts input subtitle to GPAC TTXT format. +.TP +.B \-ttxt trackID +exports given text track to GPAC TTXT format. +.TP +.B \-srt +converts input subtitle to SRT format. +.TP +.B \-srt trackID +exports given text track to SRT subtitle format. +.TP +.B \-std +dumps to stdout rather than file. +.TP +.B \-stat +generates statitistic report on node/field usage for the whole presentation. +.TP +.B \-stats +generates statistic report on node/field usage per BIFS Access Unit. +.TP +.B \-statx +generates statistic report on node/field usage in the scene graph after each BIFS Access Unit. +. +.SH Meta OPTIONS +. +.LP +MP4Box supports the Meta addition to IsoMedia file format, used to make the file a generic data container (timed or untimed). +.TP +.B \-set-meta args +sets meta object type. Syntax is "ABCD[;tk=N] where: +.br +ABCD is the four character code of the type, or O/NULL to remove the meta object +.br +tk indicates whether the meta is at the root level (no "tk"), at the moov level (tk=0) or at the track level (tk=trackID). +.TP +.B \-add-item args +adds resource to the given meta. args is the item file path followed by ';' separated options: +.br +tk=N: same as above +.br +name=item_name: specifies the item name, otherwise file name is used +.br +mime=mimeType: specifies the item mime type, otherwise application/octet-stream is used +.br +encoding=enctype: specifies the content encoding type +Note that file path set to "this" or "self" will identify the item added as the whole IsoMedia file +.TP +.B \-rem-item args +removes item from given meta. Syntax is itemID[;tk=ID]. +.TP +.B \-set-primary args +sets given item as the primary one of the given meta. Syntax is itemID[;tk=ID]. +.TP +.B \-set-xml args +sets XML to the given meta. Syntax is xml_file_path[;tk=ID][;binary]. +.TP +.B \-rem-xml [tk=ID] +removes XML data from the given meta. +.TP +.B \-dump-xml xml_out_file_name[;tk=ID] +dumps the XML data of the given meta to a file. +.TP +.B \-dump-item itemID[;tk=ID][;path=fileName] +dumps the given item of the the given meta to a file. By default the item name is used as the output file name. +. +.SH SWF OPTIONS +. +.LP +MP4Box can import very simple Macromedia Flash files (".SWF"). You can specify a SWF input file with -bt, xmt and -mp4 switches. +.TP +.B \-static +all SWF defines are placed in first scene replace. By default SWF defines are sent when needed. +.TP +.B \-ctrl +uses a dedicated stream for movie control (forces -static option). +.TP +.B \-notext +removes all SWF text. +.TP +.B \-nofont +removes all embedded SWF Fonts, forcing usage of MPEG-4 Text and terminal fonts. +.TP +.B \-noline +removes all lines from SWF shapes. +.TP +.B \-nograd +removes all gradients from swf shapes. +.TP +.B \-quad +uses quadratic bezier curves instead of cubic ones. +.TP +.B \-xlp +support for lines transparency and scalability. +.TP +.B \-flatten Value +replaces 2 consecutive lines by a single one when angle between lines is less than Value (expressed in radians). Value 0 disables flattening. +. +. +.SH SUPPORTED INPUT FORMATS +.TP +.B RAW Formats and extensions +.IP +NHNT (.media .nhnt .info) +.br +MPEG Audio (.mp3) +.br +ADTS-AAC (.aac) +.br +MPEG-4 Visual (.cmp .m4v) +.br +H263 Video (.263 .h263) +.br +AVC/H264 Video (.h264 .h26L .264 .26L) +.br +JPEG Images (.jpg .jpeg) +.br +PNG Images (.png) +.br +AMR(WB) Audio (.amr .awb) +.br +EVRC Audio (.evc) +.br +SMV Audio (.smv) +. +.TP +.B Container Formats and extensions +.IP +AVI (.avi) +.br +MPEG-PS (.mpg .mpeg .vob .vcd .svcd) +.br +QCP (.qcp) +.br +OGG (.ogg) +.br +ISO-Media files (no extension checking) +. +.TP +.B Text Formats and extensions +.IP +.br +SRT Subtitles (.srt) +.br +SUB Subtitles (.sub) +.br +GPAC Timed Text (.ttxt) +.br +QuickTime TeXML Text (.xml) (cf QT documentation) +. +.TP +.B Scene Formats and extensions +.IP +MPEG-4 XMT-A (.xmt .xmta .xmt.gz .xmta.gz) +.br +.br +MPEG-4 BT (.bt .bt.gz) +.br +VRML (.wrl .wrl.gz) +.br +X3D-XML (.x3d .x3d.gz) +.br +X3D-VRML (.x3dv .x3dv.gz) +.br +MacroMedia Flash (.swf) - very limitted import support only +. +. +.SH COMMON USAGE +.TP +.B Importing a file +.IP +Import an AVI: MP4Box -add myfile.avi mymp4.mp4 +.br +Import audio from an AVI: MP4Box -add myfile.avi#audio mymp4.mp4 +.br +Import video from an AVI: MP4Box -add myfile.avi#video mymp4.mp4 +.br +Import an MP3 and leave data outside: MP4Box -dref -add myfile.mp3 mymp4.mp4 +.br +Import a JPEG or PNG: MP4Box -add myfile.jpg mymp4.mp4 +.br +Create a movie with subtitle: MP4Box -add video.avi -add audio.mp3 -add Subtitle.srt mymp4.mp4 +.br +.TP +.B Base File operations +.IP +Convert file for HTTP/FTP streaming: MP4Box -inter 1000 myfile.mp4 +.br +Convert unknown file to ISMA for HTTP/FTP streaming: MP4Box -isma -inter 1000 myfile.mp4 +.br +Convert unknown file to ISMA and respect ISMA broken sync: MP4Box -ismax myfile.mp4 +.br +Convert unknown file to 3GP : MP4Box -3gp myfile.mp4 +.br +.TP +.B Hinting a File +.IP +Prepare any mp4 for ISMA streaming: MP4Box -isma -hint myfile.mp4 +.br +Prepare any 3GP for streaming: MP4Box -hint myfile.3gp +.br +Prepare any 3GP for streaming with RTP aggregation : MP4Box -hint -multi myfile.3gp +.br +Prepare a simple audio/video mp4 optimized for server: MP4Box -hint -copy -tight myfile.mp4 +.br +Prepare a complex mp4 with BIFS for streaming: MP4Box -ocr -iod -hint myfile.mp4 +.br +.TP +.B Encoding/Decoding a file +.IP +Encode a BT file and keep node names for later inspection: MP4Box -mp4 -def pres.bt +.br +Encode a XMT-A file: MP4Box -mp4 pres.xmt +.br +Translate BT to XMT-A file: MP4Box -xmt pres.bt +.br +Translate XMT-A to BT file: MP4Box -bt pres.xmt +.br +Decode MP4 to BT: MP4Box -bt pres.mp4 +.br +. +.SH BUGS and OTHER HELP +.TP +For bug reports, more information on BT or XMT-A formats or GPAC TTXT files or ISMACryp, MPEG-4 Systems usage and more help on MP4Box please visit the GPAC web site http://gpac.sourceforge.net +. +.SH "AUTHORS" +.LP +Jean Le Feuvre - GPAC (c) 2000-2005 +. +.SH "SEE ALSO" +.LP +GPAC(1), MP4Client(1) diff --git a/doc/man/mp4client.1 b/doc/man/mp4client.1 new file mode 100644 index 0000000..14bbc04 --- /dev/null +++ b/doc/man/mp4client.1 @@ -0,0 +1,243 @@ +.TH "MP4Client" 1 "March 2005" "MP4Client" "GPAC" +. +.SH NAME +.LP +MP4Client \- GPAC MPEG-4 command-line Player +.SH "SYNOPSIS" +.LP +.B MP4Client +.RI [options] \ [file] +.br +. +.SH "DESCRIPTION" +.LP +MP4Client is GPAC command-line player. It supports all GPAC playback features (2D and 3D support, local playback, RTP streaming, HTTP faststart, many audio and video codecs ...). MP4Client also supports visual extraction to BMP, RAW or AVI (no compression, no audio). +. +. +.SH STARTUP OPTIONS +.P +.TP +.B \-c file +specifies config file to use. Default config file is located in user home directory and created if not found. +.TP +.B \-rti file +logs run-time information to file. Information logged is: FPS, CPU, Memory usage. +.TP +.B \-quiet +removes script message, buffering and downloading status. +. +.SH PLAYBACK OPTIONS +A file can be controled during playback by typing one of the following key at prompt. +.TP +.B o +connects to a new URL entered at prompt by user. +.TP +.B r +restarts the current presentation +.TP +.B p +plays/pauses the current presentation +.TP +.B s +step one frame ahead in the current presentation +.TP +.B z +seek into presentation. Seek percentage is entered at prompt +.TP +.B t +prints current timing of the main timeline of the presentation +.TP +.B w +prints world info of the current presentation +.TP +.B v +prints list of all GF_ObjectDescriptor used in current presentation. +.TP +.B i +prints information of a given OD. Object ID is entered at prompt. +.TP +.B b +prints all objects timing and buffering info. +.TP +.B m +prints all objects buffering and memory info. +.TP +.B d +dumps current presentation scene graph to file or stdout. +.TP +.B k +Turns stress mode on/off. +.TP +.B n +Changes current navigation mode if possible/allowed. When a Layer3D is selected, sets layer3D navigation if allowed. +.TP +.B x +Reset to last active viewpoint. When a layer3D is selected, resets layer3D viewpoint. +.TP +.B 2 +Reload MPEG-4 Client with GPAC 2D Renderer. +.TP +.B 3 +Reload MPEG-4 Client with GPAC 3D Renderer. +.TP +.B 4 +forces 4/3 Aspect Ratio. +.TP +.B 5 +forces 16/9 Aspect Ratio. +.TP +.B 6 +forces no aspect ratio (always fill screen). +.TP +.B 7 +forces original Aspect Ratio (default mode). +.TP +.B l +prints the list of available plugins. +.TP +.B c +prints some GPAC configuration info. +.TP +.B q +quits the application. +.TP +.B C +enables streaming cache. +.TP +.B S +stops streaming cache and record it. +.TP +.B A +stops streaming cache and discard it. +.TP +.B h +prints list of available commands. +. +.SH WINDOW SHORTCUTS +When MP4Client window has the focus, the following shortcuts are available +.TP +.B Alt + right +Seeks 5% ahead from current time. +.TP +.B Alt + left +Seeks 5% before the current time +.TP +.B Alt + up +Increases volume by 5% +.TP +.B Alt + down +Decreases volume by 5% +.TP +.B HOME +Resets navigation to last bound viewpoint/viewport +.TP +.B 4, 5, 6, 7 +Same as non-window values +.TP +.B ESCAPE , alt+return, doubleclick +Toggles fullscreen on/off. +.TP +.B ctrl + 'f' +Prints current rendering framerate. +.TP +.B ctrl + 'R' +Sends a redraw message (complete scene state recompute). +.TP +.B ctrl + 'P' +Play/Pause. +.TP +.B ctrl + 'S' +Step one frame ahead. +. +.SH CONFIGURATION FILE +When launched for the first time, MP4Client will ask for a font directory and a cache directory. +.TP +.B Font Directory +Indicates the absolute path to a directory containing a TrueType font repository. In case this directory is not specified or does not contain valid TrueType fonts, text will be disabled. +.TP +.B Cache Directory +Indicates the absolute path to a directory with write access to store files retrieved through HTTP downloads. In case this directory is not specified or has no write access HTTP downloads won't work. +. +.SH NAVIGATION +Navigation with MP4Client depends on the renderer type (2D or 3D) and also on content - some content may disable user selectable navigation. +.TP +.B Walk & Fly modes +Mouse: Horizontal move: Horizontal pan - Vertical move: Z-translate - Vertical move+CTRL or Wheel: Vertical pan - Right Click (Walk mode only): Jump +Keys: left/right: Horizontal pan - left/right+CTRL: Horizontal translate - up/down: Z-translate - up/down+CTRL: Vertical pan +.TP +.B Pan mode +Mouse: Horizontal move: Horizontal pan - Vertical move: Vertical pan - Vertical move+CTRL or Wheel: Z-translate +Keys: left/right: Horizontal pan - left/right+CTRL: Horizontal translate - up/down: Vertical pan - up/down+CTRL: Z-translate +.TP +.B Slide mode +Mouse: Horizontal move: Horizontal translate - Vertical move: Vertical translate - Vertical move+CTRL or Wheel: Z-translate +Keys: left/right: Horizontal translate - left/right+CTRL: Horizontal pan - up/down: Vertical translate - up/down+CTRL: Z-translate +.TP +.B Examine & Orbit mode +Mouse: Horizontal move: Y-Axis rotate - Horizontal move+CTRL: No move - Vertical move: X-Axis rotate - Vertical move+CTRL or Wheel: Z-translate +Keys: left/right: Y-Axis rotate - left/right+CTRL: Horizontal translate - up/down: X-Axis rotate - up/down+CTRL: Y-translate +.TP +.B Game mode (press END to escape) +Mouse: Horizontal move: H pan - Vertical move: Vertical pan +Keys: left/right: Horizontal translate - up/down: Z-translate +.TP +.B All 3D modes +CTRL+PGUP/PGDOWN will zoom in/out camera (field of view) +.TP +.B Slide Mode in 2D +Mouse: Horizontal move: Horizontal translate - Vertical move: Vertical translate - V move+CTRL: zoom +Keys: left/right: Horizontal translate - up/down: Vertical translate - up/down+CTRL: zoom +.TP +.B Examine Mode in 2D (3D renderer only) +Mouse: Horizontal move: Y-Axis rotate - Vertical move: X-Axis rotate +Keys: left/right: Y-Axis rotate - up/down: X-Axis rotate +.TP +.B SHIFT +.Speeds up movement +. +.SH VISUAL EXTRACTION OPTIONS +When used to dump a visual presentation, the client is no longer interactive. All GPAC features are supported during capture, except audio-related ones. The following options can be passed at prompt: +.P +.TP +.B \-bmp [times] +dumps the specified frames to BMP format. If no time is specified, capture is made at t=0s. time is given in millisecond, as an SMPTE time code or as HH:MM:SS:MS time code. You may specify several frame times by using a dash-separated list of times (eg, '0-10-15'). +.TP +.B \-raw [times] +dumps the specified frames to RAW format. If no time is specified, cpature is made at t=0s. +.TP +.B \-avi start:end +dumps the specified segment to uncompressed AVI format. +.TP +.B \-fps rate +specifies frame rate for AVI dumping. Default frame rate is 25.0. +.TP +.B \-size WxH +specifies frame size for dumping. Default frame size is the scene size. +.TP +.B \-fill +uses fill aspect ratio. By default, the scene aspect ratio is preserved when dumping. +.TP +.B \-show +show window while dumping the content. By default, the window is hidden during capture. +.TP +.B \-2d +forces usage of 2D renderer. By default, MP4Client uses the renderer indicated in GPAC configuration file. +.TP +.B \-3d +forces usage of 3D renderer. By default, MP4Client uses the renderer indicated in GPAC configuration file. +.TP +.B \-quiet +removes script message, buffering and downloading status. +. +.SH "FILES" +.LP +.B GPAC Configuration File: +~/.gpacrc +. +.SH "AUTHORS" +.LP +Jean Le Feuvre - GPAC (c) 2000-2005 - ENST 2005-200X +. +.SH "SEE ALSO" +.LP +GPAC(1), MP4Box(1) diff --git a/extra_lib/include/a52dec/a52.h b/extra_lib/include/a52dec/a52.h new file mode 100644 index 0000000..9db52cc --- /dev/null +++ b/extra_lib/include/a52dec/a52.h @@ -0,0 +1,62 @@ +/* + * a52.h + * Copyright (C) 2000-2002 Michel Lespinasse + * Copyright (C) 1999-2000 Aaron Holtzman + * + * This file is part of a52dec, a free ATSC A-52 stream decoder. + * See http://liba52.sourceforge.net/ for updates. + * + * a52dec is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * a52dec is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef A52_H +#define A52_H + +#ifndef LIBA52_DOUBLE +typedef float sample_t; +#else +typedef double sample_t; +#endif + +typedef struct a52_state_s a52_state_t; + +#define A52_CHANNEL 0 +#define A52_MONO 1 +#define A52_STEREO 2 +#define A52_3F 3 +#define A52_2F1R 4 +#define A52_3F1R 5 +#define A52_2F2R 6 +#define A52_3F2R 7 +#define A52_CHANNEL1 8 +#define A52_CHANNEL2 9 +#define A52_DOLBY 10 +#define A52_CHANNEL_MASK 15 + +#define A52_LFE 16 +#define A52_ADJUST_LEVEL 32 + +a52_state_t * a52_init (uint32_t mm_accel); +sample_t * a52_samples (a52_state_t * state); +int a52_syncinfo (uint8_t * buf, int * flags, + int * sample_rate, int * bit_rate); +int a52_frame (a52_state_t * state, uint8_t * buf, int * flags, + sample_t * level, sample_t bias); +void a52_dynrng (a52_state_t * state, + sample_t (* call) (sample_t, void *), void * data); +int a52_block (a52_state_t * state); +void a52_free (a52_state_t * state); + +#endif /* A52_H */ diff --git a/extra_lib/include/a52dec/mm_accel.h b/extra_lib/include/a52dec/mm_accel.h new file mode 100644 index 0000000..25258c3 --- /dev/null +++ b/extra_lib/include/a52dec/mm_accel.h @@ -0,0 +1,37 @@ +/* + * mm_accel.h + * Copyright (C) 2000-2002 Michel Lespinasse + * Copyright (C) 1999-2000 Aaron Holtzman + * + * This file is part of a52dec, a free ATSC A-52 stream decoder. + * See http://liba52.sourceforge.net/ for updates. + * + * a52dec is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * a52dec is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MM_ACCEL_H +#define MM_ACCEL_H + +/* generic accelerations */ +#define MM_ACCEL_DJBFFT 0x00000001 + +/* x86 accelerations */ +#define MM_ACCEL_X86_MMX 0x80000000 +#define MM_ACCEL_X86_3DNOW 0x40000000 +#define MM_ACCEL_X86_MMXEXT 0x20000000 + +uint32_t mm_accel (void); + +#endif /* MM_ACCEL_H */ diff --git a/extra_lib/include/faad/faad.h b/extra_lib/include/faad/faad.h new file mode 100644 index 0000000..afa5446 --- /dev/null +++ b/extra_lib/include/faad/faad.h @@ -0,0 +1,35 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: faad.h,v 1.2 2008/12/02 18:04:42 jeanlf Exp $ +**/ + +/* warn people for update */ +//#pragma message("please update faad2 include filename and function names!") + +/* Backwards compatible link */ +#include "neaacdec.h" diff --git a/extra_lib/include/faad/neaacdec.h b/extra_lib/include/faad/neaacdec.h new file mode 100644 index 0000000..52a1d73 --- /dev/null +++ b/extra_lib/include/faad/neaacdec.h @@ -0,0 +1,255 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: neaacdec.h,v 1.2 2008/12/02 18:04:42 jeanlf Exp $ +**/ + +#ifndef __NEAACDEC_H__ +#define __NEAACDEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#if 1 +/* MACROS FOR BACKWARDS COMPATIBILITY */ +/* structs */ +#define faacDecHandle NeAACDecHandle +#define faacDecConfiguration NeAACDecConfiguration +#define faacDecConfigurationPtr NeAACDecConfigurationPtr +#define faacDecFrameInfo NeAACDecFrameInfo +/* functions */ +#define faacDecGetErrorMessage NeAACDecGetErrorMessage +#define faacDecSetConfiguration NeAACDecSetConfiguration +#define faacDecGetCurrentConfiguration NeAACDecGetCurrentConfiguration +#define faacDecInit NeAACDecInit +#define faacDecInit2 NeAACDecInit2 +#define faacDecInitDRM NeAACDecInitDRM +#define faacDecPostSeekReset NeAACDecPostSeekReset +#define faacDecOpen NeAACDecOpen +#define faacDecClose NeAACDecClose +#define faacDecDecode NeAACDecDecode +#define AudioSpecificConfig NeAACDecAudioSpecificConfig +#endif + + +#ifdef _WIN32 + #pragma pack(push, 8) + #ifndef NEAACDECAPI + #define NEAACDECAPI __cdecl + #endif +#else + #ifndef NEAACDECAPI + #define NEAACDECAPI + #endif +#endif + +#define FAAD2_VERSION "2.6" + +/* object types for AAC */ +#define MAIN 1 +#define LC 2 +#define SSR 3 +#define LTP 4 +#define HE_AAC 5 +#define ER_LC 17 +#define ER_LTP 19 +#define LD 23 +#define DRM_ER_LC 27 /* special object type for DRM */ + +/* header types */ +#define RAW 0 +#define ADIF 1 +#define ADTS 2 + +/* SBR signalling */ +#define NO_SBR 0 +#define SBR_UPSAMPLED 1 +#define SBR_DOWNSAMPLED 2 +#define NO_SBR_UPSAMPLED 3 + +/* library output formats */ +#define FAAD_FMT_16BIT 1 +#define FAAD_FMT_24BIT 2 +#define FAAD_FMT_32BIT 3 +#define FAAD_FMT_FLOAT 4 +#define FAAD_FMT_FIXED FAAD_FMT_FLOAT +#define FAAD_FMT_DOUBLE 5 + +/* Capabilities */ +#define LC_DEC_CAP (1<<0) /* Can decode LC */ +#define MAIN_DEC_CAP (1<<1) /* Can decode MAIN */ +#define LTP_DEC_CAP (1<<2) /* Can decode LTP */ +#define LD_DEC_CAP (1<<3) /* Can decode LD */ +#define ERROR_RESILIENCE_CAP (1<<4) /* Can decode ER */ +#define FIXED_POINT_CAP (1<<5) /* Fixed point */ + +/* Channel definitions */ +#define FRONT_CHANNEL_CENTER (1) +#define FRONT_CHANNEL_LEFT (2) +#define FRONT_CHANNEL_RIGHT (3) +#define SIDE_CHANNEL_LEFT (4) +#define SIDE_CHANNEL_RIGHT (5) +#define BACK_CHANNEL_LEFT (6) +#define BACK_CHANNEL_RIGHT (7) +#define BACK_CHANNEL_CENTER (8) +#define LFE_CHANNEL (9) +#define UNKNOWN_CHANNEL (0) + +/* DRM channel definitions */ +#define DRMCH_MONO 1 +#define DRMCH_STEREO 2 +#define DRMCH_SBR_MONO 3 +#define DRMCH_SBR_STEREO 4 +#define DRMCH_SBR_PS_STEREO 5 + + +/* A decode call can eat up to FAAD_MIN_STREAMSIZE bytes per decoded channel, + so at least so much bytes per channel should be available in this stream */ +#define FAAD_MIN_STREAMSIZE 768 /* 6144 bits/channel */ + + +typedef void *NeAACDecHandle; + +typedef struct mp4AudioSpecificConfig +{ + /* Audio Specific Info */ + unsigned char objectTypeIndex; + unsigned char samplingFrequencyIndex; + unsigned long samplingFrequency; + unsigned char channelsConfiguration; + + /* GA Specific Info */ + unsigned char frameLengthFlag; + unsigned char dependsOnCoreCoder; + unsigned short coreCoderDelay; + unsigned char extensionFlag; + unsigned char aacSectionDataResilienceFlag; + unsigned char aacScalefactorDataResilienceFlag; + unsigned char aacSpectralDataResilienceFlag; + unsigned char epConfig; + + char sbr_present_flag; + char forceUpSampling; + char downSampledSBR; +} mp4AudioSpecificConfig; + +typedef struct NeAACDecConfiguration +{ + unsigned char defObjectType; + unsigned long defSampleRate; + unsigned char outputFormat; + unsigned char downMatrix; + unsigned char useOldADTSFormat; + unsigned char dontUpSampleImplicitSBR; +} NeAACDecConfiguration, *NeAACDecConfigurationPtr; + +typedef struct NeAACDecFrameInfo +{ + unsigned long bytesconsumed; + unsigned long samples; + unsigned char channels; + unsigned char error; + unsigned long samplerate; + + /* SBR: 0: off, 1: on; upsample, 2: on; downsampled, 3: off; upsampled */ + unsigned char sbr; + + /* MPEG-4 ObjectType */ + unsigned char object_type; + + /* AAC header type; MP4 will be signalled as RAW also */ + unsigned char header_type; + + /* multichannel configuration */ + unsigned char num_front_channels; + unsigned char num_side_channels; + unsigned char num_back_channels; + unsigned char num_lfe_channels; + unsigned char channel_position[64]; + + /* PS: 0: off, 1: on */ + unsigned char ps; +} NeAACDecFrameInfo; + +char* NEAACDECAPI NeAACDecGetErrorMessage(unsigned char errcode); + +unsigned long NEAACDECAPI NeAACDecGetCapabilities(void); + +NeAACDecHandle NEAACDECAPI NeAACDecOpen(void); + +NeAACDecConfigurationPtr NEAACDECAPI NeAACDecGetCurrentConfiguration(NeAACDecHandle hDecoder); + +unsigned char NEAACDECAPI NeAACDecSetConfiguration(NeAACDecHandle hDecoder, + NeAACDecConfigurationPtr config); + +/* Init the library based on info from the AAC file (ADTS/ADIF) */ +long NEAACDECAPI NeAACDecInit(NeAACDecHandle hDecoder, + unsigned char *buffer, + unsigned long buffer_size, + unsigned long *samplerate, + unsigned char *channels); + +/* Init the library using a DecoderSpecificInfo */ +char NEAACDECAPI NeAACDecInit2(NeAACDecHandle hDecoder, unsigned char *pBuffer, + unsigned long SizeOfDecoderSpecificInfo, + unsigned long *samplerate, unsigned char *channels); + +/* Init the library for DRM */ +char NEAACDECAPI NeAACDecInitDRM(NeAACDecHandle *hDecoder, unsigned long samplerate, + unsigned char channels); + +void NEAACDECAPI NeAACDecPostSeekReset(NeAACDecHandle hDecoder, long frame); + +void NEAACDECAPI NeAACDecClose(NeAACDecHandle hDecoder); + +void* NEAACDECAPI NeAACDecDecode(NeAACDecHandle hDecoder, + NeAACDecFrameInfo *hInfo, + unsigned char *buffer, + unsigned long buffer_size); + +void* NEAACDECAPI NeAACDecDecode2(NeAACDecHandle hDecoder, + NeAACDecFrameInfo *hInfo, + unsigned char *buffer, + unsigned long buffer_size, + void **sample_buffer, + unsigned long sample_buffer_size); + +char NEAACDECAPI NeAACDecAudioSpecificConfig(unsigned char *pBuffer, + unsigned long buffer_size, + mp4AudioSpecificConfig *mp4ASC); + +#ifdef _WIN32 + #pragma pack(pop) +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/extra_lib/include/ffmpeg/avcodec.h b/extra_lib/include/ffmpeg/avcodec.h new file mode 100644 index 0000000..e6c6bfb --- /dev/null +++ b/extra_lib/include/ffmpeg/avcodec.h @@ -0,0 +1,2714 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_H +#define AVCODEC_H + +/** + * @file avcodec.h + * external api header. + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "avutil.h" + +#ifndef _WIN32_WCE +#include /* size_t */ +#endif + +#define AV_STRINGIFY(s) AV_TOSTRING(s) +#define AV_TOSTRING(s) #s + +#define LIBAVCODEC_VERSION_INT ((51<<16)+(32<<8)+0) +#define LIBAVCODEC_VERSION 51.32.0 +#define LIBAVCODEC_BUILD LIBAVCODEC_VERSION_INT + +#define LIBAVCODEC_IDENT "Lavc" AV_STRINGIFY(LIBAVCODEC_VERSION) + +#define AV_NOPTS_VALUE INT64_C(0x8000000000000000) +#define AV_TIME_BASE 1000000 +#ifdef CONFIG_MSVC +static AVRational AV_TIME_BASE_Q_HACK() { AVRational r; r.num=1; r.den=AV_TIME_BASE; return r;} +#define AV_TIME_BASE_Q AV_TIME_BASE_Q_HACK() +#else +#define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE} +#endif + +enum CodecID { + CODEC_ID_NONE, + CODEC_ID_MPEG1VIDEO, + CODEC_ID_MPEG2VIDEO, /* prefered ID for MPEG Video 1 or 2 decoding */ + CODEC_ID_MPEG2VIDEO_XVMC, + CODEC_ID_H261, + CODEC_ID_H263, + CODEC_ID_RV10, + CODEC_ID_RV20, + CODEC_ID_MJPEG, + CODEC_ID_MJPEGB, + CODEC_ID_LJPEG, + CODEC_ID_SP5X, + CODEC_ID_JPEGLS, + CODEC_ID_MPEG4, + CODEC_ID_RAWVIDEO, + CODEC_ID_MSMPEG4V1, + CODEC_ID_MSMPEG4V2, + CODEC_ID_MSMPEG4V3, + CODEC_ID_WMV1, + CODEC_ID_WMV2, + CODEC_ID_H263P, + CODEC_ID_H263I, + CODEC_ID_FLV1, + CODEC_ID_SVQ1, + CODEC_ID_SVQ3, + CODEC_ID_DVVIDEO, + CODEC_ID_HUFFYUV, + CODEC_ID_CYUV, + CODEC_ID_H264, + CODEC_ID_INDEO3, + CODEC_ID_VP3, + CODEC_ID_THEORA, + CODEC_ID_ASV1, + CODEC_ID_ASV2, + CODEC_ID_FFV1, + CODEC_ID_4XM, + CODEC_ID_VCR1, + CODEC_ID_CLJR, + CODEC_ID_MDEC, + CODEC_ID_ROQ, + CODEC_ID_INTERPLAY_VIDEO, + CODEC_ID_XAN_WC3, + CODEC_ID_XAN_WC4, + CODEC_ID_RPZA, + CODEC_ID_CINEPAK, + CODEC_ID_WS_VQA, + CODEC_ID_MSRLE, + CODEC_ID_MSVIDEO1, + CODEC_ID_IDCIN, + CODEC_ID_8BPS, + CODEC_ID_SMC, + CODEC_ID_FLIC, + CODEC_ID_TRUEMOTION1, + CODEC_ID_VMDVIDEO, + CODEC_ID_MSZH, + CODEC_ID_ZLIB, + CODEC_ID_QTRLE, + CODEC_ID_SNOW, + CODEC_ID_TSCC, + CODEC_ID_ULTI, + CODEC_ID_QDRAW, + CODEC_ID_VIXL, + CODEC_ID_QPEG, + CODEC_ID_XVID, + CODEC_ID_PNG, + CODEC_ID_PPM, + CODEC_ID_PBM, + CODEC_ID_PGM, + CODEC_ID_PGMYUV, + CODEC_ID_PAM, + CODEC_ID_FFVHUFF, + CODEC_ID_RV30, + CODEC_ID_RV40, + CODEC_ID_VC1, + CODEC_ID_WMV3, + CODEC_ID_LOCO, + CODEC_ID_WNV1, + CODEC_ID_AASC, + CODEC_ID_INDEO2, + CODEC_ID_FRAPS, + CODEC_ID_TRUEMOTION2, + CODEC_ID_BMP, + CODEC_ID_CSCD, + CODEC_ID_MMVIDEO, + CODEC_ID_ZMBV, + CODEC_ID_AVS, + CODEC_ID_SMACKVIDEO, + CODEC_ID_NUV, + CODEC_ID_KMVC, + CODEC_ID_FLASHSV, + CODEC_ID_CAVS, + CODEC_ID_JPEG2000, + CODEC_ID_VMNC, + CODEC_ID_VP5, + CODEC_ID_VP6, + CODEC_ID_VP6F, + CODEC_ID_TARGA, + CODEC_ID_DSICINVIDEO, + CODEC_ID_TIERTEXSEQVIDEO, + CODEC_ID_TIFF, + CODEC_ID_GIF, + CODEC_ID_FFH264, + + /* various pcm "codecs" */ + CODEC_ID_PCM_S16LE= 0x10000, + CODEC_ID_PCM_S16BE, + CODEC_ID_PCM_U16LE, + CODEC_ID_PCM_U16BE, + CODEC_ID_PCM_S8, + CODEC_ID_PCM_U8, + CODEC_ID_PCM_MULAW, + CODEC_ID_PCM_ALAW, + CODEC_ID_PCM_S32LE, + CODEC_ID_PCM_S32BE, + CODEC_ID_PCM_U32LE, + CODEC_ID_PCM_U32BE, + CODEC_ID_PCM_S24LE, + CODEC_ID_PCM_S24BE, + CODEC_ID_PCM_U24LE, + CODEC_ID_PCM_U24BE, + CODEC_ID_PCM_S24DAUD, + + /* various adpcm codecs */ + CODEC_ID_ADPCM_IMA_QT= 0x11000, + CODEC_ID_ADPCM_IMA_WAV, + CODEC_ID_ADPCM_IMA_DK3, + CODEC_ID_ADPCM_IMA_DK4, + CODEC_ID_ADPCM_IMA_WS, + CODEC_ID_ADPCM_IMA_SMJPEG, + CODEC_ID_ADPCM_MS, + CODEC_ID_ADPCM_4XM, + CODEC_ID_ADPCM_XA, + CODEC_ID_ADPCM_ADX, + CODEC_ID_ADPCM_EA, + CODEC_ID_ADPCM_G726, + CODEC_ID_ADPCM_CT, + CODEC_ID_ADPCM_SWF, + CODEC_ID_ADPCM_YAMAHA, + CODEC_ID_ADPCM_SBPRO_4, + CODEC_ID_ADPCM_SBPRO_3, + CODEC_ID_ADPCM_SBPRO_2, + + /* AMR */ + CODEC_ID_AMR_NB= 0x12000, + CODEC_ID_AMR_WB, + + /* RealAudio codecs*/ + CODEC_ID_RA_144= 0x13000, + CODEC_ID_RA_288, + + /* various DPCM codecs */ + CODEC_ID_ROQ_DPCM= 0x14000, + CODEC_ID_INTERPLAY_DPCM, + CODEC_ID_XAN_DPCM, + CODEC_ID_SOL_DPCM, + + CODEC_ID_MP2= 0x15000, + CODEC_ID_MP3, /* prefered ID for MPEG Audio layer 1, 2 or3 decoding */ + CODEC_ID_AAC, +#if LIBAVCODEC_VERSION_INT < ((52<<16)+(0<<8)+0) + CODEC_ID_MPEG4AAC, +#endif + CODEC_ID_AC3, + CODEC_ID_DTS, + CODEC_ID_VORBIS, + CODEC_ID_DVAUDIO, + CODEC_ID_WMAV1, + CODEC_ID_WMAV2, + CODEC_ID_MACE3, + CODEC_ID_MACE6, + CODEC_ID_VMDAUDIO, + CODEC_ID_SONIC, + CODEC_ID_SONIC_LS, + CODEC_ID_FLAC, + CODEC_ID_MP3ADU, + CODEC_ID_MP3ON4, + CODEC_ID_SHORTEN, + CODEC_ID_ALAC, + CODEC_ID_WESTWOOD_SND1, + CODEC_ID_GSM, + CODEC_ID_QDM2, + CODEC_ID_COOK, + CODEC_ID_TRUESPEECH, + CODEC_ID_TTA, + CODEC_ID_SMACKAUDIO, + CODEC_ID_QCELP, + CODEC_ID_WAVPACK, + CODEC_ID_DSICINAUDIO, + CODEC_ID_IMC, + CODEC_ID_MUSEPACK7, + CODEC_ID_MLP, + + /* subtitle codecs */ + CODEC_ID_DVD_SUBTITLE= 0x17000, + CODEC_ID_DVB_SUBTITLE, + + CODEC_ID_MPEG2TS= 0x20000, /* _FAKE_ codec to indicate a raw MPEG2 transport + stream (only used by libavformat) */ +}; + +/* CODEC_ID_MP3LAME is absolete */ +#define CODEC_ID_MP3LAME CODEC_ID_MP3 + +enum CodecType { + CODEC_TYPE_UNKNOWN = -1, + CODEC_TYPE_VIDEO, + CODEC_TYPE_AUDIO, + CODEC_TYPE_DATA, + CODEC_TYPE_SUBTITLE, +}; + +/* currently unused, may be used if 24/32 bits samples ever supported */ +/* all in native endian */ +enum SampleFormat { + SAMPLE_FMT_NONE = -1, + SAMPLE_FMT_U8, ///< unsigned 8 bits + SAMPLE_FMT_S16, ///< signed 16 bits + SAMPLE_FMT_S24, ///< signed 24 bits + SAMPLE_FMT_S32, ///< signed 32 bits + SAMPLE_FMT_FLT, ///< float +}; + +/* in bytes */ +#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio + +/** + * Required number of additionally allocated bytes at the end of the input bitstream for decoding. + * this is mainly needed because some optimized bitstream readers read + * 32 or 64 bit at once and could read over the end
    + * Note, if the first 23 bits of the additional bytes are not 0 then damaged + * MPEG bitstreams could cause overread and segfault + */ +#define FF_INPUT_BUFFER_PADDING_SIZE 8 + +/** + * minimum encoding buffer size. + * used to avoid some checks during header writing + */ +#define FF_MIN_BUFFER_SIZE 16384 + +/* motion estimation type, EPZS by default */ +enum Motion_Est_ID { + ME_ZERO = 1, + ME_FULL, + ME_LOG, + ME_PHODS, + ME_EPZS, + ME_X1, + ME_HEX, + ME_UMH, + ME_ITER, +}; + +enum AVDiscard{ +//we leave some space between them for extensions (drop some keyframes for intra only or drop just some bidir frames) + AVDISCARD_NONE =-16, ///< discard nothing + AVDISCARD_DEFAULT= 0, ///< discard useless packets like 0 size packets in avi + AVDISCARD_NONREF = 8, ///< discard all non reference + AVDISCARD_BIDIR = 16, ///< discard all bidirectional frames + AVDISCARD_NONKEY = 32, ///< discard all frames except keyframes + AVDISCARD_ALL = 48, ///< discard all +}; + +typedef struct RcOverride{ + int start_frame; + int end_frame; + int qscale; // if this is 0 then quality_factor will be used instead + float quality_factor; +} RcOverride; + +#define FF_MAX_B_FRAMES 16 + +/* encoding support + these flags can be passed in AVCodecContext.flags before initing + Note: not everything is supported yet. +*/ + +#define CODEC_FLAG_QSCALE 0x0002 ///< use fixed qscale +#define CODEC_FLAG_4MV 0x0004 ///< 4 MV per MB allowed / Advanced prediction for H263 +#define CODEC_FLAG_QPEL 0x0010 ///< use qpel MC +#define CODEC_FLAG_GMC 0x0020 ///< use GMC +#define CODEC_FLAG_MV0 0x0040 ///< always try a MB with MV=<0,0> +#define CODEC_FLAG_PART 0x0080 ///< use data partitioning +/* parent program gurantees that the input for b-frame containing streams is not written to + for at least s->max_b_frames+1 frames, if this is not set than the input will be copied */ +#define CODEC_FLAG_INPUT_PRESERVED 0x0100 +#define CODEC_FLAG_PASS1 0x0200 ///< use internal 2pass ratecontrol in first pass mode +#define CODEC_FLAG_PASS2 0x0400 ///< use internal 2pass ratecontrol in second pass mode +#define CODEC_FLAG_EXTERN_HUFF 0x1000 ///< use external huffman table (for mjpeg) +#define CODEC_FLAG_GRAY 0x2000 ///< only decode/encode grayscale +#define CODEC_FLAG_EMU_EDGE 0x4000///< don't draw edges +#define CODEC_FLAG_PSNR 0x8000 ///< error[?] variables will be set during encoding +#define CODEC_FLAG_TRUNCATED 0x00010000 /** input bitstream might be truncated at a random location instead + of only at frame boundaries */ +#define CODEC_FLAG_NORMALIZE_AQP 0x00020000 ///< normalize adaptive quantization +#define CODEC_FLAG_INTERLACED_DCT 0x00040000 ///< use interlaced dct +#define CODEC_FLAG_LOW_DELAY 0x00080000 ///< force low delay +#define CODEC_FLAG_ALT_SCAN 0x00100000 ///< use alternate scan +#define CODEC_FLAG_TRELLIS_QUANT 0x00200000 ///< use trellis quantization +#define CODEC_FLAG_GLOBAL_HEADER 0x00400000 ///< place global headers in extradata instead of every keyframe +#define CODEC_FLAG_BITEXACT 0x00800000 ///< use only bitexact stuff (except (i)dct) +/* Fx : Flag for h263+ extra options */ +#define CODEC_FLAG_H263P_AIC 0x01000000 ///< H263 Advanced intra coding / MPEG4 AC prediction (remove this) +#define CODEC_FLAG_AC_PRED 0x01000000 ///< H263 Advanced intra coding / MPEG4 AC prediction +#define CODEC_FLAG_H263P_UMV 0x02000000 ///< Unlimited motion vector +#define CODEC_FLAG_CBP_RD 0x04000000 ///< use rate distortion optimization for cbp +#define CODEC_FLAG_QP_RD 0x08000000 ///< use rate distortion optimization for qp selectioon +#define CODEC_FLAG_H263P_AIV 0x00000008 ///< H263 Alternative inter vlc +#define CODEC_FLAG_OBMC 0x00000001 ///< OBMC +#define CODEC_FLAG_LOOP_FILTER 0x00000800 ///< loop filter +#define CODEC_FLAG_H263P_SLICE_STRUCT 0x10000000 +#define CODEC_FLAG_INTERLACED_ME 0x20000000 ///< interlaced motion estimation +#define CODEC_FLAG_SVCD_SCAN_OFFSET 0x40000000 ///< will reserve space for SVCD scan offset user data +#define CODEC_FLAG_CLOSED_GOP ((int)0x80000000) +#define CODEC_FLAG2_FAST 0x00000001 ///< allow non spec compliant speedup tricks +#define CODEC_FLAG2_STRICT_GOP 0x00000002 ///< strictly enforce GOP size +#define CODEC_FLAG2_NO_OUTPUT 0x00000004 ///< skip bitstream encoding +#define CODEC_FLAG2_LOCAL_HEADER 0x00000008 ///< place global headers at every keyframe instead of in extradata +#define CODEC_FLAG2_BPYRAMID 0x00000010 ///< H.264 allow b-frames to be used as references +#define CODEC_FLAG2_WPRED 0x00000020 ///< H.264 weighted biprediction for b-frames +#define CODEC_FLAG2_MIXED_REFS 0x00000040 ///< H.264 one reference per partition, as opposed to one reference per macroblock +#define CODEC_FLAG2_8X8DCT 0x00000080 ///< H.264 high profile 8x8 transform +#define CODEC_FLAG2_FASTPSKIP 0x00000100 ///< H.264 fast pskip +#define CODEC_FLAG2_AUD 0x00000200 ///< H.264 access unit delimiters +#define CODEC_FLAG2_BRDO 0x00000400 ///< b-frame rate-distortion optimization +#define CODEC_FLAG2_INTRA_VLC 0x00000800 ///< use MPEG-2 intra VLC table +#define CODEC_FLAG2_MEMC_ONLY 0x00001000 ///< only do ME/MC (I frames -> ref, P frame -> ME+MC) +#define CODEC_FLAG2_DROP_FRAME_TIMECODE 0x00002000 ///< timecode is in drop frame format +#define CODEC_FLAG2_SKIP_RD 0x00004000 ///< RD optimal MB level residual skiping + +/* Unsupported options : + * Syntax Arithmetic coding (SAC) + * Reference Picture Selection + * Independant Segment Decoding */ +/* /Fx */ +/* codec capabilities */ + +#define CODEC_CAP_DRAW_HORIZ_BAND 0x0001 ///< decoder can use draw_horiz_band callback +/** + * Codec uses get_buffer() for allocating buffers. + * direct rendering method 1 + */ +#define CODEC_CAP_DR1 0x0002 +/* if 'parse_only' field is true, then avcodec_parse_frame() can be + used */ +#define CODEC_CAP_PARSE_ONLY 0x0004 +#define CODEC_CAP_TRUNCATED 0x0008 +/* codec can export data for HW decoding (XvMC) */ +#define CODEC_CAP_HWACCEL 0x0010 +/** + * codec has a non zero delay and needs to be feeded with NULL at the end to get the delayed data. + * if this is not set, the codec is guranteed to never be feeded with NULL data + */ +#define CODEC_CAP_DELAY 0x0020 +/** + * Codec can be fed a final frame with a smaller size. + * This can be used to prevent truncation of the last audio samples. + */ +#define CODEC_CAP_SMALL_LAST_FRAME 0x0040 + +//the following defines may change, don't expect compatibility if you use them +#define MB_TYPE_INTRA4x4 0x0001 +#define MB_TYPE_INTRA16x16 0x0002 //FIXME h264 specific +#define MB_TYPE_INTRA_PCM 0x0004 //FIXME h264 specific +#define MB_TYPE_16x16 0x0008 +#define MB_TYPE_16x8 0x0010 +#define MB_TYPE_8x16 0x0020 +#define MB_TYPE_8x8 0x0040 +#define MB_TYPE_INTERLACED 0x0080 +#define MB_TYPE_DIRECT2 0x0100 //FIXME +#define MB_TYPE_ACPRED 0x0200 +#define MB_TYPE_GMC 0x0400 +#define MB_TYPE_SKIP 0x0800 +#define MB_TYPE_P0L0 0x1000 +#define MB_TYPE_P1L0 0x2000 +#define MB_TYPE_P0L1 0x4000 +#define MB_TYPE_P1L1 0x8000 +#define MB_TYPE_L0 (MB_TYPE_P0L0 | MB_TYPE_P1L0) +#define MB_TYPE_L1 (MB_TYPE_P0L1 | MB_TYPE_P1L1) +#define MB_TYPE_L0L1 (MB_TYPE_L0 | MB_TYPE_L1) +#define MB_TYPE_QUANT 0x00010000 +#define MB_TYPE_CBP 0x00020000 +//Note bits 24-31 are reserved for codec specific use (h264 ref0, mpeg1 0mv, ...) + +/** + * Pan Scan area. + * this specifies the area which should be displayed. Note there may be multiple such areas for one frame + */ +typedef struct AVPanScan{ + /** + * id. + * - encoding: set by user. + * - decoding: set by lavc + */ + int id; + + /** + * width and height in 1/16 pel + * - encoding: set by user. + * - decoding: set by lavc + */ + int width; + int height; + + /** + * position of the top left corner in 1/16 pel for up to 3 fields/frames. + * - encoding: set by user. + * - decoding: set by lavc + */ + int16_t position[3][2]; +}AVPanScan; + +#define FF_COMMON_FRAME \ + /**\ + * pointer to the picture planes.\ + * this might be different from the first allocated byte\ + * - encoding: \ + * - decoding: \ + */\ + uint8_t *data[4];\ + int linesize[4];\ + /**\ + * pointer to the first allocated byte of the picture. can be used in get_buffer/release_buffer\ + * this isn't used by lavc unless the default get/release_buffer() is used\ + * - encoding: \ + * - decoding: \ + */\ + uint8_t *base[4];\ + /**\ + * 1 -> keyframe, 0-> not\ + * - encoding: set by lavc\ + * - decoding: set by lavc\ + */\ + int key_frame;\ +\ + /**\ + * picture type of the frame, see ?_TYPE below.\ + * - encoding: set by lavc for coded_picture (and set by user for input)\ + * - decoding: set by lavc\ + */\ + int pict_type;\ +\ + /**\ + * presentation timestamp in time_base units (time when frame should be shown to user)\ + * if AV_NOPTS_VALUE then frame_rate = 1/time_base will be assumed\ + * - encoding: MUST be set by user\ + * - decoding: set by lavc\ + */\ + int64_t pts;\ +\ + /**\ + * picture number in bitstream order.\ + * - encoding: set by\ + * - decoding: set by lavc\ + */\ + int coded_picture_number;\ + /**\ + * picture number in display order.\ + * - encoding: set by\ + * - decoding: set by lavc\ + */\ + int display_picture_number;\ +\ + /**\ + * quality (between 1 (good) and FF_LAMBDA_MAX (bad)) \ + * - encoding: set by lavc for coded_picture (and set by user for input)\ + * - decoding: set by lavc\ + */\ + int quality; \ +\ + /**\ + * buffer age (1->was last buffer and dint change, 2->..., ...).\ + * set to INT_MAX if the buffer has not been used yet \ + * - encoding: unused\ + * - decoding: MUST be set by get_buffer()\ + */\ + int age;\ +\ + /**\ + * is this picture used as reference\ + * - encoding: unused\ + * - decoding: set by lavc (before get_buffer() call))\ + */\ + int reference;\ +\ + /**\ + * QP table\ + * - encoding: unused\ + * - decoding: set by lavc\ + */\ + int8_t *qscale_table;\ + /**\ + * QP store stride\ + * - encoding: unused\ + * - decoding: set by lavc\ + */\ + int qstride;\ +\ + /**\ + * mbskip_table[mb]>=1 if MB didnt change\ + * stride= mb_width = (width+15)>>4\ + * - encoding: unused\ + * - decoding: set by lavc\ + */\ + uint8_t *mbskip_table;\ +\ + /**\ + * Motion vector table.\ + * @code\ + * example:\ + * int mv_sample_log2= 4 - motion_subsample_log2;\ + * int mb_width= (width+15)>>4;\ + * int mv_stride= (mb_width << mv_sample_log2) + 1;\ + * motion_val[direction][x + y*mv_stride][0->mv_x, 1->mv_y];\ + * @endcode\ + * - encoding: set by user\ + * - decoding: set by lavc\ + */\ + int16_t (*motion_val[2])[2];\ +\ + /**\ + * Macroblock type table\ + * mb_type_base + mb_width + 2\ + * - encoding: set by user\ + * - decoding: set by lavc\ + */\ + uint32_t *mb_type;\ +\ + /**\ + * log2 of the size of the block which a single vector in motion_val represents: \ + * (4->16x16, 3->8x8, 2-> 4x4, 1-> 2x2)\ + * - encoding: unused\ + * - decoding: set by lavc\ + */\ + uint8_t motion_subsample_log2;\ +\ + /**\ + * for some private data of the user\ + * - encoding: unused\ + * - decoding: set by user\ + */\ + void *opaque;\ +\ + /**\ + * error\ + * - encoding: set by lavc if flags&CODEC_FLAG_PSNR\ + * - decoding: unused\ + */\ + uint64_t error[4];\ +\ + /**\ + * type of the buffer (to keep track of who has to dealloc data[*])\ + * - encoding: set by the one who allocs it\ + * - decoding: set by the one who allocs it\ + * Note: user allocated (direct rendering) & internal buffers can not coexist currently\ + */\ + int type;\ + \ + /**\ + * when decoding, this signal how much the picture must be delayed.\ + * extra_delay = repeat_pict / (2*fps)\ + * - encoding: unused\ + * - decoding: set by lavc\ + */\ + int repeat_pict;\ + \ + /**\ + * \ + */\ + int qscale_type;\ + \ + /**\ + * The content of the picture is interlaced.\ + * - encoding: set by user\ + * - decoding: set by lavc (default 0)\ + */\ + int interlaced_frame;\ + \ + /**\ + * if the content is interlaced, is top field displayed first.\ + * - encoding: set by user\ + * - decoding: set by lavc\ + */\ + int top_field_first;\ + \ + /**\ + * Pan scan.\ + * - encoding: set by user\ + * - decoding: set by lavc\ + */\ + AVPanScan *pan_scan;\ + \ + /**\ + * tell user application that palette has changed from previous frame.\ + * - encoding: ??? (no palette-enabled encoder yet)\ + * - decoding: set by lavc (default 0)\ + */\ + int palette_has_changed;\ + \ + /**\ + * Codec suggestion on buffer type if != 0\ + * - encoding: unused\ + * - decoding: set by lavc (before get_buffer() call))\ + */\ + int buffer_hints;\ +\ + /**\ + * DCT coeffitients\ + * - encoding: unused\ + * - decoding: set by lavc\ + */\ + short *dct_coeff;\ +\ + /**\ + * Motion referece frame index\ + * - encoding: set by user\ + * - decoding: set by lavc\ + */\ + int8_t *ref_index[2]; + +#define FF_QSCALE_TYPE_MPEG1 0 +#define FF_QSCALE_TYPE_MPEG2 1 +#define FF_QSCALE_TYPE_H264 2 + +#define FF_BUFFER_TYPE_INTERNAL 1 +#define FF_BUFFER_TYPE_USER 2 ///< Direct rendering buffers (image is (de)allocated by user) +#define FF_BUFFER_TYPE_SHARED 4 ///< buffer from somewhere else, don't dealloc image (data/base), all other tables are not shared +#define FF_BUFFER_TYPE_COPY 8 ///< just a (modified) copy of some other buffer, don't dealloc anything + + +#define FF_I_TYPE 1 // Intra +#define FF_P_TYPE 2 // Predicted +#define FF_B_TYPE 3 // Bi-dir predicted +#define FF_S_TYPE 4 // S(GMC)-VOP MPEG4 +#define FF_SI_TYPE 5 +#define FF_SP_TYPE 6 + +#define FF_BUFFER_HINTS_VALID 0x01 // Buffer hints value is meaningful (if 0 ignore) +#define FF_BUFFER_HINTS_READABLE 0x02 // Codec will read from buffer +#define FF_BUFFER_HINTS_PRESERVE 0x04 // User must not alter buffer content +#define FF_BUFFER_HINTS_REUSABLE 0x08 // Codec will reuse the buffer (update) + +/** + * Audio Video Frame. + */ +typedef struct AVFrame { + FF_COMMON_FRAME +} AVFrame; + +#define DEFAULT_FRAME_RATE_BASE 1001000 + +/** + * main external api structure. + */ +typedef struct AVCodecContext { + /** + * Info on struct for av_log + * - set by avcodec_alloc_context + */ + AVClass *av_class; + /** + * the average bitrate. + * - encoding: set by user. unused for constant quantizer encoding + * - decoding: set by lavc. 0 or some bitrate if this info is available in the stream + */ + int bit_rate; + + /** + * number of bits the bitstream is allowed to diverge from the reference. + * the reference can be CBR (for CBR pass1) or VBR (for pass2) + * - encoding: set by user. unused for constant quantizer encoding + * - decoding: unused + */ + int bit_rate_tolerance; + + /** + * CODEC_FLAG_*. + * - encoding: set by user. + * - decoding: set by user. + */ + int flags; + + /** + * some codecs needs additionnal format info. It is stored here + * - encoding: set by user. + * - decoding: set by lavc. (FIXME is this ok?) + */ + int sub_id; + + /** + * motion estimation algorithm used for video coding. + * 1 (zero), 2 (full), 3 (log), 4 (phods), 5 (epzs), 6 (x1), 7 (hex), + * 8 (umh), 9 (iter) [7, 8 are x264 specific, 9 is snow specific] + * - encoding: MUST be set by user. + * - decoding: unused + */ + int me_method; + + /** + * some codecs need / can use extra-data like huffman tables. + * mjpeg: huffman tables + * rv10: additional flags + * mpeg4: global headers (they can be in the bitstream or here) + * the allocated memory should be FF_INPUT_BUFFER_PADDING_SIZE bytes larger + * then extradata_size to avoid prolems if its read with the bitstream reader + * the bytewise contents of extradata must not depend on the architecture or cpu endianness + * - encoding: set/allocated/freed by lavc. + * - decoding: set/allocated/freed by user. + */ + uint8_t *extradata; + int extradata_size; + + /** + * this is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. for fixed-fps content, + * timebase should be 1/framerate and timestamp increments should be + * identically 1. + * - encoding: MUST be set by user + * - decoding: set by lavc. + */ + AVRational time_base; + + /* video only */ + /** + * picture width / height. + * - encoding: MUST be set by user. + * - decoding: set by lavc. + * Note, for compatibility its possible to set this instead of + * coded_width/height before decoding + */ + int width, height; + +#define FF_ASPECT_EXTENDED 15 + + /** + * the number of pictures in a group of pitures, or 0 for intra_only. + * - encoding: set by user. + * - decoding: unused + */ + int gop_size; + + /** + * pixel format, see PIX_FMT_xxx. + * - encoding: set by user. + * - decoding: set by lavc. + */ + enum PixelFormat pix_fmt; + + /** + * Frame rate emulation. If not zero lower layer (i.e. format handler) + * has to read frames at native frame rate. + * - encoding: set by user. + * - decoding: unused. + */ + int rate_emu; + + /** + * if non NULL, 'draw_horiz_band' is called by the libavcodec + * decoder to draw an horizontal band. It improve cache usage. Not + * all codecs can do that. You must check the codec capabilities + * before + * - encoding: unused + * - decoding: set by user. + * @param height the height of the slice + * @param y the y position of the slice + * @param type 1->top field, 2->bottom field, 3->frame + * @param offset offset into the AVFrame.data from which the slice should be read + */ + void (*draw_horiz_band)(struct AVCodecContext *s, + const AVFrame *src, int offset[4], + int y, int type, int height); + + /* audio only */ + int sample_rate; ///< samples per sec + int channels; + + /** + * audio sample format. + * - encoding: set by user. + * - decoding: set by lavc. + */ + enum SampleFormat sample_fmt; ///< sample format, currenly unused + + /* the following data should not be initialized */ + /** + * samples per packet. initialized when calling 'init' + */ + int frame_size; + int frame_number; ///< audio or video frame number + int real_pict_num; ///< returns the real picture number of previous encoded frame + + /** + * number of frames the decoded output will be delayed relative to + * the encoded input. + * - encoding: set by lavc. + * - decoding: unused + */ + int delay; + + /* - encoding parameters */ + float qcompress; ///< amount of qscale change between easy & hard scenes (0.0-1.0) + float qblur; ///< amount of qscale smoothing over time (0.0-1.0) + + /** + * minimum quantizer. + * - encoding: set by user. + * - decoding: unused + */ + int qmin; + + /** + * maximum quantizer. + * - encoding: set by user. + * - decoding: unused + */ + int qmax; + + /** + * maximum quantizer difference between frames. + * - encoding: set by user. + * - decoding: unused + */ + int max_qdiff; + + /** + * maximum number of b frames between non b frames. + * note: the output will be delayed by max_b_frames+1 relative to the input + * - encoding: set by user. + * - decoding: unused + */ + int max_b_frames; + + /** + * qscale factor between ip and b frames. + * - encoding: set by user. + * - decoding: unused + */ + float b_quant_factor; + + /** obsolete FIXME remove */ + int rc_strategy; +#define FF_RC_STRATEGY_XVID 1 + + int b_frame_strategy; + + /** + * hurry up amount. + * deprecated in favor of skip_idct and skip_frame + * - encoding: unused + * - decoding: set by user. 1-> skip b frames, 2-> skip idct/dequant too, 5-> skip everything except header + */ + int hurry_up; + + struct AVCodec *codec; + + void *priv_data; + + /* unused, FIXME remove*/ + int rtp_mode; + + int rtp_payload_size; /* The size of the RTP payload: the coder will */ + /* do it's best to deliver a chunk with size */ + /* below rtp_payload_size, the chunk will start */ + /* with a start code on some codecs like H.263 */ + /* This doesn't take account of any particular */ + /* headers inside the transmited RTP payload */ + + + /* The RTP callback: This function is called */ + /* every time the encoder has a packet to send */ + /* Depends on the encoder if the data starts */ + /* with a Start Code (it should) H.263 does. */ + /* mb_nb contains the number of macroblocks */ + /* encoded in the RTP payload */ + void (*rtp_callback)(struct AVCodecContext *avctx, void *data, int size, int mb_nb); + + /* statistics, used for 2-pass encoding */ + int mv_bits; + int header_bits; + int i_tex_bits; + int p_tex_bits; + int i_count; + int p_count; + int skip_count; + int misc_bits; + + /** + * number of bits used for the previously encoded frame. + * - encoding: set by lavc + * - decoding: unused + */ + int frame_bits; + + /** + * private data of the user, can be used to carry app specific stuff. + * - encoding: set by user + * - decoding: set by user + */ + void *opaque; + + char codec_name[32]; + enum CodecType codec_type; /* see CODEC_TYPE_xxx */ + enum CodecID codec_id; /* see CODEC_ID_xxx */ + + /** + * fourcc (LSB first, so "ABCD" -> ('D'<<24) + ('C'<<16) + ('B'<<8) + 'A'). + * this is used to workaround some encoder bugs + * - encoding: set by user, if not then the default based on codec_id will be used + * - decoding: set by user, will be converted to upper case by lavc during init + */ + unsigned int codec_tag; + + /** + * workaround bugs in encoders which sometimes cannot be detected automatically. + * - encoding: set by user + * - decoding: set by user + */ + int workaround_bugs; +#define FF_BUG_AUTODETECT 1 ///< autodetection +#define FF_BUG_OLD_MSMPEG4 2 +#define FF_BUG_XVID_ILACE 4 +#define FF_BUG_UMP4 8 +#define FF_BUG_NO_PADDING 16 +#define FF_BUG_AMV 32 +#define FF_BUG_AC_VLC 0 ///< will be removed, libavcodec can now handle these non compliant files by default +#define FF_BUG_QPEL_CHROMA 64 +#define FF_BUG_STD_QPEL 128 +#define FF_BUG_QPEL_CHROMA2 256 +#define FF_BUG_DIRECT_BLOCKSIZE 512 +#define FF_BUG_EDGE 1024 +#define FF_BUG_HPEL_CHROMA 2048 +#define FF_BUG_DC_CLIP 4096 +#define FF_BUG_MS 8192 ///< workaround various bugs in microsofts broken decoders +//#define FF_BUG_FAKE_SCALABILITY 16 //autodetection should work 100% + + /** + * luma single coeff elimination threshold. + * - encoding: set by user + * - decoding: unused + */ + int luma_elim_threshold; + + /** + * chroma single coeff elimination threshold. + * - encoding: set by user + * - decoding: unused + */ + int chroma_elim_threshold; + + /** + * strictly follow the std (MPEG4, ...). + * - encoding: set by user + * - decoding: unused + */ + int strict_std_compliance; +#define FF_COMPLIANCE_VERY_STRICT 2 ///< strictly conform to a older more strict version of the spec or reference software +#define FF_COMPLIANCE_STRICT 1 ///< strictly conform to all the things in the spec no matter what consequences +#define FF_COMPLIANCE_NORMAL 0 +#define FF_COMPLIANCE_INOFFICIAL -1 ///< allow inofficial extensions +#define FF_COMPLIANCE_EXPERIMENTAL -2 ///< allow non standarized experimental things + + /** + * qscale offset between ip and b frames. + * if > 0 then the last p frame quantizer will be used (q= lastp_q*factor+offset) + * if < 0 then normal ratecontrol will be done (q= -normal_q*factor+offset) + * - encoding: set by user. + * - decoding: unused + */ + float b_quant_offset; + + /** + * error resilience higher values will detect more errors but may missdetect + * some more or less valid parts as errors. + * - encoding: unused + * - decoding: set by user + */ + int error_resilience; +#define FF_ER_CAREFUL 1 +#define FF_ER_COMPLIANT 2 +#define FF_ER_AGGRESSIVE 3 +#define FF_ER_VERY_AGGRESSIVE 4 + + /** + * called at the beginning of each frame to get a buffer for it. + * if pic.reference is set then the frame will be read later by lavc + * avcodec_align_dimensions() should be used to find the required width and + * height, as they normally need to be rounded up to the next multiple of 16 + * - encoding: unused + * - decoding: set by lavc, user can override + */ + int (*get_buffer)(struct AVCodecContext *c, AVFrame *pic); + + /** + * called to release buffers which where allocated with get_buffer. + * a released buffer can be reused in get_buffer() + * pic.data[*] must be set to NULL + * - encoding: unused + * - decoding: set by lavc, user can override + */ + void (*release_buffer)(struct AVCodecContext *c, AVFrame *pic); + + /** + * if 1 the stream has a 1 frame delay during decoding. + * - encoding: set by lavc + * - decoding: set by lavc + */ + int has_b_frames; + + /** + * number of bytes per packet if constant and known or 0 + * used by some WAV based audio codecs + */ + int block_align; + + int parse_only; /* - decoding only: if true, only parsing is done + (function avcodec_parse_frame()). The frame + data is returned. Only MPEG codecs support this now. */ + + /** + * 0-> h263 quant 1-> mpeg quant. + * - encoding: set by user. + * - decoding: unused + */ + int mpeg_quant; + + /** + * pass1 encoding statistics output buffer. + * - encoding: set by lavc + * - decoding: unused + */ + char *stats_out; + + /** + * pass2 encoding statistics input buffer. + * concatenated stuff from stats_out of pass1 should be placed here + * - encoding: allocated/set/freed by user + * - decoding: unused + */ + char *stats_in; + + /** + * ratecontrol qmin qmax limiting method. + * 0-> clipping, 1-> use a nice continous function to limit qscale wthin qmin/qmax + * - encoding: set by user. + * - decoding: unused + */ + float rc_qsquish; + + float rc_qmod_amp; + int rc_qmod_freq; + + /** + * ratecontrol override, see RcOverride. + * - encoding: allocated/set/freed by user. + * - decoding: unused + */ + RcOverride *rc_override; + int rc_override_count; + + /** + * rate control equation. + * - encoding: set by user + * - decoding: unused + */ + char *rc_eq; + + /** + * maximum bitrate. + * - encoding: set by user. + * - decoding: unused + */ + int rc_max_rate; + + /** + * minimum bitrate. + * - encoding: set by user. + * - decoding: unused + */ + int rc_min_rate; + + /** + * decoder bitstream buffer size. + * - encoding: set by user. + * - decoding: unused + */ + int rc_buffer_size; + float rc_buffer_aggressivity; + + /** + * qscale factor between p and i frames. + * if > 0 then the last p frame quantizer will be used (q= lastp_q*factor+offset) + * if < 0 then normal ratecontrol will be done (q= -normal_q*factor+offset) + * - encoding: set by user. + * - decoding: unused + */ + float i_quant_factor; + + /** + * qscale offset between p and i frames. + * - encoding: set by user. + * - decoding: unused + */ + float i_quant_offset; + + /** + * initial complexity for pass1 ratecontrol. + * - encoding: set by user. + * - decoding: unused + */ + float rc_initial_cplx; + + /** + * dct algorithm, see FF_DCT_* below. + * - encoding: set by user + * - decoding: unused + */ + int dct_algo; +#define FF_DCT_AUTO 0 +#define FF_DCT_FASTINT 1 +#define FF_DCT_INT 2 +#define FF_DCT_MMX 3 +#define FF_DCT_MLIB 4 +#define FF_DCT_ALTIVEC 5 +#define FF_DCT_FAAN 6 + + /** + * luminance masking (0-> disabled). + * - encoding: set by user + * - decoding: unused + */ + float lumi_masking; + + /** + * temporary complexity masking (0-> disabled). + * - encoding: set by user + * - decoding: unused + */ + float temporal_cplx_masking; + + /** + * spatial complexity masking (0-> disabled). + * - encoding: set by user + * - decoding: unused + */ + float spatial_cplx_masking; + + /** + * p block masking (0-> disabled). + * - encoding: set by user + * - decoding: unused + */ + float p_masking; + + /** + * darkness masking (0-> disabled). + * - encoding: set by user + * - decoding: unused + */ + float dark_masking; + + + /* for binary compatibility */ + int unused; + + /** + * idct algorithm, see FF_IDCT_* below. + * - encoding: set by user + * - decoding: set by user + */ + int idct_algo; +#define FF_IDCT_AUTO 0 +#define FF_IDCT_INT 1 +#define FF_IDCT_SIMPLE 2 +#define FF_IDCT_SIMPLEMMX 3 +#define FF_IDCT_LIBMPEG2MMX 4 +#define FF_IDCT_PS2 5 +#define FF_IDCT_MLIB 6 +#define FF_IDCT_ARM 7 +#define FF_IDCT_ALTIVEC 8 +#define FF_IDCT_SH4 9 +#define FF_IDCT_SIMPLEARM 10 +#define FF_IDCT_H264 11 +#define FF_IDCT_VP3 12 +#define FF_IDCT_IPP 13 +#define FF_IDCT_XVIDMMX 14 +#define FF_IDCT_CAVS 15 +#define FF_IDCT_SIMPLEARMV5TE 16 +#define FF_IDCT_SIMPLEARMV6 17 + + /** + * slice count. + * - encoding: set by lavc + * - decoding: set by user (or 0) + */ + int slice_count; + /** + * slice offsets in the frame in bytes. + * - encoding: set/allocated by lavc + * - decoding: set/allocated by user (or NULL) + */ + int *slice_offset; + + /** + * error concealment flags. + * - encoding: unused + * - decoding: set by user + */ + int error_concealment; +#define FF_EC_GUESS_MVS 1 +#define FF_EC_DEBLOCK 2 + + /** + * dsp_mask could be add used to disable unwanted CPU features + * CPU features (i.e. MMX, SSE. ...) + * + * with FORCE flag you may instead enable given CPU features + * (Dangerous: usable in case of misdetection, improper usage however will + * result into program crash) + */ + unsigned dsp_mask; +#define FF_MM_FORCE 0x80000000 /* force usage of selected flags (OR) */ + /* lower 16 bits - CPU features */ +#ifdef HAVE_MMX +#define FF_MM_MMX 0x0001 /* standard MMX */ +#define FF_MM_3DNOW 0x0004 /* AMD 3DNOW */ +#define FF_MM_MMXEXT 0x0002 /* SSE integer functions or AMD MMX ext */ +#define FF_MM_SSE 0x0008 /* SSE functions */ +#define FF_MM_SSE2 0x0010 /* PIV SSE2 functions */ +#define FF_MM_3DNOWEXT 0x0020 /* AMD 3DNowExt */ +#endif /* HAVE_MMX */ +#ifdef HAVE_IWMMXT +#define FF_MM_IWMMXT 0x0100 /* XScale IWMMXT */ +#endif /* HAVE_IWMMXT */ + + /** + * bits per sample/pixel from the demuxer (needed for huffyuv). + * - encoding: set by lavc + * - decoding: set by user + */ + int bits_per_sample; + + /** + * prediction method (needed for huffyuv). + * - encoding: set by user + * - decoding: unused + */ + int prediction_method; +#define FF_PRED_LEFT 0 +#define FF_PRED_PLANE 1 +#define FF_PRED_MEDIAN 2 + + /** + * sample aspect ratio (0 if unknown). + * numerator and denominator must be relative prime and smaller then 256 for some video standards + * - encoding: set by user. + * - decoding: set by lavc. + */ + AVRational sample_aspect_ratio; + + /** + * the picture in the bitstream. + * - encoding: set by lavc + * - decoding: set by lavc + */ + AVFrame *coded_frame; + + /** + * debug. + * - encoding: set by user. + * - decoding: set by user. + */ + int debug; +#define FF_DEBUG_PICT_INFO 1 +#define FF_DEBUG_RC 2 +#define FF_DEBUG_BITSTREAM 4 +#define FF_DEBUG_MB_TYPE 8 +#define FF_DEBUG_QP 16 +#define FF_DEBUG_MV 32 +#define FF_DEBUG_DCT_COEFF 0x00000040 +#define FF_DEBUG_SKIP 0x00000080 +#define FF_DEBUG_STARTCODE 0x00000100 +#define FF_DEBUG_PTS 0x00000200 +#define FF_DEBUG_ER 0x00000400 +#define FF_DEBUG_MMCO 0x00000800 +#define FF_DEBUG_BUGS 0x00001000 +#define FF_DEBUG_VIS_QP 0x00002000 +#define FF_DEBUG_VIS_MB_TYPE 0x00004000 + + /** + * debug. + * - encoding: set by user. + * - decoding: set by user. + */ + int debug_mv; +#define FF_DEBUG_VIS_MV_P_FOR 0x00000001 //visualize forward predicted MVs of P frames +#define FF_DEBUG_VIS_MV_B_FOR 0x00000002 //visualize forward predicted MVs of B frames +#define FF_DEBUG_VIS_MV_B_BACK 0x00000004 //visualize backward predicted MVs of B frames + + /** + * error. + * - encoding: set by lavc if flags&CODEC_FLAG_PSNR + * - decoding: unused + */ + uint64_t error[4]; + + /** + * minimum MB quantizer. + * - encoding: unused + * - decoding: unused + */ + int mb_qmin; + + /** + * maximum MB quantizer. + * - encoding: unused + * - decoding: unused + */ + int mb_qmax; + + /** + * motion estimation compare function. + * - encoding: set by user. + * - decoding: unused + */ + int me_cmp; + /** + * subpixel motion estimation compare function. + * - encoding: set by user. + * - decoding: unused + */ + int me_sub_cmp; + /** + * macroblock compare function (not supported yet). + * - encoding: set by user. + * - decoding: unused + */ + int mb_cmp; + /** + * interlaced dct compare function + * - encoding: set by user. + * - decoding: unused + */ + int ildct_cmp; +#define FF_CMP_SAD 0 +#define FF_CMP_SSE 1 +#define FF_CMP_SATD 2 +#define FF_CMP_DCT 3 +#define FF_CMP_PSNR 4 +#define FF_CMP_BIT 5 +#define FF_CMP_RD 6 +#define FF_CMP_ZERO 7 +#define FF_CMP_VSAD 8 +#define FF_CMP_VSSE 9 +#define FF_CMP_NSSE 10 +#define FF_CMP_W53 11 +#define FF_CMP_W97 12 +#define FF_CMP_DCTMAX 13 +#define FF_CMP_DCT264 14 +#define FF_CMP_CHROMA 256 + + /** + * ME diamond size & shape. + * - encoding: set by user. + * - decoding: unused + */ + int dia_size; + + /** + * amount of previous MV predictors (2a+1 x 2a+1 square). + * - encoding: set by user. + * - decoding: unused + */ + int last_predictor_count; + + /** + * pre pass for motion estimation. + * - encoding: set by user. + * - decoding: unused + */ + int pre_me; + + /** + * motion estimation pre pass compare function. + * - encoding: set by user. + * - decoding: unused + */ + int me_pre_cmp; + + /** + * ME pre pass diamond size & shape. + * - encoding: set by user. + * - decoding: unused + */ + int pre_dia_size; + + /** + * subpel ME quality. + * - encoding: set by user. + * - decoding: unused + */ + int me_subpel_quality; + + /** + * callback to negotiate the pixelFormat. + * @param fmt is the list of formats which are supported by the codec, + * its terminated by -1 as 0 is a valid format, the formats are ordered by quality + * the first is allways the native one + * @return the choosen format + * - encoding: unused + * - decoding: set by user, if not set then the native format will always be choosen + */ + enum PixelFormat (*get_format)(struct AVCodecContext *s, const enum PixelFormat * fmt); + + /** + * DTG active format information (additionnal aspect ratio + * information only used in DVB MPEG2 transport streams). 0 if + * not set. + * + * - encoding: unused. + * - decoding: set by decoder + */ + int dtg_active_format; +#define FF_DTG_AFD_SAME 8 +#define FF_DTG_AFD_4_3 9 +#define FF_DTG_AFD_16_9 10 +#define FF_DTG_AFD_14_9 11 +#define FF_DTG_AFD_4_3_SP_14_9 13 +#define FF_DTG_AFD_16_9_SP_14_9 14 +#define FF_DTG_AFD_SP_4_3 15 + + /** + * Maximum motion estimation search range in subpel units. + * if 0 then no limit + * + * - encoding: set by user. + * - decoding: unused. + */ + int me_range; + + /** + * intra quantizer bias. + * - encoding: set by user. + * - decoding: unused + */ + int intra_quant_bias; +#define FF_DEFAULT_QUANT_BIAS 999999 + + /** + * inter quantizer bias. + * - encoding: set by user. + * - decoding: unused + */ + int inter_quant_bias; + + /** + * color table ID. + * - encoding: unused. + * - decoding: which clrtable should be used for 8bit RGB images + * table have to be stored somewhere FIXME + */ + int color_table_id; + + /** + * internal_buffer count. + * Don't touch, used by lavc default_get_buffer() + */ + int internal_buffer_count; + + /** + * internal_buffers. + * Don't touch, used by lavc default_get_buffer() + */ + void *internal_buffer; + +#define FF_LAMBDA_SHIFT 7 +#define FF_LAMBDA_SCALE (1< ('D'<<24) + ('C'<<16) + ('B'<<8) + 'A'). + * this is used to workaround some encoder bugs + * - encoding: unused + * - decoding: set by user, will be converted to upper case by lavc during init + */ + unsigned int stream_codec_tag; + + /** + * scene change detection threshold. + * 0 is default, larger means fewer detected scene changes + * - encoding: set by user. + * - decoding: unused + */ + int scenechange_threshold; + + /** + * minimum lagrange multipler + * - encoding: set by user. + * - decoding: unused + */ + int lmin; + + /** + * maximum lagrange multipler + * - encoding: set by user. + * - decoding: unused + */ + int lmax; + + /** + * Palette control structure + * - encoding: ??? (no palette-enabled encoder yet) + * - decoding: set by user. + */ + struct AVPaletteControl *palctrl; + + /** + * noise reduction strength + * - encoding: set by user. + * - decoding: unused + */ + int noise_reduction; + + /** + * called at the beginning of a frame to get cr buffer for it. + * buffer type (size, hints) must be the same. lavc won't check it. + * lavc will pass previous buffer in pic, function should return + * same buffer or new buffer with old frame "painted" into it. + * if pic.data[0] == NULL must behave like get_buffer(). + * - encoding: unused + * - decoding: set by lavc, user can override + */ + int (*reget_buffer)(struct AVCodecContext *c, AVFrame *pic); + + /** + * number of bits which should be loaded into the rc buffer before decoding starts + * - encoding: set by user. + * - decoding: unused + */ + int rc_initial_buffer_occupancy; + + /** + * + * - encoding: set by user. + * - decoding: unused + */ + int inter_threshold; + + /** + * CODEC_FLAG2_*. + * - encoding: set by user. + * - decoding: set by user. + */ + int flags2; + + /** + * simulates errors in the bitstream to test error concealment. + * - encoding: set by user. + * - decoding: unused. + */ + int error_rate; + + /** + * MP3 antialias algorithm, see FF_AA_* below. + * - encoding: unused + * - decoding: set by user + */ + int antialias_algo; +#define FF_AA_AUTO 0 +#define FF_AA_FASTINT 1 //not implemented yet +#define FF_AA_INT 2 +#define FF_AA_FLOAT 3 + /** + * Quantizer noise shaping. + * - encoding: set by user + * - decoding: unused + */ + int quantizer_noise_shaping; + + /** + * Thread count. + * is used to decide how many independant tasks should be passed to execute() + * - encoding: set by user + * - decoding: set by user + */ + int thread_count; + + /** + * the codec may call this to execute several independant things. it will return only after + * finishing all tasks, the user may replace this with some multithreaded implementation, the + * default implementation will execute the parts serially + * @param count the number of things to execute + * - encoding: set by lavc, user can override + * - decoding: set by lavc, user can override + */ + int (*execute)(struct AVCodecContext *c, int (*func)(struct AVCodecContext *c2, void *arg), void **arg2, int *ret, int count); + + /** + * Thread opaque. + * can be used by execute() to store some per AVCodecContext stuff. + * - encoding: set by execute() + * - decoding: set by execute() + */ + void *thread_opaque; + + /** + * Motion estimation threshold. under which no motion estimation is + * performed, but instead the user specified motion vectors are used + * + * - encoding: set by user + * - decoding: unused + */ + int me_threshold; + + /** + * Macroblock threshold. under which the user specified macroblock types will be used + * - encoding: set by user + * - decoding: unused + */ + int mb_threshold; + + /** + * precision of the intra dc coefficient - 8. + * - encoding: set by user + * - decoding: unused + */ + int intra_dc_precision; + + /** + * noise vs. sse weight for the nsse comparsion function. + * - encoding: set by user + * - decoding: unused + */ + int nsse_weight; + + /** + * number of macroblock rows at the top which are skipped. + * - encoding: unused + * - decoding: set by user + */ + int skip_top; + + /** + * number of macroblock rows at the bottom which are skipped. + * - encoding: unused + * - decoding: set by user + */ + int skip_bottom; + + /** + * profile + * - encoding: set by user + * - decoding: set by lavc + */ + int profile; +#define FF_PROFILE_UNKNOWN -99 + + /** + * level + * - encoding: set by user + * - decoding: set by lavc + */ + int level; +#define FF_LEVEL_UNKNOWN -99 + + /** + * low resolution decoding. 1-> 1/2 size, 2->1/4 size + * - encoding: unused + * - decoding: set by user + */ + int lowres; + + /** + * bitsream width / height. may be different from width/height if lowres + * or other things are used + * - encoding: unused + * - decoding: set by user before init if known, codec should override / dynamically change if needed + */ + int coded_width, coded_height; + + /** + * frame skip threshold + * - encoding: set by user + * - decoding: unused + */ + int frame_skip_threshold; + + /** + * frame skip factor + * - encoding: set by user + * - decoding: unused + */ + int frame_skip_factor; + + /** + * frame skip exponent + * - encoding: set by user + * - decoding: unused + */ + int frame_skip_exp; + + /** + * frame skip comparission function + * - encoding: set by user. + * - decoding: unused + */ + int frame_skip_cmp; + + /** + * border processing masking. raises the quantizer for mbs on the borders + * of the picture. + * - encoding: set by user + * - decoding: unused + */ + float border_masking; + + /** + * minimum MB lagrange multipler. + * - encoding: set by user. + * - decoding: unused + */ + int mb_lmin; + + /** + * maximum MB lagrange multipler. + * - encoding: set by user. + * - decoding: unused + */ + int mb_lmax; + + /** + * + * - encoding: set by user. + * - decoding: unused + */ + int me_penalty_compensation; + + /** + * + * - encoding: unused + * - decoding: set by user. + */ + enum AVDiscard skip_loop_filter; + + /** + * + * - encoding: unused + * - decoding: set by user. + */ + enum AVDiscard skip_idct; + + /** + * + * - encoding: unused + * - decoding: set by user. + */ + enum AVDiscard skip_frame; + + /** + * + * - encoding: set by user. + * - decoding: unused + */ + int bidir_refine; + + /** + * + * - encoding: set by user. + * - decoding: unused + */ + int brd_scale; + + /** + * constant rate factor - quality-based VBR - values ~correspond to qps + * - encoding: set by user. + * - decoding: unused + */ + float crf; + + /** + * constant quantization parameter rate control method + * - encoding: set by user. + * - decoding: unused + */ + int cqp; + + /** + * minimum gop size + * - encoding: set by user. + * - decoding: unused + */ + int keyint_min; + + /** + * number of reference frames + * - encoding: set by user. + * - decoding: unused + */ + int refs; + + /** + * chroma qp offset from luma + * - encoding: set by user. + * - decoding: unused + */ + int chromaoffset; + + /** + * influences how often b-frames are used + * - encoding: set by user. + * - decoding: unused + */ + int bframebias; + + /** + * trellis RD quantization + * - encoding: set by user. + * - decoding: unused + */ + int trellis; + + /** + * reduce fluctuations in qp (before curve compression) + * - encoding: set by user. + * - decoding: unused + */ + float complexityblur; + + /** + * in-loop deblocking filter alphac0 parameter + * alpha is in the range -6...6 + * - encoding: set by user. + * - decoding: unused + */ + int deblockalpha; + + /** + * in-loop deblocking filter beta parameter + * beta is in the range -6...6 + * - encoding: set by user. + * - decoding: unused + */ + int deblockbeta; + + /** + * macroblock subpartition sizes to consider - p8x8, p4x4, b8x8, i8x8, i4x4 + * - encoding: set by user. + * - decoding: unused + */ + int partitions; +#define X264_PART_I4X4 0x001 /* Analyse i4x4 */ +#define X264_PART_I8X8 0x002 /* Analyse i8x8 (requires 8x8 transform) */ +#define X264_PART_P8X8 0x010 /* Analyse p16x8, p8x16 and p8x8 */ +#define X264_PART_P4X4 0x020 /* Analyse p8x4, p4x8, p4x4 */ +#define X264_PART_B8X8 0x100 /* Analyse b16x8, b8x16 and b8x8 */ + + /** + * direct mv prediction mode - 0 (none), 1 (spatial), 2 (temporal) + * - encoding: set by user. + * - decoding: unused + */ + int directpred; + + /** + * audio cutoff bandwidth (0 means "automatic") . Currently used only by FAAC + * - encoding: set by user. + * - decoding: unused + */ + int cutoff; + + /** + * multiplied by qscale for each frame and added to scene_change_score + * - encoding: set by user. + * - decoding: unused + */ + int scenechange_factor; + + /** + * + * note: value depends upon the compare functin used for fullpel ME + * - encoding: set by user. + * - decoding: unused + */ + int mv0_threshold; + + /** + * adjusts sensitivity of b_frame_strategy 1 + * - encoding: set by user. + * - decoding: unused + */ + int b_sensitivity; + + /** + * - encoding: set by user. + * - decoding: unused + */ + int compression_level; +#define FF_COMPRESSION_DEFAULT -1 + + /** + * sets whether to use LPC mode - used by FLAC encoder + * - encoding: set by user. + * - decoding: unused. + */ + int use_lpc; + + /** + * LPC coefficient precision - used by FLAC encoder + * - encoding: set by user. + * - decoding: unused. + */ + int lpc_coeff_precision; + + /** + * - encoding: set by user. + * - decoding: unused. + */ + int min_prediction_order; + + /** + * - encoding: set by user. + * - decoding: unused. + */ + int max_prediction_order; + + /** + * search method for selecting prediction order + * - encoding: set by user. + * - decoding: unused. + */ + int prediction_order_method; + + /** + * - encoding: set by user. + * - decoding: unused. + */ + int min_partition_order; + + /** + * - encoding: set by user. + * - decoding: unused. + */ + int max_partition_order; + + /** + * GOP timecode frame start number, in non drop frame format + * - encoding: set by user. + * - decoding: unused. + */ + int64_t timecode_frame_start; +} AVCodecContext; + +/** + * AVCodec. + */ +typedef struct AVCodec { + const char *name; + enum CodecType type; + enum CodecID id; + int priv_data_size; + int (*init)(AVCodecContext *); + int (*encode)(AVCodecContext *, uint8_t *buf, int buf_size, void *data); + int (*close)(AVCodecContext *); + int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, + uint8_t *buf, int buf_size); + int capabilities; + struct AVCodec *next; + void (*flush)(AVCodecContext *); + const AVRational *supported_framerates; ///array of supported framerates, or NULL if any, array is terminated by {0,0} + const enum PixelFormat *pix_fmts; ///array of supported pixel formats, or NULL if unknown, array is terminanted by -1 +} AVCodec; + +/** + * four components are given, that's all. + * the last component is alpha + */ +typedef struct AVPicture { + uint8_t *data[4]; + int linesize[4]; ///< number of bytes per line +} AVPicture; + +/** + * AVPaletteControl + * This structure defines a method for communicating palette changes + * between and demuxer and a decoder. + * this is totally broken, palette changes should be sent as AVPackets + */ +#define AVPALETTE_SIZE 1024 +#define AVPALETTE_COUNT 256 +typedef struct AVPaletteControl { + + /* demuxer sets this to 1 to indicate the palette has changed; + * decoder resets to 0 */ + int palette_changed; + + /* 4-byte ARGB palette entries, stored in native byte order; note that + * the individual palette components should be on a 8-bit scale; if + * the palette data comes from a IBM VGA native format, the component + * data is probably 6 bits in size and needs to be scaled */ + unsigned int palette[AVPALETTE_COUNT]; + +} AVPaletteControl attribute_deprecated; + +typedef struct AVSubtitleRect { + uint16_t x; + uint16_t y; + uint16_t w; + uint16_t h; + uint16_t nb_colors; + int linesize; + uint32_t *rgba_palette; + uint8_t *bitmap; +} AVSubtitleRect; + +typedef struct AVSubtitle { + uint16_t format; /* 0 = graphics */ + uint32_t start_display_time; /* relative to packet pts, in ms */ + uint32_t end_display_time; /* relative to packet pts, in ms */ + uint32_t num_rects; + AVSubtitleRect *rects; +} AVSubtitle; + +extern AVCodec ac3_encoder; +extern AVCodec amr_nb_encoder; +extern AVCodec amr_wb_encoder; +extern AVCodec asv1_encoder; +extern AVCodec asv2_encoder; +extern AVCodec bmp_encoder; +extern AVCodec dvvideo_encoder; +extern AVCodec faac_encoder; +extern AVCodec ffv1_encoder; +extern AVCodec ffvhuff_encoder; +extern AVCodec flac_encoder; +extern AVCodec flashsv_encoder; +extern AVCodec flv_encoder; +extern AVCodec gif_encoder; +extern AVCodec h261_encoder; +extern AVCodec h263_encoder; +extern AVCodec h263p_encoder; +extern AVCodec h264_encoder; +extern AVCodec huffyuv_encoder; +extern AVCodec jpegls_encoder; +extern AVCodec libgsm_encoder; +extern AVCodec libtheora_encoder; +extern AVCodec ljpeg_encoder; +extern AVCodec mdec_encoder; +extern AVCodec mjpeg_encoder; +extern AVCodec mp2_encoder; +extern AVCodec mp3lame_encoder; +extern AVCodec mpeg1video_encoder; +extern AVCodec mpeg2video_encoder; +extern AVCodec mpeg4_encoder; +extern AVCodec msmpeg4v1_encoder; +extern AVCodec msmpeg4v2_encoder; +extern AVCodec msmpeg4v3_encoder; +extern AVCodec oggvorbis_encoder; +extern AVCodec pam_encoder; +extern AVCodec pbm_encoder; +extern AVCodec pgm_encoder; +extern AVCodec pgmyuv_encoder; +extern AVCodec png_encoder; +extern AVCodec ppm_encoder; +extern AVCodec rv10_encoder; +extern AVCodec rv20_encoder; +extern AVCodec snow_encoder; +extern AVCodec sonic_encoder; +extern AVCodec sonic_ls_encoder; +extern AVCodec svq1_encoder; +extern AVCodec vcr1_encoder; +extern AVCodec vorbis_encoder; +extern AVCodec wmav1_encoder; +extern AVCodec wmav2_encoder; +extern AVCodec wmv1_encoder; +extern AVCodec wmv2_encoder; +extern AVCodec x264_encoder; +extern AVCodec xvid_encoder; +extern AVCodec zlib_encoder; +extern AVCodec zmbv_encoder; + +extern AVCodec aac_decoder; +extern AVCodec aasc_decoder; +extern AVCodec alac_decoder; +extern AVCodec amr_nb_decoder; +extern AVCodec amr_wb_decoder; +extern AVCodec asv1_decoder; +extern AVCodec asv2_decoder; +extern AVCodec avs_decoder; +extern AVCodec bmp_decoder; +extern AVCodec cavs_decoder; +extern AVCodec cinepak_decoder; +extern AVCodec cljr_decoder; +extern AVCodec cook_decoder; +extern AVCodec cscd_decoder; +extern AVCodec cyuv_decoder; +extern AVCodec dsicinaudio_decoder; +extern AVCodec dsicinvideo_decoder; +extern AVCodec dvvideo_decoder; +extern AVCodec eightbps_decoder; +extern AVCodec ffv1_decoder; +extern AVCodec ffvhuff_decoder; +extern AVCodec flac_decoder; +extern AVCodec flashsv_decoder; +extern AVCodec flic_decoder; +extern AVCodec flv_decoder; +extern AVCodec fourxm_decoder; +extern AVCodec fraps_decoder; +extern AVCodec gif_decoder; +extern AVCodec h261_decoder; +extern AVCodec h263_decoder; +extern AVCodec h263i_decoder; +extern AVCodec h264_decoder; +extern AVCodec huffyuv_decoder; +extern AVCodec idcin_decoder; +extern AVCodec imc_decoder; +extern AVCodec indeo2_decoder; +extern AVCodec indeo3_decoder; +extern AVCodec interplay_dpcm_decoder; +extern AVCodec interplay_video_decoder; +extern AVCodec kmvc_decoder; +extern AVCodec libgsm_decoder; +extern AVCodec loco_decoder; +extern AVCodec mace3_decoder; +extern AVCodec mace6_decoder; +extern AVCodec mdec_decoder; +extern AVCodec mjpeg_decoder; +extern AVCodec mjpegb_decoder; +extern AVCodec mmvideo_decoder; +extern AVCodec mp2_decoder; +extern AVCodec mp3_decoder; +extern AVCodec mp3adu_decoder; +extern AVCodec mp3on4_decoder; +extern AVCodec mpc7_decoder; +extern AVCodec mpeg1video_decoder; +extern AVCodec mpeg2video_decoder; +extern AVCodec mpeg4_decoder; +extern AVCodec mpeg4aac_decoder; +extern AVCodec mpeg_xvmc_decoder; +extern AVCodec mpegvideo_decoder; +extern AVCodec msmpeg4v1_decoder; +extern AVCodec msmpeg4v2_decoder; +extern AVCodec msmpeg4v3_decoder; +extern AVCodec msrle_decoder; +extern AVCodec msvideo1_decoder; +extern AVCodec mszh_decoder; +extern AVCodec nuv_decoder; +extern AVCodec oggvorbis_decoder; +extern AVCodec png_decoder; +extern AVCodec qdm2_decoder; +extern AVCodec qdraw_decoder; +extern AVCodec qpeg_decoder; +extern AVCodec qtrle_decoder; +extern AVCodec ra_144_decoder; +extern AVCodec ra_288_decoder; +extern AVCodec roq_decoder; +extern AVCodec roq_dpcm_decoder; +extern AVCodec rpza_decoder; +extern AVCodec rv10_decoder; +extern AVCodec rv20_decoder; +extern AVCodec rv30_decoder; +extern AVCodec rv40_decoder; +extern AVCodec shorten_decoder; +extern AVCodec smackaud_decoder; +extern AVCodec smacker_decoder; +extern AVCodec smc_decoder; +extern AVCodec snow_decoder; +extern AVCodec sol_dpcm_decoder; +extern AVCodec sonic_decoder; +extern AVCodec sp5x_decoder; +extern AVCodec svq1_decoder; +extern AVCodec svq3_decoder; +extern AVCodec targa_decoder; +extern AVCodec theora_decoder; +extern AVCodec tiertexseqvideo_decoder; +extern AVCodec tiff_decoder; +extern AVCodec truemotion1_decoder; +extern AVCodec truemotion2_decoder; +extern AVCodec truespeech_decoder; +extern AVCodec tscc_decoder; +extern AVCodec tta_decoder; +extern AVCodec ulti_decoder; +extern AVCodec vc1_decoder; +extern AVCodec vcr1_decoder; +extern AVCodec vmdaudio_decoder; +extern AVCodec vmdvideo_decoder; +extern AVCodec vmnc_decoder; +extern AVCodec vorbis_decoder; +extern AVCodec vp3_decoder; +extern AVCodec vp5_decoder; +extern AVCodec vp6_decoder; +extern AVCodec vp6f_decoder; +extern AVCodec vqa_decoder; +extern AVCodec wavpack_decoder; +extern AVCodec wmav1_decoder; +extern AVCodec wmav2_decoder; +extern AVCodec wmv1_decoder; +extern AVCodec wmv2_decoder; +extern AVCodec wmv3_decoder; +extern AVCodec wnv1_decoder; +extern AVCodec ws_snd1_decoder; +extern AVCodec xan_dpcm_decoder; +extern AVCodec xan_wc3_decoder; +extern AVCodec xl_decoder; +extern AVCodec zlib_decoder; +extern AVCodec zmbv_decoder; + +/* pcm codecs */ +#define PCM_CODEC(id, name) \ +extern AVCodec name ## _decoder; \ +extern AVCodec name ## _encoder + +PCM_CODEC(CODEC_ID_PCM_ALAW, pcm_alaw); +PCM_CODEC(CODEC_ID_PCM_MULAW, pcm_mulaw); +PCM_CODEC(CODEC_ID_PCM_S8, pcm_s8); +PCM_CODEC(CODEC_ID_PCM_S16BE, pcm_s16be); +PCM_CODEC(CODEC_ID_PCM_S16LE, pcm_s16le); +PCM_CODEC(CODEC_ID_PCM_S24BE, pcm_s24be); +PCM_CODEC(CODEC_ID_PCM_S24DAUD, pcm_s24daud); +PCM_CODEC(CODEC_ID_PCM_S24LE, pcm_s24le); +PCM_CODEC(CODEC_ID_PCM_S32BE, pcm_s32be); +PCM_CODEC(CODEC_ID_PCM_S32LE, pcm_s32le); +PCM_CODEC(CODEC_ID_PCM_U8, pcm_u8); +PCM_CODEC(CODEC_ID_PCM_U16BE, pcm_u16be); +PCM_CODEC(CODEC_ID_PCM_U16LE, pcm_u16le); +PCM_CODEC(CODEC_ID_PCM_U24BE, pcm_u24be); +PCM_CODEC(CODEC_ID_PCM_U24LE, pcm_u24le); +PCM_CODEC(CODEC_ID_PCM_U32BE, pcm_u32be); +PCM_CODEC(CODEC_ID_PCM_U32LE, pcm_u32le); + +/* adpcm codecs */ + +PCM_CODEC(CODEC_ID_ADPCM_4XM, adpcm_4xm); +PCM_CODEC(CODEC_ID_ADPCM_ADX, adpcm_adx); +PCM_CODEC(CODEC_ID_ADPCM_CT, adpcm_ct); +PCM_CODEC(CODEC_ID_ADPCM_EA, adpcm_ea); +PCM_CODEC(CODEC_ID_ADPCM_G726, adpcm_g726); +PCM_CODEC(CODEC_ID_ADPCM_IMA_DK3, adpcm_ima_dk3); +PCM_CODEC(CODEC_ID_ADPCM_IMA_DK4, adpcm_ima_dk4); +PCM_CODEC(CODEC_ID_ADPCM_IMA_QT, adpcm_ima_qt); +PCM_CODEC(CODEC_ID_ADPCM_IMA_WAV, adpcm_ima_wav); +PCM_CODEC(CODEC_ID_ADPCM_IMA_WS, adpcm_ima_ws); +PCM_CODEC(CODEC_ID_ADPCM_MS, adpcm_ms); +PCM_CODEC(CODEC_ID_ADPCM_SBPRO_2, adpcm_sbpro_2); +PCM_CODEC(CODEC_ID_ADPCM_SBPRO_3, adpcm_sbpro_3); +PCM_CODEC(CODEC_ID_ADPCM_SBPRO_4, adpcm_sbpro_4); +PCM_CODEC(CODEC_ID_ADPCM_SMJPEG, adpcm_ima_smjpeg); +PCM_CODEC(CODEC_ID_ADPCM_SWF, adpcm_swf); +PCM_CODEC(CODEC_ID_ADPCM_XA, adpcm_xa); +PCM_CODEC(CODEC_ID_ADPCM_YAMAHA, adpcm_yamaha); + +#undef PCM_CODEC + +/* dummy raw video codec */ +extern AVCodec rawvideo_decoder; +extern AVCodec rawvideo_encoder; + +/* the following codecs use external GPL libs */ +extern AVCodec ac3_decoder; +extern AVCodec dts_decoder; + +/* subtitles */ +extern AVCodec dvbsub_decoder; +extern AVCodec dvbsub_encoder; +extern AVCodec dvdsub_decoder; +extern AVCodec dvdsub_encoder; + +/* resample.c */ + +struct ReSampleContext; +struct AVResampleContext; + +typedef struct ReSampleContext ReSampleContext; + +ReSampleContext *audio_resample_init(int output_channels, int input_channels, + int output_rate, int input_rate); +int audio_resample(ReSampleContext *s, short *output, short *input, int nb_samples); +void audio_resample_close(ReSampleContext *s); + +struct AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_length, int log2_phase_count, int linear, double cutoff); +int av_resample(struct AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx); +void av_resample_compensate(struct AVResampleContext *c, int sample_delta, int compensation_distance); +void av_resample_close(struct AVResampleContext *c); + +/* YUV420 format is assumed ! */ + +struct ImgReSampleContext; + +typedef struct ImgReSampleContext ImgReSampleContext; + +ImgReSampleContext *img_resample_init(int output_width, int output_height, + int input_width, int input_height); + +ImgReSampleContext *img_resample_full_init(int owidth, int oheight, + int iwidth, int iheight, + int topBand, int bottomBand, + int leftBand, int rightBand, + int padtop, int padbottom, + int padleft, int padright); + + +void img_resample(ImgReSampleContext *s, + AVPicture *output, const AVPicture *input); + +void img_resample_close(ImgReSampleContext *s); + +/** + * Allocate memory for a picture. Call avpicture_free to free it. + * + * @param picture the picture to be filled in. + * @param pix_fmt the format of the picture. + * @param width the width of the picture. + * @param height the height of the picture. + * @return 0 if successful, -1 if not. + */ +int avpicture_alloc(AVPicture *picture, int pix_fmt, int width, int height); + +/* Free a picture previously allocated by avpicture_alloc. */ +void avpicture_free(AVPicture *picture); + +int avpicture_fill(AVPicture *picture, uint8_t *ptr, + int pix_fmt, int width, int height); +int avpicture_layout(const AVPicture* src, int pix_fmt, int width, int height, + unsigned char *dest, int dest_size); +int avpicture_get_size(int pix_fmt, int width, int height); +void avcodec_get_chroma_sub_sample(int pix_fmt, int *h_shift, int *v_shift); +const char *avcodec_get_pix_fmt_name(int pix_fmt); +void avcodec_set_dimensions(AVCodecContext *s, int width, int height); +enum PixelFormat avcodec_get_pix_fmt(const char* name); +unsigned int avcodec_pix_fmt_to_codec_tag(enum PixelFormat p); + +#define FF_LOSS_RESOLUTION 0x0001 /* loss due to resolution change */ +#define FF_LOSS_DEPTH 0x0002 /* loss due to color depth change */ +#define FF_LOSS_COLORSPACE 0x0004 /* loss due to color space conversion */ +#define FF_LOSS_ALPHA 0x0008 /* loss of alpha bits */ +#define FF_LOSS_COLORQUANT 0x0010 /* loss due to color quantization */ +#define FF_LOSS_CHROMA 0x0020 /* loss of chroma (e.g. rgb to gray conversion) */ + +int avcodec_get_pix_fmt_loss(int dst_pix_fmt, int src_pix_fmt, + int has_alpha); +int avcodec_find_best_pix_fmt(int pix_fmt_mask, int src_pix_fmt, + int has_alpha, int *loss_ptr); + +#define FF_ALPHA_TRANSP 0x0001 /* image has some totally transparent pixels */ +#define FF_ALPHA_SEMI_TRANSP 0x0002 /* image has some transparent pixels */ +int img_get_alpha_info(const AVPicture *src, + int pix_fmt, int width, int height); + +/* convert among pixel formats */ +int img_convert(AVPicture *dst, int dst_pix_fmt, + const AVPicture *src, int pix_fmt, + int width, int height); + +/* deinterlace a picture */ +int avpicture_deinterlace(AVPicture *dst, const AVPicture *src, + int pix_fmt, int width, int height); + +/* external high level API */ + +extern AVCodec *first_avcodec; + +/* returns LIBAVCODEC_VERSION_INT constant */ +unsigned avcodec_version(void); +/* returns LIBAVCODEC_BUILD constant */ +unsigned avcodec_build(void); +void avcodec_init(void); + +void register_avcodec(AVCodec *format); +AVCodec *avcodec_find_encoder(enum CodecID id); +AVCodec *avcodec_find_encoder_by_name(const char *name); +AVCodec *avcodec_find_decoder(enum CodecID id); +AVCodec *avcodec_find_decoder_by_name(const char *name); +void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode); + +void avcodec_get_context_defaults(AVCodecContext *s); +AVCodecContext *avcodec_alloc_context(void); +void avcodec_get_frame_defaults(AVFrame *pic); +AVFrame *avcodec_alloc_frame(void); + +int avcodec_default_get_buffer(AVCodecContext *s, AVFrame *pic); +void avcodec_default_release_buffer(AVCodecContext *s, AVFrame *pic); +int avcodec_default_reget_buffer(AVCodecContext *s, AVFrame *pic); +void avcodec_align_dimensions(AVCodecContext *s, int *width, int *height); +int avcodec_check_dimensions(void *av_log_ctx, unsigned int w, unsigned int h); +enum PixelFormat avcodec_default_get_format(struct AVCodecContext *s, const enum PixelFormat * fmt); + +int avcodec_thread_init(AVCodecContext *s, int thread_count); +void avcodec_thread_free(AVCodecContext *s); +int avcodec_thread_execute(AVCodecContext *s, int (*func)(AVCodecContext *c2, void *arg2),void **arg, int *ret, int count); +int avcodec_default_execute(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2),void **arg, int *ret, int count); +//FIXME func typedef + +/** + * opens / inits the AVCodecContext. + * not thread save! + */ +int avcodec_open(AVCodecContext *avctx, AVCodec *codec); + + +attribute_deprecated int avcodec_decode_audio(AVCodecContext *avctx, int16_t *samples, + int *frame_size_ptr, + uint8_t *buf, int buf_size); +/** + * Decode an audio frame. + * + * @param avctx the codec context. + * @param samples output buffer, 16 byte aligned + * @param frame_size_ptr the output buffer size in bytes (you MUST set this to the allocated size before calling avcodec_decode_audio2()), zero if no frame could be compressed + * @param buf input buffer, 16 byte aligned + * @param buf_size the input buffer size + * @return 0 if successful, -1 if not. + */ +int avcodec_decode_audio2(AVCodecContext *avctx, int16_t *samples, + int *frame_size_ptr, + uint8_t *buf, int buf_size); +int avcodec_decode_video(AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, + uint8_t *buf, int buf_size); +int avcodec_decode_subtitle(AVCodecContext *avctx, AVSubtitle *sub, + int *got_sub_ptr, + const uint8_t *buf, int buf_size); +int avcodec_parse_frame(AVCodecContext *avctx, uint8_t **pdata, + int *data_size_ptr, + uint8_t *buf, int buf_size); +int avcodec_encode_audio(AVCodecContext *avctx, uint8_t *buf, int buf_size, + const short *samples); +int avcodec_encode_video(AVCodecContext *avctx, uint8_t *buf, int buf_size, + const AVFrame *pict); +int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, + const AVSubtitle *sub); + +int avcodec_close(AVCodecContext *avctx); + +void avcodec_register_all(void); + +void avcodec_flush_buffers(AVCodecContext *avctx); + +void avcodec_default_free_buffers(AVCodecContext *s); + +/* misc usefull functions */ + +/** + * returns a single letter to describe the picture type + */ +char av_get_pict_type_char(int pict_type); + +/** + * returns codec bits per sample + */ +int av_get_bits_per_sample(enum CodecID codec_id); + +/* frame parsing */ +typedef struct AVCodecParserContext { + void *priv_data; + struct AVCodecParser *parser; + int64_t frame_offset; /* offset of the current frame */ + int64_t cur_offset; /* current offset + (incremented by each av_parser_parse()) */ + int64_t last_frame_offset; /* offset of the last frame */ + /* video info */ + int pict_type; /* XXX: put it back in AVCodecContext */ + int repeat_pict; /* XXX: put it back in AVCodecContext */ + int64_t pts; /* pts of the current frame */ + int64_t dts; /* dts of the current frame */ + + /* private data */ + int64_t last_pts; + int64_t last_dts; + int fetch_timestamp; + +#define AV_PARSER_PTS_NB 4 + int cur_frame_start_index; + int64_t cur_frame_offset[AV_PARSER_PTS_NB]; + int64_t cur_frame_pts[AV_PARSER_PTS_NB]; + int64_t cur_frame_dts[AV_PARSER_PTS_NB]; + + int flags; +#define PARSER_FLAG_COMPLETE_FRAMES 0x0001 +} AVCodecParserContext; + +typedef struct AVCodecParser { + int codec_ids[5]; /* several codec IDs are permitted */ + int priv_data_size; + int (*parser_init)(AVCodecParserContext *s); + int (*parser_parse)(AVCodecParserContext *s, + AVCodecContext *avctx, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size); + void (*parser_close)(AVCodecParserContext *s); + int (*split)(AVCodecContext *avctx, const uint8_t *buf, int buf_size); + struct AVCodecParser *next; +} AVCodecParser; + +extern AVCodecParser *av_first_parser; + +void av_register_codec_parser(AVCodecParser *parser); +AVCodecParserContext *av_parser_init(int codec_id); +int av_parser_parse(AVCodecParserContext *s, + AVCodecContext *avctx, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, + int64_t pts, int64_t dts); +int av_parser_change(AVCodecParserContext *s, + AVCodecContext *avctx, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, int keyframe); +void av_parser_close(AVCodecParserContext *s); + +extern AVCodecParser aac_parser; +extern AVCodecParser ac3_parser; +extern AVCodecParser cavsvideo_parser; +extern AVCodecParser dvbsub_parser; +extern AVCodecParser dvdsub_parser; +extern AVCodecParser h261_parser; +extern AVCodecParser h263_parser; +extern AVCodecParser h264_parser; +extern AVCodecParser mjpeg_parser; +extern AVCodecParser mpeg4video_parser; +extern AVCodecParser mpegaudio_parser; +extern AVCodecParser mpegvideo_parser; +extern AVCodecParser pnm_parser; +extern AVCodecParser vc1_parser; + + +typedef struct AVBitStreamFilterContext { + void *priv_data; + struct AVBitStreamFilter *filter; + AVCodecParserContext *parser; + struct AVBitStreamFilterContext *next; +} AVBitStreamFilterContext; + + +typedef struct AVBitStreamFilter { + const char *name; + int priv_data_size; + int (*filter)(AVBitStreamFilterContext *bsfc, + AVCodecContext *avctx, const char *args, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, int keyframe); + struct AVBitStreamFilter *next; +} AVBitStreamFilter; + +extern AVBitStreamFilter *av_first_bitstream_filter; + +void av_register_bitstream_filter(AVBitStreamFilter *bsf); +AVBitStreamFilterContext *av_bitstream_filter_init(const char *name); +int av_bitstream_filter_filter(AVBitStreamFilterContext *bsfc, + AVCodecContext *avctx, const char *args, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, int keyframe); +void av_bitstream_filter_close(AVBitStreamFilterContext *bsf); + +extern AVBitStreamFilter dump_extradata_bsf; +extern AVBitStreamFilter remove_extradata_bsf; +extern AVBitStreamFilter noise_bsf; +extern AVBitStreamFilter mp3_header_compress_bsf; +extern AVBitStreamFilter mp3_header_decompress_bsf; +extern AVBitStreamFilter mjpega_dump_header_bsf; + + +/* memory */ +void *av_fast_realloc(void *ptr, unsigned int *size, unsigned int min_size); +/* for static data only */ +/* call av_free_static to release all staticaly allocated tables */ +void av_free_static(void); +void *av_mallocz_static(unsigned int size); +void *av_realloc_static(void *ptr, unsigned int size); + +void img_copy(AVPicture *dst, const AVPicture *src, + int pix_fmt, int width, int height); + +int img_crop(AVPicture *dst, const AVPicture *src, + int pix_fmt, int top_band, int left_band); + +int img_pad(AVPicture *dst, const AVPicture *src, int height, int width, int pix_fmt, + int padtop, int padbottom, int padleft, int padright, int *color); + +extern unsigned int av_xiphlacing(unsigned char *s, unsigned int v); + +#ifdef __cplusplus +} +#endif + +#endif /* AVCODEC_H */ diff --git a/extra_lib/include/ffmpeg/avformat.h b/extra_lib/include/ffmpeg/avformat.h new file mode 100644 index 0000000..38099d4 --- /dev/null +++ b/extra_lib/include/ffmpeg/avformat.h @@ -0,0 +1,549 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_H +#define AVFORMAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define LIBAVFORMAT_VERSION_INT ((51<<16)+(8<<8)+0) +#define LIBAVFORMAT_VERSION 51.8.0 +#define LIBAVFORMAT_BUILD LIBAVFORMAT_VERSION_INT + +#define LIBAVFORMAT_IDENT "Lavf" AV_STRINGIFY(LIBAVFORMAT_VERSION) + +#include +#include /* FILE */ +#include "avcodec.h" + +#include "avio.h" + +/* packet functions */ + +typedef struct AVPacket { + int64_t pts; ///< presentation time stamp in time_base units + int64_t dts; ///< decompression time stamp in time_base units + uint8_t *data; + int size; + int stream_index; + int flags; + int duration; ///< presentation duration in time_base units (0 if not available) + void (*destruct)(struct AVPacket *); + void *priv; + int64_t pos; ///< byte position in stream, -1 if unknown +} AVPacket; +#define PKT_FLAG_KEY 0x0001 + +void av_destruct_packet_nofree(AVPacket *pkt); +void av_destruct_packet(AVPacket *pkt); + +/* initialize optional fields of a packet */ +static inline void av_init_packet(AVPacket *pkt) +{ + pkt->pts = AV_NOPTS_VALUE; + pkt->dts = AV_NOPTS_VALUE; + pkt->pos = -1; + pkt->duration = 0; + pkt->flags = 0; + pkt->stream_index = 0; + pkt->destruct= av_destruct_packet_nofree; +} + +int av_new_packet(AVPacket *pkt, int size); +int av_get_packet(ByteIOContext *s, AVPacket *pkt, int size); +int av_dup_packet(AVPacket *pkt); + +/** + * Free a packet + * + * @param pkt packet to free + */ +static inline void av_free_packet(AVPacket *pkt) +{ + if (pkt && pkt->destruct) { + pkt->destruct(pkt); + } +} + +/*************************************************/ +/* fractional numbers for exact pts handling */ + +/* the exact value of the fractional number is: 'val + num / den'. num + is assumed to be such as 0 <= num < den */ +typedef struct AVFrac { + int64_t val, num, den; +} AVFrac attribute_deprecated; + +/*************************************************/ +/* input/output formats */ + +struct AVCodecTag; + +struct AVFormatContext; + +/* this structure contains the data a format has to probe a file */ +typedef struct AVProbeData { + const char *filename; + unsigned char *buf; + int buf_size; +} AVProbeData; + +#define AVPROBE_SCORE_MAX 100 ///< max score, half of that is used for file extension based detection + +typedef struct AVFormatParameters { + AVRational time_base; + int sample_rate; + int channels; + int width; + int height; + enum PixelFormat pix_fmt; + int channel; /* used to select dv channel */ + const char *device; /* video, audio or DV device */ + const char *standard; /* tv standard, NTSC, PAL, SECAM */ + int mpeg2ts_raw:1; /* force raw MPEG2 transport stream output, if possible */ + int mpeg2ts_compute_pcr:1; /* compute exact PCR for each transport + stream packet (only meaningful if + mpeg2ts_raw is TRUE */ + int initial_pause:1; /* do not begin to play the stream + immediately (RTSP only) */ + int prealloced_context:1; + enum CodecID video_codec_id; + enum CodecID audio_codec_id; +} AVFormatParameters; + +//! demuxer will use url_fopen, no opened file should be provided by the caller +#define AVFMT_NOFILE 0x0001 +#define AVFMT_NEEDNUMBER 0x0002 /* needs '%d' in filename */ +#define AVFMT_SHOW_IDS 0x0008 /* show format stream IDs numbers */ +#define AVFMT_RAWPICTURE 0x0020 /* format wants AVPicture structure for + raw picture data */ +#define AVFMT_GLOBALHEADER 0x0040 /* format wants global header */ +#define AVFMT_NOTIMESTAMPS 0x0080 /* format doesnt need / has any timestamps */ +#define AVFMT_GENERIC_INDEX 0x0100 /* use generic index building code */ + +typedef struct AVOutputFormat { + const char *name; + const char *long_name; + const char *mime_type; + const char *extensions; /* comma separated extensions */ + /* size of private data so that it can be allocated in the wrapper */ + int priv_data_size; + /* output support */ + enum CodecID audio_codec; /* default audio codec */ + enum CodecID video_codec; /* default video codec */ + int (*write_header)(struct AVFormatContext *); + int (*write_packet)(struct AVFormatContext *, AVPacket *pkt); + int (*write_trailer)(struct AVFormatContext *); + /* can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_GLOBALHEADER */ + int flags; + /* currently only used to set pixel format if not YUV420P */ + int (*set_parameters)(struct AVFormatContext *, AVFormatParameters *); + int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, AVPacket *in, int flush); + + /** + * list of supported codec_id-codec_tag pairs, ordered by "better choice first" + * the arrays are all CODEC_ID_NONE terminated + */ + const struct AVCodecTag **codec_tag; + + /* private fields */ + struct AVOutputFormat *next; +} AVOutputFormat; + +typedef struct AVInputFormat { + const char *name; + const char *long_name; + /* size of private data so that it can be allocated in the wrapper */ + int priv_data_size; + /* tell if a given file has a chance of being parsing by this format */ + int (*read_probe)(AVProbeData *); + /* read the format header and initialize the AVFormatContext + structure. Return 0 if OK. 'ap' if non NULL contains + additionnal paramters. Only used in raw format right + now. 'av_new_stream' should be called to create new streams. */ + int (*read_header)(struct AVFormatContext *, + AVFormatParameters *ap); + /* read one packet and put it in 'pkt'. pts and flags are also + set. 'av_new_stream' can be called only if the flag + AVFMTCTX_NOHEADER is used. */ + int (*read_packet)(struct AVFormatContext *, AVPacket *pkt); + /* close the stream. The AVFormatContext and AVStreams are not + freed by this function */ + int (*read_close)(struct AVFormatContext *); + /** + * seek to a given timestamp relative to the frames in + * stream component stream_index + * @param stream_index must not be -1 + * @param flags selects which direction should be preferred if no exact + * match is available + */ + int (*read_seek)(struct AVFormatContext *, + int stream_index, int64_t timestamp, int flags); + /** + * gets the next timestamp in AV_TIME_BASE units. + */ + int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index, + int64_t *pos, int64_t pos_limit); + /* can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER */ + int flags; + /* if extensions are defined, then no probe is done. You should + usually not use extension format guessing because it is not + reliable enough */ + const char *extensions; + /* general purpose read only value that the format can use */ + int value; + + /* start/resume playing - only meaningful if using a network based format + (RTSP) */ + int (*read_play)(struct AVFormatContext *); + + /* pause playing - only meaningful if using a network based format + (RTSP) */ + int (*read_pause)(struct AVFormatContext *); + + const struct AVCodecTag **codec_tag; + + /* private fields */ + struct AVInputFormat *next; +} AVInputFormat; + +typedef struct AVIndexEntry { + int64_t pos; + int64_t timestamp; +#define AVINDEX_KEYFRAME 0x0001 + int flags:2; + int size:30; //yeah trying to keep the size of this small to reduce memory requirements (its 24 vs 32 byte due to possible 8byte align) + int min_distance; /* min distance between this and the previous keyframe, used to avoid unneeded searching */ +} AVIndexEntry; + +typedef struct AVStream { + int index; /* stream index in AVFormatContext */ + int id; /* format specific stream id */ + AVCodecContext *codec; /* codec context */ + /** + * real base frame rate of the stream. + * this is the lowest framerate with which all timestamps can be + * represented accurately (its the least common multiple of all + * framerates in the stream), Note, this value is just a guess! + * for example if the timebase is 1/90000 and all frames have either + * approximately 3600 or 1800 timer ticks then r_frame_rate will be 50/1 + */ + AVRational r_frame_rate; + void *priv_data; + /* internal data used in av_find_stream_info() */ + int64_t codec_info_duration; + int codec_info_nb_frames; + /* encoding: PTS generation when outputing stream */ + AVFrac pts; + + /** + * this is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. for fixed-fps content, + * timebase should be 1/framerate and timestamp increments should be + * identically 1. + */ + AVRational time_base; + int pts_wrap_bits; /* number of bits in pts (used for wrapping control) */ + /* ffmpeg.c private use */ + int stream_copy; /* if TRUE, just copy stream */ + enum AVDiscard discard; ///< selects which packets can be discarded at will and dont need to be demuxed + //FIXME move stuff to a flags field? + /* quality, as it has been removed from AVCodecContext and put in AVVideoFrame + * MN:dunno if thats the right place, for it */ + float quality; + /* decoding: position of the first frame of the component, in + AV_TIME_BASE fractional seconds. */ + int64_t start_time; + /* decoding: duration of the stream, in AV_TIME_BASE fractional + seconds. */ + int64_t duration; + + char language[4]; /* ISO 639 3-letter language code (empty string if undefined) */ + + /* av_read_frame() support */ + int need_parsing; ///< 1->full parsing needed, 2->only parse headers dont repack + struct AVCodecParserContext *parser; + + int64_t cur_dts; + int last_IP_duration; + int64_t last_IP_pts; + /* av_seek_frame() support */ + AVIndexEntry *index_entries; /* only used if the format does not + support seeking natively */ + int nb_index_entries; + unsigned int index_entries_allocated_size; + + int64_t nb_frames; ///< number of frames in this stream if known or 0 + +#define MAX_REORDER_DELAY 4 + int64_t pts_buffer[MAX_REORDER_DELAY+1]; +} AVStream; + +#define AVFMTCTX_NOHEADER 0x0001 /* signal that no header is present + (streams are added dynamically) */ + +#define MAX_STREAMS 20 + +/* format I/O context */ +typedef struct AVFormatContext { + const AVClass *av_class; /* set by av_alloc_format_context */ + /* can only be iformat or oformat, not both at the same time */ + struct AVInputFormat *iformat; + struct AVOutputFormat *oformat; + void *priv_data; + ByteIOContext pb; + unsigned int nb_streams; + AVStream *streams[MAX_STREAMS]; + char filename[1024]; /* input or output filename */ + /* stream info */ + int64_t timestamp; + char title[512]; + char author[512]; + char copyright[512]; + char comment[512]; + char album[512]; + int year; /* ID3 year, 0 if none */ + int track; /* track number, 0 if none */ + char genre[32]; /* ID3 genre */ + + int ctx_flags; /* format specific flags, see AVFMTCTX_xx */ + /* private data for pts handling (do not modify directly) */ + /* This buffer is only needed when packets were already buffered but + not decoded, for example to get the codec parameters in mpeg + streams */ + struct AVPacketList *packet_buffer; + + /* decoding: position of the first frame of the component, in + AV_TIME_BASE fractional seconds. NEVER set this value directly: + it is deduced from the AVStream values. */ + int64_t start_time; + /* decoding: duration of the stream, in AV_TIME_BASE fractional + seconds. NEVER set this value directly: it is deduced from the + AVStream values. */ + int64_t duration; + /* decoding: total file size. 0 if unknown */ + int64_t file_size; + /* decoding: total stream bitrate in bit/s, 0 if not + available. Never set it directly if the file_size and the + duration are known as ffmpeg can compute it automatically. */ + int bit_rate; + + /* av_read_frame() support */ + AVStream *cur_st; + const uint8_t *cur_ptr; + int cur_len; + AVPacket cur_pkt; + + /* av_seek_frame() support */ + int64_t data_offset; /* offset of the first packet */ + int index_built; + + int mux_rate; + int packet_size; + int preload; + int max_delay; + +#define AVFMT_NOOUTPUTLOOP -1 +#define AVFMT_INFINITEOUTPUTLOOP 0 + /* number of times to loop output in formats that support it */ + int loop_output; + + int flags; +#define AVFMT_FLAG_GENPTS 0x0001 ///< generate pts if missing even if it requires parsing future frames +#define AVFMT_FLAG_IGNIDX 0x0002 ///< ignore index + + int loop_input; + /* decoding: size of data to probe; encoding unused */ + unsigned int probesize; + + /** + * maximum duration in AV_TIME_BASE units over which the input should be analyzed in av_find_stream_info() + */ + int max_analyze_duration; +} AVFormatContext; + +typedef struct AVPacketList { + AVPacket pkt; + struct AVPacketList *next; +} AVPacketList; + +extern AVInputFormat *first_iformat; +extern AVOutputFormat *first_oformat; + +enum CodecID av_guess_image2_codec(const char *filename); + +/* XXX: use automatic init with either ELF sections or C file parser */ +/* modules */ + +/* utils.c */ +void av_register_input_format(AVInputFormat *format); +void av_register_output_format(AVOutputFormat *format); +AVOutputFormat *guess_stream_format(const char *short_name, + const char *filename, const char *mime_type); +AVOutputFormat *guess_format(const char *short_name, + const char *filename, const char *mime_type); +enum CodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name, + const char *filename, const char *mime_type, enum CodecType type); + +void av_hex_dump(FILE *f, uint8_t *buf, int size); +void av_pkt_dump(FILE *f, AVPacket *pkt, int dump_payload); + +void av_register_all(void); + +/* codec tag <-> codec id */ +enum CodecID av_codec_get_id(const struct AVCodecTag **tags, unsigned int tag); +unsigned int av_codec_get_tag(const struct AVCodecTag **tags, enum CodecID id); + +/* media file input */ +AVInputFormat *av_find_input_format(const char *short_name); +AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened); +int av_open_input_stream(AVFormatContext **ic_ptr, + ByteIOContext *pb, const char *filename, + AVInputFormat *fmt, AVFormatParameters *ap); +int av_open_input_file(AVFormatContext **ic_ptr, const char *filename, + AVInputFormat *fmt, + int buf_size, + AVFormatParameters *ap); +/* no av_open for output, so applications will need this: */ +AVFormatContext *av_alloc_format_context(void); + +#define AVERROR_UNKNOWN (-1) /* unknown error */ +#define AVERROR_IO (-2) /* i/o error */ +#define AVERROR_NUMEXPECTED (-3) /* number syntax expected in filename */ +#define AVERROR_INVALIDDATA (-4) /* invalid data found */ +#define AVERROR_NOMEM (-5) /* not enough memory */ +#define AVERROR_NOFMT (-6) /* unknown format */ +#define AVERROR_NOTSUPP (-7) /* operation not supported */ + +int av_find_stream_info(AVFormatContext *ic); +int av_read_packet(AVFormatContext *s, AVPacket *pkt); +int av_read_frame(AVFormatContext *s, AVPacket *pkt); +int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags); +int av_read_play(AVFormatContext *s); +int av_read_pause(AVFormatContext *s); +void av_close_input_file(AVFormatContext *s); +AVStream *av_new_stream(AVFormatContext *s, int id); +void av_set_pts_info(AVStream *s, int pts_wrap_bits, + int pts_num, int pts_den); + +#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward +#define AVSEEK_FLAG_BYTE 2 ///< seeking based on position in bytes +#define AVSEEK_FLAG_ANY 4 ///< seek to any frame, even non keyframes + +int av_find_default_stream_index(AVFormatContext *s); +int av_index_search_timestamp(AVStream *st, int64_t timestamp, int flags); +int av_add_index_entry(AVStream *st, + int64_t pos, int64_t timestamp, int size, int distance, int flags); +int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts, int flags); +void av_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp); +int64_t av_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, int64_t pos_min, int64_t pos_max, int64_t pos_limit, int64_t ts_min, int64_t ts_max, int flags, int64_t *ts_ret, int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )); + +/* media file output */ +int av_set_parameters(AVFormatContext *s, AVFormatParameters *ap); +int av_write_header(AVFormatContext *s); +int av_write_frame(AVFormatContext *s, AVPacket *pkt); +int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt); +int av_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush); + +int av_write_trailer(AVFormatContext *s); + +void dump_format(AVFormatContext *ic, + int index, + const char *url, + int is_output); +int parse_image_size(int *width_ptr, int *height_ptr, const char *str); +int parse_frame_rate(int *frame_rate, int *frame_rate_base, const char *arg); +int64_t parse_date(const char *datestr, int duration); + +int64_t av_gettime(void); + +/* ffm specific for ffserver */ +#define FFM_PACKET_SIZE 4096 +offset_t ffm_read_write_index(int fd); +void ffm_write_write_index(int fd, offset_t pos); +void ffm_set_write_index(AVFormatContext *s, offset_t pos, offset_t file_size); + +int find_info_tag(char *arg, int arg_size, const char *tag1, const char *info); + +int av_get_frame_filename(char *buf, int buf_size, + const char *path, int number); +int av_filename_number_test(const char *filename); + +/* grab specific */ +int video_grab_init(void); +int audio_init(void); + +/* DV1394 */ +int dv1394_init(void); +int dc1394_init(void); + +#ifdef HAVE_AV_CONFIG_H + +#include "os_support.h" + +int strstart(const char *str, const char *val, const char **ptr); +int stristart(const char *str, const char *val, const char **ptr); +void pstrcpy(char *buf, int buf_size, const char *str); +char *pstrcat(char *buf, int buf_size, const char *s); + +void __dynarray_add(unsigned long **tab_ptr, int *nb_ptr, unsigned long elem); + +#ifdef __GNUC__ +#define dynarray_add(tab, nb_ptr, elem)\ +do {\ + typeof(tab) _tab = (tab);\ + typeof(elem) _elem = (elem);\ + (void)sizeof(**_tab == _elem); /* check that types are compatible */\ + __dynarray_add((unsigned long **)_tab, nb_ptr, (unsigned long)_elem);\ +} while(0) +#else +#define dynarray_add(tab, nb_ptr, elem)\ +do {\ + __dynarray_add((unsigned long **)(tab), nb_ptr, (unsigned long)(elem));\ +} while(0) +#endif + +time_t mktimegm(struct tm *tm); +struct tm *brktimegm(time_t secs, struct tm *tm); +const char *small_strptime(const char *p, const char *fmt, + struct tm *dt); + +struct in_addr; +int resolve_host(struct in_addr *sin_addr, const char *hostname); + +void url_split(char *proto, int proto_size, + char *authorization, int authorization_size, + char *hostname, int hostname_size, + int *port_ptr, + char *path, int path_size, + const char *url); + +int match_ext(const char *filename, const char *extensions); + +#endif /* HAVE_AV_CONFIG_H */ + +#ifdef __cplusplus +} +#endif + +#endif /* AVFORMAT_H */ + diff --git a/extra_lib/include/ffmpeg/avio.h b/extra_lib/include/ffmpeg/avio.h new file mode 100644 index 0000000..f44d526 --- /dev/null +++ b/extra_lib/include/ffmpeg/avio.h @@ -0,0 +1,212 @@ +/* + * unbuffered io for ffmpeg system + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVIO_H +#define AVIO_H + +/* output byte stream handling */ + +typedef int64_t offset_t; + +/* unbuffered I/O */ + +struct URLContext { + struct URLProtocol *prot; + int flags; + int is_streamed; /* true if streamed (no seek possible), default = false */ + int max_packet_size; /* if non zero, the stream is packetized with this max packet size */ + void *priv_data; +#if LIBAVFORMAT_VERSION_INT >= (52<<16) + char *filename; /* specified filename */ +#else + char filename[1]; /* specified filename */ +#endif +}; + +typedef struct URLContext URLContext; + +typedef struct URLPollEntry { + URLContext *handle; + int events; + int revents; +} URLPollEntry; + +#define URL_RDONLY 0 +#define URL_WRONLY 1 +#define URL_RDWR 2 + +typedef int URLInterruptCB(void); + +int url_open(URLContext **h, const char *filename, int flags); +int url_read(URLContext *h, unsigned char *buf, int size); +int url_write(URLContext *h, unsigned char *buf, int size); +offset_t url_seek(URLContext *h, offset_t pos, int whence); +int url_close(URLContext *h); +int url_exist(const char *filename); +offset_t url_filesize(URLContext *h); +int url_get_max_packet_size(URLContext *h); +void url_get_filename(URLContext *h, char *buf, int buf_size); + +/* the callback is called in blocking functions to test regulary if + asynchronous interruption is needed. -EINTR is returned in this + case by the interrupted function. 'NULL' means no interrupt + callback is given. */ +void url_set_interrupt_cb(URLInterruptCB *interrupt_cb); + +/* not implemented */ +int url_poll(URLPollEntry *poll_table, int n, int timeout); + +/** + * passing this as the "whence" parameter to a seek function causes it to + * return the filesize without seeking anywhere, supporting this is optional + * if its not supprted then the seek function will return <0 + */ +#define AVSEEK_SIZE 0x10000 + +typedef struct URLProtocol { + const char *name; + int (*url_open)(URLContext *h, const char *filename, int flags); + int (*url_read)(URLContext *h, unsigned char *buf, int size); + int (*url_write)(URLContext *h, unsigned char *buf, int size); + offset_t (*url_seek)(URLContext *h, offset_t pos, int whence); + int (*url_close)(URLContext *h); + struct URLProtocol *next; +} URLProtocol; + +extern URLProtocol *first_protocol; +extern URLInterruptCB *url_interrupt_cb; + +int register_protocol(URLProtocol *protocol); + +typedef struct { + unsigned char *buffer; + int buffer_size; + unsigned char *buf_ptr, *buf_end; + void *opaque; + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size); + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size); + offset_t (*seek)(void *opaque, offset_t offset, int whence); + offset_t pos; /* position in the file of the current buffer */ + int must_flush; /* true if the next seek should flush */ + int eof_reached; /* true if eof reached */ + int write_flag; /* true if open for writing */ + int is_streamed; + int max_packet_size; + unsigned long checksum; + unsigned char *checksum_ptr; + unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size); + int error; ///< contains the error code or 0 if no error happened +} ByteIOContext; + +int init_put_byte(ByteIOContext *s, + unsigned char *buffer, + int buffer_size, + int write_flag, + void *opaque, + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + offset_t (*seek)(void *opaque, offset_t offset, int whence)); + +void put_byte(ByteIOContext *s, int b); +void put_buffer(ByteIOContext *s, const unsigned char *buf, int size); +void put_le64(ByteIOContext *s, uint64_t val); +void put_be64(ByteIOContext *s, uint64_t val); +void put_le32(ByteIOContext *s, unsigned int val); +void put_be32(ByteIOContext *s, unsigned int val); +void put_le24(ByteIOContext *s, unsigned int val); +void put_be24(ByteIOContext *s, unsigned int val); +void put_le16(ByteIOContext *s, unsigned int val); +void put_be16(ByteIOContext *s, unsigned int val); +void put_tag(ByteIOContext *s, const char *tag); + +void put_strz(ByteIOContext *s, const char *buf); + +offset_t url_fseek(ByteIOContext *s, offset_t offset, int whence); +void url_fskip(ByteIOContext *s, offset_t offset); +offset_t url_ftell(ByteIOContext *s); +offset_t url_fsize(ByteIOContext *s); +int url_feof(ByteIOContext *s); +int url_ferror(ByteIOContext *s); + +#define URL_EOF (-1) +int url_fgetc(ByteIOContext *s); +#ifdef __GNUC__ +int url_fprintf(ByteIOContext *s, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); +#else +int url_fprintf(ByteIOContext *s, const char *fmt, ...); +#endif +char *url_fgets(ByteIOContext *s, char *buf, int buf_size); + +void put_flush_packet(ByteIOContext *s); + +int get_buffer(ByteIOContext *s, unsigned char *buf, int size); +int get_partial_buffer(ByteIOContext *s, unsigned char *buf, int size); +int get_byte(ByteIOContext *s); +unsigned int get_le24(ByteIOContext *s); +unsigned int get_le32(ByteIOContext *s); +uint64_t get_le64(ByteIOContext *s); +unsigned int get_le16(ByteIOContext *s); + +char *get_strz(ByteIOContext *s, char *buf, int maxlen); +unsigned int get_be16(ByteIOContext *s); +unsigned int get_be24(ByteIOContext *s); +unsigned int get_be32(ByteIOContext *s); +uint64_t get_be64(ByteIOContext *s); + +static inline int url_is_streamed(ByteIOContext *s) +{ + return s->is_streamed; +} + +int url_fdopen(ByteIOContext *s, URLContext *h); +int url_setbufsize(ByteIOContext *s, int buf_size); +int url_fopen(ByteIOContext *s, const char *filename, int flags); +int url_fclose(ByteIOContext *s); +URLContext *url_fileno(ByteIOContext *s); +int url_fget_max_packet_size(ByteIOContext *s); + +int url_open_buf(ByteIOContext *s, uint8_t *buf, int buf_size, int flags); +int url_close_buf(ByteIOContext *s); + +int url_open_dyn_buf(ByteIOContext *s); +int url_open_dyn_packet_buf(ByteIOContext *s, int max_packet_size); +int url_close_dyn_buf(ByteIOContext *s, uint8_t **pbuffer); + +unsigned long get_checksum(ByteIOContext *s); +void init_checksum(ByteIOContext *s, unsigned long (*update_checksum)(unsigned long c, const uint8_t *p, unsigned int len), unsigned long checksum); + +/* file.c */ +extern URLProtocol file_protocol; +extern URLProtocol pipe_protocol; + +/* udp.c */ +extern URLProtocol udp_protocol; +int udp_set_remote_url(URLContext *h, const char *uri); +int udp_get_local_port(URLContext *h); +int udp_get_file_handle(URLContext *h); + +/* tcp.c */ +extern URLProtocol tcp_protocol; + +/* http.c */ +extern URLProtocol http_protocol; + +#endif + diff --git a/extra_lib/include/ffmpeg/avutil.h b/extra_lib/include/ffmpeg/avutil.h new file mode 100644 index 0000000..d85755c --- /dev/null +++ b/extra_lib/include/ffmpeg/avutil.h @@ -0,0 +1,137 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_H +#define AVUTIL_H + +/** + * @file avutil.h + * external api header. + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#define AV_STRINGIFY(s) AV_TOSTRING(s) +#define AV_TOSTRING(s) #s + +#define LIBAVUTIL_VERSION_INT ((49<<16)+(3<<8)+0) +#define LIBAVUTIL_VERSION 49.3.0 +#define LIBAVUTIL_BUILD LIBAVUTIL_VERSION_INT + +#define LIBAVUTIL_IDENT "Lavu" AV_STRINGIFY(LIBAVUTIL_VERSION) + + +#include "common.h" +#include "mathematics.h" +#include "rational.h" +#include "integer.h" +#include "intfloat_readwrite.h" +#include "log.h" + +/** + * Pixel format. Notes: + * + * PIX_FMT_RGB32 is handled in an endian-specific manner. A RGBA + * color is put together as: + * (A << 24) | (R << 16) | (G << 8) | B + * This is stored as BGRA on little endian CPU architectures and ARGB on + * big endian CPUs. + * + * When the pixel format is palettized RGB (PIX_FMT_PAL8), the palettized + * image data is stored in AVFrame.data[0]. The palette is transported in + * AVFrame.data[1] and, is 1024 bytes long (256 4-byte entries) and is + * formatted the same as in PIX_FMT_RGB32 described above (i.e., it is + * also endian-specific). Note also that the individual RGB palette + * components stored in AVFrame.data[1] should be in the range 0..255. + * This is important as many custom PAL8 video codecs that were designed + * to run on the IBM VGA graphics adapter use 6-bit palette components. + */ +enum PixelFormat { + PIX_FMT_NONE= -1, + PIX_FMT_YUV420P, ///< Planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) + PIX_FMT_YUYV422, ///< Packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr + PIX_FMT_RGB24, ///< Packed RGB 8:8:8, 24bpp, RGBRGB... + PIX_FMT_BGR24, ///< Packed RGB 8:8:8, 24bpp, BGRBGR... + PIX_FMT_YUV422P, ///< Planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) + PIX_FMT_YUV444P, ///< Planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) + PIX_FMT_RGB32, ///< Packed RGB 8:8:8, 32bpp, (msb)8A 8R 8G 8B(lsb), in cpu endianness + PIX_FMT_YUV410P, ///< Planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) + PIX_FMT_YUV411P, ///< Planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) + PIX_FMT_RGB565, ///< Packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), in cpu endianness + PIX_FMT_RGB555, ///< Packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), in cpu endianness most significant bit to 0 + PIX_FMT_GRAY8, ///< Y , 8bpp + PIX_FMT_MONOWHITE, ///< Y , 1bpp, 1 is white + PIX_FMT_MONOBLACK, ///< Y , 1bpp, 0 is black + PIX_FMT_PAL8, ///< 8 bit with PIX_FMT_RGB32 palette + PIX_FMT_YUVJ420P, ///< Planar YUV 4:2:0, 12bpp, full scale (jpeg) + PIX_FMT_YUVJ422P, ///< Planar YUV 4:2:2, 16bpp, full scale (jpeg) + PIX_FMT_YUVJ444P, ///< Planar YUV 4:4:4, 24bpp, full scale (jpeg) + PIX_FMT_XVMC_MPEG2_MC,///< XVideo Motion Acceleration via common packet passing(xvmc_render.h) + PIX_FMT_XVMC_MPEG2_IDCT, + PIX_FMT_UYVY422, ///< Packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 + PIX_FMT_UYYVYY411, ///< Packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 + PIX_FMT_BGR32, ///< Packed RGB 8:8:8, 32bpp, (msb)8A 8B 8G 8R(lsb), in cpu endianness + PIX_FMT_BGR565, ///< Packed RGB 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), in cpu endianness + PIX_FMT_BGR555, ///< Packed RGB 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), in cpu endianness most significant bit to 1 + PIX_FMT_BGR8, ///< Packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) + PIX_FMT_BGR4, ///< Packed RGB 1:2:1, 4bpp, (msb)1B 2G 1R(lsb) + PIX_FMT_BGR4_BYTE, ///< Packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) + PIX_FMT_RGB8, ///< Packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) + PIX_FMT_RGB4, ///< Packed RGB 1:2:1, 4bpp, (msb)2R 3G 3B(lsb) + PIX_FMT_RGB4_BYTE, ///< Packed RGB 1:2:1, 8bpp, (msb)2R 3G 3B(lsb) + PIX_FMT_NV12, ///< Planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 for UV + PIX_FMT_NV21, ///< as above, but U and V bytes are swapped + + PIX_FMT_RGB32_1, ///< Packed RGB 8:8:8, 32bpp, (msb)8R 8G 8B 8A(lsb), in cpu endianness + PIX_FMT_BGR32_1, ///< Packed RGB 8:8:8, 32bpp, (msb)8B 8G 8R 8A(lsb), in cpu endianness + + PIX_FMT_GRAY16BE, ///< Y , 16bpp, big-endian + PIX_FMT_GRAY16LE, ///< Y , 16bpp, little-endian + PIX_FMT_NB, ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions +}; + +#ifdef WORDS_BIGENDIAN +#define PIX_FMT_RGBA PIX_FMT_RGB32_1 +#define PIX_FMT_BGRA PIX_FMT_BGR32_1 +#define PIX_FMT_ARGB PIX_FMT_RGB32 +#define PIX_FMT_ABGR PIX_FMT_BGR32 +#define PIX_FMT_GRAY16 PIX_FMT_GRAY16BE +#else +#define PIX_FMT_RGBA PIX_FMT_BGR32 +#define PIX_FMT_BGRA PIX_FMT_RGB32 +#define PIX_FMT_ARGB PIX_FMT_BGR32_1 +#define PIX_FMT_ABGR PIX_FMT_RGB32_1 +#define PIX_FMT_GRAY16 PIX_FMT_GRAY16LE +#endif + +#if LIBAVUTIL_VERSION_INT < (50<<16) +#define PIX_FMT_UYVY411 PIX_FMT_UYYVYY411 +#define PIX_FMT_RGBA32 PIX_FMT_RGB32 +#define PIX_FMT_YUV422 PIX_FMT_YUYV422 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AVUTIL_H */ diff --git a/extra_lib/include/ffmpeg/common.h b/extra_lib/include/ffmpeg/common.h new file mode 100644 index 0000000..d75bb5f --- /dev/null +++ b/extra_lib/include/ffmpeg/common.h @@ -0,0 +1,408 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file common.h + * common internal and external api header. + */ + +#ifndef COMMON_H +#define COMMON_H + +#if (defined(WIN32) || defined(_WIN32_WCE) ) && !defined(__MINGW32__) && !defined(__CYGWIN__) +# define CONFIG_MSVC +#endif + +/*THIS CONFIG IS FOR WINCE ONLY!!*/ +#ifdef CONFIG_MSVC +#define CONFIG_WIN32 +#define EMULATE_INTTYPES +#define CONFIG_ALIGN +#define inline __inline + +#ifdef _WIN32_WCE +#define perror(n) +#endif + +#elif defined(__SYMBIAN32__) +#define EMULATE_INTTYPES +#endif + +#ifdef HAVE_AV_CONFIG_H +/* only include the following when compiling package */ +# include "config.h" + +# include +# include +# include +# include +# include +# ifndef _WIN32_WCE +# ifndef __BEOS__ +# include +# else +# include "berrno.h" +# endif +# endif +# include +#endif /* HAVE_AV_CONFIG_H */ + +#ifndef EMULATE_INTTYPES +# include +#else + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + +# ifdef CONFIG_WIN32 + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; +# else /* other OS */ + typedef signed long long int64_t; + typedef unsigned long long uint64_t; +# endif /* other OS */ +#endif /* HAVE_INTTYPES_H */ + + +#ifndef av_always_inline +#if defined(__GNUC__) && (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ > 0) +# define av_always_inline __attribute__((always_inline)) inline +#else +# define av_always_inline inline +#endif +#endif + +#ifdef HAVE_AV_CONFIG_H +# include "internal.h" +#endif /* HAVE_AV_CONFIG_H */ + +#ifndef attribute_deprecated +#if defined(__GNUC__) && (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ > 0) +# define attribute_deprecated __attribute__((deprecated)) +#else +# define attribute_deprecated +#endif +#endif + +#ifndef INT64_C +# ifdef CONFIG_MSVC +# define INT64_C(x) (x ## i64) +# define UINT64_C(x) (x ## Ui64) +# else +# define INT64_C(x) (x ## LL) +# define UINT64_C(x) (x ## ULL) +# endif +#endif + +//rounded divison & shift +#define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b)) +/* assume b>0 */ +#define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) +#define FFABS(a) ((a) >= 0 ? (a) : (-(a))) +#define FFSIGN(a) ((a) > 0 ? 1 : -1) + +#define FFMAX(a,b) ((a) > (b) ? (a) : (b)) +#define FFMIN(a,b) ((a) > (b) ? (b) : (a)) + +#define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0) + +/* misc math functions */ +extern const uint8_t ff_log2_tab[256]; + +static inline int av_log2(unsigned int v) +{ + int n; + + n = 0; + if (v & 0xffff0000) { + v >>= 16; + n += 16; + } + if (v & 0xff00) { + v >>= 8; + n += 8; + } + n += ff_log2_tab[v]; + + return n; +} + +static inline int av_log2_16bit(unsigned int v) +{ + int n; + + n = 0; + if (v & 0xff00) { + v >>= 8; + n += 8; + } + n += ff_log2_tab[v]; + + return n; +} + +/* median of 3 */ +static inline int mid_pred(int a, int b, int c) +{ +#ifdef HAVE_CMOV + int i=b; + asm volatile( + "cmp %2, %1 \n\t" + "cmovg %1, %0 \n\t" + "cmovg %2, %1 \n\t" + "cmp %3, %1 \n\t" + "cmovl %3, %1 \n\t" + "cmp %1, %0 \n\t" + "cmovg %1, %0 \n\t" + :"+&r"(i), "+&r"(a) + :"r"(b), "r"(c) + ); + return i; +#elif 0 + int t= (a-b)&((a-b)>>31); + a-=t; + b+=t; + b-= (b-c)&((b-c)>>31); + b+= (a-b)&((a-b)>>31); + + return b; +#else + if(a>b){ + if(c>b){ + if(c>a) b=a; + else b=c; + } + }else{ + if(b>c){ + if(c>a) b=c; + else b=a; + } + } + return b; +#endif +} + +/** + * clip a signed integer value into the amin-amax range + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static inline int clip(int a, int amin, int amax) +{ + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** + * clip a signed integer value into the 0-255 range + * @param a value to clip + * @return clipped value + */ +static inline uint8_t clip_uint8(int a) +{ + if (a&(~255)) return (-a)>>31; + else return a; +} + +/* math */ +int64_t ff_gcd(int64_t a, int64_t b); + +/** + * converts fourcc string to int + */ +static inline int ff_get_fourcc(const char *s){ +#ifdef HAVE_AV_CONFIG_H + assert( strlen(s)==4 ); +#endif + + return (s[0]) + (s[1]<<8) + (s[2]<<16) + (s[3]<<24); +} + +#define MKTAG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24)) +#define MKBETAG(a,b,c,d) (d | (c << 8) | (b << 16) | (a << 24)) + +/*! + * \def GET_UTF8(val, GET_BYTE, ERROR) + * converts a utf-8 character (up to 4 bytes long) to its 32-bit ucs-4 encoded form + * \param val is the output and should be of type uint32_t. It holds the converted + * ucs-4 character and should be a left value. + * \param GET_BYTE gets utf-8 encoded bytes from any proper source. It can be + * a function or a statement whose return value or evaluated value is of type + * uint8_t. It will be executed up to 4 times for values in the valid utf-8 range, + * and up to 7 times in the general case. + * \param ERROR action that should be taken when an invalid utf-8 byte is returned + * from GET_BYTE. It should be a statement that jumps out of the macro, + * like exit(), goto, return, break, or continue. + */ +#define GET_UTF8(val, GET_BYTE, ERROR)\ + val= GET_BYTE;\ + {\ + int ones= 7 - av_log2(val ^ 255);\ + if(ones==1)\ + ERROR\ + val&= 127>>ones;\ + while(--ones > 0){\ + int tmp= GET_BYTE - 128;\ + if(tmp>>6)\ + ERROR\ + val= (val<<6) + tmp;\ + }\ + } + +/*! + * \def PUT_UTF8(val, tmp, PUT_BYTE) + * converts a 32-bit unicode character to its utf-8 encoded form (up to 4 bytes long). + * \param val is an input only argument and should be of type uint32_t. It holds + * a ucs4 encoded unicode character that is to be converted to utf-8. If + * val is given as a function it's executed only once. + * \param tmp is a temporary variable and should be of type uint8_t. It + * represents an intermediate value during conversion that is to be + * outputted by PUT_BYTE. + * \param PUT_BYTE writes the converted utf-8 bytes to any proper destination. + * It could be a function or a statement, and uses tmp as the input byte. + * For example, PUT_BYTE could be "*output++ = tmp;" PUT_BYTE will be + * executed up to 4 times for values in the valid utf-8 range and up to + * 7 times in the general case, depending on the length of the converted + * unicode character. + */ +#define PUT_UTF8(val, tmp, PUT_BYTE)\ + {\ + int bytes, shift;\ + uint32_t in = val;\ + if (in < 0x80) {\ + tmp = in;\ + PUT_BYTE\ + } else {\ + bytes = (av_log2(in) + 4) / 5;\ + shift = (bytes - 1) * 6;\ + tmp = (256 - (256 >> bytes)) | (in >> shift);\ + PUT_BYTE\ + while (shift >= 6) {\ + shift -= 6;\ + tmp = 0x80 | ((in >> shift) & 0x3f);\ + PUT_BYTE\ + }\ + }\ + } + +#if defined(ARCH_X86) || defined(ARCH_POWERPC) +#if defined(ARCH_X86_64) +static inline uint64_t read_time(void) +{ + uint64_t a, d; + asm volatile( "rdtsc\n\t" + : "=a" (a), "=d" (d) + ); + return (d << 32) | (a & 0xffffffff); +} +#elif defined(ARCH_X86_32) +static inline long long read_time(void) +{ + long long l; + asm volatile( "rdtsc\n\t" + : "=A" (l) + ); + return l; +} +#else //FIXME check ppc64 +static inline uint64_t read_time(void) +{ + uint32_t tbu, tbl, temp; + + /* from section 2.2.1 of the 32-bit PowerPC PEM */ + __asm__ __volatile__( + "1:\n" + "mftbu %2\n" + "mftb %0\n" + "mftbu %1\n" + "cmpw %2,%1\n" + "bne 1b\n" + : "=r"(tbl), "=r"(tbu), "=r"(temp) + : + : "cc"); + + return (((uint64_t)tbu)<<32) | (uint64_t)tbl; +} +#endif + +#define START_TIMER \ +uint64_t tend;\ +uint64_t tstart= read_time();\ + +#define STOP_TIMER(id) \ +tend= read_time();\ +{\ + static uint64_t tsum=0;\ + static int tcount=0;\ + static int tskip_count=0;\ + if(tcount<2 || tend - tstart < 8*tsum/tcount){\ + tsum+= tend - tstart;\ + tcount++;\ + }else\ + tskip_count++;\ + if(((tcount+tskip_count)&(tcount+tskip_count-1))==0){\ + av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" dezicycles in %s, %d runs, %d skips\n", tsum*10/tcount, id, tcount, tskip_count);\ + }\ +} +#else +#define START_TIMER +#define STOP_TIMER(id) {} +#endif + +/* memory */ + +#ifdef __GNUC__ + #define DECLARE_ALIGNED(n,t,v) t v __attribute__ ((aligned (n))) +#else + #define DECLARE_ALIGNED(n,t,v) __declspec(align(n)) t v +#endif + +/* memory */ +void *av_malloc(unsigned int size); +void *av_realloc(void *ptr, unsigned int size); +void av_free(void *ptr); + +void *av_mallocz(unsigned int size); +char *av_strdup(const char *s); +void av_freep(void *ptr); + +#ifndef restrict +# define restrict +#endif + +#if defined(CONFIG_MSVC) || defined(__SYMBIAN32__) +typedef int64_t int_fast64_t; +typedef uint64_t uint_fast64_t; +typedef int32_t int_fast32_t; +typedef uint32_t uint_fast32_t; +typedef int16_t int_fast16_t; +typedef uint16_t uint_fast16_t; +typedef int8_t int_fast8_t; +typedef uint8_t uint_fast8_t; +#endif + +#endif /* COMMON_H */ + diff --git a/extra_lib/include/ffmpeg/integer.h b/extra_lib/include/ffmpeg/integer.h new file mode 100644 index 0000000..a50ad9b --- /dev/null +++ b/extra_lib/include/ffmpeg/integer.h @@ -0,0 +1,49 @@ +/* + * arbitrary precision integers + * Copyright (c) 2004 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/** + * @file integer.h + * arbitrary precision integers + * @author Michael Niedermayer + */ + +#ifndef INTEGER_H +#define INTEGER_H + +#define AV_INTEGER_SIZE 8 + +typedef struct AVInteger{ + uint16_t v[AV_INTEGER_SIZE]; +} AVInteger; + +AVInteger av_add_i(AVInteger a, AVInteger b); +AVInteger av_sub_i(AVInteger a, AVInteger b); +int av_log2_i(AVInteger a); +AVInteger av_mul_i(AVInteger a, AVInteger b); +int av_cmp_i(AVInteger a, AVInteger b); +AVInteger av_shr_i(AVInteger a, int s); +AVInteger av_mod_i(AVInteger *quot, AVInteger a, AVInteger b); +AVInteger av_div_i(AVInteger a, AVInteger b); +AVInteger av_int2i(int64_t a); +int64_t av_i2int(AVInteger a); + +#endif // INTEGER_H diff --git a/extra_lib/include/ffmpeg/intfloat_readwrite.h b/extra_lib/include/ffmpeg/intfloat_readwrite.h new file mode 100644 index 0000000..c535b64 --- /dev/null +++ b/extra_lib/include/ffmpeg/intfloat_readwrite.h @@ -0,0 +1,39 @@ +/* + * copyright (c) 2005 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INTFLOAT_READWRITE_H +#define INTFLOAT_READWRITE_H + +#include "common.h" + +/* IEEE 80 bits extended float */ +typedef struct AVExtFloat { + uint8_t exponent[2]; + uint8_t mantissa[8]; +} AVExtFloat; + +double av_int2dbl(int64_t v); +float av_int2flt(int32_t v); +double av_ext2dbl(const AVExtFloat ext); +int64_t av_dbl2int(double d); +int32_t av_flt2int(float d); +AVExtFloat av_dbl2ext(double d); + +#endif /* INTFLOAT_READWRITE_H */ diff --git a/extra_lib/include/ffmpeg/log.h b/extra_lib/include/ffmpeg/log.h new file mode 100644 index 0000000..56ca012 --- /dev/null +++ b/extra_lib/include/ffmpeg/log.h @@ -0,0 +1,102 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LOG_H +#define LOG_H + +#include + +/** + * Used by av_log + */ +typedef struct AVCLASS AVClass; +struct AVCLASS { + const char* class_name; + const char* (*item_name)(void*); /* actually passing a pointer to an AVCodecContext + or AVFormatContext, which begin with an AVClass. + Needed because av_log is in libavcodec and has no visibility + of AVIn/OutputFormat */ + const struct AVOption *option; +}; + +/* av_log API */ + +#if LIBAVUTIL_VERSION_INT < (50<<16) +#define AV_LOG_QUIET -1 +#define AV_LOG_FATAL 0 +#define AV_LOG_ERROR 0 +#define AV_LOG_WARNING 1 +#define AV_LOG_INFO 1 +#define AV_LOG_VERBOSE 1 +#define AV_LOG_DEBUG 2 +#else +#define AV_LOG_QUIET -8 + +/** + * something went really wrong and we will crash now + */ +#define AV_LOG_PANIC 0 + +/** + * something went wrong and recovery is not possible + * like no header in a format which depends on it or a combination + * of parameters which are not allowed + */ +#define AV_LOG_FATAL 8 + +/** + * something went wrong and cannot losslessly be recovered + * but not all future data is affected + */ +#define AV_LOG_ERROR 16 + +/** + * something somehow does not look correct / something which may or may not + * lead to some problems like use of -vstrict -2 + */ +#define AV_LOG_WARNING 24 + +#define AV_LOG_INFO 32 +#define AV_LOG_VERBOSE 40 + +/** + * stuff which is only useful for libav* developers + */ +#define AV_LOG_DEBUG 48 +#endif +extern int av_log_level; + +#ifdef __GNUC__ +extern void av_log(void*, int level, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); +#else +extern void av_log(void*, int level, const char *fmt, ...); +#endif + +#if LIBAVUTIL_VERSION_INT < (50<<16) +extern void av_vlog(void*, int level, const char *fmt, va_list); +extern int av_log_get_level(void); +extern void av_log_set_level(int); +extern void av_log_set_callback(void (*)(void*, int, const char*, va_list)); +extern void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl); +#else +extern void (*av_vlog)(void*, int, const char*, va_list); +#endif + +#endif /* LOG_H */ diff --git a/extra_lib/include/ffmpeg/mathematics.h b/extra_lib/include/ffmpeg/mathematics.h new file mode 100644 index 0000000..0b74b25 --- /dev/null +++ b/extra_lib/include/ffmpeg/mathematics.h @@ -0,0 +1,51 @@ +/* + * copyright (c) 2005 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MATHEMATICS_H +#define MATHEMATICS_H + +#include "rational.h" + +enum AVRounding { + AV_ROUND_ZERO = 0, ///< round toward zero + AV_ROUND_INF = 1, ///< round away from zero + AV_ROUND_DOWN = 2, ///< round toward -infinity + AV_ROUND_UP = 3, ///< round toward +infinity + AV_ROUND_NEAR_INF = 5, ///< round to nearest and halfway cases away from zero +}; + +/** + * rescale a 64bit integer with rounding to nearest. + * a simple a*b/c isn't possible as it can overflow + */ +int64_t av_rescale(int64_t a, int64_t b, int64_t c); + +/** + * rescale a 64bit integer with specified rounding. + * a simple a*b/c isn't possible as it can overflow + */ +int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding); + +/** + * rescale a 64bit integer by 2 rational numbers. + */ +int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq); + +#endif /* MATHEMATICS_H */ diff --git a/extra_lib/include/ffmpeg/rational.h b/extra_lib/include/ffmpeg/rational.h new file mode 100644 index 0000000..1bbfd13 --- /dev/null +++ b/extra_lib/include/ffmpeg/rational.h @@ -0,0 +1,79 @@ +/* + * Rational numbers + * Copyright (c) 2003 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/** + * @file rational.h + * Rational numbers. + * @author Michael Niedermayer + */ + +#ifndef RATIONAL_H +#define RATIONAL_H + +/** + * Rational number num/den. + */ +typedef struct AVRational{ + int num; ///< numerator + int den; ///< denominator +} AVRational; + +static inline AVRational _AVRational(int num,int den) +{ + AVRational r; + r.num = num; + r.den = den; + return r; +} + +/** + * returns 0 if a==b, 1 if a>b and -1 if a>63)|1; + else return 0; +} + +/** + * converts the given AVRational to a double. + */ +static inline double av_q2d(AVRational a){ + return a.num / (double) a.den; +} + +/** + * reduce a fraction. + * this is usefull for framerate calculations + * @param max the maximum allowed for dst_nom & dst_den + * @return 1 if exact, 0 otherwise + */ +int av_reduce(int *dst_nom, int *dst_den, int64_t nom, int64_t den, int64_t max); + +AVRational av_mul_q(AVRational b, AVRational c); +AVRational av_div_q(AVRational b, AVRational c); +AVRational av_add_q(AVRational b, AVRational c); +AVRational av_sub_q(AVRational b, AVRational c); +AVRational av_d2q(double d, int max); + +#endif // RATIONAL_H diff --git a/extra_lib/include/freetype/freetype/cache/ftccache.h b/extra_lib/include/freetype/freetype/cache/ftccache.h new file mode 100644 index 0000000..701b13e --- /dev/null +++ b/extra_lib/include/freetype/freetype/cache/ftccache.h @@ -0,0 +1,304 @@ +/***************************************************************************/ +/* */ +/* ftccache.h */ +/* */ +/* FreeType internal cache interface (specification). */ +/* */ +/* Copyright 2000-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTCCACHE_H__ +#define __FTCCACHE_H__ + + +/* define to allow cache lookup inlining */ +#define FTC_CACHE_USE_INLINE + + +FT_BEGIN_HEADER + + /* handle to cache object */ + typedef struct FTC_CacheRec_* FTC_Cache; + + /* handle to cache class */ + typedef const struct FTC_Cache_ClassRec_* FTC_Cache_Class; + + /* handle to cache node family */ + typedef struct FTC_FamilyRec_* FTC_Family; + + /* handle to cache root query */ + typedef struct FTC_QueryRec_* FTC_Query; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE NODE DEFINITIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* Each cache controls one or more cache nodes. Each node is part of */ + /* the global_lru list of the manager. Its `data' field however is used */ + /* as a reference count for now. */ + /* */ + /* A node can be anything, depending on the type of information held by */ + /* the cache. It can be an individual glyph image, a set of bitmaps */ + /* glyphs for a given size, some metrics, etc. */ + /* */ + /*************************************************************************/ + + /* structure size should be 20 bytes on 32-bits machines */ + typedef struct FTC_NodeRec_ + { + FTC_Node mru_next; /* circular mru list pointer */ + FTC_Node mru_prev; /* circular mru list pointer */ + FTC_Node link; /* used for hashing */ + FT_UInt32 hash; /* used for hashing too */ + FT_UShort fam_index; /* index of family the node belongs to */ + FT_Short ref_count; /* reference count for this node */ + + } FTC_NodeRec; + + +#define FTC_NODE( x ) ( (FTC_Node)(x) ) +#define FTC_NODE_P( x ) ( (FTC_Node*)(x) ) + + + /*************************************************************************/ + /* */ + /* These functions are exported so that they can be called from */ + /* user-provided cache classes; otherwise, they are really part of the */ + /* cache sub-system internals. */ + /* */ + + /* can be used as a FTC_Node_DoneFunc */ + FT_EXPORT( void ) + ftc_node_done( FTC_Node node, + FTC_Cache cache ); + + /* reserved for manager's use */ + FT_EXPORT( void ) + ftc_node_destroy( FTC_Node node, + FTC_Manager manager ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE QUERY DEFINITIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* A structure modelling a cache node query. The following fields must */ + /* all be set by the @FTC_Family_CompareFunc method of a cache's family */ + /* list. */ + /* */ + typedef struct FTC_QueryRec_ + { + FTC_Family family; + FT_UFast hash; + + } FTC_QueryRec; + + +#define FTC_QUERY( x ) ( (FTC_Query)(x) ) +#define FTC_QUERY_P( x ) ( (FTC_Query*)(x) ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE FAMILY DEFINITIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct FTC_FamilyRec_ + { + FT_LruNodeRec lru; + FTC_Cache cache; + FT_UInt num_nodes; + FT_UInt fam_index; + + } FTC_FamilyRec; + + +#define FTC_FAMILY( x ) ( (FTC_Family)(x) ) +#define FTC_FAMILY_P( x ) ( (FTC_Family*)(x) ) + + + /*************************************************************************/ + /* */ + /* These functions are exported so that they can be called from */ + /* user-provided cache classes; otherwise, they are really part of the */ + /* cache sub-system internals. */ + /* */ + + /* must be called by any FTC_Node_InitFunc routine */ + FT_EXPORT( FT_Error ) + ftc_family_init( FTC_Family family, + FTC_Query query, + FTC_Cache cache ); + + + /* can be used as a FTC_Family_DoneFunc; otherwise, must be called */ + /* by any family finalizer function */ + FT_EXPORT( void ) + ftc_family_done( FTC_Family family ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE DEFINITIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* each cache really implements a dynamic hash table to manage its nodes */ + typedef struct FTC_CacheRec_ + { + FTC_Manager manager; + FT_Memory memory; + FTC_Cache_Class clazz; + + FT_UInt cache_index; /* in manager's table */ + FT_Pointer cache_data; /* used by cache node methods */ + + FT_UFast p; + FT_UFast mask; + FT_Long slack; + FTC_Node* buckets; + + FT_LruList_ClassRec family_class; + FT_LruList families; + + } FTC_CacheRec; + + +#define FTC_CACHE( x ) ( (FTC_Cache)(x) ) +#define FTC_CACHE_P( x ) ( (FTC_Cache*)(x) ) + + + /* initialize a given cache */ + typedef FT_Error + (*FTC_Cache_InitFunc)( FTC_Cache cache ); + + /* clear a cache */ + typedef void + (*FTC_Cache_ClearFunc)( FTC_Cache cache ); + + /* finalize a given cache */ + typedef void + (*FTC_Cache_DoneFunc)( FTC_Cache cache ); + + + typedef FT_Error + (*FTC_Family_InitFunc)( FTC_Family family, + FTC_Query query, + FTC_Cache cache ); + + typedef FT_Int + (*FTC_Family_CompareFunc)( FTC_Family family, + FTC_Query query ); + + typedef void + (*FTC_Family_DoneFunc)( FTC_Family family, + FTC_Cache cache ); + + /* initialize a new cache node */ + typedef FT_Error + (*FTC_Node_InitFunc)( FTC_Node node, + FT_Pointer type, + FTC_Cache cache ); + + /* compute the weight of a given cache node */ + typedef FT_ULong + (*FTC_Node_WeightFunc)( FTC_Node node, + FTC_Cache cache ); + + /* compare a node to a given key pair */ + typedef FT_Bool + (*FTC_Node_CompareFunc)( FTC_Node node, + FT_Pointer key, + FTC_Cache cache ); + + /* finalize a given cache node */ + typedef void + (*FTC_Node_DoneFunc)( FTC_Node node, + FTC_Cache cache ); + + + typedef struct FTC_Cache_ClassRec_ + { + FT_UInt cache_size; + FTC_Cache_InitFunc cache_init; + FTC_Cache_ClearFunc cache_clear; + FTC_Cache_DoneFunc cache_done; + + FT_UInt family_size; + FTC_Family_InitFunc family_init; + FTC_Family_CompareFunc family_compare; + FTC_Family_DoneFunc family_done; + + FT_UInt node_size; + FTC_Node_InitFunc node_init; + FTC_Node_WeightFunc node_weight; + FTC_Node_CompareFunc node_compare; + FTC_Node_DoneFunc node_done; + + } FTC_Cache_ClassRec; + + + /* */ + + + /*************************************************************************/ + /* */ + /* These functions are exported so that they can be called from */ + /* user-provided cache classes; otherwise, they are really part of the */ + /* cache sub-system internals. */ + /* */ + + /* can be used directly as FTC_Cache_DoneFunc(), or called by custom */ + /* cache finalizers */ + FT_EXPORT( void ) + ftc_cache_done( FTC_Cache cache ); + + /* can be used directly as FTC_Cache_ClearFunc(), or called by custom */ + /* cache clear routines */ + FT_EXPORT( void ) + ftc_cache_clear( FTC_Cache cache ); + + /* initalize the hash table within the cache */ + FT_EXPORT( FT_Error ) + ftc_cache_init( FTC_Cache cache ); + + /* can be called when the key's hash value has been computed */ + FT_EXPORT( FT_Error ) + ftc_cache_lookup( FTC_Cache cache, + FTC_Query query, + FTC_Node *anode ); + + /* */ + +FT_END_HEADER + + +#endif /* __FTCCACHE_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/cache/ftccmap.h b/extra_lib/include/freetype/freetype/cache/ftccmap.h new file mode 100644 index 0000000..a41989b --- /dev/null +++ b/extra_lib/include/freetype/freetype/cache/ftccmap.h @@ -0,0 +1,216 @@ +/***************************************************************************/ +/* */ +/* ftccmap.h */ +/* */ +/* FreeType charmap cache (specification). */ +/* */ +/* Copyright 2000-2001, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTCCMAP_H__ +#define __FTCCMAP_H__ + +#include +#include FT_CACHE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /*
    */ + /* cache_subsystem */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* @type: */ + /* FTC_CMapCache */ + /* */ + /* @description: */ + /* An opaque handle used to manager a charmap cache. This cache is */ + /* to hold character codes -> glyph indices mappings. */ + /* */ + typedef struct FTC_CMapCacheRec_* FTC_CMapCache; + + + /*************************************************************************/ + /* */ + /* @type: */ + /* FTC_CMapDesc */ + /* */ + /* @description: */ + /* A handle to an @FTC_CMapDescRec structure used to describe a given */ + /* charmap in a charmap cache. */ + /* */ + /* Each @FTC_CMapDesc describes which charmap (of which @FTC_FaceID) */ + /* we want to use in @FTC_CMapCache_Lookup. */ + /* */ + typedef struct FTC_CMapDescRec_* FTC_CMapDesc; + + + /*************************************************************************/ + /* */ + /* @enum: */ + /* FTC_CMapType */ + /* */ + /* @description: */ + /* The list of valid @FTC_CMapDesc types. They indicate how we want */ + /* to address a charmap within an @FTC_FaceID. */ + /* */ + /* @values: */ + /* FTC_CMAP_BY_INDEX :: */ + /* Address a charmap by its index in the corresponding @FT_Face. */ + /* */ + /* FTC_CMAP_BY_ENCODING :: */ + /* Use a @FT_Face charmap that corresponds to a given encoding. */ + /* */ + /* FTC_CMAP_BY_ID :: */ + /* Use an @FT_Face charmap that corresponds to a given */ + /* (platform,encoding) ID. See @FTC_CMapIdRec. */ + /* */ + typedef enum FTC_CMapType_ + { + FTC_CMAP_BY_INDEX = 0, + FTC_CMAP_BY_ENCODING = 1, + FTC_CMAP_BY_ID = 2 + + } FTC_CMapType; + + + /*************************************************************************/ + /* */ + /* @struct: */ + /* FTC_CMapIdRec */ + /* */ + /* @description: */ + /* A short structure to identify a charmap by a (platform,encoding) */ + /* pair of values. */ + /* */ + /* @fields: */ + /* platform :: The platform ID. */ + /* */ + /* encoding :: The encoding ID. */ + /* */ + typedef struct FTC_CMapIdRec_ + { + FT_UInt platform; + FT_UInt encoding; + + } FTC_CMapIdRec; + + + /*************************************************************************/ + /* */ + /* @struct: */ + /* FTC_CMapDescRec */ + /* */ + /* @description: */ + /* A structure to describe a given charmap to @FTC_CMapCache. */ + /* */ + /* @fields: */ + /* face_id :: @FTC_FaceID of the face this charmap belongs to. */ + /* */ + /* type :: The type of charmap, see @FTC_CMapType. */ + /* */ + /* u.index :: For @FTC_CMAP_BY_INDEX types, this is the charmap */ + /* index (within a @FT_Face) we want to use. */ + /* */ + /* u.encoding :: For @FTC_CMAP_BY_ENCODING types, this is the charmap */ + /* encoding we want to use. see @FT_Encoding. */ + /* */ + /* u.id :: For @FTC_CMAP_BY_ID types, this is the */ + /* (platform,encoding) pair we want to use. see */ + /* @FTC_CMapIdRec and @FT_CharMapRec. */ + /* */ + typedef struct FTC_CMapDescRec_ + { + FTC_FaceID face_id; + FTC_CMapType type; + + union + { + FT_UInt index; + FT_Encoding encoding; + FTC_CMapIdRec id; + + } u; + + } FTC_CMapDescRec; + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FTC_CMapCache_New */ + /* */ + /* @description: */ + /* Creates a new charmap cache. */ + /* */ + /* @input: */ + /* manager :: A handle to the cache manager. */ + /* */ + /* @output: */ + /* acache :: A new cache handle. NULL in case of error. */ + /* */ + /* @return: */ + /* FreeType error code. 0 means success. */ + /* */ + /* @note: */ + /* Like all other caches, this one will be destroyed with the cache */ + /* manager. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_CMapCache_New( FTC_Manager manager, + FTC_CMapCache *acache ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FTC_CMapCache_Lookup */ + /* */ + /* @description: */ + /* Translates a character code into a glyph index, using the charmap */ + /* cache. */ + /* */ + /* @input: */ + /* cache :: A charmap cache handle. */ + /* */ + /* cmap_desc :: A charmap descriptor handle. */ + /* */ + /* char_code :: The character code (in the corresponding charmap). */ + /* */ + /* @return: */ + /* Glyph index. 0 means "no glyph". */ + /* */ + /* @note: */ + /* This function doesn't return @FTC_Node handles, since there is no */ + /* real use for them with typical uses of charmaps. */ + /* */ + FT_EXPORT( FT_UInt ) + FTC_CMapCache_Lookup( FTC_CMapCache cache, + FTC_CMapDesc cmap_desc, + FT_UInt32 char_code ); + + /* */ + + +FT_END_HEADER + + +#endif /* __FTCCMAP_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/cache/ftcglyph.h b/extra_lib/include/freetype/freetype/cache/ftcglyph.h new file mode 100644 index 0000000..ee72c33 --- /dev/null +++ b/extra_lib/include/freetype/freetype/cache/ftcglyph.h @@ -0,0 +1,191 @@ +/***************************************************************************/ +/* */ +/* ftcglyph.h */ +/* */ +/* FreeType abstract glyph cache (specification). */ +/* */ +/* Copyright 2000-2001, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Important: The functions defined in this file are only used to */ + /* implement an abstract glyph cache class. You need to */ + /* provide additional logic to implement a complete cache. */ + /* For example, see `ftcimage.h' and `ftcimage.c' which */ + /* implement a FT_Glyph cache based on this code. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********* *********/ + /********* WARNING, THIS IS BETA CODE. *********/ + /********* *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#ifndef __FTCGLYPH_H__ +#define __FTCGLYPH_H__ + + +#include +#include FT_CACHE_H +#include FT_CACHE_MANAGER_H + + +FT_BEGIN_HEADER + + + /* each glyph set is characterized by a "glyph set type" which must be */ + /* defined by sub-classes */ + typedef struct FTC_GlyphFamilyRec_* FTC_GlyphFamily; + + /* handle to a glyph cache node */ + typedef struct FTC_GlyphNodeRec_* FTC_GlyphNode; + + + /* size should be 24 + chunk size on 32-bit machines; */ + /* note that the node's hash is ((gfam->hash << 16) | glyph_index) -- */ + /* this _must_ be set properly by the glyph node initializer */ + /* */ + typedef struct FTC_GlyphNodeRec_ + { + FTC_NodeRec node; + FT_UShort item_count; + FT_UShort item_start; + + } FTC_GlyphNodeRec; + + +#define FTC_GLYPH_NODE( x ) ( (FTC_GlyphNode)(x) ) +#define FTC_GLYPH_NODE_P( x ) ( (FTC_GlyphNode*)(x) ) + + + typedef struct FTC_GlyphQueryRec_ + { + FTC_QueryRec query; + FT_UInt gindex; + + } FTC_GlyphQueryRec, *FTC_GlyphQuery; + + +#define FTC_GLYPH_QUERY( x ) ( (FTC_GlyphQuery)(x) ) + + + /* a glyph set is used to categorize glyphs of a given type */ + typedef struct FTC_GlyphFamilyRec_ + { + FTC_FamilyRec family; + FT_UInt32 hash; + FT_UInt item_total; /* total number of glyphs in family */ + FT_UInt item_count; /* number of glyph items per node */ + + } FTC_GlyphFamilyRec; + + +#define FTC_GLYPH_FAMILY( x ) ( (FTC_GlyphFamily)(x) ) +#define FTC_GLYPH_FAMILY_P( x ) ( (FTC_GlyphFamily*)(x) ) + +#define FTC_GLYPH_FAMILY_MEMORY( x ) FTC_FAMILY(x)->cache->memory + + + /* each glyph node contains a 'chunk' of glyph items; */ + /* translate a glyph index into a chunk index */ +#define FTC_GLYPH_FAMILY_CHUNK( gfam, gindex ) \ + ( ( gindex ) / FTC_GLYPH_FAMILY( gfam )->item_count ) + + /* find a glyph index's chunk, and return its start index */ +#define FTC_GLYPH_FAMILY_START( gfam, gindex ) \ + ( FTC_GLYPH_FAMILY_CHUNK( gfam, gindex ) * \ + FTC_GLYPH_FAMILY( gfam )->item_count ) + + /* compute a glyph request's hash value */ +#define FTC_GLYPH_FAMILY_HASH( gfam, gindex ) \ + ( (FT_UFast)( \ + ( FTC_GLYPH_FAMILY( gfam )->hash << 16 ) | \ + ( FTC_GLYPH_FAMILY_CHUNK( gfam, gindex ) & 0xFFFFU ) ) ) + + /* must be called in an FTC_Family_CompareFunc to update the query */ + /* whenever a glyph set is matched in the lookup, or when it */ + /* is created */ +#define FTC_GLYPH_FAMILY_FOUND( gfam, gquery ) \ + do \ + { \ + FTC_QUERY( gquery )->family = FTC_FAMILY( gfam ); \ + FTC_QUERY( gquery )->hash = \ + FTC_GLYPH_FAMILY_HASH( gfam, \ + FTC_GLYPH_QUERY( gquery )->gindex ); \ + } while ( 0 ) + + /* retrieve glyph index of glyph node */ +#define FTC_GLYPH_NODE_GINDEX( x ) \ + ( (FT_UInt)( FTC_GLYPH_NODE( x )->node.hash & 0xFFFFU ) ) + + + /*************************************************************************/ + /* */ + /* These functions are exported so that they can be called from */ + /* user-provided cache classes; otherwise, they are really part of the */ + /* cache sub-system internals. */ + /* */ + + /* must be called by derived FTC_Node_InitFunc routines */ + FT_EXPORT( void ) + ftc_glyph_node_init( FTC_GlyphNode node, + FT_UInt gindex, /* glyph index for node */ + FTC_GlyphFamily gfam ); + + /* returns TRUE iff the query's glyph index correspond to the node; */ + /* this assumes that the "family" and "hash" fields of the query are */ + /* already correctly set */ + FT_EXPORT( FT_Bool ) + ftc_glyph_node_compare( FTC_GlyphNode gnode, + FTC_GlyphQuery gquery ); + + /* must be called by derived FTC_Node_DoneFunc routines */ + FT_EXPORT( void ) + ftc_glyph_node_done( FTC_GlyphNode node, + FTC_Cache cache ); + + + /* must be called by derived FTC_Family_InitFunc; */ + /* calls "ftc_family_init" */ + FT_EXPORT( FT_Error ) + ftc_glyph_family_init( FTC_GlyphFamily gfam, + FT_UInt32 hash, + FT_UInt item_count, + FT_UInt item_total, + FTC_GlyphQuery gquery, + FTC_Cache cache ); + + FT_EXPORT( void ) + ftc_glyph_family_done( FTC_GlyphFamily gfam ); + + + /* */ + +FT_END_HEADER + + +#endif /* __FTCGLYPH_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/cache/ftcimage.h b/extra_lib/include/freetype/freetype/cache/ftcimage.h new file mode 100644 index 0000000..2d0b780 --- /dev/null +++ b/extra_lib/include/freetype/freetype/cache/ftcimage.h @@ -0,0 +1,313 @@ +/***************************************************************************/ +/* */ +/* ftcimage.h */ +/* */ +/* FreeType Image cache (specification). */ +/* */ +/* Copyright 2000-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Each image cache really manages FT_Glyph objects. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTCIMAGE_H__ +#define __FTCIMAGE_H__ + + +#include +#include FT_CACHE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /*
    */ + /* cache_subsystem */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** IMAGE CACHE OBJECT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /************************************************************************** + * + * @struct: + * FTC_ImageTypeRec + * + * @description: + * A simple structure used to describe the type of glyph image to be + * loaded into the cache. + * + * @fields: + * font :: An @FTC_FontRec used to describe the glyph's face and size. + * + * flags :: The load flags to be applied when loading the glyph; see + * the @FT_LOAD_XXX constants for details. + * + * @note: + * This type completely replaces the @FTC_Image_Desc structure which is + * now obsolete. + */ + typedef struct FTC_ImageTypeRec_ + { + FTC_FontRec font; + FT_Int32 flags; + + } FTC_ImageTypeRec; + + typedef struct FTC_ImageTypeRec_* FTC_ImageType; + + /* */ + +#define FTC_IMAGE_TYPE_COMPARE( d1, d2 ) \ + ( FTC_FONT_COMPARE( &(d1)->font, &(d2)->font ) && \ + (d1)->flags == (d2)->flags ) + +#define FTC_IMAGE_TYPE_HASH( d ) \ + (FT_UFast)( FTC_FONT_HASH( &(d)->font ) ^ \ + ( (d)->flags << 4 ) ) + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_ImageCache */ + /* */ + /* */ + /* A handle to an glyph image cache object. They are designed to */ + /* hold many distinct glyph images while not exceeding a certain */ + /* memory threshold. */ + /* */ + typedef struct FTC_ImageCacheRec_* FTC_ImageCache; + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_ImageCache_New */ + /* */ + /* */ + /* Creates a new glyph image cache. */ + /* */ + /* */ + /* manager :: The parent manager for the image cache. */ + /* */ + /* */ + /* acache :: A handle to the new glyph image cache object. */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_ImageCache_New( FTC_Manager manager, + FTC_ImageCache *acache ); + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_ImageCache_Lookup */ + /* */ + /* */ + /* Retrieves a given glyph image from a glyph image cache. */ + /* */ + /* */ + /* cache :: A handle to the source glyph image cache. */ + /* */ + /* type :: A pointer to a glyph image type descriptor. */ + /* */ + /* gindex :: The glyph index to retrieve. */ + /* */ + /* */ + /* aglyph :: The corresponding @FT_Glyph object. 0 in case of */ + /* failure. */ + /* */ + /* anode :: Used to return the address of of the corresponding cache */ + /* node after incrementing its reference count (see note */ + /* below). */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + /* */ + /* The returned glyph is owned and managed by the glyph image cache. */ + /* Never try to transform or discard it manually! You can however */ + /* create a copy with @FT_Glyph_Copy and modify the new one. */ + /* */ + /* If "anode" is _not_ NULL, it receives the address of the cache */ + /* node containing the glyph image, after increasing its reference */ + /* count. This ensures that the node (as well as the FT_Glyph) will */ + /* always be kept in the cache until you call @FTC_Node_Unref to */ + /* "release" it. */ + /* */ + /* If "anode" is NULL, the cache node is left unchanged, which means */ + /* that the FT_Glyph could be flushed out of the cache on the next */ + /* call to one of the caching sub-system APIs. Don't assume that it */ + /* is persistent! */ + /* */ + FT_EXPORT( FT_Error ) + FTC_ImageCache_Lookup( FTC_ImageCache cache, + FTC_ImageType type, + FT_UInt gindex, + FT_Glyph *aglyph, + FTC_Node *anode ); + + /* */ + +#define ftc_image_format( x ) ( (x) & 7 ) + + +#define ftc_image_format_bitmap 0x0000 +#define ftc_image_format_outline 0x0001 + +#define ftc_image_format_mask 0x000F + +#define ftc_image_flag_monochrome 0x0010 +#define ftc_image_flag_unhinted 0x0020 +#define ftc_image_flag_autohinted 0x0040 +#define ftc_image_flag_unscaled 0x0080 +#define ftc_image_flag_no_sbits 0x0100 + + /* monochrome bitmap */ +#define ftc_image_mono ftc_image_format_bitmap | \ + ftc_image_flag_monochrome + + /* anti-aliased bitmap */ +#define ftc_image_grays ftc_image_format_bitmap + + /* scaled outline */ +#define ftc_image_outline ftc_image_format_outline + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_Image_Desc */ + /* */ + /* */ + /* THIS TYPE IS DEPRECATED. Use @FTC_ImageTypeRec instead. */ + /* */ + /* A simple structure used to describe a given glyph image category. */ + /* */ + /* */ + /* font :: An @FTC_FontRec used to describe the glyph's face */ + /* and size. */ + /* */ + /* image_type :: The glyph image's type. */ + /* */ + typedef struct FTC_Image_Desc_ + { + FTC_FontRec font; + FT_UInt image_type; + + } FTC_Image_Desc; + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_Image_Cache */ + /* */ + /* */ + /* THIS TYPE IS DEPRECATED. Use @FTC_ImageCache instead. */ + /* */ + typedef FTC_ImageCache FTC_Image_Cache; + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_Image_Cache_New */ + /* */ + /* */ + /* THIS FUNCTION IS DEPRECATED. Use @FTC_ImageCache_New instead. */ + /* */ + /* Creates a new glyph image cache. */ + /* */ + /* */ + /* manager :: The parent manager for the image cache. */ + /* */ + /* */ + /* acache :: A handle to the new glyph image cache object. */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Image_Cache_New( FTC_Manager manager, + FTC_Image_Cache *acache ); + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_Image_Cache_Lookup */ + /* */ + /* */ + /* THIS FUNCTION IS DEPRECATED. Use @FTC_ImageCache_Lookup instead. */ + /* */ + /* */ + /* cache :: A handle to the source glyph image cache. */ + /* */ + /* desc :: A pointer to a glyph image descriptor. */ + /* */ + /* gindex :: The glyph index to retrieve. */ + /* */ + /* */ + /* aglyph :: The corresponding @FT_Glyph object. 0 in case of */ + /* failure. */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + /* */ + /* The returned glyph is owned and managed by the glyph image cache. */ + /* Never try to transform or discard it manually! You can however */ + /* create a copy with @FT_Glyph_Copy and modify the new one. */ + /* */ + /* Because the glyph image cache limits the total amount of memory */ + /* taken by the glyphs it holds, the returned glyph might disappear */ + /* on a later invocation of this function! It is a cache after */ + /* all... */ + /* */ + /* Use this function to "lock" the glyph as long as it is needed. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Image_Cache_Lookup( FTC_Image_Cache cache, + FTC_Image_Desc* desc, + FT_UInt gindex, + FT_Glyph *aglyph ); + + /* */ + +FT_END_HEADER + + +#endif /* __FTCIMAGE_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/cache/ftcmanag.h b/extra_lib/include/freetype/freetype/cache/ftcmanag.h new file mode 100644 index 0000000..97c7759 --- /dev/null +++ b/extra_lib/include/freetype/freetype/cache/ftcmanag.h @@ -0,0 +1,244 @@ +/***************************************************************************/ +/* */ +/* ftcmanag.h */ +/* */ +/* FreeType Cache Manager (specification). */ +/* */ +/* Copyright 2000-2001 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* A cache manager is in charge of the following: */ + /* */ + /* - Maintain a mapping between generic FTC_FaceIDs and live FT_Face */ + /* objects. The mapping itself is performed through a user-provided */ + /* callback. However, the manager maintains a small cache of FT_Face */ + /* and FT_Size objects in order to speed up things considerably. */ + /* */ + /* - Manage one or more cache objects. Each cache is in charge of */ + /* holding a varying number of `cache nodes'. Each cache node */ + /* represents a minimal amount of individually accessible cached */ + /* data. For example, a cache node can be an FT_Glyph image */ + /* containing a vector outline, or some glyph metrics, or anything */ + /* else. */ + /* */ + /* Each cache node has a certain size in bytes that is added to the */ + /* total amount of `cache memory' within the manager. */ + /* */ + /* All cache nodes are located in a global LRU list, where the oldest */ + /* node is at the tail of the list. */ + /* */ + /* Each node belongs to a single cache, and includes a reference */ + /* count to avoid destroying it (due to caching). */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********* *********/ + /********* WARNING, THIS IS BETA CODE. *********/ + /********* *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#ifndef __FTCMANAG_H__ +#define __FTCMANAG_H__ + + +#include +#include FT_CACHE_H +#include FT_CACHE_INTERNAL_LRU_H +#include FT_CACHE_INTERNAL_CACHE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /*
    */ + /* cache_subsystem */ + /* */ + /*************************************************************************/ + + +#define FTC_MAX_FACES_DEFAULT 2 +#define FTC_MAX_SIZES_DEFAULT 4 +#define FTC_MAX_BYTES_DEFAULT 200000L /* ~200kByte by default */ + + /* maximum number of caches registered in a single manager */ +#define FTC_MAX_CACHES 16 + + + typedef struct FTC_FamilyEntryRec_ + { + FTC_Family family; + FTC_Cache cache; + FT_UInt index; + FT_UInt link; + + } FTC_FamilyEntryRec, *FTC_FamilyEntry; + + +#define FTC_FAMILY_ENTRY_NONE ( (FT_UInt)-1 ) + + + typedef struct FTC_FamilyTableRec_ + { + FT_UInt count; + FT_UInt size; + FTC_FamilyEntry entries; + FT_UInt free; + + } FTC_FamilyTableRec, *FTC_FamilyTable; + + + FT_EXPORT( FT_Error ) + ftc_family_table_alloc( FTC_FamilyTable table, + FT_Memory memory, + FTC_FamilyEntry *aentry ); + + FT_EXPORT( void ) + ftc_family_table_free( FTC_FamilyTable table, + FT_UInt idx ); + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_ManagerRec */ + /* */ + /* */ + /* The cache manager structure. */ + /* */ + /* */ + /* library :: A handle to a FreeType library instance. */ + /* */ + /* faces_list :: The lru list of @FT_Face objects in the cache. */ + /* */ + /* sizes_list :: The lru list of @FT_Size objects in the cache. */ + /* */ + /* max_weight :: The maximum cache pool weight. */ + /* */ + /* cur_weight :: The current cache pool weight. */ + /* */ + /* num_nodes :: The current number of nodes in the manager. */ + /* */ + /* nodes_list :: The global lru list of all cache nodes. */ + /* */ + /* caches :: A table of installed/registered cache objects. */ + /* */ + /* request_data :: User-provided data passed to the requester. */ + /* */ + /* request_face :: User-provided function used to implement a mapping */ + /* between abstract @FTC_FaceID values and real */ + /* @FT_Face objects. */ + /* */ + /* families :: Global table of families. */ + /* */ + typedef struct FTC_ManagerRec_ + { + FT_Library library; + FT_LruList faces_list; + FT_LruList sizes_list; + + FT_ULong max_weight; + FT_ULong cur_weight; + + FT_UInt num_nodes; + FTC_Node nodes_list; + + FTC_Cache caches[FTC_MAX_CACHES]; + + FT_Pointer request_data; + FTC_Face_Requester request_face; + + FTC_FamilyTableRec families; + + } FTC_ManagerRec; + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_Manager_Compress */ + /* */ + /* */ + /* This function is used to check the state of the cache manager if */ + /* its `num_bytes' field is greater than its `max_bytes' field. It */ + /* will flush as many old cache nodes as possible (ignoring cache */ + /* nodes with a non-zero reference count). */ + /* */ + /* */ + /* manager :: A handle to the cache manager. */ + /* */ + /* */ + /* Client applications should not call this function directly. It is */ + /* normally invoked by specific cache implementations. */ + /* */ + /* The reason this function is exported is to allow client-specific */ + /* cache classes. */ + /* */ + FT_EXPORT( void ) + FTC_Manager_Compress( FTC_Manager manager ); + + + /* this must be used internally for the moment */ + FT_EXPORT( FT_Error ) + FTC_Manager_Register_Cache( FTC_Manager manager, + FTC_Cache_Class clazz, + FTC_Cache *acache ); + + + /* can be called to increment a node's reference count */ + FT_EXPORT( void ) + FTC_Node_Ref( FTC_Node node, + FTC_Manager manager ); + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_Node_Unref */ + /* */ + /* */ + /* Decrement a cache node's internal reference count. When the count */ + /* reaches 0, it is not destroyed but becomes eligible for subsequent */ + /* cache flushes. */ + /* */ + /* */ + /* node :: The cache node handle. */ + /* */ + /* manager :: The cache manager handle. */ + /* */ + FT_EXPORT( void ) + FTC_Node_Unref( FTC_Node node, + FTC_Manager manager ); + + /* */ + +FT_END_HEADER + + +#endif /* __FTCMANAG_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/cache/ftcsbits.h b/extra_lib/include/freetype/freetype/cache/ftcsbits.h new file mode 100644 index 0000000..6f8ef99 --- /dev/null +++ b/extra_lib/include/freetype/freetype/cache/ftcsbits.h @@ -0,0 +1,275 @@ +/***************************************************************************/ +/* */ +/* ftcsbits.h */ +/* */ +/* A small-bitmap cache (specification). */ +/* */ +/* Copyright 2000-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTCSBITS_H__ +#define __FTCSBITS_H__ + + +#include +#include FT_CACHE_H +#include FT_CACHE_IMAGE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /*
    */ + /* cache_subsystem */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_SBit */ + /* */ + /* */ + /* A handle to a small bitmap descriptor. See the @FTC_SBitRec */ + /* structure for details. */ + /* */ + typedef struct FTC_SBitRec_* FTC_SBit; + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_SBitRec */ + /* */ + /* */ + /* A very compact structure used to describe a small glyph bitmap. */ + /* */ + /* */ + /* width :: The bitmap width in pixels. */ + /* */ + /* height :: The bitmap height in pixels. */ + /* */ + /* left :: The horizontal distance from the pen position to the */ + /* left bitmap border (a.k.a. `left side bearing', or */ + /* `lsb'). */ + /* */ + /* top :: The vertical distance from the pen position (on the */ + /* baseline) to the upper bitmap border (a.k.a. `top */ + /* side bearing'). The distance is positive for upwards */ + /* Y coordinates. */ + /* */ + /* format :: The format of the glyph bitmap (monochrome or gray). */ + /* */ + /* max_grays :: Maximum gray level value (in the range 1 to 255). */ + /* */ + /* pitch :: The number of bytes per bitmap line. May be positive */ + /* or negative. */ + /* */ + /* xadvance :: The horizontal advance width in pixels. */ + /* */ + /* yadvance :: The vertical advance height in pixels. */ + /* */ + /* buffer :: A pointer to the bitmap pixels. */ + /* */ + typedef struct FTC_SBitRec_ + { + FT_Byte width; + FT_Byte height; + FT_Char left; + FT_Char top; + + FT_Byte format; + FT_Byte max_grays; + FT_Short pitch; + FT_Char xadvance; + FT_Char yadvance; + + FT_Byte* buffer; + + } FTC_SBitRec; + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_SBitCache */ + /* */ + /* */ + /* A handle to a small bitmap cache. These are special cache objects */ + /* used to store small glyph bitmaps (and anti-aliased pixmaps) in a */ + /* much more efficient way than the traditional glyph image cache */ + /* implemented by @FTC_ImageCache. */ + /* */ + typedef struct FTC_SBitCacheRec_* FTC_SBitCache; + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_SBit_Cache */ + /* */ + /* */ + /* DEPRECATED. Use @FTC_SBitCache instead. */ + /* */ + typedef FTC_SBitCache FTC_SBit_Cache; + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_SBitCache_New */ + /* */ + /* */ + /* Creates a new cache to store small glyph bitmaps. */ + /* */ + /* */ + /* manager :: A handle to the source cache manager. */ + /* */ + /* */ + /* acache :: A handle to the new sbit cache. NULL in case of error. */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_SBitCache_New( FTC_Manager manager, + FTC_SBitCache *acache ); + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_SBitCache_Lookup */ + /* */ + /* */ + /* Looks up a given small glyph bitmap in a given sbit cache and */ + /* "lock" it to prevent its flushing from the cache until needed */ + /* */ + /* */ + /* cache :: A handle to the source sbit cache. */ + /* */ + /* type :: A pointer to the glyph image type descriptor. */ + /* */ + /* gindex :: The glyph index. */ + /* */ + /* */ + /* sbit :: A handle to a small bitmap descriptor. */ + /* */ + /* anode :: Used to return the address of of the corresponding cache */ + /* node after incrementing its reference count (see note */ + /* below). */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + /* */ + /* The small bitmap descriptor and its bit buffer are owned by the */ + /* cache and should never be freed by the application. They might */ + /* as well disappear from memory on the next cache lookup, so don't */ + /* treat them as persistent data. */ + /* */ + /* The descriptor's `buffer' field is set to 0 to indicate a missing */ + /* glyph bitmap. */ + /* */ + /* If "anode" is _not_ NULL, it receives the address of the cache */ + /* node containing the bitmap, after increasing its reference count. */ + /* This ensures that the node (as well as the image) will always be */ + /* kept in the cache until you call @FTC_Node_Unref to "release" it. */ + /* */ + /* If "anode" is NULL, the cache node is left unchanged, which means */ + /* that the bitmap could be flushed out of the cache on the next */ + /* call to one of the caching sub-system APIs. Don't assume that it */ + /* is persistent! */ + /* */ + FT_EXPORT( FT_Error ) + FTC_SBitCache_Lookup( FTC_SBitCache cache, + FTC_ImageType type, + FT_UInt gindex, + FTC_SBit *sbit, + FTC_Node *anode ); + + + /* */ + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_SBit_Cache_New */ + /* */ + /* */ + /* DEPRECATED. Use @FTC_SBitCache_New instead. */ + /* */ + /* Creates a new cache to store small glyph bitmaps. */ + /* */ + /* */ + /* manager :: A handle to the source cache manager. */ + /* */ + /* */ + /* acache :: A handle to the new sbit cache. NULL in case of error. */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_SBit_Cache_New( FTC_Manager manager, + FTC_SBit_Cache *acache ); + + + /*************************************************************************/ + /* */ + /* */ + /* FTC_SBit_Cache_Lookup */ + /* */ + /* */ + /* DEPRECATED. Use @FTC_SBitCache_Lookup instead. */ + /* */ + /* Looks up a given small glyph bitmap in a given sbit cache. */ + /* */ + /* */ + /* cache :: A handle to the source sbit cache. */ + /* */ + /* desc :: A pointer to the glyph image descriptor. */ + /* */ + /* gindex :: The glyph index. */ + /* */ + /* */ + /* sbit :: A handle to a small bitmap descriptor. */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + /* */ + /* The small bitmap descriptor and its bit buffer are owned by the */ + /* cache and should never be freed by the application. They might */ + /* as well disappear from memory on the next cache lookup, so don't */ + /* treat them as persistent data. */ + /* */ + /* The descriptor's `buffer' field is set to 0 to indicate a missing */ + /* glyph bitmap. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_SBit_Cache_Lookup( FTC_SBit_Cache cache, + FTC_Image_Desc* desc, + FT_UInt gindex, + FTC_SBit *sbit ); + + +FT_END_HEADER + +#endif /* __FTCSBITS_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/cache/ftlru.h b/extra_lib/include/freetype/freetype/cache/ftlru.h new file mode 100644 index 0000000..d446c60 --- /dev/null +++ b/extra_lib/include/freetype/freetype/cache/ftlru.h @@ -0,0 +1,208 @@ +/***************************************************************************/ +/* */ +/* ftlru.h */ +/* */ +/* Simple LRU list-cache (specification). */ +/* */ +/* Copyright 2000-2001, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* An LRU is a list that cannot hold more than a certain number of */ + /* elements (`max_elements'). All elements in the list are sorted in */ + /* least-recently-used order, i.e., the `oldest' element is at the tail */ + /* of the list. */ + /* */ + /* When doing a lookup (either through `Lookup()' or `Lookup_Node()'), */ + /* the list is searched for an element with the corresponding key. If */ + /* it is found, the element is moved to the head of the list and is */ + /* returned. */ + /* */ + /* If no corresponding element is found, the lookup routine will try to */ + /* obtain a new element with the relevant key. If the list is already */ + /* full, the oldest element from the list is discarded and replaced by a */ + /* new one; a new element is added to the list otherwise. */ + /* */ + /* Note that it is possible to pre-allocate the element list nodes. */ + /* This is handy if `max_elements' is sufficiently small, as it saves */ + /* allocations/releases during the lookup process. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********* *********/ + /********* WARNING, THIS IS BETA CODE. *********/ + /********* *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#ifndef __FTLRU_H__ +#define __FTLRU_H__ + + +#include +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /* generic list key type */ + typedef FT_Pointer FT_LruKey; + + /* a list list handle */ + typedef struct FT_LruListRec_* FT_LruList; + + /* a list class handle */ + typedef const struct FT_LruList_ClassRec_* FT_LruList_Class; + + /* a list node handle */ + typedef struct FT_LruNodeRec_* FT_LruNode; + + /* the list node structure */ + typedef struct FT_LruNodeRec_ + { + FT_LruNode next; + FT_LruKey key; + + } FT_LruNodeRec; + + + /* the list structure */ + typedef struct FT_LruListRec_ + { + FT_Memory memory; + FT_LruList_Class clazz; + FT_LruNode nodes; + FT_UInt max_nodes; + FT_UInt num_nodes; + FT_Pointer data; + + } FT_LruListRec; + + + /* initialize a list list */ + typedef FT_Error + (*FT_LruList_InitFunc)( FT_LruList list ); + + /* finalize a list list */ + typedef void + (*FT_LruList_DoneFunc)( FT_LruList list ); + + /* this method is used to initialize a new list element node */ + typedef FT_Error + (*FT_LruNode_InitFunc)( FT_LruNode node, + FT_LruKey key, + FT_Pointer data ); + + /* this method is used to finalize a given list element node */ + typedef void + (*FT_LruNode_DoneFunc)( FT_LruNode node, + FT_Pointer data ); + + /* If defined, this method is called when the list if full */ + /* during the lookup process -- it is used to change the contents */ + /* of a list element node instead of calling `done_element()', */ + /* then `init_element()'. Set it to 0 for default behaviour. */ + typedef FT_Error + (*FT_LruNode_FlushFunc)( FT_LruNode node, + FT_LruKey new_key, + FT_Pointer data ); + + /* If defined, this method is used to compare a list element node */ + /* with a given key during a lookup. If set to 0, the `key' */ + /* fields will be directly compared instead. */ + typedef FT_Bool + (*FT_LruNode_CompareFunc)( FT_LruNode node, + FT_LruKey key, + FT_Pointer data ); + + /* A selector is used to indicate whether a given list element node */ + /* is part of a selection for FT_LruList_Remove_Selection(). The */ + /* functrion must return true (i.e., non-null) to indicate that the */ + /* node is part of it. */ + typedef FT_Bool + (*FT_LruNode_SelectFunc)( FT_LruNode node, + FT_Pointer data, + FT_Pointer list_data ); + + /* LRU class */ + typedef struct FT_LruList_ClassRec_ + { + FT_UInt list_size; + FT_LruList_InitFunc list_init; /* optional */ + FT_LruList_DoneFunc list_done; /* optional */ + + FT_UInt node_size; + FT_LruNode_InitFunc node_init; /* MANDATORY */ + FT_LruNode_DoneFunc node_done; /* optional */ + FT_LruNode_FlushFunc node_flush; /* optional */ + FT_LruNode_CompareFunc node_compare; /* optional */ + + } FT_LruList_ClassRec; + + + /* The following functions must be exported in the case where */ + /* applications would want to write their own cache classes. */ + + FT_EXPORT( FT_Error ) + FT_LruList_New( FT_LruList_Class clazz, + FT_UInt max_elements, + FT_Pointer user_data, + FT_Memory memory, + FT_LruList *alist ); + + FT_EXPORT( void ) + FT_LruList_Reset( FT_LruList list ); + + FT_EXPORT( void ) + FT_LruList_Destroy ( FT_LruList list ); + + FT_EXPORT( FT_Error ) + FT_LruList_Lookup( FT_LruList list, + FT_LruKey key, + FT_LruNode *anode ); + + FT_EXPORT( void ) + FT_LruList_Remove( FT_LruList list, + FT_LruNode node ); + + FT_EXPORT( void ) + FT_LruList_Remove_Selection( FT_LruList list, + FT_LruNode_SelectFunc select_func, + FT_Pointer select_data ); + + /* */ + +FT_END_HEADER + + +#endif /* __FTLRU_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/config/ftconfig.h b/extra_lib/include/freetype/freetype/config/ftconfig.h new file mode 100644 index 0000000..d23b73e --- /dev/null +++ b/extra_lib/include/freetype/freetype/config/ftconfig.h @@ -0,0 +1,340 @@ +/***************************************************************************/ +/* */ +/* ftconfig.h */ +/* */ +/* ANSI-specific configuration file (specification only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This header file contains a number of macro definitions that are used */ + /* by the rest of the engine. Most of the macros here are automatically */ + /* determined at compile time, and you should not need to change it to */ + /* port FreeType, except to compile the library with a non-ANSI */ + /* compiler. */ + /* */ + /* Note however that if some specific modifications are needed, we */ + /* advise you to place a modified copy in your build directory. */ + /* */ + /* The build directory is usually `freetype/builds/', and */ + /* contains system-specific files that are always included first when */ + /* building the library. */ + /* */ + /* This ANSI version should stay in `include/freetype/config'. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTCONFIG_H__ +#define __FTCONFIG_H__ + +#include +#include FT_CONFIG_OPTIONS_H +#include FT_CONFIG_STANDARD_LIBRARY_H + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* PLATFORM-SPECIFIC CONFIGURATION MACROS */ + /* */ + /* These macros can be toggled to suit a specific system. The current */ + /* ones are defaults used to compile FreeType in an ANSI C environment */ + /* (16bit compilers are also supported). Copy this file to your own */ + /* `freetype/builds/' directory, and edit it to port the engine. */ + /* */ + /*************************************************************************/ + + + /* The number of bytes in an `int' type. */ +#if FT_UINT_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_INT 4 +#elif FT_UINT_MAX == 0xFFFFU +#define FT_SIZEOF_INT 2 +#elif FT_UINT_MAX > 0xFFFFFFFFU && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFU +#define FT_SIZEOF_INT 8 +#else +#error "Unsupported number of bytes in `int' type!" +#endif + + /* The number of bytes in a `long' type. */ +#if FT_ULONG_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_LONG 4 +#elif FT_ULONG_MAX > 0xFFFFFFFFU && FT_ULONG_MAX == 0xFFFFFFFFFFFFFFFFU +#define FT_SIZEOF_LONG 8 +#else +#error "Unsupported number of bytes in `long' type!" +#endif + + + /* Preferred alignment of data */ +#define FT_ALIGNMENT 8 + + + /* FT_UNUSED is a macro used to indicate that a given parameter is not */ + /* used -- this is only used to get rid of unpleasant compiler warnings */ +#ifndef FT_UNUSED +#define FT_UNUSED( arg ) ( (arg) = (arg) ) +#endif + + + /*************************************************************************/ + /* */ + /* AUTOMATIC CONFIGURATION MACROS */ + /* */ + /* These macros are computed from the ones defined above. Don't touch */ + /* their definition, unless you know precisely what you are doing. No */ + /* porter should need to mess with them. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Mac support */ + /* */ + /* This is the only necessary change, so it is defined here instead */ + /* providing a new configuration file. */ + /* */ +#if ( defined( __APPLE__ ) && !defined( DARWIN_NO_CARBON ) ) || \ + ( defined( __MWERKS__ ) && defined( macintosh ) ) +#define FT_MACINTOSH 1 +#endif + + + /*************************************************************************/ + /* */ + /* IntN types */ + /* */ + /* Used to guarantee the size of some specific integers. */ + /* */ + typedef signed short FT_Int16; + typedef unsigned short FT_UInt16; + +#if FT_SIZEOF_INT == 4 + + typedef signed int FT_Int32; + typedef unsigned int FT_UInt32; + +#elif FT_SIZEOF_LONG == 4 + + typedef signed long FT_Int32; + typedef unsigned long FT_UInt32; + +#else +#error "no 32bit type found -- please check your configuration files" +#endif + + /* look up an integer type that is at least 32 bits */ +#if FT_SIZEOF_INT >= 4 + + typedef int FT_Fast; + typedef unsigned int FT_UFast; + +#elif FT_SIZEOF_LONG >= 4 + + typedef long FT_Fast; + typedef unsigned long FT_UFast; + +#endif + + + /* determine whether we have a 64-bit int type for platforms without */ + /* Autoconf */ +#if FT_SIZEOF_LONG == 8 + + /* FT_LONG64 must be defined if a 64-bit type is available */ +#define FT_LONG64 +#define FT_INT64 long + +#elif defined( _MSC_VER ) && _MSC_VER >= 900 /* Visual C++ (and Intel C++) */ + + /* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 + +#elif defined( __BORLANDC__ ) /* Borland C++ */ + + /* XXXX: We should probably check the value of __BORLANDC__ in order */ + /* to test the compiler version. */ + + /* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 + +#elif defined( __WATCOMC__ ) /* Watcom C++ */ + + /* Watcom doesn't provide 64-bit data types */ + +#elif defined( __MWERKS__ ) /* Metrowerks CodeWarrior */ + +#define FT_LONG64 +#define FT_INT64 long long int + +#elif defined( __GNUC__ ) + + /* GCC provides the "long long" type */ +#define FT_LONG64 +#define FT_INT64 long long int + +#endif /* FT_SIZEOF_LONG == 8 */ + + +#define FT_BEGIN_STMNT do { +#define FT_END_STMNT } while ( 0 ) +#define FT_DUMMY_STMNT FT_BEGIN_STMNT FT_END_STMNT + + + /*************************************************************************/ + /* */ + /* A 64-bit data type will create compilation problems if you compile */ + /* in strict ANSI mode. To avoid them, we disable their use if */ + /* __STDC__ is defined. You can however ignore this rule by */ + /* defining the FT_CONFIG_OPTION_FORCE_INT64 configuration macro. */ + /* */ +#if defined( FT_LONG64 ) && !defined( FT_CONFIG_OPTION_FORCE_INT64 ) + +#ifdef __STDC__ + + /* undefine the 64-bit macros in strict ANSI compilation mode */ +#undef FT_LONG64 +#undef FT_INT64 + +#endif /* __STDC__ */ + +#endif /* FT_LONG64 && !FT_CONFIG_OPTION_FORCE_INT64 */ + + +#ifdef FT_MAKE_OPTION_SINGLE_OBJECT + +#define FT_LOCAL( x ) static x +#define FT_LOCAL_DEF( x ) static x + +#else + +#ifdef __cplusplus +#define FT_LOCAL( x ) extern "C" x +#define FT_LOCAL_DEF( x ) extern "C" x +#else +#define FT_LOCAL( x ) extern x +#define FT_LOCAL_DEF( x ) x +#endif + +#endif /* FT_MAKE_OPTION_SINGLE_OBJECT */ + + +#ifndef FT_BASE + +#ifdef __cplusplus +#define FT_BASE( x ) extern "C" x +#else +#define FT_BASE( x ) extern x +#endif + +#endif /* !FT_BASE */ + + +#ifndef FT_BASE_DEF + +#ifdef __cplusplus +#define FT_BASE_DEF( x ) extern "C" x +#else +#define FT_BASE_DEF( x ) extern x +#endif + +#endif /* !FT_BASE_DEF */ + + +#ifndef FT_EXPORT + +#ifdef __cplusplus +#define FT_EXPORT( x ) extern "C" x +#else +#define FT_EXPORT( x ) extern x +#endif + +#endif /* !FT_EXPORT */ + + +#ifndef FT_EXPORT_DEF + +#ifdef __cplusplus +#define FT_EXPORT_DEF( x ) extern "C" x +#else +#define FT_EXPORT_DEF( x ) extern x +#endif + +#endif /* !FT_EXPORT_DEF */ + + +#ifndef FT_EXPORT_VAR + +#ifdef __cplusplus +#define FT_EXPORT_VAR( x ) extern "C" x +#else +#define FT_EXPORT_VAR( x ) extern x +#endif + +#endif /* !FT_EXPORT_VAR */ + + /* The following macros are needed to compile the library with a */ + /* C++ compiler and with 16bit compilers. */ + /* */ + + /* This is special. Within C++, you must specify `extern "C"' for */ + /* functions which are used via function pointers, and you also */ + /* must do that for structures which contain function pointers to */ + /* assure C linkage -- it's not possible to have (local) anonymous */ + /* functions which are accessed by (global) function pointers. */ + /* */ + /* */ + /* FT_CALLBACK_DEF is used to _define_ a callback function. */ + /* */ + /* FT_CALLBACK_TABLE is used to _declare_ a constant variable that */ + /* contains pointers to callback functions. */ + /* */ + /* FT_CALLBACK_TABLE_DEF is used to _define_ a constant variable */ + /* that contains pointers to callback functions. */ + /* */ + /* */ + /* Some 16bit compilers have to redefine these macros to insert */ + /* the infamous `_cdecl' or `__fastcall' declarations. */ + /* */ +#ifndef FT_CALLBACK_DEF +#ifdef __cplusplus +#define FT_CALLBACK_DEF( x ) extern "C" x +#else +#define FT_CALLBACK_DEF( x ) static x +#endif +#endif /* FT_CALLBACK_DEF */ + +#ifndef FT_CALLBACK_TABLE +#ifdef __cplusplus +#define FT_CALLBACK_TABLE extern "C" +#define FT_CALLBACK_TABLE_DEF extern "C" +#else +#define FT_CALLBACK_TABLE extern +#define FT_CALLBACK_TABLE_DEF /* nothing */ +#endif +#endif /* FT_CALLBACK_TABLE */ + + +FT_END_HEADER + + +#endif /* __FTCONFIG_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/config/ftheader.h b/extra_lib/include/freetype/freetype/config/ftheader.h new file mode 100644 index 0000000..22cd62e --- /dev/null +++ b/extra_lib/include/freetype/freetype/config/ftheader.h @@ -0,0 +1,537 @@ +/***************************************************************************/ +/* */ +/* ftheader.h */ +/* */ +/* Build macros of the FreeType 2 library. */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef __FT_HEADER_H__ +#define __FT_HEADER_H__ + + /*@***********************************************************************/ + /* */ + /* */ + /* FT_BEGIN_HEADER */ + /* */ + /* */ + /* This macro is used in association with @FT_END_HEADER in header */ + /* files to ensure that the declarations within are properly */ + /* encapsulated in an `extern "C" { .. }' block when included from a */ + /* C++ compiler. */ + /* */ +#ifdef __cplusplus +#define FT_BEGIN_HEADER extern "C" { +#else +#define FT_BEGIN_HEADER /* nothing */ +#endif + + + /*@***********************************************************************/ + /* */ + /* */ + /* FT_END_HEADER */ + /* */ + /* */ + /* This macro is used in association with @FT_BEGIN_HEADER in header */ + /* files to ensure that the declarations within are properly */ + /* encapsulated in an `extern "C" { .. }' block when included from a */ + /* C++ compiler. */ + /* */ +#ifdef __cplusplus +#define FT_END_HEADER } +#else +#define FT_END_HEADER /* nothing */ +#endif + + + /*************************************************************************/ + /* */ + /* Aliases for the FreeType 2 public and configuration files. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /*
    */ + /* header_file_macros */ + /* */ + /* */ + /* Header File Macros */ + /* */ + /* <Abstract> */ + /* Macro definitions used to #include specific header files. */ + /* */ + /* <Description> */ + /* The following macros are defined to the name of specific */ + /* FreeType 2 header files. They can be used directly in #include */ + /* statements as in: */ + /* */ + /* { */ + /* #include FT_FREETYPE_H */ + /* #include FT_MULTIPLE_MASTERS_H */ + /* #include FT_GLYPH_H */ + /* } */ + /* */ + /* There are several reasons why we are now using macros to name */ + /* public header files. The first one is that such macros are not */ + /* limited to the infamous 8.3 naming rule required by DOS (and */ + /* `FT_MULTIPLE_MASTERS_H' is a lot more meaningful than `ftmm.h'). */ + /* */ + /* The second reason is that is allows for more flexibility in the */ + /* way FreeType 2 is installed on a given system. */ + /* */ + /*************************************************************************/ + + /* configuration files */ + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_CONFIG_CONFIG_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* FreeType 2 configuration data. */ + /* */ +#ifndef FT_CONFIG_CONFIG_H +#define FT_CONFIG_CONFIG_H <freetype/config/ftconfig.h> +#endif + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_CONFIG_STANDARD_LIBRARY_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* FreeType 2 configuration data. */ + /* */ +#ifndef FT_CONFIG_STANDARD_LIBRARY_H +#define FT_CONFIG_STANDARD_LIBRARY_H <freetype/config/ftstdlib.h> +#endif + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_CONFIG_OPTIONS_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* FreeType 2 project-specific configuration options. */ + /* */ +#ifndef FT_CONFIG_OPTIONS_H +#define FT_CONFIG_OPTIONS_H <freetype/config/ftoption.h> +#endif + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_CONFIG_MODULES_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the list of FreeType 2 modules that are statically linked to new */ + /* library instances in @FT_Init_FreeType. */ + /* */ +#ifndef FT_CONFIG_MODULES_H +#define FT_CONFIG_MODULES_H <freetype/config/ftmodule.h> +#endif + + /* public headers */ + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_FREETYPE_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the base FreeType 2 API. */ + /* */ +#define FT_FREETYPE_H <freetype/freetype.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_ERRORS_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the list of FreeType 2 error codes (and messages). */ + /* */ + /* It is included by @FT_FREETYPE_H. */ + /* */ +#define FT_ERRORS_H <freetype/fterrors.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_MODULE_ERRORS_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the list of FreeType 2 module error offsets (and messages). */ + /* */ +#define FT_MODULE_ERRORS_H <freetype/ftmoderr.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_SYSTEM_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the FreeType 2 interface to low-level operations (i.e. memory */ + /* management and stream i/o). */ + /* */ + /* It is included by @FT_FREETYPE_H. */ + /* */ +#define FT_SYSTEM_H <freetype/ftsystem.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_IMAGE_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* types definitions related to glyph images (i.e. bitmaps, outlines, */ + /* scan-converter parameters). */ + /* */ + /* It is included by @FT_FREETYPE_H. */ + /* */ +#define FT_IMAGE_H <freetype/ftimage.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_TYPES_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the basic data types defined by FreeType 2. */ + /* */ + /* It is included by @FT_FREETYPE_H. */ + /* */ +#define FT_TYPES_H <freetype/fttypes.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_LIST_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the list management API of FreeType 2. */ + /* */ + /* (Most applications will never need to include this file.) */ + /* */ +#define FT_LIST_H <freetype/ftlist.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_OUTLINE_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the scalable outline management API of FreeType 2. */ + /* */ +#define FT_OUTLINE_H <freetype/ftoutln.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_SIZES_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the API used to manage multiple @FT_Size objects per face. */ + /* */ +#define FT_SIZES_H <freetype/ftsizes.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_MODULE_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the module management API of FreeType 2. */ + /* */ +#define FT_MODULE_H <freetype/ftmodapi.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_RENDER_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the renderer module management API of FreeType 2. */ + /* */ +#define FT_RENDER_H <freetype/ftrender.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_TYPE1_TABLES_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the types and API specific to the Type 1 format. */ + /* */ +#define FT_TYPE1_TABLES_H <freetype/t1tables.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_TRUETYPE_IDS_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the enumeration values used to identify name strings, languages, */ + /* encodings, etc. This file really contains a _large_ set of */ + /* constant macro definitions, taken from the TrueType and OpenType */ + /* specifications. */ + /* */ +#define FT_TRUETYPE_IDS_H <freetype/ttnameid.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_TRUETYPE_TABLES_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the types and API specific to the TrueType (as well as OpenType) */ + /* format. */ + /* */ +#define FT_TRUETYPE_TABLES_H <freetype/tttables.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_TRUETYPE_TAGS_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the definitions of TrueType 4-byte `tags' used to identify blocks */ + /* in SFNT-based font formats (i.e. TrueType and OpenType). */ + /* */ +#define FT_TRUETYPE_TAGS_H <freetype/tttags.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_BDF_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the definitions of an API to access BDF-specific strings from a */ + /* face. */ + /* */ +#define FT_BDF_H <freetype/ftbdf.h> + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_GZIP_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the definitions of an API to support for gzip-compressed files. */ + /* */ +#define FT_GZIP_H <freetype/ftgzip.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_WINFONTS_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the definitions of an API to support Windows .FNT files */ + /* */ +#define FT_WINFONTS_H <freetype/ftwinfnt.h> + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_GLYPH_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the API of the optional glyph management component. */ + /* */ +#define FT_GLYPH_H <freetype/ftglyph.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_BBOX_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the API of the optional exact bounding box computation routines. */ + /* */ +#define FT_BBOX_H <freetype/ftbbox.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_CACHE_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the API of the optional FreeType 2 cache sub-system. */ + /* */ +#define FT_CACHE_H <freetype/ftcache.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_CACHE_IMAGE_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the `glyph image' API of the FreeType 2 cache sub-system. */ + /* */ + /* It is used to define a cache for @FT_Glyph elements. You can also */ + /* see the API defined in @FT_CACHE_SMALL_BITMAPS_H if you only need */ + /* to store small glyph bitmaps, as it will use less memory. */ + /* */ +#define FT_CACHE_IMAGE_H <freetype/cache/ftcimage.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_CACHE_SMALL_BITMAPS_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the `small bitmaps' API of the FreeType 2 cache sub-system. */ + /* */ + /* It is used to define a cache for small glyph bitmaps in a */ + /* relatively memory-efficient way. You can also use the API defined */ + /* in @FT_CACHE_IMAGE_H if you want to cache arbitrary glyph images, */ + /* including scalable outlines. */ + /* */ +#define FT_CACHE_SMALL_BITMAPS_H <freetype/cache/ftcsbits.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_CACHE_CHARMAP_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the `charmap' API of the FreeType 2 cache sub-system. */ + /* */ +#define FT_CACHE_CHARMAP_H <freetype/cache/ftccmap.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_MAC_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the Macintosh-specific FreeType 2 API. The latter is used to */ + /* access fonts embedded in resource forks. */ + /* */ + /* This header file must be explicitly included by client */ + /* applications compiled on the Mac (note that the base API still */ + /* works though). */ + /* */ +#define FT_MAC_H <freetype/ftmac.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_MULTIPLE_MASTERS_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the optional multiple-masters management API of FreeType 2. */ + /* */ +#define FT_MULTIPLE_MASTERS_H <freetype/ftmm.h> + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_SFNT_NAMES_H */ + /* */ + /* @description: */ + /* A macro used in #include statements to name the file containing */ + /* the optional FreeType 2 API used to access embedded `name' strings */ + /* in SFNT-based font formats (i.e. TrueType and OpenType). */ + /* */ +#define FT_SFNT_NAMES_H <freetype/ftsnames.h> + + /* */ + +#define FT_TRIGONOMETRY_H <freetype/fttrigon.h> +#define FT_STROKER_H <freetype/ftstroke.h> +#define FT_SYNTHESIS_H <freetype/ftsynth.h> +#define FT_ERROR_DEFINITIONS_H <freetype/fterrdef.h> + +#define FT_CACHE_MANAGER_H <freetype/cache/ftcmanag.h> + +#define FT_CACHE_INTERNAL_LRU_H <freetype/cache/ftlru.h> +#define FT_CACHE_INTERNAL_GLYPH_H <freetype/cache/ftcglyph.h> +#define FT_CACHE_INTERNAL_CACHE_H <freetype/cache/ftccache.h> + +#define FT_XFREE86_H <freetype/ftxf86.h> + +#define FT_INCREMENTAL_H <freetype/ftincrem.h> + +#define FT_TRUETYPE_UNPATENTED_H <freetype/ttunpat.h> + + /* now include internal headers definitions from <freetype/internal/...> */ + +#define FT_INTERNAL_INTERNAL_H <freetype/internal/internal.h> +#include FT_INTERNAL_INTERNAL_H + + +#endif /* __FT2_BUILD_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/config/ftmodule.h b/extra_lib/include/freetype/freetype/config/ftmodule.h new file mode 100644 index 0000000..d0e6f16 --- /dev/null +++ b/extra_lib/include/freetype/freetype/config/ftmodule.h @@ -0,0 +1,19 @@ +FT_USE_MODULE(autohint_module_class) +FT_USE_MODULE(cff_driver_class) +FT_USE_MODULE(t1cid_driver_class) +FT_USE_MODULE(pcf_driver_class) +FT_USE_MODULE(bdf_driver_class) +FT_USE_MODULE(psaux_module_class) +FT_USE_MODULE(psnames_module_class) +FT_USE_MODULE(pshinter_module_class) +FT_USE_MODULE(ft_raster1_renderer_class) +FT_USE_MODULE(sfnt_module_class) +FT_USE_MODULE(ft_smooth_renderer_class) +FT_USE_MODULE(ft_smooth_lcd_renderer_class) +FT_USE_MODULE(ft_smooth_lcdv_renderer_class) +FT_USE_MODULE(tt_driver_class) +FT_USE_MODULE(t1_driver_class) +FT_USE_MODULE(t42_driver_class) +FT_USE_MODULE(pfr_driver_class) +FT_USE_MODULE(winfnt_driver_class) + diff --git a/extra_lib/include/freetype/freetype/config/ftoption.h b/extra_lib/include/freetype/freetype/config/ftoption.h new file mode 100644 index 0000000..3581caa --- /dev/null +++ b/extra_lib/include/freetype/freetype/config/ftoption.h @@ -0,0 +1,530 @@ +/***************************************************************************/ +/* */ +/* ftoption.h */ +/* */ +/* User-selectable configuration macros (specification only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTOPTION_H__ +#define __FTOPTION_H__ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* USER-SELECTABLE CONFIGURATION MACROS */ + /* */ + /* This file contains the default configuration macro definitions for */ + /* a standard build of the FreeType library. There are three ways to */ + /* use this file to build project-specific versions of the library: */ + /* */ + /* - You can modify this file by hand, but this is not recommended in */ + /* cases where you would like to build several versions of the */ + /* library from a single source directory. */ + /* */ + /* - You can put a copy of this file in your build directory, more */ + /* precisely in "$BUILD/freetype/config/ftoption.h", where "$BUILD" */ + /* is the name of a directory that is included _before_ the FreeType */ + /* include path during compilation. */ + /* */ + /* The default FreeType Makefiles and Jamfiles use the build */ + /* directory "builds/<system>" by default, but you can easily change */ + /* that for your own projects. */ + /* */ + /* - Copy the file <ft2build.h> to "$BUILD/ft2build.h" and modify it */ + /* slightly to pre-define the macro FT_CONFIG_OPTIONS_H used to */ + /* locate this file during the build. For example, */ + /* */ + /* #define FT_CONFIG_OPTIONS_H <myftoptions.h> */ + /* #include <freetype/config/ftheader.h> */ + /* */ + /* will use "$BUILD/myftoptions.h" instead of this file for macro */ + /* definitions. */ + /* */ + /* Note also that you can similarly pre-define the macro */ + /* FT_CONFIG_MODULES_H used to locate the file listing of the modules */ + /* that are statically linked to the library at compile time. By */ + /* default, this file is <freetype/config/ftmodule.h>. */ + /* */ + /* We highly recommend using the third method whenever possible. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** G E N E R A L F R E E T Y P E 2 C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Many compilers provide a non-ANSI 64-bit data type that can be used */ + /* by FreeType to speed up some computations. However, this will create */ + /* some problems when compiling the library in strict ANSI mode. */ + /* */ + /* For this reason, the use of 64-bit integers is normally disabled when */ + /* the __STDC__ macro is defined. You can however disable this by */ + /* defining the macro FT_CONFIG_OPTION_FORCE_INT64 here. */ + /* */ + /* For most compilers, this will only create compilation warnings when */ + /* building the library. */ + /* */ + /* ObNote: The compiler-specific 64-bit integers are detected in the */ + /* file "ftconfig.h" either statically or through the */ + /* `configure' script on supported platforms. */ + /* */ +#undef FT_CONFIG_OPTION_FORCE_INT64 + + + /*************************************************************************/ + /* */ + /* Gzip-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* 'gzip' program. This is mostly used to parse many of the PCF files */ + /* that come with XFree86. The implementation uses `zlib' to */ + /* partially uncompress the file on the fly (see src/base/ftgzip.c). */ + /* */ + /* Define this macro if you want to enable this "feature". Note that */ + /* this will however force you to link the zlib to any program that */ + /* also uses FreeType. */ + /* */ +#define FT_CONFIG_OPTION_USE_ZLIB + + + /*************************************************************************/ + /* */ + /* ZLib library selection */ + /* */ + /* This macro is only used when FT_CONFIG_OPTION_USE_ZLIB is defined. */ + /* It allows FreeType's "ftgzip" component to link to the system's */ + /* installation of the ZLib library. This is useful on systems like */ + /* Unix or VMS where it generally is already available. */ + /* */ + /* If you let it undefined, the component will use its own copy */ + /* of the zlib sources instead. These have been modified to be */ + /* included directly within the component and *not* export external */ + /* function names. This allows you to link any program with FreeType */ + /* _and_ ZLib without linking conflicts. */ + /* */ + /* Do not #undef this macro here since the build system might define */ + /* it for certain configurations only. */ + /* */ +/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */ + + + /*************************************************************************/ + /* */ + /* DLL export compilation */ + /* */ + /* When compiling FreeType as a DLL, some systems/compilers need a */ + /* special keyword in front OR after the return type of function */ + /* declarations. */ + /* */ + /* Two macros are used within the FreeType source code to define */ + /* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */ + /* */ + /* FT_EXPORT( return_type ) */ + /* */ + /* is used in a function declaration, as in */ + /* */ + /* FT_EXPORT( FT_Error ) */ + /* FT_Init_FreeType( FT_Library* alibrary ); */ + /* */ + /* */ + /* FT_EXPORT_DEF( return_type ) */ + /* */ + /* is used in a function definition, as in */ + /* */ + /* FT_EXPORT_DEF( FT_Error ) */ + /* FT_Init_FreeType( FT_Library* alibrary ) */ + /* { */ + /* ... some code ... */ + /* return FT_Err_Ok; */ + /* } */ + /* */ + /* You can provide your own implementation of FT_EXPORT and */ + /* FT_EXPORT_DEF here if you want. If you leave them undefined, they */ + /* will be later automatically defined as `extern return_type' to */ + /* allow normal compilation. */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_EXPORT(x) extern x */ +/* #define FT_EXPORT_DEF(x) x */ + + + /*************************************************************************/ + /* */ + /* Glyph Postscript Names handling */ + /* */ + /* By default, FreeType 2 is compiled with the `PSNames' module. This */ + /* module is in charge of converting a glyph name string into a */ + /* Unicode value, or return a Macintosh standard glyph name for the */ + /* use with the TrueType `post' table. */ + /* */ + /* Undefine this macro if you do not want `PSNames' compiled in your */ + /* build of FreeType. This has the following effects: */ + /* */ + /* - The TrueType driver will provide its own set of glyph names, */ + /* if you build it to support postscript names in the TrueType */ + /* `post' table. */ + /* */ + /* - The Type 1 driver will not be able to synthetize a Unicode */ + /* charmap out of the glyphs found in the fonts. */ + /* */ + /* You would normally undefine this configuration macro when building */ + /* a version of FreeType that doesn't contain a Type 1 or CFF driver. */ + /* */ +#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES + + + /*************************************************************************/ + /* */ + /* Postscript Names to Unicode Values support */ + /* */ + /* By default, FreeType 2 is built with the `PSNames' module compiled */ + /* in. Among other things, the module is used to convert a glyph name */ + /* into a Unicode value. This is especially useful in order to */ + /* synthetize on the fly a Unicode charmap from the CFF/Type 1 driver */ + /* through a big table named the `Adobe Glyph List' (AGL). */ + /* */ + /* Undefine this macro if you do not want the Adobe Glyph List */ + /* compiled in your `PSNames' module. The Type 1 driver will not be */ + /* able to synthetize a Unicode charmap out of the glyphs found in the */ + /* fonts. */ + /* */ +#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST + + + /*************************************************************************/ + /* */ + /* Support for Mac fonts */ + /* */ + /* Define this macro if you want support for outline fonts in Mac */ + /* format (mac dfont, mac resource, macbinary containing a mac */ + /* resource) on non-Mac platforms. */ + /* */ + /* Note that the `FOND' resource isn't checked. */ + /* */ +#define FT_CONFIG_OPTION_MAC_FONTS + + + /*************************************************************************/ + /* */ + /* Allow the use of FT_Incremental_Interface to load typefaces that */ + /* contain no glyph data, but supply it via a callback function. */ + /* This allows FreeType to be used with the PostScript language, using */ + /* the GhostScript interpreter. */ + /* */ +/* #define FT_CONFIG_OPTION_INCREMENTAL */ + + + /*************************************************************************/ + /* */ + /* The size in bytes of the render pool used by the scan-line converter */ + /* to do all of its work. */ + /* */ + /* This must be greater than 4KByte. */ + /* */ +#define FT_RENDER_POOL_SIZE 16384L + + + /*************************************************************************/ + /* */ + /* FT_MAX_MODULES */ + /* */ + /* The maximum number of modules that can be registered in a single */ + /* FreeType library object. 32 is the default. */ + /* */ +#define FT_MAX_MODULES 32 + + + /*************************************************************************/ + /* */ + /* Debug level */ + /* */ + /* FreeType can be compiled in debug or trace mode. In debug mode, */ + /* errors are reported through the `ftdebug' component. In trace */ + /* mode, additional messages are sent to the standard output during */ + /* execution. */ + /* */ + /* Define FT_DEBUG_LEVEL_ERROR to build the library in debug mode. */ + /* Define FT_DEBUG_LEVEL_TRACE to build it in trace mode. */ + /* */ + /* Don't define any of these macros to compile in `release' mode! */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_DEBUG_LEVEL_ERROR */ +/* #define FT_DEBUG_LEVEL_TRACE */ + + + /*************************************************************************/ + /* */ + /* Memory Debugging */ + /* */ + /* FreeType now comes with an integrated memory debugger that is */ + /* capable of detecting simple errors like memory leaks or double */ + /* deletes. To compile it within your build of the library, you */ + /* should define FT_DEBUG_MEMORY here. */ + /* */ + /* Note that the memory debugger is only activated at runtime when */ + /* when the _environment_ variable "FT2_DEBUG_MEMORY" is defined also! */ + /* */ + /* Do not #undef this macro here since the build system might define */ + /* it for certain configurations only. */ + /* */ +/* #define FT_DEBUG_MEMORY */ + + + /*************************************************************************/ + /* */ + /* Module errors */ + /* */ + /* If this macro is set (which is _not_ the default), the higher byte */ + /* of an error code gives the module in which the error has occurred, */ + /* while the lower byte is the real error code. */ + /* */ + /* Setting this macro makes sense for debugging purposes only, since */ + /* it would break source compatibility of certain programs that use */ + /* FreeType 2. */ + /* */ + /* More details can be found in the files ftmoderr.h and fterrors.h. */ + /* */ +#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS + + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** S F N T D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_EMBEDDED_BITMAPS if you want to support */ + /* embedded bitmaps in all formats using the SFNT module (namely */ + /* TrueType & OpenType). */ + /* */ +#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */ + /* load and enumerate the glyph Postscript names in a TrueType or */ + /* OpenType file. */ + /* */ + /* Note that when you do not compile the `PSNames' module by undefining */ + /* the above FT_CONFIG_OPTION_POSTSCRIPT_NAMES, the `sfnt' module will */ + /* contain additional code used to read the PS Names table from a font. */ + /* */ + /* (By default, the module uses `PSNames' to extract glyph names.) */ + /* */ +#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_SFNT_NAMES if your applications need to */ + /* access the internal name table in a SFNT-based format like TrueType */ + /* or OpenType. The name table contains various strings used to */ + /* describe the font, like family name, copyright, version, etc. It */ + /* does not contain any glyph name though. */ + /* */ + /* Accessing SFNT names is done through the functions declared in */ + /* `freetype/ftnames.h'. */ + /* */ +#define TT_CONFIG_OPTION_SFNT_NAMES + + + /*************************************************************************/ + /* */ + /* TrueType CMap support */ + /* */ + /* Here you can fine-tune which TrueType CMap table format shall be */ + /* supported. */ +#define TT_CONFIG_CMAP_FORMAT_0 +#define TT_CONFIG_CMAP_FORMAT_2 +#define TT_CONFIG_CMAP_FORMAT_4 +#define TT_CONFIG_CMAP_FORMAT_6 +#define TT_CONFIG_CMAP_FORMAT_8 +#define TT_CONFIG_CMAP_FORMAT_10 +#define TT_CONFIG_CMAP_FORMAT_12 + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** T R U E T Y P E D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_BYTECODE_INTERPRETER if you want to compile */ + /* a bytecode interpreter in the TrueType driver. Note that there are */ + /* important patent issues related to the use of the interpreter. */ + /* */ + /* By undefining this, you will only compile the code necessary to load */ + /* TrueType glyphs without hinting. */ + /* */ + /* Do not #undef this macro here, since the build system might */ + /* define it for certain configurations only. */ + /* */ +/* #define TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_UNPATENTED_HINTING (in addition to */ + /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER) to compile the unpatented */ + /* work-around hinting system. Note that for the moment, the algorithm */ + /* is only used when selected at runtime through the parameter tag */ + /* FT_PARAM_TAG_UNPATENTED_HINTING; or when the debug hook */ + /* FT_DEBUG_HOOK_UNPATENTED_HINTING is globally actived */ + /* */ +#define TT_CONFIG_OPTION_UNPATENTED_HINTING + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_INTERPRETER_SWITCH to compile the TrueType */ + /* bytecode interpreter with a huge switch statement, rather than a call */ + /* table. This results in smaller and faster code for a number of */ + /* architectures. */ + /* */ + /* Note however that on some compiler/processor combinations, undefining */ + /* this macro will generate faster, though larger, code. */ + /* */ +#define TT_CONFIG_OPTION_INTERPRETER_SWITCH + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED to compile the */ + /* TrueType glyph loader to use Apple's definition of how to handle */ + /* component offsets in composite glyphs. */ + /* */ + /* Apple and MS disagree on the default behavior of component offsets */ + /* in composites. Apple says that they should be scaled by the scale */ + /* factors in the transformation matrix (roughly, it's more complex) */ + /* while MS says they should not. OpenType defines two bits in the */ + /* composite flags array which can be used to disambiguate, but old */ + /* fonts will not have them. */ + /* */ + /* http://partners.adobe.com/asn/developer/opentype/glyf.html */ + /* http://fonts.apple.com/TTRefMan/RM06/Chap6glyf.html */ + /* */ +#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** T Y P E 1 D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* T1_MAX_DICT_DEPTH is the maximal depth of nest dictionaries and */ + /* arrays in the Type 1 stream (see t1load.c). A minimum of 4 is */ + /* required. */ + /* */ +#define T1_MAX_DICT_DEPTH 5 + + + /*************************************************************************/ + /* */ + /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ + /* calls during glyph loading. */ + /* */ +#define T1_MAX_SUBRS_CALLS 16 + + + /*************************************************************************/ + /* */ + /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */ + /* minimum of 16 is required. */ + /* */ + /* The Chinese font MingTiEG-Medium (CNS 11643 character set) needs 256. */ + /* */ +#define T1_MAX_CHARSTRINGS_OPERANDS 256 + + + /*************************************************************************/ + /* */ + /* Define this configuration macro if you want to prevent the */ + /* compilation of `t1afm', which is in charge of reading Type 1 AFM */ + /* files into an existing face. Note that if set, the T1 driver will be */ + /* unable to produce kerning distances. */ + /* */ +#undef T1_CONFIG_OPTION_NO_AFM + + + /*************************************************************************/ + /* */ + /* Define this configuration macro if you want to prevent the */ + /* compilation of the Multiple Masters font support in the Type 1 */ + /* driver. */ + /* */ +#undef T1_CONFIG_OPTION_NO_MM_SUPPORT + + /* */ + +/* + * The FT_CONFIG_OPTION_CHESTER_XXXX macros are used to toggle some recent + * improvements to the auto-hinter contributed by David Chester. They will + * most likely disappear completely in the next release. For now, you + * should always keep them defined. + * + */ +#define FT_CONFIG_OPTION_CHESTER_HINTS + +#ifdef FT_CONFIG_OPTION_CHESTER_HINTS + +#define FT_CONFIG_CHESTER_SMALL_F +#define FT_CONFIG_CHESTER_ASCENDER +#define FT_CONFIG_CHESTER_SERIF +#define FT_CONFIG_CHESTER_STEM +#define FT_CONFIG_CHESTER_BLUE_SCALE + +#endif /* FT_CONFIG_OPTION_CHESTER_HINTS */ + +FT_END_HEADER + + +#endif /* __FTOPTION_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/config/ftstdlib.h b/extra_lib/include/freetype/freetype/config/ftstdlib.h new file mode 100644 index 0000000..239bcf4 --- /dev/null +++ b/extra_lib/include/freetype/freetype/config/ftstdlib.h @@ -0,0 +1,147 @@ +/***************************************************************************/ +/* */ +/* ftstdlib.h */ +/* */ +/* ANSI-specific library and header configuration file (specification */ +/* only). */ +/* */ +/* Copyright 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to group all #includes to the ANSI C library that */ + /* FreeType normally requires. It also defines macros to rename the */ + /* standard functions within the FreeType source code. */ + /* */ + /* Load a file which defines __FTSTDLIB_H__ before this one to override */ + /* it. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTSTDLIB_H__ +#define __FTSTDLIB_H__ + + + /**********************************************************************/ + /* */ + /* integer limits */ + /* */ + /* UINT_MAX and ULONG_MAX are used to automatically compute the size */ + /* of `int' and `long' in bytes at compile-time. So far, this works */ + /* for all platforms the library has been tested on. */ + /* */ + /* Note that on the extremely rare platforms that do not provide */ + /* integer types that are _exactly_ 16 and 32 bits wide (e.g. some */ + /* old Crays where `int' is 36 bits), we do not make any guarantee */ + /* about the correct behaviour of FT2 with all fonts. */ + /* */ + /* In these case, "ftconfig.h" will refuse to compile anyway with a */ + /* message like "couldn't find 32-bit type" or something similar. */ + /* */ + /* IMPORTANT NOTE: We do not define aliases for heap management and */ + /* i/o routines (i.e. malloc/free/fopen/fread/...) */ + /* since these functions should all be encapsulated */ + /* by platform-specific implementations of */ + /* "ftsystem.c". */ + /* */ + /**********************************************************************/ + + +#include <limits.h> + +#define FT_UINT_MAX UINT_MAX +#define FT_ULONG_MAX ULONG_MAX + + + /**********************************************************************/ + /* */ + /* character and string processing */ + /* */ + /**********************************************************************/ + + +#include <ctype.h> + +#define ft_isalnum isalnum +#define ft_isupper isupper +#define ft_islower islower +#define ft_isdigit isdigit +#define ft_isxdigit isxdigit + + +#include <string.h> + +#define ft_strlen strlen +#define ft_strcat strcat +#define ft_strcmp strcmp +#define ft_strncmp strncmp +#define ft_memcpy memcpy +#define ft_strcpy strcpy +#define ft_strncpy strncpy +#define ft_memset memset +#define ft_memmove memmove +#define ft_memcmp memcmp + +#include <stdio.h> + +#define ft_sprintf sprintf + + + /**********************************************************************/ + /* */ + /* sorting */ + /* */ + /**********************************************************************/ + + +#include <stdlib.h> + +#define ft_qsort qsort +#define ft_exit exit /* only used to exit from unhandled exceptions */ + +#define ft_atoi atoi + + + /**********************************************************************/ + /* */ + /* execution control */ + /* */ + /**********************************************************************/ + + +#include <setjmp.h> + +#define ft_jmp_buf jmp_buf /* note: this cannot be a typedef since */ + /* jmp_buf is defined as a macro */ + /* on certain platforms */ + +#define ft_setjmp setjmp /* same thing here */ +#define ft_longjmp longjmp /* " */ + +/*avoids warning..*/ +#include <stddef.h> +#ifndef offsetof +#define offsetof(s,m) ((size_t)&(((s*)0)->m)) +#endif + + /* the following is only used for debugging purposes, i.e. when */ + /* FT_DEBUG_LEVEL_ERROR or FT_DEBUG_LEVEL_TRACE are defined */ + /* */ +#include <stdarg.h> + + +#endif /* __FTSTDLIB_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/freetype.h b/extra_lib/include/freetype/freetype/freetype.h new file mode 100644 index 0000000..285d8ef --- /dev/null +++ b/extra_lib/include/freetype/freetype/freetype.h @@ -0,0 +1,2986 @@ +/***************************************************************************/ +/* */ +/* freetype.h */ +/* */ +/* FreeType high-level API and common types (specification only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FT_FREETYPE_H +#error "`ft2build.h' hasn't been included yet!" +#error "Please always use macros to include FreeType header files." +#error "Example:" +#error " #include <ft2build.h>" +#error " #include FT_FREETYPE_H" +#endif + + +#ifndef __FREETYPE_H__ +#define __FREETYPE_H__ + + + /*************************************************************************/ + /* */ + /* The `raster' component duplicates some of the declarations in */ + /* freetype.h for stand-alone use if _FREETYPE_ isn't defined. */ + /* */ + + + /*************************************************************************/ + /* */ + /* The FREETYPE_MAJOR and FREETYPE_MINOR macros are used to version the */ + /* new FreeType design, which is able to host several kinds of font */ + /* drivers. It starts at 2.0. */ + /* */ +#define FREETYPE_MAJOR 2 +#define FREETYPE_MINOR 1 +#define FREETYPE_PATCH 7 + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_ERRORS_H +#include FT_TYPES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* B A S I C T Y P E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* base_interface */ + /* */ + /* <Title> */ + /* Base Interface */ + /* */ + /* <Abstract> */ + /* The FreeType 2 base font interface. */ + /* */ + /* <Description> */ + /* This section describes the public high-level API of FreeType 2. */ + /* */ + /* <Order> */ + /* FT_Library */ + /* FT_Face */ + /* FT_Size */ + /* FT_GlyphSlot */ + /* FT_CharMap */ + /* FT_Encoding */ + /* */ + /* FT_FaceRec */ + /* */ + /* FT_FACE_FLAG_SCALABLE */ + /* FT_FACE_FLAG_FIXED_SIZES */ + /* FT_FACE_FLAG_FIXED_WIDTH */ + /* FT_FACE_FLAG_HORIZONTAL */ + /* FT_FACE_FLAG_VERTICAL */ + /* FT_FACE_FLAG_SFNT */ + /* FT_FACE_FLAG_KERNING */ + /* FT_FACE_FLAG_MULTIPLE_MASTERS */ + /* FT_FACE_FLAG_GLYPH_NAMES */ + /* FT_FACE_FLAG_EXTERNAL_STREAM */ + /* FT_FACE_FLAG_FAST_GLYPHS */ + /* */ + /* FT_STYLE_FLAG_BOLD */ + /* FT_STYLE_FLAG_ITALIC */ + /* */ + /* FT_SizeRec */ + /* FT_Size_Metrics */ + /* */ + /* FT_GlyphSlotRec */ + /* FT_Glyph_Metrics */ + /* FT_SubGlyph */ + /* */ + /* FT_Bitmap_Size */ + /* */ + /* FT_Init_FreeType */ + /* FT_Done_FreeType */ + /* FT_Library_Version */ + /* */ + /* FT_New_Face */ + /* FT_Done_Face */ + /* FT_New_Memory_Face */ + /* FT_Open_Face */ + /* FT_Open_Args */ + /* FT_Parameter */ + /* FT_Attach_File */ + /* FT_Attach_Stream */ + /* */ + /* FT_Set_Char_Size */ + /* FT_Set_Pixel_Sizes */ + /* FT_Set_Transform */ + /* FT_Load_Glyph */ + /* FT_Get_Char_Index */ + /* FT_Get_Name_Index */ + /* FT_Load_Char */ + /* */ + /* FT_OPEN_MEMORY */ + /* FT_OPEN_STREAM */ + /* FT_OPEN_PATHNAME */ + /* FT_OPEN_DRIVER */ + /* FT_OPEN_PARAMS */ + /* */ + /* FT_LOAD_DEFAULT */ + /* FT_LOAD_RENDER */ + /* FT_LOAD_MONOCHROME */ + /* FT_LOAD_LINEAR_DESIGN */ + /* FT_LOAD_NO_SCALE */ + /* FT_LOAD_NO_HINTING */ + /* FT_LOAD_NO_BITMAP */ + /* FT_LOAD_CROP_BITMAP */ + /* */ + /* FT_LOAD_VERTICAL_LAYOUT */ + /* FT_LOAD_IGNORE_TRANSFORM */ + /* FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH */ + /* FT_LOAD_FORCE_AUTOHINT */ + /* FT_LOAD_NO_RECURSE */ + /* FT_LOAD_PEDANTIC */ + /* */ + /* FT_LOAD_TARGET_NORMAL */ + /* FT_LOAD_TARGET_LIGHT */ + /* FT_LOAD_TARGET_MONO */ + /* FT_LOAD_TARGET_LCD */ + /* FT_LOAD_TARGET_LCD_V */ + /* */ + /* FT_Render_Glyph */ + /* FT_Render_Mode */ + /* FT_Get_Kerning */ + /* FT_Kerning_Mode */ + /* FT_Get_Glyph_Name */ + /* FT_Get_Postscript_Name */ + /* */ + /* FT_CharMapRec */ + /* FT_Select_Charmap */ + /* FT_Set_Charmap */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Glyph_Metrics */ + /* */ + /* <Description> */ + /* A structure used to model the metrics of a single glyph. The */ + /* values are expressed in 26.6 fractional pixel format; if the flag */ + /* FT_LOAD_NO_SCALE is used, values are returned in font units */ + /* instead. */ + /* */ + /* <Fields> */ + /* width :: The glyph's width. */ + /* */ + /* height :: The glyph's height. */ + /* */ + /* horiBearingX :: Horizontal left side bearing. */ + /* */ + /* horiBearingY :: Horizontal top side bearing. */ + /* */ + /* horiAdvance :: Horizontal advance width. */ + /* */ + /* vertBearingX :: Vertical left side bearing. */ + /* */ + /* vertBearingY :: Vertical top side bearing. */ + /* */ + /* vertAdvance :: Vertical advance height. */ + /* */ + typedef struct FT_Glyph_Metrics_ + { + FT_Pos width; /* glyph width */ + FT_Pos height; /* glyph height */ + + FT_Pos horiBearingX; /* left side bearing in horizontal layouts */ + FT_Pos horiBearingY; /* top side bearing in horizontal layouts */ + FT_Pos horiAdvance; /* advance width for horizontal layout */ + + FT_Pos vertBearingX; /* left side bearing in vertical layouts */ + FT_Pos vertBearingY; /* top side bearing in vertical layouts */ + FT_Pos vertAdvance; /* advance height for vertical layout */ + + } FT_Glyph_Metrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Bitmap_Size */ + /* */ + /* <Description> */ + /* This structure models the size of a bitmap strike (i.e., a bitmap */ + /* instance of the font for a given resolution) in a fixed-size font */ + /* face. It is used for the `available_sizes' field of the */ + /* @FT_FaceRec structure. */ + /* */ + /* <Fields> */ + /* height :: The (vertical) baseline-to-baseline distance in pixels. */ + /* It makes most sense to define the height of a bitmap */ + /* font in this way. */ + /* */ + /* width :: The average width of the font (in pixels). Since the */ + /* algorithms to compute this value are different for the */ + /* various bitmap formats, it can only give an additional */ + /* hint if the `height' value isn't sufficient to select */ + /* the proper font. For monospaced fonts the average width */ + /* is the same as the maximum width. */ + /* */ + /* size :: The point size in 26.6 fractional format this font shall */ + /* represent (for a given vertical resolution). */ + /* */ + /* x_ppem :: The horizontal ppem value (in 26.6 fractional format). */ + /* */ + /* y_ppem :: The vertical ppem value (in 26.6 fractional format). */ + /* */ + /* <Note> */ + /* The values in this structure are taken from the bitmap font. If */ + /* the font doesn't provide a parameter it is set to zero to indicate */ + /* that the information is not available. */ + /* */ + /* The following formula converts from dpi to ppem: */ + /* */ + /* ppem = size * dpi / 72 */ + /* */ + /* where `size' is in points. */ + /* */ + /* Windows FNT: */ + /* The `size', `x_ppem', and `y_ppem' parameters are not reliable: */ + /* There exist fonts (e.g. app850.fon) which have a wrong size for */ + /* some subfonts; since FNT files don't contain ppem but dpi values */ + /* the computed x_ppem and y_ppem numbers are thus wrong also. */ + /* */ + /* TrueType embedded bitmaps: */ + /* `size', `width', and `height' values are not contained in the */ + /* bitmap strike itself. They are computed from the global font */ + /* parameters. */ + /* */ + typedef struct FT_Bitmap_Size_ + { + FT_Short height; + FT_Short width; + + FT_Pos size; + + FT_Pos x_ppem; + FT_Pos y_ppem; + + } FT_Bitmap_Size; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* O B J E C T C L A S S E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Library */ + /* */ + /* <Description> */ + /* A handle to a FreeType library instance. Each `library' is */ + /* completely independent from the others; it is the `root' of a set */ + /* of objects like fonts, faces, sizes, etc. */ + /* */ + /* It also embeds a memory manager (see @FT_Memory), as well as a */ + /* scan-line converter object (see @FT_Raster). */ + /* */ + /* <Note> */ + /* Library objects are normally created by @FT_Init_FreeType, and */ + /* destroyed with @FT_Done_FreeType. */ + /* */ + typedef struct FT_LibraryRec_ *FT_Library; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Module */ + /* */ + /* <Description> */ + /* A handle to a given FreeType module object. Each module can be a */ + /* font driver, a renderer, or anything else that provides services */ + /* to the formers. */ + /* */ + typedef struct FT_ModuleRec_* FT_Module; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Driver */ + /* */ + /* <Description> */ + /* A handle to a given FreeType font driver object. Each font driver */ + /* is a special module capable of creating faces from font files. */ + /* */ + typedef struct FT_DriverRec_* FT_Driver; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Renderer */ + /* */ + /* <Description> */ + /* A handle to a given FreeType renderer. A renderer is a special */ + /* module in charge of converting a glyph image to a bitmap, when */ + /* necessary. Each renderer supports a given glyph image format, and */ + /* one or more target surface depths. */ + /* */ + typedef struct FT_RendererRec_* FT_Renderer; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Face */ + /* */ + /* <Description> */ + /* A handle to a given typographic face object. A face object models */ + /* a given typeface, in a given style. */ + /* */ + /* <Note> */ + /* Each face object also owns a single @FT_GlyphSlot object, as well */ + /* as one or more @FT_Size objects. */ + /* */ + /* Use @FT_New_Face or @FT_Open_Face to create a new face object from */ + /* a given filepathname or a custom input stream. */ + /* */ + /* Use @FT_Done_Face to destroy it (along with its slot and sizes). */ + /* */ + /* <Also> */ + /* The @FT_FaceRec details the publicly accessible fields of a given */ + /* face object. */ + /* */ + typedef struct FT_FaceRec_* FT_Face; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Size */ + /* */ + /* <Description> */ + /* A handle to a given size object. Such an object models the data */ + /* that depends on the current _resolution_ and _character size_ in a */ + /* given @FT_Face. */ + /* */ + /* <Note> */ + /* Each face object owns one or more sizes. There is however a */ + /* single _active_ size for the face at any time that will be used by */ + /* functions like @FT_Load_Glyph, @FT_Get_Kerning, etc. */ + /* */ + /* You can use the @FT_Activate_Size API to change the current */ + /* active size of any given face. */ + /* */ + /* <Also> */ + /* The @FT_SizeRec structure details the publicly accessible fields */ + /* of a given face object. */ + /* */ + typedef struct FT_SizeRec_* FT_Size; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_GlyphSlot */ + /* */ + /* <Description> */ + /* A handle to a given `glyph slot'. A slot is a container where it */ + /* is possible to load any one of the glyphs contained in its parent */ + /* face. */ + /* */ + /* In other words, each time you call @FT_Load_Glyph or */ + /* @FT_Load_Char, the slot's content is erased by the new glyph data, */ + /* i.e. the glyph's metrics, its image (bitmap or outline), and */ + /* other control information. */ + /* */ + /* <Also> */ + /* @FT_GlyphSlotRec details the publicly accessible glyph fields. */ + /* */ + typedef struct FT_GlyphSlotRec_* FT_GlyphSlot; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_CharMap */ + /* */ + /* <Description> */ + /* A handle to a given character map. A charmap is used to translate */ + /* character codes in a given encoding into glyph indexes for its */ + /* parent's face. Some font formats may provide several charmaps per */ + /* font. */ + /* */ + /* Each face object owns zero or more charmaps, but only one of them */ + /* can be "active" and used by @FT_Get_Char_Index or @FT_Load_Char. */ + /* */ + /* The list of available charmaps in a face is available through the */ + /* "face->num_charmaps" and "face->charmaps" fields of @FT_FaceRec. */ + /* */ + /* The currently active charmap is available as "face->charmap". */ + /* You should call @FT_Set_Charmap to change it. */ + /* */ + /* <Note> */ + /* When a new face is created (either through @FT_New_Face or */ + /* @FT_Open_Face), the library looks for a Unicode charmap within */ + /* the list and automatically activates it. */ + /* */ + /* <Also> */ + /* The @FT_CharMapRec details the publicly accessible fields of a */ + /* given character map. */ + /* */ + typedef struct FT_CharMapRec_* FT_CharMap; + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_ENC_TAG */ + /* */ + /* <Description> */ + /* This macro converts four letter tags into an unsigned long. It is */ + /* used to define "encoding" identifiers (see @FT_Encoding). */ + /* */ + /* <Note> */ + /* Since many 16bit compilers don't like 32bit enumerations, you */ + /* should redefine this macro in case of problems to something like */ + /* this: */ + /* */ + /* #define FT_ENC_TAG( value, a, b, c, d ) (value) */ + /* */ + /* to get a simple enumeration without assigning special numbers. */ + /* */ + +#ifndef FT_ENC_TAG +#define FT_ENC_TAG( value, a, b, c, d ) \ + value = ( ( (FT_UInt32)(a) << 24 ) | \ + ( (FT_UInt32)(b) << 16 ) | \ + ( (FT_UInt32)(c) << 8 ) | \ + (FT_UInt32)(d) ) + +#endif /* FT_ENC_TAG */ + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Encoding */ + /* */ + /* <Description> */ + /* An enumeration used to specify encodings supported by charmaps. */ + /* Used in the @FT_Select_Charmap API function. */ + /* */ + /* <Note> */ + /* Because of 32-bit charcodes defined in Unicode (i.e., surrogates), */ + /* all character codes must be expressed as FT_Longs. */ + /* */ + /* The values of this type correspond to specific character */ + /* repertories (i.e. charsets), and not to text encoding methods */ + /* (like UTF-8, UTF-16, GB2312_EUC, etc.). */ + /* */ + /* Other encodings might be defined in the future. */ + /* */ + /* <Values> */ + /* FT_ENCODING_NONE :: */ + /* The encoding value 0 is reserved. */ + /* */ + /* FT_ENCODING_UNICODE :: */ + /* Corresponds to the Unicode character set. This value covers */ + /* all versions of the Unicode repertoire, including ASCII and */ + /* Latin-1. Most fonts include a Unicode charmap, but not all */ + /* of them. */ + /* */ + /* FT_ENCODING_MS_SYMBOL :: */ + /* Corresponds to the Microsoft Symbol encoding, used to encode */ + /* mathematical symbols in the 32..255 character code range. For */ + /* more information, see `http://www.ceviz.net/symbol.htm'. */ + /* */ + /* FT_ENCODING_SJIS :: */ + /* Corresponds to Japanese SJIS encoding. More info at */ + /* at `http://langsupport.japanreference.com/encoding.shtml'. */ + /* See note on multi-byte encodings below. */ + /* */ + /* FT_ENCODING_GB2312 :: */ + /* Corresponds to an encoding system for Simplified Chinese as used */ + /* used in mainland China. */ + /* */ + /* FT_ENCODING_BIG5 :: */ + /* Corresponds to an encoding system for Traditional Chinese as used */ + /* in Taiwan and Hong Kong. */ + /* */ + /* FT_ENCODING_WANSUNG :: */ + /* Corresponds to the Korean encoding system known as Wansung. */ + /* For more information see */ + /* `http://www.microsoft.com/typography/unicode/949.txt'. */ + /* */ + /* FT_ENCODING_JOHAB :: */ + /* The Korean standard character set (KS C-5601-1992), which */ + /* corresponds to MS Windows code page 1361. This character set */ + /* includes all possible Hangeul character combinations. */ + /* */ + /* FT_ENCODING_ADOBE_LATIN_1 :: */ + /* Corresponds to a Latin-1 encoding as defined in a Type 1 */ + /* Postscript font. It is limited to 256 character codes. */ + /* */ + /* FT_ENCODING_ADOBE_STANDARD :: */ + /* Corresponds to the Adobe Standard encoding, as found in Type 1, */ + /* CFF, and OpenType/CFF fonts. It is limited to 256 character */ + /* codes. */ + /* */ + /* FT_ENCODING_ADOBE_EXPERT :: */ + /* Corresponds to the Adobe Expert encoding, as found in Type 1, */ + /* CFF, and OpenType/CFF fonts. It is limited to 256 character */ + /* codes. */ + /* */ + /* FT_ENCODING_ADOBE_CUSTOM :: */ + /* Corresponds to a custom encoding, as found in Type 1, CFF, and */ + /* OpenType/CFF fonts. It is limited to 256 character codes. */ + /* */ + /* FT_ENCODING_APPLE_ROMAN :: */ + /* Corresponds to the 8-bit Apple roman encoding. Many TrueType and */ + /* OpenType fonts contain a charmap for this encoding, since older */ + /* versions of Mac OS are able to use it. */ + /* */ + /* FT_ENCODING_OLD_LATIN_2 :: */ + /* This value is deprecated and was never used nor reported by */ + /* FreeType. Don't use or test for it. */ + /* */ + /* FT_ENCODING_MS_SJIS :: */ + /* Same as FT_ENCODING_SJIS. Deprecated. */ + /* */ + /* FT_ENCODING_MS_GB2312 :: */ + /* Same as FT_ENCODING_GB2312. Deprecated. */ + /* */ + /* FT_ENCODING_MS_BIG5 :: */ + /* Same as FT_ENCODING_BIG5. Deprecated. */ + /* */ + /* FT_ENCODING_MS_WANSUNG :: */ + /* Same as FT_ENCODING_WANSUNG. Deprecated. */ + /* */ + /* FT_ENCODING_MS_JOHAB :: */ + /* Same as FT_ENCODING_JOHAB. Deprecated. */ + /* */ + /* <Note> */ + /* By default, FreeType automatically synthetizes a Unicode charmap */ + /* for Postscript fonts, using their glyph names dictionaries. */ + /* However, it will also report the encodings defined explicitly in */ + /* the font file, for the cases when they are needed, with the Adobe */ + /* values as well. */ + /* */ + typedef enum FT_Encoding_ + { + FT_ENC_TAG( FT_ENCODING_NONE, 0, 0, 0, 0 ), + + FT_ENC_TAG( FT_ENCODING_MS_SYMBOL, 's', 'y', 'm', 'b' ), + FT_ENC_TAG( FT_ENCODING_UNICODE, 'u', 'n', 'i', 'c' ), + + FT_ENC_TAG( FT_ENCODING_SJIS, 's', 'j', 'i', 's' ), + FT_ENC_TAG( FT_ENCODING_GB2312, 'g', 'b', ' ', ' ' ), + FT_ENC_TAG( FT_ENCODING_BIG5, 'b', 'i', 'g', '5' ), + FT_ENC_TAG( FT_ENCODING_WANSUNG, 'w', 'a', 'n', 's' ), + FT_ENC_TAG( FT_ENCODING_JOHAB, 'j', 'o', 'h', 'a' ), + + /* for backwards compatibility */ + FT_ENCODING_MS_SJIS = FT_ENCODING_SJIS, + FT_ENCODING_MS_GB2312 = FT_ENCODING_GB2312, + FT_ENCODING_MS_BIG5 = FT_ENCODING_BIG5, + FT_ENCODING_MS_WANSUNG = FT_ENCODING_WANSUNG, + FT_ENCODING_MS_JOHAB = FT_ENCODING_JOHAB, + + FT_ENC_TAG( FT_ENCODING_ADOBE_STANDARD, 'A', 'D', 'O', 'B' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_EXPERT, 'A', 'D', 'B', 'E' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_CUSTOM, 'A', 'D', 'B', 'C' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_LATIN_1, 'l', 'a', 't', '1' ), + + FT_ENC_TAG( FT_ENCODING_OLD_LATIN_2, 'l', 'a', 't', '2' ), + + FT_ENC_TAG( FT_ENCODING_APPLE_ROMAN, 'a', 'r', 'm', 'n' ) + + } FT_Encoding; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* ft_encoding_xxx */ + /* */ + /* <Description> */ + /* These constants are deprecated; use the corresponding @FT_Encoding */ + /* values instead. */ + /* */ + /* <Values> */ + /* ft_encoding_none :: see @FT_ENCODING_NONE */ + /* ft_encoding_unicode :: see @FT_ENCODING_UNICODE */ + /* ft_encoding_latin_2 :: see @FT_ENCODING_OLD_LATIN_2 */ + /* ft_encoding_symbol :: see @FT_ENCODING_MS_SYMBOL */ + /* ft_encoding_sjis :: see @FT_ENCODING_SJIS */ + /* ft_encoding_gb2312 :: see @FT_ENCODING_GB2312 */ + /* ft_encoding_big5 :: see @FT_ENCODING_BIG5 */ + /* ft_encoding_wansung :: see @FT_ENCODING_WANSUNG */ + /* ft_encoding_johab :: see @FT_ENCODING_JOHAB */ + /* */ + /* ft_encoding_adobe_standard :: see @FT_ENCODING_ADOBE_STANDARD */ + /* ft_encoding_adobe_expert :: see @FT_ENCODING_ADOBE_EXPERT */ + /* ft_encoding_adobe_custom :: see @FT_ENCODING_ADOBE_CUSTOM */ + /* ft_encoding_latin_1 :: see @FT_ENCODING_ADOBE_LATIN_1 */ + /* */ + /* ft_encoding_apple_roman :: see @FT_ENCODING_APPLE_ROMAN */ + /* */ +#define ft_encoding_none FT_ENCODING_NONE +#define ft_encoding_unicode FT_ENCODING_UNICODE +#define ft_encoding_symbol FT_ENCODING_MS_SYMBOL +#define ft_encoding_latin_1 FT_ENCODING_ADOBE_LATIN_1 +#define ft_encoding_latin_2 FT_ENCODING_OLD_LATIN_2 +#define ft_encoding_sjis FT_ENCODING_SJIS +#define ft_encoding_gb2312 FT_ENCODING_GB2312 +#define ft_encoding_big5 FT_ENCODING_BIG5 +#define ft_encoding_wansung FT_ENCODING_WANSUNG +#define ft_encoding_johab FT_ENCODING_JOHAB + +#define ft_encoding_adobe_standard FT_ENCODING_ADOBE_STANDARD +#define ft_encoding_adobe_expert FT_ENCODING_ADOBE_EXPERT +#define ft_encoding_adobe_custom FT_ENCODING_ADOBE_CUSTOM +#define ft_encoding_apple_roman FT_ENCODING_APPLE_ROMAN + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_CharMapRec */ + /* */ + /* <Description> */ + /* The base charmap structure. */ + /* */ + /* <Fields> */ + /* face :: A handle to the parent face object. */ + /* */ + /* encoding :: An @FT_Encoding tag identifying the charmap. Use */ + /* this with @FT_Select_Charmap. */ + /* */ + /* platform_id :: An ID number describing the platform for the */ + /* following encoding ID. This comes directly from */ + /* the TrueType specification and should be emulated */ + /* for other formats. */ + /* */ + /* encoding_id :: A platform specific encoding number. This also */ + /* comes from the TrueType specification and should be */ + /* emulated similarly. */ + /* */ + typedef struct FT_CharMapRec_ + { + FT_Face face; + FT_Encoding encoding; + FT_UShort platform_id; + FT_UShort encoding_id; + + } FT_CharMapRec; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* B A S E O B J E C T C L A S S E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Face_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an FT_Face_InternalRec structure, used to */ + /* model private data of a given @FT_Face object. */ + /* */ + /* This structure might change between releases of FreeType 2 and is */ + /* not generally available to client applications. */ + /* */ + typedef struct FT_Face_InternalRec_* FT_Face_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_FaceRec */ + /* */ + /* <Description> */ + /* FreeType root face class structure. A face object models the */ + /* resolution and point-size independent data found in a font file. */ + /* */ + /* <Fields> */ + /* num_faces :: In the case where the face is located in a */ + /* collection (i.e., a file which embeds */ + /* several faces), this is the total number of */ + /* faces found in the resource. 1 by default. */ + /* */ + /* face_index :: The index of the face in its font file. */ + /* Usually, this is 0 for all normal font */ + /* formats. It can be more in the case of */ + /* collections (which embed several fonts in a */ + /* single resource/file). */ + /* */ + /* face_flags :: A set of bit flags that give important */ + /* information about the face; see the */ + /* @FT_FACE_FLAG_XXX constants for details. */ + /* */ + /* style_flags :: A set of bit flags indicating the style of */ + /* the face (i.e., italic, bold, underline, */ + /* etc). See the @FT_STYLE_FLAG_XXX */ + /* constants. */ + /* */ + /* num_glyphs :: The total number of glyphs in the face. */ + /* */ + /* family_name :: The face's family name. This is an ASCII */ + /* string, usually in English, which describes */ + /* the typeface's family (like `Times New */ + /* Roman', `Bodoni', `Garamond', etc). This */ + /* is a least common denominator used to list */ + /* fonts. Some formats (TrueType & OpenType) */ + /* provide localized and Unicode versions of */ + /* this string. Applications should use the */ + /* format specific interface to access them. */ + /* */ + /* style_name :: The face's style name. This is an ASCII */ + /* string, usually in English, which describes */ + /* the typeface's style (like `Italic', */ + /* `Bold', `Condensed', etc). Not all font */ + /* formats provide a style name, so this field */ + /* is optional, and can be set to NULL. As */ + /* for `family_name', some formats provide */ + /* localized/Unicode versions of this string. */ + /* Applications should use the format specific */ + /* interface to access them. */ + /* */ + /* num_fixed_sizes :: The number of fixed sizes available in this */ + /* face. This should be set to 0 for scalable */ + /* fonts, unless its face includes a set of */ + /* glyphs (called a `strike') for the */ + /* specified sizes. */ + /* */ + /* available_sizes :: An array of sizes specifying the available */ + /* bitmap/graymap sizes that are contained in */ + /* in the font face. Should be set to NULL if */ + /* the field `num_fixed_sizes' is set to 0. */ + /* */ + /* num_charmaps :: The total number of character maps in the */ + /* face. */ + /* */ + /* charmaps :: A table of pointers to the face's charmaps. */ + /* Used to scan the list of available charmaps */ + /* -- this table might change after a call to */ + /* @FT_Attach_File or @FT_Attach_Stream (e.g. */ + /* if used to hook an additional encoding or */ + /* CMap to the face object). */ + /* */ + /* generic :: A field reserved for client uses. See the */ + /* @FT_Generic type description. */ + /* */ + /* bbox :: The font bounding box. Coordinates are */ + /* expressed in font units (see units_per_EM). */ + /* The box is large enough to contain any */ + /* glyph from the font. Thus, bbox.yMax can */ + /* be seen as the `maximal ascender', */ + /* bbox.yMin as the `minimal descender', and */ + /* the maximal glyph width is given by */ + /* `bbox.xMax-bbox.xMin' (not to be confused */ + /* with the maximal _advance_width_). Only */ + /* relevant for scalable formats. */ + /* */ + /* units_per_EM :: The number of font units per EM square for */ + /* this face. This is typically 2048 for */ + /* TrueType fonts, 1000 for Type1 fonts, and */ + /* should be set to the (unrealistic) value 1 */ + /* for fixed-sizes fonts. Only relevant for */ + /* scalable formats. */ + /* */ + /* ascender :: The face's ascender is the vertical */ + /* distance from the baseline to the topmost */ + /* point of any glyph in the face. This */ + /* field's value is positive, expressed in */ + /* font units. Some font designs use a value */ + /* different from `bbox.yMax'. Only relevant */ + /* for scalable formats. */ + /* */ + /* descender :: The face's descender is the vertical */ + /* distance from the baseline to the */ + /* bottommost point of any glyph in the face. */ + /* This field's value is *negative* for values */ + /* below the baseline. It is expressed in */ + /* font units. Some font designs use a value */ + /* different from `bbox.yMin'. Only relevant */ + /* for scalable formats. */ + /* */ + /* height :: The face's height is the vertical distance */ + /* from one baseline to the next when writing */ + /* several lines of text. Its value is always */ + /* positive, expressed in font units. The */ + /* value can be computed as */ + /* `ascender+descender+line_gap' where the */ + /* value of `line_gap' is also called */ + /* `external leading'. Only relevant for */ + /* scalable formats. */ + /* */ + /* max_advance_width :: The maximal advance width, in font units, */ + /* for all glyphs in this face. This can be */ + /* used to make word wrapping computations */ + /* faster. Only relevant for scalable */ + /* formats. */ + /* */ + /* max_advance_height :: The maximal advance height, in font units, */ + /* for all glyphs in this face. This is only */ + /* relevant for vertical layouts, and should */ + /* be set to the `height' for fonts that do */ + /* not provide vertical metrics. Only */ + /* relevant for scalable formats. */ + /* */ + /* underline_position :: The position, in font units, of the */ + /* underline line for this face. It's the */ + /* center of the underlining stem. Only */ + /* relevant for scalable formats. */ + /* */ + /* underline_thickness :: The thickness, in font units, of the */ + /* underline for this face. Only relevant for */ + /* scalable formats. */ + /* */ + /* glyph :: The face's associated glyph slot(s). This */ + /* object is created automatically with a new */ + /* face object. However, certain kinds of */ + /* applications (mainly tools like converters) */ + /* can need more than one slot to ease their */ + /* task. */ + /* */ + /* size :: The current active size for this face. */ + /* */ + /* charmap :: The current active charmap for this face. */ + /* */ + typedef struct FT_FaceRec_ + { + FT_Long num_faces; + FT_Long face_index; + + FT_Long face_flags; + FT_Long style_flags; + + FT_Long num_glyphs; + + FT_String* family_name; + FT_String* style_name; + + FT_Int num_fixed_sizes; + FT_Bitmap_Size* available_sizes; + + FT_Int num_charmaps; + FT_CharMap* charmaps; + + FT_Generic generic; + + /*# the following are only relevant to scalable outlines */ + FT_BBox bbox; + + FT_UShort units_per_EM; + FT_Short ascender; + FT_Short descender; + FT_Short height; + + FT_Short max_advance_width; + FT_Short max_advance_height; + + FT_Short underline_position; + FT_Short underline_thickness; + + FT_GlyphSlot glyph; + FT_Size size; + FT_CharMap charmap; + + /*@private begin */ + + FT_Driver driver; + FT_Memory memory; + FT_Stream stream; + + FT_ListRec sizes_list; + + FT_Generic autohint; + void* extensions; + + FT_Face_Internal internal; + + /*@private end */ + + } FT_FaceRec; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_FACE_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit flags used in the 'face_flags' field of the */ + /* @FT_FaceRec structure. They inform client applications of */ + /* properties of the corresponding face. */ + /* */ + /* <Values> */ + /* FT_FACE_FLAG_SCALABLE :: */ + /* Indicates that the face provides vectorial outlines. This */ + /* doesn't prevent embedded bitmaps, i.e., a face can have both */ + /* this bit and @FT_FACE_FLAG_FIXED_SIZES set. */ + /* */ + /* FT_FACE_FLAG_FIXED_SIZES :: */ + /* Indicates that the face contains `fixed sizes', i.e., bitmap */ + /* strikes for some given pixel sizes. See the `num_fixed_sizes' */ + /* and `available_sizes' fields of @FT_FaceRec. */ + /* */ + /* FT_FACE_FLAG_FIXED_WIDTH :: */ + /* Indicates that the face contains fixed-width characters (like */ + /* Courier, Lucido, MonoType, etc.). */ + /* */ + /* FT_FACE_FLAG_SFNT :: */ + /* Indicates that the face uses the `sfnt' storage scheme. For */ + /* now, this means TrueType and OpenType. */ + /* */ + /* FT_FACE_FLAG_HORIZONTAL :: */ + /* Indicates that the face contains horizontal glyph metrics. This */ + /* should be set for all common formats. */ + /* */ + /* FT_FACE_FLAG_VERTICAL :: */ + /* Indicates that the face contains vertical glyph metrics. This */ + /* is only available in some formats, not all of them. */ + /* */ + /* FT_FACE_FLAG_KERNING :: */ + /* Indicates that the face contains kerning information. If set, */ + /* the kerning distance can be retrieved through the function */ + /* @FT_Get_Kerning. Note that if unset, this function will always */ + /* return the vector (0,0). */ + /* */ + /* FT_FACE_FLAG_FAST_GLYPHS :: */ + /* THIS FLAG IS DEPRECATED. DO NOT USE OR TEST IT. */ + /* */ + /* FT_FACE_FLAG_MULTIPLE_MASTERS :: */ + /* Indicates that the font contains multiple masters and is capable */ + /* of interpolating between them. See the multiple-masters */ + /* specific API for details. */ + /* */ + /* FT_FACE_FLAG_GLYPH_NAMES :: */ + /* Indicates that the font contains glyph names that can be */ + /* retrieved through @FT_Get_Glyph_Name. Note that some TrueType */ + /* fonts contain broken glyph name tables. Use the function */ + /* @FT_Has_PS_Glyph_Names when needed. */ + /* */ + /* FT_FACE_FLAG_EXTERNAL_STREAM :: */ + /* Used internally by FreeType to indicate that a face's stream was */ + /* provided by the client application and should not be destroyed */ + /* when @FT_Done_Face is called. Don't read or test this flag. */ + /* */ +#define FT_FACE_FLAG_SCALABLE ( 1L << 0 ) +#define FT_FACE_FLAG_FIXED_SIZES ( 1L << 1 ) +#define FT_FACE_FLAG_FIXED_WIDTH ( 1L << 2 ) +#define FT_FACE_FLAG_SFNT ( 1L << 3 ) +#define FT_FACE_FLAG_HORIZONTAL ( 1L << 4 ) +#define FT_FACE_FLAG_VERTICAL ( 1L << 5 ) +#define FT_FACE_FLAG_KERNING ( 1L << 6 ) +#define FT_FACE_FLAG_FAST_GLYPHS ( 1L << 7 ) +#define FT_FACE_FLAG_MULTIPLE_MASTERS ( 1L << 8 ) +#define FT_FACE_FLAG_GLYPH_NAMES ( 1L << 9 ) +#define FT_FACE_FLAG_EXTERNAL_STREAM ( 1L << 10 ) + + /* */ + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_HAS_HORIZONTAL( face ) */ + /* */ + /* @description: */ + /* A macro that returns true whenever a face object contains */ + /* horizontal metrics (this is true for all font formats though). */ + /* */ + /* @also: */ + /* @FT_HAS_VERTICAL can be used to check for vertical metrics. */ + /* */ +#define FT_HAS_HORIZONTAL( face ) \ + ( face->face_flags & FT_FACE_FLAG_HORIZONTAL ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_HAS_VERTICAL( face ) */ + /* */ + /* @description: */ + /* A macro that returns true whenever a face object contains vertical */ + /* metrics. */ + /* */ +#define FT_HAS_VERTICAL( face ) \ + ( face->face_flags & FT_FACE_FLAG_VERTICAL ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_HAS_KERNING( face ) */ + /* */ + /* @description: */ + /* A macro that returns true whenever a face object contains kerning */ + /* data that can be accessed with @FT_Get_Kerning. */ + /* */ +#define FT_HAS_KERNING( face ) \ + ( face->face_flags & FT_FACE_FLAG_KERNING ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_IS_SCALABLE( face ) */ + /* */ + /* @description: */ + /* A macro that returns true whenever a face object contains a */ + /* scalable font face (true for TrueType, Type 1, CID, and */ + /* OpenType/CFF font formats. */ + /* */ +#define FT_IS_SCALABLE( face ) \ + ( face->face_flags & FT_FACE_FLAG_SCALABLE ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_IS_SFNT( face ) */ + /* */ + /* @description: */ + /* A macro that returns true whenever a face object contains a font */ + /* whose format is based on the SFNT storage scheme. This usually */ + /* means: TrueType fonts, OpenType fonts, as well as SFNT-based */ + /* embedded bitmap fonts. */ + /* */ + /* If this macro is true, all functions defined in @FT_SFNT_NAMES_H */ + /* and @FT_TRUETYPE_TABLES_H are available. */ + /* */ +#define FT_IS_SFNT( face ) \ + ( face->face_flags & FT_FACE_FLAG_SFNT ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_IS_FIXED_WIDTH( face ) */ + /* */ + /* @description: */ + /* A macro that returns true whenever a face object contains a font */ + /* face that contains fixed-width (or "monospace", "fixed-pitch", */ + /* etc.) glyphs. */ + /* */ +#define FT_IS_FIXED_WIDTH( face ) \ + ( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_HAS_FIXED_SIZES( face ) */ + /* */ + /* @description: */ + /* A macro that returns true whenever a face object contains some */ + /* embedded bitmaps. See the `available_sizes' field of the */ + /* @FT_FaceRec structure. */ + /* */ +#define FT_HAS_FIXED_SIZES( face ) \ + ( face->face_flags & FT_FACE_FLAG_FIXED_SIZES ) + + + /* */ + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_HAS_FAST_GLYPHS( face ) */ + /* */ + /* @description: */ + /* Deprecated; indicates that the face contains so-called "fast" */ + /* glyph bitmaps. */ + /* */ +#define FT_HAS_FAST_GLYPHS( face ) \ + ( face->face_flags & FT_FACE_FLAG_FAST_GLYPHS ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_HAS_GLYPH_NAMES( face ) */ + /* */ + /* @description: */ + /* A macro that returns true whenever a face object contains some */ + /* glyph names that can be accessed through @FT_Get_Glyph_Name. */ + /* */ +#define FT_HAS_GLYPH_NAMES( face ) \ + ( face->face_flags & FT_FACE_FLAG_GLYPH_NAMES ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_HAS_MULTIPLE_MASTERS( face ) */ + /* */ + /* @description: */ + /* A macro that returns true whenever a face object contains some */ + /* multiple masters. The functions provided by */ + /* @FT_MULTIPLE_MASTERS_H are then available to choose the exact */ + /* design you want. */ + /* */ +#define FT_HAS_MULTIPLE_MASTERS( face ) \ + ( face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS ) + + + /*************************************************************************/ + /* */ + /* <Constant> */ + /* FT_STYLE_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit-flags used to indicate the style of a given face. */ + /* These are used in the `style_flags' field of @FT_FaceRec. */ + /* */ + /* <Values> */ + /* FT_STYLE_FLAG_ITALIC :: */ + /* Indicates that a given face is italicized. */ + /* */ + /* FT_STYLE_FLAG_BOLD :: */ + /* Indicates that a given face is bold. */ + /* */ +#define FT_STYLE_FLAG_ITALIC ( 1 << 0 ) +#define FT_STYLE_FLAG_BOLD ( 1 << 1 ) + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Size_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an FT_Size_InternalRec structure, used to */ + /* model private data of a given FT_Size object. */ + /* */ + typedef struct FT_Size_InternalRec_* FT_Size_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_Metrics */ + /* */ + /* <Description> */ + /* The size metrics structure returned scaled important distances for */ + /* a given size object. */ + /* */ + /* <Fields> */ + /* x_ppem :: The character width, expressed in integer pixels. */ + /* This is the width of the EM square expressed in */ + /* pixels, hence the term `ppem' (pixels per EM). */ + /* */ + /* y_ppem :: The character height, expressed in integer pixels. */ + /* This is the height of the EM square expressed in */ + /* pixels, hence the term `ppem' (pixels per EM). */ + /* */ + /* x_scale :: A simple 16.16 fixed point format coefficient used */ + /* to scale horizontal distances expressed in font */ + /* units to fractional (26.6) pixel coordinates. */ + /* */ + /* y_scale :: A simple 16.16 fixed point format coefficient used */ + /* to scale vertical distances expressed in font */ + /* units to fractional (26.6) pixel coordinates. */ + /* */ + /* ascender :: The ascender, expressed in 26.6 fixed point */ + /* pixels. Positive for ascenders above the */ + /* baseline. */ + /* */ + /* descender :: The descender, expressed in 26.6 fixed point */ + /* pixels. Negative for descenders below the */ + /* baseline. */ + /* */ + /* height :: The text height, expressed in 26.6 fixed point */ + /* pixels. Always positive. */ + /* */ + /* max_advance :: Maximum horizontal advance, expressed in 26.6 */ + /* fixed point pixels. Always positive. */ + /* */ + /* <Note> */ + /* For scalable fonts, the values of `ascender', `descender', and */ + /* `height' are scaled versions of `face->ascender', */ + /* `face->descender', and `face->height', respectively. */ + /* */ + /* Unfortunately, due to glyph hinting, these values might not be */ + /* exact for certain fonts. They thus must be treated as unreliable */ + /* with an error margin of at least one pixel! */ + /* */ + /* Indeed, the only way to get the exact pixel ascender and descender */ + /* is to render _all_ glyphs. As this would be a definite */ + /* performance hit, it is up to client applications to perform such */ + /* computations. */ + /* */ + typedef struct FT_Size_Metrics_ + { + FT_UShort x_ppem; /* horizontal pixels per EM */ + FT_UShort y_ppem; /* vertical pixels per EM */ + + FT_Fixed x_scale; /* two scales used to convert font units */ + FT_Fixed y_scale; /* to 26.6 frac. pixel coordinates */ + + FT_Pos ascender; /* ascender in 26.6 frac. pixels */ + FT_Pos descender; /* descender in 26.6 frac. pixels */ + FT_Pos height; /* text height in 26.6 frac. pixels */ + FT_Pos max_advance; /* max horizontal advance, in 26.6 pixels */ + + } FT_Size_Metrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SizeRec */ + /* */ + /* <Description> */ + /* FreeType root size class structure. A size object models the */ + /* resolution and pointsize dependent data of a given face. */ + /* */ + /* <Fields> */ + /* face :: Handle to the parent face object. */ + /* */ + /* generic :: A typeless pointer, which is unused by the FreeType */ + /* library or any of its drivers. It can be used by */ + /* client applications to link their own data to each size */ + /* object. */ + /* */ + /* metrics :: Metrics for this size object. This field is read-only. */ + /* */ + typedef struct FT_SizeRec_ + { + FT_Face face; /* parent face object */ + FT_Generic generic; /* generic pointer for client uses */ + FT_Size_Metrics metrics; /* size metrics */ + FT_Size_Internal internal; + + } FT_SizeRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SubGlyph */ + /* */ + /* <Description> */ + /* The subglyph structure is an internal object used to describe */ + /* subglyphs (for example, in the case of composites). */ + /* */ + /* <Note> */ + /* The subglyph implementation is not part of the high-level API, */ + /* hence the forward structure declaration. */ + /* */ + typedef struct FT_SubGlyphRec_* FT_SubGlyph; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Slot_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an FT_Slot_InternalRec structure, used to */ + /* model private data of a given FT_GlyphSlot object. */ + /* */ + typedef struct FT_Slot_InternalRec_* FT_Slot_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_GlyphSlotRec */ + /* */ + /* <Description> */ + /* FreeType root glyph slot class structure. A glyph slot is a */ + /* container where individual glyphs can be loaded, be they */ + /* vectorial or bitmap/graymaps. */ + /* */ + /* <Fields> */ + /* library :: A handle to the FreeType library instance */ + /* this slot belongs to. */ + /* */ + /* face :: A handle to the parent face object. */ + /* */ + /* next :: In some cases (like some font tools), several */ + /* glyph slots per face object can be a good */ + /* thing. As this is rare, the glyph slots are */ + /* listed through a direct, single-linked list */ + /* using its `next' field. */ + /* */ + /* generic :: A typeless pointer which is unused by the */ + /* FreeType library or any of its drivers. It */ + /* can be used by client applications to link */ + /* their own data to each glyph slot object. */ + /* */ + /* metrics :: The metrics of the last loaded glyph in the */ + /* slot. The returned values depend on the last */ + /* load flags (see the @FT_Load_Glyph API */ + /* function) and can be expressed either in 26.6 */ + /* fractional pixels or font units. */ + /* */ + /* Note that even when the glyph image is */ + /* transformed, the metrics are not. */ + /* */ + /* linearHoriAdvance :: For scalable formats only, this field holds */ + /* the linearly scaled horizontal advance width */ + /* for the glyph (i.e. the scaled and unhinted */ + /* value of the hori advance). This can be */ + /* important to perform correct WYSIWYG layout. */ + /* */ + /* Note that this value is expressed by default */ + /* in 16.16 pixels. However, when the glyph is */ + /* loaded with the FT_LOAD_LINEAR_DESIGN flag, */ + /* this field contains simply the value of the */ + /* advance in original font units. */ + /* */ + /* linearVertAdvance :: For scalable formats only, this field holds */ + /* the linearly scaled vertical advance height */ + /* for the glyph. See linearHoriAdvance for */ + /* comments. */ + /* */ + /* advance :: This is the transformed advance width for the */ + /* glyph. */ + /* */ + /* format :: This field indicates the format of the image */ + /* contained in the glyph slot. Typically */ + /* FT_GLYPH_FORMAT_BITMAP, */ + /* FT_GLYPH_FORMAT_OUTLINE, and */ + /* FT_GLYPH_FORMAT_COMPOSITE, but others are */ + /* possible. */ + /* */ + /* bitmap :: This field is used as a bitmap descriptor */ + /* when the slot format is */ + /* FT_GLYPH_FORMAT_BITMAP. Note that the */ + /* address and content of the bitmap buffer can */ + /* change between calls of @FT_Load_Glyph and a */ + /* few other functions. */ + /* */ + /* bitmap_left :: This is the bitmap's left bearing expressed */ + /* in integer pixels. Of course, this is only */ + /* valid if the format is */ + /* FT_GLYPH_FORMAT_BITMAP. */ + /* */ + /* bitmap_top :: This is the bitmap's top bearing expressed in */ + /* integer pixels. Remember that this is the */ + /* distance from the baseline to the top-most */ + /* glyph scanline, upwards y-coordinates being */ + /* *positive*. */ + /* */ + /* outline :: The outline descriptor for the current glyph */ + /* image if its format is */ + /* FT_GLYPH_FORMAT_OUTLINE. */ + /* */ + /* num_subglyphs :: The number of subglyphs in a composite glyph. */ + /* This field is only valid for the composite */ + /* glyph format that should normally only be */ + /* loaded with the @FT_LOAD_NO_RECURSE flag. */ + /* For now this is internal to FreeType. */ + /* */ + /* subglyphs :: An array of subglyph descriptors for */ + /* composite glyphs. There are `num_subglyphs' */ + /* elements in there. Currently internal to */ + /* FreeType. */ + /* */ + /* control_data :: Certain font drivers can also return the */ + /* control data for a given glyph image (e.g. */ + /* TrueType bytecode, Type 1 charstrings, etc.). */ + /* This field is a pointer to such data. */ + /* */ + /* control_len :: This is the length in bytes of the control */ + /* data. */ + /* */ + /* other :: Really wicked formats can use this pointer to */ + /* present their own glyph image to client apps. */ + /* Note that the app will need to know about the */ + /* image format. */ + /* */ + /* <Note> */ + /* If @FT_Load_Glyph is called with default flags (see */ + /* @FT_LOAD_DEFAULT) the glyph image is loaded in the glyph slot in */ + /* its native format (e.g. a vectorial outline for TrueType and */ + /* Type 1 formats). */ + /* */ + /* This image can later be converted into a bitmap by calling */ + /* @FT_Render_Glyph. This function finds the current renderer for */ + /* the native image's format then invokes it. */ + /* */ + /* The renderer is in charge of transforming the native image through */ + /* the slot's face transformation fields, then convert it into a */ + /* bitmap that is returned in `slot->bitmap'. */ + /* */ + /* Note that `slot->bitmap_left' and `slot->bitmap_top' are also used */ + /* to specify the position of the bitmap relative to the current pen */ + /* position (e.g. coordinates [0,0] on the baseline). Of course, */ + /* `slot->format' is also changed to `FT_GLYPH_FORMAT_BITMAP' . */ + /* */ + typedef struct FT_GlyphSlotRec_ + { + FT_Library library; + FT_Face face; + FT_GlyphSlot next; + FT_UInt reserved; /* retained for binary compatibility */ + FT_Generic generic; + + FT_Glyph_Metrics metrics; + FT_Fixed linearHoriAdvance; + FT_Fixed linearVertAdvance; + FT_Vector advance; + + FT_Glyph_Format format; + + FT_Bitmap bitmap; + FT_Int bitmap_left; + FT_Int bitmap_top; + + FT_Outline outline; + + FT_UInt num_subglyphs; + FT_SubGlyph subglyphs; + + void* control_data; + long control_len; + + void* other; + + FT_Slot_Internal internal; + + } FT_GlyphSlotRec; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* F U N C T I O N S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Init_FreeType */ + /* */ + /* <Description> */ + /* Initializes a new FreeType library object. The set of modules */ + /* that are registered by this function is determined at build time. */ + /* */ + /* <Output> */ + /* alibrary :: A handle to a new library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Init_FreeType( FT_Library *alibrary ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Library_Version */ + /* */ + /* <Description> */ + /* Return the version of the FreeType library being used. This is */ + /* useful when dynamically linking to the library, since one cannot */ + /* use the macros FT_FREETYPE_MAJOR, FT_FREETYPE_MINOR, and */ + /* FT_FREETYPE_PATCH. */ + /* */ + /* <Input> */ + /* library :: A source library handle. */ + /* */ + /* <Output> */ + /* amajor :: The major version number. */ + /* */ + /* aminor :: The minor version number. */ + /* */ + /* apatch :: The patch version number. */ + /* */ + /* <Note> */ + /* The reason why this function takes a 'library' argument is because */ + /* certain programs implement library initialization in a custom way */ + /* that doesn't use `FT_Init_FreeType'. */ + /* */ + /* In such cases, the library version might not be available before */ + /* the library object has been created. */ + /* */ + FT_EXPORT( void ) + FT_Library_Version( FT_Library library, + FT_Int *amajor, + FT_Int *aminor, + FT_Int *apatch ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_FreeType */ + /* */ + /* <Description> */ + /* Destroys a given FreeType library object and all of its childs, */ + /* including resources, drivers, faces, sizes, etc. */ + /* */ + /* <Input> */ + /* library :: A handle to the target library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_FreeType( FT_Library library ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_OPEN_XXX */ + /* */ + /* <Description> */ + /* A list of bit-field constants used within the `flags' field of the */ + /* @FT_Open_Args structure. */ + /* */ + /* <Values> */ + /* FT_OPEN_MEMORY :: This is a memory-based stream. */ + /* */ + /* FT_OPEN_STREAM :: Copy the stream from the `stream' field. */ + /* */ + /* FT_OPEN_PATHNAME :: Create a new input stream from a C */ + /* path name. */ + /* */ + /* FT_OPEN_DRIVER :: Use the `driver' field. */ + /* */ + /* FT_OPEN_PARAMS :: Use the `num_params' & `params' field. */ + /* */ + /* ft_open_memory :: Deprecated; use @FT_OPEN_MEMORY instead. */ + /* */ + /* ft_open_stream :: Deprecated; use @FT_OPEN_STREAM instead. */ + /* */ + /* ft_open_pathname :: Deprecated; use @FT_OPEN_PATHNAME instead. */ + /* */ + /* ft_open_driver :: Deprecated; use @FT_OPEN_DRIVER instead. */ + /* */ + /* ft_open_params :: Deprecated; use @FT_OPEN_PARAMS instead. */ + /* */ + /* <Note> */ + /* The `FT_OPEN_MEMORY', `FT_OPEN_STREAM', and `FT_OPEN_PATHNAME' */ + /* flags are mutually exclusive. */ + /* */ +#define FT_OPEN_MEMORY 0x1 +#define FT_OPEN_STREAM 0x2 +#define FT_OPEN_PATHNAME 0x4 +#define FT_OPEN_DRIVER 0x8 +#define FT_OPEN_PARAMS 0x10 + +#define ft_open_memory FT_OPEN_MEMORY /* deprecated */ +#define ft_open_stream FT_OPEN_STREAM /* deprecated */ +#define ft_open_pathname FT_OPEN_PATHNAME /* deprecated */ +#define ft_open_driver FT_OPEN_DRIVER /* deprecated */ +#define ft_open_params FT_OPEN_PARAMS /* deprecated */ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Parameter */ + /* */ + /* <Description> */ + /* A simple structure used to pass more or less generic parameters */ + /* to @FT_Open_Face. */ + /* */ + /* <Fields> */ + /* tag :: A 4-byte identification tag. */ + /* */ + /* data :: A pointer to the parameter data. */ + /* */ + /* <Note> */ + /* The id and function of parameters are driver-specific. */ + /* */ + typedef struct FT_Parameter_ + { + FT_ULong tag; + FT_Pointer data; + + } FT_Parameter; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Open_Args */ + /* */ + /* <Description> */ + /* A structure used to indicate how to open a new font file/stream. */ + /* A pointer to such a structure can be used as a parameter for the */ + /* functions @FT_Open_Face and @FT_Attach_Stream. */ + /* */ + /* <Fields> */ + /* flags :: A set of bit flags indicating how to use the */ + /* structure. */ + /* */ + /* memory_base :: The first byte of the file in memory. */ + /* */ + /* memory_size :: The size in bytes of the file in memory. */ + /* */ + /* pathname :: A pointer to an 8-bit file pathname. */ + /* */ + /* stream :: A handle to a source stream object. */ + /* */ + /* driver :: This field is exclusively used by @FT_Open_Face; */ + /* it simply specifies the font driver to use to open */ + /* the face. If set to 0, FreeType will try to load */ + /* the face with each one of the drivers in its list. */ + /* */ + /* num_params :: The number of extra parameters. */ + /* */ + /* params :: Extra parameters passed to the font driver when */ + /* opening a new face. */ + /* */ + /* <Note> */ + /* The stream type is determined by the contents of `flags' which */ + /* are tested in the following order by @FT_Open_Face: */ + /* */ + /* If the `FT_OPEN_MEMORY' bit is set, assume that this is a */ + /* memory file of `memory_size' bytes,located at `memory_address'. */ + /* */ + /* Otherwise, if the `FT_OPEN_STREAM' bit is set, assume that a */ + /* custom input stream `stream' is used. */ + /* */ + /* Otherwise, if the `FT_OPEN_PATHNAME' bit is set, assume that this */ + /* is a normal file and use `pathname' to open it. */ + /* */ + /* If the `FT_OPEN_DRIVER' bit is set, @FT_Open_Face will only try to */ + /* open the file with the driver whose handler is in `driver'. */ + /* */ + /* If the `FT_OPEN_PARAMS' bit is set, the parameters given by */ + /* `num_params' and `params' will be used. They are ignored */ + /* otherwise. */ + /* */ + typedef struct FT_Open_Args_ + { + FT_UInt flags; + const FT_Byte* memory_base; + FT_Long memory_size; + FT_String* pathname; + FT_Stream stream; + FT_Module driver; + FT_Int num_params; + FT_Parameter* params; + + } FT_Open_Args; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face */ + /* */ + /* <Description> */ + /* Creates a new face object from a given resource and typeface index */ + /* using a pathname to the font file. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* pathname :: A path to the font file. */ + /* */ + /* face_index :: The index of the face within the resource. The */ + /* first face has index 0. */ + /* <Output> */ + /* aface :: A handle to a new face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* Unlike FreeType 1.x, this function automatically creates a glyph */ + /* slot for the face object which can be accessed directly through */ + /* `face->glyph'. */ + /* */ + /* @FT_New_Face can be used to determine and/or check the font format */ + /* of a given font resource. If the `face_index' field is negative, */ + /* the function will _not_ return any face handle in `aface'. Its */ + /* return value should be 0 if the font format is recognized, or */ + /* non-zero otherwise. */ + /* */ + /* Each new face object created with this function also owns a */ + /* default @FT_Size object, accessible as `face->size'. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face( FT_Library library, + const char* filepathname, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Memory_Face */ + /* */ + /* <Description> */ + /* Creates a new face object from a given resource and typeface index */ + /* using a font file already loaded into memory. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* file_base :: A pointer to the beginning of the font data. */ + /* */ + /* file_size :: The size of the memory chunk used by the font data. */ + /* */ + /* face_index :: The index of the face within the resource. The */ + /* first face has index 0. */ + /* <Output> */ + /* aface :: A handle to a new face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The font data bytes are used _directly_ by the @FT_Face object. */ + /* This means that they are not copied, and that the client is */ + /* responsible for releasing/destroying them _after_ the */ + /* corresponding call to @FT_Done_Face . */ + /* */ + /* Unlike FreeType 1.x, this function automatically creates a glyph */ + /* slot for the face object which can be accessed directly through */ + /* `face->glyph'. */ + /* */ + /* @FT_New_Memory_Face can be used to determine and/or check the font */ + /* format of a given font resource. If the `face_index' field is */ + /* negative, the function will _not_ return any face handle in */ + /* `aface'. Its return value should be 0 if the font format is */ + /* recognized, or non-zero otherwise. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Memory_Face( FT_Library library, + const FT_Byte* file_base, + FT_Long file_size, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Open_Face */ + /* */ + /* <Description> */ + /* Opens a face object from a given resource and typeface index using */ + /* an `FT_Open_Args' structure. If the face object doesn't exist, it */ + /* will be created. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* args :: A pointer to an `FT_Open_Args' structure which must */ + /* be filled by the caller. */ + /* */ + /* face_index :: The index of the face within the resource. The */ + /* first face has index 0. */ + /* <Output> */ + /* aface :: A handle to a new face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* Unlike FreeType 1.x, this function automatically creates a glyph */ + /* slot for the face object which can be accessed directly through */ + /* `face->glyph'. */ + /* */ + /* @FT_Open_Face can be used to determine and/or check the font */ + /* format of a given font resource. If the `face_index' field is */ + /* negative, the function will _not_ return any face handle in */ + /* `*face'. Its return value should be 0 if the font format is */ + /* recognized, or non-zero otherwise. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Open_Face( FT_Library library, + const FT_Open_Args* args, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Attach_File */ + /* */ + /* <Description> */ + /* `Attaches' a given font file to an existing face. This is usually */ + /* to read additional information for a single face object. For */ + /* example, it is used to read the AFM files that come with Type 1 */ + /* fonts in order to add kerning data and other metrics. */ + /* */ + /* <InOut> */ + /* face :: The target face object. */ + /* */ + /* <Input> */ + /* filepathname :: An 8-bit pathname naming the `metrics' file. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* If your font file is in memory, or if you want to provide your */ + /* own input stream object, use @FT_Attach_Stream. */ + /* */ + /* The meaning of the `attach' action (i.e., what really happens when */ + /* the new file is read) is not fixed by FreeType itself. It really */ + /* depends on the font format (and thus the font driver). */ + /* */ + /* Client applications are expected to know what they are doing */ + /* when invoking this function. Most drivers simply do not implement */ + /* file attachments. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Attach_File( FT_Face face, + const char* filepathname ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Attach_Stream */ + /* */ + /* <Description> */ + /* This function is similar to @FT_Attach_File with the exception */ + /* that it reads the attachment from an arbitrary stream. */ + /* */ + /* <InOut> */ + /* face :: The target face object. */ + /* */ + /* <Input> */ + /* parameters :: A pointer to an FT_Open_Args structure used to */ + /* describe the input stream to FreeType. */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The meaning of the `attach' (i.e. what really happens when the */ + /* new file is read) is not fixed by FreeType itself. It really */ + /* depends on the font format (and thus the font driver). */ + /* */ + /* Client applications are expected to know what they are doing */ + /* when invoking this function. Most drivers simply do not implement */ + /* file attachments. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Attach_Stream( FT_Face face, + FT_Open_Args* parameters ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Face */ + /* */ + /* <Description> */ + /* Discards a given face object, as well as all of its child slots */ + /* and sizes. */ + /* */ + /* <Input> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_Face( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Char_Size */ + /* */ + /* <Description> */ + /* Sets the character dimensions of a given face object. The */ + /* `char_width' and `char_height' values are used for the width and */ + /* height, respectively, expressed in 26.6 fractional points. */ + /* */ + /* If the horizontal or vertical resolution values are zero, a */ + /* default value of 72dpi is used. Similarly, if one of the */ + /* character dimensions is zero, its value is set equal to the other. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Input> */ + /* char_width :: The character width, in 26.6 fractional points. */ + /* */ + /* char_height :: The character height, in 26.6 fractional */ + /* points. */ + /* */ + /* horz_resolution :: The horizontal resolution. */ + /* */ + /* vert_resolution :: The vertical resolution. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* When dealing with fixed-size faces (i.e., non-scalable formats), */ + /* @FT_Set_Pixel_Sizes provides a more convenient interface. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Char_Size( FT_Face face, + FT_F26Dot6 char_width, + FT_F26Dot6 char_height, + FT_UInt horz_resolution, + FT_UInt vert_resolution ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Pixel_Sizes */ + /* */ + /* <Description> */ + /* Sets the character dimensions of a given face object. The width */ + /* and height are expressed in integer pixels. */ + /* */ + /* If one of the character dimensions is zero, its value is set equal */ + /* to the other. */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Input> */ + /* pixel_width :: The character width, in integer pixels. */ + /* */ + /* pixel_height :: The character height, in integer pixels. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The values of `pixel_width' and `pixel_height' correspond to the */ + /* pixel values of the _typographic_ character size, which are NOT */ + /* necessarily the same as the dimensions of the glyph `bitmap */ + /* cells'. */ + /* */ + /* The `character size' is really the size of an abstract square */ + /* called the `EM', used to design the font. However, depending */ + /* on the font design, glyphs will be smaller or greater than the */ + /* EM. */ + /* */ + /* This means that setting the pixel size to, say, 8x8 doesn't */ + /* guarantee in any way that you will get glyph bitmaps that all fit */ + /* within an 8x8 cell (sometimes even far from it). */ + /* */ + /* For bitmap fonts, `pixel_height' usually is a reliable value for */ + /* the height of the bitmap cell. Drivers for bitmap font formats */ + /* which contain a single bitmap strike only (BDF, PCF, FNT) ignore */ + /* `pixel_width'. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Pixel_Sizes( FT_Face face, + FT_UInt pixel_width, + FT_UInt pixel_height ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Load_Glyph */ + /* */ + /* <Description> */ + /* A function used to load a single glyph within a given glyph slot, */ + /* for a given size. */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object where the glyph */ + /* will be loaded. */ + /* */ + /* <Input> */ + /* glyph_index :: The index of the glyph in the font file. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* @FT_LOAD_XXX constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* If the glyph image is not a bitmap, and if the bit flag */ + /* FT_LOAD_IGNORE_TRANSFORM is unset, the glyph image will be */ + /* transformed with the information passed to a previous call to */ + /* @FT_Set_Transform. */ + /* */ + /* Note that this also transforms the `face.glyph.advance' field, but */ + /* *not* the values in `face.glyph.metrics'. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Load_Glyph( FT_Face face, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Load_Char */ + /* */ + /* <Description> */ + /* A function used to load a single glyph within a given glyph slot, */ + /* for a given size, according to its character code. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object where the glyph */ + /* will be loaded. */ + /* */ + /* <Input> */ + /* char_code :: The glyph's character code, according to the */ + /* current charmap used in the face. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* @FT_LOAD_XXX constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* If the face has no current charmap, or if the character code */ + /* is not defined in the charmap, this function will return an */ + /* error. */ + /* */ + /* If the glyph image is not a bitmap, and if the bit flag */ + /* FT_LOAD_IGNORE_TRANSFORM is unset, the glyph image will be */ + /* transformed with the information passed to a previous call to */ + /* @FT_Set_Transform. */ + /* */ + /* Note that this also transforms the `face.glyph.advance' field, but */ + /* *not* the values in `face.glyph.metrics'. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Load_Char( FT_Face face, + FT_ULong char_code, + FT_Int32 load_flags ); + + + /*************************************************************************** + * + * @enum: + * FT_LOAD_XXX + * + * @description: + * A list of bit-field constants, used with @FT_Load_Glyph to indicate + * what kind of operations to perform during glyph loading. + * + * @values: + * FT_LOAD_DEFAULT :: + * Corresponding to 0, this value is used a default glyph load. In this + * case, the following will happen: + * + * 1. FreeType looks for a bitmap for the glyph corresponding to the + * face's current size. If one is found, the function returns. The + * bitmap data can be accessed from the glyph slot (see note below). + * + * 2. If no embedded bitmap is searched or found, FreeType looks for a + * scalable outline. If one is found, it is loaded from the font + * file, scaled to device pixels, then "hinted" to the pixel grid in + * order to optimize it. The outline data can be accessed from the + * glyph slot (see note below). + * + * Note that by default, the glyph loader doesn't render outlines into + * bitmaps. The following flags are used to modify this default + * behaviour to more specific and useful cases. + * + * FT_LOAD_NO_SCALE :: + * Don't scale the vector outline being loaded to 26.6 fractional + * pixels, but kept in font units. Note that this also disables + * hinting and the loading of embedded bitmaps. You should only use it + * when you want to retrieve the original glyph outlines in font units. + * + * FT_LOAD_NO_HINTING :: + * Don't hint glyph outlines after their scaling to device pixels. + * This generally generates "blurrier" glyphs in anti-aliased modes. + * + * This flag is ignored if @FT_LOAD_NO_SCALE is set. + * + * FT_LOAD_RENDER :: + * Render the glyph outline immediately into a bitmap before the glyph + * loader returns. By default, the glyph is rendered for the + * @FT_RENDER_MODE_NORMAL mode, which corresponds to 8-bit anti-aliased + * bitmaps using 256 opacity levels. You can use either + * @FT_LOAD_TARGET_MONO or @FT_LOAD_MONOCHROME to render 1-bit + * monochrome bitmaps. + * + * This flag is ignored if @FT_LOAD_NO_SCALE is set. + * + * FT_LOAD_NO_BITMAP :: + * Don't look for bitmaps when loading the glyph. Only scalable + * outlines will be loaded when available, and scaled, hinted, or + * rendered depending on other bit flags. + * + * This does not prevent you from rendering outlines to bitmaps + * with @FT_LOAD_RENDER, however. + * + * FT_LOAD_VERTICAL_LAYOUT :: + * Prepare the glyph image for vertical text layout. This basically + * means that `face.glyph.advance' will correspond to the vertical + * advance height (instead of the default horizontal advance width), + * and that the glyph image will be translated to match the vertical + * bearings positions. + * + * FT_LOAD_FORCE_AUTOHINT :: + * Force the use of the FreeType auto-hinter when a glyph outline is + * loaded. You shouldn't need this in a typical application, since it + * is mostly used to experiment with its algorithm. + * + * FT_LOAD_CROP_BITMAP :: + * Indicates that the glyph loader should try to crop the bitmap (i.e., + * remove all space around its black bits) when loading it. This is + * only useful when loading embedded bitmaps in certain fonts, since + * bitmaps rendered with @FT_LOAD_RENDER are always cropped by default. + * + * FT_LOAD_PEDANTIC :: + * Indicates that the glyph loader should perform pedantic + * verifications during glyph loading, rejecting invalid fonts. This + * is mostly used to detect broken glyphs in fonts. By default, + * FreeType tries to handle broken fonts also. + * + * FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH :: + * Indicates that the glyph loader should ignore the global advance + * width defined in the font. As far as we know, this is only used by + * the X-TrueType font server, in order to deal correctly with the + * incorrect metrics contained in DynaLab's TrueType CJK fonts. + * + * FT_LOAD_NO_RECURSE :: + * This flag is only used internally. It merely indicates that the + * glyph loader should not load composite glyphs recursively. Instead, + * it should set the `num_subglyph' and `subglyphs' values of the glyph + * slot accordingly, and set "glyph->format" to + * @FT_GLYPH_FORMAT_COMPOSITE. + * + * The description of sub-glyphs is not available to client + * applications for now. + * + * FT_LOAD_IGNORE_TRANSFORM :: + * Indicates that the glyph loader should not try to transform the + * loaded glyph image. This doesn't prevent scaling, hinting, or + * rendering. + * + * FT_LOAD_MONOCHROME :: + * This flag is used with @FT_LOAD_RENDER to indicate that you want + * to render a 1-bit monochrome glyph bitmap from a vectorial outline. + * + * Note that this has no effect on the hinting algorithm used by the + * glyph loader. You should better use @FT_LOAD_TARGET_MONO if you + * want to render monochrome-optimized glyph images instead. + * + * FT_LOAD_LINEAR_DESIGN :: + * Return the linearly scaled metrics expressed in original font units + * instead of the default 16.16 pixel values. + * + * FT_LOAD_NO_AUTOHINT :: + * Indicates that the auto-hinter should never be used to hint glyph + * outlines. This doesn't prevent native format-specific hinters from + * being used. This can be important for certain fonts where unhinted + * output is better than auto-hinted one. + */ +#define FT_LOAD_DEFAULT 0x0 +#define FT_LOAD_NO_SCALE 0x1 +#define FT_LOAD_NO_HINTING 0x2 +#define FT_LOAD_RENDER 0x4 +#define FT_LOAD_NO_BITMAP 0x8 +#define FT_LOAD_VERTICAL_LAYOUT 0x10 +#define FT_LOAD_FORCE_AUTOHINT 0x20 +#define FT_LOAD_CROP_BITMAP 0x40 +#define FT_LOAD_PEDANTIC 0x80 +#define FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH 0x200 +#define FT_LOAD_NO_RECURSE 0x400 +#define FT_LOAD_IGNORE_TRANSFORM 0x800 +#define FT_LOAD_MONOCHROME 0x1000 +#define FT_LOAD_LINEAR_DESIGN 0x2000 + + /* temporary hack! */ +#define FT_LOAD_SBITS_ONLY 0x4000 +#define FT_LOAD_NO_AUTOHINT 0x8000U + + /* */ + + +#define FT_LOAD_TARGET_( x ) ( (FT_Int32)( (x) & 15 ) << 16 ) +#define FT_LOAD_TARGET_MODE( x ) ( (FT_Render_Mode)( ( (x) >> 16 ) & 15 ) ) + + + /*************************************************************************** + * + * @enum: + * FT_LOAD_TARGET_XXX + * + * @description: + * A list of load targets. XXX + * + * @values: + * FT_LOAD_TARGET_NORMAL :: + * XXX + * + * FT_LOAD_TARGET_LIGHT :: + * XXX + * + * FT_LOAD_TARGET_MONO :: + * XXX + * + * FT_LOAD_TARGET_LCD :: + * XXX + * + * FT_LOAD_TARGET_LCD_V :: + * XXX + */ + +#define FT_LOAD_TARGET_NORMAL FT_LOAD_TARGET_( FT_RENDER_MODE_NORMAL ) +#define FT_LOAD_TARGET_LIGHT FT_LOAD_TARGET_( FT_RENDER_MODE_LIGHT ) +#define FT_LOAD_TARGET_MONO FT_LOAD_TARGET_( FT_RENDER_MODE_MONO ) +#define FT_LOAD_TARGET_LCD FT_LOAD_TARGET_( FT_RENDER_MODE_LCD ) +#define FT_LOAD_TARGET_LCD_V FT_LOAD_TARGET_( FT_RENDER_MODE_LCD_V ) + + /* */ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Transform */ + /* */ + /* <Description> */ + /* A function used to set the transformation that is applied to glyph */ + /* images just before they are converted to bitmaps in a glyph slot */ + /* when @FT_Render_Glyph is called. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the transformation's 2x2 matrix. Use 0 for */ + /* the identity matrix. */ + /* delta :: A pointer to the translation vector. Use 0 for the null */ + /* vector. */ + /* */ + /* <Note> */ + /* The transformation is only applied to scalable image formats after */ + /* the glyph has been loaded. It means that hinting is unaltered by */ + /* the transformation and is performed on the character size given in */ + /* the last call to @FT_Set_Char_Size or @FT_Set_Pixel_Sizes. */ + /* */ + FT_EXPORT( void ) + FT_Set_Transform( FT_Face face, + FT_Matrix* matrix, + FT_Vector* delta ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Render_Mode */ + /* */ + /* <Description> */ + /* An enumeration type that lists the render modes supported by */ + /* FreeType 2. Each mode corresponds to a specific type of scanline */ + /* conversion performed on the outline, as well as specific */ + /* hinting optimizations. */ + /* */ + /* For bitmap fonts the `bitmap->pixel_mode' field in the */ + /* @FT_GlyphSlotRec structure gives the format of the returned */ + /* bitmap. */ + /* */ + /* <Values> */ + /* FT_RENDER_MODE_NORMAL :: */ + /* This is the default render mode; it corresponds to 8-bit */ + /* anti-aliased bitmaps, using 256 levels of opacity. */ + /* */ + /* FT_RENDER_MODE_LIGHT :: */ + /* This is similar to @FT_RENDER_MODE_NORMAL, except that this */ + /* changes the hinting to prevent stem width quantization. This */ + /* results in glyph shapes that are more similar to the original, */ + /* while being a bit more fuzzy ("better shapes", instead of */ + /* "better contrast" if you want :-). */ + /* */ + /* FT_RENDER_MODE_MONO :: */ + /* This mode corresponds to 1-bit bitmaps. */ + /* */ + /* FT_RENDER_MODE_LCD :: */ + /* This mode corresponds to horizontal RGB/BGR sub-pixel displays, */ + /* like LCD-screens. It produces 8-bit bitmaps that are 3 times */ + /* the width of the original glyph outline in pixels, and which use */ + /* the @FT_PIXEL_MODE_LCD mode. */ + /* */ + /* FT_RENDER_MODE_LCD_V :: */ + /* This mode corresponds to vertical RGB/BGR sub-pixel displays */ + /* (like PDA screens, rotated LCD displays, etc.). It produces */ + /* 8-bit bitmaps that are 3 times the height of the original */ + /* glyph outline in pixels and use the @FT_PIXEL_MODE_LCD_V mode. */ + /* */ + /* <Note> */ + /* The LCD-optimized glyph bitmaps produced by FT_Render_Glyph are */ + /* _not filtered_ to reduce color-fringes. It is up to the caller to */ + /* perform this pass. */ + /* */ + typedef enum FT_Render_Mode_ + { + FT_RENDER_MODE_NORMAL = 0, + FT_RENDER_MODE_LIGHT, + FT_RENDER_MODE_MONO, + FT_RENDER_MODE_LCD, + FT_RENDER_MODE_LCD_V, + + FT_RENDER_MODE_MAX + + } FT_Render_Mode; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* ft_render_mode_xxx */ + /* */ + /* <Description> */ + /* These constats are deprecated. Use the corresponding */ + /* @FT_Render_Mode values instead. */ + /* */ + /* <Values> */ + /* ft_render_mode_normal :: see @FT_RENDER_MODE_NORMAL */ + /* ft_render_mode_mono :: see @FT_RENDER_MODE_MONO */ + /* */ +#define ft_render_mode_normal FT_RENDER_MODE_NORMAL +#define ft_render_mode_mono FT_RENDER_MODE_MONO + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Render_Glyph */ + /* */ + /* <Description> */ + /* Converts a given glyph image to a bitmap. It does so by */ + /* inspecting the glyph image format, find the relevant renderer, and */ + /* invoke it. */ + /* */ + /* <InOut> */ + /* slot :: A handle to the glyph slot containing the image to */ + /* convert. */ + /* */ + /* <Input> */ + /* render_mode :: This is the render mode used to render the glyph */ + /* image into a bitmap. See FT_Render_Mode for a list */ + /* of possible values. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Render_Glyph( FT_GlyphSlot slot, + FT_Render_Mode render_mode ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Kerning_Mode */ + /* */ + /* <Description> */ + /* An enumeration used to specify which kerning values to return in */ + /* @FT_Get_Kerning. */ + /* */ + /* <Values> */ + /* FT_KERNING_DEFAULT :: Return scaled and grid-fitted kerning */ + /* distances (value is 0). */ + /* */ + /* FT_KERNING_UNFITTED :: Return scaled but un-grid-fitted kerning */ + /* distances. */ + /* */ + /* FT_KERNING_UNSCALED :: Return the kerning vector in original font */ + /* units. */ + /* */ + typedef enum FT_Kerning_Mode_ + { + FT_KERNING_DEFAULT = 0, + FT_KERNING_UNFITTED, + FT_KERNING_UNSCALED + + } FT_Kerning_Mode; + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* ft_kerning_default */ + /* */ + /* <Description> */ + /* This constant is deprecated. Please use @FT_KERNING_DEFAULT */ + /* instead. */ + /* */ +#define ft_kerning_default FT_KERNING_DEFAULT + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* ft_kerning_unfitted */ + /* */ + /* <Description> */ + /* This constant is deprecated. Please use @FT_KERNING_UNFITTED */ + /* instead. */ + /* */ +#define ft_kerning_unfitted FT_KERNING_UNFITTED + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* ft_kerning_unscaled */ + /* */ + /* <Description> */ + /* This constant is deprecated. Please use @FT_KERNING_UNSCALED */ + /* instead. */ + /* */ +#define ft_kerning_unscaled FT_KERNING_UNSCALED + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Kerning */ + /* */ + /* <Description> */ + /* Returns the kerning vector between two glyphs of a same face. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* left_glyph :: The index of the left glyph in the kern pair. */ + /* */ + /* right_glyph :: The index of the right glyph in the kern pair. */ + /* */ + /* kern_mode :: See @FT_Kerning_Mode for more information. */ + /* Determines the scale/dimension of the returned */ + /* kerning vector. */ + /* */ + /* <Output> */ + /* akerning :: The kerning vector. This is in font units for */ + /* scalable formats, and in pixels for fixed-sizes */ + /* formats. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* Only horizontal layouts (left-to-right & right-to-left) are */ + /* supported by this method. Other layouts, or more sophisticated */ + /* kernings, are out of the scope of this API function -- they can be */ + /* implemented through format-specific interfaces. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Kerning( FT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_UInt kern_mode, + FT_Vector *akerning ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Glyph_Name */ + /* */ + /* <Description> */ + /* Retrieves the ASCII name of a given glyph in a face. This only */ + /* works for those faces where FT_HAS_GLYPH_NAME(face) returns true. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* glyph_index :: The glyph index. */ + /* */ + /* buffer_max :: The maximal number of bytes available in the */ + /* buffer. */ + /* */ + /* <Output> */ + /* buffer :: A pointer to a target buffer where the name will be */ + /* copied to. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* An error is returned if the face doesn't provide glyph names or if */ + /* the glyph index is invalid. In all cases of failure, the first */ + /* byte of `buffer' will be set to 0 to indicate an empty name. */ + /* */ + /* The glyph name is truncated to fit within the buffer if it is too */ + /* long. The returned string is always zero-terminated. */ + /* */ + /* This function is not compiled within the library if the config */ + /* macro FT_CONFIG_OPTION_NO_GLYPH_NAMES is defined in */ + /* `include/freetype/config/ftoptions.h' */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Glyph_Name( FT_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Postscript_Name */ + /* */ + /* <Description> */ + /* Retrieves the ASCII Postscript name of a given face, if available. */ + /* This should only work with Postscript and TrueType fonts. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Return> */ + /* A pointer to the face's Postscript name. NULL if un-available. */ + /* */ + /* <Note> */ + /* The returned pointer is owned by the face and will be destroyed */ + /* with it. */ + /* */ + FT_EXPORT( const char* ) + FT_Get_Postscript_Name( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Select_Charmap */ + /* */ + /* <Description> */ + /* Selects a given charmap by its encoding tag (as listed in */ + /* `freetype.h'). */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* encoding :: A handle to the selected charmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function will return an error if no charmap in the face */ + /* corresponds to the encoding queried here. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Select_Charmap( FT_Face face, + FT_Encoding encoding ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Charmap */ + /* */ + /* <Description> */ + /* Selects a given charmap for character code to glyph index */ + /* decoding. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* charmap :: A handle to the selected charmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function will return an error if the charmap is not part of */ + /* the face (i.e., if it is not listed in the face->charmaps[] */ + /* table). */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Charmap( FT_Face face, + FT_CharMap charmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Char_Index */ + /* */ + /* <Description> */ + /* Returns the glyph index of a given character code. This function */ + /* uses a charmap object to do the translation. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* charcode :: The character code. */ + /* */ + /* <Return> */ + /* The glyph index. 0 means `undefined character code'. */ + /* */ + /* <Note> */ + /* FreeType computes its own glyph indices which are not necessarily */ + /* the same as used in the font in case the font is based on glyph */ + /* indices. Reason for this behaviour is to assure that index 0 is */ + /* never used, representing the missing glyph. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Char_Index( FT_Face face, + FT_ULong charcode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_First_Char */ + /* */ + /* <Description> */ + /* This function is used to return the first character code in the */ + /* current charmap of a given face. It will also return the */ + /* corresponding glyph index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Output> */ + /* agindex :: Glyph index of first character code. 0 if charmap is */ + /* empty. */ + /* */ + /* <Return> */ + /* The charmap's first character code. */ + /* */ + /* <Note> */ + /* You should use this function with @FT_Get_Next_Char to be able to */ + /* parse all character codes available in a given charmap. The code */ + /* should look like this: */ + /* */ + /* { */ + /* FT_ULong charcode; */ + /* FT_UInt gindex; */ + /* */ + /* */ + /* charcode = FT_Get_First_Char( face, &gindex ); */ + /* while ( gindex != 0 ) */ + /* { */ + /* ... do something with (charcode,gindex) pair ... */ + /* */ + /* charcode = FT_Get_Next_Char( face, charcode, &gindex ); */ + /* } */ + /* } */ + /* */ + /* Note that `*agindex' will be set to 0 if the charmap is empty. */ + /* The result itself can be 0 in two cases: if the charmap is empty */ + /* or when the value 0 is the first valid character code. */ + /* */ + FT_EXPORT( FT_ULong ) + FT_Get_First_Char( FT_Face face, + FT_UInt *agindex ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Next_Char */ + /* */ + /* <Description> */ + /* This function is used to return the next character code in the */ + /* current charmap of a given face following the value 'char_code', */ + /* as well as the corresponding glyph index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* char_code :: The starting character code. */ + /* */ + /* <Output> */ + /* agindex :: Glyph index of first character code. 0 if charmap */ + /* is empty. */ + /* */ + /* <Return> */ + /* The charmap's next character code. */ + /* */ + /* <Note> */ + /* You should use this function with @FT_Get_First_Char to walk */ + /* through all character codes available in a given charmap. See */ + /* the note for this function for a simple code example. */ + /* */ + /* Note that `*agindex' will be set to 0 when there are no more codes */ + /* in the charmap. */ + /* */ + FT_EXPORT( FT_ULong ) + FT_Get_Next_Char( FT_Face face, + FT_ULong char_code, + FT_UInt *agindex ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Name_Index */ + /* */ + /* <Description> */ + /* Returns the glyph index of a given glyph name. This function uses */ + /* driver specific objects to do the translation. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* glyph_name :: The glyph name. */ + /* */ + /* <Return> */ + /* The glyph index. 0 means `undefined character code'. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Name_Index( FT_Face face, + FT_String* glyph_name ); + + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* computations */ + /* */ + /* <Title> */ + /* Computations */ + /* */ + /* <Abstract> */ + /* Crunching fixed numbers and vectors */ + /* */ + /* <Description> */ + /* This section contains various functions used to perform */ + /* computations on 16.16 fixed-float numbers or 2d vectors. */ + /* */ + /* <Order> */ + /* FT_MulDiv */ + /* FT_MulFix */ + /* FT_DivFix */ + /* FT_RoundFix */ + /* FT_CeilFix */ + /* FT_FloorFix */ + /* FT_Vector_Transform */ + /* FT_Matrix_Multiply */ + /* FT_Matrix_Invert */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_MulDiv */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation `(a*b)/c' */ + /* with maximal accuracy (it uses a 64-bit intermediate integer */ + /* whenever necessary). */ + /* */ + /* This function isn't necessarily as fast as some processor specific */ + /* operations, but is at least completely portable. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. */ + /* c :: The divisor. */ + /* */ + /* <Return> */ + /* The result of `(a*b)/c'. This function never traps when trying to */ + /* divide by zero; it simply returns `MaxInt' or `MinInt' depending */ + /* on the signs of `a' and `b'. */ + /* */ + FT_EXPORT( FT_Long ) + FT_MulDiv( FT_Long a, + FT_Long b, + FT_Long c ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_MulFix */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation */ + /* `(a*b)/0x10000' with maximal accuracy. Most of the time this is */ + /* used to multiply a given value by a 16.16 fixed float factor. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. Use a 16.16 factor here whenever */ + /* possible (see note below). */ + /* */ + /* <Return> */ + /* The result of `(a*b)/0x10000'. */ + /* */ + /* <Note> */ + /* This function has been optimized for the case where the absolute */ + /* value of `a' is less than 2048, and `b' is a 16.16 scaling factor. */ + /* As this happens mainly when scaling from notional units to */ + /* fractional pixels in FreeType, it resulted in noticeable speed */ + /* improvements between versions 2.x and 1.x. */ + /* */ + /* As a conclusion, always try to place a 16.16 factor as the */ + /* _second_ argument of this function; this can make a great */ + /* difference. */ + /* */ + FT_EXPORT( FT_Long ) + FT_MulFix( FT_Long a, + FT_Long b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_DivFix */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation */ + /* `(a*0x10000)/b' with maximal accuracy. Most of the time, this is */ + /* used to divide a given value by a 16.16 fixed float factor. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. Use a 16.16 factor here whenever */ + /* possible (see note below). */ + /* */ + /* <Return> */ + /* The result of `(a*0x10000)/b'. */ + /* */ + /* <Note> */ + /* The optimization for FT_DivFix() is simple: If (a << 16) fits in */ + /* 32 bits, then the division is computed directly. Otherwise, we */ + /* use a specialized version of @FT_MulDiv. */ + /* */ + FT_EXPORT( FT_Long ) + FT_DivFix( FT_Long a, + FT_Long b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_RoundFix */ + /* */ + /* <Description> */ + /* A very simple function used to round a 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number to be rounded. */ + /* */ + /* <Return> */ + /* The result of `(a + 0x8000) & -0x10000'. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_RoundFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_CeilFix */ + /* */ + /* <Description> */ + /* A very simple function used to compute the ceiling function of a */ + /* 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number for which the ceiling function is to be computed. */ + /* */ + /* <Return> */ + /* The result of `(a + 0x10000 - 1) & -0x10000'. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_CeilFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_FloorFix */ + /* */ + /* <Description> */ + /* A very simple function used to compute the floor function of a */ + /* 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number for which the floor function is to be computed. */ + /* */ + /* <Return> */ + /* The result of `a & -0x10000'. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_FloorFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Vector_Transform */ + /* */ + /* <Description> */ + /* Transforms a single vector through a 2x2 matrix. */ + /* */ + /* <InOut> */ + /* vector :: The target vector to transform. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the source 2x2 matrix. */ + /* */ + /* <Note> */ + /* The result is undefined if either `vector' or `matrix' is invalid. */ + /* */ + FT_EXPORT( void ) + FT_Vector_Transform( FT_Vector* vec, + FT_Matrix* matrix ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FREETYPE_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftbbox.h b/extra_lib/include/freetype/freetype/ftbbox.h new file mode 100644 index 0000000..f98c89e --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftbbox.h @@ -0,0 +1,89 @@ +/***************************************************************************/ +/* */ +/* ftbbox.h */ +/* */ +/* FreeType exact bbox computation (specification). */ +/* */ +/* Copyright 1996-2001, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This component has a _single_ role: to compute exact outline bounding */ + /* boxes. */ + /* */ + /* It is separated from the rest of the engine for various technical */ + /* reasons. It may well be integrated in `ftoutln' later. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTBBOX_H__ +#define __FTBBOX_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_BBox */ + /* */ + /* <Description> */ + /* Computes the exact bounding box of an outline. This is slower */ + /* than computing the control box. However, it uses an advanced */ + /* algorithm which returns _very_ quickly when the two boxes */ + /* coincide. Otherwise, the outline Bezier arcs are walked over to */ + /* extract their extrema. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source outline. */ + /* */ + /* <Output> */ + /* abbox :: The outline's exact bounding box. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Get_BBox( FT_Outline* outline, + FT_BBox *abbox ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FTBBOX_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftbdf.h b/extra_lib/include/freetype/freetype/ftbdf.h new file mode 100644 index 0000000..aa41144 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftbdf.h @@ -0,0 +1,200 @@ +/***************************************************************************/ +/* */ +/* ftbdf.h */ +/* */ +/* FreeType API for accessing BDF-specific strings (specification). */ +/* */ +/* Copyright 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTBDF_H__ +#define __FTBDF_H__ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* bdf_fonts */ + /* */ + /* <Title> */ + /* BDF Fonts */ + /* */ + /* <Abstract> */ + /* BDF-specific APIs */ + /* */ + /* <Description> */ + /* This section contains the declaration of BDF-specific functions. */ + /* */ + /*************************************************************************/ + + + /********************************************************************** + * + * @enum: + * FT_PropertyType + * + * @description: + * A list of BDF property types. + * + * @values: + * BDF_PROPERTY_TYPE_NONE :: + * Value 0 is used to indicate a missing property. + * + * BDF_PROPERTY_TYPE_ATOM :: + * Property is a string atom. + * + * BDF_PROPERTY_TYPE_INTEGER :: + * Property is a 32-bit signed integer. + * + * BDF_PROPERTY_TYPE_CARDINAL :: + * Property is a 32-bit unsigned integer. + */ + typedef enum BDF_PropertyType_ + { + BDF_PROPERTY_TYPE_NONE = 0, + BDF_PROPERTY_TYPE_ATOM = 1, + BDF_PROPERTY_TYPE_INTEGER = 2, + BDF_PROPERTY_TYPE_CARDINAL = 3 + + } BDF_PropertyType; + + + /********************************************************************** + * + * @type: + * BDF_Property + * + * @description: + * A handle to a @BDF_PropertyRec structure to model a given + * BDF/PCF property. + */ + typedef struct BDF_PropertyRec_* BDF_Property; + + + /********************************************************************** + * + * @struct: + * BDF_PropertyRec + * + * @description: + * This structure models a given BDF/PCF property. + * + * @fields: + * type :: + * The property type. + * + * u.atom :: + * The atom string, if type is @BDF_PROPERTY_TYPE_ATOM. + * + * u.integer :: + * A signed integer, if type is @BDF_PROPERTY_TYPE_INTEGER. + * + * u.cardinal :: + * An unsigned integer, if type is @BDF_PROPERTY_TYPE_CARDINAL. + */ + typedef struct BDF_PropertyRec_ + { + BDF_PropertyType type; + union { + const char* atom; + FT_Int32 integer; + FT_UInt32 cardinal; + + } u; + + } BDF_PropertyRec; + + + /********************************************************************** + * + * @function: + * FT_Get_BDF_Charset_ID + * + * @description: + * Retrieves a BDF font character set identity, according to + * the BDF specification. + * + * @input: + * face :: + * A handle to the input face. + * + * @output: + * acharset_encoding :: + * Charset encoding, as a C string, owned by the face. + * + * acharset_registry :: + * Charset registry, as a C string, owned by the face. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * This function only works with BDF faces, returning an error otherwise. + */ + FT_EXPORT( FT_Error ) + FT_Get_BDF_Charset_ID( FT_Face face, + const char* *acharset_encoding, + const char* *acharset_registry ); + + + /********************************************************************** + * + * @function: + * FT_Get_BDF_Property + * + * @description: + * Retrieves a BDF property from a BDF or PCF font file. + * + * @input: + * face :: A handle to the input face. + * + * name :: The property name. + * + * @output: + * aproperty :: The property. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * This function works with BDF _and_ PCF fonts. It returns an error + * otherwise. It also returns an error if the property is not in the + * font. + * + * In case of error, "aproperty->type" is always set to + * @BDF_PROPERTY_TYPE_NONE. + */ + FT_EXPORT( FT_Error ) + FT_Get_BDF_Property( FT_Face face, + const char* prop_name, + BDF_PropertyRec *aproperty ); + + /* */ + +FT_END_HEADER + +#endif /* __FTBDF_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftcache.h b/extra_lib/include/freetype/freetype/ftcache.h new file mode 100644 index 0000000..fd13f5d --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftcache.h @@ -0,0 +1,414 @@ +/***************************************************************************/ +/* */ +/* ftcache.h */ +/* */ +/* FreeType Cache subsystem (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********* *********/ + /********* WARNING, THIS IS BETA CODE. *********/ + /********* *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#ifndef __FTCACHE_H__ +#define __FTCACHE_H__ + + +#include <ft2build.h> +#include FT_GLYPH_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* cache_subsystem */ + /* */ + /* <Title> */ + /* Cache Sub-System */ + /* */ + /* <Abstract> */ + /* How to cache face, size, and glyph data with FreeType 2. */ + /* */ + /* <Description> */ + /* This section describes the FreeType 2 cache sub-system which is */ + /* stile in beta. */ + /* */ + /* <Order> */ + /* FTC_Manager */ + /* FTC_FaceID */ + /* FTC_Face_Requester */ + /* */ + /* FTC_Manager_New */ + /* FTC_Manager_Lookup_Face */ + /* FTC_Manager_Lookup_Size */ + /* */ + /* FTC_Node */ + /* FTC_Node_Ref */ + /* FTC_Node_Unref */ + /* */ + /* FTC_Font */ + /* FTC_ImageCache */ + /* FTC_ImageCache_New */ + /* FTC_ImageCache_Lookup */ + /* */ + /* FTC_SBit */ + /* FTC_SBitCache */ + /* FTC_SBitCache_New */ + /* FTC_SBitCache_Lookup */ + /* */ + /* */ + /* FTC_Image_Desc */ + /* FTC_Image_Cache */ + /* FTC_Image_Cache_Lookup */ + /* */ + /* FTC_SBit_Cache */ + /* FTC_SBit_Cache_Lookup */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BASIC TYPE DEFINITIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_FaceID */ + /* */ + /* <Description> */ + /* A generic pointer type that is used to identity face objects. The */ + /* contents of such objects is application-dependent. */ + /* */ + typedef FT_Pointer FTC_FaceID; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FTC_Face_Requester */ + /* */ + /* <Description> */ + /* A callback function provided by client applications. It is used */ + /* to translate a given @FTC_FaceID into a new valid @FT_Face object. */ + /* */ + /* <Input> */ + /* face_id :: The face ID to resolve. */ + /* */ + /* library :: A handle to a FreeType library object. */ + /* */ + /* data :: Application-provided request data. */ + /* */ + /* <Output> */ + /* aface :: A new @FT_Face handle. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The face requester should not perform funny things on the returned */ + /* face object, like creating a new @FT_Size for it, or setting a */ + /* transformation through @FT_Set_Transform! */ + /* */ + typedef FT_Error + (*FTC_Face_Requester)( FTC_FaceID face_id, + FT_Library library, + FT_Pointer request_data, + FT_Face* aface ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FTC_FontRec */ + /* */ + /* <Description> */ + /* A simple structure used to describe a given `font' to the cache */ + /* manager. Note that a `font' is the combination of a given face */ + /* with a given character size. */ + /* */ + /* <Fields> */ + /* face_id :: The ID of the face to use. */ + /* */ + /* pix_width :: The character width in integer pixels. */ + /* */ + /* pix_height :: The character height in integer pixels. */ + /* */ + typedef struct FTC_FontRec_ + { + FTC_FaceID face_id; + FT_UShort pix_width; + FT_UShort pix_height; + + } FTC_FontRec; + + + /* */ + + +#define FTC_FONT_COMPARE( f1, f2 ) \ + ( (f1)->face_id == (f2)->face_id && \ + (f1)->pix_width == (f2)->pix_width && \ + (f1)->pix_height == (f2)->pix_height ) + +#define FT_POINTER_TO_ULONG( p ) ((FT_ULong)(FT_Pointer)(p)) + +#define FTC_FACE_ID_HASH( i ) \ + ((FT_UInt32)(( FT_POINTER_TO_ULONG( i ) >> 3 ) ^ \ + ( FT_POINTER_TO_ULONG( i ) << 7 ) ) ) + +#define FTC_FONT_HASH( f ) \ + (FT_UInt32)( FTC_FACE_ID_HASH((f)->face_id) ^ \ + ((f)->pix_width << 8) ^ \ + ((f)->pix_height) ) + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_Font */ + /* */ + /* <Description> */ + /* A simple handle to an @FTC_FontRec structure. */ + /* */ + typedef FTC_FontRec* FTC_Font; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE MANAGER OBJECT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_Manager */ + /* */ + /* <Description> */ + /* This object is used to cache one or more @FT_Face objects, along */ + /* with corresponding @FT_Size objects. */ + /* */ + typedef struct FTC_ManagerRec_* FTC_Manager; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_Node */ + /* */ + /* <Description> */ + /* An opaque handle to a cache node object. Each cache node is */ + /* reference-counted. A node with a count of 0 might be flushed */ + /* out of a full cache whenever a lookup request is performed. */ + /* */ + /* If you lookup nodes, you have the ability to "acquire" them, i.e., */ + /* to increment their reference count. This will prevent the node */ + /* from being flushed out of the cache until you explicitly "release" */ + /* it (see @FTC_Node_Unref). */ + /* */ + /* See also @FTC_SBitCache_Lookup and @FTC_ImageCache_Lookup. */ + /* */ + typedef struct FTC_NodeRec_* FTC_Node; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_New */ + /* */ + /* <Description> */ + /* Creates a new cache manager. */ + /* */ + /* <Input> */ + /* library :: The parent FreeType library handle to use. */ + /* */ + /* max_faces :: Maximum number of faces to keep alive in manager. */ + /* Use 0 for defaults. */ + /* */ + /* max_sizes :: Maximum number of sizes to keep alive in manager. */ + /* Use 0 for defaults. */ + /* */ + /* max_bytes :: Maximum number of bytes to use for cached data. */ + /* Use 0 for defaults. */ + /* */ + /* requester :: An application-provided callback used to translate */ + /* face IDs into real @FT_Face objects. */ + /* */ + /* req_data :: A generic pointer that is passed to the requester */ + /* each time it is called (see @FTC_Face_Requester). */ + /* */ + /* <Output> */ + /* amanager :: A handle to a new manager object. 0 in case of */ + /* failure. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Manager_New( FT_Library library, + FT_UInt max_faces, + FT_UInt max_sizes, + FT_ULong max_bytes, + FTC_Face_Requester requester, + FT_Pointer req_data, + FTC_Manager *amanager ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_Reset */ + /* */ + /* <Description> */ + /* Empties a given cache manager. This simply gets rid of all the */ + /* currently cached @FT_Face and @FT_Size objects within the manager. */ + /* */ + /* <InOut> */ + /* manager :: A handle to the manager. */ + /* */ + FT_EXPORT( void ) + FTC_Manager_Reset( FTC_Manager manager ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_Done */ + /* */ + /* <Description> */ + /* Destroys a given manager after emptying it. */ + /* */ + /* <Input> */ + /* manager :: A handle to the target cache manager object. */ + /* */ + FT_EXPORT( void ) + FTC_Manager_Done( FTC_Manager manager ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_Lookup_Face */ + /* */ + /* <Description> */ + /* Retrieves the @FT_Face object that corresponds to a given face ID */ + /* through a cache manager. */ + /* */ + /* <Input> */ + /* manager :: A handle to the cache manager. */ + /* */ + /* face_id :: The ID of the face object. */ + /* */ + /* <Output> */ + /* aface :: A handle to the face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The returned @FT_Face object is always owned by the manager. You */ + /* should never try to discard it yourself. */ + /* */ + /* The @FT_Face object doesn't necessarily have a current size object */ + /* (i.e., face->size can be 0). If you need a specific `font size', */ + /* use @FTC_Manager_Lookup_Size instead. */ + /* */ + /* Never change the face's transformation matrix (i.e., never call */ + /* the @FT_Set_Transform function) on a returned face! If you need */ + /* to transform glyphs, do it yourself after glyph loading. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Manager_Lookup_Face( FTC_Manager manager, + FTC_FaceID face_id, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_Lookup_Size */ + /* */ + /* <Description> */ + /* Retrieves the @FT_Face and @FT_Size objects that correspond to a */ + /* given font. */ + /* */ + /* <Input> */ + /* manager :: A handle to the cache manager. */ + /* */ + /* font :: The font to use. */ + /* */ + /* <Output> */ + /* aface :: A pointer to the handle of the face object. Set it to */ + /* zero if you don't need it. */ + /* */ + /* asize :: A pointer to the handle of the size object. Set it to */ + /* zero if you don't need it. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The returned @FT_Face object is always owned by the manager. You */ + /* should never try to discard it yourself. */ + /* */ + /* Never change the face's transformation matrix (i.e., never call */ + /* the @FT_Set_Transform function) on a returned face! If you need */ + /* to transform glyphs, do it yourself after glyph loading. */ + /* */ + /* Similarly, the returned @FT_Size object is always owned by the */ + /* manager. You should never try to discard it, and never change its */ + /* settings with @FT_Set_Pixel_Sizes or @FT_Set_Char_Size! */ + /* */ + /* The returned size object is the face's current size, which means */ + /* that you can call @FT_Load_Glyph with the face if you need to. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Manager_Lookup_Size( FTC_Manager manager, + FTC_Font font, + FT_Face *aface, + FT_Size *asize ); + + +FT_END_HEADER + +#endif /* __FTCACHE_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftchapters.h b/extra_lib/include/freetype/freetype/ftchapters.h new file mode 100644 index 0000000..c134ec1 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftchapters.h @@ -0,0 +1,69 @@ +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* core_api */ +/* */ +/* <Title> */ +/* Core API */ +/* */ +/* <Sections> */ +/* basic_types */ +/* base_interface */ +/* glyph_management */ +/* mac_specific */ +/* sizes_management */ +/* header_file_macros */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* format_specific */ +/* */ +/* <Title> */ +/* Format-Specific API */ +/* */ +/* <Sections> */ +/* multiple_masters */ +/* truetype_tables */ +/* type1_tables */ +/* sfnt_names */ +/* bdf_fonts */ +/* pfr_fonts */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* cache_subsystem */ +/* */ +/* <Title> */ +/* Cache Sub-System */ +/* */ +/* <Sections> */ +/* cache_subsystem */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* support_api */ +/* */ +/* <Title> */ +/* Support API */ +/* */ +/* <Sections> */ +/* computations */ +/* list_processing */ +/* outline_processing */ +/* raster */ +/* system_interface */ +/* module_management */ +/* */ +/***************************************************************************/ + diff --git a/extra_lib/include/freetype/freetype/fterrdef.h b/extra_lib/include/freetype/freetype/fterrdef.h new file mode 100644 index 0000000..4ef606d --- /dev/null +++ b/extra_lib/include/freetype/freetype/fterrdef.h @@ -0,0 +1,229 @@ +/***************************************************************************/ +/* */ +/* fterrdef.h */ +/* */ +/* FreeType error codes (specification). */ +/* */ +/* Copyright 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** LIST OF ERROR CODES/MESSAGES *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + + /* You need to define both FT_ERRORDEF_ and FT_NOERRORDEF_ before */ + /* including this file. */ + + + /* generic errors */ + + FT_NOERRORDEF_( Ok, 0x00, \ + "no error" ) + + FT_ERRORDEF_( Cannot_Open_Resource, 0x01, \ + "cannot open resource" ) + FT_ERRORDEF_( Unknown_File_Format, 0x02, \ + "unknown file format" ) + FT_ERRORDEF_( Invalid_File_Format, 0x03, \ + "broken file" ) + FT_ERRORDEF_( Invalid_Version, 0x04, \ + "invalid FreeType version" ) + FT_ERRORDEF_( Lower_Module_Version, 0x05, \ + "module version is too low" ) + FT_ERRORDEF_( Invalid_Argument, 0x06, \ + "invalid argument" ) + FT_ERRORDEF_( Unimplemented_Feature, 0x07, \ + "unimplemented feature" ) + FT_ERRORDEF_( Invalid_Table, 0x08, \ + "broken table" ) + FT_ERRORDEF_( Invalid_Offset, 0x09, \ + "broken offset within table" ) + + /* glyph/character errors */ + + FT_ERRORDEF_( Invalid_Glyph_Index, 0x10, \ + "invalid glyph index" ) + FT_ERRORDEF_( Invalid_Character_Code, 0x11, \ + "invalid character code" ) + FT_ERRORDEF_( Invalid_Glyph_Format, 0x12, \ + "unsupported glyph image format" ) + FT_ERRORDEF_( Cannot_Render_Glyph, 0x13, \ + "cannot render this glyph format" ) + FT_ERRORDEF_( Invalid_Outline, 0x14, \ + "invalid outline" ) + FT_ERRORDEF_( Invalid_Composite, 0x15, \ + "invalid composite glyph" ) + FT_ERRORDEF_( Too_Many_Hints, 0x16, \ + "too many hints" ) + FT_ERRORDEF_( Invalid_Pixel_Size, 0x17, \ + "invalid pixel size" ) + + /* handle errors */ + + FT_ERRORDEF_( Invalid_Handle, 0x20, \ + "invalid object handle" ) + FT_ERRORDEF_( Invalid_Library_Handle, 0x21, \ + "invalid library handle" ) + FT_ERRORDEF_( Invalid_Driver_Handle, 0x22, \ + "invalid module handle" ) + FT_ERRORDEF_( Invalid_Face_Handle, 0x23, \ + "invalid face handle" ) + FT_ERRORDEF_( Invalid_Size_Handle, 0x24, \ + "invalid size handle" ) + FT_ERRORDEF_( Invalid_Slot_Handle, 0x25, \ + "invalid glyph slot handle" ) + FT_ERRORDEF_( Invalid_CharMap_Handle, 0x26, \ + "invalid charmap handle" ) + FT_ERRORDEF_( Invalid_Cache_Handle, 0x27, \ + "invalid cache manager handle" ) + FT_ERRORDEF_( Invalid_Stream_Handle, 0x28, \ + "invalid stream handle" ) + + /* driver errors */ + + FT_ERRORDEF_( Too_Many_Drivers, 0x30, \ + "too many modules" ) + FT_ERRORDEF_( Too_Many_Extensions, 0x31, \ + "too many extensions" ) + + /* memory errors */ + + FT_ERRORDEF_( Out_Of_Memory, 0x40, \ + "out of memory" ) + FT_ERRORDEF_( Unlisted_Object, 0x41, \ + "unlisted object" ) + + /* stream errors */ + + FT_ERRORDEF_( Cannot_Open_Stream, 0x51, \ + "cannot open stream" ) + FT_ERRORDEF_( Invalid_Stream_Seek, 0x52, \ + "invalid stream seek" ) + FT_ERRORDEF_( Invalid_Stream_Skip, 0x53, \ + "invalid stream skip" ) + FT_ERRORDEF_( Invalid_Stream_Read, 0x54, \ + "invalid stream read" ) + FT_ERRORDEF_( Invalid_Stream_Operation, 0x55, \ + "invalid stream operation" ) + FT_ERRORDEF_( Invalid_Frame_Operation, 0x56, \ + "invalid frame operation" ) + FT_ERRORDEF_( Nested_Frame_Access, 0x57, \ + "nested frame access" ) + FT_ERRORDEF_( Invalid_Frame_Read, 0x58, \ + "invalid frame read" ) + + /* raster errors */ + + FT_ERRORDEF_( Raster_Uninitialized, 0x60, \ + "raster uninitialized" ) + FT_ERRORDEF_( Raster_Corrupted, 0x61, \ + "raster corrupted" ) + FT_ERRORDEF_( Raster_Overflow, 0x62, \ + "raster overflow" ) + FT_ERRORDEF_( Raster_Negative_Height, 0x63, \ + "negative height while rastering" ) + + /* cache errors */ + + FT_ERRORDEF_( Too_Many_Caches, 0x70, \ + "too many registered caches" ) + + /* TrueType and SFNT errors */ + + FT_ERRORDEF_( Invalid_Opcode, 0x80, \ + "invalid opcode" ) + FT_ERRORDEF_( Too_Few_Arguments, 0x81, \ + "too few arguments" ) + FT_ERRORDEF_( Stack_Overflow, 0x82, \ + "stack overflow" ) + FT_ERRORDEF_( Code_Overflow, 0x83, \ + "code overflow" ) + FT_ERRORDEF_( Bad_Argument, 0x84, \ + "bad argument" ) + FT_ERRORDEF_( Divide_By_Zero, 0x85, \ + "division by zero" ) + FT_ERRORDEF_( Invalid_Reference, 0x86, \ + "invalid reference" ) + FT_ERRORDEF_( Debug_OpCode, 0x87, \ + "found debug opcode" ) + FT_ERRORDEF_( ENDF_In_Exec_Stream, 0x88, \ + "found ENDF opcode in execution stream" ) + FT_ERRORDEF_( Nested_DEFS, 0x89, \ + "nested DEFS" ) + FT_ERRORDEF_( Invalid_CodeRange, 0x8A, \ + "invalid code range" ) + FT_ERRORDEF_( Execution_Too_Long, 0x8B, \ + "execution context too long" ) + FT_ERRORDEF_( Too_Many_Function_Defs, 0x8C, \ + "too many function definitions" ) + FT_ERRORDEF_( Too_Many_Instruction_Defs, 0x8D, \ + "too many instruction definitions" ) + FT_ERRORDEF_( Table_Missing, 0x8E, \ + "SFNT font table missing" ) + FT_ERRORDEF_( Horiz_Header_Missing, 0x8F, \ + "horizontal header (hhea) table missing" ) + FT_ERRORDEF_( Locations_Missing, 0x90, \ + "locations (loca) table missing" ) + FT_ERRORDEF_( Name_Table_Missing, 0x91, \ + "name table missing" ) + FT_ERRORDEF_( CMap_Table_Missing, 0x92, \ + "character map (cmap) table missing" ) + FT_ERRORDEF_( Hmtx_Table_Missing, 0x93, \ + "horizontal metrics (hmtx) table missing" ) + FT_ERRORDEF_( Post_Table_Missing, 0x94, \ + "PostScript (post) table missing" ) + FT_ERRORDEF_( Invalid_Horiz_Metrics, 0x95, \ + "invalid horizontal metrics" ) + FT_ERRORDEF_( Invalid_CharMap_Format, 0x96, \ + "invalid character map (cmap) format" ) + FT_ERRORDEF_( Invalid_PPem, 0x97, \ + "invalid ppem value" ) + FT_ERRORDEF_( Invalid_Vert_Metrics, 0x98, \ + "invalid vertical metrics" ) + FT_ERRORDEF_( Could_Not_Find_Context, 0x99, \ + "could not find context" ) + FT_ERRORDEF_( Invalid_Post_Table_Format, 0x9A, \ + "invalid PostScript (post) table format" ) + FT_ERRORDEF_( Invalid_Post_Table, 0x9B, \ + "invalid PostScript (post) table" ) + + /* CFF, CID, and Type 1 errors */ + + FT_ERRORDEF_( Syntax_Error, 0xA0, \ + "opcode syntax error" ) + FT_ERRORDEF_( Stack_Underflow, 0xA1, \ + "argument stack underflow" ) + + /* BDF errors */ + + FT_ERRORDEF_( Missing_Startfont_Field, 0xB0, \ + "`STARTFONT' field missing" ) + FT_ERRORDEF_( Missing_Font_Field, 0xB1, \ + "`FONT' field missing" ) + FT_ERRORDEF_( Missing_Size_Field, 0xB2, \ + "`SIZE' field missing" ) + FT_ERRORDEF_( Missing_Chars_Field, 0xB3, \ + "`CHARS' field missing" ) + FT_ERRORDEF_( Missing_Startchar_Field, 0xB4, \ + "`STARTCHAR' field missing" ) + FT_ERRORDEF_( Missing_Encoding_Field, 0xB5, \ + "`ENCODING' field missing" ) + FT_ERRORDEF_( Missing_Bbx_Field, 0xB6, \ + "`BBX' field missing" ) + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/fterrors.h b/extra_lib/include/freetype/freetype/fterrors.h new file mode 100644 index 0000000..1def4f9 --- /dev/null +++ b/extra_lib/include/freetype/freetype/fterrors.h @@ -0,0 +1,207 @@ +/***************************************************************************/ +/* */ +/* fterrors.h */ +/* */ +/* FreeType error code handling (specification). */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This special header file is used to define the handling of FT2 */ + /* enumeration constants. It can also be used to generate error message */ + /* strings with a small macro trick explained below. */ + /* */ + /* I - Error Formats */ + /* ----------------- */ + /* */ + /* Since release 2.1, the error constants have changed. The lower */ + /* byte of the error value gives the "generic" error code, while the */ + /* higher byte indicates in which module the error occurred. */ + /* */ + /* You can use the macro FT_ERROR_BASE(x) macro to extract the generic */ + /* error code from an FT_Error value. */ + /* */ + /* The configuration macro FT_CONFIG_OPTION_USE_MODULE_ERRORS can be */ + /* undefined in ftoption.h in order to make the higher byte always */ + /* zero, in case you need to be compatible with previous versions of */ + /* FreeType 2. */ + /* */ + /* */ + /* II - Error Message strings */ + /* -------------------------- */ + /* */ + /* The error definitions below are made through special macros that */ + /* allow client applications to build a table of error message strings */ + /* if they need it. The strings are not included in a normal build of */ + /* FreeType 2 to save space (most client applications do not use */ + /* them). */ + /* */ + /* To do so, you have to define the following macros before including */ + /* this file: */ + /* */ + /* FT_ERROR_START_LIST :: */ + /* This macro is called before anything else to define the start of */ + /* the error list. It is followed by several FT_ERROR_DEF calls */ + /* (see below). */ + /* */ + /* FT_ERROR_DEF( e, v, s ) :: */ + /* This macro is called to define one single error. */ + /* `e' is the error code identifier (e.g. FT_Err_Invalid_Argument). */ + /* `v' is the error numerical value. */ + /* `s' is the corresponding error string. */ + /* */ + /* FT_ERROR_END_LIST :: */ + /* This macro ends the list. */ + /* */ + /* Additionally, you have to undefine __FTERRORS_H__ before #including */ + /* this file. */ + /* */ + /* Here is a simple example: */ + /* */ + /* { */ + /* #undef __FTERRORS_H__ */ + /* #define FT_ERRORDEF( e, v, s ) { e, s }, */ + /* #define FT_ERROR_START_LIST { */ + /* #define FT_ERROR_END_LIST { 0, 0 } }; */ + /* */ + /* const struct */ + /* { */ + /* int err_code; */ + /* const char* err_msg */ + /* } ft_errors[] = */ + /* */ + /* #include FT_ERRORS_H */ + /* } */ + /* */ + /*************************************************************************/ + + +#ifndef __FTERRORS_H__ +#define __FTERRORS_H__ + + + /* include module base error codes */ +#include FT_MODULE_ERRORS_H + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SETUP MACROS *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#undef FT_NEED_EXTERN_C + +#undef FT_ERR_XCAT +#undef FT_ERR_CAT + +#define FT_ERR_XCAT( x, y ) x ## y +#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) + + + /* FT_ERR_PREFIX is used as a prefix for error identifiers. */ + /* By default, we use `FT_Err_'. */ + /* */ +#ifndef FT_ERR_PREFIX +#define FT_ERR_PREFIX FT_Err_ +#endif + + + /* FT_ERR_BASE is used as the base for module-specific errors. */ + /* */ +#ifdef FT_CONFIG_OPTION_USE_MODULE_ERRORS + +#ifndef FT_ERR_BASE +#define FT_ERR_BASE FT_Mod_Err_Base +#endif + +#else + +#undef FT_ERR_BASE +#define FT_ERR_BASE 0 + +#endif /* FT_CONFIG_OPTION_USE_MODULE_ERRORS */ + + + /* If FT_ERRORDEF is not defined, we need to define a simple */ + /* enumeration type. */ + /* */ +#ifndef FT_ERRORDEF + +#define FT_ERRORDEF( e, v, s ) e = v, +#define FT_ERROR_START_LIST enum { +#define FT_ERROR_END_LIST FT_ERR_CAT( FT_ERR_PREFIX, Max ) }; + +#ifdef __cplusplus +#define FT_NEED_EXTERN_C + extern "C" { +#endif + +#endif /* !FT_ERRORDEF */ + + + /* this macro is used to define an error */ +#define FT_ERRORDEF_( e, v, s ) \ + FT_ERRORDEF( FT_ERR_CAT( FT_ERR_PREFIX, e ), v + FT_ERR_BASE, s ) + + /* this is only used for FT_Err_Ok, which must be 0! */ +#define FT_NOERRORDEF_( e, v, s ) \ + FT_ERRORDEF( FT_ERR_CAT( FT_ERR_PREFIX, e ), v, s ) + + +#ifdef FT_ERROR_START_LIST + FT_ERROR_START_LIST +#endif + + + /* no include the error codes */ +#include FT_ERROR_DEFINITIONS_H + + +#ifdef FT_ERROR_END_LIST + FT_ERROR_END_LIST +#endif + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SIMPLE CLEANUP *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + +#ifdef FT_NEED_EXTERN_C + } +#endif + +#undef FT_ERROR_START_LIST +#undef FT_ERROR_END_LIST + +#undef FT_ERRORDEF +#undef FT_ERRORDEF_ +#undef FT_NOERRORDEF_ + +#undef FT_NEED_EXTERN_C +#undef FT_ERR_PREFIX +#undef FT_ERR_BASE +#undef FT_ERR_CONCAT + +#endif /* __FTERRORS_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftglyph.h b/extra_lib/include/freetype/freetype/ftglyph.h new file mode 100644 index 0000000..8019e16 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftglyph.h @@ -0,0 +1,566 @@ +/***************************************************************************/ +/* */ +/* ftglyph.h */ +/* */ +/* FreeType convenience functions to handle glyphs (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file contains the definition of several convenience functions */ + /* that can be used by client applications to easily retrieve glyph */ + /* bitmaps and outlines from a given face. */ + /* */ + /* These functions should be optional if you are writing a font server */ + /* or text layout engine on top of FreeType. However, they are pretty */ + /* handy for many other simple uses of the library. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTGLYPH_H__ +#define __FTGLYPH_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* glyph_management */ + /* */ + /* <Title> */ + /* Glyph Management */ + /* */ + /* <Abstract> */ + /* Generic interface to manage individual glyph data. */ + /* */ + /* <Description> */ + /* This section contains definitions used to manage glyph data */ + /* through generic FT_Glyph objects. Each of them can contain a */ + /* bitmap, a vector outline, or even images in other formats. */ + /* */ + /*************************************************************************/ + + + /* forward declaration to a private type */ + typedef struct FT_Glyph_Class_ FT_Glyph_Class; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Glyph */ + /* */ + /* <Description> */ + /* Handle to an object used to model generic glyph images. It is a */ + /* pointer to the @FT_GlyphRec structure and can contain a glyph */ + /* bitmap or pointer. */ + /* */ + /* <Note> */ + /* Glyph objects are not owned by the library. You must thus release */ + /* them manually (through @FT_Done_Glyph) _before_ calling */ + /* @FT_Done_FreeType. */ + /* */ + typedef struct FT_GlyphRec_* FT_Glyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_GlyphRec */ + /* */ + /* <Description> */ + /* The root glyph structure contains a given glyph image plus its */ + /* advance width in 16.16 fixed float format. */ + /* */ + /* <Fields> */ + /* library :: A handle to the FreeType library object. */ + /* */ + /* clazz :: A pointer to the glyph's class. Private. */ + /* */ + /* format :: The format of the glyph's image. */ + /* */ + /* advance :: A 16.16 vector that gives the glyph's advance width. */ + /* */ + typedef struct FT_GlyphRec_ + { + FT_Library library; + const FT_Glyph_Class* clazz; + FT_Glyph_Format format; + FT_Vector advance; + + } FT_GlyphRec; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_BitmapGlyph */ + /* */ + /* <Description> */ + /* A handle to an object used to model a bitmap glyph image. This is */ + /* a sub-class of @FT_Glyph, and a pointer to @FT_BitmapGlyphRec. */ + /* */ + typedef struct FT_BitmapGlyphRec_* FT_BitmapGlyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_BitmapGlyphRec */ + /* */ + /* <Description> */ + /* A structure used for bitmap glyph images. This really is a */ + /* `sub-class' of `FT_GlyphRec'. */ + /* */ + /* <Fields> */ + /* root :: The root FT_Glyph fields. */ + /* */ + /* left :: The left-side bearing, i.e., the horizontal distance */ + /* from the current pen position to the left border of the */ + /* glyph bitmap. */ + /* */ + /* top :: The top-side bearing, i.e., the vertical distance from */ + /* the current pen position to the top border of the glyph */ + /* bitmap. This distance is positive for upwards-y! */ + /* */ + /* bitmap :: A descriptor for the bitmap. */ + /* */ + /* <Note> */ + /* You can typecast FT_Glyph to FT_BitmapGlyph if you have */ + /* glyph->format == FT_GLYPH_FORMAT_BITMAP. This lets you access */ + /* the bitmap's contents easily. */ + /* */ + /* The corresponding pixel buffer is always owned by the BitmapGlyph */ + /* and is thus created and destroyed with it. */ + /* */ + typedef struct FT_BitmapGlyphRec_ + { + FT_GlyphRec root; + FT_Int left; + FT_Int top; + FT_Bitmap bitmap; + + } FT_BitmapGlyphRec; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_OutlineGlyph */ + /* */ + /* <Description> */ + /* A handle to an object used to model an outline glyph image. This */ + /* is a sub-class of @FT_Glyph, and a pointer to @FT_OutlineGlyphRec. */ + /* */ + typedef struct FT_OutlineGlyphRec_* FT_OutlineGlyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_OutlineGlyphRec */ + /* */ + /* <Description> */ + /* A structure used for outline (vectorial) glyph images. This */ + /* really is a `sub-class' of `FT_GlyphRec'. */ + /* */ + /* <Fields> */ + /* root :: The root FT_Glyph fields. */ + /* */ + /* outline :: A descriptor for the outline. */ + /* */ + /* <Note> */ + /* You can typecast FT_Glyph to FT_OutlineGlyph if you have */ + /* glyph->format == FT_GLYPH_FORMAT_OUTLINE. This lets you access */ + /* the outline's content easily. */ + /* */ + /* As the outline is extracted from a glyph slot, its coordinates are */ + /* expressed normally in 26.6 pixels, unless the flag */ + /* FT_LOAD_NO_SCALE was used in FT_Load_Glyph() or FT_Load_Char(). */ + /* */ + /* The outline's tables are always owned by the object and are */ + /* destroyed with it. */ + /* */ + typedef struct FT_OutlineGlyphRec_ + { + FT_GlyphRec root; + FT_Outline outline; + + } FT_OutlineGlyphRec; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Glyph */ + /* */ + /* <Description> */ + /* A function used to extract a glyph image from a slot. */ + /* */ + /* <Input> */ + /* slot :: A handle to the source glyph slot. */ + /* */ + /* <Output> */ + /* aglyph :: A handle to the glyph object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Glyph( FT_GlyphSlot slot, + FT_Glyph *aglyph ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Copy */ + /* */ + /* <Description> */ + /* A function used to copy a glyph image. Note that the created */ + /* @FT_Glyph object must be released with @FT_Done_Glyph. */ + /* */ + /* <Input> */ + /* source :: A handle to the source glyph object. */ + /* */ + /* <Output> */ + /* target :: A handle to the target glyph object. 0 in case of */ + /* error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_Copy( FT_Glyph source, + FT_Glyph *target ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Transform */ + /* */ + /* <Description> */ + /* Transforms a glyph image if its format is scalable. */ + /* */ + /* <InOut> */ + /* glyph :: A handle to the target glyph object. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to a 2x2 matrix to apply. */ + /* */ + /* delta :: A pointer to a 2d vector to apply. Coordinates are */ + /* expressed in 1/64th of a pixel. */ + /* */ + /* <Return> */ + /* FreeType error code (the glyph format is not scalable if it is */ + /* not zero). */ + /* */ + /* <Note> */ + /* The 2x2 transformation matrix is also applied to the glyph's */ + /* advance vector. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_Transform( FT_Glyph glyph, + FT_Matrix* matrix, + FT_Vector* delta ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Glyph_BBox_Mode */ + /* */ + /* <Description> */ + /* The mode how the values of @FT_Glyph_Get_CBox are returned. */ + /* */ + /* <Values> */ + /* FT_GLYPH_BBOX_UNSCALED :: */ + /* Return unscaled font units. */ + /* */ + /* FT_GLYPH_BBOX_SUBPIXELS :: */ + /* Return unfitted 26.6 coordinates. */ + /* */ + /* FT_GLYPH_BBOX_GRIDFIT :: */ + /* Return grid-fitted 26.6 coordinates. */ + /* */ + /* FT_GLYPH_BBOX_TRUNCATE :: */ + /* Return coordinates in integer pixels. */ + /* */ + /* FT_GLYPH_BBOX_PIXELS :: */ + /* Return grid-fitted pixel coordinates. */ + /* */ + typedef enum FT_Glyph_BBox_Mode_ + { + FT_GLYPH_BBOX_UNSCALED = 0, + FT_GLYPH_BBOX_SUBPIXELS = 0, + FT_GLYPH_BBOX_GRIDFIT = 1, + FT_GLYPH_BBOX_TRUNCATE = 2, + FT_GLYPH_BBOX_PIXELS = 3 + + } FT_Glyph_BBox_Mode; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* ft_glyph_bbox_xxx */ + /* */ + /* <Description> */ + /* These constants are deprecated. Use the corresponding */ + /* @FT_Glyph_BBox_Mode values instead. */ + /* */ + /* <Values> */ + /* ft_glyph_bbox_unscaled :: see @FT_GLYPH_BBOX_UNSCALED */ + /* ft_glyph_bbox_subpixels :: see @FT_GLYPH_BBOX_SUBPIXELS */ + /* ft_glyph_bbox_gridfit :: see @FT_GLYPH_BBOX_GRIDFIT */ + /* ft_glyph_bbox_truncate :: see @FT_GLYPH_BBOX_TRUNCATE */ + /* ft_glyph_bbox_pixels :: see @FT_GLYPH_BBOX_PIXELS */ + /* */ +#define ft_glyph_bbox_unscaled FT_GLYPH_BBOX_UNSCALED +#define ft_glyph_bbox_subpixels FT_GLYPH_BBOX_SUBPIXELS +#define ft_glyph_bbox_gridfit FT_GLYPH_BBOX_GRIDFIT +#define ft_glyph_bbox_truncate FT_GLYPH_BBOX_TRUNCATE +#define ft_glyph_bbox_pixels FT_GLYPH_BBOX_PIXELS + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Get_CBox */ + /* */ + /* <Description> */ + /* Returns a glyph's `control box'. The control box encloses all the */ + /* outline's points, including Bezier control points. Though it */ + /* coincides with the exact bounding box for most glyphs, it can be */ + /* slightly larger in some situations (like when rotating an outline */ + /* which contains Bezier outside arcs). */ + /* */ + /* Computing the control box is very fast, while getting the bounding */ + /* box can take much more time as it needs to walk over all segments */ + /* and arcs in the outline. To get the latter, you can use the */ + /* `ftbbox' component which is dedicated to this single task. */ + /* */ + /* <Input> */ + /* glyph :: A handle to the source glyph object. */ + /* */ + /* mode :: The mode which indicates how to interpret the returned */ + /* bounding box values. */ + /* */ + /* <Output> */ + /* acbox :: The glyph coordinate bounding box. Coordinates are */ + /* expressed in 1/64th of pixels if it is grid-fitted. */ + /* */ + /* <Note> */ + /* Coordinates are relative to the glyph origin, using the Y-upwards */ + /* convention. */ + /* */ + /* If the glyph has been loaded with FT_LOAD_NO_SCALE, `bbox_mode' */ + /* must be set to `FT_GLYPH_BBOX_UNSCALED' to get unscaled font */ + /* units in 26.6 pixel format. The value `FT_GLYPH_BBOX_SUBPIXELS' */ + /* is another name for this constant. */ + /* */ + /* Note that the maximum coordinates are exclusive, which means that */ + /* one can compute the width and height of the glyph image (be it in */ + /* integer or 26.6 pixels) as: */ + /* */ + /* width = bbox.xMax - bbox.xMin; */ + /* height = bbox.yMax - bbox.yMin; */ + /* */ + /* Note also that for 26.6 coordinates, if `bbox_mode' is set to */ + /* `FT_GLYPH_BBOX_GRIDFIT', the coordinates will also be grid-fitted, */ + /* which corresponds to: */ + /* */ + /* bbox.xMin = FLOOR(bbox.xMin); */ + /* bbox.yMin = FLOOR(bbox.yMin); */ + /* bbox.xMax = CEILING(bbox.xMax); */ + /* bbox.yMax = CEILING(bbox.yMax); */ + /* */ + /* To get the bbox in pixel coordinates, set `bbox_mode' to */ + /* `FT_GLYPH_BBOX_TRUNCATE'. */ + /* */ + /* To get the bbox in grid-fitted pixel coordinates, set `bbox_mode' */ + /* to `FT_GLYPH_BBOX_PIXELS'. */ + /* */ + FT_EXPORT( void ) + FT_Glyph_Get_CBox( FT_Glyph glyph, + FT_UInt bbox_mode, + FT_BBox *acbox ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_To_Bitmap */ + /* */ + /* <Description> */ + /* Converts a given glyph object to a bitmap glyph object. */ + /* */ + /* <InOut> */ + /* the_glyph :: A pointer to a handle to the target glyph. */ + /* */ + /* <Input> */ + /* render_mode :: An enumeration that describe how the data is */ + /* rendered. */ + /* */ + /* origin :: A pointer to a vector used to translate the glyph */ + /* image before rendering. Can be 0 (if no */ + /* translation). The origin is expressed in */ + /* 26.6 pixels. */ + /* */ + /* destroy :: A boolean that indicates that the original glyph */ + /* image should be destroyed by this function. It is */ + /* never destroyed in case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The glyph image is translated with the `origin' vector before */ + /* rendering. */ + /* */ + /* The first parameter is a pointer to a FT_Glyph handle, that will */ + /* be replaced by this function. Typically, you would use (omitting */ + /* error handling): */ + /* */ + /* */ + /* { */ + /* FT_Glyph glyph; */ + /* FT_BitmapGlyph glyph_bitmap; */ + /* */ + /* */ + /* // load glyph */ + /* error = FT_Load_Char( face, glyph_index, FT_LOAD_DEFAUT ); */ + /* */ + /* // extract glyph image */ + /* error = FT_Get_Glyph( face->glyph, &glyph ); */ + /* */ + /* // convert to a bitmap (default render mode + destroy old) */ + /* if ( glyph->format != FT_GLYPH_FORMAT_BITMAP ) */ + /* { */ + /* error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_DEFAULT, */ + /* 0, 1 ); */ + /* if ( error ) // glyph unchanged */ + /* ... */ + /* } */ + /* */ + /* // access bitmap content by typecasting */ + /* glyph_bitmap = (FT_BitmapGlyph)glyph; */ + /* */ + /* // do funny stuff with it, like blitting/drawing */ + /* ... */ + /* */ + /* // discard glyph image (bitmap or not) */ + /* FT_Done_Glyph( glyph ); */ + /* } */ + /* */ + /* */ + /* This function will always fail if the glyph's format isn't */ + /* scalable. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_To_Bitmap( FT_Glyph* the_glyph, + FT_Render_Mode render_mode, + FT_Vector* origin, + FT_Bool destroy ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Glyph */ + /* */ + /* <Description> */ + /* Destroys a given glyph. */ + /* */ + /* <Input> */ + /* glyph :: A handle to the target glyph object. */ + /* */ + FT_EXPORT( void ) + FT_Done_Glyph( FT_Glyph glyph ); + + + /* other helpful functions */ + + /*************************************************************************/ + /* */ + /* <Section> */ + /* computations */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Matrix_Multiply */ + /* */ + /* <Description> */ + /* Performs the matrix operation `b = a*b'. */ + /* */ + /* <Input> */ + /* a :: A pointer to matrix `a'. */ + /* */ + /* <InOut> */ + /* b :: A pointer to matrix `b'. */ + /* */ + /* <Note> */ + /* The result is undefined if either `a' or `b' is zero. */ + /* */ + FT_EXPORT( void ) + FT_Matrix_Multiply( const FT_Matrix* a, + FT_Matrix* b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Matrix_Invert */ + /* */ + /* <Description> */ + /* Inverts a 2x2 matrix. Returns an error if it can't be inverted. */ + /* */ + /* <InOut> */ + /* matrix :: A pointer to the target matrix. Remains untouched in */ + /* case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Matrix_Invert( FT_Matrix* matrix ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FTGLYPH_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftgzip.h b/extra_lib/include/freetype/freetype/ftgzip.h new file mode 100644 index 0000000..5d7228b --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftgzip.h @@ -0,0 +1,100 @@ +/***************************************************************************/ +/* */ +/* ftgzip.h */ +/* */ +/* Gzip-compressed stream support. */ +/* */ +/* Copyright 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTGZIP_H__ +#define __FTGZIP_H__ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* <Section> */ + /* gzip */ + /* */ + /* <Title> */ + /* GZIP Streams */ + /* */ + /* <Abstract> */ + /* Using gzip-compressed font files */ + /* */ + /* <Description> */ + /* This section contains the declaration of Gzip-specific functions. */ + /* */ + /*************************************************************************/ + + + /************************************************************************ + * + * @function: + * FT_Stream_OpenGzip + * + * @description: + * Open a new stream to parse gzip-compressed font files. This is + * mainly used to support the compressed *.pcf.gz fonts that come + * with XFree86. + * + * @input: + * stream :: The target embedding stream. + * + * source :: The source stream. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * The source stream must be opened _before_ calling this function. + * + * Calling the internal function FT_Stream_Close on the new stream will + * *not* call FT_Stream_Close on the source stream. None of the stream + * objects will be released to the heap. + * + * The stream implementation is very basic and resets the decompression + * process each time seeking backwards is needed within the stream. + * + * In certain builds of the library, gzip compression recognition is + * automatically handled when calling @FT_New_Face or @FT_Open_Face. + * This means that if no font driver is capable of handling the raw + * compressed file, the library will try to open a gzipped stream from + * it and re-open the face with it. + * + * This function may return "FT_Err_Unimplemented" if your build of + * FreeType was not compiled with zlib support. + */ + FT_EXPORT( FT_Error ) + FT_Stream_OpenGzip( FT_Stream stream, + FT_Stream source ); + + /* */ + + +FT_END_HEADER + +#endif /* __FTGZIP_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftimage.h b/extra_lib/include/freetype/freetype/ftimage.h new file mode 100644 index 0000000..2875965 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftimage.h @@ -0,0 +1,1236 @@ +/***************************************************************************/ +/* */ +/* ftimage.h */ +/* */ +/* FreeType glyph image formats and default raster interface */ +/* (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* Note: A `raster' is simply a scan-line converter, used to render */ + /* FT_Outlines into FT_Bitmaps. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTIMAGE_H__ +#define __FTIMAGE_H__ + + +/* _STANDALONE_ is from ftgrays.c */ +#ifndef _STANDALONE_ +#include <ft2build.h> +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Pos */ + /* */ + /* <Description> */ + /* The type FT_Pos is a 32-bit integer used to store vectorial */ + /* coordinates. Depending on the context, these can represent */ + /* distances in integer font units, or 16,16, or 26.6 fixed float */ + /* pixel coordinates. */ + /* */ + typedef signed long FT_Pos; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Vector */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2D vector; coordinates are of */ + /* the FT_Pos type. */ + /* */ + /* <Fields> */ + /* x :: The horizontal coordinate. */ + /* y :: The vertical coordinate. */ + /* */ + typedef struct FT_Vector_ + { + FT_Pos x; + FT_Pos y; + + } FT_Vector; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_BBox */ + /* */ + /* <Description> */ + /* A structure used to hold an outline's bounding box, i.e., the */ + /* coordinates of its extrema in the horizontal and vertical */ + /* directions. */ + /* */ + /* <Fields> */ + /* xMin :: The horizontal minimum (left-most). */ + /* */ + /* yMin :: The vertical minimum (bottom-most). */ + /* */ + /* xMax :: The horizontal maximum (right-most). */ + /* */ + /* yMax :: The vertical maximum (top-most). */ + /* */ + typedef struct FT_BBox_ + { + FT_Pos xMin, yMin; + FT_Pos xMax, yMax; + + } FT_BBox; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Pixel_Mode */ + /* */ + /* <Description> */ + /* An enumeration type used to describe the format of pixels in a */ + /* given bitmap. Note that additional formats may be added in the */ + /* future. */ + /* */ + /* <Values> */ + /* FT_PIXEL_MODE_NONE :: */ + /* Value 0 is reserved. */ + /* */ + /* FT_PIXEL_MODE_MONO :: */ + /* A monochrome bitmap, using 1 bit per pixel. Note that pixels */ + /* are stored in most-significant order (MSB), which means that */ + /* the left-most pixel in a byte has value 128. */ + /* */ + /* FT_PIXEL_MODE_GRAY :: */ + /* An 8-bit bitmap, generally used to represent anti-aliased glyph */ + /* images. Each pixel is stored in one byte. Note that the number */ + /* of value "gray" levels is stored in the `num_bytes' field of */ + /* the @FT_Bitmap structure (it generally is 256). */ + /* */ + /* FT_PIXEL_MODE_GRAY2 :: */ + /* A 2-bit/pixel bitmap, used to represent embedded anti-aliased */ + /* bitmaps in font files according to the OpenType specification. */ + /* We haven't found a single font using this format, however. */ + /* */ + /* FT_PIXEL_MODE_GRAY4 :: */ + /* A 4-bit/pixel bitmap, used to represent embedded anti-aliased */ + /* bitmaps in font files according to the OpenType specification. */ + /* We haven't found a single font using this format, however. */ + /* */ + /* FT_PIXEL_MODE_LCD :: */ + /* An 8-bit bitmap, used to represent RGB or BGR decimated glyph */ + /* images used for display on LCD displays; the bitmap's width is */ + /* three times wider than the original glyph image. See also */ + /* @FT_RENDER_MODE_LCD. */ + /* */ + /* FT_PIXEL_MODE_LCD_V :: */ + /* An 8-bit bitmap, used to represent RGB or BGR decimated glyph */ + /* images used for display on rotated LCD displays; the bitmap's */ + /* height is three times taller than the original glyph image. */ + /* See also @FT_RENDER_MODE_LCD_V. */ + /* */ + typedef enum FT_Pixel_Mode_ + { + FT_PIXEL_MODE_NONE = 0, + FT_PIXEL_MODE_MONO, + FT_PIXEL_MODE_GRAY, + FT_PIXEL_MODE_GRAY2, + FT_PIXEL_MODE_GRAY4, + FT_PIXEL_MODE_LCD, + FT_PIXEL_MODE_LCD_V, + + FT_PIXEL_MODE_MAX /* do not remove */ + + } FT_Pixel_Mode; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* ft_pixel_mode_xxx */ + /* */ + /* <Description> */ + /* A list of deprecated constants. Use the corresponding */ + /* @FT_Pixel_Mode values instead. */ + /* */ + /* <Values> */ + /* ft_pixel_mode_none :: see @FT_PIXEL_MODE_NONE */ + /* ft_pixel_mode_mono :: see @FT_PIXEL_MODE_MONO */ + /* ft_pixel_mode_grays :: see @FT_PIXEL_MODE_GRAY */ + /* ft_pixel_mode_pal2 :: see @FT_PIXEL_MODE_GRAY2 */ + /* ft_pixel_mode_pal4 :: see @FT_PIXEL_MODE_GRAY4 */ + /* */ +#define ft_pixel_mode_none FT_PIXEL_MODE_NONE +#define ft_pixel_mode_mono FT_PIXEL_MODE_MONO +#define ft_pixel_mode_grays FT_PIXEL_MODE_GRAY +#define ft_pixel_mode_pal2 FT_PIXEL_MODE_GRAY2 +#define ft_pixel_mode_pal4 FT_PIXEL_MODE_GRAY4 + + /* */ + +#if 0 + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Palette_Mode */ + /* */ + /* <Description> */ + /* THIS TYPE IS DEPRECATED. DO NOT USE IT! */ + /* */ + /* An enumeration type used to describe the format of a bitmap */ + /* palette, used with ft_pixel_mode_pal4 and ft_pixel_mode_pal8. */ + /* */ + /* <Fields> */ + /* ft_palette_mode_rgb :: The palette is an array of 3-bytes RGB */ + /* records. */ + /* */ + /* ft_palette_mode_rgba :: The palette is an array of 4-bytes RGBA */ + /* records. */ + /* */ + /* <Note> */ + /* As ft_pixel_mode_pal2, pal4 and pal8 are currently unused by */ + /* FreeType, these types are not handled by the library itself. */ + /* */ + typedef enum FT_Palette_Mode_ + { + ft_palette_mode_rgb = 0, + ft_palette_mode_rgba, + + ft_palettte_mode_max /* do not remove */ + + } FT_Palette_Mode; + + /* */ + +#endif + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Bitmap */ + /* */ + /* <Description> */ + /* A structure used to describe a bitmap or pixmap to the raster. */ + /* Note that we now manage pixmaps of various depths through the */ + /* `pixel_mode' field. */ + /* */ + /* <Fields> */ + /* rows :: The number of bitmap rows. */ + /* */ + /* width :: The number of pixels in bitmap row. */ + /* */ + /* pitch :: The pitch's absolute value is the number of bytes */ + /* taken by one bitmap row, including padding. */ + /* However, the pitch is positive when the bitmap has */ + /* a `down' flow, and negative when it has an `up' */ + /* flow. In all cases, the pitch is an offset to add */ + /* to a bitmap pointer in order to go down one row. */ + /* */ + /* buffer :: A typeless pointer to the bitmap buffer. This */ + /* value should be aligned on 32-bit boundaries in */ + /* most cases. */ + /* */ + /* num_grays :: This field is only used with */ + /* `FT_PIXEL_MODE_GRAY'; it gives the number of gray */ + /* levels used in the bitmap. */ + /* */ + /* pixel_mode :: The pixel mode, i.e., how pixel bits are stored. */ + /* See @FT_Pixel_Mode for possible values. */ + /* */ + /* palette_mode :: This field is only used with paletted pixel modes; */ + /* it indicates how the palette is stored. */ + /* */ + /* palette :: A typeless pointer to the bitmap palette; only */ + /* used for paletted pixel modes. */ + /* */ + /* <Note> */ + /* For now, the only pixel mode supported by FreeType are mono and */ + /* grays. However, drivers might be added in the future to support */ + /* more `colorful' options. */ + /* */ + /* When using pixel modes pal2, pal4 and pal8 with a void `palette' */ + /* field, a gray pixmap with respectively 4, 16, and 256 levels of */ + /* gray is assumed. This, in order to be compatible with some */ + /* embedded bitmap formats defined in the TrueType specification. */ + /* */ + /* Note that no font was found presenting such embedded bitmaps, so */ + /* this is currently completely unhandled by the library. */ + /* */ + typedef struct FT_Bitmap_ + { + int rows; + int width; + int pitch; + unsigned char* buffer; + short num_grays; + char pixel_mode; + char palette_mode; + void* palette; + + } FT_Bitmap; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Outline */ + /* */ + /* <Description> */ + /* This structure is used to describe an outline to the scan-line */ + /* converter. */ + /* */ + /* <Fields> */ + /* n_contours :: The number of contours in the outline. */ + /* */ + /* n_points :: The number of points in the outline. */ + /* */ + /* points :: A pointer to an array of `n_points' FT_Vector */ + /* elements, giving the outline's point coordinates. */ + /* */ + /* tags :: A pointer to an array of `n_points' chars, giving */ + /* each outline point's type. If bit 0 is unset, the */ + /* point is `off' the curve, i.e. a Bezier control */ + /* point, while it is `on' when set. */ + /* */ + /* Bit 1 is meaningful for `off' points only. If set, */ + /* it indicates a third-order Bezier arc control point; */ + /* and a second-order control point if unset. */ + /* */ + /* contours :: An array of `n_contours' shorts, giving the end */ + /* point of each contour within the outline. For */ + /* example, the first contour is defined by the points */ + /* `0' to `contours[0]', the second one is defined by */ + /* the points `contours[0]+1' to `contours[1]', etc. */ + /* */ + /* flags :: A set of bit flags used to characterize the outline */ + /* and give hints to the scan-converter and hinter on */ + /* how to convert/grid-fit it. See FT_Outline_Flags. */ + /* */ + typedef struct FT_Outline_ + { + short n_contours; /* number of contours in glyph */ + short n_points; /* number of points in the glyph */ + + FT_Vector* points; /* the outline's points */ + char* tags; /* the points flags */ + short* contours; /* the contour end points */ + + int flags; /* outline masks */ + + } FT_Outline; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_OUTLINE_FLAGS */ + /* */ + /* <Description> */ + /* A list of bit-field constants use for the flags in an outline's */ + /* `flags' field. */ + /* */ + /* <Values> */ + /* FT_OUTLINE_NONE :: Value 0 is reserved. */ + /* */ + /* FT_OUTLINE_OWNER :: If set, this flag indicates that the */ + /* outline's field arrays (i.e. */ + /* `points', `flags' & `contours') are */ + /* `owned' by the outline object, and */ + /* should thus be freed when it is */ + /* destroyed. */ + /* */ + /* FT_OUTLINE_EVEN_ODD_FILL :: By default, outlines are filled using */ + /* the non-zero winding rule. If set to */ + /* 1, the outline will be filled using */ + /* the even-odd fill rule (only works */ + /* with the smooth raster). */ + /* */ + /* FT_OUTLINE_REVERSE_FILL :: By default, outside contours of an */ + /* outline are oriented in clock-wise */ + /* direction, as defined in the TrueType */ + /* specification. This flag is set if */ + /* the outline uses the opposite */ + /* direction (typically for Type 1 */ + /* fonts). This flag is ignored by the */ + /* scan-converter. However, it is very */ + /* important for the auto-hinter. */ + /* */ + /* FT_OUTLINE_IGNORE_DROPOUTS :: By default, the scan converter will */ + /* try to detect drop-outs in an outline */ + /* and correct the glyph bitmap to */ + /* ensure consistent shape continuity. */ + /* If set, this flag hints the scan-line */ + /* converter to ignore such cases. */ + /* */ + /* FT_OUTLINE_HIGH_PRECISION :: This flag indicates that the */ + /* scan-line converter should try to */ + /* convert this outline to bitmaps with */ + /* the highest possible quality. It is */ + /* typically set for small character */ + /* sizes. Note that this is only a */ + /* hint, that might be completely */ + /* ignored by a given scan-converter. */ + /* */ + /* FT_OUTLINE_SINGLE_PASS :: This flag is set to force a given */ + /* scan-converter to only use a single */ + /* pass over the outline to render a */ + /* bitmap glyph image. Normally, it is */ + /* set for very large character sizes. */ + /* It is only a hint, that might be */ + /* completely ignored by a given */ + /* scan-converter. */ + /* */ +#define FT_OUTLINE_NONE 0x0 +#define FT_OUTLINE_OWNER 0x1 +#define FT_OUTLINE_EVEN_ODD_FILL 0x2 +#define FT_OUTLINE_REVERSE_FILL 0x4 +#define FT_OUTLINE_IGNORE_DROPOUTS 0x8 + +#define FT_OUTLINE_HIGH_PRECISION 0x100 +#define FT_OUTLINE_SINGLE_PASS 0x200 + + + /************************************************************************* + * + * @enum: + * ft_outline_flags + * + * @description: + * These constants are deprecated. Please use the corresponding + * @FT_OUTLINE_FLAGS values. + * + * @values: + * ft_outline_none :: See @FT_OUTLINE_NONE. + * ft_outline_owner :: See @FT_OUTLINE_OWNER. + * ft_outline_even_odd_fill :: See @FT_OUTLINE_EVEN_ODD_FILL. + * ft_outline_reverse_fill :: See @FT_OUTLINE_REVERSE_FILL. + * ft_outline_ignore_dropouts :: See @FT_OUTLINE_IGNORE_DROPOUTS. + * ft_outline_high_precision :: See @FT_OUTLINE_HIGH_PRECISION. + * ft_outline_single_pass :: See @FT_OUTLINE_SINGLE_PASS. + */ +#define ft_outline_none FT_OUTLINE_NONE +#define ft_outline_owner FT_OUTLINE_OWNER +#define ft_outline_even_odd_fill FT_OUTLINE_EVEN_ODD_FILL +#define ft_outline_reverse_fill FT_OUTLINE_REVERSE_FILL +#define ft_outline_ignore_dropouts FT_OUTLINE_IGNORE_DROPOUTS +#define ft_outline_high_precision FT_OUTLINE_HIGH_PRECISION +#define ft_outline_single_pass FT_OUTLINE_SINGLE_PASS + + /* */ + +#define FT_CURVE_TAG( flag ) ( flag & 3 ) + +#define FT_CURVE_TAG_ON 1 +#define FT_CURVE_TAG_CONIC 0 +#define FT_CURVE_TAG_CUBIC 2 + +#define FT_CURVE_TAG_TOUCH_X 8 /* reserved for the TrueType hinter */ +#define FT_CURVE_TAG_TOUCH_Y 16 /* reserved for the TrueType hinter */ + +#define FT_CURVE_TAG_TOUCH_BOTH ( FT_CURVE_TAG_TOUCH_X | \ + FT_CURVE_TAG_TOUCH_Y ) + +#define FT_Curve_Tag_On FT_CURVE_TAG_ON +#define FT_Curve_Tag_Conic FT_CURVE_TAG_CONIC +#define FT_Curve_Tag_Cubic FT_CURVE_TAG_CUBIC +#define FT_Curve_Tag_Touch_X FT_CURVE_TAG_TOUCH_X +#define FT_Curve_Tag_Touch_Y FT_CURVE_TAG_TOUCH_Y + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_MoveToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `move */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `move to' is emitted to start a new contour in an outline. */ + /* */ + /* <Input> */ + /* to :: A pointer to the target point of the `move to'. */ + /* */ + /* user :: A typeless pointer which is passed from the caller of the */ + /* decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0 means success. */ + /* */ + typedef int + (*FT_Outline_MoveToFunc)( FT_Vector* to, + void* user ); + +#define FT_Outline_MoveTo_Func FT_Outline_MoveToFunc + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_LineToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `line */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `line to' is emitted to indicate a segment in the outline. */ + /* */ + /* <Input> */ + /* to :: A pointer to the target point of the `line to'. */ + /* */ + /* user :: A typeless pointer which is passed from the caller of the */ + /* decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0 means success. */ + /* */ + typedef int + (*FT_Outline_LineToFunc)( FT_Vector* to, + void* user ); + +#define FT_Outline_LineTo_Func FT_Outline_LineToFunc + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_ConicToFunc */ + /* */ + /* <Description> */ + /* A function pointer type use to describe the signature of a `conic */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `conic to' is emitted to indicate a second-order Bezier arc in */ + /* the outline. */ + /* */ + /* <Input> */ + /* control :: An intermediate control point between the last position */ + /* and the new target in `to'. */ + /* */ + /* to :: A pointer to the target end point of the conic arc. */ + /* */ + /* user :: A typeless pointer which is passed from the caller of */ + /* the decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0 means success. */ + /* */ + typedef int + (*FT_Outline_ConicToFunc)( FT_Vector* control, + FT_Vector* to, + void* user ); + +#define FT_Outline_ConicTo_Func FT_Outline_ConicToFunc + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_CubicToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `cubic */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `cubic to' is emitted to indicate a third-order Bezier arc. */ + /* */ + /* <Input> */ + /* control1 :: A pointer to the first Bezier control point. */ + /* */ + /* control2 :: A pointer to the second Bezier control point. */ + /* */ + /* to :: A pointer to the target end point. */ + /* */ + /* user :: A typeless pointer which is passed from the caller of */ + /* the decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0 means success. */ + /* */ + typedef int + (*FT_Outline_CubicToFunc)( FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to, + void* user ); + +#define FT_Outline_CubicTo_Func FT_Outline_CubicToFunc + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Outline_Funcs */ + /* */ + /* <Description> */ + /* A structure to hold various function pointers used during outline */ + /* decomposition in order to emit segments, conic, and cubic Beziers, */ + /* as well as `move to' and `close to' operations. */ + /* */ + /* <Fields> */ + /* move_to :: The `move to' emitter. */ + /* */ + /* line_to :: The segment emitter. */ + /* */ + /* conic_to :: The second-order Bezier arc emitter. */ + /* */ + /* cubic_to :: The third-order Bezier arc emitter. */ + /* */ + /* shift :: The shift that is applied to coordinates before they */ + /* are sent to the emitter. */ + /* */ + /* delta :: The delta that is applied to coordinates before they */ + /* are sent to the emitter, but after the shift. */ + /* */ + /* <Note> */ + /* The point coordinates sent to the emitters are the transformed */ + /* version of the original coordinates (this is important for high */ + /* accuracy during scan-conversion). The transformation is simple: */ + /* */ + /* x' = (x << shift) - delta */ + /* y' = (x << shift) - delta */ + /* */ + /* Set the value of `shift' and `delta' to 0 to get the original */ + /* point coordinates. */ + /* */ + typedef struct FT_Outline_Funcs_ + { + FT_Outline_MoveToFunc move_to; + FT_Outline_LineToFunc line_to; + FT_Outline_ConicToFunc conic_to; + FT_Outline_CubicToFunc cubic_to; + + int shift; + FT_Pos delta; + + } FT_Outline_Funcs; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_IMAGE_TAG */ + /* */ + /* <Description> */ + /* This macro converts four letter tags into an unsigned long. */ + /* */ + /* <Note> */ + /* Since many 16bit compilers don't like 32bit enumerations, you */ + /* should redefine this macro in case of problems to something like */ + /* this: */ + /* */ + /* #define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) (value) */ + /* */ + /* to get a simple enumeration without assigning special numbers. */ + /* */ +#ifndef FT_IMAGE_TAG +#define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) \ + value = ( ( (unsigned long)_x1 << 24 ) | \ + ( (unsigned long)_x2 << 16 ) | \ + ( (unsigned long)_x3 << 8 ) | \ + (unsigned long)_x4 ) +#endif /* FT_IMAGE_TAG */ + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Glyph_Format */ + /* */ + /* <Description> */ + /* An enumeration type used to describe the format of a given glyph */ + /* image. Note that this version of FreeType only supports two image */ + /* formats, even though future font drivers will be able to register */ + /* their own format. */ + /* */ + /* <Values> */ + /* FT_GLYPH_FORMAT_NONE :: */ + /* The value 0 is reserved and does describe a glyph format. */ + /* */ + /* FT_GLYPH_FORMAT_COMPOSITE :: */ + /* The glyph image is a composite of several other images. This */ + /* format is _only_ used with @FT_LOAD_NO_RECURSE, and is used to */ + /* report compound glyphs (like accented characters). */ + /* */ + /* FT_GLYPH_FORMAT_BITMAP :: */ + /* The glyph image is a bitmap, and can be described as an */ + /* @FT_Bitmap. You generally need to access the `bitmap' field of */ + /* the @FT_GlyphSlotRec structure to read it. */ + /* */ + /* FT_GLYPH_FORMAT_OUTLINE :: */ + /* The glyph image is a vertorial outline made of line segments */ + /* and Bezier arcs; it can be described as an @FT_Outline; you */ + /* generally want to access the `outline' field of the */ + /* @FT_GlyphSlotRec structure to read it. */ + /* */ + /* FT_GLYPH_FORMAT_PLOTTER :: */ + /* The glyph image is a vectorial path with no inside/outside */ + /* contours. Some Type 1 fonts, like those in the Hershey family, */ + /* contain glyphs in this format. These are described as */ + /* @FT_Outline, but FreeType isn't currently capable of rendering */ + /* them correctly. */ + /* */ + typedef enum FT_Glyph_Format_ + { + FT_IMAGE_TAG( FT_GLYPH_FORMAT_NONE, 0, 0, 0, 0 ), + + FT_IMAGE_TAG( FT_GLYPH_FORMAT_COMPOSITE, 'c', 'o', 'm', 'p' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_BITMAP, 'b', 'i', 't', 's' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_OUTLINE, 'o', 'u', 't', 'l' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_PLOTTER, 'p', 'l', 'o', 't' ) + + } FT_Glyph_Format; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* ft_glyph_format_xxx */ + /* */ + /* <Description> */ + /* A list of decprecated constants. Use the corresponding */ + /* @FT_Glyph_Format values instead. */ + /* */ + /* <Values> */ + /* ft_glyph_format_none :: see @FT_GLYPH_FORMAT_NONE */ + /* ft_glyph_format_composite :: see @FT_GLYPH_FORMAT_COMPOSITE */ + /* ft_glyph_format_bitmap :: see @FT_GLYPH_FORMAT_BITMAP */ + /* ft_glyph_format_outline :: see @FT_GLYPH_FORMAT_OUTLINE */ + /* ft_glyph_format_plotter :: see @FT_GLYPH_FORMAT_PLOTTER */ + /* */ +#define ft_glyph_format_none FT_GLYPH_FORMAT_NONE +#define ft_glyph_format_composite FT_GLYPH_FORMAT_COMPOSITE +#define ft_glyph_format_bitmap FT_GLYPH_FORMAT_BITMAP +#define ft_glyph_format_outline FT_GLYPH_FORMAT_OUTLINE +#define ft_glyph_format_plotter FT_GLYPH_FORMAT_PLOTTER + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** R A S T E R D E F I N I T I O N S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* A raster is a scan converter, in charge of rendering an outline into */ + /* a a bitmap. This section contains the public API for rasters. */ + /* */ + /* Note that in FreeType 2, all rasters are now encapsulated within */ + /* specific modules called `renderers'. See `freetype/ftrender.h' for */ + /* more details on renderers. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* raster */ + /* */ + /* <Title> */ + /* Scanline converter */ + /* */ + /* <Abstract> */ + /* How vectorial outlines are converted into bitmaps and pixmaps. */ + /* */ + /* <Description> */ + /* This section contains technical definitions. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Raster */ + /* */ + /* <Description> */ + /* A handle (pointer) to a raster object. Each object can be used */ + /* independently to convert an outline into a bitmap or pixmap. */ + /* */ + typedef struct FT_RasterRec_* FT_Raster; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Span */ + /* */ + /* <Description> */ + /* A structure used to model a single span of gray (or black) pixels */ + /* when rendering a monochrome or anti-aliased bitmap. */ + /* */ + /* <Fields> */ + /* x :: The span's horizontal start position. */ + /* */ + /* len :: The span's length in pixels. */ + /* */ + /* coverage :: The span color/coverage, ranging from 0 (background) */ + /* to 255 (foreground). Only used for anti-aliased */ + /* rendering. */ + /* */ + /* <Note> */ + /* This structure is used by the span drawing callback type named */ + /* FT_SpanFunc which takes the y-coordinate of the span as a */ + /* a parameter. */ + /* */ + /* The coverage value is always between 0 and 255, even if the number */ + /* of gray levels have been set through FT_Set_Gray_Levels(). */ + /* */ + typedef struct FT_Span_ + { + short x; + unsigned short len; + unsigned char coverage; + + } FT_Span; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_SpanFunc */ + /* */ + /* <Description> */ + /* A function used as a call-back by the anti-aliased renderer in */ + /* order to let client applications draw themselves the gray pixel */ + /* spans on each scan line. */ + /* */ + /* <Input> */ + /* y :: The scanline's y-coordinate. */ + /* */ + /* count :: The number of spans to draw on this scanline. */ + /* */ + /* spans :: A table of `count' spans to draw on the scanline. */ + /* */ + /* user :: User-supplied data that is passed to the callback. */ + /* */ + /* <Note> */ + /* This callback allows client applications to directly render the */ + /* gray spans of the anti-aliased bitmap to any kind of surfaces. */ + /* */ + /* This can be used to write anti-aliased outlines directly to a */ + /* given background bitmap, and even perform translucency. */ + /* */ + /* Note that the `count' field cannot be greater than a fixed value */ + /* defined by the FT_MAX_GRAY_SPANS configuration macro in */ + /* ftoption.h. By default, this value is set to 32, which means that */ + /* if there are more than 32 spans on a given scanline, the callback */ + /* will be called several times with the same `y' parameter in order */ + /* to draw all callbacks. */ + /* */ + /* Otherwise, the callback is only called once per scan-line, and */ + /* only for those scanlines that do have `gray' pixels on them. */ + /* */ + typedef void + (*FT_SpanFunc)( int y, + int count, + FT_Span* spans, + void* user ); + +#define FT_Raster_Span_Func FT_SpanFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_BitTest_Func */ + /* */ + /* <Description> */ + /* THIS TYPE IS DEPRECATED. DO NOT USE IT. */ + /* */ + /* A function used as a call-back by the monochrome scan-converter */ + /* to test whether a given target pixel is already set to the drawing */ + /* `color'. These tests are crucial to implement drop-out control */ + /* per-se the TrueType spec. */ + /* */ + /* <Input> */ + /* y :: The pixel's y-coordinate. */ + /* */ + /* x :: The pixel's x-coordinate. */ + /* */ + /* user :: User-supplied data that is passed to the callback. */ + /* */ + /* <Return> */ + /* 1 if the pixel is `set', 0 otherwise. */ + /* */ + typedef int + (*FT_Raster_BitTest_Func)( int y, + int x, + void* user ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_BitSet_Func */ + /* */ + /* <Description> */ + /* THIS TYPE IS DEPRECATED. DO NOT USE IT. */ + /* */ + /* A function used as a call-back by the monochrome scan-converter */ + /* to set an individual target pixel. This is crucial to implement */ + /* drop-out control according to the TrueType specification. */ + /* */ + /* <Input> */ + /* y :: The pixel's y-coordinate. */ + /* */ + /* x :: The pixel's x-coordinate. */ + /* */ + /* user :: User-supplied data that is passed to the callback. */ + /* */ + /* <Return> */ + /* 1 if the pixel is `set', 0 otherwise. */ + /* */ + typedef void + (*FT_Raster_BitSet_Func)( int y, + int x, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_RASTER_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit flag constants as used in the `flags' field of a */ + /* @FT_Raster_Params structure. */ + /* */ + /* <Values> */ + /* FT_RASTER_FLAG_DEFAULT :: This value is 0. */ + /* */ + /* FT_RASTER_FLAG_AA :: This flag is set to indicate that an */ + /* anti-aliased glyph image should be */ + /* generated. Otherwise, it will be */ + /* monochrome (1-bit). */ + /* */ + /* FT_RASTER_FLAG_DIRECT :: This flag is set to indicate direct */ + /* rendering. In this mode, client */ + /* applications must provide their own span */ + /* callback. This lets them directly */ + /* draw or compose over an existing bitmap. */ + /* If this bit is not set, the target */ + /* pixmap's buffer _must_ be zeroed before */ + /* rendering. */ + /* */ + /* Note that for now, direct rendering is */ + /* only possible with anti-aliased glyphs. */ + /* */ + /* FT_RASTER_FLAG_CLIP :: This flag is only used in direct */ + /* rendering mode. If set, the output will */ + /* be clipped to a box specified in the */ + /* "clip_box" field of the FT_Raster_Params */ + /* structure. */ + /* */ + /* Note that by default, the glyph bitmap */ + /* is clipped to the target pixmap, except */ + /* in direct rendering mode where all spans */ + /* are generated if no clipping box is set. */ + /* */ +#define FT_RASTER_FLAG_DEFAULT 0x0 +#define FT_RASTER_FLAG_AA 0x1 +#define FT_RASTER_FLAG_DIRECT 0x2 +#define FT_RASTER_FLAG_CLIP 0x4 + + /* deprecated */ +#define ft_raster_flag_default FT_RASTER_FLAG_DEFAULT +#define ft_raster_flag_aa FT_RASTER_FLAG_AA +#define ft_raster_flag_direct FT_RASTER_FLAG_DIRECT +#define ft_raster_flag_clip FT_RASTER_FLAG_CLIP + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Raster_Params */ + /* */ + /* <Description> */ + /* A structure to hold the arguments used by a raster's render */ + /* function. */ + /* */ + /* <Fields> */ + /* target :: The target bitmap. */ + /* */ + /* source :: A pointer to the source glyph image (e.g. an */ + /* FT_Outline). */ + /* */ + /* flags :: The rendering flags. */ + /* */ + /* gray_spans :: The gray span drawing callback. */ + /* */ + /* black_spans :: The black span drawing callback. */ + /* */ + /* bit_test :: The bit test callback. UNIMPLEMENTED! */ + /* */ + /* bit_set :: The bit set callback. UNIMPLEMENTED! */ + /* */ + /* user :: User-supplied data that is passed to each drawing */ + /* callback. */ + /* */ + /* clip_box :: An optional clipping box. It is only used in */ + /* direct rendering mode. Note that coordinates here */ + /* should be expressed in _integer_ pixels (and not in */ + /* 26.6 fixed-point units). */ + /* */ + /* <Note> */ + /* An anti-aliased glyph bitmap is drawn if the FT_RASTER_FLAG_AA bit */ + /* flag is set in the `flags' field, otherwise a monochrome bitmap */ + /* will be generated. */ + /* */ + /* If the FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the */ + /* raster will call the `gray_spans' callback to draw gray pixel */ + /* spans, in the case of an aa glyph bitmap, it will call */ + /* `black_spans', and `bit_test' and `bit_set' in the case of a */ + /* monochrome bitmap. This allows direct composition over a */ + /* pre-existing bitmap through user-provided callbacks to perform the */ + /* span drawing/composition. */ + /* */ + /* Note that the `bit_test' and `bit_set' callbacks are required when */ + /* rendering a monochrome bitmap, as they are crucial to implement */ + /* correct drop-out control as defined in the TrueType specification. */ + /* */ + typedef struct FT_Raster_Params_ + { + FT_Bitmap* target; + void* source; + int flags; + FT_SpanFunc gray_spans; + FT_SpanFunc black_spans; + FT_Raster_BitTest_Func bit_test; /* doesn't work! */ + FT_Raster_BitSet_Func bit_set; /* doesn't work! */ + void* user; + FT_BBox clip_box; + + } FT_Raster_Params; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_NewFunc */ + /* */ + /* <Description> */ + /* A function used to create a new raster object. */ + /* */ + /* <Input> */ + /* memory :: A handle to the memory allocator. */ + /* */ + /* <Output> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* <Return> */ + /* Error code. 0 means success. */ + /* */ + /* <Note> */ + /* The `memory' parameter is a typeless pointer in order to avoid */ + /* un-wanted dependencies on the rest of the FreeType code. In */ + /* practice, it is a FT_Memory, i.e., a handle to the standard */ + /* FreeType memory allocator. However, this field can be completely */ + /* ignored by a given raster implementation. */ + /* */ + typedef int + (*FT_Raster_NewFunc)( void* memory, + FT_Raster* raster ); + +#define FT_Raster_New_Func FT_Raster_NewFunc + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_DoneFunc */ + /* */ + /* <Description> */ + /* A function used to destroy a given raster object. */ + /* */ + /* <Input> */ + /* raster :: A handle to the raster object. */ + /* */ + typedef void + (*FT_Raster_DoneFunc)( FT_Raster raster ); + +#define FT_Raster_Done_Func FT_Raster_DoneFunc + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_ResetFunc */ + /* */ + /* <Description> */ + /* FreeType provides an area of memory called the `render pool', */ + /* available to all registered rasters. This pool can be freely used */ + /* during a given scan-conversion but is shared by all rasters. Its */ + /* content is thus transient. */ + /* */ + /* This function is called each time the render pool changes, or just */ + /* after a new raster object is created. */ + /* */ + /* <Input> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* pool_base :: The address in memory of the render pool. */ + /* */ + /* pool_size :: The size in bytes of the render pool. */ + /* */ + /* <Note> */ + /* Rasters can ignore the render pool and rely on dynamic memory */ + /* allocation if they want to (a handle to the memory allocator is */ + /* passed to the raster constructor). However, this is not */ + /* recommended for efficiency purposes. */ + /* */ + typedef void + (*FT_Raster_ResetFunc)( FT_Raster raster, + unsigned char* pool_base, + unsigned long pool_size ); + +#define FT_Raster_Reset_Func FT_Raster_ResetFunc + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_SetModeFunc */ + /* */ + /* <Description> */ + /* This function is a generic facility to change modes or attributes */ + /* in a given raster. This can be used for debugging purposes, or */ + /* simply to allow implementation-specific `features' in a given */ + /* raster module. */ + /* */ + /* <Input> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* mode :: A 4-byte tag used to name the mode or property. */ + /* */ + /* args :: A pointer to the new mode/property to use. */ + /* */ + typedef int + (*FT_Raster_SetModeFunc)( FT_Raster raster, + unsigned long mode, + void* args ); + +#define FT_Raster_Set_Mode_Func FT_Raster_SetModeFunc + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_RenderFunc */ + /* */ + /* <Description> */ + /* Invokes a given raster to scan-convert a given glyph image into a */ + /* target bitmap. */ + /* */ + /* <Input> */ + /* raster :: A handle to the raster object. */ + /* */ + /* params :: A pointer to a FT_Raster_Params structure used to store */ + /* the rendering parameters. */ + /* */ + /* <Return> */ + /* Error code. 0 means success. */ + /* */ + /* <Note> */ + /* The exact format of the source image depends on the raster's glyph */ + /* format defined in its FT_Raster_Funcs structure. It can be an */ + /* FT_Outline or anything else in order to support a large array of */ + /* glyph formats. */ + /* */ + /* Note also that the render function can fail and return a */ + /* FT_Err_Unimplemented_Feature error code if the raster used does */ + /* not support direct composition. */ + /* */ + /* XXX: For now, the standard raster doesn't support direct */ + /* composition but this should change for the final release (see */ + /* the files demos/src/ftgrays.c and demos/src/ftgrays2.c for */ + /* examples of distinct implementations which support direct */ + /* composition). */ + /* */ + typedef int + (*FT_Raster_RenderFunc)( FT_Raster raster, + FT_Raster_Params* params ); + +#define FT_Raster_Render_Func FT_Raster_RenderFunc + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Raster_Funcs */ + /* */ + /* <Description> */ + /* A structure used to describe a given raster class to the library. */ + /* */ + /* <Fields> */ + /* glyph_format :: The supported glyph format for this raster. */ + /* */ + /* raster_new :: The raster constructor. */ + /* */ + /* raster_reset :: Used to reset the render pool within the raster. */ + /* */ + /* raster_render :: A function to render a glyph into a given bitmap. */ + /* */ + /* raster_done :: The raster destructor. */ + /* */ + typedef struct FT_Raster_Funcs_ + { + FT_Glyph_Format glyph_format; + FT_Raster_NewFunc raster_new; + FT_Raster_ResetFunc raster_reset; + FT_Raster_SetModeFunc raster_set_mode; + FT_Raster_RenderFunc raster_render; + FT_Raster_DoneFunc raster_done; + + } FT_Raster_Funcs; + + + /* */ + + +FT_END_HEADER + +#endif /* __FTIMAGE_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftincrem.h b/extra_lib/include/freetype/freetype/ftincrem.h new file mode 100644 index 0000000..6a1aa1f --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftincrem.h @@ -0,0 +1,292 @@ +/***************************************************************************/ +/* */ +/* ftincrem.h */ +/* */ +/* FreeType incremental loading (specification). */ +/* */ +/* Copyright 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTINCREM_H__ +#define __FTINCREM_H__ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************** + * + * @type: + * FT_Incremental + * + * @description: + * An opaque type describing a user-provided object used to implement + * "incremental" glyph loading within FreeType. This is used to support + * embedded fonts in certain environments (e.g. Postscript interpreters), + * where the glyph data isn't in the font file, or must be overridden by + * different values. + * + * @note: + * It is up to client applications to create and implement @FT_Incremental + * objects, as long as they provide implementations for the methods + * @FT_Incremental_GetGlyphDataFunc, @FT_Incremental_FreeGlyphDataFunc + * and @FT_Incremental_GetGlyphMetricsFunc. + * + * See the description of @FT_Incremental_InterfaceRec to understand how + * to use incremental objects with FreeType. + */ + typedef struct FT_IncrementalRec_* FT_Incremental; + + + /*************************************************************************** + * + * @struct: + * FT_Incremental_Metrics + * + * @description: + * A small structure used to contain the basic glyph metrics returned + * by the @FT_Incremental_GetGlyphMetricsFunc method. + * + * @fields: + * bearing_x :: + * Left bearing, in font units. + * + * bearing_y :: + * Top bearing, in font units. + * + * advance :: + * Glyph advance, in font units. + * + * @note: + * These correspond to horizontal or vertical metrics depending on the + * value of the 'vertical' argument to the function + * @FT_Incremental_GetGlyphMetricsFunc. + */ + typedef struct FT_Incremental_MetricsRec_ + { + FT_Long bearing_x; + FT_Long bearing_y; + FT_Long advance; + + } FT_Incremental_MetricsRec, *FT_Incremental_Metrics; + + + /*************************************************************************** + * + * @type: + * FT_Incremental_GetGlyphDataFunc + * + * @description: + * A function called by FreeType to access a given glyph's data bytes + * during @FT_Load_Glyph or @FT_Load_Char if incremental loading is + * enabled. + * + * Note that the format of the glyph's data bytes depends on the font + * file format. For TrueType, it must correspond to the raw bytes within + * the 'glyf' table. For Postscript formats, it must correspond to the + * *unencrypted* charstring bytes, without any 'lenIV' header. It is + * undefined for any other format. + * + * @input: + * incremental :: + * Handle to an opaque @FT_Incremental handle provided by the client + * application. + * + * glyph_index :: + * Index of relevant glyph. + * + * @output: + * adata :: + * A structure describing the returned glyph data bytes (which will be + * accessed as a read-only byte block). + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * If this function returns succesfully the method + * @FT_Incremental_FreeGlyphDataFunc will be called later to release + * the data bytes. + * + * Nested calls to @FT_Incremental_GetGlyphDataFunc can happen for + * compound glyphs. + */ + typedef FT_Error + (*FT_Incremental_GetGlyphDataFunc)( FT_Incremental incremental, + FT_UInt glyph_index, + FT_Data* adata ); + + + /*************************************************************************** + * + * @type: + * FT_Incremental_FreeGlyphDataFunc + * + * @description: + * A function used to release the glyph data bytes returned by a + * successful call to @FT_Incremental_GetGlyphDataFunc. + * + * @input: + * incremental :: + * A handle to an opaque @FT_Incremental handle provided by the client + * application. + * + * data :: + * A structure describing the glyph data bytes (which will be accessed + * as a read-only byte block). + */ + typedef void + (*FT_Incremental_FreeGlyphDataFunc)( FT_Incremental incremental, + FT_Data* data ); + + + /*************************************************************************** + * + * @type: + * FT_Incremental_GetGlyphMetricsFunc + * + * @description: + * A function used to retrieve the basic metrics of a given glyph index + * before accessing its data. This is necessary because, in certain + * formats like TrueType, the metrics are stored in a different place from + * the glyph images proper. + * + * @input: + * incremental :: + * A handle to an opaque @FT_Incremental handle provided by the client + * application. + * + * glyph_index :: + * Index of relevant glyph. + * + * vertical :: + * If true, return vertical metrics. + * + * ametrics :: + * This parameter is used for both input and output. + * The original glyph metrics, if any, in font units. If metrics are + * not available all the values must be set to zero. + * + * @output: + * ametrics :: + * The replacement glyph metrics in font units. + * + */ + typedef FT_Error + (*FT_Incremental_GetGlyphMetricsFunc) + ( FT_Incremental incremental, + FT_UInt glyph_index, + FT_Bool vertical, + FT_Incremental_MetricsRec *ametrics ); + + + /************************************************************************** + * + * @struct: + * FT_Incremental_FuncsRec + * + * @description: + * A table of functions for accessing fonts that load data + * incrementally. Used in @FT_Incremental_InterfaceRec. + * + * @fields: + * get_glyph_data :: + * The function to get glyph data. Must not be null. + * + * free_glyph_data :: + * The function to release glyph data. Must not be null. + * + * get_glyph_metrics :: + * The function to get glyph metrics. May be null if the font does + * not provide overriding glyph metrics. + */ + typedef struct FT_Incremental_FuncsRec_ + { + FT_Incremental_GetGlyphDataFunc get_glyph_data; + FT_Incremental_FreeGlyphDataFunc free_glyph_data; + FT_Incremental_GetGlyphMetricsFunc get_glyph_metrics; + + } FT_Incremental_FuncsRec; + + + /*************************************************************************** + * + * @struct: + * FT_Incremental_InterfaceRec + * + * @description: + * A structure to be used with @FT_Open_Face to indicate that the user + * wants to support incremental glyph loading. You should use it with + * @FT_PARAM_TAG_INCREMENTAL as in the following example: + * + * { + * FT_Incremental_InterfaceRec inc_int; + * FT_Parameter parameter; + * FT_Open_Args open_args; + * + * + * // set up incremental descriptor + * inc_int.funcs = my_funcs; + * inc_int.object = my_object; + * + * // set up optional parameter + * parameter.tag = FT_PARAM_TAG_INCREMENTAL; + * parameter.data = &inc_int; + * + * // set up FT_Open_Args structure + * open_args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS; + * open_args.pathname = my_font_pathname; + * open_args.num_params = 1; + * open_args.params = ¶meter; // we use one optional argument + * + * // open the font + * error = FT_Open_Face( library, &open_args, index, &face ); + * ... + * } + */ + typedef struct FT_Incremental_InterfaceRec_ + { + const FT_Incremental_FuncsRec* funcs; + FT_Incremental object; + + } FT_Incremental_InterfaceRec; + + + /*************************************************************************** + * + * @constant: + * FT_PARAM_TAG_INCREMENTAL + * + * @description: + * A constant used as the tag of @FT_Parameter structures to indicate + * an incremental loading object to be used by FreeType. + * + */ +#define FT_PARAM_TAG_INCREMENTAL FT_MAKE_TAG( 'i', 'n', 'c', 'r' ) + + /* */ + +FT_END_HEADER + +#endif /* __FTINCREM_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftlist.h b/extra_lib/include/freetype/freetype/ftlist.h new file mode 100644 index 0000000..ae9801b --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftlist.h @@ -0,0 +1,274 @@ +/***************************************************************************/ +/* */ +/* ftlist.h */ +/* */ +/* Generic list support for FreeType (specification). */ +/* */ +/* Copyright 1996-2001, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file implements functions relative to list processing. Its */ + /* data structures are defined in `freetype.h'. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTLIST_H__ +#define __FTLIST_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* list_processing */ + /* */ + /* <Title> */ + /* List Processing */ + /* */ + /* <Abstract> */ + /* Simple management of lists. */ + /* */ + /* <Description> */ + /* This section contains various definitions related to list */ + /* processing using doubly-linked nodes. */ + /* */ + /* <Order> */ + /* FT_List */ + /* FT_ListNode */ + /* FT_ListRec */ + /* FT_ListNodeRec */ + /* */ + /* FT_List_Add */ + /* FT_List_Insert */ + /* FT_List_Find */ + /* FT_List_Remove */ + /* FT_List_Up */ + /* FT_List_Iterate */ + /* FT_List_Iterator */ + /* FT_List_Finalize */ + /* FT_List_Destructor */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Find */ + /* */ + /* <Description> */ + /* Finds the list node for a given listed object. */ + /* */ + /* <Input> */ + /* list :: A pointer to the parent list. */ + /* data :: The address of the listed object. */ + /* */ + /* <Return> */ + /* List node. NULL if it wasn't found. */ + /* */ + FT_EXPORT( FT_ListNode ) + FT_List_Find( FT_List list, + void* data ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Add */ + /* */ + /* <Description> */ + /* Appends an element to the end of a list. */ + /* */ + /* <InOut> */ + /* list :: A pointer to the parent list. */ + /* node :: The node to append. */ + /* */ + FT_EXPORT( void ) + FT_List_Add( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Insert */ + /* */ + /* <Description> */ + /* Inserts an element at the head of a list. */ + /* */ + /* <InOut> */ + /* list :: A pointer to parent list. */ + /* node :: The node to insert. */ + /* */ + FT_EXPORT( void ) + FT_List_Insert( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Remove */ + /* */ + /* <Description> */ + /* Removes a node from a list. This function doesn't check whether */ + /* the node is in the list! */ + /* */ + /* <Input> */ + /* node :: The node to remove. */ + /* */ + /* <InOut> */ + /* list :: A pointer to the parent list. */ + /* */ + FT_EXPORT( void ) + FT_List_Remove( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Up */ + /* */ + /* <Description> */ + /* Moves a node to the head/top of a list. Used to maintain LRU */ + /* lists. */ + /* */ + /* <InOut> */ + /* list :: A pointer to the parent list. */ + /* node :: The node to move. */ + /* */ + FT_EXPORT( void ) + FT_List_Up( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_List_Iterator */ + /* */ + /* <Description> */ + /* An FT_List iterator function which is called during a list parse */ + /* by FT_List_Iterate(). */ + /* */ + /* <Input> */ + /* node :: The current iteration list node. */ + /* */ + /* user :: A typeless pointer passed to FT_List_Iterate(). */ + /* Can be used to point to the iteration's state. */ + /* */ + typedef FT_Error + (*FT_List_Iterator)( FT_ListNode node, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Iterate */ + /* */ + /* <Description> */ + /* Parses a list and calls a given iterator function on each element. */ + /* Note that parsing is stopped as soon as one of the iterator calls */ + /* returns a non-zero value. */ + /* */ + /* <Input> */ + /* list :: A handle to the list. */ + /* iterator :: An interator function, called on each node of the */ + /* list. */ + /* user :: A user-supplied field which is passed as the second */ + /* argument to the iterator. */ + /* */ + /* <Return> */ + /* The result (a FreeType error code) of the last iterator call. */ + /* */ + FT_EXPORT( FT_Error ) + FT_List_Iterate( FT_List list, + FT_List_Iterator iterator, + void* user ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_List_Destructor */ + /* */ + /* <Description> */ + /* An FT_List iterator function which is called during a list */ + /* finalization by FT_List_Finalize() to destroy all elements in a */ + /* given list. */ + /* */ + /* <Input> */ + /* system :: The current system object. */ + /* */ + /* data :: The current object to destroy. */ + /* */ + /* user :: A typeless pointer passed to FT_List_Iterate(). It can */ + /* be used to point to the iteration's state. */ + /* */ + typedef void + (*FT_List_Destructor)( FT_Memory memory, + void* data, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Finalize */ + /* */ + /* <Description> */ + /* Destroys all elements in the list as well as the list itself. */ + /* */ + /* <Input> */ + /* list :: A handle to the list. */ + /* */ + /* destroy :: A list destructor that will be applied to each element */ + /* of the list. */ + /* */ + /* memory :: The current memory object which handles deallocation. */ + /* */ + /* user :: A user-supplied field which is passed as the last */ + /* argument to the destructor. */ + /* */ + FT_EXPORT( void ) + FT_List_Finalize( FT_List list, + FT_List_Destructor destroy, + FT_Memory memory, + void* user ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FTLIST_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftmac.h b/extra_lib/include/freetype/freetype/ftmac.h new file mode 100644 index 0000000..b9f89ba --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftmac.h @@ -0,0 +1,128 @@ +/***************************************************************************/ +/* */ +/* ftmac.h */ +/* */ +/* Additional Mac-specific API. */ +/* */ +/* Copyright 1996-2001 by */ +/* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* NOTE: Include this file after <freetype/freetype.h> and after the */ +/* Mac-specific <Types.h> header (or any other Mac header that */ +/* includes <Types.h>); we use Handle type. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTMAC_H__ +#define __FTMAC_H__ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* mac_specific */ + /* */ + /* <Title> */ + /* Mac-Specific Interface */ + /* */ + /* <Abstract> */ + /* Only available on the Macintosh. */ + /* */ + /* <Description> */ + /* The following definitions are only available if FreeType is */ + /* compiled on a Macintosh. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face_From_FOND */ + /* */ + /* <Description> */ + /* Creates a new face object from an FOND resource. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* fond :: An FOND resource. */ + /* */ + /* face_index :: Only supported for the -1 `sanity check' special */ + /* case. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Notes> */ + /* This function can be used to create FT_Face abjects from fonts */ + /* that are installed in the system like so: */ + /* */ + /* { */ + /* fond = GetResource( 'FOND', fontName ); */ + /* error = FT_New_Face_From_FOND( library, fond, 0, &face ); */ + /* } */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face_From_FOND( FT_Library library, + Handle fond, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_GetFile_From_Mac_Name */ + /* */ + /* <Description> */ + /* Returns an FSSpec for the disk file containing the named font. */ + /* */ + /* <Input> */ + /* fontName :: Mac OS name of the font (eg. Times New Roman Bold). */ + /* */ + /* <Output> */ + /* pathSpec :: FSSpec to the file. For passing to @FT_New_Face. */ + /* */ + /* face_index :: Index of the face. For passing to @FT_New_Face. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT_DEF( FT_Error ) + FT_GetFile_From_Mac_Name( char* fontName, + FSSpec* pathSpec, + FT_Long* face_index ); + + /* */ + + +FT_END_HEADER + + +#endif /* __FTMAC_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftmm.h b/extra_lib/include/freetype/freetype/ftmm.h new file mode 100644 index 0000000..7f565be --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftmm.h @@ -0,0 +1,188 @@ +/***************************************************************************/ +/* */ +/* ftmm.h */ +/* */ +/* FreeType Multiple Master font interface (specification). */ +/* */ +/* Copyright 1996-2001 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTMM_H__ +#define __FTMM_H__ + + +#include <ft2build.h> +#include FT_TYPE1_TABLES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* multiple_masters */ + /* */ + /* <Title> */ + /* Multiple Masters */ + /* */ + /* <Abstract> */ + /* How to manage Multiple Masters fonts. */ + /* */ + /* <Description> */ + /* The following types and functions are used to manage Multiple */ + /* Master fonts, i.e. the selection of specific design instances by */ + /* setting design axis coordinates. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_MM_Axis */ + /* */ + /* <Description> */ + /* A simple structure used to model a given axis in design space for */ + /* Multiple Masters fonts. */ + /* */ + /* <Fields> */ + /* name :: The axis's name. */ + /* */ + /* minimum :: The axis's minimum design coordinate. */ + /* */ + /* maximum :: The axis's maximum design coordinate. */ + /* */ + typedef struct FT_MM_Axis_ + { + FT_String* name; + FT_Long minimum; + FT_Long maximum; + + } FT_MM_Axis; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Multi_Master */ + /* */ + /* <Description> */ + /* A structure used to model the axes and space of a Multiple Masters */ + /* font. */ + /* */ + /* <Fields> */ + /* num_axis :: Number of axes. Cannot exceed 4. */ + /* */ + /* num_designs :: Number of designs; should ne normally 2^num_axis */ + /* even though the Type 1 specification strangely */ + /* allows for intermediate designs to be present. This */ + /* number cannot exceed 16. */ + /* */ + /* axis :: A table of axis descriptors. */ + /* */ + typedef struct FT_Multi_Master_ + { + FT_UInt num_axis; + FT_UInt num_designs; + FT_MM_Axis axis[T1_MAX_MM_AXIS]; + + } FT_Multi_Master; + + /* */ + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Multi_Master */ + /* */ + /* <Description> */ + /* Retrieves the Multiple Master descriptor of a given font. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* <Output> */ + /* amaster :: The Multiple Masters descriptor. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Multi_Master( FT_Face face, + FT_Multi_Master *amaster ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_MM_Design_Coordinates */ + /* */ + /* <Description> */ + /* For Multiple Masters fonts, choose an interpolated font design */ + /* through design coordinates. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face. */ + /* */ + /* <Input> */ + /* num_coords :: The number of design coordinates (must be equal to */ + /* the number of axes in the font). */ + /* */ + /* coords :: An array of design coordinates. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_MM_Design_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Long* coords ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_MM_Blend_Coordinates */ + /* */ + /* <Description> */ + /* For Multiple Masters fonts, choose an interpolated font design */ + /* through normalized blend coordinates. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face. */ + /* */ + /* <Input> */ + /* num_coords :: The number of design coordinates (must be equal to */ + /* the number of axes in the font). */ + /* */ + /* coords :: The design coordinates array (each element must be */ + /* between 0 and 1.0). */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_MM_Blend_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FTMM_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftmoderr.h b/extra_lib/include/freetype/freetype/ftmoderr.h new file mode 100644 index 0000000..c9b8121 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftmoderr.h @@ -0,0 +1,153 @@ +/***************************************************************************/ +/* */ +/* ftmoderr.h */ +/* */ +/* FreeType module error offsets (specification). */ +/* */ +/* Copyright 2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the FreeType module error offsets. */ + /* */ + /* The lower byte gives the error code, the higher byte gives the */ + /* module. The base module has error offset 0. For example, the error */ + /* `FT_Err_Invalid_File_Format' has value 0x003, the error */ + /* `TT_Err_Invalid_File_Format' has value 0xF03, the error */ + /* `T1_Err_Invalid_File_Format' has value 0x1003, etc. */ + /* */ + /* Undefine the macro FT_CONFIG_OPTION_USE_MODULE_ERRORS in ftoption.h */ + /* to make the higher byte always zero (disabling the module error */ + /* mechanism). */ + /* */ + /* It can also be used to create a module error message table easily */ + /* with something like */ + /* */ + /* { */ + /* #undef __FTMODERR_H__ */ + /* #define FT_MODERRDEF( e, v, s ) { FT_Mod_Err_ ## e, s }, */ + /* #define FT_MODERR_START_LIST { */ + /* #define FT_MODERR_END_LIST { 0, 0 } }; */ + /* */ + /* const struct */ + /* { */ + /* int mod_err_offset; */ + /* const char* mod_err_msg */ + /* } ft_mod_errors[] = */ + /* */ + /* #include FT_MODULE_ERRORS_H */ + /* } */ + /* */ + /* To use such a table, all errors must be ANDed with 0xFF00 to remove */ + /* the error code. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTMODERR_H__ +#define __FTMODERR_H__ + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SETUP MACROS *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#undef FT_NEED_EXTERN_C + +#ifndef FT_MODERRDEF + +#ifdef FT_CONFIG_OPTION_USE_MODULE_ERRORS +#define FT_MODERRDEF( e, v, s ) FT_Mod_Err_ ## e = v, +#else +#define FT_MODERRDEF( e, v, s ) FT_Mod_Err_ ## e = 0, +#endif + +#define FT_MODERR_START_LIST enum { +#define FT_MODERR_END_LIST FT_Mod_Err_Max }; + +#ifdef __cplusplus +#define FT_NEED_EXTERN_C + extern "C" { +#endif + +#endif /* !FT_MODERRDEF */ + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** LIST MODULE ERROR BASES *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#ifdef FT_MODERR_START_LIST + FT_MODERR_START_LIST +#endif + + + FT_MODERRDEF( Base, 0x000, "base module" ) + FT_MODERRDEF( Autohint, 0x100, "autohinter module" ) + FT_MODERRDEF( BDF, 0x200, "BDF module" ) + FT_MODERRDEF( Cache, 0x300, "cache module" ) + FT_MODERRDEF( CFF, 0x400, "CFF module" ) + FT_MODERRDEF( CID, 0x500, "CID module" ) + FT_MODERRDEF( Gzip, 0x600, "Gzip module" ) + FT_MODERRDEF( PCF, 0x700, "PCF module" ) + FT_MODERRDEF( PFR, 0x800, "PFR module" ) + FT_MODERRDEF( PSaux, 0x900, "PS auxiliary module" ) + FT_MODERRDEF( PShinter, 0xA00, "PS hinter module" ) + FT_MODERRDEF( PSnames, 0xB00, "PS names module" ) + FT_MODERRDEF( Raster, 0xC00, "raster module" ) + FT_MODERRDEF( SFNT, 0xD00, "SFNT module" ) + FT_MODERRDEF( Smooth, 0xE00, "smooth raster module" ) + FT_MODERRDEF( TrueType, 0xF00, "TrueType module" ) + FT_MODERRDEF( Type1, 0x1000, "Type 1 module" ) + FT_MODERRDEF( Type42, 0x1100, "Type 42 module" ) + FT_MODERRDEF( Winfonts, 0x1200, "Windows FON/FNT module" ) + + +#ifdef FT_MODERR_END_LIST + FT_MODERR_END_LIST +#endif + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** CLEANUP *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#ifdef FT_NEED_EXTERN_C + } +#endif + +#undef FT_MODERR_START_LIST +#undef FT_MODERR_END_LIST +#undef FT_MODERRDEF +#undef FT_NEED_EXTERN_C + + +#endif /* __FTMODERR_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftmodule.h b/extra_lib/include/freetype/freetype/ftmodule.h new file mode 100644 index 0000000..caf081d --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftmodule.h @@ -0,0 +1,314 @@ +/***************************************************************************/ +/* */ +/* ftmodule.h */ +/* */ +/* FreeType modules public interface (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTMODULE_H__ +#define __FTMODULE_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* module_management */ + /* */ + /* <Title> */ + /* Module Management */ + /* */ + /* <Abstract> */ + /* How to add, upgrade, and remove modules from FreeType. */ + /* */ + /* <Description> */ + /* The definitions below are used to manage modules within FreeType. */ + /* Modules can be added, upgraded, and removed at runtime. */ + /* */ + /*************************************************************************/ + + + /* module bit flags */ +#define FT_MODULE_FONT_DRIVER 1 /* this module is a font driver */ +#define FT_MODULE_RENDERER 2 /* this module is a renderer */ +#define FT_MODULE_HINTER 4 /* this module is a glyph hinter */ +#define FT_MODULE_STYLER 8 /* this module is a styler */ + +#define FT_MODULE_DRIVER_SCALABLE 0x100 /* the driver supports */ + /* scalable fonts */ +#define FT_MODULE_DRIVER_NO_OUTLINES 0x200 /* the driver does not */ + /* support vector outlines */ +#define FT_MODULE_DRIVER_HAS_HINTER 0x400 /* the driver provides its */ + /* own hinter */ + + + /* deprecated values */ +#define ft_module_font_driver FT_MODULE_FONT_DRIVER +#define ft_module_renderer FT_MODULE_RENDERER +#define ft_module_hinter FT_MODULE_HINTER +#define ft_module_styler FT_MODULE_STYLER + +#define ft_module_driver_scalable FT_MODULE_DRIVER_SCALABLE +#define ft_module_driver_no_outlines FT_MODULE_DRIVER_NO_OUTLINES +#define ft_module_driver_has_hinter FT_MODULE_DRIVER_HAS_HINTER + + + typedef void + (*FT_Module_Interface)( void ); + + typedef FT_Error + (*FT_Module_Constructor)( FT_Module module ); + + typedef void + (*FT_Module_Destructor)( FT_Module module ); + + typedef FT_Module_Interface + (*FT_Module_Requester)( FT_Module module, + const char* name ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Module_Class */ + /* */ + /* <Description> */ + /* The module class descriptor. */ + /* */ + /* <Fields> */ + /* module_flags :: Bit flags describing the module. */ + /* */ + /* module_size :: The size of one module object/instance in */ + /* bytes. */ + /* */ + /* module_name :: The name of the module. */ + /* */ + /* module_version :: The version, as a 16.16 fixed number */ + /* (major.minor). */ + /* */ + /* module_requires :: The version of FreeType this module requires */ + /* (starts at version 2.0, i.e 0x20000) */ + /* */ + /* module_init :: A function used to initialize (not create) a */ + /* new module object. */ + /* */ + /* module_done :: A function used to finalize (not destroy) a */ + /* given module object */ + /* */ + /* get_interface :: Queries a given module for a specific */ + /* interface by name. */ + /* */ + typedef struct FT_Module_Class_ + { + FT_ULong module_flags; + FT_Long module_size; + const FT_String* module_name; + FT_Fixed module_version; + FT_Fixed module_requires; + + const void* module_interface; + + FT_Module_Constructor module_init; + FT_Module_Destructor module_done; + FT_Module_Requester get_interface; + + } FT_Module_Class; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Add_Module */ + /* */ + /* <Description> */ + /* Adds a new module to a given library instance. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library object. */ + /* */ + /* <Input> */ + /* clazz :: A pointer to class descriptor for the module. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* An error will be returned if a module already exists by that name, */ + /* or if the module requires a version of FreeType that is too great. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Add_Module( FT_Library library, + const FT_Module_Class* clazz ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Module */ + /* */ + /* <Description> */ + /* Finds a module by its name. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object. */ + /* */ + /* module_name :: The module's name (as an ASCII string). */ + /* */ + /* <Return> */ + /* A module handle. 0 if none was found. */ + /* */ + /* <Note> */ + /* You should better be familiar with FreeType internals to know */ + /* which module to look for :-) */ + /* */ + FT_EXPORT( FT_Module ) + FT_Get_Module( FT_Library library, + const char* module_name ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Remove_Module */ + /* */ + /* <Description> */ + /* Removes a given module from a library instance. */ + /* */ + /* <InOut> */ + /* library :: A handle to a library object. */ + /* */ + /* <Input> */ + /* module :: A handle to a module object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The module object is destroyed by the function in case of success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Remove_Module( FT_Library library, + FT_Module module ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Library */ + /* */ + /* <Description> */ + /* This function is used to create a new FreeType library instance */ + /* from a given memory object. It is thus possible to use libraries */ + /* with distinct memory allocators within the same program. */ + /* */ + /* <Input> */ + /* memory :: A handle to the original memory object. */ + /* */ + /* <Output> */ + /* alibrary :: A pointer to handle of a new library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Library( FT_Memory memory, + FT_Library *alibrary ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Library */ + /* */ + /* <Description> */ + /* Discards a given library object. This closes all drivers and */ + /* discards all resource objects. */ + /* */ + /* <Input> */ + /* library :: A handle to the target library. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_Library( FT_Library library ); + + + + typedef void + (*FT_DebugHook_Func)( void* arg ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Debug_Hook */ + /* */ + /* <Description> */ + /* Sets a debug hook function for debugging the interpreter of a font */ + /* format. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library object. */ + /* */ + /* <Input> */ + /* hook_index :: The index of the debug hook. You should use the */ + /* values defined in ftobjs.h, e.g. */ + /* FT_DEBUG_HOOK_TRUETYPE. */ + /* */ + /* debug_hook :: The function used to debug the interpreter. */ + /* */ + /* <Note> */ + /* Currently, four debug hook slots are available, but only two (for */ + /* the TrueType and the Type 1 interpreter) are defined. */ + /* */ + FT_EXPORT( void ) + FT_Set_Debug_Hook( FT_Library library, + FT_UInt hook_index, + FT_DebugHook_Func debug_hook ); + + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Add_Default_Modules */ + /* */ + /* <Description> */ + /* Adds the set of default drivers to a given library object. */ + /* This is only useful when you create a library object with */ + /* FT_New_Library() (usually to plug a custom memory manager). */ + /* */ + /* <InOut> */ + /* library :: A handle to a new library object. */ + /* */ + FT_EXPORT( void ) + FT_Add_Default_Modules( FT_Library library ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FTMODULE_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftoutln.h b/extra_lib/include/freetype/freetype/ftoutln.h new file mode 100644 index 0000000..79ed35b --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftoutln.h @@ -0,0 +1,472 @@ +/***************************************************************************/ +/* */ +/* ftoutln.h */ +/* */ +/* Support for the FT_Outline type used to store glyph shapes of */ +/* most scalable font formats (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTOUTLN_H__ +#define __FTOUTLN_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /* <Title> */ + /* Outline Processing */ + /* */ + /* <Abstract> */ + /* Functions to create, transform, and render vectorial glyph images. */ + /* */ + /* <Description> */ + /* This section contains routines used to create and destroy scalable */ + /* glyph images known as `outlines'. These can also be measured, */ + /* transformed, and converted into bitmaps and pixmaps. */ + /* */ + /* <Order> */ + /* FT_Outline */ + /* FT_OUTLINE_FLAGS */ + /* FT_Outline_New */ + /* FT_Outline_Done */ + /* FT_Outline_Copy */ + /* FT_Outline_Translate */ + /* FT_Outline_Transform */ + /* FT_Outline_Reverse */ + /* FT_Outline_Check */ + /* */ + /* FT_Outline_Get_CBox */ + /* FT_Outline_Get_BBox */ + /* */ + /* FT_Outline_Get_Bitmap */ + /* FT_Outline_Render */ + /* */ + /* FT_Outline_Decompose */ + /* FT_Outline_Funcs */ + /* FT_Outline_MoveTo_Func */ + /* FT_Outline_LineTo_Func */ + /* FT_Outline_ConicTo_Func */ + /* FT_Outline_CubicTo_Func */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Decompose */ + /* */ + /* <Description> */ + /* Walks over an outline's structure to decompose it into individual */ + /* segments and Bezier arcs. This function is also able to emit */ + /* `move to' and `close to' operations to indicate the start and end */ + /* of new contours in the outline. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source target. */ + /* */ + /* func_interface :: A table of `emitters', i.e,. function pointers */ + /* called during decomposition to indicate path */ + /* operations. */ + /* */ + /* <InOut> */ + /* user :: A typeless pointer which is passed to each */ + /* emitter during the decomposition. It can be */ + /* used to store the state during the */ + /* decomposition. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means sucess. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Decompose( FT_Outline* outline, + const FT_Outline_Funcs* func_interface, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_New */ + /* */ + /* <Description> */ + /* Creates a new outline of a given size. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object from where the */ + /* outline is allocated. Note however that the new */ + /* outline will NOT necessarily be FREED, when */ + /* destroying the library, by FT_Done_FreeType(). */ + /* */ + /* numPoints :: The maximal number of points within the outline. */ + /* */ + /* numContours :: The maximal number of contours within the outline. */ + /* */ + /* <Output> */ + /* anoutline :: A handle to the new outline. NULL in case of */ + /* error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The reason why this function takes a `library' parameter is simply */ + /* to use the library's memory allocator. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_New( FT_Library library, + FT_UInt numPoints, + FT_Int numContours, + FT_Outline *anoutline ); + + + FT_EXPORT( FT_Error ) + FT_Outline_New_Internal( FT_Memory memory, + FT_UInt numPoints, + FT_Int numContours, + FT_Outline *anoutline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Done */ + /* */ + /* <Description> */ + /* Destroys an outline created with FT_Outline_New(). */ + /* */ + /* <Input> */ + /* library :: A handle of the library object used to allocate the */ + /* outline. */ + /* */ + /* outline :: A pointer to the outline object to be discarded. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* If the outline's `owner' field is not set, only the outline */ + /* descriptor will be released. */ + /* */ + /* The reason why this function takes an `library' parameter is */ + /* simply to use FT_Free(). */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Done( FT_Library library, + FT_Outline* outline ); + + + FT_EXPORT( FT_Error ) + FT_Outline_Done_Internal( FT_Memory memory, + FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Check */ + /* */ + /* <Description> */ + /* Check the contents of an outline descriptor. */ + /* */ + /* <Input> */ + /* outline :: A handle to a source outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Check( FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_CBox */ + /* */ + /* <Description> */ + /* Returns an outline's `control box'. The control box encloses all */ + /* the outline's points, including Bezier control points. Though it */ + /* coincides with the exact bounding box for most glyphs, it can be */ + /* slightly larger in some situations (like when rotating an outline */ + /* which contains Bezier outside arcs). */ + /* */ + /* Computing the control box is very fast, while getting the bounding */ + /* box can take much more time as it needs to walk over all segments */ + /* and arcs in the outline. To get the latter, you can use the */ + /* `ftbbox' component which is dedicated to this single task. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <Output> */ + /* acbox :: The outline's control box. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Get_CBox( FT_Outline* outline, + FT_BBox *acbox ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Translate */ + /* */ + /* <Description> */ + /* Applies a simple translation to the points of an outline. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Input> */ + /* xOffset :: The horizontal offset. */ + /* */ + /* yOffset :: The vertical offset. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Translate( FT_Outline* outline, + FT_Pos xOffset, + FT_Pos yOffset ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Copy */ + /* */ + /* <Description> */ + /* Copies an outline into another one. Both objects must have the */ + /* same sizes (number of points & number of contours) when this */ + /* function is called. */ + /* */ + /* <Input> */ + /* source :: A handle to the source outline. */ + /* */ + /* <Output> */ + /* target :: A handle to the target outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Copy( FT_Outline* source, + FT_Outline *target ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Transform */ + /* */ + /* <Description> */ + /* Applies a simple 2x2 matrix to all of an outline's points. Useful */ + /* for applying rotations, slanting, flipping, etc. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the transformation matrix. */ + /* */ + /* <Note> */ + /* You can use FT_Outline_Translate() if you need to translate the */ + /* outline's points. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Transform( FT_Outline* outline, + FT_Matrix* matrix ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Reverse */ + /* */ + /* <Description> */ + /* Reverses the drawing direction of an outline. This is used to */ + /* ensure consistent fill conventions for mirrored glyphs. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Note> */ + /* This functions toggles the bit flag `FT_OUTLINE_REVERSE_FILL' in */ + /* the outline's `flags' field. */ + /* */ + /* It shouldn't be used by a normal client application, unless it */ + /* knows what it is doing. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Reverse( FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_Bitmap */ + /* */ + /* <Description> */ + /* Renders an outline within a bitmap. The outline's image is simply */ + /* OR-ed to the target bitmap. */ + /* */ + /* <Input> */ + /* library :: A handle to a FreeType library object. */ + /* */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <Output> */ + /* abitmap :: A pointer to the target bitmap descriptor. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function does NOT CREATE the bitmap, it only renders an */ + /* outline image within the one you pass to it! */ + /* */ + /* It will use the raster correponding to the default glyph format. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Get_Bitmap( FT_Library library, + FT_Outline* outline, + FT_Bitmap *abitmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Render */ + /* */ + /* <Description> */ + /* Renders an outline within a bitmap using the current scan-convert. */ + /* This functions uses an FT_Raster_Params structure as an argument, */ + /* allowing advanced features like direct composition, translucency, */ + /* etc. */ + /* */ + /* <Input> */ + /* library :: A handle to a FreeType library object. */ + /* */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <InOut> */ + /* params :: A pointer to a FT_Raster_Params structure used to */ + /* describe the rendering operation. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* You should know what you are doing and how FT_Raster_Params works */ + /* to use this function. */ + /* */ + /* The field `params.source' will be set to `outline' before the scan */ + /* converter is called, which means that the value you give to it is */ + /* actually ignored. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Render( FT_Library library, + FT_Outline* outline, + FT_Raster_Params* params ); + + + /************************************************************************** + * + * @enum: + * FT_Orientation + * + * @description: + * A list of values used to describe an outline's contour orientation. + * + * The TrueType and Postscript specifications use different conventions + * to determine whether outline contours should be filled or unfilled. + * + * @values: + * FT_ORIENTATION_TRUETYPE :: + * According to the TrueType specification, clockwise contours must + * be filled, and counter-clockwise ones must be unfilled. + * + * FT_ORIENTATION_POSTSCRIPT :: + * According to the Postscript specification, counter-clockwise contours + * must be filled, and clockwise ones must be unfilled. + * + * FT_ORIENTATION_FILL_RIGHT :: + * This is identical to @FT_ORIENTATION_TRUETYPE, but is used to + * remember that in TrueType, everything that is to the right of + * the drawing direction of a contour must be filled. + * + * FT_ORIENTATION_FILL_LEFT :: + * This is identical to @FT_ORIENTATION_POSTSCRIPT, but is used to + * remember that in Postscript, everything that is to the left of + * the drawing direction of a contour must be filled. + */ + typedef enum + { + FT_ORIENTATION_TRUETYPE = 0, + FT_ORIENTATION_POSTSCRIPT = 1, + FT_ORIENTATION_FILL_RIGHT = FT_ORIENTATION_TRUETYPE, + FT_ORIENTATION_FILL_LEFT = FT_ORIENTATION_POSTSCRIPT + + } FT_Orientation; + + + /************************************************************************** + * + * @function: + * FT_Outline_Get_Orientation + * + * @description: + * This function analyzes a glyph outline and tries to compute its + * fill orientation (see @FT_Orientation). This is done by computing + * the direction of each global horizontal and/or vertical extrema + * within the outline. + * + * Note that this will return @FT_ORIENTATION_TRUETYPE for empty + * outlines. + * + * @input: + * outline :: + * A handle to the source outline. + * + * @return: + * The orientation. + * + */ + FT_EXPORT( FT_Orientation ) + FT_Outline_Get_Orientation( FT_Outline* outline ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FTOUTLN_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftpfr.h b/extra_lib/include/freetype/freetype/ftpfr.h new file mode 100644 index 0000000..c9a5675 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftpfr.h @@ -0,0 +1,172 @@ +/***************************************************************************/ +/* */ +/* ftpfr.h */ +/* */ +/* FreeType API for accessing PFR-specific data (specification only). */ +/* */ +/* Copyright 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTPFR_H__ +#define __FTPFR_H__ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* pfr_fonts */ + /* */ + /* <Title> */ + /* PFR Fonts */ + /* */ + /* <Abstract> */ + /* PFR/TrueDoc specific APIs */ + /* */ + /* <Description> */ + /* This section contains the declaration of PFR-specific functions. */ + /* */ + /*************************************************************************/ + + + /********************************************************************** + * + * @function: + * FT_Get_PFR_Metrics + * + * @description: + * Return the outline and metrics resolutions of a given PFR face. + * + * @input: + * face :: Handle to the input face. It can be a non-PFR face. + * + * @output: + * aoutline_resolution :: + * Outline resolution. This is equivalent to `face->units_per_EM'. + * Optional (parameter can be NULL). + * + * ametrics_resolution :: + * Metrics resolution. This is equivalent to `outline_resolution' + * for non-PFR fonts. Optional (parameter can be NULL). + * + * ametrics_x_scale :: + * A 16.16 fixed-point number used to scale distance expressed + * in metrics units to device sub-pixels. This is equivalent to + * `face->size->x_scale', but for metrics only. Optional (parameter + * can be NULL) + * + * ametrics_y_scale :: + * Same as `ametrics_x_scale' but for the vertical direction. + * optional (parameter can be NULL) + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * If the input face is not a PFR, this function will return an error. + * However, in all cases, it will return valid values. + */ + FT_EXPORT( FT_Error ) + FT_Get_PFR_Metrics( FT_Face face, + FT_UInt *aoutline_resolution, + FT_UInt *ametrics_resolution, + FT_Fixed *ametrics_x_scale, + FT_Fixed *ametrics_y_scale ); + + + /********************************************************************** + * + * @function: + * FT_Get_PFR_Kerning + * + * @description: + * Return the kerning pair corresponding to two glyphs in a PFR face. + * The distance is expressed in metrics units, unlike the result of + * @FT_Get_Kerning. + * + * @input: + * face :: A handle to the input face. + * + * left :: Index of the left glyph. + * + * right :: Index of the right glyph. + * + * @output: + * avector :: A kerning vector. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * This function always return distances in original PFR metrics + * units. This is unlike @FT_Get_Kerning with the @FT_KERNING_UNSCALED + * mode, which always returns distances converted to outline units. + * + * You can use the value of the `x_scale' and `y_scale' parameters + * returned by @FT_Get_PFR_Metrics to scale these to device sub-pixels. + */ + FT_EXPORT( FT_Error ) + FT_Get_PFR_Kerning( FT_Face face, + FT_UInt left, + FT_UInt right, + FT_Vector *avector ); + + + /********************************************************************** + * + * @function: + * FT_Get_PFR_Advance + * + * @description: + * Return a given glyph advance, expressed in original metrics units, + * from a PFR font. + * + * @input: + * face :: A handle to the input face. + * + * gindex :: The glyph index. + * + * @output: + * aadvance :: The glyph advance in metrics units. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * You can use the `x_scale' or `y_scale' results of @FT_Get_PFR_Metrics + * to convert the advance to device sub-pixels (i.e. 1/64th of pixels). + */ + FT_EXPORT( FT_Error ) + FT_Get_PFR_Advance( FT_Face face, + FT_UInt gindex, + FT_Pos *aadvance ); + + /* */ + + +FT_END_HEADER + +#endif /* __FTBDF_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftrender.h b/extra_lib/include/freetype/freetype/ftrender.h new file mode 100644 index 0000000..db3ca94 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftrender.h @@ -0,0 +1,229 @@ +/***************************************************************************/ +/* */ +/* ftrender.h */ +/* */ +/* FreeType renderer modules public interface (specification). */ +/* */ +/* Copyright 1996-2001 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTRENDER_H__ +#define __FTRENDER_H__ + + +#include <ft2build.h> +#include FT_MODULE_H +#include FT_GLYPH_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* module_management */ + /* */ + /*************************************************************************/ + + + /* create a new glyph object */ + typedef FT_Error + (*FT_Glyph_InitFunc)( FT_Glyph glyph, + FT_GlyphSlot slot ); + + /* destroys a given glyph object */ + typedef void + (*FT_Glyph_DoneFunc)( FT_Glyph glyph ); + + typedef void + (*FT_Glyph_TransformFunc)( FT_Glyph glyph, + FT_Matrix* matrix, + FT_Vector* delta ); + + typedef void + (*FT_Glyph_GetBBoxFunc)( FT_Glyph glyph, + FT_BBox* abbox ); + + typedef FT_Error + (*FT_Glyph_CopyFunc)( FT_Glyph source, + FT_Glyph target ); + + typedef FT_Error + (*FT_Glyph_PrepareFunc)( FT_Glyph glyph, + FT_GlyphSlot slot ); + +/* deprecated */ +#define FT_Glyph_Init_Func FT_Glyph_InitFunc +#define FT_Glyph_Done_Func FT_Glyph_DoneFunc +#define FT_Glyph_Transform_Func FT_Glyph_TransformFunc +#define FT_Glyph_BBox_Func FT_Glyph_GetBBoxFunc +#define FT_Glyph_Copy_Func FT_Glyph_CopyFunc +#define FT_Glyph_Prepare_Func FT_Glyph_PrepareFunc + + + struct FT_Glyph_Class_ + { + FT_Long glyph_size; + FT_Glyph_Format glyph_format; + FT_Glyph_InitFunc glyph_init; + FT_Glyph_DoneFunc glyph_done; + FT_Glyph_CopyFunc glyph_copy; + FT_Glyph_TransformFunc glyph_transform; + FT_Glyph_GetBBoxFunc glyph_bbox; + FT_Glyph_PrepareFunc glyph_prepare; + }; + + + typedef FT_Error + (*FT_Renderer_RenderFunc)( FT_Renderer renderer, + FT_GlyphSlot slot, + FT_UInt mode, + FT_Vector* origin ); + + typedef FT_Error + (*FT_Renderer_TransformFunc)( FT_Renderer renderer, + FT_GlyphSlot slot, + FT_Matrix* matrix, + FT_Vector* delta ); + + + typedef void + (*FT_Renderer_GetCBoxFunc)( FT_Renderer renderer, + FT_GlyphSlot slot, + FT_BBox* cbox ); + + + typedef FT_Error + (*FT_Renderer_SetModeFunc)( FT_Renderer renderer, + FT_ULong mode_tag, + FT_Pointer mode_ptr ); + +/* deprecated identifiers */ +#define FTRenderer_render FT_Renderer_RenderFunc +#define FTRenderer_transform FT_Renderer_TransformFunc +#define FTRenderer_getCBox FT_Renderer_GetCBoxFunc +#define FTRenderer_setMode FT_Renderer_SetModeFunc + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Renderer_Class */ + /* */ + /* <Description> */ + /* The renderer module class descriptor. */ + /* */ + /* <Fields> */ + /* root :: The root FT_Module_Class fields. */ + /* */ + /* glyph_format :: The glyph image format this renderer handles. */ + /* */ + /* render_glyph :: A method used to render the image that is in a */ + /* given glyph slot into a bitmap. */ + /* */ + /* set_mode :: A method used to pass additional parameters. */ + /* */ + /* raster_class :: For `FT_GLYPH_FORMAT_OUTLINE' renderers only, this */ + /* is a pointer to its raster's class. */ + /* */ + /* raster :: For `FT_GLYPH_FORMAT_OUTLINE' renderers only. this */ + /* is a pointer to the corresponding raster object, */ + /* if any. */ + /* */ + typedef struct FT_Renderer_Class_ + { + FT_Module_Class root; + + FT_Glyph_Format glyph_format; + + FT_Renderer_RenderFunc render_glyph; + FT_Renderer_TransformFunc transform_glyph; + FT_Renderer_GetCBoxFunc get_glyph_cbox; + FT_Renderer_SetModeFunc set_mode; + + FT_Raster_Funcs* raster_class; + + } FT_Renderer_Class; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Renderer */ + /* */ + /* <Description> */ + /* Retrieves the current renderer for a given glyph format. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object. */ + /* */ + /* format :: The glyph format. */ + /* */ + /* <Return> */ + /* A renderer handle. 0 if none found. */ + /* */ + /* <Note> */ + /* An error will be returned if a module already exists by that name, */ + /* or if the module requires a version of FreeType that is too great. */ + /* */ + /* To add a new renderer, simply use FT_Add_Module(). To retrieve a */ + /* renderer by its name, use FT_Get_Module(). */ + /* */ + FT_EXPORT( FT_Renderer ) + FT_Get_Renderer( FT_Library library, + FT_Glyph_Format format ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Renderer */ + /* */ + /* <Description> */ + /* Sets the current renderer to use, and set additional mode. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library object. */ + /* */ + /* <Input> */ + /* renderer :: A handle to the renderer object. */ + /* */ + /* num_params :: The number of additional parameters. */ + /* */ + /* parameters :: Additional parameters. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* In case of success, the renderer will be used to convert glyph */ + /* images in the renderer's known format into bitmaps. */ + /* */ + /* This doesn't change the current renderer for other formats. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Renderer( FT_Library library, + FT_Renderer renderer, + FT_UInt num_params, + FT_Parameter* parameters ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FTRENDER_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftsizes.h b/extra_lib/include/freetype/freetype/ftsizes.h new file mode 100644 index 0000000..f0ec915 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftsizes.h @@ -0,0 +1,159 @@ +/***************************************************************************/ +/* */ +/* ftsizes.h */ +/* */ +/* FreeType size objects management (specification). */ +/* */ +/* Copyright 1996-2001, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Typical application would normally not need to use these functions. */ + /* However, they have been placed in a public API for the rare cases */ + /* where they are needed. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTSIZES_H__ +#define __FTSIZES_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* sizes_management */ + /* */ + /* <Title> */ + /* Size management */ + /* */ + /* <Abstract> */ + /* Managing multiple sizes per face */ + /* */ + /* <Description> */ + /* When creating a new face object (e.g. with @FT_New_Face), an */ + /* @FT_Size object is automatically created and used to store all */ + /* pixel-size dependent information, available in the "face->size" */ + /* field. */ + /* */ + /* It is however possible to create more sizes for a given face, */ + /* mostly in order to manage several character pixel sizes of the */ + /* same font family and style. See @FT_New_Size and @FT_Done_Size. */ + /* */ + /* Note that @FT_Set_Pixel_Sizes and @FT_Set_Char_Size only */ + /* modify the contents of the current "active" size; you thus need */ + /* to use @FT_Activate_Size to change it. */ + /* */ + /* 99% of applications won't need the functions provided here, */ + /* especially if they use the caching sub-system, so be cautious */ + /* when using these. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Size */ + /* */ + /* <Description> */ + /* Creates a new size object from a given face object. */ + /* */ + /* <Input> */ + /* face :: A handle to a parent face object. */ + /* */ + /* <Output> */ + /* asize :: A handle to a new size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* You need to call @FT_Activate_Size in order to select the new size */ + /* for upcoming calls to @FT_Set_Pixel_Sizes, @FT_Set_Char_Size, */ + /* @FT_Load_Glyph, @FT_Load_Char, etc. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Size( FT_Face face, + FT_Size* size ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Size */ + /* */ + /* <Description> */ + /* Discards a given size object. Note that @FT_Done_Face */ + /* automatically discards all size objects allocated with */ + /* @FT_New_Size. */ + /* */ + /* <Input> */ + /* size :: A handle to a target size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_Size( FT_Size size ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Activate_Size */ + /* */ + /* <Description> */ + /* Even though it is possible to create several size objects for a */ + /* given face (see @FT_New_Size for details), functions like */ + /* @FT_Load_Glyph or @FT_Load_Char only use the last-created one to */ + /* determine the "current character pixel size". */ + /* */ + /* This function can be used to "activate" a previously created size */ + /* object. */ + /* */ + /* <Input> */ + /* size :: A handle to a target size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* If "face" is the size's parent face object, this function changes */ + /* the value of "face->size" to the input size handle. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Activate_Size( FT_Size size ); + + /* */ + + +FT_END_HEADER + +#endif /* __FTSIZES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftsnames.h b/extra_lib/include/freetype/freetype/ftsnames.h new file mode 100644 index 0000000..0e2d9a4 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftsnames.h @@ -0,0 +1,167 @@ +/***************************************************************************/ +/* */ +/* ftsnames.h */ +/* */ +/* Simple interface to access SFNT name tables (which are used */ +/* to hold font names, copyright info, notices, etc.) (specification). */ +/* */ +/* This is _not_ used to retrieve glyph names! */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FT_SFNT_NAMES_H__ +#define __FT_SFNT_NAMES_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* sfnt_names */ + /* */ + /* <Title> */ + /* SFNT Names */ + /* */ + /* <Abstract> */ + /* Access the names embedded in TrueType and OpenType files. */ + /* */ + /* <Description> */ + /* The TrueType and OpenType specification allow the inclusion of */ + /* a special `names table' in font files. This table contains */ + /* textual (and internationalized) information regarding the font, */ + /* like family name, copyright, version, etc. */ + /* */ + /* The definitions below are used to access them if available. */ + /* */ + /* Note that this has nothing to do with glyph names! */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SfntName */ + /* */ + /* <Description> */ + /* A structure used to model an SFNT `name' table entry. */ + /* */ + /* <Fields> */ + /* platform_id :: The platform ID for `string'. */ + /* */ + /* encoding_id :: The encoding ID for `string'. */ + /* */ + /* language_id :: The language ID for `string'. */ + /* */ + /* name_id :: An identifier for `string'. */ + /* */ + /* string :: The `name' string. Note that its format differs */ + /* depending on the (platform,encoding) pair. It can */ + /* be a Pascal String, a UTF-16 one, etc.. */ + /* */ + /* Generally speaking, the string is not */ + /* zero-terminated. Please refer to the TrueType */ + /* specification for details.. */ + /* */ + /* string_len :: The length of `string' in bytes. */ + /* */ + /* <Note> */ + /* Possible values for `platform_id', `encoding_id', `language_id', */ + /* and `name_id' are given in the file `ttnameid.h'. For details */ + /* please refer to the TrueType or OpenType specification. */ + /* */ + typedef struct FT_SfntName_ + { + FT_UShort platform_id; + FT_UShort encoding_id; + FT_UShort language_id; + FT_UShort name_id; + + FT_Byte* string; /* this string is *not* null-terminated! */ + FT_UInt string_len; /* in bytes */ + + } FT_SfntName; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Sfnt_Name_Count */ + /* */ + /* <Description> */ + /* Retrieves the number of name strings in the SFNT `name' table. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* <Return> */ + /* The number of strings in the `name' table. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Sfnt_Name_Count( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Sfnt_Name */ + /* */ + /* <Description> */ + /* Retrieves a string of the SFNT `name' table for a given index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* idx :: The index of the `name' string. */ + /* */ + /* <Output> */ + /* aname :: The indexed FT_SfntName structure. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The `string' array returned in the `aname' structure is not */ + /* null-terminated. */ + /* */ + /* Use FT_Get_Sfnt_Name_Count() to get the total number of available */ + /* `name' table entries, then do a loop until you get the right */ + /* platform, encoding, and name ID. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Sfnt_Name( FT_Face face, + FT_UInt idx, + FT_SfntName *aname ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FT_SFNT_NAMES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftstroker.h b/extra_lib/include/freetype/freetype/ftstroker.h new file mode 100644 index 0000000..a714056 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftstroker.h @@ -0,0 +1,139 @@ +#ifndef __FT_STROKER_H__ +#define __FT_STROKER_H__ + +#include <ft2build.h> +#include FT_OUTLINE_H + +FT_BEGIN_HEADER + +/*@************************************************************* + * + * @type: FT_Stroker + * + * @description: + * opaque handler to a path stroker object + */ + typedef struct FT_StrokerRec_* FT_Stroker; + + +/*@************************************************************* + * + * @enum: FT_Stroker_LineJoin + * + * @description: + * these values determine how two joining lines are rendered + * in a stroker. + * + * @values: + * FT_STROKER_LINEJOIN_ROUND :: + * used to render rounded line joins. circular arcs are used + * to join two lines smoothly + * + * FT_STROKER_LINEJOIN_BEVEL :: + * used to render beveled line joins; i.e. the two joining lines + * are extended until they intersect + * + * FT_STROKER_LINEJOIN_MITER :: + * same as beveled rendering, except that an additional line + * break is added if the angle between the two joining lines + * is too closed (this is useful to avoid unpleasant spikes + * in beveled rendering). + */ + typedef enum + { + FT_STROKER_LINEJOIN_ROUND = 0, + FT_STROKER_LINEJOIN_BEVEL, + FT_STROKER_LINEJOIN_MITER + + } FT_Stroker_LineJoin; + + +/*@************************************************************* + * + * @enum: FT_Stroker_LineCap + * + * @description: + * these values determine how the end of opened sub-paths are + * rendered in a stroke + * + * @values: + * FT_STROKER_LINECAP_BUTT :: + * the end of lines is rendered as a full stop on the last + * point itself + * + * FT_STROKER_LINECAP_ROUND :: + * the end of lines is rendered as a half-circle around the + * last point + * + * FT_STROKER_LINECAP_SQUARE :: + * the end of lines is rendered as a square around the + * last point + */ + typedef enum + { + FT_STROKER_LINECAP_BUTT = 0, + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINECAP_SQUARE + + } FT_Stroker_LineCap; + + /* */ + + FT_EXPORT( FT_Error ) + FT_Stroker_New( FT_Memory memory, + FT_Stroker *astroker ); + + FT_EXPORT( void ) + FT_Stroker_Set( FT_Stroker stroker, + FT_Fixed radius, + FT_Stroker_LineCap line_cap, + FT_Stroker_LineJoin line_join, + FT_Fixed miter_limit ); + + + FT_EXPORT( FT_Error ) + FT_Stroker_ParseOutline( FT_Stroker stroker, + FT_Outline* outline, + FT_Bool opened ); + + FT_EXPORT( FT_Error ) + FT_Stroker_BeginSubPath( FT_Stroker stroker, + FT_Vector* to, + FT_Bool open ); + + FT_EXPORT( FT_Error ) + FT_Stroker_EndSubPath( FT_Stroker stroker ); + + + FT_EXPORT( FT_Error ) + FT_Stroker_LineTo( FT_Stroker stroker, + FT_Vector* to ); + + FT_EXPORT( FT_Error ) + FT_Stroker_ConicTo( FT_Stroker stroker, + FT_Vector* control, + FT_Vector* to ); + + FT_EXPORT( FT_Error ) + FT_Stroker_CubicTo( FT_Stroker stroker, + FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to ); + + + FT_EXPORT( FT_Error ) + FT_Stroker_GetCounts( FT_Stroker stroker, + FT_UInt *anum_points, + FT_UInt *anum_contours ); + + FT_EXPORT( void ) + FT_Stroker_Export( FT_Stroker stroker, + FT_Outline* outline ); + + FT_EXPORT( void ) + FT_Stroker_Done( FT_Stroker stroker ); + + +FT_END_HEADER + +#endif /* __FT_STROKER_H__ */ diff --git a/extra_lib/include/freetype/freetype/ftsynth.h b/extra_lib/include/freetype/freetype/ftsynth.h new file mode 100644 index 0000000..f0c7385 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftsynth.h @@ -0,0 +1,71 @@ +/***************************************************************************/ +/* */ +/* ftsynth.h */ +/* */ +/* FreeType synthesizing code for emboldening and slanting */ +/* (specification). */ +/* */ +/* Copyright 2000-2001, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********* *********/ + /********* WARNING, THIS IS ALPHA CODE, THIS API *********/ + /********* IS DUE TO CHANGE UNTIL STRICTLY NOTIFIED BY THE *********/ + /********* FREETYPE DEVELOPMENT TEAM *********/ + /********* *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#ifndef __FTSYNTH_H__ +#define __FTSYNTH_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /* This code is completely experimental -- use with care! */ + /* It will probably be completely rewritten in the future */ + /* or even integrated into the library. */ + FT_EXPORT( void ) + FT_GlyphSlot_Embolden( FT_GlyphSlot slot ); + + + FT_EXPORT( void ) + FT_GlyphSlot_Oblique( FT_GlyphSlot slot ); + + /* */ + +FT_END_HEADER + +#endif /* __FTSYNTH_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftsysio.h b/extra_lib/include/freetype/freetype/ftsysio.h new file mode 100644 index 0000000..f8e4954 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftsysio.h @@ -0,0 +1,195 @@ +#ifndef __FT_SYSTEM_IO_H__ +#define __FT_SYSTEM_IO_H__ + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** NOTE: THE CONTENT OF THIS FILE IS NOT CURRENTLY USED *****/ + /***** IN NORMAL BUILDS. CONSIDER IT EXPERIMENTAL. *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + + /******************************************************************** + * + * designing custom streams is a bit different now + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +#include <ft2build.h> +#include FT_INTERNAL_OBJECT_H + +FT_BEGIN_HEADER + + /*@******************************************************************* + * + * @type: FT_Stream + * + * @description: + * handle to an input stream object. These are also @FT_Object handles + */ + typedef struct FT_StreamRec_* FT_Stream; + + + /*@******************************************************************* + * + * @type: FT_Stream_Class + * + * @description: + * opaque handle to a @FT_Stream_ClassRec class structure describing + * the methods of input streams + */ + typedef const struct FT_Stream_ClassRec_* FT_Stream_Class; + + + /*@******************************************************************* + * + * @functype: FT_Stream_ReadFunc + * + * @description: + * a method used to read bytes from an input stream into memory + * + * @input: + * stream :: target stream handle + * buffer :: target buffer address + * size :: number of bytes to read + * + * @return: + * number of bytes effectively read. Must be <= 'size'. + */ + typedef FT_ULong (*FT_Stream_ReadFunc)( FT_Stream stream, + FT_Byte* buffer, + FT_ULong size ); + + + /*@******************************************************************* + * + * @functype: FT_Stream_SeekFunc + * + * @description: + * a method used to seek to a new position within a stream + * + * @input: + * stream :: target stream handle + * pos :: new read position, from start of stream + * + * @return: + * error code. 0 means success + */ + typedef FT_Error (*FT_Stream_SeekFunc)( FT_Stream stream, + FT_ULong pos ); + + + /*@******************************************************************* + * + * @struct: FT_Stream_ClassRec + * + * @description: + * a structure used to describe an input stream class + * + * @input: + * clazz :: root @FT_ClassRec fields + * stream_read :: stream byte read method + * stream_seek :: stream seek method + */ + typedef struct FT_Stream_ClassRec_ + { + FT_ClassRec clazz; + FT_Stream_ReadFunc stream_read; + FT_Stream_SeekFunc stream_seek; + + } FT_Stream_ClassRec; + + /* */ + +#define FT_STREAM_CLASS(x) ((FT_Stream_Class)(x)) +#define FT_STREAM_CLASS__READ(x) FT_STREAM_CLASS(x)->stream_read +#define FT_STREAM_CLASS__SEEK(x) FT_STREAM_CLASS(x)->stream_seek; + + /*@******************************************************************* + * + * @struct: FT_StreamRec + * + * @description: + * the input stream object structure. See @FT_Stream_ClassRec for + * its class descriptor + * + * @fields: + * object :: root @FT_ObjectRec fields + * size :: size of stream in bytes (0 if unknown) + * pos :: current position within stream + * base :: for memory-based streams, the address of the stream's + * first data byte in memory. NULL otherwise + * + * cursor :: the current cursor position within an input stream + * frame. Only valid within a FT_FRAME_ENTER .. FT_FRAME_EXIT + * block; NULL otherwise + * + * limit :: the current frame limit within a FT_FRAME_ENTER .. + * FT_FRAME_EXIT block. NULL otherwise + */ + typedef struct FT_StreamRec_ + { + FT_ObjectRec object; + FT_ULong size; + FT_ULong pos; + const FT_Byte* base; + const FT_Byte* cursor; + const FT_Byte* limit; + + } FT_StreamRec; + + /* some useful macros */ +#define FT_STREAM(x) ((FT_Stream)(x)) +#define FT_STREAM_P(x) ((FT_Stream*)(x)) + +#define FT_STREAM__READ(x) FT_STREAM_CLASS__READ(FT_OBJECT__CLASS(x)) +#define FT_STREAM__SEEK(x) FT_STREAM_CLASS__SEEK(FT_OBJECT__CLASS(x)) + +#define FT_STREAM_IS_BASED(x) ( FT_STREAM(x)->base != NULL ) + + /* */ + + /* create new memory-based stream */ + FT_BASE( FT_Error ) ft_stream_new_memory( const FT_Byte* stream_base, + FT_ULong stream_size, + FT_Memory memory, + FT_Stream *astream ); + + FT_BASE( FT_Error ) ft_stream_new_iso( const char* pathanme, + FT_Memory memory, + FT_Stream *astream ); + + + /* handle to default stream class implementation for a given build */ + /* this is used by "FT_New_Face" */ + /* */ + FT_APIVAR( FT_Type ) ft_stream_default_type; + +FT_END_HEADER + +#endif /* __FT_SYSTEM_STREAM_H__ */ diff --git a/extra_lib/include/freetype/freetype/ftsysmem.h b/extra_lib/include/freetype/freetype/ftsysmem.h new file mode 100644 index 0000000..8de0c4d --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftsysmem.h @@ -0,0 +1,202 @@ +#ifndef __FT_SYSTEM_MEMORY_H__ +#define __FT_SYSTEM_MEMORY_H__ + +#include <ft2build.h> + +FT_BEGIN_HEADER + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** NOTE: THE CONTENT OF THIS FILE IS NOT CURRENTLY USED *****/ + /***** IN NORMAL BUILDS. CONSIDER IT EXPERIMENTAL. *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + + /*@********************************************************************** + * + * @type: FT_Memory + * + * @description: + * opaque handle to a memory manager handle. Note that since FreeType + * 2.2, the memory manager structure FT_MemoryRec is hidden to client + * applications. + * + * however, you can still define custom allocators easily using the + * @ft_memory_new API + */ + typedef struct FT_MemoryRec_* FT_Memory; + + + /*@********************************************************************** + * + * @functype: FT_Memory_AllocFunc + * + * @description: + * a function used to allocate a block of memory. + * + * @input: + * size :: size of blocks in bytes. Always > 0 !! + * mem_data :: memory-manager specific optional argument + * (see @ft_memory_new) + * + * @return: + * address of new block. NULL in case of memory exhaustion + */ + typedef FT_Pointer (*FT_Memory_AllocFunc)( FT_ULong size, + FT_Pointer mem_data ); + + + /*@********************************************************************** + * + * @functype: FT_Memory_FreeFunc + * + * @description: + * a function used to release a block of memory created through + * @FT_Memory_AllocFunc or @FT_Memory_ReallocFunc + * + * @input: + * block :: address of target memory block. cannot be NULL !! + * mem_data :: memory-manager specific optional argument + * (see @ft_memory_new) + */ + typedef void (*FT_Memory_FreeFunc) ( FT_Pointer block, + FT_Pointer mem_data ); + + + /*@********************************************************************** + * + * @functype: FT_Memory_ReallocFunc + * + * @description: + * a function used to reallocate a memory block. + * + * @input: + * block :: address of target memory block. cannot be NULL !! + * new_size :: new requested size in bytes + * cur_size :: current block size in bytes + * mem_data :: memory-manager specific optional argument + * (see @ft_memory_new) + */ + typedef FT_Pointer (*FT_Memory_ReallocFunc)( FT_Pointer block, + FT_ULong new_size, + FT_ULong cur_size, + FT_Pointer mem_data ); + + + /*@********************************************************************** + * + * @functype: FT_Memory_CreateFunc + * + * @description: + * a function used to create a @FT_Memory object to model a + * memory manager + * + * @input: + * size :: size of memory manager structure in bytes + * init_data :: optional initialisation argument + * + * @output: + * amem_data :: memory-manager specific argument to block management + * routines. + * + * @return: + * handle to new memory manager object. NULL in case of failure + */ + typedef FT_Pointer (*FT_Memory_CreateFunc)( FT_UInt size, + FT_Pointer init_data, + FT_Pointer *amem_data ); + + + /*@********************************************************************** + * + * @functype: FT_Memory_DestroyFunc + * + * @description: + * a function used to destroy a given @FT_Memory manager + * + * @input: + * memory :: target memory manager handle + * mem_data :: option manager-specific argument + */ + typedef void (*FT_Memory_DestroyFunc)( FT_Memory memory, + FT_Pointer mem_data ); + + + /*@********************************************************************** + * + * @struct: FT_Memory_FuncsRec + * + * @description: + * a function used to hold all methods of a given memory manager + * implementation. + * + * @fields: + * mem_alloc :: block allocation routine + * mem_free :: block release routine + * mem_realloc :: block re-allocation routine + * mem_create :: manager creation routine + * mem_destroy :: manager destruction routine + */ + typedef struct FT_Memory_FuncsRec_ + { + FT_Memory_AllocFunc mem_alloc; + FT_Memory_FreeFunc mem_free; + FT_Memory_ReallocFunc mem_realloc; + FT_Memory_CreateFunc mem_create; + FT_Memory_DestroyFunc mem_destroy; + + } FT_Memory_FuncsRec, *FT_Memory_Funcs; + + + /*@********************************************************************** + * + * @type: FT_Memory_Funcs + * + * @description: + * a pointer to a constant @FT_Memory_FuncsRec structure used to + * describe a given memory manager implementation. + */ + typedef const FT_Memory_FuncsRec* FT_Memory_Funcs; + + + /*@********************************************************************** + * + * @function: ft_memory_new + * + * @description: + * create a new memory manager, given a set of memory methods + * + * @input: + * mem_funcs :: handle to memory manager implementation descriptor + * mem_init_data :: optional initialisation argument, passed to + * @FT_Memory_CreateFunc + * + * @return: + * new memory manager handle. NULL in case of failure + */ + FT_BASE( FT_Memory ) + ft_memory_new( FT_Memory_Funcs mem_funcs, + FT_Pointer mem_init_data ); + + + /*@********************************************************************** + * + * @function: ft_memory_destroy + * + * @description: + * destroy a given memory manager + * + * @input: + * memory :: handle to target memory manager + */ + FT_BASE( void ) + ft_memory_destroy( FT_Memory memory ); + +/* */ + +FT_END_HEADER + +#endif /* __FT_SYSTEM_MEMORY_H__ */ diff --git a/extra_lib/include/freetype/freetype/ftsystem.h b/extra_lib/include/freetype/freetype/ftsystem.h new file mode 100644 index 0000000..d1c55f7 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftsystem.h @@ -0,0 +1,309 @@ +/***************************************************************************/ +/* */ +/* ftsystem.h */ +/* */ +/* FreeType low-level system interface definition (specification). */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTSYSTEM_H__ +#define __FTSYSTEM_H__ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* system_interface */ + /* */ + /* <Title> */ + /* System Interface */ + /* */ + /* <Abstract> */ + /* How FreeType manages memory and i/o. */ + /* */ + /* <Description> */ + /* This section contains various definitions related to memory */ + /* management and i/o access. You need to understand this */ + /* information if you want to use a custom memory manager or you own */ + /* input i/o streams. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* M E M O R Y M A N A G E M E N T */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* @type: */ + /* FT_Memory */ + /* */ + /* @description: */ + /* A handle to a given memory manager object, defined with a */ + /* @FT_MemoryRec structure. */ + /* */ + typedef struct FT_MemoryRec_* FT_Memory; + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* FT_Alloc_Func */ + /* */ + /* @description: */ + /* A function used to allocate `size' bytes from `memory'. */ + /* */ + /* @input: */ + /* memory :: A handle to the source memory manager. */ + /* */ + /* size :: The size in bytes to allocate. */ + /* */ + /* @return: */ + /* Address of new memory block. 0 in case of failure. */ + /* */ + typedef void* + (*FT_Alloc_Func)( FT_Memory memory, + long size ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* FT_Free_Func */ + /* */ + /* @description: */ + /* A function used to release a given block of memory. */ + /* */ + /* @input: */ + /* memory :: A handle to the source memory manager. */ + /* */ + /* block :: The address of the target memory block. */ + /* */ + typedef void + (*FT_Free_Func)( FT_Memory memory, + void* block ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* FT_Realloc_Func */ + /* */ + /* @description: */ + /* a function used to re-allocate a given block of memory. */ + /* */ + /* @input: */ + /* memory :: A handle to the source memory manager. */ + /* */ + /* cur_size :: The block's current size in bytes. */ + /* */ + /* new_size :: The block's requested new size. */ + /* */ + /* block :: The block's current address. */ + /* */ + /* @return: */ + /* New block address. 0 in case of memory shortage. */ + /* */ + /* @note: */ + /* In case of error, the old block must still be available. */ + /* */ + typedef void* + (*FT_Realloc_Func)( FT_Memory memory, + long cur_size, + long new_size, + void* block ); + + + /*************************************************************************/ + /* */ + /* @struct: */ + /* FT_MemoryRec */ + /* */ + /* @description: */ + /* A structure used to describe a given memory manager to FreeType 2. */ + /* */ + /* @fields: */ + /* user :: A generic typeless pointer for user data. */ + /* */ + /* alloc :: A pointer type to an allocation function. */ + /* */ + /* free :: A pointer type to an memory freeing function. */ + /* */ + /* realloc :: A pointer type to a reallocation function. */ + /* */ + struct FT_MemoryRec_ + { + void* user; + FT_Alloc_Func alloc; + FT_Free_Func free; + FT_Realloc_Func realloc; + }; + + + /*************************************************************************/ + /* */ + /* I / O M A N A G E M E N T */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* @type: */ + /* FT_Stream */ + /* */ + /* @description: */ + /* A handle to an input stream. */ + /* */ + typedef struct FT_StreamRec_* FT_Stream; + + + /*************************************************************************/ + /* */ + /* @struct: */ + /* FT_StreamDesc */ + /* */ + /* @description: */ + /* A union type used to store either a long or a pointer. This is */ + /* used to store a file descriptor or a FILE* in an input stream. */ + /* */ + typedef union FT_StreamDesc_ + { + long value; + void* pointer; + + } FT_StreamDesc; + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* FT_Stream_IoFunc */ + /* */ + /* @description: */ + /* A function used to seek and read data from a given input stream. */ + /* */ + /* @input: */ + /* stream :: A handle to the source stream. */ + /* */ + /* offset :: The offset of read in stream (always from start). */ + /* */ + /* buffer :: The address of the read buffer. */ + /* */ + /* count :: The number of bytes to read from the stream. */ + /* */ + /* @return: */ + /* The number of bytes effectively read by the stream. */ + /* */ + /* @note: */ + /* This function might be called to perform a seek or skip operation */ + /* with a `count' of 0. */ + /* */ + typedef unsigned long + (*FT_Stream_IoFunc)( FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* FT_Stream_CloseFunc */ + /* */ + /* @description: */ + /* A function used to close a given input stream. */ + /* */ + /* @input: */ + /* stream :: A handle to the target stream. */ + /* */ + typedef void + (*FT_Stream_CloseFunc)( FT_Stream stream ); + + + /*************************************************************************/ + /* */ + /* @struct: */ + /* FT_StreamRec */ + /* */ + /* @description: */ + /* A structure used to describe an input stream. */ + /* */ + /* @input: */ + /* base :: For memory-based streams, this is the address of the */ + /* first stream byte in memory. This field should */ + /* always be set to NULL for disk-based streams. */ + /* */ + /* size :: The stream size in bytes. */ + /* */ + /* pos :: The current position within the stream. */ + /* */ + /* descriptor :: This field is a union that can hold an integer or a */ + /* pointer. It is used by stream implementations to */ + /* store file descriptors or FILE* pointers. */ + /* */ + /* pathname :: This field is completely ignored by FreeType. */ + /* However, it is often useful during debugging to use */ + /* it to store the stream's filename (where available). */ + /* */ + /* read :: The stream's input function. */ + /* */ + /* close :: The stream;s close function. */ + /* */ + /* memory :: The memory manager to use to preload frames. This is */ + /* set internally by FreeType and shouldn't be touched */ + /* by stream implementations. */ + /* */ + /* cursor :: This field is set and used internally by FreeType */ + /* when parsing frames. */ + /* */ + /* limit :: This field is set and used internally by FreeType */ + /* when parsing frames. */ + /* */ + typedef struct FT_StreamRec_ + { + unsigned char* base; + unsigned long size; + unsigned long pos; + + FT_StreamDesc descriptor; + FT_StreamDesc pathname; + FT_Stream_IoFunc read; + FT_Stream_CloseFunc close; + + FT_Memory memory; + unsigned char* cursor; + unsigned char* limit; + + } FT_StreamRec; + + + /* */ + + +FT_END_HEADER + +#endif /* __FTSYSTEM_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/fttrigon.h b/extra_lib/include/freetype/freetype/fttrigon.h new file mode 100644 index 0000000..0462265 --- /dev/null +++ b/extra_lib/include/freetype/freetype/fttrigon.h @@ -0,0 +1,315 @@ +/***************************************************************************/ +/* */ +/* fttrigon.h */ +/* */ +/* FreeType trigonometric functions (specification). */ +/* */ +/* Copyright 2001, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTTRIGON_H__ +#define __FTTRIGON_H__ + +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* @section: */ + /* computations */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* @type: */ + /* FT_Angle */ + /* */ + /* @description: */ + /* This type is used to model angle values in FreeType. Note that */ + /* the angle is a 16.16 fixed float value expressed in degrees. */ + /* */ + typedef FT_Fixed FT_Angle; + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_ANGLE_PI */ + /* */ + /* @description: */ + /* The angle pi expressed in @FT_Angle units. */ + /* */ +#define FT_ANGLE_PI ( 180L << 16 ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_ANGLE_2PI */ + /* */ + /* @description: */ + /* The angle 2*pi expressed in @FT_Angle units. */ + /* */ +#define FT_ANGLE_2PI ( FT_ANGLE_PI * 2 ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_ANGLE_PI2 */ + /* */ + /* @description: */ + /* The angle pi/2 expressed in @FT_Angle units. */ + /* */ +#define FT_ANGLE_PI2 ( FT_ANGLE_PI / 2 ) + + + /*************************************************************************/ + /* */ + /* @macro: */ + /* FT_ANGLE_PI4 */ + /* */ + /* @description: */ + /* The angle pi/4 expressed in @FT_Angle units. */ + /* */ +#define FT_ANGLE_PI4 ( FT_ANGLE_PI / 4 ) + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Sin */ + /* */ + /* @description: */ + /* Return the sinus of a given angle in fixed point format. */ + /* */ + /* @input: */ + /* angle :: The input angle. */ + /* */ + /* @return: */ + /* The sinus value. */ + /* */ + /* @note: */ + /* If you need both the sinus and cosinus for a given angle, use the */ + /* function @FT_Vector_Unit. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_Sin( FT_Angle angle ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Cos */ + /* */ + /* @description: */ + /* Return the cosinus of a given angle in fixed point format. */ + /* */ + /* @input: */ + /* angle :: The input angle. */ + /* */ + /* @return: */ + /* The cosinus value. */ + /* */ + /* @note: */ + /* If you need both the sinus and cosinus for a given angle, use the */ + /* function @FT_Vector_Unit. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_Cos( FT_Angle angle ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Tan */ + /* */ + /* @description: */ + /* Return the tangent of a given angle in fixed point format. */ + /* */ + /* @input: */ + /* angle :: The input angle. */ + /* */ + /* @return: */ + /* The tangent value. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_Tan( FT_Angle angle ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Atan2 */ + /* */ + /* @description: */ + /* Return the arc-tangent corresponding to a given vector (x,y) in */ + /* the 2d plane. */ + /* */ + /* @input: */ + /* x :: The horizontal vector coordinate. */ + /* */ + /* y :: The vertical vector coordinate. */ + /* */ + /* @return: */ + /* The arc-tangent value (i.e. angle). */ + /* */ + FT_EXPORT( FT_Angle ) + FT_Atan2( FT_Fixed x, + FT_Fixed y ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Angle_Diff */ + /* */ + /* @description: */ + /* Return the difference between two angles. The result is always */ + /* constrained to the ]-PI..PI] interval. */ + /* */ + /* @input: */ + /* angle1 :: First angle. */ + /* */ + /* angle2 :: Second angle. */ + /* */ + /* @return: */ + /* Contrainted value of `value2-value1'. */ + /* */ + FT_EXPORT( FT_Angle ) + FT_Angle_Diff( FT_Angle angle1, + FT_Angle angle2 ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Vector_Unit */ + /* */ + /* @description: */ + /* Return the unit vector corresponding to a given angle. After the */ + /* call, the value of `vec.x' will be `sin(angle)', and the value of */ + /* `vec.y' will be `cos(angle)'. */ + /* */ + /* This function is useful to retrieve both the sinus and cosinus of */ + /* a given angle quickly. */ + /* */ + /* @output: */ + /* vec :: The address of target vector. */ + /* */ + /* @input: */ + /* angle :: The address of angle. */ + /* */ + FT_EXPORT( void ) + FT_Vector_Unit( FT_Vector* vec, + FT_Angle angle ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Vector_Rotate */ + /* */ + /* @description: */ + /* Rotate a vector by a given angle. */ + /* */ + /* @inout: */ + /* vec :: The address of target vector. */ + /* */ + /* @input: */ + /* angle :: The address of angle. */ + /* */ + FT_EXPORT( void ) + FT_Vector_Rotate( FT_Vector* vec, + FT_Angle angle ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Vector_Length */ + /* */ + /* @description: */ + /* Return the length of a given vector. */ + /* */ + /* @input: */ + /* vec :: The address of target vector. */ + /* */ + /* @return: */ + /* The vector length, expressed in the same units that the original */ + /* vector coordinates. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_Vector_Length( FT_Vector* vec ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Vector_Polarize */ + /* */ + /* @description: */ + /* Compute both the length and angle of a given vector. */ + /* */ + /* @input: */ + /* vec :: The address of source vector. */ + /* */ + /* @output: */ + /* length :: The vector length. */ + /* angle :: The vector angle. */ + /* */ + FT_EXPORT( void ) + FT_Vector_Polarize( FT_Vector* vec, + FT_Fixed *length, + FT_Angle *angle ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Vector_From_Polar */ + /* */ + /* @description: */ + /* Compute vector coordinates from a length and angle. */ + /* */ + /* @output: */ + /* vec :: The address of source vector. */ + /* */ + /* @input: */ + /* length :: The vector length. */ + /* angle :: The vector angle. */ + /* */ + FT_EXPORT( void ) + FT_Vector_From_Polar( FT_Vector* vec, + FT_Fixed length, + FT_Angle angle ); + + /* */ + + +FT_END_HEADER + +#endif /* __FTTRIGON_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/fttypes.h b/extra_lib/include/freetype/freetype/fttypes.h new file mode 100644 index 0000000..2782f74 --- /dev/null +++ b/extra_lib/include/freetype/freetype/fttypes.h @@ -0,0 +1,558 @@ +/***************************************************************************/ +/* */ +/* fttypes.h */ +/* */ +/* FreeType simple types definitions (specification only). */ +/* */ +/* Copyright 1996-2001 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTTYPES_H__ +#define __FTTYPES_H__ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_SYSTEM_H +#include FT_IMAGE_H + +#include <stddef.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /* <Title> */ + /* Basic Data Types */ + /* */ + /* <Abstract> */ + /* The basic data types defined by the library. */ + /* */ + /* <Description> */ + /* This section contains the basic data types defined by FreeType 2, */ + /* ranging from simple scalar types to bitmap descriptors. More */ + /* font-specific structures are defined in a different section. */ + /* */ + /* <Order> */ + /* FT_Byte */ + /* FT_Char */ + /* FT_Int */ + /* FT_UInt */ + /* FT_Short */ + /* FT_UShort */ + /* FT_Long */ + /* FT_ULong */ + /* FT_Bool */ + /* FT_Offset */ + /* FT_PtrDist */ + /* FT_String */ + /* FT_Error */ + /* FT_Fixed */ + /* FT_Pointer */ + /* FT_Pos */ + /* FT_Vector */ + /* FT_BBox */ + /* FT_Matrix */ + /* FT_FWord */ + /* FT_UFWord */ + /* FT_F2Dot14 */ + /* FT_UnitVector */ + /* FT_F26Dot6 */ + /* */ + /* */ + /* FT_Generic */ + /* FT_Generic_Finalizer */ + /* */ + /* FT_Bitmap */ + /* FT_Pixel_Mode */ + /* FT_Palette_Mode */ + /* FT_Glyph_Format */ + /* FT_IMAGE_TAG */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Bool */ + /* */ + /* <Description> */ + /* A typedef of unsigned char, used for simple booleans. */ + /* */ + typedef unsigned char FT_Bool; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_FWord */ + /* */ + /* <Description> */ + /* A signed 16-bit integer used to store a distance in original font */ + /* units. */ + /* */ + typedef signed short FT_FWord; /* distance in FUnits */ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UFWord */ + /* */ + /* <Description> */ + /* An unsigned 16-bit integer used to store a distance in original */ + /* font units. */ + /* */ + typedef unsigned short FT_UFWord; /* unsigned distance */ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Char */ + /* */ + /* <Description> */ + /* A simple typedef for the _signed_ char type. */ + /* */ + typedef signed char FT_Char; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Byte */ + /* */ + /* <Description> */ + /* A simple typedef for the _unsigned_ char type. */ + /* */ + typedef unsigned char FT_Byte; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_String */ + /* */ + /* <Description> */ + /* A simple typedef for the char type, usually used for strings. */ + /* */ + typedef char FT_String; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Short */ + /* */ + /* <Description> */ + /* A typedef for signed short. */ + /* */ + typedef signed short FT_Short; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UShort */ + /* */ + /* <Description> */ + /* A typedef for unsigned short. */ + /* */ + typedef unsigned short FT_UShort; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Int */ + /* */ + /* <Description> */ + /* A typedef for the int type. */ + /* */ + typedef int FT_Int; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UInt */ + /* */ + /* <Description> */ + /* A typedef for the unsigned int type. */ + /* */ + typedef unsigned int FT_UInt; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Long */ + /* */ + /* <Description> */ + /* A typedef for signed long. */ + /* */ + typedef signed long FT_Long; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_ULong */ + /* */ + /* <Description> */ + /* A typedef for unsigned long. */ + /* */ + typedef unsigned long FT_ULong; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_F2Dot14 */ + /* */ + /* <Description> */ + /* A signed 2.14 fixed float type used for unit vectors. */ + /* */ + typedef signed short FT_F2Dot14; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_F26Dot6 */ + /* */ + /* <Description> */ + /* A signed 26.6 fixed float type used for vectorial pixel */ + /* coordinates. */ + /* */ + typedef signed long FT_F26Dot6; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Fixed */ + /* */ + /* <Description> */ + /* This type is used to store 16.16 fixed float values, like scales */ + /* or matrix coefficients. */ + /* */ + typedef signed long FT_Fixed; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Error */ + /* */ + /* <Description> */ + /* The FreeType error code type. A value of 0 is always interpreted */ + /* as a successful operation. */ + /* */ + typedef int FT_Error; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Pointer */ + /* */ + /* <Description> */ + /* A simple typedef for a typeless pointer. */ + /* */ + typedef void* FT_Pointer; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Offset */ + /* */ + /* <Description> */ + /* This is equivalent to the ANSI C `size_t' type, i.e. the largest */ + /* _unsigned_ integer type used to express a file size or position, */ + /* or a memory block size. */ + /* */ + typedef size_t FT_Offset; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_PtrDist */ + /* */ + /* <Description> */ + /* This is equivalent to the ANSI C `ptrdiff_t' type, i.e. the */ + /* largest _signed_ integer type used to express the distance */ + /* between two pointers. */ + /* */ + typedef size_t FT_PtrDist; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_UnitVector */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2D vector unit vector. Uses */ + /* FT_F2Dot14 types. */ + /* */ + /* <Fields> */ + /* x :: Horizontal coordinate. */ + /* */ + /* y :: Vertical coordinate. */ + /* */ + typedef struct FT_UnitVector_ + { + FT_F2Dot14 x; + FT_F2Dot14 y; + + } FT_UnitVector; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Matrix */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2x2 matrix. Coefficients are */ + /* in 16.16 fixed float format. The computation performed is: */ + /* */ + /* { */ + /* x' = x*xx + y*xy */ + /* y' = x*yx + y*yy */ + /* } */ + /* */ + /* <Fields> */ + /* xx :: Matrix coefficient. */ + /* */ + /* xy :: Matrix coefficient. */ + /* */ + /* yx :: Matrix coefficient. */ + /* */ + /* yy :: Matrix coefficient. */ + /* */ + typedef struct FT_Matrix_ + { + FT_Fixed xx, xy; + FT_Fixed yx, yy; + + } FT_Matrix; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Data */ + /* */ + /* <Description> */ + /* Read-only binary data represented as a pointer and a length. */ + /* */ + /* <Fields> */ + /* pointer :: The data. */ + /* */ + /* length :: The length of the data in bytes. */ + /* */ + typedef struct FT_Data_ + { + const FT_Byte* pointer; + FT_Int length; + + } FT_Data; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Generic_Finalizer */ + /* */ + /* <Description> */ + /* Describes a function used to destroy the `client' data of any */ + /* FreeType object. See the description of the FT_Generic type for */ + /* details of usage. */ + /* */ + /* <Input> */ + /* The address of the FreeType object which is under finalization. */ + /* Its client data is accessed through its `generic' field. */ + /* */ + typedef void (*FT_Generic_Finalizer)(void* object); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Generic */ + /* */ + /* <Description> */ + /* Client applications often need to associate their own data to a */ + /* variety of FreeType core objects. For example, a text layout API */ + /* might want to associate a glyph cache to a given size object. */ + /* */ + /* Most FreeType object contains a `generic' field, of type */ + /* FT_Generic, which usage is left to client applications and font */ + /* servers. */ + /* */ + /* It can be used to store a pointer to client-specific data, as well */ + /* as the address of a `finalizer' function, which will be called by */ + /* FreeType when the object is destroyed (for example, the previous */ + /* client example would put the address of the glyph cache destructor */ + /* in the `finalizer' field). */ + /* */ + /* <Fields> */ + /* data :: A typeless pointer to any client-specified data. This */ + /* field is completely ignored by the FreeType library. */ + /* */ + /* finalizer :: A pointer to a `generic finalizer' function, which */ + /* will be called when the object is destroyed. If this */ + /* field is set to NULL, no code will be called. */ + /* */ + typedef struct FT_Generic_ + { + void* data; + FT_Generic_Finalizer finalizer; + + } FT_Generic; + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_MAKE_TAG */ + /* */ + /* <Description> */ + /* This macro converts four letter tags which are used to label */ + /* TrueType tables into an unsigned long to be used within FreeType. */ + /* */ + /* <Note> */ + /* The produced values *must* be 32bit integers. Don't redefine this */ + /* macro. */ + /* */ +#define FT_MAKE_TAG( _x1, _x2, _x3, _x4 ) \ + ( ( (FT_ULong)_x1 << 24 ) | \ + ( (FT_ULong)_x2 << 16 ) | \ + ( (FT_ULong)_x3 << 8 ) | \ + (FT_ULong)_x4 ) + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* L I S T M A N A G E M E N T */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* list_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_ListNode */ + /* */ + /* <Description> */ + /* Many elements and objects in FreeType are listed through a */ + /* FT_List record (see FT_ListRec). As its name suggests, a */ + /* FT_ListNode is a handle to a single list element. */ + /* */ + typedef struct FT_ListNodeRec_* FT_ListNode; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_List */ + /* */ + /* <Description> */ + /* A handle to a list record (see FT_ListRec). */ + /* */ + typedef struct FT_ListRec_* FT_List; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_ListNodeRec */ + /* */ + /* <Description> */ + /* A structure used to hold a single list element. */ + /* */ + /* <Fields> */ + /* prev :: The previous element in the list. NULL if first. */ + /* */ + /* next :: The next element in the list. NULL if last. */ + /* */ + /* data :: A typeless pointer to the listed object. */ + /* */ + typedef struct FT_ListNodeRec_ + { + FT_ListNode prev; + FT_ListNode next; + void* data; + + } FT_ListNodeRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_ListRec */ + /* */ + /* <Description> */ + /* A structure used to hold a simple doubly-linked list. These are */ + /* used in many parts of FreeType. */ + /* */ + /* <Fields> */ + /* head :: The head (first element) of doubly-linked list. */ + /* */ + /* tail :: The tail (last element) of doubly-linked list. */ + /* */ + typedef struct FT_ListRec_ + { + FT_ListNode head; + FT_ListNode tail; + + } FT_ListRec; + + + /* */ + +#define FT_IS_EMPTY( list ) ( (list).head == 0 ) + + /* return base error code (without module-specific prefix) */ +#define FT_ERROR_BASE( x ) ( (x) & 0xFF ) + + /* return module error code */ +#define FT_ERROR_MODULE( x ) ( (x) & 0xFF00U ) + +#define FT_BOOL( x ) ( (FT_Bool)( x ) ) + +FT_END_HEADER + +#endif /* __FTTYPES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftwinfnt.h b/extra_lib/include/freetype/freetype/ftwinfnt.h new file mode 100644 index 0000000..61234d4 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftwinfnt.h @@ -0,0 +1,135 @@ +/***************************************************************************/ +/* */ +/* ftwinfnt.h */ +/* */ +/* FreeType API for accessing Windows fnt-specific data. */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTWINFNT_H__ +#define __FTWINFNT_H__ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* winfnt_fonts */ + /* */ + /* <Title> */ + /* Window FNT Fonts */ + /* */ + /* <Abstract> */ + /* Windows FNT specific APIs */ + /* */ + /* <Description> */ + /* This section contains the declaration of Windows FNT specific */ + /* functions. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_WinFNT_HeaderRec_ */ + /* */ + /* <Description> */ + /* Windows FNT Header info. */ + /* */ + typedef struct FT_WinFNT_HeaderRec_ + { + FT_UShort version; + FT_ULong file_size; + FT_Byte copyright[60]; + FT_UShort file_type; + FT_UShort nominal_point_size; + FT_UShort vertical_resolution; + FT_UShort horizontal_resolution; + FT_UShort ascent; + FT_UShort internal_leading; + FT_UShort external_leading; + FT_Byte italic; + FT_Byte underline; + FT_Byte strike_out; + FT_UShort weight; + FT_Byte charset; + FT_UShort pixel_width; + FT_UShort pixel_height; + FT_Byte pitch_and_family; + FT_UShort avg_width; + FT_UShort max_width; + FT_Byte first_char; + FT_Byte last_char; + FT_Byte default_char; + FT_Byte break_char; + FT_UShort bytes_per_row; + FT_ULong device_offset; + FT_ULong face_name_offset; + FT_ULong bits_pointer; + FT_ULong bits_offset; + FT_Byte reserved; + FT_ULong flags; + FT_UShort A_space; + FT_UShort B_space; + FT_UShort C_space; + FT_UShort color_table_offset; + FT_ULong reserved1[4]; + + } FT_WinFNT_HeaderRec, *FT_WinFNT_Header; + + + /********************************************************************** + * + * @function: + * FT_Get_WinFNT_Header + * + * @description: + * Retrieve a Windows FNT font info header. + * + * @input: + * face :: A handle to the input face. + * + * @output: + * aheader :: The WinFNT header. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * This function only works with Windows FNT faces, returning an error + * otherwise. + */ + FT_EXPORT( FT_Error ) + FT_Get_WinFNT_Header( FT_Face face, + FT_WinFNT_HeaderRec *aheader ); + + + /* */ + +FT_END_HEADER + +#endif /* __FTWINFNT_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ftxf86.h b/extra_lib/include/freetype/freetype/ftxf86.h new file mode 100644 index 0000000..aa93275 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ftxf86.h @@ -0,0 +1,60 @@ +/***************************************************************************/ +/* */ +/* ftxf86.h */ +/* */ +/* Support functions for X11. */ +/* */ +/* Copyright 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTXF86_H__ +#define __FTXF86_H__ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /* this comment is intentionally disabled for now, to prevent this */ + /* function from appearing in the API Reference. */ + + /*@***********************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_X11_Font_Format */ + /* */ + /* <Description> */ + /* Return a string describing the format of a given face as an X11 */ + /* FONT_PROPERTY. It should only be used by the FreeType 2 font */ + /* backend of the XFree86 font server. */ + /* */ + /* <Input> */ + /* face :: Input face handle. */ + /* */ + /* <Return> */ + /* Font format string. NULL in case of error. */ + /* */ + FT_EXPORT_DEF( const char* ) + FT_Get_X11_Font_Format( FT_Face face ); + + /* */ + +FT_END_HEADER + +#endif /* __FTXF86_H__ */ diff --git a/extra_lib/include/freetype/freetype/internal/autohint.h b/extra_lib/include/freetype/freetype/internal/autohint.h new file mode 100644 index 0000000..22340af --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/autohint.h @@ -0,0 +1,205 @@ +/***************************************************************************/ +/* */ +/* autohint.h */ +/* */ +/* High-level `autohint' module-specific interface (specification). */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* The auto-hinter is used to load and automatically hint glyphs if a */ + /* format-specific hinter isn't available. */ + /* */ + /*************************************************************************/ + + +#ifndef __AUTOHINT_H__ +#define __AUTOHINT_H__ + + + /*************************************************************************/ + /* */ + /* A small technical note regarding automatic hinting in order to */ + /* clarify this module interface. */ + /* */ + /* An automatic hinter might compute two kinds of data for a given face: */ + /* */ + /* - global hints: Usually some metrics that describe global properties */ + /* of the face. It is computed by scanning more or less */ + /* agressively the glyphs in the face, and thus can be */ + /* very slow to compute (even if the size of global */ + /* hints is really small). */ + /* */ + /* - glyph hints: These describe some important features of the glyph */ + /* outline, as well as how to align them. They are */ + /* generally much faster to compute than global hints. */ + /* */ + /* The current FreeType auto-hinter does a pretty good job while */ + /* performing fast computations for both global and glyph hints. */ + /* However, we might be interested in introducing more complex and */ + /* powerful algorithms in the future, like the one described in the John */ + /* D. Hobby paper, which unfortunately requires a lot more horsepower. */ + /* */ + /* Because a sufficiently sophisticated font management system would */ + /* typically implement an LRU cache of opened face objects to reduce */ + /* memory usage, it is a good idea to be able to avoid recomputing */ + /* global hints every time the same face is re-opened. */ + /* */ + /* We thus provide the ability to cache global hints outside of the face */ + /* object, in order to speed up font re-opening time. Of course, this */ + /* feature is purely optional, so most client programs won't even notice */ + /* it. */ + /* */ + /* I initially thought that it would be a good idea to cache the glyph */ + /* hints too. However, my general idea now is that if you really need */ + /* to cache these too, you are simply in need of a new font format, */ + /* where all this information could be stored within the font file and */ + /* decoded on the fly. */ + /* */ + /*************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + typedef struct FT_AutoHinterRec_ *FT_AutoHinter; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_AutoHinter_GlobalGetFunc */ + /* */ + /* <Description> */ + /* Retrieves the global hints computed for a given face object the */ + /* resulting data is dissociated from the face and will survive a */ + /* call to FT_Done_Face(). It must be discarded through the API */ + /* FT_AutoHinter_GlobalDoneFunc(). */ + /* */ + /* <Input> */ + /* hinter :: A handle to the source auto-hinter. */ + /* */ + /* face :: A handle to the source face object. */ + /* */ + /* <Output> */ + /* global_hints :: A typeless pointer to the global hints. */ + /* */ + /* global_len :: The size in bytes of the global hints. */ + /* */ + typedef void + (*FT_AutoHinter_GlobalGetFunc)( FT_AutoHinter hinter, + FT_Face face, + void** global_hints, + long* global_len ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_AutoHinter_GlobalDoneFunc */ + /* */ + /* <Description> */ + /* Discards the global hints retrieved through */ + /* FT_AutoHinter_GlobalGetFunc(). This is the only way these hints */ + /* are freed from memory. */ + /* */ + /* <Input> */ + /* hinter :: A handle to the auto-hinter module. */ + /* */ + /* global :: A pointer to retrieved global hints to discard. */ + /* */ + typedef void + (*FT_AutoHinter_GlobalDoneFunc)( FT_AutoHinter hinter, + void* global ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_AutoHinter_GlobalResetFunc */ + /* */ + /* <Description> */ + /* This function is used to recompute the global metrics in a given */ + /* font. This is useful when global font data changes (e.g. Multiple */ + /* Masters fonts where blend coordinates change). */ + /* */ + /* <Input> */ + /* hinter :: A handle to the source auto-hinter. */ + /* */ + /* face :: A handle to the face. */ + /* */ + typedef void + (*FT_AutoHinter_GlobalResetFunc)( FT_AutoHinter hinter, + FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_AutoHinter_GlyphLoadFunc */ + /* */ + /* <Description> */ + /* This function is used to load, scale, and automatically hint a */ + /* glyph from a given face. */ + /* */ + /* <Input> */ + /* face :: A handle to the face. */ + /* */ + /* glyph_index :: The glyph index. */ + /* */ + /* load_flags :: The load flags. */ + /* */ + /* <Note> */ + /* This function is capable of loading composite glyphs by hinting */ + /* each sub-glyph independently (which improves quality). */ + /* */ + /* It will call the font driver with FT_Load_Glyph(), with */ + /* FT_LOAD_NO_SCALE set. */ + /* */ + typedef FT_Error + (*FT_AutoHinter_GlyphLoadFunc)( FT_AutoHinter hinter, + FT_GlyphSlot slot, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_AutoHinter_ServiceRec */ + /* */ + /* <Description> */ + /* The auto-hinter module's interface. */ + /* */ + typedef struct FT_AutoHinter_ServiceRec_ + { + FT_AutoHinter_GlobalResetFunc reset_face; + FT_AutoHinter_GlobalGetFunc get_global_hints; + FT_AutoHinter_GlobalDoneFunc done_global_hints; + FT_AutoHinter_GlyphLoadFunc load_glyph; + + } FT_AutoHinter_ServiceRec, *FT_AutoHinter_Service; + + +FT_END_HEADER + +#endif /* __AUTOHINT_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/bdftypes.h b/extra_lib/include/freetype/freetype/internal/bdftypes.h new file mode 100644 index 0000000..3a1ec64 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/bdftypes.h @@ -0,0 +1,58 @@ +/* bdftypes.h + + FreeType font driver for bdf fonts + + Copyright (C) 2001, 2002 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef __BDFTYPES_H__ +#define __BDFTYPES_H__ + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_BDF_H + + +FT_BEGIN_HEADER + + + typedef struct BDF_Public_FaceRec_ + { + FT_FaceRec root; + + char* charset_encoding; + char* charset_registry; + + } BDF_Public_FaceRec, *BDF_Public_Face; + + + typedef FT_Error (*BDF_GetPropertyFunc)( FT_Face face, + const char* prop_name, + BDF_PropertyRec *aproperty ); + +FT_END_HEADER + + +#endif /* __BDFTYPES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/cfftypes.h b/extra_lib/include/freetype/freetype/internal/cfftypes.h new file mode 100644 index 0000000..e19e593 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/cfftypes.h @@ -0,0 +1,256 @@ +/***************************************************************************/ +/* */ +/* cfftypes.h */ +/* */ +/* Basic OpenType/CFF type definitions and interface (specification */ +/* only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __CFFTYPES_H__ +#define __CFFTYPES_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CFF_IndexRec */ + /* */ + /* <Description> */ + /* A structure used to model a CFF Index table. */ + /* */ + /* <Fields> */ + /* stream :: The source input stream. */ + /* */ + /* count :: The number of elements in the index. */ + /* */ + /* off_size :: The size in bytes of object offsets in index. */ + /* */ + /* data_offset :: The position of first data byte in the index's */ + /* bytes. */ + /* */ + /* offsets :: A table of element offsets in the index. */ + /* */ + /* bytes :: If the index is loaded in memory, its bytes. */ + /* */ + typedef struct CFF_IndexRec_ + { + FT_Stream stream; + FT_UInt count; + FT_Byte off_size; + FT_ULong data_offset; + + FT_ULong* offsets; + FT_Byte* bytes; + + } CFF_IndexRec, *CFF_Index; + + + typedef struct CFF_EncodingRec_ + { + FT_UInt format; + FT_ULong offset; + + FT_UInt count; + FT_UShort sids [256]; /* avoid dynamic allocations */ + FT_UShort codes[256]; + + } CFF_EncodingRec, *CFF_Encoding; + + + typedef struct CFF_CharsetRec_ + { + + FT_UInt format; + FT_ULong offset; + + FT_UShort* sids; + + } CFF_CharsetRec, *CFF_Charset; + + + typedef struct CFF_FontRecDictRec_ + { + FT_UInt version; + FT_UInt notice; + FT_UInt copyright; + FT_UInt full_name; + FT_UInt family_name; + FT_UInt weight; + FT_Bool is_fixed_pitch; + FT_Fixed italic_angle; + FT_Fixed underline_position; + FT_Fixed underline_thickness; + FT_Int paint_type; + FT_Int charstring_type; + FT_Matrix font_matrix; + FT_UShort units_per_em; + FT_Vector font_offset; + FT_ULong unique_id; + FT_BBox font_bbox; + FT_Pos stroke_width; + FT_ULong charset_offset; + FT_ULong encoding_offset; + FT_ULong charstrings_offset; + FT_ULong private_offset; + FT_ULong private_size; + FT_Long synthetic_base; + FT_UInt embedded_postscript; + FT_UInt base_font_name; + FT_UInt postscript; + + /* these should only be used for the top-level font dictionary */ + FT_UInt cid_registry; + FT_UInt cid_ordering; + FT_ULong cid_supplement; + + FT_Long cid_font_version; + FT_Long cid_font_revision; + FT_Long cid_font_type; + FT_Long cid_count; + FT_ULong cid_uid_base; + FT_ULong cid_fd_array_offset; + FT_ULong cid_fd_select_offset; + FT_UInt cid_font_name; + + } CFF_FontRecDictRec, *CFF_FontRecDict; + + + typedef struct CFF_PrivateRec_ + { + FT_Byte num_blue_values; + FT_Byte num_other_blues; + FT_Byte num_family_blues; + FT_Byte num_family_other_blues; + + FT_Pos blue_values[14]; + FT_Pos other_blues[10]; + FT_Pos family_blues[14]; + FT_Pos family_other_blues[10]; + + FT_Fixed blue_scale; + FT_Pos blue_shift; + FT_Pos blue_fuzz; + FT_Pos standard_width; + FT_Pos standard_height; + + FT_Byte num_snap_widths; + FT_Byte num_snap_heights; + FT_Pos snap_widths[13]; + FT_Pos snap_heights[13]; + FT_Bool force_bold; + FT_Fixed force_bold_threshold; + FT_Int lenIV; + FT_Int language_group; + FT_Fixed expansion_factor; + FT_Long initial_random_seed; + FT_ULong local_subrs_offset; + FT_Pos default_width; + FT_Pos nominal_width; + + } CFF_PrivateRec, *CFF_Private; + + + typedef struct CFF_FDSelectRec_ + { + FT_Byte format; + FT_UInt range_count; + + /* that's the table, taken from the file `as is' */ + FT_Byte* data; + FT_UInt data_size; + + /* small cache for format 3 only */ + FT_UInt cache_first; + FT_UInt cache_count; + FT_Byte cache_fd; + + } CFF_FDSelectRec, *CFF_FDSelect; + + + /* A SubFont packs a font dict and a private dict together. They are */ + /* needed to support CID-keyed CFF fonts. */ + typedef struct CFF_SubFontRec_ + { + CFF_FontRecDictRec font_dict; + CFF_PrivateRec private_dict; + + CFF_IndexRec local_subrs_index; + FT_UInt num_local_subrs; + FT_Byte** local_subrs; + + } CFF_SubFontRec, *CFF_SubFont; + + + /* maximum number of sub-fonts in a CID-keyed file */ +#define CFF_MAX_CID_FONTS 16 + + + typedef struct CFF_FontRec_ + { + FT_Stream stream; + FT_Memory memory; + FT_UInt num_faces; + FT_UInt num_glyphs; + + FT_Byte version_major; + FT_Byte version_minor; + FT_Byte header_size; + FT_Byte absolute_offsize; + + + CFF_IndexRec name_index; + CFF_IndexRec top_dict_index; + CFF_IndexRec string_index; + CFF_IndexRec global_subrs_index; + + CFF_EncodingRec encoding; + CFF_CharsetRec charset; + + CFF_IndexRec charstrings_index; + CFF_IndexRec font_dict_index; + CFF_IndexRec private_index; + CFF_IndexRec local_subrs_index; + + FT_String* font_name; + FT_UInt num_global_subrs; + FT_Byte** global_subrs; + + CFF_SubFontRec top_font; + FT_UInt num_subfonts; + CFF_SubFont subfonts[CFF_MAX_CID_FONTS]; + + CFF_FDSelectRec fd_select; + + /* interface to PostScript hinter */ + void* pshinter; + + /* interface to Postscript Names service */ + void* psnames; + + } CFF_FontRec, *CFF_Font; + + +FT_END_HEADER + +#endif /* __CFFTYPES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/fnttypes.h b/extra_lib/include/freetype/freetype/internal/fnttypes.h new file mode 100644 index 0000000..3efa860 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/fnttypes.h @@ -0,0 +1,104 @@ +/***************************************************************************/ +/* */ +/* fnttypes.h */ +/* */ +/* Basic Windows FNT/FON type definitions and interface (specification */ +/* only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FNTTYPES_H__ +#define __FNTTYPES_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_WINFONTS_H + + +FT_BEGIN_HEADER + + + typedef struct WinMZ_HeaderRec_ + { + FT_UShort magic; + /* skipped content */ + FT_UShort lfanew; + + } WinMZ_HeaderRec; + + + typedef struct WinNE_HeaderRec_ + { + FT_UShort magic; + /* skipped content */ + FT_UShort resource_tab_offset; + FT_UShort rname_tab_offset; + + } WinNE_HeaderRec; + + + typedef struct WinNameInfoRec_ + { + FT_UShort offset; + FT_UShort length; + FT_UShort flags; + FT_UShort id; + FT_UShort handle; + FT_UShort usage; + + } WinNameInfoRec; + + + typedef struct WinResourceInfoRec_ + { + FT_UShort type_id; + FT_UShort count; + + } WinResourceInfoRec; + + +#define WINFNT_MZ_MAGIC 0x5A4D +#define WINFNT_NE_MAGIC 0x454E + + + typedef struct FNT_FontRec_ + { + FT_ULong offset; + FT_Int size_shift; + + FT_WinFNT_HeaderRec header; + + FT_Byte* fnt_frame; + FT_ULong fnt_size; + + } FNT_FontRec, *FNT_Font; + + + typedef struct FNT_FaceRec_ + { + FT_FaceRec root; + FNT_Font font; + + FT_CharMap charmap_handle; + FT_CharMapRec charmap; /* a single charmap per face */ + + } FNT_FaceRec, *FNT_Face; + + +FT_END_HEADER + +#endif /* __FNTTYPES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/ftcalc.h b/extra_lib/include/freetype/freetype/internal/ftcalc.h new file mode 100644 index 0000000..af1f640 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftcalc.h @@ -0,0 +1,77 @@ +/***************************************************************************/ +/* */ +/* ftcalc.h */ +/* */ +/* Arithmetic computations (specification). */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTCALC_H__ +#define __FTCALC_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + FT_EXPORT( FT_Int32 ) FT_SqrtFixed( FT_Int32 x ); + + +#define SQRT_32( x ) FT_Sqrt32( x ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Sqrt32 */ + /* */ + /* <Description> */ + /* Computes the square root of an Int32 integer (which will be */ + /* handled as an unsigned long value). */ + /* */ + /* <Input> */ + /* x :: The value to compute the root for. */ + /* */ + /* <Return> */ + /* The result of `sqrt(x)'. */ + /* */ + FT_EXPORT( FT_Int32 ) + FT_Sqrt32( FT_Int32 x ); + + + /*************************************************************************/ + /* */ + /* FT_MulDiv() and FT_MulFix() are declared in freetype.h. */ + /* */ + /*************************************************************************/ + + +#define INT_TO_F26DOT6( x ) ( (FT_Long)(x) << 6 ) +#define INT_TO_F2DOT14( x ) ( (FT_Long)(x) << 14 ) +#define INT_TO_FIXED( x ) ( (FT_Long)(x) << 16 ) +#define F2DOT14_TO_FIXED( x ) ( (FT_Long)(x) << 2 ) +#define FLOAT_TO_FIXED( x ) ( (FT_Long)( x * 65536.0 ) ) + +#define ROUND_F26DOT6( x ) ( x >= 0 ? ( ( (x) + 32 ) & -64 ) \ + : ( -( ( 32 - (x) ) & -64 ) ) ) + + +FT_END_HEADER + +#endif /* __FTCALC_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/ftcore.h b/extra_lib/include/freetype/freetype/internal/ftcore.h new file mode 100644 index 0000000..3c3c94f --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftcore.h @@ -0,0 +1,185 @@ +#ifndef __FT_CORE_H__ +#define __FT_CORE_H__ + +#include <ft2build.h> +#include FT_TYPES_H +#include FT_SYSTEM_MEMORY_H + +FT_BEGIN_HEADER + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** C L E A N U P S T A C K *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + + /************************************************************************ + * + * @functype: FT_CleanupFunc + * + * @description: + * a function used to cleanup a given item on the cleanup stack + * + * @input: + * item :: target item pointer + * item_data :: optional argument to cleanup routine + */ + typedef void (*FT_CleanupFunc)( FT_Pointer item, + FT_Pointer item_data ); + + + + /************************************************************************ + * + * @type: FT_XHandler + * + * @description: + * handle to an exception-handler structure for the FreeType + * exception sub-system + * + * @note: + * exception handlers are allocated on the stack within a + * @FT_XTRY macro. Do not try to access them directly. + */ + typedef struct FT_XHandlerRec_* FT_XHandler; + + +/* the size of a cleanup chunk in bytes is FT_CLEANUP_CHUNK_SIZE*12 + 4 */ +/* this must be a small power of 2 whenever possible.. */ +/* */ +/* with a value of 5, we have a byte size of 64 bytes per chunk.. */ +/* */ +#define FT_CLEANUP_CHUNK_SIZE 5 + + + + typedef struct FT_CleanupItemRec_ + { + FT_Pointer item; + FT_CleanupFunc item_func; + FT_Pointer item_data; + + } FT_CleanupItemRec; + + + typedef struct FT_CleanupChunkRec_* FT_CleanupChunk; + + typedef struct FT_CleanupChunkRec_ + { + FT_CleanupChunk link; + FT_CleanupItemRec items[ FT_CLEANUP_CHUNK_SIZE ]; + + } FT_CleanupChunkRec; + + + typedef struct FT_CleanupStackRec_ + { + FT_CleanupItem top; + FT_CleanupItem limit; + FT_CleanupChunk chunk; + FT_CleanupChunkRec chunk_0; /* avoids stupid dynamic allocation */ + FT_Memory memory; + + } FT_CleanupStackRec, *FT_CleanupStack; + + + FT_BASE( void ) + ft_cleanup_stack_push( FT_CleanupStack stack, + FT_Pointer item, + FT_CleanupFunc item_func, + FT_Pointer item_data ); + + FT_BASE( void ) + ft_cleanup_stack_pop( FT_CleanupStack stack, + FT_Int destroy ); + + FT_BASE( FT_CleanupItem ) + ft_cleanup_stack_peek( FT_CleanupStack stack ); + + FT_BASE( void ) + ft_cleanup_throw( FT_CleanupStack stack, + FT_Error error ); + + + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** M E M O R Y M A N A G E R *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + typedef struct FT_MemoryRec_ + { + FT_Memory_AllocFunc mem_alloc; /* shortcut to funcs->mem_alloc */ + FT_Memory_FreeFunc mem_free; /* shortcut to funcs->mem_free */ + FT_Pointer mem_data; + const FT_Memory_Funcs mem_funcs; + + FT_CleanupStackRec cleanup_stack; + FT_Pointer meta_class; + + } FT_MemoryRec; + + +#define FT_MEMORY(x) ((FT_Memory)(x)) +#define FT_MEMORY__ALLOC(x) FT_MEMORY(x)->mem_alloc +#define FT_MEMORY__FREE(x) FT_MEMORY(x)->mem_free +#define FT_MEMORY__REALLOC(x) FT_MEMORY(x)->mem_funcs->mem_realloc +#define FT_MEMORY__CLEANUP(x) (&FT_MEMORY(x)->cleanup_stack) +#define FT_MEMORY__META_CLASS(x) ((FT_MetaClass)(FT_MEMORY(x)->meta_class)) + + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** E X C E P T I O N H A N D L I N G *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + + /************************************************************************ + * + * @struct: FT_XHandlerRec + * + * @description: + * exception handler structure + * + * @fields: + * previous :: previous handler in chain. + * jum_buffer :: processor state used by setjmp/longjmp to implement + * exception control transfer + * error :: exception error code + * mark :: top of cleanup stack when @FT_XTRY is used + */ + typedef struct FT_XHandlerRec_ + { + FT_XHandler previous; + ft_jmp_buf jump_buffer; + volatile FT_Error error; + FT_Pointer mark; + + } FT_XHandlerRec; + + FT_BASE( void ) + ft_xhandler_enter( FT_XHandler xhandler, + FT_Memory memory ); + + FT_BASE( void ) + ft_xhandler_exit( FT_XHandler xhandler ); + + + + + + + + +FT_END_HEADER + +#endif /* __FT_CORE_H__ */ diff --git a/extra_lib/include/freetype/freetype/internal/ftdebug.h b/extra_lib/include/freetype/freetype/internal/ftdebug.h new file mode 100644 index 0000000..129c91f --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftdebug.h @@ -0,0 +1,196 @@ +/***************************************************************************/ +/* */ +/* ftdebug.h */ +/* */ +/* Debugging and logging component (specification). */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/* */ +/* IMPORTANT: A description of FreeType's debugging support can be */ +/* found in "docs/DEBUG.TXT". Read it if you need to use or */ +/* understand this code. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTDEBUG_H__ +#define __FTDEBUG_H__ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H + + +FT_BEGIN_HEADER + + + /* force the definition of FT_DEBUG_LEVEL_ERROR if FT_DEBUG_LEVEL_TRACE */ + /* is already defined; this simplifies the following #ifdefs */ + /* */ +#ifdef FT_DEBUG_LEVEL_TRACE +#undef FT_DEBUG_LEVEL_ERROR +#define FT_DEBUG_LEVEL_ERROR +#endif + + + /*************************************************************************/ + /* */ + /* Define the trace enums as well as the trace levels array when they */ + /* are needed. */ + /* */ + /*************************************************************************/ + +#ifdef FT_DEBUG_LEVEL_TRACE + +#define FT_TRACE_DEF( x ) trace_ ## x , + + /* defining the enumeration */ + typedef enum + { +#include FT_INTERNAL_TRACE_H + trace_count + + } FT_Trace; + + + /* defining the array of trace levels, provided by `src/base/ftdebug.c' */ + extern int ft_trace_levels[trace_count]; + +#undef FT_TRACE_DEF + +#endif /* FT_DEBUG_LEVEL_TRACE */ + + + /*************************************************************************/ + /* */ + /* Define the FT_TRACE macro */ + /* */ + /* IMPORTANT! */ + /* */ + /* Each component must define the macro FT_COMPONENT to a valid FT_Trace */ + /* value before using any TRACE macro. */ + /* */ + /*************************************************************************/ + +#ifdef FT_DEBUG_LEVEL_TRACE + +#define FT_TRACE( level, varformat ) \ + do \ + { \ + if ( ft_trace_levels[FT_COMPONENT] >= level ) \ + FT_Message varformat; \ + } while ( 0 ) + +#else /* !FT_DEBUG_LEVEL_TRACE */ + +#define FT_TRACE( level, varformat ) do ; while ( 0 ) /* nothing */ + +#endif /* !FT_DEBUG_LEVEL_TRACE */ + + + /*************************************************************************/ + /* */ + /* You need two opening resp. closing parentheses! */ + /* */ + /* Example: FT_TRACE0(( "Value is %i", foo )) */ + /* */ + /*************************************************************************/ + +#define FT_TRACE0( varformat ) FT_TRACE( 0, varformat ) +#define FT_TRACE1( varformat ) FT_TRACE( 1, varformat ) +#define FT_TRACE2( varformat ) FT_TRACE( 2, varformat ) +#define FT_TRACE3( varformat ) FT_TRACE( 3, varformat ) +#define FT_TRACE4( varformat ) FT_TRACE( 4, varformat ) +#define FT_TRACE5( varformat ) FT_TRACE( 5, varformat ) +#define FT_TRACE6( varformat ) FT_TRACE( 6, varformat ) +#define FT_TRACE7( varformat ) FT_TRACE( 7, varformat ) + + + /*************************************************************************/ + /* */ + /* Define the FT_ERROR macro */ + /* */ + /*************************************************************************/ + +#ifdef FT_DEBUG_LEVEL_ERROR + +#define FT_ERROR( varformat ) FT_Message varformat + +#else /* !FT_DEBUG_LEVEL_ERROR */ + +#define FT_ERROR( varformat ) do ; while ( 0 ) /* nothing */ + +#endif /* !FT_DEBUG_LEVEL_ERROR */ + + + /*************************************************************************/ + /* */ + /* Define the FT_ASSERT macro */ + /* */ + /*************************************************************************/ + +#ifdef FT_DEBUG_LEVEL_ERROR + +#define FT_ASSERT( condition ) \ + do \ + { \ + if ( !( condition ) ) \ + FT_Panic( "assertion failed on line %d of file %s\n", \ + __LINE__, __FILE__ ); \ + } while ( 0 ) + +#else /* !FT_DEBUG_LEVEL_ERROR */ + +#define FT_ASSERT( condition ) do ; while ( 0 ) + +#endif /* !FT_DEBUG_LEVEL_ERROR */ + + + /*************************************************************************/ + /* */ + /* Define 'FT_Message' and 'FT_Panic' when needed */ + /* */ + /*************************************************************************/ + +#ifdef FT_DEBUG_LEVEL_ERROR + +#include "stdio.h" /* for vprintf() */ + + /* print a message */ + FT_EXPORT( void ) + FT_Message( const char* fmt, ... ); + + /* print a message and exit */ + FT_EXPORT( void ) + FT_Panic( const char* fmt, ... ); + +#endif /* FT_DEBUG_LEVEL_ERROR */ + + + FT_BASE( void ) + ft_debug_init( void ); + + +#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */ + + /* we disable the warning `conditional expression is constant' here */ + /* in order to compile cleanly with the maximum level of warnings */ +#pragma warning( disable : 4127 ) + +#endif /* _MSC_VER */ + + +FT_END_HEADER + +#endif /* __FTDEBUG_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/ftdriver.h b/extra_lib/include/freetype/freetype/internal/ftdriver.h new file mode 100644 index 0000000..1f1c55a --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftdriver.h @@ -0,0 +1,202 @@ +/***************************************************************************/ +/* */ +/* ftdriver.h */ +/* */ +/* FreeType font driver interface (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTDRIVER_H__ +#define __FTDRIVER_H__ + + +#include <ft2build.h> +#include FT_MODULE_H + + +FT_BEGIN_HEADER + + + typedef FT_Error + (*FT_Face_InitFunc)( FT_Stream stream, + FT_Face face, + FT_Int typeface_index, + FT_Int num_params, + FT_Parameter* parameters ); + + typedef void + (*FT_Face_DoneFunc)( FT_Face face ); + + + typedef FT_Error + (*FT_Size_InitFunc)( FT_Size size ); + + typedef void + (*FT_Size_DoneFunc)( FT_Size size ); + + + typedef FT_Error + (*FT_Slot_InitFunc)( FT_GlyphSlot slot ); + + typedef void + (*FT_Slot_DoneFunc)( FT_GlyphSlot slot ); + + + typedef FT_Error + (*FT_Size_ResetPointsFunc)( FT_Size size, + FT_F26Dot6 char_width, + FT_F26Dot6 char_height, + FT_UInt horz_resolution, + FT_UInt vert_resolution ); + + typedef FT_Error + (*FT_Size_ResetPixelsFunc)( FT_Size size, + FT_UInt pixel_width, + FT_UInt pixel_height ); + + typedef FT_Error + (*FT_Slot_LoadFunc)( FT_GlyphSlot slot, + FT_Size size, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + + typedef FT_UInt + (*FT_CharMap_CharIndexFunc)( FT_CharMap charmap, + FT_Long charcode ); + + typedef FT_Long + (*FT_CharMap_CharNextFunc)( FT_CharMap charmap, + FT_Long charcode ); + + typedef FT_Error + (*FT_Face_GetKerningFunc)( FT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_Vector* kerning ); + + + typedef FT_Error + (*FT_Face_AttachFunc)( FT_Face face, + FT_Stream stream ); + + + typedef FT_Error + (*FT_Face_GetAdvancesFunc)( FT_Face face, + FT_UInt first, + FT_UInt count, + FT_Bool vertical, + FT_UShort* advances ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Driver_ClassRec */ + /* */ + /* <Description> */ + /* The font driver class. This structure mostly contains pointers to */ + /* driver methods. */ + /* */ + /* <Fields> */ + /* root :: The parent module. */ + /* */ + /* face_object_size :: The size of a face object in bytes. */ + /* */ + /* size_object_size :: The size of a size object in bytes. */ + /* */ + /* slot_object_size :: The size of a glyph object in bytes. */ + /* */ + /* init_face :: The format-specific face constructor. */ + /* */ + /* done_face :: The format-specific face destructor. */ + /* */ + /* init_size :: The format-specific size constructor. */ + /* */ + /* done_size :: The format-specific size destructor. */ + /* */ + /* init_slot :: The format-specific slot constructor. */ + /* */ + /* done_slot :: The format-specific slot destructor. */ + /* */ + /* set_char_sizes :: A handle to a function used to set the new */ + /* character size in points + resolution. Can be */ + /* set to 0 to indicate default behaviour. */ + /* */ + /* set_pixel_sizes :: A handle to a function used to set the new */ + /* character size in pixels. Can be set to 0 to */ + /* indicate default behaviour. */ + /* */ + /* load_glyph :: A function handle to load a given glyph image */ + /* in a slot. This field is mandatory! */ + /* */ + /* get_char_index :: A function handle to return the glyph index of */ + /* a given character for a given charmap. This */ + /* field is mandatory! */ + /* */ + /* get_kerning :: A function handle to return the unscaled */ + /* kerning for a given pair of glyphs. Can be */ + /* set to 0 if the format doesn't support */ + /* kerning. */ + /* */ + /* attach_file :: This function handle is used to read */ + /* additional data for a face from another */ + /* file/stream. For example, this can be used to */ + /* add data from AFM or PFM files on a Type 1 */ + /* face, or a CIDMap on a CID-keyed face. */ + /* */ + /* get_advances :: A function handle used to return the advances */ + /* of 'count' glyphs, starting at `index'. the */ + /* `vertical' flags must be set when vertical */ + /* advances are queried. The advances buffer is */ + /* caller-allocated. */ + /* */ + /* <Note> */ + /* Most function pointers, with the exception of `load_glyph' and */ + /* `get_char_index' can be set to 0 to indicate a default behaviour. */ + /* */ + typedef struct FT_Driver_ClassRec_ + { + FT_Module_Class root; + + FT_Long face_object_size; + FT_Long size_object_size; + FT_Long slot_object_size; + + FT_Face_InitFunc init_face; + FT_Face_DoneFunc done_face; + + FT_Size_InitFunc init_size; + FT_Size_DoneFunc done_size; + + FT_Slot_InitFunc init_slot; + FT_Slot_DoneFunc done_slot; + + FT_Size_ResetPointsFunc set_char_sizes; + FT_Size_ResetPixelsFunc set_pixel_sizes; + + FT_Slot_LoadFunc load_glyph; + + FT_Face_GetKerningFunc get_kerning; + FT_Face_AttachFunc attach_file; + FT_Face_GetAdvancesFunc get_advances; + + } FT_Driver_ClassRec, *FT_Driver_Class; + + +FT_END_HEADER + +#endif /* __FTDRIVER_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/ftexcept.h b/extra_lib/include/freetype/freetype/internal/ftexcept.h new file mode 100644 index 0000000..5d20765 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftexcept.h @@ -0,0 +1,79 @@ +#ifndef __FT_EXCEPT_H__ +#define __FT_EXCEPT_H__ + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H + +FT_BEGIN_HEADER + + + + /* I can't find a better place for this for now */ + + +/* the size of a cleanup chunk in bytes is FT_CLEANUP_CHUNK_SIZE*12 + 4 */ +/* this must be a small power of 2 whenever possible.. */ +/* */ +/* with a value of 5, we have a byte size of 64 bytes per chunk.. */ +/* */ +#define FT_CLEANUP_CHUNK_SIZE 5 + + + + typedef struct FT_CleanupItemRec_ + { + FT_Pointer item; + FT_CleanupFunc item_func; + FT_Pointer item_data; + + } FT_CleanupItemRec; + + typedef struct FT_CleanupChunkRec_* FT_CleanupChunk; + + typedef struct FT_CleanupChunkRec_ + { + FT_CleanupChunk link; + FT_CleanupItemRec items[ FT_CLEANUP_CHUNK_SIZE ]; + + } FT_CleanupChunkRec; + + + typedef struct FT_CleanupStackRec_ + { + FT_CleanupItem top; + FT_CleanupItem limit; + FT_CleanupChunk chunk; + FT_CleanupChunkRec chunk_0; /* avoids stupid dynamic allocation */ + FT_Memory memory; + + } FT_CleanupStackRec, *FT_CleanupStack; + + + FT_BASE( void ) + ft_cleanup_stack_push( FT_CleanupStack stack, + FT_Pointer item, + FT_CleanupFunc item_func, + FT_Pointer item_data ); + + FT_BASE( void ) + ft_cleanup_stack_pop( FT_CleanupStack stack, + FT_Int destroy ); + + FT_BASE( FT_CleanupItem ) + ft_cleanup_stack_peek( FT_CleanupStack stack ); + + FT_BASE( void ) + ft_xhandler_enter( FT_XHandler xhandler, + FT_Memory memory ); + + FT_BASE( void ) + ft_xhandler_exit( FT_XHandler xhandler ); + + + FT_BASE( void ) + ft_cleanup_throw( FT_CleanupStack stack, + FT_Error error ); + +FT_END_HEADER + +#endif /* __FT_EXCEPT_H__ */ diff --git a/extra_lib/include/freetype/freetype/internal/ftgloadr.h b/extra_lib/include/freetype/freetype/internal/ftgloadr.h new file mode 100644 index 0000000..284f4e3 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftgloadr.h @@ -0,0 +1,147 @@ +/***************************************************************************/ +/* */ +/* ftgloadr.h */ +/* */ +/* The FreeType glyph loader (specification). */ +/* */ +/* Copyright 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTGLOADR_H__ +#define __FTGLOADR_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_GlyphLoader */ + /* */ + /* <Description> */ + /* The glyph loader is an internal object used to load several glyphs */ + /* together (for example, in the case of composites). */ + /* */ + /* <Note> */ + /* The glyph loader implementation is not part of the high-level API, */ + /* hence the forward structure declaration. */ + /* */ + typedef struct FT_GlyphLoaderRec_* FT_GlyphLoader ; + + +#define FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS 1 +#define FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES 2 +#define FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID 4 +#define FT_SUBGLYPH_FLAG_SCALE 8 +#define FT_SUBGLYPH_FLAG_XY_SCALE 0x40 +#define FT_SUBGLYPH_FLAG_2X2 0x80 +#define FT_SUBGLYPH_FLAG_USE_MY_METRICS 0x200 + + + typedef struct FT_SubGlyphRec_ + { + FT_Int index; + FT_UShort flags; + FT_Int arg1; + FT_Int arg2; + FT_Matrix transform; + + } FT_SubGlyphRec; + + + typedef struct FT_GlyphLoadRec_ + { + FT_Outline outline; /* outline */ + FT_Vector* extra_points; /* extra points table */ + FT_UInt num_subglyphs; /* number of subglyphs */ + FT_SubGlyph subglyphs; /* subglyphs */ + + } FT_GlyphLoadRec, *FT_GlyphLoad; + + + typedef struct FT_GlyphLoaderRec_ + { + FT_Memory memory; + FT_UInt max_points; + FT_UInt max_contours; + FT_UInt max_subglyphs; + FT_Bool use_extra; + + FT_GlyphLoadRec base; + FT_GlyphLoadRec current; + + void* other; /* for possible future extension? */ + + } FT_GlyphLoaderRec; + + + /* create new empty glyph loader */ + FT_BASE( FT_Error ) + FT_GlyphLoader_New( FT_Memory memory, + FT_GlyphLoader *aloader ); + + /* add an extra points table to a glyph loader */ + FT_BASE( FT_Error ) + FT_GlyphLoader_CreateExtra( FT_GlyphLoader loader ); + + /* destroy a glyph loader */ + FT_BASE( void ) + FT_GlyphLoader_Done( FT_GlyphLoader loader ); + + /* reset a glyph loader (frees everything int it) */ + FT_BASE( void ) + FT_GlyphLoader_Reset( FT_GlyphLoader loader ); + + /* rewind a glyph loader */ + FT_BASE( void ) + FT_GlyphLoader_Rewind( FT_GlyphLoader loader ); + + /* check that there is enough room to add 'n_points' and 'n_contours' */ + /* to the glyph loader */ + FT_BASE( FT_Error ) + FT_GlyphLoader_CheckPoints( FT_GlyphLoader loader, + FT_UInt n_points, + FT_UInt n_contours ); + + /* check that there is enough room to add 'n_subs' sub-glyphs to */ + /* a glyph loader */ + FT_BASE( FT_Error ) + FT_GlyphLoader_CheckSubGlyphs( FT_GlyphLoader loader, + FT_UInt n_subs ); + + /* prepare a glyph loader, i.e. empty the current glyph */ + FT_BASE( void ) + FT_GlyphLoader_Prepare( FT_GlyphLoader loader ); + + /* add the current glyph to the base glyph */ + FT_BASE( void ) + FT_GlyphLoader_Add( FT_GlyphLoader loader ); + + /* copy points from one glyph loader to another */ + FT_BASE( FT_Error ) + FT_GlyphLoader_CopyPoints( FT_GlyphLoader target, + FT_GlyphLoader source ); + + /* */ + + +FT_END_HEADER + +#endif /* __FTGLOADR_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/fthash.h b/extra_lib/include/freetype/freetype/internal/fthash.h new file mode 100644 index 0000000..b95b6c9 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/fthash.h @@ -0,0 +1,502 @@ +/****************************************************************** + * + * fthash.h - fast dynamic hash tables + * + * Copyright 2002 by + * David Turner, Robert Wilhelm, and Werner Lemberg + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * + * This header is used to define dynamic hash tables as described + * by the article "Main-Memory Linear Hashing - Some Enhancements + * of Larson's Algorithm" by Mikael Petterson. + * + * Basically, linear hashing prevents big "stalls" during + * resizes of the buckets array by only splitting one bucket + * at a time. This ensures excellent response time even when + * the table is frequently resized.. + * + * + * Note that the use of the FT_Hash type is rather unusual in order + * to be as generic and efficient as possible. See the comments in the + * following definitions for more details. + */ + +#ifndef __FT_HASH_H__ +#define __FT_HASH_H__ + +#include <ft2build.h> +#include FT_TYPES_H + +FT_BEGIN_HEADER + + /*********************************************************** + * + * @type: FT_Hash + * + * @description: + * handle to a @FT_HashRec structure used to model a + * dynamic hash table + */ + typedef struct FT_HashRec_* FT_Hash; + + + /*********************************************************** + * + * @type: FT_HashNode + * + * @description: + * handle to a @FT_HashNodeRec structure used to model a + * single node of a hash table + */ + typedef struct FT_HashNodeRec_* FT_HashNode; + + + /*********************************************************** + * + * @type: FT_HashLookup + * + * @description: + * handle to a @FT_HashNode pointer. This is returned by + * the @ft_hash_lookup function and can later be used by + * @ft_hash_add or @ft_hash_remove + */ + typedef FT_HashNode* FT_HashLookup; + + + /*********************************************************** + * + * @type: FT_Hash_EqualFunc + * + * @description: + * a function used to compare two nodes of the hash table + * + * @input: + * node1 :: handle to first node + * node2 :: handle to second node + * + * @return: + * 1 iff the 'keys' in 'node1' and 'node2' are identical. + * 0 otherwise. + */ + typedef FT_Int (*FT_Hash_EqualFunc)( FT_HashNode node1, + FT_HashNode node2 ); + + + /*********************************************************** + * + * @struct: FT_HashRec + * + * @description: + * a structure used to model a dynamic hash table. + * + * @fields: + * memory :: memory manager used to allocate + * the buckets array and the hash nodes + * + * buckets :: array of hash buckets + * + * node_size :: size of node in bytes + * node_compare :: a function used to compare two nodes + * node_hash :: a function used to compute the hash + * value of a given node + * p :: + * mask :: + * slack :: + * + * @note: + * 'p', 'mask' and 'slack' are control values managed by + * the hash table. Do not try to interpret them directly. + * + * You can grab the hash table size by calling + * '@ft_hash_get_size'. + */ + typedef struct FT_HashRec_ + { + FT_HashNode* buckets; + FT_UInt p; + FT_UInt mask; /* really maxp-1 */ + FT_Long slack; + FT_Hash_EqualFunc node_equal; + FT_Memory memory; + + } FT_HashRec; + + + /*********************************************************** + * + * @struct: FT_HashNodeRec + * + * @description: + * a structure used to model the root fields of a dynamic + * hash table node. + * + * it's up to client applications to "sub-class" this + * structure to add relevant (key,value) definitions + * + * @fields: + * link :: pointer to next node in bucket's collision list + * hash :: 32-bit hash value for this node + * + * @note: + * it's up to client applications to "sub-class" this structure + * to add relevant (key,value) type definitions. For example, + * if we want to build a "string -> int" mapping, we could use + * something like: + * + * { + * typedef struct MyNodeRec_ + * { + * FT_HashNodeRec hnode; + * const char* key; + * int value; + * + * } MyNodeRec, *MyNode; + * } + * + */ + typedef struct FT_HashNodeRec_ + { + FT_HashNode link; + FT_UInt32 hash; + + } FT_HashNodeRec; + + + /**************************************************************** + * + * @function: ft_hash_init + * + * @description: + * initialize a dynamic hash table + * + * @input: + * table :: handle to target hash table structure + * node_equal :: node comparison function + * memory :: memory manager handle used to allocate the + * buckets array within the hash table + * + * @return: + * error code. 0 means success + * + * @note: + * the node comparison function should only compare node _keys_ + * and ignore values !! with good hashing computation (which the + * user must perform itself), the comparison function should be + * pretty seldom called. + * + * here is a simple example: + * + * { + * static int my_equal( MyNode node1, + * MyNode node2 ) + * { + * // compare keys of 'node1' and 'node2' + * return (strcmp( node1->key, node2->key ) == 0); + * } + * + * .... + * + * ft_hash_init( &hash, (FT_Hash_EqualFunc) my_compare, memory ); + * .... + * } + */ + FT_BASE( FT_Error ) + ft_hash_init( FT_Hash table, + FT_Hash_EqualFunc compare, + FT_Memory memory ); + + + /**************************************************************** + * + * @function: ft_hash_lookup + * + * @description: + * search a hash table to find a node corresponding to a given + * key. + * + * @input: + * table :: handle to target hash table structure + * keynode :: handle to a reference hash node that will be + * only used for key comparisons with the table's + * elements + * + * @return: + * a pointer-to-hash-node value, which must be used as followed: + * + * - if '*result' is NULL, the key wasn't found in the hash + * table. The value of 'result' can be used to add new elements + * through @ft_hash_add however.. + * + * - if '*result' is not NULL, it's a handle to the first table + * node that corresponds to the search key. The value of 'result' + * can be used to remove this element through @ft_hash_remove + * + * @note: + * here is an example: + * + * { + * // maps a string to an integer with a hash table + * // returns -1 in case of failure + * // + * int my_lookup( FT_Hash table, + * const char* key ) + * { + * MyNode* pnode; + * MyNodeRec noderec; + * + * // set-up key node. It's 'hash' and 'key' fields must + * // be set correctly.. we ignore 'link' and 'value' + * // + * noderec.hnode.hash = strhash( key ); + * noderec.key = key; + * + * // perform search - return value + * // + * pnode = (MyNode) ft_hash_lookup( table, &noderec ); + * if ( *pnode ) + * { + * // we found it + * return (*pnode)->value; + * } + * return -1; + * } + * } + */ + FT_BASE_DEF( FT_HashLookup ) + ft_hash_lookup( FT_Hash table, + FT_HashNode keynode ); + + + /**************************************************************** + * + * @function: ft_hash_add + * + * @description: + * add a new node to a dynamic hash table. the user must + * call @ft_hash_lookup and allocate a new node before calling + * this function. + * + * @input: + * table :: hash table handle + * lookup :: pointer-to-hash-node value returned by @ft_hash_lookup + * new_node :: handle to new hash node. All its fields must be correctly + * set, including 'hash'. + * + * @return: + * error code. 0 means success + * + * @note: + * this function should always be used _after_ a call to @ft_hash_lookup + * that returns a pointer to a NULL handle. Here's an example: + * + * { + * // sets the value corresponding to a given string key + * // + * void my_set( FT_Hash table, + * const char* key, + * int value ) + * { + * MyNode* pnode; + * MyNodeRec noderec; + * MyNode node; + * + * // set-up key node. It's 'hash' and 'key' fields must + * // be set correctly.. + * noderec.hnode.hash = strhash( key ); + * noderec.key = key; + * + * // perform search - return value + * pnode = (MyNode) ft_hash_lookup( table, &noderec ); + * if ( *pnode ) + * { + * // we found it, simply replace the value in the node + * (*pnode)->value = value; + * return; + * } + * + * // allocate a new node - and set it up + * node = (MyNode) malloc( sizeof(*node) ); + * if ( node == NULL ) ..... + * + * node->hnode.hash = noderec.hnode.hash; + * node->key = key; + * node->value = value; + * + * // add it to the hash table + * error = ft_hash_add( table, pnode, node ); + * if (error) .... + * } + */ + FT_BASE( FT_Error ) + ft_hash_add( FT_Hash table, + FT_HashLookup lookup, + FT_HashNode new_node ); + + + /**************************************************************** + * + * @function: ft_hash_remove + * + * @description: + * try to remove the node corresponding to a given key from + * a hash table. This must be called after @ft_hash_lookup + * + * @input: + * table :: hash table handle + * lookup :: pointer-to-hash-node value returned by @ft_hash_lookup + * + * @note: + * this function doesn't free the node itself !! Here's an example: + * + * { + * // sets the value corresponding to a given string key + * // + * void my_remove( FT_Hash table, + * const char* key ) + * { + * MyNodeRec noderec; + * MyNode node; + * + * noderec.hnode.hash = strhash(key); + * noderec.key = key; + * node = &noderec; + * + * pnode = ft_hash_lookup( table, &noderec ); + * node = *pnode; + * if ( node != NULL ) + * { + * error = ft_hash_remove( table, pnode ); + * if ( !error ) + * free( node ); + * } + * } + * } + */ + FT_BASE( FT_Error ) + ft_hash_remove( FT_Hash table, + FT_HashLookup lookup ); + + + + /**************************************************************** + * + * @function: ft_hash_get_size + * + * @description: + * return the number of elements in a given hash table + * + * @input: + * table :: handle to target hash table structure + * + * @return: + * number of elements. 0 if empty + */ + FT_BASE( FT_UInt ) + ft_hash_get_size( FT_Hash table ); + + + + /**************************************************************** + * + * @functype: FT_Hash_ForeachFunc + * + * @description: + * a function used to iterate over all elements of a given + * hash table + * + * @input: + * node :: handle to target @FT_HashNodeRec node structure + * data :: optional argument to iteration routine + * + * @also: @ft_hash_foreach + */ + typedef void (*FT_Hash_ForeachFunc)( const FT_HashNode node, + const FT_Pointer data ); + + + /**************************************************************** + * + * @function: ft_hash_foreach + * + * @description: + * parse over all elements in a hash table + * + * @input: + * table :: handle to target hash table structure + * foreach_func :: iteration routine called for each element + * foreach_data :: optional argument to the iteration routine + * + * @note: + * this function is often used to release all elements from a + * hash table. See the example given for @ft_hash_done + */ + FT_BASE( void ) + ft_hash_foreach( FT_Hash table, + FT_Hash_ForeachFunc foreach_func, + const FT_Pointer foreach_data ); + + + + /**************************************************************** + * + * @function: ft_hash_done + * + * @description: + * finalize a given hash table + * + * @input: + * table :: handle to target hash table structure + * node_func :: optional iteration function pointer. this + * can be used to destroy all nodes explicitely + * node_data :: optional argument to the node iterator + * + * @note: + * this function simply frees the hash table's buckets. + * you probably will need to call @ft_hash_foreach to + * destroy all its elements before @ft_hash_done, as in + * the following example: + * + * { + * static void my_node_clear( const MyNode node ) + * { + * free( node ); + * } + * + * static void my_done( FT_Hash table ) + * { + * ft_hash_done( table, (FT_Hash_ForeachFunc) my_node_clear, NULL ); + * } + * } + */ + FT_BASE( void ) + ft_hash_done( FT_Hash table, + FT_Hash_ForeachFunc item_func, + const FT_Pointer item_data ); + + /* */ + + /* compute bucket index from hash value in a dynamic hash table */ + /* this is only used to break encapsulation to speed lookups in */ + /* the FreeType cache manager !! */ + /* */ + +#define FT_HASH_COMPUTE_INDEX(_table,_hash,_index) \ + { \ + FT_UInt _mask = (_table)->mask; \ + FT_UInt _hash0 = (_hash); \ + \ + (_index) = (FT_UInt)( (_hash0) & _mask ) ); \ + if ( (_index) < (_table)->p ) \ + (_index) = (FT_uInt)( (_hash0) & ( 2*_mask+1 ) ); \ + } + + +FT_END_HEADER + +#endif /* __FT_HASH_H__ */ diff --git a/extra_lib/include/freetype/freetype/internal/ftmemory.h b/extra_lib/include/freetype/freetype/internal/ftmemory.h new file mode 100644 index 0000000..8da5fad --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftmemory.h @@ -0,0 +1,293 @@ +/***************************************************************************/ +/* */ +/* ftmemory.h */ +/* */ +/* The FreeType memory management macros (specification). */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTMEMORY_H__ +#define __FTMEMORY_H__ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_TYPES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_SET_ERROR */ + /* */ + /* <Description> */ + /* This macro is used to set an implicit `error' variable to a given */ + /* expression's value (usually a function call), and convert it to a */ + /* boolean which is set whenever the value is != 0. */ + /* */ +#undef FT_SET_ERROR +#define FT_SET_ERROR( expression ) \ + ( ( error = (expression) ) != 0 ) + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** M E M O R Y ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + +#ifdef FT_DEBUG_MEMORY + + FT_BASE( FT_Error ) + FT_Alloc_Debug( FT_Memory memory, + FT_Long size, + void* *P, + const char* file_name, + FT_Long line_no ); + + FT_BASE( FT_Error ) + FT_Realloc_Debug( FT_Memory memory, + FT_Long current, + FT_Long size, + void* *P, + const char* file_name, + FT_Long line_no ); + + FT_BASE( void ) + FT_Free_Debug( FT_Memory memory, + FT_Pointer block, + const char* file_name, + FT_Long line_no ); + +#endif + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Alloc */ + /* */ + /* <Description> */ + /* Allocates a new block of memory. The returned area is always */ + /* zero-filled; this is a strong convention in many FreeType parts. */ + /* */ + /* <Input> */ + /* memory :: A handle to a given `memory object' which handles */ + /* allocation. */ + /* */ + /* size :: The size in bytes of the block to allocate. */ + /* */ + /* <Output> */ + /* P :: A pointer to the fresh new block. It should be set to */ + /* NULL if `size' is 0, or in case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_BASE( FT_Error ) + FT_Alloc( FT_Memory memory, + FT_Long size, + void* *P ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Realloc */ + /* */ + /* <Description> */ + /* Reallocates a block of memory pointed to by `*P' to `Size' bytes */ + /* from the heap, possibly changing `*P'. */ + /* */ + /* <Input> */ + /* memory :: A handle to a given `memory object' which handles */ + /* reallocation. */ + /* */ + /* current :: The current block size in bytes. */ + /* */ + /* size :: The new block size in bytes. */ + /* */ + /* <InOut> */ + /* P :: A pointer to the fresh new block. It should be set to */ + /* NULL if `size' is 0, or in case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* All callers of FT_Realloc() _must_ provide the current block size */ + /* as well as the new one. */ + /* */ + FT_BASE( FT_Error ) + FT_Realloc( FT_Memory memory, + FT_Long current, + FT_Long size, + void* *P ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Free */ + /* */ + /* <Description> */ + /* Releases a given block of memory allocated through FT_Alloc(). */ + /* */ + /* <Input> */ + /* memory :: A handle to a given `memory object' which handles */ + /* memory deallocation */ + /* */ + /* P :: This is the _address_ of a _pointer_ which points to the */ + /* allocated block. It is always set to NULL on exit. */ + /* */ + /* <Note> */ + /* If P or *P is NULL, this function should return successfully. */ + /* This is a strong convention within all of FreeType and its */ + /* drivers. */ + /* */ + FT_BASE( void ) + FT_Free( FT_Memory memory, + void* *P ); + + +#define FT_MEM_SET( dest, byte, count ) ft_memset( dest, byte, count ) + +#define FT_MEM_COPY( dest, source, count ) ft_memcpy( dest, source, count ) + +#define FT_MEM_MOVE( dest, source, count ) ft_memmove( dest, source, count ) + + +#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count ) + +#define FT_ZERO( p ) FT_MEM_ZERO( p, sizeof ( *(p) ) ) + + + /*************************************************************************/ + /* */ + /* We first define FT_MEM_ALLOC, FT_MEM_REALLOC, and FT_MEM_FREE. All */ + /* macros use an _implicit_ `memory' parameter to access the current */ + /* memory allocator. */ + /* */ + +#ifdef FT_DEBUG_MEMORY + +#define FT_MEM_ALLOC( _pointer_, _size_ ) \ + FT_Alloc_Debug( memory, _size_, \ + (void**)&(_pointer_), __FILE__, __LINE__ ) + +#define FT_MEM_REALLOC( _pointer_, _current_, _size_ ) \ + FT_Realloc_Debug( memory, _current_, _size_, \ + (void**)&(_pointer_), __FILE__, __LINE__ ) + +#define FT_MEM_FREE( _pointer_ ) \ + FT_Free_Debug( memory, (void**)&(_pointer_), __FILE__, __LINE__ ) + + +#else /* !FT_DEBUG_MEMORY */ + + +#define FT_MEM_ALLOC( _pointer_, _size_ ) \ + FT_Alloc( memory, _size_, (void**)&(_pointer_) ) + +#define FT_MEM_FREE( _pointer_ ) \ + FT_Free( memory, (void**)&(_pointer_) ) + +#define FT_MEM_REALLOC( _pointer_, _current_, _size_ ) \ + FT_Realloc( memory, _current_, _size_, (void**)&(_pointer_) ) + + +#endif /* !FT_DEBUG_MEMORY */ + + + /*************************************************************************/ + /* */ + /* The following functions macros expect that their pointer argument is */ + /* _typed_ in order to automatically compute array element sizes. */ + /* */ + +#define FT_MEM_NEW( _pointer_ ) \ + FT_MEM_ALLOC( _pointer_, sizeof ( *(_pointer_) ) ) + +#define FT_MEM_NEW_ARRAY( _pointer_, _count_ ) \ + FT_MEM_ALLOC( _pointer_, (_count_) * sizeof ( *(_pointer_) ) ) + +#define FT_MEM_RENEW_ARRAY( _pointer_, _old_, _new_ ) \ + FT_MEM_REALLOC( _pointer_, (_old_) * sizeof ( *(_pointer_) ), \ + (_new_) * sizeof ( *(_pointer_) ) ) + + + /*************************************************************************/ + /* */ + /* the following macros are obsolete but kept for compatibility reasons */ + /* */ + +#define FT_MEM_ALLOC_ARRAY( _pointer_, _count_, _type_ ) \ + FT_MEM_ALLOC( _pointer_, (_count_) * sizeof ( _type_ ) ) + +#define FT_MEM_REALLOC_ARRAY( _pointer_, _old_, _new_, _type_ ) \ + FT_MEM_REALLOC( _pointer_, (_old_) * sizeof ( _type ), \ + (_new_) * sizeof ( _type_ ) ) + + + /*************************************************************************/ + /* */ + /* The following macros are variants of their FT_MEM_XXXX equivalents; */ + /* they are used to set an _implicit_ `error' variable and return TRUE */ + /* if an error occured (i.e. if 'error != 0'). */ + /* */ + +#define FT_ALLOC( _pointer_, _size_ ) \ + FT_SET_ERROR( FT_MEM_ALLOC( _pointer_, _size_ ) ) + +#define FT_REALLOC( _pointer_, _current_, _size_ ) \ + FT_SET_ERROR( FT_MEM_REALLOC( _pointer_, _current_, _size_ ) ) + +#define FT_FREE( _pointer_ ) \ + FT_MEM_FREE( _pointer_ ) + +#define FT_NEW( _pointer_ ) \ + FT_SET_ERROR( FT_MEM_NEW( _pointer_ ) ) + +#define FT_NEW_ARRAY( _pointer_, _count_ ) \ + FT_SET_ERROR( FT_MEM_NEW_ARRAY( _pointer_, _count_ ) ) + +#define FT_RENEW_ARRAY( _pointer_, _old_, _new_ ) \ + FT_SET_ERROR( FT_MEM_RENEW_ARRAY( _pointer_, _old_, _new_ ) ) + +#define FT_ALLOC_ARRAY( _pointer_, _count_, _type_ ) \ + FT_SET_ERROR( FT_MEM_ALLOC( _pointer_, \ + (_count_) * sizeof ( _type_ ) ) ) + +#define FT_REALLOC_ARRAY( _pointer_, _old_, _new_, _type_ ) \ + FT_SET_ERROR( FT_MEM_REALLOC( _pointer_, \ + (_old_) * sizeof ( _type_ ), \ + (_new_) * sizeof ( _type_ ) ) ) + + /* */ + + +FT_END_HEADER + +#endif /* __FTMEMORY_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/ftobject.h b/extra_lib/include/freetype/freetype/internal/ftobject.h new file mode 100644 index 0000000..f285d9e --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftobject.h @@ -0,0 +1,533 @@ +#ifndef __FT_OBJECT_H__ +#define __FT_OBJECT_H__ + +#include <ft2build.h> +#include FT_FREETYPE_H + +FT_BEGIN_HEADER + + /************************************************************** + * + * @type: FT_Object + * + * @description: + * handle to a FreeType Object. See @FT_ObjectRec + */ + typedef struct FT_ObjectRec_* FT_Object; + + + /************************************************************** + * + * @type: FT_Class + * + * @description: + * handle to a constant class handle to a FreeType Object. + * + * Note that a class is itself a @FT_Object and are dynamically + * allocated on the heap. + * + * @also: + * @FT_ClassRec, @FT_Object, @FT_ObjectRec, @FT_Type, @FT_TypeRec + */ + typedef const struct FT_ClassRec_* FT_Class; + + + /************************************************************** + * + * @type: FT_Type + * + * @description: + * handle to a constant structure (of type @FT_TypeRec) used + * to describe a given @FT_Class type to the FreeType object + * sub-system. + */ + typedef const struct FT_TypeRec_* FT_Type; + + + + /************************************************************** + * + * @struct: FT_ObjectRec + * + * @description: + * a structure describing the root fields of all @FT_Object + * class instances in FreeType + * + * @fields: + * clazz :: handle to the object's class + * ref_count :: object's reference count. Starts at 1 + */ + typedef struct FT_ObjectRec_ + { + FT_Class clazz; + FT_Int ref_count; + + } FT_ObjectRec; + + + /************************************************************** + * + * @macro: FT_OBJECT (x) + * + * @description: + * a useful macro to type-cast anything to a @FT_Object + * handle. No check performed.. + */ +#define FT_OBJECT(x) ((FT_Object)(x)) + + + /************************************************************** + * + * @macro: FT_OBJECT_P (x) + * + * @description: + * a useful macro to type-cast anything to a pointer to + * @FT_Object handle. + */ +#define FT_OBJECT_P(x) ((FT_Object*)(x)) + + + /************************************************************** + * + * @macro: FT_OBJECT__CLASS (obj) + * + * @description: + * a useful macro to return the class of any object + */ +#define FT_OBJECT__CLASS(x) FT_OBJECT(x)->clazz + + + /************************************************************** + * + * @macro: FT_OBJECT__REF_COUNT (obj) + * + * @description: + * a useful macro to return the reference count of any object + */ +#define FT_OBJECT__REF_COUNT(x) FT_OBJECT(x)->ref_count + + + /************************************************************** + * + * @macro: FT_OBJECT__MEMORY (obj) + * + * @description: + * a useful macro to return a handle to the memory manager + * used to allocate a given object + */ +#define FT_OBJECT__MEMORY(x) FT_CLASS__MEMORY(FT_OBJECT(x)->clazz) + + + /************************************************************** + * + * @macro: FT_OBJECT__LIBRARY (obj) + * + * @description: + * a useful macro to return a handle to the library handle + * that owns the object + */ +#define FT_OBJECT__LIBRARY(x) FT_CLASS__LIBRARY(FT_OBJECT(x)->clazz) + + + /************************************************************** + * + * @functype: FT_Object_InitFunc + * + * @description: + * a function used to initialize a new object + * + * @input: + * object :: target object handle + * init_data :: optional pointer to initialization data + * + * @return: + * error code. 0 means success + */ + typedef FT_Error (*FT_Object_InitFunc)( FT_Object object, + FT_Pointer init_data ); + + /************************************************************** + * + * @functype: FT_Object_DoneFunc + * + * @description: + * a function used to finalize a given object + * + * @input: + * object :: handle to target object + */ + typedef void (*FT_Object_DoneFunc)( FT_Object object ); + + + /************************************************************** + * + * @struct: FT_ClassRec + * + * @description: + * a structure used to describe a given object class within + * FreeType + * + * @fields: + * object :: root @FT_ObjectRec fields, since each class is + * itself an object. (it's an instance of the + * "metaclass", a special object of the FreeType + * object sub-system.) + * + * magic :: a 32-bit magic number used for decoding + * super :: pointer to super class + * type :: the @FT_Type descriptor of this class + * memory :: the current memory manager handle + * library :: the current library handle + * info :: an opaque pointer to class-specific information + * managed by the FreeType object sub-system + * + * class_done :: the class destructor function + * + * obj_size :: size of class instances in bytes + * obj_init :: class instance constructor + * obj_done :: class instance destructor + */ + typedef struct FT_ClassRec_ + { + FT_ObjectRec object; + FT_UInt32 magic; + FT_Class super; + FT_Type type; + FT_Memory memory; + FT_Library library; + FT_Pointer info; + + FT_Object_DoneFunc class_done; + + FT_UInt obj_size; + FT_Object_InitFunc obj_init; + FT_Object_DoneFunc obj_done; + + } FT_ClassRec; + + + /************************************************************** + * + * @macro: FT_CLASS (x) + * + * @description: + * a useful macro to convert anything to a class handle + * without checks + */ +#define FT_CLASS(x) ((FT_Class)(x)) + + + /************************************************************** + * + * @macro: FT_CLASS_P (x) + * + * @description: + * a useful macro to convert anything to a pointer to a class + * handle without checks + */ +#define FT_CLASS_P(x) ((FT_Class*)(x)) + + + /************************************************************** + * + * @macro: FT_CLASS__MEMORY (clazz) + * + * @description: + * a useful macro to return the memory manager handle of a + * given class + */ +#define FT_CLASS__MEMORY(x) FT_CLASS(x)->memory + + + /************************************************************** + * + * @macro: FT_CLASS__LIBRARY (clazz) + * + * @description: + * a useful macro to return the library handle of a + * given class + */ +#define FT_CLASS__LIBRARY(x) FT_CLASS(x)->library + + + + /************************************************************** + * + * @macro: FT_CLASS__TYPE (clazz) + * + * @description: + * a useful macro to return the type of a given class + * given class + */ +#define FT_CLASS__TYPE(x) FT_CLASS(x)->type + + /* */ +#define FT_CLASS__INFO(x) FT_CLASS(x)->info +#define FT_CLASS__MAGIC(x) FT_CLASS(x)->magic + + + /************************************************************** + * + * @struct: FT_TypeRec + * + * @description: + * a structure used to describe a given class to the FreeType + * object sub-system. + * + * @fields: + * name :: class name. only used for debugging + * super :: type of super-class. NULL if none + * + * class_size :: size of class structure in bytes + * class_init :: class constructor + * class_done :: class finalizer + * + * obj_size :: instance size in bytes + * obj_init :: instance constructor. can be NULL + * obj_done :: instance destructor. can be NULL + * + * @note: + * if 'obj_init' is NULL, the class will use it's parent + * constructor, if any + * + * if 'obj_done' is NULL, the class will use it's parent + * finalizer, if any + * + * the object sub-system allocates a new class, copies + * the content of its super-class into the new structure, + * _then_ calls 'clazz_init'. + * + * 'class_init' and 'class_done' can be NULL, in which case + * the parent's class constructor and destructor wil be used + */ + typedef struct FT_TypeRec_ + { + const char* name; + FT_Type super; + + FT_UInt class_size; + FT_Object_InitFunc class_init; + FT_Object_DoneFunc class_done; + + FT_UInt obj_size; + FT_Object_InitFunc obj_init; + FT_Object_DoneFunc obj_done; + + } FT_TypeRec; + + + /************************************************************** + * + * @macro: FT_TYPE (x) + * + * @description: + * a useful macro to convert anything to a class type handle + * without checks + */ +#define FT_TYPE(x) ((FT_Type)(x)) + + + /************************************************************** + * + * @function: ft_object_check + * + * @description: + * checks that a handle points to a valid @FT_Object + * + * @input: + * obj :: handle/pointer + * + * @return: + * 1 iff the handle points to a valid object. 0 otherwise + */ + FT_BASE( FT_Int ) + ft_object_check( FT_Pointer obj ); + + + /************************************************************** + * + * @function: ft_object_is_a + * + * @description: + * checks that a handle points to a valid @FT_Object that + * is an instance of a given class (or of any of its sub-classes) + * + * @input: + * obj :: handle/pointer + * clazz :: class handle to check + * + * @return: + * 1 iff the handle points to a valid 'clazz' instance. 0 + * otherwise. + */ + FT_BASE( FT_Int ) + ft_object_is_a( FT_Pointer obj, + FT_Class clazz ); + + + /************************************************************** + * + * @function: ft_object_create + * + * @description: + * create a new object (class instance) + * + * @output: + * aobject :: new object handle. NULL in case of error + * + * @input: + * clazz :: object's class pointer + * init_data :: optional pointer to initialization data + * + * @return: + * error code. 0 means success + */ + FT_BASE( FT_Error ) + ft_object_create( FT_Object *aobject, + FT_Class clazz, + FT_Pointer init_data ); + + + /************************************************************** + * + * @function: ft_object_create_from_type + * + * @description: + * create a new object (class instance) from a @FT_Type + * + * @output: + * aobject :: new object handle. NULL in case of error + * + * @input: + * type :: object's type descriptor + * init_data :: optional pointer to initialization data + * + * @return: + * error code. 0 means success + * + * @note: + * this function is slower than @ft_object_create + * + * this is equivalent to calling @ft_class_from_type followed by + * @ft_object_create + */ + FT_BASE( FT_Error ) + ft_object_create_from_type( FT_Object *aobject, + FT_Type type, + FT_Pointer init_data, + FT_Library library ); + + + + /************************************************************** + * + * @macro FT_OBJ_CREATE (object,class,init) + * + * @description: + * a convenient macro used to create new objects. see + * @ft_object_create for details + */ +#define FT_OBJ_CREATE( _obj, _clazz, _init ) \ + ft_object_create( FT_OBJECT_P(&(_obj)), _clazz, _init ) + + + /************************************************************** + * + * @macro FT_CREATE (object,class,init) + * + * @description: + * a convenient macro used to create new objects. It also + * sets an _implicit_ local variable named "error" to the error + * code returned by the object constructor. + */ +#define FT_CREATE( _obj, _clazz, _init ) \ + FT_SET_ERROR( FT_OBJ_CREATE( _obj, _clazz, _init ) ) + + /************************************************************** + * + * @macro FT_OBJ_CREATE_FROM_TYPE (object,type,init) + * + * @description: + * a convenient macro used to create new objects. see + * @ft_object_create_from_type for details + */ +#define FT_OBJ_CREATE_FROM_TYPE( _obj, _type, _init, _lib ) \ + ft_object_create_from_type( FT_OBJECT_P(&(_obj)), _type, _init, _lib ) + + + /************************************************************** + * + * @macro FT_CREATE_FROM_TYPE (object,type,init) + * + * @description: + * a convenient macro used to create new objects. It also + * sets an _implicit_ local variable named "error" to the error + * code returned by the object constructor. + */ +#define FT_CREATE_FROM_TYPE( _obj, _type, _init, _lib ) \ + FT_SET_ERROR( FT_OBJ_CREATE_FROM_TYPE( _obj, _type, _init, _lib ) ) + + + /* */ + + /************************************************************** + * + * @function: ft_class_from_type + * + * @description: + * retrieves the class object corresponding to a given type + * descriptor. The class is created when needed + * + * @output: + * aclass :: handle to corresponding class object. NULL in + * case of error + * + * @input: + * type :: type descriptor handle + * library :: library handle + * + * @return: + * error code. 0 means success + */ + FT_BASE( FT_Error ) + ft_class_from_type( FT_Class *aclass, + FT_Type type, + FT_Library library ); + + + /* */ + +#include FT_INTERNAL_HASH_H + + typedef struct FT_ClassHNodeRec_* FT_ClassHNode; + + typedef struct FT_ClassHNodeRec_ + { + FT_HashNodeRec hnode; + FT_Type type; + FT_Class clazz; + + } FT_ClassHNodeRec; + + typedef struct FT_MetaClassRec_ + { + FT_ClassRec clazz; /* the meta-class is a class itself */ + FT_HashRec type_to_class; /* the type => class hash table */ + + } FT_MetaClassRec, *FT_MetaClass; + + + /* initialize meta class */ + FT_BASE( FT_Error ) + ft_metaclass_init( FT_MetaClass meta, + FT_Library library ); + + /* finalize meta class - destroy all registered class objects */ + FT_BASE( void ) + ft_metaclass_done( FT_MetaClass meta ); + + /* */ + +FT_END_HEADER + +#endif /* __FT_OBJECT_H__ */ diff --git a/extra_lib/include/freetype/freetype/internal/ftobjs.h b/extra_lib/include/freetype/freetype/internal/ftobjs.h new file mode 100644 index 0000000..fa1b29f --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftobjs.h @@ -0,0 +1,845 @@ +/***************************************************************************/ +/* */ +/* ftobjs.h */ +/* */ +/* The FreeType private base classes (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file contains the definition of all internal FreeType classes. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTOBJS_H__ +#define __FTOBJS_H__ + +#include <ft2build.h> +#include FT_CONFIG_STANDARD_LIBRARY_H /* for ft_setjmp and ft_longjmp */ +#include FT_RENDER_H +#include FT_SIZES_H +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_GLYPH_LOADER_H +#include FT_INTERNAL_DRIVER_H +#include FT_INTERNAL_AUTOHINT_H +#include FT_INTERNAL_SERVICE_H + +#ifdef FT_CONFIG_OPTION_INCREMENTAL +#include FT_INCREMENTAL_H +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* Some generic definitions. */ + /* */ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL (void*)0 +#endif + + + /*************************************************************************/ + /* */ + /* The min and max functions missing in C. As usual, be careful not to */ + /* write things like MIN( a++, b++ ) to avoid side effects. */ + /* */ +#ifndef MIN +#define MIN( a, b ) ( (a) < (b) ? (a) : (b) ) +#endif + +#ifndef MAX +#define MAX( a, b ) ( (a) > (b) ? (a) : (b) ) +#endif + +#ifndef ABS +#define ABS( a ) ( (a) < 0 ? -(a) : (a) ) +#endif + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** V A L I D A T I O N ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* handle to a validation object */ + typedef struct FT_ValidatorRec_* FT_Validator; + + + /*************************************************************************/ + /* */ + /* There are three distinct validation levels defined here: */ + /* */ + /* FT_VALIDATE_DEFAULT :: */ + /* A table that passes this validation level can be used reliably by */ + /* FreeType. It generally means that all offsets have been checked to */ + /* prevent out-of-bound reads, array counts are correct, etc. */ + /* */ + /* FT_VALIDATE_TIGHT :: */ + /* A table that passes this validation level can be used reliably and */ + /* doesn't contain invalid data. For example, a charmap table that */ + /* returns invalid glyph indices will not pass, even though it can */ + /* be used with FreeType in default mode (the library will simply */ + /* return an error later when trying to load the glyph). */ + /* */ + /* It also check that fields that must be a multiple of 2, 4, or 8 */ + /* don't have incorrect values, etc. */ + /* */ + /* FT_VALIDATE_PARANOID :: */ + /* Only for font debugging. Checks that a table follows the */ + /* specification by 100%. Very few fonts will be able to pass this */ + /* level anyway but it can be useful for certain tools like font */ + /* editors/converters. */ + /* */ + typedef enum FT_ValidationLevel_ + { + FT_VALIDATE_DEFAULT = 0, + FT_VALIDATE_TIGHT, + FT_VALIDATE_PARANOID + + } FT_ValidationLevel; + + + /* validator structure */ + typedef struct FT_ValidatorRec_ + { + const FT_Byte* base; /* address of table in memory */ + const FT_Byte* limit; /* `base' + sizeof(table) in memory */ + FT_ValidationLevel level; /* validation level */ + FT_Error error; /* error returned. 0 means success */ + + ft_jmp_buf jump_buffer; /* used for exception handling */ + + } FT_ValidatorRec; + + +#define FT_VALIDATOR( x ) ((FT_Validator)( x )) + + + FT_BASE( void ) + ft_validator_init( FT_Validator valid, + const FT_Byte* base, + const FT_Byte* limit, + FT_ValidationLevel level ); + + FT_BASE( FT_Int ) + ft_validator_run( FT_Validator valid ); + + /* Sets the error field in a validator, then calls `longjmp' to return */ + /* to high-level caller. Using `setjmp/longjmp' avoids many stupid */ + /* error checks within the validation routines. */ + /* */ + FT_BASE( void ) + ft_validator_error( FT_Validator valid, + FT_Error error ); + + + /* Calls ft_validate_error. Assumes that the `valid' local variable */ + /* holds a pointer to the current validator object. */ + /* */ +#define FT_INVALID( _error ) ft_validator_error( valid, _error ) + + /* called when a broken table is detected */ +#define FT_INVALID_TOO_SHORT FT_INVALID( FT_Err_Invalid_Table ) + + /* called when an invalid offset is detected */ +#define FT_INVALID_OFFSET FT_INVALID( FT_Err_Invalid_Offset ) + + /* called when an invalid format/value is detected */ +#define FT_INVALID_FORMAT FT_INVALID( FT_Err_Invalid_Table ) + + /* called when an invalid glyph index is detected */ +#define FT_INVALID_GLYPH_ID FT_INVALID( FT_Err_Invalid_Glyph_Index ) + + /* called when an invalid field value is detected */ +#define FT_INVALID_DATA FT_INVALID( FT_Err_Invalid_Table ) + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** C H A R M A P S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* handle to internal charmap object */ + typedef struct FT_CMapRec_* FT_CMap; + + /* handle to charmap class structure */ + typedef const struct FT_CMap_ClassRec_* FT_CMap_Class; + + /* internal charmap object structure */ + typedef struct FT_CMapRec_ + { + FT_CharMapRec charmap; + FT_CMap_Class clazz; + + } FT_CMapRec; + + /* typecase any pointer to a charmap handle */ +#define FT_CMAP( x ) ((FT_CMap)( x )) + + /* obvious macros */ +#define FT_CMAP_PLATFORM_ID( x ) FT_CMAP( x )->charmap.platform_id +#define FT_CMAP_ENCODING_ID( x ) FT_CMAP( x )->charmap.encoding_id +#define FT_CMAP_ENCODING( x ) FT_CMAP( x )->charmap.encoding +#define FT_CMAP_FACE( x ) FT_CMAP( x )->charmap.face + + + /* class method definitions */ + typedef FT_Error + (*FT_CMap_InitFunc)( FT_CMap cmap, + FT_Pointer init_data ); + + typedef void + (*FT_CMap_DoneFunc)( FT_CMap cmap ); + + typedef FT_UInt + (*FT_CMap_CharIndexFunc)( FT_CMap cmap, + FT_UInt32 char_code ); + + typedef FT_UInt + (*FT_CMap_CharNextFunc)( FT_CMap cmap, + FT_UInt32 *achar_code ); + + + typedef struct FT_CMap_ClassRec_ + { + FT_ULong size; + FT_CMap_InitFunc init; + FT_CMap_DoneFunc done; + FT_CMap_CharIndexFunc char_index; + FT_CMap_CharNextFunc char_next; + + } FT_CMap_ClassRec; + + + /* create a new charmap and add it to charmap->face */ + FT_BASE( FT_Error ) + FT_CMap_New( FT_CMap_Class clazz, + FT_Pointer init_data, + FT_CharMap charmap, + FT_CMap *acmap ); + + /* destroy a charmap (don't remove it from face's list though) */ + FT_BASE( void ) + FT_CMap_Done( FT_CMap cmap ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Face_InternalRec */ + /* */ + /* <Description> */ + /* This structure contains the internal fields of each FT_Face */ + /* object. These fields may change between different releases of */ + /* FreeType. */ + /* */ + /* <Fields> */ + /* max_points :: */ + /* The maximal number of points used to store the vectorial outline */ + /* of any glyph in this face. If this value cannot be known in */ + /* advance, or if the face isn't scalable, this should be set to 0. */ + /* Only relevant for scalable formats. */ + /* */ + /* max_contours :: */ + /* The maximal number of contours used to store the vectorial */ + /* outline of any glyph in this face. If this value cannot be */ + /* known in advance, or if the face isn't scalable, this should be */ + /* set to 0. Only relevant for scalable formats. */ + /* */ + /* transform_matrix :: */ + /* A 2x2 matrix of 16.16 coefficients used to transform glyph */ + /* outlines after they are loaded from the font. Only used by the */ + /* convenience functions. */ + /* */ + /* transform_delta :: */ + /* A translation vector used to transform glyph outlines after they */ + /* are loaded from the font. Only used by the convenience */ + /* functions. */ + /* */ + /* transform_flags :: */ + /* Some flags used to classify the transform. Only used by the */ + /* convenience functions. */ + /* */ + /* services :: */ + /* A cache for frequently used services. It should be only */ + /* accessed with the macro `FT_FACE_LOOKUP_SERVICE'. */ + /* */ + /* incremental_interface :: */ + /* If non-null, the interface through which glyph data and metrics */ + /* are loaded incrementally for faces that do not provide all of */ + /* this data when first opened. This field exists only if */ + /* @FT_CONFIG_OPTION_INCREMENTAL is defined. */ + /* */ + typedef struct FT_Face_InternalRec_ + { + FT_UShort max_points; + FT_Short max_contours; + + FT_Matrix transform_matrix; + FT_Vector transform_delta; + FT_Int transform_flags; + + FT_ServiceCacheRec services; + +#ifdef FT_CONFIG_OPTION_INCREMENTAL + FT_Incremental_InterfaceRec* incremental_interface; +#endif + + } FT_Face_InternalRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Slot_InternalRec */ + /* */ + /* <Description> */ + /* This structure contains the internal fields of each FT_GlyphSlot */ + /* object. These fields may change between different releases of */ + /* FreeType. */ + /* */ + /* <Fields> */ + /* loader :: The glyph loader object used to load outlines */ + /* into the glyph slot. */ + /* */ + /* flags :: Possible values are zero or */ + /* FT_GLYPH_OWN_BITMAP. The latter indicates */ + /* that the FT_GlyphSlot structure owns the */ + /* bitmap buffer. */ + /* */ + /* glyph_transformed :: Boolean. Set to TRUE when the loaded glyph */ + /* must be transformed through a specific */ + /* font transformation. This is _not_ the same */ + /* as the face transform set through */ + /* FT_Set_Transform(). */ + /* */ + /* glyph_matrix :: The 2x2 matrix corresponding to the glyph */ + /* transformation, if necessary. */ + /* */ + /* glyph_delta :: The 2d translation vector corresponding to */ + /* the glyph transformation, if necessary. */ + /* */ + /* glyph_hints :: Format-specific glyph hints management. */ + /* */ + +#define FT_GLYPH_OWN_BITMAP 0x1 + + typedef struct FT_Slot_InternalRec_ + { + FT_GlyphLoader loader; + FT_UInt flags; + FT_Bool glyph_transformed; + FT_Matrix glyph_matrix; + FT_Vector glyph_delta; + void* glyph_hints; + + } FT_GlyphSlot_InternalRec; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** M O D U L E S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_ModuleRec */ + /* */ + /* <Description> */ + /* A module object instance. */ + /* */ + /* <Fields> */ + /* clazz :: A pointer to the module's class. */ + /* */ + /* library :: A handle to the parent library object. */ + /* */ + /* memory :: A handle to the memory manager. */ + /* */ + /* generic :: A generic structure for user-level extensibility (?). */ + /* */ + typedef struct FT_ModuleRec_ + { + FT_Module_Class* clazz; + FT_Library library; + FT_Memory memory; + FT_Generic generic; + + } FT_ModuleRec; + + + /* typecast an object to a FT_Module */ +#define FT_MODULE( x ) ((FT_Module)( x )) +#define FT_MODULE_CLASS( x ) FT_MODULE( x )->clazz +#define FT_MODULE_LIBRARY( x ) FT_MODULE( x )->library +#define FT_MODULE_MEMORY( x ) FT_MODULE( x )->memory + + +#define FT_MODULE_IS_DRIVER( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_FONT_DRIVER ) + +#define FT_MODULE_IS_RENDERER( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_RENDERER ) + +#define FT_MODULE_IS_HINTER( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_HINTER ) + +#define FT_MODULE_IS_STYLER( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_STYLER ) + +#define FT_DRIVER_IS_SCALABLE( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_DRIVER_SCALABLE ) + +#define FT_DRIVER_USES_OUTLINES( x ) !( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_DRIVER_NO_OUTLINES ) + +#define FT_DRIVER_HAS_HINTER( x ) ( FT_MODULE_CLASS( x )->module_flags & \ + FT_MODULE_DRIVER_HAS_HINTER ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Module_Interface */ + /* */ + /* <Description> */ + /* Finds a module and returns its specific interface as a typeless */ + /* pointer. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object. */ + /* */ + /* module_name :: The module's name (as an ASCII string). */ + /* */ + /* <Return> */ + /* A module-specific interface if available, 0 otherwise. */ + /* */ + /* <Note> */ + /* You should better be familiar with FreeType internals to know */ + /* which module to look for, and what its interface is :-) */ + /* */ + FT_BASE( const void* ) + FT_Get_Module_Interface( FT_Library library, + const char* mod_name ); + + FT_BASE( FT_Pointer ) + ft_module_get_service( FT_Module module, + const char* service_id ); + + /* */ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** FACE, SIZE & GLYPH SLOT OBJECTS ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* a few macros used to perform easy typecasts with minimal brain damage */ + +#define FT_FACE( x ) ((FT_Face)(x)) +#define FT_SIZE( x ) ((FT_Size)(x)) +#define FT_SLOT( x ) ((FT_GlyphSlot)(x)) + +#define FT_FACE_DRIVER( x ) FT_FACE( x )->driver +#define FT_FACE_LIBRARY( x ) FT_FACE_DRIVER( x )->root.library +#define FT_FACE_MEMORY( x ) FT_FACE( x )->memory +#define FT_FACE_STREAM( x ) FT_FACE( x )->stream + +#define FT_SIZE_FACE( x ) FT_SIZE( x )->face +#define FT_SLOT_FACE( x ) FT_SLOT( x )->face + +#define FT_FACE_SLOT( x ) FT_FACE( x )->glyph +#define FT_FACE_SIZE( x ) FT_FACE( x )->size + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_GlyphSlot */ + /* */ + /* <Description> */ + /* It is sometimes useful to have more than one glyph slot for a */ + /* given face object. This function is used to create additional */ + /* slots. All of them are automatically discarded when the face is */ + /* destroyed. */ + /* */ + /* <Input> */ + /* face :: A handle to a parent face object. */ + /* */ + /* <Output> */ + /* aslot :: A handle to a new glyph slot object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_BASE( FT_Error ) + FT_New_GlyphSlot( FT_Face face, + FT_GlyphSlot *aslot ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_GlyphSlot */ + /* */ + /* <Description> */ + /* Destroys a given glyph slot. Remember however that all slots are */ + /* automatically destroyed with its parent. Using this function is */ + /* not always mandatory. */ + /* */ + /* <Input> */ + /* slot :: A handle to a target glyph slot. */ + /* */ + FT_BASE( void ) + FT_Done_GlyphSlot( FT_GlyphSlot slot ); + + /* */ + + /* + * Free the bitmap of a given glyphslot when needed + * (i.e., only when it was allocated with ft_glyphslot_alloc_bitmap). + */ + FT_BASE( void ) + ft_glyphslot_free_bitmap( FT_GlyphSlot slot ); + + + /* + * Allocate a new bitmap buffer in a glyph slot. + */ + FT_BASE( FT_Error ) + ft_glyphslot_alloc_bitmap( FT_GlyphSlot slot, + FT_ULong size ); + + + /* + * Set the bitmap buffer in a glyph slot to a given pointer. + * The buffer will not be freed by a later call to ft_glyphslot_free_bitmap. + */ + FT_BASE( void ) + ft_glyphslot_set_bitmap( FT_GlyphSlot slot, + FT_Byte* buffer ); + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** R E N D E R E R S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +#define FT_RENDERER( x ) ((FT_Renderer)( x )) +#define FT_GLYPH( x ) ((FT_Glyph)( x )) +#define FT_BITMAP_GLYPH( x ) ((FT_BitmapGlyph)( x )) +#define FT_OUTLINE_GLYPH( x ) ((FT_OutlineGlyph)( x )) + + + typedef struct FT_RendererRec_ + { + FT_ModuleRec root; + FT_Renderer_Class* clazz; + FT_Glyph_Format glyph_format; + FT_Glyph_Class glyph_class; + + FT_Raster raster; + FT_Raster_Render_Func raster_render; + FT_Renderer_RenderFunc render; + + } FT_RendererRec; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** F O N T D R I V E R S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* typecast a module into a driver easily */ +#define FT_DRIVER( x ) ((FT_Driver)(x)) + + /* typecast a module as a driver, and get its driver class */ +#define FT_DRIVER_CLASS( x ) FT_DRIVER( x )->clazz + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_DriverRec */ + /* */ + /* <Description> */ + /* The root font driver class. A font driver is responsible for */ + /* managing and loading font files of a given format. */ + /* */ + /* <Fields> */ + /* root :: Contains the fields of the root module class. */ + /* */ + /* clazz :: A pointer to the font driver's class. Note that */ + /* this is NOT root.clazz. `class' wasn't used */ + /* as it is a reserved word in C++. */ + /* */ + /* faces_list :: The list of faces currently opened by this */ + /* driver. */ + /* */ + /* extensions :: A typeless pointer to the driver's extensions */ + /* registry, if they are supported through the */ + /* configuration macro FT_CONFIG_OPTION_EXTENSIONS. */ + /* */ + /* glyph_loader :: The glyph loader for all faces managed by this */ + /* driver. This object isn't defined for unscalable */ + /* formats. */ + /* */ + typedef struct FT_DriverRec_ + { + FT_ModuleRec root; + FT_Driver_Class clazz; + + FT_ListRec faces_list; + void* extensions; + + FT_GlyphLoader glyph_loader; + + } FT_DriverRec; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** L I B R A R I E S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + +/* this hook is used by the TrueType debugger. It must be set to an alternate + * truetype bytecode interpreter function + */ +#define FT_DEBUG_HOOK_TRUETYPE 0 + + +/* set this debug hook to a non-null pointer to force unpatented hinting + * for all faces when both TT_CONFIG_OPTION_BYTECODE_INTERPRETER and + * TT_CONFIG_OPTION_UNPATENTED_HINTING are defined. this is only used + * during debugging + */ +#define FT_DEBUG_HOOK_UNPATENTED_HINTING 1 + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_LibraryRec */ + /* */ + /* <Description> */ + /* The FreeType library class. This is the root of all FreeType */ + /* data. Use FT_New_Library() to create a library object, and */ + /* FT_Done_Library() to discard it and all child objects. */ + /* */ + /* <Fields> */ + /* memory :: The library's memory object. Manages memory */ + /* allocation. */ + /* */ + /* generic :: Client data variable. Used to extend the */ + /* Library class by higher levels and clients. */ + /* */ + /* version_major :: The major version number of the library. */ + /* */ + /* version_minor :: The minor version number of the library. */ + /* */ + /* version_patch :: The current patch level of the library. */ + /* */ + /* num_modules :: The number of modules currently registered */ + /* within this library. This is set to 0 for new */ + /* libraries. New modules are added through the */ + /* FT_Add_Module() API function. */ + /* */ + /* modules :: A table used to store handles to the currently */ + /* registered modules. Note that each font driver */ + /* contains a list of its opened faces. */ + /* */ + /* renderers :: The list of renderers currently registered */ + /* within the library. */ + /* */ + /* cur_renderer :: The current outline renderer. This is a */ + /* shortcut used to avoid parsing the list on */ + /* each call to FT_Outline_Render(). It is a */ + /* handle to the current renderer for the */ + /* FT_GLYPH_FORMAT_OUTLINE format. */ + /* */ + /* auto_hinter :: XXX */ + /* */ + /* raster_pool :: The raster object's render pool. This can */ + /* ideally be changed dynamically at run-time. */ + /* */ + /* raster_pool_size :: The size of the render pool in bytes. */ + /* */ + /* debug_hooks :: XXX */ + /* */ + typedef struct FT_LibraryRec_ + { + FT_Memory memory; /* library's memory manager */ + + FT_Generic generic; + + FT_Int version_major; + FT_Int version_minor; + FT_Int version_patch; + + FT_UInt num_modules; + FT_Module modules[FT_MAX_MODULES]; /* module objects */ + + FT_ListRec renderers; /* list of renderers */ + FT_Renderer cur_renderer; /* current outline renderer */ + FT_Module auto_hinter; + + FT_Byte* raster_pool; /* scan-line conversion */ + /* render pool */ + FT_ULong raster_pool_size; /* size of render pool in bytes */ + + FT_DebugHook_Func debug_hooks[4]; + + } FT_LibraryRec; + + + FT_BASE( FT_Renderer ) + FT_Lookup_Renderer( FT_Library library, + FT_Glyph_Format format, + FT_ListNode* node ); + + FT_BASE( FT_Error ) + FT_Render_Glyph_Internal( FT_Library library, + FT_GlyphSlot slot, + FT_Render_Mode render_mode ); + + typedef const char* + (*FT_Face_GetPostscriptNameFunc)( FT_Face face ); + + typedef FT_Error + (*FT_Face_GetGlyphNameFunc)( FT_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ); + + typedef FT_UInt + (*FT_Face_GetGlyphNameIndexFunc)( FT_Face face, + FT_String* glyph_name ); + + +#ifndef FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Memory */ + /* */ + /* <Description> */ + /* Creates a new memory object. */ + /* */ + /* <Return> */ + /* A pointer to the new memory object. 0 in case of error. */ + /* */ + FT_EXPORT( FT_Memory ) + FT_New_Memory( void ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Memory */ + /* */ + /* <Description> */ + /* Discards memory manager. */ + /* */ + /* <Input> */ + /* memory :: A handle to the memory manager. */ + /* */ + FT_EXPORT( void ) + FT_Done_Memory( FT_Memory memory ); + +#endif /* !FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM */ + + + /* Define default raster's interface. The default raster is located in */ + /* `src/base/ftraster.c'. */ + /* */ + /* Client applications can register new rasters through the */ + /* FT_Set_Raster() API. */ + +#ifndef FT_NO_DEFAULT_RASTER + FT_EXPORT_VAR( FT_Raster_Funcs ) ft_default_raster; +#endif + + +FT_END_HEADER + +#endif /* __FTOBJS_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/ftserv.h b/extra_lib/include/freetype/freetype/internal/ftserv.h new file mode 100644 index 0000000..d77e1d5 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftserv.h @@ -0,0 +1,259 @@ +/***************************************************************************/ +/* */ +/* ftserv.h */ +/* */ +/* The FreeType services (specification only). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* Each module can export one or more `services'. Each service is */ + /* identified by a constant string and modeled by a pointer; the latter */ + /* generally corresponds to a structure containing function pointers. */ + /* */ + /* Note that a service's data cannot be a mere function pointer because */ + /* in C it is possible that function pointers might be implemented */ + /* differently than data pointers (e.g. 48 bits instead of 32). */ + /* */ + /*************************************************************************/ + + +#ifndef __FTSERV_H__ +#define __FTSERV_H__ + + +FT_BEGIN_HEADER + + + /* + * @macro: + * FT_FACE_FIND_SERVICE + * + * @description: + * This macro is used to look up a service from a face's driver module. + * + * @input: + * face :: + * The source face handle. + * + * id :: + * A string describing the service as defined in the service's + * header files (e.g. FT_SERVICE_ID_MULTI_MASTERS which expands to + * `multi-masters'). It is automatically prefixed with + * `FT_SERVICE_ID_'. + * + * @output: + * ptr :: + * A variable that receives the service pointer. Will be NULL + * if not found. + */ +#define FT_FACE_FIND_SERVICE( face, ptr, id ) \ + FT_BEGIN_STMNT \ + FT_Module module = FT_MODULE( FT_FACE(face)->driver ); \ + /* the strange cast is to allow C++ compilation */ \ + FT_Pointer* Pptr = (FT_Pointer*) &(ptr); \ + \ + \ + *Pptr = NULL; \ + if ( module->clazz->get_interface ) \ + *Pptr = module->clazz->get_interface( module, FT_SERVICE_ID_ ## id ); \ + FT_END_STMNT + + + /* + * @macro: + * FT_FACE_FIND_GLOBAL_SERVICE + * + * @description: + * This macro is used to look up a service from all modules. + * + * @input: + * face :: + * The source face handle. + * + * id :: + * A string describing the service as defined in the service's + * header files (e.g. FT_SERVICE_ID_MULTI_MASTERS which expands to + * `multi-masters'). It is automatically prefixed with + * `FT_SERVICE_ID_'. + * + * @output: + * ptr :: + * A variable that receives the service pointer. Will be NULL + * if not found. + */ +#define FT_FACE_FIND_GLOBAL_SERVICE( face, ptr, id ) \ + FT_BEGIN_STMNT \ + FT_Module module = FT_MODULE( FT_FACE(face)->driver ); \ + /* the strange cast is to allow C++ compilation */ \ + FT_Pointer* Pptr = (FT_Pointer*) &(ptr); \ + \ + \ + *Pptr = ft_module_get_service( module, FT_SERVICE_ID_ ## id ); \ + FT_END_STMNT + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** S E R V I C E D E S C R I P T O R S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * The following structure is used to _describe_ a given service + * to the library. This is useful to build simple static service lists. + */ + typedef struct FT_ServiceDescRec_ + { + const char* serv_id; /* service name */ + const void* serv_data; /* service pointer/data */ + + } FT_ServiceDescRec; + + typedef const FT_ServiceDescRec* FT_ServiceDesc; + + + /* + * Parse a list of FT_ServiceDescRec descriptors and look for + * a specific service by ID. Note that the last element in the + * array must be { NULL, NULL }, and that the function should + * return NULL if the service isn't available. + * + * This function can be used by modules to implement their + * `get_service' method. + */ + FT_BASE( FT_Pointer ) + ft_service_list_lookup( FT_ServiceDesc service_descriptors, + const char* service_id ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** S E R V I C E S C A C H E *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* + * This structure is used to store a cache for several frequently used + * services. It is the type of `face->internal->services'. You + * should only use FT_FACE_LOOKUP_SERVICE to access it. + * + * All fields should have the type FT_Pointer to relax compilation + * dependencies. We assume the developer isn't completely stupid. + * + * Each field must be named `service_XXXX' where `XXX' corresponds to + * the correct FT_SERVICE_ID_XXXX macro. See the definition of + * FT_FACE_LOOKUP_SERVICE below how this is implemented. + * + */ + typedef struct FT_ServiceCacheRec_ + { + FT_Pointer service_POSTSCRIPT_FONT_NAME; + FT_Pointer service_MULTI_MASTERS; + FT_Pointer service_GLYPH_DICT; + FT_Pointer service_PFR_METRICS; + FT_Pointer service_WINFNT; + + } FT_ServiceCacheRec, *FT_ServiceCache; + + + /* + * A magic number used within the services cache. + */ +#define FT_SERVICE_UNAVAILABLE ((FT_Pointer)-2) /* magic number */ + + + /* + * @macro: + * FT_FACE_LOOKUP_SERVICE + * + * @description: + * This macro is used to lookup a service from a face's driver module + * using its cache. + * + * @input: + * face:: + * The source face handle containing the cache. + * + * field :: + * The field name in the cache. + * + * id :: + * The service ID. + * + * @output: + * ptr :: + * A variable receiving the service data. NULL if not available. + */ +#define FT_FACE_LOOKUP_SERVICE( face, ptr, id ) \ + FT_BEGIN_STMNT \ + /* the strange cast is to allow C++ compilation */ \ + FT_Pointer* pptr = (FT_Pointer*)&(ptr); \ + FT_Pointer svc; \ + \ + \ + svc = FT_FACE(face)->internal->services. service_ ## id ; \ + if ( svc == FT_SERVICE_UNAVAILABLE ) \ + svc = NULL; \ + else if ( svc == NULL ) \ + { \ + FT_FACE_FIND_SERVICE( face, svc, id ); \ + \ + FT_FACE(face)->internal->services. service_ ## id = \ + (FT_Pointer)( svc != NULL ? svc \ + : FT_SERVICE_UNAVAILABLE ); \ + } \ + *pptr = svc; \ + FT_END_STMNT + + + /* + * A macro used to define new service structure types. + */ + +#define FT_DEFINE_SERVICE( name ) \ + typedef struct FT_Service_ ## name ## Rec_ \ + FT_Service_ ## name ## Rec ; \ + typedef struct FT_Service_ ## name ## Rec_ \ + const * FT_Service_ ## name ; \ + struct FT_Service_ ## name ## Rec_ + + /* */ + + /* + * The header files containing the services. + */ + +#define FT_SERVICE_MULTIPLE_MASTERS_H <freetype/internal/services/svmm.h> +#define FT_SERVICE_POSTSCRIPT_NAME_H <freetype/internal/services/svpostnm.h> +#define FT_SERVICE_POSTSCRIPT_CMAPS_H <freetype/internal/services/svpscmap.h> +#define FT_SERVICE_POSTSCRIPT_INFO_H <freetype/internal/services/svpsinfo.h> +#define FT_SERVICE_GLYPH_DICT_H <freetype/internal/services/svgldict.h> +#define FT_SERVICE_BDF_H <freetype/internal/services/svbdf.h> +#define FT_SERVICE_XFREE86_NAME_H <freetype/internal/services/svxf86nm.h> +#define FT_SERVICE_SFNT_H <freetype/internal/services/svsfnt.h> +#define FT_SERVICE_PFR_H <freetype/internal/services/svpfr.h> +#define FT_SERVICE_WINFNT_H <freetype/internal/services/svwinfnt.h> + + /* */ + +FT_END_HEADER + +#endif /* __FTSERV_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/ftstream.h b/extra_lib/include/freetype/freetype/internal/ftstream.h new file mode 100644 index 0000000..618f64a --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/ftstream.h @@ -0,0 +1,498 @@ +/***************************************************************************/ +/* */ +/* ftstream.h */ +/* */ +/* Stream handling (specification). */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTSTREAM_H__ +#define __FTSTREAM_H__ + + +#include <ft2build.h> +#include FT_SYSTEM_H +#include FT_INTERNAL_OBJECTS_H + + +FT_BEGIN_HEADER + + + /* format of an 8-bit frame_op value: */ + /* */ + /* bit 76543210 */ + /* xxxxxxes */ + /* */ + /* s is set to 1 if the value is signed. */ + /* e is set to 1 if the value is little-endian. */ + /* xxx is a command. */ + +#define FT_FRAME_OP_SHIFT 2 +#define FT_FRAME_OP_SIGNED 1 +#define FT_FRAME_OP_LITTLE 2 +#define FT_FRAME_OP_COMMAND( x ) ( x >> FT_FRAME_OP_SHIFT ) + +#define FT_MAKE_FRAME_OP( command, little, sign ) \ + ( ( command << FT_FRAME_OP_SHIFT ) | ( little << 1 ) | sign ) + +#define FT_FRAME_OP_END 0 +#define FT_FRAME_OP_START 1 /* start a new frame */ +#define FT_FRAME_OP_BYTE 2 /* read 1-byte value */ +#define FT_FRAME_OP_SHORT 3 /* read 2-byte value */ +#define FT_FRAME_OP_LONG 4 /* read 4-byte value */ +#define FT_FRAME_OP_OFF3 5 /* read 3-byte value */ +#define FT_FRAME_OP_BYTES 6 /* read a bytes sequence */ + + + typedef enum FT_Frame_Op_ + { + ft_frame_end = 0, + ft_frame_start = FT_MAKE_FRAME_OP( FT_FRAME_OP_START, 0, 0 ), + + ft_frame_byte = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTE, 0, 0 ), + ft_frame_schar = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTE, 0, 1 ), + + ft_frame_ushort_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 0, 0 ), + ft_frame_short_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 0, 1 ), + ft_frame_ushort_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 1, 0 ), + ft_frame_short_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 1, 1 ), + + ft_frame_ulong_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 0, 0 ), + ft_frame_long_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 0, 1 ), + ft_frame_ulong_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 1, 0 ), + ft_frame_long_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 1, 1 ), + + ft_frame_uoff3_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 0, 0 ), + ft_frame_off3_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 0, 1 ), + ft_frame_uoff3_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 1, 0 ), + ft_frame_off3_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 1, 1 ), + + ft_frame_bytes = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTES, 0, 0 ), + ft_frame_skip = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTES, 0, 1 ) + + } FT_Frame_Op; + + + typedef struct FT_Frame_Field_ + { + FT_Byte value; + FT_Byte size; + FT_UShort offset; + + } FT_Frame_Field; + + + /* Construct an FT_Frame_Field out of a structure type and a field name. */ + /* The structure type must be set in the FT_STRUCTURE macro before */ + /* calling the FT_FRAME_START() macro. */ + /* */ +#define FT_FIELD_SIZE( f ) \ + (FT_Byte)sizeof ( ((FT_STRUCTURE*)0)->f ) + +#define FT_FIELD_SIZE_DELTA( f ) \ + (FT_Byte)sizeof ( ((FT_STRUCTURE*)0)->f[0] ) + +#define FT_FIELD_OFFSET( f ) \ + (FT_UShort)( offsetof( FT_STRUCTURE, f ) ) + +#define FT_FRAME_FIELD( frame_op, field ) \ + { \ + frame_op, \ + FT_FIELD_SIZE( field ), \ + FT_FIELD_OFFSET( field ) \ + } + +#define FT_MAKE_EMPTY_FIELD( frame_op ) { frame_op, 0, 0 } + +#define FT_FRAME_START( size ) { ft_frame_start, 0, size } +#define FT_FRAME_END { ft_frame_end, 0, 0 } + +#define FT_FRAME_LONG( f ) FT_FRAME_FIELD( ft_frame_long_be, f ) +#define FT_FRAME_ULONG( f ) FT_FRAME_FIELD( ft_frame_ulong_be, f ) +#define FT_FRAME_SHORT( f ) FT_FRAME_FIELD( ft_frame_short_be, f ) +#define FT_FRAME_USHORT( f ) FT_FRAME_FIELD( ft_frame_ushort_be, f ) +#define FT_FRAME_OFF3( f ) FT_FRAME_FIELD( ft_frame_off3_be, f ) +#define FT_FRAME_UOFF3( f ) FT_FRAME_FIELD( ft_frame_uoff3_be, f ) +#define FT_FRAME_BYTE( f ) FT_FRAME_FIELD( ft_frame_byte, f ) +#define FT_FRAME_CHAR( f ) FT_FRAME_FIELD( ft_frame_schar, f ) + +#define FT_FRAME_LONG_LE( f ) FT_FRAME_FIELD( ft_frame_long_le, f ) +#define FT_FRAME_ULONG_LE( f ) FT_FRAME_FIELD( ft_frame_ulong_le, f ) +#define FT_FRAME_SHORT_LE( f ) FT_FRAME_FIELD( ft_frame_short_le, f ) +#define FT_FRAME_USHORT_LE( f ) FT_FRAME_FIELD( ft_frame_ushort_le, f ) +#define FT_FRAME_OFF3_LE( f ) FT_FRAME_FIELD( ft_frame_off3_le, f ) +#define FT_FRAME_UOFF3_LE( f ) FT_FRAME_FIELD( ft_frame_uoff3_le, f ) + +#define FT_FRAME_SKIP_LONG { ft_frame_long_be, 0, 0 } +#define FT_FRAME_SKIP_SHORT { ft_frame_short_be, 0, 0 } +#define FT_FRAME_SKIP_BYTE { ft_frame_byte, 0, 0 } + +#define FT_FRAME_BYTES( field, count ) \ + { \ + ft_frame_bytes, \ + count, \ + FT_FIELD_OFFSET( field ) \ + } + +#define FT_FRAME_SKIP_BYTES( count ) { ft_frame_skip, count, 0 } + + + /*************************************************************************/ + /* */ + /* Integer extraction macros -- the `buffer' parameter must ALWAYS be of */ + /* type `char*' or equivalent (1-byte elements). */ + /* */ + +#define FT_BYTE_( p, i ) ( ((const FT_Byte*)(p))[(i)] ) +#define FT_INT8_( p, i ) ( ((const FT_Char*)(p))[(i)] ) + +#define FT_INT16( x ) ( (FT_Int16)(x) ) +#define FT_UINT16( x ) ( (FT_UInt16)(x) ) +#define FT_INT32( x ) ( (FT_Int32)(x) ) +#define FT_UINT32( x ) ( (FT_UInt32)(x) ) + +#define FT_BYTE_I16( p, i, s ) ( FT_INT16( FT_BYTE_( p, i ) ) << (s) ) +#define FT_BYTE_U16( p, i, s ) ( FT_UINT16( FT_BYTE_( p, i ) ) << (s) ) +#define FT_BYTE_I32( p, i, s ) ( FT_INT32( FT_BYTE_( p, i ) ) << (s) ) +#define FT_BYTE_U32( p, i, s ) ( FT_UINT32( FT_BYTE_( p, i ) ) << (s) ) + +#define FT_INT8_I16( p, i, s ) ( FT_INT16( FT_INT8_( p, i ) ) << (s) ) +#define FT_INT8_U16( p, i, s ) ( FT_UINT16( FT_INT8_( p, i ) ) << (s) ) +#define FT_INT8_I32( p, i, s ) ( FT_INT32( FT_INT8_( p, i ) ) << (s) ) +#define FT_INT8_U32( p, i, s ) ( FT_UINT32( FT_INT8_( p, i ) ) << (s) ) + + +#define FT_PEEK_SHORT( p ) FT_INT16( FT_INT8_I16( p, 0, 8) | \ + FT_BYTE_I16( p, 1, 0) ) + +#define FT_PEEK_USHORT( p ) FT_UINT16( FT_BYTE_U16( p, 0, 8 ) | \ + FT_BYTE_U16( p, 1, 0 ) ) + +#define FT_PEEK_LONG( p ) FT_INT32( FT_INT8_I32( p, 0, 24 ) | \ + FT_BYTE_I32( p, 1, 16 ) | \ + FT_BYTE_I32( p, 2, 8 ) | \ + FT_BYTE_I32( p, 3, 0 ) ) + +#define FT_PEEK_ULONG( p ) FT_UINT32( FT_BYTE_U32( p, 0, 24 ) | \ + FT_BYTE_U32( p, 1, 16 ) | \ + FT_BYTE_U32( p, 2, 8 ) | \ + FT_BYTE_U32( p, 3, 0 ) ) + +#define FT_PEEK_OFF3( p ) FT_INT32( FT_INT8_I32( p, 0, 16 ) | \ + FT_BYTE_I32( p, 1, 8 ) | \ + FT_BYTE_I32( p, 2, 0 ) ) + +#define FT_PEEK_UOFF3( p ) FT_UINT32( FT_BYTE_U32( p, 0, 16 ) | \ + FT_BYTE_U32( p, 1, 8 ) | \ + FT_BYTE_U32( p, 2, 0 ) ) + +#define FT_PEEK_SHORT_LE( p ) FT_INT16( FT_INT8_I16( p, 1, 8 ) | \ + FT_BYTE_I16( p, 0, 0 ) ) + +#define FT_PEEK_USHORT_LE( p ) FT_UINT16( FT_BYTE_U16( p, 1, 8 ) | \ + FT_BYTE_U16( p, 0, 0 ) ) + +#define FT_PEEK_LONG_LE( p ) FT_INT32( FT_INT8_I32( p, 3, 24 ) | \ + FT_BYTE_I32( p, 2, 16 ) | \ + FT_BYTE_I32( p, 1, 8 ) | \ + FT_BYTE_I32( p, 0, 0 ) ) + +#define FT_PEEK_ULONG_LE( p ) FT_UINT32( FT_BYTE_U32( p, 3, 24 ) | \ + FT_BYTE_U32( p, 2, 16 ) | \ + FT_BYTE_U32( p, 1, 8 ) | \ + FT_BYTE_U32( p, 0, 0 ) ) + +#define FT_PEEK_OFF3_LE( p ) FT_INT32( FT_INT8_I32( p, 2, 16 ) | \ + FT_BYTE_I32( p, 1, 8 ) | \ + FT_BYTE_I32( p, 0, 0 ) ) + +#define FT_PEEK_UOFF3_LE( p ) FT_UINT32( FT_BYTE_U32( p, 2, 16 ) | \ + FT_BYTE_U32( p, 1, 8 ) | \ + FT_BYTE_U32( p, 0, 0 ) ) + + +#define FT_NEXT_CHAR( buffer ) \ + ( (signed char)*buffer++ ) + +#define FT_NEXT_BYTE( buffer ) \ + ( (unsigned char)*buffer++ ) + +#define FT_NEXT_SHORT( buffer ) \ + ( (short)( buffer += 2, FT_PEEK_SHORT( buffer - 2 ) ) ) + +#define FT_NEXT_USHORT( buffer ) \ + ( (unsigned short)( buffer += 2, FT_PEEK_USHORT( buffer - 2 ) ) ) + +#define FT_NEXT_OFF3( buffer ) \ + ( (long)( buffer += 3, FT_PEEK_OFF3( buffer - 3 ) ) ) + +#define FT_NEXT_UOFF3( buffer ) \ + ( (unsigned long)( buffer += 3, FT_PEEK_UOFF3( buffer - 3 ) ) ) + +#define FT_NEXT_LONG( buffer ) \ + ( (long)( buffer += 4, FT_PEEK_LONG( buffer - 4 ) ) ) + +#define FT_NEXT_ULONG( buffer ) \ + ( (unsigned long)( buffer += 4, FT_PEEK_ULONG( buffer - 4 ) ) ) + + +#define FT_NEXT_SHORT_LE( buffer ) \ + ( (short)( buffer += 2, FT_PEEK_SHORT_LE( buffer - 2 ) ) ) + +#define FT_NEXT_USHORT_LE( buffer ) \ + ( (unsigned short)( buffer += 2, FT_PEEK_USHORT_LE( buffer - 2 ) ) ) + +#define FT_NEXT_OFF3_LE( buffer ) \ + ( (long)( buffer += 3, FT_PEEK_OFF3_LE( buffer - 3 ) ) ) + +#define FT_NEXT_UOFF3_LE( buffer ) \ + ( (unsigned long)( buffer += 3, FT_PEEK_UOFF3_LE( buffer - 3 ) ) ) + +#define FT_NEXT_LONG_LE( buffer ) \ + ( (long)( buffer += 4, FT_PEEK_LONG_LE( buffer - 4 ) ) ) + +#define FT_NEXT_ULONG_LE( buffer ) \ + ( (unsigned long)( buffer += 4, FT_PEEK_ULONG_LE( buffer - 4 ) ) ) + + + /*************************************************************************/ + /* */ + /* Each GET_xxxx() macro uses an implicit `stream' variable. */ + /* */ +#define FT_GET_MACRO( func, type ) ( (type)func( stream ) ) + +#define FT_GET_CHAR() FT_GET_MACRO( FT_Stream_GetChar, FT_Char ) +#define FT_GET_BYTE() FT_GET_MACRO( FT_Stream_GetChar, FT_Byte ) +#define FT_GET_SHORT() FT_GET_MACRO( FT_Stream_GetShort, FT_Short ) +#define FT_GET_USHORT() FT_GET_MACRO( FT_Stream_GetShort, FT_UShort ) +#define FT_GET_OFF3() FT_GET_MACRO( FT_Stream_GetOffset, FT_Long ) +#define FT_GET_UOFF3() FT_GET_MACRO( FT_Stream_GetOffset, FT_ULong ) +#define FT_GET_LONG() FT_GET_MACRO( FT_Stream_GetLong, FT_Long ) +#define FT_GET_ULONG() FT_GET_MACRO( FT_Stream_GetLong, FT_ULong ) +#define FT_GET_TAG4() FT_GET_MACRO( FT_Stream_GetLong, FT_ULong ) + +#define FT_GET_SHORT_LE() FT_GET_MACRO( FT_Stream_GetShortLE, FT_Short ) +#define FT_GET_USHORT_LE() FT_GET_MACRO( FT_Stream_GetShortLE, FT_UShort ) +#define FT_GET_LONG_LE() FT_GET_MACRO( FT_Stream_GetLongLE, FT_Long ) +#define FT_GET_ULONG_LE() FT_GET_MACRO( FT_Stream_GetLongLE, FT_ULong ) + +#define FT_READ_MACRO( func, type, var ) \ + ( var = (type)func( stream, &error ), \ + error != FT_Err_Ok ) + +#define FT_READ_BYTE( var ) FT_READ_MACRO( FT_Stream_ReadChar, FT_Byte, var ) +#define FT_READ_CHAR( var ) FT_READ_MACRO( FT_Stream_ReadChar, FT_Char, var ) +#define FT_READ_SHORT( var ) FT_READ_MACRO( FT_Stream_ReadShort, FT_Short, var ) +#define FT_READ_USHORT( var ) FT_READ_MACRO( FT_Stream_ReadShort, FT_UShort, var ) +#define FT_READ_OFF3( var ) FT_READ_MACRO( FT_Stream_ReadOffset, FT_Long, var ) +#define FT_READ_UOFF3( var ) FT_READ_MACRO( FT_Stream_ReadOffset, FT_ULong, var ) +#define FT_READ_LONG( var ) FT_READ_MACRO( FT_Stream_ReadLong, FT_Long, var ) +#define FT_READ_ULONG( var ) FT_READ_MACRO( FT_Stream_ReadLong, FT_ULong, var ) + +#define FT_READ_SHORT_LE( var ) FT_READ_MACRO( FT_Stream_ReadShortLE, FT_Short, var ) +#define FT_READ_USHORT_LE( var ) FT_READ_MACRO( FT_Stream_ReadShortLE, FT_UShort, var ) +#define FT_READ_LONG_LE( var ) FT_READ_MACRO( FT_Stream_ReadLongLE, FT_Long, var ) +#define FT_READ_ULONG_LE( var ) FT_READ_MACRO( FT_Stream_ReadLongLE, FT_ULong, var ) + + +#ifndef FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM + + /* initialize a stream for reading a regular system stream */ + FT_EXPORT( FT_Error ) + FT_Stream_Open( FT_Stream stream, + const char* filepathname ); + +#endif /* FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM */ + + + /* initialize a stream for reading in-memory data */ + FT_BASE( void ) + FT_Stream_OpenMemory( FT_Stream stream, + const FT_Byte* base, + FT_ULong size ); + + /* close a stream (does not destroy the stream structure) */ + FT_BASE( void ) + FT_Stream_Close( FT_Stream stream ); + + + /* seek within a stream. position is relative to start of stream */ + FT_BASE( FT_Error ) + FT_Stream_Seek( FT_Stream stream, + FT_ULong pos ); + + /* skip bytes in a stream */ + FT_BASE( FT_Error ) + FT_Stream_Skip( FT_Stream stream, + FT_Long distance ); + + /* return current stream position */ + FT_BASE( FT_Long ) + FT_Stream_Pos( FT_Stream stream ); + + /* read bytes from a stream into a user-allocated buffer, returns an */ + /* error if not all bytes could be read. */ + FT_BASE( FT_Error ) + FT_Stream_Read( FT_Stream stream, + FT_Byte* buffer, + FT_ULong count ); + + /* read bytes from a stream at a given position */ + FT_BASE( FT_Error ) + FT_Stream_ReadAt( FT_Stream stream, + FT_ULong pos, + FT_Byte* buffer, + FT_ULong count ); + + /* Enter a frame of `count' consecutive bytes in a stream. Returns an */ + /* error if the frame could not be read/accessed. The caller can use */ + /* the FT_Stream_Get_XXX functions to retrieve frame data without */ + /* error checks. */ + /* */ + /* You must _always_ call FT_Stream_ExitFrame() once you have entered */ + /* a stream frame! */ + /* */ + FT_BASE( FT_Error ) + FT_Stream_EnterFrame( FT_Stream stream, + FT_ULong count ); + + /* exit a stream frame */ + FT_BASE( void ) + FT_Stream_ExitFrame( FT_Stream stream ); + + /* Extract a stream frame. If the stream is disk-based, a heap block */ + /* is allocated and the frame bytes are read into it. If the stream */ + /* is memory-based, this function simply set a pointer to the data. */ + /* */ + /* Useful to optimize access to memory-based streams transparently. */ + /* */ + /* All extracted frames must be `freed` with a call to the function */ + /* FT_Stream_ReleaseFrame(). */ + /* */ + FT_BASE( FT_Error ) + FT_Stream_ExtractFrame( FT_Stream stream, + FT_ULong count, + FT_Byte** pbytes ); + + /* release an extract frame (see FT_Stream_ExtractFrame) */ + FT_BASE( void ) + FT_Stream_ReleaseFrame( FT_Stream stream, + FT_Byte** pbytes ); + + /* read a byte from an entered frame */ + FT_BASE( FT_Char ) + FT_Stream_GetChar( FT_Stream stream ); + + /* read a 16-bit big-endian integer from an entered frame */ + FT_BASE( FT_Short ) + FT_Stream_GetShort( FT_Stream stream ); + + /* read a 24-bit big-endian integer from an entered frame */ + FT_BASE( FT_Long ) + FT_Stream_GetOffset( FT_Stream stream ); + + /* read a 32-bit big-endian integer from an entered frame */ + FT_BASE( FT_Long ) + FT_Stream_GetLong( FT_Stream stream ); + + /* read a 16-bit little-endian integer from an entered frame */ + FT_BASE( FT_Short ) + FT_Stream_GetShortLE( FT_Stream stream ); + + /* read a 32-bit little-endian integer from an entered frame */ + FT_BASE( FT_Long ) + FT_Stream_GetLongLE( FT_Stream stream ); + + + /* read a byte from a stream */ + FT_BASE( FT_Char ) + FT_Stream_ReadChar( FT_Stream stream, + FT_Error* error ); + + /* read a 16-bit big-endian integer from a stream */ + FT_BASE( FT_Short ) + FT_Stream_ReadShort( FT_Stream stream, + FT_Error* error ); + + /* read a 24-bit big-endian integer from a stream */ + FT_BASE( FT_Long ) + FT_Stream_ReadOffset( FT_Stream stream, + FT_Error* error ); + + /* read a 32-bit big-endian integer from a stream */ + FT_BASE( FT_Long ) + FT_Stream_ReadLong( FT_Stream stream, + FT_Error* error ); + + /* read a 16-bit little-endian integer from a stream */ + FT_BASE( FT_Short ) + FT_Stream_ReadShortLE( FT_Stream stream, + FT_Error* error ); + + /* read a 32-bit little-endian integer from a stream */ + FT_BASE( FT_Long ) + FT_Stream_ReadLongLE( FT_Stream stream, + FT_Error* error ); + + /* Read a structure from a stream. The structure must be described */ + /* by an array of FT_Frame_Field records. */ + FT_BASE( FT_Error ) + FT_Stream_ReadFields( FT_Stream stream, + const FT_Frame_Field* fields, + void* structure ); + + +#define FT_STREAM_POS() \ + FT_Stream_Pos( stream ) + +#define FT_STREAM_SEEK( position ) \ + FT_SET_ERROR( FT_Stream_Seek( stream, position ) ) + +#define FT_STREAM_SKIP( distance ) \ + FT_SET_ERROR( FT_Stream_Skip( stream, distance ) ) + +#define FT_STREAM_READ( buffer, count ) \ + FT_SET_ERROR( FT_Stream_Read( stream, \ + (FT_Byte*)buffer, \ + count ) ) + +#define FT_STREAM_READ_AT( position, buffer, count ) \ + FT_SET_ERROR( FT_Stream_ReadAt( stream, \ + position, \ + (FT_Byte*)buffer, \ + count ) ) + +#define FT_STREAM_READ_FIELDS( fields, object ) \ + FT_SET_ERROR( FT_Stream_ReadFields( stream, fields, object ) ) + + +#define FT_FRAME_ENTER( size ) \ + FT_SET_ERROR( FT_Stream_EnterFrame( stream, size ) ) + +#define FT_FRAME_EXIT() \ + FT_Stream_ExitFrame( stream ) + +#define FT_FRAME_EXTRACT( size, bytes ) \ + FT_SET_ERROR( FT_Stream_ExtractFrame( stream, size, \ + (FT_Byte**)&(bytes) ) ) + +#define FT_FRAME_RELEASE( bytes ) \ + FT_Stream_ReleaseFrame( stream, (FT_Byte**)&(bytes) ) + + +FT_END_HEADER + +#endif /* __FTSTREAM_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/fttrace.h b/extra_lib/include/freetype/freetype/internal/fttrace.h new file mode 100644 index 0000000..ffc0b14 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/fttrace.h @@ -0,0 +1,106 @@ +/***************************************************************************/ +/* */ +/* fttrace.h */ +/* */ +/* Tracing handling (specification only). */ +/* */ +/* Copyright 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +/* definitions of trace levels for FreeType 2 */ + +/* the first level must always be `trace_any' */ +FT_TRACE_DEF( any ) + +/* base components */ +FT_TRACE_DEF( calc ) /* calculations (ftcalc.c) */ +FT_TRACE_DEF( memory ) /* memory manager (ftobjs.c) */ +FT_TRACE_DEF( stream ) /* stream manager (ftstream.c) */ +FT_TRACE_DEF( io ) /* i/o interface (ftsystem.c) */ +FT_TRACE_DEF( list ) /* list management (ftlist.c) */ +FT_TRACE_DEF( init ) /* initialization (ftinit.c) */ +FT_TRACE_DEF( objs ) /* base objects (ftobjs.c) */ +FT_TRACE_DEF( outline ) /* outline management (ftoutln.c) */ +FT_TRACE_DEF( glyph ) /* glyph management (ftglyph.c) */ + +FT_TRACE_DEF( raster ) /* monochrome rasterizer (ftraster.c) */ +FT_TRACE_DEF( smooth ) /* anti-aliasing raster (ftgrays.c) */ +FT_TRACE_DEF( mm ) /* MM interface (ftmm.c) */ + +/* Cache sub-system */ +FT_TRACE_DEF( cache ) /* cache sub-system (ftcache.c, etc..) */ + +/* SFNT driver components */ +FT_TRACE_DEF( sfobjs ) /* SFNT object handler (sfobjs.c) */ +FT_TRACE_DEF( ttcmap ) /* charmap handler (ttcmap.c) */ +FT_TRACE_DEF( ttload ) /* basic TrueType tables (ttload.c) */ +FT_TRACE_DEF( ttpost ) /* PS table processing (ttpost.c) */ +FT_TRACE_DEF( ttsbit ) /* TrueType sbit handling (ttsbit.c) */ + +/* TrueType driver components */ +FT_TRACE_DEF( ttdriver ) /* TT font driver (ttdriver.c) */ +FT_TRACE_DEF( ttgload ) /* TT glyph loader (ttgload.c) */ +FT_TRACE_DEF( ttinterp ) /* bytecode interpreter (ttinterp.c) */ +FT_TRACE_DEF( ttobjs ) /* TT objects manager (ttobjs.c) */ +FT_TRACE_DEF( ttpload ) /* TT data/program loader (ttpload.c) */ + +/* Type 1 driver components */ +FT_TRACE_DEF( t1driver ) +FT_TRACE_DEF( t1gload ) +FT_TRACE_DEF( t1hint ) +FT_TRACE_DEF( t1load ) +FT_TRACE_DEF( t1objs ) +FT_TRACE_DEF( t1parse ) + +/* PostScript helper module `psaux' */ +FT_TRACE_DEF( t1decode ) +FT_TRACE_DEF( psobjs ) + +/* PostScript hinting module `pshinter' */ +FT_TRACE_DEF( pshrec ) +FT_TRACE_DEF( pshalgo1 ) +FT_TRACE_DEF( pshalgo2 ) + +/* Type 2 driver components */ +FT_TRACE_DEF( cffdriver ) +FT_TRACE_DEF( cffgload ) +FT_TRACE_DEF( cffload ) +FT_TRACE_DEF( cffobjs ) +FT_TRACE_DEF( cffparse ) + +/* Type 42 driver component */ +FT_TRACE_DEF( t42 ) + +/* CID driver components */ +FT_TRACE_DEF( cidafm ) +FT_TRACE_DEF( ciddriver ) +FT_TRACE_DEF( cidgload ) +FT_TRACE_DEF( cidload ) +FT_TRACE_DEF( cidobjs ) +FT_TRACE_DEF( cidparse ) + +/* Windows fonts component */ +FT_TRACE_DEF( winfnt ) + +/* PCF fonts components */ +FT_TRACE_DEF( pcfdriver ) +FT_TRACE_DEF( pcfread ) + +/* BDF fonts component */ +FT_TRACE_DEF( bdfdriver ) +FT_TRACE_DEF( bdflib ) + +/* PFR fonts component */ +FT_TRACE_DEF( pfr ) + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/internal.h b/extra_lib/include/freetype/freetype/internal/internal.h new file mode 100644 index 0000000..0348789 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/internal.h @@ -0,0 +1,48 @@ +/***************************************************************************/ +/* */ +/* internal.h */ +/* */ +/* Internal header files (specification only). */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is automatically included by `ft2build.h'. */ + /* Do not include it manually! */ + /* */ + /*************************************************************************/ + + +#define FT_INTERNAL_OBJECTS_H <freetype/internal/ftobjs.h> +#define FT_INTERNAL_STREAM_H <freetype/internal/ftstream.h> +#define FT_INTERNAL_MEMORY_H <freetype/internal/ftmemory.h> +#define FT_INTERNAL_DEBUG_H <freetype/internal/ftdebug.h> +#define FT_INTERNAL_CALC_H <freetype/internal/ftcalc.h> +#define FT_INTERNAL_DRIVER_H <freetype/internal/ftdriver.h> +#define FT_INTERNAL_TRACE_H <freetype/internal/fttrace.h> +#define FT_INTERNAL_GLYPH_LOADER_H <freetype/internal/ftgloadr.h> +#define FT_INTERNAL_SFNT_H <freetype/internal/sfnt.h> +#define FT_INTERNAL_SERVICE_H <freetype/internal/ftserv.h> + +#define FT_INTERNAL_TRUETYPE_TYPES_H <freetype/internal/tttypes.h> +#define FT_INTERNAL_TYPE1_TYPES_H <freetype/internal/t1types.h> + +#define FT_INTERNAL_POSTSCRIPT_AUX_H <freetype/internal/psaux.h> +#define FT_INTERNAL_POSTSCRIPT_HINTS_H <freetype/internal/pshints.h> +#define FT_INTERNAL_POSTSCRIPT_GLOBALS_H <freetype/internal/psglobal.h> + +#define FT_INTERNAL_AUTOHINT_H <freetype/internal/autohint.h> + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/pcftypes.h b/extra_lib/include/freetype/freetype/internal/pcftypes.h new file mode 100644 index 0000000..382796f --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/pcftypes.h @@ -0,0 +1,56 @@ +/* pcftypes.h + + FreeType font driver for pcf fonts + + Copyright (C) 2000, 2001, 2002 by + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#ifndef __PCFTYPES_H__ +#define __PCFTYPES_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + typedef struct PCF_Public_FaceRec_ + { + FT_FaceRec root; + FT_StreamRec gzip_stream; + FT_Stream gzip_source; + + char* charset_encoding; + char* charset_registry; + + } PCF_Public_FaceRec, *PCF_Public_Face; + + +FT_END_HEADER + +#endif /* __PCFTYPES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/pfr.h b/extra_lib/include/freetype/freetype/internal/pfr.h new file mode 100644 index 0000000..51be620 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/pfr.h @@ -0,0 +1,60 @@ +/***************************************************************************/ +/* */ +/* pfr.h */ +/* */ +/* Internal PFR service functions (specification only). */ +/* */ +/* Copyright 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __PFR_H__ +#define __PFR_H__ + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + typedef FT_Error (*FT_PFR_GetMetricsFunc)( FT_Face face, + FT_UInt *aoutline, + FT_UInt *ametrics, + FT_Fixed *ax_scale, + FT_Fixed *ay_scale ); + + typedef FT_Error (*FT_PFR_GetKerningFunc)( FT_Face face, + FT_UInt left, + FT_UInt right, + FT_Vector *avector ); + + typedef FT_Error (*FT_PFR_GetAdvanceFunc)( FT_Face face, + FT_UInt gindex, + FT_Pos *aadvance ); + + + typedef struct FT_PFR_ServiceRec_ + { + FT_PFR_GetMetricsFunc get_metrics; + FT_PFR_GetKerningFunc get_kerning; + FT_PFR_GetAdvanceFunc get_advance; + + } FT_PFR_ServiceRec, *FT_PFR_Service; + +#define FT_PFR_SERVICE_NAME "pfr" + + +FT_END_HEADER + +#endif /* __PFR_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/psaux.h b/extra_lib/include/freetype/freetype/internal/psaux.h new file mode 100644 index 0000000..e013d52 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/psaux.h @@ -0,0 +1,733 @@ +/***************************************************************************/ +/* */ +/* psaux.h */ +/* */ +/* Auxiliary functions and data structures related to PostScript fonts */ +/* (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __PSAUX_H__ +#define __PSAUX_H__ + + +#include <ft2build.h> +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_TYPE1_TYPES_H +#include FT_SERVICE_POSTSCRIPT_CMAPS_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1_TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + typedef struct PS_TableRec_* PS_Table; + typedef const struct PS_Table_FuncsRec_* PS_Table_Funcs; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_Table_FuncsRec */ + /* */ + /* <Description> */ + /* A set of function pointers to manage PS_Table objects. */ + /* */ + /* <Fields> */ + /* table_init :: Used to initialize a table. */ + /* */ + /* table_done :: Finalizes resp. destroy a given table. */ + /* */ + /* table_add :: Adds a new object to a table. */ + /* */ + /* table_release :: Releases table data, then finalizes it. */ + /* */ + typedef struct PS_Table_FuncsRec_ + { + FT_Error + (*init)( PS_Table table, + FT_Int count, + FT_Memory memory ); + + void + (*done)( PS_Table table ); + + FT_Error + (*add)( PS_Table table, + FT_Int idx, + void* object, + FT_PtrDist length ); + + void + (*release)( PS_Table table ); + + } PS_Table_FuncsRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_TableRec */ + /* */ + /* <Description> */ + /* A PS_Table is a simple object used to store an array of objects in */ + /* a single memory block. */ + /* */ + /* <Fields> */ + /* block :: The address in memory of the growheap's block. This */ + /* can change between two object adds, due to */ + /* reallocation. */ + /* */ + /* cursor :: The current top of the grow heap within its block. */ + /* */ + /* capacity :: The current size of the heap block. Increments by */ + /* 1kByte chunks. */ + /* */ + /* max_elems :: The maximum number of elements in table. */ + /* */ + /* num_elems :: The current number of elements in table. */ + /* */ + /* elements :: A table of element addresses within the block. */ + /* */ + /* lengths :: A table of element sizes within the block. */ + /* */ + /* memory :: The object used for memory operations */ + /* (alloc/realloc). */ + /* */ + /* funcs :: A table of method pointers for this object. */ + /* */ + typedef struct PS_TableRec_ + { + FT_Byte* block; /* current memory block */ + FT_Offset cursor; /* current cursor in memory block */ + FT_Offset capacity; /* current size of memory block */ + FT_Long init; + + FT_Int max_elems; + FT_Int num_elems; + FT_Byte** elements; /* addresses of table elements */ + FT_Int* lengths; /* lengths of table elements */ + + FT_Memory memory; + PS_Table_FuncsRec funcs; + + } PS_TableRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 FIELDS & TOKENS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct PS_ParserRec_* PS_Parser; + + typedef struct T1_TokenRec_* T1_Token; + + typedef struct T1_FieldRec_* T1_Field; + + + /* simple enumeration type used to identify token types */ + typedef enum T1_TokenType_ + { + T1_TOKEN_TYPE_NONE = 0, + T1_TOKEN_TYPE_ANY, + T1_TOKEN_TYPE_STRING, + T1_TOKEN_TYPE_ARRAY, + + /* do not remove */ + T1_TOKEN_TYPE_MAX + + } T1_TokenType; + + + /* a simple structure used to identify tokens */ + typedef struct T1_TokenRec_ + { + FT_Byte* start; /* first character of token in input stream */ + FT_Byte* limit; /* first character after the token */ + T1_TokenType type; /* type of token */ + + } T1_TokenRec; + + + /* enumeration type used to identify object fields */ + typedef enum T1_FieldType_ + { + T1_FIELD_TYPE_NONE = 0, + T1_FIELD_TYPE_BOOL, + T1_FIELD_TYPE_INTEGER, + T1_FIELD_TYPE_FIXED, + T1_FIELD_TYPE_FIXED_1000, + T1_FIELD_TYPE_STRING, + T1_FIELD_TYPE_KEY, + T1_FIELD_TYPE_BBOX, + T1_FIELD_TYPE_INTEGER_ARRAY, + T1_FIELD_TYPE_FIXED_ARRAY, + T1_FIELD_TYPE_CALLBACK, + + /* do not remove */ + T1_FIELD_TYPE_MAX + + } T1_FieldType; + + + typedef enum T1_FieldLocation_ + { + T1_FIELD_LOCATION_CID_INFO, + T1_FIELD_LOCATION_FONT_DICT, + T1_FIELD_LOCATION_FONT_INFO, + T1_FIELD_LOCATION_PRIVATE, + T1_FIELD_LOCATION_BBOX, + + /* do not remove */ + T1_FIELD_LOCATION_MAX + + } T1_FieldLocation; + + + typedef void + (*T1_Field_ParseFunc)( FT_Face face, + FT_Pointer parser ); + + + /* structure type used to model object fields */ + typedef struct T1_FieldRec_ + { + const char* ident; /* field identifier */ + T1_FieldLocation location; + T1_FieldType type; /* type of field */ + T1_Field_ParseFunc reader; + FT_UInt offset; /* offset of field in object */ + FT_Byte size; /* size of field in bytes */ + FT_UInt array_max; /* maximal number of elements for */ + /* array */ + FT_UInt count_offset; /* offset of element count for */ + /* arrays */ + } T1_FieldRec; + + +#define T1_NEW_SIMPLE_FIELD( _ident, _type, _fname ) \ + { \ + _ident, T1CODE, _type, \ + 0, \ + FT_FIELD_OFFSET( _fname ), \ + FT_FIELD_SIZE( _fname ), \ + 0, 0 \ + }, + +#define T1_NEW_CALLBACK_FIELD( _ident, _reader ) \ + { \ + _ident, T1CODE, T1_FIELD_TYPE_CALLBACK, \ + (T1_Field_ParseFunc)_reader, \ + 0, 0, \ + 0, 0 \ + }, + +#define T1_NEW_TABLE_FIELD( _ident, _type, _fname, _max ) \ + { \ + _ident, T1CODE, _type, \ + 0, \ + FT_FIELD_OFFSET( _fname ), \ + FT_FIELD_SIZE_DELTA( _fname ), \ + _max, \ + FT_FIELD_OFFSET( num_ ## _fname ) \ + }, + +#define T1_NEW_TABLE_FIELD2( _ident, _type, _fname, _max ) \ + { \ + _ident, T1CODE, _type, \ + 0, \ + FT_FIELD_OFFSET( _fname ), \ + FT_FIELD_SIZE_DELTA( _fname ), \ + _max, 0 \ + }, + + +#define T1_FIELD_BOOL( _ident, _fname ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_BOOL, _fname ) + +#define T1_FIELD_NUM( _ident, _fname ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_INTEGER, _fname ) + +#define T1_FIELD_FIXED( _ident, _fname ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_FIXED, _fname ) + +#define T1_FIELD_FIXED_1000( _ident, _fname ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_FIXED_1000, _fname ) + +#define T1_FIELD_STRING( _ident, _fname ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_STRING, _fname ) + +#define T1_FIELD_KEY( _ident, _fname ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_KEY, _fname ) + +#define T1_FIELD_BBOX( _ident, _fname ) \ + T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_BBOX, _fname ) + + +#define T1_FIELD_NUM_TABLE( _ident, _fname, _fmax ) \ + T1_NEW_TABLE_FIELD( _ident, T1_FIELD_TYPE_INTEGER_ARRAY, \ + _fname, _fmax ) + +#define T1_FIELD_FIXED_TABLE( _ident, _fname, _fmax ) \ + T1_NEW_TABLE_FIELD( _ident, T1_FIELD_TYPE_FIXED_ARRAY, \ + _fname, _fmax ) + +#define T1_FIELD_NUM_TABLE2( _ident, _fname, _fmax ) \ + T1_NEW_TABLE_FIELD2( _ident, T1_FIELD_TYPE_INTEGER_ARRAY, \ + _fname, _fmax ) + +#define T1_FIELD_FIXED_TABLE2( _ident, _fname, _fmax ) \ + T1_NEW_TABLE_FIELD2( _ident, T1_FIELD_TYPE_FIXED_ARRAY, \ + _fname, _fmax ) + +#define T1_FIELD_CALLBACK( _ident, _name ) \ + T1_NEW_CALLBACK_FIELD( _ident, _name ) + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 PARSER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef const struct PS_Parser_FuncsRec_* PS_Parser_Funcs; + + typedef struct PS_Parser_FuncsRec_ + { + void + (*init)( PS_Parser parser, + FT_Byte* base, + FT_Byte* limit, + FT_Memory memory ); + + void + (*done)( PS_Parser parser ); + + void + (*skip_spaces)( PS_Parser parser ); + void + (*skip_PS_token)( PS_Parser parser ); + + FT_Long + (*to_int)( PS_Parser parser ); + FT_Fixed + (*to_fixed)( PS_Parser parser, + FT_Int power_ten ); + + FT_Error + (*to_bytes)( PS_Parser parser, + FT_Byte* bytes, + FT_Long max_bytes, + FT_Long* pnum_bytes, + FT_Bool delimiters ); + + FT_Int + (*to_coord_array)( PS_Parser parser, + FT_Int max_coords, + FT_Short* coords ); + FT_Int + (*to_fixed_array)( PS_Parser parser, + FT_Int max_values, + FT_Fixed* values, + FT_Int power_ten ); + + void + (*to_token)( PS_Parser parser, + T1_Token token ); + void + (*to_token_array)( PS_Parser parser, + T1_Token tokens, + FT_UInt max_tokens, + FT_Int* pnum_tokens ); + + FT_Error + (*load_field)( PS_Parser parser, + const T1_Field field, + void** objects, + FT_UInt max_objects, + FT_ULong* pflags ); + + FT_Error + (*load_field_table)( PS_Parser parser, + const T1_Field field, + void** objects, + FT_UInt max_objects, + FT_ULong* pflags ); + + } PS_Parser_FuncsRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_ParserRec */ + /* */ + /* <Description> */ + /* A PS_Parser is an object used to parse a Type 1 font very quickly. */ + /* */ + /* <Fields> */ + /* cursor :: The current position in the text. */ + /* */ + /* base :: Start of the processed text. */ + /* */ + /* limit :: End of the processed text. */ + /* */ + /* error :: The last error returned. */ + /* */ + /* memory :: The object used for memory operations (alloc/realloc). */ + /* */ + /* funcs :: A table of functions for the parser. */ + /* */ + typedef struct PS_ParserRec_ + { + FT_Byte* cursor; + FT_Byte* base; + FT_Byte* limit; + FT_Error error; + FT_Memory memory; + + PS_Parser_FuncsRec funcs; + + } PS_ParserRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 BUILDER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + typedef struct T1_BuilderRec_* T1_Builder; + + + typedef FT_Error + (*T1_Builder_Check_Points_Func)( T1_Builder builder, + FT_Int count ); + + typedef void + (*T1_Builder_Add_Point_Func)( T1_Builder builder, + FT_Pos x, + FT_Pos y, + FT_Byte flag ); + + typedef FT_Error + (*T1_Builder_Add_Point1_Func)( T1_Builder builder, + FT_Pos x, + FT_Pos y ); + + typedef FT_Error + (*T1_Builder_Add_Contour_Func)( T1_Builder builder ); + + typedef FT_Error + (*T1_Builder_Start_Point_Func)( T1_Builder builder, + FT_Pos x, + FT_Pos y ); + + typedef void + (*T1_Builder_Close_Contour_Func)( T1_Builder builder ); + + + typedef const struct T1_Builder_FuncsRec_* T1_Builder_Funcs; + + typedef struct T1_Builder_FuncsRec_ + { + void + (*init)( T1_Builder builder, + FT_Face face, + FT_Size size, + FT_GlyphSlot slot, + FT_Bool hinting ); + + void + (*done)( T1_Builder builder ); + + T1_Builder_Check_Points_Func check_points; + T1_Builder_Add_Point_Func add_point; + T1_Builder_Add_Point1_Func add_point1; + T1_Builder_Add_Contour_Func add_contour; + T1_Builder_Start_Point_Func start_point; + T1_Builder_Close_Contour_Func close_contour; + + } T1_Builder_FuncsRec; + + + /*************************************************************************/ + /* */ + /* <Structure> */ + /* T1_BuilderRec */ + /* */ + /* <Description> */ + /* A structure used during glyph loading to store its outline. */ + /* */ + /* <Fields> */ + /* memory :: The current memory object. */ + /* */ + /* face :: The current face object. */ + /* */ + /* glyph :: The current glyph slot. */ + /* */ + /* loader :: XXX */ + /* */ + /* base :: The base glyph outline. */ + /* */ + /* current :: The current glyph outline. */ + /* */ + /* max_points :: maximum points in builder outline */ + /* */ + /* max_contours :: Maximal number of contours in builder outline. */ + /* */ + /* last :: The last point position. */ + /* */ + /* scale_x :: The horizontal scale (FUnits to sub-pixels). */ + /* */ + /* scale_y :: The vertical scale (FUnits to sub-pixels). */ + /* */ + /* pos_x :: The horizontal translation (if composite glyph). */ + /* */ + /* pos_y :: The vertical translation (if composite glyph). */ + /* */ + /* left_bearing :: The left side bearing point. */ + /* */ + /* advance :: The horizontal advance vector. */ + /* */ + /* bbox :: Unused. */ + /* */ + /* path_begun :: A flag which indicates that a new path has begun. */ + /* */ + /* load_points :: If this flag is not set, no points are loaded. */ + /* */ + /* no_recurse :: Set but not used. */ + /* */ + /* error :: An error code that is only used to report memory */ + /* allocation problems. */ + /* */ + /* metrics_only :: A boolean indicating that we only want to compute */ + /* the metrics of a given glyph, not load all of its */ + /* points. */ + /* */ + /* funcs :: An array of function pointers for the builder. */ + /* */ + typedef struct T1_BuilderRec_ + { + FT_Memory memory; + FT_Face face; + FT_GlyphSlot glyph; + FT_GlyphLoader loader; + FT_Outline* base; + FT_Outline* current; + + FT_Vector last; + + FT_Fixed scale_x; + FT_Fixed scale_y; + + FT_Pos pos_x; + FT_Pos pos_y; + + FT_Vector left_bearing; + FT_Vector advance; + + FT_BBox bbox; /* bounding box */ + FT_Bool path_begun; + FT_Bool load_points; + FT_Bool no_recurse; + FT_Bool shift; + + FT_Error error; /* only used for memory errors */ + FT_Bool metrics_only; + + void* hints_funcs; /* hinter-specific */ + void* hints_globals; /* hinter-specific */ + + T1_Builder_FuncsRec funcs; + + } T1_BuilderRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** T1 DECODER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + +#if 0 + + /*************************************************************************/ + /* */ + /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ + /* calls during glyph loading. */ + /* */ +#define T1_MAX_SUBRS_CALLS 8 + + + /*************************************************************************/ + /* */ + /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */ + /* minimum of 16 is required. */ + /* */ +#define T1_MAX_CHARSTRINGS_OPERANDS 32 + +#endif /* 0 */ + + + typedef struct T1_Decoder_ZoneRec_ + { + FT_Byte* cursor; + FT_Byte* base; + FT_Byte* limit; + + } T1_Decoder_ZoneRec, *T1_Decoder_Zone; + + + typedef struct T1_DecoderRec_* T1_Decoder; + typedef const struct T1_Decoder_FuncsRec_* T1_Decoder_Funcs; + + + typedef FT_Error + (*T1_Decoder_Callback)( T1_Decoder decoder, + FT_UInt glyph_index ); + + + typedef struct T1_Decoder_FuncsRec_ + { + FT_Error + (*init)( T1_Decoder decoder, + FT_Face face, + FT_Size size, + FT_GlyphSlot slot, + FT_Byte** glyph_names, + PS_Blend blend, + FT_Bool hinting, + FT_Render_Mode hint_mode, + T1_Decoder_Callback callback ); + + void + (*done)( T1_Decoder decoder ); + + FT_Error + (*parse_charstrings)( T1_Decoder decoder, + FT_Byte* base, + FT_UInt len ); + + } T1_Decoder_FuncsRec; + + + typedef struct T1_DecoderRec_ + { + T1_BuilderRec builder; + + FT_Long stack[T1_MAX_CHARSTRINGS_OPERANDS]; + FT_Long* top; + + T1_Decoder_ZoneRec zones[T1_MAX_SUBRS_CALLS + 1]; + T1_Decoder_Zone zone; + + FT_Service_PsCMaps psnames; /* for seac */ + FT_UInt num_glyphs; + FT_Byte** glyph_names; + + FT_Int lenIV; /* internal for sub routine calls */ + FT_UInt num_subrs; + FT_Byte** subrs; + FT_Int* subrs_len; /* array of subrs length (optional) */ + + FT_Matrix font_matrix; + FT_Vector font_offset; + + FT_Int flex_state; + FT_Int num_flex_vectors; + FT_Vector flex_vectors[7]; + + PS_Blend blend; /* for multiple master support */ + + FT_Render_Mode hint_mode; + + T1_Decoder_Callback parse_callback; + T1_Decoder_FuncsRec funcs; + + } T1_DecoderRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** TYPE1 CHARMAPS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef const struct T1_CMap_ClassesRec_* T1_CMap_Classes; + + typedef struct T1_CMap_ClassesRec_ + { + FT_CMap_Class standard; + FT_CMap_Class expert; + FT_CMap_Class custom; + FT_CMap_Class unicode; + + } T1_CMap_ClassesRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PSAux Module Interface *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct PSAux_ServiceRec_ + { + /* don't use `PS_Table_Funcs' and friends to avoid compiler warnings */ + const PS_Table_FuncsRec* ps_table_funcs; + const PS_Parser_FuncsRec* ps_parser_funcs; + const T1_Builder_FuncsRec* t1_builder_funcs; + const T1_Decoder_FuncsRec* t1_decoder_funcs; + + void + (*t1_decrypt)( FT_Byte* buffer, + FT_Offset length, + FT_UShort seed ); + + T1_CMap_Classes t1_cmap_classes; + + } PSAux_ServiceRec, *PSAux_Service; + + /* backwards-compatible type definition */ + typedef PSAux_ServiceRec PSAux_Interface; + +FT_END_HEADER + +#endif /* __PSAUX_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/pshints.h b/extra_lib/include/freetype/freetype/internal/pshints.h new file mode 100644 index 0000000..cd48f6c --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/pshints.h @@ -0,0 +1,626 @@ +/***************************************************************************/ +/* */ +/* pshints.h */ +/* */ +/* Interface to Postscript-specific (Type 1 and Type 2) hints */ +/* recorders (specification only). These are used to support native */ +/* T1/T2 hints in the "type1", "cid" and "cff" font drivers. */ +/* */ +/* Copyright 2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __PSHINTS_H__ +#define __PSHINTS_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TYPE1_TABLES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** INTERNAL REPRESENTATION OF GLOBALS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct PSH_GlobalsRec_* PSH_Globals; + + typedef FT_Error + (*PSH_Globals_NewFunc)( FT_Memory memory, + T1_Private* private_dict, + PSH_Globals* aglobals ); + + typedef FT_Error + (*PSH_Globals_SetScaleFunc)( PSH_Globals globals, + FT_Fixed x_scale, + FT_Fixed y_scale, + FT_Fixed x_delta, + FT_Fixed y_delta ); + + typedef void + (*PSH_Globals_DestroyFunc)( PSH_Globals globals ); + + + typedef struct PSH_Globals_FuncsRec_ + { + PSH_Globals_NewFunc create; + PSH_Globals_SetScaleFunc set_scale; + PSH_Globals_DestroyFunc destroy; + + } PSH_Globals_FuncsRec, *PSH_Globals_Funcs; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PUBLIC TYPE 1 HINTS RECORDER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* @type: */ + /* T1_Hints */ + /* */ + /* @description: */ + /* This is a handle to an opaque structure used to record glyph hints */ + /* from a Type 1 character glyph character string. */ + /* */ + /* The methods used to operate on this object are defined by the */ + /* @T1_Hints_FuncsRec structure. Recording glyph hints is normally */ + /* achieved through the following scheme: */ + /* */ + /* - Open a new hint recording session by calling the "open" method. */ + /* This will rewind the recorder and prepare it for new input. */ + /* */ + /* - For each hint found in the glyph charstring, call the */ + /* corresponding method ("stem", "stem3", or "reset"). Note that */ + /* these functions do not return an error code. */ + /* */ + /* - Close the recording session by calling the "close" method. It */ + /* will return an error code if the hints were invalid or something */ + /* strange happened (e.g. memory shortage). */ + /* */ + /* The hints accumulated in the object can later be used by the */ + /* PostScript hinter. */ + /* */ + typedef struct T1_HintsRec_* T1_Hints; + + + /*************************************************************************/ + /* */ + /* @type: */ + /* T1_Hints_Funcs */ + /* */ + /* @description: */ + /* A pointer to the @T1_Hints_FuncsRec structure that defines the */ + /* API of a given @T1_Hints object. */ + /* */ + typedef const struct T1_Hints_FuncsRec_* T1_Hints_Funcs; + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T1_Hints_OpenFunc */ + /* */ + /* @description: */ + /* A method of the @T1_Hints class used to prepare it for a new */ + /* Type 1 hints recording session. */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 1 hints recorder. */ + /* */ + /* @note: */ + /* You should always call the @T1_Hints_CloseFunc method in order to */ + /* close an opened recording session. */ + /* */ + typedef void + (*T1_Hints_OpenFunc)( T1_Hints hints ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T1_Hints_SetStemFunc */ + /* */ + /* @description: */ + /* A method of the @T1_Hints class used to record a new horizontal or */ + /* vertical stem. This corresponds to the Type 1 "hstem" and "vstem" */ + /* operators. */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 1 hints recorder. */ + /* */ + /* dimension :: 0 for horizontal stems (hstem), 1 for vertical ones */ + /* (vstem). */ + /* */ + /* coords :: Array of 2 integers, used as (position,length) stem */ + /* descriptor. */ + /* */ + /* @note: */ + /* Use vertical coordinates (y) for horizontal stems (dim=0). Use */ + /* horizontal coordinates (x) for vertical stems (dim=1). */ + /* */ + /* "coords[0]" is the absolute stem position (lowest coordinate); */ + /* "coords[1]" is the length. */ + /* */ + /* The length can be negative, in which case it must be either -20 or */ + /* -21. It will be interpreted as a "ghost" stem, according to */ + /* Type 1 specification. */ + /* */ + /* If the length is -21 (corresponding to a bottom ghost stem), then */ + /* the real stem position is "coords[0]+coords[1]". */ + /* */ + typedef void + (*T1_Hints_SetStemFunc)( T1_Hints hints, + FT_UInt dimension, + FT_Long* coords ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T1_Hints_SetStem3Func */ + /* */ + /* @description: */ + /* A method of the @T1_Hints class used to record three */ + /* counter-controlled horizontal or vertical stems at once. */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 1 hints recorder. */ + /* */ + /* dimension :: 0 for horizontal stems, 1 for vertical ones. */ + /* */ + /* coords :: An array of 6 integers, holding 3 (position,length) */ + /* pairs for the counter-controlled stems. */ + /* */ + /* @note: */ + /* Use vertical coordinates (y) for horizontal stems (dim=0). Use */ + /* horizontal coordinates (x) for vertical stems (dim=1). */ + /* */ + /* The lengths cannot be negative (ghost stems are never */ + /* counter-controlled). */ + /* */ + typedef void + (*T1_Hints_SetStem3Func)( T1_Hints hints, + FT_UInt dimension, + FT_Long* coords ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T1_Hints_ResetFunc */ + /* */ + /* @description: */ + /* A method of the @T1_Hints class used to reset the stems hints in a */ + /* recording session. */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 1 hints recorder. */ + /* */ + /* end_point :: The index of the last point in the input glyph in */ + /* which the previously defined hints apply. */ + /* */ + typedef void + (*T1_Hints_ResetFunc)( T1_Hints hints, + FT_UInt end_point ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T1_Hints_CloseFunc */ + /* */ + /* @description: */ + /* A method of the @T1_Hints class used to close a hint recording */ + /* session. */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 1 hints recorder. */ + /* */ + /* end_point :: The index of the last point in the input glyph. */ + /* */ + /* @return: */ + /* FreeType error code. 0 means success. */ + /* */ + /* @note: */ + /* The error code will be set to indicate that an error occured */ + /* during the recording session. */ + /* */ + typedef FT_Error + (*T1_Hints_CloseFunc)( T1_Hints hints, + FT_UInt end_point ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T1_Hints_ApplyFunc */ + /* */ + /* @description: */ + /* A method of the @T1_Hints class used to apply hints to the */ + /* corresponding glyph outline. Must be called once all hints have */ + /* been recorded. */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 1 hints recorder. */ + /* */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* globals :: The hinter globals for this font. */ + /* */ + /* hint_mode :: Hinting information. */ + /* */ + /* @return: */ + /* FreeType error code. 0 means success. */ + /* */ + /* @note: */ + /* On input, all points within the outline are in font coordinates. */ + /* On output, they are in 1/64th of pixels. */ + /* */ + /* The scaling transformation is taken from the "globals" object */ + /* which must correspond to the same font as the glyph. */ + /* */ + typedef FT_Error + (*T1_Hints_ApplyFunc)( T1_Hints hints, + FT_Outline* outline, + PSH_Globals globals, + FT_Render_Mode hint_mode ); + + + /*************************************************************************/ + /* */ + /* @struct: */ + /* T1_Hints_FuncsRec */ + /* */ + /* @description: */ + /* The structure used to provide the API to @T1_Hints objects. */ + /* */ + /* @fields: */ + /* hints :: A handle to the T1 Hints recorder. */ + /* */ + /* open :: The function to open a recording session. */ + /* */ + /* close :: The function to close a recording session. */ + /* */ + /* stem :: The function to set a simple stem. */ + /* */ + /* stem3 :: The function to set counter-controlled stems. */ + /* */ + /* reset :: The function to reset stem hints. */ + /* */ + /* apply :: The function to apply the hints to the corresponding */ + /* glyph outline. */ + /* */ + typedef struct T1_Hints_FuncsRec_ + { + T1_Hints hints; + T1_Hints_OpenFunc open; + T1_Hints_CloseFunc close; + T1_Hints_SetStemFunc stem; + T1_Hints_SetStem3Func stem3; + T1_Hints_ResetFunc reset; + T1_Hints_ApplyFunc apply; + + } T1_Hints_FuncsRec; + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PUBLIC TYPE 2 HINTS RECORDER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* @type: */ + /* T2_Hints */ + /* */ + /* @description: */ + /* This is a handle to an opaque structure used to record glyph hints */ + /* from a Type 2 character glyph character string. */ + /* */ + /* The methods used to operate on this object are defined by the */ + /* @T2_Hints_FuncsRec structure. Recording glyph hints is normally */ + /* achieved through the following scheme: */ + /* */ + /* - Open a new hint recording session by calling the "open" method. */ + /* This will rewind the recorder and prepare it for new input. */ + /* */ + /* - For each hint found in the glyph charstring, call the */ + /* corresponding method ("stems", "hintmask", "counters"). Note */ + /* that these functions do not return an error code. */ + /* */ + /* - Close the recording session by calling the "close" method. It */ + /* will return an error code if the hints were invalid or something */ + /* strange happened (e.g. memory shortage). */ + /* */ + /* The hints accumulated in the object can later be used by the */ + /* Postscript hinter. */ + /* */ + typedef struct T2_HintsRec_* T2_Hints; + + + /*************************************************************************/ + /* */ + /* @type: */ + /* T2_Hints_Funcs */ + /* */ + /* @description: */ + /* A pointer to the @T2_Hints_FuncsRec structure that defines the API */ + /* of a given @T2_Hints object. */ + /* */ + typedef const struct T2_Hints_FuncsRec_* T2_Hints_Funcs; + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T2_Hints_OpenFunc */ + /* */ + /* @description: */ + /* A method of the @T2_Hints class used to prepare it for a new */ + /* Type 2 hints recording session. */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 2 hints recorder. */ + /* */ + /* @note: */ + /* You should always call the @T2_Hints_CloseFunc method in order to */ + /* close an opened recording session. */ + /* */ + typedef void + (*T2_Hints_OpenFunc)( T2_Hints hints ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T2_Hints_StemsFunc */ + /* */ + /* @description: */ + /* A method of the @T2_Hints class used to set the table of stems in */ + /* either the vertical or horizontal dimension. Equivalent to the */ + /* "hstem", "vstem", "hstemhm", and "vstemhm" Type 2 operators. */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 2 hints recorder. */ + /* */ + /* dimension :: 0 for horizontal stems (hstem), 1 for vertical ones */ + /* (vstem). */ + /* */ + /* count :: The number of stems. */ + /* */ + /* coords :: An array of "count" (position,length) pairs. */ + /* */ + /* @note: */ + /* Use vertical coordinates (y) for horizontal stems (dim=0). Use */ + /* horizontal coordinates (x) for vertical stems (dim=1). */ + /* */ + /* There are "2*count" elements in the "coords" aray. Each even */ + /* element is an absolute position in font units, each odd element is */ + /* a length in font units. */ + /* */ + /* A length can be negative, in which case it must be either -20 or */ + /* -21. It will be interpreted as a "ghost" stem, according to the */ + /* Type 1 specification. */ + /* */ + typedef void + (*T2_Hints_StemsFunc)( T2_Hints hints, + FT_UInt dimension, + FT_UInt count, + FT_Fixed* coordinates ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T2_Hints_MaskFunc */ + /* */ + /* @description: */ + /* A method of the @T2_Hints class used to set a given hintmask */ + /* (this corresponds to the "hintmask" Type 2 operator). */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 2 hints recorder. */ + /* */ + /* end_point :: The glyph index of the last point to which the */ + /* previously defined/activated hints apply. */ + /* */ + /* bit_count :: The number of bits in the hint mask. */ + /* */ + /* bytes :: An array of bytes modelling the hint mask. */ + /* */ + /* @note: */ + /* If the hintmask starts the charstring (before any glyph point */ + /* definition), the value of "end_point" should be 0. */ + /* */ + /* "bit_count" is the number of meaningful bits in the "bytes" array; */ + /* it must be equal to the total number of hints defined so far */ + /* (i.e. horizontal+verticals). */ + /* */ + /* The "bytes" array can come directly from the Type 2 charstring and */ + /* respects the same format. */ + /* */ + typedef void + (*T2_Hints_MaskFunc)( T2_Hints hints, + FT_UInt end_point, + FT_UInt bit_count, + const FT_Byte* bytes ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T2_Hints_CounterFunc */ + /* */ + /* @description: */ + /* A method of the @T2_Hints class used to set a given counter mask */ + /* (this corresponds to the "hintmask" Type 2 operator). */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 2 hints recorder. */ + /* */ + /* end_point :: A glyph index of the last point to which the */ + /* previously defined/active hints apply. */ + /* */ + /* bit_count :: The number of bits in the hint mask. */ + /* */ + /* bytes :: An array of bytes modelling the hint mask. */ + /* */ + /* @note: */ + /* If the hintmask starts the charstring (before any glyph point */ + /* definition), the value of "end_point" should be 0. */ + /* */ + /* "bit_count" is the number of meaningful bits in the "bytes" array; */ + /* it must be equal to the total number of hints defined so far */ + /* (i.e. horizontal+verticals). */ + /* */ + /* The "bytes" array can come directly from the Type 2 charstring and */ + /* respects the same format. */ + /* */ + typedef void + (*T2_Hints_CounterFunc)( T2_Hints hints, + FT_UInt bit_count, + const FT_Byte* bytes ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T2_Hints_CloseFunc */ + /* */ + /* @description: */ + /* A method of the @T2_Hints class used to close a hint recording */ + /* session. */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 2 hints recorder. */ + /* */ + /* end_point :: The index of the last point in the input glyph. */ + /* */ + /* @return: */ + /* FreeType error code. 0 means success. */ + /* */ + /* @note: */ + /* The error code will be set to indicate that an error occured */ + /* during the recording session. */ + /* */ + typedef FT_Error + (*T2_Hints_CloseFunc)( T2_Hints hints, + FT_UInt end_point ); + + + /*************************************************************************/ + /* */ + /* @functype: */ + /* T2_Hints_ApplyFunc */ + /* */ + /* @description: */ + /* A method of the @T2_Hints class used to apply hints to the */ + /* corresponding glyph outline. Must be called after the "close" */ + /* method. */ + /* */ + /* @input: */ + /* hints :: A handle to the Type 2 hints recorder. */ + /* */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* globals :: The hinter globals for this font. */ + /* */ + /* hint_mode :: Hinting information. */ + /* */ + /* @return: */ + /* FreeType error code. 0 means success. */ + /* */ + /* @note: */ + /* On input, all points within the outline are in font coordinates. */ + /* On output, they are in 1/64th of pixels. */ + /* */ + /* The scaling transformation is taken from the "globals" object */ + /* which must correspond to the same font than the glyph. */ + /* */ + typedef FT_Error + (*T2_Hints_ApplyFunc)( T2_Hints hints, + FT_Outline* outline, + PSH_Globals globals, + FT_Render_Mode hint_mode ); + + + /*************************************************************************/ + /* */ + /* @struct: */ + /* T2_Hints_FuncsRec */ + /* */ + /* @description: */ + /* The structure used to provide the API to @T2_Hints objects. */ + /* */ + /* @fields: */ + /* hints :: A handle to the T2 hints recorder object. */ + /* */ + /* open :: The function to open a recording session. */ + /* */ + /* close :: The function to close a recording session. */ + /* */ + /* stems :: The function to set the dimension's stems table. */ + /* */ + /* hintmask :: The function to set hint masks. */ + /* */ + /* counter :: The function to set counter masks. */ + /* */ + /* apply :: The function to apply the hints on the corresponding */ + /* glyph outline. */ + /* */ + typedef struct T2_Hints_FuncsRec_ + { + T2_Hints hints; + T2_Hints_OpenFunc open; + T2_Hints_CloseFunc close; + T2_Hints_StemsFunc stems; + T2_Hints_MaskFunc hintmask; + T2_Hints_CounterFunc counter; + T2_Hints_ApplyFunc apply; + + } T2_Hints_FuncsRec; + + + /* */ + + + typedef struct PSHinter_Interface_ + { + PSH_Globals_Funcs (*get_globals_funcs)( FT_Module module ); + T1_Hints_Funcs (*get_t1_funcs) ( FT_Module module ); + T2_Hints_Funcs (*get_t2_funcs) ( FT_Module module ); + + } PSHinter_Interface; + + typedef PSHinter_Interface* PSHinter_Service; + + +FT_END_HEADER + +#endif /* __PSHINTS_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/psnames.h b/extra_lib/include/freetype/freetype/internal/psnames.h new file mode 100644 index 0000000..0f4ec86 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/psnames.h @@ -0,0 +1,241 @@ +/***************************************************************************/ +/* */ +/* psnames.h */ +/* */ +/* High-level interface for the `PSNames' module (in charge of */ +/* various functions related to Postscript glyph names conversion). */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __PSNAMES_H__ +#define __PSNAMES_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* PS_Unicode_Value_Func */ + /* */ + /* <Description> */ + /* A function used to return the Unicode index corresponding to a */ + /* given glyph name. */ + /* */ + /* <Input> */ + /* glyph_name :: The glyph name. */ + /* */ + /* <Return> */ + /* The Unicode character index resp. the non-Unicode value 0xFFFF if */ + /* the glyph name has no known Unicode meaning. */ + /* */ + /* <Note> */ + /* This function is able to map several different glyph names to the */ + /* same Unicode value, according to the rules defined in the Adobe */ + /* Glyph List table. */ + /* */ + /* This function will not be compiled if the configuration macro */ + /* FT_CONFIG_OPTION_ADOBE_GLYPH_LIST is undefined. */ + /* */ + typedef FT_UInt32 + (*PS_Unicode_Value_Func)( const char* glyph_name ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* PS_Unicode_Index_Func */ + /* */ + /* <Description> */ + /* A function used to return the glyph index corresponding to a given */ + /* Unicode value. */ + /* */ + /* <Input> */ + /* num_glyphs :: The number of glyphs in the face. */ + /* */ + /* glyph_names :: An array of glyph name pointers. */ + /* */ + /* unicode :: The Unicode value. */ + /* */ + /* <Return> */ + /* The glyph index resp. 0xFFFF if no glyph corresponds to this */ + /* Unicode value. */ + /* */ + /* <Note> */ + /* This function is able to recognize several glyph names per Unicode */ + /* value, according to the Adobe Glyph List. */ + /* */ + /* This function will not be compiled if the configuration macro */ + /* FT_CONFIG_OPTION_ADOBE_GLYPH_LIST is undefined. */ + /* */ + typedef FT_UInt + (*PS_Unicode_Index_Func)( FT_UInt num_glyphs, + const char** glyph_names, + FT_ULong unicode ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* PS_Macintosh_Name_Func */ + /* */ + /* <Description> */ + /* A function used to return the glyph name corresponding to an Apple */ + /* glyph name index. */ + /* */ + /* <Input> */ + /* name_index :: The index of the Mac name. */ + /* */ + /* <Return> */ + /* The glyph name, or 0 if the index is invalid. */ + /* */ + /* <Note> */ + /* This function will not be compiled if the configuration macro */ + /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES is undefined. */ + /* */ + typedef const char* + (*PS_Macintosh_Name_Func)( FT_UInt name_index ); + + + typedef const char* + (*PS_Adobe_Std_Strings_Func)( FT_UInt string_index ); + + + typedef struct PS_UniMap_ + { + FT_UInt unicode; + FT_UInt glyph_index; + + } PS_UniMap; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_Unicodes */ + /* */ + /* <Description> */ + /* A simple table used to map Unicode values to glyph indices. It is */ + /* built by the PS_Build_Unicodes table according to the glyphs */ + /* present in a font file. */ + /* */ + /* <Fields> */ + /* num_codes :: The number of glyphs in the font that match a given */ + /* Unicode value. */ + /* */ + /* unicodes :: An array of unicode values, sorted in increasing */ + /* order. */ + /* */ + /* gindex :: An array of glyph indices, corresponding to each */ + /* Unicode value. */ + /* */ + /* <Note> */ + /* Use the function PS_Lookup_Unicode() to retrieve the glyph index */ + /* corresponding to a given Unicode character code. */ + /* */ + typedef struct PS_Unicodes_ + { + FT_UInt num_maps; + PS_UniMap* maps; + + } PS_Unicodes; + + + typedef FT_Error + (*PS_Build_Unicodes_Func)( FT_Memory memory, + FT_UInt num_glyphs, + const char** glyph_names, + PS_Unicodes* unicodes ); + + typedef FT_UInt + (*PS_Lookup_Unicode_Func)( PS_Unicodes* unicodes, + FT_UInt unicode ); + + typedef FT_ULong + (*PS_Next_Unicode_Func)( PS_Unicodes* unicodes, + FT_ULong unicode ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PSNames_Interface */ + /* */ + /* <Description> */ + /* This structure defines the PSNames interface. */ + /* */ + /* <Fields> */ + /* unicode_value :: A function used to convert a glyph name */ + /* into a Unicode character code. */ + /* */ + /* build_unicodes :: A function which builds up the Unicode */ + /* mapping table. */ + /* */ + /* lookup_unicode :: A function used to return the glyph index */ + /* corresponding to a given Unicode */ + /* character. */ + /* */ + /* macintosh_name :: A function used to return the standard */ + /* Apple glyph Postscript name corresponding */ + /* to a given string index (used by the */ + /* TrueType `post' table). */ + /* */ + /* adobe_std_strings :: A function that returns a pointer to a */ + /* Adobe Standard String for a given SID. */ + /* */ + /* adobe_std_encoding :: A table of 256 unsigned shorts that maps */ + /* character codes in the Adobe Standard */ + /* Encoding to SIDs. */ + /* */ + /* adobe_expert_encoding :: A table of 256 unsigned shorts that maps */ + /* character codes in the Adobe Expert */ + /* Encoding to SIDs. */ + /* */ + /* <Note> */ + /* `unicode_value' and `unicode_index' will be set to 0 if the */ + /* configuration macro FT_CONFIG_OPTION_ADOBE_GLYPH_LIST is */ + /* undefined. */ + /* */ + /* `macintosh_name' will be set to 0 if the configuration macro */ + /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES is undefined. */ + /* */ + typedef struct PSNames_Interface_ + { + PS_Unicode_Value_Func unicode_value; + PS_Build_Unicodes_Func build_unicodes; + PS_Lookup_Unicode_Func lookup_unicode; + PS_Macintosh_Name_Func macintosh_name; + + PS_Adobe_Std_Strings_Func adobe_std_strings; + const unsigned short* adobe_std_encoding; + const unsigned short* adobe_expert_encoding; + + PS_Next_Unicode_Func next_unicode; + + } PSNames_Interface; + + + typedef PSNames_Interface* PSNames_Service; + + +FT_END_HEADER + +#endif /* __PSNAMES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/services/svbdf.h b/extra_lib/include/freetype/freetype/internal/services/svbdf.h new file mode 100644 index 0000000..0f7fc61 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/services/svbdf.h @@ -0,0 +1,57 @@ +/***************************************************************************/ +/* */ +/* svbdf.h */ +/* */ +/* The FreeType BDF services (specification). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVBDF_H__ +#define __SVBDF_H__ + +#include FT_BDF_H +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_BDF "bdf" + + typedef FT_Error + (*FT_BDF_GetCharsetIdFunc)( FT_Face face, + const char* *acharset_encoding, + const char* *acharset_registry ); + + typedef FT_Error + (*FT_BDF_GetPropertyFunc)( FT_Face face, + const char* prop_name, + BDF_PropertyRec *aproperty ); + + + FT_DEFINE_SERVICE( BDF ) + { + FT_BDF_GetCharsetIdFunc get_charset_id; + FT_BDF_GetPropertyFunc get_property; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* __SVBDF_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/services/svgldict.h b/extra_lib/include/freetype/freetype/internal/services/svgldict.h new file mode 100644 index 0000000..e5e56b2 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/services/svgldict.h @@ -0,0 +1,60 @@ +/***************************************************************************/ +/* */ +/* svgldict.h */ +/* */ +/* The FreeType glyph dictionary services (specification). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVGLDICT_H__ +#define __SVGLDICT_H__ + +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + + /* + * A service used to retrieve glyph names, as well as to find the + * index of a given glyph name in a font. + * + */ + +#define FT_SERVICE_ID_GLYPH_DICT "glyph-dict" + + + typedef FT_Error + (*FT_GlyphDict_GetNameFunc)( FT_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ); + + typedef FT_UInt + (*FT_GlyphDict_NameIndexFunc)( FT_Face face, + FT_String* glyph_name ); + + + FT_DEFINE_SERVICE( GlyphDict ) + { + FT_GlyphDict_GetNameFunc get_name; + FT_GlyphDict_NameIndexFunc name_index; /* optional */ + }; + + /* */ + + +FT_END_HEADER + + +#endif /* __SVGLDICT_H__ */ diff --git a/extra_lib/include/freetype/freetype/internal/services/svmm.h b/extra_lib/include/freetype/freetype/internal/services/svmm.h new file mode 100644 index 0000000..926975c --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/services/svmm.h @@ -0,0 +1,68 @@ +/***************************************************************************/ +/* */ +/* svmm.h */ +/* */ +/* The FreeType Multiple Masters services (specification). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVMM_H__ +#define __SVMM_H__ + +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + + /* + * A service used to manage multiple-masters data in a given face. + * + * See the related APIs in `ftmm.h' (FT_MULTIPLE_MASTERS_H). + * + */ + +#define FT_SERVICE_ID_MULTI_MASTERS "multi-masters" + + + typedef FT_Error + (*FT_Get_MM_Func)( FT_Face face, + FT_Multi_Master* master ); + + typedef FT_Error + (*FT_Set_MM_Design_Func)( FT_Face face, + FT_UInt num_coords, + FT_Long* coords ); + + typedef FT_Error + (*FT_Set_MM_Blend_Func)( FT_Face face, + FT_UInt num_coords, + FT_Long* coords ); + + + FT_DEFINE_SERVICE( MultiMasters ) + { + FT_Get_MM_Func get_mm; + FT_Set_MM_Design_Func set_mm_design; + FT_Set_MM_Blend_Func set_mm_blend; + }; + + /* */ + + +FT_END_HEADER + +#endif /* __SVMM_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/services/svpfr.h b/extra_lib/include/freetype/freetype/internal/services/svpfr.h new file mode 100644 index 0000000..b610505 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/services/svpfr.h @@ -0,0 +1,65 @@ +/***************************************************************************/ +/* */ +/* svpfr.h */ +/* */ +/* Internal PFR service functions (specification). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVPFR_H__ +#define __SVPFR_H__ + +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_PFR_METRICS "pfr-metrics" + + + typedef FT_Error + (*FT_PFR_GetMetricsFunc)( FT_Face face, + FT_UInt *aoutline, + FT_UInt *ametrics, + FT_Fixed *ax_scale, + FT_Fixed *ay_scale ); + + typedef FT_Error + (*FT_PFR_GetKerningFunc)( FT_Face face, + FT_UInt left, + FT_UInt right, + FT_Vector *avector ); + + typedef FT_Error + (*FT_PFR_GetAdvanceFunc)( FT_Face face, + FT_UInt gindex, + FT_Pos *aadvance ); + + + FT_DEFINE_SERVICE( PfrMetrics ) + { + FT_PFR_GetMetricsFunc get_metrics; + FT_PFR_GetKerningFunc get_kerning; + FT_PFR_GetAdvanceFunc get_advance; + + }; + + /* */ + +FT_END_HEADER + +#endif /* __SVPFR_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/services/svpostnm.h b/extra_lib/include/freetype/freetype/internal/services/svpostnm.h new file mode 100644 index 0000000..7f1700a --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/services/svpostnm.h @@ -0,0 +1,58 @@ +/***************************************************************************/ +/* */ +/* svpostnm.h */ +/* */ +/* The FreeType PostScript name services (specification). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVPOSTNM_H__ +#define __SVPOSTNM_H__ + +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + /* + * A trivial service used to retrieve the PostScript name of a given + * font when available. The `get_name' field should never be NULL. + * + * The correponding function can return NULL to indicate that the + * PostScript name is not available. + * + * The name is owned by the face and will be destroyed with it. + */ + +#define FT_SERVICE_ID_POSTSCRIPT_FONT_NAME "postscript-font-name" + + + typedef const char* + (*FT_PsName_GetFunc)( FT_Face face ); + + + FT_DEFINE_SERVICE( PsFontName ) + { + FT_PsName_GetFunc get_ps_font_name; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* __SVPOSTNM_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/services/svpscmap.h b/extra_lib/include/freetype/freetype/internal/services/svpscmap.h new file mode 100644 index 0000000..ade96ef --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/services/svpscmap.h @@ -0,0 +1,113 @@ +/***************************************************************************/ +/* */ +/* svpscmap.h */ +/* */ +/* The FreeType PostScript charmap service (specification). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVPSCMAP_H__ +#define __SVPSCMAP_H__ + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_POSTSCRIPT_CMAPS "postscript-cmaps" + + + /* + * Adobe glyph name to unicode value + */ + typedef FT_UInt32 + (*PS_Unicode_ValueFunc)( const char* glyph_name ); + + /* + * Unicode value to Adobe glyph name index. 0xFFFF if not found. + */ + typedef FT_UInt + (*PS_Unicode_Index_Func)( FT_UInt num_glyphs, + const char** glyph_names, + FT_ULong unicode ); + + /* + * Macintosh name id to glyph name. NULL if invalid index. + */ + typedef const char* + (*PS_Macintosh_Name_Func)( FT_UInt name_index ); + + /* + * Adobe standard string ID to glyph name. NULL if invalid index. + */ + typedef const char* + (*PS_Adobe_Std_Strings_Func)( FT_UInt string_index ); + + /* + * Simple unicode -> glyph index charmap built from font glyph names + * table. + */ + typedef struct PS_UniMap_ + { + FT_UInt unicode; + FT_UInt glyph_index; + + } PS_UniMap; + + + typedef struct PS_Unicodes_ + { + FT_UInt num_maps; + PS_UniMap* maps; + + } PS_Unicodes; + + + typedef FT_Error + (*PS_Unicodes_InitFunc)( FT_Memory memory, + FT_UInt num_glyphs, + const char** glyph_names, + PS_Unicodes* unicodes ); + + typedef FT_UInt + (*PS_Unicodes_CharIndexFunc)( PS_Unicodes* unicodes, + FT_UInt unicode ); + + typedef FT_ULong + (*PS_Unicodes_CharNextFunc)( PS_Unicodes* unicodes, + FT_ULong unicode ); + + + FT_DEFINE_SERVICE( PsCMaps ) + { + PS_Unicode_ValueFunc unicode_value; + + PS_Unicodes_InitFunc unicodes_init; + PS_Unicodes_CharIndexFunc unicodes_char_index; + PS_Unicodes_CharNextFunc unicodes_char_next; + + PS_Macintosh_Name_Func macintosh_name; + PS_Adobe_Std_Strings_Func adobe_std_strings; + const unsigned short* adobe_std_encoding; + const unsigned short* adobe_expert_encoding; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* __SVPSCMAP_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/services/svpsinfo.h b/extra_lib/include/freetype/freetype/internal/services/svpsinfo.h new file mode 100644 index 0000000..73c02cd --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/services/svpsinfo.h @@ -0,0 +1,55 @@ +/***************************************************************************/ +/* */ +/* svpsinfo.h */ +/* */ +/* The FreeType PostScript info service (specification). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVPSINFO_H__ +#define __SVPSINFO_H__ + +#include FT_INTERNAL_SERVICE_H +#include FT_INTERNAL_TYPE1_TYPES_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_POSTSCRIPT_INFO "postscript-info" + + + typedef FT_Error + (*PS_GetFontInfoFunc)( FT_Face face, + PS_FontInfoRec* afont_info ); + + typedef FT_Int + (*PS_HasGlyphNamesFunc)( FT_Face face ); + + + FT_DEFINE_SERVICE( PsInfo ) + { + PS_GetFontInfoFunc ps_get_font_info; + PS_HasGlyphNamesFunc ps_has_glyph_names; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* __SVPSINFO_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/services/svsfnt.h b/extra_lib/include/freetype/freetype/internal/services/svsfnt.h new file mode 100644 index 0000000..06a5b1c --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/services/svsfnt.h @@ -0,0 +1,69 @@ +/***************************************************************************/ +/* */ +/* svsfnt.h */ +/* */ +/* The FreeType PostScript name services (specification). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVSFNT_H__ +#define __SVSFNT_H__ + +#include FT_INTERNAL_SERVICE_H +#include FT_TRUETYPE_TABLES_H + + +FT_BEGIN_HEADER + + + /* + * SFNT table loading service. + */ + +#define FT_SERVICE_ID_SFNT_TABLE "sfnt-table" + + + /* + * Used to implement FT_Load_Sfnt_Table(). + */ + typedef FT_Error + (*FT_SFNT_TableLoadFunc)( FT_Face face, + FT_ULong tag, + FT_Long offset, + FT_Byte* buffer, + FT_ULong* length ); + + /* + * Used to implement FT_Get_Sfnt_Table(). + */ + typedef void* + (*FT_SFNT_TableGetFunc)( FT_Face face, + FT_Sfnt_Tag tag ); + + + FT_DEFINE_SERVICE( SFNT_Table ) + { + FT_SFNT_TableLoadFunc load_table; + FT_SFNT_TableGetFunc get_table; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* __SVSFNT_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/services/svwinfnt.h b/extra_lib/include/freetype/freetype/internal/services/svwinfnt.h new file mode 100644 index 0000000..57f7765 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/services/svwinfnt.h @@ -0,0 +1,50 @@ +/***************************************************************************/ +/* */ +/* svwinfnt.h */ +/* */ +/* The FreeType Windows FNT/FONT service (specification). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVWINFNT_H__ +#define __SVWINFNT_H__ + +#include FT_INTERNAL_SERVICE_H +#include FT_WINFONTS_H + + +FT_BEGIN_HEADER + + +#define FT_SERVICE_ID_WINFNT "winfonts" + + typedef FT_Error + (*FT_WinFnt_GetHeaderFunc)( FT_Face face, + FT_WinFNT_HeaderRec *aheader ); + + + FT_DEFINE_SERVICE( WinFnt ) + { + FT_WinFnt_GetHeaderFunc get_header; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* __SVWINFNT_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/services/svxf86nm.h b/extra_lib/include/freetype/freetype/internal/services/svxf86nm.h new file mode 100644 index 0000000..3a33abc --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/services/svxf86nm.h @@ -0,0 +1,55 @@ +/***************************************************************************/ +/* */ +/* svxf86nm.h */ +/* */ +/* The FreeType XFree86 services (specification only). */ +/* */ +/* Copyright 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVXF86NM_H__ +#define __SVXF86NM_H__ + +#include FT_INTERNAL_SERVICE_H + + +FT_BEGIN_HEADER + + + /* + * A trivial service used to return the name of a face's font driver, + * according to the XFree86 nomenclature. Note that the service data + * is a simple constant string pointer. + */ + +#define FT_SERVICE_ID_XF86_NAME "xf86-driver-name" + +#define FT_XF86_FORMAT_TRUETYPE "TrueType" +#define FT_XF86_FORMAT_TYPE_1 "Type 1" +#define FT_XF86_FORMAT_BDF "BDF" +#define FT_XF86_FORMAT_PCF "PCF" +#define FT_XF86_FORMAT_TYPE_42 "Type 42" +#define FT_XF86_FORMAT_CID "CID Type 1" +#define FT_XF86_FORMAT_CFF "CFF" +#define FT_XF86_FORMAT_PFR "PFR" +#define FT_XF86_FORMAT_WINFNT "Windows FNT" + + /* */ + + +FT_END_HEADER + + +#endif /* __SVXF86NM_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/sfnt.h b/extra_lib/include/freetype/freetype/internal/sfnt.h new file mode 100644 index 0000000..76a92e8 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/sfnt.h @@ -0,0 +1,529 @@ +/***************************************************************************/ +/* */ +/* sfnt.h */ +/* */ +/* High-level `sfnt' driver interface (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __SFNT_H__ +#define __SFNT_H__ + + +#include <ft2build.h> +#include FT_INTERNAL_DRIVER_H +#include FT_INTERNAL_TRUETYPE_TYPES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Init_Face_Func */ + /* */ + /* <Description> */ + /* First part of the SFNT face object initialization. This will find */ + /* the face in a SFNT file or collection, and load its format tag in */ + /* face->format_tag. */ + /* */ + /* <Input> */ + /* stream :: The input stream. */ + /* */ + /* face :: A handle to the target face object. */ + /* */ + /* face_index :: The index of the TrueType font, if we are opening a */ + /* collection. */ + /* */ + /* num_params :: The number of additional parameters. */ + /* */ + /* params :: Optional additional parameters. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The stream cursor must be at the font file's origin. */ + /* */ + /* This function recognizes fonts embedded in a `TrueType */ + /* collection'. */ + /* */ + /* Once the format tag has been validated by the font driver, it */ + /* should then call the TT_Load_Face_Func() callback to read the rest */ + /* of the SFNT tables in the object. */ + /* */ + typedef FT_Error + (*TT_Init_Face_Func)( FT_Stream stream, + TT_Face face, + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_Face_Func */ + /* */ + /* <Description> */ + /* Second part of the SFNT face object initialization. This will */ + /* load the common SFNT tables (head, OS/2, maxp, metrics, etc.) in */ + /* the face object. */ + /* */ + /* <Input> */ + /* stream :: The input stream. */ + /* */ + /* face :: A handle to the target face object. */ + /* */ + /* face_index :: The index of the TrueType font, if we are opening a */ + /* collection. */ + /* */ + /* num_params :: The number of additional parameters. */ + /* */ + /* params :: Optional additional parameters. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function must be called after TT_Init_Face_Func(). */ + /* */ + typedef FT_Error + (*TT_Load_Face_Func)( FT_Stream stream, + TT_Face face, + FT_Int face_index, + FT_Int num_params, + FT_Parameter* params ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Done_Face_Func */ + /* */ + /* <Description> */ + /* A callback used to delete the common SFNT data from a face. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Note> */ + /* This function does NOT destroy the face object. */ + /* */ + typedef void + (*TT_Done_Face_Func)( TT_Face face ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_SFNT_HeaderRec_Func */ + /* */ + /* <Description> */ + /* Loads the header of a SFNT font file. Supports collections. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* face_index :: The index of the TrueType font, if we are opening a */ + /* collection. */ + /* */ + /* <Output> */ + /* sfnt :: The SFNT header. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The stream cursor must be at the font file's origin. */ + /* */ + /* This function recognizes fonts embedded in a `TrueType */ + /* collection'. */ + /* */ + /* This function checks that the header is valid by looking at the */ + /* values of `search_range', `entry_selector', and `range_shift'. */ + /* */ + typedef FT_Error + (*TT_Load_SFNT_HeaderRec_Func)( TT_Face face, + FT_Stream stream, + FT_Long face_index, + SFNT_Header sfnt ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_Directory_Func */ + /* */ + /* <Description> */ + /* Loads the table directory into a face object. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* sfnt :: The SFNT header. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The stream cursor must be on the first byte after the 4-byte font */ + /* format tag. This is the case just after a call to */ + /* TT_Load_Format_Tag(). */ + /* */ + typedef FT_Error + (*TT_Load_Directory_Func)( TT_Face face, + FT_Stream stream, + SFNT_Header sfnt ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_Any_Func */ + /* */ + /* <Description> */ + /* Loads any font table into client memory. */ + /* */ + /* <Input> */ + /* face :: The face object to look for. */ + /* */ + /* tag :: The tag of table to load. Use the value 0 if you want */ + /* to access the whole font file, else set this parameter */ + /* to a valid TrueType table tag that you can forge with */ + /* the MAKE_TT_TAG macro. */ + /* */ + /* offset :: The starting offset in the table (or the file if */ + /* tag == 0). */ + /* */ + /* length :: The address of the decision variable: */ + /* */ + /* If length == NULL: */ + /* Loads the whole table. Returns an error if */ + /* `offset' == 0! */ + /* */ + /* If *length == 0: */ + /* Exits immediately; returning the length of the given */ + /* table or of the font file, depending on the value of */ + /* `tag'. */ + /* */ + /* If *length != 0: */ + /* Loads the next `length' bytes of table or font, */ + /* starting at offset `offset' (in table or font too). */ + /* */ + /* <Output> */ + /* buffer :: The address of target buffer. */ + /* */ + /* <Return> */ + /* TrueType error code. 0 means success. */ + /* */ + typedef FT_Error + (*TT_Load_Any_Func)( TT_Face face, + FT_ULong tag, + FT_Long offset, + FT_Byte *buffer, + FT_ULong* length ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_SBit_Image_Func */ + /* */ + /* <Description> */ + /* Loads a given glyph sbit image from the font resource. This also */ + /* returns its metrics. */ + /* */ + /* <Input> */ + /* face :: The target face object. */ + /* */ + /* x_ppem :: The horizontal resolution in points per EM. */ + /* */ + /* y_ppem :: The vertical resolution in points per EM. */ + /* */ + /* glyph_index :: The current glyph index. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* <Output> */ + /* amap :: The target pixmap. */ + /* */ + /* ametrics :: A big sbit metrics structure for the glyph image. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. Returns an error if no */ + /* glyph sbit exists for the index. */ + /* */ + /* <Note> */ + /* The `map.buffer' field is always freed before the glyph is loaded. */ + /* */ + typedef FT_Error + (*TT_Load_SBit_Image_Func)( TT_Face face, + FT_ULong strike_index, + FT_UInt glyph_index, + FT_UInt load_flags, + FT_Stream stream, + FT_Bitmap *amap, + TT_SBit_MetricsRec *ametrics ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Set_SBit_Strike_Func */ + /* */ + /* <Description> */ + /* Selects an sbit strike for given horizontal and vertical ppem */ + /* values. */ + /* */ + /* <Input> */ + /* face :: The target face object. */ + /* */ + /* x_ppem :: The horizontal resolution in points per EM. */ + /* */ + /* y_ppem :: The vertical resolution in points per EM. */ + /* */ + /* <Output> */ + /* astrike_index :: The index of the sbit strike. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. Returns an error if no */ + /* sbit strike exists for the selected ppem values. */ + /* */ + typedef FT_Error + (*TT_Set_SBit_Strike_Func)( TT_Face face, + FT_Int x_ppem, + FT_Int y_ppem, + FT_ULong *astrike_index ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Get_PS_Name_Func */ + /* */ + /* <Description> */ + /* Gets the PostScript glyph name of a glyph. */ + /* */ + /* <Input> */ + /* idx :: The glyph index. */ + /* */ + /* PSname :: The address of a string pointer. Will be NULL in case */ + /* of error, otherwise it is a pointer to the glyph name. */ + /* */ + /* You must not modify the returned string! */ + /* */ + /* <Output> */ + /* FreeType error code. 0 means success. */ + /* */ + typedef FT_Error + (*TT_Get_PS_Name_Func)( TT_Face face, + FT_UInt idx, + FT_String** PSname ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_Metrics_Func */ + /* */ + /* <Description> */ + /* Loads the horizontal or vertical header in a face object. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* vertical :: A boolean flag. If set, load vertical metrics. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + typedef FT_Error + (*TT_Load_Metrics_Func)( TT_Face face, + FT_Stream stream, + FT_Bool vertical ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_CharMap_Load_Func */ + /* */ + /* <Description> */ + /* Loads a given TrueType character map into memory. */ + /* */ + /* <Input> */ + /* face :: A handle to the parent face object. */ + /* */ + /* stream :: A handle to the current stream object. */ + /* */ + /* <InOut> */ + /* cmap :: A pointer to a cmap object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The function assumes that the stream is already in use (i.e., */ + /* opened). In case of error, all partially allocated tables are */ + /* released. */ + /* */ + typedef FT_Error + (*TT_CharMap_Load_Func)( TT_Face face, + TT_CMapTable cmap, + FT_Stream input ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_CharMap_Free_Func */ + /* */ + /* <Description> */ + /* Destroys a character mapping table. */ + /* */ + /* <Input> */ + /* face :: A handle to the parent face object. */ + /* */ + /* cmap :: A handle to a cmap object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + typedef FT_Error + (*TT_CharMap_Free_Func)( TT_Face face, + TT_CMapTable cmap ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Load_Table_Func */ + /* */ + /* <Description> */ + /* Loads a given TrueType table. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The function will use `face->goto_table' to seek the stream to */ + /* the start of the table. */ + /* */ + typedef FT_Error + (*TT_Load_Table_Func)( TT_Face face, + FT_Stream stream ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Free_Table_Func */ + /* */ + /* <Description> */ + /* Frees a given TrueType table. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + typedef void + (*TT_Free_Table_Func)( TT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* SFNT_Interface */ + /* */ + /* <Description> */ + /* This structure holds pointers to the functions used to load and */ + /* free the basic tables that are required in a `sfnt' font file. */ + /* */ + /* <Fields> */ + /* Check the various xxx_Func() descriptions for details. */ + /* */ + typedef struct SFNT_Interface_ + { + TT_Loader_GotoTableFunc goto_table; + + TT_Init_Face_Func init_face; + TT_Load_Face_Func load_face; + TT_Done_Face_Func done_face; + FT_Module_Requester get_interface; + + TT_Load_Any_Func load_any; + TT_Load_SFNT_HeaderRec_Func load_sfnt_header; + TT_Load_Directory_Func load_directory; + + /* these functions are called by `load_face' but they can also */ + /* be called from external modules, if there is a need to do so */ + TT_Load_Table_Func load_header; + TT_Load_Metrics_Func load_metrics; + TT_Load_Table_Func load_charmaps; + TT_Load_Table_Func load_max_profile; + TT_Load_Table_Func load_os2; + TT_Load_Table_Func load_psnames; + + TT_Load_Table_Func load_names; + TT_Free_Table_Func free_names; + + /* optional tables */ + TT_Load_Table_Func load_hdmx; + TT_Free_Table_Func free_hdmx; + + TT_Load_Table_Func load_kerning; + TT_Load_Table_Func load_gasp; + TT_Load_Table_Func load_pclt; + + /* see `ttload.h' */ + TT_Load_Table_Func load_bitmap_header; + + /* see `ttsbit.h' */ + TT_Set_SBit_Strike_Func set_sbit_strike; + TT_Load_Table_Func load_sbits; + TT_Load_SBit_Image_Func load_sbit_image; + TT_Free_Table_Func free_sbits; + + /* see `ttpost.h' */ + TT_Get_PS_Name_Func get_psname; + TT_Free_Table_Func free_psnames; + + /* see `ttcmap.h' */ + TT_CharMap_Load_Func load_charmap; + TT_CharMap_Free_Func free_charmap; + + } SFNT_Interface; + + + /* transitional */ + typedef SFNT_Interface* SFNT_Service; + + +FT_END_HEADER + +#endif /* __SFNT_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/t1types.h b/extra_lib/include/freetype/freetype/internal/t1types.h new file mode 100644 index 0000000..08bdfb1 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/t1types.h @@ -0,0 +1,200 @@ +/***************************************************************************/ +/* */ +/* t1types.h */ +/* */ +/* Basic Type1/Type2 type definitions and interface (specification */ +/* only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __T1TYPES_H__ +#define __T1TYPES_H__ + + +#include <ft2build.h> +#include FT_TYPE1_TABLES_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H +#include FT_INTERNAL_SERVICE_H +#include FT_SERVICE_POSTSCRIPT_CMAPS_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** REQUIRED TYPE1/TYPE2 TABLES DEFINITIONS ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* T1_EncodingRec */ + /* */ + /* <Description> */ + /* A structure modeling a custom encoding. */ + /* */ + /* <Fields> */ + /* num_chars :: The number of character codes in the encoding. */ + /* Usually 256. */ + /* */ + /* code_first :: The lowest valid character code in the encoding. */ + /* */ + /* code_last :: The highest valid character code in the encoding. */ + /* */ + /* char_index :: An array of corresponding glyph indices. */ + /* */ + /* char_name :: An array of corresponding glyph names. */ + /* */ + typedef struct T1_EncodingRecRec_ + { + FT_Int num_chars; + FT_Int code_first; + FT_Int code_last; + + FT_UShort* char_index; + FT_String** char_name; + + } T1_EncodingRec, *T1_Encoding; + + + typedef enum T1_EncodingType_ + { + T1_ENCODING_TYPE_NONE = 0, + T1_ENCODING_TYPE_ARRAY, + T1_ENCODING_TYPE_STANDARD, + T1_ENCODING_TYPE_ISOLATIN1, + T1_ENCODING_TYPE_EXPERT + + } T1_EncodingType; + + + typedef struct T1_FontRec_ + { + PS_FontInfoRec font_info; /* font info dictionary */ + PS_PrivateRec private_dict; /* private dictionary */ + FT_String* font_name; /* top-level dictionary */ + + T1_EncodingType encoding_type; + T1_EncodingRec encoding; + + FT_Byte* subrs_block; + FT_Byte* charstrings_block; + FT_Byte* glyph_names_block; + + FT_Int num_subrs; + FT_Byte** subrs; + FT_Int* subrs_len; + + FT_Int num_glyphs; + FT_String** glyph_names; /* array of glyph names */ + FT_Byte** charstrings; /* array of glyph charstrings */ + FT_Int* charstrings_len; + + FT_Byte paint_type; + FT_Byte font_type; + FT_Matrix font_matrix; + FT_Vector font_offset; + FT_BBox font_bbox; + FT_Long font_id; + + FT_Fixed stroke_width; + + } T1_FontRec, *T1_Font; + + + typedef struct CID_SubrsRec_ + { + FT_UInt num_subrs; + FT_Byte** code; + + } CID_SubrsRec, *CID_Subrs; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** ORIGINAL T1_FACE CLASS DEFINITION ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This structure/class is defined here because it is common to the */ + /* following formats: TTF, OpenType-TT, and OpenType-CFF. */ + /* */ + /* Note, however, that the classes TT_Size, TT_GlyphSlot, and TT_CharMap */ + /* are not shared between font drivers, and are thus defined normally in */ + /* `ttobjs.h'. */ + /* */ + /*************************************************************************/ + + typedef struct T1_FaceRec_* T1_Face; + typedef struct CID_FaceRec_* CID_Face; + + + typedef struct T1_FaceRec_ + { + FT_FaceRec root; + T1_FontRec type1; + const void* psnames; + const void* psaux; + const void* afm_data; + FT_CharMapRec charmaprecs[2]; + FT_CharMap charmaps[2]; + PS_Unicodes unicode_map; + + /* support for Multiple Masters fonts */ + PS_Blend blend; + + /* since FT 2.1 - interface to PostScript hinter */ + const void* pshinter; + + } T1_FaceRec; + + + typedef struct CID_FaceRec_ + { + FT_FaceRec root; + void* psnames; + void* psaux; + CID_FaceInfoRec cid; + void* afm_data; + CID_Subrs subrs; + + /* since FT 2.1 - interface to PostScript hinter */ + void* pshinter; + + } CID_FaceRec; + + +FT_END_HEADER + +#endif /* __T1TYPES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/t42types.h b/extra_lib/include/freetype/freetype/internal/t42types.h new file mode 100644 index 0000000..7562252 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/t42types.h @@ -0,0 +1,55 @@ +/***************************************************************************/ +/* */ +/* t42types.h */ +/* */ +/* Type 42 font data types (specification only). */ +/* */ +/* Copyright 2002 by Roberto Alameda. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __T42TYPES_H__ +#define __T42TYPES_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TYPE1_TABLES_H +#include FT_INTERNAL_TYPE1_TYPES_H +#include FT_INTERNAL_POSTSCRIPT_NAMES_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H + + +FT_BEGIN_HEADER + + + typedef struct T42_FaceRec_ + { + FT_FaceRec root; + T1_FontRec type1; + const void* psnames; + const void* psaux; + const void* afm_data; + FT_Byte* ttf_data; + FT_ULong ttf_size; + FT_Face ttf_face; + FT_CharMapRec charmaprecs[2]; + FT_CharMap charmaps[2]; + PS_Unicodes unicode_map; + + } T42_FaceRec, *T42_Face; + + +FT_END_HEADER + +#endif /* __T1TYPES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/internal/tttypes.h b/extra_lib/include/freetype/freetype/internal/tttypes.h new file mode 100644 index 0000000..2b419f1 --- /dev/null +++ b/extra_lib/include/freetype/freetype/internal/tttypes.h @@ -0,0 +1,1678 @@ +/***************************************************************************/ +/* */ +/* tttypes.h */ +/* */ +/* Basic SFNT/TrueType type definitions and interface (specification */ +/* only). */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __TTTYPES_H__ +#define __TTTYPES_H__ + + +#include <ft2build.h> +#include FT_TRUETYPE_TABLES_H +#include FT_INTERNAL_OBJECTS_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** REQUIRED TRUETYPE/OPENTYPE TABLES DEFINITIONS ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TTC_HeaderRec */ + /* */ + /* <Description> */ + /* TrueType collection header. This table contains the offsets of */ + /* the font headers of each distinct TrueType face in the file. */ + /* */ + /* <Fields> */ + /* tag :: Must be `ttc ' to indicate a TrueType collection. */ + /* */ + /* version :: The version number. */ + /* */ + /* count :: The number of faces in the collection. The */ + /* specification says this should be an unsigned long, but */ + /* we use a signed long since we need the value -1 for */ + /* specific purposes. */ + /* */ + /* offsets :: The offsets of the font headers, one per face. */ + /* */ + typedef struct TTC_HeaderRec_ + { + FT_ULong tag; + FT_Fixed version; + FT_Long count; + FT_ULong* offsets; + + } TTC_HeaderRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* SFNT_HeaderRec */ + /* */ + /* <Description> */ + /* SFNT file format header. */ + /* */ + /* <Fields> */ + /* format_tag :: The font format tag. */ + /* */ + /* num_tables :: The number of tables in file. */ + /* */ + /* search_range :: Must be `16 * (max power of 2 <= num_tables)'. */ + /* */ + /* entry_selector :: Must be log2 of `search_range / 16'. */ + /* */ + /* range_shift :: Must be `num_tables * 16 - search_range'. */ + /* */ + typedef struct SFNT_HeaderRec_ + { + FT_ULong format_tag; + FT_UShort num_tables; + FT_UShort search_range; + FT_UShort entry_selector; + FT_UShort range_shift; + + FT_ULong offset; /* not in file */ + + } SFNT_HeaderRec, *SFNT_Header; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_TableDirRec */ + /* */ + /* <Description> */ + /* This structure models a TrueType table directory. It is used to */ + /* access the various tables of the font face. */ + /* */ + /* <Fields> */ + /* version :: The version number; starts with 0x00010000. */ + /* */ + /* numTables :: The number of tables. */ + /* */ + /* searchRange :: Unused. */ + /* */ + /* entrySelector :: Unused. */ + /* */ + /* rangeShift :: Unused. */ + /* */ + /* <Note> */ + /* This structure is only used during font opening. */ + /* */ + typedef struct TT_TableDirRec_ + { + FT_Fixed version; /* should be 0x10000 */ + FT_UShort numTables; /* number of tables */ + + FT_UShort searchRange; /* These parameters are only used */ + FT_UShort entrySelector; /* for a dichotomy search in the */ + FT_UShort rangeShift; /* directory. We ignore them. */ + + } TT_TableDirRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_TableRec */ + /* */ + /* <Description> */ + /* This structure describes a given table of a TrueType font. */ + /* */ + /* <Fields> */ + /* Tag :: A four-bytes tag describing the table. */ + /* */ + /* CheckSum :: The table checksum. This value can be ignored. */ + /* */ + /* Offset :: The offset of the table from the start of the TrueType */ + /* font in its resource. */ + /* */ + /* Length :: The table length (in bytes). */ + /* */ + typedef struct TT_TableRec_ + { + FT_ULong Tag; /* table type */ + FT_ULong CheckSum; /* table checksum */ + FT_ULong Offset; /* table file offset */ + FT_ULong Length; /* table length */ + + } TT_TableRec, *TT_Table; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_CMapDirRec */ + /* */ + /* <Description> */ + /* This structure describes the directory of the `cmap' table, */ + /* containing the font's character mappings table. */ + /* */ + /* <Fields> */ + /* tableVersionNumber :: The version number. */ + /* */ + /* numCMaps :: The number of charmaps in the font. */ + /* */ + /* <Note> */ + /* This structure is only used during font loading. */ + /* */ + typedef struct TT_CMapDirRec_ + { + FT_UShort tableVersionNumber; + FT_UShort numCMaps; + + } TT_CMapDirRec, *TT_CMapDir; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_CMapDirEntryRec */ + /* */ + /* <Description> */ + /* This structure describes a charmap in a TrueType font. */ + /* */ + /* <Fields> */ + /* platformID :: An ID used to specify for which platform this */ + /* charmap is defined (FreeType manages all platforms). */ + /* */ + /* encodingID :: A platform-specific ID used to indicate which source */ + /* encoding is used in this charmap. */ + /* */ + /* offset :: The offset of the charmap relative to the start of */ + /* the `cmap' table. */ + /* */ + /* <Note> */ + /* This structure is only used during font loading. */ + /* */ + typedef struct TT_CMapDirEntryRec_ + { + FT_UShort platformID; + FT_UShort platformEncodingID; + FT_Long offset; + + } TT_CMapDirEntryRec, *TT_CMapDirEntry; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_LongMetricsRec */ + /* */ + /* <Description> */ + /* A structure modeling the long metrics of the `hmtx' and `vmtx' */ + /* TrueType tables. The values are expressed in font units. */ + /* */ + /* <Fields> */ + /* advance :: The advance width or height for the glyph. */ + /* */ + /* bearing :: The left-side or top-side bearing for the glyph. */ + /* */ + typedef struct TT_LongMetricsRec_ + { + FT_UShort advance; + FT_Short bearing; + + } TT_LongMetricsRec, *TT_LongMetrics; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* TT_ShortMetrics */ + /* */ + /* <Description> */ + /* A simple type to model the short metrics of the `hmtx' and `vmtx' */ + /* tables. */ + /* */ + typedef FT_Short TT_ShortMetrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_NameEntryRec */ + /* */ + /* <Description> */ + /* A structure modeling TrueType name records. Name records are used */ + /* to store important strings like family name, style name, */ + /* copyright, etc. in _localized_ versions (i.e., language, encoding, */ + /* etc). */ + /* */ + /* <Fields> */ + /* platformID :: The ID of the name's encoding platform. */ + /* */ + /* encodingID :: The platform-specific ID for the name's encoding. */ + /* */ + /* languageID :: The platform-specific ID for the name's language. */ + /* */ + /* nameID :: The ID specifying what kind of name this is. */ + /* */ + /* stringLength :: The length of the string in bytes. */ + /* */ + /* stringOffset :: The offset to the string in the `name' table. */ + /* */ + /* string :: A pointer to the string's bytes. Note that these */ + /* are usually UTF-16 encoded characters. */ + /* */ + typedef struct TT_NameEntryRec_ + { + FT_UShort platformID; + FT_UShort encodingID; + FT_UShort languageID; + FT_UShort nameID; + FT_UShort stringLength; + FT_ULong stringOffset; + + /* this last field is not defined in the spec */ + /* but used by the FreeType engine */ + + FT_Byte* string; + + } TT_NameEntryRec, *TT_NameEntry; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_NameTableRec */ + /* */ + /* <Description> */ + /* A structure modeling the TrueType name table. */ + /* */ + /* <Fields> */ + /* format :: The format of the name table. */ + /* */ + /* numNameRecords :: The number of names in table. */ + /* */ + /* storageOffset :: The offset of the name table in the `name' */ + /* TrueType table. */ + /* */ + /* names :: An array of name records. */ + /* */ + /* stream :: the file's input stream. */ + /* */ + typedef struct TT_NameTableRec_ + { + FT_UShort format; + FT_UInt numNameRecords; + FT_UInt storageOffset; + TT_NameEntryRec* names; + FT_Stream stream; + + } TT_NameTableRec, *TT_NameTable; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** OPTIONAL TRUETYPE/OPENTYPE TABLES DEFINITIONS ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_GaspRangeRec */ + /* */ + /* <Description> */ + /* A tiny structure used to model a gasp range according to the */ + /* TrueType specification. */ + /* */ + /* <Fields> */ + /* maxPPEM :: The maximum ppem value to which `gaspFlag' applies. */ + /* */ + /* gaspFlag :: A flag describing the grid-fitting and anti-aliasing */ + /* modes to be used. */ + /* */ + typedef struct TT_GaspRangeRec_ + { + FT_UShort maxPPEM; + FT_UShort gaspFlag; + + } TT_GaspRangeRec, *TT_GaspRange; + + +#define TT_GASP_GRIDFIT 0x01 +#define TT_GASP_DOGRAY 0x02 + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_GaspRec */ + /* */ + /* <Description> */ + /* A structure modeling the TrueType `gasp' table used to specify */ + /* grid-fitting and anti-aliasing behaviour. */ + /* */ + /* <Fields> */ + /* version :: The version number. */ + /* */ + /* numRanges :: The number of gasp ranges in table. */ + /* */ + /* gaspRanges :: An array of gasp ranges. */ + /* */ + typedef struct TT_Gasp_ + { + FT_UShort version; + FT_UShort numRanges; + TT_GaspRange gaspRanges; + + } TT_GaspRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_HdmxEntryRec */ + /* */ + /* <Description> */ + /* A small structure used to model the pre-computed widths of a given */ + /* size. They are found in the `hdmx' table. */ + /* */ + /* <Fields> */ + /* ppem :: The pixels per EM value at which these metrics apply. */ + /* */ + /* max_width :: The maximum advance width for this metric. */ + /* */ + /* widths :: An array of widths. Note: These are 8-bit bytes. */ + /* */ + typedef struct TT_HdmxEntryRec_ + { + FT_Byte ppem; + FT_Byte max_width; + FT_Byte* widths; + + } TT_HdmxEntryRec, *TT_HdmxEntry; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_HdmxRec */ + /* */ + /* <Description> */ + /* A structure used to model the `hdmx' table, which contains */ + /* pre-computed widths for a set of given sizes/dimensions. */ + /* */ + /* <Fields> */ + /* version :: The version number. */ + /* */ + /* num_records :: The number of hdmx records. */ + /* */ + /* records :: An array of hdmx records. */ + /* */ + typedef struct TT_HdmxRec_ + { + FT_UShort version; + FT_Short num_records; + TT_HdmxEntry records; + + } TT_HdmxRec, *TT_Hdmx; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Kern0_PairRec */ + /* */ + /* <Description> */ + /* A structure used to model a kerning pair for the kerning table */ + /* format 0. The engine now loads this table if it finds one in the */ + /* font file. */ + /* */ + /* <Fields> */ + /* left :: The index of the left glyph in pair. */ + /* */ + /* right :: The index of the right glyph in pair. */ + /* */ + /* value :: The kerning distance. A positive value spaces the */ + /* glyphs, a negative one makes them closer. */ + /* */ + typedef struct TT_Kern0_PairRec_ + { + FT_UShort left; /* index of left glyph in pair */ + FT_UShort right; /* index of right glyph in pair */ + FT_FWord value; /* kerning value */ + + } TT_Kern0_PairRec, *TT_Kern0_Pair; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** EMBEDDED BITMAPS SUPPORT ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_MetricsRec */ + /* */ + /* <Description> */ + /* A structure used to hold the big metrics of a given glyph bitmap */ + /* in a TrueType or OpenType font. These are usually found in the */ + /* `EBDT' (Microsoft) or `bloc' (Apple) table. */ + /* */ + /* <Fields> */ + /* height :: The glyph height in pixels. */ + /* */ + /* width :: The glyph width in pixels. */ + /* */ + /* horiBearingX :: The horizontal left bearing. */ + /* */ + /* horiBearingY :: The horizontal top bearing. */ + /* */ + /* horiAdvance :: The horizontal advance. */ + /* */ + /* vertBearingX :: The vertical left bearing. */ + /* */ + /* vertBearingY :: The vertical top bearing. */ + /* */ + /* vertAdvance :: The vertical advance. */ + /* */ + typedef struct TT_SBit_MetricsRec_ + { + FT_Byte height; + FT_Byte width; + + FT_Char horiBearingX; + FT_Char horiBearingY; + FT_Byte horiAdvance; + + FT_Char vertBearingX; + FT_Char vertBearingY; + FT_Byte vertAdvance; + + } TT_SBit_MetricsRec, *TT_SBit_Metrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_SmallMetricsRec */ + /* */ + /* <Description> */ + /* A structure used to hold the small metrics of a given glyph bitmap */ + /* in a TrueType or OpenType font. These are usually found in the */ + /* `EBDT' (Microsoft) or the `bdat' (Apple) table. */ + /* */ + /* <Fields> */ + /* height :: The glyph height in pixels. */ + /* */ + /* width :: The glyph width in pixels. */ + /* */ + /* bearingX :: The left-side bearing. */ + /* */ + /* bearingY :: The top-side bearing. */ + /* */ + /* advance :: The advance width or height. */ + /* */ + typedef struct TT_SBit_Small_Metrics_ + { + FT_Byte height; + FT_Byte width; + + FT_Char bearingX; + FT_Char bearingY; + FT_Byte advance; + + } TT_SBit_SmallMetricsRec, *TT_SBit_SmallMetrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_LineMetricsRec */ + /* */ + /* <Description> */ + /* A structure used to describe the text line metrics of a given */ + /* bitmap strike, for either a horizontal or vertical layout. */ + /* */ + /* <Fields> */ + /* ascender :: The ascender in pixels. */ + /* */ + /* descender :: The descender in pixels. */ + /* */ + /* max_width :: The maximum glyph width in pixels. */ + /* */ + /* caret_slope_enumerator :: Rise of the caret slope, typically set */ + /* to 1 for non-italic fonts. */ + /* */ + /* caret_slope_denominator :: Rise of the caret slope, typically set */ + /* to 0 for non-italic fonts. */ + /* */ + /* caret_offset :: Offset in pixels to move the caret for */ + /* proper positioning. */ + /* */ + /* min_origin_SB :: Minimum of horiBearingX (resp. */ + /* vertBearingY). */ + /* min_advance_SB :: Minimum of */ + /* */ + /* horizontal advance - */ + /* ( horiBearingX + width ) */ + /* */ + /* resp. */ + /* */ + /* vertical advance - */ + /* ( vertBearingY + height ) */ + /* */ + /* max_before_BL :: Maximum of horiBearingY (resp. */ + /* vertBearingY). */ + /* */ + /* min_after_BL :: Minimum of */ + /* */ + /* horiBearingY - height */ + /* */ + /* resp. */ + /* */ + /* vertBearingX - width */ + /* */ + /* pads :: Unused (to make the size of the record */ + /* a multiple of 32 bits. */ + /* */ + typedef struct TT_SBit_LineMetricsRec_ + { + FT_Char ascender; + FT_Char descender; + FT_Byte max_width; + FT_Char caret_slope_numerator; + FT_Char caret_slope_denominator; + FT_Char caret_offset; + FT_Char min_origin_SB; + FT_Char min_advance_SB; + FT_Char max_before_BL; + FT_Char min_after_BL; + FT_Char pads[2]; + + } TT_SBit_LineMetricsRec, *TT_SBit_LineMetrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_RangeRec */ + /* */ + /* <Description> */ + /* A TrueType/OpenType subIndexTable as defined in the `EBLC' */ + /* (Microsoft) or `bloc' (Apple) tables. */ + /* */ + /* <Fields> */ + /* first_glyph :: The first glyph index in the range. */ + /* */ + /* last_glyph :: The last glyph index in the range. */ + /* */ + /* index_format :: The format of index table. Valid values are 1 */ + /* to 5. */ + /* */ + /* image_format :: The format of `EBDT' image data. */ + /* */ + /* image_offset :: The offset to image data in `EBDT'. */ + /* */ + /* image_size :: For index formats 2 and 5. This is the size in */ + /* bytes of each glyph bitmap. */ + /* */ + /* big_metrics :: For index formats 2 and 5. This is the big */ + /* metrics for each glyph bitmap. */ + /* */ + /* num_glyphs :: For index formats 4 and 5. This is the number of */ + /* glyphs in the code array. */ + /* */ + /* glyph_offsets :: For index formats 1 and 3. */ + /* */ + /* glyph_codes :: For index formats 4 and 5. */ + /* */ + /* table_offset :: The offset of the index table in the `EBLC' */ + /* table. Only used during strike loading. */ + /* */ + typedef struct TT_SBit_RangeRec + { + FT_UShort first_glyph; + FT_UShort last_glyph; + + FT_UShort index_format; + FT_UShort image_format; + FT_ULong image_offset; + + FT_ULong image_size; + TT_SBit_MetricsRec metrics; + FT_ULong num_glyphs; + + FT_ULong* glyph_offsets; + FT_UShort* glyph_codes; + + FT_ULong table_offset; + + } TT_SBit_RangeRec, *TT_SBit_Range; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_StrikeRec */ + /* */ + /* <Description> */ + /* A structure used describe a given bitmap strike in the `EBLC' */ + /* (Microsoft) or `bloc' (Apple) tables. */ + /* */ + /* <Fields> */ + /* num_index_ranges :: The number of index ranges. */ + /* */ + /* index_ranges :: An array of glyph index ranges. */ + /* */ + /* color_ref :: Unused. `color_ref' is put in for future */ + /* enhancements, but these fields are already */ + /* in use by other platforms (e.g. Newton). */ + /* For details, please see */ + /* */ + /* http://fonts.apple.com/ */ + /* TTRefMan/RM06/Chap6bloc.html */ + /* */ + /* hori :: The line metrics for horizontal layouts. */ + /* */ + /* vert :: The line metrics for vertical layouts. */ + /* */ + /* start_glyph :: The lowest glyph index for this strike. */ + /* */ + /* end_glyph :: The highest glyph index for this strike. */ + /* */ + /* x_ppem :: The number of horizontal pixels per EM. */ + /* */ + /* y_ppem :: The number of vertical pixels per EM. */ + /* */ + /* bit_depth :: The bit depth. Valid values are 1, 2, 4, */ + /* and 8. */ + /* */ + /* flags :: Is this a vertical or horizontal strike? For */ + /* details, please see */ + /* */ + /* http://fonts.apple.com/ */ + /* TTRefMan/RM06/Chap6bloc.html */ + /* */ + typedef struct TT_SBit_StrikeRec_ + { + FT_Int num_ranges; + TT_SBit_Range sbit_ranges; + FT_ULong ranges_offset; + + FT_ULong color_ref; + + TT_SBit_LineMetricsRec hori; + TT_SBit_LineMetricsRec vert; + + FT_UShort start_glyph; + FT_UShort end_glyph; + + FT_Byte x_ppem; + FT_Byte y_ppem; + + FT_Byte bit_depth; + FT_Char flags; + + } TT_SBit_StrikeRec, *TT_SBit_Strike; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_ComponentRec */ + /* */ + /* <Description> */ + /* A simple structure to describe a compound sbit element. */ + /* */ + /* <Fields> */ + /* glyph_code :: The element's glyph index. */ + /* */ + /* x_offset :: The element's left bearing. */ + /* */ + /* y_offset :: The element's top bearing. */ + /* */ + typedef struct TT_SBit_ComponentRec_ + { + FT_UShort glyph_code; + FT_Char x_offset; + FT_Char y_offset; + + } TT_SBit_ComponentRec, *TT_SBit_Component; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_SBit_ScaleRec */ + /* */ + /* <Description> */ + /* A structure used describe a given bitmap scaling table, as defined */ + /* in the `EBSC' table. */ + /* */ + /* <Fields> */ + /* hori :: The horizontal line metrics. */ + /* */ + /* vert :: The vertical line metrics. */ + /* */ + /* x_ppem :: The number of horizontal pixels per EM. */ + /* */ + /* y_ppem :: The number of vertical pixels per EM. */ + /* */ + /* x_ppem_substitute :: Substitution x_ppem value. */ + /* */ + /* y_ppem_substitute :: Substitution y_ppem value. */ + /* */ + typedef struct TT_SBit_ScaleRec_ + { + TT_SBit_LineMetricsRec hori; + TT_SBit_LineMetricsRec vert; + + FT_Byte x_ppem; + FT_Byte y_ppem; + + FT_Byte x_ppem_substitute; + FT_Byte y_ppem_substitute; + + } TT_SBit_ScaleRec, *TT_SBit_Scale; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** POSTSCRIPT GLYPH NAMES SUPPORT ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Post_20Rec */ + /* */ + /* <Description> */ + /* Postscript names sub-table, format 2.0. Stores the PS name of */ + /* each glyph in the font face. */ + /* */ + /* <Fields> */ + /* num_glyphs :: The number of named glyphs in the table. */ + /* */ + /* num_names :: The number of PS names stored in the table. */ + /* */ + /* glyph_indices :: The indices of the glyphs in the names arrays. */ + /* */ + /* glyph_names :: The PS names not in Mac Encoding. */ + /* */ + typedef struct TT_Post_20Rec_ + { + FT_UShort num_glyphs; + FT_UShort num_names; + FT_UShort* glyph_indices; + FT_Char** glyph_names; + + } TT_Post_20Rec, *TT_Post_20; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Post_25Rec */ + /* */ + /* <Description> */ + /* Postscript names sub-table, format 2.5. Stores the PS name of */ + /* each glyph in the font face. */ + /* */ + /* <Fields> */ + /* num_glyphs :: The number of glyphs in the table. */ + /* */ + /* offsets :: An array of signed offsets in a normal Mac */ + /* Postscript name encoding. */ + /* */ + typedef struct TT_Post_25_ + { + FT_UShort num_glyphs; + FT_Char* offsets; + + } TT_Post_25Rec, *TT_Post_25; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Post_NamesRec */ + /* */ + /* <Description> */ + /* Postscript names table, either format 2.0 or 2.5. */ + /* */ + /* <Fields> */ + /* loaded :: A flag to indicate whether the PS names are loaded. */ + /* */ + /* format_20 :: The sub-table used for format 2.0. */ + /* */ + /* format_25 :: The sub-table used for format 2.5. */ + /* */ + typedef struct TT_Post_NamesRec_ + { + FT_Bool loaded; + + union + { + TT_Post_20Rec format_20; + TT_Post_25Rec format_25; + + } names; + + } TT_Post_NamesRec, *TT_Post_Names; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** TRUETYPE CHARMAPS SUPPORT ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* format 0 */ + + typedef struct TT_CMap0_ + { + FT_ULong language; /* for Mac fonts (originally ushort) */ + + FT_Byte* glyphIdArray; + + } TT_CMap0Rec, *TT_CMap0; + + + /* format 2 */ + + typedef struct TT_CMap2SubHeaderRec_ + { + FT_UShort firstCode; /* first valid low byte */ + FT_UShort entryCount; /* number of valid low bytes */ + FT_Short idDelta; /* delta value to glyphIndex */ + FT_UShort idRangeOffset; /* offset from here to 1st code */ + + } TT_CMap2SubHeaderRec, *TT_CMap2SubHeader; + + + typedef struct TT_CMap2Rec_ + { + FT_ULong language; /* for Mac fonts (originally ushort) */ + + FT_UShort* subHeaderKeys; /* high byte mapping table */ + /* value = subHeader index * 8 */ + TT_CMap2SubHeader subHeaders; + FT_UShort* glyphIdArray; + FT_UShort numGlyphId; /* control value */ + + } TT_CMap2Rec, *TT_CMap2; + + + /* format 4 */ + + typedef struct TT_CMap4Segment_ + { + FT_UShort endCount; + FT_UShort startCount; + FT_Short idDelta; + FT_UShort idRangeOffset; + + } TT_CMap4SegmentRec, *TT_CMap4Segment; + + + typedef struct TT_CMap4Rec_ + { + FT_ULong language; /* for Mac fonts (originally ushort) */ + + FT_UShort segCountX2; /* number of segments * 2 */ + FT_UShort searchRange; /* these parameters can be used */ + FT_UShort entrySelector; /* for a binary search */ + FT_UShort rangeShift; + + TT_CMap4Segment segments; + FT_UShort* glyphIdArray; + FT_UShort numGlyphId; /* control value */ + + TT_CMap4Segment last_segment; /* last used segment; this is a small */ + /* cache to potentially increase speed */ + } TT_CMap4Rec, *TT_CMap4; + + + /* format 6 */ + + typedef struct TT_CMap6_ + { + FT_ULong language; /* for Mac fonts (originally ushort) */ + + FT_UShort firstCode; /* first character code of subrange */ + FT_UShort entryCount; /* number of character codes in subrange */ + + FT_UShort* glyphIdArray; + + } TT_CMap6Rec, *TT_CMap6; + + + /* auxiliary table for format 8 and 12 */ + + typedef struct TT_CMapGroupRec_ + { + FT_ULong startCharCode; + FT_ULong endCharCode; + FT_ULong startGlyphID; + + } TT_CMapGroupRec, *TT_CMapGroup; + + + /* FreeType handles format 8 and 12 identically. It is not necessary to + cover mixed 16bit and 32bit codes since FreeType always uses FT_ULong + for input character codes -- converting Unicode surrogates to 32bit + character codes must be done by the application. */ + + typedef struct TT_CMap8_12Rec_ + { + FT_ULong language; /* for Mac fonts */ + + FT_ULong nGroups; + TT_CMapGroup groups; + + TT_CMapGroup last_group; /* last used group; this is a small */ + /* cache to potentially increase speed */ + } TT_CMap8_12Rec, *TT_CMap8_12; + + + /* format 10 */ + + typedef struct TT_CMap10Rec_ + { + FT_ULong language; /* for Mac fonts */ + + FT_ULong startCharCode; /* first character covered */ + FT_ULong numChars; /* number of characters covered */ + + FT_UShort* glyphs; + + } TT_CMap10Rec, *TT_CMap10; + + + typedef struct TT_CMapTableRec_* TT_CMapTable; + + + typedef FT_UInt + (*TT_CharMap_Func)( TT_CMapTable charmap, + FT_ULong char_code ); + + typedef FT_ULong + (*TT_CharNext_Func)( TT_CMapTable charmap, + FT_ULong char_code ); + + + /* charmap table */ + typedef struct TT_CMapTableRec_ + { + FT_UShort platformID; + FT_UShort platformEncodingID; + FT_UShort format; + FT_ULong length; /* must be ulong for formats 8, 10, and 12 */ + + FT_Bool loaded; + FT_ULong offset; + + union + { + TT_CMap0Rec cmap0; + TT_CMap2Rec cmap2; + TT_CMap4Rec cmap4; + TT_CMap6Rec cmap6; + TT_CMap8_12Rec cmap8_12; + TT_CMap10Rec cmap10; + } c; + + TT_CharMap_Func get_index; + TT_CharNext_Func get_next_char; + + } TT_CMapTableRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_CharMapRec */ + /* */ + /* <Description> */ + /* The TrueType character map object type. */ + /* */ + /* <Fields> */ + /* root :: The parent character map structure. */ + /* */ + /* cmap :: The used character map. */ + /* */ + typedef struct TT_CharMapRec_ + { + FT_CharMapRec root; + TT_CMapTableRec cmap; + + } TT_CharMapRec; + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*** ***/ + /*** ***/ + /*** ORIGINAL TT_FACE CLASS DEFINITION ***/ + /*** ***/ + /*** ***/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This structure/class is defined here because it is common to the */ + /* following formats: TTF, OpenType-TT, and OpenType-CFF. */ + /* */ + /* Note, however, that the classes TT_Size, TT_GlyphSlot, and TT_CharMap */ + /* are not shared between font drivers, and are thus defined in */ + /* `ttobjs.h'. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* TT_Face */ + /* */ + /* <Description> */ + /* A handle to a TrueType face/font object. A TT_Face encapsulates */ + /* the resolution and scaling independent parts of a TrueType font */ + /* resource. */ + /* */ + /* <Note> */ + /* The TT_Face structure is also used as a `parent class' for the */ + /* OpenType-CFF class (T2_Face). */ + /* */ + typedef struct TT_FaceRec_* TT_Face; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* TT_CharMap */ + /* */ + /* <Description> */ + /* A handle to a TrueType character mapping object. */ + /* */ + typedef struct TT_CharMapRec_* TT_CharMap; + + + /* a function type used for the truetype bytecode interpreter hooks */ + typedef FT_Error + (*TT_Interpreter)( void* exec_context ); + + /* forward declaration */ + typedef struct TT_LoaderRec_* TT_Loader; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Loader_GotoTableFunc */ + /* */ + /* <Description> */ + /* Seeks a stream to the start of a given TrueType table. */ + /* */ + /* <Input> */ + /* face :: A handle to the target face object. */ + /* */ + /* tag :: A 4-byte tag used to name the table. */ + /* */ + /* stream :: The input stream. */ + /* */ + /* <Output> */ + /* length :: The length of the table in bytes. Set to 0 if not */ + /* needed. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* The stream cursor must be at the font file's origin. */ + /* */ + typedef FT_Error + (*TT_Loader_GotoTableFunc)( TT_Face face, + FT_ULong tag, + FT_Stream stream, + FT_ULong* length ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Loader_StartGlyphFunc */ + /* */ + /* <Description> */ + /* Seeks a stream to the start of a given glyph element, and opens a */ + /* frame for it. */ + /* */ + /* <Input> */ + /* loader :: The current TrueType glyph loader object. */ + /* */ + /* glyph index :: The index of the glyph to access. */ + /* */ + /* offset :: The offset of the glyph according to the */ + /* `locations' table. */ + /* */ + /* byte_count :: The size of the frame in bytes. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function is normally equivalent to FT_STREAM_SEEK(offset) */ + /* followed by FT_FRAME_ENTER(byte_count) with the loader's stream, */ + /* but alternative formats (e.g. compressed ones) might use something */ + /* different. */ + /* */ + typedef FT_Error + (*TT_Loader_StartGlyphFunc)( TT_Loader loader, + FT_UInt glyph_index, + FT_ULong offset, + FT_UInt byte_count ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Loader_ReadGlyphFunc */ + /* */ + /* <Description> */ + /* Reads one glyph element (its header, a simple glyph, or a */ + /* composite) from the loader's current stream frame. */ + /* */ + /* <Input> */ + /* loader :: The current TrueType glyph loader object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + typedef FT_Error + (*TT_Loader_ReadGlyphFunc)( TT_Loader loader ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* TT_Loader_EndGlyphFunc */ + /* */ + /* <Description> */ + /* Closes the current loader stream frame for the glyph. */ + /* */ + /* <Input> */ + /* loader :: The current TrueType glyph loader object. */ + /* */ + typedef void + (*TT_Loader_EndGlyphFunc)( TT_Loader loader ); + + + /*************************************************************************/ + /* */ + /* TrueType Face Type */ + /* */ + /* <Struct> */ + /* TT_Face */ + /* */ + /* <Description> */ + /* The TrueType face class. These objects model the resolution and */ + /* point-size independent data found in a TrueType font file. */ + /* */ + /* <Fields> */ + /* root :: The base FT_Face structure, managed by the */ + /* base layer. */ + /* */ + /* ttc_header :: The TrueType collection header, used when */ + /* the file is a `ttc' rather than a `ttf'. */ + /* For ordinary font files, the field */ + /* `ttc_header.count' is set to 0. */ + /* */ + /* format_tag :: The font format tag. */ + /* */ + /* num_tables :: The number of TrueType tables in this font */ + /* file. */ + /* */ + /* dir_tables :: The directory of TrueType tables for this */ + /* font file. */ + /* */ + /* header :: The font's font header (`head' table). */ + /* Read on font opening. */ + /* */ + /* horizontal :: The font's horizontal header (`hhea' */ + /* table). This field also contains the */ + /* associated horizontal metrics table */ + /* (`hmtx'). */ + /* */ + /* max_profile :: The font's maximum profile table. Read on */ + /* font opening. Note that some maximum */ + /* values cannot be taken directly from this */ + /* table. We thus define additional fields */ + /* below to hold the computed maxima. */ + /* */ + /* max_components :: The maximum number of glyph components */ + /* required to load any composite glyph from */ + /* this font. Used to size the load stack. */ + /* */ + /* vertical_info :: A boolean which is set when the font file */ + /* contains vertical metrics. If not, the */ + /* value of the `vertical' field is */ + /* undefined. */ + /* */ + /* vertical :: The font's vertical header (`vhea' table). */ + /* This field also contains the associated */ + /* vertical metrics table (`vmtx'), if found. */ + /* IMPORTANT: The contents of this field is */ + /* undefined if the `verticalInfo' field is */ + /* unset. */ + /* */ + /* num_names :: The number of name records within this */ + /* TrueType font. */ + /* */ + /* name_table :: The table of name records (`name'). */ + /* */ + /* os2 :: The font's OS/2 table (`OS/2'). */ + /* */ + /* postscript :: The font's PostScript table (`post' */ + /* table). The PostScript glyph names are */ + /* not loaded by the driver on face opening. */ + /* See the `ttpost' module for more details. */ + /* */ + /* cmap_table :: Address of the face's `cmap' SFNT table */ + /* in memory (it's an extracted frame). */ + /* */ + /* cmap_size :: The size in bytes of the `cmap_table' */ + /* described above. */ + /* */ + /* num_charmaps :: The number of character mappings in the */ + /* font. */ + /* */ + /* charmaps :: The array of charmap objects for this font */ + /* file. Note that this field is a typeless */ + /* pointer. The Reason is that the format of */ + /* charmaps varies with the underlying font */ + /* format and cannot be determined here. */ + /* */ + /* goto_table :: A function called by each TrueType table */ + /* loader to position a stream's cursor to */ + /* the start of a given table according to */ + /* its tag. It defaults to TT_Goto_Face but */ + /* can be different for strange formats (e.g. */ + /* Type 42). */ + /* */ + /* access_glyph_frame :: A function used to access the frame of a */ + /* given glyph within the face's font file. */ + /* */ + /* forget_glyph_frame :: A function used to forget the frame of a */ + /* given glyph when all data has been loaded. */ + /* */ + /* read_glyph_header :: A function used to read a glyph header. */ + /* It must be called between an `access' and */ + /* `forget'. */ + /* */ + /* read_simple_glyph :: A function used to read a simple glyph. */ + /* It must be called after the header was */ + /* read, and before the `forget'. */ + /* */ + /* read_composite_glyph :: A function used to read a composite glyph. */ + /* It must be called after the header was */ + /* read, and before the `forget'. */ + /* */ + /* sfnt :: A pointer to the SFNT service. */ + /* */ + /* psnames :: A pointer to the PostScript names service. */ + /* */ + /* hdmx :: The face's horizontal device metrics */ + /* (`hdmx' table). This table is optional in */ + /* TrueType/OpenType fonts. */ + /* */ + /* gasp :: The grid-fitting and scaling properties */ + /* table (`gasp'). This table is optional in */ + /* TrueType/OpenType fonts. */ + /* */ + /* pclt :: The `pclt' SFNT table. */ + /* */ + /* num_sbit_strikes :: The number of sbit strikes, i.e., bitmap */ + /* sizes, embedded in this font. */ + /* */ + /* sbit_strikes :: An array of sbit strikes embedded in this */ + /* font. This table is optional in a */ + /* TrueType/OpenType font. */ + /* */ + /* num_sbit_scales :: The number of sbit scales for this font. */ + /* */ + /* sbit_scales :: Array of sbit scales embedded in this */ + /* font. This table is optional in a */ + /* TrueType/OpenType font. */ + /* */ + /* postscript_names :: A table used to store the Postscript names */ + /* of the glyphs for this font. See the */ + /* file `ttconfig.h' for comments on the */ + /* TT_CONFIG_OPTION_POSTSCRIPT_NAMES option. */ + /* */ + /* num_locations :: The number of glyph locations in this */ + /* TrueType file. This should be */ + /* identical to the number of glyphs. */ + /* Ignored for Type 2 fonts. */ + /* */ + /* glyph_locations :: An array of longs. These are offsets to */ + /* glyph data within the `glyf' table. */ + /* Ignored for Type 2 font faces. */ + /* */ + /* font_program_size :: Size in bytecodes of the face's font */ + /* program. 0 if none defined. Ignored for */ + /* Type 2 fonts. */ + /* */ + /* font_program :: The face's font program (bytecode stream) */ + /* executed at load time, also used during */ + /* glyph rendering. Comes from the `fpgm' */ + /* table. Ignored for Type 2 font fonts. */ + /* */ + /* cvt_program_size :: The size in bytecodes of the face's cvt */ + /* program. Ignored for Type 2 fonts. */ + /* */ + /* cvt_program :: The face's cvt program (bytecode stream) */ + /* executed each time an instance/size is */ + /* changed/reset. Comes from the `prep' */ + /* table. Ignored for Type 2 fonts. */ + /* */ + /* cvt_size :: Size of the control value table (in */ + /* entries). Ignored for Type 2 fonts. */ + /* */ + /* cvt :: The face's original control value table. */ + /* Coordinates are expressed in unscaled font */ + /* units. Comes from the `cvt ' table. */ + /* Ignored for Type 2 fonts. */ + /* */ + /* num_kern_pairs :: The number of kerning pairs present in the */ + /* font file. The engine only loads the */ + /* first horizontal format 0 kern table it */ + /* finds in the font file. Ignored for */ + /* Type 2 fonts. */ + /* */ + /* kern_table_index :: The index of the kerning table in the font */ + /* kerning directory. Ignored for Type 2 */ + /* fonts. */ + /* */ + /* interpreter :: A pointer to the TrueType bytecode */ + /* interpreters field is also used to hook */ + /* the debugger in `ttdebug'. */ + /* */ + /* unpatented_hinting :: If true, use only unpatented methods in */ + /* the bytecode interpreter. */ + /* */ + /* extra :: Reserved for third-party font drivers. */ + /* */ + typedef struct TT_FaceRec_ + { + FT_FaceRec root; + + TTC_HeaderRec ttc_header; + + FT_ULong format_tag; + FT_UShort num_tables; + TT_Table dir_tables; + + TT_Header header; /* TrueType header table */ + TT_HoriHeader horizontal; /* TrueType horizontal header */ + + TT_MaxProfile max_profile; + FT_ULong max_components; + + FT_Bool vertical_info; + TT_VertHeader vertical; /* TT Vertical header, if present */ + + FT_UShort num_names; /* number of name records */ + TT_NameTableRec name_table; /* name table */ + + TT_OS2 os2; /* TrueType OS/2 table */ + TT_Postscript postscript; /* TrueType Postscript table */ + + FT_Byte* cmap_table; /* extracted 'cmap' table */ + FT_ULong cmap_size; + + TT_Loader_GotoTableFunc goto_table; + + TT_Loader_StartGlyphFunc access_glyph_frame; + TT_Loader_EndGlyphFunc forget_glyph_frame; + TT_Loader_ReadGlyphFunc read_glyph_header; + TT_Loader_ReadGlyphFunc read_simple_glyph; + TT_Loader_ReadGlyphFunc read_composite_glyph; + + /* a typeless pointer to the SFNT_Interface table used to load */ + /* the basic TrueType tables in the face object */ + void* sfnt; + + /* a typeless pointer to the FT_Service_PsCMapsRec table used to */ + /* handle glyph names <-> unicode & Mac values */ + void* psnames; + + + /***********************************************************************/ + /* */ + /* Optional TrueType/OpenType tables */ + /* */ + /***********************************************************************/ + + /* horizontal device metrics */ + TT_HdmxRec hdmx; + + /* grid-fitting and scaling table */ + TT_GaspRec gasp; /* the `gasp' table */ + + /* PCL 5 table */ + TT_PCLT pclt; + + /* embedded bitmaps support */ + FT_ULong num_sbit_strikes; + TT_SBit_Strike sbit_strikes; + + FT_ULong num_sbit_scales; + TT_SBit_Scale sbit_scales; + + /* postscript names table */ + TT_Post_NamesRec postscript_names; + + + /***********************************************************************/ + /* */ + /* TrueType-specific fields (ignored by the OTF-Type2 driver) */ + /* */ + /***********************************************************************/ + + /* the glyph locations */ + FT_UShort num_locations; + FT_Long* glyph_locations; + + /* the font program, if any */ + FT_ULong font_program_size; + FT_Byte* font_program; + + /* the cvt program, if any */ + FT_ULong cvt_program_size; + FT_Byte* cvt_program; + + /* the original, unscaled, control value table */ + FT_ULong cvt_size; + FT_Short* cvt; + + /* the format 0 kerning table, if any */ + FT_Int num_kern_pairs; + FT_Int kern_table_index; + TT_Kern0_Pair kern_pairs; + + /* A pointer to the bytecode interpreter to use. This is also */ + /* used to hook the debugger for the `ttdebug' utility. */ + TT_Interpreter interpreter; + +#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING + /* Use unpatented hinting only. */ + FT_Bool unpatented_hinting; +#endif + + /***********************************************************************/ + /* */ + /* Other tables or fields. This is used by derivative formats like */ + /* OpenType. */ + /* */ + /***********************************************************************/ + + FT_Generic extra; + + const char* postscript_name; + + } TT_FaceRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_GlyphZoneRec */ + /* */ + /* <Description> */ + /* A glyph zone is used to load, scale and hint glyph outline */ + /* coordinates. */ + /* */ + /* <Fields> */ + /* memory :: A handle to the memory manager. */ + /* */ + /* max_points :: The maximal size in points of the zone. */ + /* */ + /* max_contours :: Max size in links contours of thez one. */ + /* */ + /* n_points :: The current number of points in the zone. */ + /* */ + /* n_contours :: The current number of contours in the zone. */ + /* */ + /* org :: The original glyph coordinates (font */ + /* units/scaled). */ + /* */ + /* cur :: The current glyph coordinates (scaled/hinted). */ + /* */ + /* tags :: The point control tags. */ + /* */ + /* contours :: The contours end points. */ + /* */ + typedef struct TT_GlyphZoneRec_ + { + FT_Memory memory; + FT_UShort max_points; + FT_UShort max_contours; + FT_UShort n_points; /* number of points in zone */ + FT_Short n_contours; /* number of contours */ + + FT_Vector* org; /* original point coordinates */ + FT_Vector* cur; /* current point coordinates */ + + FT_Byte* tags; /* current touch flags */ + FT_UShort* contours; /* contour end points */ + + } TT_GlyphZoneRec, *TT_GlyphZone; + + + /* handle to execution context */ + typedef struct TT_ExecContextRec_* TT_ExecContext; + + /* glyph loader structure */ + typedef struct TT_LoaderRec_ + { + FT_Face face; + FT_Size size; + FT_GlyphSlot glyph; + FT_GlyphLoader gloader; + + FT_ULong load_flags; + FT_UInt glyph_index; + + FT_Stream stream; + FT_Int byte_len; + + FT_Short n_contours; + FT_BBox bbox; + FT_Int left_bearing; + FT_Int advance; + FT_Int linear; + FT_Bool linear_def; + FT_Bool preserve_pps; + FT_Vector pp1; + FT_Vector pp2; + + FT_ULong glyf_offset; + + /* the zone where we load our glyphs */ + TT_GlyphZoneRec base; + TT_GlyphZoneRec zone; + + TT_ExecContext exec; + FT_Byte* instructions; + FT_ULong ins_pos; + + /* for possible extensibility in other formats */ + void* other; + + } TT_LoaderRec; + + +FT_END_HEADER + +#endif /* __TTTYPES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/t1tables.h b/extra_lib/include/freetype/freetype/t1tables.h new file mode 100644 index 0000000..af8c416 --- /dev/null +++ b/extra_lib/include/freetype/freetype/t1tables.h @@ -0,0 +1,397 @@ +/***************************************************************************/ +/* */ +/* t1tables.h */ +/* */ +/* Basic Type 1/Type 2 tables definitions and interface (specification */ +/* only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __T1TABLES_H__ +#define __T1TABLES_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* type1_tables */ + /* */ + /* <Title> */ + /* Type 1 Tables */ + /* */ + /* <Abstract> */ + /* Type 1 (PostScript) specific font tables. */ + /* */ + /* <Description> */ + /* This section contains the definition of Type 1-specific tables, */ + /* including structures related to other PostScript font formats. */ + /* */ + /*************************************************************************/ + + + /* Note that we separate font data in PS_FontInfoRec and PS_PrivateRec */ + /* structures in order to support Multiple Master fonts. */ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_FontInfoRec */ + /* */ + /* <Description> */ + /* A structure used to model a Type1/Type2 FontInfo dictionary. Note */ + /* that for Multiple Master fonts, each instance has its own */ + /* FontInfo. */ + /* */ + typedef struct PS_FontInfoRec + { + FT_String* version; + FT_String* notice; + FT_String* full_name; + FT_String* family_name; + FT_String* weight; + FT_Long italic_angle; + FT_Bool is_fixed_pitch; + FT_Short underline_position; + FT_UShort underline_thickness; + + } PS_FontInfoRec, *PS_FontInfo; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* T1_FontInfo */ + /* */ + /* <Description> */ + /* This type is equivalent to @PS_FontInfoRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef PS_FontInfoRec T1_FontInfo; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_PrivateRec */ + /* */ + /* <Description> */ + /* A structure used to model a Type1/Type2 private dictionary. Note */ + /* that for Multiple Master fonts, each instance has its own Private */ + /* dictionary. */ + /* */ + typedef struct PS_PrivateRec_ + { + FT_Int unique_id; + FT_Int lenIV; + + FT_Byte num_blue_values; + FT_Byte num_other_blues; + FT_Byte num_family_blues; + FT_Byte num_family_other_blues; + + FT_Short blue_values[14]; + FT_Short other_blues[10]; + + FT_Short family_blues [14]; + FT_Short family_other_blues[10]; + + FT_Fixed blue_scale; + FT_Int blue_shift; + FT_Int blue_fuzz; + + FT_UShort standard_width[1]; + FT_UShort standard_height[1]; + + FT_Byte num_snap_widths; + FT_Byte num_snap_heights; + FT_Bool force_bold; + FT_Bool round_stem_up; + + FT_Short snap_widths [13]; /* including std width */ + FT_Short snap_heights[13]; /* including std height */ + + FT_Long language_group; + FT_Long password; + + FT_Short min_feature[2]; + + } PS_PrivateRec, *PS_Private; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* T1_Private */ + /* */ + /* <Description> */ + /* This type is equivalent to @PS_PrivateRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef PS_PrivateRec T1_Private; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* T1_Blend_Flags */ + /* */ + /* <Description> */ + /* A set of flags used to indicate which fields are present in a */ + /* given blen dictionary (font info or private). Used to support */ + /* Multiple Masters fonts. */ + /* */ + typedef enum + { + /*# required fields in a FontInfo blend dictionary */ + T1_BLEND_UNDERLINE_POSITION = 0, + T1_BLEND_UNDERLINE_THICKNESS, + T1_BLEND_ITALIC_ANGLE, + + /*# required fields in a Private blend dictionary */ + T1_BLEND_BLUE_VALUES, + T1_BLEND_OTHER_BLUES, + T1_BLEND_STANDARD_WIDTH, + T1_BLEND_STANDARD_HEIGHT, + T1_BLEND_STEM_SNAP_WIDTHS, + T1_BLEND_STEM_SNAP_HEIGHTS, + T1_BLEND_BLUE_SCALE, + T1_BLEND_BLUE_SHIFT, + T1_BLEND_FAMILY_BLUES, + T1_BLEND_FAMILY_OTHER_BLUES, + T1_BLEND_FORCE_BOLD, + + /*# never remove */ + T1_BLEND_MAX + + } T1_Blend_Flags; + + + /*# backwards compatible definitions */ +#define t1_blend_underline_position T1_BLEND_UNDERLINE_POSITION +#define t1_blend_underline_thickness T1_BLEND_UNDERLINE_THICKNESS +#define t1_blend_italic_angle T1_BLEND_ITALIC_ANGLE +#define t1_blend_blue_values T1_BLEND_BLUE_VALUES +#define t1_blend_other_blues T1_BLEND_OTHER_BLUES +#define t1_blend_standard_widths T1_BLEND_STANDARD_WIDTH +#define t1_blend_standard_height T1_BLEND_STANDARD_HEIGHT +#define t1_blend_stem_snap_widths T1_BLEND_STEM_SNAP_WIDTHS +#define t1_blend_stem_snap_heights T1_BLEND_STEM_SNAP_HEIGHTS +#define t1_blend_blue_scale T1_BLEND_BLUE_SCALE +#define t1_blend_blue_shift T1_BLEND_BLUE_SHIFT +#define t1_blend_family_blues T1_BLEND_FAMILY_BLUES +#define t1_blend_family_other_blues T1_BLEND_FAMILY_OTHER_BLUES +#define t1_blend_force_bold T1_BLEND_FORCE_BOLD +#define t1_blend_max T1_BLEND_MAX + + + /* maximum number of Multiple Masters designs, as defined in the spec */ +#define T1_MAX_MM_DESIGNS 16 + + /* maximum number of Multiple Masters axes, as defined in the spec */ +#define T1_MAX_MM_AXIS 4 + + /* maximum number of elements in a design map */ +#define T1_MAX_MM_MAP_POINTS 20 + + + /* this structure is used to store the BlendDesignMap entry for an axis */ + typedef struct PS_DesignMap_ + { + FT_Byte num_points; + FT_Fixed* design_points; + FT_Fixed* blend_points; + + } PS_DesignMapRec, *PS_DesignMap; + + /* backwards-compatible definition */ + typedef PS_DesignMapRec T1_DesignMap; + + + typedef struct PS_BlendRec_ + { + FT_UInt num_designs; + FT_UInt num_axis; + + FT_String* axis_names[T1_MAX_MM_AXIS]; + FT_Fixed* design_pos[T1_MAX_MM_DESIGNS]; + PS_DesignMapRec design_map[T1_MAX_MM_AXIS]; + + FT_Fixed* weight_vector; + FT_Fixed* default_weight_vector; + + PS_FontInfo font_infos[T1_MAX_MM_DESIGNS + 1]; + PS_Private privates [T1_MAX_MM_DESIGNS + 1]; + + FT_ULong blend_bitflags; + + FT_BBox* bboxes [T1_MAX_MM_DESIGNS + 1]; + + } PS_BlendRec, *PS_Blend; + + + /* backwards-compatible definition */ + typedef PS_BlendRec T1_Blend; + + + typedef struct CID_FaceDictRec_ + { + PS_PrivateRec private_dict; + + FT_UInt len_buildchar; + FT_Fixed forcebold_threshold; + FT_Pos stroke_width; + FT_Fixed expansion_factor; + + FT_Byte paint_type; + FT_Byte font_type; + FT_Matrix font_matrix; + FT_Vector font_offset; + + FT_UInt num_subrs; + FT_ULong subrmap_offset; + FT_Int sd_bytes; + + } CID_FaceDictRec, *CID_FaceDict; + + + /* backwards-compatible definition */ + typedef CID_FaceDictRec CID_FontDict; + + + typedef struct CID_FaceInfoRec_ + { + FT_String* cid_font_name; + FT_Fixed cid_version; + FT_Int cid_font_type; + + FT_String* registry; + FT_String* ordering; + FT_Int supplement; + + PS_FontInfoRec font_info; + FT_BBox font_bbox; + FT_ULong uid_base; + + FT_Int num_xuid; + FT_ULong xuid[16]; + + FT_ULong cidmap_offset; + FT_Int fd_bytes; + FT_Int gd_bytes; + FT_ULong cid_count; + + FT_Int num_dicts; + CID_FaceDict font_dicts; + + FT_ULong data_offset; + + } CID_FaceInfoRec, *CID_FaceInfo; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_Info */ + /* */ + /* <Description> */ + /* This type is equivalent to CID_FaceInfoRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef CID_FaceInfoRec CID_Info; + + /* */ + + + /************************************************************************ + * + * @function: + * FT_Has_PS_Glyph_Names + * + * @description: + * Return true if a given face provides reliable Postscript glyph + * names. This is similar to using the @FT_HAS_GLYPH_NAMES macro, + * except that certain fonts (mostly TrueType) contain incorrect + * glyph name tables. + * + * When this function returns true, the caller is sure that the glyph + * names returned by @FT_Get_Glyph_Name are reliable. + * + * @input: + * face :: + * face handle + * + * @return: + * Boolean. True if glyph names are reliable. + */ + FT_EXPORT( FT_Int ) + FT_Has_PS_Glyph_Names( FT_Face face ); + + + /************************************************************************ + * + * @function: + * FT_Get_PS_Font_Info + * + * @description: + * Retrieve the @PS_FontInfoRec structure corresponding to a given + * Postscript font. + * + * @input: + * face :: + * Postscript face handle. + * + * @output: + * afont_info :: + * Output font info structure pointer. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * The string pointers within the font info structure are owned by + * the face and don't need to be freed by the caller. + * + * If the font's format is not Postscript-based, this function will + * return the FT_Err_Invalid_Argument error code. + */ + FT_EXPORT( FT_Error ) + FT_Get_PS_Font_Info( FT_Face face, + PS_FontInfoRec *afont_info ); + + /* */ + + + +FT_END_HEADER + +#endif /* __T1TABLES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/ttnameid.h b/extra_lib/include/freetype/freetype/ttnameid.h new file mode 100644 index 0000000..8b56686 --- /dev/null +++ b/extra_lib/include/freetype/freetype/ttnameid.h @@ -0,0 +1,1075 @@ +/***************************************************************************/ +/* */ +/* ttnameid.h */ +/* */ +/* TrueType name ID definitions (specification only). */ +/* */ +/* Copyright 1996-2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __TTNAMEID_H__ +#define __TTNAMEID_H__ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* Possible values for the `platform' identifier code in the name */ + /* records of the TTF `name' table. */ + /* */ + /*************************************************************************/ + + + /*********************************************************************** + * + * @enum: + * TT_PLATFORM_XXX + * + * @description: + * A list of valid values for the `platform_id' identifier code in + * @FT_CharMapRec and @FT_SfntName structures. + * + * @values: + * TT_PLATFORM_APPLE_UNICODE :: + * Used by Apple to indicate a Unicode character map and/or name entry. + * See @TT_APPLE_ID_XXX for corresponding `encoding_id' values. Note + * that name entries in this format are coded as big-endian UCS-2 + * character codes _only_. + * + * TT_PLATFORM_MACINTOSH :: + * Used by Apple to indicate a MacOS-specific charmap and/or name entry. + * See @TT_MAC_ID_XXX for corresponding `encoding_id' values. Note that + * most TrueType fonts contain an Apple roman charmap to be usable on + * MacOS systems (even if they contain a Microsoft charmap as well). + * + * TT_PLATFORM_ISO :: + * This value was used to specify Unicode charmaps. It is however + * now deprecated. See @TT_ISO_ID_XXX for a list of corresponding + * `encoding_id' values. + * + * TT_PLATFORM_MICROSOFT :: + * Used by Microsoft to indicate Windows-specific charmaps. See + * @TT_MS_ID_XXX for a list of corresponding `encoding_id' values. + * Note that most fonts contain a Unicode charmap using + * (@TT_PLATFORM_MICROSOFT, @TT_MS_ID_UNICODE_CS). + * + * TT_PLATFORM_CUSTOM :: + * Used to indicate application-specific charmaps. + * + * TT_PLATFORM_ADOBE :: + * This value isn't part of any font format specification, but is used + * by FreeType to report Adobe-specific charmaps in an @FT_CharMapRec + * structure. See @TT_ADOBE_ID_XXX. + */ + +#define TT_PLATFORM_APPLE_UNICODE 0 +#define TT_PLATFORM_MACINTOSH 1 +#define TT_PLATFORM_ISO 2 /* deprecated */ +#define TT_PLATFORM_MICROSOFT 3 +#define TT_PLATFORM_CUSTOM 4 +#define TT_PLATFORM_ADOBE 7 /* artificial */ + + + /*********************************************************************** + * + * @enum: + * TT_APPLE_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_APPLE_UNICODE charmaps and name entries. + * + * @values: + * TT_APPLE_ID_DEFAULT :: + * Unicode version 1.0. + * TT_APPLE_ID_UNICODE_1_1 :: + * Unicode 1.1; specifies Hangul characters starting at U+34xx. + * TT_APPLE_ID_ISO_10646 :: + * Deprecated (identical to preceding.) + * TT_APPLE_ID_UNICODE_2_0 :: + * Unicode 2.0 and beyond (UTF-16 BMP only.) + * TT_APPLE_ID_UNICODE_32 :: + * Unicode 3.1 and beyond, using UTF-32 + */ + +#define TT_APPLE_ID_DEFAULT 0 /* Unicode 1.0 */ +#define TT_APPLE_ID_UNICODE_1_1 1 /* specify Hangul at U+34xx */ +#define TT_APPLE_ID_ISO_10646 2 /* deprecated */ +#define TT_APPLE_ID_UNICODE_2_0 3 /* or later */ +#define TT_APPLE_ID_UNICODE_32 4 /* 2.0 or later, full repertoire */ + + + /*********************************************************************** + * + * @enum: + * TT_MAC_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_MACINTOSH charmaps and name entries. + * + * @values: + * TT_MAC_ID_ROMAN :: + * TT_MAC_ID_JAPANESE :: + * TT_MAC_ID_TRADITIONAL_CHINESE :: + * TT_MAC_ID_KOREAN :: + * TT_MAC_ID_ARABIC :: + * TT_MAC_ID_HEBREW :: + * TT_MAC_ID_GREEK :: + * TT_MAC_ID_RUSSIAN :: + * TT_MAC_ID_RSYMBOL :: + * TT_MAC_ID_DEVANAGARI :: + * TT_MAC_ID_GURMUKHI :: + * TT_MAC_ID_GUJARATI :: + * TT_MAC_ID_ORIYA :: + * TT_MAC_ID_BENGALI :: + * TT_MAC_ID_TAMIL :: + * TT_MAC_ID_TELUGU :: + * TT_MAC_ID_KANNADA :: + * TT_MAC_ID_MALAYALAM :: + * TT_MAC_ID_SINHALESE :: + * TT_MAC_ID_BURMESE :: + * TT_MAC_ID_KHMER :: + * TT_MAC_ID_THAI :: + * TT_MAC_ID_LAOTIAN :: + * TT_MAC_ID_GEORGIAN :: + * TT_MAC_ID_ARMENIAN :: + * TT_MAC_ID_MALDIVIAN :: + * TT_MAC_ID_SIMPLIFIED_CHINESE :: + * TT_MAC_ID_TIBETAN :: + * TT_MAC_ID_MONGOLIAN :: + * TT_MAC_ID_GEEZ :: + * TT_MAC_ID_SLAVIC :: + * TT_MAC_ID_VIETNAMESE :: + * TT_MAC_ID_SINDHI :: + * TT_MAC_ID_UNINTERP :: + */ + +#define TT_MAC_ID_ROMAN 0 +#define TT_MAC_ID_JAPANESE 1 +#define TT_MAC_ID_TRADITIONAL_CHINESE 2 +#define TT_MAC_ID_KOREAN 3 +#define TT_MAC_ID_ARABIC 4 +#define TT_MAC_ID_HEBREW 5 +#define TT_MAC_ID_GREEK 6 +#define TT_MAC_ID_RUSSIAN 7 +#define TT_MAC_ID_RSYMBOL 8 +#define TT_MAC_ID_DEVANAGARI 9 +#define TT_MAC_ID_GURMUKHI 10 +#define TT_MAC_ID_GUJARATI 11 +#define TT_MAC_ID_ORIYA 12 +#define TT_MAC_ID_BENGALI 13 +#define TT_MAC_ID_TAMIL 14 +#define TT_MAC_ID_TELUGU 15 +#define TT_MAC_ID_KANNADA 16 +#define TT_MAC_ID_MALAYALAM 17 +#define TT_MAC_ID_SINHALESE 18 +#define TT_MAC_ID_BURMESE 19 +#define TT_MAC_ID_KHMER 20 +#define TT_MAC_ID_THAI 21 +#define TT_MAC_ID_LAOTIAN 22 +#define TT_MAC_ID_GEORGIAN 23 +#define TT_MAC_ID_ARMENIAN 24 +#define TT_MAC_ID_MALDIVIAN 25 +#define TT_MAC_ID_SIMPLIFIED_CHINESE 25 +#define TT_MAC_ID_TIBETAN 26 +#define TT_MAC_ID_MONGOLIAN 27 +#define TT_MAC_ID_GEEZ 28 +#define TT_MAC_ID_SLAVIC 29 +#define TT_MAC_ID_VIETNAMESE 30 +#define TT_MAC_ID_SINDHI 31 +#define TT_MAC_ID_UNINTERP 32 + + + /*********************************************************************** + * + * @enum: + * TT_ISO_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_ISO charmaps and name entries. + * + * Their use is now deprecated. + * + * @values: + * TT_ISO_ID_7BIT_ASCII :: + * ASCII. + * TT_ISO_ID_10646 :: + * ISO/10646. + * TT_ISO_ID_8859_1 :: + * Also known as Latin-1. + */ + +#define TT_ISO_ID_7BIT_ASCII 0 +#define TT_ISO_ID_10646 1 +#define TT_ISO_ID_8859_1 2 + + + /*********************************************************************** + * + * @enum: + * TT_MS_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_MICROSOFT charmaps and name entries. + * + * @values: + * TT_MS_ID_SYMBOL_CS :: + * Corresponds to Microsoft symbol encoding. See + * @FT_ENCODING_MS_SYMBOL. + * + * TT_MS_ID_UNICODE_CS :: + * Corresponds to a Microsoft WGL4 charmap, matching Unicode. See + * @FT_ENCODING_UNICODE. + * + * TT_MS_ID_SJIS :: + * Corresponds to SJIS Japanese encoding. See @FT_ENCODING_SJIS. + * + * TT_MS_ID_GB2312 :: + * Corresponds to Simplified Chinese as used in Mainland China. See + * @FT_ENCODING_GB2312. + * + * TT_MS_ID_BIG_5 :: + * Corresponds to Traditional Chinese as used in Taiwan and Hong Kong. + * See @FT_ENCODING_BIG5. + * + * TT_MS_ID_WANSUNG :: + * Corresponds to Korean Wansung encoding. See @FT_ENCODING_WANSUNG. + * + * TT_MS_ID_JOHAB :: + * Corresponds to Johab encoding. See @FT_ENCODING_JOHAB. + * + * TT_MS_ID_UCS_4 :: + * Corresponds to UCS-4 or UTF-32 charmaps. This has been added to + * the OpenType specification version 1.4 (mid-2001.) + */ + +#define TT_MS_ID_SYMBOL_CS 0 +#define TT_MS_ID_UNICODE_CS 1 +#define TT_MS_ID_SJIS 2 +#define TT_MS_ID_GB2312 3 +#define TT_MS_ID_BIG_5 4 +#define TT_MS_ID_WANSUNG 5 +#define TT_MS_ID_JOHAB 6 +#define TT_MS_ID_UCS_4 10 + + + /*********************************************************************** + * + * @enum: + * TT_ADOBE_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_ADOBE charmaps. This is a FreeType-specific extension! + * + * @values: + * TT_ADOBE_ID_STANDARD :: + * Adobe standard encoding. + * TT_ADOBE_ID_EXPERT :: + * Adobe expert encoding. + * TT_ADOBE_ID_CUSTOM :: + * Adobe custom encoding. + */ + +#define TT_ADOBE_ID_STANDARD 0 +#define TT_ADOBE_ID_EXPERT 1 +#define TT_ADOBE_ID_CUSTOM 2 + + + /*************************************************************************/ + /* */ + /* Possible values of the language identifier field in the name records */ + /* of the TTF `name' table if the `platform' identifier code is */ + /* TT_PLATFORM_MACINTOSH. */ + /* */ + /* The canonical source for the Apple assigned Language ID's is at */ + /* */ + /* http://fonts.apple.com/TTRefMan/RM06/Chap6name.html */ + /* */ +#define TT_MAC_LANGID_ENGLISH 0 +#define TT_MAC_LANGID_FRENCH 1 +#define TT_MAC_LANGID_GERMAN 2 +#define TT_MAC_LANGID_ITALIAN 3 +#define TT_MAC_LANGID_DUTCH 4 +#define TT_MAC_LANGID_SWEDISH 5 +#define TT_MAC_LANGID_SPANISH 6 +#define TT_MAC_LANGID_DANISH 7 +#define TT_MAC_LANGID_PORTUGUESE 8 +#define TT_MAC_LANGID_NORWEGIAN 9 +#define TT_MAC_LANGID_HEBREW 10 +#define TT_MAC_LANGID_JAPANESE 11 +#define TT_MAC_LANGID_ARABIC 12 +#define TT_MAC_LANGID_FINNISH 13 +#define TT_MAC_LANGID_GREEK 14 +#define TT_MAC_LANGID_ICELANDIC 15 +#define TT_MAC_LANGID_MALTESE 16 +#define TT_MAC_LANGID_TURKISH 17 +#define TT_MAC_LANGID_CROATIAN 18 +#define TT_MAC_LANGID_CHINESE_TRADITIONAL 19 +#define TT_MAC_LANGID_URDU 20 +#define TT_MAC_LANGID_HINDI 21 +#define TT_MAC_LANGID_THAI 22 +#define TT_MAC_LANGID_KOREAN 23 +#define TT_MAC_LANGID_LITHUANIAN 24 +#define TT_MAC_LANGID_POLISH 25 +#define TT_MAC_LANGID_HUNGARIAN 26 +#define TT_MAC_LANGID_ESTONIAN 27 +#define TT_MAC_LANGID_LETTISH 28 +#define TT_MAC_LANGID_SAAMISK 29 +#define TT_MAC_LANGID_FAEROESE 30 +#define TT_MAC_LANGID_FARSI 31 +#define TT_MAC_LANGID_RUSSIAN 32 +#define TT_MAC_LANGID_CHINESE_SIMPLIFIED 33 +#define TT_MAC_LANGID_FLEMISH 34 +#define TT_MAC_LANGID_IRISH 35 +#define TT_MAC_LANGID_ALBANIAN 36 +#define TT_MAC_LANGID_ROMANIAN 37 +#define TT_MAC_LANGID_CZECH 38 +#define TT_MAC_LANGID_SLOVAK 39 +#define TT_MAC_LANGID_SLOVENIAN 40 +#define TT_MAC_LANGID_YIDDISH 41 +#define TT_MAC_LANGID_SERBIAN 42 +#define TT_MAC_LANGID_MACEDONIAN 43 +#define TT_MAC_LANGID_BULGARIAN 44 +#define TT_MAC_LANGID_UKRAINIAN 45 +#define TT_MAC_LANGID_BYELORUSSIAN 46 +#define TT_MAC_LANGID_UZBEK 47 +#define TT_MAC_LANGID_KAZAKH 48 +#define TT_MAC_LANGID_AZERBAIJANI 49 +#define TT_MAC_LANGID_AZERBAIJANI_CYRILLIC_SCRIPT 49 +#define TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT 50 +#define TT_MAC_LANGID_ARMENIAN 51 +#define TT_MAC_LANGID_GEORGIAN 52 +#define TT_MAC_LANGID_MOLDAVIAN 53 +#define TT_MAC_LANGID_KIRGHIZ 54 +#define TT_MAC_LANGID_TAJIKI 55 +#define TT_MAC_LANGID_TURKMEN 56 +#define TT_MAC_LANGID_MONGOLIAN 57 +#define TT_MAC_LANGID_MONGOLIAN_MONGOLIAN_SCRIPT 57 +#define TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT 58 +#define TT_MAC_LANGID_PASHTO 59 +#define TT_MAC_LANGID_KURDISH 60 +#define TT_MAC_LANGID_KASHMIRI 61 +#define TT_MAC_LANGID_SINDHI 62 +#define TT_MAC_LANGID_TIBETAN 63 +#define TT_MAC_LANGID_NEPALI 64 +#define TT_MAC_LANGID_SANSKRIT 65 +#define TT_MAC_LANGID_MARATHI 66 +#define TT_MAC_LANGID_BENGALI 67 +#define TT_MAC_LANGID_ASSAMESE 68 +#define TT_MAC_LANGID_GUJARATI 69 +#define TT_MAC_LANGID_PUNJABI 70 +#define TT_MAC_LANGID_ORIYA 71 +#define TT_MAC_LANGID_MALAYALAM 72 +#define TT_MAC_LANGID_KANNADA 73 +#define TT_MAC_LANGID_TAMIL 74 +#define TT_MAC_LANGID_TELUGU 75 +#define TT_MAC_LANGID_SINHALESE 76 +#define TT_MAC_LANGID_BURMESE 77 +#define TT_MAC_LANGID_KHMER 78 +#define TT_MAC_LANGID_LAO 79 +#define TT_MAC_LANGID_VIETNAMESE 80 +#define TT_MAC_LANGID_INDONESIAN 81 +#define TT_MAC_LANGID_TAGALOG 82 +#define TT_MAC_LANGID_MALAY_ROMAN_SCRIPT 83 +#define TT_MAC_LANGID_MALAY_ARABIC_SCRIPT 84 +#define TT_MAC_LANGID_AMHARIC 85 +#define TT_MAC_LANGID_TIGRINYA 86 +#define TT_MAC_LANGID_GALLA 87 +#define TT_MAC_LANGID_SOMALI 88 +#define TT_MAC_LANGID_SWAHILI 89 +#define TT_MAC_LANGID_RUANDA 90 +#define TT_MAC_LANGID_RUNDI 91 +#define TT_MAC_LANGID_CHEWA 92 +#define TT_MAC_LANGID_MALAGASY 93 +#define TT_MAC_LANGID_ESPERANTO 94 +#define TT_MAC_LANGID_WELSH 128 +#define TT_MAC_LANGID_BASQUE 129 +#define TT_MAC_LANGID_CATALAN 130 +#define TT_MAC_LANGID_LATIN 131 +#define TT_MAC_LANGID_QUECHUA 132 +#define TT_MAC_LANGID_GUARANI 133 +#define TT_MAC_LANGID_AYMARA 134 +#define TT_MAC_LANGID_TATAR 135 +#define TT_MAC_LANGID_UIGHUR 136 +#define TT_MAC_LANGID_DZONGKHA 137 +#define TT_MAC_LANGID_JAVANESE 138 +#define TT_MAC_LANGID_SUNDANESE 139 + + +#if 0 /* these seem to be errors that have been dropped */ + +#define TT_MAC_LANGID_SCOTTISH_GAELIC 140 +#define TT_MAC_LANGID_IRISH_GAELIC 141 + +#endif + + + /* The following codes are new as of 2000-03-10 */ +#define TT_MAC_LANGID_GALICIAN 140 +#define TT_MAC_LANGID_AFRIKAANS 141 +#define TT_MAC_LANGID_BRETON 142 +#define TT_MAC_LANGID_INUKTITUT 143 +#define TT_MAC_LANGID_SCOTTISH_GAELIC 144 +#define TT_MAC_LANGID_MANX_GAELIC 145 +#define TT_MAC_LANGID_IRISH_GAELIC 146 +#define TT_MAC_LANGID_TONGAN 147 +#define TT_MAC_LANGID_GREEK_POLYTONIC 148 +#define TT_MAC_LANGID_GREELANDIC 149 +#define TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT 150 + + + /*************************************************************************/ + /* */ + /* Possible values of the language identifier field in the name records */ + /* of the TTF `name' table if the `platform' identifier code is */ + /* TT_PLATFORM_MICROSOFT. */ + /* */ + /* The canonical source for the MS assigned LCID's used to be at */ + /* */ + /* http://www.microsoft.com/typography/OTSPEC/lcid-cp.txt */ + /* */ + /* Now (2002-11-15), the Microsoft site directs to */ + /* */ + /* http://www.microsoft.com/globaldev/reference/loclanghome.asp */ + /* http://support.microsoft.com/support/kb/articles/Q224/8/04.ASP */ + /* */ +#define TT_MS_LANGID_ARABIC_SAUDI_ARABIA 0x0401 +#define TT_MS_LANGID_ARABIC_IRAQ 0x0801 +#define TT_MS_LANGID_ARABIC_EGYPT 0x0c01 +#define TT_MS_LANGID_ARABIC_LIBYA 0x1001 +#define TT_MS_LANGID_ARABIC_ALGERIA 0x1401 +#define TT_MS_LANGID_ARABIC_MOROCCO 0x1801 +#define TT_MS_LANGID_ARABIC_TUNISIA 0x1c01 +#define TT_MS_LANGID_ARABIC_OMAN 0x2001 +#define TT_MS_LANGID_ARABIC_YEMEN 0x2401 +#define TT_MS_LANGID_ARABIC_SYRIA 0x2801 +#define TT_MS_LANGID_ARABIC_JORDAN 0x2c01 +#define TT_MS_LANGID_ARABIC_LEBANON 0x3001 +#define TT_MS_LANGID_ARABIC_KUWAIT 0x3401 +#define TT_MS_LANGID_ARABIC_UAE 0x3801 +#define TT_MS_LANGID_ARABIC_BAHRAIN 0x3c01 +#define TT_MS_LANGID_ARABIC_QATAR 0x4001 +#define TT_MS_LANGID_BULGARIAN_BULGARIA 0x0402 +#define TT_MS_LANGID_CATALAN_SPAIN 0x0403 +#define TT_MS_LANGID_CHINESE_TAIWAN 0x0404 +#define TT_MS_LANGID_CHINESE_PRC 0x0804 +#define TT_MS_LANGID_CHINESE_HONG_KONG 0x0c04 +#define TT_MS_LANGID_CHINESE_SINGAPORE 0x1004 + +#if 1 /* this used to be this value (and it still is in many places) */ +#define TT_MS_LANGID_CHINESE_MACAU 0x1404 +#else /* but beware, Microsoft may change its mind... + the most recent Word reference has the following: */ +#define TT_MS_LANGID_CHINESE_MACAU TT_MS_LANGID_CHINESE_HONG_KONG +#endif + +#define TT_MS_LANGID_CZECH_CZECH_REPUBLIC 0x0405 +#define TT_MS_LANGID_DANISH_DENMARK 0x0406 +#define TT_MS_LANGID_GERMAN_GERMANY 0x0407 +#define TT_MS_LANGID_GERMAN_SWITZERLAND 0x0807 +#define TT_MS_LANGID_GERMAN_AUSTRIA 0x0c07 +#define TT_MS_LANGID_GERMAN_LUXEMBOURG 0x1007 +#define TT_MS_LANGID_GERMAN_LIECHTENSTEI 0x1407 +#define TT_MS_LANGID_GREEK_GREECE 0x0408 +#define TT_MS_LANGID_ENGLISH_UNITED_STATES 0x0409 +#define TT_MS_LANGID_ENGLISH_UNITED_KINGDOM 0x0809 +#define TT_MS_LANGID_ENGLISH_AUSTRALIA 0x0c09 +#define TT_MS_LANGID_ENGLISH_CANADA 0x1009 +#define TT_MS_LANGID_ENGLISH_NEW_ZEALAND 0x1409 +#define TT_MS_LANGID_ENGLISH_IRELAND 0x1809 +#define TT_MS_LANGID_ENGLISH_SOUTH_AFRICA 0x1c09 +#define TT_MS_LANGID_ENGLISH_JAMAICA 0x2009 +#define TT_MS_LANGID_ENGLISH_CARIBBEAN 0x2409 +#define TT_MS_LANGID_ENGLISH_BELIZE 0x2809 +#define TT_MS_LANGID_ENGLISH_TRINIDAD 0x2c09 +#define TT_MS_LANGID_ENGLISH_ZIMBABWE 0x3009 +#define TT_MS_LANGID_ENGLISH_PHILIPPINES 0x3409 +#define TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT 0x040a +#define TT_MS_LANGID_SPANISH_MEXICO 0x080a +#define TT_MS_LANGID_SPANISH_SPAIN_INTERNATIONAL_SORT 0x0c0a +#define TT_MS_LANGID_SPANISH_GUATEMALA 0x100a +#define TT_MS_LANGID_SPANISH_COSTA_RICA 0x140a +#define TT_MS_LANGID_SPANISH_PANAMA 0x180a +#define TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC 0x1c0a +#define TT_MS_LANGID_SPANISH_VENEZUELA 0x200a +#define TT_MS_LANGID_SPANISH_COLOMBIA 0x240a +#define TT_MS_LANGID_SPANISH_PERU 0x280a +#define TT_MS_LANGID_SPANISH_ARGENTINA 0x2c0a +#define TT_MS_LANGID_SPANISH_ECUADOR 0x300a +#define TT_MS_LANGID_SPANISH_CHILE 0x340a +#define TT_MS_LANGID_SPANISH_URUGUAY 0x380a +#define TT_MS_LANGID_SPANISH_PARAGUAY 0x3c0a +#define TT_MS_LANGID_SPANISH_BOLIVIA 0x400a +#define TT_MS_LANGID_SPANISH_EL_SALVADOR 0x440a +#define TT_MS_LANGID_SPANISH_HONDURAS 0x480a +#define TT_MS_LANGID_SPANISH_NICARAGUA 0x4c0a +#define TT_MS_LANGID_SPANISH_PUERTO_RICO 0x500a +#define TT_MS_LANGID_FINNISH_FINLAND 0x040b +#define TT_MS_LANGID_FRENCH_FRANCE 0x040c +#define TT_MS_LANGID_FRENCH_BELGIUM 0x080c +#define TT_MS_LANGID_FRENCH_CANADA 0x0c0c +#define TT_MS_LANGID_FRENCH_SWITZERLAND 0x100c +#define TT_MS_LANGID_FRENCH_LUXEMBOURG 0x140c +#define TT_MS_LANGID_FRENCH_MONACO 0x180c +#define TT_MS_LANGID_HEBREW_ISRAEL 0x040d +#define TT_MS_LANGID_HUNGARIAN_HUNGARY 0x040e +#define TT_MS_LANGID_ICELANDIC_ICELAND 0x040f +#define TT_MS_LANGID_ITALIAN_ITALY 0x0410 +#define TT_MS_LANGID_ITALIAN_SWITZERLAND 0x0810 +#define TT_MS_LANGID_JAPANESE_JAPAN 0x0411 +#define TT_MS_LANGID_KOREAN_EXTENDED_WANSUNG_KOREA 0x0412 +#define TT_MS_LANGID_KOREAN_JOHAB_KOREA 0x0812 +#define TT_MS_LANGID_DUTCH_NETHERLANDS 0x0413 +#define TT_MS_LANGID_DUTCH_BELGIUM 0x0813 +#define TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL 0x0414 +#define TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK 0x0814 +#define TT_MS_LANGID_POLISH_POLAND 0x0415 +#define TT_MS_LANGID_PORTUGUESE_BRAZIL 0x0416 +#define TT_MS_LANGID_PORTUGUESE_PORTUGAL 0x0816 +#define TT_MS_LANGID_RHAETO_ROMANIC_SWITZERLAND 0x0417 +#define TT_MS_LANGID_ROMANIAN_ROMANIA 0x0418 +#define TT_MS_LANGID_MOLDAVIAN_MOLDAVIA 0x0818 +#define TT_MS_LANGID_RUSSIAN_RUSSIA 0x0419 +#define TT_MS_LANGID_RUSSIAN_MOLDAVIA 0x0819 +#define TT_MS_LANGID_CROATIAN_CROATIA 0x041a +#define TT_MS_LANGID_SERBIAN_SERBIA_LATIN 0x081a +#define TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC 0x0c1a +#define TT_MS_LANGID_SLOVAK_SLOVAKIA 0x041b +#define TT_MS_LANGID_ALBANIAN_ALBANIA 0x041c +#define TT_MS_LANGID_SWEDISH_SWEDEN 0x041d +#define TT_MS_LANGID_SWEDISH_FINLAND 0x081d +#define TT_MS_LANGID_THAI_THAILAND 0x041e +#define TT_MS_LANGID_TURKISH_TURKEY 0x041f +#define TT_MS_LANGID_URDU_PAKISTAN 0x0420 +#define TT_MS_LANGID_INDONESIAN_INDONESIA 0x0421 +#define TT_MS_LANGID_UKRAINIAN_UKRAINE 0x0422 +#define TT_MS_LANGID_BELARUSIAN_BELARUS 0x0423 +#define TT_MS_LANGID_SLOVENE_SLOVENIA 0x0424 +#define TT_MS_LANGID_ESTONIAN_ESTONIA 0x0425 +#define TT_MS_LANGID_LATVIAN_LATVIA 0x0426 +#define TT_MS_LANGID_LITHUANIAN_LITHUANIA 0x0427 +#define TT_MS_LANGID_CLASSIC_LITHUANIAN_LITHUANIA 0x0827 + +#if 0 /* this seems to be an error that have been dropped */ +#define TT_MS_LANGID_MAORI_NEW_ZEALAND 0x0428 +#endif + +#define TT_MS_LANGID_FARSI_IRAN 0x0429 +#define TT_MS_LANGID_VIETNAMESE_VIET_NAM 0x042a +#define TT_MS_LANGID_ARMENIAN_ARMENIA 0x042b +#define TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN 0x042c +#define TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC 0x082c +#define TT_MS_LANGID_BASQUE_SPAIN 0x042d +#define TT_MS_LANGID_SORBIAN_GERMANY 0x042e +#define TT_MS_LANGID_MACEDONIAN_MACEDONIA 0x042f +#define TT_MS_LANGID_SUTU_SOUTH_AFRICA 0x0430 +#define TT_MS_LANGID_TSONGA_SOUTH_AFRICA 0x0431 +#define TT_MS_LANGID_TSWANA_SOUTH_AFRICA 0x0432 +#define TT_MS_LANGID_VENDA_SOUTH_AFRICA 0x0433 +#define TT_MS_LANGID_XHOSA_SOUTH_AFRICA 0x0434 +#define TT_MS_LANGID_ZULU_SOUTH_AFRICA 0x0435 +#define TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA 0x0436 +#define TT_MS_LANGID_GEORGIAN_GEORGIA 0x0437 +#define TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS 0x0438 +#define TT_MS_LANGID_HINDI_INDIA 0x0439 +#define TT_MS_LANGID_MALTESE_MALTA 0x043a +#define TT_MS_LANGID_SAAMI_LAPONIA 0x043b + +#if 0 /* this seems to be a previous invertion */ +#define TT_MS_LANGID_IRISH_GAELIC_IRELAND 0x043c +#define TT_MS_LANGID_SCOTTISH_GAELIC_UNITED_KINGDOM 0x083c +#else +#define TT_MS_LANGID_SCOTTISH_GAELIC_UNITED_KINGDOM 0x083c +#define TT_MS_LANGID_IRISH_GAELIC_IRELAND 0x043c +#endif + +#define TT_MS_LANGID_MALAY_MALAYSIA 0x043e +#define TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM 0x083e +#define TT_MS_LANGID_KAZAK_KAZAKSTAN 0x043f +#define TT_MS_LANGID_SWAHILI_KENYA 0x0441 +#define TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN 0x0443 +#define TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC 0x0843 +#define TT_MS_LANGID_TATAR_TATARSTAN 0x0444 +#define TT_MS_LANGID_BENGALI_INDIA 0x0445 +#define TT_MS_LANGID_PUNJABI_INDIA 0x0446 +#define TT_MS_LANGID_GUJARATI_INDIA 0x0447 +#define TT_MS_LANGID_ORIYA_INDIA 0x0448 +#define TT_MS_LANGID_TAMIL_INDIA 0x0449 +#define TT_MS_LANGID_TELUGU_INDIA 0x044a +#define TT_MS_LANGID_KANNADA_INDIA 0x044b +#define TT_MS_LANGID_MALAYALAM_INDIA 0x044c +#define TT_MS_LANGID_ASSAMESE_INDIA 0x044d +#define TT_MS_LANGID_MARATHI_INDIA 0x044e +#define TT_MS_LANGID_SANSKRIT_INDIA 0x044f +#define TT_MS_LANGID_KONKANI_INDIA 0x0457 + + /* new as of 2001-01-01 */ +#define TT_MS_LANGID_ARABIC_GENERAL 0x0001 +#define TT_MS_LANGID_CHINESE_GENERAL 0x0004 +#define TT_MS_LANGID_ENGLISH_GENERAL 0x0009 +#define TT_MS_LANGID_FRENCH_WEST_INDIES 0x1c0c +#define TT_MS_LANGID_FRENCH_REUNION 0x200c +#define TT_MS_LANGID_FRENCH_CONGO 0x240c + /* which was formerly: */ +#define TT_MS_LANGID_FRENCH_ZAIRE TT_MS_LANGID_FRENCH_CONGO + +#define TT_MS_LANGID_FRENCH_SENEGAL 0x280c +#define TT_MS_LANGID_FRENCH_CAMEROON 0x2c0c +#define TT_MS_LANGID_FRENCH_COTE_D_IVOIRE 0x300c +#define TT_MS_LANGID_FRENCH_MALI 0x340c +#define TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA 0x101a +#define TT_MS_LANGID_URDU_INDIA 0x0820 +#define TT_MS_LANGID_TAJIK_TAJIKISTAN 0x0428 +#define TT_MS_LANGID_YIDDISH_GERMANY 0x043d +#define TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN 0x0440 + /* alias declared in Windows 2000 */ +#define TT_MS_LANGID_KIRGHIZ_KIRGHIZ_REPUBLIC \ + TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN + +#define TT_MS_LANGID_TURKMEN_TURKMENISTAN 0x0442 +#define TT_MS_LANGID_MONGOLIAN_MONGOLIA /* Cyrillic */ 0x0450 + + /* the following seems to be inconsistent; + here is the current "official" way: */ +#define TT_MS_LANGID_TIBETAN_BHUTAN 0x0451 + /* and here is what is used by Passport SDK */ +#define TT_MS_LANGID_TIBETAN_CHINA 0x0451 +#define TT_MS_LANGID_DZONGHKA_BHUTAN 0x0851 + /* end of inconsistency */ + +#define TT_MS_LANGID_WELSH_WALES 0x0452 +#define TT_MS_LANGID_KHMER_CAMBODIA 0x0453 +#define TT_MS_LANGID_LAO_LAOS 0x0454 +#define TT_MS_LANGID_BURMESE_MYANMAR 0x0455 +#define TT_MS_LANGID_GALICIAN_SPAIN 0x0456 +#define TT_MS_LANGID_MANIPURI_INDIA 0x0458 +#define TT_MS_LANGID_SINDHI_INDIA 0x0459 + /* the following one is only encountered in Microsoft RTF specification */ +#define TT_MS_LANGID_KASHMIRI_PAKISTAN 0x0460 + /* the following one is not in the Passport list, looks like an omission */ +#define TT_MS_LANGID_KASHMIRI_INDIA 0x0860 +#define TT_MS_LANGID_NEPALI_NEPAL 0x0461 +#define TT_MS_LANGID_NEPALI_INDIA 0x0861 +#define TT_MS_LANGID_FRISIAN_NETHERLANDS 0x0462 + + /* new as of 2001-03-01 (from Office Xp) */ +#define TT_MS_LANGID_ENGLISH_HONG_KONG 0x3c09 +#define TT_MS_LANGID_ENGLISH_INDIA 0x4009 +#define TT_MS_LANGID_ENGLISH_MALAYSIA 0x4409 +#define TT_MS_LANGID_ENGLISH_SINGAPORE 0x4809 +#define TT_MS_LANGID_SYRIAC_SYRIA 0x045a +#define TT_MS_LANGID_SINHALESE_SRI_LANKA 0x045b +#define TT_MS_LANGID_CHEROKEE_UNITED_STATES 0x045c +#define TT_MS_LANGID_INUKTITUT_CANADA 0x045d +#define TT_MS_LANGID_AMHARIC_ETHIOPIA 0x045e +#define TT_MS_LANGID_TAMAZIGHT_MOROCCO 0x045f +#define TT_MS_LANGID_TAMAZIGHT_MOROCCO_LATIN 0x085f +#define TT_MS_LANGID_PASHTO_AFGHANISTAN 0x0463 +#define TT_MS_LANGID_FILIPINO_PHILIPPINES 0x0464 +#define TT_MS_LANGID_DHIVEHI_MALDIVES 0x0465 + /* alias declared in Windows 2000 */ +#define TT_MS_LANGID_DIVEHI_MALDIVES TT_MS_LANGID_DHIVEHI_MALDIVES + /* for language codes from 0x0466 to 0x0471 see below */ +#define TT_MS_LANGID_OROMO_ETHIOPIA 0x0472 +#define TT_MS_LANGID_TIGRIGNA_ETHIOPIA 0x0473 +#define TT_MS_LANGID_TIGRIGNA_ERYTHREA 0x0873 + /* also spelled in the `Passport SDK' list as: */ +#define TT_MS_LANGID_TIGRIGNA_ERYTREA TT_MS_LANGID_TIGRIGNA_ERYTHREA + + /* New additions from Windows Xp/Passport SDK 2001-11-10. */ + + /* don't ask what this one means... It is commented out currently. */ +#if 0 +#define TT_MS_LANGID_GREEK_GREECE2 0x2008 +#endif + +#define TT_MS_LANGID_SPANISH_UNITED_STATES 0x540a + /* The following two IDs blatantly violate MS specs by using a */ + /* sublanguage > 0x1F. */ +#define TT_MS_LANGID_SPANISH_LATIN_AMERICA 0xE40aU +#define TT_MS_LANGID_FRENCH_NORTH_AFRICA 0xE40cU + +#define TT_MS_LANGID_FRENCH_MOROCCO 0x380c +#define TT_MS_LANGID_FRENCH_HAITI 0x3c0c +#define TT_MS_LANGID_BENGALI_BANGLADESH 0x0845 +#define TT_MS_LANGID_PUNJABI_ARABIC_PAKISTAN 0x0846 +#define TT_MS_LANGID_MONGOLIAN_MONGOLIA_MONGOLIAN 0x0850 +#define TT_MS_LANGID_EDO_NIGERIA 0x0466 +#define TT_MS_LANGID_FULFULDE_NIGERIA 0x0467 +#define TT_MS_LANGID_HAUSA_NIGERIA 0x0468 +#define TT_MS_LANGID_IBIBIO_NIGERIA 0x0469 +#define TT_MS_LANGID_YORUBA_NIGERIA 0x046a + /* language codes from 0x046b to 0x046f are (still) unknown. */ +#define TT_MS_LANGID_IGBO_NIGERIA 0x0470 +#define TT_MS_LANGID_KANURI_NIGERIA 0x0471 +#define TT_MS_LANGID_GUARANI_PARAGUAY 0x0474 +#define TT_MS_LANGID_HAWAIIAN_UNITED_STATES 0x0475 +#define TT_MS_LANGID_LATIN 0x0476 +#define TT_MS_LANGID_SOMALI_SOMALIA 0x0477 + /* Note: Yi does not have a (proper) ISO 639-2 code, since it is mostly */ + /* not written (but OTOH the peculiar writing system is worth */ + /* studying). */ +#define TT_MS_LANGID_YI_CHINA 0x0478 +#define TT_MS_LANGID_PAPIAMENTU_NETHERLANDS_ANTILLES 0x0479 + + + /*************************************************************************/ + /* */ + /* Possible values of the `name' identifier field in the name records of */ + /* the TTF `name' table. These values are platform independent. */ + /* */ +#define TT_NAME_ID_COPYRIGHT 0 +#define TT_NAME_ID_FONT_FAMILY 1 +#define TT_NAME_ID_FONT_SUBFAMILY 2 +#define TT_NAME_ID_UNIQUE_ID 3 +#define TT_NAME_ID_FULL_NAME 4 +#define TT_NAME_ID_VERSION_STRING 5 +#define TT_NAME_ID_PS_NAME 6 +#define TT_NAME_ID_TRADEMARK 7 + + /* the following values are from the OpenType spec */ +#define TT_NAME_ID_MANUFACTURER 8 +#define TT_NAME_ID_DESIGNER 9 +#define TT_NAME_ID_DESCRIPTION 10 +#define TT_NAME_ID_VENDOR_URL 11 +#define TT_NAME_ID_DESIGNER_URL 12 +#define TT_NAME_ID_LICENSE 13 +#define TT_NAME_ID_LICENSE_URL 14 + /* number 15 is reserved */ +#define TT_NAME_ID_PREFERRED_FAMILY 16 +#define TT_NAME_ID_PREFERRED_SUBFAMILY 17 +#define TT_NAME_ID_MAC_FULL_NAME 18 + + /* The following code is new as of 2000-01-21 */ +#define TT_NAME_ID_SAMPLE_TEXT 19 + + /* This is new in OpenType 1.3 */ +#define TT_NAME_ID_CID_FINDFONT_NAME 20 + + + /*************************************************************************/ + /* */ + /* Bit mask values for the Unicode Ranges from the TTF `OS2 ' table. */ + /* */ + /* Updated 02-Jul-2000. */ + /* */ + + /* General Scripts Area */ + + /* Bit 0 Basic Latin */ +#define TT_UCR_BASIC_LATIN (1L << 0) /* U+0020-U+007E */ + /* Bit 1 C1 Controls and Latin-1 Supplement */ +#define TT_UCR_LATIN1_SUPPLEMENT (1L << 1) /* U+0080-U+00FF */ + /* Bit 2 Latin Extended-A */ +#define TT_UCR_LATIN_EXTENDED_A (1L << 2) /* U+0100-U+017F */ + /* Bit 3 Latin Extended-B */ +#define TT_UCR_LATIN_EXTENDED_B (1L << 3) /* U+0180-U+024F */ + /* Bit 4 IPA Extensions */ +#define TT_UCR_IPA_EXTENSIONS (1L << 4) /* U+0250-U+02AF */ + /* Bit 5 Spacing Modifier Letters */ +#define TT_UCR_SPACING_MODIFIER (1L << 5) /* U+02B0-U+02FF */ + /* Bit 6 Combining Diacritical Marks */ +#define TT_UCR_COMBINING_DIACRITICS (1L << 6) /* U+0300-U+036F */ + /* Bit 7 Greek and Coptic */ +#define TT_UCR_GREEK (1L << 7) /* U+0370-U+03FF */ + /* Bit 8 is reserved (was: Greek Symbols and Coptic) */ + /* Bit 9 Cyrillic + */ + /* Cyrillic Supplementary */ +#define TT_UCR_CYRILLIC (1L << 9) /* U+0400-U+04FF */ + /* U+0500-U+052F */ + /* Bit 10 Armenian */ +#define TT_UCR_ARMENIAN (1L << 10) /* U+0530-U+058F */ + /* Bit 11 Hebrew */ +#define TT_UCR_HEBREW (1L << 11) /* U+0590-U+05FF */ + /* Bit 12 is reserved (was: Hebrew Extended) */ + /* Bit 13 Arabic */ +#define TT_UCR_ARABIC (1L << 13) /* U+0600-U+06FF */ + /* Bit 14 is reserved (was: Arabic Extended) */ + /* Bit 15 Devanagari */ +#define TT_UCR_DEVANAGARI (1L << 15) /* U+0900-U+097F */ + /* Bit 16 Bengali */ +#define TT_UCR_BENGALI (1L << 16) /* U+0980-U+09FF */ + /* Bit 17 Gurmukhi */ +#define TT_UCR_GURMUKHI (1L << 17) /* U+0A00-U+0A7F */ + /* Bit 18 Gujarati */ +#define TT_UCR_GUJARATI (1L << 18) /* U+0A80-U+0AFF */ + /* Bit 19 Oriya */ +#define TT_UCR_ORIYA (1L << 19) /* U+0B00-U+0B7F */ + /* Bit 20 Tamil */ +#define TT_UCR_TAMIL (1L << 20) /* U+0B80-U+0BFF */ + /* Bit 21 Telugu */ +#define TT_UCR_TELUGU (1L << 21) /* U+0C00-U+0C7F */ + /* Bit 22 Kannada */ +#define TT_UCR_KANNADA (1L << 22) /* U+0C80-U+0CFF */ + /* Bit 23 Malayalam */ +#define TT_UCR_MALAYALAM (1L << 23) /* U+0D00-U+0D7F */ + /* Bit 24 Thai */ +#define TT_UCR_THAI (1L << 24) /* U+0E00-U+0E7F */ + /* Bit 25 Lao */ +#define TT_UCR_LAO (1L << 25) /* U+0E80-U+0EFF */ + /* Bit 26 Georgian */ +#define TT_UCR_GEORGIAN (1L << 26) /* U+10A0-U+10FF */ + /* Bit 27 is reserved (was Georgian Extended) */ + /* Bit 28 Hangul Jamo */ +#define TT_UCR_HANGUL_JAMO (1L << 28) /* U+1100-U+11FF */ + /* Bit 29 Latin Extended Additional */ +#define TT_UCR_LATIN_EXTENDED_ADDITIONAL (1L << 29) /* U+1E00-U+1EFF */ + /* Bit 30 Greek Extended */ +#define TT_UCR_GREEK_EXTENDED (1L << 30) /* U+1F00-U+1FFF */ + + /* Symbols Area */ + + /* Bit 31 General Punctuation */ +#define TT_UCR_GENERAL_PUNCTUATION (1L << 31) /* U+2000-U+206F */ + /* Bit 32 Superscripts And Subscripts */ +#define TT_UCR_SUPERSCRIPTS_SUBSCRIPTS (1L << 0) /* U+2070-U+209F */ + /* Bit 33 Currency Symbols */ +#define TT_UCR_CURRENCY_SYMBOLS (1L << 1) /* U+20A0-U+20CF */ + /* Bit 34 Combining Diacritical Marks For Symbols */ +#define TT_UCR_COMBINING_DIACRITICS_SYMB (1L << 2) /* U+20D0-U+20FF */ + /* Bit 35 Letterlike Symbols */ +#define TT_UCR_LETTERLIKE_SYMBOLS (1L << 3) /* U+2100-U+214F */ + /* Bit 36 Number Forms */ +#define TT_UCR_NUMBER_FORMS (1L << 4) /* U+2150-U+218F */ + /* Bit 37 Arrows + */ + /* Supplemental Arrows-A + */ + /* Supplemental Arrows-B */ +#define TT_UCR_ARROWS (1L << 5) /* U+2190-U+21FF */ + /* U+27F0-U+27FF */ + /* U+2900-U+297F */ + /* Bit 38 Mathematical Operators + */ + /* Supplemental Mathematical Operators + */ + /* Miscellaneous Mathematical Symbols-A + */ + /* Miscellaneous Mathematical Symbols-B */ +#define TT_UCR_MATHEMATICAL_OPERATORS (1L << 6) /* U+2200-U+22FF */ + /* U+2A00-U+2AFF */ + /* U+27C0-U+27EF */ + /* U+2980-U+29FF */ + /* Bit 39 Miscellaneous Technical */ +#define TT_UCR_MISCELLANEOUS_TECHNICAL (1L << 7) /* U+2300-U+23FF */ + /* Bit 40 Control Pictures */ +#define TT_UCR_CONTROL_PICTURES (1L << 8) /* U+2400-U+243F */ + /* Bit 41 Optical Character Recognition */ +#define TT_UCR_OCR (1L << 9) /* U+2440-U+245F */ + /* Bit 42 Enclosed Alphanumerics */ +#define TT_UCR_ENCLOSED_ALPHANUMERICS (1L << 10) /* U+2460-U+24FF */ + /* Bit 43 Box Drawing */ +#define TT_UCR_BOX_DRAWING (1L << 11) /* U+2500-U+257F */ + /* Bit 44 Block Elements */ +#define TT_UCR_BLOCK_ELEMENTS (1L << 12) /* U+2580-U+259F */ + /* Bit 45 Geometric Shapes */ +#define TT_UCR_GEOMETRIC_SHAPES (1L << 13) /* U+25A0-U+25FF */ + /* Bit 46 Miscellaneous Symbols */ +#define TT_UCR_MISCELLANEOUS_SYMBOLS (1L << 14) /* U+2600-U+26FF */ + /* Bit 47 Dingbats */ +#define TT_UCR_DINGBATS (1L << 15) /* U+2700-U+27BF */ + + /* CJK Phonetics and Symbols Area */ + + /* Bit 48 CJK Symbols and Punctuation */ +#define TT_UCR_CJK_SYMBOLS (1L << 16) /* U+3000-U+303F */ + /* Bit 49 Hiragana */ +#define TT_UCR_HIRAGANA (1L << 17) /* U+3040-U+309F */ + /* Bit 50 Katakana + */ + /* Katakana Phonetic Extensions */ +#define TT_UCR_KATAKANA (1L << 18) /* U+30A0-U+30FF */ + /* U+31F0-U+31FF */ + /* Bit 51 Bopomofo + */ + /* Bopomofo Extended */ +#define TT_UCR_BOPOMOFO (1L << 19) /* U+3100-U+312F */ + /* U+31A0-U+31BF */ + /* Bit 52 Hangul Compatibility Jamo */ +#define TT_UCR_HANGUL_COMPATIBILITY_JAMO (1L << 20) /* U+3130-U+318F */ + /* Bit 53 Kanbun */ +#define TT_UCR_CJK_MISC (1L << 21) /* U+3190-U+319F */ +#define TT_UCR_KANBUN TT_UCR_CJK_MISC + /* Bit 54 Enclosed CJK Letters and Months */ +#define TT_UCR_ENCLOSED_CJK_LETTERS_MONTHS (1L << 22) /* U+3200-U+32FF */ + /* Bit 55 CJK Compatibility */ +#define TT_UCR_CJK_COMPATIBILITY (1L << 23) /* U+3300-U+33FF */ + + /* Hangul Syllables Area */ + + /* Bit 56 Hangul */ +#define TT_UCR_HANGUL (1L << 24) /* U+AC00-U+D7A3 */ + + /* Surrogates Area */ + + /* Bit 57 High Surrogates + */ + /* High Private Use Surrogates + */ + /* Low Surrogates */ +#define TT_UCR_SURROGATES (1L << 25) /* U+D800-U+DB7F */ + /* U+DB80-U+DBFF */ + /* U+DC00-U+DFFF */ + /* According to OpenType specs v.1.3+, setting bit 57 implies that there */ + /* is at least one codepoint beyond the Basic Multilingual Plane that is */ + /* supported by this font. So it really means: >= U+10000 */ + + /* Bit 58 is reserved for Unicode SubRanges */ + + /* CJK Ideographs Area */ + + /* Bit 59 CJK Unified Ideographs + */ + /* CJK Radicals Supplement + */ + /* Kangxi Radicals + */ + /* Ideographic Description Characters + */ + /* CJK Unified Ideographs Extension A */ + /* CJK Unified Ideographs Extension A + */ + /* CJK Unified Ideographs Extension B + */ + /* Kanbun */ +#define TT_UCR_CJK_UNIFIED_IDEOGRAPHS (1L << 27) /* U+4E00-U+9FFF */ + /* U+2E80-U+2EFF */ + /* U+2F00-U+2FDF */ + /* U+2FF0-U+2FFF */ + /* U+3400-U+4DB5 */ + /*U+20000-U+2A6DF*/ + /* U+3190-U+319F */ + + /* Private Use Area */ + + /* Bit 60 Private Use */ +#define TT_UCR_PRIVATE_USE (1L << 28) /* U+E000-U+F8FF */ + + /* Compatibility Area and Specials */ + + /* Bit 61 CJK Compatibility Ideographs + */ + /* CJK Compatibility Ideographs Supplement */ +#define TT_UCR_CJK_COMPATIBILITY_IDEOGRAPHS (1L << 29) /* U+F900-U+FAFF */ + /*U+2F800-U+2FA1F*/ + /* Bit 62 Alphabetic Presentation Forms */ +#define TT_UCR_ALPHABETIC_PRESENTATION_FORMS (1L << 30) /* U+FB00-U+FB4F */ + /* Bit 63 Arabic Presentation Forms-A */ +#define TT_UCR_ARABIC_PRESENTATIONS_A (1L << 31) /* U+FB50-U+FDFF */ + /* Bit 64 Combining Half Marks */ +#define TT_UCR_COMBINING_HALF_MARKS (1L << 0) /* U+FE20-U+FE2F */ + /* Bit 65 CJK Compatibility Forms */ +#define TT_UCR_CJK_COMPATIBILITY_FORMS (1L << 1) /* U+FE30-U+FE4F */ + /* Bit 66 Small Form Variants */ +#define TT_UCR_SMALL_FORM_VARIANTS (1L << 2) /* U+FE50-U+FE6F */ + /* Bit 67 Arabic Presentation Forms-B */ +#define TT_UCR_ARABIC_PRESENTATIONS_B (1L << 3) /* U+FE70-U+FEFE */ + /* Bit 68 Halfwidth and Fullwidth Forms */ +#define TT_UCR_HALFWIDTH_FULLWIDTH_FORMS (1L << 4) /* U+FF00-U+FFEF */ + /* Bit 69 Specials */ +#define TT_UCR_SPECIALS (1L << 5) /* U+FFF0-U+FFFD */ + /* Bit 70 Tibetan */ +#define TT_UCR_TIBETAN (1L << 6) /* U+0F00-U+0FFF */ + /* Bit 71 Syriac */ +#define TT_UCR_SYRIAC (1L << 7) /* U+0700-U+074F */ + /* Bit 72 Thaana */ +#define TT_UCR_THAANA (1L << 8) /* U+0780-U+07BF */ + /* Bit 73 Sinhala */ +#define TT_UCR_SINHALA (1L << 9) /* U+0D80-U+0DFF */ + /* Bit 74 Myanmar */ +#define TT_UCR_MYANMAR (1L << 10) /* U+1000-U+109F */ + /* Bit 75 Ethiopic */ +#define TT_UCR_ETHIOPIC (1L << 11) /* U+1200-U+137F */ + /* Bit 76 Cherokee */ +#define TT_UCR_CHEROKEE (1L << 12) /* U+13A0-U+13FF */ + /* Bit 77 Unified Canadian Aboriginal Syllabics */ +#define TT_UCR_CANADIAN_ABORIGINAL_SYLLABICS (1L << 13) /* U+1400-U+167F */ + /* Bit 78 Ogham */ +#define TT_UCR_OGHAM (1L << 14) /* U+1680-U+169F */ + /* Bit 79 Runic */ +#define TT_UCR_RUNIC (1L << 15) /* U+16A0-U+16FF */ + /* Bit 80 Khmer */ +#define TT_UCR_KHMER (1L << 16) /* U+1780-U+17FF */ + /* Bit 81 Mongolian */ +#define TT_UCR_MONGOLIAN (1L << 17) /* U+1800-U+18AF */ + /* Bit 82 Braille Patterns */ +#define TT_UCR_BRAILLE (1L << 18) /* U+2800-U+28FF */ + /* Bit 83 Yi Syllables + */ + /* Yi Radicals */ +#define TT_UCR_YI (1L << 19) /* U+A000-U+A48F */ + /* U+A490-U+A4CF */ + /* Bit 84 Tagalog + */ + /* Hanunoo + */ + /* Buhid + */ + /* Tagbanwa */ +#define TT_UCR_PHILIPPINE (1L << 20) /* U+1700-U+171F */ + /* U+1720-U+173F */ + /* U+1740-U+175F */ + /* U+1760-U+177F */ + /* Bit 85 Old Italic */ +#define TT_UCR_OLD_ITALIC (1L << 21) /*U+10300-U+1032F*/ + /* Bit 86 Gothic */ +#define TT_UCR_GOTHIC (1L << 22) /*U+10330-U+1034F*/ + /* Bit 87 Deseret */ +#define TT_UCR_DESERET (1L << 23) /*U+10400-U+1044F*/ + /* Bit 88 Byzantine Musical Symbols + */ + /* Musical Symbols */ +#define TT_UCR_MUSICAL_SYMBOLS (1L << 24) /*U+1D000-U+1D0FF*/ + /*U+1D100-U+1D1FF*/ + /* Bit 89 Mathematical Alphanumeric Symbols */ +#define TT_UCR_MATH_ALPHANUMERIC_SYMBOLS (1L << 25) /*U+1D400-U+1D7FF*/ + /* Bit 90 Private Use (plane 15) + */ + /* Private Use (plane 16) */ +#define TT_UCR_PRIVATE_USE_SUPPLEMENTARY (1L << 26) /*U+F0000-U+FFFFD*/ + /*U+100000-U+10FFFD*/ + /* Bit 91 Variation Selectors */ +#define TT_UCR_VARIATION_SELECTORS (1L << 27) /* U+FE00-U+FE0F */ + /* Bit 92 Tags */ +#define TT_UCR_TAGS (1L << 28) /*U+E0000-U+E007F*/ + + + /*************************************************************************/ + /* */ + /* Some compilers have a very limited length of identifiers. */ + /* */ +#if defined( __TURBOC__ ) && __TURBOC__ < 0x0410 || defined( __PACIFIC__ ) +#define HAVE_LIMIT_ON_IDENTS +#endif + + +#ifndef HAVE_LIMIT_ON_IDENTS + + + /*************************************************************************/ + /* */ + /* Here some alias #defines in order to be clearer. */ + /* */ + /* These are not always #defined to stay within the 31 character limit */ + /* which some compilers have. */ + /* */ + /* Credits go to Dave Hoo <dhoo@flash.net> for pointing out that modern */ + /* Borland compilers (read: from BC++ 3.1 on) can increase this limit. */ + /* If you get a warning with such a compiler, use the -i40 switch. */ + /* */ +#define TT_UCR_ARABIC_PRESENTATION_FORMS_A \ + TT_UCR_ARABIC_PRESENTATIONS_A +#define TT_UCR_ARABIC_PRESENTATION_FORMS_B \ + TT_UCR_ARABIC_PRESENTATIONS_B + +#define TT_UCR_COMBINING_DIACRITICAL_MARKS \ + TT_UCR_COMBINING_DIACRITICS +#define TT_UCR_COMBINING_DIACRITICAL_MARKS_SYMB \ + TT_UCR_COMBINING_DIACRITICS_SYMB + + +#endif /* !HAVE_LIMIT_ON_IDENTS */ + + +FT_END_HEADER + +#endif /* __TTNAMEID_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/tttables.h b/extra_lib/include/freetype/freetype/tttables.h new file mode 100644 index 0000000..32f78be --- /dev/null +++ b/extra_lib/include/freetype/freetype/tttables.h @@ -0,0 +1,674 @@ +/***************************************************************************/ +/* */ +/* tttables.h */ +/* */ +/* Basic SFNT/TrueType tables definitions and interface */ +/* (specification only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __TTTABLES_H__ +#define __TTTABLES_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* <Section> */ + /* truetype_tables */ + /* */ + /* <Title> */ + /* TrueType Tables */ + /* */ + /* <Abstract> */ + /* TrueType-specific table types and functions. */ + /* */ + /* <Description> */ + /* This section contains the definition of TrueType-specific tables */ + /* as well as some routines used to access and process them. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Header */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType font header table. All */ + /* fields follow the TrueType specification. */ + /* */ + typedef struct TT_Header_ + { + FT_Fixed Table_Version; + FT_Fixed Font_Revision; + + FT_Long CheckSum_Adjust; + FT_Long Magic_Number; + + FT_UShort Flags; + FT_UShort Units_Per_EM; + + FT_Long Created [2]; + FT_Long Modified[2]; + + FT_Short xMin; + FT_Short yMin; + FT_Short xMax; + FT_Short yMax; + + FT_UShort Mac_Style; + FT_UShort Lowest_Rec_PPEM; + + FT_Short Font_Direction; + FT_Short Index_To_Loc_Format; + FT_Short Glyph_Data_Format; + + } TT_Header; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_HoriHeader */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType horizontal header, the `hhea' */ + /* table, as well as the corresponding horizontal metrics table, */ + /* i.e., the `hmtx' table. */ + /* */ + /* <Fields> */ + /* Version :: The table version. */ + /* */ + /* Ascender :: The font's ascender, i.e., the distance */ + /* from the baseline to the top-most of all */ + /* glyph points found in the font. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of the */ + /* glyphs found in the font (maybe ASCII). */ + /* */ + /* You should use the `sTypoAscender' field */ + /* of the OS/2 table instead if you want */ + /* the correct one. */ + /* */ + /* Descender :: The font's descender, i.e., the distance */ + /* from the baseline to the bottom-most of */ + /* all glyph points found in the font. It */ + /* is negative. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of the */ + /* glyphs found in the font (maybe ASCII). */ + /* */ + /* You should use the `sTypoDescender' */ + /* field of the OS/2 table instead if you */ + /* want the correct one. */ + /* */ + /* Line_Gap :: The font's line gap, i.e., the distance */ + /* to add to the ascender and descender to */ + /* get the BTB, i.e., the */ + /* baseline-to-baseline distance for the */ + /* font. */ + /* */ + /* advance_Width_Max :: This field is the maximum of all advance */ + /* widths found in the font. It can be */ + /* used to compute the maximum width of an */ + /* arbitrary string of text. */ + /* */ + /* min_Left_Side_Bearing :: The minimum left side bearing of all */ + /* glyphs within the font. */ + /* */ + /* min_Right_Side_Bearing :: The minimum right side bearing of all */ + /* glyphs within the font. */ + /* */ + /* xMax_Extent :: The maximum horizontal extent (i.e., the */ + /* `width' of a glyph's bounding box) for */ + /* all glyphs in the font. */ + /* */ + /* caret_Slope_Rise :: The rise coefficient of the cursor's */ + /* slope of the cursor (slope=rise/run). */ + /* */ + /* caret_Slope_Run :: The run coefficient of the cursor's */ + /* slope. */ + /* */ + /* Reserved :: 10 reserved bytes. */ + /* */ + /* metric_Data_Format :: Always 0. */ + /* */ + /* number_Of_HMetrics :: Number of HMetrics entries in the `hmtx' */ + /* table -- this value can be smaller than */ + /* the total number of glyphs in the font. */ + /* */ + /* long_metrics :: A pointer into the `hmtx' table. */ + /* */ + /* short_metrics :: A pointer into the `hmtx' table. */ + /* */ + /* <Note> */ + /* IMPORTANT: The TT_HoriHeader and TT_VertHeader structures should */ + /* be identical except for the names of their fields which */ + /* are different. */ + /* */ + /* This ensures that a single function in the `ttload' */ + /* module is able to read both the horizontal and vertical */ + /* headers. */ + /* */ + typedef struct TT_HoriHeader_ + { + FT_Fixed Version; + FT_Short Ascender; + FT_Short Descender; + FT_Short Line_Gap; + + FT_UShort advance_Width_Max; /* advance width maximum */ + + FT_Short min_Left_Side_Bearing; /* minimum left-sb */ + FT_Short min_Right_Side_Bearing; /* minimum right-sb */ + FT_Short xMax_Extent; /* xmax extents */ + FT_Short caret_Slope_Rise; + FT_Short caret_Slope_Run; + FT_Short caret_Offset; + + FT_Short Reserved[4]; + + FT_Short metric_Data_Format; + FT_UShort number_Of_HMetrics; + + /* The following fields are not defined by the TrueType specification */ + /* but they are used to connect the metrics header to the relevant */ + /* `HMTX' table. */ + + void* long_metrics; + void* short_metrics; + + } TT_HoriHeader; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_VertHeader */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType vertical header, the `vhea' */ + /* table, as well as the corresponding vertical metrics table, i.e., */ + /* the `vmtx' table. */ + /* */ + /* <Fields> */ + /* Version :: The table version. */ + /* */ + /* Ascender :: The font's ascender, i.e., the distance */ + /* from the baseline to the top-most of */ + /* all glyph points found in the font. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of */ + /* the glyphs found in the font (maybe */ + /* ASCII). */ + /* */ + /* You should use the `sTypoAscender' */ + /* field of the OS/2 table instead if you */ + /* want the correct one. */ + /* */ + /* Descender :: The font's descender, i.e., the */ + /* distance from the baseline to the */ + /* bottom-most of all glyph points found */ + /* in the font. It is negative. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of */ + /* the glyphs found in the font (maybe */ + /* ASCII). */ + /* */ + /* You should use the `sTypoDescender' */ + /* field of the OS/2 table instead if you */ + /* want the correct one. */ + /* */ + /* Line_Gap :: The font's line gap, i.e., the distance */ + /* to add to the ascender and descender to */ + /* get the BTB, i.e., the */ + /* baseline-to-baseline distance for the */ + /* font. */ + /* */ + /* advance_Height_Max :: This field is the maximum of all */ + /* advance heights found in the font. It */ + /* can be used to compute the maximum */ + /* height of an arbitrary string of text. */ + /* */ + /* min_Top_Side_Bearing :: The minimum top side bearing of all */ + /* glyphs within the font. */ + /* */ + /* min_Bottom_Side_Bearing :: The minimum bottom side bearing of all */ + /* glyphs within the font. */ + /* */ + /* yMax_Extent :: The maximum vertical extent (i.e., the */ + /* `height' of a glyph's bounding box) for */ + /* all glyphs in the font. */ + /* */ + /* caret_Slope_Rise :: The rise coefficient of the cursor's */ + /* slope of the cursor (slope=rise/run). */ + /* */ + /* caret_Slope_Run :: The run coefficient of the cursor's */ + /* slope. */ + /* */ + /* caret_Offset :: The cursor's offset for slanted fonts. */ + /* This value is `reserved' in vmtx */ + /* version 1.0. */ + /* */ + /* Reserved :: 8 reserved bytes. */ + /* */ + /* metric_Data_Format :: Always 0. */ + /* */ + /* number_Of_HMetrics :: Number of VMetrics entries in the */ + /* `vmtx' table -- this value can be */ + /* smaller than the total number of glyphs */ + /* in the font. */ + /* */ + /* long_metrics :: A pointer into the `vmtx' table. */ + /* */ + /* short_metrics :: A pointer into the `vmtx' table. */ + /* */ + /* <Note> */ + /* IMPORTANT: The TT_HoriHeader and TT_VertHeader structures should */ + /* be identical except for the names of their fields which */ + /* are different. */ + /* */ + /* This ensures that a single function in the `ttload' */ + /* module is able to read both the horizontal and vertical */ + /* headers. */ + /* */ + typedef struct TT_VertHeader_ + { + FT_Fixed Version; + FT_Short Ascender; + FT_Short Descender; + FT_Short Line_Gap; + + FT_UShort advance_Height_Max; /* advance height maximum */ + + FT_Short min_Top_Side_Bearing; /* minimum left-sb or top-sb */ + FT_Short min_Bottom_Side_Bearing; /* minimum right-sb or bottom-sb */ + FT_Short yMax_Extent; /* xmax or ymax extents */ + FT_Short caret_Slope_Rise; + FT_Short caret_Slope_Run; + FT_Short caret_Offset; + + FT_Short Reserved[4]; + + FT_Short metric_Data_Format; + FT_UShort number_Of_VMetrics; + + /* The following fields are not defined by the TrueType specification */ + /* but they're used to connect the metrics header to the relevant */ + /* `HMTX' or `VMTX' table. */ + + void* long_metrics; + void* short_metrics; + + } TT_VertHeader; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_OS2 */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType OS/2 table. This is the long */ + /* table version. All fields comply to the TrueType specification. */ + /* */ + /* Note that we now support old Mac fonts which do not include an */ + /* OS/2 table. In this case, the `version' field is always set to */ + /* 0xFFFF. */ + /* */ + typedef struct TT_OS2_ + { + FT_UShort version; /* 0x0001 - more or 0xFFFF */ + FT_Short xAvgCharWidth; + FT_UShort usWeightClass; + FT_UShort usWidthClass; + FT_Short fsType; + FT_Short ySubscriptXSize; + FT_Short ySubscriptYSize; + FT_Short ySubscriptXOffset; + FT_Short ySubscriptYOffset; + FT_Short ySuperscriptXSize; + FT_Short ySuperscriptYSize; + FT_Short ySuperscriptXOffset; + FT_Short ySuperscriptYOffset; + FT_Short yStrikeoutSize; + FT_Short yStrikeoutPosition; + FT_Short sFamilyClass; + + FT_Byte panose[10]; + + FT_ULong ulUnicodeRange1; /* Bits 0-31 */ + FT_ULong ulUnicodeRange2; /* Bits 32-63 */ + FT_ULong ulUnicodeRange3; /* Bits 64-95 */ + FT_ULong ulUnicodeRange4; /* Bits 96-127 */ + + FT_Char achVendID[4]; + + FT_UShort fsSelection; + FT_UShort usFirstCharIndex; + FT_UShort usLastCharIndex; + FT_Short sTypoAscender; + FT_Short sTypoDescender; + FT_Short sTypoLineGap; + FT_UShort usWinAscent; + FT_UShort usWinDescent; + + /* only version 1 tables: */ + + FT_ULong ulCodePageRange1; /* Bits 0-31 */ + FT_ULong ulCodePageRange2; /* Bits 32-63 */ + + /* only version 2 tables: */ + + FT_Short sxHeight; + FT_Short sCapHeight; + FT_UShort usDefaultChar; + FT_UShort usBreakChar; + FT_UShort usMaxContext; + + } TT_OS2; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Postscript */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType Postscript table. All fields */ + /* comply to the TrueType table. This structure does not reference */ + /* the Postscript glyph names, which can be nevertheless accessed */ + /* with the `ttpost' module. */ + /* */ + typedef struct TT_Postscript_ + { + FT_Fixed FormatType; + FT_Fixed italicAngle; + FT_Short underlinePosition; + FT_Short underlineThickness; + FT_ULong isFixedPitch; + FT_ULong minMemType42; + FT_ULong maxMemType42; + FT_ULong minMemType1; + FT_ULong maxMemType1; + + /* Glyph names follow in the file, but we don't */ + /* load them by default. See the ttpost.c file. */ + + } TT_Postscript; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_PCLT */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType PCLT table. All fields */ + /* comply to the TrueType table. */ + /* */ + typedef struct TT_PCLT_ + { + FT_Fixed Version; + FT_ULong FontNumber; + FT_UShort Pitch; + FT_UShort xHeight; + FT_UShort Style; + FT_UShort TypeFamily; + FT_UShort CapHeight; + FT_UShort SymbolSet; + FT_Char TypeFace[16]; + FT_Char CharacterComplement[8]; + FT_Char FileName[6]; + FT_Char StrokeWeight; + FT_Char WidthType; + FT_Byte SerifStyle; + FT_Byte Reserved; + + } TT_PCLT; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_MaxProfile */ + /* */ + /* <Description> */ + /* The maximum profile is a table containing many max values which */ + /* can be used to pre-allocate arrays. This ensures that no memory */ + /* allocation occurs during a glyph load. */ + /* */ + /* <Fields> */ + /* version :: The version number. */ + /* */ + /* numGlyphs :: The number of glyphs in this TrueType */ + /* font. */ + /* */ + /* maxPoints :: The maximum number of points in a */ + /* non-composite TrueType glyph. See also */ + /* the structure element */ + /* `maxCompositePoints'. */ + /* */ + /* maxContours :: The maximum number of contours in a */ + /* non-composite TrueType glyph. See also */ + /* the structure element */ + /* `maxCompositeContours'. */ + /* */ + /* maxCompositePoints :: The maximum number of points in a */ + /* composite TrueType glyph. See also the */ + /* structure element `maxPoints'. */ + /* */ + /* maxCompositeContours :: The maximum number of contours in a */ + /* composite TrueType glyph. See also the */ + /* structure element `maxContours'. */ + /* */ + /* maxZones :: The maximum number of zones used for */ + /* glyph hinting. */ + /* */ + /* maxTwilightPoints :: The maximum number of points in the */ + /* twilight zone used for glyph hinting. */ + /* */ + /* maxStorage :: The maximum number of elements in the */ + /* storage area used for glyph hinting. */ + /* */ + /* maxFunctionDefs :: The maximum number of function */ + /* definitions in the TrueType bytecode for */ + /* this font. */ + /* */ + /* maxInstructionDefs :: The maximum number of instruction */ + /* definitions in the TrueType bytecode for */ + /* this font. */ + /* */ + /* maxStackElements :: The maximum number of stack elements used */ + /* during bytecode interpretation. */ + /* */ + /* maxSizeOfInstructions :: The maximum number of TrueType opcodes */ + /* used for glyph hinting. */ + /* */ + /* maxComponentElements :: An obscure value related to composite */ + /* glyphs definitions. */ + /* */ + /* maxComponentDepth :: An obscure value related to composite */ + /* glyphs definitions. Probably the maximum */ + /* number of simple glyphs in a composite. */ + /* */ + /* <Note> */ + /* This structure is only used during font loading. */ + /* */ + typedef struct TT_MaxProfile_ + { + FT_Fixed version; + FT_UShort numGlyphs; + FT_UShort maxPoints; + FT_UShort maxContours; + FT_UShort maxCompositePoints; + FT_UShort maxCompositeContours; + FT_UShort maxZones; + FT_UShort maxTwilightPoints; + FT_UShort maxStorage; + FT_UShort maxFunctionDefs; + FT_UShort maxInstructionDefs; + FT_UShort maxStackElements; + FT_UShort maxSizeOfInstructions; + FT_UShort maxComponentElements; + FT_UShort maxComponentDepth; + + } TT_MaxProfile; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Sfnt_Tag */ + /* */ + /* <Description> */ + /* An enumeration used to specify the index of an SFNT table. */ + /* Used in the @FT_Get_Sfnt_Table API function. */ + /* */ + typedef enum + { + ft_sfnt_head = 0, + ft_sfnt_maxp = 1, + ft_sfnt_os2 = 2, + ft_sfnt_hhea = 3, + ft_sfnt_vhea = 4, + ft_sfnt_post = 5, + ft_sfnt_pclt = 6, + + sfnt_max /* internal end mark */ + + } FT_Sfnt_Tag; + + /* */ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Sfnt_Table */ + /* */ + /* <Description> */ + /* Returns a pointer to a given SFNT table within a face. */ + /* */ + /* <Input> */ + /* face :: A handle to the source. */ + /* */ + /* tag :: The index of the SFNT table. */ + /* */ + /* <Return> */ + /* A type-less pointer to the table. This will be 0 in case of */ + /* error, or if the corresponding table was not found *OR* loaded */ + /* from the file. */ + /* */ + /* <Note> */ + /* The table is owned by the face object and disappears with it. */ + /* */ + /* This function is only useful to access SFNT tables that are loaded */ + /* by the sfnt/truetype/opentype drivers. See @FT_Sfnt_Tag for a */ + /* list. */ + /* */ + FT_EXPORT( void* ) + FT_Get_Sfnt_Table( FT_Face face, + FT_Sfnt_Tag tag ); + + + /************************************************************************** + * + * <Function> + * FT_Load_Sfnt_Table + * + * <Description> + * Loads any font table into client memory. + * + * <Input> + * face :: A handle to the source face. + * + * tag :: The 4-byte tag of the table to load. Use the value 0 if + * you want to access the whole font file. Otherwise, you can + * use one of the definitions found in the @FT_TRUETYPE_TAGS_H + * file, or forge a new one with @FT_MAKE_TAG. + * + * offset :: The starting offset in the table (or file if tag == 0). + * + * <Output> + * buffer :: The target buffer address. The client must ensure that + * the memory array is big enough to hold the data. + * + * <InOut> + * length :: If the `length' parameter is NULL, then try to load the whole + * table. Return an error code if it fails. + * + * Else, if `*length' is 0, exit immediately while returning + * the table's (or file) full size in it. + * + * Else the number of bytes to read from the table or file, + * from the starting offset. + * + * <Return> + * FreeType error code. 0 means success. + * + * <Note> + * If you need to determine the table's length you should first call this + * function with `*length' set to 0, as in the following example: + * + * { + * FT_ULong length = 0; + * + * + * error = FT_Load_Sfnt_Table( face, tag, 0, NULL, &length ); + * if ( error ) { ... table does not exist ... } + * + * buffer = malloc( length ); + * if ( buffer == NULL ) { ... not enough memory ... } + * + * error = FT_Load_Sfnt_Table( face,tag, 0, buffer, &length ); + * if ( error ) { ... could not load table ... } + * } + */ + FT_EXPORT( FT_Error ) + FT_Load_Sfnt_Table( FT_Face face, + FT_ULong tag, + FT_Long offset, + FT_Byte* buffer, + FT_ULong* length ); + + /* */ + + +FT_END_HEADER + +#endif /* __TTTABLES_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/freetype/tttags.h b/extra_lib/include/freetype/freetype/tttags.h new file mode 100644 index 0000000..ac64758 --- /dev/null +++ b/extra_lib/include/freetype/freetype/tttags.h @@ -0,0 +1,80 @@ +/***************************************************************************/ +/* */ +/* tttags.h */ +/* */ +/* Tags for TrueType tables (specification only). */ +/* */ +/* Copyright 1996-2001 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __TTAGS_H__ +#define __TTAGS_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + +#define TTAG_cmap FT_MAKE_TAG( 'c', 'm', 'a', 'p' ) +#define TTAG_cvt FT_MAKE_TAG( 'c', 'v', 't', ' ' ) +#define TTAG_CFF FT_MAKE_TAG( 'C', 'F', 'F', ' ' ) +#define TTAG_DSIG FT_MAKE_TAG( 'D', 'S', 'I', 'G' ) +#define TTAG_bhed FT_MAKE_TAG( 'b', 'h', 'e', 'd' ) +#define TTAG_bdat FT_MAKE_TAG( 'b', 'd', 'a', 't' ) +#define TTAG_bloc FT_MAKE_TAG( 'b', 'l', 'o', 'c' ) +#define TTAG_EBDT FT_MAKE_TAG( 'E', 'B', 'D', 'T' ) +#define TTAG_EBLC FT_MAKE_TAG( 'E', 'B', 'L', 'C' ) +#define TTAG_EBSC FT_MAKE_TAG( 'E', 'B', 'S', 'C' ) +#define TTAG_fpgm FT_MAKE_TAG( 'f', 'p', 'g', 'm' ) +#define TTAG_fvar FT_MAKE_TAG( 'f', 'v', 'a', 'r' ) +#define TTAG_gasp FT_MAKE_TAG( 'g', 'a', 's', 'p' ) +#define TTAG_glyf FT_MAKE_TAG( 'g', 'l', 'y', 'f' ) +#define TTAG_GSUB FT_MAKE_TAG( 'G', 'S', 'U', 'B' ) +#define TTAG_hdmx FT_MAKE_TAG( 'h', 'd', 'm', 'x' ) +#define TTAG_head FT_MAKE_TAG( 'h', 'e', 'a', 'd' ) +#define TTAG_hhea FT_MAKE_TAG( 'h', 'h', 'e', 'a' ) +#define TTAG_hmtx FT_MAKE_TAG( 'h', 'm', 't', 'x' ) +#define TTAG_kern FT_MAKE_TAG( 'k', 'e', 'r', 'n' ) +#define TTAG_loca FT_MAKE_TAG( 'l', 'o', 'c', 'a' ) +#define TTAG_LTSH FT_MAKE_TAG( 'L', 'T', 'S', 'H' ) +#define TTAG_maxp FT_MAKE_TAG( 'm', 'a', 'x', 'p' ) +#define TTAG_MMSD FT_MAKE_TAG( 'M', 'M', 'S', 'D' ) +#define TTAG_MMFX FT_MAKE_TAG( 'M', 'M', 'F', 'X' ) +#define TTAG_name FT_MAKE_TAG( 'n', 'a', 'm', 'e' ) +#define TTAG_OS2 FT_MAKE_TAG( 'O', 'S', '/', '2' ) +#define TTAG_OTTO FT_MAKE_TAG( 'O', 'T', 'T', 'O' ) +#define TTAG_PCLT FT_MAKE_TAG( 'P', 'C', 'L', 'T' ) +#define TTAG_post FT_MAKE_TAG( 'p', 'o', 's', 't' ) +#define TTAG_prep FT_MAKE_TAG( 'p', 'r', 'e', 'p' ) +#define TTAG_true FT_MAKE_TAG( 't', 'r', 'u', 'e' ) +#define TTAG_ttc FT_MAKE_TAG( 't', 't', 'c', ' ' ) +#define TTAG_ttcf FT_MAKE_TAG( 't', 't', 'c', 'f' ) +#define TTAG_VDMX FT_MAKE_TAG( 'V', 'D', 'M', 'X' ) +#define TTAG_vhea FT_MAKE_TAG( 'v', 'h', 'e', 'a' ) +#define TTAG_vmtx FT_MAKE_TAG( 'v', 'm', 't', 'x' ) + + +FT_END_HEADER + +#endif /* __TTAGS_H__ */ + + +/* END */ diff --git a/extra_lib/include/freetype/ft2build.h b/extra_lib/include/freetype/ft2build.h new file mode 100644 index 0000000..2ecfdea --- /dev/null +++ b/extra_lib/include/freetype/ft2build.h @@ -0,0 +1,39 @@ +/***************************************************************************/ +/* */ +/* ft2build.h */ +/* */ +/* FreeType 2 build and setup macros. */ +/* (Generic version) */ +/* */ +/* Copyright 1996-2001 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file corresponds to the default "ft2build.h" file for */ + /* FreeType 2. It uses the "freetype" include root. */ + /* */ + /* Note that specific platforms might use a different configuration. */ + /* See builds/unix/ft2unix.h for an example. */ + /* */ + /*************************************************************************/ + + +#ifndef __FT2_BUILD_GENERIC_H__ +#define __FT2_BUILD_GENERIC_H__ + +#include <freetype/config/ftheader.h> + +#endif /* __FT2_BUILD_GENERIC_H__ */ + + +/* END */ diff --git a/extra_lib/include/jpeg/jconfig.h b/extra_lib/include/jpeg/jconfig.h new file mode 100644 index 0000000..7143622 --- /dev/null +++ b/extra_lib/include/jpeg/jconfig.h @@ -0,0 +1,60 @@ +/*jconfig.h for Microsoft Visual C++ and MinGW32 platform*/ +/* jconfig.cfg --- source file edited by configure script */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +#ifdef __MINGW32__ +#undef void +#undef const +#endif +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS +#undef NEED_SHORT_EXTERNAL_NAMES +/* Define this if you get warnings about undefined structures. */ +#undef INCOMPLETE_TYPES_BROKEN + +#ifndef __MINGW32__ + +/* Define "boolean" as unsigned char, not int, per Windows custom */ +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ +#endif + + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#ifdef __MINGW32__ +#define INLINE __inline__ +/* These are for configuring the JPEG memory manager. */ +#undef DEFAULT_MAX_MEM +#undef NO_MKTEMP +#endif + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#undef TWO_FILE_COMMANDLINE +#undef NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. */ +#undef PROGRESS_REPORT + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/extra_lib/include/jpeg/jmorecfg.h b/extra_lib/include/jpeg/jmorecfg.h new file mode 100644 index 0000000..c7ed28a --- /dev/null +++ b/extra_lib/include/jpeg/jmorecfg.h @@ -0,0 +1,368 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef INT32 +//typedef long INT32; +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#ifdef FAR +#undef FAR +#endif +#define FAR +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/extra_lib/include/jpeg/jpeglib.h b/extra_lib/include/jpeg/jpeglib.h new file mode 100644 index 0000000..d1be8dd --- /dev/null +++ b/extra_lib/include/jpeg/jpeglib.h @@ -0,0 +1,1096 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ + +#define JPEG_LIB_VERSION 62 /* Version 6b */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values of 1,2,4,8 are likely to be supported. Note that different + * components may receive different IDCT scalings. + */ + int DCT_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.doc concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#endif /* JPEGLIB_H */ diff --git a/extra_lib/include/js/jsapi.h b/extra_lib/include/js/jsapi.h new file mode 100644 index 0000000..464f19f --- /dev/null +++ b/extra_lib/include/js/jsapi.h @@ -0,0 +1,2220 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsapi_h___ +#define jsapi_h___ +/* + * JavaScript API. + */ +#include <stddef.h> +#include <stdio.h> +#include "jspubtd.h" + +JS_BEGIN_EXTERN_C + +/* + * Type tags stored in the low bits of a jsval. + */ +#define JSVAL_OBJECT 0x0 /* untagged reference to object */ +#define JSVAL_INT 0x1 /* tagged 31-bit integer value */ +#define JSVAL_DOUBLE 0x2 /* tagged reference to double */ +#define JSVAL_STRING 0x4 /* tagged reference to string */ +#define JSVAL_BOOLEAN 0x6 /* tagged boolean value */ + +/* Type tag bitfield length and derived macros. */ +#define JSVAL_TAGBITS 3 +#define JSVAL_TAGMASK JS_BITMASK(JSVAL_TAGBITS) +#define JSVAL_TAG(v) ((v) & JSVAL_TAGMASK) +#define JSVAL_SETTAG(v,t) ((v) | (t)) +#define JSVAL_CLRTAG(v) ((v) & ~(jsval)JSVAL_TAGMASK) +#define JSVAL_ALIGN JS_BIT(JSVAL_TAGBITS) + +/* Predicates for type testing. */ +#define JSVAL_IS_OBJECT(v) (JSVAL_TAG(v) == JSVAL_OBJECT) +#define JSVAL_IS_NUMBER(v) (JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v)) +#define JSVAL_IS_INT(v) (((v) & JSVAL_INT) && (v) != JSVAL_VOID) +#define JSVAL_IS_DOUBLE(v) (JSVAL_TAG(v) == JSVAL_DOUBLE) +#define JSVAL_IS_STRING(v) (JSVAL_TAG(v) == JSVAL_STRING) +#define JSVAL_IS_BOOLEAN(v) (JSVAL_TAG(v) == JSVAL_BOOLEAN) +#define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL) +#define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID) +#define JSVAL_IS_PRIMITIVE(v) (!JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v)) + +/* Objects, strings, and doubles are GC'ed. */ +#define JSVAL_IS_GCTHING(v) (!((v) & JSVAL_INT) && !JSVAL_IS_BOOLEAN(v)) +#define JSVAL_TO_GCTHING(v) ((void *)JSVAL_CLRTAG(v)) +#define JSVAL_TO_OBJECT(v) ((JSObject *)JSVAL_TO_GCTHING(v)) +#define JSVAL_TO_DOUBLE(v) ((jsdouble *)JSVAL_TO_GCTHING(v)) +#define JSVAL_TO_STRING(v) ((JSString *)JSVAL_TO_GCTHING(v)) +#define OBJECT_TO_JSVAL(obj) ((jsval)(obj)) +#define DOUBLE_TO_JSVAL(dp) JSVAL_SETTAG((jsval)(dp), JSVAL_DOUBLE) +#define STRING_TO_JSVAL(str) JSVAL_SETTAG((jsval)(str), JSVAL_STRING) + +/* Lock and unlock the GC thing held by a jsval. */ +#define JSVAL_LOCK(cx,v) (JSVAL_IS_GCTHING(v) \ + ? JS_LockGCThing(cx, JSVAL_TO_GCTHING(v)) \ + : JS_TRUE) +#define JSVAL_UNLOCK(cx,v) (JSVAL_IS_GCTHING(v) \ + ? JS_UnlockGCThing(cx, JSVAL_TO_GCTHING(v)) \ + : JS_TRUE) + +/* Domain limits for the jsval int type. */ +#define JSVAL_INT_BITS 31 +#define JSVAL_INT_POW2(n) ((jsval)1 << (n)) +#define JSVAL_INT_MIN ((jsval)1 - JSVAL_INT_POW2(30)) +#define JSVAL_INT_MAX (JSVAL_INT_POW2(30) - 1) +#define INT_FITS_IN_JSVAL(i) ((jsuint)((i)+JSVAL_INT_MAX) <= 2*JSVAL_INT_MAX) +#define JSVAL_TO_INT(v) ((jsint)(v) >> 1) +#define INT_TO_JSVAL(i) (((jsval)(i) << 1) | JSVAL_INT) + +/* Convert between boolean and jsval. */ +#define JSVAL_TO_BOOLEAN(v) ((JSBool)((v) >> JSVAL_TAGBITS)) +#define BOOLEAN_TO_JSVAL(b) JSVAL_SETTAG((jsval)(b) << JSVAL_TAGBITS, \ + JSVAL_BOOLEAN) + +/* A private data pointer (2-byte-aligned) can be stored as an int jsval. */ +#define JSVAL_TO_PRIVATE(v) ((void *)((v) & ~JSVAL_INT)) +#define PRIVATE_TO_JSVAL(p) ((jsval)(p) | JSVAL_INT) + +/* Property attributes, set in JSPropertySpec and passed to API functions. */ +#define JSPROP_ENUMERATE 0x01 /* property is visible to for/in loop */ +#define JSPROP_READONLY 0x02 /* not settable: assignment is no-op */ +#define JSPROP_PERMANENT 0x04 /* property cannot be deleted */ +#define JSPROP_EXPORTED 0x08 /* property is exported from object */ +#define JSPROP_GETTER 0x10 /* property holds getter function */ +#define JSPROP_SETTER 0x20 /* property holds setter function */ +#define JSPROP_SHARED 0x40 /* don't allocate a value slot for this + property; don't copy the property on + set of the same-named property in an + object that delegates to a prototype + containing this property */ +#define JSPROP_INDEX 0x80 /* name is actually (jsint) index */ + +/* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */ +#define JSFUN_LAMBDA 0x08 /* expressed, not declared, function */ +#define JSFUN_GETTER JSPROP_GETTER +#define JSFUN_SETTER JSPROP_SETTER +#define JSFUN_BOUND_METHOD 0x40 /* bind this to fun->object's parent */ +#define JSFUN_HEAVYWEIGHT 0x80 /* activation requires a Call object */ + +#define JSFUN_DISJOINT_FLAGS(f) ((f) & 0x0f) +#define JSFUN_GSFLAGS(f) ((f) & (JSFUN_GETTER | JSFUN_SETTER)) + +#ifdef MOZILLA_1_8_BRANCH + +/* + * Squeeze three more bits into existing 8-bit flags by taking advantage of + * the invalid combination (JSFUN_GETTER | JSFUN_SETTER). + */ +#define JSFUN_GETTER_TEST(f) (JSFUN_GSFLAGS(f) == JSFUN_GETTER) +#define JSFUN_SETTER_TEST(f) (JSFUN_GSFLAGS(f) == JSFUN_SETTER) +#define JSFUN_FLAGS_TEST(f,t) (JSFUN_GSFLAGS(~(f)) ? (f) & (t) : 0) +#define JSFUN_BOUND_METHOD_TEST(f) JSFUN_FLAGS_TEST(f, JSFUN_BOUND_METHOD) +#define JSFUN_HEAVYWEIGHT_TEST(f) JSFUN_FLAGS_TEST(f, JSFUN_HEAVYWEIGHT) + +#define JSFUN_GSFLAG2ATTR(f) (JSFUN_GETTER_TEST(f) ? JSPROP_GETTER : \ + JSFUN_SETTER_TEST(f) ? JSPROP_SETTER : 0) + +#define JSFUN_THISP_FLAGS(f) (JSFUN_GSFLAGS(~(f)) ? 0 : \ + (f) & JSFUN_THISP_PRIMITIVE) +#define JSFUN_THISP_TEST(f,t) ((f) == (t) || (f) == JSFUN_THISP_PRIMITIVE) + +#define JSFUN_THISP_STRING 0x30 /* |this| may be a primitive string */ +#define JSFUN_THISP_NUMBER 0x70 /* |this| may be a primitive number */ +#define JSFUN_THISP_BOOLEAN 0xb0 /* |this| may be a primitive boolean */ +#define JSFUN_THISP_PRIMITIVE 0xf0 /* |this| may be any primitive value */ + +#define JSFUN_FLAGS_MASK 0xf8 /* overlay JSFUN_* attributes */ + +#else + +#define JSFUN_GETTER_TEST(f) ((f) & JSFUN_GETTER) +#define JSFUN_SETTER_TEST(f) ((f) & JSFUN_SETTER) +#define JSFUN_BOUND_METHOD_TEST(f) ((f) & JSFUN_BOUND_METHOD) +#define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT) + +#define JSFUN_GSFLAG2ATTR(f) JSFUN_GSFLAGS(f) + +#define JSFUN_THISP_FLAGS(f) (f) +#define JSFUN_THISP_TEST(f,t) ((f) & t) + +#define JSFUN_THISP_STRING 0x0100 /* |this| may be a primitive string */ +#define JSFUN_THISP_NUMBER 0x0200 /* |this| may be a primitive number */ +#define JSFUN_THISP_BOOLEAN 0x0400 /* |this| may be a primitive boolean */ +#define JSFUN_THISP_PRIMITIVE 0x0700 /* |this| may be any primitive value */ + +#define JSFUN_FLAGS_MASK 0x07f8 /* overlay JSFUN_* attributes -- + note that bit #15 is used internally + to flag interpreted functions */ + +#endif + +/* + * Re-use JSFUN_LAMBDA, which applies only to scripted functions, for use in + * JSFunctionSpec arrays that specify generic native prototype methods, i.e., + * methods of a class prototype that are exposed as static methods taking an + * extra leading argument: the generic |this| parameter. + * + * If you set this flag in a JSFunctionSpec struct's flags initializer, then + * that struct must live at least as long as the native static method object + * created due to this flag by JS_DefineFunctions or JS_InitClass. Typically + * JSFunctionSpec structs are allocated in static arrays. + */ +#define JSFUN_GENERIC_NATIVE JSFUN_LAMBDA + +/* + * Well-known JS values. The extern'd variables are initialized when the + * first JSContext is created by JS_NewContext (see below). + */ +#define JSVAL_VOID INT_TO_JSVAL(0 - JSVAL_INT_POW2(30)) +#define JSVAL_NULL OBJECT_TO_JSVAL(0) +#define JSVAL_ZERO INT_TO_JSVAL(0) +#define JSVAL_ONE INT_TO_JSVAL(1) +#define JSVAL_FALSE BOOLEAN_TO_JSVAL(JS_FALSE) +#define JSVAL_TRUE BOOLEAN_TO_JSVAL(JS_TRUE) + +/* + * Microseconds since the epoch, midnight, January 1, 1970 UTC. See the + * comment in jstypes.h regarding safe int64 usage. + */ +extern JS_PUBLIC_API(int64) +JS_Now(); + +/* Don't want to export data, so provide accessors for non-inline jsvals. */ +extern JS_PUBLIC_API(jsval) +JS_GetNaNValue(JSContext *cx); + +extern JS_PUBLIC_API(jsval) +JS_GetNegativeInfinityValue(JSContext *cx); + +extern JS_PUBLIC_API(jsval) +JS_GetPositiveInfinityValue(JSContext *cx); + +extern JS_PUBLIC_API(jsval) +JS_GetEmptyStringValue(JSContext *cx); + +/* + * Format is a string of the following characters (spaces are insignificant), + * specifying the tabulated type conversions: + * + * b JSBool Boolean + * c uint16/jschar ECMA uint16, Unicode char + * i int32 ECMA int32 + * u uint32 ECMA uint32 + * j int32 Rounded int32 (coordinate) + * d jsdouble IEEE double + * I jsdouble Integral IEEE double + * s char * C string + * S JSString * Unicode string, accessed by a JSString pointer + * W jschar * Unicode character vector, 0-terminated (W for wide) + * o JSObject * Object reference + * f JSFunction * Function private + * v jsval Argument value (no conversion) + * * N/A Skip this argument (no vararg) + * / N/A End of required arguments + * + * The variable argument list after format must consist of &b, &c, &s, e.g., + * where those variables have the types given above. For the pointer types + * char *, JSString *, and JSObject *, the pointed-at memory returned belongs + * to the JS runtime, not to the calling native code. The runtime promises + * to keep this memory valid so long as argv refers to allocated stack space + * (so long as the native function is active). + * + * Fewer arguments than format specifies may be passed only if there is a / + * in format after the last required argument specifier and argc is at least + * the number of required arguments. More arguments than format specifies + * may be passed without error; it is up to the caller to deal with trailing + * unconverted arguments. + */ +extern JS_PUBLIC_API(JSBool) +JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, + ...); + +#ifdef va_start +extern JS_PUBLIC_API(JSBool) +JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, + const char *format, va_list ap); +#endif + +/* + * Inverse of JS_ConvertArguments: scan format and convert trailing arguments + * into jsvals, GC-rooted if necessary by the JS stack. Return null on error, + * and a pointer to the new argument vector on success. Also return a stack + * mark on success via *markp, in which case the caller must eventually clean + * up by calling JS_PopArguments. + * + * Note that the number of actual arguments supplied is specified exclusively + * by format, so there is no argc parameter. + */ +extern JS_PUBLIC_API(jsval *) +JS_PushArguments(JSContext *cx, void **markp, const char *format, ...); + +#ifdef va_start +extern JS_PUBLIC_API(jsval *) +JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap); +#endif + +extern JS_PUBLIC_API(void) +JS_PopArguments(JSContext *cx, void *mark); + +#ifdef JS_ARGUMENT_FORMATTER_DEFINED + +/* + * Add and remove a format string handler for JS_{Convert,Push}Arguments{,VA}. + * The handler function has this signature (see jspubtd.h): + * + * JSBool MyArgumentFormatter(JSContext *cx, const char *format, + * JSBool fromJS, jsval **vpp, va_list *app); + * + * It should return true on success, and return false after reporting an error + * or detecting an already-reported error. + * + * For a given format string, for example "AA", the formatter is called from + * JS_ConvertArgumentsVA like so: + * + * formatter(cx, "AA...", JS_TRUE, &sp, &ap); + * + * sp points into the arguments array on the JS stack, while ap points into + * the stdarg.h va_list on the C stack. The JS_TRUE passed for fromJS tells + * the formatter to convert zero or more jsvals at sp to zero or more C values + * accessed via pointers-to-values at ap, updating both sp (via *vpp) and ap + * (via *app) to point past the converted arguments and their result pointers + * on the C stack. + * + * When called from JS_PushArgumentsVA, the formatter is invoked thus: + * + * formatter(cx, "AA...", JS_FALSE, &sp, &ap); + * + * where JS_FALSE for fromJS means to wrap the C values at ap according to the + * format specifier and store them at sp, updating ap and sp appropriately. + * + * The "..." after "AA" is the rest of the format string that was passed into + * JS_{Convert,Push}Arguments{,VA}. The actual format trailing substring used + * in each Convert or PushArguments call is passed to the formatter, so that + * one such function may implement several formats, in order to share code. + * + * Remove just forgets about any handler associated with format. Add does not + * copy format, it points at the string storage allocated by the caller, which + * is typically a string constant. If format is in dynamic storage, it is up + * to the caller to keep the string alive until Remove is called. + */ +extern JS_PUBLIC_API(JSBool) +JS_AddArgumentFormatter(JSContext *cx, const char *format, + JSArgumentFormatter formatter); + +extern JS_PUBLIC_API(void) +JS_RemoveArgumentFormatter(JSContext *cx, const char *format); + +#endif /* JS_ARGUMENT_FORMATTER_DEFINED */ + +extern JS_PUBLIC_API(JSBool) +JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp); + +extern JS_PUBLIC_API(JSFunction *) +JS_ValueToFunction(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(JSFunction *) +JS_ValueToConstructor(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(JSString *) +JS_ValueToString(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(JSBool) +JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); + +/* + * Convert a value to a number, then to an int32, according to the ECMA rules + * for ToInt32. + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); + +/* + * Convert a value to a number, then to a uint32, according to the ECMA rules + * for ToUint32. + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); + +/* + * Convert a value to a number, then to an int32 if it fits by rounding to + * nearest; but failing with an error report if the double is out of range + * or unordered. + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip); + +/* + * ECMA ToUint16, for mapping a jsval to a Unicode point. + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); + +extern JS_PUBLIC_API(JSBool) +JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); + +extern JS_PUBLIC_API(JSType) +JS_TypeOfValue(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(const char *) +JS_GetTypeName(JSContext *cx, JSType type); + +/************************************************************************/ + +/* + * Initialization, locking, contexts, and memory allocation. + */ +#define JS_NewRuntime JS_Init +#define JS_DestroyRuntime JS_Finish +#define JS_LockRuntime JS_Lock +#define JS_UnlockRuntime JS_Unlock + +extern JS_PUBLIC_API(JSRuntime *) +JS_NewRuntime(uint32 maxbytes); + +extern JS_PUBLIC_API(void) +JS_DestroyRuntime(JSRuntime *rt); + +extern JS_PUBLIC_API(void) +JS_ShutDown(void); + +JS_PUBLIC_API(void *) +JS_GetRuntimePrivate(JSRuntime *rt); + +JS_PUBLIC_API(void) +JS_SetRuntimePrivate(JSRuntime *rt, void *data); + +#ifdef JS_THREADSAFE + +extern JS_PUBLIC_API(void) +JS_BeginRequest(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_EndRequest(JSContext *cx); + +/* Yield to pending GC operations, regardless of request depth */ +extern JS_PUBLIC_API(void) +JS_YieldRequest(JSContext *cx); + +extern JS_PUBLIC_API(jsrefcount) +JS_SuspendRequest(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth); + +#ifdef __cplusplus +JS_END_EXTERN_C + +class JSAutoRequest { + public: + JSAutoRequest(JSContext *cx) : mContext(cx), mSaveDepth(0) { + JS_BeginRequest(mContext); + } + ~JSAutoRequest() { + JS_EndRequest(mContext); + } + + void suspend() { + mSaveDepth = JS_SuspendRequest(mContext); + } + void resume() { + JS_ResumeRequest(mContext, mSaveDepth); + } + + protected: + JSContext *mContext; + jsrefcount mSaveDepth; + +#if 0 + private: + static void *operator new(size_t) CPP_THROW_NEW { return 0; }; + static void operator delete(void *, size_t) { }; +#endif +}; + +JS_BEGIN_EXTERN_C +#endif + +#endif /* JS_THREADSAFE */ + +extern JS_PUBLIC_API(void) +JS_Lock(JSRuntime *rt); + +extern JS_PUBLIC_API(void) +JS_Unlock(JSRuntime *rt); + +extern JS_PUBLIC_API(JSContextCallback) +JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback); + +extern JS_PUBLIC_API(JSContext *) +JS_NewContext(JSRuntime *rt, size_t stackChunkSize); + +extern JS_PUBLIC_API(void) +JS_DestroyContext(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_DestroyContextNoGC(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_DestroyContextMaybeGC(JSContext *cx); + +extern JS_PUBLIC_API(void *) +JS_GetContextPrivate(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_SetContextPrivate(JSContext *cx, void *data); + +extern JS_PUBLIC_API(JSRuntime *) +JS_GetRuntime(JSContext *cx); + +extern JS_PUBLIC_API(JSContext *) +JS_ContextIterator(JSRuntime *rt, JSContext **iterp); + +extern JS_PUBLIC_API(JSVersion) +JS_GetVersion(JSContext *cx); + +extern JS_PUBLIC_API(JSVersion) +JS_SetVersion(JSContext *cx, JSVersion version); + +extern JS_PUBLIC_API(const char *) +JS_VersionToString(JSVersion version); + +extern JS_PUBLIC_API(JSVersion) +JS_StringToVersion(const char *string); + +/* + * JS options are orthogonal to version, and may be freely composed with one + * another as well as with version. + * + * JSOPTION_VAROBJFIX is recommended -- see the comments associated with the + * prototypes for JS_ExecuteScript, JS_EvaluateScript, etc. + */ +#define JSOPTION_STRICT JS_BIT(0) /* warn on dubious practice */ +#define JSOPTION_WERROR JS_BIT(1) /* convert warning to error */ +#define JSOPTION_VAROBJFIX JS_BIT(2) /* make JS_EvaluateScript use + the last object on its 'obj' + param's scope chain as the + ECMA 'variables object' */ +#define JSOPTION_PRIVATE_IS_NSISUPPORTS \ + JS_BIT(3) /* context private data points + to an nsISupports subclass */ +#define JSOPTION_COMPILE_N_GO JS_BIT(4) /* caller of JS_Compile*Script + promises to execute compiled + script once only; enables + compile-time scope chain + resolution of consts. */ +#define JSOPTION_ATLINE JS_BIT(5) /* //@line number ["filename"] + option supported for the + XUL preprocessor and kindred + beasts. */ +#define JSOPTION_XML JS_BIT(6) /* EMCAScript for XML support: + parse <!-- --> as a token, + not backward compatible with + the comment-hiding hack used + in HTML script tags. */ +#define JSOPTION_NATIVE_BRANCH_CALLBACK \ + JS_BIT(7) /* the branch callback set by + JS_SetBranchCallback may be + called with a null script + parameter, by native code + that loops intensively */ +#define JSOPTION_DONT_REPORT_UNCAUGHT \ + JS_BIT(8) /* When returning from the + outermost API call, prevent + uncaught exceptions from + being converted to error + reports */ + +extern JS_PUBLIC_API(uint32) +JS_GetOptions(JSContext *cx); + +extern JS_PUBLIC_API(uint32) +JS_SetOptions(JSContext *cx, uint32 options); + +extern JS_PUBLIC_API(uint32) +JS_ToggleOptions(JSContext *cx, uint32 options); + +extern JS_PUBLIC_API(const char *) +JS_GetImplementationVersion(void); + +extern JS_PUBLIC_API(JSObject *) +JS_GetGlobalObject(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_SetGlobalObject(JSContext *cx, JSObject *obj); + +/* + * Initialize standard JS class constructors, prototypes, and any top-level + * functions and constants associated with the standard classes (e.g. isNaN + * for Number). + * + * NB: This sets cx's global object to obj if it was null. + */ +extern JS_PUBLIC_API(JSBool) +JS_InitStandardClasses(JSContext *cx, JSObject *obj); + +/* + * Resolve id, which must contain either a string or an int, to a standard + * class name in obj if possible, defining the class's constructor and/or + * prototype and storing true in *resolved. If id does not name a standard + * class or a top-level property induced by initializing a standard class, + * store false in *resolved and just return true. Return false on error, + * as usual for JSBool result-typed API entry points. + * + * This API can be called directly from a global object class's resolve op, + * to define standard classes lazily. The class's enumerate op should call + * JS_EnumerateStandardClasses(cx, obj), to define eagerly during for..in + * loops any classes not yet resolved lazily. + */ +extern JS_PUBLIC_API(JSBool) +JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, + JSBool *resolved); + +extern JS_PUBLIC_API(JSBool) +JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj); + +/* + * Enumerate any already-resolved standard class ids into ida, or into a new + * JSIdArray if ida is null. Return the augmented array on success, null on + * failure with ida (if it was non-null on entry) destroyed. + */ +extern JS_PUBLIC_API(JSIdArray *) +JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, + JSIdArray *ida); + +extern JS_PUBLIC_API(JSBool) +JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, + JSObject **objp); + +extern JS_PUBLIC_API(JSObject *) +JS_GetScopeChain(JSContext *cx); + +extern JS_PUBLIC_API(void *) +JS_malloc(JSContext *cx, size_t nbytes); + +extern JS_PUBLIC_API(void *) +JS_realloc(JSContext *cx, void *p, size_t nbytes); + +extern JS_PUBLIC_API(void) +JS_free(JSContext *cx, void *p); + +extern JS_PUBLIC_API(char *) +JS_strdup(JSContext *cx, const char *s); + +extern JS_PUBLIC_API(jsdouble *) +JS_NewDouble(JSContext *cx, jsdouble d); + +extern JS_PUBLIC_API(JSBool) +JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval); + +/* + * A JS GC root is a pointer to a JSObject *, JSString *, or jsdouble * that + * itself points into the GC heap (more recently, we support this extension: + * a root may be a pointer to a jsval v for which JSVAL_IS_GCTHING(v) is true). + * + * Therefore, you never pass JSObject *obj to JS_AddRoot(cx, obj). You always + * call JS_AddRoot(cx, &obj), passing obj by reference. And later, before obj + * or the structure it is embedded within goes out of scope or is freed, you + * must call JS_RemoveRoot(cx, &obj). + * + * Also, use JS_AddNamedRoot(cx, &structPtr->memberObj, "structPtr->memberObj") + * in preference to JS_AddRoot(cx, &structPtr->memberObj), in order to identify + * roots by their source callsites. This way, you can find the callsite while + * debugging if you should fail to do JS_RemoveRoot(cx, &structPtr->memberObj) + * before freeing structPtr's memory. + */ +extern JS_PUBLIC_API(JSBool) +JS_AddRoot(JSContext *cx, void *rp); + +#ifdef NAME_ALL_GC_ROOTS +#define JS_DEFINE_TO_TOKEN(def) #def +#define JS_DEFINE_TO_STRING(def) JS_DEFINE_TO_TOKEN(def) +#define JS_AddRoot(cx,rp) JS_AddNamedRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__)) +#endif + +extern JS_PUBLIC_API(JSBool) +JS_AddNamedRoot(JSContext *cx, void *rp, const char *name); + +extern JS_PUBLIC_API(JSBool) +JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name); + +extern JS_PUBLIC_API(JSBool) +JS_RemoveRoot(JSContext *cx, void *rp); + +extern JS_PUBLIC_API(JSBool) +JS_RemoveRootRT(JSRuntime *rt, void *rp); + +/* + * The last GC thing of each type (object, string, double, external string + * types) created on a given context is kept alive until another thing of the + * same type is created, using a newborn root in the context. These newborn + * roots help native code protect newly-created GC-things from GC invocations + * activated before those things can be rooted using local or global roots. + * + * However, the newborn roots can also entrain great gobs of garbage, so the + * JS_GC entry point clears them for the context on which GC is being forced. + * Embeddings may need to do likewise for all contexts. + * + * See the scoped local root API immediately below for a better way to manage + * newborns in cases where native hooks (functions, getters, setters, etc.) + * create many GC-things, potentially without connecting them to predefined + * local roots such as *rval or argv[i] in an active native function. Using + * JS_EnterLocalRootScope disables updating of the context's per-gc-thing-type + * newborn roots, until control flow unwinds and leaves the outermost nesting + * local root scope. + */ +extern JS_PUBLIC_API(void) +JS_ClearNewbornRoots(JSContext *cx); + +/* + * Scoped local root management allows native functions, getter/setters, etc. + * to avoid worrying about the newborn root pigeon-holes, overloading local + * roots allocated in argv and *rval, or ending up having to call JS_Add*Root + * and JS_RemoveRoot to manage global roots temporarily. + * + * Instead, calling JS_EnterLocalRootScope and JS_LeaveLocalRootScope around + * the body of the native hook causes the engine to allocate a local root for + * each newborn created in between the two API calls, using a local root stack + * associated with cx. For example: + * + * JSBool + * my_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) + * { + * JSBool ok; + * + * if (!JS_EnterLocalRootScope(cx)) + * return JS_FALSE; + * ok = my_GetPropertyBody(cx, obj, id, vp); + * JS_LeaveLocalRootScope(cx); + * return ok; + * } + * + * NB: JS_LeaveLocalRootScope must be called once for every prior successful + * call to JS_EnterLocalRootScope. If JS_EnterLocalRootScope fails, you must + * not make the matching JS_LeaveLocalRootScope call. + * + * JS_LeaveLocalRootScopeWithResult(cx, rval) is an alternative way to leave + * a local root scope that protects a result or return value, by effectively + * pushing it in the caller's local root scope. + * + * In case a native hook allocates many objects or other GC-things, but the + * native protects some of those GC-things by storing them as property values + * in an object that is itself protected, the hook can call JS_ForgetLocalRoot + * to free the local root automatically pushed for the now-protected GC-thing. + * + * JS_ForgetLocalRoot works on any GC-thing allocated in the current local + * root scope, but it's more time-efficient when called on references to more + * recently created GC-things. Calling it successively on other than the most + * recently allocated GC-thing will tend to average the time inefficiency, and + * may risk O(n^2) growth rate, but in any event, you shouldn't allocate too + * many local roots if you can root as you go (build a tree of objects from + * the top down, forgetting each latest-allocated GC-thing immediately upon + * linking it to its parent). + */ +extern JS_PUBLIC_API(JSBool) +JS_EnterLocalRootScope(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_LeaveLocalRootScope(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); + +extern JS_PUBLIC_API(void) +JS_ForgetLocalRoot(JSContext *cx, void *thing); + +#ifdef __cplusplus +JS_END_EXTERN_C + +class JSAutoLocalRootScope { + public: + JSAutoLocalRootScope(JSContext *cx) : mContext(cx) { + JS_EnterLocalRootScope(mContext); + } + ~JSAutoLocalRootScope() { + JS_LeaveLocalRootScope(mContext); + } + + void forget(void *thing) { + JS_ForgetLocalRoot(mContext, thing); + } + + protected: + JSContext *mContext; + +#if 0 + private: + static void *operator new(size_t) CPP_THROW_NEW { return 0; }; + static void operator delete(void *, size_t) { }; +#endif +}; + +JS_BEGIN_EXTERN_C +#endif + +#ifdef DEBUG +extern JS_PUBLIC_API(void) +JS_DumpNamedRoots(JSRuntime *rt, + void (*dump)(const char *name, void *rp, void *data), + void *data); +#endif + +/* + * Call JS_MapGCRoots to map the GC's roots table using map(rp, name, data). + * The root is pointed at by rp; if the root is unnamed, name is null; data is + * supplied from the third parameter to JS_MapGCRoots. + * + * The map function should return JS_MAP_GCROOT_REMOVE to cause the currently + * enumerated root to be removed. To stop enumeration, set JS_MAP_GCROOT_STOP + * in the return value. To keep on mapping, return JS_MAP_GCROOT_NEXT. These + * constants are flags; you can OR them together. + * + * This function acquires and releases rt's GC lock around the mapping of the + * roots table, so the map function should run to completion in as few cycles + * as possible. Of course, map cannot call JS_GC, JS_MaybeGC, JS_BeginRequest, + * or any JS API entry point that acquires locks, without double-tripping or + * deadlocking on the GC lock. + * + * JS_MapGCRoots returns the count of roots that were successfully mapped. + */ +#define JS_MAP_GCROOT_NEXT 0 /* continue mapping entries */ +#define JS_MAP_GCROOT_STOP 1 /* stop mapping entries */ +#define JS_MAP_GCROOT_REMOVE 2 /* remove and free the current entry */ + +typedef intN +(* JS_DLL_CALLBACK JSGCRootMapFun)(void *rp, const char *name, void *data); + +extern JS_PUBLIC_API(uint32) +JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); + +extern JS_PUBLIC_API(JSBool) +JS_LockGCThing(JSContext *cx, void *thing); + +extern JS_PUBLIC_API(JSBool) +JS_LockGCThingRT(JSRuntime *rt, void *thing); + +extern JS_PUBLIC_API(JSBool) +JS_UnlockGCThing(JSContext *cx, void *thing); + +extern JS_PUBLIC_API(JSBool) +JS_UnlockGCThingRT(JSRuntime *rt, void *thing); + +/* + * For implementors of JSObjectOps.mark, to mark a GC-thing reachable via a + * property or other strong ref identified for debugging purposes by name. + * The name argument's storage needs to live only as long as the call to + * this routine. + * + * The final arg is used by GC_MARK_DEBUG code to build a ref path through + * the GC's live thing graph. Implementors of JSObjectOps.mark should pass + * its final arg through to this function when marking all GC-things that are + * directly reachable from the object being marked. + * + * See the JSMarkOp typedef in jspubtd.h, and the JSObjectOps struct below. + */ +extern JS_PUBLIC_API(void) +JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg); + +extern JS_PUBLIC_API(void) +JS_GC(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_MaybeGC(JSContext *cx); + +extern JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallback(JSContext *cx, JSGCCallback cb); + +extern JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb); + +extern JS_PUBLIC_API(JSBool) +JS_IsAboutToBeFinalized(JSContext *cx, void *thing); + +typedef enum JSGCParamKey { + JSGC_MAX_BYTES = 0, /* maximum nominal heap before last ditch GC */ + JSGC_MAX_MALLOC_BYTES = 1 /* # of JS_malloc bytes before last ditch GC */ +} JSGCParamKey; + +extern JS_PUBLIC_API(void) +JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value); + +/* + * Add a finalizer for external strings created by JS_NewExternalString (see + * below) using a type-code returned from this function, and that understands + * how to free or release the memory pointed at by JS_GetStringChars(str). + * + * Return a nonnegative type index if there is room for finalizer in the + * global GC finalizers table, else return -1. If the engine is compiled + * JS_THREADSAFE and used in a multi-threaded environment, this function must + * be invoked on the primordial thread only, at startup -- or else the entire + * program must single-thread itself while loading a module that calls this + * function. + */ +extern JS_PUBLIC_API(intN) +JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer); + +/* + * Remove finalizer from the global GC finalizers table, returning its type + * code if found, -1 if not found. + * + * As with JS_AddExternalStringFinalizer, there is a threading restriction + * if you compile the engine JS_THREADSAFE: this function may be called for a + * given finalizer pointer on only one thread; different threads may call to + * remove distinct finalizers safely. + * + * You must ensure that all strings with finalizer's type have been collected + * before calling this function. Otherwise, string data will be leaked by the + * GC, for want of a finalizer to call. + */ +extern JS_PUBLIC_API(intN) +JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer); + +/* + * Create a new JSString whose chars member refers to external memory, i.e., + * memory requiring special, type-specific finalization. The type code must + * be a nonnegative return value from JS_AddExternalStringFinalizer. + */ +extern JS_PUBLIC_API(JSString *) +JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type); + +/* + * Returns the external-string finalizer index for this string, or -1 if it is + * an "internal" (native to JS engine) string. + */ +extern JS_PUBLIC_API(intN) +JS_GetExternalStringGCType(JSRuntime *rt, JSString *str); + +/* + * Sets maximum (if stack grows upward) or minimum (downward) legal stack byte + * address in limitAddr for the thread or process stack used by cx. To disable + * stack size checking, pass 0 for limitAddr. + */ +extern JS_PUBLIC_API(void) +JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr); + +/************************************************************************/ + +/* + * Classes, objects, and properties. + */ + +/* For detailed comments on the function pointer types, see jspubtd.h. */ +struct JSClass { + const char *name; + uint32 flags; + + /* Mandatory non-null function pointer members. */ + JSPropertyOp addProperty; + JSPropertyOp delProperty; + JSPropertyOp getProperty; + JSPropertyOp setProperty; + JSEnumerateOp enumerate; + JSResolveOp resolve; + JSConvertOp convert; + JSFinalizeOp finalize; + + /* Optionally non-null members start here. */ + JSGetObjectOps getObjectOps; + JSCheckAccessOp checkAccess; + JSNative call; + JSNative construct; + JSXDRObjectOp xdrObject; + JSHasInstanceOp hasInstance; + JSMarkOp mark; + JSReserveSlotsOp reserveSlots; +}; + +struct JSExtendedClass { + JSClass base; + JSEqualityOp equality; + JSObjectOp outerObject; + JSObjectOp innerObject; + void (*reserved0)(); + void (*reserved1)(); + void (*reserved2)(); + void (*reserved3)(); + void (*reserved4)(); +}; + +#define JSCLASS_HAS_PRIVATE (1<<0) /* objects have private slot */ +#define JSCLASS_NEW_ENUMERATE (1<<1) /* has JSNewEnumerateOp hook */ +#define JSCLASS_NEW_RESOLVE (1<<2) /* has JSNewResolveOp hook */ +#define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) /* private is (nsISupports *) */ +#define JSCLASS_SHARE_ALL_PROPERTIES (1<<4) /* all properties are SHARED */ +#define JSCLASS_NEW_RESOLVE_GETS_START (1<<5) /* JSNewResolveOp gets starting + object in prototype chain + passed in via *objp in/out + parameter */ +#define JSCLASS_CONSTRUCT_PROTOTYPE (1<<6) /* call constructor on class + prototype */ +#define JSCLASS_DOCUMENT_OBSERVER (1<<7) /* DOM document observer */ + +/* + * To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or + * JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where + * n is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1. + */ +#define JSCLASS_RESERVED_SLOTS_SHIFT 8 /* room for 8 flags below */ +#define JSCLASS_RESERVED_SLOTS_WIDTH 8 /* and 16 above this field */ +#define JSCLASS_RESERVED_SLOTS_MASK JS_BITMASK(JSCLASS_RESERVED_SLOTS_WIDTH) +#define JSCLASS_HAS_RESERVED_SLOTS(n) (((n) & JSCLASS_RESERVED_SLOTS_MASK) \ + << JSCLASS_RESERVED_SLOTS_SHIFT) +#define JSCLASS_RESERVED_SLOTS(clasp) (((clasp)->flags \ + >> JSCLASS_RESERVED_SLOTS_SHIFT) \ + & JSCLASS_RESERVED_SLOTS_MASK) + +#define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \ + JSCLASS_RESERVED_SLOTS_WIDTH) + +/* True if JSClass is really a JSExtendedClass. */ +#define JSCLASS_IS_EXTENDED (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) +#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1)) +#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2)) + +/* + * ECMA-262 requires that most constructors used internally create objects + * with "the original Foo.prototype value" as their [[Prototype]] (__proto__) + * member initial value. The "original ... value" verbiage is there because + * in ECMA-262, global properties naming class objects are read/write and + * deleteable, for the most part. + * + * Implementing this efficiently requires that global objects have classes + * with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS won't break + * anything except the ECMA-262 "original prototype value" behavior, which was + * broken for years in SpiderMonkey. In other words, without these flags you + * get backward compatibility. + */ +#define JSCLASS_GLOBAL_FLAGS \ + (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT)) + +/* Fast access to the original value of each standard class's prototype. */ +#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8) +#define JSCLASS_CACHED_PROTO_WIDTH 8 +#define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(JSCLASS_CACHED_PROTO_WIDTH) +#define JSCLASS_HAS_CACHED_PROTO(key) ((key) << JSCLASS_CACHED_PROTO_SHIFT) +#define JSCLASS_CACHED_PROTO_KEY(clasp) (((clasp)->flags \ + >> JSCLASS_CACHED_PROTO_SHIFT) \ + & JSCLASS_CACHED_PROTO_MASK) + +/* Initializer for unused members of statically initialized JSClass structs. */ +#define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,0,0,0 +#define JSCLASS_NO_RESERVED_MEMBERS 0,0,0,0,0 + +/* For detailed comments on these function pointer types, see jspubtd.h. */ +struct JSObjectOps { + /* Mandatory non-null function pointer members. */ + JSNewObjectMapOp newObjectMap; + JSObjectMapOp destroyObjectMap; + JSLookupPropOp lookupProperty; + JSDefinePropOp defineProperty; + JSPropertyIdOp getProperty; + JSPropertyIdOp setProperty; + JSAttributesOp getAttributes; + JSAttributesOp setAttributes; + JSPropertyIdOp deleteProperty; + JSConvertOp defaultValue; + JSNewEnumerateOp enumerate; + JSCheckAccessIdOp checkAccess; + + /* Optionally non-null members start here. */ + JSObjectOp thisObject; + JSPropertyRefOp dropProperty; + JSNative call; + JSNative construct; + JSXDRObjectOp xdrObject; + JSHasInstanceOp hasInstance; + JSSetObjectSlotOp setProto; + JSSetObjectSlotOp setParent; + JSMarkOp mark; + JSFinalizeOp clear; + JSGetRequiredSlotOp getRequiredSlot; + JSSetRequiredSlotOp setRequiredSlot; +}; + +struct JSXMLObjectOps { + JSObjectOps base; + JSGetMethodOp getMethod; + JSSetMethodOp setMethod; + JSEnumerateValuesOp enumerateValues; + JSEqualityOp equality; + JSConcatenateOp concatenate; +}; + +/* + * Classes that expose JSObjectOps via a non-null getObjectOps class hook may + * derive a property structure from this struct, return a pointer to it from + * lookupProperty and defineProperty, and use the pointer to avoid rehashing + * in getAttributes and setAttributes. + * + * The jsid type contains either an int jsval (see JSVAL_IS_INT above), or an + * internal pointer that is opaque to users of this API, but which users may + * convert from and to a jsval using JS_ValueToId and JS_IdToValue. + */ +struct JSProperty { + jsid id; +}; + +struct JSIdArray { + jsint length; + jsid vector[1]; /* actually, length jsid words */ +}; + +extern JS_PUBLIC_API(void) +JS_DestroyIdArray(JSContext *cx, JSIdArray *ida); + +extern JS_PUBLIC_API(JSBool) +JS_ValueToId(JSContext *cx, jsval v, jsid *idp); + +extern JS_PUBLIC_API(JSBool) +JS_IdToValue(JSContext *cx, jsid id, jsval *vp); + +/* + * The magic XML namespace id is int-tagged, but not a valid integer jsval. + * Global object classes in embeddings that enable JS_HAS_XML_SUPPORT (E4X) + * should handle this id specially before converting id via JSVAL_TO_INT. + */ +#define JS_DEFAULT_XML_NAMESPACE_ID ((jsid) JSVAL_VOID) + +/* + * JSNewResolveOp flag bits. + */ +#define JSRESOLVE_QUALIFIED 0x01 /* resolve a qualified property id */ +#define JSRESOLVE_ASSIGNING 0x02 /* resolve on the left of assignment */ +#define JSRESOLVE_DETECTING 0x04 /* 'if (o.p)...' or '(o.p) ?...:...' */ +#define JSRESOLVE_DECLARING 0x08 /* var, const, or function prolog op */ +#define JSRESOLVE_CLASSNAME 0x10 /* class name used when constructing */ + +extern JS_PUBLIC_API(JSBool) +JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_EnumerateStub(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id); + +extern JS_PUBLIC_API(JSBool) +JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp); + +extern JS_PUBLIC_API(void) +JS_FinalizeStub(JSContext *cx, JSObject *obj); + +struct JSConstDoubleSpec { + jsdouble dval; + const char *name; + uint8 flags; + uint8 spare[3]; +}; + +/* + * To define an array element rather than a named property member, cast the + * element's index to (const char *) and initialize name with it, and set the + * JSPROP_INDEX bit in flags. + */ +struct JSPropertySpec { + const char *name; + int8 tinyid; + uint8 flags; + JSPropertyOp getter; + JSPropertyOp setter; +}; + +struct JSFunctionSpec { + const char *name; + JSNative call; +#ifdef MOZILLA_1_8_BRANCH + uint8 nargs; + uint8 flags; + uint16 extra; +#else + uint16 nargs; + uint16 flags; + uint32 extra; /* extra & 0xFFFF: + number of arg slots for local GC roots + extra >> 16: + reserved, must be zero */ +#endif +}; + +extern JS_PUBLIC_API(JSObject *) +JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, + JSClass *clasp, JSNative constructor, uintN nargs, + JSPropertySpec *ps, JSFunctionSpec *fs, + JSPropertySpec *static_ps, JSFunctionSpec *static_fs); + +#ifdef JS_THREADSAFE +extern JS_PUBLIC_API(JSClass *) +JS_GetClass(JSContext *cx, JSObject *obj); + +#define JS_GET_CLASS(cx,obj) JS_GetClass(cx, obj) +#else +extern JS_PUBLIC_API(JSClass *) +JS_GetClass(JSObject *obj); + +#define JS_GET_CLASS(cx,obj) JS_GetClass(obj) +#endif + +extern JS_PUBLIC_API(JSBool) +JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); + +extern JS_PUBLIC_API(JSBool) +JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +extern JS_PUBLIC_API(void *) +JS_GetPrivate(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_SetPrivate(JSContext *cx, JSObject *obj, void *data); + +extern JS_PUBLIC_API(void *) +JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, + jsval *argv); + +extern JS_PUBLIC_API(JSObject *) +JS_GetPrototype(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto); + +extern JS_PUBLIC_API(JSObject *) +JS_GetParent(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent); + +extern JS_PUBLIC_API(JSObject *) +JS_GetConstructor(JSContext *cx, JSObject *proto); + +/* + * Get a unique identifier for obj, good for the lifetime of obj (even if it + * is moved by a copying GC). Return false on failure (likely out of memory), + * and true with *idp containing the unique id on success. + */ +extern JS_PUBLIC_API(JSBool) +JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp); + +extern JS_PUBLIC_API(JSObject *) +JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); + +extern JS_PUBLIC_API(JSBool) +JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep); + +extern JS_PUBLIC_API(JSObject *) +JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent); + +extern JS_PUBLIC_API(JSObject *) +JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, uintN argc, jsval *argv); + +extern JS_PUBLIC_API(JSObject *) +JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, + JSObject *proto, uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds); + +extern JS_PUBLIC_API(JSBool) +JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps); + +extern JS_PUBLIC_API(JSBool) +JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs); + +/* + * Determine the attributes (JSPROP_* flags) of a property on a given object. + * + * If the object does not have a property by that name, *foundp will be + * JS_FALSE and the value of *attrsp is undefined. + */ +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, + uintN *attrsp, JSBool *foundp); + +/* + * The same, but if the property is native, return its getter and setter via + * *getterp and *setterp, respectively (and only if the out parameter pointer + * is not null). + */ +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const char *name, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp); + +/* + * Set the attributes of a property on a given object. + * + * If the object does not have a property by that name, *foundp will be + * JS_FALSE and nothing will be altered. + */ +extern JS_PUBLIC_API(JSBool) +JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, + uintN attrs, JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, + int8 tinyid, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, + const char *alias); + +extern JS_PUBLIC_API(JSBool) +JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, + uintN flags, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_DefineUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs); + +/* + * Determine the attributes (JSPROP_* flags) of a property on a given object. + * + * If the object does not have a property by that name, *foundp will be + * JS_FALSE and the value of *attrsp is undefined. + */ +extern JS_PUBLIC_API(JSBool) +JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN *attrsp, JSBool *foundp); + +/* + * The same, but if the property is native, return its getter and setter via + * *getterp and *setterp, respectively (and only if the out parameter pointer + * is not null). + */ +extern JS_PUBLIC_API(JSBool) +JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp); + +/* + * Set the attributes of a property on a given object. + * + * If the object does not have a property by that name, *foundp will be + * JS_FALSE and nothing will be altered. + */ +extern JS_PUBLIC_API(JSBool) +JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN attrs, JSBool *foundp); + + +extern JS_PUBLIC_API(JSBool) +JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + int8 tinyid, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_HasUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + JSBool *vp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *rval); + +extern JS_PUBLIC_API(JSObject *) +JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector); + +extern JS_PUBLIC_API(JSBool) +JS_IsArrayObject(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); + +extern JS_PUBLIC_API(JSBool) +JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length); + +extern JS_PUBLIC_API(JSBool) +JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); + +extern JS_PUBLIC_API(JSBool) +JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias); + +extern JS_PUBLIC_API(JSBool) +JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval); + +extern JS_PUBLIC_API(void) +JS_ClearScope(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSIdArray *) +JS_Enumerate(JSContext *cx, JSObject *obj); + +/* + * Create an object to iterate over enumerable properties of obj, in arbitrary + * property definition order. NB: This differs from longstanding for..in loop + * order, which uses order of property definition in obj. + */ +extern JS_PUBLIC_API(JSObject *) +JS_NewPropertyIterator(JSContext *cx, JSObject *obj); + +/* + * Return true on success with *idp containing the id of the next enumerable + * property to visit using iterobj, or JSVAL_VOID if there is no such property + * left to visit. Return false on error. + */ +extern JS_PUBLIC_API(JSBool) +JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp); + +extern JS_PUBLIC_API(JSBool) +JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp); + +extern JS_PUBLIC_API(JSCheckAccessOp) +JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb); + +extern JS_PUBLIC_API(JSBool) +JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v); + +/************************************************************************/ + +/* + * Security protocol. + */ +struct JSPrincipals { + char *codebase; + + /* XXX unspecified and unused by Mozilla code -- can we remove these? */ + void * (* JS_DLL_CALLBACK getPrincipalArray)(JSContext *cx, JSPrincipals *); + JSBool (* JS_DLL_CALLBACK globalPrivilegesEnabled)(JSContext *cx, JSPrincipals *); + + /* Don't call "destroy"; use reference counting macros below. */ + jsrefcount refcount; + + void (* JS_DLL_CALLBACK destroy)(JSContext *cx, JSPrincipals *); + JSBool (* JS_DLL_CALLBACK subsume)(JSPrincipals *, JSPrincipals *); +}; + +#ifdef JS_THREADSAFE +#define JSPRINCIPALS_HOLD(cx, principals) JS_HoldPrincipals(cx,principals) +#define JSPRINCIPALS_DROP(cx, principals) JS_DropPrincipals(cx,principals) + +extern JS_PUBLIC_API(jsrefcount) +JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals); + +extern JS_PUBLIC_API(jsrefcount) +JS_DropPrincipals(JSContext *cx, JSPrincipals *principals); + +#else +#define JSPRINCIPALS_HOLD(cx, principals) (++(principals)->refcount) +#define JSPRINCIPALS_DROP(cx, principals) \ + ((--(principals)->refcount == 0) \ + ? ((*(principals)->destroy)((cx), (principals)), 0) \ + : (principals)->refcount) +#endif + +extern JS_PUBLIC_API(JSPrincipalsTranscoder) +JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px); + +extern JS_PUBLIC_API(JSObjectPrincipalsFinder) +JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop); + +/************************************************************************/ + +/* + * Functions and scripts. + */ +extern JS_PUBLIC_API(JSFunction *) +JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags, + JSObject *parent, const char *name); + +extern JS_PUBLIC_API(JSObject *) +JS_GetFunctionObject(JSFunction *fun); + +/* + * Deprecated, useful only for diagnostics. Use JS_GetFunctionId instead for + * anonymous vs. "anonymous" disambiguation and Unicode fidelity. + */ +extern JS_PUBLIC_API(const char *) +JS_GetFunctionName(JSFunction *fun); + +/* + * Return the function's identifier as a JSString, or null if fun is unnamed. + * The returned string lives as long as fun, so you don't need to root a saved + * reference to it if fun is well-connected or rooted, and provided you bound + * the use of the saved reference by fun's lifetime. + * + * Prefer JS_GetFunctionId over JS_GetFunctionName because it returns null for + * truly anonymous functions, and because it doesn't chop to ISO-Latin-1 chars + * from UTF-16-ish jschars. + */ +extern JS_PUBLIC_API(JSString *) +JS_GetFunctionId(JSFunction *fun); + +/* + * Return JSFUN_* flags for fun. + */ +extern JS_PUBLIC_API(uintN) +JS_GetFunctionFlags(JSFunction *fun); + +/* + * Return the arity (length) of fun. + */ +extern JS_PUBLIC_API(uint16) +JS_GetFunctionArity(JSFunction *fun); + +/* + * Infallible predicate to test whether obj is a function object (faster than + * comparing obj's class name to "Function", but equivalent unless someone has + * overwritten the "Function" identifier with a different constructor and then + * created instances using that constructor that might be passed in as obj). + */ +extern JS_PUBLIC_API(JSBool) +JS_ObjectIsFunction(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs); + +extern JS_PUBLIC_API(JSFunction *) +JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, + uintN nargs, uintN attrs); + +extern JS_PUBLIC_API(JSFunction *) +JS_DefineUCFunction(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, JSNative call, + uintN nargs, uintN attrs); + +extern JS_PUBLIC_API(JSObject *) +JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); + +/* + * Given a buffer, return JS_FALSE if the buffer might become a valid + * javascript statement with the addition of more lines. Otherwise return + * JS_TRUE. The intent is to support interactive compilation - accumulate + * lines in a buffer until JS_BufferIsCompilableUnit is true, then pass it to + * the compiler. + */ +extern JS_PUBLIC_API(JSBool) +JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, + const char *bytes, size_t length); + +/* + * The JSScript objects returned by the following functions refer to string and + * other kinds of literals, including doubles and RegExp objects. These + * literals are vulnerable to garbage collection; to root script objects and + * prevent literals from being collected, create a rootable object using + * JS_NewScriptObject, and root the resulting object using JS_Add[Named]Root. + */ +extern JS_PUBLIC_API(JSScript *) +JS_CompileScript(JSContext *cx, JSObject *obj, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileUCScript(JSContext *cx, JSObject *obj, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, + FILE *fh); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, + const char *filename, FILE *fh, + JSPrincipals *principals); + +/* + * NB: you must use JS_NewScriptObject and root a pointer to its return value + * in order to keep a JSScript and its atoms safe from garbage collection after + * creating the script via JS_Compile* and before a JS_ExecuteScript* call. + * E.g., and without error checks: + * + * JSScript *script = JS_CompileFile(cx, global, filename); + * JSObject *scrobj = JS_NewScriptObject(cx, script); + * JS_AddNamedRoot(cx, &scrobj, "scrobj"); + * do { + * jsval result; + * JS_ExecuteScript(cx, global, script, &result); + * JS_GC(); + * } while (!JSVAL_IS_BOOLEAN(result) || JSVAL_TO_BOOLEAN(result)); + * JS_RemoveRoot(cx, &scrobj); + */ +extern JS_PUBLIC_API(JSObject *) +JS_NewScriptObject(JSContext *cx, JSScript *script); + +/* + * Infallible getter for a script's object. If JS_NewScriptObject has not been + * called on script yet, the return value will be null. + */ +extern JS_PUBLIC_API(JSObject *) +JS_GetScriptObject(JSScript *script); + +extern JS_PUBLIC_API(void) +JS_DestroyScript(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, + uintN nargs, const char **argnames, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, const char *name, + uintN nargs, const char **argnames, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, + uintN nargs, const char **argnames, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, const char *name, + uintN nargs, const char **argnames, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSString *) +JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, + uintN indent); + +/* + * API extension: OR this into indent to avoid pretty-printing the decompiled + * source resulting from JS_DecompileFunction{,Body}. + */ +#define JS_DONT_PRETTY_PRINT ((uintN)0x8000) + +extern JS_PUBLIC_API(JSString *) +JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent); + +extern JS_PUBLIC_API(JSString *) +JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent); + +/* + * NB: JS_ExecuteScript, JS_ExecuteScriptPart, and the JS_Evaluate*Script* + * quadruplets all use the obj parameter as the initial scope chain header, + * the 'this' keyword value, and the variables object (ECMA parlance for where + * 'var' and 'function' bind names) of the execution context for script. + * + * Using obj as the variables object is problematic if obj's parent (which is + * the scope chain link; see JS_SetParent and JS_NewObject) is not null: in + * this case, variables created by 'var x = 0', e.g., go in obj, but variables + * created by assignment to an unbound id, 'x = 0', go in the last object on + * the scope chain linked by parent. + * + * ECMA calls that last scoping object the "global object", but note that many + * embeddings have several such objects. ECMA requires that "global code" be + * executed with the variables object equal to this global object. But these + * JS API entry points provide freedom to execute code against a "sub-global", + * i.e., a parented or scoped object, in which case the variables object will + * differ from the last object on the scope chain, resulting in confusing and + * non-ECMA explicit vs. implicit variable creation. + * + * Caveat embedders: unless you already depend on this buggy variables object + * binding behavior, you should call JS_SetOptions(cx, JSOPTION_VAROBJFIX) or + * JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_VAROBJFIX) -- the latter if + * someone may have set other options on cx already -- for each context in the + * application, if you pass parented objects as the obj parameter, or may ever + * pass such objects in the future. + * + * Why a runtime option? The alternative is to add six or so new API entry + * points with signatures matching the following six, and that doesn't seem + * worth the code bloat cost. Such new entry points would probably have less + * obvious names, too, so would not tend to be used. The JS_SetOption call, + * OTOH, can be more easily hacked into existing code that does not depend on + * the bug; such code can continue to use the familiar JS_EvaluateScript, + * etc., entry points. + */ +extern JS_PUBLIC_API(JSBool) +JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval); + +/* + * Execute either the function-defining prolog of a script, or the script's + * main body, but not both. + */ +typedef enum JSExecPart { JSEXEC_PROLOG, JSEXEC_MAIN } JSExecPart; + +extern JS_PUBLIC_API(JSBool) +JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, + JSExecPart part, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateScript(JSContext *cx, JSObject *obj, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateUCScript(JSContext *cx, JSObject *obj, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, + jsval *argv, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, + jsval *argv, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, + jsval *argv, jsval *rval); + +extern JS_PUBLIC_API(JSBranchCallback) +JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb); + +extern JS_PUBLIC_API(JSBool) +JS_IsRunning(JSContext *cx); + +extern JS_PUBLIC_API(JSBool) +JS_IsConstructing(JSContext *cx); + +/* + * Returns true if a script is executing and its current bytecode is a set + * (assignment) operation, even if there are native (no script) stack frames + * between the script and the caller to JS_IsAssigning. + */ +extern JS_FRIEND_API(JSBool) +JS_IsAssigning(JSContext *cx); + +/* + * Set the second return value, which should be a string or int jsval that + * identifies a property in the returned object, to form an ECMA reference + * type value (obj, id). Only native methods can return reference types, + * and if the returned value is used on the left-hand side of an assignment + * op, the identified property will be set. If the return value is in an + * r-value, the interpreter just gets obj[id]'s value. + */ +extern JS_PUBLIC_API(void) +JS_SetCallReturnValue2(JSContext *cx, jsval v); + +/* + * Saving and restoring frame chains. + * + * These two functions are used to set aside cx->fp while that frame is + * inactive. After a call to JS_SaveFrameChain, it looks as if there is no + * code running on cx. Before calling JS_RestoreFrameChain, cx's call stack + * must be balanced and all nested calls to JS_SaveFrameChain must have had + * matching JS_RestoreFrameChain calls. + * + * JS_SaveFrameChain deals with cx not having any code running on it. A null + * return does not signify an error and JS_RestoreFrameChain handles null + * frames. + */ +extern JS_PUBLIC_API(JSStackFrame *) +JS_SaveFrameChain(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp); + +/************************************************************************/ + +/* + * Strings. + * + * NB: JS_NewString takes ownership of bytes on success, avoiding a copy; but + * on error (signified by null return), it leaves bytes owned by the caller. + * So the caller must free bytes in the error case, if it has no use for them. + * In contrast, all the JS_New*StringCopy* functions do not take ownership of + * the character memory passed to them -- they copy it. + */ +extern JS_PUBLIC_API(JSString *) +JS_NewString(JSContext *cx, char *bytes, size_t length); + +extern JS_PUBLIC_API(JSString *) +JS_NewStringCopyN(JSContext *cx, const char *s, size_t n); + +extern JS_PUBLIC_API(JSString *) +JS_NewStringCopyZ(JSContext *cx, const char *s); + +extern JS_PUBLIC_API(JSString *) +JS_InternString(JSContext *cx, const char *s); + +extern JS_PUBLIC_API(JSString *) +JS_NewUCString(JSContext *cx, jschar *chars, size_t length); + +extern JS_PUBLIC_API(JSString *) +JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n); + +extern JS_PUBLIC_API(JSString *) +JS_NewUCStringCopyZ(JSContext *cx, const jschar *s); + +extern JS_PUBLIC_API(JSString *) +JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length); + +extern JS_PUBLIC_API(JSString *) +JS_InternUCString(JSContext *cx, const jschar *s); + +extern JS_PUBLIC_API(char *) +JS_GetStringBytes(JSString *str); + +extern JS_PUBLIC_API(jschar *) +JS_GetStringChars(JSString *str); + +extern JS_PUBLIC_API(size_t) +JS_GetStringLength(JSString *str); + +extern JS_PUBLIC_API(intN) +JS_CompareStrings(JSString *str1, JSString *str2); + +/* + * Mutable string support. A string's characters are never mutable in this JS + * implementation, but a growable string has a buffer that can be reallocated, + * and a dependent string is a substring of another (growable, dependent, or + * immutable) string. The direct data members of the (opaque to API clients) + * JSString struct may be changed in a single-threaded way for growable and + * dependent strings. + * + * Therefore mutable strings cannot be used by more than one thread at a time. + * You may call JS_MakeStringImmutable to convert the string from a mutable + * (growable or dependent) string to an immutable (and therefore thread-safe) + * string. The engine takes care of converting growable and dependent strings + * to immutable for you if you store strings in multi-threaded objects using + * JS_SetProperty or kindred API entry points. + * + * If you store a JSString pointer in a native data structure that is (safely) + * accessible to multiple threads, you must call JS_MakeStringImmutable before + * retiring the store. + */ +extern JS_PUBLIC_API(JSString *) +JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length); + +/* + * Create a dependent string, i.e., a string that owns no character storage, + * but that refers to a slice of another string's chars. Dependent strings + * are mutable by definition, so the thread safety comments above apply. + */ +extern JS_PUBLIC_API(JSString *) +JS_NewDependentString(JSContext *cx, JSString *str, size_t start, + size_t length); + +/* + * Concatenate two strings, resulting in a new growable string. If you create + * the left string and pass it to JS_ConcatStrings on a single thread, try to + * use JS_NewGrowableString to create the left string -- doing so helps Concat + * avoid allocating a new buffer for the result and copying left's chars into + * the new buffer. See above for thread safety comments. + */ +extern JS_PUBLIC_API(JSString *) +JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right); + +/* + * Convert a dependent string into an independent one. This function does not + * change the string's mutability, so the thread safety comments above apply. + */ +extern JS_PUBLIC_API(const jschar *) +JS_UndependString(JSContext *cx, JSString *str); + +/* + * Convert a mutable string (either growable or dependent) into an immutable, + * thread-safe one. + */ +extern JS_PUBLIC_API(JSBool) +JS_MakeStringImmutable(JSContext *cx, JSString *str); + +/* + * Return JS_TRUE if C (char []) strings passed via the API and internally + * are UTF-8. The source must be compiled with JS_C_STRINGS_ARE_UTF8 defined + * to get UTF-8 support. + */ +JS_PUBLIC_API(JSBool) +JS_CStringsAreUTF8(); + +/* + * Character encoding support. + * + * For both JS_EncodeCharacters and JS_DecodeBytes, set *dstlenp to the size + * of the destination buffer before the call; on return, *dstlenp contains the + * number of bytes (JS_EncodeCharacters) or jschars (JS_DecodeBytes) actually + * stored. To determine the necessary destination buffer size, make a sizing + * call that passes NULL for dst. + * + * On errors, the functions report the error. In that case, *dstlenp contains + * the number of characters or bytes transferred so far. If cx is NULL, no + * error is reported on failure, and the functions simply return JS_FALSE. + * + * NB: Neither function stores an additional zero byte or jschar after the + * transcoded string. + * + * If the source has been compiled with the #define JS_C_STRINGS_ARE_UTF8 to + * enable UTF-8 interpretation of C char[] strings, then JS_EncodeCharacters + * encodes to UTF-8, and JS_DecodeBytes decodes from UTF-8, which may create + * addititional errors if the character sequence is malformed. If UTF-8 + * support is disabled, the functions deflate and inflate, respectively. + */ +JS_PUBLIC_API(JSBool) +JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, + size_t *dstlenp); + +JS_PUBLIC_API(JSBool) +JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, + size_t *dstlenp); + +/************************************************************************/ + +/* + * Locale specific string conversion and error message callbacks. + */ +struct JSLocaleCallbacks { + JSLocaleToUpperCase localeToUpperCase; + JSLocaleToLowerCase localeToLowerCase; + JSLocaleCompare localeCompare; + JSLocaleToUnicode localeToUnicode; + JSErrorCallback localeGetErrorMessage; +}; + +/* + * Establish locale callbacks. The pointer must persist as long as the + * JSContext. Passing NULL restores the default behaviour. + */ +extern JS_PUBLIC_API(void) +JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks); + +/* + * Return the address of the current locale callbacks struct, which may + * be NULL. + */ +extern JS_PUBLIC_API(JSLocaleCallbacks *) +JS_GetLocaleCallbacks(JSContext *cx); + +/************************************************************************/ + +/* + * Error reporting. + */ + +/* + * Report an exception represented by the sprintf-like conversion of format + * and its arguments. This exception message string is passed to a pre-set + * JSErrorReporter function (set by JS_SetErrorReporter; see jspubtd.h for + * the JSErrorReporter typedef). + */ +extern JS_PUBLIC_API(void) +JS_ReportError(JSContext *cx, const char *format, ...); + +/* + * Use an errorNumber to retrieve the format string, args are char * + */ +extern JS_PUBLIC_API(void) +JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, + void *userRef, const uintN errorNumber, ...); + +/* + * Use an errorNumber to retrieve the format string, args are jschar * + */ +extern JS_PUBLIC_API(void) +JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, + void *userRef, const uintN errorNumber, ...); + +/* + * As above, but report a warning instead (JSREPORT_IS_WARNING(report.flags)). + * Return true if there was no error trying to issue the warning, and if the + * warning was not converted into an error due to the JSOPTION_WERROR option + * being set, false otherwise. + */ +extern JS_PUBLIC_API(JSBool) +JS_ReportWarning(JSContext *cx, const char *format, ...); + +extern JS_PUBLIC_API(JSBool) +JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, + JSErrorCallback errorCallback, void *userRef, + const uintN errorNumber, ...); + +extern JS_PUBLIC_API(JSBool) +JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, + JSErrorCallback errorCallback, void *userRef, + const uintN errorNumber, ...); + +/* + * Complain when out of memory. + */ +extern JS_PUBLIC_API(void) +JS_ReportOutOfMemory(JSContext *cx); + +struct JSErrorReport { + const char *filename; /* source file name, URL, etc., or null */ + uintN lineno; /* source line number */ + const char *linebuf; /* offending source line without final \n */ + const char *tokenptr; /* pointer to error token in linebuf */ + const jschar *uclinebuf; /* unicode (original) line buffer */ + const jschar *uctokenptr; /* unicode (original) token pointer */ + uintN flags; /* error/warning, etc. */ + uintN errorNumber; /* the error number, e.g. see js.msg */ + const jschar *ucmessage; /* the (default) error message */ + const jschar **messageArgs; /* arguments for the error message */ +}; + +/* + * JSErrorReport flag values. These may be freely composed. + */ +#define JSREPORT_ERROR 0x0 /* pseudo-flag for default case */ +#define JSREPORT_WARNING 0x1 /* reported via JS_ReportWarning */ +#define JSREPORT_EXCEPTION 0x2 /* exception was thrown */ +#define JSREPORT_STRICT 0x4 /* error or warning due to strict option */ + +/* + * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception + * has been thrown for this runtime error, and the host should ignore it. + * Exception-aware hosts should also check for JS_IsExceptionPending if + * JS_ExecuteScript returns failure, and signal or propagate the exception, as + * appropriate. + */ +#define JSREPORT_IS_WARNING(flags) (((flags) & JSREPORT_WARNING) != 0) +#define JSREPORT_IS_EXCEPTION(flags) (((flags) & JSREPORT_EXCEPTION) != 0) +#define JSREPORT_IS_STRICT(flags) (((flags) & JSREPORT_STRICT) != 0) + +extern JS_PUBLIC_API(JSErrorReporter) +JS_SetErrorReporter(JSContext *cx, JSErrorReporter er); + +/************************************************************************/ + +/* + * Regular Expressions. + */ +#define JSREG_FOLD 0x01 /* fold uppercase to lowercase */ +#define JSREG_GLOB 0x02 /* global exec, creates array of matches */ +#define JSREG_MULTILINE 0x04 /* treat ^ and $ as begin and end of line */ + +extern JS_PUBLIC_API(JSObject *) +JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags); + +extern JS_PUBLIC_API(JSObject *) +JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags); + +extern JS_PUBLIC_API(void) +JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline); + +extern JS_PUBLIC_API(void) +JS_ClearRegExpStatics(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_ClearRegExpRoots(JSContext *cx); + +/* TODO: compile, exec, get/set other statics... */ + +/************************************************************************/ + +extern JS_PUBLIC_API(JSBool) +JS_IsExceptionPending(JSContext *cx); + +extern JS_PUBLIC_API(JSBool) +JS_GetPendingException(JSContext *cx, jsval *vp); + +extern JS_PUBLIC_API(void) +JS_SetPendingException(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(void) +JS_ClearPendingException(JSContext *cx); + +extern JS_PUBLIC_API(JSBool) +JS_ReportPendingException(JSContext *cx); + +/* + * Save the current exception state. This takes a snapshot of cx's current + * exception state without making any change to that state. + * + * The returned state pointer MUST be passed later to JS_RestoreExceptionState + * (to restore that saved state, overriding any more recent state) or else to + * JS_DropExceptionState (to free the state struct in case it is not correct + * or desirable to restore it). Both Restore and Drop free the state struct, + * so callers must stop using the pointer returned from Save after calling the + * Release or Drop API. + */ +extern JS_PUBLIC_API(JSExceptionState *) +JS_SaveExceptionState(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state); + +extern JS_PUBLIC_API(void) +JS_DropExceptionState(JSContext *cx, JSExceptionState *state); + +/* + * If the given value is an exception object that originated from an error, + * the exception will contain an error report struct, and this API will return + * the address of that struct. Otherwise, it returns NULL. The lifetime of + * the error report struct that might be returned is the same as the lifetime + * of the exception object. + */ +extern JS_PUBLIC_API(JSErrorReport *) +JS_ErrorFromException(JSContext *cx, jsval v); + +/* + * Given a reported error's message and JSErrorReport struct pointer, throw + * the corresponding exception on cx. + */ +extern JS_PUBLIC_API(JSBool) +JS_ThrowReportedError(JSContext *cx, const char *message, + JSErrorReport *reportp); + +#ifdef JS_THREADSAFE + +/* + * Associate the current thread with the given context. This is done + * implicitly by JS_NewContext. + * + * Returns the old thread id for this context, which should be treated as + * an opaque value. This value is provided for comparison to 0, which + * indicates that ClearContextThread has been called on this context + * since the last SetContextThread, or non-0, which indicates the opposite. + */ +extern JS_PUBLIC_API(jsword) +JS_GetContextThread(JSContext *cx); + +extern JS_PUBLIC_API(jsword) +JS_SetContextThread(JSContext *cx); + +extern JS_PUBLIC_API(jsword) +JS_ClearContextThread(JSContext *cx); + +#endif /* JS_THREADSAFE */ + +/************************************************************************/ + +JS_END_EXTERN_C + +#endif /* jsapi_h___ */ diff --git a/extra_lib/include/js/jsautocfg.h b/extra_lib/include/js/jsautocfg.h new file mode 100644 index 0000000..a7f5d6b --- /dev/null +++ b/extra_lib/include/js/jsautocfg.h @@ -0,0 +1,50 @@ +#ifndef js_cpucfg___ +#define js_cpucfg___ + +/* AUTOMATICALLY GENERATED - DO NOT EDIT */ + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define JS_BYTES_PER_BYTE 1L +#define JS_BYTES_PER_SHORT 2L +#define JS_BYTES_PER_INT 4L +#define JS_BYTES_PER_INT64 8L +#define JS_BYTES_PER_LONG 4L +#define JS_BYTES_PER_FLOAT 4L +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 4L +#define JS_BYTES_PER_DWORD 8L + +#define JS_BITS_PER_BYTE 8L +#define JS_BITS_PER_SHORT 16L +#define JS_BITS_PER_INT 32L +#define JS_BITS_PER_INT64 64L +#define JS_BITS_PER_LONG 32L +#define JS_BITS_PER_FLOAT 32L +#define JS_BITS_PER_DOUBLE 64L +#define JS_BITS_PER_WORD 32L + +#define JS_BITS_PER_BYTE_LOG2 3L +#define JS_BITS_PER_SHORT_LOG2 4L +#define JS_BITS_PER_INT_LOG2 5L +#define JS_BITS_PER_INT64_LOG2 6L +#define JS_BITS_PER_LONG_LOG2 5L +#define JS_BITS_PER_FLOAT_LOG2 5L +#define JS_BITS_PER_DOUBLE_LOG2 6L +#define JS_BITS_PER_WORD_LOG2 5L + +#define JS_ALIGN_OF_SHORT 2L +#define JS_ALIGN_OF_INT 4L +#define JS_ALIGN_OF_LONG 4L +#define JS_ALIGN_OF_INT64 4L +#define JS_ALIGN_OF_FLOAT 4L +#define JS_ALIGN_OF_DOUBLE 4L +#define JS_ALIGN_OF_POINTER 4L +#define JS_ALIGN_OF_WORD 4L + +#define JS_BYTES_PER_WORD_LOG2 2L +#define JS_BYTES_PER_DWORD_LOG2 3L +#define JS_WORDS_PER_DWORD_LOG2 1L + +#endif /* js_cpucfg___ */ diff --git a/extra_lib/include/js/jscompat.h b/extra_lib/include/js/jscompat.h new file mode 100644 index 0000000..80d8605 --- /dev/null +++ b/extra_lib/include/js/jscompat.h @@ -0,0 +1,57 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* -*- Mode: C; tab-width: 8 -*- + * Copyright (C) 1996-1999 Netscape Communications Corporation, All Rights Reserved. + */ +#ifndef jscompat_h___ +#define jscompat_h___ +/* + * Compatibility glue for various NSPR versions. We must always define int8, + * int16, jsword, and so on to minimize differences with js/ref, no matter what + * the NSPR typedef names may be. + */ +#include "jstypes.h" +#include "jslong.h" + +typedef JSIntn intN; +typedef JSUintn uintN; +typedef JSUword jsuword; +typedef JSWord jsword; +typedef float float32; +#define allocPriv allocPool +#endif /* jscompat_h___ */ diff --git a/extra_lib/include/js/jscpucfg.h b/extra_lib/include/js/jscpucfg.h new file mode 100644 index 0000000..316205d --- /dev/null +++ b/extra_lib/include/js/jscpucfg.h @@ -0,0 +1,262 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef js_cpucfg___ +#define js_cpucfg___ + +#include "jsosdep.h" + +#if defined(XP_WIN) || defined(XP_OS2) || defined(WINCE) + +#if defined(_WIN64) + +#if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define JS_BYTES_PER_BYTE 1L +#define JS_BYTES_PER_SHORT 2L +#define JS_BYTES_PER_INT 4L +#define JS_BYTES_PER_INT64 8L +#define JS_BYTES_PER_LONG 4L +#define JS_BYTES_PER_FLOAT 4L +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 8L +#define JS_BYTES_PER_DWORD 8L + +#define JS_BITS_PER_BYTE 8L +#define JS_BITS_PER_SHORT 16L +#define JS_BITS_PER_INT 32L +#define JS_BITS_PER_INT64 64L +#define JS_BITS_PER_LONG 32L +#define JS_BITS_PER_FLOAT 32L +#define JS_BITS_PER_DOUBLE 64L +#define JS_BITS_PER_WORD 64L + +#define JS_BITS_PER_BYTE_LOG2 3L +#define JS_BITS_PER_SHORT_LOG2 4L +#define JS_BITS_PER_INT_LOG2 5L +#define JS_BITS_PER_INT64_LOG2 6L +#define JS_BITS_PER_LONG_LOG2 5L +#define JS_BITS_PER_FLOAT_LOG2 5L +#define JS_BITS_PER_DOUBLE_LOG2 6L +#define JS_BITS_PER_WORD_LOG2 6L + +#define JS_ALIGN_OF_SHORT 2L +#define JS_ALIGN_OF_INT 4L +#define JS_ALIGN_OF_LONG 4L +#define JS_ALIGN_OF_INT64 8L +#define JS_ALIGN_OF_FLOAT 4L +#define JS_ALIGN_OF_DOUBLE 8L +#define JS_ALIGN_OF_POINTER 8L +#define JS_ALIGN_OF_WORD 8L + +#define JS_BYTES_PER_WORD_LOG2 3L +#define JS_BYTES_PER_DWORD_LOG2 3L +#define PR_WORDS_PER_DWORD_LOG2 0L +#else /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ +#error "CPU type is unknown" +#endif /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ + +#elif defined(_WIN32) || defined(XP_OS2) || defined(WINCE) + +#ifdef __WATCOMC__ +#define HAVE_VA_LIST_AS_ARRAY +#endif + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define JS_BYTES_PER_BYTE 1L +#define JS_BYTES_PER_SHORT 2L +#define JS_BYTES_PER_INT 4L +#define JS_BYTES_PER_INT64 8L +#define JS_BYTES_PER_LONG 4L +#define JS_BYTES_PER_FLOAT 4L +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 4L +#define JS_BYTES_PER_DWORD 8L + +#define JS_BITS_PER_BYTE 8L +#define JS_BITS_PER_SHORT 16L +#define JS_BITS_PER_INT 32L +#define JS_BITS_PER_INT64 64L +#define JS_BITS_PER_LONG 32L +#define JS_BITS_PER_FLOAT 32L +#define JS_BITS_PER_DOUBLE 64L +#define JS_BITS_PER_WORD 32L + +#define JS_BITS_PER_BYTE_LOG2 3L +#define JS_BITS_PER_SHORT_LOG2 4L +#define JS_BITS_PER_INT_LOG2 5L +#define JS_BITS_PER_INT64_LOG2 6L +#define JS_BITS_PER_LONG_LOG2 5L +#define JS_BITS_PER_FLOAT_LOG2 5L +#define JS_BITS_PER_DOUBLE_LOG2 6L +#define JS_BITS_PER_WORD_LOG2 5L + +#define JS_ALIGN_OF_SHORT 2L +#define JS_ALIGN_OF_INT 4L +#define JS_ALIGN_OF_LONG 4L +#define JS_ALIGN_OF_INT64 8L +#define JS_ALIGN_OF_FLOAT 4L +#define JS_ALIGN_OF_DOUBLE 4L +#define JS_ALIGN_OF_POINTER 4L +#define JS_ALIGN_OF_WORD 4L + +#define JS_BYTES_PER_WORD_LOG2 2L +#define JS_BYTES_PER_DWORD_LOG2 3L +#define PR_WORDS_PER_DWORD_LOG2 1L +#endif /* _WIN32 || XP_OS2 || WINCE*/ + +#if defined(_WINDOWS) && !defined(_WIN32) /* WIN16 */ + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define JS_BYTES_PER_BYTE 1L +#define JS_BYTES_PER_SHORT 2L +#define JS_BYTES_PER_INT 2L +#define JS_BYTES_PER_INT64 8L +#define JS_BYTES_PER_LONG 4L +#define JS_BYTES_PER_FLOAT 4L +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 4L +#define JS_BYTES_PER_DWORD 8L + +#define JS_BITS_PER_BYTE 8L +#define JS_BITS_PER_SHORT 16L +#define JS_BITS_PER_INT 16L +#define JS_BITS_PER_INT64 64L +#define JS_BITS_PER_LONG 32L +#define JS_BITS_PER_FLOAT 32L +#define JS_BITS_PER_DOUBLE 64L +#define JS_BITS_PER_WORD 32L + +#define JS_BITS_PER_BYTE_LOG2 3L +#define JS_BITS_PER_SHORT_LOG2 4L +#define JS_BITS_PER_INT_LOG2 4L +#define JS_BITS_PER_INT64_LOG2 6L +#define JS_BITS_PER_LONG_LOG2 5L +#define JS_BITS_PER_FLOAT_LOG2 5L +#define JS_BITS_PER_DOUBLE_LOG2 6L +#define JS_BITS_PER_WORD_LOG2 5L + +#define JS_ALIGN_OF_SHORT 2L +#define JS_ALIGN_OF_INT 2L +#define JS_ALIGN_OF_LONG 2L +#define JS_ALIGN_OF_INT64 2L +#define JS_ALIGN_OF_FLOAT 2L +#define JS_ALIGN_OF_DOUBLE 2L +#define JS_ALIGN_OF_POINTER 2L +#define JS_ALIGN_OF_WORD 2L + +#define JS_BYTES_PER_WORD_LOG2 2L +#define JS_BYTES_PER_DWORD_LOG2 3L +#define PR_WORDS_PER_DWORD_LOG2 1L + +#endif /* defined(_WINDOWS) && !defined(_WIN32) */ + +#if defined(__SYMBIAN32__) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +//no float support +#define Bad_float_h + +#define JS_BYTES_PER_BYTE 1L +#define JS_BYTES_PER_SHORT 2L +#define JS_BYTES_PER_INT 4L +#define JS_BYTES_PER_INT64 8L +#define JS_BYTES_PER_LONG 4L +#define JS_BYTES_PER_FLOAT 4L +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 4L +#define JS_BYTES_PER_DWORD 8L + +#define JS_BITS_PER_BYTE 8L +#define JS_BITS_PER_SHORT 16L +#define JS_BITS_PER_INT 32L +#define JS_BITS_PER_INT64 64L +#define JS_BITS_PER_LONG 32L +#define JS_BITS_PER_FLOAT 32L +#define JS_BITS_PER_DOUBLE 64L +#define JS_BITS_PER_WORD 32L + +#define JS_BITS_PER_BYTE_LOG2 3L +#define JS_BITS_PER_SHORT_LOG2 4L +#define JS_BITS_PER_INT_LOG2 5L +#define JS_BITS_PER_INT64_LOG2 6L +#define JS_BITS_PER_LONG_LOG2 5L +#define JS_BITS_PER_FLOAT_LOG2 5L +#define JS_BITS_PER_DOUBLE_LOG2 6L +#define JS_BITS_PER_WORD_LOG2 5L + +#define JS_ALIGN_OF_SHORT 2L +#define JS_ALIGN_OF_INT 4L +#define JS_ALIGN_OF_LONG 4L +#define JS_ALIGN_OF_INT64 8L +#define JS_ALIGN_OF_FLOAT 4L +#define JS_ALIGN_OF_DOUBLE 4L +#define JS_ALIGN_OF_POINTER 4L +#define JS_ALIGN_OF_WORD 4L + +#define JS_BYTES_PER_WORD_LOG2 2L +#define JS_BYTES_PER_DWORD_LOG2 3L +#define PR_WORDS_PER_DWORD_LOG2 1L + +#endif /* __SYMBIAN32__*/ + + +#elif defined(XP_UNIX) || defined(XP_BEOS) + +#error "This file is supposed to be auto-generated on UNIX platforms, but the" +#error "static version for Mac and Windows platforms is being used." +#error "Something's probably wrong with paths/headers/dependencies/Makefiles." + +#else + +#error "Must define one of XP_BEOS, XP_OS2, XP_WIN, or XP_UNIX" + +#endif + +#ifndef JS_STACK_GROWTH_DIRECTION +#define JS_STACK_GROWTH_DIRECTION (-1) +#endif + +#endif /* js_cpucfg___ */ diff --git a/extra_lib/include/js/jsdbgapi.h b/extra_lib/include/js/jsdbgapi.h new file mode 100644 index 0000000..d2e1f1c --- /dev/null +++ b/extra_lib/include/js/jsdbgapi.h @@ -0,0 +1,406 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsdbgapi_h___ +#define jsdbgapi_h___ +/* + * JS debugger API. + */ +#include "jsapi.h" +#include "jsopcode.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +extern void +js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op); + +extern JS_PUBLIC_API(JSBool) +JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, + JSTrapHandler handler, void *closure); + +extern JS_PUBLIC_API(JSOp) +JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc); + +extern JS_PUBLIC_API(void) +JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, + JSTrapHandler *handlerp, void **closurep); + +extern JS_PUBLIC_API(void) +JS_ClearScriptTraps(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(void) +JS_ClearAllTraps(JSContext *cx); + +extern JS_PUBLIC_API(JSTrapStatus) +JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep); + +/************************************************************************/ + +extern JS_PUBLIC_API(JSBool) +JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, + JSWatchPointHandler handler, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, + JSWatchPointHandler *handlerp, void **closurep); + +extern JS_PUBLIC_API(JSBool) +JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_ClearAllWatchPoints(JSContext *cx); + +#ifdef JS_HAS_OBJ_WATCHPOINT +/* + * Hide these non-API function prototypes by testing whether the internal + * header file "jsconfig.h" has been included. + */ +extern void +js_MarkWatchPoints(JSContext *cx); + +extern JSScopeProperty * +js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id); + +extern JSPropertyOp +js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, + const JSScopeProperty *sprop); + +extern JSBool JS_DLL_CALLBACK +js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool JS_DLL_CALLBACK +js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JSPropertyOp +js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter); + +#endif /* JS_HAS_OBJ_WATCHPOINT */ + +/************************************************************************/ + +extern JS_PUBLIC_API(uintN) +JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); + +extern JS_PUBLIC_API(jsbytecode *) +JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_GetFunctionScript(JSContext *cx, JSFunction *fun); + +extern JS_PUBLIC_API(JSNative) +JS_GetFunctionNative(JSContext *cx, JSFunction *fun); + +extern JS_PUBLIC_API(JSPrincipals *) +JS_GetScriptPrincipals(JSContext *cx, JSScript *script); + +/* + * Stack Frame Iterator + * + * Used to iterate through the JS stack frames to extract + * information from the frames. + */ + +extern JS_PUBLIC_API(JSStackFrame *) +JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp); + +extern JS_PUBLIC_API(JSScript *) +JS_GetFrameScript(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(jsbytecode *) +JS_GetFramePC(JSContext *cx, JSStackFrame *fp); + +/* + * Get the closest scripted frame below fp. If fp is null, start from cx->fp. + */ +extern JS_PUBLIC_API(JSStackFrame *) +JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp); + +/* + * Return a weak reference to fp's principals. A null return does not denote + * an error, it means there are no principals. + */ +extern JS_PUBLIC_API(JSPrincipals *) +JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp); + +/* + * This API is like JS_StackFramePrincipals(cx, caller), except that if + * cx->runtime->findObjectPrincipals is non-null, it returns the weaker of + * the caller's principals and the object principals of fp's callee function + * object (fp->argv[-2]), which is eval, Function, or a similar eval-like + * method. The caller parameter should be JS_GetScriptedCaller(cx, fp). + * + * All eval-like methods must use JS_EvalFramePrincipals to acquire a weak + * reference to the correct principals for the eval call to be secure, given + * an embedding that calls JS_SetObjectPrincipalsFinder (see jsapi.h). + */ +extern JS_PUBLIC_API(JSPrincipals *) +JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller); + +extern JS_PUBLIC_API(void *) +JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(void) +JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation); + +extern JS_PUBLIC_API(void *) +JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSBool) +JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp); + +/* this is deprecated, use JS_GetFrameScopeChain instead */ +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameObject(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameThis(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSFunction *) +JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp); + +/* XXXrginda Initially published with typo */ +#define JS_IsContructorFrame JS_IsConstructorFrame +extern JS_PUBLIC_API(JSBool) +JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSBool) +JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(jsval) +JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(void) +JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval); + +/** + * Return fp's callee function object (fp->argv[-2]) if it has one. + */ +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp); + +/************************************************************************/ + +extern JS_PUBLIC_API(const char *) +JS_GetScriptFilename(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(uintN) +JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(uintN) +JS_GetScriptLineExtent(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(JSVersion) +JS_GetScriptVersion(JSContext *cx, JSScript *script); + +/************************************************************************/ + +/* + * Hook setters for script creation and destruction, see jsprvtd.h for the + * typedefs. These macros provide binary compatibility and newer, shorter + * synonyms. + */ +#define JS_SetNewScriptHook JS_SetNewScriptHookProc +#define JS_SetDestroyScriptHook JS_SetDestroyScriptHookProc + +extern JS_PUBLIC_API(void) +JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata); + +extern JS_PUBLIC_API(void) +JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, + void *callerdata); + +/************************************************************************/ + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +/************************************************************************/ + +typedef struct JSPropertyDesc { + jsval id; /* primary id, a string or int */ + jsval value; /* property value */ + uint8 flags; /* flags, see below */ + uint8 spare; /* unused */ + uint16 slot; /* argument/variable slot */ + jsval alias; /* alias id if JSPD_ALIAS flag */ +} JSPropertyDesc; + +#define JSPD_ENUMERATE 0x01 /* visible to for/in loop */ +#define JSPD_READONLY 0x02 /* assignment is error */ +#define JSPD_PERMANENT 0x04 /* property cannot be deleted */ +#define JSPD_ALIAS 0x08 /* property has an alias id */ +#define JSPD_ARGUMENT 0x10 /* argument to function */ +#define JSPD_VARIABLE 0x20 /* local variable in function */ +#define JSPD_EXCEPTION 0x40 /* exception occurred fetching the property, */ + /* value is exception */ +#define JSPD_ERROR 0x80 /* native getter returned JS_FALSE without */ + /* throwing an exception */ + +typedef struct JSPropertyDescArray { + uint32 length; /* number of elements in array */ + JSPropertyDesc *array; /* alloc'd by Get, freed by Put */ +} JSPropertyDescArray; + +extern JS_PUBLIC_API(JSScopeProperty *) +JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp); + +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, + JSPropertyDesc *pd); + +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda); + +extern JS_PUBLIC_API(void) +JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda); + +/************************************************************************/ + +extern JS_PUBLIC_API(JSBool) +JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure); + +/************************************************************************/ + +extern JS_PUBLIC_API(size_t) +JS_GetObjectTotalSize(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(size_t) +JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun); + +extern JS_PUBLIC_API(size_t) +JS_GetScriptTotalSize(JSContext *cx, JSScript *script); + +/* + * Get the top-most running script on cx starting from fp, or from the top of + * cx's frame stack if fp is null, and return its script filename flags. If + * the script has a null filename member, return JSFILENAME_NULL. + */ +extern JS_PUBLIC_API(uint32) +JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp); + +/* + * Get the script filename flags for the script. If the script doesn't have a + * filename, return JSFILENAME_NULL. + */ +extern JS_PUBLIC_API(uint32) +JS_GetScriptFilenameFlags(JSScript *script); + +/* + * Associate flags with a script filename prefix in rt, so that any subsequent + * script compilation will inherit those flags if the script's filename is the + * same as prefix, or if prefix is a substring of the script's filename. + * + * The API defines only one flag bit, JSFILENAME_SYSTEM, leaving the remaining + * 31 bits up to the API client to define. The union of all 32 bits must not + * be a legal combination, however, in order to preserve JSFILENAME_NULL as a + * unique value. API clients may depend on JSFILENAME_SYSTEM being a set bit + * in JSFILENAME_NULL -- a script with a null filename member is presumed to + * be a "system" script. + */ +extern JS_PUBLIC_API(JSBool) +JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags); + +#define JSFILENAME_NULL 0xffffffff /* null script filename */ +#define JSFILENAME_SYSTEM 0x00000001 /* "system" script, see below */ + +/* + * Return true if obj is a "system" object, that is, one flagged by a prior + * call to JS_FlagSystemObject(cx, obj). What "system" means is up to the API + * client, but it can be used to coordinate access control policies based on + * script filenames and their prefixes, using JS_FlagScriptFilenamePrefix and + * JS_GetTopScriptFilenameFlags. + */ +extern JS_PUBLIC_API(JSBool) +JS_IsSystemObject(JSContext *cx, JSObject *obj); + +/* + * Flag obj as a "system" object. The API client can flag system objects to + * optimize access control checks. The engine stores but does not interpret + * the per-object flag set by this call. + */ +extern JS_PUBLIC_API(void) +JS_FlagSystemObject(JSContext *cx, JSObject *obj); + +JS_END_EXTERN_C + +#endif /* jsdbgapi_h___ */ diff --git a/extra_lib/include/js/jslong.h b/extra_lib/include/js/jslong.h new file mode 100644 index 0000000..059cf00 --- /dev/null +++ b/extra_lib/include/js/jslong.h @@ -0,0 +1,437 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* +** File: jslong.h +** Description: Portable access to 64 bit numerics +** +** Long-long (64-bit signed integer type) support. Some C compilers +** don't support 64 bit integers yet, so we use these macros to +** support both machines that do and don't. +**/ +#ifndef jslong_h___ +#define jslong_h___ + +#include "jstypes.h" + +JS_BEGIN_EXTERN_C + +/*********************************************************************** +** DEFINES: JSLL_MaxInt +** JSLL_MinInt +** JSLL_Zero +** DESCRIPTION: +** Various interesting constants and static variable +** initializer +***********************************************************************/ +#ifdef HAVE_WATCOM_BUG_2 +JSInt64 __pascal __loadds __export + JSLL_MaxInt(void); +JSInt64 __pascal __loadds __export + JSLL_MinInt(void); +JSInt64 __pascal __loadds __export + JSLL_Zero(void); +#else +extern JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void); +extern JS_PUBLIC_API(JSInt64) JSLL_MinInt(void); +extern JS_PUBLIC_API(JSInt64) JSLL_Zero(void); +#endif + +#define JSLL_MAXINT JSLL_MaxInt() +#define JSLL_MININT JSLL_MinInt() +#define JSLL_ZERO JSLL_Zero() + +#ifdef JS_HAVE_LONG_LONG + +#if JS_BYTES_PER_LONG == 8 +#define JSLL_INIT(hi, lo) ((hi ## L << 32) + lo ## L) +#elif (defined(WIN32) || defined(WIN16)) && !defined(__GNUC__) +#define JSLL_INIT(hi, lo) ((hi ## i64 << 32) + lo ## i64) +#else +#define JSLL_INIT(hi, lo) ((hi ## LL << 32) + lo ## LL) +#endif + +/*********************************************************************** +** MACROS: JSLL_* +** DESCRIPTION: +** The following macros define portable access to the 64 bit +** math facilities. +** +***********************************************************************/ + +/*********************************************************************** +** MACROS: JSLL_<relational operators> +** +** JSLL_IS_ZERO Test for zero +** JSLL_EQ Test for equality +** JSLL_NE Test for inequality +** JSLL_GE_ZERO Test for zero or positive +** JSLL_CMP Compare two values +***********************************************************************/ +#define JSLL_IS_ZERO(a) ((a) == 0) +#define JSLL_EQ(a, b) ((a) == (b)) +#define JSLL_NE(a, b) ((a) != (b)) +#define JSLL_GE_ZERO(a) ((a) >= 0) +#define JSLL_CMP(a, op, b) ((JSInt64)(a) op (JSInt64)(b)) +#define JSLL_UCMP(a, op, b) ((JSUint64)(a) op (JSUint64)(b)) + +/*********************************************************************** +** MACROS: JSLL_<logical operators> +** +** JSLL_AND Logical and +** JSLL_OR Logical or +** JSLL_XOR Logical exclusion +** JSLL_OR2 A disgusting deviation +** JSLL_NOT Negation (one's compliment) +***********************************************************************/ +#define JSLL_AND(r, a, b) ((r) = (a) & (b)) +#define JSLL_OR(r, a, b) ((r) = (a) | (b)) +#define JSLL_XOR(r, a, b) ((r) = (a) ^ (b)) +#define JSLL_OR2(r, a) ((r) = (r) | (a)) +#define JSLL_NOT(r, a) ((r) = ~(a)) + +/*********************************************************************** +** MACROS: JSLL_<mathematical operators> +** +** JSLL_NEG Negation (two's compliment) +** JSLL_ADD Summation (two's compliment) +** JSLL_SUB Difference (two's compliment) +***********************************************************************/ +#define JSLL_NEG(r, a) ((r) = -(a)) +#define JSLL_ADD(r, a, b) ((r) = (a) + (b)) +#define JSLL_SUB(r, a, b) ((r) = (a) - (b)) + +/*********************************************************************** +** MACROS: JSLL_<mathematical operators> +** +** JSLL_MUL Product (two's compliment) +** JSLL_DIV Quotient (two's compliment) +** JSLL_MOD Modulus (two's compliment) +***********************************************************************/ +#define JSLL_MUL(r, a, b) ((r) = (a) * (b)) +#define JSLL_DIV(r, a, b) ((r) = (a) / (b)) +#define JSLL_MOD(r, a, b) ((r) = (a) % (b)) + +/*********************************************************************** +** MACROS: JSLL_<shifting operators> +** +** JSLL_SHL Shift left [0..64] bits +** JSLL_SHR Shift right [0..64] bits with sign extension +** JSLL_USHR Unsigned shift right [0..64] bits +** JSLL_ISHL Signed shift left [0..64] bits +***********************************************************************/ +#define JSLL_SHL(r, a, b) ((r) = (JSInt64)(a) << (b)) +#define JSLL_SHR(r, a, b) ((r) = (JSInt64)(a) >> (b)) +#define JSLL_USHR(r, a, b) ((r) = (JSUint64)(a) >> (b)) +#define JSLL_ISHL(r, a, b) ((r) = (JSInt64)(a) << (b)) + +/*********************************************************************** +** MACROS: JSLL_<conversion operators> +** +** JSLL_L2I Convert to signed 32 bit +** JSLL_L2UI Convert to unsigned 32 bit +** JSLL_L2F Convert to floating point +** JSLL_L2D Convert to floating point +** JSLL_I2L Convert signed to 64 bit +** JSLL_UI2L Convert unsigned to 64 bit +** JSLL_F2L Convert float to 64 bit +** JSLL_D2L Convert float to 64 bit +***********************************************************************/ +#define JSLL_L2I(i, l) ((i) = (JSInt32)(l)) +#define JSLL_L2UI(ui, l) ((ui) = (JSUint32)(l)) +#define JSLL_L2F(f, l) ((f) = (JSFloat64)(l)) +#define JSLL_L2D(d, l) ((d) = (JSFloat64)(l)) + +#define JSLL_I2L(l, i) ((l) = (JSInt64)(i)) +#define JSLL_UI2L(l, ui) ((l) = (JSInt64)(ui)) +#define JSLL_F2L(l, f) ((l) = (JSInt64)(f)) +#define JSLL_D2L(l, d) ((l) = (JSInt64)(d)) + +/*********************************************************************** +** MACROS: JSLL_UDIVMOD +** DESCRIPTION: +** Produce both a quotient and a remainder given an unsigned +** INPUTS: JSUint64 a: The dividend of the operation +** JSUint64 b: The quotient of the operation +** OUTPUTS: JSUint64 *qp: pointer to quotient +** JSUint64 *rp: pointer to remainder +***********************************************************************/ +#define JSLL_UDIVMOD(qp, rp, a, b) \ + (*(qp) = ((JSUint64)(a) / (b)), \ + *(rp) = ((JSUint64)(a) % (b))) + +#else /* !JS_HAVE_LONG_LONG */ + +#ifdef IS_LITTLE_ENDIAN +#define JSLL_INIT(hi, lo) {JS_INT32(lo), JS_INT32(hi)} +#else +#define JSLL_INIT(hi, lo) {JS_INT32(hi), JS_INT32(lo)} +#endif + +#define JSLL_IS_ZERO(a) (((a).hi == 0) && ((a).lo == 0)) +#define JSLL_EQ(a, b) (((a).hi == (b).hi) && ((a).lo == (b).lo)) +#define JSLL_NE(a, b) (((a).hi != (b).hi) || ((a).lo != (b).lo)) +#define JSLL_GE_ZERO(a) (((a).hi >> 31) == 0) + +#ifdef DEBUG +#define JSLL_CMP(a, op, b) (JS_ASSERT((#op)[1] != '='), JSLL_REAL_CMP(a, op, b)) +#define JSLL_UCMP(a, op, b) (JS_ASSERT((#op)[1] != '='), JSLL_REAL_UCMP(a, op, b)) +#else +#define JSLL_CMP(a, op, b) JSLL_REAL_CMP(a, op, b) +#define JSLL_UCMP(a, op, b) JSLL_REAL_UCMP(a, op, b) +#endif + +#define JSLL_REAL_CMP(a,op,b) (((JSInt32)(a).hi op (JSInt32)(b).hi) || \ + (((a).hi == (b).hi) && ((a).lo op (b).lo))) +#define JSLL_REAL_UCMP(a,op,b) (((a).hi op (b).hi) || \ + (((a).hi == (b).hi) && ((a).lo op (b).lo))) + +#define JSLL_AND(r, a, b) ((r).lo = (a).lo & (b).lo, \ + (r).hi = (a).hi & (b).hi) +#define JSLL_OR(r, a, b) ((r).lo = (a).lo | (b).lo, \ + (r).hi = (a).hi | (b).hi) +#define JSLL_XOR(r, a, b) ((r).lo = (a).lo ^ (b).lo, \ + (r).hi = (a).hi ^ (b).hi) +#define JSLL_OR2(r, a) ((r).lo = (r).lo | (a).lo, \ + (r).hi = (r).hi | (a).hi) +#define JSLL_NOT(r, a) ((r).lo = ~(a).lo, \ + (r).hi = ~(a).hi) + +#define JSLL_NEG(r, a) ((r).lo = -(JSInt32)(a).lo, \ + (r).hi = -(JSInt32)(a).hi - ((r).lo != 0)) +#define JSLL_ADD(r, a, b) { \ + JSInt64 _a, _b; \ + _a = a; _b = b; \ + (r).lo = _a.lo + _b.lo; \ + (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo); \ +} + +#define JSLL_SUB(r, a, b) { \ + JSInt64 _a, _b; \ + _a = a; _b = b; \ + (r).lo = _a.lo - _b.lo; \ + (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo); \ +} + +#define JSLL_MUL(r, a, b) { \ + JSInt64 _a, _b; \ + _a = a; _b = b; \ + JSLL_MUL32(r, _a.lo, _b.lo); \ + (r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \ +} + +#define jslo16(a) ((a) & JS_BITMASK(16)) +#define jshi16(a) ((a) >> 16) + +#define JSLL_MUL32(r, a, b) { \ + JSUint32 _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3; \ + _a1 = jshi16(a), _a0 = jslo16(a); \ + _b1 = jshi16(b), _b0 = jslo16(b); \ + _y0 = _a0 * _b0; \ + _y1 = _a0 * _b1; \ + _y2 = _a1 * _b0; \ + _y3 = _a1 * _b1; \ + _y1 += jshi16(_y0); /* can't carry */ \ + _y1 += _y2; /* might carry */ \ + if (_y1 < _y2) \ + _y3 += (JSUint32)(JS_BIT(16)); /* propagate */ \ + (r).lo = (jslo16(_y1) << 16) + jslo16(_y0); \ + (r).hi = _y3 + jshi16(_y1); \ +} + +#define JSLL_UDIVMOD(qp, rp, a, b) jsll_udivmod(qp, rp, a, b) + +extern JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b); + +#define JSLL_DIV(r, a, b) { \ + JSInt64 _a, _b; \ + JSUint32 _negative = (JSInt32)(a).hi < 0; \ + if (_negative) { \ + JSLL_NEG(_a, a); \ + } else { \ + _a = a; \ + } \ + if ((JSInt32)(b).hi < 0) { \ + _negative ^= 1; \ + JSLL_NEG(_b, b); \ + } else { \ + _b = b; \ + } \ + JSLL_UDIVMOD(&(r), 0, _a, _b); \ + if (_negative) \ + JSLL_NEG(r, r); \ +} + +#define JSLL_MOD(r, a, b) { \ + JSInt64 _a, _b; \ + JSUint32 _negative = (JSInt32)(a).hi < 0; \ + if (_negative) { \ + JSLL_NEG(_a, a); \ + } else { \ + _a = a; \ + } \ + if ((JSInt32)(b).hi < 0) { \ + JSLL_NEG(_b, b); \ + } else { \ + _b = b; \ + } \ + JSLL_UDIVMOD(0, &(r), _a, _b); \ + if (_negative) \ + JSLL_NEG(r, r); \ +} + +#define JSLL_SHL(r, a, b) { \ + if (b) { \ + JSInt64 _a; \ + _a = a; \ + if ((b) < 32) { \ + (r).lo = _a.lo << ((b) & 31); \ + (r).hi = (_a.hi << ((b) & 31)) | (_a.lo >> (32 - (b))); \ + } else { \ + (r).lo = 0; \ + (r).hi = _a.lo << ((b) & 31); \ + } \ + } else { \ + (r) = (a); \ + } \ +} + +/* a is an JSInt32, b is JSInt32, r is JSInt64 */ +#define JSLL_ISHL(r, a, b) { \ + if (b) { \ + JSInt64 _a; \ + _a.lo = (a); \ + _a.hi = 0; \ + if ((b) < 32) { \ + (r).lo = (a) << ((b) & 31); \ + (r).hi = ((a) >> (32 - (b))); \ + } else { \ + (r).lo = 0; \ + (r).hi = (a) << ((b) & 31); \ + } \ + } else { \ + (r).lo = (a); \ + (r).hi = 0; \ + } \ +} + +#define JSLL_SHR(r, a, b) { \ + if (b) { \ + JSInt64 _a; \ + _a = a; \ + if ((b) < 32) { \ + (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ + (r).hi = (JSInt32)_a.hi >> ((b) & 31); \ + } else { \ + (r).lo = (JSInt32)_a.hi >> ((b) & 31); \ + (r).hi = (JSInt32)_a.hi >> 31; \ + } \ + } else { \ + (r) = (a); \ + } \ +} + +#define JSLL_USHR(r, a, b) { \ + if (b) { \ + JSInt64 _a; \ + _a = a; \ + if ((b) < 32) { \ + (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ + (r).hi = _a.hi >> ((b) & 31); \ + } else { \ + (r).lo = _a.hi >> ((b) & 31); \ + (r).hi = 0; \ + } \ + } else { \ + (r) = (a); \ + } \ +} + +#define JSLL_L2I(i, l) ((i) = (l).lo) +#define JSLL_L2UI(ui, l) ((ui) = (l).lo) +#define JSLL_L2F(f, l) { double _d; JSLL_L2D(_d, l); (f) = (JSFloat64)_d; } + +#define JSLL_L2D(d, l) { \ + int _negative; \ + JSInt64 _absval; \ + \ + _negative = (l).hi >> 31; \ + if (_negative) { \ + JSLL_NEG(_absval, l); \ + } else { \ + _absval = l; \ + } \ + (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo; \ + if (_negative) \ + (d) = -(d); \ +} + +#define JSLL_I2L(l, i) { JSInt32 _i = (i) >> 31; (l).lo = (i); (l).hi = _i; } +#define JSLL_UI2L(l, ui) ((l).lo = (ui), (l).hi = 0) +#define JSLL_F2L(l, f) { double _d = (double)f; JSLL_D2L(l, _d); } + +#define JSLL_D2L(l, d) { \ + int _negative; \ + double _absval, _d_hi; \ + JSInt64 _lo_d; \ + \ + _negative = ((d) < 0); \ + _absval = _negative ? -(d) : (d); \ + \ + (l).hi = _absval / 4.294967296e9; \ + (l).lo = 0; \ + JSLL_L2D(_d_hi, l); \ + _absval -= _d_hi; \ + _lo_d.hi = 0; \ + if (_absval < 0) { \ + _lo_d.lo = -_absval; \ + JSLL_SUB(l, l, _lo_d); \ + } else { \ + _lo_d.lo = _absval; \ + JSLL_ADD(l, l, _lo_d); \ + } \ + \ + if (_negative) \ + JSLL_NEG(l, l); \ +} + +#endif /* !JS_HAVE_LONG_LONG */ + +JS_END_EXTERN_C + +#endif /* jslong_h___ */ diff --git a/extra_lib/include/js/jsosdep.h b/extra_lib/include/js/jsosdep.h new file mode 100644 index 0000000..96841c2 --- /dev/null +++ b/extra_lib/include/js/jsosdep.h @@ -0,0 +1,115 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsosdep_h___ +#define jsosdep_h___ +/* + * OS (and machine, and compiler XXX) dependent information. + */ + +#if defined(XP_WIN) || defined(XP_OS2) + +#if defined(_WIN32) || defined (XP_OS2) || defined (__SYMBIAN32__) +#define JS_HAVE_LONG_LONG +#else +#undef JS_HAVE_LONG_LONG +#endif +#endif /* XP_WIN || XP_OS2 */ + +#ifdef XP_BEOS +#define JS_HAVE_LONG_LONG +#endif + + +#ifdef XP_UNIX + +/* + * Get OS specific header information. + */ +#if defined(XP_MACOSX) || defined(DARWIN) +#define JS_HAVE_LONG_LONG + +#elif defined(AIXV3) || defined(AIX) +#define JS_HAVE_LONG_LONG + +#elif defined(BSDI) +#define JS_HAVE_LONG_LONG + +#elif defined(HPUX) +#define JS_HAVE_LONG_LONG + +#elif defined(IRIX) +#define JS_HAVE_LONG_LONG + +#elif defined(linux) +#define JS_HAVE_LONG_LONG + +#elif defined(OSF1) +#define JS_HAVE_LONG_LONG + +#elif defined(_SCO_DS) +#undef JS_HAVE_LONG_LONG + +#elif defined(SOLARIS) +#define JS_HAVE_LONG_LONG + +#elif defined(FREEBSD) +#define JS_HAVE_LONG_LONG + +#elif defined(SUNOS4) +#undef JS_HAVE_LONG_LONG + +/* +** Missing function prototypes +*/ + +extern void *sbrk(int); + +#elif defined(UNIXWARE) +#undef JS_HAVE_LONG_LONG + +#elif defined(VMS) && defined(__ALPHA) +#define JS_HAVE_LONG_LONG + +#endif + +#endif /* XP_UNIX */ + +#endif /* jsosdep_h___ */ + diff --git a/extra_lib/include/js/jsotypes.h b/extra_lib/include/js/jsotypes.h new file mode 100644 index 0000000..38d7286 --- /dev/null +++ b/extra_lib/include/js/jsotypes.h @@ -0,0 +1,202 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * This section typedefs the old 'native' types to the new PR<type>s. + * These definitions are scheduled to be eliminated at the earliest + * possible time. The NSPR API is implemented and documented using + * the new definitions. + */ + +/* + * Note that we test for PROTYPES_H, not JSOTYPES_H. This is to avoid + * double-definitions of scalar types such as uint32, if NSPR's + * protypes.h is also included. + */ +#ifndef PROTYPES_H +#define PROTYPES_H + +#ifdef XP_BEOS +/* BeOS defines most int types in SupportDefs.h (int8, uint8, int16, + * uint16, int32, uint32, int64, uint64), so in the interest of + * not conflicting with other definitions elsewhere we have to skip the + * #ifdef jungle below, duplicate some definitions, and do our stuff. + */ +#include <SupportDefs.h> + +typedef JSUintn uintn; +#ifndef _XP_Core_ +typedef JSIntn intn; +#endif + +#else + +/* SVR4 typedef of uint is commonly found on UNIX machines. */ +#if defined(XP_UNIX) && !defined(__QNXNTO__) +#include <sys/types.h> +#else +typedef JSUintn uint; +#endif + +typedef JSUintn uintn; +typedef JSUint64 uint64; +#if !defined(_WIN32) && !defined(XP_OS2) +typedef JSUint32 uint32; +#else +typedef unsigned long uint32; +#endif +typedef JSUint16 uint16; +typedef JSUint8 uint8; + +#ifndef _XP_Core_ +typedef JSIntn intn; +#endif + +/* + * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very + * common header file) defines the types int8, int16, int32, and int64. + * So we don't define these four types here to avoid conflicts in case + * the code also includes sys/types.h. + */ +#if defined(AIX) && defined(HAVE_SYS_INTTYPES_H) +#include <sys/inttypes.h> +#else +typedef JSInt64 int64; + +/* /usr/include/model.h on HP-UX defines int8, int16, and int32 */ +#ifdef HPUX +#include <model.h> +#else +#if !defined(_WIN32) && !defined(XP_OS2) +typedef JSInt32 int32; +#else +typedef long int32; +#endif +typedef JSInt16 int16; +typedef JSInt8 int8; +#endif /* HPUX */ +#endif /* AIX && HAVE_SYS_INTTYPES_H */ + +#endif /* XP_BEOS */ + +typedef JSFloat64 float64; + +/* Re: jsbit.h */ +#define TEST_BIT JS_TEST_BIT +#define SET_BIT JS_SET_BIT +#define CLEAR_BIT JS_CLEAR_BIT + +/* Re: prarena.h->plarena.h */ +#define PRArena PLArena +#define PRArenaPool PLArenaPool +#define PRArenaStats PLArenaStats +#define PR_ARENA_ALIGN PL_ARENA_ALIGN +#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL +#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE +#define PR_ARENA_GROW PL_ARENA_GROW +#define PR_ARENA_MARK PL_ARENA_MARK +#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED +#define PR_CLEAR_ARENA PL_CLEAR_ARENA +#define PR_ARENA_RELEASE PL_ARENA_RELEASE +#define PR_COUNT_ARENA PL_COUNT_ARENA +#define PR_ARENA_DESTROY PL_ARENA_DESTROY +#define PR_InitArenaPool PL_InitArenaPool +#define PR_FreeArenaPool PL_FreeArenaPool +#define PR_FinishArenaPool PL_FinishArenaPool +#define PR_CompactArenaPool PL_CompactArenaPool +#define PR_ArenaFinish PL_ArenaFinish +#define PR_ArenaAllocate PL_ArenaAllocate +#define PR_ArenaGrow PL_ArenaGrow +#define PR_ArenaRelease PL_ArenaRelease +#define PR_ArenaCountAllocation PL_ArenaCountAllocation +#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth +#define PR_ArenaCountGrowth PL_ArenaCountGrowth +#define PR_ArenaCountRelease PL_ArenaCountRelease +#define PR_ArenaCountRetract PL_ArenaCountRetract + +/* Re: prevent.h->plevent.h */ +#define PREvent PLEvent +#define PREventQueue PLEventQueue +#define PR_CreateEventQueue PL_CreateEventQueue +#define PR_DestroyEventQueue PL_DestroyEventQueue +#define PR_GetEventQueueMonitor PL_GetEventQueueMonitor +#define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR +#define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR +#define PR_PostEvent PL_PostEvent +#define PR_PostSynchronousEvent PL_PostSynchronousEvent +#define PR_GetEvent PL_GetEvent +#define PR_EventAvailable PL_EventAvailable +#define PREventFunProc PLEventFunProc +#define PR_MapEvents PL_MapEvents +#define PR_RevokeEvents PL_RevokeEvents +#define PR_ProcessPendingEvents PL_ProcessPendingEvents +#define PR_WaitForEvent PL_WaitForEvent +#define PR_EventLoop PL_EventLoop +#define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD +#define PRHandleEventProc PLHandleEventProc +#define PRDestroyEventProc PLDestroyEventProc +#define PR_InitEvent PL_InitEvent +#define PR_GetEventOwner PL_GetEventOwner +#define PR_HandleEvent PL_HandleEvent +#define PR_DestroyEvent PL_DestroyEvent +#define PR_DequeueEvent PL_DequeueEvent +#define PR_GetMainEventQueue PL_GetMainEventQueue + +/* Re: prhash.h->plhash.h */ +#define PRHashEntry PLHashEntry +#define PRHashTable PLHashTable +#define PRHashNumber PLHashNumber +#define PRHashFunction PLHashFunction +#define PRHashComparator PLHashComparator +#define PRHashEnumerator PLHashEnumerator +#define PRHashAllocOps PLHashAllocOps +#define PR_NewHashTable PL_NewHashTable +#define PR_HashTableDestroy PL_HashTableDestroy +#define PR_HashTableRawLookup PL_HashTableRawLookup +#define PR_HashTableRawAdd PL_HashTableRawAdd +#define PR_HashTableRawRemove PL_HashTableRawRemove +#define PR_HashTableAdd PL_HashTableAdd +#define PR_HashTableRemove PL_HashTableRemove +#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries +#define PR_HashTableLookup PL_HashTableLookup +#define PR_HashTableDump PL_HashTableDump +#define PR_HashString PL_HashString +#define PR_CompareStrings PL_CompareStrings +#define PR_CompareValues PL_CompareValues + +#endif /* !defined(PROTYPES_H) */ diff --git a/extra_lib/include/js/jsproto.tbl b/extra_lib/include/js/jsproto.tbl new file mode 100644 index 0000000..1b306c7 --- /dev/null +++ b/extra_lib/include/js/jsproto.tbl @@ -0,0 +1,116 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set sw=4 ts=8 et tw=80 ft=c: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey 1.7 work in progress, released + * February 14, 2006. + * + * The Initial Developer of the Original Code is + * Brendan Eich <brendan@mozilla.org> + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +//#include "jsconfig.h" + +#if JS_HAS_SCRIPT_OBJECT +# define SCRIPT_INIT js_InitScriptClass +#else +# define SCRIPT_INIT js_InitNullClass +#endif + +#if JS_HAS_XML_SUPPORT +# define XML_INIT js_InitXMLClass +# define NAMESPACE_INIT js_InitNamespaceClass +# define QNAME_INIT js_InitQNameClass +# define ANYNAME_INIT js_InitAnyNameClass +# define ATTRIBUTE_INIT js_InitAttributeNameClass +#else +# define XML_INIT js_InitNullClass +# define NAMESPACE_INIT js_InitNullClass +# define QNAME_INIT js_InitNullClass +# define ANYNAME_INIT js_InitNullClass +# define ATTRIBUTE_INIT js_InitNullClass +#endif + +#if JS_HAS_GENERATORS +# define GENERATOR_INIT js_InitIteratorClasses +#else +# define GENERATOR_INIT js_InitNullClass +#endif + +#if JS_HAS_FILE_OBJECT +# define FILE_INIT js_InitFileClass +#else +# define FILE_INIT js_InitNullClass +#endif + +/* + * Enumerator codes in the second column must not change -- they are part of + * the JS XDR API. + */ +JS_PROTO(Null, 0, js_InitNullClass) +JS_PROTO(Object, 1, js_InitFunctionAndObjectClasses) +JS_PROTO(Function, 2, js_InitFunctionAndObjectClasses) +JS_PROTO(Array, 3, js_InitArrayClass) +JS_PROTO(Boolean, 4, js_InitBooleanClass) +JS_PROTO(Call, 5, js_InitCallClass) +JS_PROTO(Date, 6, js_InitDateClass) +JS_PROTO(Math, 7, js_InitMathClass) +JS_PROTO(Number, 8, js_InitNumberClass) +JS_PROTO(String, 9, js_InitStringClass) +JS_PROTO(RegExp, 10, js_InitRegExpClass) +JS_PROTO(Script, 11, SCRIPT_INIT) +JS_PROTO(XML, 12, XML_INIT) +JS_PROTO(Namespace, 13, NAMESPACE_INIT) +JS_PROTO(QName, 14, QNAME_INIT) +JS_PROTO(AnyName, 15, ANYNAME_INIT) +JS_PROTO(AttributeName, 16, ATTRIBUTE_INIT) +JS_PROTO(Error, 17, js_InitExceptionClasses) +JS_PROTO(InternalError, 18, js_InitExceptionClasses) +JS_PROTO(EvalError, 19, js_InitExceptionClasses) +JS_PROTO(RangeError, 20, js_InitExceptionClasses) +JS_PROTO(ReferenceError, 21, js_InitExceptionClasses) +JS_PROTO(SyntaxError, 22, js_InitExceptionClasses) +JS_PROTO(TypeError, 23, js_InitExceptionClasses) +JS_PROTO(URIError, 24, js_InitExceptionClasses) +JS_PROTO(Generator, 25, GENERATOR_INIT) +JS_PROTO(Iterator, 26, js_InitIteratorClasses) +JS_PROTO(StopIteration, 27, js_InitIteratorClasses) +JS_PROTO(UnusedProto28, 28, js_InitNullClass) +JS_PROTO(File, 29, FILE_INIT) +JS_PROTO(Block, 30, js_InitBlockClass) + +#undef SCRIPT_INIT +#undef XML_INIT +#undef NAMESPACE_INIT +#undef QNAME_INIT +#undef ANYNAME_INIT +#undef ATTRIBUTE_INIT +#undef GENERATOR_INIT +#undef FILE_INIT diff --git a/extra_lib/include/js/jspubtd.h b/extra_lib/include/js/jspubtd.h new file mode 100644 index 0000000..4e8c92a --- /dev/null +++ b/extra_lib/include/js/jspubtd.h @@ -0,0 +1,667 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jspubtd_h___ +#define jspubtd_h___ +/* + * JS public API typedefs. + */ +#include "jstypes.h" +#include "jscompat.h" + +JS_BEGIN_EXTERN_C + +/* Scalar typedefs. */ +typedef uint16 jschar; +typedef int32 jsint; +typedef uint32 jsuint; +typedef float64 jsdouble; +typedef jsword jsval; +typedef jsword jsid; +typedef int32 jsrefcount; /* PRInt32 if JS_THREADSAFE, see jslock.h */ + +/* + * Run-time version enumeration. See jsconfig.h for compile-time counterparts + * to these values that may be selected by the JS_VERSION macro, and tested by + * #if expressions. + */ +typedef enum JSVersion { + JSVERSION_1_0 = 100, + JSVERSION_1_1 = 110, + JSVERSION_1_2 = 120, + JSVERSION_1_3 = 130, + JSVERSION_1_4 = 140, + JSVERSION_ECMA_3 = 148, + JSVERSION_1_5 = 150, + JSVERSION_1_6 = 160, + JSVERSION_1_7 = 170, + JSVERSION_DEFAULT = 0, + JSVERSION_UNKNOWN = -1 +} JSVersion; + +#define JSVERSION_IS_ECMA(version) \ + ((version) == JSVERSION_DEFAULT || (version) >= JSVERSION_1_3) + +/* Result of typeof operator enumeration. */ +typedef enum JSType { + JSTYPE_VOID, /* undefined */ + JSTYPE_OBJECT, /* object */ + JSTYPE_FUNCTION, /* function */ + JSTYPE_STRING, /* string */ + JSTYPE_NUMBER, /* number */ + JSTYPE_BOOLEAN, /* boolean */ + JSTYPE_NULL, /* null */ + JSTYPE_XML, /* xml object */ + JSTYPE_LIMIT +} JSType; + +/* Dense index into cached prototypes and class atoms for standard objects. */ +typedef enum JSProtoKey { +#define JS_PROTO(name,code,init) JSProto_##name = code, +#include "jsproto.tbl" +#undef JS_PROTO + JSProto_LIMIT +} JSProtoKey; + +/* JSObjectOps.checkAccess mode enumeration. */ +typedef enum JSAccessMode { + JSACC_PROTO = 0, /* XXXbe redundant w.r.t. id */ + JSACC_PARENT = 1, /* XXXbe redundant w.r.t. id */ + JSACC_IMPORT = 2, /* import foo.bar */ + JSACC_WATCH = 3, /* a watchpoint on object foo for id 'bar' */ + JSACC_READ = 4, /* a "get" of foo.bar */ + JSACC_WRITE = 8, /* a "set" of foo.bar = baz */ + JSACC_LIMIT +} JSAccessMode; + +#define JSACC_TYPEMASK (JSACC_WRITE - 1) + +/* + * This enum type is used to control the behavior of a JSObject property + * iterator function that has type JSNewEnumerate. + */ +typedef enum JSIterateOp { + JSENUMERATE_INIT, /* Create new iterator state */ + JSENUMERATE_NEXT, /* Iterate once */ + JSENUMERATE_DESTROY /* Destroy iterator state */ +} JSIterateOp; + +/* Struct typedefs. */ +typedef struct JSClass JSClass; +typedef struct JSExtendedClass JSExtendedClass; +typedef struct JSConstDoubleSpec JSConstDoubleSpec; +typedef struct JSContext JSContext; +typedef struct JSErrorReport JSErrorReport; +typedef struct JSFunction JSFunction; +typedef struct JSFunctionSpec JSFunctionSpec; +typedef struct JSIdArray JSIdArray; +typedef struct JSProperty JSProperty; +typedef struct JSPropertySpec JSPropertySpec; +typedef struct JSObject JSObject; +typedef struct JSObjectMap JSObjectMap; +typedef struct JSObjectOps JSObjectOps; +typedef struct JSXMLObjectOps JSXMLObjectOps; +typedef struct JSRuntime JSRuntime; +typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ +typedef struct JSScript JSScript; +typedef struct JSStackFrame JSStackFrame; +typedef struct JSString JSString; +typedef struct JSXDRState JSXDRState; +typedef struct JSExceptionState JSExceptionState; +typedef struct JSLocaleCallbacks JSLocaleCallbacks; + +/* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */ + +/* + * Add, delete, get or set a property named by id in obj. Note the jsval id + * type -- id may be a string (Unicode property identifier) or an int (element + * index). The *vp out parameter, on success, is the new property value after + * an add, get, or set. After a successful delete, *vp is JSVAL_FALSE iff + * obj[id] can't be deleted (because it's permanent). + */ +typedef JSBool +(* JS_DLL_CALLBACK JSPropertyOp)(JSContext *cx, JSObject *obj, jsval id, + jsval *vp); + +/* + * This function type is used for callbacks that enumerate the properties of + * a JSObject. The behavior depends on the value of enum_op: + * + * JSENUMERATE_INIT + * A new, opaque iterator state should be allocated and stored in *statep. + * (You can use PRIVATE_TO_JSVAL() to tag the pointer to be stored). + * + * The number of properties that will be enumerated should be returned as + * an integer jsval in *idp, if idp is non-null, and provided the number of + * enumerable properties is known. If idp is non-null and the number of + * enumerable properties can't be computed in advance, *idp should be set + * to JSVAL_ZERO. + * + * JSENUMERATE_NEXT + * A previously allocated opaque iterator state is passed in via statep. + * Return the next jsid in the iteration using *idp. The opaque iterator + * state pointed at by statep is destroyed and *statep is set to JSVAL_NULL + * if there are no properties left to enumerate. + * + * JSENUMERATE_DESTROY + * Destroy the opaque iterator state previously allocated in *statep by a + * call to this function when enum_op was JSENUMERATE_INIT. + * + * The return value is used to indicate success, with a value of JS_FALSE + * indicating failure. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSNewEnumerateOp)(JSContext *cx, JSObject *obj, + JSIterateOp enum_op, + jsval *statep, jsid *idp); + +/* + * The old-style JSClass.enumerate op should define all lazy properties not + * yet reflected in obj. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSEnumerateOp)(JSContext *cx, JSObject *obj); + +/* + * Resolve a lazy property named by id in obj by defining it directly in obj. + * Lazy properties are those reflected from some peer native property space + * (e.g., the DOM attributes for a given node reflected as obj) on demand. + * + * JS looks for a property in an object, and if not found, tries to resolve + * the given id. If resolve succeeds, the engine looks again in case resolve + * defined obj[id]. If no such property exists directly in obj, the process + * is repeated with obj's prototype, etc. + * + * NB: JSNewResolveOp provides a cheaper way to resolve lazy properties. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSResolveOp)(JSContext *cx, JSObject *obj, jsval id); + +/* + * Like JSResolveOp, but flags provide contextual information as follows: + * + * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id + * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment + * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence + * JSRESOLVE_DECLARING var, const, or function prolog declaration opcode + * JSRESOLVE_CLASSNAME class name used when constructing + * + * The *objp out parameter, on success, should be null to indicate that id + * was not resolved; and non-null, referring to obj or one of its prototypes, + * if id was resolved. + * + * This hook instead of JSResolveOp is called via the JSClass.resolve member + * if JSCLASS_NEW_RESOLVE is set in JSClass.flags. + * + * Setting JSCLASS_NEW_RESOLVE and JSCLASS_NEW_RESOLVE_GETS_START further + * extends this hook by passing in the starting object on the prototype chain + * via *objp. Thus a resolve hook implementation may define the property id + * being resolved in the object in which the id was first sought, rather than + * in a prototype object whose class led to the resolve hook being called. + * + * When using JSCLASS_NEW_RESOLVE_GETS_START, the resolve hook must therefore + * null *objp to signify "not resolved". With only JSCLASS_NEW_RESOLVE and no + * JSCLASS_NEW_RESOLVE_GETS_START, the hook can assume *objp is null on entry. + * This is not good practice, but enough existing hook implementations count + * on it that we can't break compatibility by passing the starting object in + * *objp without a new JSClass flag. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSNewResolveOp)(JSContext *cx, JSObject *obj, jsval id, + uintN flags, JSObject **objp); + +/* + * Convert obj to the given type, returning true with the resulting value in + * *vp on success, and returning false on error or exception. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, + jsval *vp); + +/* + * Finalize obj, which the garbage collector has determined to be unreachable + * from other live objects or from GC roots. Obviously, finalizers must never + * store a reference to obj. + */ +typedef void +(* JS_DLL_CALLBACK JSFinalizeOp)(JSContext *cx, JSObject *obj); + +/* + * Used by JS_AddExternalStringFinalizer and JS_RemoveExternalStringFinalizer + * to extend and reduce the set of string types finalized by the GC. + */ +typedef void +(* JS_DLL_CALLBACK JSStringFinalizeOp)(JSContext *cx, JSString *str); + +/* + * The signature for JSClass.getObjectOps, used by JS_NewObject's internals + * to discover the set of high-level object operations to use for new objects + * of the given class. All native objects have a JSClass, which is stored as + * a private (int-tagged) pointer in obj->slots[JSSLOT_CLASS]. In contrast, + * all native and host objects have a JSObjectMap at obj->map, which may be + * shared among a number of objects, and which contains the JSObjectOps *ops + * pointer used to dispatch object operations from API calls. + * + * Thus JSClass (which pre-dates JSObjectOps in the API) provides a low-level + * interface to class-specific code and data, while JSObjectOps allows for a + * higher level of operation, which does not use the object's class except to + * find the class's JSObjectOps struct, by calling clasp->getObjectOps, and to + * finalize the object. + * + * If this seems backwards, that's because it is! API compatibility requires + * a JSClass *clasp parameter to JS_NewObject, etc. Most host objects do not + * need to implement the larger JSObjectOps, and can share the common JSScope + * code and data used by the native (js_ObjectOps, see jsobj.c) ops. + * + * Further extension to preserve API compatibility: if this function returns + * a pointer to JSXMLObjectOps.base, not to JSObjectOps, then the engine calls + * extended hooks needed for E4X. + */ +typedef JSObjectOps * +(* JS_DLL_CALLBACK JSGetObjectOps)(JSContext *cx, JSClass *clasp); + +/* + * JSClass.checkAccess type: check whether obj[id] may be accessed per mode, + * returning false on error/exception, true on success with obj[id]'s last-got + * value in *vp, and its attributes in *attrsp. As for JSPropertyOp above, id + * is either a string or an int jsval. + * + * See JSCheckAccessIdOp, below, for the JSObjectOps counterpart, which takes + * a jsid (a tagged int or aligned, unique identifier pointer) rather than a + * jsval. The native js_ObjectOps.checkAccess simply forwards to the object's + * clasp->checkAccess, so that both JSClass and JSObjectOps implementors may + * specialize access checks. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsval id, + JSAccessMode mode, jsval *vp); + +/* + * Encode or decode an object, given an XDR state record representing external + * data. See jsxdrapi.h. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSXDRObjectOp)(JSXDRState *xdr, JSObject **objp); + +/* + * Check whether v is an instance of obj. Return false on error or exception, + * true on success with JS_TRUE in *bp if v is an instance of obj, JS_FALSE in + * *bp otherwise. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSHasInstanceOp)(JSContext *cx, JSObject *obj, jsval v, + JSBool *bp); + +/* + * Function type for JSClass.mark and JSObjectOps.mark, called from the GC to + * scan live GC-things reachable from obj's private data structure. For each + * such thing, a mark implementation must call + * + * JS_MarkGCThing(cx, thing, name, arg); + * + * The trailing name and arg parameters are used for GC_MARK_DEBUG-mode heap + * dumping and ref-path tracing. The mark function should pass a (typically + * literal) string naming the private data member for name, and it must pass + * the opaque arg parameter through from its caller. + * + * For the JSObjectOps.mark hook, the return value is the number of slots at + * obj->slots to scan. For JSClass.mark, the return value is ignored. + * + * NB: JSMarkOp implementations cannot allocate new GC-things (JS_NewObject + * called from a mark function will fail silently, e.g.). + */ +typedef uint32 +(* JS_DLL_CALLBACK JSMarkOp)(JSContext *cx, JSObject *obj, void *arg); + +/* + * The optional JSClass.reserveSlots hook allows a class to make computed + * per-instance object slots reservations, in addition to or instead of using + * JSCLASS_HAS_RESERVED_SLOTS(n) in the JSClass.flags initializer to reserve + * a constant-per-class number of slots. Implementations of this hook should + * return the number of slots to reserve, not including any reserved by using + * JSCLASS_HAS_RESERVED_SLOTS(n) in JSClass.flags. + * + * NB: called with obj locked by the JSObjectOps-specific mutual exclusion + * mechanism appropriate for obj, so don't nest other operations that might + * also lock obj. + */ +typedef uint32 +(* JS_DLL_CALLBACK JSReserveSlotsOp)(JSContext *cx, JSObject *obj); + +/* JSObjectOps function pointer typedefs. */ + +/* + * Create a new subclass of JSObjectMap (see jsobj.h), with the nrefs and ops + * members initialized from the same-named parameters, and with the nslots and + * freeslot members initialized according to ops and clasp. Return null on + * error, non-null on success. + * + * JSObjectMaps are reference-counted by generic code in the engine. Usually, + * the nrefs parameter to JSObjectOps.newObjectMap will be 1, to count the ref + * returned to the caller on success. After a successful construction, some + * number of js_HoldObjectMap and js_DropObjectMap calls ensue. When nrefs + * reaches 0 due to a js_DropObjectMap call, JSObjectOps.destroyObjectMap will + * be called to dispose of the map. + */ +typedef JSObjectMap * +(* JS_DLL_CALLBACK JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs, + JSObjectOps *ops, JSClass *clasp, + JSObject *obj); + +/* + * Generic type for an infallible JSObjectMap operation, used currently by + * JSObjectOps.destroyObjectMap. + */ +typedef void +(* JS_DLL_CALLBACK JSObjectMapOp)(JSContext *cx, JSObjectMap *map); + +/* + * Look for id in obj and its prototype chain, returning false on error or + * exception, true on success. On success, return null in *propp if id was + * not found. If id was found, return the first object searching from obj + * along its prototype chain in which id names a direct property in *objp, and + * return a non-null, opaque property pointer in *propp. + * + * If JSLookupPropOp succeeds and returns with *propp non-null, that pointer + * may be passed as the prop parameter to a JSAttributesOp, as a short-cut + * that bypasses id re-lookup. In any case, a non-null *propp result after a + * successful lookup must be dropped via JSObjectOps.dropProperty. + * + * NB: successful return with non-null *propp means the implementation may + * have locked *objp and added a reference count associated with *propp, so + * callers should not risk deadlock by nesting or interleaving other lookups + * or any obj-bearing ops before dropping *propp. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id, + JSObject **objp, JSProperty **propp); + +/* + * Define obj[id], a direct property of obj named id, having the given initial + * value, with the specified getter, setter, and attributes. If the propp out + * param is non-null, *propp on successful return contains an opaque property + * pointer usable as a speedup hint with JSAttributesOp. But note that propp + * may be null, indicating that the caller is not interested in recovering an + * opaque pointer to the newly-defined property. + * + * If propp is non-null and JSDefinePropOp succeeds, its caller must be sure + * to drop *propp using JSObjectOps.dropProperty in short order, just as with + * JSLookupPropOp. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSDefinePropOp)(JSContext *cx, JSObject *obj, + jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs, JSProperty **propp); + +/* + * Get, set, or delete obj[id], returning false on error or exception, true + * on success. If getting or setting, the new value is returned in *vp on + * success. If deleting without error, *vp will be JSVAL_FALSE if obj[id] is + * permanent, and JSVAL_TRUE if id named a direct property of obj that was in + * fact deleted, or if id names no direct property of obj (id could name a + * prototype property, or no property in obj or its prototype chain). + */ +typedef JSBool +(* JS_DLL_CALLBACK JSPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, + jsval *vp); + +/* + * Get or set attributes of the property obj[id]. Return false on error or + * exception, true with current attributes in *attrsp. If prop is non-null, + * it must come from the *propp out parameter of a prior JSDefinePropOp or + * JSLookupPropOp call. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSAttributesOp)(JSContext *cx, JSObject *obj, jsid id, + JSProperty *prop, uintN *attrsp); + +/* + * JSObjectOps.checkAccess type: check whether obj[id] may be accessed per + * mode, returning false on error/exception, true on success with obj[id]'s + * last-got value in *vp, and its attributes in *attrsp. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSCheckAccessIdOp)(JSContext *cx, JSObject *obj, jsid id, + JSAccessMode mode, jsval *vp, + uintN *attrsp); + +/* + * A generic type for functions mapping an object to another object, or null + * if an error or exception was thrown on cx. Used by JSObjectOps.thisObject + * at present. + */ +typedef JSObject * +(* JS_DLL_CALLBACK JSObjectOp)(JSContext *cx, JSObject *obj); + +/* + * A generic type for functions taking a context, object, and property, with + * no return value. Used by JSObjectOps.dropProperty currently (see above, + * JSDefinePropOp and JSLookupPropOp, for the object-locking protocol in which + * dropProperty participates). + */ +typedef void +(* JS_DLL_CALLBACK JSPropertyRefOp)(JSContext *cx, JSObject *obj, + JSProperty *prop); + +/* + * Function type for JSObjectOps.setProto and JSObjectOps.setParent. These + * hooks must check for cycles without deadlocking, and otherwise take special + * steps. See jsobj.c, js_SetProtoOrParent, for an example. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSSetObjectSlotOp)(JSContext *cx, JSObject *obj, + uint32 slot, JSObject *pobj); + +/* + * Get and set a required slot, one that should already have been allocated. + * These operations are infallible, so required slots must be pre-allocated, + * or implementations must suppress out-of-memory errors. The native ops + * (js_ObjectOps, see jsobj.c) access slots reserved by including a call to + * the JSCLASS_HAS_RESERVED_SLOTS(n) macro in the JSClass.flags initializer. + * + * NB: the slot parameter is a zero-based index into obj->slots[], unlike the + * index parameter to the JS_GetReservedSlot and JS_SetReservedSlot API entry + * points, which is a zero-based index into the JSCLASS_RESERVED_SLOTS(clasp) + * reserved slots that come after the initial well-known slots: proto, parent, + * class, and optionally, the private data slot. + */ +typedef jsval +(* JS_DLL_CALLBACK JSGetRequiredSlotOp)(JSContext *cx, JSObject *obj, + uint32 slot); + +typedef JSBool +(* JS_DLL_CALLBACK JSSetRequiredSlotOp)(JSContext *cx, JSObject *obj, + uint32 slot, jsval v); + +typedef JSObject * +(* JS_DLL_CALLBACK JSGetMethodOp)(JSContext *cx, JSObject *obj, jsid id, + jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSSetMethodOp)(JSContext *cx, JSObject *obj, jsid id, + jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSEnumerateValuesOp)(JSContext *cx, JSObject *obj, + JSIterateOp enum_op, + jsval *statep, jsid *idp, jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSEqualityOp)(JSContext *cx, JSObject *obj, jsval v, + JSBool *bp); + +typedef JSBool +(* JS_DLL_CALLBACK JSConcatenateOp)(JSContext *cx, JSObject *obj, jsval v, + jsval *vp); + +/* Typedef for native functions called by the JS VM. */ + +typedef JSBool +(* JS_DLL_CALLBACK JSNative)(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval); + +/* Callbacks and their arguments. */ + +typedef enum JSContextOp { + JSCONTEXT_NEW, + JSCONTEXT_DESTROY +} JSContextOp; + +/* + * The possible values for contextOp when the runtime calls the callback are: + * JSCONTEXT_NEW JS_NewContext succesfully created a new JSContext + * instance. The callback can initialize the instance as + * required. If the callback returns false, the instance + * will be destroyed and JS_NewContext returns null. In + * this case the callback is not called again. + * JSCONTEXT_DESTROY One of JS_DestroyContext* methods is called. The + * callback may perform its own cleanup and must always + * return true. + * Any other value For future compatibility the callback must do nothing + * and return true in this case. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSContextCallback)(JSContext *cx, uintN contextOp); + +typedef enum JSGCStatus { + JSGC_BEGIN, + JSGC_END, + JSGC_MARK_END, + JSGC_FINALIZE_END +} JSGCStatus; + +typedef JSBool +(* JS_DLL_CALLBACK JSGCCallback)(JSContext *cx, JSGCStatus status); + +typedef JSBool +(* JS_DLL_CALLBACK JSBranchCallback)(JSContext *cx, JSScript *script); + +typedef void +(* JS_DLL_CALLBACK JSErrorReporter)(JSContext *cx, const char *message, + JSErrorReport *report); + +/* + * Possible exception types. These types are part of a JSErrorFormatString + * structure. They define which error to throw in case of a runtime error. + * JSEXN_NONE marks an unthrowable error. + */ +typedef enum JSExnType { + JSEXN_NONE = -1, + JSEXN_ERR, + JSEXN_INTERNALERR, + JSEXN_EVALERR, + JSEXN_RANGEERR, + JSEXN_REFERENCEERR, + JSEXN_SYNTAXERR, + JSEXN_TYPEERR, + JSEXN_URIERR, + JSEXN_LIMIT +} JSExnType; + +typedef struct JSErrorFormatString { + /* The error format string (UTF-8 if JS_C_STRINGS_ARE_UTF8 is defined). */ + const char *format; + + /* The number of arguments to expand in the formatted error message. */ + uint16 argCount; + + /* One of the JSExnType constants above. */ + int16 exnType; +} JSErrorFormatString; + +typedef const JSErrorFormatString * +(* JS_DLL_CALLBACK JSErrorCallback)(void *userRef, const char *locale, + const uintN errorNumber); + +#ifdef va_start +#define JS_ARGUMENT_FORMATTER_DEFINED 1 + +typedef JSBool +(* JS_DLL_CALLBACK JSArgumentFormatter)(JSContext *cx, const char *format, + JSBool fromJS, jsval **vpp, + va_list *app); +#endif + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleToUpperCase)(JSContext *cx, JSString *src, + jsval *rval); + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleToLowerCase)(JSContext *cx, JSString *src, + jsval *rval); + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleCompare)(JSContext *cx, + JSString *src1, JSString *src2, + jsval *rval); + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleToUnicode)(JSContext *cx, char *src, jsval *rval); + +/* + * Security protocol types. + */ +typedef struct JSPrincipals JSPrincipals; + +/* + * XDR-encode or -decode a principals instance, based on whether xdr->mode is + * JSXDR_ENCODE, in which case *principalsp should be encoded; or JSXDR_DECODE, + * in which case implementations must return a held (via JSPRINCIPALS_HOLD), + * non-null *principalsp out parameter. Return true on success, false on any + * error, which the implementation must have reported. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSPrincipalsTranscoder)(JSXDRState *xdr, + JSPrincipals **principalsp); + +/* + * Return a weak reference to the principals associated with obj, possibly via + * the immutable parent chain leading from obj to a top-level container (e.g., + * a window object in the DOM level 0). If there are no principals associated + * with obj, return null. Therefore null does not mean an error was reported; + * in no event should an error be reported or an exception be thrown by this + * callback's implementation. + */ +typedef JSPrincipals * +(* JS_DLL_CALLBACK JSObjectPrincipalsFinder)(JSContext *cx, JSObject *obj); + +JS_END_EXTERN_C + +#endif /* jspubtd_h___ */ diff --git a/extra_lib/include/js/jstypes.h b/extra_lib/include/js/jstypes.h new file mode 100644 index 0000000..8aca929 --- /dev/null +++ b/extra_lib/include/js/jstypes.h @@ -0,0 +1,464 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* +** File: jstypes.h +** Description: Definitions of NSPR's basic types +** +** Prototypes and macros used to make up for deficiencies in ANSI environments +** that we have found. +** +** Since we do not wrap <stdlib.h> and all the other standard headers, authors +** of portable code will not know in general that they need these definitions. +** Instead of requiring these authors to find the dependent uses in their code +** and take the following steps only in those C files, we take steps once here +** for all C files. +**/ + +#ifndef jstypes_h___ +#define jstypes_h___ + +#include <stddef.h> + +/*********************************************************************** +** MACROS: JS_EXTERN_API +** JS_EXPORT_API +** DESCRIPTION: +** These are only for externally visible routines and globals. For +** internal routines, just use "extern" for type checking and that +** will not export internal cross-file or forward-declared symbols. +** Define a macro for declaring procedures return types. We use this to +** deal with windoze specific type hackery for DLL definitions. Use +** JS_EXTERN_API when the prototype for the method is declared. Use +** JS_EXPORT_API for the implementation of the method. +** +** Example: +** in dowhim.h +** JS_EXTERN_API( void ) DoWhatIMean( void ); +** in dowhim.c +** JS_EXPORT_API( void ) DoWhatIMean( void ) { return; } +** +** +***********************************************************************/ +#ifdef WIN32 +/* These also work for __MWERKS__ */ +#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_API(__type) __declspec(dllexport) __type +#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#elif defined(XP_OS2) && defined(__declspec) + +#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_API(__type) __declspec(dllexport) __type +#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#elif defined(WIN16) + +#ifdef _WINDLL +#define JS_EXTERN_API(__type) extern __type _cdecl _export _loadds +#define JS_EXPORT_API(__type) __type _cdecl _export _loadds +#define JS_EXTERN_DATA(__type) extern __type _export +#define JS_EXPORT_DATA(__type) __type _export + +#define JS_DLL_CALLBACK __cdecl __loadds +#define JS_STATIC_DLL_CALLBACK(__x) static __x CALLBACK + +#else /* this must be .EXE */ +#define JS_EXTERN_API(__type) extern __type _cdecl _export +#define JS_EXPORT_API(__type) __type _cdecl _export +#define JS_EXTERN_DATA(__type) extern __type _export +#define JS_EXPORT_DATA(__type) __type _export + +#define JS_DLL_CALLBACK __cdecl __loadds +#define JS_STATIC_DLL_CALLBACK(__x) __x JS_DLL_CALLBACK +#endif /* _WINDLL */ + +#else /* Unix */ + +#ifdef HAVE_VISIBILITY_ATTRIBUTE +#define JS_EXTERNAL_VIS __attribute__((visibility ("default"))) +#else +#define JS_EXTERNAL_VIS +#endif + +#define JS_EXTERN_API(__type) extern JS_EXTERNAL_VIS __type +#define JS_EXPORT_API(__type) JS_EXTERNAL_VIS __type +#define JS_EXTERN_DATA(__type) extern JS_EXTERNAL_VIS __type +#define JS_EXPORT_DATA(__type) JS_EXTERNAL_VIS __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#endif + +#ifdef _WIN32 +# if defined(__MWERKS__) || defined(__GNUC__) +# define JS_IMPORT_API(__x) __x +# else +# define JS_IMPORT_API(__x) __declspec(dllimport) __x +# endif +#elif defined(XP_OS2) && defined(__declspec) +# define JS_IMPORT_API(__x) __declspec(dllimport) __x +#else +# define JS_IMPORT_API(__x) JS_EXPORT_API (__x) +#endif + +#if defined(_WIN32) && !defined(__MWERKS__) +# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x +#elif defined(XP_OS2) && defined(__declspec) +# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x +#else +# define JS_IMPORT_DATA(__x) JS_EXPORT_DATA (__x) +#endif + +/* + * The linkage of JS API functions differs depending on whether the file is + * used within the JS library or not. Any source file within the JS + * interpreter should define EXPORT_JS_API whereas any client of the library + * should not. + */ +#ifdef EXPORT_JS_API +#define JS_PUBLIC_API(t) JS_EXPORT_API(t) +#define JS_PUBLIC_DATA(t) JS_EXPORT_DATA(t) +#else +#define JS_PUBLIC_API(t) JS_IMPORT_API(t) +#define JS_PUBLIC_DATA(t) JS_IMPORT_DATA(t) +#endif + +#define JS_FRIEND_API(t) JS_PUBLIC_API(t) +#define JS_FRIEND_DATA(t) JS_PUBLIC_DATA(t) + +#ifdef _WIN32 +# define JS_INLINE __inline +#elif defined(__GNUC__) +# define JS_INLINE +#else +# define JS_INLINE +#endif + +/*********************************************************************** +** MACROS: JS_BEGIN_MACRO +** JS_END_MACRO +** DESCRIPTION: +** Macro body brackets so that macros with compound statement definitions +** behave syntactically more like functions when called. +***********************************************************************/ +#define JS_BEGIN_MACRO do { +#define JS_END_MACRO } while (0) + +/*********************************************************************** +** MACROS: JS_BEGIN_EXTERN_C +** JS_END_EXTERN_C +** DESCRIPTION: +** Macro shorthands for conditional C++ extern block delimiters. +***********************************************************************/ +#ifdef __cplusplus +#define JS_BEGIN_EXTERN_C extern "C" { +#define JS_END_EXTERN_C } +#else +#define JS_BEGIN_EXTERN_C +#define JS_END_EXTERN_C +#endif + +/*********************************************************************** +** MACROS: JS_BIT +** JS_BITMASK +** DESCRIPTION: +** Bit masking macros. XXX n must be <= 31 to be portable +***********************************************************************/ +#define JS_BIT(n) ((JSUint32)1 << (n)) +#define JS_BITMASK(n) (JS_BIT(n) - 1) + +/*********************************************************************** +** MACROS: JS_PTR_TO_INT32 +** JS_PTR_TO_UINT32 +** JS_INT32_TO_PTR +** JS_UINT32_TO_PTR +** DESCRIPTION: +** Integer to pointer and pointer to integer conversion macros. +***********************************************************************/ +#define JS_PTR_TO_INT32(x) ((jsint)((char *)(x) - (char *)0)) +#define JS_PTR_TO_UINT32(x) ((jsuint)((char *)(x) - (char *)0)) +#define JS_INT32_TO_PTR(x) ((void *)((char *)0 + (jsint)(x))) +#define JS_UINT32_TO_PTR(x) ((void *)((char *)0 + (jsuint)(x))) + +/*********************************************************************** +** MACROS: JS_HOWMANY +** JS_ROUNDUP +** JS_MIN +** JS_MAX +** DESCRIPTION: +** Commonly used macros for operations on compatible types. +***********************************************************************/ +#define JS_HOWMANY(x,y) (((x)+(y)-1)/(y)) +#define JS_ROUNDUP(x,y) (JS_HOWMANY(x,y)*(y)) +#define JS_MIN(x,y) ((x)<(y)?(x):(y)) +#define JS_MAX(x,y) ((x)>(y)?(x):(y)) + +#if (defined(XP_WIN) && !defined(CROSS_COMPILE)) || defined (WINCE) +# include "jscpucfg.h" /* Use standard Mac or Windows configuration */ +#elif defined(XP_UNIX) || defined(XP_BEOS) || defined(XP_OS2) || defined(CROSS_COMPILE) +# include "jsautocfg.h" /* Use auto-detected configuration */ +# include "jsosdep.h" /* ...and platform-specific flags */ +#else +# error "Must define one of XP_BEOS, XP_OS2, XP_WIN or XP_UNIX" +#endif + +JS_BEGIN_EXTERN_C + +/************************************************************************ +** TYPES: JSUint8 +** JSInt8 +** DESCRIPTION: +** The int8 types are known to be 8 bits each. There is no type that +** is equivalent to a plain "char". +************************************************************************/ +#if JS_BYTES_PER_BYTE == 1 +typedef unsigned char JSUint8; +typedef signed char JSInt8; +#else +#error No suitable type for JSInt8/JSUint8 +#endif + +/************************************************************************ +** TYPES: JSUint16 +** JSInt16 +** DESCRIPTION: +** The int16 types are known to be 16 bits each. +************************************************************************/ +#if JS_BYTES_PER_SHORT == 2 +typedef unsigned short JSUint16; +typedef short JSInt16; +#else +#error No suitable type for JSInt16/JSUint16 +#endif + +/************************************************************************ +** TYPES: JSUint32 +** JSInt32 +** DESCRIPTION: +** The int32 types are known to be 32 bits each. +************************************************************************/ +#if JS_BYTES_PER_INT == 4 +typedef unsigned int JSUint32; +typedef int JSInt32; +#define JS_INT32(x) x +#define JS_UINT32(x) x ## U +#elif JS_BYTES_PER_LONG == 4 +typedef unsigned long JSUint32; +typedef long JSInt32; +#define JS_INT32(x) x ## L +#define JS_UINT32(x) x ## UL +#else +#error No suitable type for JSInt32/JSUint32 +#endif + +/************************************************************************ +** TYPES: JSUint64 +** JSInt64 +** DESCRIPTION: +** The int64 types are known to be 64 bits each. Care must be used when +** declaring variables of type JSUint64 or JSInt64. Different hardware +** architectures and even different compilers have varying support for +** 64 bit values. The only guaranteed portability requires the use of +** the JSLL_ macros (see jslong.h). +************************************************************************/ +#ifdef JS_HAVE_LONG_LONG +#if JS_BYTES_PER_LONG == 8 +typedef long JSInt64; +typedef unsigned long JSUint64; +#elif defined(WIN16) +typedef __int64 JSInt64; +typedef unsigned __int64 JSUint64; +#elif defined(WIN32) && !defined(__GNUC__) +typedef __int64 JSInt64; +typedef unsigned __int64 JSUint64; +#else +typedef long long JSInt64; +typedef unsigned long long JSUint64; +#endif /* JS_BYTES_PER_LONG == 8 */ +#else /* !JS_HAVE_LONG_LONG */ +typedef struct { +#ifdef IS_LITTLE_ENDIAN + JSUint32 lo, hi; +#else + JSUint32 hi, lo; +#endif +} JSInt64; +typedef JSInt64 JSUint64; +#endif /* !JS_HAVE_LONG_LONG */ + +/************************************************************************ +** TYPES: JSUintn +** JSIntn +** DESCRIPTION: +** The JSIntn types are most appropriate for automatic variables. They are +** guaranteed to be at least 16 bits, though various architectures may +** define them to be wider (e.g., 32 or even 64 bits). These types are +** never valid for fields of a structure. +************************************************************************/ +#if JS_BYTES_PER_INT >= 2 +typedef int JSIntn; +typedef unsigned int JSUintn; +#else +#error 'sizeof(int)' not sufficient for platform use +#endif + +/************************************************************************ +** TYPES: JSFloat64 +** DESCRIPTION: +** NSPR's floating point type is always 64 bits. +************************************************************************/ +typedef double JSFloat64; + +/************************************************************************ +** TYPES: JSSize +** DESCRIPTION: +** A type for representing the size of objects. +************************************************************************/ +typedef size_t JSSize; + +/************************************************************************ +** TYPES: JSPtrDiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer sutraction. +************************************************************************/ +typedef ptrdiff_t JSPtrdiff; + +/************************************************************************ +** TYPES: JSUptrdiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer sutraction. +************************************************************************/ +#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 +typedef JSUint64 JSUptrdiff; +#else +typedef unsigned long JSUptrdiff; +#endif + +/************************************************************************ +** TYPES: JSBool +** DESCRIPTION: +** Use JSBool for variables and parameter types. Use JS_FALSE and JS_TRUE +** for clarity of target type in assignments and actual arguments. Use +** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans +** just as you would C int-valued conditions. +************************************************************************/ +typedef JSIntn JSBool; +#define JS_TRUE (JSIntn)1 +#define JS_FALSE (JSIntn)0 + +/************************************************************************ +** TYPES: JSPackedBool +** DESCRIPTION: +** Use JSPackedBool within structs where bitfields are not desireable +** but minimum and consistent overhead matters. +************************************************************************/ +typedef JSUint8 JSPackedBool; + +/* +** A JSWord is an integer that is the same size as a void* +*/ +#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 +typedef JSInt64 JSWord; +typedef JSUint64 JSUword; +#else +typedef long JSWord; +typedef unsigned long JSUword; +#endif + +#include "jsotypes.h" + +/*********************************************************************** +** MACROS: JS_LIKELY +** JS_UNLIKELY +** DESCRIPTION: +** These macros allow you to give a hint to the compiler about branch +** probability so that it can better optimize. Use them like this: +** +** if (JS_LIKELY(v == 1)) { +** ... expected code path ... +** } +** +** if (JS_UNLIKELY(v == 0)) { +** ... non-expected code path ... +** } +** +***********************************************************************/ +#if defined(__GNUC__) && (__GNUC__ > 2) +#define JS_LIKELY(x) (__builtin_expect((x), 1)) +#define JS_UNLIKELY(x) (__builtin_expect((x), 0)) +#else +#define JS_LIKELY(x) (x) +#define JS_UNLIKELY(x) (x) +#endif + +/*********************************************************************** +** MACROS: JS_ARRAY_LENGTH +** JS_ARRAY_END +** DESCRIPTION: +** Macros to get the number of elements and the pointer to one past the +** last element of a C array. Use them like this: +** +** jschar buf[10], *s; +** JSString *str; +** ... +** for (s = buf; s != JS_ARRAY_END(buf); ++s) *s = ...; +** ... +** str = JS_NewStringCopyN(cx, buf, JS_ARRAY_LENGTH(buf)); +** ... +** +***********************************************************************/ + +#define JS_ARRAY_LENGTH(array) (sizeof (array) / sizeof (array)[0]) +#define JS_ARRAY_END(array) ((array) + JS_ARRAY_LENGTH(array)) + +JS_END_EXTERN_C + +#endif /* jstypes_h___ */ diff --git a/extra_lib/include/js/jsxdrapi.h b/extra_lib/include/js/jsxdrapi.h new file mode 100644 index 0000000..35d9918 --- /dev/null +++ b/extra_lib/include/js/jsxdrapi.h @@ -0,0 +1,223 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsxdrapi_h___ +#define jsxdrapi_h___ + +/* + * JS external data representation interface API. + * + * The XDR system is comprised of three major parts: + * + * - the state serialization/deserialization APIs, which allow consumers + * of the API to serialize JS runtime state (script bytecodes, atom maps, + * object graphs, etc.) for later restoration. These portions + * are implemented in various appropriate files, such as jsscript.c + * for the script portions and jsobj.c for object state. + * - the callback APIs through which the runtime requests an opaque + * representation of a native object, and through which the runtime + * constructs a live native object from an opaque representation. These + * portions are the responsibility of the native object implementor. + * - utility functions for en/decoding of primitive types, such as + * JSStrings. This portion is implemented in jsxdrapi.c. + * + * Spiritually guided by Sun's XDR, where appropriate. + */ + +#include "jspubtd.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +/* We use little-endian byteorder for all encoded data */ + +#if defined IS_LITTLE_ENDIAN +#define JSXDR_SWAB32(x) x +#define JSXDR_SWAB16(x) x +#elif defined IS_BIG_ENDIAN +#define JSXDR_SWAB32(x) (((uint32)(x) >> 24) | \ + (((uint32)(x) >> 8) & 0xff00) | \ + (((uint32)(x) << 8) & 0xff0000) | \ + ((uint32)(x) << 24)) +#define JSXDR_SWAB16(x) (((uint16)(x) >> 8) | ((uint16)(x) << 8)) +#else +#error "unknown byte order" +#endif + +#define JSXDR_ALIGN 4 + +typedef enum JSXDRMode { + JSXDR_ENCODE, + JSXDR_DECODE, + JSXDR_FREE +} JSXDRMode; + +typedef enum JSXDRWhence { + JSXDR_SEEK_SET, + JSXDR_SEEK_CUR, + JSXDR_SEEK_END +} JSXDRWhence; + +typedef struct JSXDROps { + JSBool (*get32)(JSXDRState *, uint32 *); + JSBool (*set32)(JSXDRState *, uint32 *); + JSBool (*getbytes)(JSXDRState *, char *, uint32); + JSBool (*setbytes)(JSXDRState *, char *, uint32); + void * (*raw)(JSXDRState *, uint32); + JSBool (*seek)(JSXDRState *, int32, JSXDRWhence); + uint32 (*tell)(JSXDRState *); + void (*finalize)(JSXDRState *); +} JSXDROps; + +struct JSXDRState { + JSXDRMode mode; + JSXDROps *ops; + JSContext *cx; + JSClass **registry; + uintN numclasses; + uintN maxclasses; + void *reghash; + void *userdata; + JSScript *script; +}; + +extern JS_PUBLIC_API(void) +JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx); + +extern JS_PUBLIC_API(JSXDRState *) +JS_XDRNewMem(JSContext *cx, JSXDRMode mode); + +extern JS_PUBLIC_API(void *) +JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp); + +extern JS_PUBLIC_API(void) +JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len); + +extern JS_PUBLIC_API(uint32) +JS_XDRMemDataLeft(JSXDRState *xdr); + +extern JS_PUBLIC_API(void) +JS_XDRMemResetData(JSXDRState *xdr); + +extern JS_PUBLIC_API(void) +JS_XDRDestroy(JSXDRState *xdr); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint8(JSXDRState *xdr, uint8 *b); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint16(JSXDRState *xdr, uint16 *s); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint32(JSXDRState *xdr, uint32 *lp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len); + +extern JS_PUBLIC_API(JSBool) +JS_XDRCString(JSXDRState *xdr, char **sp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRCStringOrNull(JSXDRState *xdr, char **sp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRString(JSXDRState *xdr, JSString **strp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRDouble(JSXDRState *xdr, jsdouble **dp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRValue(JSXDRState *xdr, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRScript(JSXDRState *xdr, JSScript **scriptp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *lp); + +extern JS_PUBLIC_API(uint32) +JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name); + +extern JS_PUBLIC_API(JSClass *) +JS_XDRFindClassById(JSXDRState *xdr, uint32 id); + +/* + * Magic numbers. + */ +#define JSXDR_MAGIC_SCRIPT_1 0xdead0001 +#define JSXDR_MAGIC_SCRIPT_2 0xdead0002 +#define JSXDR_MAGIC_SCRIPT_3 0xdead0003 +#define JSXDR_MAGIC_SCRIPT_4 0xdead0004 +#define JSXDR_MAGIC_SCRIPT_5 0xdead0005 +#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_5 + +/* + * Bytecode version number. Decrement the second term whenever JS bytecode + * changes incompatibly. + * + * This version number should be XDR'ed once near the front of any file or + * larger storage unit containing XDR'ed bytecode and other data, and checked + * before deserialization of bytecode. If the saved version does not match + * the current version, abort deserialization and invalidate the file. + */ +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 16) + +/* + * Library-private functions. + */ +extern JSBool +js_XDRAtom(JSXDRState *xdr, JSAtom **atomp); + +extern JSBool +js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp); + +/* + * FIXME: This is non-unicode version of js_XDRStringAtom that performs lossy + * conversion. Do not use it in the new code! See bug 325202. + */ +extern JSBool +js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp); + +JS_END_EXTERN_C + +#endif /* ! jsxdrapi_h___ */ diff --git a/extra_lib/include/mad/mad.h b/extra_lib/include/mad/mad.h new file mode 100644 index 0000000..955827d --- /dev/null +++ b/extra_lib/include/mad/mad.h @@ -0,0 +1,952 @@ +/* + * libmad - MPEG audio decoder library + * Copyright (C) 2000-2004 Underbit Technologies, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * If you would like to negotiate alternate licensing terms, you may do + * so by contacting: Underbit Technologies, Inc. <info@underbit.com> + */ + +# ifdef __cplusplus +extern "C" { +# endif + +#ifndef _WIN32_WCE +# define FPM_INTEL +#else +# define FPM_DEFAULT +#endif + + + +# define SIZEOF_INT 4 +# define SIZEOF_LONG 4 +# define SIZEOF_LONG_LONG 8 + + +/* Id: version.h,v 1.24 2003/05/27 22:40:37 rob Exp */ + +# ifndef LIBMAD_VERSION_H +# define LIBMAD_VERSION_H + +# define MAD_VERSION_MAJOR 0 +# define MAD_VERSION_MINOR 15 +# define MAD_VERSION_PATCH 1 +# define MAD_VERSION_EXTRA " (beta)" + +# define MAD_VERSION_STRINGIZE(str) #str +# define MAD_VERSION_STRING(num) MAD_VERSION_STRINGIZE(num) + +# define MAD_VERSION MAD_VERSION_STRING(MAD_VERSION_MAJOR) "." \ + MAD_VERSION_STRING(MAD_VERSION_MINOR) "." \ + MAD_VERSION_STRING(MAD_VERSION_PATCH) \ + MAD_VERSION_EXTRA + +# define MAD_PUBLISHYEAR "2000-2003" +# define MAD_AUTHOR "Underbit Technologies, Inc." +# define MAD_EMAIL "info@underbit.com" + +extern char const mad_version[]; +extern char const mad_copyright[]; +extern char const mad_author[]; +extern char const mad_build[]; + +# endif + +/* Id: fixed.h,v 1.36 2003/05/28 04:36:00 rob Exp */ + +# ifndef LIBMAD_FIXED_H +# define LIBMAD_FIXED_H + +# if SIZEOF_INT >= 4 +typedef signed int mad_fixed_t; + +typedef signed int mad_fixed64hi_t; +typedef unsigned int mad_fixed64lo_t; +# else +typedef signed long mad_fixed_t; + +typedef signed long mad_fixed64hi_t; +typedef unsigned long mad_fixed64lo_t; +# endif + +# if defined(_MSC_VER) +# define mad_fixed64_t signed __int64 +# elif 1 || defined(__GNUC__) +# define mad_fixed64_t signed long long +# endif + +# if defined(FPM_FLOAT) +typedef double mad_sample_t; +# else +typedef mad_fixed_t mad_sample_t; +# endif + +/* + * Fixed-point format: 0xABBBBBBB + * A == whole part (sign + 3 bits) + * B == fractional part (28 bits) + * + * Values are signed two's complement, so the effective range is: + * 0x80000000 to 0x7fffffff + * -8.0 to +7.9999999962747097015380859375 + * + * The smallest representable value is: + * 0x00000001 == 0.0000000037252902984619140625 (i.e. about 3.725e-9) + * + * 28 bits of fractional accuracy represent about + * 8.6 digits of decimal accuracy. + * + * Fixed-point numbers can be added or subtracted as normal + * integers, but multiplication requires shifting the 64-bit result + * from 56 fractional bits back to 28 (and rounding.) + * + * Changing the definition of MAD_F_FRACBITS is only partially + * supported, and must be done with care. + */ + +# define MAD_F_FRACBITS 28 + +# if MAD_F_FRACBITS == 28 +# define MAD_F(x) ((mad_fixed_t) (x##L)) +# else +# if MAD_F_FRACBITS < 28 +# warning "MAD_F_FRACBITS < 28" +# define MAD_F(x) ((mad_fixed_t) \ + (((x##L) + \ + (1L << (28 - MAD_F_FRACBITS - 1))) >> \ + (28 - MAD_F_FRACBITS))) +# elif MAD_F_FRACBITS > 28 +# error "MAD_F_FRACBITS > 28 not currently supported" +# define MAD_F(x) ((mad_fixed_t) \ + ((x##L) << (MAD_F_FRACBITS - 28))) +# endif +# endif + +# define MAD_F_MIN ((mad_fixed_t) -0x80000000L) +# define MAD_F_MAX ((mad_fixed_t) +0x7fffffffL) + +# define MAD_F_ONE MAD_F(0x10000000) + +# define mad_f_tofixed(x) ((mad_fixed_t) \ + ((x) * (double) (1L << MAD_F_FRACBITS) + 0.5)) +# define mad_f_todouble(x) ((double) \ + ((x) / (double) (1L << MAD_F_FRACBITS))) + +# define mad_f_intpart(x) ((x) >> MAD_F_FRACBITS) +# define mad_f_fracpart(x) ((x) & ((1L << MAD_F_FRACBITS) - 1)) + /* (x should be positive) */ + +# define mad_f_fromint(x) ((x) << MAD_F_FRACBITS) + +# define mad_f_add(x, y) ((x) + (y)) +# define mad_f_sub(x, y) ((x) - (y)) + +# if defined(FPM_FLOAT) +# error "FPM_FLOAT not yet supported" + +# undef MAD_F +# define MAD_F(x) mad_f_todouble(x) + +# define mad_f_mul(x, y) ((x) * (y)) +# define mad_f_scale64 + +# undef ASO_ZEROCHECK + +# elif defined(FPM_64BIT) + +/* + * This version should be the most accurate if 64-bit types are supported by + * the compiler, although it may not be the most efficient. + */ +# if defined(OPT_ACCURACY) +# define mad_f_mul(x, y) \ + ((mad_fixed_t) \ + ((((mad_fixed64_t) (x) * (y)) + \ + (1L << (MAD_F_SCALEBITS - 1))) >> MAD_F_SCALEBITS)) +# else +# define mad_f_mul(x, y) \ + ((mad_fixed_t) (((mad_fixed64_t) (x) * (y)) >> MAD_F_SCALEBITS)) +# endif + +# define MAD_F_SCALEBITS MAD_F_FRACBITS + +/* --- Intel --------------------------------------------------------------- */ + +# elif defined(FPM_INTEL) + +# if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4035) /* no return value */ +static __forceinline +mad_fixed_t mad_f_mul_inline(mad_fixed_t x, mad_fixed_t y) +{ + enum { + fracbits = MAD_F_FRACBITS + }; + + __asm { + mov eax, x + imul y + shrd eax, edx, fracbits + } + + /* implicit return of eax */ +} +# pragma warning(pop) + +# define mad_f_mul mad_f_mul_inline +# define mad_f_scale64 +# else +/* + * This Intel version is fast and accurate; the disposition of the least + * significant bit depends on OPT_ACCURACY via mad_f_scale64(). + */ +# define MAD_F_MLX(hi, lo, x, y) \ + asm ("imull %3" \ + : "=a" (lo), "=d" (hi) \ + : "%a" (x), "rm" (y) \ + : "cc") + +# if defined(OPT_ACCURACY) +/* + * This gives best accuracy but is not very fast. + */ +# define MAD_F_MLA(hi, lo, x, y) \ + ({ mad_fixed64hi_t __hi; \ + mad_fixed64lo_t __lo; \ + MAD_F_MLX(__hi, __lo, (x), (y)); \ + asm ("addl %2,%0\n\t" \ + "adcl %3,%1" \ + : "=rm" (lo), "=rm" (hi) \ + : "r" (__lo), "r" (__hi), "0" (lo), "1" (hi) \ + : "cc"); \ + }) +# endif /* OPT_ACCURACY */ + +# if defined(OPT_ACCURACY) +/* + * Surprisingly, this is faster than SHRD followed by ADC. + */ +# define mad_f_scale64(hi, lo) \ + ({ mad_fixed64hi_t __hi_; \ + mad_fixed64lo_t __lo_; \ + mad_fixed_t __result; \ + asm ("addl %4,%2\n\t" \ + "adcl %5,%3" \ + : "=rm" (__lo_), "=rm" (__hi_) \ + : "0" (lo), "1" (hi), \ + "ir" (1L << (MAD_F_SCALEBITS - 1)), "ir" (0) \ + : "cc"); \ + asm ("shrdl %3,%2,%1" \ + : "=rm" (__result) \ + : "0" (__lo_), "r" (__hi_), "I" (MAD_F_SCALEBITS) \ + : "cc"); \ + __result; \ + }) +# else +# define mad_f_scale64(hi, lo) \ + ({ mad_fixed_t __result; \ + asm ("shrdl %3,%2,%1" \ + : "=rm" (__result) \ + : "0" (lo), "r" (hi), "I" (MAD_F_SCALEBITS) \ + : "cc"); \ + __result; \ + }) +# endif /* OPT_ACCURACY */ + +# define MAD_F_SCALEBITS MAD_F_FRACBITS +# endif + +/* --- ARM ----------------------------------------------------------------- */ + +# elif defined(FPM_ARM) + +/* + * This ARM V4 version is as accurate as FPM_64BIT but much faster. The + * least significant bit is properly rounded at no CPU cycle cost! + */ +# if 1 +/* + * This is faster than the default implementation via MAD_F_MLX() and + * mad_f_scale64(). + */ +# define mad_f_mul(x, y) \ + ({ mad_fixed64hi_t __hi; \ + mad_fixed64lo_t __lo; \ + mad_fixed_t __result; \ + asm ("smull %0, %1, %3, %4\n\t" \ + "movs %0, %0, lsr %5\n\t" \ + "adc %2, %0, %1, lsl %6" \ + : "=&r" (__lo), "=&r" (__hi), "=r" (__result) \ + : "%r" (x), "r" (y), \ + "M" (MAD_F_SCALEBITS), "M" (32 - MAD_F_SCALEBITS) \ + : "cc"); \ + __result; \ + }) +# endif + +# define MAD_F_MLX(hi, lo, x, y) \ + asm ("smull %0, %1, %2, %3" \ + : "=&r" (lo), "=&r" (hi) \ + : "%r" (x), "r" (y)) + +# define MAD_F_MLA(hi, lo, x, y) \ + asm ("smlal %0, %1, %2, %3" \ + : "+r" (lo), "+r" (hi) \ + : "%r" (x), "r" (y)) + +# define MAD_F_MLN(hi, lo) \ + asm ("rsbs %0, %2, #0\n\t" \ + "rsc %1, %3, #0" \ + : "=r" (lo), "=r" (hi) \ + : "0" (lo), "1" (hi) \ + : "cc") + +# define mad_f_scale64(hi, lo) \ + ({ mad_fixed_t __result; \ + asm ("movs %0, %1, lsr %3\n\t" \ + "adc %0, %0, %2, lsl %4" \ + : "=&r" (__result) \ + : "r" (lo), "r" (hi), \ + "M" (MAD_F_SCALEBITS), "M" (32 - MAD_F_SCALEBITS) \ + : "cc"); \ + __result; \ + }) + +# define MAD_F_SCALEBITS MAD_F_FRACBITS + +/* --- MIPS ---------------------------------------------------------------- */ + +# elif defined(FPM_MIPS) + +/* + * This MIPS version is fast and accurate; the disposition of the least + * significant bit depends on OPT_ACCURACY via mad_f_scale64(). + */ +# define MAD_F_MLX(hi, lo, x, y) \ + asm ("mult %2,%3" \ + : "=l" (lo), "=h" (hi) \ + : "%r" (x), "r" (y)) + +# if defined(HAVE_MADD_ASM) +# define MAD_F_MLA(hi, lo, x, y) \ + asm ("madd %2,%3" \ + : "+l" (lo), "+h" (hi) \ + : "%r" (x), "r" (y)) +# elif defined(HAVE_MADD16_ASM) +/* + * This loses significant accuracy due to the 16-bit integer limit in the + * multiply/accumulate instruction. + */ +# define MAD_F_ML0(hi, lo, x, y) \ + asm ("mult %2,%3" \ + : "=l" (lo), "=h" (hi) \ + : "%r" ((x) >> 12), "r" ((y) >> 16)) +# define MAD_F_MLA(hi, lo, x, y) \ + asm ("madd16 %2,%3" \ + : "+l" (lo), "+h" (hi) \ + : "%r" ((x) >> 12), "r" ((y) >> 16)) +# define MAD_F_MLZ(hi, lo) ((mad_fixed_t) (lo)) +# endif + +# if defined(OPT_SPEED) +# define mad_f_scale64(hi, lo) \ + ((mad_fixed_t) ((hi) << (32 - MAD_F_SCALEBITS))) +# define MAD_F_SCALEBITS MAD_F_FRACBITS +# endif + +/* --- SPARC --------------------------------------------------------------- */ + +# elif defined(FPM_SPARC) + +/* + * This SPARC V8 version is fast and accurate; the disposition of the least + * significant bit depends on OPT_ACCURACY via mad_f_scale64(). + */ +# define MAD_F_MLX(hi, lo, x, y) \ + asm ("smul %2, %3, %0\n\t" \ + "rd %%y, %1" \ + : "=r" (lo), "=r" (hi) \ + : "%r" (x), "rI" (y)) + +/* --- PowerPC ------------------------------------------------------------- */ + +# elif defined(FPM_PPC) + +/* + * This PowerPC version is fast and accurate; the disposition of the least + * significant bit depends on OPT_ACCURACY via mad_f_scale64(). + */ +# define MAD_F_MLX(hi, lo, x, y) \ + do { \ + asm ("mullw %0,%1,%2" \ + : "=r" (lo) \ + : "%r" (x), "r" (y)); \ + asm ("mulhw %0,%1,%2" \ + : "=r" (hi) \ + : "%r" (x), "r" (y)); \ + } \ + while (0) + +# if defined(OPT_ACCURACY) +/* + * This gives best accuracy but is not very fast. + */ +# define MAD_F_MLA(hi, lo, x, y) \ + ({ mad_fixed64hi_t __hi; \ + mad_fixed64lo_t __lo; \ + MAD_F_MLX(__hi, __lo, (x), (y)); \ + asm ("addc %0,%2,%3\n\t" \ + "adde %1,%4,%5" \ + : "=r" (lo), "=r" (hi) \ + : "%r" (lo), "r" (__lo), \ + "%r" (hi), "r" (__hi) \ + : "xer"); \ + }) +# endif + +# if defined(OPT_ACCURACY) +/* + * This is slower than the truncating version below it. + */ +# define mad_f_scale64(hi, lo) \ + ({ mad_fixed_t __result, __round; \ + asm ("rotrwi %0,%1,%2" \ + : "=r" (__result) \ + : "r" (lo), "i" (MAD_F_SCALEBITS)); \ + asm ("extrwi %0,%1,1,0" \ + : "=r" (__round) \ + : "r" (__result)); \ + asm ("insrwi %0,%1,%2,0" \ + : "+r" (__result) \ + : "r" (hi), "i" (MAD_F_SCALEBITS)); \ + asm ("add %0,%1,%2" \ + : "=r" (__result) \ + : "%r" (__result), "r" (__round)); \ + __result; \ + }) +# else +# define mad_f_scale64(hi, lo) \ + ({ mad_fixed_t __result; \ + asm ("rotrwi %0,%1,%2" \ + : "=r" (__result) \ + : "r" (lo), "i" (MAD_F_SCALEBITS)); \ + asm ("insrwi %0,%1,%2,0" \ + : "+r" (__result) \ + : "r" (hi), "i" (MAD_F_SCALEBITS)); \ + __result; \ + }) +# endif + +# define MAD_F_SCALEBITS MAD_F_FRACBITS + +/* --- Default ------------------------------------------------------------- */ + +# elif defined(FPM_DEFAULT) + +/* + * This version is the most portable but it loses significant accuracy. + * Furthermore, accuracy is biased against the second argument, so care + * should be taken when ordering operands. + * + * The scale factors are constant as this is not used with SSO. + * + * Pre-rounding is required to stay within the limits of compliance. + */ +# if defined(OPT_SPEED) +# define mad_f_mul(x, y) (((x) >> 12) * ((y) >> 16)) +# else +# define mad_f_mul(x, y) ((((x) + (1L << 11)) >> 12) * \ + (((y) + (1L << 15)) >> 16)) +# endif + +/* ------------------------------------------------------------------------- */ + +# else +# error "no FPM selected" +# endif + +/* default implementations */ + +# if !defined(mad_f_mul) +# define mad_f_mul(x, y) \ + ({ register mad_fixed64hi_t __hi; \ + register mad_fixed64lo_t __lo; \ + MAD_F_MLX(__hi, __lo, (x), (y)); \ + mad_f_scale64(__hi, __lo); \ + }) +# endif + +# if !defined(MAD_F_MLA) +# define MAD_F_ML0(hi, lo, x, y) ((lo) = mad_f_mul((x), (y))) +# define MAD_F_MLA(hi, lo, x, y) ((lo) += mad_f_mul((x), (y))) +# define MAD_F_MLN(hi, lo) ((lo) = -(lo)) +# define MAD_F_MLZ(hi, lo) ((void) (hi), (mad_fixed_t) (lo)) +# endif + +# if !defined(MAD_F_ML0) +# define MAD_F_ML0(hi, lo, x, y) MAD_F_MLX((hi), (lo), (x), (y)) +# endif + +# if !defined(MAD_F_MLN) +# define MAD_F_MLN(hi, lo) ((hi) = ((lo) = -(lo)) ? ~(hi) : -(hi)) +# endif + +# if !defined(MAD_F_MLZ) +# define MAD_F_MLZ(hi, lo) mad_f_scale64((hi), (lo)) +# endif + +# if !defined(mad_f_scale64) +# if defined(OPT_ACCURACY) +# define mad_f_scale64(hi, lo) \ + ((((mad_fixed_t) \ + (((hi) << (32 - (MAD_F_SCALEBITS - 1))) | \ + ((lo) >> (MAD_F_SCALEBITS - 1)))) + 1) >> 1) +# else +# define mad_f_scale64(hi, lo) \ + ((mad_fixed_t) \ + (((hi) << (32 - MAD_F_SCALEBITS)) | \ + ((lo) >> MAD_F_SCALEBITS))) +# endif +# define MAD_F_SCALEBITS MAD_F_FRACBITS +# endif + +/* C routines */ + +mad_fixed_t mad_f_abs(mad_fixed_t); +mad_fixed_t mad_f_div(mad_fixed_t, mad_fixed_t); + +# endif + +/* Id: bit.h,v 1.11 2003/05/27 22:40:36 rob Exp */ + +# ifndef LIBMAD_BIT_H +# define LIBMAD_BIT_H + +struct mad_bitptr { + unsigned char const *byte; + unsigned short cache; + unsigned short left; +}; + +void mad_bit_init(struct mad_bitptr *, unsigned char const *); + +# define mad_bit_finish(bitptr) /* nothing */ + +unsigned int mad_bit_length(struct mad_bitptr const *, + struct mad_bitptr const *); + +# define mad_bit_bitsleft(bitptr) ((bitptr)->left) +unsigned char const *mad_bit_nextbyte(struct mad_bitptr const *); + +void mad_bit_skip(struct mad_bitptr *, unsigned int); +unsigned long mad_bit_read(struct mad_bitptr *, unsigned int); +void mad_bit_write(struct mad_bitptr *, unsigned int, unsigned long); + +unsigned short mad_bit_crc(struct mad_bitptr, unsigned int, unsigned short); + +# endif + +/* Id: timer.h,v 1.15 2003/05/27 22:40:37 rob Exp */ + +# ifndef LIBMAD_TIMER_H +# define LIBMAD_TIMER_H + +typedef struct { + signed long seconds; /* whole seconds */ + unsigned long fraction; /* 1/MAD_TIMER_RESOLUTION seconds */ +} mad_timer_t; + +extern mad_timer_t const mad_timer_zero; + +# define MAD_TIMER_RESOLUTION 352800000UL + +enum mad_units { + MAD_UNITS_HOURS = -2, + MAD_UNITS_MINUTES = -1, + MAD_UNITS_SECONDS = 0, + + /* metric units */ + + MAD_UNITS_DECISECONDS = 10, + MAD_UNITS_CENTISECONDS = 100, + MAD_UNITS_MILLISECONDS = 1000, + + /* audio sample units */ + + MAD_UNITS_8000_HZ = 8000, + MAD_UNITS_11025_HZ = 11025, + MAD_UNITS_12000_HZ = 12000, + + MAD_UNITS_16000_HZ = 16000, + MAD_UNITS_22050_HZ = 22050, + MAD_UNITS_24000_HZ = 24000, + + MAD_UNITS_32000_HZ = 32000, + MAD_UNITS_44100_HZ = 44100, + MAD_UNITS_48000_HZ = 48000, + + /* video frame/field units */ + + MAD_UNITS_24_FPS = 24, + MAD_UNITS_25_FPS = 25, + MAD_UNITS_30_FPS = 30, + MAD_UNITS_48_FPS = 48, + MAD_UNITS_50_FPS = 50, + MAD_UNITS_60_FPS = 60, + + /* CD audio frames */ + + MAD_UNITS_75_FPS = 75, + + /* video drop-frame units */ + + MAD_UNITS_23_976_FPS = -24, + MAD_UNITS_24_975_FPS = -25, + MAD_UNITS_29_97_FPS = -30, + MAD_UNITS_47_952_FPS = -48, + MAD_UNITS_49_95_FPS = -50, + MAD_UNITS_59_94_FPS = -60 +}; + +# define mad_timer_reset(timer) ((void) (*(timer) = mad_timer_zero)) + +int mad_timer_compare(mad_timer_t, mad_timer_t); + +# define mad_timer_sign(timer) mad_timer_compare((timer), mad_timer_zero) + +void mad_timer_negate(mad_timer_t *); +mad_timer_t mad_timer_abs(mad_timer_t); + +void mad_timer_set(mad_timer_t *, unsigned long, unsigned long, unsigned long); +void mad_timer_add(mad_timer_t *, mad_timer_t); +void mad_timer_multiply(mad_timer_t *, signed long); + +signed long mad_timer_count(mad_timer_t, enum mad_units); +unsigned long mad_timer_fraction(mad_timer_t, unsigned long); +void mad_timer_string(mad_timer_t, char *, char const *, + enum mad_units, enum mad_units, unsigned long); + +# endif + +/* Id: stream.h,v 1.18 2003/05/27 22:40:37 rob Exp */ + +# ifndef LIBMAD_STREAM_H +# define LIBMAD_STREAM_H + + +# define MAD_BUFFER_GUARD 8 +# define MAD_BUFFER_MDLEN (511 + 2048 + MAD_BUFFER_GUARD) + +enum mad_error { + MAD_ERROR_NONE = 0x0000, /* no error */ + + MAD_ERROR_BUFLEN = 0x0001, /* input buffer too small (or EOF) */ + MAD_ERROR_BUFPTR = 0x0002, /* invalid (null) buffer pointer */ + + MAD_ERROR_NOMEM = 0x0031, /* not enough memory */ + + MAD_ERROR_LOSTSYNC = 0x0101, /* lost synchronization */ + MAD_ERROR_BADLAYER = 0x0102, /* reserved header layer value */ + MAD_ERROR_BADBITRATE = 0x0103, /* forbidden bitrate value */ + MAD_ERROR_BADSAMPLERATE = 0x0104, /* reserved sample frequency value */ + MAD_ERROR_BADEMPHASIS = 0x0105, /* reserved emphasis value */ + + MAD_ERROR_BADCRC = 0x0201, /* CRC check failed */ + MAD_ERROR_BADBITALLOC = 0x0211, /* forbidden bit allocation value */ + MAD_ERROR_BADSCALEFACTOR = 0x0221, /* bad scalefactor index */ + MAD_ERROR_BADFRAMELEN = 0x0231, /* bad frame length */ + MAD_ERROR_BADBIGVALUES = 0x0232, /* bad big_values count */ + MAD_ERROR_BADBLOCKTYPE = 0x0233, /* reserved block_type */ + MAD_ERROR_BADSCFSI = 0x0234, /* bad scalefactor selection info */ + MAD_ERROR_BADDATAPTR = 0x0235, /* bad main_data_begin pointer */ + MAD_ERROR_BADPART3LEN = 0x0236, /* bad audio data length */ + MAD_ERROR_BADHUFFTABLE = 0x0237, /* bad Huffman table select */ + MAD_ERROR_BADHUFFDATA = 0x0238, /* Huffman data overrun */ + MAD_ERROR_BADSTEREO = 0x0239 /* incompatible block_type for JS */ +}; + +# define MAD_RECOVERABLE(error) ((error) & 0xff00) + +struct mad_stream { + unsigned char const *buffer; /* input bitstream buffer */ + unsigned char const *bufend; /* end of buffer */ + unsigned long skiplen; /* bytes to skip before next frame */ + + int sync; /* stream sync found */ + unsigned long freerate; /* free bitrate (fixed) */ + + unsigned char const *this_frame; /* start of current frame */ + unsigned char const *next_frame; /* start of next frame */ + struct mad_bitptr ptr; /* current processing bit pointer */ + + struct mad_bitptr anc_ptr; /* ancillary bits pointer */ + unsigned int anc_bitlen; /* number of ancillary bits */ + + unsigned char (*main_data)[MAD_BUFFER_MDLEN]; + /* Layer III main_data() */ + unsigned int md_len; /* bytes in main_data */ + + int options; /* decoding options (see below) */ + enum mad_error error; /* error code (see above) */ +}; + +enum { + MAD_OPTION_IGNORECRC = 0x0001, /* ignore CRC errors */ + MAD_OPTION_HALFSAMPLERATE = 0x0002 /* generate PCM at 1/2 sample rate */ +# if 0 /* not yet implemented */ + MAD_OPTION_LEFTCHANNEL = 0x0010, /* decode left channel only */ + MAD_OPTION_RIGHTCHANNEL = 0x0020, /* decode right channel only */ + MAD_OPTION_SINGLECHANNEL = 0x0030 /* combine channels */ +# endif +}; + +void mad_stream_init(struct mad_stream *); +void mad_stream_finish(struct mad_stream *); + +# define mad_stream_options(stream, opts) \ + ((void) ((stream)->options = (opts))) + +void mad_stream_buffer(struct mad_stream *, + unsigned char const *, unsigned long); +void mad_stream_skip(struct mad_stream *, unsigned long); + +int mad_stream_sync(struct mad_stream *); + +char const *mad_stream_errorstr(struct mad_stream const *); + +# endif + +/* Id: frame.h,v 1.19 2003/05/27 22:40:36 rob Exp */ + +# ifndef LIBMAD_FRAME_H +# define LIBMAD_FRAME_H + + +enum mad_layer { + MAD_LAYER_I = 1, /* Layer I */ + MAD_LAYER_II = 2, /* Layer II */ + MAD_LAYER_III = 3 /* Layer III */ +}; + +enum mad_mode { + MAD_MODE_SINGLE_CHANNEL = 0, /* single channel */ + MAD_MODE_DUAL_CHANNEL = 1, /* dual channel */ + MAD_MODE_JOINT_STEREO = 2, /* joint (MS/intensity) stereo */ + MAD_MODE_STEREO = 3 /* normal LR stereo */ +}; + +enum mad_emphasis { + MAD_EMPHASIS_NONE = 0, /* no emphasis */ + MAD_EMPHASIS_50_15_US = 1, /* 50/15 microseconds emphasis */ + MAD_EMPHASIS_CCITT_J_17 = 3, /* CCITT J.17 emphasis */ + MAD_EMPHASIS_RESERVED = 2 /* unknown emphasis */ +}; + +struct mad_header { + enum mad_layer layer; /* audio layer (1, 2, or 3) */ + enum mad_mode mode; /* channel mode (see above) */ + int mode_extension; /* additional mode info */ + enum mad_emphasis emphasis; /* de-emphasis to use (see above) */ + + unsigned long bitrate; /* stream bitrate (bps) */ + unsigned int samplerate; /* sampling frequency (Hz) */ + + unsigned short crc_check; /* frame CRC accumulator */ + unsigned short crc_target; /* final target CRC checksum */ + + int flags; /* flags (see below) */ + int private_bits; /* private bits (see below) */ + + mad_timer_t duration; /* audio playing time of frame */ +}; + +struct mad_frame { + struct mad_header header; /* MPEG audio header */ + + int options; /* decoding options (from stream) */ + + mad_fixed_t sbsample[2][36][32]; /* synthesis subband filter samples */ + mad_fixed_t (*overlap)[2][32][18]; /* Layer III block overlap data */ +}; + +# define MAD_NCHANNELS(header) ((header)->mode ? 2 : 1) +# define MAD_NSBSAMPLES(header) \ + ((header)->layer == MAD_LAYER_I ? 12 : \ + (((header)->layer == MAD_LAYER_III && \ + ((header)->flags & MAD_FLAG_LSF_EXT)) ? 18 : 36)) + +enum { + MAD_FLAG_NPRIVATE_III = 0x0007, /* number of Layer III private bits */ + MAD_FLAG_INCOMPLETE = 0x0008, /* header but not data is decoded */ + + MAD_FLAG_PROTECTION = 0x0010, /* frame has CRC protection */ + MAD_FLAG_COPYRIGHT = 0x0020, /* frame is copyright */ + MAD_FLAG_ORIGINAL = 0x0040, /* frame is original (else copy) */ + MAD_FLAG_PADDING = 0x0080, /* frame has additional slot */ + + MAD_FLAG_I_STEREO = 0x0100, /* uses intensity joint stereo */ + MAD_FLAG_MS_STEREO = 0x0200, /* uses middle/side joint stereo */ + MAD_FLAG_FREEFORMAT = 0x0400, /* uses free format bitrate */ + + MAD_FLAG_LSF_EXT = 0x1000, /* lower sampling freq. extension */ + MAD_FLAG_MC_EXT = 0x2000, /* multichannel audio extension */ + MAD_FLAG_MPEG_2_5_EXT = 0x4000 /* MPEG 2.5 (unofficial) extension */ +}; + +enum { + MAD_PRIVATE_HEADER = 0x0100, /* header private bit */ + MAD_PRIVATE_III = 0x001f /* Layer III private bits (up to 5) */ +}; + +void mad_header_init(struct mad_header *); + +# define mad_header_finish(header) /* nothing */ + +int mad_header_decode(struct mad_header *, struct mad_stream *); + +void mad_frame_init(struct mad_frame *); +void mad_frame_finish(struct mad_frame *); + +int mad_frame_decode(struct mad_frame *, struct mad_stream *); + +void mad_frame_mute(struct mad_frame *); + +# endif + +/* Id: synth.h,v 1.14 2003/05/27 22:40:37 rob Exp */ + +# ifndef LIBMAD_SYNTH_H +# define LIBMAD_SYNTH_H + + +struct mad_pcm { + unsigned int samplerate; /* sampling frequency (Hz) */ + unsigned short channels; /* number of channels */ + unsigned short length; /* number of samples per channel */ + mad_fixed_t samples[2][1152]; /* PCM output samples [ch][sample] */ +}; + +struct mad_synth { + mad_fixed_t filter[2][2][2][16][8]; /* polyphase filterbank outputs */ + /* [ch][eo][peo][s][v] */ + + unsigned int phase; /* current processing phase */ + + struct mad_pcm pcm; /* PCM output */ +}; + +/* single channel PCM selector */ +enum { + MAD_PCM_CHANNEL_SINGLE = 0 +}; + +/* dual channel PCM selector */ +enum { + MAD_PCM_CHANNEL_DUAL_1 = 0, + MAD_PCM_CHANNEL_DUAL_2 = 1 +}; + +/* stereo PCM selector */ +enum { + MAD_PCM_CHANNEL_STEREO_LEFT = 0, + MAD_PCM_CHANNEL_STEREO_RIGHT = 1 +}; + +void mad_synth_init(struct mad_synth *); + +# define mad_synth_finish(synth) /* nothing */ + +void mad_synth_mute(struct mad_synth *); + +void mad_synth_frame(struct mad_synth *, struct mad_frame const *); + +# endif + +/* Id: decoder.h,v 1.16 2003/05/27 22:40:36 rob Exp */ + +# ifndef LIBMAD_DECODER_H +# define LIBMAD_DECODER_H + + +enum mad_decoder_mode { + MAD_DECODER_MODE_SYNC = 0, + MAD_DECODER_MODE_ASYNC +}; + +enum mad_flow { + MAD_FLOW_CONTINUE = 0x0000, /* continue normally */ + MAD_FLOW_STOP = 0x0010, /* stop decoding normally */ + MAD_FLOW_BREAK = 0x0011, /* stop decoding and signal an error */ + MAD_FLOW_IGNORE = 0x0020 /* ignore the current frame */ +}; + +struct mad_decoder { + enum mad_decoder_mode mode; + + int options; + + struct { + long pid; + int in; + int out; + } async; + + struct { + struct mad_stream stream; + struct mad_frame frame; + struct mad_synth synth; + } *sync; + + void *cb_data; + + enum mad_flow (*input_func)(void *, struct mad_stream *); + enum mad_flow (*header_func)(void *, struct mad_header const *); + enum mad_flow (*filter_func)(void *, + struct mad_stream const *, struct mad_frame *); + enum mad_flow (*output_func)(void *, + struct mad_header const *, struct mad_pcm *); + enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *); + enum mad_flow (*message_func)(void *, void *, unsigned int *); +}; + +void mad_decoder_init(struct mad_decoder *, void *, + enum mad_flow (*)(void *, struct mad_stream *), + enum mad_flow (*)(void *, struct mad_header const *), + enum mad_flow (*)(void *, + struct mad_stream const *, + struct mad_frame *), + enum mad_flow (*)(void *, + struct mad_header const *, + struct mad_pcm *), + enum mad_flow (*)(void *, + struct mad_stream *, + struct mad_frame *), + enum mad_flow (*)(void *, void *, unsigned int *)); +int mad_decoder_finish(struct mad_decoder *); + +# define mad_decoder_options(decoder, opts) \ + ((void) ((decoder)->options = (opts))) + +int mad_decoder_run(struct mad_decoder *, enum mad_decoder_mode); +int mad_decoder_message(struct mad_decoder *, void *, unsigned int *); + +# endif + +# ifdef __cplusplus +} +# endif diff --git a/extra_lib/include/ogg/ogg.h b/extra_lib/include/ogg/ogg.h new file mode 100644 index 0000000..48bbfb1 --- /dev/null +++ b/extra_lib/include/ogg/ogg.h @@ -0,0 +1,202 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel libogg include + last mod: $Id: ogg.h,v 1.2 2008/12/02 18:04:43 jeanlf Exp $ + + ********************************************************************/ +#ifndef _OGG_H +#define _OGG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <ogg/os_types.h> + +typedef struct { + long endbyte; + int endbit; + + unsigned char *buffer; + unsigned char *ptr; + long storage; +} oggpack_buffer; + +/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/ + +typedef struct { + unsigned char *header; + long header_len; + unsigned char *body; + long body_len; +} ogg_page; + +/* ogg_stream_state contains the current encode/decode state of a logical + Ogg bitstream **********************************************************/ + +typedef struct { + unsigned char *body_data; /* bytes from packet bodies */ + long body_storage; /* storage elements allocated */ + long body_fill; /* elements stored; fill mark */ + long body_returned; /* elements of fill returned */ + + + int *lacing_vals; /* The values that will go to the segment table */ + ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + long lacing_storage; + long lacing_fill; + long lacing_packet; + long lacing_returned; + + unsigned char header[282]; /* working space for header encode */ + int header_fill; + + int e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + int b_o_s; /* set after we've written the initial page + of a logical bitstream */ + long serialno; + long pageno; + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + ogg_int64_t granulepos; + +} ogg_stream_state; + +/* ogg_packet is used to encapsulate the data and metadata belonging + to a single raw Ogg/Vorbis packet *************************************/ + +typedef struct { + unsigned char *packet; + long bytes; + long b_o_s; + long e_o_s; + + ogg_int64_t granulepos; + + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ +} ogg_packet; + +typedef struct { + unsigned char *data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; +} ogg_sync_state; + +/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ + +extern void oggpack_writeinit(oggpack_buffer *b); +extern void oggpack_writetrunc(oggpack_buffer *b,long bits); +extern void oggpack_writealign(oggpack_buffer *b); +extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpack_reset(oggpack_buffer *b); +extern void oggpack_writeclear(oggpack_buffer *b); +extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpack_look(oggpack_buffer *b,int bits); +extern long oggpack_look1(oggpack_buffer *b); +extern void oggpack_adv(oggpack_buffer *b,int bits); +extern void oggpack_adv1(oggpack_buffer *b); +extern long oggpack_read(oggpack_buffer *b,int bits); +extern long oggpack_read1(oggpack_buffer *b); +extern long oggpack_bytes(oggpack_buffer *b); +extern long oggpack_bits(oggpack_buffer *b); +extern unsigned char *oggpack_get_buffer(oggpack_buffer *b); + +extern void oggpackB_writeinit(oggpack_buffer *b); +extern void oggpackB_writetrunc(oggpack_buffer *b,long bits); +extern void oggpackB_writealign(oggpack_buffer *b); +extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpackB_reset(oggpack_buffer *b); +extern void oggpackB_writeclear(oggpack_buffer *b); +extern void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpackB_look(oggpack_buffer *b,int bits); +extern long oggpackB_look1(oggpack_buffer *b); +extern void oggpackB_adv(oggpack_buffer *b,int bits); +extern void oggpackB_adv1(oggpack_buffer *b); +extern long oggpackB_read(oggpack_buffer *b,int bits); +extern long oggpackB_read1(oggpack_buffer *b); +extern long oggpackB_bytes(oggpack_buffer *b); +extern long oggpackB_bits(oggpack_buffer *b); +extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b); + +/* Ogg BITSTREAM PRIMITIVES: encoding **************************/ + +extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og); + +/* Ogg BITSTREAM PRIMITIVES: decoding **************************/ + +extern int ogg_sync_init(ogg_sync_state *oy); +extern int ogg_sync_clear(ogg_sync_state *oy); +extern int ogg_sync_reset(ogg_sync_state *oy); +extern int ogg_sync_destroy(ogg_sync_state *oy); + +extern char *ogg_sync_buffer(ogg_sync_state *oy, long size); +extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes); +extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); +extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); +extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); + +/* Ogg BITSTREAM PRIMITIVES: general ***************************/ + +extern int ogg_stream_init(ogg_stream_state *os,int serialno); +extern int ogg_stream_clear(ogg_stream_state *os); +extern int ogg_stream_reset(ogg_stream_state *os); +extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno); +extern int ogg_stream_destroy(ogg_stream_state *os); +extern int ogg_stream_eos(ogg_stream_state *os); + +extern void ogg_page_checksum_set(ogg_page *og); + +extern int ogg_page_version(ogg_page *og); +extern int ogg_page_continued(ogg_page *og); +extern int ogg_page_bos(ogg_page *og); +extern int ogg_page_eos(ogg_page *og); +extern ogg_int64_t ogg_page_granulepos(ogg_page *og); +extern int ogg_page_serialno(ogg_page *og); +extern long ogg_page_pageno(ogg_page *og); +extern int ogg_page_packets(ogg_page *og); + +extern void ogg_packet_clear(ogg_packet *op); + + +#ifdef __cplusplus +} +#endif + +#endif /* _OGG_H */ + + + + + + diff --git a/extra_lib/include/ogg/os_types.h b/extra_lib/include/ogg/os_types.h new file mode 100644 index 0000000..604d853 --- /dev/null +++ b/extra_lib/include/ogg/os_types.h @@ -0,0 +1,127 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id: os_types.h,v 1.2 2008/12/02 18:04:43 jeanlf Exp $ + + ********************************************************************/ +#ifndef _OS_TYPES_H +#define _OS_TYPES_H + +/* make it easy on the folks that want to compile the libs with a + different malloc than stdlib */ +#define _ogg_malloc malloc +#define _ogg_calloc calloc +#define _ogg_realloc realloc +#define _ogg_free free + +#if defined(_WIN32) + +# if defined(__CYGWIN__) +# include <_G_config.h> + typedef _G_int64_t ogg_int64_t; + typedef _G_int32_t ogg_int32_t; + typedef _G_uint32_t ogg_uint32_t; + typedef _G_int16_t ogg_int16_t; + typedef _G_uint16_t ogg_uint16_t; +# elif defined(__MINGW32__) + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + typedef unsigned long long ogg_uint64_t; +# elif defined(__MWERKS__) + typedef long long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; +# else + /* MSVC/Borland */ + typedef __int64 ogg_int64_t; + typedef __int32 ogg_int32_t; + typedef unsigned __int32 ogg_uint32_t; + typedef __int16 ogg_int16_t; + typedef unsigned __int16 ogg_uint16_t; +# endif + +#elif defined(__MACOS__) + +# include <sys/types.h> + typedef SInt16 ogg_int16_t; + typedef UInt16 ogg_uint16_t; + typedef SInt32 ogg_int32_t; + typedef UInt32 ogg_uint32_t; + typedef SInt64 ogg_int64_t; + +#elif defined(__MACOSX__) /* MacOS X Framework build */ + +# include <sys/types.h> + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined(__BEOS__) + + /* Be */ +# include <inttypes.h> + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined (__EMX__) + + /* OS/2 GCC */ + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined (DJGPP) + + /* DJGPP */ + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined(R5900) + + /* PS2 EE */ + typedef long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned ogg_uint32_t; + typedef short ogg_int16_t; + +#elif defined(__SYMBIAN32__) + + /* Symbian GCC */ + typedef signed short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef signed int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long int ogg_int64_t; + +#else + +# include <sys/types.h> +# include <ogg/config_types.h> + +#endif + +#endif /* _OS_TYPES_H */ diff --git a/extra_lib/include/openjpeg/openjpeg.h b/extra_lib/include/openjpeg/openjpeg.h new file mode 100644 index 0000000..ffcaaca --- /dev/null +++ b/extra_lib/include/openjpeg/openjpeg.h @@ -0,0 +1,911 @@ + /* + * Copyright (c) 2002-2007, Communications and Remote Sensing Laboratory, Universite catholique de Louvain (UCL), Belgium + * Copyright (c) 2002-2007, Professor Benoit Macq + * Copyright (c) 2001-2003, David Janssens + * Copyright (c) 2002-2003, Yannick Verschueren + * Copyright (c) 2003-2007, Francois-Olivier Devaux and Antonin Descampe + * Copyright (c) 2005, Herve Drolon, FreeImage Team + * Copyright (c) 2006-2007, Parvatha Elangovan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef OPENJPEG_H +#define OPENJPEG_H + +#define OPENJPEG_VERSION "1.3.0" + +/* +========================================================== + Compiler directives +========================================================== +*/ + +#if defined(OPJ_STATIC) || !(defined(WIN32) || defined(__WIN32__)) +#define OPJ_API +#define OPJ_CALLCONV +#else +#define OPJ_CALLCONV __stdcall +/* +The following ifdef block is the standard way of creating macros which make exporting +from a DLL simpler. All files within this DLL are compiled with the OPJ_EXPORTS +symbol defined on the command line. this symbol should not be defined on any project +that uses this DLL. This way any other project whose source files include this file see +OPJ_API functions as being imported from a DLL, wheras this DLL sees symbols +defined with this macro as being exported. +*/ +#ifdef OPJ_EXPORTS +#define OPJ_API __declspec(dllexport) +#else +#define OPJ_API __declspec(dllimport) +#endif /* OPJ_EXPORTS */ +#endif /* !OPJ_STATIC || !WIN32 */ + +#ifndef __cplusplus +#if defined(HAVE_STDBOOL_H) +/* +The C language implementation does correctly provide the standard header +file "stdbool.h". + */ +#include <stdbool.h> +#else +/* +The C language implementation does not provide the standard header file +"stdbool.h" as required by ISO/IEC 9899:1999. Try to compensate for this +braindamage below. +*/ +#if !defined(bool) +#define bool int +#endif +#if !defined(true) +#define true 1 +#endif +#if !defined(false) +#define false 0 +#endif +#endif +#endif /* __cplusplus */ + +/* +========================================================== + Useful constant definitions +========================================================== +*/ + +#define OPJ_PATH_LEN 4096 /**< Maximum allowed size for filenames */ + +#define J2K_MAXRLVLS 33 /**< Number of maximum resolution level authorized */ +#define J2K_MAXBANDS (3*J2K_MAXRLVLS-2) /**< Number of maximum sub-band linked to number of resolution level */ + +/* UniPG>> */ +#define JPWL_MAX_NO_TILESPECS 16 /**< Maximum number of tile parts expected by JPWL: increase at your will */ +#define JPWL_MAX_NO_PACKSPECS 16 /**< Maximum number of packet parts expected by JPWL: increase at your will */ +#define JPWL_MAX_NO_MARKERS 512 /**< Maximum number of JPWL markers: increase at your will */ +#define JPWL_PRIVATEINDEX_NAME "jpwl_index_privatefilename" /**< index file name used when JPWL is on */ +#define JPWL_EXPECTED_COMPONENTS 3 /**< Expect this number of components, so you'll find better the first EPB */ +#define JPWL_MAXIMUM_TILES 8192 /**< Expect this maximum number of tiles, to avoid some crashes */ +#define JPWL_MAXIMUM_HAMMING 2 /**< Expect this maximum number of bit errors in marker id's */ +#define JPWL_MAXIMUM_EPB_ROOM 65450 /**< Expect this maximum number of bytes for composition of EPBs */ +/* <<UniPG */ + +/* +========================================================== + enum definitions +========================================================== +*/ +/** +Rsiz Capabilities +*/ +typedef enum RSIZ_CAPABILITIES { + STD_RSIZ = 0, /** Standard JPEG2000 profile*/ + CINEMA2K = 3, /** Profile name for a 2K image*/ + CINEMA4K = 4 /** Profile name for a 4K image*/ +} OPJ_RSIZ_CAPABILITIES; + +/** +Digital cinema operation mode +*/ +typedef enum CINEMA_MODE { + OFF = 0, /** Not Digital Cinema*/ + CINEMA2K_24 = 1, /** 2K Digital Cinema at 24 fps*/ + CINEMA2K_48 = 2, /** 2K Digital Cinema at 48 fps*/ + CINEMA4K_24 = 3 /** 4K Digital Cinema at 24 fps*/ +}OPJ_CINEMA_MODE; + +/** +Progression order +*/ +typedef enum PROG_ORDER { + PROG_UNKNOWN = -1, /**< place-holder */ + LRCP = 0, /**< layer-resolution-component-precinct order */ + RLCP = 1, /**< resolution-layer-component-precinct order */ + RPCL = 2, /**< resolution-precinct-component-layer order */ + PCRL = 3, /**< precinct-component-resolution-layer order */ + CPRL = 4 /**< component-precinct-resolution-layer order */ +} OPJ_PROG_ORDER; + +/** +Supported image color spaces +*/ +typedef enum COLOR_SPACE { + CLRSPC_UNKNOWN = -1, /**< place-holder */ + CLRSPC_SRGB = 1, /**< sRGB */ + CLRSPC_GRAY = 2, /**< grayscale */ + CLRSPC_SYCC = 3 /**< YUV */ +} OPJ_COLOR_SPACE; + +/** +Supported codec +*/ +typedef enum CODEC_FORMAT { + CODEC_UNKNOWN = -1, /**< place-holder */ + CODEC_J2K = 0, /**< JPEG-2000 codestream : read/write */ + CODEC_JPT = 1, /**< JPT-stream (JPEG 2000, JPIP) : read only */ + CODEC_JP2 = 2 /**< JPEG-2000 file format : read/write */ +} OPJ_CODEC_FORMAT; + +/** +Limit decoding to certain portions of the codestream. +*/ +typedef enum LIMIT_DECODING { + NO_LIMITATION = 0, /**< No limitation for the decoding. The entire codestream will de decoded */ + LIMIT_TO_MAIN_HEADER = 1, /**< The decoding is limited to the Main Header */ + DECODE_ALL_BUT_PACKETS = 2 /**< Decode everything except the JPEG 2000 packets */ +} OPJ_LIMIT_DECODING; + +/* +========================================================== + event manager typedef definitions +========================================================== +*/ + +/** +Callback function prototype for events +@param msg Event message +@param client_data +*/ +typedef void (*opj_msg_callback) (const char *msg, void *client_data); + +/** +Message handler object +used for +<ul> +<li>Error messages +<li>Warning messages +<li>Debugging messages +</ul> +*/ +typedef struct opj_event_mgr { + /** Error message callback if available, NULL otherwise */ + opj_msg_callback error_handler; + /** Warning message callback if available, NULL otherwise */ + opj_msg_callback warning_handler; + /** Debug message callback if available, NULL otherwise */ + opj_msg_callback info_handler; +} opj_event_mgr_t; + + +/* +========================================================== + codec typedef definitions +========================================================== +*/ + +/** +Progression order changes +*/ +typedef struct opj_poc { + /** Resolution num start, Component num start, given by POC */ + int resno0, compno0; + /** Layer num end,Resolution num end, Component num end, given by POC */ + int layno1, resno1, compno1; + /** Layer num start,Precinct num start, Precinct num end */ + int layno0, precno0, precno1; + /** Progression order enum*/ + OPJ_PROG_ORDER prg1,prg; + /** Progression order string*/ + char progorder[5]; + /** Tile number */ + int tile; + /** Start and end values for Tile width and height*/ + int tx0,tx1,ty0,ty1; + /** Start value, initialised in pi_initialise_encode*/ + int layS, resS, compS, prcS; + /** End value, initialised in pi_initialise_encode */ + int layE, resE, compE, prcE; + /** Start and end values of Tile width and height, initialised in pi_initialise_encode*/ + int txS,txE,tyS,tyE,dx,dy; + /** Temporary values for Tile parts, initialised in pi_create_encode */ + int lay_t, res_t, comp_t, prc_t,tx0_t,ty0_t; +} opj_poc_t; + +/** +Compression parameters +*/ +typedef struct opj_cparameters { + /** size of tile: tile_size_on = false (not in argument) or = true (in argument) */ + bool tile_size_on; + /** XTOsiz */ + int cp_tx0; + /** YTOsiz */ + int cp_ty0; + /** XTsiz */ + int cp_tdx; + /** YTsiz */ + int cp_tdy; + /** allocation by rate/distortion */ + int cp_disto_alloc; + /** allocation by fixed layer */ + int cp_fixed_alloc; + /** add fixed_quality */ + int cp_fixed_quality; + /** fixed layer */ + int *cp_matrice; + /** comment for coding */ + char *cp_comment; + /** csty : coding style */ + int csty; + /** progression order (default LRCP) */ + OPJ_PROG_ORDER prog_order; + /** progression order changes */ + opj_poc_t POC[32]; + /** number of progression order changes (POC), default to 0 */ + int numpocs; + /** number of layers */ + int tcp_numlayers; + /** rates of layers */ + float tcp_rates[100]; + /** different psnr for successive layers */ + float tcp_distoratio[100]; + /** number of resolutions */ + int numresolution; + /** initial code block width, default to 64 */ + int cblockw_init; + /** initial code block height, default to 64 */ + int cblockh_init; + /** mode switch (cblk_style) */ + int mode; + /** 1 : use the irreversible DWT 9-7, 0 : use lossless compression (default) */ + int irreversible; + /** region of interest: affected component in [0..3], -1 means no ROI */ + int roi_compno; + /** region of interest: upshift value */ + int roi_shift; + /* number of precinct size specifications */ + int res_spec; + /** initial precinct width */ + int prcw_init[J2K_MAXRLVLS]; + /** initial precinct height */ + int prch_init[J2K_MAXRLVLS]; + + /**@name command line encoder parameters (not used inside the library) */ + /*@{*/ + /** input file name */ + char infile[OPJ_PATH_LEN]; + /** output file name */ + char outfile[OPJ_PATH_LEN]; + /** DEPRECATED. Index generation is now handeld with the opj_encode_with_info() function. Set to NULL */ + int index_on; + /** DEPRECATED. Index generation is now handeld with the opj_encode_with_info() function. Set to NULL */ + char index[OPJ_PATH_LEN]; + /** subimage encoding: origin image offset in x direction */ + int image_offset_x0; + /** subimage encoding: origin image offset in y direction */ + int image_offset_y0; + /** subsampling value for dx */ + int subsampling_dx; + /** subsampling value for dy */ + int subsampling_dy; + /** input file format 0: PGX, 1: PxM, 2: BMP 3:TIF*/ + int decod_format; + /** output file format 0: J2K, 1: JP2, 2: JPT */ + int cod_format; + /*@}*/ + +/* UniPG>> */ + /**@name JPWL encoding parameters */ + /*@{*/ + /** enables writing of EPC in MH, thus activating JPWL */ + bool jpwl_epc_on; + /** error protection method for MH (0,1,16,32,37-128) */ + int jpwl_hprot_MH; + /** tile number of header protection specification (>=0) */ + int jpwl_hprot_TPH_tileno[JPWL_MAX_NO_TILESPECS]; + /** error protection methods for TPHs (0,1,16,32,37-128) */ + int jpwl_hprot_TPH[JPWL_MAX_NO_TILESPECS]; + /** tile number of packet protection specification (>=0) */ + int jpwl_pprot_tileno[JPWL_MAX_NO_PACKSPECS]; + /** packet number of packet protection specification (>=0) */ + int jpwl_pprot_packno[JPWL_MAX_NO_PACKSPECS]; + /** error protection methods for packets (0,1,16,32,37-128) */ + int jpwl_pprot[JPWL_MAX_NO_PACKSPECS]; + /** enables writing of ESD, (0=no/1/2 bytes) */ + int jpwl_sens_size; + /** sensitivity addressing size (0=auto/2/4 bytes) */ + int jpwl_sens_addr; + /** sensitivity range (0-3) */ + int jpwl_sens_range; + /** sensitivity method for MH (-1=no,0-7) */ + int jpwl_sens_MH; + /** tile number of sensitivity specification (>=0) */ + int jpwl_sens_TPH_tileno[JPWL_MAX_NO_TILESPECS]; + /** sensitivity methods for TPHs (-1=no,0-7) */ + int jpwl_sens_TPH[JPWL_MAX_NO_TILESPECS]; + /*@}*/ +/* <<UniPG */ + + /** Digital Cinema compliance 0-not compliant, 1-compliant*/ + OPJ_CINEMA_MODE cp_cinema; + /** Maximum rate for each component. If == 0, component size limitation is not considered */ + int max_comp_size; + /** Profile name*/ + OPJ_RSIZ_CAPABILITIES cp_rsiz; + /** Tile part generation*/ + char tp_on; + /** Flag for Tile part generation*/ + char tp_flag; + /** MCT (multiple component transform) */ + char tcp_mct; +} opj_cparameters_t; + +/** +Decompression parameters +*/ +typedef struct opj_dparameters { + /** + Set the number of highest resolution levels to be discarded. + The image resolution is effectively divided by 2 to the power of the number of discarded levels. + The reduce factor is limited by the smallest total number of decomposition levels among tiles. + if != 0, then original dimension divided by 2^(reduce); + if == 0 or not used, image is decoded to the full resolution + */ + int cp_reduce; + /** + Set the maximum number of quality layers to decode. + If there are less quality layers than the specified number, all the quality layers are decoded. + if != 0, then only the first "layer" layers are decoded; + if == 0 or not used, all the quality layers are decoded + */ + int cp_layer; + + /**@name command line encoder parameters (not used inside the library) */ + /*@{*/ + /** input file name */ + char infile[OPJ_PATH_LEN]; + /** output file name */ + char outfile[OPJ_PATH_LEN]; + /** input file format 0: J2K, 1: JP2, 2: JPT */ + int decod_format; + /** output file format 0: PGX, 1: PxM, 2: BMP */ + int cod_format; + /*@}*/ + +/* UniPG>> */ + /**@name JPWL decoding parameters */ + /*@{*/ + /** activates the JPWL correction capabilities */ + bool jpwl_correct; + /** expected number of components */ + int jpwl_exp_comps; + /** maximum number of tiles */ + int jpwl_max_tiles; + /*@}*/ +/* <<UniPG */ + + /** + Specify whether the decoding should be done on the entire codestream, or be limited to the main header + Limiting the decoding to the main header makes it possible to extract the characteristics of the codestream + if == NO_LIMITATION, the entire codestream is decoded; + if == LIMIT_TO_MAIN_HEADER, only the main header is decoded; + */ + OPJ_LIMIT_DECODING cp_limit_decoding; + +} opj_dparameters_t; + +/** Common fields between JPEG-2000 compression and decompression master structs. */ + +#define opj_common_fields \ + opj_event_mgr_t *event_mgr; /**< pointer to the event manager */\ + void * client_data; /**< Available for use by application */\ + bool is_decompressor; /**< So common code can tell which is which */\ + OPJ_CODEC_FORMAT codec_format; /**< selected codec */\ + void *j2k_handle; /**< pointer to the J2K codec */\ + void *jp2_handle; /**< pointer to the JP2 codec */\ + void *mj2_handle /**< pointer to the MJ2 codec */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * opj_common_struct_t, only of opj_cinfo_t and opj_dinfo_t. + */ +typedef struct opj_common_struct { + opj_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual opj_cinfo_t or + * opj_dinfo_t. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +} opj_common_struct_t; + +typedef opj_common_struct_t * opj_common_ptr; + +/** +Compression context info +*/ +typedef struct opj_cinfo { + /** Fields shared with opj_dinfo_t */ + opj_common_fields; + /* other specific fields go here */ +} opj_cinfo_t; + +/** +Decompression context info +*/ +typedef struct opj_dinfo { + /** Fields shared with opj_cinfo_t */ + opj_common_fields; + /* other specific fields go here */ +} opj_dinfo_t; + +/* +========================================================== + I/O stream typedef definitions +========================================================== +*/ + +/* + * Stream open flags. + */ +/** The stream was opened for reading. */ +#define OPJ_STREAM_READ 0x0001 +/** The stream was opened for writing. */ +#define OPJ_STREAM_WRITE 0x0002 + +/** +Byte input-output stream (CIO) +*/ +typedef struct opj_cio { + /** codec context */ + opj_common_ptr cinfo; + + /** open mode (read/write) either OPJ_STREAM_READ or OPJ_STREAM_WRITE */ + int openmode; + /** pointer to the start of the buffer */ + unsigned char *buffer; + /** buffer size in bytes */ + int length; + + /** pointer to the start of the stream */ + unsigned char *start; + /** pointer to the end of the stream */ + unsigned char *end; + /** pointer to the current position */ + unsigned char *bp; +} opj_cio_t; + +/* +========================================================== + image typedef definitions +========================================================== +*/ + +/** +Defines a single image component +*/ +typedef struct opj_image_comp { + /** XRsiz: horizontal separation of a sample of ith component with respect to the reference grid */ + int dx; + /** YRsiz: vertical separation of a sample of ith component with respect to the reference grid */ + int dy; + /** data width */ + int w; + /** data height */ + int h; + /** x component offset compared to the whole image */ + int x0; + /** y component offset compared to the whole image */ + int y0; + /** precision */ + int prec; + /** image depth in bits */ + int bpp; + /** signed (1) / unsigned (0) */ + int sgnd; + /** number of decoded resolution */ + int resno_decoded; + /** number of division by 2 of the out image compared to the original size of image */ + int factor; + /** image component data */ + int *data; +} opj_image_comp_t; + +/** +Defines image data and characteristics +*/ +typedef struct opj_image { + /** XOsiz: horizontal offset from the origin of the reference grid to the left side of the image area */ + int x0; + /** YOsiz: vertical offset from the origin of the reference grid to the top side of the image area */ + int y0; + /** Xsiz: width of the reference grid */ + int x1; + /** Ysiz: height of the reference grid */ + int y1; + /** number of components in the image */ + int numcomps; + /** color space: sRGB, Greyscale or YUV */ + OPJ_COLOR_SPACE color_space; + /** image components */ + opj_image_comp_t *comps; +} opj_image_t; + +/** +Component parameters structure used by the opj_image_create function +*/ +typedef struct opj_image_comptparm { + /** XRsiz: horizontal separation of a sample of ith component with respect to the reference grid */ + int dx; + /** YRsiz: vertical separation of a sample of ith component with respect to the reference grid */ + int dy; + /** data width */ + int w; + /** data height */ + int h; + /** x component offset compared to the whole image */ + int x0; + /** y component offset compared to the whole image */ + int y0; + /** precision */ + int prec; + /** image depth in bits */ + int bpp; + /** signed (1) / unsigned (0) */ + int sgnd; +} opj_image_cmptparm_t; + +/* +========================================================== + Information on the JPEG 2000 codestream +========================================================== +*/ + +/** +Index structure : Information concerning a packet inside tile +*/ +typedef struct opj_packet_info { + /** packet start position (including SOP marker if it exists) */ + int start_pos; + /** end of packet header position (including EPH marker if it exists)*/ + int end_ph_pos; + /** packet end position */ + int end_pos; + /** packet distorsion */ + double disto; +} opj_packet_info_t; + +/** +Index structure : Information concerning tile-parts +*/ +typedef struct opj_tp_info { + /** start position of tile part */ + int tp_start_pos; + /** end position of tile part header */ + int tp_end_header; + /** end position of tile part */ + int tp_end_pos; + /** start packet of tile part */ + int tp_start_pack; + /** number of packets of tile part */ + int tp_numpacks; +} opj_tp_info_t; + +/** +Index structure : information regarding tiles +*/ +typedef struct opj_tile_info { + /** value of thresh for each layer by tile cfr. Marcela */ + double *thresh; + /** number of tile */ + int tileno; + /** start position */ + int start_pos; + /** end position of the header */ + int end_header; + /** end position */ + int end_pos; + /** precinct number for each resolution level (width) */ + int pw[33]; + /** precinct number for each resolution level (height) */ + int ph[33]; + /** precinct size (in power of 2), in X for each resolution level */ + int pdx[33]; + /** precinct size (in power of 2), in Y for each resolution level */ + int pdy[33]; + /** information concerning packets inside tile */ + opj_packet_info_t *packet; + /** add fixed_quality */ + int numpix; + /** add fixed_quality */ + double distotile; + /** number of tile parts */ + int num_tps; + /** information concerning tile parts */ + opj_tp_info_t *tp; +} opj_tile_info_t; + +/* UniPG>> */ +/** +Marker structure +*/ +typedef struct opj_marker_info_t { + /** marker type */ + unsigned short int type; + /** position in codestream */ + int pos; + /** length, marker val included */ + int len; +} opj_marker_info_t; +/* <<UniPG */ + +/** +Index structure of the codestream +*/ +typedef struct opj_codestream_info { + /** maximum distortion reduction on the whole image (add for Marcela) */ + double D_max; + /** packet number */ + int packno; + /** writing the packet in the index with t2_encode_packets */ + int index_write; + /** image width */ + int image_w; + /** image height */ + int image_h; + /** progression order */ + OPJ_PROG_ORDER prog; + /** tile size in x */ + int tile_x; + /** tile size in y */ + int tile_y; + /** */ + int tile_Ox; + /** */ + int tile_Oy; + /** number of tiles in X */ + int tw; + /** number of tiles in Y */ + int th; + /** component numbers */ + int numcomps; + /** number of layer */ + int numlayers; + /** number of decomposition for each component */ + int *numdecompos; +/* UniPG>> */ + /** number of markers */ + int marknum; + /** list of markers */ + opj_marker_info_t *marker; + /** actual size of markers array */ + int maxmarknum; +/* <<UniPG */ + /** main header position */ + int main_head_start; + /** main header position */ + int main_head_end; + /** codestream's size */ + int codestream_size; + /** information regarding tiles inside image */ + opj_tile_info_t *tile; +} opj_codestream_info_t; + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +========================================================== + openjpeg version +========================================================== +*/ + +OPJ_API const char * OPJ_CALLCONV opj_version(void); + +/* +========================================================== + image functions definitions +========================================================== +*/ + +/** +Create an image +@param numcmpts number of components +@param cmptparms components parameters +@param clrspc image color space +@return returns a new image structure if successful, returns NULL otherwise +*/ +OPJ_API opj_image_t* OPJ_CALLCONV opj_image_create(int numcmpts, opj_image_cmptparm_t *cmptparms, OPJ_COLOR_SPACE clrspc); + +/** +Deallocate any resources associated with an image +@param image image to be destroyed +*/ +OPJ_API void OPJ_CALLCONV opj_image_destroy(opj_image_t *image); + +/* +========================================================== + stream functions definitions +========================================================== +*/ + +/** +Open and allocate a memory stream for read / write. +On reading, the user must provide a buffer containing encoded data. The buffer will be +wrapped by the returned CIO handle. +On writing, buffer parameters must be set to 0: a buffer will be allocated by the library +to contain encoded data. +@param cinfo Codec context info +@param buffer Reading: buffer address. Writing: NULL +@param length Reading: buffer length. Writing: 0 +@return Returns a CIO handle if successful, returns NULL otherwise +*/ +OPJ_API opj_cio_t* OPJ_CALLCONV opj_cio_open(opj_common_ptr cinfo, unsigned char *buffer, int length); + +/** +Close and free a CIO handle +@param cio CIO handle to free +*/ +OPJ_API void OPJ_CALLCONV opj_cio_close(opj_cio_t *cio); + +/** +Get position in byte stream +@param cio CIO handle +@return Returns the position in bytes +*/ +OPJ_API int OPJ_CALLCONV cio_tell(opj_cio_t *cio); +/** +Set position in byte stream +@param cio CIO handle +@param pos Position, in number of bytes, from the beginning of the stream +*/ +OPJ_API void OPJ_CALLCONV cio_seek(opj_cio_t *cio, int pos); + +/* +========================================================== + event manager functions definitions +========================================================== +*/ + +OPJ_API opj_event_mgr_t* OPJ_CALLCONV opj_set_event_mgr(opj_common_ptr cinfo, opj_event_mgr_t *event_mgr, void *context); + +/* +========================================================== + codec functions definitions +========================================================== +*/ +/** +Creates a J2K/JPT/JP2 decompression structure +@param format Decoder to select +@return Returns a handle to a decompressor if successful, returns NULL otherwise +*/ +OPJ_API opj_dinfo_t* OPJ_CALLCONV opj_create_decompress(OPJ_CODEC_FORMAT format); +/** +Destroy a decompressor handle +@param dinfo decompressor handle to destroy +*/ +OPJ_API void OPJ_CALLCONV opj_destroy_decompress(opj_dinfo_t *dinfo); +/** +Set decoding parameters to default values +@param parameters Decompression parameters +*/ +OPJ_API void OPJ_CALLCONV opj_set_default_decoder_parameters(opj_dparameters_t *parameters); +/** +Setup the decoder decoding parameters using user parameters. +Decoding parameters are returned in j2k->cp. +@param dinfo decompressor handle +@param parameters decompression parameters +*/ +OPJ_API void OPJ_CALLCONV opj_setup_decoder(opj_dinfo_t *dinfo, opj_dparameters_t *parameters); +/** +Decode an image from a JPEG-2000 codestream +@param dinfo decompressor handle +@param cio Input buffer stream +@return Returns a decoded image if successful, returns NULL otherwise +*/ +OPJ_API opj_image_t* OPJ_CALLCONV opj_decode(opj_dinfo_t *dinfo, opj_cio_t *cio); + +/** +Decode an image from a JPEG-2000 codestream and extract the codestream information +@param dinfo decompressor handle +@param cio Input buffer stream +@param cstr_info Codestream information structure if needed afterwards, NULL otherwise +@return Returns a decoded image if successful, returns NULL otherwise +*/ +OPJ_API opj_image_t* OPJ_CALLCONV opj_decode_with_info(opj_dinfo_t *dinfo, opj_cio_t *cio, opj_codestream_info_t *cstr_info); +/** +Creates a J2K/JP2 compression structure +@param format Coder to select +@return Returns a handle to a compressor if successful, returns NULL otherwise +*/ +OPJ_API opj_cinfo_t* OPJ_CALLCONV opj_create_compress(OPJ_CODEC_FORMAT format); +/** +Destroy a compressor handle +@param cinfo compressor handle to destroy +*/ +OPJ_API void OPJ_CALLCONV opj_destroy_compress(opj_cinfo_t *cinfo); +/** +Set encoding parameters to default values, that means : +<ul> +<li>Lossless +<li>1 tile +<li>Size of precinct : 2^15 x 2^15 (means 1 precinct) +<li>Size of code-block : 64 x 64 +<li>Number of resolutions: 6 +<li>No SOP marker in the codestream +<li>No EPH marker in the codestream +<li>No sub-sampling in x or y direction +<li>No mode switch activated +<li>Progression order: LRCP +<li>No index file +<li>No ROI upshifted +<li>No offset of the origin of the image +<li>No offset of the origin of the tiles +<li>Reversible DWT 5-3 +</ul> +@param parameters Compression parameters +*/ +OPJ_API void OPJ_CALLCONV opj_set_default_encoder_parameters(opj_cparameters_t *parameters); +/** +Setup the encoder parameters using the current image and using user parameters. +@param cinfo Compressor handle +@param parameters Compression parameters +@param image Input filled image +*/ +OPJ_API void OPJ_CALLCONV opj_setup_encoder(opj_cinfo_t *cinfo, opj_cparameters_t *parameters, opj_image_t *image); +/** +Encode an image into a JPEG-2000 codestream +@param cinfo compressor handle +@param cio Output buffer stream +@param image Image to encode +@param index Depreacted -> Set to NULL. To extract index, used opj_encode_wci() +@return Returns true if successful, returns false otherwise +*/ +OPJ_API bool OPJ_CALLCONV opj_encode(opj_cinfo_t *cinfo, opj_cio_t *cio, opj_image_t *image, char *index); +/** +Encode an image into a JPEG-2000 codestream and extract the codestream information +@param cinfo compressor handle +@param cio Output buffer stream +@param image Image to encode +@param cstr_info Codestream information structure if needed afterwards, NULL otherwise +@return Returns true if successful, returns false otherwise +*/ +OPJ_API bool OPJ_CALLCONV opj_encode_with_info(opj_cinfo_t *cinfo, opj_cio_t *cio, opj_image_t *image, opj_codestream_info_t *cstr_info); +/** +Destroy Codestream information after compression or decompression +@param cstr_info Codestream information structure +*/ +OPJ_API void OPJ_CALLCONV opj_destroy_cstr_info(opj_codestream_info_t *cstr_info); + +#ifdef __cplusplus +} +#endif + +#endif /* OPENJPEG_H */ diff --git a/extra_lib/include/png/png.h b/extra_lib/include/png/png.h new file mode 100644 index 0000000..7faa021 --- /dev/null +++ b/extra_lib/include/png/png.h @@ -0,0 +1,3597 @@ +/* png.h - header file for PNG reference library + * + * libpng version 1.2.33 - October 31, 2008 + * Copyright (c) 1998-2008 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.2.33 - October 31, 2008: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 10.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 10.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 10.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 10.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + * 1.2.9beta4-11 13 10209 12.so.0.9[.0] + * 1.2.9rc1 13 10209 12.so.0.9[.0] + * 1.2.9 13 10209 12.so.0.9[.0] + * 1.2.10beta1-8 13 10210 12.so.0.10[.0] + * 1.2.10rc1-3 13 10210 12.so.0.10[.0] + * 1.2.10 13 10210 12.so.0.10[.0] + * 1.2.11beta1-4 13 10211 12.so.0.11[.0] + * 1.0.19rc1-5 10 10019 10.so.0.19[.0] + * 1.2.11rc1-5 13 10211 12.so.0.11[.0] + * 1.0.19 10 10019 10.so.0.19[.0] + * 1.2.11 13 10211 12.so.0.11[.0] + * 1.0.20 10 10020 10.so.0.20[.0] + * 1.2.12 13 10212 12.so.0.12[.0] + * 1.2.13beta1 13 10213 12.so.0.13[.0] + * 1.0.21 10 10021 10.so.0.21[.0] + * 1.2.13 13 10213 12.so.0.13[.0] + * 1.2.14beta1-2 13 10214 12.so.0.14[.0] + * 1.0.22rc1 10 10022 10.so.0.22[.0] + * 1.2.14rc1 13 10214 12.so.0.14[.0] + * 1.0.22 10 10022 10.so.0.22[.0] + * 1.2.14 13 10214 12.so.0.14[.0] + * 1.2.15beta1-6 13 10215 12.so.0.15[.0] + * 1.0.23rc1-5 10 10023 10.so.0.23[.0] + * 1.2.15rc1-5 13 10215 12.so.0.15[.0] + * 1.0.23 10 10023 10.so.0.23[.0] + * 1.2.15 13 10215 12.so.0.15[.0] + * 1.2.16beta1-2 13 10216 12.so.0.16[.0] + * 1.2.16rc1 13 10216 12.so.0.16[.0] + * 1.0.24 10 10024 10.so.0.24[.0] + * 1.2.16 13 10216 12.so.0.16[.0] + * 1.2.17beta1-2 13 10217 12.so.0.17[.0] + * 1.0.25rc1 10 10025 10.so.0.25[.0] + * 1.2.17rc1-3 13 10217 12.so.0.17[.0] + * 1.0.25 10 10025 10.so.0.25[.0] + * 1.2.17 13 10217 12.so.0.17[.0] + * 1.0.26 10 10026 10.so.0.26[.0] + * 1.2.18 13 10218 12.so.0.18[.0] + * 1.2.19beta1-31 13 10219 12.so.0.19[.0] + * 1.0.27rc1-6 10 10027 10.so.0.27[.0] + * 1.2.19rc1-6 13 10219 12.so.0.19[.0] + * 1.0.27 10 10027 10.so.0.27[.0] + * 1.2.19 13 10219 12.so.0.19[.0] + * 1.2.20beta01-04 13 10220 12.so.0.20[.0] + * 1.0.28rc1-6 10 10028 10.so.0.28[.0] + * 1.2.20rc1-6 13 10220 12.so.0.20[.0] + * 1.0.28 10 10028 10.so.0.28[.0] + * 1.2.20 13 10220 12.so.0.20[.0] + * 1.2.21beta1-2 13 10221 12.so.0.21[.0] + * 1.2.21rc1-3 13 10221 12.so.0.21[.0] + * 1.0.29 10 10029 10.so.0.29[.0] + * 1.2.21 13 10221 12.so.0.21[.0] + * 1.2.22beta1-4 13 10222 12.so.0.22[.0] + * 1.0.30rc1 10 10030 10.so.0.30[.0] + * 1.2.22rc1 13 10222 12.so.0.22[.0] + * 1.0.30 10 10030 10.so.0.30[.0] + * 1.2.22 13 10222 12.so.0.22[.0] + * 1.2.23beta01-05 13 10223 12.so.0.23[.0] + * 1.2.23rc01 13 10223 12.so.0.23[.0] + * 1.2.23 13 10223 12.so.0.23[.0] + * 1.2.24beta01-02 13 10224 12.so.0.24[.0] + * 1.2.24rc01 13 10224 12.so.0.24[.0] + * 1.2.24 13 10224 12.so.0.24[.0] + * 1.2.25beta01-06 13 10225 12.so.0.25[.0] + * 1.2.25rc01-02 13 10225 12.so.0.25[.0] + * 1.0.31 10 10031 10.so.0.31[.0] + * 1.2.25 13 10225 12.so.0.25[.0] + * 1.2.26beta01-06 13 10226 12.so.0.26[.0] + * 1.2.26rc01 13 10226 12.so.0.26[.0] + * 1.2.26 13 10226 12.so.0.26[.0] + * 1.0.32 10 10032 10.so.0.32[.0] + * 1.2.27beta01-06 13 10227 12.so.0.27[.0] + * 1.2.27rc01 13 10227 12.so.0.27[.0] + * 1.0.33 10 10033 10.so.0.33[.0] + * 1.2.27 13 10227 12.so.0.27[.0] + * 1.0.34 10 10034 10.so.0.34[.0] + * 1.2.28 13 10228 12.so.0.28[.0] + * 1.2.29beta01-03 13 10229 12.so.0.29[.0] + * 1.2.29rc01 13 10229 12.so.0.29[.0] + * 1.0.35 10 10035 10.so.0.35[.0] + * 1.2.29 13 10229 12.so.0.29[.0] + * 1.0.37 10 10037 10.so.0.37[.0] + * 1.2.30beta01-04 13 10230 12.so.0.30[.0] + * 1.0.38rc01-08 10 10038 10.so.0.38[.0] + * 1.2.30rc01-08 13 10230 12.so.0.30[.0] + * 1.0.38 10 10038 10.so.0.38[.0] + * 1.2.30 13 10230 12.so.0.30[.0] + * 1.0.39rc01-03 10 10039 10.so.0.39[.0] + * 1.2.31rc01-03 13 10231 12.so.0.31[.0] + * 1.0.39 10 10039 10.so.0.39[.0] + * 1.2.31 13 10231 12.so.0.31[.0] + * 1.2.32beta01-02 13 10232 12.so.0.32[.0] + * 1.0.40rc01 10 10040 10.so.0.40[.0] + * 1.2.32rc01 13 10232 12.so.0.32[.0] + * 1.0.40 10 10040 10.so.0.40[.0] + * 1.2.32 13 10232 12.so.0.32[.0] + * 1.2.33beta01-02 13 10233 12.so.0.33[.0] + * 1.2.33rc01-02 13 10233 12.so.0.33[.0] + * 1.0.41rc01 10 10041 10.so.0.41[.0] + * 1.2.33 13 10233 12.so.0.33[.0] + * 1.0.41 10 10041 10.so.0.41[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * <http://www.w3.org/TR/2003/REC-PNG-20031110/ + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: + * + * If you modify libpng you may insert additional notices immediately following + * this sentence. + * + * libpng versions 1.2.6, August 15, 2004, through 1.2.33, October 31, 2008, are + * Copyright (c) 2004, 2006-2008 Glenn Randers-Pehrson, and are + * distributed according to the same disclaimer and license as libpng-1.2.5 + * with the following individual added to the list of Contributing Authors: + * + * Cosmin Truta + * + * libpng versions 1.0.7, July 1, 2000, through 1.2.5, October 3, 2002, are + * Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are + * distributed according to the same disclaimer and license as libpng-1.0.6 + * with the following individuals added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Gilles Vollant + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of the + * library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is with + * the user. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998, 1999, 2000 Glenn Randers-Pehrson, and are + * distributed according to the same disclaimer and license as libpng-0.96, + * with the following individuals added to the list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996, 1997 Andreas Dilger + * Distributed according to the same disclaimer and license as libpng-0.88, + * with the following individuals added to the list of Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing Authors + * and Group 42, Inc. disclaim all warranties, expressed or implied, + * including, without limitation, the warranties of merchantability and of + * fitness for any purpose. The Contributing Authors and Group 42, Inc. + * assume no liability for direct, indirect, incidental, special, exemplary, + * or consequential damages, which may result from the use of the PNG + * Reference Library, even if advised of the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and + * must not be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from + * any source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, without + * fee, and encourage the use of this source code as a component to + * supporting the PNG file format in commercial products. If you use this + * source code in a product, acknowledgment is not required but would be + * appreciated. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s",png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * Libpng is OSI Certified Open Source Software. OSI Certified is a + * certification mark of the Open Source Initiative. + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* + * Y2K compliance in libpng: + * ========================= + * + * October 31, 2008 + * + * Since the PNG Development group is an ad-hoc body, we can't make + * an official declaration. + * + * This is your unofficial assurance that libpng from version 0.71 and + * upward through 1.2.33 are Y2K compliant. It is my belief that earlier + * versions were also Y2K compliant. + * + * Libpng only has three year fields. One is a 2-byte unsigned integer + * that will hold years up to 65535. The other two hold the date in text + * format, and will hold years up to 9999. + * + * The integer is + * "png_uint_16 year" in png_time_struct. + * + * The strings are + * "png_charp time_buffer" in png_struct and + * "near_time_buffer", which is a local character string in png.c. + * + * There are seven time-related functions: + * png.c: png_convert_to_rfc_1123() in png.c + * (formerly png_convert_to_rfc_1152() in error) + * png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c + * png_convert_from_time_t() in pngwrite.c + * png_get_tIME() in pngget.c + * png_handle_tIME() in pngrutil.c, called in pngread.c + * png_set_tIME() in pngset.c + * png_write_tIME() in pngwutil.c, called in pngwrite.c + * + * All handle dates properly in a Y2K environment. The + * png_convert_from_time_t() function calls gmtime() to convert from system + * clock time, which returns (year - 1900), which we properly convert to + * the full 4-digit year. There is a possibility that applications using + * libpng are not passing 4-digit years into the png_convert_to_rfc_1123() + * function, or that they are incorrectly passing only a 2-digit year + * instead of "year - 1900" into the png_convert_from_struct_tm() function, + * but this is not under our control. The libpng documentation has always + * stated that it works with 4-digit years, and the APIs have been + * documented as such. + * + * The tIME chunk itself is also Y2K compliant. It uses a 2-byte unsigned + * integer to hold the year, and can hold years as large as 65535. + * + * zlib, upon which libpng depends, is also Y2K compliant. It contains + * no date-related code. + * + * Glenn Randers-Pehrson + * libpng maintainer + * PNG Development Group + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.2.33" +#define PNG_HEADER_VERSION_STRING \ + " libpng version 1.2.33 - October 31, 2008\n" + +#define PNG_LIBPNG_VER_SONUM 0 +#define PNG_LIBPNG_VER_DLLNUM 13 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 2 +#define PNG_LIBPNG_VER_RELEASE 33 +/* This should match the numeric part of the final component of + * PNG_LIBPNG_VER_STRING, omitting any leading zero: */ + +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that would be octal. + * We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here (only + * version 1.0.0 was mis-numbered 100 instead of 10000). From + * version 1.0.1 it's xxyyzz, where x=major, y=minor, z=release */ +#define PNG_LIBPNG_VER 10233 /* 1.2.33 */ + +#ifndef PNG_VERSION_INFO_ONLY +/* include the compression library's header */ +#include "zlib.h" +#endif + +/* include all user configurable info, including optional assembler routines */ +#include "pngconf.h" + +/* + * Added at libpng-1.2.8 */ +/* Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#if defined(PNG_USER_PRIVATEBUILD) +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# if defined(PNG_LIBPNG_SPECIALBUILD) +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* This file is arranged in several sections. The first section contains + * structure and type definitions. The second section contains the external + * library functions, while the third has the internal library functions, + * which applications aren't expected to use directly. + */ + +#ifndef PNG_NO_TYPECAST_NULL +#define int_p_NULL (int *)NULL +#define png_bytep_NULL (png_bytep)NULL +#define png_bytepp_NULL (png_bytepp)NULL +#define png_doublep_NULL (png_doublep)NULL +#define png_error_ptr_NULL (png_error_ptr)NULL +#define png_flush_ptr_NULL (png_flush_ptr)NULL +#define png_free_ptr_NULL (png_free_ptr)NULL +#define png_infopp_NULL (png_infopp)NULL +#define png_malloc_ptr_NULL (png_malloc_ptr)NULL +#define png_read_status_ptr_NULL (png_read_status_ptr)NULL +#define png_rw_ptr_NULL (png_rw_ptr)NULL +#define png_structp_NULL (png_structp)NULL +#define png_uint_16p_NULL (png_uint_16p)NULL +#define png_voidp_NULL (png_voidp)NULL +#define png_write_status_ptr_NULL (png_write_status_ptr)NULL +#else +#define int_p_NULL NULL +#define png_bytep_NULL NULL +#define png_bytepp_NULL NULL +#define png_doublep_NULL NULL +#define png_error_ptr_NULL NULL +#define png_flush_ptr_NULL NULL +#define png_free_ptr_NULL NULL +#define png_infopp_NULL NULL +#define png_malloc_ptr_NULL NULL +#define png_read_status_ptr_NULL NULL +#define png_rw_ptr_NULL NULL +#define png_structp_NULL NULL +#define png_uint_16p_NULL NULL +#define png_voidp_NULL NULL +#define png_write_status_ptr_NULL NULL +#endif + +/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (PNG_CONST char) png_libpng_ver[18]; + /* need room for 99.99.99beta99z */ +#else +#define png_libpng_ver png_get_header_ver(NULL) +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +/* This was removed in version 1.0.5c */ +/* Structures to facilitate easy interlacing. See png.c for more details */ +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_start[7]; +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_inc[7]; +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_ystart[7]; +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_yinc[7]; +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_mask[7]; +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_dsp_mask[7]; +/* This isn't currently used. If you need it, see png.c for more details. +PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_height[7]; +*/ +#endif + +#endif /* PNG_NO_EXTERN */ + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color FAR * png_colorp; +typedef png_color FAR * FAR * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 FAR * png_color_16p; +typedef png_color_16 FAR * FAR * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 FAR * png_color_8p; +typedef png_color_8 FAR * FAR * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry FAR * png_sPLT_entryp; +typedef png_sPLT_entry FAR * FAR * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t FAR * png_sPLT_tp; +typedef png_sPLT_t FAR * FAR * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text", "lang", and + * "lang_key" fields can be regular C strings, empty strings, or NULL pointers. + * However, the * structure returned by png_get_text() will always contain + * regular zero-terminated C strings (possibly empty), never NULL pointers, + * so they can be safely used in printf() and other string-handling functions. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + png_size_t text_length; /* length of the text string */ +#ifdef PNG_iTXt_SUPPORTED + png_size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +#endif +} png_text; +typedef png_text FAR * png_textp; +typedef png_text FAR * FAR * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time FAR * png_timep; +typedef png_time FAR * FAR * png_timepp; + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + */ +#define PNG_CHUNK_NAME_LENGTH 5 +typedef struct png_unknown_chunk_t +{ + png_byte name[PNG_CHUNK_NAME_LENGTH]; + png_byte *data; + png_size_t size; + + /* libpng-using applications should NOT directly modify this byte. */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; +typedef png_unknown_chunk FAR * png_unknown_chunkp; +typedef png_unknown_chunk FAR * FAR * png_unknown_chunkpp; +#endif + +/* png_info is a structure that holds the information in a PNG file so + * that the application can find out the characteristics of the image. + * If you are reading the file, this structure will tell you what is + * in the PNG file. If you are writing the file, fill in the information + * you want to put into the PNG file, then call png_write_info(). + * The names chosen should be very close to the PNG specification, so + * consult that document for information about the meaning of each field. + * + * With libpng < 0.95, it was only possible to directly set and read the + * the values in the png_info_struct, which meant that the contents and + * order of the values had to remain fixed. With libpng 0.95 and later, + * however, there are now functions that abstract the contents of + * png_info_struct from the application, so this makes it easier to use + * libpng with dynamic libraries, and even makes it possible to use + * libraries that don't have all of the libpng ancillary chunk-handing + * functionality. + * + * In any case, the order of the parameters in png_info_struct should NOT + * be changed for as long as possible to keep compatibility with applications + * that use the old direct-access method with png_info_struct. + * + * The following members may have allocated storage attached that should be + * cleaned up before the structure is discarded: palette, trans, text, + * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile, + * splt_palettes, scal_unit, row_pointers, and unknowns. By default, these + * are automatically freed when the info structure is deallocated, if they were + * allocated internally by libpng. This behavior can be changed by means + * of the png_data_freer() function. + * + * More allocation details: all the chunk-reading functions that + * change these members go through the corresponding png_set_* + * functions. A function to clear these members is available: see + * png_free_data(). The png_set_* functions do not depend on being + * able to point info structure members to any of the storage they are + * passed (they make their own copies), EXCEPT that the png_set_text + * functions use the same storage passed to them in the text_ptr or + * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns + * functions do not make their own copies. + */ +typedef struct png_info_struct +{ + /* the following are necessary for every PNG file */ + png_uint_32 width; /* width of image in pixels (from IHDR) */ + png_uint_32 height; /* height of image in pixels (from IHDR) */ + png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */ + png_uint_32 rowbytes; /* bytes needed to hold an untransformed row */ + png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */ + png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */ + png_uint_16 num_trans; /* number of transparent palette color (tRNS) */ + png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */ + png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */ + /* The following three should have been named *_method not *_type */ + png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */ + png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */ + png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + + /* The following is informational only on read, and not used on writes. */ + png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte spare_byte; /* to align the data, and for future use */ + png_byte signature[8]; /* magic bytes read by libpng from start of file */ + + /* The rest of the data is optional. If you are reading, check the + * valid field to see if the information in these are valid. If you + * are writing, set the valid field to those chunks you want written, + * and initialize the appropriate fields below. + */ + +#if defined(PNG_gAMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + /* The gAMA chunk describes the gamma characteristics of the system + * on which the image was created, normally in the range [1.0, 2.5]. + * Data is valid if (valid & PNG_INFO_gAMA) is non-zero. + */ + float gamma; /* gamma value of image, if (valid & PNG_INFO_gAMA) */ +#endif + +#if defined(PNG_sRGB_SUPPORTED) + /* GR-P, 0.96a */ + /* Data valid if (valid & PNG_INFO_sRGB) non-zero. */ + png_byte srgb_intent; /* sRGB rendering intent [0, 1, 2, or 3] */ +#endif + +#if defined(PNG_TEXT_SUPPORTED) + /* The tEXt, and zTXt chunks contain human-readable textual data in + * uncompressed, compressed, and optionally compressed forms, respectively. + * The data in "text" is an array of pointers to uncompressed, + * null-terminated C strings. Each chunk has a keyword that describes the + * textual data contained in that chunk. Keywords are not required to be + * unique, and the text string may be empty. Any number of text chunks may + * be in an image. + */ + int num_text; /* number of comments read/to write */ + int max_text; /* current size of text array */ + png_textp text; /* array of comments read/to write */ +#endif /* PNG_TEXT_SUPPORTED */ + +#if defined(PNG_tIME_SUPPORTED) + /* The tIME chunk holds the last time the displayed image data was + * modified. See the png_time struct for the contents of this struct. + */ + png_time mod_time; +#endif + +#if defined(PNG_sBIT_SUPPORTED) + /* The sBIT chunk specifies the number of significant high-order bits + * in the pixel data. Values are in the range [1, bit_depth], and are + * only specified for the channels in the pixel data. The contents of + * the low-order bits is not specified. Data is valid if + * (valid & PNG_INFO_sBIT) is non-zero. + */ + png_color_8 sig_bit; /* significant bits in color channels */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \ +defined(PNG_READ_BACKGROUND_SUPPORTED) + /* The tRNS chunk supplies transparency data for paletted images and + * other image types that don't need a full alpha channel. There are + * "num_trans" transparency values for a paletted image, stored in the + * same order as the palette colors, starting from index 0. Values + * for the data are in the range [0, 255], ranging from fully transparent + * to fully opaque, respectively. For non-paletted images, there is a + * single color specified that should be treated as fully transparent. + * Data is valid if (valid & PNG_INFO_tRNS) is non-zero. + */ + png_bytep trans; /* transparent values for paletted image */ + png_color_16 trans_values; /* transparent color for non-palette image */ +#endif + +#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + /* The bKGD chunk gives the suggested image background color if the + * display program does not have its own background color and the image + * is needs to composited onto a background before display. The colors + * in "background" are normally in the same color space/depth as the + * pixel data. Data is valid if (valid & PNG_INFO_bKGD) is non-zero. + */ + png_color_16 background; +#endif + +#if defined(PNG_oFFs_SUPPORTED) + /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards + * and downwards from the top-left corner of the display, page, or other + * application-specific co-ordinate space. See the PNG_OFFSET_ defines + * below for the unit types. Valid if (valid & PNG_INFO_oFFs) non-zero. + */ + png_int_32 x_offset; /* x offset on page */ + png_int_32 y_offset; /* y offset on page */ + png_byte offset_unit_type; /* offset units type */ +#endif + +#if defined(PNG_pHYs_SUPPORTED) + /* The pHYs chunk gives the physical pixel density of the image for + * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_ + * defines below). Data is valid if (valid & PNG_INFO_pHYs) is non-zero. + */ + png_uint_32 x_pixels_per_unit; /* horizontal pixel density */ + png_uint_32 y_pixels_per_unit; /* vertical pixel density */ + png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */ +#endif + +#if defined(PNG_hIST_SUPPORTED) + /* The hIST chunk contains the relative frequency or importance of the + * various palette entries, so that a viewer can intelligently select a + * reduced-color palette, if required. Data is an array of "num_palette" + * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST) + * is non-zero. + */ + png_uint_16p hist; +#endif + +#ifdef PNG_cHRM_SUPPORTED + /* The cHRM chunk describes the CIE color characteristics of the monitor + * on which the PNG was created. This data allows the viewer to do gamut + * mapping of the input image to ensure that the viewer sees the same + * colors in the image as the creator. Values are in the range + * [0.0, 0.8]. Data valid if (valid & PNG_INFO_cHRM) non-zero. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float x_white; + float y_white; + float x_red; + float y_red; + float x_green; + float y_green; + float x_blue; + float y_blue; +#endif +#endif + +#if defined(PNG_pCAL_SUPPORTED) + /* The pCAL chunk describes a transformation between the stored pixel + * values and original physical data values used to create the image. + * The integer range [0, 2^bit_depth - 1] maps to the floating-point + * range given by [pcal_X0, pcal_X1], and are further transformed by a + * (possibly non-linear) transformation function given by "pcal_type" + * and "pcal_params" into "pcal_units". Please see the PNG_EQUATION_ + * defines below, and the PNG-Group's PNG extensions document for a + * complete description of the transformations and how they should be + * implemented, and for a description of the ASCII parameter strings. + * Data values are valid if (valid & PNG_INFO_pCAL) non-zero. + */ + png_charp pcal_purpose; /* pCAL chunk description string */ + png_int_32 pcal_X0; /* minimum value */ + png_int_32 pcal_X1; /* maximum value */ + png_charp pcal_units; /* Latin-1 string giving physical units */ + png_charpp pcal_params; /* ASCII strings containing parameter values */ + png_byte pcal_type; /* equation type (see PNG_EQUATION_ below) */ + png_byte pcal_nparams; /* number of parameters given in pcal_params */ +#endif + +/* New members added in libpng-1.0.6 */ +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + /* storage for unknown chunks that the library doesn't recognize. */ + png_unknown_chunkp unknown_chunks; + png_size_t unknown_chunks_num; +#endif + +#if defined(PNG_iCCP_SUPPORTED) + /* iCCP chunk data. */ + png_charp iccp_name; /* profile name */ + png_charp iccp_profile; /* International Color Consortium profile data */ + /* Note to maintainer: should be png_bytep */ + png_uint_32 iccp_proflen; /* ICC profile data length */ + png_byte iccp_compression; /* Always zero */ +#endif + +#if defined(PNG_sPLT_SUPPORTED) + /* data on sPLT chunks (there may be more than one). */ + png_sPLT_tp splt_palettes; + png_uint_32 splt_palettes_num; +#endif + +#if defined(PNG_sCAL_SUPPORTED) + /* The sCAL chunk describes the actual physical dimensions of the + * subject matter of the graphic. The chunk contains a unit specification + * a byte value, and two ASCII strings representing floating-point + * values. The values are width and height corresponsing to one pixel + * in the image. This external representation is converted to double + * here. Data values are valid if (valid & PNG_INFO_sCAL) is non-zero. + */ + png_byte scal_unit; /* unit of physical scale */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + double scal_pixel_width; /* width of one pixel */ + double scal_pixel_height; /* height of one pixel */ +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_charp scal_s_width; /* string containing height */ + png_charp scal_s_height; /* string containing width */ +#endif +#endif + +#if defined(PNG_INFO_IMAGE_SUPPORTED) + /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS) non-zero */ + /* Data valid if (valid & PNG_INFO_IDAT) non-zero */ + png_bytepp row_pointers; /* the image bits */ +#endif + +#if defined(PNG_FIXED_POINT_SUPPORTED) && defined(PNG_gAMA_SUPPORTED) + png_fixed_point int_gamma; /* gamma of image, if (valid & PNG_INFO_gAMA) */ +#endif + +#if defined(PNG_cHRM_SUPPORTED) && defined(PNG_FIXED_POINT_SUPPORTED) + png_fixed_point int_x_white; + png_fixed_point int_y_white; + png_fixed_point int_x_red; + png_fixed_point int_y_red; + png_fixed_point int_x_green; + png_fixed_point int_y_green; + png_fixed_point int_x_blue; + png_fixed_point int_y_blue; +#endif + +} png_info; + +typedef png_info FAR * png_infop; +typedef png_info FAR * FAR * png_infopp; + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((png_size_t)(-1)) +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* PNG_MAX_UINT is deprecated; use PNG_UINT_31_MAX instead. */ +#define PNG_MAX_UINT PNG_UINT_31_MAX +#endif + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_<chunk> defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_uint_32 rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); +#endif +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* WRITE only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf; /* used in png_error */ +#endif + png_error_ptr error_fn; /* function for printing errors and aborting */ + png_error_ptr warning_fn; /* function for printing warnings */ + png_voidp error_ptr; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn; /* function for writing output data */ + png_rw_ptr read_data_fn; /* function for reading input data */ + png_voidp io_ptr; /* ptr to application struct for I/O functions */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr; /* user supplied struct for user transform */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations; /* which transformations to perform */ + + z_stream zstream; /* pointer to decompression structure (below) */ + png_bytep zbuf; /* buffer for zlib */ + png_size_t zbuf_size; /* size of zbuf */ + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ + + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_uint_32 rowbytes; /* size of row in bytes */ + png_uint_32 irowbytes; /* size of current interlaced row in bytes */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_bytep prev_row; /* buffer to save previous (unfiltered) row */ + png_bytep row_buf; /* buffer to save current (unfiltered) row */ +#ifndef PNG_NO_WRITE_FILTER + png_bytep sub_row; /* buffer to save "sub" row when filtering */ + png_bytep up_row; /* buffer to save "up" row when filtering */ + png_bytep avg_row; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ +#endif + png_row_info row_info; /* used for transformation routines */ + + png_uint_32 idat_size; /* current IDAT size for read */ + png_uint_32 crc; /* current chunk CRC value */ + png_colorp palette; /* palette from the input file */ + png_uint_16 num_palette; /* number of color entries in palette */ + png_uint_16 num_trans; /* number of transparency values */ + png_byte chunk_name[5]; /* null-terminated name of current chunk */ + png_byte compression; /* file compression type (always 0) */ + png_byte filter; /* file filter type (always 0) */ + png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass; /* current interlace pass (0 - 6) */ + png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type; /* color type of file */ + png_byte bit_depth; /* bit depth of file */ + png_byte usr_bit_depth; /* bit depth of users row */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte channels; /* number of channels in file */ + png_byte usr_channels; /* channels at start of write */ + png_byte sig_bytes; /* magic bytes read/written from start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +#ifdef PNG_LEGACY_SUPPORTED + png_byte filler; /* filler byte for pixel expansion */ +#else + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif +#endif + +#if defined(PNG_bKGD_SUPPORTED) + png_byte background_gamma_type; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma; +# endif + png_color_16 background; /* background color in screen gamma space */ +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_flush_ptr output_flush_fn;/* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift; /* number of "insignificant" bits 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma; /* file gamma value */ + float screen_gamma; /* screen gamma value (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_bytep gamma_to_1; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ + png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans; /* transparency values for paletted files */ + png_color_16 trans_values; /* transparency values for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after each prog. row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + png_size_t save_buffer_size; /* amount of data now in save_buffer */ + png_size_t save_buffer_max; /* total size of save_buffer */ + png_size_t buffer_size; /* total amount of available input data */ + png_size_t current_buffer_size; /* amount of data now in current_buffer */ + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ + +# if defined(PNG_TEXT_SUPPORTED) + png_size_t current_text_size; /* current size of text input data */ + png_size_t current_text_left; /* how much text left to read in input */ + png_charp current_text; /* current text chunk buffer */ + png_charp current_text_ptr; /* current location in current_text */ +# endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* for the Borland special 64K segment handler */ + png_bytepp offset_table_ptr; + png_bytep offset_table; + png_uint_16 offset_table_number; + png_uint_16 offset_table_count; + png_uint_16 offset_table_count_free; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + png_bytep palette_lookup; /* lookup table for dithering */ + png_bytep dither_index; /* index translation for palette files */ +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist; /* histogram */ +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_byte heuristic_method; /* heuristic for row filter selection */ + png_byte num_prev_filters; /* number of weights for previous rows */ + png_bytep prev_filters; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_charp time_buffer; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) + png_voidp user_chunk_ptr; + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + int num_chunk_list; + png_bytep chunk_list; +#endif + +/* New members added in libpng-1.0.3 */ +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_byte rgb_to_gray_status; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff; + png_uint_16 rgb_to_gray_green_coeff; + png_uint_16 rgb_to_gray_blue_coeff; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* changed from png_byte to png_uint_32 at version 1.2.0 */ +#ifdef PNG_1_0_X + png_byte mng_features_permitted; +#else + png_uint_32 mng_features_permitted; +#endif /* PNG_1_0_X */ +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_byte filter_type; +#endif + +#if defined(PNG_1_0_X) +/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ + png_uint_32 row_buf_size; +#endif + +/* New members added in libpng-1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +# if !defined(PNG_1_0_X) +# if defined(PNG_MMX_CODE_SUPPORTED) + png_byte mmx_bitdepth_threshold; + png_uint_32 mmx_rowbytes_threshold; +# endif + png_uint_32 asm_flags; +# endif +#endif + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn; /* function for allocating memory */ + png_free_ptr free_fn; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep dither_sort; /* working sort array */ + png_bytep index_to_palette; /* where the original index currently is */ + /* in the palette */ + png_bytep palette_to_index; /* which original index points to this */ + /* palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type; + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max; + png_uint_32 user_height_max; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + /* storage for unknown chunk that the library doesn't recognize. */ + png_unknown_chunk unknown_chunk; +#endif + +/* New members added in libpng-1.2.26 */ + png_uint_32 old_big_row_buf_size, old_prev_row_size; + +/* New member added in libpng-1.2.30 */ + png_charp chunkdata; /* buffer for reading chunk data */ + +}; + + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_2_33; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first <num_bytes> magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)); + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +extern PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +extern PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_uint_32 size)); +#endif + +/* Reset the compression stream */ +extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +extern PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +#endif + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +extern PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)); + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize the info structure (old interface - DEPRECATED) */ +extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)); +#undef png_info_init +#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ + png_sizeof(png_info)); +#endif + +extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the information before the actual image data. */ +extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* convert from a struct tm to png_time */ +extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* convert from time_t to png_time. Uses gmtime() */ +extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_WRITE_tIME_SUPPORTED */ +#endif /* _WIN32_WCE */ + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)); +#endif +extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated */ +extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)); +#endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* Expand the grayscale to 24-bit RGB if necessary. */ +extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip the second byte of information from a 16-bit depth file. */ +extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Turn on dithering, and reduce the palette to the number of colors available. */ +extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_dither)); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ +/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ +extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, + int empty_plte_permitted)); +#endif +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set how many lines between output flushes - 0 for no flushing */ +extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* optional update palette with requested transformations */ +extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* optional call to update the users info structure */ +extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read one or more rows of image data. */ +extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read a row of data. */ +extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the whole image into memory at once. */ +extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* write a row of image data */ +extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* write a few rows of image data */ +extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* write the image data */ +extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* writes the end of the PNG file. */ +extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the end of the PNG file. */ +extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* free any memory associated with the png_info_struct */ +extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* free all memory used by the read (old method - NOT DLL EXPORTED) */ +extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ +extern void png_write_destroy PNGARG((png_structp png_ptr)); + +/* set the libpng method of handling chunk CRC errors */ +extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +extern PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +extern PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +extern PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#if !defined(PNG_NO_STDIO) +/* Initialize the input/output for the PNG file to the default functions. */ +extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + */ +extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* returns the user pointer associated with the push read functions */ +extern PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* function to be called when data becomes available */ +extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_uint_32 size)); + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* Added at libpng version 1.2.4 */ +extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_uint_32 size)); +#endif + +/* frees a pointer allocated by png_malloc() */ +extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +#if defined(PNG_1_0_X) +/* Function to allocate memory for zlib. */ +extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items, + uInt size)); + +/* Function to free memory for zlib */ +extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr)); +#endif + +/* Free data that was allocated internally */ +extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +#ifdef PNG_FREE_ME_SUPPORTED +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application */ +extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +#endif +/* assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_uint_32 size)); +extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, + png_voidp s1, png_voidp s2, png_uint_32 size)); + +extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, + png_voidp s1, int value, png_uint_32 size)); + +#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ +extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, + int check)); +#endif /* USE_FAR_KEYWORD */ + +#ifndef PNG_NO_ERROR_TEXT +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); + +/* The same, but the chunk name is prepended to the error string. */ +extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); +#else +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)); +#endif + +#ifndef PNG_NO_WARNINGS +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Non-fatal error in libpng, chunk name is prepended to message. */ +extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_NO_WARNINGS */ + +/* The png_set_<chunk> functions are for storing values in the png_info_struct. + * Similarly, the png_get_<chunk> calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_<chunk> functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* Returns row_pointers, which is an array of pointers to scanlines that was +returned from png_read_png(). */ +extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use +by png_write_png(). */ +extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) +/* png_get_text also returns the number of text chunks in *num_text */ +extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* + * Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#if defined(PNG_TEXT_SUPPORTED) +extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans, int *num_trans, + png_color_16p *trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans, int num_trans, + png_color_16p trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +/* provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behaviour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +extern PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + If you need to turn it off for a chunk that your application has freed, + you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */ +extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* The "params" pointer is currently not used and is for future expansion. */ +extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + */ +#ifdef PNG_DEBUG +#if (PNG_DEBUG > 0) +#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +#include <crtdbg.h> +#if (PNG_DEBUG > 1) +#define png_debug(l,m) _RPT0(_CRT_WARN,m) +#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m,p1) +#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m,p1,p2) +#endif +#else /* PNG_DEBUG_FILE || !_MSC_VER */ +#ifndef PNG_DEBUG_FILE +#define PNG_DEBUG_FILE stderr +#endif /* PNG_DEBUG_FILE */ +#if (PNG_DEBUG > 1) +#define png_debug(l,m) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ +} +#define png_debug1(l,m,p1) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ +} +#define png_debug2(l,m,p1,p2) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ +} +#endif /* (PNG_DEBUG > 1) */ +#endif /* _MSC_VER */ +#endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +#define png_debug(l, m) +#endif +#ifndef png_debug1 +#define png_debug1(l, m, p1) +#endif +#ifndef png_debug2 +#define png_debug2(l, m, p1, p2) +#endif + +extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Added to version 1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if defined(PNG_MMX_CODE_SUPPORTED) +#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 +#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 +#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 +#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 +#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 +#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 +#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ + +#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) +#define PNG_MMX_WRITE_FLAGS ( 0 ) + +#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ + | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ + | PNG_MMX_READ_FLAGS \ + | PNG_MMX_WRITE_FLAGS ) + +#define PNG_SELECT_READ 1 +#define PNG_SELECT_WRITE 2 +#endif /* PNG_MMX_CODE_SUPPORTED */ + +#if !defined(PNG_1_0_X) +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) + PNGARG((int flag_select, int *compilerID)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) + PNGARG((int flag_select)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flags) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) + PNGARG((png_structp png_ptr)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_asm_flags) + PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_mmx_thresholds) + PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold)); + +#endif /* PNG_1_0_X */ + +#if !defined(PNG_1_0_X) +/* png.c, pnggccrd.c, or pngvcrd.c */ +extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +#endif + + +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 - \ + (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L - \ + (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) + +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +/* Inline macros to do direct reads of bytes from the input buffer. These + * require that you are using an architecture that uses PNG byte ordering + * (MSB first) and supports unaligned data storage. I think that PowerPC + * in big-endian mode and 680x0 are the only ones that will support this. + * The x86 line of processors definitely do not. The png_get_int_32() + * routine also assumes we are using two's complement format for negative + * values, which is almost certainly true. + */ +#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED) +# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) +# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) +# define png_get_int_32(buf) ( *((png_int_32p) (buf))) +#else +extern PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); +#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ +extern PNG_EXPORT(png_uint_32,png_get_uint_31) + PNGARG((png_structp png_ptr, png_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). + */ +extern PNG_EXPORT(void,png_save_uint_32) + PNGARG((png_bytep buf, png_uint_32 i)); +extern PNG_EXPORT(void,png_save_int_32) + PNGARG((png_bytep buf, png_int_32 i)); + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +extern PNG_EXPORT(void,png_save_uint_16) + PNGARG((png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ + +/* ************************************************************************* */ + +/* These next functions are used internally in the code. They generally + * shouldn't be used unless you are writing code to add or replace some + * functionality in libpng. More information about most functions can + * be found in the files where the functions are located. + */ + + +/* Various modes of operation, that are visible to applications because + * they are used for unknown chunk location. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ +#define PNG_HAVE_IEND 0x10 + +#if defined(PNG_INTERNAL) + +/* More modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 +#define PNG_HAVE_sRGB 0x80 +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ + +/* flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_DITHER 0x0040 +#define PNG_BACKGROUND 0x0080 +#define PNG_BACKGROUND_EXPAND 0x0100 + /* 0x0200 unused */ +#define PNG_16_TO_8 0x0400 +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000L +#define PNG_PACKSWAP 0x10000L +#define PNG_SWAP_ALPHA 0x20000L +#define PNG_STRIP_ALPHA 0x40000L +#define PNG_INVERT_ALPHA 0x80000L +#define PNG_USER_TRANSFORM 0x100000L +#define PNG_RGB_TO_GRAY_ERR 0x200000L +#define PNG_RGB_TO_GRAY_WARN 0x400000L +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ + /* 0x800000L Unused */ +#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +/* flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_SHIFT 8 +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_SHIFT 3 +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 +#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 +#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 +#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 +#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_FREE_PLTE 0x1000 +#define PNG_FLAG_FREE_TRNS 0x2000 +#define PNG_FLAG_FREE_HIST 0x4000 +#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L +#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ + /* 0x800000L unused */ + /* 0x1000000L unused */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ + (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + ideal-delta..ideal+delta. Each argument is evaluated twice. + "ideal" and "delta" should be constants, normally simple + integers, "value" a variable. Added to libpng-1.2.6 JB */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* place to hold the signature string for a PNG file. */ +#ifdef PNG_USE_GLOBAL_ARRAYS + PNG_EXPORT_VAR (PNG_CONST png_byte FARDATA) png_sig[8]; +#else +#endif +#endif /* PNG_NO_EXTERN */ + +/* Constant strings for known chunk types. If you need to add a chunk, + * define the name here, and add an invocation of the macro in png.c and + * wherever it's needed. + */ +#define PNG_IHDR png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} +#define PNG_IDAT png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} +#define PNG_IEND png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} +#define PNG_PLTE png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} +#define PNG_bKGD png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} +#define PNG_cHRM png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} +#define PNG_gAMA png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} +#define PNG_hIST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} +#define PNG_iCCP png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} +#define PNG_iTXt png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} +#define PNG_oFFs png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} +#define PNG_pCAL png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} +#define PNG_sCAL png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} +#define PNG_pHYs png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} +#define PNG_sBIT png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} +#define PNG_sPLT png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} +#define PNG_sRGB png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} +#define PNG_tEXt png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} +#define PNG_tIME png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} +#define PNG_tRNS png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} +#define PNG_zTXt png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} + +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (png_byte FARDATA) png_IHDR[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IDAT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IEND[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_PLTE[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_bKGD[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_cHRM[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_gAMA[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_hIST[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iCCP[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iTXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_oFFs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pHYs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sBIT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sPLT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sRGB[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tEXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tIME[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tRNS[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_zTXt[5]; +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for reading, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_read_struct instead). + */ +extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)); +#undef png_read_init +#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for writing, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_write_struct instead). + */ +extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)); +#undef png_write_init +#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Allocate memory for an internal libpng struct */ +PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); + +/* Free memory from internal libpng struct */ +PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); + +PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr + malloc_fn, png_voidp mem_ptr)); +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, + png_free_ptr free_fn, png_voidp mem_ptr)); + +/* Free any memory that info_ptr points to and reset struct. */ +PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_1_0_X +/* Function to allocate memory for zlib. */ +PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); + +/* Function to free memory for zlib */ +PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); + +#ifdef PNG_SIZE_T +/* Function to convert a sizeof an item to png_sizeof item */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +#endif + +/* Next four functions are used internally as callbacks. PNGAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ + +PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif + +PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) +PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); +#endif +#endif +#else /* PNG_1_0_X */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif +#endif /* PNG_1_0_X */ + +/* Reset the CRC variable */ +PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); + +/* Write the "data" buffer to whatever output you are using. */ +PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, + png_size_t length)); + +/* Decompress data in a chunk that uses compression */ +#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +PNG_EXTERN void png_decompress_chunk PNGARG((png_structp png_ptr, + int comp_type, png_size_t chunklength, + png_size_t prefix_length, png_size_t *data_length)); +#endif + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, + png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); +#endif + +/* simple function to write the signature */ +PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)); + +/* write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, + png_uint_32 height, + int bit_depth, int color_type, int compression_method, int filter_method, + int interlace_method)); + +PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, + png_uint_32 num_pal)); + +PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point + file_gamma)); +#endif +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, + int color_type)); +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, + double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, + int intent)); +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, + png_charp name, int compression_type, + png_charp profile, int proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, + png_sPLT_tp palette)); +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, + png_color_16p values, int number, int color_type)); +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, + png_color_16p values, int color_type)); +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, + int num_hist)); +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, + png_charp key, png_charpp new_key)); +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len)); +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len, int compression)); +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, + int compression, png_charp key, png_charp lang, png_charp lang_key, + png_charp text)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) /* Added at version 1.0.14 and 1.2.4 */ +PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type)); +#endif + +#if defined(PNG_WRITE_pCAL_SUPPORTED) +PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, + png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params)); +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type)); +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, + png_timep mod_time)); +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, + int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, + int unit, png_charp width, png_charp height)); +#endif +#endif +#endif + +/* Called when finished processing a row of data */ +PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); + +/* Internal use only. Called before first row of data */ +PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)); +#endif + +/* combine a row of data, dealing with alpha, etc. if requested */ +PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, + int mask)); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) +/* expand an interlaced row */ +/* OLD pre-1.0.9 interface: +PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations)); + */ +PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* grab pixels out of a row for an interlaced pass */ +PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass)); +#endif + +/* unfilter a row */ +PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, + png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); + +/* Choose the best filter to use and filter the row data */ +PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, + png_row_infop row_info)); + +/* Write out the filtered row. */ +PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, + png_bytep filtered_row)); +/* finish a row while reading, dealing with interlacing passes, etc. */ +PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); + +/* initialize the row buffers, etc. */ +PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); +/* optional call to update the users info structure */ +PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +/* these are the functions that do the transformations */ +#if defined(PNG_READ_FILLER_SUPPORTED) +PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop + row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) +PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p sig_bits)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, + png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup)); + +# if defined(PNG_CORRECT_PALETTE_SUPPORTED) +PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette)); +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) +PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth)); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p bit_depth)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background, + png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift)); +#else +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background)); +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift)); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, + png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); +PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, + png_bytep row, png_color_16p trans_value)); +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* decode the IHDR chunk */ +PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); + +#if defined(PNG_READ_bKGD_SUPPORTED) +PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_gAMA_SUPPORTED) +PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_iCCP_SUPPORTED) +extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sPLT_SUPPORTED) +extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_sRGB_SUPPORTED) +PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tRNS_SUPPORTED) +PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); + +PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, + png_bytep chunk_name)); + +/* handle the transformations for reading and writing */ +PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); + +PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, + png_uint_32 length)); +PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); +PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if defined(PNG_MMX_CODE_SUPPORTED) +/* png.c */ /* PRIVATE */ +PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)); +#endif +#endif + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_x_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_y_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_x_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_y_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_pHYs_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pHYs_dpi PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* Read the chunk header (length + type name) */ +PNG_EXTERN png_uint_32 png_read_chunk_header PNGARG((png_structp png_ptr)); + +/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ + +#endif /* PNG_INTERNAL */ + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* do not put anything past this line */ +#endif /* PNG_H */ diff --git a/extra_lib/include/png/pngconf.h b/extra_lib/include/png/pngconf.h new file mode 100644 index 0000000..a59102c --- /dev/null +++ b/extra_lib/include/png/pngconf.h @@ -0,0 +1,1487 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.2.33 - October 31, 2008 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2008 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +/* Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#define PNG_1_2_X + +/* + * PNG_USER_CONFIG has to be defined on the compiler command line. This + * includes the resource compiler for Windows DLL configurations. + */ +#ifdef PNG_USER_CONFIG +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD +# endif +#include "pngusr.h" +#endif + +/* PNG_CONFIGURE_LIBPNG is set by the "configure" script. */ +#ifdef PNG_CONFIGURE_LIBPNG +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#endif + +/* + * Added at libpng-1.2.8 + * + * If you create a private DLL you need to define in "pngusr.h" the followings: + * #define PNG_USER_PRIVATEBUILD <Describes by whom and why this version of + * the DLL was built> + * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." + * #define PNG_USER_DLLFNAME_POSTFIX <two-letter postfix that serve to + * distinguish your DLL from those of the official release. These + * correspond to the trailing letters that come after the version + * number and must match your private DLL name> + * e.g. // private DLL "libpng13gx.dll" + * #define PNG_USER_DLLFNAME_POSTFIX "gx" + * + * The following macros are also at your disposal if you want to complete the + * DLL VERSIONINFO structure. + * - PNG_USER_VERSIONINFO_COMMENTS + * - PNG_USER_VERSIONINFO_COMPANYNAME + * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS + */ + +#ifdef __STDC__ +#ifdef SPECIALBUILD +# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ + are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") +#endif + +#ifdef PRIVATEBUILD +# pragma message("PRIVATEBUILD is deprecated.\ + Use PNG_USER_PRIVATEBUILD instead.") +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +#endif +#endif /* __STDC__ */ + +#ifndef PNG_VERSION_INFO_ONLY + +/* End of material added to libpng-1.2.8 */ + +/* Added at libpng-1.2.19, removed at libpng-1.2.20 because it caused trouble + Restored at libpng-1.2.21 */ +#if !defined(PNG_NO_WARN_UNINITIALIZED_ROW) && \ + !defined(PNG_WARN_UNINITIALIZED_ROW) +# define PNG_WARN_UNINITIALIZED_ROW 1 +#endif +/* End of material added at libpng-1.2.19/1.2.21 */ + +/* This is the size of the compression buffer, and thus the size of + * an IDAT chunk. Make this whatever size you feel is best for your + * machine. One of these will be allocated per png_struct. When this + * is full, it writes the data to the disk, and does some other + * calculations. Making this an extremely small size will slow + * the library down, but you may want to experiment to determine + * where it becomes significant, if you are concerned with memory + * usage. Note that zlib allocates at least 32Kb also. For readers, + * this describes the size of the buffer available to read the data in. + * Unless this gets smaller than the size of a row (compressed), + * it should not make much difference how big this is. + */ + +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif + +/* Enable if you want a write-only libpng */ + +#ifndef PNG_NO_READ_SUPPORTED +# define PNG_READ_SUPPORTED +#endif + +/* Enable if you want a read-only libpng */ + +#ifndef PNG_NO_WRITE_SUPPORTED +# define PNG_WRITE_SUPPORTED +#endif + +/* Enabled by default in 1.2.0. You can disable this if you don't need to + support PNGs that are embedded in MNG datastreams */ +#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES) +# ifndef PNG_MNG_FEATURES_SUPPORTED +# define PNG_MNG_FEATURES_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_FLOATING_POINT_SUPPORTED +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FLOATING_POINT_SUPPORTED +# endif +#endif + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. +#define PNG_MAX_MALLOC_64K + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +/* Special munging to support doing things the 'cygwin' way: + * 'Normal' png-on-win32 defines/defaults: + * PNG_BUILD_DLL -- building dll + * PNG_USE_DLL -- building an application, linking to dll + * (no define) -- building static library, or building an + * application and linking to the static lib + * 'Cygwin' defines/defaults: + * PNG_BUILD_DLL -- (ignored) building the dll + * (no define) -- (ignored) building an application, linking to the dll + * PNG_STATIC -- (ignored) building the static lib, or building an + * application that links to the static lib. + * ALL_STATIC -- (ignored) building various static libs, or building an + * application that links to the static libs. + * Thus, + * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and + * this bit of #ifdefs will define the 'correct' config variables based on + * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but + * unnecessary. + * + * Also, the precedence order is: + * ALL_STATIC (since we can't #undef something outside our namespace) + * PNG_BUILD_DLL + * PNG_STATIC + * (nothing) == PNG_USE_DLL + * + * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent + * of auto-import in binutils, we no longer need to worry about + * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, + * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes + * to __declspec() stuff. However, we DO need to worry about + * PNG_BUILD_DLL and PNG_STATIC because those change some defaults + * such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed. + */ +#if defined(__CYGWIN__) +# if defined(ALL_STATIC) +# if defined(PNG_BUILD_DLL) +# undef PNG_BUILD_DLL +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# if !defined(PNG_STATIC) +# define PNG_STATIC +# endif +# else +# if defined (PNG_BUILD_DLL) +# if defined(PNG_STATIC) +# undef PNG_STATIC +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# else +# if defined(PNG_STATIC) +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# else +# if !defined(PNG_USE_DLL) +# define PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# endif +# endif +# endif +#endif + +/* This protects us against compilers that run on a windowing system + * and thus don't have or would rather us not use the stdio types: + * stdin, stdout, and stderr. The only one currently used is stderr + * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will + * prevent these from being compiled and used. #defining PNG_NO_STDIO + * will also prevent these, plus will prevent the entire set of stdio + * macros and functions (FILE *, printf, etc.) from being compiled and used, + * unless (PNG_DEBUG > 0) has been #defined. + * + * #define PNG_NO_CONSOLE_IO + * #define PNG_NO_STDIO + */ + +#if defined(_WIN32_WCE) +# include <windows.h> + /* Console I/O functions are not supported on WindowsCE */ +# define PNG_NO_CONSOLE_IO +# ifdef PNG_DEBUG +# undef PNG_DEBUG +# endif +#endif + +#ifdef PNG_BUILD_DLL +# ifndef PNG_CONSOLE_IO_SUPPORTED +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# endif +#endif + +# ifdef PNG_NO_STDIO +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# include <stdio.h> +# endif +# endif +# else +# if !defined(_WIN32_WCE) +/* "stdio.h" functions are not supported on WindowsCE */ +# include <stdio.h> +# endif +# endif + +/* This macro protects us against machines that don't have function + * prototypes (ie K&R style headers). If your compiler does not handle + * function prototypes, define this macro and use the included ansi2knr. + * I've always been able to use _NO_PROTO as the indicator, but you may + * need to drag the empty declaration out in front of here, or change the + * ifdef to suit your own needs. + */ +#ifndef PNGARG + +#ifdef OF /* zlib prototype munger */ +# define PNGARG(arglist) OF(arglist) +#else + +#ifdef _NO_PROTO +# define PNGARG(arglist) () +# ifndef PNG_TYPECAST_NULL +# define PNG_TYPECAST_NULL +# endif +#else +# define PNGARG(arglist) arglist +#endif /* _NO_PROTO */ + + +#endif /* OF */ + +#endif /* PNGARG */ + +/* Try to determine if we are compiling on a Mac. Note that testing for + * just __MWERKS__ is not good enough, because the Codewarrior is now used + * on non-Mac platforms. + */ +#ifndef MACOS +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) +# define MACOS +# endif +#endif + +/* enough people need this for various reasons to include it here */ +#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) +# include <sys/types.h> +#endif + +#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) +# define PNG_SETJMP_SUPPORTED +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This is an attempt to force a single setjmp behaviour on Linux. If + * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. + */ + +# ifdef __linux__ +# ifdef _BSD_SOURCE +# define PNG_SAVE_BSD_SOURCE +# undef _BSD_SOURCE +# endif +# ifdef _SETJMP_H + /* If you encounter a compiler error here, see the explanation + * near the end of INSTALL. + */ + __pngconf.h__ already includes setjmp.h; + __dont__ include it again.; +# endif +# endif /* __linux__ */ + + /* include setjmp.h for error handling */ +# include <setjmp.h> + +# ifdef __linux__ +# ifdef PNG_SAVE_BSD_SOURCE +# ifndef _BSD_SOURCE +# define _BSD_SOURCE +# endif +# undef PNG_SAVE_BSD_SOURCE +# endif +# endif /* __linux__ */ +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef BSD +# include <strings.h> +#else +# include <string.h> +#endif + +/* Other defines for things like memory and the like can go here. */ +#ifdef PNG_INTERNAL + +#include <stdlib.h> + +/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which + * aren't usually used outside the library (as far as I know), so it is + * debatable if they should be exported at all. In the future, when it is + * possible to have run-time registry of chunk-handling functions, some of + * these will be made available again. +#define PNG_EXTERN extern + */ +#define PNG_EXTERN + +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ + +#if defined(PNG_FLOATING_POINT_SUPPORTED) +# if defined(MACOS) + /* We need to check that <math.h> hasn't already been included earlier + * as it seems it doesn't agree with <fp.h>, yet we should really use + * <fp.h> if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include <fp.h> +# endif +# else +# include <math.h> +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include <m68881.h> +# endif +#endif + +/* Codewarrior on NT has linking problems without this. */ +#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) +# define PNG_ALWAYS_EXTERN +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include <mem.h> +# include <alloc.h> +#endif + +/* I have no idea why is this necessary... */ +#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \ + defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__)) +# include <malloc.h> +#endif + +/* This controls how fine the dithering gets. As this allocates + * a largish chunk of memory (32K), those who are not as concerned + * with dithering quality can decrease some or all of these. + */ +#ifndef PNG_DITHER_RED_BITS +# define PNG_DITHER_RED_BITS 5 +#endif +#ifndef PNG_DITHER_GREEN_BITS +# define PNG_DITHER_GREEN_BITS 5 +#endif +#ifndef PNG_DITHER_BLUE_BITS +# define PNG_DITHER_BLUE_BITS 5 +#endif + +/* This controls how fine the gamma correction becomes when you + * are only interested in 8 bits anyway. Increasing this value + * results in more memory being used, and more pow() functions + * being called to fill in the gamma tables. Don't set this value + * less then 8, and even that may not work (I haven't tested it). + */ + +#ifndef PNG_MAX_GAMMA_8 +# define PNG_MAX_GAMMA_8 11 +#endif + +/* This controls how much a difference in gamma we can tolerate before + * we actually start doing gamma conversion. + */ +#ifndef PNG_GAMMA_THRESHOLD +# define PNG_GAMMA_THRESHOLD 0.05 +#endif + +#endif /* PNG_INTERNAL */ + +/* The following uses const char * instead of char * for error + * and warning message functions, so some compilers won't complain. + * If you do not want to use const, define PNG_NO_CONST here. + */ + +#ifndef PNG_NO_CONST +# define PNG_CONST const +#else +# define PNG_CONST +#endif + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to and #undef, and that part of the library will not be compiled. If + * your linker can't find a function, you may want to make sure the + * ability is defined here. Some of these depend upon some others being + * defined. I haven't figured out all the interactions here, so you may + * have to experiment awhile to get everything to compile. If you are + * creating or using a shared library, you probably shouldn't touch this, + * as it will affect the size of the structures, and this will cause bad + * things to happen if the library and/or application ever change. + */ + +/* Any features you will not be using can be undef'ed here */ + +/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user + * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS + * on the compile line, then pick and choose which ones to define without + * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED + * if you only want to have a png-compliant reader/writer but don't need + * any of the extra transformations. This saves about 80 kbytes in a + * typical installation of the library. (PNG_NO_* form added in version + * 1.0.1c, for consistency) + */ + +/* The size of the png_text structure changed in libpng-1.0.6 when + * iTXt support was added. iTXt support was turned off by default through + * libpng-1.2.x, to support old apps that malloc the png_text structure + * instead of calling png_set_text() and letting libpng malloc it. It + * was turned on by default in libpng-1.3.0. + */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +# ifndef PNG_NO_iTXt_SUPPORTED +# define PNG_NO_iTXt_SUPPORTED +# endif +# ifndef PNG_NO_READ_iTXt +# define PNG_NO_READ_iTXt +# endif +# ifndef PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_iTXt +# endif +#endif + +#if !defined(PNG_NO_iTXt_SUPPORTED) +# if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt) +# define PNG_READ_iTXt +# endif +# if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt) +# define PNG_WRITE_iTXt +# endif +#endif + +/* The following support, added after version 1.0.0, can be turned off here en + * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility + * with old applications that require the length of png_struct and png_info + * to remain unchanged. + */ + +#ifdef PNG_LEGACY_SUPPORTED +# define PNG_NO_FREE_ME +# define PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_NO_READ_USER_CHUNKS +# define PNG_NO_READ_iCCP +# define PNG_NO_WRITE_iCCP +# define PNG_NO_READ_iTXt +# define PNG_NO_WRITE_iTXt +# define PNG_NO_READ_sCAL +# define PNG_NO_WRITE_sCAL +# define PNG_NO_READ_sPLT +# define PNG_NO_WRITE_sPLT +# define PNG_NO_INFO_IMAGE +# define PNG_NO_READ_RGB_TO_GRAY +# define PNG_NO_READ_USER_TRANSFORM +# define PNG_NO_WRITE_USER_TRANSFORM +# define PNG_NO_USER_MEM +# define PNG_NO_READ_EMPTY_PLTE +# define PNG_NO_MNG_FEATURES +# define PNG_NO_FIXED_POINT_SUPPORTED +#endif + +/* Ignore attempt to turn off both floating and fixed point support */ +#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ + !defined(PNG_NO_FIXED_POINT_SUPPORTED) +# define PNG_FIXED_POINT_SUPPORTED +#endif + +#ifndef PNG_NO_FREE_ME +# define PNG_FREE_ME_SUPPORTED +#endif + +#if defined(PNG_READ_SUPPORTED) + +#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_TRANSFORMS) +# define PNG_READ_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_READ_EXPAND +# define PNG_READ_EXPAND_SUPPORTED +# endif +# ifndef PNG_NO_READ_SHIFT +# define PNG_READ_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACK +# define PNG_READ_PACK_SUPPORTED +# endif +# ifndef PNG_NO_READ_BGR +# define PNG_READ_BGR_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP +# define PNG_READ_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACKSWAP +# define PNG_READ_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT +# define PNG_READ_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_READ_DITHER +# define PNG_READ_DITHER_SUPPORTED +# endif +# ifndef PNG_NO_READ_BACKGROUND +# define PNG_READ_BACKGROUND_SUPPORTED +# endif +# ifndef PNG_NO_READ_16_TO_8 +# define PNG_READ_16_TO_8_SUPPORTED +# endif +# ifndef PNG_NO_READ_FILLER +# define PNG_READ_FILLER_SUPPORTED +# endif +# ifndef PNG_NO_READ_GAMMA +# define PNG_READ_GAMMA_SUPPORTED +# endif +# ifndef PNG_NO_READ_GRAY_TO_RGB +# define PNG_READ_GRAY_TO_RGB_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP_ALPHA +# define PNG_READ_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT_ALPHA +# define PNG_READ_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_STRIP_ALPHA +# define PNG_READ_STRIP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_USER_TRANSFORM +# define PNG_READ_USER_TRANSFORM_SUPPORTED +# endif +# ifndef PNG_NO_READ_RGB_TO_GRAY +# define PNG_READ_RGB_TO_GRAY_SUPPORTED +# endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_PROGRESSIVE_READ) && \ + !defined(PNG_PROGRESSIVE_READ_SUPPORTED) /* if you don't do progressive */ +# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ +#endif /* about interlacing capability! You'll */ + /* still have interlacing unless you change the following line: */ + +#define PNG_READ_INTERLACING_SUPPORTED /* required in PNG-compliant decoders */ + +#ifndef PNG_NO_READ_COMPOSITE_NODIV +# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ +# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ +# endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, will be removed from version 2.0.0. + Use PNG_MNG_FEATURES_SUPPORTED instead. */ +#ifndef PNG_NO_READ_EMPTY_PLTE +# define PNG_READ_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_WRITE_SUPPORTED) + +# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_TRANSFORMS) +# define PNG_WRITE_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_WRITE_SHIFT +# define PNG_WRITE_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACK +# define PNG_WRITE_PACK_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_BGR +# define PNG_WRITE_BGR_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_SWAP +# define PNG_WRITE_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACKSWAP +# define PNG_WRITE_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT +# define PNG_WRITE_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_FILLER +# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ +# endif +# ifndef PNG_NO_WRITE_SWAP_ALPHA +# define PNG_WRITE_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT_ALPHA +# define PNG_WRITE_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_USER_TRANSFORM +# define PNG_WRITE_USER_TRANSFORM_SUPPORTED +# endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ + !defined(PNG_WRITE_INTERLACING_SUPPORTED) +#define PNG_WRITE_INTERLACING_SUPPORTED /* not required for PNG-compliant + encoders, but can cause trouble + if left undefined */ +#endif + +#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ + !defined(PNG_WRITE_WEIGHTED_FILTER) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_FLUSH +# define PNG_WRITE_FLUSH_SUPPORTED +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */ +#ifndef PNG_NO_WRITE_EMPTY_PLTE +# define PNG_WRITE_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef PNG_1_0_X +# ifndef PNG_NO_ERROR_NUMBERS +# define PNG_ERROR_NUMBERS_SUPPORTED +# endif +#endif /* PNG_1_0_X */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +# ifndef PNG_NO_USER_TRANSFORM_PTR +# define PNG_USER_TRANSFORM_PTR_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_STDIO +# define PNG_TIME_RFC1123_SUPPORTED +#endif + +/* This adds extra functions in pngget.c for accessing data from the + * info pointer (added in version 0.99) + * png_get_image_width() + * png_get_image_height() + * png_get_bit_depth() + * png_get_color_type() + * png_get_compression_type() + * png_get_filter_type() + * png_get_interlace_type() + * png_get_pixel_aspect_ratio() + * png_get_pixels_per_meter() + * png_get_x_offset_pixels() + * png_get_y_offset_pixels() + * png_get_x_offset_microns() + * png_get_y_offset_microns() + */ +#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) +# define PNG_EASY_ACCESS_SUPPORTED +#endif + +/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 + * and removed from version 1.2.20. The following will be removed + * from libpng-1.4.0 +*/ + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_OPTIMIZED_CODE) +# ifndef PNG_OPTIMIZED_CODE_SUPPORTED +# define PNG_OPTIMIZED_CODE_SUPPORTED +# endif +#endif + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE) +# ifndef PNG_ASSEMBLER_CODE_SUPPORTED +# define PNG_ASSEMBLER_CODE_SUPPORTED +# endif + +# if defined(__GNUC__) && defined(__x86_64__) && (__GNUC__ < 4) + /* work around 64-bit gcc compiler bugs in gcc-3.x */ +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if defined(__APPLE__) +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if (defined(__MWERKS__) && ((__MWERKS__ < 0x0900) || macintosh)) +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_MMX_CODE_SUPPORTED +# endif + +#endif +/* end of obsolete code to be removed from libpng-1.4.0 */ + +#if !defined(PNG_1_0_X) +#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) +# define PNG_USER_MEM_SUPPORTED +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#if !defined(PNG_1_0_X) +#ifndef PNG_SET_USER_LIMITS_SUPPORTED +#if !defined(PNG_NO_SET_USER_LIMITS) && !defined(PNG_SET_USER_LIMITS_SUPPORTED) +# define PNG_SET_USER_LIMITS_SUPPORTED +#endif +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGS no matter + * how large, set these limits to 0x7fffffffL + */ +#ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +#endif +#ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +#endif + +/* These are currently experimental features, define them if you want */ + +/* very little testing */ +/* +#ifdef PNG_READ_SUPPORTED +# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# endif +#endif +*/ + +/* This is only for PowerPC big-endian and 680x0 systems */ +/* some testing */ +/* +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +# define PNG_READ_BIG_ENDIAN_SUPPORTED +#endif +*/ + +/* Buggy compilers (e.g., gcc 2.7.2.2) need this */ +/* +#define PNG_NO_POINTER_INDEXING +*/ + +/* These functions are turned off by default, as they will be phased out. */ +/* +#define PNG_USELESS_TESTS_SUPPORTED +#define PNG_CORRECT_PALETTE_SUPPORTED +*/ + +/* Any chunks you are not interested in, you can undef here. The + * ones that allocate memory may be expecially important (hIST, + * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info + * a bit smaller. + */ + +#if defined(PNG_READ_SUPPORTED) && \ + !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_ANCILLARY_CHUNKS) +# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#if defined(PNG_WRITE_SUPPORTED) && \ + !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) +# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_READ_TEXT +# define PNG_NO_READ_iTXt +# define PNG_NO_READ_tEXt +# define PNG_NO_READ_zTXt +#endif +#ifndef PNG_NO_READ_bKGD +# define PNG_READ_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +#endif +#ifndef PNG_NO_READ_cHRM +# define PNG_READ_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +#endif +#ifndef PNG_NO_READ_gAMA +# define PNG_READ_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +#endif +#ifndef PNG_NO_READ_hIST +# define PNG_READ_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +#endif +#ifndef PNG_NO_READ_iCCP +# define PNG_READ_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +#endif +#ifndef PNG_NO_READ_iTXt +# ifndef PNG_READ_iTXt_SUPPORTED +# define PNG_READ_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_oFFs +# define PNG_READ_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +#endif +#ifndef PNG_NO_READ_pCAL +# define PNG_READ_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_sCAL +# define PNG_READ_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_pHYs +# define PNG_READ_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +#endif +#ifndef PNG_NO_READ_sBIT +# define PNG_READ_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sPLT +# define PNG_READ_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sRGB +# define PNG_READ_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +#endif +#ifndef PNG_NO_READ_tEXt +# define PNG_READ_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_tIME +# define PNG_READ_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +#endif +#ifndef PNG_NO_READ_tRNS +# define PNG_READ_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +#endif +#ifndef PNG_NO_READ_zTXt +# define PNG_READ_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif +#if !defined(PNG_NO_READ_USER_CHUNKS) && \ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# define PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_USER_CHUNKS_SUPPORTED +# ifdef PNG_NO_READ_UNKNOWN_CHUNKS +# undef PNG_NO_READ_UNKNOWN_CHUNKS +# endif +# ifdef PNG_NO_HANDLE_AS_UNKNOWN +# undef PNG_NO_HANDLE_AS_UNKNOWN +# endif +#endif +#ifndef PNG_NO_READ_OPT_PLTE +# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ +#endif /* optional PLTE chunk in RGB and RGBA images */ +#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ + defined(PNG_READ_zTXt_SUPPORTED) +# define PNG_READ_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +#endif + +#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_WRITE_TEXT +# define PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_tEXt +# define PNG_NO_WRITE_zTXt +#endif +#ifndef PNG_NO_WRITE_bKGD +# define PNG_WRITE_bKGD_SUPPORTED +# ifndef PNG_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_cHRM +# define PNG_WRITE_cHRM_SUPPORTED +# ifndef PNG_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_gAMA +# define PNG_WRITE_gAMA_SUPPORTED +# ifndef PNG_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_hIST +# define PNG_WRITE_hIST_SUPPORTED +# ifndef PNG_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iCCP +# define PNG_WRITE_iCCP_SUPPORTED +# ifndef PNG_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iTXt +# ifndef PNG_WRITE_iTXt_SUPPORTED +# define PNG_WRITE_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_oFFs +# define PNG_WRITE_oFFs_SUPPORTED +# ifndef PNG_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pCAL +# define PNG_WRITE_pCAL_SUPPORTED +# ifndef PNG_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sCAL +# define PNG_WRITE_sCAL_SUPPORTED +# ifndef PNG_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pHYs +# define PNG_WRITE_pHYs_SUPPORTED +# ifndef PNG_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sBIT +# define PNG_WRITE_sBIT_SUPPORTED +# ifndef PNG_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sPLT +# define PNG_WRITE_sPLT_SUPPORTED +# ifndef PNG_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sRGB +# define PNG_WRITE_sRGB_SUPPORTED +# ifndef PNG_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tEXt +# define PNG_WRITE_tEXt_SUPPORTED +# ifndef PNG_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tIME +# define PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tRNS +# define PNG_WRITE_tRNS_SUPPORTED +# ifndef PNG_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_zTXt +# define PNG_WRITE_zTXt_SUPPORTED +# ifndef PNG_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +# endif +#endif +#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ + defined(PNG_WRITE_zTXt_SUPPORTED) +# define PNG_WRITE_TEXT_SUPPORTED +# ifndef PNG_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +# endif +#endif + +#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ + +/* Turn this off to disable png_read_png() and + * png_write_png() and leave the row_pointers member + * out of the info structure. + */ +#ifndef PNG_NO_INFO_IMAGE +# define PNG_INFO_IMAGE_SUPPORTED +#endif + +/* need the time information for reading tIME chunks */ +#if defined(PNG_tIME_SUPPORTED) +# if !defined(_WIN32_WCE) + /* "time.h" functions are not supported on WindowsCE */ +# include <time.h> +# endif +#endif + +/* Some typedefs to get us started. These should be safe on most of the + * common platforms. The typedefs should be at least as large as the + * numbers suggest (a png_uint_32 must be at least 32 bits long), but they + * don't have to be exactly that size. Some compilers dislike passing + * unsigned shorts as function parameters, so you may be better off using + * unsigned int for png_uint_16. Likewise, for 64-bit systems, you may + * want to have unsigned int for png_uint_32 instead of unsigned long. + */ + +typedef unsigned long png_uint_32; +typedef long png_int_32; +typedef unsigned short png_uint_16; +typedef short png_int_16; +typedef unsigned char png_byte; + +/* This is usually size_t. It is typedef'ed just in case you need it to + change (I'm not sure if you will or not, so I thought I'd be safe) */ +#ifdef PNG_SIZE_T + typedef PNG_SIZE_T png_size_t; +# define png_sizeof(x) png_convert_size(sizeof(x)) +#else + typedef size_t png_size_t; +# define png_sizeof(x) sizeof(x) +#endif + +/* The following is needed for medium model support. It cannot be in the + * PNG_INTERNAL section. Needs modification for other compilers besides + * MSC. Model independent support declares all arrays and pointers to be + * large using the far keyword. The zlib version used must also support + * model independent data. As of version zlib 1.0.4, the necessary changes + * have been made in zlib. The USE_FAR_KEYWORD define triggers other + * changes that are needed. (Tim Wegner) + */ + +/* Separate compiler dependencies (problem here is that zlib.h always + defines FAR. (SJT) */ +#ifdef __BORLANDC__ +# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) +# define LDATA 1 +# else +# define LDATA 0 +# endif + /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ +# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) +# define PNG_MAX_MALLOC_64K +# if (LDATA != 1) +# ifndef FAR +# define FAR __far +# endif +# define USE_FAR_KEYWORD +# endif /* LDATA != 1 */ + /* Possibly useful for moving data out of default segment. + * Uncomment it if you want. Could also define FARDATA as + * const if your compiler supports it. (SJT) +# define FARDATA FAR + */ +# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ +#endif /* __BORLANDC__ */ + + +/* Suggest testing for specific compiler first before testing for + * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, + * making reliance oncertain keywords suspect. (SJT) + */ + +/* MSC Medium model */ +#if defined(FAR) +# if defined(M_I86MM) +# define USE_FAR_KEYWORD +# define FARDATA FAR +# include <dos.h> +# endif +#endif + +/* SJT: default case */ +#ifndef FAR +# define FAR +#endif + +/* At this point FAR is always defined */ +#ifndef FARDATA +# define FARDATA +#endif + +/* Typedef for floating-point numbers that are converted + to fixed-point with a multiple of 100,000, e.g., int_gamma */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void FAR * png_voidp; +typedef png_byte FAR * png_bytep; +typedef png_uint_32 FAR * png_uint_32p; +typedef png_int_32 FAR * png_int_32p; +typedef png_uint_16 FAR * png_uint_16p; +typedef png_int_16 FAR * png_int_16p; +typedef PNG_CONST char FAR * png_const_charp; +typedef char FAR * png_charp; +typedef png_fixed_point FAR * png_fixed_point_p; + +#ifndef PNG_NO_STDIO +#if defined(_WIN32_WCE) +typedef HANDLE png_FILE_p; +#else +typedef FILE * png_FILE_p; +#endif +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * png_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte FAR * FAR * png_bytepp; +typedef png_uint_32 FAR * FAR * png_uint_32pp; +typedef png_int_32 FAR * FAR * png_int_32pp; +typedef png_uint_16 FAR * FAR * png_uint_16pp; +typedef png_int_16 FAR * FAR * png_int_16pp; +typedef PNG_CONST char FAR * FAR * png_const_charpp; +typedef char FAR * FAR * png_charpp; +typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * FAR * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char FAR * FAR * FAR * png_charppp; + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* SPC - Is this stuff deprecated? */ +/* It'll be removed as of libpng-1.3.0 - GR-P */ +/* libpng typedefs for types in zlib. If zlib changes + * or another compression library is used, then change these. + * Eliminates need to change all the source files. + */ +typedef charf * png_zcharp; +typedef charf * FAR * png_zcharpp; +typedef z_stream FAR * png_zstreamp; +#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */ + +/* + * Define PNG_BUILD_DLL if the module being built is a Windows + * LIBPNG DLL. + * + * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. + * It is equivalent to Microsoft predefined macro _DLL that is + * automatically defined when you compile using the share + * version of the CRT (C Run-Time library) + * + * The cygwin mods make this behavior a little different: + * Define PNG_BUILD_DLL if you are building a dll for use with cygwin + * Define PNG_STATIC if you are building a static library for use with cygwin, + * -or- if you are building an application that you want to link to the + * static library. + * PNG_USE_DLL is defined by default (no user action needed) unless one of + * the other flags is defined. + */ + +#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) +# define PNG_DLL +#endif +/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib. + * When building a static lib, default to no GLOBAL ARRAYS, but allow + * command-line override + */ +#if defined(__CYGWIN__) +# if !defined(PNG_STATIC) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +# else +# if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# endif +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +#endif + +/* Do not use global arrays (helps with building DLL's) + * They are no longer used in libpng itself, since version 1.0.5c, + * but might be required for some pre-1.0.5c applications. + */ +#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# if defined(PNG_NO_GLOBAL_ARRAYS) || \ + (defined(__GNUC__) && defined(PNG_DLL)) || defined(_MSC_VER) +# define PNG_USE_LOCAL_ARRAYS +# else +# define PNG_USE_GLOBAL_ARRAYS +# endif +#endif + +#if defined(__CYGWIN__) +# undef PNGAPI +# define PNGAPI __cdecl +# undef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", + * you may get warnings regarding the linkage of png_zalloc and png_zfree. + * Don't ignore those warnings; you must also reset the default calling + * convention in your compiler to match your PNGAPI, and you must build + * zlib and your applications the same way you build libpng. + */ + +#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) +# ifndef PNG_NO_MODULEDEF +# define PNG_NO_MODULEDEF +# endif +#endif + +#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) +# define PNG_IMPEXP +#endif + +#if defined (__SYMBIAN32__) +# define PNGAPI +#elif defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ + (( defined(_Windows) || defined(_WINDOWS) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) + +# ifndef PNGAPI +# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) +# define PNGAPI __cdecl +# else +# define PNGAPI _cdecl +# endif +# endif + +# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ + 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) +# define PNG_IMPEXP +# endif + +# if !defined(PNG_IMPEXP) + +# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol +# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol + + /* Borland/Microsoft */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) +# define PNG_EXPORT PNG_EXPORT_TYPE1 +# else +# define PNG_EXPORT PNG_EXPORT_TYPE2 +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __export +# else +# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in + VC++ */ +# endif /* Exists in Borland C++ for + C++ classes (== huge) */ +# endif +# endif + +# if !defined(PNG_IMPEXP) +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __declspec(dllexport) +# else +# define PNG_IMPEXP __declspec(dllimport) +# endif +# endif +# endif /* PNG_IMPEXP */ +#else /* !(DLL || non-cygwin WINDOWS) */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# ifndef PNGAPI +# define PNGAPI _System +# endif +# else +# if 0 /* ... other platforms, with other meanings */ +# endif +# endif +#endif + +#ifndef PNGAPI +# define PNGAPI +#endif +#ifndef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef PNG_BUILDSYMS +# ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END +# endif +# ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT +# endif +# endif +#endif + +#ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type +# endif +#endif + +/* User may want to use these so they are not in PNG_INTERNAL. Any library + * functions that are passed far data must be model independent. + */ + +#ifndef PNG_ABORT +# if defined(_WIN32_WCE) +# define PNG_ABORT() +# else +# define PNG_ABORT() abort() +# endif +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED) +#endif + +#if defined(USE_FAR_KEYWORD) /* memory model independent fns */ +/* use this to make far-to-near assignments */ +# define CHECK 1 +# define NOCHECK 0 +# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) +# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) +# define png_snprintf _fsnprintf /* Added to v 1.2.19 */ +# define png_strlen _fstrlen +# define png_memcmp _fmemcmp /* SJT: added */ +# define png_memcpy _fmemcpy +# define png_memset _fmemset +#else /* use the usual functions */ +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# ifndef PNG_NO_SNPRINTF +# ifdef _MSC_VER +# define png_snprintf _snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 _snprintf +# define png_snprintf6 _snprintf +# else +# define png_snprintf snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 snprintf +# define png_snprintf6 snprintf +# endif +# else + /* You don't have or don't want to use snprintf(). Caution: Using + * sprintf instead of snprintf exposes your application to accidental + * or malevolent buffer overflows. If you don't have snprintf() + * as a general rule you should provide one (you can get one from + * Portable OpenSSH). */ +# define png_snprintf(s1,n,fmt,x1) sprintf(s1,fmt,x1) +# define png_snprintf2(s1,n,fmt,x1,x2) sprintf(s1,fmt,x1,x2) +# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ + sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) +# endif +# define png_strlen strlen +# define png_memcmp memcmp /* SJT: added */ +# define png_memcpy memcpy +# define png_memset memset +#endif +/* End of memory model independent support */ + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + +/* Added at libpng-1.2.8 */ +#endif /* PNG_VERSION_INFO_ONLY */ + +#endif /* PNGCONF_H */ diff --git a/extra_lib/include/theora/theora.h b/extra_lib/include/theora/theora.h new file mode 100644 index 0000000..d99c95b --- /dev/null +++ b/extra_lib/include/theora/theora.h @@ -0,0 +1,794 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: theora.h,v 1.2 2008/12/02 18:04:43 jeanlf Exp $ + + ********************************************************************/ + +#ifndef _O_THEORA_H_ +#define _O_THEORA_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include <stddef.h> /* for size_t */ + +#include <ogg/ogg.h> + +/** \defgroup oldfuncs Legacy pre-1.0 C API */ +/* @{ */ + +/** \mainpage + * + * \section intro Introduction + * + * This is the documentation for the libtheora legacy C API, declared in + * the theora.h header, which describes the old interface used before + * the 1.0 release. This API was widely deployed for several years and + * remains supported, but for new code we recommend the cleaner API + * declared in theoradec.h and theoraenc.h. + * + * libtheora is the reference implementation for + * <a href="http://www.theora.org/">Theora</a>, a free video codec. + * Theora is derived from On2's VP3 codec with improved integration for + * Ogg multimedia formats by <a href="http://www.xiph.org/">Xiph.Org</a>. + * + * \section overview Overview + * + * This library will both decode and encode theora packets to/from raw YUV + * frames. In either case, the packets will most likely either come from or + * need to be embedded in an Ogg stream. Use + * <a href="http://xiph.org/ogg/">libogg</a> or + * <a href="http://www.annodex.net/software/liboggz/index.html">liboggz</a> + * to extract/package these packets. + * + * \section decoding Decoding Process + * + * Decoding can be separated into the following steps: + * -# initialise theora_info and theora_comment structures using + * theora_info_init() and theora_comment_init(): + \verbatim + theora_info info; + theora_comment comment; + + theora_info_init(&info); + theora_comment_init(&comment); + \endverbatim + * -# retrieve header packets from Ogg stream (there should be 3) and decode + * into theora_info and theora_comment structures using + * theora_decode_header(). See \ref identification for more information on + * identifying which packets are theora packets. + \verbatim + int i; + for (i = 0; i < 3; i++) + { + (get a theora packet "op" from the Ogg stream) + theora_decode_header(&info, &comment, op); + } + \endverbatim + * -# initialise the decoder based on the information retrieved into the + * theora_info struct by theora_decode_header(). You will need a + * theora_state struct. + \verbatim + theora_state state; + + theora_decode_init(&state, &info); + \endverbatim + * -# pass in packets and retrieve decoded frames! See the yuv_buffer + * documentation for information on how to retrieve raw YUV data. + \verbatim + yuf_buffer buffer; + while (last packet was not e_o_s) { + (get a theora packet "op" from the Ogg stream) + theora_decode_packetin(&state, op); + theora_decode_YUVout(&state, &buffer); + } + \endverbatim + * + * + * \subsection identification Identifying Theora Packets + * + * All streams inside an Ogg file have a unique serial_no attached to the + * stream. Typically, you will want to + * - retrieve the serial_no for each b_o_s (beginning of stream) page + * encountered within the Ogg file; + * - test the first (only) packet on that page to determine if it is a theora + * packet; + * - once you have found a theora b_o_s page then use the retrieved serial_no + * to identify future packets belonging to the same theora stream. + * + * Note that you \e cannot use theora_packet_isheader() to determine if a + * packet is a theora packet or not, as this function does not perform any + * checking beyond whether a header bit is present. Instead, use the + * theora_decode_header() function and check the return value; or examine the + * header bytes at the beginning of the Ogg page. + * + * \subsection example Example Decoder + * + * See <a href="http://svn.xiph.org/trunk/theora/examples/dump_video.c"> + * examples/dump_video.c</a> for a simple decoder implementation. + * + * \section encoding Encoding Process + * + * See <a href="http://svn.xiph.org/trunk/theora/examples/encoder_example.c"> + * examples/encoder_example.c</a> for a simple encoder implementation. + */ + +/** \file + * The libtheora pre-1.0 legacy C API. + */ + +/** + * A YUV buffer for passing uncompressed frames to and from the codec. + * This holds a Y'CbCr frame in planar format. The CbCr planes can be + * subsampled and have their own separate dimensions and row stride + * offsets. Note that the strides may be negative in some + * configurations. For theora the width and height of the largest plane + * must be a multiple of 16. The actual meaningful picture size and + * offset are stored in the theora_info structure; frames returned by + * the decoder may need to be cropped for display. + * + * All samples are 8 bits. Within each plane samples are ordered by + * row from the top of the frame to the bottom. Within each row samples + * are ordered from left to right. + * + * During decode, the yuv_buffer struct is allocated by the user, but all + * fields (including luma and chroma pointers) are filled by the library. + * These pointers address library-internal memory and their contents should + * not be modified. + * + * Conversely, during encode the user allocates the struct and fills out all + * fields. The user also manages the data addressed by the luma and chroma + * pointers. See the encoder_example.c and dump_video.c example files in + * theora/examples/ for more information. + */ +typedef struct { + int y_width; /**< Width of the Y' luminance plane */ + int y_height; /**< Height of the luminance plane */ + int y_stride; /**< Offset in bytes between successive rows */ + + int uv_width; /**< Width of the Cb and Cr chroma planes */ + int uv_height; /**< Height of the chroma planes */ + int uv_stride; /**< Offset between successive chroma rows */ + unsigned char *y; /**< Pointer to start of luminance data */ + unsigned char *u; /**< Pointer to start of Cb data */ + unsigned char *v; /**< Pointer to start of Cr data */ + +} yuv_buffer; + +/** + * A Colorspace. + */ +typedef enum { + OC_CS_UNSPECIFIED, /**< The colorspace is unknown or unspecified */ + OC_CS_ITU_REC_470M, /**< This is the best option for 'NTSC' content */ + OC_CS_ITU_REC_470BG, /**< This is the best option for 'PAL' content */ + OC_CS_NSPACES /**< This marks the end of the defined colorspaces */ +} theora_colorspace; + +/** + * A Chroma subsampling + * + * These enumerate the available chroma subsampling options supported + * by the theora format. See Section 4.4 of the specification for + * exact definitions. + */ +typedef enum { + OC_PF_420, /**< Chroma subsampling by 2 in each direction (4:2:0) */ + OC_PF_RSVD, /**< Reserved value */ + OC_PF_422, /**< Horizonatal chroma subsampling by 2 (4:2:2) */ + OC_PF_444, /**< No chroma subsampling at all (4:4:4) */ +} theora_pixelformat; + +/** + * Theora bitstream info. + * Contains the basic playback parameters for a stream, + * corresponding to the initial 'info' header packet. + * + * Encoded theora frames must be a multiple of 16 in width and height. + * To handle other frame sizes, a crop rectangle is specified in + * frame_height and frame_width, offset_x and * offset_y. The offset + * and size should still be a multiple of 2 to avoid chroma sampling + * shifts. Offset values in this structure are measured from the + * upper left of the image. + * + * Frame rate, in frames per second, is stored as a rational + * fraction. Aspect ratio is also stored as a rational fraction, and + * refers to the aspect ratio of the frame pixels, not of the + * overall frame itself. + * + * See <a href="http://svn.xiph.org/trunk/theora/examples/encoder_example.c"> + * examples/encoder_example.c</a> for usage examples of the + * other paramters and good default settings for the encoder parameters. + */ +typedef struct { + ogg_uint32_t width; /**< encoded frame width */ + ogg_uint32_t height; /**< encoded frame height */ + ogg_uint32_t frame_width; /**< display frame width */ + ogg_uint32_t frame_height; /**< display frame height */ + ogg_uint32_t offset_x; /**< horizontal offset of the displayed frame */ + ogg_uint32_t offset_y; /**< vertical offset of the displayed frame */ + ogg_uint32_t fps_numerator; /**< frame rate numerator **/ + ogg_uint32_t fps_denominator; /**< frame rate denominator **/ + ogg_uint32_t aspect_numerator; /**< pixel aspect ratio numerator */ + ogg_uint32_t aspect_denominator; /**< pixel aspect ratio denominator */ + theora_colorspace colorspace; /**< colorspace */ + int target_bitrate; /**< nominal bitrate in bits per second */ + int quality; /**< Nominal quality setting, 0-63 */ + int quick_p; /**< Quick encode/decode */ + + /* decode only */ + unsigned char version_major; + unsigned char version_minor; + unsigned char version_subminor; + + void *codec_setup; + + /* encode only */ + int dropframes_p; + int keyframe_auto_p; + ogg_uint32_t keyframe_frequency; + ogg_uint32_t keyframe_frequency_force; /* also used for decode init to + get granpos shift correct */ + ogg_uint32_t keyframe_data_target_bitrate; + ogg_int32_t keyframe_auto_threshold; + ogg_uint32_t keyframe_mindistance; + ogg_int32_t noise_sensitivity; + ogg_int32_t sharpness; + + theora_pixelformat pixelformat; /**< chroma subsampling mode to expect */ + +} theora_info; + +/** Codec internal state and context. + */ +typedef struct{ + theora_info *i; + ogg_int64_t granulepos; + + void *internal_encode; + void *internal_decode; + +} theora_state; + +/** + * Comment header metadata. + * + * This structure holds the in-stream metadata corresponding to + * the 'comment' header packet. + * + * Meta data is stored as a series of (tag, value) pairs, in + * length-encoded string vectors. The first occurence of the + * '=' character delimits the tag and value. A particular tag + * may occur more than once. The character set encoding for + * the strings is always UTF-8, but the tag names are limited + * to case-insensitive ASCII. See the spec for details. + * + * In filling in this structure, theora_decode_header() will + * null-terminate the user_comment strings for safety. However, + * the bitstream format itself treats them as 8-bit clean, + * and so the length array should be treated as authoritative + * for their length. + */ +typedef struct theora_comment{ + char **user_comments; /**< An array of comment string vectors */ + int *comment_lengths; /**< An array of corresponding string vector lengths in bytes */ + int comments; /**< The total number of comment string vectors */ + char *vendor; /**< The vendor string identifying the encoder, null terminated */ + +} theora_comment; + + +/**\name theora_control() codes */ + +/**\anchor decctlcodes + * These are the available request codes for theora_control() + * when called with a decoder instance. + * By convention, these are odd, to distinguish them from the + * \ref encctlcodes "encoder control codes". + * Keep any experimental or vendor-specific values above \c 0x8000.*/ + +/**Get the maximum post-processing level. + * The decoder supports a post-processing filter that can improve + * the appearance of the decoded images. This returns the highest + * level setting for this post-processor, corresponding to maximum + * improvement and computational expense. + */ +#define TH_DECCTL_GET_PPLEVEL_MAX (1) + +/**Set the post-processing level. + * Sets the level of post-processing to use when decoding the + * compressed stream. This must be a value between zero (off) + * and the maximum returned by TH_DECCTL_GET_PPLEVEL_MAX. + */ +#define TH_DECCTL_SET_PPLEVEL (3) + +/**Sets the maximum distance between key frames. + * This can be changed during an encode, but will be bounded by + * <tt>1<<th_info#keyframe_granule_shift</tt>. + * If it is set before encoding begins, th_info#keyframe_granule_shift will + * be enlarged appropriately. + * + * \param[in] buf <tt>ogg_uint32_t</tt>: The maximum distance between key + * frames. + * \param[out] buf <tt>ogg_uint32_t</tt>: The actual maximum distance set. + * \retval TH_FAULT \a theora_state or \a buf is <tt>NULL</tt>. + * \retval TH_EINVAL \a buf_sz is not <tt>sizeof(ogg_uint32_t)</tt>. + * \retval TH_IMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE (4) + +/**Set the granule position. + * Call this after a seek, to update the internal granulepos + * in the decoder, to insure that subsequent frames are marked + * properly. If you track timestamps yourself and do not use + * the granule postion returned by the decoder, then you do + * not need to use this control. + */ +#define TH_DECCTL_SET_GRANPOS (5) + + +/**\anchor encctlcodes + * These are the available request codes for theora_control() + * when called with an encoder instance. + * By convention, these are even, to distinguish them from the + * \ref decctlcodes "decoder control codes". + * Keep any experimental or vendor-specific values above \c 0x8000.*/ +/*@{*/ +/**Sets the quantization parameters to use. + * The parameters are copied, not stored by reference, so they can be freed + * after this call. + * <tt>NULL</tt> may be specified to revert to the default parameters. + * For the current encoder, <tt>scale[ci!=0][qi]</tt> must be no greater than + * <tt>scale[ci!=0][qi-1]</tt> and <tt>base[qti][pli][qi][ci]</tt> must be no + * greater than <tt>base[qti][pli][qi-1][ci]</tt>. + * These two conditions ensure that the actual quantizer for a given \a qti, + * \a pli, and \a ci does not increase as \a qi increases. + * + * \param[in] buf #th_quant_info + * \retval TH_FAULT \a theora_state is <tt>NULL</tt>. + * \retval TH_EINVAL Encoding has already begun, the quantization parameters + * do not meet one of the above stated conditions, \a buf + * is <tt>NULL</tt> and \a buf_sz is not zero, or \a buf + * is non-<tt>NULL</tt> and \a buf_sz is not + * <tt>sizeof(#th_quant_info)</tt>. + * \retval TH_IMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_QUANT_PARAMS (2) +/**Disables any encoder features that would prevent lossless transcoding back + * to VP3. + * This primarily means disabling block-level QI values and not using 4MV mode + * when any of the luma blocks in a macro block are not coded. + * It also includes using the VP3 quantization tables and Huffman codes; if you + * set them explicitly after calling this function, the resulting stream will + * not be VP3-compatible. + * If you enable VP3-compatibility when encoding 4:2:2 or 4:4:4 source + * material, or when using a picture region smaller than the full frame (e.g. + * a non-multiple-of-16 width or height), then non-VP3 bitstream features will + * still be disabled, but the stream will still not be VP3-compatible, as VP3 + * was not capable of encoding such formats. + * If you call this after encoding has already begun, then the quantization + * tables and codebooks cannot be changed, but the frame-level features will + * be enabled or disabled as requested. + * + * \param[in] buf <tt>int</tt>: a non-zero value to enable VP3 compatibility, + * or 0 to disable it (the default). + * \param[out] buf <tt>int</tt>: 1 if all bitstream features required for + * VP3-compatibility could be set, and 0 otherwise. + * The latter will be returned if the pixel format is not + * 4:2:0, the picture region is smaller than the full frame, + * or if encoding has begun, preventing the quantization + * tables and codebooks from being set. + * \retval TH_FAULT \a theora_state or \a buf is <tt>NULL</tt>. + * \retval TH_EINVAL \a buf_sz is not <tt>sizeof(int)</tt>. + * \retval TH_IMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_VP3_COMPATIBLE (10) +/**Gets the maximum speed level. + * Higher speed levels favor quicker encoding over better quality per bit. + * Depending on the encoding mode, and the internal algorithms used, quality + * may actually improve, but in this case bitrate will also likely increase. + * In any case, overall rate/distortion performance will probably decrease. + * The maximum value, and the meaning of each value, may change depending on + * the current encoding mode (VBR vs. CQI, etc.). + * + * \param[out] buf int: The maximum encoding speed level. + * \retval TH_FAULT \a theora_state or \a buf is <tt>NULL</tt>. + * \retval TH_EINVAL \a buf_sz is not <tt>sizeof(int)</tt>. + * \retval TH_IMPL Not supported by this implementation in the current + * encoding mode.*/ +#define TH_ENCCTL_GET_SPLEVEL_MAX (12) +/**Sets the speed level. + * By default a speed value of 1 is used. + * + * \param[in] buf int: The new encoding speed level. + * 0 is slowest, larger values use less CPU. + * \retval TH_FAULT \a theora_state or \a buf is <tt>NULL</tt>. + * \retval TH_EINVAL \a buf_sz is not <tt>sizeof(int)</tt>, or the + * encoding speed level is out of bounds. + * The maximum encoding speed level may be + * implementation- and encoding mode-specific, and can be + * obtained via #TH_ENCCTL_GET_SPLEVEL_MAX. + * \retval TH_IMPL Not supported by this implementation in the current + * encoding mode.*/ +#define TH_ENCCTL_SET_SPLEVEL (14) +/*@}*/ + +#define OC_FAULT -1 /**< General failure */ +#define OC_EINVAL -10 /**< Library encountered invalid internal data */ +#define OC_DISABLED -11 /**< Requested action is disabled */ +#define OC_BADHEADER -20 /**< Header packet was corrupt/invalid */ +#define OC_NOTFORMAT -21 /**< Packet is not a theora packet */ +#define OC_VERSION -22 /**< Bitstream version is not handled */ +#define OC_IMPL -23 /**< Feature or action not implemented */ +#define OC_BADPACKET -24 /**< Packet is corrupt */ +#define OC_NEWPACKET -25 /**< Packet is an (ignorable) unhandled extension */ +#define OC_DUPFRAME 1 /**< Packet is a dropped frame */ + +/** + * Retrieve a human-readable string to identify the encoder vendor and version. + * \returns A version string. + */ +extern const char *theora_version_string(void); + +/** + * Retrieve a 32-bit version number. + * This number is composed of a 16-bit major version, 8-bit minor version + * and 8 bit sub-version, composed as follows: +<pre> + (VERSION_MAJOR<<16) + (VERSION_MINOR<<8) + (VERSION_SUB) +</pre> +* \returns The version number. +*/ +extern ogg_uint32_t theora_version_number(void); + +/** + * Initialize the theora encoder. + * \param th The theora_state handle to initialize for encoding. + * \param ti A theora_info struct filled with the desired encoding parameters. + * \retval 0 Success + */ +extern int theora_encode_init(theora_state *th, theora_info *ti); + +/** + * Submit a YUV buffer to the theora encoder. + * \param t A theora_state handle previously initialized for encoding. + * \param yuv A buffer of YUV data to encode. Note that both the yuv_buffer + * struct and the luma/chroma buffers within should be allocated by + * the user. + * \retval OC_EINVAL Encoder is not ready, or is finished. + * \retval -1 The size of the given frame differs from those previously input + * \retval 0 Success + */ +extern int theora_encode_YUVin(theora_state *t, yuv_buffer *yuv); + +/** + * Request the next packet of encoded video. + * The encoded data is placed in a user-provided ogg_packet structure. + * \param t A theora_state handle previously initialized for encoding. + * \param last_p whether this is the last packet the encoder should produce. + * \param op An ogg_packet structure to fill. libtheora will set all + * elements of this structure, including a pointer to encoded + * data. The memory for the encoded data is owned by libtheora. + * \retval 0 No internal storage exists OR no packet is ready + * \retval -1 The encoding process has completed + * \retval 1 Success + */ +extern int theora_encode_packetout( theora_state *t, int last_p, + ogg_packet *op); + +/** + * Request a packet containing the initial header. + * A pointer to the header data is placed in a user-provided ogg_packet + * structure. + * \param t A theora_state handle previously initialized for encoding. + * \param op An ogg_packet structure to fill. libtheora will set all + * elements of this structure, including a pointer to the header + * data. The memory for the header data is owned by libtheora. + * \retval 0 Success + */ +extern int theora_encode_header(theora_state *t, ogg_packet *op); + +/** + * Request a comment header packet from provided metadata. + * A pointer to the comment data is placed in a user-provided ogg_packet + * structure. + * \param tc A theora_comment structure filled with the desired metadata + * \param op An ogg_packet structure to fill. libtheora will set all + * elements of this structure, including a pointer to the encoded + * comment data. The memory for the comment data is owned by + * libtheora. + * \retval 0 Success + */ +extern int theora_encode_comment(theora_comment *tc, ogg_packet *op); + +/** + * Request a packet containing the codebook tables for the stream. + * A pointer to the codebook data is placed in a user-provided ogg_packet + * structure. + * \param t A theora_state handle previously initialized for encoding. + * \param op An ogg_packet structure to fill. libtheora will set all + * elements of this structure, including a pointer to the codebook + * data. The memory for the header data is owned by libtheora. + * \retval 0 Success + */ +extern int theora_encode_tables(theora_state *t, ogg_packet *op); + +/** + * Decode an Ogg packet, with the expectation that the packet contains + * an initial header, comment data or codebook tables. + * + * \param ci A theora_info structure to fill. This must have been previously + * initialized with theora_info_init(). If \a op contains an initial + * header, theora_decode_header() will fill \a ci with the + * parsed header values. If \a op contains codebook tables, + * theora_decode_header() will parse these and attach an internal + * representation to \a ci->codec_setup. + * \param cc A theora_comment structure to fill. If \a op contains comment + * data, theora_decode_header() will fill \a cc with the parsed + * comments. + * \param op An ogg_packet structure which you expect contains an initial + * header, comment data or codebook tables. + * + * \retval OC_BADHEADER \a op is NULL; OR the first byte of \a op->packet + * has the signature of an initial packet, but op is + * not a b_o_s packet; OR this packet has the signature + * of an initial header packet, but an initial header + * packet has already been seen; OR this packet has the + * signature of a comment packet, but the initial header + * has not yet been seen; OR this packet has the signature + * of a comment packet, but contains invalid data; OR + * this packet has the signature of codebook tables, + * but the initial header or comments have not yet + * been seen; OR this packet has the signature of codebook + * tables, but contains invalid data; + * OR the stream being decoded has a compatible version + * but this packet does not have the signature of a + * theora initial header, comments, or codebook packet + * \retval OC_VERSION The packet data of \a op is an initial header with + * a version which is incompatible with this version of + * libtheora. + * \retval OC_NEWPACKET the stream being decoded has an incompatible (future) + * version and contains an unknown signature. + * \retval 0 Success + * + * \note The normal usage is that theora_decode_header() be called on the + * first three packets of a theora logical bitstream in succession. + */ +extern int theora_decode_header(theora_info *ci, theora_comment *cc, + ogg_packet *op); + +/** + * Initialize a theora_state handle for decoding. + * \param th The theora_state handle to initialize. + * \param c A theora_info struct filled with the desired decoding parameters. + * This is of course usually obtained from a previous call to + * theora_decode_header(). + * \retval 0 Success + */ +extern int theora_decode_init(theora_state *th, theora_info *c); + +/** + * Input a packet containing encoded data into the theora decoder. + * \param th A theora_state handle previously initialized for decoding. + * \param op An ogg_packet containing encoded theora data. + * \retval 0 Success + * \retval OC_BADPACKET \a op does not contain encoded video data + */ +extern int theora_decode_packetin(theora_state *th,ogg_packet *op); + +/** + * Output the next available frame of decoded YUV data. + * \param th A theora_state handle previously initialized for decoding. + * \param yuv A yuv_buffer in which libtheora should place the decoded data. + * Note that the buffer struct itself is allocated by the user, but + * that the luma and chroma pointers will be filled in by the + * library. Also note that these luma and chroma regions should be + * considered read-only by the user. + * \retval 0 Success + */ +extern int theora_decode_YUVout(theora_state *th,yuv_buffer *yuv); + +/** + * Report whether a theora packet is a header or not + * This function does no verification beyond checking the header + * flag bit so it should not be used for bitstream identification; + * use theora_decode_header() for that. + * + * \param op An ogg_packet containing encoded theora data. + * \retval 1 The packet is a header packet + * \retval 0 The packet is not a header packet (and so contains frame data) + * + * Thus function was added in the 1.0alpha4 release. + */ +extern int theora_packet_isheader(ogg_packet *op); + +/** + * Report whether a theora packet is a keyframe or not + * + * \param op An ogg_packet containing encoded theora data. + * \retval 1 The packet contains a keyframe image + * \retval 0 The packet is contains an interframe delta + * \retval -1 The packet is not an image data packet at all + * + * Thus function was added in the 1.0alpha4 release. + */ +extern int theora_packet_iskeyframe(ogg_packet *op); + +/** + * Report the granulepos shift radix + * + * When embedded in Ogg, Theora uses a two-part granulepos, + * splitting the 64-bit field into two pieces. The more-significant + * section represents the frame count at the last keyframe, + * and the less-significant section represents the count of + * frames since the last keyframe. In this way the overall + * field is still non-decreasing with time, but usefully encodes + * a pointer to the last keyframe, which is necessary for + * correctly restarting decode after a seek. + * + * This function reports the number of bits used to represent + * the distance to the last keyframe, and thus how the granulepos + * field must be shifted or masked to obtain the two parts. + * + * Since libtheora returns compressed data in an ogg_packet + * structure, this may be generally useful even if the Theora + * packets are not being used in an Ogg container. + * + * \param ti A previously initialized theora_info struct + * \returns The bit shift dividing the two granulepos fields + * + * This function was added in the 1.0alpha5 release. + */ +int theora_granule_shift(theora_info *ti); + +/** + * Convert a granulepos to an absolute frame index, starting at 0. + * The granulepos is interpreted in the context of a given theora_state handle. + * + * Note that while the granulepos encodes the frame count (i.e. starting + * from 1) this call returns the frame index, starting from zero. Thus + * One can calculate the presentation time by multiplying the index by + * the rate. + * + * \param th A previously initialized theora_state handle (encode or decode) + * \param granulepos The granulepos to convert. + * \returns The frame index corresponding to \a granulepos. + * \retval -1 The given granulepos is undefined (i.e. negative) + * + * Thus function was added in the 1.0alpha4 release. + */ +extern ogg_int64_t theora_granule_frame(theora_state *th,ogg_int64_t granulepos); + +/** + * Convert a granulepos to absolute time in seconds. The granulepos is + * interpreted in the context of a given theora_state handle, and gives + * the end time of a frame's presentation as used in Ogg mux ordering. + * + * \param th A previously initialized theora_state handle (encode or decode) + * \param granulepos The granulepos to convert. + * \returns The absolute time in seconds corresponding to \a granulepos. + * This is the "end time" for the frame, or the latest time it should + * be displayed. + * It is not the presentation time. + * \retval -1. The given granulepos is undefined (i.e. negative), or + * \retval -1. The function has been disabled because floating + * point support is not available. + */ +extern double theora_granule_time(theora_state *th,ogg_int64_t granulepos); + +/** + * Initialize a theora_info structure. All values within the given theora_info + * structure are initialized, and space is allocated within libtheora for + * internal codec setup data. + * \param c A theora_info struct to initialize. + */ +extern void theora_info_init(theora_info *c); + +/** + * Clear a theora_info structure. All values within the given theora_info + * structure are cleared, and associated internal codec setup data is freed. + * \param c A theora_info struct to initialize. + */ +extern void theora_info_clear(theora_info *c); + +/** + * Free all internal data associated with a theora_state handle. + * \param t A theora_state handle. + */ +extern void theora_clear(theora_state *t); + +/** + * Initialize an allocated theora_comment structure + * \param tc An allocated theora_comment structure + **/ +extern void theora_comment_init(theora_comment *tc); + +/** + * Add a comment to an initialized theora_comment structure + * \param tc A previously initialized theora comment structure + * \param comment A null-terminated string encoding the comment in the form + * "TAG=the value" + * + * Neither theora_comment_add() nor theora_comment_add_tag() support + * comments containing null values, although the bitstream format + * supports this. To add such comments you will need to manipulate + * the theora_comment structure directly. + **/ + +extern void theora_comment_add(theora_comment *tc, char *comment); + +/** + * Add a comment to an initialized theora_comment structure. + * \param tc A previously initialized theora comment structure + * \param tag A null-terminated string containing the tag + * associated with the comment. + * \param value The corresponding value as a null-terminated string + * + * Neither theora_comment_add() nor theora_comment_add_tag() support + * comments containing null values, although the bitstream format + * supports this. To add such comments you will need to manipulate + * the theora_comment structure directly. + **/ +extern void theora_comment_add_tag(theora_comment *tc, + char *tag, char *value); + +/** + * Look up a comment value by tag. + * \param tc Tn initialized theora_comment structure + * \param tag The tag to look up + * \param count The instance of the tag. The same tag can appear multiple + * times, each with a distinct and ordered value, so an index + * is required to retrieve them all. + * \returns A pointer to the queried tag's value + * \retval NULL No matching tag is found + * + * \note Use theora_comment_query_count() to get the legal range for the + * count parameter. + **/ + +extern char *theora_comment_query(theora_comment *tc, char *tag, int count); + +/** Look up the number of instances of a tag. + * \param tc An initialized theora_comment structure + * \param tag The tag to look up + * \returns The number on instances of a particular tag. + * + * Call this first when querying for a specific tag and then interate + * over the number of instances with separate calls to + * theora_comment_query() to retrieve all instances in order. + **/ +extern int theora_comment_query_count(theora_comment *tc, char *tag); + +/** + * Clear an allocated theora_comment struct so that it can be freed. + * \param tc An allocated theora_comment structure. + **/ +extern void theora_comment_clear(theora_comment *tc); + +/**Encoder control function. + * This is used to provide advanced control the encoding process. + * \param th A #theora_state handle. + * \param req The control code to process. + * See \ref encctlcodes "the list of available control codes" + * for details. + * \param buf The parameters for this control code. + * \param buf_sz The size of the parameter buffer.*/ +extern int theora_control(theora_state *th,int req,void *buf,size_t buf_sz); + +/* @} */ /* end oldfuncs doxygen group */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _O_THEORA_H_ */ diff --git a/extra_lib/include/vorbis/codec.h b/extra_lib/include/vorbis/codec.h new file mode 100644 index 0000000..0357bef --- /dev/null +++ b/extra_lib/include/vorbis/codec.h @@ -0,0 +1,241 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + + ******************************************************************** + + function: libvorbis codec headers + last mod: $Id: codec.h,v 1.2 2008/12/02 18:04:43 jeanlf Exp $ + + ********************************************************************/ + +#ifndef _vorbis_codec_h_ +#define _vorbis_codec_h_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include <ogg/ogg.h> + +typedef struct vorbis_info{ + int version; + int channels; + long rate; + + /* The below bitrate declarations are *hints*. + Combinations of the three values carry the following implications: + + all three set to the same value: + implies a fixed rate bitstream + only nominal set: + implies a VBR stream that averages the nominal bitrate. No hard + upper/lower limit + upper and or lower set: + implies a VBR bitstream that obeys the bitrate limits. nominal + may also be set to give a nominal rate. + none set: + the coder does not care to speculate. + */ + + long bitrate_upper; + long bitrate_nominal; + long bitrate_lower; + long bitrate_window; + + void *codec_setup; +} vorbis_info; + +/* vorbis_dsp_state buffers the current vorbis audio + analysis/synthesis state. The DSP state belongs to a specific + logical bitstream ****************************************************/ +typedef struct vorbis_dsp_state{ + int analysisp; + vorbis_info *vi; + + float **pcm; + float **pcmret; + int pcm_storage; + int pcm_current; + int pcm_returned; + + int preextrapolate; + int eofflag; + + long lW; + long W; + long nW; + long centerW; + + ogg_int64_t granulepos; + ogg_int64_t sequence; + + ogg_int64_t glue_bits; + ogg_int64_t time_bits; + ogg_int64_t floor_bits; + ogg_int64_t res_bits; + + void *backend_state; +} vorbis_dsp_state; + +typedef struct vorbis_block{ + /* necessary stream state for linking to the framing abstraction */ + float **pcm; /* this is a pointer into local storage */ + oggpack_buffer opb; + + long lW; + long W; + long nW; + int pcmend; + int mode; + + int eofflag; + ogg_int64_t granulepos; + ogg_int64_t sequence; + vorbis_dsp_state *vd; /* For read-only access of configuration */ + + /* local storage to avoid remallocing; it's up to the mapping to + structure it */ + void *localstore; + long localtop; + long localalloc; + long totaluse; + struct alloc_chain *reap; + + /* bitmetrics for the frame */ + long glue_bits; + long time_bits; + long floor_bits; + long res_bits; + + void *internal; + +} vorbis_block; + +/* vorbis_block is a single block of data to be processed as part of +the analysis/synthesis stream; it belongs to a specific logical +bitstream, but is independant from other vorbis_blocks belonging to +that logical bitstream. *************************************************/ + +struct alloc_chain{ + void *ptr; + struct alloc_chain *next; +}; + +/* vorbis_info contains all the setup information specific to the + specific compression/decompression mode in progress (eg, + psychoacoustic settings, channel setup, options, codebook + etc). vorbis_info and substructures are in backends.h. +*********************************************************************/ + +/* the comments are not part of vorbis_info so that vorbis_info can be + static storage */ +typedef struct vorbis_comment{ + /* unlimited user comment fields. libvorbis writes 'libvorbis' + whatever vendor is set to in encode */ + char **user_comments; + int *comment_lengths; + int comments; + char *vendor; + +} vorbis_comment; + + +/* libvorbis encodes in two abstraction layers; first we perform DSP + and produce a packet (see docs/analysis.txt). The packet is then + coded into a framed OggSquish bitstream by the second layer (see + docs/framing.txt). Decode is the reverse process; we sync/frame + the bitstream and extract individual packets, then decode the + packet back into PCM audio. + + The extra framing/packetizing is used in streaming formats, such as + files. Over the net (such as with UDP), the framing and + packetization aren't necessary as they're provided by the transport + and the streaming layer is not used */ + +/* Vorbis PRIMITIVES: general ***************************************/ + +extern void vorbis_info_init(vorbis_info *vi); +extern void vorbis_info_clear(vorbis_info *vi); +extern int vorbis_info_blocksize(vorbis_info *vi,int zo); +extern void vorbis_comment_init(vorbis_comment *vc); +extern void vorbis_comment_add(vorbis_comment *vc, char *comment); +extern void vorbis_comment_add_tag(vorbis_comment *vc, + char *tag, char *contents); +extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count); +extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag); +extern void vorbis_comment_clear(vorbis_comment *vc); + +extern int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb); +extern int vorbis_block_clear(vorbis_block *vb); +extern void vorbis_dsp_clear(vorbis_dsp_state *v); +extern double vorbis_granule_time(vorbis_dsp_state *v, + ogg_int64_t granulepos); + +/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/ + +extern int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi); +extern int vorbis_commentheader_out(vorbis_comment *vc, ogg_packet *op); +extern int vorbis_analysis_headerout(vorbis_dsp_state *v, + vorbis_comment *vc, + ogg_packet *op, + ogg_packet *op_comm, + ogg_packet *op_code); +extern float **vorbis_analysis_buffer(vorbis_dsp_state *v,int vals); +extern int vorbis_analysis_wrote(vorbis_dsp_state *v,int vals); +extern int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb); +extern int vorbis_analysis(vorbis_block *vb,ogg_packet *op); + +extern int vorbis_bitrate_addblock(vorbis_block *vb); +extern int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd, + ogg_packet *op); + +/* Vorbis PRIMITIVES: synthesis layer *******************************/ +extern int vorbis_synthesis_idheader(ogg_packet *op); +extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc, + ogg_packet *op); + +extern int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi); +extern int vorbis_synthesis_restart(vorbis_dsp_state *v); +extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op); +extern int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op); +extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb); +extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm); +extern int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm); +extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples); +extern long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op); + +extern int vorbis_synthesis_halfrate(vorbis_info *v,int flag); +extern int vorbis_synthesis_halfrate_p(vorbis_info *v); + +/* Vorbis ERRORS and return codes ***********************************/ + +#define OV_FALSE -1 +#define OV_EOF -2 +#define OV_HOLE -3 + +#define OV_EREAD -128 +#define OV_EFAULT -129 +#define OV_EIMPL -130 +#define OV_EINVAL -131 +#define OV_ENOTVORBIS -132 +#define OV_EBADHEADER -133 +#define OV_EVERSION -134 +#define OV_ENOTAUDIO -135 +#define OV_EBADPACKET -136 +#define OV_EBADLINK -137 +#define OV_ENOSEEK -138 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/extra_lib/include/vorbis/vorbisenc.h b/extra_lib/include/vorbis/vorbisenc.h new file mode 100644 index 0000000..48afcc5 --- /dev/null +++ b/extra_lib/include/vorbis/vorbisenc.h @@ -0,0 +1,112 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: vorbis encode-engine setup + last mod: $Id: vorbisenc.h,v 1.2 2008/12/02 18:04:43 jeanlf Exp $ + + ********************************************************************/ + +#ifndef _OV_ENC_H_ +#define _OV_ENC_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include "codec.h" + +extern int vorbis_encode_init(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_managed(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_vbr(vorbis_info *vi, + long channels, + long rate, + + float quality /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_init_vbr(vorbis_info *vi, + long channels, + long rate, + + float base_quality /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_setup_init(vorbis_info *vi); + +extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg); + + /* deprecated rate management supported only for compatability */ +#define OV_ECTL_RATEMANAGE_GET 0x10 +#define OV_ECTL_RATEMANAGE_SET 0x11 +#define OV_ECTL_RATEMANAGE_AVG 0x12 +#define OV_ECTL_RATEMANAGE_HARD 0x13 + +struct ovectl_ratemanage_arg { + int management_active; + + long bitrate_hard_min; + long bitrate_hard_max; + double bitrate_hard_window; + + long bitrate_av_lo; + long bitrate_av_hi; + double bitrate_av_window; + double bitrate_av_window_center; +}; + + + /* new rate setup */ +#define OV_ECTL_RATEMANAGE2_GET 0x14 +#define OV_ECTL_RATEMANAGE2_SET 0x15 + +struct ovectl_ratemanage2_arg { + int management_active; + + long bitrate_limit_min_kbps; + long bitrate_limit_max_kbps; + long bitrate_limit_reservoir_bits; + double bitrate_limit_reservoir_bias; + + long bitrate_average_kbps; + double bitrate_average_damping; +}; + + + +#define OV_ECTL_LOWPASS_GET 0x20 +#define OV_ECTL_LOWPASS_SET 0x21 + +#define OV_ECTL_IBLOCK_GET 0x30 +#define OV_ECTL_IBLOCK_SET 0x31 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + + diff --git a/extra_lib/include/vorbis/vorbisfile.h b/extra_lib/include/vorbis/vorbisfile.h new file mode 100644 index 0000000..5453c23 --- /dev/null +++ b/extra_lib/include/vorbis/vorbisfile.h @@ -0,0 +1,183 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: stdio-based convenience library for opening/seeking/decoding + last mod: $Id: vorbisfile.h,v 1.2 2008/12/02 18:04:43 jeanlf Exp $ + + ********************************************************************/ + +#ifndef _OV_FILE_H_ +#define _OV_FILE_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include <stdio.h> +#include "codec.h" + +/* The function prototypes for the callbacks are basically the same as for + * the stdio functions fread, fseek, fclose, ftell. + * The one difference is that the FILE * arguments have been replaced with + * a void * - this is to be used as a pointer to whatever internal data these + * functions might need. In the stdio case, it's just a FILE * cast to a void * + * + * If you use other functions, check the docs for these functions and return + * the right values. For seek_func(), you *MUST* return -1 if the stream is + * unseekable + */ +typedef struct { + size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); + int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); + int (*close_func) (void *datasource); + long (*tell_func) (void *datasource); +} ov_callbacks; + +/* a few sets of convenient callbacks, especially for use under + * Windows where ov_open_callbacks() should always be used instead of + * ov_open() to avoid problems with incompatable crt.o version linking + * issues. */ + +static int _ov_header_fseek_wrap(FILE *f,ogg_int64_t off,int whence){ + if(f==NULL)return(-1); + return fseek(f,off,whence); +} + +static ov_callbacks OV_CALLBACKS_DEFAULT = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _ov_header_fseek_wrap, + (int (*)(void *)) fclose, + (long (*)(void *)) ftell +}; + +static ov_callbacks OV_CALLBACKS_NOCLOSE = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _ov_header_fseek_wrap, + (int (*)(void *)) NULL, + (long (*)(void *)) ftell +}; + +static ov_callbacks OV_CALLBACKS_STREAMONLY = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) NULL, + (int (*)(void *)) fclose, + (long (*)(void *)) NULL +}; + +static ov_callbacks OV_CALLBACKS_STREAMONLY_NOCLOSE = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) NULL, + (int (*)(void *)) NULL, + (long (*)(void *)) NULL +}; + +#define NOTOPEN 0 +#define PARTOPEN 1 +#define OPENED 2 +#define STREAMSET 3 +#define INITSET 4 + +typedef struct OggVorbis_File { + void *datasource; /* Pointer to a FILE *, etc. */ + int seekable; + ogg_int64_t offset; + ogg_int64_t end; + ogg_sync_state oy; + + /* If the FILE handle isn't seekable (eg, a pipe), only the current + stream appears */ + int links; + ogg_int64_t *offsets; + ogg_int64_t *dataoffsets; + long *serialnos; + ogg_int64_t *pcmlengths; /* overloaded to maintain binary + compatability; x2 size, stores both + beginning and end values */ + vorbis_info *vi; + vorbis_comment *vc; + + /* Decoding working state local storage */ + ogg_int64_t pcm_offset; + int ready_state; + long current_serialno; + int current_link; + + double bittrack; + double samptrack; + + ogg_stream_state os; /* take physical pages, weld into a logical + stream of packets */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + ov_callbacks callbacks; + +} OggVorbis_File; + + +extern int ov_clear(OggVorbis_File *vf); +extern int ov_fopen(char *path,OggVorbis_File *vf); +extern int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); +extern int ov_open_callbacks(void *datasource, OggVorbis_File *vf, + char *initial, long ibytes, ov_callbacks callbacks); + +extern int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); +extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf, + char *initial, long ibytes, ov_callbacks callbacks); +extern int ov_test_open(OggVorbis_File *vf); + +extern long ov_bitrate(OggVorbis_File *vf,int i); +extern long ov_bitrate_instant(OggVorbis_File *vf); +extern long ov_streams(OggVorbis_File *vf); +extern long ov_seekable(OggVorbis_File *vf); +extern long ov_serialnumber(OggVorbis_File *vf,int i); + +extern ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i); +extern ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i); +extern double ov_time_total(OggVorbis_File *vf,int i); + +extern int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek(OggVorbis_File *vf,double pos); +extern int ov_time_seek_page(OggVorbis_File *vf,double pos); + +extern int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek_lap(OggVorbis_File *vf,double pos); +extern int ov_time_seek_page_lap(OggVorbis_File *vf,double pos); + +extern ogg_int64_t ov_raw_tell(OggVorbis_File *vf); +extern ogg_int64_t ov_pcm_tell(OggVorbis_File *vf); +extern double ov_time_tell(OggVorbis_File *vf); + +extern vorbis_info *ov_info(OggVorbis_File *vf,int link); +extern vorbis_comment *ov_comment(OggVorbis_File *vf,int link); + +extern long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int samples, + int *bitstream); +extern long ov_read(OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream); +extern int ov_crosslap(OggVorbis_File *vf1,OggVorbis_File *vf2); + +extern int ov_halfrate(OggVorbis_File *vf,int flag); +extern int ov_halfrate_p(OggVorbis_File *vf); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + + diff --git a/extra_lib/include/xvid/xvid.h b/extra_lib/include/xvid/xvid.h new file mode 100644 index 0000000..2c7d25d --- /dev/null +++ b/extra_lib/include/xvid/xvid.h @@ -0,0 +1,786 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - XviD Main header file - + * + * Copyright(C) 2001-2004 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: xvid.h,v 1.2 2008/12/02 18:04:43 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _XVID_H_ +#define _XVID_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************** + * versioning + ****************************************************************************/ + +/* versioning + version takes the form "$major.$minor.$patch" + $patch is incremented when there is no api change + $minor is incremented when the api is changed, but remains backwards compatible + $major is incremented when the api is changed significantly + + when initialising an xvid structure, you must always zero it, and set the version field. + memset(&struct,0,sizeof(struct)); + struct.version = XVID_VERSION; + + XVID_UNSTABLE is defined only during development. + */ + +#define XVID_MAKE_VERSION(a,b,c) ((((a)&0xff)<<16) | (((b)&0xff)<<8) | ((c)&0xff)) +#define XVID_VERSION_MAJOR(a) ((char)(((a)>>16) & 0xff)) +#define XVID_VERSION_MINOR(a) ((char)(((a)>> 8) & 0xff)) +#define XVID_VERSION_PATCH(a) ((char)(((a)>> 0) & 0xff)) + +#define XVID_MAKE_API(a,b) ((((a)&0xff)<<16) | (((b)&0xff)<<0)) +#define XVID_API_MAJOR(a) (((a)>>16) & 0xff) +#define XVID_API_MINOR(a) (((a)>> 0) & 0xff) + +#define XVID_VERSION XVID_MAKE_VERSION(1,1,3) +#define XVID_API XVID_MAKE_API(4, 1) + +/* Bitstream Version + * this will be writen into the bitstream to allow easy detection of xvid + * encoder bugs in the decoder, without this it might not possible to + * automatically distinquish between a file which has been encoded with an + * old & buggy XVID from a file which has been encoded with a bugfree version + * see the infamous interlacing bug ... + * + * this MUST be increased if an encoder bug is fixed, increasing it too often + * doesnt hurt but not increasing it could cause difficulty for decoders in the + * future + */ +#define XVID_BS_VERSION 46 + +/***************************************************************************** + * error codes + ****************************************************************************/ + + /* all functions return values <0 indicate error */ + +#define XVID_ERR_FAIL -1 /* general fault */ +#define XVID_ERR_MEMORY -2 /* memory allocation error */ +#define XVID_ERR_FORMAT -3 /* file format error */ +#define XVID_ERR_VERSION -4 /* structure version not supported */ +#define XVID_ERR_END -5 /* encoder only; end of stream reached */ + + + +/***************************************************************************** + * xvid_image_t + ****************************************************************************/ + +/* colorspace values */ + +#define XVID_CSP_PLANAR (1<< 0) /* 4:2:0 planar (==I420, except for pointers/strides) */ +#define XVID_CSP_USER XVID_CSP_PLANAR +#define XVID_CSP_I420 (1<< 1) /* 4:2:0 planar */ +#define XVID_CSP_YV12 (1<< 2) /* 4:2:0 planar */ +#define XVID_CSP_YUY2 (1<< 3) /* 4:2:2 packed */ +#define XVID_CSP_UYVY (1<< 4) /* 4:2:2 packed */ +#define XVID_CSP_YVYU (1<< 5) /* 4:2:2 packed */ +#define XVID_CSP_BGRA (1<< 6) /* 32-bit bgra packed */ +#define XVID_CSP_ABGR (1<< 7) /* 32-bit abgr packed */ +#define XVID_CSP_RGBA (1<< 8) /* 32-bit rgba packed */ +#define XVID_CSP_ARGB (1<<15) /* 32-bit argb packed */ +#define XVID_CSP_BGR (1<< 9) /* 24-bit bgr packed */ +#define XVID_CSP_RGB555 (1<<10) /* 16-bit rgb555 packed */ +#define XVID_CSP_RGB565 (1<<11) /* 16-bit rgb565 packed */ +#define XVID_CSP_SLICE (1<<12) /* decoder only: 4:2:0 planar, per slice rendering */ +#define XVID_CSP_INTERNAL (1<<13) /* decoder only: 4:2:0 planar, returns ptrs to internal buffers */ +#define XVID_CSP_NULL (1<<14) /* decoder only: dont output anything */ +#define XVID_CSP_VFLIP (1<<31) /* vertical flip mask */ + +/* xvid_image_t + for non-planar colorspaces use only plane[0] and stride[0] + four plane reserved for alpha*/ +typedef struct { + int csp; /* [in] colorspace; or with XVID_CSP_VFLIP to perform vertical flip */ + void * plane[4]; /* [in] image plane ptrs */ + int stride[4]; /* [in] image stride; "bytes per row"*/ +} xvid_image_t; + +/* video-object-sequence profiles */ +#define XVID_PROFILE_S_L0 0x08 /* simple */ +#define XVID_PROFILE_S_L1 0x01 +#define XVID_PROFILE_S_L2 0x02 +#define XVID_PROFILE_S_L3 0x03 +#define XVID_PROFILE_ARTS_L1 0x91 /* advanced realtime simple */ +#define XVID_PROFILE_ARTS_L2 0x92 +#define XVID_PROFILE_ARTS_L3 0x93 +#define XVID_PROFILE_ARTS_L4 0x94 +#define XVID_PROFILE_AS_L0 0xf0 /* advanced simple */ +#define XVID_PROFILE_AS_L1 0xf1 +#define XVID_PROFILE_AS_L2 0xf2 +#define XVID_PROFILE_AS_L3 0xf3 +#define XVID_PROFILE_AS_L4 0xf4 + +/* aspect ratios */ +#define XVID_PAR_11_VGA 1 /* 1:1 vga (square), default if supplied PAR is not a valid value */ +#define XVID_PAR_43_PAL 2 /* 4:3 pal (12:11 625-line) */ +#define XVID_PAR_43_NTSC 3 /* 4:3 ntsc (10:11 525-line) */ +#define XVID_PAR_169_PAL 4 /* 16:9 pal (16:11 625-line) */ +#define XVID_PAR_169_NTSC 5 /* 16:9 ntsc (40:33 525-line) */ +#define XVID_PAR_EXT 15 /* extended par; use par_width, par_height */ + +/* frame type flags */ +#define XVID_TYPE_VOL -1 /* decoder only: vol was decoded */ +#define XVID_TYPE_NOTHING 0 /* decoder only (encoder stats): nothing was decoded/encoded */ +#define XVID_TYPE_AUTO 0 /* encoder: automatically determine coding type */ +#define XVID_TYPE_IVOP 1 /* intra frame */ +#define XVID_TYPE_PVOP 2 /* predicted frame */ +#define XVID_TYPE_BVOP 3 /* bidirectionally encoded */ +#define XVID_TYPE_SVOP 4 /* predicted+sprite frame */ + + +/***************************************************************************** + * xvid_global() + ****************************************************************************/ + +/* cpu_flags definitions (make sure to sync this with cpuid.asm for ia32) */ + +#define XVID_CPU_FORCE (1<<31) /* force passed cpu flags */ +#define XVID_CPU_ASM (1<< 7) /* native assembly */ +/* ARCH_IS_IA32 */ +#define XVID_CPU_MMX (1<< 0) /* mmx : pentiumMMX,k6 */ +#define XVID_CPU_MMXEXT (1<< 1) /* mmx-ext : pentium2, athlon */ +#define XVID_CPU_SSE (1<< 2) /* sse : pentium3, athlonXP */ +#define XVID_CPU_SSE2 (1<< 3) /* sse2 : pentium4, athlon64 */ +#define XVID_CPU_3DNOW (1<< 4) /* 3dnow : k6-2 */ +#define XVID_CPU_3DNOWEXT (1<< 5) /* 3dnow-ext : athlon */ +#define XVID_CPU_TSC (1<< 6) /* tsc : Pentium */ +/* ARCH_IS_PPC */ +#define XVID_CPU_ALTIVEC (1<< 0) /* altivec */ + + +#define XVID_DEBUG_ERROR (1<< 0) +#define XVID_DEBUG_STARTCODE (1<< 1) +#define XVID_DEBUG_HEADER (1<< 2) +#define XVID_DEBUG_TIMECODE (1<< 3) +#define XVID_DEBUG_MB (1<< 4) +#define XVID_DEBUG_COEFF (1<< 5) +#define XVID_DEBUG_MV (1<< 6) +#define XVID_DEBUG_RC (1<< 7) +#define XVID_DEBUG_DEBUG (1<<31) + +/* XVID_GBL_INIT param1 */ +typedef struct { + int version; + unsigned int cpu_flags; /* [in:opt] zero = autodetect cpu; XVID_CPU_FORCE|{cpu features} = force cpu features */ + int debug; /* [in:opt] debug level */ +} xvid_gbl_init_t; + + +/* XVID_GBL_INFO param1 */ +typedef struct { + int version; + int actual_version; /* [out] returns the actual xvidcore version */ + const char * build; /* [out] if !null, points to description of this xvid core build */ + unsigned int cpu_flags; /* [out] detected cpu features */ + int num_threads; /* [out] detected number of cpus/threads */ +} xvid_gbl_info_t; + + +/* XVID_GBL_CONVERT param1 */ +typedef struct { + int version; + xvid_image_t input; /* [in] input image & colorspace */ + xvid_image_t output; /* [in] output image & colorspace */ + int width; /* [in] width */ + int height; /* [in] height */ + int interlacing; /* [in] interlacing */ +} xvid_gbl_convert_t; + + +#define XVID_GBL_INIT 0 /* initialize xvidcore; must be called before using xvid_decore, or xvid_encore) */ +#define XVID_GBL_INFO 1 /* return some info about xvidcore, and the host computer */ +#define XVID_GBL_CONVERT 2 /* colorspace conversion utility */ + +extern int xvid_global(void *handle, int opt, void *param1, void *param2); + + +/***************************************************************************** + * xvid_decore() + ****************************************************************************/ + +#define XVID_DEC_CREATE 0 /* create decore instance; return 0 on success */ +#define XVID_DEC_DESTROY 1 /* destroy decore instance: return 0 on success */ +#define XVID_DEC_DECODE 2 /* decode a frame: returns number of bytes consumed >= 0 */ + +extern int xvid_decore(void *handle, int opt, void *param1, void *param2); + +/* XVID_DEC_CREATE param 1 + image width & height may be specified here when the dimensions are + known in advance. */ +typedef struct { + int version; + int width; /* [in:opt] image width */ + int height; /* [in:opt] image width */ + void * handle; /* [out] decore context handle */ +} xvid_dec_create_t; + + +/* XVID_DEC_DECODE param1 */ +/* general flags */ +#define XVID_LOWDELAY (1<<0) /* lowdelay mode */ +#define XVID_DISCONTINUITY (1<<1) /* indicates break in stream */ +#define XVID_DEBLOCKY (1<<2) /* perform luma deblocking */ +#define XVID_DEBLOCKUV (1<<3) /* perform chroma deblocking */ +#define XVID_FILMEFFECT (1<<4) /* adds film grain */ +#define XVID_DERINGUV (1<<5) /* perform chroma deringing, requires deblocking to work */ +#define XVID_DERINGY (1<<6) /* perform luma deringing, requires deblocking to work */ + +#define XVID_DEC_FAST (1<<29) /* disable postprocessing to decrease cpu usage *todo* */ +#define XVID_DEC_DROP (1<<30) /* drop bframes to decrease cpu usage *todo* */ +#define XVID_DEC_PREROLL (1<<31) /* decode as fast as you can, don't even show output *todo* */ + +typedef struct { + int version; + int general; /* [in:opt] general flags */ + void *bitstream; /* [in] bitstream (read from)*/ + int length; /* [in] bitstream length */ + xvid_image_t output; /* [in] output image (written to) */ +/* ------- v1.1.x ------- */ + int brightness; /* [in] brightness offset (0=none) */ +} xvid_dec_frame_t; + + +/* XVID_DEC_DECODE param2 :: optional */ +typedef struct +{ + int version; + + int type; /* [out] output data type */ + union { + struct { /* type>0 {XVID_TYPE_IVOP,XVID_TYPE_PVOP,XVID_TYPE_BVOP,XVID_TYPE_SVOP} */ + int general; /* [out] flags */ + int time_base; /* [out] time base */ + int time_increment; /* [out] time increment */ + + /* XXX: external deblocking stuff */ + int * qscale; /* [out] pointer to quantizer table */ + int qscale_stride; /* [out] quantizer scale stride */ + + } vop; + struct { /* XVID_TYPE_VOL */ + int general; /* [out] flags */ + int width; /* [out] width */ + int height; /* [out] height */ + int par; /* [out] pixel aspect ratio (refer to XVID_PAR_xxx above) */ + int par_width; /* [out] aspect ratio width [1..255] */ + int par_height; /* [out] aspect ratio height [1..255] */ + } vol; + } data; +} xvid_dec_stats_t; + +#define XVID_ZONE_QUANT (1<<0) +#define XVID_ZONE_WEIGHT (1<<1) + +typedef struct +{ + int frame; + int mode; + int increment; + int base; +} xvid_enc_zone_t; + + +/*---------------------------------------------------------------------------- + * xvid_enc_stats_t structure + * + * Used in: + * - xvid_plg_data_t structure + * - optional parameter in xvid_encore() function + * + * .coding_type = XVID_TYPE_NOTHING if the stats are not given + *--------------------------------------------------------------------------*/ + +typedef struct { + int version; + + /* encoding parameters */ + int type; /* [out] coding type */ + int quant; /* [out] frame quantizer */ + int vol_flags; /* [out] vol flags (see above) */ + int vop_flags; /* [out] vop flags (see above) */ + + /* bitrate */ + int length; /* [out] frame length */ + + int hlength; /* [out] header length (bytes) */ + int kblks; /* [out] number of blocks compressed as Intra */ + int mblks; /* [out] number of blocks compressed as Inter */ + int ublks; /* [out] number of blocks marked as not_coded */ + + int sse_y; /* [out] Y plane's sse */ + int sse_u; /* [out] U plane's sse */ + int sse_v; /* [out] V plane's sse */ +} xvid_enc_stats_t; + +/***************************************************************************** + xvid plugin system -- internals + + xvidcore will call XVID_PLG_INFO and XVID_PLG_CREATE during XVID_ENC_CREATE + before encoding each frame xvidcore will call XVID_PLG_BEFORE + after encoding each frame xvidcore will call XVID_PLG_AFTER + xvidcore will call XVID_PLG_DESTROY during XVID_ENC_DESTROY + ****************************************************************************/ + + +#define XVID_PLG_CREATE (1<<0) +#define XVID_PLG_DESTROY (1<<1) +#define XVID_PLG_INFO (1<<2) +#define XVID_PLG_BEFORE (1<<3) +#define XVID_PLG_FRAME (1<<4) +#define XVID_PLG_AFTER (1<<5) + +/* xvid_plg_info_t.flags */ +#define XVID_REQORIGINAL (1<<0) /* plugin requires a copy of the original (uncompressed) image */ +#define XVID_REQPSNR (1<<1) /* plugin requires psnr between the uncompressed and compressed image*/ +#define XVID_REQDQUANTS (1<<2) /* plugin requires access to the dquant table */ + + +typedef struct +{ + int version; + int flags; /* [in:opt] plugin flags */ +} xvid_plg_info_t; + + +typedef struct +{ + int version; + + int num_zones; /* [out] */ + xvid_enc_zone_t * zones; /* [out] */ + + int width; /* [out] */ + int height; /* [out] */ + int mb_width; /* [out] */ + int mb_height; /* [out] */ + int fincr; /* [out] */ + int fbase; /* [out] */ + + void * param; /* [out] */ +} xvid_plg_create_t; + + +typedef struct +{ + int version; + + int num_frames; /* [out] total frame encoded */ +} xvid_plg_destroy_t; + +typedef struct +{ + int version; + + xvid_enc_zone_t * zone; /* [out] current zone */ + + int width; /* [out] */ + int height; /* [out] */ + int mb_width; /* [out] */ + int mb_height; /* [out] */ + int fincr; /* [out] */ + int fbase; /* [out] */ + + int min_quant[3]; /* [out] */ + int max_quant[3]; /* [out] */ + + xvid_image_t reference; /* [out] -> [out] */ + xvid_image_t current; /* [out] -> [in,out] */ + xvid_image_t original; /* [out] after: points the original (uncompressed) copy of the current frame */ + int frame_num; /* [out] frame number */ + + int type; /* [in,out] */ + int quant; /* [in,out] */ + + int * dquant; /* [in,out] pointer to diff quantizer table */ + int dquant_stride; /* [in,out] diff quantizer stride */ + + int vop_flags; /* [in,out] */ + int vol_flags; /* [in,out] */ + int motion_flags; /* [in,out] */ + +/* Deprecated, use the stats field instead. + * Will disapear before 1.0 */ + int length; /* [out] after: length of encoded frame */ + int kblks; /* [out] number of blocks compressed as Intra */ + int mblks; /* [out] number of blocks compressed as Inter */ + int ublks; /* [out] number of blocks marked not_coded */ + int sse_y; /* [out] Y plane's sse */ + int sse_u; /* [out] U plane's sse */ + int sse_v; /* [out] V plane's sse */ +/* End of duplicated data, kept only for binary compatibility */ + + int bquant_ratio; /* [in] */ + int bquant_offset; /* [in] */ + + xvid_enc_stats_t stats; /* [out] frame statistics */ +} xvid_plg_data_t; + +/***************************************************************************** + xvid plugin system -- external + + the application passes xvid an array of "xvid_plugin_t" at XVID_ENC_CREATE. the array + indicates the plugin function pointer and plugin-specific data. + xvidcore handles the rest. example: + + xvid_enc_create_t create; + xvid_enc_plugin_t plugins[2]; + + plugins[0].func = xvid_psnr_func; + plugins[0].param = NULL; + plugins[1].func = xvid_cbr_func; + plugins[1].param = &cbr_data; + + create.num_plugins = 2; + create.plugins = plugins; + + ****************************************************************************/ + +typedef int (xvid_plugin_func)(void * handle, int opt, void * param1, void * param2); + +typedef struct +{ + xvid_plugin_func * func; + void * param; +} xvid_enc_plugin_t; + + +extern xvid_plugin_func xvid_plugin_single; /* single-pass rate control */ +extern xvid_plugin_func xvid_plugin_2pass1; /* two-pass rate control: first pass */ +extern xvid_plugin_func xvid_plugin_2pass2; /* two-pass rate control: second pass */ + +extern xvid_plugin_func xvid_plugin_lumimasking; /* lumimasking */ + +extern xvid_plugin_func xvid_plugin_psnr; /* write psnr values to stdout */ +extern xvid_plugin_func xvid_plugin_dump; /* dump before and after yuvpgms */ + + +/* single pass rate control + * CBR and Constant quantizer modes */ +typedef struct +{ + int version; + + int bitrate; /* [in] bits per second */ + int reaction_delay_factor; /* [in] */ + int averaging_period; /* [in] */ + int buffer; /* [in] */ +} xvid_plugin_single_t; + + +typedef struct { + int version; + + char * filename; +} xvid_plugin_2pass1_t; + + +#define XVID_PAYBACK_BIAS 0 /* payback with bias */ +#define XVID_PAYBACK_PROP 1 /* payback proportionally */ + +typedef struct { + int version; + + int bitrate; /* [in] target bitrate (bits per second) */ + char * filename; /* [in] first pass stats filename */ + + int keyframe_boost; /* [in] keyframe boost percentage: [0..100] */ + int curve_compression_high; /* [in] percentage of compression performed on the high part of the curve (above average) */ + int curve_compression_low; /* [in] percentage of compression performed on the low part of the curve (below average) */ + int overflow_control_strength;/* [in] Payback delay expressed in number of frames */ + int max_overflow_improvement; /* [in] percentage of allowed range for a frame that gets bigger because of overflow bonus */ + int max_overflow_degradation; /* [in] percentage of allowed range for a frame that gets smaller because of overflow penalty */ + + int kfreduction; /* [in] maximum bitrate reduction applied to an iframe under the kfthreshold distance limit */ + int kfthreshold; /* [in] if an iframe is closer to the next iframe than this distance, a quantity of bits + * is substracted from its bit allocation. The reduction is computed as multiples of + * kfreduction/kthreshold. It reaches kfreduction when the distance == kfthreshold, + * 0 for 1<distance<kfthreshold */ + + int container_frame_overhead; /* [in] How many bytes the controller has to compensate per frame due to container format overhead */ + +/* ------- v1.1.x ------- */ + int vbv_size; /* [in] buffer size (bits) */ + int vbv_initial; /* [in] initial buffer occupancy (bits) */ + int vbv_maxrate; /* [in] max processing bitrate (bits per second) */ + int vbv_peakrate; /* [in:opt] max average bitrate over 3 seconds (bits per second) */ + +}xvid_plugin_2pass2_t; + +/***************************************************************************** + * ENCODER API + ****************************************************************************/ + +/*---------------------------------------------------------------------------- + * Encoder operations + *--------------------------------------------------------------------------*/ + +#define XVID_ENC_CREATE 0 /* create encoder instance; returns 0 on success */ +#define XVID_ENC_DESTROY 1 /* destroy encoder instance; returns 0 on success */ +#define XVID_ENC_ENCODE 2 /* encode a frame: returns number of ouput bytes + * 0 means this frame should not be written (ie. encoder lag) */ + + +/*---------------------------------------------------------------------------- + * Encoder entry point + *--------------------------------------------------------------------------*/ + +extern int xvid_encore(void *handle, int opt, void *param1, void *param2); + +/* Quick API reference + * + * XVID_ENC_CREATE operation + * - handle: ignored + * - opt: XVID_ENC_CREATE + * - param1: address of a xvid_enc_create_t structure + * - param2: ignored + * + * XVID_ENC_ENCODE operation + * - handle: an instance returned by a CREATE op + * - opt: XVID_ENC_ENCODE + * - param1: address of a xvid_enc_frame_t structure + * - param2: address of a xvid_enc_stats_t structure (optional) + * its return value is asynchronous to what is written to the buffer + * depending on the delay introduced by bvop use. It's display + * ordered. + * + * XVID_ENC_DESTROY operation + * - handle: an instance returned by a CREATE op + * - opt: XVID_ENC_DESTROY + * - param1: ignored + * - param2: ignored + */ + + +/*---------------------------------------------------------------------------- + * "Global" flags + * + * These flags are used for xvid_enc_create_t->global field during instance + * creation (operation XVID_ENC_CREATE) + *--------------------------------------------------------------------------*/ + +#define XVID_GLOBAL_PACKED (1<<0) /* packed bitstream */ +#define XVID_GLOBAL_CLOSED_GOP (1<<1) /* closed_gop: was DX50BVOP dx50 bvop compatibility */ +#define XVID_GLOBAL_EXTRASTATS_ENABLE (1<<2) +#if 0 +#define XVID_GLOBAL_VOL_AT_IVOP (1<<3) /* write vol at every ivop: WIN32/divx compatibility */ +#define XVID_GLOBAL_FORCE_VOL (1<<4) /* when vol-based parameters are changed, insert an ivop NOT recommended */ +#endif +#define XVID_GLOBAL_DIVX5_USERDATA (1<<5) /* write divx5 userdata string + this is implied if XVID_GLOBAL_PACKED is set */ + +/*---------------------------------------------------------------------------- + * "VOL" flags + * + * These flags are used for xvid_enc_frame_t->vol_flags field during frame + * encoding (operation XVID_ENC_ENCODE) + *--------------------------------------------------------------------------*/ + +#define XVID_VOL_MPEGQUANT (1<<0) /* enable MPEG type quantization */ +#define XVID_VOL_EXTRASTATS (1<<1) /* enable plane sse stats */ +#define XVID_VOL_QUARTERPEL (1<<2) /* enable quarterpel: frames will encoded as quarterpel */ +#define XVID_VOL_GMC (1<<3) /* enable GMC; frames will be checked for gmc suitability */ +#define XVID_VOL_REDUCED_ENABLE (1<<4) /* enable reduced resolution vops: frames will be checked for rrv suitability */ + /* NOTE: the reduced resolution feature is not supported anymore. This flag will have no effect! */ +#define XVID_VOL_INTERLACING (1<<5) /* enable interlaced encoding */ + + +/*---------------------------------------------------------------------------- + * "VOP" flags + * + * These flags are used for xvid_enc_frame_t->vop_flags field during frame + * encoding (operation XVID_ENC_ENCODE) + *--------------------------------------------------------------------------*/ + +/* Always valid */ +#define XVID_VOP_DEBUG (1<< 0) /* print debug messages in frames */ +#define XVID_VOP_HALFPEL (1<< 1) /* use halfpel interpolation */ +#define XVID_VOP_INTER4V (1<< 2) /* use 4 motion vectors per MB */ +#define XVID_VOP_TRELLISQUANT (1<< 3) /* use trellis based R-D "optimal" quantization */ +#define XVID_VOP_CHROMAOPT (1<< 4) /* enable chroma optimization pre-filter */ +#define XVID_VOP_CARTOON (1<< 5) /* use 'cartoon mode' */ +#define XVID_VOP_GREYSCALE (1<< 6) /* enable greyscale only mode (even for color input material chroma is ignored) */ +#define XVID_VOP_HQACPRED (1<< 7) /* high quality ac prediction */ +#define XVID_VOP_MODEDECISION_RD (1<< 8) /* enable DCT-ME and use it for mode decision */ +#define XVID_VOP_FAST_MODEDECISION_RD (1<<12) /* use simplified R-D mode decision */ +#define XVID_VOP_RD_BVOP (1<<13) /* enable rate-distortion mode decision in b-frames */ + +/* Only valid for vol_flags|=XVID_VOL_INTERLACING */ +#define XVID_VOP_TOPFIELDFIRST (1<< 9) /* set top-field-first flag */ +#define XVID_VOP_ALTERNATESCAN (1<<10) /* set alternate vertical scan flag */ + +/* only valid for vol_flags|=XVID_VOL_REDUCED_ENABLED */ +#define XVID_VOP_REDUCED (1<<11) /* reduced resolution vop */ + /* NOTE: reduced resolution feature is not supported anymore. This flag will have no effect! */ + +/*---------------------------------------------------------------------------- + * "Motion" flags + * + * These flags are used for xvid_enc_frame_t->motion field during frame + * encoding (operation XVID_ENC_ENCODE) + *--------------------------------------------------------------------------*/ + +/* Motion Estimation Search Patterns */ +#define XVID_ME_ADVANCEDDIAMOND16 (1<< 0) /* use advdiamonds instead of diamonds as search pattern */ +#define XVID_ME_ADVANCEDDIAMOND8 (1<< 1) /* use advdiamond for XVID_ME_EXTSEARCH8 */ +#define XVID_ME_USESQUARES16 (1<< 2) /* use squares instead of diamonds as search pattern */ +#define XVID_ME_USESQUARES8 (1<< 3) /* use square for XVID_ME_EXTSEARCH8 */ + +/* SAD operator based flags */ +#define XVID_ME_HALFPELREFINE16 (1<< 4) +#define XVID_ME_HALFPELREFINE8 (1<< 6) +#define XVID_ME_QUARTERPELREFINE16 (1<< 7) +#define XVID_ME_QUARTERPELREFINE8 (1<< 8) +#define XVID_ME_GME_REFINE (1<< 9) +#define XVID_ME_EXTSEARCH16 (1<<10) /* extend PMV by more searches */ +#define XVID_ME_EXTSEARCH8 (1<<11) /* use diamond/square for extended 8x8 search */ +#define XVID_ME_CHROMA_PVOP (1<<12) /* also use chroma for P_VOP/S_VOP ME */ +#define XVID_ME_CHROMA_BVOP (1<<13) /* also use chroma for B_VOP ME */ +#define XVID_ME_FASTREFINE16 (1<<25) /* use low-complexity refinement functions */ +#define XVID_ME_FASTREFINE8 (1<<29) /* low-complexity 8x8 sub-block refinement */ + +/* Rate Distortion based flags + * Valid when XVID_VOP_MODEDECISION_RD is enabled */ +#define XVID_ME_HALFPELREFINE16_RD (1<<14) /* perform RD-based halfpel refinement */ +#define XVID_ME_HALFPELREFINE8_RD (1<<15) /* perform RD-based halfpel refinement for 8x8 mode */ +#define XVID_ME_QUARTERPELREFINE16_RD (1<<16) /* perform RD-based qpel refinement */ +#define XVID_ME_QUARTERPELREFINE8_RD (1<<17) /* perform RD-based qpel refinement for 8x8 mode */ +#define XVID_ME_EXTSEARCH_RD (1<<18) /* perform RD-based search using square pattern enable XVID_ME_EXTSEARCH8 to do this in 8x8 search as well */ +#define XVID_ME_CHECKPREDICTION_RD (1<<19) /* always check vector equal to prediction */ + +/* Other */ +#define XVID_ME_DETECT_STATIC_MOTION (1<<24) /* speed-up ME by detecting stationary scenes */ +#define XVID_ME_SKIP_DELTASEARCH (1<<26) /* speed-up by skipping b-frame delta search */ +#define XVID_ME_FAST_MODEINTERPOLATE (1<<27) /* speed-up by partly skipping interpolate mode */ +#define XVID_ME_BFRAME_EARLYSTOP (1<<28) /* speed-up by early exiting b-search */ + +/* Unused */ +#define XVID_ME_UNRESTRICTED16 (1<<20) /* unrestricted ME, not implemented */ +#define XVID_ME_OVERLAPPING16 (1<<21) /* overlapping ME, not implemented */ +#define XVID_ME_UNRESTRICTED8 (1<<22) /* unrestricted ME, not implemented */ +#define XVID_ME_OVERLAPPING8 (1<<23) /* overlapping ME, not implemented */ + + +/*---------------------------------------------------------------------------- + * xvid_enc_create_t structure definition + * + * This structure is passed as param1 during an instance creation (operation + * XVID_ENC_CREATE) + *--------------------------------------------------------------------------*/ + +typedef struct { + int version; + + int profile; /* [in] profile@level; refer to XVID_PROFILE_xxx */ + int width; /* [in] frame dimensions; width, pixel units */ + int height; /* [in] frame dimensions; height, pixel units */ + + int num_zones; /* [in:opt] number of bitrate zones */ + xvid_enc_zone_t * zones; /* ^^ zone array */ + + int num_plugins; /* [in:opt] number of plugins */ + xvid_enc_plugin_t * plugins; /* ^^ plugin array */ + + int num_threads; /* [in:opt] number of threads */ + int max_bframes; /* [in:opt] max sequential bframes (0=disable bframes) */ + + int global; /* [in:opt] global flags; controls encoding behavior */ + + /* --- vol-based stuff; included here for convenience */ + int fincr; /* [in:opt] framerate increment; set to zero for variable framerate */ + int fbase; /* [in] framerate base frame_duration = fincr/fbase seconds*/ + /* ---------------------------------------------- */ + + /* --- vop-based; included here for convenience */ + int max_key_interval; /* [in:opt] the maximum interval between key frames */ + + int frame_drop_ratio; /* [in:opt] frame dropping: 0=drop none... 100=drop all */ + + int bquant_ratio; /* [in:opt] bframe quantizer multipier/offeset; used to decide bframes quant when bquant==-1 */ + int bquant_offset; /* bquant = (avg(past_ref_quant,future_ref_quant)*bquant_ratio + bquant_offset) / 100 */ + + int min_quant[3]; /* [in:opt] */ + int max_quant[3]; /* [in:opt] */ + /* ---------------------------------------------- */ + + void *handle; /* [out] encoder instance handle */ +} xvid_enc_create_t; + + +/*---------------------------------------------------------------------------- + * xvid_enc_frame_t structure definition + * + * This structure is passed as param1 during a frame encoding (operation + * XVID_ENC_ENCODE) + *--------------------------------------------------------------------------*/ + +/* out value for the frame structure->type field + * unlike stats output in param2, this field is not asynchronous and tells + * the client app, if the frame written into the stream buffer is an ivop + * usually used for indexing purpose in the container */ +#define XVID_KEYFRAME (1<<1) + +/* The structure */ +typedef struct { + int version; + + /* VOL related stuff + * unless XVID_FORCEVOL is set, the encoder will not react to any changes + * here until the next VOL (keyframe). */ + + int vol_flags; /* [in] vol flags */ + unsigned char *quant_intra_matrix; /* [in:opt] custom intra qmatrix */ + unsigned char *quant_inter_matrix; /* [in:opt] custom inter qmatrix */ + + int par; /* [in:opt] pixel aspect ratio (refer to XVID_PAR_xxx above) */ + int par_width; /* [in:opt] aspect ratio width */ + int par_height; /* [in:opt] aspect ratio height */ + + /* Other fields that can change on a frame base */ + + int fincr; /* [in:opt] framerate increment, for variable framerate only */ + int vop_flags; /* [in] (general)vop-based flags */ + int motion; /* [in] ME options */ + + xvid_image_t input; /* [in] input image (read from) */ + + int type; /* [in:opt] coding type */ + int quant; /* [in] frame quantizer; if <=0, automatic (ratecontrol) */ + int bframe_threshold; + + void *bitstream; /* [in:opt] bitstream ptr (written to)*/ + int length; /* [in:opt] bitstream length (bytes) */ + + int out_flags; /* [out] bitstream output flags */ +} xvid_enc_frame_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extra_lib/include/zlib/zconf.h b/extra_lib/include/zlib/zconf.h new file mode 100644 index 0000000..19892af --- /dev/null +++ b/extra_lib/include/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zconf.h,v 1.2 2008/12/02 18:04:43 jeanlf Exp $ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include <windows.h> + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include <sys/types.h> /* for off_t */ +# include <unistd.h> /* for SEEK_* and off_t */ +# ifdef VMS +# include <unixio.h> /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/extra_lib/include/zlib/zlib.h b/extra_lib/include/zlib/zlib.h new file mode 100644 index 0000000..0228179 --- /dev/null +++ b/extra_lib/include/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/extra_lib/lib/arm_ppc02_deb/dummy b/extra_lib/lib/arm_ppc02_deb/dummy new file mode 100644 index 0000000..e69de29 diff --git a/extra_lib/lib/arm_ppc02_rel/dummy b/extra_lib/lib/arm_ppc02_rel/dummy new file mode 100644 index 0000000..e69de29 diff --git a/extra_lib/lib/arm_ppc03_deb/dummy b/extra_lib/lib/arm_ppc03_deb/dummy new file mode 100644 index 0000000..e69de29 diff --git a/extra_lib/lib/arm_ppc03_rel/dummy b/extra_lib/lib/arm_ppc03_rel/dummy new file mode 100644 index 0000000..e69de29 diff --git a/extra_lib/lib/gcc/dummy b/extra_lib/lib/gcc/dummy new file mode 100644 index 0000000..e69de29 diff --git a/extra_lib/lib/w32_deb/dummy b/extra_lib/lib/w32_deb/dummy new file mode 100644 index 0000000..e69de29 diff --git a/extra_lib/lib/w32_rel/dummy b/extra_lib/lib/w32_rel/dummy new file mode 100644 index 0000000..e69de29 diff --git a/gpac.spec b/gpac.spec new file mode 100644 index 0000000..892e841 --- /dev/null +++ b/gpac.spec @@ -0,0 +1,101 @@ +# $Id: gpac.spec,v 1.5 2008/12/02 18:04:42 jeanlf Exp $ +Summary: GPAC is a multimedia framework covering MPEG-4, VRML/X3D and SVG. +Name: gpac +Version: 0.4.5 +Release: DEV +License: LGPL +Group: Applications/Multimedia +Source0: gpac-0.4.5.tar.gz%{?_with_amr:Source1:http://www.3gpp.org/ftp/Specs/archive/26_series/26.073/26073-700.zip} +URL: http://gpac.sourceforge.net/ +BuildRoot: %{_tmppath}/%{name}-root +Requires: SDL +%{!?_without_js:Requires: js} +%{!?_without_freetype:Requires: freetype} +%{!?_without_faad:Requires: faad2} +%{!?_without_jpeg:Requires: libjpeg-6b} +%{!?_without_png:Requires: libpng} +%{!?_without_mad:Requires: libmad} +%{!?_without_xvid:Requires: xvidcore} +%{!?_without_ffmpeg:Requires: ffmpeg} +%{!?_without_jack:Requires: libjack} +BuildRequires: SDL-devel +%{!?_without_js:BuildRequires: js-devel} +%{!?_without_freetype:BuildRequires: freetype-devel} +%{!?_without_faad:BuildRequires: faad2-devel} +%{!?_without_jpeg:BuildRequires: libjpeg-devel} +%{!?_without_png:BuildRequires: libpng-devel} +%{!?_without_mad:BuildRequires: libmad-devel} +%{!?_without_xvid:BuildRequires: xvidcore-devel} +%{!?_without_ffmpeg:BuildRequires: ffmpeg-devel} +%{!?_without_jack:BuildRequires: libjack-devel} + +%description +GPAC is a multimedia framework for MPEG-4, VRML/X3D and SVG/SMIL. GPAC is built upon an implementation of the MPEG-4 Systems +standard (ISO/IEC 14496-1) developed from scratch in C. + +The main development goal is to provide a clean (a.k.a. readable by as many +people as possible), small and flexible alternative to the MPEG-4 Systems +reference software (known as IM1 and distributed in ISO/IEC 14496-5). + +The second development goal is to achieve integration of recent multimedia +standards (SVG/SMIL, VRML, X3D, SWF, etc) into a single framework. +GPAC already supports most of VRML97 and SVG Tiny 1.2, as well as some X3D and simple SWF support. + +GPAC already features 2D and 3D multimedia playback, MPEG-4 Systems +encoders/multiplexers and publishing tools for content distribution. + +GPAC is licensed under the GNU Lesser General Public License. + +The current GPAC release (0.4.5) already covers a very large part of the MPEG-4 standard, +and features what can probably be seen as the most advanced and robust 2D MPEG-4 +Player available worldwide, as well as a decent 3D MPEG-4/VRML player. + +Available rpmbuild rebuild options : +--without : js freetype faad a52 jpeg png mad xvid ffmpeg +--with : amr + +To build with amr, download +http://www.3gpp.org/ftp/Specs/archive/26_series/26.073/26073-700.zip +and put it in the rpm source directory. Then invoke rpmbuild with the +"--with amr" option. + +%prep +%setup -q -n gpac +%if %{?_with_amr:1}%{!?_with_amr:0} +mkdir -p modules/amr_dec/amr_nb +cd Plugins/amr_dec/AMR_NB +unzip -j /usr/src/redhat/SOURCES/26073-700.zip +unzip -j 26073-700_ANSI_C_source_code.zip +cd ../../.. +%endif + +%build +%configure --enable-oss-audio %{?_with_amr: --enable-amr-nb} %{?_without_js: --disable-js} %{?_without_freetype: --disable-ft} %{?_without_faad: --disable-faad} %{?_without_jpeg: --disable-jpeg} %{?_without_png: --disable-png} %{?_without_mad: --disable-mad} %{?_without_xvid: --disable-xvid} %{?_without_ffmpeg: --disable-ffmpeg} %{?_without_jack: --disable-jack} +make + +%install +rm -rf %{buildroot} +%makeinstall + +%clean +rm -rf %{buildroot} + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + + +%files +%defattr(-, root, root) +%doc AUTHORS BUGS Changelog COPYING README TODO +%{_bindir}/* +%{_libdir}/* +%{_mandir}/man1/* + +%changelog +* Wed Feb 13 2008 Pierre Souchay +- Added libjack +* Wed Jul 13 2005 Jean Le Feuvre +- Updated for GPAC LGPL release +* Mon Aug 09 2004 Sverker Abrahamsson <sverker@abrahamsson.com> +- Initial RPM release diff --git a/include/gpac/avparse.h b/include/gpac/avparse.h new file mode 100644 index 0000000..530882c --- /dev/null +++ b/include/gpac/avparse.h @@ -0,0 +1,208 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Authoring Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_PARSERS_AV_H_ +#define _GF_PARSERS_AV_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/bitstream.h> + +/*basic MPEG (1,2,4) visual object parser (DSI extraction and timing/framing)*/ +typedef struct +{ + /*video PL*/ + u8 VideoPL; + u8 RAP_stream, objectType, has_shape, enh_layer; + /*video resolution*/ + u16 width, height; + /*pixel aspect ratio*/ + u8 par_num, par_den; + + u16 clock_rate; + u8 NumBitsTimeIncrement; + u32 time_increment; + /*for MPEG 1/2*/ + Double fps; +} GF_M4VDecSpecInfo; + + +typedef struct __tag_m4v_parser GF_M4VParser; + +GF_M4VParser *gf_m4v_parser_new(char *data, u32 data_size, Bool mpeg12video); +GF_M4VParser *gf_m4v_parser_bs_new(GF_BitStream *bs, Bool mpeg12video); +void gf_m4v_parser_del(GF_M4VParser *m4v); +GF_Err gf_m4v_parse_config(GF_M4VParser *m4v, GF_M4VDecSpecInfo *dsi); + +/*get a frame (can contain GOP). The parser ALWAYS resync on the next object in the bitstream +thus you can seek the bitstream to copy the payload without re-seeking it */ +GF_Err gf_m4v_parse_frame(GF_M4VParser *m4v, GF_M4VDecSpecInfo dsi, u8 *frame_type, u32 *time_inc, u32 *size, u32 *start, Bool *is_coded); +/*returns current object start in bitstream*/ +u32 gf_m4v_get_object_start(GF_M4VParser *m4v); +/*returns 1 if current object is a valid MPEG-4 Visual object*/ +Bool gf_m4v_is_valid_object_type(GF_M4VParser *m4v); +/*returns readable description of profile*/ +const char *gf_m4v_get_profile_name(u8 video_pl); +/*decodes DSI*/ +GF_Err gf_m4v_get_config(char *rawdsi, u32 rawdsi_size, GF_M4VDecSpecInfo *dsi); +/*rewrites PL code in DSI*/ +void gf_m4v_rewrite_pl(char **io_dsi, u32 *io_dsi_len, u8 PL); +/*rewrites PAR code in DSI. Negative values will remove the par*/ +GF_Err gf_m4v_rewrite_par(char **o_data, u32 *o_dataLen, s32 par_n, s32 par_d); + +/*MP3 tools*/ +u8 gf_mp3_num_channels(u32 hdr); +u16 gf_mp3_sampling_rate(u32 hdr); +u16 gf_mp3_window_size(u32 hdr); +u16 gf_mp3_bit_rate(u32 hdr); +u8 gf_mp3_object_type_indication(u32 hdr); +u8 gf_mp3_layer(u32 hdr); +u8 gf_mp3_version(u32 hdr); +const char *gf_mp3_version_name(u32 hdr); +u16 gf_mp3_frame_size(u32 hdr); +u32 gf_mp3_get_next_header(FILE* in); +u32 gf_mp3_get_next_header_mem(char *buffer, u32 size, u32 *pos); + +/*vorbis tools*/ +typedef struct +{ + u32 sample_rate, channels, version; + s32 max_r, avg_r, low_r; + u32 min_block, max_block; + + /*do not touch, parser private*/ + Bool is_init; + u32 modebits; + Bool mode_flag[64]; +} GF_VorbisParser; + +/*call with vorbis header packets - you MUST initialize the structure to 0 before!! +returns 1 if success, 0 if error.*/ +Bool gf_vorbis_parse_header(GF_VorbisParser *vp, char *data, u32 data_len); +/*returns 0 if init error or not a vorbis frame, otherwise returns the number of audio samples +in this frame*/ +u32 gf_vorbis_check_frame(GF_VorbisParser *vp, char *data, u32 data_length); + + +enum +{ + GF_M4A_AAC_MAIN = 1, + GF_M4A_AAC_LC = 2, + GF_M4A_AAC_SSR = 3, + GF_M4A_AAC_LTP = 4, + GF_M4A_AAC_SBR = 5, + GF_M4A_AAC_SCALABLE = 6, + GF_M4A_TWINVQ = 7, + GF_M4A_CELP = 8, + GF_M4A_HVXC = 9, + GF_M4A_TTSI = 12, + GF_M4A_MAIN_SYNTHETIC = 13, + GF_M4A_WAVETABLE_SYNTHESIS = 14, + GF_M4A_GENERAL_MIDI = 15, + GF_M4A_ALGO_SYNTH_AUDIO_FX = 16, + GF_M4A_ER_AAC_LC = 17, + GF_M4A_ER_AAC_LTP = 19, + GF_M4A_ER_AAC_SCALABLE = 20, + GF_M4A_ER_TWINVQ = 21, + GF_M4A_ER_BSAC = 22, + GF_M4A_ER_AAC_LD = 23, + GF_M4A_ER_CELP = 24, + GF_M4A_ER_HVXC = 25, + GF_M4A_ER_HILN = 26, + GF_M4A_ER_PARAMETRIC = 27, + GF_M4A_SSC = 28, + //GF_M4A_PS = 29, + GF_M4A_LAYER1 = 32, + GF_M4A_LAYER2 = 33, + GF_M4A_LAYER3 = 34, + GF_M4A_DST = 35, + GF_M4A_ALS = 36, +}; + +static const u32 GF_M4ASampleRates[] = +{ + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350, 0, 0, 0 +}; + +/*get Audio type from dsi. return audio codec type:*/ +typedef struct +{ + u32 nb_chan; + u32 base_object_type, base_sr, base_sr_index; + /*SBR*/ + Bool has_sbr; + u32 sbr_object_type, sbr_sr, sbr_sr_index; + /*PL indication*/ + u8 audioPL; +} GF_M4ADecSpecInfo; +/*parses dsi and updates audioPL*/ +GF_Err gf_m4a_get_config(char *dsi, u32 dsi_size, GF_M4ADecSpecInfo *cfg); +/*gets audioPL for given cfg*/ +u32 gf_m4a_get_profile(GF_M4ADecSpecInfo *cfg); +const char *gf_m4a_object_type_name(u32 objectType); +const char *gf_m4a_get_profile_name(u8 audio_pl); + +GF_Err gf_m4a_write_config(GF_M4ADecSpecInfo *cfg, char **dsi, u32 *dsi_size); +GF_Err gf_m4a_parse_config(GF_BitStream *bs, GF_M4ADecSpecInfo *cfg, Bool size_known); + +typedef struct +{ + u32 bitrate; + u32 sample_rate; + u32 framesize; + u32 channels; + /*only set if full parse*/ + u8 fscod, bsid, bsmod, acmod, lfon, brcode; +} GF_AC3Header; + +Bool gf_ac3_parser(u8 *buffer, u32 buffer_size, u32 *pos, GF_AC3Header *out_hdr, Bool full_parse); +Bool gf_ac3_parser_bs(GF_BitStream *bs, GF_AC3Header *hdr, Bool full_parse); + + +GF_Err gf_avc_get_sps_info(char *sps, u32 sps_size, u32 *width, u32 *height, s32 *par_n, s32 *par_d); + +const char *gf_avc_get_profile_name(u8 video_prof); + + +/*gets image size (bs must contain the whole image) +@OTI: image type (JPEG=0x6C, PNG=0x6D) +@width, height: image resolution - for jpeg max size if thumbnail included*/ +void gf_img_parse(GF_BitStream *bs, u8 *OTI, u32 *mtype, u32 *width, u32 *height, char **dsi, u32 *dsi_len); + +GF_Err gf_img_jpeg_dec(char *jpg, u32 jpg_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size, u32 dst_nb_comp); + +GF_Err gf_img_png_dec(char *png, u32 png_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size); +GF_Err gf_img_png_enc(char *data, u32 width, u32 height, u32 pixel_format, char *dst, u32 *dst_size); + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_PARSERS_AV_H_*/ + diff --git a/include/gpac/base_coding.h b/include/gpac/base_coding.h new file mode 100644 index 0000000..ee02e9c --- /dev/null +++ b/include/gpac/base_coding.h @@ -0,0 +1,105 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_BASE_CODING_H_ +#define _GF_BASE_CODING_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file <gpac/base_coding.h> + * \brief Base coding functions. + */ + +/*! + * \addtogroup bascod_grp base coding + * \ingroup utils_grp + * \brief Base Coding functions + * + * This section documents the base encoding and decoding functions of the GPAC framework. + * @{ + */ + +#include <gpac/tools.h> + +/*! + *\brief base64 encoder + * + *Encodes a data buffer to Base64 + *\param in_buffer input data buffer + *\param in_buffer_size input data buffer size + *\param out_buffer output Base64 buffer location + *\param out_buffer_size output Base64 buffer allocated size + *\return size of the encoded Base64 buffer + *\note the encoded data buffer is not NULL-terminated. + */ +u32 gf_base64_encode(char *in_buffer, u32 in_buffer_size, char *out_buffer, u32 out_buffer_size); +/*! + *\brief base64 decoder + * + *Decodes a Base64 buffer to data + *\param in_buffer input Base64 buffer + *\param in_buffer_size input Base64 buffer size + *\param out_buffer output data buffer location + *\param out_buffer_size output data buffer allocated size + *\return size of the decoded buffer + */ +u32 gf_base64_decode(char *in_buffer, u32 in_buffer_size, char *out_buffer, u32 out_buffer_size); + +/*! + *\brief base16 encoder + * + *Encodes a data buffer to Base16 + *\param in_buffer input data buffer + *\param in_buffer_size input data buffer size + *\param out_buffer output Base16 buffer location + *\param out_buffer_size output Base16 buffer allocated size + *\return size of the encoded Base16 buffer + *\note the encoded data buffer is not NULL-terminated. + */ +u32 gf_base16_encode(char *in_buffer, u32 in_buffer_size, char *out_buffer, u32 out_buffer_size); + +/*! + *\brief base16 decoder + * + *Decodes a Base16 buffer to data + *\param in_buffer input Base16 buffer + *\param in_buffer_size input Base16 buffer size + *\param out_buffer output data buffer location + *\param out_buffer_size output data buffer allocated size + *\return size of the decoded buffer + */ +u32 gf_base16_decode(char *in_buffer, u32 in_buffer_size, char *out_buffer, u32 out_buffer_size); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_BASE_CODING_H_*/ + diff --git a/include/gpac/bifs.h b/include/gpac/bifs.h new file mode 100644 index 0000000..f818295 --- /dev/null +++ b/include/gpac/bifs.h @@ -0,0 +1,95 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_BIFS_H_ +#define _GF_BIFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#include <gpac/nodes_mpeg4.h> +/*for BIFSConfig*/ +#include <gpac/mpeg4_odf.h> + +typedef struct __tag_bifs_dec GF_BifsDecoder; + +/*BIFS decoder constructor - + @command_dec: if set, the decoder will only work in memory mode (creating commands for the graph) + otherwise the decoder will always apply commands while decoding them*/ +GF_BifsDecoder *gf_bifs_decoder_new(GF_SceneGraph *scenegraph, Bool command_dec); +void gf_bifs_decoder_del(GF_BifsDecoder *codec); + +/*sets the scene time. Scene time is the real clock of the bifs stream in secs*/ +void gf_bifs_decoder_set_time_offset(GF_BifsDecoder *codec, Double ts); + +/*signals the sizeInfo of the config should be ignored - used for BIFS in AnimationStream nodes*/ +void gf_bifs_decoder_ignore_size_info(GF_BifsDecoder *codec); + +/*setup a stream*/ +GF_Err gf_bifs_decoder_configure_stream(GF_BifsDecoder *codec, u16 ESID, char *DecoderSpecificInfo, u32 DecoderSpecificInfoLength, u32 objectTypeIndication); +/*removes a stream*/ +GF_Err gf_bifs_decoder_remove_stream(GF_BifsDecoder *codec, u16 ESID); + +/*decode a BIFS AU and applies it to the graph (non-memory mode only)*/ +GF_Err gf_bifs_decode_au(GF_BifsDecoder *codec, u16 ESID, char *data, u32 data_length, Double ts_offset); + +/*Memory BIFS decoding - fills the command list with the content of the AU - cf scenegraph_vrml.h for commands usage + @ESID: ID of input stream + @data, @data_length: BIFS AU + @com_list: target list for decoded commands +*/ +GF_Err gf_bifs_decode_command_list(GF_BifsDecoder *codec, u16 ESID, char *data, u32 data_length, GF_List *com_list); + + +/*BIFS encoding*/ +typedef struct __tag_bifs_enc GF_BifsEncoder; + +/*constructor - @graph: scene graph being encoded*/ +GF_BifsEncoder *gf_bifs_encoder_new(GF_SceneGraph *graph); +/*destructor*/ +void gf_bifs_encoder_del(GF_BifsEncoder *codec); +/*setup a destination stream*/ +GF_Err gf_bifs_encoder_new_stream(GF_BifsEncoder *codec, u16 ESID, GF_BIFSConfig *cfg, Bool encodeNames, Bool has_predictive); +/*encodes a list of commands for the given stream in the output buffer - data is dynamically allocated for output +the scenegraph used is the one described in SceneReplace command, hence scalable streams shall be encoded in time order +*/ +GF_Err gf_bifs_encode_au(GF_BifsEncoder *codec, u16 ESID, GF_List *command_list, char **out_data, u32 *out_data_length); +/*returns encoded config desc*/ +GF_Err gf_bifs_encoder_get_config(GF_BifsEncoder *codec, u16 ESID, char **out_data, u32 *out_data_length); +/*returns BIFS version used by codec for given stream*/ +u8 gf_bifs_encoder_get_version(GF_BifsEncoder *codec, u16 ESID); + +/*Encodes current graph as a scene replace*/ +GF_Err gf_bifs_encoder_get_rap(GF_BifsEncoder *codec, char **out_data, u32 *out_data_length); + +#ifdef __cplusplus +} +#endif + + + +#endif /*_GF_BIFS_H_*/ + diff --git a/include/gpac/bifsengine.h b/include/gpac/bifsengine.h new file mode 100644 index 0000000..69e1769 --- /dev/null +++ b/include/gpac/bifsengine.h @@ -0,0 +1,138 @@ +/* + * GPAC Multimedia Framework + * + * Authors: Cyril Concolato - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_BIFSENGINE_H_ +#define _GF_BIFSENGINE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/scene_manager.h> + +#ifndef GPAC_READ_ONLY + +typedef struct __tag_bifs_engine GF_BifsEngine; + +/** + * @calling_object is the calling object on which call back will be called + * @inputContext is the name of a scene file (bt, xmt or mp4) to initialize the coding context + * + * must be called only one time (by process calling the DLL) before other calls + */ +GF_BifsEngine *gf_beng_init(void *calling_object, char *inputContext); + +/** + * @calling_object is the calling object on which call back will be called + * @inputContext is an UTF-8 scene description (with or without IOD) in BT or XMT-A format + * @width, @height: width and height of scene if no IOD is given in the context. + * @usePixelMetrics: metrics system used in the scene, if no IOD is given in the context. + * + * must be called only one time (by process calling the DLL) before other calls + */ +GF_BifsEngine *gf_beng_init_from_string(void *calling_object, char *inputContext, u32 width, u32 height, Bool usePixelMetrics); + + +/** + * @calling_object is the calling object on which call back will be called + * @ctx is an already loaded scene manager + * + * must be called only one time (by process calling the DLL) before other calls + */ +GF_BifsEngine *gf_beng_init_from_context(void *calling_object, GF_SceneManager *ctx); + +/** + * @beng, pointer to the GF_BifsEngine returned by BENC_Init + * @config: pointer to the encoded BIFS config (memory is not allocated) + * @config_len: length of the buffer + * + * must be called after BENC_Init + */ +void gf_beng_get_stream_config(GF_BifsEngine *beng, char **config, u32 *config_len); + +/** + * Encodes the AU context which is not encoded when calling BENC_EncodeAUFromString/File + * Should be called after Aggregate. + * + * @beng, pointer to the GF_BifsEngine returned by BENC_Init + * @AUCallback, pointer on a callback function to get the result of the coding the AU using the current context + * + */ +GF_Err gf_beng_encode_context(GF_BifsEngine *beng, GF_Err (*AUCallback)(void *, char *data, u32 size, u64 ts)); + +/** + * @beng, pointer to the GF_BifsEngine returned by BENC_Init + * @auFile, name of a file containing a description for an access unit (BT or XMT) + * @AUCallback, pointer on a callback function to get the result of the coding the AU using the current context + * + */ +GF_Err gf_beng_encode_from_file(GF_BifsEngine *beng, char *auFile, GF_Err (*AUCallback)(void *, char *data, u32 size, u64 ts)); + +/** + * @beng, pointer to the GF_BifsEngine returned by BENC_Init + * @auString, a char string to encode (must one or several complete nodes in BT + * @AUCallback, pointer on a callback function to get the result of the coding the AU using the current context + * + */ +GF_Err gf_beng_encode_from_string(GF_BifsEngine *beng, char *auString, GF_Err (*AUCallback)(void *, char *data, u32 size, u64 ts)); + +/** + * @beng, pointer to the GF_BifsEngine returned by BENC_Init + * @ctxFileName, name of the file to save the current state of the BIFS scene to + * + * save the current context of the beng. + * if you want to save an aggregate context, use BENC_AggregateCurrentContext before + * + */ +GF_Err gf_beng_save_context(GF_BifsEngine *beng, char *ctxFileName); + +/** + * @beng, pointer to the GF_BifsEngine returned by BENC_Init + * + * aggregates the current context of the beng, creates a scene replace + * + */ +GF_Err gf_beng_aggregate_context(GF_BifsEngine *beng); + +/** + * @beng, pointer to the GF_BifsEngine returned by BENC_Init + * + * release the memory used by this beng, no more call on the beng should happen after this + * + */ +void gf_beng_terminate(GF_BifsEngine *beng); + + +#endif + + +#ifdef __cplusplus +} +#endif // __cplusplus + + +#endif /*_GF_BIFSENGINE_H_*/ + diff --git a/include/gpac/bitstream.h b/include/gpac/bitstream.h new file mode 100644 index 0000000..22ac172 --- /dev/null +++ b/include/gpac/bitstream.h @@ -0,0 +1,450 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_BITSTREAM_H_ +#define _GF_BITSTREAM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file <gpac/bitstream.h> + * \brief bitstream functions. + */ + +/*! + * \addtogroup bs_grp bitstream + * \ingroup utils_grp + * \brief BitStream object + * + * This section documents the bitstream object of the GPAC framework. + * \note Unless specified, all functions assume Big-Endian ordering of data in the bitstream. + * @{ + */ + +#include <gpac/tools.h> + + +enum +{ + GF_BITSTREAM_READ = 0, + GF_BITSTREAM_WRITE, +}; + +typedef struct __tag_bitstream GF_BitStream; + +/*! + * \brief bitstream constructor + * + * Constructs a bitstream from a buffer (read or write mode) + * \param buffer buffer to read or write. In WRITE mode, this can be NULL to let the bitstream object dynamically allocate memory, in which case the size param is ignored. + * \param size size of the buffer given. + * \param mode operation mode for this bitstream: GF_BITSTREAM_READ for read, GF_BITSTREAM_WRITE for write. + * \return new bitstream object + * \note In write mode on an existing data buffer, data overflow is never signaled but simply ignored, it is the caller responsability to ensure it + * does not write more than possible. + */ +GF_BitStream *gf_bs_new(char *buffer, u64 size, u32 mode); +/*! + * \brief bitstream constructor from file handle + * + * Creates a bitstream from a file handle. + * \param f handle of the file to use. This handle must be created with binary mode. + * \param mode operation mode for this bitstream: GF_BITSTREAM_READ for read, GF_BITSTREAM_WRITE for write. + * \return new bitstream object + * \note - You have to open your file in the appropriated mode:\n + * - GF_BITSTREAM_READ: bitstream is constructed for reading\n + * - GF_BITSTREAM_WRITE: bitstream is constructed for writing\n + * \note - you may use any of these modes for a file with read/write access. + * \warning RESULTS ARE UNEXPECTED IF YOU TOUCH THE FILE WHILE USING THE BITSTREAM. + */ +GF_BitStream *gf_bs_from_file(FILE *f, u32 mode); +/*! + * \brief bitstream constructor from file handle + * + * Deletes the bitstream object. If the buffer was created by the bitstream, it is deleted if still present. + */ +void gf_bs_del(GF_BitStream *bs); + +/*! + * \brief integer reading + * + * Reads an integer coded on a number of bit. + * \param bs the target bitstream + * \param nBits the number of bits to read + * \return the integer value read. + */ +u32 gf_bs_read_int(GF_BitStream *bs, u32 nBits); +/*! + * \brief large integer reading + * + * Reads a large integer coded on a number of bit bigger than 32. + * \param bs the target bitstream + * \param nBits the number of bits to read + * \return the large integer value read. + */ +u64 gf_bs_read_long_int(GF_BitStream *bs, u32 nBits); +/*! + * \brief float reading + * + * Reads a float coded as IEEE 32 bit format. + * \param bs the target bitstream + * \return the float value read. + */ +Float gf_bs_read_float(GF_BitStream *bs); +/*! + * \brief double reading + * + * Reads a double coded as IEEE 64 bit format. + * \param bs the target bitstream + * \return the double value read. + */ +Double gf_bs_read_double(GF_BitStream *bs); +/*! + * \brief data reading + * + * Reads a data buffer + * \param bs the target bitstream + * \param data the data buffer to be filled + * \param nbBytes the amount of bytes to read + * \return the number of bytes actually read. + * \warning the data buffer passed must be large enough to hold the desired amount of bytes. + */ +u32 gf_bs_read_data(GF_BitStream *bs, char *data, u32 nbBytes); + +/*! + * \brief align char reading + * + * Reads an integer coded on 8 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \return the char value read. + */ +u32 gf_bs_read_u8(GF_BitStream *bs); +/*! + * \brief align short reading + * + * Reads an integer coded on 16 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \return the short value read. + */ +u32 gf_bs_read_u16(GF_BitStream *bs); +/*! + * \brief align 24-bit integer reading + * + * Reads an integer coded on 24 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_read_u24(GF_BitStream *bs); +/*! + * \brief align integer reading + * + * Reads an integer coded on 32 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_read_u32(GF_BitStream *bs); +/*! + * \brief align large integer reading + * + * Reads an integer coded on 64 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \return the large integer value read. + */ +u64 gf_bs_read_u64(GF_BitStream *bs); +/*! + * \brief little endian integer reading + * + * Reads an integer coded on 32 bits in little-endian order. + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_read_u32_le(GF_BitStream *bs); +/*! + * \brief little endian integer reading + * + * Reads an integer coded on 16 bits in little-endian order. + * \param bs the target bitstream + * \return the integer value read. + */ +u16 gf_bs_read_u16_le(GF_BitStream *bs); + + +/*! + * \brief variable length integer reading + * + * Reads an integer coded on a variable number of 4-bits chunks. The number of chunks is given by the number of non-0 bits at the begining. + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_read_vluimsbf5(GF_BitStream *bs); + +/*! + * \brief bit position + * + * Returns current bit position in the bitstream - only works in memory mode. + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_get_bit_offset(GF_BitStream *bs); + +/*! + * \brief current bit position + * + * Returns bit position in the current byte of the bitstream - only works in memory mode. + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_get_bit_position(GF_BitStream *bs); + + +/*! + * \brief integer writing + * + * Writes an integer on a given number of bits. + * \param bs the target bitstream + * \param value the integer to write + * \param nBits number of bits used to code the integer + */ +void gf_bs_write_int(GF_BitStream *bs, s32 value, s32 nBits); +/*! + * \brief large integer writing + * + * Writes an integer on a given number of bits greater than 32. + * \param bs the target bitstream + * \param value the large integer to write + * \param nBits number of bits used to code the integer + */ +void gf_bs_write_long_int(GF_BitStream *bs, s64 value, s32 nBits); +/*! + * \brief float writing + * + * Writes a float in IEEE 32 bits format. + * \param bs the target bitstream + * \param value the float to write + */ +void gf_bs_write_float(GF_BitStream *bs, Float value); +/*! + * \brief double writing + * + * Writes a double in IEEE 64 bits format. + * \param bs the target bitstream + * \param value the double to write + */ +void gf_bs_write_double(GF_BitStream *bs, Double value); +/*! + * \brief data writing + * + * Writes a data buffer. + * \param bs the target bitstream + * \param data the data to write + * \param nbBytes number of data bytes to write + */ +u32 gf_bs_write_data(GF_BitStream *bs, char *data, u32 nbBytes); + +/*! + * \brief align char writing + * + * Writes an integer on 8 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \param value the char value to write + */ +void gf_bs_write_u8(GF_BitStream *bs, u32 value); +/*! + * \brief align short writing + * + * Writes an integer on 16 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \param value the short value to write + */ +void gf_bs_write_u16(GF_BitStream *bs, u32 value); +/*! + * \brief align 24-bits integer writing + * + * Writes an integer on 24 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \param value the integer value to write + */ +void gf_bs_write_u24(GF_BitStream *bs, u32 value); +/*! + * \brief align integer writing + * + * Writes an integer on 32 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \param value the integer value to write + */ +void gf_bs_write_u32(GF_BitStream *bs, u32 value); +/*! + * \brief align large integer writing + * + * Writes an integer on 64 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \param value the large integer value to write + */ +void gf_bs_write_u64(GF_BitStream *bs, u64 value); +/*! + * \brief little endian integer writing + * + * Writes an integer on 32 bits in little-endian order. + * \param bs the target bitstream + * \param value the integer value to write + */ +void gf_bs_write_u32_le(GF_BitStream *bs, u32 value); +/*! + * \brief little endian short writing + * + * Writes an integer on 16 bits in little-endian order. + * \param bs the target bitstream + * \param value the short value to write + */ +void gf_bs_write_u16_le(GF_BitStream *bs, u32 value); + +/*! + * \brief end of bitstream management + * + * Assigns a notification callback function for end of stream signaling in read mode + * \param bs the target bitstream + * \param EndOfStream the notification function to use + * \param par opaque user data passed to the bitstream + */ +void gf_bs_set_eos_callback(GF_BitStream *bs, void (*EndOfStream)(void *par), void *par); + +/*! + * \brief bitstream alignment + * + * Aligns bitstream to next byte boundary. In write mode, this will write 0 bit values until alignment. + * \param bs the target bitstream + * \return the number of bits read/written until alignment + */ +u8 gf_bs_align(GF_BitStream *bs); +/*! + * \brief capacity query + * + * Returns the number of bytes still available in the bitstream in read mode. + * \param bs the target bitstream + * \return the number of bytes still available in read mode, -1 in write modes. + */ +u64 gf_bs_available(GF_BitStream *bs); +/*! + * \brief buffer fetching + * + * Fetches the internal bitstream buffer in write mode. If a buffer was given at the bitstream construction, or if the bitstream is in read mode, this does nothing. + * \param bs the target bitstream + * \param output address of a memory block to be allocated for bitstream data. + * \param outSize set to the size of the allocated memory block. + * \note + * It is the user responsability to destroy the allocated buffer + * Once this function has been called, the internal bitstream buffer is reseted. + */ +void gf_bs_get_content(GF_BitStream *bs, char **output, u32 *outSize); +/*! + * \brief byte skipping + * + * Skips bytes in the bitstream. In Write mode, this will write the 0 integer value for memory-based bitstreams or seek the stream + for file-based bitstream. + * \param bs the target bitstream + * \param nbBytes the number of bytes to skip + */ +void gf_bs_skip_bytes(GF_BitStream *bs, u64 nbBytes); + +/*! + *\brief bitstream seeking + * + *Seeks the bitstream to a given offset after the begining of the stream. This will perform alignment of the bitstream in all modes. + *\warning Results are unpredictable if seeking beyond the bitstream end is performed. + *\param bs the target bitstream + *\param offset buffer/file offset to seek to + */ +GF_Err gf_bs_seek(GF_BitStream *bs, u64 offset); + +/*! + *\brief bit peeking + * + *Peeks a given number of bits (read without moving the position indicator) for read modes only. + *\param bs the target bitstream + *\param numBits the number of bits to peek + *\param byte_offset + * if set, bitstream is aligned and moved from byte_offset before peeking (byte-aligned picking) + * otherwise, bitstream is not aligned and bits are peeked from current state + *\return the integer value read +*/ +u32 gf_bs_peek_bits(GF_BitStream *bs, u32 numBits, u32 byte_offset); + +/*! + *\brief bit reservoir query + * + * Queries the number of bits available in read mode. + *\param bs the target bitstream + *\return number of available bits if position is in the last byte of the buffer/stream, 8 otherwise + */ +u8 gf_bs_bits_available(GF_BitStream *bs); +/*! + *\brief position query + * + *Returns the reading/writting position in the buffer/file. + *\param bs the target bitstream + *\return the read/write position of the bitstream + */ +u64 gf_bs_get_position(GF_BitStream *bs); +/*! + *\brief size query + * + *Returns the size of the associated buffer/file. + *\param bs the target bitstream + *\return the size of the bitstream + */ +u64 gf_bs_get_size(GF_BitStream *bs); +/*! + *\brief file-based size query + * + *Returns the size of a file-based bitstream and force a seek to end of file. This is used in case the file handle + *describes a file being constructed on disk while being read? + * + *\param bs the target bitstream + *\return the disk size of the associated file + */ +u64 gf_bs_get_refreshed_size(GF_BitStream *bs); + + + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_BITSTREAM_H_*/ + diff --git a/include/gpac/color.h b/include/gpac/color.h new file mode 100644 index 0000000..9c864eb --- /dev/null +++ b/include/gpac/color.h @@ -0,0 +1,239 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools interfaces + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_RASTER_2D_H_ +#define _GF_RASTER_2D_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/constants.h> + + +/*! + *\addtogroup color_grp color + *\ingroup utils_grp + *\brief Color tools + * + *This section documents color tools for image processing and color conversion + * @{ + */ + + +/*!\brief Video framebuffer object + * + *The video framebuffer object represents uncompressed color data like images in a variety of formats. Data in + *the video framebuffer MUST be continuous. +*/ +typedef struct +{ + /*!Width of the video framebuffer */ + u32 width; + /*!Height of the video framebuffer */ + u32 height; + /*!Vertical pitch of the video framebuffer (number of bytes to skip to go down one line in the buffer). May be + negative for some framebuffers (embedded devices)*/ + s32 pitch; + /*!Pixel format of the video framebuffer*/ + u32 pixel_format; + /*!pointer to the begining of the video memory (top-left corner)*/ + char *video_buffer; + /*!indicates that the video data reside on systems memory or video card one*/ + Bool is_hardware_memory; +} GF_VideoSurface; + +/*!\brief Video Window object + * + *The video window object represents a rectangle in framebuffer coordinate system +*/ +typedef struct +{ + /*!left-most coordinate of the rectangle*/ + u32 x; + /*!top-most coordinate of the rectangle*/ + u32 y; + /*!width of the rectangle*/ + u32 w; + /*!height of the rectangle*/ + u32 h; +} GF_Window; + + +/*!\brief color matrix object + * + *The Color transformation matrix object allows complete color space transformation (shift, rotate, skew, add).\n + *The matrix coefs are in rgba order, hence the color RGBA is transformed to: + \code + R' m0 m1 m2 m3 m4 R + G' m5 m6 m7 m8 m9 G + B' = m10 m11 m12 m13 m14 x B + A' m15 m16 m17 m18 m19 A + 0 0 0 0 0 1 0 + \endcode + *Coeficients are in intensity scale, ranging from 0 to \ref FIX_ONE. +*/ +typedef struct +{ + /*!color matrix coefficient*/ + Fixed m[20]; + /*!internal flag to speed up things when matrix is identity. This is a read only flag, do not modify it*/ + u32 identity; +} GF_ColorMatrix; + +/*!\brief ARGB color object + * + *The color type used in the GPAC framework represents colors in the form 0xAARRGGBB, with each component ranging from 0 to 255 +*/ +typedef u32 GF_Color; +/*!\hideinitializer color formating macro from alpha, red, green and blue components expressed as integers ranging from 0 to 255*/ +#define GF_COL_ARGB(a, r, g, b) ((a)<<24 | (r)<<16 | (g)<<8 | (b)) +/*!\hideinitializer color formating macro from alpha, red, green and blue components expressed as fixed numbers ranging from 0 to \ref FIX_ONE*/ +#define GF_COL_ARGB_FIXED(_a, _r, _g, _b) GF_COL_ARGB(FIX2INT(255*(_a)), FIX2INT(255*(_r)), FIX2INT(255*(_g)), FIX2INT(255*(_b))) +/*!\hideinitializer gets alpha component of a color*/ +#define GF_COL_A(c) (u8) ((c)>>24) +/*!\hideinitializer gets red component of a color*/ +#define GF_COL_R(c) (u8) ( ((c)>>16) & 0xFF) +/*!\hideinitializer gets green component of a color*/ +#define GF_COL_G(c) (u8) ( ((c)>>8) & 0xFF) +/*!\hideinitializer gets blue component of a color*/ +#define GF_COL_B(c) (u8) ( (c) & 0xFF) +/*!\hideinitializer 16-bits color formating macro from red, green and blue components*/ +#define GF_COL_565(r, g, b) (u16) (((r & 248)<<8) + ((g & 252)<<3) + (b>>3)) +/*!\hideinitializer 15-bits color formating macro from red, green and blue components*/ +#define GF_COL_555(r, g, b) (u16) (((r & 248)<<7) + ((g & 248)<<2) + (b>>3)) +/*!\hideinitializer 15-bits color formating macro from red, green and blue components*/ +#define GF_COL_444(r, g, b) (u16) (((r & 240)<<4) + ((g & 240)) + ((b & 240)>>4)) +/*!\hideinitializer 16-bits alphagrey color formating macro alpha and grey components*/ +#define GF_COL_AG(a, g) (u16) ( (a << 8) | g) +/*!\hideinitializer transfoms a 32-bits color into a 16-bits one.\note alpha component is lost*/ +#define GF_COL_TO_565(c) (((GF_COL_R(c) & 248)<<8) + ((GF_COL_G(c) & 252)<<3) + (GF_COL_B(c)>>3)) +/*!\hideinitializer transfoms a 32-bits color into a 15-bits one.\note alpha component is lost*/ +#define GF_COL_TO_555(c) (((GF_COL_R(c) & 248)<<7) + ((GF_COL_G(c) & 248)<<2) + (GF_COL_B(c)>>3)) +/*!\hideinitializer transfoms a 32-bits color into a 16-bits alphagrey one.\note red component is used for grey, green and blue components are lost.*/ +#define GF_COL_TO_AG(c) ( (GF_COL_A(c) << 8) | GF_COL_R(c)) +/*!\hideinitializer transfoms a 32-bits color into a 15-bits one.\note alpha component is lost*/ +#define GF_COL_TO_444(c) (((GF_COL_R(c) & 240)<<4) + ((GF_COL_G(c) & 240)) + ((GF_COL_B(c)>>4) & 240) ) + +/*!Inits a color matrix to identity*/ +void gf_cmx_init(GF_ColorMatrix *_this); +/*!Inits all coefficients of a color matrix + *\param _this color matrix to initialize + *\param coefs list of the 20 fixed numbers to copy +*/ +void gf_cmx_set_all(GF_ColorMatrix *_this, Fixed *coefs); +/*!Inits all coefficients of a color matrix + *\param _this color matrix to initialize + *\param mrr red-to-red multiplication factor + *\param mrg red-to-green multiplication factor + *\param mrb red-to-blue multiplication factor + *\param mra red-to-alpha multiplication factor + *\param tr red translation factor + *\param mgr green-to-red multiplication factor + *\param mgg green-to-green multiplication factor + *\param mgb green-to-blue multiplication factor + *\param mga green-to-alpha multiplication factor + *\param tg green translation factor + *\param mbr blue-to-red multiplication factor + *\param mbg blue-to-green multiplication factor + *\param mbb blue-to-blue multiplication factor + *\param mba blue-to-alpha multiplication factor + *\param tb blue translation factor + *\param mar alpha-to-red multiplication factor + *\param mag alpha-to-green multiplication factor + *\param mab alpha-to-blue multiplication factor + *\param maa alpha-to-alpha multiplication factor + *\param ta alpha translation factor +*/ +void gf_cmx_set(GF_ColorMatrix *_this, + Fixed mrr, Fixed mrg, Fixed mrb, Fixed mra, Fixed tr, + Fixed mgr, Fixed mgg, Fixed mgb, Fixed mga, Fixed tg, + Fixed mbr, Fixed mbg, Fixed mbb, Fixed mba, Fixed tb, + Fixed mar, Fixed mag, Fixed mab, Fixed maa, Fixed ta); +/*!Inits a matrix from another matrix + *\param _this color matrix to initialize + *\param from color matrix to copy from +*/ +void gf_cmx_copy(GF_ColorMatrix *_this, GF_ColorMatrix *from); +/*!\brief color matrix multiplication + * + *Multiplies a color matrix by another one. Result is _this*with + *\param _this color matrix to transform. Once the function called, _this will contain the resulting color matrix + *\param with color matrix to add +*/ +void gf_cmx_multiply(GF_ColorMatrix *_this, GF_ColorMatrix *with); +/*!\brief color matrix transform + * + *Transforms a color with a given color matrix + *\param _this color matrix to use. + *\param col color to transform + *\return transformed color +*/ +GF_Color gf_cmx_apply(GF_ColorMatrix *_this, GF_Color col); +/*!\brief color components matrix transform + * + *Transforms color components with a given color matrix + *\param _this color matrix to use. + *\param a pointer to alpha component. Once the function is called, a contains the transformed alpha component + *\param r pointer to red component. Once the function is called, r contains the transformed red component + *\param g pointer to green component. Once the function is called, g contains the transformed green component + *\param b pointer to blue component. Once the function is called, b contains the transformed blue component +*/ +void gf_cmx_apply_fixed(GF_ColorMatrix *_this, Fixed *a, Fixed *r, Fixed *g, Fixed *b); + + +/*!\brief Color Key descriptor + * + *The ColorKey object represents a ColorKey with low and high threshold keying +*/ +typedef struct +{ + /*!color key R, G, and B components*/ + u8 r, g, b; + /*!Alpha value for opaque (non-keyed) pixels*/ + u8 alpha; + /*!low variance threshold*/ + u8 low; + /*!high variance threshold*/ + u8 high; +} GF_ColorKey; + +/*!\brief not done yet + * + */ +GF_Err gf_stretch_bits(GF_VideoSurface *dst, GF_VideoSurface *src, GF_Window *dst_wnd, GF_Window *src_wnd, s32 dst_x_pitch, u8 alpha, Bool flip, GF_ColorKey *colorKey, GF_ColorMatrix * cmat); + + + +/*! @} */ + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_RASTER_2D_H_*/ + diff --git a/include/gpac/compositor.h b/include/gpac/compositor.h new file mode 100644 index 0000000..e6dca34 --- /dev/null +++ b/include/gpac/compositor.h @@ -0,0 +1,164 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_COMPOSITOR_H_ +#define _GF_COMPOSITOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/*include scene graph API*/ +#include <gpac/scenegraph.h> +/*GF_User and GF_Terminal */ +#include <gpac/user.h> +/*frame buffer definition*/ +#include <gpac/color.h> + +typedef struct __tag_compositor GF_Compositor; + +/*creates default compositor +if self_threaded, video compositor uses a dedicated thread, otherwise visual rendering is done by the user +audio compositor always runs in its own thread if enabled +term may be NULL, in which case InputSensors won't be enabled +*/ +GF_Compositor *gf_sc_new(GF_User *user_interface, Bool self_threaded, GF_Terminal *term); +void gf_sc_del(GF_Compositor *sr); + +/*sets simulation frame rate*/ +void gf_sc_set_fps(GF_Compositor *sr, Double fps); + +/*set the root scene graph of the compositor - if NULL remove current and reset simulation time*/ +GF_Err gf_sc_set_scene(GF_Compositor *sr, GF_SceneGraph *scene_graph); + +/*if the compositor doesn't use its own thread for visual, this will perform a render pass*/ +Bool gf_sc_draw_frame(GF_Compositor *sr); + +/*inits rendering info for the node - shall be called for all nodes the parent system doesn't handle*/ +void gf_sc_on_node_init(GF_Compositor *sr, GF_Node *node); + +/*notify the given node has been modified. The compositor filters object to decide whether the scene graph has to be +traversed or not- if object is NULL, this means complete traversing of the graph is requested (use carefully since it +can be a time consuming operation)*/ +void gf_sc_invalidate(GF_Compositor *sr, GF_Node *byObj); + +/*return the compositor time - this is the time every time line syncs on*/ +u32 gf_sc_get_clock(GF_Compositor *sr); + + +/*locks/unlocks the visual scene rendering - modification of the scene tree shall only happen when scene compositor is locked*/ +void gf_sc_lock(GF_Compositor *sr, Bool doLock); +/*locks/unlocks the audio scene rendering - this is needed whenever an audio object changes config on the fly*/ +void gf_sc_lock_audio(GF_Compositor *sr, Bool doLock); + +/*notify user input - returns 0 if event hasn't been handled by the compositor*/ +Bool gf_sc_user_event(GF_Compositor *sr, GF_Event *event); + +/*maps screen coordinates to bifs 2D coordinates for the current zoom/pan settings +X and Y are point coordinates in the display expressed in BIFS-like fashion (0,0) at center of +display and Y increasing from bottom to top*/ +void gf_sc_map_point(GF_Compositor *sr, s32 X, s32 Y, Fixed *bifsX, Fixed *bifsY); + +/*signal the size of the display area has been changed*/ +GF_Err gf_sc_size_changed(GF_Compositor *sr, u32 NewWidth, u32 NewHeight); + +/*set/get user options - options are as defined in user.h*/ +GF_Err gf_sc_set_option(GF_Compositor *sr, u32 type, u32 value); +u32 gf_sc_get_option(GF_Compositor *sr, u32 type); + +/*returns current FPS +if @absoluteFPS is set, the return value is the absolute framerate, eg NbFrameCount/NbTimeSpent regardless of +whether a frame has been drawn or not, which means the FPS returned can be much greater than the compositor FPS +if @absoluteFPS is not set, the return value is the FPS taking into account not drawn frames (eg, less than or equal to +compositor FPS) +*/ +Double gf_sc_get_fps(GF_Compositor *sr, Bool absoluteFPS); + +Bool gf_sc_has_text_selection(GF_Compositor *compositor); +const char *gf_sc_get_selected_text(GF_Compositor *compositor); + +GF_Err gf_sc_paste_text(GF_Compositor *compositor, const char *text); + +/*user-define management: this is used for instant visual rendering of the scene graph, +for exporting or authoring tools preview. User is responsible for calling render when desired and shall also maintain +scene timing*/ + +/*force render tick*/ +void gf_sc_render(GF_Compositor *sr); +/*gets screen buffer - this locks the scene graph too until released is called*/ +GF_Err gf_sc_get_screen_buffer(GF_Compositor *sr, GF_VideoSurface *framebuffer, Bool depth_buffer); +/*releases screen buffer and unlocks graph*/ +GF_Err gf_sc_release_screen_buffer(GF_Compositor *sr, GF_VideoSurface *framebuffer); + +/*renders one frame*/ +void gf_sc_simulation_tick(GF_Compositor *sr); + +/*forces graphics cache recompute*/ +void gf_sc_reset_graphics(GF_Compositor *sr); + +/*picks a node (may return NULL) - coords are given in OS client system coordinate, as in UserInput*/ +GF_Node *gf_sc_pick_node(GF_Compositor *sr, s32 X, s32 Y); + +/*get viewpoints/viewports for main scene - idx is 1-based, and if greater than number of viewpoints return GF_EOS*/ +GF_Err gf_sc_get_viewpoint(GF_Compositor *sr, u32 viewpoint_idx, const char **outName, Bool *is_bound); +/*set viewpoints/viewports for main scene given its name - idx is 1-based, or 0 to retrieve by viewpoint name +if only one viewpoint is present in the scene, this will bind/unbind it*/ +GF_Err gf_sc_set_viewpoint(GF_Compositor *sr, u32 viewpoint_idx, const char *viewpoint_name); + +/*render subscene root node. rs is the current traverse stack +this is needed to handle graph metrics changes between scenes...*/ +void gf_sc_traverse_subscene(GF_Compositor *sr, GF_Node *inline_parent, GF_SceneGraph *subscene, void *rs); + +/*set outupt size*/ +GF_Err gf_sc_set_size(GF_Compositor *sr, u32 NewWidth, u32 NewHeight); +/*get outupt size*/ +Bool gf_sc_get_size(GF_Compositor *sr, u32 *Width, u32 *Height); + +/*returns total length of audio hardware buffer in ms, 0 if no audio*/ +u32 gf_sc_get_audio_buffer_length(GF_Compositor *sr); + +/*add/remove extra scene from compositor (extra scenes are OSDs or any other scene graphs not directly +usable by main scene, like 3GP text streams*/ +void gf_sc_register_extra_graph(GF_Compositor *sr, GF_SceneGraph *extra_scene, Bool do_remove); + +/*gets audio hardware delay*/ +u32 gf_sc_get_audio_delay(GF_Compositor *sr); + +/*returns total length of audio hardware buffer in ms, 0 if no audio*/ +u32 gf_sc_get_audio_buffer_length(GF_Compositor *sr); + +void *gf_sc_get_visual_compositor(GF_Compositor *sr); + +GF_Compositor *gf_sc_get_compositor(GF_Node *node); + +Bool gf_sc_script_action(GF_Compositor *sr, u32 type, GF_Node *n, GF_JSAPIParam *param); + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_COMPOSITOR_H_*/ + diff --git a/include/gpac/config_file.h b/include/gpac/config_file.h new file mode 100644 index 0000000..17d7f13 --- /dev/null +++ b/include/gpac/config_file.h @@ -0,0 +1,165 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_CONFIG_FILE_H_ +#define _GF_CONFIG_FILE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file <gpac/config_file.h> + * \brief configuration file functions. + */ + + /*! + * \addtogroup cfg_grp configuration + * \ingroup utils_grp + * \brief Configuration File object + * + * This section documents the configuration file object of the GPAC framework. + * This file is formatted as the INI file mode of WIN32 in sections and keys.\n + *\note For more information on the GPAC configuration file itself, please refer to the GPAC configuration help provided with GPAC. + * @{ + */ + +#include <gpac/tools.h> + + +typedef struct __tag_config GF_Config; + +/*! + * \brief configuration file constructor + * + *Constructs a configuration file + *\param filePath directory the file is located in + *\param fileName name of the configuration file + *\return the configuration file object + */ +GF_Config *gf_cfg_new(const char *filePath, const char *fileName); +/*! + * \brief configuration file destructor + * + *Destroys the configuration file and saves it if needed. + *\param cfgFile the target configuration file + */ +void gf_cfg_del(GF_Config *cfgFile); +/*! + * \brief configuration saving + * + *Saves the configuration file if modified. + *\param cfgFile the target configuration file + */ +GF_Err gf_cfg_save(GF_Config *iniFile); +/*! + * \brief key value query + * + *Gets a key value from its section and name. + *\param cfgFile the target configuration file + *\param secName the desired key parent section name + *\param keyName the desired key name + *\return the desired key value if found, NULL otherwise. + */ +const char *gf_cfg_get_key(GF_Config *cfgFile, const char *secName, const char *keyName); +/*! + * \brief key value update + * + *Sets a key value from its section and name. + *\param cfgFile the target configuration file + *\param secName the desired key parent section name + *\param keyName the desired key name + *\param keyValue the desired key value + *\note this will also create both section and key if they are not found in the configuration file + */ +GF_Err gf_cfg_set_key(GF_Config *cfgFile, const char *secName, const char *keyName, const char *keyValue); +/*! + * \brief section count query + * + *Gets the number of sections in the configuration file + *\param cfgFile the target configuration file + *\return the number of sections + */ +u32 gf_cfg_get_section_count(GF_Config *cfgFile); +/*! + * \brief section name query + * + *Gets a section name based on its index + *\param cfgFile the target configuration file + *\param secIndex 0-based index of the section to query + *\return the section name if found, NULL otherwise + */ +const char *gf_cfg_get_section_name(GF_Config *cfgFile, u32 secIndex); +/*! + * \brief key count query + * + *Gets the number of keys in a section of the configuration file + *\param cfgFile the target configuration file + *\param secName the target section + *\return the number of keys in the section + */ +u32 gf_cfg_get_key_count(GF_Config *cfgFile, const char *secName); +/*! + * \brief key count query + * + *Gets the number of keys in a section of the configuration file + *\param cfgFile the target configuration file + *\param secName the target section + *\param keyIndex 0-based index of the key in the section + *\return the key name if found, NULL otherwise + */ +const char *gf_cfg_get_key_name(GF_Config *cfgFile, const char *secName, u32 keyIndex); + +/*! + * \brief key insertion + * + *Inserts a new key in a given section. Returns an error if a key with the given name + *already exists in the section + *\param cfgFile the target configuration file + *\param secName the target section + *\param keyName the name of the target key + *\param keyValue the value for the new key + *\param index the 0-based index position of the new key + */ +GF_Err gf_cfg_insert_key(GF_Config *cfgFile, const char *secName, const char *keyName, const char *keyValue, u32 index); + +/*! + * \brief section destrouction + * + *Removes all entries in the given section + *\param cfgFile the target configuration file + *\param secName the target section + */ +void gf_cfg_del_section(GF_Config *cfgFile, const char *secName); + +/*! @} */ + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_CONFIG_FILE_H_*/ + diff --git a/include/gpac/constants.h b/include/gpac/constants.h new file mode 100644 index 0000000..54cad88 --- /dev/null +++ b/include/gpac/constants.h @@ -0,0 +1,403 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / exported constants + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_CONSTANTS_H_ +#define _GF_CONSTANTS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/tools.h> + +/*! \addtogroup cst_grp constants + * \brief Constants used within GPAC + * + * This section documents some constants used in the GPAC framework which are not related to + * any specific sub-project. + * \ingroup utils_grp + * @{ + */ + + +/*! + * \brief Supported media stream types + * \hideinitializer + * + * Supported media stream types for media objects. +*/ +enum +{ + /*!MPEG-4 Object Descriptor Stream*/ + GF_STREAM_OD = 0x01, + /*!MPEG-4 Object Clock Reference Stream*/ + GF_STREAM_OCR = 0x02, + /*!MPEG-4 Scene Description Stream*/ + GF_STREAM_SCENE = 0x03, + /*!Visual Stream (Video, Image or MPEG-4 SNHC Tools)*/ + GF_STREAM_VISUAL = 0x04, + /*!Audio Stream (Audio, MPEG-4 Structured-Audio Tools)*/ + GF_STREAM_AUDIO = 0x05, + /*!MPEG-7 Description Stream*/ + GF_STREAM_MPEG7 = 0x06, + /*!MPEG-4 Intellectual Property Management and Protection Stream*/ + GF_STREAM_IPMP = 0x07, + /*!MPEG-4 Object Content Information Stream*/ + GF_STREAM_OCI = 0x08, + /*!MPEG-4 MPEGlet Stream*/ + GF_STREAM_MPEGJ = 0x09, + /*!MPEG-4 User Interaction Stream*/ + GF_STREAM_INTERACT = 0x0A, + /*!MPEG-4 IPMP Tool Stream*/ + GF_STREAM_IPMP_TOOL = 0x0B, + /*!MPEG-4 Font Data Stream*/ + GF_STREAM_FONT = 0x0C, + /*!MPEG-4 Streaming Text Stream*/ + GF_STREAM_TEXT = 0x0D, + /*!Nero Digital Subpicture Stream*/ + GF_STREAM_ND_SUBPIC = 0x38, + + /*GPAC internal stream types*/ + + + /*!GPAC Private Scene streams\n + *\n\note + *this stream type (MPEG-4 user-private) is reserved for streams only used to create a scene decoder + *handling the scene without input streams, as is the case for file readers (BT/VRML/XML..).\n + *The decoderSpecificInfo carried is as follows: + \code + u32 file_size: total file size + char file_name[dsi_size - sizeof(u32)]: local file name. + \n\note: File may be a cache file, it is the decoder responsability to check if the file is completely + downloaded before parsing if needed. + \endcode + *The inBufferLength param for decoders using these streams is the stream clock in ms (no input data is given).\n + *The "dummy_in" module is available to generate these streams for common files, and also takes care of proper + clock init in case of seeking.\n + *This is a reentrant stream type: if any media object with this streamtype also exist in the scene, they will be + *attached to the scene decoder (except when a new inline scene is detected, in which case a new decoder will + *be created). This allows for animation/sprite usage along with the systems timing/stream management.\n + *\n + *the objectTypeIndication currently in use for these streams are documented below\n + */ + GF_STREAM_PRIVATE_SCENE = 0x20, +}; + + +/*! + * Media Object types + * + * This type provides a hint to network modules which may have to generate an service descriptor on the fly. + * They occur only if objects/services used in the scene are not referenced through ObjectDescriptors (MPEG-4) + * but direct through URL +*/ +enum +{ + /*!service descriptor expected is of undefined type. This should be treated like GF_MEDIA_OBJECT_SCENE*/ + GF_MEDIA_OBJECT_UNDEF = 0, + /*!service descriptor expected is of SCENE type and shall contain a scene stream and OD one if needed*/ + GF_MEDIA_OBJECT_SCENE, + /*!service descriptor expected is of SCENE UPDATES type (animation streams)*/ + GF_MEDIA_OBJECT_UPDATES, + /*!service descriptor expected is of VISUAL type*/ + GF_MEDIA_OBJECT_VIDEO, + /*!service descriptor expected is of AUDIO type*/ + GF_MEDIA_OBJECT_AUDIO, + /*!service descriptor expected is of TEXT type (3GPP/MPEG4)*/ + GF_MEDIA_OBJECT_TEXT, + /*!service descriptor expected is of UserInteraction type (MPEG-4 InputSensor)*/ + GF_MEDIA_OBJECT_INTERACT, +}; + + +/*! + * \brief Pixel Formats + * + * Supported pixel formats for everything using video + *\note For textures using 32 bit ARGB/RGB_32/BGR_32: + *\li on little endian machines, shall be ordered in memory as BGRA, + *\li on big endians, shall be ordered in memory as ARGB + *so that *(u32*)pixel_mem is always ARGB (0xAARRGGBB). +*/ +typedef enum +{ + /*!8 bit GREY */ + GF_PIXEL_GREYSCALE = GF_4CC('G','R','E','Y'), + /*!16 bit greyscale*/ + GF_PIXEL_ALPHAGREY = GF_4CC('G','R','A','L'), + /*!12 bit RGB on 16 bits (4096 colors)*/ + GF_PIXEL_RGB_444 = GF_4CC('R','4','4','4'), + /*!15 bit RGB*/ + GF_PIXEL_RGB_555 = GF_4CC('R','5','5','5'), + /*!16 bit RGB*/ + GF_PIXEL_RGB_565 = GF_4CC('R','5','6','5'), + /*!24 bit RGB*/ + GF_PIXEL_RGB_24 = GF_4CC('R','G','B','3'), + /*!24 bit BGR - used for graphics cards video format signaling*/ + GF_PIXEL_BGR_24 = GF_4CC('B','G','R','3'), + /*!32 bit RGB*/ + GF_PIXEL_RGB_32 = GF_4CC('R','G','B','4'), + /*!32 bit BGR - used for graphics cards video format signaling*/ + GF_PIXEL_BGR_32 = GF_4CC('B','G','R','4'), + + /*!32 bit ARGB.*/ + GF_PIXEL_ARGB = GF_4CC('A','R','G','B'), + /*!32 bit RGBA (openGL like)*/ + GF_PIXEL_RGBA = GF_4CC('R','G','B', 'A'), + + /*!YUV packed format*/ + GF_PIXEL_YUY2 = GF_4CC('Y','U','Y','2'), + /*!YUV packed format*/ + GF_PIXEL_YVYU = GF_4CC('Y','V','Y','U'), + /*!YUV packed format*/ + GF_PIXEL_UYVY = GF_4CC('U','Y','V','Y'), + /*!YUV packed format*/ + GF_PIXEL_VYUY = GF_4CC('V','Y','U','Y'), + /*!YUV packed format*/ + GF_PIXEL_Y422 = GF_4CC('Y','4','2','2'), + /*!YUV packed format*/ + GF_PIXEL_UYNV = GF_4CC('U','Y','N','V'), + /*!YUV packed format*/ + GF_PIXEL_YUNV = GF_4CC('Y','U','N','V'), + /*!YUV packed format*/ + GF_PIXEL_V422 = GF_4CC('V','4','2','2'), + + /*!YUV planar format*/ + GF_PIXEL_YV12 = GF_4CC('Y','V','1','2'), + /*!YUV planar format*/ + GF_PIXEL_IYUV = GF_4CC('I','Y','U','V'), + /*!YUV planar format*/ + GF_PIXEL_I420 = GF_4CC('I','4','2','0'), + + /*used in triscope mode and dumping rgbds textures in non-triscope mode*/ + GF_PIXEL_RGBDS = GF_4CC('3', 'C', 'D', 'S'), + + /*used for dumping rgbds textures in non-triscope mode*/ + GF_PIXEL_RGBD = GF_4CC('R', 'G', 'B', 'D'), + + /*!YV12 + Alpha plane*/ + GF_PIXEL_YUVA = GF_4CC('Y', 'U', 'V', 'A') + + +} GF_PixelFormat; + + +/*! + * \brief Scene ObjectTypeIndication Formats + * + * Supported ObjectTypeIndication for scene description streams. *_FILE_* are only used with private scene streams + * and only carry the file name for the scene. Other internal stream types can be used in a real streaming environment +*/ +enum +{ + /*!OTI for BIFS v1*/ + GPAC_OTI_SCENE_BIFS = 0x01, + /*!OTI for BIFS v2*/ + GPAC_OTI_SCENE_BIFS_V2 = 0x02, + /*!OTI for BIFS InputSensor streams*/ + GPAC_OTI_SCENE_INTERACT = 0x03, + /*!OTI forLASeR streams*/ + GPAC_OTI_SCENE_LASER = 0x09, + + /*!OTI for dummy streams (dsi = file name) using the generci context loader (BIFS/VRML/SWF/...) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_GENERIC = 0xC0, + /*!OTI for SVG dummy stream (dsi = file name) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_SVG = 0xC1, + /*!OTI for LASeR/SAF+XML dummy stream (dsi = file name) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_LASER = 0xC2, + /*!OTI for XBL dummy streams (dsi = file name) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_XBL = 0xC3, + /*!OTI for EPG dummy streams (dsi = null) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_EPG = 0xC4, + + /*!OTI for streaming SVG - GPAC internal*/ + GPAC_OTI_SCENE_SVG = 0xD0, + /*!OTI for streaming SVG + gz - GPAC internal*/ + GPAC_OTI_SCENE_SVG_GZ = 0xD1, + /*!OTI for DIMS (dsi = 3GPP DIMS configuration) - GPAC internal*/ + GPAC_OTI_SCENE_DIMS = 0xD2, +}; + + +/*! + * \brief Extra ObjectTypeIndication + * + * ObjectTypeIndication for media (audio/video) codecs not defined in MPEG-4. Since GPAC signals streams through MPEG-4 Descriptions, + * it needs extensions for non-MPEG-4 streams such as AMR, H263 , etc.\n + *\note The decoder specific info for such streams is always carried encoded, with the following syntax:\n + * DSI Syntax for audio streams + \code + * u32 codec_four_cc: the codec 4CC reg code / codec id for ffmpeg + * u32 sample_rate: sampling rate or 0 if unknown + * u16 nb_channels: num channels or 0 if unknown + * u16 frame_size: num audio samples per frame or 0 if unknown + * u8 nb_bits_per_sample: nb bits or 0 if unknown + * u8 num_frames_per_au: num audio frames per AU (used in 3GPP, max 15), 0 if unknown + * char *data: per-codec extensions till end of DSI bitstream + \endcode + \n + * DSI Syntax for video streams + \code + * u32 codec_four_cc: the codec 4CC reg code / codec id for ffmpeg + * u16 width: video width or 0 if unknown + * u16 height: video height or 0 if unknown + * char *data: per-codec extensions till end of DSI bitstream + \endcode +*/ +#define GPAC_OTI_MEDIA_GENERIC 0x80 + +/*! + * \brief FFMPEG ObjectTypeIndication + * + * ObjectTypeIndication for FFMPEG codecs not defined in MPEG-4. FFMPEG uses the base GPAC_OTI_MEDIA_GENERIC specific info formats, and extends it as follows: + \code + * u32 bit_rate: the stream rate or 0 if unknown + * u32 codec_tag: FFMPEG codec tag as defined in libavcodec + * char *data: codec extensions till end of DSI bitstream + \endcode + */ +#define GPAC_OTI_MEDIA_FFMPEG 0x81 + + +/*! + * \brief OGG ObjectTypeIndication + * + * Object type indication for all OGG media. The DSI contains all intitialization ogg packets for the codec + * and is formated as follows:\n + *\code + while (dsi_size) { + bit(16) packet_size; + char packet[packet_size]; + dsi_size -= packet_size; + }\endcode +*/ +#define GPAC_OTI_MEDIA_OGG 0xDD + + + +/*channel cfg flags - DECODERS MUST OUTPUT STEREO/MULTICHANNEL IN THIS ORDER*/ +/*! + * \brief Audio Channel Configuration + * + * Audio channel flags for spatialization. + \note Decoders must output stereo/multichannel audio channels in this order in the decoded audio frame. + */ +enum +{ + /*!Left Audio Channel*/ + GF_AUDIO_CH_FRONT_LEFT = (1), + /*!Right Audio Channel*/ + GF_AUDIO_CH_FRONT_RIGHT = (1<<1), + /*!Center Audio Channel - may also be used to signal monophonic audio*/ + GF_AUDIO_CH_FRONT_CENTER = (1<<2), + /*!LFE Audio Channel*/ + GF_AUDIO_CH_LFE = (1<<3), + /*!Back Left Audio Channel*/ + GF_AUDIO_CH_BACK_LEFT = (1<<4), + /*!Back Right Audio Channel*/ + GF_AUDIO_CH_BACK_RIGHT = (1<<5), + /*!Back Center Audio Channel*/ + GF_AUDIO_CH_BACK_CENTER = (1<<6), + /*!Side Left Audio Channel*/ + GF_AUDIO_CH_SIDE_LEFT = (1<<7), + /*!Side Right Audio Channel*/ + GF_AUDIO_CH_SIDE_RIGHT = (1<<8) +}; + + + +/*DIMS unit flags */ +/*! + * \brief DIMS Unit header flags + * + * DIMS Unit header flags as 3GPP TS 26.142. + */ +enum +{ + /*!S: is-Scene: DIMS unit contains a complete document (<svg>*/ + GF_DIMS_UNIT_S = 1, + /*!M: is-RAP: DIMS unit is a random access point*/ + GF_DIMS_UNIT_M = 1<<1, + /*!I: is-Redundant: DIMS unit is made of redundant data*/ + GF_DIMS_UNIT_I = 1<<2, + /*!D: redundant-exit: DIMS unit is the end of redundant data*/ + GF_DIMS_UNIT_D = 1<<3, + /*!P: priority: DIMS unit is high priority*/ + GF_DIMS_UNIT_P = 1<<4, + /*!C: compressed: DIMS unit is compressed*/ + GF_DIMS_UNIT_C = 1<<5 +}; + + +/*! + \cond DUMMY_DOXY_SECTION +*/ + +/*AVC NAL unit types*/ +#define GF_AVC_NALU_NON_IDR_SLICE 0x1 +#define GF_AVC_NALU_DP_A_SLICE 0x2 +#define GF_AVC_NALU_DP_B_SLICE 0x3 +#define GF_AVC_NALU_DP_C_SLICE 0x4 +#define GF_AVC_NALU_IDR_SLICE 0x5 +#define GF_AVC_NALU_SEI 0x6 +#define GF_AVC_NALU_SEQ_PARAM 0x7 +#define GF_AVC_NALU_PIC_PARAM 0x8 +#define GF_AVC_NALU_ACCESS_UNIT 0x9 +#define GF_AVC_NALU_END_OF_SEQ 0xa +#define GF_AVC_NALU_END_OF_STREAM 0xb +#define GF_AVC_NALU_FILLER_DATA 0xc + +#define GF_AVC_TYPE_P 0 +#define GF_AVC_TYPE_B 1 +#define GF_AVC_TYPE_I 2 +#define GF_AVC_TYPE_SP 3 +#define GF_AVC_TYPE_SI 4 +#define GF_AVC_TYPE2_P 5 +#define GF_AVC_TYPE2_B 6 +#define GF_AVC_TYPE2_I 7 +#define GF_AVC_TYPE2_SP 8 +#define GF_AVC_TYPE2_SI 9 + + +/*rate sizes - note that these sizes INCLUDE the rate_type header byte*/ +static const u32 GF_QCELP_RATE_TO_SIZE [] = {0, 1, 1, 4, 2, 8, 3, 17, 4, 35, 5, 8, 14, 1}; +static const u32 GF_QCELP_RATE_TO_SIZE_NB = 7; +static const u32 GF_SMV_EVRC_RATE_TO_SIZE [] = {0, 1, 1, 3, 2, 6, 3, 11, 4, 23, 5, 1}; +static const u32 GF_SMV_EVRC_RATE_TO_SIZE_NB = 6; +static const u32 GF_AMR_FRAME_SIZE[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0 }; +static const u32 GF_AMR_WB_FRAME_SIZE[16] = { 17, 23, 32, 36, 40, 46, 50, 58, 60, 5, 5, 0, 0, 0, 0, 0 }; + + +/*! + \endcond +*/ + + +/*! @} */ + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_CONSTANTS_H_*/ diff --git a/include/gpac/crypt.h b/include/gpac/crypt.h new file mode 100644 index 0000000..20cb825 --- /dev/null +++ b/include/gpac/crypt.h @@ -0,0 +1,190 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Crypto Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + The GPAC crypto lib is a simplified version of libmcrypt - not all algos are included. + Doc here is man mcrypt + Original libmcrypt license +*/ + +/* + * Copyright (C) 1998,1999,2000 Nikos Mavroyanopoulos + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _GF_CRYPT_H_ +#define _GF_CRYPT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/tools.h> + +/*max number of possible key sizes for all supported modes*/ +#define MAX_KEY_SIZES 4 + +/*crypto lib handler*/ +typedef struct _tag_crypt_stream GF_Crypt; + +/*supported modes (case insensitive): "CBC", "CFB", "CTR", "ECB", "nCFB", "nOFB", "OFB", "STREAM"*/ +/*supported algos (case insensitive): + "AES-128" == "Rijndael-128" + "AES-192" == "Rijndael-192" + "AES-256" == "Rijndael-256" + "DES", "3DES" +*/ + + +/*opens crypto context - algo and mode SHALL NOT be NULL*/ +GF_Crypt *gf_crypt_open(const char *algorithm, const char *mode); +/*close crypto context*/ +void gf_crypt_close(GF_Crypt *gfc); + +/* sets the state of the algorithm. Can be used only with block algorithms and certain modes like CBC, CFB etc. +It is usefully if you want to restart or start a different encryption quickly. +*/ +GF_Err gf_crypt_set_state(GF_Crypt *gfc, const void *iv, int size); +/*gets the state of the algorithm. Can be used only certain modes and algorithms. +The size will hold the size of the state and the state must have enough bytes to hold it. +*/ +GF_Err gf_crypt_get_state(GF_Crypt *gfc, void *iv, int *size); +/*Returns 1 if the algorithm is a block algorithm or 0 if it is a stream algorithm.*/ +Bool gf_crypt_is_block_algorithm(GF_Crypt *gfc); +/*Returns 1 if the mode is for use with block algorithms, otherwise it returns 0.*/ +Bool gf_crypt_is_block_algorithm_mode(GF_Crypt *gfc); +/*Returns 1 if the mode outputs blocks of bytes or 0 if it outputs bytes. (eg. 1 for cbc and ecb, and 0 for cfb and stream)*/ +Bool gf_crypt_is_block_mode(GF_Crypt *gfc); +/*Returns the block size of the algorithm specified by the encryption descriptor in bytes.*/ +u32 gf_crypt_get_block_size(GF_Crypt *gfc); +/*Returns the maximum supported key size of the algorithm specified by the encryption descriptor in bytes.*/ +u32 gf_crypt_get_key_size(GF_Crypt *gfc); +/*Returns the number of supported key sizes. +@keys: array of at least MAX_KEY_SIZES size - will hold the supported sizes*/ +u32 gf_crypt_get_supported_key_sizes(GF_Crypt *gfc, u32 *key_sizes); +/*Returns size (in bytes) of the IV of the algorithm specified for the context. +If it is '0' then the IV is ignored in that algorithm. +IV is used in CBC, CFB, OFB modes, and in some algorithms in STREAM mode. +*/ +u32 gf_crypt_get_iv_size(GF_Crypt *gfc); +/*Returns 1 if the mode needs an IV, 0 otherwise. +Some 'stream' algorithms may need an IV even if the mode itself does not need an IV. +*/ +Bool gf_crypt_mode_has_iv(GF_Crypt *gfc); + +/*guess what these do...*/ +const char *gf_crypt_get_algorithm_name(GF_Crypt *gfc); +u32 gf_crypt_get_algorithm_version(GF_Crypt *gfc); +const char *gf_crypt_get_mode_name(GF_Crypt *gfc); +u32 gf_crypt_get_mode_version(GF_Crypt *gfc); + + +/* +This function initializes all buffers for the specified context +@Lenofkey: key size in BYTES - maximum value of lenofkey should be the one obtained by +calling gf_crypt_get_key_size() and every value smaller than this is legal. +@IV: usually size of the algorithms block size - get it by calling gf_crypt_get_iv_size(). + IV is ignored in ECB. IV MUST exist in CFB, CBC, STREAM, nOFB and OFB modes. + It needs to be random and unique (but not secret). The same IV must be used + for encryption/decryption. +After calling this function you can use the descriptor for encryption or decryption (not both). +*/ +GF_Err gf_crypt_init(GF_Crypt *gfc, void *key, u32 lenofkey, const void *IV); +/*releases context buffers - you may call gf_crypt_init after that, or gf_crypt_close*/ +void gf_crypt_deinit(GF_Crypt *gfc); +/*changes key and IV*/ +GF_Err gf_crypt_set_key(GF_Crypt *gfc, void *key, u32 keysize, const void *iv); + +/* +main encryption function. +@Plaintext, @len: plaintext to encrypt - len should be k*algorithms_block_size if used in a mode +which operated in blocks (cbc, ecb, nofb), or whatever when used in cfb or ofb which operate in streams. +The plaintext is replaced by the ciphertext. +*/ +GF_Err gf_crypt_encrypt(GF_Crypt *gfc, void *plaintext, int len); +/*decryption function. It is almost the same with gf_crypt_generic.*/ +GF_Err gf_crypt_decrypt(GF_Crypt *gfc, void *ciphertext, int len); + +/*various queries on both modes and algo*/ +u32 gf_crypt_str_get_algorithm_version(const char *algorithm); +u32 gf_crypt_str_get_mode_version(const char *mode); +Bool gf_crypt_str_is_block_algorithm(const char *algorithm); +Bool gf_crypt_str_is_block_algorithm_mode(const char *algorithm); +Bool gf_crypt_str_is_block_mode(const char *mode); +u32 gf_crypt_str_module_get_algo_block_size(const char *algorithm); +u32 gf_crypt_str_module_get_algo_key_size(const char *algorithm); +u32 gf_crypt_str_get_algo_supported_key_sizes(const char *algorithm, int *keys); + + + +/*SHA1 from Christophe Devine*/ +typedef struct +{ + u32 total[2]; + u32 state[5]; + u8 buffer[64]; +} GF_SHA1Context; + +/* + * Core SHA-1 functions + */ +void gf_sha1_starts(GF_SHA1Context *ctx ); +void gf_sha1_update(GF_SHA1Context *ctx, u8 *input, u32 length); +void gf_sha1_finish(GF_SHA1Context *ctx, u8 digest[20] ); + +/* + * Output SHA-1(file contents), returns 0 if successful. + */ +int gf_sha1_file(char *filename, u8 digest[20]); + +/* + * Output SHA-1(buf) + */ +void gf_sha1_csum(u8 *buf, u32 buflen, u8 digest[20]); + +/* + * Output HMAC-SHA-1(key,buf) + */ +void gf_sha1_hmac(u8 *key, u32 keylen, u8 *buf, u32 buflen, u8 digest[20]); + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_CRYPT_H_*/ + diff --git a/include/gpac/download.h b/include/gpac/download.h new file mode 100644 index 0000000..89ccf6e --- /dev/null +++ b/include/gpac/download.h @@ -0,0 +1,277 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_DOWNLOAD_H_ +#define _GF_DOWNLOAD_H_ + +/*! + * \file <gpac/download.h> + * \brief Downloader functions. + */ + +/*! + * \addtogroup dld_grp downloader + * \ingroup utils_grp + * \brief File Downloader objects + * + * This section documents the file downloading tools the GPAC framework. Currently HTTP is supported, HTTPS is under testing but may not be supported + *depending on GPAC compilation options (HTTPS in GPAC needs OpenSSL installed on the system). + * + * @{ + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/tools.h> +#include <gpac/module.h> + + +/*!the download manager object. This is usually not used by GPAC modules*/ +typedef struct __gf_download_manager GF_DownloadManager; +/*!the download manager session.*/ +typedef struct __gf_download_session GF_DownloadSession; + +/*! + *\brief download manager constructor + * + *Creates a new download manager object. + *\param cfg optional configuration file. Currently the download manager needs a configuration file for cache location and + *other options. The cache directory must be indicated in the section "General", key "CacheDirectory" of the configuration + *file. If the cache directory is not found, the cache will be disabled but the downloader will still work. + *\return the download manager object +*/ +GF_DownloadManager *gf_dm_new(GF_Config *cfg); +/* + *\brief download manager destructor + * + *Deletes the download manager. All running sessions are aborted + *\param dm the download manager object + */ +void gf_dm_del(GF_DownloadManager *dm); + +/*! + *\brief callback function for authentication + * + * The gf_dm_get_usr_pass type is the type for the callback of the \ref gf_dm_set_auth_callback function used for password retrieval + *\param usr_cbk opaque user data + *\param site_url url of the site the user and password are requested for + *\param usr_name the user name for this site. The allocated space for this buffer is 50 bytes. \note this varaibale may already be formatted. + *\param password the password for this site and user. The allocated space for this buffer is 50 bytes. + *\return 0 if user didn't fill in the information which will result in an authentication failure, 1 otherwise. +*/ +typedef Bool (*gf_dm_get_usr_pass)(void *usr_cbk, const char *site_url, char *usr_name, char *password); + +/*! + *\brief password retrieval assignment + * + *Assigns the callback function used for user password retrieval. If no such function is assigned to the download manager, + *all downloads requiring authentication will fail. + *\param dm the download manager object + *\param get_pass \ref gf_dm_get_usr_pass callback function for user and password retrieval. + *\param usr_cbk opaque user data passed to callback function + */ +void gf_dm_set_auth_callback(GF_DownloadManager *dm, gf_dm_get_usr_pass get_pass, void *usr_cbk); + +/*!downloader session message types*/ +enum +{ + /*!signal that session is setup and waiting for connection request*/ + GF_NETIO_SETUP = 0, + /*!signal that session connection is done*/ + GF_NETIO_CONNECTED, + /*!request a protocol method from the user. Default value is "GET" for HTTP*/ + GF_NETIO_GET_METHOD, + /*!request a header from the user. */ + GF_NETIO_GET_HEADER, + /*!requesting content from the user, if any. Content is appended to the request*/ + GF_NETIO_GET_CONTENT, + /*!signal that request is sent and waiting for server reply*/ + GF_NETIO_WAIT_FOR_REPLY, + /*!signal a header to user. */ + GF_NETIO_PARSE_HEADER, + /*!signal request reply to user. The reply is always sent after the headers*/ + GF_NETIO_PARSE_REPLY, + /*!send data to the user*/ + GF_NETIO_DATA_EXCHANGE, + /*!all data has been transfered*/ + GF_NETIO_DATA_TRANSFERED, + /*!signal that the session has been deconnected*/ + GF_NETIO_DISCONNECTED, + /*!downloader session failed (error code set) or done/destroyed (no error code)*/ + GF_NETIO_STATE_ERROR +}; + +/*!session download flags*/ +enum +{ + /*!session is not threaded, the user must explicitely fetch the data */ + GF_NETIO_SESSION_NOT_THREADED = 1, + /*!session has no cache: data will be sent to the user if threaded mode (live streams like radios & co)*/ + GF_NETIO_SESSION_NOT_CACHED = 1<<1, +}; + + +/*!protocol I/O parameter*/ +typedef struct +{ + /*!parameter message type*/ + u32 msg_type; + /*error code if any. Valid for all message types.*/ + GF_Err error; + /*!data received or data to send. Only valid for GF_NETIO_GET_CONTENT and GF_NETIO_DATA_EXCHANGE (when no cache is setup) messages*/ + char *data; + /*!size of associated data. Only valid for GF_NETIO_GET_CONTENT and GF_NETIO_DATA_EXCHANGE messages*/ + u32 size; + /*protocol header. Only valid for GF_NETIO_GET_HEADER, GF_NETIO_PARSE_HEADER and GF_NETIO_GET_METHOD*/ + char *name; + /*protocol header value or server response. Only alid for GF_NETIO_GET_HEADER, GF_NETIO_PARSE_HEADER and GF_NETIO_PARSE_REPLY*/ + char *value; + /*response code - only valid for GF_NETIO_PARSE_REPLY*/ + u32 reply; +} GF_NETIO_Parameter; + +/*! + *\brief callback function for data reception and state signaling + * + * The gf_dm_user_io type is the type for the data callback function of a download session + *\param usr_cbk opaque user data + *\param parameter the input/output parameter structure +*/ +typedef void (*gf_dm_user_io)(void *usr_cbk, GF_NETIO_Parameter *parameter); + + + +/*! + *\brief download session constructor + * + *Creates a new download session + *\param dm the download manager object + *\param url file to retrieve (no PUT/POST yet, only downloading is supported) + *\param dl_flags combination of session download flags + *\param user_io \ref gf_dm_user_io callback function for data reception and service messages + *\param usr_cbk opaque user data passed to callback function + *\param error error for failure cases + *\return the session object or NULL if error. If no error is indicated and a NULL session is returned, this means the file is local + */ +GF_DownloadSession * gf_dm_sess_new(GF_DownloadManager *dm, char *url, u32 dl_flags, + gf_dm_user_io user_io, + void *usr_cbk, + GF_Err *error); + +/*! + *brief downloader session destructor + * + *Deletes the download session, cleaning the cache if indicated in the configuration file of the download manager (section "Downloader", key "CleanCache") + *\param sess the download session +*/ +void gf_dm_sess_del(GF_DownloadSession * sess); +/*! + *\brief aborts downloading + * + *Aborts all operations in the session, regardless of its state. The session cannot be reused once this is called. + *\param sess the download session + */ +void gf_dm_sess_abort(GF_DownloadSession * sess); +/*! + *\brief sets private data + * + *associate private data with the session. + *\param sess the download session + *\param private_data the private data + *\warning the private_data parameter is reserved for bandwidth statistics per service when used in the GPAC terminal. + */ +void gf_dm_sess_set_private(GF_DownloadSession * sess, void *private_data); + +/*! + *\brief gets private data + * + *Gets private data associated with the session. + *\param sess the download session + *\return the private data + *\warning the private_data parameter is reserved for bandwidth statistics per service when used in the GPAC terminal. + */ +void *gf_dm_sess_get_private(GF_DownloadSession * sess); +/*! + *\brief gets last session error + * + *Gets the last error that occured in the session + *\param sess the download session + *\return the last error + */ +GF_Err gf_dm_sess_last_error(GF_DownloadSession *sess); + +/*! + *\brief fetches data on session + * + *Fetches data from the server. This will also performs connections and all needed exchange with server. + *\param sess the download session + *\param buffer destination buffer + *\param buffer_size destination buffer allocated size + *\param read_size amount of data actually fetched + *\note this can only be used when the session is not threaded + */ +GF_Err gf_dm_sess_fetch_data(GF_DownloadSession * sess, char *buffer, u32 buffer_size, u32 *read_size); + +/*! + *\brief get mime type + * + *Fetches the mime type of the URL this session is fetching + *\param sess the download session + *\return the mime type of the URL, or NULL if error. You should get the error with \ref gf_dm_sess_last_error + */ +const char *gf_dm_sess_mime_type(GF_DownloadSession * sess); +/*! + *\brief get cache file name + * + *Gets the cache file name for the session. + *\param sess the download session + *\return the absolute path of the cache file, or NULL if the session is not cached*/ +const char *gf_dm_sess_get_cache_name(GF_DownloadSession * sess); +/*! + *\brief get statistics + * + *Gets download statistics for the session. All output parameters are optional and may be set to NULL. + *\param sess the download session + *\param server the remote server address + *\param path the path on the remote server + *\param total_size the total size in bytes the file fetched, 0 if unknown. + *\param bytes_done the amount of bytes received from the server + *\param bytes_per_sec the average data rate in bytes per seconds + *\param net_status the session status + */ +GF_Err gf_dm_sess_get_stats(GF_DownloadSession * sess, const char **server, const char **path, u32 *total_size, u32 *bytes_done, u32 *bytes_per_sec, u32 *net_status); + + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_DOWNLOAD_H_*/ + diff --git a/include/gpac/esi.h b/include/gpac/esi.h new file mode 100644 index 0000000..3e49933 --- /dev/null +++ b/include/gpac/esi.h @@ -0,0 +1,183 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / Elementary Stream Interface sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_ESI_H_ +#define _GF_ESI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/tools.h> + +/* ESI input control commands*/ +enum +{ + /*forces a data flush from interface to dest (caller) - used for non-threaded interfaces + corresponding parameter: unused + */ + GF_ESI_INPUT_DATA_FLUSH, + /*pulls a COMPLETE AU from the stream + corresponding parameter: pointer to a GF_ESIPacket to fill. The indut data_len in the packet is used to indicate any padding in bytes + */ + GF_ESI_INPUT_DATA_PULL, + /*releases the currently pulled AU from the stream - AU cannot be pulled after that, unless seek happens + corresponding parameter: unused + */ + GF_ESI_INPUT_DATA_RELEASE, + + /*destroys any allocated resource by the stream interface*/ + GF_ESI_INPUT_DESTROY, +}; + +/* ESI output control commands*/ +enum +{ + /*forces a data flush from interface to dest (caller) - used for non-threaded interfaces + corresponding parameter: unused + */ + GF_ESI_OUTPUT_DATA_DISPATCH +}; + +/* + data packet flags +*/ +enum +{ + GF_ESI_DATA_AU_START = 1, + GF_ESI_DATA_AU_END = 1<<1, + GF_ESI_DATA_AU_RAP = 1<<2, + GF_ESI_DATA_HAS_CTS = 1<<3, + GF_ESI_DATA_HAS_DTS = 1<<4, + GF_ESI_DATA_ENCRYPTED = 1<<5, +}; + +typedef struct __data_packet_ifce +{ + u32 flags; + char *data; + u32 data_len; + /*DTS, CTS/PTS and duration expressed in media timescale*/ + u64 dts, cts; + u32 duration; + u32 pck_sn; + /*MPEG-4 stuff*/ + u32 au_sn; + /*for packets using ISMACrypt/OMA/3GPP based crypto*/ + u32 isma_bso; +} GF_ESIPacket; + +struct __esi_video_info +{ + u32 width, height, par; + Double FPS; +}; +struct __esi_audio_info +{ + u32 sample_rate, nb_channels; +}; + +enum +{ + /*data can be pulled from this stream*/ + GF_ESI_AU_PULL_CAP = 1, + /*DTS is signaled for this stream*/ + GF_ESI_SIGNAL_DTS = 1<<1, + /*no more data to expect from this stream*/ + GF_ESI_STREAM_IS_OVER = 1<<2, +}; + +typedef struct __elementary_stream_ifce +{ + /*misc caps of the stream*/ + u32 caps; + /*matches PID for MPEG2, ES_ID for MPEG-4*/ + u32 stream_id; + /*MPEG-TS program number if any*/ + u16 program_number; + /*MPEG-4 ST/OTIs*/ + u8 stream_type; + u8 object_type_indication; + /* MPEG-4 SL Config */ + GF_SLConfig sl_config; + /*stream 4CC for non-mpeg codecs, 0 otherwise (stream is identified through StreamType/ObjectType)*/ + u32 fourcc; + /*packed 3-char language code (4CC with last byte ' ')*/ + u32 lang; + /*media timescale*/ + u32 timescale; + /*duration in ms - 0 if unknown*/ + Double duration; + /*average bit rate in bit/sec - 0 if unknown*/ + u32 bit_rate; + + struct __esi_video_info info_video; + struct __esi_audio_info info_audio; + + /*input ES control from caller*/ + GF_Err (*input_ctrl)(struct __elementary_stream_ifce *_self, u32 ctrl_type, void *param); + /*input user data of interface - usually set by interface owner*/ + void *input_udta; + + /*output ES control of destination*/ + GF_Err (*output_ctrl)(struct __elementary_stream_ifce *_self, u32 ctrl_type, void *param); + /*output user data of interface - usually set during interface setup*/ + void *output_udta; + +} GF_ESInterface; + +typedef struct __service_ifce +{ + u32 type; + + /*input service control from caller*/ + GF_Err (*input_ctrl)(struct __service_ifce *_self, u32 ctrl_type, void *param); + /*input user data of interface - usually set by interface owner*/ + void *input_udta; + + /*output service control of destination*/ + GF_Err (*output_ctrl)(struct __service_ifce *_self, u32 ctrl_type, void *param); + /*output user data of interface - usually set during interface setup*/ + void *output_udta; + + GF_ESInterface **streams; + u32 nb_streams; +} GF_ServiceInterface; + + +typedef struct __data_io +{ + u32 (*read)(struct __data_io *_self, char *buffer, u32 nb_bytes); + u32 (*write)(struct __data_io *_self, char *buffer, u32 nb_bytes); + void *udta; +} GF_DataIO; + + +#ifdef __cplusplus +} +#endif + +#endif //_GF_ESI_H_ + diff --git a/include/gpac/events.h b/include/gpac/events.h new file mode 100644 index 0000000..df62203 --- /dev/null +++ b/include/gpac/events.h @@ -0,0 +1,689 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Events management + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#ifndef _GF_EVENTS_H_ +#define _GF_EVENTS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/math.h> +#include <gpac/tools.h> + +/* + minimal event system + + DO NOT CHANGE THEIR POSITION IN THE LIST, USED TO SPEED UP FILTERING OF USER INPUT EVENTS +*/ + +/*events*/ +enum { + + /****************************************************** + + Events used for both GPAC internals and DOM Events + + *******************************************************/ + /*MouseEvents*/ + GF_EVENT_CLICK, + GF_EVENT_MOUSEUP, + GF_EVENT_MOUSEDOWN, + GF_EVENT_MOUSEOVER, + GF_EVENT_MOUSEOUT, + /*!! ALL MOUSE EVENTS SHALL BE DECLARED BEFORE MOUSEMOVE !! */ + GF_EVENT_MOUSEMOVE, + /*mouse wheel event*/ + GF_EVENT_MOUSEWHEEL, + + /*Key Events*/ + GF_EVENT_KEYUP, + GF_EVENT_KEYDOWN, /* covers KeyDown, KeyPress and AccessKey */ + GF_EVENT_LONGKEYPRESS, + /*character input*/ + GF_EVENT_TEXTINPUT, + + + /****************************************************** + + Events used for DOM Events only + + *******************************************************/ + GF_EVENT_TEXTSELECT, + + /*DOM UIEvents*/ + GF_EVENT_FOCUSIN, + GF_EVENT_FOCUSOUT, + GF_EVENT_ACTIVATE, + GF_EVENT_CHANGE, + GF_EVENT_FOCUS, + GF_EVENT_BLUR, + /*SVG (HTML) Events*/ + GF_EVENT_LOAD, + GF_EVENT_UNLOAD, + GF_EVENT_ABORT, + GF_EVENT_ERROR, + GF_EVENT_RESIZE, + GF_EVENT_SCROLL, + GF_EVENT_ZOOM, + GF_EVENT_BEGIN, /*this is a fake event, it is NEVER fired, only used in SMIL begin*/ + GF_EVENT_BEGIN_EVENT, + GF_EVENT_END, /*this is a fake event, it is NEVER fired, only used in SMIL end*/ + GF_EVENT_END_EVENT, + GF_EVENT_REPEAT, /*this is a fake event, it is NEVER fired, only used in SMIL repeat*/ + GF_EVENT_REPEAT_EVENT, + + /*DOM MutationEvents - NOT SUPPORTED YET*/ + GF_EVENT_TREE_MODIFIED, + GF_EVENT_NODE_INSERTED, + GF_EVENT_NODE_REMOVED, + GF_EVENT_NODE_INSERTED_DOC, + GF_EVENT_NODE_REMOVED_DOC, + GF_EVENT_ATTR_MODIFIED, + GF_EVENT_CHAR_DATA_MODIFIED, + GF_EVENT_NODE_NAME_CHANGED, + GF_EVENT_ATTR_NAME_CHANGED, + + GF_EVENT_DCCI_PROP_CHANGE, + + /*LASeR events*/ + GF_EVENT_ACTIVATED, + GF_EVENT_DEACTIVATED, + GF_EVENT_PAUSE, + GF_EVENT_PAUSED_EVENT, + GF_EVENT_PLAY, + GF_EVENT_REPEAT_KEY, + GF_EVENT_RESUME_EVENT, + GF_EVENT_SHORT_ACCESSKEY, + /*pseudo-event, only used in LASeR coding*/ + GF_EVENT_EXECUTION_TIME, + + /*MediaAccess events - cf http://www.w3.org/TR/MediaAccessEvents*/ + GF_EVENT_MEDIA_BEGIN_SESSION_SETUP, + GF_EVENT_MEDIA_END_SESSION_SETUP, + GF_EVENT_MEDIA_DATA_REQUEST, + GF_EVENT_MEDIA_PLAYABLE, + GF_EVENT_MEDIA_NOT_PLAYABLE, + GF_EVENT_MEDIA_DATA_PROGRESS, + GF_EVENT_MEDIA_END_OF_DATA, + GF_EVENT_MEDIA_STOP, + GF_EVENT_MEDIA_ERROR, + + GF_EVENT_BATTERY, + GF_EVENT_CPU, + GF_EVENT_UNKNOWN, + + + /****************************************************** + + Events used for GPAC internals only + + *******************************************************/ + + /*same as mousedown, generated internally by GPAC*/ + GF_EVENT_DBLCLICK, + + /*window events*/ + /*size has changed - indicate new w & h in .x end .y fields of event. + When sent from gpac to a video plugin, indicates the output size should be changed. This is only sent when the plugin + manages the output video himself + When sent from a video plugin to gpac, indicates the output size has been changed. This is only sent when the plugin + manages the output video himself + */ + GF_EVENT_SIZE, + /*signals the scene size (if indicated in scene) upon connection (sent to the user event proc only) + if scene size hasn't changed (seeking or other) this event is not sent + */ + GF_EVENT_SCENE_SIZE, + GF_EVENT_SHOWHIDE, /*window show/hide (minimized or other). This is also sent to the user to signal focus switch in fullscreen*/ + GF_EVENT_SET_CURSOR, /*set mouse cursor*/ + GF_EVENT_SET_CAPTION, /*set window caption*/ + GF_EVENT_MOVE, /*move window*/ + GF_EVENT_REFRESH, /*window needs repaint (whenever needed, eg restore, hide->show, background refresh, paint)*/ + GF_EVENT_QUIT, /*window is being closed*/ + /*video hw setup message: + - when sent from gpac to plugin, indicates that the plugin should re-setup hardware context due to a window resize: + * for 2D output, this means resizing the backbuffer if needed (depending on HW constraints) + * for 3D output, this means re-setup of OpenGL context (depending on HW constraints). Depending on windowing systems + and implementations, it could be possible to resize a window without destroying the GL context. + + - when sent from plugin to gpac, indicates that hardware resources must be resetup before next render step (this is mainly + due to discard all openGL textures and cached objects) + */ + GF_EVENT_VIDEO_SETUP, + + /*terminal events*/ + GF_EVENT_CONNECT, /*signal URL is connected*/ + GF_EVENT_DURATION, /*signal duration of presentation*/ + GF_EVENT_AUTHORIZATION, /*indicates a user and pass is queried*/ + GF_EVENT_NAVIGATE, /*indicates the user app should load or jump to the given URL.*/ + GF_EVENT_NAVIGATE_INFO, /*indicates the link or its description under the mouse pointer*/ + GF_EVENT_MESSAGE, /*message from the MPEG-4 terminal*/ + GF_EVENT_PROGRESS, /*progress message from the MPEG-4 terminal*/ + GF_EVENT_VIEWPOINTS, /*indicates viewpoint list has changed - no struct associated*/ + GF_EVENT_STREAMLIST, /*indicates stream list has changed - no struct associated - only used when no scene info is present*/ + GF_EVENT_METADATA, /*indicates a change in associated metadata*/ + GF_EVENT_MIGRATE, /*indicates a session migration request*/ + GF_EVENT_DISCONNECT, /*indicates the current url should be disconnected*/ + GF_EVENT_SYS_COLORS, /*queries the list of system colors*/ +}; + +/*GPAC/DOM3 key codes*/ +enum { + GF_KEY_UNIDENTIFIED = 0, + GF_KEY_ACCEPT = 1, /* "Accept" The Accept (Commit) key.*/ + GF_KEY_AGAIN, /* "Again" The Again key.*/ + GF_KEY_ALLCANDIDATES, /* "AllCandidates" The All Candidates key.*/ + GF_KEY_ALPHANUM, /*"Alphanumeric" The Alphanumeric key.*/ + GF_KEY_ALT, /*"Alt" The Alt (Menu) key.*/ + GF_KEY_ALTGRAPH, /*"AltGraph" The Alt-Graph key.*/ + GF_KEY_APPS, /*"Apps" The Application key.*/ + GF_KEY_ATTN, /*"Attn" The ATTN key.*/ + GF_KEY_BROWSERBACK, /*"BrowserBack" The Browser Back key.*/ + GF_KEY_BROWSERFAVORITES, /*"BrowserFavorites" The Browser Favorites key.*/ + GF_KEY_BROWSERFORWARD, /*"BrowserForward" The Browser Forward key.*/ + GF_KEY_BROWSERHOME, /*"BrowserHome" The Browser Home key.*/ + GF_KEY_BROWSERREFRESH, /*"BrowserRefresh" The Browser Refresh key.*/ + GF_KEY_BROWSERSEARCH, /*"BrowserSearch" The Browser Search key.*/ + GF_KEY_BROWSERSTOP, /*"BrowserStop" The Browser Stop key.*/ + GF_KEY_CAPSLOCK, /*"CapsLock" The Caps Lock (Capital) key.*/ + GF_KEY_CLEAR, /*"Clear" The Clear key.*/ + GF_KEY_CODEINPUT, /*"CodeInput" The Code Input key.*/ + GF_KEY_COMPOSE, /*"Compose" The Compose key.*/ + GF_KEY_CONTROL, /*"Control" The Control (Ctrl) key.*/ + GF_KEY_CRSEL, /*"Crsel" The Crsel key.*/ + GF_KEY_CONVERT, /*"Convert" The Convert key.*/ + GF_KEY_COPY, /*"Copy" The Copy key.*/ + GF_KEY_CUT, /*"Cut" The Cut key.*/ + GF_KEY_DOWN, /*"Down" The Down Arrow key.*/ + GF_KEY_END, /*"End" The End key.*/ + GF_KEY_ENTER, /*"Enter" The Enter key. + Note: This key identifier is also used for the Return (Macintosh numpad) key.*/ + GF_KEY_ERASEEOF, /*"EraseEof" The Erase EOF key.*/ + GF_KEY_EXECUTE, /*"Execute" The Execute key.*/ + GF_KEY_EXSEL, /*"Exsel" The Exsel key.*/ + GF_KEY_F1, /*"F1" The F1 key.*/ + GF_KEY_F2, /*"F2" The F2 key.*/ + GF_KEY_F3, /*"F3" The F3 key.*/ + GF_KEY_F4, /*"F4" The F4 key.*/ + GF_KEY_F5, /*"F5" The F5 key.*/ + GF_KEY_F6, /*"F6" The F6 key.*/ + GF_KEY_F7, /*"F7" The F7 key.*/ + GF_KEY_F8, /*"F8" The F8 key.*/ + GF_KEY_F9, /*"F9" The F9 key.*/ + GF_KEY_F10, /*"F10" The F10 key.*/ + GF_KEY_F11, /*"F11" The F11 key.*/ + GF_KEY_F12, /*"F12" The F12 key.*/ + GF_KEY_F13, /*"F13" The F13 key.*/ + GF_KEY_F14, /*"F14" The F14 key.*/ + GF_KEY_F15, /*"F15" The F15 key.*/ + GF_KEY_F16, /*"F16" The F16 key.*/ + GF_KEY_F17, /*"F17" The F17 key.*/ + GF_KEY_F18, /*"F18" The F18 key.*/ + GF_KEY_F19, /*"F19" The F19 key.*/ + GF_KEY_F20, /*"F20" The F20 key.*/ + GF_KEY_F21, /*"F21" The F21 key.*/ + GF_KEY_F22, /*"F22" The F22 key.*/ + GF_KEY_F23, /*"F23" The F23 key.*/ + GF_KEY_F24, /*"F24" The F24 key.*/ + GF_KEY_FINALMODE, /*"FinalMode" The Final Mode (Final) key used on some asian keyboards.*/ + GF_KEY_FIND, /*"Find" The Find key.*/ + GF_KEY_FULLWIDTH, /*"FullWidth" The Full-Width Characters key.*/ + GF_KEY_HALFWIDTH, /*"HalfWidth" The Half-Width Characters key.*/ + GF_KEY_HANGULMODE, /*"HangulMode" The Hangul (Korean characters) Mode key.*/ + GF_KEY_HANJAMODE, /*"HanjaMode" The Hanja (Korean characters) Mode key.*/ + GF_KEY_HELP, /*"Help" The Help key.*/ + GF_KEY_HIRAGANA, /*"Hiragana" The Hiragana (Japanese Kana characters) key.*/ + GF_KEY_HOME, /*"Home" The Home key.*/ + GF_KEY_INSERT, /*"Insert" The Insert (Ins) key.*/ + GF_KEY_JAPANESEHIRAGANA, /*"JapaneseHiragana" The Japanese-Hiragana key.*/ + GF_KEY_JAPANESEKATAKANA, /*"JapaneseKatakana" The Japanese-Katakana key.*/ + GF_KEY_JAPANESEROMAJI, /*"JapaneseRomaji" The Japanese-Romaji key.*/ + GF_KEY_JUNJAMODE, /*"JunjaMode" The Junja Mode key.*/ + GF_KEY_KANAMODE, /*"KanaMode" The Kana Mode (Kana Lock) key.*/ + GF_KEY_KANJIMODE, /*"KanjiMode" The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key.*/ + GF_KEY_KATAKANA, /*"Katakana" The Katakana (Japanese Kana characters) key.*/ + GF_KEY_LAUNCHAPPLICATION1, /*"LaunchApplication1" The Start Application One key.*/ + GF_KEY_LAUNCHAPPLICATION2, /*"LaunchApplication2" The Start Application Two key.*/ + GF_KEY_LAUNCHMAIL, /*"LaunchMail" The Start Mail key.*/ + GF_KEY_LEFT, /*"Left" The Left Arrow key.*/ + GF_KEY_META, /*"Meta" The Meta key.*/ + GF_KEY_MEDIANEXTTRACK, /*"MediaNextTrack" The Media Next Track key.*/ + GF_KEY_MEDIAPLAYPAUSE, /*"MediaPlayPause" The Media Play Pause key.*/ + GF_KEY_MEDIAPREVIOUSTRACK, /*"MediaPreviousTrack" The Media Previous Track key.*/ + GF_KEY_MEDIASTOP, /*"MediaStop" The Media Stok key.*/ + GF_KEY_MODECHANGE, /*"ModeChange" The Mode Change key.*/ + GF_KEY_NONCONVERT, /*"Nonconvert" The Nonconvert (Don't Convert) key.*/ + GF_KEY_NUMLOCK, /*"NumLock" The Num Lock key.*/ + GF_KEY_PAGEDOWN, /*"PageDown" The Page Down (Next) key.*/ + GF_KEY_PAGEUP, /*"PageUp" The Page Up key.*/ + GF_KEY_PASTE, /*"Paste" The Paste key.*/ + GF_KEY_PAUSE, /*"Pause" The Pause key.*/ + GF_KEY_PLAY, /*"Play" The Play key.*/ + GF_KEY_PREVIOUSCANDIDATE, /*"PreviousCandidate" The Previous Candidate function key.*/ + GF_KEY_PRINTSCREEN, /*"PrintScreen" The Print Screen (PrintScrn, SnapShot) key.*/ + GF_KEY_PROCESS, /*"Process" The Process key.*/ + GF_KEY_PROPS, /*"Props" The Props key.*/ + GF_KEY_RIGHT, /*"Right" The Right Arrow key.*/ + GF_KEY_ROMANCHARACTERS, /*"RomanCharacters" The Roman Characters function key.*/ + GF_KEY_SCROLL, /*"Scroll" The Scroll Lock key.*/ + GF_KEY_SELECT, /*"Select" The Select key.*/ + GF_KEY_SELECTMEDIA, /*"SelectMedia" The Select Media key.*/ + GF_KEY_SHIFT, /*"Shift" The Shift key.*/ + GF_KEY_STOP, /*"Stop" The Stop key.*/ + GF_KEY_UP, /*"Up" The Up Arrow key.*/ + GF_KEY_UNDO, /*"Undo" The Undo key.*/ + GF_KEY_VOLUMEDOWN, /*"VolumeDown" The Volume Down key.*/ + GF_KEY_VOLUMEMUTE, /*"VolumeMute" The Volume Mute key.*/ + GF_KEY_VOLUMEUP, /*"VolumeUp" The Volume Up key.*/ + GF_KEY_WIN, /*"Win" The Windows Logo key.*/ + GF_KEY_ZOOM, /*"Zoom" The Zoom key.*/ + GF_KEY_BACKSPACE, /*"U+0008" The Backspace (Back) key.*/ + GF_KEY_TAB, /*"U+0009" The Horizontal Tabulation (Tab) key.*/ + GF_KEY_CANCEL, /*"U+0018" The Cancel key.*/ + GF_KEY_ESCAPE, /*"U+001B" The Escape (Esc) key.*/ + GF_KEY_SPACE, /*"U+0020" The Space (Spacebar) key.*/ + GF_KEY_EXCLAMATION, /*"U+0021" The Exclamation Mark (Factorial, Bang) key (!).*/ + GF_KEY_QUOTATION, /*"U+0022" The Quotation Mark (Quote Double) key (").*/ + GF_KEY_NUMBER, /*"U+0023" The Number Sign (Pound Sign, Hash, Crosshatch, Octothorpe) key (#).*/ + GF_KEY_DOLLAR, /*"U+0024" The Dollar Sign (milreis, escudo) key ($).*/ + GF_KEY_AMPERSAND, /*"U+0026" The Ampersand key (&).*/ + GF_KEY_APOSTROPHE, /*"U+0027" The Apostrophe (Apostrophe-Quote, APL Quote) key (').*/ + GF_KEY_LEFTPARENTHESIS, /*"U+0028" The Left Parenthesis (Opening Parenthesis) key (().*/ + GF_KEY_RIGHTPARENTHESIS, /*"U+0029" The Right Parenthesis (Closing Parenthesis) key ()).*/ + GF_KEY_STAR, /*"U+002A" The Asterix (Star) key (*).*/ + GF_KEY_PLUS, /*"U+002B" The Plus Sign (Plus) key (+).*/ + GF_KEY_COMMA, /*"U+002C" The Comma (decimal separator) sign key (,).*/ + GF_KEY_HYPHEN, /*"U+002D" The Hyphen-minus (hyphen or minus sign) key (-).*/ + GF_KEY_FULLSTOP, /*"U+002E" The Full Stop (period, dot, decimal point) key (.).*/ + GF_KEY_SLASH, /*"U+002F" The Solidus (slash, virgule, shilling) key (/).*/ + GF_KEY_0, /*"U+0030" The Digit Zero key (0).*/ + GF_KEY_1, /*"U+0031" The Digit One key (1).*/ + GF_KEY_2, /*"U+0032" The Digit Two key (2).*/ + GF_KEY_3, /*"U+0033" The Digit Three key (3).*/ + GF_KEY_4, /*"U+0034" The Digit Four key (4).*/ + GF_KEY_5, /*"U+0035" The Digit Five key (5).*/ + GF_KEY_6, /*"U+0036" The Digit Six key (6).*/ + GF_KEY_7, /*"U+0037" The Digit Seven key (7).*/ + GF_KEY_8, /*"U+0038" The Digit Eight key (8).*/ + GF_KEY_9, /*"U+0039" The Digit Nine key (9).*/ + GF_KEY_COLON, /*"U+003A" The Colon key (:).*/ + GF_KEY_SEMICOLON, /*"U+003B" The Semicolon key (;).*/ + GF_KEY_LESSTHAN, /*"U+003C" The Less-Than Sign key (<).*/ + GF_KEY_EQUALS, /*"U+003D" The Equals Sign key (=).*/ + GF_KEY_GREATERTHAN, /*"U+003E" The Greater-Than Sign key (>).*/ + GF_KEY_QUESTION, /*"U+003F" The Question Mark key (?).*/ + GF_KEY_AT, /*"U+0040" The Commercial At (@) key.*/ + GF_KEY_A, /*"U+0041" The Latin Capital Letter A key (A).*/ + GF_KEY_B, /*"U+0042" The Latin Capital Letter B key (B).*/ + GF_KEY_C, /*"U+0043" The Latin Capital Letter C key (C).*/ + GF_KEY_D, /*"U+0044" The Latin Capital Letter D key (D).*/ + GF_KEY_E, /*"U+0045" The Latin Capital Letter E key (E).*/ + GF_KEY_F, /*"U+0046" The Latin Capital Letter F key (F).*/ + GF_KEY_G, /*"U+0047" The Latin Capital Letter G key (G).*/ + GF_KEY_H, /*"U+0048" The Latin Capital Letter H key (H).*/ + GF_KEY_I, /*"U+0049" The Latin Capital Letter I key (I).*/ + GF_KEY_J, /*"U+004A" The Latin Capital Letter J key (J).*/ + GF_KEY_K, /*"U+004B" The Latin Capital Letter K key (K).*/ + GF_KEY_L, /*"U+004C" The Latin Capital Letter L key (L).*/ + GF_KEY_M, /*"U+004D" The Latin Capital Letter M key (M).*/ + GF_KEY_N, /*"U+004E" The Latin Capital Letter N key (N).*/ + GF_KEY_O, /*"U+004F" The Latin Capital Letter O key (O).*/ + GF_KEY_P, /*"U+0050" The Latin Capital Letter P key (P).*/ + GF_KEY_Q, /*"U+0051" The Latin Capital Letter Q key (Q).*/ + GF_KEY_R, /*"U+0052" The Latin Capital Letter R key (R).*/ + GF_KEY_S, /*"U+0053" The Latin Capital Letter S key (S).*/ + GF_KEY_T, /*"U+0054" The Latin Capital Letter T key (T).*/ + GF_KEY_U, /*"U+0055" The Latin Capital Letter U key (U).*/ + GF_KEY_V, /*"U+0056" The Latin Capital Letter V key (V).*/ + GF_KEY_W, /*"U+0057" The Latin Capital Letter W key (W).*/ + GF_KEY_X, /*"U+0058" The Latin Capital Letter X key (X).*/ + GF_KEY_Y, /*"U+0059" The Latin Capital Letter Y key (Y).*/ + GF_KEY_Z, /*"U+005A" The Latin Capital Letter Z key (Z).*/ + GF_KEY_LEFTSQUAREBRACKET, /*"U+005B" The Left Square Bracket (Opening Square Bracket) key ([).*/ + GF_KEY_BACKSLASH, /*"U+005C" The Reverse Solidus (Backslash) key (\).*/ + GF_KEY_RIGHTSQUAREBRACKET, /*"U+005D" The Right Square Bracket (Closing Square Bracket) key (]).*/ + GF_KEY_CIRCUM, /*"U+005E" The Circumflex Accent key (^).*/ + GF_KEY_UNDERSCORE, /*"U+005F" The Low Sign (Spacing Underscore, Underscore) key (_).*/ + GF_KEY_GRAVEACCENT, /*"U+0060" The Grave Accent (Back Quote) key (`).*/ + GF_KEY_LEFTCURLYBRACKET, /*"U+007B" The Left Curly Bracket (Opening Curly Bracket, Opening Brace, Brace Left) key ({).*/ + GF_KEY_PIPE, /*"U+007C" The Vertical Line (Vertical Bar, Pipe) key (|).*/ + GF_KEY_RIGHTCURLYBRACKET, /*"U+007D" The Right Curly Bracket (Closing Curly Bracket, Closing Brace, Brace Right) key (}).*/ + GF_KEY_DEL, /*"U+007F" The Delete (Del) Key.*/ + GF_KEY_INVERTEXCLAMATION, /*"U+00A1" The Inverted Exclamation Mark key (�).*/ + GF_KEY_DEADGRAVE, /*"U+0300" The Combining Grave Accent (Greek Varia, Dead Grave) key.*/ + GF_KEY_DEADEACUTE, /*"U+0301" The Combining Acute Accent (Stress Mark, Greek Oxia, Tonos, Dead Eacute) key.*/ + GF_KEY_DEADCIRCUM, /*"U+0302" The Combining Circumflex Accent (Hat, Dead Circumflex) key.*/ + GF_KEY_DEADTILDE, /*"U+0303" The Combining Tilde (Dead Tilde) key.*/ + GF_KEY_DEADMACRON, /*"U+0304" The Combining Macron (Long, Dead Macron) key.*/ + GF_KEY_DEADBREVE, /*"U+0306" The Combining Breve (Short, Dead Breve) key.*/ + GF_KEY_DEADABOVEDOT, /*"U+0307" The Combining Dot Above (Derivative, Dead Above Dot) key.*/ + GF_KEY_DEADDIARESIS, /*"U+0308" The Combining Diaeresis (Double Dot Abode, Umlaut, Greek Dialytika, Double Derivative, Dead Diaeresis) key.*/ + GF_KEY_DEADRINGABOVE, /*"U+030A" The Combining Ring Above (Dead Above Ring) key.*/ + GF_KEY_DEADDOUBLEACUTE, /*"U+030B" The Combining Double Acute Accent (Dead Doubleacute) key.*/ + GF_KEY_DEADCARON, /*"U+030C" The Combining Caron (Hacek, V Above, Dead Caron) key.*/ + GF_KEY_DEADCEDILLA, /*"U+0327" The Combining Cedilla (Dead Cedilla) key.*/ + GF_KEY_DEADOGONEK, /*"U+0328" The Combining Ogonek (Nasal Hook, Dead Ogonek) key.*/ + GF_KEY_DEADIOTA, /*"U+0345" The Combining Greek Ypogegrammeni (Greek Non-Spacing Iota Below, Iota Subscript, Dead Iota) key.*/ + GF_KEY_EURO, /*"U+20AC" The Euro Currency Sign key (�).*/ + GF_KEY_DEADVOICESOUND, /*"U+3099" The Combining Katakana-Hiragana Voiced Sound Mark (Dead Voiced Sound) key.*/ + GF_KEY_DEADSEMIVOICESOUND, /*"U+309A" The Combining Katakana-Hiragana Semi-Voiced Sound Mark (Dead Semivoiced Sound) key. */ + + /*non-dom keys, used in LASeR*/ + GF_KEY_CELL_SOFT1, /*soft1 key of cell phones*/ + GF_KEY_CELL_SOFT2, /*soft2 key of cell phones*/ + + /*for joystick handling*/ + GF_KEY_JOYSTICK +}; + + +/*key modifiers state - set by terminal (not set by video driver)*/ +enum +{ + GF_KEY_MOD_SHIFT = (1), + GF_KEY_MOD_CTRL = (1<<2), + GF_KEY_MOD_ALT = (1<<3), + + GF_KEY_EXT_NUMPAD = (1<<4), + GF_KEY_EXT_LEFT = (1<<5), + GF_KEY_EXT_RIGHT = (1<<6) +}; + +/*mouse button modifiers*/ +enum +{ + GF_MOUSE_LEFT = 0, + GF_MOUSE_MIDDLE, + GF_MOUSE_RIGHT +}; + +/*event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_MOUSEMOVE, GF_EVENT_MOUSEWHEEL, GF_EVENT_MOUSEDOWN, GF_EVENT_MOUSEUP*/ + u8 type; + /*mouse location in output window, 2D-like: top-left (0,0), increasing y towards bottom*/ + s32 x, y; + /*wheel position (wheel current delta / wheel absolute delta) for GF_EVENT_MouseWheel*/ + Fixed wheel_pos; + /*0: left - 1: middle, 2- right*/ + u32 button; + /*key modifier*/ + u32 key_states; +} GF_EventMouse; + +/*event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_KEYDOWN and GF_EVENT_KEYUP*/ + u8 type; + /*above GPAC/DOM key code*/ + u32 key_code; + /* hadrware key value (matching ASCI) */ + u32 hw_code; + /*key modifier*/ + u32 flags; +} GF_EventKey; + +/*event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_TEXTINPUT*/ + u8 type; + /*above virtual key code*/ + u32 unicode_char; +} GF_EventChar; + +/*event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_SIZE*/ + u8 type; + /*width and height*/ + u16 width, height; +} GF_EventSize; + +/*event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_VIDEO_SETUP*/ + u8 type; + /*width and height of visual surface to allocate*/ + u16 width, height; + /*indicates whether double buffering is desired*/ + Bool back_buffer; + /*indicates whether system memory for the backbuffer is desired (no video blitting)*/ + Bool system_memory; + /*indicates whether opengl context shall be created. Values are: + 0: no opengl context shall be created + 1: opengl context shall be created for the main window and set as the current one + 2: an extra opengl context shall be created for offscreen rendering and set as the current one + if not supported, mix of 2D (raster) and 3D (openGL) will be disabled + */ + u32 opengl_mode; +} GF_EventVideoSetup; + +/*event proc return value: ignored +this event may be triggered by the compositor if owning window or if shortcut fullscreen is detected*/ +typedef struct +{ + /*GF_EVENT_SHOWHIDE*/ + u8 type; + /*0: hidden - 1: visible - 2: fullscreen*/ + u32 show_type; +} GF_EventShow; + + +/*sensor signaling*/ +enum +{ + GF_CURSOR_NORMAL = 0x00, + GF_CURSOR_ANCHOR, + GF_CURSOR_TOUCH, + /*discSensor, cylinderSensor, sphereSensor*/ + GF_CURSOR_ROTATE, + /*proximitySensor & proximitySensor2D*/ + GF_CURSOR_PROXIMITY, + /*planeSensor & planeSensor2D*/ + GF_CURSOR_PLANE, + /*collision*/ + GF_CURSOR_COLLIDE, + GF_CURSOR_HIDE, +}; + +/*event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_SET_CURSOR*/ + u8 type; + /*set if is visible*/ + u32 cursor_type; +} GF_EventCursor; + +/*event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_SET_CAPTION*/ + u8 type; + /*window style flags - NOT USED YET*/ + const char *caption; +} GF_EventCaption; + +/*event proc: never posted*/ +typedef struct +{ + /*GF_EVENT_MOVE*/ + u8 type; + s32 x, y; + /*0: absolute positionning, 1: relative move, 2: use alignment constraints*/ + Bool relative; + /*0: left/top, 1: middle, 2: right/bottom*/ + u8 align_x, align_y; +} GF_EventMove; + +/*duration may be signaled several times: it may change when setting up streams +event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_DURATION*/ + u8 type; + /*duration in seconds*/ + Double duration; + /*is seeking supported for service*/ + Bool can_seek; +} GF_EventDuration; + +/*event proc return value: 0 if URL not supported, 1 if accepted (it is the user responsability to load the url) +YOU SHALL NOT DIRECTLY OPEN THE NEW URL IN THE EVENT PROC, THIS WOULD DEADLOCK THE TERMINAL +*/ +typedef struct +{ + /*GF_EVENT_NAVIGATE and GF_EVENT_NAVIGATE_INFO*/ + u8 type; + /*new url to open / data to handle*/ + const char *to_url; + /*parameters (cf vrml spec) - UNUSED for GF_EVENT_NAVIGATE_INFO*/ + u32 param_count; + const char **parameters; +} GF_EventNavigate; + + +/*event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_MESSAGE*/ + u8 type; + /*name of service issuing the message*/ + const char *service; + /*message*/ + const char *message; + /*error if any*/ + GF_Err error; +} GF_EventMessage; + +/*event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_PROGRESS*/ + u8 type; + /*name of service issuing the progress notif*/ + const char *service; + /*progress type: 0: buffering, 1: downloading, 2: importing (BT/VRML/...)*/ + u32 progress_type; + /*amount done and total amount of operation. + For buffer events, expresses current and total desired stream buffer in scene in milliseconds + For download events, expresses current and total size of download in bytes + For import events, no units defined (depends on importers) + */ + u32 done, total; +} GF_EventProgress; + +/*event proc return value: ignored*/ +typedef struct +{ + /*GF_EVENT_CONNECT*/ + u8 type; + /*sent upon connection/deconnection completion. if any error, it is signaled through message event*/ + Bool is_connected; +} GF_EventConnect; + +/*event proc return value: 1 if info has been completed, 0 otherwise (and operation this request was for +will then fail)*/ +typedef struct +{ + /*GF_EVENT_AUTHORIZATION*/ + u8 type; + /*the URL the auth request is for*/ + const char *site_url; + /*user name (provided buffer can hold 50 bytes). It may already be formated, or an empty ("") string*/ + char *user; + /*password (provided buffer can hold 50 bytes)*/ + char *password; +} GF_EventAuthorize; + + +/*event proc return value: 1 if info has been completed, 0 otherwise */ +typedef struct +{ + /*GF_EVENT_SYS_COLORS*/ + u8 type; + /*ARGB colors, in order: + ActiveBorder, ActiveCaption, AppWorkspace, Background, ButtonFace, ButtonHighlight, ButtonShadow, + ButtonText, CaptionText, GrayText, Highlight, HighlightText, InactiveBorder, InactiveCaption, + InactiveCaptionText, InfoBackground, InfoText, Menu, MenuText, Scrollbar, ThreeDDarkShadow, + ThreeDFace, ThreeDHighlight, ThreeDLightShadow, ThreeDShadow, Window, WindowFrame, WindowText + */ + u32 sys_colors[28]; +} GF_EventSysColors; + +/*Mutation AttrChangeType Signaling*/ +enum +{ + GF_MUTATION_ATTRCHANGE_MODIFICATION = 0x01, + GF_MUTATION_ATTRCHANGE_ADDITION = 0x02, + GF_MUTATION_ATTRCHANGE_REMOVAL = 0x03, +}; + +typedef struct { + /* GF_EVENT_TREE_MODIFIED, GF_EVENT_NODE_INSERTED, GF_EVENT_NODE_REMOVED, GF_EVENT_NODE_INSERTED_DOC, GF_EVENT_NODE_REMOVED_DOC, GF_EVENT_ATTR_MODIFIED, GF_EVENT_CHAR_DATA_MODIFIED */ + u8 type; + void *relatedNode; + void *prevValue; + void *newValue; + void *attrName; + u8 attrChange; +} GF_EventMutation; + +typedef union +{ + u8 type; + GF_EventMouse mouse; + GF_EventKey key; + GF_EventChar character; + GF_EventSize size; + GF_EventShow show; + GF_EventDuration duration; + GF_EventNavigate navigate; + GF_EventMessage message; + GF_EventProgress progress; + GF_EventConnect connect; + GF_EventCaption caption; + GF_EventCursor cursor; + GF_EventAuthorize auth; + GF_EventSysColors sys_cols; + GF_EventMove move; + GF_EventVideoSetup setup; + GF_EventMutation mutation; +} GF_Event; + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_EVENTS_H_*/ + diff --git a/include/gpac/ietf.h b/include/gpac/ietf.h new file mode 100644 index 0000000..2d6d778 --- /dev/null +++ b/include/gpac/ietf.h @@ -0,0 +1,1319 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_IETF_H_ +#define _GF_IETF_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/list.h> +#include <gpac/bitstream.h> +#include <gpac/sync_layer.h> +#include <gpac/network.h> + + +/**************************************************************************** + + RTSP VERSION 1.0 LIBRARY EXPORTS + +****************************************************************************/ + +#define GF_RTSP_VERSION "RTSP/1.0" + + +/* + * RTSP NOTIF CODES + */ +enum +{ + NC_RTSP_Continue = 100, + NC_RTSP_OK = 200, + NC_RTSP_Created = 201, + NC_RTSP_Low_on_Storage_Space = 250, + + NC_RTSP_Multiple_Choice = 300, + NC_RTSP_Moved_Permanently = 301, + NC_RTSP_Moved_Temporarily = 302, + NC_RTSP_See_Other = 303, + NC_RTSP_Use_Proxy = 305, + + NC_RTSP_Bad_Request = 400, + NC_RTSP_Unauthorized = 401, + NC_RTSP_Payment_Required = 402, + NC_RTSP_Forbidden = 403, + NC_RTSP_Not_Found = 404, + NC_RTSP_Method_Not_Allowed = 405, + NC_RTSP_Not_Acceptable = 406, + NC_RTSP_Proxy_Authentication_Required = 407, + NC_RTSP_Request_Timeout = 408, + NC_RTSP_Gone = 410, + NC_RTSP_Length_Required = 411, + NC_RTSP_Precondition_Failed = 412, + NC_RTSP_Request_Entity_Too_Large = 413, + NC_RTSP_Request_URI_Too_Long = 414, + NC_RTSP_Unsupported_Media_Type = 415, + + NC_RTSP_Invalid_parameter = 451, + NC_RTSP_Illegal_Conference_Identifier = 452, + NC_RTSP_Not_Enough_Bandwidth = 453, + NC_RTSP_Session_Not_Found = 454, + NC_RTSP_Method_Not_Valid_In_This_State = 455, + NC_RTSP_Header_Field_Not_Valid = 456, + NC_RTSP_Invalid_Range = 457, + NC_RTSP_Parameter_Is_ReadOnly = 458, + NC_RTSP_Aggregate_Operation_Not_Allowed = 459, + NC_RTSP_Only_Aggregate_Operation_Allowed = 460, + NC_RTSP_Unsupported_Transport = 461, + NC_RTSP_Destination_Unreachable = 462, + + NC_RTSP_Internal_Server_Error = 500, + NC_RTSP_Not_Implemented = 501, + NC_RTSP_Bad_Gateway = 502, + NC_RTSP_Service_Unavailable = 503, + NC_RTSP_Gateway_Timeout = 504, + NC_RTSP_RTSP_Version_Not_Supported = 505, + + NC_RTSP_Option_not_support = 551, +}; + +const char *gf_rtsp_nc_to_string(u32 ErrCode); + +/* + Common structures between commands and responses +*/ + +/* + RTSP Range information - RTSP Session level only (though this is almost the same + format as an SDP range, this is not used in the SDP lib as "a=range" is not part of SDP + but part of RTSP +*/ +typedef struct { + /* start and end range. If end is -1, the range is open (from start to unknown) */ + Double start, end; + /* use SMPTE range (Start and End specify the number of frames) (currently not supported) */ + u32 UseSMPTE; + /* framerate for SMPTE range */ + Double FPS; +} GF_RTSPRange; + +/* +parses a Range line and returns range header structure. can be used for RTSP extension of SDP +NB: Only support for npt for now +*/ +GF_RTSPRange *gf_rtsp_range_parse(char *range_buf); + +GF_RTSPRange *gf_rtsp_range_new(); +void gf_rtsp_range_del(GF_RTSPRange *range); + +/* + Transport structure + contains all network info for RTSP sessions (ports, uni/multi-cast, ...) +*/ + +/* + Transport Profiles as defined in RFC 2326 +*/ +#define GF_RTSP_PROFILE_RTP_AVP "RTP/AVP" +#define GF_RTSP_PROFILE_RTP_AVP_TCP "RTP/AVP/TCP" +#define GF_RTSP_PROFILE_UDP "udp" + + +typedef struct +{ + /* set to 1 if unicast */ + Bool IsUnicast; + /* for multicast */ + char *destination; + /* for redirections internal to servers */ + char *source; + /*IsRecord is usually 0 (PLAY) . If set, Append specify that the stream should + be concatenated to existing resources */ + Bool IsRecord, Append; + /* in case transport is on TCP/RTSP, If only 1 ID is specified, it is stored in rtpID (this + is not RTP interleaving) */ + Bool IsInterleaved, rtpID, rtcpID; + /* Multicast specific */ + u32 MulticastLayers; + u8 TTL; + /*RTP specific*/ + + /*port for multicast*/ + /*server port in unicast - RTP implies low is even , and last is low+1*/ + u16 port_first, port_last; + /*client port in unicast - RTP implies low is even , and last is low+1*/ + u16 client_port_first, client_port_last; + u32 SSRC; + + /*Transport protocol. In this version we only support RTP/AVP, the following flag tells + us if this is RTP/AVP/TCP or RTP/AVP (default)*/ + char *Profile; +} GF_RTSPTransport; + + +GF_RTSPTransport *gf_rtsp_transport_clone(GF_RTSPTransport *original); +void gf_rtsp_transport_del(GF_RTSPTransport *transp); + + +/* + RTSP Command + the RTSP Response is sent by a client / received by a server + text Allocation is done by the lib when parsing a command, and + is automatically freed when calling reset / delete. Therefore you must + set/allocate the fields yourself when writing a command (client) + +*/ + +/*ALL RTSP METHODS - all other methods will be ignored*/ +#define GF_RTSP_DESCRIBE "DESCRIBE" +#define GF_RTSP_SETUP "SETUP" +#define GF_RTSP_PLAY "PLAY" +#define GF_RTSP_PAUSE "PAUSE" +#define GF_RTSP_RECORD "RECORD" +#define GF_RTSP_TEARDOWN "TEARDOWN" +#define GF_RTSP_GET_PARAMETER "GET_PARAMETER" +#define GF_RTSP_SET_PARAMETER "SET_PARAMETER" +#define GF_RTSP_OPTIONS "OPTIONS" +#define GF_RTSP_ANNOUNCE "ANNOUNCE" +#define GF_RTSP_REDIRECTE "REDIRECT" + + +typedef struct +{ + char *Accept; + char *Accept_Encoding; + char *Accept_Language; + char *Authorization; + u32 Bandwidth; + u32 Blocksize; + char *Cache_Control; + char *Conference; + char *Connection; + u32 Content_Length; + u32 CSeq; + char *From; + char *Proxy_Authorization; + char *Proxy_Require; + GF_RTSPRange *Range; + char *Referer; + Double Scale; + char *Session; + Double Speed; + /*nota : RTSP allows several configurations for a single channel (multicast and + unicast , ...). Usually only 1*/ + GF_List *Transports; + char *User_Agent; + + /*type of the command, one of the described above*/ + char *method; + + /*Header extensions*/ + GF_List *Xtensions; + + /*body of the command, size is Content-Length (auto computed when sent). It is not + terminated by a NULL char*/ + char *body; + + /* + Specify ControlString if your request targets + a specific media stream in the service. If null, the service name only will be used + for control (for ex, both A and V streams in a single file) + If the request is GF_RTSP_OPTIONS, you must provide a control string containing the options + you want to query + */ + char *ControlString; + + /*user data: this is never touched by the lib, its intend is to help stacking + RTSP commands in your app*/ + void *user_data; + + + /* + Server side Extensions + */ + + /*full URL of the command. Not used at client side, as the URL is ALWAYS relative + to the server / service of the RTSP session + On the server side however redirections are up to the server, so we cannot decide for it */ + char *service_name; + /*RTSP status code of the command as parsed. One of the above RTSP StatusCode*/ + u32 StatusCode; +} GF_RTSPCommand; + + +GF_RTSPCommand *gf_rtsp_command_new(); +void gf_rtsp_command_del(GF_RTSPCommand *com); +void gf_rtsp_command_reset(GF_RTSPCommand *com); + + + +/* + RTSP Response + the RTSP Response is received by a client / sent by a server + text Allocation is done by the lib when parsing a response, and + is automatically freed when calling reset / delete. Therefore you must + allocate the fields yourself when writing a response (server) + +*/ + +/* + RTP-Info for RTP channels. There may be several RTP-Infos in one response + based on the server implementation (DSS/QTSS begaves this way) +*/ +typedef struct +{ + /*control string of the channel*/ + char *url; + /*seq num for asociated rtp_time*/ + u32 seq; + /*rtp TimeStamp corresponding to the Range start specified in the PLAY request*/ + u32 rtp_time; + /*ssrc of sender if known, 0 otherwise*/ + u32 ssrc; +} GF_RTPInfo; + + + +/* + RTSP Response +*/ +typedef struct +{ + /* response code*/ + u32 ResponseCode; + /* comment from the server */ + char *ResponseInfo; + + /* Header Fields */ + char *Accept; + char *Accept_Encoding; + char *Accept_Language; + char *Allow; + char *Authorization; + u32 Bandwidth; + u32 Blocksize; + char *Cache_Control; + char *Conference; + char *Connection; + char *Content_Base; + char *Content_Encoding; + char *Content_Language; + u32 Content_Length; + char *Content_Location; + char *Content_Type; + u32 CSeq; + char *Date; + char *Expires; + char *From; + char *Host; + char *If_Match; + char *If_Modified_Since; + char *Last_Modified; + char *Location; + char *Proxy_Authenticate; + char *Proxy_Require; + char *Public; + GF_RTSPRange *Range; + char *Referer; + char *Require; + char *Retry_After; + GF_List *RTP_Infos; + Double Scale; + char *Server; + char *Session; + u32 SessionTimeOut; + Double Speed; + char *Timestamp; + /*nota : RTSP allows several configurations for a single channel (multicast and + unicast , ...). Usually only 1*/ + GF_List *Transports; + char *Unsupported; + char *User_Agent; + char *Vary; + char *Via; + char *WWW_Authenticate; + + /*Header extensions*/ + GF_List *Xtensions; + + /*body of the response, size is Content-Length (auto computed when sent). It is not + terminated by a NULL char when response is parsed but must be null-terminated when + response is being sent*/ + char *body; +} GF_RTSPResponse; + + +GF_RTSPResponse *gf_rtsp_response_new(); +void gf_rtsp_response_del(GF_RTSPResponse *rsp); +void gf_rtsp_response_reset(GF_RTSPResponse *rsp); + + + +typedef struct _tag_rtsp_session GF_RTSPSession; + +GF_RTSPSession *gf_rtsp_session_new(char *sURL, u16 DefaultPort); +void gf_rtsp_session_del(GF_RTSPSession *sess); + +GF_Err gf_rtsp_set_buffer_size(GF_RTSPSession *sess, u32 BufferSize); + +/*force the IP address the client is using*/ +void gf_rtsp_set_mobile_ip(GF_RTSPSession *sess, char *MobileIP); + + +/*Reset state machine, invalidate SessionID +NOTE: RFC2326 requires that the session is reseted when all RTP streams +are closed. As this lib doesn't maintain the number of valid streams +you MUST call reset when all your streams are shutdown (either requested through +TEARDOWN or signaled through RTCP BYE packets for RTP, or any other signaling means +for other protocols) +reset connection will destroy the socket - this is isefull in case of timeouts, because +some servers do not restart with the right CSeq...*/ +void gf_rtsp_session_reset(GF_RTSPSession *sess, Bool ResetConnection); + +u32 gf_rtsp_is_my_session(GF_RTSPSession *sess, char *url); +const char *gf_rtsp_get_last_session_id(GF_RTSPSession *sess); +char *gf_rtsp_get_server_name(GF_RTSPSession *sess); +char *gf_rtsp_get_service_name(GF_RTSPSession *sess); +u16 gf_rtsp_get_session_port(GF_RTSPSession *sess); + +/*Fetch an RTSP response from the server the GF_RTSPResponse will be reseted before fetch*/ +GF_Err gf_rtsp_get_response(GF_RTSPSession *sess, GF_RTSPResponse *rsp); + + +/*RTSP State Machine. The only non blocking mode is GF_RTSP_STATE_WAIT_FOR_CONTROL*/ +enum +{ + /*Initialized (connection might be off, but all structures are in place) + This is the default state between # requests (aka, DESCRIBE and SETUP + or SETUP and PLAY ...)*/ + GF_RTSP_STATE_INIT = 0, + /*Waiting*/ + GF_RTSP_STATE_WAITING, + /*PLAY, PAUSE, RECORD. Aggregation is allowed for the same type, you can send several command + in a row. However the session will return GF_SERVICE_ERROR if you do not have + a valid SessionID in the command + You cannot issue a SETUP / DESCRIBE while in this state*/ + GF_RTSP_STATE_WAIT_FOR_CONTROL, + + /*FATAL ERROR: session is invalidated by server. Call reset and restart from SETUP if needed*/ + GF_RTSP_STATE_INVALIDATED +}; + +u32 gf_rtsp_get_session_state(GF_RTSPSession *sess); +/*aggregate command state-machine: the PLAY/PAUSE can be aggregated +(sent before the reply is received). This function gets the last command sent*/ +char *gf_rtsp_get_last_request(GF_RTSPSession *sess); +/*foce a reset in case of pbs*/ +void gf_rtsp_reset_aggregation(GF_RTSPSession *sess); + +/* + Send an RTSP request to the server. +*/ +GF_Err gf_rtsp_send_command(GF_RTSPSession *sess, GF_RTSPCommand *com); + + +GF_Err gf_rtsp_set_interleave_callback(GF_RTSPSession *sess, + GF_Err (*SignalData)(GF_RTSPSession *sess, void *cbk_ptr, char *buffer, u32 bufferSize, Bool IsRTCP) + ); + + +GF_Err gf_rtsp_session_read(GF_RTSPSession *sess); + +GF_Err gf_rtsp_register_interleave(GF_RTSPSession *sess, void *the_ch, u8 LowInterID, u8 HighInterID); +u32 gf_rtsp_unregister_interleave(GF_RTSPSession *sess, u8 LowInterID); + + + +/* + Server side session constructor + create a new RTSP session from an existing socket in listen state. If no pending connection + is detected, return NULL +*/ +GF_RTSPSession *gf_rtsp_session_new_server(GF_Socket *rtsp_listener); + +/*fetch an RTSP request. The GF_RTSPCommand will be reseted before fetch*/ +GF_Err gf_rtsp_get_command(GF_RTSPSession *sess, GF_RTSPCommand *com); + +/*unpack the URL, check the service name / server. Typically used when a client sends a + DESCRIBE || SETUP url RTSP/1.0. Server / service name check must be performed by your app as redirection +or services available are unknown here.*/ +GF_Err gf_rtsp_load_service_name(GF_RTSPSession *sess, char *URL); + +/*geenrates a session ID fpor the given session*/ +char *gf_rtsp_generate_session_id(GF_RTSPSession *sess); + +/*send the RTSP response*/ +GF_Err gf_rtsp_send_response(GF_RTSPSession *sess, GF_RTSPResponse *rsp); + +/*gets the IP address of the local host running the session +buffer shall be GF_MAX_IP_NAME_LEN long*/ +GF_Err gf_rtsp_get_session_ip(GF_RTSPSession *sess, char *buffer); + +/*returns the next available ID for interleaving. It is recommended that you use 2 +consecutive IDs for RTP/RTCP interleaving*/ +u8 gf_rtsp_get_next_interleave_id(GF_RTSPSession *sess); + +/*gets the IP address of the connected peer - buffer shall be GF_MAX_IP_NAME_LEN long*/ +GF_Err gf_rtsp_get_remote_address(GF_RTSPSession *sess, char *buffer); + + +/* + RTP LIB EXPORTS +*/ + + +typedef struct tagRTP_HEADER { + /*version, must be 2*/ + u8 Version; + /*padding bits in the payload*/ + u8 Padding; + /*header extension is defined*/ + u8 Extension; + /*number of CSRC (<=15)*/ + u8 CSRCCount; + /*Marker Bit*/ + u8 Marker; + /*payload type on 7 bits*/ + u8 PayloadType; + /*packet seq number*/ + u16 SequenceNumber; + /*packet time stamp*/ + u32 TimeStamp; + /*sync source identifier*/ + u32 SSRC; + /*in our basic client, CSRC should always be NULL*/ + u32 CSRC[16]; +} GF_RTPHeader; + + + + +/* + structure containing the rtpmap information +*/ +typedef struct +{ + /*dynamic payload type of this map*/ + u32 PayloadType; + /*registered payload name of this map*/ + char *payload_name; + /*RTP clock rate (TS resolution) of this map*/ + u32 ClockRate; + /*optional parameters for audio, specifying number of channels. Unused for other media types.*/ + u32 AudioChannels; +} GF_RTPMap; + + + +typedef struct __tag_rtp_channel GF_RTPChannel; + +GF_RTPChannel *gf_rtp_new(); +void gf_rtp_del(GF_RTPChannel *ch); + +/*you configure a server channel through the transport structure, with the same info as a +client channel, the client_port_* info designing the REMOTE client and port_* designing +your server channel*/ +GF_Err gf_rtp_setup_transport(GF_RTPChannel *ch, GF_RTSPTransport *trans_info, char *remote_address); + +/*auto-setup of rtp/rtcp transport ports - only effective in unicast, non interleaved cases. +for multicast port setup MUST be done through the above gf_rtp_setup_transport function +this will take care of port reuse*/ +GF_Err gf_rtp_set_ports(GF_RTPChannel *ch, u16 first_port); + +/*init of payload information. only ONE payload per sync source is supported in this +version of the library (a sender cannot switch payload types on a single media)*/ +GF_Err gf_rtp_setup_payload(GF_RTPChannel *ch, GF_RTPMap *map); + +/*enables sending of NAT keep-alive packets for NAT traversal + @nat_timeout: specifies the inactivity period in ms after which NAT keepalive packets are sent. + If 0, disables NAT keep-alive packets +*/ +void gf_rtp_enable_nat_keepalive(GF_RTPChannel *ch, u32 nat_timeout); + + +/*initialize the RTP channel. + +UDPBufferSize: UDP stack buffer size if configurable by OS/ISP - ignored otherwise +NOTE: on WinCE devices, this is not configurable on an app bases but for the whole OS +you must update the device registry with: + [HKEY_LOCAL_MACHINE\Comm\Afd] + DgramBuffer=dword:N + + where N is the number of UDP datagrams a socket should be able to buffer. For multimedia +app you should set N as large as possible. The device MUST be reseted for the param to take effect + +ReorederingSize: max number of packets to queue for reordering. 0 means no reordering +MaxReorderDelay: max time to wait in ms before releasing first packet in reoderer when only one packet is present. +If 0 and reordering size is specified, defaults to 200 ms (usually enough). +IsSource: if true, the channel is a sender (media data, sender report, Receiver report processing) +if source, you must specify the Path MTU size. The RTP lib won't send any packet bigger than this size +your application shall perform payload size splitting if needed +local_interface_ip: local interface address to use for multicast. If NULL, default address is used +*/ +GF_Err gf_rtp_initialize(GF_RTPChannel *ch, u32 UDPBufferSize, Bool IsSource, u32 PathMTU, u32 ReorederingSize, u32 MaxReorderDelay, char *local_interface_ip); + +/*init the RTP info after a PLAY or PAUSE, rtp_time is the rtp TimeStamp of the RTP packet +with seq_num sequence number. This info is needed to compute the CurrentTime of the RTP channel +ssrc may not be known if sender hasn't indicated it (use 0 then)*/ +GF_Err gf_rtp_set_info_rtp(GF_RTPChannel *ch, u32 seq_num, u32 rtp_time, u32 ssrc); + +/*retrieve current RTP time in sec. If rtp_time was unknown (not on demand media) the time is absolute. +Otherwise this is the time in ms elapsed since the last PLAY range start value +Not supported yet if played without RTSP (aka RTCP time not supported)*/ +Double gf_rtp_get_current_time(GF_RTPChannel *ch); + + +void gf_rtp_reset_buffers(GF_RTPChannel *ch); + +/*read any data on UDP only (not valid for TCP). Performs re-ordering if configured for it +returns amount of data read (raw UDP packet size)*/ +u32 gf_rtp_read_rtp(GF_RTPChannel *ch, char *buffer, u32 buffer_size); +u32 gf_rtp_read_rtcp(GF_RTPChannel *ch, char *buffer, u32 buffer_size); + +/*decodes an RTP packet and gets the begining of the RTP payload*/ +GF_Err gf_rtp_decode_rtp(GF_RTPChannel *ch, char *pck, u32 pck_size, GF_RTPHeader *rtp_hdr, u32 *PayloadStart); + +/*decodes an RTCP packet and update timing info, send RR too*/ +GF_Err gf_rtp_decode_rtcp(GF_RTPChannel *ch, char *pck, u32 pck_size); + +/*computes and send Receiver report. If the channel is a TCP channel, you must specify +the callback function. NOTE: many RTP implementation do NOT process RTCP info received on TCP... +the lib will decide whether the report shall be sent or not, therefore you should call +this function at regular times*/ +GF_Err gf_rtp_send_rtcp_report(GF_RTPChannel *ch, + GF_Err (*RTP_TCPCallback)(void *cbk, char *pck, u32 pck_size), + void *rtsp_cbk); + +/*send a BYE info (leaving the session)*/ +GF_Err gf_rtp_send_bye(GF_RTPChannel *ch, + GF_Err (*RTP_TCPCallback)(void *cbk, char *pck, u32 pck_size), + void *rtsp_cbk); + + +/*send RTP packet*/ +GF_Err gf_rtp_send_packet(GF_RTPChannel *ch, GF_RTPHeader *rtp_hdr, char *extra_header, u32 extra_header_size, char *pck, u32 pck_size); + +enum +{ + GF_RTCP_INFO_NAME = 0, + GF_RTCP_INFO_EMAIL, + GF_RTCP_INFO_PHONE, + GF_RTCP_INFO_LOCATION, + GF_RTCP_INFO_TOOL, + GF_RTCP_INFO_NOTE, + GF_RTCP_INFO_PRIV +}; + +/*sets RTCP info sent in RTCP reports. info_string shall NOT exceed 255 chars*/ +GF_Err gf_rtp_set_info_rtcp(GF_RTPChannel *ch, u32 InfoCode, char *info_string); + +u32 gf_rtp_is_unicast(GF_RTPChannel *ch); +u32 gf_rtp_is_interleaved(GF_RTPChannel *ch); +u32 gf_rtp_get_clockrate(GF_RTPChannel *ch); +u32 gf_rtp_is_active(GF_RTPChannel *ch); +u8 gf_rtp_get_low_interleave_id(GF_RTPChannel *ch); +u8 gf_rtp_get_hight_interleave_id(GF_RTPChannel *ch); +GF_RTSPTransport *gf_rtp_get_transport(GF_RTPChannel *ch); +u32 gf_rtp_get_local_ssrc(GF_RTPChannel *ch); + +Float gf_rtp_get_loss(GF_RTPChannel *ch); +u32 gf_rtp_get_tcp_bytes_sent(GF_RTPChannel *ch); +void gf_rtp_get_ports(GF_RTPChannel *ch, u16 *rtp_port, u16 *rtcp_port); + + + + + +/**************************************************************************** + + SDP LIBRARY EXPORTS + + Note: SDP is mainly a text protocol with + well defined containers. The following structures are used to write / read + SDP informations, and the library also provides consistency checking + + When reading SDP, all text items/structures are allocated by the lib, and you + must call gf_sdp_info_reset(GF_SDPInfo *sdp) or gf_sdp_info_del(GF_SDPInfo *sdp) to release the memory + + When writing the SDP from a GF_SDPInfo, the output buffer is allocated by the library, + and you must release it yourself + + Some quick constructors are available for GF_SDPConnection and GF_SDPMedia in order to set up + some specific parameters to their default value + + An extra function gf_sdp_info_check(GF_SDPInfo *sdp) is provided for compliency check + with RFC2327: all requested fields are checked as well as conflicting information forbidden + in RFC 2327 +****************************************************************************/ + +/* + All attributes x-ZZZZ are considered as extensions attributes. If no "x-" is found + the attributes in the RTSP response is SKIPPED. The "x-" radical is removed in the structure + when parsing commands / responses +*/ +typedef struct +{ + char *Name; + char *Value; +} GF_X_Attribute; + + +/* + Structure for bandwidth info +*/ +typedef struct +{ + /*"CT", "AS" are defined. Private extensions must be "X-*" ( * "are recommended to be short")*/ + char *name; + /*in kBitsPerSec*/ + u32 value; +} GF_SDPBandwidth; + +/* + Structure for Time info +*/ +/*we do not support more than ... time offsets / zone adjustment +if more are needed, RFC recommends to use several entries rather than a big*/ +#define GF_SDP_MAX_TIMEOFFSET 10 + +typedef struct +{ + /*NPT time in sec*/ + u32 StartTime; + /*if 0, session is unbound. NPT time in sec*/ + u32 StopTime; + /*if 0 session is not repeated. Expressed in sec. + Session is signaled repeated every repeatInterval*/ + u32 RepeatInterval; + /*active duration of the session in sec*/ + u32 ActiveDuration; + + /*time offsets to use with repeat. Specify a non-regular repeat time from the Start time*/ + u32 OffsetFromStart[GF_SDP_MAX_TIMEOFFSET]; + /*Number of offsets*/ + u32 NbRepeatOffsets; + + /*EX of repeat: + a session happens 3 times a week, on mon 1PM, thu 3PM and fri 10AM + 1- StartTime should be NPT for the session on the very first monday, StopTime + the end of this session + 2- the repeatInterval should be 1 week, ActiveDuration the length of the session + 3- 3 offsets: 0 (for monday) (3*24+2)*3600 for thu and (4*24-3) for fri + */ + + + /*timezone adjustments, to cope with #timezones, daylight saving countries and co ... + Ex: adjTime = [2882844526 2898848070] adjOffset=[-1h 0] + [0]: at 2882844526 the time base by which the session's repeat times are calculated + is shifted back by 1 hour + [1]: at time 2898848070 the session's original time base is restored + */ + + /*Adjustment time at which the corresponding time offset is to be applied to the + session time line (time used to compute the "repeat session"). + All Expressed in NPT*/ + u32 AdjustmentTime[GF_SDP_MAX_TIMEOFFSET]; + /* Offset with the session time line, ALWAYS ABSOLUTE OFFSET TO the specified StartTime*/ + s32 AdjustmentOffset[GF_SDP_MAX_TIMEOFFSET]; + /*Number of offsets.*/ + u32 NbZoneOffsets; +} GF_SDPTiming; + + +typedef struct +{ + /*only "IN" currently defined*/ + char *net_type; + /*"IP4","IP6"*/ + char *add_type; + /*hex IPv6 address or doted IPv4 address*/ + char *host; + /*TTL - MUST BE PRESENT if IP is multicast - -1 otherwise*/ + s32 TTL; + /*multiple address counts - ONLY in media descriptions if needed. This + is used for content scaling, when # quality of the same media are multicasted on + # IP addresses*/ + u32 add_count; +} GF_SDPConnection; + +/* + FMTP: description of dynamic payload types. This is opaque at the SDP level. + Each attributes is assumed to be formatted as <param_name=param_val; ...> + If not the case the attribute will have an empty value string and only the + parameter name. +*/ +typedef struct +{ + /*payload type of the format described*/ + u32 PayloadType; + /*list of GF_X_Attribute elements. The Value field may be NULL*/ + GF_List *Attributes; +} GF_SDP_FMTP; + +typedef struct +{ + /*m= + 0: application - 1:video - 2: audio - 3: text - 4:data - 5: control*/ + u32 Type; + /*Port Number - For transports based on UDP, the value should be in the range 1024 + to 65535 inclusive. For RTP compliance it should be an even number*/ + u32 PortNumber; + /*number of ports described. If >= 2, the next media(s) in the SDP will be configured + to use the next tuple (for RTP). If 0 or 1, ignored + Note: this is used for scalable media: PortNumber indicates the port of the base + media and NumPorts the ports||total number of the upper layers*/ + u32 NumPorts; + /*currently ony "RTP/AVP" and "udp" defined*/ + char *Profile; + + /*list of GF_SDPConnection's. A media can have several connection in case of scalable content*/ + GF_List *Connections; + + /*RTPMaps contains a list SDPRTPMaps*/ + GF_List *RTPMaps; + + /*FMTP contains a list of FMTP structures*/ + GF_List *FMTP; + + /*for RTP this is PayloadType, but can be opaque (string) depending on the app. + Formated as XX WW QQ FF + When reading the SDP, the payloads defined in RTPMap are removed from this list + When writing the SDP for RTP, you should only specify static payload types here, + as dynamic ones are stored in RTPMaps and automatically written*/ + char *fmt_list; + + /*all attributes not defined in RFC 2327 for the media*/ + GF_List *Attributes; + + /*Other SDP attributes for media desc*/ + + /*k= + method is 'clear' (key follows), 'base64' (key in base64), 'uri' (key is the URI) + or 'prompt' (key not included)*/ + char *k_method, *k_key; + + GF_List *Bandwidths; + + /*0 if not present*/ + u32 PacketTime; + /*0: none - 1: recv, 2: send, 3 both*/ + u32 SendReceive; + char *orientation, *sdplang, *lang; + /*for video only, 0.0 if not present*/ + Double FrameRate; + /*between 0 and 10, -1 if not present*/ + s32 Quality; +} GF_SDPMedia; + +typedef struct +{ + /*v=*/ + u32 Version; + /*o=*/ + char *o_username, *o_session_id, *o_version, *o_address; + /*"IN" for Net, "IP4" or "IP6" for address are currently valid*/ + char *o_net_type, *o_add_type; + + /*s=*/ + char *s_session_name; + /*i=*/ + char *i_description; + /*u=*/ + char *u_uri; + /*e=*/ + char *e_email; + /*p=*/ + char *p_phone; + /*c= either 1 or 0 GF_SDPConnection */ + GF_SDPConnection *c_connection; + /*b=*/ + GF_List *b_bandwidth; + /*All time info (t, r, z)*/ + GF_List *Timing; + /*k= + method is 'clear' (key follows), 'base64' (key in base64), 'uri' (key is the URI) + or 'prompt' (key not included)*/ + char *k_method, *k_key; + /*all possible attributes (a=), session level*/ + char *a_cat, *a_keywds, *a_tool; + /*0: none, 1: recv, 2: send, 3 both*/ + u32 a_SendReceive; + /*should be `broadcast', `meeting', `moderated', `test' or `H332'*/ + char *a_type; + char *a_charset; + char *a_sdplang, *a_lang; + + /*all attributes not defined in RFC 2327 for the presentation*/ + GF_List *Attributes; + + /*list of media in the SDP*/ + GF_List *media_desc; +} GF_SDPInfo; + + +/* + Memory Consideration: the destructors free all non-NULL string. You should therefore + be carefull while (de-)assigning the strings. The function gf_sdp_info_parse() performs a complete + reset of the GF_SDPInfo +*/ +/*constructor*/ +GF_SDPInfo *gf_sdp_info_new(); +/*destructor*/ +void gf_sdp_info_del(GF_SDPInfo *sdp); +/*reset all structures (destroys substructure too)*/ +void gf_sdp_info_reset(GF_SDPInfo *sdp); +/*Parses a memory SDP buffer*/ +GF_Err gf_sdp_info_parse(GF_SDPInfo *sdp, char *sdp_text, u32 text_size); +/*check the consistency of the GF_SDPInfo*/ +GF_Err gf_sdp_info_check(GF_SDPInfo *sdp); +/*write the SDP to a new buffer and returns it. Automatically checks the SDP before calling*/ +GF_Err gf_sdp_info_write(GF_SDPInfo *sdp, char **out_str_buf); + + +/* + Const/dest for GF_SDPMedia +*/ +GF_SDPMedia *gf_sdp_media_new(); +void gf_sdp_media_del(GF_SDPMedia *media); + +/* + Const/dest for GF_SDPConnection +*/ +GF_SDPConnection *gf_sdp_conn_new(); +void gf_sdp_conn_del(GF_SDPConnection *conn); + +/* + Const/dest for SDP FMTP +*/ +GF_SDP_FMTP *gf_sdp_fmtp_new(); +void gf_sdp_fmtp_del(GF_SDP_FMTP *fmtp); + + + +/* + RTP packetizer +*/ + + +/*RTP<->SL mapping*/ +typedef struct +{ + /*1 - required options*/ + + /*mode, or "" if no mode ("generic" should be used instead)*/ + char mode[30]; + + /*config of the stream if carried in SDP*/ + char *config; + u32 configSize; + /* Stream Type*/ + u8 StreamType; + /* stream profile and level indication - for AVC/H264, 0xPPCCLL, with PP:profile, CC:compatibility, LL:level*/ + u32 PL_ID; + + + /*2 - optional options*/ + + /*size of AUs if constant*/ + u32 ConstantSize; + /*duration of AUs if constant, in RTP timescale*/ + u32 ConstantDuration; + + /* Object Type Indication */ + u8 ObjectTypeIndication; + /*audio max displacement when interleaving (eg, de-interleaving window buffer max length) in RTP timescale*/ + u32 maxDisplacement; + /*de-interleaveBufferSize if not recomputable from maxDisplacement*/ + u32 deinterleaveBufferSize; + + /*The number of bits on which the AU-size field is encoded in the AU-header*/ + u32 SizeLength; + /*The number of bits on which the AU-Index is encoded in the first AU-header*/ + u32 IndexLength; + /*The number of bits on which the AU-Index-delta field is encoded in any non-first AU-header*/ + u32 IndexDeltaLength; + + /*The number of bits on which the DTS-delta field is encoded in the AU-header*/ + u32 DTSDeltaLength; + /*The number of bits on which the CTS-delta field is encoded in the AU-header*/ + u32 CTSDeltaLength; + /*random access point flag present*/ + Bool RandomAccessIndication; + + /*The number of bits on which the Stream-state field is encoded in the AU-header (systems only)*/ + u32 StreamStateIndication; + /*The number of bits that is used to encode the auxiliary-data-size field + (no normative usage of this section)*/ + u32 AuxiliaryDataSizeLength; + + /*ISMACryp stuff*/ + u8 IV_length, IV_delta_length; + u8 KI_length; + + /*internal stuff*/ + /*len of first AU header in an RTP payload*/ + u32 auh_first_min_len; + u32 auh_min_len; +} GP_RTPSLMap; + + +/*packetizer config flags - some flags are dynamically re-assigned when detecting multiSL / B-Frames / ...*/ +enum +{ + /*forces MPEG-4 generic transport if MPEG-4 systems mapping is available*/ + GP_RTP_PCK_FORCE_MPEG4 = (1), + /*Enables AUs concatenation in an RTP packet (if payload supports it) - this forces GP_RTP_PCK_SIGNAL_SIZE for MPEG-4*/ + GP_RTP_PCK_USE_MULTI = (1<<1), + /*if set, audio interleaving is used if payload supports it (forces GP_RTP_PCK_USE_MULTI flag) + THIS IS CURRENTLY NOT IMPLEMENTED*/ + GP_RTP_PCK_USE_INTERLEAVING = (1<<2), + /*uses static RTP payloadID if any defined*/ + GP_RTP_PCK_USE_STATIC_ID = (1<<3), + + /*MPEG-4 generic transport option*/ + /*if flag set, RAP flag is signaled in RTP payload*/ + GP_RTP_PCK_SIGNAL_RAP = (1<<4), + /*if flag set, AU indexes are signaled in RTP payload*/ + GP_RTP_PCK_SIGNAL_AU_IDX = (1<<5), + /*if flag set, AU size is signaled in RTP payload*/ + GP_RTP_PCK_SIGNAL_SIZE = (1<<6), + /*if flag set, CTS is signaled in RTP payload - DTS is automatically set if needed*/ + GP_RTP_PCK_SIGNAL_TS = (1<<7), + + /*setup payload for carouseling of systems streams*/ + GP_RTP_PCK_AUTO_CAROUSEL = (1<<8), + + /*use LATM payload for AAC-LC*/ + GP_RTP_PCK_USE_LATM_AAC = (1<<9), + + /*ISMACryp options*/ + /*signals that input data is selectively encrypted (eg not all input frames are encrypted) + - this is usually automatically set by hinter*/ + GP_RTP_PCK_SELECTIVE_ENCRYPTION = (1<<10), + /*signals that each sample will have its own key indicator - ignored in non-multi modes + if not set and key indicator changes, a new RTP packet will be forced*/ + GP_RTP_PCK_KEY_IDX_PER_AU = (1<<11), +}; + + + +/* + Generic packetization tools - used by track hinters and future live tools +*/ + +/*currently supported payload types*/ +enum +{ + /*not defined*/ + GF_RTP_PAYT_UNKNOWN, + /*use generic MPEG-4 transport - RFC 3016 and RFC 3640*/ + GF_RTP_PAYT_MPEG4, + /*use generic MPEG-1/2 video transport - RFC 2250*/ + GF_RTP_PAYT_MPEG12_VIDEO, + /*use generic MPEG-1/2 audio transport - RFC 2250*/ + GF_RTP_PAYT_MPEG12_AUDIO, + /*use H263 transport - RFC 2429*/ + GF_RTP_PAYT_H263, + /*use AMR transport - RFC 3267*/ + GF_RTP_PAYT_AMR, + /*use AMR-WB transport - RFC 3267*/ + GF_RTP_PAYT_AMR_WB, + /*use QCELP transport - RFC 2658*/ + GF_RTP_PAYT_QCELP, + /*use EVRC/SMV transport - RFC 3558*/ + GF_RTP_PAYT_EVRC_SMV, + /*use 3GPP Text transport - no RFC yet, only draft*/ + GF_RTP_PAYT_3GPP_TEXT, + /*use H264 transport - no RFC yet, only draft*/ + GF_RTP_PAYT_H264_AVC, + /*use LATM for AAC-LC*/ + GF_RTP_PAYT_LATM, + /*use 3GPP DIMS format*/ + GF_RTP_PAYT_3GPP_DIMS, + /*use AC3 audio format*/ + GF_RTP_PAYT_AC3, +}; + + + +/* + RTP packetizer +*/ + + + +/* + RTP -> SL packetization tool + You should ONLY modify the GF_SLHeader while packetizing, all the rest is private + to the tool. + Also note that AU start/end is automatically updated, therefore you should only + set CTS-DTS-OCR-sequenceNumber (which is automatically incremented when spliting a payload) + -padding-idle infos + SL flags are computed on the fly, but you may wish to modify them in case of + packet drop/... at the encoder side + +*/ +struct __tag_rtp_packetizer +{ + /*input packet sl header cfg. modify only if needed*/ + GF_SLHeader sl_header; + + /* + + PRIVATE _ DO NOT TOUCH + */ + + /*RTP payload type (RFC type, NOT the RTP hdr payT)*/ + u32 rtp_payt; + /*packetization flags*/ + u32 flags; + /*Path MTU size without 12-bytes RTP header*/ + u32 Path_MTU; + /*max packet duration in RTP TS*/ + u32 max_ptime; + + /*payload type of RTP packets - only one payload type can be used in GPAC*/ + u8 PayloadType; + + /*RTP header of current packet*/ + GF_RTPHeader rtp_header; + + /*RTP packet handling callbacks*/ + void (*OnNewPacket)(void *cbk_obj, GF_RTPHeader *header); + void (*OnPacketDone)(void *cbk_obj, GF_RTPHeader *header); + void (*OnDataReference)(void *cbk_obj, u32 payload_size, u32 offset_from_orig); + void (*OnData)(void *cbk_obj, char *data, u32 data_size, Bool is_header); + void *cbk_obj; + + /********************************* + MPEG-4 Generic hinting + *********************************/ + + /*SL to RTP map*/ + GP_RTPSLMap slMap; + /*SL conf and state*/ + GF_SLConfig sl_config; + + /*set to 1 if firstSL in RTP packet*/ + Bool first_sl_in_rtp; + Bool has_AU_header; + /*current info writers*/ + GF_BitStream *pck_hdr, *payload; + + /*AU SN of last au*/ + u32 last_au_sn; + + /*info for the current packet*/ + u32 auh_size, bytesInPacket; + + /********************************* + ISMACryp info + *********************************/ + Bool force_flush, is_encrypted; + u64 IV, first_AU_IV; + char *key_indicator; + + /********************************* + AVC-H264 info + *********************************/ + /*AVC non-IDR flag: set if all NAL in current packet are non-IDR (disposable)*/ + Bool avc_non_idr; + + /********************************* + AC3 info + *********************************/ + /*ac3 ft flags*/ + u8 ac3_ft; + +}; + +/*generic rtp builder (packetizer)*/ +typedef struct __tag_rtp_packetizer GP_RTPPacketizer; + +/*creates a new builder + @hintType: hint media type, one of the above + @flags: hint flags (cf above) + @slc: user-given SL config to use. If none specified, default RFC config is used + @cbk_obj: callback object passed back in functions + @OnNewPacket: callback function starting new RTP packet + @header: rtp header for new packet - note that RTP header flags are not used until PacketDone is called + @OnPacketDone: callback function closing current RTP packet + @header: final rtp header for packet + @OnDataReference: optional, to call each time data from input buffer is added to current RTP packet. + If not set, data must be added through OnData + @payload_size: size of reference data + @offset_from_orig: start offset in input buffer + @OnData: to call each time data is added to current RTP packet (either extra data from payload or + data from input when not using referencing) + @is_head: signal the data added MUST be inserted at the begining of the payload. Otherwise data + is concatenated as received +*/ +GP_RTPPacketizer *gf_rtp_builder_new(u32 hintType, + GF_SLConfig *slc, + u32 flags, + void *cbk_obj, + void (*OnNewPacket)(void *cbk, GF_RTPHeader *header), + void (*OnPacketDone)(void *cbk, GF_RTPHeader *header), + void (*OnDataReference)(void *cbk, u32 payload_size, u32 offset_from_orig), + void (*OnData)(void *cbk, char *data, u32 data_size, Bool is_head) + ); + +/*destroy builder*/ +void gf_rtp_builder_del(GP_RTPPacketizer *builder); + +/* + init the builder + @MaxPayloadSize: maximum payload size of RTP packets (eg MTU minus IP/UDP/RTP headers) + @max_ptime: maximum packet duration IN RTP TIMESCALE + @StreamType: MPEG-4 system stream type - MUST always be provided for payloads format specifying + audio or video streams + @OTI : MPEG-4 system objectTypeIndication - may be 0 if stream is not mpeg4 systems + + *** all other params are for MultiSL draft *** + + @avgSize: average size of an AU. This is not always known (real-time encoding). +In this case you should specify a rough compute indicating how many packets could be +stored per RTP packet. for ex AAC stereo at 44100 k / 64kbps , one AU ~= 380 bytes +so 3 AUs for 1500 MTU is ok - BE CAREFULL: MultiSL adds some SL info on top of the 12 +byte RTP header so you should specify a smaller size +The packetizer will ALWAYS make sure there's no pb storing the packets so specifying +more will result in a slight overhead in the SL mapping but the gain to singleSL +will still be worth it. + -Nota: at init, the packetizer can decide to switch to SingleSL if the average size +specified is too close to the PathMTU + + @maxSize: max size of an AU. If unknown (real-time) set to 0 + @avgTS: average CTS progression (1000/FPS for video) + @maxDTS: maximum DTS offset in case of bidirectional coding. + @IV_length: size (in bytes) of IV when ISMACrypted + @KI_length: size (in bytes) of key indicator when ISMACrypted + @pref_mode: MPEG-4 generic only, specifies the payload mode - can be NULL (mode generic) +*/ + +void gf_rtp_builder_init(GP_RTPPacketizer *builder, u8 PayloadType, u32 MaxPayloadSize, u32 max_ptime, + u32 StreamType, u32 OTI, u32 PL_ID, + u32 avgSize, u32 maxSize, + u32 avgTS, u32 maxDTS, + u32 IV_length, u32 KI_length, + char *pref_mode); + +/*set frame crypto info*/ +void gp_rtp_builder_set_cryp_info(GP_RTPPacketizer *builder, u64 IV, char *key_indicator, Bool is_encrypted); +/*packetize input buffer +@data, @data_size: input buffer +@IsAUEnd: set to one if this buffer is the last of the AU +@FullAUSize: complete access unit size if known, 0 otherwise +@duration: sample duration in rtp timescale (only needed for 3GPP text streams) +@descIndex: sample description index (only needed for 3GPP text streams) +*/ +GF_Err gf_rtp_builder_process(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration, u8 descIndex); + +/*format the "fmtp: " attribute for the MPEG-4 generic packetizer. sdpline shall be at least 2000 char*/ +GF_Err gf_rtp_builder_format_sdp(GP_RTPPacketizer *builder, char *payload_name, char *sdpLine, char *dsi, u32 dsi_size); +/*formats SDP payload name and media name - both MUST be at least 20 bytes*/ +Bool gf_rtp_builder_get_payload_name(GP_RTPPacketizer *builder, char *szPayloadName, char *szMediaName); + + + + + +/*rtp payload flags*/ +enum +{ + /*AU end was detected (eg next packet is AU start)*/ + GF_RTP_NEW_AU = (1), + /*AMR config*/ + GF_RTP_AMR_ALIGN = (1<<1), + /*for RFC3016, signals bitstream inspection for RAP discovery*/ + GF_RTP_M4V_CHECK_RAP = (1<<2), + /*flag set when unreliable usage of the M bit is detected*/ + GF_RTP_UNRELIABLE_M = (1<<3), + + /*AWFULL hack at rtp level to cope with ffmpeg h264 crashes when jumping in stream without IDR*/ + GF_RTP_AVC_WAIT_RAP = (1<<4), + /*ISMACryp stuff*/ + GF_RTP_HAS_ISMACRYP = (1<<5), + GF_RTP_ISMA_SEL_ENC = (1<<6), + GF_RTP_ISMA_HAS_KEY_IDX = (1<<7) +}; + +/* + SL -> RTP packetization tool + +*/ +struct __tag_rtp_depacketizer +{ + /*depacketize routine*/ + void (*depacketize)(struct __tag_rtp_depacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size); + + /*output packet sl header cfg*/ + GF_SLHeader sl_hdr; + + /*RTP payload type (RFC type, NOT the RTP hdr payT)*/ + u32 payt; + /*depacketization flags*/ + u32 flags; + + /*callback routine*/ + void (*on_sl_packet)(void *udta, char *payload, u32 size, GF_SLHeader *hdr, GF_Err e); + void *udta; + + /*SL <-> RTP map*/ + GP_RTPSLMap sl_map; + u32 clock_rate; + + /*inter-packet reconstruction bitstream (for 3GP text and H264)*/ + GF_BitStream *inter_bs; + + /*H264/AVC config*/ + u32 h264_pck_mode; + + /*3GP text reassembler state*/ + u8 nb_txt_frag, cur_txt_frag, sidx, txt_len, nb_mod_frag; + + /*ISMACryp*/ + u32 isma_scheme; + char *key; +}; + +/*generic rtp builder (packetizer)*/ +typedef struct __tag_rtp_depacketizer GF_RTPDepacketizer; + +GF_RTPDepacketizer *gf_rtp_depacketizer_new(GF_SDPMedia *media, void (*sl_packet_cbk)(void *udta, char *payload, u32 size, GF_SLHeader *hdr, GF_Err e), void *udta); +void gf_rtp_depacketizer_del(GF_RTPDepacketizer *rtp); +void gf_rtp_depacketizer_reset(GF_RTPDepacketizer *rtp, Bool full_reset); +void gf_rtp_depacketizer_process(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size); + +void gf_rtp_depacketizer_get_slconfig(GF_RTPDepacketizer *rtp, GF_SLConfig *sl); + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_IETF_H_*/ + diff --git a/include/gpac/internal/avilib.h b/include/gpac/internal/avilib.h new file mode 100644 index 0000000..08ac5be --- /dev/null +++ b/include/gpac/internal/avilib.h @@ -0,0 +1,434 @@ +/* + * avilib.h + * + * Copyright (C) Thomas Östreich - June 2001 + * multiple audio track support Copyright (C) 2002 Thomas Östreich + * + * Original code: + * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de> + * + * This file is part of transcode, a linux video stream processing tool + * + * transcode is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * transcode is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_AVILIB_H_ +#define _GF_AVILIB_H_ + +#include <gpac/tools.h> + +#define AVI_MAX_TRACKS 8 + +typedef struct +{ + u64 key; + u64 pos; + u64 len; +} video_index_entry; + +typedef struct +{ + u64 pos; + u64 len; + u64 tot; +} audio_index_entry; + + +// Index types + + +#define AVI_INDEX_OF_INDEXES 0x00 // when each entry in aIndex + // array points to an index chunk +#define AVI_INDEX_OF_CHUNKS 0x01 // when each entry in aIndex + // array points to a chunk in the file +#define AVI_INDEX_IS_DATA 0x80 // when each entry is aIndex is + // really the data +// bIndexSubtype codes for INDEX_OF_CHUNKS +// +#define AVI_INDEX_2FIELD 0x01 // when fields within frames + // are also indexed + + + +typedef struct _avisuperindex_entry { + u64 qwOffset; // absolute file offset + u32 dwSize; // size of index chunk at this offset + u32 dwDuration; // time span in stream ticks +} avisuperindex_entry; + +typedef struct _avistdindex_entry { + u32 dwOffset; // qwBaseOffset + this is absolute file offset + u32 dwSize; // bit 31 is set if this is NOT a keyframe +} avistdindex_entry; + +// Standard index +typedef struct _avistdindex_chunk { + char fcc[4]; // ix## + u32 dwSize; // size of this chunk + u16 wLongsPerEntry; // must be sizeof(aIndex[0])/sizeof(DWORD) + u8 bIndexSubType; // must be 0 + u8 bIndexType; // must be AVI_INDEX_OF_CHUNKS + u32 nEntriesInUse; // + char dwChunkId[4]; // '##dc' or '##db' or '##wb' etc.. + u64 qwBaseOffset; // all dwOffsets in aIndex array are relative to this + u32 dwReserved3; // must be 0 + avistdindex_entry *aIndex; +} avistdindex_chunk; + + +// Base Index Form 'indx' +typedef struct _avisuperindex_chunk { + char fcc[4]; + u32 dwSize; // size of this chunk + u16 wLongsPerEntry; // size of each entry in aIndex array (must be 8 for us) + u8 bIndexSubType; // future use. must be 0 + u8 bIndexType; // one of AVI_INDEX_* codes + u32 nEntriesInUse; // index of first unused member in aIndex array + char dwChunkId[4]; // fcc of what is indexed + u32 dwReserved[3]; // meaning differs for each index type/subtype. + // 0 if unused + avisuperindex_entry *aIndex; // where are the ix## chunks + avistdindex_chunk **stdindex; // the ix## chunks itself (array) +} avisuperindex_chunk; + + + +typedef struct track_s +{ + + long a_fmt; /* Audio format, see #defines below */ + long a_chans; /* Audio channels, 0 for no audio */ + long a_rate; /* Rate in Hz */ + long a_bits; /* bits per audio sample */ + long mp3rate; /* mp3 bitrate kbs*/ + long a_vbr; /* 0 == no Variable BitRate */ + long padrate; /* byte rate used for zero padding */ + + long audio_strn; /* Audio stream number */ + u64 audio_bytes; /* Total number of bytes of audio data */ + long audio_chunks; /* Chunks of audio data in the file */ + + char audio_tag[4]; /* Tag of audio data */ + long audio_posc; /* Audio position: chunk */ + long audio_posb; /* Audio position: byte within chunk */ + + u64 a_codech_off; /* absolut offset of audio codec information */ + u64 a_codecf_off; /* absolut offset of audio codec information */ + + audio_index_entry *audio_index; + avisuperindex_chunk *audio_superindex; + +} track_t; + +typedef struct +{ + u32 bi_size; + u32 bi_width; + u32 bi_height; + u16 bi_planes; + u16 bi_bit_count; + u32 bi_compression; + u32 bi_size_image; + u32 bi_x_pels_per_meter; + u32 bi_y_pels_per_meter; + u32 bi_clr_used; + u32 bi_clr_important; +} alBITMAPINFOHEADER; + +typedef struct +{ + u16 w_format_tag; + u16 n_channels; + u32 n_samples_per_sec; + u32 n_avg_bytes_per_sec; + u16 n_block_align; + u16 w_bits_per_sample; + u16 cb_size; +} alWAVEFORMATEX; + +typedef struct +{ + u32 fcc_type; + u32 fcc_handler; + u32 dw_flags; + u32 dw_caps; + u16 w_priority; + u16 w_language; + u32 dw_scale; + u32 dw_rate; + u32 dw_start; + u32 dw_length; + u32 dw_initial_frames; + u32 dw_suggested_buffer_size; + u32 dw_quality; + u32 dw_sample_size; + u16 dw_left; + u16 dw_top; + u16 dw_right; + u16 dw_bottom; +} alAVISTREAMHEADER; + +typedef struct +{ + + FILE *fdes; /* File descriptor of AVI file */ + long mode; /* 0 for reading, 1 for writing */ + + long width; /* Width of a video frame */ + long height; /* Height of a video frame */ + double fps; /* Frames per second */ + char compressor[8]; /* Type of compressor, 4 bytes + padding for 0 byte */ + char compressor2[8]; /* Type of compressor, 4 bytes + padding for 0 byte */ + u32 video_strn; /* Video stream number */ + long video_frames; /* Number of video frames */ + char video_tag[4]; /* Tag of video data */ + long video_pos; /* Number of next frame to be read + (if index present) */ + alAVISTREAMHEADER video_stream_header; + + u32 max_len; /* maximum video chunk present */ + + track_t track[AVI_MAX_TRACKS]; // up to AVI_MAX_TRACKS audio tracks supported + + s64 pos; /* position in file */ + long n_idx; /* number of index entries actually filled */ + long max_idx; /* number of index entries actually allocated */ + + s64 v_codech_off; /* absolut offset of video codec (strh) info */ + s64 v_codecf_off; /* absolut offset of video codec (strf) info */ + + u8 (*idx)[16]; /* index entries (AVI idx1 tag) */ + + video_index_entry *video_index; + avisuperindex_chunk *video_superindex; /* index of indices */ + int is_opendml; /* set to 1 if this is an odml file with multiple index chunks */ + + s64 last_pos; /* Position of last frame written */ + u32 last_len; /* Length of last frame written */ + int must_use_index; /* Flag if frames are duplicated */ + s64 movi_start; + int total_frames; /* total number of frames if dmlh is present */ + + u32 anum; // total number of audio tracks + u32 aptr; // current audio working track + char *index_file; // read the avi index from this file + + alBITMAPINFOHEADER *bitmap_info_header; + alWAVEFORMATEX *wave_format_ex[AVI_MAX_TRACKS]; + alAVISTREAMHEADER stream_headers[AVI_MAX_TRACKS]; + + void* extradata; + unsigned long extradata_size; +} avi_t; + +#define AVI_MODE_WRITE 0 +#define AVI_MODE_READ 1 + +/* The error codes delivered by avi_open_input_file */ + +#define AVI_ERR_SIZELIM 1 /* The write of the data would exceed + the maximum size of the AVI file. + This is more a warning than an error + since the file may be closed safely */ + +#define AVI_ERR_OPEN 2 /* Error opening the AVI file - wrong path + name or file nor readable/writable */ + +#define AVI_ERR_READ 3 /* Error reading from AVI File */ + +#define AVI_ERR_WRITE 4 /* Error writing to AVI File, + disk full ??? */ + +#define AVI_ERR_WRITE_INDEX 5 /* Could not write index to AVI file + during close, file may still be + usable */ + +#define AVI_ERR_CLOSE 6 /* Could not write header to AVI file + or not truncate the file during close, + file is most probably corrupted */ + +#define AVI_ERR_NOT_PERM 7 /* Operation not permitted: + trying to read from a file open + for writing or vice versa */ + +#define AVI_ERR_NO_MEM 8 /* malloc failed */ + +#define AVI_ERR_NO_AVI 9 /* Not an AVI file */ + +#define AVI_ERR_NO_HDRL 10 /* AVI file has no has no header list, + corrupted ??? */ + +#define AVI_ERR_NO_MOVI 11 /* AVI file has no has no MOVI list, + corrupted ??? */ + +#define AVI_ERR_NO_VIDS 12 /* AVI file contains no video data */ + +#define AVI_ERR_NO_IDX 13 /* The file has been opened with + getIndex==0, but an operation has been + performed that needs an index */ + +/* Possible Audio formats */ + +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_UNKNOWN (0x0000) +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM (0x0001) +#endif +#define WAVE_FORMAT_ADPCM (0x0002) +#define WAVE_FORMAT_IBM_CVSD (0x0005) +#define WAVE_FORMAT_ALAW (0x0006) +#define WAVE_FORMAT_MULAW (0x0007) +#define WAVE_FORMAT_OKI_ADPCM (0x0010) +#define WAVE_FORMAT_DVI_ADPCM (0x0011) +#define WAVE_FORMAT_DIGISTD (0x0015) +#define WAVE_FORMAT_DIGIFIX (0x0016) +#define WAVE_FORMAT_YAMAHA_ADPCM (0x0020) +#define WAVE_FORMAT_DSP_TRUESPEECH (0x0022) +#define WAVE_FORMAT_GSM610 (0x0031) +#define IBM_FORMAT_MULAW (0x0101) +#define IBM_FORMAT_ALAW (0x0102) +#define IBM_FORMAT_ADPCM (0x0103) +#endif + +avi_t* AVI_open_output_file(char * filename); +void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor); +void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate); +int AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe); +int AVI_dup_frame(avi_t *AVI); +int AVI_write_audio(avi_t *AVI, char *data, long bytes); +int AVI_append_audio(avi_t *AVI, char *data, long bytes); +u64 AVI_bytes_remain(avi_t *AVI); +int AVI_close(avi_t *AVI); +u64 AVI_bytes_written(avi_t *AVI); + +avi_t *AVI_open_input_file(char *filename, int getIndex); +avi_t *AVI_open_input_indexfile(char *filename, int getIndex, char *indexfile); +avi_t *AVI_open_fd(FILE *fd, int getIndex); +avi_t *AVI_open_indexfd(FILE *fd, int getIndex, char *indexfile); +int avi_parse_input_file(avi_t *AVI, int getIndex); +int avi_parse_index_from_file(avi_t *AVI, char *filename); +long AVI_audio_mp3rate(avi_t *AVI); +long AVI_audio_padrate(avi_t *AVI); +long AVI_video_frames(avi_t *AVI); +int AVI_video_width(avi_t *AVI); +int AVI_video_height(avi_t *AVI); +double AVI_frame_rate(avi_t *AVI); +char* AVI_video_compressor(avi_t *AVI); + +int AVI_audio_channels(avi_t *AVI); +int AVI_audio_bits(avi_t *AVI); +int AVI_audio_format(avi_t *AVI); +long AVI_audio_rate(avi_t *AVI); +u64 AVI_audio_bytes(avi_t *AVI); +long AVI_audio_chunks(avi_t *AVI); +int AVI_can_read_audio(avi_t *AVI); + +long AVI_max_video_chunk(avi_t *AVI); + +long AVI_frame_size(avi_t *AVI, long frame); +long AVI_audio_size(avi_t *AVI, long frame); +int AVI_seek_start(avi_t *AVI); +int AVI_set_video_position(avi_t *AVI, long frame); +u64 AVI_get_video_position(avi_t *AVI, long frame); +long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe); + +int AVI_set_audio_position(avi_t *AVI, long byte); +int AVI_set_audio_bitrate(avi_t *AVI, long bitrate); + +long AVI_get_audio_position_index(avi_t *AVI); +int AVI_set_audio_position_index(avi_t *AVI, long indexpos); + +long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes, int *continuous); + +int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf, + char *audbuf, long max_audbuf, + long *len); + +int AVI_scan(char *name); +int AVI_dump(char *name, int mode); + +char *AVI_codec2str(short cc); +int AVI_file_check(char *import_file); + +void AVI_info(avi_t *avifile); +u64 AVI_max_size(void); +int avi_update_header(avi_t *AVI); + +int AVI_set_audio_track(avi_t *AVI, u32 track); +int AVI_get_audio_track(avi_t *AVI); +int AVI_audio_tracks(avi_t *AVI); + +void AVI_set_audio_vbr(avi_t *AVI, long is_vbr); +long AVI_get_audio_vbr(avi_t *AVI); + +void AVI_set_comment_fd(avi_t *AVI, int fd); +int AVI_get_comment_fd(avi_t *AVI); + +struct riff_struct +{ + u8 id[4]; /* RIFF */ + u32 len; + u8 wave_id[4]; /* WAVE */ +}; + + +struct chunk_struct +{ + u8 id[4]; + u32 len; +}; + +struct common_struct +{ + u16 wFormatTag; + u16 wChannels; + u32 dwSamplesPerSec; + u32 dwAvgBytesPerSec; + u16 wBlockAlign; + u16 wBitsPerSample; /* Only for PCM */ +}; + +struct wave_header +{ + struct riff_struct riff; + struct chunk_struct format; + struct common_struct common; + struct chunk_struct data; +}; + +// Simple WAV IO +int AVI_read_wave_header( int fd, struct wave_header * wave ); +int AVI_write_wave_header( int fd, const struct wave_header * wave ); +size_t AVI_read_wave_pcm_data( int fd, void * buffer, size_t buflen ); +size_t AVI_write_wave_pcm_data( int fd, const void * buffer, size_t buflen ); + + +struct AVIStreamHeader { + long fccType; + long fccHandler; + long dwFlags; + long dwPriority; + long dwInitialFrames; + long dwScale; + long dwRate; + long dwStart; + long dwLength; + long dwSuggestedBufferSize; + long dwQuality; + long dwSampleSize; +}; + +#endif /*_GF_AVILIB_H_*/ diff --git a/include/gpac/internal/bifs_dev.h b/include/gpac/internal/bifs_dev.h new file mode 100644 index 0000000..6af7710 --- /dev/null +++ b/include/gpac/internal/bifs_dev.h @@ -0,0 +1,223 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_BIFS_DEV_H_ +#define _GF_BIFS_DEV_H_ + + +#include <gpac/nodes_mpeg4.h> +#include <gpac/bitstream.h> +#include <gpac/bifs.h> +#include <gpac/thread.h> +#include <gpac/internal/scenegraph_dev.h> + +typedef struct { + /*node this mask is for*/ + GF_Node *node; + /*in case node is not defined yet*/ + u32 node_id; + /*the rest is not needed at the current time, we only support simple sugnaling for FDP, BDP and IFS2D + which are using pre-defs masks*/ +} BIFSElementaryMask; + +typedef struct +{ + /*v1 or v2*/ + u8 version; + /*BIFS config - common fields*/ + u16 NodeIDBits; + u16 RouteIDBits; + Bool PixelMetrics; + /*set to 0, 0 if no size is specified*/ + u16 Width, Height; + + /*BIFS-Anim - not supported */ + /*if 1 the BIFS_Anim codec is reset at each intra frame*/ + Bool BAnimRAP; + /*list of elementary masks for BIFS anim*/ + GF_List *elementaryMasks; + + /*BIFS v2 add-on*/ + Bool Use3DMeshCoding; + Bool UsePredictiveMFField; + u16 ProtoIDBits; +} BIFSConfig; + + + +/*per_stream config support*/ +typedef struct +{ + BIFSConfig config; + u16 ESID; +} BIFSStreamInfo; + +/*per_stream config support*/ +typedef struct +{ + GF_Node *node; + SFCommandBuffer *cb; +} CommandBufferItem; + + +struct __tag_bifs_dec +{ + GF_Err LastError; + /*all attached streams*/ + GF_List *streamInfo; + /*active stream*/ + BIFSStreamInfo *info; + + Bool UseName; + + GF_SceneGraph *scenegraph; + /*modified during conditional execution / proto parsing*/ + GF_SceneGraph *current_graph; + + /*Quantization*/ + /*QP stack*/ + GF_List *QPs; + /*active QP*/ + M_QuantizationParameter *ActiveQP; + + /*QP 14 stuff: we need to store the last numb of fields in the last received Coord //field (!!!)*/ + + /*number of iten in the Coord field*/ + u32 NumCoord; + Bool coord_stored, storing_coord; + + /*only set at SceneReplace during proto parsing, NULL otherwise*/ + GF_Proto *pCurrentProto; + + /*when set the decoder works with commands rather than modifying the scene graph directly*/ + Bool dec_memory_mode; + Bool force_keep_qp; + /*only set in mem mode. Conditionals/InputSensors are stacked while decoding, then decoded once the AU is decoded + to make sure all nodes potentially used by the conditional command buffer are created*/ + GF_List *command_buffers; + + Bool ignore_size; + Bool is_com_dec; + Double cts_offset; +}; + + +/*decodes an GF_Node*/ +GF_Node *gf_bifs_dec_node(GF_BifsDecoder * codec, GF_BitStream *bs, u32 NDT_Tag); +/*decodes an SFField (to get a ptr to the field, use gf_node_get_field ) +the FieldIndex is used for Quantzation*/ +GF_Err gf_bifs_dec_sf_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); +/*decodes a Field (either SF or MF). The field MUST BE EMPTY*/ +GF_Err gf_bifs_dec_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); +/*decodes a route*/ +GF_Err gf_bifs_dec_route(GF_BifsDecoder * codec, GF_BitStream *bs, Bool is_insert); +/*get name*/ +void gf_bifs_dec_name(GF_BitStream *bs, char *name); + +BIFSStreamInfo *gf_bifs_dec_get_stream(GF_BifsDecoder * codec, u16 ESID); +/*decodes a BIFS command frame*/ +GF_Err gf_bifs_dec_command(GF_BifsDecoder * codec, GF_BitStream *bs); +/*decodes proto list - if proto_list is not NULL, protos parsed are not registered with the parent graph +and added to the list*/ +GF_Err gf_bifs_dec_proto_list(GF_BifsDecoder * codec, GF_BitStream *bs, GF_List *proto_list); +/*decodes field(s) of a node - exported for MultipleReplace*/ +GF_Err gf_bifs_dec_node_list(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, Bool is_proto); +GF_Err gf_bifs_dec_node_mask(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, Bool is_proto); + +/*called once a field has been modified through a command, send eventOut or propagate eventIn if needed*/ +void gf_bifs_check_field_change(GF_Node *node, GF_FieldInfo *field); + +GF_Err gf_bifs_flush_command_list(GF_BifsDecoder *codec); + +struct __tag_bifs_enc +{ + GF_Err LastError; + /*all attached streams*/ + GF_List *streamInfo; + /*active stream*/ + BIFSStreamInfo *info; + + Bool UseName; + + /*the scene graph the codec is encoding (set htrough ReplaceScene or manually)*/ + GF_SceneGraph *scene_graph; + /*current proto graph for DEF/USE*/ + GF_SceneGraph *current_proto_graph; + + /*Quantization*/ + /*QP stack*/ + GF_List *QPs; + /*active QP*/ + M_QuantizationParameter *ActiveQP; + + u32 NumCoord; + Bool coord_stored, storing_coord; + + GF_Proto *encoding_proto; + + /*keep track of DEF/USE*/ + GF_List *encoded_nodes; + Bool is_encoding_command; +}; + +GF_Err gf_bifs_enc_commands(GF_BifsEncoder *codec, GF_List *comList, GF_BitStream *bs); + +GF_Err gf_bifs_enc_node(GF_BifsEncoder * codec, GF_Node *node, u32 NDT_Tag, GF_BitStream *bs); +GF_Err gf_bifs_enc_sf_field(GF_BifsEncoder *codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); +GF_Err gf_bifs_enc_field(GF_BifsEncoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); +GF_Err gf_bifs_enc_mf_field(GF_BifsEncoder *codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); +GF_Err gf_bifs_enc_route(GF_BifsEncoder *codec, GF_Route *r, GF_BitStream *bs); +void gf_bifs_enc_name(GF_BifsEncoder *codec, GF_BitStream *bs, char *name); +GF_Node *gf_bifs_enc_find_node(GF_BifsEncoder *codec, u32 nodeID); + +#define GF_BIFS_WRITE_INT(codec, bs, val, nbBits, str, com) {\ + gf_bs_write_int(bs, val, nbBits); \ + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[BIFS] %s\t\t%d\t\t%d\t\t%s\n", str, nbBits, val, com ? com : "") ); \ + } \ + +GF_Route *gf_bifs_enc_is_field_ised(GF_BifsEncoder *codec, GF_Node *node, u32 fieldIndex); + +/*get field QP and anim info*/ +Bool gf_bifs_get_aq_info(GF_Node *Node, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits); + +/*get the absolute field 0_based index (or ALL mode) given the field index in IndexMode*/ +GF_Err gf_bifs_get_field_index(GF_Node *Node, u32 inField, u8 IndexMode, u32 *allField); + +/*returns the opaque NodeDataType of the node "children" field if any, or 0*/ +u32 gf_bifs_get_child_table(GF_Node *Node); + +/*returns binary type of node in the given version of the desired NDT*/ +u32 gf_bifs_get_node_type(u32 NDT_Tag, u32 NodeTag, u32 Version); + +/*converts field index from all_mode to given mode*/ +GF_Err gf_bifs_field_index_by_mode(GF_Node *node, u32 all_ind, u8 indexMode, u32 *outField); + +/*return number of bits needed to code all nodes present in the specified NDT*/ +u32 gf_bifs_get_ndt_bits(u32 NDT_Tag, u32 Version); +/*return absolute node tag given its type in the NDT and the NDT version number*/ +u32 gf_bifs_ndt_get_node_type(u32 NDT_Tag, u32 NodeType, u32 Version); + +#endif //_GF_BIFS_DEV_H_ + diff --git a/include/gpac/internal/bifs_tables.h b/include/gpac/internal/bifs_tables.h new file mode 100644 index 0000000..59449f5 --- /dev/null +++ b/include/gpac/internal/bifs_tables.h @@ -0,0 +1,661 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + DO NOT MOFIFY - File generated on GMT Thu Aug 07 11:43:26 2008 + + BY MPEG4Gen for GPAC Version 0.4.5-DEV +*/ + +#ifndef _NDT_H +#define _NDT_H + +#include <gpac/nodes_mpeg4.h> + + + +u32 ALL_GetNodeType(const u32 *table, const u32 count, u32 NodeTag, u32 Version); + + + +/* NDT BIFS Version 1 */ + +#define SFWorldNode_V1_NUMBITS 7 +#define SFWorldNode_V1_Count 100 + +static const u32 SFWorldNode_V1_TypeToTag[100] = { + TAG_MPEG4_Anchor, TAG_MPEG4_AnimationStream, TAG_MPEG4_Appearance, TAG_MPEG4_AudioBuffer, TAG_MPEG4_AudioClip, TAG_MPEG4_AudioDelay, TAG_MPEG4_AudioFX, TAG_MPEG4_AudioMix, TAG_MPEG4_AudioSource, TAG_MPEG4_AudioSwitch, TAG_MPEG4_Background, TAG_MPEG4_Background2D, TAG_MPEG4_Billboard, TAG_MPEG4_Bitmap, TAG_MPEG4_Box, TAG_MPEG4_Circle, TAG_MPEG4_Collision, TAG_MPEG4_Color, TAG_MPEG4_ColorInterpolator, TAG_MPEG4_CompositeTexture2D, TAG_MPEG4_CompositeTexture3D, TAG_MPEG4_Conditional, TAG_MPEG4_Cone, TAG_MPEG4_Coordinate, TAG_MPEG4_Coordinate2D, TAG_MPEG4_CoordinateInterpolator, TAG_MPEG4_CoordinateInterpolator2D, TAG_MPEG4_Curve2D, TAG_MPEG4_Cylinder, TAG_MPEG4_CylinderSensor, TAG_MPEG4_DirectionalLight, TAG_MPEG4_DiscSensor, TAG_MPEG4_ElevationGrid, TAG_MPEG4_Expression, TAG_MPEG4_Extrusion, TAG_MPEG4_Face, TAG_MPEG4_FaceDefMesh, TAG_MPEG4_FaceDefTables, TAG_MPEG4_FaceDefTransform, TAG_MPEG4_FAP, TAG_MPEG4_FDP, TAG_MPEG4_FIT, TAG_MPEG4_Fog, TAG_MPEG4_FontStyle, TAG_MPEG4_Form, TAG_MPEG4_Group, TAG_MPEG4_ImageTexture, TAG_MPEG4_IndexedFaceSet, TAG_MPEG4_IndexedFaceSet2D, TAG_MPEG4_IndexedLineSet, TAG_MPEG4_IndexedLineSet2D, TAG_MPEG4_Inline, TAG_MPEG4_LOD, TAG_MPEG4_Layer2D, TAG_MPEG4_Layer3D, TAG_MPEG4_Layout, TAG_MPEG4_LineProperties, TAG_MPEG4_ListeningPoint, TAG_MPEG4_Material, TAG_MPEG4_Material2D, TAG_MPEG4_MovieTexture, TAG_MPEG4_NavigationInfo, TAG_MPEG4_Normal, TAG_MPEG4_NormalInterpolator, TAG_MPEG4_OrderedGroup, TAG_MPEG4_OrientationInterpolator, TAG_MPEG4_PixelTexture, TAG_MPEG4_PlaneSensor, TAG_MPEG4_PlaneSensor2D, TAG_MPEG4_PointLight, TAG_MPEG4_PointSet, TAG_MPEG4_PointSet2D, TAG_MPEG4_PositionInterpolator, TAG_MPEG4_PositionInterpolator2D, TAG_MPEG4_ProximitySensor2D, TAG_MPEG4_ProximitySensor, TAG_MPEG4_QuantizationParameter, TAG_MPEG4_Rectangle, TAG_MPEG4_ScalarInterpolator, TAG_MPEG4_Script, TAG_MPEG4_Shape, TAG_MPEG4_Sound, TAG_MPEG4_Sound2D, TAG_MPEG4_Sphere, TAG_MPEG4_SphereSensor, TAG_MPEG4_SpotLight, TAG_MPEG4_Switch, TAG_MPEG4_TermCap, TAG_MPEG4_Text, TAG_MPEG4_TextureCoordinate, TAG_MPEG4_TextureTransform, TAG_MPEG4_TimeSensor, TAG_MPEG4_TouchSensor, TAG_MPEG4_Transform, TAG_MPEG4_Transform2D, TAG_MPEG4_Valuator, TAG_MPEG4_Viewpoint, TAG_MPEG4_VisibilitySensor, TAG_MPEG4_Viseme, TAG_MPEG4_WorldInfo +}; + +#define SF3DNode_V1_NUMBITS 6 +#define SF3DNode_V1_Count 52 + +static const u32 SF3DNode_V1_TypeToTag[52] = { + TAG_MPEG4_Anchor, TAG_MPEG4_AnimationStream, TAG_MPEG4_Background, TAG_MPEG4_Background2D, TAG_MPEG4_Billboard, TAG_MPEG4_Collision, TAG_MPEG4_ColorInterpolator, TAG_MPEG4_Conditional, TAG_MPEG4_CoordinateInterpolator, TAG_MPEG4_CoordinateInterpolator2D, TAG_MPEG4_CylinderSensor, TAG_MPEG4_DirectionalLight, TAG_MPEG4_DiscSensor, TAG_MPEG4_Face, TAG_MPEG4_Fog, TAG_MPEG4_Form, TAG_MPEG4_Group, TAG_MPEG4_Inline, TAG_MPEG4_LOD, TAG_MPEG4_Layer2D, TAG_MPEG4_Layer3D, TAG_MPEG4_Layout, TAG_MPEG4_ListeningPoint, TAG_MPEG4_NavigationInfo, TAG_MPEG4_NormalInterpolator, TAG_MPEG4_OrderedGroup, TAG_MPEG4_OrientationInterpolator, TAG_MPEG4_PlaneSensor, TAG_MPEG4_PlaneSensor2D, TAG_MPEG4_PointLight, TAG_MPEG4_PositionInterpolator, TAG_MPEG4_PositionInterpolator2D, TAG_MPEG4_ProximitySensor2D, TAG_MPEG4_ProximitySensor, TAG_MPEG4_QuantizationParameter, TAG_MPEG4_ScalarInterpolator, TAG_MPEG4_Script, TAG_MPEG4_Shape, TAG_MPEG4_Sound, TAG_MPEG4_Sound2D, TAG_MPEG4_SphereSensor, TAG_MPEG4_SpotLight, TAG_MPEG4_Switch, TAG_MPEG4_TermCap, TAG_MPEG4_TimeSensor, TAG_MPEG4_TouchSensor, TAG_MPEG4_Transform, TAG_MPEG4_Transform2D, TAG_MPEG4_Valuator, TAG_MPEG4_Viewpoint, TAG_MPEG4_VisibilitySensor, TAG_MPEG4_WorldInfo +}; + +#define SF2DNode_V1_NUMBITS 5 +#define SF2DNode_V1_Count 31 + +static const u32 SF2DNode_V1_TypeToTag[31] = { + TAG_MPEG4_Anchor, TAG_MPEG4_AnimationStream, TAG_MPEG4_Background2D, TAG_MPEG4_ColorInterpolator, TAG_MPEG4_Conditional, TAG_MPEG4_CoordinateInterpolator2D, TAG_MPEG4_DiscSensor, TAG_MPEG4_Face, TAG_MPEG4_Form, TAG_MPEG4_Group, TAG_MPEG4_Inline, TAG_MPEG4_LOD, TAG_MPEG4_Layer2D, TAG_MPEG4_Layer3D, TAG_MPEG4_Layout, TAG_MPEG4_OrderedGroup, TAG_MPEG4_PlaneSensor2D, TAG_MPEG4_PositionInterpolator2D, TAG_MPEG4_ProximitySensor2D, TAG_MPEG4_QuantizationParameter, TAG_MPEG4_ScalarInterpolator, TAG_MPEG4_Script, TAG_MPEG4_Shape, TAG_MPEG4_Sound2D, TAG_MPEG4_Switch, TAG_MPEG4_TermCap, TAG_MPEG4_TimeSensor, TAG_MPEG4_TouchSensor, TAG_MPEG4_Transform2D, TAG_MPEG4_Valuator, TAG_MPEG4_WorldInfo +}; + +#define SFStreamingNode_V1_NUMBITS 3 +#define SFStreamingNode_V1_Count 5 + +static const u32 SFStreamingNode_V1_TypeToTag[5] = { + TAG_MPEG4_AnimationStream, TAG_MPEG4_AudioClip, TAG_MPEG4_AudioSource, TAG_MPEG4_Inline, TAG_MPEG4_MovieTexture +}; + +#define SFAppearanceNode_V1_NUMBITS 1 +#define SFAppearanceNode_V1_Count 1 + +static const u32 SFAppearanceNode_V1_TypeToTag[1] = { + TAG_MPEG4_Appearance +}; + +#define SFAudioNode_V1_NUMBITS 3 +#define SFAudioNode_V1_Count 7 + +static const u32 SFAudioNode_V1_TypeToTag[7] = { + TAG_MPEG4_AudioBuffer, TAG_MPEG4_AudioClip, TAG_MPEG4_AudioDelay, TAG_MPEG4_AudioFX, TAG_MPEG4_AudioMix, TAG_MPEG4_AudioSource, TAG_MPEG4_AudioSwitch +}; + +#define SFBackground3DNode_V1_NUMBITS 1 +#define SFBackground3DNode_V1_Count 1 + +static const u32 SFBackground3DNode_V1_TypeToTag[1] = { + TAG_MPEG4_Background +}; + +#define SFBackground2DNode_V1_NUMBITS 1 +#define SFBackground2DNode_V1_Count 1 + +static const u32 SFBackground2DNode_V1_TypeToTag[1] = { + TAG_MPEG4_Background2D +}; + +#define SFGeometryNode_V1_NUMBITS 5 +#define SFGeometryNode_V1_Count 17 + +static const u32 SFGeometryNode_V1_TypeToTag[17] = { + TAG_MPEG4_Bitmap, TAG_MPEG4_Box, TAG_MPEG4_Circle, TAG_MPEG4_Cone, TAG_MPEG4_Curve2D, TAG_MPEG4_Cylinder, TAG_MPEG4_ElevationGrid, TAG_MPEG4_Extrusion, TAG_MPEG4_IndexedFaceSet, TAG_MPEG4_IndexedFaceSet2D, TAG_MPEG4_IndexedLineSet, TAG_MPEG4_IndexedLineSet2D, TAG_MPEG4_PointSet, TAG_MPEG4_PointSet2D, TAG_MPEG4_Rectangle, TAG_MPEG4_Sphere, TAG_MPEG4_Text +}; + +#define SFColorNode_V1_NUMBITS 1 +#define SFColorNode_V1_Count 1 + +static const u32 SFColorNode_V1_TypeToTag[1] = { + TAG_MPEG4_Color +}; + +#define SFTextureNode_V1_NUMBITS 3 +#define SFTextureNode_V1_Count 5 + +static const u32 SFTextureNode_V1_TypeToTag[5] = { + TAG_MPEG4_CompositeTexture2D, TAG_MPEG4_CompositeTexture3D, TAG_MPEG4_ImageTexture, TAG_MPEG4_MovieTexture, TAG_MPEG4_PixelTexture +}; + +#define SFCoordinateNode_V1_NUMBITS 1 +#define SFCoordinateNode_V1_Count 1 + +static const u32 SFCoordinateNode_V1_TypeToTag[1] = { + TAG_MPEG4_Coordinate +}; + +#define SFCoordinate2DNode_V1_NUMBITS 1 +#define SFCoordinate2DNode_V1_Count 1 + +static const u32 SFCoordinate2DNode_V1_TypeToTag[1] = { + TAG_MPEG4_Coordinate2D +}; + +#define SFExpressionNode_V1_NUMBITS 1 +#define SFExpressionNode_V1_Count 1 + +static const u32 SFExpressionNode_V1_TypeToTag[1] = { + TAG_MPEG4_Expression +}; + +#define SFFaceDefMeshNode_V1_NUMBITS 1 +#define SFFaceDefMeshNode_V1_Count 1 + +static const u32 SFFaceDefMeshNode_V1_TypeToTag[1] = { + TAG_MPEG4_FaceDefMesh +}; + +#define SFFaceDefTablesNode_V1_NUMBITS 1 +#define SFFaceDefTablesNode_V1_Count 1 + +static const u32 SFFaceDefTablesNode_V1_TypeToTag[1] = { + TAG_MPEG4_FaceDefTables +}; + +#define SFFaceDefTransformNode_V1_NUMBITS 1 +#define SFFaceDefTransformNode_V1_Count 1 + +static const u32 SFFaceDefTransformNode_V1_TypeToTag[1] = { + TAG_MPEG4_FaceDefTransform +}; + +#define SFFAPNode_V1_NUMBITS 1 +#define SFFAPNode_V1_Count 1 + +static const u32 SFFAPNode_V1_TypeToTag[1] = { + TAG_MPEG4_FAP +}; + +#define SFFDPNode_V1_NUMBITS 1 +#define SFFDPNode_V1_Count 1 + +static const u32 SFFDPNode_V1_TypeToTag[1] = { + TAG_MPEG4_FDP +}; + +#define SFFITNode_V1_NUMBITS 1 +#define SFFITNode_V1_Count 1 + +static const u32 SFFITNode_V1_TypeToTag[1] = { + TAG_MPEG4_FIT +}; + +#define SFFogNode_V1_NUMBITS 1 +#define SFFogNode_V1_Count 1 + +static const u32 SFFogNode_V1_TypeToTag[1] = { + TAG_MPEG4_Fog +}; + +#define SFFontStyleNode_V1_NUMBITS 1 +#define SFFontStyleNode_V1_Count 1 + +static const u32 SFFontStyleNode_V1_TypeToTag[1] = { + TAG_MPEG4_FontStyle +}; + +#define SFTopNode_V1_NUMBITS 3 +#define SFTopNode_V1_Count 4 + +static const u32 SFTopNode_V1_TypeToTag[4] = { + TAG_MPEG4_Group, TAG_MPEG4_Layer2D, TAG_MPEG4_Layer3D, TAG_MPEG4_OrderedGroup +}; + +#define SFLinePropertiesNode_V1_NUMBITS 1 +#define SFLinePropertiesNode_V1_Count 1 + +static const u32 SFLinePropertiesNode_V1_TypeToTag[1] = { + TAG_MPEG4_LineProperties +}; + +#define SFMaterialNode_V1_NUMBITS 2 +#define SFMaterialNode_V1_Count 2 + +static const u32 SFMaterialNode_V1_TypeToTag[2] = { + TAG_MPEG4_Material, TAG_MPEG4_Material2D +}; + +#define SFNavigationInfoNode_V1_NUMBITS 1 +#define SFNavigationInfoNode_V1_Count 1 + +static const u32 SFNavigationInfoNode_V1_TypeToTag[1] = { + TAG_MPEG4_NavigationInfo +}; + +#define SFNormalNode_V1_NUMBITS 1 +#define SFNormalNode_V1_Count 1 + +static const u32 SFNormalNode_V1_TypeToTag[1] = { + TAG_MPEG4_Normal +}; + +#define SFTextureCoordinateNode_V1_NUMBITS 1 +#define SFTextureCoordinateNode_V1_Count 1 + +static const u32 SFTextureCoordinateNode_V1_TypeToTag[1] = { + TAG_MPEG4_TextureCoordinate +}; + +#define SFTextureTransformNode_V1_NUMBITS 1 +#define SFTextureTransformNode_V1_Count 1 + +static const u32 SFTextureTransformNode_V1_TypeToTag[1] = { + TAG_MPEG4_TextureTransform +}; + +#define SFViewpointNode_V1_NUMBITS 1 +#define SFViewpointNode_V1_Count 1 + +static const u32 SFViewpointNode_V1_TypeToTag[1] = { + TAG_MPEG4_Viewpoint +}; + +#define SFVisemeNode_V1_NUMBITS 1 +#define SFVisemeNode_V1_Count 1 + +static const u32 SFVisemeNode_V1_TypeToTag[1] = { + TAG_MPEG4_Viseme +}; + + +u32 NDT_V1_GetNumBits(u32 NDT_Tag); +u32 NDT_V1_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType); +u32 NDT_V1_GetNodeType(u32 NDT_Tag, u32 NodeTag); + + + + +/* NDT BIFS Version 2 */ + +#define SFWorldNode_V2_NUMBITS 4 +#define SFWorldNode_V2_Count 12 + +static const u32 SFWorldNode_V2_TypeToTag[12] = { + TAG_MPEG4_AcousticMaterial, TAG_MPEG4_AcousticScene, TAG_MPEG4_ApplicationWindow, TAG_MPEG4_BAP, TAG_MPEG4_BDP, TAG_MPEG4_Body, TAG_MPEG4_BodyDefTable, TAG_MPEG4_BodySegmentConnectionHint, TAG_MPEG4_DirectiveSound, TAG_MPEG4_Hierarchical3DMesh, TAG_MPEG4_MaterialKey, TAG_MPEG4_PerceptualParameters +}; + +#define SF3DNode_V2_NUMBITS 3 +#define SF3DNode_V2_Count 3 + +static const u32 SF3DNode_V2_TypeToTag[3] = { + TAG_MPEG4_AcousticScene, TAG_MPEG4_Body, TAG_MPEG4_DirectiveSound +}; + +#define SF2DNode_V2_NUMBITS 2 +#define SF2DNode_V2_Count 2 + +static const u32 SF2DNode_V2_TypeToTag[2] = { + TAG_MPEG4_ApplicationWindow, TAG_MPEG4_Body +}; + +#define SFGeometryNode_V2_NUMBITS 2 +#define SFGeometryNode_V2_Count 1 + +static const u32 SFGeometryNode_V2_TypeToTag[1] = { + TAG_MPEG4_Hierarchical3DMesh +}; + +#define SFMaterialNode_V2_NUMBITS 2 +#define SFMaterialNode_V2_Count 2 + +static const u32 SFMaterialNode_V2_TypeToTag[2] = { + TAG_MPEG4_AcousticMaterial, TAG_MPEG4_MaterialKey +}; + +#define SFBAPNode_V2_NUMBITS 2 +#define SFBAPNode_V2_Count 1 + +static const u32 SFBAPNode_V2_TypeToTag[1] = { + TAG_MPEG4_BAP +}; + +#define SFBDPNode_V2_NUMBITS 2 +#define SFBDPNode_V2_Count 1 + +static const u32 SFBDPNode_V2_TypeToTag[1] = { + TAG_MPEG4_BDP +}; + +#define SFBodyDefTableNode_V2_NUMBITS 2 +#define SFBodyDefTableNode_V2_Count 1 + +static const u32 SFBodyDefTableNode_V2_TypeToTag[1] = { + TAG_MPEG4_BodyDefTable +}; + +#define SFBodySegmentConnectionHintNode_V2_NUMBITS 2 +#define SFBodySegmentConnectionHintNode_V2_Count 1 + +static const u32 SFBodySegmentConnectionHintNode_V2_TypeToTag[1] = { + TAG_MPEG4_BodySegmentConnectionHint +}; + +#define SFPerceptualParameterNode_V2_NUMBITS 2 +#define SFPerceptualParameterNode_V2_Count 1 + +static const u32 SFPerceptualParameterNode_V2_TypeToTag[1] = { + TAG_MPEG4_PerceptualParameters +}; + + +u32 NDT_V2_GetNumBits(u32 NDT_Tag); +u32 NDT_V2_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType); +u32 NDT_V2_GetNodeType(u32 NDT_Tag, u32 NodeTag); + + + + +/* NDT BIFS Version 3 */ + +#define SFWorldNode_V3_NUMBITS 2 +#define SFWorldNode_V3_Count 3 + +static const u32 SFWorldNode_V3_TypeToTag[3] = { + TAG_MPEG4_TemporalTransform, TAG_MPEG4_TemporalGroup, TAG_MPEG4_ServerCommand +}; + +#define SF3DNode_V3_NUMBITS 2 +#define SF3DNode_V3_Count 3 + +static const u32 SF3DNode_V3_TypeToTag[3] = { + TAG_MPEG4_TemporalTransform, TAG_MPEG4_TemporalGroup, TAG_MPEG4_ServerCommand +}; + +#define SF2DNode_V3_NUMBITS 2 +#define SF2DNode_V3_Count 3 + +static const u32 SF2DNode_V3_TypeToTag[3] = { + TAG_MPEG4_TemporalTransform, TAG_MPEG4_TemporalGroup, TAG_MPEG4_ServerCommand +}; + +#define SFTemporalNode_V3_NUMBITS 2 +#define SFTemporalNode_V3_Count 2 + +static const u32 SFTemporalNode_V3_TypeToTag[2] = { + TAG_MPEG4_TemporalTransform, TAG_MPEG4_TemporalGroup +}; + + +u32 NDT_V3_GetNumBits(u32 NDT_Tag); +u32 NDT_V3_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType); +u32 NDT_V3_GetNodeType(u32 NDT_Tag, u32 NodeTag); + + + + +/* NDT BIFS Version 4 */ + +#define SFWorldNode_V4_NUMBITS 3 +#define SFWorldNode_V4_Count 5 + +static const u32 SFWorldNode_V4_TypeToTag[5] = { + TAG_MPEG4_InputSensor, TAG_MPEG4_MatteTexture, TAG_MPEG4_MediaBuffer, TAG_MPEG4_MediaControl, TAG_MPEG4_MediaSensor +}; + +#define SF3DNode_V4_NUMBITS 3 +#define SF3DNode_V4_Count 5 + +static const u32 SF3DNode_V4_TypeToTag[5] = { + TAG_MPEG4_InputSensor, TAG_MPEG4_MatteTexture, TAG_MPEG4_MediaBuffer, TAG_MPEG4_MediaControl, TAG_MPEG4_MediaSensor +}; + +#define SF2DNode_V4_NUMBITS 3 +#define SF2DNode_V4_Count 5 + +static const u32 SF2DNode_V4_TypeToTag[5] = { + TAG_MPEG4_InputSensor, TAG_MPEG4_MatteTexture, TAG_MPEG4_MediaBuffer, TAG_MPEG4_MediaControl, TAG_MPEG4_MediaSensor +}; + +#define SFTextureNode_V4_NUMBITS 1 +#define SFTextureNode_V4_Count 1 + +static const u32 SFTextureNode_V4_TypeToTag[1] = { + TAG_MPEG4_MatteTexture +}; + + +u32 NDT_V4_GetNumBits(u32 NDT_Tag); +u32 NDT_V4_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType); +u32 NDT_V4_GetNodeType(u32 NDT_Tag, u32 NodeTag); + + + + +/* NDT BIFS Version 5 */ + +#define SFWorldNode_V5_NUMBITS 6 +#define SFWorldNode_V5_Count 39 + +static const u32 SFWorldNode_V5_TypeToTag[39] = { + TAG_MPEG4_BitWrapper, TAG_MPEG4_CoordinateInterpolator4D, TAG_MPEG4_DepthImage, TAG_MPEG4_FFD, TAG_MPEG4_Implicit, TAG_MPEG4_XXLFM_Appearance, TAG_MPEG4_XXLFM_BlendList, TAG_MPEG4_XXLFM_FrameList, TAG_MPEG4_XXLFM_LightMap, TAG_MPEG4_XXLFM_SurfaceMapList, TAG_MPEG4_XXLFM_ViewMapList, TAG_MPEG4_MeshGrid, TAG_MPEG4_NonLinearDeformer, TAG_MPEG4_NurbsCurve, TAG_MPEG4_NurbsCurve2D, TAG_MPEG4_NurbsSurface, TAG_MPEG4_OctreeImage, TAG_MPEG4_XXParticles, TAG_MPEG4_XXParticleInitBox, TAG_MPEG4_XXPlanarObstacle, TAG_MPEG4_XXPointAttractor, TAG_MPEG4_PointTexture, TAG_MPEG4_PositionAnimator, TAG_MPEG4_PositionAnimator2D, TAG_MPEG4_PositionInterpolator4D, TAG_MPEG4_ProceduralTexture, TAG_MPEG4_Quadric, TAG_MPEG4_SBBone, TAG_MPEG4_SBMuscle, TAG_MPEG4_SBSegment, TAG_MPEG4_SBSite, TAG_MPEG4_SBSkinnedModel, TAG_MPEG4_SBVCAnimation, TAG_MPEG4_ScalarAnimator, TAG_MPEG4_SimpleTexture, TAG_MPEG4_SolidRep, TAG_MPEG4_SubdivisionSurface, TAG_MPEG4_SubdivSurfaceSector, TAG_MPEG4_WaveletSubdivisionSurface +}; + +#define SF3DNode_V5_NUMBITS 5 +#define SF3DNode_V5_Count 17 + +static const u32 SF3DNode_V5_TypeToTag[17] = { + TAG_MPEG4_BitWrapper, TAG_MPEG4_CoordinateInterpolator4D, TAG_MPEG4_DepthImage, TAG_MPEG4_FFD, TAG_MPEG4_OctreeImage, TAG_MPEG4_XXParticles, TAG_MPEG4_PositionAnimator, TAG_MPEG4_PositionAnimator2D, TAG_MPEG4_PositionInterpolator4D, TAG_MPEG4_SBBone, TAG_MPEG4_SBMuscle, TAG_MPEG4_SBSegment, TAG_MPEG4_SBSite, TAG_MPEG4_SBSkinnedModel, TAG_MPEG4_SBVCAnimation, TAG_MPEG4_ScalarAnimator, TAG_MPEG4_WaveletSubdivisionSurface +}; + +#define SF2DNode_V5_NUMBITS 4 +#define SF2DNode_V5_Count 9 + +static const u32 SF2DNode_V5_TypeToTag[9] = { + TAG_MPEG4_BitWrapper, TAG_MPEG4_PositionAnimator2D, TAG_MPEG4_SBBone, TAG_MPEG4_SBMuscle, TAG_MPEG4_SBSegment, TAG_MPEG4_SBSite, TAG_MPEG4_SBSkinnedModel, TAG_MPEG4_SBVCAnimation, TAG_MPEG4_ScalarAnimator +}; + +#define SFAppearanceNode_V5_NUMBITS 1 +#define SFAppearanceNode_V5_Count 1 + +static const u32 SFAppearanceNode_V5_TypeToTag[1] = { + TAG_MPEG4_XXLFM_Appearance +}; + +#define SFGeometryNode_V5_NUMBITS 4 +#define SFGeometryNode_V5_Count 10 + +static const u32 SFGeometryNode_V5_TypeToTag[10] = { + TAG_MPEG4_BitWrapper, TAG_MPEG4_Implicit, TAG_MPEG4_MeshGrid, TAG_MPEG4_NonLinearDeformer, TAG_MPEG4_NurbsCurve, TAG_MPEG4_NurbsCurve2D, TAG_MPEG4_NurbsSurface, TAG_MPEG4_Quadric, TAG_MPEG4_SolidRep, TAG_MPEG4_SubdivisionSurface +}; + +#define SFTextureNode_V5_NUMBITS 1 +#define SFTextureNode_V5_Count 1 + +static const u32 SFTextureNode_V5_TypeToTag[1] = { + TAG_MPEG4_ProceduralTexture +}; + +#define SFDepthImageNode_V5_NUMBITS 1 +#define SFDepthImageNode_V5_Count 1 + +static const u32 SFDepthImageNode_V5_TypeToTag[1] = { + TAG_MPEG4_DepthImage +}; + +#define SFBlendListNode_V5_NUMBITS 1 +#define SFBlendListNode_V5_Count 1 + +static const u32 SFBlendListNode_V5_TypeToTag[1] = { + TAG_MPEG4_XXLFM_BlendList +}; + +#define SFFrameListNode_V5_NUMBITS 1 +#define SFFrameListNode_V5_Count 1 + +static const u32 SFFrameListNode_V5_TypeToTag[1] = { + TAG_MPEG4_XXLFM_FrameList +}; + +#define SFLightMapNode_V5_NUMBITS 1 +#define SFLightMapNode_V5_Count 1 + +static const u32 SFLightMapNode_V5_TypeToTag[1] = { + TAG_MPEG4_XXLFM_LightMap +}; + +#define SFSurfaceMapNode_V5_NUMBITS 1 +#define SFSurfaceMapNode_V5_Count 1 + +static const u32 SFSurfaceMapNode_V5_TypeToTag[1] = { + TAG_MPEG4_XXLFM_SurfaceMapList +}; + +#define SFViewMapNode_V5_NUMBITS 1 +#define SFViewMapNode_V5_Count 1 + +static const u32 SFViewMapNode_V5_TypeToTag[1] = { + TAG_MPEG4_XXLFM_ViewMapList +}; + +#define SFParticleInitializerNode_V5_NUMBITS 1 +#define SFParticleInitializerNode_V5_Count 1 + +static const u32 SFParticleInitializerNode_V5_TypeToTag[1] = { + TAG_MPEG4_XXParticleInitBox +}; + +#define SFInfluenceNode_V5_NUMBITS 2 +#define SFInfluenceNode_V5_Count 2 + +static const u32 SFInfluenceNode_V5_TypeToTag[2] = { + TAG_MPEG4_XXPlanarObstacle, TAG_MPEG4_XXPointAttractor +}; + +#define SFDepthTextureNode_V5_NUMBITS 2 +#define SFDepthTextureNode_V5_Count 2 + +static const u32 SFDepthTextureNode_V5_TypeToTag[2] = { + TAG_MPEG4_PointTexture, TAG_MPEG4_SimpleTexture +}; + +#define SFSBBoneNode_V5_NUMBITS 1 +#define SFSBBoneNode_V5_Count 1 + +static const u32 SFSBBoneNode_V5_TypeToTag[1] = { + TAG_MPEG4_SBBone +}; + +#define SFSBMuscleNode_V5_NUMBITS 1 +#define SFSBMuscleNode_V5_Count 1 + +static const u32 SFSBMuscleNode_V5_TypeToTag[1] = { + TAG_MPEG4_SBMuscle +}; + +#define SFSBSegmentNode_V5_NUMBITS 1 +#define SFSBSegmentNode_V5_Count 1 + +static const u32 SFSBSegmentNode_V5_TypeToTag[1] = { + TAG_MPEG4_SBSegment +}; + +#define SFSBSiteNode_V5_NUMBITS 1 +#define SFSBSiteNode_V5_Count 1 + +static const u32 SFSBSiteNode_V5_TypeToTag[1] = { + TAG_MPEG4_SBSite +}; + +#define SFBaseMeshNode_V5_NUMBITS 1 +#define SFBaseMeshNode_V5_Count 1 + +static const u32 SFBaseMeshNode_V5_TypeToTag[1] = { + TAG_MPEG4_SubdivisionSurface +}; + +#define SFSubdivSurfaceSectorNode_V5_NUMBITS 1 +#define SFSubdivSurfaceSectorNode_V5_Count 1 + +static const u32 SFSubdivSurfaceSectorNode_V5_TypeToTag[1] = { + TAG_MPEG4_SubdivSurfaceSector +}; + + +u32 NDT_V5_GetNumBits(u32 NDT_Tag); +u32 NDT_V5_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType); +u32 NDT_V5_GetNodeType(u32 NDT_Tag, u32 NodeTag); + + + + +/* NDT BIFS Version 6 */ + +#define SFWorldNode_V6_NUMBITS 4 +#define SFWorldNode_V6_Count 12 + +static const u32 SFWorldNode_V6_TypeToTag[12] = { + TAG_MPEG4_Clipper2D, TAG_MPEG4_ColorTransform, TAG_MPEG4_Ellipse, TAG_MPEG4_LinearGradient, TAG_MPEG4_PathLayout, TAG_MPEG4_RadialGradient, TAG_MPEG4_SynthesizedTexture, TAG_MPEG4_TransformMatrix2D, TAG_MPEG4_Viewport, TAG_MPEG4_XCurve2D, TAG_MPEG4_XFontStyle, TAG_MPEG4_XLineProperties +}; + +#define SF3DNode_V6_NUMBITS 3 +#define SF3DNode_V6_Count 5 + +static const u32 SF3DNode_V6_TypeToTag[5] = { + TAG_MPEG4_Clipper2D, TAG_MPEG4_ColorTransform, TAG_MPEG4_PathLayout, TAG_MPEG4_TransformMatrix2D, TAG_MPEG4_Viewport +}; + +#define SF2DNode_V6_NUMBITS 3 +#define SF2DNode_V6_Count 5 + +static const u32 SF2DNode_V6_TypeToTag[5] = { + TAG_MPEG4_Clipper2D, TAG_MPEG4_ColorTransform, TAG_MPEG4_PathLayout, TAG_MPEG4_TransformMatrix2D, TAG_MPEG4_Viewport +}; + +#define SFGeometryNode_V6_NUMBITS 2 +#define SFGeometryNode_V6_Count 2 + +static const u32 SFGeometryNode_V6_TypeToTag[2] = { + TAG_MPEG4_Ellipse, TAG_MPEG4_XCurve2D +}; + +#define SFTextureNode_V6_NUMBITS 2 +#define SFTextureNode_V6_Count 3 + +static const u32 SFTextureNode_V6_TypeToTag[3] = { + TAG_MPEG4_LinearGradient, TAG_MPEG4_RadialGradient, TAG_MPEG4_SynthesizedTexture +}; + +#define SFFontStyleNode_V6_NUMBITS 1 +#define SFFontStyleNode_V6_Count 1 + +static const u32 SFFontStyleNode_V6_TypeToTag[1] = { + TAG_MPEG4_XFontStyle +}; + +#define SFLinePropertiesNode_V6_NUMBITS 1 +#define SFLinePropertiesNode_V6_Count 1 + +static const u32 SFLinePropertiesNode_V6_TypeToTag[1] = { + TAG_MPEG4_XLineProperties +}; + +#define SFTextureTransformNode_V6_NUMBITS 1 +#define SFTextureTransformNode_V6_Count 1 + +static const u32 SFTextureTransformNode_V6_TypeToTag[1] = { + TAG_MPEG4_TransformMatrix2D +}; + +#define SFViewportNode_V6_NUMBITS 1 +#define SFViewportNode_V6_Count 1 + +static const u32 SFViewportNode_V6_TypeToTag[1] = { + TAG_MPEG4_Viewport +}; + + +u32 NDT_V6_GetNumBits(u32 NDT_Tag); +u32 NDT_V6_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType); +u32 NDT_V6_GetNodeType(u32 NDT_Tag, u32 NodeTag); + + + +u32 NDT_GetChildTable(u32 NodeTag); + + + + +#endif /*_NDT_H*/ + diff --git a/include/gpac/internal/camera.h b/include/gpac/internal/camera.h new file mode 100644 index 0000000..88d7bd7 --- /dev/null +++ b/include/gpac/internal/camera.h @@ -0,0 +1,184 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _CAMERA_H_ +#define _CAMERA_H_ + +//#include <gpac/internal/compositor_dev.h> +#include <gpac/scenegraph_vrml.h> + +/*camera flags*/ +enum +{ + /*if set frustum needs to be recomputed + we avoid computing it at each frame/interaction since that's a lot of matrix maths*/ + CAM_IS_DIRTY = 1, + /*if set when ortho, indicates the viewport matrix shall be used when computing modelview (2D only)*/ + CAM_HAS_VIEWPORT = 1<<2, +}; + +enum +{ + /*only valid at root node*/ + CULL_NOT_SET = 0, + /*subtree completely outside view vol*/ + CULL_OUTSIDE, + /*subtree completely inside view vol*/ + CULL_INSIDE, + /*subtree overlaps view vol - FIXME: would be nice to keep track of intersecting planes*/ + CULL_INTERSECTS +}; + +/*navigation info flags - non-VRML ones are simply blaxxun contact ones */ +enum +{ + /*headlight is on*/ + NAV_HEADLIGHT = 1, + /*any navigation (eg, user-interface navigation control allowed)*/ + NAV_ANY = 1<<1 +}; + +/*frustum object*/ +enum +{ + FRUS_NEAR_PLANE = 0, + FRUS_FAR_PLANE, + FRUS_LEFT_PLANE, + FRUS_RIGHT_PLANE, + FRUS_BOTTOM_PLANE, + FRUS_TOP_PLANE +}; + + + +enum +{ + /*nothing detected*/ + CF_NONE = 0, + /*collision detected*/ + CF_COLLISION = 1, + /*gravity detecion enabled*/ + CF_DO_GRAVITY = (1<<1), + /*gravity detected*/ + CF_GRAVITY = (1<<2), + /*viewpoint is stored at end of animation*/ + CF_STORE_VP = (1<<3), +}; + +typedef struct _camera +{ + /*this flag MUST be set by the owner of the camera*/ + Bool is_3D; + + u32 flags; + + /*viewport info*/ + GF_Rect vp; + /*not always same as VP due to aspect ratio*/ + Fixed width, height; + Fixed z_near, z_far; + + /*current vectors*/ + Fixed fieldOfView; + SFVec3f up, position, target; + + /*initial vp for reset*/ + SFVec3f vp_position; + SFRotation vp_orientation; + Fixed vp_fov, vp_dist; + + /*animation path*/ + SFVec3f start_pos, end_pos; + SFRotation start_ori, end_ori; + Fixed start_fov, end_fov; + /*for 2D cameras we never animate except for vp reset*/ + Fixed start_zoom; + SFVec2f start_trans, start_rot; + + /*center of examine movement*/ + SFVec3f examine_center; + + /*anim*/ + u32 anim_len, anim_start; + Bool jumping; + Fixed dheight; + + /*navigation info - overwridden by any bindable NavigationInfo node*/ + u32 navigation_flags, navigate_mode; + SFVec3f avatar_size; + Fixed visibility, speed; + Bool had_viewpoint, had_nav_info; + + /*last camera position before collision& gravity detection*/ + SFVec3f last_pos; + u32 collide_flags; + /*collision point in world coord*/ + SFVec3f collide_point; + /*collide dist in world coord, used to check if we have a closer collision*/ + Fixed collide_dist; + /*ground in world coord*/ + SFVec3f ground_point; + /*ground dist in world coord, used to check if we have a closer ground*/ + Fixed ground_dist; + /*for obstacle detection*/ + Bool last_had_ground; + Bool last_had_col; + + /*projection & modelview matrices*/ + GF_Matrix projection, modelview; + /*unprojection matrix = INV(P*M) used for screen->world compute*/ + GF_Matrix unprojection; + /*viewport matrix*/ + GF_Matrix viewport; + /*frustum planes*/ + GF_Plane planes[6]; + /*p vertex idx per plane (for bbox-frustum intersection checks)*/ + u32 p_idx[6]; + /*frustrum bounding sphere (for sphere-sphere frustum intersection checks)*/ + SFVec3f center; + Fixed radius; +} GF_Camera; + +/*invalidate camera to force recompute of all params*/ +void camera_invalidate(GF_Camera *cam); +/*updates camera. user transform is only used in 2D to set global user zoom/pan/translate*/ +void camera_update(GF_Camera *cam, GF_Matrix2D *user_transform, Bool center_coords); +/*reset to last viewport*/ +void camera_reset_viewpoint(GF_Camera *cam, Bool animate); +/*move camera to given vp*/ +void camera_move_to(GF_Camera *cam, SFVec3f pos, SFVec3f target, SFVec3f up); +Bool camera_animate(GF_Camera *cam); +void camera_stop_anim(GF_Camera *cam); +/*start jump mode*/ +void camera_jump(GF_Camera *cam); + +void camera_set_vectors(GF_Camera *cam, SFVec3f pos, SFRotation ori, Fixed fov); + +SFRotation camera_get_orientation(SFVec3f pos, SFVec3f target, SFVec3f up); +SFVec3f camera_get_pos_dir(GF_Camera *cam); +SFVec3f camera_get_target_dir(GF_Camera *cam); +SFVec3f camera_get_right_dir(GF_Camera *cam); + +#endif + diff --git a/include/gpac/internal/compositor_dev.h b/include/gpac/internal/compositor_dev.h new file mode 100644 index 0000000..311c2b3 --- /dev/null +++ b/include/gpac/internal/compositor_dev.h @@ -0,0 +1,1155 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Rendering sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _COMPOSITOR_DEV_H_ +#define _COMPOSITOR_DEV_H_ + + +#include <gpac/compositor.h> +/*include scene graph API*/ +#include <gpac/thread.h> +/*bridge between the rendering engine and the systems media engine*/ +#include <gpac/mediaobject.h> + +/*raster2D API*/ +#include <gpac/modules/raster2d.h> +/*font engine API*/ +#include <gpac/modules/font.h> +/*AV hardware API*/ +#include <gpac/modules/video_out.h> +#include <gpac/modules/audio_out.h> + +/*SVG properties*/ +#ifndef GPAC_DISABLE_SVG +#include <gpac/scenegraph_svg.h> +#endif + + +/*if defined, events are queued before being processed, otherwise they are handled whenever triggered*/ +//#define GF_SR_EVENT_QUEUE + + +/*use 2D caching for groups*/ +//#define GF_SR_USE_VIDEO_CACHE + + +/*FPS computed on this number of frame*/ +#define GF_SR_FPS_COMPUTE_SIZE 30 + +enum +{ + GF_SR_CFG_OVERRIDE_SIZE = 1, + GF_SR_CFG_SET_SIZE = 1<<1, + GF_SR_CFG_AR = 1<<2, + GF_SR_CFG_FULLSCREEN = 1<<3, + /*flag is set whenever we're reconfiguring visual. This will discard all UI + messages during this phase in order to avoid any deadlocks*/ + GF_SR_IN_RECONFIG = 1<<4, + /*special flag indicating the set size is actually due to a notif by the plugin*/ + GF_SR_CFG_WINDOWSIZE_NOTIF = 1<<10, +}; + + + +/*forward definition of the visual manager*/ +typedef struct _visual_manager GF_VisualManager; +typedef struct _draw_aspect_2d DrawAspect2D; +typedef struct _traversing_state GF_TraverseState; +typedef struct _gf_ft_mgr GF_FontManager; + +#ifndef GPAC_DISABLE_3D +#include <gpac/internal/camera.h> +#include <gpac/internal/mesh.h> + +#ifdef WIN32 +#include <windows.h> +typedef void (APIENTRY * PFNGLARBMULTITEXTUREPROC)(unsigned int target); +#else +typedef void (*PFNGLARBMULTITEXTUREPROC) (unsigned int target); +#endif + +typedef struct +{ + Bool multisample; + Bool bgra_texture; + Bool abgr_texture; + Bool npot_texture; + Bool rect_texture; + u32 yuv_texture; + PFNGLARBMULTITEXTUREPROC glActiveTextureARB; + PFNGLARBMULTITEXTUREPROC glClientActiveTextureARB; +} GLCaps; + +#endif + +#define DOUBLECLICK_TIME_MS 250 + +enum +{ + /*no text selection*/ + GF_SC_TSEL_NONE = 0, + /*text selection in progress*/ + GF_SC_TSEL_ACTIVE, + /*text selection frozen*/ + GF_SC_TSEL_FROZEN, + /*text selection has just been released*/ + GF_SC_TSEL_RELEASED, +}; + +struct __tag_compositor +{ + /*the main user*/ + GF_User *user; + /*terminal - only used for InputSensor*/ + GF_Terminal *term; + + /*audio renderer*/ + struct _audio_render *audio_renderer; + /*video out*/ + GF_VideoOutput *video_out; + /*2D rasterizer*/ + GF_Raster2D *rasterizer; + + /*visual rendering thread if used*/ + GF_Thread *VisualThread; + /*0: not init, 1: running, 2: exit requested, 3: done*/ + u32 video_th_state; + + /*compositor exclusive access to the scene and display*/ + GF_Mutex *mx; + + /*the main scene graph*/ + GF_SceneGraph *scene; + /*extra scene graphs (OSD, etc), always registered in draw order. That's the module responsability + to draw them*/ + GF_List *extra_scenes; + + /*all time nodes registered*/ + GF_List *time_nodes; + /*all textures (texture handlers)*/ + GF_List *textures; + +#ifdef GF_SR_EVENT_QUEUE + /*event queue*/ + GF_List *events; + GF_Mutex *ev_mx; +#endif + + /*simulation frame rate*/ + Double frame_rate; + u32 frame_duration; + u32 frame_time[GF_SR_FPS_COMPUTE_SIZE]; + u32 current_frame; + + u32 last_click_time; + + /*display size*/ + u32 display_width, display_height; + + /*visual output location on window (we may draw background color outside of it) + vp_x & vp_y: horizontal & vertical offset of the drawing area in the video output + vp_width & vp_height: width & height of the drawing area + * in scalable mode, this is the display size + * in not scalable mode, this is the final drawing area size (dst_w & dst_h of the blit) + */ + u32 vp_x, vp_y, vp_width, vp_height; + /*backbuffer size - in scalable mode, matches display size, otherwise matches scene size*/ + u32 output_width, output_height; + + /*scene size if any*/ + u32 scene_width, scene_height; + Bool has_size_info; + Bool fullscreen; + /*!! paused will not stop display (this enables pausing a VRML world and still examining it)*/ + Bool paused, step_mode; + Bool draw_next_frame; + /*freeze_display prevents any screen updates - needed when output driver uses direct video memory access*/ + Bool is_hidden, freeze_display; + + /*current frame number*/ + u32 frame_number; + /*count number of initialized sensors*/ + u32 interaction_sensors; + + /*set whenever 3D HW ctx changes (need to rebuild dlists/textures if any used)*/ + Bool reset_graphics; + + /*font engine*/ + GF_FontManager *font_manager; + /*set whenever a new font has been received*/ + Bool reset_fonts; + + /*options*/ + u32 aspect_ratio, antiAlias, texture_text_mode; + Bool high_speed, stress_mode; + Bool force_opengl_2d; + + /*key modif*/ + u32 key_states; + u32 interaction_level; + + /*size override when no size info is present + flags: 1: size override is requested (cfg) + 2: size override has been applied + */ + u32 override_size_flags; + + /*any of the above flags - reseted at each simulation tick*/ + u32 msg_type; + /*for size*/ + u32 new_width, new_height; + + /*current background color*/ + u32 back_color; + + /*bounding box draw type: none, unit box/rect and sphere (3D only)*/ + u32 draw_bvol; + + /*list of system colors*/ + u32 sys_colors[28]; + + /*all visual managers created*/ + GF_List *visuals; + /*all outlines cached*/ + GF_List *strike_bank; + + /*main visual manager - the one managing the primary video output*/ + GF_VisualManager *visual; + /*set to false whenever a new scene is attached to compositor*/ + Bool root_visual_setup; + + /*indicates whether the aspect ratio shall be recomputed: + 1: AR changed + 2: AR changed and root visual type changed between 2D and 3D + */ + u32 recompute_ar; + + Bool zoom_changed; + + /*traversing context*/ + struct _traversing_state *traverse_state; + + /*current picked node if any*/ + GF_Node *grab_node; + /*current picked node's parent use if any*/ + GF_Node *grab_use; + /*current focus node if any*/ + GF_Node *focus_node; + /*parent use node of the current focus node if any*/ + GF_Node *focus_used; + /*current parent focus node if any - needed to navigate within PROTOs*/ + GF_List *focus_ancestors; + GF_List *focus_use_stack; + /*focus node uses dom events*/ + Bool focus_uses_dom_events; + /*current sensor type*/ + u32 sensor_type; + /*list of VRML sensors active before the picking phase (eg active at the previous pass)*/ + GF_List *previous_sensors; + /*list of VRML sensors active after the picking phase*/ + GF_List *sensors; + /*indicates a sensor is currently active*/ + Bool grabbed_sensor; + + /*hardware handle for 2D screen access - currently only used with win32 (HDC) */ + void *hw_context; + /*indicates whether HW is locked*/ + Bool hw_locked; + /*screen buffer for direct access*/ + GF_VideoSurface hw_surface; + /*output buffer is configured in video memory*/ + Bool video_memory; + /*indicate if overlays were prezsent in the previous frame*/ + Bool last_had_overlays; + + /*options*/ + Bool scalable_zoom; + Bool enable_yuv_hw; + /*disables partial hardware blit (eg during dirty rect) to avoid artefacts*/ + Bool disable_partial_hw_blit; + + /*user navigation mode*/ + u32 navigate_mode; + /*set if content doesn't allow navigation*/ + Bool navigation_disabled; + + /*user mouse navigation state: + 0: not active + 1: pre-active phase: mouse has been clicked and waiting for mouse move to confirm. This allows + for clicking on objects in the navigation mode + 2: navigation is grabbed + */ + u32 navigation_state; + /*navigation x & y grab point in scene coord system*/ + Fixed grab_x, grab_y; + /*aspect ratio scale factor*/ + Fixed scale_x, scale_y; + /*user zoom level*/ + Fixed zoom; + /*user pan*/ + Fixed trans_x, trans_y; + /*user rotation angle - ALWAYS CENTERED*/ + Fixed rotation; + + Bool skip_flush; +#ifndef GPAC_DISABLE_SVG + u32 num_clicks; +#endif + + /*a dedicated drawable for focus highlight */ + struct _drawable *focus_highlight; + /*highlight fill and stroke colors (ARGB)*/ + u32 highlight_fill, highlight_stroke; + + /*picking info*/ + + /*picked node*/ + GF_Node *hit_node; + /*appearance at hit point - used for composite texture*/ + GF_Node *hit_appear; + /*parent use stack - SVG only*/ + GF_List *hit_use_stack, *prev_hit_use_stack; + /*picked node uses DOM event or VRML events ?*/ + Bool hit_use_dom_events; + + /*world->local and local->world transform at hit point*/ + GF_Matrix hit_world_to_local, hit_local_to_world; + /*hit point in local coord & world coord*/ + SFVec3f hit_local_point, hit_world_point; + /*tex coords at hit point*/ + SFVec2f hit_texcoords; + /*picking ray in world coord system*/ + GF_Ray hit_world_ray; + /*normal at hit point, local coord system*/ + SFVec3f hit_normal; + /*distance from ray origin used to discards further hits - FIXME: may not properly work with transparent layer3D*/ + Fixed hit_square_dist; + + /*text selection and edition*/ + + /*the active parent text node under selection*/ + GF_Node *text_selection; + /*text selection start/end in world coord system*/ + SFVec2f start_sel, end_sel; + /*text selection state*/ + u32 store_text_state; + /*parent text node when a text is hit (to handle tspan selection)*/ + GF_Node *hit_text; + u32 sel_buffer_len, sel_buffer_alloc; + u16 *sel_buffer; + u8 *selected_text; + /*text selection color - reverse video not yet supported*/ + u32 text_sel_color; + + /*set whenever the focus node is a text node*/ + u32 focus_text_type; + Bool edit_is_tspan; + /*pointer to edited text*/ + char **edited_text; + u32 caret_pos, dom_text_pos; + +#ifndef GPAC_DISABLE_3D + /*options*/ + /*emulate power-of-2 for video texturing by using a po2 texture and texture scaling. If any textureTransform + is applied to this texture, black stripes will appear on the borders. + If not set video is written through glDrawPixels with bitmap (slow scaling), or converted to + po2 texture*/ + Bool emul_pow2; + /*use openGL for outline rather than vectorial ones*/ + Bool raster_outlines; + /*disable RECT extensions (except for Bitmap...)*/ + Bool disable_rect_ext; + /*disable RECT extensions (except for Bitmap...)*/ + Bool bitmap_use_pixels; + /*disable RECT extensions (except for Bitmap...)*/ + u32 draw_normals; + /*backface cull type: 0 off, 1: on, 2: on with transparency*/ + u32 backcull; + /*polygon atialiasing*/ + Bool poly_aa; + /*disable gluScaleImage*/ + Bool disable_glu_scale; + /*wireframe/solid mode*/ + u32 wiremode; + /*collision detection mode*/ + u32 collide_mode; + /*gravity enabled*/ + Bool gravity_on; + /*AABB tree-based culling is disabled*/ + Bool disable_gl_cull; + /*YUV textures in OpenGL are disabled (soft YUV->RGB )*/ + Bool disable_yuvgl; + + /*unit box (1.0 size) and unit sphere (1.0 radius)*/ + GF_Mesh *unit_bbox; + + /*active layer3D for layer navigation - may be NULL*/ + GF_Node *active_layer; + + GLCaps gl_caps; + + u32 offscreen_width, offscreen_height; + +#ifdef GPAC_USE_TINYGL + void *tgl_ctx; +#endif + +#endif + + u32 networks_time; + u32 decoders_time; + + u32 visual_config_time; + u32 traverse_setup_time; + u32 traverse_and_direct_draw_time; + u32 indirect_draw_time; + +#ifdef GF_SR_USE_VIDEO_CACHE + /*video cache size / max size in kbytes*/ + u32 video_cache_current_size, video_cache_max_size; + u32 cache_scale, cache_tolerance; + /*sorted list (by cache priority) of cached groups - permanent for the lifetime of the scene/cache object*/ + GF_List *cached_groups; + /*list of groups being cached in one frame */ + GF_List *cached_groups_queue; +#endif + + /*used for -depth dump*/ + Fixed OGLDepthGain; /*gain applied to OpenGL depth buffer, a float [0..1])*/ + Fixed OGLDepthOffset; /*offset applied to OpenGL depth buffer*/ + +#ifdef GPAC_TRISCOPE_MODE + void *RenoirHandler; +#endif + +}; + + +/*base stack for timed nodes (nodes that activate themselves at given times) + @UpdateTimeNode: shall be setup by the node handler and is called once per simulation frame + @is_registerd: all handlers indicate store their state if wanted (provided for conveniency but not inspected by the compositor) + @needs_unregister: all handlers indicate they can be removed from the list of active time nodes +in order to save time. THIS IS INSPECTED by the compositor at each simulation tick after calling UpdateTimeNode +and if set, the node is removed right away from the list +*/ +typedef struct _time_node +{ + void (*UpdateTimeNode)(struct _time_node *); + Bool is_registered, needs_unregister; + /*user data*/ + void *udta; +} GF_TimeNode; + +void gf_sc_register_time_node(GF_Compositor *sr, GF_TimeNode *tn); +void gf_sc_unregister_time_node(GF_Compositor *sr, GF_TimeNode *tn); + +enum +{ + /*texture repeat along s*/ + GF_SR_TEXTURE_REPEAT_S = (1<<0), + /*texture repeat along t*/ + GF_SR_TEXTURE_REPEAT_T = (1<<1), + /*texture is a matte texture*/ + GF_SR_TEXTURE_MATTE = (1<<2), + /*texture doesn't need vertical flip for OpenGL*/ + GF_SR_TEXTURE_NO_GL_FLIP = (1<<3), + /*Set durin a composition cycle. If not set at the end of the cycle, + the hardware binding is released*/ + GF_SR_TEXTURE_USED = (1<<4), +}; + +typedef struct _gf_sc_texture_handler +{ + GF_Node *owner; + GF_Compositor *compositor; + /*low-level texture object - this is not exposed out of libgpac*/ + struct __texture_wrapper *tx_io; + /*media stream*/ + GF_MediaObject *stream; + /*texture is open (for DEF/USE)*/ + Bool is_open; + /*this is needed in case the Url is changed - note that media nodes cannot point to different + URLs (when they could in VRML), the MF is only holding media segment descriptions*/ + MFURL current_url; + /*to override by each texture node*/ + void (*update_texture_fcnt)(struct _gf_sc_texture_handler *txh); + /*needs_release if a visual frame is grabbed (not used by modules)*/ + Bool needs_release; + /*stream_finished: indicates stream is over (not used by modules)*/ + Bool stream_finished; + /*needs_refresh: indicates texture content has been changed - needed by modules performing tile drawing*/ + Bool needs_refresh; + /*needed to discard same frame fetch*/ + u32 last_frame_time; + /*active display in the texture (0, 0 == top, left)*/ + //GF_Rect active_window; + /*texture is transparent*/ + Bool transparent; + /*flags for user - the repeatS and repeatT are set upon creation, the rest is NEVER touched by compositor*/ + u32 flags; + /*gradients are relative to the object bounds, therefore a gradient is not the same if used on 2 different + objects - since we don't want to build an offscreen texture for the gradient, gradients have to be updated + at each draw - the matrix shall be updated to the gradient transformation in the local system + MUST be set for gradient textures*/ + void (*compute_gradient_matrix)(struct _gf_sc_texture_handler *txh, GF_Rect *bounds, GF_Matrix2D *mat, Bool for_3d); + + /*image data for natural media*/ + char *data; + u32 width, height, stride, pixelformat, pixel_ar; + /*if set texture has been transformed by MatteTexture -> disable blit*/ + Bool has_cmat; + + /*matteTexture parent if any*/ + GF_Node *matteTexture; + + +#ifdef GPAC_TRISCOPE_MODE + + void *RenoirObject; + +#endif + + +} GF_TextureHandler; + +/*setup texturing object*/ +void gf_sc_texture_setup(GF_TextureHandler *hdl, GF_Compositor *sr, GF_Node *owner); +/*destroy texturing object*/ +void gf_sc_texture_destroy(GF_TextureHandler *txh); + +/*return texture handle for built-in textures (movieTexture, ImageTexture and PixelTexture)*/ +GF_TextureHandler *gf_sc_texture_get_handler(GF_Node *n); + +/*these ones are needed by modules only for Background(2D) handling*/ + +/*returns 1 if url changed from current one*/ +Bool gf_sc_texture_check_url_change(GF_TextureHandler *txh, MFURL *url); +/*starts associated object*/ +GF_Err gf_sc_texture_play(GF_TextureHandler *txh, MFURL *url); +GF_Err gf_sc_texture_play_from_to(GF_TextureHandler *txh, MFURL *url, Double start_offset, Double end_offset, Bool can_loop, Bool lock_scene_timeline); +/*stops associated object*/ +void gf_sc_texture_stop(GF_TextureHandler *txh); +/*restarts associated object - DO NOT CALL stop/start*/ +void gf_sc_texture_restart(GF_TextureHandler *txh); +/*common routine for all video texture: fetches a frame and update the 2D texture object */ +void gf_sc_texture_update_frame(GF_TextureHandler *txh, Bool disable_resync); +/*release video memory if needed*/ +void gf_sc_texture_release_stream(GF_TextureHandler *txh); + + + +/*sensor node handler - this is not defined as a stack because Anchor is both a grouping node and a +sensor node, and we DO need the groupingnode stack...*/ +typedef struct _sensor_handler +{ + /*sensor enabled or not ?*/ + Bool (*IsEnabled)(GF_Node *node); + /*user input on sensor: + is_over: pointing device is over a shape the sensor is attached to + evt_type: mouse event type + compositor: pointer to compositor - hit info is stored at compositor level + */ + void (*OnUserEvent)(struct _sensor_handler *sh, Bool is_over, GF_Event *ev, GF_Compositor *compositor); + /*pointer to the sensor node*/ + GF_Node *sensor; +} GF_SensorHandler; + +/*returns TRUE if the node is a pointing device sensor node that can be stacked during traversing (all sensor except anchor)*/ +Bool compositor_mpeg4_is_sensor_node(GF_Node *node); +/*returns associated sensor handler from traversable stack (the node handler is always responsible for creation/deletion) +returns NULL if not a sensor or sensor is not activated*/ +GF_SensorHandler *compositor_mpeg4_get_sensor_handler(GF_Node *n); + +/*rendering modes*/ +enum +{ + /*regular traversing mode for z-sorting: + - 2D mode: builds the display list (may draw directly if requested) + - 3D mode: sort & queue transparent objects + */ + TRAVERSE_SORT = 0, + /*explicit draw routine used when flushing 2D display list*/ + TRAVERSE_DRAW_2D, + /*pick routine*/ + TRAVERSE_PICK, + /*get bounds routine: returns bounds in local coord system (including node transform if any)*/ + TRAVERSE_GET_BOUNDS, + /*set to signal bindable render - only called on bindable stack top if present. + for background (drawing request), viewports/viewpoints fog and navigation (setup) + all other nodes SHALL NOT RESPOND TO THIS CALL + */ + TRAVERSE_BINDABLE, + + /*writes the text selection into the compositor buffer - we need a traversing mode for this operation + to handle properly text and tspans*/ + TRAVERSE_GET_TEXT, + +#ifndef GPAC_DISABLE_3D + /*explicit draw routine used when flushing 3D display list*/ + TRAVERSE_DRAW_3D, + /*set global lights on. Since the model_matrix is not pushed to the target in this + pass, global lights shall not forget to do it (cf lighting.c)*/ + TRAVERSE_LIGHTING, + /*collision routine*/ + TRAVERSE_COLLIDE, +#endif +}; + + +typedef struct _group_cache_candidate GF_CacheCandidate; + + + +#define MAX_USER_CLIP_PLANES 4 + +/*the traversing context: set_up at top-level and passed through SFNode_Render. Each node is responsible for +restoring the context state before returning*/ +struct _traversing_state +{ + struct _audio_group *audio_parent; + struct _soundinterface *sound_holder; + +#ifndef GPAC_DISABLE_SVG + SVGPropertiesPointers *svg_props; + u32 svg_flags; +#endif + + /*current traversing mode*/ + u32 traversing_mode; + /*for 2D drawing, indicates objects are to be drawn as soon as traversed, at each frame*/ + Bool direct_draw; + /*current subtree is part of a switched-off subtree (needed for audio)*/ + Bool switched_off; + /*set by the traversed subtree to indicate no cull shall be performed*/ + Bool disable_cull; + + /*indicates if we are in a layer or not*/ + Bool is_layer; + /*current graph traversed is in pixel metrics*/ + Bool pixel_metrics; + /*minimal half-dimension (w/2, h/2)*/ + Fixed min_hsize; + + /*indicates if the current subtree is fliped compared to the target visual*/ + Bool fliped_coords; + + /*current size of viewport being traverse (root scene, layers)*/ + SFVec2f vp_size; + + /*the one and only visual manager currently being traversed*/ + GF_VisualManager *visual; + + /*current background and viewport stacks*/ + GF_List *backgrounds; + GF_List *viewpoints; + + /*current transformation from top-level*/ + GF_Matrix2D transform; + /*current color transformation from top-level*/ + GF_ColorMatrix color_mat; + /* Contains the viewbox transform, used for svg ref() transform */ + GF_Matrix2D vb_transform; + + /*if set all nodes shall be redrawn - set only at specific places in the tree*/ + Bool invalidate_all; + + /*text splitting: 0: no splitting, 1: word by word, 2:letter by letter*/ + u32 text_split_mode; + /*1-based idx of text element drawn*/ + u32 text_split_idx; + + /*all VRML sensors for the current level*/ + GF_List *vrml_sensors; + + /*current appearance when traversing geometry nodes*/ + GF_Node *appear; + /*parent group for composition: can be Form, Layout or Layer2D*/ + struct _parent_node_2d *parent; + + /*group/object bounds in local coordinate system*/ + GF_Rect bounds; + + /*node for which bounds should be fetched - SVG only*/ + GF_Node *for_node; + Bool abort_bounds_traverse; + GF_Matrix2D mx_at_node; + Bool ignore_strike; + + GF_List *use_stack; + /* Styling Property and others for SVG context */ +#ifndef GPAC_DISABLE_SVG + SVG_Number *parent_use_opacity; + SVGAllAttributes *parent_anim_atts; + + /*SVG text rendering state*/ + Bool in_svg_text; + Bool in_svg_text_area; + + /* current chunk & position of last placed text chunk*/ + u32 chunk_index; + Fixed text_end_x, text_end_y; + + /* text & tspan state*/ + GF_List *x_anchors; + SVG_Coordinates *text_x, *text_y, *text_rotate; + u32 count_x, count_y, count_rotate, idx_rotate; + + /* textArea state*/ + Fixed max_length, max_height; + Fixed base_x, base_y; + Fixed line_spacing; + Fixed base_shift; + /*quick and dirty hack to try to solve xml:space across text and tspans without + flattening the DOMText nodes + 0: first block of text + 1: previous block of text ended with a space + 2: previous block of text did NOT end with a space + */ + u32 last_char_type; + /*in textArea, indicates that the children bounds must be refreshed due to a baseline adjustment*/ + u32 refresh_children_bounds; +#endif + GF_Node *text_parent; + + /*current context to be drawn - only set when drawing in 2D mode or 3D for SVG*/ + struct _drawable_context *ctx; + + /*world ray for picking - in 2D, orig is 2D mouse pos and direction is -z*/ + GF_Ray ray; + + /*we have 2 clippers, one for regular clipping (layout, form if clipping) which is maintained in world coord system + and one for layer2D which is maintained in parent coord system (cf layer rendering). The layer clipper + is used only when cascading layers - layer3D doesn't use clippers*/ + Bool has_clip, has_layer_clip; + /*active clipper in world coord system */ + GF_Rect clipper, layer_clipper; + + + /*set when traversing a cached group during offscreen bitmap construction.*/ + Bool in_group_cache; + + +#ifndef GPAC_DISABLE_3D + /*the current camera*/ + GF_Camera *camera; + + /*current object (model) transformation from top-level, view is NOT included*/ + GF_Matrix model_matrix; + + /*fog bind stack*/ + GF_List *fogs; /*holds fogs info*/ + /*navigation bind stack*/ + GF_List *navigations; + + /*when drawing, signals the mesh is transparent (enables blending)*/ + Bool mesh_is_transparent; + /*when drawing, signals the number of textures used by the mesh*/ + u32 mesh_num_textures; + + /*bounds for TRAVERSE_GET_BOUNDS and background rendering*/ + GF_BBox bbox; + + /*cull flag (used to skip culling of children when parent bbox is completely inside/outside frustum)*/ + u32 cull_flag; + + /*toggle local lights on/off - field is ONLY valid in TRAVERSE_RENDER mode, and local lights + are always set off in reverse order as when set on*/ + Bool local_light_on; + /*current directional ligths contexts - only valid in TRAVERSE_RENDER*/ + GF_List *local_lights; + + /*clip planes in world coords*/ + GF_Plane clip_planes[MAX_USER_CLIP_PLANES]; + u32 num_clip_planes; + + + /*layer traversal state: + set to the first traversed layer3D when picking + set to the current layer3D traversed when rendering 3D to an offscreen bitmap. This alows other + nodes (typically bindables) seting the layer dirty flags to force a redraw + */ + GF_Node *layer3d; +#endif + + + /*depth z-axis info for 2D scenes in triscope mode*/ + Fixed depth; + +#ifdef GF_SR_USE_VIDEO_CACHE + /*set to 1 if cache evaluation can be skipped - this is only set when there is not enough memory + to cache a sub-group, in which case the group cannot be cached (we're caching in display coordinates)*/ + Bool cache_too_small; +#endif +}; + +/* + Audio mixer - MAX 6 CHANNELS SUPPORTED +*/ + +/*the audio object as used by the mixer. All audio nodes need to implement this interface*/ +typedef struct _audiointerface +{ + /*fetch audio data for a given audio delay (~soundcard drift) - if delay is 0 sync should not be performed + (eg intermediate mix) */ + char *(*FetchFrame) (void *callback, u32 *size, u32 audio_delay_ms); + /*release a number of bytes in the indicated frame (ts)*/ + void (*ReleaseFrame) (void *callback, u32 nb_bytes); + /*get media speed*/ + Fixed (*GetSpeed)(void *callback); + /*gets volume for each channel - vol = Fixed[6]. returns 1 if volume shall be changed (!= 1.0)*/ + Bool (*GetChannelVolume)(void *callback, Fixed *vol); + /*returns 1 if muted*/ + Bool (*IsMuted)(void *callback); + /*user callback*/ + void *callback; + /*returns 0 if config is not known yet or changed, + otherwise AND IF @for_reconf is set, updates member var below and return TRUE + You may return 0 to force parent user invalidation*/ + Bool (*GetConfig)(struct _audiointerface *ai, Bool for_reconf); + /*updated cfg, or 0 otherwise*/ + u32 chan, bps, samplerate, ch_cfg; +} GF_AudioInterface; + +typedef struct __audiomix GF_AudioMixer; + +/*create mixer - ar is NULL for any sub-mixers, or points to the main audio renderer (mixer outputs to sound driver)*/ +GF_AudioMixer *gf_mixer_new(struct _audio_render *ar); +void gf_mixer_del(GF_AudioMixer *am); +void gf_mixer_remove_all(GF_AudioMixer *am); +void gf_mixer_add_input(GF_AudioMixer *am, GF_AudioInterface *src); +void gf_mixer_remove_input(GF_AudioMixer *am, GF_AudioInterface *src); +void gf_mixer_lock(GF_AudioMixer *am, Bool lockIt); +/*mix inputs in buffer, return number of bytes written to output*/ +u32 gf_mixer_get_output(GF_AudioMixer *am, void *buffer, u32 buffer_size); +/*reconfig all sources if needed - returns TRUE if main audio config changed +NOTE: this is called at each gf_mixer_get_output by the mixer. To call externally for audio hardware +reconfiguration only*/ +Bool gf_mixer_reconfig(GF_AudioMixer *am); +/*retrieves mixer cfg*/ +void gf_mixer_get_config(GF_AudioMixer *am, u32 *outSR, u32 *outCH, u32 *outBPS, u32 *outChCfg); +/*called by audio renderer in case the hardware used a different setup than requested*/ +void gf_mixer_set_config(GF_AudioMixer *am, u32 outSR, u32 outCH, u32 outBPS, u32 ch_cfg); +Bool gf_mixer_is_src_present(GF_AudioMixer *am, GF_AudioInterface *ifce); +u32 gf_mixer_get_src_count(GF_AudioMixer *am); +void gf_mixer_force_chanel_out(GF_AudioMixer *am, u32 num_channels); +u32 gf_mixer_get_block_align(GF_AudioMixer *am); +Bool gf_mixer_must_reconfig(GF_AudioMixer *am); +Bool gf_mixer_empty(GF_AudioMixer *am); + +/*the audio renderer*/ +typedef struct _audio_render +{ + GF_AudioOutput *audio_out; + + Bool disable_resync; + Bool disable_multichannel; + + /*startup time (the audio renderer is used when present as the system clock)*/ + u32 startTime; + /*frozen time counter if set*/ + Bool Frozen; + u32 FreezeTime; + + /*final output*/ + GF_AudioMixer *mixer; + Bool need_reconfig; + /*client*/ + GF_User *user; + + /*audio thread if output not self-threaded*/ + GF_Thread *th; + /*thread state: 0: not intit, 1: running, 2: waiting for stop, 3: done*/ + u32 audio_th_state; + + u32 audio_delay, volume, pan; +} GF_AudioRenderer; + +/*creates audio renderer*/ +GF_AudioRenderer *gf_sc_ar_load(GF_User *user); +/*deletes audio renderer*/ +void gf_sc_ar_del(GF_AudioRenderer *ar); +/*control audio renderer - CtrlType: + 0: pause + 1: resume + 2: clean HW buffer and play +*/ +void gf_sc_ar_control(GF_AudioRenderer *ar, u32 CtrlType); +/*set volume and pan*/ +void gf_sc_ar_set_volume(GF_AudioRenderer *ar, u32 Volume); +void gf_sc_ar_set_pan(GF_AudioRenderer *ar, u32 Balance); +/*set audio priority*/ +void gf_sc_ar_set_priority(GF_AudioRenderer *ar, u32 priority); +/*gets time in msec - this is the only clock used by the whole ESM system - depends on the audio driver*/ +u32 gf_sc_ar_get_clock(GF_AudioRenderer *ar); +/*reset all input nodes*/ +void gf_sc_ar_reset(GF_AudioRenderer *ar); +/*add audio node*/ +void gf_sc_ar_add_src(GF_AudioRenderer *ar, GF_AudioInterface *source); +/*remove audio node*/ +void gf_sc_ar_remove_src(GF_AudioRenderer *ar, GF_AudioInterface *source); +/*reconfig audio hardware if needed*/ +void gf_sc_ar_reconfig(GF_AudioRenderer *ar); +u32 gf_sc_ar_get_delay(GF_AudioRenderer *ar); + +/*the sound node interface for intensity & spatialization*/ +typedef struct _soundinterface +{ + /*gets volume for each channel - vol = Fixed[6]. returns 1 if volume shall be changed (!= 1.0) + if NULL channels are always at full intensity*/ + Bool (*GetChannelVolume)(GF_Node *owner, Fixed *vol); + /*get sound priority (0: min, 255: max) - used by mixer to determine*/ + u8 (*GetPriority) (GF_Node *owner); + /*node owning the structure*/ + GF_Node *owner; +} GF_SoundInterface; + +/*audio common to AudioClip and AudioSource*/ +typedef struct +{ + GF_Node *owner; + GF_Compositor *compositor; + GF_AudioInterface input_ifce; + /*can be NULL if the audio node generates its output from other input*/ + GF_MediaObject *stream; + /*object speed and intensity*/ + Fixed speed, intensity; + Bool stream_finished; + Bool need_release; + MFURL url; + u32 is_open; + Bool is_muted; + Bool register_with_renderer, register_with_parent; + + GF_SoundInterface *snd; +} GF_AudioInput; +/*setup interface with audio renderer - overwrite any functions needed after setup EXCEPT callback object*/ +void gf_sc_audio_setup(GF_AudioInput *ai, GF_Compositor *sr, GF_Node *node); +/*open audio object*/ +GF_Err gf_sc_audio_open(GF_AudioInput *ai, MFURL *url, Double clipBegin, Double clipEnd); +/*closes audio object*/ +void gf_sc_audio_stop(GF_AudioInput *ai); +/*restarts audio object (cf note in MediaObj)*/ +void gf_sc_audio_restart(GF_AudioInput *ai); + +Bool gf_sc_audio_check_url(GF_AudioInput *ai, MFURL *url); + +/*base grouping audio node (nodes with several audio sources as children)*/ +#define AUDIO_GROUP_NODE \ + GF_AudioInput output; \ + void (*add_source)(struct _audio_group *_this, GF_AudioInput *src); \ + +typedef struct _audio_group +{ + AUDIO_GROUP_NODE +} GF_AudioGroup; + + +/*register audio node with parent audio renderer (mixer or main renderer)*/ +void gf_sc_audio_register(GF_AudioInput *ai, GF_TraverseState *tr_state); +void gf_sc_audio_unregister(GF_AudioInput *ai); + + +#ifndef GPAC_DISABLE_SVG +GF_Err gf_term_get_mfurl_from_xlink(GF_Node *node, MFURL *mfurl); +Fixed gf_sc_svg_convert_length_to_display(GF_Compositor *sr, SVG_Length *length); + +char *gf_term_resolve_xlink(GF_Node *node, char *the_url); +#endif + +GF_Err compositor_2d_set_aspect_ratio(GF_Compositor *sr); +void compositor_2d_set_user_transform(GF_Compositor *sr, Fixed zoom, Fixed tx, Fixed ty, Bool is_resize) ; +GF_Err compositor_2d_get_video_access(GF_VisualManager *surf); +void compositor_2d_release_video_access(GF_VisualManager *surf); +Bool compositor_2d_draw_bitmap(GF_VisualManager *visual, GF_TraverseState *tr_state, struct _drawable_context *ctx, GF_ColorKey *col_key); +GF_Rect compositor_2d_update_clipper(GF_TraverseState *tr_state, GF_Rect this_clip, Bool *need_restore, GF_Rect *original, Bool for_layer); + +Bool compositor_get_2d_plane_intersection(GF_Ray *ray, SFVec3f *res); + +void compositor_send_resize_event(GF_Compositor *compositor, Fixed old_z, Fixed old_tx, Fixed old_ty, Bool is_resize); + +void compositor_set_cache_memory(GF_Compositor *compositor, u32 memory); +/*checks whether the background node is transparent or not*/ +Bool compositor_background_transparent(GF_Node *node); + +#ifndef GPAC_DISABLE_3D + +GF_Err compositor_3d_set_aspect_ratio(GF_Compositor *sr); +GF_Camera *compositor_3d_get_camera(GF_Compositor *sr); +void compositor_3d_reset_camera(GF_Compositor *sr); +GF_Camera *compositor_layer3d_get_camera(GF_Node *node); +void compositor_layer3d_bind_camera(GF_Node *node, Bool do_bind, u32 nav_value); +void compositor_3d_draw_bitmap(struct _drawable *stack, DrawAspect2D *asp, GF_TraverseState *tr_state, Fixed width, Fixed height, Fixed bmp_scale_x, Fixed bmp_scale_y); + +GF_Err compositor_3d_get_screen_buffer(GF_Compositor *sr, GF_VideoSurface *fb, Bool depth_buffer); +GF_Err compositor_3d_release_screen_buffer(GF_Compositor *sr, GF_VideoSurface *framebuffer); + +void gf_sc_load_opengl_extensions(GF_Compositor *sr); + +#endif + +Bool gf_sc_exec_event(GF_Compositor *sr, GF_Event *evt); +void gf_sc_get_nodes_bounds(GF_Node *self, GF_ChildNodeItem *children, GF_TraverseState *tr_state, s32 *child_idx); + + +void gf_sc_visual_register(GF_Compositor *sr, GF_VisualManager *surf); +void gf_sc_visual_unregister(GF_Compositor *sr, GF_VisualManager *surf); +Bool gf_sc_visual_is_registered(GF_Compositor *sr, GF_VisualManager *surf); + +Bool gf_sc_pick_in_clipper(GF_TraverseState *tr_state, GF_Rect *clip); + +void compositor_gradient_update(GF_TextureHandler *txh); +void compositor_set_ar_scale(GF_Compositor *sr, Fixed scaleX, Fixed scaleY); + +/*reset focus if node being deleted has the focus - must be called for each focusable node (internally called for 2D & 3D drawable nodes)*/ +void gf_sc_check_focus_upon_destroy(GF_Node *n); + +#ifndef GPAC_DISABLE_SVG + +void compositor_svg_build_gradient_texture(GF_TextureHandler *txh); + +/*base routine fo all svg elements: + - check for conditional processing (requiredXXX, ...) + - apply animation and inheritance + + returns 0 if the node shall not be traversed due to conditional processing +*/ +Bool compositor_svg_traverse_base(GF_Node *node, SVGAllAttributes *all_atts, GF_TraverseState *tr_state, SVGPropertiesPointers *backup_props, u32 *backup_flags); +Bool compositor_svg_is_display_off(SVGPropertiesPointers *props); +void compositor_svg_apply_local_transformation(GF_TraverseState *tr_state, SVGAllAttributes *atts, GF_Matrix2D *backup_matrix_2d, GF_Matrix *backup_matrix); +void compositor_svg_restore_parent_transformation(GF_TraverseState *tr_state, GF_Matrix2D *backup_matrix_2d, GF_Matrix *backup_matrix); + +void compositor_svg_traverse_children(GF_ChildNodeItem *children, GF_TraverseState *tr_state); + +Bool compositor_svg_evaluate_conditional(GF_Compositor *compositor, SVGAllAttributes *all_atts); + +/*returns the node associated with the given xlink - this is not always the target node of the xlink structure due +to async restart of animation nodes*/ +GF_Node *compositor_svg_get_xlink_resource_node(GF_Node *node, XMLRI *xlink); + +#endif + +/*Text handling*/ + +typedef struct _gf_font GF_Font; + +struct _gf_font +{ + /*fonts are linked within the font manager*/ + GF_Font *next; + /*list of glyphs in the font*/ + GF_Glyph *glyph; + + char *name; + u32 em_size; + u32 styles; + /*font uits in em size*/ + s32 ascent, descent, underline, line_spacing, max_advance_h, max_advance_v; + s32 baseline; + + /*only set for embedded font engines (SVG fonts)*/ + GF_Font *(*get_alias)(void *udta); + GF_Err (*get_glyphs)(void *udta, const char *utf_string, u32 *glyph_ids_buffer, u32 *io_glyph_ids_buffer_size, const char *xml_lang, Bool *is_rtl); + GF_Glyph *(*load_glyph)(void *udta, u32 glyph_name); + void *udta; + + Bool not_loaded; + + struct _gf_ft_mgr *ft_mgr; + /*list of spans currently using the font - this is needed to allow for dynamic discard of the font*/ + GF_List *spans; +}; + +enum +{ + /*span direction is horizontal*/ + GF_TEXT_SPAN_HORIZONTAL = 1, + /*span is underlined*/ + GF_TEXT_SPAN_UNDERLINE = 1<<1, + /*span is fliped (coord systems with Y-axis pointing downwards like SVG)*/ + GF_TEXT_SPAN_FLIP = 1<<2, + /*span is in the current text selection*/ + GF_TEXT_SPAN_RIGHT_TO_LEFT = 1<<3, + /*span is in the current text selection*/ + GF_TEXT_SPAN_SELECTED = 1<<4 +}; + +typedef struct __text_span +{ + GF_Font *font; + + GF_Glyph **glyphs; + u32 nb_glyphs; + + u32 flags; + + Fixed font_size; + + /*scale to apply to get to requested font size*/ + Fixed font_scale; + GF_Rect bounds; + + /*MPEG-4 span scaling (length & maxExtend)*/ + Fixed x_scale, y_scale; + /*x (resp. y) offset in local coord system. Ignored if per-glyph dx (resp dy) are specified*/ + Fixed off_x, off_y; + + /*per-glyph positioning - when allocated, this is the same number as the glyphs*/ + Fixed *dx, *dy, *rot; + + /*span language*/ +// const char *lang; + + /*span texturing and 3D tools*/ + struct _span_internal *ext; + + /*SVG stuff :(*/ + GF_Node *anchor; + GF_Node *user; +} GF_TextSpan; + +GF_FontManager *gf_font_manager_new(GF_User *user); +void gf_font_manager_del(GF_FontManager *fm); + +GF_Font *gf_font_manager_set_font(GF_FontManager *fm, char **alt_fonts, u32 nb_fonts, u32 styles); +GF_Font *gf_font_manager_set_font_ex(GF_FontManager *fm, char **alt_fonts, u32 nb_fonts, u32 styles, Bool check_only); + +GF_TextSpan *gf_font_manager_create_span(GF_FontManager *fm, GF_Font *font, char *span, Fixed font_size, Bool needs_x_offset, Bool needs_y_offset, Bool needs_rotate, const char *lang, Bool fliped_text, u32 styles, GF_Node *user); +void gf_font_manager_delete_span(GF_FontManager *fm, GF_TextSpan *tspan); + +GF_Err gf_font_manager_register_font(GF_FontManager *fm, GF_Font *font); +GF_Err gf_font_manager_unregister_font(GF_FontManager *fm, GF_Font *font); + +void gf_font_manager_refresh_span_bounds(GF_TextSpan *span); +GF_Path *gf_font_span_create_path(GF_TextSpan *span); + + +void gf_font_spans_draw_2d(GF_List *spans, GF_TraverseState *tr_state, u32 hl_color, Bool force_texture_text, GF_Rect *bounds); +void gf_font_spans_draw_3d(GF_List *spans, GF_TraverseState *tr_state, DrawAspect2D *asp, u32 text_hl, Bool force_texturing); +void gf_font_spans_pick(GF_Node *node, GF_List *spans, GF_TraverseState *tr_state, GF_Rect *node_bounds, Bool use_dom_events, struct _drawable *drawable); +void gf_font_spans_get_selection(GF_Node *node, GF_List *spans, GF_TraverseState *tr_state); + +GF_Font *gf_compositor_svg_set_font(GF_FontManager *fm, char *a_font, u32 styles, Bool check_only); + +#endif /*_COMPOSITOR_DEV_H_*/ + diff --git a/include/gpac/internal/config_static.h b/include/gpac/internal/config_static.h new file mode 100644 index 0000000..2dcd660 --- /dev/null +++ b/include/gpac/internal/config_static.h @@ -0,0 +1,89 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * Copyright (c) ENST 2008 - + * All rights reserved + * + * This file is part of GPAC + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_CONFIG_H_ +#define _GF_CONFIG_H_ + +/*this file defines all common macros for libgpac compilation*/ + +/*platform is big endian*/ +//#define GPAC_BIG_ENDIAN + +/*SSL enabled*/ +//#define GPAC_HAS_SSL + +/*spidermonkey enabled*/ +#define GPAC_HAS_SPIDERMONKEY + +/*libjpeg enabled*/ +#define GPAC_HAS_JPEG + +/*pnj enabled*/ +#define GPAC_HAS_PNG + +/*IPv6 enabled - for win32, this is evaluated at compile time, !! do not uncomment !!*/ +//#define GPAC_HAS_IPV6 + +/*SVG disabled*/ +//#define GPAC_DISABLE_SVG + +/*3D compositor disabled*/ +//#define GPAC_DISABLE_3D + +/*use TinyGL instead of OpenGL*/ +//#define GPAC_USE_TINYGL + +/*use OpenGL ES instead of OpenGL*/ +//#define GPAC_USE_OGL_ES + + +#if defined(_WIN32_WCE) + +/*use intel fixed-point*/ +//#define GPAC_USE_IGPP +/*use intel fixed-point with high precision*/ +//#define GPAC_USE_IGPP_HP + +#if defined(GPAC_USE_IGPP) && defined(GPAC_USE_IGPP_HP) +#error "Only one of GPAC_USE_IGPP and GPAC_USE_IGPP_HP can be defined" +#endif + + +#if !defined(GPAC_DISABLE_3D) && !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_OGL_ES) +#define GPAC_USE_OGL_ES +#endif + +#endif + + +#if !defined(GPAC_USE_OGL_ES) && !defined(GPAC_USE_TINYGL) +#define GPAC_HAS_GLU +#endif + + + +#endif /*_GF_CONFIG_H_*/ + diff --git a/include/gpac/internal/crypt_dev.h b/include/gpac/internal/crypt_dev.h new file mode 100644 index 0000000..79d6c25 --- /dev/null +++ b/include/gpac/internal/crypt_dev.h @@ -0,0 +1,158 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Crypto Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_CRYPT_DEV_H_ +#define _GF_CRYPT_DEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/crypt.h> + +/*the samllest version of the lib: only AES-128-CTR supported*/ +#define GPAC_CRYPT_ISMA_ONLY + + +#if !defined(GPAC_CRYPT_ISMA_ONLY) && defined(GPAC_READ_ONLY) +#define GPAC_CRYPT_ISMA_ONLY +#endif + +typedef void (*mcryptfunc)(void*,void*); +typedef GF_Err (*mcrypt_setkeystream)(void *, const void *, int, const void *, int); +typedef GF_Err (*mcrypt_setkeyblock) (void *, const void *, int); +typedef GF_Err (*mcrypt_docrypt) (void *, const void *, int); + +/*private - do not use*/ +typedef struct _tag_crypt_stream +{ + const char *algo_name; + u32 algo_version; + const char *mode_name; + u32 mode_version; + + /* Holds the algorithm's internal key */ + char *akey; + /* holds the mode's internal buffers */ + char *abuf; + /* holds the key */ + char *keyword_given; + + /*all below are static vars for mode and algo - sizes are in bytes*/ + + /*modes access*/ + GF_Err (*_init_mcrypt) (void *, void *, int, void *, int); + void (*_end_mcrypt) (void *); + GF_Err (*_mcrypt) (void *, void *, int, int, void *, mcryptfunc func, mcryptfunc func2); + GF_Err (*_mdecrypt) (void *, void *, int, int, void *, mcryptfunc func, mcryptfunc func2); + GF_Err (*_mcrypt_set_state) (void *, void *, int ); + GF_Err (*_mcrypt_get_state) (void *, void *, int *); + /*algo access*/ + void *a_encrypt; + void *a_decrypt; + void *a_set_key; + + u32 algo_size; + u32 algo_block_size; + u32 key_size; + u32 num_key_sizes; + u32 key_sizes[MAX_KEY_SIZES]; + u32 algo_IV_size; + + u32 mode_size; + Bool is_block_algo, is_block_algo_mode, is_block_mode, has_IV; +} GF_CryptStream; + +/*modes*/ +void gf_crypt_register_cbc(GF_Crypt *td); +void gf_crypt_register_cfb(GF_Crypt *td); +void gf_crypt_register_ctr(GF_Crypt *td); +void gf_crypt_register_ecb(GF_Crypt *td); +void gf_crypt_register_ncfb(GF_Crypt *td); +void gf_crypt_register_nofb(GF_Crypt *td); +void gf_crypt_register_ofb(GF_Crypt *td); +void gf_crypt_register_stream(GF_Crypt *td); +/*algos*/ +void gf_crypt_register_des(GF_Crypt *td); +void gf_crypt_register_3des(GF_Crypt *td); +void gf_crypt_register_rijndael_128(GF_Crypt *td); +void gf_crypt_register_rijndael_192(GF_Crypt *td); +void gf_crypt_register_rijndael_256(GF_Crypt *td); + + +#define rotl32(x,n) (((x) << ((u32)(n))) | ((x) >> (32 - (u32)(n)))) +#define rotr32(x,n) (((x) >> ((u32)(n))) | ((x) << (32 - (u32)(n)))) +#define rotl16(x,n) (((x) << ((u16)(n))) | ((x) >> (16 - (u16)(n)))) +#define rotr16(x,n) (((x) >> ((u16)(n))) | ((x) << (16 - (u16)(n)))) + +/* Use hardware rotations.. when available */ +#ifdef swap32 +# define byteswap32(x) swap32(x) +#else +# ifdef swap_32 +# define byteswap32(x) swap_32(x) +# else +# ifdef bswap_32 +# define byteswap32(x) bswap_32(x) +# else +# define byteswap32(x) ((rotl32(x, 8) & 0x00ff00ff) | (rotr32(x, 8) & 0xff00ff00)) +# endif +# endif +#endif + +#ifdef swap16 +# define byteswap16(x) swap16(x) +#else +# ifdef swap_16 +# define byteswap16(x) swap_16(x) +# else +# ifdef bswap_16 +# define byteswap16(x) bswap_16(x) +# else +# define byteswap16(x) ((rotl16(x, 8) & 0x00ff) | (rotr16(x, 8) & 0xff00)) +# endif +# endif +#endif + +GFINLINE static +void memxor(unsigned char *o1, unsigned char *o2, int length) +{ + int i; + + for (i = 0; i < length; i++) { + o1[i] ^= o2[i]; + } + return; +} + + +#define Bzero(x, y) memset(x, 0, y) + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_CRYPT_DEV_H_*/ + diff --git a/include/gpac/internal/ietf_dev.h b/include/gpac/internal/ietf_dev.h new file mode 100644 index 0000000..41ebf66 --- /dev/null +++ b/include/gpac/internal/ietf_dev.h @@ -0,0 +1,327 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_IETF_DEV_H_ +#define _GF_IETF_DEV_H_ + +#include <gpac/ietf.h> +#include <gpac/thread.h> + +/* + RTP intern +*/ + +typedef struct +{ + /*version of the packet. Must be 2*/ + u8 Version; + /*padding bits at the end of the payload*/ + u8 Padding; + /*number of reports*/ + u8 Count; + /*payload type of RTCP pck*/ + u8 PayloadType; + /*The length of this RTCP packet in 32-bit words minus one including the header and any padding*/ + u16 Length; + /*sync source identifier*/ + u32 SSRC; +} GF_RTCPHeader; + + +typedef struct __PRO_item +{ + struct __PRO_item *next; + u32 pck_seq_num; + void *pck; + u32 size; +} GF_POItem; + +typedef struct __PO +{ + struct __PRO_item *in; + u32 head_seqnum; + u32 Count; + u32 MaxCount; + u32 IsInit; + u32 MaxDelay, LastTime; +} GF_RTPReorder; + +/* creates new RTP reorderer + @MaxCount: forces automatic packet flush. 0 means no flush + @MaxDelay: is the max time in ms the queue will wait for a missing packet +*/ +GF_RTPReorder *gf_rtp_reorderer_new(u32 MaxCount, u32 MaxDelay); +void gf_rtp_reorderer_del(GF_RTPReorder *po); +/*reset the Queue*/ +void gf_rtp_reorderer_reset(GF_RTPReorder *po); + +/*Adds a packet to the queue. Packet Data is memcopied*/ +GF_Err gf_rtp_reorderer_add(GF_RTPReorder *po, void *pck, u32 pck_size, u32 pck_seqnum); +/*gets the output of the queue. Packet Data IS YOURS to delete*/ +void *gf_rtp_reorderer_get(GF_RTPReorder *po, u32 *pck_size); + + +/*the RTP channel with both RTP and RTCP sockets and buffers +each channel is identified by a control string given in RTSP Describe +this control string is used with Darwin +*/ +struct __tag_rtp_channel +{ + /*global transport info for the session*/ + GF_RTSPTransport net_info; + + /*RTP CHANNEL*/ + GF_Socket *rtp; + /*RTCP CHANNEL*/ + GF_Socket *rtcp; + + /*RTP Packet reordering. Turned on/off during initialization. The library forces a 200 ms + max latency at the reordering queue*/ + GF_RTPReorder *po; + + /*RTCP report times*/ + u32 last_report_time; + u32 next_report_time; + + /*NAT keep-alive*/ + u32 last_nat_keepalive_time, nat_keepalive_time_period; + + + /*the seq number of the first packet as signaled by the server if any, or first + RTP SN received (RTP multicast)*/ + u32 rtp_first_SN; + /*the TS of the associated first packet as signaled by the server if any, or first + RTP TS received (RTP multicast)*/ + u32 rtp_time; + /*NPT from the rtp_time*/ + u32 CurrentTime; + /*num loops of pck sn*/ + u32 num_sn_loops; + /*some mapping info - we should support # payloads*/ + u8 PayloadType; + u32 TimeScale; + + /*static buffer for RTP sending*/ + char *send_buffer; + u32 send_buffer_size; + u32 pck_sent_since_last_sr; + u32 last_pck_ts; + u32 last_pck_ntp_sec, last_pck_ntp_frac; + u32 num_pck_sent, num_payload_bytes; + + /*RTCP info*/ + char *s_name, *s_email, *s_location, *s_phone, *s_tool, *s_note, *s_priv; +// s8 first_rtp_pck; + s8 first_SR; + u32 SSRC; + u32 SenderSSRC; + + u32 last_pck_sn; + + char *CName; + + u32 rtcp_bytes_sent; + /*total pck rcv*/ + u32 tot_num_pck_rcv, tot_num_pck_expected; + /*stats since last SR*/ + u32 last_num_pck_rcv, last_num_pck_expected, last_num_pck_loss; + /*jitter compute*/ + u32 Jitter, ntp_init; + s32 last_deviance; + /*NTP of last SR*/ + u32 last_SR_NTP_sec, last_SR_NTP_frac; + /*RTP time at last SR as indicated in SR*/ + u32 last_SR_rtp_time; + /*payload info*/ + u32 total_pck, total_bytes; +}; + +/*gets UTC in the channel RTP timescale*/ +u32 gf_rtp_channel_time(GF_RTPChannel *ch); +/*gets time in 1/65536 seconds (for reports)*/ +u32 gf_rtp_get_report_time(); +/*updates the time for the next report (SR, RR)*/ +void gf_rtp_get_next_report_time(GF_RTPChannel *ch); + + +/* + RTSP intern +*/ + +#define GF_RTSP_DEFAULT_BUFFER 2048 +#define GF_RTSP_VERSION "RTSP/1.0" + +/*macros for RTSP command and response formmating*/ +#define RTSP_WRITE_STEPALLOC 250 + +#define RTSP_WRITE_ALLOC_STR(buf, buf_size, pos, str) \ + if (str) { \ + if (strlen((const char *) str)+pos >= buf_size) { \ + buf_size += RTSP_WRITE_STEPALLOC; \ + buf = (char *) realloc(buf, buf_size); \ + } \ + strcpy(buf+pos, (const char *) str); \ + pos += strlen((const char *) str); \ + }\ + +#define RTSP_WRITE_HEADER(buf, buf_size, pos, type, str) \ + if (str) { \ + RTSP_WRITE_ALLOC_STR(buf, buf_size, pos, type); \ + RTSP_WRITE_ALLOC_STR(buf, buf_size, pos, ": "); \ + RTSP_WRITE_ALLOC_STR(buf, buf_size, pos, str); \ + RTSP_WRITE_ALLOC_STR(buf, buf_size, pos, "\r\n"); \ + } \ + +#define RTSP_WRITE_INT(buf, buf_size, pos, d, sig) \ + if (sig) { \ + sprintf(temp, "%d", d); \ + } else { \ + sprintf(temp, "%u", d); \ + } \ + RTSP_WRITE_ALLOC_STR(buf, buf_size, pos, temp); + +#define RTSP_WRITE_FLOAT(buf, buf_size, pos, d) \ + sprintf(temp, "%.4f", d); \ + RTSP_WRITE_ALLOC_STR(buf, buf_size, pos, temp); + +/*default packet size, but resize on the fly if needed*/ +#define RTSP_PCK_SIZE 6000 +#define RTSP_TCP_BUF_SIZE 0x10000ul + + +typedef struct +{ + u8 rtpID; + u8 rtcpID; + void *ch_ptr; +} GF_TCPChan; + +/************************************** + RTSP Session +***************************************/ +struct _tag_rtsp_session +{ + /*service name (extracted from URL) ex: news/latenight.mp4, vod.mp4 ...*/ + char *Service; + /*server name (extracted from URL)*/ + char *Server; + /*server port (extracted from URL)*/ + u16 Port; + + /*if RTSP is on UDP*/ + u8 ConnectionType; + /*TCP interleaving ID*/ + u8 InterID; + /*http tunnel*/ + Bool HasTunnel; + GF_Socket *http; + char HTTP_Cookie[30]; + u32 CookieRadLen; + + /*RTSP CHANNEL*/ + GF_Socket *connection; + u32 SockBufferSize; + /*needs connection*/ + u32 NeedConnection; + + /*the RTSP sequence number*/ + u32 CSeq; + /*this is for aggregated request in order to check SeqNum*/ + u32 NbPending; + + /*RTSP sessionID, arbitrary length, alpha-numeric*/ + const char *last_session_id; + + /*RTSP STATE machine*/ + u32 RTSP_State; + char RTSPLastRequest[40]; + + /*current buffer from TCP if any*/ + char TCPBuffer[RTSP_TCP_BUF_SIZE]; + u32 CurrentSize, CurrentPos; + + /*RTSP interleaving*/ + GF_Err (*RTSP_SignalData)(GF_RTSPSession *sess, void *chan, char *buffer, u32 bufferSize, Bool IsRTCP); + + /*buffer for pck reconstruction*/ + char *rtsp_pck_buf; + u32 rtsp_pck_size; + u32 pck_start, payloadSize; + + /*all RTP channels in an interleaved RTP on RTSP session*/ + GF_List *TCPChannels; + /*thread-safe, full duplex library for PLAY and RECORD*/ + GF_Mutex *mx; + + char *MobileIP; +}; + +GF_RTSPSession *gf_rtsp_session_new(char *sURL, u16 DefaultPort); + +/*check connection status*/ +GF_Err gf_rtsp_check_connection(GF_RTSPSession *sess); +/*send data on RTSP*/ +GF_Err gf_rtsp_send_data(GF_RTSPSession *sess, char *buffer, u32 Size); + +/* + Common RTSP tools +*/ + +/*locate body-start and body size in response/commands*/ +void gf_rtsp_get_body_info(GF_RTSPSession *sess, u32 *body_start, u32 *body_size); +/*read TCP until a full command/response is received*/ +GF_Err gf_rtsp_read_reply(GF_RTSPSession *sess); +/*fill the TCP buffer*/ +GF_Err gf_rtsp_fill_buffer(GF_RTSPSession *sess); +/*force a fill on TCP buffer - used for de-interleaving and TCP-fragmented RTSP messages*/ +GF_Err gf_rtsp_refill_buffer(GF_RTSPSession *sess); +/*parses a transport string and returns a transport structure*/ +GF_RTSPTransport *gf_rtsp_transport_parse(char *buffer); +/*parsing of header for com and rsp*/ +GF_Err gf_rtsp_parse_header(char *buffer, u32 BufferSize, u32 BodyStart, GF_RTSPCommand *com, GF_RTSPResponse *rsp); +void gf_rtsp_set_command_value(GF_RTSPCommand *com, char *Header, char *Value); +void gf_rtsp_set_response_value(GF_RTSPResponse *rsp, char *Header, char *Value); +/*deinterleave a data packet*/ +GF_Err gf_rtsp_set_deinterleave(GF_RTSPSession *sess); +/*start session through HTTP tunnel (QTSS)*/ +GF_Err gf_rtsp_http_tunnel_start(GF_RTSPSession *sess, char *UserAgent); + + +/*packetization routines*/ +GF_Err gp_rtp_builder_do_mpeg4(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize); +GF_Err gp_rtp_builder_do_h263(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize); +GF_Err gp_rtp_builder_do_amr(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize); +GF_Err gp_rtp_builder_do_mpeg12_video(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize); +GF_Err gp_rtp_builder_do_mpeg12_audio(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize); +GF_Err gp_rtp_builder_do_tx3g(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration, u8 descIndex); +GF_Err gp_rtp_builder_do_avc(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize); +GF_Err gp_rtp_builder_do_qcelp(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize); +GF_Err gp_rtp_builder_do_smv(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize); +GF_Err gp_rtp_builder_do_latm(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration); +GF_Err gp_rtp_builder_do_dims(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration); +GF_Err gp_rtp_builder_do_ac3(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize); + + +#endif /*_GF_IETF_DEV_H_*/ + diff --git a/include/gpac/internal/isomedia_dev.h b/include/gpac/internal/isomedia_dev.h new file mode 100644 index 0000000..bb38ee9 --- /dev/null +++ b/include/gpac/internal/isomedia_dev.h @@ -0,0 +1,3140 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_ISOMEDIA_DEV_H_ +#define _GF_ISOMEDIA_DEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/isomedia.h> + + +//the default size is 64, cause we need to handle large boxes... +#define GF_ISOM_BOX \ + u32 type; \ + u64 size; \ + +#define GF_ISOM_FULL_BOX \ + GF_ISOM_BOX \ + u8 version; \ + u32 flags; \ + +#define GF_ISOM_UUID_BOX \ + GF_ISOM_BOX \ + u8 uuid[16]; \ + +typedef struct +{ + GF_ISOM_BOX +} GF_Box; + +typedef struct +{ + GF_ISOM_FULL_BOX +} GF_FullBox; + +typedef struct +{ + GF_ISOM_UUID_BOX +} GF_UUIDBox; + +/*constructor*/ +GF_Box *gf_isom_box_new(u32 boxType); + +GF_Err gf_isom_box_write(GF_Box *ptr, GF_BitStream *bs); +GF_Err gf_isom_box_read(GF_Box *ptr, GF_BitStream *bs); +void gf_isom_box_del(GF_Box *ptr); +GF_Err gf_isom_box_size(GF_Box *ptr); + +GF_Err gf_isom_parse_box(GF_Box **outBox, GF_BitStream *bs); +GF_Err gf_isom_read_box_list(GF_Box *s, GF_BitStream *bs, GF_Err (*add_box)(GF_Box *par, GF_Box *b)); +GF_Err gf_isom_read_box_list_ex(GF_Box *parent, GF_BitStream *bs, GF_Err (*add_box)(GF_Box *par, GF_Box *b), u32 parent_type); + +GF_Err gf_isom_box_get_size(GF_Box *ptr); +GF_Err gf_isom_full_box_get_size(GF_Box *ptr); +GF_Err gf_isom_box_write_header(GF_Box *ptr, GF_BitStream *bs); +GF_Err gf_isom_full_box_read(GF_Box *ptr, GF_BitStream *bs); +GF_Err gf_isom_full_box_write(GF_Box *s, GF_BitStream *bs); +void gf_isom_full_box_init(GF_Box *ptr); +void gf_isom_box_array_del(GF_List *boxList); +GF_Err gf_isom_box_array_write(GF_Box *parent, GF_List *list, GF_BitStream *bs); +GF_Err gf_isom_box_array_size(GF_Box *parent, GF_List *list); + + +enum +{ + GF_ISOM_BOX_TYPE_CO64 = GF_4CC( 'c', 'o', '6', '4' ), + GF_ISOM_BOX_TYPE_STCO = GF_4CC( 's', 't', 'c', 'o' ), + GF_ISOM_BOX_TYPE_CRHD = GF_4CC( 'c', 'r', 'h', 'd' ), + GF_ISOM_BOX_TYPE_CTTS = GF_4CC( 'c', 't', 't', 's' ), + GF_ISOM_BOX_TYPE_CPRT = GF_4CC( 'c', 'p', 'r', 't' ), + GF_ISOM_BOX_TYPE_CHPL = GF_4CC( 'c', 'h', 'p', 'l' ), + GF_ISOM_BOX_TYPE_URL = GF_4CC( 'u', 'r', 'l', ' ' ), + GF_ISOM_BOX_TYPE_URN = GF_4CC( 'u', 'r', 'n', ' ' ), + GF_ISOM_BOX_TYPE_DINF = GF_4CC( 'd', 'i', 'n', 'f' ), + GF_ISOM_BOX_TYPE_DREF = GF_4CC( 'd', 'r', 'e', 'f' ), + GF_ISOM_BOX_TYPE_STDP = GF_4CC( 's', 't', 'd', 'p' ), + GF_ISOM_BOX_TYPE_EDTS = GF_4CC( 'e', 'd', 't', 's' ), + GF_ISOM_BOX_TYPE_ELST = GF_4CC( 'e', 'l', 's', 't' ), + GF_ISOM_BOX_TYPE_UUID = GF_4CC( 'u', 'u', 'i', 'd' ), + GF_ISOM_BOX_TYPE_FREE = GF_4CC( 'f', 'r', 'e', 'e' ), + GF_ISOM_BOX_TYPE_HDLR = GF_4CC( 'h', 'd', 'l', 'r' ), + GF_ISOM_BOX_TYPE_GMHD = GF_4CC( 'g', 'm', 'h', 'd' ), + GF_ISOM_BOX_TYPE_HMHD = GF_4CC( 'h', 'm', 'h', 'd' ), + GF_ISOM_BOX_TYPE_HINT = GF_4CC( 'h', 'i', 'n', 't' ), + GF_ISOM_BOX_TYPE_MDIA = GF_4CC( 'm', 'd', 'i', 'a' ), + GF_ISOM_BOX_TYPE_MDAT = GF_4CC( 'm', 'd', 'a', 't' ), + GF_ISOM_BOX_TYPE_MDHD = GF_4CC( 'm', 'd', 'h', 'd' ), + GF_ISOM_BOX_TYPE_MINF = GF_4CC( 'm', 'i', 'n', 'f' ), + GF_ISOM_BOX_TYPE_MOOV = GF_4CC( 'm', 'o', 'o', 'v' ), + GF_ISOM_BOX_TYPE_MVHD = GF_4CC( 'm', 'v', 'h', 'd' ), + GF_ISOM_BOX_TYPE_STSD = GF_4CC( 's', 't', 's', 'd' ), + GF_ISOM_BOX_TYPE_STSZ = GF_4CC( 's', 't', 's', 'z' ), + GF_ISOM_BOX_TYPE_STZ2 = GF_4CC( 's', 't', 'z', '2' ), + GF_ISOM_BOX_TYPE_STBL = GF_4CC( 's', 't', 'b', 'l' ), + GF_ISOM_BOX_TYPE_STSC = GF_4CC( 's', 't', 's', 'c' ), + GF_ISOM_BOX_TYPE_STSH = GF_4CC( 's', 't', 's', 'h' ), + GF_ISOM_BOX_TYPE_SKIP = GF_4CC( 's', 'k', 'i', 'p' ), + GF_ISOM_BOX_TYPE_SMHD = GF_4CC( 's', 'm', 'h', 'd' ), + GF_ISOM_BOX_TYPE_STSS = GF_4CC( 's', 't', 's', 's' ), + GF_ISOM_BOX_TYPE_STTS = GF_4CC( 's', 't', 't', 's' ), + GF_ISOM_BOX_TYPE_TRAK = GF_4CC( 't', 'r', 'a', 'k' ), + GF_ISOM_BOX_TYPE_TKHD = GF_4CC( 't', 'k', 'h', 'd' ), + GF_ISOM_BOX_TYPE_TREF = GF_4CC( 't', 'r', 'e', 'f' ), + GF_ISOM_BOX_TYPE_UDTA = GF_4CC( 'u', 'd', 't', 'a' ), + GF_ISOM_BOX_TYPE_VMHD = GF_4CC( 'v', 'm', 'h', 'd' ), + GF_ISOM_BOX_TYPE_FTYP = GF_4CC( 'f', 't', 'y', 'p' ), + GF_ISOM_BOX_TYPE_FADB = GF_4CC( 'p', 'a', 'd', 'b' ), + GF_ISOM_BOX_TYPE_PDIN = GF_4CC( 'p', 'd', 'i', 'n' ), + GF_ISOM_BOX_TYPE_SDTP = GF_4CC( 's', 'd', 't', 'p' ), + +#ifndef GPAC_ISOM_NO_FRAGMENTS + /*Movie Fragments*/ + GF_ISOM_BOX_TYPE_MVEX = GF_4CC( 'm', 'v', 'e', 'x' ), + GF_ISOM_BOX_TYPE_MEHD = GF_4CC( 'm', 'e', 'h', 'd' ), + GF_ISOM_BOX_TYPE_TREX = GF_4CC( 't', 'r', 'e', 'x' ), + GF_ISOM_BOX_TYPE_MOOF = GF_4CC( 'm', 'o', 'o', 'f' ), + GF_ISOM_BOX_TYPE_MFHD = GF_4CC( 'm', 'f', 'h', 'd' ), + GF_ISOM_BOX_TYPE_TRAF = GF_4CC( 't', 'r', 'a', 'f' ), + GF_ISOM_BOX_TYPE_TFHD = GF_4CC( 't', 'f', 'h', 'd' ), + GF_ISOM_BOX_TYPE_TRUN = GF_4CC( 't', 'r', 'u', 'n' ), +#endif + + /*MP4 extensions*/ + GF_ISOM_BOX_TYPE_DPND = GF_4CC( 'd', 'p', 'n', 'd' ), + GF_ISOM_BOX_TYPE_IODS = GF_4CC( 'i', 'o', 'd', 's' ), + GF_ISOM_BOX_TYPE_ESDS = GF_4CC( 'e', 's', 'd', 's' ), + GF_ISOM_BOX_TYPE_MPOD = GF_4CC( 'm', 'p', 'o', 'd' ), + GF_ISOM_BOX_TYPE_SYNC = GF_4CC( 's', 'y', 'n', 'c' ), + GF_ISOM_BOX_TYPE_IPIR = GF_4CC( 'i', 'p', 'i', 'r' ), + GF_ISOM_BOX_TYPE_SDHD = GF_4CC( 's', 'd', 'h', 'd' ), + GF_ISOM_BOX_TYPE_ODHD = GF_4CC( 'o', 'd', 'h', 'd' ), + GF_ISOM_BOX_TYPE_NMHD = GF_4CC( 'n', 'm', 'h', 'd' ), + GF_ISOM_BOX_TYPE_MP4S = GF_4CC( 'm', 'p', '4', 's' ), + GF_ISOM_BOX_TYPE_MP4A = GF_4CC( 'm', 'p', '4', 'a' ), + GF_ISOM_BOX_TYPE_MP4V = GF_4CC( 'm', 'p', '4', 'v' ), + + /*AVC / H264 extension*/ + GF_ISOM_BOX_TYPE_AVCC = GF_4CC( 'a', 'v', 'c', 'C' ), + GF_ISOM_BOX_TYPE_BTRT = GF_4CC( 'b', 't', 'r', 't' ), + GF_ISOM_BOX_TYPE_M4DS = GF_4CC( 'm', '4', 'd', 's' ), + GF_ISOM_BOX_TYPE_AVC1 = GF_4CC( 'a', 'v', 'c', '1' ), + GF_ISOM_BOX_TYPE_PASP = GF_4CC( 'p', 'a', 's', 'p' ), + + /*3GPP extensions*/ + GF_ISOM_BOX_TYPE_DAMR = GF_4CC( 'd', 'a', 'm', 'r' ), + GF_ISOM_BOX_TYPE_D263 = GF_4CC( 'd', '2', '6', '3' ), + GF_ISOM_BOX_TYPE_DEVC = GF_4CC( 'd', 'e', 'v', 'c' ), + GF_ISOM_BOX_TYPE_DQCP = GF_4CC( 'd', 'q', 'c', 'p' ), + GF_ISOM_BOX_TYPE_DSMV = GF_4CC( 'd', 's', 'm', 'v' ), + GF_ISOM_BOX_TYPE_TSEL = GF_4CC( 't', 's', 'e', 'l' ), + + /*3GPP text / MPEG-4 StreamingText*/ + GF_ISOM_BOX_TYPE_FTAB = GF_4CC( 'f', 't', 'a', 'b' ), + GF_ISOM_BOX_TYPE_TX3G = GF_4CC( 't', 'x', '3', 'g' ), + GF_ISOM_BOX_TYPE_STYL = GF_4CC( 's', 't', 'y', 'l' ), + GF_ISOM_BOX_TYPE_HLIT = GF_4CC( 'h', 'l', 'i', 't' ), + GF_ISOM_BOX_TYPE_HCLR = GF_4CC( 'h', 'c', 'l', 'r' ), + GF_ISOM_BOX_TYPE_KROK = GF_4CC( 'k', 'r', 'o', 'k' ), + GF_ISOM_BOX_TYPE_DLAY = GF_4CC( 'd', 'l', 'a', 'y' ), + GF_ISOM_BOX_TYPE_HREF = GF_4CC( 'h', 'r', 'e', 'f' ), + GF_ISOM_BOX_TYPE_TBOX = GF_4CC( 't', 'b', 'o', 'x' ), + GF_ISOM_BOX_TYPE_BLNK = GF_4CC( 'b', 'l', 'n', 'k' ), + GF_ISOM_BOX_TYPE_TWRP = GF_4CC( 't', 'w', 'r', 'p' ), + + /* ISO Base Media File Format Extensions for MPEG-21 */ + GF_ISOM_BOX_TYPE_META = GF_4CC( 'm', 'e', 't', 'a' ), + GF_ISOM_BOX_TYPE_XML = GF_4CC( 'x', 'm', 'l', ' ' ), + GF_ISOM_BOX_TYPE_BXML = GF_4CC( 'b', 'x', 'm', 'l' ), + GF_ISOM_BOX_TYPE_ILOC = GF_4CC( 'i', 'l', 'o', 'c' ), + GF_ISOM_BOX_TYPE_PITM = GF_4CC( 'p', 'i', 't', 'm' ), + GF_ISOM_BOX_TYPE_IPRO = GF_4CC( 'i', 'p', 'r', 'o' ), + GF_ISOM_BOX_TYPE_INFE = GF_4CC( 'i', 'n', 'f', 'e' ), + GF_ISOM_BOX_TYPE_IINF = GF_4CC( 'i', 'i', 'n', 'f' ), + GF_ISOM_BOX_TYPE_ENCA = GF_4CC( 'e', 'n', 'c', 'a' ), + GF_ISOM_BOX_TYPE_ENCV = GF_4CC( 'e', 'n', 'c', 'v' ), + GF_ISOM_BOX_TYPE_ENCT = GF_4CC( 'e', 'n', 'c', 't' ), + GF_ISOM_BOX_TYPE_ENCS = GF_4CC( 'e', 'n', 'c', 's' ), + GF_ISOM_BOX_TYPE_SINF = GF_4CC( 's', 'i', 'n', 'f' ), + GF_ISOM_BOX_TYPE_FRMA = GF_4CC( 'f', 'r', 'm', 'a' ), + GF_ISOM_BOX_TYPE_SCHM = GF_4CC( 's', 'c', 'h', 'm' ), + GF_ISOM_BOX_TYPE_SCHI = GF_4CC( 's', 'c', 'h', 'i' ), + + GF_ISOM_BOX_TYPE_METX = GF_4CC( 'm', 'e', 't', 'x' ), + GF_ISOM_BOX_TYPE_METT = GF_4CC( 'm', 'e', 't', 't' ), + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + GF_ISOM_BOX_TYPE_IKMS = GF_4CC( 'i', 'K', 'M', 'S' ), + GF_ISOM_BOX_TYPE_ISFM = GF_4CC( 'i', 'S', 'F', 'M' ), + + /* Hinting boxes */ + GF_ISOM_BOX_TYPE_RTP_STSD = GF_4CC( 'r', 't', 'p', ' ' ), + GF_ISOM_BOX_TYPE_HNTI = GF_4CC( 'h', 'n', 't', 'i' ), + GF_ISOM_BOX_TYPE_RTP = GF_4CC( 'r', 't', 'p', ' ' ), + GF_ISOM_BOX_TYPE_SDP = GF_4CC( 's', 'd', 'p', ' ' ), + GF_ISOM_BOX_TYPE_HINF = GF_4CC( 'h', 'i', 'n', 'f' ), + GF_ISOM_BOX_TYPE_NAME = GF_4CC( 'n', 'a', 'm', 'e' ), + GF_ISOM_BOX_TYPE_TRPY = GF_4CC( 't', 'r', 'p', 'y' ), + GF_ISOM_BOX_TYPE_NUMP = GF_4CC( 'n', 'u', 'm', 'p' ), + GF_ISOM_BOX_TYPE_TOTL = GF_4CC( 't', 'o', 't', 'l' ), + GF_ISOM_BOX_TYPE_NPCK = GF_4CC( 'n', 'p', 'c', 'k' ), + GF_ISOM_BOX_TYPE_TPYL = GF_4CC( 't', 'p', 'y', 'l' ), + GF_ISOM_BOX_TYPE_TPAY = GF_4CC( 't', 'p', 'a', 'y' ), + GF_ISOM_BOX_TYPE_MAXR = GF_4CC( 'm', 'a', 'x', 'r' ), + GF_ISOM_BOX_TYPE_DMED = GF_4CC( 'd', 'm', 'e', 'd' ), + GF_ISOM_BOX_TYPE_DIMM = GF_4CC( 'd', 'i', 'm', 'm' ), + GF_ISOM_BOX_TYPE_DREP = GF_4CC( 'd', 'r', 'e', 'p' ), + GF_ISOM_BOX_TYPE_TMIN = GF_4CC( 't', 'm', 'i', 'n' ), + GF_ISOM_BOX_TYPE_TMAX = GF_4CC( 't', 'm', 'a', 'x' ), + GF_ISOM_BOX_TYPE_PMAX = GF_4CC( 'p', 'm', 'a', 'x' ), + GF_ISOM_BOX_TYPE_DMAX = GF_4CC( 'd', 'm', 'a', 'x' ), + GF_ISOM_BOX_TYPE_PAYT = GF_4CC( 'p', 'a', 'y', 't' ), + GF_ISOM_BOX_TYPE_RELY = GF_4CC( 'r', 'e', 'l', 'y' ), + GF_ISOM_BOX_TYPE_TIMS = GF_4CC( 't', 'i', 'm', 's' ), + GF_ISOM_BOX_TYPE_TSRO = GF_4CC( 't', 's', 'r', 'o' ), + GF_ISOM_BOX_TYPE_SNRO = GF_4CC( 's', 'n', 'r', 'o' ), + GF_ISOM_BOX_TYPE_RTPO = GF_4CC( 'r', 't', 'p', 'o' ), + + /*internal type for track references*/ + GF_ISOM_BOX_TYPE_REFT = GF_4CC( 'R', 'E', 'F', 'T' ), + + /* Apple extensions */ + + GF_ISOM_BOX_TYPE_ILST = GF_4CC( 'i', 'l', 's', 't' ), + GF_ISOM_BOX_TYPE_0xA9NAM = GF_4CC( 0xA9, 'n', 'a', 'm' ), + GF_ISOM_BOX_TYPE_0xA9CMT = GF_4CC( 0xA9, 'c', 'm', 't' ), + GF_ISOM_BOX_TYPE_0xA9DAY = GF_4CC( 0xA9, 'd', 'a', 'y' ), + GF_ISOM_BOX_TYPE_0xA9ART = GF_4CC( 0xA9, 'A', 'R', 'T' ), + GF_ISOM_BOX_TYPE_0xA9TRK = GF_4CC( 0xA9, 't', 'r', 'k' ), + GF_ISOM_BOX_TYPE_0xA9ALB = GF_4CC( 0xA9, 'a', 'l', 'b' ), + GF_ISOM_BOX_TYPE_0xA9COM = GF_4CC( 0xA9, 'c', 'o', 'm' ), + GF_ISOM_BOX_TYPE_0xA9WRT = GF_4CC( 0xA9, 'w', 'r', 't' ), + GF_ISOM_BOX_TYPE_0xA9TOO = GF_4CC( 0xA9, 't', 'o', 'o' ), + GF_ISOM_BOX_TYPE_0xA9CPY = GF_4CC( 0xA9, 'c', 'p', 'y' ), + GF_ISOM_BOX_TYPE_0xA9DES = GF_4CC( 0xA9, 'd', 'e', 's' ), + GF_ISOM_BOX_TYPE_0xA9GEN = GF_4CC( 0xA9, 'g', 'e', 'n' ), + GF_ISOM_BOX_TYPE_0xA9GRP = GF_4CC( 0xA9, 'g', 'r', 'p' ), + GF_ISOM_BOX_TYPE_0xA9ENC = GF_4CC( 0xA9, 'e', 'n', 'c' ), + GF_ISOM_BOX_TYPE_aART = GF_4CC( 'a', 'A', 'R', 'T' ), + GF_ISOM_BOX_TYPE_PGAP = GF_4CC( 'p', 'g', 'a', 'p' ), + GF_ISOM_BOX_TYPE_GNRE = GF_4CC( 'g', 'n', 'r', 'e' ), + GF_ISOM_BOX_TYPE_DISK = GF_4CC( 'd', 'i', 's', 'k' ), + GF_ISOM_BOX_TYPE_TRKN = GF_4CC( 't', 'r', 'k', 'n' ), + GF_ISOM_BOX_TYPE_TMPO = GF_4CC( 't', 'm', 'p', 'o' ), + GF_ISOM_BOX_TYPE_CPIL = GF_4CC( 'c', 'p', 'i', 'l' ), + GF_ISOM_BOX_TYPE_COVR = GF_4CC( 'c', 'o', 'v', 'r' ), + GF_ISOM_BOX_TYPE_iTunesSpecificInfo = GF_4CC( '-', '-', '-', '-' ), + GF_ISOM_BOX_TYPE_DATA = GF_4CC( 'd', 'a', 't', 'a' ), + + GF_ISOM_HANDLER_TYPE_MDIR = GF_4CC( 'm', 'd', 'i', 'r' ), + GF_ISOM_BOX_TYPE_CHAP = GF_4CC( 'c', 'h', 'a', 'p' ), + + /*OMA (P)DCF boxes*/ + GF_ISOM_BOX_TYPE_OHDR = GF_4CC( 'o', 'h', 'd', 'r' ), + GF_ISOM_BOX_TYPE_GRPI = GF_4CC( 'g', 'r', 'p', 'i' ), + GF_ISOM_BOX_TYPE_MDRI = GF_4CC( 'm', 'd', 'r', 'i' ), + GF_ISOM_BOX_TYPE_ODTT = GF_4CC( 'o', 'd', 't', 't' ), + GF_ISOM_BOX_TYPE_ODRB = GF_4CC( 'o', 'd', 'r', 'b' ), + GF_ISOM_BOX_TYPE_ODKM = GF_4CC( 'o', 'd', 'k', 'm' ), + GF_ISOM_BOX_TYPE_ODAF = GF_4CC( 'o', 'd', 'a', 'f' ), + + /*3GPP DIMS */ + GF_ISOM_BOX_TYPE_DIMS = GF_4CC( 'd', 'i', 'm', 's' ), + GF_ISOM_BOX_TYPE_DIMC = GF_4CC( 'd', 'i', 'm', 'C' ), + GF_ISOM_BOX_TYPE_DIST = GF_4CC( 'd', 'i', 'S', 'T' ), + + + GF_ISOM_BOX_TYPE_AC3 = GF_4CC( 'a', 'c', '-', '3' ), + GF_ISOM_BOX_TYPE_DAC3 = GF_4CC( 'd', 'a', 'c', '3' ), + + /*ALL INTERNAL BOXES - NEVER WRITTEN TO FILE!!*/ + + /*generic handlers*/ + GF_ISOM_BOX_TYPE_GNRM = GF_4CC( 'g', 'n', 'r', 'm' ), + GF_ISOM_BOX_TYPE_GNRV = GF_4CC( 'g', 'n', 'r', 'v' ), + GF_ISOM_BOX_TYPE_GNRA = GF_4CC( 'g', 'n', 'r', 'a' ), + /*storage of AU fragments (for MPEG-4 visual resync marker (video packets), located in stbl.*/ + GF_ISOM_BOX_TYPE_STSF = GF_4CC( 'S', 'T', 'S', 'F' ), + /*base constructor of all hint formats (currently only RTP uses it)*/ + GF_ISOM_BOX_TYPE_GHNT = GF_4CC( 'g', 'h', 'n', 't' ), + /*for compatibility with old files hinted for DSS - needs special parsing*/ + GF_ISOM_BOX_TYPE_VOID = GF_4CC( 'V', 'O', 'I', 'D' ), +}; + + +typedef struct +{ + GF_ISOM_BOX + /*note: the data is NEVER loaded to the mdat in this lib*/ + u64 dataSize; + char *data; +} GF_MediaDataBox; + +typedef struct +{ + GF_ISOM_BOX + char *data; + u32 dataSize; +} GF_UnknownBox; + +typedef struct +{ + GF_ISOM_UUID_BOX + char *data; + u32 dataSize; +} GF_UnknownUUIDBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 creationTime; + u64 modificationTime; + u32 timeScale; + u64 duration; + u32 nextTrackID; + u32 preferredRate; + u16 preferredVolume; + char reserved[10]; + u32 matrixA; + u32 matrixB; + u32 matrixU; + u32 matrixC; + u32 matrixD; + u32 matrixV; + u32 matrixW; + u32 matrixX; + u32 matrixY; + u32 previewTime; + u32 previewDuration; + u32 posterTime; + u32 selectionTime; + u32 selectionDuration; + u32 currentTime; +} GF_MovieHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_Descriptor *descriptor; +} GF_ObjectDescriptorBox; + +/*used for entry list*/ +typedef struct +{ + u64 segmentDuration; + s64 mediaTime; + u32 mediaRate; +} GF_EdtsEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *entryList; +} GF_EditListBox; + +typedef struct +{ + GF_ISOM_BOX + GF_EditListBox *editList; +} GF_EditBox; + + +/*used to classify boxes in the UserData GF_Box*/ +typedef struct +{ + u32 boxType; + u8 uuid[16]; + GF_List *boxList; +} GF_UserDataMap; + +typedef struct +{ + GF_ISOM_BOX + GF_List *recordList; +} GF_UserDataBox; + +typedef struct +{ + GF_ISOM_BOX + GF_MovieHeaderBox *mvhd; + GF_ObjectDescriptorBox *iods; + GF_UserDataBox *udta; +#ifndef GPAC_ISOM_NO_FRAGMENTS + struct __tag_mvex_box *mvex; +#endif + /*meta box if any*/ + struct __tag_meta_box *meta; + /*track boxes*/ + GF_List *trackList; + /*other boxes*/ + GF_List *boxes; + + GF_ISOFile *mov; + +} GF_MovieBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 creationTime; + u64 modificationTime; + u32 trackID; + u32 reserved1; + u64 duration; + u32 reserved2[2]; + u16 layer; + u16 alternate_group; + u16 volume; + u16 reserved3; + u32 matrix[9]; + u32 width, height; +} GF_TrackHeaderBox; + +typedef struct +{ + GF_ISOM_BOX + GF_List *boxList; +} GF_TrackReferenceBox; + + + +typedef struct +{ + GF_ISOM_BOX + GF_UserDataBox *udta; + GF_TrackHeaderBox *Header; + struct __tag_media_box *Media; + GF_EditBox *editBox; + GF_TrackReferenceBox *References; + /*meta box if any*/ + struct __tag_meta_box *meta; + /*other*/ + GF_List *boxes; + + GF_MovieBox *moov; + /*private for media padding*/ + u32 padding_bytes; + /*private for editing*/ + char *name; + /*private for editing*/ + Bool is_unpacked; +} GF_TrackBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 creationTime; + u64 modificationTime; + u32 timeScale; + u64 duration; + char packedLanguage[4]; + u16 reserved; +} GF_MediaHeaderBox; + + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 reserved1; + u32 handlerType; + u8 reserved2[12]; + char *nameUTF8; +} GF_HandlerBox; + +typedef struct __tag_media_box +{ + GF_ISOM_BOX + GF_TrackBox *mediaTrack; + GF_MediaHeaderBox *mediaHeader; + GF_HandlerBox *handler; + struct __tag_media_info_box *information; + u64 BytesMissing; +} GF_MediaBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 reserved; +} GF_VideoMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 reserved; +} GF_SoundMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + /*this is used for us INTERNALLY*/ + u32 subType; + u32 maxPDUSize; + u32 avgPDUSize; + u32 maxBitrate; + u32 avgBitrate; + u32 slidingAverageBitrate; +} GF_HintMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX +} GF_MPEGMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX +} GF_ODMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX +} GF_OCRMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX +} GF_SceneMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *boxList; +} GF_DataReferenceBox; + +typedef struct +{ + GF_ISOM_BOX + GF_DataReferenceBox *dref; +} GF_DataInformationBox; + +#define GF_ISOM_DATAENTRY_FIELDS \ + char *location; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_ISOM_DATAENTRY_FIELDS +} GF_DataEntryBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_ISOM_DATAENTRY_FIELDS +} GF_DataEntryURLBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_ISOM_DATAENTRY_FIELDS + char *nameURN; +} GF_DataEntryURNBox; + +typedef struct +{ + u32 sampleCount; + u32 sampleDelta; +} GF_SttsEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_SttsEntry *entries; + u32 nb_entries, alloc_size; + +#ifndef GPAC_READ_ONLY + /*cache for WRITE*/ + u32 w_currentSampleNum; + u64 w_LastDTS; +#endif + /*cache for READ*/ + u32 r_FirstSampleInEntry; + u32 r_currentEntryIndex; + u64 r_CurrentDTS; +} GF_TimeToSampleBox; + + +/*TO CHECK - it could be reasonnable to only use 16bits for both count and offset*/ +typedef struct +{ + u32 sampleCount; + u32 decodingOffset; +} GF_DttsEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_DttsEntry *entries; + u32 nb_entries, alloc_size; + +#ifndef GPAC_READ_ONLY + u32 w_LastSampleNumber; + /*force one sample per entry*/ + Bool unpack_mode; +#endif + /*Cache for read*/ + u32 r_currentEntryIndex; + u32 r_FirstSampleInEntry; +} GF_CompositionOffsetBox; + + +typedef struct +{ + u32 SampleNumber; + u32 fragmentCount; + u16 *fragmentSizes; +} GF_StsfEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *entryList; +#ifndef GPAC_READ_ONLY + /*Cache for write*/ + GF_StsfEntry *w_currentEntry; + u32 w_currentEntryIndex; +#endif + /*Cache for read*/ + u32 r_currentEntryIndex; + GF_StsfEntry *r_currentEntry; +} GF_SampleFragmentBox; + + +#define GF_ISOM_SAMPLE_ENTRY_FIELDS \ + GF_ISOM_UUID_BOX \ + u16 dataReferenceIndex; \ + char reserved[ 6 ]; \ + struct __tag_protect_box *protection_info; + +/*base sample entry box (never used but for typecasting)*/ +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS +} GF_SampleEntryBox; + +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + /*box type as specified in the file (not this box's type!!)*/ + u32 EntryType; + + char *data; + u32 data_size; +} GF_GenericSampleEntryBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_ESD *desc; +} GF_ESDBox; + +/*for all MPEG4 media except audio and video*/ +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + GF_ESDBox *esd; + /*used for hinting when extracting the OD stream...*/ + GF_SLConfig *slc; +} GF_MPEGSampleEntryBox; + +typedef struct +{ + GF_ISOM_BOX + u32 hSpacing; + u32 vSpacing; +} GF_PixelAspectRatioBox; + + +#define GF_ISOM_VISUAL_SAMPLE_ENTRY \ + GF_ISOM_SAMPLE_ENTRY_FIELDS \ + u16 version; \ + u16 revision; \ + u32 vendor; \ + u32 temporal_quality; \ + u32 spacial_quality; \ + u16 Width, Height; \ + u32 horiz_res, vert_res; \ + u32 entry_data_size; \ + u16 frames_per_sample; \ + char compressor_name[33]; \ + u16 bit_depth; \ + s16 color_table_index; \ + GF_PixelAspectRatioBox *pasp; + +typedef struct +{ + GF_ISOM_VISUAL_SAMPLE_ENTRY +} GF_VisualSampleEntryBox; + +void gf_isom_video_sample_entry_init(GF_VisualSampleEntryBox *ent); +GF_Err gf_isom_video_sample_entry_read(GF_VisualSampleEntryBox *ptr, GF_BitStream *bs); +#ifndef GPAC_READ_ONLY +void gf_isom_video_sample_entry_write(GF_VisualSampleEntryBox *ent, GF_BitStream *bs); +void gf_isom_video_sample_entry_size(GF_VisualSampleEntryBox *ent); +#endif + +typedef struct +{ + GF_ISOM_BOX + u32 bufferSizeDB; + u32 maxBitrate; + u32 avgBitrate; +} GF_MPEG4BitRateBox; + +typedef struct +{ + GF_ISOM_BOX + GF_List *descriptors; +} GF_MPEG4ExtensionDescriptorsBox; + +typedef struct +{ + GF_ISOM_BOX + GF_AVCConfig *config; +} GF_AVCConfigurationBox; + +typedef struct +{ + GF_ISOM_VISUAL_SAMPLE_ENTRY + GF_ESDBox *esd; + /*used for Publishing*/ + GF_SLConfig *slc; + + /*avc extensions - we merged with regular 'mp4v' box to handle isma E&A signaling of AVC*/ + GF_AVCConfigurationBox *avc_config; + GF_MPEG4BitRateBox *bitrate; + /*ext descriptors*/ + GF_MPEG4ExtensionDescriptorsBox *descr; + /*internally emulated esd*/ + GF_ESD *emul_esd; + /*iPod's hack*/ + GF_UnknownUUIDBox *ipod_ext; + +} GF_MPEGVisualSampleEntryBox; + + +/*this is the default visual sdst (to handle unknown media)*/ +typedef struct +{ + GF_ISOM_VISUAL_SAMPLE_ENTRY + /*box type as specified in the file (not this box's type!!)*/ + u32 EntryType; + /*opaque description data (ESDS in MP4, SMI in SVQ3, ...)*/ + char *data; + u32 data_size; +} GF_GenericVisualSampleEntryBox; + + +#define GF_ISOM_AUDIO_SAMPLE_ENTRY \ + GF_ISOM_SAMPLE_ENTRY_FIELDS \ + u16 version; \ + u16 revision; \ + u32 vendor; \ + u16 channel_count; \ + u16 bitspersample; \ + u16 compression_id; \ + u16 packet_size; \ + u16 samplerate_hi; \ + u16 samplerate_lo; + + +typedef struct +{ + GF_ISOM_AUDIO_SAMPLE_ENTRY +} GF_AudioSampleEntryBox; + +void gf_isom_audio_sample_entry_init(GF_AudioSampleEntryBox *ptr); +GF_Err gf_isom_audio_sample_entry_read(GF_AudioSampleEntryBox *ptr, GF_BitStream *bs); +#ifndef GPAC_READ_ONLY +void gf_isom_audio_sample_entry_write(GF_AudioSampleEntryBox *ptr, GF_BitStream *bs); +void gf_isom_audio_sample_entry_size(GF_AudioSampleEntryBox *ptr); +#endif + + +typedef struct +{ + GF_ISOM_AUDIO_SAMPLE_ENTRY + GF_ESDBox *esd; + GF_SLConfig *slc; +} GF_MPEGAudioSampleEntryBox; + +typedef struct +{ + GF_ISOM_BOX + GF_3GPConfig cfg; +} GF_3GPPConfigBox; + +typedef struct +{ + GF_ISOM_AUDIO_SAMPLE_ENTRY + GF_3GPPConfigBox *info; +} GF_3GPPAudioSampleEntryBox; + +typedef struct +{ + GF_ISOM_VISUAL_SAMPLE_ENTRY + GF_3GPPConfigBox *info; +} GF_3GPPVisualSampleEntryBox; + +/*this is the default visual sdst (to handle unknown media)*/ +typedef struct +{ + GF_ISOM_AUDIO_SAMPLE_ENTRY + /*box type as specified in the file (not this box's type!!)*/ + u32 EntryType; + /*opaque description data (ESDS in MP4, ...)*/ + char *data; + u32 data_size; +} GF_GenericAudioSampleEntryBox; + +typedef struct +{ + GF_ISOM_BOX + GF_AC3Config cfg; +} GF_AC3ConfigBox; + +typedef struct +{ + GF_ISOM_AUDIO_SAMPLE_ENTRY + GF_AC3ConfigBox *info; +} GF_AC3SampleEntryBox; + + + + +typedef struct +{ + GF_ISOM_FULL_BOX + u8 profile; + u8 level; + u8 pathComponents; + Bool fullRequestHost; + Bool streamType; + u8 containsRedundant; + char *textEncoding; + char *contentEncoding; +} GF_DIMSSceneConfigBox; + +typedef struct +{ + GF_ISOM_BOX + char *content_script_types; +} GF_DIMSScriptTypesBox; + +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + GF_DIMSSceneConfigBox *config; + GF_MPEG4BitRateBox *bitrate; + GF_DIMSScriptTypesBox *scripts; +} GF_DIMSSampleEntryBox; + + +/*base sample entry box (never used but for typecasting)*/ +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + char *content_encoding; //optional + char *mime_type_or_namespace; //not optional + char *xml_schema_loc; // optional + GF_MPEG4BitRateBox *bitrate; // optional +} GF_MetaDataSampleEntryBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *boxList; +} GF_SampleDescriptionBox; + + +typedef struct +{ + GF_ISOM_FULL_BOX + /*if this is the compact version, sample size is actually fieldSize*/ + u32 sampleSize; + u32 sampleCount; + u32 alloc_size; + u32 *sizes; +} GF_SampleSizeBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 nb_entries; + u32 alloc_size; + u32 *offsets; +} GF_ChunkOffsetBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 nb_entries; + u32 alloc_size; + u64 *offsets; +} GF_ChunkLargeOffsetBox; + +typedef struct +{ + u32 firstChunk; + u32 nextChunk; + u32 samplesPerChunk; + u32 sampleDescriptionIndex; + u8 isEdited; +} GF_StscEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_StscEntry *entries; + u32 alloc_size, nb_entries; + + /*0-based cache for READ. In WRITE mode, we always have 1 sample per chunk so no need for a cache*/ + u32 currentIndex; + /*first sample number in this chunk*/ + u32 firstSampleInCurrentChunk; + u32 currentChunk; + u32 ghostNumber; +} GF_SampleToChunkBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 alloc_size, nb_entries; + u32 *sampleNumbers; + /*cache for READ mode (in write we realloc no matter what)*/ + u32 r_LastSyncSample; + /*0-based index in the array*/ + u32 r_LastSampleIndex; +} GF_SyncSampleBox; + +typedef struct +{ + u32 shadowedSampleNumber; + s32 syncSampleNumber; +} GF_StshEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *entries; + /*Cache for read mode*/ + u32 r_LastEntryIndex; + u32 r_LastFoundSample; +} GF_ShadowSyncBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 nb_entries; + u16 *priorities; +} GF_DegradationPriorityBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 SampleCount; + u8 *padbits; +} GF_PaddingBitsBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 sampleCount; + /*each dep type is packed on 1 byte*/ + u8 *sample_info; +} GF_SampleDependencyTypeBox; + + +typedef struct +{ + GF_ISOM_BOX + GF_TimeToSampleBox *TimeToSample; + GF_CompositionOffsetBox *CompositionOffset; + GF_SyncSampleBox *SyncSample; + GF_SampleDescriptionBox *SampleDescription; + GF_SampleSizeBox *SampleSize; + GF_SampleToChunkBox *SampleToChunk; + /*untyped, to handle 32 bits and 64 bits chunkOffsets*/ + GF_Box *ChunkOffset; + GF_ShadowSyncBox *ShadowSync; + GF_DegradationPriorityBox *DegradationPriority; + GF_PaddingBitsBox *PaddingBits; + GF_SampleDependencyTypeBox *SampleDep; + GF_SampleFragmentBox *Fragments; + + u32 MaxSamplePerChunk; + u16 groupID; + u16 trackPriority; + u32 currentEntryIndex; +} GF_SampleTableBox; + +typedef struct __tag_media_info_box +{ + GF_ISOM_BOX + GF_DataInformationBox *dataInformation; + GF_SampleTableBox *sampleTable; + GF_Box *InfoHeader; + struct __tag_data_map *dataHandler; + u32 dataEntryIndex; + GF_List *boxes; +} GF_MediaInformationBox; + + +typedef struct +{ + GF_ISOM_BOX + char *data; + u32 dataSize; +} GF_FreeSpaceBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + char packedLanguageCode[4]; + char *notice; +} GF_CopyrightBox; + + +typedef struct +{ + char *name; + u64 start_time; +} GF_ChapterEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *list; +} GF_ChapterListBox; + +typedef struct +{ + GF_ISOM_BOX + u32 reference_type; + u32 trackIDCount; + u32 *trackIDs; +} GF_TrackReferenceTypeBox; + +typedef struct +{ + GF_ISOM_BOX + u32 majorBrand; + u32 minorVersion; + u32 altCount; + u32 *altBrand; +} GF_FileTypeBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 *rates; + u32 *times; + u32 count; +} GF_ProgressiveDownloadBox; + + +/* + 3GPP streaming text boxes +*/ + +typedef struct +{ + GF_ISOM_BOX + u32 entry_count; + GF_FontRecord *fonts; +} GF_FontTableBox; + +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS \ + u32 displayFlags; + s8 horizontal_justification; + s8 vertical_justification; + /*ARGB*/ + u32 back_color; + GF_BoxRecord default_box; + GF_StyleRecord default_style; + GF_FontTableBox *font_table; +} GF_TextSampleEntryBox; + +typedef struct +{ + GF_ISOM_BOX + u32 entry_count; + GF_StyleRecord *styles; +} GF_TextStyleBox; + +typedef struct +{ + GF_ISOM_BOX + u16 startcharoffset; + u16 endcharoffset; +} GF_TextHighlightBox; + +typedef struct +{ + GF_ISOM_BOX + /*ARGB*/ + u32 hil_color; +} GF_TextHighlightColorBox; + +typedef struct +{ + u32 highlight_endtime; + u16 start_charoffset; + u16 end_charoffset; +} KaraokeRecord; + +typedef struct +{ + GF_ISOM_BOX + u32 highlight_starttime; + u16 nb_entries; + KaraokeRecord *records; +} GF_TextKaraokeBox; + +typedef struct +{ + GF_ISOM_BOX + u32 scroll_delay; +} GF_TextScrollDelayBox; + +typedef struct +{ + GF_ISOM_BOX + u16 startcharoffset; + u16 endcharoffset; + char *URL; + char *URL_hint; +} GF_TextHyperTextBox; + +typedef struct +{ + GF_ISOM_BOX + GF_BoxRecord box; +} GF_TextBoxBox; + +typedef struct +{ + GF_ISOM_BOX + u16 startcharoffset; + u16 endcharoffset; +} GF_TextBlinkBox; + +typedef struct +{ + GF_ISOM_BOX + u8 wrap_flag; +} GF_TextWrapBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 switchGroup; + u32 *attributeList; + u32 attributeListCount; +} GF_TrackSelectionBox; + +/* + MPEG-21 extensions +*/ +typedef struct +{ + GF_ISOM_FULL_BOX + u32 xml_length; + char *xml; +} GF_XMLBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 data_length; + char *data; +} GF_BinaryXMLBox; + +typedef struct +{ + u64 extent_offset; + u64 extent_length; +#ifndef GPAC_READ_OLNLY + /*for storage only*/ + u64 original_extent_offset; +#endif +} GF_ItemExtentEntry; + +typedef struct +{ + u16 item_ID; + u16 data_reference_index; + u64 base_offset; +#ifndef GPAC_READ_OLNLY + /*for storage only*/ + u64 original_base_offset; +#endif + GF_List *extent_entries; +} GF_ItemLocationEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + u8 offset_size; + u8 length_size; + u8 base_offset_size; + GF_List *location_entries; +} GF_ItemLocationBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u16 item_ID; +} GF_PrimaryItemBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *protection_information; +} GF_ItemProtectionBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u16 item_ID; + u16 item_protection_index; + /*zero-terminated strings*/ + char *item_name; + char *content_type; + char *content_encoding; + // needed to actually read the resource file, but not written in the MP21 file. + char *full_path; +} GF_ItemInfoEntryBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *item_infos; +} GF_ItemInfoBox; + +typedef struct +{ + GF_ISOM_BOX + u32 data_format; +} GF_OriginalFormatBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 scheme_type; + u32 scheme_version; + char *URI; +} GF_SchemeTypeBox; + +/*ISMACryp specific*/ +typedef struct +{ + GF_ISOM_FULL_BOX + /*zero-terminated string*/ + char *URI; +} GF_ISMAKMSBox; + +/*ISMACryp specific*/ +typedef struct __isma_format_box +{ + GF_ISOM_FULL_BOX + u8 selective_encryption; + u8 key_indicator_length; + u8 IV_length; +} GF_ISMASampleFormatBox; + +typedef struct +{ + GF_ISOM_BOX + GF_ISMAKMSBox *ikms; + GF_ISMASampleFormatBox *isfm; + struct __oma_kms_box *okms; +} GF_SchemeInformationBox; + +typedef struct __tag_protect_box +{ + GF_ISOM_BOX + GF_OriginalFormatBox *original_format; + GF_SchemeTypeBox *scheme_type; + GF_SchemeInformationBox *info; +} GF_ProtectionInfoBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *descriptors; +} GF_IPMPInfoBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_IPMP_ToolList *ipmp_tools; + GF_List *descriptors; +} GF_IPMPControlBox; + + +typedef struct __tag_meta_box +{ + GF_ISOM_FULL_BOX + GF_HandlerBox *handler; + GF_PrimaryItemBox *primary_resource; + GF_DataInformationBox *file_locations; + GF_ItemLocationBox *item_locations; + GF_ItemProtectionBox *protections; + GF_ItemInfoBox *item_infos; + GF_IPMPControlBox *IPMP_control; + GF_List *other_boxes; +} GF_MetaBox; + + + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +/*V2 boxes - Movie Fragments*/ + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 fragment_duration; +} GF_MovieExtendsHeaderBox; + + +typedef struct __tag_mvex_box +{ + GF_ISOM_BOX + GF_List *TrackExList; + GF_MovieExtendsHeaderBox *mehd; + GF_ISOFile *mov; +} GF_MovieExtendsBox; + +/*the TrackExtends contains default values for the track fragments*/ +typedef struct +{ + GF_ISOM_FULL_BOX + u32 trackID; + u32 def_sample_desc_index; + u32 def_sample_duration; + u32 def_sample_size; + u32 def_sample_flags; + GF_TrackBox *track; +} GF_TrackExtendsBox; + +/*indicates the seq num of this fragment*/ +typedef struct +{ + GF_ISOM_FULL_BOX + u32 sequence_number; +} GF_MovieFragmentHeaderBox; + +/*MovieFragment is a container IN THE FILE, contains 1 fragment*/ +typedef struct +{ + GF_ISOM_BOX + GF_MovieFragmentHeaderBox *mfhd; + GF_List *TrackList; + GF_ISOFile *mov; +} GF_MovieFragmentBox; + + +/*FLAGS for TRAF*/ +enum +{ + GF_ISOM_TRAF_BASE_OFFSET = 0x01, + GF_ISOM_TRAF_SAMPLE_DESC = 0x02, + GF_ISOM_TRAF_SAMPLE_DUR = 0x08, + GF_ISOM_TRAF_SAMPLE_SIZE = 0x10, + GF_ISOM_TRAF_SAMPLE_FLAGS = 0x20, + GF_ISOM_TRAF_DUR_EMPTY = 0x10000 +}; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 trackID; + /* all the following are optional fields */ + u64 base_data_offset; + u32 sample_desc_index; + u32 def_sample_duration; + u32 def_sample_size; + u32 def_sample_flags; + u32 EmptyDuration; + u8 IFrameSwitching; +} GF_TrackFragmentHeaderBox; + +typedef struct +{ + GF_ISOM_BOX + GF_TrackFragmentHeaderBox *tfhd; + GF_List *TrackRuns; + /*keep a pointer to default flags*/ + GF_TrackExtendsBox *trex; + /*when data caching is on*/ + u32 DataCache; +} GF_TrackFragmentBox; + +/*FLAGS for TRUN : specify what is written in the SampleTable of TRUN*/ +enum +{ + GF_ISOM_TRUN_DATA_OFFSET = 0x01, + GF_ISOM_TRUN_FIRST_FLAG = 0x04, + GF_ISOM_TRUN_DURATION = 0x100, + GF_ISOM_TRUN_SIZE = 0x200, + GF_ISOM_TRUN_FLAGS = 0x400, + GF_ISOM_TRUN_CTS_OFFSET = 0x800 +}; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 sample_count; + /*the following are optional fields */ + s32 data_offset; + u32 first_sample_flags; + /*can be empty*/ + GF_List *entries; + + /*in write mode with data caching*/ + GF_BitStream *cache; +} GF_TrackFragmentRunBox; + +typedef struct +{ + u32 Duration; + u32 size; + u32 flags; + u32 CTS_Offset; +} GF_TrunEntry; + +#endif + + +/*RTP Hint Track Sample Entry*/ +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + u16 HintTrackVersion; + u16 LastCompatibleVersion; + u32 MaxPacketSize; + GF_List *HintDataTable; + /*this is where we store the current RTP sample in read/write mode*/ + struct __tag_hint_sample *hint_sample; + /*current hint sample in read mode, 1-based (0 is reset)*/ + u32 cur_sample; + u32 pck_sn, ts_offset, ssrc; + GF_TrackReferenceTypeBox *hint_ref; +} GF_HintSampleEntryBox; + + +typedef struct +{ + GF_ISOM_BOX + u32 subType; + char *sdpText; +} GF_RTPBox; + +typedef struct +{ + GF_ISOM_BOX + char *sdpText; +} GF_SDPBox; + +typedef struct +{ + GF_ISOM_BOX + s32 timeOffset; +} GF_RTPOBox; + +typedef struct +{ + GF_ISOM_BOX + /*contains GF_SDPBox if in track, GF_RTPBox if in movie*/ + GF_Box *SDP; + GF_List *boxList; +} GF_HintTrackInfoBox; + +typedef struct +{ + GF_ISOM_BOX + u8 reserved; + u8 prefered; + u8 required; +} GF_RelyHintBox; + +/*********************************************************** + data entry tables for RTP +***********************************************************/ +typedef struct +{ + GF_ISOM_BOX + u32 timeScale; +} GF_TSHintEntryBox; + +typedef struct +{ + GF_ISOM_BOX + u32 TimeOffset; +} GF_TimeOffHintEntryBox; + +typedef struct +{ + GF_ISOM_BOX + u32 SeqOffset; +} GF_SeqOffHintEntryBox; + + + +/*********************************************************** + hint track information boxes for RTP +***********************************************************/ + +/*Total number of bytes that will be sent, including 12-byte RTP headers, but not including any network headers*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbBytes; +} GF_TRPYBox; + +/*32-bits version of trpy used in Darwin*/ +typedef struct +{ + GF_ISOM_BOX + u32 nbBytes; +} GF_TOTLBox; + +/*Total number of network packets that will be sent*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbPackets; +} GF_NUMPBox; + +/*32-bits version of nump used in Darwin*/ +typedef struct +{ + GF_ISOM_BOX + u32 nbPackets; +} GF_NPCKBox; + + +/*Total number of bytes that will be sent, not including 12-byte RTP headers*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbBytes; +} GF_NTYLBox; + +/*32-bits version of tpyl used in Darwin*/ +typedef struct +{ + GF_ISOM_BOX + u32 nbBytes; +} GF_TPAYBox; + +/*Maximum data rate in bits per second.*/ +typedef struct +{ + GF_ISOM_BOX + u32 granularity; + u32 maxDataRate; +} GF_MAXRBox; + + +/*Total number of bytes from the media track to be sent*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbBytes; +} GF_DMEDBox; + +/*Number of bytes of immediate data to be sent*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbBytes; +} GF_DIMMBox; + + +/*Number of bytes of repeated data to be sent*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbBytes; +} GF_DREPBox; + +/*Smallest relative transmission time, in milliseconds. signed integer for smoothing*/ +typedef struct +{ + GF_ISOM_BOX + s32 minTime; +} GF_TMINBox; + +/*Largest relative transmission time, in milliseconds.*/ +typedef struct +{ + GF_ISOM_BOX + s32 maxTime; +} GF_TMAXBox; + +/*Largest packet, in bytes, including 12-byte RTP header*/ +typedef struct +{ + GF_ISOM_BOX + u32 maxSize; +} GF_PMAXBox; + +/*Longest packet duration, in milliseconds*/ +typedef struct +{ + GF_ISOM_BOX + u32 maxDur; +} GF_DMAXBox; + +/*32-bit payload type number, followed by rtpmap payload string */ +typedef struct +{ + GF_ISOM_BOX + u32 payloadCode; + char *payloadString; +} GF_PAYTBox; + + +typedef struct +{ + GF_ISOM_BOX + char *string; +} GF_NameBox; + +typedef struct +{ + GF_ISOM_BOX + GF_List *dataRates; + GF_List *boxList; +} GF_HintInfoBox; + +/*Apple extension*/ + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 reserved; + char *data; + u32 dataSize; +} GF_DataBox; + +typedef struct +{ + GF_ISOM_BOX + GF_DataBox *data; +} GF_ListItemBox; + +typedef struct +{ + GF_ISOM_BOX + GF_List *tags; +} GF_ItemListBox; + +/*OMA (P)DCF extensions*/ +typedef struct +{ + GF_ISOM_FULL_BOX + u8 EncryptionMethod; + u8 PaddingScheme; + u64 PlaintextLength; + char *ContentID; + char *RightsIssuerURL; + char *TextualHeaders; + u32 TextualHeadersLen; + GF_List *ExtendedHeaders; +} GF_OMADRMCommonHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u8 GKEncryptionMethod; + char *GroupID; + u16 GKLength; + char *GroupKey; +} GF_OMADRMGroupIDBox; + +typedef struct +{ + GF_ISOM_BOX + GF_List *boxes; +} GF_OMADRMMutableInformationBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + char TransactionID[16]; +} GF_OMADRMTransactionTrackingBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + char *oma_ro; + u32 oma_ro_size; +} GF_OMADRMRightsObjectBox; + +/*identical*/ +typedef struct __isma_format_box GF_OMADRMAUFormatBox; + +typedef struct __oma_kms_box +{ + GF_ISOM_FULL_BOX + GF_OMADRMCommonHeaderBox *hdr; + GF_OMADRMAUFormatBox *fmt; +} GF_OMADRMKMSBox; + + + +/* + Data Map (media storage) stuff +*/ + +/*regular file IO*/ +#define GF_ISOM_DATA_FILE 0x01 +/*File Mapaing object, read-only mode on complete files (no download)*/ +#define GF_ISOM_DATA_FILE_MAPPING 0x02 +/*External file object. Needs implementation*/ +#define GF_ISOM_DATA_FILE_EXTERN 0x03 + +/*Data Map modes*/ +enum +{ + /*read mode*/ + GF_ISOM_DATA_MAP_READ = 1, + /*write mode*/ + GF_ISOM_DATA_MAP_WRITE = 2, + /*the following modes are just ways of signaling extended functionalities + edit mode, to make sure the file is here, set to GF_ISOM_DATA_MAP_READ afterwards*/ + GF_ISOM_DATA_MAP_EDIT = 3, + /*read-only access to the movie file: we create a file mapping object + mode is set to GF_ISOM_DATA_MAP_READ afterwards*/ + GF_ISOM_DATA_MAP_READ_ONLY = 4, +}; + +/*this is the DataHandler structure each data handler has its own bitstream*/ +#define GF_ISOM_BASE_DATA_HANDLER \ + u8 type; \ + u64 curPos; \ + u8 mode; \ + GF_BitStream *bs; + +typedef struct __tag_data_map +{ + GF_ISOM_BASE_DATA_HANDLER +} GF_DataMap; + +typedef struct +{ + GF_ISOM_BASE_DATA_HANDLER + FILE *stream; + Bool last_acces_was_read; +#ifndef GPAC_READ_ONLY + char *temp_file; +#endif +} GF_FileDataMap; + +/*file mapping handler. used if supported, only on read mode for complete files (not in file download)*/ +typedef struct +{ + GF_ISOM_BASE_DATA_HANDLER + char *name; + u32 file_size; + char *byte_map; + u32 byte_pos; +} GF_FileMappingDataMap; + +GF_Err gf_isom_datamap_new(const char *location, const char *parentPath, u8 mode, GF_DataMap **outDataMap); +void gf_isom_datamap_del(GF_DataMap *ptr); +GF_Err gf_isom_datamap_open(GF_MediaBox *minf, u32 dataRefIndex, u8 Edit); +void gf_isom_datamap_close(GF_MediaInformationBox *minf); +u32 gf_isom_datamap_get_data(GF_DataMap *map, char *buffer, u32 bufferLength, u64 Offset); + +/*File-based data map*/ +GF_DataMap *gf_isom_fdm_new(const char *sPath, u8 mode); +void gf_isom_fdm_del(GF_FileDataMap *ptr); +u32 gf_isom_fdm_get_data(GF_FileDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset); + +#ifndef GPAC_READ_ONLY +GF_DataMap *gf_isom_fdm_new_temp(const char *sTempPath); +#endif + +/*file-mapping, read only*/ +GF_DataMap *gf_isom_fmo_new(const char *sPath, u8 mode); +void gf_isom_fmo_del(GF_FileMappingDataMap *ptr); +u32 gf_isom_fmo_get_data(GF_FileMappingDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset); + +#ifndef GPAC_READ_ONLY +u64 gf_isom_datamap_get_offset(GF_DataMap *map); +GF_Err gf_isom_datamap_add_data(GF_DataMap *ptr, char *data, u32 dataSize); +#endif + +/* + Movie stuff +*/ + + +/*time def for MP4/QT/MJ2K files*/ +#define GF_ISOM_MAC_TIME_OFFSET 2082758400/*208284480 */ + +#ifndef GPAC_ISOM_NO_FRAGMENTS +#define GF_ISOM_FORMAT_FRAG_FLAGS(pad, sync, deg) ( ( (pad) << 17) | ( ( !(sync) ) << 16) | (deg) ); +#define GF_ISOM_GET_FRAG_PAD(flag) ( (flag) >> 17) & 0x7 +#define GF_ISOM_GET_FRAG_SYNC(flag) ( ! ( ( (flag) >> 16) & 0x1)) +#define GF_ISOM_GET_FRAG_DEG(flag) (flag) & 0x7FFF +#endif + +enum +{ + GF_ISOM_FRAG_WRITE_READY = 0x01, + GF_ISOM_FRAG_READ_DEBUG = 0x02, +}; + +/*this is our movie object*/ +struct __tag_isom { + /*the last fatal error*/ + GF_Err LastError; + /*the original filename*/ + char *fileName; + /*the original file in read/edit, and also used in fragments mode + once the first moov has been written + Nota: this API doesn't allow fragments BEFORE the MOOV in order + to make easily parsable files (note there could be some data (mdat) before + the moov*/ + GF_DataMap *movieFileMap; + +#ifndef GPAC_READ_ONLY + /*the final file name*/ + char *finalName; + /*the file where we store edited samples (for READ_WRITE and WRITE mode only)*/ + GF_DataMap *editFileMap; + /*the interleaving time for dummy mode (in movie TimeScale)*/ + u32 interleavingTime; +#endif + + u8 openMode; + u8 storageMode; + /*if true 3GPP text streams are read as MPEG-4 StreamingText*/ + u8 convert_streaming_text; + u8 is_jp2; + + /*main boxes for fast access*/ + /*moov*/ + GF_MovieBox *moov; + /*our MDAT box (one and only one when we store the file)*/ + GF_MediaDataBox *mdat; + /*file brand (since v2, NULL means mp4 v1)*/ + GF_FileTypeBox *brand; + /*progressive download info*/ + GF_ProgressiveDownloadBox *pdin; + /*meta box if any*/ + GF_MetaBox *meta; + +#ifndef GPAC_ISOM_NO_FRAGMENTS + u32 FragmentsFlags, NextMoofNumber; + /*active fragment*/ + GF_MovieFragmentBox *moof; + /*in WRITE mode, this is the current MDAT where data is written*/ + /*in READ mode this is the last valid file position before a gf_isom_box_read failed*/ + u64 current_top_box_start; +#endif + + /*this contains ALL the root boxes excepts fragments*/ + GF_List *TopBoxes; + + /*default track for sync of MPEG4 streams - this is the first accessed stream without OCR info - only set in READ mode*/ + s32 es_id_default_sync; +}; + +/*time function*/ +u64 gf_isom_get_mp4time(); +/*set the last error of the file. if file is NULL, set the static error (used for IO errors*/ +void gf_isom_set_last_error(GF_ISOFile *the_file, GF_Err error); +GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing); +GF_ISOFile *gf_isom_new_movie(); +/*Movie and Track access functions*/ +GF_TrackBox *gf_isom_get_track_from_file(GF_ISOFile *the_file, u32 trackNumber); +GF_TrackBox *gf_isom_get_track(GF_MovieBox *moov, u32 trackNumber); +GF_TrackBox *gf_isom_get_track_from_id(GF_MovieBox *moov, u32 trackID); +u32 gf_isom_get_tracknum_from_id(GF_MovieBox *moov, u32 trackID); +/*open a movie*/ +GF_ISOFile *gf_isom_open_file(const char *fileName, u32 OpenMode, const char *tmp_dir); +/*close and delete a movie*/ +void gf_isom_delete_movie(GF_ISOFile *mov); +/*StreamDescription reconstruction Functions*/ +GF_Err GetESD(GF_MovieBox *moov, u32 trackID, u32 StreamDescIndex, GF_ESD **outESD); +GF_Err GetESDForTime(GF_MovieBox *moov, u32 trackID, u64 CTS, GF_ESD **outESD); +GF_Err Media_GetSampleDesc(GF_MediaBox *mdia, u32 SampleDescIndex, GF_SampleEntryBox **out_entry, u32 *dataRefIndex); +GF_Err Media_GetSampleDescIndex(GF_MediaBox *mdia, u64 DTS, u32 *sampleDescIndex); +/*get esd for given sample desc - + @true_desc_only: if true doesn't emulate desc and returns native ESD, + otherwise emulates if needed/possible (TimedText) and return a hard copy of the desc +*/ +GF_Err Media_GetESD(GF_MediaBox *mdia, u32 sampleDescIndex, GF_ESD **esd, Bool true_desc_only); +Bool Track_IsMPEG4Stream(u32 HandlerType); +Bool IsMP4Description(u32 entryType); +/*Find a reference of a given type*/ +GF_Err Track_FindRef(GF_TrackBox *trak, u32 ReferenceType, GF_TrackReferenceTypeBox **dpnd); +/*Time and sample*/ +GF_Err GetMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *MediaTime, s64 *SegmentStartTime, s64 *MediaOffset, u8 *useEdit); +GF_Err Media_GetSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample **samp, u32 *sampleDescriptionIndex, Bool no_data, u64 *out_offset); +GF_Err Media_CheckDataEntry(GF_MediaBox *mdia, u32 dataEntryIndex); +GF_Err Media_FindSyncSample(GF_SampleTableBox *stbl, u32 searchFromTime, u32 *sampleNumber, u8 mode); +GF_Err Media_RewriteODFrame(GF_MediaBox *mdia, GF_ISOSample *sample); +GF_Err Media_FindDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex); +Bool Media_IsSelfContained(GF_MediaBox *mdia, u32 StreamDescIndex); + +/*check the TimeToSample for the given time and return the Sample number +if the entry is not found, return the closest sampleNumber in prevSampleNumber and 0 in sampleNumber +if the DTS required is after all DTSs in the list, set prevSampleNumber and SampleNumber to 0 +useCTS specifies that we're looking for a composition time +*/ +GF_Err findEntryForTime(GF_SampleTableBox *stbl, u64 DTS, u8 useCTS, u32 *sampleNumber, u32 *prevSampleNumber); +/*Reading of the sample tables*/ +GF_Err stbl_GetSampleSize(GF_SampleSizeBox *stsz, u32 SampleNumber, u32 *Size); +GF_Err stbl_GetSampleCTS(GF_CompositionOffsetBox *ctts, u32 SampleNumber, u32 *CTSoffset); +GF_Err stbl_GetSampleDTS(GF_TimeToSampleBox *stts, u32 SampleNumber, u64 *DTS); +/*find a RAP or set the prev / next RAPs if vars are passed*/ +GF_Err stbl_GetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 *IsRAP, u32 *prevRAP, u32 *nextRAP); +GF_Err stbl_GetSampleInfos(GF_SampleTableBox *stbl, u32 sampleNumber, u64 *offset, u32 *chunkNumber, u32 *descIndex, u8 *isEdited); +GF_Err stbl_GetSampleShadow(GF_ShadowSyncBox *stsh, u32 *sampleNumber, u32 *syncNum); +GF_Err stbl_GetPaddingBits(GF_PaddingBitsBox *padb, u32 SampleNumber, u8 *PadBits); +u32 stbl_GetSampleFragmentCount(GF_SampleFragmentBox *stsf, u32 sampleNumber); +u32 stbl_GetSampleFragmentSize(GF_SampleFragmentBox *stsf, u32 sampleNumber, u32 FragmentIndex); +GF_Err stbl_GetSampleDepType(GF_SampleDependencyTypeBox *stbl, u32 SampleNumber, u32 *dependsOn, u32 *dependedOn, u32 *redundant); + +/*unpack sample2chunk and chunk offset so that we have 1 sample per chunk (edition mode only)*/ +GF_Err stbl_UnpackOffsets(GF_SampleTableBox *stbl); +GF_Err SetTrackDuration(GF_TrackBox *trak); +GF_Err Media_SetDuration(GF_TrackBox *trak); + +/*rewrites 3GP samples desc as MPEG-4 ESD*/ +GF_Err gf_isom_get_ttxt_esd(GF_MediaBox *mdia, GF_ESD **out_esd); +/*inserts TTU header - only used when conversion to StreamingText is on*/ +GF_Err gf_isom_rewrite_text_sample(GF_ISOSample *samp, u32 sampleDescriptionIndex, u32 sample_dur); + +GF_UserDataMap *udta_getEntry(GF_UserDataBox *ptr, u32 box_type, bin128 *uuid); + +#ifndef GPAC_READ_ONLY + +GF_Err FlushCaptureMode(GF_ISOFile *movie); +GF_Err CanAccessMovie(GF_ISOFile *movie, u32 Mode); +GF_ISOFile *gf_isom_create_movie(const char *fileName, u32 OpenMode, const char *tmp_dir); +void gf_isom_insert_moov(GF_ISOFile *file); + +GF_Err WriteToFile(GF_ISOFile *movie); +GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, GF_ESD *esd, u32 *outStreamIndex); +u8 RequestTrack(GF_MovieBox *moov, u32 TrackID); +/*Track-Media setup*/ +GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale); +GF_Err Media_ParseODFrame(GF_MediaBox *mdia, GF_ISOSample *sample, GF_ISOSample **od_samp); +GF_Err Media_AddSample(GF_MediaBox *mdia, u64 data_offset, GF_ISOSample *sample, u32 StreamDescIndex, u32 syncShadowNumber); +GF_Err Media_CreateDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex); +/*update a media sample. ONLY in edit mode*/ +GF_Err Media_UpdateSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample *sample, Bool data_only); +GF_Err Media_UpdateSampleReference(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset); +/*addition in the sample tables*/ +GF_Err stbl_AddDTS(GF_SampleTableBox *stbl, u64 DTS, u32 *sampleNumber, u32 LastAUDefDuration); +GF_Err stbl_AddCTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 CTSoffset); +GF_Err stbl_AddSize(GF_SampleSizeBox *stsz, u32 sampleNumber, u32 size); +GF_Err stbl_AddRAP(GF_SyncSampleBox *stss, u32 sampleNumber); +GF_Err stbl_AddShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber, u32 shadowNumber); +GF_Err stbl_AddChunkOffset(GF_MediaBox *mdia, u32 sampleNumber, u32 StreamDescIndex, u64 offset); +/*NB - no add for padding, this is done only through SetPaddingBits*/ + +GF_Err stbl_AddSampleFragment(GF_SampleTableBox *stbl, u32 sampleNumber, u16 size); + +/*update of the sample table +all these functions are called in edit and we always have 1 sample per chunk*/ +GF_Err stbl_SetChunkOffset(GF_MediaBox *mdia, u32 sampleNumber, u64 offset); +GF_Err stbl_SetSampleCTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 offset); +GF_Err stbl_SetSampleSize(GF_SampleSizeBox *stsz, u32 SampleNumber, u32 size); +GF_Err stbl_SetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 isRAP); +GF_Err stbl_SetSyncShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber, u32 syncSample); +GF_Err stbl_SetPaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber, u8 bits); +/*for adding fragmented samples*/ +GF_Err stbl_SampleSizeAppend(GF_SampleSizeBox *stsz, u32 data_size); +/*writing of the final chunk info in edit mode*/ +GF_Err stbl_SetChunkAndOffset(GF_SampleTableBox *stbl, u32 sampleNumber, u32 StreamDescIndex, GF_SampleToChunkBox *the_stsc, GF_Box **the_stco, u64 data_offset, u8 forceNewChunk); +/*EDIT LIST functions*/ +GF_EdtsEntry *CreateEditEntry(u64 EditDuration, u64 MediaTime, u8 EditMode); + +GF_Err stbl_SetRedundant(GF_SampleTableBox *stbl, u32 sampleNumber); +GF_Err stbl_AddRedundant(GF_SampleTableBox *stbl, u32 sampleNumber); + +/*REMOVE functions*/ +GF_Err stbl_RemoveDTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 LastAUDefDuration); +GF_Err stbl_RemoveCTS(GF_SampleTableBox *stbl, u32 sampleNumber); +GF_Err stbl_RemoveSize(GF_SampleSizeBox *stsz, u32 sampleNumber); +GF_Err stbl_RemoveChunk(GF_SampleTableBox *stbl, u32 sampleNumber); +GF_Err stbl_RemoveRAP(GF_SampleTableBox *stbl, u32 sampleNumber); +GF_Err stbl_RemoveShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber); +GF_Err stbl_RemovePaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber); +GF_Err stbl_RemoveSampleFragments(GF_SampleTableBox *stbl, u32 sampleNumber); +GF_Err stbl_RemoveRedundant(GF_SampleTableBox *stbl, u32 SampleNumber); + +#ifndef GPAC_ISOM_NO_FRAGMENTS +GF_Err StoreFragment(GF_ISOFile *movie); +#endif + +#endif + + +GF_Err GetNextMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *OutMovieTime); +GF_Err GetPrevMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *OutMovieTime); + +Bool IsHintTrack(GF_TrackBox *trak); +Bool CheckHintFormat(GF_TrackBox *trak, u32 HintType); +u32 GetHintFormat(GF_TrackBox *trak); + + +/*locate a box by its type or UUID*/ +GF_ItemListBox *gf_ismo_locate_box(GF_List *list, u32 boxType, bin128 UUID); + +GF_Err moov_AddBox(GF_Box *ptr, GF_Box *a); +GF_Err tref_AddBox(GF_Box *ptr, GF_Box *a); +GF_Err trak_AddBox(GF_Box *ptr, GF_Box *a); +GF_Err mvex_AddBox(GF_Box *ptr, GF_Box *a); +GF_Err stsd_AddBox(GF_SampleDescriptionBox *ptr, GF_Box *a); +GF_Err hnti_AddBox(GF_HintTrackInfoBox *hnti, GF_Box *a); +GF_Err udta_AddBox(GF_UserDataBox *ptr, GF_Box *a); +GF_Err edts_AddBox(GF_Box *s, GF_Box *a); +GF_Err stdp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sdtp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dinf_AddBox(GF_Box *s, GF_Box *a); +GF_Err minf_AddBox(GF_Box *s, GF_Box *a); +GF_Err mdia_AddBox(GF_Box *s, GF_Box *a); +GF_Err stbl_AddBox(GF_SampleTableBox *ptr, GF_Box *a); + +GF_Err AVC_UpdateESD(GF_MPEGVisualSampleEntryBox *avc, GF_ESD *esd); +void AVC_RewriteESDescriptor(GF_MPEGVisualSampleEntryBox *avc); +GF_Err reftype_AddRefTrack(GF_TrackReferenceTypeBox *ref, u32 trackID, u16 *outRefIndex); + + +/* + Hinting stuff +*/ + +/*the HintType for each protocol*/ +enum +{ + GF_ISMO_HINT_RTP = 1, + /*not supported yet*/ + GF_ISMO_MPEG2_TS = 2 +}; + +/***************************************************** + RTP Data Entries +*****************************************************/ + +#define GF_ISMO_BASE_DTE_ENTRY \ + u8 source; + +typedef struct +{ + GF_ISMO_BASE_DTE_ENTRY +} GF_GenericDTE; + +typedef struct +{ + GF_ISMO_BASE_DTE_ENTRY +} GF_EmptyDTE; + +typedef struct +{ + GF_ISMO_BASE_DTE_ENTRY + u8 dataLength; + char data[14]; +} GF_ImmediateDTE; + +typedef struct +{ + GF_ISMO_BASE_DTE_ENTRY + s8 trackRefIndex; + u32 sampleNumber; + u16 dataLength; + u32 byteOffset; + u16 bytesPerComp; + u16 samplesPerComp; +} GF_SampleDTE; + +typedef struct +{ + GF_ISMO_BASE_DTE_ENTRY + s8 trackRefIndex; + u32 streamDescIndex; + u16 dataLength; + u32 byteOffset; + u32 reserved; +} GF_StreamDescDTE; + +GF_GenericDTE *NewDTE(u8 type); +void DelDTE(GF_GenericDTE *dte); +GF_Err ReadDTE(GF_GenericDTE *dte, GF_BitStream *bs); +GF_Err WriteDTE(GF_GenericDTE *dte, GF_BitStream *bs); +GF_Err OffsetDTE(GF_GenericDTE *dte, u32 offset, u32 HintSampleNumber); + +/***************************************************** + RTP Sample +*****************************************************/ + +/*data cache when reading*/ +typedef struct __tag_hint_data_cache +{ + GF_ISOSample *samp; + GF_TrackBox *trak; + u32 sample_num; +} GF_HintDataCache; + + +typedef struct __tag_hint_sample +{ + /*used internally for future protocol support (write only)*/ + u8 HintType; + /*QT packets*/ + u16 reserved; + GF_List *packetTable; + char *AdditionalData; + u32 dataLength; + /*used internally for hinting*/ + u64 TransmissionTime; + /*for read only, used to store samples fetched while building packets*/ + GF_List *sample_cache; +} GF_HintSample; + +GF_HintSample *gf_isom_hint_sample_new(u32 ProtocolType); +void gf_isom_hint_sample_del(GF_HintSample *ptr); +GF_Err gf_isom_hint_sample_read(GF_HintSample *ptr, GF_BitStream *bs, u32 sampleSize); +GF_Err gf_isom_hint_sample_write(GF_HintSample *ptr, GF_BitStream *bs); +u32 gf_isom_hint_sample_size(GF_HintSample *ptr); + +/***************************************************** + Hint Packets (generic packet for future protocol support) +*****************************************************/ +#define GF_ISOM_BASE_PACKET \ + s32 relativeTransTime; + + +typedef struct +{ + GF_ISOM_BASE_PACKET +} GF_HintPacket; + +GF_HintPacket *gf_isom_hint_pck_new(u8 HintType); +void gf_isom_hint_pck_del(u8 HintType, GF_HintPacket *ptr); +GF_Err gf_isom_hint_pck_read(u8 HintType, GF_HintPacket *ptr, GF_BitStream *bs); +GF_Err gf_isom_hint_pck_write(u8 HintType, GF_HintPacket *ptr, GF_BitStream *bs); +u32 gf_isom_hint_pck_size(u8 HintType, GF_HintPacket *ptr); +GF_Err gf_isom_hint_pck_offset(u8 HintType, GF_HintPacket *ptr, u32 offset, u32 HintSampleNumber); +GF_Err gf_isom_hint_pck_add_dte(u8 HintType, GF_HintPacket *ptr, GF_GenericDTE *dte, u8 AtBegin); +/*get the size of the packet AS RECONSTRUCTED BY THE SERVER (without CSRC)*/ +u32 gf_isom_hint_pck_length(u8 HintType, GF_HintPacket *ptr); + +/*the RTP packet*/ +typedef struct +{ + GF_ISOM_BASE_PACKET + + /*RTP Header*/ + u8 P_bit; + u8 X_bit; + u8 M_bit; + /*on 7 bits */ + u8 payloadType; + u16 SequenceNumber; + /*Hinting flags*/ + u8 B_bit; + u8 R_bit; + /*ExtraInfos TLVs - not really used */ + GF_List *TLV; + /*DataTable - contains the DTEs...*/ + GF_List *DataTable; +} GF_RTPPacket; + +GF_RTPPacket *gf_isom_hint_rtp_new(); +void gf_isom_hint_rtp_del(GF_RTPPacket *ptr); +GF_Err gf_isom_hint_rtp_read(GF_RTPPacket *ptr, GF_BitStream *bs); +GF_Err gf_isom_hint_rtp_write(GF_RTPPacket *ptr, GF_BitStream *bs); +u32 gf_isom_hint_rtp_size(GF_RTPPacket *ptr); +GF_Err gf_isom_hint_rtp_offset(GF_RTPPacket *ptr, u32 offset, u32 HintSampleNumber); +u32 gf_isom_hint_rtp_length(GF_RTPPacket *ptr); + + + +struct _3gpp_text_sample +{ + char *text; + u32 len; + + GF_TextStyleBox *styles; + /*at most one of these*/ + GF_TextHighlightColorBox *highlight_color; + GF_TextScrollDelayBox *scroll_delay; + GF_TextBoxBox *box; + GF_TextWrapBox *wrap; + + GF_List *others; + GF_TextKaraokeBox *cur_karaoke; +}; + +GF_TextSample *gf_isom_parse_texte_sample(GF_BitStream *bs); +GF_TextSample *gf_isom_parse_texte_sample_from_data(char *data, u32 dataLength); + + + +/* + these are exported just in case, there should never be needed outside the lib +*/ + +GF_Box *reftype_New(); +GF_Box *free_New(); +GF_Box *mdat_New(); +GF_Box *moov_New(); +GF_Box *mvhd_New(); +GF_Box *mdhd_New(); +GF_Box *vmhd_New(); +GF_Box *smhd_New(); +GF_Box *hmhd_New(); +GF_Box *nmhd_New(); +GF_Box *stbl_New(); +GF_Box *dinf_New(); +GF_Box *url_New(); +GF_Box *urn_New(); +GF_Box *cprt_New(); +GF_Box *chpl_New(); +GF_Box *hdlr_New(); +GF_Box *iods_New(); +GF_Box *trak_New(); +GF_Box *mp4s_New(); +GF_Box *mp4v_New(); +GF_Box *mp4a_New(); +GF_Box *edts_New(); +GF_Box *udta_New(); +GF_Box *dref_New(); +GF_Box *stsd_New(); +GF_Box *stts_New(); +GF_Box *ctts_New(); +GF_Box *stsh_New(); +GF_Box *elst_New(); +GF_Box *stsc_New(); +GF_Box *stsz_New(); +GF_Box *stco_New(); +GF_Box *stss_New(); +GF_Box *stdp_New(); +GF_Box *sdtp_New(); +GF_Box *co64_New(); +GF_Box *esds_New(); +GF_Box *minf_New(); +GF_Box *tkhd_New(); +GF_Box *tref_New(); +GF_Box *mdia_New(); +GF_Box *defa_New(); +GF_Box *uuid_New(); +GF_Box *void_New(); +GF_Box *stsf_New(); +GF_Box *gnrm_New(); +GF_Box *gnrv_New(); +GF_Box *gnra_New(); +GF_Box *pdin_New(); + +void reftype_del(GF_Box *); +void free_del(GF_Box *); +void mdat_del(GF_Box *); +void moov_del(GF_Box *); +void mvhd_del(GF_Box *); +void mdhd_del(GF_Box *); +void vmhd_del(GF_Box *); +void smhd_del(GF_Box *); +void hmhd_del(GF_Box *); +void nmhd_del(GF_Box *); +void stbl_del(GF_Box *); +void dinf_del(GF_Box *); +void url_del(GF_Box *); +void urn_del(GF_Box *); +void chpl_del(GF_Box *); +void cprt_del(GF_Box *); +void hdlr_del(GF_Box *); +void iods_del(GF_Box *); +void trak_del(GF_Box *); +void mp4s_del(GF_Box *); +void mp4v_del(GF_Box *); +void mp4a_del(GF_Box *); +void edts_del(GF_Box *); +void udta_del(GF_Box *); +void dref_del(GF_Box *); +void stsd_del(GF_Box *); +void stts_del(GF_Box *); +void ctts_del(GF_Box *); +void stsh_del(GF_Box *); +void elst_del(GF_Box *); +void stsc_del(GF_Box *); +void stsz_del(GF_Box *); +void stco_del(GF_Box *); +void stss_del(GF_Box *); +void stdp_del(GF_Box *); +void sdtp_del(GF_Box *); +void co64_del(GF_Box *); +void esds_del(GF_Box *); +void minf_del(GF_Box *); +void tkhd_del(GF_Box *); +void tref_del(GF_Box *); +void mdia_del(GF_Box *); +void defa_del(GF_Box *); +void uuid_del(GF_Box *); +void void_del(GF_Box *); +void stsf_del(GF_Box *); +void gnrm_del(GF_Box *); +void gnrv_del(GF_Box *); +void gnra_del(GF_Box *); +void pdin_del(GF_Box *); + +GF_Err reftype_Write(GF_Box *s, GF_BitStream *bs); +GF_Err free_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mdat_Write(GF_Box *s, GF_BitStream *bs); +GF_Err moov_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mvhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mdhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err vmhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err smhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hmhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err nmhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stbl_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dinf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err url_Write(GF_Box *s, GF_BitStream *bs); +GF_Err urn_Write(GF_Box *s, GF_BitStream *bs); +GF_Err chpl_Write(GF_Box *s, GF_BitStream *bs); +GF_Err cprt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hdlr_Write(GF_Box *s, GF_BitStream *bs); +GF_Err iods_Write(GF_Box *s, GF_BitStream *bs); +GF_Err trak_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mp4s_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mp4v_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mp4a_Write(GF_Box *s, GF_BitStream *bs); +GF_Err edts_Write(GF_Box *s, GF_BitStream *bs); +GF_Err udta_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dref_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stsd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stts_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ctts_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stsh_Write(GF_Box *s, GF_BitStream *bs); +GF_Err elst_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stsc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stsz_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stco_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stss_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stdp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err sdtp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err co64_Write(GF_Box *s, GF_BitStream *bs); +GF_Err esds_Write(GF_Box *s, GF_BitStream *bs); +GF_Err minf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tkhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tref_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mdia_Write(GF_Box *s, GF_BitStream *bs); +GF_Err defa_Write(GF_Box *s, GF_BitStream *bs); +GF_Err uuid_Write(GF_Box *s, GF_BitStream *bs); +GF_Err void_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stsf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gnrm_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gnrv_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gnra_Write(GF_Box *s, GF_BitStream *bs); +GF_Err pdin_Write(GF_Box *s, GF_BitStream *bs); + +GF_Err reftype_Size(GF_Box *); +GF_Err free_Size(GF_Box *); +GF_Err mdat_Size(GF_Box *); +GF_Err moov_Size(GF_Box *); +GF_Err mvhd_Size(GF_Box *); +GF_Err mdhd_Size(GF_Box *); +GF_Err vmhd_Size(GF_Box *); +GF_Err smhd_Size(GF_Box *); +GF_Err hmhd_Size(GF_Box *); +GF_Err nmhd_Size(GF_Box *); +GF_Err stbl_Size(GF_Box *); +GF_Err dinf_Size(GF_Box *); +GF_Err url_Size(GF_Box *); +GF_Err urn_Size(GF_Box *); +GF_Err chpl_Size(GF_Box *); +GF_Err cprt_Size(GF_Box *); +GF_Err hdlr_Size(GF_Box *); +GF_Err iods_Size(GF_Box *); +GF_Err trak_Size(GF_Box *); +GF_Err mp4s_Size(GF_Box *); +GF_Err mp4v_Size(GF_Box *); +GF_Err mp4a_Size(GF_Box *); +GF_Err edts_Size(GF_Box *); +GF_Err udta_Size(GF_Box *); +GF_Err dref_Size(GF_Box *); +GF_Err stsd_Size(GF_Box *); +GF_Err stts_Size(GF_Box *); +GF_Err ctts_Size(GF_Box *); +GF_Err stsh_Size(GF_Box *); +GF_Err elst_Size(GF_Box *); +GF_Err stsc_Size(GF_Box *); +GF_Err stsz_Size(GF_Box *); +GF_Err stco_Size(GF_Box *); +GF_Err stss_Size(GF_Box *); +GF_Err stdp_Size(GF_Box *); +GF_Err sdtp_Size(GF_Box *); +GF_Err co64_Size(GF_Box *); +GF_Err esds_Size(GF_Box *); +GF_Err minf_Size(GF_Box *); +GF_Err tkhd_Size(GF_Box *); +GF_Err tref_Size(GF_Box *); +GF_Err mdia_Size(GF_Box *); +GF_Err defa_Size(GF_Box *); +GF_Err uuid_Size(GF_Box *); +GF_Err void_Size(GF_Box *); +GF_Err stsf_Size(GF_Box *); +GF_Err gnrm_Size(GF_Box *); +GF_Err gnrv_Size(GF_Box *); +GF_Err gnra_Size(GF_Box *); +GF_Err pdin_Size(GF_Box *); + +GF_Err reftype_Read(GF_Box *s, GF_BitStream *bs); +GF_Err free_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mdat_Read(GF_Box *s, GF_BitStream *bs); +GF_Err moov_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mvhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mdhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err vmhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err smhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hmhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err nmhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stbl_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dinf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err url_Read(GF_Box *s, GF_BitStream *bs); +GF_Err urn_Read(GF_Box *s, GF_BitStream *bs); +GF_Err chpl_Read(GF_Box *s, GF_BitStream *bs); +GF_Err cprt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hdlr_Read(GF_Box *s, GF_BitStream *bs); +GF_Err iods_Read(GF_Box *s, GF_BitStream *bs); +GF_Err trak_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mp4s_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mp4v_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mp4a_Read(GF_Box *s, GF_BitStream *bs); +GF_Err edts_Read(GF_Box *s, GF_BitStream *bs); +GF_Err udta_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dref_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stsd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stts_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ctts_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stsh_Read(GF_Box *s, GF_BitStream *bs); +GF_Err elst_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stsc_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stsz_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stco_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stss_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stdp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sdtp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err co64_Read(GF_Box *s, GF_BitStream *bs); +GF_Err esds_Read(GF_Box *s, GF_BitStream *bs); +GF_Err minf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tkhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tref_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mdia_Read(GF_Box *s, GF_BitStream *bs); +GF_Err defa_Read(GF_Box *s, GF_BitStream *bs); +GF_Err uuid_Read(GF_Box *s, GF_BitStream *bs); +GF_Err void_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stsf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err pdin_Read(GF_Box *s, GF_BitStream *bs); + + +GF_Box *hinf_New(); +GF_Box *trpy_New(); +GF_Box *totl_New(); +GF_Box *nump_New(); +GF_Box *npck_New(); +GF_Box *tpyl_New(); +GF_Box *tpay_New(); +GF_Box *maxr_New(); +GF_Box *dmed_New(); +GF_Box *dimm_New(); +GF_Box *drep_New(); +GF_Box *tmin_New(); +GF_Box *tmax_New(); +GF_Box *pmax_New(); +GF_Box *dmax_New(); +GF_Box *payt_New(); +GF_Box *name_New(); +GF_Box *rely_New(); +GF_Box *snro_New(); +GF_Box *tims_New(); +GF_Box *tsro_New(); +GF_Box *ghnt_New(); +GF_Box *hnti_New(); +GF_Box *sdp_New(); +GF_Box *rtpo_New(); + +void hinf_del(GF_Box *s); +void trpy_del(GF_Box *s); +void totl_del(GF_Box *s); +void nump_del(GF_Box *s); +void npck_del(GF_Box *s); +void tpyl_del(GF_Box *s); +void tpay_del(GF_Box *s); +void maxr_del(GF_Box *s); +void dmed_del(GF_Box *s); +void dimm_del(GF_Box *s); +void drep_del(GF_Box *s); +void tmin_del(GF_Box *s); +void tmax_del(GF_Box *s); +void pmax_del(GF_Box *s); +void dmax_del(GF_Box *s); +void payt_del(GF_Box *s); +void name_del(GF_Box *s); +void rely_del(GF_Box *s); +void snro_del(GF_Box *s); +void tims_del(GF_Box *s); +void tsro_del(GF_Box *s); +void ghnt_del(GF_Box *s); +void hnti_del(GF_Box *a); +void sdp_del(GF_Box *a); +void rtpo_del(GF_Box *s); + +GF_Err hinf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err trpy_Read(GF_Box *s, GF_BitStream *bs); +GF_Err totl_Read(GF_Box *s, GF_BitStream *bs); +GF_Err nump_Read(GF_Box *s, GF_BitStream *bs); +GF_Err npck_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tpyl_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tpay_Read(GF_Box *s, GF_BitStream *bs); +GF_Err maxr_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dmed_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dimm_Read(GF_Box *s, GF_BitStream *bs); +GF_Err drep_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tmin_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tmax_Read(GF_Box *s, GF_BitStream *bs); +GF_Err pmax_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dmax_Read(GF_Box *s, GF_BitStream *bs); +GF_Err payt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err name_Read(GF_Box *s, GF_BitStream *bs); +GF_Err rely_Read(GF_Box *s, GF_BitStream *bs); +GF_Err snro_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tims_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tsro_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ghnt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hnti_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sdp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err rtpo_Read(GF_Box *s, GF_BitStream *bs); + +GF_Err hinf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err trpy_Write(GF_Box *s, GF_BitStream *bs); +GF_Err totl_Write(GF_Box *s, GF_BitStream *bs); +GF_Err nump_Write(GF_Box *s, GF_BitStream *bs); +GF_Err npck_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tpyl_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tpay_Write(GF_Box *s, GF_BitStream *bs); +GF_Err maxr_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dmed_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dimm_Write(GF_Box *s, GF_BitStream *bs); +GF_Err drep_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tmin_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tmax_Write(GF_Box *s, GF_BitStream *bs); +GF_Err pmax_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dmax_Write(GF_Box *s, GF_BitStream *bs); +GF_Err payt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err name_Write(GF_Box *s, GF_BitStream *bs); +GF_Err rely_Write(GF_Box *s, GF_BitStream *bs); +GF_Err snro_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tims_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tsro_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ghnt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hnti_Write(GF_Box *s, GF_BitStream *bs); +GF_Err sdp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err rtpo_Write(GF_Box *s, GF_BitStream *bs); + +GF_Err hinf_Size(GF_Box *s); +GF_Err trpy_Size(GF_Box *s); +GF_Err totl_Size(GF_Box *s); +GF_Err nump_Size(GF_Box *s); +GF_Err npck_Size(GF_Box *s); +GF_Err tpyl_Size(GF_Box *s); +GF_Err tpay_Size(GF_Box *s); +GF_Err maxr_Size(GF_Box *s); +GF_Err dmed_Size(GF_Box *s); +GF_Err dimm_Size(GF_Box *s); +GF_Err drep_Size(GF_Box *s); +GF_Err tmin_Size(GF_Box *s); +GF_Err tmax_Size(GF_Box *s); +GF_Err pmax_Size(GF_Box *s); +GF_Err dmax_Size(GF_Box *s); +GF_Err payt_Size(GF_Box *s); +GF_Err name_Size(GF_Box *s); +GF_Err rely_Size(GF_Box *s); +GF_Err snro_Size(GF_Box *s); +GF_Err tims_Size(GF_Box *s); +GF_Err tsro_Size(GF_Box *s); +GF_Err ghnt_Size(GF_Box *s); +GF_Err hnti_Size(GF_Box *s); +GF_Err sdp_Size(GF_Box *s); +GF_Err rtpo_Size(GF_Box *s); + + +GF_Box *ftyp_New(); +void ftyp_del(GF_Box *s); +GF_Err ftyp_Read(GF_Box *s,GF_BitStream *bs); +GF_Err ftyp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ftyp_Size(GF_Box *s); + +GF_Box *padb_New(); +void padb_del(GF_Box *s); +GF_Err padb_Read(GF_Box *s, GF_BitStream *bs); +GF_Err padb_Write(GF_Box *s, GF_BitStream *bs); +GF_Err padb_Size(GF_Box *s); + +GF_Box *gppa_New(u32 type); +GF_Box *gppv_New(u32 type); +GF_Box *gppc_New(u32 type); +void gppa_del(GF_Box *s); +void gppv_del(GF_Box *s); +void gppc_del(GF_Box *s); +GF_Err gppa_Read(GF_Box *s, GF_BitStream *bs); +GF_Err gppv_Read(GF_Box *s, GF_BitStream *bs); +GF_Err gppc_Read(GF_Box *s, GF_BitStream *bs); +#ifndef GPAC_READ_ONLY +GF_Err gppa_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gppv_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gppc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gppa_Size(GF_Box *s); +GF_Err gppv_Size(GF_Box *s); +GF_Err gppc_Size(GF_Box *s); +#endif + + +#ifndef GPAC_ISOM_NO_FRAGMENTS +GF_Box *mvex_New(); +GF_Box *trex_New(); +GF_Box *moof_New(); +GF_Box *mfhd_New(); +GF_Box *traf_New(); +GF_Box *tfhd_New(); +GF_Box *trun_New(); + +void mvex_del(GF_Box *s); +void trex_del(GF_Box *s); +void moof_del(GF_Box *s); +void mfhd_del(GF_Box *s); +void traf_del(GF_Box *s); +void tfhd_del(GF_Box *s); +void trun_del(GF_Box *s); + +GF_Err mvex_Read(GF_Box *s, GF_BitStream *bs); +GF_Err trex_Read(GF_Box *s, GF_BitStream *bs); +GF_Err moof_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mfhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err traf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tfhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err trun_Read(GF_Box *s, GF_BitStream *bs); + +GF_Err mvex_Write(GF_Box *s, GF_BitStream *bs); +GF_Err trex_Write(GF_Box *s, GF_BitStream *bs); +GF_Err moof_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mfhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err traf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tfhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err trun_Write(GF_Box *s, GF_BitStream *bs); + +GF_Err mvex_Size(GF_Box *s); +GF_Err trex_Size(GF_Box *s); +GF_Err moof_Size(GF_Box *s); +GF_Err mfhd_Size(GF_Box *s); +GF_Err traf_Size(GF_Box *s); +GF_Err tfhd_Size(GF_Box *s); +GF_Err trun_Size(GF_Box *s); + + +GF_Box *mehd_New(); +void mehd_del(GF_Box *s); +GF_Err mehd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mehd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mehd_Size(GF_Box *s); + +#endif + +/*avc ext*/ +GF_Box *avcc_New(); +void avcc_del(GF_Box *s); +GF_Err avcc_Read(GF_Box *s, GF_BitStream *bs); +GF_Err avcc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err avcc_Size(GF_Box *s); + +GF_Box *avc1_New(); + +GF_Box *m4ds_New(); +void m4ds_del(GF_Box *s); +GF_Err m4ds_Read(GF_Box *s, GF_BitStream *bs); +GF_Err m4ds_Write(GF_Box *s, GF_BitStream *bs); +GF_Err m4ds_Size(GF_Box *s); + +GF_Box *btrt_New(); +void btrt_del(GF_Box *s); +GF_Err btrt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err btrt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err btrt_Size(GF_Box *s); + + +/*3GPP streaming text*/ +GF_Box *ftab_New(); +GF_Box *tx3g_New(); +GF_Box *styl_New(); +GF_Box *hlit_New(); +GF_Box *hclr_New(); +GF_Box *krok_New(); +GF_Box *dlay_New(); +GF_Box *href_New(); +GF_Box *tbox_New(); +GF_Box *blnk_New(); +GF_Box *twrp_New(); + +void ftab_del(GF_Box *s); +void tx3g_del(GF_Box *s); +void styl_del(GF_Box *s); +void hlit_del(GF_Box *s); +void hclr_del(GF_Box *s); +void krok_del(GF_Box *s); +void dlay_del(GF_Box *s); +void href_del(GF_Box *s); +void tbox_del(GF_Box *s); +void blnk_del(GF_Box *s); +void twrp_del(GF_Box *s); + +GF_Err ftab_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tx3g_Read(GF_Box *s, GF_BitStream *bs); +GF_Err styl_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hlit_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hclr_Read(GF_Box *s, GF_BitStream *bs); +GF_Err krok_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dlay_Read(GF_Box *s, GF_BitStream *bs); +GF_Err href_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tbox_Read(GF_Box *s, GF_BitStream *bs); +GF_Err blnk_Read(GF_Box *s, GF_BitStream *bs); +GF_Err twrp_Read(GF_Box *s, GF_BitStream *bs); + +#ifndef GPAC_READ_ONLY +GF_Err ftab_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tx3g_Write(GF_Box *s, GF_BitStream *bs); +GF_Err styl_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hlit_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hclr_Write(GF_Box *s, GF_BitStream *bs); +GF_Err krok_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dlay_Write(GF_Box *s, GF_BitStream *bs); +GF_Err href_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tbox_Write(GF_Box *s, GF_BitStream *bs); +GF_Err blnk_Write(GF_Box *s, GF_BitStream *bs); +GF_Err twrp_Write(GF_Box *s, GF_BitStream *bs); + +GF_Err ftab_Size(GF_Box *s); +GF_Err tx3g_Size(GF_Box *s); +GF_Err styl_Size(GF_Box *s); +GF_Err hlit_Size(GF_Box *s); +GF_Err hclr_Size(GF_Box *s); +GF_Err krok_Size(GF_Box *s); +GF_Err dlay_Size(GF_Box *s); +GF_Err href_Size(GF_Box *s); +GF_Err tbox_Size(GF_Box *s); +GF_Err blnk_Size(GF_Box *s); +GF_Err twrp_Size(GF_Box *s); +#endif + + +/* MPEG-21 functions */ +GF_Box *meta_New(); +GF_Box *xml_New(); +GF_Box *bxml_New(); +GF_Box *iloc_New(); +GF_Box *pitm_New(); +GF_Box *ipro_New(); +GF_Box *infe_New(); +GF_Box *iinf_New(); +GF_Box *sinf_New(); +GF_Box *frma_New(); +GF_Box *schm_New(); +GF_Box *schi_New(); +GF_Box *enca_New(); +GF_Box *encv_New(); +GF_Box *encs_New(); + +void meta_del(GF_Box *s); +void xml_del(GF_Box *s); +void bxml_del(GF_Box *s); +void iloc_del(GF_Box *s); +void pitm_del(GF_Box *s); +void ipro_del(GF_Box *s); +void infe_del(GF_Box *s); +void iinf_del(GF_Box *s); +void sinf_del(GF_Box *s); +void frma_del(GF_Box *s); +void schm_del(GF_Box *s); +void schi_del(GF_Box *s); + +GF_Err meta_Read(GF_Box *s, GF_BitStream *bs); +GF_Err xml_Read(GF_Box *s, GF_BitStream *bs); +GF_Err bxml_Read(GF_Box *s, GF_BitStream *bs); +GF_Err iloc_Read(GF_Box *s, GF_BitStream *bs); +GF_Err pitm_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ipro_Read(GF_Box *s, GF_BitStream *bs); +GF_Err infe_Read(GF_Box *s, GF_BitStream *bs); +GF_Err iinf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sinf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err frma_Read(GF_Box *s, GF_BitStream *bs); +GF_Err schm_Read(GF_Box *s, GF_BitStream *bs); +GF_Err schi_Read(GF_Box *s, GF_BitStream *bs); + +#ifndef GPAC_READ_ONLY +GF_Err meta_Write(GF_Box *s, GF_BitStream *bs); +GF_Err xml_Write(GF_Box *s, GF_BitStream *bs); +GF_Err bxml_Write(GF_Box *s, GF_BitStream *bs); +GF_Err iloc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err pitm_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ipro_Write(GF_Box *s, GF_BitStream *bs); +GF_Err infe_Write(GF_Box *s, GF_BitStream *bs); +GF_Err iinf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err sinf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err frma_Write(GF_Box *s, GF_BitStream *bs); +GF_Err schm_Write(GF_Box *s, GF_BitStream *bs); +GF_Err schi_Write(GF_Box *s, GF_BitStream *bs); + +GF_Err meta_Size(GF_Box *s); +GF_Err xml_Size(GF_Box *s); +GF_Err bxml_Size(GF_Box *s); +GF_Err iloc_Size(GF_Box *s); +GF_Err pitm_Size(GF_Box *s); +GF_Err ipro_Size(GF_Box *s); +GF_Err infe_Size(GF_Box *s); +GF_Err iinf_Size(GF_Box *s); +GF_Err sinf_Size(GF_Box *s); +GF_Err frma_Size(GF_Box *s); +GF_Err schm_Size(GF_Box *s); +GF_Err schi_Size(GF_Box *s); +#endif + +/* end of MPEG-21 functions */ + + +/** ISMACryp functions **/ +GF_Box *iKMS_New(); +GF_Box *iSFM_New(); +void iKMS_del(GF_Box *s); +void iSFM_del(GF_Box *s); +GF_Err iKMS_Read(GF_Box *s, GF_BitStream *bs); +GF_Err iSFM_Read(GF_Box *s, GF_BitStream *bs); +#ifndef GPAC_READ_ONLY +GF_Err iKMS_Write(GF_Box *s, GF_BitStream *bs); +GF_Err iSFM_Write(GF_Box *s, GF_BitStream *bs); +GF_Err iKMS_Size(GF_Box *s); +GF_Err iSFM_Size(GF_Box *s); +#endif + +/* Apple extensions */ +void ilst_del(GF_Box *s); +void ListItem_del(GF_Box *s); +void data_del(GF_Box *s); +GF_Err ilst_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ListItem_Read(GF_Box *s, GF_BitStream *bs); +GF_Err data_Read(GF_Box *s, GF_BitStream *bs); +GF_Box *ilst_New(); +GF_Box *ListItem_New(u32 type); +GF_Box *data_New(); +#ifndef GPAC_READ_ONLY +GF_Err ilst_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ListItem_Write(GF_Box *s, GF_BitStream *bs); +GF_Err data_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ilst_Size(GF_Box *s); +GF_Err ListItem_Size(GF_Box *s); +GF_Err data_Size(GF_Box *s); +#endif + + +GF_Err gb_box_array_dump(GF_List *list, FILE * trace); +GF_Err reftype_dump(GF_Box *a, FILE * trace); +GF_Err free_dump(GF_Box *a, FILE * trace); +GF_Err mdat_dump(GF_Box *a, FILE * trace); +GF_Err moov_dump(GF_Box *a, FILE * trace); +GF_Err mvhd_dump(GF_Box *a, FILE * trace); +GF_Err mdhd_dump(GF_Box *a, FILE * trace); +GF_Err vmhd_dump(GF_Box *a, FILE * trace); +GF_Err smhd_dump(GF_Box *a, FILE * trace); +GF_Err hmhd_dump(GF_Box *a, FILE * trace); +GF_Err nmhd_dump(GF_Box *a, FILE * trace); +GF_Err stbl_dump(GF_Box *a, FILE * trace); +GF_Err dinf_dump(GF_Box *a, FILE * trace); +GF_Err url_dump(GF_Box *a, FILE * trace); +GF_Err urn_dump(GF_Box *a, FILE * trace); +GF_Err cprt_dump(GF_Box *a, FILE * trace); +GF_Err hdlr_dump(GF_Box *a, FILE * trace); +GF_Err iods_dump(GF_Box *a, FILE * trace); +GF_Err trak_dump(GF_Box *a, FILE * trace); +GF_Err mp4s_dump(GF_Box *a, FILE * trace); +GF_Err mp4v_dump(GF_Box *a, FILE * trace); +GF_Err mp4a_dump(GF_Box *a, FILE * trace); +GF_Err edts_dump(GF_Box *a, FILE * trace); +GF_Err udta_dump(GF_Box *a, FILE * trace); +GF_Err dref_dump(GF_Box *a, FILE * trace); +GF_Err stsd_dump(GF_Box *a, FILE * trace); +GF_Err stts_dump(GF_Box *a, FILE * trace); +GF_Err ctts_dump(GF_Box *a, FILE * trace); +GF_Err stsh_dump(GF_Box *a, FILE * trace); +GF_Err elst_dump(GF_Box *a, FILE * trace); +GF_Err stsc_dump(GF_Box *a, FILE * trace); +GF_Err stsz_dump(GF_Box *a, FILE * trace); +GF_Err stco_dump(GF_Box *a, FILE * trace); +GF_Err stss_dump(GF_Box *a, FILE * trace); +GF_Err stdp_dump(GF_Box *a, FILE * trace); +GF_Err sdtp_dump(GF_Box *a, FILE * trace); +GF_Err co64_dump(GF_Box *a, FILE * trace); +GF_Err esds_dump(GF_Box *a, FILE * trace); +GF_Err minf_dump(GF_Box *a, FILE * trace); +GF_Err tkhd_dump(GF_Box *a, FILE * trace); +GF_Err tref_dump(GF_Box *a, FILE * trace); +GF_Err mdia_dump(GF_Box *a, FILE * trace); +GF_Err defa_dump(GF_Box *a, FILE * trace); +GF_Err void_dump(GF_Box *a, FILE * trace); +GF_Err ftyp_dump(GF_Box *a, FILE * trace); +GF_Err padb_dump(GF_Box *a, FILE * trace); +GF_Err stsf_dump(GF_Box *a, FILE * trace); +GF_Err gnrm_dump(GF_Box *a, FILE * trace); +GF_Err gnrv_dump(GF_Box *a, FILE * trace); +GF_Err gnra_dump(GF_Box *a, FILE * trace); +GF_Err gppa_dump(GF_Box *a, FILE * trace); +GF_Err gppv_dump(GF_Box *a, FILE * trace); +GF_Err gppc_dump(GF_Box *a, FILE * trace); +GF_Err chpl_dump(GF_Box *a, FILE * trace); +GF_Err dpin_dump(GF_Box *a, FILE * trace); + +GF_Err hinf_dump(GF_Box *a, FILE * trace); +GF_Err trpy_dump(GF_Box *a, FILE * trace); +GF_Err totl_dump(GF_Box *a, FILE * trace); +GF_Err nump_dump(GF_Box *a, FILE * trace); +GF_Err npck_dump(GF_Box *a, FILE * trace); +GF_Err tpyl_dump(GF_Box *a, FILE * trace); +GF_Err tpay_dump(GF_Box *a, FILE * trace); +GF_Err maxr_dump(GF_Box *a, FILE * trace); +GF_Err dmed_dump(GF_Box *a, FILE * trace); +GF_Err dimm_dump(GF_Box *a, FILE * trace); +GF_Err drep_dump(GF_Box *a, FILE * trace); +GF_Err tmin_dump(GF_Box *a, FILE * trace); +GF_Err tmax_dump(GF_Box *a, FILE * trace); +GF_Err pmax_dump(GF_Box *a, FILE * trace); +GF_Err dmax_dump(GF_Box *a, FILE * trace); +GF_Err payt_dump(GF_Box *a, FILE * trace); +GF_Err name_dump(GF_Box *a, FILE * trace); +GF_Err rely_dump(GF_Box *a, FILE * trace); +GF_Err snro_dump(GF_Box *a, FILE * trace); +GF_Err tims_dump(GF_Box *a, FILE * trace); +GF_Err tsro_dump(GF_Box *a, FILE * trace); +GF_Err ghnt_dump(GF_Box *a, FILE * trace); +GF_Err hnti_dump(GF_Box *a, FILE * trace); +GF_Err sdp_dump(GF_Box *a, FILE * trace); +GF_Err rtpo_dump(GF_Box *a, FILE * trace); + + + +#ifndef GPAC_ISOM_NO_FRAGMENTS +GF_Err mvex_dump(GF_Box *a, FILE * trace); +GF_Err mehd_dump(GF_Box *a, FILE * trace); +GF_Err trex_dump(GF_Box *a, FILE * trace); +GF_Err moof_dump(GF_Box *a, FILE * trace); +GF_Err mfhd_dump(GF_Box *a, FILE * trace); +GF_Err traf_dump(GF_Box *a, FILE * trace); +GF_Err tfhd_dump(GF_Box *a, FILE * trace); +GF_Err trun_dump(GF_Box *a, FILE * trace); +#endif + +GF_Err avcc_dump(GF_Box *a, FILE * trace); +GF_Err avc1_dump(GF_Box *a, FILE * trace); +GF_Err m4ds_dump(GF_Box *a, FILE * trace); +GF_Err btrt_dump(GF_Box *a, FILE * trace); + +GF_Err ftab_dump(GF_Box *a, FILE * trace); +GF_Err tx3g_dump(GF_Box *a, FILE * trace); +GF_Err styl_dump(GF_Box *a, FILE * trace); +GF_Err hlit_dump(GF_Box *a, FILE * trace); +GF_Err hclr_dump(GF_Box *a, FILE * trace); +GF_Err krok_dump(GF_Box *a, FILE * trace); +GF_Err dlay_dump(GF_Box *a, FILE * trace); +GF_Err href_dump(GF_Box *a, FILE * trace); +GF_Err tbox_dump(GF_Box *a, FILE * trace); +GF_Err blnk_dump(GF_Box *a, FILE * trace); +GF_Err twrp_dump(GF_Box *a, FILE * trace); + +/* ISMACryp dump */ +GF_Err iKMS_dump(GF_Box *a, FILE * trace); +GF_Err iSFM_dump(GF_Box *a, FILE * trace); + +/*MPEG-21 extensions dump*/ +GF_Err meta_dump(GF_Box *a, FILE * trace); +GF_Err xml_dump(GF_Box *a, FILE * trace); +GF_Err bxml_dump(GF_Box *a, FILE * trace); +GF_Err iloc_dump(GF_Box *a, FILE * trace); +GF_Err pitm_dump(GF_Box *a, FILE * trace); +GF_Err ipro_dump(GF_Box *a, FILE * trace); +GF_Err infe_dump(GF_Box *a, FILE * trace); +GF_Err iinf_dump(GF_Box *a, FILE * trace); +GF_Err sinf_dump(GF_Box *a, FILE * trace); +GF_Err frma_dump(GF_Box *a, FILE * trace); +GF_Err schm_dump(GF_Box *a, FILE * trace); +GF_Err schi_dump(GF_Box *a, FILE * trace); + +/*Apple extensions*/ +GF_Err ilst_dump(GF_Box *a, FILE * trace); +GF_Err ListItem_dump(GF_Box *a, FILE * trace); +GF_Err data_dump(GF_Box *a, FILE * trace); + +/*Apple extensions*/ +GF_MetaBox *gf_isom_apple_get_meta_extensions(GF_ISOFile *mov); + +#ifndef GPAC_READ_ONLY +GF_MetaBox *gf_isom_apple_create_meta_extensions(GF_ISOFile *mov); +#endif //GPAC_READ_ONLY + +/*OMA extensions*/ +GF_Box *ohdr_New(); +void ohdr_del(GF_Box *s); +GF_Err ohdr_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ohdr_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ohdr_Size(GF_Box *s); +GF_Err ohdr_dump(GF_Box *a, FILE * trace); +GF_Box *grpi_New(); +void grpi_del(GF_Box *s); +GF_Err grpi_Read(GF_Box *s, GF_BitStream *bs); +GF_Err grpi_Write(GF_Box *s, GF_BitStream *bs); +GF_Err grpi_Size(GF_Box *s); +GF_Err grpi_dump(GF_Box *a, FILE * trace); +GF_Box *mdri_New(); +void mdri_del(GF_Box *s); +GF_Err mdri_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mdri_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mdri_Size(GF_Box *s); +GF_Err mdri_dump(GF_Box *a, FILE * trace); +GF_Box *odtt_New(); +void odtt_del(GF_Box *s); +GF_Err odtt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err odtt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err odtt_Size(GF_Box *s); +GF_Err odtt_dump(GF_Box *a, FILE * trace); +GF_Box *odrb_New(); +void odrb_del(GF_Box *s); +GF_Err odrb_Read(GF_Box *s, GF_BitStream *bs); +GF_Err odrb_Write(GF_Box *s, GF_BitStream *bs); +GF_Err odrb_Size(GF_Box *s); +GF_Err odrb_dump(GF_Box *a, FILE * trace); +GF_Box *odkm_New(); +void odkm_del(GF_Box *s); +GF_Err odkm_Read(GF_Box *s, GF_BitStream *bs); +GF_Err odkm_Write(GF_Box *s, GF_BitStream *bs); +GF_Err odkm_Size(GF_Box *s); +GF_Err odkm_dump(GF_Box *a, FILE * trace); + + +GF_Box *pasp_New(); +void pasp_del(GF_Box *s); +GF_Err pasp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err pasp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err pasp_Size(GF_Box *s); +GF_Err pasp_dump(GF_Box *a, FILE * trace); + +GF_Box *metx_New(u32 type); +void metx_del(GF_Box *s); +GF_Err metx_Read(GF_Box *s, GF_BitStream *bs); +GF_Err metx_Write(GF_Box *s, GF_BitStream *bs); +GF_Err metx_Size(GF_Box *s); +GF_Err metx_dump(GF_Box *a, FILE * trace); + + + +GF_Box *tsel_New(); +void tsel_del(GF_Box *s); +GF_Err tsel_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tsel_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tsel_Size(GF_Box *s); +GF_Err tsel_dump(GF_Box *a, FILE * trace); + + +GF_Box *dimC_New(); +void dimC_del(GF_Box *s); +GF_Err dimC_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dimC_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dimC_Size(GF_Box *s); +GF_Err dimC_dump(GF_Box *a, FILE * trace); + +GF_Box *dims_New(); +void dims_del(GF_Box *s); +GF_Err dims_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dims_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dims_Size(GF_Box *s); +GF_Err dims_dump(GF_Box *a, FILE * trace); + +GF_Box *diST_New(); +void diST_del(GF_Box *s); +GF_Err diST_Read(GF_Box *s, GF_BitStream *bs); +GF_Err diST_Write(GF_Box *s, GF_BitStream *bs); +GF_Err diST_Size(GF_Box *s); +GF_Err diST_dump(GF_Box *a, FILE * trace); + + +GF_Box *ac3_New(); +void ac3_del(GF_Box *s); +GF_Err ac3_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ac3_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ac3_Size(GF_Box *s); +GF_Err ac3_dump(GF_Box *a, FILE * trace); + +GF_Box *dac3_New(); +void dac3_del(GF_Box *s); +GF_Err dac3_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dac3_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dac3_Size(GF_Box *s); +GF_Err dac3_dump(GF_Box *a, FILE * trace); + +#ifdef __cplusplus +} +#endif + +#endif //_GF_ISOMEDIA_DEV_H_ + diff --git a/include/gpac/internal/laser_dev.h b/include/gpac/internal/laser_dev.h new file mode 100644 index 0000000..ddb8e7e --- /dev/null +++ b/include/gpac/internal/laser_dev.h @@ -0,0 +1,337 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / LASeR codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_LASER_DEV_H_ +#define _GF_LASER_DEV_H_ + +#include <gpac/laser.h> + +/*per_stream config support*/ +typedef struct +{ + GF_LASERConfig cfg; + u16 ESID; +} LASeRStreamInfo; + +typedef struct +{ + /*colors can be encoded on up to 16 bits per comp*/ + u16 r, g, b; +} LSRCol; + +struct __tag_laser_codec +{ + GF_BitStream *bs; + GF_SceneGraph *sg; + GF_Err last_error; + + /*all attached streams*/ + GF_List *streamInfo; + + LASeRStreamInfo *info; + Fixed res_factor/*2^-coord_res*/; + /*duplicated from config*/ + u8 scale_bits; + u8 coord_bits; + u16 time_resolution; + u16 color_scale; + + LSRCol *col_table; + u32 nb_cols; + /*computed dynamically*/ + u32 colorIndexBits; + GF_List *font_table; + u32 fontIndexBits; + + u32 privateData_id_index, privateTag_index; + + /*decoder only*/ + Double (*GetSceneTime)(void *cbk); + void *cbk; + + /*sameElement coding*/ + SVG_Element *prev_g; + SVG_Element *prev_line; + SVG_Element *prev_path; + SVG_Element *prev_polygon; + SVG_Element *prev_rect; + SVG_Element *prev_text; + SVG_Element *prev_use; + GF_Node *current_root; + + /*0: normal playback, store script content + 1: memory decoding of scene, decompress script into commands + */ + Bool memory_dec; + + GF_List *defered_hrefs; + GF_List *defered_anims; + GF_List *defered_listeners; + + char *cache_dir, *service_name; + GF_List *unresolved_commands; +}; + +s32 gf_lsr_anim_type_from_attribute(u32 tag); +s32 gf_lsr_anim_type_to_attribute(u32 tag); +s32 gf_lsr_rare_type_from_attribute(u32 tag); +s32 gf_lsr_rare_type_to_attribute(u32 tag); +u32 gf_lsr_same_rare(SVGAllAttributes *elt_atts, SVGAllAttributes *base_atts); + + +/*transform*/ +#define RARE_TRANSFORM 47 + +enum +{ + LSR_EVT_abort = 0, + LSR_EVT_accessKey = 1, + LSR_EVT_activate = 2, + LSR_EVT_activatedEvent = 3, + LSR_EVT_beginEvent = 4, + LSR_EVT_click = 5, + LSR_EVT_deactivatedEvent = 6, + LSR_EVT_endEvent = 7, + LSR_EVT_error = 8, + LSR_EVT_executionTime = 9, + LSR_EVT_focusin = 10, + LSR_EVT_focusout = 11, + LSR_EVT_keydown = 12, + LSR_EVT_keyup = 13, + LSR_EVT_load = 14, + LSR_EVT_longAccessKey = 15, + LSR_EVT_mousedown = 16, + LSR_EVT_mousemove = 17, + LSR_EVT_mouseout = 18, + LSR_EVT_mouseover = 19, + LSR_EVT_mouseup = 20, + LSR_EVT_pause = 21, + LSR_EVT_pausedEvent = 22, + LSR_EVT_play = 23, + LSR_EVT_repeatEvent = 24, + LSR_EVT_repeatKey = 25, + LSR_EVT_resize = 26, + LSR_EVT_resumedEvent = 27, + LSR_EVT_scroll = 28, + LSR_EVT_shortAccessKey = 29, + LSR_EVT_textinput = 30, + LSR_EVT_unload = 31, + LSR_EVT_zoom = 32 +}; + +u32 dom_to_lsr_key(u32 dom_k); + + +#define LSR_UPDATE_TYPE_ROTATE 76 +#define LSR_UPDATE_TYPE_SCALE 79 +#define LSR_UPDATE_TYPE_SVG_HEIGHT 94 +#define LSR_UPDATE_TYPE_SVG_WIDTH 95 +#define LSR_UPDATE_TYPE_TEXT_CONTENT 107 +#define LSR_UPDATE_TYPE_TRANSFORM 108 +#define LSR_UPDATE_TYPE_TRANSLATION 110 + + +/*LASeR commands code*/ +enum +{ + LSR_UPDATE_ADD = 0, + LSR_UPDATE_CLEAN, + LSR_UPDATE_DELETE, + LSR_UPDATE_INSERT, + LSR_UPDATE_NEW_SCENE, + LSR_UPDATE_REFRESH_SCENE, + LSR_UPDATE_REPLACE, + LSR_UPDATE_RESTORE, + LSR_UPDATE_SAVE, + LSR_UPDATE_SEND_EVENT, + LSR_UPDATE_EXTEND, + LSR_UPDATE_TEXT_CONTENT +}; + +/*Code point Path code*/ +enum +{ + LSR_PATH_COM_C = 0, + LSR_PATH_COM_H, + LSR_PATH_COM_L, + LSR_PATH_COM_M, + LSR_PATH_COM_Q, + LSR_PATH_COM_S, + LSR_PATH_COM_T, + LSR_PATH_COM_V, + LSR_PATH_COM_Z, + LSR_PATH_COM_c, + LSR_PATH_COM_h, + LSR_PATH_COM_l, + LSR_PATH_COM_m, + LSR_PATH_COM_q, + LSR_PATH_COM_s, + LSR_PATH_COM_t, + LSR_PATH_COM_v, + LSR_PATH_COM_z +}; + + + + +enum +{ + LSR_SCENE_CONTENT_MODEL_a = 0, + LSR_SCENE_CONTENT_MODEL_animate, + LSR_SCENE_CONTENT_MODEL_animateColor, + LSR_SCENE_CONTENT_MODEL_animateMotion, + LSR_SCENE_CONTENT_MODEL_animateTransform, + LSR_SCENE_CONTENT_MODEL_audio, + LSR_SCENE_CONTENT_MODEL_circle, + LSR_SCENE_CONTENT_MODEL_defs, + LSR_SCENE_CONTENT_MODEL_desc, + LSR_SCENE_CONTENT_MODEL_ellipse, + LSR_SCENE_CONTENT_MODEL_foreignObject, + LSR_SCENE_CONTENT_MODEL_g, + LSR_SCENE_CONTENT_MODEL_image, + LSR_SCENE_CONTENT_MODEL_line, + LSR_SCENE_CONTENT_MODEL_linearGradient, + LSR_SCENE_CONTENT_MODEL_metadata, + LSR_SCENE_CONTENT_MODEL_mpath, + LSR_SCENE_CONTENT_MODEL_path, + LSR_SCENE_CONTENT_MODEL_polygon, + LSR_SCENE_CONTENT_MODEL_polyline, + LSR_SCENE_CONTENT_MODEL_radialGradient, + LSR_SCENE_CONTENT_MODEL_rect, + LSR_SCENE_CONTENT_MODEL_sameg, + LSR_SCENE_CONTENT_MODEL_sameline, + LSR_SCENE_CONTENT_MODEL_samepath, + LSR_SCENE_CONTENT_MODEL_samepathfill, + LSR_SCENE_CONTENT_MODEL_samepolygon, + LSR_SCENE_CONTENT_MODEL_samepolygonfill, + LSR_SCENE_CONTENT_MODEL_samepolygonstroke, + LSR_SCENE_CONTENT_MODEL_samepolyline, + LSR_SCENE_CONTENT_MODEL_samepolylinefill, + LSR_SCENE_CONTENT_MODEL_samepolylinestroke, + LSR_SCENE_CONTENT_MODEL_samerect, + LSR_SCENE_CONTENT_MODEL_samerectfill, + LSR_SCENE_CONTENT_MODEL_sametext, + LSR_SCENE_CONTENT_MODEL_sametextfill, + LSR_SCENE_CONTENT_MODEL_sameuse, + LSR_SCENE_CONTENT_MODEL_script, + LSR_SCENE_CONTENT_MODEL_set, + LSR_SCENE_CONTENT_MODEL_stop, + LSR_SCENE_CONTENT_MODEL_switch, + LSR_SCENE_CONTENT_MODEL_text, + LSR_SCENE_CONTENT_MODEL_title, + LSR_SCENE_CONTENT_MODEL_tspan, + LSR_SCENE_CONTENT_MODEL_use, + LSR_SCENE_CONTENT_MODEL_video, + LSR_SCENE_CONTENT_MODEL_listener, + LSR_SCENE_CONTENT_MODEL_conditional, + LSR_SCENE_CONTENT_MODEL_cursorManager, + LSR_SCENE_CONTENT_MODEL_element_any, + LSR_SCENE_CONTENT_MODEL_privateContainer, + LSR_SCENE_CONTENT_MODEL_rectClip, + LSR_SCENE_CONTENT_MODEL_selector, + LSR_SCENE_CONTENT_MODEL_simpleLayout, + LSR_SCENE_CONTENT_MODEL_textContent, + LSR_SCENE_CONTENT_MODEL_extension, +}; + +enum +{ + LSR_UPDATE_CONTENT_MODEL_a = 0, + LSR_UPDATE_CONTENT_MODEL_animate, + LSR_UPDATE_CONTENT_MODEL_animateColor, + LSR_UPDATE_CONTENT_MODEL_animateMotion, + LSR_UPDATE_CONTENT_MODEL_animateTransform, + LSR_UPDATE_CONTENT_MODEL_audio, + LSR_UPDATE_CONTENT_MODEL_circle, + LSR_UPDATE_CONTENT_MODEL_defs, + LSR_UPDATE_CONTENT_MODEL_desc, + LSR_UPDATE_CONTENT_MODEL_ellipse, + LSR_UPDATE_CONTENT_MODEL_foreignObject, + LSR_UPDATE_CONTENT_MODEL_g, + LSR_UPDATE_CONTENT_MODEL_image, + LSR_UPDATE_CONTENT_MODEL_line, + LSR_UPDATE_CONTENT_MODEL_linearGradient, + LSR_UPDATE_CONTENT_MODEL_metadata, + LSR_UPDATE_CONTENT_MODEL_mpath, + LSR_UPDATE_CONTENT_MODEL_path, + LSR_UPDATE_CONTENT_MODEL_polygon, + LSR_UPDATE_CONTENT_MODEL_polyline, + LSR_UPDATE_CONTENT_MODEL_radialGradient, + LSR_UPDATE_CONTENT_MODEL_rect, + LSR_UPDATE_CONTENT_MODEL_script, + LSR_UPDATE_CONTENT_MODEL_set, + LSR_UPDATE_CONTENT_MODEL_stop, + LSR_UPDATE_CONTENT_MODEL_svg, + LSR_UPDATE_CONTENT_MODEL_switch, + LSR_UPDATE_CONTENT_MODEL_text, + LSR_UPDATE_CONTENT_MODEL_title, + LSR_UPDATE_CONTENT_MODEL_tspan, + LSR_UPDATE_CONTENT_MODEL_use, + LSR_UPDATE_CONTENT_MODEL_video, + LSR_UPDATE_CONTENT_MODEL_listener, +}; + +enum +{ + LSR_UPDATE_CONTENT_MODEL2_conditional = 0, + LSR_UPDATE_CONTENT_MODEL2_cursorManager, + LSR_UPDATE_CONTENT_MODEL2_extend, + LSR_UPDATE_CONTENT_MODEL2_private, + LSR_UPDATE_CONTENT_MODEL2_rectClip, + LSR_UPDATE_CONTENT_MODEL2_selector, + LSR_UPDATE_CONTENT_MODEL2_simpleLayout, +}; + +/*just to remember them, not implemented yet*/ +enum +{ + LSR_SVG12_EXT_animation = 0, + LSR_SVG12_EXT_discard, + LSR_SVG12_EXT_font, + LSR_SVG12_EXT_font_face, + LSR_SVG12_EXT_font_face_src, + LSR_SVG12_EXT_font_face_uri, + LSR_SVG12_EXT_glyph, + LSR_SVG12_EXT_handler, + LSR_SVG12_EXT_hkern, + LSR_SVG12_EXT_missingGlyph, + LSR_SVG12_EXT_prefetch, + LSR_SVG12_EXT_solidColor, + LSR_SVG12_EXT_tBreak, + LSR_SVG12_EXT_textArea, +}; + +/*just to remember them, not implemented yet*/ +enum +{ + LSR_AMD1_EXT_animateScroll = 0, + LSR_AMD1_EXT_setScroll, + LSR_AMD1_EXT_streamSource, + LSR_AMD1_EXT_updateSource, +}; + +#endif + diff --git a/include/gpac/internal/media_dev.h b/include/gpac/internal/media_dev.h new file mode 100644 index 0000000..cebcf4d --- /dev/null +++ b/include/gpac/internal/media_dev.h @@ -0,0 +1,148 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_MEDIA_DEV_H_ +#define _GF_MEDIA_DEV_H_ + +#include <gpac/media_tools.h> + +GF_Err gf_import_message(GF_MediaImporter *import, GF_Err e, char *format, ...); + +/*returns 0 if not a start code, or size of start code (3 or 4 bytes). If start code, bitstream +is positionned AFTER start code*/ +u32 AVC_IsStartCode(GF_BitStream *bs); +/*returns size of chunk between current and next startcode (excluding startcode sizes), 0 if no more startcodes (eos)*/ +u32 AVC_NextStartCode(GF_BitStream *bs); +/*returns NAL unit type - bitstream must be sync'ed!!*/ +u8 AVC_NALUType(GF_BitStream *bs); +/*slice NALU*/ +Bool AVC_NALUIsSlice(u8 type); + + +typedef struct +{ + s32 profile_idc; + s32 level_idc; + s32 prof_compat; + s32 log2_max_frame_num; + u32 poc_type, poc_cycle_length; + s32 log2_max_poc_lsb; + s32 delta_pic_order_always_zero_flag; + s32 offset_for_non_ref_pic, offset_for_top_to_bottom_field; + Bool frame_mbs_only_flag; + + s16 offset_for_ref_frame[256]; + + s32 timing_info_present_flag; + u32 num_units_in_tick; + u32 time_scale; + s32 fixed_frame_rate_flag; + + u32 width, height; + u32 par_num, par_den; + /*used to discard repeated SPSs - 0: not parsed, 1 parsed, 2 sent*/ + u32 status; +} AVC_SPS; + +typedef struct +{ + s32 sps_id; + s32 pic_order_present; /* pic_order_present_flag*/ + s32 redundant_pic_cnt_present; /* redundant_pic_cnt_present_flag */ + int slice_group_count; /* num_slice_groups_minus1 + 1*/ + /*used to discard repeated SPSs - 0: not parsed, 1 parsed, 2 sent*/ + u32 status; +} AVC_PPS; + +typedef struct +{ + u8 nal_ref_idc, nal_unit_type, field_pic_flag, bottom_field_flag; + u32 frame_num, idr_pic_id, poc_lsb, slice_type; + s32 delta_poc_bottom; + s32 delta_poc[2]; + s32 redundant_pic_cnt; + + s32 poc; + u32 poc_msb, poc_msb_prev, poc_lsb_prev, frame_num_prev; + s32 frame_num_offset, frame_num_offset_prev; + + AVC_SPS *sps; + AVC_PPS *pps; +} AVCSliceInfo; + + +typedef struct +{ + u32 frame_cnt; + u8 exact_match_flag; + u8 broken_link_flag; + u8 changing_slice_group_idc; + u8 valid; +} AVCSeiRecoveryPoint; + + +typedef struct +{ + AVCSeiRecoveryPoint recovery_point; + /*to be eventually completed by other sei*/ + +} AVCSei; + +typedef struct +{ + AVC_SPS sps[32]; + AVC_PPS pps[255]; + + AVCSliceInfo s_info; + AVCSei sei; +} AVCState; + +/*return sps ID or -1 if error*/ +s32 AVC_ReadSeqInfo(GF_BitStream *bs, AVCState *avc, u32 *vui_flag_pos); +/*return pps ID or -1 if error*/ +s32 AVC_ReadPictParamSet(GF_BitStream *bs, AVCState *avc); +/*is slice a RAP*/ +Bool AVC_SliceIsIDR(AVCState *avc); +/*parses NALU, updates avc state and returns: + 1 if NALU part of new frame + 0 if NALU part of prev frame + -1 if bitstream error +*/ +s32 AVC_ParseNALU(GF_BitStream *bs, u32 nal_hdr, AVCState *avc); +/*remove SEI messages not allowed in MP4*/ +u32 AVC_ReformatSEI_NALU(char *buffer, u32 nal_size, AVCState *avc); + +GF_Err AVC_ChangePAR(GF_AVCConfig *avcc, s32 ar_n, s32 ar_d); + + +typedef struct +{ + u8 rate_idx; + u8 pck_size; +} QCPRateTable; + + +#endif /*_GF_MEDIA_DEV_H_*/ + diff --git a/include/gpac/internal/mesh.h b/include/gpac/internal/mesh.h new file mode 100644 index 0000000..57fba27 --- /dev/null +++ b/include/gpac/internal/mesh.h @@ -0,0 +1,285 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_MESH_H_ +#define _GF_MESH_H_ + +#include <gpac/scenegraph_vrml.h> +#include <gpac/path2d.h> + +/*by default we store each color on 32 bit rather than 4 floats (128 bits)*/ + +//#define MESH_USE_SFCOLOR + +#ifdef MESH_USE_SFCOLOR +#define MESH_MAKE_COL(_argb) _argb +#define MESH_GET_COLOR(_argb, _vertex) _argb = (_vertex).color; +#else +#define MESH_MAKE_COL(_argb) GF_COL_ARGB(FIX2INT(255*(_argb.alpha)), FIX2INT(255*(_argb.blue)), FIX2INT(255*(_argb.green)), FIX2INT(255*(_argb.red))) +#define MESH_GET_COLOR(_argb, _vertex) { _argb.alpha = INT2FIX(GF_COL_A((_vertex).color))/255; _argb.red = INT2FIX(GF_COL_R((_vertex).color))/255; _argb.green = INT2FIX(GF_COL_G((_vertex).color))/255; _argb.blue = INT2FIX(GF_COL_B((_vertex).color))/255; } +#endif + +/*by default we store normals as signed bytes rather than floats*/ + +//#define MESH_USE_FIXED_NORMAL + +#ifdef MESH_USE_FIXED_NORMAL +#define MESH_SET_NORMAL(_vertex, _nor) _vertex.normal = _nor; +#define MESH_GET_NORMAL(_nor, _vertex) _nor = _vertex.normal; +#define MESH_NORMAL_UNIT FIX_ONE +#else + +typedef struct +{ + s8 x, y, z; + s8 __dummy; +} SFVec3f_bytes; + +#define MESH_NORMAL_UNIT 1 + +#ifdef GPAC_FIXED_POINT +#define MESH_SET_NORMAL(_vertex, _nor) { SFVec3f_bytes __nor; __nor.x = (s8) FIX2INT(_nor.x*100); __nor.y = (s8) FIX2INT(_nor.y*100); __nor.z = (s8) FIX2INT(_nor.z*100); __nor.__dummy=0; _vertex.normal = __nor; } +#define MESH_GET_NORMAL(_nor, _vertex) { (_nor).x = INT2FIX(_vertex.normal.x); (_nor).y = INT2FIX(_vertex.normal.y); (_nor).z = INT2FIX(_vertex.normal.z); gf_vec_norm(&(_nor)); } +#else +#define MESH_SET_NORMAL(_vertex, _nor) { SFVec3f_bytes __nor; __nor.x = (s8) (_nor.x*100); __nor.y = (s8) (_nor.y*100); __nor.z = (s8) (_nor.z*100); __nor.__dummy=0; _vertex.normal = __nor; } +#define MESH_GET_NORMAL(_nor, _vertex) { (_nor).x = _vertex.normal.x; (_nor).y = _vertex.normal.y; (_nor).z = _vertex.normal.z; gf_vec_norm(&(_nor)); } +#endif + +#endif + +typedef struct +{ + /*position*/ + SFVec3f pos; + /*texture coordinates*/ + SFVec2f texcoords; + /*normal*/ +#ifdef MESH_USE_FIXED_NORMAL + SFVec3f normal; +#else + SFVec3f_bytes normal; +#endif + /*color if used by mesh object*/ +#ifdef MESH_USE_SFCOLOR + SFColorRGBA color; +#else + u32 color; +#endif +} GF_Vertex; + +/*mesh type used*/ +enum +{ + /*default: triangles described by indices (nb triangles = nb indices / 3) */ + MESH_TRIANGLES = 0, + /*point set: indices is meaningless*/ + MESH_POINTSET, + /*line set: lines described by indices (nb lines = nb indices / 2) */ + MESH_LINESET, +}; + +/*mesh flags*/ +enum +{ + /*vertex.color is used*/ + MESH_HAS_COLOR = 1, + /*mesh is 2D: normal should be ignored and a global normal set to 0 0 1*/ + MESH_IS_2D = 1<<1, + /*mesh has no texture coords - disable texturing*/ + MESH_NO_TEXTURE = 1<<2, + /*mesh faces are clockwise*/ + MESH_IS_CW = 1<<3, + /*mesh is solid (back face culling + 2 side lighting)*/ + MESH_IS_SOLID = 1<<4, + /*mesh has smoothed normals*/ + MESH_IS_SMOOTHED = 1<<5, + /*vertex.color is used with alpha channel*/ + MESH_HAS_ALPHA = 1<<6, +}; + +/*indexes as used in glDrawElements - note that integer type is not allowed with oglES*/ +#ifdef GPAC_USE_OGL_ES +#define IDX_TYPE u16 +#else +#define IDX_TYPE u32 +#endif + +/*mesh object used by all 2D/3D primitives. */ +typedef struct __gf_mesh +{ + /*vertex list*/ + u32 v_count, v_alloc; + GF_Vertex *vertices; + /*triangle indexes*/ + u32 i_count, i_alloc; + IDX_TYPE *indices; + + /*one of the above type*/ + u32 mesh_type; + + /*one of the above flags*/ + u32 flags; + + /*bounds info: bounding box and bounding sphere radius*/ + GF_BBox bounds; + + /*aabb tree of the mesh if any*/ + struct __AABBNode *aabb_root; + /*triangle indexes used in AABB tree - order may be different than the one in mesh->indices*/ + IDX_TYPE *aabb_indices; +// u32 aabb_nb_index; +} GF_Mesh; + +GF_Mesh *new_mesh(); +void mesh_free(GF_Mesh *mesh); +/*reset mesh*/ +void mesh_reset(GF_Mesh *mesh); +/*recompute mesh bounds*/ +void mesh_update_bounds(GF_Mesh *mesh); +/*adds new vertex*/ +void mesh_set_vertex_vx(GF_Mesh *mesh, GF_Vertex *vx); +/*adds new vertex (exported for tesselator only)*/ +void mesh_set_vertex(GF_Mesh *mesh, Fixed x, Fixed y, Fixed z, Fixed nx, Fixed ny, Fixed nz, Fixed u, Fixed v); +/*adds an index (exported for tesselator only)*/ +void mesh_set_index(GF_Mesh *mesh, u32 idx); +/*adds an point & associated color, normal set to NULL*/ +void mesh_set_point(GF_Mesh *mesh, Fixed x, Fixed y, Fixed z, SFColorRGBA col); +/*adds an index (exported for tesselator only)*/ +void mesh_set_triangle(GF_Mesh *mesh, u32 id1, u32 id2, u32 id3); +/*make dest mesh the clone of orig*/ +void mesh_clone(GF_Mesh *dest, GF_Mesh *orig); +/*recompute all normals*/ +void mesh_recompute_normals(GF_Mesh *mesh); +/*generate texture coordinate - ONLY LOCAL MODES SUPPORTED FOR NOW*/ +void mesh_generate_tex_coords(GF_Mesh *mesh, GF_Node *__texCoords); + +/*inserts a box (lines only) of size 1.0 1.0 1.0*/ +void mesh_new_unit_bbox(GF_Mesh *mesh); + +/*insert base primitives - low res indicates less subdivision steps for circles (cone, cylinder, ellipse, sphere)*/ +void mesh_new_rectangle(GF_Mesh *mesh, SFVec2f size); +void mesh_new_ellipse(GF_Mesh *mesh, Fixed a_dia, Fixed b_dia, Bool low_res); +void mesh_new_box(GF_Mesh *mesh, SFVec3f size); +void mesh_new_cylinder(GF_Mesh *mesh, Fixed height, Fixed radius, Bool bottom, Bool side, Bool top, Bool low_res); +void mesh_new_cone(GF_Mesh *mesh, Fixed height, Fixed radius, Bool bottom, Bool side, Bool low_res); +void mesh_new_sphere(GF_Mesh *mesh, Fixed radius, Bool low_res); +/*inserts ILS/ILS2D and IFS2D outline when not filled*/ +void mesh_new_ils(GF_Mesh *mesh, GF_Node *__coord, MFInt32 *coordIndex, GF_Node *__color, MFInt32 *colorIndex, Bool colorPerVertex, Bool do_close); +/*inserts IFS2D*/ +void mesh_new_ifs2d(GF_Mesh *mesh, GF_Node *ifs2d); +/*inserts IFS*/ +void mesh_new_ifs(GF_Mesh *mesh, GF_Node *ifs); +/*inserts PS/PS2D*/ +void mesh_new_ps(GF_Mesh *mesh, GF_Node *__coord, GF_Node *__color); +/*inserts ElevationGrid*/ +void mesh_new_elevation_grid(GF_Mesh *mesh, GF_Node *eg); +/*inserts Extrusion*/ +void mesh_new_extrusion(GF_Mesh *mesh, GF_Node *ext); +/*builds mesh from path, performing tesselation if desired*/ +void mesh_from_path(GF_Mesh *mesh, GF_Path *path); +/*builds mesh for outline of the given path*/ +void mesh_get_outline(GF_Mesh *mesh, GF_Path *path); +/*constructs an extrusion from given path - mesh is reseted, txcoords computed from path bounds +@thespine: spine line +@creaseAngle: creaseAngle for normal smoothing, 0 for no smoothing +begin_cap, end_cap: indicates whether start/end faces shall be added +@spine_ori: orientation at spine points +@spine_scale: scale at spine points +@tx_along_spine: if set, texture coords are generated so that the texture is mapped on the side, +otherwise the same txcoords are used all along the extrusion spine +*/ +void mesh_extrude_path(GF_Mesh *mesh, GF_Path *path, MFVec3f *thespine, Fixed creaseAngle, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool tx_along_spine); +/*special extension of the above: APPENDS an extrusion from given path - mesh is NOT reseted, txcoords are computed based on min_cx, min_cy, width_cx, width_cy*/ +void mesh_extrude_path_ext(GF_Mesh *mesh, GF_Path *path, MFVec3f *thespine, Fixed creaseAngle, Fixed min_cx, Fixed min_cy, Fixed width_cx, Fixed width_cy, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool tx_along_spine); + +/*returns 1 if intersection and set outPoint to closest intersection, 0 otherwise*/ +Bool gf_mesh_intersect_ray(GF_Mesh *mesh, GF_Ray *r, SFVec3f *outPoint, SFVec3f *outNormal, SFVec2f *outTexCoords); +/*returns 1 if any face is less than min_dist from pos, with collision point on closest face (towards pos)*/ +Bool gf_mesh_closest_face(GF_Mesh *mesh, SFVec3f pos, Fixed min_dist, SFVec3f *outPoint); + + + + +/*AABB tree node (exported for bounds drawing)*/ +typedef struct __AABBNode +{ + /*bbox*/ + SFVec3f min, max; + /*sorted indices in mesh indices list*/ + IDX_TYPE *indices; + /*nb triangles*/ + u32 nb_idx; + /*children nodes, NULL if leaf*/ + struct __AABBNode *pos, *neg; +} AABBNode; + +/*tree construction modes*/ +enum +{ + /*AABB tree is not used*/ + AABB_NONE, + /*longest box axis is used to divide an AABB node*/ + AABB_LONGEST, + /*keep tree well-balanced*/ + AABB_BALANCED, + /*best axis is use: test largest, then middle, then smallest axis*/ + AABB_BEST_AXIS, + /*use variance to pick axis*/ + AABB_SPLATTER, + /*fifty/fifty point split*/ + AABB_FIFTY, +}; + +void gf_mesh_build_aabbtree(GF_Mesh *mesh); + + +/* + * tesselation functions + */ + +/*appends given face (and tesselate if needed) to the mesh. Only vertices are used in the face +indices are ignored. +partially implemented on ogl-ES*/ +void TesselateFaceMesh(GF_Mesh *mesh, GF_Mesh *face); + +#ifndef GPAC_USE_OGL_ES +/*converts 2D path into a polygon - these are only partially implemented when using oglES +for_outline: + 0, regular odd/even windining rule with texCoords + 1, zero-non-zero windining rule without texCoords + 2, zero-non-zero windining rule with texCoords +*/ +void TesselatePath(GF_Mesh *mesh, GF_Path *path, u32 outline_style); + +/*appends given face (and tesselate if needed) to the mesh. Only vertices are used in the face +indices are ignored. +Same as TesselateFaceMesh + faces info to determine where are the polygons in the face - used by extruder only +*/ +void TesselateFaceMeshComplex(GF_Mesh *dest, GF_Mesh *orig, u32 nbFaces, u32 *ptsPerFaces); + +#endif + +#endif /*_GF_MESH_H_*/ + diff --git a/include/gpac/internal/odf_dev.h b/include/gpac/internal/odf_dev.h new file mode 100644 index 0000000..8ff4046 --- /dev/null +++ b/include/gpac/internal/odf_dev.h @@ -0,0 +1,364 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_OD_DEV_H_ +#define _GF_OD_DEV_H_ + +#include <gpac/mpeg4_odf.h> + +/*read-write OD formatted strings*/ +GF_Err gf_odf_read_url_string(GF_BitStream *bs, char **string, u32 *readBytes); +GF_Err gf_odf_write_url_string(GF_BitStream *bs, char *string); +u32 gf_odf_size_url_string(char *string); + +/*descriptors base functions*/ +GF_Descriptor *gf_odf_create_descriptor(u8 tag); +GF_Err gf_odf_delete_descriptor(GF_Descriptor *desc); +GF_Err gf_odf_parse_descriptor(GF_BitStream *bs, GF_Descriptor **desc, u32 *size); +GF_Err gf_odf_read_descriptor(GF_BitStream *bs, GF_Descriptor *desc, u32 DescSize); +GF_Err gf_odf_write_base_descriptor(GF_BitStream *bs, u8 tag, u32 size); +GF_Err gf_odf_write_descriptor(GF_BitStream *bs, GF_Descriptor *desc); +GF_Err gf_odf_size_descriptor(GF_Descriptor *desc, u32 *outSize); +GF_Err gf_odf_delete_descriptor_list(GF_List *descList); +GF_Err gf_odf_write_descriptor_list(GF_BitStream *bs, GF_List *descList); +GF_Err gf_odf_write_descriptor_list_filter(GF_BitStream *bs, GF_List *descList, u8 tag_only); +GF_Err gf_odf_size_descriptor_list(GF_List *descList, u32 *outSize); + +/*handle lazy bitstreams where SizeOfInstance is always encoded on 4 bytes*/ +s32 gf_odf_size_field_size(u32 size_desc); + +GF_Err DumpDescList(GF_List *list, FILE *trace, u32 indent, const char *ListName, Bool XMTDump, Bool no_skip_empty); + +/*IPMPX tools*/ +u32 gf_ipmpx_array_size(GF_BitStream *bs, u32 *array_size); +void gf_ipmpx_write_array(GF_BitStream *bs, char *data, u32 data_len); + +/*QoS qualifiers base functions*/ +GF_Err gf_odf_parse_qos_qual(GF_BitStream *bs, GF_QoS_Default **qos_qual, u32 *qos_size); +void gf_odf_delete_qos_qual(GF_QoS_Default *qos); +GF_Err gf_odf_size_qos_qual(GF_QoS_Default *qos); +GF_Err gf_odf_write_qos_qual(GF_BitStream *bs, GF_QoS_Default *qos); + +GF_Descriptor *gf_odf_new_iod(); +GF_Descriptor *gf_odf_new_esd(); +GF_Descriptor *gf_odf_new_dcd(); +GF_Descriptor *gf_odf_new_slc(u8 predef); +GF_Descriptor *gf_odf_new_cc(); +GF_Descriptor *gf_odf_new_cc_date(); +GF_Descriptor *gf_odf_new_cc_name(); +GF_Descriptor *gf_odf_new_ci(); +GF_Descriptor *gf_odf_new_default(); +GF_Descriptor *gf_odf_new_esd_inc(); +GF_Descriptor *gf_odf_new_esd_ref(); +GF_Descriptor *gf_odf_new_exp_text(); +GF_Descriptor *gf_odf_new_pl_ext(); +GF_Descriptor *gf_odf_new_ipi_ptr(); +GF_Descriptor *gf_odf_new_ipmp(); +GF_Descriptor *gf_odf_new_ipmp_ptr(); +GF_Descriptor *gf_odf_new_kw(); +GF_Descriptor *gf_odf_new_lang(); +GF_Descriptor *gf_odf_new_isom_iod(); +GF_Descriptor *gf_odf_new_isom_od(); +GF_Descriptor *gf_odf_new_od(); +GF_Descriptor *gf_odf_new_oci_date(); +GF_Descriptor *gf_odf_new_oci_name(); +GF_Descriptor *gf_odf_new_pl_idx(); +GF_Descriptor *gf_odf_new_qos(); +GF_Descriptor *gf_odf_new_rating(); +GF_Descriptor *gf_odf_new_reg(); +GF_Descriptor *gf_odf_new_short_text(); +GF_Descriptor *gf_odf_new_smpte_camera(); +GF_Descriptor *gf_odf_new_sup_cid(); +GF_Descriptor *gf_odf_new_segment(); +GF_Descriptor *gf_odf_new_mediatime(); +GF_Descriptor *gf_odf_new_ipmp_tool_list(); +GF_Descriptor *gf_odf_new_ipmp_tool(); +GF_Descriptor *gf_odf_new_muxinfo(); +GF_Descriptor *gf_odf_New_ElemMask(); +GF_Descriptor *gf_odf_new_bifs_cfg(); +GF_Descriptor *gf_odf_new_ui_cfg(); +GF_Descriptor *gf_odf_new_laser_cfg(); +GF_Descriptor *gf_odf_new_auxvid(); + + +GF_Err gf_odf_del_iod(GF_InitialObjectDescriptor *iod); +GF_Err gf_odf_del_esd(GF_ESD *esd); +GF_Err gf_odf_del_dcd(GF_DecoderConfig *dcd); +GF_Err gf_odf_del_slc(GF_SLConfig *sl); +GF_Err gf_odf_del_cc(GF_CCDescriptor *ccd); +GF_Err gf_odf_del_cc_date(GF_CC_Date *cdd); +GF_Err gf_odf_del_cc_name(GF_CC_Name *cnd); +GF_Err gf_odf_del_ci(GF_CIDesc *cid); +GF_Err gf_odf_del_default(GF_DefaultDescriptor *dd); +GF_Err gf_odf_del_esd_inc(GF_ES_ID_Inc *esd_inc); +GF_Err gf_odf_del_esd_ref(GF_ES_ID_Ref *esd_ref); +GF_Err gf_odf_del_exp_text(GF_ExpandedTextual *etd); +GF_Err gf_odf_del_pl_ext(GF_PLExt *pld); +GF_Err gf_odf_del_ipi_ptr(GF_IPIPtr *ipid); +GF_Err gf_odf_del_ipmp(GF_IPMP_Descriptor *ipmp); +GF_Err gf_odf_del_ipmp_ptr(GF_IPMPPtr *ipmpd); +GF_Err gf_odf_del_kw(GF_KeyWord *kwd); +GF_Err gf_odf_del_lang(GF_Language *ld); +GF_Err gf_odf_del_isom_iod(GF_IsomInitialObjectDescriptor *iod); +GF_Err gf_odf_del_isom_od(GF_IsomObjectDescriptor *od); +GF_Err gf_odf_del_od(GF_ObjectDescriptor *od); +GF_Err gf_odf_del_oci_date(GF_OCI_Data *ocd); +GF_Err gf_odf_del_oci_name(GF_OCICreators *ocn); +GF_Err gf_odf_del_pl_idx(GF_PL_IDX *plid); +GF_Err gf_odf_del_qos(GF_QoS_Descriptor *qos); +GF_Err gf_odf_del_rating(GF_Rating *rd); +GF_Err gf_odf_del_reg(GF_Registration *reg); +GF_Err gf_odf_del_short_text(GF_ShortTextual *std); +GF_Err gf_odf_del_smpte_camera(GF_SMPTECamera *cpd); +GF_Err gf_odf_del_sup_cid(GF_SCIDesc *scid); +GF_Err gf_odf_del_segment(GF_Segment *sd); +GF_Err gf_odf_del_mediatime(GF_MediaTime *mt); +GF_Err gf_odf_del_ipmp_tool_list(GF_IPMP_ToolList *ipmptl); +GF_Err gf_odf_del_ipmp_tool(GF_IPMP_Tool *ipmp); +GF_Err gf_odf_del_muxinfo(GF_MuxInfo *mi); +GF_Err gf_odf_del_bifs_cfg(GF_BIFSConfig *desc); +GF_Err gf_odf_del_ui_cfg(GF_UIConfig *desc); +GF_Err gf_odf_del_laser_cfg(GF_LASERConfig *desc); +GF_Err gf_odf_del_auxvid(GF_AuxVideoDescriptor *ld); + +GF_Err gf_odf_read_iod(GF_BitStream *bs, GF_InitialObjectDescriptor *iod, u32 DescSize); +GF_Err gf_odf_read_esd(GF_BitStream *bs, GF_ESD *esd, u32 DescSize); +GF_Err gf_odf_read_dcd(GF_BitStream *bs, GF_DecoderConfig *dcd, u32 DescSize); +GF_Err gf_odf_read_slc(GF_BitStream *bs, GF_SLConfig *sl, u32 DescSize); +GF_Err gf_odf_read_cc(GF_BitStream *bs, GF_CCDescriptor *ccd, u32 DescSize); +GF_Err gf_odf_read_cc_date(GF_BitStream *bs, GF_CC_Date *cdd, u32 DescSize); +GF_Err gf_odf_read_cc_name(GF_BitStream *bs, GF_CC_Name *cnd, u32 DescSize); +GF_Err gf_odf_read_ci(GF_BitStream *bs, GF_CIDesc *cid, u32 DescSize); +GF_Err gf_odf_read_default(GF_BitStream *bs, GF_DefaultDescriptor *dd, u32 DescSize); +GF_Err gf_odf_read_esd_inc(GF_BitStream *bs, GF_ES_ID_Inc *esd_inc, u32 DescSize); +GF_Err gf_odf_read_esd_ref(GF_BitStream *bs, GF_ES_ID_Ref *esd_ref, u32 DescSize); +GF_Err gf_odf_read_exp_text(GF_BitStream *bs, GF_ExpandedTextual *etd, u32 DescSize); +GF_Err gf_odf_read_pl_ext(GF_BitStream *bs, GF_PLExt *pld, u32 DescSize); +GF_Err gf_odf_read_ipi_ptr(GF_BitStream *bs, GF_IPIPtr *ipid, u32 DescSize); +GF_Err gf_odf_read_ipmp(GF_BitStream *bs, GF_IPMP_Descriptor *ipmp, u32 DescSize); +GF_Err gf_odf_read_ipmp_ptr(GF_BitStream *bs, GF_IPMPPtr *ipmpd, u32 DescSize); +GF_Err gf_odf_read_kw(GF_BitStream *bs, GF_KeyWord *kwd, u32 DescSize); +GF_Err gf_odf_read_lang(GF_BitStream *bs, GF_Language *ld, u32 DescSize); +GF_Err gf_odf_read_isom_iod(GF_BitStream *bs, GF_IsomInitialObjectDescriptor *iod, u32 DescSize); +GF_Err gf_odf_read_isom_od(GF_BitStream *bs, GF_IsomObjectDescriptor *od, u32 DescSize); +GF_Err gf_odf_read_od(GF_BitStream *bs, GF_ObjectDescriptor *od, u32 DescSize); +GF_Err gf_odf_read_oci_date(GF_BitStream *bs, GF_OCI_Data *ocd, u32 DescSize); +GF_Err gf_odf_read_oci_name(GF_BitStream *bs, GF_OCICreators *ocn, u32 DescSize); +GF_Err gf_odf_read_pl_idx(GF_BitStream *bs, GF_PL_IDX *plid, u32 DescSize); +GF_Err gf_odf_read_qos(GF_BitStream *bs, GF_QoS_Descriptor *qos, u32 DescSize); +GF_Err gf_odf_read_rating(GF_BitStream *bs, GF_Rating *rd, u32 DescSize); +GF_Err gf_odf_read_reg(GF_BitStream *bs, GF_Registration *reg, u32 DescSize); +GF_Err gf_odf_read_short_text(GF_BitStream *bs, GF_ShortTextual *std, u32 DescSize); +GF_Err gf_odf_read_smpte_camera(GF_BitStream *bs, GF_SMPTECamera *cpd, u32 DescSize); +GF_Err gf_odf_read_sup_cid(GF_BitStream *bs, GF_SCIDesc *scid, u32 DescSize); +GF_Err gf_odf_read_segment(GF_BitStream *bs, GF_Segment *sd, u32 DescSize); +GF_Err gf_odf_read_mediatime(GF_BitStream *bs, GF_MediaTime *mt, u32 DescSize); +GF_Err gf_odf_read_muxinfo(GF_BitStream *bs, GF_MuxInfo *mi, u32 DescSize); +GF_Err gf_odf_read_ipmp_tool_list(GF_BitStream *bs, GF_IPMP_ToolList *ipmptl, u32 DescSize); +GF_Err gf_odf_read_ipmp_tool(GF_BitStream *bs, GF_IPMP_Tool *ipmp, u32 DescSize); +GF_Err gf_odf_read_auxvid(GF_BitStream *bs, GF_AuxVideoDescriptor *ld, u32 DescSize); + +GF_Err gf_odf_size_iod(GF_InitialObjectDescriptor *iod, u32 *outSize); +GF_Err gf_odf_size_esd(GF_ESD *esd, u32 *outSize); +GF_Err gf_odf_size_dcd(GF_DecoderConfig *dcd, u32 *outSize); +GF_Err gf_odf_size_slc(GF_SLConfig *sl, u32 *outSize); +GF_Err gf_odf_size_cc(GF_CCDescriptor *ccd, u32 *outSize); +GF_Err gf_odf_size_cc_date(GF_CC_Date *cdd, u32 *outSize); +GF_Err gf_odf_size_cc_name(GF_CC_Name *cnd, u32 *outSize); +GF_Err gf_odf_size_ci(GF_CIDesc *cid, u32 *outSize); +GF_Err gf_odf_size_default(GF_DefaultDescriptor *dd, u32 *outSize); +GF_Err gf_odf_size_esd_inc(GF_ES_ID_Inc *esd_inc, u32 *outSize); +GF_Err gf_odf_size_esd_ref(GF_ES_ID_Ref *esd_ref, u32 *outSize); +GF_Err gf_odf_size_exp_text(GF_ExpandedTextual *etd, u32 *outSize); +GF_Err gf_odf_size_pl_ext(GF_PLExt *pld, u32 *outSize); +GF_Err gf_odf_size_ipi_ptr(GF_IPIPtr *ipid, u32 *outSize); +GF_Err gf_odf_size_ipmp(GF_IPMP_Descriptor *ipmp, u32 *outSize); +GF_Err gf_odf_size_ipmp_ptr(GF_IPMPPtr *ipmpd, u32 *outSize); +GF_Err gf_odf_size_kw(GF_KeyWord *kwd, u32 *outSize); +GF_Err gf_odf_size_lang(GF_Language *ld, u32 *outSize); +GF_Err gf_odf_size_isom_iod(GF_IsomInitialObjectDescriptor *iod, u32 *outSize); +GF_Err gf_odf_size_isom_od(GF_IsomObjectDescriptor *od, u32 *outSize); +GF_Err gf_odf_size_od(GF_ObjectDescriptor *od, u32 *outSize); +GF_Err gf_odf_size_oci_date(GF_OCI_Data *ocd, u32 *outSize); +GF_Err gf_odf_size_oci_name(GF_OCICreators *ocn, u32 *outSize); +GF_Err gf_odf_size_pl_idx(GF_PL_IDX *plid, u32 *outSize); +GF_Err gf_odf_size_qos(GF_QoS_Descriptor *qos, u32 *outSize); +GF_Err gf_odf_size_rating(GF_Rating *rd, u32 *outSize); +GF_Err gf_odf_size_reg(GF_Registration *reg, u32 *outSize); +GF_Err gf_odf_size_short_text(GF_ShortTextual *std, u32 *outSize); +GF_Err gf_odf_size_smpte_camera(GF_SMPTECamera *cpd, u32 *outSize); +GF_Err gf_odf_size_sup_cid(GF_SCIDesc *scid, u32 *outSize); +GF_Err gf_odf_size_segment(GF_Segment *sd, u32 *outSize); +GF_Err gf_odf_size_mediatime(GF_MediaTime *mt, u32 *outSize); +GF_Err gf_odf_size_muxinfo(GF_MuxInfo *mi, u32 *outSize); +GF_Err gf_odf_size_ipmp_tool_list(GF_IPMP_ToolList *ipmptl, u32 *outSize); +GF_Err gf_odf_size_ipmp_tool(GF_IPMP_Tool *ipmp, u32 *outSize); +GF_Err gf_odf_size_auxvid(GF_AuxVideoDescriptor *ld, u32 *outSize); + +GF_Err gf_odf_write_iod(GF_BitStream *bs, GF_InitialObjectDescriptor *iod); +GF_Err gf_odf_write_esd(GF_BitStream *bs, GF_ESD *esd); +GF_Err gf_odf_write_dcd(GF_BitStream *bs, GF_DecoderConfig *dcd); +GF_Err gf_odf_write_slc(GF_BitStream *bs, GF_SLConfig *sl); +GF_Err gf_odf_write_cc(GF_BitStream *bs, GF_CCDescriptor *ccd); +GF_Err gf_odf_write_cc_date(GF_BitStream *bs, GF_CC_Date *cdd); +GF_Err gf_odf_write_cc_name(GF_BitStream *bs, GF_CC_Name *cnd); +GF_Err gf_odf_write_ci(GF_BitStream *bs, GF_CIDesc *cid); +GF_Err gf_odf_write_default(GF_BitStream *bs, GF_DefaultDescriptor *dd); +GF_Err gf_odf_write_esd_inc(GF_BitStream *bs, GF_ES_ID_Inc *esd_inc); +GF_Err gf_odf_write_esd_ref(GF_BitStream *bs, GF_ES_ID_Ref *esd_ref); +GF_Err gf_odf_write_exp_text(GF_BitStream *bs, GF_ExpandedTextual *etd); +GF_Err gf_odf_write_pl_ext(GF_BitStream *bs, GF_PLExt *pld); +GF_Err gf_odf_write_ipi_ptr(GF_BitStream *bs, GF_IPIPtr *ipid); +GF_Err gf_odf_write_ipmp(GF_BitStream *bs, GF_IPMP_Descriptor *ipmp); +GF_Err gf_odf_write_ipmp_ptr(GF_BitStream *bs, GF_IPMPPtr *ipmpd); +GF_Err gf_odf_write_kw(GF_BitStream *bs, GF_KeyWord *kwd); +GF_Err gf_odf_write_lang(GF_BitStream *bs, GF_Language *ld); +GF_Err gf_odf_write_isom_iod(GF_BitStream *bs, GF_IsomInitialObjectDescriptor *iod); +GF_Err gf_odf_write_isom_od(GF_BitStream *bs, GF_IsomObjectDescriptor *od); +GF_Err gf_odf_write_od(GF_BitStream *bs, GF_ObjectDescriptor *od); +GF_Err gf_odf_write_oci_date(GF_BitStream *bs, GF_OCI_Data *ocd); +GF_Err gf_odf_write_oci_name(GF_BitStream *bs, GF_OCICreators *ocn); +GF_Err gf_odf_write_pl_idx(GF_BitStream *bs, GF_PL_IDX *plid); +GF_Err gf_odf_write_qos(GF_BitStream *bs, GF_QoS_Descriptor *qos); +GF_Err gf_odf_write_rating(GF_BitStream *bs, GF_Rating *rd); +GF_Err gf_odf_write_reg(GF_BitStream *bs, GF_Registration *reg); +GF_Err gf_odf_write_short_text(GF_BitStream *bs, GF_ShortTextual *std); +GF_Err gf_odf_write_smpte_camera(GF_BitStream *bs, GF_SMPTECamera *cpd); +GF_Err gf_odf_write_sup_cid(GF_BitStream *bs, GF_SCIDesc *scid); +GF_Err gf_odf_write_segment(GF_BitStream *bs, GF_Segment *sd); +GF_Err gf_odf_write_mediatime(GF_BitStream *bs, GF_MediaTime *mt); +GF_Err gf_odf_write_muxinfo(GF_BitStream *bs, GF_MuxInfo *mi); +GF_Err gf_odf_write_ipmp_tool_list(GF_BitStream *bs, GF_IPMP_ToolList *ipmptl); +GF_Err gf_odf_write_ipmp_tool(GF_BitStream *bs, GF_IPMP_Tool *ipmp); +GF_Err gf_odf_write_auxvid(GF_BitStream *bs, GF_AuxVideoDescriptor *ld); + +GF_Descriptor *gf_odf_new_text_cfg(); +GF_Err gf_odf_del_text_cfg(GF_TextConfig *desc); +GF_Descriptor *gf_odf_new_tx3g(); +GF_Err gf_odf_del_tx3g(GF_TextSampleDescriptor *sd); + +/*our commands base functions*/ +GF_ODCom *gf_odf_create_command(u8 tag); +GF_Err gf_odf_delete_command(GF_ODCom *com); +GF_Err gf_odf_parse_command(GF_BitStream *bs, GF_ODCom **com, u32 *com_size); +GF_Err gf_odf_read_command(GF_BitStream *bs, GF_ODCom *com, u32 gf_odf_size_command); +GF_Err gf_odf_size_command(GF_ODCom *com, u32 *outSize); +GF_Err gf_odf_write_command(GF_BitStream *bs, GF_ODCom *com); + +GF_ODCom *gf_odf_new_od_remove(); +GF_ODCom *gf_odf_new_od_update(); +GF_ODCom *gf_odf_new_esd_update(); +GF_ODCom *gf_odf_new_esd_remove(); +GF_ODCom *gf_odf_new_ipmp_update(); +GF_ODCom *gf_odf_new_ipmp_remove(); +GF_ODCom *gf_odf_new_base_command(); + +GF_Err gf_odf_del_od_remove(GF_ODRemove *ODRemove); +GF_Err gf_odf_del_od_update(GF_ODUpdate *ODUpdate); +GF_Err gf_odf_del_esd_update(GF_ESDUpdate *ESDUpdate); +GF_Err gf_odf_del_esd_remove(GF_ESDRemove *ESDRemove); +GF_Err gf_odf_del_ipmp_update(GF_IPMPUpdate *IPMPDUpdate); +GF_Err gf_odf_del_ipmp_remove(GF_IPMPRemove *IPMPDRemove); +GF_Err gf_odf_del_base_command(GF_BaseODCom *bcRemove); + +GF_Err gf_odf_read_od_remove(GF_BitStream *bs, GF_ODRemove *odRem, u32 gf_odf_size_command); +GF_Err gf_odf_read_od_update(GF_BitStream *bs, GF_ODUpdate *odUp, u32 gf_odf_size_command); +GF_Err gf_odf_read_esd_update(GF_BitStream *bs, GF_ESDUpdate *esdUp, u32 gf_odf_size_command); +GF_Err gf_odf_read_esd_remove(GF_BitStream *bs, GF_ESDRemove *esdRem, u32 gf_odf_size_command); +GF_Err gf_odf_read_ipmp_update(GF_BitStream *bs, GF_IPMPUpdate *ipmpUp, u32 gf_odf_size_command); +GF_Err gf_odf_read_ipmp_remove(GF_BitStream *bs, GF_IPMPRemove *ipmpRem, u32 gf_odf_size_command); +GF_Err gf_odf_read_base_command(GF_BitStream *bs, GF_BaseODCom *bcRem, u32 gf_odf_size_command); + +GF_Err gf_odf_size_od_remove(GF_ODRemove *odRem, u32 *outSize); +GF_Err gf_odf_size_od_update(GF_ODUpdate *odUp, u32 *outSize); +GF_Err gf_odf_size_esd_update(GF_ESDUpdate *esdUp, u32 *outSize); +GF_Err gf_odf_size_esd_remove(GF_ESDRemove *esdRem, u32 *outSize); +GF_Err gf_odf_size_ipmp_update(GF_IPMPUpdate *ipmpUp, u32 *outSize); +GF_Err gf_odf_size_ipmp_remove(GF_IPMPRemove *ipmpRem, u32 *outSize); +GF_Err gf_odf_size_base_command(GF_BaseODCom *bcRem, u32 *outSize); + +GF_Err gf_odf_write_od_remove(GF_BitStream *bs, GF_ODRemove *odRem); +GF_Err gf_odf_write_od_update(GF_BitStream *bs, GF_ODUpdate *odUp); +GF_Err gf_odf_write_esd_update(GF_BitStream *bs, GF_ESDUpdate *esdUp); +GF_Err gf_odf_write_esd_remove(GF_BitStream *bs, GF_ESDRemove *esdRem); +GF_Err gf_odf_write_ipmp_update(GF_BitStream *bs, GF_IPMPUpdate *ipmpUp); +GF_Err gf_odf_write_ipmp_remove(GF_BitStream *bs, GF_IPMPRemove *ipmpRem); +GF_Err gf_odf_write_base_command(GF_BitStream *bs, GF_BaseODCom *bcRem); + + + +/*dumping*/ +GF_Err gf_odf_dump_iod(GF_InitialObjectDescriptor *iod, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_esd(GF_ESD *esd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_dcd(GF_DecoderConfig *dcd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_slc(GF_SLConfig *sl, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_cc(GF_CCDescriptor *ccd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_cc_date(GF_CC_Date *cdd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_cc_name(GF_CC_Name *cnd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ci(GF_CIDesc *cid, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_default(GF_DefaultDescriptor *dd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_esd_inc(GF_ES_ID_Inc *esd_inc, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_esd_ref(GF_ES_ID_Ref *esd_ref, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_exp_text(GF_ExpandedTextual *etd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_pl_ext(GF_PLExt *pld, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipi_ptr(GF_IPIPtr *ipid, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp(GF_IPMP_Descriptor *ipmp, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp_ptr(GF_IPMPPtr *ipmpd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_kw(GF_KeyWord *kwd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_lang(GF_Language *ld, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_isom_iod(GF_IsomInitialObjectDescriptor *iod, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_isom_od(GF_IsomObjectDescriptor *od, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_od(GF_ObjectDescriptor *od, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_oci_date(GF_OCI_Data *ocd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_oci_name(GF_OCICreators *ocn, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_pl_idx(GF_PL_IDX *plid, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_qos(GF_QoS_Descriptor *qos, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_rating(GF_Rating *rd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_reg(GF_Registration *reg, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_short_text(GF_ShortTextual *std, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_smpte_camera(GF_SMPTECamera *cpd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_sup_cid(GF_SCIDesc *scid, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_segment(GF_Segment *sd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_mediatime(GF_MediaTime *mt, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_muxinfo(GF_MuxInfo *mi, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_bifs_cfg(GF_BIFSConfig *dsi, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_laser_cfg(GF_LASERConfig *dsi, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ui_cfg(GF_UIConfig *dsi, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_txtcfg(GF_TextConfig *desc, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp_tool_list(GF_IPMP_ToolList *tl, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp_tool(GF_IPMP_Tool*t, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_aux_vid(GF_AuxVideoDescriptor *ld, FILE *trace, u32 indent, Bool XMTDump); + + +GF_Err gf_odf_dump_od_update(GF_ODUpdate *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_od_remove(GF_ODRemove *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_esd_update(GF_ESDUpdate *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_esd_remove(GF_ESDRemove *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp_update(GF_IPMPUpdate *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp_remove(GF_IPMPRemove *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_base_command(GF_BaseODCom *com, FILE *trace, u32 indent, Bool XMTDump); + +#endif /*_GF_OD_DEV_H_*/ + diff --git a/include/gpac/internal/ogg.h b/include/gpac/internal/ogg.h new file mode 100644 index 0000000..867de04 --- /dev/null +++ b/include/gpac/internal/ogg.h @@ -0,0 +1,209 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: code raw [Vorbis] packets into framed OggSquish stream and + decode Ogg streams back into raw packets + + note: The CRC code is directly derived from public domain code by + Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html + for details. + + ********************************************************************/ + + +#ifndef _GF_OGG_H_ +#define _GF_OGG_H_ + +/*DON'T CLASH WITH OFFICIAL OGG IF ALREADY INCLUDED*/ +#ifndef _OGG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/tools.h> + +typedef struct { + s32 endbyte; + s32 endbit; + + unsigned char *buffer; + unsigned char *ptr; + s32 storage; +} oggpack_buffer; + +/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/ + +typedef struct { + unsigned char *header; + s32 header_len; + unsigned char *body; + s32 body_len; +} ogg_page; + +/* ogg_stream_state contains the current encode/decode state of a logical + Ogg bitstream **********************************************************/ + +typedef struct { + unsigned char *body_data; /* bytes from packet bodies */ + s32 body_storage; /* storage elements allocated */ + s32 body_fill; /* elements stored; fill mark */ + s32 body_returned; /* elements of fill returned */ + + + s32 *lacing_vals; /* The values that will go to the segment table */ + s64 *granule_vals; /* granulepos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + s32 lacing_storage; + s32 lacing_fill; + s32 lacing_packet; + s32 lacing_returned; + + unsigned char header[282]; /* working space for header encode */ + s32 header_fill; + + s32 e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + s32 b_o_s; /* set after we've written the initial page + of a logical bitstream */ + s32 serialno; + s32 pageno; + s64 packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + s64 granulepos; + +} ogg_stream_state; + +/* ogg_packet is used to encapsulate the data and metadata belonging + to a single raw Ogg/Vorbis packet *************************************/ + +typedef struct { + unsigned char *packet; + s32 bytes; + s32 b_o_s; + s32 e_o_s; + + s64 granulepos; + + s64 packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ +} ogg_packet; + +typedef struct { + unsigned char *data; + s32 storage; + s32 fill; + s32 returned; + + s32 unsynced; + s32 headerbytes; + s32 bodybytes; +} ogg_sync_state; + + + +/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ + +void oggpack_writeinit(oggpack_buffer *b); +void oggpack_writetrunc(oggpack_buffer *b,s32 bits); +void oggpack_writealign(oggpack_buffer *b); +void oggpack_writecopy(oggpack_buffer *b,void *source,s32 bits); +void oggpack_reset(oggpack_buffer *b); +void oggpack_writeclear(oggpack_buffer *b); +void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,s32 bytes); +void oggpack_write(oggpack_buffer *b,u32 value,s32 bits); +s32 oggpack_look(oggpack_buffer *b,s32 bits); +s32 oggpack_look1(oggpack_buffer *b); +void oggpack_adv(oggpack_buffer *b,s32 bits); +void oggpack_adv1(oggpack_buffer *b); +s32 oggpack_read(oggpack_buffer *b,s32 bits); +s32 oggpack_read1(oggpack_buffer *b); +s32 oggpack_bytes(oggpack_buffer *b); +s32 oggpack_bits(oggpack_buffer *b); +unsigned char *oggpack_get_buffer(oggpack_buffer *b); + +void oggpackB_writeinit(oggpack_buffer *b); +void oggpackB_writetrunc(oggpack_buffer *b,s32 bits); +void oggpackB_writealign(oggpack_buffer *b); +void oggpackB_writecopy(oggpack_buffer *b,void *source,s32 bits); +void oggpackB_reset(oggpack_buffer *b); +void oggpackB_writeclear(oggpack_buffer *b); +void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,s32 bytes); +void oggpackB_write(oggpack_buffer *b,u32 value,s32 bits); +s32 oggpackB_look(oggpack_buffer *b,s32 bits); +s32 oggpackB_look1(oggpack_buffer *b); +void oggpackB_adv(oggpack_buffer *b,s32 bits); +void oggpackB_adv1(oggpack_buffer *b); +s32 oggpackB_read(oggpack_buffer *b,s32 bits); +s32 oggpackB_read1(oggpack_buffer *b); +s32 oggpackB_bytes(oggpack_buffer *b); +s32 oggpackB_bits(oggpack_buffer *b); +unsigned char *oggpackB_get_buffer(oggpack_buffer *b); + +/* Ogg BITSTREAM PRIMITIVES: encoding **************************/ + +s32 ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +s32 ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +s32 ogg_stream_flush(ogg_stream_state *os, ogg_page *og); + +/* Ogg BITSTREAM PRIMITIVES: decoding **************************/ + +s32 ogg_sync_init(ogg_sync_state *oy); +s32 ogg_sync_clear(ogg_sync_state *oy); +s32 ogg_sync_reset(ogg_sync_state *oy); +s32 ogg_sync_destroy(ogg_sync_state *oy); + +char *ogg_sync_buffer(ogg_sync_state *oy, s32 size); +s32 ogg_sync_wrote(ogg_sync_state *oy, s32 bytes); +s32 ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +s32 ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); +s32 ogg_stream_pagein(ogg_stream_state *os, ogg_page *og); +s32 ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); +s32 ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); + +/* Ogg BITSTREAM PRIMITIVES: general ***************************/ + +s32 ogg_stream_init(ogg_stream_state *os,s32 serialno); +s32 ogg_stream_clear(ogg_stream_state *os); +s32 ogg_stream_reset(ogg_stream_state *os); +s32 ogg_stream_reset_serialno(ogg_stream_state *os,s32 serialno); +s32 ogg_stream_destroy(ogg_stream_state *os); +s32 ogg_stream_eos(ogg_stream_state *os); +void ogg_page_checksum_set(ogg_page *og); +s32 ogg_page_version(ogg_page *og); +s32 ogg_page_continued(ogg_page *og); +s32 ogg_page_bos(ogg_page *og); +s32 ogg_page_eos(ogg_page *og); +s64 ogg_page_granulepos(ogg_page *og); +s32 ogg_page_serialno(ogg_page *og); +s32 ogg_page_pageno(ogg_page *og); +s32 ogg_page_packets(ogg_page *og); + +void ogg_packet_clear(ogg_packet *op); + + +#ifdef __cplusplus +} +#endif + + +#endif /*_OGG_H*/ + +#endif /*_GF_OGG_H_*/ + diff --git a/include/gpac/internal/scenegraph_dev.h b/include/gpac/internal/scenegraph_dev.h new file mode 100644 index 0000000..54d88a0 --- /dev/null +++ b/include/gpac/internal/scenegraph_dev.h @@ -0,0 +1,1033 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#ifndef _GF_SCENEGRAPH_DEV_H_ +#define _GF_SCENEGRAPH_DEV_H_ + +/*defined this macro to enable cyclic render*/ +#define GF_CYCLIC_TRAVERSE_ON + +/*defined this macro to enable scene replacement from inside (through conditional)*/ +//#define GF_SELF_REPLACE_ENABLE + +/*for vrml base types, ROUTEs and PROTOs*/ +#include <gpac/scenegraph_vrml.h> + +#include <gpac/scenegraph_svg.h> + +#ifdef GPAC_HAS_SPIDERMONKEY + +/*WIN32 and WinCE config (no configure script)*/ +#if defined(WIN32) || defined(_WIN32_WCE) || defined(__SYMBIAN32__) +#ifndef XP_PC +#define XP_PC +#endif +#ifndef XP_WIN +#define XP_WIN +#endif +/*WINCE specific config*/ +#if defined(_WIN32_WCE) +#include <windows.h> +#define XP_WINCE +#endif +#endif + +/*other platforms should be setup through configure*/ + +#endif + + +void gf_node_setup(GF_Node *p, u32 tag); + +typedef struct _parent_list +{ + struct _parent_list *next; + GF_Node *node; +} GF_ParentList; + + +/*internal flag reserved for NodeID*/ +#define GF_NODE_IS_DEF 0x80000000 // 1<<31 +/*internal flag reserved for activate/deactivate*/ +#define GF_NODE_IS_DEACTIVATED 0x40000000 // 1<<30 +/*internal flag reserved for node with scripting bindings*/ +#define GF_NODE_HAS_BINDING 0x20000000 // 1<<29 + +#ifdef GF_CYCLIC_TRAVERSE_ON +#define GF_NODE_IN_TRAVERSE 0x10000000 // 1<<28 +#define GF_NODE_INTERNAL_FLAGS 0xF0000000 +#else +#define GF_NODE_INTERNAL_FLAGS 0xE0000000 +#endif + +struct _node_interactive_ext +{ + /*routes on eventOut, ISed routes, ... for VRML-based scene graphs + THIS IS DYNAMICALLY CREATED*/ + GF_List *routes; + +#ifndef GPAC_DISABLE_SVG + /*event listeners - THIS IS DYNAMICALLY CREATED*/ + GF_DOMEventTarget *dom_evt; + + /* SVG animations are registered in the target node - THIS IS DYNAMICALLY CREATED*/ + GF_List *animations; +#endif + +}; + +typedef struct _nodepriv +{ + /*node type*/ + u16 tag; + /*number of instances of this node in the graph*/ + u16 num_instances; + /*node flags*/ + u32 flags; + /*scenegraph holding the node*/ + struct __tag_scene_graph *scenegraph; + + /*user defined callback function */ + void (*UserCallback)(struct _base_node *node, void *render_stack, Bool node_destroy); + /*user defined stack*/ + void *UserPrivate; + + /*list of all parent nodes (needed to invalidate parent tree)*/ + GF_ParentList *parents; + + /*holder for all interactive stuff - THIS IS DYNAMICALLY CREATED*/ + struct _node_interactive_ext *interact; +} NodePriv; + + +typedef struct __tag_node_id +{ + struct __tag_node_id *next; + GF_Node *node; + + /*node ID*/ + u32 NodeID; + /*node def name*/ + char *NodeName; +} NodeIDedItem; + +typedef struct +{ + char *name; + char *qname; + u32 xmlns_id; +} GF_XMLNS; + +struct __tag_scene_graph +{ + /*used to discriminate between node and scenegraph*/ + u64 __reserved_null; + + /*all DEF nodes (explicit)*/ + NodeIDedItem *id_node, *id_node_last; + + /*all routes available*/ + GF_List *Routes; + + /*all routes available*/ + GF_List *exported_nodes; + + /*when a proto is instanciated it creates its own scene graph. BIFS/VRML specify that the namespace is the same + (eg cannot reuse a NodeID or route name/ID), but this could be done differently by some other stds + if NULL this is the main scenegraph*/ + struct _proto_instance *pOwningProto; + + /*all first-level protos of the graph (the only ones that can be instanciated in this graph)*/ + GF_List *protos; + /*all first-level protos of the graph not currently registered - memory handling of graph only*/ + GF_List *unregistered_protos; + + /*pointer to the root node*/ + GF_Node *RootNode; + + /*routes to be activated (cascade model). This is used at the top-level graph only (eg + proto routes use that too, ecept ISed fields). It is the app responsability to + correctly connect or browse scene graphs connected through Inline*/ + GF_List *routes_to_activate; + + /*since events may trigger deletion of objects we use a 2 step delete*/ + GF_List *routes_to_destroy; + + u32 simulation_tick; + + /*user private data*/ + void *userpriv; + + /*callback routines*/ + /*node callback*/ + void (*NodeCallback)(void *user_priv, u32 type, GF_Node *node, void *ctxdata); + /*real scene time callback*/ + Double (*GetSceneTime)(void *userpriv); + + GF_SceneGraph *(*GetExternProtoLib)(void *userpriv, MFURL *lib_url); + + /*parent scene if any*/ + struct __tag_scene_graph *parent_scene; + + /*size info and pixel metrics - this is not used internally, however it helps when rendering + and decoding modules don't know each-other (as in MPEG4)*/ + u32 width, height; + Bool usePixelMetrics; + + Bool modified; + + /*application interface for javascript*/ + gf_sg_script_action script_action; + void *script_action_cbck; + + /*script loader*/ + void (*script_load)(GF_Node *node); + /*callback to JS upon node modif*/ + void (*on_node_modified)(struct __tag_scene_graph *sg, GF_Node *node, GF_FieldInfo *info); + + u32 max_defined_route_id; + + /*namespaces list. This list is used while parsing/dumping the tree to store the hierarchy of xmlns attributes in subtrees. + It is a stack of GF_XMLNS structures pushed/popped at each element*/ + GF_List *ns; + +#ifdef GF_SELF_REPLACE_ENABLE + /*to detect replace scene from within conditionals*/ + Bool graph_has_been_reset; +#endif + /*global qp used in BIFS coding*/ + GF_Node *global_qp; + + /*use stack as used in the dom_fire_event - this is only valid during an event fire, and may be NULL*/ + GF_List *use_stack; + + /*temp storage for name conversions*/ + char szNameBuffer[100]; + +#ifndef GPAC_DISABLE_SVG + GF_DOMEventTarget dom_evt; + u32 nb_evts_focus; + u32 nb_evts_mouse; + u32 nb_evts_key; + u32 nb_evts_ui; + u32 nb_evts_text; + u32 nb_evts_smil; + u32 nb_evts_mutation; + u32 nb_evts_laser; + u32 nb_evts_mae; + u32 nb_evts_svg; + u32 dom_evt_filter; + + GF_List *xlink_hrefs; + GF_List *smil_timed_elements; + GF_List *modified_smil_timed_elements; + Bool update_smil_timing; + + /*listeners to add*/ + GF_List *listeners_to_add; + +#ifdef GPAC_HAS_SPIDERMONKEY + struct __tag_svg_script_ctx *svg_js; +#endif + +#endif + +#ifdef GPAC_HAS_SPIDERMONKEY + GF_List *scripts; + /* + Note about reference counter + + A DOM document (<=> scenegraph) may be created through javascript, and the JS object having created the + document may be destroyed while the document is still in use. Moreover with XMLHttpRequest, the + "associated" doc is re-created at each request, but the script may still refer to the original document. + Since the document doesn't have a fixed owner, a reference counter is use and the scenegraph is kept alive + until the last object using it is destroyed. + + If this counter is set to 0 when creating DOM Elements/..., this means the scenegraph is hold by an external + entity (typically the player), and cannot be destroyed from the scripting engine + */ + u32 reference_count; + /*DOM nodes*/ + GF_List *objects; + /*DOM document*/ + struct JSObject *document; + + Bool dcci_doc; +#endif +}; + +void gf_sg_parent_setup(GF_Node *pNode); +void gf_sg_parent_reset(GF_Node *pNode); + +void *gf_node_get_name_address(GF_Node*node); + +void gf_node_changed_internal(GF_Node *node, GF_FieldInfo *field, Bool notify_scripts); + +struct _route +{ + u8 is_setup; + /*set to true for proto IS fields*/ + u8 IS_route; + + u32 ID; + char *name; + + GF_Node *FromNode; + GF_FieldInfo FromField; + + GF_Node *ToNode; + GF_FieldInfo ToField; + + /*scope of this route*/ + GF_SceneGraph *graph; + u32 lastActivateTime; +}; + +void gf_sg_route_unqueue(GF_SceneGraph *sg, GF_Route *r); +/*returns TRUE if route modified destination node*/ +Bool gf_sg_route_activate(GF_Route *r); +void gf_sg_route_queue(GF_SceneGraph *pSG, GF_Route *r); + +void gf_sg_destroy_routes(GF_SceneGraph *sg); + + +/*MPEG4 def*/ +GF_Node *gf_sg_mpeg4_node_new(u32 NodeTag); +u32 gf_sg_mpeg4_node_get_child_ndt(GF_Node *node); +GF_Err gf_sg_mpeg4_node_get_field_index(GF_Node *node, u32 inField, u8 code_mode, u32 *fieldIndex); +GF_Err gf_sg_mpeg4_node_get_field(GF_Node *node, GF_FieldInfo *field); +u32 gf_sg_mpeg4_node_get_field_count(GF_Node *node, u8 code_mode); +void gf_sg_mpeg4_node_del(GF_Node *node); +const char *gf_sg_mpeg4_node_get_class_name(u32 NodeTag); +Bool gf_sg_mpeg4_node_get_aq_info(GF_Node *node, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits); +s32 gf_sg_mpeg4_node_get_field_index_by_name(GF_Node *node, char *name); + +/*X3D def*/ +GF_Node *gf_sg_x3d_node_new(u32 NodeTag); +GF_Err gf_sg_x3d_node_get_field(GF_Node *node, GF_FieldInfo *field); +u32 gf_sg_x3d_node_get_field_count(GF_Node *node); +void gf_sg_x3d_node_del(GF_Node *node); +const char *gf_sg_x3d_node_get_class_name(u32 NodeTag); +s32 gf_sg_x3d_node_get_field_index_by_name(GF_Node *node, char *name); + +Bool gf_x3d_get_node_type(u32 NDT_Tag, u32 NodeTag); + +void gf_sg_mfint32_del(MFInt32 par); +void gf_sg_mffloat_del(MFFloat par); +void gf_sg_mfdouble_del(MFDouble par); +void gf_sg_mfbool_del(MFBool par); +void gf_sg_mfcolor_del(MFColor par); +void gf_sg_mfcolor_rgba_del(MFColorRGBA par); +void gf_sg_mfrotation_del(MFRotation par); +void gf_sg_mfstring_del(MFString par); +void gf_sg_mftime_del(MFTime par); +void gf_sg_mfvec2f_del(MFVec2f par); +void gf_sg_mfvec3f_del(MFVec3f par); +void gf_sg_mfvec4f_del(MFVec4f par); +void gf_sg_mfvec2d_del(MFVec2d par); +void gf_sg_mfvec3d_del(MFVec3d par); +void gf_sg_sfimage_del(SFImage im); +void gf_sg_sfstring_del(SFString par); +void gf_sg_mfscript_del(MFScript sc); +void gf_sg_sfcommand_del(SFCommandBuffer cb); +void gf_sg_sfurl_del(SFURL url); + +Bool gf_sg_vrml_node_init(GF_Node *node); +Bool gf_sg_vrml_node_changed(GF_Node *node, GF_FieldInfo *field); + + + +#ifndef GPAC_DISABLE_SVG + + +/* reset functions for SVG types */ +void gf_svg_reset_path(SVG_PathData path); +void gf_svg_reset_iri(GF_SceneGraph *sg, XMLRI*iri); +/* delete functions for SVG types */ +void gf_svg_delete_paint (GF_SceneGraph *sg, SVG_Paint *paint); +void gf_smil_delete_times (GF_List *l); +void gf_svg_delete_points (GF_List *l); +void gf_svg_delete_coordinates (GF_List *l); +/*for keyTimes, keyPoints and keySplines*/ +void gf_smil_delete_key_types (GF_List *l); + +u32 gf_node_get_attribute_count(GF_Node *node); +GF_Err gf_node_get_attribute_info(GF_Node *node, GF_FieldInfo *info) ; + + +/*SMIL anim tools*/ + +typedef struct __xlink_attrip_ptrs { + XMLRI *href; + SVG_ContentType *type; + SVG_String *title; + XMLRI *arcrole; + XMLRI *role; + SVG_String *show; + SVG_String *actuate; +} XLinkAttributesPointers; + +typedef struct __smil_time_attrip_ptrs { + SMIL_Times *begin, *end; + SVG_Clock *clipBegin, *clipEnd; + SMIL_Duration *dur; + SMIL_RepeatCount *repeatCount; + SMIL_Duration *repeatDur; + SMIL_Restart *restart; + SMIL_Fill *fill; + SMIL_Duration *max; + SMIL_Duration *min; + struct _smil_timing_rti *runtime; /* contains values for runtime handling of the SMIL timing */ +} SMILTimingAttributesPointers; + +typedef struct __smil_sync_attrip_ptrs { + SMIL_SyncBehavior *syncBehavior, *syncBehaviorDefault; + SMIL_SyncTolerance *syncTolerance, *syncToleranceDefault; + SVG_Boolean *syncMaster; + XMLRI *syncReference; +} SMILSyncAttributesPointers; + +typedef struct __smil_anim_attrip_ptrs { + SMIL_AttributeName *attributeName; + SMIL_AttributeType *attributeType; + SMIL_AnimateValue *to, *by, *from; + SMIL_AnimateValues *values; + SMIL_CalcMode *calcMode; + SMIL_Accumulate *accumulate; + SMIL_Additive *additive; + SMIL_KeySplines *keySplines; + SMIL_KeyTimes *keyTimes; + SVG_TransformType *type; + SVG_Boolean *lsr_enabled; + + SMIL_KeyPoints *keyPoints; + SVG_String *origin; + SVG_Rotate *rotate; + SVG_PathData *path; +} SMILAnimationAttributesPointers; + + +typedef struct { + GF_DOM_BASE_NODE + + /*shortcuts for xlink, anim, timing attributes*/ + XLinkAttributesPointers *xlinkp; + SMILAnimationAttributesPointers *animp; + SMILTimingAttributesPointers *timingp; +} SVGTimedAnimBaseElement; + +GF_Err gf_node_animation_add(GF_Node *node, void *animation); +GF_Err gf_node_animation_del(GF_Node *node); +u32 gf_node_animation_count(GF_Node *node); +void *gf_node_animation_get(GF_Node *node, u32 i); +Bool gf_svg_is_inherit(GF_FieldInfo *a); +Bool gf_svg_is_current_color(GF_FieldInfo *a); + +void gf_svg_reset_animate_values(SMIL_AnimateValues anim_values, GF_SceneGraph *sg); +void gf_svg_reset_animate_value(SMIL_AnimateValue anim_value, GF_SceneGraph *sg); + +Bool gf_svg_is_timing_tag(u32 tag); +Bool gf_svg_is_animation_tag(u32 tag); +u32 gf_svg_get_modification_flags(SVG_Element *n, GF_FieldInfo *info); + +Bool gf_svg_resolve_smil_times(GF_Node *anim, void *event_base_element, GF_List *smil_times, Bool is_end, const char *node_name); + + +/* SMIL Timing structures */ +/* status of an SMIL timed element */ +enum { + SMIL_STATUS_WAITING_TO_BEGIN = 0, + SMIL_STATUS_ACTIVE, + SMIL_STATUS_POST_ACTIVE, + SMIL_STATUS_FROZEN, + SMIL_STATUS_DONE +}; + +typedef struct { + u32 activation_cycle; + u32 nb_iterations; + + /* for the case where min > simple duration*/ + Bool min_active; + + /* negative values mean indefinite */ + Double begin, + end, + simple_duration, + active_duration, + repeat_duration; + +} SMIL_Interval; + +struct _smil_timing_rti +{ + GF_Node *timed_elt; + SMILTimingAttributesPointers *timingp; + + Double scene_time; + Fixed normalized_simple_time; + Bool force_reevaluation; + + /* SMIL element life-cycle status */ + u8 status; + + SMIL_Interval *current_interval; + SMIL_Interval *next_interval; + + /* Evaluation of animations is postponed untill tree traversal, so that inherit values can be computed + Other timed elements (audio, video, animation) are evaluated directly and do not require + scene tree traversal.*/ + Bool postpone; + + void (*evaluate)(struct _smil_timing_rti *rti, Fixed normalized_simple_time, u32 state); + u32 evaluate_status; + +#if 0 + /* is called only when the timed element is active */ + void (*activation)(struct _smil_timing_rti *rti, Fixed normalized_simple_time); + + /* is called (possibly many times) when the timed element is frozen */ + void (*freeze)(struct _smil_timing_rti *rti, Fixed normalized_simple_time); + + /* is called (only once) when the timed element is restored */ + void (*restore)(struct _smil_timing_rti *rti, Fixed normalized_simple_time); + + /* is called only when the timed element is inactive and receives a fraction event, the second parameter is ignored */ + void (*fraction_activation)(struct _smil_timing_rti *rti, Fixed normalized_simple_time); +#endif + /* simulated normalized simple time */ + Fixed fraction; + + Bool paused; + Double media_duration; + + /* shortcut when this rti corresponds to an animation */ + struct _smil_anim_rti *rai; +}; + +void gf_smil_timing_init_runtime_info(GF_Node *timed_elt); +void gf_smil_timing_delete_runtime_info(GF_Node *timed_elt, SMIL_Timing_RTI *rti); +Fixed gf_smil_timing_get_normalized_simple_time(SMIL_Timing_RTI *rti, Double scene_time, Bool *force_end); +/*returns 1 if an animation changed a value in the rendering tree */ +s32 gf_smil_timing_notify_time(SMIL_Timing_RTI *rti, Double scene_time); + + +/* SMIL Animation Structures */ +/* This structure is used per animated attribute, + it contains: + - all the animations applying to the same attribute, + - the specified value before any inheritance has been applied nor any animation started + (as specified in the SVG document), + - the presentation value passed from one animation to the next one, at the same level in the tree + - a boolean indicating if the animated attribute is in fact a property + + and if the attribute is a property: + - a pointer to presentation value passed from the previous level in the tree + - a pointer to the value of the color property (for handling of 'currentColor'), from previous level in the tree + - the location of the attribute in the elt structure when it was created + (used for fast comparison of SVG properties when animating from/to/by/values/... inherited values) +*/ +typedef struct { + GF_List *anims; + GF_FieldInfo specified_value; + GF_FieldInfo presentation_value; + Bool is_property; + GF_FieldInfo parent_presentation_value; + GF_FieldInfo current_color_value; + void *orig_dom_ptr; + /* flag set by any animation to inform other animations that there base value has changed */ + Bool presentation_value_changed; + /* flag used for rendering */ + u32 dirty_flags; + Bool dirty_parents; +} SMIL_AttributeAnimations; + +/* This structure is per animation element, + it holds the result of the animation and + some info to make animation computation faster */ +typedef struct _smil_anim_rti { + SMIL_AttributeAnimations *owner; + + Bool is_first_anim; + + /* animation element */ + GF_Node *anim_elt; + SMILAnimationAttributesPointers *animp; + SMILTimingAttributesPointers *timingp; + XLinkAttributesPointers *xlinkp; + + /* in case of animateTransform without from or to, the underlying value is the identity transform */ + GF_Matrix2D identity; + GF_FieldInfo default_transform_value; + + /* result of the animation */ + GF_FieldInfo interpolated_value; + + /* last value of the animation, used in accumulation phase */ + /* normally the far pointer in the last specified value is a pointer to a real attribute value, + and there's no need to allocate a new value. Except if the last specified value is the last + point in a path (animateMotion) in which case we allocate a matrix as last spec value, + which we need to delete (see animate-elem-202-t.svg). This is signaled if rai->path is not NULL*/ + GF_FieldInfo last_specified_value; + + /* temporary value needed when the type of + the key values is different from the target attribute type */ + GF_FieldInfo tmp_value; + + /* the number of values in animations should be constant (unless updated with LASeR commands) + we can store them to avoid computing them at each cycle */ + u32 values_count; + u32 key_times_count; + u32 key_points_count; + u32 key_splines_count; + + + /* In change detection mode, we test previous animation parameters to determine + if a new evaluation of the animation will produce a different result. + The result of these test is stored in interpolated_value_changed */ + Bool change_detection_mode; + Bool interpolated_value_changed; + s32 previous_key_index; + u32 previous_keytime_index; + Fixed previous_coef; + s32 previous_iteration; + Bool anim_done; + + GF_Path *path; + u8 rotate; + GF_PathIterator *path_iterator; + Fixed length; + +} SMIL_Anim_RTI; + +void gf_smil_anim_init_node(GF_Node *node); +void gf_smil_anim_init_discard(GF_Node *node); +void gf_smil_anim_init_runtime_info(GF_Node *node); +void gf_smil_anim_delete_runtime_info(SMIL_Anim_RTI *rai); +void gf_smil_anim_delete_animations(GF_Node *e); +void gf_smil_anim_remove_from_target(GF_Node *anim, GF_Node *target); + +void gf_sg_handle_dom_event(GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer); +void gf_smil_setup_events(GF_Node *node); + +void gf_smil_anim_reset_variables(SMIL_Anim_RTI *rai); +void gf_smil_anim_set_anim_runtime_in_timing(GF_Node *n); + +void gf_smil_timing_pause(GF_Node *node); +void gf_smil_timing_resume(GF_Node *node); + +#endif + + +// +// MF Fields tools +// WARNING: MF / SF Nodes CANNOT USE THESE FUNCTIONS +// + +//return the size (in bytes) of fixed fields (buffers are handled as a char ptr , 1 byte) +u32 gf_sg_vrml_get_sf_size(u32 FieldType); + + +/*BASE node (GF_Node) destructor*/ +void gf_node_free(GF_Node *node); + +/*node destructor dispatcher: redirects destruction for each graph type: VRML/MPEG4, X3D, SVG...)*/ +void gf_node_del(GF_Node *node); + +/*creates an undefined GF_Node - for parsing only*/ +GF_Node *gf_sg_new_base_node(); + +/*returns field type from its name*/ +u32 gf_sg_field_type_by_name(char *fieldType); + + + +/* + Proto node + +*/ + +/*field interface to codec. This is used to do the node decoding, index translation +and all QP/BIFS Anim parsing. */ +struct _protofield +{ + u8 EventType; + u8 FieldType; + /*if UseName, otherwise fieldN*/ + char *FieldName; + + /*default field value*/ + void *def_value; + + GF_Node *def_sfnode_value; + GF_ChildNodeItem *def_mfnode_value; + + /*coding indexes*/ + u32 IN_index, OUT_index, DEF_index, ALL_index; + + /*Quantization*/ + u32 QP_Type, hasMinMax; + void *qp_min_value, *qp_max_value; + /*this is for QP=13 only*/ + u32 NumBits; + + /*Animation*/ + u32 Anim_Type; + + void *userpriv; + void (*OnDelete)(void *ptr); +}; + +GF_ProtoFieldInterface *gf_sg_proto_new_field_interface(u32 FieldType); + +/*set QP and anim info for a proto field (BIFS allows for that in proto coding)*/ +GF_Err gf_bifs_proto_field_set_aq_info(GF_ProtoFieldInterface *field, u32 QP_Type, u32 hasMinMax, u32 QPSFType, void *qp_min_value, void *qp_max_value, u32 QP13_NumBits); + +/*proto field instance. since it is useless to duplicate all coding info, names and the like +we seperate proto declaration and proto instanciation*/ +typedef struct +{ + u8 EventType; + u8 FieldType; + u8 has_been_accessed; + void *field_pointer; +} GF_ProtoField; + + +struct _proto +{ + /*1 - Prototype interface*/ + u32 ID; + char *Name; + GF_List *proto_fields; + + /*pointer to parent scene graph*/ + struct __tag_scene_graph *parent_graph; + /*pointer to proto scene graph*/ + struct __tag_scene_graph *sub_graph; + + /*2 - proto implementation as declared in the bitstream*/ + GF_List *node_code; + + /*num fields*/ + u32 NumIn, NumOut, NumDef, NumDyn; + + void *userpriv; + void (*OnDelete)(void *ptr); + + /*URL of extern proto lib (if none, URL is empty)*/ + MFURL ExternProto; + + /*list of instances*/ + GF_List *instances; +}; + +/*proto field API*/ +u32 gf_sg_proto_get_num_fields(GF_Node *node, u8 code_mode); +GF_Err gf_sg_proto_get_field(GF_Proto *proto, GF_Node *node, GF_FieldInfo *field); + + +typedef struct _proto_instance +{ + /*this is a node*/ + BASE_NODE + + /*Prototype interface for coding and field addressing*/ + GF_Proto *proto_interface; + + /*proto implementation at run-time (aka the state of the nodes may differ accross + different instances of the proto)*/ + GF_List *fields; + + /*a proto doesn't have one root SFnode but a collection of nodes for implementation*/ + GF_List *node_code; + + /*node for proto rendering, first of all declared nodes*/ + GF_Node *RenderingNode; + + /*in case the PROTO is destroyed*/ + char *proto_name; + + /*scripts are loaded once all IS routes are activated and node code is loaded*/ + GF_List *scripts_to_load; + + Bool is_loaded; +} GF_ProtoInstance; + +/*destroy proto*/ +void gf_sg_proto_del_instance(GF_ProtoInstance *inst); +GF_Err gf_sg_proto_get_field_index(GF_ProtoInstance *proto, u32 index, u32 code_mode, u32 *all_index); +Bool gf_sg_proto_get_aq_info(GF_Node *Node, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits); +GF_Err gf_sg_proto_get_field_ind_static(GF_Node *Node, u32 inField, u8 IndexMode, u32 *allField); +GF_Node *gf_sg_proto_create_node(GF_SceneGraph *scene, GF_Proto *proto, GF_ProtoInstance *from_inst); +void gf_sg_proto_instanciate(GF_ProtoInstance *proto_node); + +/*get tag of first node in proto code - used for validation only*/ +u32 gf_sg_proto_get_root_tag(GF_Proto *proto); + + +/*to call when a proto field has been modified (at creation or through commands, modifications through events +are handled internally). +node can be the proto instance or a node from the proto code +this will call NodeChanged if needed, forward to proto/node or trigger any route if needed*/ +void gf_sg_proto_check_field_change(GF_Node *node, u32 fieldIndex); + +s32 gf_sg_proto_get_field_index_by_name(GF_Proto *proto, GF_Node *node, char *name); + +GF_Node *gf_vrml_node_clone(GF_SceneGraph *inScene, GF_Node *orig, GF_Node *cloned_parent, char *inst_id_suffix); + + +/* + Script node +*/ + +typedef struct +{ + //extra script fields + GF_List *fields; + + //BIFS coding stuff + u32 numIn, numDef, numOut; + +#ifdef GPAC_HAS_SPIDERMONKEY + struct JSContext *js_ctx; + struct JSObject *js_obj; + struct JSObject *js_browser; + /*all attached objects (eg, not created by the script) are stored here so that we don't + allocate them again and again when getting properties. Garbage collection is performed (if needed) + on these objects after each eventIn execution*/ + GF_List *js_cache; + struct JSObject *event; +#endif + + void (*JS_PreDestroy)(GF_Node *node); + void (*JS_EventIn)(GF_Node *node, GF_FieldInfo *in_field); + + Bool is_loaded; +} GF_ScriptPriv; + +/*setup script stack*/ +void gf_sg_script_init(GF_Node *node); +/*get script field*/ +GF_Err gf_sg_script_get_field(GF_Node *node, GF_FieldInfo *info); +/*get effective field count per event mode*/ +u32 gf_sg_script_get_num_fields(GF_Node *node, u8 IndexMode); +/*translate field index from inMode to ALL mode*/ +GF_Err gf_sg_script_get_field_index(GF_Node *Node, u32 inField, u8 IndexMode, u32 *allField); +/*create dynamic fields in the clone*/ +GF_Err gf_sg_script_prepare_clone(GF_Node *dest, GF_Node *orig); + +struct _scriptfield +{ + u32 eventType; + u32 fieldType; + char *name; + + s32 IN_index, OUT_index, DEF_index; + u32 ALL_index; + + //real field + void *pField; + + Double last_route_time; + Bool activate_event_out; +}; + + +#ifdef GPAC_HAS_SPIDERMONKEY + + +#include <gpac/download.h> +#include <gpac/network.h> + + +#define JS_SETUP_CLASS(the_class, cname, flag, getp, setp, fin) \ + memset(&the_class, 0, sizeof(the_class)); \ + the_class.name = cname; \ + the_class.flags = flag; \ + the_class.addProperty = JS_PropertyStub; \ + the_class.delProperty = JS_PropertyStub; \ + the_class.getProperty = getp; \ + the_class.setProperty = setp; \ + the_class.enumerate = JS_EnumerateStub; \ + the_class.resolve = JS_ResolveStub; \ + the_class.convert = JS_ConvertStub; \ + the_class.finalize = fin; \ + the_class.hasInstance = js_has_instance; + +typedef struct +{ + GF_Node *node; + GF_DownloadSession *sess; +} JSFileDownload; + +struct JSContext *gf_sg_ecmascript_new(GF_SceneGraph *sg); +void gf_sg_ecmascript_del(struct JSContext *); + +void gf_sg_script_init_sm_api(GF_ScriptPriv *sc, GF_Node *script); + +typedef struct +{ + GF_FieldInfo field; + GF_Node *owner; + struct JSObject *obj; + + /*JS list for MFFields or NULL*/ + struct JSObject *js_list; + + /*when creating SFnode from inside the script, the node is stored here untill attached to an object*/ + GF_Node *temp_node; + GF_ChildNodeItem *temp_list; + /*when not owned by a node*/ + void *field_ptr; + + Bool reevaluate; +} GF_JSField; + +#ifndef GPAC_DISABLE_SVG +void JSScript_LoadSVG(GF_Node *node); + +typedef struct __tag_svg_script_ctx +{ + Bool (*script_execute)(struct __tag_scene_graph *sg, char *utf8_script, GF_DOM_Event *event); + Bool (*handler_execute)(GF_Node *n, GF_DOM_Event *event, GF_Node *observer); + u32 nb_scripts; + /*global script context for the scene*/ + struct JSContext *js_ctx; + /*global object*/ + struct JSObject *global; + /*global event object - used to update the associated DOMEvent (JS private stack) when dispatching events*/ + struct JSObject *event; +} GF_SVGJS; + +#endif /*GPAC_DISABLE_SVG*/ + +/*initialize DOM Core (subset) + xmlHTTPRequest API. The global object MUST have private data storage +and its private data MUST be a scenegraph. This scenegraph is only used to create new documents +and setup the callback pointers*/ +void dom_js_load(GF_SceneGraph *scene, struct JSContext *c, struct JSObject *global); +/*unloads the DOM core support (to be called upon destruction only, once the JSContext has been destroyed +to releases all resources used by DOM JS)*/ +void dom_js_unload(); + +/*defines a new global object "document" of type Document*/ +void dom_js_define_document(struct JSContext *c, struct JSObject *global, GF_SceneGraph *doc); +/*defines a new global object "evt" of type Event*/ +struct JSObject *dom_js_define_event(struct JSContext *c, struct JSObject *global); + +struct JSObject *gf_dom_new_event(struct JSContext *c); + +struct JSObject *dom_js_get_node_proto(struct JSContext *c); +struct JSObject *dom_js_get_element_proto(struct JSContext *c); +struct JSObject *dom_js_get_document_proto(struct JSContext *c); +struct JSObject *dom_js_get_event_proto(struct JSContext *c); + +void dom_set_class_selector(struct JSContext *c, /*struct JSClass*/void *(*get_element_class)(GF_Node *n), /*struct JSClass*/void *(*get_document_class)(GF_SceneGraph *n) ); + +enum +{ + GF_DOM_EXC_INDEX_SIZE_ERR = 1, + GF_DOM_EXC_DOMSTRING_SIZE_ERR = 2, + GF_DOM_EXC_HIERARCHY_REQUEST_ERR = 3, + GF_DOM_EXC_WRONG_DOCUMENT_ERR = 4, + GF_DOM_EXC_INVALID_CHARACTER_ERR = 5, + GF_DOM_EXC_NO_DATA_ALLOWED_ERR = 6, + GF_DOM_EXC_NO_MODIFICATION_ALLOWED_ERR = 7, + GF_DOM_EXC_NOT_FOUND_ERR = 8, + GF_DOM_EXC_NOT_SUPPORTED_ERR = 9, + GF_DOM_EXC_INUSE_ATTRIBUTE_ERR = 10, + GF_DOM_EXC_INVALID_STATE_ERR = 11, + GF_DOM_EXC_SYNTAX_ERR = 12, + GF_DOM_EXC_INVALID_MODIFICATION_ERR = 13, + GF_DOM_EXC_NAMESPACE_ERR = 14, + GF_DOM_EXC_INVALID_ACCESS_ERR = 15, + GF_DOM_EXC_VALIDATION_ERR = 16, + GF_DOM_EXC_TYPE_MISMATCH_ERR = 17, +}; + +int dom_throw_exception(struct JSContext *c, u32 code); + +void gf_sg_handle_dom_event_for_vrml(GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer); + +void gf_sg_load_script_extensions(GF_SceneGraph *sg, struct JSContext *c, struct JSObject *obj, Bool unload); + +#endif /*GPAC_HAS_SPIDERMONKEY*/ + +GF_Err gf_sg_reload_xml_doc(const char *src, GF_SceneGraph *scene); + +SVG_Element *gf_svg_create_node(u32 tag); +Bool gf_svg_node_init(GF_Node *node); +void gf_svg_node_del(GF_Node *node); +Bool gf_svg_node_changed(GF_Node *node, GF_FieldInfo *field); +const char *gf_xml_get_element_name(GF_Node *node); + +SVGAttribute *gf_node_create_attribute_from_datatype(u32 data_type, u32 attribute_tag); + +GF_Err gf_node_get_attribute_by_name(GF_Node *node, char *name, u32 xmlns_code, Bool create_if_not_found, Bool set_default, GF_FieldInfo *field); +void *gf_svg_get_property_pointer_from_tag(SVGPropertiesPointers *output_property_context, u32 prop_tag); +void *gf_svg_get_property_pointer(SVG_Element *elt, void *input_attribute, + SVGPropertiesPointers *output_property_context); + +Bool gf_svg_is_property(GF_Node *node, GF_FieldInfo *target_attribute); + +/*exported for LASeR paring*/ +u32 svg_parse_point(SVG_Point *p, char *value_string); + +/*activates node. This is used by LASeR:activate and whenever a node is inserted in the scene +through DOM*/ +GF_Err gf_node_activate(GF_Node *node); +/*deactivates node. This is used by LASeR:deactivate and whenever a node is removed from the scene +through DOM*/ +GF_Err gf_node_deactivate(GF_Node *node); + +/*post a listener to be added - this is only used by LASeR:activate and DOM.addEventListener. This +is to ensure that when a node is processing an event creating a new listener on this node, this listener +will not be triggered*/ +void gf_dom_listener_post_add(GF_Node *obs, GF_Node *listener); +/*process all pending add_listener request*/ +void gf_dom_listener_process_add(GF_SceneGraph *sg); + + +void gf_node_delete_attributes(GF_Node *node); + + +typedef GF_DOMNode XBL_Element; +const char *gf_xbl_get_element_name(u32 tag); +u32 gf_xbl_get_element_tag(const char *element_name); +XBL_Element *gf_xbl_create_node(u32 ElementTag); +u32 gf_xbl_get_attribute_tag(u32 element_tag, const char *attribute_name); +GF_DOMAttribute *gf_xbl_create_attribute(GF_DOMNode *elt, u32 tag); + + +GF_Node *gf_xml_node_clone(GF_SceneGraph *inScene, GF_Node *orig, GF_Node *cloned_parent, char *inst_id, Bool deep); + +GF_Err gf_dom_listener_del(GF_Node *listener, GF_DOMEventTarget *target); + +GF_DOMHandler *gf_dom_listener_build_ex(GF_Node *node, u32 event_type, u32 event_parameter, GF_Node *handler, GF_Node **out_listener); + + +#endif /*_GF_SCENEGRAPH_DEV_H_*/ + diff --git a/include/gpac/internal/swf_dev.h b/include/gpac/internal/swf_dev.h new file mode 100644 index 0000000..3eb9ba1 --- /dev/null +++ b/include/gpac/internal/swf_dev.h @@ -0,0 +1,361 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_SWF_DEV_H_ +#define _GF_SWF_DEV_H_ + +#include <gpac/scene_manager.h> +#include <gpac/color.h> + +#define SWF_TWIP_SCALE (1/20.0f) + + +typedef struct SWFReader SWFReader; +typedef struct SWFSound SWFSound; +typedef struct SWFText SWFText; +typedef struct SWFEditText SWFEditText; +typedef struct SWF_Button SWF_Button; +typedef struct SWFShape SWFShape; +typedef struct SWFFont SWFFont; +typedef struct SWFAction SWFAction; + +enum +{ + SWF_PLACE , + SWF_REPLACE, + SWF_MOVE, +}; + + +struct SWFReader +{ + GF_SceneLoader *load; + FILE *input; + + char *localPath; + /*file header*/ + u32 length; + char *mem; + u32 frame_rate; + u32 frame_count; + Fixed width, height; + Bool has_interact, no_as; + + /*copy of the swf import flags*/ + u32 flags; + + /*bit reader*/ + GF_BitStream *bs; + GF_Err ioerr; + + u32 current_frame; + + /*current tag*/ + u32 tag, size; + + GF_List *display_list; + u32 max_depth; + + /*defined fonts*/ + GF_List *fonts; + + /*define sounds*/ + GF_List *sounds; + + /*the one and only sound stream for current timeline*/ + SWFSound *sound_stream; + + /*when creating sprites: + 1- all BIFS AUs in sprites are random access + 2- depth is ignored in Sprites + */ + u32 current_sprite_id; + + /*the parser can decide to remove nearly aligned pppoints in lineTo sequences*/ + /*flatten limit - 0 means no flattening*/ + Fixed flat_limit; + /*number of points removed*/ + u32 flatten_points; + + u8 *jpeg_hdr; + u32 jpeg_hdr_size; + + + /*callback functions for translator*/ + GF_Err (*set_backcol)(SWFReader *read, u32 xrgb); + GF_Err (*show_frame)(SWFReader *read); + + /*checks if display list is large enough - returns 1 if yes, 0 otherwise (and allocate space)*/ + Bool (*allocate_depth)(SWFReader *read, u32 depth); + GF_Err (*place_obj)(SWFReader *read, u32 depth, u32 ID, u32 prev_id, u32 type, GF_Matrix2D *mat, GF_ColorMatrix *cmat, GF_Matrix2D *prev_mat, GF_ColorMatrix *prev_cmat); + GF_Err (*remove_obj)(SWFReader *read, u32 depth, u32 ID); + + GF_Err (*define_shape)(SWFReader *read, SWFShape *shape, SWFFont *parent_font, Bool last_sub_shape); + GF_Err (*define_sprite)(SWFReader *read, u32 nb_frames); + GF_Err (*define_text)(SWFReader *read, SWFText *text); + GF_Err (*define_edit_text)(SWFReader *read, SWFEditText *text); + /*@button is NULL to signal end of button declaration, non-null otherwise. "action" callback will be + called inbetween*/ + GF_Err (*define_button)(SWFReader *read, SWF_Button *button); + + GF_Err (*setup_image)(SWFReader *read, u32 ID, char *fileName); + /*called whenever a sound is found. For soundstreams, called twice, once on the header (declaration), + and one on the first soundstream block for offset signaling*/ + GF_Err (*setup_sound)(SWFReader *read, SWFSound *snd, Bool soundstream_first_block); + GF_Err (*start_sound)(SWFReader *read, SWFSound *snd, Bool stop); + /*performs an action, returns 0 if action not supported*/ + Bool (*action)(SWFReader *read, SWFAction *act); + + void (*finalize)(SWFReader *read); + + + /* <BIFS conversion state> */ + + /*all simple appearances (no texture)*/ + GF_List *apps; + + GF_List *buttons; + + /*current BIFS stream*/ + GF_StreamContext *bifs_es; + GF_AUContext *bifs_au; + + GF_StreamContext *bifs_dict_es; + GF_AUContext *bifs_dict_au; + + /*for sound insert*/ + GF_Node *root; + + /*current OD AU*/ + GF_StreamContext *od_es; + GF_AUContext *od_au; + + GF_Node *cur_shape; + u16 prev_od_id, prev_es_id; + + u32 wait_frame; + SWF_Button *btn; + GF_List *btn_over, *btn_not_over, *btn_active, *btn_not_active; + + /* </BIFS conversion state> */ +}; + + +void swf_report(SWFReader *read, GF_Err e, char *format, ...); +SWFFont *swf_find_font(SWFReader *read, u32 fontID); +GF_Err swf_parse_sprite(SWFReader *read); + + +GF_Err swf_to_bifs_init(SWFReader *read); + + + +typedef struct +{ + Fixed x, y; + Fixed w, h; +} SWFRec; + +typedef struct +{ + /*0: not defined, otherwise index of shape*/ + u32 nbType; + /*0: moveTo, 1: lineTo, 2: quad curveTo*/ + u32 *types; + SFVec2f *pts; + u32 nbPts; + /*used by SWF->BIFS for IndexedCurveSet*/ + u32 *idx; +} SWFPath; + +typedef struct +{ + u32 type; + u32 solid_col; + u32 nbGrad; + u32 *grad_col; + u8 *grad_ratio; + GF_Matrix2D mat; + u32 img_id; + Fixed width; + + SWFPath *path; +} SWFShapeRec; + +struct SWFShape +{ + GF_List *fill_left, *fill_right, *lines; + u32 ID; + SWFRec rc; +}; + +/*SWF font object*/ +struct SWFFont +{ + u32 fontID; + u32 nbGlyphs; + GF_List *glyphs; + + /*the following may all be overridden by a DefineFontInfo*/ + + /*index -> glyph code*/ + u16 *glyph_codes; + /*index -> glyph advance*/ + s16 *glyph_adv; + + /*font flags (SWF 3.0)*/ + Bool has_layout; + Bool has_shiftJIS; + Bool is_unicode, is_ansi; + Bool is_bold, is_italic; + s16 ascent, descent, leading; + + /*font familly*/ + char *fontName; +}; + +/*chunk of text with the same aspect (font, col)*/ +typedef struct +{ + u32 fontID; + u32 col; + /*font size*/ + u32 fontSize; + /*origin point in local metrics*/ + Fixed orig_x, orig_y; + + u32 nbGlyphs; + u32 *indexes; + Fixed *dx; +} SWFGlyphRec; + +struct SWFText +{ + u32 ID; + GF_Matrix2D mat; + GF_List *text; +}; + +struct SWFEditText +{ + u32 ID; + char *init_value; + SWFRec bounds; + Bool word_wrap, multiline, password, read_only, auto_size, no_select, html, outlines, has_layout, border; + u32 color; + Fixed max_length, font_height; + u32 fontID; + + u32 align; + Fixed left, right, indent, leading; +}; + + +enum +{ + SWF_SND_UNCOMP = 0, + SWF_SND_ADPCM, + SWF_SND_MP3 +}; + +struct SWFSound +{ + u32 ID; + u8 format; + /*0: 5.5k - 1: 11k - 2: 22k - 3: 44k*/ + u8 sound_rate; + u8 bits_per_sample; + Bool stereo; + u16 sample_count; + u32 frame_delay_ms; + + /*IO*/ + FILE *output; + char *szFileName; + + /*set when sound is setup (OD inserted)*/ + Bool is_setup; +}; + +typedef struct +{ + /*interaction states*/ + Bool hitTest, down, over, up; + u32 character_id; + u16 depth; + GF_Matrix2D mx; + GF_ColorMatrix cmx; + Bool skip; +} SWF_ButtonRecord; + + +struct SWF_Button +{ + u32 count; + SWF_ButtonRecord buttons[40]; + u32 ID; +}; + +/*AS codes.*/ +enum +{ + GF_SWF_AS3_GOTO_FRAME, + GF_SWF_AS3_GET_URL, + GF_SWF_AS3_NEXT_FRAME, + GF_SWF_AS3_PREV_FRAME, + GF_SWF_AS3_PLAY, + GF_SWF_AS3_STOP, + GF_SWF_AS3_TOGGLE_QUALITY, + GF_SWF_AS3_STOP_SOUNDS, + GF_SWF_AS3_WAIT_FOR_FRAME, + GF_SWF_AS3_SET_TARGET, + GF_SWF_AS3_GOTO_LABEL, +}; + +enum +{ + GF_SWF_COND_IDLE_TO_OVERDOWN = 1, + GF_SWF_COND_OUTDOWN_TO_IDLE = 1<<1, + GF_SWF_COND_OUTDOWN_TO_OVERDOWN = 1<<2, + GF_SWF_COND_OVERDOWN_TO_OUTDOWN = 1<<3, + GF_SWF_COND_OVERDOWN_TO_OUTUP = 1<<4, + GF_SWF_COND_OVERUP_TO_OVERDOWN = 1<<5, + GF_SWF_COND_OVERUP_TO_IDLE = 1<<6, + GF_SWF_COND_IDLE_TO_OVERUP = 1<<7, + GF_SWF_COND_OVERDOWN_TO_IDLE = 1<<8, +}; + +struct SWFAction +{ + u32 type; + u32 frame_number; + u32 button_mask, button_key; + /*target (geturl/set_target), label (goto_frame)*/ + char *target; + char *url; +}; + + + +#endif /*_GF_SWF_DEV_H_*/ diff --git a/include/gpac/internal/terminal_dev.h b/include/gpac/internal/terminal_dev.h new file mode 100644 index 0000000..1559aa6 --- /dev/null +++ b/include/gpac/internal/terminal_dev.h @@ -0,0 +1,851 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Stream Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_TERMINAL_DEV_H_ +#define _GF_TERMINAL_DEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#include <gpac/terminal.h> +#include <gpac/mpeg4_odf.h> + +#include <gpac/modules/service.h> +#include <gpac/modules/codec.h> +#include <gpac/modules/ipmp.h> +#include <gpac/mediaobject.h> +#include <gpac/thread.h> + +typedef struct _inline_scene GF_InlineScene; +typedef struct _media_manager GF_MediaManager; +typedef struct _object_clock GF_Clock; +typedef struct _es_channel GF_Channel; +typedef struct _generic_codec GF_Codec; +typedef struct _composition_memory GF_CompositionMemory; + + +struct _net_service +{ + /*the module handling this service - must be declared first to typecast with GF_DownlaodSession upon deletion*/ + GF_InputService *ifce; + + /*the terminal*/ + struct _tag_terminal *term; + /*service url*/ + char *url; + /*od_manager owning service, NULL for services created for remote channels*/ + struct _od_manager *owner; + /*number of attached remote channels ODM (ESD URLs)*/ + u32 nb_ch_users; + /*number of attached remote ODM (OD URLs)*/ + u32 nb_odm_users; + + /*clock objects. Kept at service level since ESID namespace is the service one*/ + GF_List *Clocks; + /*all downloaders objects used in this service*/ + GF_List *dnloads; + /*cache asscoiated with service, if any*/ + GF_StreamingCache *cache; +}; + + +/*opens service - performs URL concatenation if parent service specified*/ +GF_ClientService *gf_term_service_new(GF_Terminal *term, GF_ObjectManager *owner, const char *url, GF_ClientService *parent_service, GF_Err *ret_code); +/*destroy service*/ +void gf_term_service_del(GF_ClientService *nets); + +/*access to the module interfaces - cf net_api.h GF_InputService for details*/ +GF_Err gf_term_service_command(GF_ClientService *ns, GF_NetworkCommand *com); +Bool gf_term_service_can_handle_url(GF_ClientService *ns, char *url); + +GF_Err gf_term_channel_get_sl_packet(GF_ClientService *ns, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *is_compressed, GF_Err *out_reception_status, Bool *is_new_data); +GF_Err gf_term_channel_release_sl_packet(GF_ClientService *ns, LPNETCHANNEL channel); + +/*cache open/close*/ +GF_Err gf_term_service_cache_load(GF_ClientService *ns); +GF_Err gf_term_service_cache_close(GF_ClientService *ns, Bool no_save); + +/*forwards all clocks of the given amount of time. Can only be used when terminal is in paused mode +this is mainly designed for movie dumping in MP4Client*/ +GF_Err gf_term_step_clocks(GF_Terminal * term, u32 ms_diff); + +void gf_term_sample_clocks(GF_Terminal *term); + +/* + Inline scene stuff +*/ +struct _inline_scene +{ + /*root OD of the subscene, ALWAYS namespace of the parent scene*/ + struct _od_manager *root_od; + /*scene codec: top level decoder decoding/generating the scene - can be BIFS, VRML parser, etc*/ + struct _generic_codec *scene_codec; + /*OD codec - specific to MPEG-4, only present at the inline level (media ressources are always scoped here)*/ + struct _generic_codec *od_codec; + + /*struct _od_managers used, namespace of this scene. The chain does not have the root_od + it only contains OD sent through OD UPDATE in the OD stream(s) attached + to this scene. Remote ODs are not added, only there parents are*/ + GF_List *ODlist; + /*list of MOs (links between OD and nodes)*/ + GF_List *media_objects; + /*list of externproto libraries*/ + GF_List *extern_protos; + /*list of nodes using this inline*/ + GF_List *inline_nodes; + /*list of extra scene graphs (text streams, generic OSDs, ...)*/ + GF_List *extra_scenes; + /*inline scene graph*/ + GF_SceneGraph *graph; + /*graph state - if not attached, no traversing of inline + 0: not attached + 1: attached + 2: temp graph attached. The temp graph is generated when waiting for the first scene AU to be processed + */ + u32 graph_attached; + /*togles inline restart - needed because the restart may be triggered from inside the scene or from + parent scene, hence 2 render passes must be used + special value 2 means scene URL changes (for anchor navigation*/ + u32 needs_restart; + /*duration of inline scene*/ + u64 duration; + /*if not 0, all objects in the scene will run on this clock. Needed in GPAC when clock references do not + respect object graph (eg IOD depending on external stream for clock)*/ + u16 force_sub_clock_id; + /*world info node or title node*/ + void *world_info; + + Bool is_dynamic_scene; + /*clock for dynamic scene - current assumption is that all selected streams are synchronized in the dyn scene*/ + GF_Clock *dyn_ck; + /*URLs of current video, audio and subs (we can't store objects since they may be destroyed when seeking)*/ + SFURL visual_url, audio_url, text_url; + /*set to 1 when single time-line presentation with ONE OD AU is detected - the goal is to prevent + OD shutdown/startup when seeking. This will also remove unneeded net traffic for AddChannel/RemoveChannel + like RTSP TEARDOWN/SETUP*/ + Bool static_media_ressources; + + /*current simulation time of the compositor*/ + Double simulation_time; + + /*current IRI fragment if any*/ + char *fragment_uri; + + /*secondary resource scene*/ + Bool secondary_resource; + + char *redirect_xml_base; +}; + +GF_InlineScene *gf_inline_new(GF_InlineScene *parentScene); +void gf_inline_del(GF_InlineScene *is); +struct _od_manager *gf_inline_find_odm(GF_InlineScene *is, u16 OD_ID); +void gf_inline_disconnect(GF_InlineScene *is, Bool for_shutdown); +void gf_inline_remove_object(GF_InlineScene *is, GF_ObjectManager *odm, Bool for_shutdown); +/*browse all (media) channels and send buffering info to the app*/ +void gf_inline_buffering_info(GF_InlineScene *is); +void gf_inline_attach_to_compositor(GF_InlineScene *is); +struct _mediaobj *gf_inline_get_media_object(GF_InlineScene *is, MFURL *url, u32 obj_type_hint, Bool lock_timelines); +struct _mediaobj *gf_inline_get_media_object_ex(GF_InlineScene *is, MFURL *url, u32 obj_type_hint, Bool lock_timelines, struct _mediaobj *sync_ref, Bool always_load_new, GF_Node *node_ptr); +void gf_inline_setup_object(GF_InlineScene *is, GF_ObjectManager *odm); +/*restarts inline scene - care has to be taken not to remove the scene while it is traversed*/ +void gf_inline_restart(GF_InlineScene *is); +/*updates scene duration based on settings*/ +void gf_inline_set_duration(GF_InlineScene *is); +/*locate media object by ODID (non dynamic ODs) or URL (dynamic ODs)*/ +struct _mediaobj *gf_inline_find_object(GF_InlineScene *is, u16 ODID, char *url); +/*returns scene time in sec - exact meaning of time depends on standard used*/ +Double gf_inline_get_time(void *_is); +/*returns true if the given node DEF name is the url target view (eg blabla#myview)*/ +Bool gf_inline_default_scene_viewpoint(GF_Node *node); +/*register extra scene graph for on-screen display*/ +void gf_inline_register_extra_graph(GF_InlineScene *is, GF_SceneGraph *extra_scene, Bool do_remove); +/*forces scene size info (without changing pixel metrics) - this may be needed by modules using extra graphs (like timedtext)*/ +void gf_inline_force_scene_size(GF_InlineScene *is, u32 width, u32 height); +/*regenerate a scene graph based on available objects - can only be called for dynamic OD streams*/ +void gf_inline_regenerate(GF_InlineScene *is); +/*selects given ODM for dynamic scenes*/ +void gf_inline_select_object(GF_InlineScene *is, GF_ObjectManager *odm); +/*restarts dynamic scene from given time: scene graph is not reseted, objects are just restarted +instead of closed and reopened. If a media control is present on inline, from_time is overriden by MC range*/ +void gf_inline_restart_dynamic(GF_InlineScene *is, u64 from_time); +/*owner inline node has been modified*/ +void gf_inline_on_modified(GF_Node *node); +/*returns scene graph associated with an externProto lib - exported for VRML/X3D loaded*/ +GF_SceneGraph *gf_inline_get_proto_lib(void *_is, MFURL *lib_url); +/*exported for compositor: handles filtering of "self" parameter indicating anchor only acts on container inline scene +not root one. Returns 1 if handled (cf user.h, navigate event)*/ +Bool gf_inline_process_anchor(GF_Node *caller, GF_Event *evt); +/*extern proto fetcher*/ +GF_SceneGraph *gf_inline_get_proto_lib(void *SceneCallback, MFURL *lib_url); +void gf_inline_force_scene_size_video(GF_InlineScene *is, GF_MediaObject *mo); +void gf_inline_sample_time(GF_InlineScene *is); +/*compares object URL with another URL - ONLY USE THIS WITH DYNAMIC ODs*/ +Bool gf_mo_is_same_url(GF_MediaObject *obj, MFURL *inline_url); + +void gf_mo_update_caps(GF_MediaObject *mo); + + +GF_Node *gf_inline_get_subscene_root(GF_Node *inline_node); +GF_Node *gf_inline_get_parent_node(GF_Node *node, u32 idx); + +const char *gf_inline_get_fragment_uri(GF_Node *node); +void gf_inline_set_fragment_uri(GF_Node *node, const char *uri); + +enum +{ + /*threading up to decoder*/ + GF_TERM_THREAD_FREE, + /*single thread for all decoders*/ + GF_TERM_THREAD_SINGLE, + /*all media (image, video, audio) decoders are threaded*/ + GF_TERM_THREAD_MULTI, +}; + +enum +{ + GF_TERM_RUNNING= 1, + GF_TERM_DEAD = 1<<1, + GF_TERM_SINGLE_THREAD = 1<<2, + GF_TERM_MULTI_THREAD = 1<<3, + GF_TERM_SYSDEC_RESYNC = 1<<4, + GF_TERM_SINGLE_CLOCK = 1<<5, + GF_TERM_DRAW_FRAME = 1<<6 +}; + + + +struct _tag_terminal +{ + u32 flags; + + /*callback to user application*/ + GF_User *user; + /*scene compositor*/ + struct __tag_compositor *compositor; + /*file downloader*/ + GF_DownloadManager *downloader; + /*top level scene*/ + GF_InlineScene *root_scene; + + /*Media manager*/ + GF_List *codecs; + /*mutex for decoder access*/ + GF_Mutex *mm_mx; + /*decoding thread*/ + GF_Thread *mm_thread; + /*last codec used in mm loop*/ + u32 last_codec; + /*thread priority*/ + s32 priority; + u32 cumulated_priority; + /*frame duration*/ + u32 frame_duration; + + /*net services*/ + GF_List *net_services; + /*net services to be destroyed*/ + GF_List *net_services_to_remove; + /*channels waiting for service CONNECT ack to be setup*/ + GF_List *channels_pending; + /*media objects pending for stop/play*/ + GF_List *media_queue; + /*network lock*/ + GF_Mutex *net_mx; + /*all X3D key/mouse/string sensors*/ + GF_List *x3d_sensors; + /*all input stream decoders*/ + GF_List *input_streams; + + /*options (cf config doc)*/ + Bool enable_cache; + /*data timeout for network buffering in ms - if no data is received within this timeout + the initial buffering aborts. */ + u32 net_data_timeout; + + u32 play_state; + + u32 reload_state; + char *reload_url; + + /*special list used by nodes needing a call to RenderNode but not in the traverese scene graph (VRML/MPEG-4 protos only). + For these nodes, the traverse effect passed will be NULL. This is only used by InputSensor node at the moment*/ + GF_List *nodes_pending; + + /*root node of the user prefs*/ + GF_SceneGraph *dcci_doc; + + GF_List *extensions; + GF_List *unthreaded_extensions; +}; + + + +GF_Err gf_term_init_scheduler(GF_Terminal *term, u32 threading_mode); +void gf_term_stop_scheduler(GF_Terminal *term); +void gf_term_add_codec(GF_Terminal *term, GF_Codec *codec); +void gf_term_remove_codec(GF_Terminal *term, GF_Codec *codec); +void gf_term_start_codec(GF_Codec *codec); +void gf_term_stop_codec(GF_Codec *codec); +void gf_term_set_threading(GF_Terminal *term, u32 mode); +void gf_term_set_priority(GF_Terminal *term, s32 Priority); + + +/*error report function*/ +void gf_term_message(GF_Terminal *app, const char *service, const char *message, GF_Err error); +/*creates service for given OD / URL*/ +void gf_term_connect_object(GF_Terminal *app, GF_ObjectManager *odm, char *serviceURL, GF_ClientService *ParentService); +/*creates service for given channel / URL*/ +GF_Err gf_term_connect_remote_channel(GF_Terminal *app, GF_Channel *ch, char *URL); + +/*called by media manager to perform service maintenance: +servive shutdown: this is needed because service handler may be asynchronous +object Play: this is needed to properly handle multiplexed sources (all channels must be connected before play) +service restart +*/ +void gf_term_handle_services(GF_Terminal *app); +/*close service and queue for delete*/ +void gf_term_close_services(GF_Terminal *app, GF_ClientService *service); + +/*locks net manager*/ +void gf_term_lock_net(GF_Terminal *app, Bool LockIt); + + +/*locks scene compositor*/ +void gf_term_lock_compositor(GF_Terminal *app, Bool LockIt); +/*get scene compositor time - FIXME this is not flexible enough for SMIL/Multiple time containers*/ +u32 gf_term_get_time(GF_Terminal *term); +/*forces scene composition*/ +void gf_term_invalidate_compositor(GF_Terminal *term); + +/*callbacks for scene graph library so that all related ESM nodes are properly instanciated*/ +void gf_term_node_callback(void *_is, u32 type, GF_Node *node, void *param); + +/*add/rem node requiring a call to render without being present in traversed graph (VRML/MPEG-4 protos). +For these nodes, the traverse effect passed will be NULL.*/ +void gf_term_queue_node_traverse(GF_Terminal *term, GF_Node *node); +void gf_term_unqueue_node_traverse(GF_Terminal *term, GF_Node *node); + + + +/*clock*/ +struct _object_clock +{ + u16 clockID; + GF_Terminal *term; + GF_Mutex *mx; + /*no_time_ctrl : set if ANY stream running on this clock has no time control capabilities - this avoids applying + mediaControl and others that would break stream dependencies*/ + Bool use_ocr, clock_init, has_seen_eos, no_time_ctrl; + u32 init_time, StartTime, PauseTime, Paused; + /*the number of streams buffering on this clock*/ + u32 Buffering; + /*associated media control if any*/ + struct _media_control *mc; + /*for MC only (no FlexTime)*/ + Fixed speed; + u32 discontinuity_time; + s32 drift; +}; + +/*destroys clock*/ +void gf_clock_del(GF_Clock *ck); +/*finds a clock by ID or by ES_ID*/ +GF_Clock *gf_clock_find(GF_List *Clocks, u16 clockID, u16 ES_ID); +/*attach clock returns a new clock or the clock this stream (ES_ID) depends on (OCR_ES_ID) +hasOCR indicates whether the stream being attached carries object clock references +@clocks: list of clocks in ES namespace (service) +@is: inline scene to solve clock dependencies +*/ +GF_Clock *gf_clock_attach(GF_List *clocks, GF_InlineScene *is, u16 OCR_ES_ID, u16 ES_ID, s32 hasOCR); +/*reset clock (only called by channel owning clock)*/ +void gf_clock_reset(GF_Clock *ck); +/*stops clock (only called for scene clock)*/ +void gf_clock_stop(GF_Clock *ck); +/*return clock time in ms*/ +u32 gf_clock_time(GF_Clock *ck); +/*return ellapsed time in ms since start of the clock*/ +u32 gf_clock_ellapse_time(GF_Clock *ck); +/*sets clock time - FIXME: drift updates for OCRs*/ +void gf_clock_set_time(GF_Clock *ck, u32 TS); +/*return clock time in ms without drift adjustment - used by audio objects only*/ +u32 gf_clock_real_time(GF_Clock *ck); +/*pause the clock*/ +void gf_clock_pause(GF_Clock *ck); +/*resume the clock*/ +void gf_clock_resume(GF_Clock *ck); +/*returns true if clock started*/ +Bool gf_clock_is_started(GF_Clock *ck); +/*toggles buffering on (clock is paused at the first stream buffering) */ +void gf_clock_buffer_on(GF_Clock *ck); +/*toggles buffering off (clock is paused at the last stream restarting) */ +void gf_clock_buffer_off(GF_Clock *ck); +/*set clock speed scaling factor*/ +void gf_clock_set_speed(GF_Clock *ck, Fixed speed); +/*set clock drift - used to resync audio*/ +void gf_clock_adjust_drift(GF_Clock *ck, s32 ms_drift); + +enum +{ + /*channel is setup and waits for connection request*/ + GF_ESM_ES_SETUP = 0, + /*waiting for server reply*/ + GF_ESM_ES_WAIT_FOR_ACK, + /*connection OK*/ + GF_ESM_ES_CONNECTED, + /*data exchange on this service/channel*/ + GF_ESM_ES_RUNNING, + /*deconnection OK - a download channel can automatically disconnect when download is done*/ + GF_ESM_ES_DISCONNECTED, + /*service/channel is not (no longer) available/found and should be removed*/ + GF_ESM_ES_UNAVAILABLE +}; + +/*data channel (elementary stream)*/ +struct _es_channel +{ + /*security check on channel*/ + u32 chan_id; + /*service this channel belongs to*/ + GF_ClientService *service; + /*stream descriptor*/ + GF_ESD *esd; + /*parent OD for this stream*/ + struct _od_manager *odm; + u32 es_state; + Bool is_pulling; + u32 media_padding_bytes; + /*IO mutex*/ + GF_Mutex *mx; + u32 AU_Count; + /*decoding buffers for push mode*/ + struct _decoding_buffer * AU_buffer_first, * AU_buffer_last; + /*static decoding buffer for pull mode*/ + struct _decoding_buffer * AU_buffer_pull; + /*channel buffer flag*/ + Bool BufferOn; + /*min level to trigger buffering on, max to trigger it off. */ + u32 MinBuffer, MaxBuffer; + /*amount of buffered media - this is the DTS of the last received AU minus the onject clock time, to make sure + we always have MaxBuffer ms ready for composition when resuming the clock*/ + s32 BufferTime; + /*last received AU time - if exceeding a certain time and buffering is on, buffering is turned off. + This is needed for streams with very short duration (less than buffer time) and stream with only one AU (BIFS/OD)*/ + u32 last_au_time; + /*Current reassemnbling buffer - currently packets are NOT reordered, only AUs are*/ + char *buffer; + u32 len, allocSize; + /*only for last packet of an AU*/ + u8 padingBits; + Bool IsEndOfStream; + /* SL reassembler */ + /*current AU TSs*/ + u32 DTS, CTS; + /*AU and Packet seq num info*/ + u32 au_sn, pck_sn; + u32 max_au_sn, max_pck_sn; + /*the AU length indicated in the SL Header. */ + u32 AULength; + /*state indicator: 0 OK, 1: not tuned in, 2: has error and needs RAP*/ + u32 stream_state; + /*the AU in reception is RAP*/ + Bool IsRap; + /*signal that next AU is an AU start*/ + Bool NextIsAUStart; + /*if codec resilient, packet drops are not considered as fatal for AU reconstruction (eg no wait for RAP)*/ + Bool codec_resilient; + /*when starting a channel, the first AU is ALWAYS fetched when buffering - this forces + BIFS and OD to be decoded and first frame render, in order to detect media objects that would also need + buffering - note this doesn't affect the clock, it is still paused if buffering*/ + Bool first_au_fetched; + + /* used in Carousel, to skip packets until the end of AU */ + Bool skip_carousel_au; + + /* TimeStamp to Media Time mapping*/ + /*TS (in TSResolution) corresponding to the SeedTime of the decoder. Delivered by net, otherwise 0*/ + u64 seed_ts; + /*media time offset corresponding to SeedTS. This is needed when the channel doesn't own the clock*/ + u32 ts_offset; + /*scaling factors to remap to timestamps in milliseconds*/ + u64 ts_res; + Double ocr_scale; + /*clock driving this stream - currently only CTS is supported (no OCR)*/ + struct _object_clock *clock; + /*flag for clock init. Only a channel owning the clock will set this flag on clock init*/ + Bool IsClockInit; + + /*duration of last received AU if any, 0 if not known (most of the time)*/ + u32 au_duration; + /*A channel with this flag set considers each incoming packet as a complete AU and assigns timestamps + upon reception matching the reception time, then dispatching it into the decoding buffer (only tested + with audi video). This flag is turned on by setting esd->slconfig->predefined to 'SLPredef_SkipSL' */ + Bool skip_sl; + + /*indicates that decoding can be called directly when receiving a complete AU on this channel + This is used by systems streams in non-seekable (eg broadcast/multicast, MPEG-2 TS multiplexes) to + make sure resources are setup as fast as possible. If the AU is too early, it will be kept in the + decoding buffer*/ + Bool dispatch_after_db; + + /*indicates that decoding is called directly when receiving a packet on this channel + This is used to bypass SL defragmenting and decoding buffer for EIT internal streams*/ + Bool bypass_sl_and_db; + + GF_IPMPTool *ipmp_tool; + Bool is_protected; + + /*TSs as received from network - these are used for cache storage*/ + u64 net_dts, net_cts; +}; + +/*creates a new channel for this stream*/ +GF_Channel *gf_es_new(GF_ESD *esd); +/*destroys channel*/ +void gf_es_del(GF_Channel *ch); +/*(un)locks channel*/ +void gf_es_lock(GF_Channel *ch, u32 LockIt); +/*setup channel for reception of data*/ +GF_Err gf_es_start(GF_Channel *ch); +/*stop channel from receiving data*/ +GF_Err gf_es_stop(GF_Channel *ch); +/*handles reception of an SL PDU*/ +void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *StreamBuf, u32 StreamLength, GF_SLHeader *header, GF_Err reception_status); +/*signals end of stream on the channel*/ +void gf_es_on_eos(GF_Channel *ch); +/*fetches first AU available for decoding on this channel*/ +struct _decoding_buffer *gf_es_get_au(GF_Channel *ch); +/*drops first AU on this channel*/ +void gf_es_drop_au(GF_Channel *ch); +/*performs final setup upon connection confirm*/ +void gf_es_on_connect(GF_Channel *ch); +/*reconfigure SL for this channel*/ +void gf_es_reconfig_sl(GF_Channel *ch, GF_SLConfig *slc); +/*hack for streaming: whenever a time map (media time <-> TS time) event is received on the channel reset decoding buffer +this is needed because all server tested resend packets on already running channel*/ +void gf_es_map_time(GF_Channel *ch, Bool reset); +/*dummy channels are used by scene decoders which don't use ESM but load directly the scene graph themselves +these channels are ALWAYS pulling ones, and this function will init the channel clock if needed*/ +void gf_es_init_dummy(GF_Channel *ch); +/*setup DRM info*/ +void gf_es_config_drm(GF_Channel *ch, GF_NetComDRMConfig *isma_cryp); + +/* + decoder stuff +*/ +enum +{ + /*stop: the decoder is not playing*/ + GF_ESM_CODEC_STOP = 0, + /*stop: the decoder is playing*/ + GF_ESM_CODEC_PLAY = 1, + /*End Of Stream: when the base layer signals it's done, this triggers media-specific + handling of the CB. + For video, the output is kept alive, For audio, the output is reseted (don't want audio loop ;)*/ + GF_ESM_CODEC_EOS = 2, + /*pause: the decoder is stoped but the CB is kept intact + THIS IS NOT USED AS A CODEC STATUS, but only for signaling that the CB shouldn't + be reseted - the real status of a "paused" decoder is STOP*/ + GF_ESM_CODEC_PAUSE = 3, + /*Buffer: transition state: the decoder runs (fetch data/decode) but the clock + is not running (no composition). This is used for rebuffering channels (rtp...)*/ + GF_ESM_CODEC_BUFFER = 4 +}; + +enum +{ + GF_ESM_CODEC_HAS_UPSTREAM = 1, + /*the codec uses the interface from another codec (only used by private scene streams to handle + any intern sprite/animation streams)*/ + GF_ESM_CODEC_IS_USE = 1<<1, + /*set for OD codec when static (ressources are declared in OD stream esd a la ISMA*/ + GF_ESM_CODEC_IS_STATIC_OD = 1<<2, +}; + +struct _generic_codec +{ + /*codec type (streamType from base layer)*/ + u32 type; + u32 flags; + /*current decoder interface */ + GF_BaseDecoder *decio; + /*composition memory for media streams*/ + struct _composition_memory *CB; + /*input media channles*/ + GF_List *inChannels; + /*a pointer to the OD that owns the decoder.*/ + struct _od_manager *odm; + u32 Status; + Bool Muted; + struct _object_clock *ck; + /*priority of this media object. This is ALWAYS the base layer priority + PriorityBoost is set when the CB is under critical limit (for now only audio uses the feature) + and results in a bigger time slice for the codec. Only on/off value for now*/ + u32 Priority, PriorityBoost; + /*last processed DTS - sanity check for scalability*/ + u32 last_unit_dts; + /*last processed CTS on base layer - seeking detection*/ + u32 last_unit_cts; + /*in case the codec performs temporal re-ordering itself*/ + Bool is_reordering; + u32 prev_au_size; + u32 bytes_per_sec; + Double fps; + + /*statistics*/ + u32 last_stat_start, cur_bit_size; + u32 avg_bit_rate, max_bit_rate; + u32 total_dec_time, nb_dec_frames, max_dec_time; + /*number of droped frames*/ + u32 nb_droped; + + /*for CTS reconstruction (channels not using SL): we cannot just update timing at each frame, not precise enough + since we use ms and not microsec TSs*/ + u32 cur_audio_bytes, cur_video_frames; +}; + +GF_Codec *gf_codec_new(GF_ObjectManager *odm, GF_ESD *base_layer, s32 PL, GF_Err *e); +void gf_codec_del(GF_Codec *codec); +GF_Err gf_codec_add_channel(GF_Codec *codec, GF_Channel *ch); +/*returns TRUE if stream was present, false otherwise*/ +Bool gf_codec_remove_channel(GF_Codec *codec, GF_Channel *ch); +GF_Err gf_codec_process(GF_Codec *codec, u32 TimeAvailable); +GF_Err gf_codec_get_capability(GF_Codec *codec, GF_CodecCapability *cap); +GF_Err gf_codec_set_capability(GF_Codec *codec, GF_CodecCapability cap); +void gf_codec_set_status(GF_Codec *codec, u32 Status); +/*returns a new codec using an existing loaded decoder - only used by private scene to handle != timelines, for +instance when loading a BT with an animation stream*/ +GF_Codec *gf_codec_use_codec(GF_Codec *codec, GF_ObjectManager *odm); + +/*OD manager*/ + + +/*all inserted ODs have this ODID*/ +#define GF_ESM_DYNAMIC_OD_ID 1050 + +enum +{ + /*flag set if object cannot be time-controloed*/ + GF_ODM_NO_TIME_CTRL = (1<<1), + /*flag set if subscene uses parent scene timeline*/ + GF_ODM_INHERIT_TIMELINE = (1<<2), + /*flag set if object has been redirected*/ + GF_ODM_REMOTE_OD = (1<<3), + /*flag set if object has profile indications*/ + GF_ODM_HAS_PROFILES = (1<<4), + /*flag set if object governs profile of inline subscenes*/ + GF_ODM_INLINE_PROFILES = (1<<5), + /*flag set if object declared by network service, not from OD stream*/ + GF_ODM_NOT_IN_OD_STREAM = (1<<6), + + /*dynamic flags*/ + + /*flag set if associated subscene must be regenerated*/ + GF_ODM_REGENERATE_SCENE = (1<<10), +}; + +enum +{ + GF_ODM_STATE_STOP, + GF_ODM_STATE_PLAY, + GF_ODM_STATE_IN_SETUP, + GF_ODM_STATE_BLOCKED, +}; + +struct _od_manager +{ + /*pointer to terminal*/ + struct _tag_terminal *term; + /*the service used by this ODM. If the service private data is this ODM, then the service was created for this ODM*/ + GF_ClientService *net_service; + /*parent scene or NULL for root scene*/ + struct _inline_scene *parentscene; + /*channels associated with this object (media channels, OCR, IPMP, OCI, etc)*/ + GF_List *channels; + /*sub scene for inline/animation or NULL */ + struct _inline_scene *subscene; + /*object codec (media or BIFS for AnimationStream) attached if any*/ + struct _generic_codec *codec; + /*OCI codec attached if any*/ + struct _generic_codec *oci_codec; + /*OCR codec attached if any*/ + struct _generic_codec *ocr_codec; + + /*MPEG-4 object descriptor*/ + GF_ObjectDescriptor *OD; + + /*exclusive access is required since rendering and media management don't always take place in the same thread*/ + GF_Mutex *mx; + + u32 flags; + + /*PLs*/ + u8 Audio_PL, Graphics_PL, OD_PL, Scene_PL, Visual_PL; + + /*interface with scene rendering*/ + struct _mediaobj *mo; + + /*number of channels with connection not yet acknowledge*/ + u32 pending_channels; + u32 state; + /* during playback: timing as evaluated by the composition memory or the scene codec */ + u32 current_time; + /*full object duration 0 if unknown*/ + u64 duration; + /* + upon start: media start time as requested by scene compositor (eg not media control) + set to -1 upon stop to postpone stop request + */ + u64 media_start_time, media_stop_time; + + /*the one and only media control currently attached to this object*/ + struct _media_control *media_ctrl; + /*the list of media control controling the object*/ + GF_List *mc_stack; + /*the media sensor(s) attached to this object*/ + GF_List *ms_stack; +}; + + +GF_ObjectManager *gf_odm_new(); +void gf_odm_del(GF_ObjectManager *ODMan); +void gf_odm_lock(GF_ObjectManager *odm, u32 LockIt); + +/*setup service entry point*/ +void gf_odm_setup_entry_point(GF_ObjectManager *odm, const char *sub_url); +/*setup OD*/ +void gf_odm_setup_object(GF_ObjectManager *odm, GF_ClientService *parent_serv); +/*disctonnect OD and removes it if desired (otherwise only STOP is propagated)*/ +void gf_odm_disconnect(GF_ObjectManager *odman, Bool do_remove); +/*setup an ESD*/ +GF_Err gf_odm_setup_es(GF_ObjectManager *odm, GF_ESD *esd, GF_ClientService *service, GF_MediaObject *sync_ref); +/*removes an ESD (this destroys associated channel if any)*/ +void gf_odm_remove_es(GF_ObjectManager *odm, u16 ES_ID); +/*set stream duration - updates object duration accordingly*/ +void gf_odm_set_duration(GF_ObjectManager *odm, GF_Channel *, u64 stream_duration); +/*signals end of stream on channels*/ +void gf_odm_on_eos(GF_ObjectManager *odm, GF_Channel *); +/*start Object streams and queue object for network PLAY*/ +void gf_odm_start(GF_ObjectManager *odm); +/*stop OD streams*/ +void gf_odm_stop(GF_ObjectManager *odm, Bool force_close); +/*send PLAY request to network - needed to properly handle multiplexed inputs +ONLY called by service handler (media manager thread)*/ +void gf_odm_play(GF_ObjectManager *odm); + +/*returns 1 if this is a segment switch, 0 otherwise - takes care of object restart if segment switch*/ +Bool gf_odm_check_segment_switch(GF_ObjectManager *odm); +/*pause object (mediaControl use only)*/ +void gf_odm_pause(GF_ObjectManager *odm); +/*resume object (mediaControl use only)*/ +void gf_odm_resume(GF_ObjectManager *odm); +/*set object speed*/ +void gf_odm_set_speed(GF_ObjectManager *odm, Fixed speed); +/*returns the clock of the media stream (video, audio or bifs), NULL otherwise */ +struct _object_clock *gf_odm_get_media_clock(GF_ObjectManager *odm); +/*adds segment descriptors targeted by the URL to the list and sort them - the input list must be empty*/ +void gf_odm_init_segments(GF_ObjectManager *odm, GF_List *list, MFURL *url); +/*returns true if this OD depends on the given clock*/ +Bool gf_odm_shares_clock(GF_ObjectManager *odm, struct _object_clock *ock); + +GF_Segment *gf_odm_find_segment(GF_ObjectManager *odm, char *descName); +/*locks ODM with destruction check - returns 0 if object manager is not attached to object*/ +Bool gf_odm_lock_mo(struct _mediaobj *mo); + + +/*GF_MediaObject: link between real object manager and scene. although there is a one-to-one mapping between a +MediaObject and an ObjectManager, we have to keep them seperated in order to handle OD remove commands which destroy +ObjectManagers. */ +struct _mediaobj +{ + /*type is as defined in constants.h # GF_MEDIA_OBJECT_* */ + u32 type; + /*one of the above flags*/ + u32 flags; + + + /* private to ESM*/ + + /*media object manager - private to the sync engine*/ + struct _od_manager *odm; + /*OD ID of the object*/ + u32 OD_ID; + /*OD URL for object not using MPEG4 OD urls*/ + MFURL URLs; + /*session join*/ + u32 num_open; + /*shared object restart handling*/ + u32 num_to_restart, num_restart; + Fixed speed; + + /*shared object info: if 0 a new frame will be checked, otherwise current is returned*/ + u32 nb_fetch; + /*frame presentation time*/ + u32 timestamp; + /*data frame size*/ + u32 framesize; + /*pointer to data frame */ + char *frame; + /*nodes currently registered with the media object - used to dispatch MediaAccessEvents*/ + GF_List *nodes; + /*pointer to the node responsible for the creation of this media object + ONLY used for scene media type (animationStreams) + Reset upon creation of the decoder. + */ + void *node_ptr; +}; + +GF_MediaObject *gf_mo_new(); + + +/*used for delayed channel setup*/ +typedef struct +{ + struct _generic_codec *dec; + struct _es_channel *ch; +} GF_ChannelSetup; + +/*post-poned channel connect*/ +GF_Err gf_odm_post_es_setup(struct _es_channel *ch, struct _generic_codec *dec, GF_Err err); + +/* + special entry point: specify directly a service interface for service input +*/ +void gf_term_attach_service(GF_Terminal *term, GF_InputService *service_hdl); + +/*media access events */ +void gf_term_service_media_event(GF_ObjectManager *odm, u32 event_type); + +u32 URL_GetODID(MFURL *url); + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_TERMINAL_DEV_H_*/ + + diff --git a/include/gpac/internal/vobsub.h b/include/gpac/internal/vobsub.h new file mode 100644 index 0000000..170f82b --- /dev/null +++ b/include/gpac/internal/vobsub.h @@ -0,0 +1,82 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) by Falco (Ivan Vecera) 2006 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_VOBSUB_H_ +#define _GF_VOBSUB_H_ + +#include <gpac/tools.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define VOBSUBIDXVER 7 + +typedef struct _tag_vobsub_pos +{ + u64 filepos; + u64 start; + u64 stop; +} vobsub_pos; + +typedef struct _tag_vobsub_lang +{ + u32 id; + char *name; + GF_List *subpos; +} vobsub_lang; + +typedef struct _tag_vobsub_file +{ + u32 width; + u32 height; + u8 palette[16][4]; + u32 num_langs; + vobsub_lang langs[32]; +} vobsub_file; + +GFINLINE static void vobsub_trim_ext(char *filename) +{ + char *pos = strchr(filename, '.'); + + if (pos != NULL) { + if (!stricmp(pos, ".idx") || !stricmp(pos, ".sub")) { + *pos = '\0'; + } + } +} + +s32 vobsub_lang_name(u16 id); +char *vobsub_lang_id(char *name); +GF_Err vobsub_read_idx(FILE *file, vobsub_file *vobsub, int *version); +void vobsub_free(vobsub_file *vobsub); +GF_Err vobsub_get_subpic_duration(char *data, u32 psize, u32 dsize, u32 *duration); +GF_Err vobsub_packetize_subpicture(FILE *fsub, u64 pts, char *data, u32 dataSize); + +#ifdef __cplusplus +} +#endif + +#endif /* _GF_VOBSUB_H_ */ diff --git a/include/gpac/ismacryp.h b/include/gpac/ismacryp.h new file mode 100644 index 0000000..caf57da --- /dev/null +++ b/include/gpac/ismacryp.h @@ -0,0 +1,116 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Authoring Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_ISMACRYP_H_ +#define _GF_ISMACRYP_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/isomedia.h> + +enum +{ + /*no selective encryption*/ + GF_ISMACRYP_SELENC_NONE = 0, + /*only encrypts RAP samples*/ + GF_ISMACRYP_SELENC_RAP = 1, + /*only encrypts non-RAP samples*/ + GF_ISMACRYP_SELENC_NON_RAP = 2, + /*selective encryption of random samples*/ + GF_ISMACRYP_SELENC_RAND = 3, + /*selective encryption of a random sample in given range*/ + GF_ISMACRYP_SELENC_RAND_RANGE = 4, + /*selective encryption of first sample in given range*/ + GF_ISMACRYP_SELENC_RANGE = 5, + /*encryption of all samples but the preview range*/ + GF_ISMACRYP_SELENC_PREVIEW = 6, +}; + +typedef struct +{ + /*0: ISMACryp - 1: OMA DRM*/ + u32 enc_type; + u32 trackID; + unsigned char key[16]; + unsigned char salt[16]; + + /*the rest is only used for encryption*/ + char KMS_URI[5000]; + char Scheme_URI[5000]; + /*selecive encryption type*/ + u32 sel_enc_type; + u32 sel_enc_range; + /*IPMP signaling: 0: none, 1: IPMP, 2: IPMPX + when IPMP signaling is enabled, the OD stream will be updated with + IPMP Update commands*/ + u32 ipmp_type; + /*if not set and IPMP enabled, defaults to TrackID*/ + u32 ipmp_desc_id; + + /*OMA extensions*/ + /*0: none, 1: AES CBC, 2: AES CTR*/ + u8 encryption; + char TextualHeaders[5000]; + u32 TextualHeadersLen; + char TransactionID[17]; + +} GF_TrackCryptInfo; + +/*encrypts track - logs, progress: info callbacks, NULL for stdout*/ +GF_Err gf_ismacryp_encrypt_track(GF_ISOFile *mp4, GF_TrackCryptInfo *tci, void (*progress)(void *cbk, u32 done, u32 total), void *cbk); + +/*decrypts track - logs, progress: info callbacks, NULL for stdout*/ +GF_Err gf_ismacryp_decrypt_track(GF_ISOFile *mp4, GF_TrackCryptInfo *tci, void (*progress)(void *cbk, u32 done, u32 total), void *cbk); + +/*decrypt a file +@drm_file: location of DRM data (cf MP4Box doc). +@LogMsg: redirection for message or NULL for stdout +*/ +GF_Err gf_ismacryp_decrypt_file(GF_ISOFile *mp4file, const char *drm_file); + +/*Crypt a the file +@drm_file: location of DRM data. +@LogMsg: redirection for message or NULL for stdout +*/ +GF_Err gf_ismacryp_crypt_file(GF_ISOFile *mp4file, const char *drm_file); + +/*loads key and salt from a LOCAL gpac-DRM file (cf MP4Box doc)*/ +GF_Err gf_ismacryp_gpac_get_info(u32 stream_id, char *drm_file, char *key, char *salt); + +/*loads key and salt for MPEG4IP protected files*/ +Bool gf_ismacryp_mpeg4ip_get_info(char *kms_uri, char *key, char *salt); + +/*computes file hash. If file is ISO-based, computre hash according to OMA (P)DCF (without MutableDRMInformation box)*/ +GF_Err gf_media_get_file_hash(const char *file, u8 hash[20]); + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_ISMACRYP_H_*/ + diff --git a/include/gpac/iso639.h b/include/gpac/iso639.h new file mode 100644 index 0000000..5ea68b2 --- /dev/null +++ b/include/gpac/iso639.h @@ -0,0 +1,523 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_ISO_639_H +#define _GF_ISO_639_H + + +/*ISO 639 code names + - first string is readable english name of the language + - second string is 3-char code of language as per ISO/IEC 639-2 + - third string is 2-char code of language as per ISO/IEC 639-1, and may be empty +*/ +static const char *GF_ISO639_Lang[] = +{ + "Abkhazian","abk", "ab", + "Achinese","ace", "", + "Acoli","ach", "", + "Adangme","ada", "", + "Adygei","ady", "", + "Adyghe","ady", "", + "Afar","aar", "aa", + "Afrihili","afh", "", + "Afrikaans","afr", "af", + "Afro-Asiatic (Other)","afa", "", + "Akan","aka", "", + "Akkadian","akk", "", + "Albanian","alb/sqi", "sq", + "Aleut","ale", "", + "Algonquian languages","alg", "", + "Altaic (Other)","tut", "", + "Amharic","amh", "am", + "Apache languages","apa", "", + "Arabic","ara", "ar", + "Aragonese","arg", "", + "Aramaic","arc", "", + "Arapaho","arp", "", + "Araucanian","arn", "", + "Arawak","arw", "", + "Armenian","arm/hye", "hy", + "Artificial (Other)","art", "", + "Assamese","ast", "as", + "Athapascan languages","ath", "", + "Australian languages","aus", "", + "Austronesian (Other)","map", "", + "Avaric","ava", "", + "Avestan","ave", "", + "Awadhi","awa", "", + "Aymara","aym", "ay", + "Azerbaijani","aze", "az", + "Bable","ast", "", + "Balinese","ban", "", + "Baltic (Other)","bat", "", + "Baluchi","bal", "", + "Bambara","bam", "", + "Bamileke languages","bai", "", + "Banda","bad", "", + "Bantu (Other)","bnt", "", + "Basa","bas", "", + "Bashkir","bak", "ba", + "Basque","baq/eus", "eu", + "Batak (Indonesia)","btk", "", + "Beja","bej", "", + "Belarusian","bel", "be", + "Bemba","bem", "", + "Bengali","ben", "bn", + "Berber (Other)","ber", "", + "Bhojpuri","bho", "", + "Bihari","bih", "bh", + "Bikol","bik", "", + "Bini","bin", "", + "Bislama","bis", "bi", + "Bokmål, Norwegian","nob", "", + "Bosnian","bos", "", + "Braj","bra", "", + "Breton","bre", "br", + "Buginese","bug", "", + "Bulgarian","bul", "bg", + "Buriat","bua", "", + "Burmese","bur/mya", "my", + "Caddo","cad", "", + "Carib","car", "", + "Castilian","spa", "", + "Catalan","cat", "ca", + "Caucasian (Other)","cau", "", + "Cebuano","ceb", "", + "Celtic (Other)","cel", "", + "Central American Indian (Other)","cai", "", + "Chagatai","chg", "", + "Chamic languages","cmc", "", + "Chamorro","cha", "", + "Chechen","che", "", + "Cherokee","chr" , "", + "Chewa","nya", "", + "Cheyenne","chy", "", + "Chibcha","chb", "", + "Chichewa","nya", "", + "Chinese","chi/zho", "zh", + "Chinook jargon","chn", "", + "Chipewyan","chp", "", + "Choctaw","cho", "", + "Chuang","zha", "", + "Church Slavic (Slavonic)","chu", "", + "Chuukese","chk", "", + "Chuvash","chv", "", + "Coptic","cop", "", + "Cornish","cor", "", + "Corsican","cos", "co", + "Cree","cre" , "", + "Creek","mus" , "", + "Creoles and pidgins(Other)","crp", "", + "Creoles and pidgins, English-based (Other)","cpe", "", + "Creoles and pidgins, French-based (Other)","cpf", "", + "Creoles and pidgins, Portuguese-based (Other)","cpp", "", + "Crimean Tatar","crh", "", + "Crimean Turkish","crh", "", + "Croatian","scr/hrv", "hr", + "Cushitic (Other)","cus", "", + "Czech","cze/ces", "cs", + "Dakota","dak", "", + "Danish","dan", "da", + "Dargwa","dar", "", + "Dayak","day", "", + "Delaware","del", "", + "Dinka","din", "", + "Divehi","div" , "", + "Dogri","doi", "", + "Dogrib","dgr" , "", + "Dravidian (Other)","dra", "", + "Duala","dua", "", + "Dutch","dut/nld", "nl", + "Dutch, Middle (ca. 1050-1350)","dum", "", + "Dyula","dyu", "", + "Dzongkha","dzo", "dz", + "Efik","efi", "", + "Egyptian (Ancient)","egy", "", + "Ekajuk","eka", "", + "Elamite","elx", "", + "English","eng", "en", + "English, Middle (1100-1500)","enm", "", + "English, Old (ca.450-1100)","ang", "", + "Erzya","myv", "", + "Esperanto","epo", "eo", + "Estonian","est", "et", + "Ewe","ewe", "", + "Ewondo","ewo", "", + "Fang","fan", "", + "Fanti","fat" , "", + "Faroese","fao", "fo", + "Fijian","fij", "fj", + "Finnish","fin", "fi", + "Finno-Ugrian (Other)","fiu", "", + "Fon","fon", "", + "French","fre/fra", "fr", + "French, Middle (ca.1400-1600)","frm", "", + "French, Old (842-ca.1400)","fro", "", + "Frisian","fry", "fy", + "Friulian","fur" , "", + "Fulah","ful", "", + "Ga","gaa", "", + "Gaelic","gla", "", + "Gallegan","glg", "gl", + "Ganda","lug", "", + "Gayo","gay", "", + "Gbaya","gba" , "", + "Geez","gez", "", + "Georgian","geo/kat", "ka", + "German","ger/deu", "de", + "German, Low","nds" , "", + "German, Middle High (ca.1050-1500)","gmh", "", + "German, Old High (ca.750-1050)","goh", "", + "Germanic (Other)","gem", "", + "Gikuyu","kik", "", + "Gilbertese","gil", "", + "Gondi","gon", "", + "Gorontalo","gor", "", + "Gothic","got", "", + "Grebo","grb", "", + "Greek, Ancient (to 1453)","grc", "", + "Greek, Modern (1453-)","gre/ell", "el", + "Guarani","grn", "gn", + "Gujarati","guj", "gu", + "Gwich´in","gwi", "", + "Haida","hai", "", + "Haitian","hat", "", + "Haitian Creole","hat", "", + "Hausa","hau", "ha", + "Hawaiian","haw", "", + "Hebrew","heb", "he", + "Herero","her", "", + "Hiligaynon","hil", "", + "Himachali","him", "", + "Hindi","hin", "hi", + "Hiri Motu","hmo", "", + "Hittite","hit", "", + "Hmong","hmn", "", + "Hungarian","hun", "hu", + "Hupa","hup", "", + "Iban","iba", "", + "Icelandic","ice/isl", "is", + "Ido","ido", "", + "Igbo","ibo" , "", + "Ijo","ijo", "", + "Iloko","ilo", "", + "Inari Sami","smn", "", + "Indic (Other)","inc", "", + "Indo-European (Other)","ine", "", + "Indonesian","ind", "id", + "Ingush","inh", "", + "Interlingua (International Auxiliary Language Association)","ina", "ia", + "Interlingue","ile", "", + "Inuktitut","iku", "iu", + "Inupiaq","ipk", "ik", + "Iranian (Other)","ira", "", + "Irish","gle", "ga", + "Irish, Middle (900-1200)","mga", "", + "Irish, Old (to 900)","sga", "", + "Iroquoian languages","iro", "", + "Italian","ita", "it", + "Japanese","jpn", "ja", + "Javanese","jav", "jv", + "Judeo-Arabic","jrb", "", + "Judeo-Persian","jpr", "", + "Kabardian","kbd", "", + "Kabyle","kab", "", + "Kachin","kac", "", + "Kalaallisut","kal", "kl", + "Kalmyk","xal", "", + "Kamba","kam", "", + "Kannada","kan", "kn", + "Kanuri","kau", "", + "Karachay-Balkar","krc", "", + "Kara-Kalpak","kaa", "", + "Karen","kar", "", + "Kashmiri","kas", "ks", + "Kashubian","csb", "", + "Kawi","kaw", "", + "Kazakh","kaz", "kk", + "Khasi","kha", "", + "Khmer","khm", "km", + "Khoisan (Other)","khi", "", + "Khotanese","kho", "", + "Kikuyu","kik", "", + "Kimbundu","kmb", "", + "Kinyarwanda","kin", "rw", + "Kirghiz","kir", "ky", + "Komi","kom", "", + "Kongo","kon" , "", + "Konkani","kok" , "", + "Korean","kor", "ko", + "Kosraean","kos" , "", + "Kpelle","kpe" , "", + "Kru","kro" , "", + "Kuanyama","kua", "", + "Kumyk","kum" , "", + "Kurdish","kur", "ku", + "Kurukh","kru" , "", + "Kutenai","kut", "", + "Kwanyama","kua", "", + "Ladino","lad" , "", + "Lahnda","lah" , "", + "Lamba","lam" , "", + "Lao","lao", "lo", + "Latin","lat", "la", + "Latvian","lav", "lv", + "Letzeburgesch","ltz", "", + "Lezghian (lezLimburgan - limLimburger - limlimburgish)","lim", "", + "Lingala","lin", "ln", + "Lithuanian","lit", "lt", + "Low German","nds", "", + "Low Saxon","nds", "", + "Lozi","loz" , "", + "Luba-Katanga","lub" , "", + "Luba-Lulua","lua" , "", + "Luiseno","lui", "", + "Lule Sami","smj", "", + "Lunda","lun" , "", + "Luo (Kenya and Tanzania)","luo", "", + "Luxembourgish","ltz", "", + "Lushai","lus" , "", + "Macedonian","mac/mkd", "mk", + "Madurese","mad" , "", + "Magahi","mag" , "", + "Maithili","mai", "", + "Makasar","mak", "", + "Malagasy","mlg", "mg", + "Malay","may/msa", "ms", + "Malayalam","mal", "", + "Maltese","mlt", "ml", + "Manchu","mnc", "", + "Mandar","mdr", "", + "Mandingo","man", "", + "Manipuri","mni" , "", + "Manobo languages","mno" , "", + "Manx","glv", "", + "Maori","mao/mri", "mi", + "Marathi","mar", "mr", + "Mari","chm" , "", + "Marshallese","mah", "", + "Marwari","mwr" , "", + "Masai","mas" , "", + "Mayan languages","myn" , "", + "Mende","men" , "", + "Micmac","mic" , "", + "Minangkabau","min" , "", + "Miscellaneous languages","mis" , "", + "Mohawk","moh" , "", + "Moksha","mdf", "", + "Moldavian","mol", "mo", + "Mon-Khmer (Other)","mkh" , "", + "Mongo","lol" , "", + "Mongolian","mon", "mn", + "Mossi","mos" , "", + "Multiple languages","mul" , "", + "Munda languages","mun" , "", + "Nahuatl","nah" , "", + "Nauru","nau", "na", + "Navaho","nav", "", + "Navajo","nav", "", + "Ndebele, North","nde", "", + "Ndebele, South","nbl", "", + "Ndonga, ndoNeapolitan","nap", "", + "Nepali","nep", "ne", + "Newari","new" , "", + "Nias","nia" , "", + "Niger-Kordofanian (Other)","nic", "", + "Nilo-Saharan (Other)","ssa" , "", + "Niuean","niu" , "", + "Nogai","nog", "", + "Norse, Old","non", "", + "North American Indian (Other)","nai" , "", + "Northern Sami","sme", "", + "North Ndebele","nde", "", + "Norwegian","nor", "no", + "Norwegian Bokmål","nob", "", + "Norwegian Nynorsk","nno", "", + "Nubian languages","nub" , "", + "Nyamwezi","nym" , "", + "Nyanja","nya", "", + "Nyankole","nyn", "", + "Nynorsk, Norwegian","nno" , "", + "Nyoro","nyo" , "", + "Nzima","nzi" , "", + "Occitan (post 1500)","oci", "oc", + "Ojibwa","oji" , "", + "Old Bulgarian","chu", "", + "Old Church Slavonic","chu", "", + "Old Slavonic","chu", "", + "Oriya","ori", "or", + "Oromo","orm", "om", + "Osage","osa", "", + "Ossetian - Ossetic","oss", "", + "Otomian languages","oto", "", + "Pahlavi","pal" , "", + "Palauan","pau", "", + "Pali","pli", "", + "Pampanga","pam", "", + "Pangasinan","pag", "", + "Panjabi","pan", "pa", + "Papiamento","pap" , "", + "Papuan (Other)","paa" , "", + "Persian","per/fas", "fa", + "Persian, Old (ca.600-400)","peo" , "", + "Philippine (Other)","phi" , "", + "Phoenician","phn" , "", + "Pohnpeian","pon" , "", + "Polish","pol", "pl", + "Portuguese","por", "pt", + "Prakrit languages","pra", "", + "Provençal","oci", "", + "Provençal, Old (to 1500)","pro" , "", + "Pushto","pus", "ps", + "Quechua","que", "qu", + "Raeto-Romance","roh", "rm", + "Rajasthani","raj", "", + "Rapanui","rap", "", + "Rarotongan","rar", "", + "Romance (Other)","roa", "", + "Romanian","rum/ron", "ro", + "Romany","rom" , "", + "Rundi","run", "rn", + "Russian","rus", "ru", + "Salishan languages","sal" , "", + "Samaritan Aramaic","sam" , "", + "Sami languages (Other)","smi" , "", + "Samoan","smo", "sm", + "Sandawe","sad" , "", + "Sango","sag", "sg", + "Sanskrit","san", "sa", + "Santali","sat", "", + "Sardinian","srd", "", + "Sasak","sas" , "", + "Saxon, Low","nds", "", + "Scots","sco", "", + "Scottish Gaelic","gla" , "", + "Selkup","sel" , "", + "Semitic (Other)","sem" , "", + "Serbian","srp", "sr", + "Serbo-Croatian", "scr", "sh", + "Serer","srr" , "", + "Shan","shn" , "", + "Shona","sna", "sn", + "Sichuan Yi","iii", "", + "Sidamo","sid" , "", + "Sign languages","sgn" , "", + "Siksika","bla" , "", + "Sindhi","snd", "sd", + "Sinhalese","sin", "si", + "Sino-Tibetan (Other)","sit", "", + "Siouan languages","sio", "", + "Skolt Sami","sms" , "", + "Slave (Athapascan)","den" , "", + "Slavic (Other)","sla" , "", + "Slovak","slo", "sk", + "Slovenian","slv", "sl", + "Sogdian","sog" , "", + "Somali","som", "so", + "Songhai","son" , "", + "Soninke","snk" , "", + "Sorbian languages","wen" , "", + "Sotho, Northern","nso" , "", + "Sotho, Southern","sot", "st", + "South American Indian (Other)","sai" , "", + "Southern Sami","sma", "", + "South Ndebele","nbl", "", + "Spanish","spa", "es", + "Sukuma","suk", "", + "Sumerian","sux" , "", + "Sundanese","sun", "su", + "Susu","sus" , "", + "Swahili","swa", "sw", + "Swati","ssw", "ss", + "Swedish","swe", "sv", + "Syriac","syr", "", + "Tagalog","tgl", "tl", + "Tahitian","tah", "", + "Tai (Other)","tai" , "", + "Tajik","tgk", "tg", + "Tamashek","tmh" , "", + "Tamil","tam", "ta", + "Tatar","tat", "tt", + "Telugu","tel", "te", + "Tereno","ter" , "", + "Tetum","tet" , "", + "Thai","tha", "th", + "Tibetan","tib", "bo", + "Tigre","tig" , "", + "Tigrinya","tir", "ti", + "Timne","tem" , "", + "Tiv","tiv" , "", + "Tlingit","tli" , "", + "Tok Pisin","tpi" , "", + "Tokelau","tkl" , "", + "Tonga (Nyasa)","tog" , "to", + "Tonga (Tonga Islands)","ton", "", + "Tsimshian","tsi" , "", + "Tsonga","tso", "ts", + "Tswana","tsn", "tn", + "Tumbuka","tum" , "", + "Tupi languages","tup", "", + "Turkish","tur", "tr", + "Turkish, Ottoman (1500-1928)","ota" , "", + "Turkmen","tuk", "tk", + "Tuvalu","tvl", "", + "Tuvinian","tyv" , "", + "Twi","twi", "tw", + "Udmurt (udmUgaritic)","uga" , "", + "Uighur","uig", "ug", + "Ukrainian","ukr", "uk", + "Umbundu","umb" , "", + "Undetermined","und", "", + "Urdu","urd", "ur", + "Uzbek","uzb", "uz", + "Vai","vai", "", + "Venda","ven" , "", + "Vietnamese","vie", "vi", + "Volapük","vol", "vo", + "Votic","vot", "", + "Wakashan languages","wak" , "", + "Walamo","wal" , "", + "Walloon","wln", "", + "Waray","war", "", + "Washo","was" , "", + "Welsh","wel", "cy", + "Wolof","wol", "wo", + "Xhosa","xho", "xh", + "Yakut","sah" , "", + "Yao","yao" , "", + "Yapese","yap", "", + "Yiddish","yid", "yi", + "Yoruba","yor", "yo", + "Yupik languages","ypk" , "", + "Zande","znd" , "", + "Zapotec","zap" , "", + "Zenaga","zen" , "", + "Zhuang","zha", "za", + "Zulu","zul", "zu", + "Zuni","zun", "", + NULL +}; + + +#endif diff --git a/include/gpac/isomedia.h b/include/gpac/isomedia.h new file mode 100644 index 0000000..06c8610 --- /dev/null +++ b/include/gpac/isomedia.h @@ -0,0 +1,1852 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#ifndef _GF_ISOMEDIA_H_ +#define _GF_ISOMEDIA_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + +#include <gpac/mpeg4_odf.h> + + +/*the isomedia file*/ +typedef struct __tag_isom GF_ISOFile; + +/*media sample object*/ +typedef struct +{ + /*data size*/ + u32 dataLength; + /*data with padding if requested*/ + char *data; + /*decoding time*/ + u64 DTS; + /*relative offset for composition if needed*/ + u32 CTS_Offset; + /*Random Access Point flag: + 0: not random access + 1: regular RAP, + 2: sample is a redundant RAP. If set when adding the sample, this will create a sample dependency entry + */ + u8 IsRAP; +} GF_ISOSample; + + +/*creates a new empty sample*/ +GF_ISOSample *gf_isom_sample_new(); + +/*delete a sample. NOTE:the buffer content will be destroyed by default. +if you wish to keep the buffer, set dataLength to 0 in the sample +before deleting it +the pointer is set to NULL after deletion*/ +void gf_isom_sample_del(GF_ISOSample **samp); + + + +/******************************************************************** + FILE FORMAT CONSTANTS +********************************************************************/ + +/*Modes for file opening + NOTE 1: All the READ function in this API can be used in EDIT/WRITE mode. +However, some unexpected errors or values may happen in that case, depending +on how much modifications you made (timing, track with 0 samples, ...) + On the other hand, none of the EDIT/WRITE functions will work in +READ mode. + NOTE 2: The output structure of a edited file will sometimes be different +from the original file, but the media-data and meta-data will be identical. +The only change happens in the file media-data container(s) during edition + NOTE 3: when editing the file, you MUST set the final name of the modified file +to something different. This API doesn't allow file overwriting. +*/ +enum +{ + /*Opens file for dumping: same as read-only but keeps all movie fragments info untouched*/ + GF_ISOM_OPEN_READ_DUMP = 0, + /*Opens a file in READ ONLY mode*/ + GF_ISOM_OPEN_READ, + /*Opens a file in WRITE ONLY mode. Media Data is captured on the fly. In this mode, + the editing functions are disabled.*/ + GF_ISOM_OPEN_WRITE, + /*Opens an existing file in EDIT mode*/ + GF_ISOM_OPEN_EDIT, + /*Creates a new file in EDIT mode*/ + GF_ISOM_WRITE_EDIT +}; + +/*Movie Options for file writing*/ +enum +{ + /*FLAT: the MediaData (MPEG4 ESs) is stored at the begining of the file*/ + GF_ISOM_STORE_FLAT = 1, + /*STREAMABLE: the MetaData (File Info) is stored at the begining of the file + for fast access during download*/ + GF_ISOM_STORE_STREAMABLE, + /*INTERLEAVED: Same as STREAMABLE, plus the media data is mixed by chunk of fixed duration*/ + GF_ISOM_STORE_INTERLEAVED, + /*INTERLEAVED +DRIFT: Same as INTERLEAVED, and adds time drift control to avoid creating too long chunks*/ + GF_ISOM_STORE_DRIFT_INTERLEAVED, + /*tightly interleaves samples based on their DTS, therefore allowing better placement of samples in the file. + This is used for both http interleaving and Hinting optimizations*/ + GF_ISOM_STORE_TIGHT + +}; + +/*Some track may depend on other tracks for several reasons. They reference these tracks +through the following Reference Types*/ +enum +{ + /*ref type for the OD track dependencies*/ + GF_ISOM_REF_OD = GF_4CC( 'm', 'p', 'o', 'd' ), + /*ref type for stream dependencies*/ + GF_ISOM_REF_DECODE = GF_4CC( 'd', 'p', 'n', 'd' ), + /*ref type for OCR (Object Clock Reference) dependencies*/ + GF_ISOM_REF_OCR = GF_4CC( 's', 'y', 'n', 'c' ), + /*ref type for IPI (Intellectual Property Information) dependencies*/ + GF_ISOM_REF_IPI = GF_4CC( 'i', 'p', 'i', 'r' ), + /*ref type for timed Meta Data tracks*/ + GF_ISOM_REF_META = GF_4CC( 'c', 'd', 's', 'c' ), + /*ref type for Hint tracks*/ + GF_ISOM_REF_HINT = GF_4CC( 'h', 'i', 'n', 't' ), + /*ref type for QT Chapter tracks*/ + GF_ISOM_REF_CHAP = GF_4CC( 'c', 'h', 'a', 'p' ) +}; + +/*Track Edition flag*/ +enum { + /*empty segment in the track (no media for this segment)*/ + GF_ISOM_EDIT_EMPTY = 0x00, + /*dwelled segment in the track (one media sample for this segment)*/ + GF_ISOM_EDIT_DWELL = 0x01, + /*normal segment in the track*/ + GF_ISOM_EDIT_NORMAL = 0x02 +}; + +/*Generic Media Types (YOU HAVE TO USE ONE OF THESE TYPES FOR COMPLIANT ISO MEDIA FILES)*/ +enum +{ + /*base media types*/ + GF_ISOM_MEDIA_VISUAL = GF_4CC( 'v', 'i', 'd', 'e' ), + GF_ISOM_MEDIA_AUDIO = GF_4CC( 's', 'o', 'u', 'n' ), + GF_ISOM_MEDIA_HINT = GF_4CC( 'h', 'i', 'n', 't' ), + GF_ISOM_MEDIA_META = GF_4CC( 'm', 'e', 't', 'a' ), + GF_ISOM_MEDIA_TEXT = GF_4CC( 't', 'e', 'x', 't' ), + GF_ISOM_MEDIA_SUBPIC = GF_4CC( 's', 'u', 'b', 'p' ), + + /*MPEG-4 media types*/ + GF_ISOM_MEDIA_OD = GF_4CC( 'o', 'd', 's', 'm' ), + GF_ISOM_MEDIA_OCR = GF_4CC( 'c', 'r', 's', 'm' ), + GF_ISOM_MEDIA_SCENE = GF_4CC( 's', 'd', 's', 'm' ), + GF_ISOM_MEDIA_MPEG7 = GF_4CC( 'm', '7', 's', 'm' ), + GF_ISOM_MEDIA_OCI = GF_4CC( 'o', 'c', 's', 'm' ), + GF_ISOM_MEDIA_IPMP = GF_4CC( 'i', 'p', 's', 'm' ), + GF_ISOM_MEDIA_MPEGJ = GF_4CC( 'm', 'j', 's', 'm' ), + /*GPAC-defined, for any track using MPEG-4 systems signaling but with undefined streaml types*/ + GF_ISOM_MEDIA_ESM = GF_4CC( 'g', 'e', 's', 'm' ), + + /*DIMS media type (same as scene but with a different mediaInfo)*/ + GF_ISOM_MEDIA_DIMS = GF_4CC( 'd', 'i', 'm', 's' ), + + GF_ISOM_MEDIA_FLASH = GF_4CC( 'f', 'l', 's', 'h' ) +}; + +/* Encryption Scheme Type in the SchemeTypeInfoBox */ +enum +{ + GF_ISOM_ISMACRYP_SCHEME = GF_4CC( 'i', 'A', 'E', 'C' ) +}; + +/*specific media sub-types - you shall make sure the media sub type is what you expect*/ +enum +{ + /*reserved, internal use in the lib. Indicates the track complies to MPEG-4 system + specification, and the usual OD framework tools may be used*/ + GF_ISOM_SUBTYPE_MPEG4 = GF_4CC( 'M', 'P', 'E', 'G' ), + + /*reserved, internal use in the lib. Indicates the track is of GF_ISOM_SUBTYPE_MPEG4 + but it is encrypted.*/ + GF_ISOM_SUBTYPE_MPEG4_CRYP = GF_4CC( 'E', 'N', 'C', 'M' ), + + /*AVC/H264 media type - not listed as an MPEG-4 type, ALTHOUGH this library automatically remaps + GF_AVCConfig to MPEG-4 ESD*/ + GF_ISOM_SUBTYPE_AVC_H264 = GF_4CC( 'a', 'v', 'c', '1' ), + + /*3GPP(2) extension subtypes*/ + GF_ISOM_SUBTYPE_3GP_H263 = GF_4CC( 's', '2', '6', '3' ), + GF_ISOM_SUBTYPE_3GP_AMR = GF_4CC( 's', 'a', 'm', 'r' ), + GF_ISOM_SUBTYPE_3GP_AMR_WB = GF_4CC( 's', 'a', 'w', 'b' ), + GF_ISOM_SUBTYPE_3GP_EVRC = GF_4CC( 's', 'e', 'v', 'c' ), + GF_ISOM_SUBTYPE_3GP_QCELP = GF_4CC( 's', 'q', 'c', 'p' ), + GF_ISOM_SUBTYPE_3GP_SMV = GF_4CC( 's', 's', 'm', 'v' ), + + /*3GPP DIMS*/ + GF_ISOM_SUBTYPE_3GP_DIMS = GF_4CC( 'd', 'i', 'm', 's' ), + + GF_ISOM_SUBTYPE_AC3 = GF_4CC( 'a', 'c', '-', '3' ), +}; + + + + +/*direction for sample search (including SyncSamples search) +Function using search allways specify the desired time in composition (presentation) time + + (Sample N-1) DesiredTime (Sample N) + +FORWARD: will give the next sample given the desired time (eg, N) +BACKWARD: will give the previous sample given the desired time (eg, N-1) +SYNCFORWARD: will search from the desired point in time for a sync sample if any + If no sync info, behaves as FORWARD +SYNCBACKWARD: will search till the desired point in time for a sync sample if any + If no sync info, behaves as BACKWARD +SYNCSHADOW: use the sync shadow information to retrieve the sample. + If no SyncShadow info, behave as SYNCBACKWARD +*/ +enum +{ + GF_ISOM_SEARCH_FORWARD = 1, + GF_ISOM_SEARCH_BACKWARD = 2, + GF_ISOM_SEARCH_SYNC_FORWARD = 3, + GF_ISOM_SEARCH_SYNC_BACKWARD = 4, + GF_ISOM_SEARCH_SYNC_SHADOW = 5 +}; + +/*Predefined File Brand codes (MPEG-4 and JPEG2000)*/ +enum +{ + /*file complying to the generic ISO Media File (base specification ISO/IEC 14496-12) + this is the default brand when creating a new movie*/ + GF_ISOM_BRAND_ISOM = GF_4CC( 'i', 's', 'o', 'm' ), + /*file complying to the generic ISO Media File (base specification ISO/IEC 14496-12) + Meta extensions*/ + GF_ISOM_BRAND_ISO2 = GF_4CC( 'i', 's', 'o', '2' ), + /*file complying to ISO/IEC 14496-1 2001 edition. A .mp4 file without a brand + is equivalent to a file compatible with this brand*/ + GF_ISOM_BRAND_MP41 = GF_4CC( 'm', 'p', '4', '1' ), + /*file complying to ISO/IEC 14496-14 (MP4 spec)*/ + GF_ISOM_BRAND_MP42 = GF_4CC( 'm', 'p', '4', '2' ), + /*file complying to ISO/IEC 15444-3 (JPEG2000) without profile restriction*/ + GF_ISOM_BRAND_MJP2 = GF_4CC( 'm', 'j', 'p', '2' ), + /*file complying to ISO/IEC 15444-3 (JPEG2000) with simple profile restriction*/ + GF_ISOM_BRAND_MJ2S = GF_4CC( 'm', 'j', '2', 's' ), + /*old versions of 3GPP spec (without timed text)*/ + GF_ISOM_BRAND_3GP4 = GF_4CC('3', 'g', 'p', '4'), + GF_ISOM_BRAND_3GP5 = GF_4CC('3', 'g', 'p', '5'), + /*final version of 3GPP file spec*/ + GF_ISOM_BRAND_3GP6 = GF_4CC('3', 'g', 'p', '6'), + /*generci 3GPP file (several audio tracks, etc..)*/ + GF_ISOM_BRAND_3GG6 = GF_4CC('3', 'g', 'g', '6'), + /*3GPP2 file spec*/ + GF_ISOM_BRAND_3G2A = GF_4CC('3', 'g', '2', 'a'), + /*AVC file spec*/ + GF_ISOM_BRAND_AVC1 = GF_4CC('a', 'v', 'c', '1'), + /* file complying to ISO/IEC 21000-9:2005 (MPEG-21 spec)*/ + GF_ISOM_BRAND_MP21 = GF_4CC('m', 'p', '2', '1'), +}; + + +/*MPEG-4 ProfileAndLevel codes*/ +enum +{ + GF_ISOM_PL_AUDIO, + GF_ISOM_PL_VISUAL, + GF_ISOM_PL_GRAPHICS, + GF_ISOM_PL_SCENE, + GF_ISOM_PL_OD, + GF_ISOM_PL_MPEGJ, + /*not a profile, just set/unset inlineFlag*/ + GF_ISOM_PL_INLINE, +}; + + +/******************************************************************** + GENERAL API FUNCTIONS +********************************************************************/ + +/*get the last fatal error that occured in the file +ANY FUNCTION OF THIS API WON'T BE PROCESSED IF THE FILE HAS AN ERROR +Note: some function may return an error while the movie has no error +the last error is a FatalError, and is not always set if a bad +param is specified...*/ +GF_Err gf_isom_last_error(GF_ISOFile *the_file); + +/*returns 1 if target file is an IsoMedia file, 0 otherwise*/ +Bool gf_isom_probe_file(const char *fileName); + +/*Opens an isoMedia File. +tmp_dir: for the 2 edit modes only, specifies a location for temp file. If NULL, the librairy will use the default +OS temporary file management schemes.*/ +GF_ISOFile *gf_isom_open(const char *fileName, u32 OpenMode, const char *tmp_dir); + +/*close the file, write it if new/edited*/ +GF_Err gf_isom_close(GF_ISOFile *the_file); + +/*delete the movie without saving it.*/ +void gf_isom_delete(GF_ISOFile *the_file); + +/*Get the mode of an open file*/ +u8 gf_isom_get_mode(GF_ISOFile *the_file); + +Bool gf_isom_is_JPEG2000(GF_ISOFile *mov); + +/******************************************************************** + STREAMING API FUNCTIONS +********************************************************************/ +/*open a movie that can be uncomplete in READ_ONLY mode +to use for http streaming & co + +NOTE: you must buffer the data to a local file, this mode DOES NOT handle +http/ftp/... streaming + +BytesMissing is the predicted number of bytes missing for the file to be loaded +Note that if the file is not optimized for streaming, this number is not accurate +If the movie is successfully loaded (the_file non-NULL), BytesMissing is zero +*/ +GF_Err gf_isom_open_progressive(const char *fileName, GF_ISOFile **the_file, u64 *BytesMissing); + +/*If requesting a sample fails with error GF_ISOM_INCOMPLETE_FILE, use this function +to get the number of bytes missing to retrieve the sample*/ +u64 gf_isom_get_missing_bytes(GF_ISOFile *the_file, u32 trackNumber); + + +/*Fragmented movie extensions*/ + +/*return 0 if movie isn't fragmented, 1 otherwise*/ +u32 gf_isom_is_fragmented(GF_ISOFile *the_file); +/*return 0 if track isn't fragmented, 1 otherwise*/ +u32 gf_isom_is_track_fragmented(GF_ISOFile *the_file, u32 TrackID); + +/*a file being downloaded may be a fragmented file. In this case only partial info +is available once the file is successfully open (gf_isom_open_progressive), and since there is +no information wrt number fragments (which could actually be generated on the fly +at the sender side), you must call this function on regular bases in order to +load newly downloaded fragments. Note this may result in Track/Movie duration changes +and SampleCount change too ...*/ +GF_Err gf_isom_refresh_fragmented(GF_ISOFile *the_file, u64 *MissingBytes); + +/*check if file has movie info, eg has tracks & dynamic media. Some files may just use +the base IsoMedia structure without "moov" container*/ +Bool gf_isom_has_movie(GF_ISOFile *file); + +/******************************************************************** + READING API FUNCTIONS +********************************************************************/ + +/*return the number of tracks in the movie, or -1 if error*/ +u32 gf_isom_get_track_count(GF_ISOFile *the_file); + +/*return the timescale of the movie, 0 if error*/ +u32 gf_isom_get_timescale(GF_ISOFile *the_file); + +/*return the duration of the movie, 0 if error*/ +u64 gf_isom_get_duration(GF_ISOFile *the_file); + +/*return the creation info of the movie*/ +GF_Err gf_isom_get_creation_time(GF_ISOFile *the_file, u64 *creationTime, u64 *modificationTime); + +/*return the trackID of track number n, or 0 if error*/ +u32 gf_isom_get_track_id(GF_ISOFile *the_file, u32 trackNumber); + +/*return the track number of the track of specified ID, or 0 if error*/ +u32 gf_isom_get_track_by_id(GF_ISOFile *the_file, u32 trackID); + +/*gets the enable flag of a track 0: NO, 1: yes, 2: error*/ +u8 gf_isom_is_track_enabled(GF_ISOFile *the_file, u32 trackNumber); + +/* determines if the track is encrypted 0: NO, 1: yes, 2: error*/ +u8 gf_isom_is_track_encrypted(GF_ISOFile *the_file, u32 trackNumber); + +/*get the track duration return 0 if bad param*/ +u64 gf_isom_get_track_duration(GF_ISOFile *the_file, u32 trackNumber); + +/*return the media type FOUR CHAR code type of the media*/ +u32 gf_isom_get_media_type(GF_ISOFile *the_file, u32 trackNumber); + +/*return the media type FOUR CHAR code type of the media*/ +u32 gf_isom_get_media_subtype(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); + +/*return the media type FOUR CHAR code type of an MPEG4 media (eg, mp4a, mp4v, enca, encv, etc...) +returns 0 if not MPEG-4 subtype*/ +u32 gf_isom_get_mpeg4_subtype(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); + +/*Get the media (composition) time given the absolute time in the Movie +mediaTime is set to 0 if the media is not playing at that time (empty time segment)*/ +GF_Err gf_isom_get_media_time(GF_ISOFile *the_file, u32 trackNumber, u32 movieTime, u64 *MediaTime); + +/*Get the number of "streams" stored in the media - a media can have several stream descriptions...*/ +u32 gf_isom_get_sample_description_count(GF_ISOFile *the_file, u32 trackNumber); + +/*Get the stream description index (eg, the ESD) for a given time IN MEDIA TIMESCALE +return 0 if error or if empty*/ +u32 gf_isom_get_sample_description_index(GF_ISOFile *the_file, u32 trackNumber, u64 for_time); + +/*returns 1 if samples refering to the given stream description are present in the file +0 otherwise*/ +Bool gf_isom_is_self_contained(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex); + +/*get the media duration (without edit) return 0 if no samples (URL streams)*/ +u64 gf_isom_get_media_duration(GF_ISOFile *the_file, u32 trackNumber); + +/*Get the timeScale of the media. */ +u32 gf_isom_get_media_timescale(GF_ISOFile *the_file, u32 trackNumber); + +/*return the maximum chunk duration of the track in milliseconds*/ +u32 gf_isom_get_max_chunk_duration(GF_ISOFile *the_file, u32 trackNumber); + +/*Get the HandlerDescription name. The outName must be: + (outName != NULL && *outName == NULL) +the handler name is the string version of the MediaTypes*/ +GF_Err gf_isom_get_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char **outName); + +/*Check a DataReference of this track (index >= 1) +A Data Reference allows to construct a file without integrating the media data*/ +GF_Err gf_isom_check_data_reference(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex); + +/*get the location of the data. If URL && URN are NULL, the data is in this file +both strings are const: don't free them.*/ +GF_Err gf_isom_get_data_reference(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, const char **outURL, const char **outURN); + +/*Get the number of samples - return 0 if error*/ +u32 gf_isom_get_sample_count(GF_ISOFile *the_file, u32 trackNumber); + +/*Get constant sample size, or 0 if size not constant*/ +u32 gf_isom_get_constant_sample_size(GF_ISOFile *the_file, u32 trackNumber); +/*returns total amount of media bytes in track*/ +u64 gf_isom_get_media_data_size(GF_ISOFile *the_file, u32 trackNumber); + +/*It may be desired to fetch samples with a bigger allocated buffer than their real size, in case the decoder +reads more data than available. This sets the amount of extra bytes to allocate when reading samples from this track +NOTE: the dataLength of the sample does NOT include padding*/ +GF_Err gf_isom_set_sample_padding(GF_ISOFile *the_file, u32 trackNumber, u32 padding_bytes); + +/*return a sample given its number, and set the StreamDescIndex of this sample +this index allows to retrieve the stream description if needed (2 media in 1 track) +return NULL if error*/ +GF_ISOSample *gf_isom_get_sample(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 *StreamDescriptionIndex); + +/*same as gf_isom_get_sample but doesn't fetch media data +@StreamDescriptionIndex (optional): set to stream description index +@data_offset (optional): set to sample start offset in file. + + NOTE: when both StreamDescriptionIndex and data_offset are NULL, only DTS, CTS_Offset and RAP indications are +retrieved (faster) +*/ +GF_ISOSample *gf_isom_get_sample_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 *StreamDescriptionIndex, u64 *data_offset); + +/*retrieves given sample DTS*/ +u64 gf_isom_get_sample_dts(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); + +/*returns sample duration in media timeScale*/ +u32 gf_isom_get_sample_duration(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); + +/*gets a sample given a desired decoding time IN MEDIA TIME SCALE +and set the StreamDescIndex of this sample +this index allows to retrieve the stream description if needed (2 media in 1 track) +return GF_EOS if the desired time exceeds the media duration +WARNING: the sample may not be sync even though the sync was requested (depends on the media and the editList) +the SampleNum is optional. If non-NULL, will contain the sampleNumber*/ +GF_Err gf_isom_get_sample_for_media_time(GF_ISOFile *the_file, u32 trackNumber, u64 desiredTime, u32 *StreamDescriptionIndex, u8 SearchMode, GF_ISOSample **sample, u32 *SampleNum); + +/*retrieves given sample DTS*/ +u32 gf_isom_get_sample_from_dts(GF_ISOFile *the_file, u32 trackNumber, u64 dts); + +/*Track Edition functions*/ + +/*return a sample given a desired time in the movie. MovieTime is IN MEDIA TIME SCALE , handles edit list. +and set the StreamDescIndex of this sample +this index allows to retrieve the stream description if needed (2 media in 1 track) +sample must be set to NULL before calling. + +result Sample is NULL if an error occured +if no sample is playing, an empty sample is returned with no data and a DTS set to MovieTime when serching in sync modes +if no sample is playing, the closest sample in the edit time-line is returned when serching in regular modes + +WARNING: the sample may not be sync even though the sync was requested (depends on the media and the editList) + +Note: this function will handle re-timestamping the sample according to the mapping of the media time-line +on the track time-line. The sample TSs (DTS / CTS offset) are expressed in MEDIA TIME SCALE +(to match the media stream TS resolution as indicated in media header / SLConfig) + +sampleNumber is optional and gives the number of the sample in the media +*/ +GF_Err gf_isom_get_sample_for_movie_time(GF_ISOFile *the_file, u32 trackNumber, u64 movieTime, u32 *StreamDescriptionIndex, u8 SearchMode, GF_ISOSample **sample, u32 *sampleNumber); + +/*get the number of edited segment*/ +u32 gf_isom_get_edit_segment_count(GF_ISOFile *the_file, u32 trackNumber); + +/*Get the desired segment information*/ +GF_Err gf_isom_get_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u32 SegmentIndex, u64 *EditTime, u64 *SegmentDuration, u64 *MediaTime, u8 *EditMode); + +/*get the number of languages for the copyright*/ +u32 gf_isom_get_copyright_count(GF_ISOFile *the_file); +/*get the copyright and its language code given the index*/ +GF_Err gf_isom_get_copyright(GF_ISOFile *the_file, u32 Index, const char **threeCharCodes, const char **notice); +/*get the opaque watermark info if any - returns GF_NOT_SUPPORTED if not present*/ +GF_Err gf_isom_get_watermark(GF_ISOFile *the_file, bin128 UUID, u8** data, u32* length); + +/*get the number of chapter for movie or track if trackNumber !=0*/ +u32 gf_isom_get_chapter_count(GF_ISOFile *the_file, u32 trackNumber); +/*get the given movie or track (trackNumber!=0) chapter time and name - index is 1-based +@chapter_time: retrives start time in milliseconds - may be NULL. +@name: retrieves chapter name - may be NULL - SHALL NOT be destroyed by user +*/ +GF_Err gf_isom_get_chapter(GF_ISOFile *the_file, u32 trackNumber, u32 Index, u64 *chapter_time, const char **name); + +/* +return 0 if the media has no sync point info (eg, all samples are RAPs) +return 1 if the media has sync points (eg some samples are RAPs) +return 2 if the media has empty sync point info (eg no samples are RAPs). This will likely only happen + in scalable context +*/ +u8 gf_isom_has_sync_points(GF_ISOFile *the_file, u32 trackNumber); + +/*returns number of sync points*/ +u32 gf_isom_get_sync_point_count(GF_ISOFile *the_file, u32 trackNumber); + +/*returns 1 if one sample of the track is found to have a composition time offset (DTS<CTS)*/ +Bool gf_isom_has_time_offset(GF_ISOFile *the_file, u32 trackNumber); + +/*returns 1 if the track has sync shadow samples*/ +Bool gf_isom_has_sync_shadows(GF_ISOFile *the_file, u32 trackNumber); + +/*returns 1 if the track has sample dep indications*/ +Bool gf_isom_has_sample_dependency(GF_ISOFile *the_file, u32 trackNumber); + +/*rough estimation of file size, only works for completely self-contained files and without fragmentation +for the current time*/ +u64 gf_isom_estimate_size(GF_ISOFile *the_file); + +u32 gf_isom_get_next_alternate_group_id(GF_ISOFile *movie); + + +/* + MPEG-4 Systems extensions +*/ + +/*check if files has root OD/IOD or not*/ +Bool gf_isom_has_root_od(GF_ISOFile *the_file); + +/*return the root Object descriptor of the movie (can be NULL, OD or IOD, you have to check its tag) +YOU HAVE TO DELETE THE DESCRIPTOR +*/ +GF_Descriptor *gf_isom_get_root_od(GF_ISOFile *the_file); + +/*check the presence of a track in IOD. 0: NO, 1: YES, 2: ERROR*/ +u8 gf_isom_is_track_in_root_od(GF_ISOFile *the_file, u32 trackNumber); + +/*Get the GF_ESD given the StreamDescriptionIndex - YOU HAVE TO DELETE THE DESCRIPTOR*/ +GF_ESD *gf_isom_get_esd(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex); + +/*Get the decoderConfigDescriptor given the StreamDescriptionIndex - YOU HAVE TO DELETE THE DESCRIPTOR*/ +GF_DecoderConfig *gf_isom_get_decoder_config(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex); + +/*sets default TrackID (or ES_ID) for clock references. If trackNumber is 0, default sync track ID is reseted +and will be reassigned at next ESD fetch*/ +void gf_isom_set_default_sync_track(GF_ISOFile *file, u32 trackNumber); + +/*Return the number of track references of a track for a given ReferenceType - return -1 if error*/ +s32 gf_isom_get_reference_count(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType); + +/*Return the referenced track number for a track and a given ReferenceType and Index +return -1 if error, 0 if the reference is a NULL one, or the trackNumber +*/ +GF_Err gf_isom_get_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 referenceIndex, u32 *refTrack); + +u8 gf_isom_get_pl_indication(GF_ISOFile *the_file, u8 PL_Code); + +/*locates the first ObjectDescriptor using the given track by inspecting any OD tracks*/ +u32 gf_isom_find_od_for_track(GF_ISOFile *file, u32 track); + +/*returns file name*/ +const char *gf_isom_get_filename(GF_ISOFile *the_file); + +/* + Update of the Reading API for IsoMedia Version 2 +*/ + +/*retrieves the brand of the file. The brand is introduced in V2 to differenciate +MP4, MJPEG2000 and QT while indicating compatibilities +the brand is one of the above defined code, or any other registered brand + +minorVersion is an optional parameter (can be set to NULL) , + "informative integer for the minor version of the major brand" +AlternateBrandsCount is an optional parameter (can be set to NULL) , + giving the number of compatible brands. + + The function will set brand to 0 if no brand indication is found in the file +*/ +GF_Err gf_isom_get_brand_info(GF_ISOFile *the_file, u32 *brand, u32 *minorVersion, u32 *AlternateBrandsCount); + +/*gets an alternate brand indication. BrandIndex is 1-based +Note that the Major brand should always be indicated in the alternate brands*/ +GF_Err gf_isom_get_alternate_brand(GF_ISOFile *the_file, u32 BrandIndex, u32 *brand); + +/*get the number of padding bits at the end of a given sample if any*/ +GF_Err gf_isom_get_sample_padding_bits(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u8 *NbBits); +/*indicates whether the track samples use padding bits or not*/ +Bool gf_isom_has_padding_bits(GF_ISOFile *the_file, u32 trackNumber); + +/*returns width and height of the given visual sample desc - error if not a visual track*/ +GF_Err gf_isom_get_visual_info(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, u32 *Width, u32 *Height); + +/*returns samplerate, channels and bps of the given audio track - error if not a audio track*/ +GF_Err gf_isom_get_audio_info(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, u32 *SampleRate, u32 *Channels, u8 *bitsPerSample); + +/*returns track visual info - all coord values are expressed as 16.16 fixed point floats*/ +GF_Err gf_isom_get_track_layout_info(GF_ISOFile *the_file, u32 trackNumber, u32 *width, u32 *height, s32 *translation_x, s32 *translation_y, s16 *layer); + +/*returns width and height of the given visual sample desc - error if not a visual track*/ +GF_Err gf_isom_get_pixel_aspect_ratio(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, u32 *hSpacing, u32 *vSpacing); + +/* + User Data Manipulation (cf write API too) +*/ + +/* Gets the number of UserDataItems with the same ID / UUID in the desired track or +in the movie if trackNumber is set to 0*/ +u32 gf_isom_get_user_data_count(GF_ISOFile *the_file, u32 trackNumber, u32 UserDataType, bin128 UUID); +/* Gets the UserData for the specified item from the track or the movie if trackNumber is set to 0 +data is allocated by the function and is yours to free +you musty pass (userData != NULL && *userData=NULL)*/ +GF_Err gf_isom_get_user_data(GF_ISOFile *the_file, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex, char **userData, u32 *userDataSize); + + +/*gets 3char media language code - @three_char_code must be at least 4 char long*/ +GF_Err gf_isom_get_media_language(GF_ISOFile *the_file, u32 trackNumber, char *three_char_code); + +/*Unknown sample description*/ +typedef struct +{ + /*codec tag is the containing box's tag, 0 if UUID is used*/ + u32 codec_tag; + /*entry UUID if no tag is used*/ + bin128 UUID; + + u16 version; + u16 revision; + u32 vendor_code; + + /*video codecs only*/ + u32 temporal_quality; + u32 spacial_quality; + u16 width, height; + u32 h_res, v_res; + u16 depth; + u16 color_table_index; + char compressor_name[33]; + + /*audio codecs only*/ + u32 samplerate; + u16 nb_channels; + u16 bits_per_sample; + + /*if present*/ + char *extension_buf; + u32 extension_buf_size; +} GF_GenericSampleDescription; + +/*returns wrapper for unknown entries - you must delete it yourself*/ +GF_GenericSampleDescription *gf_isom_get_generic_sample_description(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex); + +/*retrieves default values for a track fragment. Each variable is optional and +if set will contain the default value for this track samples*/ +GF_Err gf_isom_get_fragment_defaults(GF_ISOFile *the_file, u32 trackNumber, + u32 *defaultDuration, u32 *defaultSize, u32 *defaultDescriptionIndex, + u32 *defaultRandomAccess, u8 *defaultPadding, u16 *defaultDegradationPriority); + + +/*non standard extensions used for video packets in order to keep AU structure in the file format +(no normative tables for that). Info is NOT written to disk. +*/ +/*get number of fragments for a sample */ +u32 gf_isom_get_sample_fragment_count(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); +/*get sample fragment size*/ +u16 gf_isom_get_sample_fragment_size(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 FragmentIndex); + +/*returns 1 if file is single AV (max one audio, one video, one text and basic od/bifs)*/ +Bool gf_isom_is_single_av(GF_ISOFile *file); + +/*guess which std this file refers to. return value: + GF_ISOM_BRAND_ISOM: unrecognized std + GF_ISOM_BRAND_3GP5: 3GP file (max 1 audio, 1 video) without text track + GF_ISOM_BRAND_3GP6: 3GP file (max 1 audio, 1 video) with text track + GF_ISOM_BRAND_3GG6: 3GP file multitrack file + GF_ISOM_BRAND_3G2A: 3GP2 file + GF_ISOM_BRAND_AVC1: AVC file + FCC("ISMA"): ISMA file (may overlap with 3GP) + GF_ISOM_BRAND_MP42: any generic MP4 file (eg with BIFS/OD/MPEG-4 systems stuff) + + for files without movie, returns the file meta handler type +*/ +u32 gf_isom_guess_specification(GF_ISOFile *file); + + +#ifndef GPAC_READ_ONLY + + +/******************************************************************** + EDITING/WRITING API FUNCTIONS +********************************************************************/ + +/*set the timescale of the movie*/ +GF_Err gf_isom_set_timescale(GF_ISOFile *the_file, u32 timeScale); + +/*creates a new Track. If trackID = 0, the trackID is chosen by the API +returns the track number or 0 if error*/ +u32 gf_isom_new_track(GF_ISOFile *the_file, u32 trackID, u32 MediaType, u32 TimeScale); + +/*removes the desired track - internal cross dependancies will be updated. +WARNING: any OD streams with references to this track through ODUpdate, ESDUpdate, ESDRemove commands +will be rewritten*/ +GF_Err gf_isom_remove_track(GF_ISOFile *the_file, u32 trackNumber); + +/*sets the enable flag of a track*/ +GF_Err gf_isom_set_track_enabled(GF_ISOFile *the_file, u32 trackNumber, u8 enableTrack); + +/*changes the trackID - all track references present in the file are updated +returns error if trackID is already in used in the file*/ +GF_Err gf_isom_set_track_id(GF_ISOFile *the_file, u32 trackNumber, u32 trackID); + +/*Add samples to a track. Use streamDescriptionIndex to specify the desired stream (if several)*/ +GF_Err gf_isom_add_sample(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample); + +/*Add sync shadow sample to a track. +- There must be a regular sample with the same DTS. +- Sync Shadow samples MUST be RAP +- Currently, adding sync shadow must be done in order (no sample insertion) +*/ +GF_Err gf_isom_add_sample_shadow(GF_ISOFile *the_file, u32 trackNumber, GF_ISOSample *sample); + +/*add data to current sample in the track. Use this function for media with +fragmented options such as MPEG-4 video packets. This will update the data size. +CANNOT be used with OD media type*/ +GF_Err gf_isom_append_sample_data(GF_ISOFile *the_file, u32 trackNumber, char *data, u32 data_size); + +/*Add sample references to a track. The dataOffset is the offset of the data in the referenced file +you MUST have created a StreamDescription with URL or URN specifying your referenced file +Use streamDescriptionIndex to specify the desired stream (if several)*/ +GF_Err gf_isom_add_sample_reference(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample, u64 dataOffset); + +/*set the duration of the last media sample. If not set, the duration of the last sample is the +duration of the previous one if any, or media TimeScale (default value).*/ +GF_Err gf_isom_set_last_sample_duration(GF_ISOFile *the_file, u32 trackNumber, u32 duration); + +/*sets a track reference*/ +GF_Err gf_isom_set_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 ReferencedTrackID); + +/*removes a track reference*/ +GF_Err gf_isom_remove_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 ReferenceIndex); + +/*sets track handler name. name is either NULL (reset), a UTF-8 formatted string or a UTF8 file +resource in the form "file://path/to/file_utf8" */ +GF_Err gf_isom_set_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char *nameUTF8); + +/*Update the sample size table - this is needed when using @gf_isom_append_sample_data in case the resulting samples +are of same sizes (typically in 3GP speech tracks)*/ +GF_Err gf_isom_refresh_size_info(GF_ISOFile *file, u32 trackNumber); + +/*Update Sample functions*/ + +/*update a given sample of the media. +@data_only: if set, only the sample data is updated, not other info*/ +GF_Err gf_isom_update_sample(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, Bool data_only); + +/*update a sample reference in the media. Note that the sample MUST exists, +that sample->data MUST be NULL and sample->dataLength must be NON NULL;*/ +GF_Err gf_isom_update_sample_reference(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset); + +/*Remove a given sample*/ +GF_Err gf_isom_remove_sample(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); + +/*changes media time scale*/ +GF_Err gf_isom_set_media_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 new_timescale); + +/*set the save file name of the (edited) movie. +If the movie is edited, the default fileName is avp_#openName) +NOTE: you cannot save an edited file under the same name (overwrite not allowed) +If the movie is created (WRITE mode), the default filename is #openName*/ +GF_Err gf_isom_set_final_name(GF_ISOFile *the_file, char *filename); + + +/*set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED)*/ +GF_Err gf_isom_set_storage_mode(GF_ISOFile *the_file, u8 storageMode); +u8 gf_isom_get_storage_mode(GF_ISOFile *the_file); + +/*set the interleaving time of media data (INTERLEAVED mode only) +InterleaveTime is in MovieTimeScale*/ +GF_Err gf_isom_set_interleave_time(GF_ISOFile *the_file, u32 InterleaveTime); +u32 gf_isom_get_interleave_time(GF_ISOFile *the_file); + +/*set the copyright in one language.*/ +GF_Err gf_isom_set_copyright(GF_ISOFile *the_file, const char *threeCharCode, char *notice); + +/*deletes copyright (1-based indexes)*/ +GF_Err gf_isom_remove_copyright(GF_ISOFile *the_file, u32 index); + + +GF_Err gf_isom_set_alternate_group_id(GF_ISOFile *movie, u32 trackNumber, u32 groupId); + +/*add chapter info: +if trackNumber is 0, the chapter info is added to the movie, otherwise to the track +@timestamp: chapter start time in milliseconds. Chapters are added in order to the file. If a chapter with same timestamp + is found, its name is updated but no entry is created. +@name: chapter name. If NULL, defaults to 'Chapter N' +*/ +GF_Err gf_isom_add_chapter(GF_ISOFile *the_file, u32 trackNumber, u64 timestamp, char *name); + +/*deletes copyright (1-based index, index 0 for all)*/ +GF_Err gf_isom_remove_chapter(GF_ISOFile *the_file, u32 trackNumber, u32 index); + +/*set watermark info for movie*/ +GF_Err gf_isom_set_watermark(GF_ISOFile *the_file, bin128 UUID, u8* data, u32 length); + +/*Track Edition functions - used to change the normal playback of the media if desired +NOTE: IT IS THE USER RESPONSABILITY TO CREATE A CONSISTENT TIMELINE FOR THE TRACK +This API provides the basic hooks and some basic consistency checking +but can not check the desired functionality of the track edits +*/ + +/*update or insert a new edit segment in the track time line. Edits are used to modify +the media normal timing. EditTime and EditDuration are expressed in Movie TimeScale +If a segment with EditTime already exists, IT IS ERASED +if there is a segment before this new one, its duration is adjust to match EditTime of +the new segment +WARNING: The first segment always have an EditTime of 0. You should insert an empty or dwelled segment first.*/ +GF_Err gf_isom_set_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u8 EditMode); + +/*same as above except only modifies duartion type and mediaType*/ +GF_Err gf_isom_modify_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u32 seg_index, u64 EditDuration, u64 MediaTime, u8 EditMode); +/*same as above except only appends new segment*/ +GF_Err gf_isom_append_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u64 EditDuration, u64 MediaTime, u8 EditMode); + +/*remove the edit segments for the whole track*/ +GF_Err gf_isom_remove_edit_segments(GF_ISOFile *the_file, u32 trackNumber); + +/*remove the given edit segment (1-based index). If this is not the last segment, the next segment duration +is updated to maintain a continous timeline*/ +GF_Err gf_isom_remove_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u32 seg_index); + +/* + User Data Manipulation + + You can add specific typed data to either a track or the movie: the UserData + The type must be formated as a FourCC if you have a registered 4CC type + but the usual is to set a UUID (128 bit ID for box type) which never conflict + with existing structures in the format + To manipulate a UUID user data set the UserDataType to 0 and specify a valid UUID. +Otherwise the UUID parameter is ignored + Several items with the same ID or UUID can be added (this allows you to store any + kind/number of private information under a unique ID / UUID) +*/ +/*Add a user data item in the desired track or in the movie if TrackNumber is 0*/ +GF_Err gf_isom_add_user_data(GF_ISOFile *the_file, u32 trackNumber, u32 UserDataType, bin128 UUID, char *data, u32 DataLength); + +/*remove all user data items from the desired track or from the movie if TrackNumber is 0*/ +GF_Err gf_isom_remove_user_data(GF_ISOFile *the_file, u32 trackNumber, u32 UserDataType, bin128 UUID); + +/*remove a user data item from the desired track or from the movie if TrackNumber is 0 +use the UDAT read functions to get the item index*/ +GF_Err gf_isom_remove_user_data_item(GF_ISOFile *the_file, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex); + +/*remove track, moov (trackNumber=0) or file-level (trackNumber=0xFFFFFFFF) UUID box of matching type*/ +GF_Err gf_isom_remove_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID); +/*adds track, moov (trackNumber=0) or file-level (trackNumber=0xFFFFFFFF) UUID box of given type*/ +GF_Err gf_isom_add_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID, char *data, u32 data_size); + +/* + Update of the Writing API for IsoMedia Version 2 +*/ + +/*use a compact track version for sample size. This is not usually recommended +except for speech codecs where the track has a lot of small samples +compaction is done automatically while writing based on the track's sample sizes*/ +GF_Err gf_isom_use_compact_size(GF_ISOFile *the_file, u32 trackNumber, u8 CompactionOn); + +/*sets the brand of the movie*/ +GF_Err gf_isom_set_brand_info(GF_ISOFile *the_file, u32 MajorBrand, u32 MinorVersion); + +/*adds or remove an alternate brand for the movie*/ +GF_Err gf_isom_modify_alternate_brand(GF_ISOFile *the_file, u32 Brand, u8 AddIt); + +/*removes all alternate brands except major brand*/ +GF_Err gf_isom_reset_alt_brands(GF_ISOFile *movie); + +/*set the number of padding bits at the end of a given sample if needed +if the function is never called the padding bit info is ignored +this MUST be called on an existin sample*/ +GF_Err gf_isom_set_sample_padding_bits(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u8 NbBits); + + +/*since v2 you must specify w/h of video tracks for authoring tools (no decode the video cfg / first sample)*/ +GF_Err gf_isom_set_visual_info(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, u32 Width, u32 Height); + +/*mainly used for 3GPP text since most ISO-based formats ignore these (except MJ2K) +all coord values are expressed as 16.16 fixed point floats*/ +GF_Err gf_isom_set_track_layout_info(GF_ISOFile *the_file, u32 trackNumber, u32 width, u32 height, s32 translation_x, s32 translation_y, s16 layer); + +GF_Err gf_isom_set_pixel_aspect_ratio(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, u32 hSpacing, u32 vSpacing); + +/*set SR & nbChans for audio description*/ +GF_Err gf_isom_set_audio_info(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, u32 sampleRate, u32 nbChannels, u8 bitsPerSample); + +/*non standard extensions: set/remove a fragment of a sample - this is used for video packets +in order to keep AU structure in the file format (no normative tables for that). Info is NOT written to disk*/ +GF_Err gf_isom_add_sample_fragment(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u16 FragmentSize); +GF_Err gf_isom_remove_sample_fragment(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); +/*remove all sample fragment info for this track*/ +GF_Err gf_isom_remove_sample_fragments(GF_ISOFile *the_file, u32 trackNumber); + +/*set CTS unpack mode (used for B-frames & like): in unpack mode, each sample uses one entry in CTTS tables +unpack=0: set unpack on - !!creates a CTTS table if none found!! +unpack=1: set unpack off and repacks all table info +*/ +GF_Err gf_isom_set_cts_packing(GF_ISOFile *the_file, u32 trackNumber, Bool unpack); +/*modify CTS offset of a given sample (used for B-frames) - MUST be called in unpack mode only*/ +GF_Err gf_isom_modify_cts_offset(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 offset); +/*remove CTS offset table (used for B-frames)*/ +GF_Err gf_isom_remove_cts_info(GF_ISOFile *the_file, u32 trackNumber); + +/*set 3char code media language*/ +GF_Err gf_isom_set_media_language(GF_ISOFile *the_file, u32 trackNumber, char *three_char_code); + +/*removes given stream description*/ +GF_Err gf_isom_remove_sample_description(GF_ISOFile *the_file, u32 trackNumber, u32 streamDescIndex); + +/* + some authoring extensions +*/ +/*sets name for authoring - if name is NULL reset authoring name*/ +GF_Err gf_isom_set_track_name(GF_ISOFile *the_file, u32 trackNumber, char *name); +/*gets authoring name*/ +const char *gf_isom_get_track_name(GF_ISOFile *the_file, u32 trackNumber); + +/* + MPEG-4 Extensions +*/ + +/*set a profile and level indication for the movie iod (created if needed) +if the flag is ProfileLevel is 0 this means the movie doesn't require +the specific codec (equivalent to 0xFF value in MPEG profiles)*/ +GF_Err gf_isom_set_pl_indication(GF_ISOFile *the_file, u8 PL_Code, u8 ProfileLevel); + +/*set the rootOD ID of the movie if you need it. By default, movies are created without root ODs*/ +GF_Err gf_isom_set_root_od_id(GF_ISOFile *the_file, u32 OD_ID); + +/*set the rootOD URL of the movie if you need it (only needed to create empty file pointing +to external ressource)*/ +GF_Err gf_isom_set_root_od_url(GF_ISOFile *the_file, char *url_string); + +/*remove the root OD*/ +GF_Err gf_isom_remove_root_od(GF_ISOFile *the_file); + +/*Add a system descriptor to the OD of the movie*/ +GF_Err gf_isom_add_desc_to_root_od(GF_ISOFile *the_file, GF_Descriptor *theDesc); + +/*add a track to the root OD*/ +GF_Err gf_isom_add_track_to_root_od(GF_ISOFile *the_file, u32 trackNumber); + +/*remove a track to the root OD*/ +GF_Err gf_isom_remove_track_from_root_od(GF_ISOFile *the_file, u32 trackNumber); + +/*Create a new StreamDescription (GF_ESD) in the file. The URL and URN are used to +describe external media, this will creat a data reference for the media*/ +GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *the_file, u32 trackNumber, GF_ESD *esd, char *URLname, char *URNname, u32 *outDescriptionIndex); + +/*use carefully. Very usefull when you made a lot of changes (IPMP, IPI, OCI, ...) +THIS WILL REPLACE THE WHOLE DESCRIPTOR ...*/ +GF_Err gf_isom_change_mpeg4_description(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_ESD *newESD); + +/*Add a system descriptor to the ESD of a stream - you have to delete the descriptor*/ +GF_Err gf_isom_add_desc_to_description(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_Descriptor *theDesc); + + +/*Default extensions*/ + +/*Create a new unknown StreamDescription in the file. The URL and URN are used to +describe external media, this will creat a data reference for the media +use this to store media not currently supported by the ISO media format +*/ +GF_Err gf_isom_new_generic_sample_description(GF_ISOFile *the_file, u32 trackNumber, char *URLname, char *URNname, GF_GenericSampleDescription *udesc, u32 *outDescriptionIndex); + +/*change the data field of an unknown sample description*/ +GF_Err gf_isom_change_generic_sample_description(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_GenericSampleDescription *udesc); + +/* +special shortcut for stream description cloning from a given input file (this avoids inspecting for media type) +@the_file, @trackNumber: destination file and track +@orig_file, @orig_track, @orig_desc_index: orginal file, track and sample description +@URLname, @URNname, @outDescriptionIndex: same usage as with gf_isom_new_mpeg4_description +*/ +GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, char *URLname, char *URNname, u32 *outDescriptionIndex); + +/*special shortcut: clones a track (everything except media data and sample info (DTS? CTS, RAPs, etc...) +also clones sampleDescriptions +@keep_data_ref: if set, external data references are kept, otherwise they are removed (track media data will be self-contained) +@dest_track: track number of cloned track*/ +GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *dest_file, Bool keep_data_ref, u32 *dest_track); +/*special shortcut: clones IOD PLs from orig to dest if any*/ +GF_Err gf_isom_clone_pl_indications(GF_ISOFile *orig, GF_ISOFile *dest); +/*clones root OD from input to output file, without copying root OD track references*/ +GF_Err gf_isom_clone_root_od(GF_ISOFile *input, GF_ISOFile *output); + +/*returns true if same set of sample description in both tracks - this does include self-contained checking +and reserved flags. The specific media cfg (DSI & co) is not analysed, only +a brutal memory comparaison is done*/ +Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, GF_ISOFile *f2, u32 tk2); + +GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on); + +/* + Movie Fragments Writing API + Movie Fragments is a feature of ISO media files for fragmentation + of a presentation meta-data and interleaving with its media data. + This enables faster http fast start for big movies, and also reduces the risk + of data loss in case of a recording crash, because meta data and media data + can be written to disk at regular times + This API provides simple function calls to setup such a movie and write it + The process implies: + 1- creating a movie in the usual way (track, stream descriptions, (IOD setup + copyright, ...) + 2- possibly add some samples in the regular fashion + 3- setup track fragments for all track that will be written in a fragmented way + (note that you can create/write a track that has no fragmentation at all) + 4- finalize the movie for fragmentation (this will flush all meta-data and + any media-data added to disk, ensuring all vital information for the presentation + is stored on file and not lost in case of crash/poweroff) + + then 5-6 as often as desired + 5- start a new movie fragment + 6- add samples to each setup track + + + IMPORTANT NOTES: + * Movie Fragments can only be used in GF_ISOM_OPEN_WRITE mode (capturing) + and no editing functionalities can be used + * the fragmented movie API uses TrackID and not TrackNumber +*/ + +/* +setup a track for fragmentation by specifying some default values for +storage efficiency +*TrackID: track identifier +*DefaultStreamDescriptionIndex: the default description used by samples in this track +*DefaultSampleDuration: default duration of samples in this track +*DefaultSampleSize: default size of samples in this track (0 if unknown) +*DefaultSampleIsSync: default key-flag (RAP) of samples in this track +*DefaultSamplePadding: default padding bits for samples in this track +*DefaultDegradationPriority: default degradation priority for samples in this track + +*/ +GF_Err gf_isom_setup_track_fragment(GF_ISOFile *the_file, u32 TrackID, + u32 DefaultStreamDescriptionIndex, + u32 DefaultSampleDuration, + u32 DefaultSampleSize, + u8 DefaultSampleIsSync, + u8 DefaultSamplePadding, + u16 DefaultDegradationPriority); + +/*flushes data to disk and prepare movie fragmentation*/ +GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *the_file); + +/*starts a new movie fragment*/ +GF_Err gf_isom_start_fragment(GF_ISOFile *the_file); + + +enum +{ + /*indicates that the track fragment has no samples but still has a duration + (silence-detection in audio codecs, ...). + param: indicates duration*/ + GF_ISOM_TRAF_EMPTY, + /*I-Frame detection: this can reduce file size by detecting I-frames and + optimizing sample flags (padding, priority, ..) + param: on/off (0/1)*/ + GF_ISOM_TRAF_RANDOM_ACCESS, + /*activate data cache on track fragment. This is usefull when writing interleaved + media from a live source (typically audio-video), and greatly reduces file size + param: Number of samples (> 1) to cache before disk flushing. You shouldn't try + to cache too many samples since this will load your memory. base that on FPS/SR*/ + GF_ISOM_TRAF_DATA_CACHE +}; + +/*set options. Options can be set at the begining of each new fragment only, and for the +lifetime of the fragment*/ +GF_Err gf_isom_set_fragment_option(GF_ISOFile *the_file, u32 TrackID, u32 Code, u32 param); + + +/*adds a sample to a fragmented track + +*TrackID: destination track +*sample: sample to add +*StreamDescriptionIndex: stream description for this sample. If 0, the default one +is used +*Duration: sample duration. +Note: because of the interleaved nature of the meta/media data, the sample duration +MUST be provided (in case of regular tracks, this was computed internally by the lib) +*PaddingBits: padding bits for the sample, or 0 +*DegradationPriority for the sample, or 0 + +*/ + +GF_Err gf_isom_fragment_add_sample(GF_ISOFile *the_file, u32 TrackID, GF_ISOSample *sample, + u32 StreamDescriptionIndex, + u32 Duration, + u8 PaddingBits, u16 DegradationPriority); + +/*appends data into last sample of track for video fragments/other media +CANNOT be used with OD tracks*/ +GF_Err gf_isom_fragment_append_data(GF_ISOFile *the_file, u32 TrackID, char *data, u32 data_size, u8 PaddingBits); + + + +/****************************************************************** + GENERIC Publishing API +******************************************************************/ + +/*Removes all sync shadow entries for a given track. The shadow samples are NOT removed; they must be removed +by the user app*/ +GF_Err gf_isom_remove_sync_shadows(GF_ISOFile *the_file, u32 trackNumber); + +/*Use this function to do the shadowing if you use shadowing. +the sample to be shadowed MUST be a non-sync sample (ignored if not) +the sample shadowing must be a Sync sample (error if not)*/ +GF_Err gf_isom_set_sync_shadow(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 syncSample); + +/*set the GroupID of a track (only used for optimized interleaving). By setting GroupIDs +you can specify the storage order for media data of a group of streams. This is usefull +for BIFS presentation so that static resources of the scene can be downloaded before BIFS*/ +GF_Err gf_isom_set_track_group(GF_ISOFile *the_file, u32 trackNumber, u32 GroupID); + +/*set the priority of a track within a Group (used for optimized interleaving and hinting). +This allows tracks to be stored before other within a same group, for instance the +hint track data can be stored just before the media data, reducing disk seeking +for a same time, within a group of tracks, the track with the lowest inversePriority will +be written first*/ +GF_Err gf_isom_set_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber, u32 InversePriority); + +/*set the max SamplesPerChunk (for file optimization, mainly in FLAT and STREAMABLE modes)*/ +GF_Err gf_isom_set_max_samples_per_chunk(GF_ISOFile *the_file, u32 trackNumber, u32 maxSamplesPerChunk); + +/*associate a given SL config with a given ESD while extracting the OD information +all the SL params must be fixed by the calling app! +The SLConfig is stored by the API for further use. A NULL pointer will result +in using the default SLConfig (predefined = 2) remapped to predefined = 0 +This is usefull while reading the IOD / OD stream of an MP4 file. Note however that +only full AUs are extracted, therefore the calling application must SL-packetize the streams*/ +GF_Err gf_isom_set_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig *slConfig); + +GF_Err gf_isom_get_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig **slConfig); + +u32 gf_isom_get_track_group(GF_ISOFile *the_file, u32 trackNumber); +u32 gf_isom_get_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber); + +/*stores movie config (storage mode, interleave time, track groupIDs, priorities and names) in UDTA(kept on disk) +if @remove_all is set, removes all stored info, otherwise recompute all stored info*/ +GF_Err gf_isom_store_movie_config(GF_ISOFile *the_file, Bool remove_all); +/*restores movie config (storage mode, interleave time, track groupIDs, priorities and names) if found*/ +GF_Err gf_isom_load_movie_config(GF_ISOFile *the_file); + +/*setup interleaving for storage (shortcut for storeage mode + interleave_time)*/ +GF_Err gf_isom_make_interleave(GF_ISOFile *mp4file, Double TimeInSec); + + +/****************************************************************** + GENERIC HINTING WRITING API +******************************************************************/ + +/*supported hint formats - ONLY RTP now*/ +enum +{ + GF_ISOM_HINT_RTP = GF_4CC('r', 't', 'p', ' '), +}; + + +/*Setup the resources based on the hint format +This function MUST be called after creating a new hint track and before +any other calls on this track*/ +GF_Err gf_isom_setup_hint_track(GF_ISOFile *the_file, u32 trackNumber, u32 HintType); + +/*Create a HintDescription for the HintTrack +the rely flag indicates whether a reliable transport protocol is desired/required +for data transport + 0: not desired (UDP/IP). NB: most RTP streaming servers only support UDP/IP for data + 1: preferable (TCP/IP if possible or UDP/IP) + 2: required (TCP/IP only) +The HintDescriptionIndex is set, to be used when creating a HINT sample +*/ +GF_Err gf_isom_new_hint_description(GF_ISOFile *the_file, u32 trackNumber, s32 HintTrackVersion, s32 LastCompatibleVersion, u8 Rely, u32 *HintDescriptionIndex); + +/*Starts a new sample for the hint track. A sample is just a collection of packets +the transmissionTime is indicated in the media timeScale of the hint track*/ +GF_Err gf_isom_begin_hint_sample(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 TransmissionTime); + +/*stores the hint sample in the file once all your packets for this sample are done +set IsRandomAccessPoint if you want to indicate that this is a random access point +in the stream*/ +GF_Err gf_isom_end_hint_sample(GF_ISOFile *the_file, u32 trackNumber, u8 IsRandomAccessPoint); + + +/****************************************************************** + PacketHandling functions + Data can be added at the end or at the beginning of the current packet + by setting AtBegin to 1 the data will be added at the begining + This allows constructing the packet payload before any meta-data +******************************************************************/ + +/*adds a blank chunk of data in the sample that is skipped while streaming*/ +GF_Err gf_isom_hint_blank_data(GF_ISOFile *the_file, u32 trackNumber, u8 AtBegin); + +/*adds a chunk of data in the packet that is directly copied while streaming +NOTE: dataLength MUST BE <= 14 bytes, and you should only use this function +to add small blocks of data (encrypted parts, specific headers, ...)*/ +GF_Err gf_isom_hint_direct_data(GF_ISOFile *the_file, u32 trackNumber, char *data, u32 dataLength, u8 AtBegin); + +/*adds a reference to some sample data in the packet +SourceTrackID: the ID of the track where the referenced sample is +SampleNumber: the sample number containing the data to be added +DataLength: the length of bytes to copy in the packet +offsetInSample: the offset in bytes in the sample at which to begin copying data + +extra_data: only used when the sample is actually the sample that will contain this packet +(usefull to store en encrypted version of a packet only available while streaming) + In this case, set SourceTrackID to the HintTrack ID and SampleNumber to 0 + In this case, the DataOffset MUST BE NULL and length will indicate the extra_data size + +Note that if you want to reference a previous HintSample in the hintTrack, you will +have to parse the sample yourself ... +*/ +GF_Err gf_isom_hint_sample_data(GF_ISOFile *the_file, u32 trackNumber, u32 SourceTrackID, u32 SampleNumber, u16 DataLength, u32 offsetInSample, char *extra_data, u8 AtBegin); + + +/*adds a reference to some stream description data in the packet (headers, ...) +SourceTrackID: the ID of the track where the referenced sample is +StreamDescriptionIndex: the index of the stream description in the desired track +DataLength: the length of bytes to copy in the packet +offsetInDescription: the offset in bytes in the description at which to begin copying data + +Since it is far from being obvious what this offset is, we recommend not using this +function. The ISO Media Format specification is currently being updated to solve +this issue*/ +GF_Err gf_isom_hint_sample_description_data(GF_ISOFile *the_file, u32 trackNumber, u32 SourceTrackID, u32 StreamDescriptionIndex, u16 DataLength, u32 offsetInDescription, u8 AtBegin); + + +/****************************************************************** + RTP SPECIFIC WRITING API +******************************************************************/ + +/*Creates a new RTP packet in the HintSample. If a previous packet was created, +it is stored in the hint sample and a new packet is created. +- relativeTime: RTP time offset of this packet in the HintSample if any - in hint track +time scale. Used for data smoothing by servers. +- PackingBit: the 'P' bit of the RTP packet header +- eXtensionBit: the'X' bit of the RTP packet header +- MarkerBit: the 'M' bit of the RTP packet header +- PayloadType: the payload type, on 7 bits, format 0x0XXXXXXX +- B_frame: indicates if this is a B-frame packet. Can be skipped by a server +- IsRepeatedPacket: indicates if this is a duplicate packet of a previous one. +Can be skipped by a server +- SequenceNumber: the RTP base sequence number of the packet. Because of support for repeated +packets, you have to set the sequence number yourself.*/ +GF_Err gf_isom_rtp_packet_begin(GF_ISOFile *the_file, u32 trackNumber, s32 relativeTime, u8 PackingBit, u8 eXtensionBit, u8 MarkerBit, u8 PayloadType, u8 B_frame, u8 IsRepeatedPacket, u16 SequenceNumber); + +/*set the flags of the RTP packet*/ +GF_Err gf_isom_rtp_packet_set_flags(GF_ISOFile *the_file, u32 trackNumber, u8 PackingBit, u8 eXtensionBit, u8 MarkerBit, u8 disposable_packet, u8 IsRepeatedPacket); + +/*set the time offset of this packet. This enables packets to be placed in the hint track +in decoding order, but have their presentation time-stamp in the transmitted +packet in a different order. Typically used for MPEG video with B-frames +*/ +GF_Err gf_isom_rtp_packet_set_offset(GF_ISOFile *the_file, u32 trackNumber, s32 timeOffset); + + +/*set some specific info in the HintDescription for RTP*/ + +/*sets the RTP TimeScale that the server use to send packets +some RTP payloads may need a specific timeScale that is not the timeScale in the file format +the default timeScale choosen by the API is the MediaTimeScale of the hint track*/ +GF_Err gf_isom_rtp_set_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 TimeScale); +/*sets the RTP TimeOffset that the server will add to the packets +if not set, the server adds a random offset*/ +GF_Err gf_isom_rtp_set_time_offset(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 TimeOffset); +/*sets the RTP SequenceNumber Offset that the server will add to the packets +if not set, the server adds a random offset*/ +GF_Err gf_isom_rtp_set_time_sequence_offset(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 SequenceNumberOffset); + + + +/****************************************************************** + SDP SPECIFIC WRITING API +******************************************************************/ +/*add an SDP line to the SDP container at the track level (media-specific SDP info) +NOTE: the \r\n end of line for SDP is automatically inserted*/ +GF_Err gf_isom_sdp_add_track_line(GF_ISOFile *the_file, u32 trackNumber, const char *text); +/*remove all SDP info at the track level*/ +GF_Err gf_isom_sdp_clean_track(GF_ISOFile *the_file, u32 trackNumber); + +/*add an SDP line to the SDP container at the movie level (presentation SDP info) +NOTE: the \r\n end of line for SDP is automatically inserted*/ +GF_Err gf_isom_sdp_add_line(GF_ISOFile *the_file, const char *text); +/*remove all SDP info at the movie level*/ +GF_Err gf_isom_sdp_clean(GF_ISOFile *the_file); + +#endif /*GPAC_READ_ONLY*/ + +/*Get SDP info at the movie level*/ +GF_Err gf_isom_sdp_get(GF_ISOFile *the_file, const char **sdp, u32 *length); +/*Get SDP info at the track level*/ +GF_Err gf_isom_sdp_track_get(GF_ISOFile *the_file, u32 trackNumber, const char **sdp, u32 *length); + +u32 gf_isom_get_payt_count(GF_ISOFile *the_file, u32 trackNumber); +const char *gf_isom_get_payt_info(GF_ISOFile *the_file, u32 trackNumber, u32 index, u32 *payID); + +/*dumps file structures into XML trace file */ +GF_Err gf_isom_dump(GF_ISOFile *file, FILE *trace); +/*dumps RTP hint samples structure into XML trace file + @trackNumber, @SampleNum: hint track and hint sample number + @trace: output +*/ +GF_Err gf_isom_dump_hint_sample(GF_ISOFile *the_file, u32 trackNumber, u32 SampleNum, FILE * trace); + + + +/*small hint reader - performs data caching*/ + +/*resets hint reading parameters, returns an error if the hint type is not supported for reading +packet sequence number is always reseted to 0 +@sample_start: indicates from where the packets should be read (regular 1-based sample number) +@ts_offset: constant offset for timestamps, must be expressed in media timescale (which is the hint timescale). + usually 0 (no offset) +@sn_offset: offset for packet sequence number (first packet will have a SN of 1 + sn_offset) + usually 0 +@ssrc: sync source identifier for RTP +*/ +GF_Err gf_isom_reset_hint_reader(GF_ISOFile *the_file, u32 trackNumber, u32 sample_start, u32 ts_offset, u32 sn_offset, u32 ssrc); + +/*reads next hint packet. ALl packets are read in transmission (decoding) order +returns an error if not supported, or GF_EOS when no more packets are available +currently only RTP reader is supported +@pck_data, @pck_size: output packet data (must be freed by caller) - contains all info to be sent + on the wire, eg for RTP contains the RTP header and the data +@disposable (optional): indicates that the packet can be droped when late (B-frames & co) +@repeated (optional): indicates this is a repeated packet (same one has already been sent) +@trans_ts (optional): indicates the transmission time of the packet, expressed in hint timescale, taking into account +the ts_offset specified in gf_isom_reset_hint_reader. Depending on packets this may not be the same +as the hint sample timestamp + ts_offset, some packets may need to be sent earlier (B-frames) +@sample_num (optional): indicates hint sample number the packet belongs to +*/ +GF_Err gf_isom_next_hint_packet(GF_ISOFile *the_file, u32 trackNumber, char **pck_data, u32 *pck_size, Bool *disposable, Bool *repeated, u32 *trans_ts, u32 *sample_num); + + +/* + 3GPP specific extensions + NOTE: MPEG-4 OD Framework cannot be used with 3GPP files. + Stream Descriptions are not GF_ESD, just generic config options as specified in this file +*/ + +/*Generic 3GP/3GP2 config record*/ +typedef struct +{ + /*GF_4CC record type, one fo the above GF_ISOM_SUBTYPE_3GP_ * subtypes*/ + u32 type; + /*4CC vendor name*/ + u32 vendor; + /*codec version*/ + u8 decoder_version; + /*number of sound frames per IsoMedia sample, >0 and <=15. The very last sample may contain less frames. */ + u8 frames_per_sample; + + /*H263 ONLY - Level and profile*/ + u8 H263_level, H263_profile; + + /*AMR(WB) ONLY - num of mode for the codec*/ + u16 AMR_mode_set; + /*AMR(WB) ONLY - changes in codec mode per sample*/ + u8 AMR_mode_change_period; +} GF_3GPConfig; + + +/*return the 3GP config for this tream description, NULL if not a 3GPP track*/ +GF_3GPConfig *gf_isom_3gp_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex); +#ifndef GPAC_READ_ONLY +/*create the track config*/ +GF_Err gf_isom_3gp_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_3GPConfig *config, char *URLname, char *URNname, u32 *outDescriptionIndex); +/*update the track config - subtypes shall NOT differ*/ +GF_Err gf_isom_3gp_config_update(GF_ISOFile *the_file, u32 trackNumber, GF_3GPConfig *config, u32 DescriptionIndex); +#endif /*GPAC_READ_ONLY*/ + +/*AVC/H264 extensions - GF_AVCConfig is defined in mpeg4_odf.h*/ + +/*gets uncompressed AVC config - user is responsible for deleting it*/ +GF_AVCConfig *gf_isom_avc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); + +#ifndef GPAC_READ_ONLY +/*creates new AVC config*/ +GF_Err gf_isom_avc_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AVCConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex); +/*updates AVC config*/ +GF_Err gf_isom_avc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_AVCConfig *cfg); +#endif + + +/* + 3GP timed text handling + + NOTE: currently only writing API is developped, the reading one is not used in MPEG-4 since + MPEG-4 maps 3GP timed text to MPEG-4 Streaming Text (part 17) +*/ + +/*set streamihng text reading mode: if do_convert is set, all text samples will be retrieved as TTUs +and ESD will be emulated for text tracks.*/ +GF_Err gf_isom_text_set_streaming_mode(GF_ISOFile *the_file, Bool do_convert); + +/*exports text track to given format +@dump_type: 0 for TTXT, 1 for srt, 2 for SVG +*/ +GF_Err gf_isom_text_dump(GF_ISOFile *the_file, u32 track, FILE *dump, u32 dump_type); + +/*returns encoded TX3G box (text sample description for 3GPP text streams) as needed by RTP or other standards: + @sidx: 1-based stream description index + @sidx_offset: + if 0, the sidx will NOT be written before the encoded TX3G + if not 0, the sidx will be written before the encoded TX3G, with the given offset. Offset sshould be at + least 128 for most commmon usage of TX3G (RTP, MPEG-4 timed text, etc) + +*/ +GF_Err gf_isom_text_get_encoded_tx3g(GF_ISOFile *file, u32 track, u32 sidx, u32 sidx_offset, char **tx3g, u32 *tx3g_size); + +/*checks if this text description is already inserted +@outDescIdx: set to 0 if not found, or descIndex +@same_style, @same_box: indicates if default styles and box are used +*/ +GF_Err gf_isom_text_has_similar_description(GF_ISOFile *the_file, u32 trackNumber, GF_TextSampleDescriptor *desc, u32 *outDescIdx, Bool *same_box, Bool *same_styles); + +/*text sample formatting*/ +typedef struct _3gpp_text_sample GF_TextSample; +/*creates text sample handle*/ +GF_TextSample *gf_isom_new_text_sample(); +/*destroy text sample handle*/ +void gf_isom_delete_text_sample(GF_TextSample *tx_samp); + +#ifndef GPAC_READ_ONLY + +/*Create a new TextSampleDescription in the file. +The URL and URN are used to describe external media, this will create a data reference for the media +GF_TextSampleDescriptor is defined in mpeg4_odf.h +*/ +GF_Err gf_isom_new_text_description(GF_ISOFile *the_file, u32 trackNumber, GF_TextSampleDescriptor *desc, char *URLname, char *URNname, u32 *outDescriptionIndex); +/*change the text sample description*/ +GF_Err gf_isom_update_text_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_TextSampleDescriptor *desc); + +/*reset text sample content*/ +GF_Err gf_isom_text_reset(GF_TextSample * tx_samp); +/*reset text sample styles but keep text*/ +GF_Err gf_isom_text_reset_styles(GF_TextSample * samp); + +/*sets UTF16 marker for text data. This MUST be called on an empty sample. If text data added later +on (cf below) is not formatted as UTF16 data(2 bytes char) the resulting text sample won't be compliant, +but this library WON'T WARN*/ +GF_Err gf_isom_text_set_utf16_marker(GF_TextSample * samp); +/*append text to sample - text_len is the number of bytes to be written from text_data. This allows +handling UTF8 and UTF16 strings in a transparent manner*/ +GF_Err gf_isom_text_add_text(GF_TextSample * tx_samp, char *text_data, u32 text_len); +/*append style modifyer to sample*/ +GF_Err gf_isom_text_add_style(GF_TextSample * tx_samp, GF_StyleRecord *rec); +/*appends highlight modifier for the sample + @start_char: first char highlighted, + @end_char: first char not highlighted*/ +GF_Err gf_isom_text_add_highlight(GF_TextSample * samp, u16 start_char, u16 end_char); +/*sets highlight color for the whole sample*/ +GF_Err gf_isom_text_set_highlight_color(GF_TextSample * samp, u8 r, u8 g, u8 b, u8 a); +GF_Err gf_isom_text_set_highlight_color_argb(GF_TextSample * samp, u32 argb); +/*appends a new karaoke sequence in the sample + @start_time: karaoke start time expressed in text stream timescale, but relative to the sample media time +*/ +GF_Err gf_isom_text_add_karaoke(GF_TextSample * samp, u32 start_time); +/*appends a new segment in the current karaoke sequence - you must build sequences in order to be compliant + @end_time: segment end time expressed in text stream timescale, but relative to the sample media time + @start_char: first char highlighted, + @end_char: first char not highlighted +*/ +GF_Err gf_isom_text_set_karaoke_segment(GF_TextSample * samp, u32 end_time, u16 start_char, u16 end_char); +/*sets scroll delay for the whole sample (scrolling is enabled through GF_TextSampleDescriptor.DisplayFlags) + @scroll_delay: delay for scrolling expressed in text stream timescale +*/ +GF_Err gf_isom_text_set_scroll_delay(GF_TextSample * samp, u32 scroll_delay); +/*appends hyperlinking for the sample + @URL: ASCII url + @altString: ASCII hint (tooltip, ...) for end user + @start_char: first char hyperlinked, + @end_char: first char not hyperlinked +*/ +GF_Err gf_isom_text_add_hyperlink(GF_TextSample * samp, char *URL, char *altString, u16 start_char, u16 end_char); +/*sets current text box (display pos&size within the text track window) for the sample*/ +GF_Err gf_isom_text_set_box(GF_TextSample * samp, s16 top, s16 left, s16 bottom, s16 right); +/*appends blinking for the sample + @start_char: first char blinking, + @end_char: first char not blinking +*/ +GF_Err gf_isom_text_add_blink(GF_TextSample * samp, u16 start_char, u16 end_char); +/*sets wrap flag for the sample - currently only 0 (no wrap) and 1 ("soft wrap") are allowed in 3GP*/ +GF_Err gf_isom_text_set_wrap(GF_TextSample * samp, u8 wrap_flags); + +/*formats sample as a regular GF_ISOSample. The resulting sample will always be marked as random access +text sample content is kept untouched*/ +GF_ISOSample *gf_isom_text_to_sample(GF_TextSample * tx_samp); + +#endif //GPAC_READ_ONLY + +/***************************************************** + ISMACryp Samples +*****************************************************/ +/*flags for GF_ISMASample*/ +enum +{ + /*signals the stream the sample belongs to uses selective encryption*/ + GF_ISOM_ISMA_USE_SEL_ENC = 1, + /*signals the sample is encrypted*/ + GF_ISOM_ISMA_IS_ENCRYPTED = 2, +}; + +typedef struct +{ + /*IV in ISMACryp is Byte Stream Offset*/ + u64 IV; + u8 IV_length;/*repeated from sampleDesc for convenience*/ + u8 *key_indicator; + u8 KI_length;/*repeated from sampleDesc for convenience*/ + u32 dataLength; + char *data; + u32 flags; +} GF_ISMASample; +/** + * creates a new empty ISMA sample + */ +GF_ISMASample *gf_isom_ismacryp_new_sample(); + +/*delete an ISMA sample. NOTE:the buffers content will be destroyed by default. +if you wish to keep the buffer, set dataLength to 0 in the sample before deleting it*/ +void gf_isom_ismacryp_delete_sample(GF_ISMASample *samp); + +/*decodes ISMACryp sample based on all info in ISMACryp sample description*/ +GF_ISMASample *gf_isom_ismacryp_sample_from_data(char *data, u32 dataLength, Bool use_selective_encryption, u8 KI_length, u8 IV_length); +/*rewrites samp content from s content*/ +GF_Err gf_isom_ismacryp_sample_to_sample(GF_ISMASample *s, GF_ISOSample *dest); + +/*decodes ISMACryp sample based on sample and its descrition index - returns NULL if not an ISMA sample +Note: input sample is NOT destroyed*/ +GF_ISMASample *gf_isom_get_ismacryp_sample(GF_ISOFile *the_file, u32 trackNumber, GF_ISOSample *samp, u32 sampleDescriptionIndex); + +/*returns whether the given media is a protected one or not - return scheme protection 4CC*/ +u32 gf_isom_is_media_encrypted(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex); + +/*returns whether the given media is a protected ISMACryp one or not*/ +Bool gf_isom_is_ismacryp_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex); + +/*returns whether the given media is a protected ISMACryp one or not*/ +Bool gf_isom_is_omadrm_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex); + +GF_Err gf_isom_get_omadrm_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, + u32 *outSchemeType, u32 *outSchemeVersion, + const char **outContentID, const char **outRightsIssuerURL, const char **outTextualHeaders, u32 *outTextualHeadersLen, u64 *outPlaintextLength, u32 *outEncryptionType, Bool *outSelectiveEncryption, u32 *outIVLength, u32 *outKeyIndicationLength); +/*retrieves ISMACryp info for the given track & SDI - all output parameters are optional - URIs SHALL NOT BE MODIFIED BY USER + @outOriginalFormat: retrieves orginal protected media format - usually GF_ISOM_SUBTYPE_MPEG4 + @outSchemeType: retrieves 4CC of protection scheme (GF_ISOM_ISMACRYP_SCHEME = iAEC in ISMACryp 1.0) + outSchemeVersion: retrieves version of protection scheme (1 in ISMACryp 1.0) + outSchemeURI: retrieves URI location of scheme + outKMS_URI: retrieves URI location of key management system - only valid with ISMACryp 1.0 + outSelectiveEncryption: specifies whether sample-based encryption is used in media - only valid with ISMACryp 1.0 + outIVLength: specifies length of Initial Vector - only valid with ISMACryp 1.0 + outKeyIndicationLength: specifies length of key indicator - only valid with ISMACryp 1.0 + + outSelectiveEncryption, outIVLength and outKeyIndicationLength are usually not needed to decode an + ISMA sample when using gf_isom_get_ismacryp_sample fct above +*/ +GF_Err gf_isom_get_ismacryp_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, u32 *outSchemeType, u32 *outSchemeVersion, const char **outSchemeURI, const char **outKMS_URI, Bool *outSelectiveEncryption, u32 *outIVLength, u32 *outKeyIndicationLength); + + +#ifndef GPAC_READ_ONLY +/*removes ISMACryp protection info (does not perform decryption :)*/ +GF_Err gf_isom_remove_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex); + +/*creates ISMACryp protection info (does not perform encryption :)*/ +GF_Err gf_isom_set_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, u32 scheme_type, + u32 scheme_version, char *scheme_uri, char *kms_URI, + Bool selective_encryption, u32 KI_length, u32 IV_length); + +/*change scheme URI and/or KMS URI for crypted files. Other params cannot be changed once the media is crypted + @scheme_uri: new scheme URI, or NULL to keep previous + @kms_uri: new KMS URI, or NULL to keep previous +*/ +GF_Err gf_isom_change_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, char *scheme_uri, char *kms_uri); + + +GF_Err gf_isom_set_oma_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, + char *contentID, char *kms_URI, u32 encryption_type, u64 plainTextLength, char *textual_headers, u32 textual_headers_len, + Bool selective_encryption, u32 KI_length, u32 IV_length); + +#endif + +/*xml dumpers*/ +GF_Err gf_isom_dump_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, FILE * trace); +GF_Err gf_isom_dump_ismacryp_sample(GF_ISOFile *the_file, u32 trackNumber, u32 SampleNum, FILE *trace); + + +/******************************************************************** + GENERAL META API FUNCTIONS + + Meta can be stored at several places in the file layout: + * root level (like moov, ftyp and co) + * moov level + * track level + Meta API uses the following parameters for all functions: + + gf_isom_*_meta_*(GF_ISOFile *file, Bool root_meta, u32 track_num, ....) with: + @root_meta: if set, accesses file root meta + @track_num: if root_meta not set, specifies whether the target meta is at the + moov level (track_num=0) or at the track level. + +********************************************************************/ + +/*gets meta type. Returned value: 0 if no meta found, or four char code of meta (eg, "mp21", "smil", ...)*/ +u32 gf_isom_get_meta_type(GF_ISOFile *file, Bool root_meta, u32 track_num); + +/*indicates if the meta has an XML container (note that XML data can also be included as items). +return value: 0 (no XML or error), 1 (XML text), 2 (BinaryXML, eg BiM) */ +u32 gf_isom_has_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num); + +/*extracts XML (if any) from given meta + @outName: output file path and location for writing + @is_binary: indicates if XML is Bim or regular XML +*/ +GF_Err gf_isom_extract_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, char *outName, Bool *is_binary); + +/*returns number of items described in this meta*/ +u32 gf_isom_get_meta_item_count(GF_ISOFile *file, Bool root_meta, u32 track_num); + +/*gets item info for the given item + @item_num: 1-based index of item to query + @itemID (optional): item ID in file + @is_self_reference: item is the file itself + @item_name (optional): item name + @item_mime_type (optional): item mime type + @item_encoding (optional): item content encoding type + @item_url, @item_urn (optional): url/urn of external resource containing this item data if any. + When item is fully contained in file, these are set to NULL + +*/ +GF_Err gf_isom_get_meta_item_info(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num, + u32 *itemID, u32 *protection_idx, Bool *is_self_reference, + const char **item_name, const char **item_mime_type, const char **item_encoding, + const char **item_url, const char **item_urn); + + +/*gets item idx from item ID*/ +u32 gf_isom_get_meta_item_by_id(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_ID); + +/*extracts item from given meta + @item_num: 1-based index of item to query +*/ +GF_Err gf_isom_extract_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num, const char *dump_file_name); + +/*retirves primary item ID, 0 if none found (primary can also be stored through meta XML)*/ +u32 gf_isom_get_meta_primary_item_id(GF_ISOFile *file, Bool root_meta, u32 track_num); + +#ifndef GPAC_READ_ONLY + +/*sets meta type (four char int, eg "mp21", ... + Creates a meta box if none found + if metaType is 0, REMOVES META +*/ +GF_Err gf_isom_set_meta_type(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 metaType); + +/*removes meta XML info if any*/ +GF_Err gf_isom_remove_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num); + +/*set meta XML data from file - erase any previously (Binary)XML info*/ +GF_Err gf_isom_set_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, char *XMLFileName, Bool IsBinaryXML); + +/*adds item to meta: + @self_reference: indicates this item is the file itself + @resource_path: file to add - can be NULL when URL/URN is used + @item_name: item name - if NULL, use file name. CANNOT BE NULL if resource_path is not set + @mime_type: item mime type - if NULL, use "application/octet-stream" + @content_encoding: content encoding type - if NULL, none specified + @URL, @URN: if set, resource will be remote (same as stream descriptions) +*/ +GF_Err gf_isom_add_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, const char *mime_type, const char *content_encoding, const char *URL, const char *URN); + +/*removes item from meta*/ +GF_Err gf_isom_remove_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num); + +/*sets the given item as the primary one. You SHALL NOT use this if the meta has a valid XML data*/ +GF_Err gf_isom_set_meta_primary_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num); + +#endif + + +/******************************************************************** + Timed Meta-Data extensions +********************************************************************/ + +GF_Err gf_isom_get_timed_meta_data_info(GF_ISOFile *file, u32 track, u32 sampleDescription, Bool *is_xml, const char **mime_or_namespace, const char **content_encoding, const char **schema_loc); + +#ifndef GPAC_READ_ONLY +/*create a new timed metat data sample description for this track*/ +GF_Err gf_isom_timed_meta_data_config_new(GF_ISOFile *movie, u32 trackNumber, Bool is_xml, char *mime_or_namespace, char *content_encoding, char *schema_loc, char *URLname, char *URNname, u32 *outDescriptionIndex); +#endif + + +/******************************************************************** + iTunes info tags +********************************************************************/ +enum +{ + /*probe is only used ti check if iTunes info are present*/ + GF_ISOM_ITUNE_PROBE = 0, + GF_ISOM_ITUNE_ALBUM = GF_4CC( 0xA9, 'a', 'l', 'b' ), + GF_ISOM_ITUNE_ARTIST = GF_4CC( 0xA9, 'A', 'R', 'T' ), + GF_ISOM_ITUNE_COMMENT = GF_4CC( 0xA9, 'c', 'm', 't' ), + GF_ISOM_ITUNE_COMPILATION = GF_4CC( 'c', 'p', 'i', 'l' ), + GF_ISOM_ITUNE_COMPOSER = GF_4CC( 0xA9, 'c', 'o', 'm' ), + GF_ISOM_ITUNE_COVER_ART = GF_4CC( 'c', 'o', 'v', 'r' ), + GF_ISOM_ITUNE_CREATED = GF_4CC( 0xA9, 'd', 'a', 'y' ), + GF_ISOM_ITUNE_DISK = GF_4CC( 'd', 'i', 's', 'k' ), + GF_ISOM_ITUNE_TOOL = GF_4CC( 0xA9, 't', 'o', 'o' ), + GF_ISOM_ITUNE_GENRE = GF_4CC( 'g', 'n', 'r', 'e' ), + GF_ISOM_ITUNE_GROUP = GF_4CC( 0xA9, 'g', 'r', 'p' ), + GF_ISOM_ITUNE_ITUNES_DATA = GF_4CC( '-', '-', '-', '-' ), + GF_ISOM_ITUNE_NAME = GF_4CC( 0xA9, 'n', 'a', 'm' ), + GF_ISOM_ITUNE_TEMPO = GF_4CC( 't', 'm', 'p', 'o' ), + GF_ISOM_ITUNE_TRACK = GF_4CC( 0xA9, 't', 'r', 'k' ), + GF_ISOM_ITUNE_TRACKNUMBER = GF_4CC( 't', 'r', 'k', 'n' ), + GF_ISOM_ITUNE_WRITER = GF_4CC( 0xA9, 'w', 'r', 't' ), + GF_ISOM_ITUNE_ENCODER = GF_4CC( 0xA9, 'e', 'n', 'c' ), + GF_ISOM_ITUNE_ALBUM_ARTIST = GF_4CC( 'a', 'A', 'R', 'T' ), + GF_ISOM_ITUNE_GAPELESS = GF_4CC( 'p', 'g', 'a', 'p' ), +}; +/*get the given tag info. +!! 'genre' may be coded by ID, the libisomedia doesn't translate the ID. In such a case, the result data is set to NULL +and the data_len to the genre ID +returns GF_URL_ERROR if no tag is present in the file +*/ +GF_Err gf_isom_apple_get_tag(GF_ISOFile *mov, u32 tag, const char **data, u32 *data_len); +#ifndef GPAC_READ_ONLY +/*set the given tag info. If data and data_len are 0, removes the given tag +For 'genre', data may be NULL in which case the genre ID taken from the data_len parameter +*/ +GF_Err gf_isom_apple_set_tag(GF_ISOFile *mov, u32 tag, const char *data, u32 data_len); + +/*sets compatibility tag on AVC tracks (needed by iPod to play files... hurray for standards)*/ +GF_Err gf_isom_set_ipod_compatible(GF_ISOFile *the_file, u32 trackNumber); +#endif + + +/*3GPP Alternate Group API - (c) 2007 ENST & ResonateMP4*/ + +/*gets the number of switching groups declared in this track if any: +trackNumber: track number +alternateGroupID: alternate group id of track if speciifed, 0 otherwise +nb_groups: number of switching groups defined for this track +*/ +GF_Err gf_isom_get_track_switch_group_count(GF_ISOFile *movie, u32 trackNumber, u32 *alternateGroupID, u32 *nb_groups); + +/*returns the list of criteria (expressed as 4CC IDs, cf 3GPP TS 26.244) +trackNumber: track number +group_index: 1-based index of the group to inspect +switchGroupID: ID of the switch group if any, 0 otherwise (alternate-only group) +criteriaListSize: number of criteria items in returned list +*/ +const u32 *gf_isom_get_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 group_index, u32 *switchGroupID, u32 *criteriaListSize); + +#ifndef GPAC_READ_ONLY +/*sets a new (switch) group for this track +trackNumber: track +trackRefGroup: number of a track belonging to the same alternate group. If 0, a new alternate group will be created for this track +is_switch_group: if set, indicates that a switch group identifier shall be assigned to the created group. Otherwise, the criteria list is associated with the entire alternate group +switchGroupID: SHALL NOT BE NULL + input: specifies the desired switchGroupID to use. If value is 0, next available switchGroupID in file is used. + output: indicates the switchGroupID used. +criteriaList, criteriaListCount: criteria list and size. Criterias are expressed as 4CC IDs, cf 3GPP TS 26.244 +*/ +GF_Err gf_isom_set_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 trackRefGroup, Bool is_switch_group, u32 *switchGroupID, u32 *criteriaList, u32 criteriaListCount); + +/*resets track switch group information for the track or for the entire alternate group this track belongs to if reset_all_group is set*/ +GF_Err gf_isom_reset_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, Bool reset_all_group); + +/*resets ALL track switch group information in the entire movie*/ +GF_Err gf_isom_reset_switch_parameters(GF_ISOFile *movie); + +#endif + + +typedef struct +{ + u8 profile; + u8 level; + u8 pathComponents; + Bool fullRequestHost; + Bool streamType; + u8 containsRedundant; + const char *textEncoding; + const char *contentEncoding; + const char *content_script_types; +} GF_DIMSDescription; + +GF_Err gf_isom_get_dims_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_DIMSDescription *desc); +#ifndef GPAC_READ_ONLY +GF_Err gf_isom_new_dims_description(GF_ISOFile *movie, u32 trackNumber, GF_DIMSDescription *desc, char *URLname, char *URNname, u32 *outDescriptionIndex); +GF_Err gf_isom_update_dims_description(GF_ISOFile *movie, u32 trackNumber, GF_DIMSDescription *desc, char *URLname, char *URNname, u32 DescriptionIndex); +#endif + + + + +/*AC3 config record*/ +typedef struct +{ + u8 fscod; + u8 bsid; + u8 bsmod; + u8 acmod; + u8 lfon; + u8 brcode; +} GF_AC3Config; + +#ifndef GPAC_READ_ONLY +GF_Err gf_isom_ac3_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AC3Config *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex); +#endif + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_ISOMEDIA_H_*/ + + diff --git a/include/gpac/laser.h b/include/gpac/laser.h new file mode 100644 index 0000000..800da28 --- /dev/null +++ b/include/gpac/laser.h @@ -0,0 +1,88 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_LASER_H_ +#define _GF_LASER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/nodes_svg.h> + +/*for LASeRConfig*/ +#include <gpac/mpeg4_odf.h> + +#ifndef GPAC_DISABLE_SVG + +typedef struct __tag_laser_codec GF_LASeRCodec; + + +/*LASeR decoder constructor*/ +GF_LASeRCodec *gf_laser_decoder_new(GF_SceneGraph *scenegraph); +void gf_laser_decoder_del(GF_LASeRCodec *codec); + +/*sets the scene time. Scene time is the real clock of the bifs stream in secs*/ +void gf_laser_decoder_set_clock(GF_LASeRCodec *codec, Double (*GetSceneTime)(void *st_cbk), void *st_cbk ); + +/*setup a stream*/ +GF_Err gf_laser_decoder_configure_stream(GF_LASeRCodec *codec, u16 ESID, char *DecoderSpecificInfo, u32 DecoderSpecificInfoLength); +/*removes a stream*/ +GF_Err gf_laser_decoder_remove_stream(GF_LASeRCodec *codec, u16 ESID); + +/*decode a laser AU and applies it to the graph (non-memory mode only)*/ +GF_Err gf_laser_decode_au(GF_LASeRCodec *codec, u16 ESID, char *data, u32 data_length); + +/*Memory laser decoding - fills the command list with the content of the AU - cf scenegraph_vrml.h for commands usage + @ESID: ID of input stream + @data, @data_length: BIFS AU + @com_list: target list for decoded commands +*/ +GF_Err gf_laser_decode_command_list(GF_LASeRCodec *codec, u16 ESID, char *data, u32 data_length, GF_List *com_list); + + +/*constructor - @graph: scene graph being encoded*/ +GF_LASeRCodec *gf_laser_encoder_new(GF_SceneGraph *graph); +/*destructor*/ +void gf_laser_encoder_del(GF_LASeRCodec *codec); +/*setup a destination stream*/ +GF_Err gf_laser_encoder_new_stream(GF_LASeRCodec *codec, u16 ESID, GF_LASERConfig *cfg); +/*encodes a list of commands for the given stream in the output buffer - data is dynamically allocated for output*/ +GF_Err gf_laser_encode_au(GF_LASeRCodec *codec, u16 ESID, GF_List *command_list, Bool reset_encoding_context, char **out_data, u32 *out_data_length); +/*returns encoded config desc*/ +GF_Err gf_laser_encoder_get_config(GF_LASeRCodec *codec, u16 ESID, char **out_data, u32 *out_data_length); + +/*Encodes current graph as a scene replace*/ +GF_Err gf_laser_encoder_get_rap(GF_LASeRCodec *codec, char **out_data, u32 *out_data_length); + + +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/include/gpac/list.h b/include/gpac/list.h new file mode 100644 index 0000000..eab8993 --- /dev/null +++ b/include/gpac/list.h @@ -0,0 +1,168 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_LIST_H_ +#define _GF_LIST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file <gpac/list.h> + * \brief list functions. + */ + +/*! + * \addtogroup list_grp list + * \ingroup utils_grp + * \brief List object + * + * This section documents the list object of the GPAC framework. + * @{ + */ + +#include <gpac/tools.h> + +typedef struct _tag_array GF_List; + +/*! + * \brief list constructor + * + * Constructs a new list object + * \return new list object + */ +GF_List *gf_list_new(); +/*! + * \brief list destructor + * + * Destructs a list object + * \param ptr list object to destruct + * \note It is the caller responsability to destroy the content of the list if needed + */ +void gf_list_del(GF_List *ptr); +/*! + * \brief get count + * + * Returns number of items in the list + * \param ptr target list object + * \return number of items in the list + */ +u32 gf_list_count(GF_List *ptr); +/*! + * \brief add item + * + * Adds an item at the end of the list + * \param ptr target list object + * \param item item to add + */ +GF_Err gf_list_add(GF_List *ptr, void* item); +/*! + * \brief inserts item + * + * Insert an item in the list + * \param ptr target list object + * \param item item to add + * \param position insertion position. It is expressed between 0 and gf_list_count-1, and any bigger value is equivalent to gf_list_add + */ +GF_Err gf_list_insert(GF_List *ptr, void *item, u32 position); +/*! + * \brief removes item + * + * Removes an item from the list given its position + * \param ptr target list object + * \param position position of the item to remove. It is expressed between 0 and gf_list_count-1. + * \note It is the caller responsability to destroy the content of the list if needed + */ +GF_Err gf_list_rem(GF_List *ptr, u32 position); +/*! + * \brief gets item + * + * Gets an item from the list given its position + * \param ptr target list object + * \param position position of the item to get. It is expressed between 0 and gf_list_count-1. + */ +void *gf_list_get(GF_List *ptr, u32 position); +/*! + * \brief finds item + * + * Finds an item in the list + * \param ptr target list object. + * \param item the item to find. + * \return 0-based item position in the list, or -1 if the item could not be found. + */ +s32 gf_list_find(GF_List *ptr, void *item); +/*! + * \brief deletes item + * + * Deletes an item from the list + * \param ptr target list object. + * \param item the item to find. + * \return 0-based item position in the list before removal, or -1 if the item could not be found. + */ +s32 gf_list_del_item(GF_List *ptr, void *item); +/*! + * \brief resets list + * + * Resets the content of the list + * \param ptr target list object. + * \note It is the caller responsability to destroy the content of the list if needed + */ +void gf_list_reset(GF_List *ptr); +/*! + * \brief gets last item + * + * Gets last item o fthe list + * \param ptr target list object + */ +void *gf_list_last(GF_List *ptr); +/*! + * \brief removes last item + * + * Removes the last item of the list + * \param ptr target list object + * \note It is the caller responsability to destroy the content of the list if needed + */ +GF_Err gf_list_rem_last(GF_List *ptr); + + +/*! + * \brief list enumerator + * + * Retrieves given list item and increment current position + * \param ptr target list object + * \param pos target item position. The position is automatically incremented regardless of the return value + * \note A typical enumeration will start with a value of 0 until NULL is returned. + */ +void *gf_list_enum(GF_List *ptr, u32 *pos); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_LIST_H_*/ + diff --git a/include/gpac/math.h b/include/gpac/math.h new file mode 100644 index 0000000..36dad51 --- /dev/null +++ b/include/gpac/math.h @@ -0,0 +1,1048 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_MATH_H_ +#define _GF_MATH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file <gpac/math.h> + * \brief math and trigo functions. + */ + +#include <gpac/setup.h> + +/*NOTE: there is a conflict on Win32 VC6 with C++ and gpac headers when including <math.h>*/ +#if !defined(__cplusplus) || defined(__SYMBIAN32__) +#include <math.h> +#endif + +/*! + \cond DUMMY_DOXY_SECTION +*/ + +#ifndef GPAC_FIXED_POINT +/*note: + to turn fp on, change to GPAC_FIXED_POINT + to turn fp off, change to GPAC_NO_FIXED_POINT + this is needed by configure+sed to modify this file directly +*/ +#define GPAC_NO_FIXED_POINT +#endif + +/*! + \endcond +*/ + + +/*! + *\addtogroup math_grp math + *\ingroup utils_grp + *\brief Mathematics and Trigonometric functions + * + *This section documents the math and trigo functions used in the GPAC framework. GPAC can be compiled with + *fixed-point support, representing float values on a 16.16 signed integer, which implies a developer + *must take care of float computations when using GPAC.\n + *A developper should not need to know in which mode the framework has been compiled as long as he uses + *the math functions of GPAC which work in both float and fixed-point mode.\n + *Using fixed-point version is decided at compilation time and cannot be changed. The feature is signaled + *through the following macros: + *- GPAC_FIXED_POINT: when defined, GPAC has been compiled in fixed-point mode + *- GPAC_NO_FIXED_POINT: when defined, GPAC has been compiled in regular (float) mode + * @{ + */ + + +/***************************************************************************************** + FIXED-POINT SUPPORT - HARDCODED FOR 16.16 representation + the software rasterizer also use a 16.16 representation even in non-fixed version +******************************************************************************************/ + +#ifdef GPAC_FIXED_POINT + +/*! + *Fixed 16.16 number + *\hideinitializer + \note This documentation has been generated for a fixed-point version of the GPAC framework. + */ +typedef s32 Fixed; +#define FIX_ONE 0x10000L +#define INT2FIX(v) ((Fixed)( ((s32) (v) ) << 16)) +#define FLT2FIX(v) ((Fixed) ((v) * FIX_ONE)) +#define FIX2INT(v) ((s32)(((v)+((FIX_ONE>>1)))>>16)) +#define FIX2FLT(v) ((Float)( ((Float)(v)) / ((Float) FIX_ONE))) +#define FIX_EPSILON 2 +#define FIX_MAX 0x7FFFFFFF +#define FIX_MIN -FIX_MAX +#define GF_PI2 102944 +#define GF_PI 205887 +#define GF_2PI 411774 + +/*!\return 1/a, expressed as fixed number*/ +Fixed gf_invfix(Fixed a); +/*!\return a*b, expressed as fixed number*/ +Fixed gf_mulfix(Fixed a, Fixed b); +/*!\return a*b/c, expressed as fixed number*/ +Fixed gf_muldiv(Fixed a, Fixed b, Fixed c); +/*!\return a/b, expressed as fixed number*/ +Fixed gf_divfix(Fixed a, Fixed b); +/*!\return sqrt(a), expressed as fixed number*/ +Fixed gf_sqrt(Fixed x); +/*!\return ceil(a), expressed as fixed number*/ +Fixed gf_ceil(Fixed a); +/*!\return floor(a), expressed as fixed number*/ +Fixed gf_floor(Fixed a); +/*!\return cos(a), expressed as fixed number*/ +Fixed gf_cos(Fixed angle); +/*!\return sin(a), expressed as fixed number*/ +Fixed gf_sin(Fixed angle); +/*!\return tan(a), expressed as fixed number*/ +Fixed gf_tan(Fixed angle); +/*!\return acos(a), expressed as fixed number*/ +Fixed gf_acos(Fixed angle); +/*!\return asin(a), expressed as fixed number*/ +Fixed gf_asin(Fixed angle); +/*!\return atan(y, x), expressed as fixed number*/ +Fixed gf_atan2(Fixed y, Fixed x); + +#else + + +/*!Fixed is 32bit float number + \note This documentation has been generated for a float version of the GPAC framework. +*/ +typedef Float Fixed; +#define FIX_ONE 1.0f +#define INT2FIX(v) ((Float) (v)) +#define FLT2FIX(v) ((Float) (v)) +#define FIX2INT(v) ((s32)(v)) +#define FIX2FLT(v) ((Float) (v)) +#define FIX_EPSILON GF_EPSILON_FLOAT +#define FIX_MAX GF_MAX_FLOAT +#define FIX_MIN -GF_MAX_FLOAT +#define GF_PI2 1.5707963267949f +#define GF_PI 3.1415926535898f +#define GF_2PI 6.2831853071796f + +/*!\hideinitializer 1/_a, expressed as fixed number*/ +#define gf_invfix(_a) (FIX_ONE/(_a)) +/*!\hideinitializer _a*_b, expressed as fixed number*/ +#define gf_mulfix(_a, _b) ((_a)*(_b)) +/*!\hideinitializer _a*_b/_c, expressed as fixed number*/ +#define gf_muldiv(_a, _b, _c) ((_c) ? (_a)*(_b)/(_c) : GF_MAX_FLOAT) +/*!\hideinitializer _a/_b, expressed as fixed number*/ +#define gf_divfix(_a, _b) ((_b) ? (_a)/(_b) : GF_MAX_FLOAT) +/*!\hideinitializer sqrt(_a), expressed as fixed number*/ +#define gf_sqrt(_a) ((Float) sqrt(_a)) +/*!\hideinitializer ceil(_a), expressed as fixed number*/ +#define gf_ceil(_a) ((Float) ceil(_a)) +/*!\hideinitializer floor(_a), expressed as fixed number*/ +#define gf_floor(_a) ((Float) floor(_a)) +/*!\hideinitializer cos(_a), expressed as fixed number*/ +#define gf_cos(_a) ((Float) cos(_a)) +/*!\hideinitializer sin(_a), expressed as fixed number*/ +#define gf_sin(_a) ((Float) sin(_a)) +/*!\hideinitializer tan(_a), expressed as fixed number*/ +#define gf_tan(_a) ((Float) tan(_a)) +/*!\hideinitializer atan2(_y,_x), expressed as fixed number*/ +#define gf_atan2(_y, _x) ((Float) atan2(_y, _x)) +/*!\hideinitializer acos(_a), expressed as fixed number*/ +#define gf_acos(_a) ((Float) acos(_a)) +/*!\hideinitializer asin(_a), expressed as fixed number*/ +#define gf_asin(_a) ((Float) asin(_a)) + +#endif + +/*!\def FIX_ONE + \hideinitializer + Fixed unit value +*/ +/*!\def INT2FIX(v) + \hideinitializer + Conversion from integer to fixed +*/ +/*!\def FLT2FIX(v) + \hideinitializer + Conversion from float to fixed +*/ +/*!\def FIX2INT(v) + \hideinitializer + Conversion from fixed to integer +*/ +/*!\def FIX2FLT(v) + \hideinitializer + Conversion from fixed to float +*/ +/*!\def FIX_EPSILON + \hideinitializer + Epsilon Fixed (positive value closest to 0) +*/ +/*!\def FIX_MAX + \hideinitializer + Maximum Fixed (maximum representable fixed value) +*/ +/*!\def FIX_MIN + \hideinitializer + Minimum Fixed (minimum representable fixed value) +*/ +/*!\def GF_PI2 + \hideinitializer + PI/2 expressed as Fixed +*/ +/*!\def GF_PI + \hideinitializer + PI expressed as Fixed +*/ +/*!\def GF_2PI + \hideinitializer + 2*PI expressed as Fixed +*/ + +Fixed gf_angle_diff(Fixed a, Fixed b); + +/*! + * \brief Field bit-size + * + * Gets the number of bits needed to represent the value. + * \param MaxVal Maximum value to be represented. + * \return number of bits required to represent the value. + */ +u32 gf_get_bit_size(u32 MaxVal); + +/*! + * \brief Get power of 2 + * + * Gets the closest power of 2 greater or equal to the value. + * \param val value to be used. + * \return requested power of 2. + */ +u32 gf_get_next_pow2(u32 val); + +/*! + *\addtogroup math2d_grp math2d + *\ingroup math_grp + *\brief 2D Mathematics functions + * + *This section documents mathematic tools for 2D geometry and color matrices operations + * @{ + */ + +/*!\brief 2D point + * + *The 2D point object is used in all the GPAC framework for both point and vector representation. +*/ +typedef struct __vec2f +{ + Fixed x; + Fixed y; +} GF_Point2D; +/*! + *\brief get 2D vector length + * + *Gets the length of a 2D vector + *\return length of the vector + */ +Fixed gf_v2d_len(GF_Point2D *vec); +/*! + *\brief 2D vector from polar coordinates + * + *Constructs a 2D vector from its polar coordinates + *\param length the length of the vector + *\param angle the angle of the vector in radians + *\return the 2D vector + */ +GF_Point2D gf_v2d_from_polar(Fixed length, Fixed angle); + +/*!\brief rectangle 2D + * + *The 2D rectangle used in the GPAC project. + */ +typedef struct +{ + /*!the left coordinate of the rectangle*/ + Fixed x; + /*!the top coordinate of the rectangle, regardless of the canvas orientation. In other words, y is always the + greatest coordinate value, even if the rectangle is presented bottom-up. This insures proper rectangles testing*/ + Fixed y; + /*!the width of the rectangle. Width must be greater than or equal to 0*/ + Fixed width; + /*!the height of the rectangle. Height must be greater than or equal to 0*/ + Fixed height; +} GF_Rect; + +/*! + \brief rectangle union + * + *Gets the union of two rectangles. + *\param rc1 first rectangle of the union. Upon return, this rectangle will contain the result of the union + *\param rc2 second rectangle of the union +*/ +void gf_rect_union(GF_Rect *rc1, GF_Rect *rc2); +/*! + \brief centers a rectangle + * + *Builds a rectangle centered on the origin + *\param w width of the rectangle + *\param h height of the rectangle + *\return centered rectangle object +*/ +GF_Rect gf_rect_center(Fixed w, Fixed h); +/*! + \brief rectangle overlap test + * + *Tests if two rectangles overlap. + *\param rc1 first rectangle to test + *\param rc2 second rectangle to test + *\return 1 if rectangles overlap, 0 otherwise +*/ +Bool gf_rect_overlaps(GF_Rect rc1, GF_Rect rc2); +/*! + \brief rectangle identity test + * + *Tests if two rectangles are identical. + *\param rc1 first rectangle to test + *\param rc2 second rectangle to test + *\return 1 if rectangles are identical, 0 otherwise +*/ +Bool gf_rect_equal(GF_Rect rc1, GF_Rect rc2); + +/*! + *\brief pixel-aligned rectangle + * + *Pixel-aligned rectangle used in the GPAC framework. This is usually needed for 2D drawing algorithms. + */ +typedef struct +{ + /*!the left coordinate of the rectangle*/ + s32 x; + /*!the top coordinate of the rectangle, regardless of the canvas orientation. In other words, y is always the + greatest coordinate value, even if the rectangle is presented bottom-up. This insures proper rectangles operations*/ + s32 y; + /*!the width of the rectangle. Width must be greater than or equal to 0*/ + s32 width; + /*!the height of the rectangle. Height must be greater than or equal to 0*/ + s32 height; +} GF_IRect; +/*! + *\brief gets the pixelized version of a rectangle + * + *Returns the smallest pixel-aligned rectangle completely containing a rectangle + *\param r the rectangle to transform + *\return the pixel-aligned transformed rectangle +*/ +GF_IRect gf_rect_pixelize(GF_Rect *r); + + +/*! + *\brief 2D matrix + * + *The 2D affine matrix object usied in GPAC. The transformation of P(x,y) in P'(X, Y) is: + \code + X = m[0]*x + m[1]*y + m[2]; + Y = m[3]*x + m[4]*y + m[5]; + \endcode +*/ +typedef struct +{ + Fixed m[6]; +} GF_Matrix2D; + +/*!\brief matrix initialization + *\hideinitializer + * + *Inits the matrix to the identity matrix +*/ +#define gf_mx2d_init(_obj) { memset((_obj).m, 0, sizeof(Fixed)*6); (_obj).m[0] = (_obj).m[4] = FIX_ONE; } +/*!\brief matrix copy + *\hideinitializer + * + *Copies the matrix _from to the matrix _obj +*/ +#define gf_mx2d_copy(_obj, from) memcpy((_obj).m, (from).m, sizeof(Fixed)*6) +/*!\brief matrix identity testing + *\hideinitializer + * + *This macro evaluates to 1 if the matrix _obj is the identity matrix, 0 otherwise +*/ +#define gf_mx2d_is_identity(_obj) ((!(_obj).m[1] && !(_obj).m[2] && !(_obj).m[3] && !(_obj).m[5] && ((_obj).m[0]==FIX_ONE) && ((_obj).m[4]==FIX_ONE)) ? 1 : 0) + +/*!\brief 2D matrix multiplication + * + *Multiplies two 2D matrices from*_this + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param from transformation matrix to add +*/ +void gf_mx2d_add_matrix(GF_Matrix2D *_this, GF_Matrix2D *from); + +/*!\brief 2D matrix pre-multiplication + * + *Multiplies two 2D matrices _this*from + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param from transformation matrix to add +*/ +void gf_mx2d_pre_multiply(GF_Matrix2D *_this, GF_Matrix2D *from); + +/*!\brief matrix translating + * + *Translates a 2D matrix + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param cx horizontal translation + *\param cy vertical translation +*/ +void gf_mx2d_add_translation(GF_Matrix2D *_this, Fixed cx, Fixed cy); +/*!\brief matrix rotating + * + *Rotates a 2D matrix + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param cx horizontal rotation center coordinate + *\param cy vertical rotation center coordinate + *\param angle rotation angle in radians +*/ +void gf_mx2d_add_rotation(GF_Matrix2D *_this, Fixed cx, Fixed cy, Fixed angle); +/*!\brief matrix scaling + * + *Scales a 2D matrix + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param scale_x horizontal scaling factor + *\param scale_y vertical scaling factor +*/ +void gf_mx2d_add_scale(GF_Matrix2D *_this, Fixed scale_x, Fixed scale_y); +/*!\brief matrix uncentered scaling + * + *Scales a 2D matrix with a non-centered scale + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param scale_x horizontal scaling factor + *\param scale_y vertical scaling factor + *\param cx horizontal scaling center coordinate + *\param cy vertical scaling center coordinate + *\param angle scale orienttion angle in radians +*/ +void gf_mx2d_add_scale_at(GF_Matrix2D *_this, Fixed scale_x, Fixed scale_y, Fixed cx, Fixed cy, Fixed angle); +/*!\brief matrix skewing + * + *Skews a 2D matrix + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param skew_x horizontal skew factor + *\param skew_y vertical skew factor +*/ +void gf_mx2d_add_skew(GF_Matrix2D *_this, Fixed skew_x, Fixed skew_y); +/*!\brief matrix horizontal skewing + * + *Skews a 2D matrix horizontally by a given angle + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param angle horizontal skew angle in radians +*/ +void gf_mx2d_add_skew_x(GF_Matrix2D *_this, Fixed angle); +/*!\brief matrix vertical skewing + * + *Skews a 2D matrix vertically by a given angle + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param angle vertical skew angle in radians +*/ +void gf_mx2d_add_skew_y(GF_Matrix2D *_this, Fixed angle); +/*!\brief matrix inversing + * + *Inverses a 2D matrix + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix +*/ +void gf_mx2d_inverse(GF_Matrix2D *_this); +/*!\brief matrix coordinate transformation + * + *Applies a 2D matrix transformation to coordinates + *\param _this transformation matrix + *\param x pointer to horizontal coordinate. Once the function is called, x contains the transformed horizontal coordinate + *\param y pointer to vertical coordinate. Once the function is called, y contains the transformed vertical coordinate +*/ +void gf_mx2d_apply_coords(GF_Matrix2D *_this, Fixed *x, Fixed *y); +/*!\brief matrix point transformation + * + *Applies a 2D matrix transformation to a 2D point + *\param _this transformation matrix + *\param pt pointer to 2D point. Once the function is called, pt contains the transformed point +*/ +void gf_mx2d_apply_point(GF_Matrix2D *_this, GF_Point2D *pt); +/*!\brief matrix rectangle transformation + * + *Applies a 2D matrix transformation to a rectangle, giving the enclosing rectangle of the transformed one + *\param _this transformation matrix + *\param rc pointer to rectangle. Once the function is called, rc contains the transformed rectangle +*/ +void gf_mx2d_apply_rect(GF_Matrix2D *_this, GF_Rect *rc); + +/*!\brief matrix decomposition + * + *Decomposes a 2D matrix M as M=Scale x Rotation x Translation if possible + *\param _this transformation matrix + *\param scale resulting scale part + *\param rotate resulting rotation part + *\param translate resulting translation part + *\return 0 if matrix cannot be decomposed, 1 otherwise +*/ +Bool gf_mx2d_decompose(GF_Matrix2D *_this, GF_Point2D *scale, Fixed *rotate, GF_Point2D *translate); + +/*! @} */ + + +/*! + *\addtogroup math3d_grp math3d + *\ingroup math_grp + *\brief 3D Mathematics functions + * + *This section documents mathematic tools for 3D geometry operations + * @{ + */ + +/*!\brief 3D point or vector + * + *The 3D point object is used in all the GPAC framework for both point and vector representation. +*/ +typedef struct __vec3f +{ + Fixed x; + Fixed y; + Fixed z; +} GF_Vec; + +/*base vector operations are MACROs for faster access*/ +/*!\hideinitializer macro evaluating to 1 if vectors are equal, 0 otherwise*/ +#define gf_vec_equal(v1, v2) (((v1).x == (v2).x) && ((v1).y == (v2).y) && ((v1).z == (v2).z)) +/*!\hideinitializer macro reversing a vector v = v*/ +#define gf_vec_rev(v) { (v).x = -(v).x; (v).y = -(v).y; (v).z = -(v).z; } +/*!\hideinitializer macro performing the minus operation res = v1 - v2*/ +#define gf_vec_diff(res, v1, v2) { (res).x = (v1).x - (v2).x; (res).y = (v1).y - (v2).y; (res).z = (v1).z - (v2).z; } +/*!\hideinitializer macro performing the add operation res = v1 + v2*/ +#define gf_vec_add(res, v1, v2) { (res).x = (v1).x + (v2).x; (res).y = (v1).y + (v2).y; (res).z = (v1).z + (v2).z; } + +/*! + *\brief get 3D vector length + * + *Gets the length of a 3D vector + *\return length of the vector + */ +Fixed gf_vec_len(GF_Vec v); +/*! + *\brief get 3D vector square length + * + *Gets the square length of a 3D vector + *\return square length of the vector + */ +Fixed gf_vec_lensq(GF_Vec v); +/*! + *\brief get 3D vector dot product + * + *Gets the dot product of two vectors + *\return dot product of the vectors + */ +Fixed gf_vec_dot(GF_Vec v1, GF_Vec v2); +/*! + *\brief vector normalization + * + *Norms the vector, eg make its length equal to \ref FIX_ONE + *\param v vector to normalize + */ +void gf_vec_norm(GF_Vec *v); +/*! + *\brief vector scaling + * + *Scales a vector by a given amount + *\param v vector to scale + *\param f scale factor + *\return scaled vector + */ +GF_Vec gf_vec_scale(GF_Vec v, Fixed f); +/*! + *\brief vector cross product + * + *Gets the cross product of two vectors + *\param v1 first vector + *\param v2 second vector + *\return cross-product vector + */ +GF_Vec gf_vec_cross(GF_Vec v1, GF_Vec v2); + +/*!\brief 4D vector + * + *The 4D vector object is used in all the GPAC framework for 4 dimension vectors, VRML Rotations and quaternions representation. +*/ +typedef struct __vec4f +{ + Fixed x; + Fixed y; + Fixed z; + Fixed q; +} GF_Vec4; + + +/*!\brief 3D matrix + * + *The 3D matrix object used in GPAC. The matrix is oriented like OpenGL matrices (column-major ordering), with + the translation part at the end of the coefficients list. + \note Unless specified otherwise, the matrix object is always expected to represent an affine transformation. + */ +typedef struct +{ + Fixed m[16]; +} GF_Matrix; + + +/*!\hideinitializer gets the len of a quaternion*/ +#define gf_quat_len(v) gf_sqrt(gf_mulfix((v).q,(v).q) + gf_mulfix((v).x,(v).x) + gf_mulfix((v).y,(v).y) + gf_mulfix((v).z,(v).z)) +/*!\hideinitializer normalizes a quaternion*/ +#define gf_quat_norm(v) { \ + Fixed __mag = gf_quat_len(v); \ + (v).x = gf_divfix((v).x, __mag); (v).y = gf_divfix((v).y, __mag); (v).z = gf_divfix((v).z, __mag); (v).q = gf_divfix((v).q, __mag); \ + } \ + +/*!\brief quaternion to rotation + * + *Transforms a quaternion to a Rotation, expressed as a 4 dimension vector with x,y,z for axis and q for rotation angle + *\param quat the quaternion to transform + *\return the rotation value + */ +GF_Vec4 gf_quat_to_rotation(GF_Vec4 *quat); +/*!\brief quaternion from rotation + * + *Transforms a Rotation to a quaternion + *\param rot the rotation to transform + *\return the quaternion value + */ +GF_Vec4 gf_quat_from_rotation(GF_Vec4 rot); +/*!inverses a quaternion*/ +GF_Vec4 gf_quat_get_inv(GF_Vec4 *quat); +/*!\brief quaternion multiplication + * + *Multiplies two quaternions + *\param q1 the first quaternion + *\param q2 the second quaternion + *\return the resulting quaternion + */ +GF_Vec4 gf_quat_multiply(GF_Vec4 *q1, GF_Vec4 *q2); +/*!\brief quaternion vector rotating + * + *Rotates a vector with a quaternion + *\param quat the quaternion modelizing the rotation + *\param vec the vector to rotate + *\return the resulting vector + */ +GF_Vec gf_quat_rotate(GF_Vec4 *quat, GF_Vec *vec); +/*!\brief quaternion from axis and cos + * + *Constructs a quaternion from an axis and a cosinus value (shortcut to \ref gf_quat_from_rotation) + *\param axis the rotation axis + *\param cos_a the rotation cosinus value + *\return the resulting quaternion + */ +GF_Vec4 gf_quat_from_axis_cos(GF_Vec axis, Fixed cos_a); +/*!\brief quaternion interpolation + * + *Interpolates two quaternions using spherical linear interpolation + *\param q1 the first quaternion + *\param q2 the second quaternion + *\param frac the fraction of the interpolation, between 0 and \ref FIX_ONE + *\return the interpolated quaternion + */ +GF_Vec4 gf_quat_slerp(GF_Vec4 q1, GF_Vec4 q2, Fixed frac); + +/*!\brief 3D Bounding Box + * + *The 3D Bounding Box is a 3D Axis-Aligned Bounding Box used to in various tools of the GPAC framework for bounds + estimation of a 3D object. It features an axis-aligned box and a sphere bounding volume for fast intersection tests. + */ +typedef struct +{ + /*!minimum x, y, and z of the object*/ + GF_Vec min_edge; + /*!maximum x, y, and z of the object*/ + GF_Vec max_edge; + + /*!center of the bounding box.\note this is computed from min_edge and max_edge*/ + GF_Vec center; + /*!radius of the bounding sphere for this box.\note this is computed from min_edge and max_edge*/ + Fixed radius; + /*!the bbox center and radius are valid*/ + Bool is_set; +} GF_BBox; +/*!updates information of the bounding box based on the edge information*/ +void gf_bbox_refresh(GF_BBox *b); +/*!builds a bounding box from a 2D rectangle*/ +void gf_bbox_from_rect(GF_BBox *box, GF_Rect *rc); +/*!builds a rectangle from a 3D bounding box.\note The z dimension is lost and no projection is performed*/ +void gf_rect_from_bbox(GF_Rect *rc, GF_BBox *box); +/*!\brief bounding box expansion + * + *Checks if a point is inside a bounding box and updates the bounding box to include it if not the case + *\param box the bounding box object + *\param pt the 3D point to check +*/ +void gf_bbox_grow_point(GF_BBox *box, GF_Vec pt); +/*!performs the union of two bounding boxes*/ +void gf_bbox_union(GF_BBox *b1, GF_BBox *b2); +/*!checks if two bounding boxes are equal or not*/ +Bool gf_bbox_equal(GF_BBox *b1, GF_BBox *b2); +/*!checks if a point is inside a bounding box or not*/ +Bool gf_bbox_point_inside(GF_BBox *box, GF_Vec *p); +/*!\brief get box vertices + * + *Returns the 8 bounding box vertices given the minimum and maximum edge. Vertices are ordered to respect + "p-vertex indexes", (vertex from a box closest to plane) and so that n-vertex (vertex from a box farthest from plane) + is 7-p_vx_idx + *\param bmin minimum edge of the box + *\param bmax maximum edge of the box + *\param vecs list of 8 3D points used to store the vertices. +*/ +void gf_bbox_get_vertices(GF_Vec bmin, GF_Vec bmax, GF_Vec *vecs); + + +/*!\brief matrix initialization + *\hideinitializer + * + *Inits the matrix to the identity matrix +*/ +#define gf_mx_init(_obj) { memset((_obj).m, 0, sizeof(Fixed)*16); (_obj).m[0] = (_obj).m[5] = (_obj).m[10] = (_obj).m[15] = FIX_ONE; } +/*!\brief matrix copy + *\hideinitializer + * + *Copies the matrix _from to the matrix _obj +*/ +#define gf_mx_copy(_obj, from) memcpy(&(_obj), &(from), sizeof(GF_Matrix)); +/*!\brief matrix constructor from 2D + * + *Initializes a 3D matrix from a 2D matrix.\note all z-related coefficients will be set to default. +*/ +void gf_mx_from_mx2d(GF_Matrix *mx, GF_Matrix2D *mat2D); +/*!\brief matrix identity testing + * + *Tests if two matrices are equal or not. + \return 1 if matrices are same, 0 otherwise +*/ +Bool gf_mx_equal(GF_Matrix *mx1, GF_Matrix *mx2); +/*!\brief matrix translation + * + *Translates a matrix + *\param mx the matrix being transformed. Once the function is called, contains the result matrix + *\param tx horizontal translation + *\param ty vertical translation + *\param tz depth translation +*/ +void gf_mx_add_translation(GF_Matrix *mx, Fixed tx, Fixed ty, Fixed tz); +/*!\brief matrix scaling + * + *Scales a matrix + *\param mx the matrix being transformed. Once the function is called, contains the result matrix + *\param sx horizontal translation scaling + *\param sy vertical translation scaling + *\param sz depth translation scaling +*/ +void gf_mx_add_scale(GF_Matrix *mx, Fixed sx, Fixed sy, Fixed sz); +/*!\brief matrix rotating + * + *Rotates a matrix + *\param mx the matrix being transformed. Once the function is called, contains the result matrix + *\param angle rotation angle in radians + *\param x horizontal coordinate of rotation axis + *\param y vertical coordinate of rotation axis + *\param z depth coordinate of rotation axis +*/ +void gf_mx_add_rotation(GF_Matrix *mx, Fixed angle, Fixed x, Fixed y, Fixed z); +/*!\brief matrices multiplication + * + *Multiplies a matrix with another one mx = mx*mul + *\param mx the matrix being transformed. Once the function is called, contains the result matrix + *\param mul the matrix to add +*/ +void gf_mx_add_matrix(GF_Matrix *mx, GF_Matrix *mul); +/*!\brief 2D matrix multiplication + * + *Adds a 2D affine matrix to a matrix + *\param mx the matrix + *\param mat2D the matrix to premultiply + */ +void gf_mx_add_matrix_2d(GF_Matrix *mx, GF_Matrix2D *mat2D); + +/*!\brief affine matrix inversion + * + *Inverses an affine matrix.\warning Results are undefined if the matrix is not an affine one + *\param mx the matrix to inverse + */ +void gf_mx_inverse(GF_Matrix *mx); +/*!\brief matrix point transformation + * + *Applies a 3D matrix transformation to a 3D point + *\param mx transformation matrix + *\param pt pointer to 3D point. Once the function is called, pt contains the transformed point +*/ +void gf_mx_apply_vec(GF_Matrix *mx, GF_Vec *pt); +/*!\brief matrix rectangle transformation + * + *Applies a 3D matrix transformation to a rectangle, giving the enclosing rectangle of the transformed one.\note all depth information are discarded. + *\param _this transformation matrix + *\param rc pointer to rectangle. Once the function is called, rc contains the transformed rectangle +*/ +void gf_mx_apply_rect(GF_Matrix *_this, GF_Rect *rc); +/*!\brief ortho matrix construction + * + *Creates an orthogonal projection matrix + *\param mx matrix to initialize + *\param left min horizontal coordinate of viewport + *\param right max horizontal coordinate of viewport + *\param bottom min vertical coordinate of viewport + *\param top max vertical coordinate of viewport + *\param z_near min depth coordinate of viewport + *\param z_far max depth coordinate of viewport +*/ +void gf_mx_ortho(GF_Matrix *mx, Fixed left, Fixed right, Fixed bottom, Fixed top, Fixed z_near, Fixed z_far); +/*!\brief perspective matrix construction + * + *Creates a perspective projection matrix + *\param mx matrix to initialize + *\param foc camera field of view angle in radian + *\param aspect_ratio viewport aspect ratio + *\param z_near min depth coordinate of viewport + *\param z_far max depth coordinate of viewport +*/ +void gf_mx_perspective(GF_Matrix *mx, Fixed foc, Fixed aspect_ratio, Fixed z_near, Fixed z_far); +/*!\brief creates look matrix + * + *Creates a transformation matrix looking at a given direction from a given point (camera matrix). + *\param mx matrix to initialize + *\param position position + *\param target look direction + *\param up_vector vector describing the up direction +*/ +void gf_mx_lookat(GF_Matrix *mx, GF_Vec position, GF_Vec target, GF_Vec up_vector); +/*!\brief matrix box transformation + * + *Applies a 3D matrix transformation to a bounding box, giving the enclosing box of the transformed one + *\param mx transformation matrix + *\param b pointer to bounding box. Once the function is called, contains the transformed bounding box +*/ +void gf_mx_apply_bbox(GF_Matrix *mx, GF_BBox *b); +/*!\brief matrix box sphere transformation + * + *Applies a 3D matrix transformation to a bounding box, computing only the enclosing sphere of the transformed one. + *\param mx transformation matrix + *\param b pointer to bounding box. Once the function is called, contains the transformed bounding sphere +*/ +void gf_mx_apply_bbox_sphere(GF_Matrix *mx, GF_BBox *box); +/*!\brief non-affine matrix multiplication + * + *Multiplies two non-affine matrices mx = mx*mul +*/ +void gf_mx_add_matrix_4x4(GF_Matrix *mat, GF_Matrix *mul); +/*!\brief non-affine matrix inversion + * + *Inverses a non-affine matrices + *\return 1 if inversion was done, 0 if inversion not possible. +*/ +Bool gf_mx_inverse_4x4(GF_Matrix *mx); +/*!\brief matrix 4D vector transformation + * + *Applies a 3D non-affine matrix transformation to a 4 dimension vector + *\param mx transformation matrix + *\param vec pointer to the vector. Once the function is called, contains the transformed vector +*/ +void gf_mx_apply_vec_4x4(GF_Matrix *mx, GF_Vec4 *vec); +/*!\brief matrix decomposition + * + *Decomposes a matrix into translation, scale, shear and rotate + *\param mx the matrix to decompose + *\param translate the decomposed translation part + *\param scale the decomposed scaling part + *\param rotate the decomposed rotation part, expressed as a Rotataion (axis + angle) + *\param shear the decomposed shear part + */ +void gf_mx_decompose(GF_Matrix *mx, GF_Vec *translate, GF_Vec *scale, GF_Vec4 *rotate, GF_Vec *shear); +/*!\brief matrix vector rotation + * + *Rotates a vector with a given matrix, ignoring any translation. + *\param mx transformation matrix + *\param pt pointer to 3D vector. Once the function is called, pt contains the transformed vector + */ +void gf_mx_rotate_vector(GF_Matrix *mx, GF_Vec *pt); +/*!\brief matrix initialization from vectors + * + *Inits a matrix to rotate the local axis in the given vectors + \param mx matrix to initialize + \param x_axis target normalized X axis + \param y_axis target normalized Y axis + \param z_axis target normalized Z axis +*/ +void gf_mx_rotation_matrix_from_vectors(GF_Matrix *mx, GF_Vec x_axis, GF_Vec y_axis, GF_Vec z_axis); +/*!\brief matrix to 2D matrix + * + *Inits a 2D matrix by removing all depth info from a 3D matrix + *\param mx2d 2D matrix to initialize + *\param mx 3D matrix to use +*/ +void gf_mx2d_from_mx(GF_Matrix2D *mx2d, GF_Matrix *mx); + +/*!\brief Plane object*/ +typedef struct +{ + /*!normal vector to the plane*/ + GF_Vec normal; + /*!distance from origin of the plane*/ + Fixed d; +} GF_Plane; +/*!\brief matrix plane transformation + * + *Transorms a plane by a given matrix + *\param mx the matrix to use + *\param plane pointer to 3D plane. Once the function is called, plane contains the transformed plane + */ +void gf_mx_apply_plane(GF_Matrix *mx, GF_Plane *plane); +/*!\brief point to plane distance + * + *Gets the distance between a point and a plne + *\param plane the plane to use + *\param p pointer to ^point to check + *\return the distance between the place and the point + */ +Fixed gf_plane_get_distance(GF_Plane *plane, GF_Vec *p); +/*!\brief closest point on a line + * + *Gets the closest point on a line from a given point in space + *\param line_pt a point of the line to test + *\param line_vec the normalized direction vector of the line + *\param pt the point to check + *\return the closest point on the line to the desired point + */ +GF_Vec gf_closest_point_to_line(GF_Vec line_pt, GF_Vec line_vec, GF_Vec pt); +/*!\brief box p-vertex index + * + *Gets the p-vertex index for a given plane. The p-vertex index is the index of the closest vertex of a bounding box to the plane. The vertices of a box are always + *ordered in GPAC? cf \ref gf_bbox_get_vertices + *\param p the plane to check + *\return the p-vertex index value, ranging from 0 to 7 +*/ +u32 gf_plane_get_p_vertex_idx(GF_Plane *p); +/*!\brief plane line intersection + * + *Checks for the intersection of a plane and a line + *\param plane plane to test + *\param linepoint a point on the line to test + *\param linevec normalized direction vector of the line to test + *\param outPoint optional pointer to retrieve the intersection point, NULL otherwise + *\return 1 if line and plane intersect, 0 otherwise +*/ +Bool gf_plane_intersect_line(GF_Plane *plane, GF_Vec *linepoint, GF_Vec *linevec, GF_Vec *outPoint); + +/*!Classification types for box/plane position used in \ref gf_bbox_plane_relation*/ +enum +{ + /*!box is in front of the plane*/ + GF_BBOX_FRONT, + /*!box intersects the plane*/ + GF_BBOX_INTER, + /*!box is back of the plane*/ + GF_BBOX_BACK +}; +/*!\brief box-plane relation + * + *Gets the spatial relation between a box and a plane + *\param box the box to check + *\param p the plane to check + *\return the relation type + */ +u32 gf_bbox_plane_relation(GF_BBox *box, GF_Plane *p); + +/*!\brief 3D Ray + * + *The 3D ray object is used in GPAC for all collision and mouse interaction tests +*/ +typedef struct +{ + /*!origin point of the ray*/ + GF_Vec orig; + /*!normalized direction vector of the ray*/ + GF_Vec dir; +} GF_Ray; + +/*!\brief ray constructor + * + *Constructs a ray object + *\param start starting point of the ray + *\param end end point of the ray, or any point on the ray + *\return the ray object +*/ +GF_Ray gf_ray(GF_Vec start, GF_Vec end); +/*!\brief matrix ray transformation + * + *Transforms a ray by a given transformation matrix + *\param mx the matrix to use + *\param r pointer to the ray. Once the function is called, contains the transformed ray +*/ +void gf_mx_apply_ray(GF_Matrix *mx, GF_Ray *r); +/*!\brief ray box intersection test + * + *Checks if a ray intersects a box or not + *\param ray the ray to check + *\param min_edge the minimum edge of the box to check + *\param max_edge the maximum edge of the box to check + *\param out_point optional location of a 3D point to store the intersection, NULL otherwise. + *\return retuns 1 if the ray intersects the box, 0 otherwise +*/ +Bool gf_ray_hit_box(GF_Ray *ray, GF_Vec min_edge, GF_Vec max_edge, GF_Vec *out_point); +/*!\brief ray sphere intersection test + * + *Checks if a ray intersects a box or not + *\param ray the ray to check + *\param center the center of the sphere to check. If NULL, the origin (0,0,0)is used + *\param radius the radius of the sphere to check + *\param out_point optional location of a 3D point to store the intersection, NULL otherwise + *\return retuns 1 if the ray intersects the sphere, 0 otherwise +*/ +Bool gf_ray_hit_sphere(GF_Ray *ray, GF_Vec *center, Fixed radius, GF_Vec *out_point); +/*!\brief ray triangle intersection test + * + *Checks if a ray intersects a triangle or not + *\param ray the ray to check + *\param v0 first vertex of the triangle + *\param v1 second vertex of the triangle + *\param v2 third vertex of the triangle + *\param dist optional location of a fixed number to store the intersection distance from ray origin if any, NULL otherwise + *\return retuns 1 if the ray intersects the triangle, 0 otherwise +*/ +Bool gf_ray_hit_triangle(GF_Ray *ray, GF_Vec *v0, GF_Vec *v1, GF_Vec *v2, Fixed *dist); +/*same as above and performs backface cull (solid meshes)*/ +/*!\brief ray triangle intersection test + * + *Checks if a ray intersects a triangle or not, performing backface culling. For parameters details, look at \ref gf_ray_hit_triangle_backcull + */ +Bool gf_ray_hit_triangle_backcull(GF_Ray *ray, GF_Vec *v0, GF_Vec *v1, GF_Vec *v2, Fixed *dist); + +/*! @} */ + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MATH_H_*/ + diff --git a/include/gpac/media_tools.h b/include/gpac/media_tools.h new file mode 100644 index 0000000..eb4cc6e --- /dev/null +++ b/include/gpac/media_tools.h @@ -0,0 +1,335 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Authoring Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_MEDIA_H_ +#define _GF_MEDIA_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/isomedia.h> +#include <gpac/avparse.h> + +/* + track importers + + All these can import a file into a dedicated track. If esd is NULL the track is blindly added + otherwise it is added with the requested ESID if non-0, otherwise the new trackID is stored in ESID + if use_data_ref is set, data is only referenced in the file + if duration is not 0, only the first duration seconds are imported + NOTE: if an ESD is specified, its decoderSpecificInfo is also updated +*/ +/*track importer flags*/ +enum +{ + /*references data rather than copy, whenever possible*/ + GF_IMPORT_USE_DATAREF = 1, + /*for AVI video: imports at constant FPS (eg imports N-Vops due to encoder drops)*/ + GF_IMPORT_NO_FRAME_DROP = 1<<1, + /*for CMP ASP only: forces treating the input as packed bitsream and discards all n-vops*/ + GF_IMPORT_FORCE_PACKED = 1<<2, + /*for AAC audio: forces SBR mode with implicit signaling (backward compatible)*/ + GF_IMPORT_SBR_IMPLICIT = 1<<3, + /*for AAC audio: forces SBR mode with explicit signaling (non-backward compatible). + Will override GF_IMPORT_SBR_IMPLICIT flag when set*/ + GF_IMPORT_SBR_EXPLICIT = 1<<4, + /*forces MPEG-4 import - some 3GP2 streams have both native IsoMedia sample description and MPEG-4 one possible*/ + GF_IMPORT_FORCE_MPEG4 = 1<<5, + /*special flag for text import at run time (never set on probe), indicates to leave the text box empty + so that we dynamically adapt to display size*/ + GF_IMPORT_SKIP_TXT_BOX = 1<<6, + /*indicates to keep unknown tracks from .MOV/.IsoMedia files*/ + GF_IMPORT_KEEP_ALL_TRACKS = 1<<7, + /*uses compact size in .MOV/.IsoMedia files*/ + GF_IMPORT_USE_COMPACT_SIZE = 1<<8, + + /*when set, only updates tracks info and return*/ + GF_IMPORT_PROBE_ONLY = 1<<20, + /*only set when probing, signals several frames per sample possible*/ + GF_IMPORT_3GPP_AGGREGATION = 1<<21, + /*only set when probing, signals video FPS overridable*/ + GF_IMPORT_OVERRIDE_FPS = 1<<22, + /*only set when probing, signals duration not usable*/ + GF_IMPORT_NO_DURATION = 1<<23, + /*when set by user during import, will abort*/ + GF_IMPORT_DO_ABORT = 1<<31 +}; + +#define GF_IMPORT_MAX_TRACKS 100 +struct __track_video_info +{ + u32 width, height, par; + Double FPS; +}; +struct __track_audio_info +{ + u32 sample_rate, nb_channels; +}; + +struct __track_import_info +{ + u32 track_num; + /*track type (GF_ISOM_MEDIA_****)*/ + u32 type; + /*media type ('MPG1', 'MPG2', ISO 4CC, AVI 4CC)*/ + u32 media_type; + /*possible import flags*/ + u32 flags; + /*media format info*/ + struct __track_video_info video_info; + struct __track_audio_info audio_info; + + u32 lang; + /*for MPEG2 TS: program number*/ + u16 prog_num; +}; + +struct __program_import_info +{ + u32 number; + char name[40]; +}; + +/*track dumper*/ +typedef struct __track_import +{ + GF_ISOFile *dest; + /*media to import: + MP4/ISO media: trackID + AVI files: + 0: first video and first audio, + 1: video track + 2->any: audio track(s) + MPEG-PS files with N video streams: + 0: first video and first audio + 1->N: video track + N+1->any: audio track + TrackNums can be obtain with probing + */ + u32 trackID; + /*media source - selects importer type based on extension*/ + char *in_name; + /*import duration if any*/ + u32 duration; + /*importer flags*/ + u32 flags; + /*if non 0, force video FPS (CMP, AVI, OGG, H264) - also used by SUB import*/ + Double video_fps; + /*optional ESD*/ + GF_ESD *esd; + /*optional format indication for media source (used in IM1)*/ + char *streamFormat; + /*frame per sample cumulation (3GP media only) - MAX 15, ignored in data ref*/ + u32 frames_per_sample; + /*track ID of imported media in destination file*/ + u32 final_trackID; + + /*for MP4 import only*/ + GF_ISOFile *orig; + + /*for text import*/ + u32 fontSize; + char *fontName; + + /*number of tracks after probing - may be set to 0, in which case no track + selection can be performed. It may also be inaccurate if probing doesn't + detect all available tracks (cf ogg import)*/ + u32 nb_tracks; + /*track info after probing (GF_IMPORT_PROBE_ONLY set).*/ + struct __track_import_info tk_info[GF_IMPORT_MAX_TRACKS]; + + /*for MPEG-TS and similar: program names*/ + u32 nb_progs; + struct __program_import_info pg_info[GF_IMPORT_MAX_TRACKS]; + + GF_Err last_error; +} GF_MediaImporter; + +GF_Err gf_media_import(GF_MediaImporter *importer); + +enum +{ + /*track dumper types are formatted as flags for conveniency for + authoring tools, but never used as a OR'ed set*/ + /*native format (JPG, PNG, MP3, etc) if supported*/ + GF_EXPORT_NATIVE = 1, + /*raw samples (including hint tracks for rtp)*/ + GF_EXPORT_RAW_SAMPLES = (1<<1), + /*NHNT format (any MPEG-4 media)*/ + GF_EXPORT_NHNT = (1<<2), + /*AVI (MPEG4 video and AVC tracks only)*/ + GF_EXPORT_AVI = (1<<3), + /*MP4 (all except OD)*/ + GF_EXPORT_MP4 = (1<<4), + /*AVI->RAW to dump video (trackID=1) or audio (trackID>=2)*/ + GF_EXPORT_AVI_NATIVE = (1<<5), + /*NHML format (any media)*/ + GF_EXPORT_NHML = (1<<6), + /*SAF format*/ + GF_EXPORT_SAF = (1<<7), + + /*following ones are real flags*/ + /* + for MP4 extraction, indicates track should be added to dest file if any + for raw extraction, indicates data shall be appended at the end of output file if present + */ + GF_EXPORT_MERGE = (1<<10), + /*indicates QCP file format possible as well as native (EVRC and SMV audio only)*/ + GF_EXPORT_USE_QCP = (1<<11), + /*indicates full NHML dump*/ + GF_EXPORT_NHML_FULL = (1<<11), + /*ony probes extraction format*/ + GF_EXPORT_PROBE_ONLY = (1<<30), + /*when set by user during export, will abort*/ + GF_EXPORT_DO_ABORT = (1<<31), +}; + +/*track dumper*/ +typedef struct __track_exporter +{ + GF_ISOFile *file; + u32 trackID; + /*sample number to export for GF_EXPORT_RAW_SAMPLES only*/ + u32 sample_num; + /*out name WITHOUT extension*/ + char *out_name; + /*dump type*/ + u32 flags; + /*non-IsoMedia file (AVI)*/ + char *in_name; +} GF_MediaExporter; + +/*if error returns same value as error signaled in message*/ +GF_Err gf_media_export(GF_MediaExporter *dump); + + + +/* + RTP IsoMedia file hinting +*/ +typedef struct __tag_isom_hinter GF_RTPHinter; + +GF_RTPHinter *gf_hinter_track_new(GF_ISOFile *file, u32 TrackNum, + u32 Path_MTU, u32 max_ptime, u32 default_rtp_rate, u32 hint_flags, u8 PayloadID, + Bool copy_media, u32 InterleaveGroupID, u8 InterleaveGroupPriority, GF_Err *e); +/*delete the track hinter*/ +void gf_hinter_track_del(GF_RTPHinter *tkHinter); +/*hints all samples in the media track*/ +GF_Err gf_hinter_track_process(GF_RTPHinter *tkHint); +/*returns media bandwidth in kbps*/ +u32 gf_hinter_track_get_bandwidth(GF_RTPHinter *tkHinter); +/*retrieves hinter flags*/ +u32 gf_hinter_track_get_flags(GF_RTPHinter *tkHinter); +/*retrieves rtp payload name + @payloadName: static buffer for retrieval, minimum 30 bytes +*/ +void gf_hinter_track_get_payload_name(GF_RTPHinter *tkHint, char *payloadName); + +/*finalizes hinting process for the track (setup flags, write SDP for RTP, ...) + @AddSystemInfo: if non-0, systems info are duplicated in the SDP (decoder cfg, PL IDs ..) +*/ +GF_Err gf_hinter_track_finalize(GF_RTPHinter *tkHint, Bool AddSystemInfo); + +/*SDP IOD flag*/ +enum +{ + /*no IOD included*/ + GF_SDP_IOD_NONE = 0, + /*base64 encoding of the regular MPEG-4 IOD*/ + GF_SDP_IOD_REGULAR, + /*base64 encoding of IOD containing BIFS and OD tracks (one AU only) - this is used for ISMA 1.0 profiles + note that the "hinted" file will loose all systems info*/ + GF_SDP_IOD_ISMA, + /*same as ISMA but removes all clock references from IOD*/ + GF_SDP_IOD_ISMA_STRICT, +}; + +/*finalizes hinting process for the file (setup flags, write SDP for RTP, ...) + @IOD_Profile: see above + @bandwidth: total bandwidth in kbps of all hinted tracks, 0 means no bandwidth info at session level +*/ +GF_Err gf_hinter_finalize(GF_ISOFile *file, u32 IOD_Profile, u32 bandwidth); + + +/*returns TRUE if the encoded data fits in an ESD url - streamType is the systems stream type needed to +signal data mime-type (OD, BIFS or any) */ +Bool gf_hinter_can_embbed_data(char *data, u32 data_size, u32 streamType); + +/*save file as fragmented movie*/ +GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double MaxFragmentDuration); + +/*adds chapter info contained in file - import_fps is optional (most formats don't use it), defaults to 25*/ +GF_Err gf_media_import_chapters(GF_ISOFile *file, char *chap_file, Double import_fps); + + +/*make the file ISMA compliant: creates ISMA BIFS / OD tracks if needed, and update audio/video IDs +the file should not contain more than one audio and one video track +@keepImage: if set, generates system info if image is found - only used for image imports +*/ +GF_Err gf_media_make_isma(GF_ISOFile *mp4file, Bool keepESIDs, Bool keepImage, Bool no_ocr); + +/*make the file 3GP compliant && sets profile +*/ +GF_Err gf_media_make_3gpp(GF_ISOFile *mp4file); + +/*make the file playable on a PSP +*/ +GF_Err gf_media_make_psp(GF_ISOFile *mp4file); + +/*creates (if needed) a GF_ESD for the given track - THIS IS RESERVED for local playback +only, since the OTI used when emulated is not standard...*/ +GF_ESD *gf_media_map_esd(GF_ISOFile *mp4, u32 track); + +/*changes pixel aspect ratio for visual tracks if supported. Negative values remove any PAR info*/ +GF_Err gf_media_change_par(GF_ISOFile *file, u32 track, s32 ar_num, s32 ar_den); + + +/*SAF Multiplexer object. The multiplexer supports concurencial (multi-threaded) access*/ +typedef struct __saf_muxer GF_SAFMuxer; +/*SAF Multiplexer constructor*/ +GF_SAFMuxer *gf_saf_mux_new(); +/*SAF Multiplexer destructor*/ +void gf_saf_mux_del(GF_SAFMuxer *mux); +/*adds a new stream in the SAF multiplex*/ +GF_Err gf_saf_mux_stream_add(GF_SAFMuxer *mux, u32 stream_id, u32 ts_res, u32 buffersize_db, u8 stream_type, u8 object_type, char *mime_type, char *dsi, u32 dsi_len, char *remote_url); +/*removes a stream from the SAF multiplex*/ +GF_Err gf_saf_mux_stream_rem(GF_SAFMuxer *mux, u32 stream_id); +/*adds an AU to the given stream. !!AU data will be freed by the multiplexer!! +AUs are not reinterleaved based on their CTS, in order to enable audio interleaving +*/ +GF_Err gf_saf_mux_add_au(GF_SAFMuxer *mux, u32 stream_id, u32 CTS, char *data, u32 data_len, Bool is_rap); +/*gets the content of the multiplexer for the given time. +if force_end_of_session is set, this flushes the SAF Session - no more operations will be allowed on the muxer*/ +GF_Err gf_saf_mux_for_time(GF_SAFMuxer *mux, u32 time_ms, Bool force_end_of_session, char **out_data, u32 *out_size); + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MEDIA_H_*/ + diff --git a/include/gpac/mediaobject.h b/include/gpac/mediaobject.h new file mode 100644 index 0000000..4d1e25e --- /dev/null +++ b/include/gpac/mediaobject.h @@ -0,0 +1,149 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Stream Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#ifndef _GF_MEDIA_OBJECT_H_ +#define _GF_MEDIA_OBJECT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/scenegraph_vrml.h> + + +/* + Media Object + + opaque handler for all natural media objects (audio, video, image) so that compositor and systems engine +are not too tied up. + NOTE: the media object location relies on the node parent graph (this is to deal with namespaces in OD framework) +therefore it is the task of the media management app to setup clear links between the scene graph and its ressources +(but this is not mandatory, cf URLs in VRML ) + + TODO - add interface for shape coding positioning in mediaObject and in the decoder API +*/ + +typedef struct _mediaobj GF_MediaObject; + +/*locate media object related to the given node - url designes the object to find - returns NULL if +URL cannot be handled - note that until the mediaObject.isInit member is true, the media object is not valid +(and could actually never be) */ +GF_MediaObject *gf_mo_register(GF_Node *node, MFURL *url, Bool lock_timelines); +/*unregister the node from the media object*/ +void gf_mo_unregister(GF_Node *node, GF_MediaObject *mo); + +/*opens media object*/ +void gf_mo_play(GF_MediaObject *mo, Double clipBegin, Double clipEnd, Bool can_loop); +/*stops media object - video memory is not reset, last frame is kept*/ +void gf_mo_stop(GF_MediaObject *mo); +/*restarts media object - shall be used for all looping media instead of stop/play for mediaControl +to restart appropriated objects*/ +void gf_mo_restart(GF_MediaObject *mo); + +void gf_mo_pause(GF_MediaObject *mo); +void gf_mo_resume(GF_MediaObject *mo); + +/* + Note on mediaControl: mediaControl is the media management app responsability, therefore +is hidden from the rendering app. Since MediaControl overrides default settings of the node (speed and loop) +you must use the gf_mo_get_speed and gf_mo_get_loop in order to know whether the related field applies or not +*/ + +/*set speed of media - speed is not always applied, depending on media control settings. +NOTE: audio pitching is the responsability of the rendering app*/ +void gf_mo_set_speed(GF_MediaObject *mo, Fixed speed); +/*returns current speed of media - in_speed is the speed of the media as set in the node (MovieTexture, +AudioClip and AudioSource) - the return value is the real speed of the media as overloaded by mediaControl if any*/ +Fixed gf_mo_get_speed(GF_MediaObject *mo, Fixed in_speed); +/*returns looping flag of media - in_loop is the looping flag of the media as set in the node (MovieTexture, +AudioClip) - the return value is the real loop flag of the media as overloaded by mediaControl if any*/ +Bool gf_mo_get_loop(GF_MediaObject *mo, Bool in_loop); +/*returns media object duration*/ +Double gf_mo_get_duration(GF_MediaObject *mo); +/*returns whether the object should be deactivated (stop) or not - this checks object status as well as +mediaControl status */ +Bool gf_mo_should_deactivate(GF_MediaObject *mo); +/*checks whether the target object is changed - you MUST use this in order to detect url changes*/ +Bool gf_mo_url_changed(GF_MediaObject *mo, MFURL *url); + + +/*fetch media data + +*/ +char *gf_mo_fetch_data(GF_MediaObject *mo, Bool resync, Bool *eos, u32 *timestamp, u32 *size); + +/*release given amount of media data - nb_bytes is used for audio - if forceDrop is set, the unlocked frame will be +droped if all bytes are consumed, otherwise it will be droped based on object time - typically, video fetches with the resync +flag set and release without forceDrop, while audio fetches without resync but forces buffer drop. If forceDrop is set to 2, +the frame will be stated as a discraded frame*/ +void gf_mo_release_data(GF_MediaObject *mo, u32 nb_bytes, s32 forceDrop); +/*get media time*/ +void gf_mo_get_media_time(GF_MediaObject *mo, u32 *media_time, u32 *media_dur); +/*get object clock*/ +void gf_mo_get_object_time(GF_MediaObject *mo, u32 *obj_time); +/*returns mute flag of media - if muted the media shouldn't be displayed*/ +Bool gf_mo_is_muted(GF_MediaObject *mo); +/*returns end of stream state*/ +Bool gf_mo_is_done(GF_MediaObject *mo); +/*resyncs clock - only audio objects are allowed to use this*/ +void gf_mo_adjust_clock(GF_MediaObject *mo, s32 ms_drift); + +u32 gf_mo_get_last_frame_time(GF_MediaObject *mo); + +Bool gf_mo_get_visual_info(GF_MediaObject *mo, u32 *width, u32 *height, u32 *stride, u32 *pixel_ar, u32 *pixelFormat); + +Bool gf_mo_get_audio_info(GF_MediaObject *mo, u32 *sample_rate, u32 *bits_per_sample, u32 *num_channels, u32 *channel_config); + +/*checks if the service associated withthis object has an audio stream*/ +Bool gf_mo_has_audio(GF_MediaObject *mo); + +enum +{ + /*this is set to 0 by the OD manager whenever a change occur in the media (w/h change, SR change, etc) + as a hint for the compositor*/ + GF_MO_IS_INIT = (1<<1), + /*used by animation stream to remove TEXT from display upon delete and URL change*/ + GF_MO_DISPLAY_REMOVE = (1<<2), +}; + +u32 gf_mo_get_flags(GF_MediaObject *mo); +void gf_mo_set_flag(GF_MediaObject *mo, u32 flag, Bool set_on); + +/*loads a new resource as indicated in the xlink:href attribute of the node. If this points to a fragment +of the current document, returns NULL. This will automatically trigger a play request on the resource*/ +GF_MediaObject *gf_mo_load_xlink_resource(GF_Node *node, Bool primary_resource, Double clipBegin, Double clipEnd); +void gf_mo_unload_xlink_resource(GF_Node *node, GF_MediaObject *mo); +/*returns scene graph associated with a scene/document object, or NULL if wrong type or not loaded*/ +GF_SceneGraph *gf_mo_get_scenegraph(GF_MediaObject *mo); + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MEDIA_OBJECT_H_*/ + + diff --git a/include/gpac/module.h b/include/gpac/module.h new file mode 100644 index 0000000..594b02f --- /dev/null +++ b/include/gpac/module.h @@ -0,0 +1,235 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_MODULE_H_ +#define _GF_MODULE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file <gpac/module.h> + * \brief plugable module functions. + */ + +/*! + * \addtogroup mods_grp plugable modules + * \ingroup utils_grp + * \brief Plugable Module functions + * + *This section documents the plugable module functions of the GPAC framework. + *A module is a dynamic/shared library providing one or several interfaces to the GPAC framework. + *A module cannot provide several interfaces of the same type. Each module must export the following functions: + \code + * Bool QueryInterface(u32 interface_type); + \endcode + * This function is used to query supported interfaces. It returns non zero if the module handles this interface type, 0 otherwise.\n + \code + GF_BaseInterface *LoadInterface(u32 interface_type); + \endcode + * This function is used to load an interface. It returns the interface object, NULL if error.\n + \code + void ShutdownInterface(GF_BaseInterface *interface); + \endcode + *This function is used to destroy an interface.\n\n + *Each interface must begin with the interface macro in order to be type-casted to the base interface structure. + \code + struct { + GF_DECL_MODULE_INTERFACE + extensions; + }; + \endcode + * @{ + */ + +#include <gpac/config_file.h> + +typedef struct __tag_mod_man GF_ModuleManager; + +/*! + *\brief common module interface + *\hideinitializer + * + *This is the module interface declaration macro. It must be placed first in an interface structure declaration. +*/ +#define GF_DECL_MODULE_INTERFACE \ + u32 InterfaceType; \ + const char *module_name; \ + const char *author_name; \ + void *HPLUG; \ + +/*! + *\brief Base Interface + * + *This structure represent a base interface, e.g. the minimal interface declaration without functionalities. Each interface is + *type-casted to this structure and shall always be checked against its interface type. API Versioning is taken care of in the + *interface type itsel, changing at each modification of the interface API + */ +typedef struct +{ + GF_DECL_MODULE_INTERFACE +} GF_BaseInterface; + +/*! + *\brief module interface registration + *\hideinitializer + * + *This is the module interface registration macro. A module must call this macro whenever creating a new interface. + *- \e _ifce: interface being registered + *- \e _ifce_type: the four character code defining the interface type. + *- \e _ifce_name: a printable string giving the interface name (const char *). + *- \e _ifce_author: a printable string giving the author name (const char *). + *\n + *This is a sample GPAC codec interface declaration: + \code +GF_BaseInterface *MyDecoderInterfaceLoad() { + GF_MediaDecoder *ifce; + GF_SAFEALLOC(ifce, GF_MediaDecoder); + GF_REGISTER_MODULE_INTERFACE(ifce, GF_MEDIA_DECODER_INTERFACE, "Sample Decoder", "The Author") + //follows any initialization private to the decoder + return (GF_BaseInterface *)ifce; +} + \endcode +*/ +#define GF_REGISTER_MODULE_INTERFACE(_ifce, _ifce_type, _ifce_name, _ifce_author) \ + _ifce->InterfaceType = _ifce_type; \ + _ifce->module_name = _ifce_name ? _ifce_name : "unknown"; \ + _ifce->author_name = _ifce_author ? _ifce_author : "gpac distribution"; \ + +/*! + *\brief module manager construtcor + * + *Constructs a module manager object. + *\param directory absolute path to the directory where the manager shall look for modules + *\param cfgFile GPAC configuration file handle. If this is NULL, the modules won't be able to share the configuration + *file with the rest of the GPAC framework. + *\return the module manager object +*/ +GF_ModuleManager *gf_modules_new(const char *directory, GF_Config *cfgFile); +/*! + *\brief module manager destructor + * + *Destroys the module manager + *\param pm the module manager + */ +void gf_modules_del(GF_ModuleManager *pm); +/*! + *\brief refreshes modules + * + *Refreshes all modules in the manager directory and load unloaded ones + *\param pm the module manager + *\return the number of loaded modules + */ +u32 gf_modules_refresh(GF_ModuleManager *pm); + +/*! + *\brief get module count + * + *Gets the number of modules found in the manager directory + *\param pm the module manager + *\return the number of loaded modules + */ +u32 gf_modules_get_count(GF_ModuleManager *pm); + +/*! + *\brief get module file name + * + *Gets a module shared library file name based on its index + *\param pm the module manager + *\param index the 0-based index of the module to query + *\return the name of the shared library module + */ +const char *gf_modules_get_file_name(GF_ModuleManager *pm, u32 index); + +/*! + *\brief loads an interface + * + *Loads an interface in the desired module. + *\param pm the module manager + *\param index the 0-based index of the module to load the interface from + *\param InterfaceFamily type of the interface to load + *\return the interface object if found and loaded, NULL otherwise. + */ +GF_BaseInterface *gf_modules_load_interface(GF_ModuleManager *pm, u32 index, u32 InterfaceFamily); + +/*! + *\brief loads an interface by module name + * + *Loads an interface in the desired module + *\param pm the module manager + *\param mod_name the name of the module (shared library file) or of the interface as declared when registered. + *\param InterfaceFamily type of the interface to load + *\return the interface object if found and loaded, NULL otherwise. + */ +GF_BaseInterface *gf_modules_load_interface_by_name(GF_ModuleManager *pm, const char *mod_name, u32 InterfaceFamily); + +/*! + *\brief interface shutdown + * + *Closes an interface + *\param interface_obj the interface to close + */ +GF_Err gf_modules_close_interface(GF_BaseInterface *interface_obj); + +/*! + *\brief interface option query + * + *Gets an option from the config file associated with the module manager + *\param interface_obj the interface object used + *\param secName the desired key parent section name + *\param keyName the desired key name + *\return the desired key value if found, NULL otherwise. + */ +const char *gf_modules_get_option(GF_BaseInterface *interface_obj, const char *secName, const char *keyName); +/*! + *\brief interface option update + * + *Sets an option in the config file associated with the module manager + *\param interface_obj the interface object used + *\param secName the desired key parent section name + *\param keyName the desired key name + *\param keyValue the desired key value + *\note this will also create both section and key if they are not found in the configuration file + */ +GF_Err gf_modules_set_option(GF_BaseInterface *interface_obj, const char *secName, const char *keyName, const char *keyValue); + +/*! + *\brief get config fiole + * + *Gets the configuration file for the module instance + *\param interface_obj the interface object used + *\return handle to the config file + */ +GF_Config *gf_modules_get_config(GF_BaseInterface *ifce); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MODULE_H_*/ + diff --git a/include/gpac/modules/audio_out.h b/include/gpac/modules/audio_out.h new file mode 100644 index 0000000..c1ea6ee --- /dev/null +++ b/include/gpac/modules/audio_out.h @@ -0,0 +1,133 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / modules interfaces + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + + Note on video driver: this is not a graphics driver, the only thing requested from this driver + is accessing video memory and performing stretch of YUV and RGB on the backbuffer (bitmap node) + the graphics driver is a different entity that performs 2D rasterization + +*/ + +#ifndef _GF_MODULE_AUDIO_OUT_H_ +#define _GF_MODULE_AUDIO_OUT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*include event system*/ +#include <gpac/module.h> + + +/* + Audio hardware output module +*/ + +/*interface name and version for audio output*/ +#define GF_AUDIO_OUTPUT_INTERFACE GF_4CC('G','A','O', 0x03) + +/*interface returned on query interface*/ +typedef struct _audiooutput +{ + /* interface declaration*/ + GF_DECL_MODULE_INTERFACE + + /*setup system + Win32: os_handle is HWND + + if num_buffer is set, the audio driver should work with num_buffers with a total amount of audio data + equal to total_duration ms + if not set the driver is free to decide what to do + */ + GF_Err (*Setup) (struct _audiooutput *aout, void *os_handle, u32 num_buffers, u32 total_duration); + + /*shutdown system */ + void (*Shutdown) (struct _audiooutput *aout); + + /*query output frequency available - if the requested sampleRate is not available, the driver shall return the best + possible sampleRate able to handle NbChannels and NbBitsPerSample - if it doesn't handle the NbChannels + the internal mixer will do it + */ + GF_Err (*QueryOutputSampleRate)(struct _audiooutput *aout, u32 *io_desired_samplerate, u32 *io_NbChannels, u32 *io_nbBitsPerSample); + + /*set output config - if audio is not running, driver must start it + *SampleRate, *NbChannels, *nbBitsPerSample: + input: desired value + output: final values + channel_cfg is the channels output cfg, eg set of flags as specified in constants.h + */ + GF_Err (*ConfigureOutput) (struct _audiooutput *aout, u32 *SampleRate, u32 *NbChannels, u32 *nbBitsPerSample, u32 channel_cfg); + + /*returns total buffer size used in ms. This is needed to compute the min size of audio decoders output*/ + u32 (*GetTotalBufferTime)(struct _audiooutput *aout); + + /*returns audio delay in ms, eg time delay until written audio data is outputed by the sound card + This function is only called after ConfigureOuput*/ + u32 (*GetAudioDelay)(struct _audiooutput *aout); + + /*set output volume(between 0 and 100) */ + void (*SetVolume) (struct _audiooutput *aout, u32 Volume); + /*set balance (between 0 and 100, 0=full left, 100=full right)*/ + void (*SetPan) (struct _audiooutput *aout, u32 pan); + /*freezes soundcard flow - must not be NULL for self threaded + PlayType: 0: pause, 1: resume, 2: reset HW buffer and play. + */ + void (*Play) (struct _audiooutput *aout, u32 PlayType); + /*specifies whether the driver relies on the app to feed data or runs standalone*/ + Bool SelfThreaded; + + /*if not using private thread, this should perform sleep & fill of HW buffer + the audio render loop in this case is: while (run) {driver->WriteAudio(); if (reconf) Reconfig();} + the driver must therefore give back the hand to the renderer as often as possible - the usual way is: + gf_sleep untill hw data can be written + write HW data + return + */ + void (*WriteAudio)(struct _audiooutput *aout); + + /*if using private thread the following MUST be provided*/ + void (*SetPriority)(struct _audiooutput *aout, u32 priority); + + /*your private data handler - should be allocated when creating the interface object*/ + void *opaque; + + /*these are assigned by the audio renderer once module is loaded*/ + + /*fills the buffer with audio data, returns effective bytes written - the rest is filled with 0*/ + u32 (*FillBuffer) (void *audio_renderer, char *buffer, u32 buffer_size); + void *audio_renderer; + +} GF_AudioOutput; + + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MODULE_AUDIO_OUT_H_*/ + diff --git a/include/gpac/modules/codec.h b/include/gpac/modules/codec.h new file mode 100644 index 0000000..26c3181 --- /dev/null +++ b/include/gpac/modules/codec.h @@ -0,0 +1,231 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / modules interfaces + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#ifndef _GF_MODULE_CODEC_H_ +#define _GF_MODULE_CODEC_H_ + + +#include <gpac/module.h> +#include <gpac/mpeg4_odf.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/*multimedia processing levels*/ +enum +{ + /*normal, full processing*/ + GF_CODEC_LEVEL_NORMAL, + /*codec is late, should scale down processing*/ + GF_CODEC_LEVEL_LATE, + /*codec is very late, should turn off post-processing, even drop*/ + GF_CODEC_LEVEL_VERY_LATE, + /*input frames are already late before decoding*/ + GF_CODEC_LEVEL_DROP, + /*this is a special level indicating that a seek is happening (decode but no dispatch) + it is set dynamically*/ + GF_CODEC_LEVEL_SEEK +}; + +/*the structure for capabilities*/ +typedef struct +{ + /*cap code cf below*/ + u16 CapCode; + union { + u32 valueInt; + Float valueFloat; + } cap; +} GF_CodecCapability; + + +/* + all codecs capabilities +*/ + +enum +{ + /*size of a single composition unit */ + GF_CODEC_OUTPUT_SIZE = 0x01, + /*resilency: if packets are lost within an AU, resilience means the AU won't be discarded and the codec + will try to decode */ + GF_CODEC_RESILIENT, + /*critical level of composition memory - if below, media management for the object */ + GF_CODEC_BUFFER_MIN, + /*maximum size in CU of composition memory */ + GF_CODEC_BUFFER_MAX, + /*flags that all AUs should be discarded till next RAP (needed in case RAPs are not carried by the transport + protocol */ + GF_CODEC_WAIT_RAP, + /*number of padding bytes needed - if the decoder needs padding input cannot be pulled and data is duplicated*/ + GF_CODEC_PADDING_BYTES, + /*codecs can be threaded at will - by default a single thread is used for all decoders and priority is handled + by the app, but a codec can configure itself to run in a dedicated thread*/ + GF_CODEC_WANTS_THREAD, + + /*video width and height and horizontal pitch (in YV12 we assume half Y pitch for U and V planes) */ + GF_CODEC_WIDTH, + GF_CODEC_HEIGHT, + GF_CODEC_STRIDE, + GF_CODEC_FPS, + /*Pixel Aspect Ratio, expressed as (par.num<<16) | par.den*/ + GF_CODEC_PAR, + /*video color mode - color modes are defined in constants.h*/ + GF_CODEC_PIXEL_FORMAT, + /*isgnal decoder performs frame re-ordering in temporal scalability*/ + GF_CODEC_REORDER, + + /*Audio sample rate*/ + GF_CODEC_SAMPLERATE, + /*Audio num channels*/ + GF_CODEC_NB_CHAN, + /*Audio bps*/ + GF_CODEC_BITS_PER_SAMPLE, + /*audio frame format*/ + GF_CODEC_CHANNEL_CONFIG, + /*this is only used for audio in case transport mapping relies on sampleRate (RTP) + gets the CU duration in samplerate unit (type: int) */ + GF_CODEC_CU_DURATION, + + /*This is only called on scene decoders to signal that potential overlay scene should be + showed (cap.valueINT=1) or hidden (cap.valueINT=0). Currently only used with SetCap*/ + GF_CODEC_SHOW_SCENE, + /*This is only called on scene decoders, GetCap only. If the decoder may continue modifying the scene once the last AU is received, + it must set cap.valueINT to 1 (typically, text stream decoder will hold the scene for a given duration + after the last AU). Otherwise the decoder will be stoped and ask to remove any extra scene being displayed*/ + GF_CODEC_MEDIA_NOT_OVER, +}; + + + /* Generic interface used by both media decoders and scene decoders +@AttachStream: + Add a Stream to the codec. If DependsOnESID is NULL, the stream is a base layer + UpStream means that the decoder should send feedback on this channel. + WARNING: Feedback format is not standardized by MPEG + the same API is used for both encoder and decoder (decSpecInfo is ignored + for an encoder) +@DetachStream: + Remove stream +@GetCapabilities: + Get the desired capability given its code +@SetCapabilities + Set the desired capability given its code if possible + if the codec does not support the request capability, return GF_NOT_SUPPORTED +@CanHandleStream + Can module handle this codec? Return 0 if No and !0 otherwise + decoderSpecificInfo is provided for MPEG4 audio/visual where a bunch of codecs are defined + with same objectType +@GetDecoderName + returns codec name - only called once the stream is successfully attached +@privateStack + user defined. +*/ + +#define GF_CODEC_BASE_INTERFACE(IFCE_NAME) \ + GF_DECL_MODULE_INTERFACE \ + GF_Err (*AttachStream)(IFCE_NAME, GF_ESD *esd);\ + GF_Err (*DetachStream)(IFCE_NAME, u16 ES_ID);\ + GF_Err (*GetCapabilities)(IFCE_NAME, GF_CodecCapability *capability);\ + GF_Err (*SetCapabilities)(IFCE_NAME, GF_CodecCapability capability);\ + Bool (*CanHandleStream)(IFCE_NAME, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL);\ + const char *(*GetName)(IFCE_NAME);\ + void *privateStack; \ + + +typedef struct _basedecoder +{ + GF_CODEC_BASE_INTERFACE(struct _basedecoder *) +} GF_BaseDecoder; + +/*interface name and version for media decoder */ +#define GF_MEDIA_DECODER_INTERFACE GF_4CC('G', 'M', 'D', 0x02) + +/*the media module interface. A media module MUST be implemented in synchronous mode as time +and resources management is done by the terminal*/ +typedef struct _mediadecoder +{ + GF_CODEC_BASE_INTERFACE(struct _basedecoder *) + + /*Process the media data in inAU. + @inBuffer, inBufferLength: encoded input data (complete framing of encoded data) + @ES_ID: stream this data belongs too (scalable object) + @outBuffer, outBufferLength: allocated data for decoding - if outBufferLength is not enough + you must set the size in outBufferLength and GF_BUFFER_TOO_SMALL + + @PaddingBits is the padding at the end of the buffer (some codecs need this info) + @mmlevel: speed indicator for the decoding - cf above for values*/ + GF_Err (*ProcessData)(struct _mediadecoder *, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel); +} GF_MediaDecoder; + + + +typedef struct _inline_scene *LPINLINESCENE; + +/*interface name and version for scene decoder */ +#define GF_SCENE_DECODER_INTERFACE GF_4CC('G', 'S', 'D', 0x02) + +typedef struct _scenedecoder +{ + GF_CODEC_BASE_INTERFACE(struct _basedecoder *) + + /*attaches scene to the decoder - a scene may be attached to several decoders of several types + (BIFS or others scene dec, ressource decoders (OD), etc. + is: inline scene owning graph (and not just graph), defined in intern/terminal_dev.h. With inline scene + the complete terminal is exposed so there's pretty much everything doable in a scene decoder + @is_scene_root: set to true if this decoder is the root of the scene, false otherwise (either another decoder + or a re-entrant call, cf below) + This is called once upon creation of the decoder (several times if re-entrant) + */ + GF_Err (*AttachScene)(struct _scenedecoder *, LPINLINESCENE is, Bool is_scene_root); + /*releases scene. If the decoder manages nodes / resources in the scene, + THESE MUST BE DESTROYED. May be NULL if decoder doesn't manage nodes but only create them (like BIFS, OD) and + doesn't have to be instructed the scene is about to be resumed + This is called each time the scene is about to be reseted (eg, seek and destroy) + */ + GF_Err (*ReleaseScene)(struct _scenedecoder *); + /*Process the scene data in inAU. + @inBuffer, inBufferLength: encoded input data (complete framing of encoded data) + @ES_ID: stream this data belongs too (scalable object) + @AU_Time: specifies the current AU time. This is usually unused, however is needed for decoder + handling the scene graph without input data (cf below). In this case the buffer passed is always NULL and the AU + time caries the time of the scene (or of the stream object attached to the scene decoder, cf below) + @mmlevel: speed indicator for the decoding - cf above for values*/ + GF_Err (*ProcessData)(struct _scenedecoder *, char *inBuffer, u32 inBufferLength, + u16 ES_ID, u32 AU_Time, u32 mmlevel); + +} GF_SceneDecoder; + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_MODULE_CODEC_H_*/ + diff --git a/include/gpac/modules/font.h b/include/gpac/modules/font.h new file mode 100644 index 0000000..e466f11 --- /dev/null +++ b/include/gpac/modules/font.h @@ -0,0 +1,126 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / modules interfaces + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_MODULE_FONT_H_ +#define _GF_MODULE_FONT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/path2d.h> +#include <gpac/module.h> +#include <gpac/user.h> + + +typedef struct _gf_glyph +{ + /*glyphs are stored as linked lists*/ + struct _gf_glyph *next; + /*glyph ID as used in *_get_glyphs - this may not match the UTF name*/ + u32 ID; + /*UTF-name of the glyph if any*/ + u32 utf_name; + GF_Path *path; + /*width of the glyph - !! this can be more than the horizontal advance !! */ + u32 width; + /*glyph horizontal advance in font EM size*/ + s32 horiz_advance; + /*height of the glyph - !! this can be more than the vertical advance !! */ + u32 height; + /*glyph vertical advance in font EM size*/ + s32 vert_advance; +} GF_Glyph; + +enum +{ + /*font styles*/ + GF_FONT_ITALIC = 1, + GF_FONT_OBLIQUE = 1<<1, + /*font variant (smallcaps)*/ + GF_FONT_SMALLCAPS = 1<<2, + /*font decoration*/ + GF_FONT_UNDERLINED = 1<<3, + GF_FONT_STRIKEOUT = 1<<4, + + /*all font weight modification are placed AFTER 1<<9*/ + GF_FONT_WEIGHT_100 = 1<<10, + GF_FONT_WEIGHT_LIGHTER = 1<<11, + GF_FONT_WEIGHT_200 = 1<<12, + GF_FONT_WEIGHT_300 = 1<<13, + GF_FONT_WEIGHT_400 = 1<<14, + GF_FONT_WEIGHT_NORMAL = 1<<15, + GF_FONT_WEIGHT_500 = 1<<16, + GF_FONT_WEIGHT_600 = 1<<17, + GF_FONT_WEIGHT_700 = 1<<18, + GF_FONT_WEIGHT_BOLD = 1<<19, + GF_FONT_WEIGHT_800 = 1<<20, + GF_FONT_WEIGHT_900 = 1<<21, + GF_FONT_WEIGHT_BOLDER = 1<<22 +}; +/*mask for font styles used for CSS2 selection*/ +#define GF_FONT_STYLE_MASK 0x00000007 +/*mask for all font weight*/ +#define GF_FONT_WEIGHT_MASK 0xFFFFFC00 + +/*interface name and version for font engine*/ +#define GF_FONT_READER_INTERFACE GF_4CC('G','F','T', 0x01) + + +typedef struct _font_reader +{ + /* interface declaration*/ + GF_DECL_MODULE_INTERFACE + + /*inits font engine.*/ + GF_Err (*init_font_engine)(struct _font_reader *dr); + /*shutdown font engine*/ + GF_Err (*shutdown_font_engine)(struct _font_reader *dr); + + /*set active font . @styles indicates font styles (PLAIN, BOLD, ITALIC, + BOLDITALIC and UNDERLINED, STRIKEOUT)*/ + GF_Err (*set_font)(struct _font_reader *dr, const char *fontName, u32 styles); + /*gets font info*/ + GF_Err (*get_font_info)(struct _font_reader *dr, char **font_name, s32 *em_size, s32 *ascent, s32 *descent, s32 *underline, s32 *line_spacing, s32 *max_advance_h, s32 *max_advance_v); + + /*translate string to glyph sequence*/ + GF_Err (*get_glyphs)(struct _font_reader *dr, const char *utf_string, u32 *glyph_id_buffer, u32 *io_glyph_id_buffer_size, const char *xml_lang, Bool *rev_layout); + + /*loads glyph by name - returns NULL if glyph cannot be found*/ + GF_Glyph *(*load_glyph)(struct _font_reader *dr, u32 glyph_name); + +/*module private*/ + void *udta; +} GF_FontReader; + + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MODULE_FONT_H_*/ + diff --git a/include/gpac/modules/ipmp.h b/include/gpac/modules/ipmp.h new file mode 100644 index 0000000..0e0930d --- /dev/null +++ b/include/gpac/modules/ipmp.h @@ -0,0 +1,131 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / modules interfaces + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_MODULE_IPMP_H_ +#define _GF_MODULE_IPMP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/module.h> + +/* + NOTE ON IPMP TOOLS + + The current implementation is very basic and does not follow MPEG-4 IPMPX architecture + This is just a place holder for ISMA/OMA-like schemes + Currently all operations are synchronous... +*/ + +enum +{ + /*push some configuration data to the IPMP tool*/ + GF_IPMP_TOOL_SETUP, + /*request access to the object (eg, PLAY)*/ + GF_IPMP_TOOL_GRANT_ACCESS, + /*release access to the object (eg, STOP)*/ + GF_IPMP_TOOL_RELEASE_ACCESS, + /*push some configuration data to the IPMP tool*/ + GF_IPMP_TOOL_PROCESS_DATA, +}; + +typedef struct +{ + u32 scheme_version; + u32 scheme_type; + const char *scheme_uri; + const char *kms_uri; +} GF_ISMACrypConfig; + +typedef struct +{ + u32 scheme_version; + u32 scheme_type; + const char *scheme_uri; + const char *kms_uri; + /*SHA-1 hash*/ + u8 hash[20]; + + const char *contentID; + u32 oma_drm_crypt_type; + Bool oma_drm_use_pad, oma_drm_use_hdr; + const char *oma_drm_textual_headers; + u32 oma_drm_textual_headers_len; +} GF_OMADRM2Config; + +/*IPMP events*/ +typedef struct +{ + /*event type*/ + u32 event_type; + + /*gpac's channel triggering this event, NULL if unknown/unspecified*/ + struct _es_channel *channel; + + /*identifier of the config data (GF_IPMP_TOOL_SETUP)*/ + u32 config_data_code; + /*config data (GF_IPMP_TOOL_SETUP). Type depends on the config_data_code*/ + void *config_data; + + Bool restart_requested; + + /*data manipulation (GF_IPMP_TOOL_PROCESS_DATA) - data is always processed in-place in a + synchronous way*/ + char *data; + u32 data_size; + u32 out_data_size; + /*indicates if payload passed is encrypted or not - this is used by ISMA, OMA and 3GP*/ + Bool is_encrypted; + /*ISMA payload resync indicator*/ + u64 isma_BSO; +} GF_IPMPEvent; + +/*interface name and version for IPMP tools*/ +#define GF_IPMP_TOOL_INTERFACE GF_4CC('G','I','P', 0x01) + +typedef struct _ipmp_tool GF_IPMPTool; + +struct _ipmp_tool +{ + /* interface declaration*/ + GF_DECL_MODULE_INTERFACE + + /*process an ipmp event*/ + GF_Err (*process)(GF_IPMPTool *dr, GF_IPMPEvent *evt); + /*tool private*/ + void *udta; + +}; + + +#ifdef __cplusplus +} +#endif + + +#endif /*#define _GF_MODULE_IPMP_H_ +*/ + diff --git a/include/gpac/modules/js_usr.h b/include/gpac/modules/js_usr.h new file mode 100644 index 0000000..36390c9 --- /dev/null +++ b/include/gpac/modules/js_usr.h @@ -0,0 +1,66 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / modules interfaces + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_MODULE_JS_USR_H_ +#define _GF_MODULE_JS_USR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/module.h> +#include <gpac/scenegraph.h> + +/*interface name and version for JavaScript User Extensions*/ +#define GF_JS_USER_EXT_INTERFACE GF_4CC('G','J','S', 0x01) + +typedef struct _js_usr_ext GF_JSUserExtension; + +struct _js_usr_ext +{ + /* interface declaration*/ + GF_DECL_MODULE_INTERFACE + + /*load JS extension + doc: scene graph in which the extension is loaded + jsctx: JavaScript context in which the extension is loaded. + For BIFS/VRML/X3D, one context is created per script node + For other graphs, one context is created per scene/document + global: JavaScript global object for the context + unload: if true, the extension should be unloaded from the JavaScript context (called upon destroy). Otherwise it should be loaded + */ + void (*load)(GF_JSUserExtension *jsext, GF_SceneGraph *doc, struct JSContext *jsctx, struct JSObject *global, Bool unload); + /*module private*/ + void *udta; +}; + + +#ifdef __cplusplus +} +#endif + + +#endif /*#define _GF_MODULE_JS_USR_H_*/ + diff --git a/include/gpac/modules/raster2d.h b/include/gpac/modules/raster2d.h new file mode 100644 index 0000000..088d063 --- /dev/null +++ b/include/gpac/modules/raster2d.h @@ -0,0 +1,258 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / modules interfaces + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_MODULE_RASTER2D_H_ +#define _GF_MODULE_RASTER2D_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/path2d.h> +#include <gpac/module.h> +#include <gpac/color.h> + + +/*stencil types*/ +typedef enum +{ + /*solid color stencil*/ + GF_STENCIL_SOLID = 0, + /*linear color gradient stencil*/ + GF_STENCIL_LINEAR_GRADIENT, + /*radial color gradient stencil*/ + GF_STENCIL_RADIAL_GRADIENT, + /*texture stencil*/ + GF_STENCIL_VERTEX_GRADIENT, + /*texture stencil*/ + GF_STENCIL_TEXTURE, +} GF_StencilType; + + +/*gradient filling modes*/ +typedef enum +{ + /*edge colors are repeated until path is filled*/ + GF_GRADIENT_MODE_PAD, + /*pattern is inversed each time it's repeated*/ + GF_GRADIENT_MODE_SPREAD, + /*pattern is repeated to fill path*/ + GF_GRADIENT_MODE_REPEAT +} GF_GradientMode; + + +/*texture tiling flags*/ +typedef enum +{ + /*texture is repeated in its horizontal direction*/ + GF_TEXTURE_REPEAT_S = (1<<1), + /*texture is repeated in its horizontal direction*/ + GF_TEXTURE_REPEAT_T = (1<<2), + /*texture is fliped vertically*/ + GF_TEXTURE_FLIP = (1<<3), +} GF_TextureTiling; + +/*filter levels for texturing - up to the graphics engine but the following levels are used by +the client*/ +typedef enum +{ + /*high speed mapping (ex, no filtering applied)*/ + GF_TEXTURE_FILTER_HIGH_SPEED, + /*compromise between speed and quality (ex, filter to nearest pixel)*/ + GF_TEXTURE_FILTER_MID, + /*high quality mapping (ex, bi-linear/bi-cubic interpolation)*/ + GF_TEXTURE_FILTER_HIGH_QUALITY +} GF_TextureFilter; + +/* rasterizer antialiasing depending on the graphics engine*/ +typedef enum +{ + /*raster should use fastest mode possible (eg, no antialiasing)*/ + GF_RASTER_HIGH_SPEED, + /*raster should use fast mode and good quality if possible*/ + GF_RASTER_MID, + /*raster should use antialiasing*/ + GF_RASTER_HIGH_QUALITY +} GF_RasterLevel; + + +/*user routines for raserizer. common syntaxes: + @cbk: user defined callback + @x, y: first pixel position of the run, in device memory (top-left) coordinates + @run_h_len: number of pixels to fill on line + @color: color to fill pixel with. USER MUST IGNORE THE ALPHA COMPONENT OF THIS COLOR, the final + alpha is computed by the lib + @alpha: blending amount (0->0xFF) for the pixels +*/ +typedef struct +{ + void *cbk; + /*fills line pixels without any blending operation*/ + void (*fill_run_no_alpha)(void *cbk, u32 x, u32 y, u32 run_h_len, GF_Color color); + /*fills line pixels without blending operation - alpha combines both fill color and anti-aliasing blending*/ + void (*fill_run_alpha)(void *cbk, u32 x, u32 y, u32 run_h_len, GF_Color color, u32 alpha); +} GF_RasterCallback; + + + +/*opaque handler for all stencils*/ +typedef void *GF_STENCIL; + +/*visual surface handler*/ +typedef void *GF_SURFACE; + +/*interface name and version for raster2D*/ +#define GF_RASTER_2D_INTERFACE GF_4CC('G','R','2', 0x02) + +/*graphics driver*/ +typedef struct _raster2d_interface +{ + /* interface declaration*/ + GF_DECL_MODULE_INTERFACE + + GF_STENCIL (*stencil_new) (struct _raster2d_interface *, GF_StencilType type); + /*common destructor for all stencils*/ + void (*stencil_delete) (GF_STENCIL _this); + /*set stencil transformation matrix*/ + GF_Err (*stencil_set_matrix) (GF_STENCIL _this, GF_Matrix2D *mat); + /*solid brush - set brush color*/ + GF_Err (*stencil_set_brush_color) (GF_STENCIL _this, GF_Color c); + /*gradient brushes*/ + /*sets gradient repeat mode - return GF_NOT_SUPPORTED if driver doesn't support this to let the app compute repeat patterns + this may be called before the gradient is setup*/ + GF_Err (*stencil_set_gradient_mode) (GF_STENCIL _this, GF_GradientMode mode); + /*set linear gradient. line is defined by start and end, and you can give interpolation colors at specified positions*/ + GF_Err (*stencil_set_linear_gradient) (GF_STENCIL _this, Fixed start_x, Fixed start_y, Fixed end_x, Fixed end_y); + /*radial gradient brush center point, focal point and radius - colors can only be set through set_interpolation */ + GF_Err (*stencil_set_radial_gradient) (GF_STENCIL _this, Fixed cx, Fixed cy, Fixed fx, Fixed fy, Fixed x_radius, Fixed y_radius); + /*radial and linear gradient (not used with vertex) - set color interpolation at given points, + @pos[i]: distance from (center for radial, start for linear) expressed between 0 and 1 (1 being the gradient bounds) + @col[i]: associated color + NOTE 1: the colors at 0 and 1.0 MUST be provided + NOTE 2: colors shall be fed in order from 0 to 1 + NOTE 3: this overrides the colors provided for linear gradient + */ + GF_Err (*stencil_set_gradient_interpolation) (GF_STENCIL _this, Fixed *pos, GF_Color *col, u32 count); + + /*vertex gradient : set limit path */ + GF_Err (*stencil_set_vertex_path) (GF_STENCIL _this, GF_Path *path); + /*set the center of the gradient*/ + GF_Err (*stencil_set_vertex_center) (GF_STENCIL _this, Fixed cx, Fixed cy, u32 color); + /*set the center of the gradient*/ + GF_Err (*stencil_set_vertex_colors) (GF_STENCIL _this, u32 *colors, u32 nbCol); + + /*sets global alpha blending level for stencil (texture and gradients) + the alpha channel shall be combined with the color matrix if any*/ + GF_Err (*stencil_set_alpha) (GF_STENCIL _this, u8 alpha); + + /*set stencil texture + @pixels: texture data, from top to bottom + @width, @height: texture size + @stride: texture horizontal pitch (bytes to skip to get to next row) + @pixelFormat: texture pixel format as defined in file constants.h + @destination_format_hint: this is the current pixel format of the destination surface, and is given + as a hint in case the texture needs to be converted by the stencil + @no_copy: if set, specifies the texture data shall not be cached by the module (eg it must be able + to directly modify the given memory + NOTE: this stencil acts as a data wrapper, the pixel data is not required to be locally copied + data is not required to be available for texturing until the stencil is used in a draw operation + */ + GF_Err (*stencil_set_texture) (GF_STENCIL _this, char *pixels, u32 width, u32 height, u32 stride, GF_PixelFormat pixelFormat, GF_PixelFormat destination_format_hint, Bool no_copy); + /*creates internal texture - pixel data is owned by texture brush - set to NULL if not supported - this is used to + cope with engines that don't support random strides (ex: Gdiplus needs stride to be a multiple of 4) + if not set the compositor will create its own mem texture and pass it through set_texture - pixel format shall + be respected as far as Alpha is concerned (eg alpha info shall be kept and used in blit) */ + GF_Err (*stencil_create_texture) (GF_STENCIL _this, u32 width, u32 height, GF_PixelFormat pixelFormat); + /*signals the texture has been modified (internal texture only)*/ + void (*stencil_texture_modified) (GF_STENCIL _this); + + /*sets texture tile mode*/ + GF_Err (*stencil_set_tiling) (GF_STENCIL _this, GF_TextureTiling mode); + /*sets texture filtering mode*/ + GF_Err (*stencil_set_filter) (GF_STENCIL _this, GF_TextureFilter filter_mode); + /*set stencil color matrix - texture stencils only. If matrix is NULL, resets current color matrix*/ + GF_Err (*stencil_set_color_matrix) (GF_STENCIL _this, GF_ColorMatrix *cmat); + + /*creates surface object*/ + /* @center_coords: true indicates mathematical-like coord system, + false indicates computer-like coord system */ + GF_SURFACE (*surface_new) (struct _raster2d_interface *, Bool center_coords); + /* delete surface object */ + void (*surface_delete) (GF_SURFACE _this); + + /* attach surface object to device object (Win32: HDC) width and height are target surface size*/ + GF_Err (*surface_attach_to_device) (GF_SURFACE _this, void *os_handle, u32 width, u32 height); + /* attach surface object to stencil object*/ + GF_Err (*surface_attach_to_texture) (GF_SURFACE _this, GF_STENCIL sten); + /* attach surface object to memory buffer if supported + @pixels: texture data + @width, @height: texture size + @stride: texture horizontal pitch (bytes to skip to get to next row) + @pixelFormat: texture pixel format + */ + GF_Err (*surface_attach_to_buffer) (GF_SURFACE _this, char *pixels, u32 width, u32 height, u32 stride, GF_PixelFormat pixelFormat); + + GF_Err (*surface_attach_to_callbacks) (GF_SURFACE _this, GF_RasterCallback *callbacks, u32 width, u32 height); + + /* detach surface object */ + void (*surface_detach) (GF_SURFACE _this); + + /*sets rasterizer precision */ + GF_Err (*surface_set_raster_level) (GF_SURFACE _this, GF_RasterLevel RasterSetting); + /* set the given matrix as the current transformations for all drawn paths + if NULL reset the current transformation */ + GF_Err (*surface_set_matrix) (GF_SURFACE _this, GF_Matrix2D *mat); + /* set the given rectangle as a clipper - nothing will be drawn outside this clipper + if the clipper is NULL then no clipper is set + NB: the clipper is not affected by the surface matrix and is given in pixels + CF ABOVE NOTE ON CLIPPERS*/ + GF_Err (*surface_set_clipper) (GF_SURFACE _this, GF_IRect *rc); + + /*sets the given path as the current one for drawing - the surface transform is NEVER changed between + setting the path and filling, only the clipper may change*/ + GF_Err (*surface_set_path) (GF_SURFACE _this, GF_Path *path); + /*fills the current path using the given stencil - can be called several times with the same current path*/ + GF_Err (*surface_fill) (GF_SURFACE _this, GF_STENCIL stencil); + + /*flushes to surface*/ + GF_Err (*surface_flush) (GF_SURFACE _this); + + /*clears given pixel rect on the surface with the given color - REQUIRED + the given rect is formatted as a clipper - CF ABOVE NOTE ON CLIPPERS*/ + GF_Err (*surface_clear)(GF_SURFACE _this, GF_IRect *rc, GF_Color col); + +/*private:*/ + void *internal; +} GF_Raster2D; + + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MODULE_RASTER2D_H_*/ + diff --git a/include/gpac/modules/service.h b/include/gpac/modules/service.h new file mode 100644 index 0000000..6c9c346 --- /dev/null +++ b/include/gpac/modules/service.h @@ -0,0 +1,467 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / modules interfaces + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_SERVICE_H_ +#define _GF_SERVICE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*for SL, ESD and OD*/ +#include <gpac/mpeg4_odf.h> +#include <gpac/download.h> + +/*handle to service*/ +typedef struct _net_service GF_ClientService; + +/*handle to channel*/ +typedef void *LPNETCHANNEL; + +enum +{ + /*channel control, app->module. Note that most modules don't need to handle pause/resume/set_speed*/ + GF_NET_CHAN_PLAY, + GF_NET_CHAN_STOP, + GF_NET_CHAN_PAUSE, + GF_NET_CHAN_RESUME, + GF_NET_CHAN_SET_SPEED, + /*channel configuration, app->module*/ + GF_NET_CHAN_CONFIG, + /*channel duration, app<->module (in case duration is not known at setup)*/ + GF_NET_CHAN_DURATION, + /*channel buffer, app->module*/ + GF_NET_CHAN_BUFFER, + /*channel buffer query, app<-module*/ + GF_NET_CHAN_BUFFER_QUERY, + /*retrieves DSI from channel (DSI may be caried by net with a != value than OD), app->module*/ + GF_NET_CHAN_GET_DSI, + /*set media padding for all AUs fetched (pull mode only). + If not supported the channel will have to run in push mode. app->module*/ + GF_NET_CHAN_SET_PADDING, + /*sets input channel to pull mode if possible, app->module*/ + GF_NET_CHAN_SET_PULL, + /*query channel capability to pause/resume and seek(play from an arbitrary range) + a non-interactive channel doesn't have to handle SET_SPEED, PAUSE and RESUME commands but can + still work in pull mode*/ + GF_NET_CHAN_INTERACTIVE, + /*map net time (OTB) to media time (up only) - this is needed by some signaling protocols when the + real play range is not the requested one */ + GF_NET_CHAN_MAP_TIME, + /*reconfiguration of channel comming from network (up only) - this is used to override the SL config + if it differs from the one specified at config*/ + GF_NET_CHAN_RECONFIG, + /*signal channel is ISMACryp'ted (net->term only)*/ + GF_NET_CHAN_DRM_CFG, + + /*retrieves ESD for channel - net->term only, for cache configuration*/ + GF_NET_CHAN_GET_ESD, + /*retrieves visual PAR as indicated in container if any*/ + GF_NET_CHAN_GET_PIXEL_AR, + + /*service buffer query (for all channels running in service), app<-module*/ + GF_NET_BUFFER_QUERY, + /*retrieves network stats for service/channel; app->module*/ + GF_NET_GET_STATS, + /*retrieves whether service can be cached (rtp, http streaming radios, etc) or not. No associated struct*/ + GF_NET_IS_CACHABLE, + + /*sets info for service - net->term only*/ + GF_NET_SERVICE_INFO, + /*checks if there is an audio stream in the service - term->net only*/ + GF_NET_SERVICE_HAS_AUDIO, +}; + +/*channel command for all commands that don't need params: +GF_NET_CHAN_SET_PULL: module shall return GF_OK or GF_NOT_SUPPORTED +GF_NET_CHAN_INTERACTIVE: module shall return GF_OK or GF_NOT_SUPPORTED +*/ +typedef struct +{ + /*command type*/ + u32 command_type; + /*channel*/ + LPNETCHANNEL on_channel; +} GF_NetComBase; + +/*GF_NET_CHAN_PLAY, GF_NET_CHAN_SET_SPEED*/ +typedef struct +{ + u32 command_type; + LPNETCHANNEL on_channel; + /*params for GF_NET_CHAN_PLAY, ranges in sec - if range is <0, then it is ignored (eg [2, -1] with speed>0 means 2 +oo) */ + Double start_range, end_range; + /*params for GF_NET_CHAN_PLAY and GF_NET_CHAN_SPEED*/ + Double speed; +} GF_NetComPlay; + + +/*GF_NET_CHAN_CONFIG, GF_NET_CHAN_RECONFIG +channel config may happen as soon as the channel is open, even if the module hasn't acknowledge creation +channel config can also be used from network to app, with GF_NET_CHAN_RECONFIG type - only the SL config is then used +*/ +typedef struct +{ + u32 command_type; + LPNETCHANNEL on_channel; + + /*SL config of the stream as delivered in OD (app->channel) or by network (channel->app)*/ + GF_SLConfig sl_config; + /*stream priority packet drops are more tolerable if low priority - app->channel only*/ + u32 priority; + /*sync ID: all channels with the same sync ID run on the same timeline, thus the module should + try to match this - note this may not be possible (typically RTP/RTSP)*/ + u32 sync_id; + /*audio frame duration and sample rate if any - this is needed by some RTP payload*/ + u32 frame_duration, sample_rate; +} GF_NetComConfig; + +/*GF_NET_CHAN_BUFFER, GF_NET_CHAN_BUFFER_QUERY*/ +typedef struct +{ + u32 command_type; + LPNETCHANNEL on_channel; + /*the recommended buffering limits in ms - this depends on the modules preferences and on the service + type (multicast, vod, ...) - below buffer_min the stream will pause if possible until buffer_max is reached + note the app will fill in default values before querying*/ + u32 min, max; + /*only used with GF_NET_CHAN_BUFFER_QUERY - amount of media in decoding buffer, in ms*/ + u32 occupancy; +} GF_NetComBuffer; + +/*GF_NET_CHAN_DURATION*/ +typedef struct +{ + u32 command_type; + LPNETCHANNEL on_channel; + /*duration in sec*/ + Double duration; +} GF_NetComDuration; + +/*GF_NET_CHAN_GET_DSI*/ +typedef struct +{ + u32 command_type; + LPNETCHANNEL on_channel; + /*carries specific info for codec - data shall be allocated by service and is freed by user*/ + char *dsi; + u32 dsi_len; +} GF_NetComGetDSI; + +/*GF_NET_CHAN_SET_PADDING*/ +typedef struct +{ + u32 command_type; + LPNETCHANNEL on_channel; + u32 padding_bytes; +} GF_NetComPadding; + +/*GF_NET_CHAN_MAP_TIME*/ +typedef struct +{ + u32 command_type; + LPNETCHANNEL on_channel; + /*MediaTime at this timestamp*/ + Double media_time; + /*TS where mapping is done (in SL TS resolution)*/ + u64 timestamp; + /*specifies whether decoder input data shall be discarded or only have its timing updated*/ + Bool reset_buffers; +} GF_NetComMapTime; + +/*GF_NET_CHAN_ISMACRYP_CFG*/ +typedef struct +{ + u32 command_type; + LPNETCHANNEL on_channel; + + + /*per channel, regardless of DRM schemes (ISMA, OMA, )*/ + u32 scheme_version; + u32 scheme_type; + const char *scheme_uri; + const char *kms_uri; + /*OMA DRM info*/ + const char *contentID; + u32 oma_drm_crypt_type; + Bool oma_drm_use_pad, oma_drm_use_hdr; + const char *oma_drm_textual_headers; + u32 oma_drm_textual_headers_len; + + /*SHA-1 file hash*/ + u8 hash[20]; +} GF_NetComDRMConfig; + +/*GF_NET_CHAN_GET_ESD*/ +typedef struct +{ + u32 command_type; + LPNETCHANNEL on_channel; + const GF_ESD *esd; + Bool is_iod_stream; +} GF_NetComGetESD; + +/*GF_NET_GET_STATS +Notes +1: only channels using network must reply. All channels fetching data through a +file downloader (cf below) shall NOT answer, the app manages downloader bandwidth internally. +2: BANDWIDTH USED BY SIGNALING PROTOCOL IS IGNORED IN GPAC +*/ +typedef struct __netstatcom +{ + u32 command_type; + /*MAY BE NULL, in which case the module must fill in ONLY the control channel part. This + is not used yet, but could be with a protocol using a single control socket for N media channels.*/ + LPNETCHANNEL on_channel; + /*percentage of packet loss from network. This cannot be figured out by the app since there is no + one-to-one mapping between the protocol packets and the final SL packet (cf RTP payloads)*/ + Float pck_loss_percentage; + /*channel port, control channel port if any (eg RTCP)*/ + u16 port, ctrl_port; + /*bandwidth used by channel & its control channel if any (both up and down) - expressed in bits per second*/ + u32 bw_up, bw_down, ctrl_bw_down, ctrl_bw_up; + /*set to 0 if channel is not part of a multiplex. Otherwise set to the multiplex port, and + above port info shall be identifiers in the multiplex - note that multiplexing overhead is ignored + in GPAC for the current time*/ + u16 multiplex_port; +} GF_NetComStats; + +/*GF_NET_CHAN_GET_PIXEL_AR*/ +typedef struct +{ + u32 command_type; + LPNETCHANNEL on_channel; + u32 hSpacing, vSpacing; +} GF_NetComPixelAR; + +/*GF_NET_SERVICE_INFO*/ +typedef struct __netinfocom +{ + u32 command_type; + /*currently NULL only*/ + LPNETCHANNEL on_channel; + /*packed trackNumber(16 bits)/totaltrack(16 bits)*/ + u32 track_info; + u32 genre; + const char *album; + const char *artist; + const char *comment; + const char *composer; + const char *name; + const char *writer; +} GF_NetComInfo; + +/*GF_NET_CHAN_GET_PIXEL_AR*/ +typedef struct +{ + u32 command_type; + char *base_url; +} GF_NetComHasAudio; + +typedef union __netcommand +{ + u32 command_type; + GF_NetComBase base; + GF_NetComPlay play; + GF_NetComConfig cfg; + GF_NetComBuffer buffer; + GF_NetComDuration duration; + GF_NetComGetDSI get_dsi; + GF_NetComPadding pad; + GF_NetComMapTime map_time; + GF_NetComStats net_stats; + GF_NetComDRMConfig drm_cfg; + GF_NetComGetESD cache_esd; + GF_NetComInfo info; + GF_NetComPixelAR par; + GF_NetComHasAudio audio; +} GF_NetworkCommand; + +/* + network modules +*/ + +/*interface name and version for input service*/ +#define GF_NET_CLIENT_INTERFACE GF_4CC('G', 'I', 'S', 0x01) + +typedef struct _netinterface +{ + /* interface declaration*/ + GF_DECL_MODULE_INTERFACE + + /*retuns 1 if module can process this URL, 0 otherwise. This is only called when the file extension/mimeType cannot be + retrieved in the cfg file, otherwise the mime type/file ext is used to load service. Typically a module would + register its mime types in this function (cf gf_term_register_mime_type below) + */ + Bool (*CanHandleURL)(struct _netinterface *, const char *url); + + /*connects the service to the desired URL - the service handle is used for callbacks. + Only one service can be connected to a loaded interface. + */ + GF_Err (*ConnectService) (struct _netinterface *, GF_ClientService *serv, const char *url); + + /*disconnects service - the module is no longer used after this call - if immediate_shutdown is set the module + shall not attempt to get confirmation from remote side, it will be deleted right away + + NOTE: depending on how the client/server exchange is happening, it may happen that the CloseService is called + in the same context as a reply from your module. This can result into deadlocks if you're using threads. + You should therefore only try to destroy threads used in the interface shutdown process, which is guarantee + to be in a different context call. + */ + GF_Err (*CloseService) (struct _netinterface *); + + /*retrieves service decsriptor (expressed as an MPEG4 OD/IOD) for accessing this service + descriptor is allocated by plugin and destroyed by user + the IOD shall refer to the service attached to the module + @expect_type is a hint in case the service regenerates an IOD. It indicates whether the entry point expected is + INLINE, BIFS animation stream, video, audio or input sensor. + @sub_url: indicates fetching of an IOD for a given object in the service. + Only used for services handling the optional CanHandleURLInService below + NULL for main service + service extension for sub-service (cf CanHandleURLInService below). For ex, + "rtsp://myserver/file.mp4/ES_ID=3" and "rtsp://myserver/file.mp4/ES_ID=4" + or "file.avi#audio" and "file.avi#video".In this case a partial IOD for the desired object is expected + Note: once a service is acknowledged as connected, this function must be executed synchronously + The service can return NULL for a descriptor: + * if the expected media type is a single media, this means the media couldn't be found + * if the expected media type is a scene, this means the terminalk shall create and manage the scene + */ + GF_Descriptor *(*GetServiceDescriptor) (struct _netinterface *, u32 expect_type, const char *sub_url); + + + /*sends command to the service / channel - cf command structure*/ + GF_Err (*ServiceCommand) (struct _netinterface *, GF_NetworkCommand *com); + + /*data channel setup - url is either + "ES_ID=ID" where ID is the stream ID in this service + or a control string depending on the service/stream. The URL is first used to load a module able to handle it, + so the module has no redirection to handle + */ + GF_Err (*ConnectChannel) (struct _netinterface *, LPNETCHANNEL channel, const char *url, Bool upstream); + /*teardown of data channel*/ + GF_Err (*DisconnectChannel) (struct _netinterface *, LPNETCHANNEL channel); + + /*optional - fetch MPEG4 data from channel - data shall not be duplicated and must be released at ReleaseData + SL info shall be written to provided header - if the data is a real SL packet the flag sl_compressed shall be + set to signal the app this is a full SL pdu (@out_sl_hdr is then ignored) + set to NULL if not supported + */ + GF_Err (*ChannelGetSLP) (struct _netinterface *, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data); + + /*optional - release SLP data allocated on channel by the previous call, if any + set to NULL if not supported*/ + GF_Err (*ChannelReleaseSLP) (struct _netinterface *, LPNETCHANNEL channel); + + /*this is needed for modules to query other modules, the typical case being 2 ESD URLs pointing to the + same media (audio and video streams in an RTSP session). This is always used on loaded modules but + doesn't have to be declared*/ + Bool (*CanHandleURLInService)(struct _netinterface *, const char *url); + +/*private*/ + void *priv; +} GF_InputService; + +/*callback functions - these can be linked with non-LGPL modules*/ +/*message from service - error is set if error*/ +void gf_term_on_message(GF_ClientService *service, GF_Err error, const char *message); +/*to call on service (if channel is NULL) or channel connect completed*/ +void gf_term_on_connect(GF_ClientService *service, LPNETCHANNEL ns, GF_Err response); +/*to call on service (if channel is NULL) or channel disconnect completed*/ +void gf_term_on_disconnect(GF_ClientService *service, LPNETCHANNEL ns, GF_Err response); +/* acknowledgement of service command - service commands handle both services and channels +Most of the time commands are NOT acknowledged, typical acknowledgement are needed for setup and control +with remote servers. +command can also be triggered from the service (QoS, broadcast announcements) +cf above for command usage +*/ +void gf_term_on_command(GF_ClientService *service, GF_NetworkCommand *com, GF_Err response); +/*to call when data packet is received. +@data, data_size: data received +@hdr: uncompressed SL header passed with data for stream sync - if not present then data shall be a valid SL packet + (header + PDU). Note that using an SLConfig resulting in an empty GF_SLHeader allows sending raw data directly +@reception_status: data reception status. To signal end of stream, set this to GF_EOS +*/ +void gf_term_on_sl_packet(GF_ClientService *service, LPNETCHANNEL ns, char *data, u32 data_size, GF_SLHeader *hdr, GF_Err reception_status); +/*returns URL associated with service (so that you don't need to store it)*/ +const char *gf_term_get_service_url(GF_ClientService *service); + +/*adds a new media from network. !! The media descriptor is then owned/destroyed by the term!! +media_desc: object descriptor for the new media. May be NULL to force scene rebuilt. +no_scene_check: specifies if the scene description shall be rebuilt or not. +*/ +void gf_term_add_media(GF_ClientService *service, GF_Descriptor *media_desc, Bool no_scene_update); + + +/*check if @fileExt extension is supported for given mimeType, and if associated with module. If mimeType not registered, register it for given module*/ +Bool gf_term_check_extension(GF_InputService *ifce, const char *mimeType, const char *extList, const char *description, const char *fileExt); +/*register mime types & file extensions - most modules should only need the check version above*/ +void gf_term_register_mime_type(GF_InputService *ifce, const char *mimeType, const char *extList, const char *description); + +GF_InputService *gf_term_get_service_interface(GF_ClientService *service); + +/*file downloading - can and MUST be used by any module (regardless of license) in order not to interfere +with net management*/ +/*creates a new downloading session in the given service - if url is relative, it will be interpreted through +the service URL*/ +GF_DownloadSession * gf_term_download_new(GF_ClientService *service, const char *url, u32 flags, gf_dm_user_io user_io, void *cbk); +/*closes the downloading session*/ +void gf_term_download_del(GF_DownloadSession * dnload); +/*send progress and connection messages to user...*/ +void gf_term_download_update_stats(GF_DownloadSession * sess); + + +/*MPEG-4 media cache interface name*/ +#define GF_STREAMING_MEDIA_CACHE GF_4CC('G', 'M', 'C', 0x01) + +typedef struct _cacheinterface +{ + /* interface declaration*/ + GF_DECL_MODULE_INTERFACE + + /*opens media cache at given place - extension is handled by cache module + @serv: service owning cache (eg, where to send data when requested) + @keep_existing_files: don't overwrite previously recorded sessions*/ + GF_Err (*Open)(struct _cacheinterface *, GF_ClientService *serv, const char *location_and_name, Bool keep_existing_files); + /*closes media cache, delete file(s) if desired*/ + GF_Err (*Close)(struct _cacheinterface *, Bool delete_cache); + /*writes data to cache. data is always a complete AU as reconstructed by gpac core + If first time data is written, user should query channel desc through service commands*/ + GF_Err (*Write)(struct _cacheinterface *, LPNETCHANNEL ch, char *data, u32 data_size, GF_SLHeader *sl_hdr); + + /*same as reader, except they MUST be provided - in other words, only PULL mode is supported for cache + at the current time*/ + GF_Err (*ServiceCommand) (struct _cacheinterface *, GF_NetworkCommand *com); + GF_Err (*ChannelGetSLP) (struct _cacheinterface *, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data); + GF_Err (*ChannelReleaseSLP) (struct _cacheinterface *, LPNETCHANNEL channel); + + /*module private*/ + void *priv; +} GF_StreamingCache; + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_SERVICE_H_*/ diff --git a/include/gpac/modules/term_ext.h b/include/gpac/modules/term_ext.h new file mode 100644 index 0000000..b77f7d3 --- /dev/null +++ b/include/gpac/modules/term_ext.h @@ -0,0 +1,84 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / modules interfaces + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_MODULE_TERM_EXT_H_ +#define _GF_MODULE_TERM_EXT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/terminal.h> + +/*interface name and version for Terminal Extensions services*/ +#define GF_TERM_EXT_INTERFACE GF_4CC('G','T','E', 0x01) + +typedef struct _term_ext GF_TermExt; + + +enum +{ + /*start terminal extension. If 0 is returned, the module will be unloaded*/ + GF_TERM_EXT_START = 1, + /*stop terminal extension*/ + GF_TERM_EXT_STOP, + /*process extension - the GF_TERM_EXT_CAP_NOT_THREADED capability MUST be set*/ + GF_TERM_EXT_PROCESS, +}; + +enum +{ + /*signal the extension is to be called on regular basis (once per simulation tick). This MUST be set during + the GF_TERM_EXT_START command and cannot be changed at run-time*/ + GF_TERM_EXT_CAP_NOT_THREADED = 1<<1, +}; + + +struct _term_ext +{ + /* interface declaration*/ + GF_DECL_MODULE_INTERFACE + + /*caps of the module*/ + u32 caps; + + /*load JS extension + termext: pointer to the module + term: pointer to GPAC terminal + */ + Bool (*process)(GF_TermExt *termext, GF_Terminal *term, u32 action); + + /*module private*/ + void *udta; +}; + + +#ifdef __cplusplus +} +#endif + + +#endif /*#define _GF_MODULE_TERM_EXT_H_*/ + diff --git a/include/gpac/modules/video_out.h b/include/gpac/modules/video_out.h new file mode 100644 index 0000000..4f97110 --- /dev/null +++ b/include/gpac/modules/video_out.h @@ -0,0 +1,188 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / modules interfaces + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + + Note on video driver: this is not a graphics driver, the only thing requested from this driver + is accessing video memory and performing stretch of YUV and RGB on the backbuffer (bitmap node) + the graphics driver is a different entity that performs 2D rasterization + +*/ + +#ifndef _GF_MODULE_VIDEO_OUT_H_ +#define _GF_MODULE_VIDEO_OUT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*include module system*/ +#include <gpac/module.h> +/*include event system*/ +#include <gpac/events.h> +/*include framebuffer definition*/ +#include <gpac/color.h> + +/* + Video hardware output module +*/ + +enum +{ + /*HW supports YUV->backbuffer blitting*/ + GF_VIDEO_HW_HAS_YUV = (1<<1), + /*HW supports keying*/ + GF_VIDEO_HW_HAS_COLOR_KEY = (1<<2), + /*HW supports 90 degres rotation of display in 2D mode (Mobile Phones & PDAs)*/ + GF_VIDEO_HW_CAN_ROTATE = (1<<3), + /*HW supports OpenGL rendering. Whether this is OpenGL or OpenGL-ES depends on compilation settings + and cannot be changed at runtime*/ + GF_VIDEO_HW_OPENGL = (1<<4), + /*HW supports OpenGL offscreen rendering. Whether this is OpenGL or OpenGL-ES depends on compilation settings + and cannot be changed at runtime*/ + GF_VIDEO_HW_OPENGL_OFFSCREEN = (1<<5), + /*HW supports OpenGL offscreen rendering with alpha. Whether this is OpenGL or OpenGL-ES depends on compilation settings + and cannot be changed at runtime*/ + GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA = (1<<6), + /*HW supports YUV overlays*/ + GF_VIDEO_HW_HAS_YUV_OVERLAY = (1<<7), +}; + +/*interface name and version for video output*/ +#define GF_VIDEO_OUTPUT_INTERFACE GF_4CC('G','V','O',0x04) + +/* + video output interface + + the video output may run in 2 modes: 2D and 3D. + + ** the 2D video output works by accessing a backbuffer surface on the video mem board - + the app accesses to the surface through the GF_VideoSurface handler. + The module may support HW blitting of RGB or YUV data to backbuffer. + + ** the 3D video output only handles window management and openGL contexts setup. + The context shall be setup in Resize and SetFullScreen calls which are always happening in the main + rendering thread. This will take care of openGL context issues with multithreading + + By default all modules are required to be setup in 2D. If 3D is needed, a GF_EVENT_VIDEO_SETUP will + be sent with the desired configuration. + + Except Setup and Shutdown functions, all interface functions are called through the main compositor thread + or its user to avoid multithreading issues. Care must still be taken when handling events +*/ +typedef struct _video_out +{ + /* interface declaration*/ + GF_DECL_MODULE_INTERFACE + + /*setup system - if os_handle is NULL the driver shall create the output display (common case) + the other case is currently only used by child windows on win32 and winCE + @init_flags: a list of initialization flags as specified in user.h*/ + GF_Err (*Setup)(struct _video_out *vout, void *os_handle, void *os_display, u32 init_flags); + /*shutdown system */ + void (*Shutdown) (struct _video_out *vout); + + /*flush video: the video shall be presented to screen + the destination area to update is in client display coordinates (0,0) being top-left, (w,h) bottom-right + Note: dest is always NULL in 3D mode (buffer flip only)*/ + GF_Err (*Flush) (struct _video_out *vout, GF_Window *dest); + + GF_Err (*SetFullScreen) (struct _video_out *vout, Bool fs_on, u32 *new_disp_width, u32 *new_disp_height); + + /*window events sent to output: + GF_EVENT_SET_CURSOR: sets cursor + GF_EVENT_SET_CAPTION: sets caption + GF_EVENT_SHOWHIDE: show/hide output window for self-managed output + GF_EVENT_SIZE: inital window resize upon scene load + GF_EVENT_VIDEO_SETUP: all HW related setup: + * for 2D output, this means resizing the backbuffer if needed (depending on HW constraints) + * for 3D output, this means re-setup of OpenGL context (depending on HW constraints). + * This can be a request for an offscreen rendering surface. If supported, this surface SHALL + be readable through glReadPixels. If not supported, just return an error. + Note that GPAC never uses more than one GL context (offscreen or main video) + * Depending on windowing systems and implementations, it could be possible to resize a window + without destroying the GL context. If the GL context is destroyed, the module should send an event + of the same type to the player. + + This function is also called with a NULL event at the begining of each rendering cycle, in order to allow event + handling for modules uncapable of safe multithreading (eg X11) + */ + GF_Err (*ProcessEvent)(struct _video_out *vout, GF_Event *event); + + /*pass events to user (assigned before setup) - return 1 if the event has been processed by GPAC + (eiher scene or navigation), 0 otherwise*/ + void *evt_cbk_hdl; + Bool (*on_event)(void *hdl, GF_Event *event); + + /* + All the following are 2D specific and are NEVER called in 3D mode + */ + /*locks backbuffer video memory + do_lock: specifies whether backbuffer shall be locked or released + */ + GF_Err (*LockBackBuffer)(struct _video_out *vout, GF_VideoSurface *video_info, Bool do_lock); + + /*lock video mem through OS context (only HDC for Win32 at the moment) + do_lock: specifies whether OS context shall be locked or released*/ + void *(*LockOSContext)(struct _video_out *vout, Bool do_lock); + + /*blit surface src to backbuffer - if a window is not specified, the full surface is used + the blitter MUST support stretching and RGB24 sources. Support for YUV is indicated in the hw caps + of the driver. If none is supported, just set this function to NULL and let gpac performs software blitting. + Whenever this function fails, the blit will be performed in software mode + if is_overlay is set, this is an overlay on the video memory (Flush would have been called before) + overlay_type 1: this is regular overlay without color keying + overlay_type 2: this is overlay with color keying + */ + GF_Err (*Blit)(struct _video_out *vout, GF_VideoSurface *video_src, GF_Window *src_wnd, GF_Window *dst_wnd, u32 overlay_type); + + /*set of above HW flags*/ + u32 hw_caps; + /*main pixel format of video board (informative only)*/ + u32 pixel_format; + /*yuv pixel format if HW YUV blitting is supported (informative only) */ + u32 yuv_pixel_format; + /*maximum resolution of the screen*/ + u32 max_screen_width, max_screen_height; + + /*overlay color key used by the hardware bliter - if not set, only top-level overlay can be used*/ + u32 overlay_color_key; +#ifdef ENABLE_JOYSTICK + u32 centered_mode; +#endif + + /*driver private*/ + void *opaque; +} GF_VideoOutput; + + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MODULE_VIDEO_OUT_H_*/ + diff --git a/include/gpac/mpeg4_odf.h b/include/gpac/mpeg4_odf.h new file mode 100644 index 0000000..d98bf71 --- /dev/null +++ b/include/gpac/mpeg4_odf.h @@ -0,0 +1,1697 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 Object Descriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_MPEG4_ODF_H_ +#define _GF_MPEG4_ODF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/list.h> +#include <gpac/bitstream.h> +#include <gpac/sync_layer.h> + +/*************************************** + Descriptors Tag +***************************************/ +enum +{ + GF_ODF_OD_TAG = 0x01, + GF_ODF_IOD_TAG = 0x02, + GF_ODF_ESD_TAG = 0x03, + GF_ODF_DCD_TAG = 0x04, + GF_ODF_DSI_TAG = 0x05, + GF_ODF_SLC_TAG = 0x06, + GF_ODF_CI_TAG = 0x07, + GF_ODF_SCI_TAG = 0x08, + GF_ODF_IPI_PTR_TAG = 0x09, + GF_ODF_IPMP_PTR_TAG = 0x0A, + GF_ODF_IPMP_TAG = 0x0B, + GF_ODF_QOS_TAG = 0x0C, + GF_ODF_REG_TAG = 0x0D, + + /*FILE FORMAT RESERVED IDs - NEVER CREATE / USE THESE DESCRIPTORS*/ + GF_ODF_ESD_INC_TAG = 0x0E, + GF_ODF_ESD_REF_TAG = 0x0F, + GF_ODF_ISOM_IOD_TAG = 0x10, + GF_ODF_ISOM_OD_TAG = 0x11, + GF_ODF_ISOM_IPI_PTR_TAG = 0x12, + /*END FILE FORMAT RESERVED*/ + + GF_ODF_EXT_PL_TAG = 0x13, + GF_ODF_PL_IDX_TAG = 0x14, + + GF_ODF_ISO_BEGIN_TAG = 0x15, + GF_ODF_ISO_END_TAG = 0x3F, + + GF_ODF_CC_TAG = 0x40, + GF_ODF_KW_TAG = 0x41, + GF_ODF_RATING_TAG = 0x42, + GF_ODF_LANG_TAG = 0x43, + GF_ODF_SHORT_TEXT_TAG = 0x44, + GF_ODF_TEXT_TAG = 0x45, + GF_ODF_CC_NAME_TAG = 0x46, + GF_ODF_CC_DATE_TAG = 0x47, + GF_ODF_OCI_NAME_TAG = 0x48, + GF_ODF_OCI_DATE_TAG = 0x49, + GF_ODF_SMPTE_TAG = 0x4A, + + GF_ODF_SEGMENT_TAG = 0x4B, + GF_ODF_MEDIATIME_TAG = 0x4C, + + GF_ODF_IPMP_TL_TAG = 0x60, + GF_ODF_IPMP_TOOL_TAG = 0x61, + + GF_ODF_ISO_RES_BEGIN_TAG = 0x62, + GF_ODF_ISO_RES_END_TAG = 0xBF, + + GF_ODF_USER_BEGIN_TAG = 0xC0, + + /*internal descriptor for mux input description*/ + GF_ODF_MUXINFO_TAG = GF_ODF_USER_BEGIN_TAG, + /*internal descriptor for bifs config input description*/ + GF_ODF_BIFS_CFG_TAG = GF_ODF_USER_BEGIN_TAG + 1, + /*internal descriptor for UI config input description*/ + GF_ODF_UI_CFG_TAG = GF_ODF_USER_BEGIN_TAG + 2, + /*internal descriptor for TextConfig description*/ + GF_ODF_TEXT_CFG_TAG = GF_ODF_USER_BEGIN_TAG + 3, + GF_ODF_TX3G_TAG = GF_ODF_USER_BEGIN_TAG + 4, + GF_ODF_ELEM_MASK_TAG = GF_ODF_USER_BEGIN_TAG + 5, + /*internal descriptor for LASeR config input description*/ + GF_ODF_LASER_CFG_TAG = GF_ODF_USER_BEGIN_TAG + 6, + + GF_ODF_USER_END_TAG = 0xFE, + + GF_ODF_OCI_BEGIN_TAG = 0x40, + GF_ODF_OCI_END_TAG = (GF_ODF_ISO_RES_BEGIN_TAG - 1), + + GF_ODF_EXT_BEGIN_TAG = 0x80, + GF_ODF_EXT_END_TAG = 0xFE, + + + /*descriptor for aucilary video data*/ + GF_ODF_AUX_VIDEO_DATA = GF_ODF_EXT_BEGIN_TAG + 1, +}; + + +/*************************************** + Descriptors +***************************************/ + +#define BASE_DESCRIPTOR \ + u8 tag; + +typedef struct +{ + BASE_DESCRIPTOR +} GF_Descriptor; + + +/* default descriptor. + NOTE: The decoderSpecificInfo is used as a default desc with tag 0x05 */ +typedef struct +{ + BASE_DESCRIPTOR + u32 dataLength; + char *data; +} GF_DefaultDescriptor; + +/*Object Descriptor*/ +typedef struct +{ + BASE_DESCRIPTOR + GF_List *ipmp_tools; +} GF_IPMP_ToolList; + +/*ObjectDescriptor*/ +typedef struct +{ + BASE_DESCRIPTOR + u16 objectDescriptorID; + char *URLString; + GF_List *ESDescriptors; + GF_List *OCIDescriptors; + /*includes BOTH IPMP_DescriptorPointer (IPMP & IPMPX) and GF_IPMP_Descriptor (IPMPX only)*/ + GF_List *IPMP_Descriptors; + GF_List *extensionDescriptors; +} GF_ObjectDescriptor; + +/*GF_InitialObjectDescriptor - WARNING: even though the bitstream IOD is not +a bit extension of OD, internally it is a real overclass of OD +we usually typecast IOD to OD when flags are not needed !!!*/ +typedef struct +{ + BASE_DESCRIPTOR + u16 objectDescriptorID; + char *URLString; + GF_List *ESDescriptors; + GF_List *OCIDescriptors; + /*includes BOTH IPMP_DescriptorPointer (IPMP & IPMPX) and GF_IPMP_Descriptor (IPMPX only)*/ + GF_List *IPMP_Descriptors; + GF_List *extensionDescriptors; + + /*IOD extensions*/ + u8 inlineProfileFlag; + u8 OD_profileAndLevel; + u8 scene_profileAndLevel; + u8 audio_profileAndLevel; + u8 visual_profileAndLevel; + u8 graphics_profileAndLevel; + + GF_IPMP_ToolList *IPMPToolList; +} GF_InitialObjectDescriptor; + +/*File Format Object Descriptor*/ +typedef struct +{ + BASE_DESCRIPTOR + u16 objectDescriptorID; + char *URLString; + GF_List *ES_ID_RefDescriptors; + GF_List *OCIDescriptors; + GF_List *IPMP_Descriptors; + GF_List *extensionDescriptors; + GF_List *ES_ID_IncDescriptors; +} GF_IsomObjectDescriptor; + +/*File Format Initial Object Descriptor - same remark as IOD*/ +typedef struct +{ + BASE_DESCRIPTOR + u16 objectDescriptorID; + char *URLString; + GF_List *ES_ID_RefDescriptors; + GF_List *OCIDescriptors; + GF_List *IPMP_Descriptors; + GF_List *extensionDescriptors; + GF_List *ES_ID_IncDescriptors; + + u8 inlineProfileFlag; + u8 OD_profileAndLevel; + u8 scene_profileAndLevel; + u8 audio_profileAndLevel; + u8 visual_profileAndLevel; + u8 graphics_profileAndLevel; + + GF_IPMP_ToolList *IPMPToolList; +} GF_IsomInitialObjectDescriptor; + + +/*File Format ES Descriptor for IOD*/ +typedef struct { + BASE_DESCRIPTOR + u32 trackID; +} GF_ES_ID_Inc; + +/*File Format ES Descriptor for OD*/ +typedef struct { + BASE_DESCRIPTOR + u16 trackRef; +} GF_ES_ID_Ref; + +/*Decoder config Descriptor*/ +typedef struct +{ + BASE_DESCRIPTOR + u8 objectTypeIndication; + u8 streamType; + u8 upstream; + u32 bufferSizeDB; + u32 maxBitrate; + u32 avgBitrate; + GF_DefaultDescriptor *decoderSpecificInfo; + GF_List *profileLevelIndicationIndexDescriptor; +} GF_DecoderConfig; + + +/*Content Identification Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u8 compatibility; + u8 protectedContent; + u8 contentTypeFlag; + u8 contentIdentifierFlag; + u8 contentType; + u8 contentIdentifierType; + /*international code string*/ + char *contentIdentifier; +} GF_CIDesc; + +/*Supplementary Content Identification Descriptor)*/ +typedef struct { + BASE_DESCRIPTOR + u32 languageCode; + char *supplContentIdentifierTitle; + char *supplContentIdentifierValue; +} GF_SCIDesc; + +/*IPI (Intelectual Property Identification) Descriptor Pointer*/ +typedef struct { + BASE_DESCRIPTOR + u16 IPI_ES_Id; +} GF_IPIPtr; + +/*IPMP Descriptor Pointer*/ +typedef struct { + BASE_DESCRIPTOR + u8 IPMP_DescriptorID; + u16 IPMP_DescriptorIDEx; + u16 IPMP_ES_ID; +} GF_IPMPPtr; + +/*IPMPX control points*/ +enum +{ + /*no control point*/ + IPMP_CP_NONE = 0, + /*control point between DB and decoder*/ + IPMP_CP_DB = 1, + /*control point between decoder and CB*/ + IPMP_CP_CB = 2, + /*control point between CB and render*/ + IPMP_CP_CM = 3, + /*control point in BIFS tree (???)*/ + IPMP_CP_BIFS = 4, + /*the rest is reserved or forbidden(0xFF)*/ +}; + +/*IPMPX base classe*/ +#define GF_IPMPX_BASE \ + u8 tag; \ + u8 version; \ + u32 dataID; \ + +typedef struct +{ + GF_IPMPX_BASE +} GF_GF_IPMPX_Base; + +/*IPMP descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u8 IPMP_DescriptorID; + u16 IPMPS_Type; + /*if IPMPS_Type=0, NULL-terminated URL, else if IPMPS_Type is not IPMPX, opaque data*/ + char *opaque_data; + /*if IPMPS_Type=0, irrelevant (strlen(URL)), else if IPMPS_Type is not IPMPX, opaque data size*/ + u32 opaque_data_size; + + /*IPMPX specific*/ + u16 IPMP_DescriptorIDEx; + bin128 IPMP_ToolID; + u8 control_point; + u8 cp_sequence_code; + GF_List *ipmpx_data; +} GF_IPMP_Descriptor; + + +/*IPMPTool*/ +#define MAX_IPMP_ALT_TOOLS 20 +typedef struct +{ + BASE_DESCRIPTOR + bin128 IPMP_ToolID; + /*if set, this is an alternate tool*/ + u32 num_alternate; + bin128 specificToolID[MAX_IPMP_ALT_TOOLS]; + + struct _tagIPMPXParamDesc *toolParamDesc; + char *tool_url; +} GF_IPMP_Tool; + + +/* Elementary Mask of Bifs Config - parsing only */ +typedef struct { + BASE_DESCRIPTOR + u32 node_id; // referenced nodeID + char *node_name; // referenced node name +} GF_ElementaryMask; + +/*BIFSConfig - parsing only, STORED IN ESD:DCD:DSI*/ +typedef struct __tag_bifs_config +{ + BASE_DESCRIPTOR + u32 version; + u16 nodeIDbits; + u16 routeIDbits; + u16 protoIDbits; + Bool pixelMetrics; + u16 pixelWidth, pixelHeight; + /*BIFS-Anim stuff*/ + Bool randomAccess; + GF_List *elementaryMasks; + /*internal extensions for encoding*/ + Bool useNames; +} GF_BIFSConfig; + +/*flags for style*/ +enum +{ + GF_TXT_STYLE_NORMAL = 0, + GF_TXT_STYLE_BOLD = 1, + GF_TXT_STYLE_ITALIC = 2, + GF_TXT_STYLE_UNDERLINED = 4 +}; + +typedef struct +{ + u16 startCharOffset; + u16 endCharOffset; + u16 fontID; + u8 style_flags; + u8 font_size; + /*ARGB*/ + u32 text_color; +} GF_StyleRecord; + +typedef struct +{ + u16 fontID; + char *fontName; +} GF_FontRecord; + +typedef struct +{ + s16 top, left, bottom, right; +} GF_BoxRecord; + +/*scroll flags*/ +enum +{ + GF_TXT_SCROLL_CREDITS = 0, + GF_TXT_SCROLL_MARQUEE = 1, + GF_TXT_SCROLL_DOWN = 2, + GF_TXT_SCROLL_RIGHT = 3 +}; + +/* display flags*/ +enum +{ + GF_TXT_SCROLL_IN = 0x00000020, + GF_TXT_SCROLL_OUT = 0x00000040, + /*use one of the scroll flags, eg GF_TXT_SCROLL_DIRECTION | GF_TXT_SCROLL_CREDITS*/ + GF_TXT_SCROLL_DIRECTION = 0x00000180, + GF_TXT_KARAOKE = 0x00000800, + GF_TXT_VERTICAL = 0x00020000, + GF_TXT_FILL_REGION = 0x00040000, +}; + +typedef struct +{ + /*this is defined as a descriptor for parsing*/ + BASE_DESCRIPTOR + + u32 displayFlags; + /*left, top: 0 - centered: 1 - bottom, right: -1*/ + s8 horiz_justif, vert_justif; + /*ARGB*/ + u32 back_color; + GF_BoxRecord default_pos; + GF_StyleRecord default_style; + + u32 font_count; + GF_FontRecord *fonts; + + /*unused in isomedia but needed for streamingText*/ + u8 sample_index; +} GF_TextSampleDescriptor; + +typedef struct +{ + BASE_DESCRIPTOR + /*only 0x10 shall be used for 3GP text stream*/ + u8 Base3GPPFormat; + /*only 0x10 shall be used for StreamingText*/ + u8 MPEGExtendedFormat; + /*only 0x10 shall be used for StreamingText (base profile, base level)*/ + u8 profileLevel; + u32 timescale; + /*0 forbidden, 1: out-of-band desc only, 2: in-band desc only, 3: both*/ + u8 sampleDescriptionFlags; + /*More negative layer values are towards the viewer*/ + s16 layer; + /*text track width & height*/ + u16 text_width; + u16 text_height; + /*compatible 3GP formats, same coding as 3GPPBaseFormat*/ + u8 nb_compatible_formats; + u8 compatible_formats[20]; + /*defined in isomedia.h*/ + GF_List *sample_descriptions; + + /*if true info below are valid (cf 3GPP for their meaning)*/ + Bool has_vid_info; + u16 video_width; + u16 video_height; + s16 horiz_offset; + s16 vert_offset; +} GF_TextConfig; + + +/*MuxInfo descriptor - parsing only, stored in ESD:extDescr*/ +typedef struct { + BASE_DESCRIPTOR + /*input location*/ + char *file_name; + /*input groupID for interleaving*/ + u32 GroupID; + /*input stream format (not required, guessed from file_name)*/ + char *streamFormat; + /*time offset in ms from first TS (appends an edit list in mp4)*/ + u32 startTime; + + /*media length to import in ms (from 0)*/ + u32 duration; + + /*SRT/SUB import extensions - only support for text and italic style*/ + char *textNode; + char *fontNode; + + /*video and SUB import*/ + Double frame_rate; + + /*same as importer flags, cf media.h*/ + u32 import_flags; + + /*indicates input file shall be destryed - used during SWF import*/ + Bool delete_file; +} GF_MuxInfo; + +typedef struct +{ + BASE_DESCRIPTOR + /*input type*/ + char *deviceName; + /*string sensor terminaison (validation) char*/ + char termChar; + /*string sensor deletion char*/ + char delChar; + /*device-specific data*/ + char *ui_data; + u32 ui_data_length; +} GF_UIConfig; + +/*LASERConfig - parsing only, STORED IN ESD:DCD:DSI*/ +typedef struct __tag_laser_config +{ + BASE_DESCRIPTOR + u8 profile; + u8 level; + u8 pointsCodec; + u8 pathComponents; + u8 fullRequestHost; + u16 time_resolution; + u8 colorComponentBits; + s8 resolution; + u8 coord_bits; + u8 scale_bits_minus_coord_bits; + u8 newSceneIndicator; + u8 extensionIDBits; + + /*the rest of the structure is never coded, only used for the config of GPAC...*/ + Bool force_string_ids;/*forces all nodes to be defined with string IDs*/ +} GF_LASERConfig; + + +/*************************************** + QoS Tags +***************************************/ +enum +{ + QoSMaxDelayTag = 0x01, + QoSPrefMaxDelayTag = 0x02, + QoSLossProbTag = 0x03, + QoSMaxGapLossTag = 0x04, + QoSMaxAUSizeTag = 0x41, + QoSAvgAUSizeTag = 0x42, + QoSMaxAURateTag = 0x43 +}; + +/*************************************** + QoS Qualifiers +***************************************/ +typedef struct { + BASE_DESCRIPTOR + u8 predefined; + GF_List *QoS_Qualifiers; +} GF_QoS_Descriptor; + + +#define QOS_BASE_QUALIFIER \ + u8 tag; \ + u32 size; + +typedef struct { + QOS_BASE_QUALIFIER +} GF_QoS_Default; + +typedef struct { + QOS_BASE_QUALIFIER + u32 MaxDelay; +} GF_QoS_MaxDelay; + +typedef struct { + QOS_BASE_QUALIFIER + u32 PrefMaxDelay; +} GF_QoS_PrefMaxDelay; + +typedef struct { + QOS_BASE_QUALIFIER + Float LossProb; +} GF_QoS_LossProb; + +typedef struct { + QOS_BASE_QUALIFIER + u32 MaxGapLoss; +} GF_QoS_MaxGapLoss; + +typedef struct { + QOS_BASE_QUALIFIER + u32 MaxAUSize; +} GF_QoS_MaxAUSize; + +typedef struct { + QOS_BASE_QUALIFIER + u32 AvgAUSize; +} GF_QoS_AvgAUSize; + +typedef struct { + QOS_BASE_QUALIFIER + u32 MaxAURate; +} GF_QoS_MaxAURate; + +typedef struct { + QOS_BASE_QUALIFIER + u32 DataLength; /*max size class : 2^28 - 1*/ + char *Data; +} GF_QoS_Private; + + +/*Registration Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 formatIdentifier; + u32 dataLength; + char *additionalIdentificationInfo; +} GF_Registration; + +/*Language Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 langCode; +} GF_Language; + +/*Elementary Stream Descriptor*/ +typedef struct +{ + BASE_DESCRIPTOR + u16 ESID; + u16 OCRESID; + u16 dependsOnESID; + u8 streamPriority; + char *URLString; + GF_DecoderConfig *decoderConfig; + GF_SLConfig *slConfig; + GF_IPIPtr *ipiPtr; + GF_QoS_Descriptor *qos; + GF_Registration *RegDescriptor; + /*0 or 1 lang desc*/ + GF_Language *langDesc; + + GF_List *IPIDataSet; + GF_List *IPMPDescriptorPointers; + GF_List *extensionDescriptors; + +} GF_ESD; + + +/*Auxiliary Video Data Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 aux_video_type; + u32 position_offset_h; + u32 position_offset_v; + u32 knear; + u32 kfar; + u32 parallax_zero; + u32 parallax_scale; + u32 dref; + u32 wref; +} GF_AuxVideoDescriptor; + +/*Content Classification Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 classificationEntity; + u16 classificationTable; + u32 dataLength; + char *contentClassificationData; +} GF_CCDescriptor; + + +/*this structure is used in GF_KeyWord*/ +typedef struct { + char *keyWord; +} GF_KeyWordItem; + +/*Key Word Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 languageCode; + u8 isUTF8; + GF_List *keyWordsList; +} GF_KeyWord; + +/*Rating Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 ratingEntity; + u16 ratingCriteria; + u32 infoLength; + char *ratingInfo; +} GF_Rating; + + +/*Short Textual Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 langCode; + u8 isUTF8; + char *eventName; + char *eventText; +} GF_ShortTextual; + + +/*this structure is used in GF_ExpandedTextual*/ +typedef struct { + char *text; +} GF_ETD_ItemText; + +/*Expanded Textual Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 langCode; + u8 isUTF8; + GF_List *itemDescriptionList; + GF_List *itemTextList; + char *NonItemText; +} GF_ExpandedTextual; + +/*this structure is used in GF_CC_Name*/ +typedef struct { + u32 langCode; + u8 isUTF8; + char *contentCreatorName; +} GF_ContentCreatorInfo; + +/*Content Creator Name GF_Descriptor +NOTE: the desctructor will delete all the items in the list +(GF_ContentCreatorInfo items) */ +typedef struct { + BASE_DESCRIPTOR + GF_List *ContentCreators; +} GF_CC_Name; + +/*Content Creation Date Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + char contentCreationDate[5]; +} GF_CC_Date; + + +/*this structure is used in GF_OCICreators*/ +typedef struct { + u32 langCode; + u8 isUTF8; + char *OCICreatorName; +} GF_OCICreator_item; + +/*OCI Creator Name Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + GF_List *OCICreators; +} GF_OCICreators; + +/*OCI Creation Date Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + char OCICreationDate[5]; +} GF_OCI_Data; + + +/*this structure is used in GF_SMPTECamera*/ +typedef struct { + u8 paramID; + u32 param; +} GF_SmpteParam; + +/*Smpte Camera Position Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u8 cameraID; + GF_List *ParamList; +} GF_SMPTECamera; + + +/*Extension Profile Level Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u8 profileLevelIndicationIndex; + u8 ODProfileLevelIndication; + u8 SceneProfileLevelIndication; + u8 AudioProfileLevelIndication; + u8 VisualProfileLevelIndication; + u8 GraphicsProfileLevelIndication; + u8 MPEGJProfileLevelIndication; +} GF_PLExt; + +/*Profile Level Indication Index Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u8 profileLevelIndicationIndex; +} GF_PL_IDX; + + +/*AVC config descriptor - not a real MPEG-4 descriptor */ +/*used for sequenceParameterSetNALUnit and pictureParameterSetNALUnit*/ +typedef struct +{ + u16 size; + char *data; +} GF_AVCConfigSlot; + +typedef struct +{ + u8 configurationVersion; + u8 AVCProfileIndication; + u8 profile_compatibility; + u8 AVCLevelIndication; + u8 nal_unit_size; + + GF_List *sequenceParameterSets; + GF_List *pictureParameterSets; +} GF_AVCConfig; + + +/************************************************************ + Media Control Extensions +************************************************************/ +typedef struct +{ + BASE_DESCRIPTOR + Double startTime; + Double Duration; + char *SegmentName; +} GF_Segment; + +typedef struct +{ + BASE_DESCRIPTOR + Double mediaTimeStamp; +} GF_MediaTime; + + +/**************************************************************************** + + MPEG-4 SYSTEM - OBJECT DESCRIPTORS COMMANDS DECLARATION + +****************************************************************************/ + + +/*************************************** + Commands Tags +***************************************/ +enum +{ + GF_ODF_OD_UPDATE_TAG = 0x01, + GF_ODF_OD_REMOVE_TAG = 0x02, + GF_ODF_ESD_UPDATE_TAG = 0x03, + GF_ODF_ESD_REMOVE_TAG = 0x04, + GF_ODF_IPMP_UPDATE_TAG = 0x05, + GF_ODF_IPMP_REMOVE_TAG = 0x06, + + /*file format reserved*/ + GF_ODF_ESD_REMOVE_REF_TAG = 0x07, + + GF_ODF_COM_ISO_BEGIN_TAG = 0x0D, + GF_ODF_COM_ISO_END_TAG = 0xBF, + + GF_ODF_COM_USER_BEGIN_TAG = 0xC0, + GF_ODF_COM_USER_END_TAG = 0xFE +}; + +/*************************************** + OD commands +***************************************/ +#define BASE_OD_COMMAND \ + u8 tag; + +/*the (abstract) base command. */ +typedef struct { + BASE_OD_COMMAND +} GF_ODCom; + +/*the default bcommand*/ +typedef struct { + BASE_OD_COMMAND + u32 dataSize; + char *data; +} GF_BaseODCom; + +/*Object Descriptor Update +NB: the list can contain OD or IOD, except internally in the File Format (only MP4_OD)*/ +typedef struct +{ + BASE_OD_COMMAND + GF_List *objectDescriptors; +} GF_ODUpdate; + +/*Object Descriptor Remove*/ +typedef struct +{ + BASE_OD_COMMAND + u32 NbODs; + u16 *OD_ID; +} GF_ODRemove; + +/*Elementary Stream Descriptor Update*/ +typedef struct +{ + BASE_OD_COMMAND + u16 ODID; + GF_List *ESDescriptors; +} GF_ESDUpdate; + +/*Elementary Stream Descriptor Remove*/ +typedef struct { + BASE_OD_COMMAND + u16 ODID; + u32 NbESDs; + u16 *ES_ID; +} GF_ESDRemove; + +/*IPMP Descriptor Update*/ +typedef struct { + BASE_OD_COMMAND + GF_List *IPMPDescList; +} GF_IPMPUpdate; + +/*IPMP Descriptor Remove*/ +typedef struct { + BASE_OD_COMMAND + u32 NbIPMPDs; + /*now this is bad: only IPMPv1 descriptors can be removed at run tim...*/ + u8 *IPMPDescID; +} GF_IPMPRemove; + + + + + + +/******************************************************************** + OD Exports +********************************************************************/ + +/*OD CODEC object - just a simple reader/writer*/ +typedef struct tagODCoDec +{ + GF_BitStream *bs; + GF_List *CommandList; +} GF_ODCodec; + +/*construction / destruction*/ +GF_ODCodec *gf_odf_codec_new(); +void gf_odf_codec_del(GF_ODCodec *codec); +/* add a command to the codec command list. */ +GF_Err gf_odf_codec_add_com(GF_ODCodec *codec, GF_ODCom *command); +/*encode the current coimmand list - once called the commands are destroyed*/ +GF_Err gf_odf_codec_encode(GF_ODCodec *codec, Bool delete_content); +/*get the encoded AU. user is responsible of allocated space*/ +GF_Err gf_odf_codec_get_au(GF_ODCodec *codec, char **outAU, u32 *au_length); +/* set the encoded AU to the codec*/ +GF_Err gf_odf_codec_set_au(GF_ODCodec *codec, char *au, u32 au_length); +/*decode the previously set-up AU*/ +GF_Err gf_odf_codec_decode(GF_ODCodec *codec); +/*get the first OD command in the list. Once called, the command is removed +from the command list. Return NULL when commandList is empty*/ +GF_ODCom *gf_odf_codec_get_com(GF_ODCodec *codec); + +/*apply a command to the codec command list. Command is duplicated if needed +This is used for state maintenance and RAP generation.*/ +GF_Err gf_odf_codec_apply_com(GF_ODCodec *codec, GF_ODCom *command); + +/************************************************************ + GF_ODCom Functions +************************************************************/ + +/*Commands Creation / Destruction*/ +GF_ODCom *gf_odf_com_new(u8 tag); +GF_Err gf_odf_com_del(GF_ODCom **com); + + +/************************************************************ + Descriptors Functions +************************************************************/ + +/*Descriptors Creation / Destruction*/ +GF_Descriptor *gf_odf_desc_new(u8 tag); +void gf_odf_desc_del(GF_Descriptor *desc); + +/*use this function to decode a standalone descriptor +the raw descriptor MUST be formatted with tag and size field!!! +a new desc is created and you must delete it when done*/ +GF_Err gf_odf_desc_read(char *raw_desc, u32 descSize, GF_Descriptor * *outDesc); + +/*use this function to encode a standalone descriptor +the desc will be formatted with tag and size field +the output buffer is allocated and you must delete it when done*/ +GF_Err gf_odf_desc_write(GF_Descriptor *desc, char **outEncDesc, u32 *outSize); + +/*use this function to get the size of a standalone descriptor (including tag and size fields) +return 0 if error*/ +u32 gf_odf_desc_size(GF_Descriptor *desc); + +/*this is usefull to duplicate on the fly a descriptor*/ +GF_Err gf_odf_desc_copy(GF_Descriptor *inDesc, GF_Descriptor **outDesc); + + +/*This functions handles internally what desc can be added to another desc +and adds it. NO DUPLICATION of the descriptor, so +once a desc is added to its parent, destroying the parent WILL DESTROY +this descriptor*/ +GF_Err gf_odf_desc_add_desc(GF_Descriptor *parentDesc, GF_Descriptor *newDesc); + + +/*this is a helper for building a preformatted GF_ESD with decoderConfig, decoderSpecificInfo with no data and +SLConfig descriptor with predefined*/ +GF_ESD *gf_odf_desc_esd_new(u32 sl_predefined); + + +/*Since IPMP V2, we introduce a new set of functions to read / write a list of descriptors +that have no containers (a bit like an OD command, but for descriptors) +This is usefull for IPMPv2 DecoderSpecificInfo which contains a set of IPMP_Declarators +As it could be used for other purposes we keep it generic +you must create the list yourself, the functions just encode/decode from/to the list*/ + +/*uncompress an encoded list of descriptors. You must pass an empty GF_List structure +to know exactly what was in the buffer*/ +GF_Err gf_odf_desc_list_read(char *raw_list, u32 raw_size, GF_List *descList); +/*compress all descriptors in the list into a single buffer. The buffer is allocated +by the lib and must be destroyed by your app +you must pass (outEncList != NULL && *outEncList == NULL)*/ +GF_Err gf_odf_desc_list_write(GF_List *descList, char **outEncList, u32 *outSize); +/*returns size of encoded desc list*/ +GF_Err gf_odf_desc_list_size(GF_List *descList, u32 *outSize); +/*destroy the descriptors in a list but not the list*/ +GF_Err gf_odf_desc_list_del(GF_List *descList); + +/*retuns NULL if unknown, otherwise value*/ +const char *gf_odf_stream_type_name(u32 streamType); +u32 gf_odf_stream_type_by_name(const char *streamType); + +/*special function for authoring - convert DSI to BIFSConfig*/ +GF_BIFSConfig *gf_odf_get_bifs_config(GF_DefaultDescriptor *dsi, u8 oti); +/*special function for authoring - convert DSI to LASERConfig*/ +GF_Err gf_odf_get_laser_config(GF_DefaultDescriptor *dsi, GF_LASERConfig *cfg); +/*sepcial function for authoring - convert DSI to TextConfig*/ +GF_Err gf_odf_get_text_config(GF_DefaultDescriptor *dsi, u8 oti, GF_TextConfig *cfg); +/*special function for authoring - convert DSI to UIConfig*/ +GF_Err gf_odf_get_ui_config(GF_DefaultDescriptor *dsi, GF_UIConfig *cfg); +/*converts UIConfig to dsi - does not destroy input descr but does create output one*/ +GF_Err gf_odf_encode_ui_config(GF_UIConfig *cfg, GF_DefaultDescriptor **out_dsi); + +/*simple constructor/destructor*/ +GF_AVCConfig *gf_odf_avc_cfg_new(); +void gf_odf_avc_cfg_del(GF_AVCConfig *cfg); +/*gets GF_AVCConfig from MPEG-4 DSI*/ +GF_AVCConfig *gf_odf_avc_cfg_read(char *dsi, u32 dsi_size); +/*writes GF_AVCConfig as MPEG-4 DSI*/ +GF_Err gf_odf_avc_cfg_write(GF_AVCConfig *cfg, char **outData, u32 *outSize); + + + +/************************************************************ + QoS Qualifiers Functions +************************************************************/ + +/*QoS Qualifiers Creation / Destruction*/ +GF_QoS_Default *gf_odf_qos_new(u8 tag); +GF_Err gf_odf_qos_del(GF_QoS_Default **qos); + +/*READ/WRITE functions: QoS qualifiers are special descriptors but follow the same rules as descriptors. +therefore, use gf_odf_desc_read and gf_odf_desc_write for QoS*/ + +/*same function, but for QoS, as a Qualifier IS NOT a descriptor*/ +GF_Err gf_odf_qos_add_qualif(GF_QoS_Descriptor *desc, GF_QoS_Default *qualif); + + + +/* + OCI Stream AU is a list of OCI event (like OD AU is a list of OD commands) +*/ + +typedef struct __tag_oci_event OCIEvent; + +OCIEvent *gf_oci_event_new(u16 EventID); +void gf_oci_event_del(OCIEvent *event); + +GF_Err gf_oci_event_set_start_time(OCIEvent *event, u8 Hours, u8 Minutes, u8 Seconds, u8 HundredSeconds, u8 IsAbsoluteTime); +GF_Err gf_oci_event_set_duration(OCIEvent *event, u8 Hours, u8 Minutes, u8 Seconds, u8 HundredSeconds); +GF_Err gf_oci_event_add_desc(OCIEvent *event, GF_Descriptor *oci_desc); + +GF_Err gf_oci_event_get_id(OCIEvent *event, u16 *ID); +GF_Err gf_oci_event_get_start_time(OCIEvent *event, u8 *Hours, u8 *Minutes, u8 *Seconds, u8 *HundredSeconds, u8 *IsAbsoluteTime); +GF_Err gf_oci_event_get_duration(OCIEvent *event, u8 *Hours, u8 *Minutes, u8 *Seconds, u8 *HundredSeconds); +u32 gf_oci_event_get_desc_count(OCIEvent *event); +GF_Descriptor *gf_oci_event_get_desc(OCIEvent *event, u32 DescIndex); +GF_Err gf_oci_event_rem_desc(OCIEvent *event, u32 DescIndex); + + + +typedef struct __tag_oci_codec OCICodec; + +/*construction / destruction +IsEncoder specifies an OCI Event encoder +version is for future extensions, and only 0x01 is valid for now*/ +OCICodec *gf_oci_codec_new(u8 IsEncoder, u8 Version); +void gf_oci_codec_del(OCICodec *codec); + +/* ENCODER FUNCTIONS +add a command to the codec event list. +The event WILL BE DESTROYED BY THE CODEC after encoding*/ +GF_Err gf_oci_codec_add_event(OCICodec *codec, OCIEvent *event); + +/*encode AU. The memory allocation is done in place +WARNING: once this function called, the codec event List is empty +and events destroyed +you must set *outAU = NULL*/ +GF_Err gf_oci_codec_encode(OCICodec *codec, char **outAU, u32 *au_length); + + + +/*Decoder: decode the previously set-up AU +the input buffer is cleared once decoded*/ +GF_Err gf_oci_codec_decode(OCICodec *codec, char *au, u32 au_length); + +/*get the first OCI Event in the list. Once called, the event is removed +from the event list. Return NULL when the event List is empty +you MUST delete events */ +OCIEvent *gf_oci_codec_get_event(OCICodec *codec); + + +/*OD dump tools*/ +GF_Err gf_odf_dump_au(char *data, u32 dataLength, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_com(void *p, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_desc(void *ptr, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_com_list(GF_List *commandList, FILE *trace, u32 indent, Bool XMTDump); + +/*OCI dump tools*/ +GF_Err gf_oci_dump_event(OCIEvent *ev, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_oci_dump_au(u8 version, char *au, u32 au_length, FILE *trace, u32 indent, Bool XMTDump); + + +/*OD parsing tools (XMT/BT)*/ +/*returns desc tag based on name*/ +u32 gf_odf_get_tag_by_name(char *descName); +/*field type for OD/QoS/IPMPX/etc*/ +enum +{ + /*regular type*/ + GF_ODF_FT_DEFAULT = 0, + /*single descriptor type*/ + GF_ODF_FT_OD = 1, + /*descriptor list type*/ + GF_ODF_FT_OD_LIST = 2, + /*IPMP Data type*/ + GF_ODF_FT_IPMPX = 3, + /*IPMP Data list type*/ + GF_ODF_FT_IPMPX_LIST = 4, + /*IPMP ByteArray type*/ + GF_ODF_FT_IPMPX_BA = 5, + /*IPMP ByteArray list type*/ + GF_ODF_FT_IPMPX_BA_LIST = 6, +}; +u32 gf_odf_get_field_type(GF_Descriptor *desc, char *fieldName); +/*set non-descriptor field value - value string shall be presented without ' or " characters*/ +GF_Err gf_odf_set_field(GF_Descriptor *desc, char *fieldName, char *val); + + + + +/* + IPMPX extensions - IPMP Data only (messages are not supported yet) +*/ + +typedef struct +{ + u32 length; + char *data; +} GF_IPMPX_ByteArray; + +/*IPMPX authentication descriptors*/ +#define GF_IPMPX_AUTH_DESC \ + u8 tag; \ + +typedef struct +{ + GF_IPMPX_AUTH_DESC +} GF_IPMPX_Authentication; + +enum +{ + GF_IPMPX_AUTH_Forbidden_Tag = 0x00, + GF_IPMPX_AUTH_AlgorithmDescr_Tag = 0x01, + GF_IPMPX_AUTH_KeyDescr_Tag = 0x02, +}; + +typedef struct +{ + GF_IPMPX_AUTH_DESC + char *keyBody; + u32 keyBodyLength; +} GF_IPMPX_AUTH_KeyDescriptor; + +typedef struct +{ + GF_IPMPX_AUTH_DESC + /*used if no specAlgoID*/ + u16 regAlgoID; + GF_IPMPX_ByteArray *specAlgoID; + GF_IPMPX_ByteArray *OpaqueData; +} GF_IPMPX_AUTH_AlgorithmDescriptor; + + +/*IPMP data messages*/ +enum +{ + GF_IPMPX_OPAQUE_DATA_TAG = 0x01, + GF_IPMPX_AUDIO_WM_INIT_TAG = 0x02, + GF_IPMPX_VIDEO_WM_INIT_TAG = 0x03, + GF_IPMPX_SEL_DEC_INIT_TAG = 0x04, + GF_IPMPX_KEY_DATA_TAG = 0x05, + GF_IPMPX_AUDIO_WM_SEND_TAG = 0x06, + GF_IPMPX_VIDEO_WM_SEND_TAG = 0x07, + GF_IPMPX_RIGHTS_DATA_TAG = 0x08, + GF_IPMPX_SECURE_CONTAINER_TAG = 0x09, + GF_IPMPX_ADD_TOOL_LISTENER_TAG = 0x0A, + GF_IPMPX_REMOVE_TOOL_LISTENER_TAG = 0x0B, + GF_IPMPX_INIT_AUTHENTICATION_TAG = 0x0C, + GF_IPMPX_MUTUAL_AUTHENTICATION_TAG = 0x0D, + GF_IPMPX_USER_QUERY_TAG = 0x0E, + GF_IPMPX_USER_RESPONSE_TAG = 0x0F, + GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG = 0x10, + GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG = 0x11, + GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG = 0x12, + /*NO ASSOCIATED STRUCTURE*/ + GF_IPMPX_GET_TOOLS_TAG = 0x13, + GF_IPMPX_GET_TOOLS_RESPONSE_TAG = 0x14, + GF_IPMPX_GET_TOOL_CONTEXT_TAG = 0x15, + GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG = 0x16, + GF_IPMPX_CONNECT_TOOL_TAG = 0x17, + GF_IPMPX_DISCONNECT_TOOL_TAG = 0x18, + GF_IPMPX_NOTIFY_TOOL_EVENT_TAG = 0x19, + GF_IPMPX_CAN_PROCESS_TAG = 0x1A, + GF_IPMPX_TRUST_SECURITY_METADATA_TAG = 0x1B, + GF_IPMPX_TOOL_API_CONFIG_TAG = 0x1C, + + /*ISMA*/ + GF_IPMPX_ISMACRYP_TAG = 0xD0, + + /*intern ones for parsing (not real datas)*/ + GF_IPMPX_TRUSTED_TOOL_TAG = 0xA1, + GF_IPMPX_TRUST_SPECIFICATION_TAG = 0xA2, + /*emulate algo descriptors as base IPMP classes for parsing...*/ + GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG = 0xA3, + GF_IPMPX_KEY_DESCRIPTOR_TAG = 0xA4, + GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG = 0xA5, + GF_IPMPX_SEL_ENC_BUFFER_TAG = 0xA6, + GF_IPMPX_SEL_ENC_FIELD_TAG = 0xA7, +}; + +typedef char GF_IPMPX_Date[5]; + + +#define GF_IPMPX_DATA_BASE \ + u8 tag; \ + u8 Version; \ + u8 dataID; \ + +typedef struct +{ + GF_IPMPX_DATA_BASE +} GF_IPMPX_Data; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u32 Context; + u8 AuthType; +} GF_IPMPX_InitAuthentication; + +/*NOT a real DATA, only used as data for parsing*/ +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_Date startDate; + u8 attackerProfile; + u32 trustedDuration; + GF_IPMPX_ByteArray *CCTrustMetadata; +} GF_IPMPX_TrustSpecification; + +/*NOT a real DATA, only used as data for parsing*/ +typedef struct +{ + GF_IPMPX_DATA_BASE + bin128 toolID; + GF_IPMPX_Date AuditDate; + GF_List *trustSpecifications; +} GF_IPMPX_TrustedTool; + +typedef struct _ipmpx_TrustSecurityMetadata +{ + GF_IPMPX_DATA_BASE + GF_List *TrustedTools; +} GF_IPMPX_TrustSecurityMetadata; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + Bool failedNegotiation; + + GF_List *candidateAlgorithms; + GF_List *agreedAlgorithms; + GF_IPMPX_ByteArray *AuthenticationData; + + /*inclAuthCodes will be set if any of the members is set (cf spec...)*/ + u32 certType; + /*GF_IPMPX_ByteArray list*/ + GF_List *certificates; + GF_IPMPX_AUTH_KeyDescriptor *publicKey; + GF_IPMPX_ByteArray *opaque; + GF_IPMPX_TrustSecurityMetadata *trustData; + GF_IPMPX_ByteArray *authCodes; +} GF_IPMPX_MutualAuthentication; + +typedef struct +{ + GF_IPMPX_DATA_BASE + /*if set MAC is part of the encrypted data*/ + Bool isMACEncrypted; + + GF_IPMPX_ByteArray *encryptedData; + GF_IPMPX_Data *protectedMsg; + GF_IPMPX_ByteArray *MAC; +} GF_IPMPX_SecureContainer; + +typedef struct +{ + GF_List *ipmp_tools; +} GF_IPMPX_GetToolsResponse; + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ByteArray *main_class; + GF_IPMPX_ByteArray *subClass; + GF_IPMPX_ByteArray *typeData; + GF_IPMPX_ByteArray *type; + GF_IPMPX_ByteArray *addedData; +} GF_IPMPX_ParametricDescriptionItem; + +typedef struct _tagIPMPXParamDesc +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ByteArray *descriptionComment; + u8 majorVersion; + u8 minorVersion; + /*list of GF_IPMPX_ParametricDescriptionItem*/ + GF_List *descriptions; +} GF_IPMPX_ParametricDescription; + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ParametricDescription *description; +} GF_IPMPX_ToolParamCapabilitiesQuery; + +typedef struct +{ + GF_IPMPX_DATA_BASE + Bool capabilitiesSupported; +} GF_IPMPX_ToolParamCapabilitiesResponse; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMP_Descriptor *toolDescriptor; +} GF_IPMPX_ConnectTool; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u32 IPMP_ToolContextID; +} GF_IPMPX_DisconnectTool; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 scope; + u16 IPMP_DescriptorIDEx; +} GF_IPMPX_GetToolContext; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + u16 OD_ID; + u16 ESD_ID; + u32 IPMP_ToolContextID; +} GF_IPMPX_GetToolContextResponse; + +/*GF_IPMPX_LISTEN_Types*/ +enum +{ + GF_IPMPX_LISTEN_CONNECTED = 0x00, + GF_IPMPX_LISTEN_CONNECTIONFAILED = 0x01, + GF_IPMPX_LISTEN_DISCONNECTED = 0x02, + GF_IPMPX_LISTEN_DISCONNECTIONFAILED = 0x03, + GF_IPMPX_LISTEN_WATERMARKDETECTED = 0x04, +}; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 scope; + /*events to listen to*/ + u8 eventTypeCount; + u8 eventType[10]; +} GF_IPMPX_AddToolNotificationListener; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 eventTypeCount; + u8 eventType[10]; +} GF_IPMPX_RemoveToolNotificationListener; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u16 OD_ID; + u16 ESD_ID; + u8 eventType; + u32 IPMP_ToolContextID; +} GF_IPMPX_NotifyToolEvent; + +typedef struct +{ + GF_IPMPX_DATA_BASE + Bool canProcess; +} GF_IPMPX_CanProcess; + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ByteArray *opaqueData; +} GF_IPMPX_OpaqueData; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ByteArray *keyBody; + /*flags meaning + hasStartDTS = 1; + hasStartPacketID = 1<<1; + hasExpireDTS = 1<<2; + hasExpirePacketID = 1<<3 + */ + u32 flags; + + u64 startDTS; + u32 startPacketID; + u64 expireDTS; + u32 expirePacketID; + GF_IPMPX_ByteArray *OpaqueData; +} GF_IPMPX_KeyData; + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ByteArray *rightsInfo; +} GF_IPMPX_RightsData; + + +/*not a real GF_IPMPX_Data in spec, but emulated as if for parsing*/ +typedef struct +{ + GF_IPMPX_DATA_BASE + bin128 cipher_Id; + u8 syncBoundary; + /*block mode if stream cypher info is NULL*/ + u8 mode; + u16 blockSize; + u16 keySize; + GF_IPMPX_ByteArray *Stream_Cipher_Specific_Init_Info; +} GF_IPMPX_SelEncBuffer; + +/*not a real GF_IPMPX_Data in spec, but emulated as if for parsing*/ +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 field_Id; + u8 field_Scope; + u8 buf; + + u16 mappingTableSize; + u16 *mappingTable; + GF_IPMPX_ByteArray *shuffleSpecificInfo; +} GF_IPMPX_SelEncField; + + +/*mediaTypeExtension*/ +enum +{ + GF_IPMPX_SE_MT_ISO_IEC = 0x00, + GF_IPMPX_SE_MT_ITU = 0x01 + /*the rest is reserved or forbidden*/ +}; + +/*compliance*/ +enum +{ + GF_IPMPX_SE_COMP_FULLY = 0x00, + GF_IPMPX_SE_COMP_VIDEO_PACKETS = 0x01, + GF_IPMPX_SE_COMP_VIDEO_VOP = 0x02, + GF_IPMPX_SE_COMP_VIDEO_NONE = 0x03, + GF_IPMPX_SE_COMP_VIDEO_GOB = 0x04, + /*0x05-2F ISO Reserved for video*/ + GF_IPMPX_SE_COMP_AAC_DF = 0x30, + GF_IPMPX_SE_COMP_AAC_NONE = 0x31, + /* + 0x32 - 0x5F ISO Reserved for audio + 0x60 - 0xCF ISO Reserved + 0xD0 - 0xFE User Defined + 0xFF Forbidden + */ +}; + +/*syncBoundary*/ +enum +{ + GF_IPMPX_SE_SYNC_VID7EO_PACKETS = 0x00, + GF_IPMPX_SE_SYNC_VIDEO_VOP = 0x01, + GF_IPMPX_SE_SYNC_VIDEO_GOV = 0x02, + /*0x03-2F ISO Reserved for video,*/ + GF_IPMPX_SE_SYNC_AAC_DF = 0x30, + /*0x31 - 0x5F ISO Reserved for audio + 0x60 - 0xCF ISO Reserved + 0xD0 - 0xFE User Defined + 0xFF Forbidden + */ +}; + +/*field_Id*/ +enum +{ + GF_IPMPX_SE_FID_VIDEO_MV = 0x00, + GF_IPMPX_SE_FID_VIDEO_DC = 0x01, + GF_IPMPX_SE_FID_VIDEO_DCT_SIGN = 0x02, + GF_IPMPX_SE_FID_VIDEO_DQUANT = 0x03, + GF_IPMPX_SE_FID_VIDEO_DCT_COEF = 0x04, + GF_IPMPX_SE_FID_VIDEO_ALL = 0x05, + /*0x06-2F ISO Reserved for video*/ + GF_IPMPX_SE_FID_AAC_SIGN = 0x30, + GF_IPMPX_SE_FID_AAC_CODEWORDS = 0x31, + GF_IPMPX_SE_FID_AAC_SCALE = 0x32, + /*0x32 - 0x5F ISO Reserved for audio + 0x60 - 0xCF ISO Reserved + 0xD0 - 0xFE User Defined + 0xFF Forbidden*/ +}; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 mediaTypeExtension; + u8 mediaTypeIndication; + u8 profileLevelIndication; + u8 compliance; + + GF_List *SelEncBuffer; + + GF_List *SelEncFields; + + u16 RLE_DataLength; + u16 *RLE_Data; +} GF_IPMPX_SelectiveDecryptionInit; + + +/*watermark init ops*/ +enum +{ + GF_IPMPX_WM_INSERT = 0, + GF_IPMPX_WM_EXTRACT = 1, + GF_IPMPX_WM_REMARK = 2, + GF_IPMPX_WM_DETECT_COMPRESSION = 3, +}; + +/*used for both audio and video WM init*/ +typedef struct +{ + GF_IPMPX_DATA_BASE + /* + for audio: PCM defined (0x01) and all audio objectTypeIndications + for video: YUV defined (0x01) and all visual objectTypeIndications + */ + u8 inputFormat; + u8 requiredOp; + + /*valid for audio WM, inputFormat=0x01*/ + u8 nChannels; + u8 bitPerSample; + u32 frequency; + + /*valid for video WM, inputFormat=0x01*/ + u16 frame_horizontal_size; + u16 frame_vertical_size; + u8 chroma_format; + + u32 wmPayloadLen; + char *wmPayload; + + u16 wmRecipientId; + + u32 opaqueDataSize; + char *opaqueData; +} GF_IPMPX_WatermarkingInit; + + + +/*WM status*/ +enum +{ + GF_IPMPX_WM_PAYLOAD = 0, + GF_IPMPX_WM_NOPAYLOAD = 1, + GF_IPMPX_WM_NONE = 2, + GF_IPMPX_WM_UNKNOWN = 3 +}; + +/*compression status*/ +enum +{ + GF_IPMPX_WM_COMPRESSION = 0, + GF_IPMPX_WM_NO_COMPRESSION = 1, + GF_IPMPX_WM_COMPRESSION_UNKNOWN = 2, +}; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 wm_status; + u8 compression_status; + /*if payload is set, status is FORCED to AUDIO_GF_IPMPX_WM_PAYLOAD*/ + GF_IPMPX_ByteArray *payload; + GF_IPMPX_ByteArray *opaqueData; +} GF_IPMPX_SendWatermark; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + /*GPAC only supports non-0 IDs*/ + u32 Instantiation_API_ID; + u32 Messaging_API_ID; + GF_IPMPX_ByteArray *opaqueData; +} GF_IPMPX_ToolAPI_Config; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 cryptoSuite; + u8 IV_length; + Bool use_selective_encryption; + u8 key_indicator_length; +} GF_IPMPX_ISMACryp; + + +/*constructor/destructor*/ +GF_IPMPX_Data *gf_ipmpx_data_new(u8 tag); +void gf_ipmpx_data_del(GF_IPMPX_Data *p); + +/*parse from bitstream*/ +GF_Err gf_ipmpx_data_parse(GF_BitStream *bs, GF_IPMPX_Data **out_data); +/*get IPMP_Data contained size (eg without tag & sizeofinstance)*/ +u32 gf_ipmpx_data_size(GF_IPMPX_Data *p); +/*get fulml IPMP_Data encoded size (eg with tag & sizeofinstance)*/ +u32 gf_ipmpx_data_full_size(GF_IPMPX_Data *p); +/*writes IPMP_Data to buffer*/ +GF_Err gf_ipmpx_data_write(GF_BitStream *bs, GF_IPMPX_Data *_p); + +/*returns GF_IPMPX_Tag based on name*/ +u8 gf_ipmpx_get_tag(char *dataName); +/*return values: cf above */ +u32 gf_ipmpx_get_field_type(GF_IPMPX_Data *p, char *fieldName); +GF_Err gf_ipmpx_set_field(GF_IPMPX_Data *desc, char *fieldName, char *val); +/*assign subdata*/ +GF_Err gf_ipmpx_set_sub_data(GF_IPMPX_Data *desc, char *fieldName, GF_IPMPX_Data *subdesc); +/*assign bytearray*/ +GF_Err gf_ipmpx_set_byte_array(GF_IPMPX_Data *p, char *field, char *str); + +/*ipmpx dumper*/ +GF_Err gf_ipmpx_dump_data(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump); + + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_MPEG4_ODF_H_*/ diff --git a/include/gpac/mpegts.h b/include/gpac/mpegts.h new file mode 100644 index 0000000..d508634 --- /dev/null +++ b/include/gpac/mpegts.h @@ -0,0 +1,681 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Walid B.H - Jean Le Feuvre + * Copyright (c)2006-200X ENST - All rights reserved + * + * This file is part of GPAC / MPEG2-TS sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_MPEG_TS_H_ +#define _GF_MPEG_TS_H_ + +#include <gpac/list.h> +#include <gpac/internal/odf_dev.h> + +typedef struct tag_m2ts_demux GF_M2TS_Demuxer; +typedef struct tag_m2ts_es GF_M2TS_ES; +typedef struct tag_m2ts_section_es GF_M2TS_SECTION_ES; + +/*Maximum number of streams in a TS*/ +#define GF_M2TS_MAX_STREAMS 8192 + +/*MPEG-2 TS Media types*/ +enum +{ + GF_M2TS_VIDEO_MPEG1 = 0x01, + GF_M2TS_VIDEO_MPEG2 = 0x02, + GF_M2TS_AUDIO_MPEG1 = 0x03, + GF_M2TS_AUDIO_MPEG2 = 0x04, + GF_M2TS_PRIVATE_SECTION = 0x05, + GF_M2TS_PRIVATE_DATA = 0x06, + GF_M2TS_AUDIO_AAC = 0x0F, + GF_M2TS_VIDEO_MPEG4 = 0x10, + GF_M2TS_AUDIO_LATM_AAC = 0x11, + + GF_M2TS_SYSTEMS_MPEG4_PES = 0x12, + GF_M2TS_SYSTEMS_MPEG4_SECTIONS = 0x13, + + GF_M2TS_VIDEO_H264 = 0x1B, + + GF_M2TS_AUDIO_AC3 = 0x81, + GF_M2TS_AUDIO_DTS = 0x8A, + GF_M2TS_SUBTITLE_DVB = 0x100, +}; +/*returns readable name for given stream type*/ +const char *gf_m2ts_get_stream_name(u32 streamType); + +/*PES data framing modes*/ +enum +{ + /*use data framing: recompute start of AUs (data frames)*/ + GF_M2TS_PES_FRAMING_DEFAULT, + /*don't use data framing: all packets are raw PES packets*/ + GF_M2TS_PES_FRAMING_RAW, + /*skip pes processing: all transport packets related to this stream are discarded*/ + GF_M2TS_PES_FRAMING_SKIP +}; + +/*PES packet flags*/ +enum +{ + GF_M2TS_PES_PCK_RAP = 1, + GF_M2TS_PES_PCK_AU_START = 1<<1, + /*visual frame starting in this packet is an I frame or IDR (AVC/H264)*/ + GF_M2TS_PES_PCK_I_FRAME = 1<<2, + /*visual frame starting in this packet is a P frame*/ + GF_M2TS_PES_PCK_P_FRAME = 1<<3, + /*visual frame starting in this packet is a B frame*/ + GF_M2TS_PES_PCK_B_FRAME = 1<<4 +}; + +/*Events used by the MPEGTS demuxer*/ +enum +{ + /*PAT has been found (service connection) - no assoctiated parameter*/ + GF_M2TS_EVT_PAT_FOUND = 0, + /*PAT has been updated - no assoctiated parameter*/ + GF_M2TS_EVT_PAT_UPDATE, + /*repeated PAT has been found (carousel) - no assoctiated parameter*/ + GF_M2TS_EVT_PAT_REPEAT, + /*PMT has been found (service tune-in) - assoctiated parameter: new PMT*/ + GF_M2TS_EVT_PMT_FOUND, + /*repeated PMT has been found (carousel) - assoctiated parameter: updated PMT*/ + GF_M2TS_EVT_PMT_REPEAT, + /*PMT has been changed - assoctiated parameter: updated PMT*/ + GF_M2TS_EVT_PMT_UPDATE, + /*SDT has been received - assoctiated parameter: none*/ + GF_M2TS_EVT_SDT_FOUND, + /*repeated SDT has been found (carousel) - assoctiated parameter: none*/ + GF_M2TS_EVT_SDT_REPEAT, + /*SDT has been received - assoctiated parameter: none*/ + GF_M2TS_EVT_SDT_UPDATE, + /*INT has been received - assoctiated parameter: none*/ + GF_M2TS_EVT_INT_FOUND, + /*repeated INT has been found (carousel) - assoctiated parameter: none*/ + GF_M2TS_EVT_INT_REPEAT, + /*INT has been received - assoctiated parameter: none*/ + GF_M2TS_EVT_INT_UPDATE, + /*PES packet has been received - assoctiated parameter: PES packet*/ + GF_M2TS_EVT_PES_PCK, + /*PCR has been received - assoctiated parameter: PES packet with no data*/ + GF_M2TS_EVT_PES_PCR, + /*An MPEG-4 SL Packet has been received in a section - assoctiated parameter: SL packet */ + GF_M2TS_EVT_SL_PCK, + /*An IP datagram has been received in a section - assoctiated parameter: IP datagram */ + GF_M2TS_EVT_IP_DATAGRAM, + + /*AAC config has been extracted - associated parameter: PES Packet with encoded M4ADecSpecInfo in its data + THIS MUST BE CLEANED UP + */ + GF_M2TS_EVT_AAC_CFG, +#if 0 + /* An EIT message for the present or following event on this TS has been received */ + GF_M2TS_EVT_EIT_ACTUAL_PF, + /* An EIT message for the schedule of this TS has been received */ + GF_M2TS_EVT_EIT_ACTUAL_SCHEDULE, + /* An EIT message for the present or following event of an other TS has been received */ + GF_M2TS_EVT_EIT_OTHER_PF, + /* An EIT message for the schedule of an other TS has been received */ + GF_M2TS_EVT_EIT_OTHER_SCHEDULE, + /* A message to inform about the current date and time in the TS */ + GF_M2TS_EVT_TDT, + /* A message to inform about the current time offset in the TS */ + GF_M2TS_EVT_TOT, +#endif + /* A generic event message for EIT, TDT, TOT etc */ + GF_M2TS_EVT_DVB_GENERAL, +}; + +enum +{ + GF_M2TS_TABLE_START = 1, + GF_M2TS_TABLE_END = 1<<1, + GF_M2TS_TABLE_FOUND = 1<<2, + GF_M2TS_TABLE_UPDATE = 1<<3, + GF_M2TS_TABLE_REPEAT = 1<<4, +}; + +typedef void (*gf_m2ts_section_callback)(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *es, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status); + +typedef struct __m2ts_demux_section +{ + unsigned char *data; + u32 data_size; +} GF_M2TS_Section; + +typedef struct __m2ts_demux_table +{ + struct __m2ts_demux_table *next; + + Bool is_init; + + /*table id*/ + u8 table_id; + u16 ex_table_id; + + /*reassembler state*/ + u8 version_number; + u8 last_version_number; + + u8 current_next_indicator; + + u8 section_number; + u8 last_section_number; + + GF_List *sections; + +} GF_M2TS_Table; + + +typedef struct GF_M2TS_SectionFilter +{ + /*section reassembler*/ + s16 cc; + /*section buffer (max 4096)*/ + char *section; + /*current section length as indicated in section header*/ + u16 length; + /*number of bytes received from current section*/ + u16 received; + /*error indiator when reaggregating sections*/ + u8 had_error; + + /*section->table aggregator*/ + GF_M2TS_Table *table; + + /* indicates that the section and last_section_number do not need to be checked */ + Bool process_individual; + + /* indicates that the section header with table id and extended table id ... is + not parsed by the TS demuxer and left for the application */ + Bool direct_dispatch; + + gf_m2ts_section_callback process_section; +} GF_M2TS_SectionFilter; + + + +/*MPEG-2 TS program object*/ +typedef struct +{ + GF_List *streams; + u32 pmt_pid; + u32 pcr_pid; + u32 number; + + GF_InitialObjectDescriptor *pmt_iod; + + /*list of additional ODs found per program !! used by media importer only , refine this !! + this list is only created when MPEG-4 over MPEG-2 is detected + the list AND the ODs contained in it are destroyed when destroying the demuxer + */ + GF_List *additional_ods; + /*first dts found on this program - this is used by parsers, but not setup by the lib*/ + u64 first_dts; +} GF_M2TS_Program; + +/*ES flags*/ +enum +{ + /*ES is a section stream*/ + GF_M2TS_ES_IS_SECTION = 1, + /*ES is an mpeg-4 flexmux stream*/ + GF_M2TS_ES_IS_FMC = 1<<1, + /*ES is an mpeg-4 SL-packetized stream*/ + GF_M2TS_ES_IS_SL = 1<<2, + /*ES is an mpeg-4 Object Descriptor SL-packetized stream*/ + GF_M2TS_ES_IS_MPEG4_OD = 1<<3, + + /*all flags above this mask are used by importers & co*/ + GF_M2TS_ES_STATIC_FLAGS_MASK = 0x0000FFFF, + + /*Flag used by importers*/ + GF_M2TS_ES_FIRST_DTS = 1<<20, +}; + +/*Abstract Section/PES stream object, only used for type casting*/ +#define ABSTRACT_ES \ + GF_M2TS_Program *program; \ + u32 flags; \ + u32 pid; \ + u32 stream_type; \ + u32 mpeg4_es_id; \ + void *user; \ + u64 first_dts; + +struct tag_m2ts_es +{ + ABSTRACT_ES +}; + +/*INT object*/ +typedef struct +{ + u32 id; +} GF_M2TS_INT; + +struct tag_m2ts_section_es +{ + ABSTRACT_ES + GF_M2TS_SectionFilter *sec; + + /* MPE Frame object, MPE-FEC related data */ + GF_M2TS_INT *ip_mac_not_table; +}; + +/*******************************************************************************/ +typedef struct tag_m2ts_dvb_sub +{ + char language[3]; + u8 type; + u16 composition_page_id; + u16 ancillary_page_id; +} GF_M2TS_DVB_Subtitling_Descriptor; + +typedef struct tag_m2ts_dvb_teletext +{ + char language[3]; + u8 type; + u8 magazine_number; + u8 page_number; +} GF_M2TS_DVB_Teletext_Descriptor; + +/*MPEG-2 TS ES object*/ +typedef struct tag_m2ts_pes +{ + ABSTRACT_ES + u32 lang; + + /*object info*/ + u32 vid_w, vid_h, vid_par, aud_sr, aud_nb_ch; + /*user private*/ + + + /*mpegts lib private - do not touch :)*/ + /*PES re-assembler*/ + unsigned char *data; + u32 data_len, pes_len; + Bool rap; + u64 PTS, DTS; + + /*PES reframer - if NULL, pes processing is skiped*/ + u32 frame_state; + void (*reframe)(struct tag_m2ts_demux *ts, struct tag_m2ts_pes *pes, u64 DTS, u64 CTS, unsigned char *data, u32 data_len); + /*LATM stuff - should be moved out of mpegts*/ + unsigned char *buf; + u32 buf_len; + + GF_M2TS_DVB_Subtitling_Descriptor sub; + +} GF_M2TS_PES; + +/*SDT information object*/ +typedef struct +{ + u16 original_network_id; + u16 transport_stream_id; + u32 service_id; + u32 EIT_schedule; + u32 EIT_present_following; + u32 running_status; + u32 free_CA_mode; + u8 service_type; + unsigned char *provider, *service; +} GF_M2TS_SDT; + +typedef struct +{ + u16 network_id; + unsigned char *network_name; + u16 original_network_id; + u16 transport_stream_id; + u16 service_id; + u32 service_type; + u32 logical_channel_number; +} GF_M2TS_NIT; + +#define GF_M2TS_BASE_DESCRIPTOR u32 tag; + +typedef struct { + u8 content_nibble_level_1, content_nibble_level_2, user_nibble; +} GF_M2TS_DVB_Content_Descriptor; + +typedef struct { + char country_code[3]; + u8 value; +} GF_M2TS_DVB_Rating_Descriptor; + +typedef struct { + unsigned char lang[3]; + unsigned char *event_name, *event_text; +} GF_M2TS_DVB_Short_Event_Descriptor; + +typedef struct { + unsigned char *item; + unsigned char *description; +} GF_M2TS_DVB_Extended_Event_Item; + +typedef struct { + unsigned char lang[3]; + u32 last; + GF_List *items; + unsigned char *text; +} GF_M2TS_DVB_Extended_Event_Descriptor; + +/*EIT information objects*/ +typedef struct +{ + u32 year, month, day, time; + + /* local time offset descriptor data */ + char country_code[3]; + u8 country_region_id; + u8 local_time_offset_polarity; + u16 local_time_offset; + /*time_of_change*/ + u32 toc_year, toc_month, toc_day, toc_time; + u16 next_time_offset; +} GF_M2TS_DateTime_Event; + +typedef struct { + u8 stream_content; + u8 component_type; + u8 component_tag; + char language_code[3]; + unsigned char *text; +} GF_M2TS_Component; + +typedef struct +{ + u16 event_id; + GF_M2TS_DateTime_Event start; + u32 duration; + u8 running_status; + u8 free_CA_mode; + GF_List *short_events; + GF_List *extended_events; + GF_List *components; + GF_List *contents; + GF_List *ratings; +} GF_M2TS_EIT_Event; + +typedef struct +{ + u32 original_network_id; + u32 transport_stream_id; + u16 service_id; + GF_List *events; +} GF_M2TS_EIT; + +void gf_m2ts_decode_mjd_date(u32 date, u32 *year, u32 *month, u32 *day); + + +/*MPEG-2 TS packet*/ +typedef struct +{ + char *data; + u32 data_len; + u32 flags; + u64 PTS, DTS; + /*parent stream*/ + GF_M2TS_PES *stream; +} GF_M2TS_PES_PCK; + +/*MPEG-4 SL packet from MPEG-2 TS*/ +typedef struct +{ + char *data; + u32 data_len; + /*parent stream */ + GF_M2TS_ES *stream; +} GF_M2TS_SL_PCK; + +/*MPEG-2 TS demuxer*/ +struct tag_m2ts_demux +{ + GF_M2TS_ES *ess[GF_M2TS_MAX_STREAMS]; + GF_List *programs; + /*keep it seperate for now - TODO check if we're sure of the order*/ + GF_List *SDTs; + + /*user callback - MUST NOT BE NULL*/ + void (*on_event)(struct tag_m2ts_demux *ts, u32 evt_type, void *par); + /*private user data*/ + void *user; + + /*private resync buffer*/ + char *buffer; + u32 buffer_size, alloc_size; + /*default transport PID filters*/ + GF_M2TS_SectionFilter *pat, *nit, *sdt, *eit, *tdt_tot_st; + + /* Structure to hold all the INT tables if the TS contains IP streams */ + GF_List *ip_mac_not_tables; + + Bool has_4on2; + /* analyser */ + FILE *pes_out; + + +}; + + +GF_M2TS_Demuxer *gf_m2ts_demux_new(); +void gf_m2ts_demux_del(GF_M2TS_Demuxer *ts); +void gf_m2ts_reset_parsers(GF_M2TS_Demuxer *ts); +GF_Err gf_m2ts_set_pes_framing(GF_M2TS_PES *pes, u32 mode); +GF_Err gf_m2ts_process_data(GF_M2TS_Demuxer *ts, char *data, u32 data_size); + +u32 gf_m2ts_crc32_check(char *data, u32 len); + +/*MPEG-2 Descriptor tags*/ +enum +{ + /* ... */ + GF_M2TS_VIDEO_STREAM_DESCRIPTOR = 0x02, + GF_M2TS_AUDIO_STREAM_DESCRIPTOR = 0x03, + GF_M2TS_HIERARCHY_DESCRIPTOR = 0x04, + GF_M2TS_REGISTRATION_DESCRIPTOR = 0x05, + GF_M2TS_DATA_STREAM_ALIGNEMENT_DESCRIPTOR = 0x06, + GF_M2TS_TARGET_BACKGROUND_GRID_DESCRIPTOR = 0x07, + GF_M2TS_VIEW_WINDOW_DESCRIPTOR = 0x08, + GF_M2TS_CA_DESCRIPTOR = 0x09, + GF_M2TS_ISO_639_LANGUAGE_DESCRIPTOR = 0x0A, + /* ... */ + GF_M2TS_STD_DESCRIPTOR = 0x17, + /* ... */ + GF_M2TS_MPEG4_VIDEO_DESCRIPTOR = 0x1B, + GF_M2TS_MPEG4_AUDIO_DESCRIPTOR = 0x1C, + GF_M2TS_MPEG4_IOD_DESCRIPTOR = 0x1D, + GF_M2TS_MPEG4_SL_DESCRIPTOR = 0x1E, + GF_M2TS_MPEG4_FMC_DESCRIPTOR = 0x1F, + /* ... */ + GF_M2TS_AVC_VIDEO_DESCRIPTOR = 0x28, + /* ... */ + GF_M2TS_AVC_TIMING_HRD_DESCRIPTOR = 0x2A, + /* ... */ + + /* 0x2D - 0x3F - ISO/IEC 13818-6 values */ + /* 0x40 - 0xFF - User Private values */ + GF_M2TS_DVB_NETWORK_NAME_DESCRIPTOR = 0x40, + GF_M2TS_DVB_SERVICE_LIST_DESCRIPTOR = 0x41, + GF_M2TS_DVB_STUFFING_DESCRIPTOR = 0x42, + GF_M2TS_DVB_SAT_DELIVERY_SYSTEM_DESCRIPTOR = 0x43, + GF_M2TS_DVB_CABLE_DELIVERY_SYSTEM_DESCRIPTOR = 0x44, + GF_M2TS_DVB_VBI_DATA_DESCRIPTOR = 0x45, + GF_M2TS_DVB_VBI_TELETEXT_DESCRIPTOR = 0x46, + GF_M2TS_DVB_BOUQUET_NAME_DESCRIPTOR = 0x47, + GF_M2TS_DVB_SERVICE_DESCRIPTOR = 0x48, + GF_M2TS_DVB_COUNTRY_AVAILABILITY_DESCRIPTOR = 0x49, + GF_M2TS_DVB_LINKAGE_DESCRIPTOR = 0x4A, + GF_M2TS_DVB_NVOD_REFERENCE_DESCRIPTOR = 0x4B, + GF_M2TS_DVB_TIME_SHIFTED_SERVICE_DESCRIPTOR = 0x4C, + GF_M2TS_DVB_SHORT_EVENT_DESCRIPTOR = 0x4D, + GF_M2TS_DVB_EXTENDED_EVENT_DESCRIPTOR = 0x4E, + GF_M2TS_DVB_TIME_SHIFTED_EVENT_DESCRIPTOR = 0x4F, + GF_M2TS_DVB_COMPONENT_DESCRIPTOR = 0x50, + GF_M2TS_DVB_MOSAIC_DESCRIPTOR = 0x51, + GF_M2TS_DVB_STREAM_IDENTIFIER_DESCRIPTOR = 0x52, + GF_M2TS_DVB_CA_IDENTIFIER_DESCRIPTOR = 0x53, + GF_M2TS_DVB_CONTENT_DESCRIPTOR = 0x54, + GF_M2TS_DVB_PARENTAL_RATING_DESCRIPTOR = 0x55, + GF_M2TS_DVB_TELETEXT_DESCRIPTOR = 0x56, + /* ... */ + GF_M2TS_DVB_LOCAL_TIME_OFFSET_DESCRIPTOR = 0x58, + GF_M2TS_DVB_SUBTITLING_DESCRIPTOR = 0x59, + /* ... */ + GF_M2TS_DVB_DATA_BROADCAST_DESCRIPTOR = 0x64, + /* ... */ + GF_M2TS_DVB_DATA_BROADCAST_ID_DESCRIPTOR = 0x66, + /* ... */ + GF_M2TS_DVB_AC3_DESCRIPTOR = 0x6A, + +}; + +/* Reserved PID values */ +enum { + GF_M2TS_PID_PAT = 0x0000, + GF_M2TS_PID_CAT = 0x0001, + GF_M2TS_PID_TSDT = 0x0002, + /* reserved 0x0003 to 0x000F */ + GF_M2TS_PID_NIT_ST = 0x0010, + GF_M2TS_PID_SDT_BAT_ST = 0x0011, + GF_M2TS_PID_EIT_ST_CIT = 0x0012, + GF_M2TS_PID_RST_ST = 0x0013, + GF_M2TS_PID_TDT_TOT_ST = 0x0014, + GF_M2TS_PID_NET_SYNC = 0x0015, + GF_M2TS_PID_RNT = 0x0016, + /* reserved 0x0017 to 0x001B */ + GF_M2TS_PID_IN_SIG = 0x001C, + GF_M2TS_PID_MEAS = 0x001D, + GF_M2TS_PID_DIT = 0x001E, + GF_M2TS_PID_SIT = 0x001F +}; + +/* max size includes first header, second header, payload and CRC */ +enum { + GF_M2TS_TABLE_ID_PAT = 0x00, + GF_M2TS_TABLE_ID_CAT = 0x01, + GF_M2TS_TABLE_ID_PMT = 0x02, + GF_M2TS_TABLE_ID_TSDT = 0x03, /* max size for section 1024 */ + GF_M2TS_TABLE_ID_MPEG4_BIFS = 0x04, /* max size for section 4096 */ + GF_M2TS_TABLE_ID_MPEG4_OD = 0x05, /* max size for section 4096 */ + GF_M2TS_TABLE_ID_METADATA = 0x06, + GF_M2TS_TABLE_ID_IPMP_CONTROL = 0x07, + /* 0x08 - 0x37 reserved */ + /* 0x38 - 0x3D DSM-CC defined */ + GF_M2TS_TABLE_ID_DSM_CC_PRIVATE = 0x3E, /* used for MPE (only, not MPE-FEC) */ + /* 0x3F DSM-CC defined */ + GF_M2TS_TABLE_ID_NIT_ACTUAL = 0x40, /* max size for section 1024 */ + GF_M2TS_TABLE_ID_NIT_OTHER = 0x41, + GF_M2TS_TABLE_ID_SDT_ACTUAL = 0x42, /* max size for section 1024 */ + /* 0x43 - 0x45 reserved */ + GF_M2TS_TABLE_ID_SDT_OTHER = 0x46, /* max size for section 1024 */ + /* 0x47 - 0x49 reserved */ + GF_M2TS_TABLE_ID_BAT = 0x4a, /* max size for section 1024 */ + /* 0x4b - 0x4d reserved */ + GF_M2TS_TABLE_ID_EIT_ACTUAL_PF = 0x4E, /* max size for section 4096 */ + GF_M2TS_TABLE_ID_EIT_OTHER_PF = 0x4F, + /* 0x50 - 0x6f EIT SCHEDULE */ + GF_M2TS_TABLE_ID_EIT_SCHEDULE_MIN = 0x50, + GF_M2TS_TABLE_ID_EIT_SCHEDULE_ACTUAL_MAX= 0x5F, + GF_M2TS_TABLE_ID_EIT_SCHEDULE_MAX = 0x6F, + + GF_M2TS_TABLE_ID_TDT = 0x70, + GF_M2TS_TABLE_ID_RST = 0x71, /* max size for section 1024 */ + GF_M2TS_TABLE_ID_ST = 0x72, /* max size for section 4096 */ + GF_M2TS_TABLE_ID_TOT = 0x73, + GF_M2TS_TABLE_ID_AI = 0x74, + GF_M2TS_TABLE_ID_CONT = 0x75, + GF_M2TS_TABLE_ID_RC = 0x76, + GF_M2TS_TABLE_ID_CID = 0x77, + GF_M2TS_TABLE_ID_MPE_FEC = 0x78, + GF_M2TS_TABLE_ID_RES_NOT = 0x79, + /* 0x7A - 0x7D reserved */ + GF_M2TS_TABLE_ID_DIT = 0x7E, + GF_M2TS_TABLE_ID_SIT = 0x7F, /* max size for section 4096 */ + /* 0x80 - 0xfe reserved */ + /* 0xff reserved */ +}; + +enum { + M2TS_ADAPTATION_RESERVED = 0, + M2TS_ADAPTATION_NONE = 1, + M2TS_ADAPTATION_ONLY = 2, + M2TS_ADAPTATION_AND_PAYLOAD = 3, +}; + + +#define SECTION_HEADER_LENGTH 3 /* header till the last bit of the section_length field */ +#define SECTION_ADDITIONAL_HEADER_LENGTH 5 /* header from the last bit of the section_length field to the payload */ +#define CRC_LENGTH 4 + +typedef struct +{ + u8 sync; + u8 error; + u8 payload_start; + u8 priority; + u16 pid; + u8 scrambling_ctrl; + u8 adaptation_field; + u8 continuity_counter; +} GF_M2TS_Header; + +typedef struct +{ + u32 discontinuity_indicator; + u32 random_access_indicator; + u32 priority_indicator; + + u32 PCR_flag; + u64 PCR_base, PCR_ext; + + u32 OPCR_flag; + u64 OPCR_base, OPCR_ext; + + u32 splicing_point_flag; + u32 transport_private_data_flag; + u32 adaptation_field_extension_flag; +/* + u32 splice_countdown; + u32 transport_private_data_length; + u32 adaptation_field_extension_length; + u32 ltw_flag; + u32 piecewise_rate_flag; + u32 seamless_splice_flag; + u32 ltw_valid_flag; + u32 ltw_offset; + u32 piecewise_rate; + u32 splice_type; + u32 DTS_next_AU; +*/ +} GF_M2TS_AdaptationField; + +typedef struct +{ + u8 id; + u16 pck_len; + u8 data_alignment; + u64 PTS, DTS; + u8 hdr_data_len; +} GF_M2TS_PESHeader; + +#endif //_GF_MPEG_TS_H_ diff --git a/include/gpac/network.h b/include/gpac/network.h new file mode 100644 index 0000000..0e2bb23 --- /dev/null +++ b/include/gpac/network.h @@ -0,0 +1,403 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_NET_H_ +#define _GF_NET_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file <gpac/network.h> + * \brief IP network functions. + */ + + /*! + * \addtogroup net_grp network + * \ingroup utils_grp + * \brief IP Network Functions + * + *This section documents the IP network functions of the GPAC framework. + * @{ + */ + +#include <gpac/tools.h> + + +/*! + *\brief URL local test + * + *Tests whether a URL describes a local file or not + *\param url the url to analyze + *\return 1 if the URL describes a local file, 0 otherwise + */ +Bool gf_url_is_local(const char *url); + +/*! + *\brief gets absolute file path + * + *Gets the absolute file path from a relative path and its parent absolute one. This can only be used with file paths. + *\param pathName the relative path name of a file + *\param parentPath the absolute parent path name + *\return absolute path name of the file, or NULL if bad paths are provided. + \note the returned string must be freed by user + */ +char *gf_url_get_absolute_path(const char *pathName, const char *parentPath); +/*concatenates URL and gets back full URL - returned string must be freed by user*/ +/*! + *\brief URL concatenation + * + *Concatenates a relative URL with its parent URL + *\param parentName URL of the parent service + *\param pathName URL of the service + *\return absolute path name of the service, or NULL if bad paths are provided or if the service path is already an absolute one. + \note the returned string must be freed by user + */ +char *gf_url_concatenate(const char *parentName, const char *pathName); + + +/*! + *\brief URL to file system + * + *Converts a local URL to a file system value. Removes all white spaces and similar + *\param url url to convert + */ +void gf_url_to_fs_path(char *url); + +/*! + *\brief gets UTC time + * + *Gets UTC time since midnight Jan 1970 + *\param sec number of seconds + *\param msec number of milliseconds + */ +void gf_utc_time_since_1970(u32 *sec, u32 *msec); + + +/*! + * \brief NTP seconds from 1900 to 1970 + * \hideinitializer + * + * Macro giving the number of seconds from from 1900 to 1970 +*/ +#define GF_NTP_SEC_1900_TO_1970 2208988800ul + +/*! + *\brief gets NTP time + * + *Gets NTP (Network Time Protocol) in seconds and fractional side + \param sec NTP time in seconds + \param frac fractional NTP time expressed in 1 / (1<<32 - 1) seconds units + */ +void gf_net_get_ntp(u32 *sec, u32 *frac); + +/*! + * Socket options + * \hideinitializer + */ +enum +{ + /*!Reuses port.*/ + GF_SOCK_REUSE_PORT = 1, + /*!Forces IPV6 if available.*/ + GF_SOCK_FORCE_IPV6 = 1<<1 +}; + +/*! + *\brief abstracted socket object + * + *The abstracted socket object allows you to build client and server applications very simply + *with support for unicast and multicast (no IPv6 yet) +*/ +typedef struct __tag_socket GF_Socket; + +/*!Buffer size to pass for IP address retrieval*/ +#define GF_MAX_IP_NAME_LEN 516 + +/*!socket is a TCP socket*/ +#define GF_SOCK_TYPE_TCP 0x01 +/*!socket is a UDP socket*/ +#define GF_SOCK_TYPE_UDP 0x02 + +/*! + *\brief socket constructor + * + *Constructs a socket object + *\param SocketType the socket type to create, either UDP or TCP + *\return the socket object or NULL if network initialization failure + */ +GF_Socket *gf_sk_new(u32 SocketType); +/*! + *\brief socket destructor + * + *Deletes a socket object + *\param sock the socket object + */ +void gf_sk_del(GF_Socket *sock); + +/*! + *\brief reset internal buffer + * + *Forces the internal socket buffer to be reseted (discarded) + *\param sock the socket object + */ +void gf_sk_reset(GF_Socket *sock); +/*! + *\brief socket buffer size control + * + *Sets the size of the internal buffer of the socket. The socket MUST be bound or connected before. + *\param sock the socket object + *\param send_buffer if 0, sets the size of the reception buffer, otherwise sets the size of the emission buffer + *\param new_size new size of the buffer in bytes. + *\warning This operation may fail depending on the provider, hardware... + */ +GF_Err gf_sk_set_buffer_size(GF_Socket *sock, Bool send_buffer, u32 new_size); + +/*! + *\brief blocking mode control + * + *Sets the blocking mode of a socket on or off. A blocking socket will wait for the net operation to be possible + *while a non-blocking one would return an error. By default, sockets are created in blocking mode + *\param sock the socket object + *\param NonBlockingOn set to 1 to use on-blocking sockets, 0 otherwise + */ +GF_Err gf_sk_set_block_mode(GF_Socket *sock, Bool NonBlockingOn); +/*! + *\brief socket binding + * + *Binds the given socket to the specified port. + *\param local_ip the local interface IP address if desired. If NULL, the default interface will be used. + *\param sock the socket object + *\param port port number to bind this socket to + *\param peer_name the remote server address + *\param peer_port remote port number to connect the socket to + *\param options list of option for the bind operation. + */ +GF_Err gf_sk_bind(GF_Socket *sock, char *local_ip, u16 port, char *peer_name, u16 peer_port, u32 options); +/*! + *\brief connects a socket + * + *Connects a socket to a remote peer on a given port + *\param sock the socket object + *\param peer_name the remote server address (IP or DNS) + *\param port remote port number to connect the socket to + *\param local_ip the local (client) address (IP or DNS) if any, NULL otherwise. + */ +GF_Err gf_sk_connect(GF_Socket *sock, char *peer_name, u16 port, char *local_ip); +/*! + *\brief data emission + * + *Sends a buffer on the socket. The socket must be in a bound or connected mode + *\param sock the socket object + *\param buffer the data buffer to send + *\param length the data length to send + */ +GF_Err gf_sk_send(GF_Socket *sock, char *buffer, u32 length); +/*! + *\brief data reception + * + *Fetches data on a socket. The socket must be in a bound or connected state + *\param sock the socket object + *\param buffer the recpetion buffer where data is written + *\param length the allocated size of the reception buffer + *\param start_from the offset in the reception buffer where to start writing + *\param read the actual number of bytes received + */ +GF_Err gf_sk_receive(GF_Socket *sock, char *buffer, u32 length, u32 start_from, u32 *read); +/*! + *\brief socket listening + * + *Sets the socket in a listening state. This socket must have been bound to a port before + *\param sock the socket object + *\param max_conn the maximum number of simultaneous connection this socket will accept + */ +GF_Err gf_sk_listen(GF_Socket *sock, u32 max_conn); +/*! + *\brief socket accept + * + *Accepts an incomming connection on a listening socket + *\param sock the socket object + *\param new_conn the resulting connection socket object + */ +GF_Err gf_sk_accept(GF_Socket *sock, GF_Socket **new_conn); + +/*! + *\brief server socket mode + * + *Disable the Nable algo (e.g. set TCP_NODELAY) and set the KEEPALIVE on + *\param sock the socket object + *\param server_on sets server mode on or off +*/ +GF_Err gf_sk_server_mode(GF_Socket *sock, Bool server_on); + +/*! + *\brief get local host name + * + *Retrieves local host name. + *\param buffer destination buffer for name. Buffer must be GF_MAX_IP_NAME_LEN long + */ +GF_Err gf_sk_get_host_name(char *buffer); + +/*! + *\brief get local IP + * + *Gets local IP address of a connected socket, typically used for server after an ACCEPT + *\param sock the socket object + *\param buffer destination buffer for IP address. Buffer must be GF_MAX_IP_NAME_LEN long + */ +GF_Err gf_sk_get_local_ip(GF_Socket *sock, char *buffer); +/*! + *\brief get local info + * + *Gets local socket info of a socket + *\param sock the socket object + *\param port local port number of the socket + *\param sock_type socket type (UDP or TCP) + */ +GF_Err gf_sk_get_local_info(GF_Socket *sock, u16 *port, u32 *sock_type); + +/*! + *\brief get remote address + * + *Gets the remote address of a peer. The socket MUST be connected. + *\param sock the socket object + *\param buffer destination buffer for IP address. Buffer must be GF_MAX_IP_NAME_LEN long + */ +GF_Err gf_sk_get_remote_address(GF_Socket *sock, char *buffer); + +/*! + *\brief set remote address + * + *Sets the remote address of a socket. This is used by connectionless sockets using SendTo and ReceiveFrom + *\param sock the socket object + *\param address the remote peer address + *\param port the remote peer port + */ +GF_Err gf_sk_set_remote(GF_Socket *sock, char *address, u16 port); + + +/*! + *\brief multicast setup + * + *Performs multicast setup (BIND and JOIN) for the socket object + *\param sock the socket object + *\param multi_ip_add the multicast IP address + *\param multi_port the multicast port number + *\param TTL the multicast TTL (Time-To-Live) + *\param no_bind if sets, only join the multicast + *\param local_interface_ip the local interface IP address if desired. If NULL, the default interface will be used. + */ +GF_Err gf_sk_setup_multicast(GF_Socket *sock, char *multi_ip_add, u16 multi_port, u32 TTL, Bool no_bind, char *local_interface_ip); +/*! + *brief multicast address test + * + *tests whether an IP address is a multicast one or not + *\param multi_ip_add the multicast IP address to test + *\return 1 if the address is a multicast one, 0 otherwise + */ +u32 gf_sk_is_multicast_address(char *multi_ip_add); + +/*! + *\brief send data with wait delay + * + *Sends data with a max wait delay. This is used for http / ftp sockets mainly. The socket must be connected. + *\param sock the socket object + *\param buffer the data buffer to send + *\param length the data length to send + *\param delay_sec the maximum delay in second to wait before aborting + *\return If the operation timeed out, the function will return a GF_IP_SOCK_WOULD_BLOCK error. + */ +GF_Err gf_sk_send_wait(GF_Socket *sock, char *buffer, u32 length, u32 delay_sec); +/* receive data with a max wait delay of Second - used for http / ftp sockets mainly*/ +/*! + *\brief receive data with wait delay + * + *Fetches data with a max wait delay. This is used for http / ftp sockets mainly. The socket must be connected. + *\param sock the socket object + *\param buffer the recpetion buffer where data is written + *\param length the allocated size of the reception buffer + *\param start_from the offset in the reception buffer where to start writing + *\param read the actual number of bytes received + *\param delay_sec the maximum delay in second to wait before aborting + *\return If the operation timeed out, the function will return a GF_IP_SOCK_WOULD_BLOCK error. + */ +GF_Err gf_sk_receive_wait(GF_Socket *sock, char *buffer, u32 length, u32 start_from, u32 *read, u32 delay_sec); + +/*! + *\brief gets socket handle + * + *Gets the socket low-level handle as used by OpenSSL. + *\param sock the socket object + *\return the socket handle + */ +s32 gf_sk_get_handle(GF_Socket *sock); + + +/*! + *\brief gets ipv6 support + * + *Returns IPV6 support information. + *\return 2 if the machine has IPV6 support, 1 if the library was compiled with IPV6 support, 0 otherwise + */ +u32 gf_net_has_ipv6(); + + +/*! + *\brief checks address type + * + *Checks if an address is an IPV6 or IPV4 one. + *\return true 1 if address is IPV6 one, 0 otherwise + */ +Bool gf_net_is_ipv6(char *address); + + +/*! + * \brief MobileIP Callback + * + * The gf_net_mobileip_ctrl_cbk type is the type for the callback of the \ref gf_net_set_mobileip_callback function. By default no mobileip is used + * \param cbck Opaque user data. + * \param start boolean indicating wether the MobileIP subsystem should be started or stoped. + * \return Error code if needed. + * + */ +typedef GF_Err (*gf_net_mobileip_ctrl_cbk)(Bool start); + +/*! + *\brief Assigns MobileIP callback + * + *Assigns the MobileIP control callback. + *\param _mobip_cbk MobileIP control callback + */ +void gf_net_mobileip_set_callback(gf_net_mobileip_ctrl_cbk _mobip_cbk, const char *MobileIP); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_NET_H_*/ + diff --git a/include/gpac/nodes_mpeg4.h b/include/gpac/nodes_mpeg4.h new file mode 100644 index 0000000..7433f23 --- /dev/null +++ b/include/gpac/nodes_mpeg4.h @@ -0,0 +1,1916 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + DO NOT MOFIFY - File generated on GMT Thu Aug 07 11:43:26 2008 + + BY MPEG4Gen for GPAC Version 0.4.5-DEV +*/ + +#ifndef _nodes_mpeg4_H +#define _nodes_mpeg4_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/scenegraph_vrml.h> + + + +enum { + TAG_MPEG4_Anchor = GF_NODE_RANGE_FIRST_MPEG4, + TAG_MPEG4_AnimationStream, + TAG_MPEG4_Appearance, + TAG_MPEG4_AudioBuffer, + TAG_MPEG4_AudioClip, + TAG_MPEG4_AudioDelay, + TAG_MPEG4_AudioFX, + TAG_MPEG4_AudioMix, + TAG_MPEG4_AudioSource, + TAG_MPEG4_AudioSwitch, + TAG_MPEG4_Background, + TAG_MPEG4_Background2D, + TAG_MPEG4_Billboard, + TAG_MPEG4_Bitmap, + TAG_MPEG4_Box, + TAG_MPEG4_Circle, + TAG_MPEG4_Collision, + TAG_MPEG4_Color, + TAG_MPEG4_ColorInterpolator, + TAG_MPEG4_CompositeTexture2D, + TAG_MPEG4_CompositeTexture3D, + TAG_MPEG4_Conditional, + TAG_MPEG4_Cone, + TAG_MPEG4_Coordinate, + TAG_MPEG4_Coordinate2D, + TAG_MPEG4_CoordinateInterpolator, + TAG_MPEG4_CoordinateInterpolator2D, + TAG_MPEG4_Curve2D, + TAG_MPEG4_Cylinder, + TAG_MPEG4_CylinderSensor, + TAG_MPEG4_DirectionalLight, + TAG_MPEG4_DiscSensor, + TAG_MPEG4_ElevationGrid, + TAG_MPEG4_Expression, + TAG_MPEG4_Extrusion, + TAG_MPEG4_Face, + TAG_MPEG4_FaceDefMesh, + TAG_MPEG4_FaceDefTables, + TAG_MPEG4_FaceDefTransform, + TAG_MPEG4_FAP, + TAG_MPEG4_FDP, + TAG_MPEG4_FIT, + TAG_MPEG4_Fog, + TAG_MPEG4_FontStyle, + TAG_MPEG4_Form, + TAG_MPEG4_Group, + TAG_MPEG4_ImageTexture, + TAG_MPEG4_IndexedFaceSet, + TAG_MPEG4_IndexedFaceSet2D, + TAG_MPEG4_IndexedLineSet, + TAG_MPEG4_IndexedLineSet2D, + TAG_MPEG4_Inline, + TAG_MPEG4_LOD, + TAG_MPEG4_Layer2D, + TAG_MPEG4_Layer3D, + TAG_MPEG4_Layout, + TAG_MPEG4_LineProperties, + TAG_MPEG4_ListeningPoint, + TAG_MPEG4_Material, + TAG_MPEG4_Material2D, + TAG_MPEG4_MovieTexture, + TAG_MPEG4_NavigationInfo, + TAG_MPEG4_Normal, + TAG_MPEG4_NormalInterpolator, + TAG_MPEG4_OrderedGroup, + TAG_MPEG4_OrientationInterpolator, + TAG_MPEG4_PixelTexture, + TAG_MPEG4_PlaneSensor, + TAG_MPEG4_PlaneSensor2D, + TAG_MPEG4_PointLight, + TAG_MPEG4_PointSet, + TAG_MPEG4_PointSet2D, + TAG_MPEG4_PositionInterpolator, + TAG_MPEG4_PositionInterpolator2D, + TAG_MPEG4_ProximitySensor2D, + TAG_MPEG4_ProximitySensor, + TAG_MPEG4_QuantizationParameter, + TAG_MPEG4_Rectangle, + TAG_MPEG4_ScalarInterpolator, + TAG_MPEG4_Script, + TAG_MPEG4_Shape, + TAG_MPEG4_Sound, + TAG_MPEG4_Sound2D, + TAG_MPEG4_Sphere, + TAG_MPEG4_SphereSensor, + TAG_MPEG4_SpotLight, + TAG_MPEG4_Switch, + TAG_MPEG4_TermCap, + TAG_MPEG4_Text, + TAG_MPEG4_TextureCoordinate, + TAG_MPEG4_TextureTransform, + TAG_MPEG4_TimeSensor, + TAG_MPEG4_TouchSensor, + TAG_MPEG4_Transform, + TAG_MPEG4_Transform2D, + TAG_MPEG4_Valuator, + TAG_MPEG4_Viewpoint, + TAG_MPEG4_VisibilitySensor, + TAG_MPEG4_Viseme, + TAG_MPEG4_WorldInfo, + TAG_MPEG4_AcousticMaterial, + TAG_MPEG4_AcousticScene, + TAG_MPEG4_ApplicationWindow, + TAG_MPEG4_BAP, + TAG_MPEG4_BDP, + TAG_MPEG4_Body, + TAG_MPEG4_BodyDefTable, + TAG_MPEG4_BodySegmentConnectionHint, + TAG_MPEG4_DirectiveSound, + TAG_MPEG4_Hierarchical3DMesh, + TAG_MPEG4_MaterialKey, + TAG_MPEG4_PerceptualParameters, + TAG_MPEG4_TemporalTransform, + TAG_MPEG4_TemporalGroup, + TAG_MPEG4_ServerCommand, + TAG_MPEG4_InputSensor, + TAG_MPEG4_MatteTexture, + TAG_MPEG4_MediaBuffer, + TAG_MPEG4_MediaControl, + TAG_MPEG4_MediaSensor, + TAG_MPEG4_BitWrapper, + TAG_MPEG4_CoordinateInterpolator4D, + TAG_MPEG4_DepthImage, + TAG_MPEG4_FFD, + TAG_MPEG4_Implicit, + TAG_MPEG4_XXLFM_Appearance, + TAG_MPEG4_XXLFM_BlendList, + TAG_MPEG4_XXLFM_FrameList, + TAG_MPEG4_XXLFM_LightMap, + TAG_MPEG4_XXLFM_SurfaceMapList, + TAG_MPEG4_XXLFM_ViewMapList, + TAG_MPEG4_MeshGrid, + TAG_MPEG4_NonLinearDeformer, + TAG_MPEG4_NurbsCurve, + TAG_MPEG4_NurbsCurve2D, + TAG_MPEG4_NurbsSurface, + TAG_MPEG4_OctreeImage, + TAG_MPEG4_XXParticles, + TAG_MPEG4_XXParticleInitBox, + TAG_MPEG4_XXPlanarObstacle, + TAG_MPEG4_XXPointAttractor, + TAG_MPEG4_PointTexture, + TAG_MPEG4_PositionAnimator, + TAG_MPEG4_PositionAnimator2D, + TAG_MPEG4_PositionInterpolator4D, + TAG_MPEG4_ProceduralTexture, + TAG_MPEG4_Quadric, + TAG_MPEG4_SBBone, + TAG_MPEG4_SBMuscle, + TAG_MPEG4_SBSegment, + TAG_MPEG4_SBSite, + TAG_MPEG4_SBSkinnedModel, + TAG_MPEG4_SBVCAnimation, + TAG_MPEG4_ScalarAnimator, + TAG_MPEG4_SimpleTexture, + TAG_MPEG4_SolidRep, + TAG_MPEG4_SubdivisionSurface, + TAG_MPEG4_SubdivSurfaceSector, + TAG_MPEG4_WaveletSubdivisionSurface, + TAG_MPEG4_Clipper2D, + TAG_MPEG4_ColorTransform, + TAG_MPEG4_Ellipse, + TAG_MPEG4_LinearGradient, + TAG_MPEG4_PathLayout, + TAG_MPEG4_RadialGradient, + TAG_MPEG4_SynthesizedTexture, + TAG_MPEG4_TransformMatrix2D, + TAG_MPEG4_Viewport, + TAG_MPEG4_XCurve2D, + TAG_MPEG4_XFontStyle, + TAG_MPEG4_XLineProperties, + TAG_LastImplementedMPEG4 +}; + +typedef struct _tagAnchor +{ + BASE_NODE + VRML_CHILDREN + SFString description; /*exposedField*/ + MFString parameter; /*exposedField*/ + MFURL url; /*exposedField*/ + SFBool activate; /*eventIn*/ + void (*on_activate)(GF_Node *pThis); /*eventInHandler*/ +} M_Anchor; + + +typedef struct _tagAnimationStream +{ + BASE_NODE + SFBool loop; /*exposedField*/ + SFFloat speed; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime stopTime; /*exposedField*/ + MFURL url; /*exposedField*/ + SFTime duration_changed; /*eventOut*/ + SFBool isActive; /*eventOut*/ +} M_AnimationStream; + + +typedef struct _tagAppearance +{ + BASE_NODE + GF_Node *material; /*exposedField*/ + GF_Node *texture; /*exposedField*/ + GF_Node *textureTransform; /*exposedField*/ +} M_Appearance; + + +typedef struct _tagAudioBuffer +{ + BASE_NODE + SFBool loop; /*exposedField*/ + SFFloat pitch; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime stopTime; /*exposedField*/ + GF_ChildNodeItem *children; /*exposedField*/ + SFInt32 numChan; /*exposedField*/ + MFInt32 phaseGroup; /*exposedField*/ + SFFloat length; /*exposedField*/ + SFTime duration_changed; /*eventOut*/ + SFBool isActive; /*eventOut*/ +} M_AudioBuffer; + + +typedef struct _tagAudioClip +{ + BASE_NODE + SFString description; /*exposedField*/ + SFBool loop; /*exposedField*/ + SFFloat pitch; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime stopTime; /*exposedField*/ + MFURL url; /*exposedField*/ + SFTime duration_changed; /*eventOut*/ + SFBool isActive; /*eventOut*/ +} M_AudioClip; + + +typedef struct _tagAudioDelay +{ + BASE_NODE + VRML_CHILDREN + SFTime delay; /*exposedField*/ + SFInt32 numChan; /*field*/ + MFInt32 phaseGroup; /*field*/ +} M_AudioDelay; + + +typedef struct _tagAudioFX +{ + BASE_NODE + VRML_CHILDREN + SFString orch; /*exposedField*/ + SFString score; /*exposedField*/ + MFFloat params; /*exposedField*/ + SFInt32 numChan; /*field*/ + MFInt32 phaseGroup; /*field*/ +} M_AudioFX; + + +typedef struct _tagAudioMix +{ + BASE_NODE + VRML_CHILDREN + SFInt32 numInputs; /*exposedField*/ + MFFloat matrix; /*exposedField*/ + SFInt32 numChan; /*field*/ + MFInt32 phaseGroup; /*field*/ +} M_AudioMix; + + +typedef struct _tagAudioSource +{ + BASE_NODE + VRML_CHILDREN + MFURL url; /*exposedField*/ + SFFloat pitch; /*exposedField*/ + SFFloat speed; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime stopTime; /*exposedField*/ + SFInt32 numChan; /*field*/ + MFInt32 phaseGroup; /*field*/ +} M_AudioSource; + + +typedef struct _tagAudioSwitch +{ + BASE_NODE + VRML_CHILDREN + MFInt32 whichChoice; /*exposedField*/ + SFInt32 numChan; /*field*/ + MFInt32 phaseGroup; /*field*/ +} M_AudioSwitch; + + +typedef struct _tagBackground +{ + BASE_NODE + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + MFFloat groundAngle; /*exposedField*/ + MFColor groundColor; /*exposedField*/ + MFURL backUrl; /*exposedField*/ + MFURL bottomUrl; /*exposedField*/ + MFURL frontUrl; /*exposedField*/ + MFURL leftUrl; /*exposedField*/ + MFURL rightUrl; /*exposedField*/ + MFURL topUrl; /*exposedField*/ + MFFloat skyAngle; /*exposedField*/ + MFColor skyColor; /*exposedField*/ + SFBool isBound; /*eventOut*/ +} M_Background; + + +typedef struct _tagBackground2D +{ + BASE_NODE + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + SFColor backColor; /*exposedField*/ + MFURL url; /*exposedField*/ + SFBool isBound; /*eventOut*/ +} M_Background2D; + + +typedef struct _tagBillboard +{ + BASE_NODE + VRML_CHILDREN + SFVec3f axisOfRotation; /*exposedField*/ +} M_Billboard; + + +typedef struct _tagBitmap +{ + BASE_NODE + SFVec2f scale; /*exposedField*/ +} M_Bitmap; + + +typedef struct _tagBox +{ + BASE_NODE + SFVec3f size; /*field*/ +} M_Box; + + +typedef struct _tagCircle +{ + BASE_NODE + SFFloat radius; /*exposedField*/ +} M_Circle; + + +typedef struct _tagCollision +{ + BASE_NODE + VRML_CHILDREN + SFBool collide; /*exposedField*/ + GF_Node *proxy; /*field*/ + SFTime collideTime; /*eventOut*/ +} M_Collision; + + +typedef struct _tagColor +{ + BASE_NODE + MFColor color; /*exposedField*/ +} M_Color; + + +typedef struct _tagColorInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFColor keyValue; /*exposedField*/ + SFColor value_changed; /*eventOut*/ +} M_ColorInterpolator; + + +typedef struct _tagCompositeTexture2D +{ + BASE_NODE + VRML_CHILDREN + SFInt32 pixelWidth; /*exposedField*/ + SFInt32 pixelHeight; /*exposedField*/ + GF_Node *background; /*exposedField*/ + GF_Node *viewport; /*exposedField*/ + SFInt32 repeatSandT; /*field*/ +} M_CompositeTexture2D; + + +typedef struct _tagCompositeTexture3D +{ + BASE_NODE + VRML_CHILDREN + SFInt32 pixelWidth; /*exposedField*/ + SFInt32 pixelHeight; /*exposedField*/ + GF_Node *background; /*exposedField*/ + GF_Node *fog; /*exposedField*/ + GF_Node *navigationInfo; /*exposedField*/ + GF_Node *viewpoint; /*exposedField*/ + SFBool repeatS; /*field*/ + SFBool repeatT; /*field*/ +} M_CompositeTexture3D; + + +typedef struct _tagConditional +{ + BASE_NODE + SFBool activate; /*eventIn*/ + void (*on_activate)(GF_Node *pThis); /*eventInHandler*/ + SFBool reverseActivate; /*eventIn*/ + void (*on_reverseActivate)(GF_Node *pThis); /*eventInHandler*/ + SFCommandBuffer buffer; /*exposedField*/ + SFBool isActive; /*eventOut*/ +} M_Conditional; + + +typedef struct _tagCone +{ + BASE_NODE + SFFloat bottomRadius; /*field*/ + SFFloat height; /*field*/ + SFBool side; /*field*/ + SFBool bottom; /*field*/ +} M_Cone; + + +typedef struct _tagCoordinate +{ + BASE_NODE + MFVec3f point; /*exposedField*/ +} M_Coordinate; + + +typedef struct _tagCoordinate2D +{ + BASE_NODE + MFVec2f point; /*exposedField*/ +} M_Coordinate2D; + + +typedef struct _tagCoordinateInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec3f keyValue; /*exposedField*/ + MFVec3f value_changed; /*eventOut*/ +} M_CoordinateInterpolator; + + +typedef struct _tagCoordinateInterpolator2D +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec2f keyValue; /*exposedField*/ + MFVec2f value_changed; /*eventOut*/ +} M_CoordinateInterpolator2D; + + +typedef struct _tagCurve2D +{ + BASE_NODE + GF_Node *point; /*exposedField*/ + SFFloat fineness; /*exposedField*/ + MFInt32 type; /*exposedField*/ +} M_Curve2D; + + +typedef struct _tagCylinder +{ + BASE_NODE + SFBool bottom; /*field*/ + SFFloat height; /*field*/ + SFFloat radius; /*field*/ + SFBool side; /*field*/ + SFBool top; /*field*/ +} M_Cylinder; + + +typedef struct _tagCylinderSensor +{ + BASE_NODE + SFBool autoOffset; /*exposedField*/ + SFFloat diskAngle; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFFloat maxAngle; /*exposedField*/ + SFFloat minAngle; /*exposedField*/ + SFFloat offset; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFRotation rotation_changed; /*eventOut*/ + SFVec3f trackPoint_changed; /*eventOut*/ +} M_CylinderSensor; + + +typedef struct _tagDirectionalLight +{ + BASE_NODE + SFFloat ambientIntensity; /*exposedField*/ + SFColor color; /*exposedField*/ + SFVec3f direction; /*exposedField*/ + SFFloat intensity; /*exposedField*/ + SFBool on; /*exposedField*/ +} M_DirectionalLight; + + +typedef struct _tagDiscSensor +{ + BASE_NODE + SFBool autoOffset; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFFloat maxAngle; /*exposedField*/ + SFFloat minAngle; /*exposedField*/ + SFFloat offset; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFFloat rotation_changed; /*eventOut*/ + SFVec2f trackPoint_changed; /*eventOut*/ +} M_DiscSensor; + + +typedef struct _tagElevationGrid +{ + BASE_NODE + MFFloat set_height; /*eventIn*/ + void (*on_set_height)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *normal; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + MFFloat height; /*field*/ + SFBool ccw; /*field*/ + SFBool colorPerVertex; /*field*/ + SFFloat creaseAngle; /*field*/ + SFBool normalPerVertex; /*field*/ + SFBool solid; /*field*/ + SFInt32 xDimension; /*field*/ + SFFloat xSpacing; /*field*/ + SFInt32 zDimension; /*field*/ + SFFloat zSpacing; /*field*/ +} M_ElevationGrid; + + +typedef struct _tagExtrusion +{ + BASE_NODE + MFVec2f set_crossSection; /*eventIn*/ + void (*on_set_crossSection)(GF_Node *pThis); /*eventInHandler*/ + MFRotation set_orientation; /*eventIn*/ + void (*on_set_orientation)(GF_Node *pThis); /*eventInHandler*/ + MFVec2f set_scale; /*eventIn*/ + void (*on_set_scale)(GF_Node *pThis); /*eventInHandler*/ + MFVec3f set_spine; /*eventIn*/ + void (*on_set_spine)(GF_Node *pThis); /*eventInHandler*/ + SFBool beginCap; /*field*/ + SFBool ccw; /*field*/ + SFBool convex; /*field*/ + SFFloat creaseAngle; /*field*/ + MFVec2f crossSection; /*field*/ + SFBool endCap; /*field*/ + MFRotation orientation; /*field*/ + MFVec2f scale; /*field*/ + SFBool solid; /*field*/ + MFVec3f spine; /*field*/ +} M_Extrusion; + + +typedef struct _tagFog +{ + BASE_NODE + SFColor color; /*exposedField*/ + SFString fogType; /*exposedField*/ + SFFloat visibilityRange; /*exposedField*/ + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + SFBool isBound; /*eventOut*/ +} M_Fog; + + +typedef struct _tagFontStyle +{ + BASE_NODE + MFString family; /*exposedField*/ + SFBool horizontal; /*exposedField*/ + MFString justify; /*exposedField*/ + SFString language; /*exposedField*/ + SFBool leftToRight; /*exposedField*/ + SFFloat size; /*exposedField*/ + SFFloat spacing; /*exposedField*/ + SFString style; /*exposedField*/ + SFBool topToBottom; /*exposedField*/ +} M_FontStyle; + + +typedef struct _tagForm +{ + BASE_NODE + VRML_CHILDREN + SFVec2f size; /*exposedField*/ + MFInt32 groups; /*exposedField*/ + MFString constraints; /*exposedField*/ + MFInt32 groupsIndex; /*exposedField*/ +} M_Form; + + +typedef struct _tagGroup +{ + BASE_NODE + VRML_CHILDREN +} M_Group; + + +typedef struct _tagImageTexture +{ + BASE_NODE + MFURL url; /*exposedField*/ + SFBool repeatS; /*field*/ + SFBool repeatT; /*field*/ +} M_ImageTexture; + + +typedef struct _tagIndexedFaceSet +{ + BASE_NODE + MFInt32 set_colorIndex; /*eventIn*/ + void (*on_set_colorIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_coordIndex; /*eventIn*/ + void (*on_set_coordIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_normalIndex; /*eventIn*/ + void (*on_set_normalIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_texCoordIndex; /*eventIn*/ + void (*on_set_texCoordIndex)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + GF_Node *normal; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + SFBool ccw; /*field*/ + MFInt32 colorIndex; /*field*/ + SFBool colorPerVertex; /*field*/ + SFBool convex; /*field*/ + MFInt32 coordIndex; /*field*/ + SFFloat creaseAngle; /*field*/ + MFInt32 normalIndex; /*field*/ + SFBool normalPerVertex; /*field*/ + SFBool solid; /*field*/ + MFInt32 texCoordIndex; /*field*/ +} M_IndexedFaceSet; + + +typedef struct _tagIndexedFaceSet2D +{ + BASE_NODE + MFInt32 set_colorIndex; /*eventIn*/ + void (*on_set_colorIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_coordIndex; /*eventIn*/ + void (*on_set_coordIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_texCoordIndex; /*eventIn*/ + void (*on_set_texCoordIndex)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + MFInt32 colorIndex; /*field*/ + SFBool colorPerVertex; /*field*/ + SFBool convex; /*field*/ + MFInt32 coordIndex; /*field*/ + MFInt32 texCoordIndex; /*field*/ +} M_IndexedFaceSet2D; + + +typedef struct _tagIndexedLineSet +{ + BASE_NODE + MFInt32 set_colorIndex; /*eventIn*/ + void (*on_set_colorIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_coordIndex; /*eventIn*/ + void (*on_set_coordIndex)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + MFInt32 colorIndex; /*field*/ + SFBool colorPerVertex; /*field*/ + MFInt32 coordIndex; /*field*/ +} M_IndexedLineSet; + + +typedef struct _tagIndexedLineSet2D +{ + BASE_NODE + MFInt32 set_colorIndex; /*eventIn*/ + void (*on_set_colorIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_coordIndex; /*eventIn*/ + void (*on_set_coordIndex)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + MFInt32 colorIndex; /*field*/ + SFBool colorPerVertex; /*field*/ + MFInt32 coordIndex; /*field*/ +} M_IndexedLineSet2D; + + +typedef struct _tagInline +{ + BASE_NODE + MFURL url; /*exposedField*/ +} M_Inline; + + +typedef struct _tagLOD +{ + BASE_NODE + GF_ChildNodeItem *level; /*exposedField*/ + SFVec3f center; /*field*/ + MFFloat range; /*field*/ +} M_LOD; + + +typedef struct _tagLayer2D +{ + BASE_NODE + VRML_CHILDREN + SFVec2f size; /*exposedField*/ + GF_Node *background; /*exposedField*/ + GF_Node *viewport; /*exposedField*/ +} M_Layer2D; + + +typedef struct _tagLayer3D +{ + BASE_NODE + VRML_CHILDREN + SFVec2f size; /*exposedField*/ + GF_Node *background; /*exposedField*/ + GF_Node *fog; /*exposedField*/ + GF_Node *navigationInfo; /*exposedField*/ + GF_Node *viewpoint; /*exposedField*/ +} M_Layer3D; + + +typedef struct _tagLayout +{ + BASE_NODE + VRML_CHILDREN + SFBool wrap; /*exposedField*/ + SFVec2f size; /*exposedField*/ + SFBool horizontal; /*exposedField*/ + MFString justify; /*exposedField*/ + SFBool leftToRight; /*exposedField*/ + SFBool topToBottom; /*exposedField*/ + SFFloat spacing; /*exposedField*/ + SFBool smoothScroll; /*exposedField*/ + SFBool loop; /*exposedField*/ + SFBool scrollVertical; /*exposedField*/ + SFFloat scrollRate; /*exposedField*/ + SFInt32 scrollMode; /*exposedField*/ +} M_Layout; + + +typedef struct _tagLineProperties +{ + BASE_NODE + SFColor lineColor; /*exposedField*/ + SFInt32 lineStyle; /*exposedField*/ + SFFloat width; /*exposedField*/ +} M_LineProperties; + + +typedef struct _tagListeningPoint +{ + BASE_NODE + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + SFBool jump; /*exposedField*/ + SFRotation orientation; /*exposedField*/ + SFVec3f position; /*exposedField*/ + SFString description; /*field*/ + SFTime bindTime; /*eventOut*/ + SFBool isBound; /*eventOut*/ +} M_ListeningPoint; + + +typedef struct _tagMaterial +{ + BASE_NODE + SFFloat ambientIntensity; /*exposedField*/ + SFColor diffuseColor; /*exposedField*/ + SFColor emissiveColor; /*exposedField*/ + SFFloat shininess; /*exposedField*/ + SFColor specularColor; /*exposedField*/ + SFFloat transparency; /*exposedField*/ +} M_Material; + + +typedef struct _tagMaterial2D +{ + BASE_NODE + SFColor emissiveColor; /*exposedField*/ + SFBool filled; /*exposedField*/ + GF_Node *lineProps; /*exposedField*/ + SFFloat transparency; /*exposedField*/ +} M_Material2D; + + +typedef struct _tagMovieTexture +{ + BASE_NODE + SFBool loop; /*exposedField*/ + SFFloat speed; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime stopTime; /*exposedField*/ + MFURL url; /*exposedField*/ + SFBool repeatS; /*field*/ + SFBool repeatT; /*field*/ + SFTime duration_changed; /*eventOut*/ + SFBool isActive; /*eventOut*/ +} M_MovieTexture; + + +typedef struct _tagNavigationInfo +{ + BASE_NODE + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + MFFloat avatarSize; /*exposedField*/ + SFBool headlight; /*exposedField*/ + SFFloat speed; /*exposedField*/ + MFString type; /*exposedField*/ + SFFloat visibilityLimit; /*exposedField*/ + SFBool isBound; /*eventOut*/ +} M_NavigationInfo; + + +typedef struct _tagNormal +{ + BASE_NODE + MFVec3f vector; /*exposedField*/ +} M_Normal; + + +typedef struct _tagNormalInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec3f keyValue; /*exposedField*/ + MFVec3f value_changed; /*eventOut*/ +} M_NormalInterpolator; + + +typedef struct _tagOrderedGroup +{ + BASE_NODE + VRML_CHILDREN + MFFloat order; /*exposedField*/ +} M_OrderedGroup; + + +typedef struct _tagOrientationInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFRotation keyValue; /*exposedField*/ + SFRotation value_changed; /*eventOut*/ +} M_OrientationInterpolator; + + +typedef struct _tagPixelTexture +{ + BASE_NODE + SFImage image; /*exposedField*/ + SFBool repeatS; /*field*/ + SFBool repeatT; /*field*/ +} M_PixelTexture; + + +typedef struct _tagPlaneSensor +{ + BASE_NODE + SFBool autoOffset; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFVec2f maxPosition; /*exposedField*/ + SFVec2f minPosition; /*exposedField*/ + SFVec3f offset; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFVec3f trackPoint_changed; /*eventOut*/ + SFVec3f translation_changed; /*eventOut*/ +} M_PlaneSensor; + + +typedef struct _tagPlaneSensor2D +{ + BASE_NODE + SFBool autoOffset; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFVec2f maxPosition; /*exposedField*/ + SFVec2f minPosition; /*exposedField*/ + SFVec2f offset; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFVec2f trackPoint_changed; /*eventOut*/ + SFVec2f translation_changed; /*eventOut*/ +} M_PlaneSensor2D; + + +typedef struct _tagPointLight +{ + BASE_NODE + SFFloat ambientIntensity; /*exposedField*/ + SFVec3f attenuation; /*exposedField*/ + SFColor color; /*exposedField*/ + SFFloat intensity; /*exposedField*/ + SFVec3f location; /*exposedField*/ + SFBool on; /*exposedField*/ + SFFloat radius; /*exposedField*/ +} M_PointLight; + + +typedef struct _tagPointSet +{ + BASE_NODE + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ +} M_PointSet; + + +typedef struct _tagPointSet2D +{ + BASE_NODE + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ +} M_PointSet2D; + + +typedef struct _tagPositionInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec3f keyValue; /*exposedField*/ + SFVec3f value_changed; /*eventOut*/ +} M_PositionInterpolator; + + +typedef struct _tagPositionInterpolator2D +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec2f keyValue; /*exposedField*/ + SFVec2f value_changed; /*eventOut*/ +} M_PositionInterpolator2D; + + +typedef struct _tagProximitySensor2D +{ + BASE_NODE + SFVec2f center; /*exposedField*/ + SFVec2f size; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFVec2f position_changed; /*eventOut*/ + SFFloat orientation_changed; /*eventOut*/ + SFTime enterTime; /*eventOut*/ + SFTime exitTime; /*eventOut*/ +} M_ProximitySensor2D; + + +typedef struct _tagProximitySensor +{ + BASE_NODE + SFVec3f center; /*exposedField*/ + SFVec3f size; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFVec3f position_changed; /*eventOut*/ + SFRotation orientation_changed; /*eventOut*/ + SFTime enterTime; /*eventOut*/ + SFTime exitTime; /*eventOut*/ +} M_ProximitySensor; + + +typedef struct _tagQuantizationParameter +{ + BASE_NODE + SFBool isLocal; /*field*/ + SFBool position3DQuant; /*field*/ + SFVec3f position3DMin; /*field*/ + SFVec3f position3DMax; /*field*/ + SFInt32 position3DNbBits; /*field*/ + SFBool position2DQuant; /*field*/ + SFVec2f position2DMin; /*field*/ + SFVec2f position2DMax; /*field*/ + SFInt32 position2DNbBits; /*field*/ + SFBool drawOrderQuant; /*field*/ + SFFloat drawOrderMin; /*field*/ + SFFloat drawOrderMax; /*field*/ + SFInt32 drawOrderNbBits; /*field*/ + SFBool colorQuant; /*field*/ + SFFloat colorMin; /*field*/ + SFFloat colorMax; /*field*/ + SFInt32 colorNbBits; /*field*/ + SFBool textureCoordinateQuant; /*field*/ + SFFloat textureCoordinateMin; /*field*/ + SFFloat textureCoordinateMax; /*field*/ + SFInt32 textureCoordinateNbBits; /*field*/ + SFBool angleQuant; /*field*/ + SFFloat angleMin; /*field*/ + SFFloat angleMax; /*field*/ + SFInt32 angleNbBits; /*field*/ + SFBool scaleQuant; /*field*/ + SFFloat scaleMin; /*field*/ + SFFloat scaleMax; /*field*/ + SFInt32 scaleNbBits; /*field*/ + SFBool keyQuant; /*field*/ + SFFloat keyMin; /*field*/ + SFFloat keyMax; /*field*/ + SFInt32 keyNbBits; /*field*/ + SFBool normalQuant; /*field*/ + SFInt32 normalNbBits; /*field*/ + SFBool sizeQuant; /*field*/ + SFFloat sizeMin; /*field*/ + SFFloat sizeMax; /*field*/ + SFInt32 sizeNbBits; /*field*/ + SFBool useEfficientCoding; /*field*/ +} M_QuantizationParameter; + + +typedef struct _tagRectangle +{ + BASE_NODE + SFVec2f size; /*exposedField*/ +} M_Rectangle; + + +typedef struct _tagScalarInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFFloat keyValue; /*exposedField*/ + SFFloat value_changed; /*eventOut*/ +} M_ScalarInterpolator; + + +typedef struct _tagScript +{ + BASE_NODE + MFScript url; /*exposedField*/ + SFBool directOutput; /*field*/ + SFBool mustEvaluate; /*field*/ +} M_Script; + + +typedef struct _tagShape +{ + BASE_NODE + GF_Node *appearance; /*exposedField*/ + GF_Node *geometry; /*exposedField*/ +} M_Shape; + + +typedef struct _tagSound +{ + BASE_NODE + SFVec3f direction; /*exposedField*/ + SFFloat intensity; /*exposedField*/ + SFVec3f location; /*exposedField*/ + SFFloat maxBack; /*exposedField*/ + SFFloat maxFront; /*exposedField*/ + SFFloat minBack; /*exposedField*/ + SFFloat minFront; /*exposedField*/ + SFFloat priority; /*exposedField*/ + GF_Node *source; /*exposedField*/ + SFBool spatialize; /*field*/ +} M_Sound; + + +typedef struct _tagSound2D +{ + BASE_NODE + SFFloat intensity; /*exposedField*/ + SFVec2f location; /*exposedField*/ + GF_Node *source; /*exposedField*/ + SFBool spatialize; /*field*/ +} M_Sound2D; + + +typedef struct _tagSphere +{ + BASE_NODE + SFFloat radius; /*field*/ +} M_Sphere; + + +typedef struct _tagSphereSensor +{ + BASE_NODE + SFBool autoOffset; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFRotation offset; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFRotation rotation_changed; /*eventOut*/ + SFVec3f trackPoint_changed; /*eventOut*/ +} M_SphereSensor; + + +typedef struct _tagSpotLight +{ + BASE_NODE + SFFloat ambientIntensity; /*exposedField*/ + SFVec3f attenuation; /*exposedField*/ + SFFloat beamWidth; /*exposedField*/ + SFColor color; /*exposedField*/ + SFFloat cutOffAngle; /*exposedField*/ + SFVec3f direction; /*exposedField*/ + SFFloat intensity; /*exposedField*/ + SFVec3f location; /*exposedField*/ + SFBool on; /*exposedField*/ + SFFloat radius; /*exposedField*/ +} M_SpotLight; + + +typedef struct _tagSwitch +{ + BASE_NODE + GF_ChildNodeItem *choice; /*exposedField*/ + SFInt32 whichChoice; /*exposedField*/ +} M_Switch; + + +typedef struct _tagTermCap +{ + BASE_NODE + SFTime evaluate; /*eventIn*/ + void (*on_evaluate)(GF_Node *pThis); /*eventInHandler*/ + SFInt32 capability; /*exposedField*/ + SFInt32 value; /*eventOut*/ +} M_TermCap; + + +typedef struct _tagText +{ + BASE_NODE + MFString string; /*exposedField*/ + MFFloat length; /*exposedField*/ + GF_Node *fontStyle; /*exposedField*/ + SFFloat maxExtent; /*exposedField*/ +} M_Text; + + +typedef struct _tagTextureCoordinate +{ + BASE_NODE + MFVec2f point; /*exposedField*/ +} M_TextureCoordinate; + + +typedef struct _tagTextureTransform +{ + BASE_NODE + SFVec2f center; /*exposedField*/ + SFFloat rotation; /*exposedField*/ + SFVec2f scale; /*exposedField*/ + SFVec2f translation; /*exposedField*/ +} M_TextureTransform; + + +typedef struct _tagTimeSensor +{ + BASE_NODE + SFTime cycleInterval; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFBool loop; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime stopTime; /*exposedField*/ + SFTime cycleTime; /*eventOut*/ + SFFloat fraction_changed; /*eventOut*/ + SFBool isActive; /*eventOut*/ + SFTime time; /*eventOut*/ +} M_TimeSensor; + + +typedef struct _tagTouchSensor +{ + BASE_NODE + SFBool enabled; /*exposedField*/ + SFVec3f hitNormal_changed; /*eventOut*/ + SFVec3f hitPoint_changed; /*eventOut*/ + SFVec2f hitTexCoord_changed; /*eventOut*/ + SFBool isActive; /*eventOut*/ + SFBool isOver; /*eventOut*/ + SFTime touchTime; /*eventOut*/ +} M_TouchSensor; + + +typedef struct _tagTransform +{ + BASE_NODE + VRML_CHILDREN + SFVec3f center; /*exposedField*/ + SFRotation rotation; /*exposedField*/ + SFVec3f scale; /*exposedField*/ + SFRotation scaleOrientation; /*exposedField*/ + SFVec3f translation; /*exposedField*/ +} M_Transform; + + +typedef struct _tagTransform2D +{ + BASE_NODE + VRML_CHILDREN + SFVec2f center; /*exposedField*/ + SFFloat rotationAngle; /*exposedField*/ + SFVec2f scale; /*exposedField*/ + SFFloat scaleOrientation; /*exposedField*/ + SFVec2f translation; /*exposedField*/ +} M_Transform2D; + + +typedef struct _tagValuator +{ + BASE_NODE + SFBool inSFBool; /*eventIn*/ + void (*on_inSFBool)(GF_Node *pThis); /*eventInHandler*/ + SFColor inSFColor; /*eventIn*/ + void (*on_inSFColor)(GF_Node *pThis); /*eventInHandler*/ + MFColor inMFColor; /*eventIn*/ + void (*on_inMFColor)(GF_Node *pThis); /*eventInHandler*/ + SFFloat inSFFloat; /*eventIn*/ + void (*on_inSFFloat)(GF_Node *pThis); /*eventInHandler*/ + MFFloat inMFFloat; /*eventIn*/ + void (*on_inMFFloat)(GF_Node *pThis); /*eventInHandler*/ + SFInt32 inSFInt32; /*eventIn*/ + void (*on_inSFInt32)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 inMFInt32; /*eventIn*/ + void (*on_inMFInt32)(GF_Node *pThis); /*eventInHandler*/ + SFRotation inSFRotation; /*eventIn*/ + void (*on_inSFRotation)(GF_Node *pThis); /*eventInHandler*/ + MFRotation inMFRotation; /*eventIn*/ + void (*on_inMFRotation)(GF_Node *pThis); /*eventInHandler*/ + SFString inSFString; /*eventIn*/ + void (*on_inSFString)(GF_Node *pThis); /*eventInHandler*/ + MFString inMFString; /*eventIn*/ + void (*on_inMFString)(GF_Node *pThis); /*eventInHandler*/ + SFTime inSFTime; /*eventIn*/ + void (*on_inSFTime)(GF_Node *pThis); /*eventInHandler*/ + SFVec2f inSFVec2f; /*eventIn*/ + void (*on_inSFVec2f)(GF_Node *pThis); /*eventInHandler*/ + MFVec2f inMFVec2f; /*eventIn*/ + void (*on_inMFVec2f)(GF_Node *pThis); /*eventInHandler*/ + SFVec3f inSFVec3f; /*eventIn*/ + void (*on_inSFVec3f)(GF_Node *pThis); /*eventInHandler*/ + MFVec3f inMFVec3f; /*eventIn*/ + void (*on_inMFVec3f)(GF_Node *pThis); /*eventInHandler*/ + SFBool outSFBool; /*eventOut*/ + SFColor outSFColor; /*eventOut*/ + MFColor outMFColor; /*eventOut*/ + SFFloat outSFFloat; /*eventOut*/ + MFFloat outMFFloat; /*eventOut*/ + SFInt32 outSFInt32; /*eventOut*/ + MFInt32 outMFInt32; /*eventOut*/ + SFRotation outSFRotation; /*eventOut*/ + MFRotation outMFRotation; /*eventOut*/ + SFString outSFString; /*eventOut*/ + MFString outMFString; /*eventOut*/ + SFTime outSFTime; /*eventOut*/ + SFVec2f outSFVec2f; /*eventOut*/ + MFVec2f outMFVec2f; /*eventOut*/ + SFVec3f outSFVec3f; /*eventOut*/ + MFVec3f outMFVec3f; /*eventOut*/ + SFFloat Factor1; /*exposedField*/ + SFFloat Factor2; /*exposedField*/ + SFFloat Factor3; /*exposedField*/ + SFFloat Factor4; /*exposedField*/ + SFFloat Offset1; /*exposedField*/ + SFFloat Offset2; /*exposedField*/ + SFFloat Offset3; /*exposedField*/ + SFFloat Offset4; /*exposedField*/ + SFBool Sum; /*exposedField*/ +} M_Valuator; + + +typedef struct _tagViewpoint +{ + BASE_NODE + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + SFFloat fieldOfView; /*exposedField*/ + SFBool jump; /*exposedField*/ + SFRotation orientation; /*exposedField*/ + SFVec3f position; /*exposedField*/ + SFString description; /*field*/ + SFTime bindTime; /*eventOut*/ + SFBool isBound; /*eventOut*/ +} M_Viewpoint; + + +typedef struct _tagVisibilitySensor +{ + BASE_NODE + SFVec3f center; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFVec3f size; /*exposedField*/ + SFTime enterTime; /*eventOut*/ + SFTime exitTime; /*eventOut*/ + SFBool isActive; /*eventOut*/ +} M_VisibilitySensor; + + +typedef struct _tagWorldInfo +{ + BASE_NODE + MFString info; /*field*/ + SFString title; /*field*/ +} M_WorldInfo; + + +typedef struct _tagAcousticMaterial +{ + BASE_NODE + SFFloat ambientIntensity; /*exposedField*/ + SFColor diffuseColor; /*exposedField*/ + SFColor emissiveColor; /*exposedField*/ + SFFloat shininess; /*exposedField*/ + SFColor specularColor; /*exposedField*/ + SFFloat transparency; /*exposedField*/ + MFFloat reffunc; /*field*/ + MFFloat transfunc; /*field*/ + MFFloat refFrequency; /*field*/ + MFFloat transFrequency; /*field*/ +} M_AcousticMaterial; + + +typedef struct _tagAcousticScene +{ + BASE_NODE + SFVec3f center; /*field*/ + SFVec3f Size; /*field*/ + MFTime reverbTime; /*field*/ + MFFloat reverbFreq; /*field*/ + SFFloat reverbLevel; /*exposedField*/ + SFTime reverbDelay; /*exposedField*/ +} M_AcousticScene; + + +typedef struct _tagApplicationWindow +{ + BASE_NODE + SFBool isActive; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime stopTime; /*exposedField*/ + SFString description; /*exposedField*/ + MFString parameter; /*exposedField*/ + MFURL url; /*exposedField*/ + SFVec2f size; /*exposedField*/ +} M_ApplicationWindow; + + +typedef struct _tagDirectiveSound +{ + BASE_NODE + SFVec3f direction; /*exposedField*/ + SFFloat intensity; /*exposedField*/ + SFVec3f location; /*exposedField*/ + GF_Node *source; /*exposedField*/ + GF_Node *perceptualParameters; /*exposedField*/ + SFBool roomEffect; /*exposedField*/ + SFBool spatialize; /*exposedField*/ + MFFloat directivity; /*field*/ + MFFloat angles; /*field*/ + MFFloat frequency; /*field*/ + SFFloat speedOfSound; /*field*/ + SFFloat distance; /*field*/ + SFBool useAirabs; /*field*/ +} M_DirectiveSound; + + +typedef struct _tagHierarchical3DMesh +{ + BASE_NODE + SFInt32 triangleBudget; /*eventIn*/ + void (*on_triangleBudget)(GF_Node *pThis); /*eventInHandler*/ + SFFloat level; /*exposedField*/ + MFURL url; /*field*/ + SFBool doneLoading; /*eventOut*/ +} M_Hierarchical3DMesh; + + +typedef struct _tagMaterialKey +{ + BASE_NODE + SFBool isKeyed; /*exposedField*/ + SFBool isRGB; /*exposedField*/ + SFColor keyColor; /*exposedField*/ + SFFloat lowThreshold; /*exposedField*/ + SFFloat highThreshold; /*exposedField*/ + SFFloat transparency; /*exposedField*/ +} M_MaterialKey; + + +typedef struct _tagPerceptualParameters +{ + BASE_NODE + SFFloat sourcePresence; /*exposedField*/ + SFFloat sourceWarmth; /*exposedField*/ + SFFloat sourceBrilliance; /*exposedField*/ + SFFloat roomPresence; /*exposedField*/ + SFFloat runningReverberance; /*exposedField*/ + SFFloat envelopment; /*exposedField*/ + SFFloat lateReverberance; /*exposedField*/ + SFFloat heavyness; /*exposedField*/ + SFFloat liveness; /*exposedField*/ + MFFloat omniDirectivity; /*exposedField*/ + MFFloat directFilterGains; /*exposedField*/ + MFFloat inputFilterGains; /*exposedField*/ + SFFloat refDistance; /*exposedField*/ + SFFloat freqLow; /*exposedField*/ + SFFloat freqHigh; /*exposedField*/ + SFTime timeLimit1; /*exposedField*/ + SFTime timeLimit2; /*exposedField*/ + SFTime timeLimit3; /*exposedField*/ + SFTime modalDensity; /*exposedField*/ +} M_PerceptualParameters; + + +typedef struct _tagTemporalTransform +{ + BASE_NODE + VRML_CHILDREN + MFURL url; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime optimalDuration; /*exposedField*/ + SFBool active; /*exposedField*/ + SFFloat speed; /*exposedField*/ + SFVec2f scalability; /*exposedField*/ + MFInt32 stretchMode; /*exposedField*/ + MFInt32 shrinkMode; /*exposedField*/ + SFTime maxDelay; /*exposedField*/ + SFTime actualDuration; /*eventOut*/ +} M_TemporalTransform; + + +typedef struct _tagTemporalGroup +{ + BASE_NODE + VRML_CHILDREN + SFBool costart; /*field*/ + SFBool coend; /*field*/ + SFBool meet; /*field*/ + MFFloat priority; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFInt32 activeChild; /*eventOut*/ +} M_TemporalGroup; + + +typedef struct _tagServerCommand +{ + BASE_NODE + SFBool trigger; /*eventIn*/ + void (*on_trigger)(GF_Node *pThis); /*eventInHandler*/ + SFBool enable; /*exposedField*/ + MFURL url; /*exposedField*/ + SFString command; /*exposedField*/ +} M_ServerCommand; + + +typedef struct _tagInputSensor +{ + BASE_NODE + SFBool enabled; /*exposedField*/ + SFCommandBuffer buffer; /*exposedField*/ + MFURL url; /*exposedField*/ + SFTime eventTime; /*eventOut*/ +} M_InputSensor; + + +typedef struct _tagMatteTexture +{ + BASE_NODE + GF_Node *surfaceA; /*field*/ + GF_Node *surfaceB; /*field*/ + GF_Node *alphaSurface; /*field*/ + SFString operation; /*exposedField*/ + SFBool overwrite; /*field*/ + SFFloat fraction; /*exposedField*/ + MFFloat parameter; /*exposedField*/ +} M_MatteTexture; + + +typedef struct _tagMediaBuffer +{ + BASE_NODE + SFFloat bufferSize; /*exposedField*/ + MFURL url; /*exposedField*/ + SFTime mediaStartTime; /*exposedField*/ + SFTime mediaStopTime; /*exposedField*/ + SFBool isBuffered; /*eventOut*/ + SFBool enabled; /*exposedField*/ +} M_MediaBuffer; + + +typedef struct _tagMediaControl +{ + BASE_NODE + MFURL url; /*exposedField*/ + SFTime mediaStartTime; /*exposedField*/ + SFTime mediaStopTime; /*exposedField*/ + SFFloat mediaSpeed; /*exposedField*/ + SFBool loop; /*exposedField*/ + SFBool preRoll; /*exposedField*/ + SFBool mute; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFBool isPreRolled; /*eventOut*/ +} M_MediaControl; + + +typedef struct _tagMediaSensor +{ + BASE_NODE + MFURL url; /*exposedField*/ + SFTime mediaCurrentTime; /*eventOut*/ + SFTime streamObjectStartTime; /*eventOut*/ + SFTime mediaDuration; /*eventOut*/ + SFBool isActive; /*eventOut*/ + MFString info; /*eventOut*/ +} M_MediaSensor; + + +typedef struct _tagCoordinateInterpolator4D +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec4f keyValue; /*exposedField*/ + MFVec4f value_changed; /*eventOut*/ +} M_CoordinateInterpolator4D; + + +typedef struct _tagNonLinearDeformer +{ + BASE_NODE + SFVec3f axis; /*exposedField*/ + MFFloat extend; /*exposedField*/ + GF_Node *geometry; /*exposedField*/ + SFFloat param; /*exposedField*/ + SFInt32 type; /*exposedField*/ +} M_NonLinearDeformer; + + +typedef struct _tagPositionAnimator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + SFVec2f fromTo; /*exposedField*/ + MFFloat key; /*exposedField*/ + MFRotation keyOrientation; /*exposedField*/ + SFInt32 keyType; /*exposedField*/ + MFVec2f keySpline; /*exposedField*/ + MFVec3f keyValue; /*exposedField*/ + SFInt32 keyValueType; /*exposedField*/ + SFVec3f offset; /*exposedField*/ + MFFloat weight; /*exposedField*/ + SFVec3f endValue; /*eventOut*/ + SFRotation rotation_changed; /*eventOut*/ + SFVec3f value_changed; /*eventOut*/ +} M_PositionAnimator; + + +typedef struct _tagPositionAnimator2D +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + SFVec2f fromTo; /*exposedField*/ + MFFloat key; /*exposedField*/ + SFInt32 keyOrientation; /*exposedField*/ + SFInt32 keyType; /*exposedField*/ + MFVec2f keySpline; /*exposedField*/ + MFVec2f keyValue; /*exposedField*/ + SFInt32 keyValueType; /*exposedField*/ + SFVec2f offset; /*exposedField*/ + MFFloat weight; /*exposedField*/ + SFVec2f endValue; /*eventOut*/ + SFFloat rotation_changed; /*eventOut*/ + SFVec2f value_changed; /*eventOut*/ +} M_PositionAnimator2D; + + +typedef struct _tagPositionInterpolator4D +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec4f keyValue; /*exposedField*/ + SFVec4f value_changed; /*eventOut*/ +} M_PositionInterpolator4D; + + +typedef struct _tagScalarAnimator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + SFVec2f fromTo; /*exposedField*/ + MFFloat key; /*exposedField*/ + SFInt32 keyType; /*exposedField*/ + MFVec2f keySpline; /*exposedField*/ + MFFloat keyValue; /*exposedField*/ + SFInt32 keyValueType; /*exposedField*/ + SFFloat offset; /*exposedField*/ + MFFloat weight; /*exposedField*/ + SFFloat endValue; /*eventOut*/ + SFFloat value_changed; /*eventOut*/ +} M_ScalarAnimator; + + +typedef struct _tagClipper2D +{ + BASE_NODE + VRML_CHILDREN + GF_Node *geometry; /*exposedField*/ + SFBool inside; /*exposedField*/ + GF_Node *transform; /*exposedField*/ + SFBool XOR; /*exposedField*/ +} M_Clipper2D; + + +typedef struct _tagColorTransform +{ + BASE_NODE + VRML_CHILDREN + SFFloat mrr; /*exposedField*/ + SFFloat mrg; /*exposedField*/ + SFFloat mrb; /*exposedField*/ + SFFloat mra; /*exposedField*/ + SFFloat tr; /*exposedField*/ + SFFloat mgr; /*exposedField*/ + SFFloat mgg; /*exposedField*/ + SFFloat mgb; /*exposedField*/ + SFFloat mga; /*exposedField*/ + SFFloat tg; /*exposedField*/ + SFFloat mbr; /*exposedField*/ + SFFloat mbg; /*exposedField*/ + SFFloat mbb; /*exposedField*/ + SFFloat mba; /*exposedField*/ + SFFloat tb; /*exposedField*/ + SFFloat mar; /*exposedField*/ + SFFloat mag; /*exposedField*/ + SFFloat mab; /*exposedField*/ + SFFloat maa; /*exposedField*/ + SFFloat ta; /*exposedField*/ +} M_ColorTransform; + + +typedef struct _tagEllipse +{ + BASE_NODE + SFVec2f radius; /*exposedField*/ +} M_Ellipse; + + +typedef struct _tagLinearGradient +{ + BASE_NODE + SFVec2f endPoint; /*exposedField*/ + MFFloat key; /*exposedField*/ + MFColor keyValue; /*exposedField*/ + MFFloat opacity; /*exposedField*/ + SFInt32 spreadMethod; /*exposedField*/ + SFVec2f startPoint; /*exposedField*/ + GF_Node *transform; /*exposedField*/ +} M_LinearGradient; + + +typedef struct _tagPathLayout +{ + BASE_NODE + VRML_CHILDREN + GF_Node *geometry; /*exposedField*/ + MFInt32 alignment; /*exposedField*/ + SFFloat pathOffset; /*exposedField*/ + SFFloat spacing; /*exposedField*/ + SFBool reverseLayout; /*exposedField*/ + SFInt32 wrapMode; /*exposedField*/ + SFBool splitText; /*exposedField*/ +} M_PathLayout; + + +typedef struct _tagRadialGradient +{ + BASE_NODE + SFVec2f center; /*exposedField*/ + SFVec2f focalPoint; /*exposedField*/ + MFFloat key; /*exposedField*/ + MFColor keyValue; /*exposedField*/ + MFFloat opacity; /*exposedField*/ + SFFloat radius; /*exposedField*/ + SFInt32 spreadMethod; /*exposedField*/ + GF_Node *transform; /*exposedField*/ +} M_RadialGradient; + + +typedef struct _tagTransformMatrix2D +{ + BASE_NODE + VRML_CHILDREN + SFFloat mxx; /*exposedField*/ + SFFloat mxy; /*exposedField*/ + SFFloat tx; /*exposedField*/ + SFFloat myx; /*exposedField*/ + SFFloat myy; /*exposedField*/ + SFFloat ty; /*exposedField*/ +} M_TransformMatrix2D; + + +typedef struct _tagViewport +{ + BASE_NODE + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + SFVec2f position; /*exposedField*/ + SFVec2f size; /*exposedField*/ + SFFloat orientation; /*exposedField*/ + MFInt32 alignment; /*exposedField*/ + SFInt32 fit; /*exposedField*/ + SFString description; /*field*/ + SFTime bindTime; /*eventOut*/ + SFBool isBound; /*eventOut*/ +} M_Viewport; + + +typedef struct _tagXCurve2D +{ + BASE_NODE + GF_Node *point; /*exposedField*/ + SFFloat fineness; /*exposedField*/ + MFInt32 type; /*exposedField*/ +} M_XCurve2D; + + +typedef struct _tagXFontStyle +{ + BASE_NODE + MFString fontName; /*exposedField*/ + SFBool horizontal; /*exposedField*/ + MFString justify; /*exposedField*/ + SFString language; /*exposedField*/ + SFBool leftToRight; /*exposedField*/ + SFFloat size; /*exposedField*/ + SFString stretch; /*exposedField*/ + SFFloat letterSpacing; /*exposedField*/ + SFFloat wordSpacing; /*exposedField*/ + SFInt32 weight; /*exposedField*/ + SFBool fontKerning; /*exposedField*/ + SFString style; /*exposedField*/ + SFBool topToBottom; /*exposedField*/ + MFString featureName; /*exposedField*/ + MFInt32 featureStartOffset; /*exposedField*/ + MFInt32 featureLength; /*exposedField*/ + MFInt32 featureValue; /*exposedField*/ +} M_XFontStyle; + + +typedef struct _tagXLineProperties +{ + BASE_NODE + SFColor lineColor; /*exposedField*/ + SFInt32 lineStyle; /*exposedField*/ + SFBool isCenterAligned; /*exposedField*/ + SFBool isScalable; /*exposedField*/ + SFInt32 lineCap; /*exposedField*/ + SFInt32 lineJoin; /*exposedField*/ + SFFloat miterLimit; /*exposedField*/ + SFFloat transparency; /*exposedField*/ + SFFloat width; /*exposedField*/ + SFFloat dashOffset; /*exposedField*/ + MFFloat dashes; /*exposedField*/ + GF_Node *texture; /*exposedField*/ + GF_Node *textureTransform; /*exposedField*/ +} M_XLineProperties; + + +/*NodeDataType tags*/ +enum { + NDT_SFWorldNode = 1, + NDT_SF3DNode, + NDT_SF2DNode, + NDT_SFStreamingNode, + NDT_SFAppearanceNode, + NDT_SFAudioNode, + NDT_SFBackground3DNode, + NDT_SFBackground2DNode, + NDT_SFGeometryNode, + NDT_SFColorNode, + NDT_SFTextureNode, + NDT_SFCoordinateNode, + NDT_SFCoordinate2DNode, + NDT_SFExpressionNode, + NDT_SFFaceDefMeshNode, + NDT_SFFaceDefTablesNode, + NDT_SFFaceDefTransformNode, + NDT_SFFAPNode, + NDT_SFFDPNode, + NDT_SFFITNode, + NDT_SFFogNode, + NDT_SFFontStyleNode, + NDT_SFTopNode, + NDT_SFLinePropertiesNode, + NDT_SFMaterialNode, + NDT_SFNavigationInfoNode, + NDT_SFNormalNode, + NDT_SFTextureCoordinateNode, + NDT_SFTextureTransformNode, + NDT_SFViewpointNode, + NDT_SFVisemeNode, + NDT_SFViewportNode, + NDT_SFBAPNode, + NDT_SFBDPNode, + NDT_SFBodyDefTableNode, + NDT_SFBodySegmentConnectionHintNode, + NDT_SFPerceptualParameterNode, + NDT_SFTemporalNode, + NDT_SFDepthImageNode, + NDT_SFBlendListNode, + NDT_SFFrameListNode, + NDT_SFLightMapNode, + NDT_SFSurfaceMapNode, + NDT_SFViewMapNode, + NDT_SFParticleInitializerNode, + NDT_SFInfluenceNode, + NDT_SFDepthTextureNode, + NDT_SFSBBoneNode, + NDT_SFSBMuscleNode, + NDT_SFSBSegmentNode, + NDT_SFSBSiteNode, + NDT_SFBaseMeshNode, + NDT_SFSubdivSurfaceSectorNode +}; + +/*All BIFS versions handled*/ +#define GF_BIFS_NUM_VERSION 6 + +enum { + GF_BIFS_V1 = 1, + GF_BIFS_V2, + GF_BIFS_V3, + GF_BIFS_V4, + GF_BIFS_V5, + GF_BIFS_V6, + GF_BIFS_LAST_VERSION = GF_BIFS_V6 +}; + + + +#ifdef __cplusplus +} +#endif + + + +#endif /*_nodes_mpeg4_H*/ + diff --git a/include/gpac/nodes_svg.h b/include/gpac/nodes_svg.h new file mode 100644 index 0000000..4c146f0 --- /dev/null +++ b/include/gpac/nodes_svg.h @@ -0,0 +1,491 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean Le Feuvre + * Copyright (c)2004-200X ENST - All rights reserved + * + * This file is part of GPAC / XML-based Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_XML_NODES_H +#define _GF_XML_NODES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/scenegraph_svg.h> + +enum { + TAG_SVG_a = GF_NODE_RANGE_FIRST_SVG, + TAG_SVG_animate, + TAG_SVG_animateColor, + TAG_SVG_animateMotion, + TAG_SVG_animateTransform, + TAG_SVG_animation, + TAG_SVG_audio, + TAG_SVG_circle, + TAG_SVG_defs, + TAG_SVG_desc, + TAG_SVG_discard, + TAG_SVG_ellipse, + TAG_SVG_font, + TAG_SVG_font_face, + TAG_SVG_font_face_src, + TAG_SVG_font_face_uri, + TAG_SVG_foreignObject, + TAG_SVG_g, + TAG_SVG_glyph, + TAG_SVG_handler, + TAG_SVG_hkern, + TAG_SVG_image, + TAG_SVG_line, + TAG_SVG_linearGradient, + TAG_SVG_listener, + TAG_SVG_metadata, + TAG_SVG_missing_glyph, + TAG_SVG_mpath, + TAG_SVG_path, + TAG_SVG_polygon, + TAG_SVG_polyline, + TAG_SVG_prefetch, + TAG_SVG_radialGradient, + TAG_SVG_rect, + TAG_SVG_script, + TAG_SVG_set, + TAG_SVG_solidColor, + TAG_SVG_stop, + TAG_SVG_svg, + TAG_SVG_switch, + TAG_SVG_tbreak, + TAG_SVG_text, + TAG_SVG_textArea, + TAG_SVG_title, + TAG_SVG_tspan, + TAG_SVG_use, + TAG_SVG_video, + + + TAG_LSR_conditional, + TAG_LSR_cursorManager, + TAG_LSR_rectClip, + TAG_LSR_selector, + TAG_LSR_simpleLayout, + + /*undefined elements (when parsing) use this tag*/ + TAG_SVG_UndefinedElement +}; + +/* Definition of SVG 3 attribute internal tags - 200 defined */ +/* TAG names are made of "TAG_SVG_ATT_" + SVG attribute name (with - replaced by _) */ +enum { + TAG_SVG_ATT_id = TAG_SVG_ATT_RANGE_FIRST, + TAG_SVG_ATT__class, + + TAG_SVG_ATT_requiredFeatures, + TAG_SVG_ATT_requiredExtensions, + TAG_SVG_ATT_requiredFormats, + TAG_SVG_ATT_requiredFonts, + TAG_SVG_ATT_systemLanguage, + TAG_SVG_ATT_display, + TAG_SVG_ATT_visibility, + TAG_SVG_ATT_image_rendering, + TAG_SVG_ATT_pointer_events, + TAG_SVG_ATT_shape_rendering, + TAG_SVG_ATT_text_rendering, + TAG_SVG_ATT_audio_level, + TAG_SVG_ATT_viewport_fill, + TAG_SVG_ATT_viewport_fill_opacity, + TAG_SVG_ATT_overflow, + TAG_SVG_ATT_fill_opacity, + TAG_SVG_ATT_stroke_opacity, + TAG_SVG_ATT_fill, + TAG_SVG_ATT_fill_rule, + TAG_SVG_ATT_stroke, + TAG_SVG_ATT_stroke_dasharray, + TAG_SVG_ATT_stroke_dashoffset, + TAG_SVG_ATT_stroke_linecap, + TAG_SVG_ATT_stroke_linejoin, + TAG_SVG_ATT_stroke_miterlimit, + TAG_SVG_ATT_stroke_width, + TAG_SVG_ATT_color, + TAG_SVG_ATT_color_rendering, + TAG_SVG_ATT_vector_effect, + TAG_SVG_ATT_solid_color, + TAG_SVG_ATT_solid_opacity, + TAG_SVG_ATT_display_align, + TAG_SVG_ATT_line_increment, + TAG_SVG_ATT_stop_color, + TAG_SVG_ATT_stop_opacity, + TAG_SVG_ATT_font_family, + TAG_SVG_ATT_font_size, + TAG_SVG_ATT_font_style, + TAG_SVG_ATT_font_variant, + TAG_SVG_ATT_font_weight, + TAG_SVG_ATT_text_anchor, + TAG_SVG_ATT_text_align, + TAG_SVG_ATT_text_decoration, + TAG_SVG_ATT_focusHighlight, + TAG_SVG_ATT_externalResourcesRequired, + TAG_SVG_ATT_focusable, + TAG_SVG_ATT_nav_next, + TAG_SVG_ATT_nav_prev, + TAG_SVG_ATT_nav_up, + TAG_SVG_ATT_nav_up_right, + TAG_SVG_ATT_nav_right, + TAG_SVG_ATT_nav_down_right, + TAG_SVG_ATT_nav_down, + TAG_SVG_ATT_nav_down_left, + TAG_SVG_ATT_nav_left, + TAG_SVG_ATT_nav_up_left, + TAG_SVG_ATT_transform, + TAG_SVG_ATT_target, + TAG_SVG_ATT_attributeName, + TAG_SVG_ATT_attributeType, + TAG_SVG_ATT_begin, + TAG_SVG_ATT_dur, + TAG_SVG_ATT_end, + TAG_SVG_ATT_repeatCount, + TAG_SVG_ATT_repeatDur, + TAG_SVG_ATT_restart, + TAG_SVG_ATT_smil_fill, + TAG_SVG_ATT_min, + TAG_SVG_ATT_max, + TAG_SVG_ATT_to, + TAG_SVG_ATT_calcMode, + TAG_SVG_ATT_values, + TAG_SVG_ATT_keyTimes, + TAG_SVG_ATT_keySplines, + TAG_SVG_ATT_from, + TAG_SVG_ATT_by, + TAG_SVG_ATT_additive, + TAG_SVG_ATT_accumulate, + TAG_SVG_ATT_path, + TAG_SVG_ATT_keyPoints, + TAG_SVG_ATT_rotate, + TAG_SVG_ATT_origin, + TAG_SVG_ATT_transform_type, + TAG_SVG_ATT_clipBegin, + TAG_SVG_ATT_clipEnd, + TAG_SVG_ATT_syncBehavior, + TAG_SVG_ATT_syncTolerance, + TAG_SVG_ATT_syncMaster, + TAG_SVG_ATT_syncReference, + TAG_SVG_ATT_x, + TAG_SVG_ATT_y, + TAG_SVG_ATT_width, + TAG_SVG_ATT_height, + TAG_SVG_ATT_preserveAspectRatio, + TAG_SVG_ATT_initialVisibility, + TAG_SVG_ATT_type, + TAG_SVG_ATT_cx, + TAG_SVG_ATT_cy, + TAG_SVG_ATT_r, + TAG_SVG_ATT_cursorManager_x, + TAG_SVG_ATT_cursorManager_y, + TAG_SVG_ATT_rx, + TAG_SVG_ATT_ry, + TAG_SVG_ATT_horiz_adv_x, + TAG_SVG_ATT_horiz_origin_x, + TAG_SVG_ATT_font_stretch, + TAG_SVG_ATT_unicode_range, + TAG_SVG_ATT_panose_1, + TAG_SVG_ATT_widths, + TAG_SVG_ATT_bbox, + TAG_SVG_ATT_units_per_em, + TAG_SVG_ATT_stemv, + TAG_SVG_ATT_stemh, + TAG_SVG_ATT_slope, + TAG_SVG_ATT_cap_height, + TAG_SVG_ATT_x_height, + TAG_SVG_ATT_accent_height, + TAG_SVG_ATT_ascent, + TAG_SVG_ATT_descent, + TAG_SVG_ATT_ideographic, + TAG_SVG_ATT_alphabetic, + TAG_SVG_ATT_mathematical, + TAG_SVG_ATT_hanging, + TAG_SVG_ATT_underline_position, + TAG_SVG_ATT_underline_thickness, + TAG_SVG_ATT_strikethrough_position, + TAG_SVG_ATT_strikethrough_thickness, + TAG_SVG_ATT_overline_position, + TAG_SVG_ATT_overline_thickness, + TAG_SVG_ATT_d, + TAG_SVG_ATT_unicode, + TAG_SVG_ATT_glyph_name, + TAG_SVG_ATT_arabic_form, + TAG_SVG_ATT_lang, + TAG_SVG_ATT_u1, + TAG_SVG_ATT_g1, + TAG_SVG_ATT_u2, + TAG_SVG_ATT_g2, + TAG_SVG_ATT_k, + TAG_SVG_ATT_opacity, + TAG_SVG_ATT_x1, + TAG_SVG_ATT_y1, + TAG_SVG_ATT_x2, + TAG_SVG_ATT_y2, + TAG_SVG_ATT_gradientUnits, + TAG_SVG_ATT_spreadMethod, + TAG_SVG_ATT_gradientTransform, + TAG_SVG_ATT_pathLength, + TAG_SVG_ATT_points, + TAG_SVG_ATT_mediaSize, + TAG_SVG_ATT_mediaTime, + TAG_SVG_ATT_mediaCharacterEncoding, + TAG_SVG_ATT_mediaContentEncodings, + TAG_SVG_ATT_bandwidth, + TAG_SVG_ATT_fx, + TAG_SVG_ATT_fy, + TAG_SVG_ATT_size, + TAG_SVG_ATT_choice, + TAG_SVG_ATT_delta, + TAG_SVG_ATT_offset, + TAG_SVG_ATT_syncBehaviorDefault, + TAG_SVG_ATT_syncToleranceDefault, + TAG_SVG_ATT_viewBox, + TAG_SVG_ATT_zoomAndPan, + TAG_SVG_ATT_version, + TAG_SVG_ATT_baseProfile, + TAG_SVG_ATT_contentScriptType, + TAG_SVG_ATT_snapshotTime, + TAG_SVG_ATT_timelineBegin, + TAG_SVG_ATT_playbackOrder, + TAG_SVG_ATT_editable, + TAG_SVG_ATT_text_x, + TAG_SVG_ATT_text_y, + TAG_SVG_ATT_text_rotate, + TAG_SVG_ATT_transformBehavior, + TAG_SVG_ATT_overlay, + TAG_SVG_ATT_fullscreen, + TAG_SVG_ATT_motionTransform, +}; + +struct _all_atts { + XML_Space *xml_space; + XMLRI *xml_base; + SVG_ID *xml_id; + SVG_LanguageID *xml_lang; + + DOM_String *xlink_type; + XMLRI *xlink_role; + XMLRI *xlink_arcrole; + DOM_String *xlink_title; + XMLRI *xlink_href; + DOM_String *xlink_show; + DOM_String *xlink_actuate; + + XMLEV_Event *event; + XMLEV_Phase *phase; + XMLEV_Propagate *propagate; + XMLEV_DefaultAction *defaultAction; + XML_IDREF *observer; + XML_IDREF *listener_target; + XMLRI *handler; + + SVG_ID *id; + SVG_String *_class; + SVG_ListOfIRI *requiredFeatures; + SVG_ListOfIRI *requiredExtensions; + SVG_FormatList *requiredFormats; + SVG_FontList *requiredFonts; + SVG_LanguageIDs *systemLanguage; + SVG_Display *display; + SVG_Visibility *visibility; + SVG_RenderingHint *image_rendering; + SVG_PointerEvents *pointer_events; + SVG_RenderingHint *shape_rendering; + SVG_RenderingHint *text_rendering; + SVG_Number *audio_level; + SVG_Paint *viewport_fill; + SVG_Number *viewport_fill_opacity; + SVG_String *overflow; + SVG_Number *fill_opacity; + SVG_Number *stroke_opacity; + SVG_Paint *fill; + SVG_FillRule *fill_rule; + SVG_Paint *stroke; + SVG_StrokeDashArray *stroke_dasharray; + SVG_Length *stroke_dashoffset; + SVG_StrokeLineCap *stroke_linecap; + SVG_StrokeLineJoin *stroke_linejoin; + SVG_Number *stroke_miterlimit; + SVG_Length *stroke_width; + SVG_Paint *color; + SVG_RenderingHint *color_rendering; + SVG_VectorEffect *vector_effect; + SVG_SVGColor *solid_color; + SVG_Number *solid_opacity; + SVG_DisplayAlign *display_align; + SVG_Number *line_increment; + SVG_SVGColor *stop_color; + SVG_Number *stop_opacity; + SVG_FontFamily *font_family; + SVG_FontSize *font_size; + SVG_FontStyle *font_style; + SVG_FontVariant *font_variant; + SVG_FontWeight *font_weight; + SVG_TextAnchor *text_anchor; + SVG_TextAlign *text_align; + SVG_String *text_decoration; + SVG_FocusHighlight *focusHighlight; + SVG_Boolean *externalResourcesRequired; + SVG_Focusable *focusable; + SVG_Focus *nav_next; + SVG_Focus *nav_prev; + SVG_Focus *nav_up; + SVG_Focus *nav_up_right; + SVG_Focus *nav_right; + SVG_Focus *nav_down_right; + SVG_Focus *nav_down; + SVG_Focus *nav_down_left; + SVG_Focus *nav_left; + SVG_Focus *nav_up_left; + SVG_Transform *transform; + SVG_String *target; + SMIL_AttributeName *attributeName; + SMIL_AttributeType *attributeType; + SMIL_Times *begin; + SVG_Boolean *lsr_enabled; + SMIL_Duration *dur; + SMIL_Times *end; + SMIL_RepeatCount *repeatCount; + SMIL_Duration *repeatDur; + SMIL_Restart *restart; + SMIL_Fill *smil_fill; + SMIL_Duration *min; + SMIL_Duration *max; + SMIL_AnimateValue *to; + SMIL_CalcMode *calcMode; + SMIL_AnimateValues *values; + SMIL_KeyTimes *keyTimes; + SMIL_KeySplines *keySplines; + SMIL_AnimateValue *from; + SMIL_AnimateValue *by; + SMIL_Additive *additive; + SMIL_Accumulate *accumulate; + SVG_PathData *path; + SMIL_KeyPoints *keyPoints; + SVG_Rotate *rotate; + SVG_String *origin; + SVG_TransformType *transform_type; + SVG_Clock *clipBegin; + SVG_Clock *clipEnd; + SMIL_SyncBehavior *syncBehavior; + SMIL_SyncTolerance *syncTolerance; + SVG_Boolean *syncMaster; + XMLRI *syncReference; + SVG_Coordinate *x; + SVG_Coordinate *y; + SVG_Length *width; + SVG_Length *height; + SVG_PreserveAspectRatio *preserveAspectRatio; + SVG_InitialVisibility *initialVisibility; + SVG_ContentType *type; + SVG_Coordinate *cx; + SVG_Coordinate *cy; + SVG_Length *r; + SVG_Length *cursorManager_x; + SVG_Length *cursorManager_y; + SVG_Length *rx; + SVG_Length *ry; + SVG_Number *horiz_adv_x; + SVG_Number *horiz_origin_x; + SVG_String *font_stretch; + SVG_String *unicode_range; + SVG_String *panose_1; + SVG_String *widths; + SVG_String *bbox; + SVG_Number *units_per_em; + SVG_Number *stemv; + SVG_Number *stemh; + SVG_Number *slope; + SVG_Number *cap_height; + SVG_Number *x_height; + SVG_Number *accent_height; + SVG_Number *ascent; + SVG_Number *descent; + SVG_Number *ideographic; + SVG_Number *alphabetic; + SVG_Number *mathematical; + SVG_Number *hanging; + SVG_Number *underline_position; + SVG_Number *underline_thickness; + SVG_Number *strikethrough_position; + SVG_Number *strikethrough_thickness; + SVG_Number *overline_position; + SVG_Number *overline_thickness; + SVG_PathData *d; + SVG_String *unicode; + SVG_String *glyph_name; + SVG_String *arabic_form; + SVG_LanguageIDs *lang; + SVG_String *u1; + SVG_String *g1; + SVG_String *u2; + SVG_String *g2; + SVG_Number *k; + SVG_Number *opacity; + SVG_Coordinate *x1; + SVG_Coordinate *y1; + SVG_Coordinate *x2; + SVG_Coordinate *y2; + SVG_GradientUnit *gradientUnits; + SVG_SpreadMethod *spreadMethod; + SVG_Transform *gradientTransform; + SVG_Number *pathLength; + SVG_Points *points; + SVG_Number *mediaSize; + SVG_String *mediaTime; + SVG_String *mediaCharacterEncoding; + SVG_String *mediaContentEncodings; + SVG_Number *bandwidth; + SVG_Coordinate *fx; + SVG_Coordinate *fy; + LASeR_Size *size; + LASeR_Choice *choice; + LASeR_Size *delta; + SVG_Number *offset; + SMIL_SyncBehavior *syncBehaviorDefault; + SMIL_SyncTolerance *syncToleranceDefault; + SVG_ViewBox *viewBox; + SVG_ZoomAndPan *zoomAndPan; + SVG_String *version; + SVG_String *baseProfile; + SVG_ContentType *contentScriptType; + SVG_Clock *snapshotTime; + SVG_TimelineBegin *timelineBegin; + SVG_PlaybackOrder *playbackOrder; + SVG_Boolean *editable; + SVG_Coordinates *text_x; + SVG_Coordinates *text_y; + SVG_Numbers *text_rotate; + SVG_TransformBehavior *transformBehavior; + SVG_Overlay *overlay; + SVG_Boolean *fullscreen; + SVG_Motion *motionTransform; +}; +#ifdef __cplusplus +} +#endif + + + +#endif /*_GF_SVG_NODES_H*/ + diff --git a/include/gpac/nodes_x3d.h b/include/gpac/nodes_x3d.h new file mode 100644 index 0000000..22f6ba6 --- /dev/null +++ b/include/gpac/nodes_x3d.h @@ -0,0 +1,1396 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / X3D Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + DO NOT MOFIFY - File generated on GMT Thu Aug 07 11:44:22 2008 + + BY X3DGen for GPAC Version 0.4.5-DEV +*/ + +#ifndef _GF_X3D_NODES_H +#define _GF_X3D_NODES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/scenegraph_vrml.h> + + + +enum { + TAG_X3D_Anchor = GF_NODE_RANGE_FIRST_X3D, + TAG_X3D_Appearance, + TAG_X3D_Arc2D, + TAG_X3D_ArcClose2D, + TAG_X3D_AudioClip, + TAG_X3D_Background, + TAG_X3D_Billboard, + TAG_X3D_BooleanFilter, + TAG_X3D_BooleanSequencer, + TAG_X3D_BooleanToggle, + TAG_X3D_BooleanTrigger, + TAG_X3D_Box, + TAG_X3D_Circle2D, + TAG_X3D_Collision, + TAG_X3D_Color, + TAG_X3D_ColorInterpolator, + TAG_X3D_ColorRGBA, + TAG_X3D_Cone, + TAG_X3D_Contour2D, + TAG_X3D_ContourPolyline2D, + TAG_X3D_Coordinate, + TAG_X3D_CoordinateDouble, + TAG_X3D_Coordinate2D, + TAG_X3D_CoordinateInterpolator, + TAG_X3D_CoordinateInterpolator2D, + TAG_X3D_Cylinder, + TAG_X3D_CylinderSensor, + TAG_X3D_DirectionalLight, + TAG_X3D_Disk2D, + TAG_X3D_ElevationGrid, + TAG_X3D_EspduTransform, + TAG_X3D_Extrusion, + TAG_X3D_FillProperties, + TAG_X3D_Fog, + TAG_X3D_FontStyle, + TAG_X3D_GeoCoordinate, + TAG_X3D_GeoElevationGrid, + TAG_X3D_GeoLocation, + TAG_X3D_GeoLOD, + TAG_X3D_GeoMetadata, + TAG_X3D_GeoOrigin, + TAG_X3D_GeoPositionInterpolator, + TAG_X3D_GeoTouchSensor, + TAG_X3D_GeoViewpoint, + TAG_X3D_Group, + TAG_X3D_HAnimDisplacer, + TAG_X3D_HAnimHumanoid, + TAG_X3D_HAnimJoint, + TAG_X3D_HAnimSegment, + TAG_X3D_HAnimSite, + TAG_X3D_ImageTexture, + TAG_X3D_IndexedFaceSet, + TAG_X3D_IndexedLineSet, + TAG_X3D_IndexedTriangleFanSet, + TAG_X3D_IndexedTriangleSet, + TAG_X3D_IndexedTriangleStripSet, + TAG_X3D_Inline, + TAG_X3D_IntegerSequencer, + TAG_X3D_IntegerTrigger, + TAG_X3D_KeySensor, + TAG_X3D_LineProperties, + TAG_X3D_LineSet, + TAG_X3D_LoadSensor, + TAG_X3D_LOD, + TAG_X3D_Material, + TAG_X3D_MetadataDouble, + TAG_X3D_MetadataFloat, + TAG_X3D_MetadataInteger, + TAG_X3D_MetadataSet, + TAG_X3D_MetadataString, + TAG_X3D_MovieTexture, + TAG_X3D_MultiTexture, + TAG_X3D_MultiTextureCoordinate, + TAG_X3D_MultiTextureTransform, + TAG_X3D_NavigationInfo, + TAG_X3D_Normal, + TAG_X3D_NormalInterpolator, + TAG_X3D_NurbsCurve, + TAG_X3D_NurbsCurve2D, + TAG_X3D_NurbsOrientationInterpolator, + TAG_X3D_NurbsPatchSurface, + TAG_X3D_NurbsPositionInterpolator, + TAG_X3D_NurbsSet, + TAG_X3D_NurbsSurfaceInterpolator, + TAG_X3D_NurbsSweptSurface, + TAG_X3D_NurbsSwungSurface, + TAG_X3D_NurbsTextureCoordinate, + TAG_X3D_NurbsTrimmedSurface, + TAG_X3D_OrientationInterpolator, + TAG_X3D_PixelTexture, + TAG_X3D_PlaneSensor, + TAG_X3D_PointLight, + TAG_X3D_PointSet, + TAG_X3D_Polyline2D, + TAG_X3D_Polypoint2D, + TAG_X3D_PositionInterpolator, + TAG_X3D_PositionInterpolator2D, + TAG_X3D_ProximitySensor, + TAG_X3D_ReceiverPdu, + TAG_X3D_Rectangle2D, + TAG_X3D_ScalarInterpolator, + TAG_X3D_Script, + TAG_X3D_Shape, + TAG_X3D_SignalPdu, + TAG_X3D_Sound, + TAG_X3D_Sphere, + TAG_X3D_SphereSensor, + TAG_X3D_SpotLight, + TAG_X3D_StaticGroup, + TAG_X3D_StringSensor, + TAG_X3D_Switch, + TAG_X3D_Text, + TAG_X3D_TextureBackground, + TAG_X3D_TextureCoordinate, + TAG_X3D_TextureCoordinateGenerator, + TAG_X3D_TextureTransform, + TAG_X3D_TimeSensor, + TAG_X3D_TimeTrigger, + TAG_X3D_TouchSensor, + TAG_X3D_Transform, + TAG_X3D_TransmitterPdu, + TAG_X3D_TriangleFanSet, + TAG_X3D_TriangleSet, + TAG_X3D_TriangleSet2D, + TAG_X3D_TriangleStripSet, + TAG_X3D_Viewpoint, + TAG_X3D_VisibilitySensor, + TAG_X3D_WorldInfo, + TAG_LastImplementedX3D +}; + +typedef struct _tagX3DAnchor +{ + BASE_NODE + VRML_CHILDREN + SFString description; /*exposedField*/ + MFString parameter; /*exposedField*/ + MFURL url; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Anchor; + + +typedef struct _tagX3DAppearance +{ + BASE_NODE + GF_Node *material; /*exposedField*/ + GF_Node *texture; /*exposedField*/ + GF_Node *textureTransform; /*exposedField*/ + GF_Node *fillProperties; /*exposedField*/ + GF_Node *lineProperties; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Appearance; + + +typedef struct _tagX3DArc2D +{ + BASE_NODE + SFFloat endAngle; /*field*/ + SFFloat radius; /*field*/ + SFFloat startAngle; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_Arc2D; + + +typedef struct _tagX3DArcClose2D +{ + BASE_NODE + SFString closureType; /*field*/ + SFFloat endAngle; /*field*/ + SFFloat radius; /*field*/ + SFFloat startAngle; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_ArcClose2D; + + +typedef struct _tagX3DAudioClip +{ + BASE_NODE + SFString description; /*exposedField*/ + SFBool loop; /*exposedField*/ + SFFloat pitch; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime stopTime; /*exposedField*/ + MFURL url; /*exposedField*/ + SFTime duration_changed; /*eventOut*/ + SFBool isActive; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFTime pauseTime; /*exposedField*/ + SFTime resumeTime; /*exposedField*/ + SFTime elapsedTime; /*eventOut*/ + SFBool isPaused; /*eventOut*/ +} X_AudioClip; + + +typedef struct _tagX3DBackground +{ + BASE_NODE + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + MFFloat groundAngle; /*exposedField*/ + MFColor groundColor; /*exposedField*/ + MFURL backUrl; /*exposedField*/ + MFURL bottomUrl; /*exposedField*/ + MFURL frontUrl; /*exposedField*/ + MFURL leftUrl; /*exposedField*/ + MFURL rightUrl; /*exposedField*/ + MFURL topUrl; /*exposedField*/ + MFFloat skyAngle; /*exposedField*/ + MFColor skyColor; /*exposedField*/ + SFBool isBound; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFTime bindTime; /*eventOut*/ +} X_Background; + + +typedef struct _tagX3DBillboard +{ + BASE_NODE + VRML_CHILDREN + SFVec3f axisOfRotation; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Billboard; + + +typedef struct _tagX3DBooleanFilter +{ + BASE_NODE + SFBool set_boolean; /*eventIn*/ + void (*on_set_boolean)(GF_Node *pThis); /*eventInHandler*/ + SFBool inputFalse; /*eventOut*/ + SFBool inputNegate; /*eventOut*/ + SFBool inputTrue; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_BooleanFilter; + + +typedef struct _tagX3DBooleanSequencer +{ + BASE_NODE + SFBool next; /*eventIn*/ + void (*on_next)(GF_Node *pThis); /*eventInHandler*/ + SFBool previous; /*eventIn*/ + void (*on_previous)(GF_Node *pThis); /*eventInHandler*/ + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFBool keyValue; /*exposedField*/ + SFBool value_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_BooleanSequencer; + + +typedef struct _tagX3DBooleanToggle +{ + BASE_NODE + SFBool set_boolean; /*eventIn*/ + void (*on_set_boolean)(GF_Node *pThis); /*eventInHandler*/ + SFBool toggle; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_BooleanToggle; + + +typedef struct _tagX3DBooleanTrigger +{ + BASE_NODE + SFTime set_triggerTime; /*eventIn*/ + void (*on_set_triggerTime)(GF_Node *pThis); /*eventInHandler*/ + SFBool triggerTrue; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_BooleanTrigger; + + +typedef struct _tagX3DBox +{ + BASE_NODE + SFVec3f size; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_Box; + + +typedef struct _tagX3DCircle2D +{ + BASE_NODE + SFFloat radius; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Circle2D; + + +typedef struct _tagX3DCollision +{ + BASE_NODE + VRML_CHILDREN + SFBool enabled; /*exposedField*/ + GF_Node *proxy; /*field*/ + SFTime collideTime; /*eventOut*/ + SFBool isActive; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_Collision; + + +typedef struct _tagX3DColor +{ + BASE_NODE + MFColor color; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Color; + + +typedef struct _tagX3DColorInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFColor keyValue; /*exposedField*/ + SFColor value_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_ColorInterpolator; + + +typedef struct _tagX3DColorRGBA +{ + BASE_NODE + MFColorRGBA color; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_ColorRGBA; + + +typedef struct _tagX3DCone +{ + BASE_NODE + SFFloat bottomRadius; /*field*/ + SFFloat height; /*field*/ + SFBool side; /*field*/ + SFBool bottom; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_Cone; + + +typedef struct _tagX3DContour2D +{ + BASE_NODE + VRML_CHILDREN + GF_Node *metadata; /*exposedField*/ +} X_Contour2D; + + +typedef struct _tagX3DContourPolyline2D +{ + BASE_NODE + MFVec2f point; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_ContourPolyline2D; + + +typedef struct _tagX3DCoordinate +{ + BASE_NODE + MFVec3f point; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Coordinate; + + +typedef struct _tagX3DCoordinateDouble +{ + BASE_NODE + MFVec3d point; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_CoordinateDouble; + + +typedef struct _tagX3DCoordinate2D +{ + BASE_NODE + MFVec2f point; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Coordinate2D; + + +typedef struct _tagX3DCoordinateInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec3f keyValue; /*exposedField*/ + MFVec3f value_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_CoordinateInterpolator; + + +typedef struct _tagX3DCoordinateInterpolator2D +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec2f keyValue; /*exposedField*/ + MFVec2f value_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_CoordinateInterpolator2D; + + +typedef struct _tagX3DCylinder +{ + BASE_NODE + SFBool bottom; /*field*/ + SFFloat height; /*field*/ + SFFloat radius; /*field*/ + SFBool side; /*field*/ + SFBool top; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_Cylinder; + + +typedef struct _tagX3DCylinderSensor +{ + BASE_NODE + SFBool autoOffset; /*exposedField*/ + SFFloat diskAngle; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFFloat maxAngle; /*exposedField*/ + SFFloat minAngle; /*exposedField*/ + SFFloat offset; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFRotation rotation_changed; /*eventOut*/ + SFVec3f trackPoint_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFString description; /*exposedField*/ + SFBool isOver; /*eventOut*/ +} X_CylinderSensor; + + +typedef struct _tagX3DDirectionalLight +{ + BASE_NODE + SFFloat ambientIntensity; /*exposedField*/ + SFColor color; /*exposedField*/ + SFVec3f direction; /*exposedField*/ + SFFloat intensity; /*exposedField*/ + SFBool on; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_DirectionalLight; + + +typedef struct _tagX3DDisk2D +{ + BASE_NODE + SFFloat innerRadius; /*field*/ + SFFloat outerRadius; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_Disk2D; + + +typedef struct _tagX3DElevationGrid +{ + BASE_NODE + MFFloat set_height; /*eventIn*/ + void (*on_set_height)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *normal; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + MFFloat height; /*field*/ + SFBool ccw; /*field*/ + SFBool colorPerVertex; /*field*/ + SFFloat creaseAngle; /*field*/ + SFBool normalPerVertex; /*field*/ + SFBool solid; /*field*/ + SFInt32 xDimension; /*field*/ + SFFloat xSpacing; /*field*/ + SFInt32 zDimension; /*field*/ + SFFloat zSpacing; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_ElevationGrid; + + +typedef struct _tagX3DExtrusion +{ + BASE_NODE + MFVec2f set_crossSection; /*eventIn*/ + void (*on_set_crossSection)(GF_Node *pThis); /*eventInHandler*/ + MFRotation set_orientation; /*eventIn*/ + void (*on_set_orientation)(GF_Node *pThis); /*eventInHandler*/ + MFVec2f set_scale; /*eventIn*/ + void (*on_set_scale)(GF_Node *pThis); /*eventInHandler*/ + MFVec3f set_spine; /*eventIn*/ + void (*on_set_spine)(GF_Node *pThis); /*eventInHandler*/ + SFBool beginCap; /*field*/ + SFBool ccw; /*field*/ + SFBool convex; /*field*/ + SFFloat creaseAngle; /*field*/ + MFVec2f crossSection; /*field*/ + SFBool endCap; /*field*/ + MFRotation orientation; /*field*/ + MFVec2f scale; /*field*/ + SFBool solid; /*field*/ + MFVec3f spine; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_Extrusion; + + +typedef struct _tagX3DFillProperties +{ + BASE_NODE + SFBool filled; /*exposedField*/ + SFColor hatchColor; /*exposedField*/ + SFBool hatched; /*exposedField*/ + SFInt32 hatchStyle; /*exposedField*/ +} X_FillProperties; + + +typedef struct _tagX3DFog +{ + BASE_NODE + SFColor color; /*exposedField*/ + SFString fogType; /*exposedField*/ + SFFloat visibilityRange; /*exposedField*/ + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + SFBool isBound; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFTime bindTime; /*eventOut*/ +} X_Fog; + + +typedef struct _tagX3DFontStyle +{ + BASE_NODE + MFString family; /*exposedField*/ + SFBool horizontal; /*exposedField*/ + MFString justify; /*exposedField*/ + SFString language; /*exposedField*/ + SFBool leftToRight; /*exposedField*/ + SFFloat size; /*exposedField*/ + SFFloat spacing; /*exposedField*/ + SFString style; /*exposedField*/ + SFBool topToBottom; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_FontStyle; + + +typedef struct _tagX3DGroup +{ + BASE_NODE + VRML_CHILDREN + GF_Node *metadata; /*exposedField*/ +} X_Group; + + +typedef struct _tagX3DImageTexture +{ + BASE_NODE + MFURL url; /*exposedField*/ + SFBool repeatS; /*field*/ + SFBool repeatT; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_ImageTexture; + + +typedef struct _tagX3DIndexedFaceSet +{ + BASE_NODE + MFInt32 set_colorIndex; /*eventIn*/ + void (*on_set_colorIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_coordIndex; /*eventIn*/ + void (*on_set_coordIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_normalIndex; /*eventIn*/ + void (*on_set_normalIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_texCoordIndex; /*eventIn*/ + void (*on_set_texCoordIndex)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + GF_Node *normal; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + SFBool ccw; /*field*/ + MFInt32 colorIndex; /*field*/ + SFBool colorPerVertex; /*field*/ + SFBool convex; /*field*/ + MFInt32 coordIndex; /*field*/ + SFFloat creaseAngle; /*field*/ + MFInt32 normalIndex; /*field*/ + SFBool normalPerVertex; /*field*/ + SFBool solid; /*field*/ + MFInt32 texCoordIndex; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_IndexedFaceSet; + + +typedef struct _tagX3DIndexedLineSet +{ + BASE_NODE + MFInt32 set_colorIndex; /*eventIn*/ + void (*on_set_colorIndex)(GF_Node *pThis); /*eventInHandler*/ + MFInt32 set_coordIndex; /*eventIn*/ + void (*on_set_coordIndex)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + MFInt32 colorIndex; /*field*/ + SFBool colorPerVertex; /*field*/ + MFInt32 coordIndex; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_IndexedLineSet; + + +typedef struct _tagX3DIndexedTriangleFanSet +{ + BASE_NODE + MFInt32 set_index; /*eventIn*/ + void (*on_set_index)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + GF_Node *normal; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + SFBool ccw; /*field*/ + SFBool colorPerVertex; /*field*/ + SFBool normalPerVertex; /*field*/ + SFBool solid; /*field*/ + MFInt32 index; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_IndexedTriangleFanSet; + + +typedef struct _tagX3DIndexedTriangleSet +{ + BASE_NODE + MFInt32 set_index; /*eventIn*/ + void (*on_set_index)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + GF_Node *normal; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + SFBool ccw; /*field*/ + SFBool colorPerVertex; /*field*/ + SFBool normalPerVertex; /*field*/ + SFBool solid; /*field*/ + MFInt32 index; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_IndexedTriangleSet; + + +typedef struct _tagX3DIndexedTriangleStripSet +{ + BASE_NODE + MFInt32 set_index; /*eventIn*/ + void (*on_set_index)(GF_Node *pThis); /*eventInHandler*/ + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + SFFloat creaseAngle; /*exposedField*/ + GF_Node *normal; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + SFBool ccw; /*field*/ + SFBool normalPerVertex; /*field*/ + SFBool solid; /*field*/ + MFInt32 index; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_IndexedTriangleStripSet; + + +typedef struct _tagX3DInline +{ + BASE_NODE + MFURL url; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ + SFBool load; /*exposedField*/ +} X_Inline; + + +typedef struct _tagX3DIntegerSequencer +{ + BASE_NODE + SFBool next; /*eventIn*/ + void (*on_next)(GF_Node *pThis); /*eventInHandler*/ + SFBool previous; /*eventIn*/ + void (*on_previous)(GF_Node *pThis); /*eventInHandler*/ + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFInt32 keyValue; /*exposedField*/ + SFInt32 value_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_IntegerSequencer; + + +typedef struct _tagX3DIntegerTrigger +{ + BASE_NODE + SFBool set_boolean; /*eventIn*/ + void (*on_set_boolean)(GF_Node *pThis); /*eventInHandler*/ + SFInt32 integerKey; /*exposedField*/ + SFInt32 triggerValue; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_IntegerTrigger; + + +typedef struct _tagX3DKeySensor +{ + BASE_NODE + SFBool enabled; /*exposedField*/ + SFInt32 actionKeyPress; /*eventOut*/ + SFInt32 actionKeyRelease; /*eventOut*/ + SFBool altKey; /*eventOut*/ + SFBool controlKey; /*eventOut*/ + SFBool isActive; /*eventOut*/ + SFString keyPress; /*eventOut*/ + SFString keyRelease; /*eventOut*/ + SFBool shiftKey; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_KeySensor; + + +typedef struct _tagX3DLineProperties +{ + BASE_NODE + SFBool applied; /*exposedField*/ + SFInt32 linetype; /*exposedField*/ + SFFloat linewidthScaleFactor; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_LineProperties; + + +typedef struct _tagX3DLineSet +{ + BASE_NODE + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + MFInt32 vertexCount; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_LineSet; + + +typedef struct _tagX3DLOD +{ + BASE_NODE + VRML_CHILDREN + SFVec3f center; /*field*/ + MFFloat range; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_LOD; + + +typedef struct _tagX3DMaterial +{ + BASE_NODE + SFFloat ambientIntensity; /*exposedField*/ + SFColor diffuseColor; /*exposedField*/ + SFColor emissiveColor; /*exposedField*/ + SFFloat shininess; /*exposedField*/ + SFColor specularColor; /*exposedField*/ + SFFloat transparency; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Material; + + +typedef struct _tagX3DMetadataDouble +{ + BASE_NODE + SFString name; /*exposedField*/ + SFString reference; /*exposedField*/ + MFDouble value; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_MetadataDouble; + + +typedef struct _tagX3DMetadataFloat +{ + BASE_NODE + SFString name; /*exposedField*/ + SFString reference; /*exposedField*/ + MFFloat value; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_MetadataFloat; + + +typedef struct _tagX3DMetadataInteger +{ + BASE_NODE + SFString name; /*exposedField*/ + SFString reference; /*exposedField*/ + MFInt32 value; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_MetadataInteger; + + +typedef struct _tagX3DMetadataSet +{ + BASE_NODE + SFString name; /*exposedField*/ + SFString reference; /*exposedField*/ + GF_ChildNodeItem *value; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_MetadataSet; + + +typedef struct _tagX3DMetadataString +{ + BASE_NODE + SFString name; /*exposedField*/ + SFString reference; /*exposedField*/ + MFString value; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_MetadataString; + + +typedef struct _tagX3DMovieTexture +{ + BASE_NODE + SFBool loop; /*exposedField*/ + SFFloat speed; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime stopTime; /*exposedField*/ + MFURL url; /*exposedField*/ + SFBool repeatS; /*field*/ + SFBool repeatT; /*field*/ + SFTime duration_changed; /*eventOut*/ + SFBool isActive; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFTime resumeTime; /*exposedField*/ + SFTime pauseTime; /*exposedField*/ + SFTime elapsedTime; /*eventOut*/ + SFBool isPaused; /*eventOut*/ +} X_MovieTexture; + + +typedef struct _tagX3DMultiTexture +{ + BASE_NODE + SFFloat alpha; /*exposedField*/ + SFColor color; /*exposedField*/ + MFString function; /*exposedField*/ + MFString mode; /*exposedField*/ + MFString source; /*exposedField*/ + GF_ChildNodeItem *texture; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_MultiTexture; + + +typedef struct _tagX3DMultiTextureCoordinate +{ + BASE_NODE + GF_ChildNodeItem *texCoord; /*MultiTextureCoordinate*/ + GF_Node *metadata; /*exposedField*/ +} X_MultiTextureCoordinate; + + +typedef struct _tagX3DMultiTextureTransform +{ + BASE_NODE + GF_ChildNodeItem *textureTransform; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_MultiTextureTransform; + + +typedef struct _tagX3DNavigationInfo +{ + BASE_NODE + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + MFFloat avatarSize; /*exposedField*/ + SFBool headlight; /*exposedField*/ + SFFloat speed; /*exposedField*/ + MFString type; /*exposedField*/ + SFFloat visibilityLimit; /*exposedField*/ + SFBool isBound; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + MFString transitionType; /*exposedField*/ + SFTime bindTime; /*eventOut*/ +} X_NavigationInfo; + + +typedef struct _tagX3DNormal +{ + BASE_NODE + MFVec3f vector; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Normal; + + +typedef struct _tagX3DNormalInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec3f keyValue; /*exposedField*/ + MFVec3f value_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_NormalInterpolator; + + +typedef struct _tagX3DOrientationInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFRotation keyValue; /*exposedField*/ + SFRotation value_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_OrientationInterpolator; + + +typedef struct _tagX3DPixelTexture +{ + BASE_NODE + SFImage image; /*exposedField*/ + SFBool repeatS; /*field*/ + SFBool repeatT; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_PixelTexture; + + +typedef struct _tagX3DPlaneSensor +{ + BASE_NODE + SFBool autoOffset; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFVec2f maxPosition; /*exposedField*/ + SFVec2f minPosition; /*exposedField*/ + SFVec3f offset; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFVec3f trackPoint_changed; /*eventOut*/ + SFVec3f translation_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFString description; /*exposedField*/ + SFBool isOver; /*eventOut*/ +} X_PlaneSensor; + + +typedef struct _tagX3DPointLight +{ + BASE_NODE + SFFloat ambientIntensity; /*exposedField*/ + SFVec3f attenuation; /*exposedField*/ + SFColor color; /*exposedField*/ + SFFloat intensity; /*exposedField*/ + SFVec3f location; /*exposedField*/ + SFBool on; /*exposedField*/ + SFFloat radius; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_PointLight; + + +typedef struct _tagX3DPointSet +{ + BASE_NODE + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_PointSet; + + +typedef struct _tagX3DPolyline2D +{ + BASE_NODE + MFVec2f lineSegments; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Polyline2D; + + +typedef struct _tagX3DPolypoint2D +{ + BASE_NODE + MFVec2f point; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Polypoint2D; + + +typedef struct _tagX3DPositionInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec3f keyValue; /*exposedField*/ + SFVec3f value_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_PositionInterpolator; + + +typedef struct _tagX3DPositionInterpolator2D +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFVec2f keyValue; /*exposedField*/ + SFVec2f value_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_PositionInterpolator2D; + + +typedef struct _tagX3DProximitySensor +{ + BASE_NODE + SFVec3f center; /*exposedField*/ + SFVec3f size; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFVec3f position_changed; /*eventOut*/ + SFRotation orientation_changed; /*eventOut*/ + SFTime enterTime; /*eventOut*/ + SFTime exitTime; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFVec3f centerOfRotation_changed; /*eventOut*/ +} X_ProximitySensor; + + +typedef struct _tagX3DRectangle2D +{ + BASE_NODE + SFVec2f size; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_Rectangle2D; + + +typedef struct _tagX3DScalarInterpolator +{ + BASE_NODE + SFFloat set_fraction; /*eventIn*/ + void (*on_set_fraction)(GF_Node *pThis); /*eventInHandler*/ + MFFloat key; /*exposedField*/ + MFFloat keyValue; /*exposedField*/ + SFFloat value_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_ScalarInterpolator; + + +typedef struct _tagX3DScript +{ + BASE_NODE + MFScript url; /*exposedField*/ + SFBool directOutput; /*field*/ + SFBool mustEvaluate; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_Script; + + +typedef struct _tagX3DShape +{ + BASE_NODE + GF_Node *appearance; /*exposedField*/ + GF_Node *geometry; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Shape; + + +typedef struct _tagX3DSound +{ + BASE_NODE + SFVec3f direction; /*exposedField*/ + SFFloat intensity; /*exposedField*/ + SFVec3f location; /*exposedField*/ + SFFloat maxBack; /*exposedField*/ + SFFloat maxFront; /*exposedField*/ + SFFloat minBack; /*exposedField*/ + SFFloat minFront; /*exposedField*/ + SFFloat priority; /*exposedField*/ + GF_Node *source; /*exposedField*/ + SFBool spatialize; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_Sound; + + +typedef struct _tagX3DSphere +{ + BASE_NODE + SFFloat radius; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_Sphere; + + +typedef struct _tagX3DSphereSensor +{ + BASE_NODE + SFBool autoOffset; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFRotation offset; /*exposedField*/ + SFBool isActive; /*eventOut*/ + SFRotation rotation_changed; /*eventOut*/ + SFVec3f trackPoint_changed; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFString description; /*exposedField*/ + SFBool isOver; /*eventOut*/ +} X_SphereSensor; + + +typedef struct _tagX3DSpotLight +{ + BASE_NODE + SFFloat ambientIntensity; /*exposedField*/ + SFVec3f attenuation; /*exposedField*/ + SFFloat beamWidth; /*exposedField*/ + SFColor color; /*exposedField*/ + SFFloat cutOffAngle; /*exposedField*/ + SFVec3f direction; /*exposedField*/ + SFFloat intensity; /*exposedField*/ + SFVec3f location; /*exposedField*/ + SFBool on; /*exposedField*/ + SFFloat radius; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_SpotLight; + + +typedef struct _tagX3DStaticGroup +{ + BASE_NODE + VRML_CHILDREN + GF_Node *metadata; /*exposedField*/ +} X_StaticGroup; + + +typedef struct _tagX3DStringSensor +{ + BASE_NODE + SFBool deletionAllowed; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFString enteredText; /*eventOut*/ + SFString finalText; /*eventOut*/ + SFBool isActive; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_StringSensor; + + +typedef struct _tagX3DSwitch +{ + BASE_NODE + VRML_CHILDREN + SFInt32 whichChoice; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Switch; + + +typedef struct _tagX3DText +{ + BASE_NODE + MFString string; /*exposedField*/ + MFFloat length; /*exposedField*/ + GF_Node *fontStyle; /*exposedField*/ + SFFloat maxExtent; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Text; + + +typedef struct _tagX3DTextureBackground +{ + BASE_NODE + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + MFFloat groundAngle; /*exposedField*/ + MFColor groundColor; /*exposedField*/ + GF_Node *backTexture; /*exposedField*/ + GF_Node *bottomTexture; /*exposedField*/ + GF_Node *frontTexture; /*exposedField*/ + GF_Node *leftTexture; /*exposedField*/ + GF_Node *rightTexture; /*exposedField*/ + GF_Node *topTexture; /*exposedField*/ + MFFloat skyAngle; /*exposedField*/ + MFColor skyColor; /*exposedField*/ + MFFloat transparency; /*exposedField*/ + SFTime bindTime; /*exposedField*/ + SFBool isBound; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_TextureBackground; + + +typedef struct _tagX3DTextureCoordinate +{ + BASE_NODE + MFVec2f point; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_TextureCoordinate; + + +typedef struct _tagX3DTextureCoordinateGenerator +{ + BASE_NODE + SFString mode; /*exposedField*/ + MFFloat parameter; /*TextureCoordinateGenerator*/ + GF_Node *metadata; /*exposedField*/ +} X_TextureCoordinateGenerator; + + +typedef struct _tagX3DTextureTransform +{ + BASE_NODE + SFVec2f center; /*exposedField*/ + SFFloat rotation; /*exposedField*/ + SFVec2f scale; /*exposedField*/ + SFVec2f translation; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_TextureTransform; + + +typedef struct _tagX3DTimeSensor +{ + BASE_NODE + SFTime cycleInterval; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFBool loop; /*exposedField*/ + SFTime startTime; /*exposedField*/ + SFTime stopTime; /*exposedField*/ + SFTime cycleTime; /*eventOut*/ + SFFloat fraction_changed; /*eventOut*/ + SFBool isActive; /*eventOut*/ + SFTime time; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFTime pauseTime; /*exposedField*/ + SFTime resumeTime; /*exposedField*/ + SFTime elapsedTime; /*eventOut*/ + SFBool isPaused; /*eventOut*/ +} X_TimeSensor; + + +typedef struct _tagX3DTimeTrigger +{ + BASE_NODE + SFBool set_boolean; /*eventIn*/ + void (*on_set_boolean)(GF_Node *pThis); /*eventInHandler*/ + SFTime triggerTime; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_TimeTrigger; + + +typedef struct _tagX3DTouchSensor +{ + BASE_NODE + SFBool enabled; /*exposedField*/ + SFVec3f hitNormal_changed; /*eventOut*/ + SFVec3f hitPoint_changed; /*eventOut*/ + SFVec2f hitTexCoord_changed; /*eventOut*/ + SFBool isActive; /*eventOut*/ + SFBool isOver; /*eventOut*/ + SFTime touchTime; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFString description; /*exposedField*/ +} X_TouchSensor; + + +typedef struct _tagX3DTransform +{ + BASE_NODE + VRML_CHILDREN + SFVec3f center; /*exposedField*/ + SFRotation rotation; /*exposedField*/ + SFVec3f scale; /*exposedField*/ + SFRotation scaleOrientation; /*exposedField*/ + SFVec3f translation; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_Transform; + + +typedef struct _tagX3DTriangleFanSet +{ + BASE_NODE + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + MFInt32 fanCount; /*exposedField*/ + GF_Node *normal; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + SFBool ccw; /*field*/ + SFBool colorPerVertex; /*field*/ + SFBool normalPerVertex; /*field*/ + SFBool solid; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_TriangleFanSet; + + +typedef struct _tagX3DTriangleSet +{ + BASE_NODE + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + GF_Node *normal; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + SFBool ccw; /*field*/ + SFBool colorPerVertex; /*field*/ + SFBool normalPerVertex; /*field*/ + SFBool solid; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_TriangleSet; + + +typedef struct _tagX3DTriangleSet2D +{ + BASE_NODE + MFVec2f vertices; /*exposedField*/ + GF_Node *metadata; /*exposedField*/ +} X_TriangleSet2D; + + +typedef struct _tagX3DTriangleStripSet +{ + BASE_NODE + GF_Node *color; /*exposedField*/ + GF_Node *coord; /*exposedField*/ + GF_Node *normal; /*exposedField*/ + MFInt32 stripCount; /*exposedField*/ + GF_Node *texCoord; /*exposedField*/ + SFBool ccw; /*field*/ + SFBool colorPerVertex; /*field*/ + SFBool normalPerVertex; /*field*/ + SFBool solid; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_TriangleStripSet; + + +typedef struct _tagX3DViewpoint +{ + BASE_NODE + SFBool set_bind; /*eventIn*/ + void (*on_set_bind)(GF_Node *pThis); /*eventInHandler*/ + SFFloat fieldOfView; /*exposedField*/ + SFBool jump; /*exposedField*/ + SFRotation orientation; /*exposedField*/ + SFVec3f position; /*exposedField*/ + SFString description; /*field*/ + SFTime bindTime; /*eventOut*/ + SFBool isBound; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ + SFVec3f centerOfRotation; /*exposedField*/ +} X_Viewpoint; + + +typedef struct _tagX3DVisibilitySensor +{ + BASE_NODE + SFVec3f center; /*exposedField*/ + SFBool enabled; /*exposedField*/ + SFVec3f size; /*exposedField*/ + SFTime enterTime; /*eventOut*/ + SFTime exitTime; /*eventOut*/ + SFBool isActive; /*eventOut*/ + GF_Node *metadata; /*exposedField*/ +} X_VisibilitySensor; + + +typedef struct _tagX3DWorldInfo +{ + BASE_NODE + MFString info; /*field*/ + SFString title; /*field*/ + GF_Node *metadata; /*exposedField*/ +} X_WorldInfo; + + +#ifdef __cplusplus +} +#endif + + + +#endif /*_GF_X3D_NODES_H*/ + diff --git a/include/gpac/nodes_xbl.h b/include/gpac/nodes_xbl.h new file mode 100644 index 0000000..742f525 --- /dev/null +++ b/include/gpac/nodes_xbl.h @@ -0,0 +1,70 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean Le Feuvre + * Copyright (c)2004-200X ENST - All rights reserved + * + * This file is part of GPAC / XBL Elements + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_XBL_NODES_H +#define _GF_XBL_NODES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/internal/scenegraph_dev.h> +#include <gpac/svg_types.h> + +#define XBL_String_datatype SVG_String_datatype + +struct _all_atts { + DOM_String *id; + DOM_String *extends; + DOM_String *display; + DOM_String *inheritstyle; + DOM_String *includes; + DOM_String *name; + DOM_String *implements; + DOM_String *type; + DOM_String *readonly; + DOM_String *onget; + DOM_String *onset; + DOM_String *event; + DOM_String *action; + DOM_String *phase; + DOM_String *button; + DOM_String *modifiers; + DOM_String *keycode; + DOM_String *key; + DOM_String *charcode; + DOM_String *clickcount; + DOM_String *command; + DOM_String *preventdefault; + DOM_String *src; +}; +#ifdef __cplusplus +} +#endif + + + +#endif /*_GF_SVG_NODES_H*/ + diff --git a/include/gpac/options.h b/include/gpac/options.h new file mode 100644 index 0000000..67da71f --- /dev/null +++ b/include/gpac/options.h @@ -0,0 +1,291 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Stream Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#ifndef _GF_OPTIONS_H_ +#define _GF_OPTIONS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/*AspectRatio Type */ +enum +{ + GF_ASPECT_RATIO_KEEP = 0, /*keep AR*/ + GF_ASPECT_RATIO_16_9, /*keep 16/9*/ + GF_ASPECT_RATIO_4_3, /*keep 4/3*/ + GF_ASPECT_RATIO_FILL_SCREEN /*none (all rendering area used)*/ +}; + +/*AntiAlias settings*/ +enum +{ + GF_ANTIALIAS_NONE = 0, /*no antialiasing*/ + GF_ANTIALIAS_TEXT, /*only text has antialiasing*/ + GF_ANTIALIAS_FULL /*full antialiasing*/ +}; + +/*GF_StreamingCache settings*/ +enum +{ + GF_MEDIA_CACHE_ENABLED = 0, /*cache on (with SET option, turns it on if possible)*/ + GF_MEDIA_CACHE_DISABLED, /*cache off (with SET option saves current cache if any)*/ + GF_MEDIA_CACHE_DISCARD, /*only used for SET option: turns cache off and discards current cache if any*/ + GF_MEDIA_CACHE_RUNNING, /*only used in GET option: caching is enabled and currently running*/ +}; + +/*PlayState settings*/ +enum +{ + GF_STATE_PLAYING = 0, /*terminal is playing*/ + GF_STATE_PAUSED, /*terminal is paused*/ + GF_STATE_STEP_PAUSE, /*get/set only: terminal will pause after next frame (simulation tick). On get, indicates that rendering step hasn't performed yet*/ +}; + +/*refresh mode*/ +enum +{ + GF_REFRESH_NORMAL = 0, /*posts normal redraw message */ + GF_REFRESH_FULL, /*posts full redraw message, including reset of hardware resources*/ +}; + +/*interaction level settings*/ +enum +{ + /*regular interactions enabled (touch sensors)*/ + GF_INTERACT_NORMAL = 1, + /*InputSensor interactions enabled (mouse and keyboard)*/ + GF_INTERACT_INPUT_SENSOR = 2, + /*all navigation interactions enabled (mouse and keyboard)*/ + GF_INTERACT_NAVIGATION = 4, + + /*NOTE: GF_INTERACT_NORMAL and GF_INTERACT_NAVIGATION filter events. If set, any event processed by + these 2 modules won't be forwarded to the user*/ +}; + +/*BoundingVolume settings*/ +enum +{ + GF_BOUNDS_NONE = 0, /*doesn't draw bounding volume*/ + GF_BOUNDS_BOX, /*draw object bounding box / rect*/ + GF_BOUNDS_AABB /*draw object AABB tree (3D only) */ +}; + +/*Wireframe settings*/ +enum +{ + GF_WIREFRAME_NONE = 0, /*draw solid volumes*/ + GF_WIREFRAME_ONLY, /*draw only wireframe*/ + GF_WIREFRAME_SOLID /*draw wireframe on solid object*/ +}; + + +/*navigation type*/ +enum +{ + /*navigation is disabled by content and cannot be forced by user*/ + GF_NAVIGATE_TYPE_NONE, + /*2D navigation modes only can be used*/ + GF_NAVIGATE_TYPE_2D, + /*3D navigation modes only can be used*/ + GF_NAVIGATE_TYPE_3D +}; + +/*navigation modes - non-VRML ones are simply blaxxun contact ones*/ +enum +{ + /*no navigation*/ + GF_NAVIGATE_NONE = 0, + /*3D navigation modes*/ + /*walk navigation*/ + GF_NAVIGATE_WALK, + /*fly navigation*/ + GF_NAVIGATE_FLY, + /*pan navigation*/ + GF_NAVIGATE_PAN, + /*game navigation*/ + GF_NAVIGATE_GAME, + /*slide navigation, for 2D and 3D*/ + GF_NAVIGATE_SLIDE, + /*all modes below disable collision detection & gravity in 3D*/ + /*examine navigation, for 2D and 3D */ + GF_NAVIGATE_EXAMINE, + /*orbit navigation - 3D only*/ + GF_NAVIGATE_ORBIT, + /*QT-VR like navigation - 3D only*/ + GF_NAVIGATE_VR, +}; + +/*collision flags*/ +enum +{ + /*no collision*/ + GF_COLLISION_NONE, + /*regular collision*/ + GF_COLLISION_NORMAL, + /*collision with camera displacement*/ + GF_COLLISION_DISPLACEMENT, +}; + +/*TextTexturing settings*/ +enum +{ + GF_TEXTURE_TEXT_DEFAULT = 0, /*text drawn as texture in 3D mode, regular in 2D mode*/ + GF_TEXTURE_TEXT_NEVER, /*text never drawn as texture*/ + GF_TEXTURE_TEXT_ALWAYS /*text always drawn*/ +}; + +/*Normal drawing settings*/ +enum +{ + GF_NORMALS_NONE = 0, /*normals never drawn*/ + GF_NORMALS_FACE, /*normals drawn per face (at barycenter)*/ + GF_NORMALS_VERTEX /*normals drawn per vertex*/ +}; + + +/*Back-face culling mode*/ +enum +{ + GF_BACK_CULL_OFF = 0, /*backface culling disabled*/ + GF_BACK_CULL_ON, /*backface culliong enabled*/ + GF_BACK_CULL_ALPHA, /*backface culling enabled alos for transparent meshes*/ +}; + +/*high-level options*/ +enum +{ + /*set/get antialias flag (value: one of the AntiAlias enum) - may be ignored in OpenGL mode depending on graphic cards*/ + GF_OPT_ANTIALIAS =0, + /*set/get fast mode (value: boolean) */ + GF_OPT_HIGHSPEED, + /*set/get fullscreen flag (value: boolean) */ + GF_OPT_FULLSCREEN, + /*reset top-level transform to original (value: boolean)*/ + GF_OPT_ORIGINAL_VIEW, + /*overrides BIFS size info for simple AV - this is not recommended since + it will resize the window to the size of the biggest texture (thus some elements + may be lost)*/ + GF_OPT_OVERRIDE_SIZE, + /*set / get audio volume (value is intensity between 0 and 100) */ + GF_OPT_AUDIO_VOLUME, + /*set / get audio pan (value is pan between 0 (all left) and 100(all right) )*/ + GF_OPT_AUDIO_PAN, + /*get javascript flag (no set, depends on compil) - value: boolean, true if JS enabled in build*/ + GF_OPT_HAS_JAVASCRIPT, + /*get selectable stream flag (no set) - value: boolean, true if audio/video/subtitle stream selection is + possible with content (if an MPEG-4 scene description is not present). Use regular OD browsing to get streams*/ + GF_OPT_CAN_SELECT_STREAMS, + /*set/get control interaction, OR'ed combination of interaction flags*/ + GF_OPT_INTERACTION_LEVEL, + /*set display window visible / get show/hide state*/ + GF_OPT_VISIBLE, + /*set freeze display on/off / get freeze state freeze_display prevents any screen updates + needed when output driver uses direct video memory access*/ + GF_OPT_FREEZE_DISPLAY, + /*get isOver flag: if true the file can be restarted, otherwise it should not + this is used to check is there are several timelines, timesensors or interactions, in which case + the file could be running for an undetermined period. + Note that nothing prevents the user app to restart such a file*/ + GF_OPT_IS_FINISHED, + /*set/get aspect ratio (value: one of AspectRatio enum) */ + GF_OPT_ASPECT_RATIO, + /*send a redraw message (SetOption only): all graphics info (display list, vectorial path) is + recomputed, and textures are reloaded in HW*/ + GF_OPT_REFRESH, + /*set/get stress mode (value: boolean) - in stress mode a GF_OPT_FORCE_REDRAW is emulated at each frame*/ + GF_OPT_STRESS_MODE, + /*get/set bounding volume drawing (value: one of the above option)*/ + GF_OPT_DRAW_BOUNDS, + /*get/set texture text option - when enabled and usable (that depends on content), text is first rendered + to a texture and only the texture is drawn, rather than drawing all the text each time (CPU intensive)*/ + GF_OPT_TEXTURE_TEXT, + /*fake option, reload config file (set only), including drivers. Plugins configs are not reloaded*/ + GF_OPT_RELOAD_CONFIG, + /*get: returns whether the content enable navigation and if it's 2D or 3D. + set: reset viewpoint (whatever value is given)*/ + GF_OPT_NAVIGATION_TYPE, + /*get current navigation mode - set navigation mode if allowed by content - this is not a resident + option (eg not stored in cfg)*/ + GF_OPT_NAVIGATION, + /*get/set GF_StreamingCache state - cf above states for set*/ + GF_OPT_MEDIA_CACHE, + /*get/set Play state - cf above states for set*/ + GF_OPT_PLAY_STATE, + /*get/set OpenGL force mode - returns error if OpenGL is not supported*/ + GF_OPT_USE_OPENGL, + + /*set/get direct draw flag. In direct draw, the screen is entirely redrawn at each frame + value: boolean + */ + GF_OPT_DIRECT_DRAW, + /*set/get scalable zoom (value: boolean)*/ + GF_OPT_SCALABLE_ZOOM, + /*set/get YUV acceleration (value: boolean) */ + GF_OPT_YUV_HARDWARE, + /*get (set not supported yet) hardware YUV format (value: YUV 4CC) */ + GF_OPT_YUV_FORMAT, + + /*max video cache size in kbytes*/ + GF_OPT_VIDEO_CACHE_SIZE, + + + /* 3D ONLY OPTIONS */ + /*set/get raster outline flag (value: boolean) - when set, no vectorial outlining is done, only + openGL raster outline*/ + GF_OPT_RASTER_OUTLINES, + /*set/get pow2 emulation flag (value: boolean) - when set, video textures with non power of 2 dimensions + are emulated as pow2 by expanding the video buffer (image is not scaled). Otherwise the entire image + is rescaled. This flag does not affect image textures, which are always rescaled*/ + GF_OPT_EMULATE_POW2, + /*get/set polygon antialiasing flag (value: boolean) (may be ugly with some cards)*/ + GF_OPT_POLYGON_ANTIALIAS, + /*get/set wireframe flag (value: cf above) (may be ugly with some cards)*/ + GF_OPT_WIREFRAME, + /*get/set wireframe flag (value: cf above) (may be ugly with some cards)*/ + GF_OPT_NORMALS, + /*disable backface culling*/ + GF_OPT_BACK_CULL, + /*get/set RECT Ext flag (value: boolean) - when set, GL rectangular texture extension is not used + (but NPO2 texturing is if available)*/ + GF_OPT_NO_RECT_TEXTURE, + /*get/set bitmap draw mode. If set, bitmap doesn't use texturing but direct video copy*/ + GF_OPT_BITMAP_COPY, + /*set/get headlight (value: boolean)*/ + GF_OPT_HEADLIGHT, + /*set/get collision (value: cf above)*/ + GF_OPT_COLLISION, + /*set/get gravity*/ + GF_OPT_GRAVITY, +}; + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_USER_H_*/ + diff --git a/include/gpac/path2d.h b/include/gpac/path2d.h new file mode 100644 index 0000000..e96d2e3 --- /dev/null +++ b/include/gpac/path2d.h @@ -0,0 +1,611 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_PATH2D_H_ +#define _GF_PATH2D_H_ + +/*! + * \file <gpac/path2d.h> + * \brief 2D Vectorial Path functions. + */ + + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/math.h> +#include <gpac/constants.h> + + +/*! + *\addtogroup path_grp path2d + *\ingroup utils_grp + *\brief Vectorial 2D Path manipulation functions + * + *This section documents the 2D path object used in the GPAC framework. + * @{ + */ + + +/*!\brief 2D Path Object + * + *The 2D path object is used to construct complex 2D shapes for later drawing + * or outlining. + */ +typedef struct +{ + /*! number of contours in path*/ + u32 n_contours; + /*! number of points in path and alloc size*/ + u32 n_points, n_alloc_points; + /*! path points */ + GF_Point2D *points; + /*! point tags (one per point)*/ + u8 *tags; + /*! contour end points*/ + u32 *contours; + /*! path bbox - NEVER USE WITHOUT FIRST CALLING \ref gf_path_get_bounds*/ + GF_Rect bbox; + /*! path flags*/ + s32 flags; + /*! fineness to use whenever flattening the path - default is \ref FIX_ONE*/ + Fixed fineness; +} GF_Path; + + +/*! + * \brief path constructor + * + * Constructs an empty 2D path object + * \return new path object + */ +GF_Path *gf_path_new(); +/*! + * \brief path destructor + * + * Destructs a 2D path object + * \param gp the target path + */ +void gf_path_del(GF_Path *gp); +/*! + * \brief path reset + * + * Resets the 2D path object + * \param gp the target path + */ +void gf_path_reset(GF_Path *gp); +/*! + * \brief path copy constuctor + * + * Resets a copy of a 2D path object + * \param gp the target path + * \return new path copy + */ +GF_Path *gf_path_clone(GF_Path *gp); +/*! + * \brief path close + * + * Closes current path contour + * \param gp the target path + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_close(GF_Path *gp); +/*! + * \brief path moveTo + * + * Starts a new contour from the specified point + * \param gp the target path + * \param x x-coordinate of the new point + * \param y y-coordinate of the new point + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_move_to(GF_Path *gp, Fixed x, Fixed y); +/*! + * \brief starts new contour + * + * Starts a new contour from the specified point + * \param gp the target path + * \param pt pointer to the new start point + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_move_to_vec(GF_Path *gp, GF_Point2D *pt); +/*! + * \brief adds line to path + * + * Adds a line from the current point in path to the specified point + * \param gp the target path + * \param x x-coordinate of the line end + * \param y y-coordinate of the line end + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_line_to(GF_Path *gp, Fixed x, Fixed y); +/*! + * \brief adds line to path + * + * Adds a line from the current point in path to the specified point + * \param gp the target path + * \param pt line end + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_line_to_vec(GF_Path *gp, GF_Point2D *pt); +/*! + * \brief adds cubic to path + * + * Adds a cubic bezier curve to the current contour, starting from the current path point + * \param gp the target path + * \param c1_x x-coordinate of the first control point of the cubic curve + * \param c1_y y-coordinate of the first control point of the cubic curve + * \param c2_x x-coordinate of the second control point of the cubic curve + * \param c2_y y-coordinate of the second control point of the cubic curve + * \param x x-coordinate of the end point of the cubic curve + * \param y y-coordinate of the end point of the cubic curve + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_cubic_to(GF_Path *gp, Fixed c1_x, Fixed c1_y, Fixed c2_x, Fixed c2_y, Fixed x, Fixed y); +/*! + * \brief adds cubic to path + * + * Adds a cubic bezier curve to the current contour, starting from the current path point + * \param gp the target path + * \param c1 first control point of the cubic curve + * \param c2 second control point of the cubic curve + * \param pt end point of the cubic curve + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_cubic_to_vec(GF_Path *gp, GF_Point2D *c1, GF_Point2D *c2, GF_Point2D *pt); +/*! + * \brief adds quadratic to path + * + * Adds a quadratic bezier curve to the current contour, starting from the current path point + * \param gp the target path + * \param c_x x-coordinate of the control point of the quadratic curve + * \param c_y y-coordinate of the control point of the quadratic curve + * \param x x-coordinate of the end point of the cubic quadratic + * \param y y-coordinate of the end point of the cubic quadratic + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_quadratic_to(GF_Path *gp, Fixed c_x, Fixed c_y, Fixed x, Fixed y); +/*! + * \brief adds quadratic to path + * + * Adds a quadratic bezier curve to the current contour, starting from the current path point + * \param gp the target path + * \param c control point of the quadratic curve + * \param pt end point of the cubic quadratic + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_quadratic_to_vec(GF_Path *gp, GF_Point2D *c, GF_Point2D *pt); +/*! + * \brief adds rectangle to path + * + * Adds a rectangle contour to the path + * \param gp the target path + * \param cx x-coordinate of the rectangle center + * \param cy y-coordinate of the rectangle center + * \param w width of the rectangle + * \param h height of the rectangle + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_rect_center(GF_Path *gp, Fixed cx, Fixed cy, Fixed w, Fixed h); +/*! + * \brief adds rectangle to path + * + * Adds a rectangle contour to the path + * \param gp the target path + * \param ox left-most coordinate of the rectangle + * \param oy top-most coordinate of the rectangle + * \param w width of the rectangle + * \param h height of the rectangle + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_rect(GF_Path *gp, Fixed ox, Fixed oy, Fixed w, Fixed h); +/*! + * \brief adds ellipse to path + * + * Adds an ellipse contour to the path + * \param gp the target path + * \param cx x-coordinate of the ellipse center + * \param cy y-coordinate of the ellipse center + * \param a_axis length of the horizontal ellipse axis + * \param b_axis length of the vertical ellipse axis + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_ellipse(GF_Path *gp, Fixed cx, Fixed cy, Fixed a_axis, Fixed b_axis); +/*! + * \brief adds N-bezier curve to path + * + * Adds an N-degree bezier curve to the path, starting from the current point + * \param gp the target path + * \param pts points used to define the curve + * \param nb_pts number of points used to define the curve. The degree of the curve is therefore (nb_pts-1). + * \return error code if any error, \ref GF_OK otherwise + * \note the fineness of the path must be set before calling this function. + */ +GF_Err gf_path_add_bezier(GF_Path *gp, GF_Point2D *pts, u32 nb_pts); +/*! + * \brief adds arc as described in MPEG-4 BIFS to path + * + * Adds an arc contour to the path from focal and end points. + * \param gp the target path + * \param end_x x-coordinate of the arc end point + * \param end_y y-coordinate of the arc end point + * \param fa_x x-coordinate of the arc first focal point + * \param fa_y y-coordinate of the arc first focal point + * \param fb_x x-coordinate of the arc second focal point + * \param fb_y y-coordinate of the arc second focal point + * \param cw if 1, the arc will be clockwise, otherwise counter-clockwise. + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_arc_to(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed fa_x, Fixed fa_y, Fixed fb_x, Fixed fb_y, Bool cw); +/*! + * \brief adds arc as described in SVG to path + * + * Adds an arc contour to the path from end point, radii and 3 parameters. + * \param gp the target path + * \param end_x x-coordinate of the arc end point + * \param end_y y-coordinate of the arc end point + * \param r_x x-axis radius + * \param r_y y-axis radius + * \param x_axis_rotation angle for the x-axis + * \param large_arc_flag large or short arc selection + * \param sweep_flag if 1, the arc will be clockwise, otherwise counter-clockwise. + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_svg_arc_to(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed r_x, Fixed r_y, Fixed x_axis_rotation, Bool large_arc_flag, Bool sweep_flag); +/*! + * \brief adds arc to path + * + * Adds an arc contour to the path. + * \param gp the target path + * \param radius radius of the arc + * \param start_angle start angle of the arc in radians + * \param end_angle end angle of the arc in radians + * \param close_type closing type: 0 for open arc, 1 for close arc, 2 for pie + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_arc(GF_Path *gp, Fixed radius, Fixed start_angle, Fixed end_angle, u32 close_type); + +/*! + * \brief concatenates path + * + * Adds a sub-path to the path with a given transform. + * \param gp the target path + * \param subpath the path to add + * \param mat Matrix for subpath + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_add_subpath(GF_Path *gp, GF_Path *subpath, GF_Matrix2D *mx); +/*! + * \brief gets path control bounds + * + * Gets the path control bounds, i.e. the rectangle covering all lineTo and bezier control points. + * \param gp the target path + * \param rc pointer to rectangle receiving the control rectangle + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_get_control_bounds(GF_Path *gp, GF_Rect *rc); +/*! + * \brief gets path bounds + * + * Gets the path bounds, i.e. the rectangle covering all points in path except bezier control points. + * \param gp the target path + * \param rc pointer to rectangle receiving the control rectangle + * \return error code if any error, \ref GF_OK otherwise + */ +GF_Err gf_path_get_bounds(GF_Path *gp, GF_Rect *rc); +/*! + * \brief flattens path + * + * Flattens the path, i.e. transform all bezier curves to lines according to the path flatness. + * \param gp the target path + */ +void gf_path_flatten(GF_Path *gp); +/*! + * \brief gets flatten copy of path + * + * Gets a flatten copy of the path. + * \param gp the target path + * \return the flatten path + */ +GF_Path *gf_path_get_flatten(GF_Path *gp); +/*! + * \brief point over path testing + * + * Tests if a point is over a path or not, according to the path filling rule. + * \param gp the target path + * \param x x-coordinate of the point to check + * \param y y-coordinate of the point to check + * \return 1 if the point is over the path, 0 otherwise. + */ +Bool gf_path_point_over(GF_Path *gp, Fixed x, Fixed y); + +/*! + * \brief path init testing + * + * Tests if the path is empty or not. + * \param gp the target path + * \return 1 if the path is empty, 0 otherwise. + */ +Bool gf_path_is_empty(GF_Path *gp); + +/*! + * \brief path iterator + * + * The path iterator object is used to compute the length of a given path as well + * as transformation matrices along this path. + */ +typedef struct _path_iterator GF_PathIterator; + +/*! + * \brief path iterator constructor + * + * Creates a new path iterator from a given path + * \param gp the target path + * \return the path iterator object. + */ +GF_PathIterator *gf_path_iterator_new(GF_Path *gp); +/*! + * \brief path iterator destructor + * + * Destructs the path iterator object + * \param it the target path iterator + */ +void gf_path_iterator_del(GF_PathIterator *it); + +/*! + * \brief get path length + * + * Gets a path length from its iterator + * \param it the target path iterator + * \return the length of the path + */ +Fixed gf_path_iterator_get_length(GF_PathIterator *it); +/*! + *\brief gets transformation matrix at given point on path + * + * Gets the transformation of a given point on the path, given by offset from origin. + *The transform is so that a local system is translated to the given point, its x-axis tangent + *to the path and in the same direction. The path direction is from first point to last point + *of the path. + * \param it the target path iterator + * \param offset length on the path in local system unit + * \param follow_tangent indicates if transformation shall be computed if offset indicates a point outside the path (<0 or >path_length). In which case the path shall be virtually extended by the tangent at origin (offset <0) or at end (offset>path_length). Otherwise the transformation is not computed and 0 is returned. + * \param mat matrix to be transformed (transformation shall be appended) - the matrix shall not be initialized + * \param smooth_edges indicates if discontinuities shall be smoothed. If not set, the rotation angle THETA is the slope (DX/DY) of the current segment found. + * \param length_after_point if set and smooth_edges is set, the amount of the object that lies on next segment shall be computed according to length_after_point. + \code + Let: + len_last: length of current checked segment + len1: length of all previous segments so that len1 + len_last >= offset then if (offset + length_after_point > len1 + len_last) { + ratio = (len1 + len_last - offset) / length_after_point; + then THETA = ratio * slope(L1) + (1-ratio) * slope(L2) + + Of course care must be taken for PI/2 angles and similar situations + \endcode + + * \return 1 if matrix has been updated, 0 otherwise, if failure or if point is out of path without tangent extension. + */ +Bool gf_path_iterator_get_transform(GF_PathIterator *it, Fixed offset, Bool follow_tangent, GF_Matrix2D *mat, Bool smooth_edges, Fixed length_after_point); + + + +/*! brief gets convexity type for a 2D polygon + * + * Gets the convexity type of the given 2D polygon + * \param pts the points of the polygon + * \param nb_pts number of points in the polygon + * \return the convexity type of the polygon +*/ +u32 gf_polygone2d_get_convexity(GF_Point2D *pts, u32 nb_pts); + + +/* 2D Path constants */ + +/*! + *2D Path point tags + * \hideinitializer + */ +enum +{ + /*/! Point is on curve (moveTo, lineTo, end of splines)*/ + GF_PATH_CURVE_ON = 1, + /*! Point is a contour close*/ + GF_PATH_CLOSE = 5, + /*! Point is a quadratic control point*/ + GF_PATH_CURVE_CONIC = 0, + /*! Point is a cubic control point*/ + GF_PATH_CURVE_CUBIC = 2, +}; + + +/*! + *2D Path flags + * \hideinitializer + */ +enum +{ + /*! Path is filled using the zero-nonzero rule. If not set, filling uses odd/even rule*/ + GF_PATH_FILL_ZERO_NONZERO = 1, + /*! When set bbox must be recomputed. + \note Read only, used to avoid wasting time on bounds calculation*/ + GF_PATH_BBOX_DIRTY = 2, + /*! Indicates the path is flattened flattened + \note Read only, used to avoid wasting time on flattening*/ + GF_PATH_FLATTENED = 4, +}; + +/*! + * 2D Polygon convexity type + * \hideinitializer + */ +enum +{ + /*! Polygon is either complex or unknown*/ + GF_POLYGON_COMPLEX, + /*! Polygon is complex, starting in counter-clockwise order*/ + GF_POLYGON_COMPLEX_CCW, + /*! Polygon is complex, starting in clockwise order*/ + GF_POLYGON_COMPLEX_CW, + /*! Polygon is a counter-clockwise convex polygon*/ + GF_POLYGON_CONVEX_CCW, + /*! Polygon is a clockwise convex polygon*/ + GF_POLYGON_CONVEX_CW, + /*! Polygon is a convex line (degenerated path with all segments aligned)*/ + GF_POLYGON_CONVEX_LINE +}; + +/*! + * Stencil alignment type for outlining + * \hideinitializer + */ +enum +{ + /*! outline is centered on the path (default)*/ + GF_PATH_LINE_CENTER = 0, + /*! outline is inside the path*/ + GF_PATH_LINE_INSIDE, + /*! outline is outside the path*/ + GF_PATH_LINE_OUTSIDE, +}; + +/*! + * Line cap type for outlining + * \hideinitializer + */ +enum +{ + /*! End of line is flat (default)*/ + GF_LINE_CAP_FLAT = 0, + /*! End of line is round*/ + GF_LINE_CAP_ROUND, + /*! End of line is square*/ + GF_LINE_CAP_SQUARE, + /*! End of line is triangle*/ + GF_LINE_CAP_TRIANGLE, +}; + +/*! + * Line join type for outlining + * \hideinitializer + */ +enum +{ + /*! Line join is a miter join (default)*/ + GF_LINE_JOIN_MITER = 0, + /*! Line join is a round join*/ + GF_LINE_JOIN_ROUND, + /*! Line join is a bevel join*/ + GF_LINE_JOIN_BEVEL, + /*! Line join is a miter then bevel join*/ + GF_LINE_JOIN_MITER_SVG +}; + +/*! + * Dash types for outlining + * \hideinitializer + */ +enum +{ + /*! No dashing is used (default)*/ + GF_DASH_STYLE_PLAIN = 0, + /*! Predefined dash pattern is used*/ + GF_DASH_STYLE_DASH, + /*! Predefined dot pattern is used*/ + GF_DASH_STYLE_DOT, + /*! Predefined dash-dot pattern is used*/ + GF_DASH_STYLE_DASH_DOT, + /*! Predefined dash-dash-dot pattern is used*/ + GF_DASH_STYLE_DASH_DASH_DOT, + /*! Predefined dash-dot-dot pattern is used*/ + GF_DASH_STYLE_DASH_DOT_DOT, + /*! Custom pattern is used. Dash lengths are given in percentage of the pen width*/ + GF_DASH_STYLE_CUSTOM, + /*! SVG pattern is used. Dash lengths are given in the same unit as the pen width + and dash offset follows SVG specs (offset in dash pattern)*/ + GF_DASH_STYLE_SVG, +}; + + +/*!\brief Custom dash pattern + * + *The custom dash pattern object is used to specify custom dashes when outlining a path. + */ +typedef struct +{ + /*! Number of dashes in the pattern*/ + u32 num_dash; + /*! Value of the pattern dashes. Unit depends on the dash type*/ + Fixed *dashes; +} GF_DashSettings; + +/*!\brief Pen properties + * + *The pen properties object is used to specify several parameters used when building + *the vectorial outline of a path. + */ +typedef struct +{ + /*! The width of the outline*/ + Fixed width; + /*! The style of the lines ends*/ + u8 cap; + /*! The style of the lines joins*/ + u8 join; + /*! The alignment of the outline with regard to the path*/ + u8 align; + /*! The dash style of the line*/ + u8 dash; + /*! The miter limit of the line joins*/ + Fixed miterLimit; + /*! The initial dash offset in the outline. All points before this offset will be + * ignored when building the outline*/ + Fixed dash_offset; + /*! The dash pattern used for curstom dashing*/ + GF_DashSettings *dash_set; + /*! The author-specified path length. Ignored if <= 0*/ + Fixed path_length; +} GF_PenSettings; + +/*! brief builds the vectorial outline of a path + * + * Builds the vectorial outline of a path for the given settings. The outline of a path is a path. + * \param path the desired path to outline + * \param pen the properties of the virtual pen used for outlining + * \return the outline of the path +*/ +GF_Path *gf_path_get_outline(GF_Path *path, GF_PenSettings pen); + + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_PATH2D_H_*/ + diff --git a/include/gpac/scene_manager.h b/include/gpac/scene_manager.h new file mode 100644 index 0000000..8769a9a --- /dev/null +++ b/include/gpac/scene_manager.h @@ -0,0 +1,429 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Authoring Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_SCENE_MANAGER_H_ +#define _GF_SCENE_MANAGER_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/isomedia.h> +#include <gpac/scenegraph_vrml.h> + +/* + Memory scene management + +*/ + +/*NDT check - return 1 if node belongs to given NDT. Handles proto, and assumes undefined nodes +always belong to the desired NDT*/ +Bool gf_node_in_table(GF_Node *node, u32 NDTType); + +/*generic systems access unit context*/ +typedef struct +{ + /*AU timing in TimeStampResolution*/ + u64 timing; + /*timing in sec - used if timing isn't set*/ + Double timing_sec; + /*random access indication - may be overriden by encoder*/ + Bool is_rap; + /*opaque command list per stream type*/ + GF_List *commands; + + /*pointer to owner stream*/ + struct _stream_context *owner; +} GF_AUContext; + +/*generic stream context*/ +typedef struct _stream_context +{ + /*ESID of stream, or 0 if unknown in which case it is automatically updated at encode stage*/ + u16 ESID; + /*stream type - used as a hint, the encoder(s) may override it*/ + u8 streamType; + u8 objectType; + u32 timeScale; + GF_List *AUs; + + u64 dump_time_offset; + /*last stream AU time, when playing the context directly*/ + u64 last_au_time; + /*set if stream is part of root OD (playback only)*/ + Bool in_root_od; +} GF_StreamContext; + +/*generic presentation context*/ +typedef struct +{ + /*the one and only scene graph used by the scene manager.*/ + GF_SceneGraph *scene_graph; + + /*all systems streams used in presentation*/ + GF_List *streams; + /*(initial) object descriptor if any - if not set the encoder will generate it*/ + GF_ObjectDescriptor *root_od; + + /*scene resolution*/ + u32 scene_width, scene_height; + Bool is_pixel_metrics; + + /*BIFS encoding - these is needed for: + - protos in protos which define subscene graph, hence seperate namespace, but are coded with the same IDs + - route insertions which are not tracked by the scene graph + we could do this by hand (counting protos & route insert) but let's be lazy + */ + u32 max_node_id, max_route_id, max_proto_id; +} GF_SceneManager; + +/*scene manager constructor - @scene_graph: scene graph used by the manager. */ +GF_SceneManager *gf_sm_new(GF_SceneGraph *scene_graph); +/*scene manager destructor - does not destroy the attached scene graph*/ +void gf_sm_del(GF_SceneManager *ctx); +/*retrive or create a stream context in the presentation context +WARNING: if a stream with the same streamType and no ESID already exists in the context, +it is assigned the requested ES_ID - this is needed to solve base layer*/ +GF_StreamContext *gf_sm_stream_new(GF_SceneManager *ctx, u16 ES_ID, u8 streamType, u8 objectType); +/*removes and destroy stream context from presentation context*/ +void gf_sm_stream_del(GF_SceneManager *ctx, GF_StreamContext *sc); +/*locate a stream based on its id*/ +GF_StreamContext *gf_sm_stream_find(GF_SceneManager *ctx, u16 ES_ID); +/*create a new AU context in the given stream context*/ +GF_AUContext *gf_sm_stream_au_new(GF_StreamContext *stream, u64 timing, Double time_ms, Bool isRap); + +/*reset the context: +- purge all access units on all streams +- destroy root OD +*/ +void gf_sm_reset(GF_SceneManager *ctx); + +/*applies all commands in all streams (only BIFS for now): the context manager will only have one command per +stream, this command being a random access*/ +GF_Err gf_sm_make_random_access(GF_SceneManager *ctx); + +/*translates SRT/SUB (TTXT not supported) source into BIFS command stream source + @src: GF_ESD of new stream (MUST be created before to store TS resolution) + @mux: GF_MuxInfo of src stream - shall contain a valid file, and at least the textNode member set +*/ +GF_Err gf_sm_import_bifs_subtitle(GF_SceneManager *ctx, GF_ESD *src, GF_MuxInfo *mux); + + +/*SWF to MPEG-4 flags*/ +enum +{ + /*all data in dictionary is in first frame*/ + GF_SM_SWF_STATIC_DICT = 1, + /*remove all text*/ + GF_SM_SWF_NO_TEXT = (1<<1), + /*remove embedded fonts (force device font usage)*/ + GF_SM_SWF_NO_FONT = (1<<2), + /*forces XCurve2D which supports quadratic bezier*/ + GF_SM_SWF_QUAD_CURVE = (1<<3), + /*forces line remove*/ + GF_SM_SWF_NO_LINE = (1<<4), + /*forces XLineProperties (supports scalable lines)*/ + GF_SM_SWF_SCALABLE_LINE = (1<<5), + /*forces gradient remove (using center color) */ + GF_SM_SWF_NO_GRADIENT = (1<<6), + /*use a dedicated BIFS stream to control display list. This allows positioning in the movie + (jump to frame, etc..) as well as looping from inside the movie (set by default)*/ + GF_SM_SWF_SPLIT_TIMELINE = (1<<7), + /*enable appearance reuse*/ + GF_SM_SWF_REUSE_APPEARANCE = (1<<9), + /*enable IndexedCurve2D proto*/ + GF_SM_SWF_USE_IC2D = (1<<10), +}; + +/*general loader flags*/ +enum +{ + /*if set, always load MPEG-4 nodes, otherwise X3D versions are used for vrml/x3d*/ + GF_SM_LOAD_MPEG4_STRICT = 1, + /*signal loading is done for playback: + scrips will be queued in their parent command for later loading + SFTime (MPEG-4 only) fields will be handled correctly when inserting/creating nodes based on AU timing + */ + GF_SM_LOAD_FOR_PLAYBACK = 1<<1, + + /*special flag indicating that the context is already loaded & valid (eg no default stream creations & co) + this is used when performing diff encoding (eg the file to load only has updates). + When set, gf_sm_load_init will NOT attempt to parse first frame*/ + GF_SM_LOAD_CONTEXT_READY = 1<<2, + + /* in this mode, each root svg tag will be interpreted as a REPLACE SCENE */ + GF_SM_LOAD_CONTEXT_STREAMING = 1<<3, +}; + +/*loader type, usually detected based on file ext*/ +enum +{ + GF_SM_LOAD_BT = 1, /*BT loader*/ + GF_SM_LOAD_VRML, /*VRML97 loader*/ + GF_SM_LOAD_X3DV, /*X3D VRML loader*/ + GF_SM_LOAD_XMTA, /*XMT-A loader*/ + GF_SM_LOAD_X3D, /*X3D XML loader*/ + GF_SM_LOAD_SVG_DA, /*SVG loader with dynamic allocation of attributes */ + GF_SM_LOAD_XSR, /*LASeR+XML loader*/ + GF_SM_LOAD_DIMS, /*DIMS LASeR+XML loader*/ + GF_SM_LOAD_SWF, /*SWF->MPEG-4 converter*/ + GF_SM_LOAD_QT, /*MOV->MPEG-4 converter (only cubic QTVR for now)*/ + GF_SM_LOAD_MP4, /*MP4 memory loader*/ + GF_SM_LOAD_XBL +}; + +typedef struct +{ + /*scene graph worked on - may be NULL if ctx is present*/ + GF_SceneGraph *scene_graph; + + struct _inline_scene *is; + + /*context manager to load (MUST BE RESETED BEFORE if needed) - may be NULL for loaders not using commands, + in which case the graph will be directly updated*/ + GF_SceneManager *ctx; + /*file to import except IsoMedia files*/ + const char *fileName; + /*IsoMedia file to import (we need to be able to load from an opened file for scene stats)*/ + GF_ISOFile *isom; + /*swf import flags*/ + u32 swf_import_flags; + /*swf flatten limit: angle limit below which 2 lines are considered as aligned, + in which case the lines are merged as one. If 0, no flattening happens*/ + Float swf_flatten_limit; + /*swf extraction path: if set, swf media (mp3, jpeg) are extracted to this path. If not set + media are extracted to original file directory*/ + const char *localPath; + + /*loader flags*/ + u32 flags; + + /*private to loader*/ + void *loader_priv; + /*loader type, one of the above value. If not set, detected based on file extension*/ + u32 type; +} GF_SceneLoader; + +/*initializes the context loader - this will load any IOD and the first frame of the main scene*/ +GF_Err gf_sm_load_init(GF_SceneLoader *load); +/*completely loads context*/ +GF_Err gf_sm_load_run(GF_SceneLoader *load); +/*terminates the context loader*/ +void gf_sm_load_done(GF_SceneLoader *load); + +/*parses memory scene (any textural format) into the context +!! THE LOADER TYPE MUST BE ASSIGNED (BT/WRL/XMT/X3D/SVG only) !! +The string MUST be at least 4 bytes long in order to detect BOM (unicode encoding). +The string can ba either UTF-8 or UTF-16 data +if clean_at_end is set, associated parser is destroyed. Otherwise, a call to gf_sm_load_done must be done +to clean ressources (needed for SAX progressive loading) +*/ +GF_Err gf_sm_load_string(GF_SceneLoader *load, char *str, Bool clean_at_end); + + +/*scene dump mode*/ +enum +{ + /*BT*/ + GF_SM_DUMP_BT = 0, + /*XMT-A*/ + GF_SM_DUMP_XMTA, + /*VRML Text (WRL)*/ + GF_SM_DUMP_VRML, + /*X3D Text (x3dv)*/ + GF_SM_DUMP_X3D_VRML, + /*X3D XML*/ + GF_SM_DUMP_X3D_XML, + /*LASeR XML*/ + GF_SM_DUMP_LASER, + /*SVG dump (only dumps svg root of the first LASeR unit*/ + GF_SM_DUMP_SVG, + /*blind XML dump*/ + GF_SM_DUMP_XML, + /*automatic selection of MPEG4 vs X3D, text mode*/ + GF_SM_DUMP_AUTO_TXT, + /*automatic selection of MPEG4 vs X3D, xml mode*/ + GF_SM_DUMP_AUTO_XML, +}; + +#ifndef GPAC_READ_ONLY + +/*dumps scene context to BT or XMT +@rad_name: file name & loc without extension - if NULL dump will happen in stdout +@dump_mode: one of the above*/ +GF_Err gf_sm_dump(GF_SceneManager *ctx, char *rad_name, u32 dump_mode); + +#endif + + +/*encoding flags*/ +enum +{ + /*if flag set, DEF names are encoded*/ + GF_SM_ENCODE_USE_NAMES = 1, + /*if flag set, RAP are generated inband rather than as redundant samples*/ + GF_SM_ENCODE_RAP_INBAND = 2, + /*if flag set, RAP are generated inband rather than as sync shadow samples*/ + GF_SM_ENCODE_RAP_SHADOW = 4, +}; + +typedef struct +{ + /*encoding flags*/ + u32 flags; + /*delay between 2 RAP in ms. If 0 RAPs are not forced - BIFS and LASeR only for now*/ + u32 rap_freq; + /*if set, any unknown stream in the scene will be looked for in @mediaSource (MP4 only)*/ + char *mediaSource; + /*LASeR */ + /*resolution*/ + s32 resolution; + /*coordBits, scaleBits*/ + u32 coord_bits, scale_bits; + /*auto quantification type: + 0: none + 1: LASeR + 2: BIFS + */ + u32 auto_quant; +} GF_SMEncodeOptions; + +/* +encodes scene context into @mp4. +if @log is set, generates BIFS encoder log file +*/ +GF_Err gf_sm_encode_to_file(GF_SceneManager *ctx, GF_ISOFile *mp4, GF_SMEncodeOptions *opt); + +/*Dumping tools*/ +typedef struct _scenedump GF_SceneDumper; +/*create a scene dumper +@graph: scene graph being dumped +@rad_name: file radical (NULL for stdout) - if not NULL MUST BE GF_MAX_PATH length +@indent_char: indent format +@XMLDump: if set, dumps in XML format otherwise regular text +returns NULL if can't create a file +*/ +GF_SceneDumper *gf_sm_dumper_new(GF_SceneGraph *graph, char *rad_name, char indent_char, Bool XMLDump); +void gf_sm_dumper_del(GF_SceneDumper *bd); + +/*dumps commands list +@indent: indent to use +@skip_replace_scene_header: if set and dumping in BT mode, the initial REPLACE SCENE header is skipped +*/ +GF_Err gf_sm_dump_command_list(GF_SceneDumper *sdump, GF_List *comList, u32 indent, Bool skip_first_replace); + +/*dumps complete graph - +@skip_proto: proto declarations are skipped +@skip_routes: routes are not dumped +*/ +GF_Err gf_sm_dump_graph(GF_SceneDumper *sdump, Bool skip_proto, Bool skip_routes); + + +#ifndef GPAC_READ_ONLY + +/*stat object - to refine :)*/ + +/*store nodes or proto stats*/ +typedef struct +{ + /*node type or protoID*/ + u32 tag; + const char *name; + /*number of created nodes*/ + u32 nb_created; + /*number of used nodes*/ + u32 nb_used; + /*number of used nodes*/ + u32 nb_del; +} GF_NodeStats; + +typedef struct _scenestat +{ + GF_List *node_stats; + GF_List *proto_stats; + + /*ranges of all SFVec2fs for points only (MFVec2fs)*/ + SFVec2f max_2d, min_2d; + /* resolution of 2D points (nb bits for integer part and decimal part)*/ + u32 int_res_2d, frac_res_2d; + /* resolution of scale coefficient (nb bits for integer part)*/ + u32 scale_int_res_2d, scale_frac_res_2d; + + Fixed max_fixed, min_fixed; + + /*number of parsed 2D points*/ + u32 count_2d; + /*number of deleted 2D points*/ + u32 rem_2d; + + /*ranges of all SFVec3fs for points only (MFVec3fs)*/ + SFVec3f max_3d, min_3d; + u32 count_3d; + /*number of deleted 3D points*/ + u32 rem_3d; + + u32 count_float, rem_float; + u32 count_color, rem_color; + /*all SFVec2f other than MFVec2fs elements*/ + u32 count_2f; + /*all SFVec3f other than MFVec3fs elements*/ + u32 count_3f; + + u32 nb_svg_attributes; + + GF_StreamContext *base_layer; +} GF_SceneStatistics; + +typedef struct _statman GF_StatManager; + +/*creates new stat handler*/ +GF_StatManager *gf_sm_stats_new(); +/*deletes stat handler*/ +void gf_sm_stats_del(GF_StatManager *stat); +/*reset statistics*/ +void gf_sm_stats_reset(GF_StatManager *stat); + +/*get statistics - do NOT modify the returned structure*/ +GF_SceneStatistics *gf_sm_stats_get(GF_StatManager *stat); + +/*produces stat report for a complete graph*/ +GF_Err gf_sm_stats_for_graph(GF_StatManager *stat, GF_SceneGraph *sg); + +/*produces stat report for the full scene*/ +GF_Err gf_sm_stats_for_scene(GF_StatManager *stat, GF_SceneManager *sm); + +/*produces stat report for the given command*/ +GF_Err gf_sm_stats_for_command(GF_StatManager *stat, GF_Command *com); + +#endif + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_SCENE_MANAGER_H_*/ + diff --git a/include/gpac/scenegraph.h b/include/gpac/scenegraph.h new file mode 100644 index 0000000..d4e7742 --- /dev/null +++ b/include/gpac/scenegraph.h @@ -0,0 +1,758 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_SCENEGRAPH_H_ +#define _GF_SCENEGRAPH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/list.h> +#include <gpac/math.h> + +/* + TAG definitions are static, in order to be able to mix nodes from different standard + in a single scenegraph. These TAGs are only used internally (they do not match any + binary encoding) +*/ +enum { + /*undefined node: just the base node class, used for parsing*/ + TAG_UndefinedNode = 0, + /*all MPEG-4/VRML/X3D proto instances have this tag*/ + TAG_ProtoNode, + + /*reserved TAG ranges per standard*/ + + /*range for MPEG4*/ + GF_NODE_RANGE_FIRST_MPEG4, + GF_NODE_RANGE_LAST_MPEG4 = GF_NODE_RANGE_FIRST_MPEG4+512, + + /*range for X3D*/ + GF_NODE_RANGE_FIRST_X3D, + GF_NODE_RANGE_LAST_X3D = GF_NODE_RANGE_FIRST_X3D+512, + + /*all nodes after this are always parent nodes*/ + GF_NODE_RANGE_LAST_VRML, + + /*DOM container for BIFS/LASeR/etc updates*/ + TAG_DOMUpdates, + + /*all nodes below MUST be parent nodes*/ + GF_NODE_FIRST_PARENT_NODE_TAG, + + /*DOM text node*/ + TAG_DOMText, + /*all nodes below MUST use the base DOM structure (with dyn attribute list)*/ + GF_NODE_FIRST_DOM_NODE_TAG, + + /*full node*/ + TAG_DOMFullNode = GF_NODE_FIRST_DOM_NODE_TAG, + + /*range for SVG*/ + GF_NODE_RANGE_FIRST_SVG, + GF_NODE_RANGE_LAST_SVG = GF_NODE_RANGE_FIRST_SVG+100, + + /*range for XBL*/ + GF_NODE_RANGE_FIRST_XBL, + TAG_XBL_bindings = GF_NODE_RANGE_FIRST_XBL, + TAG_XBL_binding, + TAG_XBL_content, + TAG_XBL_children, + TAG_XBL_implementation, + TAG_XBL_constructor, + TAG_XBL_destructor, + TAG_XBL_field, + TAG_XBL_property, + TAG_XBL_getter, + TAG_XBL_setter, + TAG_XBL_method, + TAG_XBL_parameter, + TAG_XBL_body, + TAG_XBL_handlers, + TAG_XBL_handler, + TAG_XBL_resources, + TAG_XBL_stylesheet, + TAG_XBL_image, + GF_NODE_RANGE_LAST_XBL, +}; + + + +/*private handler for this library on all nodes*/ +#define BASE_NODE struct _nodepriv *sgprivate; + +/*base node type*/ +typedef struct _base_node +{ + BASE_NODE +} GF_Node; + +/*child storage - this is not integrated in the base node, because of VRML/MPEG-4 USE: a node +may be present at different places in the tree, hence have different "next" siblings.*/ +typedef struct _child_node +{ + struct _child_node *next; + GF_Node *node; +} GF_ChildNodeItem; + +/*grouping nodes macro : + children: list of children SFNodes +*/ + +#define CHILDREN \ + struct _child_node *children; + +typedef struct +{ + BASE_NODE + CHILDREN +} GF_ParentNode; + +/*adds a child to a given container*/ +GF_Err gf_node_list_add_child(GF_ChildNodeItem **list, GF_Node *n); +/*adds a child to a given container, updating last position*/ +GF_Err gf_node_list_add_child_last(GF_ChildNodeItem **list, GF_Node *n, GF_ChildNodeItem **last_child); +/*inserts a child to a given container - if pos doesn't match, append the child*/ +GF_Err gf_node_list_insert_child(GF_ChildNodeItem **list, GF_Node *n, u32 pos); +/*removes a child to a given container - return 0 if child not found*/ +Bool gf_node_list_del_child(GF_ChildNodeItem **list, GF_Node *n); +/*finds a child in a given container, returning its 0-based index if found, -1 otherwise*/ +s32 gf_node_list_find_child(GF_ChildNodeItem *list, GF_Node *n); +/*finds a child in a given container given its index, returning the child or NULL if not found +if pos is <0, returns the last child*/ +GF_Node *gf_node_list_get_child(GF_ChildNodeItem *list, s32 pos); +/*gets the number of children in a given container*/ +u32 gf_node_list_get_count(GF_ChildNodeItem *list); +/*deletes node entry at given idx, returning node if found, NULL otherwise*/ +GF_Node *gf_node_list_del_child_idx(GF_ChildNodeItem **list, u32 pos); + + + +/*tag is set upon creation and cannot be modified*/ +u32 gf_node_get_tag(GF_Node*); +/*set node def + @ID: node ID, !=0 set def node - if a different node with the same ID exists, returns error. +You may change the node ID by recalling the function with a different ID value. You may get a node ID +by calling the gf_sg_get_next_available_node_id function + @defName: optional readable name (script, MPEGJ). To change the name, recall the function with a different name and the same ID +*/ +GF_Err gf_node_set_id(GF_Node*n, u32 nodeID, const char *nodeDEFName); +/*get def name of the node , NULL if not set*/ +const char *gf_node_get_name(GF_Node*); +/*get def name of the node , or the string representation of the node pointer if not set*/ +const char *gf_node_get_log_name(GF_Node*); +/*get def ID of the node, 0 if node not def*/ +u32 gf_node_get_id(GF_Node*); +/* gets node built-in name (eg 'Appearance', ..) */ +const char *gf_node_get_class_name(GF_Node *Node); + +u32 gf_sg_node_get_tag_by_class_name(const char *name, u32 xmlns); + +/*unset the node ID*/ +GF_Err gf_node_remove_id(GF_Node *p); + +/*get/set user private stack*/ +void *gf_node_get_private(GF_Node*); +void gf_node_set_private(GF_Node*, void *); + +/*set traversal callback function. If a node has no associated callback, the traversing of the +graph won't propagate below it. It is the app responsability to setup traversing functions as needed +VRML/MPEG4: Instanciated Protos are handled internally as well as interpolators, valuators and scripts +@is_destroy: set when the node is about to be destroyed +*/ +GF_Err gf_node_set_callback_function(GF_Node *, void (*NodeFunction)(GF_Node *node, void *traverse_state, Bool is_destroy) ); + +/*register a node (DEFed or not), specifying parent if any. +A node must be registered whenever used by something (a parent node, a command, whatever) to prevent its +destruction (think of it as a reference counting). +NOTE: NODES ARE CREATED WITHOUT BEING REGISTERED +*/ +GF_Err gf_node_register(GF_Node *node, GF_Node *parent_node); + +/*unregister a node from parent (node may or not be DEF'ed). Parent may be NULL (DEF root node, commands). +This MUST be called whenever a node is destroyed (removed from a parent node) +If this is the last instance of the node, the node is destroyed +NOTE: NODES ARE CREATED WITHOUT BEING REGISTERED, hence they MUST be registered at least once before +being destroyed +*/ +GF_Err gf_node_unregister(GF_Node *node, GF_Node *parent_node); +/*deletes all node instances in the given list*/ +void gf_node_unregister_children(GF_Node *node, GF_ChildNodeItem *childrenlist); + +/*get all parents of the node and replace the old_node by the new node in all parents +Note: if the new node is not DEFed, only the first instance of "old_node" will be replaced, the other ones deleted*/ +GF_Err gf_node_replace(GF_Node *old_node, GF_Node *new_node, Bool updateOrderedGroup); + +/*returns number of instances for this node*/ +u32 gf_node_get_num_instances(GF_Node *node); + + +/*calls node traverse callback routine on this node*/ +void gf_node_traverse(GF_Node *node, void *udta); +/*allows a node to be re-rendered - by default a node in its render phase will never be rendered a second time. +Use this function to enable a second render for this node - this must be called while node is being rendered*/ +void gf_node_allow_cyclic_traverse(GF_Node *node); + +/*blindly calls traverse callback on all children nodes */ +void gf_node_traverse_children(GF_Node *node, void *renderStack); +/*returns number of parent for this node (parent are kept regardless of DEF state)*/ +u32 gf_node_get_parent_count(GF_Node *node); +/*returns desired parent for this node (parent are kept regardless of DEF state) +idx is 0-based parent index*/ +GF_Node *gf_node_get_parent(GF_Node *node, u32 idx); + + +enum +{ + /*flag set whenever a field of the node has been modified*/ + GF_SG_NODE_DIRTY = 1, + /*flag set whenever a child node of this node has been modified + NOTE: unloaded extern protos always invalidate their parent subgraph to get a chance + of being loaded. It is the user responsability to clear the CHILD_DIRTY flag before traversing + if relying on this flag for sub-tree discarding (eg, culling or similar)*/ + GF_SG_CHILD_DIRTY = 1<<1, + + /*flag set by bindable nodes to indicate a modification of the bindable stack. This is + only used for offscreen rendering of Layer3D*/ + GF_SG_VRML_BINDABLE_DIRTY = 1<<2, + + /*flag set whenever a ColorTransform node is removed from a parent node*/ + GF_SG_VRML_COLOR_DIRTY = 1<<3, + + + /*SVG-specific flags due to mix of geometry and appearance & co attributes*/ + /*SVG geometry changed is the same as base flag*/ + GF_SG_SVG_GEOMETRY_DIRTY = GF_SG_NODE_DIRTY, + GF_SG_SVG_COLOR_DIRTY = GF_SG_VRML_BINDABLE_DIRTY, + GF_SG_SVG_DISPLAYALIGN_DIRTY = 1<<3, + GF_SG_SVG_FILL_DIRTY = 1<<4, + GF_SG_SVG_FILLOPACITY_DIRTY = 1<<5, + GF_SG_SVG_FILLRULE_DIRTY = 1<<6, + GF_SG_SVG_FONTFAMILY_DIRTY = 1<<7, + GF_SG_SVG_FONTSIZE_DIRTY = 1<<8, + GF_SG_SVG_FONTSTYLE_DIRTY = 1<<9, + GF_SG_SVG_FONTVARIANT_DIRTY = 1<<10, + GF_SG_SVG_FONTWEIGHT_DIRTY = 1<<11, + GF_SG_SVG_LINEINCREMENT_DIRTY = 1<<12, + GF_SG_SVG_OPACITY_DIRTY = 1<<13, + GF_SG_SVG_SOLIDCOLOR_OR_OPACITY_DIRTY = 1<<14, + GF_SG_SVG_STOPCOLOR_OR_OPACITY_DIRTY = 1<<15, + GF_SG_SVG_STROKE_DIRTY = 1<<16, + GF_SG_SVG_STROKEDASHARRAY_DIRTY = 1<<17, + GF_SG_SVG_STROKEDASHOFFSET_DIRTY= 1<<18, + GF_SG_SVG_STROKELINECAP_DIRTY = 1<<19, + GF_SG_SVG_STROKELINEJOIN_DIRTY = 1<<20, + GF_SG_SVG_STROKEMITERLIMIT_DIRTY= 1<<21, + GF_SG_SVG_STROKEOPACITY_DIRTY = 1<<22, + GF_SG_SVG_STROKEWIDTH_DIRTY = 1<<23, + GF_SG_SVG_TEXTPOSITION_DIRTY = 1<<24, + GF_SG_SVG_DISPLAY_DIRTY = 1<<25, + GF_SG_SVG_VECTOREFFECT_DIRTY = 1<<26, + GF_SG_SVG_XLINK_HREF_DIRTY = 1<<27, +}; + +/*set dirty flags. +if @flags is 0, sets the base flags on (GF_SG_NODE_DIRTY). +if @flags is not 0, adds the flags to the node dirty state + +If @invalidate_parents is set, all parent subtrees for this node are marked as GF_SG_CHILD_DIRTY +Note: parent subtree marking aborts if a node in the subtree is already marked with GF_SG_CHILD_DIRTY +which means tat if you never clean the dirty flags, no propagation will take place +*/ +void gf_node_dirty_set(GF_Node *node, u32 flags, Bool dirty_parents); + +/*mark all parent subtrees for this node as GF_SG_CHILD_DIRTY +Note: parent subtree marking aborts if a node in the subtree is already marked with GF_SG_CHILD_DIRTY +which means that if you never clean the dirty flags, no propagation will take place +*/ +void gf_node_dirty_parents(GF_Node *node); + +/*set dirty flag off. It is the user responsability to clear dirty flags +if @flags is 0, all flags are set off +if @flags is not 0, removes the indicated flags from the node dirty state +*/ +void gf_node_dirty_clear(GF_Node *node, u32 flags); + +/*if the node is in a dirty state, resets it and the state of all its children*/ +void gf_node_dirty_reset(GF_Node *node); + +/*get dirty flag value*/ +u32 gf_node_dirty_get(GF_Node *node); + +/*Notes on GF_FieldInfo +all scene graph implementations should answer node field query with this interface. +In case an implementation does not use this: + - the implementation shall handle the parent node dirty flag itself most of the time + - the implementation shall NOT allow referencing of a graph node in a parent graph node (when inlining +content) otherwise the app is guaranteed to crash. +*/ + +/*other fieldTypes may be ignored by implmentation not using VRML/MPEG4 native types*/ + +typedef struct +{ + /*0-based index of the field in the node*/ + u32 fieldIndex; + /*field type - VRML/MPEG4 types are listed in scenegraph_vrml.h*/ + u32 fieldType; + /*far ptr to the field (eg GF_Node **, GF_List**, MFInt32 *, ...)*/ + void *far_ptr; + /*field name*/ + const char *name; + /*NDT type in case of SF/MFNode field - cf BIFS specific tools*/ + u32 NDTtype; + /*event type*/ + u32 eventType; + /*eventin handler if any*/ + void (*on_event_in)(GF_Node *pNode); +} GF_FieldInfo; + +/*returns number of field for this node*/ +u32 gf_node_get_field_count(GF_Node *node); + +/*fill the field info structure for the given field*/ +GF_Err gf_node_get_field(GF_Node *node, u32 FieldIndex, GF_FieldInfo *info); + +/*get the field by its name*/ +GF_Err gf_node_get_field_by_name(GF_Node *node, char *name, GF_FieldInfo *field); + +typedef struct __tag_scene_graph GF_SceneGraph; + +/*scene graph constructor*/ +GF_SceneGraph *gf_sg_new(); + +/*creates a sub scene graph (typically used with Inline node): independent graph with same private stack, +and user callbacks as parent. All routes triggered in this subgraph are executed in the parent graph (this +means you only have to activate routes on the main graph) +NOTE: the resulting graph is not destroyed when the parent graph is +*/ +GF_SceneGraph *gf_sg_new_subscene(GF_SceneGraph *scene); + +/*destructor*/ +void gf_sg_del(GF_SceneGraph *sg); +/*reset the full graph - all nodes, routes and protos are destroyed*/ +void gf_sg_reset(GF_SceneGraph *sg); + +/*parses the given XML document and returns a scene graph composed of GF_DOMFullNode*/ +GF_Err gf_sg_new_from_xml_doc(const char *src, GF_SceneGraph **scene); + +/*set/get user private data*/ +void gf_sg_set_private(GF_SceneGraph *sg, void *user_priv); +void *gf_sg_get_private(GF_SceneGraph *sg); + +/*set the scene timer (fct returns time in sec)*/ +void gf_sg_set_scene_time_callback(GF_SceneGraph *scene, Double (*GetSceneTime)(void *user_priv) ); + +enum +{ + /*function called upon node creation. + ctxdata is not used*/ + GF_SG_CALLBACK_INIT = 0, + /*function called upon node modification. You typically will set some of the dirty flags here. + ctxdata is the fieldInfo pointer of the modified field*/ + GF_SG_CALLBACK_MODIFIED, + /*function called when the a "set dirty" propagates to root node of the graph + ctxdata is not used*/ + GF_SG_CALLBACK_GRAPH_DIRTY, +}; + +/*set node callback: function called upon node creation. +Application should instanciate the node rendering stack and any desired callback*/ +void gf_sg_set_node_callback(GF_SceneGraph *sg, void (*NodeCallback)(void *user_priv, u32 type, GF_Node *node, void *ctxdata) ); + +/*get/set the root node of the graph*/ +GF_Node *gf_sg_get_root_node(GF_SceneGraph *sg); +void gf_sg_set_root_node(GF_SceneGraph *sg, GF_Node *node); + +/*finds a registered node by ID*/ +GF_Node *gf_sg_find_node(GF_SceneGraph *sg, u32 nodeID); +/*finds a registered node by DEF name*/ +GF_Node *gf_sg_find_node_by_name(GF_SceneGraph *sg, char *name); + +/*used to signal modification of a node, indicating which field is modified - exposed for BIFS codec, +should not be needed by other apps*/ +void gf_node_changed(GF_Node *node, GF_FieldInfo *fieldChanged); + +/*returns the graph this node belongs to*/ +GF_SceneGraph *gf_node_get_graph(GF_Node *node); + +/*Set size info for the graph - by default graphs have no size and are in meter metrics (VRML like) +if any of width or height is 0, the graph has no size info*/ +void gf_sg_set_scene_size_info(GF_SceneGraph *sg, u32 width, u32 Height, Bool usePixelMetrics); +/*returns 1 if pixelMetrics*/ +Bool gf_sg_use_pixel_metrics(GF_SceneGraph *sg); +/*returns 0 if no size info, otherwise 1 and set width/height*/ +Bool gf_sg_get_scene_size_info(GF_SceneGraph *sg, u32 *width, u32 *Height); + +/*creates a node of the given tag. sg is the parent scenegraph of the node, +eg the root one for scene nodes or the proto one for proto code (cf proto) +Note: + - NODE IS NOT REGISTERED (no instances) AND CANNOT BE DESTROYED UNTIL REGISTERED + - this doesn't perform application setup for the node, this must be done by the caller +*/ +GF_Node *gf_node_new(GF_SceneGraph *sg, u32 tag); +/*inits node (either internal stack or user-defined) - usually called once the node has been fully loaded*/ +void gf_node_init(GF_Node *node); + +/*clones a node in the given graph and register with parent cloned. The cloning handles ID based on id_suffix: + id_suffix = NULL: all IDs are removed from the cloned subtree, (each node instance will become a hard copy) + id_suffix = "": ID will be kept exactly as they where in the original subtree - this may lead to errors due to + the presence of the same ID depending on the standard (DOM, ...). + id_suffix = anything: all IDs are translated ($(name) -> $(name)id_suffix) and bynary IDs are generated on the fly +*/ +GF_Node *gf_node_clone(GF_SceneGraph *inScene, GF_Node *orig, GF_Node *cloned_parent, char *id_suffix, Bool deep); + +/*gets scene time for scene this node belongs too, 0 if timeline not specified*/ +Double gf_node_get_scene_time(GF_Node *node); + +/*retuns next available NodeID*/ +u32 gf_sg_get_next_available_node_id(GF_SceneGraph *sg); +/*retuns max ID used in graph*/ +u32 gf_sg_get_max_node_id(GF_SceneGraph *sg); + +const char *gf_node_get_name_and_id(GF_Node*node, u32 *id); + + +enum +{ + GF_SG_FOCUS_AUTO = 1, + GF_SG_FOCUS_NEXT, + GF_SG_FOCUS_PREV, + GF_SG_FOCUS_NORTH, + GF_SG_FOCUS_NORTH_EAST, + GF_SG_FOCUS_EAST, + GF_SG_FOCUS_SOUTH_EAST, + GF_SG_FOCUS_SOUTH, + GF_SG_FOCUS_SOUTH_WEST, + GF_SG_FOCUS_WEST, + GF_SG_FOCUS_NORTH_WEST +}; + +typedef struct +{ + const char *url; + const char **params; + u32 nb_params; +} GF_JSAPIURI; + +typedef struct +{ + const char *section; + const char *key; + const char *key_val; +} GF_JSAPIOPT; + + /*for script message option*/ +typedef struct +{ + GF_Err e; + const char *msg; +} GF_JSAPIINFO; + + +typedef union +{ + u32 opt; + Fixed val; + GF_Point2D pt; + GF_Rect rc; + Double time; + GF_BBox bbox; + GF_Matrix mx; + GF_JSAPIURI uri; + GF_JSAPIOPT gpac_cfg; + GF_Node *node; + struct __gf_download_manager *dnld_man; + GF_SceneGraph *scene; + void *term; + GF_JSAPIINFO info; +} GF_JSAPIParam; + +enum +{ + /*!push message from script engine.*/ + GF_JSAPI_OP_MESSAGE, + /*!get scene URI.*/ + GF_JSAPI_OP_GET_SCENE_URI, + /*!get current user agent scale.*/ + GF_JSAPI_OP_GET_SCALE, + /*!set current user agent scale.*/ + GF_JSAPI_OP_SET_SCALE, + /*!get current user agent rotation.*/ + GF_JSAPI_OP_GET_ROTATION, + /*!set current user agent rotation.*/ + GF_JSAPI_OP_SET_ROTATION, + /*!get current user agent translation.*/ + GF_JSAPI_OP_GET_TRANSLATE, + /*!set current user agent translation.*/ + GF_JSAPI_OP_SET_TRANSLATE, + /*!get node time.*/ + GF_JSAPI_OP_GET_TIME, + /*!set node time.*/ + GF_JSAPI_OP_SET_TIME, + /*!get current viewport.*/ + GF_JSAPI_OP_GET_VIEWPORT, + /*!get object bounding box in object local coord system.*/ + GF_JSAPI_OP_GET_LOCAL_BBOX, + /*!get object bounding box in world (screen) coord system.*/ + GF_JSAPI_OP_GET_SCREEN_BBOX, + /*!get transform matrix at object.*/ + GF_JSAPI_OP_GET_TRANSFORM, + /*!move focus according to opt value.*/ + GF_JSAPI_OP_MOVE_FOCUS, + /*!set focus to given node.*/ + GF_JSAPI_OP_GET_FOCUS, + /*!set focus to given node.*/ + GF_JSAPI_OP_SET_FOCUS, + /*!replace target scene URL*/ + GF_JSAPI_OP_LOAD_URL, + /*!get option by section and key*/ + GF_JSAPI_OP_GET_OPT, + /*!get option by section and key*/ + GF_JSAPI_OP_SET_OPT, + /*!retrieve download manager*/ + GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, + /*!get navigation speed if any*/ + GF_JSAPI_OP_GET_SPEED, + /*!get current frame rate*/ + GF_JSAPI_OP_GET_FPS, + /*!set current title*/ + GF_JSAPI_OP_SET_TITLE, + /*!gets DCCI scenegraph if any*/ + GF_JSAPI_OP_GET_DCCI, + /*!gets subscene for current node if any*/ + GF_JSAPI_OP_GET_SUBSCENE, + /*!resolves relative Xlink based on xml:base*/ + GF_JSAPI_OP_RESOLVE_XLINK, + /*!evaluates if the given IRI is available for playback (returns 1) or not. If the IRI is + NULL, this evaluates whether the scene is ready for composition (canvas setup) or not.*/ + GF_JSAPI_OP_EVAL_IRI, + + /*!gets GPAC terminal*/ + GF_JSAPI_OP_GET_TERM, + + /*!pauses an SVG element*/ + GF_JSAPI_OP_PAUSE_SVG, + /*!resumes an SVG ELEMENT*/ + GF_JSAPI_OP_RESUME_SVG, +}; +/* +interface to various get/set options: + type: operand type, one of the above + node: target node, scene root node or NULL + param: i/o param, depending on operand type +*/ +typedef Bool (*gf_sg_script_action)(void *callback, u32 type, GF_Node *node, GF_JSAPIParam *param); + +/*assign API to scene graph - by default, sub-graphs inherits the API if set*/ +void gf_sg_set_script_action(GF_SceneGraph *scene, gf_sg_script_action script_act, void *cbk); + +/*load script into engine - this should be called only for script in main scene, loading of scripts +in protos is done internally when instanciating the proto*/ +void gf_sg_script_load(GF_Node *script); + +/*returns true if current lib has javascript support*/ +Bool gf_sg_has_scripting(); + + + +/* + scene graph command tools used for BIFS and LASeR + These are used to store updates in memory without applying changes to the graph, + for dumpers, encoders ... + The commands can then be applied through this lib +*/ + +/* + Currently defined possible modifications +*/ +enum +{ + /*BIFS commands*/ + GF_SG_SCENE_REPLACE = 0, + GF_SG_NODE_REPLACE, + GF_SG_FIELD_REPLACE, + GF_SG_INDEXED_REPLACE, + GF_SG_ROUTE_REPLACE, + GF_SG_NODE_DELETE, + GF_SG_INDEXED_DELETE, + GF_SG_ROUTE_DELETE, + GF_SG_NODE_INSERT, + GF_SG_INDEXED_INSERT, + GF_SG_ROUTE_INSERT, + /*extended updates (BIFS-only)*/ + GF_SG_PROTO_INSERT, + GF_SG_PROTO_DELETE, + GF_SG_PROTO_DELETE_ALL, + GF_SG_MULTIPLE_REPLACE, + GF_SG_MULTIPLE_INDEXED_REPLACE, + GF_SG_GLOBAL_QUANTIZER, + /*same as NodeDelete, and also updates OrderedGroup.order when deleting a child*/ + GF_SG_NODE_DELETE_EX, + + /*BIFS*/ + GF_SG_FIELD_REPLACE_OP, + GF_SG_INDEXED_REPLACE_OP, + + GF_SG_LAST_BIFS_COMMAND, + + + /*LASER commands*/ + GF_SG_LSR_NEW_SCENE, + GF_SG_LSR_REFRESH_SCENE, + GF_SG_LSR_ADD, + GF_SG_LSR_CLEAN, + GF_SG_LSR_REPLACE, + GF_SG_LSR_DELETE, + GF_SG_LSR_INSERT, + GF_SG_LSR_RESTORE, + GF_SG_LSR_SAVE, + GF_SG_LSR_SEND_EVENT, + GF_SG_LSR_ACTIVATE, + GF_SG_LSR_DEACTIVATE, + + GF_SG_UNDEFINED +}; + + +/* + single command wrapper + + NOTE: In order to maintain node registry, the nodes replaced/inserted MUST be registered with + their parents even when the command is never applied. Registering shall be performed + with gf_node_register (see below). + If you fail to do so, a node may be destroyed when destroying a command while still used + in another command or in the graph - this will just crash. +*/ + +/*structure used to store field info, pos and static pointers to GF_Node/MFNode in commands*/ +typedef struct +{ + u32 fieldIndex; + /*field type*/ + u32 fieldType; + /*field pointer for multiple replace/multiple indexed replace - if multiple indexed replace, must be the SF field being changed*/ + void *field_ptr; + /*replace/insert/delete pos - -1 is append except in multiple indexed replace*/ + s32 pos; + + /*Whenever field pointer is of type GF_Node, store the node here and set the far pointer to this address.*/ + GF_Node *new_node; + /*Whenever field pointer is of type MFNode, store the node list here and set the far pointer to this address.*/ + GF_ChildNodeItem *node_list; +} GF_CommandField; + +typedef struct +{ + GF_SceneGraph *in_scene; + u32 tag; + + /*node the command applies to - may be NULL*/ + GF_Node *node; + + /*list of GF_CommandField for all field commands replace/ index insert / index replace / index delete / MultipleReplace / MultipleIndexedreplace + the content is destroyed when deleting the command*/ + GF_List *command_fields; + + /*may be NULL, and may be present with any command inserting a node*/ + GF_List *scripts_to_load; + /*for authoring purposes - must be cleaned by user*/ + Bool unresolved; + char *unres_name; + + /*scene replace command: + root node is stored in com->node + protos are stored in com->new_proto_list + routes are stored as RouteInsert in the same frame + BIFS only + */ + Bool use_names; + + /*proto list to insert - BIFS only*/ + GF_List *new_proto_list; + /*proto ID list to delete - BIFS only*/ + u32 *del_proto_list; + u32 del_proto_list_size; + + + /*route insert, replace and delete (BIFS only) + fromNodeID is also used to identify operandElementId in LASeR Add/Replace + + OR + sendEvent + */ +// union { + u32 RouteID; + u32 send_event_name; +// }; +// union { + char *def_name; + char *send_event_string; +// }; +// union { + u32 fromNodeID; + s32 send_event_integer; +// }; + u32 fromFieldIndex; + +// union { + u32 toNodeID; + s32 send_event_x; +// }; +// union { + u32 toFieldIndex; + s32 send_event_y; +// }; + Bool aggregated; +} GF_Command; + + +/*creates command - graph only needed for SceneReplace*/ +GF_Command *gf_sg_command_new(GF_SceneGraph *in_scene, u32 tag); +/*deletes command*/ +void gf_sg_command_del(GF_Command *com); +/*apply command to graph - the command content is kept unchanged for authoring purposes - THIS NEEDS TESTING AND FIXING +@time_offset: offset for time fields if desired*/ +GF_Err gf_sg_command_apply(GF_SceneGraph *inScene, GF_Command *com, Double time_offset); +/*apply list if command to graph - the command content is kept unchanged for authoring purposes +@time_offset: offset for time fields if desired*/ +GF_Err gf_sg_command_apply_list(GF_SceneGraph *graph, GF_List *comList, Double time_offset); +/*returns new commandFieldInfo structure and registers it with command*/ +GF_CommandField *gf_sg_command_field_new(GF_Command *com); +/*clones the command in another graph - needed for uncompressed conditional in protos +if force_clone is not set and the target graph is the same as the command graph, nodes are just registered +with the new commands rather than cloned*/ +GF_Command *gf_sg_command_clone(GF_Command *com, GF_SceneGraph *inGraph, Bool force_clone); + +#ifdef __cplusplus +} +#endif + + + +#endif /*_GF_SCENEGRAPH_H_*/ + + diff --git a/include/gpac/scenegraph_svg.h b/include/gpac/scenegraph_svg.h new file mode 100644 index 0000000..2d5d534 --- /dev/null +++ b/include/gpac/scenegraph_svg.h @@ -0,0 +1,620 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004 + * All rights reserved + * + * This file is part of GPAC / SVG Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_SG_SVG_H_ +#define _GF_SG_SVG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/scenegraph.h> +#include <gpac/svg_types.h> + +/******************************************************************************* + * + * DOM base scene graph + * + *******************************************************************************/ + +enum +{ + /*should never be used, this is only a parsing error*/ + TAG_DOM_ATTRIBUTE_NULL, + /*this tag is set for a full dom attribute only - attribute name is then available*/ + TAG_DOM_ATT_any, + + TAG_XML_ATT_RANGE_FIRST, + TAG_XML_ATT_id = TAG_XML_ATT_RANGE_FIRST, + TAG_XML_ATT_base, + TAG_XML_ATT_lang, + TAG_XML_ATT_space, + TAG_XML_ATT_RANGE_LAST, + TAG_XLINK_ATT_RANGE_FIRST, + + TAG_XLINK_ATT_type = TAG_XLINK_ATT_RANGE_FIRST, + TAG_XLINK_ATT_role, + TAG_XLINK_ATT_arcrole, + TAG_XLINK_ATT_title, + TAG_XLINK_ATT_href, + TAG_XLINK_ATT_show, + TAG_XLINK_ATT_actuate, + TAG_XLINK_ATT_RANGE_LAST, + + TAG_XMLEV_ATT_RANGE_FIRST, + TAG_XMLEV_ATT_event, + TAG_XMLEV_ATT_phase, + TAG_XMLEV_ATT_propagate, + TAG_XMLEV_ATT_defaultAction, + TAG_XMLEV_ATT_observer, + TAG_XMLEV_ATT_target, + TAG_XMLEV_ATT_handler, + TAG_XMLEV_ATT_RANGE_LAST, + + TAG_LSR_ATT_RANGE_FIRST, + TAG_LSR_ATT_enabled, + TAG_LSR_ATT_RANGE_LAST, + /*these attribute types are only use for binary purpose*/ + TAG_LSR_ATT_children, + TAG_LSR_ATT_overflow, + TAG_LSR_ATT_rotation, + TAG_LSR_ATT_scale, + TAG_LSR_ATT_translation, + TAG_LSR_ATT_svg_width, + TAG_LSR_ATT_svg_height, + TAG_LSR_ATT_textContent, + /*WHAT THE HECK IS THIS THING IN THE SDL BUT NOWHERE IN THE SPEC ?*/ + TAG_LSR_ATT_text_display, + + TAG_SVG_ATT_RANGE_FIRST, + + TAG_XBL_ATT_RANGE_FIRST = TAG_SVG_ATT_RANGE_FIRST + 256, + TAG_XBL_ATT_id = TAG_XBL_ATT_RANGE_FIRST, + TAG_XBL_ATT_extends, + TAG_XBL_ATT_display, + TAG_XBL_ATT_inheritstyle, + TAG_XBL_ATT_includes, + TAG_XBL_ATT_name, + TAG_XBL_ATT_implements, + TAG_XBL_ATT_type, + TAG_XBL_ATT_readonly, + TAG_XBL_ATT_onget, + TAG_XBL_ATT_onset, + TAG_XBL_ATT_event, + TAG_XBL_ATT_action, + TAG_XBL_ATT_phase, + TAG_XBL_ATT_button, + TAG_XBL_ATT_modifiers, + TAG_XBL_ATT_keycode, + TAG_XBL_ATT_key, + TAG_XBL_ATT_charcode, + TAG_XBL_ATT_clickcount, + TAG_XBL_ATT_command, + TAG_XBL_ATT_preventdefault, + TAG_XBL_ATT_src, +}; + + +#define GF_DOM_BASE_ATTRIBUTE \ + u16 tag; /*attribute identifier*/ \ + u16 data_type; /*attribute datatype*/ \ + void *data; /*data pointer*/ \ + struct __dom_base_attribute *next; /*next attribute*/ + +#define GF_DOM_FULL_ATTRIBUTE \ + GF_DOM_ATTRIBUTE \ + +typedef struct __dom_base_attribute +{ + GF_DOM_BASE_ATTRIBUTE +} GF_DOMAttribute; + +typedef struct __dom_full_attribute +{ + GF_DOM_BASE_ATTRIBUTE + u32 xmlns; + char *name; /*attribute name - in this case, the data field is the attribute literal value*/ +} GF_DOMFullAttribute; + +#define GF_DOM_BASE_NODE \ + BASE_NODE \ + CHILDREN \ + GF_DOMAttribute *attributes; + +typedef struct __dom_base_node +{ + GF_DOM_BASE_NODE +} GF_DOMNode; + +typedef struct __dom_full_node +{ + GF_DOM_BASE_NODE + char *name; + u32 ns; +} GF_DOMFullNode; + + + +enum +{ + /*no NS specified, it will be evaluated from attribute/node name*/ + GF_XMLNS_NONE, + + GF_XMLNS_XML, + GF_XMLNS_XLINK, + GF_XMLNS_XMLEV, + GF_XMLNS_LASER, + GF_XMLNS_SVG, + GF_XMLNS_XBL, + + /*any other namespace uses the CRC32 of the namespace as an identifier*/ +}; + +GF_Err gf_sg_add_namespace(GF_SceneGraph *sg, char *name, char *qname); +GF_Err gf_sg_remove_namespace(GF_SceneGraph *sg, char *name, char *qname); +u32 gf_sg_get_namespace_code(GF_SceneGraph *sg, char *qname); +u32 gf_sg_get_namespace_code_from_name(GF_SceneGraph *sg, char *name); +const char *gf_sg_get_namespace_qname(GF_SceneGraph *sg, u32 xmlns_id); + +u32 gf_xml_get_element_namespace(GF_Node *n); +const char *gf_sg_get_namespace(GF_SceneGraph *sg, u32 xmlns_id); + +void gf_xml_push_namespaces(GF_DOMNode *elt); +void gf_xml_pop_namespaces(GF_DOMNode *elt); + + + + +enum +{ + GF_DOM_TEXT_REGULAR = 0, + GF_DOM_TEXT_CDATA, + /*inserted text node (typically external script)*/ + GF_DOM_TEXT_INSERTED +}; + +typedef struct +{ + BASE_NODE + CHILDREN + char *textContent; + u32 type; +} GF_DOMText; + +/*creates a new text node, assign string (does NOT duplicate it) and register node with parent if desired*/ +GF_DOMText *gf_dom_add_text_node(GF_Node *parent, char *text_data); + +/*creates a new text node - this DOES NOT register the node at all*/ +GF_DOMText *gf_dom_new_text_node(GF_SceneGraph *sg); + +typedef struct +{ + BASE_NODE + CHILDREN + char *data; + u32 data_size; + GF_List *updates; +} GF_DOMUpdates; + +/*creates a new updates node and register node with parent*/ +GF_DOMUpdates *gf_dom_add_updates_node(GF_Node *parent); + +typedef struct +{ + Bool bufferValid; + u32 level; + Fixed remaining_time; + u16 status; + const char *session_name; + u32 nb_streams; + struct mae_item {u32 streamType; u32 mediaType; u32 transport; } streams[20]; +} GF_DOMMediaAccessEvent; + +/* + DOM event handling +*/ +enum +{ + GF_DOM_EVENT_PHASE_CAPTURE = 1, + GF_DOM_EVENT_PHASE_AT_TARGET = 2, + GF_DOM_EVENT_PHASE_BUBBLE = 3, + + /*special phase indicating the event has been canceled*/ + GF_DOM_EVENT_PHASE_CANCEL = 1<<5, + /*special phase indicating the event has been canceled immediately*/ + GF_DOM_EVENT_PHASE_CANCEL_ALL = 1<<6, + /*special phase indicating the default action of the event is prevented*/ + GF_DOM_EVENT_PHASE_PREVENT = 1<<7, +}; + +/*possible event targets*/ +enum +{ + GF_DOM_EVENT_NODE, + GF_DOM_EVENT_DOCUMENT, + GF_DOM_EVENT_XHR, + GF_DOM_EVENT_TIMER, + GF_DOM_EVENT_CONNECTION, +}; + +typedef struct +{ + GF_List *evt_list; + void *ptr; + u32 ptr_type; +} GF_DOMEventTarget; + +GF_Err gf_dom_listener_add(GF_Node *listener, GF_DOMEventTarget *evt_target); + + +typedef struct +{ + /*event type, as defined in <gpac/events.h>*/ + u32 type; + /*event phase type, READ-ONLY + 0: at target, 1: bubbling, 2: capturing , 3: canceled + */ + u8 event_phase; + u8 bubbles; + u8 cancelable; + /*output only - indicates UI events (mouse) have been detected*/ + u8 has_ui_events; + + /*we don't use a GF_DOMEventTarget here since the structure is only created when events are attached */ + void *target; + u32 target_type; + + GF_DOMEventTarget *currentTarget; + Double timestamp; + /*UIEvent extension. + For mouse extensions: number of clicks + For key event: the key code + For SMIL event: number of iteration (repeat) + */ + u32 detail; + + /*MouseEvent extension*/ + s32 screenX, screenY; + s32 clientX, clientY; + u32 button; + /*key flags*/ + u32 key_flags; + /*key hardware code*/ + u32 key_hw_code; + GF_Node *relatedTarget; + /*Zoom event*/ + GF_Rect screen_rect; + GF_Point2D prev_translate, new_translate; + Fixed prev_scale, new_scale; + /* CPU */ + u32 cpu_percentage; + /* Battery */ + Bool onBattery; + u32 batteryState, batteryLevel; + /*smil event time*/ + Double smil_event_time; + /* mutation event */ + GF_Node *relatedNode; + + /*DOM event used in VRML (GPAC's internal)*/ + Bool is_vrml; + GF_DOMMediaAccessEvent *mae; + + /*number of listeners triggered by the event*/ + u32 consumed; +} GF_DOM_Event; + +/*fires event on the specified node +BE CAREFULL: event execution may very well destroy ANY node, especially the event target node !! +*/ +Bool gf_dom_event_fire(GF_Node *node, GF_DOM_Event *event); + +/*fires event on the specified node +BE CAREFULL: event execution may very well destroy ANY node, especially the event target node !! +use_stack: a list of parent node/use node pairs for bubbling phase - may be NULL +*/ +Bool gf_dom_event_fire_ex(GF_Node *node, GF_DOM_Event *event, GF_List *use_stack); + +u32 gf_dom_event_type_by_name(const char *name); +const char *gf_dom_event_get_name(u32 type); + +const char *gf_dom_get_key_name(u32 key_identifier); +u32 gf_dom_get_key_type(char *key_name); + + +/*listener is simply a node added to the node events list. +Only one observer can be attached to a listener. The listener will remove itself from the observer +event list when destructed.*/ + +typedef struct __xml_ev_handler +{ + GF_DOM_BASE_NODE + void (*handle_event)(GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer); + /*if handler targets a VRML script, point to the script here*/ + void *js_context; + /*target EventListener object (this) */ + void *evt_listen_obj; + /*function value for spidermonkey - we cannot use JS_CallFunction since it does not work on closures + we use 64 bits to store the value for portability safety */ + u64 js_fun_val; + /*compiled function for the case were CallFunction is needed*/ + void *js_fun; +} GF_DOMHandler; + + + +enum +{ + /*basic DOM events*/ + GF_DOM_EVENT_DOM = 1, + /*DOM mutation events*/ + GF_DOM_EVENT_MUTATION = 1<<1, + /*DOM mouse events*/ + GF_DOM_EVENT_MOUSE = 1<<2, + /*DOM focus events*/ + GF_DOM_EVENT_FOCUS = 1<<3, + /*DOM key events*/ + GF_DOM_EVENT_KEY = 1<<4, + /*DOM/SVG/HTML UI events (resize, scroll, ...)*/ + GF_DOM_EVENT_UI = 1<<5, + /*text events*/ + GF_DOM_EVENT_TEXT = 1<<6, + /*SVG events*/ + GF_DOM_EVENT_SVG = 1<<7, + /*SMIL events*/ + GF_DOM_EVENT_SMIL = 1<<8, + /*LASeR events*/ + GF_DOM_EVENT_LASER = 1<<9, + /*MediaAccess events*/ + GF_DOM_EVENT_MEDIA_ACCESS = 1<<10, + + /*fake events - these events are NEVER fired*/ + GF_DOM_EVENT_FAKE = 1<<31, +}; +u32 gf_dom_event_get_category(u32 type); +u32 gf_sg_get_dom_event_filter(GF_SceneGraph *sg); +u32 gf_node_get_dom_event_filter(GF_Node *node); + +void gf_sg_register_event_type(GF_SceneGraph *sg, u32 type); +void gf_sg_unregister_event_type(GF_SceneGraph *sg, u32 type); + +/*adds a listener to the node. +The listener node is NOT registered with the node (it may very well not be a direct child of the node) +@listener is a listenerElement (XML event) +*/ +GF_Err gf_node_dom_listener_add(GF_Node *node, GF_Node *listener); +u32 gf_dom_listener_count(GF_Node *node); +GF_Node *gf_dom_listener_get(GF_Node *node, u32 i); + +/*creates a default listener/handler for the given event on the given node, and return the +handler element to allow for handler function override +Listener/handler are stored at the node level*/ +GF_DOMHandler *gf_dom_listener_build(GF_Node *observer, u32 event_type, u32 event_param); + + +void gf_node_register_iri(GF_SceneGraph *sg, XMLRI *iri); +void gf_node_unregister_iri(GF_SceneGraph *sg, XMLRI *iri); +u32 gf_node_animation_count(GF_Node *node); + +GF_Err gf_node_store_embedded_data(XMLRI *iri, const char *cache_dir, const char *base_filename); + + +/************************************************** + * SVG's styling properties (see 6.1 in REC 1.1) * + *************************************************/ + +typedef struct { + /* Tiny 1.2 properties*/ + SVG_Paint *color; + SVG_Paint *fill; + SVG_Paint *stroke; + SVG_Paint *solid_color; + SVG_Paint *stop_color; + SVG_Paint *viewport_fill; + + SVG_Number *fill_opacity; + SVG_Number *solid_opacity; + SVG_Number *stop_opacity; + SVG_Number *stroke_opacity; + SVG_Number *viewport_fill_opacity; + SVG_Number *opacity; /* Restricted property in Tiny 1.2 */ + + SVG_Number *audio_level; + Fixed computed_audio_level; + + SVG_RenderingHint *color_rendering; + SVG_RenderingHint *image_rendering; + SVG_RenderingHint *shape_rendering; + SVG_RenderingHint *text_rendering; + + SVG_Display *display; + SVG_Visibility *visibility; + SVG_Overflow *overflow; /* Restricted property in Tiny 1.2 */ + + SVG_FontFamily *font_family; + SVG_FontSize *font_size; + SVG_FontStyle *font_style; + SVG_FontWeight *font_weight; + SVG_FontVariant *font_variant; + SVG_Number *line_increment; + SVG_TextAnchor *text_anchor; + SVG_DisplayAlign *display_align; + SVG_TextAlign *text_align; + + SVG_PointerEvents *pointer_events; + + SVG_FillRule *fill_rule; + + SVG_StrokeDashArray *stroke_dasharray; + SVG_Length *stroke_dashoffset; + SVG_StrokeLineCap *stroke_linecap; + SVG_StrokeLineJoin *stroke_linejoin; + SVG_Number *stroke_miterlimit; + SVG_Length *stroke_width; + SVG_VectorEffect *vector_effect; + + /* Full 1.1 props, i.e. not implemented */ +/* + SVG_String *font; + SVG_String *font_size_adjust; + SVG_String *font_stretch; + SVG_String *direction; + SVG_String *letter_spacing; + SVG_String *text_decoration; + SVG_String *unicode_bidi; + SVG_String *word_spacing; + SVG_String *clip; + SVG_String *cursor; + SVG_String *clip_path; + SVG_String *clip_rule; + SVG_String *mask; + SVG_String *enable_background; + SVG_String *filter; + SVG_String *flood_color; + SVG_String *flood_opacity; + SVG_String *lighting_color; + SVG_String *color_interpolation; + SVG_String *color_interpolation_filters; + SVG_String *color_profile; + SVG_String *marker; + SVG_String *marker_end; + SVG_String *marker_mid; + SVG_String *marker_start; + SVG_String *alignment_baseline; + SVG_String *baseline_shift; + SVG_String *dominant_baseline; + SVG_String *glyph_orientation_horizontal; + SVG_String *glyph_orientation_vertical; + SVG_String *kerning; + SVG_String *writing_mode; +*/ +} SVGPropertiesPointers; + +/************************************* + * Generic SVG element functions * + *************************************/ + +void gf_svg_properties_init_pointers(SVGPropertiesPointers *svg_props); +void gf_svg_properties_reset_pointers(SVGPropertiesPointers *svg_props); + +void gf_svg_apply_animations(GF_Node *node, SVGPropertiesPointers *render_svg_props); +Bool gf_svg_has_appearance_flag_dirty(u32 flags); + +Bool gf_svg_is_element_transformable(u32 tag); + +void *gf_svg_create_attribute_value(u32 attribute_type); +void gf_svg_delete_attribute_value(u32 type, void *value, GF_SceneGraph *sg); +/* a == b */ +Bool gf_svg_attributes_equal(GF_FieldInfo *a, GF_FieldInfo *b); +/* a = b */ +GF_Err gf_svg_attributes_copy(GF_FieldInfo *a, GF_FieldInfo *b, Bool clamp); +/* c = a + b */ +GF_Err gf_svg_attributes_add(GF_FieldInfo *a, GF_FieldInfo *b, GF_FieldInfo *c, Bool clamp); +Bool gf_svg_attribute_is_interpolatable(u32 type) ; +/* c = coef * a + (1 - coef) * b */ +GF_Err gf_svg_attributes_interpolate(GF_FieldInfo *a, GF_FieldInfo *b, GF_FieldInfo *c, Fixed coef, Bool clamp); +/* c = alpha * a + beta * b */ +GF_Err gf_svg_attributes_muladd(Fixed alpha, GF_FieldInfo *a, Fixed beta, GF_FieldInfo *b, GF_FieldInfo *c, Bool clamp); + +GF_Err gf_node_get_attribute_by_tag(GF_Node *node, u32 attribute_tag, Bool create_if_not_found, Bool set_default, GF_FieldInfo *field); + +char *gf_svg_attribute_type_to_string(u32 att_type); +GF_Err gf_svg_parse_attribute(GF_Node *n, GF_FieldInfo *info, char *attribute_content, u8 anim_value_type); +void gf_svg_parse_style(GF_Node *n, char *style); + +GF_Err gf_svg_dump_attribute(GF_Node *elt, GF_FieldInfo *info, char *attValue); +GF_Err gf_svg_dump_attribute_indexed(GF_Node *elt, GF_FieldInfo *info, char *attValue); + +void gf_svg_path_build(GF_Path *path, GF_List *commands, GF_List *points); + +GF_Err gf_svg_parse_element_id(GF_Node *n, const char *nodename, Bool warning_if_defined); + +const char *gf_svg_get_system_paint_server_name(u32 paint_type); +u32 gf_svg_get_system_paint_server_type(const char *name); + + +Bool gf_smil_notify_timed_elements(GF_SceneGraph *sg); +void gf_smil_timing_insert_clock(GF_Node *elt, Bool is_end, Double clock); + +void gf_svg_parse_transformlist(GF_Matrix2D *mat, char *attribute_content); + +typedef struct _smil_timing_rti SMIL_Timing_RTI; + +enum +{ + SMIL_TIMING_EVAL_NONE = 0, + SMIL_TIMING_EVAL_UPDATE, + SMIL_TIMING_EVAL_FREEZE, + SMIL_TIMING_EVAL_REMOVE, + SMIL_TIMING_EVAL_REPEAT, + SMIL_TIMING_EVAL_FRACTION, + SMIL_TIMING_EVAL_DISCARD, + /*signaled the animation element has been inserted in the DOM tree*/ + SMIL_TIMING_EVAL_ACTIVATE, + /*signaled the animation element has been removed from the DOM tree*/ + SMIL_TIMING_EVAL_DEACTIVATE, +}; + +void gf_smil_set_evaluation_callback(GF_Node *smil_time, + void (*smil_evaluate)(struct _smil_timing_rti *rti, Fixed normalized_simple_time, u32 state)); + +void gf_smil_set_media_duration(SMIL_Timing_RTI *rti, Double media_duration); +Double gf_smil_get_media_duration(SMIL_Timing_RTI *rti); +GF_Node *gf_smil_get_element(SMIL_Timing_RTI *rti); + +Bool gf_smil_timing_is_active(GF_Node *node); +void gf_smil_timing_modified(GF_Node *node, GF_FieldInfo *field); + +/******************************************************************************* + * + * SVG Scene Graph for dynamic allocation of attributes * + * + *******************************************************************************/ + +/*SVG attributes are just DOM ones*/ +typedef struct __dom_base_attribute SVGAttribute; +typedef struct __dom_full_attribute SVGExtendedAttribute; +typedef struct __dom_base_node SVG_Element; + +typedef struct __xml_ev_handler SVG_handlerElement; + + +typedef struct _all_atts SVGAllAttributes; + +void gf_svg_flatten_attributes(SVG_Element *e, SVGAllAttributes *all_atts); +const char *gf_svg_get_attribute_name(GF_Node *elt, u32 tag); +u32 gf_svg_apply_inheritance(SVGAllAttributes *all_atts, SVGPropertiesPointers *render_svg_props) ; + +GF_DOMAttribute *gf_xml_create_attribute(GF_Node *node, u32 tag); +u32 gf_xml_get_attribute_type(u32 tag); +u32 gf_xml_get_attribute_tag(GF_Node *node, char *attribute_name, u32 ns); + +u32 gf_xml_get_element_tag(const char *element_name, u32 xmlns); + + +#ifdef __cplusplus +} +#endif + +#endif //_GF_SG_SVG_H_ diff --git a/include/gpac/scenegraph_vrml.h b/include/gpac/scenegraph_vrml.h new file mode 100644 index 0000000..c6edb49 --- /dev/null +++ b/include/gpac/scenegraph_vrml.h @@ -0,0 +1,623 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_SG_VRML_H_ +#define _GF_SG_VRML_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/scenegraph.h> +#include <gpac/math.h> + +/* + All extensions for VRML/MPEG-4/X3D graph structure +*/ + +/*reserved NDT for MPEG4 (match binary coding)*/ +#define MPEG4_RESERVED_NDT 200 + +/*the NDTs used in X3D not defined in MPEG4*/ +enum +{ + NDT_SFMetadataNode = MPEG4_RESERVED_NDT+1, + NDT_SFFillPropertiesNode, + NDT_SFX3DLinePropertiesNode, + NDT_SFGeoOriginNode, + NDT_SFHAnimNode, + NDT_SFHAnimDisplacerNode, + NDT_SFNurbsControlCurveNode, + NDT_SFNurbsSurfaceNode, + NDT_SFNurbsCurveNode +}; + +/* + VRML / BIFS TYPES DEFINITION +*/ + +/* + event types, as defined in the specs + this should not be needed by non binary codecs +*/ +enum +{ + GF_SG_EVENT_FIELD = 0, + GF_SG_EVENT_EXPOSED_FIELD = 1, + GF_SG_EVENT_IN = 2, + GF_SG_EVENT_OUT = 3, + GF_SG_EVENT_UNKNOWN = 4 +}; +const char *gf_sg_vrml_get_event_type_name(u32 EventType, Bool forX3D); + +/* + field coding mode + + BIFS defines the bitstream syntax contextually, and therefore sometimes refer to fields as indexed + in the node ("all" mode) or just as a sub-set (in, out, def, dyn modes) of similar types +*/ +enum +{ + /*all fields and events*/ + GF_SG_FIELD_CODING_ALL = 0, + /*defined fields (exposedField and Field)*/ + GF_SG_FIELD_CODING_DEF = 1, + /*input field (exposedField and eventIn)*/ + GF_SG_FIELD_CODING_IN = 2, + /*output field (exposedField and eventOut)*/ + GF_SG_FIELD_CODING_OUT = 3, + /*field that can be animated (subset of inFields) used in BIFS_Anim only*/ + GF_SG_FIELD_CODING_DYN = 4 +}; + +/*get the number of field in the given mode (BIFS specific)*/ +u32 gf_node_get_num_fields_in_mode(GF_Node *Node, u8 IndexMode); + +/* SF Types */ +typedef Bool SFBool; +typedef s32 SFInt32; +typedef s32 SFInt; +typedef Fixed SFFloat; +typedef Double SFDouble; + +typedef struct +{ + char* buffer; +} SFString; + +typedef Double SFTime; + +typedef struct { + Fixed red; + Fixed green; + Fixed blue; +} SFColor; + +typedef struct { + Fixed red; + Fixed green; + Fixed blue; + Fixed alpha; +} SFColorRGBA; + +typedef struct { + u32 OD_ID; + char *url; +} SFURL; + +typedef struct { + Double x; + Double y; +} SFVec2d; + +typedef struct { + Double x; + Double y; + Double z; +} SFVec3d; + +/*typedef's to main math tools*/ +typedef struct __vec2f SFVec2f; +typedef struct __vec3f SFVec3f; +typedef struct __vec4f SFRotation; +typedef struct __vec4f SFVec4f; + +typedef struct { + u32 width; + u32 height; + u8 numComponents; + unsigned char* pixels; +} SFImage; +typedef struct { + u32 bufferSize; + unsigned char* buffer; + /*uncompressed command list*/ + GF_List *commandList; +} SFCommandBuffer; + +/*Note on SFScript: the javascript or vrml script is handled in its decompressed (textual) format +since most JS interpreter work with text*/ +typedef struct { + unsigned char* script_text; +} SFScript; + + +/* MF Types */ + +/*generic MF field: all MF fields use the same syntax except MFNode which uses GF_List. You can thus use +this structure to safely typecast MF field pointers*/ +typedef struct { + u32 count; + char *array; +} GenMFField; + +typedef struct { + u32 count; + s32* vals; +} MFInt32; +typedef struct { + u32 count; + s32* vals; +} MFInt; +typedef struct { + u32 count; + Fixed *vals; +} MFFloat; +typedef struct { + u32 count; + Double *vals; +} MFDouble; +typedef struct { + u32 count; + u32* vals; +} MFBool; +typedef struct { + u32 count; + SFColor* vals; +} MFColor; +typedef struct { + u32 count; + SFColorRGBA* vals; +} MFColorRGBA; +typedef struct { + u32 count; + SFRotation* vals; +} MFRotation; +typedef struct { + u32 count; + Double* vals; +} MFTime; +typedef struct { + u32 count; + SFVec2f* vals; +} MFVec2f; +typedef struct { + u32 count; + SFVec2d* vals; +} MFVec2d; +typedef struct { + u32 count; + SFVec3f* vals; +} MFVec3f; +typedef struct { + u32 count; + SFVec3d* vals; +} MFVec3d; +typedef struct { + u32 count; + SFVec4f* vals; +} MFVec4f; + +typedef struct { + u32 count; + SFURL* vals; +} MFURL; +typedef struct { + u32 count; + char** vals; +} MFString; +typedef struct { + u32 count; + SFScript *vals; +} MFScript; + + +SFColorRGBA gf_sg_sfcolor_to_rgba(SFColor val); + +/*field types, as defined in BIFS encoding (used for scripts and proto coding)*/ +enum +{ + GF_SG_VRML_SFBOOL = 0, + GF_SG_VRML_SFFLOAT = 1, + GF_SG_VRML_SFTIME = 2, + GF_SG_VRML_SFINT32 = 3, + GF_SG_VRML_SFSTRING = 4, + GF_SG_VRML_SFVEC3F = 5, + GF_SG_VRML_SFVEC2F = 6, + GF_SG_VRML_SFCOLOR = 7, + GF_SG_VRML_SFROTATION = 8, + GF_SG_VRML_SFIMAGE = 9, + GF_SG_VRML_SFNODE = 10, + /*TO CHECK*/ + GF_SG_VRML_SFVEC4F = 11, + + /*used types in GPAC but not defined in the MPEG4 spec*/ + GF_SG_VRML_SFURL, + GF_SG_VRML_SFSCRIPT, + GF_SG_VRML_SFCOMMANDBUFFER, + /*used types in X3D*/ + GF_SG_VRML_SFDOUBLE, + GF_SG_VRML_SFCOLORRGBA, + GF_SG_VRML_SFVEC2D, + GF_SG_VRML_SFVEC3D, + + GF_SG_VRML_FIRST_MF = 32, + GF_SG_VRML_MFBOOL = GF_SG_VRML_FIRST_MF, + GF_SG_VRML_MFFLOAT, + GF_SG_VRML_MFTIME, + GF_SG_VRML_MFINT32, + GF_SG_VRML_MFSTRING, + GF_SG_VRML_MFVEC3F, + GF_SG_VRML_MFVEC2F, + GF_SG_VRML_MFCOLOR, + GF_SG_VRML_MFROTATION, + GF_SG_VRML_MFIMAGE, + GF_SG_VRML_MFNODE, + GF_SG_VRML_MFVEC4F, + + /*used types in GPAC but not defined in the MPEG4 spec*/ + GF_SG_VRML_MFURL, + GF_SG_VRML_MFSCRIPT, + + /*used types in X3D*/ + GF_SG_VRML_MFDOUBLE, + GF_SG_VRML_MFCOLORRGBA, + GF_SG_VRML_MFVEC2D, + GF_SG_VRML_MFVEC3D, + + GF_SG_VRML_UNKNOWN +}; +const char *gf_sg_vrml_get_field_type_by_name(u32 FieldType); + + +/* +allocates a new field and gets it back. + NOTE: + GF_SG_VRML_MFNODE will return a pointer to a GF_List structure (eg GF_List *) + GF_SG_VRML_SFNODE will return NULL +*/ +void *gf_sg_vrml_field_pointer_new(u32 FieldType); +/*deletes a field pointer (including SF an,d MF nodes)*/ +void gf_sg_vrml_field_pointer_del(void *field, u32 FieldType); + +Bool gf_sg_vrml_is_sf_field(u32 FieldType); + +/*translates MF/SF to SF type*/ +u32 gf_sg_vrml_get_sf_type(u32 FieldType); + + +/* + MFField manipulation - MFNode cannot use these, use the GF_List functions instead + or the Node_* insertion functions + FieldType shall always be given when manipulating MFFields +*/ +/*Insert (+alloc) a slot in the MFField with a specified position for insertion and sets the ptr +to the newly created slot +@InsertAt is the 0-based index for the new slot +*/ +GF_Err gf_sg_vrml_mf_insert(void *mf, u32 FieldType, void **new_ptr, u32 InsertAt); +/*adds at the end and gets the ptr*/ +GF_Err gf_sg_vrml_mf_append(void *mf, u32 FieldType, void **new_ptr); +/*remove the desired item*/ +GF_Err gf_sg_vrml_mf_remove(void *mf, u32 FieldType, u32 RemoveFrom); +/*alloc a fixed array*/ +GF_Err gf_sg_vrml_mf_alloc(void *mf, u32 FieldType, u32 NbItems); +/*get the item in the array*/ +GF_Err gf_sg_vrml_mf_get_item(void *mf, u32 FieldType, void **new_ptr, u32 ItemPos); +/*remove all items of the MFField*/ +GF_Err gf_sg_vrml_mf_reset(void *mf, u32 FieldType); + +/*clones a field content EXCEPT SF/MFNode. Pointers to field shall be used +@dest, @orig: pointers to field +@FieldType: type of the field +*/ +void gf_sg_vrml_field_copy(void *dest, void *orig, u32 FieldType); + +/*indicates whether 2 fields of same type EXCEPT SF/MFNode are equal +@dest, @orig: pointers to field +@FieldType: type of the field +*/ +Bool gf_sg_vrml_field_equal(void *dest, void *orig, u32 FieldType); + + + +/*VRML grouping nodes macro - note we have inverted the children field to be +compatible with the base GF_ParentNode node +All grouping nodes (with "children" field) implement the following: + +addChildren: chain containing nodes to add passed as eventIn - handled internally through ROUTE +void (*on_addChildren)(GF_Node *pNode): add eventIn signaler - this is handled internally by the scene_graph and SHALL +NOT BE OVERRIDEN since it takes care of node(s) routing + +removeChildren: chain containing nodes to remove passed as eventIn - handled internally through ROUTE + +void (*on_removeChildren)(GF_Node *pNode): remove eventIn signaler - this is handled internally by the scene_graph and SHALL +NOT BE OVERRIDEN since it takes care of node(s) routing + +children: list of children SFNodes +*/ + +#define VRML_CHILDREN \ + CHILDREN \ + GF_ChildNodeItem *addChildren; \ + void (*on_addChildren)(GF_Node *pNode); \ + GF_ChildNodeItem *removeChildren; \ + void (*on_removeChildren)(GF_Node *pNode); \ + +typedef struct +{ + BASE_NODE + VRML_CHILDREN +} GF_VRMLParent; + +void gf_sg_vrml_parent_setup(GF_Node *pNode); +void gf_sg_vrml_parent_destroy(GF_Node *pNode); + + +/*set proto loader - callback is the same as simulation time callback + GetExternProtoLib is a pointer to the proto lib loader - this callback shall return the LPSCENEGRAPH +of the extern proto lib if found and loaded, NULL if not found and GF_SG_INTERNAL_PROTO for internal +hardcoded protos (extensions of MPEG-4 scene graph used for module deveopment) +*/ +#define GF_SG_INTERNAL_PROTO (GF_SceneGraph *) 0xFFFFFFFF + +/*GF_Route manip: routes are used to pass events between nodes. Event handling is managed by the scene graph +however only the nodes overloading the EventIn handler associated with the event will process the eventIn*/ +typedef struct _route GF_Route; + +/*creates a new route: + @fromNode: @fromField: address of the eventOut field triggering the route + @toNode: @toField: address of the destination eventIn field +NOTE: routes are automatically destroyed if either the target or origin node of the route is destroyed +*/ +GF_Route *gf_sg_route_new(GF_SceneGraph *sg, GF_Node *fromNode, u32 fromField, GF_Node *toNode, u32 toField); + +/*delete route*/ +void gf_sg_route_del(GF_Route *route); +GF_Err gf_sg_route_del_by_id(GF_SceneGraph *sg,u32 routeID); + +/*locate route by ID/name*/ +GF_Route *gf_sg_route_find(GF_SceneGraph *sg, u32 RouteID); +GF_Route *gf_sg_route_find_by_name(GF_SceneGraph *sg, char *name); +/*assign route ID - fails if a route with same ID already exist*/ +GF_Err gf_sg_route_set_id(GF_Route *route, u32 ID); +u32 gf_sg_route_get_id(GF_Route *route); +/*assign route name if desired*/ +GF_Err gf_sg_route_set_name(GF_Route *route, char *name); +char *gf_sg_route_get_name(GF_Route *route); + +/*retuns next available RouteID - Note this doesn't track inserted routes, that's the user responsability*/ +u32 gf_sg_get_next_available_route_id(GF_SceneGraph *sg); +/*set max defined route ID used in the scene - used to handle RouteInsert commands +note that this must be called by the user to be effective,; otherwise the max route ID is computed +from the routes present in scene*/ +void gf_sg_set_max_defined_route_id(GF_SceneGraph *sg, u32 ID); + + +/*activates all routes currently triggered - this follows the event cascade model of VRML/MPEG4: + - routes are collected during eventOut generation + - routes are activated. If eventOuts are generated during activation the cycle goes on. + + A route cannot be activated twice in the same simulation tick, hence this function shall be called + ONCE AND ONLY ONCE per simulation tick + +Note that children scene graphs register their routes with the top-level graph, so only the main +scene graph needs to be activated*/ +void gf_sg_activate_routes(GF_SceneGraph *sg); + + +/* + proto handling + + The lib allows you to construct prototype nodes as defined in VRML/MPEG4 by constructing + proto interfaces and instanciating them. An instanciated proto is handled as a single node for + rendering, thus an application will never handle proto instances for rendering +*/ + +/*opaque handler for a proto object (declaration)*/ +typedef struct _proto GF_Proto; +/*opaque handler for a proto field object (declaration)*/ +typedef struct _protofield GF_ProtoFieldInterface; + + +/*retuns next available NodeID*/ +u32 gf_sg_get_next_available_proto_id(GF_SceneGraph *sg); + +/*proto constructor identified by ID/name in the given scene +2 protos in the same scene may not have the same ID/name + +@unregistered: used for memory handling of scene graph only, the proto is not stored +in the graph main proto list but in an alternate list. Several protos with the same ID/Name can be stored unregistered +*/ +GF_Proto *gf_sg_proto_new(GF_SceneGraph *inScene, u32 ProtoID, char *name, Bool unregistered); + +/*destroy proto interface - can be used even if instances of the proto are still present*/ +GF_Err gf_sg_proto_del(GF_Proto *proto); + +/*used for memory handling of scene graph only. move proto from off-graph to in-graph or reverse*/ +GF_Err gf_sg_proto_set_in_graph(GF_Proto *proto, GF_SceneGraph *inScene, Bool set_in); + +/*returns graph associated with this proto. Such a graph cannot be used for rendering but is needed during +construction of proto dictionaries in case of nested protos*/ +GF_SceneGraph *gf_sg_proto_get_graph(GF_Proto *proto); + +/*get/set private data*/ +void gf_sg_proto_set_private(GF_Proto *proto, void *ptr, void (*OnDelete)(void *ptr) ); +void *gf_sg_proto_get_private(GF_Proto *proto); + +/*add node code - a proto is build of several nodes, the first node is used for rendering +and the others are kept private. This set of nodes is refered to as the proto "node code"*/ +GF_Err gf_sg_proto_add_node_code(GF_Proto *proto, GF_Node *pNode); + +/*gets number of field in the proto interface*/ +u32 gf_sg_proto_get_field_count(GF_Proto *proto); +/*locates field declaration by name*/ +GF_ProtoFieldInterface *gf_sg_proto_field_find_by_name(GF_Proto *proto, char *fieldName); +/*locates field declaration by index (0-based)*/ +GF_ProtoFieldInterface *gf_sg_proto_field_find(GF_Proto *proto, u32 fieldIndex); + +/*creates a new field declaration in the proto. of given fieldtype and eventType +fieldName can be NULL, if so the name will be fieldN, N being the index of the created field*/ +GF_ProtoFieldInterface *gf_sg_proto_field_new(GF_Proto *proto, u32 fieldType, u32 eventType, char *fieldName); + +/*assign the node field to a field of the proto (the node field IS the proto field) +the node shall be a node of the proto scenegraph, and the fieldtype/eventType of both fields shall match +(except SF/MFString and MF/SFURL which are allowed) due to BIFS semantics*/ +GF_Err gf_sg_proto_field_set_ised(GF_Proto *proto, u32 protoFieldIndex, GF_Node *node, u32 nodeFieldIndex); +/*set/get user private data for the proto field declaration*/ +void gf_sg_proto_field_set_private(GF_ProtoFieldInterface *field, void *ptr, void (*OnDelete)(void *ptr) ); +void *gf_sg_proto_field_get_private(GF_ProtoFieldInterface *field); +/*returns field info of the field - this is typically used to setup the default value of the field*/ +GF_Err gf_sg_proto_field_get_field(GF_ProtoFieldInterface *field, GF_FieldInfo *info); + +/* + NOTE on proto instances: + The proto instance is handled as an GF_Node outside the scenegraph lib, and is manipulated with the same functions + as an GF_Node + The proto instance may or may not be loaded. + An unloaded instance only contains the proto instance fields + A loaded instance contains the proto instance fields plus all the proto code (Nodes, routes) and + will load any scripts present in it. This allows keeping the memory usage of proto very low, especially + when nested protos (protos used as building blocks of their parent proto) are used. +*/ + +/*creates the proto interface without the proto code.*/ +GF_Node *gf_sg_proto_create_instance(GF_SceneGraph *sg, GF_Proto *proto); + +/*lodes code in this instance - all subprotos are automatically created, thus you must only instanciate +top-level protos. VRML/BIFS doesn't allow for non top-level proto instanciation in the main graph +All nodes created in this proto will be forwarded to the app for initialization*/ +GF_Err gf_sg_proto_load_code(GF_Node *proto_inst); + +/*locate a prototype definition by ID or by name. when looking by name, ID is ignored*/ +GF_Proto *gf_sg_find_proto(GF_SceneGraph *sg, u32 ProtoID, char *name); + +/*deletes all protos in given scene - does NOT delete instances of protos, only the proto object is destroyed */ +GF_Err gf_sg_delete_all_protos(GF_SceneGraph *scene); + + +/*tools for hardcoded proto*/ +/*gets proto of this node - if the node is not a prototype instance, returns NULL*/ +GF_Proto *gf_node_get_proto(GF_Node *node); +/*returns the ID of the proto*/ +u32 gf_sg_proto_get_id(GF_Proto *proto); +/*returns proto name*/ +const char *gf_sg_proto_get_class_name(GF_Proto *proto); + +/*Returns 1 if the given field is ISed to a startTime/stopTime field (MPEG-4 specific for updates)*/ +Bool gf_sg_proto_field_is_sftime_offset(GF_Node *node, GF_FieldInfo *field); + +/*set an ISed field in a proto instance (not a proto) - this is needed with dynamic node creation inside a proto +instance (conditionals)*/ +GF_Err gf_sg_proto_instance_set_ised(GF_Node *protoinst, u32 protoFieldIndex, GF_Node *node, u32 nodeFieldIndex); + +/*returns root node (the one and only one being traversed) of this proto instance if any*/ +GF_Node *gf_node_get_proto_root(GF_Node *node); + +/*returns parent ProtoInstance node if this node is in a proto*/ +GF_Node *gf_node_get_proto_parent(GF_Node *node); + +/*indicates proto field has been parsed and its value is valid - this is needed for externProtos not specifying default +values*/ +void gf_sg_proto_mark_field_loaded(GF_Node *proto_inst, GF_FieldInfo *info); + +/* + JavaScript tools +*/ + +/*script fields type don't have the same value as the bifs ones...*/ +enum +{ + GF_SG_SCRIPT_TYPE_FIELD = 0, + GF_SG_SCRIPT_TYPE_EVENT_IN, + GF_SG_SCRIPT_TYPE_EVENT_OUT, +}; + +typedef struct _scriptfield GF_ScriptField; +/*creates new sript field - script fields are dynamically added to the node, and thus can be accessed through the +same functions as other GF_Node fields*/ +GF_ScriptField *gf_sg_script_field_new(GF_Node *script, u32 eventType, u32 fieldType, const char *name); +/*retrieves field info, usefull to get the field index*/ +GF_Err gf_sg_script_field_get_info(GF_ScriptField *field, GF_FieldInfo *info); + +/*activate eventIn for script node - needed for BIFS field replace*/ +void gf_sg_script_event_in(GF_Node *node, GF_FieldInfo *in_field); + + + +/*set the scene proto loader function for externProto - callback is the same as the scene callback*/ +void gf_sg_set_proto_loader(GF_SceneGraph *scene, GF_SceneGraph *(*GetExternProtoLib)(void *SceneCallback, MFURL *lib_url)); + +/*get a pointer to the MF URL field for externProto info - DO NOT TOUCH THIS FIELD*/ +MFURL *gf_sg_proto_get_extern_url(GF_Proto *proto); + +SFRotation gf_sg_sfrotation_interpolate(SFRotation kv1, SFRotation kv2, Fixed fraction); + + + + +/*adds a new node to the "children" field +position is the 0-BASED index in the list of children, -1 means end of list (append) +DOES NOT CHECK CHILD/PARENT type compatibility +*/ +GF_Err gf_node_insert_child(GF_Node *parent, GF_Node *new_child, s32 Position); +/*removes an existing node from the "children" field*/ +GF_Err gf_node_remove_child(GF_Node *parent, GF_Node *toremove_child); +/*remove and replace given child by specified node. If node is NULL, only delete target node +position is the 0-BASED index in the list of children, -1 means end of list (append) +DOES NOT CHECK CHILD/PARENT type compatibility +*/ +GF_Err gf_node_replace_child(GF_Node *node, GF_ChildNodeItem **container, s32 pos, GF_Node *newNode); + +/*signals eventOut has been set. FieldIndex/eventName identify the eventOut field. Routes are automatically triggered +when the event is signaled*/ +void gf_node_event_out(GF_Node *node, u32 FieldIndex); +void gf_node_event_out_str(GF_Node *node, const char *eventName); + + +/*exported for parsers*/ +u32 gf_node_mpeg4_type_by_class_name(const char *node_name); +u32 gf_node_x3d_type_by_class_name(const char *node_name); + +/*exported for URL handling in compositor*/ +void gf_sg_mfurl_del(MFURL url); + +#ifdef __cplusplus +} +#endif + + + +#endif /*_GF_SG_VRML_H_*/ diff --git a/include/gpac/setup.h b/include/gpac/setup.h new file mode 100644 index 0000000..cf07b73 --- /dev/null +++ b/include/gpac/setup.h @@ -0,0 +1,377 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / general OS configuration file + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_SETUP_H_ +#define _GF_SETUP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/*WIN32 and WinCE config*/ +#if defined(WIN32) || defined(_WIN32_WCE) + +#ifdef GPAC_HAVE_CONFIG_H + +#if defined(__GNUC__) +#include <gpac/internal/config.h> +#else +#include <gpac/internal/config_static.h> +#endif + +#endif + +/*common win32 parts*/ +#include <stdio.h> +#include <stdlib.h> + + +typedef unsigned __int64 u64; +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +typedef __int64 s64; +typedef int s32; +typedef short s16; +typedef char s8; + +#if defined(__GNUC__) +#define GFINLINE inline +#else +#define GFINLINE __inline +#endif + +#define GF_PATH_SEPARATOR '\\' +#define GF_MAX_PATH 1024 + +/*WINCE config*/ +#if defined(_WIN32_WCE) + +/*winCE read-only (smaller)*/ +#ifndef GPAC_READ_ONLY +#define GPAC_READ_ONLY +#endif + +/*winCE always fixed-point*/ +#ifndef GPAC_FIXED_POINT +#define GPAC_FIXED_POINT +#endif + +/*win32 assert*/ +#ifndef assert + +void CE_Assert(u32 valid, char *file, u32 line); + +#ifndef NDEBUG +#define assert( t ) CE_Assert((unsigned int) (t), __FILE__, __LINE__ ) +#else +#define assert(t) +#endif + +#endif + + +/*performs wide->char and char->wide conversion on a buffer GF_MAX_PATH long*/ +void CE_WideToChar(unsigned short *w_str, char *str); +void CE_CharToWide(char *str, unsigned short *w_str); + + +#define strdup _strdup +#define stricmp _stricmp +#define strnicmp _strnicmp +#define strupr _strupr + +#ifndef _PTRDIFF_T_DEFINED +typedef int ptrdiff_t; +#define PTRDIFF(p1, p2, type) ((p1) - (p2)) +#define _PTRDIFF_T_DEFINED +#endif + +#ifndef _SIZE_T_DEFINED +typedef unsigned int size_t; +#define _SIZE_T_DEFINED +#endif + +#ifndef offsetof +#define offsetof(s,m) ((size_t)&(((s*)0)->m)) +#endif + +#ifndef getenv +#define getenv(a) 0L +#endif + +#define strupr _strupr +#define strlwr _strlwr + +//#define GPAC_DISABLE_LOG + +#else /*END WINCE*/ + +/*WIN32 not-WinCE*/ +#include <ctype.h> +#include <string.h> +#include <float.h> +#include <limits.h> +#include <stdarg.h> +#include <assert.h> + + +#endif /*END WIN32 non win-ce*/ +/*end WIN32 config*/ + +/*start SYMBIAN config*/ +#elif defined(__SYMBIAN32__) + +#define GFINLINE inline +#define GF_PATH_SEPARATOR '\\' + +/*we must explicitely export our functions...*/ +#define GF_EXPORT EXPORT_C + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <limits.h> +#include <ctype.h> +#include <string.h> +#include <assert.h> + +#ifdef __SERIES60_3X__ + +typedef unsigned __int64 u64; +typedef __int64 s64; + +#else + +/*FIXME - we don't have 64bit support here we should get rid of all 64bits divisions*/ +//typedef unsigned long long u64; +//typedef long long s64; + +typedef unsigned int u64; +typedef signed int s64; + +#endif /*symbian 8*/ + +/*SYMBIAN always fixed-point*/ +#ifndef GPAC_FIXED_POINT +#define GPAC_FIXED_POINT +#endif + + +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +typedef int s32; +typedef short s16; +typedef signed char s8; + +#pragma mpwc_relax on + +#define GF_MAX_PATH 260 + +/*sorry this was developed under w32 :)*/ +#define stricmp strcasecmp +#define strnicmp strncasecmp + +#ifndef strupr +char * my_str_upr(char *str); +#define strupr my_str_upr +#endif + +#ifndef strlwr +char * my_str_lwr(char *str); +#define strlwr my_str_lwr +#endif + +#ifndef DBL_MAX +#include <libc/ieeefp.h> +#define DBL_MAX (__IEEE_DBL_MAXPOWTWO) +#endif + +#ifndef FLT_MAX +#include <libc/ieeefp.h> +#define FLT_MAX (__IEEE_FLT_MAXPOWTWO) +#endif + +#ifndef FLT_EPSILON +#define FLT_EPSILON 1 +#endif + +/*end SYMBIAN config*/ + +#else + +/*UNIX likes*/ + +#ifdef GPAC_HAVE_CONFIG_H +#include "config.h" +#endif + + +/*force large file support*/ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE +#endif +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <limits.h> +#include <float.h> +#include <ctype.h> +#include <string.h> +#include <assert.h> + + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +#define GFINLINE inline + +/*sorry this was developed under w32 :)*/ +#define stricmp strcasecmp +#define strnicmp strncasecmp + +#ifndef strupr +char * my_str_upr(char *str); +#define strupr my_str_upr +#endif + +#ifndef strlwr +char * my_str_lwr(char *str); +#define strlwr my_str_lwr +#endif + +#define GF_PATH_SEPARATOR '/' + +#ifdef PATH_MAX +#define GF_MAX_PATH PATH_MAX +#else +/*PATH_MAX not defined*/ +#define GF_MAX_PATH 1023 +#endif + + +#endif /* end platform specific Win32/WinCE/UNIX*/ + +/*define what's missing*/ +#ifndef NULL +#define NULL 0 +#endif + + +typedef double Double; +typedef float Float; +/* 128 bit IDs */ +typedef u8 bin128[16]; + +#define GF_MAX_DOUBLE DBL_MAX +#define GF_MIN_DOUBLE -GF_MAX_DOUBLE +#define GF_MAX_FLOAT FLT_MAX +#define GF_MIN_FLOAT -GF_MAX_FLOAT +#define GF_EPSILON_FLOAT FLT_EPSILON +#define GF_SHORT_MAX SHRT_MAX +#define GF_SHORT_MIN SHRT_MIN + +#ifndef MIN +#define MIN(X, Y) ((X)<(Y)?(X):(Y)) +#endif +#ifndef MAX +#define MAX(X, Y) ((X)>(Y)?(X):(Y)) +#endif + +#define ABSDIFF(a, b) ( ( (a) > (b) ) ? ((a) - (b)) : ((b) - (a)) ) + +#ifndef ABS +#define ABS(a) ( ( (a) > 0 ) ? (a) : - (a) ) +#endif + +#ifndef Bool +typedef u32 Bool; +#endif + +/*GPAC memory tracking*/ +#define GPAC_MEMORY_TRACKING 0 + +#if GPAC_MEMORY_TRACKING +void *gf_malloc(size_t size); +void *gf_realloc(void *ptr, size_t size); +void gf_free(void *ptr); +char *gf_strdup(const char *str); + +#undef malloc +#define malloc gf_malloc +#undef realloc +#define realloc gf_realloc +#undef free +#define free gf_free +#undef strdup +#define strdup gf_strdup + +#endif +/*end GPAC memory tracking*/ + +#if defined (WIN32) && !defined(__GNUC__) +#define LLD "%I64d" +#define LLU "%I64u" +#define LLD_CAST +#define LLU_CAST +#elif defined (__SYMBIAN32__) +#define LLD "%d" +#define LLU "%u" +#define LLD_CAST (u32) +#define LLU_CAST (s32) +#else +#define LLD "%lld" +#define LLU "%llu" +#define LLD_CAST +#define LLU_CAST +#endif + + +#ifndef GF_EXPORT +/*use def files for windows or let compiler decide*/ +#define GF_EXPORT +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_SETUP_H_*/ diff --git a/include/gpac/svg_types.h b/include/gpac/svg_types.h new file mode 100644 index 0000000..b830cd7 --- /dev/null +++ b/include/gpac/svg_types.h @@ -0,0 +1,887 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean Le Feuvre + * Copyright (c) 2004-200X ENST + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_SVG_SVG_TYPES_H_ +#define _GF_SVG_SVG_TYPES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/path2d.h> + + + +/* Attributes in SVG can be accessed using a GF_FieldInfo structure + like it is done in the BIFS part of the implementation: + + fieldIndex: attribute tag to identify the attribute in the element in the case of dynamic alloc (default) + or index of the attribute in the element in the case of static allocation of attributes + + fieldType: attribute data type as in the enumeration below + + name: attribute name (WARNING: this may be NULL) + + far_ptr: pointer to the actual data with one of the type given in this file + + NDTType: unused in SVG + eventType: unused in SVG + on_event_in: unused in SVG +*/ + +/* SVG attribute types */ +enum { + SVG_Unknown_datatype = 0, + + /* keyword enum types */ + XML_Space_datatype, + XMLEV_Propagate_datatype, + XMLEV_DefaultAction_datatype, + XMLEV_Phase_datatype, + SVG_FillRule_datatype, + SVG_StrokeLineJoin_datatype, + SVG_StrokeLineCap_datatype, + SVG_FontStyle_datatype, + SVG_FontWeight_datatype, + SVG_FontVariant_datatype, + SVG_TextAnchor_datatype, + SVG_TransformType_datatype, + SVG_Display_datatype, + SVG_Visibility_datatype, + SVG_Overflow_datatype, + SVG_ZoomAndPan_datatype, + SVG_DisplayAlign_datatype, + SVG_PointerEvents_datatype, + SVG_RenderingHint_datatype, + SVG_VectorEffect_datatype, + SVG_PlaybackOrder_datatype, + SVG_TimelineBegin_datatype, + SVG_GradientUnit_datatype, + SVG_InitialVisibility_datatype, + SVG_FocusHighlight_datatype, + SVG_Overlay_datatype, + SVG_TransformBehavior_datatype, + SVG_SpreadMethod_datatype, + SVG_TextAlign_datatype, + SVG_Focusable_datatype, + SMIL_SyncBehavior_datatype, + SMIL_SyncTolerance_datatype, + SMIL_AttributeType_datatype, + SMIL_CalcMode_datatype, + SMIL_Additive_datatype, + SMIL_Accumulate_datatype, + SMIL_Restart_datatype, + SMIL_Fill_datatype, + + SVG_LAST_U8_PROPERTY, + + DOM_String_datatype, + DOM_StringList_datatype, + + XMLEV_Event_datatype, + XMLRI_datatype, + XMLRI_List_datatype, + XML_IDREF_datatype, + + SMIL_KeyTimes_datatype, + SMIL_KeySplines_datatype, + SMIL_KeyPoints_datatype, + SMIL_Times_datatype, + + /* animated (untyped) value */ + SMIL_AnimateValue_datatype, + SMIL_AnimateValues_datatype, + SMIL_Duration_datatype, + SMIL_RepeatCount_datatype, + SMIL_AttributeName_datatype, + + /* SVG Number */ + SVG_Number_datatype, + SVG_FontSize_datatype, + SVG_Length_datatype, + SVG_Coordinate_datatype, + SVG_Rotate_datatype, + + /* List of */ + SVG_Numbers_datatype, + SVG_Points_datatype, + SVG_Coordinates_datatype, + + /*all other types*/ + SVG_Boolean_datatype, + SVG_Color_datatype, + SVG_Paint_datatype, + SVG_PathData_datatype, + SVG_FontFamily_datatype, + SVG_ID_datatype, + + SVG_StrokeDashArray_datatype, + SVG_PreserveAspectRatio_datatype, + SVG_ViewBox_datatype, + SVG_GradientOffset_datatype, + SVG_Focus_datatype, + SVG_Clock_datatype, + SVG_ContentType_datatype, + SVG_LanguageID_datatype, + + /* Matrix related types */ + SVG_Transform_datatype, + SVG_Transform_Translate_datatype, + SVG_Transform_Scale_datatype, + SVG_Transform_SkewX_datatype, + SVG_Transform_SkewY_datatype, + SVG_Transform_Rotate_datatype, + SVG_Motion_datatype, + + /*LASeR types*/ + LASeR_Choice_datatype, + LASeR_Size_datatype, +}; + +/* Definition of SVG base data types */ +typedef char *DOM_String; +typedef DOM_String SVG_String; +typedef DOM_String SVG_ContentType; +typedef DOM_String SVG_LanguageID; +typedef DOM_String SVG_TextContent; + +/* types not yet handled properly, i.e. for the moment using strings */ +typedef DOM_String SVG_ID; +typedef DOM_String SVG_LinkTarget; +typedef DOM_String SVG_GradientOffset; + +typedef Double SVG_Clock; + +typedef GF_List *ListOfXXX; +typedef GF_List *SVG_Numbers; +typedef GF_List *SVG_Coordinates; +typedef GF_List *SVG_FeatureList; +typedef GF_List *SVG_ExtensionList; +typedef GF_List *SVG_FormatList; +typedef GF_List *SVG_ListOfIRI; +typedef GF_List *SVG_LanguageIDs; +typedef GF_List *SVG_FontList; +typedef GF_List *SVG_TransformList; +typedef GF_List *SVG_Points; +typedef GF_List *SMIL_Times; +typedef GF_List *SMIL_KeyTimes; +typedef GF_List *SMIL_KeyPoints; +/* Fixed between 0 and 1 */ +typedef GF_List *SMIL_KeySplines; + +typedef GF_Matrix2D SVG_Motion; + +/* SMIL Anim types */ +typedef struct { + /*field type*/ + u32 type; + /*field pointer*/ + void *field_ptr; + /*attribute name for textual parsing*/ + char *name; + /*attribute tag for live transcoding*/ + u32 tag; +} SMIL_AttributeName; + +enum { + /*clock time*/ + GF_SMIL_TIME_CLOCK = 0, + /*wallclock time*/ + GF_SMIL_TIME_WALLCLOCK = 1, + /*resolved time of an event, discarded when restarting animation.*/ + GF_SMIL_TIME_EVENT_RESOLVED = 2, + /*event time*/ + GF_SMIL_TIME_EVENT = 3, + /*indefinite time*/ + GF_SMIL_TIME_INDEFINITE = 4, +}; + +#define GF_SMIL_TIME_IS_CLOCK(v) (v<=GF_SMIL_TIME_EVENT_RESOLVED) +#define GF_SMIL_TIME_IS_SPECIFIED_CLOCK(v) (v<GF_SMIL_TIME_EVENT_RESOLVED) + +typedef struct +{ + u32 type; + /*for accessKey and mouse button, or repeatCount when the event is a SMIL repeat */ + u32 parameter; +} XMLEV_Event; + +typedef struct { + /* Type of timing value*/ + u8 type; + /* in case of syncbase, event, repeat value: this is the pointer to the source of the event */ + GF_Node *element; + /* id of the element before resolution of the pointer to the element */ + char *element_id; + /* listener associated with event */ + GF_Node *listener; + + /* event type and parameter */ + XMLEV_Event event; + /*set if event is + begin rather than beginEvent, + end rather than endEvent, + repeat rather than repeatEvent */ + Bool is_absolute_event; + /*clock offset (absolute or relative to event)*/ + Double clock; + +} SMIL_Time; + +enum { + SMIL_DURATION_AUTO = 0, + SMIL_DURATION_INDEFINITE, + SMIL_DURATION_MEDIA, + SMIL_DURATION_NONE, + SMIL_DURATION_DEFINED, +}; +typedef struct { + u8 type; + Double clock_value; +} SMIL_Duration; + +enum { + SMIL_RESTART_ALWAYS = 0, + SMIL_RESTART_NEVER, + SMIL_RESTART_WHENNOTACTIVE, +}; +typedef u8 SMIL_Restart; + +enum { + SMIL_FILL_FREEZE=0, + SMIL_FILL_REMOVE, + +}; +typedef u8 SMIL_Fill; + +enum { + SMIL_REPEATCOUNT_INDEFINITE = 0, + SMIL_REPEATCOUNT_DEFINED = 1, + /* used only for static allocation of SVG attributes */ + SMIL_REPEATCOUNT_UNSPECIFIED = 2 +}; +typedef struct { + u8 type; + Fixed count; +} SMIL_RepeatCount; + +typedef struct { + u8 type; + void *value; +} SMIL_AnimateValue; + +typedef struct { + u8 type; + GF_List *values; +} SMIL_AnimateValues; + +enum { + SMIL_ADDITIVE_REPLACE = 0, + SMIL_ADDITIVE_SUM +}; +typedef u8 SMIL_Additive; + +enum { + SMIL_ACCUMULATE_NONE = 0, + SMIL_ACCUMULATE_SUM +}; +typedef u8 SMIL_Accumulate; + +enum { + /*WARNING: default value is linear, order changed for LASeR coding*/ + SMIL_CALCMODE_DISCRETE = 0, + SMIL_CALCMODE_LINEAR, + SMIL_CALCMODE_PACED, + SMIL_CALCMODE_SPLINE +}; +typedef u8 SMIL_CalcMode; +/* end of SMIL Anim types */ + +enum { + XMLRI_ELEMENTID = 0, + XMLRI_STRING, + XMLRI_STREAMID +}; +typedef struct __xml_ri { + u8 type; + char *string; + void *target; + u32 lsr_stream_id; +} XMLRI; + +/*the same structure is used to watch for IDREF changes (LASeR node replace)*/ +typedef struct __xml_ri XML_IDREF; + +enum +{ + SVG_FOCUS_AUTO = 0, + SVG_FOCUS_SELF, + SVG_FOCUS_IRI, +}; + +typedef struct +{ + u8 type; + XMLRI target; +} SVG_Focus; + +enum { + SVG_FONTFAMILY_INHERIT = 0, + SVG_FONTFAMILY_VALUE +}; + +typedef struct { + u8 type; + SVG_String value; +} SVG_FontFamily; + +enum { + SVG_FONTSTYLE_INHERIT = 0, + SVG_FONTSTYLE_ITALIC = 1, + SVG_FONTSTYLE_NORMAL = 2, + SVG_FONTSTYLE_OBLIQUE = 3 +}; +typedef u8 SVG_FontStyle; + +/*the values are chosen to match LASeR code points*/ +enum { + SVG_PATHCOMMAND_M = 3, + SVG_PATHCOMMAND_L = 2, + SVG_PATHCOMMAND_C = 0, + SVG_PATHCOMMAND_S = 5, + SVG_PATHCOMMAND_Q = 4, + SVG_PATHCOMMAND_T = 6, + SVG_PATHCOMMAND_A = 20, + SVG_PATHCOMMAND_Z = 8 +}; + +#define USE_GF_PATH 1 + +#if USE_GF_PATH +typedef GF_Path SVG_PathData; +#else +typedef struct { + GF_List *commands; + GF_List *points; +} SVG_PathData; +#endif + +typedef struct { + Fixed x, y; +} SVG_Point; + +typedef struct { + Fixed x, y, angle; +} SVG_Point_Angle; + +typedef struct { + Bool is_set; + Fixed x, y, width, height; +} SVG_ViewBox; + +typedef Bool SVG_Boolean; + +/*WARNING - THESE ARE PATH FLAGS, CHECK IF WORKING*/ +enum { + SVG_FILLRULE_EVENODD= 0, + SVG_FILLRULE_NONZERO, + SVG_FILLRULE_INHERIT +}; +typedef u8 SVG_FillRule; + +enum { + SVG_STROKELINEJOIN_MITER = GF_LINE_JOIN_MITER_SVG, + SVG_STROKELINEJOIN_ROUND = GF_LINE_JOIN_ROUND, + SVG_STROKELINEJOIN_BEVEL = GF_LINE_JOIN_BEVEL, + SVG_STROKELINEJOIN_INHERIT = 100 +}; +typedef u8 SVG_StrokeLineJoin; + +/* Warning: GPAC naming is not the same as SVG naming for line cap Flat = butt and Butt = square*/ +enum { + SVG_STROKELINECAP_BUTT = GF_LINE_CAP_FLAT, + SVG_STROKELINECAP_ROUND = GF_LINE_CAP_ROUND, + SVG_STROKELINECAP_SQUARE = GF_LINE_CAP_SQUARE, + SVG_STROKELINECAP_INHERIT = 100 +}; +typedef u8 SVG_StrokeLineCap; + +enum { + SVG_OVERFLOW_INHERIT = 0, + SVG_OVERFLOW_VISIBLE = 1, + SVG_OVERFLOW_HIDDEN = 2, + SVG_OVERFLOW_SCROLL = 3, + SVG_OVERFLOW_AUTO = 4 +}; +typedef u8 SVG_Overflow; + +enum { + SVG_COLOR_RGBCOLOR = 0, + SVG_COLOR_INHERIT, + SVG_COLOR_CURRENTCOLOR, + SVG_COLOR_ACTIVE_BORDER, /*Active window border*/ + SVG_COLOR_ACTIVE_CAPTION, /*Active window caption. */ + SVG_COLOR_APP_WORKSPACE, /*Background color of multiple document interface. */ + SVG_COLOR_BACKGROUND, /*Desktop background. */ + SVG_COLOR_BUTTON_FACE, /* Face color for three-dimensional display elements. */ + SVG_COLOR_BUTTON_HIGHLIGHT, /* Dark shadow for three-dimensional display elements (for edges facing away from the light source). */ + SVG_COLOR_BUTTON_SHADOW, /* Shadow color for three-dimensional display elements. */ + SVG_COLOR_BUTTON_TEXT, /*Text on push buttons. */ + SVG_COLOR_CAPTION_TEXT, /* Text in caption, size box, and scrollbar arrow box. */ + SVG_COLOR_GRAY_TEXT, /* Disabled ('grayed') text. */ + SVG_COLOR_HIGHLIGHT, /* Item(s) selected in a control. */ + SVG_COLOR_HIGHLIGHT_TEXT, /*Text of item(s) selected in a control. */ + SVG_COLOR_INACTIVE_BORDER, /* Inactive window border. */ + SVG_COLOR_INACTIVE_CAPTION, /* Inactive window caption. */ + SVG_COLOR_INACTIVE_CAPTION_TEXT, /*Color of text in an inactive caption. */ + SVG_COLOR_INFO_BACKGROUND, /* Background color for tooltip controls. */ + SVG_COLOR_INFO_TEXT, /*Text color for tooltip controls. */ + SVG_COLOR_MENU, /*Menu background. */ + SVG_COLOR_MENU_TEXT, /* Text in menus. */ + SVG_COLOR_SCROLLBAR, /* Scroll bar gray area. */ + SVG_COLOR_3D_DARK_SHADOW, /* Dark shadow for three-dimensional display elements. */ + SVG_COLOR_3D_FACE, /* Face color for three-dimensional display elements. */ + SVG_COLOR_3D_HIGHLIGHT, /* Highlight color for three-dimensional display elements. */ + SVG_COLOR_3D_LIGHT_SHADOW, /* Light color for three-dimensional display elements (for edges facing the light source). */ + SVG_COLOR_3D_SHADOW, /* Dark shadow for three-dimensional display elements. */ + SVG_COLOR_WINDOW, /* Window background. */ + SVG_COLOR_WINDOW_FRAME, /* Window frame. */ + SVG_COLOR_WINDOW_TEXT /* Text in windows.*/ +}; + +typedef struct { + u8 type; + Fixed red, green, blue; +} SVG_Color; + +enum { + SVG_PAINT_NONE = 0, + SVG_PAINT_COLOR = 1, + SVG_PAINT_URI = 2, + SVG_PAINT_INHERIT = 3 +}; + +typedef struct { + u8 type; + SVG_Color color; + XMLRI iri; +} SVG_Paint, SVG_SVGColor; + +enum { + SVG_NUMBER_VALUE = 0, + SVG_NUMBER_PERCENTAGE = 1, + SVG_NUMBER_EMS = 2, + SVG_NUMBER_EXS = 3, + SVG_NUMBER_PX = 4, + SVG_NUMBER_CM = 5, + SVG_NUMBER_MM = 6, + SVG_NUMBER_IN = 7, + SVG_NUMBER_PT = 8, + SVG_NUMBER_PC = 9, + SVG_NUMBER_INHERIT = 10, + SVG_NUMBER_AUTO = 11, + SVG_NUMBER_AUTO_REVERSE = 12 +}; + +typedef struct { + u8 type; + Fixed value; +} SVG_Number, + SVG_FontSize, + SVG_Length, + SVG_Coordinate, + SVG_Rotate; + +typedef struct { + u8 is_ref; + GF_Matrix2D mat; +} SVG_Transform; + +enum { + SVG_TRANSFORM_MATRIX = 0, + SVG_TRANSFORM_TRANSLATE = 1, + SVG_TRANSFORM_SCALE = 2, + SVG_TRANSFORM_ROTATE = 3, + SVG_TRANSFORM_SKEWX = 4, + SVG_TRANSFORM_SKEWY = 5 +}; + +typedef u8 SVG_TransformType; + +enum { + SVG_FONTWEIGHT_100 = 0, + SVG_FONTWEIGHT_200, + SVG_FONTWEIGHT_300, + SVG_FONTWEIGHT_400, + SVG_FONTWEIGHT_500, + SVG_FONTWEIGHT_600, + SVG_FONTWEIGHT_700, + SVG_FONTWEIGHT_800, + SVG_FONTWEIGHT_900, + SVG_FONTWEIGHT_BOLD, + SVG_FONTWEIGHT_BOLDER, + SVG_FONTWEIGHT_INHERIT, + SVG_FONTWEIGHT_LIGHTER, + SVG_FONTWEIGHT_NORMAL +}; +typedef u8 SVG_FontWeight; + +enum { + SVG_FONTVARIANT_INHERIT = 0, + SVG_FONTVARIANT_NORMAL = 1, + SVG_FONTVARIANT_SMALLCAPS = 2 +}; +typedef u8 SVG_FontVariant; + +enum { + SVG_VISIBILITY_HIDDEN = 0, + SVG_VISIBILITY_INHERIT = 1, + SVG_VISIBILITY_VISIBLE = 2, + SVG_VISIBILITY_COLLAPSE = 3 +}; +typedef u8 SVG_Visibility; + +enum { + SVG_DISPLAY_INHERIT = 0, + SVG_DISPLAY_NONE = 1, + SVG_DISPLAY_INLINE = 2, + SVG_DISPLAY_BLOCK, + SVG_DISPLAY_LIST_ITEM, + SVG_DISPLAY_RUN_IN, + SVG_DISPLAY_COMPACT, + SVG_DISPLAY_MARKER, + SVG_DISPLAY_TABLE, + SVG_DISPLAY_INLINE_TABLE, + SVG_DISPLAY_TABLE_ROW_GROUP, + SVG_DISPLAY_TABLE_HEADER_GROUP, + SVG_DISPLAY_TABLE_FOOTER_GROUP, + SVG_DISPLAY_TABLE_ROW, + SVG_DISPLAY_TABLE_COLUMN_GROUP, + SVG_DISPLAY_TABLE_COLUMN, + SVG_DISPLAY_TABLE_CELL, + SVG_DISPLAY_TABLE_CAPTION +}; +typedef u8 SVG_Display; + +enum { + SVG_DISPLAYALIGN_INHERIT = 0, + SVG_DISPLAYALIGN_AUTO = 1, + SVG_DISPLAYALIGN_AFTER = 2, + SVG_DISPLAYALIGN_BEFORE = 3, + SVG_DISPLAYALIGN_CENTER = 4 +}; +typedef u8 SVG_DisplayAlign; + +enum { + SVG_TEXTALIGN_INHERIT = 0, + SVG_TEXTALIGN_START = 1, + SVG_TEXTALIGN_CENTER = 2, + SVG_TEXTALIGN_END = 3 +}; +typedef u8 SVG_TextAlign; + +enum { + SVG_STROKEDASHARRAY_NONE = 0, + SVG_STROKEDASHARRAY_INHERIT = 1, + SVG_STROKEDASHARRAY_ARRAY = 2 +}; + +typedef struct { + u32 count; + Fixed* vals; +} Array; + +typedef struct { + u8 type; + Array array; +} SVG_StrokeDashArray; + +enum { + SVG_TEXTANCHOR_INHERIT = 0, + SVG_TEXTANCHOR_END = 1, + SVG_TEXTANCHOR_MIDDLE = 2, + SVG_TEXTANCHOR_START = 3 +}; +typedef u8 SVG_TextAnchor; + +enum { + SVG_ANGLETYPE_UNKNOWN = 0, + SVG_ANGLETYPE_UNSPECIFIED = 1, + SVG_ANGLETYPE_DEG = 2, + SVG_ANGLETYPE_RAD = 3, + SVG_ANGLETYPE_GRAD = 4 +}; + +enum { + SVG_UNIT_TYPE_UNKNOWN = 0, + SVG_UNIT_TYPE_USERSPACEONUSE = 1, + SVG_UNIT_TYPE_OBJECTBOUNDINGBOX = 2 +}; + +enum { + // Alignment Types + SVG_PRESERVEASPECTRATIO_NONE = 1, + SVG_PRESERVEASPECTRATIO_XMINYMIN = 2, + SVG_PRESERVEASPECTRATIO_XMIDYMIN = 3, + SVG_PRESERVEASPECTRATIO_XMAXYMIN = 4, + SVG_PRESERVEASPECTRATIO_XMINYMID = 5, + SVG_PRESERVEASPECTRATIO_XMIDYMID = 0, //default + SVG_PRESERVEASPECTRATIO_XMAXYMID = 6, + SVG_PRESERVEASPECTRATIO_XMINYMAX = 7, + SVG_PRESERVEASPECTRATIO_XMIDYMAX = 8, + SVG_PRESERVEASPECTRATIO_XMAXYMAX = 9 +}; + +enum { + // Meet_or_slice Types + SVG_MEETORSLICE_MEET = 0, + SVG_MEETORSLICE_SLICE = 1 +}; + +typedef struct { + Bool defer; + u8 align; + u8 meetOrSlice; +} SVG_PreserveAspectRatio; + +enum { + SVG_ZOOMANDPAN_DISABLE = 0, + SVG_ZOOMANDPAN_MAGNIFY, +}; + +typedef u8 SVG_ZoomAndPan; + +enum { + LENGTHADJUST_UNKNOWN = 0, + LENGTHADJUST_SPACING = 1, + LENGTHADJUST_SPACINGANDGLYPHS = 2 +}; + +enum { + // textPath Method Types + TEXTPATH_METHODTYPE_UNKNOWN = 0, + TEXTPATH_METHODTYPE_ALIGN = 1, + TEXTPATH_METHODTYPE_STRETCH = 2 +}; +enum { + // textPath Spacing Types + TEXTPATH_SPACINGTYPE_UNKNOWN = 0, + TEXTPATH_SPACINGTYPE_AUTO = 1, + TEXTPATH_SPACINGTYPE_EXACT = 2 +}; + +enum { + // Marker Unit Types + SVG_MARKERUNITS_UNKNOWN = 0, + SVG_MARKERUNITS_USERSPACEONUSE = 1, + SVG_MARKERUNITS_STROKEWIDTH = 2 +}; +enum { + // Marker Orientation Types + SVG_MARKER_ORIENT_UNKNOWN = 0, + SVG_MARKER_ORIENT_AUTO = 1, + SVG_MARKER_ORIENT_ANGLE = 2 +}; + +enum { + // Spread Method Types + SVG_SPREADMETHOD_UNKNOWN = 0, + SVG_SPREADMETHOD_PAD = 1, + SVG_SPREADMETHOD_REFLECT = 2, + SVG_SPREADMETHOD_REPEAT = 3 +}; + +enum { + SVG_POINTEREVENTS_INHERIT = 0, + SVG_POINTEREVENTS_ALL = 1, + SVG_POINTEREVENTS_FILL = 2, + SVG_POINTEREVENTS_NONE = 3, + SVG_POINTEREVENTS_PAINTED = 4, + SVG_POINTEREVENTS_STROKE = 5, + SVG_POINTEREVENTS_VISIBLE = 6, + SVG_POINTEREVENTS_VISIBLEFILL = 7, + SVG_POINTEREVENTS_VISIBLEPAINTED = 8, + SVG_POINTEREVENTS_VISIBLESTROKE = 9, + SVG_POINTEREVENTS_BOUNDINGBOX = 10 +}; +typedef u8 SVG_PointerEvents; + +enum { + SVG_RENDERINGHINT_INHERIT = 0, + SVG_RENDERINGHINT_AUTO = 1, + SVG_RENDERINGHINT_OPTIMIZEQUALITY = 2, + SVG_RENDERINGHINT_OPTIMIZESPEED = 3, + SVG_RENDERINGHINT_OPTIMIZELEGIBILITY = 4, + SVG_RENDERINGHINT_CRISPEDGES = 5, + SVG_RENDERINGHINT_GEOMETRICPRECISION = 6, + +}; +typedef u8 SVG_RenderingHint; + +enum { + SVG_VECTOREFFECT_INHERIT = 0, + SVG_VECTOREFFECT_NONE = 1, + SVG_VECTOREFFECT_NONSCALINGSTROKE = 2, +}; +typedef u8 SVG_VectorEffect; + +enum { + XMLEVENT_PROPAGATE_CONTINUE = 0, + XMLEVENT_PROPAGATE_STOP = 1 +}; +typedef u8 XMLEV_Propagate; + +enum { + XMLEVENT_DEFAULTACTION_CANCEL = 0, + XMLEVENT_DEFAULTACTION_PERFORM, + +}; +typedef u8 XMLEV_DefaultAction; + +enum { + XMLEVENT_PHASE_DEFAULT = 0, + XMLEVENT_PHASE_CAPTURE = 1 +}; +typedef u8 XMLEV_Phase; + +enum { + SMIL_SYNCBEHAVIOR_INHERIT = 0, + /*LASeR order*/ + SMIL_SYNCBEHAVIOR_CANSLIP, + SMIL_SYNCBEHAVIOR_DEFAULT, + SMIL_SYNCBEHAVIOR_INDEPENDENT, + SMIL_SYNCBEHAVIOR_LOCKED, +}; +typedef u8 SMIL_SyncBehavior; + +enum { + SMIL_SYNCTOLERANCE_INHERIT = 0, + SMIL_SYNCTOLERANCE_DEFAULT = 1, + SMIL_SYNCTOLERANCE_VALUE = 2 +}; + +typedef struct { + u8 type; + SVG_Clock value; +} SMIL_SyncTolerance; + +enum { + SMIL_ATTRIBUTETYPE_CSS = 0, + SMIL_ATTRIBUTETYPE_XML, + SMIL_ATTRIBUTETYPE_AUTO, +}; +typedef u8 SMIL_AttributeType; + +enum { + SVG_PLAYBACKORDER_ALL = 0, + SVG_PLAYBACKORDER_FORWARDONLY = 1, +}; +typedef u8 SVG_PlaybackOrder; + +enum { + SVG_TIMELINEBEGIN_ONLOAD=0, + SVG_TIMELINEBEGIN_ONSTART, +}; +typedef u8 SVG_TimelineBegin; + +enum { + XML_SPACE_DEFAULT = 0, + XML_SPACE_PRESERVE = 1 +}; +typedef u8 XML_Space; + + +enum { + SVG_GRADIENTUNITS_OBJECT = 0, + SVG_GRADIENTUNITS_USER = 1 +}; +typedef u8 SVG_GradientUnit; + +enum { + SVG_FOCUSHIGHLIGHT_AUTO = 0, + SVG_FOCUSHIGHLIGHT_NONE = 1 +}; +typedef u8 SVG_FocusHighlight; + +enum { + SVG_INITIALVISIBILTY_WHENSTARTED = 0, + SVG_INITIALVISIBILTY_ALWAYS = 1 +}; +typedef u8 SVG_InitialVisibility; + +enum { + SVG_TRANSFORMBEHAVIOR_GEOMETRIC = 0, + SVG_TRANSFORMBEHAVIOR_PINNED, + SVG_TRANSFORMBEHAVIOR_PINNED180, + SVG_TRANSFORMBEHAVIOR_PINNED270, + SVG_TRANSFORMBEHAVIOR_PINNED90, +}; +typedef u8 SVG_TransformBehavior; + +enum { + SVG_OVERLAY_NONE = 0, + SVG_OVERLAY_TOP, +}; +typedef u8 SVG_Overlay; + +enum { + SVG_FOCUSABLE_AUTO = 0, + SVG_FOCUSABLE_TRUE, + SVG_FOCUSABLE_FALSE, +}; +typedef u8 SVG_Focusable; + + +enum { + SVG_SPREAD_PAD = 0, + SVG_SPREAD_REFLECT, + SVG_SPREAD_REPEAT, +}; +typedef u8 SVG_SpreadMethod; + +enum { + LASeR_CHOICE_ALL = 0, + LASeR_CHOICE_NONE = 1, + LASeR_CHOICE_N = 2 +}; +typedef u8 LASeR_Choice_enum; + +typedef struct { + u32 type; + u32 choice_index; +} LASeR_Choice; + +typedef struct { + Fixed width, height; +} LASeR_Size; + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_SVG_SVG_TYPES_H_*/ + + diff --git a/include/gpac/sync_layer.h b/include/gpac/sync_layer.h new file mode 100644 index 0000000..13ed64f --- /dev/null +++ b/include/gpac/sync_layer.h @@ -0,0 +1,125 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / SL header file + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_SYNC_LAYER_H_ +#define _GF_SYNC_LAYER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/*the Sync Layer config descriptor*/ +typedef struct +{ + /*base descriptor*/ + u8 tag; + + u8 predefined; + u8 useAccessUnitStartFlag; + u8 useAccessUnitEndFlag; + u8 useRandomAccessPointFlag; + u8 hasRandomAccessUnitsOnlyFlag; + u8 usePaddingFlag; + u8 useTimestampsFlag; + u8 useIdleFlag; + u8 durationFlag; + u32 timestampResolution; + u32 OCRResolution; + u8 timestampLength; + u8 OCRLength; + u8 AULength; + u8 instantBitrateLength; + u8 degradationPriorityLength; + u8 AUSeqNumLength; + u8 packetSeqNumLength; + u32 timeScale; + u16 AUDuration; + u16 CUDuration; + u64 startDTS; + u64 startCTS; +} GF_SLConfig; + +/*************************************** + SLConfig Tag +***************************************/ +enum +{ + SLPredef_Null = 0x01, + SLPredef_MP4 = 0x02, + /*intern to GPAC, means NO SL at all (for streams unable to handle AU reconstruction a timing)*/ + SLPredef_SkipSL = 0xF0 +}; + +/*set SL predefined (assign all fields according to sl->predefined value)*/ +GF_Err gf_odf_slc_set_pref(GF_SLConfig *sl); + + +typedef struct +{ + u8 accessUnitStartFlag; + u8 accessUnitEndFlag; + u8 paddingFlag; + u8 randomAccessPointFlag; + u8 OCRflag; + u8 idleFlag; + u8 decodingTimeStampFlag; + u8 compositionTimeStampFlag; + u8 instantBitrateFlag; + u8 degradationPriorityFlag; + + u8 paddingBits; + u16 packetSequenceNumber; + u64 objectClockReference; + u16 AU_sequenceNumber; + u64 decodingTimeStamp; + u64 compositionTimeStamp; + u16 accessUnitLength; + u32 instantBitrate; + u16 degradationPriority; + + /*this is NOT part of standard SL, only used internally: signals duration of access unit if known + this is usefull for streams with very random updates, to prevent buffering for instance a subtitle stream + which is likely to have no updates during the first minutes... expressed in media timescale*/ + u32 au_duration; + /*ISMACryp extensions*/ + u8 isma_encrypted; + u64 isma_BSO; +} GF_SLHeader; + + +/*packetize SL-PDU. If PDU is NULL or size 0, only writes the SL header*/ +void gf_sl_packetize(GF_SLConfig* slConfig, GF_SLHeader *Header, char *PDU, u32 size, char **outPacket, u32 *OutSize); +/*gets SL header size in bytes*/ +u32 gf_sl_get_header_size(GF_SLConfig* slConfig, GF_SLHeader *Header); + +/*depacketize SL-PDU*/ +void gf_sl_depacketize(GF_SLConfig *slConfig, GF_SLHeader *Header, char *PDU, u32 PDULength, u32 *HeaderLen); + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_SYNC_LAYER_H_*/ diff --git a/include/gpac/term_info.h b/include/gpac/term_info.h new file mode 100644 index 0000000..278a331 --- /dev/null +++ b/include/gpac/term_info.h @@ -0,0 +1,158 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Stream Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#ifndef _GF_TERM_INFO_H_ +#define _GF_TERM_INFO_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + OD Browsing API - YOU MUST INCLUDE <gpac/terminal.h> before + (this has been separated from terminal.h to limit dependency of core to mpeg4_odf.h header) + ALL ITEMS ARE READ-ONLY AND SHALL NOT BE MODIFIED +*/ +#include <gpac/mpeg4_odf.h> + +/*returns top-level OD of the presentation*/ +GF_ObjectManager *gf_term_get_root_object(GF_Terminal *term); +/*returns number of sub-ODs in the current root. scene_od must be an inline OD*/ +u32 gf_term_get_object_count(GF_Terminal *term, GF_ObjectManager *scene_od); +/*returns indexed (0-based) OD manager in the scene*/ +GF_ObjectManager *gf_term_get_object(GF_Terminal *term, GF_ObjectManager *scene_od, u32 index); +/*return values: + 0: regular media object, not inline + 1: root scene + 2: inline scene + 3: externProto library +*/ +u32 gf_term_object_subscene_type(GF_Terminal *term, GF_ObjectManager *odm); + +/*select given object when stream selection is available*/ +void gf_term_select_object(GF_Terminal *term, GF_ObjectManager *odm); + +typedef struct +{ + GF_ObjectDescriptor *od; + Double duration; + Double current_time; + /*0: stoped, 1: playing, 2: paused, 3: not setup, 4; setup failed.*/ + u32 status; + /*if set, the PL flags are valid*/ + Bool has_profiles; + Bool inline_pl; + u8 OD_pl; + u8 scene_pl; + u8 audio_pl; + u8 visual_pl; + u8 graphics_pl; + + /*name of module handling the service service */ + const char *service_handler; + /*name of service*/ + const char *service_url; + /*set if the service is owned by this object*/ + Bool owns_service; + + /*stream buffer: + -2: stream is not playing + -1: stream has no buffering + >=0: amount of media data present in buffer, in ms + */ + s32 buffer; + /*number of AUs in DB (cumulated on all input channels)*/ + u32 db_unit_count; + /*number of CUs in composition memory (if any) and CM capacity*/ + u16 cb_unit_count, cb_max_count; + /*clock drift in ms of object clock: this is the delay set by the audio renderer to keep AV in sync*/ + s32 clock_drift; + /*codec name*/ + const char *codec_name; + /*object type - match streamType (cf constants.h)*/ + u32 od_type; + /*audio properties*/ + u32 sample_rate, bits_per_sample, num_channels; + /*video properties (w & h also used for scene codecs)*/ + u32 width, height, pixelFormat, par; + + /*average birate over last second and max bitrate over one second at decoder input - expressed in bits per sec*/ + u32 avg_bitrate, max_bitrate; + u32 total_dec_time, max_dec_time, nb_dec_frames, nb_droped; + + /*set if ISMACryp present on the object - will need refinement for IPMPX... + 0: not protected - 1: protected and OK - 2: protected and DRM failed*/ + u32 protection; + + u32 lang; +} ODInfo; + +/*fills the ODInfo structure describing the OD manager*/ +GF_Err gf_term_get_object_info(GF_Terminal *term, GF_ObjectManager *odm, ODInfo *info); +/*gets current downloads info for the service - only use if ODM owns thesrevice, returns 0 otherwise. + @d_enum: in/out current enum - shall start to 0, incremented at each call. fct returns 0 if no more + downloads + @server: server name + @path: file/data location on server + @bytes_done, @total_bytes: file info. total_bytes may be 0 (eg http streaming) + @bytes_per_sec: guess what +*/ +Bool gf_term_get_download_info(GF_Terminal *term, GF_ObjectManager *odm, u32 *d_enum, const char **server, const char **path, u32 *bytes_done, u32 *total_bytes, u32 *bytes_per_sec); + +/*same principles as above , struct __netcom is defined in service.h*/ +typedef struct __netstatcom NetStatCommand; +Bool gf_term_get_channel_net_info(GF_Terminal *term, GF_ObjectManager *odm, u32 *d_enum, u32 *chid, NetStatCommand *netcom, GF_Err *ret_code); + +/*same principles as above , struct __netinfo is defined in service.h*/ +typedef struct __netinfocom NetInfoCommand; +GF_Err gf_term_get_service_info(GF_Terminal *term, GF_ObjectManager *odm, NetInfoCommand *netcom); + +/*retrieves world info of the scene @od belongs to. +If @odm is or points to an inlined OD the world info of the inlined content is retrieved +If @odm is NULL the world info of the main scene is retrieved +returns NULL if no WorldInfo available +returns world title if available +@descriptions: any textual descriptions is stored here + strings are not allocated +*/ +const char *gf_term_get_world_info(GF_Terminal *term, GF_ObjectManager *scene_od, GF_List *descriptions); + +/*dumps scene graph in specified file, in BT or XMT format +@rad_name: file radical (NULL for stdout) - if not NULL MUST BE GF_MAX_PATH length +if @skip_proto is set proto declarations are not dumped +If @odm is or points to an inlined OD the inlined scene is dumped +If @odm is NULL the main scene is dumped +*/ +GF_Err gf_term_dump_scene(GF_Terminal *term, char *rad_name, Bool xml_dump, Bool skip_proto, GF_ObjectManager *odm); + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_TERM_INFO_H_*/ diff --git a/include/gpac/terminal.h b/include/gpac/terminal.h new file mode 100644 index 0000000..8a673cc --- /dev/null +++ b/include/gpac/terminal.h @@ -0,0 +1,180 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Stream Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_TERMINAL_H_ +#define _GF_TERMINAL_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/user.h> + +/*creates a new terminal for a userApp callback*/ +GF_Terminal *gf_term_new(GF_User *user); + +/*delete the app - stop is done automatically, you don't have to do it before deleting the app +returns GF_IO_ERR if client couldn't be shutdown normally*/ +GF_Err gf_term_del(GF_Terminal *term); + +/*connects to a URL*/ +void gf_term_connect(GF_Terminal *term, const char *URL); +/*disconnects the url*/ +void gf_term_disconnect(GF_Terminal *term); +/*navigates to a given destination or shutdown/restart the current one if any. +This is the only safe way of restarting/jumping a presentation from inside the EventProc +where doing a disconnect/connect could deadlock if toURL is NULL, uses the current URL*/ +void gf_term_navigate_to(GF_Terminal *term, const char *toURL); +/*restarts url from given time (in ms). Return value: + 0: service is not connected yet + 1: service has no seeking capabilities + 2: service has been seeked +*/ +u32 gf_term_play_from_time(GF_Terminal *term, u64 from_time, Bool pause_at_first_frame); +/*connect URL and seek right away - only needed when reloading the complete player (avoids waiting +for connection and post a seek..)*/ +void gf_term_connect_from_time(GF_Terminal *term, const char *URL, u64 time_in_ms, Bool pause_at_first_frame); + +/*returns current framerate + if @absoluteFPS is set, the return value is the absolute framerate, eg NbFrameCount/NbTimeSpent regardless of +whether a frame has been drawn or not, which means the FPS returned can be much greater than the target rendering +framerate + if @absoluteFPS is not set, the return value is the FPS taking into account not drawn frames (eg, less than + or equal to compositor FPS) +*/ +Double gf_term_get_framerate(GF_Terminal *term, Bool absoluteFPS); +/*get main scene current time in milliseconds*/ +u32 gf_term_get_time_in_ms(GF_Terminal *term); + +/*returns current URL address*/ +const char *gf_term_get_url(GF_Terminal *term); + +/*get viewpoints/viewports for main scene - idx is 1-based, and if greater than number of viewpoints return GF_EOS*/ +GF_Err gf_term_get_viewpoint(GF_Terminal *term, u32 viewpoint_idx, const char **outName, Bool *is_bound); +/*set active viewpoints/viewports for main scene given its name - idx is 1-based, or 0 to set by viewpoint name +if only one viewpoint is present in the scene, this will bind/unbind it*/ +GF_Err gf_term_set_viewpoint(GF_Terminal *term, u32 viewpoint_idx, const char *viewpoint_name); + +/*adds an object to the scene - only possible if scene has selectable streams (cf GF_OPT_CAN_SELECT_STREAMS option)*/ +GF_Err gf_term_add_object(GF_Terminal *term, const char *url, Bool auto_play); + + +/*set/set option - most of the terminal cfg is done through options, please refer to user.h for details*/ +GF_Err gf_term_set_option(GF_Terminal *term, u32 opt_type, u32 opt_value); +u32 gf_term_get_option(GF_Terminal *term, u32 opt_type); + +/*checks if given URL is understood by client. +if use_parent_url is set, relative URLs are solved against the current presentation URL*/ +Bool gf_term_is_supported_url(GF_Terminal *term, const char *fileName, Bool use_parent_url, Bool no_mime_check); + +/*sets simulation frame rate*/ +GF_Err gf_term_set_simulation_frame_rate(GF_Terminal * term, Double frame_rate); +/*gets simulation frame rate*/ +Double gf_term_get_simulation_frame_rate(GF_Terminal *term); + +/*sends a set of scene commands (BT, XMT, X3D, LASeR+XML) to the scene +type indicates the language used - accepted values are + "model/x3d+xml" or "x3d": commands is an X3D+XML scene + "model/x3d+vrml" or "xrdv": commands is an X3D+VRML scene + "model/vrml" or "vrml": commands is an VRML scene + "application/x-xmt" or "xmt": commands is an XMT-A scene or a list of XMT-A updates + "application/x-bt" or "bt": commands is a BT scene or a list of BT updates + "image/svg+xml" or "svg": commands is an SVG scene + "application/x-laser+xml" or "laser": commands is an SVG/LASeR+XML scene or a list of LASeR+XML updates + if not specified, the type will be guessed from the current root node if any +*/ +GF_Err gf_term_scene_update(GF_Terminal *term, char *type, char *com); + + +/*request visual output size change: + * NOT NEEDED WHEN THE TERMINAL IS HANDLING THE DISPLAY WINDOW (cf user.h) + * if the user app manages the output window it shall resize it before calling this +*/ +GF_Err gf_term_set_size(GF_Terminal *term, u32 NewWidth, u32 NewHeight); + +/*returns current text selection if any, or NULL otherwise. If probe mode is set, returns a non-NULL string ("") +if some text is selected*/ +const char *gf_term_get_text_selection(GF_Terminal *term, Bool probe_only); +/*pastes text into current selection if any. If probe mode is set, only check if text is currently edited +if some text is selected*/ +GF_Err gf_term_paste_text(GF_Terminal *term, const char *txt, Bool probe_only); + + +/*decodes pending media and render frame. +NOTE: This can only be used when the terminal runs without visual thread (GF_TERM_NO_VISUAL_THREAD flag set) +*/ +GF_Err gf_term_process_step(GF_Terminal *term); + +/*decodes all pending media and render frame until no scene changes are detected. +NOTE: This can only be used when the terminal runs without visual thread (GF_TERM_NO_VISUAL_THREAD flag set) +*/ +GF_Err gf_term_process_flush(GF_Terminal *term); + +/*post user interaction to terminal*/ +/*NOT NEEDED WHEN THE TERMINAL IS HANDLING THE DISPLAY WINDOW (cf user.h)*/ +Bool gf_term_user_event(GF_Terminal *term, GF_Event *event); + +/*post extended user mouse interaction to terminal + X and Y are point coordinates in the display expressed in 2D coord system top-left (0,0), Y increasing towards bottom + @xxx_but_down: specifiy whether the mouse button is down(2) or up (1), 0 if unchanged + @wheel: specifiy current wheel inc (0: unchanged , +1 for one wheel delta forward, -1 for one wheel delta backward) +*/ +/*NOT NEEDED WHEN THE TERMINAL IS HANDLING THE DISPLAY WINDOW (cf user.h)*/ +void gf_term_mouse_input(GF_Terminal *term, GF_EventMouse *event); + +/*post extended user key interaction to terminal + @key_code: GPAC DOM code of input key + @hw_code: hardware code of input key + @isKeyUp: set if key is released +*/ +/*NOT NEEDED WHEN THE TERMINAL IS HANDLING THE DISPLAY WINDOW (cf user.h)*/ +void gf_term_keyboard_input(GF_Terminal *term, u32 key_code, u32 hw_code, Bool isKeyUp); + +/*post extended user character interaction to terminal + @character: unicode character input +*/ +/*NOT NEEDED WHEN THE TERMINAL IS HANDLING THE DISPLAY WINDOW (cf user.h)*/ +void gf_term_string_input(GF_Terminal *term, u32 character); + + + +/*framebuffer access*/ +#include <gpac/color.h> + +/*gets screen buffer - this locks the scene graph too until released is called*/ +GF_Err gf_term_get_screen_buffer(GF_Terminal *term, GF_VideoSurface *framebuffer); +/*releases screen buffer and unlocks graph*/ +GF_Err gf_term_release_screen_buffer(GF_Terminal *term, GF_VideoSurface *framebuffer); + + +/*ObjectManager used by both terminal and object browser (term_info.h)*/ +typedef struct _od_manager GF_ObjectManager; + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_TERMINAL_H_*/ diff --git a/include/gpac/thread.h b/include/gpac/thread.h new file mode 100644 index 0000000..083f57b --- /dev/null +++ b/include/gpac/thread.h @@ -0,0 +1,271 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_THREAD_H_ +#define _GF_THREAD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file <gpac/thread.h> + * \brief threading functions. + */ + + /*! + * \addtogroup thr_grp threading + * \ingroup utils_grp + * \brief Threading and Mutual Exclusion Functions + * + *This section documents the threading of the GPAC framework. These provide an easy way to implement + *safe multithreaded tools. + * @{ + */ + +#include <gpac/tools.h> + +/*! + *\brief Thread states + * + *Inidcates the execution status of a thread + */ +enum +{ + /*! the thread has been initialized but is not started yet*/ + GF_THREAD_STATUS_STOP = 0, + /*! the thread is running*/ + GF_THREAD_STATUS_RUN = 1, + /*! the thread has exited its body function*/ + GF_THREAD_STATUS_DEAD = 2 +}; + +/*! + *\brief abstracted thread object + * + *The abstracted thread object allows you to execute some code independently of the main process of your application. +*/ +typedef struct __tag_thread GF_Thread; + +/*! + *\brief thread constructor + * + *Constructs a new thread object + *\param log name of the thread if any + */ +GF_Thread *gf_th_new(const char *name); +/*! + *\brief thread destructor + * + * Kills the thread if running and destroys the object + *\param th the thread object + */ +void gf_th_del(GF_Thread *th); + +/*! + * \brief thread run function callback + * + *The gf_thread_run type is the type for the callback of the \ref gf_thread_run function + *\param par opaque user data + *\return exit code of the thread, usually 1 for error and 0 if normal execution + * + */ +typedef u32 (*gf_thread_run)(void *par); + +/*! + *\brief thread execution + * + *Executes the thread with the given function + *\param th the thread object + *\param run the function this thread will call + *\param par the argument to the function the thread will call + *\note A thread may be run several times but cannot be run twice in the same time. + */ +GF_Err gf_th_run(GF_Thread *th, gf_thread_run run, void *par); +/*! + *\brief thread stoping + * + *Waits for the thread exit until return + *\param th the thread object + */ +void gf_th_stop(GF_Thread *th); +/*! + *\brief thread status query + * + *Gets the thread status + *\param th the thread object + */ +u32 gf_th_status(GF_Thread *th); + +/*! + * thread priorities + */ +enum +{ + /*!Idle Priority*/ + GF_THREAD_PRIORITY_IDLE=0, + /*!Less Idle Priority*/ + GF_THREAD_PRIORITY_LESS_IDLE, + /*!Lowest Priority*/ + GF_THREAD_PRIORITY_LOWEST, + /*!Low Priority*/ + GF_THREAD_PRIORITY_LOW, + /*!Normal Priority (the default one)*/ + GF_THREAD_PRIORITY_NORMAL, + /*!High Priority*/ + GF_THREAD_PRIORITY_HIGH, + /*!Highest Priority*/ + GF_THREAD_PRIORITY_HIGHEST, + /*!First real-time priority*/ + GF_THREAD_PRIORITY_REALTIME, + /*!Last real-time priority*/ + GF_THREAD_PRIORITY_REALTIME_END=255 +}; + +/*! + *\brief thread priority + * + *Sets the thread execution priority level. + *\param th the thread object + *\param priority the desired priority + *\note this should be used with caution, especially use of real-time priorities. + */ +void gf_th_set_priority(GF_Thread *th, s32 priority); +/*! + *\brief current thread ID + * + *Gets the ID of the current thread the caller is in. +*/ +u32 gf_th_id(); + + + +/*! + *\brief abstracted mutex object + * + *The abstracted mutex object allows you to make sure that portions of the code (typically access to variables) cannot be executed + *by two threads (or a thread and the main process) at the same time. +*/ +typedef struct __tag_mutex GF_Mutex; +/* + *\brief mutex constructor + * + *Contructs a new mutex object + *\param log name of the thread if any +*/ +GF_Mutex *gf_mx_new(const char *name); +/* + *\brief mutex denstructor + * + *Destroys a mutex object. This will wait for the mutex to be released if needed. + *\param mx the mutex object +*/ +void gf_mx_del(GF_Mutex *mx); +/* + *\brief mutex locking + * + *Locks the mutex object, making sure that another thread locking this mutex cannot exectute until the mutex is unlocked. + *\param mx the mutex object + *\return 1 if success, 0 if error locking the mutex (which should never happen) +*/ +u32 gf_mx_p(GF_Mutex *mx); +/* + *\brief mutex unlocking + * + *Unlocks the mutex object, allowing other threads waiting on this mutex to continue their execution + *\param mx the mutex object +*/ +void gf_mx_v(GF_Mutex *mx); +/* + *\brief mutex non-blocking lock + * + *Attemps to lock the mutex object without blocking until the object is released. + *\param mx the mutex object + *\return 1 if the mutex has been successfully locked, in which case it shall then be unlocked, or 0 if the mutex is locked by another thread. +*/ +Bool gf_mx_try_lock(GF_Mutex *mx); + + +/********************************************************************* + Semaphore Object +**********************************************************************/ +/*! + *\brief abstracted semaphore object + * + *The abstracted semaphore object allows you to control how portions of the code (typically access to variables) are executed + *by two threads (or a thread and the main process) at the same time. The best image for a semaphore is a limited set + *of money coins (always easy to understand hmm?). If no money is in the set, nobody can buy anything until a coin is + *put back in the set. When the set is full, the money is wasted (call it "the bank"...). +*/ +typedef struct __tag_semaphore GF_Semaphore; +/* + *\brief semaphore constructor + * + *Constructs a new semaphore object + *\param MaxCount the maximum notification count of this semaphore + *\param InitCount the initial notification count of this semaphore upon construction + *\return the semaphore object + */ +GF_Semaphore *gf_sema_new(u32 MaxCount, u32 InitCount); +/* + *\brief semaphore destructor + * + *Destructs the semaphore object. This will wait for the semaphore to be released if needed. + */ +void gf_sema_del(GF_Semaphore *sm); +/* + *\brief semaphore notifivation + * + *Notifies the semaphore of a certain amount of releases. + *\param sm the semaphore object + *\param nb_rel sm the number of release to notify + *\return the number of previous notification count in the semaphore +*/ +u32 gf_sema_notify(GF_Semaphore *sm, u32 nb_rel); +/* + *\brief semaphore wait + * + *Waits for the semaphore to be accessible (eg, may wait an infinite time). + *\param sm the semaphore object +*/ +void gf_sema_wait(GF_Semaphore *sm); +/* + *\brief semaphore time wait + * + *Waits for a certain for the semaphore to be accessible, and returns when semaphore is accessible or wait time has passed. + *\param sm the semaphore object + *\param time_out the amount of time to wait for the release in milliseconds + *\return returns 1 if the semaphore was released before the timeout, 0 if the semaphore is still not released after the timeout. +*/ +Bool gf_sema_wait_for(GF_Semaphore *sm, u32 time_out); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_THREAD_H_*/ + diff --git a/include/gpac/token.h b/include/gpac/token.h new file mode 100644 index 0000000..addaeac --- /dev/null +++ b/include/gpac/token.h @@ -0,0 +1,106 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_TOKEN_H_ +#define _GF_TOKEN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file <gpac/token.h> + * \brief tokenizer functions. + */ + + /*! + * \addtogroup tok_grp tokenizer + * \ingroup utils_grp + * \brief String Tokenizer Functions + * + *This section documents the basic string tokenizer of the GPAC framework. + * @{ + */ + +#include <gpac/tools.h> + +/*! + *\brief get string component + * + *Gets the next string component comprised in a given set of characters + *\param buffer source string to scan + *\param start char offset from begining of buffer where tokenization shall start + *\param separators separator characters to use + *\param token output buffer location + *\param token_size output buffer allocated size + *\return position of the first char in the buffer after the last terminating separator, or -1 if token could not be found + */ +s32 gf_token_get(char *buffer, s32 start, char *separators, char *token, s32 token_size); +/*! + *\brief get string component without delimitting characters + * + *Gets the next string component comprised in a given set of characters, removing surrounding characters + *\param buffer source string to scan + *\param start char offset from begining of buffer where tokenization shall start + *\param separators separator characters to use + *\param strip_set surrounding characters to remove + *\param token output buffer location + *\param token_size output buffer allocated size + *\return position of the first char in the buffer after the last terminating separator, or -1 if token could not be found + */ +s32 gf_token_get_strip(char *buffer, s32 start, char *separators, char *strip_set, char *token, s32 token_size); +/*! + *\brief line removal + * + *Gets one line from buffer and remove delimiters CR, LF and CRLF + *\param buffer source string to scan + *\param start char offset from begining of buffer where tokenization shall start + *\param size size of the input buffer to analyze + *\param line_buffer output buffer location + *\param line_buffer_size output buffer allocated size + *\return position of the first char in the buffer after the last line delimiter, or -1 if no line could be found + */ +s32 gf_token_get_line(char *buffer, u32 start, u32 size, char *line_buffer, u32 line_buffer_size); +/*! + *\brief pattern location + * + *Locates a pattern in the buffer + *\param buffer source string to scan + *\param start char offset from begining of buffer where tokenization shall start + *\param size size of the input buffer to analyze + *\param pattern pattern to locate + *\return position of the first char in the buffer after the pattern, or -1 if pattern could not be found + */ +s32 gf_token_find(char *buffer, u32 start, u32 size, char *pattern); + + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_TOKEN_H_*/ + diff --git a/include/gpac/tools.h b/include/gpac/tools.h new file mode 100644 index 0000000..80da566 --- /dev/null +++ b/include/gpac/tools.h @@ -0,0 +1,655 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_TOOLS_H_ +#define _GF_TOOLS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/setup.h> + + +/*! \file "gpac/tools.h" + * \brief Base definitions and functions of GPAC. + * + * This file contains basic functions and core definitions of the GPAC framework. This file is + * usually included by all GPAC header files since it contains the error definitions. +*/ + +/*! \defgroup utils_grp utils + * You will find in this module the documentation of all tools used in GPAC. +*/ + +/*! \addtogroup tools_grp base utils + * \ingroup utils_grp + * \brief Base definitions and functions of GPAC. + * + * This section documents some very basic functions and core definitions of the GPAC framework. + * @{ + */ + +/*! + * \brief GPAC Version + * \hideinitializer + * + * Macro giving GPAC version expressed as a printable string +*/ +/*KEEP SPACE SEPARATORS FOR MAKE / GREP (SEE MAIN MAKEFILE)!!!, and NO SPACE in GPAC_VERSION for proper install*/ +#define GPAC_VERSION "0.4.5" + +#define GPAC_BUILD_NUMBER "33" +#define GPAC_FULL_VERSION GPAC_VERSION" (build "GPAC_BUILD_NUMBER")" + +/*! + * \brief GPAC Version + * \hideinitializer + * + * Macro giving GPAC version expressed as an integer, where version X.Y.Z is coded as 0x00XXYYZZ +*/ +#define GPAC_VERSION_INT 0x00000405 + +/*! + * \brief Memory allocation + * \hideinitializer + * + * Macro allocating memory and zero-ing it +*/ +#define GF_SAFEALLOC(__ptr, __struct) { __ptr = (__struct *) malloc(sizeof(__struct)); if (__ptr) memset((void *) __ptr, 0, sizeof(__struct)); } + +/*! + * \brief 4CC Formatting + * \hideinitializer + * + * Macro formating a 4-character code (or 4CC) "abcd" as 0xAABBCCDD +*/ +#define GF_4CC(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d)) +/*! + * \brief 4CC Printing + * + * returns a 4CC printable form +*/ +const char *gf_4cc_to_str(u32 type); + +/*! + * \brief large file opening + * + * Opens a large file (>4GB) + * \param file_name Same semantics as fopen + * \param mode Same semantics as fopen + * \return stream handle of the file object + * \note You only need to call this function if you're suspecting the file to be a large one (usually only media files), otherwise use regular stdio. +*/ +FILE *gf_f64_open(const char *file_name, const char *mode); +/*! + * \brief large file position query + * + * Queries the current read/write position in a large file + * \param f Same semantics as ftell + * \return position in the file + * \note You only need to call this function if you're suspecting the file to be a large one (usually only media files), otherwise use regular stdio. +*/ +u64 gf_f64_tell(FILE *f); +/*! + * \brief large file seeking + * + * Seeks the current read/write position in a large file + * \param f Same semantics as fseek + * \param pos Same semantics as fseek + * \param whence Same semantics as fseek + * \return new position in the file + * \note You only need to call this function if you're suspecting the file to be a large one (usually only media files), otherwise use regular stdio. +*/ +u64 gf_f64_seek(FILE *f, s64 pos, s32 whence); + +/*! @} */ + + +/*! \addtogroup errors_grp error codes + * \ingroup utils_grp + * \brief Errors used in GPAC. + * + * This section documents all error codes used in the GPAC framework. Most of the GPAC's functions will use these as + * return values, and some of these errors are also used for state communication with the different modules of the framework. + * @{ + */ + +/*! + * GPAC Error + * \hideinitializer + * + * positive values are warning and info, 0 means no error and negative values are errors + */ +typedef enum +{ + /*!Message from any scripting engine used in the presentation (ECMAScript, MPEG-J, ...) (Info).*/ + GF_SCRIPT_INFO = 3, + /*!Indicates an data frame has several AU packed (not MPEG-4 compliant). This is used by decoders to force + multiple decoding of the same data frame (Info).*/ + GF_PACKED_FRAMES = 2, + /*!Indicates the end of a stream or of a file (Info).*/ + GF_EOS = 1, + /*! + \n\n + */ + /*!Operation success (no error).*/ + GF_OK = 0, + /*!\n*/ + /*!One of the input parameter is not correct or cannot be used in the current operating mode of the framework.*/ + GF_BAD_PARAM = -1, + /*! Memory allocation failure.*/ + GF_OUT_OF_MEM = -2, + /*! Input/Output failure (disk access, system call failures)*/ + GF_IO_ERR = -3, + /*! The desired feature or operation is not supported by the framework*/ + GF_NOT_SUPPORTED = -4, + /*! Input data has been corrupted*/ + GF_CORRUPTED_DATA = -5, + /*! A modification was attempted on a scene node which could not be found*/ + GF_SG_UNKNOWN_NODE = -6, + /*! The PROTO node interface does not match the nodes using it*/ + GF_SG_INVALID_PROTO = -7, + /*! An error occured in the scripting engine*/ + GF_SCRIPT_ERROR = -8, + /*! Buffer is too small to contain decoded data. Decoders shall use this error whenever they need to resize their output memory buffers*/ + GF_BUFFER_TOO_SMALL = -9, + /*! Bitstream is not compliant to the specfication it refers to*/ + GF_NON_COMPLIANT_BITSTREAM = -10, + /*! No decoders could be found to handle the desired media type*/ + GF_CODEC_NOT_FOUND = -11, + /*! The URL is not properly formatted or cannot be found*/ + GF_URL_ERROR = -12, + /*! An service error has occured at the local side*/ + GF_SERVICE_ERROR = -13, + /*! A service error has occured at the remote (server) side*/ + GF_REMOTE_SERVICE_ERROR = -14, + /*! The desired stream could not be found in the service*/ + GF_STREAM_NOT_FOUND = -15, + /*! The IsoMedia file is not a valid one*/ + GF_ISOM_INVALID_FILE = -20, + /*! The IsoMedia file is not complete. Either the file is being downloaded, or it has been truncated*/ + GF_ISOM_INCOMPLETE_FILE = -21, + /*! The media in this IsoMedia track is not valid (usually due to a broken stream description)*/ + GF_ISOM_INVALID_MEDIA = -22, + /*! The requested operation cannot happen in the current opening mode of the IsoMedia file*/ + GF_ISOM_INVALID_MODE = -23, + /*! This IsoMedia track refers to media outside the file in an unknown way*/ + GF_ISOM_UNKNOWN_DATA_REF = -24, + + /*! An invalid MPEG-4 Object Descriptor was found*/ + GF_ODF_INVALID_DESCRIPTOR = -30, + /*! An MPEG-4 Object Descriptor was found or added to a forbidden descriptor*/ + GF_ODF_FORBIDDEN_DESCRIPTOR = -31, + /*! An invalid MPEG-4 BIFS command was detected*/ + GF_ODF_INVALID_COMMAND = -32, + /*! The scene has been encoded using an unknown BIFS version*/ + GF_BIFS_UNKNOWN_VERSION = -33, + + /*! The remote IP address could not be solved*/ + GF_IP_ADDRESS_NOT_FOUND = -40, + /*! The connection to the remote peer has failed*/ + GF_IP_CONNECTION_FAILURE = -41, + /*! The network operation has failed*/ + GF_IP_NETWORK_FAILURE = -42, + /*! The network connection has been closed*/ + GF_IP_CONNECTION_CLOSED = -43, + /*! The network operation has failed because no data is available*/ + GF_IP_NETWORK_EMPTY = -44, + /*! The network operation has been discarded because it would be a blocking one*/ + GF_IP_SOCK_WOULD_BLOCK = -45, + /*! UDP connection did not receive any data at all. Signaled by client services to reconfigure network if possible*/ + GF_IP_UDP_TIMEOUT = -46, + + /*! Authentication with the remote host has failed*/ + GF_AUTHENTICATION_FAILURE = -50, + /*! Script not ready for playback */ + GF_SCRIPT_NOT_READY = -51, +} GF_Err; + +/*! + * \brief Error Printing + * + * Returns a printable version of a given error + * \param e Error code requested + * \return String representing the error +*/ +const char *gf_error_to_string(GF_Err e); + +/*! @} */ + +/*! \addtogroup log_grp logging tools + * \ingroup utils_grp + * @{ + */ + +/*! + * GPAC Log Levels + * \hideinitializer + * + * These levels describes messages priority used when filtering logs + */ +enum +{ + /*! Log message describes an error*/ + GF_LOG_ERROR = 1, + /*! Log message describes a warning*/ + GF_LOG_WARNING, + /*! Log message is informational (state, etc..)*/ + GF_LOG_INFO, + /*! Log message is a debug info*/ + GF_LOG_DEBUG, +}; + +/*! + * \brief Log level assignment + * + * Sets the level used for log filtering. By default no log is performed + * \param level log level used. + * + */ +void gf_log_set_level(u32 level); + + +/*! + * GPAC Log tools + * \hideinitializer + * + * These flags describes which sub-part of GPAC generates the log and are used when filtering logs + */ +enum +{ + /*! Log message from the core library (init, threads, network calls, etc)*/ + GF_LOG_CORE = 1, + /*! Log message from a raw media parser (BIFS, LASeR, A/V formats)*/ + GF_LOG_CODING= 1<<1, + /*! Log message from a bitstream parser (IsoMedia, MPEG-2 TS, OGG, ...)*/ + GF_LOG_CONTAINER = 1<<2, + /*! Log message from the network/service stack (messages & co)*/ + GF_LOG_NETWORK = 1<<3, + /*! Log message from the RTP/RTCP stack (TS info) and packet structure & hinting (debug)*/ + GF_LOG_RTP = 1<<4, + /*! Log message from authoring subsystem (file manip, import/export)*/ + GF_LOG_AUTHOR = 1<<5, + /*! Log message from the sync layer of the terminal*/ + GF_LOG_SYNC = 1<<6, + /*! Log message from a codec*/ + GF_LOG_CODEC = 1<<7, + /*! Log message from any XML parser (context loading, etc)*/ + GF_LOG_PARSER = 1<<8, + /*! Log message from the terminal/compositor, indicating media object state*/ + GF_LOG_MEDIA = 1<<9, + /*! Log message from the scene graph/scene manager (handling of nodes and attribute modif, DOM core)*/ + GF_LOG_SCENE = 1<<10, + /*! Log message from the scripting engine*/ + GF_LOG_SCRIPT = 1<<11, + /*! Log message from event handling*/ + GF_LOG_INTERACT = 1<<12, + /*! Log message from compositor*/ + GF_LOG_COMPOSE = 1<<13, + /*! Log for video object cache */ + GF_LOG_CACHE = 1<<14, + /*! Log message from multimedia I/O devices (audio/video input/output, ...)*/ + GF_LOG_MMIO = 1<<15, + /*! Log for runtime info (times, memory, CPU usage)*/ + GF_LOG_RTI = 1<<16, + /*! Log for SMIL timing and animation*/ + GF_LOG_SMIL = 1<<17, + +}; + +/*! + * \brief Log modules assignment + * + * Sets the modules to be checked for log filtering. By default no modules are logged. + * \param tools log tools filtered. This is an OR'ed combinaison of log module flags + * + */ +void gf_log_set_tools(u32 tools); + +/*! + * \brief Log Message Callback + * + * The gf_log_cbk type is the type for the callback of the \ref gf_log_set_callback function. By default all logs are redirected to stdout + * \param cbck Opaque user data. + * \param log_level level of the log. This value is not guaranteed in multi-threaded context. + * \param log_tool tool emitting the log. This value is not guaranteed in multi-threaded context. + * \param fmt message log format. + * \param vlist message log param. + * + */ +typedef void (*gf_log_cbk)(void *cbck, u32 log_level, u32 log_tool, const char* fmt, va_list vlist); + +/*! + * \brief Log overwrite + * + * Assigns a user-defined callback for printing log messages. By default all logs are redirected to stdout + * \param usr_cbk Opaque user data + * \param cbk callback log function + * \return previous callback function +*/ +gf_log_cbk gf_log_set_callback(void *usr_cbk, gf_log_cbk cbk); + +/*! + \cond DUMMY_DOXY_SECTION +*/ + +#ifndef GPAC_DISABLE_LOG +/*note: + to turn log on, change to GPAC_ENABLE_LOG + to turn log off, change to GPAC_DISABLE_LOG + this is needed by configure+sed to modify this file directly +*/ +#define GPAC_ENABLE_LOG +#endif + +/*! + \endcond +*/ + + +/*this is all a bit ugly, but most compilers don't properly handle variadic macros...*/ +void gf_log(const char *fmt, ...); +void gf_log_lt(u32 ll, u32 lt); + +u32 gf_log_get_level(); +u32 gf_log_get_tools(); + +#ifdef GPAC_DISABLE_LOG +#define GF_LOG(_ll, _lm, __args) +#else +/*! + * \brief Message logging + * \hideinitializer + * + * Macro for logging messages. Usage is GF_LOG(log_lev, log_module, (fmt, ...)). The log function is only called if log filtering allows it. This avoids fetching logged parameters when the tool is not being logged. +*/ +#define GF_LOG(_log_level, _log_tools, __args) if ((gf_log_get_level() >= (_log_level)) && (gf_log_get_tools() & (_log_tools))) { gf_log_lt(_log_level, _log_tools); gf_log __args ;} +#endif + + +/*! @} */ + +/*! \addtogroup tools_grp + * @{ + */ + + +/*! + * \brief PseudoRandom Integer Generation Initialization + * + * Sets the starting point for generating a series of pseudorandom integers. + * \param Reset Re-initializes the random number generator +*/ +void gf_rand_init(Bool Reset); +/*! + * \brief PseudoRandom Integer Generation + * + * Returns a pseudorandom integer. +*/ +u32 gf_rand(); + +/*! + * \brief user name + * + * Gets current user (login) name. +*/ +void gf_get_user_name(char *buf, u32 buf_size); + +/*! + * \brief Directory Enumeration Callback + * + * The gf_enum_dir_item type is the type for the callback of the \ref gf_enum_directory function + * \param cbck Opaque user data. + * \param item_name File or directory name. + * \param item_path File or directory full path and name from filesystem root. + * \return 1 to abort enumeration, 0 to continue enumeration. + * + */ +typedef Bool (*gf_enum_dir_item)(void *cbck, char *item_name, char *item_path); +/*! + * \brief Directory enumeration + * + * Enumerates a directory content. Feedback is provided by the enum_dir_item function + * \param dir Directory to enumerate + * \param enum_directory If set, only directories will be enumerated, otherwise only files are. + * \param enum_dir \ref gf_enum_dir_item callback function for enumeration. + * \param cbck Opaque user data passed to callback function. + * \param filter optional filter for file extensions. If a file extension without the dot '.' character is not found in the + * filter the file will be skipped. +*/ +GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item enum_dir, void *cbck, const char *filter); + + +/*! + * \brief File Deletion + * + * Deletes a file from the disk. + * \param fileName absolute name of the file or name relative to the current working directory. +*/ +void gf_delete_file(char *fileName); +/*! + * \brief File Deletion + * + * Creates a new temporary file in binary mode + * \return stream handle to the new file ressoucre + */ +FILE *gf_temp_file_new(); + + +/*! + * \brief Progress formatting + * + * Signals progress in GPAC's operations. Note that progress signaling with this function is not thread-safe, the main purpose is to use it for authoring tools only. + * \param title title string of the progress, or NULL for no progress + * \param done Current amount performed of the action. + * \param total Total amount of the action. + */ +void gf_set_progress(char *title, u32 done, u32 total); + +/*! + * \brief Progress Callback + * + * The gf_on_progress_cbk type is the type for the callback of the \ref gf_set_progress_callback function + * \param cbck Opaque user data. + * \param title preogress title. + * \param done Current amount performed of the action + * \param total Total amount of the action. + * + */ +typedef void (*gf_on_progress_cbk)(void *cbck, char *title, u32 done, u32 total); + +/*! + * \brief Progress overwriting + * + * Iverwrites the progress signaling function by a user-defined one. + * \param user_cbk Opaque user data + * \param prog_cbk new callback function to use. Passing NULL restore default GPAC stdout notification. + */ +void gf_set_progress_callback(void *user_cbk, gf_on_progress_cbk prog_cbk); + + +/*! + * \brief Prompt checking + * + * Checks if a character is pending in the prompt buffer. + * \return 1 if a character is ready to be fetched, 0 otherwise. + * \note Function not available under WindowsCE nor SymbianOS +*/ +Bool gf_prompt_has_input(); + +/*! + * \brief Prompt character flush + * + * Returns the current character entered at prompt if any. + * \return value of the character. + * \note Function not available under WindowsCE nor SymbianOS +*/ +char gf_prompt_get_char(); + + +/*! + * \brief turns prompt echo on/off + * + * Turns the prompt character echo on/off - this is usefull when entering passwords. + * \param echo_off indicates whether echo should be turned on or off. + * \note Function not available under WindowsCE nor SymbianOS +*/ +void gf_prompt_set_echo_off(Bool echo_off); + +/*! + *\addtogroup cpu_grp Time tools + *\ingroup utils_grp + *\brief System time and CPU functions + * + *This section documents time functionalities and CPU management in GPAC. + * @{ + */ + + +/*! + * \brief System setup + * + * Inits the system high-resolution clock if any, and CPU usage manager. It is strongly recommended to call this + * function before calling any other GPAC functions, since on some systems (like winCE) it may result in a better memory usage estimation. + * \note This can be called several times but only the first call will result in system setup. + */ +void gf_sys_init(); +/*! + * \brief System closing + * + * Closes the system high-resolution clock and any CPU associated ressources. + * \note This can be called several times but the system will be closed when no more users are counted. + */ +void gf_sys_close(); +/*! + * \brief System clock query + * + * Gets the system clock time. + * \return System clock value since initialization in milliseconds. + */ +u32 gf_sys_clock(); + +/*! + * \brief Sleeps thread/process + * + * Locks calling thread/process execution for a given time. + * \param ms Amount of time to sleep in milliseconds. + */ +void gf_sleep(u32 ms); + +/*! + * \brief CRC32 compute + * + * Computes the CRC32 value of a buffer. + * \param data buffer + * \param size buffer size + * \return computed CRC32 + */ +u32 gf_crc_32(char *data, u32 size); + + +/*!\brief run-time system info object + * + *The Run-Time Info object is used to get CPU and memory occupation of the calling process. + *All time values are expressed in milliseconds (accuracy is not guaranteed). +*/ +typedef struct +{ + /*!start of the sampling period*/ + u32 sampling_instant; + /*!duration of the sampling period*/ + u32 sampling_period_duration; + /*!total amount of time (User+kernel) spent in CPU for all processes as evaluated at the end of the sampling period*/ + u32 total_cpu_time; + /*!total amount of time (User+kernel) spent in CPU for the calling process as evaluated at the end of the sampling period*/ + u32 process_cpu_time; + /*!amount of time (User+kernel) spent in CPU for all processes during the sampling period*/ + u32 total_cpu_time_diff; + /*!total amount of time (User+kernel) spent in CPU for the calling process during the sampling period*/ + u32 process_cpu_time_diff; + /*!total amount of idle time during the sampling period.*/ + u32 cpu_idle_time; + /*!percentage (from 0 to 100) of CPU usage during the sampling period.*/ + u32 total_cpu_usage; + /*!percentage (from 0 to 100) of the CPU usage by the calling process during the sampling period.*/ + u32 process_cpu_usage; + /*!calling process ID*/ + u32 pid; + /*!calling process thread count if known*/ + u32 thread_count; + /*!size of calling process allocated heaps*/ + u64 process_memory; + /*!total physical memory in system*/ + u64 physical_memory; + /*!available physical memory in system*/ + u64 physical_memory_avail; + /*!total memory currently allocated by gpac*/ + u64 gpac_memory; +} GF_SystemRTInfo; + +/*! + * Selection flags for run-time info retrieval + * \hideinitializer + */ +enum +{ + /*!Indicates all processes' times must be fetched. If not set, only the current process times will be retrieved, and the + thread count and total times won't be available*/ + GF_RTI_ALL_PROCESSES_TIMES = 1, + /*!Indicates the process allocated heap size must be fetch. If not set, only the system physical memory is fetched. + Fetching the entire ocess allocated memory can have a large impact on performances*/ + GF_RTI_PROCESS_MEMORY = 1<<1, + /*!Indicates that only system memory should be fetched. When set, all refreshing info is ignored*/ + GF_RTI_SYSTEM_MEMORY_ONLY = 1<<2, +}; + +/*! + * \brief Gets Run-Time info + * + * Gets CPU and memory usage info for the calling process and the system. Information gathering + * is controled through timeout values. + * \param refresh_time_ms refresh time period in milliseconds. If the last sampling was done less than this period ago, the run-time info is not refreshed. + * \param rti holder to the run-time info structure to update. + * \param flags specify which info is to be retrieved. + * \return 1 if info has been updated, 0 otherwise. + * \note You should not try to use a too small refresh time. Typical values are 500 ms or one second. + */ +Bool gf_sys_get_rti(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags); + + +Bool gf_sys_get_battery_state(Bool *onBattery, u32 *onCharge, u32 *level); + +/*! @} */ + + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_CORE_H_*/ + diff --git a/include/gpac/user.h b/include/gpac/user.h new file mode 100644 index 0000000..bf13ac6 --- /dev/null +++ b/include/gpac/user.h @@ -0,0 +1,111 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Stream Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#ifndef _GF_USER_H_ +#define _GF_USER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//#include <gpac/math.h> +#include <gpac/events.h> +#include <gpac/module.h> + +/*GPAC client terminal*/ +typedef struct _tag_terminal GF_Terminal; + + + +enum +{ + /*display should be hidden upon initialization*/ + GF_TERM_INIT_HIDE = 1, + /*no audio renderer will be created*/ + GF_TERM_NO_AUDIO = 1<<1, + /*terminal is used without visual threading: + * media codecs are not threaded + * all composition memories are filled before rendering + * rendering is done after media decoding + * the user is responsible for updating the terminal + */ + GF_TERM_NO_VISUAL_THREAD = 1<<2, + /*disables frame-rate regulation (used when dumping content)*/ + GF_TERM_NO_REGULATION = 1<<3, + /*lets the main user handle window events (neede for browser plugins)*/ + GF_TERM_NO_WINDOWPROC_OVERRIDE = 1<<4, + /*works in windowless mode - experimental, only supported on Win32*/ + GF_TERM_WINDOWLESS = 1<<5 +}; + +/*user object for all callbacks*/ +typedef struct +{ + /*user defined callback for all functions - cannot be NULL*/ + void *opaque; + /*the event proc. Return value depend on the event type, usually 0 + cannot be NULL if os_window_handler is specified and dont_override_window_proc is set + may be NULL otherwise*/ + Bool (*EventProc)(void *opaque, GF_Event *event); + + /*config file of client - cannot be NULL*/ + GF_Config *config; + /*modules manager - cannot be NULL - owned by the user (to allow selection of module directory)*/ + GF_ModuleManager *modules; + /*optional os window handler (HWND on win32/winCE, XWindow for X11) + if not set the video outut will create and manage the display window.*/ + void *os_window_handler; + /*for now, only used by X11 (indicates display the window is on)*/ + void *os_display; + + /*init flags bypassing GPAC config file */ + u32 init_flags; +} GF_User; + + +/*macro for event forwarding*/ +#define GF_USER_SENDEVENT(_user, _evt) (_user->EventProc ? _user->EventProc(_user->opaque, _evt) : 0) + +/*macro for message event format/send*/ +#define GF_USER_MESSAGE(_user, _serv, _msg, _e) \ + { \ + GF_Event evt; \ + if (_user->EventProc) { \ + evt.type = GF_EVENT_MESSAGE; \ + evt.message.service = _serv; \ + evt.message.message = _msg; \ + evt.message.error = _e; \ + _user->EventProc(_user->opaque, &evt); \ + } \ + } + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_USER_H_*/ + diff --git a/include/gpac/utf.h b/include/gpac/utf.h new file mode 100644 index 0000000..0863b1a --- /dev/null +++ b/include/gpac/utf.h @@ -0,0 +1,99 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_UTF_H_ +#define _GF_UTF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file <gpac/utf.h> + * \brief UTF functions. + */ + +/*! + * \addtogroup utf_grp UTF + * \ingroup utils_grp + * \brief UTF encoding functions + * + *This section documents the UTF functions of the GPAC framework.\n + *The wide characters in GPAC are unsignad shorts, in other words GPAC only supports UTF8 and UTF16 coding styles. + *\note these functions are just ports of libutf8 library tools into GPAC. + * @{ + */ + +#include <gpac/tools.h> + +/*! + *\brief wide-char to multibyte conversion + * + *Converts a wide-char string to a multibyte string + *\param dst multibyte destination buffer + *\param dst_len multibyte destination buffer size + *\param srcp address of the wide-char string. This will be set to the next char to be converted in the input buffer if not enough space in the destination, or NULL if conversion was completed. + *\return length (in byte) of the multibyte string or -1 if error. + */ +size_t gf_utf8_wcstombs(char* dst, size_t dst_len, const unsigned short** srcp); +/*converts UTF8 string to wide char string - returns (-1) if error. set @srcp to next char to be +converted if not enough space*/ +/*! + *\brief multibyte to wide-char conversion + * + *Converts a multibyte string to a wide-char string + *\param dst wide-char destination buffer + *\param dst_len wide-char destination buffer size + *\param srcp address of the multibyte character buffer. This will be set to the next char to be converted in the input buffer if not enough space in the destination, or NULL if conversion was completed. + *\return length (in unsigned short) of the wide-char string or -1 if error. + */ +size_t gf_utf8_mbstowcs(unsigned short* dst, size_t dst_len, const char** srcp); +/*! + *\brief wide-char string length + * + *Returns the length in character of a wide-char string + *\param s the wide-char string + *\return the wide-char string length + */ +size_t gf_utf8_wcslen(const unsigned short *s); + +/*! + *\brief string bidi reordering + * + *Performs a simple reordering of words in the string based on each word direction, so that glyphs are sorted in display order. + *\param utf_string the wide-char string + *\param len the len of the wide-char string + *\return 1 if the main direction is right-to-left, 0 otherwise + */ +Bool gf_utf8_reorder_bidi(u16 *utf_string, u32 len); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_UTF_H_*/ + diff --git a/include/gpac/xml.h b/include/gpac/xml.h new file mode 100644 index 0000000..0d5b917 --- /dev/null +++ b/include/gpac/xml.h @@ -0,0 +1,158 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _XML_PARSER_H_ +#define _XML_PARSER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <gpac/tools.h> +#include <gpac/list.h> + +/*! + * \file <gpac/xml.h> + * \brief XML functions. + */ + +/*! + * \addtogroup xml_grp XML + * \ingroup utils_grp + * \brief XML Parsing functions + * + *This section documents the XML functions of the GPAC framework.\n + * @{ + */ + + + +typedef struct +{ + /*name or namespace:name*/ + char *name; + /*value*/ + char *value; +} GF_XMLAttribute; + +/*XML node types*/ +enum +{ + GF_XML_NODE_TYPE = 0, + GF_XML_TEXT_TYPE, + GF_XML_CDATA_TYPE, +}; + +typedef struct _xml_node +{ + u32 type; + /* + For DOM nodes: name + For other (text, css, cdata), element content + */ + char *name; + + /*for DOM nodes only*/ + char *ns; /*namespace*/ + GF_List *attributes; + GF_List *content; +} GF_XMLNode; + + + +/* + SAX XML Parser +*/ + +typedef struct _tag_sax_parser GF_SAXParser; +typedef void (*gf_xml_sax_node_start)(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes); +typedef void (*gf_xml_sax_node_end)(void *sax_cbck, const char *node_name, const char *name_space); +typedef void (*gf_xml_sax_text_content)(void *sax_cbck, const char *content, Bool is_cdata); + +typedef void (*gf_xml_sax_progress)(void *cbck, u32 done, u32 tot); + +/*creates new sax parser - all callbacks are optionals*/ +GF_SAXParser *gf_xml_sax_new(gf_xml_sax_node_start on_node_start, + gf_xml_sax_node_end on_node_end, + gf_xml_sax_text_content on_text_content, + void *cbck); + +/*destroys sax parser */ +void gf_xml_sax_del(GF_SAXParser *parser); +/*inits parser with BOM. BOM must be 4 char string with 0 terminaison. If BOM is NULL, parsing will +assume UTF-8 compatible coding*/ +GF_Err gf_xml_sax_init(GF_SAXParser *parser, unsigned char *BOM); +/*parses input string data. string data MUST be terminated by the 0 character (eg 2 0s for UTF-16)*/ +GF_Err gf_xml_sax_parse(GF_SAXParser *parser, void *string_bytes); +/*suspends/resume sax parsing. + When resuming on file, the function will run until suspended/end of file/error + When resuming on steram, the function will simply return +*/ +GF_Err gf_xml_sax_suspend(GF_SAXParser *parser, Bool do_suspend); +/*parses file (potentially gzipped). OnProgress is optional, used to get progress callback*/ +GF_Err gf_xml_sax_parse_file(GF_SAXParser *parser, const char *fileName, gf_xml_sax_progress OnProgress); +/*get current line number*/ +u32 gf_xml_sax_get_line(GF_SAXParser *parser); +/*get file size - may be inaccurate if gzipped (only compressed file size is known)*/ +u32 gf_xml_sax_get_file_size(GF_SAXParser *parser); +/*get current file position*/ +u32 gf_xml_sax_get_file_pos(GF_SAXParser *parser); + +/*peeks a node forward in the file. May be used to pick the attribute of the first node found matching a given (attributeName, attributeValue) couple*/ +char *gf_xml_sax_peek_node(GF_SAXParser *parser, char *att_name, char *att_value, char *substitute, char *get_attr, char *end_pattern, Bool *is_substitute); + +/*file mode only, returns 1 if file is compressed, 0 otherwise*/ +Bool gf_xml_sax_binary_file(GF_SAXParser *parser); + +const char *gf_xml_sax_get_error(GF_SAXParser *parser); + +char *gf_xml_get_root_type(const char *file, GF_Err *ret_code); + +u32 gf_xml_sax_get_node_start_pos(GF_SAXParser *parser); +u32 gf_xml_sax_get_node_end_pos(GF_SAXParser *parser); + + +typedef struct _tag_dom_parser GF_DOMParser; +GF_DOMParser *gf_xml_dom_new(); +void gf_xml_dom_del(GF_DOMParser *parser); +GF_Err gf_xml_dom_parse(GF_DOMParser *parser, const char *file, gf_xml_sax_progress OnProgress, void *cbk); +GF_XMLNode *gf_xml_dom_get_root(GF_DOMParser *parser); +const char *gf_xml_dom_get_error(GF_DOMParser *parser); +u32 gf_xml_dom_get_line(GF_DOMParser *parser); + +char *gf_xml_dom_serialize(GF_XMLNode *node, Bool content_only); + + +GF_XMLNode *gf_xml_dom_detach_root(GF_DOMParser *parser); +void gf_xml_dom_node_del(GF_XMLNode *node); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_XML_PARSER_H_*/ + diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..fc7419a --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,93 @@ +include ../config.mak + +#all OS and lib independent +PLUGDIRS=aac_in ac3_in bifs_dec ctx_load dummy_in soft_raster mp3_in isom_in odf_dec rtp_in raw_out timedtext img_in svg_in saf_in mpegts_in ismacryp + +ifeq ($(DISABLE_SVG), no) +PLUGDIRS+=laser_dec svg_in +endif + +ifeq ($(CONFIG_FT), no) +else +PLUGDIRS+=ft_font +endif + +ifeq ($(CONFIG_FFMPEG), no) +else +PLUGDIRS+=ffmpeg_in +endif + +ifeq ($(CONFIG_XVID), no) +else +PLUGDIRS+=xvid_dec +endif + +ifeq ($(CONFIG_OGG), no) +else +PLUGDIRS+=ogg +endif + +ifeq ($(CONFIG_AMR_NB), yes) +PLUGDIRS+=amr_dec +endif + +ifeq ($(CONFIG_AMR_NB_FT), yes) +PLUGDIRS+=amr_float_dec +else +ifeq ($(CONFIG_AMR_WB_FT), yes) +PLUGDIRS+=amr_float_dec +endif +endif + +ifeq ($(CONFIG_OSS_AUDIO), no) +else +PLUGDIRS+=oss_audio +endif + +ifeq ($(CONFIG_ALSA), yes) +PLUGDIRS+=alsa +endif + +ifeq ($(CONFIG_JACK), yes) +PLUGDIRS+=jack +endif + +ifeq ($(CONFIG_SDL), yes) +PLUGDIRS+=sdl_out +endif +ifeq ($(CONFIG_PULSEAUDIO), yes) +PLUGDIRS+=pulseaudio +endif + +ifeq ($(CONFIG_X11),yes) +PLUGDIRS+=x11_out +endif + +ifeq ($(CONFIG_JS),no) +else +PLUGDIRS+=gpac_js +endif + +#w32 plugins +ifeq ($(CONFIG_WIN32),yes) +PLUGDIRS+=wav_out +ifeq ($(CONFIG_DIRECTX),yes) +PLUGDIRS+=dx_hw +endif + +endif + + +all: plugs + +plugs: + set -e; for i in $(PLUGDIRS) ; do $(MAKE) -C $$i all; done + +dep: + set -e; for i in $(PLUGDIRS) ; do $(MAKE) -C $$i dep; done + +clean: + set -e; for i in $(PLUGDIRS) ; do $(MAKE) -C $$i clean; done + +distclean: + set -e; for i in $(PLUGDIRS) ; do $(MAKE) -C $$i distclean; done diff --git a/modules/aac_in/Makefile b/modules/aac_in/Makefile new file mode 100644 index 0000000..535391e --- /dev/null +++ b/modules/aac_in/Makefile @@ -0,0 +1,76 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/aac_in + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= aac_in.o + +SRCS := $(OBJS:.o=.c) + +#faad config +ifeq ($(CONFIG_FAAD), no) +else +OBJS+=faad_dec.o +CFLAGS+=-DGPAC_HAS_FAAD +#local faad lib +ifeq ($(CONFIG_FAAD), local) +EXTRALIBS+=-L../../extra_lib/lib/gcc +CFLAGS+= -I$(LOCAL_INC_PATH)/faad +endif +EXTRALIBS+= -lfaad +endif + +LIB=gm_aac_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols aac_in.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) -L../../bin/gcc -lgpac $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/aac_in/aac_in.c b/modules/aac_in/aac_in.c new file mode 100644 index 0000000..ca91b86 --- /dev/null +++ b/modules/aac_in/aac_in.c @@ -0,0 +1,761 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / AAC reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/modules/service.h> +#include <gpac/modules/codec.h> +#include <gpac/avparse.h> +#include <gpac/constants.h> + +typedef struct +{ + GF_ClientService *service; + + Bool is_remote; + + FILE *stream; + u32 duration; + + Bool needs_connection; + u32 pad_bytes; + Bool done; + u32 is_inline; + LPNETCHANNEL ch; + + unsigned char *data; + u32 data_size; + GF_SLHeader sl_hdr; + + u32 sample_rate, oti, sr_idx, nb_ch, prof; + Double start_range, end_range; + u32 current_time, nb_samp; + /*file downloader*/ + GF_DownloadSession * dnload; + + Bool is_live; + char prev_data[1000]; + u32 prev_size; + + char *icy_name; + char *icy_genre; + char *icy_track_name; +} AACReader; + +typedef struct +{ + Bool is_mp2, no_crc; + u32 profile, sr_idx, nb_ch, frame_size, hdr_size; +} ADTSHeader; + +static Bool AAC_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt; + sExt = strrchr(url, '.'); + if (!sExt) return 0; + if (gf_term_check_extension(plug, "audio/x-m4a", "aac", "MPEG-4 AAC Music", sExt)) return 1; + if (gf_term_check_extension(plug, "audio/aac", "aac", "MPEG-4 AAC Music", sExt)) return 1; + if (gf_term_check_extension(plug, "audio/aacp", "aac", "MPEG-4 AACPlus Music", sExt)) return 1; + return 0; +} + +static Bool aac_is_local(const char *url) +{ + if (!strnicmp(url, "file://", 7)) return 1; + if (strstr(url, "://")) return 0; + return 1; +} + +static GF_ESD *AAC_GetESD(AACReader *read) +{ + GF_BitStream *dsi; + GF_ESD *esd; + u32 i, sbr_sr_idx; + + esd = gf_odf_desc_esd_new(0); + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + esd->decoderConfig->objectTypeIndication = read->oti; + esd->ESID = 1; + esd->OCRESID = 1; + esd->slConfig->timestampResolution = read->sample_rate; + if (read->is_live) esd->slConfig->useAccessUnitEndFlag = esd->slConfig->useAccessUnitStartFlag = 1; + dsi = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + /*write as regular AAC*/ + gf_bs_write_int(dsi, read->prof, 5); + gf_bs_write_int(dsi, read->sr_idx, 4); + gf_bs_write_int(dsi, read->nb_ch, 4); + gf_bs_align(dsi); + + /*always signal implicit S BR in case it's used*/ + sbr_sr_idx = read->sr_idx; + for (i=0; i<16; i++) { + if (GF_M4ASampleRates[i] == (u32) 2*read->sample_rate) { + sbr_sr_idx = i; + break; + } + } + gf_bs_write_int(dsi, 0x2b7, 11); + gf_bs_write_int(dsi, 5, 5); + gf_bs_write_int(dsi, 1, 1); + gf_bs_write_int(dsi, sbr_sr_idx, 4); + + gf_bs_align(dsi); + gf_bs_get_content(dsi, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(dsi); + return esd; +} + +static void AAC_SetupObject(AACReader *read) +{ + GF_ESD *esd; + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1; + esd = AAC_GetESD(read); + esd->OCRESID = 0; + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(read->service, (GF_Descriptor*)od, 0); +} + +static Bool ADTS_SyncFrame(GF_BitStream *bs, Bool is_complete, ADTSHeader *hdr) +{ + u32 val, pos, start_pos; + + start_pos = (u32) gf_bs_get_position(bs); + while (gf_bs_available(bs)) { + val = gf_bs_read_u8(bs); + if (val!=0xFF) continue; + val = gf_bs_read_int(bs, 4); + if (val != 0x0F) { + gf_bs_read_int(bs, 4); + continue; + } + hdr->is_mp2 = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 2); + hdr->no_crc = gf_bs_read_int(bs, 1); + pos = (u32) gf_bs_get_position(bs) - 2; + + hdr->profile = 1 + gf_bs_read_int(bs, 2); + hdr->sr_idx = gf_bs_read_int(bs, 4); + gf_bs_read_int(bs, 1); + hdr->nb_ch = gf_bs_read_int(bs, 3); + gf_bs_read_int(bs, 4); + hdr->frame_size = gf_bs_read_int(bs, 13); + gf_bs_read_int(bs, 11); + gf_bs_read_int(bs, 2); + hdr->hdr_size = 7; + if (!hdr->no_crc) { + gf_bs_read_u16(bs); + hdr->hdr_size = 9; + } + if (hdr->frame_size < hdr->hdr_size) { + gf_bs_seek(bs, pos+1); + continue; + } + hdr->frame_size -= hdr->hdr_size; + if (is_complete && (gf_bs_available(bs) == hdr->frame_size)) return 1; + else if (gf_bs_available(bs) <= hdr->frame_size) break; + + gf_bs_skip_bytes(bs, hdr->frame_size); + val = gf_bs_read_u8(bs); + if (val!=0xFF) { + gf_bs_seek(bs, pos+1); + continue; + } + val = gf_bs_read_int(bs, 4); + if (val!=0x0F) { + gf_bs_read_int(bs, 4); + gf_bs_seek(bs, pos+1); + continue; + } + gf_bs_seek(bs, pos+hdr->hdr_size); + return 1; + } + gf_bs_seek(bs, start_pos); + return 0; +} + +static Bool AAC_ConfigureFromFile(AACReader *read) +{ + Bool sync; + GF_BitStream *bs; + ADTSHeader hdr; + if (!read->stream) return 0; + bs = gf_bs_from_file(read->stream, GF_BITSTREAM_READ); + + sync = ADTS_SyncFrame(bs, !read->is_remote, &hdr); + if (!sync) { + gf_bs_del(bs); + return 0; + } + read->nb_ch = hdr.nb_ch; + read->prof = hdr.profile; + read->sr_idx = hdr.sr_idx; + read->oti = hdr.is_mp2 ? read->prof+0x66 : 0x40; + read->sample_rate = GF_M4ASampleRates[read->sr_idx]; + + read->duration = 0; + + if (!read->is_remote) { + read->duration = 1024; + gf_bs_skip_bytes(bs, hdr.frame_size); + while (ADTS_SyncFrame(bs, !read->is_remote, &hdr)) { + read->duration += 1024; + gf_bs_skip_bytes(bs, hdr.frame_size); + } + } + gf_bs_del(bs); + fseek(read->stream, 0, SEEK_SET); + return 1; +} + +static void AAC_RegulateDataRate(AACReader *read) +{ + GF_NetworkCommand com; + + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.command_type = GF_NET_CHAN_BUFFER_QUERY; + com.base.on_channel = read->ch; + while (read->ch) { + gf_term_on_command(read->service, &com, GF_OK); + if (com.buffer.occupancy < com.buffer.max) break; + gf_sleep(2); + } +} + +static void AAC_OnLiveData(AACReader *read, char *data, u32 data_size) +{ + u32 pos; + Bool sync; + GF_BitStream *bs; + ADTSHeader hdr; + + read->data = realloc(read->data, sizeof(char)*(read->data_size+data_size) ); + memcpy(read->data + read->data_size, data, sizeof(char)*data_size); + read->data_size += data_size; + + if (read->needs_connection) { + read->needs_connection = 0; + bs = gf_bs_new(read->data, read->data_size, GF_BITSTREAM_READ); + sync = ADTS_SyncFrame(bs, 0, &hdr); + gf_bs_del(bs); + if (!sync) return; + read->nb_ch = hdr.nb_ch; + read->prof = hdr.profile; + read->sr_idx = hdr.sr_idx; + read->oti = hdr.is_mp2 ? read->prof+0x66-1 : 0x40; + read->sample_rate = GF_M4ASampleRates[read->sr_idx]; + read->is_live = 1; + memset(&read->sl_hdr, 0, sizeof(GF_SLHeader)); + gf_term_on_connect(read->service, NULL, GF_OK); + AAC_SetupObject(read); + } + if (!read->ch) return; + + /*need a full adts header*/ + if (read->data_size<=7) return; + + bs = gf_bs_new(read->data, read->data_size, GF_BITSTREAM_READ); + hdr.frame_size = pos = 0; + while (ADTS_SyncFrame(bs, 0, &hdr)) { + pos = (u32) gf_bs_get_position(bs); + read->sl_hdr.accessUnitStartFlag = 1; + read->sl_hdr.accessUnitEndFlag = 1; + read->sl_hdr.AU_sequenceNumber++; + read->sl_hdr.compositionTimeStampFlag = 1; + read->sl_hdr.compositionTimeStamp += 1024; + gf_term_on_sl_packet(read->service, read->ch, read->data + pos, hdr.frame_size, &read->sl_hdr, GF_OK); + gf_bs_skip_bytes(bs, hdr.frame_size); + } + + pos = (u32) gf_bs_get_position(bs); + gf_bs_del(bs); + + if (pos) { + char *d; + read->data_size -= pos; + d = malloc(sizeof(char) * read->data_size); + memcpy(d, read->data + pos, sizeof(char) * read->data_size); + free(read->data); + read->data = d; + } + AAC_RegulateDataRate(read); +} + +void AAC_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + const char *szCache; + u32 total_size, bytes_done; + AACReader *read = (AACReader *) cbk; + + e = param->error; + /*done*/ + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + if (read->stream) { + read->is_remote = 0; + e = GF_EOS; + } else if (!read->needs_connection) { + return; + } + } else if (param->msg_type==GF_NETIO_PARSE_HEADER) { + if (!strcmp(param->name, "icy-name")) { + if (read->icy_name) free(read->icy_name); + read->icy_name = strdup(param->value); + } + if (!strcmp(param->name, "icy-genre")) { + if (read->icy_genre) free(read->icy_genre); + read->icy_genre = strdup(param->value); + } + if (!strcmp(param->name, "icy-meta")) { + GF_NetworkCommand com; + char *meta; + if (read->icy_track_name) free(read->icy_track_name); + read->icy_track_name = NULL; + meta = param->value; + while (meta && meta[0]) { + char *sep = strchr(meta, ';'); + if (sep) sep[0] = 0; + + if (!strnicmp(meta, "StreamTitle=", 12)) { + read->icy_track_name = strdup(meta+12); + } + if (!sep) break; + sep[0] = ';'; + meta = sep+1; + } + + com.base.command_type = GF_NET_SERVICE_INFO; + gf_term_on_command(read->service, &com, GF_OK); + } + return; + } else { + /*handle service message*/ + gf_term_download_update_stats(read->dnload); + if (param->msg_type!=GF_NETIO_DATA_EXCHANGE) return; + } + + /*data fetching or EOS*/ + if (e >= GF_OK) { + if (read->needs_connection) { + gf_dm_sess_get_stats(read->dnload, NULL, NULL, &total_size, NULL, NULL, NULL); + if (!total_size) read->is_live = 1; + } + if (read->is_live) { + if (!e) AAC_OnLiveData(read, param->data, param->size); + return; + } + if (read->stream) return; + + /*open service*/ + szCache = gf_dm_sess_get_cache_name(read->dnload); + if (!szCache) e = GF_IO_ERR; + else { + read->stream = fopen((char *) szCache, "rb"); + if (!read->stream) e = GF_SERVICE_ERROR; + else { + /*if full file at once (in cache) parse duration*/ + if (e==GF_EOS) read->is_remote = 0; + e = GF_OK; + /*not enough data*/ + if (!AAC_ConfigureFromFile(read)) { + /*get amount downloaded and check*/ + gf_dm_sess_get_stats(read->dnload, NULL, NULL, NULL, &bytes_done, NULL, NULL); + if (bytes_done>10*1024) { + e = GF_CORRUPTED_DATA; + } else { + fclose(read->stream); + read->stream = NULL; + return; + } + } + } + } + } + /*OK confirm*/ + if (read->needs_connection) { + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, e); + if (!e) AAC_SetupObject(read); + } +} + +void aac_download_file(GF_InputService *plug, char *url) +{ + AACReader *read = (AACReader*) plug->priv; + + read->needs_connection = 1; + + read->dnload = gf_term_download_new(read->service, url, 0, AAC_NetIO, read); + if (!read->dnload ) { + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } + /*service confirm is done once fetched*/ +} + + +static GF_Err AAC_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + char szURL[2048]; + char *ext; + GF_Err reply; + AACReader *read = plug->priv; + read->service = serv; + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + strcpy(szURL, url); + ext = strrchr(szURL, '#'); + if (ext) ext[0] = 0; + + /*remote fetch*/ + read->is_remote = !aac_is_local(szURL); + if (read->is_remote) { + aac_download_file(plug, (char *) szURL); + return GF_OK; + } + + reply = GF_OK; + read->stream = fopen(szURL, "rb"); + if (!read->stream) { + reply = GF_URL_ERROR; + } else if (!AAC_ConfigureFromFile(read)) { + fclose(read->stream); + read->stream = NULL; + reply = GF_NOT_SUPPORTED; + } + gf_term_on_connect(serv, NULL, reply); + if (!reply && read->is_inline ) AAC_SetupObject(read); + return GF_OK; +} + +static GF_Err AAC_CloseService(GF_InputService *plug) +{ + AACReader *read = plug->priv; + if (read->stream) fclose(read->stream); + read->stream = NULL; + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + if (read->data) free(read->data); + read->data = NULL; + gf_term_on_disconnect(read->service, NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *AAC_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + AACReader *read = plug->priv; + /*since we don't handle multitrack in aac, we don't need to check sub_url, only use expected type*/ + + /*override default*/ + if (expect_type==GF_MEDIA_OBJECT_UNDEF) expect_type=GF_MEDIA_OBJECT_AUDIO; + + /*audio object*/ + if (expect_type==GF_MEDIA_OBJECT_AUDIO) { + GF_ESD *esd; + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1; + esd = AAC_GetESD(read); + esd->OCRESID = 0; + gf_list_add(od->ESDescriptors, esd); + return (GF_Descriptor *) od; + } + read->is_inline = 1; + /*inline scene: no specific service*/ + return NULL; +} + +static GF_Err AAC_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ES_ID; + GF_Err e; + AACReader *read = plug->priv; + + e = GF_SERVICE_ERROR; + if (read->ch==channel) goto exit; + + e = GF_STREAM_NOT_FOUND; + if (strstr(url, "ES_ID")) { + sscanf(url, "ES_ID=%d", &ES_ID); + } + /*URL setup*/ + else if (!read->ch && AAC_CanHandleURL(plug, url)) ES_ID = 1; + + if (ES_ID==1) { + read->ch = channel; + e = GF_OK; + } + +exit: + gf_term_on_connect(read->service, channel, e); + return e; +} + +static GF_Err AAC_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + AACReader *read = plug->priv; + + GF_Err e = GF_STREAM_NOT_FOUND; + if (read->ch == channel) { + read->ch = NULL; + if (read->data) free(read->data); + read->data = NULL; + e = GF_OK; + } + gf_term_on_disconnect(read->service, channel, e); + return GF_OK; +} + +static GF_Err AAC_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + AACReader *read = plug->priv; + + + if (com->base.command_type==GF_NET_SERVICE_INFO) { + com->info.name = read->icy_track_name ? read->icy_track_name : read->icy_name; + com->info.comment = read->icy_genre; + return GF_OK; + } + + if (!com->base.on_channel) { + /*if live session we may cache*/ + if (read->is_live && (com->command_type==GF_NET_IS_CACHABLE)) return GF_OK; + return GF_NOT_SUPPORTED; + } + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: + if ((read->ch == com->base.on_channel) && read->is_live) return GF_NOT_SUPPORTED; + return GF_OK; + case GF_NET_CHAN_INTERACTIVE: + if ((read->ch == com->base.on_channel) && read->is_live) return GF_NOT_SUPPORTED; + return GF_OK; + case GF_NET_CHAN_BUFFER: + if ((read->ch == com->base.on_channel) && read->is_live) { + if (com->buffer.max<1000) com->buffer.max = 1000; + com->buffer.min = com->buffer.max/2; + } + return GF_OK; + case GF_NET_CHAN_SET_PADDING: + read->pad_bytes = com->pad.padding_bytes; + return GF_OK; + case GF_NET_CHAN_DURATION: + com->duration.duration = read->duration; + com->duration.duration /= read->sample_rate; + return GF_OK; + case GF_NET_CHAN_PLAY: + read->start_range = com->play.start_range; + read->end_range = com->play.end_range; + read->current_time = 0; + if (read->stream) fseek(read->stream, 0, SEEK_SET); + + if (read->ch == com->base.on_channel) { + read->done = 0; + /*PLAY after complete download, estimate duration*/ + if (!read->is_remote && !read->duration) { + AAC_ConfigureFromFile(read); + if (read->duration) { + GF_NetworkCommand rcfg; + rcfg.base.on_channel = read->ch; + rcfg.base.command_type = GF_NET_CHAN_DURATION; + rcfg.duration.duration = read->duration; + rcfg.duration.duration /= read->sample_rate; + gf_term_on_command(read->service, &rcfg, GF_OK); + } + } + } + return GF_OK; + case GF_NET_CHAN_STOP: + return GF_OK; + default: + return GF_OK; + } +} + + +static GF_Err AAC_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + u32 pos, start_from; + Bool sync; + GF_BitStream *bs; + ADTSHeader hdr; + AACReader *read = plug->priv; + + *out_reception_status = GF_OK; + *sl_compressed = 0; + *is_new_data = 0; + + memset(&read->sl_hdr, 0, sizeof(GF_SLHeader)); + read->sl_hdr.randomAccessPointFlag = 1; + read->sl_hdr.compositionTimeStampFlag = 1; + + if (read->ch != channel) return GF_STREAM_NOT_FOUND; + + /*fetching es data*/ + if (read->done) { + *out_reception_status = GF_EOS; + return GF_OK; + } + + if (!read->data) { + if (!read->stream) { + *out_data_ptr = NULL; + *out_data_size = 0; + return GF_OK; + } + bs = gf_bs_from_file(read->stream, GF_BITSTREAM_READ); + *is_new_data = 1; + +fetch_next: + pos = ftell(read->stream); + sync = ADTS_SyncFrame(bs, !read->is_remote, &hdr); + if (!sync) { + gf_bs_del(bs); + if (!read->dnload) { + *out_reception_status = GF_EOS; + read->done = 1; + } else { + fseek(read->stream, pos, SEEK_SET); + *out_reception_status = GF_OK; + } + return GF_OK; + } + + if (!hdr.frame_size) { + gf_bs_del(bs); + *out_reception_status = GF_EOS; + read->done = 1; + return GF_OK; + } + read->data_size = hdr.frame_size; + read->nb_samp = 1024; + /*we're seeking*/ + if (read->start_range && read->duration) { + start_from = (u32) (read->start_range * read->sample_rate); + if (read->current_time + read->nb_samp < start_from) { + read->current_time += read->nb_samp; + goto fetch_next; + } else { + read->start_range = 0; + } + } + + read->sl_hdr.compositionTimeStamp = read->current_time; + + read->data = malloc(sizeof(char) * (read->data_size+read->pad_bytes)); + gf_bs_read_data(bs, read->data, read->data_size); + if (read->pad_bytes) memset(read->data + read->data_size, 0, sizeof(char) * read->pad_bytes); + gf_bs_del(bs); + } + *out_sl_hdr = read->sl_hdr; + *out_data_ptr = read->data; + *out_data_size = read->data_size; + return GF_OK; +} + +static GF_Err AAC_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) +{ + AACReader *read = plug->priv; + + if (read->ch == channel) { + if (!read->data) return GF_BAD_PARAM; + free(read->data); + read->data = NULL; + read->current_time += read->nb_samp; + return GF_OK; + } + return GF_BAD_PARAM; +} + +GF_InputService *AAC_Load() +{ + AACReader *reader; + GF_InputService *plug = malloc(sizeof(GF_InputService)); + memset(plug, 0, sizeof(GF_InputService)); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC AAC Reader", "gpac distribution") + + plug->CanHandleURL = AAC_CanHandleURL; + plug->ConnectService = AAC_ConnectService; + plug->CloseService = AAC_CloseService; + plug->GetServiceDescriptor = AAC_GetServiceDesc; + plug->ConnectChannel = AAC_ConnectChannel; + plug->DisconnectChannel = AAC_DisconnectChannel; + plug->ServiceCommand = AAC_ServiceCommand; + /*we do support pull mode*/ + plug->ChannelGetSLP = AAC_ChannelGetSLP; + plug->ChannelReleaseSLP = AAC_ChannelReleaseSLP; + + reader = malloc(sizeof(AACReader)); + memset(reader, 0, sizeof(AACReader)); + plug->priv = reader; + return plug; +} + +void AAC_Delete(void *ifce) +{ + GF_InputService *plug = (GF_InputService *) ifce; + AACReader *read = plug->priv; + free(read); + free(plug); +} + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return 1; +#ifdef GPAC_HAS_FAAD + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return 1; +#endif + return 0; +} + +#ifdef GPAC_HAS_FAAD +GF_BaseDecoder *NewFAADDec(); +void DeleteFAADDec(GF_BaseDecoder *ifcg); +#endif + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return (GF_BaseInterface *)AAC_Load(); +#ifdef GPAC_HAS_FAAD + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return (GF_BaseInterface *)NewFAADDec(); +#endif + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { +#ifdef GPAC_HAS_FAAD + case GF_MEDIA_DECODER_INTERFACE: + DeleteFAADDec((GF_BaseDecoder *)ifce); + break; +#endif + case GF_NET_CLIENT_INTERFACE: + AAC_Delete(ifce); + break; + } +} diff --git a/modules/aac_in/aac_in.def b/modules/aac_in/aac_in.def new file mode 100644 index 0000000..39f84e3 --- /dev/null +++ b/modules/aac_in/aac_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_aac_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/aac_in/faad_dec.c b/modules/aac_in/faad_dec.c new file mode 100644 index 0000000..51f1a7d --- /dev/null +++ b/modules/aac_in/faad_dec.c @@ -0,0 +1,367 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / AAC reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef GPAC_HAS_FAAD + +#include <faad.h> + +#include <gpac/modules/codec.h> +#include <gpac/constants.h> +#include <gpac/avparse.h> + +typedef struct +{ + faacDecHandle codec; + faacDecFrameInfo info; + u32 sample_rate, out_size, num_samples; + u8 num_channels; + /*no support for scalability in FAAD yet*/ + u16 ES_ID; + Bool signal_mc; + Bool is_sbr; + + char ch_reorder[16]; +} FAADDec; + + +#define FAADCTX() FAADDec *ctx = (FAADDec *) ifcg->privateStack + +static GF_Err FAAD_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + GF_Err e; + GF_M4ADecSpecInfo a_cfg; + FAADCTX(); + + if (ctx->ES_ID && ctx->ES_ID!=esd->ESID) return GF_NOT_SUPPORTED; + if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->dataLength) return GF_NON_COMPLIANT_BITSTREAM; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[FAAD] Attaching stream %d\n", esd->ESID)); + + if (ctx->codec) faacDecClose(ctx->codec); + ctx->codec = faacDecOpen(); + if (!ctx->codec) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[FAAD] Error initializing decoder\n")); + return GF_IO_ERR; + } + + e = gf_m4a_get_config((unsigned char *) esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg); + if (e) return e; + if ( (s8) faacDecInit2(ctx->codec, (unsigned char *) esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, (u32 *) &ctx->sample_rate, (u8 *) &ctx->num_channels) < 0) + { + s8 res; + char *dsi; + u32 dsi_len; + switch (a_cfg.base_object_type) { + case GF_M4A_AAC_MAIN: + case GF_M4A_AAC_LC: + case GF_M4A_AAC_SSR: + case GF_M4A_AAC_LTP: + case GF_M4A_AAC_SBR: + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[FAAD] Error initializing stream %d\n", esd->ESID)); + return GF_NOT_SUPPORTED; + default: + break; + } + a_cfg.base_object_type = GF_M4A_AAC_LC; + a_cfg.has_sbr = 0; + a_cfg.nb_chan = 1; + + gf_m4a_write_config(&a_cfg, &dsi, &dsi_len); + res = faacDecInit2(ctx->codec, (unsigned char *) dsi, dsi_len, (u32 *) &ctx->sample_rate, (u8 *) &ctx->num_channels); + free(dsi); + if (res < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[FAAD] Error initializing stream %d\n", esd->ESID)); + return GF_NOT_SUPPORTED; + } + } + + ctx->is_sbr = a_cfg.has_sbr; + ctx->num_samples = 1024; + ctx->out_size = 2 * ctx->num_samples * ctx->num_channels; + ctx->ES_ID = esd->ESID; + ctx->signal_mc = ctx->num_channels>2 ? 1 : 0; + return GF_OK; +} + +static GF_Err FAAD_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + FAADCTX(); + if (ES_ID != ctx->ES_ID) return GF_BAD_PARAM; + if (ctx->codec) faacDecClose(ctx->codec); + ctx->codec = NULL; + ctx->ES_ID = 0; + ctx->sample_rate = ctx->out_size = ctx->num_samples = 0; + ctx->num_channels = 0; + return GF_OK; +} +static GF_Err FAAD_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + u32 i; + FAADCTX(); + switch (capability->CapCode) { + /*not tested yet*/ + case GF_CODEC_RESILIENT: + capability->cap.valueInt = 1; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_SAMPLERATE: + capability->cap.valueInt = ctx->sample_rate; + break; + case GF_CODEC_NB_CHAN: + capability->cap.valueInt = ctx->num_channels; + break; + case GF_CODEC_BITS_PER_SAMPLE: + capability->cap.valueInt = 16; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = 4; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = 12; + break; + /*by default AAC access unit lasts num_samples (timescale being sampleRate)*/ + case GF_CODEC_CU_DURATION: + capability->cap.valueInt = ctx->num_samples; + break; + /*to refine, it seems that 4 bytes padding is not enough on all streams ?*/ + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 8; + break; + case GF_CODEC_CHANNEL_CONFIG: + capability->cap.valueInt = 0; + for (i=0; i<ctx->num_channels; i++) { + switch (ctx->info.channel_position[i]) { + case FRONT_CHANNEL_CENTER: capability->cap.valueInt |= GF_AUDIO_CH_FRONT_CENTER; break; + case FRONT_CHANNEL_LEFT: capability->cap.valueInt |= GF_AUDIO_CH_FRONT_LEFT; break; + case FRONT_CHANNEL_RIGHT: capability->cap.valueInt |= GF_AUDIO_CH_FRONT_RIGHT; break; + case SIDE_CHANNEL_LEFT: capability->cap.valueInt |= GF_AUDIO_CH_SIDE_LEFT; break; + case SIDE_CHANNEL_RIGHT: capability->cap.valueInt |= GF_AUDIO_CH_SIDE_RIGHT; break; + case BACK_CHANNEL_LEFT: capability->cap.valueInt |= GF_AUDIO_CH_BACK_LEFT; break; + case BACK_CHANNEL_RIGHT: capability->cap.valueInt |= GF_AUDIO_CH_BACK_RIGHT; break; + case BACK_CHANNEL_CENTER: capability->cap.valueInt |= GF_AUDIO_CH_BACK_CENTER; break; + case LFE_CHANNEL: capability->cap.valueInt |= GF_AUDIO_CH_LFE; break; + default: break; + } + } + break; + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} +static GF_Err FAAD_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like SR changing ...) */ + return GF_NOT_SUPPORTED; +} + +static s8 FAAD_GetChannelPos(FAADDec *ffd, u32 ch_cfg) +{ + u32 i; + for (i=0; i<ffd->info.channels; i++) { + switch (ffd->info.channel_position[i]) { + case FRONT_CHANNEL_CENTER: if (ch_cfg==GF_AUDIO_CH_FRONT_CENTER) return i; break; + case FRONT_CHANNEL_LEFT: if (ch_cfg==GF_AUDIO_CH_FRONT_LEFT) return i; break; + case FRONT_CHANNEL_RIGHT: if (ch_cfg==GF_AUDIO_CH_FRONT_RIGHT) return i; break; + case SIDE_CHANNEL_LEFT: if (ch_cfg==GF_AUDIO_CH_SIDE_LEFT) return i; break; + case SIDE_CHANNEL_RIGHT: if (ch_cfg==GF_AUDIO_CH_SIDE_RIGHT) return i; break; + case BACK_CHANNEL_LEFT: if (ch_cfg==GF_AUDIO_CH_BACK_LEFT) return i; break; + case BACK_CHANNEL_RIGHT: if (ch_cfg==GF_AUDIO_CH_BACK_RIGHT) return i; break; + case BACK_CHANNEL_CENTER: if (ch_cfg==GF_AUDIO_CH_BACK_CENTER) return i; break; + case LFE_CHANNEL: if (ch_cfg==GF_AUDIO_CH_LFE) return i; break; + } + } + return -1; +} + +static GF_Err FAAD_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + void *buffer; + unsigned short *conv_in, *conv_out; + u32 i, j; + FAADCTX(); + + /*check not using scalabilty*/ + if (ctx->ES_ID != ES_ID) return GF_BAD_PARAM; + + /*if late or seeking don't decode*/ + switch (mmlevel) { + case GF_CODEC_LEVEL_SEEK: + case GF_CODEC_LEVEL_DROP: + *outBufferLength = 0; + return GF_OK; + default: + break; + } + + if (ctx->out_size > *outBufferLength) { + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; + } + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[FAAD] Decoding AU\n")); + buffer = faacDecDecode(ctx->codec, &ctx->info, inBuffer, inBufferLength); + if (ctx->info.error>0) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[FAAD] Error decoding AU %s\n", faacDecGetErrorMessage(ctx->info.error) )); + *outBufferLength = 0; + return GF_NON_COMPLIANT_BITSTREAM; + } + if (!ctx->info.samples || !buffer || !ctx->info.bytesconsumed) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[FAAD] empty/non complete AU\n")); + *outBufferLength = 0; + return GF_OK; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[FAAD] AU decoded\n")); + + /*FAAD froces us to decode a frame to get channel cfg*/ + if (ctx->signal_mc) { + s32 ch, idx; + ctx->signal_mc = 0; + idx = 0; + /*NOW WATCH OUT!! FAAD may very well decide to output more channels than indicated!!!*/ + ctx->num_channels = ctx->info.channels; + + /*get cfg*/ + ch = FAAD_GetChannelPos(ctx, GF_AUDIO_CH_FRONT_LEFT); + if (ch>=0) { ctx->ch_reorder[idx] = ch; idx++; } + ch = FAAD_GetChannelPos(ctx, GF_AUDIO_CH_FRONT_RIGHT); + if (ch>=0) { ctx->ch_reorder[idx] = ch; idx++; } + ch = FAAD_GetChannelPos(ctx, GF_AUDIO_CH_FRONT_CENTER); + if (ch>=0) { ctx->ch_reorder[idx] = ch; idx++; } + ch = FAAD_GetChannelPos(ctx, GF_AUDIO_CH_LFE); + if (ch>=0) { ctx->ch_reorder[idx] = ch; idx++; } + ch = FAAD_GetChannelPos(ctx, GF_AUDIO_CH_BACK_LEFT); + if (ch>=0) { ctx->ch_reorder[idx] = ch; idx++; } + ch = FAAD_GetChannelPos(ctx, GF_AUDIO_CH_BACK_RIGHT); + if (ch>=0) { ctx->ch_reorder[idx] = ch; idx++; } + ch = FAAD_GetChannelPos(ctx, GF_AUDIO_CH_BACK_CENTER); + if (ch>=0) { ctx->ch_reorder[idx] = ch; idx++; } + ch = FAAD_GetChannelPos(ctx, GF_AUDIO_CH_SIDE_LEFT); + if (ch>=0) { ctx->ch_reorder[idx] = ch; idx++; } + ch = FAAD_GetChannelPos(ctx, GF_AUDIO_CH_SIDE_RIGHT); + if (ch>=0) { ctx->ch_reorder[idx] = ch; idx++; } + + *outBufferLength = ctx->out_size; + if (sizeof(short) * ctx->info.samples > *outBufferLength) { + *outBufferLength = ctx->out_size = sizeof(short)*ctx->info.samples; + } + return GF_BUFFER_TOO_SMALL; + } + if (sizeof(short) * ctx->info.samples > *outBufferLength) { + *outBufferLength = sizeof(short)*ctx->info.samples; + return GF_BUFFER_TOO_SMALL; + } + + /*we assume left/right order*/ + if (ctx->num_channels<=2) { + memcpy(outBuffer, buffer, sizeof(short)* ctx->info.samples); + *outBufferLength = sizeof(short)*ctx->info.samples; + return GF_OK; + } + conv_in = (unsigned short *) buffer; + conv_out = (unsigned short *) outBuffer; + for (i=0; i<ctx->info.samples; i+=ctx->info.channels) { + for (j=0; j<ctx->info.channels; j++) { + conv_out[i + j] = conv_in[i + ctx->ch_reorder[j]]; + } + } + *outBufferLength = sizeof(short)*ctx->info.samples; + return GF_OK; +} + +static const char *FAAD_GetCodecName(GF_BaseDecoder *ifcg) +{ + FAADCTX(); + if (ctx->is_sbr) return "FAAD2 " FAAD2_VERSION " SBR mode"; + return "FAAD2 " FAAD2_VERSION; +} + +static Bool FAAD_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + GF_M4ADecSpecInfo a_cfg; + /*audio decs*/ + if (StreamType != GF_STREAM_AUDIO) return 0; + + switch (ObjectType) { + /*MPEG2 aac*/ + case 0x66: + case 0x67: + case 0x68: + /*MPEG4 aac*/ + case 0x40: + break; + /*cap query*/ + case 0: + return 1; + default: + return 0; + } + if (!decSpecInfoSize || !decSpecInfo) return 0; + if (gf_m4a_get_config((unsigned char *) decSpecInfo, decSpecInfoSize, &a_cfg) != GF_OK) return 0; + /*BSAC not supported*/ + if (a_cfg.base_object_type == GF_M4A_ER_BSAC) return 0; + return 1; + +} + +GF_BaseDecoder *NewFAADDec() +{ + GF_MediaDecoder *ifce; + FAADDec *dec; + + GF_SAFEALLOC(ifce, GF_MediaDecoder); + GF_SAFEALLOC(dec, FAADDec); + GF_REGISTER_MODULE_INTERFACE(ifce, GF_MEDIA_DECODER_INTERFACE, "FAAD2 Decoder", "gpac distribution") + + ifce->privateStack = dec; + + /*setup our own interface*/ + ifce->AttachStream = FAAD_AttachStream; + ifce->DetachStream = FAAD_DetachStream; + ifce->GetCapabilities = FAAD_GetCapabilities; + ifce->SetCapabilities = FAAD_SetCapabilities; + ifce->ProcessData = FAAD_ProcessData; + ifce->CanHandleStream = FAAD_CanHandleStream; + ifce->GetName = FAAD_GetCodecName; + return (GF_BaseDecoder *) ifce; +} + +void DeleteFAADDec(GF_BaseDecoder *ifcg) +{ + FAADCTX(); + if (ctx->codec) faacDecClose(ctx->codec); + free(ctx); + free(ifcg); +} + + +#endif + diff --git a/modules/ac3_in/Makefile b/modules/ac3_in/Makefile new file mode 100644 index 0000000..2ca5f88 --- /dev/null +++ b/modules/ac3_in/Makefile @@ -0,0 +1,76 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/ac3_in + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= ac3_in.o + +SRCS := $(OBJS:.o=.c) + +#faad config +ifeq ($(CONFIG_A52), no) +else +OBJS+=liba52_dec.o +CFLAGS+=-DGPAC_HAS_LIBA52 +#local faad lib +ifeq ($(CONFIG_FAAD), local) +EXTRALIBS+=-L../../extra_lib/lib/gcc +CFLAGS+= -I$(LOCAL_INC_PATH)/a52 +endif +EXTRALIBS+= -la52 +endif + +LIB=gm_ac3_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols ac3_in.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) -L../../bin/gcc -lgpac $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/ac3_in/ac3_in.c b/modules/ac3_in/ac3_in.c new file mode 100644 index 0000000..08bddb8 --- /dev/null +++ b/modules/ac3_in/ac3_in.c @@ -0,0 +1,664 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / AC3 reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/modules/service.h> +#include <gpac/modules/codec.h> +#include <gpac/avparse.h> +#include <gpac/constants.h> + +typedef struct +{ + GF_ClientService *service; + + Bool is_remote; + + FILE *stream; + u32 duration; + + Bool needs_connection; + u32 pad_bytes; + Bool done; + u32 is_inline; + LPNETCHANNEL ch; + + unsigned char *data; + u32 data_size; + GF_SLHeader sl_hdr; + + u32 sample_rate, nb_ch; + Double start_range, end_range; + u32 current_time, nb_samp; + /*file downloader*/ + GF_DownloadSession * dnload; + + Bool is_live; + char prev_data[1000]; + u32 prev_size; + + char *icy_name; + char *icy_genre; + char *icy_track_name; +} AC3Reader; + +static Bool AC3_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt; + sExt = strrchr(url, '.'); + if (!sExt) return 0; + if (gf_term_check_extension(plug, "audio/ac3", "ac3", "AC3 Music", sExt)) return 1; + if (gf_term_check_extension(plug, "audio/x-ac3", "ac3", "AC3 Music", sExt)) return 1; + return 0; +} + +static Bool ac3_is_local(const char *url) +{ + if (!strnicmp(url, "file://", 7)) return 1; + if (strstr(url, "://")) return 0; + return 1; +} + +static GF_ESD *AC3_GetESD(AC3Reader *read) +{ + GF_ESD *esd; + esd = gf_odf_desc_esd_new(0); + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + esd->decoderConfig->objectTypeIndication = 0xA5; + esd->ESID = 1; + esd->OCRESID = 1; + esd->slConfig->timestampResolution = read->sample_rate; + if (read->is_live) esd->slConfig->useAccessUnitEndFlag = esd->slConfig->useAccessUnitStartFlag = 1; + return esd; +} + +static void AC3_SetupObject(AC3Reader *read) +{ + GF_ESD *esd; + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1; + esd = AC3_GetESD(read); + esd->OCRESID = 0; + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(read->service, (GF_Descriptor*)od, 0); +} + + +static Bool AC3_ConfigureFromFile(AC3Reader *read) +{ + Bool sync; + GF_BitStream *bs; + GF_AC3Header hdr; + if (!read->stream) return 0; + bs = gf_bs_from_file(read->stream, GF_BITSTREAM_READ); + + + sync = gf_ac3_parser_bs(bs, &hdr, 1); + if (!sync) { + gf_bs_del(bs); + return 0; + } + read->nb_ch = hdr.channels; + read->sample_rate = hdr.sample_rate; + read->duration = 0; + + if (!read->is_remote) { + read->duration = 1536; + gf_bs_skip_bytes(bs, hdr.framesize); + while (gf_ac3_parser_bs(bs, &hdr, 0)) { + read->duration += 1536; + gf_bs_skip_bytes(bs, hdr.framesize); + } + } + gf_bs_del(bs); + fseek(read->stream, 0, SEEK_SET); + return 1; +} + +static void AC3_RegulateDataRate(AC3Reader *read) +{ + GF_NetworkCommand com; + + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.command_type = GF_NET_CHAN_BUFFER_QUERY; + com.base.on_channel = read->ch; + while (read->ch) { + gf_term_on_command(read->service, &com, GF_OK); + if (com.buffer.occupancy < com.buffer.max) break; + gf_sleep(2); + } +} + +static void AC3_OnLiveData(AC3Reader *read, char *data, u32 data_size) +{ + u32 pos; + Bool sync; + GF_BitStream *bs; + GF_AC3Header hdr; + + read->data = realloc(read->data, sizeof(char)*(read->data_size+data_size) ); + memcpy(read->data + read->data_size, data, sizeof(char)*data_size); + read->data_size += data_size; + + if (read->needs_connection) { + read->needs_connection = 0; + bs = gf_bs_new(read->data, read->data_size, GF_BITSTREAM_READ); + sync = gf_ac3_parser_bs(bs, &hdr, 1); + gf_bs_del(bs); + if (!sync) return; + read->nb_ch = hdr.channels; + read->sample_rate = hdr.sample_rate; + read->is_live = 1; + memset(&read->sl_hdr, 0, sizeof(GF_SLHeader)); + gf_term_on_connect(read->service, NULL, GF_OK); + AC3_SetupObject(read); + } + if (!read->ch) return; + + /*need a full ac3 header*/ + if (read->data_size<=7) return; + + bs = gf_bs_new(read->data, read->data_size, GF_BITSTREAM_READ); + hdr.framesize = pos = 0; + while (gf_ac3_parser_bs(bs, &hdr, 0)) { + pos = (u32) gf_bs_get_position(bs); + read->sl_hdr.accessUnitStartFlag = 1; + read->sl_hdr.accessUnitEndFlag = 1; + read->sl_hdr.AU_sequenceNumber++; + read->sl_hdr.compositionTimeStampFlag = 1; + read->sl_hdr.compositionTimeStamp += 1536; + gf_term_on_sl_packet(read->service, read->ch, read->data + pos, hdr.framesize, &read->sl_hdr, GF_OK); + gf_bs_skip_bytes(bs, hdr.framesize); + } + + pos = (u32) gf_bs_get_position(bs); + gf_bs_del(bs); + + if (pos) { + char *d; + read->data_size -= pos; + d = malloc(sizeof(char) * read->data_size); + memcpy(d, read->data + pos, sizeof(char) * read->data_size); + free(read->data); + read->data = d; + } + AC3_RegulateDataRate(read); +} + +void AC3_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + const char *szCache; + u32 total_size, bytes_done; + AC3Reader *read = (AC3Reader *) cbk; + + e = param->error; + /*done*/ + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + if (read->stream) { + read->is_remote = 0; + e = GF_EOS; + } else if (!read->needs_connection) { + return; + } + } else if (param->msg_type==GF_NETIO_PARSE_HEADER) { + if (!strcmp(param->name, "icy-name")) { + if (read->icy_name) free(read->icy_name); + read->icy_name = strdup(param->value); + } + if (!strcmp(param->name, "icy-genre")) { + if (read->icy_genre) free(read->icy_genre); + read->icy_genre = strdup(param->value); + } + if (!strcmp(param->name, "icy-meta")) { + GF_NetworkCommand com; + char *meta; + if (read->icy_track_name) free(read->icy_track_name); + read->icy_track_name = NULL; + meta = param->value; + while (meta && meta[0]) { + char *sep = strchr(meta, ';'); + if (sep) sep[0] = 0; + + if (!strnicmp(meta, "StreamTitle=", 12)) { + read->icy_track_name = strdup(meta+12); + } + if (!sep) break; + sep[0] = ';'; + meta = sep+1; + } + + com.base.command_type = GF_NET_SERVICE_INFO; + gf_term_on_command(read->service, &com, GF_OK); + } + return; + } else { + /*handle service message*/ + gf_term_download_update_stats(read->dnload); + if (param->msg_type!=GF_NETIO_DATA_EXCHANGE) return; + } + + /*data fetching or EOS*/ + if (e >= GF_OK) { + if (read->needs_connection) { + gf_dm_sess_get_stats(read->dnload, NULL, NULL, &total_size, NULL, NULL, NULL); + if (!total_size) read->is_live = 1; + } + if (read->is_live) { + if (!e) AC3_OnLiveData(read, param->data, param->size); + return; + } + if (read->stream) return; + + /*open service*/ + szCache = gf_dm_sess_get_cache_name(read->dnload); + if (!szCache) e = GF_IO_ERR; + else { + read->stream = fopen((char *) szCache, "rb"); + if (!read->stream) e = GF_SERVICE_ERROR; + else { + /*if full file at once (in cache) parse duration*/ + if (e==GF_EOS) read->is_remote = 0; + e = GF_OK; + /*not enough data*/ + if (!AC3_ConfigureFromFile(read)) { + /*get amount downloaded and check*/ + gf_dm_sess_get_stats(read->dnload, NULL, NULL, NULL, &bytes_done, NULL, NULL); + if (bytes_done>10*1024) { + e = GF_CORRUPTED_DATA; + } else { + fclose(read->stream); + read->stream = NULL; + return; + } + } + } + } + } + /*OK confirm*/ + if (read->needs_connection) { + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, e); + if (!e) AC3_SetupObject(read); + } +} + +void ac3_download_file(GF_InputService *plug, char *url) +{ + AC3Reader *read = (AC3Reader*) plug->priv; + + read->needs_connection = 1; + + read->dnload = gf_term_download_new(read->service, url, 0, AC3_NetIO, read); + if (!read->dnload ) { + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } + /*service confirm is done once fetched*/ +} + + +static GF_Err AC3_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + char szURL[2048]; + char *ext; + GF_Err reply; + AC3Reader *read = plug->priv; + read->service = serv; + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + strcpy(szURL, url); + ext = strrchr(szURL, '#'); + if (ext) ext[0] = 0; + + /*remote fetch*/ + read->is_remote = !ac3_is_local(szURL); + if (read->is_remote) { + ac3_download_file(plug, (char *) szURL); + return GF_OK; + } + + reply = GF_OK; + read->stream = fopen(szURL, "rb"); + if (!read->stream) { + reply = GF_URL_ERROR; + } else if (!AC3_ConfigureFromFile(read)) { + fclose(read->stream); + read->stream = NULL; + reply = GF_NOT_SUPPORTED; + } + gf_term_on_connect(serv, NULL, reply); + if (!reply && read->is_inline ) AC3_SetupObject(read); + return GF_OK; +} + +static GF_Err AC3_CloseService(GF_InputService *plug) +{ + AC3Reader *read = plug->priv; + if (read->stream) fclose(read->stream); + read->stream = NULL; + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + if (read->data) free(read->data); + read->data = NULL; + gf_term_on_disconnect(read->service, NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *AC3_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + AC3Reader *read = plug->priv; + /*since we don't handle multitrack in ac3, we don't need to check sub_url, only use expected type*/ + + /*override default*/ + if (expect_type==GF_MEDIA_OBJECT_UNDEF) expect_type=GF_MEDIA_OBJECT_AUDIO; + + /*audio object*/ + if (expect_type==GF_MEDIA_OBJECT_AUDIO) { + GF_ESD *esd; + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1; + esd = AC3_GetESD(read); + esd->OCRESID = 0; + gf_list_add(od->ESDescriptors, esd); + return (GF_Descriptor *) od; + } + read->is_inline = 1; + /*inline scene: no specific service*/ + return NULL; +} + +static GF_Err AC3_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ES_ID; + GF_Err e; + AC3Reader *read = plug->priv; + + e = GF_SERVICE_ERROR; + if (read->ch==channel) goto exit; + + e = GF_STREAM_NOT_FOUND; + if (strstr(url, "ES_ID")) { + sscanf(url, "ES_ID=%d", &ES_ID); + } + /*URL setup*/ + else if (!read->ch && AC3_CanHandleURL(plug, url)) ES_ID = 1; + + if (ES_ID==1) { + read->ch = channel; + e = GF_OK; + } + +exit: + gf_term_on_connect(read->service, channel, e); + return e; +} + +static GF_Err AC3_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + AC3Reader *read = plug->priv; + + GF_Err e = GF_STREAM_NOT_FOUND; + if (read->ch == channel) { + read->ch = NULL; + if (read->data) free(read->data); + read->data = NULL; + e = GF_OK; + } + gf_term_on_disconnect(read->service, channel, e); + return GF_OK; +} + +static GF_Err AC3_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + AC3Reader *read = plug->priv; + + + if (com->base.command_type==GF_NET_SERVICE_INFO) { + com->info.name = read->icy_track_name ? read->icy_track_name : read->icy_name; + com->info.comment = read->icy_genre; + return GF_OK; + } + + if (!com->base.on_channel) { + /*if live session we may cache*/ + if (read->is_live && (com->command_type==GF_NET_IS_CACHABLE)) return GF_OK; + return GF_NOT_SUPPORTED; + } + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: + if ((read->ch == com->base.on_channel) && read->is_live) return GF_NOT_SUPPORTED; + return GF_OK; + case GF_NET_CHAN_INTERACTIVE: + if ((read->ch == com->base.on_channel) && read->is_live) return GF_NOT_SUPPORTED; + return GF_OK; + case GF_NET_CHAN_BUFFER: + if ((read->ch == com->base.on_channel) && read->is_live) { + if (com->buffer.max<1000) com->buffer.max = 1000; + com->buffer.min = com->buffer.max/2; + } + return GF_OK; + case GF_NET_CHAN_SET_PADDING: + read->pad_bytes = com->pad.padding_bytes; + return GF_OK; + case GF_NET_CHAN_DURATION: + com->duration.duration = read->duration; + com->duration.duration /= read->sample_rate; + return GF_OK; + case GF_NET_CHAN_PLAY: + read->start_range = com->play.start_range; + read->end_range = com->play.end_range; + read->current_time = 0; + if (read->stream) fseek(read->stream, 0, SEEK_SET); + + if (read->ch == com->base.on_channel) { + read->done = 0; + /*PLAY after complete download, estimate duration*/ + if (!read->is_remote && !read->duration) { + AC3_ConfigureFromFile(read); + if (read->duration) { + GF_NetworkCommand rcfg; + rcfg.base.on_channel = read->ch; + rcfg.base.command_type = GF_NET_CHAN_DURATION; + rcfg.duration.duration = read->duration; + rcfg.duration.duration /= read->sample_rate; + gf_term_on_command(read->service, &rcfg, GF_OK); + } + } + } + return GF_OK; + case GF_NET_CHAN_STOP: + return GF_OK; + default: + return GF_OK; + } +} + + +static GF_Err AC3_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + u32 pos, start_from; + Bool sync; + GF_BitStream *bs; + GF_AC3Header hdr; + AC3Reader *read = plug->priv; + + *out_reception_status = GF_OK; + *sl_compressed = 0; + *is_new_data = 0; + + memset(&read->sl_hdr, 0, sizeof(GF_SLHeader)); + read->sl_hdr.randomAccessPointFlag = 1; + read->sl_hdr.compositionTimeStampFlag = 1; + + if (read->ch != channel) return GF_STREAM_NOT_FOUND; + + /*fetching es data*/ + if (read->done) { + *out_reception_status = GF_EOS; + return GF_OK; + } + + if (!read->data) { + if (!read->stream) { + *out_data_ptr = NULL; + *out_data_size = 0; + return GF_OK; + } + bs = gf_bs_from_file(read->stream, GF_BITSTREAM_READ); + *is_new_data = 1; + +fetch_next: + pos = ftell(read->stream); + sync = gf_ac3_parser_bs(bs, &hdr, 0); + if (!sync) { + gf_bs_del(bs); + if (!read->dnload) { + *out_reception_status = GF_EOS; + read->done = 1; + } else { + fseek(read->stream, pos, SEEK_SET); + *out_reception_status = GF_OK; + } + return GF_OK; + } + + if (!hdr.framesize) { + gf_bs_del(bs); + *out_reception_status = GF_EOS; + read->done = 1; + return GF_OK; + } + read->data_size = hdr.framesize; + read->nb_samp = 1536; + /*we're seeking*/ + if (read->start_range && read->duration) { + start_from = (u32) (read->start_range * read->sample_rate); + if (read->current_time + read->nb_samp < start_from) { + read->current_time += read->nb_samp; + goto fetch_next; + } else { + read->start_range = 0; + } + } + + read->sl_hdr.compositionTimeStamp = read->current_time; + + read->data = malloc(sizeof(char) * (read->data_size+read->pad_bytes)); + gf_bs_read_data(bs, read->data, read->data_size); + if (read->pad_bytes) memset(read->data + read->data_size, 0, sizeof(char) * read->pad_bytes); + gf_bs_del(bs); + } + *out_sl_hdr = read->sl_hdr; + *out_data_ptr = read->data; + *out_data_size = read->data_size; + return GF_OK; +} + +static GF_Err AC3_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) +{ + AC3Reader *read = plug->priv; + + if (read->ch == channel) { + if (!read->data) return GF_BAD_PARAM; + free(read->data); + read->data = NULL; + read->current_time += read->nb_samp; + return GF_OK; + } + return GF_BAD_PARAM; +} + +GF_InputService *AC3_Load() +{ + AC3Reader *reader; + GF_InputService *plug = malloc(sizeof(GF_InputService)); + memset(plug, 0, sizeof(GF_InputService)); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC AC3 Reader", "gpac distribution") + + plug->CanHandleURL = AC3_CanHandleURL; + plug->ConnectService = AC3_ConnectService; + plug->CloseService = AC3_CloseService; + plug->GetServiceDescriptor = AC3_GetServiceDesc; + plug->ConnectChannel = AC3_ConnectChannel; + plug->DisconnectChannel = AC3_DisconnectChannel; + plug->ServiceCommand = AC3_ServiceCommand; + /*we do support pull mode*/ + plug->ChannelGetSLP = AC3_ChannelGetSLP; + plug->ChannelReleaseSLP = AC3_ChannelReleaseSLP; + + reader = malloc(sizeof(AC3Reader)); + memset(reader, 0, sizeof(AC3Reader)); + plug->priv = reader; + return plug; +} + +void AC3_Delete(void *ifce) +{ + GF_InputService *plug = (GF_InputService *) ifce; + AC3Reader *read = plug->priv; + free(read); + free(plug); +} + +#ifdef GPAC_HAS_LIBA52 +GF_BaseDecoder *NewAC3Dec(); +void DeleteAC3Dec(GF_BaseDecoder *ifcg); +#endif + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return 1; +#ifdef GPAC_HAS_LIBA52 + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return 1; +#endif + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return (GF_BaseInterface *)AC3_Load(); +#ifdef GPAC_HAS_LIBA52 + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return (GF_BaseInterface *)NewAC3Dec(); +#endif + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { +#ifdef GPAC_HAS_LIBA52 + case GF_MEDIA_DECODER_INTERFACE: + DeleteAC3Dec((GF_BaseDecoder *)ifce); + break; +#endif + case GF_NET_CLIENT_INTERFACE: + AC3_Delete(ifce); + break; + } +} diff --git a/modules/ac3_in/ac3_in.def b/modules/ac3_in/ac3_in.def new file mode 100644 index 0000000..cba0550 --- /dev/null +++ b/modules/ac3_in/ac3_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_ac3_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/ac3_in/liba52_dec.c b/modules/ac3_in/liba52_dec.c new file mode 100644 index 0000000..7d356f9 --- /dev/null +++ b/modules/ac3_in/liba52_dec.c @@ -0,0 +1,339 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / AAC reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef GPAC_HAS_LIBA52 + +#include <gpac/modules/codec.h> +#include <gpac/constants.h> + +#ifndef uint32_t +#define uint32_t u32 +#endif +#ifndef uint8_t +#define uint8_t u8 +#endif + +#include <a52dec/mm_accel.h> +#include <a52dec/a52.h> + + +typedef struct +{ + a52_state_t *codec; + sample_t* samples; + + u32 sample_rate, num_samples, out_size, flags; + u8 num_channels; + /*no support for scalability in FAAD yet*/ + u16 ES_ID; + + char ch_reorder[16]; +} AC3Dec; + + +#define A52CTX() AC3Dec *ctx = (AC3Dec *) ifcg->privateStack + +static GF_Err AC3_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + A52CTX(); + + if (ctx->ES_ID && ctx->ES_ID!=esd->ESID) return GF_NOT_SUPPORTED; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[A52] Attaching stream %d\n", esd->ESID)); + + if (ctx->codec) a52_free(ctx->codec); + ctx->codec = a52_init(MM_ACCEL_DJBFFT); + if (!ctx->codec) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[A52] Error initializing decoder\n")); + return GF_IO_ERR; + } + ctx->samples = a52_samples(ctx->codec); + if (!ctx->samples) { + a52_free(ctx->codec); + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[A52] Error initializing decoder\n")); + return GF_IO_ERR; + } + + ctx->num_channels = 0; + ctx->sample_rate = 0; + ctx->out_size = 0; + ctx->num_samples = 1536; + ctx->ES_ID = esd->ESID; + return GF_OK; +} + +static GF_Err AC3_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + A52CTX(); + if (ES_ID != ctx->ES_ID) return GF_BAD_PARAM; + if (ctx->codec) a52_free(ctx->codec); + ctx->codec = NULL; + ctx->ES_ID = 0; + ctx->sample_rate = ctx->out_size = ctx->num_samples = 0; + ctx->num_channels = 0; + return GF_OK; +} +static GF_Err AC3_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + A52CTX(); + switch (capability->CapCode) { + /*not tested yet*/ + case GF_CODEC_RESILIENT: + capability->cap.valueInt = 1; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_SAMPLERATE: + capability->cap.valueInt = ctx->sample_rate; + break; + case GF_CODEC_NB_CHAN: + capability->cap.valueInt = ctx->num_channels; + break; + case GF_CODEC_BITS_PER_SAMPLE: + capability->cap.valueInt = 16; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = 4; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = 12; + break; + /*by default AAC access unit lasts num_samples (timescale being sampleRate)*/ + case GF_CODEC_CU_DURATION: + capability->cap.valueInt = ctx->num_samples; + break; + /*to refine, it seems that 4 bytes padding is not enough on all streams ?*/ + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 8; + break; + case GF_CODEC_CHANNEL_CONFIG: + capability->cap.valueInt = 0; + switch (ctx->flags & A52_CHANNEL_MASK) { + case A52_CHANNEL1: + case A52_CHANNEL2: + case A52_MONO: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER; + break; + case A52_CHANNEL: + case A52_STEREO: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT; + break; + case A52_DOLBY: + break; + case A52_3F: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER | GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT; + break; + case A52_2F1R: + capability->cap.valueInt = GF_AUDIO_CH_BACK_CENTER | GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT; + break; + case A52_3F1R: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER | GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_BACK_CENTER; + break; + case A52_2F2R: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_BACK_LEFT | GF_AUDIO_CH_BACK_RIGHT; + break; + case A52_3F2R: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER | GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_BACK_LEFT | GF_AUDIO_CH_BACK_RIGHT; + break; + } + if (ctx->flags & A52_LFE) + capability->cap.valueInt |= GF_AUDIO_CH_LFE; + break; + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} +static GF_Err AC3_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like SR changing ...) */ + return GF_NOT_SUPPORTED; +} + +#if 0 +static s8 AC3_GetChannelPos(AC3Dec *ffd, u32 ch_cfg) +{ + u32 i; + for (i=0; i<ffd->info.channels; i++) { + switch (ffd->info.channel_position[i]) { + case FRONT_CHANNEL_CENTER: if (ch_cfg==GF_AUDIO_CH_FRONT_CENTER) return i; break; + case FRONT_CHANNEL_LEFT: if (ch_cfg==GF_AUDIO_CH_FRONT_LEFT) return i; break; + case FRONT_CHANNEL_RIGHT: if (ch_cfg==GF_AUDIO_CH_FRONT_RIGHT) return i; break; + case SIDE_CHANNEL_LEFT: if (ch_cfg==GF_AUDIO_CH_SIDE_LEFT) return i; break; + case SIDE_CHANNEL_RIGHT: if (ch_cfg==GF_AUDIO_CH_SIDE_RIGHT) return i; break; + case BACK_CHANNEL_LEFT: if (ch_cfg==GF_AUDIO_CH_BACK_LEFT) return i; break; + case BACK_CHANNEL_RIGHT: if (ch_cfg==GF_AUDIO_CH_BACK_RIGHT) return i; break; + case BACK_CHANNEL_CENTER: if (ch_cfg==GF_AUDIO_CH_BACK_CENTER) return i; break; + case LFE_CHANNEL: if (ch_cfg==GF_AUDIO_CH_LFE) return i; break; + } + } + return -1; +} +#endif + +/**** the following two functions comes from a52dec */ +static GFINLINE s32 blah (s32 i) +{ + if (i > 0x43c07fff) + return 32767; + else if (i < 0x43bf8000) + return -32768; + return i - 0x43c00000; +} + +static GFINLINE void float_to_int (float * _f, s16 *samps, int nchannels) +{ + int i, j, c; + s32 * f = (s32 *) _f; // XXX assumes IEEE float format + + j = 0; + nchannels *= 256; + for (i = 0; i < 256; i++) { + for (c = 0; c < nchannels; c += 256) + samps[j++] = blah (f[i + c]); + } +} + +/**** end */ + +static const int ac3_channels[8] = { + 2, 1, 2, 3, 3, 4, 4, 5 +}; + +static GF_Err AC3_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + short *out_samples; + int i, len, bit_rate; + sample_t level; + A52CTX(); + + /*check not using scalabilty*/ + if (ctx->ES_ID != ES_ID) return GF_BAD_PARAM; + + /*if late or seeking don't decode*/ + switch (mmlevel) { + case GF_CODEC_LEVEL_SEEK: + case GF_CODEC_LEVEL_DROP: + *outBufferLength = 0; + return GF_OK; + default: + break; + } + + if (ctx->out_size > *outBufferLength) { + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; + } + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[A52] Decoding AU\n")); + + len = a52_syncinfo(inBuffer, &ctx->flags, &ctx->sample_rate, &bit_rate); + if (!len) return GF_NON_COMPLIANT_BITSTREAM; + + /*init decoder*/ + if (!ctx->out_size) { + ctx->num_channels = ac3_channels[ctx->flags & 7]; + if (ctx->flags & A52_LFE) ctx->num_channels++; + ctx->flags |= A52_ADJUST_LEVEL; + + ctx->out_size = ctx->num_channels * sizeof(short) * 1536; + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; + } + + level = 1; + if ( a52_frame(ctx->codec, inBuffer, &ctx->flags, &level, 384)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[A52] Error decoding AU\n" )); + *outBufferLength = 0; + return GF_NON_COMPLIANT_BITSTREAM; + } + + out_samples = (short*)outBuffer; + for (i=0; i<6; i++) { + if (a52_block(ctx->codec)) + return GF_NON_COMPLIANT_BITSTREAM; + + float_to_int(ctx->samples, out_samples + i * 256 * ctx->num_channels, ctx->num_channels); + } + + *outBufferLength = 6 * ctx->num_channels * 256 * sizeof(short); + + return GF_OK; +} + +static const char *AC3_GetCodecName(GF_BaseDecoder *ifcg) +{ + return "LIBA52 AC3 Decoder"; +} + +static Bool AC3_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + /*audio decs*/ + if (StreamType != GF_STREAM_AUDIO) return 0; + switch (ObjectType) { + case 0xA5: + case 0: + return 1; + } + return 0; +} + +GF_BaseDecoder *NewAC3Dec() +{ + GF_MediaDecoder *ifce; + AC3Dec *dec; + + GF_SAFEALLOC(ifce, GF_MediaDecoder); + GF_SAFEALLOC(dec, AC3Dec); + GF_REGISTER_MODULE_INTERFACE(ifce, GF_MEDIA_DECODER_INTERFACE, "LIBA52 AC3 Decoder", "gpac distribution") + + ifce->privateStack = dec; + + /*setup our own interface*/ + ifce->AttachStream = AC3_AttachStream; + ifce->DetachStream = AC3_DetachStream; + ifce->GetCapabilities = AC3_GetCapabilities; + ifce->SetCapabilities = AC3_SetCapabilities; + ifce->ProcessData = AC3_ProcessData; + ifce->CanHandleStream = AC3_CanHandleStream; + ifce->GetName = AC3_GetCodecName; + return (GF_BaseDecoder *) ifce; +} + +void DeleteAC3Dec(GF_BaseDecoder *ifcg) +{ + A52CTX(); + if (ctx->codec) a52_free(ctx->codec); + free(ctx); + free(ifcg); +} + + +#endif diff --git a/modules/alsa/Makefile b/modules/alsa/Makefile new file mode 100644 index 0000000..665579a --- /dev/null +++ b/modules/alsa/Makefile @@ -0,0 +1,60 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/alsa + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include $(OSS_CFLAGS) +LDFLAGS+=$(OSS_LDFLAGS) + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= alsa.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_alsa.$(DYN_LIB_SUFFIX) + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac -lasound + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif + diff --git a/modules/alsa/alsa.c b/modules/alsa/alsa.c new file mode 100644 index 0000000..70221e2 --- /dev/null +++ b/modules/alsa/alsa.c @@ -0,0 +1,371 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / alsa audio output module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <poll.h> +#include <alsa/asoundlib.h> +#include <gpac/modules/audio_out.h> + + +typedef struct +{ + snd_pcm_t *playback_handle; + u32 nb_ch, buf_size, delay, num_buffers, total_duration, block_align; + u32 force_sr; + const char *dev_name; + char *wav_buf; +} ALSAContext; + + +static GF_Err ALSA_Setup(GF_AudioOutput*dr, void *os_handle, u32 num_buffers, u32 total_duration) +{ + int err; + const char *opt; + ALSAContext *ctx = (ALSAContext*)dr->opaque; + + + opt = gf_modules_get_option((GF_BaseInterface *)dr, "ALSA", "ForceSampleRate"); + if (opt) ctx->force_sr = atoi(opt); + ctx->dev_name = gf_modules_get_option((GF_BaseInterface *)dr, "ALSA", "DeviceName"); + if (!ctx->dev_name) { + ctx->dev_name = "hw:0,0"; + gf_modules_set_option((GF_BaseInterface *)dr, "ALSA", "DeviceName", ctx->dev_name); + } + + /*test device*/ + err = snd_pcm_open(&ctx->playback_handle, ctx->dev_name, SND_PCM_STREAM_PLAYBACK, 0); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot open audio device %s: %s\n", ctx->dev_name, snd_strerror (err)) ); + return GF_IO_ERR; + } + ctx->num_buffers = num_buffers ? num_buffers : 2; + ctx->total_duration = total_duration ? total_duration : 100; + return GF_OK; +} + +static void ALSA_Shutdown(GF_AudioOutput*dr) +{ + ALSAContext *ctx = (ALSAContext*)dr->opaque; + if (ctx->playback_handle) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[ALSA] Closing alsa output\n") ); + snd_pcm_close(ctx->playback_handle); + ctx->playback_handle = NULL; + } + if (ctx->wav_buf) free(ctx->wav_buf); + ctx->wav_buf = NULL; +} + +static GF_Err ALSA_ConfigureOutput(GF_AudioOutput*dr, u32 *SampleRate, u32 *NbChannels, u32 *nbBitsPerSample, u32 channel_cfg) +{ + snd_pcm_hw_params_t *hw_params = NULL; + int err; + int nb_bufs, sr, period_time; + ALSAContext *ctx = (ALSAContext*)dr->opaque; + + if (!ctx) return GF_BAD_PARAM; + + /*close device*/ + if (ctx->playback_handle) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[ALSA] Closing audio device %s\n", ctx->dev_name )); + snd_pcm_close(ctx->playback_handle); + ctx->playback_handle = NULL; + } + if (ctx->wav_buf) free(ctx->wav_buf); + ctx->wav_buf = NULL; + + err = snd_pcm_open(&ctx->playback_handle, ctx->dev_name, SND_PCM_STREAM_PLAYBACK, 0/*SND_PCM_NONBLOCK*/); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot open audio device %s: %s\n", ctx->dev_name, snd_strerror (err)) ); + return GF_IO_ERR; + } + + err = snd_pcm_hw_params_malloc(&hw_params); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot allocate hardware params: %s\n", snd_strerror (err)) ); + goto err_exit; + } + err = snd_pcm_hw_params_any(ctx->playback_handle, hw_params); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot initialize hardware params: %s\n", snd_strerror (err)) ); + goto err_exit; + } + err = snd_pcm_hw_params_set_access(ctx->playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot set access type: %s\n", snd_strerror (err)) ); + goto err_exit; + } + /*set output format*/ + ctx->nb_ch = (int) (*NbChannels); + ctx->block_align = ctx->nb_ch; + if ((*nbBitsPerSample) == 16) { + ctx->block_align *= 2; + err = snd_pcm_hw_params_set_format(ctx->playback_handle, hw_params, SND_PCM_FORMAT_S16_LE); + } else { + err = snd_pcm_hw_params_set_format(ctx->playback_handle, hw_params, SND_PCM_FORMAT_U8); + } + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot set sample format: %s\n", snd_strerror (err)) ); + goto err_exit; + } + + /*set output sample rate*/ + if (ctx->force_sr) *SampleRate = ctx->force_sr; + sr = *SampleRate; + err = snd_pcm_hw_params_set_rate_near(ctx->playback_handle, hw_params, SampleRate, 0); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot set sample rate: %s\n", snd_strerror (err)) ); + goto err_exit; + } + if (sr != *SampleRate) { + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[ALSA] Sample rate %d not supported, using %d instead\n", sr, *SampleRate ) ); + sr = *SampleRate; + } + /*set output channels*/ + err = snd_pcm_hw_params_set_channels_near(ctx->playback_handle, hw_params, NbChannels); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot set channel count: %s\n", snd_strerror (err)) ); + goto err_exit; + } + if (ctx->nb_ch != *NbChannels) { + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[ALSA] %d channels not supported - using %d instead\n", ctx->nb_ch, *NbChannels ) ); + ctx->block_align /= ctx->nb_ch; + ctx->nb_ch = *NbChannels; + ctx->block_align *= ctx->nb_ch; + } + /* Set number of buffers*/ + nb_bufs = ctx->num_buffers; + err = snd_pcm_hw_params_set_periods_near(ctx->playback_handle, hw_params, &nb_bufs, 0); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot set number of HW buffers (%d): %s\n", nb_bufs, snd_strerror(err) )); + goto err_exit; + } + /* Set total buffer size*/ + ctx->buf_size = (sr * ctx->total_duration)/1000 / nb_bufs; + err = snd_pcm_hw_params_set_period_size_near(ctx->playback_handle, hw_params, (snd_pcm_uframes_t *)&ctx->buf_size, 0); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot set HW buffer size (%d): %s\n", ctx->buf_size, snd_strerror(err) )); + goto err_exit; + } + + err = snd_pcm_hw_params_get_buffer_size(hw_params, (snd_pcm_uframes_t *)&ctx->buf_size); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot get HW buffer size (%d): %s\n", ctx->buf_size, snd_strerror(err) )); + goto err_exit; + } + ctx->buf_size *= ctx->block_align; + /*get period time*/ + snd_pcm_hw_params_get_period_time(hw_params, &period_time, 0); + + err = snd_pcm_hw_params(ctx->playback_handle, hw_params); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot set parameters: %s\n", snd_strerror (err)) ); + goto err_exit; + } + snd_pcm_hw_params_free (hw_params); + hw_params = NULL; + + ctx->delay = (ctx->buf_size*1000) / (sr*ctx->block_align); + + /*allocate a single buffer*/ + ctx->wav_buf = malloc(ctx->buf_size*sizeof(char)); + if(!ctx->wav_buf) return GF_OUT_OF_MEM; + memset(ctx->wav_buf, 0, ctx->buf_size*sizeof(char)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[ALSA] Setup %d ch @ %d hz - %d periods of %d us - total buffer size %d - overall delay %d ms\n", ctx->nb_ch, sr, nb_bufs, period_time, ctx->buf_size, ctx->delay)); + + return GF_OK; + +err_exit: + if (hw_params) snd_pcm_hw_params_free(hw_params); + snd_pcm_close(ctx->playback_handle); + ctx->playback_handle = NULL; + return GF_IO_ERR; +} + +static void ALSA_WriteAudio(GF_AudioOutput*dr) +{ + u32 written; + snd_pcm_sframes_t nb_frames; + int err; + ALSAContext *ctx = (ALSAContext*)dr->opaque; + + /*wait ctx delay for device interrupt*/ + err = snd_pcm_wait(ctx->playback_handle, 1); + if (err<0) { + if (err == -EPIPE) { + GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[ALSA] Broken connection to sound card - restoring!\n")); + snd_pcm_prepare(ctx->playback_handle); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] error %s while waiting!\n", snd_strerror(err) )); + return; + } + } + + nb_frames = snd_pcm_avail_update(ctx->playback_handle); + if (nb_frames < 0) { + if (nb_frames == -EPIPE) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] an xrun occured!\n")); + snd_pcm_prepare(ctx->playback_handle); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] unknown ALSA avail update return value (%d)\n", nb_frames)); + } + return; + } + if (!nb_frames) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[ALSA] no frame to write\n" )); + return; + } + + //assert(nb_frames*ctx->block_align<=ctx->buf_size); + written = dr->FillBuffer(dr->audio_renderer, ctx->wav_buf, (u32) (ctx->block_align*nb_frames) ); + if (!written) return; + written /= ctx->block_align; + + err = snd_pcm_writei(ctx->playback_handle, ctx->wav_buf, written); + if (err == -EPIPE ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] an xrun occured!\n")); + snd_pcm_prepare(ctx->playback_handle); + err = snd_pcm_writei(ctx->playback_handle, ctx->wav_buf, nb_frames); + } + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Write failure: %s\n", snd_strerror(err))); + } +} + +static void ALSA_SetVolume(GF_AudioOutput*dr, u32 Volume) +{ +} + +static void ALSA_SetPan(GF_AudioOutput*dr, u32 Pan) +{ +} + +static void ALSA_SetPriority(GF_AudioOutput*dr, u32 Priority) +{ +} + +static u32 ALSA_GetAudioDelay(GF_AudioOutput*dr) +{ + ALSAContext *ctx = (ALSAContext*)dr->opaque; + return ctx->delay; +} + +static GF_Err ALSA_QueryOutputSampleRate(GF_AudioOutput*dr, u32 *desired_sr, u32 *NbChannels, u32 *nbBitsPerSample) +{ + ALSAContext *ctx = (ALSAContext*)dr->opaque; + int err; + snd_pcm_hw_params_t *hw_params = NULL; + + err = snd_pcm_hw_params_malloc(&hw_params); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot allocate hardware params: %s\n", snd_strerror (err)) ); + goto err_exit; + } + err = snd_pcm_hw_params_any(ctx->playback_handle, hw_params); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot initialize hardware params: %s\n", snd_strerror (err)) ); + goto err_exit; + } + + err = snd_pcm_hw_params_set_rate_near(ctx->playback_handle, hw_params, desired_sr, 0); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot check available sample rates: %s\n", snd_strerror (err)) ); + goto err_exit; + } + + err = snd_pcm_hw_params_set_channels_near(ctx->playback_handle, hw_params, NbChannels); + if (err < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[ALSA] Cannot check available channels: %s\n", snd_strerror (err)) ); + goto err_exit; + } + snd_pcm_hw_params_free (hw_params); + hw_params = NULL; + return GF_OK; +err_exit: + snd_pcm_hw_params_free (hw_params); + hw_params = NULL; + return GF_IO_ERR; +} + +void *NewALSAOutput() +{ + ALSAContext *ctx; + GF_AudioOutput*driv; + GF_SAFEALLOC(ctx, ALSAContext); + if(!ctx) return NULL; + + GF_SAFEALLOC(driv, GF_AudioOutput); + if(!driv) { + free(ctx); + return NULL; + } + driv->opaque = ctx; + driv->SelfThreaded = 0; + driv->Setup = ALSA_Setup; + driv->Shutdown = ALSA_Shutdown; + driv->ConfigureOutput = ALSA_ConfigureOutput; + driv->GetAudioDelay = ALSA_GetAudioDelay; + driv->SetVolume = ALSA_SetVolume; + driv->SetPan = ALSA_SetPan; + driv->SetPriority = ALSA_SetPriority; + driv->QueryOutputSampleRate = ALSA_QueryOutputSampleRate; + driv->WriteAudio = ALSA_WriteAudio; + GF_REGISTER_MODULE_INTERFACE(driv, GF_AUDIO_OUTPUT_INTERFACE, "ALSA Audio Output", "gpac distribution"); + return driv; +} + +void DeleteALSAOutput(void *ifce) +{ + GF_AudioOutput*dr = (GF_AudioOutput*) ifce; + ALSAContext *ctx = (ALSAContext *)dr->opaque; + free(ctx); + free(dr); +} + + +/* + * ******************************************************************** + * interface + */ +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) + return 1; + return 0; +} + +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) + return NewALSAOutput(); + return NULL; +} + +void ShutdownInterface(GF_BaseInterface *ifce) +{ + if (ifce->InterfaceType==GF_AUDIO_OUTPUT_INTERFACE) + DeleteALSAOutput((GF_AudioOutput*)ifce); +} diff --git a/modules/amr_dec/Makefile b/modules/amr_dec/Makefile new file mode 100644 index 0000000..e6121bc --- /dev/null +++ b/modules/amr_dec/Makefile @@ -0,0 +1,88 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/amr_dec + +#note: __MSDOS__ defined is the same cfg as linux. There may be some pbs on other platforms, to check... +CFLAGS= $(OPTFLAGS) -w -I$(SRC_PATH)/include -D__MSDOS__ -DMMS_IO -DWMOPS=0 -DVAD1 + + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g -DDEBUG +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS=amr_in.o amr_dec.o \ + ./amr_nb/agc.o ./amr_nb/autocorr.o ./amr_nb/az_lsp.o ./amr_nb/bits2prm.o ./amr_nb/cl_ltp.o \ + ./amr_nb/convolve.o ./amr_nb/c1035pf.o ./amr_nb/d_plsf.o ./amr_nb/d_plsf_5.o ./amr_nb/d_gain_c.o \ + ./amr_nb/d_gain_p.o ./amr_nb/dec_lag6.o ./amr_nb/d1035pf.o ./amr_nb/cor_h.o ./amr_nb/enc_lag3.o \ + ./amr_nb/enc_lag6.o ./amr_nb/g_code.o ./amr_nb/g_pitch.o ./amr_nb/int_lpc.o ./amr_nb/inter_36.o \ + ./amr_nb/inv_sqrt.o ./amr_nb/lag_wind.o ./amr_nb/levinson.o ./amr_nb/lsp_az.o ./amr_nb/lsp_lsf.o \ + ./amr_nb/ol_ltp.o ./amr_nb/pitch_fr.o ./amr_nb/pitch_ol.o ./amr_nb/pow2.o ./amr_nb/pre_big.o \ + ./amr_nb/pre_proc.o ./amr_nb/pred_lt.o ./amr_nb/preemph.o ./amr_nb/prm2bits.o ./amr_nb/pstfilt.o \ + ./amr_nb/q_gain_c.o ./amr_nb/q_gain_p.o ./amr_nb/q_plsf.o ./amr_nb/q_plsf_5.o ./amr_nb/lsfwt.o \ + ./amr_nb/reorder.o ./amr_nb/residu.o ./amr_nb/lsp.o ./amr_nb/lpc.o ./amr_nb/ec_gains.o \ + ./amr_nb/spreproc.o ./amr_nb/syn_filt.o ./amr_nb/weight_a.o ./amr_nb/qua_gain.o \ + ./amr_nb/gc_pred.o ./amr_nb/q_plsf_3.o ./amr_nb/post_pro.o ./amr_nb/dec_lag3.o ./amr_nb/dec_gain.o \ + ./amr_nb/d_plsf_3.o ./amr_nb/d4_17pf.o ./amr_nb/c4_17pf.o ./amr_nb/d3_14pf.o ./amr_nb/c3_14pf.o \ + ./amr_nb/d2_11pf.o ./amr_nb/c2_11pf.o ./amr_nb/d2_9pf.o ./amr_nb/c2_9pf.o ./amr_nb/cbsearch.o \ + ./amr_nb/spstproc.o ./amr_nb/gain_q.o ./amr_nb/cod_amr.o ./amr_nb/dec_amr.o ./amr_nb/sp_enc.o \ + ./amr_nb/sp_dec.o ./amr_nb/ph_disp.o ./amr_nb/g_adapt.o ./amr_nb/calc_en.o ./amr_nb/qgain795.o \ + ./amr_nb/qgain475.o ./amr_nb/sqrt_l.o ./amr_nb/set_sign.o ./amr_nb/s10_8pf.o ./amr_nb/bgnscd.o \ + ./amr_nb/gmed_n.o ./amr_nb/mac_32.o ./amr_nb/ex_ctrl.o ./amr_nb/c_g_aver.o ./amr_nb/lsp_avg.o \ + ./amr_nb/int_lsf.o ./amr_nb/c8_31pf.o ./amr_nb/d8_31pf.o ./amr_nb/p_ol_wgh.o ./amr_nb/ton_stab.o \ + ./amr_nb/vad1.o ./amr_nb/dtx_enc.o ./amr_nb/dtx_dec.o ./amr_nb/a_refl.o ./amr_nb/b_cn_cod.o \ + ./amr_nb/calc_cor.o ./amr_nb/hp_max.o ./amr_nb/vadname.o ./amr_nb/vad2.o ./amr_nb/r_fft.o \ + ./amr_nb/lflg_upd.o ./amr_nb/e_homing.o ./amr_nb/d_homing.o ./amr_nb/basicop2.o \ + ./amr_nb/count.o ./amr_nb/oper_32b.o ./amr_nb/copy.o ./amr_nb/log2.o \ + ./amr_nb/set_zero.o ./amr_nb/strfunc.o ./amr_nb/n_proc.o ./amr_nb/sid_sync.o + + +SRCS := $(OBJS:.o=.c) + +LIB=gm_amr_dec.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols amr_dec.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJS) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/amr_dec/amr_dec.c b/modules/amr_dec/amr_dec.c new file mode 100644 index 0000000..4881a63 --- /dev/null +++ b/modules/amr_dec/amr_dec.c @@ -0,0 +1,305 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / AMR decoder module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/*include AMR stuff*/ +#include "amr_nb/sp_dec.h" +#include "amr_nb/d_homing.h" +/*remove AMR types to avoid any typedef warning/error*/ +#undef Float +#undef Bool + +/*decoder Interface*/ +#include <gpac/modules/codec.h> +#include <gpac/modules/service.h> +#include <gpac/constants.h> + + +/*default size in CU of composition memory for audio*/ +#define DEFAULT_AUDIO_CM_SIZE 12 +/*default critical size in CU of composition memory for audio*/ +#define DEFAULT_AUDIO_CM_TRIGGER 4 + +typedef struct +{ + u32 out_size; + /*AMR NB state vars*/ + Speech_Decode_FrameState * speech_decoder_state; + enum RXFrameType rx_type; + enum Mode mode; + + s16 reset_flag, reset_flag_old; + +} AMRDec; + +#define AMRCTX() AMRDec *ctx = (AMRDec *) ifcg->privateStack + + +static GF_Err AMR_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + u32 packed_size; + AMRCTX(); + if (esd->dependsOnESID || !esd->decoderConfig->decoderSpecificInfo) return GF_NOT_SUPPORTED; + + if (strnicmp(esd->decoderConfig->decoderSpecificInfo->data, "samr", 4) + && strnicmp(esd->decoderConfig->decoderSpecificInfo->data, "amr ", 4)) return GF_NOT_SUPPORTED; + + ctx->reset_flag = 0; + ctx->reset_flag_old = 1; + ctx->mode = 0; + ctx->rx_type = 0; + ctx->speech_decoder_state = NULL; + if (Speech_Decode_Frame_init(&ctx->speech_decoder_state, "Decoder")) return GF_IO_ERR; + + packed_size = (u32) (esd->decoderConfig->decoderSpecificInfo->dataLength>14) ? esd->decoderConfig->decoderSpecificInfo->data[13] : 0; + /*max possible frames in a sample are seen in MP4, that's 15*/ + if (!packed_size) packed_size = 15; + + ctx->out_size = packed_size * 2 * 160; + return GF_OK; +} + +static GF_Err AMR_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + AMRCTX(); + Speech_Decode_Frame_exit(&ctx->speech_decoder_state); + ctx->speech_decoder_state = NULL; + return GF_OK; +} +static GF_Err AMR_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + AMRCTX(); + switch (capability->CapCode) { + /*not tested yet*/ + case GF_CODEC_RESILIENT: + capability->cap.valueInt = 1; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_SAMPLERATE: + capability->cap.valueInt = 8000; + break; + case GF_CODEC_NB_CHAN: + capability->cap.valueInt = 1; + break; + case GF_CODEC_BITS_PER_SAMPLE: + capability->cap.valueInt = 16; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = DEFAULT_AUDIO_CM_TRIGGER; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = DEFAULT_AUDIO_CM_SIZE; + break; + case GF_CODEC_CU_DURATION: + capability->cap.valueInt = 160; + break; + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 4; + break; + case GF_CODEC_CHANNEL_CONFIG: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER; + break; + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} + +static GF_Err AMR_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like SR changing ...) */ + return GF_NOT_SUPPORTED; +} + + +/* frame size in serial bitstream file (frame type + serial stream + flags) */ +#define SERIAL_FRAMESIZE (270) + +static GF_Err AMR_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + u32 offset = 0; + u8 toc, q, ft; + s16 *synth; + u8 *packed_bits; + s16 serial[SERIAL_FRAMESIZE]; + s32 i; + AMRCTX(); + + /*if late or seeking don't decode (each frame is a RAP)*/ + /* switch (mmlevel) { + case GF_CODEC_LEVEL_SEEK: + case GF_CODEC_LEVEL_DROP: + *outBufferLength = 0; + return GF_OK; + default: + break; + } + */ + if (ctx->out_size > *outBufferLength) { + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; + } + + synth = (s16 *) outBuffer; + *outBufferLength = 0; + + while (inBufferLength) { + toc = inBuffer[0]; + /* read rest of the frame based on ToC byte */ + q = (toc >> 2) & 0x01; + ft = (toc >> 3) & 0x0F; + + packed_bits = inBuffer + 1; + offset = 1 + GF_AMR_FRAME_SIZE[ft]; + + /*Unsort and unpack bits*/ + ctx->rx_type = UnpackBits(q, ft, packed_bits, &ctx->mode, &serial[1]); + + if (ctx->rx_type == RX_NO_DATA) { + ctx->mode = ctx->speech_decoder_state->prev_mode; + } else { + ctx->speech_decoder_state->prev_mode = ctx->mode; + } + + /* if homed: check if this frame is another homing frame */ + if (ctx->reset_flag_old == 1) { + /* only check until end of first subframe */ + ctx->reset_flag = decoder_homing_frame_test_first(&serial[1], ctx->mode); + } + /* produce encoder homing frame if homed & input=decoder homing frame */ + if ((ctx->reset_flag != 0) && (ctx->reset_flag_old != 0)) { + for (i = 0; i < L_FRAME; i++) { + synth[i] = EHF_MASK; + } + } else { + /* decode frame */ + Speech_Decode_Frame(ctx->speech_decoder_state, ctx->mode, &serial[1], ctx->rx_type, synth); + } + + *outBufferLength += 160*2; + synth += 160; + + /* if not homed: check whether current frame is a homing frame */ + if (ctx->reset_flag_old == 0) { + ctx->reset_flag = decoder_homing_frame_test(&serial[1], ctx->mode); + } + /* reset decoder if current frame is a homing frame */ + if (ctx->reset_flag != 0) { + Speech_Decode_Frame_reset(ctx->speech_decoder_state); + } + ctx->reset_flag_old = ctx->reset_flag; + + if (inBufferLength < offset) return GF_OK; + inBufferLength -= offset; + inBuffer += offset; + + if (*outBufferLength > ctx->out_size) return GF_NON_COMPLIANT_BITSTREAM; + } + return GF_OK; +} + + +static u32 AMR_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + /*we handle audio only*/ + if (!ObjectType) return (StreamType==GF_STREAM_AUDIO) ? 1 : 0; + + /*audio dec*/ + if (!decSpecInfo || (StreamType != GF_STREAM_AUDIO) || (ObjectType != GPAC_OTI_MEDIA_GENERIC)) return 0; + if (decSpecInfoSize<4) return 0; + if (!strnicmp(decSpecInfo, "samr", 4) || !strnicmp(decSpecInfo, "amr ", 4)) return 1; + return 0; +} + + +static const char *AMR_GetCodecName(GF_BaseDecoder *dec) +{ + return "3GPP Fixed-Point AMR NB"; +} + +GF_MediaDecoder *NewAMRDecoder() +{ + AMRDec *dec; + GF_MediaDecoder *ifce; + GF_SAFEALLOC(ifce , GF_MediaDecoder); + dec = malloc(sizeof(AMRDec)); + memset(dec, 0, sizeof(AMRDec)); + ifce->privateStack = dec; + ifce->CanHandleStream = AMR_CanHandleStream; + + /*setup our own interface*/ + ifce->AttachStream = AMR_AttachStream; + ifce->DetachStream = AMR_DetachStream; + ifce->GetCapabilities = AMR_GetCapabilities; + ifce->SetCapabilities = AMR_SetCapabilities; + ifce->ProcessData = AMR_ProcessData; + ifce->GetName = AMR_GetCodecName; + + GF_REGISTER_MODULE_INTERFACE(ifce, GF_MEDIA_DECODER_INTERFACE, "AMR-NB 3GPP decoder", "gpac distribution"); + + return ifce; +} + +void DeleteAMRDecoder(GF_BaseDecoder *ifcg) +{ + AMRCTX(); + free(ctx); + free(ifcg); +} + +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: return 1; + case GF_NET_CLIENT_INTERFACE: return 1; + default: return 0; + } +} + +GF_InputService *NewAESReader(); +void DeleteAESReader(void *ifce); + +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: return (GF_BaseInterface *) NewAMRDecoder(); + case GF_NET_CLIENT_INTERFACE: return (GF_BaseInterface *) NewAESReader(); + default: return NULL; + } +} + +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: DeleteAMRDecoder((GF_BaseDecoder *)ifce); break; + case GF_NET_CLIENT_INTERFACE: DeleteAESReader(ifce); break; + } +} diff --git a/modules/amr_dec/amr_dec.def b/modules/amr_dec/amr_dec.def new file mode 100644 index 0000000..a8a336a --- /dev/null +++ b/modules/amr_dec/amr_dec.def @@ -0,0 +1,6 @@ +LIBRARY gm_amr_dec.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/amr_dec/amr_in.c b/modules/amr_dec/amr_in.c new file mode 100644 index 0000000..07516f2 --- /dev/null +++ b/modules/amr_dec/amr_in.c @@ -0,0 +1,573 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / AMR&EVRC&SMV reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/modules/service.h> +#include <gpac/modules/codec.h> +#include <gpac/constants.h> +#include <gpac/isomedia.h> + +typedef struct +{ + GF_ClientService *service; + + Bool is_remote; + u32 start_offset; + u32 mtype, sample_rate, block_size; + + FILE *stream; + u32 duration; + + Bool needs_connection; + u32 pad_bytes; + Bool done; + u32 is_inline; + LPNETCHANNEL ch; + + unsigned char *data; + u32 data_size; + GF_SLHeader sl_hdr; + + Double start_range, end_range; + u32 current_time; + /*file downloader*/ + GF_DownloadSession * dnload; + + //Bool is_live; +} AMR_Reader; + +static GF_ESD *AMR_GetESD(AMR_Reader *read) +{ + GF_BitStream *dsi; + GF_ESD *esd; + esd = gf_odf_desc_esd_new(0); + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + esd->ESID = 1; + esd->OCRESID = 0; + esd->slConfig->timestampResolution = read->sample_rate; + /*all packets are complete AUs*/ + esd->slConfig->useAccessUnitEndFlag = esd->slConfig->useAccessUnitStartFlag = 0; + esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1; + + if ((read->mtype==GF_ISOM_SUBTYPE_3GP_AMR) || (read->mtype==GF_ISOM_SUBTYPE_3GP_AMR_WB)) { + esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_GENERIC; + dsi = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(dsi, read->mtype); + gf_bs_write_u32(dsi, (read->mtype==GF_ISOM_SUBTYPE_3GP_AMR) ? 8000 : 16000); + gf_bs_write_u16(dsi, 1); + gf_bs_write_u16(dsi, (read->mtype==GF_ISOM_SUBTYPE_3GP_AMR) ? 160 : 320); + gf_bs_write_u8(dsi, 16); + gf_bs_write_u8(dsi, 1); + gf_bs_get_content(dsi, & esd->decoderConfig->decoderSpecificInfo->data, & esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(dsi); + } + else if (read->mtype==GF_ISOM_SUBTYPE_3GP_EVRC) esd->decoderConfig->objectTypeIndication = 0xA0; + else if (read->mtype==GF_ISOM_SUBTYPE_3GP_SMV) esd->decoderConfig->objectTypeIndication = 0xA1; + return esd; +} + +static void AMR_SetupObject(AMR_Reader *read) +{ + GF_ESD *esd; + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1; + esd = AMR_GetESD(read); + esd->OCRESID = 0; + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(read->service, (GF_Descriptor*)od, 0); +} + +static Bool AMR_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt; + sExt = strrchr(url, '.'); + if (!sExt) return 0; + if (gf_term_check_extension(plug, "audio/amr", "amr awb", "AMR Speech Data", sExt)) return 1; + if (gf_term_check_extension(plug, "audio/evrc", "evc", "EVRC Speech Data", sExt)) return 1; + if (gf_term_check_extension(plug, "audio/smv", "smv", "SMV Speech Data", sExt)) return 1; + return 0; +} + +static Bool file_is_local(const char *url) +{ + if (!strnicmp(url, "file://", 7)) return 1; + if (strstr(url, "://")) return 0; + return 1; +} + + +static Bool AMR_ConfigureFromFile(AMR_Reader *read) +{ + u32 i; + char magic[20]; + + if (!read->stream) return 0; + read->mtype = 0; + read->start_offset = 6; + read->sample_rate = 8000; + read->block_size = 160; + fread(magic, 1, 20, read->stream); + + if (!strnicmp(magic, "#!AMR\n", 6)) { + fseek(read->stream, 6, SEEK_SET); + read->mtype = GF_ISOM_SUBTYPE_3GP_AMR; + } + else if (!strnicmp(magic, "#!EVRC\n", 7)) { + fseek(read->stream, 7, SEEK_SET); + read->start_offset = 7; + read->mtype = GF_ISOM_SUBTYPE_3GP_EVRC; + } + else if (!strnicmp(magic, "#!SMV\n", 6)) { + fseek(read->stream, 6, SEEK_SET); + read->mtype = GF_ISOM_SUBTYPE_3GP_SMV; + } + else if (!strnicmp(magic, "#!AMR-WB\n", 9)) { + read->mtype = GF_ISOM_SUBTYPE_3GP_AMR_WB; + read->start_offset = 9; + read->sample_rate = 16000; + read->block_size = 320; + fseek(read->stream, 9, SEEK_SET); + } + else if (!strnicmp(magic, "#!AMR_MC1.0\n", 12)) return 0; + else if (!strnicmp(magic, "#!AMR-WB_MC1.0\n", 15)) return 0; + else return 0; + + + read->duration = 0; + + if (!read->is_remote) { + u32 size; + while (!feof(read->stream)) { + u8 ft = fgetc(read->stream); + switch (read->mtype) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + ft = (ft >> 3) & 0x0F; + size = (read->mtype==GF_ISOM_SUBTYPE_3GP_AMR_WB) ? GF_AMR_WB_FRAME_SIZE[ft] : GF_AMR_FRAME_SIZE[ft]; + break; + default: + for (i=0; i<GF_SMV_EVRC_RATE_TO_SIZE_NB; i++) { + if (GF_SMV_EVRC_RATE_TO_SIZE[2*i]==ft) { + /*remove rate_type byte*/ + size = GF_SMV_EVRC_RATE_TO_SIZE[2*i+1] - 1; + break; + } + } + break; + } + + if (size) fseek(read->stream, size, SEEK_CUR); + read->duration += read->block_size; + } + } + fseek(read->stream, read->start_offset, SEEK_SET); + return 1; +} + +static void AMR_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + const char *szCache; + u32 bytes_done; + AMR_Reader *read = (AMR_Reader *) cbk; + + e = param->error; + /*done*/ + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + if (read->stream) { + read->is_remote = 0; + e = GF_EOS; + } else { + return; + } + } else { + /*handle service message*/ + gf_term_download_update_stats(read->dnload); + if (param->msg_type!=GF_NETIO_DATA_EXCHANGE) return; + } + + /*data fetching*/ + if (e >= GF_OK) { + if (read->stream) return; + + /*open service*/ + szCache = gf_dm_sess_get_cache_name(read->dnload); + if (!szCache) e = GF_IO_ERR; + else { + read->stream = fopen((char *) szCache, "rb"); + if (!read->stream) e = GF_SERVICE_ERROR; + else { + /*if full file at once (in cache) parse duration*/ + if (e==GF_EOS) read->is_remote = 0; + e = GF_OK; + /*not enough data*/ + if (!AMR_ConfigureFromFile(read)) { + /*bad data...*/ + gf_dm_sess_get_stats(read->dnload, NULL, NULL, NULL, &bytes_done, NULL, NULL); + if (bytes_done>10*1024) { + e = GF_CORRUPTED_DATA; + } else { + fclose(read->stream); + read->stream = NULL; + return; + } + } + } + } + } + /*OK confirm*/ + if (read->needs_connection) { + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, e); + if (!e) AMR_SetupObject(read); + } +} + +static void AMR_DownloadFile(GF_InputService *plug, char *url) +{ + AMR_Reader *read = (AMR_Reader*) plug->priv; + + read->needs_connection = 1; + + read->dnload = gf_term_download_new(read->service, url, 0, AMR_NetIO, read); + if (!read->dnload) { + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } + /*service confirm is done once fetched*/ +} + + +static GF_Err AMR_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + char szURL[2048]; + char *ext; + GF_Err reply; + AMR_Reader *read = plug->priv; + read->service = serv; + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + strcpy(szURL, url); + ext = strrchr(szURL, '#'); + if (ext) ext[0] = 0; + + /*remote fetch*/ + read->is_remote = !file_is_local(szURL); + if (read->is_remote) { + AMR_DownloadFile(plug, (char *) szURL); + return GF_OK; + } + + reply = GF_OK; + read->stream = fopen(szURL, "rb"); + if (!read->stream) { + reply = GF_URL_ERROR; + } else if (!AMR_ConfigureFromFile(read)) { + fclose(read->stream); + read->stream = NULL; + reply = GF_NOT_SUPPORTED; + } + gf_term_on_connect(serv, NULL, reply); + if (!reply && read->is_inline) AMR_SetupObject(read); + return GF_OK; +} + +static GF_Err AMR_CloseService(GF_InputService *plug) +{ + AMR_Reader *read = plug->priv; + if (read->stream) fclose(read->stream); + read->stream = NULL; + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + if (read->data) free(read->data); + read->data = NULL; + gf_term_on_disconnect(read->service, NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *AMR_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + AMR_Reader *read = plug->priv; + /*since we don't handle multitrack in aac, we don't need to check sub_url, only use expected type*/ + + /*override default*/ + if (expect_type==GF_MEDIA_OBJECT_UNDEF) expect_type=GF_MEDIA_OBJECT_AUDIO; + + /*audio object*/ + if (expect_type==GF_MEDIA_OBJECT_AUDIO) { + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + GF_ESD *esd = AMR_GetESD(read); + od->objectDescriptorID = 1; + gf_list_add(od->ESDescriptors, esd); + return (GF_Descriptor *) od; + } + /*let player handle scene description*/ + read->is_inline = 1; + return NULL; +} + +static GF_Err AMR_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ES_ID; + GF_Err e; + AMR_Reader *read = plug->priv; + + e = GF_SERVICE_ERROR; + if (read->ch==channel) goto exit; + + e = GF_STREAM_NOT_FOUND; + if (strstr(url, "ES_ID")) { + sscanf(url, "ES_ID=%d", &ES_ID); + } + /*URL setup*/ + else if (!read->ch && AMR_CanHandleURL(plug, url)) ES_ID = 1; + + if (ES_ID==1) { + read->ch = channel; + e = GF_OK; + } + +exit: + gf_term_on_connect(read->service, channel, e); + return e; +} + +static GF_Err AMR_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + AMR_Reader *read = plug->priv; + + GF_Err e = GF_STREAM_NOT_FOUND; + if (read->ch == channel) { + read->ch = NULL; + if (read->data) free(read->data); + read->data = NULL; + e = GF_OK; + } + gf_term_on_disconnect(read->service, channel, e); + return GF_OK; +} + +static GF_Err AMR_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + AMR_Reader *read = plug->priv; + + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: + return GF_OK; + case GF_NET_CHAN_INTERACTIVE: + return GF_OK; + case GF_NET_CHAN_BUFFER: + return GF_OK; + case GF_NET_CHAN_SET_PADDING: + read->pad_bytes = com->pad.padding_bytes; + return GF_OK; + case GF_NET_CHAN_DURATION: + com->duration.duration = read->duration; + com->duration.duration /= read->sample_rate; + return GF_OK; + case GF_NET_CHAN_PLAY: + read->start_range = com->play.start_range; + read->end_range = com->play.end_range; + read->current_time = 0; + if (read->stream) fseek(read->stream, read->start_offset, SEEK_SET); + + if (read->ch == com->base.on_channel) { + read->done = 0; + /*PLAY after complete download, estimate duration*/ + if (!read->is_remote && !read->duration) { + AMR_ConfigureFromFile(read); + if (read->duration) { + GF_NetworkCommand rcfg; + rcfg.base.on_channel = read->ch; + rcfg.base.command_type = GF_NET_CHAN_DURATION; + rcfg.duration.duration = read->duration; + rcfg.duration.duration /= read->sample_rate; + gf_term_on_command(read->service, &rcfg, GF_OK); + } + } + } + return GF_OK; + case GF_NET_CHAN_STOP: + return GF_OK; + default: + return GF_OK; + } +} + + +static GF_Err AMR_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + u32 pos, start_from, i; + u8 toc, ft; + AMR_Reader *read = plug->priv; + + *out_reception_status = GF_OK; + *sl_compressed = 0; + *is_new_data = 0; + + memset(&read->sl_hdr, 0, sizeof(GF_SLHeader)); + read->sl_hdr.randomAccessPointFlag = 1; + read->sl_hdr.compositionTimeStampFlag = 1; + if (read->ch != channel) return GF_STREAM_NOT_FOUND; + + /*fetching es data*/ + if (read->done) { + *out_reception_status = GF_EOS; + return GF_OK; + } + + if (!read->data) { + if (!read->stream) { + *out_data_ptr = NULL; + *out_data_size = 0; + return GF_OK; + } + *is_new_data = 1; + +fetch_next: + pos = ftell(read->stream); + + toc = fgetc(read->stream); + switch (read->mtype) { + case GF_ISOM_SUBTYPE_3GP_AMR: + ft = (toc >> 3) & 0x0F; + read->data_size = GF_AMR_FRAME_SIZE[ft]; + break; + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + ft = (toc >> 3) & 0x0F; + read->data_size = GF_AMR_WB_FRAME_SIZE[ft]; + break; + default: + for (i=0; i<GF_SMV_EVRC_RATE_TO_SIZE_NB; i++) { + if (GF_SMV_EVRC_RATE_TO_SIZE[2*i]==toc) { + /*remove rate_type byte*/ + read->data_size = GF_SMV_EVRC_RATE_TO_SIZE[2*i+1] - 1; + break; + } + } + break; + } + /*we're seeking*/ + if (read->start_range && read->duration) { + start_from = (u32) (read->start_range * read->sample_rate); + if (read->current_time + read->block_size < start_from) { + read->current_time += read->block_size; + fseek(read->stream, read->data_size, SEEK_CUR); + goto fetch_next; + } else { + read->start_range = 0; + } + } + + read->data_size++; + read->sl_hdr.compositionTimeStamp = read->current_time; + read->data = malloc(sizeof(char) * (read->data_size+read->pad_bytes)); + read->data[0] = toc; + if (read->data_size>1) fread(read->data + 1, read->data_size-1, 1, read->stream); + if (read->pad_bytes) memset(read->data + read->data_size, 0, sizeof(char) * read->pad_bytes); + } + *out_sl_hdr = read->sl_hdr; + *out_data_ptr = read->data; + *out_data_size = read->data_size; + return GF_OK; +} + +static GF_Err AMR_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) +{ + AMR_Reader *read = plug->priv; + + if (read->ch == channel) { + if (!read->data) return GF_BAD_PARAM; + free(read->data); + read->data = NULL; + read->current_time += read->block_size; + return GF_OK; + } + return GF_OK; +} + +GF_InputService *NewAESReader() +{ + AMR_Reader *reader; + GF_InputService *plug = malloc(sizeof(GF_InputService)); + memset(plug, 0, sizeof(GF_InputService)); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC AMR/EVRC/SMV Reader", "gpac distribution") + + plug->CanHandleURL = AMR_CanHandleURL; + plug->ConnectService = AMR_ConnectService; + plug->CloseService = AMR_CloseService; + plug->GetServiceDescriptor = AMR_GetServiceDesc; + plug->ConnectChannel = AMR_ConnectChannel; + plug->DisconnectChannel = AMR_DisconnectChannel; + plug->ServiceCommand = AMR_ServiceCommand; + /*we do support pull mode*/ + plug->ChannelGetSLP = AMR_ChannelGetSLP; + plug->ChannelReleaseSLP = AMR_ChannelReleaseSLP; + + reader = malloc(sizeof(AMR_Reader)); + memset(reader, 0, sizeof(AMR_Reader)); + plug->priv = reader; + return plug; +} + +void DeleteAESReader(void *ifce) +{ + GF_InputService *plug = (GF_InputService *) ifce; + AMR_Reader *read = plug->priv; + free(read); + free(plug); +} + + +#ifdef GPAC_AMR_IN_STANDALONE +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_NET_CLIENT_INTERFACE: return 1; + default: return 0; + } +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_NET_CLIENT_INTERFACE: return (GF_BaseInterface *) NewAESReader(); + default: return NULL; + } +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_NET_CLIENT_INTERFACE: DeleteAESReader(ifce); break; + } +} +#endif + diff --git a/modules/amr_dec/amr_nb/typedefs.h b/modules/amr_dec/amr_nb/typedefs.h new file mode 100644 index 0000000..63b0bfd --- /dev/null +++ b/modules/amr_dec/amr_nb/typedefs.h @@ -0,0 +1,195 @@ +/* +******************************************************************************** +* +* GSM AMR-NB speech codec R98 Version 7.6.0 December 12, 2001 +* R99 Version 3.3.0 +* REL-4 Version 4.1.0 +* +******************************************************************************** +* +* File : typedefs.h +* Description : Definition of platform independent data +* types and constants +* +* +* The following platform independent data types and corresponding +* preprocessor (#define) constants are defined: +* +* defined type meaning corresponding constants +* ---------------------------------------------------------- +* Char character (none) +* Bool boolean true, false +* Word8 8-bit signed minWord8, maxWord8 +* UWord8 8-bit unsigned minUWord8, maxUWord8 +* Word16 16-bit signed minWord16, maxWord16 +* UWord16 16-bit unsigned minUWord16, maxUWord16 +* Word32 32-bit signed minWord32, maxWord32 +* UWord32 32-bit unsigned minUWord32, maxUWord32 +* Float floating point minFloat, maxFloat +* +* +* The following compile switches are #defined: +* +* PLATFORM string indicating platform progam is compiled on +* possible values: "OSF", "PC", "SUN" +* +* OSF only defined if the current platform is an Alpha +* PC only defined if the current platform is a PC +* SUN only defined if the current platform is a Sun +* +* LSBFIRST is defined if the byte order on this platform is +* "least significant byte first" -> defined on DEC Alpha +* and PC, undefined on Sun +* +******************************************************************************** +*/ +#ifndef typedefs_h +#define typedefs_h "$Id $" + +/* +******************************************************************************** +* INCLUDE FILES +******************************************************************************** +*/ +#include <float.h> +#include <limits.h> + + + +/* +******************************************************************************** +* DEFINITION OF CONSTANTS +******************************************************************************** +*/ +/* + ********* define char type + */ +typedef char Char; + +/* + ********* define 8 bit signed/unsigned types & constants + */ +#if SCHAR_MAX == 127 +typedef signed char Word8; +#define minWord8 SCHAR_MIN +#define maxWord8 SCHAR_MAX + +typedef unsigned char UWord8; +#define minUWord8 0 +#define maxUWord8 UCHAR_MAX +#else +#error cannot find 8-bit type +#endif + + +/* + ********* define 16 bit signed/unsigned types & constants + */ +#if INT_MAX == 32767 +typedef int Word16; +#define minWord16 INT_MIN +#define maxWord16 INT_MAX +typedef unsigned int UWord16; +#define minUWord16 0 +#define maxUWord16 UINT_MAX +#elif SHRT_MAX == 32767 +typedef short Word16; +#define minWord16 SHRT_MIN +#define maxWord16 SHRT_MAX +typedef unsigned short UWord16; +#define minUWord16 0 +#define maxUWord16 USHRT_MAX +#else +#error cannot find 16-bit type +#endif + + +/* + ********* define 32 bit signed/unsigned types & constants + */ +#if INT_MAX == 2147483647 +typedef int Word32; +#define minWord32 INT_MIN +#define maxWord32 INT_MAX +typedef unsigned int UWord32; +#define minUWord32 0 +#define maxUWord32 UINT_MAX +#elif LONG_MAX == 2147483647 +typedef long Word32; +#define minWord32 LONG_MIN +#define maxWord32 LONG_MAX +typedef unsigned long UWord32; +#define minUWord32 0 +#define maxUWord32 ULONG_MAX +#else +#error cannot find 32-bit type +#endif + +/* + ********* define floating point type & constants + */ +/* use "#if 0" below if Float should be double; + use "#if 1" below if Float should be float + */ +#if 0 +//typedef float Float; +#define Float float +#define maxFloat FLT_MAX +#define minFloat FLT_MIN +#else +//typedef double Float; +#define Float double +#define maxFloat DBL_MAX +#define minFloat DBL_MIN +#endif + +/* + ********* define complex type + */ +typedef struct { + Float r; /* real part */ + Float i; /* imaginary part */ +} CPX; + +/* + ********* define boolean type + */ +#define Bool int +#define false 0 +#define true 1 + +/* + ********* Check current platform + */ +#if defined(__MSDOS__) +#define PC +#define PLATFORM "PC" +#define LSBFIRST +#elif defined(__osf__) +#define OSF +#define PLATFORM "OSF" +#define LSBFIRST +#elif defined(__sun__) || defined(__sun) +#define SUN +#define PLATFORM "SUN" +#undef LSBFIRST +#elif defined(linux) && defined(i386) +#define PC +#define PLATFORM "PC" +#define LSBFIRST +#else +#elif defined(WIN32) +#define PC +#define PLATFORM "PC" +#define LSBFIRST +#error "can't determine architecture; adapt typedefs.h to your platform" +#endif + +#if defined(_WIN32_WCE) +# ifndef abort +# define abort() +# endif +#endif + + +#endif diff --git a/modules/amr_float_dec/Makefile b/modules/amr_float_dec/Makefile new file mode 100644 index 0000000..48f8476 --- /dev/null +++ b/modules/amr_float_dec/Makefile @@ -0,0 +1,79 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/amr_float_dec + +#note: __MSDOS__ defined is the same cfg as linux. There may be some pbs on other platforms, to check... +CFLAGS= $(OPTFLAGS) -w -I$(SRC_PATH)/include -D__MSDOS__ + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g -DDEBUG +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS=../amr_dec/amr_in.o amr_float_dec.o + +#AMR NB obj +CFLAGS+=-DGPAC_HAS_AMR_FT +ifeq ($(CONFIG_AMR_NB_FT), yes) +OBJS+=./amr_nb_ft/interf_dec.o ./amr_nb_ft/interf_enc.o ./amr_nb_ft/sp_dec.o ./amr_nb_ft/sp_enc.o +endif + + +#AMR WB obj +CFLAGS+=-DGPAC_HAS_AMR_FT_WB +ifeq ($(CONFIG_AMR_WB_FT), yes) +OBJS+=./amr_wb_ft/dec_acelp.o ./amr_wb_ft/dec_dtx.o ./amr_wb_ft/dec_gain.o ./amr_wb_ft/dec_if.o ./amr_wb_ft/dec_lpc.o ./amr_wb_ft/dec_main.o \ + ./amr_wb_ft/dec_rom.o ./amr_wb_ft/dec_util.o ./amr_wb_ft/enc_acelp.o ./amr_wb_ft/enc_dtx.o ./amr_wb_ft/enc_gain.o ./amr_wb_ft/enc_if.o \ + ./amr_wb_ft/enc_lpc.o ./amr_wb_ft/enc_main.o ./amr_wb_ft/enc_rom.o ./amr_wb_ft/enc_util.o ./amr_wb_ft/if_rom.o +endif + + +SRCS := $(OBJS:.o=.c) + +LIB=gm_amr_float_dec.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols amr_float_dec.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/amr_float_dec/amr_float_dec.c b/modules/amr_float_dec/amr_float_dec.c new file mode 100644 index 0000000..fe4574a --- /dev/null +++ b/modules/amr_float_dec/amr_float_dec.c @@ -0,0 +1,321 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / AMR decoder module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +/*decoder Interface*/ +#include <gpac/modules/codec.h> +#include <gpac/modules/service.h> +#include <gpac/constants.h> + +#ifdef GPAC_HAS_AMR_FT +/*AMR NB*/ +#include "amr_nb_ft/interf_dec.h" +#endif + +#ifdef GPAC_HAS_AMR_FT_WB +/*AMR WB*/ +#include "amr_wb_ft/dec_if.h" +#endif + +/*default size in CU of composition memory for audio*/ +#define DEFAULT_AUDIO_CM_SIZE 12 +/*default critical size in CU of composition memory for audio*/ +#define DEFAULT_AUDIO_CM_TRIGGER 4 + +typedef struct +{ + Bool is_amr_wb; + u32 sample_rate, out_size, num_samples; + u8 num_channels; + + /*AMR NB state vars*/ + int *nb_destate; + void *wb_destate; +} AMRFTDec; + +#define AMRFTCTX() AMRFTDec *ctx = (AMRFTDec *) ifcg->privateStack + + +static GF_Err AMR_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + GF_BitStream *bs; + u32 packed; + AMRFTCTX(); + if (esd->decoderConfig || !esd->decoderConfig->decoderSpecificInfo) return GF_NOT_SUPPORTED; + + /*AMRWB dec is another module*/ + if (!strnicmp(esd->decoderConfig->decoderSpecificInfo->data, "sawb", 4)) ctx->is_amr_wb = 1; + else if (!strnicmp(esd->decoderConfig->decoderSpecificInfo->data, "samr", 4) || !strnicmp(esd->decoderConfig->decoderSpecificInfo->data, "amr ", 4)) ctx->is_amr_wb = 0; + else return GF_NOT_SUPPORTED; + + + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + gf_bs_read_u32(bs); + gf_bs_read_u16(bs); + gf_bs_read_u16(bs); + ctx->num_channels = gf_bs_read_u8(bs); + gf_bs_read_u8(bs); + packed = gf_bs_read_u8(bs); + gf_bs_del(bs); + /*max possible frames in a sample are seen in MP4, that's 15*/ + if (!packed) packed = 15; + + if (ctx->is_amr_wb) { +#ifdef GPAC_HAS_AMR_FT_WB + ctx->wb_destate = D_IF_init(); + if (!ctx->wb_destate) return GF_IO_ERR; +#else + return GF_NOT_SUPPORTED; +#endif + ctx->num_samples = 320; + ctx->sample_rate = 16000; + } else { +#ifdef GPAC_HAS_AMR_FT + ctx->nb_destate = Decoder_Interface_init(); + if (!ctx->nb_destate) return GF_IO_ERR; +#else + return GF_NOT_SUPPORTED; +#endif + ctx->num_samples = 160; + ctx->sample_rate = 8000; + } + + ctx->out_size = packed * 2 * ctx->num_samples * ctx->num_channels; + return GF_OK; +} + +static GF_Err AMR_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + AMRFTCTX(); + +#ifdef GPAC_HAS_AMR_FT + if (ctx->nb_destate) Decoder_Interface_exit(ctx->nb_destate); +#endif + ctx->nb_destate = NULL; +#ifdef GPAC_HAS_AMR_FT_WB + if (ctx->wb_destate) D_IF_exit(ctx->wb_destate); +#endif + ctx->wb_destate = NULL; + ctx->sample_rate = ctx->out_size = ctx->num_samples = 0; + ctx->num_channels = 0; + return GF_OK; +} +static GF_Err AMR_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + AMRFTCTX(); + switch (capability->CapCode) { + /*not tested yet*/ + case GF_CODEC_RESILIENT: + capability->cap.valueInt = 1; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_SAMPLERATE: + capability->cap.valueInt = ctx->sample_rate; + break; + case GF_CODEC_NB_CHAN: + capability->cap.valueInt = ctx->num_channels; + break; + case GF_CODEC_BITS_PER_SAMPLE: + capability->cap.valueInt = 16; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = DEFAULT_AUDIO_CM_TRIGGER; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = DEFAULT_AUDIO_CM_SIZE; + break; + /*FIXME: get exact sampling window*/ + case GF_CODEC_CU_DURATION: + capability->cap.valueInt = ctx->num_samples; + break; + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 4; + break; + case GF_CODEC_CHANNEL_CONFIG: + if (ctx->num_channels==1) { + capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER; + } else { + capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT; + } + break; + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} + +static GF_Err AMR_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like SR changing ...) */ + return GF_NOT_SUPPORTED; +} + + +static GF_Err AMR_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + u32 offset; + u8 toc, ft; + AMRFTCTX(); + + /*if late or seeking don't decode (each frame is a RAP)*/ + /* switch (mmlevel) { + case GF_CODEC_LEVEL_SEEK: + case GF_CODEC_LEVEL_DROP: + *outBufferLength = 0; + return GF_OK; + default: + break; + } + */ + if (ctx->out_size > *outBufferLength) { + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; + } + + *outBufferLength = 0; + + while (inBufferLength) { + toc = inBuffer[0]; + ft = (toc >> 3) & 0x0F; + + offset = 0; + if (ctx->is_amr_wb) { +#ifdef GPAC_HAS_AMR_FT_WB + D_IF_decode(ctx->wb_destate, inBuffer, (Word16 *) outBuffer, 0); + *outBufferLength += 320*2; + outBuffer += 320*2; + offset = GF_AMR_WB_FRAME_SIZE[ft] + 1; +#endif + } else { +#ifdef GPAC_HAS_AMR_FT + Decoder_Interface_Decode(ctx->nb_destate, inBuffer, (Word16 *) outBuffer, 0); + *outBufferLength += 160*2; + outBuffer += 160*2; + offset = GF_AMR_FRAME_SIZE[ft] + 1; +#endif + } + /*don't complain but...*/ + if (inBufferLength<offset) return GF_OK; + inBuffer += offset; + inBufferLength -= offset; + } + return GF_OK; +} + + +static u32 AMR_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + /*we handle audio only*/ + if (!ObjectType) return (StreamType==GF_STREAM_AUDIO) ? 1 : 0; + + /*audio dec*/ + if (!decSpecInfo || (StreamType != GF_STREAM_AUDIO) || (ObjectType != GPAC_OTI_MEDIA_GENERIC)) return 0; + if (decSpecInfoSize<4) return 0; + +#ifdef GPAC_HAS_AMR_FT + if (!strnicmp(decSpecInfo, "samr", 4) || !strnicmp(decSpecInfo, "amr ", 4)) return 1; +#endif +#ifdef GPAC_HAS_AMR_FT_WB + if (!strnicmp(decSpecInfo, "sawb", 4)) return 1; +#endif + return 0; +} + + +static const char *AMR_GetCodecName(GF_BaseDecoder *ifcg) +{ + AMRFTCTX(); + if (ctx->is_amr_wb) return "3GPP Floating-point AMR Wideband"; + return "3GPP Floating-point AMR"; +} + +GF_MediaDecoder *NewAMRFTDecoder() +{ + AMRFTDec *dec; + GF_MediaDecoder *ifce; + GF_SAFEALLOC(ifce , GF_MediaDecoder); + dec = malloc(sizeof(AMRFTDec)); + memset(dec, 0, sizeof(AMRFTDec)); + ifce->privateStack = dec; + ifce->CanHandleStream = AMR_CanHandleStream; + + /*setup our own interface*/ + ifce->AttachStream = AMR_AttachStream; + ifce->DetachStream = AMR_DetachStream; + ifce->GetCapabilities = AMR_GetCapabilities; + ifce->SetCapabilities = AMR_SetCapabilities; + ifce->ProcessData = AMR_ProcessData; + ifce->GetName = AMR_GetCodecName; + + GF_REGISTER_MODULE_INTERFACE(ifce, GF_MEDIA_DECODER_INTERFACE, "AMR-FT 3GPP decoder", "gpac distribution"); + + return ifce; +} + +void DeleteAMRFTDecoder(GF_BaseDecoder *ifcg) +{ + AMRFTCTX(); + free(ctx); + free(ifcg); +} + +/*re-include AMR reader (we coul make it an independant module...)*/ +GF_InputService *NewAESReader(); +void DeleteAESReader(void *ifce); + +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: return 1; + case GF_NET_CLIENT_INTERFACE: return 1; + default: + return 0; + } +} + +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: return (GF_BaseInterface *)NewAMRFTDecoder(); + case GF_NET_CLIENT_INTERFACE: return (GF_BaseInterface *)NewAESReader(); + default: return NULL; + } +} + +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: DeleteAMRFTDecoder((GF_BaseDecoder *)ifce); break; + case GF_NET_CLIENT_INTERFACE: DeleteAESReader(ifce); break; + } +} + diff --git a/modules/amr_float_dec/amr_float_dec.def b/modules/amr_float_dec/amr_float_dec.def new file mode 100644 index 0000000..b6049f5 --- /dev/null +++ b/modules/amr_float_dec/amr_float_dec.def @@ -0,0 +1,6 @@ +LIBRARY gm_amr_float_dec.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/amr_float_dec/amr_nb_ft/dummy b/modules/amr_float_dec/amr_nb_ft/dummy new file mode 100644 index 0000000..e69de29 diff --git a/modules/amr_float_dec/amr_wb_ft/dummy b/modules/amr_float_dec/amr_wb_ft/dummy new file mode 100644 index 0000000..e69de29 diff --git a/modules/bifs_dec/Makefile b/modules/bifs_dec/Makefile new file mode 100644 index 0000000..2f639f8 --- /dev/null +++ b/modules/bifs_dec/Makefile @@ -0,0 +1,62 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/bifs_dec + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= bifs_dec.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_bifs_dec.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols bifs_dec.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/bifs_dec/bifs_dec.c b/modules/bifs_dec/bifs_dec.c new file mode 100644 index 0000000..e5a3ce9 --- /dev/null +++ b/modules/bifs_dec/bifs_dec.c @@ -0,0 +1,187 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS decoder module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/internal/terminal_dev.h> +#include <gpac/bifs.h> +#include <gpac/constants.h> + +typedef struct +{ + GF_InlineScene *pScene; + GF_Terminal *app; + GF_BifsDecoder *codec; + u32 PL, nb_streams; +} BIFSPriv; + + + +static GF_Err BIFS_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *cap) +{ + cap->cap.valueInt = 0; + return GF_NOT_SUPPORTED; +} + +static GF_Err BIFS_SetCapabilities(GF_BaseDecoder *plug, const GF_CodecCapability capability) +{ + return GF_OK; +} + +GF_Err BIFS_AttachScene(GF_SceneDecoder *plug, GF_InlineScene *scene, Bool is_scene_decoder) +{ + BIFSPriv *priv = (BIFSPriv *)plug->privateStack; + if (priv->codec) return GF_BAD_PARAM; + priv->pScene = scene; + priv->app = scene->root_od->term; + + priv->codec = gf_bifs_decoder_new(scene->graph, 0); + /*ignore all size info on anim streams*/ + if (!is_scene_decoder) gf_bifs_decoder_ignore_size_info(priv->codec); + return GF_OK; +} + +GF_Err BIFS_ReleaseScene(GF_SceneDecoder *plug) +{ + BIFSPriv *priv = (BIFSPriv *)plug->privateStack; + if (!priv->codec || priv->nb_streams) return GF_BAD_PARAM; + gf_bifs_decoder_del(priv->codec); + priv->codec = NULL; + return GF_OK; +} + +static GF_Err BIFS_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) +{ + BIFSPriv *priv = (BIFSPriv *)plug->privateStack; + GF_Err e; + if (esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; + if (!esd->decoderConfig->decoderSpecificInfo) return GF_BAD_PARAM; + e = gf_bifs_decoder_configure_stream(priv->codec, esd->ESID, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, esd->decoderConfig->objectTypeIndication); + if (!e) priv->nb_streams++; + return e; +} + +static GF_Err BIFS_DetachStream(GF_BaseDecoder *plug, u16 ES_ID) +{ + GF_Err e; + BIFSPriv *priv = (BIFSPriv *)plug->privateStack; + e = gf_bifs_decoder_remove_stream(priv->codec, ES_ID); + if (e) return e; + priv->nb_streams--; + return GF_OK; +} + +static GF_Err BIFS_ProcessData(GF_SceneDecoder*plug, char *inBuffer, u32 inBufferLength, + u16 ES_ID, u32 AU_time, u32 mmlevel) +{ + GF_Err e = GF_OK; + BIFSPriv *priv = (BIFSPriv *)plug->privateStack; + + e = gf_bifs_decode_au(priv->codec, ES_ID, inBuffer, inBufferLength, ((Double)AU_time)/1000.0); + + /*if scene not attached do it*/ + gf_inline_attach_to_compositor(priv->pScene); + return e; +} + +Bool BIFS_CanHandleStream(GF_BaseDecoder *ifce, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + BIFSPriv *priv = (BIFSPriv *)ifce->privateStack; + if (StreamType!=GF_STREAM_SCENE) return 0; + switch (ObjectType) { + case 0x00: + return 1; + case GPAC_OTI_SCENE_BIFS: + case GPAC_OTI_SCENE_BIFS_V2: + /*Streams with this value with a StreamType indicating a systems stream (values 1,2,3, 6, 7, 8, 9) + shall be treated as if the ObjectTypeIndication had been set to 0x01*/ + case 0xFF: + priv->PL = PL; + return 1; + default: + return 0; + } +} + + +void DeleteBIFSDec(GF_BaseDecoder *plug) +{ + BIFSPriv *priv = (BIFSPriv *)plug->privateStack; + /*in case something went wrong*/ + if (priv->codec) gf_bifs_decoder_del(priv->codec); + free(priv); + free(plug); +} + +GF_BaseDecoder *NewBIFSDec() +{ + BIFSPriv *priv; + GF_SceneDecoder *tmp; + + GF_SAFEALLOC(tmp, GF_SceneDecoder); + if (!tmp) return NULL; + GF_SAFEALLOC(priv, BIFSPriv); + priv->codec = NULL; + tmp->privateStack = priv; + tmp->AttachStream = BIFS_AttachStream; + tmp->DetachStream = BIFS_DetachStream; + tmp->GetCapabilities = BIFS_GetCapabilities; + tmp->SetCapabilities = BIFS_SetCapabilities; + tmp->ProcessData = BIFS_ProcessData; + tmp->AttachScene = BIFS_AttachScene; + tmp->CanHandleStream = BIFS_CanHandleStream; + tmp->ReleaseScene = BIFS_ReleaseScene; + GF_REGISTER_MODULE_INTERFACE(tmp, GF_SCENE_DECODER_INTERFACE, "GPAC BIFS Decoder", "gpac distribution") + return (GF_BaseDecoder *) tmp; +} + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + return 1; + default: + return 0; + } +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + return (GF_BaseInterface *)NewBIFSDec(); + default: + return NULL; + } +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + DeleteBIFSDec((GF_BaseDecoder *)ifce); + break; + } +} diff --git a/modules/bifs_dec/bifs_dec.def b/modules/bifs_dec/bifs_dec.def new file mode 100644 index 0000000..3b239e6 --- /dev/null +++ b/modules/bifs_dec/bifs_dec.def @@ -0,0 +1,6 @@ +LIBRARY gm_bifs_dec.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/ctx_load/Makefile b/modules/ctx_load/Makefile new file mode 100644 index 0000000..cfce40b --- /dev/null +++ b/modules/ctx_load/Makefile @@ -0,0 +1,63 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/ctx_load + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= ctx_load.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_ctx_load.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols ctx_load.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/ctx_load/ctx_load.c b/modules/ctx_load/ctx_load.c new file mode 100644 index 0000000..807d0cb --- /dev/null +++ b/modules/ctx_load/ctx_load.c @@ -0,0 +1,748 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / GPAC Scene Context loader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/internal/terminal_dev.h> +#include <gpac/scene_manager.h> +#include <gpac/constants.h> +#include <gpac/network.h> +#include <gpac/nodes_mpeg4.h> + + +typedef struct +{ + GF_InlineScene *inline_scene; + GF_Terminal *app; + GF_SceneManager *ctx; + GF_SceneLoader load; + char *file_name; + u32 file_size; + u32 load_flags; + u32 nb_streams; + u32 base_stream_id; + u32 last_check_time, last_check_size; + /*mp3 import from flash*/ + GF_List *files_to_delete; + /*progressive loading support for XMT X3D*/ + FILE *src; + u32 file_pos, sax_max_duration; + Bool progressive_support; +} CTXLoadPriv; + + +static GF_Err CTXLoad_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *cap) +{ + cap->cap.valueInt = 0; + return GF_NOT_SUPPORTED; +} + +static GF_Err CTXLoad_SetCapabilities(GF_BaseDecoder *plug, const GF_CodecCapability capability) +{ + return GF_OK; +} + +static void ODS_SetupOD(GF_InlineScene *is, GF_ObjectDescriptor *od) +{ + GF_ObjectManager *odm; + odm = gf_inline_find_odm(is, od->objectDescriptorID); + /*remove the old OD*/ + if (odm) gf_odm_disconnect(odm, 1); + odm = gf_odm_new(); + odm->OD = od; + odm->term = is->root_od->term; + odm->parentscene = is; + gf_list_add(is->ODlist, odm); + + /*locate service owner*/ + gf_odm_setup_object(odm, is->root_od->net_service); +} + + +static void CTXLoad_Reset(CTXLoadPriv *priv) +{ + if (priv->ctx) gf_sm_del(priv->ctx); + priv->ctx = NULL; + gf_sg_reset(priv->inline_scene->graph); + if (priv->load_flags != 3) priv->load_flags = 0; + while (gf_list_count(priv->files_to_delete)) { + char *fileName = (char*)gf_list_get(priv->files_to_delete, 0); + gf_list_rem(priv->files_to_delete, 0); + gf_delete_file(fileName); + free(fileName); + } +} + +void CTXLoad_OnActivate(GF_Node *node) +{ + GF_InlineScene *is = (GF_InlineScene *) gf_node_get_private(node); + M_Conditional*c = (M_Conditional*)node; + /*always apply in parent graph to handle protos correctly*/ + if (c->activate) gf_sg_command_apply_list(gf_node_get_graph(node), c->buffer.commandList, gf_inline_get_time(is)); +} +void CTXLoad_OnReverseActivate(GF_Node *node) +{ + GF_InlineScene *is = (GF_InlineScene *) gf_node_get_private(node); + M_Conditional*c = (M_Conditional*)node; + /*always apply in parent graph to handle protos correctly*/ + if (!c->reverseActivate) + gf_sg_command_apply_list(gf_node_get_graph(node), c->buffer.commandList, gf_inline_get_time(is)); +} + +void CTXLoad_NodeCallback(void *cbk, u32 type, GF_Node *node, void *param) +{ + if ((type==GF_SG_CALLBACK_INIT) && (gf_node_get_tag(node) == TAG_MPEG4_Conditional) ) { + M_Conditional*c = (M_Conditional*)node; + c->on_activate = CTXLoad_OnActivate; + c->on_reverseActivate = CTXLoad_OnReverseActivate; + gf_node_set_private(node, cbk); + } else { + gf_term_node_callback(cbk, type, node, param); + } +} + +static Bool CTXLoad_CheckDownload(CTXLoadPriv *priv) +{ + u32 size; + FILE *f; + u32 now = gf_sys_clock(); + + if (!priv->file_size && (now - priv->last_check_time < 1000) ) return 0; + + f = fopen(priv->file_name, "rt"); + fseek(f, 0, SEEK_END); + size = ftell(f); + fclose(f); + + /*we MUST have a complete file for now ...*/ + if (!priv->file_size) { + if (priv->last_check_size == size) return 1; + priv->last_check_size = size; + priv->last_check_time = now; + } else { + if (size==priv->file_size) return 1; + } + return 0; +} + + +static GF_Err CTXLoad_Setup(GF_BaseDecoder *plug) +{ + CTXLoadPriv *priv = (CTXLoadPriv *)plug->privateStack; + if (!priv->file_name) return GF_BAD_PARAM; + + priv->ctx = gf_sm_new(priv->inline_scene->graph); + memset(&priv->load, 0, sizeof(GF_SceneLoader)); + priv->load.ctx = priv->ctx; + priv->load.is = priv->inline_scene; + priv->load.scene_graph = priv->inline_scene->graph; + priv->load.fileName = priv->file_name; + priv->load.flags = GF_SM_LOAD_FOR_PLAYBACK; + priv->load.localPath = gf_modules_get_option((GF_BaseInterface *)plug, "General", "CacheDirectory"); + priv->load.swf_import_flags = GF_SM_SWF_STATIC_DICT | GF_SM_SWF_QUAD_CURVE | GF_SM_SWF_SCALABLE_LINE | GF_SM_SWF_SPLIT_TIMELINE; + return GF_OK; +} + +static GF_Err CTXLoad_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) +{ + const char *ext; + GF_BitStream *bs; + u32 size; + CTXLoadPriv *priv = (CTXLoadPriv *)plug->privateStack; + if (esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; + + /*animation stream like*/ + if (priv->ctx) { + GF_StreamContext *sc; + u32 i = 0; + while ((sc = (GF_StreamContext *)gf_list_enum(priv->ctx->streams, &i))) { + if (esd->ESID == sc->ESID) { + priv->nb_streams++; + return GF_OK; + } + } + return GF_NON_COMPLIANT_BITSTREAM; + } + /*main dummy stream we need a dsi*/ + if (!esd->decoderConfig->decoderSpecificInfo) + return GF_NON_COMPLIANT_BITSTREAM; + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + priv->file_size = gf_bs_read_u32(bs); + gf_bs_del(bs); + size = esd->decoderConfig->decoderSpecificInfo->dataLength - sizeof(u32); + priv->file_name = (char *) malloc(sizeof(char)*(1 + size) ); + memcpy(priv->file_name, esd->decoderConfig->decoderSpecificInfo->data + sizeof(u32), sizeof(char)*(esd->decoderConfig->decoderSpecificInfo->dataLength - sizeof(u32)) ); + priv->file_name[size] = 0; + priv->nb_streams = 1; + priv->load_flags = 0; + priv->base_stream_id = esd->ESID; + + + CTXLoad_Setup(plug); + + priv->progressive_support = 0; + priv->sax_max_duration = 0; + + ext = strrchr(priv->file_name, '.'); + if (!ext) return GF_OK; + + ext++; + if (!stricmp(ext, "xmt") || !stricmp(ext, "xmtz") || !stricmp(ext, "xmta") + || !stricmp(ext, "x3d") || !stricmp(ext, "x3dz") + ) { + ext = gf_modules_get_option((GF_BaseInterface *)plug, "SAXLoader", "Progressive"); + priv->progressive_support = (ext && !stricmp(ext, "yes")) ? 1 : 0; + } + if (priv->progressive_support) { + ext = gf_modules_get_option((GF_BaseInterface *)plug, "SAXLoader", "MaxDuration"); + if (ext) priv->sax_max_duration = atoi(ext); + } + return GF_OK; +} + +static GF_Err CTXLoad_DetachStream(GF_BaseDecoder *plug, u16 ES_ID) +{ + CTXLoadPriv *priv = (CTXLoadPriv *)plug->privateStack; + priv->nb_streams --; + return GF_OK; +} + +static GF_Err CTXLoad_AttachScene(GF_SceneDecoder *plug, GF_InlineScene *scene, Bool is_scene_decoder) +{ + CTXLoadPriv *priv = (CTXLoadPriv *)plug->privateStack; + if (priv->ctx) return GF_BAD_PARAM; + + priv->inline_scene = scene; + priv->app = scene->root_od->term; + gf_sg_set_node_callback(scene->graph, CTXLoad_NodeCallback); + + return GF_OK; +} + +static GF_Err CTXLoad_ReleaseScene(GF_SceneDecoder *plug) +{ + CTXLoad_Reset((CTXLoadPriv *) plug->privateStack); + return GF_OK; +} + +static Bool CTXLoad_StreamInRootOD(GF_ObjectDescriptor *od, u32 ESID) +{ + u32 i, count; + /*no root, only one stream possible*/ + if (!od) return 1; + count = gf_list_count(od->ESDescriptors); + /*idem*/ + if (!count) return 1; + for (i=0; i<count; i++) { + GF_ESD *esd = (GF_ESD *)gf_list_get(od->ESDescriptors, i); + if (esd->ESID==ESID) return 1; + } + return 0; +} + + +Double CTXLoad_GetVRMLTime(void *cbk) +{ + u32 secs, msecs; + Double res; + gf_utc_time_since_1970(&secs, &msecs); + res = msecs; + res /= 1000; + res += secs; + return res; +} + +static void CTXLoad_CheckStreams(CTXLoadPriv *priv ) +{ + u32 i, j, max_dur; + GF_AUContext *au; + GF_StreamContext *sc; + max_dur = 0; + i=0; + while ((sc = (GF_StreamContext *)gf_list_enum(priv->ctx->streams, &i))) { + /*all streams in root OD are handled with ESID 0 to differentiate with any animation streams*/ + if (CTXLoad_StreamInRootOD(priv->ctx->root_od, sc->ESID)) sc->in_root_od = 1; + if (!sc->timeScale) sc->timeScale = 1000; + + j=0; + while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) { + if (!au->timing) au->timing = (u64) (sc->timeScale*au->timing_sec); + } + if (au && sc->in_root_od && (au->timing>max_dur)) max_dur = (u32) (au->timing * 1000 / sc->timeScale); + } + if (max_dur) { + priv->inline_scene->root_od->duration = max_dur; + gf_inline_set_duration(priv->inline_scene); + } +} + +static GF_Err CTXLoad_ProcessData(GF_SceneDecoder *plug, char *inBuffer, u32 inBufferLength, + u16 ES_ID, u32 stream_time, u32 mmlevel) +{ + GF_Err e = GF_OK; + u32 i, j, k, nb_updates, last_rap=0; + GF_AUContext *au; + Bool can_delete_com; + GF_StreamContext *sc; + CTXLoadPriv *priv = (CTXLoadPriv *)plug->privateStack; + + /*something failed*/ + if (priv->load_flags==3) return GF_EOS; + + /*this signals main scene deconnection, destroy the context if needed*/ + assert(ES_ID); + if (!priv->ctx) { + e = CTXLoad_Setup((GF_BaseDecoder *)plug); + if (e) return e; + } + + + if (stream_time==(u32)-1) { + /*seek on root stream: destroy the context manager and reload it. We cannot seek on the main stream + because commands may have changed node attributes/children and we d'ont track the initial value*/ + if (priv->base_stream_id == ES_ID) { + if (priv->src) fclose(priv->src); + priv->src = NULL; + gf_sm_load_done(&priv->load); + priv->file_pos = 0; + /*this will call detach scene*/ + gf_inline_disconnect(priv->inline_scene, 1); + return CTXLoad_Setup((GF_BaseDecoder *)plug); + } + i=0; + while ((sc = (GF_StreamContext *)gf_list_enum(priv->ctx->streams, &i))) { + /*not our stream*/ + if (!sc->in_root_od && (sc->ESID != ES_ID)) continue; + /*not the base stream*/ + if (sc->in_root_od && (priv->base_stream_id != ES_ID)) continue; + /*handle SWF media extraction*/ + if ((sc->streamType == GF_STREAM_OD) && (priv->load_flags==1)) continue; + sc->last_au_time = 0; + } + return GF_OK; + } + + if (priv->load_flags != 2) { + + if (priv->progressive_support) { + u32 entry_time; + char file_buf[4096+1]; + if (!priv->src) { + priv->src = fopen(priv->file_name, "rb"); + if (!priv->src) return GF_URL_ERROR; + priv->file_pos = 0; + } + priv->load.type = GF_SM_LOAD_XMTA; + e = GF_OK; + entry_time = gf_sys_clock(); + fseek(priv->src, priv->file_pos, SEEK_SET); + while (1) { + u32 diff, nb_read; + nb_read = fread(file_buf, 1, 4096, priv->src); + file_buf[nb_read] = 0; + if (!nb_read) { + if (priv->file_pos==priv->file_size) { + fclose(priv->src); + priv->src = NULL; + priv->load_flags = 2; + gf_sm_load_done(&priv->load); + break; + } + break; + } + + e = gf_sm_load_string(&priv->load, file_buf, 0); + priv->file_pos += nb_read; + if (e) break; + diff = gf_sys_clock() - entry_time; + if (diff > priv->sax_max_duration) break; + } + if (!priv->inline_scene->graph_attached) { + gf_sg_set_scene_size_info(priv->inline_scene->graph, priv->ctx->scene_width, priv->ctx->scene_height, priv->ctx->is_pixel_metrics); + gf_inline_attach_to_compositor(priv->inline_scene); + + CTXLoad_CheckStreams(priv); + } + } + /*load first frame only*/ + else if (!priv->load_flags) { + /*we need the whole file*/ + if (!CTXLoad_CheckDownload(priv)) return GF_OK; + + priv->load_flags = 1; + e = gf_sm_load_init(&priv->load); + if (!e) { + CTXLoad_CheckStreams(priv); + gf_sg_set_scene_size_info(priv->inline_scene->graph, priv->ctx->scene_width, priv->ctx->scene_height, priv->ctx->is_pixel_metrics); + /*VRML, override base clock*/ + if ((priv->load.type==GF_SM_LOAD_VRML) || (priv->load.type==GF_SM_LOAD_X3DV) || (priv->load.type==GF_SM_LOAD_X3D)) { + /*override clock callback*/ + gf_sg_set_scene_time_callback(priv->inline_scene->graph, CTXLoad_GetVRMLTime); + } + } + } + /*load the rest*/ + else { + priv->load_flags = 2; + e = gf_sm_load_run(&priv->load); + gf_sm_load_done(&priv->load); + } + + if (e) { + gf_sm_load_done(&priv->load); + gf_sm_del(priv->ctx); + priv->ctx = NULL; + priv->load_flags = 3; + return e; + } + + /*and figure out duration of root scene, and take care of XMT timing*/ + if (priv->load_flags==2) { + CTXLoad_CheckStreams(priv); + if (!gf_list_count(priv->ctx->streams)) { + gf_inline_attach_to_compositor(priv->inline_scene); + } + } + } + + nb_updates = 0; + + i=0; + while ((sc = (GF_StreamContext *)gf_list_enum(priv->ctx->streams, &i))) { + /*not our stream*/ + if (!sc->in_root_od && (sc->ESID != ES_ID)) continue; + /*not the base stream*/ + if (sc->in_root_od && (priv->base_stream_id != ES_ID)) continue; + /*handle SWF media extraction*/ + if ((sc->streamType == GF_STREAM_OD) && (priv->load_flags==1)) continue; + + /*check for seek*/ + if (sc->last_au_time > 1 + stream_time) { + sc->last_au_time = 0; + } + + can_delete_com = 0; + if (sc->in_root_od && (priv->load_flags==2)) can_delete_com = 1; + + /*we're in the right stream, apply update*/ + j=0; + + /*seek*/ + if (!sc->last_au_time) { + while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) { + u32 au_time = (u32) (au->timing*1000/sc->timeScale); + + if (au_time > stream_time) + break; + if (au->is_rap) last_rap = j-1; + } + j = last_rap; + } + + while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) { + u32 au_time = (u32) (au->timing*1000/sc->timeScale); + + if (au_time + 1 <= sc->last_au_time) { + /*remove first replace command*/ + if (can_delete_com && (sc->streamType==GF_STREAM_SCENE)) { + while (gf_list_count(au->commands)) { + GF_Command *com = (GF_Command *)gf_list_get(au->commands, 0); + gf_list_rem(au->commands, 0); + gf_sg_command_del(com); + } + j--; + gf_list_rem(sc->AUs, j); + gf_list_del(au->commands); + free(au); + } + continue; + } + + if (au_time > stream_time) { + nb_updates++; + break; + } + + if (sc->streamType == GF_STREAM_SCENE) { + GF_Command *com; + /*apply the commands*/ + k=0; + while ((com = (GF_Command *)gf_list_enum(au->commands, &k))) { + e = gf_sg_command_apply(priv->inline_scene->graph, com, 0); + if (e) break; + /*remove commands on base layer*/ + if (can_delete_com) { + k--; + gf_list_rem(au->commands, k); + gf_sg_command_del(com); + } + } + } + else if (sc->streamType == GF_STREAM_OD) { + /*apply the commands*/ + while (gf_list_count(au->commands)) { + Bool keep_com = 0; + GF_ODCom *com = (GF_ODCom *)gf_list_get(au->commands, 0); + gf_list_rem(au->commands, 0); + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + { + GF_ODUpdate *odU = (GF_ODUpdate *)com; + while (gf_list_count(odU->objectDescriptors)) { + GF_ESD *esd; + char *remote; + GF_MuxInfo *mux = NULL; + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *)gf_list_get(odU->objectDescriptors, 0); + gf_list_rem(odU->objectDescriptors, 0); + /*we can only work with single-stream ods*/ + esd = (GF_ESD*)gf_list_get(od->ESDescriptors, 0); + if (!esd) { + if (od->URLString) { + ODS_SetupOD(priv->inline_scene, od); + } else { + gf_odf_desc_del((GF_Descriptor *) od); + } + continue; + } + /*fix OCR dependencies*/ + if (CTXLoad_StreamInRootOD(priv->ctx->root_od, esd->OCRESID)) esd->OCRESID = priv->base_stream_id; + + /*forbidden if ESD*/ + if (od->URLString) { + gf_odf_desc_del((GF_Descriptor *) od); + continue; + } + /*look for MUX info*/ + k=0; + while ((mux = (GF_MuxInfo*)gf_list_enum(esd->extensionDescriptors, &k))) { + if (mux->tag == GF_ODF_MUXINFO_TAG) break; + mux = NULL; + } + /*we need a mux if not animation stream*/ + if (!mux || !mux->file_name) { + /*only animation streams are handled*/ + if (!esd->decoderConfig) { + gf_odf_desc_del((GF_Descriptor *) od); + } else if (esd->decoderConfig->streamType==GF_STREAM_SCENE) { + /*set ST to private scene to get sure the stream will be redirected to us*/ + esd->decoderConfig->streamType = GF_STREAM_PRIVATE_SCENE; + esd->dependsOnESID = priv->base_stream_id; + ODS_SetupOD(priv->inline_scene, od); + } else if (esd->decoderConfig->streamType==GF_STREAM_INTERACT) { + GF_UIConfig *cfg = (GF_UIConfig *) esd->decoderConfig->decoderSpecificInfo; + gf_odf_encode_ui_config(cfg, &esd->decoderConfig->decoderSpecificInfo); + gf_odf_desc_del((GF_Descriptor *) cfg); + ODS_SetupOD(priv->inline_scene, od); + } else { + gf_odf_desc_del((GF_Descriptor *) od); + } + continue; + } + /*text import*/ + if (mux->textNode) { +#ifdef GPAC_READ_ONLY + gf_odf_desc_del((GF_Descriptor *) od); + continue; +#else + e = gf_sm_import_bifs_subtitle(priv->ctx, esd, mux); + if (e) { + e = GF_OK; + gf_odf_desc_del((GF_Descriptor *) od); + continue; + } + /*set ST to private scene and dependency to base to get sure the stream will be redirected to us*/ + esd->decoderConfig->streamType = GF_STREAM_PRIVATE_SCENE; + esd->dependsOnESID = priv->base_stream_id; + ODS_SetupOD(priv->inline_scene, od); + continue; +#endif + } + + /*soundstreams are a bit of a pain, they may be declared before any data gets written*/ + if (mux->delete_file) { + FILE *t = fopen(mux->file_name, "rb"); + if (!t) { + keep_com = 1; + gf_list_insert(odU->objectDescriptors, od, 0); + break; + } + fclose(t); + } + /*remap to remote URL*/ + remote = strdup(mux->file_name); + k = od->objectDescriptorID; + /*if files were created we'll have to clean up (swf import)*/ + if (mux->delete_file) gf_list_add(priv->files_to_delete, strdup(remote)); + + gf_odf_desc_del((GF_Descriptor *) od); + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->URLString = remote; + od->objectDescriptorID = k; + ODS_SetupOD(priv->inline_scene, od); + } + if (keep_com) break; + } + break; + case GF_ODF_OD_REMOVE_TAG: + { + GF_ODRemove *odR = (GF_ODRemove*)com; + for (k=0; k<odR->NbODs; k++) { + GF_ObjectManager *odm = gf_inline_find_odm(priv->inline_scene, odR->OD_ID[k]); + if (odm) gf_odm_disconnect(odm, 1); + } + } + break; + default: + break; + } + if (keep_com) { + gf_list_insert(au->commands, com, 0); + break; + } else { + gf_odf_com_del(&com); + } + if (e) break; + } + + } + sc->last_au_time = au_time + 1; + /*attach graph to renderer*/ + if (!priv->inline_scene->graph_attached) + gf_inline_attach_to_compositor(priv->inline_scene); + if (e) return e; + + /*for root streams remove completed AUs (no longer needed)*/ + if (sc->in_root_od && !gf_list_count(au->commands) ) { + j--; + gf_list_rem(sc->AUs, j); + gf_list_del(au->commands); + free(au); + } + } + } + if (e) return e; + if ((priv->load_flags==2) && !nb_updates) return GF_EOS; + return GF_OK; +} + +const char *CTXLoad_GetName(struct _basedecoder *plug) +{ + CTXLoadPriv *priv = (CTXLoadPriv *)plug->privateStack; + + switch (priv->load.type) { + case GF_SM_LOAD_BT: return "MPEG-4 BT Parser"; + case GF_SM_LOAD_VRML: return "VRML 97 Parser"; + case GF_SM_LOAD_X3DV: return "X3D (VRML Syntax) Parser"; + case GF_SM_LOAD_XMTA: return "XMT-A Parser"; + case GF_SM_LOAD_X3D: return "X3D (XML Syntax) Parser"; + case GF_SM_LOAD_SWF: return "Flash (SWF) Emulator"; + case GF_SM_LOAD_XSR: return "LASeRML Loader"; + case GF_SM_LOAD_MP4: return "MP4 Memory Loader"; + case GF_SM_LOAD_XBL: return "XBL Parser"; + + default: return "Undetermined"; + } +} + +Bool CTXLoad_CanHandleStream(GF_BaseDecoder *ifce, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + if (StreamType==GF_STREAM_PRIVATE_SCENE) { + switch (ObjectType) { + case GPAC_OTI_PRIVATE_SCENE_GENERIC: + return 1; + /*LASeR ML: we use this plugin since it has command handling*/ + case GPAC_OTI_PRIVATE_SCENE_LASER: + return 1; + /* XBL */ + case GPAC_OTI_PRIVATE_SCENE_XBL: + return 1; + } + } + /*SVG*/ + //if ((StreamType==GF_STREAM_PRIVATE_SCENE) && (ObjectType==2)) return 1; + return 0; +} + +void DeleteContextLoader(GF_BaseDecoder *plug) +{ + CTXLoadPriv *priv = (CTXLoadPriv *)plug->privateStack; + if (priv->file_name) free(priv->file_name); + assert(!priv->ctx); + gf_list_del(priv->files_to_delete); + free(priv); + free(plug); +} + +GF_BaseDecoder *NewContextLoader() +{ + CTXLoadPriv *priv; + GF_SceneDecoder *tmp; + + GF_SAFEALLOC(tmp, GF_SceneDecoder); + GF_SAFEALLOC(priv, CTXLoadPriv); + priv->files_to_delete = gf_list_new(); + + tmp->privateStack = priv; + tmp->AttachStream = CTXLoad_AttachStream; + tmp->DetachStream = CTXLoad_DetachStream; + tmp->GetCapabilities = CTXLoad_GetCapabilities; + tmp->SetCapabilities = CTXLoad_SetCapabilities; + tmp->ProcessData = CTXLoad_ProcessData; + tmp->AttachScene = CTXLoad_AttachScene; + tmp->ReleaseScene = CTXLoad_ReleaseScene; + tmp->GetName = CTXLoad_GetName; + tmp->CanHandleStream = CTXLoad_CanHandleStream; + GF_REGISTER_MODULE_INTERFACE(tmp, GF_SCENE_DECODER_INTERFACE, "GPAC Context Loader", "gpac distribution") + return (GF_BaseDecoder*)tmp; +} + + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + return 1; + default: + return 0; + } +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: return (GF_BaseInterface *)NewContextLoader(); + default: + return NULL; + } +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + DeleteContextLoader((GF_BaseDecoder *)ifce); + break; + } +} diff --git a/modules/ctx_load/ctx_load.def b/modules/ctx_load/ctx_load.def new file mode 100644 index 0000000..98f3ac7 --- /dev/null +++ b/modules/ctx_load/ctx_load.def @@ -0,0 +1,6 @@ +LIBRARY gm_ctx_load.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/dummy_in/Makefile b/modules/dummy_in/Makefile new file mode 100644 index 0000000..5be8f45 --- /dev/null +++ b/modules/dummy_in/Makefile @@ -0,0 +1,62 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/dummy_in + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= dummy_in.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_dummy_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols dummy_in.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/dummy_in/dummy_in.c b/modules/dummy_in/dummy_in.c new file mode 100644 index 0000000..9a07559 --- /dev/null +++ b/modules/dummy_in/dummy_in.c @@ -0,0 +1,445 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Dummy input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <gpac/modules/service.h> +/*for GF_STREAM_PRIVATE_SCENE definition*/ +#include <gpac/constants.h> +#include <gpac/download.h> +#include <gpac/xml.h> + +typedef struct +{ + u32 ESID; + LPNETCHANNEL ch; + u32 start, end; +} DummyChannel; + +typedef struct +{ + /*the service we're responsible for*/ + GF_ClientService *service; + char szURL[2048]; + u32 oti; + GF_List *channels; + + /*file downloader*/ + GF_DownloadSession * dnload; + Bool is_service_connected; +} DCReader; + +DummyChannel *DC_GetChannel(DCReader *read, LPNETCHANNEL ch) +{ + DummyChannel *dc; + u32 i=0; + while ((dc = (DummyChannel *)gf_list_enum(read->channels, &i))) { + if (dc->ch && dc->ch==ch) return dc; + } + return NULL; +} + +Bool DC_RemoveChannel(DCReader *read, LPNETCHANNEL ch) +{ + DummyChannel *dc; + u32 i=0; + while ((dc = (DummyChannel *)gf_list_enum(read->channels, &i))) { + if (dc->ch && dc->ch==ch) { + gf_list_rem(read->channels, i-1); + free(dc); + return 1; + } + } + return 0; +} + +Bool DC_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt = strrchr(url, '.'); + if (sExt) { + if (!strnicmp(sExt, ".gz", 3)) sExt = strrchr(sExt, '.'); + if (!strnicmp(url, "rtsp://", 7)) return 0; + + + /*the mpeg-4 mime types for bt/xmt are NOT registered at all :)*/ + if (gf_term_check_extension(plug, "application/x-bt", "bt bt.gz btz", "MPEG-4 Text (BT)", sExt)) return 1; + if (gf_term_check_extension(plug, "application/x-xmt", "xmt xmt.gz xmtz", "MPEG-4 Text (XMT)", sExt)) return 1; + //if (gf_term_check_extension(plug, "application/x-xmta", "xmta xmta.gz xmtaz", "MPEG-4 Text (XMT)", sExt)) return 1; + /*but all these ones are*/ + if (gf_term_check_extension(plug, "model/vrml", "wrl wrl.gz", "VRML World", sExt)) return 1; + if (gf_term_check_extension(plug, "x-model/x-vrml", "wrl wrl.gz", "VRML World", sExt)) return 1; + if (gf_term_check_extension(plug, "model/x3d+vrml", "x3dv x3dv.gz x3dvz", "X3D/VRML World", sExt)) return 1; + if (gf_term_check_extension(plug, "model/x3d+xml", "x3d x3d.gz x3dz", "X3D/XML World", sExt)) return 1; + if (gf_term_check_extension(plug, "application/x-shockwave-flash", "swf", "Macromedia Flash Movie", sExt)) return 1; + if (gf_term_check_extension(plug, "image/svg+xml", "svg svg.gz svgz", "SVG Document", sExt)) return 1; + if (gf_term_check_extension(plug, "image/x-svgm", "svgm", "SVGM Document", sExt)) return 1; + if (gf_term_check_extension(plug, "application/x-LASeR+xml", "xsr", "LASeR Document", sExt)) return 1; + } + + if (!strnicmp(url, "file://", 7) || !strstr(url, "://")) { + char *rtype = gf_xml_get_root_type(url, NULL); + if (rtype) { + Bool handled = 0; + if (!strcmp(rtype, "SAFSession")) handled = 1; + else if (!strcmp(rtype, "XMT-A")) handled = 1; + else if (!strcmp(rtype, "X3D")) handled = 1; + else if (!strcmp(rtype, "svg")) handled = 1; + else if (!strcmp(rtype, "bindings")) handled = 1; + free(rtype); + return handled; + } + } + return 0; +} + +void DC_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + DCReader *read = (DCReader *) cbk; + + /*handle service message*/ + gf_term_download_update_stats(read->dnload); + + e = param->error; + + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + } else if (param->msg_type==GF_NETIO_PARSE_HEADER) { + if (!strcmp(param->name, "Content-Type")) { + if (strstr(param->value, "application/x-bt")) read->oti = GPAC_OTI_PRIVATE_SCENE_GENERIC; + if (strstr(param->value, "application/x-xmt")) read->oti = GPAC_OTI_PRIVATE_SCENE_GENERIC; + if (strstr(param->value, "model/vrml")) read->oti = GPAC_OTI_PRIVATE_SCENE_GENERIC; + if (strstr(param->value, "model/x3d+vrml")) read->oti = GPAC_OTI_PRIVATE_SCENE_GENERIC; + if (strstr(param->value, "application/x-shockwave-flash")) read->oti = GPAC_OTI_PRIVATE_SCENE_GENERIC; + if (strstr(param->value, "image/svg+xml")) read->oti = GPAC_OTI_PRIVATE_SCENE_SVG; + if (strstr(param->value, "image/x-svgm")) read->oti = GPAC_OTI_PRIVATE_SCENE_SVG; + if (strstr(param->value, "application/x-LASeR+xml")) read->oti = GPAC_OTI_PRIVATE_SCENE_GENERIC; + } + return; + } else if (!e && (param->msg_type!=GF_NETIO_DATA_EXCHANGE)) return; + + if (!e && !read->oti) + return; + + /*OK confirm*/ + if (!read->is_service_connected) { + if (!gf_dm_sess_get_cache_name(read->dnload)) e = GF_IO_ERR; + gf_term_on_connect(read->service, NULL, e); + read->is_service_connected = 1; + } +} + +void DC_DownloadFile(GF_InputService *plug, char *url) +{ + DCReader *read = (DCReader *) plug->priv; + + read->dnload = gf_term_download_new(read->service, url, 0, DC_NetIO, read); + if (!read->dnload) gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); +} + + +GF_Err DC_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + DCReader *read = (DCReader *) plug->priv; + FILE *test; + char *tmp, *ext; + + if (!read || !serv || !url) return GF_BAD_PARAM; + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + strcpy(read->szURL, url); + ext = strchr(read->szURL, '#'); + if (ext) { + char *anext; + ext[0] = 0; + anext = strrchr(read->szURL, '.'); + ext[0] = '#'; + ext = anext; + } else { + ext = strrchr(read->szURL, '.'); + } + if (ext && !stricmp(ext, ".gz")) { + char *anext; + ext[0] = 0; + anext = strrchr(read->szURL, '.'); + ext[0] = '.'; + ext = anext; + } + read->service = serv; + + if (ext) { + char *cgi_par = NULL; + ext += 1; + if (ext) { + tmp = strchr(ext, '#'); if (tmp) tmp[0] = 0; + /* Warning the '?' sign should not be present in local files but it is convenient to have it + to test web content locally */ + cgi_par = strchr(ext, '?'); if (cgi_par) cgi_par[0] = 0; + } + if (!stricmp(ext, "bt") || !stricmp(ext, "btz") || !stricmp(ext, "bt.gz") + || !stricmp(ext, "xmta") + || !stricmp(ext, "xmt") || !stricmp(ext, "xmt.gz") || !stricmp(ext, "xmtz") + || !stricmp(ext, "wrl") || !stricmp(ext, "wrl.gz") + || !stricmp(ext, "x3d") || !stricmp(ext, "x3d.gz") || !stricmp(ext, "x3dz") + || !stricmp(ext, "x3dv") || !stricmp(ext, "x3dv.gz") || !stricmp(ext, "x3dvz") + || !stricmp(ext, "swf") + ) + read->oti = GPAC_OTI_PRIVATE_SCENE_GENERIC; + + else if (!stricmp(ext, "svg") || !stricmp(ext, "svgz") || !stricmp(ext, "svg.gz")) { + read->oti = GPAC_OTI_PRIVATE_SCENE_SVG; + } + /*XML LASeR*/ + else if (!stricmp(ext, "xsr")) + read->oti = GPAC_OTI_PRIVATE_SCENE_LASER; + else if (!stricmp(ext, "xbl")) + read->oti = GPAC_OTI_PRIVATE_SCENE_XBL; + + if (cgi_par) cgi_par[0] = '?'; + } + + if (!read->oti && (!strnicmp(url, "file://", 7) || !strstr(url, "://"))) { + char *rtype = gf_xml_get_root_type(url, NULL); + if (rtype) { + if (!strcmp(rtype, "SAFSession")) read->oti = GPAC_OTI_PRIVATE_SCENE_LASER; + else if (!strcmp(rtype, "svg")) read->oti = GPAC_OTI_PRIVATE_SCENE_SVG; + else if (!strcmp(rtype, "XMT-A")) read->oti = GPAC_OTI_PRIVATE_SCENE_GENERIC; + else if (!strcmp(rtype, "X3D")) read->oti = GPAC_OTI_PRIVATE_SCENE_GENERIC; + else if (!strcmp(rtype, "bindings")) read->oti = GPAC_OTI_PRIVATE_SCENE_XBL; + free(rtype); + } + } + + /*remote fetch*/ + if (!strnicmp(url, "file://", 7)) { + url += 7; + } + else if (strstr(url, "://")) { + DC_DownloadFile(plug, read->szURL); + return GF_OK; + } + + test = fopen(read->szURL, "rt"); + if (!test) { + gf_term_on_connect(serv, NULL, GF_URL_ERROR); + return GF_OK; + } + fclose(test); + if (!read->is_service_connected) { + gf_term_on_connect(serv, NULL, GF_OK); + read->is_service_connected = 1; + } + return GF_OK; +} + +GF_Err DC_CloseService(GF_InputService *plug) +{ + DCReader *read = (DCReader *) plug->priv; + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + gf_term_on_disconnect(read->service, NULL, GF_OK); + return GF_OK; +} + +/*Dummy input just send a file name, no multitrack to handle so we don't need to check sub_url nor expected type*/ +static GF_Descriptor *DC_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + u32 size = 0; + char *uri; + GF_ESD *esd; + GF_BitStream *bs; + DCReader *read = (DCReader *) plug->priv; + GF_InitialObjectDescriptor *iod = (GF_InitialObjectDescriptor *) gf_odf_desc_new(GF_ODF_IOD_TAG); + iod->scene_profileAndLevel = 1; + iod->graphics_profileAndLevel = 1; + iod->OD_profileAndLevel = 1; + iod->audio_profileAndLevel = 0xFE; + iod->visual_profileAndLevel = 0xFE; + iod->objectDescriptorID = 1; + + esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = 1000; + esd->slConfig->useTimestampsFlag = 1; + esd->ESID = 0xFFFE; + esd->decoderConfig->streamType = GF_STREAM_PRIVATE_SCENE; + esd->decoderConfig->objectTypeIndication = read->oti; + if (read->dnload) { + uri = (char *) gf_dm_sess_get_cache_name(read->dnload); + gf_dm_sess_get_stats(read->dnload, NULL, NULL, &size, NULL, NULL, NULL); + } else { + FILE *f = fopen(read->szURL, "rt"); + fseek(f, 0, SEEK_END); + size = ftell(f); + fclose(f); + uri = read->szURL; + } + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, size); + gf_bs_write_data(bs, uri, strlen(uri)); + gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + + gf_list_add(iod->ESDescriptors, esd); + return (GF_Descriptor *)iod; +} + + +GF_Err DC_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + DCReader *read = (DCReader *) plug->priv; + DummyChannel *dc; + + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + + dc = DC_GetChannel(read, com->base.on_channel); + if (!dc) return GF_STREAM_NOT_FOUND; + + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: return GF_OK; + case GF_NET_CHAN_INTERACTIVE: return GF_OK; + /*since data is file-based, no padding is needed (decoder plugin will handle it itself)*/ + case GF_NET_CHAN_SET_PADDING: return GF_OK; + case GF_NET_CHAN_BUFFER: + com->buffer.max = com->buffer.min = 0; + return GF_OK; + case GF_NET_CHAN_DURATION: + /*this module is not made for updates, use undefined duration*/ + com->duration.duration = 0; + return GF_OK; + case GF_NET_CHAN_PLAY: + dc->start = (u32) (1000 * com->play.start_range); + dc->end = (u32) (1000 * com->play.end_range); + return GF_OK; + case GF_NET_CHAN_STOP: + return GF_OK; + case GF_NET_CHAN_CONFIG: return GF_OK; + case GF_NET_CHAN_GET_DSI: + com->get_dsi.dsi = NULL; + com->get_dsi.dsi_len = 0; + return GF_OK; + } + return GF_OK; +} + +GF_Err DC_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ESID; + DCReader *read = (DCReader *) plug->priv; + + sscanf(url, "ES_ID=%d", &ESID); + if (!ESID) { + gf_term_on_connect(read->service, channel, GF_STREAM_NOT_FOUND); + } else { + DummyChannel *dc; + GF_SAFEALLOC(dc, DummyChannel); + dc->ch = channel; + dc->ESID = ESID; + gf_list_add(read->channels, dc); + gf_term_on_connect(read->service, channel, GF_OK); + } + return GF_OK; +} + +GF_Err DC_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + Bool had_ch; + DCReader *read = (DCReader *) plug->priv; + + had_ch = DC_RemoveChannel(read, channel); + gf_term_on_disconnect(read->service, channel, had_ch ? GF_OK : GF_STREAM_NOT_FOUND); + return GF_OK; +} + +GF_Err DC_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + DummyChannel *dc; + DCReader *read = (DCReader *) plug->priv; + dc = DC_GetChannel(read, channel); + if (!dc) return GF_STREAM_NOT_FOUND; + + memset(out_sl_hdr, 0, sizeof(GF_SLHeader)); + out_sl_hdr->compositionTimeStampFlag = 1; + out_sl_hdr->compositionTimeStamp = dc->start; + out_sl_hdr->accessUnitStartFlag = 1; + *sl_compressed = 0; + *out_reception_status = GF_OK; + *is_new_data = 1; + return GF_OK; +} + +GF_Err DC_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) +{ + return GF_OK; +} + +Bool DC_CanHandleURLInService(GF_InputService *plug, const char *url) +{ + return 0; +} + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType==GF_NET_CLIENT_INTERFACE) return 1; + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + DCReader *read; + GF_InputService *plug; + if (InterfaceType != GF_NET_CLIENT_INTERFACE) return NULL; + + GF_SAFEALLOC(plug, GF_InputService); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC Dummy Loader", "gpac distribution") + + plug->CanHandleURL = DC_CanHandleURL; + plug->ConnectService = DC_ConnectService; + plug->CloseService = DC_CloseService; + plug->GetServiceDescriptor = DC_GetServiceDesc; + plug->ConnectChannel = DC_ConnectChannel; + plug->DisconnectChannel = DC_DisconnectChannel; + plug->ServiceCommand = DC_ServiceCommand; + plug->CanHandleURLInService = DC_CanHandleURLInService; + plug->ChannelGetSLP = DC_ChannelGetSLP; + plug->ChannelReleaseSLP = DC_ChannelReleaseSLP; + GF_SAFEALLOC(read, DCReader); + read->channels = gf_list_new(); + plug->priv = read; + return (GF_BaseInterface *)plug; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *bi) +{ + GF_InputService *ifcn = (GF_InputService*)bi; + if (ifcn->InterfaceType==GF_NET_CLIENT_INTERFACE) { + DCReader *read = (DCReader*)ifcn->priv; + assert(!gf_list_count(read->channels)); + gf_list_del(read->channels); + free(read); + free(bi); + } +} diff --git a/modules/dummy_in/dummy_in.def b/modules/dummy_in/dummy_in.def new file mode 100644 index 0000000..835b3b1 --- /dev/null +++ b/modules/dummy_in/dummy_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_dummy_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/dx_hw/Makefile b/modules/dx_hw/Makefile new file mode 100644 index 0000000..36e7005 --- /dev/null +++ b/modules/dx_hw/Makefile @@ -0,0 +1,69 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/dx_hw + +#DIRECTSOUND_VERSION is needed for GCC compil.. +CFLAGS= $(OPTFLAGS) -w -I$(SRC_PATH)/include -DGPAC_HAVE_CONFIG_H -DDIRECTSOUND_VERSION=0x0500 + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +ifeq ($(DX_PATH), system) +LDFLAGS_DX=-ldsound -ldxguid -lddraw -lole32 -lgdi32 -lopenGL32 +else +CFLAGS+= -I$(DX_PATH)/include +LDFLAGS_DX=-L$(DX_PATH)/lib -ldsound -ldxguid -lddraw -lole32 -lgdi32 -lopenGL32 +endif + + +#common obj +OBJS=dx_audio.o dx_video.o dx_window.o dx_2d.o copy_pixels.o + + +SRCS := $(OBJS:.o=.c) + +LIB=gm_dx_hw.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols dx_hw.def +endif + +all: $(LIB) + + +$(LIB): $(OBJS) + windres $(SRC_PATH)/modules/dx_hw/dx_hw.rc dw_hw.o + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) dw_hw.o $(LDFLAGS_DX) -L../../bin/gcc -lgpac $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) +ifeq ($(CONFIG_WIN32),yes) + rm -f dx_hw.o +endif + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/dx_hw/collide.cur b/modules/dx_hw/collide.cur new file mode 100644 index 0000000000000000000000000000000000000000..388b9f0e1384c42ea2108ac7ae67023f70ed5155 GIT binary patch literal 2238 zcmeH{A&(kS5Xa{ll9Gm`!JtlDP8B^0*Wi(8+<b+zVD2N3j5~3nnoj_!>4ixxricc2 z3LHtr^!H{{S6A6vnD_Sm-`n~BcHZuayptQbyOTP;^hHLTeIo9N&%})&ax8n*{iRtl ziM(8P9WAG_-EL+3V=L!x&(iDlq~Gt$U@(y3a44hENN#U$<^KL&#^bTf=X06OW-^^l zWipw_YPBLEQQfNRXiFAl&Il($YN%+6rfAx<?3Nh1p&PoP8+yCU(H-3xIJ%?LNuURM zpa(*rC*x$C(OJ<d{z@X{B9)Y~r%@Ob28BUkC?psZ28BUkP#6jm28BUkP#6@3LWe<N zP#6>jg+UVy4uwM-M`l$3g}(iq2m-y`GAxF<VPqsVSgH(68Yc#e!BS;`!{9JD3=V^% zN(G0(U@#aAt#k&5!C){1UP)Ue#Haf)uJX3(K!vxns!ENw!{nGc1`d<s?MymJ9B;?F zwxhh!@pt?k|F%Vrzf&8B!C`Rf(6)`L)ZL@d)3$A-27zC|7H|Z9wY80d8U!5CIJmLe zZY~N~0+s+GFsw%amf*r*Q6K~?{0Z`~Cjm>edK6208rm+P!H_T{3<*QRkTBHa1Vg}( zFeD5C18E4>!;ml}3<(3N33M0|hJ+yjNc@9j$%V-xBZo0III?oLH72{=P8N%WJUl$e z<Kv^O*K67D_p;e+<Zw91@pzQe=_IeOuX4Fu<a)iz`B%#G(-ZIUlIpg1pmpS{QOnzg yDIeaBw+g&f;J>W^-++HUt{*ec?=w%QE?!>vn(<-(KIx*J|NPp`wRLU%pW`=WcNq`> literal 0 HcmV?d00001 diff --git a/modules/dx_hw/copy_pixels.c b/modules/dx_hw/copy_pixels.c new file mode 100644 index 0000000..9422709 --- /dev/null +++ b/modules/dx_hw/copy_pixels.c @@ -0,0 +1,409 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / 2D rendering module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "dx_hw.h" + + +static u32 get_yuv_base(u32 in_pf) +{ + switch (in_pf) { + case GF_PIXEL_I420: + case GF_PIXEL_IYUV: + case GF_PIXEL_YV12: + return GF_PIXEL_YV12; + case GF_PIXEL_Y422: + case GF_PIXEL_UYNV: + case GF_PIXEL_UYVY: + return GF_PIXEL_UYVY; + case GF_PIXEL_YUNV: + case GF_PIXEL_V422: + case GF_PIXEL_YUY2: + return GF_PIXEL_YUY2; + case GF_PIXEL_YVYU: + return GF_PIXEL_YVYU; + default: + return 0; + } +} + +static Bool format_is_yuv(u32 in_pf) +{ + switch (in_pf) { + case GF_PIXEL_YUY2: + case GF_PIXEL_YVYU: + case GF_PIXEL_UYVY: + case GF_PIXEL_VYUY: + case GF_PIXEL_Y422: + case GF_PIXEL_UYNV: + case GF_PIXEL_YUNV: + case GF_PIXEL_V422: + case GF_PIXEL_YV12: + case GF_PIXEL_IYUV: + case GF_PIXEL_I420: + return 1; + /*not supported yet*/ + case GF_PIXEL_YUVA: + default: + return 0; + } +} + +static Bool is_planar_yuv(u32 pf) +{ + switch (pf) { + case GF_PIXEL_YV12: + case GF_PIXEL_I420: + case GF_PIXEL_IYUV: + return 1; + } + return 0; +} + + +static void VR_write_yv12_to_yuv(GF_VideoSurface *vs, unsigned char *src, u32 src_stride, u32 src_pf, + u32 src_width, u32 src_height, const GF_Window *src_wnd) +{ + unsigned char *pY, *pU, *pV; + pY = src; + pU = src + src_stride * src_height; + pV = src + 5*src_stride * src_height/4; + + pY = pY + src_stride * src_wnd->y + src_wnd->x; + pU = pU + (src_stride * src_wnd->y / 2 + src_wnd->x) / 2; + pV = pV + (src_stride * src_wnd->y / 2 + src_wnd->x) / 2; + + + if (is_planar_yuv(vs->pixel_format)) { + /*complete source copy*/ + if ( (vs->pitch == (s32) src_stride) && (src_wnd->w == src_width) && (src_wnd->h == src_height)) { + assert(!src_wnd->x); + assert(!src_wnd->y); + memcpy(vs->video_buffer, pY, sizeof(unsigned char)*src_width*src_height); + if (vs->pixel_format == GF_PIXEL_YV12) { + memcpy(vs->video_buffer + vs->pitch * vs->height, pV, sizeof(unsigned char)*src_width*src_height/4); + memcpy(vs->video_buffer + 5 * vs->pitch * vs->height/4, pU, sizeof(unsigned char)*src_width*src_height/4); + } else { + memcpy(vs->video_buffer + vs->pitch * vs->height, pU, sizeof(unsigned char)*src_width*src_height/4); + memcpy(vs->video_buffer + 5 * vs->pitch * vs->height/4, pV, sizeof(unsigned char)*src_width*src_height/4); + } + } else { + u32 i; + unsigned char *dst, *src, *dst2, *src2, *dst3, *src3; + + src = pY; + dst = vs->video_buffer; + + src2 = (vs->pixel_format != GF_PIXEL_YV12) ? pU : pV; + dst2 = vs->video_buffer + vs->pitch * vs->height; + src3 = (vs->pixel_format != GF_PIXEL_YV12) ? pV : pU; + dst3 = vs->video_buffer + 5*vs->pitch * vs->height/4; + for (i=0; i<src_wnd->h; i++) { + memcpy(dst, src, src_wnd->w); + src += src_stride; + dst += vs->pitch; + if (i<src_wnd->h/2) { + memcpy(dst2, src2, src_wnd->w/2); + src2 += src_stride/2; + dst2 += vs->pitch/2; + memcpy(dst3, src3, src_wnd->w/2); + src3 += src_stride/2; + dst3 += vs->pitch/2; + } + } + } + } else if (vs->pixel_format==GF_PIXEL_UYVY) { + u32 i, j; + unsigned char *dst, *y, *u, *v; + for (i=0; i<src_wnd->h; i++) { + y = pY + i*src_stride; + u = pU + (i/2) * src_stride/2; + v = pV + (i/2) * src_stride/2; + dst = vs->video_buffer + i*vs->pitch; + + for (j=0; j<src_wnd->w/2;j++) { + *dst = *u; + dst++; + u++; + *dst = *y; + dst++; + y++; + *dst = *v; + dst++; + v++; + *dst = *y; + dst++; + y++; + } + } + } else if (vs->pixel_format==GF_PIXEL_YUY2) { + u32 i, j; + unsigned char *dst, *y, *u, *v; + for (i=0; i<src_wnd->h; i++) { + y = pY + i*src_stride; + u = pU + (i/2) * src_stride/2; + v = pV + (i/2) * src_stride/2; + dst = vs->video_buffer + i*vs->pitch; + + for (j=0; j<src_wnd->w/2;j++) { + *dst = *y; + dst++; + y++; + *dst = *u; + dst++; + u++; + *dst = *y; + dst++; + y++; + *dst = *v; + dst++; + v++; + } + } + } else if (vs->pixel_format==GF_PIXEL_YVYU) { + u32 i, j; + unsigned char *dst, *y, *u, *v; + for (i=0; i<src_wnd->h; i++) { + y = pY + i*src_stride; + u = pU + (i/2) * src_stride/2; + v = pV + (i/2) * src_stride/2; + dst = vs->video_buffer + i*vs->pitch; + + for (j=0; j<src_wnd->w/2;j++) { + *dst = *y; + dst++; + y++; + *dst = *v; + dst++; + v++; + *dst = *y; + dst++; + y++; + *dst = *u; + dst++; + u++; + } + } + } + +} + +u32 get_bpp(u32 pf) +{ + switch (pf) { + case GF_PIXEL_RGB_555: + case GF_PIXEL_RGB_565: + return 2; + case GF_PIXEL_RGB_24: + case GF_PIXEL_BGR_24: + return 3; + case GF_PIXEL_RGB_32: + case GF_PIXEL_BGR_32: + case GF_PIXEL_ARGB: + return 4; + } + return 0; +} + +void rgb_to_24(GF_VideoSurface *vs, unsigned char *src, u32 src_stride, u32 src_w, u32 src_h, u32 src_pf, const GF_Window *src_wnd) +{ + u32 i; + u32 BPP = get_bpp(src_pf); + if (!BPP) return; + + /*go to start of src*/ + src += src_stride*src_wnd->y + BPP * src_wnd->x; + + if (src_pf==vs->pixel_format) { + for (i=0; i<src_wnd->h; i++) { + memcpy(vs->video_buffer + i*vs->pitch, src, sizeof(unsigned char) * BPP * src_wnd->w); + src += src_stride; + } + return; + } +} + + +void rgb_to_555(GF_VideoSurface *vs, unsigned char *src, u32 src_stride, u32 src_w, u32 src_h, u32 src_pf, const GF_Window *src_wnd) +{ + u32 i, j, r, g, b; + u32 BPP = get_bpp(src_pf); + unsigned char *dst, *cur; + if (!BPP) return; + + /*go to start of src*/ + src += src_stride*src_wnd->y + BPP * src_wnd->x; + + if (src_pf==vs->pixel_format) { + for (i=0; i<src_wnd->h; i++) { + memcpy(vs->video_buffer + i*vs->pitch, src, sizeof(unsigned char) * BPP * src_wnd->w); + } + return; + } + /*nope get all pixels*/ + for (i=0; i<src_wnd->h; i++) { + dst = vs->video_buffer + i*vs->pitch; + cur = src + i*src_stride; + for (j=0; j<src_wnd->w; j++) { + switch (src_pf) { + case GF_PIXEL_RGB_24: + r = *cur++; + g = *cur++; + b = *cur++; + * ((unsigned short *)dst) = GF_COL_555(r, g, b); + dst += 2; + break; + } + } + } +} + +void rgb_to_565(GF_VideoSurface *vs, unsigned char *src, u32 src_stride, u32 src_w, u32 src_h, u32 src_pf, const GF_Window *src_wnd) +{ + u32 i, j, r, g, b; + u32 BPP = get_bpp(src_pf); + unsigned char *dst, *cur; + if (!BPP) return; + + /*go to start of src*/ + src += src_stride*src_wnd->y + BPP * src_wnd->x; + + if (src_pf==vs->pixel_format) { + for (i=0; i<src_wnd->h; i++) { + memcpy(vs->video_buffer + i*vs->pitch, src, sizeof(unsigned char) * BPP * src_wnd->w); + } + return; + } + /*nope get all pixels*/ + for (i=0; i<src_wnd->h; i++) { + dst = vs->video_buffer + i*vs->pitch; + cur = src + i*src_stride; + for (j=0; j<src_wnd->w; j++) { + switch (src_pf) { + case GF_PIXEL_RGB_24: + r = *cur++; + g = *cur++; + b = *cur++; + * ((unsigned short *)dst) = GF_COL_565(r, g, b); + dst += 2; + break; + } + } + } +} + +void rgb_to_32(GF_VideoSurface *vs, unsigned char *src, u32 src_stride, u32 src_w, u32 src_h, u32 src_pf, const GF_Window *src_wnd) +{ + u32 i, j; + Bool isBGR; + u32 BPP = get_bpp(src_pf); + unsigned char *dst, *cur; + if (!BPP) return; + + /*go to start of src*/ + src += src_stride*src_wnd->y + BPP * src_wnd->x; + + if (src_pf==vs->pixel_format) { + for (i=0; i<src_wnd->h; i++) { + memcpy(vs->video_buffer + i*vs->pitch, src, sizeof(unsigned char) * BPP * src_wnd->w); + } + return; + } + /*get all pixels*/ + isBGR = vs->pixel_format==GF_PIXEL_BGR_32; + if (isBGR) { + for (i=0; i<src_wnd->h; i++) { + dst = vs->video_buffer + i*vs->pitch; + cur = src + i*src_stride; + for (j=0; j<src_wnd->w; j++) { + switch (src_pf) { + case GF_PIXEL_RGB_24: + dst[0] = *cur++; + dst[1] = *cur++; + dst[2] = *cur++; + dst += 4; + break; + case GF_PIXEL_BGR_24: + dst[2] = *cur++; + dst[1] = *cur++; + dst[0] = *cur++; + dst += 4; + break; + } + } + } + } else { + for (i=0; i<src_wnd->h; i++) { + dst = vs->video_buffer + i*vs->pitch; + cur = src + i*src_stride; + for (j=0; j<src_wnd->w; j++) { + switch (src_pf) { + case GF_PIXEL_RGB_24: + dst[2] = *cur++; + dst[1] = *cur++; + dst[0] = *cur++; + dst += 4; + break; + case GF_PIXEL_BGR_24: + dst[0] = *cur++; + dst[1] = *cur++; + dst[2] = *cur++; + dst += 4; + break; + } + } + } + } +} + +void dx_copy_pixels(GF_VideoSurface *dst_s, const GF_VideoSurface *src_s, const GF_Window *src_wnd) +{ + /*handle YUV input*/ + if (get_yuv_base(src_s->pixel_format)==GF_PIXEL_YV12) { + if (format_is_yuv(dst_s->pixel_format)) { + /*generic YV planar to YUV (planar or not) */ + VR_write_yv12_to_yuv(dst_s, src_s->video_buffer, src_s->pitch, src_s->pixel_format, src_s->width, src_s->height, src_wnd); + } else { + gf_stretch_bits(dst_s, (GF_VideoSurface*) src_s, NULL, (GF_Window *)src_wnd, 0, 0xFF, 0, NULL, NULL); + } + } else { + switch (dst_s->pixel_format) { + case GF_PIXEL_RGB_555: + rgb_to_555(dst_s, src_s->video_buffer, src_s->pitch, src_s->width, src_s->height, src_s->pixel_format, src_wnd); + break; + case GF_PIXEL_RGB_565: + rgb_to_565(dst_s, src_s->video_buffer, src_s->pitch, src_s->width, src_s->height, src_s->pixel_format, src_wnd); + break; + case GF_PIXEL_RGB_24: + case GF_PIXEL_BGR_24: + rgb_to_24(dst_s, src_s->video_buffer, src_s->pitch, src_s->width, src_s->height, src_s->pixel_format, src_wnd); + break; + case GF_PIXEL_RGB_32: + case GF_PIXEL_BGR_32: + rgb_to_32(dst_s, src_s->video_buffer, src_s->pitch, src_s->width, src_s->height, src_s->pixel_format, src_wnd); + break; + } + } +} + + diff --git a/modules/dx_hw/dx_2d.c b/modules/dx_hw/dx_2d.c new file mode 100644 index 0000000..dbbeb55 --- /dev/null +++ b/modules/dx_hw/dx_2d.c @@ -0,0 +1,796 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / DirectX audio and video render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "dx_hw.h" + +#define DDCONTEXT DDContext *dd = (DDContext *)dr->opaque; +#define DDBACK DDSurface *pBack = (DDSurface *) gf_list_get(dd->surfaces, 0); + + + + +static Bool pixelformat_yuv(u32 pixel_format) +{ + switch (pixel_format) { + case GF_PIXEL_YUY2: + case GF_PIXEL_YVYU: + case GF_PIXEL_UYVY: + case GF_PIXEL_VYUY: + case GF_PIXEL_Y422: + case GF_PIXEL_UYNV: + case GF_PIXEL_YUNV: + case GF_PIXEL_V422: + case GF_PIXEL_YV12: + case GF_PIXEL_IYUV: + case GF_PIXEL_I420: + return 1; + default: + return 0; + } +} + + +static u32 get_win_4CC(u32 pixel_format) +{ + switch (pixel_format) { + case GF_PIXEL_YUY2: + return mmioFOURCC('Y', 'U', 'Y', '2'); + case GF_PIXEL_YVYU: + return mmioFOURCC('Y', 'V', 'Y', 'U'); + case GF_PIXEL_UYVY: + return mmioFOURCC('U', 'Y', 'V', 'Y'); + case GF_PIXEL_VYUY: + return mmioFOURCC('V', 'Y', 'U', 'Y'); + case GF_PIXEL_Y422: + return mmioFOURCC('Y', '4', '2', '2'); + case GF_PIXEL_UYNV: + return mmioFOURCC('U', 'Y', 'N', 'V'); + case GF_PIXEL_YUNV: + return mmioFOURCC('Y', 'U', 'N', 'V'); + case GF_PIXEL_V422: + return mmioFOURCC('V', '4', '2', '2'); + case GF_PIXEL_YV12: + return mmioFOURCC('Y', 'V', '1', '2'); + case GF_PIXEL_IYUV: + return mmioFOURCC('I', 'Y', 'U', 'V'); + case GF_PIXEL_I420: + return mmioFOURCC('I', '4', '2', '0'); + default: + return 0; + } +} + +static GF_Err DD_ClearBackBuffer(GF_VideoOutput *dr, u32 color) +{ + HRESULT hr; + RECT rc; + DDBLTFX ddbltfx; + DDCONTEXT; + + // Erase the background + ZeroMemory( &ddbltfx, sizeof(ddbltfx) ); + ddbltfx.dwSize = sizeof(ddbltfx); + switch (dd->pixelFormat) { + case GF_PIXEL_RGB_565: + ddbltfx.dwFillColor = GF_COL_TO_565(color); + break; + case GF_PIXEL_RGB_555: + ddbltfx.dwFillColor = GF_COL_TO_555(color); + break; + default: + ddbltfx.dwFillColor = color; + break; + } + rc.left = rc.top = 0; + rc.right = dd->width; + rc.bottom = dd->height; +#ifdef USE_DX_3 + hr = IDirectDrawSurface_Blt(dd->pBack, &rc, NULL, NULL, DDBLT_COLORFILL, &ddbltfx ); +#else + hr = IDirectDrawSurface7_Blt(dd->pBack, &rc, NULL, NULL, DDBLT_COLORFILL, &ddbltfx ); +#endif + return FAILED(hr) ? GF_IO_ERR : GF_OK; +} + +GF_Err CreateBackBuffer(GF_VideoOutput *dr, u32 Width, u32 Height, Bool use_system_memory) +{ + Bool force_reinit; + HRESULT hr; + const char *opt; +#ifdef USE_DX_3 + DDSURFACEDESC ddsd; +#else + DDSURFACEDESC2 ddsd; +#endif + + DDCONTEXT; + + + force_reinit = 0; + if (use_system_memory && !dd->systems_memory) force_reinit = 1; + else if (!use_system_memory && dd->systems_memory) force_reinit = 1; + if (dd->pBack && !force_reinit&& !dd->fullscreen && (dd->width == Width) && (dd->height == Height) ) { + return GF_OK; + } + + if (dd->pBack) SAFE_DD_RELEASE(dd->pBack); + + /*create backbuffer*/ + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + + if (dd->systems_memory==2) { + ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + } else { + opt = NULL; + if (use_system_memory) { + opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "UseHardwareMemory"); + if (opt && !strcmp(opt, "yes")) use_system_memory = 0; + } + if (!use_system_memory) { + dd->systems_memory = 0; + ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; + } else { + ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + dd->systems_memory = 1; + } + } + + ddsd.dwWidth = Width; + ddsd.dwHeight = Height; + +#ifdef USE_DX_3 + hr = IDirectDraw_CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); +#else + hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); +#endif + if (FAILED(hr)) { + if (!dd->systems_memory) { + ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY; + ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + dd->systems_memory = 1; + if (opt && !strcmp(opt, "yes")) { + gf_modules_set_option((GF_BaseInterface *)dr, "Video", "UseHardwareMemory", "no"); + + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Hardware Video not available for backbuffer)\n")); + } +#ifdef USE_DX_3 + hr = IDirectDraw_CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); +#else + hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); +#endif + } + if (FAILED(hr)) return GF_IO_ERR; + } + + /*store size*/ + if (!dd->fullscreen) { + dd->width = Width; + dd->height = Height; + } else { + dd->fs_store_width = Width; + dd->fs_store_height = Height; + } + DD_ClearBackBuffer(dr, 0xFF000000); + + if (!dd->yuv_init) DD_InitYUV(dr); + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] BackBuffer %d x %d created on %s memory\n", Width, Height, dd->systems_memory ? "System" : "Video")); + return GF_OK; +} + +GF_Err InitDirectDraw(GF_VideoOutput *dr, u32 Width, u32 Height) +{ + HRESULT hr; + DWORD cooplev; + LPDIRECTDRAW ddraw; +#ifdef USE_DX_3 + DDSURFACEDESC ddsd; +#else + DDSURFACEDESC2 ddsd; +#endif + DDPIXELFORMAT pixelFmt; + LPDIRECTDRAWCLIPPER pcClipper; + DDCONTEXT; + + if (!dd->cur_hwnd || !Width || !Height) return GF_BAD_PARAM; + DestroyObjects(dd); + + if( FAILED( hr = DirectDrawCreate(NULL, &ddraw, NULL ) ) ) + return GF_IO_ERR; + +#ifdef USE_DX_3 + hr = IDirectDraw_QueryInterface(ddraw, &IID_IDirectDraw, (LPVOID *)&dd->pDD); +#else + hr = IDirectDraw_QueryInterface(ddraw, &IID_IDirectDraw7, (LPVOID *)&dd->pDD); +#endif + IDirectDraw_Release(ddraw); + if (FAILED(hr)) return GF_IO_ERR; + + dd->switch_res = 0; + cooplev = DDSCL_NORMAL; + /*Setup FS*/ + if (dd->fullscreen) { + + /*change display mode*/ + if (dd->switch_res) { +#ifdef USE_DX_3 + hr = IDirectDraw_SetDisplayMode(dd->pDD, dd->fs_width, dd->fs_height, dd->video_bpp); +#else + hr = IDirectDraw7_SetDisplayMode(dd->pDD, dd->fs_width, dd->fs_height, dd->video_bpp, 0, 0 ); +#endif + if( FAILED(hr)) return GF_IO_ERR; + } + dd->NeedRestore = 1; + cooplev = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN; + } + + +#ifdef USE_DX_3 + hr = IDirectDraw_SetCooperativeLevel(dd->pDD, dd->cur_hwnd, cooplev); +#else + hr = IDirectDraw7_SetCooperativeLevel(dd->pDD, dd->cur_hwnd, cooplev); +#endif + if( FAILED(hr) ) return GF_IO_ERR; + + /*create primary*/ + ZeroMemory( &ddsd, sizeof( ddsd ) ); + ddsd.dwSize = sizeof( ddsd ); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + +#ifdef USE_DX_3 + if( FAILED(IDirectDraw_CreateSurface(dd->pDD, &ddsd, &dd->pPrimary, NULL ) ) ) + return GF_IO_ERR; +#else + if( FAILED(hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &dd->pPrimary, NULL ) ) ) + return GF_IO_ERR; +#endif + + /*get pixel format of video board*/ + memset (&pixelFmt, 0, sizeof(pixelFmt)); + pixelFmt.dwSize = sizeof(pixelFmt); + hr = IDirectDrawSurface_GetPixelFormat(dd->pPrimary, &pixelFmt); + if( FAILED(hr) ) return GF_IO_ERR; + + switch(pixelFmt.dwRGBBitCount) { + case 16: + if ((pixelFmt.dwRBitMask == 0xf800) && (pixelFmt.dwGBitMask == 0x07e0) && (pixelFmt.dwBBitMask == 0x001f)) + dd->pixelFormat = GF_PIXEL_RGB_565; + else if ((pixelFmt.dwRBitMask == 0x7c00) && (pixelFmt.dwGBitMask == 0x03e0) && (pixelFmt.dwBBitMask == 0x001f)) + dd->pixelFormat = GF_PIXEL_RGB_555; + else + return GF_NOT_SUPPORTED; + dd->video_bpp = 16; + break; + case 24: + if ((pixelFmt.dwRBitMask == 0x0000FF) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0xFF0000)) + dd->pixelFormat = GF_PIXEL_BGR_24; + else if ((pixelFmt.dwRBitMask == 0xFF0000) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0x0000FF)) + dd->pixelFormat = GF_PIXEL_RGB_24; + dd->video_bpp = 24; + break; + case 32: + /*i always have color pbs in 32 bpp !!!*/ + if ((pixelFmt.dwRBitMask == 0x0000FF) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0xFF0000)) + dd->pixelFormat = GF_PIXEL_BGR_32; + else if ((pixelFmt.dwRBitMask == 0xFF0000) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0x0000FF)) + dd->pixelFormat = GF_PIXEL_RGB_32; + dd->video_bpp = 32; + break; + default: + return GF_IO_ERR; + } + + +#ifdef USE_DX_3 + if( FAILED( hr = IDirectDraw_CreateClipper(dd->pDD, 0, &pcClipper, NULL ) ) ) + return GF_IO_ERR; +#else + if( FAILED( hr = IDirectDraw7_CreateClipper(dd->pDD, 0, &pcClipper, NULL ) ) ) + return GF_IO_ERR; +#endif + + if( FAILED( hr = IDirectDrawClipper_SetHWnd(pcClipper, 0, dd->cur_hwnd) ) ) { + IDirectDrawClipper_Release(pcClipper); + return GF_IO_ERR; + } + if( FAILED( hr = IDirectDrawSurface_SetClipper(dd->pPrimary, pcClipper ) ) ) { + IDirectDrawClipper_Release(pcClipper); + return GF_IO_ERR; + } + IDirectDrawClipper_Release(pcClipper); + dd->ddraw_init = 1; + /*if YUV not initialize, init using HW video memory to setup HW caps*/ + return CreateBackBuffer(dr, Width, Height, dd->yuv_init); +} + +static GF_Err DD_LockSurface(DDContext *dd, GF_VideoSurface *vi, void *surface) +{ + HRESULT hr; +#ifdef USE_DX_3 + DDSURFACEDESC desc; +#else + DDSURFACEDESC2 desc; +#endif + + if (!dd || !vi || !surface) return GF_BAD_PARAM; + +#ifdef USE_DX_3 + ZeroMemory(&desc, sizeof(DDSURFACEDESC)); + desc.dwSize = sizeof(DDSURFACEDESC); + if (FAILED(hr = IDirectDrawSurface_Lock( (LPDIRECTDRAWSURFACE)surface, NULL, &desc, DDLOCK_SURFACEMEMORYPTR | /*DDLOCK_WRITEONLY | */ DDLOCK_WAIT, NULL))) { + return GF_IO_ERR; + } +#else + ZeroMemory(&desc, sizeof(DDSURFACEDESC2)); + desc.dwSize = sizeof(DDSURFACEDESC2); + if (FAILED(hr = IDirectDrawSurface7_Lock( (LPDIRECTDRAWSURFACE7) surface, NULL, &desc, DDLOCK_SURFACEMEMORYPTR | /*DDLOCK_WRITEONLY | */ DDLOCK_WAIT, NULL))) { + return GF_IO_ERR; + } +#endif + vi->video_buffer = desc.lpSurface; + vi->width = desc.dwWidth; + vi->height = desc.dwHeight; + vi->pitch = desc.lPitch; + vi->is_hardware_memory = dd->systems_memory ? 0 : 1; + return GF_OK; +} + +static GF_Err DD_UnlockSurface(DDContext *dd, void *surface) +{ + HRESULT hr; + if (!dd || !dd->ddraw_init) return GF_IO_ERR; +#ifdef USE_DX_3 + hr = IDirectDrawSurface_Unlock( (LPDIRECTDRAWSURFACE)surface, NULL); +#else + hr = IDirectDrawSurface7_Unlock((LPDIRECTDRAWSURFACE7)surface, NULL); +#endif + return FAILED(hr) ? GF_IO_ERR : GF_OK; +} + + +static GF_Err DD_LockBackBuffer(GF_VideoOutput *dr, GF_VideoSurface *vi, Bool do_lock) +{ + DDCONTEXT; + + if (do_lock) { + vi->pixel_format = dd->pixelFormat; + return DD_LockSurface(dd, vi, dd->pBack); + } + else return DD_UnlockSurface(dd, dd->pBack); +} + +static void *LockOSContext(GF_VideoOutput *dr, Bool do_lock) +{ + DDCONTEXT; + + if (!dd->pBack) return NULL; + + if (do_lock) { + if (!dd->lock_hdc && ! IDirectDrawSurface_IsLost(dd->pBack)) { + if (FAILED(IDirectDrawSurface_GetDC(dd->pBack, &dd->lock_hdc)) ) + dd->lock_hdc = NULL; + } + } else if (dd->lock_hdc) { + IDirectDrawSurface_ReleaseDC(dd->pBack, dd->lock_hdc); + dd->lock_hdc = NULL; + } + return (void *)dd->lock_hdc ; +} + + +static GF_Err DD_BlitSurface(DDContext *dd, DDSurface *src, GF_Window *src_wnd, GF_Window *dst_wnd, GF_ColorKey *key) +{ + HRESULT hr; + u32 dst_w, dst_h, src_w, src_h, flags; + RECT r_dst, r_src; + u32 left, top; + src_w = src_wnd ? src_wnd->w : src->width; + src_h = src_wnd ? src_wnd->h : src->height; + dst_w = dst_wnd ? dst_wnd->w : dd->width; + dst_h = dst_wnd ? dst_wnd->h : dd->height; + + if (src_wnd != NULL) MAKERECT(r_src, src_wnd); + if (dst_wnd != NULL) MAKERECT(r_dst, dst_wnd); + + if (key) { + u32 col; + DDCOLORKEY ck; + col = GF_COL_ARGB(0xFF, key->r, key->g, key->b); + ck.dwColorSpaceHighValue = ck.dwColorSpaceLowValue = col; + hr = IDirectDrawSurface_SetColorKey(src->pSurface, DDCKEY_SRCBLT, &ck); + if (FAILED(hr)) return GF_IO_ERR; + } + + if ((dst_w==src_w) && (dst_h==src_h)) { + flags = DDBLTFAST_WAIT; + if (key) flags |= DDBLTFAST_SRCCOLORKEY; + if (dst_wnd) { left = r_dst.left; top = r_dst.top; } else left = top = 0; + hr = IDirectDrawSurface_BltFast(dd->pBack, left, top, src->pSurface, src_wnd ? &r_src : NULL, flags); + } else { + flags = DDBLT_WAIT; + if (key) flags |= DDBLT_KEYSRC; + hr = IDirectDrawSurface_Blt(dd->pBack, dst_wnd ? &r_dst : NULL, src->pSurface, src_wnd ? &r_src : NULL, flags, NULL); + } + return FAILED(hr) ? GF_IO_ERR : GF_OK; +} + +static DDSurface *DD_GetSurface(GF_VideoOutput *dr, u32 width, u32 height, u32 pixel_format) +{ +#ifdef USE_DX_3 + DDSURFACEDESC ddsd; +#else + DDSURFACEDESC2 ddsd; +#endif + HRESULT hr; + DDCONTEXT; + + /*yuv format*/ + if (pixelformat_yuv(pixel_format)) { + if (dr->yuv_pixel_format) { + DDSurface *yuvp = &dd->yuv_pool; + + /*don't recreate a surface if not needed*/ + if (yuvp->pSurface && (yuvp->width >= width) && (yuvp->height >= height) ) return yuvp; + + width = MAX(yuvp->width, width); + height = MAX(yuvp->height, height); + + SAFE_DD_RELEASE(yuvp->pSurface); + memset(yuvp, 0, sizeof(DDSurface)); + + memset (&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY; + ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; + ddsd.dwWidth = width; + ddsd.dwHeight = height; + ddsd.ddpfPixelFormat.dwFourCC = get_win_4CC(dr->yuv_pixel_format); + +#ifdef USE_DX_3 + if( FAILED( hr = IDirectDraw_CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL ) ) ) + return NULL; +#else + if( FAILED( hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL ) ) ) + return NULL; +#endif + yuvp->format = dr->yuv_pixel_format; + yuvp->width = width; + yuvp->height = height; + + + return yuvp; + } + } + + /*don't recreate a surface if not needed*/ + if ((dd->rgb_pool.width >= width) && (dd->rgb_pool.height >= height) ) return &dd->rgb_pool; + width = MAX(dd->rgb_pool.width, width); + height = MAX(dd->rgb_pool.height, height); + SAFE_DD_RELEASE(dd->rgb_pool.pSurface); + memset(&dd->rgb_pool, 0, sizeof(DDSurface)); + + memset (&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + ddsd.dwWidth = width; + ddsd.dwHeight = height; + +#ifdef USE_DX_3 + if( FAILED( hr = IDirectDraw_CreateSurface(dd->pDD, &ddsd, &dd->rgb_pool.pSurface, NULL ) ) ) + return NULL; +#else + if( FAILED( hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &dd->rgb_pool.pSurface, NULL ) ) ) + return NULL; +#endif + + dd->rgb_pool.width = width; + dd->rgb_pool.height = height; + dd->rgb_pool.format = dd->pixelFormat; + return &dd->rgb_pool; +} + +static GF_Err DD_Blit(GF_VideoOutput *dr, GF_VideoSurface *video_src, GF_Window *src_wnd, GF_Window *dst_wnd, u32 overlay_type) +{ + GF_VideoSurface temp_surf; + GF_Err e; + GF_Window src_wnd_2; + u32 w, h; + DDSurface *pool; + DDCONTEXT; + + if (src_wnd) { + w = src_wnd->w; + h = src_wnd->h; + } else { + w = video_src->width; + h = video_src->height; + } + /*get RGB or YUV pool surface*/ + pool = DD_GetSurface(dr, w, h, video_src->pixel_format); + if (!pool) return GF_IO_ERR; + + temp_surf.pixel_format = pool->format; + e = DD_LockSurface(dd, &temp_surf, pool->pSurface); + if (e) return e; + + /*copy pixels to pool*/ + dx_copy_pixels(&temp_surf, video_src, src_wnd); + + e = DD_UnlockSurface(dd, pool->pSurface); + if (e) return e; + + if (overlay_type) { + HRESULT hr; + RECT dst_rc, src_rc; + POINT pt; + GF_Window dst = *dst_wnd; + GF_Window src = *src_wnd; + + src.x = src.y = 0; + MAKERECT(src_rc, (&src)); + pt.x = dst.x; + pt.y = dst.y; + ClientToScreen(dd->cur_hwnd, &pt); + dst.x = pt.x; + dst.y = pt.y; + MAKERECT(dst_rc, (&dst)); + +#if 1 + if (overlay_type==1) { + hr = IDirectDrawSurface2_UpdateOverlay(pool->pSurface, &src_rc, dd->pPrimary, &dst_rc, DDOVER_SHOW, NULL); + } else { + DDOVERLAYFX ddofx; + memset(&ddofx, 0, sizeof(DDOVERLAYFX)); + ddofx.dwSize = sizeof(DDOVERLAYFX); + ddofx.dckDestColorkey.dwColorSpaceLowValue = dr->overlay_color_key; + ddofx.dckDestColorkey.dwColorSpaceHighValue = dr->overlay_color_key; + hr = IDirectDrawSurface2_UpdateOverlay(pool->pSurface, &src_rc, dd->pPrimary, &dst_rc, DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE, &ddofx); + } + if (FAILED(hr)) { + IDirectDrawSurface2_UpdateOverlay(pool->pSurface, NULL, dd->pPrimary, NULL, DDOVER_HIDE, NULL); + } +#else + if (overlay_type==1) { + hr = IDirectDrawSurface_Blt(dd->pPrimary, &dst_rc, pool->pSurface, &src_rc, 0, NULL); + } else { + DDBLTFX ddfx; + memset(&ddfx, 0, sizeof(DDBLTFX)); + ddfx.dwSize = sizeof(DDBLTFX); + ddfx.ddckDestColorkey.dwColorSpaceLowValue = dr->overlay_color_key; + ddfx.ddckDestColorkey.dwColorSpaceHighValue = dr->overlay_color_key; + + hr = IDirectDrawSurface_Blt(dd->pPrimary, &dst_rc, pool->pSurface, &src_rc, DDBLT_WAIT | DDBLT_KEYDESTOVERRIDE, &ddfx); + } +#endif + return FAILED(hr) ? GF_IO_ERR : GF_OK; + } + + src_wnd_2.x = src_wnd_2.y = 0; + if (src_wnd) { + src_wnd_2.w = src_wnd->w; + src_wnd_2.h = src_wnd->h; + } else { + src_wnd_2.w = video_src->width; + src_wnd_2.h = video_src->height; + } + /*and blit surface*/ + return DD_BlitSurface(dd, pool, &src_wnd_2, dst_wnd, NULL); +} + + + +static GFINLINE u32 is_yuv_supported(u32 win_4cc) +{ + if (win_4cc==get_win_4CC(GF_PIXEL_YV12)) return GF_PIXEL_YV12; + else if (win_4cc==get_win_4CC(GF_PIXEL_I420)) return GF_PIXEL_I420; + else if (win_4cc==get_win_4CC(GF_PIXEL_IYUV)) return GF_PIXEL_IYUV; + else if (win_4cc==get_win_4CC(GF_PIXEL_UYVY)) return GF_PIXEL_UYVY; + else if (win_4cc==get_win_4CC(GF_PIXEL_Y422)) return GF_PIXEL_Y422; + else if (win_4cc==get_win_4CC(GF_PIXEL_UYNV)) return GF_PIXEL_UYNV; + else if (win_4cc==get_win_4CC(GF_PIXEL_YUY2)) return GF_PIXEL_YUY2; + else if (win_4cc==get_win_4CC(GF_PIXEL_YUNV)) return GF_PIXEL_YUNV; + else if (win_4cc==get_win_4CC(GF_PIXEL_V422)) return GF_PIXEL_V422; + else if (win_4cc==get_win_4CC(GF_PIXEL_YVYU)) return GF_PIXEL_YVYU; + else return 0; +} + +static GFINLINE Bool is_yuv_planar(u32 format) +{ + switch (format) { + case GF_PIXEL_YV12: + case GF_PIXEL_I420: + case GF_PIXEL_IYUV: + return 1; + default: + return 0; + } +} + +#define YUV_NUM_TEST 20 + +/*gets fastest YUV format for YUV to RGB blit from YUV overlay (support is quite random on most cards)*/ +void DD_InitYUV(GF_VideoOutput *dr) +{ + u32 w, h, j, i, num_yuv; + DWORD numCodes; + DWORD formats[30]; + DWORD *codes; + u32 now, min_packed = 0xFFFFFFFF, min_planar = 0xFFFFFFFF; + u32 best_packed = 0, best_planar = 0; + Bool checkPacked = TRUE; + const char *opt; + + DDCONTEXT; + + w = 720; + h = 576; + + dd->yuv_init = 1; + +#ifdef USE_DX_3 + IDirectDraw_GetFourCCCodes(dd->pDD, &numCodes, NULL); + if (!numCodes) return; + codes = (DWORD *)malloc(numCodes*sizeof(DWORD)); + IDirectDraw_GetFourCCCodes(dd->pDD, &numCodes, codes); +#else + IDirectDraw7_GetFourCCCodes(dd->pDD, &numCodes, NULL); + if (!numCodes) return; + codes = (DWORD *)malloc(numCodes*sizeof(DWORD)); + IDirectDraw7_GetFourCCCodes(dd->pDD, &numCodes, codes); +#endif + + num_yuv = 0; + for (i=0; i<numCodes; i++) { + formats[num_yuv] = is_yuv_supported(codes[i]); + if (formats[num_yuv]) num_yuv++; + } + free(codes); + /*too bad*/ + if (!num_yuv) { + dr->hw_caps &= ~(GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_YUV_OVERLAY); + dr->yuv_pixel_format = 0; + return; + } + + for (i=0; i<num_yuv; i++) { + /*check planar first*/ + if (!checkPacked && !is_yuv_planar(formats[i])) goto go_on; + /*then check packed */ + if (checkPacked && is_yuv_planar(formats[i])) goto go_on; + + if (dd->yuv_pool.pSurface) { + SAFE_DD_RELEASE(dd->yuv_pool.pSurface); + memset(&dd->yuv_pool, 0, sizeof(DDSurface)); + } + + dr->yuv_pixel_format = formats[i]; + if (DD_GetSurface(dr, w, h, dr->yuv_pixel_format) == NULL) + goto rem_fmt; + + now = gf_sys_clock(); + /*perform blank blit*/ + for (j=0; j<YUV_NUM_TEST; j++) { + if (DD_BlitSurface(dd, &dd->yuv_pool, NULL, NULL, NULL) != GF_OK) + goto rem_fmt; + } + now = gf_sys_clock() - now; + + if (!checkPacked) { + if (now<min_planar) { + min_planar = now; + best_planar = dr->yuv_pixel_format; + } + } else { + if (now<min_packed) { + min_packed = now; + best_packed = dr->yuv_pixel_format; + } + } + +go_on: + if (checkPacked && (i+1==num_yuv)) { + i = -1; + checkPacked = FALSE; + } + continue; + +rem_fmt: + for (j=i; j<num_yuv-1; j++) { + formats[j] = formats[j+1]; + } + i--; + num_yuv--; + } + + if (dd->yuv_pool.pSurface) { + SAFE_DD_RELEASE(dd->yuv_pool.pSurface); + memset(&dd->yuv_pool, 0, sizeof(DDSurface)); + } + /*too bad*/ + if (!num_yuv) { + dr->hw_caps &= ~(GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_YUV_OVERLAY); + dr->yuv_pixel_format = 0; + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] YUV hardware not available\n")); + return; + } + + if (best_planar && (min_planar < min_packed )) { + dr->yuv_pixel_format = best_planar; + } else { + min_planar = min_packed; + dr->yuv_pixel_format = best_packed; + } + dr->yuv_pixel_format = GF_PIXEL_YV12; + + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Picked YUV format %s - drawn in %d ms\n", gf_4cc_to_str(dr->yuv_pixel_format), min_planar)); + dr->hw_caps |= GF_VIDEO_HW_HAS_YUV_OVERLAY; + + /*enable YUV->RGB on the backbuffer ?*/ + opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "EnableOffscreenYUV"); + /*no set is the default*/ + if (!opt) { + opt = "yes"; + gf_modules_set_option((GF_BaseInterface *)dr, "Video", "EnableOffscreenYUV", "yes"); + } + if (!strcmp(opt, "yes")) dr->hw_caps |= GF_VIDEO_HW_HAS_YUV; + + /*get YUV overlay key*/ + opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "OverlayColorKey"); + /*no set is the default*/ + if (!opt) { + opt = "0101FE"; + gf_modules_set_option((GF_BaseInterface *)dr, "Video", "OverlayColorKey", "0101FE"); + } + sscanf(opt, "%06x", &dr->overlay_color_key); + if (dr->overlay_color_key) dr->overlay_color_key |= 0xFF000000; + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] YUV->RGB enabled: %s - ColorKey enabled: %s (key %x)\n", + (dr->hw_caps & GF_VIDEO_HW_HAS_YUV) ? "Yes" : "No", + dr->overlay_color_key ? "Yes" : "No", dr->overlay_color_key + )); +} + +GF_Err DD_SetBackBufferSize(GF_VideoOutput *dr, u32 width, u32 height, Bool use_system_memory) +{ + DDCONTEXT; + if (dd->output_3d_type) return GF_BAD_PARAM; + if (!dd->ddraw_init) return InitDirectDraw(dr, width, height); + return CreateBackBuffer(dr, width, height, use_system_memory); +} + + +void DD_SetupDDraw(GF_VideoOutput *driv) +{ + driv->hw_caps |= GF_VIDEO_HW_HAS_COLOR_KEY; + driv->Blit = DD_Blit; + driv->LockBackBuffer = DD_LockBackBuffer; + driv->LockOSContext = LockOSContext; +} + diff --git a/modules/dx_hw/dx_audio.c b/modules/dx_hw/dx_audio.c new file mode 100644 index 0000000..7565ffe --- /dev/null +++ b/modules/dx_hw/dx_audio.c @@ -0,0 +1,456 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / DirectX audio and video render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "dx_hw.h" + +#if (DIRECTSOUND_VERSION >= 0x0800) +#define USE_WAVE_EX +#endif + +#ifdef USE_WAVE_EX +#include <ks.h> +#include <ksmedia.h> +const static GUID GPAC_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001,0x0000,0x0010, +{0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}}; +#endif + + + +/* + DirectSound audio output +*/ +#define MAX_NUM_BUFFERS 20 + +typedef struct +{ + Bool force_config; + u32 cfg_num_buffers, cfg_duration; + + HWND hWnd; + LPDIRECTSOUND pDS; + WAVEFORMATEX format; + IDirectSoundBuffer *pOutput; + + u32 buffer_size, num_audio_buffer, total_audio_buffer_ms; + + /*notifs*/ + Bool use_notif; + u32 frame_state[MAX_NUM_BUFFERS]; + DSBPOSITIONNOTIFY notif_events[MAX_NUM_BUFFERS]; + HANDLE events[MAX_NUM_BUFFERS]; +} DSContext; + +#define DSCONTEXT() DSContext *ctx = (DSContext *)dr->opaque; + +void DS_WriteAudio(GF_AudioOutput *dr); +void DS_WriteAudio_Notifs(GF_AudioOutput *dr); + +static GF_Err DS_Setup(GF_AudioOutput *dr, void *os_handle, u32 num_buffers, u32 total_duration) +{ + DWORD flags; + HRESULT hr; + + DSCONTEXT(); + ctx->hWnd = (HWND) os_handle; + /*check if we have created a HWND (this requires that video is handled by the DX module*/ + if (!ctx->hWnd) ctx->hWnd = DD_GetGlobalHWND(); + /*too bad, use desktop as window*/ + if (!ctx->hWnd) ctx->hWnd = GetDesktopWindow(); + + ctx->force_config = (num_buffers && total_duration) ? 1 : 0; + ctx->cfg_num_buffers = num_buffers; + ctx->cfg_duration = total_duration; + if (ctx->cfg_num_buffers <= 1) ctx->cfg_num_buffers = 2; + + if ( FAILED( hr = DirectSoundCreate( NULL, &ctx->pDS, NULL ) ) ) return GF_IO_ERR; + flags = DSSCL_EXCLUSIVE; + if( FAILED( hr = IDirectSound_SetCooperativeLevel(ctx->pDS, ctx->hWnd, DSSCL_EXCLUSIVE) ) ) { + SAFE_DS_RELEASE( ctx->pDS ); + return GF_IO_ERR; + } + return GF_OK; +} + + +void DS_ResetBuffer(DSContext *ctx) +{ + VOID *pLock = NULL; + DWORD size; + + if( FAILED(IDirectSoundBuffer_Lock(ctx->pOutput, 0, ctx->buffer_size*ctx->num_audio_buffer, &pLock, &size, NULL, NULL, 0 ) ) ) + return; + memset(pLock, 0, (size_t) size); + IDirectSoundBuffer_Unlock(ctx->pOutput, pLock, size, NULL, 0L); +} + +void DS_ReleaseBuffer(GF_AudioOutput *dr) +{ + u32 i; + DSCONTEXT(); + + /*stop playing and notif proc*/ + if (ctx->pOutput) IDirectSoundBuffer_Stop(ctx->pOutput); + SAFE_DS_RELEASE(ctx->pOutput); + + /*use notif, shutdown notifier and event watcher*/ + if (ctx->use_notif) { + for (i=0; i<ctx->num_audio_buffer; i++) CloseHandle(ctx->events[i]); + } + ctx->use_notif = 0; +} + +static void DS_Shutdown(GF_AudioOutput *dr) +{ + DSCONTEXT(); + DS_ReleaseBuffer(dr); + SAFE_DS_RELEASE(ctx->pDS ); +} + +/*we assume what was asked is what we got*/ +static GF_Err DS_ConfigureOutput(GF_AudioOutput *dr, u32 *SampleRate, u32 *NbChannels, u32 *nbBitsPerSample, u32 channel_cfg) +{ + u32 i; + HRESULT hr; + const char *sOpt; + DSBUFFERDESC dsbBufferDesc; + IDirectSoundNotify *pNotify; +#ifdef USE_WAVE_EX + WAVEFORMATEXTENSIBLE format_ex; +#endif + DSCONTEXT(); + + DS_ReleaseBuffer(dr); + + ctx->format.nChannels = *NbChannels; + ctx->format.wBitsPerSample = *nbBitsPerSample; + ctx->format.nSamplesPerSec = *SampleRate; + ctx->format.cbSize = sizeof (WAVEFORMATEX); + ctx->format.wFormatTag = WAVE_FORMAT_PCM; + ctx->format.nBlockAlign = ctx->format.nChannels * ctx->format.wBitsPerSample / 8; + ctx->format.nAvgBytesPerSec = ctx->format.nSamplesPerSec * ctx->format.nBlockAlign; + + if (!ctx->force_config) { + ctx->buffer_size = ctx->format.nBlockAlign * 1024; + ctx->num_audio_buffer = 2; + } else { + ctx->num_audio_buffer = ctx->cfg_num_buffers; + ctx->buffer_size = (ctx->format.nAvgBytesPerSec * ctx->cfg_duration) / (1000 * ctx->cfg_num_buffers); + } + + /*make sure we're aligned*/ + while (ctx->buffer_size % ctx->format.nBlockAlign) ctx->buffer_size++; + + ctx->use_notif = 1; + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "Audio", "DisableNotification"); + if (sOpt && !stricmp(sOpt, "yes")) ctx->use_notif = 0; + + memset(&dsbBufferDesc, 0, sizeof(DSBUFFERDESC)); + dsbBufferDesc.dwSize = sizeof (DSBUFFERDESC); + dsbBufferDesc.dwBufferBytes = ctx->buffer_size * ctx->num_audio_buffer; + dsbBufferDesc.lpwfxFormat = &ctx->format; + dsbBufferDesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME; + if (ctx->use_notif) dsbBufferDesc.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY; + +#ifdef USE_WAVE_EX + if (channel_cfg && ctx->format.nChannels>2) { + memset(&format_ex, 0, sizeof(WAVEFORMATEXTENSIBLE)); + format_ex.Format = ctx->format; + format_ex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE); + format_ex.SubFormat = GPAC_KSDATAFORMAT_SUBTYPE_PCM; + format_ex.Samples.wValidBitsPerSample = *nbBitsPerSample; + format_ex.dwChannelMask = 0; + if (channel_cfg & GF_AUDIO_CH_FRONT_LEFT) format_ex.dwChannelMask |= SPEAKER_FRONT_LEFT; + if (channel_cfg & GF_AUDIO_CH_FRONT_RIGHT) format_ex.dwChannelMask |= SPEAKER_FRONT_RIGHT; + if (channel_cfg & GF_AUDIO_CH_FRONT_CENTER) format_ex.dwChannelMask |= SPEAKER_FRONT_CENTER; + if (channel_cfg & GF_AUDIO_CH_LFE) format_ex.dwChannelMask |= SPEAKER_LOW_FREQUENCY; + if (channel_cfg & GF_AUDIO_CH_BACK_LEFT) format_ex.dwChannelMask |= SPEAKER_BACK_LEFT; + if (channel_cfg & GF_AUDIO_CH_BACK_RIGHT) format_ex.dwChannelMask |= SPEAKER_BACK_RIGHT; + if (channel_cfg & GF_AUDIO_CH_BACK_CENTER) format_ex.dwChannelMask |= SPEAKER_BACK_CENTER; + if (channel_cfg & GF_AUDIO_CH_SIDE_LEFT) format_ex.dwChannelMask |= SPEAKER_SIDE_LEFT; + if (channel_cfg & GF_AUDIO_CH_SIDE_RIGHT) format_ex.dwChannelMask |= SPEAKER_SIDE_RIGHT; + dsbBufferDesc.lpwfxFormat = (WAVEFORMATEX *) &format_ex; + } +#endif + + + hr = IDirectSound_CreateSoundBuffer(ctx->pDS, &dsbBufferDesc, &ctx->pOutput, NULL ); + if (FAILED(hr)) { +retry: + if (ctx->use_notif) gf_modules_set_option((GF_BaseInterface *)dr, "Audio", "DisableNotification", "yes"); + ctx->use_notif = 0; + dsbBufferDesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; + hr = IDirectSound_CreateSoundBuffer(ctx->pDS, &dsbBufferDesc, &ctx->pOutput, NULL ); + if (FAILED(hr)) return GF_IO_ERR; + } + + for (i=0; i<ctx->num_audio_buffer; i++) ctx->frame_state[i] = 0; + + if (ctx->use_notif) { + hr = IDirectSoundBuffer_QueryInterface(ctx->pOutput, &IID_IDirectSoundNotify , (void **)&pNotify); + if (hr == S_OK) { + /*setup the notification positions*/ + for (i=0; i<ctx->num_audio_buffer; i++) { + ctx->events[i] = CreateEvent( NULL, FALSE, FALSE, NULL ); + ctx->notif_events[i].hEventNotify = ctx->events[i]; + ctx->notif_events[i].dwOffset = ctx->buffer_size * i; + } + + /*Tell DirectSound when to notify us*/ + hr = IDirectSoundNotify_SetNotificationPositions(pNotify, ctx->num_audio_buffer, ctx->notif_events); + + if (hr != S_OK) { + IDirectSoundNotify_Release(pNotify); + for (i=0; i<ctx->num_audio_buffer; i++) CloseHandle(ctx->events[i]); + SAFE_DS_RELEASE(ctx->pOutput); + goto retry; + } + + IDirectSoundNotify_Release(pNotify); + } else { + ctx->use_notif = 0; + } + } + if (ctx->use_notif) { + dr->WriteAudio = DS_WriteAudio_Notifs; + } else { + dr->WriteAudio = DS_WriteAudio; + } + + ctx->total_audio_buffer_ms = 1000 * ctx->buffer_size * ctx->num_audio_buffer / ctx->format.nAvgBytesPerSec; + + /*reset*/ + DS_ResetBuffer(ctx); + /*play*/ + IDirectSoundBuffer_Play(ctx->pOutput, 0, 0, DSBPLAY_LOOPING); + return GF_OK; +} + +static Bool DS_RestoreBuffer(LPDIRECTSOUNDBUFFER pDSBuffer) +{ + DWORD dwStatus; + IDirectSoundBuffer_GetStatus(pDSBuffer, &dwStatus ); + if( dwStatus & DSBSTATUS_BUFFERLOST ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] buffer lost\n")); + IDirectSoundBuffer_Restore(pDSBuffer); + IDirectSoundBuffer_GetStatus(pDSBuffer, &dwStatus); + if( dwStatus & DSBSTATUS_BUFFERLOST ) return 1; + } + return 0; +} + + + +void DS_FillBuffer(GF_AudioOutput *dr, u32 buffer) +{ + HRESULT hr; + VOID *pLock; + u32 pos; + DWORD size; + DSCONTEXT(); + + /*restoring*/ + if (DS_RestoreBuffer(ctx->pOutput)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] restoring sound buffer\n")); + return; + } + + /*lock and fill from current pos*/ + pos = buffer * ctx->buffer_size; + pLock = NULL; + if( FAILED( hr = IDirectSoundBuffer_Lock(ctx->pOutput, pos, ctx->buffer_size, + &pLock, &size, NULL, NULL, 0L ) ) ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] Error locking sound buffer\n")); + return; + } + + assert(size == ctx->buffer_size); + + dr->FillBuffer(dr->audio_renderer, pLock, size); + + /*update current pos*/ + if( FAILED( hr = IDirectSoundBuffer_Unlock(ctx->pOutput, pLock, size, NULL, 0)) ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] Error unlocking sound buffer\n")); + } + ctx->frame_state[buffer] = 1; +} + + +void DS_WriteAudio_Notifs(GF_AudioOutput *dr) +{ + s32 i, inframe, nextframe; + DSCONTEXT(); + + inframe = WaitForMultipleObjects(ctx->num_audio_buffer, ctx->events, 0, 1000); + if (inframe==WAIT_TIMEOUT) return; + inframe -= WAIT_OBJECT_0; + /*reset state*/ + ctx->frame_state[ (inframe + ctx->num_audio_buffer - 1) % ctx->num_audio_buffer] = 0; + + nextframe = (inframe + 1) % ctx->num_audio_buffer; + for (i=nextframe; (i % ctx->num_audio_buffer) != (u32) inframe; i++) { + u32 buf = i % ctx->num_audio_buffer; + if (ctx->frame_state[buf]) continue; + DS_FillBuffer(dr, buf); + } +} + +void DS_WriteAudio(GF_AudioOutput *dr) +{ + u32 retry; + DWORD in_play, cur_play; + DSCONTEXT(); + + /*wait for end of current play buffer*/ + if (IDirectSoundBuffer_GetCurrentPosition(ctx->pOutput, &in_play, NULL) != DS_OK ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] error getting sound buffer poitions\n")); + return; + } + in_play = (in_play / ctx->buffer_size); + retry = 6; + while (retry) { + if (IDirectSoundBuffer_GetCurrentPosition(ctx->pOutput, &cur_play, NULL) != DS_OK ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] error getting sound buffer poitions\n")); + return; + } + cur_play = (cur_play / ctx->buffer_size); + if (cur_play == in_play) { + gf_sleep(20); + retry--; + } else { + /**/ + ctx->frame_state[in_play] = 0; + DS_FillBuffer(dr, in_play); + return; + } + } +} + +static GF_Err DS_QueryOutputSampleRate(GF_AudioOutput *dr, u32 *desired_samplerate, u32 *NbChannels, u32 *nbBitsPerSample) +{ + /*all sample rates supported for now ... */ + return GF_OK; +} + +static void DS_Play(GF_AudioOutput *dr, u32 PlayType) +{ + DSCONTEXT(); + switch (PlayType) { + case 0: + IDirectSoundBuffer_Stop(ctx->pOutput); + break; + case 2: + DS_ResetBuffer(ctx); + case 1: + IDirectSoundBuffer_Play(ctx->pOutput, 0, 0, DSBPLAY_LOOPING); + break; + } +} + +static void DS_SetVolume(GF_AudioOutput *dr, u32 Volume) +{ + LONG Vol; + DSCONTEXT(); + if (Volume > 100) Volume = 100; + Vol = DSBVOLUME_MIN/2 + Volume * (DSBVOLUME_MAX-DSBVOLUME_MIN/2) / 100; + IDirectSoundBuffer_SetVolume(ctx->pOutput, Vol); +} + +static void DS_SetPan(GF_AudioOutput *dr, u32 Pan) +{ + LONG dspan; + DSCONTEXT(); + + if (Pan > 100) Pan = 100; + if (Pan > 50) { + dspan = DSBPAN_RIGHT * (Pan - 50) / 50; + } else if (Pan < 50) { + dspan = DSBPAN_LEFT * (50 - Pan) / 50; + } else { + dspan = 0; + } + IDirectSoundBuffer_SetPan(ctx->pOutput, dspan); +} + + +static void DS_SetPriority(GF_AudioOutput *dr, u32 Priority) +{ +} + +static u32 DS_GetAudioDelay(GF_AudioOutput *dr) +{ + DSCONTEXT(); + return ctx->total_audio_buffer_ms; +} + +static u32 DS_GetTotalBufferTime(GF_AudioOutput *dr) +{ + DSCONTEXT(); + return ctx->total_audio_buffer_ms; +} + +void *NewAudioOutput() +{ + HRESULT hr; + DSContext *ctx; + GF_AudioOutput *driv; + + if( FAILED( hr = CoInitialize(NULL) ) ) return NULL; + + + ctx = malloc(sizeof(DSContext)); + memset(ctx, 0, sizeof(DSContext)); + + driv = malloc(sizeof(GF_AudioOutput)); + memset(driv, 0, sizeof(GF_AudioOutput)); + GF_REGISTER_MODULE_INTERFACE(driv, GF_AUDIO_OUTPUT_INTERFACE, "DirectSound Audio Output", "gpac distribution"); + + driv->opaque = ctx; + + driv->Setup = DS_Setup; + driv->Shutdown = DS_Shutdown; + driv->ConfigureOutput = DS_ConfigureOutput; + driv->SetVolume = DS_SetVolume; + driv->SetPan = DS_SetPan; + driv->Play = DS_Play; + driv->SetPriority = DS_SetPriority; + driv->GetAudioDelay = DS_GetAudioDelay; + driv->GetTotalBufferTime = DS_GetTotalBufferTime; + driv->WriteAudio = DS_WriteAudio; + driv->QueryOutputSampleRate = DS_QueryOutputSampleRate; + /*never threaded*/ + driv->SelfThreaded = 0; + return driv; +} + +void DeleteAudioOutput(void *ifce) +{ + GF_AudioOutput *dr = (GF_AudioOutput *)ifce; + DSCONTEXT(); + + free(ctx); + free(ifce); + CoUninitialize(); +} + diff --git a/modules/dx_hw/dx_hw.def b/modules/dx_hw/dx_hw.def new file mode 100644 index 0000000..56d452a --- /dev/null +++ b/modules/dx_hw/dx_hw.def @@ -0,0 +1,6 @@ +LIBRARY gm_dx_hw.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/dx_hw/dx_hw.h b/modules/dx_hw/dx_hw.h new file mode 100644 index 0000000..b0ce10a --- /dev/null +++ b/modules/dx_hw/dx_hw.h @@ -0,0 +1,173 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / DirectX audio and video render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _DXHW_H +#define _DXHW_H + + +/*driver interfaces*/ +#include <gpac/modules/audio_out.h> +#include <gpac/modules/video_out.h> +#include <gpac/list.h> +#include <gpac/constants.h> +#include <gpac/thread.h> + +#include <windows.h> +#include <mmsystem.h> +#include <dsound.h> + +#ifndef _WIN32_WCE +#include <vfw.h> +#endif + +#include <ddraw.h> + +#ifdef GPAC_USE_OGL_ES +#include "GLES/egl.h" +#endif + +/* + DirectDraw video output +*/ + +#if (DIRECTDRAW_VERSION < 0x0700) +#define USE_DX_3 +#endif + +typedef struct +{ +#ifdef USE_DX_3 + LPDIRECTDRAWSURFACE pSurface; +#else + LPDIRECTDRAWSURFACE7 pSurface; +#endif + u32 width, height, format, pitch; +} DDSurface; + +typedef struct +{ + HWND os_hwnd, fs_hwnd, cur_hwnd, parent_wnd; + Bool NeedRestore; + Bool switch_res; + +#ifdef USE_DX_3 + LPDIRECTDRAW pDD; + LPDIRECTDRAWSURFACE pPrimary; + LPDIRECTDRAWSURFACE pBack; +#else + LPDIRECTDRAW7 pDD; + LPDIRECTDRAWSURFACE7 pPrimary; + LPDIRECTDRAWSURFACE7 pBack; +#endif + Bool ddraw_init; + Bool yuv_init; + Bool fullscreen; + Bool systems_memory; + + u32 width, height; + u32 fs_width, fs_height; + u32 fs_store_width, fs_store_height; + u32 store_width, store_height; + + u32 pixelFormat; + u32 video_bpp; + + HDC lock_hdc; + + /*HW surfaces for blitting+stretch*/ + DDSurface rgb_pool, yuv_pool; + + /*if we own the window*/ + GF_Thread *th; + u32 th_state; + + + Bool owns_hwnd; + u32 off_w, off_h, prev_styles; + LONG last_mouse_pos; + /*cursors*/ + HCURSOR curs_normal, curs_hand, curs_collide; + u32 cursor_type; + + /*gl*/ +#ifdef GPAC_USE_OGL_ES + NativeDisplayType gl_HDC; + EGLDisplay egldpy; + EGLSurface surface; + EGLConfig eglconfig; + EGLContext eglctx; +#else + HDC gl_HDC; + HGLRC gl_HRC; +#endif + u32 output_3d_type; + HWND gl_hwnd; + Bool has_focus; + Bool gl_double_buffer; + + DWORD orig_wnd_proc; + + u32 last_mouse_move, timer, cursor_type_backup; + Bool windowless; +} DDContext; + +void DD_SetupWindow(GF_VideoOutput *dr, Bool hide); +void DD_ShutdownWindow(GF_VideoOutput*dr); +GF_Err DD_ProcessEvent(GF_VideoOutput*dr, GF_Event *evt); + +void DestroyObjects(DDContext *dd); +GF_Err GetDisplayMode(DDContext *dd); +/*2D-only callbacks*/ +void DD_SetupDDraw(GF_VideoOutput *driv); +GF_Err InitDirectDraw(GF_VideoOutput *dr, u32 Width, u32 Height); +void DD_InitYUV(GF_VideoOutput *dr); + +GF_Err DD_SetBackBufferSize(GF_VideoOutput *dr, u32 width, u32 height, Bool use_system_memory); + +GF_Err DD_FlushEx(GF_VideoOutput *dr, GF_Window *dest, Bool wait_for_sync); + +void dx_copy_pixels(GF_VideoSurface *dst_s, const GF_VideoSurface *src_s, const GF_Window *src_wnd); + +#define MAKERECT(rc, dest) { rc.left = dest->x; rc.top = dest->y; rc.right = rc.left + dest->w; rc.bottom = rc.top + dest->h; } + +/*this is REALLY ugly, to pass the HWND to DSound when we create the window in this module*/ +HWND DD_GetGlobalHWND(); + +GF_Err DD_SetupOpenGL(GF_VideoOutput *dr); + +#ifdef USE_DX_3 +#define SAFE_DD_RELEASE(p) { if(p) { IDirectDraw_Release(p); (p)=NULL; } } +#else +#define SAFE_DD_RELEASE(p) { if(p) { IDirectDraw7_Release(p); (p)=NULL; } } +#endif + + +void *NewAudioOutput(); +void DeleteAudioOutput(void *); + + +#define SAFE_DS_RELEASE(p) { if(p) { IDirectSound_Release(p); (p)=NULL; } } + +#endif diff --git a/modules/dx_hw/dx_hw.rc b/modules/dx_hw/dx_hw.rc new file mode 100644 index 0000000..3ef7ebf --- /dev/null +++ b/modules/dx_hw/dx_hw.rc @@ -0,0 +1,65 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Cursor +// + +IDC_HAND_PTR CURSOR DISCARDABLE "hand.cur" +IDC_COLLIDE CURSOR DISCARDABLE "collide.cur" +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/modules/dx_hw/dx_video.c b/modules/dx_hw/dx_video.c new file mode 100644 index 0000000..25b421c --- /dev/null +++ b/modules/dx_hw/dx_video.c @@ -0,0 +1,491 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / DirectX audio and video render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "dx_hw.h" + +#include <gpac/user.h> + +#ifdef _WIN32_WCE +#ifdef GPAC_USE_OGL_ES +#endif +#else +#include <GL/gl.h> +#endif + +#define DDCONTEXT DDContext *dd = (DDContext *)dr->opaque; + + +static void RestoreWindow(DDContext *dd) +{ + if (!dd->NeedRestore) return; + + dd->NeedRestore = 0; + if (dd->output_3d_type==1) { +#ifndef _WIN32_WCE + ChangeDisplaySettings(NULL,0); +#endif + } else { +#ifdef USE_DX_3 + IDirectDraw_SetCooperativeLevel(dd->pDD, dd->cur_hwnd, DDSCL_NORMAL); +#else + IDirectDraw7_SetCooperativeLevel(dd->pDD, dd->cur_hwnd, DDSCL_NORMAL); +#endif + dd->NeedRestore = 0; + } + +// SetForegroundWindow(dd->cur_hwnd); + SetFocus(dd->cur_hwnd); +} + +void DestroyObjectsEx(DDContext *dd, Bool only_3d) +{ + if (!only_3d) { + RestoreWindow(dd); + + SAFE_DD_RELEASE(dd->rgb_pool.pSurface); + memset(&dd->rgb_pool, 0, sizeof(DDSurface)); + SAFE_DD_RELEASE(dd->yuv_pool.pSurface); + memset(&dd->yuv_pool, 0, sizeof(DDSurface)); + + SAFE_DD_RELEASE(dd->pPrimary); + SAFE_DD_RELEASE(dd->pBack); + SAFE_DD_RELEASE(dd->pDD); + dd->ddraw_init = 0; + + /*do not destroy associated GL context*/ + if (dd->output_3d_type==2) return; + } + + /*delete openGL context*/ +#ifdef GPAC_USE_OGL_ES + if (dd->eglctx) eglDestroyContext(dd->egldpy, dd->eglctx); + dd->eglctx = NULL; + if (dd->surface) eglDestroySurface(dd->egldpy, dd->surface); + dd->surface = NULL; + if (dd->gl_HDC) { + if (dd->egldpy) eglTerminate(dd->egldpy); + ReleaseDC(dd->cur_hwnd, (HDC) dd->gl_HDC); + dd->gl_HDC = 0L; + dd->egldpy = NULL; + } +#elif !defined(_WIN32_WCE) + if (dd->gl_HRC) { + wglMakeCurrent(dd->gl_HDC, NULL); + wglDeleteContext(dd->gl_HRC); + dd->gl_HRC = NULL; + } + if (dd->gl_HDC) { + ReleaseDC(dd->cur_hwnd, dd->gl_HDC); + dd->gl_HDC = NULL; + } +#endif +} + +void DestroyObjects(DDContext *dd) +{ + DestroyObjectsEx(dd, 0); +} + +GF_Err DD_SetupOpenGL(GF_VideoOutput *dr) +{ + const char *sOpt; + GF_Event evt; + DDCONTEXT + +#ifdef GPAC_USE_OGL_ES + EGLint major, minor; + EGLint n; + EGLConfig configs[1]; + u32 nb_bits; + u32 i=0; + static int egl_atts[20]; + + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsPerComponent"); + nb_bits = sOpt ? atoi(sOpt) : 5; + + egl_atts[i++] = EGL_RED_SIZE; egl_atts[i++] = nb_bits; + egl_atts[i++] = EGL_GREEN_SIZE; egl_atts[i++] = nb_bits; + egl_atts[i++] = EGL_BLUE_SIZE; egl_atts[i++] = nb_bits; + /*alpha for compositeTexture*/ + egl_atts[i++] = EGL_ALPHA_SIZE; egl_atts[i++] = 1; + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsDepth"); + nb_bits = sOpt ? atoi(sOpt) : 5; + egl_atts[i++] = EGL_DEPTH_SIZE; egl_atts[i++] = nb_bits; + egl_atts[i++] = EGL_STENCIL_SIZE; egl_atts[i++] = EGL_DONT_CARE; + egl_atts[i++] = EGL_NONE; + + /*already setup*/ + DestroyObjects(dd); + dd->gl_HDC = (NativeDisplayType) GetDC(dd->cur_hwnd); + dd->egldpy = eglGetDisplay(/*dd->gl_HDC*/ EGL_DEFAULT_DISPLAY); + if (!eglInitialize(dd->egldpy, &major, &minor)) return GF_IO_ERR; + if (!eglChooseConfig(dd->egldpy, egl_atts, configs, 1, &n)) return GF_IO_ERR; + dd->eglconfig = configs[0]; + dd->surface = eglCreateWindowSurface(dd->egldpy, dd->eglconfig, dd->cur_hwnd, 0); + if (!dd->surface) return GF_IO_ERR; + dd->eglctx = eglCreateContext(dd->egldpy, dd->eglconfig, NULL, NULL); + if (!dd->eglctx) { + eglDestroySurface(dd->egldpy, dd->surface); + dd->surface = 0L; + return GF_IO_ERR; + } + if (!eglMakeCurrent(dd->egldpy, dd->surface, dd->surface, dd->eglctx)) { + eglDestroyContext(dd->egldpy, dd->eglctx); + dd->eglctx = 0L; + eglDestroySurface(dd->egldpy, dd->surface); + dd->surface = 0L; + return GF_IO_ERR; + } +#elif !defined(_WIN32_WCE) + PIXELFORMATDESCRIPTOR pfd; + s32 pixelformat; + + /*already setup*/ +// if (dd->gl_HRC) return GF_OK; + DestroyObjectsEx(dd, (dd->output_3d_type==1) ? 0 : 1); + + if (dd->output_3d_type==2) { + dd->gl_HDC = GetDC(dd->gl_hwnd); + } else { + dd->gl_HDC = GetDC(dd->cur_hwnd); + } + if (!dd->gl_HDC) return GF_IO_ERR; + + memset(&pfd, 0, sizeof(pfd)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + if (dd->gl_double_buffer) + pfd.dwFlags |= PFD_DOUBLEBUFFER; + else { + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "UseGLDoubleBuffering"); + if (sOpt && !strcmp(sOpt, "yes")) pfd.dwFlags |= PFD_DOUBLEBUFFER; + } + + pfd.dwLayerMask = PFD_MAIN_PLANE; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 32; + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsDepth"); + pfd.cDepthBits = sOpt ? atoi(sOpt) : 16; + /*we need alpha support for composite textures...*/ + pfd.cAlphaBits = 8; + if ( (pixelformat = ChoosePixelFormat(dd->gl_HDC, &pfd)) == FALSE ) return GF_IO_ERR; + if (SetPixelFormat(dd->gl_HDC, pixelformat, &pfd) == FALSE) + return GF_IO_ERR; + dd->gl_HRC = wglCreateContext(dd->gl_HDC); + if (!dd->gl_HRC) return GF_IO_ERR; + if (!wglMakeCurrent(dd->gl_HDC, dd->gl_HRC)) return GF_IO_ERR; +#endif + if (dd->output_3d_type==1) { + evt.type = GF_EVENT_VIDEO_SETUP; + evt.setup.opengl_mode = 1; + dr->on_event(dr->evt_cbk_hdl, &evt); + } + return GF_OK; +} + + + + +GF_Err DD_Setup(GF_VideoOutput *dr, void *os_handle, void *os_display, u32 init_flags) +{ + RECT rc; + DDCONTEXT + dd->os_hwnd = (HWND) os_handle; + + //if (init_flags & (GF_TERM_NO_VISUAL_THREAD | GF_TERM_NO_REGULATION) ) dd->systems_memory = 2; + DD_SetupWindow(dr, init_flags); + /*fatal error*/ + if (!dd->os_hwnd) return GF_IO_ERR; + dd->cur_hwnd = dd->os_hwnd; + + dd->output_3d_type = 0; + GetWindowRect(dd->cur_hwnd, &rc); + return InitDirectDraw(dr, rc.right - rc.left, rc.bottom - rc.top); +} + +static void DD_Shutdown(GF_VideoOutput *dr) +{ + DDCONTEXT + + /*force destroy of opengl*/ + if (dd->output_3d_type) dd->output_3d_type = 1; + DestroyObjects(dd); + + DD_ShutdownWindow(dr); +} + +static GF_Err DD_SetFullScreen(GF_VideoOutput *dr, Bool bOn, u32 *outWidth, u32 *outHeight) +{ + GF_Err e; + const char *sOpt; + u32 MaxWidth, MaxHeight; + DDCONTEXT; + + if (!dd->width ||!dd->height) return GF_BAD_PARAM; + if (bOn == dd->fullscreen) return GF_OK; + if (!dd->fs_hwnd) return GF_NOT_SUPPORTED; + dd->fullscreen = bOn; + + /*whenever changing card display mode relocate fastest YUV format for blit (since it depends + on the dest pixel format)*/ + dd->yuv_init = 0; + if (dd->fullscreen) { + const char *sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "SwitchResolution"); + if (sOpt && !stricmp(sOpt, "yes")) dd->switch_res = 1; + /*get current or best fitting mode*/ + if (GetDisplayMode(dd) != GF_OK) return GF_IO_ERR; + } + + MaxWidth = MaxHeight = 0; + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "MaxResolution"); + if (sOpt) sscanf(sOpt, "%dx%d", &MaxWidth, &MaxHeight); + + /*destroy all objects*/ + DestroyObjects(dd); + if (dd->timer) KillTimer(dd->cur_hwnd, dd->timer); + dd->timer = 0; + ShowWindow(dd->cur_hwnd, SW_HIDE); + dd->cur_hwnd = dd->fullscreen ? dd->fs_hwnd : dd->os_hwnd; + ShowWindow(dd->cur_hwnd, SW_SHOW); + + if (dd->output_3d_type==1) { + DEVMODE settings; + e = GF_OK; + /*Setup FS*/ + if (dd->fullscreen) { + /*change display mode*/ + if ((MaxWidth && (dd->fs_width >= MaxWidth)) || (MaxHeight && (dd->fs_height >= MaxHeight)) ) { + dd->fs_width = MaxWidth; + dd->fs_height = MaxHeight; + } + SetWindowPos(dd->cur_hwnd, NULL, 0, 0, dd->fs_width, dd->fs_height, SWP_NOZORDER | SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS); + +#ifndef _WIN32_WCE + memset(&settings, 0, sizeof(DEVMODE)); + settings.dmSize = sizeof(DEVMODE); + settings.dmPelsWidth = dd->fs_width; + settings.dmPelsHeight = dd->fs_height; + settings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + if ( ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectDraw] cannot change display settings\n")); + e = GF_IO_ERR; + } + dd->NeedRestore = 1; +#endif + + dd->fs_store_width = dd->fs_width; + dd->fs_store_height = dd->fs_height; + } + if (!e) e = DD_SetupOpenGL(dr); + + } else { + e = InitDirectDraw(dr, dd->width, dd->height); + } + + if (bOn) { + dd->store_width = *outWidth; + dd->store_height = *outHeight; + *outWidth = dd->fs_width; + *outHeight = dd->fs_height; + } else { + *outWidth = dd->store_width; + *outHeight = dd->store_height; + } + SetForegroundWindow(dd->cur_hwnd); + SetFocus(dd->cur_hwnd); + return e; +} + + +GF_Err DD_Flush(GF_VideoOutput *dr, GF_Window *dest) +{ + RECT rc; + HRESULT hr; + DDCONTEXT; + + if (!dd) return GF_BAD_PARAM; + if (dd->output_3d_type==1) { +#ifdef GPAC_USE_OGL_ES + if (dd->surface) eglSwapBuffers(dd->egldpy, dd->surface); +#else + SwapBuffers(dd->gl_HDC); +#endif + return GF_OK; + } + + if (!dd->ddraw_init) return GF_BAD_PARAM; + + if (!dd->fullscreen && dd->windowless) { + HDC hdc; + /*lock backbuffer HDC*/ + dr->LockOSContext(dr, 1); + /*get window hdc and copy from backbuffer to window*/ + hdc = GetDC(dd->os_hwnd); + BitBlt(hdc, 0, 0, dd->width, dd->height, dd->lock_hdc, 0, 0, SRCCOPY ); + ReleaseDC(dd->os_hwnd, hdc); + /*unlock backbuffer HDC*/ + dr->LockOSContext(dr, 0); + return GF_OK; + } + + if (dest) { + POINT pt; + pt.x = dest->x; + pt.y = dest->y; + ClientToScreen(dd->cur_hwnd, &pt); + dest->x = pt.x; + dest->y = pt.y; + MAKERECT(rc, dest); + hr = IDirectDrawSurface_Blt(dd->pPrimary, &rc, dd->pBack, NULL, DDBLT_WAIT, NULL); + } else { + hr = IDirectDrawSurface_Blt(dd->pPrimary, NULL, dd->pBack, NULL, DDBLT_WAIT, NULL); + } + if (hr == DDERR_SURFACELOST) { + IDirectDrawSurface_Restore(dd->pPrimary); + IDirectDrawSurface_Restore(dd->pBack); + } + return FAILED(hr) ? GF_IO_ERR : GF_OK; +} + + + +#ifdef USE_DX_3 +HRESULT WINAPI EnumDisplayModes( LPDDSURFACEDESC lpDDDesc, LPVOID lpContext) +#else +HRESULT WINAPI EnumDisplayModes( LPDDSURFACEDESC2 lpDDDesc, LPVOID lpContext) +#endif +{ + DDContext *dd = (DDContext *) lpContext; + + //check W and H + if (dd->width <= lpDDDesc->dwWidth && dd->height <= lpDDDesc->dwHeight + //check FSW and FSH + && dd->fs_width > lpDDDesc->dwWidth && dd->fs_height > lpDDDesc->dwHeight) { + + if (lpDDDesc->dwHeight == 200) + return DDENUMRET_OK; + + dd->fs_width = lpDDDesc->dwWidth; + dd->fs_height = lpDDDesc->dwHeight; + + return DDENUMRET_CANCEL; + } + return DDENUMRET_OK; +} + +GF_Err GetDisplayMode(DDContext *dd) +{ + if (dd->switch_res) { + HRESULT hr; + Bool temp_dd = 0;; + if (!dd->pDD) { + LPDIRECTDRAW ddraw; + DirectDrawCreate(NULL, &ddraw, NULL); +#ifdef USE_DX_3 + IDirectDraw_QueryInterface(ddraw, &IID_IDirectDraw, (LPVOID *)&dd->pDD); +#else + IDirectDraw_QueryInterface(ddraw, &IID_IDirectDraw7, (LPVOID *)&dd->pDD); +#endif + temp_dd = 1; + } + //we start with a hugde res and downscale + dd->fs_width = dd->fs_height = 50000; + +#ifdef USE_DX_3 + hr = IDirectDraw_EnumDisplayModes(dd->pDD, 0L, NULL, dd, (LPDDENUMMODESCALLBACK) EnumDisplayModes); +#else + hr = IDirectDraw7_EnumDisplayModes(dd->pDD, 0L, NULL, dd, (LPDDENUMMODESCALLBACK2) EnumDisplayModes); +#endif + if (temp_dd) SAFE_DD_RELEASE(dd->pDD); + if (FAILED(hr)) return GF_IO_ERR; + } else { + dd->fs_width = GetSystemMetrics(SM_CXSCREEN); + dd->fs_height = GetSystemMetrics(SM_CYSCREEN); + } + return GF_OK; +} + + + +static void *NewDXVideoOutput() +{ + DDContext *pCtx; + GF_VideoOutput *driv = (GF_VideoOutput *) malloc(sizeof(GF_VideoOutput)); + memset(driv, 0, sizeof(GF_VideoOutput)); + GF_REGISTER_MODULE_INTERFACE(driv, GF_VIDEO_OUTPUT_INTERFACE, "DirectX Video Output", "gpac distribution"); + + pCtx = malloc(sizeof(DDContext)); + memset(pCtx, 0, sizeof(DDContext)); + driv->opaque = pCtx; + driv->Flush = DD_Flush; + driv->Setup = DD_Setup; + driv->Shutdown = DD_Shutdown; + driv->SetFullScreen = DD_SetFullScreen; + driv->ProcessEvent = DD_ProcessEvent; + + driv->max_screen_width = GetSystemMetrics(SM_CXSCREEN); + driv->max_screen_height = GetSystemMetrics(SM_CYSCREEN); + driv->hw_caps = GF_VIDEO_HW_OPENGL | GF_VIDEO_HW_OPENGL_OFFSCREEN | GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA; + + DD_SetupDDraw(driv); + + return (void *)driv; +} + +static void DeleteVideoOutput(void *ifce) +{ + GF_VideoOutput *driv = (GF_VideoOutput *) ifce; + DDContext *dd = (DDContext *)driv->opaque; + free(dd); + free(driv); +} + +/*interface query*/ +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return 1; + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) return 1; + return 0; +} +/*interface create*/ +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return NewDXVideoOutput(); + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) return NewAudioOutput(); + return NULL; +} +/*interface destroy*/ +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_VIDEO_OUTPUT_INTERFACE: + DeleteVideoOutput((GF_VideoOutput *)ifce); + break; + case GF_AUDIO_OUTPUT_INTERFACE: + DeleteAudioOutput(ifce); + break; + } +} diff --git a/modules/dx_hw/dx_window.c b/modules/dx_hw/dx_window.c new file mode 100644 index 0000000..31f7024 --- /dev/null +++ b/modules/dx_hw/dx_window.c @@ -0,0 +1,862 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / DirectX audio and video render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "dx_hw.h" +#include <gpac/user.h> +#include "resource.h" + +/*crude redef of winuser.h due to windows/winsock2 conflicts*/ +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x020A +#define WHEEL_DELTA 120 +#endif + +/*mouse hiding timeout in fullscreen, in milliseconds*/ +#define MOUSE_HIDE_TIMEOUT 1000 + +static const GF_VideoOutput *the_video_output = NULL; + +void DD_SetCursor(GF_VideoOutput *dr, u32 cursor_type); + + +#ifdef _WIN32_WCE +static void DD_GetCoordinates(DWORD lParam, GF_Event *evt) +{ + evt->mouse.x = LOWORD(lParam); + evt->mouse.y = HIWORD(lParam); +} +#else + +static void DD_GetCoordinates(DWORD lParam, GF_Event *evt) +{ + POINTS pt = MAKEPOINTS(lParam); + evt->mouse.x = pt.x; + evt->mouse.y = pt.y; +} +#endif + +static void w32_translate_key(u32 wParam, u32 lParam, GF_EventKey *evt) +{ + evt->flags = 0; + evt->hw_code = wParam; + switch (wParam) { + case VK_BACK: evt->key_code = GF_KEY_BACKSPACE; break; + case VK_TAB: evt->key_code = GF_KEY_TAB; break; + case VK_CLEAR: evt->key_code = GF_KEY_CLEAR; break; + case VK_RETURN: evt->key_code = GF_KEY_ENTER; break; + case VK_SHIFT: evt->key_code = GF_KEY_SHIFT; break; + case VK_CONTROL: evt->key_code = GF_KEY_CONTROL; break; + case VK_MENU: evt->key_code = GF_KEY_ALT; break; + case VK_PAUSE: evt->key_code = GF_KEY_PAUSE; break; + case VK_CAPITAL: evt->key_code = GF_KEY_CAPSLOCK; break; + case VK_KANA: evt->key_code = GF_KEY_KANAMODE; break; + case VK_JUNJA: evt->key_code = GF_KEY_JUNJAMODE; break; + case VK_FINAL: evt->key_code = GF_KEY_FINALMODE; break; + case VK_KANJI: evt->key_code = GF_KEY_KANJIMODE; break; + case VK_ESCAPE: evt->key_code = GF_KEY_ESCAPE; break; + case VK_CONVERT: evt->key_code = GF_KEY_CONVERT; break; + case VK_SPACE: evt->key_code = GF_KEY_SPACE; break; + case VK_PRIOR: evt->key_code = GF_KEY_PAGEUP; break; + case VK_NEXT: evt->key_code = GF_KEY_PAGEDOWN; break; + case VK_END: evt->key_code = GF_KEY_END; break; + case VK_HOME: evt->key_code = GF_KEY_HOME; break; + case VK_LEFT: evt->key_code = GF_KEY_LEFT; break; + case VK_UP: evt->key_code = GF_KEY_UP; break; + case VK_RIGHT: evt->key_code = GF_KEY_RIGHT; break; + case VK_DOWN: evt->key_code = GF_KEY_DOWN; break; + case VK_SELECT: evt->key_code = GF_KEY_SELECT; break; + case VK_PRINT: + case VK_SNAPSHOT: + evt->key_code = GF_KEY_PRINTSCREEN; break; + case VK_EXECUTE: evt->key_code = GF_KEY_EXECUTE; break; + case VK_INSERT: evt->key_code = GF_KEY_INSERT; break; + case VK_DELETE: evt->key_code = GF_KEY_DEL; break; + case VK_HELP: evt->key_code = GF_KEY_HELP; break; + +#ifndef _WIN32_WCE + case VK_NONCONVERT: evt->key_code = GF_KEY_NONCONVERT; break; + case VK_ACCEPT: evt->key_code = GF_KEY_ACCEPT; break; + case VK_MODECHANGE: evt->key_code = GF_KEY_MODECHANGE; break; +#endif + +/* case '!': evt->key_code = GF_KEY_EXCLAMATION; break; + case '"': evt->key_code = GF_KEY_QUOTATION; break; + case '#': evt->key_code = GF_KEY_NUMBER; break; + case '$': evt->key_code = GF_KEY_DOLLAR; break; + case '&': evt->key_code = GF_KEY_AMPERSAND; break; + case '\'': evt->key_code = GF_KEY_APOSTROPHE; break; + case '(': evt->key_code = GF_KEY_LEFTPARENTHESIS; break; + case ')': evt->key_code = GF_KEY_RIGHTPARENTHESIS; break; + case ',': evt->key_code = GF_KEY_COMMA; break; + case ':': evt->key_code = GF_KEY_COLON; break; + case ';': evt->key_code = GF_KEY_SEMICOLON; break; + case '<': evt->key_code = GF_KEY_LESSTHAN; break; + case '>': evt->key_code = GF_KEY_GREATERTHAN; break; + case '?': evt->key_code = GF_KEY_QUESTION; break; + case '@': evt->key_code = GF_KEY_AT; break; + case '[': evt->key_code = GF_KEY_LEFTSQUAREBRACKET; break; + case ']': evt->key_code = GF_KEY_RIGHTSQUAREBRACKET; break; + case '\\': evt->key_code = GF_KEY_BACKSLASH; break; + case '_': evt->key_code = GF_KEY_UNDERSCORE; break; + case '`': evt->key_code = GF_KEY_GRAVEACCENT; break; + case ' ': evt->key_code = GF_KEY_SPACE; break; + case '/': evt->key_code = GF_KEY_SLASH; break; + case '*': evt->key_code = GF_KEY_STAR; break; + case '-': evt->key_code = GF_KEY_HIPHEN; break; + case '+': evt->key_code = GF_KEY_PLUS; break; + case '=': evt->key_code = GF_KEY_EQUALS; break; + case '^': evt->key_code = GF_KEY_CIRCUM; break; + case '{': evt->key_code = GF_KEY_LEFTCURLYBRACKET; break; + case '}': evt->key_code = GF_KEY_RIGHTCURLYBRACKET; break; + case '|': evt->key_code = GF_KEY_PIPE; break; +*/ + + +/* case VK_LWIN: return ; + case VK_RWIN: return ; + case VK_APPS: return ; +*/ + case VK_NUMPAD0: + evt->key_code = GF_KEY_0; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD1: + evt->key_code = GF_KEY_1; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD2: + evt->key_code = GF_KEY_2; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD3: + evt->key_code = GF_KEY_3; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD4: + evt->key_code = GF_KEY_4; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD5: + evt->key_code = GF_KEY_5; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD6: + evt->key_code = GF_KEY_6; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD7: + evt->key_code = GF_KEY_7; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD8: + evt->key_code = GF_KEY_8; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD9: + evt->key_code = GF_KEY_9; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_MULTIPLY: + evt->key_code = GF_KEY_STAR; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_ADD: + evt->key_code = GF_KEY_PLUS; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_SEPARATOR: + evt->key_code = GF_KEY_FULLSTOP; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_SUBTRACT: + evt->key_code = GF_KEY_HYPHEN; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_DECIMAL: + evt->key_code = GF_KEY_COMMA; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_DIVIDE: + evt->key_code = GF_KEY_SLASH; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_F1: evt->key_code = GF_KEY_F1; break; + case VK_F2: evt->key_code = GF_KEY_F2; break; + case VK_F3: evt->key_code = GF_KEY_F3; break; + case VK_F4: evt->key_code = GF_KEY_F4; break; + case VK_F5: evt->key_code = GF_KEY_F5; break; + case VK_F6: evt->key_code = GF_KEY_F6; break; + case VK_F7: evt->key_code = GF_KEY_F7; break; + case VK_F8: evt->key_code = GF_KEY_F8; break; + case VK_F9: evt->key_code = GF_KEY_F9; break; + case VK_F10: evt->key_code = GF_KEY_F10; break; + case VK_F11: evt->key_code = GF_KEY_F11; break; + case VK_F12: evt->key_code = GF_KEY_F12; break; + case VK_F13: evt->key_code = GF_KEY_F13; break; + case VK_F14: evt->key_code = GF_KEY_F14; break; + case VK_F15: evt->key_code = GF_KEY_F15; break; + case VK_F16: evt->key_code = GF_KEY_F16; break; + case VK_F17: evt->key_code = GF_KEY_F17; break; + case VK_F18: evt->key_code = GF_KEY_F18; break; + case VK_F19: evt->key_code = GF_KEY_F19; break; + case VK_F20: evt->key_code = GF_KEY_F20; break; + case VK_F21: evt->key_code = GF_KEY_F21; break; + case VK_F22: evt->key_code = GF_KEY_F22; break; + case VK_F23: evt->key_code = GF_KEY_F23; break; + case VK_F24: evt->key_code = GF_KEY_F24; break; + + case VK_NUMLOCK: evt->key_code = GF_KEY_NUMLOCK; break; + case VK_SCROLL: evt->key_code = GF_KEY_SCROLL; break; + +/* + * VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys. + * Used only as parameters to GetAsyncKeyState() and GetKeyState(). + * No other API or message will distinguish left and right keys in this way. + */ + case VK_LSHIFT: + evt->key_code = GF_KEY_SHIFT; + evt->flags = GF_KEY_EXT_LEFT; + break; + case VK_RSHIFT: + evt->key_code = GF_KEY_SHIFT; + evt->flags = GF_KEY_EXT_RIGHT; + break; + case VK_LCONTROL: + evt->key_code = GF_KEY_CONTROL; + evt->flags = GF_KEY_EXT_LEFT; + break; + case VK_RCONTROL: + evt->key_code = GF_KEY_CONTROL; + evt->flags = GF_KEY_EXT_RIGHT; + break; + case VK_LMENU: + evt->key_code = GF_KEY_ALT; + evt->flags = GF_KEY_EXT_LEFT; + break; + case VK_RMENU: + evt->key_code = GF_KEY_ALT; + evt->flags = GF_KEY_EXT_RIGHT; + break; + +#if(WINVER >= 0x0400) + case VK_PROCESSKEY: evt->key_code = GF_KEY_PROCESS; break; +#endif /* WINVER >= 0x0400 */ + + case VK_ATTN: evt->key_code = GF_KEY_ATTN; break; + case VK_CRSEL: evt->key_code = GF_KEY_CRSEL; break; + case VK_EXSEL: evt->key_code = GF_KEY_EXSEL; break; + case VK_EREOF: evt->key_code = GF_KEY_ERASEEOF; break; + case VK_PLAY: evt->key_code = GF_KEY_PLAY; break; + case VK_ZOOM: evt->key_code = GF_KEY_ZOOM; break; + //case VK_NONAME: evt->key_code = GF_KEY_NONAME; break; + //case VK_PA1: evt->key_code = GF_KEY_PA1; break; + case VK_OEM_CLEAR: evt->key_code = GF_KEY_CLEAR; break; + + /*thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */ + /* VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */ + default: + if ((wParam>=0x30) && (wParam<=0x39)) evt->key_code = GF_KEY_0 + wParam-0x30; + else if ((wParam>=0x41) && (wParam<=0x5A)) evt->key_code = GF_KEY_A + wParam-0x41; + /*DOM 3 Events: Implementations that are unable to identify a key must use the key identifier "Unidentified".*/ + else + evt->key_code = GF_KEY_UNIDENTIFIED; + break; + } +} + + +static void mouse_move(DDContext *ctx, GF_VideoOutput *vout) +{ + if (ctx->fullscreen) { + ctx->last_mouse_move = gf_sys_clock(); + if (ctx->cursor_type==GF_CURSOR_HIDE) DD_SetCursor(vout, ctx->cursor_type_backup); + } +} + +static void mouse_start_timer(DDContext *ctx, HWND hWnd, GF_VideoOutput *vout) +{ + if (ctx->fullscreen) { + if (!ctx->timer) ctx->timer = SetTimer(hWnd, 10, 1000, NULL); + mouse_move(ctx, vout); + } +} + +void grab_mouse(DDContext *ctx, GF_VideoOutput *vout) +{ + if (ctx->fullscreen) DD_SetCursor(vout, GF_CURSOR_NORMAL); + SetCapture(ctx->cur_hwnd); + mouse_move(ctx, vout); +} +void release_mouse(DDContext *ctx, HWND hWnd, GF_VideoOutput *vout) +{ + ReleaseCapture(); + mouse_start_timer(ctx, hWnd, vout); +} + +LRESULT APIENTRY DD_WindowProc(HWND hWnd, UINT msg, UINT wParam, LONG lParam) +{ + Bool ret = 0; + GF_Event evt; + DDContext *ctx; + GF_VideoOutput *vout = (GF_VideoOutput *) GetWindowLong(hWnd, GWL_USERDATA); + + if (!vout) return DefWindowProc (hWnd, msg, wParam, lParam); + ctx = (DDContext *)vout->opaque; + + switch (msg) { + case WM_SIZE: + /*always notify GPAC since we're not sure the owner of the window is listening to these events*/ + evt.type = GF_EVENT_SIZE; + evt.size.width = LOWORD(lParam); + evt.size.height = HIWORD(lParam); + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + break; + case WM_MOVE: + evt.type = GF_EVENT_MOVE; + evt.move.x = LOWORD(lParam); + evt.move.y = HIWORD(lParam); + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + break; + case WM_CLOSE: + if (hWnd==ctx->os_hwnd) { + evt.type = GF_EVENT_QUIT; + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + } + break; + case WM_DESTROY: + if (ctx->owns_hwnd || (hWnd==ctx->fs_hwnd)) { + PostQuitMessage (0); + } else if (ctx->orig_wnd_proc) { + /*restore window proc*/ + SetWindowLong(ctx->os_hwnd, GWL_WNDPROC, ctx->orig_wnd_proc); + ctx->orig_wnd_proc = 0L; + } + break; + case WM_ACTIVATE: + if ((ctx->output_3d_type!=2) && ctx->fullscreen && (LOWORD(wParam)==WA_INACTIVE) && (hWnd==ctx->fs_hwnd)) { + evt.type = GF_EVENT_SHOWHIDE; + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + } + break; + case WM_SETCURSOR: + if (ctx->cur_hwnd==hWnd) DD_SetCursor(vout, ctx->cursor_type); + return 1; + case WM_ERASEBKGND: + //InvalidateRect(ctx->cur_hwnd, NULL, TRUE); + //break; + case WM_PAINT: + if (ctx->cur_hwnd==hWnd) { + evt.type = GF_EVENT_REFRESH; + vout->on_event(vout->evt_cbk_hdl, &evt); + } + break; + case WM_KILLFOCUS: + if (hWnd==ctx->os_hwnd) ctx->has_focus = 0; + break; + + case WM_MOUSEMOVE: + if (ctx->cur_hwnd!=hWnd) break; + if (ctx->last_mouse_pos != lParam) { + ctx->last_mouse_pos = lParam; + DD_SetCursor(vout, (ctx->cursor_type==GF_CURSOR_HIDE) ? ctx->cursor_type_backup : ctx->cursor_type); + evt.type = GF_EVENT_MOUSEMOVE; + DD_GetCoordinates(lParam, &evt); + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + mouse_start_timer(ctx, hWnd, vout); + } + break; + + case WM_TIMER: + if (wParam==10) { + if (ctx->fullscreen && (ctx->cursor_type!=GF_CURSOR_HIDE)) { + if (gf_sys_clock() > MOUSE_HIDE_TIMEOUT + ctx->last_mouse_move) { + ctx->cursor_type_backup = ctx->cursor_type; + DD_SetCursor(vout, GF_CURSOR_HIDE); + KillTimer(hWnd, ctx->timer); + ctx->timer = 0; + } + } + } + break; + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + grab_mouse(ctx, vout); + evt.type = GF_EVENT_MOUSEDOWN; + DD_GetCoordinates(lParam, &evt); + evt.mouse.button = GF_MOUSE_LEFT; + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + if (!ctx->has_focus && (hWnd==ctx->os_hwnd)) { + ctx->has_focus = 1; + SetFocus(ctx->os_hwnd); + } + break; + case WM_LBUTTONUP: + release_mouse(ctx, hWnd, vout); + evt.type = GF_EVENT_MOUSEUP; + DD_GetCoordinates(lParam, &evt); + evt.mouse.button = GF_MOUSE_LEFT; + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + break; + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + grab_mouse(ctx, vout); + evt.type = GF_EVENT_MOUSEDOWN; + DD_GetCoordinates(lParam, &evt); + evt.mouse.button = GF_MOUSE_RIGHT; + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + if (!ctx->has_focus && (hWnd==ctx->os_hwnd)) { + ctx->has_focus = 1; + SetFocus(ctx->os_hwnd); + } + break; + case WM_RBUTTONUP: + release_mouse(ctx, hWnd, vout); + evt.type = GF_EVENT_MOUSEUP; + DD_GetCoordinates(lParam, &evt); + evt.mouse.button = GF_MOUSE_RIGHT; + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + mouse_start_timer(ctx, hWnd, vout); + break; + case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: + grab_mouse(ctx, vout); + evt.type = GF_EVENT_MOUSEDOWN; + evt.mouse.button = GF_MOUSE_MIDDLE; + DD_GetCoordinates(lParam, &evt); + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + if (!ctx->has_focus && (hWnd==ctx->os_hwnd)) { + ctx->has_focus = 1; + SetFocus(ctx->os_hwnd); + } + break; + case WM_MBUTTONUP: + release_mouse(ctx, hWnd, vout); + evt.type = GF_EVENT_MOUSEUP; + DD_GetCoordinates(lParam, &evt); + evt.mouse.button = GF_MOUSE_MIDDLE; + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + mouse_start_timer(ctx, hWnd, vout); + break; + case WM_MOUSEWHEEL: + if (ctx->cur_hwnd==hWnd) { + DD_SetCursor(vout, (ctx->cursor_type==GF_CURSOR_HIDE) ? ctx->cursor_type_backup : ctx->cursor_type); + evt.type = GF_EVENT_MOUSEWHEEL; + DD_GetCoordinates(lParam, &evt); + evt.mouse.wheel_pos = FLT2FIX( ((Float) (s16) HIWORD(wParam)) / WHEEL_DELTA ); + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + mouse_start_timer(ctx, hWnd, vout); + } + break; + + /*there's a bug on alt state (we miss one event)*/ + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYUP: + case WM_KEYDOWN: + w32_translate_key(wParam, lParam, &evt.key); + evt.type = ((msg==WM_SYSKEYDOWN) || (msg==WM_KEYDOWN)) ? GF_EVENT_KEYDOWN : GF_EVENT_KEYUP; + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + break; + + case WM_CHAR: + evt.type = GF_EVENT_TEXTINPUT; + evt.character.unicode_char = wParam; + ret = vout->on_event(vout->evt_cbk_hdl, &evt); + break; + } + + if (ret) return 1; + +/* + if (ctx->parent_wnd && (hWnd==ctx->os_hwnd) ) { + return SendMessage(ctx->parent_wnd, msg, wParam, lParam); + } +*/ + return DefWindowProc (hWnd, msg, wParam, lParam); +} + +#ifndef WS_EX_LAYERED +#define WS_EX_LAYERED 0x80000 +#endif +#ifndef LWA_COLORKEY +#define LWA_COLORKEY 0x00000001 +#endif +#ifndef LWA_ALPHA +#define LWA_ALPHA 0x00000002 +#endif +typedef BOOL (WINAPI* typSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD); + + +static void SetWindowless(GF_VideoOutput *vout, HWND hWnd) +{ +#ifdef _WIN32_WCE + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DX Out] Windowloess mode not supported on Window CE\n")); + return; +#else + const char *opt; + u32 a, r, g, b; + COLORREF ckey; + typSetLayeredWindowAttributes _SetLayeredWindowAttributes; + HMODULE hUser32; + u32 isWin2K; + OSVERSIONINFO Version = {sizeof(OSVERSIONINFO)}; + GetVersionEx(&Version); + isWin2K = (Version.dwPlatformId == VER_PLATFORM_WIN32_NT && Version.dwMajorVersion >= 5); + if (!isWin2K) return; + + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Enabling windowless mode\n")); + hUser32 = GetModuleHandle("USER32.DLL"); + if (hUser32 == NULL) return; + + _SetLayeredWindowAttributes = (typSetLayeredWindowAttributes) GetProcAddress(hUser32,"SetLayeredWindowAttributes"); + if (_SetLayeredWindowAttributes == NULL) { + GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[DX Out] Win32 layered windows not supported\n")); + return; + } + + SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED); + + /*get background ckey*/ + opt = gf_modules_get_option((GF_BaseInterface *)vout, "Compositor", "ColorKey"); + if (!opt) { + gf_modules_set_option((GF_BaseInterface *)vout, "Compositor", "ColorKey", "FFFEFEFE"); + opt = "FFFEFEFE"; + } + + sscanf(opt, "%02X%02X%02X%02X", &a, &r, &g, &b); + ckey = RGB(r, g, b); + if (a<255) + _SetLayeredWindowAttributes(hWnd, ckey, (u8) a, LWA_COLORKEY|LWA_ALPHA); + else + _SetLayeredWindowAttributes(hWnd, ckey, 0, LWA_COLORKEY); + + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Using color key %s\n", opt)); +#endif +} + + +Bool DD_InitWindows(GF_VideoOutput *vout, DDContext *ctx) +{ + u32 flags; +#ifndef _WIN32_WCE + RECT rc; +#endif + WNDCLASS wc; + HINSTANCE hInst; + +#ifndef _WIN32_WCE + hInst = GetModuleHandle("gm_dx_hw.dll"); +#else + hInst = GetModuleHandle(_T("gm_dx_hw.dll")); +#endif + + memset(&wc, 0, sizeof(WNDCLASS)); +#ifndef _WIN32_WCE + wc.style = CS_BYTEALIGNWINDOW; + wc.hIcon = LoadIcon (NULL, IDI_APPLICATION); + wc.lpszClassName = "GPAC DirectDraw Output"; +#else + wc.lpszClassName = _T("GPAC DirectDraw Output"); +#endif + wc.hInstance = hInst; + wc.lpfnWndProc = DD_WindowProc; + wc.hCursor = LoadCursor (NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject (BLACK_BRUSH); + RegisterClass (&wc); + + flags = ctx->switch_res; + ctx->switch_res = 0; + + if (!ctx->os_hwnd) { + if (flags & GF_TERM_WINDOWLESS) ctx->windowless = 1; + +#ifdef _WIN32_WCE + ctx->os_hwnd = CreateWindow(_T("GPAC DirectDraw Output"), _T("GPAC DirectDraw Output"), WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL); +#else + ctx->os_hwnd = CreateWindow("GPAC DirectDraw Output", "GPAC DirectDraw Output", ctx->windowless ? WS_POPUP : WS_OVERLAPPEDWINDOW, 0, 0, 120, 100, NULL, NULL, hInst, NULL); +#endif + if (ctx->os_hwnd == NULL) { + return 0; + } + if (flags & GF_TERM_INIT_HIDE) { + ShowWindow(ctx->os_hwnd, SW_HIDE); + } else { + SetForegroundWindow(ctx->os_hwnd); + ShowWindow(ctx->os_hwnd, SW_SHOWNORMAL); + } + + /*get border & title bar sizes*/ +#ifdef _WIN32_WCE + ctx->off_w = 0; + ctx->off_h = 0; +#else + rc.left = rc.top = 0; + rc.right = rc.bottom = 100; + AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, 0); + ctx->off_w = rc.right - rc.left - 100; + ctx->off_h = rc.bottom - rc.top - 100; +#endif + ctx->owns_hwnd = 1; + + if (ctx->windowless) SetWindowless(vout, ctx->os_hwnd); + } + +#ifdef _WIN32_WCE + ctx->fs_hwnd = CreateWindow(_T("GPAC DirectDraw Output"), _T("GPAC DirectDraw FS Output"), WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL); +#else + ctx->fs_hwnd = CreateWindow("GPAC DirectDraw Output", "GPAC DirectDraw FS Output", WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL); +#endif + if (!ctx->fs_hwnd) { + return 0; + } + ShowWindow(ctx->fs_hwnd, SW_HIDE); + +#ifdef _WIN32_WCE + ctx->gl_hwnd = CreateWindow(_T("GPAC DirectDraw Output"), _T("GPAC OpenGL Offscreen"), WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL); +#else + ctx->gl_hwnd = CreateWindow("GPAC DirectDraw Output", "GPAC OpenGL Offscreen", WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL); +#endif + if (!ctx->gl_hwnd) { + return 0; + } + ShowWindow(ctx->gl_hwnd, SW_HIDE); + + /*if visible set focus*/ + if (!ctx->switch_res) SetFocus(ctx->os_hwnd); + + ctx->switch_res = 0; + SetWindowLong(ctx->os_hwnd, GWL_USERDATA, (LONG) vout); + SetWindowLong(ctx->fs_hwnd, GWL_USERDATA, (LONG) vout); + + /*load cursors*/ + ctx->curs_normal = LoadCursor(NULL, IDC_ARROW); + assert(ctx->curs_normal); + ctx->curs_hand = LoadCursor(hInst, MAKEINTRESOURCE(IDC_HAND_PTR)); + ctx->curs_collide = LoadCursor(hInst, MAKEINTRESOURCE(IDC_COLLIDE)); + ctx->cursor_type = GF_CURSOR_NORMAL; + + return 1; +} + +u32 DD_WindowThread(void *par) +{ + MSG msg; + + GF_VideoOutput *vout = par; + DDContext *ctx = (DDContext *)vout->opaque; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[DirectXOutput] Entering thread ID %d\n", gf_th_id() )); + + if (DD_InitWindows(vout, ctx)) { + ctx->th_state = 1; + while (GetMessage (&(msg), NULL, 0, 0)) { + TranslateMessage (&(msg)); + DispatchMessage (&(msg)); + } + } + ctx->th_state = 2; + return 0; +} + + +void DD_SetupWindow(GF_VideoOutput *dr, u32 flags) +{ + DDContext *ctx = (DDContext *)dr->opaque; + + if (ctx->os_hwnd) { + /*override window proc*/ + if (!(flags & GF_TERM_NO_WINDOWPROC_OVERRIDE) ) { + ctx->orig_wnd_proc = GetWindowLong(ctx->os_hwnd, GWL_WNDPROC); + SetWindowLong(ctx->os_hwnd, GWL_WNDPROC, (DWORD) DD_WindowProc); + } + ctx->parent_wnd = GetParent(ctx->os_hwnd); + } + ctx->switch_res = flags; + + /*create event thread*/ + ctx->th = gf_th_new("DirectX Video"); + gf_th_run(ctx->th, DD_WindowThread, dr); + while (!ctx->th_state) gf_sleep(2); + + if (!the_video_output) the_video_output = dr; +} + +static void dd_closewindow(HWND hWnd) +{ + PostMessage(hWnd, WM_DESTROY, 0, 0); +} + +void DD_ShutdownWindow(GF_VideoOutput *dr) +{ + DDContext *ctx = (DDContext *)dr->opaque; + + if (ctx->owns_hwnd) { + dd_closewindow(ctx->os_hwnd); + } else if (ctx->orig_wnd_proc) { + /*restore window proc*/ + SetWindowLong(ctx->os_hwnd, GWL_WNDPROC, ctx->orig_wnd_proc); + ctx->orig_wnd_proc = 0L; + } + dd_closewindow(ctx->fs_hwnd); + dd_closewindow(ctx->gl_hwnd); + + while (ctx->th_state!=2) + gf_sleep(10); + + gf_th_del(ctx->th); + ctx->th = NULL; + +#ifdef _WIN32_WCE + UnregisterClass(_T("GPAC DirectDraw Output"), GetModuleHandle(_T("gm_dx_hw.dll")) ); +#else + UnregisterClass("GPAC DirectDraw Output", GetModuleHandle("gm_dx_hw.dll")); +#endif + ctx->os_hwnd = NULL; + ctx->fs_hwnd = NULL; + ctx->gl_hwnd = NULL; + the_video_output = NULL; +} + +void DD_SetCursor(GF_VideoOutput *dr, u32 cursor_type) +{ + DDContext *ctx = (DDContext *)dr->opaque; + if (cursor_type==GF_CURSOR_HIDE) { + if (ctx->cursor_type!=GF_CURSOR_HIDE) { + ShowCursor(FALSE); + ctx->cursor_type = cursor_type; + } + return; + } + if (ctx->cursor_type==GF_CURSOR_HIDE) ShowCursor(TRUE); + ctx->cursor_type = cursor_type; + + switch (cursor_type) { + case GF_CURSOR_ANCHOR: + case GF_CURSOR_TOUCH: + case GF_CURSOR_ROTATE: + case GF_CURSOR_PROXIMITY: + case GF_CURSOR_PLANE: + SetCursor(ctx->curs_hand); + break; + case GF_CURSOR_COLLIDE: + SetCursor(ctx->curs_collide); + break; + default: + SetCursor(ctx->curs_normal); + break; + } +} + +HWND DD_GetGlobalHWND() +{ + if (!the_video_output) return NULL; + return ((DDContext*)the_video_output->opaque)->os_hwnd; +} + +/*Note: all calls to SetWindowPos are made in a non-blocking way using SWP_ASYNCWINDOWPOS. This avoids deadlocks +when the compositor request a size change and the DX window thread has grabbed the main compositor mutex. +This typically happens when switching playlist items as fast as possible*/ +GF_Err DD_ProcessEvent(GF_VideoOutput*dr, GF_Event *evt) +{ + DDContext *ctx = (DDContext *)dr->opaque; + + if (!evt) { + return GF_OK; + } + + switch (evt->type) { + case GF_EVENT_SET_CURSOR: + DD_SetCursor(dr, evt->cursor.cursor_type); + break; + case GF_EVENT_SET_CAPTION: +#ifndef _WIN32_WCE + if (evt->caption.caption) SetWindowText(ctx->os_hwnd, evt->caption.caption); +#endif + break; + case GF_EVENT_MOVE: + if (evt->move.relative == 2) { + u32 x, y, fsw, fsh; + x = y = 0; + fsw = GetSystemMetrics(SM_CXSCREEN); + fsh = GetSystemMetrics(SM_CYSCREEN); + + if (evt->move.align_x==1) x = (fsw - ctx->width) / 2; + else if (evt->move.align_x==2) x = fsw - ctx->width; + + if (evt->move.align_y==1) y = (fsh - ctx->height) / 2; + else if (evt->move.align_y==2) y = fsh - ctx->height; + + SetWindowPos(ctx->os_hwnd, NULL, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS); + } + else if (evt->move.relative) { + POINT pt; + pt.x = pt.y = 0; + MapWindowPoints(ctx->os_hwnd, NULL, &pt, 1); + SetWindowPos(ctx->os_hwnd, NULL, evt->move.x + pt.x, evt->move.y + pt.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS); + } else { + SetWindowPos(ctx->os_hwnd, NULL, evt->move.x, evt->move.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS); + } + break; + case GF_EVENT_SHOWHIDE: + ShowWindow(ctx->os_hwnd, evt->show.show_type ? SW_SHOW : SW_HIDE); + break; + /*if scene resize resize window*/ + case GF_EVENT_SIZE: + if (ctx->owns_hwnd) { + if (ctx->windowless) + SetWindowPos(ctx->os_hwnd, NULL, 0, 0, evt->size.width, evt->size.height, SWP_NOZORDER | SWP_NOMOVE | SWP_ASYNCWINDOWPOS); + else + SetWindowPos(ctx->os_hwnd, NULL, 0, 0, evt->size.width + ctx->off_w, evt->size.height + ctx->off_h, SWP_NOZORDER | SWP_NOMOVE | SWP_ASYNCWINDOWPOS); + + if (ctx->fullscreen) { + ctx->store_width = evt->size.width; + ctx->store_height = evt->size.height; + } + } + break; + /*HW setup*/ + case GF_EVENT_VIDEO_SETUP: + switch (evt->setup.opengl_mode) { + case 0: + ctx->output_3d_type = 0; + return DD_SetBackBufferSize(dr, evt->setup.width, evt->setup.height, evt->setup.system_memory); + case 1: + ctx->output_3d_type = 1; + ctx->width = evt->setup.width; + ctx->height = evt->setup.height; + ctx->gl_double_buffer = evt->setup.back_buffer; + return DD_SetupOpenGL(dr); + case 2: + ctx->output_3d_type = 2; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DX Out] Attempting to resize Offscreen OpenGL window to %d x %d\n", evt->size.width, evt->size.height)); + SetWindowPos(ctx->gl_hwnd, NULL, 0, 0, evt->size.width, evt->size.height, SWP_NOZORDER | SWP_NOMOVE | SWP_ASYNCWINDOWPOS); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DX Out] Resizing Offscreen OpenGL window to %d x %d\n", evt->size.width, evt->size.height)); + SetForegroundWindow(ctx->cur_hwnd); + ctx->gl_double_buffer = evt->setup.back_buffer; + return DD_SetupOpenGL(dr); + } + } + return GF_OK; +} diff --git a/modules/dx_hw/hand.cur b/modules/dx_hw/hand.cur new file mode 100644 index 0000000000000000000000000000000000000000..0a3cc6e9ec100655e500533613ec46e1cabb9632 GIT binary patch literal 2238 zcmeH{p>A725Jkt8rCO0eLyQcfpc)S{Y84)695%lYSexV%EG-KNDE1fFtWt)DG*YFo zAcwgsn4X=NIF|g9u$0u!?7qFTJNJ0jj>re;$kCDH>w8aRz|&{qi1<o$1d-RYn^Vg$ zC5b$pHWMR{vRp1@`Ex1jA6L@tcBR+rNx$Eh!^1-v3<feB4(0gxSVp6fOePaKJw26^ zlM@+_$1<DEC`jZqYbM5&Madaa2$x(cnxZM1RxO$>hHmJFZs>+yj}`QSURV_Lg3ct4 z?&yy02#y}CgLQag#i;lzg_J`oA!*N`FenTPgTjzVFenTPgTkONBqj_BgTkONC=7`X zgTkONC=3dN1{xd+hgOb=>;MXLeHX$B^!kxuG0Y7kqo~1>Z6Ikp7%T=$wgnD@!{9JD z432CS90r5IU@+9y85{<K!QglWW3~{V=FK|WTe|}l-i2M+YP<_f1=E5-fvMnKNEV6| zybIoW9qEmZf5E@tU$3a(U+7JNp}<h+L%lY-lAj)hnXz6YJ#hRSHiyIU%d4#%^uXcp z%E1TA>*kQd;;=Xnj$wWUU~vvQn;gMm;g6sWcjB;k?MJfIZ$rHWG#CPgfFWQA7y^d; zI>F#D1PlR#!$28={V)U!0Ykt*X#yRFfFWQA00MufSa2}dWYjR$21jJ=y2oU-TFKej znVg@W%f-co%;$5tzP^^lVj(v-H*$M>D|dHya({m>4-XHr*=%I}C*<n#lHc)^?$+Nx zov4?qTDLAk4*tL1p#yX}UoTm`k$*}@v=?|I|D0|=zmsp<u$ykpAMEVd2L$@HJh?aW z2irUk`}I+>#N*hu;W^)y-8tW$ex-7A;03I0wSS%8g6u!+T+OSGn@+@2tohGT<5$MF aSV|L9W_gTH%(;(q>yN}20*BrB&H67hVV}|f literal 0 HcmV?d00001 diff --git a/modules/dx_hw/resource.h b/modules/dx_hw/resource.h new file mode 100644 index 0000000..8a658fd --- /dev/null +++ b/modules/dx_hw/resource.h @@ -0,0 +1,21 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by dx_hw.rc +// +#define IDC_HAND_PTR 103 +#define IDC_COLLIDE 104 +#define IDC_HAND_CLOSE 148 +#define IDC_HAND_OPEN 149 +#define IDC_ZOOM_IN 150 +#define IDC_ZOOM_OUT 151 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/modules/epoc_hw/epoc_aout.cpp b/modules/epoc_hw/epoc_aout.cpp new file mode 100644 index 0000000..52c5435 --- /dev/null +++ b/modules/epoc_hw/epoc_aout.cpp @@ -0,0 +1,361 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / wave audio render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <gpac/modules/audio_out.h> +/*symbian audio stuff*/ +#include <mdaaudiooutputstream.h> +#include <mda/common/audio.h> + +#define EPOC_MAX_BUFFERS 8 + +enum { + EPOC_AUDIO_INIT = 0, + EPOC_AUDIO_OPEN, + EPOC_AUDIO_PLAY, + EPOC_AUDIO_ERROR, +}; + +class EPOCAudio : public MMdaAudioOutputStreamCallback +{ +public: + EPOCAudio(); + virtual ~EPOCAudio(); + + virtual void MaoscOpenComplete( TInt aError ); + virtual void MaoscBufferCopied( TInt aError, const TDesC8& a_Buffer); + virtual void MaoscPlayComplete( TInt ); + + GF_Err Open(u32 sample_rate, Bool stereo); + void Close(Bool and_wait); + + CMdaAudioOutputStream* m_stream; + TMdaAudioDataSettings m_settings; + u32 state; + + /*desired config*/ + u32 cfg_num_buffers, cfg_total_duration, init_vol, init_pan; + /*actual config*/ + u32 num_buffers, total_duration, buffer_len; + + /*audio buffers*/ + char *buffers[EPOC_MAX_BUFFERS]; + TPtrC8 sent_buffers[EPOC_MAX_BUFFERS]; + u32 buffer_size; + u32 current_buffer, nb_buffers_queued; +}; + +EPOCAudio::EPOCAudio() +{ + u32 i; + m_stream = NULL; + for (i=0; i<EPOC_MAX_BUFFERS; i++) buffers[i] = NULL; + state = EPOC_AUDIO_INIT; + num_buffers = 0; + init_vol = 100; +} + +EPOCAudio::~EPOCAudio() +{ + if (m_stream) { + delete m_stream; + } +} + +GF_Err EPOCAudio::Open(u32 sample_rate, Bool stereo) +{ + TInt res = 0; + u32 count; + TMdaAudioDataSettings::TAudioCaps epoc_sr; + + + switch (sample_rate) { + case 8000: epoc_sr = TMdaAudioDataSettings::ESampleRate8000Hz; break; + case 11025: epoc_sr = TMdaAudioDataSettings::ESampleRate11025Hz; break; + case 12000: epoc_sr = TMdaAudioDataSettings::ESampleRate12000Hz; break; + case 16000: epoc_sr = TMdaAudioDataSettings::ESampleRate16000Hz; break; + case 22050: epoc_sr = TMdaAudioDataSettings::ESampleRate22050Hz; break; + case 24000: epoc_sr = TMdaAudioDataSettings::ESampleRate24000Hz; break; + case 32000: epoc_sr = TMdaAudioDataSettings::ESampleRate32000Hz; break; + case 44100: epoc_sr = TMdaAudioDataSettings::ESampleRate44100Hz; break; + case 48000: epoc_sr = TMdaAudioDataSettings::ESampleRate48000Hz; break; + default: + return GF_NOT_SUPPORTED; + } + + state = EPOC_AUDIO_INIT; + + gf_sleep(10); + TRAP(res, m_stream = CMdaAudioOutputStream::NewL(*this) ); + if ((res!=KErrNone) || !m_stream) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOCAudio] Cannot create output audio stream\n")); + return GF_IO_ERR; + } + m_stream->Open(&m_settings); + + /*wait for ack - if not getting it in 50*40 = 2sec, abort*/ + count = 50; + while (count) { + if (state == EPOC_AUDIO_OPEN) break; + else if (state == EPOC_AUDIO_ERROR) { + return GF_IO_ERR; + } + gf_sleep(40); + + TInt error; + CActiveScheduler::RunIfReady(error, CActive::EPriorityIdle); + count--; + } + if (state != EPOC_AUDIO_OPEN) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOCAudio] Failed to open sound device - is it present?\n")); + return GF_NOT_SUPPORTED; + } + + TRAP(res, m_stream->SetAudioPropertiesL(epoc_sr, stereo ? TMdaAudioDataSettings::EChannelsStereo : TMdaAudioDataSettings::EChannelsMono) ); + m_stream->SetPriority(EPriorityAbsoluteHigh, EMdaPriorityPreferenceTime ); + m_stream->SetVolume(init_vol * m_stream->MaxVolume() / 100); + + current_buffer = nb_buffers_queued = 0; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOCAudio] output audio stream ready - sample rate %d - %d channels\n", sample_rate, stereo ? 2 : 1)); + return GF_OK; +} + +void EPOCAudio::Close(Bool and_wait) +{ + u32 i; + if (m_stream) { + if (state==EPOC_AUDIO_PLAY) { +#if 0 + m_stream->Stop(); + + while (0 && and_wait) { + + if (state != EPOC_AUDIO_PLAY) break; + gf_sleep(1); + + TInt error; + CActiveScheduler::RunIfReady(error, CActive::EPriorityIdle); + } +#endif + } + delete m_stream; + m_stream = NULL; + } + for (i=0; i<num_buffers; i++) { + if (buffers[i]) free(buffers[i]); + buffers[i] = NULL; + } + num_buffers = 0; + state = EPOC_AUDIO_INIT; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOCAudio] output audio stream closed\n")); +} + +void EPOCAudio::MaoscOpenComplete(TInt aError) +{ + if (aError) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOCAudio] Failed to open sound device - error %d\n", aError)); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOCAudio] Sound device opened\n", aError)); + state = EPOC_AUDIO_OPEN; + } +} + +void EPOCAudio::MaoscBufferCopied(TInt aError, const TDesC8& a_Buffer) +{ + assert(nb_buffers_queued); + nb_buffers_queued--; + state = nb_buffers_queued ? EPOC_AUDIO_PLAY : EPOC_AUDIO_OPEN; +} + +void EPOCAudio::MaoscPlayComplete(TInt aError) +{ + if (aError) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOCAudio] Playback stoped due to error %d\n", aError)); + state = EPOC_AUDIO_ERROR; + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOCAudio] Playback stoped due to user request\n")); + state = EPOC_AUDIO_OPEN; + } +} + + +static GF_Err EAUD_Setup(GF_AudioOutput *dr, void *os_handle, u32 num_buffers, u32 total_duration) +{ + EPOCAudio *ctx = (EPOCAudio *)dr->opaque; + ctx->cfg_num_buffers = MAX(num_buffers, EPOC_MAX_BUFFERS); + ctx->cfg_total_duration = total_duration; + return GF_OK; +} + +static void EAUD_Shutdown(GF_AudioOutput *dr) +{ + EPOCAudio *ctx = (EPOCAudio *)dr->opaque; + ctx->Close(1); +} + + +/*we assume what was asked is what we got*/ +static GF_Err EAUD_ConfigureOutput(GF_AudioOutput *dr, u32 *SampleRate, u32 *NbChannels, u32 *nbBitsPerSample, u32 channel_cfg) +{ + u32 snd_align, bps, i; + GF_Err e; + EPOCAudio *ctx = (EPOCAudio *)dr->opaque; + + ctx->Close(1); + + *nbBitsPerSample = 16; + if (*NbChannels > 2) *NbChannels = 2; + + e = ctx->Open(*SampleRate, (*NbChannels ==2) ? 1 : 0); + if (e) return e; + + snd_align = *NbChannels * 2; + bps = snd_align * *SampleRate; + + if (ctx->cfg_total_duration) { + ctx->num_buffers = ctx->cfg_num_buffers; + ctx->buffer_size = (bps*ctx->cfg_total_duration/1000) / ctx->num_buffers; + } else { + ctx->num_buffers = 4; + /*use 25 ms buffers*/ + ctx->buffer_size = bps / 40; + } + ctx->buffer_size /= snd_align; + ctx->buffer_size *= snd_align; + + ctx->buffer_len = ctx->buffer_size * 1000 / bps; + ctx->total_duration = ctx->buffer_len * ctx->num_buffers; + + for (i=0; i<ctx->num_buffers; i++) { + ctx->buffers[i] = (char *)malloc(sizeof(char)*ctx->buffer_size); + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOCAudio] Output audio stream configured - %d buffers of %d ms each\n", ctx->num_buffers, ctx->buffer_len)); + + return GF_OK; +} + +static void EAUD_WriteAudio(GF_AudioOutput *dr) +{ + EPOCAudio *ctx = (EPOCAudio *)dr->opaque; + + /*no buffers available...*/ + if (ctx->nb_buffers_queued == ctx->num_buffers) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOCAudio] Audio queue full - yielding to app\n")); + User::After(0); + TInt error; + CActiveScheduler::RunIfReady(error, CActive::EPriorityIdle); + return; + } + + while (ctx->nb_buffers_queued < ctx->num_buffers) { + u32 written = dr->FillBuffer(dr->audio_renderer, ctx->buffers[ctx->current_buffer], ctx->buffer_size); + //GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOCAudio] Filling audio buffer %d / %d\n", ctx->current_buffer, ctx->num_buffers)); + if (!written) return; + + ctx->sent_buffers[ctx->current_buffer].Set((const TText8 *) ctx->buffers[ctx->current_buffer], written); + + ctx->nb_buffers_queued++; + ctx->m_stream->WriteL(ctx->sent_buffers[ctx->current_buffer]); + ctx->current_buffer = (ctx->current_buffer + 1) % ctx->num_buffers; + } +} + +static void EAUD_Play(GF_AudioOutput *dr, u32 PlayType) +{ +} + +static void EAUD_SetVolume(GF_AudioOutput *dr, u32 Volume) +{ + EPOCAudio *ctx = (EPOCAudio *)dr->opaque; + ctx->init_vol = Volume; + if (ctx->m_stream) + ctx->m_stream->SetVolume(ctx->init_vol * ctx->m_stream->MaxVolume() / 100); +} + +static void EAUD_SetPan(GF_AudioOutput *dr, u32 Pan) +{ +} + + +static GF_Err EAUD_QueryOutputSampleRate(GF_AudioOutput *dr, u32 *desired_samplerate, u32 *NbChannels, u32 *nbBitsPerSample) +{ + *nbBitsPerSample = 16; + if (*NbChannels > 2) *NbChannels = 2; + return GF_OK; +} + +static u32 EAUD_GetAudioDelay(GF_AudioOutput *dr) +{ + EPOCAudio *ctx = (EPOCAudio *)dr->opaque; + return ctx->current_buffer*ctx->buffer_len; +} + +static u32 EAUD_GetTotalBufferTime(GF_AudioOutput *dr) +{ + EPOCAudio *ctx = (EPOCAudio *)dr->opaque; + return ctx->total_duration; +} + + +#ifdef __cplusplus +extern "C" { +#endif + +void *EPOC_aout_new() +{ + GF_AudioOutput *driv; + driv = (GF_AudioOutput *) malloc(sizeof(GF_AudioOutput)); + memset(driv, 0, sizeof(GF_AudioOutput)); + GF_REGISTER_MODULE_INTERFACE(driv, GF_AUDIO_OUTPUT_INTERFACE, "EPOC Audio Output", "gpac distribution") + + driv->Setup = EAUD_Setup; + driv->Shutdown = EAUD_Shutdown; + driv->ConfigureOutput = EAUD_ConfigureOutput; + driv->GetAudioDelay = EAUD_GetAudioDelay; + driv->GetTotalBufferTime = EAUD_GetTotalBufferTime; + driv->SetVolume = EAUD_SetVolume; + driv->SetPan = EAUD_SetPan; + driv->Play = EAUD_Play; + driv->QueryOutputSampleRate = EAUD_QueryOutputSampleRate; + driv->WriteAudio = EAUD_WriteAudio; + + driv->SelfThreaded = 0; + driv->opaque = new EPOCAudio(); + + return driv; +} + +void EPOC_aout_del(void *ifce) +{ + GF_AudioOutput *dr = (GF_AudioOutput *) ifce; + EPOCAudio *ctx = (EPOCAudio*)dr->opaque; + delete ctx; + free(dr); +} + +#ifdef __cplusplus +} +#endif + diff --git a/modules/epoc_hw/epoc_codec.cpp b/modules/epoc_hw/epoc_codec.cpp new file mode 100644 index 0000000..57dd7cb --- /dev/null +++ b/modules/epoc_hw/epoc_codec.cpp @@ -0,0 +1,425 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / AAC reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <gpac/modules/codec.h> +#include <gpac/constants.h> +#include <gpac/avparse.h> + +#include <mmf/server/mmfcodec.h> +#include <mmf/plugin/mmfcodecimplementationuids.hrh> + +#define KMMFFourCCCodeEAACP 0x43414520 // ' ' 'E' 'A' 'C' + +#if defined(__SYMBIAN32__) && !defined(__SERIES60_3X__) +//codec configuration UID +#define KUidMmfCodecAudioSettings 0x10203622 +#endif + +enum +{ + GF_EPOC_HAS_AMR = 1, + GF_EPOC_HAS_AMR_WB = 1<<1, + GF_EPOC_HAS_AAC = 1<<2, + GF_EPOC_HAS_HEAAC = 1<<3, + GF_EPOC_HAS_MP3 = 1<<4, +}; + +typedef struct +{ + u32 caps; + Bool is_audio; + + u32 sample_rate, out_size, num_samples; + u8 num_channels; + + const char *codec_name; + CMMFCodec *dec; + + CMMFPtrBuffer *mmf_in, *mmf_out; + TPtr8 ptr_in, ptr_out; +} EPOCCodec; + +static void EDEC_LoadCaps(GF_BaseDecoder *ifcg) +{ + EPOCCodec *ctx = (EPOCCodec *)ifcg->privateStack; + CMMFCodec *codec = NULL; + TInt err; + + ctx->caps = 0; + /*AMR*/ + TRAP(err, codec = CMMFCodec::NewL(KMMFFourCCCodeAMR, KMMFFourCCCodePCM16)); + if (err==KErrNone) { + ctx->caps |= GF_EPOC_HAS_AMR; + delete codec; + } + /*AMR-WB*/ + TRAP(err, codec = CMMFCodec::NewL(KMMFFourCCCodeAWB, KMMFFourCCCodePCM16)); + if (err==KErrNone) { + ctx->caps |= GF_EPOC_HAS_AMR_WB; + delete codec; + } + /*AAC*/ + TRAP(err, codec = CMMFCodec::NewL(KMMFFourCCCodeAAC, KMMFFourCCCodePCM16)); + if (err==KErrNone) { + ctx->caps |= GF_EPOC_HAS_AAC; + delete codec; + } + /*HE-AAC*/ + TRAP(err, codec = CMMFCodec::NewL(KMMFFourCCCodeEAACP, KMMFFourCCCodePCM16)); + if (err==KErrNone) { + ctx->caps |= GF_EPOC_HAS_HEAAC; + delete codec; + } + + /*MP3*/ + TRAP(err, codec = CMMFCodec::NewL(KMMFFourCCCodeMP3, KMMFFourCCCodePCM16)); + if (err==KErrNone) { + ctx->caps |= GF_EPOC_HAS_MP3; + delete codec; + } + +} + + +static GF_Err EDEC_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + RArray<TInt> configParams; + GF_M4ADecSpecInfo a_cfg; + Bool aac_sbr_upsample; + TInt err; + EPOCCodec *ctx = (EPOCCodec *)ifcg->privateStack; + if (esd->dependsOnESID) return GF_NOT_SUPPORTED; + if (ctx->dec) return GF_BAD_PARAM; + + + /*audio decs*/ + switch (esd->decoderConfig->objectTypeIndication) { + /*MPEG2 aac*/ + case 0x66: + case 0x67: + case 0x68: + /*MPEG4 aac*/ + case 0x40: + if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->data) return GF_NON_COMPLIANT_BITSTREAM; + if (gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg) != GF_OK) return GF_NON_COMPLIANT_BITSTREAM; + + aac_sbr_upsample = 0; +#if !defined(__SYMBIAN32__) || defined(__SERIES60_3X__) + if (a_cfg.has_sbr && (ctx->caps & GF_EPOC_HAS_HEAAC)) { + TRAP(err, ctx->dec = CMMFCodec::NewL(KMMFFourCCCodeEAACP, KMMFFourCCCodePCM16)); + if (err != KErrNone) { + a_cfg.has_sbr = 0; + goto retry_no_sbr; + } + aac_sbr_upsample = (a_cfg.base_sr<=24000) ? 1 : 0; + + configParams.Append(a_cfg.base_sr); // 0: Input Sample Frequency + configParams.Append(a_cfg.nb_chan); // 1: Num Channels [1|2] + configParams.Append(1); // 2: Input Profile Object type [1 - LC, 3 - LTP] + configParams.Append(aac_sbr_upsample ? 2048 : 1024); // 3: Output Frame Size + configParams.Append(1024); // 4: Input Frame Len [1024, 960] + configParams.Append(a_cfg.base_sr); // 5: Input Sample Rate + configParams.Append(0); // 6: 0 + configParams.Append(aac_sbr_upsample ? 0 : 1); // 7: Down Sample Mode [0|1] + configParams.Append(16); // 8: Sample resolution, 8Khz (8-bit PCM) or 16Khz (16-bit) + configParams.Append(a_cfg.sbr_sr); // 9: Output Sample Frequency + configParams.Append(5); // 10: Extension Object Type + + TRAP(err, ctx->dec->ConfigureL(TUid::Uid(KUidMmfCodecAudioSettings), (TDesC8&) configParams)); + if (err != KErrNone) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[EPOC Decoder] Failed to configure HE-AAC decoder (error %d) - retrying with AAC\n", err)); + configParams.Reset(); + goto retry_no_sbr; + } + ctx->codec_name = "EPOC HE-AAC Decoder"; + ctx->num_channels = a_cfg.nb_chan; + ctx->num_samples = aac_sbr_upsample ? 2048 : 1024; + ctx->sample_rate = a_cfg.sbr_sr; + } else +#endif + { +retry_no_sbr: + TRAP(err, ctx->dec = CMMFCodec::NewL(KMMFFourCCCodeAAC, KMMFFourCCCodePCM16)); + if (err != KErrNone) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[EPOC Decoder] Unable to load native codec: error %d\n", err)); + return GF_IO_ERR; + } + configParams.Append(a_cfg.base_sr); // Input Sample Rate + configParams.Append(a_cfg.nb_chan); // Num Channels [1|2] + configParams.Append((a_cfg.base_object_type==GF_M4A_AAC_LC) ? 1 : 3); // AAC Input Profile [1 - LC, 3 - LTP] + configParams.Append(1024); // Input Frame Len [1024, 960] + configParams.Append(0); // AAC Down Mixing [0-none | 1 mono | 2 stereo] + configParams.Append(0); // Aac output channels selection {0 - none, 1 - 1, 2 - 2} + configParams.Append(0); // Aac decimation factor {0 - none, 2 - decimation by 2, 4 - decimation by 4} + configParams.Append(0); // Aac concealment - It can be {0 - none, 1 - basic} + configParams.Append(16); // Sample resolution - It can be {16 - 16-bit resolution} + configParams.Append(0); // Sample Rate Conversion 0 : none + + TRAP(err, ctx->dec->ConfigureL(TUid::Uid(KUidMmfCodecAudioSettings), (TDesC8&) configParams)); + if (err != KErrNone) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[EPOC Decoder] Failed to configure AAC decoder (error %d)\n", err)); + return GF_NON_COMPLIANT_BITSTREAM; + } + ctx->codec_name = "EPOC AAC Decoder"; + ctx->num_channels = a_cfg.nb_chan; + ctx->num_samples = 1024; + ctx->sample_rate = a_cfg.base_sr; + } + ctx->out_size = ctx->num_channels * ctx->num_samples * 2; + break; + /*non-mpeg4 codecs*/ + case GPAC_OTI_MEDIA_GENERIC: + if (!esd->decoderConfig->decoderSpecificInfo || esd->decoderConfig->decoderSpecificInfo->dataLength<4) return GF_BAD_PARAM; + if (!strnicmp(esd->decoderConfig->decoderSpecificInfo->data, "samr", 4) || !strnicmp(esd->decoderConfig->decoderSpecificInfo->data, "amr ", 4)) { + TRAP(err, ctx->dec = CMMFCodec::NewL(KMMFFourCCCodeAMR, KMMFFourCCCodePCM16)); + if (err != KErrNone) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[EPOC Decoder] Unable to load native codec: error %d\n", err)); + return GF_IO_ERR; + } + ctx->is_audio = 1; + ctx->num_channels = 1; + ctx->num_samples = 160; + ctx->sample_rate = 8000; + ctx->out_size = ctx->num_channels * ctx->num_samples * 2; + ctx->codec_name = "EPOC AMR Decoder"; + } + else if (!strnicmp(esd->decoderConfig->decoderSpecificInfo->data, "sawb", 4)) { + TRAP(err, ctx->dec = CMMFCodec::NewL(KMMFFourCCCodeAWB, KMMFFourCCCodePCM16)); + if (err != KErrNone) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[EPOC Decoder] Unable to load native codec: error %d\n", err)); + return GF_IO_ERR; + } + ctx->is_audio = 1; + ctx->num_channels = 1; + ctx->num_samples = 320; + ctx->sample_rate = 16000; + ctx->out_size = ctx->num_channels * ctx->num_samples * 2; + ctx->codec_name = "EPOC AMR-Wideband Decoder"; + } + break; + default: + return GF_BAD_PARAM; + } + + ctx->mmf_in = CMMFPtrBuffer::NewL(); + ctx->mmf_out = CMMFPtrBuffer::NewL(); + + return GF_OK; +} + +static GF_Err EDEC_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + EPOCCodec *ctx = (EPOCCodec *)ifcg->privateStack; + + if (ctx->mmf_in) { + delete ctx->mmf_in; + ctx->mmf_in = NULL; + } + if (ctx->mmf_out) { + delete ctx->mmf_out; + ctx->mmf_out = NULL; + } + if (ctx->dec) { + delete ctx->dec; + ctx->dec = NULL; + } + return GF_OK; +} +static GF_Err EDEC_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + EPOCCodec *ctx = (EPOCCodec *)ifcg->privateStack; + + switch (capability->CapCode) { + /*not tested yet*/ + case GF_CODEC_RESILIENT: + capability->cap.valueInt = 1; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_SAMPLERATE: + capability->cap.valueInt = ctx->sample_rate; + break; + case GF_CODEC_NB_CHAN: + capability->cap.valueInt = ctx->num_channels; + break; + case GF_CODEC_BITS_PER_SAMPLE: + capability->cap.valueInt = 16; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = 4; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = 12; + break; + case GF_CODEC_CU_DURATION: + capability->cap.valueInt = ctx->num_samples; + break; + /*to refine, it seems that 4 bytes padding is not enough on all streams ?*/ + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 4; + break; + case GF_CODEC_CHANNEL_CONFIG: + capability->cap.valueInt = (ctx->num_channels==1) ? GF_AUDIO_CH_FRONT_CENTER : (GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT); + break; + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} +static GF_Err EDEC_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like SR changing ...) */ + return GF_NOT_SUPPORTED; +} + + +static GF_Err EDEC_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + TCodecProcessResult res; + EPOCCodec *ctx = (EPOCCodec *)ifcg->privateStack; + + if (*outBufferLength < ctx->out_size) { + *outBufferLength = ctx->out_size; + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("AMR buffer too small\n")); + return GF_BUFFER_TOO_SMALL; + } + + ctx->ptr_in.Set((TUint8*)inBuffer, inBufferLength, inBufferLength); + ctx->mmf_in->SetPtr(ctx->ptr_in); + ctx->ptr_out.Set((TUint8*)outBuffer, *outBufferLength, *outBufferLength); + ctx->mmf_out->SetPtr(ctx->ptr_out); + + TRAPD(e, res = ctx->dec->ProcessL(*ctx->mmf_in, *ctx->mmf_out)); + if (res.iStatus==TCodecProcessResult::EProcessError) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[EPOC Decoder] Decode failed - error %d\n", res.iStatus)); + return GF_NON_COMPLIANT_BITSTREAM; + } + + return GF_OK; +} + +static const char *EDEC_GetCodecName(GF_BaseDecoder *ifcg) +{ + EPOCCodec *ctx = (EPOCCodec *)ifcg->privateStack; + return ctx->codec_name; +} + +static Bool EDEC_CanHandleStream(GF_BaseDecoder *ifcg, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + GF_M4ADecSpecInfo a_cfg; + EPOCCodec *ctx = (EPOCCodec *)ifcg->privateStack; + + /*audio decs*/ + if (StreamType == GF_STREAM_AUDIO) { + switch (ObjectType) { + /*MPEG2 aac*/ + case 0x66: + case 0x67: + case 0x68: + /*MPEG4 aac*/ + case 0x40: + if (!decSpecInfoSize || !decSpecInfo) return 0; + if (gf_m4a_get_config(decSpecInfo, decSpecInfoSize, &a_cfg) != GF_OK) return 0; + switch (a_cfg.base_object_type) { + /*only LTP and LC supported*/ + case GF_M4A_AAC_LC: + case GF_M4A_AAC_LTP: + if ((ctx->caps & GF_EPOC_HAS_AAC) || (ctx->caps & GF_EPOC_HAS_HEAAC) ) return 1; + default: + break; + } + break; + /*MPEG1 audio*/ + case 0x69: + /*MPEG2 audio*/ + case 0x6B: + /* NOT SUPPORTED YET if (ctx->caps & GF_EPOC_HAS_MP3) return 1; */ + break; + /*non-mpeg4 codecs*/ + case GPAC_OTI_MEDIA_GENERIC: + if (!decSpecInfoSize || !decSpecInfo) return 0; + if (decSpecInfoSize<4) return 0; + if (!strnicmp(decSpecInfo, "samr", 4) || !strnicmp(decSpecInfo, "amr ", 4)) { + if (ctx->caps & GF_EPOC_HAS_AMR) return 1; + } + if (!strnicmp(decSpecInfo, "sawb", 4)) { + if (ctx->caps & GF_EPOC_HAS_AMR_WB) return 1; + } + break; + /*cap query*/ + case 0: + return 1; + default: + return 0; + } + } + return 0; +} + +#ifdef __cplusplus +extern "C" { +#endif + +GF_BaseDecoder *EPOC_codec_new() +{ + GF_MediaDecoder *ifce; + EPOCCodec *ctx; + + GF_SAFEALLOC(ifce, GF_MediaDecoder); + GF_REGISTER_MODULE_INTERFACE(ifce, GF_MEDIA_DECODER_INTERFACE, "EPOC Native Decoder", "gpac distribution") + + GF_SAFEALLOC(ctx, EPOCCodec); + ifce->privateStack = ctx; + + /*setup our own interface*/ + ifce->AttachStream = EDEC_AttachStream; + ifce->DetachStream = EDEC_DetachStream; + ifce->GetCapabilities = EDEC_GetCapabilities; + ifce->SetCapabilities = EDEC_SetCapabilities; + ifce->ProcessData = EDEC_ProcessData; + ifce->CanHandleStream = EDEC_CanHandleStream; + ifce->GetName = EDEC_GetCodecName; + EDEC_LoadCaps((GF_BaseDecoder*)ifce); + return (GF_BaseDecoder *) ifce; +} + +void EPOC_codec_del(GF_BaseDecoder *ifcg) +{ + EPOCCodec *ctx = (EPOCCodec *)ifcg->privateStack; + + free(ctx); + free(ifcg); +} + + +#ifdef __cplusplus +} +#endif diff --git a/modules/epoc_hw/epoc_vout.cpp b/modules/epoc_hw/epoc_vout.cpp new file mode 100644 index 0000000..79884d5 --- /dev/null +++ b/modules/epoc_hw/epoc_vout.cpp @@ -0,0 +1,525 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / EPOC video output module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/*driver interface*/ +#include <gpac/modules/video_out.h> +#include <gpac/modules/audio_out.h> +#include <gpac/modules/codec.h> + +#include <w32std.h> + +#ifdef GPAC_USE_OGL_ES +#include <GLES/egl.h> +#endif + +typedef struct +{ + RWindow *window; + RWsSession *session; + + u32 pixel_format, bpp, width, height; + CWsScreenDevice *screen; + CFbsBitmap *surface; + CWindowGc *gc; + + char *locked_data; + u32 output_3d_type; + +#ifdef GPAC_USE_OGL_ES + EGLDisplay egl_display; + EGLSurface egl_surface; + EGLContext egl_context; +#endif + +} EPOCVideo; + + +static void EVID_ResetSurface(GF_VideoOutput *dr, Bool gl_only) +{ + EPOCVideo *ctx = (EPOCVideo *)dr->opaque; + +#ifdef GPAC_USE_OGL_ES + if (ctx->egl_display) { + eglMakeCurrent(ctx->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (ctx->egl_context) eglDestroyContext(ctx->egl_display, ctx->egl_context); + ctx->egl_context = NULL; + if (ctx->egl_surface) eglDestroySurface(ctx->egl_display, ctx->egl_surface); + ctx->egl_surface = NULL; + eglTerminate(ctx->egl_display); + ctx->egl_display = NULL; + } +#endif + if (gl_only) return; + + if (ctx->locked_data) ctx->surface->UnlockHeap(); + ctx->locked_data = NULL; + if (ctx->surface) delete ctx->surface; + ctx->surface = NULL; + if (ctx->gc) delete ctx->gc; + ctx->gc = NULL; + if (ctx->screen) delete ctx->screen; + ctx->screen = NULL; +} + +static GF_Err EVID_InitSurface(GF_VideoOutput *dr) +{ + TInt gl_buffer_size; + TInt e; + TDisplayMode disp_mode; + TSize s; + EPOCVideo *ctx = (EPOCVideo *)dr->opaque; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Reseting video\n")); + EVID_ResetSurface(dr, 0); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Video reset OK\n")); + + ctx->screen = new CWsScreenDevice(*ctx->session); + if (!ctx->screen) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot create screen device for session\n")); + return GF_IO_ERR; + } + e = ctx->screen->Construct(); + if (e != KErrNone) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot construct screen device for session - error %d\n", e)); + return GF_IO_ERR; + } + e = ctx->screen->CreateContext(ctx->gc); + if (e != KErrNone) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot create graphical context - error %d\n", e)); + return GF_IO_ERR; + } + + ctx->surface = new CFbsBitmap(); + if (!ctx->surface) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot allocate backbuffer surface\n")); + return GF_IO_ERR; + } + + s = ctx->window->Size(); + disp_mode = ctx->screen->DisplayMode(); + e = ctx->surface->Create(s, disp_mode); + if (e != KErrNone) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot create backbuffer surface - error %d\n", e)); + return GF_IO_ERR; + } + + gl_buffer_size = 0; + switch (disp_mode) { + case EGray256: + ctx->pixel_format = GF_PIXEL_GREYSCALE; + ctx->bpp = 1; + break; + case EColor64K: + ctx->pixel_format = GF_PIXEL_RGB_565; + ctx->bpp = 2; + gl_buffer_size = 16; + break; + case EColor16M: + ctx->pixel_format = GF_PIXEL_RGB_24; + ctx->bpp = 3; + gl_buffer_size = 24; + break; + /** 4096 colour display (12 bpp). */ + case EColor4K: + ctx->pixel_format = GF_PIXEL_RGB_444; + ctx->bpp = 2; + gl_buffer_size = 12; + break; + /** True colour display mode (32 bpp, but top byte is unused and unspecified) */ + case EColor16MU: + ctx->pixel_format = GF_PIXEL_RGB_32; + ctx->bpp = 4; + gl_buffer_size = 32; + break; +#if defined(__SERIES60_3X__) + /** Display mode with alpha (24bpp colour plus 8bpp alpha) */ + case EColor16MA: + ctx->pixel_format = GF_PIXEL_ARGB; + ctx->bpp = 4; + gl_buffer_size = 32; + break; +#endif + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Unsupported display type %d\n", disp_mode)); + return GF_NOT_SUPPORTED; + } + ctx->width = s.iWidth; + ctx->height = s.iHeight; + +#ifdef GPAC_USE_OGL_ES + if (ctx->output_3d_type==1) { + if (!gl_buffer_size) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Display mode not supported by OpenGL\n")); + return GF_IO_ERR; + } + ctx->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (ctx->egl_display == NULL) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot open OpenGL display\n")); + return GF_IO_ERR; + } + + if (eglInitialize(ctx->egl_display, NULL, NULL) == EGL_FALSE) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot initialize OpenGL display\n")); + return GF_IO_ERR; + } + EGLConfig *configList = NULL; + EGLint numOfConfigs = 0; + EGLint configSize = 0; + if (eglGetConfigs(ctx->egl_display, configList, configSize, &numOfConfigs) == EGL_FALSE) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot retrieve OpenGL configurations\n")); + return GF_IO_ERR; + } + configSize = numOfConfigs; + configList = (EGLConfig*) malloc(sizeof(EGLConfig)*configSize); + + // Define properties for the wanted EGLSurface + EGLint atts[7]; + const char *opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsDepth"); + + atts[0] = EGL_BUFFER_SIZE; atts[1] = gl_buffer_size; + atts[2] = EGL_DEPTH_SIZE; atts[3] = opt ? atoi(opt) : 16; + atts[4] = EGL_SURFACE_TYPE; atts[5] = EGL_PIXMAP_BIT; + atts[6] = EGL_NONE; + + if (eglChooseConfig(ctx->egl_display, atts, configList, configSize, &numOfConfigs) == EGL_FALSE) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot choose OpenGL configuration\n")); + return GF_IO_ERR; + } + EGLConfig gl_cfg = configList[0]; + free(configList); + + ctx->egl_surface = eglCreatePixmapSurface(ctx->egl_display, gl_cfg, ctx->surface, NULL); + if (ctx->egl_surface == NULL) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot create OpenGL surface\n")); + return GF_IO_ERR; + } + ctx->egl_context = eglCreateContext(ctx->egl_display, gl_cfg, EGL_NO_CONTEXT, NULL); + if (ctx->egl_context == NULL) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot create OpenGL context\n")); + return GF_IO_ERR; + } + if (eglMakeCurrent(ctx->egl_display, ctx->egl_surface, ctx->egl_surface, ctx->egl_context) == EGL_FALSE) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot bind OpenGL context to current thread\n")); + return GF_IO_ERR; + } + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Video OpenGL setup\n")); + } + +#endif + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Video setup OK - %d x %d @ PixelFormat %s\n", ctx->width, ctx->height, gf_4cc_to_str(ctx->pixel_format) )); + return GF_OK; +} + +#ifdef GPAC_USE_OGL_ES + +static GF_Err EVID_SetupOGL_ES_Offscreen(GF_VideoOutput *dr, u32 width, u32 height) +{ + EPOCVideo *ctx = (EPOCVideo *)dr->opaque; + EVID_ResetSurface(dr, 1); + if (!ctx->screen) return GF_NOT_SUPPORTED; + + TDisplayMode disp_mode = ctx->screen->DisplayMode(); + TInt gl_buffer_size = 0; + switch (disp_mode) { + case EColor64K: gl_buffer_size = 16; break; + case EColor16M: gl_buffer_size = 24; break; + /** 4096 colour display (12 bpp). */ + case EColor4K: gl_buffer_size = 12; break; + /** True colour display mode (32 bpp, but top byte is unused and unspecified) */ + case EColor16MU: gl_buffer_size = 32; break; +#if defined(__SERIES60_3X__) + /** Display mode with alpha (24bpp colour plus 8bpp alpha) */ + case EColor16MA: gl_buffer_size = 32; break; +#endif + case EGray256: + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Unsupported display type %d for OpenGL\n", disp_mode)); + return GF_NOT_SUPPORTED; + } + + ctx->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (ctx->egl_display == NULL) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot open OpenGL display\n")); + return GF_IO_ERR; + } + + if (eglInitialize(ctx->egl_display, NULL, NULL) == EGL_FALSE) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot initialize OpenGL display\n")); + return GF_IO_ERR; + } + + EGLConfig *configList = NULL; + EGLint numOfConfigs = 0; + EGLint configSize = 0; + if (eglGetConfigs(ctx->egl_display, configList, configSize, &numOfConfigs) == EGL_FALSE) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot retrieve OpenGL configurations\n")); + return GF_IO_ERR; + } + configSize = numOfConfigs; + configList = (EGLConfig*) malloc(sizeof(EGLConfig)*configSize); + + // Define properties for the wanted EGLSurface + EGLint atts[13]; + const char *opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsDepth"); + atts[0] = EGL_RED_SIZE; atts[1] = 8; + atts[2] = EGL_GREEN_SIZE; atts[3] = 8; + atts[4] = EGL_BLUE_SIZE; atts[5] = 8; + atts[6] = EGL_ALPHA_SIZE; atts[7] = (dr->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA) ? 8 : EGL_DONT_CARE; + atts[8] = EGL_SURFACE_TYPE; atts[9] = EGL_PBUFFER_BIT; + atts[10] = EGL_DEPTH_SIZE; atts[11] = opt ? atoi(opt) : 16; + atts[12] = EGL_NONE; + + if (eglChooseConfig(ctx->egl_display, atts, configList, configSize, &numOfConfigs) == EGL_FALSE) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot choose Offscreen OpenGL configuration\n")); + return GF_IO_ERR; + } + EGLConfig gl_cfg = configList[0]; + free(configList); + + atts[0] = EGL_WIDTH; atts[1] = width; + atts[2] = EGL_HEIGHT; atts[3] = height; + atts[4] = EGL_NONE; + + ctx->egl_surface = eglCreatePbufferSurface(ctx->egl_display, gl_cfg, atts); + if (ctx->egl_surface == NULL) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot create OpenGL Pbuffer\n")); + return GF_IO_ERR; + } + ctx->egl_context = eglCreateContext(ctx->egl_display, gl_cfg, EGL_NO_CONTEXT, NULL); + if (ctx->egl_context == NULL) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot create Offscreen OpenGL context\n")); + return GF_IO_ERR; + } + if (eglMakeCurrent(ctx->egl_display, ctx->egl_surface, ctx->egl_surface, ctx->egl_context) == EGL_FALSE) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[EPOC Video] Cannot bind Offscreen OpenGL context to current thread\n")); + return GF_IO_ERR; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Offscreen OpenGL setup - size %d x %d\n", width, height)); + return GF_OK; +} +#endif + + +static GF_Err EVID_Setup(GF_VideoOutput *dr, void *os_handle, void *os_display, u32 init_flags) +{ + GF_Err res; + EPOCVideo *ctx = (EPOCVideo *)dr->opaque; + + ctx->window = (RWindow *)os_handle; + ctx->session = (RWsSession *)os_display; + + res = EVID_InitSurface(dr); + + /*setup opengl offscreen*/ +#ifdef GPAC_USE_OGL_ES + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Querying Offscreen OpenGL Capabilities\n")); + dr->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA; + GF_Err e = EVID_SetupOGL_ES_Offscreen(dr, 20, 20); + if (e!=GF_OK) { + dr->hw_caps &= ~GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA; + e = EVID_SetupOGL_ES_Offscreen(dr, 20, 20); + } + if (!e) { + dr->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Offscreen OpenGL available - alpha support: %s\n", (dr->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA) ? "yes" : "no")); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Offscreen OpenGL not available\n")); + } +#endif + + return res; +} + +static void EVID_Shutdown(GF_VideoOutput *dr) +{ + EPOCVideo *ctx = (EPOCVideo *)dr->opaque; + EVID_ResetSurface(dr, 0); + ctx->session = NULL; + ctx->window = NULL; +} + +static GF_Err EVID_SetFullScreen(GF_VideoOutput *dr, Bool bOn, u32 *outWidth, u32 *outHeight) +{ + //EPOCVideo *ctx = (EPOCVideo *)dr->opaque; + return GF_NOT_SUPPORTED; +} + +static GF_Err EVID_Flush(GF_VideoOutput *dr, GF_Window *dest) +{ + EPOCVideo *ctx = (EPOCVideo *)dr->opaque; + + if (ctx->gc && ctx->surface) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Activating window\n")); + ctx->gc->Activate(*ctx->window); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Bliting backbuffer\n")); + ctx->gc->BitBlt(TPoint(0,0), ctx->surface); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Deactivating window\n")); + ctx->gc->Deactivate(); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Flush success\n")); + } + return GF_OK; +} + +static GF_Err EVID_ProcessEvent(GF_VideoOutput *dr, GF_Event *evt) +{ + //EPOCVideo *ctx = (EPOCVideo *)dr->opaque; + if (!evt) return GF_OK; + + switch (evt->type) { + case GF_EVENT_SHOWHIDE: + break; + case GF_EVENT_SIZE: + /*nothing to do since we don't own the window*/ + break; + case GF_EVENT_VIDEO_SETUP: + ((EPOCVideo *)dr->opaque)->output_3d_type = evt->setup.opengl_mode; + if (evt->setup.opengl_mode==2) { +#ifdef GPAC_USE_OGL_ES + return EVID_SetupOGL_ES_Offscreen(dr, evt->setup.width, evt->setup.height); +#else + return GF_NOT_SUPPORTED; +#endif + } + return EVID_InitSurface(dr/*, evt->size.width, evt->size.height*/); + } + return GF_OK; +} + +static GF_Err EVID_LockBackBuffer(GF_VideoOutput *dr, GF_VideoSurface *vi, Bool do_lock) +{ + EPOCVideo *ctx = (EPOCVideo *)dr->opaque; + if (!ctx->surface) return GF_BAD_PARAM; + + if (do_lock) { + if (!ctx->locked_data) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Locking backbuffer memory\n")); + ctx->surface->LockHeap(); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Retrieving backbuffer memory address\n")); + ctx->locked_data = (char *)ctx->surface->DataAddress(); + } + vi->height = ctx->height; + vi->width = ctx->width; + vi->is_hardware_memory = 0; + vi->pitch = ctx->width * ctx->bpp; + vi->pixel_format = ctx->pixel_format; + vi->video_buffer = ctx->locked_data; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Backbuffer locked OK - address 0x%08x\n", ctx->locked_data)); + } else { + if (ctx->locked_data) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Unlocking backbuffer memory\n")); + ctx->surface->UnlockHeap(); + ctx->locked_data = NULL; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[EPOC Video] Backbuffer unlocked OK\n")); + } + } + return GF_OK; +} + +static void *EPOC_vout_new() +{ + EPOCVideo *priv; + GF_VideoOutput *driv; + GF_SAFEALLOC(driv, GF_VideoOutput); + GF_REGISTER_MODULE_INTERFACE(driv, GF_VIDEO_OUTPUT_INTERFACE, "EPOC Video Output", "gpac distribution") + + GF_SAFEALLOC(priv, EPOCVideo); + driv->opaque = priv; + + /*alpha and keying to do*/ + driv->hw_caps = 0; +#ifdef GPAC_USE_OGL_ES + /*no offscreen opengl with epoc at the moment*/ + driv->hw_caps |= GF_VIDEO_HW_OPENGL; +#endif + + driv->Setup = EVID_Setup; + driv->Shutdown = EVID_Shutdown; + driv->Flush = EVID_Flush; + driv->ProcessEvent = EVID_ProcessEvent; + driv->Blit = NULL; + driv->LockBackBuffer = EVID_LockBackBuffer; + driv->SetFullScreen = EVID_SetFullScreen; + return (void *)driv; +} +static void EPOC_vout_del(void *ifce) +{ + GF_VideoOutput *driv = (GF_VideoOutput *) ifce; + EPOCVideo *priv = (EPOCVideo *)driv->opaque; + free(priv); + free(driv); +} + +#ifdef __cplusplus +extern "C" { +#endif + + +void *EPOC_aout_new(); +void EPOC_aout_del(void *ifce); + +void EPOC_codec_del(void *ifcg); +void *EPOC_codec_new(); + +/*interface query*/ +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return 1; + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) return 1; + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return 1; + return 0; +} +/*interface create*/ +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return (GF_BaseInterface *) EPOC_vout_new(); + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) return (GF_BaseInterface *) EPOC_aout_new(); + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return (GF_BaseInterface *) EPOC_codec_new(); + return NULL; +} +/*interface destroy*/ +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + GF_VideoOutput *dd = (GF_VideoOutput *)ifce; + switch (dd->InterfaceType) { + case GF_VIDEO_OUTPUT_INTERFACE: + EPOC_vout_del(dd); + break; + case GF_AUDIO_OUTPUT_INTERFACE: + EPOC_aout_del(ifce); + break; + case GF_MEDIA_DECODER_INTERFACE: + EPOC_codec_del(ifce); + break; + } +} + +#ifdef __cplusplus +} +#endif + diff --git a/modules/ffmpeg_in/Makefile b/modules/ffmpeg_in/Makefile new file mode 100644 index 0000000..fb67f0f --- /dev/null +++ b/modules/ffmpeg_in/Makefile @@ -0,0 +1,81 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/ffmpeg_in + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +LOCAL_LIB= +LINKLIBS=-lgpac -lavcodec -lavformat -lz + +#common obj +OBJS=ffmpeg_decode.o ffmpeg_demux.o ffmpeg_load.o + +#local ffmpeg lib +ifeq ($(CONFIG_FFMPEG), local) +LOCAL_LIB=-L../../extra_lib/lib/gcc +CFLAGS+= -I$(LOCAL_INC_PATH) +endif + +ifeq ($(CONFIG_ZLIB), local) +LOCAL_LIB=-L../../extra_lib/lib/gcc +endif + + +SRCS := $(OBJS:.o=.c) + +LIB=gm_ffmpeg_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols ffmpeg_in.def +endif + +#need to add cross-libraries if not local +ifeq ($(CROSS_COMPILING),yes) +EXTRALIBS+=-L$(prefix)/lib +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) -L../../bin/gcc $(LOCAL_LIB) $(LINKLIBS) $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/ffmpeg_in/ffmpeg_decode.c b/modules/ffmpeg_in/ffmpeg_decode.c new file mode 100644 index 0000000..cdd2af4 --- /dev/null +++ b/modules/ffmpeg_in/ffmpeg_decode.c @@ -0,0 +1,776 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / FFMPEG module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "ffmpeg_in.h" +#include <gpac/avparse.h> + +static AVCodec *ffmpeg_get_codec(u32 codec_4cc) +{ + char name[5]; + AVCodec *codec; + strcpy(name, gf_4cc_to_str(codec_4cc)); + + codec = avcodec_find_decoder_by_name(name); + if (!codec) { + strupr(name); + codec = avcodec_find_decoder_by_name(name); + if (!codec) { + strlwr(name); + codec = avcodec_find_decoder_by_name(name); + } + } + /*custom mapings*/ + if (!codec) { + if (!stricmp(name, "s263")) codec = avcodec_find_decoder_by_name("h263"); + else if (!stricmp(name, "samr") || !stricmp(name, "amr ")) codec = avcodec_find_decoder_by_name("amr_nb"); + else if (!stricmp(name, "sawb")) codec = avcodec_find_decoder_by_name("amr_wb"); + } + return codec; +} + + +static void FFDEC_LoadDSI(FFDec *ffd, GF_BitStream *bs, Bool from_ff_demux) +{ + u32 dsi_size; + if (!ffd->codec) return; + + dsi_size = (u32) gf_bs_available(bs); + if (!dsi_size) return; + + /*demuxer is ffmpeg, extra data can be copied directly*/ + if (from_ff_demux) { + free(ffd->ctx->extradata); + ffd->ctx->extradata_size = dsi_size; + ffd->ctx->extradata = (uint8_t*) malloc(sizeof(char)*ffd->ctx->extradata_size); + gf_bs_read_data(bs, ffd->ctx->extradata, ffd->ctx->extradata_size); + return; + } + + switch (ffd->codec->id) { + case CODEC_ID_SVQ3: + { + u32 at_type, size; + size = gf_bs_read_u32(bs); + /*there should be an 'SMI' entry*/ + at_type = gf_bs_read_u32(bs); + if (at_type == GF_4CC('S', 'M', 'I', ' ')) { + free(ffd->ctx->extradata); + ffd->ctx->extradata_size = 0x5a + size; + ffd->ctx->extradata = (uint8_t*) malloc(sizeof(char)*ffd->ctx->extradata_size); + strcpy(ffd->ctx->extradata, "SVQ3"); + gf_bs_read_data(bs, (unsigned char *)ffd->ctx->extradata + 0x5a, size); + } + } + break; + default: + free(ffd->ctx->extradata); + ffd->ctx->extradata_size = dsi_size; + ffd->ctx->extradata = (uint8_t*) malloc(sizeof(char)*ffd->ctx->extradata_size); + gf_bs_read_data(bs, ffd->ctx->extradata, ffd->ctx->extradata_size); + break; + } +} + +static GF_Err FFDEC_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) +{ + u32 codec_id; + int gotpic; + GF_BitStream *bs; + GF_M4VDecSpecInfo dsi; + GF_Err e; + FFDec *ffd = (FFDec *)plug->privateStack; + if (ffd->ES_ID || esd->dependsOnESID || esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; + if (!ffd->oti) return GF_NOT_SUPPORTED; + ffd->ES_ID = esd->ESID; + + ffd->ctx = avcodec_alloc_context(); + + /*private FFMPEG DSI*/ + if (ffd->oti == GPAC_OTI_MEDIA_FFMPEG) { + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + codec_id = gf_bs_read_u32(bs); + if (ffd->st==GF_STREAM_AUDIO) { + ffd->ctx->codec_type = CODEC_TYPE_AUDIO; + ffd->ctx->sample_rate = gf_bs_read_u32(bs); + ffd->ctx->channels = gf_bs_read_u16(bs); + ffd->ctx->frame_size = gf_bs_read_u16(bs); + /*bits_per_sample */gf_bs_read_u8(bs); + /*num_frames_per_au*/ gf_bs_read_u8(bs); + + /*ffmpeg specific*/ + ffd->ctx->block_align = gf_bs_read_u16(bs); + } else if (ffd->st==GF_STREAM_VISUAL) { + ffd->ctx->codec_type = CODEC_TYPE_VIDEO; + ffd->ctx->width = gf_bs_read_u16(bs); + ffd->ctx->height = gf_bs_read_u16(bs); + } + ffd->ctx->bit_rate = gf_bs_read_u32(bs); + + ffd->ctx->codec_tag = gf_bs_read_u32(bs); + + ffd->codec = avcodec_find_decoder(codec_id); + FFDEC_LoadDSI(ffd, bs, 1); + gf_bs_del(bs); + } + /*private QT DSI*/ + else if (ffd->oti == GPAC_OTI_MEDIA_GENERIC) { + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + codec_id = gf_bs_read_u32(bs); + if (ffd->st==GF_STREAM_AUDIO) { + ffd->ctx->codec_type = CODEC_TYPE_AUDIO; + ffd->ctx->sample_rate = gf_bs_read_u32(bs); + ffd->ctx->channels = gf_bs_read_u16(bs); + ffd->ctx->frame_size = gf_bs_read_u16(bs); + /*bits_per_sample */ gf_bs_read_u8(bs); + /*num_frames_per_au*/ gf_bs_read_u8(bs); + /*just in case...*/ + if (codec_id == GF_4CC('a', 'm', 'r', ' ')) { + ffd->ctx->sample_rate = 8000; + ffd->ctx->channels = 1; + ffd->ctx->frame_size = 160; + } + } else if (ffd->st==GF_STREAM_VISUAL) { + ffd->ctx->codec_type = CODEC_TYPE_VIDEO; + ffd->ctx->width = gf_bs_read_u16(bs); + ffd->ctx->height = gf_bs_read_u16(bs); + } + ffd->codec = ffmpeg_get_codec(codec_id); + FFDEC_LoadDSI(ffd, bs, 0); + gf_bs_del(bs); + } + /*use std MPEG-4 st/oti*/ + else { + u32 codec_id = 0; + if (ffd->st==GF_STREAM_VISUAL) { + ffd->ctx->codec_type = CODEC_TYPE_VIDEO; + switch (ffd->oti) { + case 0x20: + codec_id = CODEC_ID_MPEG4; + break; + case 0x21: + codec_id = CODEC_ID_H264; + /*ffmpeg H264/AVC needs that*/ + //ffd->ctx->codec_tag = 0x31637661; + break; + case 0x6A: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + codec_id = CODEC_ID_MPEG2VIDEO; + break; + case 0x6C: + codec_id = CODEC_ID_MJPEG; + break; + case 0xFF: + codec_id = CODEC_ID_SVQ3; + break; + } + } else if (ffd->st==GF_STREAM_AUDIO) { + ffd->ctx->codec_type = CODEC_TYPE_AUDIO; + switch (ffd->oti) { + case 0x69: + case 0x6B: + ffd->ctx->frame_size = 1152; + codec_id = CODEC_ID_MP2; + break; + } + } + else if ((ffd->st==GF_STREAM_ND_SUBPIC) && (ffd->oti==0xe0)) { + codec_id = CODEC_ID_DVD_SUBTITLE; + } + ffd->codec = avcodec_find_decoder(codec_id); + } + /*should never happen*/ + if (!ffd->codec) return GF_OUT_OF_MEM; + + /*setup MPEG-4 video streams*/ + if ((ffd->st==GF_STREAM_VISUAL)) { + /*for all MPEG-4 variants get size*/ + if ((ffd->oti==0x20) || (ffd->oti == 0x21)) { + /*if not set this may be a remap of non-mpeg4 transport (eg, transport on MPEG-TS) where + the DSI is carried in-band*/ + if (esd->decoderConfig->decoderSpecificInfo->data) { + + /*for regular MPEG-4, try to decode and if this fails try H263 decoder at first frame*/ + if (ffd->oti==0x20) { + e = gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + if (e) return e; + ffd->ctx->width = dsi.width; + ffd->ctx->height = dsi.height; + if (!dsi.width && !dsi.height) ffd->check_short_header = 1; + ffd->previous_par = (dsi.par_num<<16) | dsi.par_den; + ffd->no_par_update = 1; + } else if (ffd->oti==0x21) { + ffd->check_h264_isma = 1; + } + + /*setup dsi for FFMPEG context BEFORE attaching decoder (otherwise not proper init)*/ + ffd->ctx->extradata = malloc(sizeof(char)*esd->decoderConfig->decoderSpecificInfo->dataLength); + memcpy(ffd->ctx->extradata, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); + ffd->ctx->extradata_size = esd->decoderConfig->decoderSpecificInfo->dataLength; + } + } + ffd->frame = avcodec_alloc_frame(); + } + + if (avcodec_open(ffd->ctx, ffd->codec)<0) return GF_NON_COMPLIANT_BITSTREAM; + + /*setup audio streams*/ + if (ffd->st==GF_STREAM_AUDIO) { + if ((ffd->codec->type == CODEC_ID_MP3LAME) || (ffd->codec->type == CODEC_ID_MP2)) { + ffd->ctx->frame_size = (ffd->ctx->sample_rate > 24000) ? 1152 : 576; + } + /*may be 0 (cfg not known yet)*/ + ffd->out_size = ffd->ctx->channels * ffd->ctx->frame_size * 2 /*16 / 8*/; + if (!ffd->ctx->sample_rate) ffd->ctx->sample_rate = 44100; + if (!ffd->ctx->channels) ffd->ctx->channels = 2; + } else { + switch (ffd->codec->id) { + case CODEC_ID_MJPEG: + case CODEC_ID_MJPEGB: + case CODEC_ID_LJPEG: +#if (LIBAVCODEC_VERSION_INT > ((51<<16)+(20<<8)+0) ) + case CODEC_ID_GIF: +#endif + ffd->pix_fmt = GF_PIXEL_RGB_24; + break; + case CODEC_ID_DVD_SUBTITLE: + ffd->frame = avcodec_alloc_frame(); + avcodec_decode_video(ffd->ctx, ffd->frame, &gotpic, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); + ffd->pix_fmt = GF_PIXEL_YV12; + break; + default: + ffd->pix_fmt = GF_PIXEL_YV12; + break; + } + ffd->out_size = ffd->ctx->width * ffd->ctx->height * 3; + + ffd->out_pix_fmt = ffd->pix_fmt; +#if defined(_WIN32_WCE) || defined(__SYMBIAN32__) +#else +// if (ffd->pix_fmt == GF_PIXEL_YV12) ffd->out_pix_fmt = GF_PIXEL_RGB_24; +#endif + + if (ffd->out_pix_fmt != GF_PIXEL_RGB_24) ffd->out_size /= 2; + } + return GF_OK; +} +static GF_Err FFDEC_DetachStream(GF_BaseDecoder *plug, u16 ES_ID) +{ + FFDec *ffd = (FFDec *)plug->privateStack; + if (!ffd->ES_ID) return GF_BAD_PARAM; + ffd->ES_ID = 0; + + if (ffd->ctx) { + if (ffd->ctx->extradata) free(ffd->ctx->extradata); + avcodec_close(ffd->ctx); + ffd->ctx = NULL; + } +#ifdef FFMPEG_SWSCALE + if (ffd->sws_ctx) { sws_freeContext(ffd->sws_ctx); ffd->sws_ctx = NULL; } +#endif + return GF_OK; +} + +static GF_Err FFDEC_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *capability) +{ + FFDec *ffd = (FFDec *)plug->privateStack; + + /*base caps*/ + switch (capability->CapCode) { + /*ffmpeg seems quite reliable*/ + case GF_CODEC_RESILIENT: + capability->cap.valueInt = 1; + return GF_OK; + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = FF_INPUT_BUFFER_PADDING_SIZE; + return GF_OK; + case GF_CODEC_REORDER: + capability->cap.valueInt = 1; + return GF_OK; + } + + if (!ffd->ctx) { + capability->cap.valueInt = 0; + return GF_OK; + } + + /*caps valid only if stream attached*/ + switch (capability->CapCode) { + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ffd->out_size; + break; + case GF_CODEC_SAMPLERATE: + capability->cap.valueInt = ffd->ctx->sample_rate; + break; + case GF_CODEC_NB_CHAN: + capability->cap.valueInt = ffd->ctx->channels; + break; + case GF_CODEC_BITS_PER_SAMPLE: + capability->cap.valueInt = 16; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = (ffd->st==GF_STREAM_AUDIO) ? 4 : 1; + break; + case GF_CODEC_BUFFER_MAX: + /*for audio let the systems engine decide since we may have very large block size (1 sec with some QT movies)*/ + capability->cap.valueInt = (ffd->st==GF_STREAM_AUDIO) ? 0 : 4; + break; + /*by default AAC access unit lasts num_samples (timescale being sampleRate)*/ + case GF_CODEC_CU_DURATION: + capability->cap.valueInt = (ffd->st==GF_STREAM_AUDIO) ? ffd->ctx->frame_size : 0; + break; + case GF_CODEC_WIDTH: + capability->cap.valueInt = ffd->ctx->width; + break; + case GF_CODEC_HEIGHT: + capability->cap.valueInt = ffd->ctx->height; + break; + case GF_CODEC_STRIDE: + capability->cap.valueInt = ffd->ctx->width; + if (ffd->out_pix_fmt==GF_PIXEL_RGB_24) capability->cap.valueInt *= 3; + break; + case GF_CODEC_FPS: + capability->cap.valueFloat = 30.0f; + break; + case GF_CODEC_PAR: + capability->cap.valueInt = ffd->previous_par; + break; + case GF_CODEC_PIXEL_FORMAT: + if (ffd->ctx->width) capability->cap.valueInt = ffd->out_pix_fmt; + break; + /*ffmpeg performs frame reordering internally*/ + case GF_CODEC_REORDER: + capability->cap.valueInt = 1; + break; + case GF_CODEC_WAIT_RAP: + //ffd->ctx->hurry_up = 5; + break; + case GF_CODEC_CHANNEL_CONFIG: + /*currently unused in ffmpeg*/ + if (ffd->ctx->channels==1) { + capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER; + } else { + capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT; + } + break; + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} + +static GF_Err FFDEC_SetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability capability) +{ + FFDec *ffd = (FFDec *)plug->privateStack; + switch (capability.CapCode) { + case GF_CODEC_WAIT_RAP: + ffd->frame_start = 0; + if (ffd->st==GF_STREAM_VISUAL) avcodec_flush_buffers(ffd->ctx); + return GF_OK; + default: + /*return unsupported to avoid confusion by the player (like color space changing ...) */ + return GF_NOT_SUPPORTED; + } +} + +static GF_Err FFDEC_ProcessData(GF_MediaDecoder *plug, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + + s32 gotpic; + u32 outsize; + FFDec *ffd = plug->privateStack; + + /*WARNING: this breaks H264 (and maybe others) decoding, disabled for now*/ +#if 1 + if (!ffd->ctx->hurry_up) { + switch (mmlevel) { + case GF_CODEC_LEVEL_SEEK: + case GF_CODEC_LEVEL_DROP: + /*skip as much as possible*/ + ffd->ctx->hurry_up = 5; + break; + case GF_CODEC_LEVEL_VERY_LATE: + case GF_CODEC_LEVEL_LATE: + /*skip B-frames*/ + ffd->ctx->hurry_up = 1; + break; + default: + ffd->ctx->hurry_up = 0; + break; + } + } +#endif + + /*audio stream*/ + if (ffd->st==GF_STREAM_AUDIO) { + s32 len; + u32 buf_size = (*outBufferLength); + (*outBufferLength) = 0; + + /*seeking don't decode*/ + if (!inBuffer || (mmlevel == GF_CODEC_LEVEL_SEEK)) { + *outBufferLength = 0; + ffd->frame_start = 0; + return GF_OK; + } + if (ffd->frame_start>inBufferLength) ffd->frame_start = 0; + +redecode: + len = avcodec_decode_audio(ffd->ctx, (short *)ffd->audio_buf, &gotpic, inBuffer + ffd->frame_start, inBufferLength - ffd->frame_start); + + if (len<0) { ffd->frame_start = 0; return GF_NON_COMPLIANT_BITSTREAM; } + if (gotpic<0) { ffd->frame_start = 0; return GF_OK; } + + ffd->ctx->hurry_up = 0; + + if (ffd->ctx->frame_size < gotpic) ffd->ctx->frame_size = gotpic; + + /*first config*/ + if (!ffd->out_size) { + ffd->out_size = ffd->ctx->channels * ffd->ctx->frame_size * 2 /* 16 / 8 */; + } + if (ffd->out_size < (u32) gotpic) { + /*looks like relying on frame_size is not a good idea for all codecs, so we use gotpic*/ + (*outBufferLength) = ffd->out_size = gotpic; + return GF_BUFFER_TOO_SMALL; + } + if (ffd->out_size > buf_size) { + /*don't use too small output chunks otherwise we'll never have enough when mixing - we could + also request more slots in the composition memory but let's not waste mem*/ + if (ffd->out_size < (u32) 576*ffd->ctx->channels) ffd->out_size=ffd->ctx->channels*576; + (*outBufferLength) = ffd->out_size; + return GF_BUFFER_TOO_SMALL; + } + + /*we're sure to have at least gotpic bytes available in output*/ + memcpy(outBuffer, ffd->audio_buf, sizeof(char) * gotpic); + (*outBufferLength) += gotpic; + outBuffer += gotpic; + + ffd->frame_start += len; + if (inBufferLength <= ffd->frame_start) { + ffd->frame_start = 0; + return GF_OK; + } + /*still space go on*/ + if ((*outBufferLength)+ffd->ctx->frame_size<ffd->out_size) goto redecode; + + /*more frames in the current sample*/ + return GF_PACKED_FRAMES; + } else { + s32 w = ffd->ctx->width; + s32 h = ffd->ctx->height; + + if (ffd->check_h264_isma) { + /*for AVC bitstreams after ISMA decryption, in case (as we do) the decryption DRM tool + doesn't put back nalu size, do it ourselves...*/ + if (inBuffer && !inBuffer[0] && !inBuffer[1] && !inBuffer[2] && (inBuffer[3]==0x01)) { + u32 nalu_size; + u32 remain = inBufferLength; + char *start, *end; + + start = inBuffer; + end = inBuffer + 4; + while (remain>4) { + if (!end[0] && !end[1] && !end[2] && (end[3]==0x01)) { + nalu_size = end - start - 4; + start[0] = (nalu_size>>24)&0xFF; + start[1] = (nalu_size>>16)&0xFF; + start[2] = (nalu_size>>8)&0xFF; + start[3] = (nalu_size)&0xFF; + start = end; + end = start+4; + continue; + } + end++; + remain--; + } + nalu_size = (inBuffer+inBufferLength) - start - 4; + start[0] = (nalu_size>>24)&0xFF; + start[1] = (nalu_size>>16)&0xFF; + start[2] = (nalu_size>>8)&0xFF; + start[3] = (nalu_size)&0xFF; + ffd->check_h264_isma = 2; + } + /*if we had ISMA E&A and lost it this is likely due to a pck loss - do NOT switch back to regular*/ + else if (ffd->check_h264_isma == 1) { + ffd->check_h264_isma = 0; + } + } + + if (avcodec_decode_video(ffd->ctx, ffd->frame, &gotpic, inBuffer, inBufferLength) < 0) { + if (!ffd->check_short_header) { + return GF_NON_COMPLIANT_BITSTREAM; + } + + /*switch to H263 (ffmpeg MPEG-4 codec doesn't understand short headers)*/ + { + u32 old_codec = ffd->codec->id; + ffd->check_short_header = 0; + /*OK we loose the DSI stored in the codec context, but H263 doesn't need any, and if we're + here this means the DSI was broken, so no big deal*/ + avcodec_close(ffd->ctx); + ffd->codec = avcodec_find_decoder(CODEC_ID_H263); + if (!ffd->codec || (avcodec_open(ffd->ctx, ffd->codec)<0)) return GF_NON_COMPLIANT_BITSTREAM; + if (avcodec_decode_video(ffd->ctx, ffd->frame, &gotpic, inBuffer, inBufferLength) < 0) { + /*nope, stay in MPEG-4*/ + avcodec_close(ffd->ctx); + ffd->codec = avcodec_find_decoder(old_codec); + assert(ffd->codec); + avcodec_open(ffd->ctx, ffd->codec); + return GF_NON_COMPLIANT_BITSTREAM; + } + } + } + ffd->ctx->hurry_up = 0; + /*recompute outsize in case on-the-fly change*/ + if ((w != ffd->ctx->width) || (h != ffd->ctx->height)) { + outsize = ffd->ctx->width * ffd->ctx->height * 3; + if (ffd->out_pix_fmt != GF_PIXEL_RGB_24) outsize /= 2; + ffd->out_size = outsize; + *outBufferLength = ffd->out_size; + if (ffd->check_h264_isma) { + inBuffer[0] = inBuffer[1] = inBuffer[2] = 0; + inBuffer[3] = 1; + } +#ifdef FFMPEG_SWSCALE + if (ffd->sws_ctx) { sws_freeContext(ffd->sws_ctx); ffd->sws_ctx = NULL; } +#endif + return GF_BUFFER_TOO_SMALL; + } + /*check PAR in case on-the-fly change*/ + if (!ffd->no_par_update && ffd->ctx->sample_aspect_ratio.num && ffd->ctx->sample_aspect_ratio.den) { + u32 new_par = (ffd->ctx->sample_aspect_ratio.num<<16) | ffd->ctx->sample_aspect_ratio.den; + if (new_par != ffd->previous_par) { + ffd->previous_par = new_par; + *outBufferLength = ffd->out_size; + return GF_BUFFER_TOO_SMALL; + } + } + + *outBufferLength = 0; + if (mmlevel == GF_CODEC_LEVEL_SEEK) return GF_OK; + + if (gotpic) { +#if defined(_WIN32_WCE) || defined(__SYMBIAN32__) + if (ffd->pix_fmt==GF_PIXEL_RGB_24) { + memcpy(outBuffer, ffd->frame->data[0], sizeof(char)*3*ffd->ctx->width); + } else { + s32 i; + char *pYO, *pUO, *pVO; + unsigned char *pYD, *pUD, *pVD; + pYO = ffd->frame->data[0]; + pUO = ffd->frame->data[1]; + pVO = ffd->frame->data[2]; + pYD = outBuffer; + pUD = outBuffer + ffd->ctx->width * ffd->ctx->height; + pVD = outBuffer + 5 * ffd->ctx->width * ffd->ctx->height / 4; + + + for (i=0; i<ffd->ctx->height; i++) { + memcpy(pYD, pYO, sizeof(char) * ffd->ctx->width); + pYD += ffd->ctx->width; + pYO += ffd->frame->linesize[0]; + if (i%2) continue; + + memcpy(pUD, pUO, sizeof(char) * ffd->ctx->width/2); + memcpy(pVD, pVO, sizeof(char) * ffd->ctx->width/2); + pUD += ffd->ctx->width/2; + pVD += ffd->ctx->width/2; + pUO += ffd->frame->linesize[1]; + pVO += ffd->frame->linesize[2]; + } + *outBufferLength = ffd->out_size; + } +#else + AVPicture pict; + u32 pix_out; + memset(&pict, 0, sizeof(pict)); + if (ffd->out_pix_fmt==GF_PIXEL_RGB_24) { + pict.data[0] = outBuffer; + pict.linesize[0] = 3*ffd->ctx->width; + pix_out = PIX_FMT_RGB24; + } else { + pict.data[0] = outBuffer; + pict.data[1] = outBuffer + ffd->ctx->width * ffd->ctx->height; + pict.data[2] = outBuffer + 5 * ffd->ctx->width * ffd->ctx->height / 4; + pict.linesize[0] = ffd->ctx->width; + pict.linesize[1] = pict.linesize[2] = ffd->ctx->width/2; + pix_out = PIX_FMT_YUV420P; + if (!mmlevel && ffd->frame->interlaced_frame) { + avpicture_deinterlace((AVPicture *) ffd->frame, (AVPicture *) ffd->frame, ffd->ctx->pix_fmt, ffd->ctx->width, ffd->ctx->height); + } + + } + pict.data[3] = 0; + pict.linesize[3] = 0; +#ifndef FFMPEG_SWSCALE + img_convert(&pict, pix_out, (AVPicture *) ffd->frame, ffd->ctx->pix_fmt, ffd->ctx->width, ffd->ctx->height); +#else + if (!ffd->sws_ctx) + ffd->sws_ctx = sws_getContext(ffd->ctx->width, ffd->ctx->height, + ffd->ctx->pix_fmt, ffd->ctx->width, ffd->ctx->height, pix_out, SWS_BICUBIC, + NULL, NULL, NULL); + + if (ffd->sws_ctx) + sws_scale(ffd->sws_ctx, ffd->frame->data, ffd->frame->linesize, 0, ffd->ctx->height->codec->height, pict.data, pict.linesize); + +#endif + + *outBufferLength = ffd->out_size; +#endif + } + } + return GF_OK; +} + +static Bool FFDEC_CanHandleStream(GF_BaseDecoder *plug, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + GF_BitStream *bs; + u32 codec_id; + Bool check_4cc; + FFDec *ffd = plug->privateStack; + + if (!ObjectType) { + if ((StreamType==GF_STREAM_VISUAL) || (StreamType==GF_STREAM_AUDIO)) return 1; + return 0; + } + + /*store types*/ + ffd->oti = ObjectType; + ffd->st = StreamType; + + codec_id = 0; + check_4cc = 0; + + /*private from FFMPEG input*/ + if (ObjectType == GPAC_OTI_MEDIA_FFMPEG) { + bs = gf_bs_new(decSpecInfo, decSpecInfoSize, GF_BITSTREAM_READ); + codec_id = gf_bs_read_u32(bs); + gf_bs_del(bs); + } + /*private from IsoMedia input*/ + else if (ObjectType == GPAC_OTI_MEDIA_GENERIC) { + bs = gf_bs_new(decSpecInfo, decSpecInfoSize, GF_BITSTREAM_READ); + codec_id = gf_bs_read_u32(bs); + check_4cc = 1; + gf_bs_del(bs); + } + else if (StreamType==GF_STREAM_AUDIO) { + /*std MPEG-2 audio*/ + if ((ObjectType==0x69) || (ObjectType==0x6B)) codec_id = CODEC_ID_MP2; + /*std AC3 audio*/ + //if (ObjectType==0xA5) codec_id = CODEC_ID_AC3; + } + + /*std MPEG-4 visual*/ + else if (StreamType==GF_STREAM_VISUAL) { + switch (ObjectType) { + /*MPEG-4 v1 simple profile*/ + case 0x20: codec_id = CODEC_ID_MPEG4; break; + /*H264 (not std OTI, just the way we use it internally)*/ + case 0x21: codec_id = CODEC_ID_H264; break; + /*MPEG1 video*/ + case 0x6A: + /*MPEG2 video*/ + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + codec_id = CODEC_ID_MPEG2VIDEO; break; + /*JPEG*/ + case 0x6C: + return 0; /*I'm having troubles with ffmpeg & jpeg, it appears to crash randomly*/ + return 1; + default: + return 0; + } + } + /*NeroDigital DVD subtitles*/ + else if ((StreamType==GF_STREAM_ND_SUBPIC) && (ObjectType==0xe0)) + return 1; + + if (!codec_id) return 0; + if (check_4cc && (ffmpeg_get_codec(codec_id) != NULL)) return 1; + if (avcodec_find_decoder(codec_id) != NULL) return 1; + return 0; +} + +static char szCodec[100]; +static const char *FFDEC_GetCodecName(GF_BaseDecoder *dec) +{ + FFDec *ffd = dec->privateStack; + if (ffd->codec) { + sprintf(szCodec, "FFMPEG %s", ffd->codec->name ? ffd->codec->name : "unknown"); + return szCodec; + } + return NULL; +} + + +void *FFDEC_Load() +{ + GF_MediaDecoder *ptr; + FFDec *priv; + + avcodec_init(); + avcodec_register_all(); + + GF_SAFEALLOC(ptr , GF_MediaDecoder); + GF_SAFEALLOC(priv , FFDec); + ptr->privateStack = priv; + + ptr->AttachStream = FFDEC_AttachStream; + ptr->DetachStream = FFDEC_DetachStream; + ptr->GetCapabilities = FFDEC_GetCapabilities; + ptr->SetCapabilities = FFDEC_SetCapabilities; + ptr->CanHandleStream = FFDEC_CanHandleStream; + ptr->GetName = FFDEC_GetCodecName; + ptr->ProcessData = FFDEC_ProcessData; + + GF_REGISTER_MODULE_INTERFACE(ptr, GF_MEDIA_DECODER_INTERFACE, "FFMPEG decoder", "gpac distribution"); + return (GF_BaseInterface *) ptr; +} + +void FFDEC_Delete(void *ifce) +{ + GF_BaseDecoder *dec = ifce; + FFDec *ffd = dec->privateStack; + + if (ffd->ctx) avcodec_close(ffd->ctx); + free(ffd); + free(dec); + +} diff --git a/modules/ffmpeg_in/ffmpeg_demux.c b/modules/ffmpeg_in/ffmpeg_demux.c new file mode 100644 index 0000000..fcc9c63 --- /dev/null +++ b/modules/ffmpeg_in/ffmpeg_demux.c @@ -0,0 +1,869 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / FFMPEG module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "ffmpeg_in.h" + +/*default buffer is 200 ms per channel*/ +#define FFD_DATA_BUFFER 800 + +//#if defined(__DARWIN__) || defined(__APPLE__) +#if !defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) +#include <errno.h> +#endif + +static u32 FFDemux_Run(void *par) +{ + AVPacket pkt; + s64 seek_to; + u64 seek_audio, seek_video; + Bool video_init, do_seek, map_audio_time, map_video_time; + GF_NetworkCommand com; + GF_NetworkCommand map; + GF_SLHeader slh; + FFDemux *ffd = (FFDemux *) par; + + memset(&map, 0, sizeof(GF_NetworkCommand)); + map.command_type = GF_NET_CHAN_MAP_TIME; + + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.command_type = GF_NET_CHAN_BUFFER_QUERY; + + memset(&slh, 0, sizeof(GF_SLHeader)); + + slh.compositionTimeStampFlag = slh.decodingTimeStampFlag = 1; + seek_to = (s64) (AV_TIME_BASE*ffd->seek_time); + map_video_time = !ffd->seekable; + + video_init = (seek_to && ffd->video_ch) ? 0 : 1; + seek_audio = seek_video = 0; + if (ffd->seekable && (ffd->audio_st>=0)) seek_audio = (u64) (s64) (ffd->seek_time*ffd->audio_tscale.den); + if (ffd->seekable && (ffd->video_st>=0)) seek_video = (u64) (s64) (ffd->seek_time*ffd->video_tscale.den); + + /*it appears that ffmpeg has trouble resyncing on some mpeg files - we trick it by restarting to 0 to get the + first video frame, and only then seek*/ + if (ffd->seekable) av_seek_frame(ffd->ctx, -1, video_init ? seek_to : 0, AVSEEK_FLAG_BACKWARD); + do_seek = !video_init; + map_audio_time = video_init ? ffd->unreliable_audio_timing : 0; + + while (ffd->is_running) { + + pkt.stream_index = -1; + /*EOF*/ + if (av_read_frame(ffd->ctx, &pkt) <0) break; + if (pkt.pts == AV_NOPTS_VALUE) pkt.pts = pkt.dts; + if (!pkt.dts) pkt.dts = pkt.pts; + + slh.compositionTimeStamp = pkt.pts; + slh.decodingTimeStamp = pkt.dts; + + gf_mx_p(ffd->mx); + /*blindly send audio as soon as video is init*/ + if (ffd->audio_ch && (pkt.stream_index == ffd->audio_st) && !do_seek) { + slh.compositionTimeStamp *= ffd->audio_tscale.num; + slh.decodingTimeStamp *= ffd->audio_tscale.num; + + if (map_audio_time) { + map.base.on_channel = ffd->audio_ch; + map.map_time.media_time = ffd->seek_time; + /*mapwith TS=0 since we don't use SL*/ + map.map_time.timestamp = 0; + map.map_time.reset_buffers = 1; + map_audio_time = 0; + gf_term_on_command(ffd->service, &map, GF_OK); + } + else if (slh.compositionTimeStamp < seek_audio) { + slh.decodingTimeStamp = slh.compositionTimeStamp = seek_audio; + } + gf_term_on_sl_packet(ffd->service, ffd->audio_ch, pkt.data, pkt.size, &slh, GF_OK); + } + else if (ffd->video_ch && (pkt.stream_index == ffd->video_st)) { + slh.compositionTimeStamp *= ffd->video_tscale.num; + slh.decodingTimeStamp *= ffd->video_tscale.num; + + /*if we get pts = 0 after a seek the demuxer is reseting PTSs, so force map time*/ + if ((!do_seek && seek_to && !slh.compositionTimeStamp) || (map_video_time) ) { + seek_to = 0; + map_video_time = 0; + + map.base.on_channel = ffd->video_ch; + map.map_time.timestamp = (u64) pkt.pts; +// map.map_time.media_time = ffd->seek_time; + map.map_time.media_time = 0; + map.map_time.reset_buffers = 0; + gf_term_on_command(ffd->service, &map, GF_OK); + } + else if (slh.compositionTimeStamp < seek_video) { + slh.decodingTimeStamp = slh.compositionTimeStamp = seek_video; + } + gf_term_on_sl_packet(ffd->service, ffd->video_ch, pkt.data, pkt.size, &slh, GF_OK); + video_init = 1; + } + gf_mx_v(ffd->mx); + av_free_packet(&pkt); + + /*here's the trick - only seek after sending the first packets of each stream - this allows ffmpeg video decoders + to resync properly*/ + if (do_seek && video_init && ffd->seekable) { + av_seek_frame(ffd->ctx, -1, seek_to, AVSEEK_FLAG_BACKWARD); + do_seek = 0; + map_audio_time = ffd->unreliable_audio_timing; + } + /*sleep untill the buffer occupancy is too low - note that this work because all streams in this + demuxer are synchronized*/ + while (1) { + if (ffd->audio_ch) { + com.base.on_channel = ffd->audio_ch; + gf_term_on_command(ffd->service, &com, GF_OK); + if (com.buffer.occupancy < ffd->data_buffer_ms) break; + } + if (ffd->video_ch) { + com.base.on_channel = ffd->video_ch; + gf_term_on_command(ffd->service, &com, GF_OK); + if (com.buffer.occupancy < ffd->data_buffer_ms) break; + } + gf_sleep(10); + + /*escape if disconnect*/ + if (!ffd->audio_run && !ffd->video_run) break; + } + if (!ffd->audio_run && !ffd->video_run) break; + } + /*signal EOS*/ + if (ffd->audio_ch) gf_term_on_sl_packet(ffd->service, ffd->audio_ch, NULL, 0, NULL, GF_EOS); + if (ffd->video_ch) gf_term_on_sl_packet(ffd->service, ffd->video_ch, NULL, 0, NULL, GF_EOS); + ffd->is_running = 2; + + return 0; +} + +static Bool FFD_CanHandleURL(GF_InputService *plug, const char *url) +{ + Bool has_audio, has_video; + s32 i; + AVFormatContext *ctx; + AVOutputFormat *fmt_out; + Bool ret = 0; + char *ext, szName[1000], szExt[20]; + const char *szExtList; + + strcpy(szName, url); + ext = strrchr(szName, '#'); + if (ext) ext[0] = 0; + + /*disable RTP/RTSP from ffmpeg*/ + if (!strnicmp(szName, "rtsp://", 7)) return 0; + if (!strnicmp(szName, "rtspu://", 8)) return 0; + if (!strnicmp(szName, "rtp://", 6)) return 0; + if (!strnicmp(szName, "plato://", 8)) return 0; + + ext = strrchr(szName, '.'); + if (ext) { + strcpy(szExt, &ext[1]); + strlwr(szExt); + if (!strcmp(szExt, "ts")) return 0; + + + /*note we forbid ffmpeg to handle files we support*/ + if (!strcmp(szExt, "mp4") || !strcmp(szExt, "mpg4") || !strcmp(szExt, "m4a") || !strcmp(szExt, "m21") + || !strcmp(szExt, "m4v") || !strcmp(szExt, "m4a") + || !strcmp(szExt, "3gp") || !strcmp(szExt, "3gpp") || !strcmp(szExt, "3gp2") || !strcmp(szExt, "3g2") + || !strcmp(szExt, "mp3") + || !strcmp(szExt, "ac3") + || !strcmp(szExt, "amr") + || !strcmp(szExt, "bt") || !strcmp(szExt, "wrl") || !strcmp(szExt, "x3dv") + || !strcmp(szExt, "xmt") || !strcmp(szExt, "xmta") || !strcmp(szExt, "x3d") + ) return 0; + + /*check any default stuff that should work with ffmpeg*/ + if (gf_term_check_extension(plug, "video/mpeg", "mpg mpeg mp2 mpa mpe mpv2", "MPEG 1/2 Movies", ext)) return 1; + if (gf_term_check_extension(plug, "video/x-mpeg", "mpg mpeg mp2 mpa mpe mpv2", "MPEG 1/2 Movies", ext)) return 1; + if (gf_term_check_extension(plug, "video/x-mpeg-systems", "mpg mpeg mp2 mpa mpe mpv2", "MPEG 1/2 Movies", ext)) return 1; + if (gf_term_check_extension(plug, "audio/basic", "snd au", "Basic Audio", ext)) return 1; + if (gf_term_check_extension(plug, "audio/x-wav", "wav", "WAV Audio", ext)) return 1; + if (gf_term_check_extension(plug, "audio/vnd.wave", "wav", "WAV Audio", ext)) return 1; + if (gf_term_check_extension(plug, "video/x-ms-asf", "asf wma wmv asx asr", "WindowsMedia Movies", ext)) return 1; + if (gf_term_check_extension(plug, "video/x-ms-wmv", "asf wma wmv asx asr", "WindowsMedia Movies", ext)) return 1; + if (gf_term_check_extension(plug, "video/avi", "avi", "AVI Movies", ext)) return 1; + if (gf_term_check_extension(plug, "video/vnd.avi", "avi", "AVI Movies", ext)) return 1; + + if (gf_term_check_extension(plug, "video/H263", "h263 263", "H263 Video", ext)) return 1; + if (gf_term_check_extension(plug, "video/H264", "h264 264", "H264 Video", ext)) return 1; + if (gf_term_check_extension(plug, "video/MPEG4", "cmp", "MPEG-4 Video", ext)) return 1; + /*we let ffmpeg handle mov because some QT files with uncompressed or adpcm audio use 1 audio sample + per MP4 sample which is a killer for our MP4 lib, whereas ffmpeg handles these as complete audio chunks + moreover ffmpeg handles cmov, we don't*/ + if (gf_term_check_extension(plug, "video/quicktime", "mov qt", "QuickTime Movies", ext)) return 1; + } + + ctx = NULL; + if (av_open_input_file(&ctx, szName, NULL, 0, NULL)<0) { + AVInputFormat *av_in = NULL;; + /*some extensions not supported by ffmpeg*/ + if (ext && !strcmp(szExt, "cmp")) av_in = av_find_input_format("m4v"); + + if (av_open_input_file(&ctx, szName, av_in, 0, NULL)<0) { + return 0; + } + } + + if (!ctx || av_find_stream_info(ctx) <0) goto exit; + /*figure out if we can use codecs or not*/ + has_video = has_audio = 0; + for(i = 0; i < ctx->nb_streams; i++) { + AVCodecContext *enc = ctx->streams[i]->codec; + switch(enc->codec_type) { + case CODEC_TYPE_AUDIO: + if (!has_audio) has_audio = 1; + break; + case CODEC_TYPE_VIDEO: + if (!has_video) has_video= 1; + break; + default: + break; + } + } + if (!has_audio && !has_video) goto exit; + ret = 1; + + fmt_out = guess_stream_format(NULL, url, NULL); + if (fmt_out) gf_term_register_mime_type(plug, fmt_out->mime_type, fmt_out->extensions, fmt_out->name); + else { + ext = strrchr(szName, '.'); + if (ext) { + strcpy(szExt, &ext[1]); + strlwr(szExt); + + szExtList = gf_modules_get_option((GF_BaseInterface *)plug, "MimeTypes", "application/x-ffmpeg"); + if (!szExtList) { + gf_term_register_mime_type(plug, "application/x-ffmpeg", szExt, "Other Movies (FFMPEG)"); + } else if (!strstr(szExtList, szExt)) { + u32 len; + char *buf; + len = strlen(szExtList) + strlen(szExt) + 10; + buf = malloc(sizeof(char)*len); + sprintf(buf, "\"%s ", szExt); + strcat(buf, &szExtList[1]); + gf_modules_set_option((GF_BaseInterface *)plug, "MimeTypes", "application/x-ffmpeg", buf); + free(buf); + } + } + } + +exit: + if (ctx) av_close_input_file(ctx); + return ret; +} + +static GF_ESD *FFD_GetESDescriptor(FFDemux *ffd, Bool for_audio) +{ + GF_BitStream *bs; + Bool dont_use_sl; + GF_ESD *esd = (GF_ESD *) gf_odf_desc_esd_new(0); + esd->ESID = 1 + (for_audio ? ffd->audio_st : ffd->video_st); + esd->decoderConfig->streamType = for_audio ? GF_STREAM_AUDIO : GF_STREAM_VISUAL; + esd->decoderConfig->avgBitrate = esd->decoderConfig->maxBitrate = 0; + + /*remap std object types - depending on input formats, FFMPEG may not have separate DSI from initial frame. + In this case we have no choice but using FFMPEG decoders*/ + if (for_audio) { + AVCodecContext *dec = ffd->ctx->streams[ffd->audio_st]->codec; + esd->slConfig->timestampResolution = ffd->audio_tscale.den; + switch (dec->codec_id) { + case CODEC_ID_MP2: + esd->decoderConfig->objectTypeIndication = 0x6B; + break; + case CODEC_ID_MP3: + esd->decoderConfig->objectTypeIndication = 0x69; + break; + case CODEC_ID_AAC: + if (!dec->extradata_size) goto opaque_audio; + esd->decoderConfig->objectTypeIndication = 0x40; + esd->decoderConfig->decoderSpecificInfo->dataLength = dec->extradata_size; + esd->decoderConfig->decoderSpecificInfo->data = malloc(sizeof(char)*dec->extradata_size); + memcpy(esd->decoderConfig->decoderSpecificInfo->data, + dec->extradata, + sizeof(char)*dec->extradata_size); + break; + default: +opaque_audio: + esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_FFMPEG; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, dec->codec_id); + gf_bs_write_u32(bs, dec->sample_rate); + gf_bs_write_u16(bs, dec->channels); + gf_bs_write_u16(bs, dec->frame_size); + gf_bs_write_u8(bs, 16); + gf_bs_write_u8(bs, 0); + /*ffmpeg specific*/ + gf_bs_write_u16(bs, dec->block_align); + gf_bs_write_u32(bs, dec->bit_rate); + gf_bs_write_u32(bs, dec->codec_tag); + if (dec->extradata_size) { + gf_bs_write_data(bs, dec->extradata, dec->extradata_size); + } + gf_bs_get_content(bs, (char **) &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + break; + } + dont_use_sl = ffd->unreliable_audio_timing; + } else { + AVCodecContext *dec = ffd->ctx->streams[ffd->video_st]->codec; + esd->slConfig->timestampResolution = ffd->video_tscale.den; + switch (dec->codec_id) { + case CODEC_ID_MPEG4: + /*there is a bug in fragmentation of raw H264 in ffmpeg, the NALU startcode (0x00000001) is split across + two frames - we therefore force internal ffmpeg codec ID to avoid NALU size recompute + at the decoder level*/ +// case CODEC_ID_H264: + /*if dsi not detected force use ffmpeg*/ + if (!dec->extradata_size) goto opaque_video; + /*otherwise use any MPEG-4 Visual*/ + esd->decoderConfig->objectTypeIndication = (dec->codec_id==CODEC_ID_H264) ? 0x21 : 0x20; + esd->decoderConfig->decoderSpecificInfo->dataLength = dec->extradata_size; + esd->decoderConfig->decoderSpecificInfo->data = malloc(sizeof(char)*dec->extradata_size); + memcpy(esd->decoderConfig->decoderSpecificInfo->data, + dec->extradata, + sizeof(char)*dec->extradata_size); + break; + case CODEC_ID_MPEG1VIDEO: + esd->decoderConfig->objectTypeIndication = 0x6A; + break; + case CODEC_ID_MPEG2VIDEO: + esd->decoderConfig->objectTypeIndication = 0x65; + break; + default: +opaque_video: + esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_FFMPEG; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, dec->codec_id); + gf_bs_write_u16(bs, dec->width); + gf_bs_write_u16(bs, dec->height); + /*ffmpeg specific*/ + gf_bs_write_u32(bs, dec->bit_rate); + gf_bs_write_u32(bs, dec->codec_tag); + + if (dec->extradata_size) { + gf_bs_write_data(bs, dec->extradata, dec->extradata_size); + } + gf_bs_get_content(bs, (char **) &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + break; + } + dont_use_sl = 0; + } + + if (dont_use_sl) { + esd->slConfig->predefined = SLPredef_SkipSL; + } else { + /*only send full AUs*/ + esd->slConfig->useAccessUnitStartFlag = esd->slConfig->useAccessUnitEndFlag = 0; + esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1; + esd->slConfig->useTimestampsFlag = 1; + } + + return esd; +} + + +static void FFD_SetupObjects(FFDemux *ffd) +{ + GF_ESD *esd; + GF_ObjectDescriptor *od; + u32 audio_esid = 0; + + if (ffd->audio_st>=0) { + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + esd = FFD_GetESDescriptor(ffd, 1); + od->objectDescriptorID = esd->ESID; + audio_esid = esd->ESID; + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(ffd->service, (GF_Descriptor*)od, (ffd->video_st>=0) ? 1 : 0); + } + if (ffd->video_st>=0) { + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + esd = FFD_GetESDescriptor(ffd, 0); + od->objectDescriptorID = esd->ESID; + esd->OCRESID = audio_esid; + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(ffd->service, (GF_Descriptor*)od, 0); + } +} + +static int ff_url_read(void *h, unsigned char *buf, int size) +{ + u32 read; + int full_size; + FFDemux *ffd = (FFDemux *)h; + + gf_term_download_update_stats(ffd->dnload); + full_size = 0; + if (ffd->buffer_used) { + if (ffd->buffer_used>size) { + ffd->buffer_used-=size; + memcpy(ffd->buffer, ffd->buffer+size, sizeof(char)*ffd->buffer_used); + if (ffd->outdbg) fwrite(buf, size, 1, ffd->outdbg); + return size; + } + full_size += ffd->buffer_used; + buf += ffd->buffer_used; + size -= ffd->buffer_used; + ffd->buffer_used = 0; + } + + while (size) { + GF_Err e = gf_dm_sess_fetch_data(ffd->dnload, buf, size, &read); + if (e==GF_EOS) break; + /*we're sync!!*/ + if (e==GF_IP_NETWORK_EMPTY) continue; + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMPEG Demuxer] error fetching bytes from network: %s\n", gf_error_to_string(e) ) ); + return -1; + } + full_size += read; + if (read==size) break; + size -= read; + buf += read; + } + if (ffd->outdbg) fwrite(ffd->buffer, full_size, 1, ffd->outdbg); + return full_size ? (int) full_size : -1; +} + +static void FFD_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ +} + +static GF_Err FFD_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + GF_Err e; + s64 last_aud_pts; + u32 i; + Bool is_local; + const char *sOpt; + char *ext, szName[1000]; + FFDemux *ffd = plug->priv; + AVInputFormat *av_in = NULL; + char szExt[20]; + + if (ffd->ctx) return GF_SERVICE_ERROR; + + strcpy(szName, url); + ext = strrchr(szName, '#'); + ffd->service_type = 0; + e = GF_NOT_SUPPORTED; + ffd->service = serv; + + if (ext) { + if (!stricmp(&ext[1], "video")) ffd->service_type = 1; + else if (!stricmp(&ext[1], "audio")) ffd->service_type = 2; + ext[0] = 0; + } + + /*some extensions not supported by ffmpeg, overload input format*/ + ext = strrchr(szName, '.'); + strcpy(szExt, ext ? ext+1 : ""); + strlwr(szExt); + if (!strcmp(szExt, "cmp")) av_in = av_find_input_format("m4v"); + + is_local = (strnicmp(url, "file://", 7) && strstr(url, "://")) ? 0 : 1; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[FFMPEG] opening file %s - local %d - av_in %08x\n", url, is_local, av_in)); + + if (!is_local) { + AVProbeData pd; + + /*setup wraper for FFMPEG I/O*/ + ffd->buffer_size = 8192; + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "FFMPEG", "IOBufferSize"); + if (sOpt) ffd->buffer_size = atoi(sOpt); + ffd->buffer = malloc(sizeof(char)*ffd->buffer_size); + ffd->outdbg = fopen("ffdeb.raw", "wb"); + + init_put_byte(&ffd->io, ffd->buffer, ffd->buffer_size, 0, ffd, ff_url_read, NULL, NULL); + + ffd->dnload = gf_term_download_new(ffd->service, url, GF_NETIO_SESSION_NOT_THREADED | GF_NETIO_SESSION_NOT_CACHED, FFD_NetIO, ffd); + if (!ffd->dnload) return GF_URL_ERROR; + while (1) { + e = gf_dm_sess_fetch_data(ffd->dnload, ffd->buffer, ffd->buffer_size, &ffd->buffer_used); + if (e==GF_EOS) break; + /*we're sync!!*/ + if (e==GF_IP_NETWORK_EMPTY) continue; + if (e) goto err_exit; + if (ffd->buffer_used) break; + } + + pd.filename = szName; + pd.buf_size = ffd->buffer_used; + pd.buf = ffd->buffer; + av_in = av_probe_input_format(&pd, 1); + if (!av_in) { + return GF_NOT_SUPPORTED; + } + if (ffd->outdbg) fwrite(ffd->buffer, ffd->buffer_used, 1, ffd->outdbg); + ffd->buffer_used = 0; + + /*setup downloader*/ + av_in->flags |= AVFMT_NOFILE; + i = av_open_input_stream(&ffd->ctx, &ffd->io, szName, av_in, NULL); + } else { + i = av_open_input_file(&ffd->ctx, szName, av_in, 0, NULL); + } + + switch (i) { + case 0: e = GF_OK; break; + case AVERROR_IO: e = GF_URL_ERROR; goto err_exit; + case AVERROR_INVALIDDATA: e = GF_NON_COMPLIANT_BITSTREAM; goto err_exit; + case AVERROR_NOMEM: e = GF_OUT_OF_MEM; goto err_exit; + case AVERROR_NOFMT: e = GF_NOT_SUPPORTED; goto err_exit; + default: e = GF_SERVICE_ERROR; goto err_exit; + } + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[FFMPEG] looking for streams in %s - %d streams - type %s\n", ffd->ctx->filename, ffd->ctx->nb_streams, ffd->ctx->iformat->name)); + + if (av_find_stream_info(ffd->ctx) <0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMPEG] cannot locate streams\n")); + e = GF_NOT_SUPPORTED; + goto err_exit; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[FFMPEG] file %s opened - %d streams\n", url, ffd->ctx->nb_streams)); + + /*figure out if we can use codecs or not*/ + ffd->audio_st = ffd->video_st = -1; + for (i = 0; i < ffd->ctx->nb_streams; i++) { + AVCodecContext *enc = ffd->ctx->streams[i]->codec; + switch(enc->codec_type) { + case CODEC_TYPE_AUDIO: + if ((ffd->audio_st<0) && (ffd->service_type!=1)) { + ffd->audio_st = i; + ffd->audio_tscale = ffd->ctx->streams[i]->time_base; + } + break; + case CODEC_TYPE_VIDEO: + if ((ffd->video_st<0) && (ffd->service_type!=2)) { + ffd->video_st = i; + ffd->video_tscale = ffd->ctx->streams[i]->time_base; + } + break; + default: + break; + } + } + if ((ffd->service_type==1) && (ffd->video_st<0)) goto err_exit; + if ((ffd->service_type==2) && (ffd->audio_st<0)) goto err_exit; + if ((ffd->video_st<0) && (ffd->audio_st<0)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMPEG] No supported streams in file\n")); + goto err_exit; + } + + + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "FFMPEG", "DataBufferMS"); + ffd->data_buffer_ms = 0; + if (sOpt) ffd->data_buffer_ms = atoi(sOpt); + if (!ffd->data_buffer_ms) ffd->data_buffer_ms = FFD_DATA_BUFFER; + + /*check we do have increasing pts. If not we can't rely on pts, we must skip SL + we assume video pts is always present*/ + if (ffd->audio_st>=0) { + last_aud_pts = 0; + for (i=0; i<20; i++) { + AVPacket pkt; + pkt.stream_index = -1; + if (av_read_frame(ffd->ctx, &pkt) <0) break; + if (pkt.pts == AV_NOPTS_VALUE) pkt.pts = pkt.dts; + if (pkt.stream_index==ffd->audio_st) last_aud_pts = pkt.pts; + } + if (last_aud_pts*ffd->audio_tscale.den<10*ffd->audio_tscale.num) ffd->unreliable_audio_timing = 1; + } + + /*build seek*/ + if (is_local) { + ffd->seekable = (av_seek_frame(ffd->ctx, -1, 0, AVSEEK_FLAG_BACKWARD)<0) ? 0 : 1; + if (!ffd->seekable) { + av_close_input_file(ffd->ctx); + av_open_input_file(&ffd->ctx, szName, av_in, 0, NULL); + av_find_stream_info(ffd->ctx); + } + } + + /*let's go*/ + gf_term_on_connect(serv, NULL, GF_OK); + if (!ffd->service_type) FFD_SetupObjects(ffd); + ffd->service_type = 0; + return GF_OK; + +err_exit: + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMPEG] Error opening file %s: %s\n", url, gf_error_to_string(e))); + if (ffd->ctx) av_close_input_file(ffd->ctx); + ffd->ctx = NULL; + gf_term_on_connect(serv, NULL, e); + return GF_OK; +} + + +static GF_Descriptor *FFD_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + GF_ObjectDescriptor *od; + GF_ESD *esd; + FFDemux *ffd = plug->priv; + + if (!ffd->ctx) return NULL; + + if (expect_type==GF_MEDIA_OBJECT_UNDEF) { + if (ffd->video_st>=0) expect_type=GF_MEDIA_OBJECT_VIDEO; + else if (ffd->audio_st>=0) expect_type=GF_MEDIA_OBJECT_AUDIO; + } + + + /*since we don't handle multitrack in ffmpeg, we don't need to check sub_url, only use expected type*/ + if (expect_type==GF_MEDIA_OBJECT_AUDIO) { + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1; + esd = FFD_GetESDescriptor(ffd, 1); + /*if session join, setup sync*/ + if (ffd->video_ch) esd->OCRESID = ffd->video_st+1; + gf_list_add(od->ESDescriptors, esd); + ffd->service_type = 2; + return (GF_Descriptor *) od; + } + if (expect_type==GF_MEDIA_OBJECT_VIDEO) { + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1; + esd = FFD_GetESDescriptor(ffd, 0); + /*if session join, setup sync*/ + if (ffd->audio_ch) esd->OCRESID = ffd->audio_st+1; + gf_list_add(od->ESDescriptors, esd); + ffd->service_type = 1; + return (GF_Descriptor *) od; + } + return NULL; +} + + +static GF_Err FFD_CloseService(GF_InputService *plug) +{ + FFDemux *ffd = plug->priv; + + ffd->is_running = 0; + + if (ffd->ctx) av_close_input_file(ffd->ctx); + ffd->ctx = NULL; + ffd->audio_ch = ffd->video_ch = NULL; + ffd->audio_run = ffd->video_run = 0; + + if (ffd->dnload) { + while (!ffd->is_running) gf_sleep(0); + ffd->is_running = 0; + gf_term_download_del(ffd->dnload); + ffd->dnload = NULL; + } + if (ffd->buffer) free(ffd->buffer); + ffd->buffer = NULL; + + gf_term_on_disconnect(ffd->service, NULL, GF_OK); + if (ffd->outdbg) fclose(ffd->outdbg); + return GF_OK; +} + +static GF_Err FFD_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + GF_Err e; + u32 ESID; + FFDemux *ffd = plug->priv; + + e = GF_STREAM_NOT_FOUND; + if (upstream) { + e = GF_ISOM_INVALID_FILE; + goto exit; + } + if (!strstr(url, "ES_ID=")) { + e = GF_NOT_SUPPORTED; + goto exit; + } + sscanf(url, "ES_ID=%d", &ESID); + + if ((s32) ESID == 1 + ffd->audio_st) { + if (ffd->audio_ch) { + e = GF_SERVICE_ERROR; + goto exit; + } + ffd->audio_ch = channel; + e = GF_OK; + } + else if ((s32) ESID == 1 + ffd->video_st) { + if (ffd->video_ch) { + e = GF_SERVICE_ERROR; + goto exit; + } + ffd->video_ch = channel; + e = GF_OK; + } + +exit: + gf_term_on_connect(ffd->service, channel, e); + return GF_OK; +} + +static GF_Err FFD_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + GF_Err e; + FFDemux *ffd = plug->priv; + + e = GF_STREAM_NOT_FOUND; + if (ffd->audio_ch == channel) { + e = GF_OK; + ffd->audio_ch = NULL; + ffd->audio_run = 0; + } + else if (ffd->video_ch == channel) { + e = GF_OK; + ffd->video_ch = NULL; + ffd->video_run = 0; + } + gf_term_on_disconnect(ffd->service, channel, e); + return GF_OK; +} + +static GF_Err FFD_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + FFDemux *ffd = plug->priv; + + + if (com->command_type==GF_NET_SERVICE_HAS_AUDIO) { + if (ffd->audio_st>=0) return GF_OK; + return GF_NOT_SUPPORTED; + } + + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + + switch (com->command_type) { + /*only BIFS/OD work in pull mode (cf ffmpeg_in.h)*/ + case GF_NET_CHAN_SET_PULL: + return GF_NOT_SUPPORTED; + case GF_NET_CHAN_INTERACTIVE: + return ffd->seekable ? GF_OK : GF_NOT_SUPPORTED; + case GF_NET_CHAN_BUFFER: + com->buffer.max = com->buffer.min = 0; + return GF_OK; + case GF_NET_CHAN_DURATION: + if (ffd->ctx->duration == AV_NOPTS_VALUE) + com->duration.duration = -1; + else + com->duration.duration = (Double) ffd->ctx->duration / AV_TIME_BASE; + return GF_OK; + /*fetch start time*/ + case GF_NET_CHAN_PLAY: + if (com->play.speed<0) return GF_NOT_SUPPORTED; + + gf_mx_p(ffd->mx); + ffd->seek_time = (com->play.start_range>=0) ? com->play.start_range : 0; + + if (ffd->audio_ch==com->base.on_channel) ffd->audio_run = 1; + else if (ffd->video_ch==com->base.on_channel) ffd->video_run = 1; + + /*play on media stream, start thread*/ + if ((ffd->audio_ch==com->base.on_channel) || (ffd->video_ch==com->base.on_channel)) { + if (ffd->is_running!=1) { + ffd->is_running = 1; + gf_th_run(ffd->thread, FFDemux_Run, ffd); + } + } + gf_mx_v(ffd->mx); + return GF_OK; + case GF_NET_CHAN_STOP: + if (ffd->audio_ch==com->base.on_channel) ffd->audio_run = 0; + else if (ffd->video_ch==com->base.on_channel) ffd->video_run = 0; + return GF_OK; + /*note we don't handle PAUSE/RESUME/SET_SPEED, this is automatically handled by the demuxing thread + through buffer occupancy queries*/ + + default: + return GF_OK; + } + + return GF_OK; +} + + +static Bool FFD_CanHandleURLInService(GF_InputService *plug, const char *url) +{ + char szURL[2048], *sep; + FFDemux *ffd = (FFDemux *)plug->priv; + const char *this_url = gf_term_get_service_url(ffd->service); + if (!this_url || !url) return 0; + + strcpy(szURL, this_url); + sep = strrchr(szURL, '#'); + if (sep) sep[0] = 0; + + if ((url[0] != '#') && strnicmp(szURL, url, sizeof(char)*strlen(szURL))) return 0; + sep = strrchr(url, '#'); + if (!stricmp(sep, "#video") && (ffd->video_st>=0)) return 1; + if (!stricmp(sep, "#audio") && (ffd->audio_st>=0)) return 1; + return 0; +} + +void *New_FFMPEG_Demux() +{ + FFDemux *priv; + GF_InputService *ffd = malloc(sizeof(GF_InputService)); + memset(ffd, 0, sizeof(GF_InputService)); + + priv = malloc(sizeof(FFDemux)); + memset(priv, 0, sizeof(FFDemux)); + + /* register all codecs, demux and protocols */ + av_register_all(); + + ffd->CanHandleURL = FFD_CanHandleURL; + ffd->CloseService = FFD_CloseService; + ffd->ConnectChannel = FFD_ConnectChannel; + ffd->ConnectService = FFD_ConnectService; + ffd->DisconnectChannel = FFD_DisconnectChannel; + ffd->GetServiceDescriptor = FFD_GetServiceDesc; + ffd->ServiceCommand = FFD_ServiceCommand; + + ffd->CanHandleURLInService = FFD_CanHandleURLInService; + + priv->thread = gf_th_new("FFMPEG Demux"); + priv->mx = gf_mx_new("FFMPEG Demux"); + + GF_REGISTER_MODULE_INTERFACE(ffd, GF_NET_CLIENT_INTERFACE, "FFMPEG Demuxer", "gpac distribution"); + ffd->priv = priv; + return ffd; +} + +void Delete_FFMPEG_Demux(void *ifce) +{ + FFDemux *ffd; + GF_InputService *ptr = (GF_InputService *)ifce; + + ffd = ptr->priv; + + gf_th_del(ffd->thread); + gf_mx_del(ffd->mx); + + free(ffd); + free(ptr); +} + + diff --git a/modules/ffmpeg_in/ffmpeg_in.def b/modules/ffmpeg_in/ffmpeg_in.def new file mode 100644 index 0000000..fee6546 --- /dev/null +++ b/modules/ffmpeg_in/ffmpeg_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_ffmpeg_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/ffmpeg_in/ffmpeg_in.h b/modules/ffmpeg_in/ffmpeg_in.h new file mode 100644 index 0000000..81358bb --- /dev/null +++ b/modules/ffmpeg_in/ffmpeg_in.h @@ -0,0 +1,178 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MP4 reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef __FFMPEG_IN_H +#define __FFMPEG_IN_H + + + +/*include net API*/ +#include <gpac/modules/service.h> +/*include decoder API*/ +#include <gpac/modules/codec.h> +#include <gpac/constants.h> +#include <gpac/thread.h> + +#if defined(WIN32) && !defined(__MINGW32__) + +#define EMULATE_INTTYPES +#define EMULATE_FAST_INT +#ifndef inline +#define inline __inline +#endif + +#if defined(__SYMBIAN32__) +#define EMULATE_INTTYPES +#endif + + +#ifndef __MINGW32__ +#define __attribute__(s) +#endif + +#endif + + +/*include FFMPEG APIs*/ +#include <ffmpeg/avformat.h> + +void gf_av_vlog(void* avcl, int level, const char *fmt, va_list vl); + + +#if LIBAVCODEC_VERSION_INT > ((52<<16)+(0<<8)+0) +#define FFMPEG_SWSCALE +#include <ffmpeg/swscale.h> +#endif + +/*FFMPEG decoder module */ +typedef struct +{ + u32 ES_ID; + u32 out_size; + u32 oti, st; + u32 previous_par; + Bool no_par_update; + + Bool check_short_header; + AVCodecContext *ctx; + AVCodec *codec; + AVFrame *frame; + u32 pix_fmt; + u32 out_pix_fmt; + +#ifdef FFMPEG_SWSCALE + struct SwsContext *sws_ctx; +#endif + + /*for audio packed frames*/ + u32 frame_start; + char audio_buf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; + Bool check_h264_isma; +} FFDec; + +void *FFDEC_Load(); +void FFDEC_Delete(void *ifce); + + +/* + reader interface + +*/ + +//#define FFMPEG_IO_BUF_SIZE 16384 + + +typedef struct +{ + /*the service we're responsible for*/ + GF_ClientService *service; + + /*input file*/ + AVFormatContext *ctx; + + Bool seekable; + Double seek_time; + + s32 audio_st, video_st; + /*app channels (only deal with 1 audio and one video for now)*/ + LPNETCHANNEL audio_ch; + LPNETCHANNEL video_ch; + Bool audio_run, video_run; + AVRational audio_tscale, video_tscale; + u32 data_buffer_ms; + + /*demuxer thread - we cannot use direct fetching because of demultiplex structure of libavformat + (reading one channel may lock the other)*/ + GF_Thread *thread; + GF_Mutex *mx; + u32 is_paused, is_running; + + u32 service_type; + Bool unreliable_audio_timing; + + /*IO wrapper*/ + /*file downloader*/ + GF_DownloadSession *dnload; + + ByteIOContext io; + char *buffer; + u32 buffer_size; + + u32 buffer_used; + + FILE *outdbg; +} FFDemux; + +void *New_FFMPEG_Demux(); +void Delete_FFMPEG_Demux(void *ifce); + + +/*The DSI sent is: + + u32 codec_id + +- for audio - + u32 sample_rate: sampling rate or 0 if unknown + u16 nb_channels: num channels or 0 if unknown + u16 nb_bits_per_sample: nb bits or 0 if unknown + u16 num_samples: num audio samples per frame or 0 if unknown + u16 block_align: audio block align + +- for video - + u32 width: video width or 0 if unknown; + u32 height: video height or 0 if unknown; + +- for both - + u32 codec_tag: ffmpeg ctx codec tag + u32 bit_rate: ffmpeg ctx bit rate + +- till end of DSI bitstream- + char *data: extra_data +*/ + + + +#endif + diff --git a/modules/ffmpeg_in/ffmpeg_load.c b/modules/ffmpeg_in/ffmpeg_load.c new file mode 100644 index 0000000..ff5b320 --- /dev/null +++ b/modules/ffmpeg_in/ffmpeg_load.c @@ -0,0 +1,55 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / FFMPEG module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "ffmpeg_in.h" + + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return 1; + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return 1; + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return FFDEC_Load(); + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return New_FFMPEG_Demux(); + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: + FFDEC_Delete(ifce); + break; + case GF_NET_CLIENT_INTERFACE: + Delete_FFMPEG_Demux(ifce); + break; + } +} diff --git a/modules/ft_font/Makefile b/modules/ft_font/Makefile new file mode 100644 index 0000000..a5db870 --- /dev/null +++ b/modules/ft_font/Makefile @@ -0,0 +1,66 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/ft_font + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include $(FT_CFLAGS) + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= ft_font.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_ft_font.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols ft_font.def +endif + + +LINKVAR=-L../../bin/gcc -lgpac $(FT_LIBS) + + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(LINKVAR) $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/ft_font/ft_font.c b/modules/ft_font/ft_font.c new file mode 100644 index 0000000..06ab03e --- /dev/null +++ b/modules/ft_font/ft_font.c @@ -0,0 +1,595 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / FreeType font engine module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <gpac/modules/font.h> +#include <gpac/list.h> +#include <gpac/utf.h> +#include <gpac/tools.h> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_OUTLINE_H +/*TrueType tables*/ +#include FT_TRUETYPE_TABLES_H + +typedef struct +{ + FT_Library library; + FT_Face active_face; + char *font_dir; + + GF_List *loaded_fonts; + + /*default fonts*/ + char font_serif[1024]; + char font_sans[1024]; + char font_fixed[1024]; +} FTBuilder; + + +static Bool ft_enum_fonts(void *cbck, char *file_name, char *file_path) +{ + char szFont[GF_MAX_PATH]; + FT_Face face; + u32 num_faces, i; + GF_FontReader *dr = cbck; + FTBuilder *ftpriv = dr->udta; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[FreeType] Enumerating font %s (%s)\n", file_name, file_path)); + + if (FT_New_Face(ftpriv->library, file_path, 0, & face )) return 0; + if (!face) return 0; + + num_faces = face->num_faces; + /*locate right font in collection if several*/ + for (i=0; i<num_faces; i++) { + + /*only scan scalable fonts*/ + if (face->face_flags & FT_FACE_FLAG_SCALABLE) { + Bool bold, italic, smallcaps; + strcpy(szFont, face->family_name); + + /*remember first font found which looks like a alphabetical one*/ + if (!strlen(ftpriv->font_dir)) { + u32 gidx; + FT_Select_Charmap(face, FT_ENCODING_UNICODE); + gidx = FT_Get_Char_Index(face, (u32) 'a'); + if (gidx) gidx = FT_Get_Char_Index(face, (u32) 'z'); + if (gidx) gidx = FT_Get_Char_Index(face, (u32) '1'); + if (gidx) gidx = FT_Get_Char_Index(face, (u32) '@'); + if (gidx) strcpy(ftpriv->font_dir, szFont); + } + + bold = italic = smallcaps = 0; + + if (face->style_name) { + char *name = strdup(face->style_name); + strupr(name); + if (strstr(name, "BOLD")) bold = 1; + if (strstr(name, "ITALIC")) italic = 1; + free(name); + } else { + if (face->style_flags & FT_STYLE_FLAG_BOLD) bold = 1; + if (face->style_flags & FT_STYLE_FLAG_ITALIC) italic = 1; + } + + if (bold) strcat(szFont, " Bold"); + if (italic) strcat(szFont, " Italic"); + if (smallcaps) strcat(szFont, " Smallcaps"); + gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", szFont, file_path); + + /*try to assign default fixed fonts*/ + if (!bold && !italic) { + Bool store = 0; + char szFont[1024]; + strcpy(szFont, face->family_name); + strlwr(szFont); + + if (!strlen(ftpriv->font_fixed)) { + if (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) store = 1; + else if (!strnicmp(face->family_name, "Courier", 15)) store = 1; + else if (strstr(szFont, "sans") || strstr(szFont, "serif")) store = 0; + else if (strstr(szFont, "monospace")) store = 1; + + if (store) strcpy(ftpriv->font_fixed, face->family_name); + } + if (!store && !strlen(ftpriv->font_sans)) { + if (!strnicmp(face->family_name, "Arial", 5)) store = 1; + else if (!strnicmp(face->family_name, "Verdana", 7)) store = 1; + else if (strstr(szFont, "serif") || strstr(szFont, "fixed")) store = 0; + else if (strstr(szFont, "sans")) store = 1; + + if (store) strcpy(ftpriv->font_sans, face->family_name); + } + if (!store && !strlen(ftpriv->font_serif)) { + if (!strnicmp(face->family_name, "Times New Roman", 15)) store = 1; + else if (strstr(szFont, "sans") || strstr(szFont, "fixed")) store = 0; + else if (strstr(szFont, "serif")) store = 1; + + if (store) strcpy(ftpriv->font_serif, face->family_name); + } + } + } + + FT_Done_Face(face); + if (i+1==num_faces) return 0; + + /*load next font in collection*/ + if (FT_New_Face(ftpriv->library, file_path, i+1, & face )) return 0; + if (!face) return 0; + } + return 0; +} + +static Bool ft_enum_fonts_dir(void *cbck, char *file_name, char *file_path) +{ + GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[FreeType] Scanning directory %s (%s)\n", file_name, file_path)); + gf_enum_directory(file_path, 0, ft_enum_fonts, cbck, "ttf;ttc"); + return gf_enum_directory(file_path, 1, ft_enum_fonts_dir, cbck, NULL); +} + + +static void ft_rescan_fonts(GF_FontReader *dr) +{ + char *font_dir; + char font_def[1024]; + u32 i, count; + GF_Config *cfg = gf_modules_get_config((GF_BaseInterface *)dr); + FTBuilder *ftpriv = (FTBuilder *)dr->udta; + + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("[FreeType] Rescaning font directory %s\n", ftpriv->font_dir)); + + count = gf_cfg_get_key_count(cfg, "FontEngine"); + for (i=0; i<count; i++) { + const char *key = gf_cfg_get_key_name(cfg, "FontEngine", i); + if (!strcmp(key, "FontReader")) continue; + if (!strcmp(key, "FontDirectory")) continue; + if (!strcmp(key, "RescanFonts")) continue; + /*any other persistent options should go here*/ + + gf_cfg_set_key(cfg, "FontEngine", key, NULL); + count--; + i--; + } + gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "RescanFonts", "no"); + + strcpy(ftpriv->font_serif, ""); + strcpy(ftpriv->font_sans, ""); + strcpy(ftpriv->font_fixed, ""); + + font_dir = ftpriv->font_dir; + /*here we will store the first font found*/ + font_def[0] = 0; + ftpriv->font_dir = font_def; + + gf_enum_directory(font_dir, 0, ft_enum_fonts, dr, "ttf;ttc"); + gf_enum_directory(font_dir, 1, ft_enum_fonts_dir, dr, NULL); + ftpriv->font_dir = font_dir; + + if ( strlen(font_def) ) { + if (!strlen(ftpriv->font_fixed)) strcpy(ftpriv->font_fixed, font_def); + if (!strlen(ftpriv->font_serif)) strcpy(ftpriv->font_serif, font_def); + if (!strlen(ftpriv->font_sans)) strcpy(ftpriv->font_sans, font_def); + } + gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "FontFixed", ftpriv->font_fixed); + gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "FontSerif", ftpriv->font_serif); + gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "FontSans", ftpriv->font_sans); + + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("[FreeType] Font directory scanned\n", ftpriv->font_dir)); +} + + + +static GF_Err ft_init_font_engine(GF_FontReader *dr) +{ + const char *sOpt; + FTBuilder *ftpriv = (FTBuilder *)dr->udta; + + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontDirectory"); + if (!sOpt) return GF_BAD_PARAM; + + /*inits freetype*/ + if (FT_Init_FreeType(&ftpriv->library) ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[FreeType] Cannot initialize FreeType\n")); + return GF_IO_ERR; + } + + /*remove the final delimiter*/ + ftpriv->font_dir = strdup(sOpt); + while ( (ftpriv->font_dir[strlen(ftpriv->font_dir)-1] == '\n') || (ftpriv->font_dir[strlen(ftpriv->font_dir)-1] == '\r') ) + ftpriv->font_dir[strlen(ftpriv->font_dir)-1] = 0; + + /*store font path*/ + if (ftpriv->font_dir[strlen(ftpriv->font_dir)-1] != GF_PATH_SEPARATOR) { + char ext[2], *temp; + ext[0] = GF_PATH_SEPARATOR; + ext[1] = 0; + temp = malloc(sizeof(char) * (strlen(ftpriv->font_dir) + 2)); + strcpy(temp, ftpriv->font_dir); + strcat(temp, ext); + free(ftpriv->font_dir); + ftpriv->font_dir = temp; + } + + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "RescanFonts"); + if (!sOpt || !strcmp(sOpt, "yes") ) + ft_rescan_fonts(dr); + + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontSerif"); + if (sOpt) strcpy(ftpriv->font_serif, sOpt); + + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontSans"); + if (sOpt) strcpy(ftpriv->font_sans, sOpt); + + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontFixed"); + if (sOpt) strcpy(ftpriv->font_fixed, sOpt); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[FreeType] Init OK - font directory %s\n", ftpriv->font_dir)); + + return GF_OK; +} + +static GF_Err ft_shutdown_font_engine(GF_FontReader *dr) +{ + FTBuilder *ftpriv = (FTBuilder *)dr->udta; + + ftpriv->active_face = NULL; + /*reset loaded fonts*/ + while (gf_list_count(ftpriv->loaded_fonts)) { + FT_Face face = gf_list_get(ftpriv->loaded_fonts, 0); + gf_list_rem(ftpriv->loaded_fonts, 0); + FT_Done_Face(face); + } + + /*exit FT*/ + if (ftpriv->library) FT_Done_FreeType(ftpriv->library); + ftpriv->library = NULL; + return GF_OK; +} + + +static Bool ft_check_face(FT_Face font, const char *fontName, u32 styles) +{ + u32 ft_style, loc_styles; + char *name; + + if (fontName && stricmp(font->family_name, fontName)) return 0; + ft_style = 0; + if (font->style_name) { + name = strdup(font->style_name); + strupr(name); + if (strstr(name, "BOLD")) ft_style |= GF_FONT_WEIGHT_BOLD; + if (strstr(name, "ITALIC")) ft_style |= GF_FONT_ITALIC; + free(name); + } else { + if (font->style_flags & FT_STYLE_FLAG_BOLD) ft_style |= GF_FONT_WEIGHT_BOLD; + if (font->style_flags & FT_STYLE_FLAG_ITALIC) ft_style |= GF_FONT_ITALIC; + } + name = strdup(font->family_name); + strupr(name); + if (strstr(name, "BOLD")) ft_style |= GF_FONT_WEIGHT_BOLD; + if (strstr(name, "ITALIC")) ft_style |= GF_FONT_ITALIC; + free(name); + + loc_styles = styles & GF_FONT_WEIGHT_MASK; + if (loc_styles>=GF_FONT_WEIGHT_BOLD) + styles = (styles & 0x00000007) | GF_FONT_WEIGHT_BOLD; + else + styles = (styles & 0x00000007); + + if (ft_style==styles) + return 1; + return 0; +} + +static FT_Face ft_font_in_cache(FTBuilder *ft, const char *fontName, u32 styles) +{ + u32 i=0; + FT_Face font; + + while ((font = gf_list_enum(ft->loaded_fonts, &i))) { + if (ft_check_face(font, fontName, styles)) return font; + } + return NULL; +} + + + +static GF_Err ft_set_font(GF_FontReader *dr, const char *OrigFontName, u32 styles) +{ + char fname[1024]; + char *fontName; + const char *opt; + FTBuilder *ftpriv = (FTBuilder *)dr->udta; + + fontName = (char *) OrigFontName; + ftpriv->active_face = NULL; + + if (!fontName || !strlen(fontName) || !stricmp(fontName, "SERIF")) { + fontName = ftpriv->font_serif; + } + else if (!stricmp(fontName, "SANS") || !stricmp(fontName, "sans-serif")) { + fontName = ftpriv->font_sans; + } + else if (!stricmp(fontName, "TYPEWRITER") || !stricmp(fontName, "monospace")) { + fontName = ftpriv->font_fixed; + } + + /*first look in loaded fonts*/ + ftpriv->active_face = ft_font_in_cache(ftpriv, fontName, styles); + if (ftpriv->active_face) return GF_OK; + + /*check cfg file - freetype is slow at loading fonts so we keep the (font name + styles)=fontfile associations + in the cfg file*/ + if (!fontName || !strlen(fontName)) return GF_NOT_SUPPORTED; + strcpy(fname, fontName); + if (styles & GF_FONT_WEIGHT_BOLD) strcat(fname, " Bold"); + if (styles & GF_FONT_ITALIC) strcat(fname, " Italic"); + + opt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", fname); + if (opt) { + FT_Face face; + if (FT_New_Face(ftpriv->library, opt, 0, & face )) return GF_IO_ERR; + if (!face) return GF_IO_ERR; + gf_list_add(ftpriv->loaded_fonts, face); + ftpriv->active_face = face; + return GF_OK; + } + + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[FreeType] Font %s not found\n", fname)); + return GF_NOT_SUPPORTED; +} + +static GF_Err ft_get_font_info(GF_FontReader *dr, char **font_name, s32 *em_size, s32 *ascent, s32 *descent, s32 *underline, s32 *line_spacing, s32 *max_advance_h, s32 *max_advance_v) +{ + FTBuilder *ftpriv = (FTBuilder *)dr->udta; + if (!ftpriv->active_face) return GF_BAD_PARAM; + + *em_size = ftpriv->active_face->units_per_EM; + *ascent = ftpriv->active_face->ascender; + *descent = ftpriv->active_face->descender; + *underline = ftpriv->active_face->underline_position; + *line_spacing = ftpriv->active_face->height; + *font_name = strdup(ftpriv->active_face->family_name); + *max_advance_h = ftpriv->active_face->max_advance_width; + *max_advance_v = ftpriv->active_face->max_advance_height; + return GF_OK; +} + + +static GF_Err ft_get_glyphs(GF_FontReader *dr, const char *utf_string, u32 *glyph_buffer, u32 *io_glyph_buffer_size, const char *xml_lang, Bool *is_rtl) +{ + u32 len; + u32 i; + u16 *conv; + char *utf8 = (char*) utf_string; + FTBuilder *ftpriv = (FTBuilder *)dr->udta; + + if (!ftpriv->active_face) return GF_BAD_PARAM; + + /*TODO: glyph substitution / ligature */ + + len = utf_string ? strlen(utf_string) : 0; + if (!len) { + *io_glyph_buffer_size = 0; + return GF_OK; + } + if (*io_glyph_buffer_size < len+1) { + *io_glyph_buffer_size = len+1; + return GF_BUFFER_TOO_SMALL; + } + len = gf_utf8_mbstowcs((u16*) glyph_buffer, *io_glyph_buffer_size, (const char **) &utf8); + if ((s32)len<0) return GF_IO_ERR; + if (utf8) return GF_IO_ERR; + + /*perform bidi relayout*/ + conv = (u16*) glyph_buffer; + *is_rtl = gf_utf8_reorder_bidi(conv, len); + /*move 16bit buffer to 32bit*/ + for (i=len; i>0; i--) { + glyph_buffer[i-1] = (u32) conv[i-1]; + } + *io_glyph_buffer_size = len; + return GF_OK; +} + + + + + +typedef struct +{ + FTBuilder *ftpriv; + GF_Path *path; + s32 last_x, last_y; +} ft_outliner; + + +static int ft_move_to(const FT_Vector *to, void *user) +{ + ft_outliner *ftol = (ft_outliner *)user; + gf_path_add_move_to(ftol->path, INT2FIX(to->x), INT2FIX(to->y) ); + ftol->last_x = to->x; + ftol->last_y = to->y; + return 0; +} +static int ft_line_to(const FT_Vector *to, void *user) +{ + ft_outliner *ftol = (ft_outliner *)user; + if ( (ftol->last_x == to->x) && (ftol->last_y == to->y)) { + gf_path_close(ftol->path); + } else { + gf_path_add_line_to(ftol->path, INT2FIX(to->x), INT2FIX(to->y) ); + } + return 0; +} +static int ft_conic_to(const FT_Vector * control, const FT_Vector *to, void *user) +{ + ft_outliner *ftol = (ft_outliner *)user; + gf_path_add_quadratic_to(ftol->path, INT2FIX(control->x), INT2FIX(control->y), INT2FIX(to->x), INT2FIX(to->y) ); + if ( (ftol->last_x == to->x) && (ftol->last_y == to->y)) gf_path_close(ftol->path); + return 0; +} +static int ft_cubic_to(const FT_Vector *c1, const FT_Vector *c2, const FT_Vector *to, void *user) +{ + ft_outliner *ftol = (ft_outliner *)user; + gf_path_add_cubic_to(ftol->path, INT2FIX(c1->x), INT2FIX(c1->y), INT2FIX(c2->x), INT2FIX(c2->y), INT2FIX(to->x), INT2FIX(to->y) ); + if ( (ftol->last_x == to->x) && (ftol->last_y == to->y)) gf_path_close(ftol->path); + return 0; +} + + +static GF_Glyph *ft_load_glyph(GF_FontReader *dr, u32 glyph_name) +{ + GF_Glyph *glyph; + u32 glyph_idx; + FT_BBox bbox; + FT_OutlineGlyph outline; + ft_outliner outl; + FT_Outline_Funcs ft_outl_funcs; + + FTBuilder *ftpriv = (FTBuilder *)dr->udta; + if (!ftpriv->active_face || !glyph_name) return NULL; + + FT_Select_Charmap(ftpriv->active_face, FT_ENCODING_UNICODE); + + glyph_idx = FT_Get_Char_Index(ftpriv->active_face, glyph_name); + /*missing glyph*/ + if (!glyph_idx) return NULL; + + /*work in design units*/ + FT_Load_Glyph(ftpriv->active_face, glyph_idx, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP); + + FT_Get_Glyph(ftpriv->active_face->glyph, (FT_Glyph*)&outline); + +#ifdef FT_GLYPH_FORMAT_OUTLINE + /*oops not vectorial...*/ + if (outline->root.format != FT_GLYPH_FORMAT_OUTLINE) return NULL; +#endif + + + GF_SAFEALLOC(glyph, GF_Glyph); + GF_SAFEALLOC(glyph->path, GF_Path); + + /*setup outliner*/ + ft_outl_funcs.shift = 0; + ft_outl_funcs.delta = 0; + ft_outl_funcs.move_to = ft_move_to; + ft_outl_funcs.line_to = ft_line_to; + ft_outl_funcs.conic_to = ft_conic_to; + ft_outl_funcs.cubic_to = ft_cubic_to; + outl.path = glyph->path; + outl.ftpriv = ftpriv; + + /*freeType is marvelous and gives back the right advance on space char !!!*/ + FT_Outline_Decompose(&outline->outline, &ft_outl_funcs, &outl); + + FT_Glyph_Get_CBox((FT_Glyph) outline, ft_glyph_bbox_unscaled, &bbox); + + glyph->ID = glyph_name; + glyph->utf_name = glyph_name; + glyph->horiz_advance = ftpriv->active_face->glyph->metrics.horiAdvance; + glyph->vert_advance = ftpriv->active_face->glyph->metrics.vertAdvance; +/* + glyph->x = bbox.xMin; + glyph->y = bbox.yMax; +*/ + glyph->width = ftpriv->active_face->glyph->metrics.width; + glyph->height = ftpriv->active_face->glyph->metrics.height; + FT_Done_Glyph((FT_Glyph) outline); + return glyph; +} + + +GF_FontReader *ft_load() +{ + GF_FontReader *dr; + FTBuilder *ftpriv; + dr = malloc(sizeof(GF_FontReader)); + memset(dr, 0, sizeof(GF_FontReader)); + GF_REGISTER_MODULE_INTERFACE(dr, GF_FONT_READER_INTERFACE, "FreeType Font Reader", "gpac distribution"); + + ftpriv = malloc(sizeof(FTBuilder)); + memset(ftpriv, 0, sizeof(FTBuilder)); + + ftpriv->loaded_fonts = gf_list_new(); + + dr->udta = ftpriv; + + + dr->init_font_engine = ft_init_font_engine; + dr->shutdown_font_engine = ft_shutdown_font_engine; + dr->set_font = ft_set_font; + dr->get_font_info = ft_get_font_info; + dr->get_glyphs = ft_get_glyphs; + dr->load_glyph = ft_load_glyph; + return dr; +} + + +void ft_delete(GF_BaseInterface *ifce) +{ + GF_FontReader *dr = (GF_FontReader *) ifce; + FTBuilder *ftpriv = dr->udta; + + + if (ftpriv->font_dir) free(ftpriv->font_dir); + assert(!gf_list_count(ftpriv->loaded_fonts) ); + + gf_list_del(ftpriv->loaded_fonts); + + free(dr->udta); + free(dr); +} + +#ifndef GPAC_STANDALONE_RENDER_2D + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_FONT_READER_INTERFACE) return 1; + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_FONT_READER_INTERFACE) return (GF_BaseInterface *)ft_load(); + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_FONT_READER_INTERFACE: + ft_delete(ifce); + break; + } +} + +#endif + diff --git a/modules/ft_font/ft_font.def b/modules/ft_font/ft_font.def new file mode 100644 index 0000000..77a3276 --- /dev/null +++ b/modules/ft_font/ft_font.def @@ -0,0 +1,6 @@ +LIBRARY gm_ft_font.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/ft_font/ft_font.h b/modules/ft_font/ft_font.h new file mode 100644 index 0000000..689976e --- /dev/null +++ b/modules/ft_font/ft_font.h @@ -0,0 +1,29 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / FreeType font engine module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _FT_FONT_H_ +#define _FT_FONT_H_ + + +#endif /*_FT_FONT_H_*/ diff --git a/modules/gapi/gapi.cpp b/modules/gapi/gapi.cpp new file mode 100644 index 0000000..dd9cb8f --- /dev/null +++ b/modules/gapi/gapi.cpp @@ -0,0 +1,1201 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / GAPI WinCE-iPaq video render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <windows.h> +#include <aygshell.h> +#include <wingdi.h> +#include <gx.h> + +#include "gapi.h" + +#ifdef GPAC_USE_OGL_ES + +#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__) + +#if 0 +# pragma message("Using OpenGL-ES Common Lite Profile") +# pragma comment(lib, "libGLES_CL") + +#define GLES_NO_PBUFFER +#define GLES_NO_PIXMAP + +#else +# pragma message("Using OpenGL-ES Common Profile") +# pragma comment(lib, "libGLES_CM") + +//#define GLES_NO_PIXMAP + +#endif + +#pragma comment(lib, "gx.lib") +#endif + +#endif + +static Bool is_landscape = 0; + + +#define PRINT(__str) OutputDebugString(_T(__str)) + +#define GAPICTX(dr) GAPIPriv *gctx = (GAPIPriv *) dr->opaque; + +static GF_Err GAPI_InitBackBuffer(GF_VideoOutput *dr, u32 VideoWidth, u32 VideoHeight); + +static GF_VideoOutput *the_video_driver = NULL; + +static void GAPI_GetCoordinates(DWORD lParam, GF_Event *evt) +{ + GAPIPriv *ctx = (GAPIPriv *)the_video_driver->opaque; + evt->mouse.x = LOWORD(lParam); + evt->mouse.y = HIWORD(lParam); + + if (ctx->fullscreen) { + POINT pt; + pt.x = evt->mouse.x; + pt.y = evt->mouse.y; + ClientToScreen(ctx->hWnd, &pt); + if (is_landscape) { + evt->mouse.x = ctx->fs_w - pt.y; + evt->mouse.y = pt.x; + } else { + evt->mouse.x = pt.x; + evt->mouse.y = pt.y; + } + } +} + +static void w32_translate_key(u32 wParam, u32 lParam, GF_EventKey *evt) +{ + evt->flags = 0; + evt->hw_code = wParam; + switch (wParam) { + case VK_BACK: evt->key_code = GF_KEY_BACKSPACE; break; + case VK_TAB: evt->key_code = GF_KEY_TAB; break; + case VK_CLEAR: evt->key_code = GF_KEY_CLEAR; break; + case VK_RETURN: evt->key_code = GF_KEY_ENTER; break; + case VK_SHIFT: evt->key_code = GF_KEY_SHIFT; break; + case VK_CONTROL: evt->key_code = GF_KEY_CONTROL; break; + case VK_MENU: evt->key_code = GF_KEY_ALT; break; + case VK_PAUSE: evt->key_code = GF_KEY_PAUSE; break; + case VK_CAPITAL: evt->key_code = GF_KEY_CAPSLOCK; break; + case VK_KANA: evt->key_code = GF_KEY_KANAMODE; break; + case VK_JUNJA: evt->key_code = GF_KEY_JUNJAMODE; break; + case VK_FINAL: evt->key_code = GF_KEY_FINALMODE; break; + case VK_KANJI: evt->key_code = GF_KEY_KANJIMODE; break; + case VK_ESCAPE: evt->key_code = GF_KEY_ESCAPE; break; + case VK_CONVERT: evt->key_code = GF_KEY_CONVERT; break; + case VK_SPACE: evt->key_code = GF_KEY_SPACE; break; + case VK_PRIOR: evt->key_code = GF_KEY_PAGEUP; break; + case VK_NEXT: evt->key_code = GF_KEY_PAGEDOWN; break; + case VK_END: evt->key_code = GF_KEY_END; break; + case VK_HOME: evt->key_code = GF_KEY_HOME; break; + case VK_LEFT: evt->key_code = GF_KEY_LEFT; break; + case VK_UP: evt->key_code = GF_KEY_UP; break; + case VK_RIGHT: evt->key_code = GF_KEY_RIGHT; break; + case VK_DOWN: evt->key_code = GF_KEY_DOWN; break; + case VK_SELECT: evt->key_code = GF_KEY_SELECT; break; + case VK_PRINT: + case VK_SNAPSHOT: + evt->key_code = GF_KEY_PRINTSCREEN; break; + case VK_EXECUTE: evt->key_code = GF_KEY_EXECUTE; break; + case VK_INSERT: evt->key_code = GF_KEY_INSERT; break; + case VK_DELETE: evt->key_code = GF_KEY_DEL; break; + case VK_HELP: evt->key_code = GF_KEY_HELP; break; + +/* case VK_LWIN: return ; + case VK_RWIN: return ; + case VK_APPS: return ; +*/ + case VK_NUMPAD0: + evt->key_code = GF_KEY_0; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD1: + evt->key_code = GF_KEY_1; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD2: + evt->key_code = GF_KEY_2; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD3: + evt->key_code = GF_KEY_3; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD4: + evt->key_code = GF_KEY_4; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD5: + evt->key_code = GF_KEY_5; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD6: + evt->key_code = GF_KEY_6; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD7: + evt->key_code = GF_KEY_7; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD8: + evt->key_code = GF_KEY_8; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_NUMPAD9: + evt->key_code = GF_KEY_9; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_MULTIPLY: + evt->key_code = GF_KEY_STAR; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_ADD: + evt->key_code = GF_KEY_PLUS; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_SEPARATOR: + evt->key_code = GF_KEY_FULLSTOP; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_SUBTRACT: + evt->key_code = GF_KEY_HYPHEN; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_DECIMAL: + evt->key_code = GF_KEY_COMMA; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_DIVIDE: + evt->key_code = GF_KEY_SLASH; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_F1: evt->key_code = GF_KEY_F1; break; + case VK_F2: evt->key_code = GF_KEY_F2; break; + case VK_F3: evt->key_code = GF_KEY_F3; break; + case VK_F4: evt->key_code = GF_KEY_F4; break; + case VK_F5: evt->key_code = GF_KEY_F5; break; +// case VK_F6: evt->key_code = GF_KEY_F6; break; +// case VK_F7: evt->key_code = GF_KEY_F7; break; + case VK_F6: evt->key_code = GF_KEY_VOLUMEUP; break; + case VK_F7: evt->key_code = GF_KEY_VOLUMEDOWN; break; + case VK_F8: evt->key_code = GF_KEY_F8; break; + + case VK_F9: evt->key_code = GF_KEY_F9; break; + case VK_F10: evt->key_code = GF_KEY_F10; break; + case VK_F11: evt->key_code = GF_KEY_F11; break; + case VK_F12: evt->key_code = GF_KEY_F12; break; + case VK_F13: evt->key_code = GF_KEY_F13; break; + case VK_F14: evt->key_code = GF_KEY_F14; break; + case VK_F15: evt->key_code = GF_KEY_F15; break; + case VK_F16: evt->key_code = GF_KEY_F16; break; + case VK_F17: evt->key_code = GF_KEY_F17; break; + case VK_F18: evt->key_code = GF_KEY_F18; break; + case VK_F19: evt->key_code = GF_KEY_F19; break; + case VK_F20: evt->key_code = GF_KEY_F20; break; + case VK_F21: evt->key_code = GF_KEY_F21; break; + case VK_F22: evt->key_code = GF_KEY_F22; break; + case VK_F23: evt->key_code = GF_KEY_F23; break; + case VK_F24: evt->key_code = GF_KEY_F24; break; + + case VK_NUMLOCK: evt->key_code = GF_KEY_NUMLOCK; break; + case VK_SCROLL: evt->key_code = GF_KEY_SCROLL; break; + +/* + * VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys. + * Used only as parameters to GetAsyncKeyState() and GetKeyState(). + * No other API or message will distinguish left and right keys in this way. + */ + case VK_LSHIFT: + evt->key_code = GF_KEY_SHIFT; + evt->flags = GF_KEY_EXT_LEFT; + break; + case VK_RSHIFT: + evt->key_code = GF_KEY_SHIFT; + evt->flags = GF_KEY_EXT_RIGHT; + break; + case VK_LCONTROL: + evt->key_code = GF_KEY_CONTROL; + evt->flags = GF_KEY_EXT_LEFT; + break; + case VK_RCONTROL: + evt->key_code = GF_KEY_CONTROL; + evt->flags = GF_KEY_EXT_RIGHT; + break; + case VK_LMENU: + evt->key_code = GF_KEY_ALT; + evt->flags = GF_KEY_EXT_LEFT; + break; + case VK_RMENU: + evt->key_code = GF_KEY_ALT; + evt->flags = GF_KEY_EXT_RIGHT; + break; + +#if(WINVER >= 0x0400) + case VK_PROCESSKEY: evt->key_code = GF_KEY_PROCESS; break; +#endif /* WINVER >= 0x0400 */ + + case VK_ATTN: evt->key_code = GF_KEY_ATTN; break; + case VK_CRSEL: evt->key_code = GF_KEY_CRSEL; break; + case VK_EXSEL: evt->key_code = GF_KEY_EXSEL; break; + case VK_EREOF: evt->key_code = GF_KEY_ERASEEOF; break; + case VK_PLAY: evt->key_code = GF_KEY_PLAY; break; + case VK_ZOOM: evt->key_code = GF_KEY_ZOOM; break; + //case VK_NONAME: evt->key_code = GF_KEY_NONAME; break; + //case VK_PA1: evt->key_code = GF_KEY_PA1; break; + case VK_OEM_CLEAR: evt->key_code = GF_KEY_CLEAR; break; + + /*thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */ + /* VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */ + default: + if ((wParam>=0x30) && (wParam<=0x39)) evt->key_code = GF_KEY_0 + wParam-0x30; + else if ((wParam>=0x41) && (wParam<=0x5A)) evt->key_code = GF_KEY_A + wParam-0x51; + else { + GAPIPriv *ctx = (GAPIPriv *)the_video_driver->opaque; + short res = (LOWORD(wParam) != 0x5b) ? LOWORD(wParam) : wParam; + + if (res==ctx->keys.vkLeft) evt->key_code = is_landscape ? GF_KEY_UP : GF_KEY_LEFT; + else if (res==ctx->keys.vkRight) evt->key_code = is_landscape ? GF_KEY_DOWN : GF_KEY_RIGHT; + else if (res==ctx->keys.vkDown) evt->key_code = is_landscape ? GF_KEY_LEFT : GF_KEY_DOWN; + else if (res==ctx->keys.vkUp) evt->key_code = is_landscape ? GF_KEY_RIGHT : GF_KEY_UP; + else if (res==ctx->keys.vkStart) evt->key_code = GF_KEY_ENTER; + else if (res==ctx->keys.vkA) + evt->key_code = GF_KEY_MEDIAPREVIOUSTRACK; + else if (res==ctx->keys.vkB) + evt->key_code = GF_KEY_MEDIANEXTTRACK; + else if (res==ctx->keys.vkC) + evt->key_code = GF_KEY_SHIFT; + else if (res==0xc1) + evt->key_code = GF_KEY_ALT; + else if (res==0xc2) + evt->key_code = GF_KEY_CONTROL; + else if (res==0xc5) + evt->key_code = GF_KEY_VOLUMEDOWN; + else { + evt->key_code = GF_KEY_UNIDENTIFIED; + } + } + break; + } +} + + + +LRESULT APIENTRY GAPI_WindowProc(HWND hWnd, UINT msg, UINT wParam, LONG lParam) +{ + GF_Event evt; + switch (msg) { + case WM_SIZE: + { + GAPIPriv *ctx = (GAPIPriv *)the_video_driver->opaque; + evt.type = GF_EVENT_SIZE; + evt.size.width = LOWORD(lParam); + evt.size.height = HIWORD(lParam); + the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt); + } + break; + case WM_CLOSE: + evt.type = GF_EVENT_QUIT; + the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt); + return 1; + case WM_DESTROY: + { + GAPIPriv *ctx = (GAPIPriv *)the_video_driver->opaque; + if (ctx->owns_hwnd ) { + PostQuitMessage (0); + } else if (ctx->orig_wnd_proc) { + /*restore window proc*/ + SetWindowLong(ctx->hWnd, GWL_WNDPROC, ctx->orig_wnd_proc); + ctx->orig_wnd_proc = 0L; + } + } + break; + + case WM_ERASEBKGND: + evt.type = GF_EVENT_REFRESH; + the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt); + break; + case WM_PAINT: + { + GAPIPriv *gctx = (GAPIPriv *)the_video_driver->opaque; + if (gctx->gx_mode || !gctx->bitmap) break; + BitBlt(gctx->hdc, gctx->dst_blt.x, gctx->dst_blt.y, gctx->bb_width, gctx->bb_height, gctx->hdcBitmap, 0, 0, SRCCOPY); + } + break; + + + case WM_MOUSEMOVE: + GAPI_GetCoordinates(lParam, &evt); + evt.type = GF_EVENT_MOUSEMOVE; + the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt); + break; + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + GAPI_GetCoordinates(lParam, &evt); + evt.type = GF_EVENT_MOUSEDOWN; + evt.mouse.button = GF_MOUSE_LEFT; + the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt); + break; + case WM_LBUTTONUP: + GAPI_GetCoordinates(lParam, &evt); + evt.type = GF_EVENT_MOUSEUP; + evt.mouse.button = GF_MOUSE_LEFT; + the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt); + break; + + /*FIXME - there's a bug on alt state (we miss one event)*/ + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + w32_translate_key(wParam, lParam, &evt.key); + evt.type = ((msg==WM_SYSKEYDOWN) || (msg==WM_KEYDOWN)) ? GF_EVENT_KEYDOWN : GF_EVENT_KEYUP; + the_video_driver->on_event(the_video_driver->evt_cbk_hdl, &evt); + break; + case WM_CHAR: + evt.type = GF_EVENT_TEXTINPUT; + evt.character.unicode_char = wParam; + break; + } + return DefWindowProc (hWnd, msg, wParam, lParam); +} + +void GAPI_WindowThread(void *par) +{ + MSG msg; + WNDCLASS wc; + GAPIPriv *ctx = (GAPIPriv *)the_video_driver->opaque; + + memset(&wc, 0, sizeof(WNDCLASS)); + wc.hInstance = GetModuleHandle(_T("gm_gapi.dll")); + wc.lpfnWndProc = GAPI_WindowProc; + wc.hCursor = LoadCursor (NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject (BLACK_BRUSH); + wc.lpszClassName = _T("GPAC GAPI Output"); + RegisterClass (&wc); + + ctx->hWnd = CreateWindow(_T("GPAC GAPI Output"), NULL, WS_POPUP, 0, 0, 120, 100, NULL, NULL, wc.hInstance, NULL); + if (ctx->hWnd == NULL) { + ctx->ThreadID = 0; + ExitThread(1); + } + ShowWindow(ctx->hWnd, SW_SHOWNORMAL); + + while (GetMessage (&(msg), NULL, 0, 0)) { + TranslateMessage (&(msg)); + DispatchMessage (&(msg)); + } + ctx->ThreadID = 0; + ExitThread (0); +} + + +void GAPI_SetupWindow(GF_VideoOutput *dr) +{ + GF_Err e; + GAPIPriv *ctx = (GAPIPriv *)dr->opaque; + if (the_video_driver) return; + the_video_driver = dr; + + if (!ctx->hWnd) { + ctx->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) GAPI_WindowThread, (LPVOID) dr, 0, &(ctx->ThreadID) ); + while (!ctx->hWnd && ctx->hThread) gf_sleep(10); + if (!ctx->hThread) return; + ctx->owns_hwnd = 1; + } else { + ctx->orig_wnd_proc = GetWindowLong(ctx->hWnd, GWL_WNDPROC); + /*override window proc*/ + SetWindowLong(ctx->hWnd, GWL_WNDPROC, (DWORD) GAPI_WindowProc); + } + +#ifdef GPAC_USE_OGL_ES + ctx->use_pbuffer = 1; + dr->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA; + e = GAPI_SetupOGL_ES_Offscreen(dr, 20, 20); + if (e!=GF_OK) { + dr->hw_caps &= ~GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA; +#ifndef GLES_NO_PIXMAP + e = GAPI_SetupOGL_ES_Offscreen(dr, 20, 20); +#endif + } + if (!e) { + dr->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN; + return; + } + ctx->use_pbuffer = 0; + + WNDCLASS wc; + HINSTANCE hInst; + hInst = GetModuleHandle(_T("gm_gapi.dll") ); + memset(&wc, 0, sizeof(WNDCLASS)); + wc.hInstance = hInst; + wc.lpfnWndProc = GAPI_WindowProc; + wc.lpszClassName = _T("GPAC GAPI Offscreen"); + RegisterClass (&wc); + + ctx->gl_hwnd = CreateWindow(_T("GPAC GAPI Offscreen"), _T("GPAC GAPI Offscreen"), WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL); + if (!ctx->gl_hwnd) return; + ShowWindow(ctx->gl_hwnd, SW_HIDE); + + dr->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA; + e = GAPI_SetupOGL_ES_Offscreen(dr, 20, 20); + + if (e!=GF_OK) { + dr->hw_caps &= ~GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA; + e = GAPI_SetupOGL_ES_Offscreen(dr, 20, 20); + } + if (e==GF_OK) dr->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN; +#endif + +} + +void GAPI_ShutdownWindow(GF_VideoOutput *dr) +{ + GAPIPriv *ctx = (GAPIPriv *)dr->opaque; + + if (ctx->owns_hwnd) { + PostMessage(ctx->hWnd, WM_DESTROY, 0, 0); + while (ctx->ThreadID) gf_sleep(10); + UnregisterClass(_T("GPAC GAPI Output"), GetModuleHandle(_T("gapi.dll"))); + CloseHandle(ctx->hThread); + ctx->hThread = NULL; + } else if (ctx->orig_wnd_proc) { + /*restore window proc*/ + SetWindowLong(ctx->hWnd, GWL_WNDPROC, ctx->orig_wnd_proc); + ctx->orig_wnd_proc = 0L; + } + ctx->hWnd = NULL; +#ifdef GPAC_USE_OGL_ES + PostMessage(ctx->gl_hwnd, WM_DESTROY, 0, 0); + ctx->gl_hwnd = NULL; + UnregisterClass(_T("GPAC GAPI Offscreen"), GetModuleHandle(_T("gm_gapi.dll") )); +#endif + + the_video_driver = NULL; +} + + +GF_Err GAPI_Clear(GF_VideoOutput *dr, u32 color) +{ + GAPICTX(dr); + gctx->erase_dest = 1; + return GF_OK; +} + +static void createPixmap(GAPIPriv *ctx, u32 pix_type) +{ + const size_t bmiSize = sizeof(BITMAPINFO) + 256U*sizeof(RGBQUAD); + BITMAPINFO* bmi; + DWORD* p; + bmi = (BITMAPINFO*)malloc(bmiSize); + memset(bmi, 0, bmiSize); + + bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi->bmiHeader.biWidth = ctx->bb_width; + bmi->bmiHeader.biHeight = -1 * (s32) ctx->bb_height; /*top-down image*/ + bmi->bmiHeader.biPlanes = (short)1; + bmi->bmiHeader.biBitCount = (unsigned int) ctx->bits_per_pixel; + bmi->bmiHeader.biCompression = BI_BITFIELDS; + bmi->bmiHeader.biClrUsed = 3; + + p = (DWORD*)bmi->bmiColors; + switch (ctx->pixel_format) { + case GF_PIXEL_RGB_555: + p[0] = 0x00007c00; p[1] = 0x000003e0; p[2] = 0x0000001f; + break; + case GF_PIXEL_RGB_565: + p[0] = 0x0000f800; p[1] = 0x000007e0; p[2] = 0x0000001f; + break; + case GF_PIXEL_RGB_24: + p[0] = 0x00ff0000; p[1] = 0x0000ff00; p[2] = 0x000000ff; + break; + } + + ctx->hdc = GetDC(ctx->hWnd); + + if (pix_type==2) { +#ifdef GPAC_USE_OGL_ES + ctx->gl_bitmap = CreateDIBSection(ctx->hdc, bmi, DIB_RGB_COLORS, (void **) &ctx->gl_bits, NULL, 0); +#endif + } else if (pix_type==1) { + ctx->hdcBitmap = CreateCompatibleDC(ctx->hdc); + ctx->bitmap = CreateDIBSection(ctx->hdc, bmi, DIB_RGB_COLORS, (void **) &ctx->bits, NULL, 0); + ctx->old_bitmap = (HBITMAP) SelectObject(ctx->hdcBitmap, ctx->bitmap); + } else { + ctx->hdcBitmap = CreateCompatibleDC(ctx->hdc); + ctx->bitmap = CreateDIBSection(ctx->hdc, bmi, DIB_RGB_COLORS, (void **) &ctx->backbuffer, NULL, 0); + ctx->old_bitmap = (HBITMAP) SelectObject(ctx->hdcBitmap, ctx->bitmap); + /*watchout - win32 always create DWORD align memory, so align our pitch*/ + while ((ctx->bb_pitch % 4) != 0) ctx->bb_pitch ++; + } + free(bmi); +} + + +#ifdef GPAC_USE_OGL_ES + +void GAPI_ReleaseOGL_ES(GAPIPriv *ctx, Bool offscreen_only) +{ + if (ctx->egldpy) { + eglMakeCurrent(ctx->egldpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (ctx->eglctx) eglDestroyContext(ctx->egldpy, ctx->eglctx); + ctx->eglctx = 0; + if (ctx->surface) eglDestroySurface(ctx->egldpy, ctx->surface); + ctx->surface = 0; + if (ctx->egldpy) eglTerminate(ctx->egldpy); + ctx->egldpy = 0; + } + if (ctx->gl_bitmap) DeleteObject(ctx->gl_bitmap); + ctx->gl_bitmap = NULL; + + if (offscreen_only) return; + + if (ctx->bitmap) DeleteObject(ctx->bitmap); + ctx->bitmap = NULL; +} + +GF_Err GAPI_SetupOGL_ES(GF_VideoOutput *dr) +{ + EGLint n, maj, min; + u32 i; + GF_Event evt; + GAPICTX(dr) + static int atts[32]; + const char *opt; + + i=0; + atts[i++] = EGL_RED_SIZE; atts[i++] = (gctx->pixel_format==GF_PIXEL_RGB_24) ? 8 : 5; + atts[i++] = EGL_GREEN_SIZE; atts[i++] = (gctx->pixel_format==GF_PIXEL_RGB_24) ? 8 : (gctx->pixel_format==GF_PIXEL_RGB_565) ? 6 : 5; + atts[i++] = EGL_BLUE_SIZE; atts[i++] = (gctx->pixel_format==GF_PIXEL_RGB_24) ? 8 : 5; + opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsDepth"); + atts[i++] = EGL_DEPTH_SIZE; atts[i++] = opt ? atoi(opt) : 16; + atts[i++] = EGL_SURFACE_TYPE; + +#ifdef GLES_NO_PIXMAP + atts[i++] = EGL_WINDOW_BIT; +#else + atts[i++] = EGL_PIXMAP_BIT; +// atts[i++] = gctx->fullscreen ? EGL_WINDOW_BIT : EGL_PIXMAP_BIT; +#endif + atts[i++] = EGL_ALPHA_SIZE; atts[i++] = EGL_DONT_CARE; + atts[i++] = EGL_STENCIL_SIZE; atts[i++] = EGL_DONT_CARE; + atts[i++] = EGL_NONE; + + /*whenever window is resized we must reinit OGL-ES*/ + GAPI_ReleaseOGL_ES(gctx, 0); + + if (!gctx->fullscreen) { + RECT rc; + ::GetClientRect(gctx->hWnd, &rc); + gctx->bb_width = rc.right-rc.left; + gctx->bb_height = rc.bottom-rc.top; + +#ifndef GLES_NO_PIXMAP + createPixmap(gctx, 1); +#endif + } + + gctx->egldpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!gctx->egldpy) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot get OpenGL display\n")); + return GF_IO_ERR; + } + if (!eglInitialize(gctx->egldpy, &maj, &min)) { + gctx->egldpy = NULL; + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot initialize OpenGL layer\n")); + return GF_IO_ERR; + } + + if (!eglChooseConfig(gctx->egldpy, atts, &gctx->eglconfig, 1, &n) || (eglGetError() != EGL_SUCCESS)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot choose OpenGL config\n")); + return GF_IO_ERR; + } + + if (gctx->fullscreen +#ifdef GLES_NO_PIXMAP + || 1 +#endif + ) { + gctx->surface = eglCreateWindowSurface(gctx->egldpy, gctx->eglconfig, gctx->hWnd, 0); + } else { + gctx->surface = eglCreatePixmapSurface(gctx->egldpy, gctx->eglconfig, gctx->bitmap, 0); + } + + if (!gctx->surface) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot create OpenGL surface - error %d\n", eglGetError())); + return GF_IO_ERR; + } + gctx->eglctx = eglCreateContext(gctx->egldpy, gctx->eglconfig, NULL, NULL); + if (!gctx->eglctx) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot create OpenGL context\n")); + eglDestroySurface(gctx->egldpy, gctx->surface); + gctx->surface = 0L; + return GF_IO_ERR; + } + if (!eglMakeCurrent(gctx->egldpy, gctx->surface, gctx->surface, gctx->eglctx)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GAPI] Cannot bind OpenGL context\n")); + eglDestroyContext(gctx->egldpy, gctx->eglctx); + gctx->eglctx = 0L; + eglDestroySurface(gctx->egldpy, gctx->surface); + gctx->surface = 0L; + return GF_IO_ERR; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[GAPI] OpenGL initialize - %d x %d \n", gctx->bb_width, gctx->bb_height)); + evt.type = GF_EVENT_VIDEO_SETUP; + dr->on_event(dr->evt_cbk_hdl, &evt); + return GF_OK; +} + + + +GF_Err GAPI_SetupOGL_ES_Offscreen(GF_VideoOutput *dr, u32 width, u32 height) +{ + int atts[15]; + const char *opt; + EGLint n, maj, min; + + GAPICTX(dr) + + GAPI_ReleaseOGL_ES(gctx, 1); + + if (!gctx->use_pbuffer) { + SetWindowPos(gctx->gl_hwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE); + createPixmap(gctx, 2); + } + + gctx->egldpy = eglGetDisplay(/*gctx->dpy*/EGL_DEFAULT_DISPLAY); + if (!eglInitialize(gctx->egldpy, &maj, &min)) { + gctx->egldpy = NULL; + return GF_IO_ERR; + } + atts[0] = EGL_RED_SIZE; atts[1] = 8; + atts[2] = EGL_GREEN_SIZE; atts[3] = 8; + atts[4] = EGL_BLUE_SIZE; atts[5] = 8; + atts[6] = EGL_ALPHA_SIZE; atts[7] = (dr->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA) ? 8 : EGL_DONT_CARE; + opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsDepth"); + atts[8] = EGL_DEPTH_SIZE; atts[9] = opt ? atoi(opt) : 16; + + atts[10] = EGL_STENCIL_SIZE; atts[11] = EGL_DONT_CARE; + atts[12] = EGL_SURFACE_TYPE; atts[13] = gctx->use_pbuffer ? EGL_PBUFFER_BIT : EGL_PIXMAP_BIT; + atts[14] = EGL_NONE; + + eglGetConfigs(gctx->egldpy, NULL, 0, &n); + if (!eglChooseConfig(gctx->egldpy, atts, &gctx->eglconfig, 1, &n)) { + return GF_IO_ERR; + } + + if (!gctx->use_pbuffer) { + gctx->surface = eglCreatePixmapSurface(gctx->egldpy, gctx->eglconfig, gctx->gl_bitmap, 0); + } else { + atts[0] = EGL_WIDTH; atts[1] = width; + atts[2] = EGL_HEIGHT; atts[3] = height; + atts[4] = EGL_NONE; + + gctx->surface = eglCreatePbufferSurface(gctx->egldpy, gctx->eglconfig, atts); + } + + if (!gctx->surface) { + return GF_IO_ERR; + } + gctx->eglctx = eglCreateContext(gctx->egldpy, gctx->eglconfig, NULL, NULL); + if (!gctx->eglctx) { + eglDestroySurface(gctx->egldpy, gctx->surface); + gctx->surface = 0L; + return GF_IO_ERR; + } + if (!eglMakeCurrent(gctx->egldpy, gctx->surface, gctx->surface, gctx->eglctx)) { + eglDestroyContext(gctx->egldpy, gctx->eglctx); + gctx->eglctx = 0L; + eglDestroySurface(gctx->egldpy, gctx->surface); + gctx->surface = 0L; + return GF_IO_ERR; + } + return GF_OK; +} + +#endif + + +void GAPI_ReleaseObjects(GAPIPriv *ctx) +{ +#ifdef GPAC_USE_OGL_ES + if (ctx->output_3d_type) GAPI_ReleaseOGL_ES(ctx, 0); + else +#endif + if (ctx->bitmap) DeleteObject(ctx->bitmap); + else if (ctx->backbuffer) free(ctx->backbuffer); + ctx->backbuffer = NULL; + ctx->bitmap = NULL; + + if (ctx->hdcBitmap) { + if (ctx->old_bitmap) SelectObject(ctx->hdcBitmap, ctx->old_bitmap); + ctx->old_bitmap = NULL; + DeleteDC(ctx->hdcBitmap); + ctx->hdcBitmap = NULL; + } + if (ctx->hdc) ReleaseDC(ctx->hWnd, ctx->hdc); + ctx->hdc = NULL; +} + +GF_Err GAPI_Setup(GF_VideoOutput *dr, void *os_handle, void *os_display, Bool noover) +{ + struct GXDisplayProperties gx = GXGetDisplayProperties(); + RECT rc; + GAPICTX(dr); + gctx->hWnd = (HWND) os_handle; + + /*get keys in both 2D and 3D modes*/ + gctx->keys = GXGetDefaultKeys(GX_NORMALKEYS); + +#if 0 + /*FIXME - not supported in rasterizer*/ + if (gx.ffFormat & kfDirect444) { + gctx->pixel_format = GF_PIXEL_RGB_444; + gctx->BPP = 2; + gctx->bitsPP = 12; + } + else +#endif + if (gx.ffFormat & kfDirect555) { + gctx->pixel_format = GF_PIXEL_RGB_555; + gctx->BPP = 2; + gctx->bits_per_pixel = 15; + } + else if (gx.ffFormat & kfDirect565) { + gctx->pixel_format = GF_PIXEL_RGB_565; + gctx->BPP = 2; + gctx->bits_per_pixel = 16; + } + else if (gx.ffFormat & kfDirect888) { + gctx->pixel_format = GF_PIXEL_RGB_24; + gctx->BPP = 3; + gctx->bits_per_pixel = 24; + } else { + return GF_NOT_SUPPORTED; + } + dr->max_screen_width = gctx->screen_w = gx.cxWidth; + dr->max_screen_height = gctx->screen_h = gx.cyHeight; + is_landscape = (gx.ffFormat & kfLandscape) ? 1 : 0; + gctx->x_pitch = gx.cbxPitch; + gctx->y_pitch = gx.cbyPitch; + + GAPI_SetupWindow(dr); + if (!gctx->hWnd) return GF_IO_ERR; + + /*setup GX*/ + if (!GXOpenDisplay(gctx->hWnd, 0L)) { + MessageBox(NULL, _T("Cannot open display"), _T("GAPI Error"), MB_OK); + return GF_IO_ERR; + } + GetClientRect(gctx->hWnd, &rc); + gctx->backup_w = rc.right - rc.left; + gctx->backup_h = rc.bottom - rc.top; + return GAPI_InitBackBuffer(dr, gctx->backup_w, gctx->backup_h); +} + +static void GAPI_Shutdown(GF_VideoOutput *dr) +{ + GAPICTX(dr); + + gf_mx_p(gctx->mx); + GAPI_ReleaseObjects(gctx); + + GXCloseDisplay(); + GAPI_ShutdownWindow(dr); + gf_mx_v(gctx->mx); +} + +static GF_Err GAPI_SetFullScreen(GF_VideoOutput *dr, Bool bOn, u32 *outWidth, u32 *outHeight) +{ + Bool is_wide_scene = (bOn==2) ? 1 : 0; + GF_Err e; + GAPICTX(dr); + + if (!gctx) return GF_BAD_PARAM; + if (is_wide_scene) bOn = 1; + if (bOn == gctx->fullscreen) return GF_OK; + +#ifdef GPAC_USE_OGL_ES + if (gctx->output_3d_type==1) { + gctx->fullscreen = bOn; + return GAPI_SetupOGL_ES(dr); + } +#endif + + gf_mx_p(gctx->mx); + GAPI_ReleaseObjects(gctx); + GXCloseDisplay(); + e = GF_OK; + if (bOn) { + if (!GXOpenDisplay(GetParent(gctx->hWnd), GX_FULLSCREEN)) { + GXOpenDisplay(gctx->hWnd, 0L); + gctx->fullscreen = 0; + e = GF_IO_ERR; + } else { + gctx->fullscreen = 1; + } + } else { + GXOpenDisplay(gctx->hWnd, 0L); + gctx->fullscreen = 0; + } + + is_landscape = 0; + if (!e) { + if (gctx->fullscreen) { + gctx->backup_w = *outWidth; + gctx->backup_h = *outHeight; + + if (is_wide_scene && (gctx->screen_w > gctx->screen_h)) is_landscape = 0; + else if (!is_wide_scene && (gctx->screen_w < gctx->screen_h)) is_landscape = 0; + else is_landscape = 1; + + if (is_landscape) { + gctx->fs_w = gctx->screen_h; + gctx->fs_h = gctx->screen_w; + } else { + gctx->fs_w = gctx->screen_w; + gctx->fs_h = gctx->screen_h; + } + *outWidth = gctx->fs_w; + *outHeight = gctx->fs_h; + } else { + *outWidth = gctx->backup_w; + *outHeight = gctx->backup_h; + } + e = GAPI_InitBackBuffer(dr, *outWidth, *outHeight); + } + gf_mx_v(gctx->mx); + + return e; +} + +GF_Err GAPI_ClearFS(GAPIPriv *gctx, unsigned char *ptr, s32 x_pitch, s32 y_pitch) +{ + s32 i, j; + gf_mx_p(gctx->mx); + if (gctx->BPP==3) { + for (i=0; i< (s32)gctx->fs_h; i++) { + unsigned char *_ptr = ptr + i*y_pitch; + for (j=0; j<(s32)gctx->fs_w; j++) { + _ptr[0] = _ptr[1] = _ptr[2] = 0; + _ptr += x_pitch; + } + } + } else { + for (i=0; i<(s32)gctx->fs_h; i++) { + unsigned char *_ptr = ptr + i*y_pitch; + for (j=0; j<(s32)gctx->fs_w; j++) { + * ((unsigned short *)_ptr) = 0; + _ptr += x_pitch; + } + } + } + gf_mx_v(gctx->mx); + return GF_OK; +} + + +static GF_Err GAPI_FlipBackBuffer(GF_VideoOutput *dr) +{ + GF_VideoSurface src, dst; + unsigned char *ptr; + GAPICTX(dr); + s32 pitch_y = gctx->y_pitch; + s32 pitch_x = gctx->x_pitch; + if (!gctx || !gctx->gx_mode) return GF_BAD_PARAM; + + gf_mx_p(gctx->mx); + + /*get a pointer to video memory*/ + ptr = (unsigned char *) GXBeginDraw(); + if (!ptr) { + gf_mx_v(gctx->mx); + return GF_IO_ERR; + } + + src.video_buffer = gctx->backbuffer; + src.width = gctx->bb_width; + src.height = gctx->bb_height; + src.pitch = gctx->bb_pitch; + src.pixel_format = gctx->pixel_format; + src.is_hardware_memory = 0; + + dst.width = gctx->dst_blt.w; + dst.height = gctx->dst_blt.h; + dst.pixel_format = gctx->pixel_format; + dst.is_hardware_memory = 1; + + + if (gctx->fullscreen) { + if (is_landscape) { + if (gctx->y_pitch>0) { + pitch_x = -gctx->y_pitch; + /*start of frame-buffer is top-left corner*/ + if (gctx->x_pitch>0) { + ptr += gctx->screen_h * gctx->y_pitch; + pitch_y = gctx->x_pitch; + } + /*start of frame-buffer is top-right corner*/ + else { + ptr += gctx->screen_h * gctx->y_pitch + gctx->screen_w * gctx->x_pitch; + pitch_y = -gctx->x_pitch; + } + } else { + pitch_x = gctx->y_pitch; + /*start of frame-buffer is bottom-left corner*/ + if (gctx->x_pitch>0) { + pitch_y = gctx->x_pitch; + } + /*start of frame-buffer is bottom-right corner*/ + else { + ptr += gctx->screen_w * gctx->x_pitch; + pitch_y = gctx->x_pitch; + } + } + } + if (gctx->erase_dest) { + gctx->erase_dest = 0; + GAPI_ClearFS(gctx, ptr, pitch_x, pitch_y); + } + } else { + gctx->dst_blt.x += gctx->off_x; + gctx->dst_blt.y += gctx->off_y; + } + + ptr += gctx->dst_blt.x * pitch_x + pitch_y * gctx->dst_blt.y; + dst.video_buffer = (char*)ptr; + dst.pitch = pitch_y; + + gf_stretch_bits(&dst, &src, NULL, NULL, pitch_x, 0xFF, 0, NULL, NULL); + + GXEndDraw(); + gf_mx_v(gctx->mx); + return GF_OK; +} + + + +static GF_Err GAPI_Flush(GF_VideoOutput *dr, GF_Window *dest) +{ + GF_Err e; + GAPICTX(dr); + + if (!gctx) return GF_BAD_PARAM; + + gf_mx_p(gctx->mx); + +#ifdef GPAC_USE_OGL_ES + if (gctx->output_3d_type==1) { +#ifndef GLES_NO_PIXMAP + if (gctx->fullscreen && gctx->surface && gctx->egldpy) { +#endif + if (gctx->erase_dest) { + InvalidateRect(gctx->hWnd, NULL, TRUE); + gctx->erase_dest = 0; + } + eglSwapBuffers(gctx->egldpy, gctx->surface); +#ifndef GLES_NO_PIXMAP + } else { + InvalidateRect(gctx->hWnd, NULL, gctx->erase_dest); + gctx->erase_dest = 0; + } +#endif + gf_mx_v(gctx->mx); + return GF_OK; + } +#endif + e = GF_OK; + if (gctx->backbuffer) { + if (dest) { + gctx->dst_blt = *dest; + } else { + assert(0); + gctx->dst_blt.x = gctx->dst_blt.y = 0; + gctx->dst_blt.w = gctx->bb_width; + gctx->dst_blt.h = gctx->bb_height; + } + if (gctx->gx_mode) { + if (!gctx->fullscreen && gctx->erase_dest) { + InvalidateRect(gctx->hWnd, NULL, TRUE); + gctx->erase_dest = 0; + } + e = GAPI_FlipBackBuffer(dr); + } else { + InvalidateRect(gctx->hWnd, NULL, gctx->erase_dest); + gctx->erase_dest = 0; + } + } + gf_mx_v(gctx->mx); + return e; +} + + +static GF_Err GAPI_ProcessEvent(GF_VideoOutput *dr, GF_Event *evt) +{ + GAPICTX(dr); + if (!evt) return GF_OK; + switch (evt->type) { + case GF_EVENT_SHOWHIDE: + if (gctx->hWnd) ShowWindow(gctx->hWnd, evt->show.show_type ? SW_SHOW : SW_HIDE); + break; + case GF_EVENT_SIZE: + /*nothing to do since we don't own the window*/ + break; + case GF_EVENT_VIDEO_SETUP: + switch (evt->setup.opengl_mode) { + case 0: +#ifdef GPAC_USE_OGL_ES + gctx->output_3d_type = 0; +#endif + return GAPI_InitBackBuffer(dr, evt->setup.width, evt->setup.height); +#ifdef GPAC_USE_OGL_ES + case 1: + gctx->output_3d_type = 1; + return GAPI_SetupOGL_ES(the_video_driver); + case 2: + gctx->output_3d_type = 2; + return GAPI_SetupOGL_ES_Offscreen(the_video_driver, evt->setup.width, evt->setup.height); +#else + default: + return GF_NOT_SUPPORTED; +#endif + } + } + return GF_OK; +} + + +static GF_Err GAPI_InitBackBuffer(GF_VideoOutput *dr, u32 VideoWidth, u32 VideoHeight) +{ + GAPICTX(dr); + + if (!gctx || !VideoWidth || !VideoHeight) return GF_BAD_PARAM; + + gf_mx_p(gctx->mx); + + GAPI_ReleaseObjects(gctx); + + gctx->bb_size = VideoWidth * VideoHeight * gctx->BPP; + gctx->bb_width = VideoWidth; + gctx->bb_height = VideoHeight; + gctx->bb_pitch = VideoWidth * gctx->BPP; + + if (gctx->force_gx || gctx->fullscreen) { + gctx->backbuffer = (char *) malloc(sizeof(unsigned char) * gctx->bb_size); + gctx->gx_mode = 1; + } else { + createPixmap(gctx, 0); + gctx->gx_mode = 0; + } + RECT rc; + GetWindowRect(gctx->hWnd, &rc); + gctx->off_x = rc.left; + gctx->off_y = rc.top; + gctx->erase_dest = 1; + + + gf_mx_v(gctx->mx); + return GF_OK; +} +static GF_Err GAPI_LockBackBuffer(GF_VideoOutput *dr, GF_VideoSurface *vi, Bool do_lock) +{ + GAPICTX(dr); + + if (do_lock) { + if (!vi) return GF_BAD_PARAM; + vi->width = gctx->bb_width; + vi->height = gctx->bb_height; + vi->pitch = gctx->bb_pitch; + vi->pixel_format = gctx->pixel_format; + vi->video_buffer = gctx->backbuffer; + vi->is_hardware_memory = 0; + } + return GF_OK; +} + + +static void *NewGAPIVideoOutput() +{ + GAPIPriv *priv; + GF_VideoOutput *driv = (GF_VideoOutput *) malloc(sizeof(GF_VideoOutput)); + memset(driv, 0, sizeof(GF_VideoOutput)); + GF_REGISTER_MODULE_INTERFACE(driv, GF_VIDEO_OUTPUT_INTERFACE, "GAPI Video Output", "gpac distribution") + + priv = (GAPIPriv *) malloc(sizeof(GAPIPriv)); + memset(priv, 0, sizeof(GAPIPriv)); + priv->mx = gf_mx_new("GAPI"); + driv->opaque = priv; + priv->force_gx = 0; + + /*alpha and keying to do*/ + driv->hw_caps = GF_VIDEO_HW_CAN_ROTATE; +#ifdef GPAC_USE_OGL_ES + driv->hw_caps = GF_VIDEO_HW_OPENGL; +#endif + + driv->Setup = GAPI_Setup; + driv->Shutdown = GAPI_Shutdown; + driv->Flush = GAPI_Flush; + driv->ProcessEvent = GAPI_ProcessEvent; + driv->Blit = NULL; + driv->LockBackBuffer = GAPI_LockBackBuffer; + driv->SetFullScreen = GAPI_SetFullScreen; + return (void *)driv; +} + +static void DeleteVideoOutput(void *ifce) +{ + GF_VideoOutput *driv = (GF_VideoOutput *) ifce; + GAPICTX(driv); + GAPI_Shutdown(driv); + gf_mx_del(gctx->mx); + free(gctx); + free(driv); +} + +/*interface query*/ +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return 1; + return 0; +} +/*interface create*/ +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return (GF_BaseInterface *) NewGAPIVideoOutput(); + return NULL; +} +/*interface destroy*/ +void ShutdownInterface(GF_BaseInterface *ifce) +{ + GF_VideoOutput *dd = (GF_VideoOutput *)ifce; + switch (dd->InterfaceType) { + case GF_VIDEO_OUTPUT_INTERFACE: + DeleteVideoOutput(dd); + break; + } +} + diff --git a/modules/gapi/gapi.def b/modules/gapi/gapi.def new file mode 100644 index 0000000..5cd98c1 --- /dev/null +++ b/modules/gapi/gapi.def @@ -0,0 +1,6 @@ +LIBRARY gm_gapi.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/gapi/gapi.h b/modules/gapi/gapi.h new file mode 100644 index 0000000..0cd2c36 --- /dev/null +++ b/modules/gapi/gapi.h @@ -0,0 +1,102 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / DirectX audio and video render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GAPI_H +#define _GAPI_H + +#include <gpac/list.h> +#include <gpac/thread.h> +/*driver interface*/ +#include <gpac/modules/video_out.h> + +#ifdef GPAC_USE_OGL_ES +#include "GLES/egl.h" +#endif + +/*driver interface*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + HWND hWnd; + DWORD orig_wnd_proc; + GF_Mutex *mx; + + GXKeyList keys; + + u32 screen_w, screen_h; + u32 fs_w, fs_h; + /*store w and h for fullscreen*/ + u32 backup_w, backup_h; + s32 x_pitch, y_pitch; + Bool fullscreen; + Bool force_gx; + Bool gx_mode; + + /*main surface info*/ + char *backbuffer; + u32 bb_size, bb_width, bb_height, bb_pitch; + u32 pixel_format; + u32 BPP, bits_per_pixel; + + GF_Window dst_blt; + DWORD ThreadID; + HANDLE hThread; + Bool owns_hwnd; + + Bool erase_dest; + u32 off_x, off_y; + + HBITMAP bitmap, old_bitmap; + DWORD * bits; + HDC hdcBitmap, hdc; + + +#ifdef GPAC_USE_OGL_ES + u32 output_3d_type; + EGLDisplay egldpy; + EGLSurface surface; + EGLConfig eglconfig; + EGLContext eglctx; + + HBITMAP gl_bitmap; + DWORD *gl_bits; + HWND gl_hwnd; + Bool use_pbuffer; +#endif + +} GAPIPriv; + +GF_Err GAPI_SetupOGL_ES_Offscreen(GF_VideoOutput *dr, u32 width, u32 height) ; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/gdip_raster/gdip_font.cpp b/modules/gdip_raster/gdip_font.cpp new file mode 100644 index 0000000..e53a350 --- /dev/null +++ b/modules/gdip_raster/gdip_font.cpp @@ -0,0 +1,393 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / GDIplus rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "gdip_priv.h" +#include <gpac/utf.h> + + + +#ifndef GDIP_MAX_STRING_SIZE +#define GDIP_MAX_STRING_SIZE 5000 +#endif + + +GF_Err gdip_init_font_engine(GF_FontReader *dr) +{ + const char *sOpt; + FontPriv *ctx = (FontPriv *)dr->udta; + + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontSerif"); + strcpy(ctx->font_serif, sOpt ? sOpt : "Times New Roman"); + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontSans"); + strcpy(ctx->font_sans, sOpt ? sOpt : "Arial"); + sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", "FontFixed"); + strcpy(ctx->font_fixed, sOpt ? sOpt : "Courier New"); + + return GF_OK; +} +GF_Err gdip_shutdown_font_engine(GF_FontReader *dr) +{ + FontPriv *ctx = (FontPriv *)dr->udta; + + if (ctx->font) GdipDeleteFontFamily(ctx->font); + ctx->font = NULL; + + /*nothing to do*/ + return GF_OK; +} + + + +static GF_Err gdip_get_glyphs(GF_FontReader *dr, const char *utf_string, u32 *glyph_buffer, u32 *io_glyph_buffer_size, const char *xml_lang, Bool *is_rtl) +{ + u32 len; + u32 i; + u16 *conv; + char *utf8 = (char*) utf_string; + FontPriv *priv = (FontPriv*)dr->udta; + + len = utf_string ? strlen(utf_string) : 0; + if (!len) { + *io_glyph_buffer_size = 0; + return GF_OK; + } + if (*io_glyph_buffer_size < len+1) { + *io_glyph_buffer_size = len+1; + return GF_BUFFER_TOO_SMALL; + } + len = gf_utf8_mbstowcs((u16*) glyph_buffer, *io_glyph_buffer_size, (const char **) &utf8); + if ((s32) len<0) return GF_IO_ERR; + if (utf8) return GF_IO_ERR; + + /*perform bidi relayout*/ + conv = (u16*) glyph_buffer; + *is_rtl = gf_utf8_reorder_bidi(conv, len); + /*move 16bit buffer to 32bit*/ + for (i=len; i>0; i--) { + glyph_buffer[i-1] = (u32) conv[i-1]; + } + *io_glyph_buffer_size = (u32) len; + return GF_OK; +} + + +static void adjust_white_space(const unsigned short *string, Float *width, Float whiteSpaceWidth) +{ + u32 len , i=0; + while (string[i] == (unsigned short) ' ') { + *width += whiteSpaceWidth; + i++; + } + if (whiteSpaceWidth<0) return; + len = gf_utf8_wcslen(string); + if (i != len) { + i = len - 1; + while (string[i] == (unsigned short) ' ') { + *width += whiteSpaceWidth; + i--; + } + } +} + +static GF_Err gdip_get_text_size(GF_FontReader *dr, const unsigned short *string, Fixed *width, Fixed *height) +{ + GpPath *path_tmp; + GpStringFormat *fmt; + FontPriv *ctx = (FontPriv *)dr->udta; + *width = *height = 0; + if (!ctx->font) return GF_BAD_PARAM; + + GdipCreateStringFormat(StringFormatFlagsNoWrap, LANG_NEUTRAL, &fmt); + GdipCreatePath(FillModeAlternate, &path_tmp); + RectF rc; + rc.X = rc.Y = 0; + rc.Width = rc.Height = 0; + GdipAddPathString(path_tmp, (const WCHAR *)string, -1, ctx->font, ctx->font_style, ctx->em_size, &rc, fmt); + + GdipGetPathWorldBounds(path_tmp, &rc, NULL, NULL); + + adjust_white_space(string, &rc.Width, ctx->whitespace_width); + *width = FLT2FIX(rc.Width); + *height = FLT2FIX(rc.Height); + + GdipDeleteStringFormat(fmt); + GdipDeletePath(path_tmp); + + return GF_OK; +} + +static GF_Err gdip_set_font(GF_FontReader *dr, const char *fontName, u32 styles) +{ + WCHAR wcFontName[GDIP_MAX_STRING_SIZE]; + FontPriv *ctx = (FontPriv *)dr->udta; + + if (ctx->font) GdipDeleteFontFamily(ctx->font); + ctx->font = NULL; + + if (fontName && strlen(fontName) >= GDIP_MAX_STRING_SIZE) fontName = NULL; + + if (!fontName || !strlen(fontName) ) fontName = ctx->font_serif; + else if (!stricmp(fontName, "SANS") || !stricmp(fontName, "sans-serif")) fontName = ctx->font_sans; + else if (!stricmp(fontName, "SERIF")) fontName = ctx->font_serif; + else if (!stricmp(fontName, "TYPEWRITER") || !stricmp(fontName, "monospace")) fontName = ctx->font_fixed; + + MultiByteToWideChar(CP_ACP, 0, fontName, strlen(fontName)+1, + wcFontName, sizeof(wcFontName)/sizeof(wcFontName[0]) ); + + + GdipCreateFontFamilyFromName(wcFontName, NULL, &ctx->font); + if (!ctx->font) return GF_NOT_SUPPORTED; + + //setup styles + ctx->font_style = 0; + if (styles & GF_FONT_WEIGHT_BOLD ) ctx->font_style |= FontStyleBold; + if (styles & GF_FONT_ITALIC) ctx->font_style |= FontStyleItalic; + + if (styles & GF_FONT_UNDERLINED) ctx->font_style |= FontStyleUnderline; + if (styles & GF_FONT_STRIKEOUT) ctx->font_style |= FontStyleStrikeout; + return GF_OK; +} + +static GF_Err gdip_get_font_info(GF_FontReader *dr, char **font_name, s32 *em_size, s32 *ascent, s32 *descent, s32 *underline, s32 *line_spacing, s32 *max_advance_h, s32 *max_advance_v) +{ + UINT16 val, em; + FontPriv *ctx = (FontPriv *)dr->udta; + + *font_name = NULL; + *em_size = *ascent = *descent = *line_spacing = *max_advance_h = *max_advance_v = 0; + if (!ctx->font) return GF_BAD_PARAM; + + GdipGetEmHeight(ctx->font, ctx->font_style, &em); + *em_size = (s32) em; + GdipGetCellAscent(ctx->font, ctx->font_style, &val); + ctx->ascent = (Float) val; + *ascent = (s32) val; + GdipGetCellDescent(ctx->font, ctx->font_style, &val); + *descent = (s32) val; *descent *= -1; + ctx->descent = -1 * (Float) val; + *underline = *descent / 2; + GdipGetLineSpacing(ctx->font, ctx->font_style, &val); + *line_spacing = (s32) val; + *max_advance_v = *ascent - *descent; + + + unsigned short test_str[4]; + Fixed w, h, w2; + ctx->em_size = (Float) *em_size; + test_str[0] = (unsigned short) '_'; + test_str[1] = (unsigned short) '\0'; + gdip_get_text_size(dr, test_str, &w, &h); + ctx->underscore_width = FIX2FLT(w); + + test_str[0] = (unsigned short) '_'; + test_str[1] = (unsigned short) ' '; + test_str[2] = (unsigned short) '_'; + test_str[3] = (unsigned short) '\0'; + gdip_get_text_size(dr, test_str, &w2, &h); + ctx->whitespace_width = FIX2FLT(w2 - 2*w); + + *max_advance_h = (s32) MAX(ctx->underscore_width, ctx->whitespace_width); + return GF_OK; +} + + + +static GF_Glyph *gdip_load_glyph(GF_FontReader *dr, u32 glyph_name) +{ + GF_Rect bounds; + GF_Glyph *glyph; + GpPath *path_tmp; + GpStringFormat *fmt; + GpMatrix *mat; + Float est_advance_h; + unsigned short str[4]; + int i; + FontPriv *ctx = (FontPriv *)dr->udta; + + if (!ctx->font) return NULL; + + RectF rc; + rc.X = rc.Y = 0; + rc.Width = rc.Height = 0; + + GdipCreateStringFormat(StringFormatFlagsNoWrap | StringFormatFlagsNoFitBlackBox | StringFormatFlagsMeasureTrailingSpaces, LANG_NEUTRAL, &fmt); + GdipSetStringFormatAlign(fmt, StringAlignmentNear); + GdipCreatePath(FillModeAlternate, &path_tmp); + + if (glyph_name==0x20) { + est_advance_h = ctx->whitespace_width; + } else { + /*to compute first glyph alignment (say 'x', we figure out its bounding full box by using the '_' char as wrapper (eg, "_x_") + then the bounding box starting from xMin of the glyph ('x_'). The difference between both will give us a good approx + of the glyph alignment*/ + str[0] = glyph_name; + str[1] = (unsigned short) '_'; + str[2] = (unsigned short) 0; + GdipAddPathString(path_tmp, (const WCHAR *)str, -1, ctx->font, ctx->font_style, ctx->em_size, &rc, fmt); + GdipGetPathWorldBounds(path_tmp, &rc, NULL, NULL); + est_advance_h = rc.Width - ctx->underscore_width; + } + + GdipResetPath(path_tmp); + + str[0] = glyph_name; + str[1] = (unsigned short) 0; + rc.X = rc.Y = 0; + rc.Width = rc.Height = 0; + GdipAddPathString(path_tmp, (const WCHAR *)str, -1, ctx->font, ctx->font_style, ctx->em_size, &rc, fmt); + + GdipGetPathWorldBounds(path_tmp, &rc, NULL, NULL); + + /*flip so that we are in a font coordinate system - also move back the glyph to x=0 and y=baseline, GdiPlus doesn't do so*/ + GdipCreateMatrix(&mat); + GdipTranslateMatrix(mat, - rc.X, -ctx->ascent, MatrixOrderAppend); + GdipScaleMatrix(mat, 1, -1, MatrixOrderAppend); + GdipTransformPath(path_tmp, mat); + GdipDeleteMatrix(mat); + + + /*start enum*/ + s32 count; + GdipGetPointCount(path_tmp, &count); + GpPointF *pts = new GpPointF[count]; + BYTE *types = new BYTE[count]; + GdipGetPathTypes(path_tmp, types, count); + GdipGetPathPoints(path_tmp, pts, count); + + GF_SAFEALLOC(glyph, GF_Glyph); + GF_SAFEALLOC(glyph->path, GF_Path); + + for (i=0; i<count; ) { + BOOL closed = 0; + s32 sub_type; + + sub_type = types[i] & PathPointTypePathTypeMask; + + if (sub_type == PathPointTypeStart) { + gf_path_add_move_to(glyph->path, FLT2FIX(pts[i].X), FLT2FIX(pts[i].Y)); + i++; + } + else if (sub_type == PathPointTypeLine) { + gf_path_add_line_to(glyph->path, FLT2FIX(pts[i].X), FLT2FIX(pts[i].Y)); + + if (types[i] & PathPointTypeCloseSubpath) gf_path_close(glyph->path); + + i++; + } + else if (sub_type == PathPointTypeBezier) { + assert(i+2<=count); + gf_path_add_cubic_to(glyph->path, FLT2FIX(pts[i].X), FLT2FIX(pts[i].Y), FLT2FIX(pts[i+1].X), FLT2FIX(pts[i+1].Y), FLT2FIX(pts[i+2].X), FLT2FIX(pts[i+2].Y)); + + if (types[i+2] & PathPointTypeCloseSubpath) gf_path_close(glyph->path); + + i += 3; + } else { + assert(0); + break; + } + } + + delete [] pts; + delete [] types; + GdipDeleteStringFormat(fmt); + GdipDeletePath(path_tmp); + + glyph->ID = glyph_name; + glyph->utf_name = glyph_name; + glyph->vert_advance = (s32) (ctx->ascent-ctx->descent); + glyph->horiz_advance = (s32) est_advance_h; + gf_path_get_bounds(glyph->path, &bounds); + glyph->width = FIX2INT(bounds.width); + glyph->height = FIX2INT(bounds.height); + return glyph; +} + + + + +GF_FontReader *gdip_new_font_driver() +{ + GdiplusStartupInput startupInput; + GF_FontReader *dr; + FontPriv *ctx; + + SAFEALLOC(ctx, FontPriv); + SAFEALLOC(dr, GF_FontReader); + GdiplusStartup(&ctx->gdiToken, &startupInput, NULL); + + GF_REGISTER_MODULE_INTERFACE(dr, GF_FONT_READER_INTERFACE, "GDIplus Font Reader", "gpac distribution") + dr->init_font_engine = gdip_init_font_engine; + dr->shutdown_font_engine = gdip_shutdown_font_engine; + dr->set_font = gdip_set_font; + dr->get_font_info = gdip_get_font_info; + dr->get_glyphs = gdip_get_glyphs; + dr->load_glyph = gdip_load_glyph; + + dr->udta = ctx; + return dr; +} + +void gdip_delete_font_driver(GF_FontReader *dr) +{ + FontPriv *ctx = (FontPriv *)dr->udta; + GdiplusShutdown(ctx->gdiToken); + + if (ctx->font) GdipDeleteFontFamily(ctx->font); + ctx->font = NULL; + + free(dr->udta); + free(dr); +} + + + +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_FONT_READER_INTERFACE) return 1; + if (InterfaceType == GF_RASTER_2D_INTERFACE) return 1; + return 0; +} + +GF_Raster2D *gdip_LoadRenderer(); +void gdip_ShutdownRenderer(GF_Raster2D *driver); + +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType==GF_FONT_READER_INTERFACE) return (GF_BaseInterface *)gdip_new_font_driver(); + if (InterfaceType==GF_RASTER_2D_INTERFACE) return (GF_BaseInterface *)gdip_LoadRenderer(); + return NULL; +} + +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_FONT_READER_INTERFACE: + gdip_delete_font_driver((GF_FontReader *)ifce); + break; + case GF_RASTER_2D_INTERFACE: + gdip_ShutdownRenderer((GF_Raster2D *)ifce); + break; + } +} + diff --git a/modules/gdip_raster/gdip_grad.cpp b/modules/gdip_raster/gdip_grad.cpp new file mode 100644 index 0000000..fefeab0 --- /dev/null +++ b/modules/gdip_raster/gdip_grad.cpp @@ -0,0 +1,406 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / GDIplus rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "gdip_priv.h" + + +GF_STENCIL gdip_new_stencil(GF_Raster2D *, GF_StencilType type) +{ + struct _stencil *sten; + + switch (type) { + case GF_STENCIL_SOLID: + case GF_STENCIL_LINEAR_GRADIENT: + case GF_STENCIL_RADIAL_GRADIENT: + case GF_STENCIL_VERTEX_GRADIENT: + case GF_STENCIL_TEXTURE: + break; + default: + return NULL; + } + SAFEALLOC(sten, struct _stencil); + sten->type = type; + sten->alpha = 255; + return (GF_STENCIL) sten; +} + +static +void gdip_delete_stencil(GF_STENCIL _this) +{ + GPSTEN(); + if (_sten->pSolid) GdipDeleteBrush(_sten->pSolid); + if (_sten->pTexture) GdipDeleteBrush(_sten->pTexture); + if (_sten->pLinear) GdipDeleteBrush(_sten->pLinear); + if (_sten->pRadial) GdipDeleteBrush(_sten->pRadial); + if (_sten->circle) GdipDeletePath(_sten->circle); + if (_sten->pMat) GdipDeleteMatrix(_sten->pMat); + if (_sten->pLinearMat) GdipDeleteMatrix(_sten->pLinearMat); + if (_sten->pBitmap) GdipDisposeImage(_sten->pBitmap); + if (_sten->conv_buf) free(_sten->conv_buf); + + if (_sten->cols) delete [] _sten->cols; + if (_sten->pos) delete [] _sten->pos; + + free(_sten); +} +static +GF_Err gdip_stencil_set_matrix(GF_STENCIL _this, GF_Matrix2D *mat) +{ + GPSTEN(); + GPMATRIX(); + if (_sten->pMat) GdipDeleteMatrix(_sten->pMat); + _sten->pMat = _mat; + return GF_OK; +} + +static +GF_Err gdip_set_brush_color(GF_STENCIL _this, GF_Color c) +{ + GPSTEN(); + CHECK_RET(GF_STENCIL_SOLID); + if (!_sten->pSolid) + GdipCreateSolidFill(c, &_sten->pSolid); + else + GdipSetSolidFillColor(_sten->pSolid, c); + + return GF_OK; +} + + +static +GF_Err gdip_set_gradient_mode(GF_STENCIL _this, GF_GradientMode mode) +{ + GPSTEN(); + CHECK2_RET(GF_STENCIL_LINEAR_GRADIENT, GF_STENCIL_RADIAL_GRADIENT); + _sten->spread = mode; + _sten->needs_rebuild = 1; + return GF_OK; +} + +static +GF_Err gdip_set_linear_gradient (GF_STENCIL _this, Fixed start_x, Fixed start_y, Fixed end_x, Fixed end_y) +{ + GPSTEN(); + CHECK_RET(GF_STENCIL_LINEAR_GRADIENT); + if (_sten->pLinear) GdipDeleteBrush(_sten->pLinear); + + _sten->start.X = FIX2FLT(start_x); + _sten->start.Y = FIX2FLT(start_y); + _sten->end.X = FIX2FLT(end_x); + _sten->end.Y = FIX2FLT(end_y); + + GdipCreateLineBrush(&_sten->start, &_sten->end, 0xFF000000, 0xFFFFFFFF, WrapModeTile, &_sten->pLinear); + if (!_sten->pLinearMat) GdipCreateMatrix(&_sten->pLinearMat); + GdipGetLineTransform(_sten->pLinear, _sten->pLinearMat); + _sten->needs_rebuild = 1; + return GF_OK; +} + +void gdip_recompute_line_gradient(GF_STENCIL _this) +{ + GpPointF start, end; + u32 i, k; + REAL w, h; + GPSTEN(); + + if (!_sten->needs_rebuild) return; + _sten->needs_rebuild = 0; + + if (_sten->pLinear) GdipDeleteBrush(_sten->pLinear); + GdipCreateLineBrush(&_sten->start, &_sten->end, 0xFFFF0000, 0xFFFF00FF, WrapModeTile, &_sten->pLinear); + switch (_sten->spread) { + case GF_GRADIENT_MODE_PAD: + break; + case GF_GRADIENT_MODE_SPREAD: + GdipSetLineWrapMode(_sten->pLinear, WrapModeTileFlipXY); + GdipSetLinePresetBlend(_sten->pLinear, (ARGB *) _sten->cols, _sten->pos, _sten->num_pos); + return; + case GF_GRADIENT_MODE_REPEAT: + GdipSetLineWrapMode(_sten->pLinear, WrapModeTile); + GdipSetLinePresetBlend(_sten->pLinear, (ARGB *) _sten->cols, _sten->pos, _sten->num_pos); + return; + } + /*currently gdiplus doesn't support padded mode on gradients, so update the line gradient by + using a line 3 times longer*/ + w = _sten->end.X - _sten->start.X; + h = _sten->end.Y - _sten->start.Y; + start.X = _sten->start.X - w; + start.Y = _sten->start.Y - h; + end.X = _sten->end.X + w; + end.Y = _sten->end.Y + h; + GdipCreateLineBrush(&start, &end, 0xFFFF0000, 0xFFFF00FF, WrapModeTile, &_sten->pLinear); + ARGB *cols = new ARGB[_sten->num_pos+2]; + REAL *pos = new REAL[_sten->num_pos+2]; + + k=0; + for (i=0; i<_sten->num_pos; i++) { + cols[i+k] = _sten->cols[i]; + pos[i+k] = (1 + _sten->pos[i])/3; + + if (!i) { + pos[1] = pos[0]; + cols[1] = cols[0]; + k=1; + pos[0] = 0; + } + } + pos[_sten->num_pos+1] = 1.0; + cols[_sten->num_pos+1] = cols[_sten->num_pos]; + + /*since depending on gradient transform the padding is likely to be not big enough, use flipXY to assure that in most + cases the x3 dilatation is enough*/ + GdipSetLineWrapMode(_sten->pLinear, WrapModeTileFlipXY); + GdipSetLinePresetBlend(_sten->pLinear, cols, pos, 2+_sten->num_pos); + + delete [] cols; + delete [] pos; +} + + +/*GDIplus is completely bugged here, we MUST build the gradient in local coord system and apply translation +after, otherwise performances are just horrible*/ +void gdip_recompute_radial_gradient(GF_STENCIL _this) +{ + s32 repeat, k; + u32 i; + GpPointF pt; + GpMatrix *mat; + GPSTEN(); + + + if (!_sten->needs_rebuild) return; + _sten->needs_rebuild = 0; + + + if (_sten->pRadial) { + GdipDeleteBrush(_sten->pRadial); + _sten->pRadial = NULL; + } + if (_sten->pSolid) { + GdipDeleteBrush(_sten->pSolid); + _sten->pSolid = NULL; + } + if (_sten->circle) { + GdipDeletePath(_sten->circle); + _sten->circle = NULL; + } + + GdipCreatePath(FillModeAlternate, &_sten->circle); + /*get number of repeats*/ + if (_sten->spread == GF_GRADIENT_MODE_PAD) { + + + GdipAddPathEllipse(_sten->circle, - _sten->radius.X, -_sten->radius.Y, + 2*_sten->radius.X, 2*_sten->radius.Y); + + GdipCreatePathGradientFromPath(_sten->circle, &_sten->pRadial); + + ARGB *blends = new ARGB[_sten->num_pos + 1]; + + /*radial blend pos are from bounds to center in gdiplus*/ + blends[0] = _sten->cols[_sten->num_pos - 1]; + for (i=0; i<_sten->num_pos;i++) { + blends[i+1] = _sten->cols[_sten->num_pos - i - 1]; + } + + REAL *pos = new REAL[_sten->num_pos + 1]; + pos[0] = 0; + for (i=0; i<_sten->num_pos;i++) { + pos[i+1] = _sten->pos[i]; + } + + GdipSetPathGradientPresetBlend(_sten->pRadial, blends, pos, _sten->num_pos + 1); + delete [] blends; + delete [] pos; + + /*set focal*/ + pt = _sten->focal; + pt.X -= _sten->center.X; + pt.Y -= _sten->center.Y; + GdipSetPathGradientCenterPoint(_sten->pRadial, &pt); + + /*set transform*/ + GdipCreateMatrix(&mat); + GdipTranslateMatrix(mat, _sten->center.X, _sten->center.Y, MatrixOrderAppend); + if (_sten->pMat) GdipMultiplyMatrix(mat, _sten->pMat, MatrixOrderAppend); + GdipSetTextureTransform((GpTexture*)_sten->pRadial, mat); + GdipDeleteMatrix(mat); + + /*create back brush*/ + GdipCreateSolidFill(_sten->cols[_sten->num_pos - 1], &_sten->pSolid); + GdipResetPath(_sten->circle); + GdipAddPathEllipse(_sten->circle, - _sten->radius.X + _sten->center.X, -_sten->radius.Y + _sten->center.Y, + 2*_sten->radius.X, 2*_sten->radius.Y); + + } else { + repeat = 10; + + GdipAddPathEllipse(_sten->circle, - repeat * _sten->radius.X, - repeat*_sten->radius.Y, + 2*repeat*_sten->radius.X, 2*repeat*_sten->radius.Y); + + GdipCreatePathGradientFromPath(_sten->circle, &_sten->pRadial); + GdipDeletePath(_sten->circle); + _sten->circle = NULL; + + ARGB *blends = new ARGB[_sten->num_pos*repeat]; + REAL *pos = new REAL[_sten->num_pos*repeat]; + + if (_sten->spread == GF_GRADIENT_MODE_REPEAT) { + for (k=0; k<repeat; k++) { + for (i=0; i<_sten->num_pos; i++) { + blends[k*_sten->num_pos + i] = _sten->cols[_sten->num_pos - i - 1]; + pos[k*_sten->num_pos + i] = (k + _sten->pos[i]) / repeat; + } + } + } else { + for (k=0; k<repeat; k++) { + for (i=0; i<_sten->num_pos; i++) { + u32 index = (k%2) ? (_sten->num_pos-i-1) : i; + blends[k*_sten->num_pos + i] = _sten->cols[index]; + if (k%2) { + pos[k*_sten->num_pos + i] = (k + (1 - _sten->pos[index]) ) / repeat; + } else { + pos[k*_sten->num_pos + i] = ( k + _sten->pos[i] ) / repeat; + } + } + } + } + GdipSetPathGradientPresetBlend(_sten->pRadial, blends, pos, _sten->num_pos*repeat); + delete [] pos; + delete [] blends; + + + /*set focal*/ + pt = _sten->focal; + pt.X -= (1 - repeat) * (_sten->focal.X - _sten->center.X) + _sten->center.X; + pt.Y -= (1 - repeat) * (_sten->focal.Y - _sten->center.Y) + _sten->center.Y; + GdipSetPathGradientCenterPoint(_sten->pRadial, &pt); + + /*set transform*/ + GdipCreateMatrix(&mat); + GdipTranslateMatrix(mat, (1 - repeat) * (_sten->focal.X - _sten->center.X) + _sten->center.X, + (1 - repeat) * (_sten->focal.Y - _sten->center.Y) + _sten->center.Y, + MatrixOrderAppend); + if (_sten->pMat) GdipMultiplyMatrix(mat, _sten->pMat, MatrixOrderAppend); + GdipSetTextureTransform((GpTexture*)_sten->pRadial, mat); + GdipDeleteMatrix(mat); + + GdipSetPathGradientWrapMode(_sten->pRadial, WrapModeTileFlipXY); + } +} + +static +GF_Err gdip_set_radial_gradient(GF_STENCIL _this, Fixed cx, Fixed cy, Fixed fx, Fixed fy, Fixed x_radius, Fixed y_radius) +{ + GPSTEN(); + CHECK_RET(GF_STENCIL_RADIAL_GRADIENT); + + /*store focal info*/ + _sten->radius.X = FIX2FLT(x_radius); + _sten->radius.Y = FIX2FLT(y_radius); + _sten->center.X = FIX2FLT(cx); + _sten->center.Y = FIX2FLT(cy); + _sten->focal.X = FIX2FLT(fx); + _sten->focal.Y = FIX2FLT(fy); + _sten->needs_rebuild = 1; + return GF_OK; +} + +static +GF_Err gdip_set_gradient_interpolation(GF_STENCIL _this, Fixed *pos, GF_Color *col, u32 count) +{ + u32 i; + GPSTEN(); + + if (_sten->cols) delete [] _sten->cols; + if (_sten->pos) delete [] _sten->pos; + + /*handle padding internally*/ + _sten->cols = new ARGB[count]; + _sten->pos = new REAL[count]; + for (i=0; i<count; i++) _sten->pos[i] = FIX2FLT(pos[i]); + memcpy(_sten->cols, col, sizeof(ARGB)*count); + _sten->num_pos = count; + _sten->needs_rebuild = 1; + return GF_OK; +} + + +static +GF_Err gdip_set_vertex_path(GF_STENCIL _this, GF_Path *path) +{ + GPSTEN(); + GpPath *p; + CHECK_RET(GF_STENCIL_VERTEX_GRADIENT); + p = gdip_create_path(path); + if (_sten->pRadial) GdipDeleteBrush(_sten->pRadial); + GdipCreatePathGradientFromPath(p, &_sten->pRadial); + GdipDeletePath(p); + return GF_OK; +} + +static +GF_Err gdip_set_vertex_center (GF_STENCIL _this, Fixed cx, Fixed cy, u32 color) +{ + GpStatus ret; + GPSTEN(); + CHECK_RET(GF_STENCIL_VERTEX_GRADIENT); + + if (!_sten->pRadial) return GF_BAD_PARAM; + _sten->center.X = FIX2FLT(cx); + _sten->center.Y = FIX2FLT(cy); + + ret = GdipSetPathGradientCenterPoint(_sten->pRadial, &_sten->center); + ret = GdipSetPathGradientCenterColor(_sten->pRadial, (ARGB) color); + return GF_OK; +} + +static +GF_Err gdip_set_vertex_colors (GF_STENCIL _this, u32 *colors, u32 nbCol) +{ + int col = nbCol; + GPSTEN(); + CHECK_RET(GF_STENCIL_VERTEX_GRADIENT); + + GpStatus ret; + if (!_sten->pRadial) return GF_BAD_PARAM; + ret = GdipSetPathGradientSurroundColorsWithCount(_sten->pRadial, (ARGB *) colors, &col); + return GF_OK; +} + + +void gdip_init_driver_grad(GF_Raster2D *driver) +{ + driver->stencil_new = gdip_new_stencil; + driver->stencil_delete = gdip_delete_stencil; + driver->stencil_set_matrix = gdip_stencil_set_matrix; + driver->stencil_set_brush_color = gdip_set_brush_color; + driver->stencil_set_gradient_mode = gdip_set_gradient_mode; + driver->stencil_set_linear_gradient = gdip_set_linear_gradient; + driver->stencil_set_radial_gradient = gdip_set_radial_gradient; + driver->stencil_set_gradient_interpolation = gdip_set_gradient_interpolation; + driver->stencil_set_vertex_path = gdip_set_vertex_path; + driver->stencil_set_vertex_center = gdip_set_vertex_center; + driver->stencil_set_vertex_colors = gdip_set_vertex_colors; +} \ No newline at end of file diff --git a/modules/gdip_raster/gdip_priv.h b/modules/gdip_raster/gdip_priv.h new file mode 100644 index 0000000..fd973ea --- /dev/null +++ b/modules/gdip_raster/gdip_priv.h @@ -0,0 +1,213 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / GDIplus rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef __GDIP_PRIV_H +#define __GDIP_PRIV_H + +#include <math.h> +#include <gpac/modules/raster2d.h> +#include <gpac/modules/font.h> +#include <windows.h> + +#ifndef ULONG_PTR +#define ULONG_PTR DWORD +#endif + +#ifndef INT16 +typedef s16 INT16; +#endif + +#ifndef UINT16 +typedef u16 UINT16; +#endif + +#define SAFEALLOC(__ptr, __struc) __ptr = (__struc*)malloc(sizeof(__struc)); if (__ptr) memset(__ptr, 0, sizeof(__struc)); + +/*all GDIPLUS includes for C api*/ + +struct IDirectDrawSurface7; + +#include "GdiplusMem.h" +#include "GdiplusEnums.h" +#include "GdiplusTypes.h" +#include "GdiplusInit.h" +#include "GdiplusPixelFormats.h" +#include "GdiplusColor.h" +#include "GdiplusMetaHeader.h" +#include "GdiplusImaging.h" +#include "GdiplusColorMatrix.h" +#include "GdiplusGpStubs.h" +#include "GdiplusColor.h" +#include "GdiplusFlat.h" + +#include <math.h> + +#define GD_PI 3.1415926536f + +/* default resolution for N-bezier curves*/ +#define GDIP_DEFAULT_RESOLUTION 64 + +struct _gdip_context +{ + ULONG_PTR gdiToken; +}; + + +/*struct translators*/ + +GFINLINE GpMatrix *mat_gpac_to_gdip(GF_Matrix2D *mat) +{ + GpMatrix *ret; + if (!mat) return NULL; + GdipCreateMatrix(&ret); + GdipSetMatrixElements(ret, FIX2FLT(mat->m[0]), FIX2FLT(mat->m[3]), FIX2FLT(mat->m[1]), FIX2FLT(mat->m[4]), FIX2FLT(mat->m[2]), FIX2FLT(mat->m[5])); + return ret; +} + + +GFINLINE void cmat_gpac_to_gdip(GF_ColorMatrix *mat, ColorMatrix *matrix) +{ + memset(matrix->m, 0, sizeof(Float)*5*5); + matrix->m[0][0] = FIX2FLT(mat->m[0]); + matrix->m[1][0] = FIX2FLT(mat->m[1]); + matrix->m[2][0] = FIX2FLT(mat->m[2]); + matrix->m[3][0] = FIX2FLT(mat->m[3]); + matrix->m[4][0] = FIX2FLT(mat->m[4]); + matrix->m[0][1] = FIX2FLT(mat->m[5]); + matrix->m[1][1] = FIX2FLT(mat->m[6]); + matrix->m[2][1] = FIX2FLT(mat->m[7]); + matrix->m[3][1] = FIX2FLT(mat->m[8]); + matrix->m[4][1] = FIX2FLT(mat->m[9]); + matrix->m[0][2] = FIX2FLT(mat->m[10]); + matrix->m[1][2] = FIX2FLT(mat->m[11]); + matrix->m[2][2] = FIX2FLT(mat->m[12]); + matrix->m[3][2] = FIX2FLT(mat->m[13]); + matrix->m[4][2] = FIX2FLT(mat->m[14]); + matrix->m[0][3] = FIX2FLT(mat->m[15]); + matrix->m[1][3] = FIX2FLT(mat->m[16]); + matrix->m[2][3] = FIX2FLT(mat->m[17]); + matrix->m[3][3] = FIX2FLT(mat->m[18]); + matrix->m[4][3] = FIX2FLT(mat->m[19]); +} + + +GFINLINE void gdip_cmat_reset(ColorMatrix *matrix) +{ + memset(matrix->m, 0, sizeof(Float)*5*5); + matrix->m[0][0] = matrix->m[1][1] = matrix->m[2][2] = matrix->m[3][3] = matrix->m[4][4] = 1.0; +} + +#define GPMATRIX() GpMatrix * _mat = mat_gpac_to_gdip(mat); + +GpPath *gdip_create_path(GF_Path * _this); + +struct _stencil +{ + GF_StencilType type; + GF_GradientMode spread; + GF_TextureTiling tiling; + + GpSolidFill *pSolid; + + GpMatrix *pMat; + + /*Linear gradient vars*/ + GpLineGradient *pLinear; + GpMatrix *pLinearMat; + GpPointF start, end; + + /*Radial gradient vars*/ + GpPathGradient *pRadial; + GpPointF center, radius, focal; + GpPath *circle; + + /*interpolation colors storage*/ + REAL *pos; + ARGB *cols; + u32 num_pos; + Bool needs_rebuild; + + /*texture specific*/ + GpTexture *pTexture; + GpBitmap *pBitmap; + u32 width, height; + ColorMatrix cmat; + Bool has_cmat; + PixelFormat format; + /*GDIplus is expecting ABGR when creating a bitmap with GdipCreateBitmapFromScan0. + Since we don't want to rewrite by hand the full image when loading textures, we + force R->B switching */ + Bool invert_br; + GF_TextureFilter tFilter; + + Bool texture_invalid; + GF_Rect wnd; + u8 alpha; + + unsigned char *conv_buf; + u32 conv_size; + unsigned char *orig_buf; + u32 orig_stride, orig_format; + Bool is_converted; + /*not used yet, we only convert to RGB or ARGB*/ + u32 destination_format; +}; +#define GPSTEN() struct _stencil *_sten = (struct _stencil *) _this; assert(_this); +#define CHECK(_type) if (_sten->type!=_type) return; +#define CHECK_RET(_type) if (_sten->type!=_type) return GF_BAD_PARAM; +#define CHECK2(_t1, _t2) if ((_sten->type!=_t1) && (_sten->type!=_t2)) return; +#define CHECK2_RET(_t1, _t2) if ((_sten->type!=_t1) && (_sten->type!=_t2)) return GF_BAD_PARAM; + +void gdip_recompute_line_gradient(GF_STENCIL _this); +void gdip_recompute_radial_gradient(GF_STENCIL _this); + +void gdip_load_texture(struct _stencil *sten); + +void gdip_init_driver_texture(GF_Raster2D *driver); +void gdip_init_driver_common(GF_Raster2D *driver); +void gdip_init_driver_grad(GF_Raster2D *driver); + +typedef struct +{ + ULONG_PTR gdiToken; + + /*text stuff*/ + Float em_size, descent, ascent; + s32 font_style; + Float whitespace_width; + Float underscore_width; + GpFontFamily *font; + + char font_serif[1024]; + char font_sans[1024]; + char font_fixed[1024]; +} FontPriv; + +GF_FontReader *gdip_new_font_driver(); +void gdip_delete_font_driver(GF_FontReader *dr); + + + +#endif //__GDIP_PRIV_H \ No newline at end of file diff --git a/modules/gdip_raster/gdip_rend.cpp b/modules/gdip_raster/gdip_rend.cpp new file mode 100644 index 0000000..20c1ec2 --- /dev/null +++ b/modules/gdip_raster/gdip_rend.cpp @@ -0,0 +1,499 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / GDIplus rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "gdip_priv.h" +#include <windows.h> + +#ifndef ULONG_PTR +#define ULONG_PTR DWORD +#endif + +#ifndef INT16 +typedef s16 INT16; +#endif + +#ifndef UINT16 +typedef u16 UINT16; +#endif + +/*all GDIPLUS includes for C api*/ + +struct IDirectDrawSurface7; + +#include "GdiplusMem.h" +#include "GdiplusEnums.h" +#include "GdiplusTypes.h" +#include "GdiplusInit.h" +#include "GdiplusPixelFormats.h" +#include "GdiplusColor.h" +#include "GdiplusMetaHeader.h" +#include "GdiplusImaging.h" +#include "GdiplusColorMatrix.h" +#include "GdiplusGpStubs.h" +#include "GdiplusColor.h" +#include "GdiplusFlat.h" + +struct _graphics +{ + GpGraphics *graph; + GpMatrix *mat; + GpPath *clip; + GpPath *current; + u32 w, h; + Bool center_coords; + + /*offscreen buffer handling*/ + GpBitmap *pBitmap; +}; + +GpPath *gdip_create_path(GF_Path *_this) +{ + GpPath *p; + u32 j, i, nb_pts, cur; + if (!_this || !_this->n_points) return NULL; + GdipCreatePath(FillModeAlternate, &p); + + GdipSetPathFillMode(p, (_this->flags & GF_PATH_FILL_ZERO_NONZERO) ? FillModeWinding : FillModeAlternate); + + cur = 0; + for (i=0; i<_this->n_contours; i++) { + nb_pts = 1+_this->contours[i] - cur; + GdipStartPathFigure(p); + for (j=cur+1; j<cur+nb_pts; ) { + switch (_this->tags[j]) { + case GF_PATH_CURVE_ON: + GdipAddPathLine(p, FIX2FLT(_this->points[j-1].x), FIX2FLT(_this->points[j-1].y), FIX2FLT(_this->points[j].x), FIX2FLT(_this->points[j].y)); + j++; + break; + case GF_PATH_CLOSE: + GdipAddPathLine(p, FIX2FLT(_this->points[j].x), FIX2FLT(_this->points[j].y), FIX2FLT(_this->points[cur].x), FIX2FLT(_this->points[cur].y)); + j++; + break; + case GF_PATH_CURVE_CUBIC: + GdipAddPathBezier(p, + FIX2FLT(_this->points[j-1].x), FIX2FLT(_this->points[j-1].y), + FIX2FLT(_this->points[j].x), FIX2FLT(_this->points[j].y), + FIX2FLT(_this->points[j+1].x), FIX2FLT(_this->points[j+1].y), + FIX2FLT(_this->points[j+2].x), FIX2FLT(_this->points[j+2].y) + ); + j+=3; + break; + case GF_PATH_CURVE_CONIC: + { + GF_Point2D ctl, end, c1, c2, start; + start = _this->points[j-1]; + ctl = _this->points[j]; + end = _this->points[j+1]; + c1.x = start.x + 2*(ctl.x - start.x) / 3; + c1.y = start.y + 2*(ctl.y - start.y) / 3; + c2.x = c1.x + (end.x - start.x) / 3; + c2.y = c1.y + (end.y - start.y) / 3; + GdipAddPathBezier(p, + FIX2FLT(start.x), FIX2FLT(start.y), + FIX2FLT(c1.x), FIX2FLT(c1.y), + FIX2FLT(c2.x), FIX2FLT(c2.y), + FIX2FLT(end.x), FIX2FLT(end.y) + ); + j+=2; + } + break; + } + } + GdipClosePathFigure(p); + cur += nb_pts; + } + return p; +} + +#define GPGRAPH() struct _graphics *_graph = (struct _graphics *)_this; + +static +GF_SURFACE gdip_new_surface(GF_Raster2D *, Bool center_coords) +{ + struct _graphics *graph; + SAFEALLOC(graph, struct _graphics); + graph->center_coords = center_coords; + return graph; +} + +static +void gdip_delete_surface(GF_SURFACE _this) +{ + GPGRAPH(); + free(_graph); +} + +/*should give the best results with the clippers*/ +#define GDIP_PIXEL_MODE PixelOffsetModeHighQuality + +static +GF_Err gdip_attach_surface_to_device(GF_SURFACE _this, void *os_handle, u32 width, u32 height) +{ + GpMatrix *mat; + HDC handle = (HDC) os_handle; + GPGRAPH(); + if (!_graph || !handle) return GF_BAD_PARAM; + if (_graph->graph) return GF_BAD_PARAM; + GdipCreateFromHDC(handle, &_graph->graph); + + GdipCreateMatrix(&mat); + if ( _graph->center_coords) { + GdipScaleMatrix(mat, 1.0, -1.0, MatrixOrderAppend); + GdipTranslateMatrix(mat, (Float) width/2, (Float) height/2, MatrixOrderAppend); + } + GdipSetWorldTransform(_graph->graph, mat); + GdipDeleteMatrix(mat); + _graph->w = width; + _graph->h = height; + GdipSetPixelOffsetMode(_graph->graph, GDIP_PIXEL_MODE); + return GF_OK; +} +static +GF_Err gdip_attach_surface_to_texture(GF_SURFACE _this, GF_STENCIL sten) +{ + GpMatrix *mat; + struct _stencil *_sten = (struct _stencil *)sten; + GPGRAPH(); + if (!_graph || !_sten || !_sten->pBitmap) return GF_BAD_PARAM; + + GdipGetImageGraphicsContext(_sten->pBitmap, &_graph->graph); + + if (_graph->center_coords) { + GdipCreateMatrix(&mat); + GdipScaleMatrix(mat, 1.0, -1.0, MatrixOrderAppend); + GdipTranslateMatrix(mat, (Float) _sten->width/2, (Float) _sten->height/2, MatrixOrderAppend); + GdipSetWorldTransform(_graph->graph, mat); + GdipDeleteMatrix(mat); + } + _graph->w = _sten->width; + _graph->h = _sten->height; + GdipSetPixelOffsetMode(_graph->graph, GDIP_PIXEL_MODE); + return GF_OK; +} +static +GF_Err gdip_attach_surface_to_buffer(GF_SURFACE _this, char *pixels, u32 width, u32 height, u32 stride, GF_PixelFormat pixelFormat) +{ + GpMatrix *mat; + u32 pFormat; + GPGRAPH(); + + if (stride%4) return GF_NOT_SUPPORTED; + + switch (pixelFormat) { + case GF_PIXEL_ALPHAGREY: + pFormat = PixelFormat16bppGrayScale; + break; + case GF_PIXEL_RGB_555: + pFormat = PixelFormat16bppRGB555; + break; + case GF_PIXEL_RGB_565: + pFormat = PixelFormat16bppRGB565; + break; + case GF_PIXEL_RGB_24: + pFormat = PixelFormat24bppRGB; + break; + case GF_PIXEL_RGB_32: + pFormat = PixelFormat32bppRGB; + break; + case GF_PIXEL_ARGB: + pFormat = PixelFormat32bppARGB; + break; + default: + return GF_NOT_SUPPORTED; + } + GdipCreateBitmapFromScan0(width, height, stride, pFormat, (unsigned char*)pixels, &_graph->pBitmap); + GdipGetImageGraphicsContext(_graph->pBitmap, &_graph->graph); + + _graph->w = width; + _graph->h = height; + if (_graph->center_coords) { + GdipCreateMatrix(&mat); + GdipScaleMatrix(mat, 1.0, -1.0, MatrixOrderAppend); + GdipTranslateMatrix(mat, (Float) width/2, (Float) height/2, MatrixOrderAppend); + GdipSetWorldTransform(_graph->graph, mat); + GdipDeleteMatrix(mat); + } + GdipSetPixelOffsetMode(_graph->graph, GDIP_PIXEL_MODE); + return GF_OK; +} + +static +void gdip_detach_surface(GF_SURFACE _this) +{ + GPGRAPH(); + if (_graph->graph) GdipDeleteGraphics(_graph->graph); + _graph->graph = NULL; + if (_graph->clip) GdipDeletePath(_graph->clip); + _graph->clip = NULL; + if (_graph->pBitmap) GdipDisposeImage(_graph->pBitmap); + _graph->pBitmap = NULL; + if (_graph->current) GdipDeletePath(_graph->current); + _graph->current = NULL; +} + + +static +GF_Err gdip_surface_set_raster_level(GF_SURFACE _this, GF_RasterLevel RasterSetting) +{ + GPGRAPH(); + switch (RasterSetting) { + case GF_RASTER_HIGH_SPEED: + GdipSetSmoothingMode(_graph->graph, SmoothingModeHighSpeed); + GdipSetCompositingQuality(_graph->graph, CompositingQualityHighSpeed); + break; + case GF_RASTER_MID: + GdipSetSmoothingMode(_graph->graph, SmoothingModeDefault); + GdipSetCompositingQuality(_graph->graph, CompositingQualityDefault); + break; + case GF_RASTER_HIGH_QUALITY: + GdipSetSmoothingMode(_graph->graph, SmoothingModeHighQuality); + GdipSetCompositingQuality(_graph->graph, CompositingQualityDefault); + /*THIS IS HORRIBLY SLOW DON'T EVEN THINK ABOUT IT*/ + /*GdipSetCompositingQuality(_graph->graph, CompositingQualityHighQuality);*/ + break; + } + return GF_OK; +} +static +GF_Err gdip_surface_set_matrix(GF_SURFACE _this, GF_Matrix2D * mat) +{ + GPGRAPH(); + if (_graph->mat) GdipDeleteMatrix(_graph->mat); + + _graph->mat = mat_gpac_to_gdip(mat); + return GF_OK; +} +static +GF_Err gdip_surface_set_clipper(GF_SURFACE _this, GF_IRect *rc) +{ + GPGRAPH(); + if (_graph->clip) GdipDeletePath(_graph->clip); + _graph->clip = 0L; + if (!rc) return GF_OK; + + GdipCreatePath(FillModeAlternate, &_graph->clip); + GdipAddPathRectangleI(_graph->clip, rc->x, rc->y - rc->height, rc->width, rc->height); + return GF_OK; +} + +static +GpBrush *gdip_get_brush(struct _stencil *_sten) +{ + if (_sten->pSolid) return _sten->pSolid; + if (_sten->pLinear) return _sten->pLinear; + if (_sten->pRadial) return _sten->pRadial; + if (_sten->pTexture) return _sten->pTexture; + return NULL; +} + +static GpPath *gdip_setup_path(struct _graphics *_this, GF_Path *path) +{ + GpPath *tr = gdip_create_path(path); + /*append current matrix*/ + if (_this->mat) GdipTransformPath(tr, _this->mat); + return tr; +} + +static +GF_Err gdip_surface_set_path(GF_SURFACE _this, GF_Path *path) +{ + struct _storepath *_path; + GPGRAPH(); + if (!_graph) return GF_BAD_PARAM; + if (_graph->current) GdipDeletePath(_graph->current); + _graph->current = NULL; + if (!path) return GF_OK; + + _path = (struct _storepath *)path; + _graph->current = gdip_setup_path(_graph, path); + return GF_OK; +} + +//#define NODRAW + +static +GF_Err gdip_surface_fill(GF_SURFACE _this, GF_STENCIL stencil) +{ + GpStatus ret; + GpMatrix *newmat; + struct _stencil *_sten; + GPGRAPH(); + if (!_this) return GF_BAD_PARAM; + if (!_graph->current) return GF_OK; + _sten = (struct _stencil *)stencil; assert(_sten); + +#ifdef NODRAW + return GF_OK; +#endif + + + if (_graph->clip) GdipSetClipPath(_graph->graph, _graph->clip, CombineModeReplace); + + switch (_sten->type) { + case GF_STENCIL_SOLID: + assert(_sten->pSolid); + GdipFillPath(_graph->graph, _sten->pSolid, _graph->current); + break; + case GF_STENCIL_LINEAR_GRADIENT: + if (_sten->pMat) { + /*rebuild gradient*/ + gdip_recompute_line_gradient(_sten); + + GdipResetTextureTransform((GpTexture*)_sten->pLinear); + if (_sten->pMat) { + GdipCloneMatrix(_sten->pMat, &newmat); + } else { + GdipCreateMatrix(&newmat); + } + GdipMultiplyMatrix(newmat, _sten->pLinearMat, MatrixOrderPrepend); + GdipSetTextureTransform((GpTexture*)_sten->pLinear, newmat); + GdipDeleteMatrix(newmat); + } + GdipFillPath(_graph->graph, _sten->pLinear, _graph->current); + break; + case GF_STENCIL_RADIAL_GRADIENT: + /*build gradient*/ + gdip_recompute_radial_gradient(_sten); + + GdipSetCompositingQuality(_graph->graph, CompositingQualityHighSpeed); + GdipSetInterpolationMode(_graph->graph, InterpolationModeLowQuality); + GdipSetSmoothingMode(_graph->graph, SmoothingModeHighSpeed); + + /*check if we need to draw solid background (GDIplus doesn't implement padded mode on path gradients)*/ + if (_sten->pSolid) { + GpPath *tr; + GdipClonePath(_sten->circle, &tr); + GdipTransformPath(tr, _sten->pMat); + GdipSetClipPath(_graph->graph, tr, CombineModeExclude); + GdipFillPath(_graph->graph, _sten->pSolid, _graph->current); + GdipDeletePath(tr); + GdipResetClip(_graph->graph); + if (_graph->clip) GdipSetClipPath(_graph->graph, _graph->clip, CombineModeReplace); + } + GdipFillPath(_graph->graph, _sten->pRadial, _graph->current); + break; + case GF_STENCIL_VERTEX_GRADIENT: + assert(_sten->pRadial); + if (_sten->pMat) GdipSetTextureTransform((GpTexture*)_sten->pRadial, _sten->pMat); + ret = GdipFillPath(_graph->graph, _sten->pRadial, _graph->current); + break; + case GF_STENCIL_TEXTURE: + gdip_load_texture(_sten); + if (_sten->pTexture) { + GpMatrix *newmat; + GdipResetTextureTransform((GpTexture*)_sten->pTexture); + if (_sten->pMat) { + GdipCloneMatrix(_sten->pMat, &newmat); + } else { + GdipCreateMatrix(&newmat); + } + /*gdip flip*/ + if (_graph->center_coords) GdipScaleMatrix(newmat, 1, -1, MatrixOrderPrepend); + GdipSetTextureTransform((GpTexture*)_sten->pTexture, newmat); + GdipDeleteMatrix(newmat); + + GdipSetInterpolationMode(_graph->graph, (_sten->tFilter==GF_TEXTURE_FILTER_HIGH_QUALITY) ? InterpolationModeHighQuality : InterpolationModeLowQuality); + GdipFillPath(_graph->graph, _sten->pTexture, _graph->current); + } + break; + } + return GF_OK; +} + + +static +GF_Err gdip_surface_flush(GF_SURFACE _this) +{ + GPGRAPH(); + GdipFlush(_graph->graph, FlushIntentionSync); + return GF_OK; +} + +static +GF_Err gdip_surface_clear(GF_SURFACE _this, GF_IRect *rc, u32 color) +{ + GpPath *path; + GPGRAPH(); + + GdipCreatePath(FillModeAlternate, &path); + if (rc) { + /*luckily enough this maps well for both flipped and unflipped coords*/ + GdipAddPathRectangleI(path, rc->x, rc->y - rc->height, rc->width, rc->height); + } else { +/* if (_graph->center_coords) { + GdipAddPathRectangleI(path, -1 * (s32)_graph->w / 2, -1 * (s32)_graph->h / 2, _graph->w, _graph->h); + } else { +*/ GdipAddPathRectangleI(path, 0, 0, _graph->w, _graph->h); +// } + } + /*we MUST use clear otherwise ARGB surfaces are not cleared correctly*/ + GdipSetClipPath(_graph->graph, path, CombineModeReplace); + GdipGraphicsClear(_graph->graph, color); + GdipDeletePath(path); + return GF_OK; +} + +void gdip_init_driver_surface(GF_Raster2D *driver) +{ + driver->surface_new = gdip_new_surface; + driver->surface_delete = gdip_delete_surface; + driver->surface_attach_to_device = gdip_attach_surface_to_device; + driver->surface_attach_to_texture = gdip_attach_surface_to_texture; + driver->surface_attach_to_buffer = gdip_attach_surface_to_buffer; + driver->surface_detach = gdip_detach_surface; + driver->surface_set_raster_level = gdip_surface_set_raster_level; + driver->surface_set_matrix = gdip_surface_set_matrix; + driver->surface_set_clipper = gdip_surface_set_clipper; + driver->surface_set_path = gdip_surface_set_path; + driver->surface_fill = gdip_surface_fill; + driver->surface_flush = gdip_surface_flush; + driver->surface_clear = gdip_surface_clear; +} + + +GF_Raster2D *gdip_LoadRenderer() +{ + GdiplusStartupInput startupInput; + GF_Raster2D *driver; + struct _gdip_context *ctx; + SAFEALLOC(ctx, struct _gdip_context); + SAFEALLOC(driver, GF_Raster2D); + GdiplusStartup(&ctx->gdiToken, &startupInput, NULL); + driver->internal = ctx; + GF_REGISTER_MODULE_INTERFACE(driver, GF_RASTER_2D_INTERFACE, "GDIplus 2D Raster", "gpac distribution") + gdip_init_driver_texture(driver); + gdip_init_driver_surface(driver); + gdip_init_driver_grad(driver); + return driver; +} + +void gdip_ShutdownRenderer(GF_Raster2D *driver) +{ + struct _gdip_context *ctx = (struct _gdip_context *)driver->internal; + + GdiplusShutdown(ctx->gdiToken); + free(driver->internal); + free(driver); +} diff --git a/modules/gdip_raster/gdip_rend.def b/modules/gdip_raster/gdip_rend.def new file mode 100644 index 0000000..a09ed49 --- /dev/null +++ b/modules/gdip_raster/gdip_rend.def @@ -0,0 +1,6 @@ +LIBRARY gm_gdip_raster.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/gdip_raster/gdip_texture.cpp b/modules/gdip_raster/gdip_texture.cpp new file mode 100644 index 0000000..91efb38 --- /dev/null +++ b/modules/gdip_raster/gdip_texture.cpp @@ -0,0 +1,483 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / GDIplus rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "gdip_priv.h" + +#define COL_565(c) ( ( ( (c>>16) & 248) << 8) + ( ( (c>>8) & 252) << 3) + ( (c&0xFF) >> 3) ) +#define COL_555(c) ((( (c>>16) & 248)<<7) + (((c>>8) & 248)<<2) + ((c&0xFF)>>3)) + +static +GF_Err gdip_set_texture(GF_STENCIL _this, char *pixels, u32 width, u32 height, u32 stride, GF_PixelFormat pixelFormat, GF_PixelFormat destination_format_hint, Bool no_copy) +{ + char *ptr; + Bool is_yuv; + u32 pFormat, isBGR, BPP, i, j, col; + unsigned char a, r, g, b; + unsigned short val; + Bool copy; + GPSTEN(); + CHECK_RET(GF_STENCIL_TEXTURE); + + gdip_cmat_reset(&_sten->cmat); + isBGR = 0; + BPP = 4; + copy = 0; + is_yuv = 0; + /*is pixel format supported ?*/ + switch (pixelFormat) { + case GF_PIXEL_GREYSCALE: + pFormat = PixelFormat24bppRGB; + BPP = 1; + /*cannot get it to work without using 24bpp rgb*/ + copy = 1; + break; + case GF_PIXEL_ALPHAGREY: + pFormat = PixelFormat32bppARGB; + BPP = 2; + /*cannot get it to work without using 32bpp argb*/ + copy = 1; + break; + case GF_PIXEL_RGB_555: + pFormat = PixelFormat16bppRGB555; + BPP = 2; + break; + case GF_PIXEL_RGB_565: + pFormat = PixelFormat16bppRGB565; + BPP = 2; + break; + case GF_PIXEL_RGB_24: + pFormat = PixelFormat24bppRGB; + BPP = 3; + /*one day I'll hope to understand how color management works with GDIplus bitmaps...*/ + isBGR = 1; +// copy = 1; + break; + case GF_PIXEL_BGR_24: + pFormat = PixelFormat24bppRGB; + BPP = 3; + break; + case GF_PIXEL_RGB_32: + pFormat = PixelFormat32bppRGB; + BPP = 4; + break; + case GF_PIXEL_ARGB: + pFormat = PixelFormat32bppARGB; + BPP = 4; + break; + case GF_PIXEL_RGBA: + pFormat = PixelFormat32bppARGB; + BPP = 4; + copy = 1; + _sten->orig_buf = (unsigned char *) pixels; + break; + case GF_PIXEL_YV12: + case GF_PIXEL_IYUV: + case GF_PIXEL_I420: + if ( (width*3)%4) return GF_NOT_SUPPORTED; + _sten->orig_format = GF_PIXEL_YV12; + is_yuv = 1; + break; + case GF_PIXEL_YUVA: + _sten->orig_format = GF_PIXEL_YUVA; + is_yuv = 1; + break; + default: + return GF_NOT_SUPPORTED; + } + + if (_sten->pBitmap) GdipDisposeImage(_sten->pBitmap); + _sten->pBitmap = NULL; + _sten->width = width; + _sten->height = height; + _sten->destination_format = destination_format_hint; + if (is_yuv) { + _sten->orig_buf = (unsigned char*)pixels; + _sten->orig_stride = stride; + _sten->is_converted = 0; + return GF_OK; + } + + _sten->is_converted = 1; + _sten->format = pFormat; + + /*GDIplus limitation : horiz_stride shall be multiple of 4 and no support for pure grayscale without palette*/ + if (!copy && pixels && !(stride%4)) { + if (no_copy && isBGR) return GF_NOT_SUPPORTED; + GdipCreateBitmapFromScan0(_sten->width, _sten->height, stride, pFormat, (unsigned char*)pixels, &_sten->pBitmap); + _sten->invert_br = isBGR; + } + /*all other cases: create a local bitmap in desired format*/ + else { + if (no_copy) return GF_NOT_SUPPORTED; + GdipCreateBitmapFromScan0(_sten->width, _sten->height, 0, pFormat, NULL, &_sten->pBitmap); + ptr = pixels; + for (j=0; j<_sten->height; j++) { + for (i=0; i<_sten->width; i++) { + switch (pixelFormat) { + case GF_PIXEL_GREYSCALE: + col = GF_COL_ARGB(255, *ptr, *ptr, *ptr); + ptr ++; + break; + case GF_PIXEL_ALPHAGREY: + r = *ptr++; + a = *ptr++; + col = GF_COL_ARGB(a, r, r, r); + break; + case GF_PIXEL_RGB_555: + val = * (unsigned short *) (ptr); + ptr+= 2; + col = COL_555(val); + break; + case GF_PIXEL_RGB_565: + val = * (unsigned short *) (ptr); + ptr+= 2; + col = COL_565(val); + break; + /*scan0 uses bgr...*/ + case GF_PIXEL_BGR_24: + case GF_PIXEL_RGB_24: + r = *ptr++; + g = *ptr++; + b = *ptr++; + if (!isBGR) { + col = GF_COL_ARGB(255, b, g, r); + } else { + col = GF_COL_ARGB(255, r, g, b); + } + break; + /*NOTE: we assume little-endian only for GDIplus platforms, so BGRA/BGRX*/ + case GF_PIXEL_RGB_32: + case GF_PIXEL_ARGB: + b = *ptr++; + g = *ptr++; + r = *ptr++; + a = *ptr++; + if (pixelFormat==GF_PIXEL_RGB_32) a = 0xFF; + col = GF_COL_ARGB(a, r, g, b); + break; + case GF_PIXEL_RGBA: + r = *ptr++; + g = *ptr++; + b = *ptr++; + a = *ptr++; + col = GF_COL_ARGB(a, r, g, b); + break; + default: + col = GF_COL_ARGB(255, 255, 255, 255); + break; + } + GdipBitmapSetPixel(_sten->pBitmap, i, j, col); + }} + } + + return GF_OK; +} + + +static +GF_Err gdip_create_texture(GF_STENCIL _this, u32 width, u32 height, GF_PixelFormat pixelFormat) +{ + u32 pFormat; + GPSTEN(); + CHECK_RET(GF_STENCIL_TEXTURE); + + gdip_cmat_reset(&_sten->cmat); + /*is pixel format supported ?*/ + switch (pixelFormat) { + case GF_PIXEL_BGR_24: + case GF_PIXEL_GREYSCALE: + case GF_PIXEL_RGB_24: + pFormat = PixelFormat24bppRGB; + break; + case GF_PIXEL_ALPHAGREY: + pFormat = PixelFormat32bppARGB; + break; + case GF_PIXEL_RGB_555: + pFormat = PixelFormat16bppRGB555; + break; + case GF_PIXEL_RGB_565: + pFormat = PixelFormat16bppRGB565; + break; + case GF_PIXEL_RGB_32: + pFormat = PixelFormat32bppRGB; + break; + case GF_PIXEL_ARGB: + pFormat = PixelFormat32bppARGB; + break; + default: + return GF_NOT_SUPPORTED; + } + + if (_sten->pBitmap) GdipDisposeImage(_sten->pBitmap); + _sten->pBitmap = NULL; + _sten->width = width; + _sten->height = height; + _sten->is_converted = 1; + _sten->format = pFormat; + GdipCreateBitmapFromScan0(_sten->width, _sten->height, 0, pFormat, NULL, &_sten->pBitmap); + return GF_OK; +} + + +static +GF_Err gdip_set_texture_repeat_mode(GF_STENCIL _this, GF_TextureTiling mode) +{ + GPSTEN(); + _sten->tiling = mode; + return GF_OK; +} + +static +GF_Err gdip_set_sr_texture_filter(GF_STENCIL _this, GF_TextureFilter filter_mode) +{ + GPSTEN(); + CHECK_RET(GF_STENCIL_TEXTURE); + _sten->tFilter = filter_mode; + return GF_OK; +} + + +static +GF_Err gdip_set_color_matrix(GF_STENCIL _this, GF_ColorMatrix *cmat) +{ + GPSTEN(); + if (!cmat || cmat->identity) { + _sten->texture_invalid = _sten->has_cmat; + _sten->has_cmat = 0; + } else { + if (_sten->invert_br) { + GF_ColorMatrix fin, rev; + memcpy(&fin, cmat, sizeof(GF_ColorMatrix)); + memset(&rev, 0, sizeof(GF_ColorMatrix)); + rev.m[0] = 0; + rev.m[2] = 1; + rev.m[10] = 1; + rev.m[12] = 0; + rev.m[6] = rev.m[18] = 1; + gf_cmx_multiply(&fin, &rev); + cmat_gpac_to_gdip(&fin, &_sten->cmat); + } else { + cmat_gpac_to_gdip(cmat, &_sten->cmat); + } + _sten->has_cmat = 1; + } + _sten->texture_invalid = 1; + return GF_OK; +} + +static +GF_Err gdip_set_alpha(GF_STENCIL _this, u8 alpha) +{ + GPSTEN(); + if (_sten->alpha != alpha) { + _sten->alpha = alpha; + _sten->texture_invalid = 1; + } + return GF_OK; +} + +void gdip_convert_texture(struct _stencil *sten); + +static +GF_Err gdip_get_pixel(GF_STENCIL _this, u32 x, u32 y, u32 *col) +{ + ARGB v; + GpStatus st; + + GPSTEN(); + if (!_sten->is_converted) gdip_convert_texture(_sten); + if (!_sten->pBitmap) return GF_BAD_PARAM; + + st = GdipBitmapGetPixel(_sten->pBitmap, x, y, &v); + + if (_sten->invert_br) { + *col = GF_COL_ARGB( ((v>>24)&0xFF), ((v)&0xFF), ((v>>8)&0xFF), ((v>>16)&0xFF) ); + } else { + *col = v; + } + return GF_OK; +} + + +static +GF_Err gdip_set_pixel(GF_STENCIL _this, u32 x, u32 y, u32 col) +{ + GpStatus st; + ARGB v; + GPSTEN(); + if (!_sten->pBitmap) return GF_BAD_PARAM; + if (!_sten->is_converted) gdip_convert_texture(_sten); + + if (_sten->invert_br) { + v = GF_COL_ARGB( ((col>>24)&0xFF), ((col)&0xFF), ((col>>8)&0xFF), ((col>>16)&0xFF) ); + } else { + v = col; + } + st = GdipBitmapSetPixel(_sten->pBitmap, x, y, v); + return GF_OK; +} + +#if 0 +static +GF_Err gdip_get_texture(GF_STENCIL _this, unsigned char **pixels, u32 *width, u32 *height, u32 *stride, GF_PixelFormat *pixelFormat) +{ + GpRect rc; + BitmapData data; + GPSTEN(); + if (!_sten->pBitmap) return GF_BAD_PARAM; + + rc.X = rc.Y = 0; + rc.Width = _sten->width; + rc.Height = _sten->height; + + GdipBitmapLockBits(_sten->pBitmap, &rc, ImageLockModeRead, _sten->format, &data); + *pixels = (unsigned char *) data.Scan0; + *width = data.Width; + *height = data.Height; + *stride = data.Stride; + switch (data.PixelFormat) { + case PixelFormat16bppRGB555: + *pixelFormat = GF_PIXEL_RGB_555; + break; + case PixelFormat16bppRGB565: + *pixelFormat = GF_PIXEL_RGB_565; + break; + case PixelFormat32bppRGB: + *pixelFormat = GF_PIXEL_RGB_32; + break; + case PixelFormat32bppARGB: + *pixelFormat = GF_PIXEL_ARGB; + break; + case PixelFormat24bppRGB: + default: + *pixelFormat = GF_PIXEL_RGB_24; + break; + } + return GF_OK; +} +#endif + + +void gdip_texture_modified(GF_STENCIL _this) +{ + GPSTEN(); + if (_sten->orig_buf && (_sten->format == PixelFormat32bppARGB)) { + gdip_set_texture(_this, (char *) _sten->orig_buf, _sten->width, _sten->height, _sten->width * 4, GF_PIXEL_RGBA, GF_PIXEL_RGBA, 0); + } + _sten->texture_invalid = 1; +} + +void gdip_init_driver_texture(GF_Raster2D *driver) +{ + driver->stencil_set_texture = gdip_set_texture; + driver->stencil_set_tiling = gdip_set_texture_repeat_mode; + driver->stencil_set_filter = gdip_set_sr_texture_filter; + driver->stencil_set_color_matrix = gdip_set_color_matrix; + driver->stencil_set_alpha = gdip_set_alpha; + driver->stencil_create_texture = gdip_create_texture; + driver->stencil_texture_modified = gdip_texture_modified; +} + + +void gdip_convert_texture(struct _stencil *sten) +{ + u32 BPP, format; + GF_VideoSurface src, dst; + + if (sten->orig_format == GF_PIXEL_YV12) { + BPP = 3; + dst.pixel_format = GF_PIXEL_BGR_24; + format = PixelFormat24bppRGB; + } else { + BPP = 4; + dst.pixel_format = GF_PIXEL_ARGB; + format = PixelFormat32bppARGB; + } + if (BPP*sten->width*sten->height > sten->conv_size) { + if (sten->conv_buf) free(sten->conv_buf); + sten->conv_size = BPP*sten->width*sten->height; + sten->conv_buf = (unsigned char *) malloc(sizeof(unsigned char)*sten->conv_size); + } + + src.height = sten->height; + src.width = sten->width; + src.pitch = sten->orig_stride; + src.pixel_format = sten->orig_format; + src.video_buffer = (char*)sten->orig_buf; + + dst.width = sten->width; + dst.height = sten->height; + dst.pitch = BPP*sten->width; + dst.video_buffer = (char*)sten->conv_buf; + + gf_stretch_bits(&dst, &src, NULL, NULL, 0, 0xFF, 0, NULL, NULL); + + if (sten->pBitmap) GdipDisposeImage(sten->pBitmap); + GdipCreateBitmapFromScan0(sten->width, sten->height, BPP*sten->width, format, sten->conv_buf, &sten->pBitmap); + sten->is_converted = 1; +} + +void gdip_load_texture(struct _stencil *sten) +{ + GpImageAttributes *attr; + ColorMatrix _cmat; + + if (sten->texture_invalid && sten->pTexture) { + GdipDeleteBrush(sten->pTexture); + sten->pTexture = NULL; + } + /*nothing to do*/ + if (sten->is_converted && sten->pTexture) return; + sten->texture_invalid = 0; + + /*convert*/ + if (!sten->is_converted) gdip_convert_texture(sten); + + GdipCreateImageAttributes(&attr); + if (sten->has_cmat) { + memcpy(_cmat.m, sten->cmat.m, sizeof(REAL)*5*5); + } else { + memset(_cmat.m, 0, sizeof(REAL)*5*5); + _cmat.m[0][0] = _cmat.m[1][1] = _cmat.m[2][2] = _cmat.m[3][3] = _cmat.m[4][4] = 1.0; + if (sten->invert_br) { + _cmat.m[0][0] = 0; + _cmat.m[0][2] = 1; + _cmat.m[2][2] = 0; + _cmat.m[2][0] = 1; + } + } + + _cmat.m[3][3] *= ((REAL) sten->alpha) /255.0f; + GdipSetImageAttributesColorMatrix(attr, ColorAdjustTypeDefault, TRUE, &_cmat, NULL, ColorMatrixFlagsDefault); + if (sten->pTexture) GdipDeleteBrush(sten->pTexture); + GdipCreateTextureIAI(sten->pBitmap, attr, 0, 0, sten->width, sten->height, &sten->pTexture); + + /*1- wrap mode is actually ignored in constructor*/ + /*2- GDIPlus does not support S / T clamping */ + GdipSetTextureWrapMode(sten->pTexture, WrapModeTile); + + GdipDisposeImageAttributes(attr); +} + diff --git a/modules/gpac_js/Makefile b/modules/gpac_js/Makefile new file mode 100644 index 0000000..31bbdb9 --- /dev/null +++ b/modules/gpac_js/Makefile @@ -0,0 +1,75 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/gpac_js + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +LINKLIBS= -lgpac +LOCAL_LIB=../../bin/gcc + +#common objects +OBJS=gpac_js.o + +ifeq ($(CONFIG_JS),no) +else +CFLAGS+=-DGPAC_HAS_SPIDERMONKEY $(JS_FLAGS) +ifeq ($(CONFIG_JS),local) +NEED_LOCAL_LIB="yes" +LOCAL_LIB+=-L../../extra_lib/lib/gcc +endif +LINKLIBS+= $(JS_LIBS) +endif + +SRCS := $(OBJS:.o=.c) + +LIB=gm_gpac_js.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols gpac_js.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L$(LOCAL_LIB) $(LINKLIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/gpac_js/gpac_js.c b/modules/gpac_js/gpac_js.c new file mode 100644 index 0000000..52af12b --- /dev/null +++ b/modules/gpac_js/gpac_js.c @@ -0,0 +1,353 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2007-200X ENST + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/*base SVG type*/ +#include <gpac/nodes_svg.h> +/*dom events*/ +#include <gpac/events.h> + +#include <gpac/download.h> +#include <gpac/network.h> +#include <gpac/xml.h> + + +#ifdef GPAC_HAS_SPIDERMONKEY + + +#if !defined(__GNUC__) +# if defined(_WIN32_WCE) +# pragma comment(lib, "js") +# elif defined (WIN32) +# pragma comment(lib, "js32") +# endif +#endif + +#include <gpac/internal/scenegraph_dev.h> +#include <jsapi.h> + +#include <gpac/modules/js_usr.h> +#include <gpac/internal/terminal_dev.h> +#include <gpac/internal/compositor_dev.h> + +typedef struct +{ + JSClass gpacClass; +} GF_GPACJSExt; + + +#define _SETUP_CLASS(the_class, cname, flag, getp, setp, fin) \ + memset(&the_class, 0, sizeof(the_class)); \ + the_class.name = cname; \ + the_class.flags = flag; \ + the_class.addProperty = JS_PropertyStub; \ + the_class.delProperty = JS_PropertyStub; \ + the_class.getProperty = getp; \ + the_class.setProperty = setp; \ + the_class.enumerate = JS_EnumerateStub; \ + the_class.resolve = JS_ResolveStub; \ + the_class.convert = JS_ConvertStub; \ + the_class.finalize = fin; + + +static JSBool gpac_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + const char *res; + char *prop_name; + GF_Terminal *term = (GF_Terminal *)JS_GetPrivate(c, obj); + if (!term) return JS_FALSE; + + if (!JSVAL_IS_STRING(id)) return JS_TRUE; + prop_name = JS_GetStringBytes(JSVAL_TO_STRING(id)); + if (!prop_name) return JS_FALSE; + + if (!strcmp(prop_name, "last_working_directory")) { + res = gf_cfg_get_key(term->user->config, "General", "LastWorkingDir"); + if (!res) res = ""; + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(c, res)); + return JS_TRUE; + } + if (!strcmp(prop_name, "scale_x")) { + *vp = DOUBLE_TO_JSVAL( JS_NewDouble(c, FIX2FLT(term->compositor->zoom) * FIX2FLT(term->compositor->scale_x)) ); + return JS_TRUE; + } + if (!strcmp(prop_name, "scale_y")) { + *vp = DOUBLE_TO_JSVAL( JS_NewDouble(c, FIX2FLT(term->compositor->zoom) * FIX2FLT(term->compositor->scale_y)) ); + return JS_TRUE; + } + if (!strcmp(prop_name, "translation_x")) { + *vp = DOUBLE_TO_JSVAL( JS_NewDouble(c, FIX2FLT(term->compositor->trans_x)) ); + return JS_TRUE; + } + if (!strcmp(prop_name, "translation_y")) { + *vp = DOUBLE_TO_JSVAL( JS_NewDouble(c, FIX2FLT(term->compositor->trans_y)) ); + return JS_TRUE; + } + + return JS_TRUE; +} +static JSBool gpac_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + char *prop_name, *prop_val; + GF_Terminal *term = (GF_Terminal *)JS_GetPrivate(c, obj); + if (!term) return JS_FALSE; + + if (!JSVAL_IS_STRING(id)) return JS_TRUE; + prop_name = JS_GetStringBytes(JSVAL_TO_STRING(id)); + + if (!strcmp(prop_name, "last_working_directory")) { + if (!JSVAL_IS_STRING(*vp)) return JS_FALSE; + prop_val = JS_GetStringBytes(JSVAL_TO_STRING(*vp)); + gf_cfg_set_key(term->user->config, "General", "LastWorkingDir", prop_val); + return JS_TRUE; + } + + return JS_TRUE; +} + +static JSBool gpac_getOption(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + const char *opt; + JSString *js_sec_name, *js_key_name, *s; + GF_Terminal *term = (GF_Terminal *)JS_GetPrivate(c, obj); + if (!term) return JS_FALSE; + + if (argc < 2) return JSVAL_FALSE; + + if (!JSVAL_IS_STRING(argv[0])) return JSVAL_FALSE; + js_sec_name = JSVAL_TO_STRING(argv[0]); + + if (!JSVAL_IS_STRING(argv[1])) return JSVAL_FALSE; + js_key_name = JSVAL_TO_STRING(argv[1]); + + opt = gf_cfg_get_key(term->user->config, JS_GetStringBytes(js_sec_name), JS_GetStringBytes(js_key_name)); + s = JS_NewStringCopyZ(c, opt ? opt : ""); + if (!s) return JS_FALSE; + *rval = STRING_TO_JSVAL(s); + return JS_TRUE; +} + +static JSBool gpac_setOption(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *js_sec_name, *js_key_name, *js_key_val; + GF_Terminal *term = (GF_Terminal *)JS_GetPrivate(c, obj); + if (!term) return JS_FALSE; + if (argc < 3) return JSVAL_FALSE; + + if (!JSVAL_IS_STRING(argv[0])) return JSVAL_FALSE; + js_sec_name = JSVAL_TO_STRING(argv[0]); + + if (!JSVAL_IS_STRING(argv[1])) return JSVAL_FALSE; + js_key_name = JSVAL_TO_STRING(argv[1]); + + if (!JSVAL_IS_STRING(argv[2])) return JSVAL_FALSE; + js_key_val = JSVAL_TO_STRING(argv[2]); + + gf_cfg_set_key(term->user->config, JS_GetStringBytes(js_sec_name), JS_GetStringBytes(js_key_name), JS_GetStringBytes(js_key_val)); + + return JSVAL_TRUE; +} + +typedef struct +{ + JSContext *c; + JSObject *array; + Bool is_dir; +} enum_dir_cbk; + +static Bool enum_dir_fct(void *cbck, char *file_name, char *file_path) +{ + char *sep; + JSString *s; + jsuint idx; + jsval v; + JSObject *obj; + enum_dir_cbk *cbk = (enum_dir_cbk*)cbck; + + obj = JS_NewObject(cbk->c, 0, 0, 0); + s = JS_NewStringCopyZ(cbk->c, file_name); + JS_DefineProperty(cbk->c, obj, "name", STRING_TO_JSVAL(s), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + sep = strrchr(file_path, '\\'); + if (!sep) sep = strrchr(file_path, '/'); + if (sep) sep[1] = 0; + s = JS_NewStringCopyZ(cbk->c, file_path); + if (sep) sep[1] = '/'; + JS_DefineProperty(cbk->c, obj, "path", STRING_TO_JSVAL(s), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(cbk->c, obj, "directory", BOOLEAN_TO_JSVAL(cbk->is_dir ? JS_TRUE : JS_FALSE), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + + JS_GetArrayLength(cbk->c, cbk->array, &idx); + v = OBJECT_TO_JSVAL(obj); + JS_SetElement(cbk->c, cbk->array, idx, &v); + return 0; +} + +static JSBool gpac_enum_directory(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + enum_dir_cbk cbk; + char *url = NULL; + char *dir = "D:\\"; + if ((argc >= 1) && JSVAL_IS_STRING(argv[0])) { + JSString *js_dir = JSVAL_TO_STRING(argv[0]); + dir = JS_GetStringBytes(js_dir); + } + if ((argc >= 2) && JSVAL_IS_BOOLEAN(argv[1])) { + if (JSVAL_TO_BOOLEAN(argv[1])==JS_TRUE) + url = gf_url_concatenate(dir, ".."); + } + cbk.c = c; + cbk.array = JS_NewArrayObject(c, 0, 0); + + cbk.is_dir = 1; + gf_enum_directory(url ? url : dir, 1, enum_dir_fct, &cbk, NULL); + cbk.is_dir = 0; + gf_enum_directory(url ? url : dir, 0, enum_dir_fct, &cbk, NULL); + *rval = OBJECT_TO_JSVAL(cbk.array); + if (url) free(url); + return JS_TRUE; +} + +static JSBool gpac_set_size(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + Bool override_size_info = 0; + u32 w, h; + jsdouble d; + GF_Terminal *term = (GF_Terminal *)JS_GetPrivate(c, obj); + if (!term) return JS_FALSE; + + w = h = 0; + if ((argc >= 1) && JSVAL_IS_NUMBER(argv[0])) { + JS_ValueToNumber(c, argv[0], &d); + w = (u32) d; + } + if ((argc >= 2) && JSVAL_IS_NUMBER(argv[1])) { + JS_ValueToNumber(c, argv[1], &d); + h = (u32) d; + } + if ((argc >= 3) && JSVAL_IS_BOOLEAN(argv[2]) && (JSVAL_TO_BOOLEAN(argv[2])==JS_TRUE) ) + override_size_info = 1; + + if (w && h) { + if (override_size_info) { + term->compositor->scene_width = w; + term->compositor->scene_height = h; + term->compositor->has_size_info = 1; + } + gf_term_set_size(term, w, h); + } + + return JS_TRUE; +} + + +static void gjs_load(GF_JSUserExtension *jsext, GF_SceneGraph *scene, JSContext *c, JSObject *global, Bool unload) +{ + GF_GPACJSExt *gjs; + JSObject *obj; + + GF_JSAPIParam par; + JSPropertySpec gpacClassProps[] = { + {0} + }; + JSFunctionSpec gpacClassFuncs[] = { + {"getOption", gpac_getOption, 3}, + {"setOption", gpac_setOption, 4}, + {"enum_directory", gpac_enum_directory, 1}, + {"set_size", gpac_set_size, 1}, + {0} + }; + + /*nothing to do on unload*/ + if (unload) return; + + if (!scene) return; + + gjs = jsext->udta; + + _SETUP_CLASS(gjs->gpacClass, "GPAC", JSCLASS_HAS_PRIVATE, gpac_getProperty, gpac_setProperty, JS_FinalizeStub); + + JS_InitClass(c, global, 0, &gjs->gpacClass, 0, 0, gpacClassProps, gpacClassFuncs, 0, 0); + obj = JS_DefineObject(c, global, "gpac", &gjs->gpacClass, 0, 0); + + if (scene->script_action) { + if (!scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_GET_TERM, scene->RootNode, &par)) + return; + JS_SetPrivate(c, obj, par.term); + } +} + + +#else +static void gjs_load(GF_JSUserExtension *jsext, GF_SceneGraph *scene, JSContext *c, JSObject *global, Bool unload) +{ +} +#endif + + + +GF_JSUserExtension *gjs_new() +{ + GF_JSUserExtension *dr; + GF_GPACJSExt *gjs; + dr = malloc(sizeof(GF_JSUserExtension)); + memset(dr, 0, sizeof(GF_JSUserExtension)); + GF_REGISTER_MODULE_INTERFACE(dr, GF_JS_USER_EXT_INTERFACE, "GPAC JavaScript Bindings", "gpac distribution"); + + GF_SAFEALLOC(gjs, GF_GPACJSExt); + dr->load = gjs_load; + dr->udta = gjs; + return dr; +} + + +void gjs_delete(GF_BaseInterface *ifce) +{ + GF_JSUserExtension *dr = (GF_JSUserExtension *) ifce; + GF_GPACJSExt *gjs = dr->udta; + free(gjs); + free(dr); +} + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_JS_USER_EXT_INTERFACE) return 1; + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_JS_USER_EXT_INTERFACE) return (GF_BaseInterface *)gjs_new(); + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_JS_USER_EXT_INTERFACE: + gjs_delete(ifce); + break; + } +} diff --git a/modules/gpac_js/gpac_js.def b/modules/gpac_js/gpac_js.def new file mode 100644 index 0000000..e339a08 --- /dev/null +++ b/modules/gpac_js/gpac_js.def @@ -0,0 +1,6 @@ +LIBRARY gm_gpac_js.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/img_in/Makefile b/modules/img_in/Makefile new file mode 100644 index 0000000..107b90c --- /dev/null +++ b/modules/img_in/Makefile @@ -0,0 +1,88 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/img_in + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +NEED_LOCAL_LIB="no" +LOCAL_LIB=../../bin/gcc +LINKLIBS= -lgpac + +#common objects +OBJS=img_dec.o img_in.o bmp_dec.o png_dec.o jpeg_dec.o + +#openjpeg config +ifeq ($(CONFIG_JP2), no) +else +OBJS+=jp2_dec.o +LINKLIBS+= -lopenjpeg +CFLAGS+=-DGPAC_HAS_JP2 +#local openjpeg lib +ifeq ($(CONFIG_JP2), local) +NEED_LOCAL_LIB="yes" +CFLAGS+= -I$(LOCAL_INC_PATH)/openjpeg +endif +endif + + +#add local lib path +ifeq ($(NEED_LOCAL_LIB), "yes") +LOCAL_LIB+=-L../../extra_lib/lib/gcc +endif + + + +SRCS := $(OBJS:.o=.c) + +LIB=gm_img_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LINKLIBS+=-lwinmm +LDFLAGS+=-export-symbols img_in.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L$(LOCAL_LIB) $(LINKLIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/img_in/bmp_dec.c b/modules/img_in/bmp_dec.c new file mode 100644 index 0000000..52f7cd3 --- /dev/null +++ b/modules/img_in/bmp_dec.c @@ -0,0 +1,206 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / image format module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "img_in.h" + +#if defined(WIN32) || defined(_WIN32_WCE) || defined(__SYMBIAN32__) +#else +#include <netinet/in.h> +#endif + +typedef struct +{ + u16 ES_ID; + u32 width, height, out_size, pixel_format; +} BMPDec; + +#define BMPCTX() BMPDec *ctx = (BMPDec *) ((IMGDec *)ifcg->privateStack)->opaque + + +static GF_Err BMP_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + BMPCTX(); + if (ctx->ES_ID && ctx->ES_ID!=esd->ESID) return GF_NOT_SUPPORTED; + if (esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; + ctx->ES_ID = esd->ESID; + return GF_OK; +} +static GF_Err BMP_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + BMPCTX(); + if (ctx->ES_ID != ES_ID) return GF_BAD_PARAM; + ctx->ES_ID = ES_ID; + return GF_OK; +} +static GF_Err BMP_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + BMPCTX(); + switch (capability->CapCode) { + case GF_CODEC_WIDTH: + capability->cap.valueInt = ctx->width; + break; + case GF_CODEC_HEIGHT: + capability->cap.valueInt = ctx->height; + break; + case GF_CODEC_STRIDE: + capability->cap.valueInt = (ctx->pixel_format == GF_PIXEL_RGB_24) ? 3 : 4; + capability->cap.valueInt *= ctx->width; + break; + case GF_CODEC_FPS: + capability->cap.valueFloat = 0; + break; + case GF_CODEC_PIXEL_FORMAT: + capability->cap.valueInt = ctx->pixel_format; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = 0; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = IMG_CM_SIZE; + break; + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 0; + break; + case GF_CODEC_PAR: + capability->cap.valueInt = 0; + break; + default: + return GF_NOT_SUPPORTED; + } + return GF_OK; +} +static GF_Err BMP_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like color space changing ...) */ + return GF_NOT_SUPPORTED; +} + +static GF_Err BMP_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + char *pix; + u32 i, j, irow, in_stride, out_stride, BPP; + GF_BitStream *bs; + BITMAPFILEHEADER fh; + BITMAPINFOHEADER fi; + + BMPCTX(); + if (inBufferLength<54) return GF_NON_COMPLIANT_BITSTREAM; + bs = gf_bs_new(inBuffer, inBufferLength, GF_BITSTREAM_READ); + +#if defined(WIN32) || defined(_WIN32_WCE) || defined(__SYMBIAN32__) + gf_bs_read_data(bs, (char *) &fh, 14); +#else + fh.bfType = gf_bs_read_u16(bs); + fh.bfSize = gf_bs_read_u32(bs); + fh.bfReserved1 = gf_bs_read_u16(bs); + fh.bfReserved2 = gf_bs_read_u16(bs); + fh.bfOffBits = gf_bs_read_u32(bs); + fh.bfOffBits = ntohl(fh.bfOffBits); +#endif + gf_bs_read_data(bs, (char *) &fi, 40); + gf_bs_del(bs); + + if ((fi.biCompression != BI_RGB) || (fi.biPlanes!=1)) return GF_NOT_SUPPORTED; + if ((fi.biBitCount!=24) && (fi.biBitCount!=32)) return GF_NOT_SUPPORTED; + + BPP = (fi.biBitCount==24) ? 3 : 4; + ctx->width = fi.biWidth; + ctx->height = fi.biHeight; + ctx->pixel_format = (fi.biBitCount==24) ? GF_PIXEL_RGB_24 : GF_PIXEL_RGBA; + + /*new cfg, reset*/ + if (ctx->out_size != ctx->width * ctx->height * BPP) { + ctx->out_size = ctx->width * ctx->height * BPP; + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; + } + out_stride = ctx->width*BPP; + in_stride = out_stride; + while (in_stride % 4) in_stride++; + + /*read*/ + if (fi.biBitCount==24) { + for (i=0; i<ctx->height; i++) { + irow = (ctx->height-1-i)*out_stride; + pix = inBuffer + fh.bfOffBits + i*in_stride; + for (j=0; j<out_stride; j+=3) { + outBuffer[j + irow] = pix[2]; + outBuffer[j+1 + irow] = pix[1]; + outBuffer[j+2 + irow] = pix[0]; + pix += 3; + } + } + } else { + for (i=0; i<ctx->height; i++) { + irow = (ctx->height-1-i)*out_stride; + pix = inBuffer + fh.bfOffBits + i*in_stride; + for (j=0; j<out_stride; j+=4) { + outBuffer[j + irow] = pix[2]; + outBuffer[j+1 + irow] = pix[1]; + outBuffer[j+2 + irow] = pix[0]; + outBuffer[j+3 + irow] = pix[3]; + pix += 4; + } + } + } + *outBufferLength = ctx->out_size; + return GF_OK; +} + +static const char *BMP_GetCodecName(GF_BaseDecoder *dec) +{ + return "BMP Decoder"; +} + +Bool NewBMPDec(GF_BaseDecoder *ifcd) +{ + IMGDec *wrap = (IMGDec *) ifcd->privateStack; + BMPDec *dec = (BMPDec *) malloc(sizeof(BMPDec)); + memset(dec, 0, sizeof(BMPDec)); + wrap->opaque = dec; + wrap->type = DEC_BMP; + + /*setup our own interface*/ + ifcd->AttachStream = BMP_AttachStream; + ifcd->DetachStream = BMP_DetachStream; + ifcd->GetCapabilities = BMP_GetCapabilities; + ifcd->SetCapabilities = BMP_SetCapabilities; + ifcd->GetName = BMP_GetCodecName; + ((GF_MediaDecoder *)ifcd)->ProcessData = BMP_ProcessData; + return 1; +} + +void DeleteBMPDec(GF_BaseDecoder *ifcg) +{ + BMPCTX(); + free(ctx); +} diff --git a/modules/img_in/img_dec.c b/modules/img_in/img_dec.c new file mode 100644 index 0000000..8493ca0 --- /dev/null +++ b/modules/img_in/img_dec.c @@ -0,0 +1,136 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / image format module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "img_in.h" + + +static Bool DEC_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + if (StreamType != GF_STREAM_VISUAL) return 0; + + switch (ObjectType) { + case 0x6D: return NewPNGDec(dec); + case 0x6C: return NewJPEGDec(dec); +#ifdef GPAC_HAS_JP2 + case 0x6E: return NewJP2Dec(dec); +#endif + case GPAC_BMP_OTI: + return NewBMPDec(dec); + case 0: + return 1;/*query for types*/ + default: +#ifdef GPAC_HAS_JP2 + if ((decSpecInfoSize>=4) && (decSpecInfo[0]=='m') && (decSpecInfo[1]=='j') && (decSpecInfo[2]=='p') && (decSpecInfo[3]=='2')) + return NewJP2Dec(dec); +#endif + return 0; + } + return 0; +} + + +GF_BaseDecoder *NewBaseDecoder() +{ + GF_MediaDecoder *ifce; + IMGDec *wrap; + GF_SAFEALLOC(ifce, GF_MediaDecoder); + if (!ifce) return NULL; + GF_SAFEALLOC(wrap, IMGDec); + if (!wrap) { + free(ifce); + return NULL; + } + ifce->privateStack = wrap; + ifce->CanHandleStream = DEC_CanHandleStream; + + GF_REGISTER_MODULE_INTERFACE(ifce, GF_MEDIA_DECODER_INTERFACE, "GPAC Image Decoder", "gpac distribution") + + /*other interfaces will be setup at run time*/ + return (GF_BaseDecoder *)ifce; +} + +void DeleteBaseDecoder(GF_BaseDecoder *ifcd) +{ + IMGDec *wrap = (IMGDec *)ifcd->privateStack; + switch (wrap->type) { + case DEC_PNG: + DeletePNGDec(ifcd); + break; + case DEC_JPEG: + DeleteJPEGDec(ifcd); + break; +#ifdef GPAC_HAS_JP2 + case DEC_JP2: + DeleteJP2Dec(ifcd); + break; +#endif + case DEC_BMP: + DeleteBMPDec(ifcd); + break; + default: + break; + } + free(wrap); + free(ifcd); +} + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: + return 1; + case GF_NET_CLIENT_INTERFACE: + return 1; + default: + return 0; + } +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: + return (GF_BaseInterface *)NewBaseDecoder(); + case GF_NET_CLIENT_INTERFACE: + return (GF_BaseInterface *)NewLoaderInterface(); + default: + return NULL; + } +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: + DeleteBaseDecoder((GF_BaseDecoder *)ifce); + break; + case GF_NET_CLIENT_INTERFACE: + DeleteLoaderInterface(ifce); + break; + } +} diff --git a/modules/img_in/img_in.c b/modules/img_in/img_in.c new file mode 100644 index 0000000..135db35 --- /dev/null +++ b/modules/img_in/img_in.c @@ -0,0 +1,364 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / image format module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "img_in.h" +#include <gpac/avparse.h> + +enum +{ + IMG_JPEG = 1, + IMG_PNG, + IMG_BMP, +}; + +typedef struct +{ + GF_ClientService *service; + /*service*/ + u32 srv_type; + + FILE *stream; + u32 img_type; + + u32 pad_bytes; + Bool done; + LPNETCHANNEL ch; + + Bool is_inline; + char *data; + u32 data_size; + + GF_SLHeader sl_hdr; + + /*file downloader*/ + GF_DownloadSession * dnload; +} IMGLoader; + + +GF_ESD *IMG_GetESD(IMGLoader *read) +{ + GF_ESD *esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = 1000; + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + esd->ESID = 1; + + if (read->img_type == IMG_BMP) + esd->decoderConfig->objectTypeIndication = GPAC_BMP_OTI; + else { + u8 OTI; + u32 mtype, w, h; + GF_BitStream *bs = gf_bs_from_file(read->stream, GF_BITSTREAM_READ); + gf_img_parse(bs, &OTI, &mtype, &w, &h, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + esd->decoderConfig->objectTypeIndication = OTI; + gf_bs_del(bs); + } + return esd; +} + +static Bool IMG_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt; + sExt = strrchr(url, '.'); + if (!sExt) return 0; + if (gf_term_check_extension(plug, "image/jpeg", "jpeg jpg", "JPEG Images", sExt)) return 1; + if (gf_term_check_extension(plug, "image/jp2", "jp2", "JPEG2000 Images", sExt)) return 1; + if (gf_term_check_extension(plug, "image/png", "png", "PNG Images", sExt)) return 1; + if (gf_term_check_extension(plug, "image/bmp", "bmp", "MS Bitmap Images", sExt)) return 1; + return 0; +} + +static Bool jp_is_local(const char *url) +{ + if (!strnicmp(url, "file://", 7)) return 1; + if (strstr(url, "://")) return 0; + return 1; +} + + +static void IMG_SetupObject(IMGLoader *read) +{ + if (!read->ch) { + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + GF_ESD *esd = IMG_GetESD(read); + od->objectDescriptorID = 1; + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(read->service, (GF_Descriptor *)od, 0); + } +} + + +void IMG_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + const char *szCache; + IMGLoader *read = (IMGLoader *) cbk; + if (!read->dnload) return; + + /*handle service message*/ + gf_term_download_update_stats(read->dnload); + + e = param->error; + /*wait to get the whole file*/ + if (!e && (param->msg_type!=GF_NETIO_DATA_TRANSFERED)) return; + + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + szCache = gf_dm_sess_get_cache_name(read->dnload); + if (!szCache) e = GF_IO_ERR; + else { + read->stream = fopen((char *) szCache, "rb"); + if (!read->stream) e = GF_SERVICE_ERROR; + else { + e = GF_OK; + fseek(read->stream, 0, SEEK_END); + read->data_size = ftell(read->stream); + fseek(read->stream, 0, SEEK_SET); + } + } + } + /*OK confirm*/ + gf_term_on_connect(read->service, NULL, e); + if (!e) IMG_SetupObject(read); +} + +void jp_download_file(GF_InputService *plug, const char *url) +{ + IMGLoader *read = (IMGLoader *) plug->priv; + + read->dnload = gf_term_download_new(read->service, url, 0, IMG_NetIO, read); + if (!read->dnload) gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + /*service confirm is done once fetched*/ +} + +static GF_Err IMG_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + char *sExt; + IMGLoader *read = (IMGLoader *)plug->priv; + + read->service = serv; + + sExt = strrchr(url, '.'); + if (!stricmp(sExt, ".jpeg") || !stricmp(sExt, ".jpg")) read->img_type = IMG_JPEG; + else if (!stricmp(sExt, ".png")) read->img_type = IMG_PNG; + else if (!stricmp(sExt, ".bmp")) read->img_type = IMG_BMP; + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + /*remote fetch*/ + if (!jp_is_local(url)) { + jp_download_file(plug, url); + return GF_OK; + } + + read->stream = fopen(url, "rb"); + if (read->stream) { + fseek(read->stream, 0, SEEK_END); + read->data_size = ftell(read->stream); + fseek(read->stream, 0, SEEK_SET); + } + gf_term_on_connect(serv, NULL, read->stream ? GF_OK : GF_URL_ERROR); + if (read->stream && read->is_inline) IMG_SetupObject(read); + return GF_OK; +} + +static GF_Err IMG_CloseService(GF_InputService *plug) +{ + IMGLoader *read = (IMGLoader *)plug->priv; + if (read->stream) fclose(read->stream); + read->stream = NULL; + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + gf_term_on_disconnect(read->service, NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *IMG_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + GF_ESD *esd; + IMGLoader *read = (IMGLoader *)plug->priv; + + /*override default*/ + if (expect_type==GF_MEDIA_OBJECT_UNDEF) expect_type=GF_MEDIA_OBJECT_VIDEO; + read->srv_type = expect_type; + + /*visual object*/ + if (expect_type==GF_MEDIA_OBJECT_VIDEO) { + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1; + esd = IMG_GetESD(read); + gf_list_add(od->ESDescriptors, esd); + return (GF_Descriptor *) od; + } + read->is_inline = 1; + return NULL; +} + +static GF_Err IMG_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ES_ID; + GF_Err e; + IMGLoader *read = (IMGLoader *)plug->priv; + + e = GF_SERVICE_ERROR; + if (read->ch==channel) goto exit; + + e = GF_STREAM_NOT_FOUND; + if (strstr(url, "ES_ID")) { + sscanf(url, "ES_ID=%d", &ES_ID); + } + /*URL setup*/ + else if (!read->ch && IMG_CanHandleURL(plug, url)) ES_ID = 1; + + if (ES_ID==1) { + read->ch = channel; + e = GF_OK; + } + +exit: + gf_term_on_connect(read->service, channel, e); + return e; +} + +static GF_Err IMG_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + GF_Err e = GF_STREAM_NOT_FOUND; + IMGLoader *read = (IMGLoader *)plug->priv; + + if (read->ch == channel) { + read->ch = NULL; + e = GF_OK; + } + gf_term_on_disconnect(read->service, channel, e); + return GF_OK; +} + +static GF_Err IMG_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + IMGLoader *read = (IMGLoader *)plug->priv; + + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + switch (com->command_type) { + case GF_NET_CHAN_SET_PADDING: + read->pad_bytes = com->pad.padding_bytes; + return GF_OK; + case GF_NET_CHAN_DURATION: + com->duration.duration = 0; + return GF_OK; + case GF_NET_CHAN_PLAY: + /*note we don't handle range since we're only dealing with images*/ + if (read->ch == com->base.on_channel) { read->done = 0; } + return GF_OK; + case GF_NET_CHAN_STOP: + return GF_OK; + default: + return GF_OK; + } +} + + +static GF_Err IMG_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + IMGLoader *read = (IMGLoader *)plug->priv; + + *out_reception_status = GF_OK; + *sl_compressed = 0; + *is_new_data = 0; + + memset(&read->sl_hdr, 0, sizeof(GF_SLHeader)); + read->sl_hdr.randomAccessPointFlag = 1; + read->sl_hdr.compositionTimeStampFlag = 1; + *out_sl_hdr = read->sl_hdr; + + /*fetching es data*/ + if (read->ch == channel) { + if (read->done) { + *out_reception_status = GF_EOS; + return GF_OK; + } + if (!read->data) { + if (!read->stream) { + *out_data_ptr = NULL; + *out_data_size = 0; + return GF_OK; + } + *is_new_data = 1; + fseek(read->stream, 0, SEEK_SET); + read->data = (char*) malloc(sizeof(char) * (read->data_size + read->pad_bytes)); + fread(read->data, sizeof(char) * read->data_size, 1, read->stream); + fseek(read->stream, 0, SEEK_SET); + if (read->pad_bytes) memset(read->data + read->data_size, 0, sizeof(char) * read->pad_bytes); + + } + *out_data_ptr = read->data; + *out_data_size = read->data_size; + return GF_OK; + } + return GF_STREAM_NOT_FOUND; +} + +static GF_Err IMG_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) +{ + IMGLoader *read = (IMGLoader *)plug->priv; + + if (read->ch == channel) { + if (!read->data) return GF_BAD_PARAM; + free(read->data); + read->data = NULL; + read->done = 1; + return GF_OK; + } + return GF_OK; +} + + +void *NewLoaderInterface() +{ + IMGLoader *priv; + GF_InputService *plug; + GF_SAFEALLOC(plug, GF_InputService); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC Image Reader", "gpac distribution") + + plug->CanHandleURL = IMG_CanHandleURL; + plug->CanHandleURLInService = NULL; + plug->ConnectService = IMG_ConnectService; + plug->CloseService = IMG_CloseService; + plug->GetServiceDescriptor = IMG_GetServiceDesc; + plug->ConnectChannel = IMG_ConnectChannel; + plug->DisconnectChannel = IMG_DisconnectChannel; + plug->ChannelGetSLP = IMG_ChannelGetSLP; + plug->ChannelReleaseSLP = IMG_ChannelReleaseSLP; + plug->ServiceCommand = IMG_ServiceCommand; + + GF_SAFEALLOC(priv, IMGLoader); + plug->priv = priv; + return plug; +} + +void DeleteLoaderInterface(void *ifce) +{ + GF_InputService *plug = (GF_InputService *) ifce; + IMGLoader *read = (IMGLoader *)plug->priv; + free(read); + free(plug); +} diff --git a/modules/img_in/img_in.def b/modules/img_in/img_in.def new file mode 100644 index 0000000..5ba5100 --- /dev/null +++ b/modules/img_in/img_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_img_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/img_in/img_in.h b/modules/img_in/img_in.h new file mode 100644 index 0000000..6f870ca --- /dev/null +++ b/modules/img_in/img_in.h @@ -0,0 +1,106 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / image format module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _IMG_IN_H +#define _IMG_IN_H + +/*all codecs are regular */ +#include <gpac/modules/codec.h> +#include <gpac/modules/service.h> +#include <gpac/constants.h> + +enum +{ + DEC_RESERVED = 0, + DEC_PNG, + DEC_JPEG, + DEC_JP2, + DEC_BMP, +}; + +typedef struct +{ + u32 type; + void *opaque; +} IMGDec; + +/*all constructors shall setup the wraper type and handle + return 1 for success, 0 otherwise +all destructors only destroy their private stacks (eg not the interface nor the wraper) +*/ +Bool NewPNGDec(GF_BaseDecoder *dec); +void DeletePNGDec(GF_BaseDecoder *dec); + +Bool NewJPEGDec(GF_BaseDecoder *dec); +void DeleteJPEGDec(GF_BaseDecoder *dec); + +#ifdef GPAC_HAS_JP2 +Bool NewJP2Dec(GF_BaseDecoder *dec); +void DeleteJP2Dec(GF_BaseDecoder *dec); +#endif + + +#if defined(WIN32) || defined(_WIN32_WCE) +#include <windows.h> +#else +typedef struct tagBITMAPFILEHEADER +{ + u16 bfType; + u32 bfSize; + u16 bfReserved1; + u16 bfReserved2; + u32 bfOffBits; +} BITMAPFILEHEADER; + +typedef struct tagBITMAPINFOHEADER{ + u32 biSize; + s32 biWidth; + s32 biHeight; + u16 biPlanes; + u16 biBitCount; + u32 biCompression; + u32 biSizeImage; + s32 biXPelsPerMeter; + s32 biYPelsPerMeter; + u32 biClrUsed; + u32 biClrImportant; +} BITMAPINFOHEADER; + +#define BI_RGB 0L + +#endif + +#define GPAC_BMP_OTI 0x82 + +Bool NewBMPDec(GF_BaseDecoder *dec); +void DeleteBMPDec(GF_BaseDecoder *dec); + +#define IMG_CM_SIZE 1 + + +void *NewLoaderInterface(); +void DeleteLoaderInterface(void *ifce); + +#endif diff --git a/modules/img_in/jp2_dec.c b/modules/img_in/jp2_dec.c new file mode 100644 index 0000000..d5bc0ca --- /dev/null +++ b/modules/img_in/jp2_dec.c @@ -0,0 +1,373 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / image format module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "img_in.h" + +#ifdef GPAC_HAS_JP2 + +#include <openjpeg.h> + +typedef struct +{ + /*no support for scalability with JPEG (progressive JPEG to test)*/ + u32 bpp, nb_comp, width, height, out_size, pixel_format, dsi_size; + char *dsi; +} JP2Dec; + +#define JP2CTX() JP2Dec *ctx = (JP2Dec *) ((IMGDec *)ifcg->privateStack)->opaque + + +static GF_Err JP2_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + GF_BitStream *bs; + JP2CTX(); + if (esd->dependsOnESID || esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; + + if (esd->decoderConfig->objectTypeIndication==0x6E) { + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + ctx->height = gf_bs_read_u32(bs); + ctx->width = gf_bs_read_u32(bs); + ctx->nb_comp = gf_bs_read_u16(bs); + ctx->bpp = 1 + gf_bs_read_u8(bs); + ctx->out_size = ctx->width * ctx->height * ctx->nb_comp /* * ctx->bpp / 8 */; + gf_bs_del(bs); + + switch (ctx->nb_comp) { + case 1: ctx->pixel_format = GF_PIXEL_GREYSCALE; break; + case 2: ctx->pixel_format = GF_PIXEL_ALPHAGREY; break; + case 3: ctx->pixel_format = GF_PIXEL_RGB_24; break; + case 4: ctx->pixel_format = GF_PIXEL_ARGB; break; + default: return GF_NOT_SUPPORTED; + } + } else { + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + gf_bs_read_u32(bs); + ctx->width = gf_bs_read_u16(bs); + ctx->height = gf_bs_read_u16(bs); + gf_bs_del(bs); + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, 12); + gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ') ); + gf_bs_write_u32(bs, 0x0D0A870A); + gf_bs_write_u32(bs, 20); + gf_bs_write_u32(bs, GF_4CC('f','t','y','p') ); + gf_bs_write_u32(bs, GF_4CC('j','p','2',' ') ); + gf_bs_write_u32(bs, 0); + gf_bs_write_u32(bs, GF_4CC('j','p','2',' ') ); + + gf_bs_write_data(bs, esd->decoderConfig->decoderSpecificInfo->data+8, esd->decoderConfig->decoderSpecificInfo->dataLength-8); + gf_bs_get_content(bs, &ctx->dsi, &ctx->dsi_size); + gf_bs_del(bs); + + ctx->nb_comp = 3; + ctx->out_size = 3*ctx->width*ctx->height/2; + ctx->pixel_format = GF_PIXEL_YV12; + } + + return GF_OK; +} +static GF_Err JP2_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + return GF_OK; +} +static GF_Err JP2_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + JP2CTX(); + switch (capability->CapCode) { + case GF_CODEC_WIDTH: + capability->cap.valueInt = ctx->width; + break; + case GF_CODEC_HEIGHT: + capability->cap.valueInt = ctx->height; + break; + case GF_CODEC_STRIDE: + if (ctx->pixel_format == GF_PIXEL_YV12) { + capability->cap.valueInt = ctx->width; + } else { + capability->cap.valueInt = ctx->width * ctx->nb_comp; + } + break; + case GF_CODEC_FPS: + capability->cap.valueFloat = 0; + break; + case GF_CODEC_PIXEL_FORMAT: + capability->cap.valueInt = ctx->pixel_format; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = 0; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = (ctx->pixel_format == GF_PIXEL_YV12) ? 4 : 1; + break; + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 4; + break; + case GF_CODEC_PAR: + capability->cap.valueInt = 0; + break; + default: + capability->cap.valueInt = 0; + return GF_NOT_SUPPORTED; + } + return GF_OK; +} +static GF_Err JP2_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like color space changing ...) */ + return GF_NOT_SUPPORTED; +} + +/** +sample error callback expecting a FILE* client object +*/ +void error_callback(const char *msg, void *client_data) +{ + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[OpenJPEG] Error %s", msg)); +} +/** +sample warning callback expecting a FILE* client object +*/ +void warning_callback(const char *msg, void *client_data) +{ + GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[OpenJPEG] Warning %s", msg)); +} +/** +sample debug callback expecting no client object +*/ +void info_callback(const char *msg, void *client_data) +{ + GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("[OpenJPEG] Info %s", msg)); +} + +/* + * Divide an integer by a power of 2 and round upwards. + * + * a divided by 2^b + */ +static int int_ceildivpow2(int a, int b) { + return (a + (1 << b) - 1) >> b; +} + +static GF_Err JP2_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + u32 i, w, wr, h, hr, wh; + opj_dparameters_t parameters; /* decompression parameters */ + opj_event_mgr_t event_mgr; /* event manager */ + opj_image_t *image = NULL; + opj_dinfo_t* dinfo = NULL; /* handle to a decompressor */ + opj_cio_t *cio = NULL; + + JP2CTX(); + +#if 1 + switch (mmlevel) { + case GF_CODEC_LEVEL_SEEK: + case GF_CODEC_LEVEL_DROP: + *outBufferLength = 0; + return GF_OK; + } +#endif + + /* configure the event callbacks (not required) */ + memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); + event_mgr.error_handler = error_callback; + event_mgr.warning_handler = warning_callback; + event_mgr.info_handler = info_callback; + + /* set decoding parameters to default values */ + opj_set_default_decoder_parameters(¶meters); + + /* get a decoder handle */ + dinfo = opj_create_decompress(CODEC_JP2); + + /* catch events using our callbacks and give a local context */ + opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr); + + /* setup the decoder decoding parameters using the current image and user parameters */ + opj_setup_decoder(dinfo, ¶meters); + + + /* open a byte stream */ + if (ctx->dsi) { + char *data; + + data = malloc(sizeof(char) * (ctx->dsi_size+inBufferLength)); + memcpy(data, ctx->dsi, ctx->dsi_size); + memcpy(data+ctx->dsi_size, inBuffer, inBufferLength); + cio = opj_cio_open((opj_common_ptr)dinfo, data, ctx->dsi_size+inBufferLength); + /* decode the stream and fill the image structure */ + image = opj_decode(dinfo, cio); + free(data); + } else { + cio = opj_cio_open((opj_common_ptr)dinfo, inBuffer, inBufferLength); + /* decode the stream and fill the image structure */ + image = opj_decode(dinfo, cio); + } + + if(!image) { + opj_destroy_decompress(dinfo); + opj_cio_close(cio); + return GF_IO_ERR; + } + + /* close the byte stream */ + opj_cio_close(cio); + + w = image->comps[0].w; + wr = int_ceildivpow2(image->comps[0].w, image->comps[0].factor); + h = image->comps[0].h; + hr = int_ceildivpow2(image->comps[0].h, image->comps[0].factor); + wh = wr*hr; + + if (ctx->nb_comp==1) { + if ((w==wr) && (h==hr)) { + for (i=0; i<wh; i++) { + outBuffer[i] = image->comps[0].data[i]; + } + } else { + for (i=0; i<wh; i++) { + outBuffer[i] = image->comps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + } + } + } + else if (ctx->nb_comp==3) { + + if ((image->comps[0].w==2*image->comps[1].w) && (image->comps[1].w==image->comps[2].w) + && (image->comps[0].h==2*image->comps[1].h) && (image->comps[1].h==image->comps[2].h)) { + + if (ctx->pixel_format != GF_PIXEL_YV12) { + ctx->pixel_format = GF_PIXEL_YV12; + ctx->out_size = 3*ctx->width*ctx->height/2; + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; + } + + if ((w==wr) && (h==hr)) { + for (i=0; i<wh; i++) { + *outBuffer = image->comps[0].data[i]; + outBuffer++; + } + w = image->comps[1].w; + wr = int_ceildivpow2(image->comps[1].w, image->comps[1].factor); + h = image->comps[1].h; + hr = int_ceildivpow2(image->comps[1].h, image->comps[1].factor); + wh = wr*hr; + for (i=0; i<wh; i++) { + *outBuffer = image->comps[1].data[i]; + outBuffer++; + } + for (i=0; i<wh; i++) { + *outBuffer = image->comps[2].data[i]; + outBuffer++; + } + } else { + for (i=0; i<wh; i++) { + *outBuffer = image->comps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + } + w = image->comps[1].w; + wr = int_ceildivpow2(image->comps[1].w, image->comps[1].factor); + h = image->comps[1].h; + hr = int_ceildivpow2(image->comps[1].h, image->comps[1].factor); + wh = wr*hr; + for (i=0; i<wh; i++) { + *outBuffer = image->comps[1].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + } + for (i=0; i<wh; i++) { + *outBuffer = image->comps[2].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + } + } + + + } else if ((image->comps[0].w==image->comps[1].w) && (image->comps[1].w==image->comps[2].w) + && (image->comps[0].h==image->comps[1].h) && (image->comps[1].h==image->comps[2].h)) { + + if ((w==wr) && (h==hr)) { + for (i=0; i<wh; i++) { + u32 idx = 3*i; + outBuffer[idx] = image->comps[0].data[i]; + outBuffer[idx+1] = image->comps[1].data[i]; + outBuffer[idx+2] = image->comps[2].data[i]; + } + } else { + for (i=0; i<wh; i++) { + u32 idx = 3*i; + outBuffer[idx] = image->comps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + outBuffer[idx+1] = image->comps[1].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + outBuffer[idx+2] = image->comps[2].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + } + } + } + } + + /* free remaining structures */ + if(dinfo) { + opj_destroy_decompress(dinfo); + } + + /* free image data structure */ + opj_image_destroy(image); + + *outBufferLength = ctx->out_size; + return GF_OK; +} + +static const char *JP2_GetCodecName(GF_BaseDecoder *dec) +{ + return "OpenJPEG "OPENJPEG_VERSION ; +} + + +Bool NewJP2Dec(GF_BaseDecoder *ifcd) +{ + IMGDec *wrap = (IMGDec *) ifcd->privateStack; + JP2Dec *dec = (JP2Dec *) malloc(sizeof(JP2Dec)); + memset(dec, 0, sizeof(JP2Dec)); + wrap->opaque = dec; + wrap->type = DEC_JPEG; + + /*setup our own interface*/ + ifcd->AttachStream = JP2_AttachStream; + ifcd->DetachStream = JP2_DetachStream; + ifcd->GetCapabilities = JP2_GetCapabilities; + ifcd->SetCapabilities = JP2_SetCapabilities; + ifcd->GetName = JP2_GetCodecName; + ((GF_MediaDecoder *)ifcd)->ProcessData = JP2_ProcessData; + return 1; +} + +void DeleteJP2Dec(GF_BaseDecoder *ifcg) +{ + JP2CTX(); + free(ctx); +} + +#endif diff --git a/modules/img_in/jpeg_dec.c b/modules/img_in/jpeg_dec.c new file mode 100644 index 0000000..59d976c --- /dev/null +++ b/modules/img_in/jpeg_dec.c @@ -0,0 +1,144 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / image format module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "img_in.h" +#include <gpac/avparse.h> + +typedef struct +{ + /*no support for scalability with JPEG (progressive JPEG to test)*/ + u16 ES_ID; + u32 BPP, width, height, out_size, pixel_format; +} JPEGDec; + +#define JPEGCTX() JPEGDec *ctx = (JPEGDec *) ((IMGDec *)ifcg->privateStack)->opaque + +static GF_Err JPEG_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + JPEGCTX(); + if (ctx->ES_ID && ctx->ES_ID!=esd->ESID) return GF_NOT_SUPPORTED; + ctx->ES_ID = esd->ESID; + ctx->BPP = 3; + return GF_OK; +} +static GF_Err JPEG_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + JPEGCTX(); + if (ctx->ES_ID != ES_ID) return GF_BAD_PARAM; + ctx->ES_ID = ES_ID; + return GF_OK; +} +static GF_Err JPEG_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + JPEGCTX(); + switch (capability->CapCode) { + case GF_CODEC_WIDTH: + capability->cap.valueInt = ctx->width; + break; + case GF_CODEC_HEIGHT: + capability->cap.valueInt = ctx->height; + break; + case GF_CODEC_STRIDE: + capability->cap.valueInt = ctx->width * ctx->BPP; + break; + case GF_CODEC_FPS: + capability->cap.valueFloat = 0; + break; + case GF_CODEC_PIXEL_FORMAT: + capability->cap.valueInt = ctx->pixel_format; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = 0; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = IMG_CM_SIZE; + break; + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 4; + break; + case GF_CODEC_PAR: + capability->cap.valueInt = 0; + break; + default: + capability->cap.valueInt = 0; + return GF_NOT_SUPPORTED; + } + return GF_OK; +} +static GF_Err JPEG_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like color space changing ...) */ + return GF_NOT_SUPPORTED; +} + +static GF_Err JPEG_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + GF_Err e; + JPEGCTX(); + + e = gf_img_jpeg_dec(inBuffer, inBufferLength, &ctx->width, &ctx->height, &ctx->pixel_format, outBuffer, outBufferLength, ctx->BPP); + switch (ctx->pixel_format) { + case GF_PIXEL_GREYSCALE: ctx->BPP = 1; break; + case GF_PIXEL_RGB_24: ctx->BPP = 3; break; + } + ctx->out_size = *outBufferLength; + return e; +} + +static const char *JPEG_GetCodecName(GF_BaseDecoder *dec) +{ + return "JPEG 6b IJG"; +} + + +Bool NewJPEGDec(GF_BaseDecoder *ifcd) +{ + IMGDec *wrap = (IMGDec *) ifcd->privateStack; + JPEGDec *dec = (JPEGDec *) malloc(sizeof(JPEGDec)); + memset(dec, 0, sizeof(JPEGDec)); + wrap->opaque = dec; + wrap->type = DEC_JPEG; + + /*setup our own interface*/ + ifcd->AttachStream = JPEG_AttachStream; + ifcd->DetachStream = JPEG_DetachStream; + ifcd->GetCapabilities = JPEG_GetCapabilities; + ifcd->SetCapabilities = JPEG_SetCapabilities; + ifcd->GetName = JPEG_GetCodecName; + ((GF_MediaDecoder *)ifcd)->ProcessData = JPEG_ProcessData; + return 1; +} + +void DeleteJPEGDec(GF_BaseDecoder *ifcg) +{ + JPEGCTX(); + free(ctx); +} diff --git a/modules/img_in/png_dec.c b/modules/img_in/png_dec.c new file mode 100644 index 0000000..e22ca11 --- /dev/null +++ b/modules/img_in/png_dec.c @@ -0,0 +1,160 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / image format module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "img_in.h" +#include <gpac/avparse.h> + +typedef struct +{ + u16 ES_ID; + u32 BPP, width, height, out_size, pixel_format; + Bool is_depth; +} PNGDec; + +#define PNGCTX() PNGDec *ctx = (PNGDec *) ((IMGDec *)ifcg->privateStack)->opaque + + +static GF_Err PNG_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + u32 i = 0; + GF_Descriptor *d = NULL; + PNGCTX(); + if (ctx->ES_ID && ctx->ES_ID!=esd->ESID) return GF_NOT_SUPPORTED; + ctx->ES_ID = esd->ESID; + + while ((d = gf_list_enum(esd->extensionDescriptors, &i))) { + if (d->tag == GF_ODF_AUX_VIDEO_DATA) { + ctx->is_depth = 1; + break; + } + } + return GF_OK; +} +static GF_Err PNG_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + PNGCTX(); + if (ctx->ES_ID != ES_ID) return GF_BAD_PARAM; + ctx->ES_ID = ES_ID; + return GF_OK; +} +static GF_Err PNG_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + PNGCTX(); + switch (capability->CapCode) { + case GF_CODEC_WIDTH: + capability->cap.valueInt = ctx->width; + break; + case GF_CODEC_HEIGHT: + capability->cap.valueInt = ctx->height; + break; + case GF_CODEC_STRIDE: + capability->cap.valueInt = ctx->width * ctx->BPP; + break; + case GF_CODEC_FPS: + capability->cap.valueFloat = 0; + break; + case GF_CODEC_PIXEL_FORMAT: + capability->cap.valueInt = ctx->pixel_format; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = 0; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = IMG_CM_SIZE; + break; + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 0; + break; + case GF_CODEC_PAR: + capability->cap.valueInt = 0; + break; + default: + return GF_NOT_SUPPORTED; + } + return GF_OK; +} +static GF_Err PNG_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like color space changing ...) */ + return GF_NOT_SUPPORTED; +} + + +static GF_Err PNG_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + GF_Err e; + PNGCTX(); + + e = gf_img_png_dec(inBuffer, inBufferLength, &ctx->width, &ctx->height, &ctx->pixel_format, outBuffer, outBufferLength); + + switch (ctx->pixel_format) { + case GF_PIXEL_GREYSCALE: ctx->BPP = 1; break; + case GF_PIXEL_ALPHAGREY: ctx->BPP = 2; break; + case GF_PIXEL_RGB_24: ctx->BPP = 3; break; + case GF_PIXEL_RGBA: + case GF_PIXEL_RGBD: + ctx->BPP = 4; + if (ctx->is_depth) ctx->pixel_format = GF_PIXEL_RGBD; + break; + } + ctx->out_size = *outBufferLength; + return e; +} + +static const char *PNG_GetCodecName(GF_BaseDecoder *dec) +{ + return "LibPNG"; +} + +Bool NewPNGDec(GF_BaseDecoder *ifcd) +{ + IMGDec *wrap = (IMGDec *) ifcd->privateStack; + PNGDec *dec = (PNGDec *) malloc(sizeof(PNGDec)); + memset(dec, 0, sizeof(PNGDec)); + wrap->opaque = dec; + wrap->type = DEC_PNG; + + /*setup our own interface*/ + ifcd->AttachStream = PNG_AttachStream; + ifcd->DetachStream = PNG_DetachStream; + ifcd->GetCapabilities = PNG_GetCapabilities; + ifcd->SetCapabilities = PNG_SetCapabilities; + ifcd->GetName = PNG_GetCodecName; + ((GF_MediaDecoder *)ifcd)->ProcessData = PNG_ProcessData; + return 1; +} + +void DeletePNGDec(GF_BaseDecoder *ifcg) +{ + PNGCTX(); + free(ctx); +} diff --git a/modules/ismacryp/Makefile b/modules/ismacryp/Makefile new file mode 100644 index 0000000..da5c6fd --- /dev/null +++ b/modules/ismacryp/Makefile @@ -0,0 +1,62 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/ismacryp + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS=ismacryp.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_ismacryp.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols ismacryp.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/ismacryp/ismacryp.c b/modules/ismacryp/ismacryp.c new file mode 100644 index 0000000..fea7d12 --- /dev/null +++ b/modules/ismacryp/ismacryp.c @@ -0,0 +1,353 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / LASeR decoder module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/modules/ipmp.h> +#include <gpac/crypt.h> +#include <gpac/ismacryp.h> +#include <gpac/base_coding.h> +#include <gpac/download.h> +#include <gpac/internal/terminal_dev.h> + + +#define OMA_DRM_MP4MC + + +enum +{ + ISMAEA_STATE_ERROR, + ISMAEA_STATE_SETUP, + ISMAEA_STATE_PLAY, +}; + +typedef struct +{ + GF_Crypt *crypt; + char key[16], salt[8]; + u64 last_IV; + u32 state; + u32 nb_allow_play; + Bool is_oma; + u32 preview_range; +} ISMAEAPriv; + + +static void ISMA_KMS_NetIO(void *cbck, GF_NETIO_Parameter *par) +{ +} + +static GF_Err ISMA_GetGPAC_KMS(ISMAEAPriv *priv, GF_Channel *ch, const char *kms_url) +{ + GF_Err e; + FILE *t; + GF_DownloadSession * sess; + if (!strnicmp(kms_url, "(ipmp)", 6)) return GF_NOT_SUPPORTED; + else if (!strnicmp(kms_url, "(uri)", 5)) kms_url += 5; + else if (!strnicmp(kms_url, "file://", 7)) kms_url += 7; + + e = GF_OK; + /*try local*/ + t = (strstr(kms_url, "://") == NULL) ? fopen(kms_url, "r") : NULL; + if (t) { + fclose(t); + return gf_ismacryp_gpac_get_info(ch->esd->ESID, (char *)kms_url, priv->key, priv->salt); + } + /*note that gpac doesn't have TLS support -> not really usefull. As a general remark, ISMACryp + is supported as a proof of concept, crypto and IPMP being the last priority on gpac...*/ + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[ISMA E&A] Fetching ISMACryp key for channel %d\n", ch->esd->ESID) ); + + sess = gf_term_download_new(ch->service, kms_url, 0, ISMA_KMS_NetIO, ch); + if (!sess) return GF_IO_ERR; + + while (1) { + e = gf_dm_sess_get_stats(sess, NULL, NULL, NULL, NULL, NULL, NULL); + if (e) break; + } + if (e==GF_EOS) { + e = gf_ismacryp_gpac_get_info(ch->esd->ESID, (char *) gf_dm_sess_get_cache_name(sess), priv->key, priv->salt); + } + gf_term_download_del(sess); + return e; +} + + +static GF_Err ISMA_Setup(ISMAEAPriv *priv, GF_IPMPEvent *evt) +{ + GF_Err e; + GF_ISMACrypConfig *cfg = (GF_ISMACrypConfig*)evt->config_data; + + priv->state = ISMAEA_STATE_ERROR; + + if (cfg->scheme_type != GF_4CC('i','A','E','C')) return GF_NOT_SUPPORTED; + if (cfg->scheme_version != 1) return GF_NOT_SUPPORTED; + + if (!cfg->kms_uri) return GF_NON_COMPLIANT_BITSTREAM; + + /*try to fetch the keys*/ + /*base64 inband encoding*/ + if (!strnicmp(cfg->kms_uri, "(key)", 5)) { + char data[100]; + gf_base64_decode((char*)cfg->kms_uri+5, strlen(cfg->kms_uri)-5, data, 100); + memcpy(priv->key, data, sizeof(char)*16); + memcpy(priv->salt, data+16, sizeof(char)*8); + } + /*hexadecimal inband encoding*/ + else if (!strnicmp(cfg->kms_uri, "(key-hexa)", 10)) { + u32 v; + char szT[3], *k; + u32 i; + szT[2] = 0; + if (strlen(cfg->kms_uri) < 10+32+16) return GF_NON_COMPLIANT_BITSTREAM; + + k = (char *)cfg->kms_uri + 10; + for (i=0; i<16; i++) { + szT[0] = k[2*i]; szT[1] = k[2*i + 1]; + sscanf(szT, "%X", &v); + priv->key[i] = v; + } + + k = (char *)cfg->kms_uri + 10 + 32; + for (i=0; i<8; i++) { + szT[0] = k[2*i]; szT[1] = k[2*i + 1]; + sscanf(szT, "%X", &v); + priv->salt[i] = v; + } + } + /*MPEG4-IP KMS*/ + else if (!stricmp(cfg->kms_uri, "AudioKey") || !stricmp(cfg->kms_uri, "VideoKey")) { + if (!gf_ismacryp_mpeg4ip_get_info((char *) cfg->kms_uri, priv->key, priv->salt)) { + return GF_BAD_PARAM; + } + } + /*gpac default scheme is used, fetch file from KMS and load keys*/ + else if (cfg->scheme_uri && !stricmp(cfg->scheme_uri, "urn:gpac:isma:encryption_scheme")) { + e = ISMA_GetGPAC_KMS(priv, evt->channel, cfg->kms_uri); + if (e) return e; + } + /*hardcoded keys*/ + else { + static u8 mysalt[] = { 8,7,6,5,4,3,2,1, 0,0,0,0,0,0,0,0 }; + static u8 mykey[][16] = { + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 } }; + memcpy(priv->salt, mysalt, sizeof(char)*8); + memcpy(priv->key, mykey, sizeof(char)*16); + } + priv->state = ISMAEA_STATE_SETUP; + //priv->nb_allow_play = 1; + return GF_OK; +} + +static GF_Err ISMA_Access(ISMAEAPriv *priv, GF_IPMPEvent *evt) +{ + GF_Err e; + char IV[16]; + + if (evt->event_type==GF_IPMP_TOOL_GRANT_ACCESS) { + if (priv->state != ISMAEA_STATE_SETUP) return GF_SERVICE_ERROR; + assert(!priv->crypt); + + //if (!priv->nb_allow_play) return GF_AUTHENTICATION_FAILURE; + //priv->nb_allow_play--; + + /*init decrypter*/ + priv->crypt = gf_crypt_open("AES-128", "CTR"); + if (!priv->crypt) return GF_IO_ERR; + + memset(IV, 0, sizeof(char)*16); + memcpy(IV, priv->salt, sizeof(char)*8); + e = gf_crypt_init(priv->crypt, priv->key, 16, IV); + if (e) return e; + + priv->state = ISMAEA_STATE_PLAY; + return GF_OK; + } + if (evt->event_type==GF_IPMP_TOOL_RELEASE_ACCESS) { + if (priv->state != ISMAEA_STATE_PLAY) return GF_SERVICE_ERROR; + if (priv->crypt) gf_crypt_close(priv->crypt); + priv->crypt = NULL; + priv->state = ISMAEA_STATE_SETUP; + return GF_OK; + } + return GF_BAD_PARAM; +} + +static GF_Err ISMA_ProcessData(ISMAEAPriv *priv, GF_IPMPEvent *evt) +{ + if (!priv->crypt) return GF_SERVICE_ERROR; + + if (!evt->is_encrypted) return GF_OK; + + /*resync IV*/ + if (!priv->last_IV || (priv->last_IV != evt->isma_BSO)) { + char IV[17]; + u64 count; + u32 remain; + GF_BitStream *bs; + count = evt->isma_BSO / 16; + remain = (u32) (evt->isma_BSO % 16); + + /*format IV to begin of counter*/ + bs = gf_bs_new(IV, 17, GF_BITSTREAM_WRITE); + gf_bs_write_u8(bs, 0); /*begin of counter*/ + gf_bs_write_data(bs, priv->salt, 8); + gf_bs_write_u64(bs, (s64) count); + gf_bs_del(bs); + gf_crypt_set_state(priv->crypt, IV, 17); + + /*decrypt remain bytes*/ + if (remain) { + char dummy[20]; + gf_crypt_decrypt(priv->crypt, dummy, remain); + } + priv->last_IV = evt->isma_BSO; + } + /*decrypt*/ + gf_crypt_decrypt(priv->crypt, evt->data, evt->data_size); + priv->last_IV += evt->data_size; + return GF_OK; +} + + +#ifdef OMA_DRM_MP4MC +static GF_Err OMA_DRM_Setup(ISMAEAPriv *priv, GF_IPMPEvent *evt) +{ + u32 hdr_pos; + GF_OMADRM2Config *cfg = (GF_OMADRM2Config*)evt->config_data; + + priv->state = ISMAEA_STATE_ERROR; + + if (cfg->scheme_type != GF_4CC('o','d','k','m')) return GF_NOT_SUPPORTED; + if (cfg->scheme_version != 0x00000200) return GF_NOT_SUPPORTED; + + hdr_pos = 0; + while (hdr_pos<cfg->oma_drm_textual_headers_len) { + u32 len; + char *sep; + if (!strncmp(cfg->oma_drm_textual_headers + hdr_pos, "PreviewRange", 12)) { + sep = strchr(cfg->oma_drm_textual_headers + hdr_pos, ':'); + if (sep) priv->preview_range = atoi(sep+1); + } + len = strlen(cfg->oma_drm_textual_headers + hdr_pos); + hdr_pos += len+1; + } + priv->is_oma = 1; + + /*TODO: call DRM agent, fetch keys*/ + if (!cfg->kms_uri) return GF_NON_COMPLIANT_BITSTREAM; + priv->state = ISMAEA_STATE_SETUP; + //priv->nb_allow_play = 1; + + /*we have preview*/ + if (priv->preview_range) return GF_OK; + return GF_NOT_SUPPORTED; +} +#endif + +static GF_Err ISMA_Process(GF_IPMPTool *plug, GF_IPMPEvent *evt) +{ + ISMAEAPriv *priv = (ISMAEAPriv *)plug->udta; + + switch (evt->event_type) { + case GF_IPMP_TOOL_SETUP: + if (evt->config_data_code == GF_4CC('i','s','m','a')) return ISMA_Setup(priv, evt); +#ifdef OMA_DRM_MP4MC + if (evt->config_data_code == GF_4CC('o','d','r','m')) return OMA_DRM_Setup(priv, evt); +#endif + return GF_NOT_SUPPORTED; + + case GF_IPMP_TOOL_GRANT_ACCESS: + case GF_IPMP_TOOL_RELEASE_ACCESS: + if (priv->is_oma) { + } else { + return ISMA_Access(priv, evt); + } + break; + case GF_IPMP_TOOL_PROCESS_DATA: + if (priv->is_oma) { + if (evt->is_encrypted) { + evt->restart_requested = 1; + return GF_EOS; + } + return GF_OK; + } + return ISMA_ProcessData(priv, evt); + } + return GF_OK; +} + +void DeleteISMACrypTool(GF_IPMPTool *plug) +{ + ISMAEAPriv *priv = (ISMAEAPriv *)plug->udta; + /*in case something went wrong*/ + if (priv->crypt) gf_crypt_close(priv->crypt); + free(priv); + free(plug); +} + +GF_IPMPTool *NewISMACrypTool() +{ + ISMAEAPriv *priv; + GF_IPMPTool *tmp; + + GF_SAFEALLOC(tmp, GF_IPMPTool); + if (!tmp) return NULL; + GF_SAFEALLOC(priv, ISMAEAPriv); + tmp->udta = priv; + tmp->process = ISMA_Process; + GF_REGISTER_MODULE_INTERFACE(tmp, GF_IPMP_TOOL_INTERFACE, "GPAC ISMACryp tool", "gpac distribution") + return (GF_IPMPTool *) tmp; +} + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_IPMP_TOOL_INTERFACE: + return 1; + default: + return 0; + } +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_IPMP_TOOL_INTERFACE: + return (GF_BaseInterface *)NewISMACrypTool(); + default: + return NULL; + } +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_IPMP_TOOL_INTERFACE: + DeleteISMACrypTool((GF_IPMPTool *)ifce); + break; + } +} diff --git a/modules/ismacryp/ismacryp.def b/modules/ismacryp/ismacryp.def new file mode 100644 index 0000000..e6966dc --- /dev/null +++ b/modules/ismacryp/ismacryp.def @@ -0,0 +1,6 @@ +LIBRARY gm_ismacryp.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/isom_in/Makefile b/modules/isom_in/Makefile new file mode 100644 index 0000000..20690da --- /dev/null +++ b/modules/isom_in/Makefile @@ -0,0 +1,62 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/isom_in + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= load.o read.o read_ch.o cache.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_isom_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols isom_in.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) -L../../bin/gcc -lgpac $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/isom_in/cache.c b/modules/isom_in/cache.c new file mode 100644 index 0000000..aefebfa --- /dev/null +++ b/modules/isom_in/cache.c @@ -0,0 +1,267 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MP4 cache module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "isom_in.h" + +#ifndef GPAC_READ_ONLY + +static GF_Err ISOW_Open(GF_StreamingCache *mc, GF_ClientService *serv, const char *location_and_name, Bool keep_existing_files) +{ + char szRoot[GF_MAX_PATH], szPath[GF_MAX_PATH], *ext; + ISOMReader *cache = (ISOMReader *)mc->priv; + if (cache->mov || cache->service) return GF_BAD_PARAM; + + strcpy(szRoot, location_and_name); + ext = strrchr(szRoot, '.'); + if (ext) ext[0] = 0; + strcpy(szPath, szRoot); + strcat(szPath, ".mp4"); + if (keep_existing_files) { + FILE *f = fopen(szPath, "rb"); + if (f) { + u32 i=0; + fclose(f); + while (1) { + sprintf(szPath, "%s_%04d.mp4", szRoot, i); + f = fopen(szPath, "rb"); + if (!f) break; + fclose(f); + i++; + } + } + } + + /*create a new movie in write mode (eg no editing)*/ + cache->mov = gf_isom_open(szPath, GF_ISOM_OPEN_WRITE, NULL); + if (!cache->mov) return gf_isom_last_error(NULL); + cache->service = serv; + return GF_OK; +} + +static GF_Err ISOW_Close(GF_StreamingCache *mc, Bool delete_cache) +{ + GF_Err e; + ISOMReader *cache = (ISOMReader *)mc->priv; + if (!cache->mov || !cache->service) return GF_BAD_PARAM; + + while (gf_list_count(cache->channels)) { + ISOMChannel *ch = (ISOMChannel *)gf_list_get(cache->channels, 0); + gf_list_rem(cache->channels, 0); + if (ch->cache_sample) { + gf_isom_add_sample(cache->mov, ch->track, 1, ch->cache_sample); + gf_isom_sample_del(&ch->cache_sample); + } + free(ch); + } + if (delete_cache) { + gf_isom_delete(cache->mov); + e = GF_OK; + } else { + e = gf_isom_close(cache->mov); + } + cache->mov = NULL; + cache->service = NULL; + return e; +} +static GF_Err ISOW_Write(GF_StreamingCache *mc, LPNETCHANNEL ch, char *data, u32 data_size, GF_SLHeader *sl_hdr) +{ + ISOMChannel *mch; + GF_ESD *esd; + u32 di, mtype; + u64 DTS, CTS; + ISOMReader *cache = (ISOMReader *)mc->priv; + if (!cache->mov || !cache->service) return GF_BAD_PARAM; + + mch = isor_get_channel(cache, ch); + if (!mch) { + Bool mapped; + GF_NetworkCommand com; + com.base.on_channel = ch; + com.base.command_type = GF_NET_CHAN_GET_ESD; + gf_term_on_command(cache->service, &com, GF_OK); + if (!com.cache_esd.esd) return GF_SERVICE_ERROR; + + esd = (GF_ESD *)com.cache_esd.esd; + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: mtype = GF_ISOM_MEDIA_OD; break; + case GF_STREAM_SCENE: mtype = GF_ISOM_MEDIA_SCENE; break; + case GF_STREAM_VISUAL: mtype = GF_ISOM_MEDIA_VISUAL; break; + case GF_STREAM_AUDIO: mtype = GF_ISOM_MEDIA_AUDIO; break; + case GF_STREAM_MPEG7: mtype = GF_ISOM_MEDIA_MPEG7; break; + case GF_STREAM_OCI: mtype = GF_ISOM_MEDIA_OCI; break; + case GF_STREAM_IPMP: mtype = GF_ISOM_MEDIA_IPMP; break; + case GF_STREAM_MPEGJ: mtype = GF_ISOM_MEDIA_MPEGJ; break; + case GF_STREAM_TEXT: mtype = GF_ISOM_MEDIA_TEXT; break; + default: return GF_NOT_SUPPORTED; + } + GF_SAFEALLOC(mch, ISOMChannel); + mch->time_scale = esd->slConfig->timestampResolution; + mch->streamType = esd->decoderConfig->streamType; + mch->track = gf_isom_new_track(cache->mov, com.cache_esd.esd->ESID, mtype, mch->time_scale); + mch->is_playing = 1; + mch->channel = ch; + mch->owner = cache; + gf_isom_set_track_enabled(cache->mov, mch->track, 1); + /*translate 3GP streams to MP4*/ + mapped = 0; + if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_MEDIA_GENERIC) { + char szCode[5]; + strncpy(szCode, esd->decoderConfig->decoderSpecificInfo->data, 4); + szCode[4]=0; + if (!stricmp(szCode, "samr") || !stricmp(szCode, "amr ") || !stricmp(szCode, "sawb")) { + GF_3GPConfig amrc; + mapped = 1; + memset(&amrc, 0, sizeof(GF_3GPConfig)); + + amrc.frames_per_sample = (u32) esd->decoderConfig->decoderSpecificInfo->data[13]; + amrc.type = (!stricmp(szCode, "sawb")) ? GF_ISOM_SUBTYPE_3GP_AMR_WB : GF_ISOM_SUBTYPE_3GP_AMR; + amrc.vendor = GF_4CC('G','P','A','C'); + gf_isom_3gp_config_new(cache->mov, mch->track, &amrc, NULL, NULL, &di); + } else if (!stricmp(szCode, "h263")) { + GF_3GPConfig h263c; + memset(&h263c, 0, sizeof(GF_3GPConfig)); + h263c.type = GF_ISOM_SUBTYPE_3GP_H263; + h263c.vendor = GF_4CC('G','P','A','C'); + gf_isom_3gp_config_new(cache->mov, mch->track, &h263c, NULL, NULL, &di); + mapped = 1; + } + } + if (!mapped) gf_isom_new_mpeg4_description(cache->mov, mch->track, esd, NULL, NULL, &di); + if (com.cache_esd.is_iod_stream) gf_isom_add_track_to_root_od(cache->mov, mch->track); + gf_list_add(cache->channels, mch); + } + + /*first sample, cache it*/ + if (!mch->cache_sample) { + mch->cache_seed_ts = sl_hdr->decodingTimeStamp; + mch->cache_sample = gf_isom_sample_new(); + mch->cache_sample->IsRAP = sl_hdr->randomAccessPointFlag; + mch->cache_sample->dataLength = data_size; + mch->cache_sample->data = (char*)malloc(sizeof(char)*data_size); + memcpy(mch->cache_sample->data, data, sizeof(char)*data_size); + return GF_OK; + } + + /*adjust DTS/CTS*/ + DTS = sl_hdr->decodingTimeStamp - mch->cache_seed_ts; + + if ((mch->streamType==GF_STREAM_VISUAL) && (DTS<=mch->cache_sample->DTS)) { + assert(DTS>mch->prev_dts); + CTS = mch->cache_sample->DTS + mch->cache_sample->CTS_Offset; + mch->cache_sample->CTS_Offset = 0; + + /*first time, shift all CTS*/ + if (!mch->frame_cts_offset) { + u32 i, count = gf_isom_get_sample_count(cache->mov, mch->track); + mch->frame_cts_offset = (u32) (DTS-mch->prev_dts); + for (i=0; i<count; i++) { + gf_isom_modify_cts_offset(cache->mov, mch->track, i+1, mch->frame_cts_offset); + } + mch->cache_sample->CTS_Offset += mch->frame_cts_offset; + } + mch->cache_sample->DTS = mch->prev_dts + mch->frame_cts_offset; + mch->cache_sample->CTS_Offset += (u32) (CTS-mch->cache_sample->DTS); + } + /*deal with reference picture insertion: if no CTS offset and biggest CTS until now, this is + a reference insertion - we must check that in order to make sure we have strictly increasing DTSs*/ + if (mch->max_cts && !mch->cache_sample->CTS_Offset && (mch->cache_sample->DTS+mch->cache_sample->CTS_Offset > mch->max_cts)) { + assert(mch->cache_sample->DTS > mch->prev_dts + mch->frame_cts_offset); + CTS = mch->cache_sample->DTS + mch->cache_sample->CTS_Offset; + mch->cache_sample->DTS = mch->prev_dts + mch->frame_cts_offset; + mch->cache_sample->CTS_Offset = (u32) (CTS-mch->cache_sample->DTS); + } + if (mch->cache_sample->CTS_Offset) + mch->max_cts = mch->cache_sample->DTS+mch->cache_sample->CTS_Offset; + + /*add cache*/ + gf_isom_add_sample(cache->mov, mch->track, 1, mch->cache_sample); + assert(!mch->prev_dts || (mch->prev_dts < mch->cache_sample->DTS)); + mch->prev_dts = mch->cache_sample->DTS; + mch->duration = MAX(mch->max_cts, mch->prev_dts); + gf_isom_sample_del(&mch->cache_sample); + + /*store sample*/ + mch->cache_sample = gf_isom_sample_new(); + mch->cache_sample->IsRAP = sl_hdr->randomAccessPointFlag; + mch->cache_sample->DTS = DTS + mch->frame_cts_offset; + mch->cache_sample->CTS_Offset = (u32) (sl_hdr->compositionTimeStamp - mch->cache_seed_ts - DTS); + mch->cache_sample->dataLength = data_size; + mch->cache_sample->data = (char*)malloc(sizeof(char)*data_size); + memcpy(mch->cache_sample->data, data, sizeof(char)*data_size); + return GF_OK; +} +static GF_Err ISOW_ServiceCommand(GF_StreamingCache *mc, GF_NetworkCommand *com) +{ + ISOMReader *cache = (ISOMReader *)mc->priv; + if (!cache->mov || !cache->service) return GF_BAD_PARAM; + + return GF_OK; +} +static GF_Err ISOW_ChannelGetSLP(GF_StreamingCache *mc, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + ISOMReader *cache = (ISOMReader *)mc->priv; + if (!cache->mov || !cache->service) return GF_BAD_PARAM; + + return GF_OK; +} +static GF_Err ISOW_ChannelReleaseSLP(GF_StreamingCache *mc, LPNETCHANNEL channel) +{ + ISOMReader *cache = (ISOMReader *)mc->priv; + if (!cache->mov || !cache->service) return GF_BAD_PARAM; + + return GF_OK; +} + + +GF_BaseInterface *isow_load_cache() +{ + ISOMReader *cache; + GF_StreamingCache *plug; + GF_SAFEALLOC(plug, GF_StreamingCache); + GF_REGISTER_MODULE_INTERFACE(plug, GF_STREAMING_MEDIA_CACHE, "GPAC IsoMedia Cache", "gpac distribution") + + plug->Open = ISOW_Open; + plug->Close = ISOW_Close; + plug->Write = ISOW_Write; + plug->ChannelGetSLP = ISOW_ChannelGetSLP; + plug->ChannelReleaseSLP = ISOW_ChannelReleaseSLP; + plug->ServiceCommand = ISOW_ServiceCommand; + + GF_SAFEALLOC(cache, ISOMReader); + cache->channels = gf_list_new(); + plug->priv = cache; + return (GF_BaseInterface *) plug; +} + +void isow_delete_cache(GF_BaseInterface *bi) +{ + GF_StreamingCache *mc = (GF_StreamingCache*) bi; + ISOMReader *cache = (ISOMReader *)mc->priv; + gf_list_del(cache->channels); + free(cache); + free(bi); +} + +#endif + diff --git a/modules/isom_in/isom_in.def b/modules/isom_in/isom_in.def new file mode 100644 index 0000000..cdff88c --- /dev/null +++ b/modules/isom_in/isom_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_isom_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/isom_in/isom_in.h b/modules/isom_in/isom_in.h new file mode 100644 index 0000000..50c0575 --- /dev/null +++ b/modules/isom_in/isom_in.h @@ -0,0 +1,116 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MP4 reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _ISMO_IN_H_ +#define _ISMO_IN_H_ + +#include <gpac/modules/service.h> +#include <gpac/media_tools.h> +#include <gpac/constants.h> + +/* + reader module + +*/ + +typedef struct +{ + /*the service we're responsible for*/ + GF_ClientService *service; + + /*current channels*/ + GF_List *channels; + + /*input file*/ + GF_ISOFile *mov; + u32 time_scale; + + /*remote file handling*/ + GF_DownloadSession * dnload; + u64 missing_bytes, last_size; + + Bool no_service_desc; + u32 base_track_id; +} ISOMReader; + + +typedef struct +{ + u32 track; + LPNETCHANNEL channel; + ISOMReader *owner; + u64 duration; + + + /*current sample*/ + GF_ISOSample *sample; + GF_SLHeader current_slh; + GF_Err last_state; + + Bool is_pulling; + + Bool has_edit_list; + u32 sample_num; + /*for edit lists*/ + u32 edit_sync_frame; + u64 sample_time, start, end; + Double speed; + + u32 time_scale; + Bool to_init, is_playing, has_rap; + u8 streamType; + + Bool is_encrypted; + + /*cache stuff*/ + u64 cache_seed_ts; + u32 frame_cts_offset; + u64 prev_dts, max_cts; + GF_ISOSample *cache_sample; +} ISOMChannel; +void isor_reset_reader(ISOMChannel *ch); +void isor_reader_get_sample(ISOMChannel *ch); +void isor_reader_release_sample(ISOMChannel *ch); + +ISOMChannel *isor_get_channel(ISOMReader *reader, LPNETCHANNEL channel); + +GF_InputService *isor_client_load(); +void isor_client_del(GF_BaseInterface *bi); + +GF_Descriptor *isor_emulate_iod(ISOMReader *read); +/*uses nero chapter info and remaps to MPEG-4 OCI if no OCI present in descriptor*/ +void isor_emulate_chapters(GF_ISOFile *file, GF_InitialObjectDescriptor *iod); + +void isor_declare_objects(ISOMReader *read); + + +#ifndef GPAC_READ_ONLY +GF_BaseInterface *isow_load_cache(); +void isow_delete_cache(GF_BaseInterface *bi); +#endif + + +#endif + diff --git a/modules/isom_in/load.c b/modules/isom_in/load.c new file mode 100644 index 0000000..72ace59 --- /dev/null +++ b/modules/isom_in/load.c @@ -0,0 +1,177 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MP4 reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "isom_in.h" + +void isor_emulate_chapters(GF_ISOFile *file, GF_InitialObjectDescriptor *iod) +{ + GF_Segment *prev_seg; + u64 prev_start; + u64 start; + u32 i, count; + if (!iod || gf_list_count(iod->OCIDescriptors)) return; + count = gf_isom_get_chapter_count(file, 0); + if (!count) return; + + prev_seg = NULL; + start = prev_start = 0; + for (i=0; i<count; i++) { + const char *name; + GF_Segment *seg; + gf_isom_get_chapter(file, 0, i+1, &start, &name); + seg = (GF_Segment *) gf_odf_desc_new(GF_ODF_SEGMENT_TAG); + seg->startTime = (Double) (s64) start; + seg->startTime /= 1000; + seg->SegmentName = strdup(name); + gf_list_add(iod->OCIDescriptors, seg); + if (prev_seg) { + prev_seg->Duration = (Double) (s64) (start - prev_start); + prev_seg->Duration /= 1000; + } else if (start) { + prev_seg = (GF_Segment *) gf_odf_desc_new(GF_ODF_SEGMENT_TAG); + prev_seg->startTime = 0; + prev_seg->Duration = (Double) (s64) (start); + prev_seg->Duration /= 1000; + gf_list_insert(iod->OCIDescriptors, prev_seg, 0); + } + prev_seg = seg; + prev_start = start; + } + if (prev_seg) { + start = 1000*gf_isom_get_duration(file); + start /= gf_isom_get_timescale(file); + if (start>prev_start) { + prev_seg->Duration = (Double) (s64) (start - prev_start); + prev_seg->Duration /= 1000; + } + } +} + +/*emulate a default IOD for all other files (3GP, weird MP4, QT )*/ +GF_Descriptor *isor_emulate_iod(ISOMReader *read) +{ + /*generate an IOD with our private dynamic OD stream*/ + GF_InitialObjectDescriptor *fake_iod = (GF_InitialObjectDescriptor *) gf_odf_desc_new(GF_ODF_IOD_TAG); + isor_emulate_chapters(read->mov, fake_iod); + read->no_service_desc = 1; + return (GF_Descriptor *)fake_iod; +} + +void isor_declare_objects(ISOMReader *read) +{ + GF_ObjectDescriptor *od; + GF_ESD *esd; + const char *tag; + u32 i, count, ocr_es_id, tlen; + + ocr_es_id = 0; + + /*TODO check for alternate tracks*/ + count = gf_isom_get_track_count(read->mov); + for (i=0; i<count; i++) { + if (!gf_isom_is_track_enabled(read->mov, i+1)) continue; + + switch (gf_isom_get_media_type(read->mov, i+1)) { + case GF_ISOM_MEDIA_AUDIO: + case GF_ISOM_MEDIA_VISUAL: + case GF_ISOM_MEDIA_TEXT: + case GF_ISOM_MEDIA_SCENE: + case GF_ISOM_MEDIA_SUBPIC: + break; + default: + continue; + } + esd = gf_media_map_esd(read->mov, i+1); + if (esd) { + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = esd->ESID; + if (!ocr_es_id) ocr_es_id = esd->ESID; + esd->OCRESID = ocr_es_id; + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(read->service, (GF_Descriptor*)od, 1); + } + } + /*if cover art, extract it in cache*/ + if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_COVER_ART, &tag, &tlen)==GF_OK) { + const char *cdir = gf_modules_get_option((GF_BaseInterface *)gf_term_get_service_interface(read->service), "General", "CacheDirectory"); + if (cdir) { + char szName[GF_MAX_PATH], *sep; + FILE *t; + sep = strrchr(gf_isom_get_filename(read->mov), '\\'); + if (!sep) sep = strrchr(gf_isom_get_filename(read->mov), '/'); + + if ((cdir[strlen(cdir)-1] != '\\') && (cdir[strlen(cdir)-1] != '/')) { + sprintf(szName, "%s/%s_cover.%s", cdir, sep, (tlen & 0x80000000) ? "png" : "jpg"); + } else { + sprintf(szName, "%s%s_cover.%s", cdir, sep, (tlen & 0x80000000) ? "png" : "jpg"); + } + t = fopen(szName, "wb"); + if (t) { + fwrite(tag, tlen & 0x7FFFFFFF, 1, t); + fclose(t); + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1050; + od->URLString = strdup(szName); + gf_term_add_media(read->service, (GF_Descriptor*)od, 1); + } + } + } + gf_term_add_media(read->service, NULL, 0); +} + + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return 1; +#ifndef GPAC_READ_ONLY + if (InterfaceType == GF_STREAMING_MEDIA_CACHE) return 1; +#endif + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) + return (GF_BaseInterface *)isor_client_load(); + +#ifndef GPAC_READ_ONLY + if (InterfaceType == GF_STREAMING_MEDIA_CACHE) + return (GF_BaseInterface *)isow_load_cache(); +#endif + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_NET_CLIENT_INTERFACE: isor_client_del(ifce); break; +#ifndef GPAC_READ_ONLY + case GF_STREAMING_MEDIA_CACHE: isow_delete_cache(ifce); break; +#endif + } +} + diff --git a/modules/isom_in/read.c b/modules/isom_in/read.c new file mode 100644 index 0000000..16a6321 --- /dev/null +++ b/modules/isom_in/read.c @@ -0,0 +1,836 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IsoMedia reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "isom_in.h" +#include <gpac/ismacryp.h> + +ISOMChannel *isor_get_channel(ISOMReader *reader, LPNETCHANNEL channel) +{ + u32 i=0; + ISOMChannel *ch; + while ((ch = (ISOMChannel *)gf_list_enum(reader->channels, &i))) { + if (ch->channel == channel) return ch; + } + return NULL; +} + +static void isor_delete_channel(ISOMReader *reader, ISOMChannel *ch) +{ + u32 i=0; + ISOMChannel *ch2; + while ((ch2 = (ISOMChannel *)gf_list_enum(reader->channels, &i))) { + if (ch2 == ch) { + isor_reset_reader(ch); + free(ch); + gf_list_rem(reader->channels, i-1); + return; + } + } +} + +static GFINLINE Bool isor_is_local(const char *url) +{ + if (!strnicmp(url, "file://", 7)) return 1; + if (strstr(url, "://")) return 0; + /*the rest is local (mounted on FS)*/ + return 1; +} + +Bool ISOR_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *ext; + if (!strnicmp(url, "rtsp://", 7)) return 0; + ext = strrchr(url, '.'); + if (!ext) return 0; + + if (gf_term_check_extension(plug, "video/mp4", "mp4 mpg4", "MPEG-4 Movies", ext)) return 1; + if (gf_term_check_extension(plug, "audio/mp4", "m4a mp4 mpg4", "MPEG-4 Music", ext)) return 1; + if (gf_term_check_extension(plug, "application/mp4", "mp4 mpg4", "MPEG-4 Applications", ext)) return 1; + if (gf_term_check_extension(plug, "video/3gpp", "3gp 3gpp", "3GPP/MMS Movies", ext)) return 1; + if (gf_term_check_extension(plug, "audio/3gpp", "3gp 3gpp", "3GPP/MMS Music",ext)) return 1; + if (gf_term_check_extension(plug, "video/3gpp2", "3g2 3gp2", "3GPP2/MMS Movies",ext)) return 1; + if (gf_term_check_extension(plug, "audio/3gpp2", "3g2 3gp2", "3GPP2/MMS Music",ext)) return 1; + + if (gf_isom_probe_file(url)) { + gf_term_check_extension(plug, "application/x-isomedia", ext+1, "IsoMedia Files", ext); + return 1; + } + return 0; +} + + + +void isor_net_io(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + u32 size = 0; + char *local_name; + ISOMReader *read = (ISOMReader *) cbk; + + /*handle service message*/ + gf_term_download_update_stats(read->dnload); + + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + e = GF_EOS; + } else if (param->msg_type==GF_NETIO_DATA_EXCHANGE) { + e = GF_OK; + size = param->size; + } else { + e = param->error; + } + + if (e<GF_OK) { + /*error opening service*/ + if (!read->mov) gf_term_on_connect(read->service, NULL, e); + return; + } + + /*open file if not done yet (bad interleaving)*/ + if (e==GF_EOS) { + const char *local_name; + if (read->mov) return; + local_name = gf_dm_sess_get_cache_name(read->dnload); + if (!local_name) { + gf_term_on_connect(read->service, NULL, GF_SERVICE_ERROR); + return; + } + e = GF_OK; + read->mov = gf_isom_open(local_name, GF_ISOM_OPEN_READ, NULL); + if (!read->mov) e = gf_isom_last_error(NULL); + else read->time_scale = gf_isom_get_timescale(read->mov); + gf_term_on_connect(read->service, NULL, GF_OK); + if (read->no_service_desc) isor_declare_objects(read); + } + + if (!size) return; + + /*service is opened, nothing to do*/ + if (read->mov) return; + + /*try to open the service*/ + local_name = (char *)gf_dm_sess_get_cache_name(read->dnload); + if (!local_name) { + gf_term_on_connect(read->service, NULL, GF_SERVICE_ERROR); + return; + } + + /*not enogh data yet*/ + if (read->missing_bytes && (read->missing_bytes > size) ) { + read->missing_bytes -= size; + return; + } + + e = gf_isom_open_progressive(local_name, &read->mov, &read->missing_bytes); + switch (e) { + case GF_ISOM_INCOMPLETE_FILE: + return; + case GF_OK: + break; + default: + gf_term_on_connect(read->service, NULL, e); + return; + } + + /*ok let's go*/ + read->time_scale = gf_isom_get_timescale(read->mov); + gf_term_on_connect(read->service, NULL, GF_OK); + if (read->no_service_desc) isor_declare_objects(read); +} + + +void isor_setup_download(GF_InputService *plug, const char *url) +{ + ISOMReader *read = (ISOMReader *) plug->priv; + read->dnload = gf_term_download_new(read->service, url, 0, isor_net_io, read); + if (!read->dnload) gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + /*service confirm is done once IOD can be fetched*/ +} + + + +GF_Err ISOR_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + char szURL[2048]; + char *tmp; + ISOMReader *read; + if (!plug || !plug->priv || !serv) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + + read->service = serv; + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + read->base_track_id = 0; + strcpy(szURL, url); + tmp = strrchr(szURL, '.'); + if (tmp) { + tmp = strchr(tmp, '#'); + if (tmp) { + if (!strnicmp(tmp, "#trackID=", 9)) { + read->base_track_id = atoi(tmp+9); + } else { + read->base_track_id = atoi(tmp+1); + } + tmp[0] = 0; + } + } + + if (isor_is_local(szURL)) { + if (!read->mov) read->mov = gf_isom_open(szURL, GF_ISOM_OPEN_READ, NULL); + if (!read->mov) { + gf_term_on_connect(serv, NULL, gf_isom_last_error(NULL)); + return GF_OK; + } + read->time_scale = gf_isom_get_timescale(read->mov); + /*reply to user*/ + gf_term_on_connect(serv, NULL, GF_OK); + if (read->no_service_desc) isor_declare_objects(read); + } else { + /*setup downloader*/ + isor_setup_download(plug, szURL); + } + return GF_OK; +} + +GF_Err ISOR_CloseService(GF_InputService *plug) +{ + GF_Err reply; + ISOMReader *read; + if (!plug || !plug->priv) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + reply = GF_OK; + + if (read->mov) gf_isom_close(read->mov); + read->mov = NULL; + + while (gf_list_count(read->channels)) { + ISOMChannel *ch = (ISOMChannel *)gf_list_get(read->channels, 0); + gf_list_rem(read->channels, 0); + isor_delete_channel(read, ch); + } + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + gf_term_on_disconnect(read->service, NULL, reply); + return GF_OK; +} + +static Bool check_mpeg4_systems(GF_InputService *plug, GF_ISOFile *mov) +{ + char *opt, *bname, *br, *next; + u32 i, count, brand, has_mpeg4; + GF_Err e; + e = gf_isom_get_brand_info(mov, &brand, &i, &count); + /*no brand == MP4 v1*/ + if (e || !brand) return 1; + + has_mpeg4 = 0; + if ((brand==GF_ISOM_BRAND_MP41) || (brand==GF_ISOM_BRAND_MP42)) has_mpeg4 = 1; + + opt = (char*) gf_modules_get_option((GF_BaseInterface *)plug, "ISOReader", "IgnoreMPEG-4ForBrands"); + if (!opt) { + gf_modules_set_option((GF_BaseInterface *)plug, "ISOReader", "IgnoreMPEG-4ForBrands", "nd*"); + opt = (char*) gf_modules_get_option((GF_BaseInterface *)plug, "ISOReader", "IgnoreMPEG-4ForBrands"); + } + + for (i=0; i<count; i++) { + e = gf_isom_get_alternate_brand(mov, i+1, &brand); + if (e) return 0; + if ((brand==GF_ISOM_BRAND_MP41) || (brand==GF_ISOM_BRAND_MP42)) { + has_mpeg4 = 1; + continue; + } + bname = (char*)gf_4cc_to_str(brand); + br = opt; + while (br) { + Bool ignore = 0; + u32 orig_len, len; + next = strchr(br, ' '); + if (next) next[0] = 0; + len = orig_len = strlen(br); + + while (len) { + if (br[len-1]=='*') { + br[len-1]=0; + len--; + } else { + break; + } + } + /*ignor all brands*/ + if (!len) ignore = 1; + else if (!strncmp(bname, br, len)) ignore = 1; + + while (len<orig_len) { + br[len] = '*'; + len++; + } + if (next) { + next[0] = ' '; + br = next + 1; + } else { + br = NULL; + } + if (ignore) return 0; + } + } + return has_mpeg4; +} + +static u32 get_track_id(GF_ISOFile *mov, u32 media_type, u32 idx) +{ + u32 i, count, cur; + cur=0; + count = gf_isom_get_track_count(mov); + for (i=0; i<count; i++) { + if (gf_isom_get_media_type(mov, i+1) != media_type) continue; + if (!idx) return gf_isom_get_track_id(mov, i+1); + cur++; + if (cur==idx) return gf_isom_get_track_id(mov, i+1); + } + return 0; +} + +/*fixme, this doesn't work properly with respect to @expect_type*/ +static GF_Descriptor *ISOR_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + u32 count, nb_st, i, trackID; + GF_ESD *esd; + ISOMReader *read; + GF_InitialObjectDescriptor *iod; + if (!plug || !plug->priv) return NULL; + read = (ISOMReader *) plug->priv; + if (!read->mov) return NULL; + + /*no matter what always read text as TTUs*/ + gf_isom_text_set_streaming_mode(read->mov, 1); + + trackID = 0; + if (!sub_url) { + trackID = read->base_track_id; + read->base_track_id = 0; + } else { + char *ext = strrchr(sub_url, '#'); + if (!ext) { + trackID = 0; + } else { + if (!strnicmp(ext, "#trackID=", 9)) trackID = atoi(ext+9); + else if (!stricmp(ext, "#video")) trackID = get_track_id(read->mov, GF_ISOM_MEDIA_VISUAL, 0); + else if (!strnicmp(ext, "#video", 6)) { + trackID = atoi(ext+6); + trackID = get_track_id(read->mov, GF_ISOM_MEDIA_VISUAL, trackID); + } + else if (!stricmp(ext, "#audio")) trackID = get_track_id(read->mov, GF_ISOM_MEDIA_AUDIO, 0); + else if (!strnicmp(ext, "#audio", 6)) { + trackID = atoi(ext+6); + trackID = get_track_id(read->mov, GF_ISOM_MEDIA_AUDIO, trackID); + } + else trackID = atoi(ext+1); + + /*if trackID is 0, assume this is a fragment identifier*/ + } + } + + if (!trackID && (expect_type!=GF_MEDIA_OBJECT_SCENE) && (expect_type!=GF_MEDIA_OBJECT_UNDEF)) { + for (i=0; i<gf_isom_get_track_count(read->mov); i++) { + u32 type = gf_isom_get_media_type(read->mov, i+1); + if ( + ((type==GF_ISOM_MEDIA_VISUAL) && (expect_type==GF_MEDIA_OBJECT_VIDEO)) + || ((type==GF_ISOM_MEDIA_AUDIO) && (expect_type==GF_MEDIA_OBJECT_AUDIO)) ) { + trackID = gf_isom_get_track_id(read->mov, i+1); + break; + } + + } + } + if (trackID && (expect_type!=GF_MEDIA_OBJECT_SCENE) ) { + u32 track = gf_isom_get_track_by_id(read->mov, trackID); + if (!track) return NULL; + esd = gf_media_map_esd(read->mov, track); + esd->OCRESID = 0; + iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(read->mov); + if (!iod) { + iod = (GF_InitialObjectDescriptor *) gf_odf_desc_new(GF_ODF_IOD_TAG); + iod->OD_profileAndLevel = iod->audio_profileAndLevel = iod->graphics_profileAndLevel = iod->scene_profileAndLevel = iod->visual_profileAndLevel = 0xFE; + } else { + while (gf_list_count(iod->ESDescriptors)) { + GF_ESD *old = (GF_ESD *)gf_list_get(iod->ESDescriptors, 0); + gf_odf_desc_del((GF_Descriptor *) old); + gf_list_rem(iod->ESDescriptors, 0); + } + } + gf_list_add(iod->ESDescriptors, esd); + isor_emulate_chapters(read->mov, iod); + return (GF_Descriptor *) iod; + } + + iod = NULL; + if (check_mpeg4_systems(plug, read->mov)) { + iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(read->mov); + if (!iod) { +#ifndef GPAC_DISABLE_LOG + GF_Err e = gf_isom_last_error(read->mov); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[IsoMedia] Cannot fetch MPEG-4 IOD (error %s) - generating one\n", gf_error_to_string(e) )); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] No MPEG-4 IOD found in file - generating one\n")); + } +#endif + } + } + if (!iod) return isor_emulate_iod(read); + + count = gf_list_count(iod->ESDescriptors); + if (!count) { + gf_odf_desc_del((GF_Descriptor*) iod); + return isor_emulate_iod(read); + } + if (count==1) { + esd = (GF_ESD *)gf_list_get(iod->ESDescriptors, 0); + switch (esd->decoderConfig->streamType) { + case GF_STREAM_SCENE: + case GF_STREAM_PRIVATE_SCENE: + break; + case GF_STREAM_VISUAL: + if (expect_type!=GF_MEDIA_OBJECT_VIDEO) { + gf_odf_desc_del((GF_Descriptor*) iod); + return isor_emulate_iod(read); + } + break; + case GF_STREAM_AUDIO: + /*we need a fake scene graph*/ + if (expect_type!=GF_MEDIA_OBJECT_AUDIO) { + gf_odf_desc_del((GF_Descriptor*) iod); + return isor_emulate_iod(read); + } + break; + default: + gf_odf_desc_del((GF_Descriptor*) iod); + return NULL; + } + } + /*check IOD is not badly formed (eg mixing audio, video or text streams)*/ + nb_st = 0; + for (i=0; i<count; i++) { + esd = (GF_ESD *)gf_list_get(iod->ESDescriptors, i); + switch (esd->decoderConfig->streamType) { + case GF_STREAM_VISUAL: nb_st |= 1; break; + case GF_STREAM_AUDIO: nb_st |= 2; break; + case GF_STREAM_TEXT: nb_st |= 4; break; + } + } + if ( (nb_st & 1) + (nb_st & 2) + (nb_st & 4) > 1) { + gf_odf_desc_del((GF_Descriptor*) iod); + return isor_emulate_iod(read); + } + + isor_emulate_chapters(read->mov, iod); + return (GF_Descriptor *) iod; +} + + +GF_Err ISOR_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ESID; + ISOMChannel *ch; + GF_NetworkCommand com; + u32 track; + Bool is_esd_url; + GF_Err e; + ISOMReader *read; + if (!plug || !plug->priv) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + + track = 0; + ch = NULL; + is_esd_url = 0; + e = GF_OK; + if (upstream) { + e = GF_ISOM_INVALID_FILE; + goto exit; + } + if (!read->mov) return GF_SERVICE_ERROR; + + if (strstr(url, "ES_ID")) { + sscanf(url, "ES_ID=%d", &ESID); + } else { + /*handle url like mypath/myfile.mp4#trackID*/ + char *track_id = strrchr(url, '.'); + if (track_id) { + track_id = strchr(url, '#'); + if (track_id) track_id ++; + } + is_esd_url = 1; + + ESID = 0; + /*if only one track ok*/ + if (gf_isom_get_track_count(read->mov)==1) ESID = gf_isom_get_track_id(read->mov, 1); + else if (track_id) { + ESID = atoi(track_id); + track = gf_isom_get_track_by_id(read->mov, (u32) ESID); + if (!track) ESID = 0; + } + + } + if (!ESID) { + e = GF_NOT_SUPPORTED; + goto exit; + } + + /*a channel cannot be open twice, it has to be closed before - NOTE a track is NOT a channel and the user can open + several times the same track as long as a dedicated channel is used*/ + ch = isor_get_channel(read, channel); + if (ch) { + e = GF_SERVICE_ERROR; + goto exit; + } + track = gf_isom_get_track_by_id(read->mov, (u32) ESID); + if (!track) { + e = GF_STREAM_NOT_FOUND; + goto exit; + } + + GF_SAFEALLOC(ch, ISOMChannel); + ch->owner = read; + ch->channel = channel; + gf_list_add(read->channels, ch); + ch->track = track; + switch (gf_isom_get_media_type(ch->owner->mov, ch->track)) { + case GF_ISOM_MEDIA_OCR: + ch->streamType = GF_STREAM_OCR; + break; + case GF_ISOM_MEDIA_SCENE: + ch->streamType = GF_STREAM_SCENE; + break; + } + + ch->has_edit_list = gf_isom_get_edit_segment_count(ch->owner->mov, ch->track) ? 1 : 0; + ch->has_rap = (gf_isom_has_sync_points(ch->owner->mov, ch->track)==1) ? 1 : 0; + ch->time_scale = gf_isom_get_media_timescale(ch->owner->mov, ch->track); + +exit: + gf_term_on_connect(read->service, channel, e); + /*if esd url reconfig SL layer*/ + if (!e && is_esd_url) { + GF_ESD *esd; + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.base.on_channel = channel; + com.command_type = GF_NET_CHAN_RECONFIG; + esd = gf_isom_get_esd(read->mov, ch->track, 1); + if (esd) { + memcpy(&com.cfg.sl_config, esd->slConfig, sizeof(GF_SLConfig)); + gf_odf_desc_del((GF_Descriptor *)esd); + } else { + com.cfg.sl_config.tag = GF_ODF_SLC_TAG; + com.cfg.sl_config.timestampLength = 32; + com.cfg.sl_config.timestampResolution = ch->time_scale; + com.cfg.sl_config.useRandomAccessPointFlag = 1; + } + gf_term_on_command(read->service, &com, GF_OK); + } + if (!e && track && gf_isom_is_track_encrypted(read->mov, track)) { + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.base.on_channel = channel; + com.command_type = GF_NET_CHAN_DRM_CFG; + ch->is_encrypted = 1; + if (gf_isom_is_ismacryp_media(read->mov, track, 1)) { + gf_isom_get_ismacryp_info(read->mov, track, 1, NULL, &com.drm_cfg.scheme_type, &com.drm_cfg.scheme_version, &com.drm_cfg.scheme_uri, &com.drm_cfg.kms_uri, NULL, NULL, NULL); + gf_term_on_command(read->service, &com, GF_OK); + } else if (gf_isom_is_omadrm_media(read->mov, track, 1)) { + gf_isom_get_omadrm_info(read->mov, track, 1, NULL, &com.drm_cfg.scheme_type, &com.drm_cfg.scheme_version, &com.drm_cfg.contentID, &com.drm_cfg.kms_uri, &com.drm_cfg.oma_drm_textual_headers, &com.drm_cfg.oma_drm_textual_headers_len, NULL, &com.drm_cfg.oma_drm_crypt_type, NULL, NULL, NULL); + + gf_media_get_file_hash(gf_isom_get_filename(read->mov), com.drm_cfg.hash); + gf_term_on_command(read->service, &com, GF_OK); + + } + } + return e; +} + +GF_Err ISOR_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + ISOMChannel *ch; + GF_Err e; + ISOMReader *read; + if (!plug || !plug->priv) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + if (!read->mov) return GF_SERVICE_ERROR; + + e = GF_OK; + ch = isor_get_channel(read, channel); + assert(ch); + if (!ch) { + e = GF_STREAM_NOT_FOUND; + goto exit; + } + /*signal the service is broken but still process the delete*/ + isor_delete_channel(read, ch); + assert(!isor_get_channel(read, channel)); + +exit: + gf_term_on_disconnect(read->service, channel, e); + return e; +} + +GF_Err ISOR_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + ISOMChannel *ch; + ISOMReader *read; + if (!plug || !plug->priv) return GF_SERVICE_ERROR; + /*cannot read native SL-PDUs*/ + if (!out_sl_hdr) return GF_NOT_SUPPORTED; + read = (ISOMReader *) plug->priv; + if (!read->mov) return GF_SERVICE_ERROR; + + *out_data_ptr = NULL; + *out_data_size = 0; + *sl_compressed = 0; + *out_reception_status = GF_OK; + ch = isor_get_channel(read, channel); + if (!ch) return GF_STREAM_NOT_FOUND; + if (!ch->is_playing) return GF_OK; + + *is_new_data = 0; + if (!ch->sample) { + /*get sample*/ + isor_reader_get_sample(ch); + *is_new_data = 1; + } + + if (ch->sample) { + *out_data_ptr = ch->sample->data; + *out_data_size = ch->sample->dataLength; + *out_sl_hdr = ch->current_slh; + } + *out_reception_status = ch->last_state; + return GF_OK; +} + +GF_Err ISOR_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) +{ + ISOMChannel *ch; + ISOMReader *read; + if (!plug || !plug->priv) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + if (!read->mov) return GF_SERVICE_ERROR; + ch = isor_get_channel(read, channel); + if (!ch) return GF_STREAM_NOT_FOUND; + if (!ch->is_playing) return GF_SERVICE_ERROR; + + if (ch->sample) { + isor_reader_release_sample(ch); + /*release sample*/ + } + return GF_OK; +} + +static u64 check_round(ISOMChannel *ch, u64 val_ts, Double val_range, Bool make_greater) +{ + Double round_check = (Double) (s64) val_ts; + round_check /= ch->time_scale; +// if (round_check != val_range) val_ts += make_greater ? 1 : -1; + return val_ts; +} + +GF_Err ISOR_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + Double track_dur, media_dur; + ISOMChannel *ch; + ISOMReader *read; + if (!plug || !plug->priv || !com) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + + if (com->command_type==GF_NET_SERVICE_INFO) { + u32 tag_len; + const char *tag; + if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_NAME, &tag, &tag_len)==GF_OK) com->info.name = tag; + if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_ARTIST, &tag, &tag_len)==GF_OK) com->info.artist = tag; + if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_ALBUM, &tag, &tag_len)==GF_OK) com->info.album = tag; + if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_COMMENT, &tag, &tag_len)==GF_OK) com->info.comment = tag; + if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_TRACK, &tag, &tag_len)==GF_OK) { + com->info.track_info = (((tag[2]<<8)|tag[3]) << 16) | ((tag[4]<<8)|tag[5]); + } + if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_COMPOSER, &tag, &tag_len)==GF_OK) com->info.composer = tag; + if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_WRITER, &tag, &tag_len)==GF_OK) com->info.writer = tag; + if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_GENRE, &tag, &tag_len)==GF_OK) { + if (tag[0]) { + com->info.genre = 0; + } else { + com->info.genre = (tag[0]<<8) | tag[1]; + } + } + return GF_OK; + } + if (com->command_type==GF_NET_SERVICE_HAS_AUDIO) { + u32 i, count; + count = gf_isom_get_track_count(read->mov); + for (i=0; i<count; i++) { + if (gf_isom_get_media_type(read->mov, i+1) == GF_ISOM_MEDIA_AUDIO) return GF_OK; + } + return GF_NOT_SUPPORTED; + } + + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + + ch = isor_get_channel(read, com->base.on_channel); + if (!ch) return GF_STREAM_NOT_FOUND; + + switch (com->command_type) { + case GF_NET_CHAN_SET_PADDING: + if (!ch->track) return GF_OK; + gf_isom_set_sample_padding(read->mov, ch->track, com->pad.padding_bytes); + return GF_OK; + case GF_NET_CHAN_SET_PULL: + ch->is_pulling = 1; + return GF_OK; + case GF_NET_CHAN_INTERACTIVE: + return GF_OK; + case GF_NET_CHAN_BUFFER: + com->buffer.max = com->buffer.min = 0; + return GF_OK; + case GF_NET_CHAN_DURATION: + if (!ch->track) { + com->duration.duration = 0; + return GF_OK; + } + ch->duration = gf_isom_get_track_duration(read->mov, ch->track); + track_dur = (Double) (s64) ch->duration; + track_dur /= read->time_scale; + if (gf_isom_get_edit_segment_count(read->mov, ch->track)) { + com->duration.duration = (Double) track_dur; + ch->duration = (u32) (track_dur * ch->time_scale); + } else { + /*some file indicate a wrong TrackDuration, get the longest*/ + ch->duration = gf_isom_get_media_duration(read->mov, ch->track); + media_dur = (Double) (s64) ch->duration; + media_dur /= ch->time_scale; + com->duration.duration = MAX(track_dur, media_dur); + } + return GF_OK; + case GF_NET_CHAN_PLAY: + if (!ch->is_pulling) return GF_NOT_SUPPORTED; + assert(!ch->is_playing); + isor_reset_reader(ch); + ch->speed = com->play.speed; + ch->start = ch->end = 0; + if (com->play.speed>0) { + if (com->play.start_range>=0) { + ch->start = (u64) (s64) (com->play.start_range * ch->time_scale); + ch->start = check_round(ch, ch->start, com->play.start_range, 1); + } + if (com->play.end_range >= com->play.start_range) { + ch->end = (u64) (s64) (com->play.end_range*ch->time_scale); + ch->end = check_round(ch, ch->end, com->play.end_range, 0); + } + } else if (com->play.speed<0) { + if (com->play.end_range>=com->play.start_range) ch->start = (u64) (s64) (com->play.start_range * ch->time_scale); + if (com->play.end_range >= 0) ch->end = (u64) (s64) (com->play.end_range*ch->time_scale); + } + ch->is_playing = 1; + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] Starting channel playback "LLD" to "LLD" (%g to %g)\n", ch->start, ch->end, com->play.start_range, com->play.end_range)); + return GF_OK; + case GF_NET_CHAN_STOP: + isor_reset_reader(ch); + return GF_OK; + + /*nothing to do on MP4 for channel config*/ + case GF_NET_CHAN_CONFIG: + return GF_OK; + case GF_NET_CHAN_GET_PIXEL_AR: + return gf_isom_get_pixel_aspect_ratio(read->mov, ch->track, 1, &com->par.hSpacing, &com->par.vSpacing); + case GF_NET_CHAN_GET_DSI: + { + /*it may happen that there are conflicting config when using ESD URLs...*/ + GF_DecoderConfig *dcd = gf_isom_get_decoder_config(read->mov, ch->track, 1); + com->get_dsi.dsi = NULL; + com->get_dsi.dsi_len = 0; + if (dcd) { + if (dcd->decoderSpecificInfo) { + com->get_dsi.dsi = dcd->decoderSpecificInfo->data; + com->get_dsi.dsi_len = dcd->decoderSpecificInfo->dataLength; + dcd->decoderSpecificInfo->data = NULL; + } + gf_odf_desc_del((GF_Descriptor *) dcd); + } + } + return GF_OK; + } + return GF_NOT_SUPPORTED; +} + +static Bool ISOR_CanHandleURLInService(GF_InputService *plug, const char *url) +{ + char szURL[2048], *sep; + ISOMReader *read = (ISOMReader *)plug->priv; + const char *this_url = gf_term_get_service_url(read->service); + if (!this_url || !url) return 0; + + if (!strcmp(this_url, url)) return 1; + + strcpy(szURL, this_url); + sep = strrchr(szURL, '#'); + if (sep) sep[0] = 0; + + /*direct addressing in service*/ + if (url[0] == '#') return 1; + if (strnicmp(szURL, url, sizeof(char)*strlen(szURL))) return 0; + return 1; +} + +GF_InputService *isor_client_load() +{ + ISOMReader *reader; + GF_InputService *plug; + GF_SAFEALLOC(plug, GF_InputService); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC IsoMedia Reader", "gpac distribution") + + plug->CanHandleURL = ISOR_CanHandleURL; + plug->ConnectService = ISOR_ConnectService; + plug->CloseService = ISOR_CloseService; + plug->GetServiceDescriptor = ISOR_GetServiceDesc; + plug->ConnectChannel = ISOR_ConnectChannel; + plug->DisconnectChannel = ISOR_DisconnectChannel; + plug->ServiceCommand = ISOR_ServiceCommand; + plug->CanHandleURLInService = ISOR_CanHandleURLInService; + /*we do support pull mode*/ + plug->ChannelGetSLP = ISOR_ChannelGetSLP; + plug->ChannelReleaseSLP = ISOR_ChannelReleaseSLP; + + GF_SAFEALLOC(reader, ISOMReader); + reader->channels = gf_list_new(); + plug->priv = reader; + return plug; +} + +void isor_client_del(GF_BaseInterface *bi) +{ + GF_InputService *plug = (GF_InputService *) bi; + ISOMReader *read = (ISOMReader *)plug->priv; + + gf_list_del(read->channels); + free(read); + free(bi); +} diff --git a/modules/isom_in/read_ch.c b/modules/isom_in/read_ch.c new file mode 100644 index 0000000..bb443cb --- /dev/null +++ b/modules/isom_in/read_ch.c @@ -0,0 +1,210 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MP4 reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "isom_in.h" + + +void isor_reset_reader(ISOMChannel *ch) +{ + memset(&ch->current_slh, 0, sizeof(GF_SLHeader)); + ch->last_state = GF_OK; + if (ch->sample) gf_isom_sample_del(&ch->sample); + ch->sample = NULL; + ch->sample_num = 0; + ch->speed = 1.0; + ch->start = ch->end = 0; + ch->to_init = 1; + ch->is_playing = 0; +} + + +static void init_reader(ISOMChannel *ch) +{ + u32 ivar; + + ch->current_slh.accessUnitEndFlag = 1; + ch->current_slh.accessUnitStartFlag = 1; + ch->current_slh.AU_sequenceNumber = 1; + ch->current_slh.compositionTimeStampFlag = 1; + ch->current_slh.decodingTimeStampFlag = 1; + ch->current_slh.packetSequenceNumber = 1; + ch->current_slh.randomAccessPointFlag = 0; + + assert(ch->sample==NULL); + + if (ch->streamType==GF_STREAM_OCR) { + assert(!ch->sample); + ch->sample = gf_isom_sample_new(); + ch->sample->IsRAP = 1; + ch->sample->DTS = ch->start; + ch->last_state=GF_OK; + } else { + /*take care of seeking out of the track range*/ + if (ch->duration<ch->start) { + ch->last_state = gf_isom_get_sample_for_movie_time(ch->owner->mov, ch->track, ch->duration, &ivar, GF_ISOM_SEARCH_SYNC_BACKWARD, &ch->sample, &ch->sample_num); + } else { + ch->last_state = gf_isom_get_sample_for_movie_time(ch->owner->mov, ch->track, ch->start, &ivar, GF_ISOM_SEARCH_SYNC_BACKWARD, &ch->sample, &ch->sample_num); + } + + if (ch->has_rap && ch->has_edit_list) { + ch->edit_sync_frame = ch->sample_num; + } + } + + /*no sample means we're not in the track range - stop*/ + if (!ch->sample) { + /*incomplete file - check if we're still downloading or not*/ + if (gf_isom_get_missing_bytes(ch->owner->mov, ch->track)) { + u32 net_status; + gf_dm_sess_get_stats(ch->owner->dnload, NULL, NULL, NULL, NULL, NULL, &net_status); + if (net_status == GF_NETIO_DATA_EXCHANGE) { + ch->last_state = GF_OK; + return; + } + ch->last_state = GF_ISOM_INCOMPLETE_FILE; + } else if (ch->sample_num) { + ch->last_state = GF_EOS; + } + } else { + ch->sample_time = ch->sample->DTS; + } + ch->to_init = 0; + ch->current_slh.decodingTimeStamp = ch->start; + ch->current_slh.compositionTimeStamp = ch->start; + ch->current_slh.randomAccessPointFlag = ch->sample ? ch->sample->IsRAP : 0; + +} + +void isor_reader_get_sample(ISOMChannel *ch) +{ + GF_Err e; + u32 ivar; + if (ch->sample) return; + + if (ch->to_init) { + init_reader(ch); + } else if (ch->has_edit_list) { + u32 prev_sample = ch->sample_num; + e = gf_isom_get_sample_for_movie_time(ch->owner->mov, ch->track, ch->sample_time + 1, &ivar, GF_ISOM_SEARCH_FORWARD, &ch->sample, &ch->sample_num); + + /*we are in forced seek mode: fetch all samples before the one matching the sample time*/ + if (ch->edit_sync_frame) { + ch->edit_sync_frame++; + if (ch->edit_sync_frame < ch->sample_num) { + gf_isom_sample_del(&ch->sample); + ch->sample = gf_isom_get_sample(ch->owner->mov, ch->track, ch->edit_sync_frame, &ivar); + ch->sample->DTS = ch->sample_time; + ch->sample->CTS_Offset = 0; + } else { + ch->edit_sync_frame = 0; + if (ch->sample) ch->sample_time = ch->sample->DTS; + } + } else { + /*we jumped to another segment - if RAP is needed look for closest rap in decoding order and + force seek mode*/ + if (ch->sample && !ch->sample->IsRAP && ch->has_rap && (ch->sample_num != prev_sample+1)) { + gf_isom_sample_del(&ch->sample); + e = gf_isom_get_sample_for_movie_time(ch->owner->mov, ch->track, ch->sample_time + 1, &ivar, GF_ISOM_SEARCH_SYNC_BACKWARD, &ch->sample, &ch->sample_num); + ch->edit_sync_frame = ch->sample_num; + ch->sample->DTS = ch->sample_time; + ch->sample->CTS_Offset = 0; + } else { + if (ch->sample) ch->sample_time = ch->sample->DTS; + } + } + + } else { + ch->sample_num++; +fetch_next: + ch->sample = gf_isom_get_sample(ch->owner->mov, ch->track, ch->sample_num, &ivar); + /*if sync shadow / carousel RAP skip*/ + if (ch->sample && (ch->sample->IsRAP==2)) { + gf_isom_sample_del(&ch->sample); + ch->sample_num++; + goto fetch_next; + } + } + if (!ch->sample) { + /*incomplete file - check if we're still downloading or not*/ + if (gf_isom_get_missing_bytes(ch->owner->mov, ch->track)) { + u32 net_status; + gf_dm_sess_get_stats(ch->owner->dnload, NULL, NULL, NULL, NULL, NULL, &net_status); + if (net_status == GF_NETIO_DATA_EXCHANGE) { + ch->last_state = GF_OK; + } else { + ch->last_state = GF_ISOM_INCOMPLETE_FILE; + } + } else if (!ch->sample_num || (ch->sample_num > gf_isom_get_sample_count(ch->owner->mov, ch->track))) { + ch->last_state = GF_EOS; + } + return; + } + ch->last_state = GF_OK; + ch->current_slh.accessUnitLength = ch->sample->dataLength; + /*still seeking or not ?*/ + if (ch->start <= ch->sample->DTS + ch->sample->CTS_Offset) { + ch->current_slh.decodingTimeStamp = ch->sample->DTS; + ch->current_slh.compositionTimeStamp = ch->sample->DTS + ch->sample->CTS_Offset; + } else { + ch->current_slh.compositionTimeStamp = ch->start; + if (ch->streamType==GF_STREAM_SCENE) + ch->current_slh.decodingTimeStamp = ch->sample->DTS; + else + ch->current_slh.decodingTimeStamp = ch->start; + } + ch->current_slh.randomAccessPointFlag = ch->sample->IsRAP; + + if (ch->end && (ch->end < ch->sample->DTS + ch->sample->CTS_Offset)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] End of Channel "LLD" (CTS "LLD")\n", ch->end, ch->sample->DTS + ch->sample->CTS_Offset)); + ch->last_state = GF_EOS; + } + + if (ch->is_encrypted) { + GF_ISMASample *ismasamp = gf_isom_get_ismacryp_sample(ch->owner->mov, ch->track, ch->sample, 1); + if (ismasamp) { + free(ch->sample->data); + ch->sample->data = ismasamp->data; + ch->sample->dataLength = ismasamp->dataLength; + ismasamp->data = NULL; + ismasamp->dataLength = 0; + ch->current_slh.isma_encrypted = (ismasamp->flags & GF_ISOM_ISMA_IS_ENCRYPTED) ? 1 : 0; + ch->current_slh.isma_BSO = ismasamp->IV; + gf_isom_ismacryp_delete_sample(ismasamp); + } else { + ch->current_slh.isma_encrypted = 0; + } + } +} + +void isor_reader_release_sample(ISOMChannel *ch) +{ + if (ch->sample) gf_isom_sample_del(&ch->sample); + ch->sample = NULL; + ch->current_slh.AU_sequenceNumber++; + ch->current_slh.packetSequenceNumber++; +} + + + diff --git a/modules/jack/Makefile b/modules/jack/Makefile new file mode 100644 index 0000000..d25d9eb --- /dev/null +++ b/modules/jack/Makefile @@ -0,0 +1,61 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/jack + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include $(OSS_CFLAGS) + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= jack.o + +SRCS := $(OBJS:.o=.c) + + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + + +LIB=gm_jack.$(DYN_LIB_SUFFIX) + +all: $(LIB) + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac -L/usr/lib -ljack + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif + diff --git a/modules/jack/jack.c b/modules/jack/jack.c new file mode 100644 index 0000000..d6872cd --- /dev/null +++ b/modules/jack/jack.c @@ -0,0 +1,572 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * Copyright (c) Pierre Souchay 2008 + * History: + * + * 2008/02/19 - v1.1 (Pierre Souchay) + * first revision + * 2008/03/11 - v1.2 (Pierre Souchay) + * added volume control + * fixed possible bug in latency computation (did not return the max value) + * + * Jack audio output module : output audio thru the jackd daemon + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <poll.h> +#include <strings.h> +#include <jack/types.h> +#include <jack/jack.h> +#include <jack/ringbuffer.h> +#include <gpac/modules/audio_out.h> + +#ifndef WIN32 +#include <unistd.h> +int +getPid () +{ + return getpid (); +} +#else + // FIXME : get handle under WIN32 ? +int +getPid () +{ + return 1979; +} +#endif + +#define MAX_JACK_CLIENT_NAME_SZ 128 +/* + * This structure defines the handle to a Jack driver + */ +typedef struct +{ + char jackClientName[MAX_JACK_CLIENT_NAME_SZ]; + jack_client_t *jack; + jack_port_t **jackPorts; + jack_nframes_t currentBlockSize; + u32 numChannels; + char *buffer; + u32 bufferSz; + u32 bytesPerSample; + char isActive; + char autoConnect; + char autoStartJackd; + jack_default_audio_sample_t **channels; + float volume; +} JackContext; + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +static void +Jack_cleanup (JackContext * ctx) +{ + u32 channels = 0; + if (ctx == NULL) + return; + ctx->isActive = 0; + GF_LOG (GF_LOG_DEBUG, GF_LOG_MMIO, ("[Jack] Jack_cleanup\n")); + if (ctx->jack != NULL && ctx->isActive) + { + jack_deactivate (ctx->jack); + } + if (ctx->buffer != NULL) + { + free (ctx->buffer); + ctx->bufferSz = 0; + ctx->buffer = NULL; + } + if (ctx->jackPorts != NULL) + { + for (channels = 0; channels < ctx->numChannels; channels++) + { + if (ctx->jackPorts[channels] != NULL) + jack_port_unregister (ctx->jack, ctx->jackPorts[channels]); + ctx->jackPorts[channels] = NULL; + } + free (ctx->jackPorts); + ctx->jackPorts = NULL; + } + if (ctx->jack != NULL) + { + jack_client_close (ctx->jack); + } + if (ctx->channels != NULL) + { + free (ctx->channels); + ctx->channels = NULL; + } + ctx->numChannels = 0; + ctx->currentBlockSize = 0; + bzero (ctx->jackClientName, MAX_JACK_CLIENT_NAME_SZ); + ctx->jack = NULL; +} + +/** + * The callback called by the jack thread + */ +static int +process_callback (jack_nframes_t nframes, void *arg) +{ + uint channel, i; + short *tmpBuffer; + size_t toRead; + size_t bytesToRead; + size_t readen; + GF_AudioOutput *dr = (GF_AudioOutput *) arg; + JackContext *ctx; + if (dr == NULL) + { + // Should not happen + return 1; + } + ctx = dr->opaque; + toRead = nframes * ctx->numChannels; + bytesToRead = toRead * ctx->bytesPerSample; + readen = dr->FillBuffer (dr->audio_renderer, (void *) ctx->buffer, + bytesToRead); + toRead = readen / ctx->bytesPerSample; + if (ctx->bytesPerSample == 2) + { + tmpBuffer = (short *) ctx->buffer; + for (channel = 0; channel < nframes; channel += ctx->numChannels) + { + for (i = 0; i < ctx->numChannels; i++) + ctx->channels[i][channel] = + (float) (ctx->volume / 32768.0 * + (tmpBuffer[channel * ctx->numChannels + i])); + } + } + else + { + for (channel = 0; channel < nframes; channel += ctx->numChannels) + { + for (i = 0; i < ctx->numChannels; i++) + ctx->channels[i][channel] = + (float) (ctx->volume / 255.0 * + (ctx->buffer[channel * ctx->numChannels + i])); + } + } + return 0; +} + +/** + * Called when jackbuffer size change + */ +static int +onBufferSizeChanged (jack_nframes_t nframes, void *arg) +{ + GF_AudioOutput *dr = (GF_AudioOutput *) arg; + JackContext *ctx; + size_t realBuffSize; + u32 channel; + if (dr == NULL) + { + // Should not happen + return 1; + } + ctx = dr->opaque; + realBuffSize = nframes * ctx->numChannels * sizeof (short); + if (ctx->buffer != NULL && ctx->bufferSz == realBuffSize) + return 0; + if (ctx->channels != NULL) + free (ctx->channels); + ctx->channels = NULL; + ctx->channels = + calloc (ctx->numChannels, sizeof (jack_default_audio_sample_t *)); + if (ctx->channels == NULL) + { + Jack_cleanup (ctx); + return 2; + } + for (channel = 0; channel < ctx->numChannels; channel++) + { + ctx->channels[channel] = + jack_port_get_buffer (ctx->jackPorts[channel], nframes); + if (ctx->channels[channel] == NULL) + { + Jack_cleanup (ctx); + return 3; + } + } + + if (ctx->buffer != NULL) + free (ctx->buffer); + ctx->buffer = calloc (realBuffSize, sizeof (char)); + if (ctx->buffer == NULL) + { + Jack_cleanup (ctx); + return 4; + } + ctx->bufferSz = realBuffSize; + GF_LOG (GF_LOG_DEBUG, GF_LOG_MMIO, + ("[Jack] onBufferSizeChanged : resized to %d.\n", realBuffSize)); + if (ctx->buffer == NULL) + { + ctx->bufferSz = 0; + Jack_cleanup (ctx); + return 5; + } + return 0; +} + +static const char *MODULE_NAME = "Jack"; + +static const char *AUTO_CONNECT_OPTION = "AutoConnect"; + +static const char *AUTO_START_JACKD_OPTION = "AutoStartJackd"; + +static const char *TRUE_OPTION = "true"; + +static const char *YES_OPTION = "yes"; + +static char +optionIsTrue (const char *optionValue) +{ + return (0 == strcasecmp (TRUE_OPTION, optionValue) || + 0 == strcasecmp (YES_OPTION, optionValue) + || 0 == strcmp ("1", optionValue)); +} + + +static GF_Err +Jack_Setup (GF_AudioOutput * dr, void *os_handle, u32 num_buffers, + u32 total_duration) +{ + const char *opt; + JackContext *ctx = (JackContext *) dr->opaque; + jack_status_t status; + jack_options_t options = JackNullOption; + + memset (ctx->jackClientName, 0, MAX_JACK_CLIENT_NAME_SZ); + snprintf (ctx->jackClientName, MAX_JACK_CLIENT_NAME_SZ, "gpac-%d", + getPid ()); + + opt = + gf_modules_get_option ((GF_BaseInterface *) dr, MODULE_NAME, + AUTO_CONNECT_OPTION); + if (opt != NULL) + { + if (optionIsTrue (opt)) + ctx->autoConnect = TRUE; + else + ctx->autoConnect = FALSE; + } + else + { + ctx->autoConnect = TRUE; + gf_modules_set_option ((GF_BaseInterface *) dr, MODULE_NAME, + AUTO_CONNECT_OPTION, YES_OPTION); + } + opt = gf_modules_get_option ((GF_BaseInterface *) dr, MODULE_NAME, + AUTO_START_JACKD_OPTION); + if (opt != NULL) + { + if (optionIsTrue (opt)) + ctx->autoStartJackd = TRUE; + else + ctx->autoStartJackd = FALSE; + } + else + { + ctx->autoStartJackd = TRUE; + gf_modules_set_option ((GF_BaseInterface *) dr, MODULE_NAME, + AUTO_START_JACKD_OPTION, YES_OPTION); + } + if (!ctx->autoStartJackd) + { + options |= JackNoStartServer; + } + ctx->jack = jack_client_open (ctx->jackClientName, options, &status, NULL); + if (status & JackNameNotUnique) + { + GF_LOG (GF_LOG_ERROR, GF_LOG_MMIO, + ("[Jack] Cannot open connection to jackd as %s since name was not unique.\n", + ctx->jackClientName)); + Jack_cleanup (ctx); + return GF_IO_ERR; + } + + if (ctx->jack == NULL) + { + GF_LOG (GF_LOG_ERROR, GF_LOG_MMIO, + ("[Jack] Cannot open connection to jackd as %s.\n", + ctx->jackClientName)); + return GF_IO_ERR; + } + return GF_OK; +} + +static void +Jack_Shutdown (GF_AudioOutput * dr) +{ + JackContext *ctx = (JackContext *) dr->opaque; + Jack_cleanup (ctx); +} + +#define JACK_PORT_NAME_MAX_SZ 128 + +static GF_Err +Jack_ConfigureOutput (GF_AudioOutput * dr, u32 * SampleRate, u32 * NbChannels, + u32 * nbBitsPerSample, u32 channel_cfg) +{ + u32 channels; + u32 i; + char port_name[JACK_PORT_NAME_MAX_SZ]; + JackContext *ctx = (JackContext *) dr->opaque; + if (!ctx) + return GF_BAD_PARAM; + ctx->bytesPerSample = *nbBitsPerSample / 8; + if (ctx->bytesPerSample > 2 || ctx->bytesPerSample < 1) + { + GF_LOG (GF_LOG_ERROR, GF_LOG_MMIO, + ("[Jack] Jack-ConfigureOutput : unable to use %d bits/sample.\n")); + return GF_BAD_PARAM; + } + ctx->numChannels = *NbChannels; + *SampleRate = jack_get_sample_rate (ctx->jack); + GF_LOG (GF_LOG_DEBUG, GF_LOG_MMIO, + ("[Jack] Jack_ConfigureOutput channels=%d, srate=%d bits/sample=%d\n", + *NbChannels, *SampleRate, *nbBitsPerSample)); + if (ctx->jackPorts == NULL) + ctx->jackPorts = calloc (ctx->numChannels, sizeof (jack_port_t *)); + if (ctx->jackPorts == NULL) + { + goto exit_cleanup; + } + if (!ctx->isActive) + { + for (channels = 0; channels < ctx->numChannels; channels++) + { + snprintf (port_name, JACK_PORT_NAME_MAX_SZ, "playback_%d", + channels + 1); + ctx->jackPorts[channels] = + jack_port_register (ctx->jack, port_name, JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + if (ctx->jackPorts[channels] == NULL) + goto exit_cleanup; + } + onBufferSizeChanged (jack_get_buffer_size (ctx->jack), dr); + jack_set_buffer_size_callback (ctx->jack, onBufferSizeChanged, dr); + jack_set_process_callback (ctx->jack, process_callback, dr); + } + ctx->currentBlockSize = jack_get_buffer_size (ctx->jack); + if (!ctx->isActive) + { + jack_activate (ctx->jack); + if (ctx->autoConnect) + { + const char **matching_outputs = + jack_get_ports (ctx->jack, NULL, NULL, + JackPortIsInput | JackPortIsPhysical | + JackPortIsTerminal); + if (matching_outputs != NULL) + { + channels = 0; + i = 0; + while (matching_outputs[i] != NULL + && channels < ctx->numChannels) + { + if (!jack_connect (ctx->jack, + jack_port_name (ctx-> + jackPorts[channels++]), + matching_outputs[i])) + { + GF_LOG (GF_LOG_INFO, GF_LOG_MMIO, + ("[Jack] Jack_ConfigureOutput: Failed to connect port[%d] to %s.\n", + channels - 1, matching_outputs[i])); + } + i++; + } + } + } + ctx->isActive = TRUE; + } + return GF_OK; +exit_cleanup: + Jack_cleanup (ctx); + return GF_IO_ERR; +} + +static void +Jack_SetVolume (GF_AudioOutput * dr, u32 Volume) +{ + JackContext *ctx = (JackContext *) dr->opaque; + if (ctx == NULL) + { + return; + } + /* We support ajust the volume to more than 100, up to +6dB even + * if frontends don't support it, it may be useful */ + if (Volume > 400) + Volume = 400; + ctx->volume = (float) Volume / 100.0; + GF_LOG (GF_LOG_DEBUG, GF_LOG_MMIO, + ("[Jack] Jack_SetVolume: Volume set to %d%%.\n", Volume)); +} + +static void +Jack_SetPan (GF_AudioOutput * dr, u32 Pan) +{ + GF_LOG (GF_LOG_INFO, GF_LOG_MMIO, ("[Jack] Jack_SetPan: Not supported.\n")); + +} + +static void +Jack_SetPriority (GF_AudioOutput * dr, u32 Priority) +{ + /** + * Jack manages the priority itself, we don't need + * to interfere here... + */ +} + +static u32 +Jack_GetAudioDelay (GF_AudioOutput * dr) +{ + jack_nframes_t max = 0; + jack_nframes_t latency; + u32 channel; + JackContext *ctx = (JackContext *) dr->opaque; + if (ctx == NULL) + { + return 0; + } + jack_recompute_total_latencies (ctx->jack); + for (channel = 0; channel < ctx->numChannels; channel++) + { + latency = + jack_port_get_total_latency (ctx->jack, ctx->jackPorts[channel]); + if (latency > max) + max = latency; + } + channel = max * 1000 / jack_get_sample_rate (ctx->jack); + GF_LOG (GF_LOG_DEBUG, GF_LOG_MMIO, + ("[Jack] Jack_GetAudioDelay latency = %d ms.\n", channel)); + return channel; +} + +static GF_Err +Jack_QueryOutputSampleRate (GF_AudioOutput * dr, u32 * desired_sr, + u32 * NbChannels, u32 * nbBitsPerSample) +{ + JackContext *ctx = (JackContext *) dr->opaque; + if (!ctx) + return GF_IO_ERR; + *desired_sr = jack_get_sample_rate (ctx->jack); + *NbChannels = 2; + GF_LOG (GF_LOG_DEBUG, GF_LOG_MMIO, + ("[Jack] Jack output sample rate %d\n", *desired_sr)); + return GF_OK; +} + +void * +NewJackOutput () +{ + JackContext *ctx; + GF_AudioOutput *driv; + GF_SAFEALLOC (ctx, JackContext); + if (!ctx) + return NULL; + GF_SAFEALLOC (driv, GF_AudioOutput); + if (!driv) + { + free (ctx); + return NULL; + } + driv->opaque = ctx; + driv->SelfThreaded = 1; + driv->Setup = Jack_Setup; + driv->Shutdown = Jack_Shutdown; + driv->ConfigureOutput = Jack_ConfigureOutput; + driv->GetAudioDelay = Jack_GetAudioDelay; + driv->SetVolume = Jack_SetVolume; + driv->SetPan = Jack_SetPan; + driv->SetPriority = Jack_SetPriority; + driv->QueryOutputSampleRate = Jack_QueryOutputSampleRate; + + ctx->jack = NULL; + ctx->numChannels = 0; + ctx->jackPorts = NULL; + ctx->currentBlockSize = 0; + ctx->numChannels = 0; + ctx->buffer = NULL; + ctx->bufferSz = 0; + ctx->bytesPerSample = 0; + ctx->isActive = FALSE; + ctx->autoConnect = FALSE; + ctx->autoStartJackd = FALSE; + ctx->volume = 1.0; + + GF_REGISTER_MODULE_INTERFACE (driv, GF_AUDIO_OUTPUT_INTERFACE, + "Jack Audio Output", "gpac distribution"); + return driv; +} + +void +DeleteJackOutput (void *ifce) +{ + GF_AudioOutput *dr = (GF_AudioOutput *) ifce; + JackContext *ctx = (JackContext *) dr->opaque; + Jack_cleanup (ctx); + free (ctx); + dr->opaque = NULL; + free (dr); +} + +/* + * ******************************************************************** + * interface + */ + +Bool +QueryInterface (u32 InterfaceType) +{ + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) + return 1; + return 0; +} + +GF_BaseInterface * +LoadInterface (u32 InterfaceType) +{ + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) + { + return NewJackOutput (); + } + return NULL; +} + +void +ShutdownInterface (GF_BaseInterface * ifce) +{ + if (ifce->InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) + DeleteJackOutput ((GF_AudioOutput *) ifce); +} diff --git a/modules/laser_dec/Makefile b/modules/laser_dec/Makefile new file mode 100644 index 0000000..947ec3c --- /dev/null +++ b/modules/laser_dec/Makefile @@ -0,0 +1,62 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/laser_dec + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS=laser_dec.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_laser_dec.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols laser_dec.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/laser_dec/laser_dec.c b/modules/laser_dec/laser_dec.c new file mode 100644 index 0000000..a6de80a --- /dev/null +++ b/modules/laser_dec/laser_dec.c @@ -0,0 +1,196 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / LASeR decoder module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/internal/terminal_dev.h> +#include <gpac/laser.h> +#include <gpac/constants.h> + +#ifndef GPAC_DISABLE_SVG + +typedef struct +{ + GF_InlineScene *pScene; + GF_Terminal *app; + GF_LASeRCodec *codec; + u32 PL, nb_streams; +} LSRPriv; + + + +static GF_Err LSR_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *cap) +{ + cap->cap.valueInt = 0; + return GF_NOT_SUPPORTED; +} + +static GF_Err LSR_SetCapabilities(GF_BaseDecoder *plug, const GF_CodecCapability capability) +{ + return GF_OK; +} + +GF_Err LSR_AttachScene(GF_SceneDecoder *plug, GF_InlineScene *scene, Bool is_scene_decoder) +{ + LSRPriv *priv = (LSRPriv *)plug->privateStack; + if (priv->codec) return GF_BAD_PARAM; + priv->pScene = scene; + priv->app = scene->root_od->term; + + priv->codec = gf_laser_decoder_new(scene->graph); + /*attach the clock*/ + gf_laser_decoder_set_clock(priv->codec, gf_inline_get_time, scene); + return GF_OK; +} + +GF_Err LSR_ReleaseScene(GF_SceneDecoder *plug) +{ + LSRPriv *priv = (LSRPriv *)plug->privateStack; + if (!priv->codec || priv->nb_streams) return GF_BAD_PARAM; + gf_laser_decoder_del(priv->codec); + priv->codec = NULL; + return GF_OK; +} + +static GF_Err LSR_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) +{ + LSRPriv *priv = (LSRPriv *)plug->privateStack; + GF_Err e; + if (esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; + e = gf_laser_decoder_configure_stream(priv->codec, esd->ESID, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); + if (!e) priv->nb_streams++; + return e; +} + +static GF_Err LSR_DetachStream(GF_BaseDecoder *plug, u16 ES_ID) +{ + GF_Err e; + LSRPriv *priv = (LSRPriv *)plug->privateStack; + e = gf_laser_decoder_remove_stream(priv->codec, ES_ID); + if (e) return e; + priv->nb_streams--; + return GF_OK; +} + +static GF_Err LSR_ProcessData(GF_SceneDecoder*plug, char *inBuffer, u32 inBufferLength, + u16 ES_ID, u32 AU_time, u32 mmlevel) +{ + GF_Err e = GF_OK; + LSRPriv *priv = (LSRPriv *)plug->privateStack; + + e = gf_laser_decode_au(priv->codec, ES_ID, inBuffer, inBufferLength); + + /*if scene not attached do it*/ + gf_inline_attach_to_compositor(priv->pScene); + return e; +} + +Bool LSR_CanHandleStream(GF_BaseDecoder *ifce, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + if ((StreamType==GF_STREAM_SCENE) && (ObjectType == GPAC_OTI_SCENE_LASER)) return 1; + return 0; +} + + +void DeleteLSRDec(GF_BaseDecoder *plug) +{ + LSRPriv *priv = (LSRPriv *)plug->privateStack; + /*in case something went wrong*/ + if (priv->codec) gf_laser_decoder_del(priv->codec); + free(priv); + free(plug); +} + +GF_BaseDecoder *NewLSRDec() +{ + LSRPriv *priv; + GF_SceneDecoder *tmp; + + GF_SAFEALLOC(tmp, GF_SceneDecoder); + if (!tmp) return NULL; + GF_SAFEALLOC(priv, LSRPriv); + priv->codec = NULL; + tmp->privateStack = priv; + tmp->AttachStream = LSR_AttachStream; + tmp->DetachStream = LSR_DetachStream; + tmp->GetCapabilities = LSR_GetCapabilities; + tmp->SetCapabilities = LSR_SetCapabilities; + tmp->ProcessData = LSR_ProcessData; + tmp->AttachScene = LSR_AttachScene; + tmp->CanHandleStream = LSR_CanHandleStream; + tmp->ReleaseScene = LSR_ReleaseScene; + GF_REGISTER_MODULE_INTERFACE(tmp, GF_SCENE_DECODER_INTERFACE, "GPAC LASeR Decoder", "gpac distribution") + return (GF_BaseDecoder *) tmp; +} + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + return 1; + default: + return 0; + } +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + return (GF_BaseInterface *)NewLSRDec(); + default: + return NULL; + } +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + DeleteLSRDec((GF_BaseDecoder *)ifce); + break; + } +} + +#else + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ +} +#endif diff --git a/modules/laser_dec/laser_dec.def b/modules/laser_dec/laser_dec.def new file mode 100644 index 0000000..3ea674b --- /dev/null +++ b/modules/laser_dec/laser_dec.def @@ -0,0 +1,6 @@ +LIBRARY gm_laser_dec.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/mp3_in/Makefile b/modules/mp3_in/Makefile new file mode 100644 index 0000000..00a1669 --- /dev/null +++ b/modules/mp3_in/Makefile @@ -0,0 +1,77 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/mp3_in + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= mp3_in.o + + + +#mad config +ifeq ($(CONFIG_MAD), no) +else +OBJS+=mad_dec.o +CFLAGS+=-DGPAC_HAS_MAD +#local mad lib +ifeq ($(CONFIG_MAD), local) +EXTRALIBS+=-L../../extra_lib/lib/gcc +CFLAGS+= -I$(LOCAL_INC_PATH)/mad +endif +EXTRALIBS+= -lmad +endif + +SRCS := $(OBJS:.o=.c) + +LIB=gm_mp3_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols mp3_in.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) -L../../bin/gcc -lgpac $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/mp3_in/mad_dec.c b/modules/mp3_in/mad_dec.c new file mode 100644 index 0000000..aa60e51 --- /dev/null +++ b/modules/mp3_in/mad_dec.c @@ -0,0 +1,348 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / codec pack module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef GPAC_HAS_MAD + +#include <gpac/modules/codec.h> +#include <gpac/constants.h> + +#if defined(_WIN32_WCE) || defined(__SYMBIAN32__) +#ifndef FPM_DEFAULT +#define FPM_DEFAULT +#endif +#endif + +#include "mad.h" + +typedef struct +{ + Bool configured; + + u32 sample_rate, out_size, num_samples; + u8 num_channels; + /*no support for scalability in FAAD yet*/ + u16 ES_ID; + u32 cb_size, cb_trig; + + unsigned char *buffer; + u32 len; + Bool first; + + + struct mad_frame frame; + struct mad_stream stream; + struct mad_synth synth; + +} MADDec; + + +#define MADCTX() MADDec *ctx = (MADDec *) ifcg->privateStack + + +static GF_Err MAD_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + MADCTX(); + if (ctx->ES_ID && ctx->ES_ID!=esd->ESID) return GF_NOT_SUPPORTED; + + if (ctx->configured) { + mad_stream_finish(&ctx->stream); + mad_frame_finish(&ctx->frame); + mad_synth_finish(&ctx->synth); + } + mad_stream_init(&ctx->stream); + mad_frame_init(&ctx->frame); + mad_synth_init(&ctx->synth); + ctx->configured = 1; + + ctx->buffer = malloc(sizeof(char) * 2*MAD_BUFFER_MDLEN); + + /*we need a frame to init, so use default values*/ + ctx->num_samples = 1152; + ctx->num_channels = 0; + ctx->sample_rate = 0; + ctx->out_size = 2 * ctx->num_samples * ctx->num_channels; + ctx->ES_ID = esd->ESID; + ctx->first = 1; + return GF_OK; +} + +static GF_Err MAD_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + MADCTX(); + if (ES_ID != ctx->ES_ID) return GF_BAD_PARAM; + ctx->ES_ID = 0; + if (ctx->buffer) free(ctx->buffer); + ctx->buffer = NULL; + ctx->sample_rate = ctx->out_size = ctx->num_samples = 0; + ctx->num_channels = 0; + if (ctx->configured) { + mad_stream_finish(&ctx->stream); + mad_frame_finish(&ctx->frame); + mad_synth_finish(&ctx->synth); + } + ctx->configured = 0; + return GF_OK; +} +static GF_Err MAD_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + MADCTX(); + switch (capability->CapCode) { + /*not tested yet*/ + case GF_CODEC_RESILIENT: + capability->cap.valueInt = 1; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_SAMPLERATE: + capability->cap.valueInt = ctx->sample_rate; + break; + case GF_CODEC_NB_CHAN: + capability->cap.valueInt = ctx->num_channels; + break; + case GF_CODEC_BITS_PER_SAMPLE: + capability->cap.valueInt = 16; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = ctx->cb_trig; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = ctx->cb_size; + break; + /*FIXME: get exact sampling window*/ + case GF_CODEC_CU_DURATION: + capability->cap.valueInt = ctx->num_samples; + break; + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 0; + break; + case GF_CODEC_CHANNEL_CONFIG: + if (ctx->num_channels==1) { + capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER; + } else { + capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT; + } + break; + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} + +static GF_Err MAD_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + MADCTX(); + switch (capability.CapCode) { + /*reset storage buffer*/ + case GF_CODEC_WAIT_RAP: + ctx->first = 1; + ctx->len = 0; + if (ctx->configured) { + mad_stream_finish(&ctx->stream); + mad_frame_finish(&ctx->frame); + mad_synth_finish(&ctx->synth); + + mad_stream_finish(&ctx->stream); + mad_frame_finish(&ctx->frame); + mad_synth_finish(&ctx->synth); + } + return GF_OK; + default: + /*return unsupported to avoid confusion by the player (like SR changing ...) */ + return GF_NOT_SUPPORTED; + } +} + +/*from miniMad.c*/ +#define MAD_SCALE(ret, s_chan) \ + chan = s_chan; \ + chan += (1L << (MAD_F_FRACBITS - 16)); \ + if (chan >= MAD_F_ONE) \ + chan = MAD_F_ONE - 1; \ + else if (chan < -MAD_F_ONE) \ + chan = -MAD_F_ONE; \ + ret = chan >> (MAD_F_FRACBITS + 1 - 16); \ + +static GF_Err MAD_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + mad_fixed_t *left_ch, *right_ch, chan; + char *ptr; + u32 num, outSize; + MADCTX(); + + /*check not using scalabilty*/ + assert(ctx->ES_ID == ES_ID); + + if (ctx->ES_ID != ES_ID) + return GF_BAD_PARAM; + + /*if late or seeking don't decode*/ + switch (mmlevel) { + case GF_CODEC_LEVEL_SEEK: + case GF_CODEC_LEVEL_DROP: + *outBufferLength = 0; + return GF_OK; + default: + break; + } + + if (ctx->out_size > *outBufferLength) { + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; + } + + if (ctx->first) { + ctx->first = 0; + memcpy(ctx->buffer, inBuffer, inBufferLength); + ctx->len = inBufferLength; + *outBufferLength = 0; + return GF_OK; + } + memcpy(ctx->buffer + ctx->len, inBuffer, inBufferLength); + ctx->len += inBufferLength; + mad_stream_buffer(&ctx->stream, ctx->buffer, ctx->len); + + if (mad_frame_decode(&ctx->frame, &ctx->stream) == -1) { + memcpy(ctx->buffer, inBuffer, inBufferLength); + ctx->len = inBufferLength; + *outBufferLength = 0; + return GF_NON_COMPLIANT_BITSTREAM; + } + + + /*first cfg, reconfig composition buffer*/ + if (!ctx->sample_rate) { + mad_synth_frame(&ctx->synth, &ctx->frame); + ctx->len -= inBufferLength; + ctx->sample_rate = ctx->synth.pcm.samplerate; + ctx->num_channels = (u8) ctx->synth.pcm.channels; + ctx->num_samples = ctx->synth.pcm.length; + ctx->out_size = 2 * ctx->num_samples * ctx->num_channels; + *outBufferLength = ctx->out_size; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[MAD] decoder initialized - MP3 sample rate %d - %d channel(s)", ctx->sample_rate, ctx->num_channels)); + return GF_BUFFER_TOO_SMALL; + } + + if (ctx->stream.next_frame) { + ctx->len = &ctx->buffer[ctx->len] - ctx->stream.next_frame; + memmove(ctx->buffer, ctx->stream.next_frame, ctx->len); + } + + + mad_synth_frame(&ctx->synth, &ctx->frame); + num = ctx->synth.pcm.length; + ptr = (char *) outBuffer; + left_ch = ctx->synth.pcm.samples[0]; + right_ch = ctx->synth.pcm.samples[1]; + outSize = 0; + + while (num--) { + s32 rs; + MAD_SCALE(rs, (*left_ch++) ); + + *ptr = (rs >> 0) & 0xff; + ptr++; + *ptr = (rs >> 8) & 0xff; + ptr++; + outSize += 2; + + if (ctx->num_channels == 2) { + MAD_SCALE(rs, (*right_ch++) ); + *ptr = (rs >> 0) & 0xff; + ptr++; + *ptr = (rs >> 8) & 0xff; + ptr++; + outSize += 2; + } + } + *outBufferLength = outSize; + return GF_OK; +} + +static const char *MAD_GetCodecName(GF_BaseDecoder *dec) +{ + return "MAD " \ + MAD_VERSION; +} + +static Bool MAD_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + /*audio decs*/ + if (StreamType != GF_STREAM_AUDIO) return 0; + + switch (ObjectType) { + /*MPEG1 audio*/ + case 0x69: + /*MPEG2 audio*/ + case 0x6B: + return 1; + /*cap query*/ + case 0: + return 1; + } + return 0; +} + +GF_BaseDecoder *NewMADDec() +{ + GF_MediaDecoder *ifce; + MADDec *dec; + + GF_SAFEALLOC(ifce, GF_MediaDecoder); + GF_SAFEALLOC(dec, MADDec); + GF_REGISTER_MODULE_INTERFACE(ifce, GF_MEDIA_DECODER_INTERFACE, "MAD Decoder", "gpac distribution") + ifce->privateStack = dec; + + dec->cb_size = 12; + dec->cb_trig = 4; + + /*setup our own interface*/ + ifce->AttachStream = MAD_AttachStream; + ifce->DetachStream = MAD_DetachStream; + ifce->GetCapabilities = MAD_GetCapabilities; + ifce->SetCapabilities = MAD_SetCapabilities; + ifce->GetName = MAD_GetCodecName; + ifce->ProcessData = MAD_ProcessData; + ifce->CanHandleStream = MAD_CanHandleStream; + return (GF_BaseDecoder *)ifce; +} + +void DeleteMADDec(GF_MediaDecoder *ifcg) +{ + MADCTX(); + if (ctx->configured) { + mad_stream_finish(&ctx->stream); + mad_frame_finish(&ctx->frame); + mad_synth_finish(&ctx->synth); + } + free(ctx); + free(ifcg); +} + +#endif diff --git a/modules/mp3_in/mp3_in.c b/modules/mp3_in/mp3_in.c new file mode 100644 index 0000000..d6a490d --- /dev/null +++ b/modules/mp3_in/mp3_in.c @@ -0,0 +1,686 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MP3 reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/modules/service.h> +#include <gpac/avparse.h> +#include <gpac/constants.h> +#include <gpac/modules/codec.h> + + +typedef struct +{ + GF_ClientService *service; + + u32 needs_connection; + Bool is_remote; + + FILE *stream; + u32 duration; + + u32 pad_bytes; + Bool done; + LPNETCHANNEL ch; + + char *data; + u32 data_size; + + GF_SLHeader sl_hdr; + + Bool is_inline; + + u32 sample_rate, oti; + Double start_range, end_range; + u32 current_time, nb_samp; + /*file downloader*/ + GF_DownloadSession * dnload; + + Bool is_live; + char prev_data[1000]; + u32 prev_size; + + + char *icy_name; + char *icy_genre; + char *icy_track_name; +} MP3Reader; + + +static Bool MP3_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt; + sExt = strrchr(url, '.'); + if (!sExt) return 0; + if (gf_term_check_extension(plug, "audio/mpeg", "mp2 mp3 mpga mpega", "MP3 Music", sExt)) return 1; + if (gf_term_check_extension(plug, "audio/x-mpeg", "mp2 mp3 mpga mpega", "MP3 Music", sExt)) return 1; + return 0; +} + +static Bool mp3_is_local(const char *url) +{ + if (!strnicmp(url, "file://", 7)) return 1; + if (strstr(url, "://")) return 0; + return 1; +} + + +static GF_ESD *MP3_GetESD(MP3Reader *read) +{ + GF_ESD *esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = read->sample_rate; + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + esd->decoderConfig->objectTypeIndication = read->oti; + esd->ESID = 1; + return esd; +} + +static void mp3_setup_object(MP3Reader *read) +{ + if (read->is_inline) { + GF_ESD *esd; + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1; + esd = MP3_GetESD(read); + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(read->service, (GF_Descriptor*)od, 0); + } +} + + +static Bool MP3_ConfigureFromFile(MP3Reader *read) +{ + u32 hdr, size, pos; + if (!read->stream) return 0; + + hdr = gf_mp3_get_next_header(read->stream); + if (!hdr) return 0; + read->sample_rate = gf_mp3_sampling_rate(hdr); + read->oti = gf_mp3_object_type_indication(hdr); + fseek(read->stream, 0, SEEK_SET); + if (!read->oti) return 0; + + /*we don't have the full file...*/ + if (read->is_remote) return 1; + + return 1; + + read->duration = gf_mp3_window_size(hdr); + size = gf_mp3_frame_size(hdr); + pos = ftell(read->stream); + fseek(read->stream, pos + size - 4, SEEK_SET); + while (1) { + hdr = gf_mp3_get_next_header(read->stream); + if (!hdr) break; + read->duration += gf_mp3_window_size(hdr); + size = gf_mp3_frame_size(hdr); + pos = ftell(read->stream); + fseek(read->stream, pos + size - 4, SEEK_SET); + } + fseek(read->stream, 0, SEEK_SET); + return 1; +} + +static void MP3_RegulateDataRate(MP3Reader *read) +{ + GF_NetworkCommand com; + + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.command_type = GF_NET_CHAN_BUFFER_QUERY; + com.base.on_channel = read->ch; + while (read->ch) { + gf_term_on_command(read->service, &com, GF_OK); + if (com.buffer.occupancy < com.buffer.max) break; + gf_sleep(2); + } +} + +static void MP3_OnLiveData(MP3Reader *read, char *data, u32 data_size) +{ + u32 hdr, size, pos; + + if (read->needs_connection) { + hdr = gf_mp3_get_next_header_mem(data, data_size, &pos); + if (!hdr) return; + read->sample_rate = gf_mp3_sampling_rate(hdr); + read->oti = gf_mp3_object_type_indication(hdr); + read->is_live = 1; + memset(&read->sl_hdr, 0, sizeof(GF_SLHeader)); + + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, GF_OK); + mp3_setup_object(read); + } + if (!data_size) return; + + read->data = realloc(read->data, sizeof(char)*(read->data_size+data_size) ); + memcpy(read->data + read->data_size, data, sizeof(char)*data_size); + read->data_size += data_size; + if (!read->ch) return; + + + data = read->data; + data_size = read->data_size; + + while (1) { + hdr = gf_mp3_get_next_header_mem(data, data_size, &pos); + + if (hdr) size = gf_mp3_frame_size(hdr); + + /*not enough data, copy over*/ + if (!hdr || (pos+size>data_size)) { + char *d = malloc(sizeof(char) * data_size); + memcpy(d, data, sizeof(char) * data_size); + free(read->data); + read->data = d; + read->data_size = data_size; + + MP3_RegulateDataRate(read); + return; + } + + read->sl_hdr.accessUnitStartFlag = 1; + read->sl_hdr.accessUnitEndFlag = 1; + read->sl_hdr.AU_sequenceNumber++; + read->sl_hdr.compositionTimeStampFlag = 1; + read->sl_hdr.compositionTimeStamp += gf_mp3_window_size(hdr); + gf_term_on_sl_packet(read->service, read->ch, data + pos, size, &read->sl_hdr, GF_OK); + data += pos + size; + assert(data_size>=pos+size); + data_size -= pos+size; + } +} + +void MP3_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + const char *szCache; + u32 total_size, bytes_done; + MP3Reader *read = (MP3Reader *) cbk; + + e = param->error; + /*done*/ + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + if (read->stream) { + read->is_remote = 0; + e = GF_EOS; + } else { + return; + } + } + else if (param->msg_type==GF_NETIO_PARSE_HEADER) { + if (!strcmp(param->name, "icy-name")) { + if (read->icy_name) free(read->icy_name); + read->icy_name = strdup(param->value); + } + if (!strcmp(param->name, "icy-genre")) { + if (read->icy_genre) free(read->icy_genre); + read->icy_genre = strdup(param->value); + } + if (!strcmp(param->name, "icy-meta")) { + GF_NetworkCommand com; + char *meta; + if (read->icy_track_name) free(read->icy_track_name); + read->icy_track_name = NULL; + meta = param->value; + while (meta && meta[0]) { + char *sep = strchr(meta, ';'); + if (sep) sep[0] = 0; + + if (!strnicmp(meta, "StreamTitle=", 12)) { + read->icy_track_name = strdup(meta+12); + } + if (!sep) break; + sep[0] = ';'; + meta = sep+1; + } + + com.base.command_type = GF_NET_SERVICE_INFO; + gf_term_on_command(read->service, &com, GF_OK); + } + return; + } else { + /*handle service message*/ + gf_term_download_update_stats(read->dnload); + if (param->msg_type!=GF_NETIO_DATA_EXCHANGE) return; + } + + if (e >= GF_OK) { + if (read->needs_connection) { + gf_dm_sess_get_stats(read->dnload, NULL, NULL, &total_size, NULL, NULL, NULL); + if (!total_size) read->is_live = 1; + } + /*looks like a live stream*/ + if (read->is_live) { + if (!e) MP3_OnLiveData(read, param->data, param->size); + return; + } + + if (read->stream) return; + + /*open service*/ + szCache = gf_dm_sess_get_cache_name(read->dnload); + if (!szCache) e = GF_IO_ERR; + else { + read->stream = fopen((char *) szCache, "rb"); + if (!read->stream) e = GF_SERVICE_ERROR; + else { + /*if full file at once (in cache) parse duration*/ + if (e==GF_EOS) read->is_remote = 0; + e = GF_OK; + /*not enough data*/ + if (!MP3_ConfigureFromFile(read)) { + gf_dm_sess_get_stats(read->dnload, NULL, NULL, NULL, &bytes_done, NULL, NULL); + /*bad data - there's likely some ID3 around...*/ + if (bytes_done>10*1024) { + e = GF_CORRUPTED_DATA; + } else { + fclose(read->stream); + read->stream = NULL; + return; + } + } + } + } + } + + /*OK confirm*/ + if (read->needs_connection) { + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, e); + if (!e) mp3_setup_object(read); + } +} + +void mp3_download_file(GF_InputService *plug, char *url) +{ + MP3Reader *read = (MP3Reader*) plug->priv; + + read->needs_connection = 1; + + read->dnload = gf_term_download_new(read->service, url, 0, MP3_NetIO, read); + if (!read->dnload) { + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } + /*service confirm is done once fetched*/ +} + + +static GF_Err MP3_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + char szURL[2048]; + char *ext; + GF_Err reply; + MP3Reader *read = plug->priv; + read->service = serv; + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + strcpy(szURL, url); + ext = strrchr(szURL, '#'); + if (ext) ext[0] = 0; + + /*remote fetch*/ + read->is_remote = !mp3_is_local(szURL); + if (read->is_remote) { + mp3_download_file(plug, (char *) szURL); + return GF_OK; + } + + reply = GF_OK; + read->stream = fopen(szURL, "rb"); + if (!read->stream) { + reply = GF_URL_ERROR; + } else if (!MP3_ConfigureFromFile(read)) { + fclose(read->stream); + read->stream = NULL; + reply = GF_NOT_SUPPORTED; + } + gf_term_on_connect(serv, NULL, reply); + if (!reply) mp3_setup_object(read); + return GF_OK; +} + +static GF_Err MP3_CloseService(GF_InputService *plug) +{ + MP3Reader *read = plug->priv; + if (read->stream) fclose(read->stream); + read->stream = NULL; + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + if (read->data) free(read->data); + read->data = NULL; + gf_term_on_disconnect(read->service, NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *MP3_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + MP3Reader *read = plug->priv; + + /*override default*/ + if (expect_type==GF_MEDIA_OBJECT_UNDEF) expect_type=GF_MEDIA_OBJECT_AUDIO; + + /*audio object*/ + if (expect_type==GF_MEDIA_OBJECT_AUDIO) { + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + GF_ESD *esd = MP3_GetESD(read); + od->objectDescriptorID = 1; + gf_list_add(od->ESDescriptors, esd); + return (GF_Descriptor *) od; + } + read->is_inline = 1; + return NULL; +} + +static GF_Err MP3_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ES_ID; + GF_Err e; + MP3Reader *read = plug->priv; + + e = GF_SERVICE_ERROR; + if (read->ch==channel) goto exit; + + e = GF_STREAM_NOT_FOUND; + if (strstr(url, "ES_ID")) { + sscanf(url, "ES_ID=%d", &ES_ID); + } + /*URL setup*/ + else if (!read->ch && MP3_CanHandleURL(plug, url)) ES_ID = 1; + + if (ES_ID==1) { + read->ch = channel; + e = GF_OK; + } + +exit: + gf_term_on_connect(read->service, channel, e); + return e; +} + +static GF_Err MP3_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + MP3Reader *read = plug->priv; + GF_Err e = GF_STREAM_NOT_FOUND; + if (read->ch == channel) { + read->ch = NULL; + if (read->data) free(read->data); + read->data = NULL; + e = GF_OK; + } + gf_term_on_disconnect(read->service, channel, e); + return GF_OK; +} + +static GF_Err MP3_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + MP3Reader *read = plug->priv; + + if (com->base.command_type==GF_NET_SERVICE_INFO) { + com->info.name = read->icy_track_name ? read->icy_track_name : read->icy_name; + com->info.comment = read->icy_genre; + return GF_OK; + } + + if (!com->base.on_channel) { + /*if live session we may cache*/ + if (read->is_live && (com->command_type==GF_NET_IS_CACHABLE)) return GF_OK; + return GF_NOT_SUPPORTED; + } + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: + if ((read->ch == com->base.on_channel) && read->is_live) return GF_NOT_SUPPORTED; + return GF_OK; + case GF_NET_CHAN_INTERACTIVE: + if ((read->ch == com->base.on_channel) && read->is_live) return GF_NOT_SUPPORTED; + return GF_OK; + case GF_NET_CHAN_BUFFER: + if ((read->ch == com->base.on_channel) && read->is_live) { + if (com->buffer.max<2000) com->buffer.max = 2000; + com->buffer.min = com->buffer.max/2; + } + return GF_OK; + case GF_NET_CHAN_SET_PADDING: + read->pad_bytes = com->pad.padding_bytes; + return GF_OK; + case GF_NET_CHAN_DURATION: + com->duration.duration = read->duration; + com->duration.duration /= read->sample_rate; + return GF_OK; + case GF_NET_CHAN_PLAY: + read->start_range = com->play.start_range; + read->end_range = com->play.end_range; + read->current_time = 0; + if (read->stream) fseek(read->stream, 0, SEEK_SET); + + if (read->ch == com->base.on_channel) { + read->done = 0; + /*PLAY after complete download, estimate duration*/ + if (!read->is_remote && !read->duration) { + MP3_ConfigureFromFile(read); + if (read->duration) { + GF_NetworkCommand rcfg; + rcfg.base.on_channel = read->ch; + rcfg.base.command_type = GF_NET_CHAN_DURATION; + rcfg.duration.duration = read->duration; + rcfg.duration.duration /= read->sample_rate; + gf_term_on_command(read->service, &rcfg, GF_OK); + } + } + } + return GF_OK; + case GF_NET_CHAN_STOP: + return GF_OK; + default: + return GF_OK; + } +} + + +static GF_Err MP3_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + u32 pos, hdr, start_from; + MP3Reader *read = plug->priv; + + if (read->ch != channel) return GF_STREAM_NOT_FOUND; + + *out_reception_status = GF_OK; + *sl_compressed = 0; + *is_new_data = 0; + + memset(&read->sl_hdr, 0, sizeof(GF_SLHeader)); + read->sl_hdr.randomAccessPointFlag = 1; + read->sl_hdr.compositionTimeStampFlag = 1; + + /*fetching es data*/ + if (read->done) { + *out_reception_status = GF_EOS; + return GF_OK; + } + if (!read->data) { + if (!read->stream) { + *out_data_ptr = NULL; + *out_data_size = 0; + return GF_OK; + } + *is_new_data = 1; + + pos = ftell(read->stream); + hdr = gf_mp3_get_next_header(read->stream); + if (!hdr) { + if (!read->dnload) { + *out_reception_status = GF_EOS; + read->done = 1; + } else { + fseek(read->stream, pos, SEEK_SET); + *out_reception_status = GF_OK; + } + return GF_OK; + } + read->data_size = gf_mp3_frame_size(hdr); + if (!read->data_size) { + *out_reception_status = GF_EOS; + read->done = 1; + return GF_OK; + } + + + /*we're seeking*/ + if (read->start_range && read->duration) { + read->current_time = 0; + start_from = (u32) (read->start_range * read->sample_rate); + fseek(read->stream, 0, SEEK_SET); + while (read->current_time<start_from) { + hdr = gf_mp3_get_next_header(read->stream); + if (!hdr) { + read->start_range = 0; + *out_reception_status = GF_SERVICE_ERROR; + return GF_OK; + } + read->current_time += gf_mp3_window_size(hdr); + read->data_size = gf_mp3_frame_size(hdr); + fseek(read->stream, read->data_size-4, SEEK_CUR); + } + read->start_range = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[MP3Demux] Seeking to frame size %d - TS %d - file pos %d\n", read->data_size, read->current_time, ftell(read->stream))); + } + + read->sl_hdr.compositionTimeStamp = read->current_time; + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[MP3Demux] Found new frame size %d - TS %d - file pos %d\n", read->data_size, read->current_time, ftell(read->stream))); + + read->current_time += gf_mp3_window_size(hdr); + + read->data = malloc(sizeof(char) * (read->data_size+read->pad_bytes)); + read->data[0] = (hdr >> 24) & 0xFF; + read->data[1] = (hdr >> 16) & 0xFF; + read->data[2] = (hdr >> 8) & 0xFF; + read->data[3] = (hdr ) & 0xFF; + /*end of file*/ + if (fread(&read->data[4], 1, read->data_size - 4, read->stream) != read->data_size-4) { + free(read->data); + read->data = NULL; + if (read->is_remote) { + fseek(read->stream, pos, SEEK_SET); + *out_reception_status = GF_OK; + } else { + *out_reception_status = GF_EOS; + } + return GF_OK; + } + if (read->pad_bytes) memset(read->data + read->data_size, 0, sizeof(char) * read->pad_bytes); + } + *out_sl_hdr = read->sl_hdr; + *out_data_ptr = read->data; + *out_data_size = read->data_size; + return GF_OK; +} + +static GF_Err MP3_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) +{ + MP3Reader *read = plug->priv; + + if (read->ch == channel) { + if (!read->data) return GF_BAD_PARAM; + free(read->data); + read->data = NULL; + return GF_OK; + } + return GF_OK; +} + +GF_InputService *MP3_Load() +{ + MP3Reader *reader; + GF_InputService *plug = malloc(sizeof(GF_InputService)); + memset(plug, 0, sizeof(GF_InputService)); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC MP3 Reader", "gpac distribution") + + plug->CanHandleURL = MP3_CanHandleURL; + plug->ConnectService = MP3_ConnectService; + plug->CloseService = MP3_CloseService; + plug->GetServiceDescriptor = MP3_GetServiceDesc; + plug->ConnectChannel = MP3_ConnectChannel; + plug->DisconnectChannel = MP3_DisconnectChannel; + plug->ServiceCommand = MP3_ServiceCommand; + /*we do support pull mode*/ + plug->ChannelGetSLP = MP3_ChannelGetSLP; + plug->ChannelReleaseSLP = MP3_ChannelReleaseSLP; + + reader = malloc(sizeof(MP3Reader)); + memset(reader, 0, sizeof(MP3Reader)); + plug->priv = reader; + return plug; +} + +void MP3_Delete(void *ifce) +{ + GF_InputService *plug = (GF_InputService *) ifce; + MP3Reader *read = plug->priv; + free(read); + free(plug); +} + + +#ifdef GPAC_HAS_MAD +GF_BaseDecoder *NewMADDec(); +void DeleteMADDec(GF_BaseDecoder *ifcg); +#endif + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return 1; +#ifdef GPAC_HAS_MAD + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return 1; +#endif + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return (GF_BaseInterface *)MP3_Load(); +#ifdef GPAC_HAS_MAD + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return (GF_BaseInterface *)NewMADDec(); +#endif + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { +#ifdef GPAC_HAS_MAD + case GF_MEDIA_DECODER_INTERFACE: + DeleteMADDec((GF_BaseDecoder *) ifce); + break; +#endif + case GF_NET_CLIENT_INTERFACE: + MP3_Delete(ifce); + break; + } +} diff --git a/modules/mp3_in/mp3_in.def b/modules/mp3_in/mp3_in.def new file mode 100644 index 0000000..84a59f0 --- /dev/null +++ b/modules/mp3_in/mp3_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_mp3_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/mpegts_in/Makefile b/modules/mpegts_in/Makefile new file mode 100644 index 0000000..23a2090 --- /dev/null +++ b/modules/mpegts_in/Makefile @@ -0,0 +1,67 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/mpegts_in + +CFLAGS= $(OPTFLAGS) -w -I$(SRC_PATH)/include + + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g -DDEBUG +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +ifeq ($(LINUX_DVB), yes) +CFLAGS+=-DGPAC_HAS_LINUX_DVB +endif + +#common obj +OBJS=mpegts_in.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_mpegts_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols mpegts_in.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/mpegts_in/mpegts_in.c b/modules/mpegts_in/mpegts_in.c new file mode 100644 index 0000000..5e79f16 --- /dev/null +++ b/modules/mpegts_in/mpegts_in.c @@ -0,0 +1,1312 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / M2TS reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/modules/service.h> +#include <gpac/modules/codec.h> +#include <gpac/mpegts.h> +#include <gpac/thread.h> +#include <gpac/network.h> +#include <gpac/constants.h> + +#ifdef GPAC_HAS_LINUX_DVB +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <linux/dvb/dmx.h> +#include <linux/dvb/frontend.h> + +typedef struct { + u32 freq; + u16 vpid; + u16 apid; + fe_spectral_inversion_t specInv; + fe_modulation_t modulation; + fe_bandwidth_t bandwidth; + fe_transmit_mode_t TransmissionMode; + fe_guard_interval_t guardInterval; + fe_code_rate_t HP_CodeRate; + fe_code_rate_t LP_CodeRate; + fe_hierarchy_t hierarchy; + + int ts_fd; +} GF_Tuner; + +#define DVB_BUFFER_SIZE 3760 // DVB buffer size 188x20 + +#endif + +#define UDP_BUFFER_SIZE 0x40000 + +typedef struct { + char *fragment; + u32 id; + /*if only pid is requested*/ + u32 pid; +} M2TSIn_Prog; + +typedef struct +{ + GF_ClientService *service; + GF_M2TS_Demuxer *ts; + + Bool request_all_pids; + GF_List *requested_progs; + GF_List *requested_pids; + + /*demuxer thread*/ + GF_Thread *th; + u32 run_state; + GF_Mutex *mx; + /*net playing*/ + GF_Socket *sock; + +#ifdef GPAC_HAS_LINUX_DVB + /*dvb playing*/ + GF_Tuner *tuner; +#endif + /*local file playing*/ + FILE *file; + char filename[GF_MAX_PATH]; + u32 start_range, end_range; + u32 file_size; + Double duration; + u32 nb_playing; + Bool file_regulate; + u64 pcr_last; + u32 stb_at_last_pcr; + u32 nb_pck; + + Bool epg_requested; + Bool has_eit; + LPNETCHANNEL eit_channel; +} M2TSIn; + +#define M2TS_BUFFER_MAX 400 + + +#ifdef GPAC_HAS_LINUX_DVB + +static GF_Err gf_dvb_tune(GF_Tuner *tuner, char *url, const char *chan_path) { + struct dmx_pes_filter_params pesFilterParams; + struct dvb_frontend_parameters frp; + int demux1, front1; + FILE *chanfile; + char line[255], chan_name_t[255]; + char freq_str[255], inv[255], bw[255], lcr[255], hier[255], cr[255], + mod[255], transm[255], gi[255], apid_str[255], vpid_str[255]; + const char *chan_conf = ":%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:"; + char *chan_name; + char *tmp; + char frontend_name[100], demux_name[100], dvr_name[100]; + u32 adapter_num; + + chanfile = fopen(chan_path, "r"); + if (!chanfile) return GF_BAD_PARAM; + + chan_name = url+6; // 6 = strlen("dvb://") + + // support for multiple frontends + tmp = strchr(chan_name, '@'); + if (tmp) { + adapter_num = atoi(tmp+1); + tmp[0] = 0; + } else { + adapter_num = 0; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Channel name %s\n", chan_name)); + + while(!feof(chanfile)) { + if ( fgets(line, 255, chanfile) != NULL) { + if (line[0]=='#') continue; + if (line[0]=='\r') continue; + if (line[0]=='\n') continue; + + strncpy(chan_name_t, line, index(line, ':')-line); + if (strncmp(chan_name,chan_name_t,strlen(chan_name))==0) { + sscanf(strstr(line,":"), chan_conf, freq_str, inv, bw, lcr, cr, mod, transm, gi, hier, apid_str, vpid_str); + tuner->freq = (uint32_t) atoi(freq_str); + tuner->apid = (uint16_t) atoi(apid_str); + tuner->vpid = (uint16_t) atoi(vpid_str); + //Inversion + if(! strcmp(inv, "INVERSION_ON")) tuner->specInv = INVERSION_ON; + else if(! strcmp(inv, "INVERSION_OFF")) tuner->specInv = INVERSION_OFF; + else tuner->specInv = INVERSION_AUTO; + //LP Code Rate + if(! strcmp(lcr, "FEC_1_2")) tuner->LP_CodeRate =FEC_1_2; + else if(! strcmp(lcr, "FEC_2_3")) tuner->LP_CodeRate =FEC_2_3; + else if(! strcmp(lcr, "FEC_3_4")) tuner->LP_CodeRate =FEC_3_4; + else if(! strcmp(lcr, "FEC_4_5")) tuner->LP_CodeRate =FEC_4_5; + else if(! strcmp(lcr, "FEC_6_7")) tuner->LP_CodeRate =FEC_6_7; + else if(! strcmp(lcr, "FEC_8_9")) tuner->LP_CodeRate =FEC_8_9; + else if(! strcmp(lcr, "FEC_5_6")) tuner->LP_CodeRate =FEC_5_6; + else if(! strcmp(lcr, "FEC_7_8")) tuner->LP_CodeRate =FEC_7_8; + else if(! strcmp(lcr, "FEC_NONE")) tuner->LP_CodeRate =FEC_NONE; + else tuner->LP_CodeRate =FEC_AUTO; + //HP Code Rate + if(! strcmp(cr, "FEC_1_2")) tuner->HP_CodeRate =FEC_1_2; + else if(! strcmp(cr, "FEC_2_3")) tuner->HP_CodeRate =FEC_2_3; + else if(! strcmp(cr, "FEC_3_4")) tuner->HP_CodeRate =FEC_3_4; + else if(! strcmp(cr, "FEC_4_5")) tuner->HP_CodeRate =FEC_4_5; + else if(! strcmp(cr, "FEC_6_7")) tuner->HP_CodeRate =FEC_6_7; + else if(! strcmp(cr, "FEC_8_9")) tuner->HP_CodeRate =FEC_8_9; + else if(! strcmp(cr, "FEC_5_6")) tuner->HP_CodeRate =FEC_5_6; + else if(! strcmp(cr, "FEC_7_8")) tuner->HP_CodeRate =FEC_7_8; + else if(! strcmp(cr, "FEC_NONE")) tuner->HP_CodeRate =FEC_NONE; + else tuner->HP_CodeRate =FEC_AUTO; + //Modulation + if(! strcmp(mod, "QAM_128")) tuner->modulation = QAM_128; + else if(! strcmp(mod, "QAM_256")) tuner->modulation = QAM_256; + else if(! strcmp(mod, "QAM_64")) tuner->modulation = QAM_64; + else if(! strcmp(mod, "QAM_32")) tuner->modulation = QAM_32; + else if(! strcmp(mod, "QAM_16")) tuner->modulation = QAM_16; + //Bandwidth + if(! strcmp(bw, "BANDWIDTH_6_MHZ")) tuner->bandwidth = BANDWIDTH_6_MHZ; + else if(! strcmp(bw, "BANDWIDTH_7_MHZ")) tuner->bandwidth = BANDWIDTH_7_MHZ; + else if(! strcmp(bw, "BANDWIDTH_8_MHZ")) tuner->bandwidth = BANDWIDTH_8_MHZ; + //Transmission Mode + if(! strcmp(transm, "TRANSMISSION_MODE_2K")) tuner->TransmissionMode = TRANSMISSION_MODE_2K; + else if(! strcmp(transm, "TRANSMISSION_MODE_8K")) tuner->TransmissionMode = TRANSMISSION_MODE_8K; + //Guard Interval + if(! strcmp(gi, "GUARD_INTERVAL_1_32")) tuner->guardInterval = GUARD_INTERVAL_1_32; + else if(! strcmp(gi, "GUARD_INTERVAL_1_16")) tuner->guardInterval = GUARD_INTERVAL_1_16; + else if(! strcmp(gi, "GUARD_INTERVAL_1_8")) tuner->guardInterval = GUARD_INTERVAL_1_8; + else tuner->guardInterval = GUARD_INTERVAL_1_4; + //Hierarchy + if(! strcmp(hier, "HIERARCHY_1")) tuner->hierarchy = HIERARCHY_1; + else if(! strcmp(hier, "HIERARCHY_2")) tuner->hierarchy = HIERARCHY_2; + else if(! strcmp(hier, "HIERARCHY_4")) tuner->hierarchy = HIERARCHY_4; + else if(! strcmp(hier, "HIERARCHY_AUTO")) tuner->hierarchy = HIERARCHY_AUTO; + else tuner->hierarchy = HIERARCHY_NONE; + + break; + } + } + } + fclose(chanfile); + + sprintf(frontend_name, "/dev/dvb/adapter%d/frontend0", adapter_num); + sprintf(demux_name, "/dev/dvb/adapter%d/demux0", adapter_num); + sprintf(dvr_name, "/dev/dvb/adapter%d/dvr0", adapter_num); + + // Open frontend + if((front1 = open(frontend_name,O_RDWR|O_NONBLOCK)) < 0){ + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Cannot open frontend %s.\n", frontend_name)); + return GF_IO_ERR; + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Frontend %s opened.\n", frontend_name)); + } + // Open demuxes + if ((demux1=open(demux_name, O_RDWR|O_NONBLOCK)) < 0){ + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Cannot open demux %s\n", demux_name)); + return GF_IO_ERR; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Demux %s opened.\n", demux_name)); + } + // Set FrontendParameters - DVB-T + frp.frequency = tuner->freq; + frp.inversion = tuner->specInv; + frp.u.ofdm.bandwidth = tuner->bandwidth; + frp.u.ofdm.code_rate_HP = tuner->HP_CodeRate; + frp.u.ofdm.code_rate_LP = tuner->LP_CodeRate; + frp.u.ofdm.constellation = tuner->modulation; + frp.u.ofdm.transmission_mode = tuner->TransmissionMode; + frp.u.ofdm.guard_interval = tuner->guardInterval; + frp.u.ofdm.hierarchy_information = tuner->hierarchy; + // Set frontend + if (ioctl(front1, FE_SET_FRONTEND, &frp) < 0){ + return GF_IO_ERR; + } + // Set dumex + pesFilterParams.pid = 0x2000; // Linux-DVB API take PID=2000 for FULL/RAW TS flag + pesFilterParams.input = DMX_IN_FRONTEND; + pesFilterParams.output = DMX_OUT_TS_TAP; + pesFilterParams.pes_type = DMX_PES_OTHER; + pesFilterParams.flags = DMX_IMMEDIATE_START; + if (ioctl(demux1, DMX_SET_PES_FILTER, &pesFilterParams) < 0){ + return GF_IO_ERR; + } + /* The following code differs from mplayer and alike because the device is opened in blocking mode */ + if ((tuner->ts_fd = open(dvr_name, O_RDONLY/*|O_NONBLOCK*/)) < 0){ + return GF_IO_ERR; + } + return GF_OK; +} +#endif + +static Bool M2TS_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt; + + if (!strnicmp(url, "udp://", 6) + || !strnicmp(url, "mpegts-udp://", 13) + || !strnicmp(url, "mpegts-tcp://", 13) +#ifdef GPAC_HAS_LINUX_DVB + || !strnicmp(url, "dvb://", 6) +#endif + ) { + return 1; + } + + sExt = strrchr(url, '.'); + if (!sExt) return 0; + if (gf_term_check_extension(plug, "video/mpeg-2", "ts m2t", "MPEG-2 TS", sExt)) return 1; + return 0; +} + +#ifdef GPAC_HAS_LINUX_DVB +static u32 gf_dvb_get_freq_from_url(const char *channels_config_path, const char *url) +{ + FILE *channels_config_file; + char line[255], name[255], *tmp, *channel_name; + + u32 freq; + + /* get rid of trailing @ */ + tmp = strchr(url, '@'); + if (tmp) tmp[0] = 0; + + channel_name = url+6; + + channels_config_file = fopen(channels_config_path, "r"); + if (!channels_config_file) return GF_BAD_PARAM; + + freq = 0; + while(!feof(channels_config_file)) { + if ( fgets(line, 255, channels_config_file) != NULL) { + if (line[0]=='#') continue; + if (line[0]=='\r') continue; + if (line[0]=='\n') continue; + + tmp = strchr(line, ':'); + tmp[0] = 0; + if (!strcmp(line, channel_name)) { + char *tmp2; + tmp++; + tmp2 = strchr(tmp, ':'); + if (tmp2) tmp2[0] = 0; + freq = (u32)atoi(tmp); + break; + } + } + } + return freq; +} +#endif + +static Bool M2TS_CanHandleURLInService(GF_InputService *plug, const char *url) +{ + Bool ret = 0; + M2TSIn *m2ts = (M2TSIn *)plug->priv; + +#ifdef GPAC_HAS_LINUX_DVB + if (!stricmp(url, "dvb://EPG")) return 1; + if (!strnicmp(url, "dvb://", 6)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[DVBIn] Checking reuse of the same tuner for %s\n", url)); + const char *chan_conf = gf_modules_get_option((GF_BaseInterface *)plug, "DVB", "ChannelsFile"); + if (!chan_conf) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[DVBIn] Cannot locate channel configuration file\n")); + ret = 0; + } + + /* if the tuner is already tuned to the same frequence, nothing needs to be done */ + else if (m2ts->tuner->freq != 0) { + char *frag = strchr(url, '#'); + if (frag) frag[0] = 0; + if (m2ts->tuner->freq == gf_dvb_get_freq_from_url(chan_conf, url)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[DVBIn] Reusing the same tuner for %s\n", url)); + ret = 1; + } + if (frag) frag[0] = '#'; + } + } else +#endif + if (!strnicmp(url, "udp://", 6) + || !strnicmp(url, "mpegts-udp://", 13) + || !strnicmp(url, "mpegts-tcp://", 13)) + { + /* TODO: check IP address ...*/ + ret = 0; + } else { + char *frag = strchr(url, '#'); + if (frag) frag[0] = 0; + if (!strlen(url) || !strcmp(url, m2ts->filename)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[DVBIn] Reusing the same input file for %s\n", url)); + ret = 1; + } + if (frag) frag[0] = '#'; + } + return ret; +} + +static GF_ObjectDescriptor *MP2TS_GetOD(M2TSIn *m2ts, GF_M2TS_PES *stream, char *dsi, u32 dsi_size, u32 *streamType) +{ + GF_ObjectDescriptor *od; + GF_ESD *esd; + + /*create a stream description for this channel*/ + esd = gf_odf_desc_esd_new(0); + esd->ESID = stream->pid; + /*ASSIGN PCR here*/ + esd->OCRESID = stream->program->pcr_pid; + switch (stream->stream_type) { + case GF_M2TS_VIDEO_MPEG1: + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + esd->decoderConfig->objectTypeIndication = 0x6A; + break; + case GF_M2TS_VIDEO_MPEG2: + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + esd->decoderConfig->objectTypeIndication = 0x65; + break; + case GF_M2TS_VIDEO_MPEG4: + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + esd->decoderConfig->objectTypeIndication = 0x20; + break; + case GF_M2TS_VIDEO_H264: + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + esd->decoderConfig->objectTypeIndication = 0x21; + break; + case GF_M2TS_AUDIO_MPEG1: + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + esd->decoderConfig->objectTypeIndication = 0x6B; + break; + case GF_M2TS_AUDIO_MPEG2: + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + esd->decoderConfig->objectTypeIndication = 0x69; + break; + case GF_M2TS_AUDIO_LATM_AAC: + case GF_M2TS_AUDIO_AAC: + if (!dsi) { + /*discard regulate until we fetch the AAC config*/ + m2ts->file_regulate = 0; + /*turn on parsing*/ + gf_m2ts_set_pes_framing(stream, GF_M2TS_PES_FRAMING_DEFAULT); + gf_odf_desc_del((GF_Descriptor *)esd); + return NULL; + } + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + esd->decoderConfig->objectTypeIndication = 0x40; + break; + case GF_M2TS_SYSTEMS_MPEG4_SECTIONS: + default: + gf_odf_desc_del((GF_Descriptor *)esd); + return NULL; + } + esd->decoderConfig->bufferSizeDB = 0; + + /*we only use AUstart indicator*/ + esd->slConfig->useAccessUnitStartFlag = 1; + esd->slConfig->useAccessUnitEndFlag = 0; + esd->slConfig->useRandomAccessPointFlag = 1; + esd->slConfig->AUSeqNumLength = 0; + esd->slConfig->timestampResolution = 90000; + + /*decoder config*/ + if (dsi) { + esd->decoderConfig->decoderSpecificInfo->data = malloc(sizeof(char)*dsi_size); + memcpy(esd->decoderConfig->decoderSpecificInfo->data, dsi, sizeof(char)*dsi_size); + esd->decoderConfig->decoderSpecificInfo->dataLength = dsi_size; + } + + /*declare object to terminal*/ + od = (GF_ObjectDescriptor*)gf_odf_desc_new(GF_ODF_OD_TAG); + gf_list_add(od->ESDescriptors, esd); + od->objectDescriptorID = stream->pid; + if (streamType) *streamType = esd->decoderConfig->streamType; + return od; +} + +static void MP2TS_DeclareStream(M2TSIn *m2ts, GF_M2TS_PES *stream, char *dsi, u32 dsi_size) +{ + GF_ObjectDescriptor *od = MP2TS_GetOD(m2ts, stream, dsi, dsi_size, NULL); + if (!od) return; + /*declare but don't regenerate scene*/ + gf_term_add_media(m2ts->service, (GF_Descriptor*)od, 1); +} + +static void MP2TS_SetupProgram(M2TSIn *m2ts, GF_M2TS_Program *prog, Bool regenerate_scene, Bool no_declare) +{ + u32 i, count; + + count = gf_list_count(prog->streams); +#ifdef GPAC_HAS_LINUX_DVB + if (m2ts->tuner) { + Bool found = 0; + for (i=0; i<count; i++) { + GF_M2TS_PES *pes = gf_list_get(prog->streams, i); + if (pes->pid==m2ts->tuner->vpid) found = 1; + else if (pes->pid==m2ts->tuner->apid) found = 1; + } + if (!found) return; + } +#endif + m2ts->file_regulate = no_declare ? 0 : 1; + + for (i=0; i<count; i++) { + GF_M2TS_ES *es = gf_list_get(prog->streams, i); + if (es->pid==prog->pmt_pid) continue; + /*move to skip mode for all PES until asked for playback*/ + if (!(es->flags & GF_M2TS_ES_IS_SECTION) && !es->user) + gf_m2ts_set_pes_framing((GF_M2TS_PES *)es, GF_M2TS_PES_FRAMING_SKIP); + if (!prog->pmt_iod && !no_declare) MP2TS_DeclareStream(m2ts, (GF_M2TS_PES *)es, NULL, 0); + } + /*force scene regeneration*/ + if (regenerate_scene) + gf_term_add_media(m2ts->service, NULL, 0); +} + +static void MP2TS_SendPacket(M2TSIn *m2ts, GF_M2TS_PES_PCK *pck) +{ + GF_SLHeader slh; + + if (!pck->stream->user) return; + + if (!pck->stream->program->first_dts && pck->PTS) { + pck->stream->program->first_dts = (pck->DTS ? pck->DTS : pck->PTS) - m2ts->start_range * 90; + } + + memset(&slh, 0, sizeof(GF_SLHeader)); + slh.accessUnitStartFlag = (pck->flags & GF_M2TS_PES_PCK_AU_START) ? 1 : 0; + if (slh.accessUnitStartFlag) { + if (pck->PTS < pck->stream->program->first_dts) return; + slh.compositionTimeStampFlag = 1; + slh.compositionTimeStamp = pck->PTS - pck->stream->program->first_dts; + if (pck->DTS) { + slh.decodingTimeStampFlag = 1; + slh.decodingTimeStamp = pck->DTS - pck->stream->program->first_dts; + } + slh.randomAccessPointFlag = (pck->flags & GF_M2TS_PES_PCK_RAP) ? 1 : 0; + } + gf_term_on_sl_packet(m2ts->service, pck->stream->user, pck->data, pck->data_len, &slh, GF_OK); +} + +static GFINLINE void MP2TS_SendSLPacket(M2TSIn *m2ts, GF_M2TS_SL_PCK *pck) +{ + gf_term_on_sl_packet(m2ts->service, pck->stream->user, pck->data, pck->data_len, NULL, GF_OK); +} + +static GF_ObjectDescriptor *M2TS_GenerateEPG_OD(M2TSIn *m2ts) +{ + /* declaring a special stream for displaying eit */ + GF_ObjectDescriptor *od; + GF_ESD *esd; + + /*create a stream description for this channel*/ + esd = gf_odf_desc_esd_new(0); + esd->ESID = GF_M2TS_PID_EIT_ST_CIT; + esd->OCRESID = GF_M2TS_PID_EIT_ST_CIT; + esd->decoderConfig->streamType = GF_STREAM_PRIVATE_SCENE; + esd->decoderConfig->objectTypeIndication = GPAC_OTI_PRIVATE_SCENE_EPG; + esd->decoderConfig->bufferSizeDB = 0; + + /*we only use AUstart indicator + esd->slConfig->useAccessUnitStartFlag = 1; + esd->slConfig->useAccessUnitEndFlag = 0; + esd->slConfig->useRandomAccessPointFlag = 1; + esd->slConfig->AUSeqNumLength = 0; + esd->slConfig->timestampResolution = 90000;*/ + + /*declare object to terminal*/ + od = (GF_ObjectDescriptor*)gf_odf_desc_new(GF_ODF_OD_TAG); + gf_list_add(od->ESDescriptors, esd); + od->objectDescriptorID = GF_M2TS_PID_EIT_ST_CIT; + return od; +} + +static void M2TS_FlushRequested(M2TSIn *m2ts) +{ + u32 i, j, req_prog_count, count, prog_id, found; + + gf_mx_p(m2ts->mx); + + found = 0; + count = gf_list_count(m2ts->requested_pids); + for (i=0; i<count; i++) { + M2TSIn_Prog *req_pid = gf_list_get(m2ts->requested_pids, i); + GF_M2TS_ES *es = m2ts->ts->ess[req_pid->pid]; + if (es==NULL) continue; + + /*move to skip mode for all PES until asked for playback*/ + if (!(es->flags & GF_M2TS_ES_IS_SECTION) && !es->user) + gf_m2ts_set_pes_framing((GF_M2TS_PES *)es, GF_M2TS_PES_FRAMING_SKIP); + MP2TS_DeclareStream(m2ts, (GF_M2TS_PES *)es, NULL, 0); + gf_list_rem(m2ts->requested_pids, i); + free(req_pid); + i--; + count--; + found++; + } + req_prog_count = gf_list_count(m2ts->requested_progs); + for (i = 0; i < req_prog_count; i++) { + M2TSIn_Prog *req_prog = gf_list_get(m2ts->requested_progs, i); + prog_id = atoi(req_prog->fragment); + count = gf_list_count(m2ts->ts->SDTs); + for (j=0; j<count; j++) { + GF_M2TS_SDT *sdt = gf_list_get(m2ts->ts->SDTs, j); + if (!stricmp(sdt->service, req_prog->fragment)) req_prog->id = sdt->service_id; + else if (sdt->service_id==prog_id) req_prog->id = sdt->service_id; + } + if (req_prog->id) { + GF_M2TS_Program *ts_prog; + count = gf_list_count(m2ts->ts->programs); + for (j=0; j<count; j++) { + ts_prog = gf_list_get(m2ts->ts->programs, j); + if (ts_prog->number==req_prog->id) { + MP2TS_SetupProgram(m2ts, ts_prog, 0, 0); + found++; + free(req_prog->fragment); + free(req_prog); + gf_list_rem(m2ts->requested_progs, i); + req_prog_count--; + i--; + break; + } + } + } + } + + if (m2ts->epg_requested) { + if (!m2ts->has_eit) { + GF_ObjectDescriptor *od = M2TS_GenerateEPG_OD(m2ts); + /*declare but don't regenerate scene*/ + gf_term_add_media(m2ts->service, (GF_Descriptor*)od, 0); + m2ts->has_eit = 1; + } + } else { + /*force scene regeneration only when EPG is not requested*/ + if (found) + gf_term_add_media(m2ts->service, NULL, 0); + } + + gf_mx_v(m2ts->mx); +} + +static void M2TS_OnEvent(GF_M2TS_Demuxer *ts, u32 evt_type, void *param) +{ + M2TSIn *m2ts = (M2TSIn *) ts->user; + switch (evt_type) { + case GF_M2TS_EVT_PAT_UPDATE: + break; + case GF_M2TS_EVT_PAT_FOUND: + /* In case the TS has one program, wait for the PMT to send connect, in case of IOD in PMT */ + if (gf_list_count(m2ts->ts->programs) != 1) + gf_term_on_connect(m2ts->service, NULL, GF_OK); + break; + case GF_M2TS_EVT_PMT_FOUND: + if (gf_list_count(m2ts->ts->programs) == 1) + gf_term_on_connect(m2ts->service, NULL, GF_OK); + + /*do not declare if single program was requested for playback*/ + MP2TS_SetupProgram(m2ts, param, m2ts->request_all_pids, m2ts->request_all_pids ? 0 : 1); + + M2TS_FlushRequested(m2ts); + break; + case GF_M2TS_EVT_PMT_REPEAT: + case GF_M2TS_EVT_PMT_UPDATE: + M2TS_FlushRequested(m2ts); + break; + case GF_M2TS_EVT_SDT_REPEAT: + case GF_M2TS_EVT_SDT_UPDATE: + case GF_M2TS_EVT_SDT_FOUND: + M2TS_FlushRequested(m2ts); + break; + case GF_M2TS_EVT_DVB_GENERAL: + if (m2ts->eit_channel) { + GF_M2TS_SL_PCK *pck = (GF_M2TS_SL_PCK *)param; + gf_term_on_sl_packet(m2ts->service, m2ts->eit_channel, pck->data, pck->data_len, NULL, GF_OK); + } + break; + case GF_M2TS_EVT_PES_PCK: + MP2TS_SendPacket(m2ts, param); + break; + case GF_M2TS_EVT_SL_PCK: + MP2TS_SendSLPacket(m2ts, param); + break; + case GF_M2TS_EVT_AAC_CFG: + { + GF_M2TS_PES_PCK *pck = (GF_M2TS_PES_PCK*)param; + if (!pck->stream->first_dts) { + gf_m2ts_set_pes_framing(pck->stream, GF_M2TS_PES_FRAMING_SKIP); + MP2TS_DeclareStream(m2ts, pck->stream, pck->data, pck->data_len); + m2ts->file_regulate = 1; + /*force scene regeneration*/ + gf_term_add_media(m2ts->service, NULL, 0); + } + } + break; + case GF_M2TS_EVT_PES_PCR: + if (m2ts->file_regulate) { + u64 pcr = ((GF_M2TS_PES_PCK *) param)->PTS; + u32 stb = gf_sys_clock(); + if (m2ts->pcr_last) { + s32 diff; + u64 pcr_diff = (pcr - m2ts->pcr_last); + pcr_diff /= 27000; + diff = (u32) pcr_diff - (stb - m2ts->stb_at_last_pcr); + if (diff>0 && (diff<1000) ) { + /*query buffer level, don't sleep if too low*/ + GF_NetworkCommand com; + com.command_type = GF_NET_BUFFER_QUERY; + gf_term_on_command(m2ts->service, &com, GF_OK); + if (com.buffer.occupancy) gf_sleep(diff); + + m2ts->nb_pck = 0; + m2ts->pcr_last = pcr; + m2ts->stb_at_last_pcr = gf_sys_clock(); + } + } else { + m2ts->pcr_last = pcr; + m2ts->stb_at_last_pcr = gf_sys_clock(); + } + } + break; + } +} + + +u32 M2TS_Run(void *_p) +{ + GF_Err e; + char data[UDP_BUFFER_SIZE]; +#ifdef GPAC_HAS_LINUX_DVB + char dvbts[DVB_BUFFER_SIZE]; +#endif + u32 size, i; + M2TSIn *m2ts = _p; + + m2ts->run_state = 1; + m2ts->ts->on_event = M2TS_OnEvent; + gf_m2ts_reset_parsers(m2ts->ts); + +#ifdef GPAC_HAS_LINUX_DVB + if (m2ts->tuner) { + // in case of DVB + while (m2ts->run_state) { + s32 ts_size = read(m2ts->tuner->ts_fd, dvbts, DVB_BUFFER_SIZE); + if (ts_size>0) gf_m2ts_process_data(m2ts->ts, dvbts, (u32) ts_size); + } + } else +#endif + if (m2ts->sock) { + Bool first_run, is_rtp; + first_run = 1; + is_rtp = 0; + while (m2ts->run_state) { + size = 0; + /*m2ts chunks by chunks*/ + e = gf_sk_receive(m2ts->sock, data, UDP_BUFFER_SIZE, 0, &size); + if (!size || e) { + gf_sleep(1); + continue; + } + if (first_run) { + first_run = 0; + /*FIXME: we assume only simple RTP packaging (no CSRC nor extensions)*/ + if ((data[0] != 0x47) && ((data[1] & 0x7F) == 33) ) { + is_rtp = 1; + } + } + /*process chunk*/ + if (is_rtp) { + gf_m2ts_process_data(m2ts->ts, data+12, size-12); + } else { + gf_m2ts_process_data(m2ts->ts, data, size); + } + } + } else { + u32 pos = 0; + if (m2ts->start_range && m2ts->duration) { + Double perc = m2ts->start_range / (1000 * m2ts->duration); + pos = (u32) (s64) (perc * m2ts->file_size); + /*align to TS packet size*/ + while (pos%188) pos++; + if (pos>=m2ts->file_size) { + m2ts->start_range = 0; + pos = 0; + } + } + fseek(m2ts->file, pos, SEEK_SET); + while (m2ts->run_state && !feof(m2ts->file) ) { + /*m2ts chunks by chunks*/ + size = fread(data, 1, 188, m2ts->file); + if (!size) break; + /*process chunk*/ + gf_m2ts_process_data(m2ts->ts, data, size); + + m2ts->nb_pck++; + /*if asked to regulate, wait until we get a play request*/ + while (m2ts->run_state && !m2ts->nb_playing && m2ts->file_regulate) { + gf_sleep(50); + continue; + } + } + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("\nEOS reached\n")); + if (m2ts->nb_playing) { + for (i=0; i<GF_M2TS_MAX_STREAMS; i++) { + GF_M2TS_PES *pes = (GF_M2TS_PES *)m2ts->ts->ess[i]; + if (!pes || (pes->pid==pes->program->pmt_pid)) continue; + if (!pes->user || !pes->reframe) continue; + gf_term_on_sl_packet(m2ts->service, pes->user, NULL, 0, NULL, GF_EOS); + gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP); + } + } + + } + m2ts->run_state = 2; + return 0; +} + +static void M2TS_OnEventPCR(GF_M2TS_Demuxer *ts, u32 evt_type, void *param) +{ + if (evt_type==GF_M2TS_EVT_PES_PCR) { + M2TSIn *m2ts = ts->user; + GF_M2TS_PES_PCK *pck = param; + if (!m2ts->nb_playing) { + m2ts->nb_playing = pck->stream->pid; + m2ts->end_range = (u32) (pck->PTS / 90); + } else if (m2ts->nb_playing == pck->stream->pid) { + m2ts->start_range = (u32) (pck->PTS / 90); + } + } +} + +#ifdef GPAC_HAS_LINUX_DVB +void M2TS_SetupDVB(GF_InputService *plug, M2TSIn *m2ts, char *url) +{ + GF_Err e = GF_OK; + char *str; + const char *chan_conf; + + if (strnicmp(url, "dvb://", 6)) { + e = GF_NOT_SUPPORTED; + goto exit; + } + + chan_conf = gf_modules_get_option((GF_BaseInterface *)plug, "DVB", "ChannelsFile"); + if (!chan_conf) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[DVBIn] Cannot locate channel configuration file\n")); + e = GF_SERVICE_ERROR; + goto exit; + } + + if (!m2ts->tuner) GF_SAFEALLOC(m2ts->tuner, GF_Tuner); + + if (m2ts->tuner->freq != 0 && m2ts->tuner->freq == gf_dvb_get_freq_from_url(chan_conf, url)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[DVBIn] Tuner already tuned to that frequency\n")); + goto exit; + } + + e = gf_dvb_tune(m2ts->tuner, url, chan_conf); + if (e) goto exit; + + m2ts->th = gf_th_new("MPEG-2 TS Demux"); + /*start playing for tune-in*/ + gf_th_run(m2ts->th, M2TS_Run, m2ts); + +exit: + if (e) gf_term_on_connect(m2ts->service, NULL, e); +} +#endif + +void M2TS_SetupLive(GF_InputService *plug, M2TSIn *m2ts, char *url) +{ + GF_Err e = GF_OK; + char *str; + u16 port; + u32 sock_type = 0; + + if (!strnicmp(url, "udp://", 6) || !strnicmp(url, "mpegts-udp://", 13)) { + sock_type = GF_SOCK_TYPE_UDP; + } else if (!strnicmp(url, "mpegts-tcp://", 13) ) { + sock_type = GF_SOCK_TYPE_TCP; + } else { + e = GF_NOT_SUPPORTED; + goto exit; + } + + url = strchr(url, ':'); + url += 3; + + m2ts->sock = gf_sk_new(sock_type); + if (!m2ts->sock) { e = GF_IO_ERR; goto exit; } + + /*setup port and src*/ + port = 1234; + str = strrchr(url, ':'); + /*take care of IPv6 address*/ + if (str && strchr(str, ']')) str = strchr(url, ':'); + if (str) { + port = atoi(str+1); + str[0] = 0; + } + + /*do we have a source ?*/ + if (strlen(url) && strcmp(url, "localhost") ) { + const char *mob_ip = NULL; + const char *mob_on = gf_modules_get_option((GF_BaseInterface*)plug, "Network", "MobileIPEnabled"); + if (mob_on && !strcmp(mob_on, "yes")) + mob_ip = gf_modules_get_option((GF_BaseInterface*)plug, "Network", "MobileIP"); + + if (gf_sk_is_multicast_address(url)) { + const char *mcast_ifce = gf_modules_get_option((GF_BaseInterface *) plug, "Network", "DefaultMCastInterface"); + if (mcast_ifce) mob_ip = mcast_ifce; + + gf_sk_setup_multicast(m2ts->sock, url, port, 0, 0, (char*)mob_ip); + } else { + gf_sk_bind(m2ts->sock, (char*)mob_ip, port, url, 0, GF_SOCK_REUSE_PORT); + } + } + if (str) str[0] = ':'; + + gf_sk_set_buffer_size(m2ts->sock, 0, UDP_BUFFER_SIZE); + gf_sk_set_block_mode(m2ts->sock, 0); + + m2ts->th = gf_th_new("MPEG-2 TS Demux"); + gf_th_set_priority(m2ts->th, GF_THREAD_PRIORITY_HIGHEST); + /*start playing for tune-in*/ + gf_th_run(m2ts->th, M2TS_Run, m2ts); + +exit: + if (e) { + gf_term_on_connect(m2ts->service, NULL, e); + } +} + +void M2TS_SetupFile(M2TSIn *m2ts, char *url) +{ +#if 0 + char data[188]; + u32 size, fsize; + s32 nb_rwd; +#endif + if (m2ts->file && !strcmp(m2ts->filename, url)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEGTSIn] TS already being processed\n")); + return; + } + + m2ts->file = fopen(url, "rb"); + if (!m2ts->file) { + gf_term_on_connect(m2ts->service, NULL, GF_URL_ERROR); + return; + } + strcpy(m2ts->filename, url); + + fseek(m2ts->file, 0, SEEK_END); + m2ts->file_size = ftell(m2ts->file); + +#if 0 + /* + estimate duration by reading the end of the file + m2ts->end_range is initialized to the PTS of the last TS packet + m2ts->nb_playing is initialized to the PID of the last TS packet + */ + m2ts->nb_playing = 0; + m2ts->ts->on_event = M2TS_OnEventPCR; + m2ts->end_range = 0; + nb_rwd = 1; + fsize = m2ts->file_size; + while (fsize % 188) fsize--; + while (1) { + fseek(m2ts->file, fsize - 188 * nb_rwd, SEEK_SET); + /*m2ts chunks by chunks*/ + size = fread(data, 1, 188, m2ts->file); + if (!size) break; + /*process chunk*/ + gf_m2ts_process_data(m2ts->ts, data, size); + if (m2ts->nb_playing) break; + nb_rwd ++; + } + + /* + reset of the file + initialization of m2ts->start_range to the PTS of the first TS packet with PID = m2ts->nb_playing + */ + fseek(m2ts->file, 0, SEEK_SET); + gf_m2ts_reset_parsers(m2ts->ts); + m2ts->start_range = 0; + while (1) { + /*m2ts chunks by chunks*/ + size = fread(data, 1, 188, m2ts->file); + if (!size) break; + /*process chunk*/ + gf_m2ts_process_data(m2ts->ts, data, size); + if (m2ts->start_range) break; + } + m2ts->duration = (m2ts->end_range - m2ts->start_range) / 300000.0; + gf_m2ts_demux_del(m2ts->ts); + + /* Creation of the real demuxer for playback */ + m2ts->ts = gf_m2ts_demux_new(); + m2ts->ts->user = m2ts; +#endif + + /* reinitialization for seek */ + m2ts->end_range = m2ts->start_range = 0; + m2ts->nb_playing = 0; + + m2ts->th = gf_th_new("MPEG-2 TS Demux"); + /*start playing for tune-in*/ + gf_th_run(m2ts->th, M2TS_Run, m2ts); +} + +static GF_Err M2TS_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + char szURL[2048]; + char *frag; + M2TSIn *m2ts = plug->priv; + m2ts->service = serv; + + strcpy(szURL, url); + frag = strrchr(szURL, '#'); + if (frag) frag[0] = 0; + + m2ts->file_regulate = 0; + m2ts->duration = 0; + + if (!strnicmp(url, "udp://", 6) + || !strnicmp(url, "mpegts-udp://", 13) + || !strnicmp(url, "mpegts-tcp://", 13) + ) { + M2TS_SetupLive(plug, m2ts, (char *) szURL); + } +#ifdef GPAC_HAS_LINUX_DVB + else if (!strnicmp(url, "dvb://", 6)) { + // DVB Setup + M2TS_SetupDVB(plug, m2ts, (char *) szURL); + } +#endif + else { + M2TS_SetupFile(m2ts, (char *) szURL); + } + return GF_OK; +} + +static GF_Err M2TS_CloseService(GF_InputService *plug) +{ + M2TSIn *m2ts = plug->priv; + + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("destroying TSin\n")); + if (m2ts->th) { + if (m2ts->run_state == 1) { + m2ts->run_state = 0; + while (m2ts->run_state!=2) gf_sleep(0); + } + gf_th_del(m2ts->th); + m2ts->th = NULL; + } + + if (m2ts->file) fclose(m2ts->file); + m2ts->file = NULL; + gf_term_on_disconnect(m2ts->service, NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *M2TS_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + M2TSIn *m2ts = plug->priv; + GF_Descriptor *desc = NULL; + char *frag; + + frag = sub_url ? strrchr(sub_url, '#') : NULL; + /*we have been requested the entire TS*/ + if (!stricmp(sub_url, "dvb://EPG")) { + m2ts->epg_requested = 1; + } else if (!frag) { + m2ts->request_all_pids = 1; + } else { + M2TSIn_Prog *prog; + frag++; + + /*we need exclusive access*/ + gf_mx_p(m2ts->mx); + if (!strnicmp(frag, "pid=", 4)) { + GF_SAFEALLOC(prog, M2TSIn_Prog); + prog->pid = atoi(frag+4); + gf_list_add(m2ts->requested_pids, prog); + } else if (!strnicmp(frag, "EPG", 3)) { + m2ts->epg_requested = 1; + } else { + u32 i, count; + count = gf_list_count(m2ts->requested_progs); + prog = NULL; + for (i=0; i<count; i++) { + prog = gf_list_get(m2ts->requested_progs, i); + if (!strcmp(prog->fragment, frag)) + break; + prog = NULL; + } + if (!prog) { + GF_SAFEALLOC(prog, M2TSIn_Prog); + gf_list_add(m2ts->requested_progs, prog); + prog->fragment = strdup(frag); + } + } + gf_mx_v(m2ts->mx); + } + + if (expect_type==GF_MEDIA_OBJECT_SCENE) { + if (gf_list_count(m2ts->ts->programs) == 1) { + GF_M2TS_Program *prog = gf_list_get(m2ts->ts->programs, 0); + if (prog->pmt_iod) { + gf_odf_desc_copy((GF_Descriptor *)prog->pmt_iod, &desc); + return desc; + } + } + if (m2ts->epg_requested) { + GF_ObjectDescriptor *od = M2TS_GenerateEPG_OD(m2ts); + m2ts->epg_requested = 0; + return (GF_Descriptor *)od; + } else { + /*returning an empty IOD means "no scene description", let the terminal handle all media objects*/ + desc = gf_odf_desc_new(GF_ODF_IOD_TAG); + ((GF_ObjectDescriptor *) desc)->objectDescriptorID = 1; + return desc; + } + } + + return NULL; +} + +static GF_Err M2TS_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ES_ID; + GF_Err e; + M2TSIn *m2ts = plug->priv; + + e = GF_STREAM_NOT_FOUND; + if (strstr(url, "ES_ID")) { + sscanf(url, "ES_ID=%d", &ES_ID); + + /* In case there is a real IOD, we need to translate PID into ESID */ + if (gf_list_count(m2ts->ts->programs) == 1) { + GF_M2TS_Program *prog = gf_list_get(m2ts->ts->programs, 0); + if (prog->pmt_iod) { + u32 i; + for (i=0; i<GF_M2TS_MAX_STREAMS; i++) { + GF_M2TS_PES *pes = (GF_M2TS_PES *)m2ts->ts->ess[i]; + if (!pes || (pes->pid==pes->program->pmt_pid)) continue; + if (pes->mpeg4_es_id == ES_ID) { + if (pes->user) { + e = GF_SERVICE_ERROR; + gf_term_on_connect(m2ts->service, channel, e); + return e; + } else { + pes->user = channel; + e = GF_OK; + gf_term_on_connect(m2ts->service, channel, e); + return e; + } + } + } + } + } + + if (ES_ID == 18) { + e = GF_OK; /* 18 is the PID of EIT packets */ + m2ts->eit_channel = channel; + } else if ((ES_ID<GF_M2TS_MAX_STREAMS) && m2ts->ts->ess[ES_ID]) { + GF_M2TS_PES *pes = (GF_M2TS_PES *)m2ts->ts->ess[ES_ID]; + if (pes->user) { + e = GF_SERVICE_ERROR; + } else { + pes->user = channel; + e = GF_OK; + } + } + } + gf_term_on_connect(m2ts->service, channel, e); + return e; +} + +static GF_M2TS_PES *M2TS_GetChannel(M2TSIn *m2ts, LPNETCHANNEL channel) +{ + u32 i; + for (i=0; i<GF_M2TS_MAX_STREAMS; i++) { + GF_M2TS_PES *pes = (GF_M2TS_PES *)m2ts->ts->ess[i]; + if (!pes || (pes->pid==pes->program->pmt_pid)) continue; + if (pes->user == channel) return pes; + } + return NULL; +} +static GF_Err M2TS_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + M2TSIn *m2ts = plug->priv; + GF_Err e = GF_STREAM_NOT_FOUND; + GF_M2TS_PES *pes = M2TS_GetChannel(m2ts, channel); + if (pes) { + pes->user = NULL; + e = GF_OK; + } + gf_term_on_disconnect(m2ts->service, channel, e); + return GF_OK; +} + +static GF_Err M2TS_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + GF_M2TS_PES *pes; + M2TSIn *m2ts = plug->priv; + + if (com->command_type==GF_NET_SERVICE_HAS_AUDIO) { + char *frag = strchr(com->audio.base_url, '#'); + if (frag && !strnicmp(frag, "#pid=", 5)) return GF_NOT_SUPPORTED; + return GF_OK; + } + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + switch (com->command_type) { + /*we cannot pull complete AUs from the stream*/ + case GF_NET_CHAN_SET_PULL: + return GF_NOT_SUPPORTED; + /*we cannot seek stream by stream*/ + case GF_NET_CHAN_INTERACTIVE: + return GF_NOT_SUPPORTED; + case GF_NET_CHAN_BUFFER: + com->buffer.max = M2TS_BUFFER_MAX; + com->buffer.min = 0; + return GF_OK; + case GF_NET_CHAN_DURATION: + com->duration.duration = m2ts->duration; + return GF_OK; + case GF_NET_CHAN_PLAY: + pes = M2TS_GetChannel(m2ts, com->base.on_channel); + if (!pes) { + if (com->base.on_channel == m2ts->eit_channel) { + return GF_OK; + } + return GF_STREAM_NOT_FOUND; + } + gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_DEFAULT); + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Setting default reframing\n")); + /*this is a multplex, only trigger the play command for the first stream activated*/ + if (!m2ts->nb_playing) { + m2ts->start_range = (u32) (com->play.start_range*1000); + m2ts->end_range = (com->play.end_range>0) ? (u32) (com->play.end_range*1000) : 0; + /*start demuxer*/ + if (m2ts->run_state!=1) { + gf_th_run(m2ts->th, M2TS_Run, m2ts); + } + } + m2ts->nb_playing++; + return GF_OK; + case GF_NET_CHAN_STOP: + pes = M2TS_GetChannel(m2ts, com->base.on_channel); + if (!pes) { + if (com->base.on_channel == m2ts->eit_channel) { + return GF_OK; + } + return GF_STREAM_NOT_FOUND; + } + gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP); + /*FIXME HOORIBLE HACK*/ + return GF_OK; + assert(m2ts->nb_playing); + m2ts->nb_playing--; + /*stop demuxer*/ + if (!m2ts->nb_playing && (m2ts->run_state==1)) { + m2ts->run_state=0; + while (m2ts->run_state!=2) gf_sleep(2); + } + return GF_OK; + default: + return GF_OK; + } +} + + +GF_InputService *NewM2TSReader() +{ + M2TSIn *reader; + GF_InputService *plug = malloc(sizeof(GF_InputService)); + memset(plug, 0, sizeof(GF_InputService)); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC MPEG-2 TS Reader", "gpac distribution") + + plug->CanHandleURL = M2TS_CanHandleURL; + plug->CanHandleURLInService = M2TS_CanHandleURLInService; + plug->ConnectService = M2TS_ConnectService; + plug->CloseService = M2TS_CloseService; + plug->GetServiceDescriptor = M2TS_GetServiceDesc; + plug->ConnectChannel = M2TS_ConnectChannel; + plug->DisconnectChannel = M2TS_DisconnectChannel; + plug->ServiceCommand = M2TS_ServiceCommand; + + reader = malloc(sizeof(M2TSIn)); + memset(reader, 0, sizeof(M2TSIn)); + plug->priv = reader; + reader->requested_progs = gf_list_new(); + reader->requested_pids = gf_list_new(); + reader->ts = gf_m2ts_demux_new(); + reader->ts->on_event = M2TS_OnEvent; + reader->ts->user = reader; + reader->mx = gf_mx_new("MPEG2 Demux"); + return plug; +} + +void DeleteM2TSReader(void *ifce) +{ + u32 i, count; + + GF_InputService *plug = (GF_InputService *) ifce; + M2TSIn *m2ts = plug->priv; + + count = gf_list_count(m2ts->requested_progs); + for (i = 0; i < count; i++) { + M2TSIn_Prog *prog = gf_list_get(m2ts->requested_progs, i); + free(prog->fragment); + free(prog); + } + gf_list_del(m2ts->requested_progs); + count = gf_list_count(m2ts->requested_pids); + for (i = 0; i < count; i++) { + M2TSIn_Prog *prog = gf_list_get(m2ts->requested_pids, i); + free(prog); + } + gf_list_del(m2ts->requested_pids); + gf_m2ts_demux_del(m2ts->ts); + gf_mx_del(m2ts->mx); + free(m2ts); + free(plug); +} + + +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_NET_CLIENT_INTERFACE: return 1; + default: return 0; + } +} + +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_NET_CLIENT_INTERFACE: return (GF_BaseInterface *) NewM2TSReader(); + default: return NULL; + } +} + +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_NET_CLIENT_INTERFACE: DeleteM2TSReader(ifce); break; + } +} diff --git a/modules/mpegts_in/mpegts_in.def b/modules/mpegts_in/mpegts_in.def new file mode 100644 index 0000000..0159b44 --- /dev/null +++ b/modules/mpegts_in/mpegts_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_mpegts_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/odf_dec/Makefile b/modules/odf_dec/Makefile new file mode 100644 index 0000000..f888c63 --- /dev/null +++ b/modules/odf_dec/Makefile @@ -0,0 +1,62 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/odf_dec + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= odf_dec.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_odf_dec.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols odf_dec.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/odf_dec/odf_dec.c b/modules/odf_dec/odf_dec.c new file mode 100644 index 0000000..6a7fe0e --- /dev/null +++ b/modules/odf_dec/odf_dec.c @@ -0,0 +1,311 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / OD decoder module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/internal/terminal_dev.h> +#include <gpac/constants.h> + +typedef struct +{ + GF_InlineScene *scene; + u32 PL; +} ODPriv; + +static GF_Err ODF_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *cap) +{ + cap->cap.valueInt = 0; + return GF_NOT_SUPPORTED; +} + +static GF_Err ODF_SetCapabilities(GF_BaseDecoder *plug, const GF_CodecCapability capability) +{ + return GF_OK; +} + + +static GF_Err ODF_AttachScene(GF_SceneDecoder *plug, GF_InlineScene *scene, Bool is_inline_scene) +{ + ODPriv *priv = (ODPriv *)plug->privateStack; + if (!priv->scene) priv->scene = scene; + return GF_OK; +} + +static void ODS_SetupOD(GF_InlineScene *is, GF_ObjectDescriptor *od) +{ + GF_ObjectManager *odm; + odm = gf_inline_find_odm(is, od->objectDescriptorID); + /*remove the old OD*/ + if (odm) gf_odm_disconnect(odm, 1); + odm = gf_odm_new(); + odm->OD = od; + odm->term = is->root_od->term; + odm->parentscene = is; + gf_list_add(is->ODlist, odm); + + /*locate service owner*/ + gf_odm_setup_object(odm, is->root_od->net_service); +} + +static GF_Err ODS_ODUpdate(ODPriv *priv, GF_ODUpdate *odU) +{ + GF_ObjectDescriptor *od; + u32 count; + + /*extract all our ODs and compare with what we already have...*/ + count = gf_list_count(odU->objectDescriptors); + if (count > 255) return GF_ODF_INVALID_DESCRIPTOR; + + while (count) { + od = (GF_ObjectDescriptor *)gf_list_get(odU->objectDescriptors, 0); + gf_list_rem(odU->objectDescriptors, 0); + count--; + ODS_SetupOD(priv->scene, od); + } + return GF_OK; +} + + + +static GF_Err ODS_RemoveOD(ODPriv *priv, GF_ODRemove *odR) +{ + u32 i; + for (i=0; i< odR->NbODs; i++) { + GF_ObjectManager *odm = gf_inline_find_odm(priv->scene, odR->OD_ID[i]); + if (odm) gf_odm_disconnect(odm, 1); + } + return GF_OK; +} + +static GF_Err ODS_UpdateESD(ODPriv *priv, GF_ESDUpdate *ESDs) +{ + GF_ESD *esd, *prev; + GF_ObjectManager *odm; + u32 count, i; + + odm = gf_inline_find_odm(priv->scene, ESDs->ODID); + /*spec: "ignore"*/ + if (!odm) return GF_OK; + + count = gf_list_count(ESDs->ESDescriptors); + + while (count) { + esd = (GF_ESD*)gf_list_get(ESDs->ESDescriptors, 0); + /*spec: "ES_Descriptors with ES_IDs that have already been received within the same name scope shall be ignored."*/ + prev = NULL; + i=0; + while ((prev = (GF_ESD*)gf_list_enum(odm->OD->ESDescriptors, &i))) { + if (prev->ESID == esd->ESID) break; + } + if (prev) { + gf_odf_desc_del((GF_Descriptor *)esd); + } else { + /*and register new stream*/ + gf_list_add(odm->OD->ESDescriptors, esd); + gf_odm_setup_es(odm, esd, odm->net_service, NULL); + } + + /*remove the desc from the AU*/ + gf_list_rem(ESDs->ESDescriptors, 0); + count--; + } + /*resetup object since a new ES has been inserted + (typically an empty object first sent, then a stream added - cf how ogg demuxer works)*/ + gf_inline_setup_object(priv->scene, odm); + return GF_OK; +} + + +static GF_Err ODS_RemoveESD(ODPriv *priv, GF_ESDRemove *ESDs) +{ + GF_ObjectManager *odm; + u32 i; + odm = gf_inline_find_odm(priv->scene, ESDs->ODID); + /*spec: "ignore"*/ + if (!odm) return GF_OK; + + for (i=0; i<ESDs->NbESDs; i++) { + /*blindly call remove*/ + gf_odm_remove_es(odm, ESDs->ES_ID[i]); + } + return GF_OK; +} + +static GF_Err ODF_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) +{ + return esd->decoderConfig->upstream ? GF_NOT_SUPPORTED : GF_OK; +} + + +static GF_Err ODF_DetachStream(GF_BaseDecoder *plug, u16 ES_ID) +{ + return GF_OK; +} + + +static GF_Err ODF_ProcessData(GF_SceneDecoder *plug, char *inBuffer, u32 inBufferLength, + u16 ES_ID, u32 AU_time, u32 mmlevel) +{ + GF_Err e; + GF_ODCom *com; + GF_ODCodec *oddec; + ODPriv *priv = (ODPriv *)plug->privateStack; + + oddec = gf_odf_codec_new(); + + e = gf_odf_codec_set_au(oddec, inBuffer, inBufferLength); + if (!e) e = gf_odf_codec_decode(oddec); + if (e) goto err_exit; + + //3- process all the commands in this AU, in order + while (1) { + com = gf_odf_codec_get_com(oddec); + if (!com) break; + + //ok, we have a command + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + e = ODS_ODUpdate(priv, (GF_ODUpdate *) com); + break; + case GF_ODF_OD_REMOVE_TAG: + e = ODS_RemoveOD(priv, (GF_ODRemove *) com); + break; + case GF_ODF_ESD_UPDATE_TAG: + e = ODS_UpdateESD(priv, (GF_ESDUpdate *)com); + break; + case GF_ODF_ESD_REMOVE_TAG: + e = ODS_RemoveESD(priv, (GF_ESDRemove *)com); + break; + case GF_ODF_IPMP_UPDATE_TAG: +#if 0 + { + GF_IPMPUpdate *ipmpU = (GF_IPMPUpdate *)com; + while (gf_list_count(ipmpU->IPMPDescList)) { + GF_IPMP_Descriptor *ipmp = gf_list_get(ipmpU->IPMPDescList, 0); + gf_list_rem(ipmpU->IPMPDescList, 0); + IS_UpdateIPMP(priv->scene, ipmp); + } + e = GF_OK; + } +#else + e = GF_OK; +#endif + break; + case GF_ODF_IPMP_REMOVE_TAG: + e = GF_NOT_SUPPORTED; + break; + /*should NEVER exist outside the file format*/ + case GF_ODF_ESD_REMOVE_REF_TAG: + e = GF_NON_COMPLIANT_BITSTREAM; + break; + default: + if (com->tag >= GF_ODF_COM_ISO_BEGIN_TAG && com->tag <= GF_ODF_COM_ISO_END_TAG) { + e = GF_ODF_FORBIDDEN_DESCRIPTOR; + } else { + /*we don't process user commands*/ + e = GF_OK; + } + break; + } + gf_odf_com_del(&com); + if (e) break; + } + +err_exit: + gf_odf_codec_del(oddec); + return e; +} + + +Bool ODF_CanHandleStream(GF_BaseDecoder *ifce, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + ODPriv *priv = (ODPriv *)ifce->privateStack; + if (StreamType==GF_STREAM_OD) { + priv->PL = PL; + return 1; + } + return 0; +} + + +void DeleteODDec(GF_BaseDecoder *plug) +{ + ODPriv *priv = (ODPriv *)plug->privateStack; + free(priv); + free(plug); +} + +GF_BaseDecoder *NewODDec() +{ + GF_SceneDecoder *tmp; + ODPriv *priv; + + GF_SAFEALLOC(tmp, GF_SceneDecoder); + if (!tmp) return NULL; + GF_SAFEALLOC(priv, ODPriv); + + tmp->privateStack = priv; + tmp->AttachStream = ODF_AttachStream; + tmp->DetachStream = ODF_DetachStream; + tmp->GetCapabilities = ODF_GetCapabilities; + tmp->SetCapabilities = ODF_SetCapabilities; + tmp->ProcessData = ODF_ProcessData; + tmp->AttachScene = ODF_AttachScene; + tmp->CanHandleStream = ODF_CanHandleStream; + + GF_REGISTER_MODULE_INTERFACE(tmp, GF_SCENE_DECODER_INTERFACE, "GPAC OD Decoder", "gpac distribution") + return (GF_BaseDecoder *) tmp; +} + + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + return 1; + default: + return 0; + } +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + return (GF_BaseInterface *)NewODDec(); + default: + return NULL; + } +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + DeleteODDec((GF_BaseDecoder *)ifce); + break; + } +} + diff --git a/modules/odf_dec/odf_dec.def b/modules/odf_dec/odf_dec.def new file mode 100644 index 0000000..9aa20e6 --- /dev/null +++ b/modules/odf_dec/odf_dec.def @@ -0,0 +1,6 @@ +LIBRARY gm_odf_dec.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/ogg/Makefile b/modules/ogg/Makefile new file mode 100644 index 0000000..8d0f645 --- /dev/null +++ b/modules/ogg/Makefile @@ -0,0 +1,100 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/ogg + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= ogg_in.o ogg_load.o + +SRCS := $(OBJS:.o=.c) + +NEED_LOCAL_LIB="no" +LOCAL_LIB=../../bin/gcc +LINKLIBS= -lgpac -logg + +ifeq ($(CONFIG_OGG), local) +NEED_LOCAL_LIB="yes" +endif + +ifeq ($(CONFIG_VORBIS), no) +else +OBJS+= vorbis_dec.o +LINKLIBS+= -lvorbis +CFLAGS+=-DGPAC_HAS_VORBIS +ifeq ($(CONFIG_VORBIS), local) +NEED_LOCAL_LIB="yes" +CFLAGS+= -I$(LOCAL_INC_PATH) +endif +endif + +ifeq ($(CONFIG_THEORA), no) +else +OBJS+= theora_dec.o +LINKLIBS+= -ltheora +CFLAGS+=-DGPAC_HAS_THEORA +ifeq ($(CONFIG_THEORA), local) +NEED_LOCAL_LIB="yes" +CFLAGS+= -I$(LOCAL_INC_PATH) +endif +endif + + +#add local lib path +ifeq ($(NEED_LOCAL_LIB), "yes") +LOCAL_LIB+=-L../../extra_lib/lib/gcc +endif + + +LIB=gm_ogg_xiph.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols ogg.def +endif + + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L$(LOCAL_LIB) $(LINKLIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/ogg/ogg.def b/modules/ogg/ogg.def new file mode 100644 index 0000000..9e6988b --- /dev/null +++ b/modules/ogg/ogg.def @@ -0,0 +1,6 @@ +LIBRARY gm_ogg_xiph.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/ogg/ogg_in.c b/modules/ogg/ogg_in.c new file mode 100644 index 0000000..2f9acad --- /dev/null +++ b/modules/ogg/ogg_in.c @@ -0,0 +1,972 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / XIPH.org module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "ogg_in.h" +#include <ogg/ogg.h> + +typedef struct +{ + u32 streamType; /*MPEG-4 streamType*/ + u32 num_init_headers; + u32 sample_rate, bitrate; + + u32 theora_kgs; + Float frame_rate; + u32 frame_rate_base; + + u32 type; +} OGGInfo; + +typedef struct +{ + ogg_stream_state os; + u32 serial_no; + /*DSI for ogg in mp4 - cf constants.h*/ + char *dsi; + u32 dsi_len; + + OGGInfo info; + Bool got_headers; + s64 seek_granule, last_granule; + + Bool is_running; + u32 parse_headers; + LPNETCHANNEL ch; + u16 ESID; + Bool eos_detected, map_time; + + u32 ogg_ts; + + GF_VorbisParser vp; +} OGGStream; + +typedef struct +{ + GF_ClientService *service; + GF_Thread *demuxer; + + GF_List *streams; + + FILE *ogfile; + u32 file_size; + Bool is_remote, is_inline; + u32 nb_playing, kill_demux, do_seek, service_type, init_remain, bos_done; + + /*ogg ogfile state*/ + ogg_sync_state oy; + + OGGStream *resync_stream; + + Bool has_video, has_audio, is_single_media; + + Double dur; + u32 data_buffer_ms; + + Bool needs_connection; + Double start_range, end_range; + /*file downloader*/ + GF_DownloadSession * dnload; + Bool is_live; + u32 tune_in_time; +} OGGReader; + + +void OGG_EndOfFile(OGGReader *read) +{ + OGGStream *st; + u32 i=0; + while ((st = gf_list_enum(read->streams, &i))) { + gf_term_on_sl_packet(read->service, st->ch, NULL, 0, NULL, GF_EOS); + } +} + +#define OGG_BUFFER_SIZE 4096 + +static Bool OGG_ReadPage(OGGReader *read, ogg_page *oggpage) +{ + char buf[OGG_BUFFER_SIZE]; + GF_Err e; + + /*remote file, check if we use cache*/ + if (read->is_remote) { + u32 total_size, status; + e = gf_dm_sess_get_stats(read->dnload, NULL, NULL, &total_size, NULL, NULL, &status); + /*not ready*/ + if ((e<GF_OK) || (status > GF_NETIO_DATA_EXCHANGE)) return 0; + if (status == GF_NETIO_DATA_EXCHANGE) { + if (!total_size && !read->is_live) { + read->is_live = 1; + read->tune_in_time = gf_sys_clock(); + } + else if (!read->is_live && !read->ogfile) { + const char *szCache = gf_dm_sess_get_cache_name(read->dnload); + if (!szCache) return 0; + read->ogfile = fopen((char *) szCache, "rb"); + if (!read->ogfile) return 0; + } + } + } + + while (ogg_sync_pageout(&read->oy, oggpage ) != 1 ) { + char *buffer; + u32 bytes; + + if (read->ogfile) { + if (feof(read->ogfile)) { + OGG_EndOfFile(read); + return 0; + } + bytes = fread(buf, 1, OGG_BUFFER_SIZE, read->ogfile); + } else { + e = gf_dm_sess_fetch_data(read->dnload, buf, OGG_BUFFER_SIZE, &bytes); + if (e) return 0; + } + if (!bytes) return 0; + buffer = ogg_sync_buffer(&read->oy, bytes); + memcpy(buffer, buf, bytes); + ogg_sync_wrote(&read->oy, bytes); + } + return 1; +} + +static OGGStream *OGG_FindStreamForPage(OGGReader *read, ogg_page *oggpage) +{ + u32 i, count; + count = gf_list_count(read->streams); + for (i=0; i<count; i++) { + OGGStream *st = gf_list_get(read->streams, i); + if (ogg_stream_pagein(&st->os, oggpage) == 0) return st; + } + return NULL; +} + + + +static GF_ObjectDescriptor *OGG_GetOD(OGGStream *st) +{ + GF_ObjectDescriptor *od; + GF_ESD *esd; + + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = (st->info.streamType==GF_STREAM_AUDIO) ? 3 : 2; + esd = gf_odf_desc_esd_new(0); + esd->decoderConfig->streamType = st->info.streamType; + esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_OGG; + esd->decoderConfig->avgBitrate = st->info.bitrate; + esd->ESID = st->ESID; + + esd->slConfig->useTimestampsFlag = 1; + esd->slConfig->useAccessUnitEndFlag = esd->slConfig->useAccessUnitStartFlag = 1; + esd->slConfig->timestampResolution = st->info.sample_rate ? st->info.sample_rate : (u32) (1000*st->info.frame_rate); + if (st->info.sample_rate) esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1; + else esd->slConfig->useRandomAccessPointFlag = 1; + + esd->decoderConfig->decoderSpecificInfo->dataLength = st->dsi_len; + esd->decoderConfig->decoderSpecificInfo->data = (char *) malloc(sizeof(char) * st->dsi_len); + memcpy(esd->decoderConfig->decoderSpecificInfo->data, st->dsi, sizeof(char) * st->dsi_len); + gf_list_add(od->ESDescriptors, esd); + return od; +} + +u64 OGG_GranuleToTime(OGGInfo *cfg, s64 granule) +{ + if (cfg->sample_rate) { + return granule; + } + if (cfg->frame_rate) { + s64 iframe = granule>>cfg->theora_kgs; + s64 pframe = granule - (iframe<<cfg->theora_kgs); + pframe += iframe; + pframe *= cfg->frame_rate_base; + return (u64) (pframe / cfg->frame_rate); + } + return 0; +} + +Double OGG_GranuleToMediaTime(OGGInfo *cfg, s64 granule) +{ + Double t = (Double) (s64) OGG_GranuleToTime(cfg, granule); + if (cfg->sample_rate) t /= cfg->sample_rate; + else t /= cfg->frame_rate_base; + return t; +} + + +static void OGG_GetStreamInfo(ogg_packet *oggpacket, OGGInfo *info) +{ + oggpack_buffer opb; + + memset(info, 0, sizeof(OGGInfo)); + + /*vorbis*/ + if ((oggpacket->bytes >= 7) && !strncmp(&oggpacket->packet[1], "vorbis", 6)) { + info->streamType = GF_STREAM_AUDIO; + oggpack_readinit(&opb, oggpacket->packet, oggpacket->bytes); + oggpack_adv( &opb, 88); + oggpack_adv( &opb, 8); /*nb chan*/ + info->sample_rate = oggpack_read(&opb, 32); + oggpack_adv( &opb, 32); /*max rate*/ + info->bitrate = oggpack_read(&opb, 32); + info->num_init_headers = 3; + info->type = OGG_VORBIS; + } + /*speex*/ + else if ((oggpacket->bytes >= 7) && !strncmp(&oggpacket->packet[0], "Speex", 5)) { + info->streamType = GF_STREAM_AUDIO; + oggpack_readinit(&opb, oggpacket->packet, oggpacket->bytes); + oggpack_adv(&opb, 224); + oggpack_adv(&opb, 32); + oggpack_adv( &opb, 32); + info->sample_rate = oggpack_read(&opb, 32); + info->type = OGG_SPEEX; + info->num_init_headers = 1; + } + /*flac*/ + else if ((oggpacket->bytes >= 4) && !strncmp(&oggpacket->packet[0], "fLaC", 4)) { + info->streamType = GF_STREAM_AUDIO; + info->type = 3; + info->num_init_headers = OGG_FLAC; + } + /*theora*/ + else if ((oggpacket->bytes >= 7) && !strncmp(&oggpacket->packet[1], "theora", 6)) { + GF_BitStream *bs; + u32 fps_numerator, fps_denominator, keyframe_freq_force; + + info->streamType = GF_STREAM_VISUAL; + info->type = OGG_THEORA; + bs = gf_bs_new(oggpacket->packet, oggpacket->bytes, GF_BITSTREAM_READ); + gf_bs_read_int(bs, 56); + gf_bs_read_int(bs, 8); /* major version num */ + gf_bs_read_int(bs, 8); /* minor version num */ + gf_bs_read_int(bs, 8); /* subminor version num */ + gf_bs_read_int(bs, 16) /*<< 4*/; /* width */ + gf_bs_read_int(bs, 16) /*<< 4*/; /* height */ + gf_bs_read_int(bs, 24); /* frame width */ + gf_bs_read_int(bs, 24); /* frame height */ + gf_bs_read_int(bs, 8); /* x offset */ + gf_bs_read_int(bs, 8); /* y offset */ + fps_numerator = gf_bs_read_u32(bs); + fps_denominator = gf_bs_read_u32(bs); + gf_bs_read_int(bs, 24); /* aspect_numerator */ + gf_bs_read_int(bs, 24); /* aspect_denominator */ + gf_bs_read_int(bs, 8); /* colorspace */ + gf_bs_read_int(bs, 24);/* bitrate */ + gf_bs_read_int(bs, 6); /* quality */ + + keyframe_freq_force = 1 << gf_bs_read_int(bs, 5); + info->theora_kgs = 0; + keyframe_freq_force--; + while (keyframe_freq_force) { + info->theora_kgs ++; + keyframe_freq_force >>= 1; + } + info->frame_rate = ((Float)fps_numerator) / fps_denominator; + info->num_init_headers = 3; + gf_bs_del(bs); + info->frame_rate_base = fps_denominator; + } +} + +static void OGG_ResetupStream(OGGReader *read, OGGStream *st, ogg_page *oggpage) +{ + ogg_stream_clear(&st->os); + ogg_stream_init(&st->os, st->serial_no); + ogg_stream_pagein(&st->os, oggpage); + st->parse_headers = st->info.num_init_headers; + + if (st->info.sample_rate) { + st->seek_granule = (s64) (read->start_range * st->info.sample_rate); + } else if (st->info.frame_rate) { + s64 seek = (s64) (read->start_range * st->info.frame_rate) - 1; + if (seek<0) seek=0; + st->seek_granule = (seek)<<st->info.theora_kgs; + } + st->last_granule = -1; +} + +static void OGG_NewStream(OGGReader *read, ogg_page *oggpage) +{ + ogg_packet oggpacket; + u32 serial_no, i; + OGGStream *st; + + /*reannounce of stream (caroussel in live streams) - until now I don't think icecast uses this*/ + serial_no = ogg_page_serialno(oggpage); + i=0; + while ((st = gf_list_enum(read->streams, &i))) { + if (st->serial_no==serial_no) { + OGG_ResetupStream(read, st, oggpage); + return; + } + } + + /*look if we have the same stream defined (eg, reuse first stream dead with same header page)*/ + i=0; + while ((st = gf_list_enum(read->streams, &i))) { + if (st->eos_detected) { + ogg_stream_state os; + ogg_stream_init(&os, serial_no); + ogg_stream_pagein(&os, oggpage); + ogg_stream_packetpeek(&os, &oggpacket); + if (st->dsi && !memcmp(st->dsi, oggpacket.packet, oggpacket.bytes)) { + ogg_stream_clear(&os); + st->serial_no = serial_no; + OGG_ResetupStream(read, st, oggpage); + return; + } + ogg_stream_clear(&os); + /*nope streams are different, signal eos on this one*/ + gf_term_on_sl_packet(read->service, st->ch, NULL, 0, NULL, GF_EOS); + } + } + + GF_SAFEALLOC(st, OGGStream); + st->serial_no = serial_no; + ogg_stream_init(&st->os, st->serial_no); + ogg_stream_pagein(&st->os, oggpage); + + ogg_stream_packetpeek(&st->os, &oggpacket); + OGG_GetStreamInfo(&oggpacket, &st->info); + + /*check we don't discard audio or visual streams*/ + if ( ((read->service_type==1) && (st->info.streamType==GF_STREAM_AUDIO)) + || ((read->service_type==2) && (st->info.streamType==GF_STREAM_VISUAL)) ) + { + ogg_stream_clear(&st->os); + free(st); + return; + } + + gf_list_add(read->streams, st); + st->ESID = 2 + gf_list_count(read->streams); + st->parse_headers = st->info.num_init_headers; + if (st->parse_headers) read->init_remain++; + + if (st->info.sample_rate) { + st->seek_granule = (s64) (read->start_range * st->info.sample_rate); + } else if (st->info.frame_rate) { + s64 seek = (s64) (read->start_range * st->info.frame_rate) - 1; + if (seek<0) seek=0; + st->seek_granule = (seek)<<st->info.theora_kgs; + } + st->last_granule = -1; + + if (st->info.streamType==GF_STREAM_VISUAL) { + read->has_video = 1; + } else { + read->has_audio = 1; + } + if (st->got_headers && read->is_inline) gf_term_add_media(read->service, (GF_Descriptor*) OGG_GetOD(st), 0); +} + +void OGG_SignalEndOfStream(OGGReader *read, OGGStream *st) +{ + if (st->eos_detected) { + gf_term_on_sl_packet(read->service, st->ch, NULL, 0, NULL, GF_EOS); + ogg_stream_clear(&st->os); + } +} + +GFINLINE void OGG_SendPackets(OGGReader *read, OGGStream *st, ogg_packet *oggpacket) +{ + GF_SLHeader slh; + memset(&slh, 0, sizeof(GF_SLHeader)); + if (st->info.type==OGG_VORBIS) { + slh.accessUnitEndFlag = slh.accessUnitStartFlag = 1; + slh.randomAccessPointFlag = 1; + slh.compositionTimeStampFlag = 1; + slh.compositionTimeStamp = st->ogg_ts; + gf_term_on_sl_packet(read->service, st->ch, oggpacket->packet, oggpacket->bytes, &slh, GF_OK); + st->ogg_ts += gf_vorbis_check_frame(&st->vp, oggpacket->packet, oggpacket->bytes); + } + else if (st->info.type==OGG_THEORA) { + oggpack_buffer opb; + oggpackB_readinit(&opb, oggpacket->packet, oggpacket->bytes); + /*new frame*/ + if (oggpackB_read(&opb, 1) == 0) { + slh.accessUnitStartFlag = slh.accessUnitEndFlag = 1; + /*add packet*/ + slh.randomAccessPointFlag = oggpackB_read(&opb, 1) ? 0 : 1; + slh.compositionTimeStampFlag = 1; + slh.compositionTimeStamp = st->ogg_ts; + gf_term_on_sl_packet(read->service, st->ch, oggpacket->packet, oggpacket->bytes, &slh, GF_OK); + st->ogg_ts += 1000; + } + } +} + +void OGG_Process(OGGReader *read) +{ + OGGStream *st; + ogg_packet oggpacket; + ogg_page oggpage; + + if (read->resync_stream) { + st = read->resync_stream; + read->resync_stream = NULL; + goto process_stream; + } + + if (!OGG_ReadPage(read, &oggpage)) { + return; + } + + if (ogg_page_bos(&oggpage)) { + OGG_NewStream(read, &oggpage); + return; + } + + st = OGG_FindStreamForPage(read, &oggpage); + if (!st) { + if (!read->bos_done && read->is_live) { + u32 now = gf_sys_clock(); + if (now-read->tune_in_time > 1000) { + gf_term_on_message(read->service, GF_OK, "Waiting for tune in..."); + read->tune_in_time = now; + } + } + return; + } + + if (ogg_page_eos(&oggpage)) + st->eos_detected = 1; + + if (st->parse_headers && !st->got_headers) { + while (ogg_stream_packetout(&st->os, &oggpacket ) > 0 ) { + GF_BitStream *bs; + if (st->info.type==OGG_VORBIS) + gf_vorbis_parse_header(&st->vp, oggpacket.packet, oggpacket.bytes); + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + if (st->dsi) { + gf_bs_write_data(bs, st->dsi, st->dsi_len); + free(st->dsi); + st->dsi = NULL; + st->dsi_len=0; + } + gf_bs_write_u16(bs, oggpacket.bytes); + gf_bs_write_data(bs, oggpacket.packet, oggpacket.bytes); + gf_bs_get_content(bs, (char **)&st->dsi, &st->dsi_len); + gf_bs_del(bs); + st->parse_headers--; + if (!st->parse_headers) { + st->got_headers = 1; + if (read->is_inline) + gf_term_add_media(read->service, (GF_Descriptor*) OGG_GetOD(st), 0); + break; + } + } + if (!st->got_headers) return; + assert(read->init_remain); + read->init_remain--; + if (!read->init_remain) read->bos_done = 1; + return; + } + /*from here we should have passed all headers*/ + if (read->init_remain) return; + +process_stream: + /*live insertion (not supported yet, just a reminder)*/ + if (!st->ch) { + read->resync_stream = st; + return; + } + + while (ogg_stream_packetout(&st->os, &oggpacket ) > 0 ) { + if (oggpacket.granulepos != -1) { + st->last_granule = oggpacket.granulepos; + } + /*stream reinit, don't resend headers*/ + if (st->parse_headers) st->parse_headers--; + else if (st->map_time) { + Double t; + if (read->start_range && (oggpacket.granulepos==-1)) continue; + t = OGG_GranuleToMediaTime(&st->info, st->last_granule); + if (t>=read->start_range) { + GF_NetworkCommand map; + map.command_type = GF_NET_CHAN_MAP_TIME; + map.map_time.on_channel = st->ch; + map.map_time.reset_buffers = (read->start_range>0.2) ? 1 : 0; + map.map_time.timestamp = st->ogg_ts = 0; + map.map_time.media_time = t; + gf_term_on_command(read->service, &map, GF_OK); + st->map_time = 0; + OGG_SendPackets(read, st, &oggpacket); + } + } + else { + OGG_SendPackets(read, st, &oggpacket); + } + } +} + +static u32 OggDemux(void *par) +{ + GF_NetworkCommand com; + Bool go; + u32 i, count; + OGGStream *st; + OGGReader *read = (OGGReader *) par; + + read->bos_done = 0; + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.command_type = GF_NET_CHAN_BUFFER_QUERY; + + if (read->needs_connection) { + read->needs_connection=0; + gf_term_on_connect(read->service, NULL, GF_OK); + } + + ogg_sync_init(&read->oy); + + + while (!read->kill_demux) { + OGG_Process(read); + + if (!read->bos_done) continue; + + /*wait for stream connection*/ + while (!read->kill_demux && !read->nb_playing) { + gf_sleep(20); + } + + /*(re)starting, seek*/ + if (read->do_seek) { + read->do_seek = 0; + ogg_sync_clear(&read->oy); + ogg_sync_init(&read->oy); +// OGG_SendStreams(read); + + if (read->ogfile) { + u32 seek_to = 0; + read->resync_stream = NULL; + if (read->dur) seek_to = (u32) (read->file_size * (read->start_range/read->dur) * 0.6f); + if ((s32) seek_to > ftell(read->ogfile) ) { + fseek(read->ogfile, seek_to, SEEK_SET); + } else { + fseek(read->ogfile, 0, SEEK_SET); + } + } + } + + + /*sleep untill the buffer occupancy is too low - note that this work because all streams in this + demuxer are synchronized*/ + go = read->nb_playing; + while (go && !read->kill_demux) { + count = gf_list_count(read->streams); + for (i=0; i<count; i++) { + st = gf_list_get(read->streams, i); + if (!st->ch) continue; + com.base.on_channel = st->ch; + gf_term_on_command(read->service, &com, GF_OK); + if (com.buffer.occupancy < read->data_buffer_ms) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[OGG] channel %d needs fill (%d ms data, %d max buffer)\n", st->ESID, com.buffer.occupancy, read->data_buffer_ms)); + go = 0; + break; + } + } + if (!i || !read->nb_playing) break; + gf_sleep(0); + } + } + ogg_sync_clear(&read->oy); + read->kill_demux=2; + return 0; +} + +/*get streams & duration*/ +Bool OGG_CheckFile(OGGReader *read) +{ + OGGInfo info, the_info; + ogg_page oggpage; + ogg_packet oggpacket; + ogg_stream_state os, the_os; + u64 max_gran; + Bool has_stream = 0; + fseek(read->ogfile, 0, SEEK_SET); + + ogg_sync_init(&read->oy); + memset(&the_info, 0, sizeof(OGGInfo)); + max_gran = 0; + while (1) { + if (!OGG_ReadPage(read, &oggpage)) break; + + if (ogg_page_bos(&oggpage)) { + ogg_stream_init(&os, ogg_page_serialno(&oggpage)); + if (ogg_stream_pagein(&os, &oggpage) >= 0 ) { + ogg_stream_packetpeek(&os, &oggpacket); + if (ogg_stream_pagein(&os, &oggpage) >= 0 ) { + ogg_stream_packetpeek(&os, &oggpacket); + OGG_GetStreamInfo(&oggpacket, &info); + } + if (!has_stream) { + has_stream = 1; + ogg_stream_init(&the_os, ogg_page_serialno(&oggpage)); + the_info = info; + } + } + ogg_stream_clear(&os); + continue; + } + if (has_stream && (ogg_stream_pagein(&the_os, &oggpage) >= 0) ) { + while (ogg_stream_packetout(&the_os, &oggpacket ) > 0 ) { + if ((oggpacket.granulepos>=0) && ((u64) oggpacket.granulepos>max_gran) ) { + max_gran = oggpacket.granulepos; + } + } + } + } + ogg_sync_clear(&read->oy); + read->file_size = ftell(read->ogfile); + fseek(read->ogfile, 0, SEEK_SET); + read->dur = 0; + if (has_stream) { + ogg_stream_clear(&the_os); + read->dur = (Double) (s64) OGG_GranuleToTime(&the_info, max_gran); + if (the_info.sample_rate) read->dur /= the_info.sample_rate; + else read->dur /= the_info.frame_rate_base; + } + return has_stream; +} + +static Bool OGG_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt; + sExt = strrchr(url, '.'); + if (!sExt) return 0; + + if (gf_term_check_extension(plug, "application/ogg", "ogg", "Xiph.org OGG Movie", sExt)) return 1; + if (gf_term_check_extension(plug, "application/x-ogg", "ogg", "Xiph.org OGG Movie", sExt)) return 1; + return 0; +} + +static Bool ogg_is_local(const char *url) +{ + if (!strnicmp(url, "file://", 7)) return 1; + if (strstr(url, "://")) return 0; + return 1; +} + +void OGG_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + OGGReader *read = (OGGReader *) cbk; + + gf_term_download_update_stats(read->dnload); + + /*done*/ + if ((param->msg_type==GF_NETIO_DATA_TRANSFERED) && read->ogfile) { + read->is_remote = 0; + /*reload file*/ + OGG_CheckFile(read); + return; + } + if (param->error && read->needs_connection) { + read->needs_connection = 0; + read->kill_demux = 2; + gf_term_on_connect(read->service, NULL, param->error); + } + /*we never receive data from here since the downloader is not threaded*/ +} + +void OGG_DownloadFile(GF_InputService *plug, char *url) +{ + OGGReader *read = (OGGReader*) plug->priv; + + read->dnload = gf_term_download_new(read->service, url, GF_NETIO_SESSION_NOT_THREADED, OGG_NetIO, read); + if (!read->dnload) { + read->kill_demux=2; + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } + /*service confirm is done once fetched, but start the demuxer thread*/ + gf_th_run(read->demuxer, OggDemux, read); +} + + +static GF_Err OGG_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + char szURL[2048]; + char *ext; + GF_Err reply; + OGGReader *read = plug->priv; + read->service = serv; + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + read->service_type = 0; + strcpy(szURL, url); + ext = strrchr(szURL, '#'); + if (ext) { + if (!strcmp(ext, "#video")) read->service_type = 1; + else if (!strcmp(ext, "#audio")) read->service_type = 2; + ext[0] = 0; + } + + /*remote fetch*/ + read->is_remote = !ogg_is_local(szURL); + if (read->is_remote) { + read->needs_connection = 1; + OGG_DownloadFile(plug, szURL); + return GF_OK; + } else { + read->ogfile = fopen(szURL, "rb"); + if (!read->ogfile) { + reply = GF_URL_ERROR; + } else { + reply = GF_OK; + /*init ogg file in local mode*/ + if (!OGG_CheckFile(read)) { + fclose(read->ogfile); + reply = GF_NON_COMPLIANT_BITSTREAM; + } else { + read->needs_connection = 1; + /*start the demuxer thread*/ + gf_th_run(read->demuxer, OggDemux, read); + return GF_OK; + } + } + } + /*error*/ + read->kill_demux=2; + gf_term_on_connect(serv, NULL, reply); + return GF_OK; +} + +static GF_Err OGG_CloseService(GF_InputService *plug) +{ + OGGReader *read = plug->priv; + if (!read->kill_demux) { + read->kill_demux = 1; + while (read->kill_demux!=2) gf_sleep(2); + } + + if (read->ogfile) fclose(read->ogfile); + read->ogfile = NULL; + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + gf_term_on_disconnect(read->service, NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *OGG_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + u32 i; + GF_ObjectDescriptor *od; + OGGStream *st; + OGGReader *read = plug->priv; + /*since we don't handle multitrack in ogg yes, we don't need to check sub_url, only use expected type*/ + + /*single object*/ + if ((expect_type==GF_MEDIA_OBJECT_AUDIO) || (expect_type==GF_MEDIA_OBJECT_VIDEO)) { + if ((expect_type==GF_MEDIA_OBJECT_AUDIO) && !read->has_audio) return NULL; + if ((expect_type==GF_MEDIA_OBJECT_VIDEO) && !read->has_video) return NULL; + i=0; + while ((st = gf_list_enum(read->streams, &i))) { + if ((expect_type==GF_MEDIA_OBJECT_AUDIO) && (st->info.streamType!=GF_STREAM_AUDIO)) continue; + if ((expect_type==GF_MEDIA_OBJECT_VIDEO) && (st->info.streamType!=GF_STREAM_VISUAL)) continue; + + od = OGG_GetOD(st); + read->is_single_media = 1; + return (GF_Descriptor *) od; + } + /*not supported yet - we need to know what's in the ogg stream for that*/ + } + read->is_inline = 1; + return NULL; +} + +static GF_Err OGG_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ES_ID, i; + GF_Err e; + OGGStream *st; + OGGReader *read = plug->priv; + + e = GF_STREAM_NOT_FOUND; + if (strstr(url, "ES_ID")) { + sscanf(url, "ES_ID=%d", &ES_ID); + } + /*URL setup*/ +// else if (!read->es_ch && OGG_CanHandleURL(plug, url)) ES_ID = 3; + + i=0; + while ((st = gf_list_enum(read->streams, &i))) { + if (st->ESID==ES_ID) { + st->ch = channel; + e = GF_OK; + break; + } + } + gf_term_on_connect(read->service, channel, e); + return e; +} + +static GF_Err OGG_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + GF_Err e; + OGGStream *st; + u32 i=0; + OGGReader *read = plug->priv; + + e = GF_STREAM_NOT_FOUND; + while ((st = gf_list_enum(read->streams, &i))) { + if (st->ch==channel) { + st->ch = NULL; + e = GF_OK; + break; + } + } + gf_term_on_disconnect(read->service, channel, e); + return GF_OK; +} + +static GF_Err OGG_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + OGGStream *st; + u32 i; + OGGReader *read = plug->priv; + + if (!com->base.on_channel) { + /*if live session we may cache*/ + if (read->is_live && (com->command_type==GF_NET_IS_CACHABLE)) return GF_OK; + return GF_NOT_SUPPORTED; + } + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: + /*no way to demux streams independently, and we keep OD as dynamic ogfile to handle + chained streams*/ + return GF_NOT_SUPPORTED; + case GF_NET_CHAN_INTERACTIVE: + //live: return GF_NOT_SUPPORTED; + return GF_OK; + case GF_NET_CHAN_BUFFER: + com->buffer.min = com->buffer.max = 0; + if (read->is_live) com->buffer.max = read->data_buffer_ms; + return GF_OK; + case GF_NET_CHAN_SET_PADDING: return GF_NOT_SUPPORTED; + + case GF_NET_CHAN_DURATION: + com->duration.duration = read->dur; + return GF_OK; + case GF_NET_CHAN_PLAY: + read->start_range = com->play.start_range; + read->end_range = com->play.end_range; + i=0; + while ((st = gf_list_enum(read->streams, &i))) { + if (st->ch == com->base.on_channel) { + st->is_running = 1; + st->map_time = read->dur ? 1 : 0; + if (!read->nb_playing) read->do_seek = 1; + read->nb_playing ++; + break; + } + } + /*recfg duration in case*/ + if (!read->is_remote && read->dur) { + GF_NetworkCommand rcfg; + rcfg.base.on_channel = NULL; + rcfg.base.command_type = GF_NET_CHAN_DURATION; + rcfg.duration.duration = read->dur; + gf_term_on_command(read->service, &rcfg, GF_OK); + } + return GF_OK; + case GF_NET_CHAN_STOP: + i=0; + while ((st = gf_list_enum(read->streams, &i))) { + if (st->ch == com->base.on_channel) { + st->is_running = 0; + read->nb_playing --; + break; + } + } + return GF_OK; + default: + return GF_OK; + } +} + +static Bool OGG_CanHandleURLInService(GF_InputService *plug, const char *url) +{ + char szURL[2048], *sep; + OGGReader *read = (OGGReader *)plug->priv; + const char *this_url = gf_term_get_service_url(read->service); + if (!this_url || !url) return 0; + + strcpy(szURL, this_url); + sep = strrchr(szURL, '#'); + if (sep) sep[0] = 0; + + if ((url[0] != '#') && strnicmp(szURL, url, sizeof(char)*strlen(szURL))) return 0; + sep = strrchr(url, '#'); + if (!stricmp(sep, "#video") && (read->has_video)) return 1; + if (!stricmp(sep, "#audio") && (read->has_audio)) return 1; + return 0; +} + +GF_InputService *OGG_LoadDemux() +{ + OGGReader *reader; + GF_InputService *plug = malloc(sizeof(GF_InputService)); + memset(plug, 0, sizeof(GF_InputService)); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC OGG Reader", "gpac distribution") + + plug->CanHandleURL = OGG_CanHandleURL; + plug->ConnectService = OGG_ConnectService; + plug->CloseService = OGG_CloseService; + plug->GetServiceDescriptor = OGG_GetServiceDesc; + plug->ConnectChannel = OGG_ConnectChannel; + plug->DisconnectChannel = OGG_DisconnectChannel; + plug->ServiceCommand = OGG_ServiceCommand; + plug->CanHandleURLInService = OGG_CanHandleURLInService; + + reader = malloc(sizeof(OGGReader)); + memset(reader, 0, sizeof(OGGReader)); + reader->streams = gf_list_new(); + reader->demuxer = gf_th_new("OGGDemux"); + reader->data_buffer_ms = 1000; + + plug->priv = reader; + return plug; +} + +void OGG_DeleteDemux(void *ifce) +{ + GF_InputService *plug = (GF_InputService *) ifce; + OGGReader *read = plug->priv; + gf_th_del(read->demuxer); + + /*just in case something went wrong*/ + while (gf_list_count(read->streams)) { + OGGStream *st = gf_list_get(read->streams, 0); + gf_list_rem(read->streams, 0); + ogg_stream_clear(&st->os); + if (st->dsi) free(st->dsi); + free(st); + } + gf_list_del(read->streams); + free(read); + free(plug); +} diff --git a/modules/ogg/ogg_in.h b/modules/ogg/ogg_in.h new file mode 100644 index 0000000..2b12b08 --- /dev/null +++ b/modules/ogg/ogg_in.h @@ -0,0 +1,63 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / XIPH.org module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef OGG_IN_H +#define OGG_IN_H + +#include <gpac/modules/service.h> +#include <gpac/modules/codec.h> +#include <gpac/avparse.h> +#include <gpac/constants.h> +#include <gpac/thread.h> + +GF_InputService *OGG_LoadDemux(); +void OGG_DeleteDemux(void *ifce); + + +enum +{ + OGG_VORBIS = 1, + OGG_SPEEX, + OGG_FLAC, + OGG_THEORA +}; + +typedef struct +{ + u32 type; + void *opaque; +} OGGWraper; + +#ifdef GPAC_HAS_VORBIS +u32 NewVorbisDecoder(GF_BaseDecoder *ifcd); +void DeleteVorbisDecoder(GF_BaseDecoder *ifcg); +#endif + +#ifdef GPAC_HAS_THEORA +u32 NewTheoraDecoder(GF_BaseDecoder *ifcd); +void DeleteTheoraDecoder(GF_BaseDecoder *ifcg); +#endif + +#endif + diff --git a/modules/ogg/ogg_load.c b/modules/ogg/ogg_load.c new file mode 100644 index 0000000..4c507de --- /dev/null +++ b/modules/ogg/ogg_load.c @@ -0,0 +1,118 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / XIPH.org module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "ogg_in.h" + + +static Bool OGG_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + /*video decs*/ + if (StreamType == GF_STREAM_VISUAL) { + switch (ObjectType) { +#ifdef GPAC_HAS_THEORA + case GPAC_OTI_MEDIA_OGG: + if (decSpecInfo && (decSpecInfoSize>=9) && !strncmp((char *) &decSpecInfo[3], "theora", 6)) { + return NewTheoraDecoder(dec); + } + return 0; + case 0: return 1;/*query for types*/ +#endif + default: return 0; + } + } + /*audio decs*/ + if (StreamType == GF_STREAM_AUDIO) { + switch (ObjectType) { +#ifdef GPAC_HAS_VORBIS + case GPAC_OTI_MEDIA_OGG: + if (decSpecInfo && (decSpecInfoSize>=9) && !strncmp((char *) &decSpecInfo[3], "vorbis", 6)) { + return NewVorbisDecoder(dec); + } + return 0; + case 0: return 1; +#endif + default: return 0; + } + } + return 0; +} + + +GF_BaseDecoder *OGG_LoadDecoder() +{ + GF_MediaDecoder *ifce; + OGGWraper *wrap; + GF_SAFEALLOC(ifce, GF_MediaDecoder); + GF_SAFEALLOC(wrap, OGGWraper); + ifce->privateStack = wrap; + ifce->CanHandleStream = OGG_CanHandleStream; + GF_REGISTER_MODULE_INTERFACE(ifce, GF_MEDIA_DECODER_INTERFACE, "GPAC XIPH.org package", "gpac distribution") + + /*other interfaces will be setup at run time*/ + return (GF_BaseDecoder *)ifce; +} + +void DeleteOGGDecoder(GF_BaseDecoder *ifcd) +{ + OGGWraper *wrap = (OGGWraper *)ifcd->privateStack; + switch (wrap->type) { +#ifdef GPAC_HAS_VORBIS + case OGG_VORBIS: DeleteVorbisDecoder(ifcd); break; +#endif +#ifdef GPAC_HAS_THEORA + case OGG_THEORA: DeleteTheoraDecoder(ifcd); break; +#endif + default: + break; + } + free(wrap); + free(ifcd); +} + + +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return 1; + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return 1; + return 0; +} + +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return (GF_BaseInterface *)OGG_LoadDemux(); + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return (GF_BaseInterface *)OGG_LoadDecoder(); + return NULL; +} + +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_NET_CLIENT_INTERFACE: + OGG_DeleteDemux(ifce); + break; + case GF_MEDIA_DECODER_INTERFACE: + DeleteOGGDecoder((GF_BaseDecoder *) ifce); + break; + } +} diff --git a/modules/ogg/theora_dec.c b/modules/ogg/theora_dec.c new file mode 100644 index 0000000..721057d --- /dev/null +++ b/modules/ogg/theora_dec.c @@ -0,0 +1,234 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / XIPH.org module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "ogg_in.h" + +#ifdef GPAC_HAS_THEORA + +#include <theora/theora.h> + +typedef struct +{ + theora_info ti; + theora_state td; + theora_comment tc; + ogg_packet op; + + u16 ES_ID; + Bool has_reconfigured; +} TheoraDec; + +#define THEORACTX() TheoraDec *ctx = (TheoraDec *) ((OGGWraper *)ifcg->privateStack)->opaque + +static GF_Err THEO_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + ogg_packet oggpacket; + GF_BitStream *bs; + + THEORACTX(); + if (ctx->ES_ID) return GF_BAD_PARAM; + + if (!esd->decoderConfig->decoderSpecificInfo) return GF_NON_COMPLIANT_BITSTREAM; + + if (esd->decoderConfig->objectTypeIndication != GPAC_OTI_MEDIA_OGG) return GF_NON_COMPLIANT_BITSTREAM; + if ( (esd->decoderConfig->decoderSpecificInfo->dataLength<9) || strncmp(&esd->decoderConfig->decoderSpecificInfo->data[3], "theora", 6)) return GF_NON_COMPLIANT_BITSTREAM; + + oggpacket.granulepos = -1; + oggpacket.b_o_s = 1; + oggpacket.e_o_s = 0; + oggpacket.packetno = 0; + + ctx->ES_ID = esd->ESID; + + theora_info_init(&ctx->ti); + theora_comment_init(&ctx->tc); + + + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + while (gf_bs_available(bs)) { + oggpacket.bytes = gf_bs_read_u16(bs); + oggpacket.packet = malloc(sizeof(char) * oggpacket.bytes); + gf_bs_read_data(bs, oggpacket.packet, oggpacket.bytes); + if (theora_decode_header(&ctx->ti, &ctx->tc, &oggpacket) < 0 ) { + free(oggpacket.packet); + gf_bs_del(bs); + return GF_NON_COMPLIANT_BITSTREAM; + } + free(oggpacket.packet); + } + theora_decode_init(&ctx->td, &ctx->ti); + gf_bs_del(bs); + return GF_OK; +} + +static GF_Err THEO_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + THEORACTX(); + if (ctx->ES_ID != ES_ID) return GF_BAD_PARAM; + + theora_clear(&ctx->td); + theora_info_clear(&ctx->ti); + theora_comment_clear(&ctx->tc); + + ctx->ES_ID = 0; + return GF_OK; +} +static GF_Err THEO_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + THEORACTX(); + switch (capability->CapCode) { + case GF_CODEC_WIDTH: + capability->cap.valueInt = ctx->ti.width; + break; + case GF_CODEC_HEIGHT: + capability->cap.valueInt = ctx->ti.height; + break; + case GF_CODEC_STRIDE: + capability->cap.valueInt = ctx->ti.width; + break; + case GF_CODEC_FPS: + capability->cap.valueFloat = (Float) ctx->ti.fps_numerator; + capability->cap.valueFloat /= ctx->ti.fps_denominator; + break; + case GF_CODEC_PIXEL_FORMAT: + capability->cap.valueInt = GF_PIXEL_YV12; + break; + case GF_CODEC_REORDER: + capability->cap.valueInt = 0; + break; + case GF_CODEC_RESILIENT: + capability->cap.valueInt = 1; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = 3*ctx->ti.width * ctx->ti.height / 2; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = 1; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = 4; + break; + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 0; + break; + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} + +static GF_Err THEO_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like SR changing ...) */ + return GF_NOT_SUPPORTED; +} + + +static GF_Err THEO_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + ogg_packet op; + yuv_buffer yuv; + u32 i; + char *pYO, *pUO, *pVO; + unsigned char *pYD, *pUD, *pVD; + + THEORACTX(); + /*not using scalabilty*/ + assert(ctx->ES_ID == ES_ID); + + op.granulepos = -1; + op.b_o_s = 0; + op.e_o_s = 0; + op.packetno = 0; + op.packet = inBuffer; + op.bytes = inBufferLength; + + + *outBufferLength = 0; + + if (theora_decode_packetin(&ctx->td, &op)<0) return GF_NON_COMPLIANT_BITSTREAM; + if (mmlevel == GF_CODEC_LEVEL_SEEK) return GF_OK; + if (theora_decode_YUVout(&ctx->td, &yuv)<0) return GF_OK; + + pYO = yuv.y; + pUO = yuv.u; + pVO = yuv.v; + pYD = outBuffer; + pUD = outBuffer + ctx->ti.width * ctx->ti.height; + pVD = outBuffer + 5 * ctx->ti.width * ctx->ti.height / 4; + + for (i=0; i<(u32)yuv.y_height; i++) { + memcpy(pYD, pYO, sizeof(char) * yuv.y_width); + pYD += ctx->ti.width; + pYO += yuv.y_stride; + if (i%2) continue; + + memcpy(pUD, pUO, sizeof(char) * yuv.uv_width); + memcpy(pVD, pVO, sizeof(char) * yuv.uv_width); + pUD += ctx->ti.width/2; + pVD += ctx->ti.width/2; + pUO += yuv.uv_stride; + pVO += yuv.uv_stride; + } + *outBufferLength = 3*ctx->ti.width*ctx->ti.height/2; + return GF_OK; +} + + + +static const char *THEO_GetCodecName(GF_BaseDecoder *dec) +{ + return "Theora Decoder"; +} + +u32 NewTheoraDecoder(GF_BaseDecoder *ifcd) +{ + TheoraDec *dec; + GF_SAFEALLOC(dec, TheoraDec); + ((OGGWraper *)ifcd->privateStack)->opaque = dec; + ((OGGWraper *)ifcd->privateStack)->type = OGG_THEORA; + /*setup our own interface*/ + ifcd->AttachStream = THEO_AttachStream; + ifcd->DetachStream = THEO_DetachStream; + ifcd->GetCapabilities = THEO_GetCapabilities; + ifcd->SetCapabilities = THEO_SetCapabilities; + ((GF_MediaDecoder*)ifcd)->ProcessData = THEO_ProcessData; + ifcd->GetName = THEO_GetCodecName; + return 1; +} + +void DeleteTheoraDecoder(GF_BaseDecoder *ifcg) +{ + THEORACTX(); + free(ctx); +} + +#endif + diff --git a/modules/ogg/vorbis_dec.c b/modules/ogg/vorbis_dec.c new file mode 100644 index 0000000..70662ae --- /dev/null +++ b/modules/ogg/vorbis_dec.c @@ -0,0 +1,269 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / XIPH.org module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "ogg_in.h" + +#ifdef GPAC_HAS_VORBIS + +#include <vorbis/codec.h> + +typedef struct +{ + vorbis_info vi; + vorbis_dsp_state vd; + vorbis_block vb; + vorbis_comment vc; + ogg_packet op; + + u16 ES_ID; + Bool has_reconfigured; +} VorbDec; + +#define VORBISCTX() VorbDec *ctx = (VorbDec *) ((OGGWraper *)ifcg->privateStack)->opaque + +static GF_Err VORB_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + ogg_packet oggpacket; + GF_BitStream *bs; + + VORBISCTX(); + if (ctx->ES_ID) return GF_BAD_PARAM; + + if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->data) return GF_NON_COMPLIANT_BITSTREAM; + if (esd->decoderConfig->objectTypeIndication != GPAC_OTI_MEDIA_OGG) return GF_NON_COMPLIANT_BITSTREAM; + if ((esd->decoderConfig->decoderSpecificInfo->dataLength<9) || strncmp(&esd->decoderConfig->decoderSpecificInfo->data[3], "vorbis", 6)) return GF_NON_COMPLIANT_BITSTREAM; + + ctx->ES_ID = esd->ESID; + + vorbis_info_init(&ctx->vi); + vorbis_comment_init(&ctx->vc); + + oggpacket.granulepos = -1; + oggpacket.b_o_s = 1; + oggpacket.e_o_s = 0; + oggpacket.packetno = 0; + + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + while (gf_bs_available(bs)) { + oggpacket.bytes = gf_bs_read_u16(bs); + oggpacket.packet = malloc(sizeof(char) * oggpacket.bytes); + gf_bs_read_data(bs, oggpacket.packet, oggpacket.bytes); + if (vorbis_synthesis_headerin(&ctx->vi, &ctx->vc, &oggpacket) < 0 ) { + free(oggpacket.packet); + gf_bs_del(bs); + return GF_NON_COMPLIANT_BITSTREAM; + } + free(oggpacket.packet); + } + vorbis_synthesis_init(&ctx->vd, &ctx->vi); + vorbis_block_init(&ctx->vd, &ctx->vb); + gf_bs_del(bs); + + return GF_OK; +} + +static GF_Err VORB_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + VORBISCTX(); + if (ctx->ES_ID != ES_ID) return GF_BAD_PARAM; + + vorbis_block_clear(&ctx->vb); + vorbis_dsp_clear(&ctx->vd); + vorbis_info_clear(&ctx->vi); + vorbis_comment_clear(&ctx->vc); + + ctx->ES_ID = 0; + return GF_OK; +} +static GF_Err VORB_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + VORBISCTX(); + switch (capability->CapCode) { + case GF_CODEC_RESILIENT: + capability->cap.valueInt = 1; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = vorbis_info_blocksize(&ctx->vi, 1) * 2 * ctx->vi.channels; + break; + case GF_CODEC_SAMPLERATE: + capability->cap.valueInt = ctx->vi.rate; + break; + case GF_CODEC_NB_CHAN: + capability->cap.valueInt = ctx->vi.channels; + break; + case GF_CODEC_BITS_PER_SAMPLE: + capability->cap.valueInt = 16; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = 4; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = ctx->vi.rate / 4 / vorbis_info_blocksize(&ctx->vi, 0); + /*blocks are not of fixed size, so indicate a default CM size*/ + //capability->cap.valueInt = 12 * vorbis_info_blocksize(&ctx->vi, 1) / vorbis_info_blocksize(&ctx->vi, 0); + break; + case GF_CODEC_CU_DURATION: + /*this CANNOT work with vorbis, blocks are not of fixed size*/ + capability->cap.valueInt = 0; + break; + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 0; + break; + case GF_CODEC_CHANNEL_CONFIG: + switch (ctx->vi.channels) { + case 1: capability->cap.valueInt = GF_AUDIO_CH_FRONT_CENTER; break; + case 2: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT; + break; + case 3: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_FRONT_CENTER; + break; + case 4: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_BACK_LEFT | GF_AUDIO_CH_BACK_RIGHT; + break; + case 5: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_FRONT_CENTER | GF_AUDIO_CH_BACK_LEFT | GF_AUDIO_CH_BACK_RIGHT; + break; + case 6: + capability->cap.valueInt = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT | GF_AUDIO_CH_FRONT_CENTER | GF_AUDIO_CH_BACK_LEFT | GF_AUDIO_CH_BACK_RIGHT | GF_AUDIO_CH_LFE; + break; + } + break; + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} + +static GF_Err VORB_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like SR changing ...) */ + return GF_NOT_SUPPORTED; +} + + +static GFINLINE void vorbis_to_intern(u32 samples, Float **pcm, char *buf, u32 channels) +{ + u32 i, j; + s32 val; + ogg_int16_t *ptr, *data = (ogg_int16_t*)buf ; + Float *mono; + + for (i=0 ; i<channels ; i++) { + ptr = &data[i]; + if (channels>2) { + /*center is third in gpac*/ + if (i==1) ptr = &data[2]; + /*right is 2nd in gpac*/ + else if (i==2) ptr = &data[1]; + /*LFE is 4th in gpac*/ + if ((channels==6) && (i>3)) { + if (i==6) ptr = &data[4]; /*LFE*/ + else ptr = &data[i+1]; /*back l/r*/ + } + } + + mono = pcm[i]; + for (j=0; j<samples; j++) { + val = (s32) (mono[j] * 32767.f); + if (val > 32767) val = 32767; + if (val < -32768) val = -32768; + *ptr = val; + ptr += channels; + } + } +} + +static GF_Err VORB_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + ogg_packet op; + Float **pcm; + u32 samples, total_samples, total_bytes; + + VORBISCTX(); + /*not using scalabilty*/ + assert(ctx->ES_ID == ES_ID); + + op.granulepos = -1; + op.b_o_s = 0; + op.e_o_s = 0; + op.packetno = 0; + op.packet = inBuffer; + op.bytes = inBufferLength; + + + *outBufferLength = 0; + + if (vorbis_synthesis(&ctx->vb, &op) == 0) + vorbis_synthesis_blockin(&ctx->vd, &ctx->vb) ; + + /*trust vorbis max block info*/ + total_samples = 0; + total_bytes = 0; + while ((samples = vorbis_synthesis_pcmout(&ctx->vd, &pcm)) > 0) { + vorbis_to_intern(samples, pcm, (char*) outBuffer + total_bytes, ctx->vi.channels); + total_bytes += samples * 2 * ctx->vi.channels; + total_samples += samples; + vorbis_synthesis_read(&ctx->vd, samples); + } + *outBufferLength = total_bytes; + return GF_OK; +} + + +static const char *VORB_GetCodecName(GF_BaseDecoder *dec) +{ + return "Vorbis Decoder"; +} + +u32 NewVorbisDecoder(GF_BaseDecoder *ifcd) +{ + VorbDec *dec; + GF_SAFEALLOC(dec, VorbDec); + ((OGGWraper *)ifcd->privateStack)->opaque = dec; + ((OGGWraper *)ifcd->privateStack)->type = OGG_VORBIS; + /*setup our own interface*/ + ifcd->AttachStream = VORB_AttachStream; + ifcd->DetachStream = VORB_DetachStream; + ifcd->GetCapabilities = VORB_GetCapabilities; + ifcd->SetCapabilities = VORB_SetCapabilities; + ((GF_MediaDecoder*)ifcd)->ProcessData = VORB_ProcessData; + ifcd->GetName = VORB_GetCodecName; + return 1; +} + +void DeleteVorbisDecoder(GF_BaseDecoder *ifcg) +{ + VORBISCTX(); + free(ctx); +} + +#endif + diff --git a/modules/oss_audio/Makefile b/modules/oss_audio/Makefile new file mode 100644 index 0000000..643dc5b --- /dev/null +++ b/modules/oss_audio/Makefile @@ -0,0 +1,69 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/oss_audio + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include $(OSS_CFLAGS) +LDFLAGS+=$(OSS_LDFLAGS) + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +ifeq ($(OSS_INC_TYPE), SYS) +else +CFLAGS+=-DOSS_FIX_INC +endif + +ifeq ($(TARGET_ARCH_ARMV4L), yes) +CFLAGS+=-DFORCE_SR_LIMIT +endif + +#common obj +OBJS= oss.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_oss_audio.$(DYN_LIB_SUFFIX) + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac $(OSS_LDFLAGS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif + diff --git a/modules/oss_audio/oss.c b/modules/oss_audio/oss.c new file mode 100644 index 0000000..079d113 --- /dev/null +++ b/modules/oss_audio/oss.c @@ -0,0 +1,273 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / linux_oss audio render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#if defined(__DARWIN__) || defined(__APPLE__) +#include <soundcard.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/stat.h> +#include <string.h> + +#else + +#include <sys/ioctl.h> +#include <unistd.h> +#include <fcntl.h> + +#ifdef OSS_FIX_INC +#include <soundcard.h> +#else +#include <sys/soundcard.h> +#endif + +#endif + +#include <gpac/modules/audio_out.h> + +#define OSS_AUDIO_DEVICE "/dev/dsp" + +typedef struct +{ + int audio_dev, sr, nb_ch; + u32 buf_size, delay, num_buffers, total_duration; + u32 force_sr; + char *wav_buf; +} OSSContext; + + +#define OSSCTX() OSSContext *ctx = (OSSContext *)dr->opaque; + + +static GF_Err OSS_Setup(GF_AudioOutput*dr, void *os_handle, u32 num_buffers, u32 total_duration) +{ + int audio; + const char *opt; + OSSCTX(); + + opt = gf_modules_get_option((GF_BaseInterface *)dr, "OSS", "ForceSampleRate"); + if (opt) ctx->force_sr = atoi(opt); + + /*open OSS in non-blocking mode*/ + audio = open(OSS_AUDIO_DEVICE, 0); + if (audio < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[OSS] Cannot open audio device\n")); + return GF_NOT_SUPPORTED; + } + + /*set blocking mode back*/ + //fcntl(audio, F_SETFL, fcntl(audio, F_GETFL) & ~FNDELAY); + ctx->audio_dev=audio; + ctx->num_buffers = num_buffers; + ctx->total_duration = total_duration; + return GF_OK; +} + +static void OSS_Shutdown(GF_AudioOutput*dr) +{ + OSSCTX(); + ioctl(ctx->audio_dev,SNDCTL_DSP_RESET); + close(ctx->audio_dev); + if (ctx->wav_buf) free(ctx->wav_buf); + ctx->wav_buf = NULL; +} + + +static GF_Err OSS_ConfigureOutput(GF_AudioOutput*dr, u32 *SampleRate, u32 *NbChannels, u32 *nbBitsPerSample, u32 channel_cfg) +{ + int format, blockalign, nb_bufs, frag_spec; + long flags; + OSSCTX(); + + if (!ctx) return GF_BAD_PARAM; + /* reset and reopen audio-device */ + ioctl(ctx->audio_dev,SNDCTL_DSP_RESET); + close(ctx->audio_dev); + if (ctx->wav_buf) free(ctx->wav_buf); + ctx->wav_buf = NULL; + ctx->audio_dev=open(OSS_AUDIO_DEVICE,O_WRONLY|O_NONBLOCK); + if (!ctx->audio_dev) return GF_IO_ERR; + + /* Make the file descriptor use blocking writes with fcntl() so that + we don't have to handle sleep() ourselves*/ + flags = fcntl(ctx->audio_dev, F_GETFL); + flags &= ~O_NONBLOCK; + if (fcntl(ctx->audio_dev, F_SETFL, flags) < 0 ) return GF_IO_ERR; + + ctx->nb_ch = (int) (*NbChannels); + if (ioctl(ctx->audio_dev, SNDCTL_DSP_CHANNELS, &ctx->nb_ch)==-1) return GF_IO_ERR; + + blockalign = ctx->nb_ch; + if ((*nbBitsPerSample) == 16) { + blockalign *= 2; + format = AFMT_S16_LE; + } else { + format = AFMT_S8; + } + if(ioctl(ctx->audio_dev, SNDCTL_DSP_SETFMT,&format)==-1) return GF_IO_ERR; + ctx->sr = (*SampleRate); + if(ioctl(ctx->audio_dev, SNDCTL_DSP_SPEED,&ctx->sr)==-1) return GF_IO_ERR; + + nb_bufs = ctx->num_buffers ? ctx->num_buffers : 8; + ctx->buf_size = (*SampleRate * blockalign * ctx->total_duration) / (1000 * nb_bufs); + frag_spec = 4; + while (ctx->buf_size > (1<<(frag_spec+1))) + frag_spec++; + + ctx->buf_size = 1<<frag_spec; + + ctx->delay = (1000*ctx->buf_size) / (*SampleRate * blockalign); + frag_spec = ((nb_bufs<<16) & 0xFFFF0000) | frag_spec; + + ctx->delay = (1000*ctx->buf_size*nb_bufs) / (*SampleRate * blockalign); + if ( ioctl(ctx->audio_dev, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) return GF_IO_ERR; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[OSS] setup %d buffers %d bytes each (%d ms buffer delay)", nb_bufs, ctx->buf_size, ctx->delay)); + ctx->wav_buf = realloc(ctx->wav_buf, ctx->buf_size*sizeof(char)); + if(!ctx->wav_buf) return GF_OUT_OF_MEM; + memset(ctx->wav_buf, 0, ctx->buf_size*sizeof(char)); + return GF_OK; +} + +static void OSS_WriteAudio(GF_AudioOutput*dr) +{ + u32 written; + OSSCTX(); + written = dr->FillBuffer(dr->audio_renderer, ctx->wav_buf, ctx->buf_size); + /*this will also perform sleep*/ + if (written) write(ctx->audio_dev, ctx->wav_buf, written); +} + +static void OSS_SetVolume(GF_AudioOutput*dr, u32 Volume) {} +static void OSS_SetPan(GF_AudioOutput*dr, u32 Pan) {} +static void OSS_SetPriority(GF_AudioOutput*dr, u32 Priority) {} +static u32 OSS_GetAudioDelay(GF_AudioOutput*dr) +{ + OSSCTX() + return ctx->delay; +} + +/* + * to get the best matching samplerate the oss-device can be set up + * with the desired sr. if not supported the returned value contains the + * best matching sr. + * + * todo: supported samplerate could depend on nb_channels and format + */ +static GF_Err OSS_QueryOutputSampleRate(GF_AudioOutput*dr, u32 *desired_sr, u32 *NbChannels, u32 *nbBitsPerSample) +{ +#ifdef FORCE_SR_LIMIT + *NbChannels = 2; + if (!( *desired_sr % 11025)) return GF_OK; + if (*desired_sr<22050) *desired_sr = 22050; + else *desired_sr = 44100; + return GF_OK; +#else + /* reset and reopen audio-device */ + int i; + OSSCTX(); + if (ctx->force_sr) { + *desired_sr = ctx->force_sr; + return GF_OK; + } + i=*desired_sr; + if(ioctl(ctx->audio_dev, SNDCTL_DSP_SPEED,&i)==-1) return GF_IO_ERR; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[OSS] uses samplerate %d for desired sr %d\n", i, *desired_sr)); + *desired_sr = i; + i = *NbChannels; + if(ioctl(ctx->audio_dev,SNDCTL_DSP_CHANNELS, &i)==-1) return GF_IO_ERR; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[OSS] uses %d channels for %d desired ones\n", i, *NbChannels)); + *NbChannels = i; + if(ioctl(ctx->audio_dev, SNDCTL_DSP_SPEED,&ctx->sr)==-1) return GF_OK; + if(ioctl(ctx->audio_dev,SNDCTL_DSP_CHANNELS, &ctx->nb_ch)==-1) return GF_OK; + return GF_OK; +#endif +} + +void *NewOSSRender() +{ + OSSContext *ctx; + GF_AudioOutput*driv; + ctx = malloc(sizeof(OSSContext)); + if(!ctx) + return NULL; + memset(ctx, 0, sizeof(OSSContext)); + driv = malloc(sizeof(GF_AudioOutput)); + if(!driv) + { + free(ctx); + ctx=NULL; + return NULL; + } + memset(driv, 0, sizeof(GF_AudioOutput)); + driv->opaque = ctx; + driv->SelfThreaded = 0; + driv->Setup = OSS_Setup; + driv->Shutdown = OSS_Shutdown; + driv->ConfigureOutput = OSS_ConfigureOutput; + driv->GetAudioDelay = OSS_GetAudioDelay; + driv->SetVolume = OSS_SetVolume; + driv->SetPan = OSS_SetPan; + driv->SetPriority = OSS_SetPriority; + driv->QueryOutputSampleRate = OSS_QueryOutputSampleRate; + driv->WriteAudio = OSS_WriteAudio; + + GF_REGISTER_MODULE_INTERFACE(driv, GF_AUDIO_OUTPUT_INTERFACE, "OSS Audio Output", "gpac distribution"); + return driv; +} + +void DeleteOSSRender(void *ifce) +{ + GF_AudioOutput*dr = (GF_AudioOutput*) ifce; + OSSContext *ctx = (OSSContext *)dr->opaque; + free(ctx); + free(dr); +} + + +/* + * ******************************************************************** + * interface + */ +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) + return 1; + return 0; +} + +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) + return NewOSSRender(); + return NULL; +} + +void ShutdownInterface(GF_BaseInterface *ifce) +{ + if (ifce->InterfaceType==GF_AUDIO_OUTPUT_INTERFACE) + DeleteOSSRender((GF_AudioOutput*)ifce); +} diff --git a/modules/pulseaudio/Makefile b/modules/pulseaudio/Makefile new file mode 100644 index 0000000..64fda77 --- /dev/null +++ b/modules/pulseaudio/Makefile @@ -0,0 +1,60 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/pulseaudio + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= pulseaudio.o + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + + +SRCS := $(OBJS:.o=.c) + +LIB=gm_pulseaudio.$(DYN_LIB_SUFFIX) + +all: $(LIB) + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac -L/usr/lib -lpulse-simple + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif + diff --git a/modules/pulseaudio/pulseaudio.c b/modules/pulseaudio/pulseaudio.c new file mode 100644 index 0000000..a1e1413 --- /dev/null +++ b/modules/pulseaudio/pulseaudio.c @@ -0,0 +1,328 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * Copyright (c) Pierre Souchay 2008 + * + * History: + * + * 2008/03/30 - v1.1 (Pierre Souchay) + * first revision + * + * PulseAudio output module : output audio thru the PulseAudio daemon + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <poll.h> +#include <pulse/pulseaudio.h> +#include <pulse/simple.h> +#include <gpac/modules/audio_out.h> + +typedef struct +{ + pa_simple *playback_handle; + pa_sample_spec sample_spec; + const char *output_name; + const char *output_description; + int errors; +} PulseAudioContext; + +static void +free_pulseaudio_resources (GF_AudioOutput * dr) +{ + if (dr == NULL) + return; + PulseAudioContext *ctx = (PulseAudioContext *) dr->opaque; + if (ctx == NULL) + return; + if (ctx->playback_handle != NULL) + { + pa_simple_free (ctx->playback_handle); + } + ctx->playback_handle = NULL; +} + +static const char *MODULE_NAME = "PulseAudio"; + +static const char *OUTPUT_NAME = "OutputName"; + +static const char *OUTPUT_DESCRIPTION = "OutputDescription"; + +static const char *DEFAULT_OUTPUT_NAME = "GPAC"; + +static const char *DEFAULT_OUTPUT_DESCRIPTION = "GPAC Output"; + +static GF_Err +PulseAudio_Setup (GF_AudioOutput * dr, void *os_handle, + u32 num_buffers, u32 total_duration) +{ + const char *opt; + PulseAudioContext *ctx = (PulseAudioContext *) dr->opaque; + if (ctx == NULL) + return GF_BAD_PARAM; + opt = gf_modules_get_option ((GF_BaseInterface *) dr, MODULE_NAME, + OUTPUT_NAME); + ctx->output_name = DEFAULT_OUTPUT_NAME; + if (opt != NULL) + { + ctx->output_name = opt; + } + else + { + gf_modules_set_option ((GF_BaseInterface *) dr, MODULE_NAME, + OUTPUT_NAME, DEFAULT_OUTPUT_NAME); + } + opt = gf_modules_get_option ((GF_BaseInterface *) dr, MODULE_NAME, + OUTPUT_DESCRIPTION); + ctx->output_description = DEFAULT_OUTPUT_DESCRIPTION; + if (opt != NULL) + { + ctx->output_description = opt; + } + else + { + gf_modules_set_option ((GF_BaseInterface *) dr, MODULE_NAME, + OUTPUT_DESCRIPTION, DEFAULT_OUTPUT_DESCRIPTION); + } + return GF_OK; +} + +static void +PulseAudio_Shutdown (GF_AudioOutput * dr) +{ + int pa_error = 0; + PulseAudioContext *ctx = (PulseAudioContext *) dr->opaque; + if (ctx == NULL) + return; + if (ctx->playback_handle) + { + GF_LOG (GF_LOG_DEBUG, GF_LOG_MMIO, + ("[PulseAudio] Closing PulseAudio output\n")); + pa_simple_drain (ctx->playback_handle, &pa_error); + if (pa_error) + { + GF_LOG (GF_LOG_ERROR, GF_LOG_MMIO, + ("[PulseAudio] Error while closing PulseAudio output: %s\n", + pa_strerror (pa_error))); + + } + } +} + +static GF_Err +PulseAudio_ConfigureOutput (GF_AudioOutput * dr, u32 * SampleRate, + u32 * NbChannels, u32 * nbBitsPerSample, + u32 channel_cfg) +{ + int pa_error = 0; + PulseAudioContext *ctx = (PulseAudioContext *) dr->opaque; + if (ctx->playback_handle != NULL) + { + GF_LOG (GF_LOG_ERROR, GF_LOG_MMIO, + ("[PulseAudio] PulseAudio output already configured !\n")); + /* Should not happen */ + pa_simple_flush (ctx->playback_handle, &pa_error); + pa_simple_free (ctx->playback_handle); + ctx->playback_handle = NULL; + } + ctx->sample_spec.format = PA_SAMPLE_S16NE; + ctx->sample_spec.channels = *NbChannels; + ctx->sample_spec.rate = *SampleRate; + ctx->playback_handle = pa_simple_new (NULL, + ctx->output_name, + PA_STREAM_PLAYBACK, + NULL, + ctx->output_description, + &(ctx->sample_spec), + NULL, NULL, &pa_error); + if (ctx->playback_handle == NULL || pa_error != 0) + { + GF_LOG (GF_LOG_ERROR, GF_LOG_MMIO, + ("[PulseAudio] Error while allocating PulseAudio output: %s.\n", + pa_strerror (pa_error))); + return GF_IO_ERR; + } + GF_LOG (GF_LOG_DEBUG, GF_LOG_MMIO, ("[PulseAudio] Initialized - sampling rate %d - %d channels\n", ctx->sample_spec.rate, ctx->sample_spec.channels)); + return GF_OK; +} + +#define BUFF_SIZE 8192 + +static void +PulseAudio_WriteAudio (GF_AudioOutput * dr) +{ + char data[BUFF_SIZE]; + int written = 0; + int pa_error = 0; + PulseAudioContext *ctx = (PulseAudioContext *) dr->opaque; + if (ctx == NULL || ctx->playback_handle == NULL) + { + if (ctx == NULL || ctx->errors == 0) + { + if (ctx != NULL) + ctx->errors++; + GF_LOG (GF_LOG_ERROR, GF_LOG_MMIO, + ("[PulseAudio] unable to connect to a PulseAudio daemon!\n"))} + return; + } + written = dr->FillBuffer (dr->audio_renderer, data, BUFF_SIZE / 4); + if (written <= 0) + { + return; + } + written = pa_simple_write (ctx->playback_handle, data, written, &pa_error); + if (pa_error != 0) + { + if (ctx->errors < 1) + GF_LOG (GF_LOG_ERROR, GF_LOG_MMIO, + ("[PulseAudio] Write failure: %s\n", pa_strerror (pa_error))); + ctx->errors++; + } + else + { + ctx->errors = 0; + } +} + +static void +PulseAudio_SetVolume (GF_AudioOutput * dr, u32 Volume) +{ + GF_LOG (GF_LOG_DEBUG, GF_LOG_MMIO, + ("[PulseAudio] Set volume to %lu: not yet implemented.\n", Volume)); +} + +static void +PulseAudio_SetPan (GF_AudioOutput * dr, u32 Pan) +{ +} + +static void +PulseAudio_SetPriority (GF_AudioOutput * dr, u32 Priority) +{ +} + +static u32 +PulseAudio_GetAudioDelay (GF_AudioOutput * dr) +{ + pa_usec_t delay = 0; + int pa_error = 0; + u32 ms_delay = 0; + PulseAudioContext *ctx = (PulseAudioContext *) dr->opaque; + if (ctx == NULL || ctx->playback_handle == NULL) + { + GF_LOG (GF_LOG_ERROR, GF_LOG_MMIO, + ("[PulseAudio] missing connection to pulseaudio daemon!\n")) + return 0; + } + delay = pa_simple_get_latency (ctx->playback_handle, &pa_error); + if (pa_error) + { + GF_LOG (GF_LOG_ERROR, GF_LOG_MMIO, + ("[PulseAudio] Error while retrieving pulseaudio delay: %s.\n", + pa_strerror (pa_error))); + return 0; + } + ms_delay = (u32) (delay / 1000); + GF_LOG (GF_LOG_DEBUG, GF_LOG_MMIO, ("[PulseAudio] Audio delay: %llu us.\n", + delay)); + return ms_delay; +} + +static GF_Err +PulseAudio_QueryOutputSampleRate (GF_AudioOutput * dr, u32 * desired_sr, + u32 * NbChannels, u32 * nbBitsPerSample) +{ + /** + * PulseAudio can do the resampling by itself and play any number of channels + */ + return GF_OK; +} + +void * +NewPulseAudioOutput () +{ + PulseAudioContext *ctx; + GF_AudioOutput *driv; + GF_SAFEALLOC (ctx, PulseAudioContext); + if (!ctx) + return NULL; + + GF_SAFEALLOC (driv, GF_AudioOutput); + if (!driv) + { + free (ctx); + return NULL; + } + driv->opaque = ctx; + ctx->playback_handle = NULL; + ctx->errors = 0; + driv->SelfThreaded = 0; + driv->Setup = PulseAudio_Setup; + driv->Shutdown = PulseAudio_Shutdown; + driv->ConfigureOutput = PulseAudio_ConfigureOutput; + driv->GetAudioDelay = PulseAudio_GetAudioDelay; + driv->SetVolume = PulseAudio_SetVolume; + driv->SetPan = PulseAudio_SetPan; + driv->SetPriority = PulseAudio_SetPriority; + driv->QueryOutputSampleRate = PulseAudio_QueryOutputSampleRate; + driv->WriteAudio = PulseAudio_WriteAudio; + GF_REGISTER_MODULE_INTERFACE (driv, GF_AUDIO_OUTPUT_INTERFACE, "PulseAudio Audio Output", "gpac distribution"); + return driv; +} + +void +DeletePulseAudioOutput (void *ifce) +{ + GF_AudioOutput *dr = (GF_AudioOutput *) ifce; + free_pulseaudio_resources (dr); + if (dr != NULL) + free (dr); +} + + +/* + * ******************************************************************** + * interface + */ +Bool +QueryInterface (u32 InterfaceType) +{ + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) + return 1; + return 0; +} + +GF_BaseInterface * +LoadInterface (u32 InterfaceType) +{ + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) + return NewPulseAudioOutput (); + return NULL; +} + +void +ShutdownInterface (GF_BaseInterface * ifce) +{ + if (ifce->InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) + DeletePulseAudioOutput ((GF_AudioOutput *) ifce); +} + diff --git a/modules/raw_out/Makefile b/modules/raw_out/Makefile new file mode 100644 index 0000000..68e5787 --- /dev/null +++ b/modules/raw_out/Makefile @@ -0,0 +1,62 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/raw_out + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= raw_video.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_raw_out.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols raw_out.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/raw_out/raw_out.def b/modules/raw_out/raw_out.def new file mode 100644 index 0000000..a8ca11b --- /dev/null +++ b/modules/raw_out/raw_out.def @@ -0,0 +1,6 @@ +LIBRARY gm_raw_out.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/raw_out/raw_video.c b/modules/raw_out/raw_video.c new file mode 100644 index 0000000..025f50d --- /dev/null +++ b/modules/raw_out/raw_video.c @@ -0,0 +1,157 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / DirectX audio and video render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +/*driver interfaces*/ +#include <gpac/modules/video_out.h> +#include <gpac/list.h> +#include <gpac/constants.h> + +typedef struct +{ + char *pixels; + u32 width, height; +} RawContext; + + +#define RAW_OUT_PIXEL_FORMAT GF_PIXEL_RGB_24 +#define NBPP 3 + +#define RAWCTX RawContext *rc = (RawContext *)dr->opaque + +static GF_Err raw_resize(GF_VideoOutput *dr, u32 w, u32 h) +{ + RAWCTX; + + if (rc->pixels) free(rc->pixels); + rc->width = w; + rc->height = h; + rc->pixels = malloc(sizeof(char) * NBPP * w * h); + if (!rc->pixels) return GF_OUT_OF_MEM; + return GF_OK; +} + +GF_Err RAW_Setup(GF_VideoOutput *dr, void *os_handle, void *os_display, u32 init_flags) +{ + raw_resize(dr, 100, 100); + return GF_OK; +} + + +static void RAW_Shutdown(GF_VideoOutput *dr) +{ + RAWCTX; + if (rc->pixels) free(rc->pixels); + rc->pixels = NULL; +} + + +static GF_Err RAW_Flush(GF_VideoOutput *dr, GF_Window *dest) +{ + return GF_OK; +} + +static GF_Err RAW_LockBackBuffer(GF_VideoOutput *dr, GF_VideoSurface *vi, Bool do_lock) +{ + RAWCTX; + if (do_lock) { + if (!vi) return GF_BAD_PARAM; + vi->height = rc->height; + vi->width = rc->width; + vi->video_buffer = rc->pixels; + vi->pitch = NBPP * vi->width; + vi->pixel_format = RAW_OUT_PIXEL_FORMAT; + } + return GF_OK; +} + +static GF_Err RAW_ProcessEvent(GF_VideoOutput *dr, GF_Event *evt) +{ + if (evt) { + switch (evt->type) { + case GF_EVENT_VIDEO_SETUP: + if (evt->setup.opengl_mode) return GF_NOT_SUPPORTED; + return raw_resize(dr, evt->setup.width, evt->setup.height); + } + } + return GF_OK; +} + +GF_VideoOutput *NewRawVideoOutput() +{ + RawContext *pCtx; + GF_VideoOutput *driv = (GF_VideoOutput *) malloc(sizeof(GF_VideoOutput)); + memset(driv, 0, sizeof(GF_VideoOutput)); + GF_REGISTER_MODULE_INTERFACE(driv, GF_VIDEO_OUTPUT_INTERFACE, "Raw Video Output", "gpac distribution") + + pCtx = malloc(sizeof(RawContext)); + memset(pCtx, 0, sizeof(RawContext)); + + driv->opaque = pCtx; + + driv->Flush = RAW_Flush; + driv->LockBackBuffer = RAW_LockBackBuffer; + driv->Setup = RAW_Setup; + driv->Shutdown = RAW_Shutdown; + driv->ProcessEvent = RAW_ProcessEvent; + return (void *)driv; +} + +void DeleteVideoOutput(void *ifce) +{ + RawContext *rc; + GF_VideoOutput *driv = (GF_VideoOutput *) ifce; + + RAW_Shutdown(driv); + rc = (RawContext *)driv->opaque; + free(rc); + free(driv); +} + +#ifndef GPAC_STANDALONE_RENDER_2D + +/*interface query*/ +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return 1; + return 0; +} +/*interface create*/ +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return (GF_BaseInterface *) NewRawVideoOutput(); + return NULL; +} +/*interface destroy*/ +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_VIDEO_OUTPUT_INTERFACE: + DeleteVideoOutput((GF_VideoOutput *)ifce); + break; + } +} + +#endif diff --git a/modules/rtp_in/Makefile b/modules/rtp_in/Makefile new file mode 100644 index 0000000..b671ef2 --- /dev/null +++ b/modules/rtp_in/Makefile @@ -0,0 +1,63 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/rtp_in + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS= rtp_in.o rtp_session.o rtp_signaling.o rtp_stream.o sdp_fetch.o sdp_load.o + + +SRCS := $(OBJS:.o=.c) + +LIB=gm_rtp_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols rtp_in.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) -L../../bin/gcc -lgpac $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/rtp_in/rtp_in.c b/modules/rtp_in/rtp_in.c new file mode 100644 index 0000000..1bd38f0 --- /dev/null +++ b/modules/rtp_in/rtp_in.c @@ -0,0 +1,751 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / RTP input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "rtp_in.h" + +static void RT_LoadPrefs(GF_InputService *plug, RTPClient *rtp) +{ + const char *sOpt; + + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Streaming", "DefaultPort"); + if (sOpt) { + rtp->default_port = atoi(sOpt); + } else { + rtp->default_port = 554; + } + if ((rtp->default_port == 80) || (rtp->default_port == 8080)) + gf_modules_set_option((GF_BaseInterface *)plug, "Streaming", "RTPoverRTSP", "yes"); + + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Streaming", "RTPoverRTSP"); + if (sOpt && !stricmp(sOpt, "yes")) { + rtp->transport_mode = 1; + } else if (sOpt && !stricmp(sOpt, "OnlyCritical")) { + rtp->transport_mode = 2; + } else { + rtp->transport_mode = 0; + } + + /* + get heneral network config for UDP + */ + /*if UDP not available don't try it*/ + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Network", "UDPNotAvailable"); + if (sOpt && !stricmp(sOpt, "yes")) { + if (!rtp->transport_mode) rtp->transport_mode = 1; + /*turn it off*/ + gf_modules_set_option((GF_BaseInterface *)plug, "Network", "UDPNotAvailable", "no"); + } + + if (!rtp->transport_mode) { + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Network", "UDPTimeout"); + if (sOpt ) { + rtp->udp_time_out = atoi(sOpt); + } else { + rtp->udp_time_out = 10000; + } + } + + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Streaming", "RTSPTimeout"); + if (sOpt ) { + rtp->time_out = atoi(sOpt); + } else { + rtp->time_out = 30000; + } + + /*packet drop emulation*/ + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Streaming", "FirstPacketDrop"); + if (sOpt) { + rtp->first_packet_drop = atoi(sOpt); + } else { + rtp->first_packet_drop = 0; + } + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Streaming", "PacketDropFrequency"); + if (sOpt) { + rtp->frequency_drop = atoi(sOpt); + } else { + rtp->frequency_drop = 0; + } + +// rtp->handle_announce = 0; +} + +static void RP_cleanup(RTPClient *rtp) +{ + RTSPSession *sess; + + while (gf_list_count(rtp->channels)) { + RTPStream *ch = (RTPStream *)gf_list_get(rtp->channels, 0); + gf_list_rem(rtp->channels, 0); + RP_DeleteStream(ch); + } + + while ( (sess = (RTSPSession *)gf_list_last(rtp->sessions)) ) { + gf_list_rem_last(rtp->sessions); + RP_DelSession(sess); + } + + if (rtp->session_desc) gf_odf_desc_del(rtp->session_desc); + rtp->session_desc = NULL; + + if (rtp->sdp_temp) { + free(rtp->sdp_temp->remote_url); + free(rtp->sdp_temp); + } + rtp->sdp_temp = NULL; +} + +u32 RP_Thread(void *param) +{ + u32 i; + GF_NetworkCommand com; + RTSPSession *sess; + RTPStream *ch; + RTPClient *rtp = (RTPClient *)param; + + rtp->th_state = 1; + com.command_type = GF_NET_CHAN_BUFFER_QUERY; + while (rtp->th_state) { + gf_mx_p(rtp->mx); + + /*fecth data on udp*/ + i=0; + while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) { + if ((ch->flags & RTP_EOS) || (ch->status!=RTP_Running) ) continue; + /*for interleaved channels don't read too fast, query the buffer occupancy*/ + if (ch->flags & RTP_INTERLEAVED) { + com.base.on_channel = ch->channel; + gf_term_on_command(rtp->service, &com, GF_OK); + /*if no buffering, use a default value (3 sec of data should do it)*/ + if (!com.buffer.max) com.buffer.max = 3000; + if (com.buffer.occupancy <= com.buffer.max) ch->rtsp->flags |= RTSP_TCP_FLUSH; + } else { + RP_ReadStream(ch); + } + } + + /*and process commands / flush TCP*/ + i=0; + while ((sess = (RTSPSession *)gf_list_enum(rtp->sessions, &i))) { + RP_ProcessCommands(sess); + + if (sess->connect_error) { + gf_term_on_connect(sess->owner->service, NULL, sess->connect_error); + sess->connect_error = 0; + } + + } + + gf_mx_v(rtp->mx); + + gf_sleep(1); + } + + if (rtp->dnload) gf_term_download_del(rtp->dnload); + rtp->dnload = NULL; + + rtp->th_state = 2; + return 0; +} + + +static Bool RP_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt = strrchr(url, '.'); + + if (sExt && gf_term_check_extension(plug, "application/sdp", "sdp", "OnDemand Media/Multicast Session", sExt)) return 1; + + /*local */ + if (strstr(url, "data:application/sdp")) return 1; + /*embedded data*/ + if (strstr(url, "data:application/mpeg4-od-au;base64") || + strstr(url, "data:application/mpeg4-bifs-au;base64") || + strstr(url, "data:application/mpeg4-es-au;base64")) return 1; + + /*we need rtsp/tcp , rtsp/udp or direct RTP sender (no control)*/ + if (!strnicmp(url, "rtsp://", 7) || !strnicmp(url, "rtspu://", 8) || !strnicmp(url, "rtp://", 6)) return 1; + /*we don't check extensions*/ + return 0; +} + +GF_Err RP_ConnectServiceEx(GF_InputService *plug, GF_ClientService *serv, const char *url, Bool skip_migration) +{ + char *session_cache; + RTSPSession *sess; + RTPClient *priv = (RTPClient *)plug->priv; + + /*store user address*/ + priv->service = serv; + + if (priv->dnload) gf_term_download_del(priv->dnload); + priv->dnload = NULL; + + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[RTP] Opening service %s\n", url)); + + /*load preferences*/ + RT_LoadPrefs(plug, priv); + + /*start thread*/ + gf_th_run(priv->th, RP_Thread, priv); + + if (!skip_migration) { + session_cache = (char *) gf_modules_get_option((GF_BaseInterface *) plug, "Streaming", "SessionMigrationFile"); + if (session_cache && session_cache[0]) { + FILE *f = fopen(session_cache, "rb"); + if (f) { + fclose(f); + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[RTP] Restarting RTSP session from %s\n", session_cache)); + RP_FetchSDP(priv, (char *) session_cache, NULL, (char *) url); + return GF_OK; + } + if (!strncmp(session_cache, "http://", 7)) { + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[RTP] Restarting RTSP session from %s\n", session_cache)); + RP_FetchSDP(priv, (char *) session_cache, NULL, (char *) url); + return GF_OK; + } + } + } + + + /*local or remote SDP*/ + if (strstr(url, "data:application/sdp") || (strnicmp(url, "rtsp", 4) && strstr(url, ".sdp")) ) { + RP_FetchSDP(priv, (char *) url, NULL, NULL); + return GF_OK; + } + + /*rtsp and rtsp over udp*/ + if (!strnicmp(url, "rtsp://", 7) || !strnicmp(url, "rtspu://", 8)) { + char *the_url = strdup(url); + char *the_ext = strrchr(the_url, '#'); + if (the_ext) { + if (!stricmp(the_ext, "#audio")) priv->media_type = GF_MEDIA_OBJECT_AUDIO; + else if (!stricmp(the_ext, "#video")) priv->media_type = GF_MEDIA_OBJECT_VIDEO; + the_ext[0] = 0; + } + sess = RP_NewSession(priv, (char *) the_url); + free(the_url); + if (!sess) { + gf_term_on_connect(serv, NULL, GF_NOT_SUPPORTED); + } else { + RP_Describe(sess, 0, NULL); + } + return GF_OK; + } + + /*direct RTP (no control) or embedded data - this means the service is attached to a single channel (no IOD) + reply right away*/ + gf_term_on_connect(serv, NULL, GF_OK); + RP_SetupObjects(priv); + return GF_OK; +} + +GF_Err RP_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + return RP_ConnectServiceEx(plug, serv, url, 0); +} + +static void RP_FlushCommands(RTPClient *rtp) +{ + u32 i, nb_com; + RTSPSession *sess; + /*process teardown on all sessions*/ + while (1) { + nb_com = 0; + i=0; + while ((sess = (RTSPSession *)gf_list_enum(rtp->sessions, &i))) { + if (!sess->connect_error) + nb_com += gf_list_count(sess->rtsp_commands); + } + if (!nb_com) break; + gf_sleep(10); + } +} + +static GF_Err RP_CloseService(GF_InputService *plug) +{ + u32 i; + const char *opt; + RTSPSession *sess; + RTPClient *rtp = (RTPClient *)plug->priv; + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[RTP] Closing service\n")); + + RP_FlushCommands(rtp); + + opt = gf_modules_get_option((GF_BaseInterface *) plug, "Network", "SessionMigration"); + if (opt && !strcmp(opt, "yes") ) { + opt = gf_modules_get_option((GF_BaseInterface *) plug, "Streaming", "SessionMigrationPause"); + if (opt && !strcmp(opt, "yes")) { + GF_NetworkCommand com; + com.command_type = GF_NET_CHAN_PAUSE; + com.base.on_channel = NULL; + /*send pause on all sessions*/ + i=0; + while ((sess = (RTSPSession *)gf_list_enum(rtp->sessions, &i))) { + RP_UserCommand(sess, NULL, &com); + } + } + + RP_SaveSessionState(rtp); + } else { + /*remove session state file*/ + if (rtp->session_state) { + gf_delete_file(rtp->session_state); + } + /*send teardown on all sessions*/ + i=0; + while ((sess = (RTSPSession *)gf_list_enum(rtp->sessions, &i))) { + RP_Teardown(sess, NULL); + } + } + RP_FlushCommands(rtp); + + /*shutdown thread*/ + if (rtp->th_state==1) rtp->th_state = 0; + + /*confirm close*/ + gf_term_on_disconnect(rtp->service, NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *RP_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + GF_Descriptor *desc; + RTPClient *priv = (RTPClient *)plug->priv; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[RTP] Fetching service descriptor\n")); + if ((expect_type!=GF_MEDIA_OBJECT_UNDEF) && (expect_type!=GF_MEDIA_OBJECT_SCENE) && (expect_type!=GF_MEDIA_OBJECT_UPDATES)) { + /*ignore the SDP IOD and regenerate one*/ + if (priv->session_desc) gf_odf_desc_del(priv->session_desc); + priv->session_desc = NULL; + priv->media_type = expect_type; + return RP_EmulateIOD(priv, sub_url); + } + + desc = priv->session_desc; + priv->session_desc = NULL; + return desc; +} + +static GF_Err RP_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ESID; + RTPStream *ch; + RTSPSession *sess; + char *es_url; + RTPClient *priv = (RTPClient *)plug->priv; + if (upstream) return GF_NOT_SUPPORTED; + + + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[RTP] Connecting channel @%08x - %s\n", channel, url)); + + ch = RP_FindChannel(priv, channel, 0, (char *) url, 0); + if (ch && (ch->status != RTP_Disconnected) ) return GF_SERVICE_ERROR; + + es_url = NULL; + sess = NULL; + if (strstr(url, "ES_ID=")) { + sscanf(url, "ES_ID=%d", &ESID); + /*first case: simple URL (same namespace)*/ + ch = RP_FindChannel(priv, NULL, ESID, NULL, 0); + /*this should not happen, the sdp must describe all streams in the service*/ + if (!ch) return GF_STREAM_NOT_FOUND; + + /*assign app channel*/ + ch->channel = channel; + sess = ch->rtsp; + } + /*rtsp url - create a session if needed*/ + else if (!strnicmp(url, "rtsp://", 7) || !strnicmp(url, "rtspu://", 8)) { + sess = RP_CheckSession(priv, (char *) url); + if (!sess) sess = RP_NewSession(priv, (char *) url); + es_url = (char *) url; + } + /*data: url*/ + else if (strstr(url, "data:application/mpeg4-od-au;base64") + || strstr(url, "data:application/mpeg4-bifs-au;base64") + || strstr(url, "data:application/mpeg4-es-au;base64") + ) { + + GF_SAFEALLOC(ch, RTPStream); + ch->control = strdup(url); + ch->owner = priv; + ch->channel = channel; + ch->status = RTP_Connected; + /*register*/ + gf_list_add(priv->channels, ch); + RP_ConfirmChannelConnect(ch, GF_OK); + + return GF_OK; + } + /*session migration resume - don't send data to the server*/ + if (ch->status==RTP_SessionResume) { + ch->flags |= RTP_CONNECTED; + RP_InitStream(ch, 0); + RP_ConfirmChannelConnect(ch, GF_OK); + return GF_OK; + } + /*send a DESCRIBE (not a setup) on the channel. If the channel is already created then the + describe is skipped and a SETUP is sent directly, otherwise the channel is first described then setup*/ + if (sess) RP_Describe(sess, es_url, channel); + /*otherwise confirm channel connection*/ + else RP_ConfirmChannelConnect(ch, GF_OK); + + return GF_OK; +} + + +static GF_Err RP_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + RTPStream *ch; + RTPClient *priv = (RTPClient *)plug->priv; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[RTP] Disconnecting channel @%08x\n", channel)); + + ch = RP_FindChannel(priv, channel, 0, NULL, 0); + if (!ch) return GF_STREAM_NOT_FOUND; + gf_mx_p(priv->mx); + /*disconnect stream BUT DO NOT DELETE IT since we don't store SDP*/ + ch->flags &= ~RTP_CONNECTED; + ch->channel = NULL; + gf_mx_v(priv->mx); + gf_term_on_disconnect(priv->service, channel, GF_OK); + return GF_OK; +} + +static GF_Err RP_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + RTPStream *ch; + RTPClient *priv = (RTPClient *)plug->priv; + + + if (com->command_type==GF_NET_SERVICE_HAS_AUDIO) { + u32 i; + for (i=0; i<gf_list_count(priv->channels); i++) { + ch = gf_list_get(priv->channels, i); + if (ch->depacketizer->sl_map.StreamType==GF_STREAM_AUDIO) + return GF_OK; + } + return GF_NOT_SUPPORTED; + } + + /*ignore commands other than channels one*/ + if (!com->base.on_channel) { + if (com->command_type==GF_NET_IS_CACHABLE) return GF_OK; + return GF_NOT_SUPPORTED; + } + + ch = RP_FindChannel(priv, com->base.on_channel, 0, NULL, 0); + if (!ch) return GF_STREAM_NOT_FOUND; + + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: + if (ch->rtp_ch || ch->rtsp || !ch->control) return GF_NOT_SUPPORTED; + /*embedded channels work in pull mode*/ + if (strstr(ch->control, "data:application/")) return GF_OK; + return GF_NOT_SUPPORTED; + case GF_NET_CHAN_INTERACTIVE: + /*looks like pure RTP / multicast etc, not interactive*/ + if (!ch->control) return GF_NOT_SUPPORTED; + /*emulated broadcast mode*/ + else if (ch->flags & RTP_FORCE_BROADCAST) return GF_NOT_SUPPORTED; + /*regular rtsp mode*/ + else if (ch->flags & RTP_HAS_RANGE) return GF_OK; + /*embedded data*/ + else if (strstr(ch->control, "application")) return GF_OK; + return GF_NOT_SUPPORTED; + case GF_NET_CHAN_BUFFER: + if (!(ch->rtp_ch || ch->rtsp || !ch->control)) { + com->buffer.max = com->buffer.min = 0; + } else { + const char *opt; + /*amount of buffering in ms*/ + opt = gf_modules_get_option((GF_BaseInterface *)plug, "Network", "BufferLength"); + com->buffer.max = opt ? atoi(opt) : 1000; + /*rebuffer low limit in ms - if the amount of buffering is less than this, rebuffering will never occur*/ + opt = gf_modules_get_option((GF_BaseInterface *)plug, "Network", "RebufferLength"); + if (opt) com->buffer.min = atoi(opt); + else com->buffer.min = 500; + if (com->buffer.min >= com->buffer.max ) com->buffer.min = 0; + } + return GF_OK; + case GF_NET_CHAN_DURATION: + com->duration.duration = (ch->flags & RTP_HAS_RANGE) ? (ch->range_end - ch->range_start) : 0; + return GF_OK; + /*RTP channel config is done upon connection, once the complete SL mapping is known + however we must store some info not carried in SDP*/ + case GF_NET_CHAN_CONFIG: + if (com->cfg.frame_duration) ch->depacketizer->sl_hdr.au_duration = com->cfg.frame_duration; + ch->ts_res = com->cfg.sl_config.timestampResolution; + return GF_OK; + + case GF_NET_CHAN_PLAY: + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[RTP] Processing play on channel @%08x - %s\n", ch, ch->rtsp ? "RTSP control" : "No control (RTP)" )); + /*is this RTSP or direct RTP?*/ + ch->flags &= ~RTP_EOS; + if (ch->rtsp) { + if (ch->status==RTP_SessionResume) { + const char *opt = gf_modules_get_option((GF_BaseInterface *) plug, "Streaming", "SessionMigrationPause"); + if (opt && !strcmp(opt, "yes")) { + ch->status = RTP_Connected; + com->play.start_range = ch->current_start; + } else { + ch->status = RTP_Running; + return GF_OK; + } + } + RP_UserCommand(ch->rtsp, ch, com); + } else { + ch->status = RTP_Running; + if (ch->rtp_ch) { + ch->check_rtp_time = 1; + gf_mx_p(priv->mx); + RP_InitStream(ch, 0); + gf_mx_v(priv->mx); + gf_rtp_set_info_rtp(ch->rtp_ch, 0, 0, 0); + } else { + /*direct channel, store current start*/ + ch->current_start = com->play.start_range; + ch->flags |= GF_RTP_NEW_AU; + gf_rtp_depacketizer_reset(ch->depacketizer, 0); + } + } + return GF_OK; + case GF_NET_CHAN_STOP: + /*is this RTSP or direct RTP?*/ + if (ch->rtsp) { + const char *opt = gf_modules_get_option((GF_BaseInterface *) plug, "Network", "SessionMigration"); + if (opt && !strcmp(opt, "yes")) { + } else { + RP_UserCommand(ch->rtsp, ch, com); + } + } else { + ch->status = RTP_Connected; + } + return GF_OK; + case GF_NET_CHAN_SET_SPEED: + case GF_NET_CHAN_PAUSE: + case GF_NET_CHAN_RESUME: + assert(ch->rtsp); + RP_UserCommand(ch->rtsp, ch, com); + return GF_OK; + + case GF_NET_CHAN_GET_DSI: + if (ch->depacketizer && ch->depacketizer->sl_map.configSize) { + com->get_dsi.dsi_len = ch->depacketizer->sl_map.configSize; + com->get_dsi.dsi = (char*)malloc(sizeof(char)*com->get_dsi.dsi_len); + memcpy(com->get_dsi.dsi, ch->depacketizer->sl_map.config, sizeof(char)*com->get_dsi.dsi_len); + } else { + com->get_dsi.dsi = NULL; + com->get_dsi.dsi_len = 0; + } + return GF_OK; + + + case GF_NET_GET_STATS: + memset(&com->net_stats, 0, sizeof(GF_NetComStats)); + if (ch->rtp_ch) { + u32 time; + Float bps; + com->net_stats.pck_loss_percentage = gf_rtp_get_loss(ch->rtp_ch); + if (ch->flags & RTP_INTERLEAVED) { + com->net_stats.multiplex_port = gf_rtsp_get_session_port(ch->rtsp->session); + com->net_stats.port = gf_rtp_get_low_interleave_id(ch->rtp_ch); + com->net_stats.ctrl_port = gf_rtp_get_hight_interleave_id(ch->rtp_ch); + } else { + com->net_stats.multiplex_port = 0; + gf_rtp_get_ports(ch->rtp_ch, &com->net_stats.port, &com->net_stats.ctrl_port); + } + if (ch->stat_stop_time) { + time = ch->stat_stop_time - ch->stat_start_time; + } else { + time = gf_sys_clock() - ch->stat_start_time; + } + bps = 8.0f * ch->rtp_bytes; bps *= 1000; bps /= time; com->net_stats.bw_down = (u32) bps; + bps = 8.0f * ch->rtcp_bytes; bps *= 1000; bps /= time; com->net_stats.ctrl_bw_down = (u32) bps; + bps = 8.0f * gf_rtp_get_tcp_bytes_sent(ch->rtp_ch); bps *= 1000; bps /= time; com->net_stats.ctrl_bw_up = (u32) bps; + } + return GF_OK; + } + return GF_NOT_SUPPORTED; +} + +static GF_Err RP_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + char *data; + RTPStream *ch; + RTPClient *priv = (RTPClient *)plug->priv; + + ch = RP_FindChannel(priv, channel, 0, NULL, 0); + if (!ch) return GF_STREAM_NOT_FOUND; + if (ch->rtp_ch || ch->rtsp || !ch->control) return GF_SERVICE_ERROR; + if (ch->status != RTP_Running) return GF_SERVICE_ERROR; + data = strstr(ch->control, ";base64"); + if (!data) return GF_SERVICE_ERROR; + + if (ch->current_start>=0) { + *sl_compressed = 0; + memset(out_sl_hdr, 0, sizeof(GF_SLHeader)); + out_sl_hdr->accessUnitEndFlag = 1; + out_sl_hdr->accessUnitStartFlag = 1; + out_sl_hdr->compositionTimeStamp = (u64) (ch->current_start * ch->ts_res); + out_sl_hdr->compositionTimeStampFlag = 1; + out_sl_hdr->randomAccessPointFlag = 1; + *out_reception_status = GF_OK; + *is_new_data = (ch->flags & GF_RTP_NEW_AU) ? 1 : 0; + + /*decode data*/ + data = strstr(data, ","); + data += 1; + *out_data_size = gf_base64_decode(data, strlen(data), ch->buffer, RTP_BUFFER_SIZE); + /*FIXME - currently only support for empty SL header*/ + *out_data_ptr = ch->buffer; + ch->flags &= ~GF_RTP_NEW_AU; + } else { + *out_data_ptr = NULL; + *out_data_size = 0; + *out_reception_status = GF_EOS; + ch->flags |= RTP_EOS; + } + return GF_OK; +} + +static GF_Err RP_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) +{ + RTPStream *ch; + RTPClient *priv = (RTPClient *)plug->priv; + + ch = RP_FindChannel(priv, channel, 0, NULL, 0); + if (!ch) return GF_STREAM_NOT_FOUND; + if (ch->rtp_ch || ch->rtsp || !ch->control) return GF_SERVICE_ERROR; + if (ch->status != RTP_Running) return GF_SERVICE_ERROR; + + /*this will trigger EOS at next fetch*/ + ch->current_start = -1.0; + return GF_OK; +} + +static Bool RP_CanHandleURLInService(GF_InputService *plug, const char *url) +{ + RTSPSession *sess; + RTPClient *priv = (RTPClient *)plug->priv; + + if (strstr(url, "data:application/mpeg4-od-au;base64") + || strstr(url, "data:application/mpeg4-bifs-au;base64") + || strstr(url, "data:application/mpeg4-es-au;base64") + ) return 1; + + if (!RP_CanHandleURL(plug, url)) return 0; + /*if this URL is part of a running session then ok*/ + sess = RP_CheckSession(priv, (char *) url); + if (sess) return 1; + return 0; +} + + +GF_InputService *RTP_Load() +{ + RTPClient *priv; + GF_InputService *plug; + GF_SAFEALLOC(plug, GF_InputService); + + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC RTP/RTSP Client", "gpac distribution") + + plug->CanHandleURL = RP_CanHandleURL; + plug->CanHandleURLInService = RP_CanHandleURLInService; + plug->ConnectService = RP_ConnectService; + plug->CloseService = RP_CloseService; + plug->GetServiceDescriptor = RP_GetServiceDesc; + plug->ConnectChannel = RP_ConnectChannel; + plug->DisconnectChannel = RP_DisconnectChannel; + plug->ServiceCommand = RP_ServiceCommand; + + /*PULL mode for embedded streams*/ + plug->ChannelGetSLP = RP_ChannelGetSLP; + plug->ChannelReleaseSLP = RP_ChannelReleaseSLP; + + GF_SAFEALLOC(priv, RTPClient); + priv->sessions = gf_list_new(); + priv->channels = gf_list_new(); + + plug->priv = priv; + + priv->time_out = 30000; + priv->mx = gf_mx_new("RTPDemux"); + priv->th = gf_th_new("RTPDemux"); + + return plug; +} + + +void RTP_Delete(GF_BaseInterface *bi) +{ + RTPClient *rtp; + u32 retry; + GF_InputService *plug = (GF_InputService *) bi; + rtp = (RTPClient *)plug->priv; + + /*shutdown thread*/ + if (rtp->th_state==1) rtp->th_state = 0; + retry = 20; + while ((rtp->th_state==1) && retry) { + gf_sleep(10); + retry--; + } + assert(retry); + + if (rtp->session_state) free(rtp->session_state); + if (rtp->remote_session_state) free(rtp->remote_session_state); + + + RP_cleanup(rtp); + gf_th_del(rtp->th); + gf_mx_del(rtp->mx); + gf_list_del(rtp->sessions); + gf_list_del(rtp->channels); + free(rtp); + free(bi); +} + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return 1; + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) return (GF_BaseInterface *)RTP_Load(); + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_NET_CLIENT_INTERFACE: + RTP_Delete(ifce); + break; + } +} diff --git a/modules/rtp_in/rtp_in.def b/modules/rtp_in/rtp_in.def new file mode 100644 index 0000000..b838420 --- /dev/null +++ b/modules/rtp_in/rtp_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_rtp_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/rtp_in/rtp_in.h b/modules/rtp_in/rtp_in.h new file mode 100644 index 0000000..2a9bc93 --- /dev/null +++ b/modules/rtp_in/rtp_in.h @@ -0,0 +1,338 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / RTP input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef RTP_IN_H +#define RTP_IN_H + +#include <gpac/thread.h> +#include <gpac/constants.h> +#include <gpac/base_coding.h> +/*module interface*/ +#include <gpac/modules/service.h> +/*IETF lib*/ +#include <gpac/ietf.h> + + +#define RTP_BUFFER_SIZE 0x100000ul +#define RTSP_BUFFER_SIZE 5000 +#define RTSP_TCP_BUFFER_SIZE 0x100000ul +#define RTSP_CLIENTNAME "GPAC " GPAC_VERSION " RTSP Client" +#define RTSP_LANGUAGE "English" + + +/*the rtsp/rtp client*/ +typedef struct +{ + /*the service we're responsible for*/ + GF_ClientService *service; + + /*the one and only IOD*/ + GF_Descriptor *session_desc; + + /*RTSP sessions*/ + GF_List *sessions; + /*RTP/RTCP media channels*/ + GF_List *channels; + + /*sdp downloader*/ + GF_DownloadSession * dnload; + /*initial sdp download if any (temp storage)*/ + struct _sdp_fetch *sdp_temp; + + /*RTSP communication/deinterleaver thread*/ + GF_Mutex *mx; + GF_Thread *th; + u32 th_state; + + /*RTSP config*/ + /*transport mode. 0 is udp, 1 is tcp, 3 is tcp if unreliable media */ + u32 transport_mode; + /*default RTSP port*/ + u16 default_port; + /*signaling timeout in msec*/ + u32 time_out; + /*udp timeout in msec*/ + u32 udp_time_out; + + /*packet drop emulation*/ + u32 first_packet_drop; + u32 frequency_drop; + + /*for single-object control*/ + u32 media_type; + + /*location of the session state (SDP file)*/ + char *session_state; + char *remote_session_state; + char *tmp_buf; + /*if set ANNOUNCE (sent by server) will be handled*/ +// Bool handle_announce; +} RTPClient; + +enum +{ + RTSP_AGG_CONTROL = 1, + RTSP_TCP_FLUSH = 1<<1, + RTSP_FORCE_INTER = 1<<2, + RTSP_WAIT_REPLY = 1<<3, + RTSP_DSS_SERVER = 1<<4, + RTSP_AGG_ONLY = 1<<5, +}; + +/*rtsp session*/ +typedef struct _rtp_session +{ + u32 flags; + + /*owner*/ + RTPClient *owner; + + /*RTSP session object*/ + GF_RTSPSession *session; + /*session ID for aggregated stream control*/ + char *session_id; + + /*session control string*/ + char *control; + + /*response object*/ + GF_RTSPResponse *rtsp_rsp; + + Double last_range; + u32 command_time; + GF_List *rtsp_commands; + GF_Err connect_error; +} RTSPSession; + +/*creates new RTSP session handler*/ +RTSPSession *RP_NewSession(RTPClient *rtp, char *session_control); +/*disconnects and destroy RTSP session handler - if immediate_shutdown do not wait for response*/ +void RP_DelSession(RTSPSession *sess); +/*check session by control string*/ +RTSPSession *RP_CheckSession(RTPClient *rtp, char *control); + +void RP_SetupObjects(RTPClient *rtp); + +void RP_ProcessCommands(RTSPSession *sess); + +/*RTP channel state*/ +enum +{ + /*channel is setup and waits for connection request*/ + RTP_Setup, + /*waiting for server reply*/ + RTP_WaitingForAck, + /*connection OK*/ + RTP_Connected, + /*data exchange on this service/channel*/ + RTP_Running, + /*deconnection OK - a download channel can automatically disconnect when download is done*/ + RTP_Disconnected, + /*service/channel is not (no longer) available/found and should be removed*/ + RTP_Unavailable, + + RTP_SessionResume + +}; + + +/*rtp channel flags*/ +enum +{ + /*static RTP channel flags*/ + + /*set if sending RTCP reports is enabled (default)*/ + RTP_ENABLE_RTCP = 1, + /*set if stream control possible*/ + RTP_HAS_RANGE = (1<<1), + /*set if RTP over RTSP*/ + RTP_INTERLEAVED = (1<<2), + /*broadcast emultaion is on (no time control for stream)*/ + RTP_FORCE_BROADCAST = (1<<3), + + /*RTP channel runtime flags*/ + + /*set if next command (PLAY/PAUSE) is to be skipped (aggregation control)*/ + RTP_SKIP_NEXT_COM = (1<<4), + /*indicates whether channel creation has been acknowledged or not + this is needed to filter real channel_connect calls from RTSP re-setup (after STOP) ones*/ + RTP_CONNECTED = (1<<5), + /*EOS signaled (RTCP or range-based)*/ + RTP_EOS = (1<<6), +}; + +/*rtp channel*/ +typedef struct +{ + /*module*/ + RTPClient *owner; + + /*channel flags*/ + u32 flags; + + /*control session (may be null)*/ + RTSPSession *rtsp; + /*session ID for independent stream control*/ + char *session_id; + + /*RTP channel*/ + GF_RTPChannel *rtp_ch; + + /*depacketizer*/ + GF_RTPDepacketizer *depacketizer; + + /*logical app channel*/ + LPNETCHANNEL channel; + u32 status; + + u32 ES_ID; + char *control; + + /*rtp receive buffer*/ + char buffer[RTP_BUFFER_SIZE]; + /*set at seek stages to resync app NPT to RTP time*/ + u32 check_rtp_time; + + /*can we control the stream ?*/ + Double range_start, range_end; + /*current start time in npt (for pause/resume)*/ + Double current_start; + + /*UDP time-out detection*/ + u32 last_udp_time; + /*RTP stats*/ + u32 rtp_bytes, rtcp_bytes, stat_start_time, stat_stop_time; + u32 ts_res; +} RTPStream; + +GF_Err RP_ConnectServiceEx(GF_InputService *plug, GF_ClientService *serv, const char *url, Bool skip_migration); + + +/*creates new RTP stream from SDP info*/ +RTPStream *RP_NewStream(RTPClient *rtp, GF_SDPMedia *media, GF_SDPInfo *sdp, RTPStream *input_stream); +/*destroys RTP stream */ +void RP_DeleteStream(RTPStream *ch); +/*resets stream state and inits RTP sockets if ResetOnly is false*/ +GF_Err RP_InitStream(RTPStream *ch, Bool ResetOnly); + +/*RTSP -> RTP de-interleaving callback*/ +GF_Err RP_DataOnTCP(GF_RTSPSession *sess, void *cbck, char *buffer, u32 bufferSize, Bool IsRTCP); +/*send confirmation of connection - if no error, also setup SL based on payload*/ +void RP_ConfirmChannelConnect(RTPStream *ch, GF_Err e); + +/*fetch sdp file - stream is the RTP channel this sdp describes, or NULL if session sdp*/ +void RP_FetchSDP(RTPClient *rtp, char *url, RTPStream *stream, char *original_url); + +/*locate RTP stream by channel or ES_ID or control*/ +RTPStream *RP_FindChannel(RTPClient *rtp, LPNETCHANNEL ch, u32 ES_ID, char *es_control, Bool remove_stream); +/*adds channel to session identified by session_control. If no session exists, the session is created if needed*/ +GF_Err RP_AddStream(RTPClient *rtp, RTPStream *stream, char *session_control); +/*removes stream from session*/ +void RP_RemoveStream(RTPClient *rtp, RTPStream *ch); +/*reads input socket and process*/ +void RP_ReadStream(RTPStream *ch); + +/*parse RTP payload for MPEG4*/ +void RP_ParsePayloadMPEG4(RTPStream *ch, GF_RTPHeader *hdr, char *payload, u32 size); +/*parse RTP payload for MPEG12*/ +void RP_ParsePayloadMPEG12(RTPStream *ch, GF_RTPHeader *hdr, char *payload, u32 size); +/*parse RTP payload for AMR*/ +void RP_ParsePayloadAMR(RTPStream *ch, GF_RTPHeader *hdr, char *payload, u32 size); +/*parse RTP payload for H263+*/ +void RP_ParsePayloadH263(RTPStream *ch, GF_RTPHeader *hdr, char *payload, u32 size); +/*parse RTP payload for 3GPP Text*/ +void RP_ParsePayloadText(RTPStream *ch, GF_RTPHeader *hdr, char *payload, u32 size); +/*parse RTP payload for H264/AVC*/ +void RP_ParsePayloadH264(RTPStream *ch, GF_RTPHeader *hdr, char *payload, u32 size); +/*parse RTP payload for LATM audio*/ +void RP_ParsePayloadLATM(RTPStream *ch, GF_RTPHeader *hdr, char *payload, u32 size); + +/*load SDP and setup described media in SDP. If stream is null this is the root +SDP and IOD will be extracted, otherwise this a channel SDP*/ +void RP_LoadSDP(RTPClient *rtp, char *sdp, u32 sdp_len, RTPStream *stream); + +/*returns 1 if payload type is supported*/ +u32 payt_get_type(RTPClient *rtp, GF_RTPMap *map, GF_SDPMedia *media); +/*setup payload type, returns 1 if success, 0 otherwise (in which case the stream will be deleted)*/ +Bool payt_setup(RTPStream *st, GF_RTPMap *map, GF_SDPMedia *media); + + +/*RTSP signaling is handled by stacking commands and processing them +in the main session thread. Each RTSP command has an associated private stack as follows*/ + +/*describe stack for single channel (not for session)*/ +typedef struct +{ + u32 ES_ID; + LPNETCHANNEL channel; + char *esd_url; +} ChannelDescribe; + +typedef struct +{ + RTPStream *ch; + GF_NetworkCommand com; +} ChannelControl; + +/*RTSP signaling */ +Bool RP_PreprocessDescribe(RTSPSession *sess, GF_RTSPCommand *com); +GF_Err RP_ProcessDescribe(RTSPSession *sess, GF_RTSPCommand *com, GF_Err e); +void RP_ProcessSetup(RTSPSession *sess, GF_RTSPCommand *com, GF_Err e); +void RP_ProcessTeardown(RTSPSession *sess, GF_RTSPCommand *com, GF_Err e); +Bool RP_PreprocessUserCom(RTSPSession *sess, GF_RTSPCommand *com); +void RP_ProcessUserCommand(RTSPSession *sess, GF_RTSPCommand *com, GF_Err e); + +/*send describe - if esd_url is given, this is a describe on es*/ +void RP_Describe(RTSPSession *sess, char *esd_url, LPNETCHANNEL channel); +/*send setup for stream*/ +void RP_Setup(RTPStream *ch); +/*filter setup if no session (rtp only), otherwise setup channel - ch_desc may be NULL +if channel association is already done*/ +GF_Err RP_SetupChannel(RTPStream *ch, ChannelDescribe *ch_desc); +/*send command for stream - handles aggregation*/ +void RP_UserCommand(RTSPSession *sess, RTPStream *ch, GF_NetworkCommand *command); +/*disconnect the session - if @ch, only the channel is teardown*/ +void RP_Teardown(RTSPSession *sess, RTPStream *ch); + +/*emulate IOD*/ +GF_Descriptor *RP_EmulateIOD(RTPClient *rtp, const char *sub_url); + + +/*sdp file downloader*/ +typedef struct _sdp_fetch +{ + RTPClient *client; + /*when loading a channel from SDP*/ + RTPStream *chan; + + char *remote_url; + char *original_url; +} SDPFetch; + + +void RP_SaveSessionState(RTPClient *rtp); + +#endif + + diff --git a/modules/rtp_in/rtp_session.c b/modules/rtp_in/rtp_session.c new file mode 100644 index 0000000..1fa3a8e --- /dev/null +++ b/modules/rtp_in/rtp_session.c @@ -0,0 +1,387 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / RTP input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "rtp_in.h" + +void RP_SendFailure(RTSPSession *sess, GF_RTSPCommand *com, GF_Err e) +{ + char sMsg[1000]; + sprintf(sMsg, "Cannot send %s", com->method); + gf_term_on_message(sess->owner->service, e, sMsg); +} + +Bool RP_ProcessResponse(RTSPSession *sess, GF_RTSPCommand *com, GF_Err e) +{ + if (!strcmp(com->method, GF_RTSP_DESCRIBE)) + return RP_ProcessDescribe(sess, com, e); + else if (!strcmp(com->method, GF_RTSP_SETUP)) + RP_ProcessSetup(sess, com, e); + else if (!strcmp(com->method, GF_RTSP_TEARDOWN)) + RP_ProcessTeardown(sess, com, e); + else if (!strcmp(com->method, GF_RTSP_PLAY) || !strcmp(com->method, GF_RTSP_PAUSE)) + RP_ProcessUserCommand(sess, com, e); + return GF_OK; +} + +/*access to command list is protected bymutex, BUT ONLY ACCESS - this way we're sure that command queueing +from app will not deadlock if we're waiting for the app to release any mutex (don't forget play request may +come on stream N while we're processing stream P setup)*/ +static GF_RTSPCommand *RP_GetCommand(RTSPSession *sess) +{ + GF_RTSPCommand *com; + gf_mx_p(sess->owner->mx); + com = (GF_RTSPCommand *)gf_list_get(sess->rtsp_commands, 0); + gf_mx_v(sess->owner->mx); + return com; +} + +static void RP_RemoveCommand(RTSPSession *sess) +{ + gf_mx_p(sess->owner->mx); + gf_list_rem(sess->rtsp_commands, 0); + gf_mx_v(sess->owner->mx); +} + +void RP_ProcessCommands(RTSPSession *sess) +{ + GF_RTSPCommand *com; + GF_Err e; + u32 time; + + com = RP_GetCommand(sess); + + /*if asked or command to send, flushout TCP - TODO: check what's going on with ANNOUNCE*/ + if ((com && !(sess->flags & RTSP_WAIT_REPLY) ) || (sess->flags & RTSP_TCP_FLUSH) ) { + while (1) { + e = gf_rtsp_session_read(sess->session); + if (e) break; + } + sess->flags &= ~RTSP_TCP_FLUSH; + } + + /*handle response or announce*/ + if ( (com && (sess->flags & RTSP_WAIT_REPLY) ) /*|| (!com && sess->owner->handle_announce)*/) { + e = gf_rtsp_get_response(sess->session, sess->rtsp_rsp); + if (e!= GF_IP_NETWORK_EMPTY) { + e = RP_ProcessResponse(sess, com, e); + /*this is a service connect error -> plugin may be discarded */ + if (e!=GF_OK) { + RP_RemoveCommand(sess); + gf_rtsp_command_del(com); + gf_term_on_connect(sess->owner->service, NULL, e); + return; + } + + RP_RemoveCommand(sess); + gf_rtsp_command_del(com); + sess->flags &= ~RTSP_WAIT_REPLY; + sess->command_time = 0; + } else { + /*evaluate timeout*/ + time = gf_sys_clock() - sess->command_time; + /*don't waste time waiting for teardown ACK, half a sec is enough. If server is not replying + in time it is likely to never reply (happens with RTP over RTSP) -> kill session + and create new one*/ + if (!strcmp(com->method, GF_RTSP_TEARDOWN) && (time>=500) ) time = sess->owner->time_out; + //signal what's going on + if (time >= sess->owner->time_out) { + if (!strcmp(com->method, GF_RTSP_TEARDOWN)) gf_rtsp_session_reset(sess->session, 1); + + RP_ProcessResponse(sess, com, GF_IP_NETWORK_FAILURE); + RP_RemoveCommand(sess); + gf_rtsp_command_del(com); + sess->flags &= ~RTSP_WAIT_REPLY; + sess->command_time = 0; + gf_rtsp_reset_aggregation(sess->session); + } + } + return; + } + + if (!com) return; + + /*send command - check RTSP session state first*/ + switch (gf_rtsp_get_session_state(sess->session)) { + case GF_RTSP_STATE_WAITING: + case GF_RTSP_STATE_WAIT_FOR_CONTROL: + return; + case GF_RTSP_STATE_INVALIDATED: + RP_SendFailure(sess, com, GF_IP_NETWORK_FAILURE); + RP_RemoveCommand(sess); + gf_rtsp_command_del(com); + sess->flags &= ~RTSP_WAIT_REPLY; + sess->command_time = 0; + return; + } + /*process*/ + com->User_Agent = RTSP_CLIENTNAME; + com->Accept_Language = RTSP_LANGUAGE; + /*if no session assigned and a session ID is valid, use it*/ + if (sess->session_id && !com->Session) + com->Session = sess->session_id; + + e = GF_OK; + /*preprocess describe before sending (always the ESD url thing)*/ + if (!strcmp(com->method, GF_RTSP_DESCRIBE)) { + com->Session = NULL; + if (!RP_PreprocessDescribe(sess, com)) { + e = GF_BAD_PARAM; + goto exit; + } + } + /*preprocess play/stop/pause before sending (aggregation)*/ + if (!strcmp(com->method, GF_RTSP_PLAY) + || !strcmp(com->method, GF_RTSP_PAUSE) + || !strcmp(com->method, GF_RTSP_TEARDOWN)) { + //command is skipped + if (!RP_PreprocessUserCom(sess, com)) { + e = GF_BAD_PARAM; + goto exit; + } + } + e = gf_rtsp_send_command(sess->session, com); + if (e) { + RP_SendFailure(sess, com, e); + RP_ProcessResponse(sess, com, e); + } else { + sess->command_time = gf_sys_clock(); + sess->flags |= RTSP_WAIT_REPLY; + } + +exit: + /*reset static strings*/ + com->User_Agent = NULL; + com->Accept_Language = NULL; + com->Session = NULL; + /*remove command*/ + if (e) { + RP_RemoveCommand(sess); + gf_rtsp_command_del(com); + sess->flags &= ~RTSP_WAIT_REPLY; + sess->command_time = 0; + } +} + + +/*locate channel - if requested remove from session*/ +RTPStream *RP_FindChannel(RTPClient *rtp, LPNETCHANNEL ch, u32 ES_ID, char *es_control, Bool remove_stream) +{ + u32 i=0; + RTPStream *st; + + while ((st = (RTPStream *)gf_list_enum(rtp->channels, &i))) { + if (ch && (st->channel==ch)) goto found; + if (ES_ID && (st->ES_ID==ES_ID)) goto found; + if (es_control && st->control) { + char *ctrl_start = strstr(es_control, st->control); + if (ctrl_start && !strcmp(ctrl_start, st->control)) goto found; + } + } + return NULL; + +found: + if (remove_stream) gf_list_rem(rtp->channels, i-1); + return st; +} + +/*locate session by control*/ +RTSPSession *RP_CheckSession(RTPClient *rtp, char *control) +{ + u32 i; + RTSPSession *sess; + if (!control) return NULL; + + if (!strcmp(control, "*")) control = (char *) gf_term_get_service_url(rtp->service); + + i=0; + while ( (sess = (RTSPSession *)gf_list_enum(rtp->sessions, &i)) ) { + if (gf_rtsp_is_my_session(sess->session, control)) return sess; + } + return NULL; +} + +RTSPSession *RP_NewSession(RTPClient *rtp, char *session_control) +{ + char *szCtrl, *szExt; + RTSPSession *tmp; + GF_RTSPSession *rtsp; + + if (!session_control) return NULL; + + /*little fix: some servers don't understand DESCRIBE URL/trackID=, so remove the trackID...*/ + szCtrl = strdup(session_control); + szExt = szCtrl ? strrchr(szCtrl, '.') : NULL; + if (szExt) { + szExt = strchr(szExt, '/'); + if (szExt) { + if (!strnicmp(szExt+1, "trackID=", 8) || !strnicmp(szExt+1, "ESID=", 5) || !strnicmp(szExt+1, "ES_ID=", 6)) szExt[0] = 0; + } + } + + rtsp = gf_rtsp_session_new(szCtrl, rtp->default_port); + free(szCtrl); + + if (!rtsp) return NULL; + + GF_SAFEALLOC(tmp, RTSPSession); + tmp->owner = rtp; + tmp->session = rtsp; + + + szCtrl = (char *)gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), "Network", "MobileIPEnabled"); + if (szCtrl && !strcmp(szCtrl, "yes")) { + char *ip = (char *)gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), "Network", "MobileIP"); + gf_rtsp_set_mobile_ip(rtsp, ip); + } + + if (rtp->transport_mode) { + gf_rtsp_set_buffer_size(rtsp, RTSP_TCP_BUFFER_SIZE); + } else { + gf_rtsp_set_buffer_size(rtsp, RTSP_BUFFER_SIZE); + } + tmp->rtsp_commands = gf_list_new(); + tmp->rtsp_rsp = gf_rtsp_response_new(); + + gf_list_add(rtp->sessions, tmp); + + return tmp; +} + +GF_Err RP_AddStream(RTPClient *rtp, RTPStream *stream, char *session_control) +{ + Bool has_aggregated_control; + char *service_name, *ctrl; + RTSPSession *in_session = RP_CheckSession(rtp, session_control); + + has_aggregated_control = 0; + if (session_control) { + //if (!strcmp(session_control, "*")) session_control = NULL; + if (session_control) has_aggregated_control = 1; + } + + /*regular setup in an established session (RTSP DESCRIBE)*/ + if (in_session) { + in_session->flags |= RTSP_AGG_CONTROL; + stream->rtsp = in_session; + gf_list_add(rtp->channels, stream); + return GF_OK; + } + + /*setup through SDP with control - assume this is RTSP and try to create a session*/ + if (stream->control) { + /*stream control is relative to main session*/ + if (strnicmp(stream->control, "rtsp://", 7) && strnicmp(stream->control, "rtspu://", 7)) { + /*locate session by control - if no control was provided for the session, use default + session*/ + if (!in_session) in_session = RP_CheckSession(rtp, session_control ? session_control : "*"); + /*none found, try to create one*/ + if (!in_session) in_session = RP_NewSession(rtp, session_control); + /*cannot add an RTSP session for this channel, check if multicast*/ +// if (!in_session && gf_rtp_is_unicast(stream->rtp_ch) ) return GF_SERVICE_ERROR; + } + /*stream control is absolute*/ + else { + in_session = RP_CheckSession(rtp, stream->control); + if (!in_session) in_session = RP_CheckSession(rtp, session_control); + if (!in_session) { + if (session_control && strstr(stream->control, session_control)) + in_session = RP_NewSession(rtp, session_control); + else + in_session = RP_NewSession(rtp, stream->control); + if (!in_session) return GF_SERVICE_ERROR; + } + /*remove session control part from channel control*/ + service_name = gf_rtsp_get_service_name(in_session->session); + ctrl = strstr(stream->control, service_name); + if (ctrl && (strlen(ctrl) != strlen(service_name)) ) { + ctrl += strlen(service_name) + 1; + service_name = strdup(ctrl); + free(stream->control); + stream->control = service_name; + } + } + } + /*no control specified, assume this is multicast*/ + else { + in_session = NULL; + } + + if (in_session) { + in_session->flags |= RTSP_AGG_CONTROL; + } else if (stream->control) { + free(stream->control); + stream->control = NULL; + } + stream->rtsp = in_session; + gf_list_add(rtp->channels, stream); + return GF_OK; +} + + +void RP_RemoveStream(RTPClient *rtp, RTPStream *ch) +{ + u32 i=0; + RTPStream *st; + gf_mx_p(rtp->mx); + while ((st = (RTPStream *)gf_list_enum(rtp->channels, &i))) { + if (st == ch) { + gf_list_rem(rtp->channels, i-1); + break; + } + } + gf_mx_v(rtp->mx); +} + +void RP_ResetSession(RTSPSession *sess, GF_Err e) +{ + GF_RTSPCommand *com; + u32 first = 1; + + //destroy command list + while (gf_list_count(sess->rtsp_commands)) { + com = (GF_RTSPCommand *)gf_list_get(sess->rtsp_commands, 0); + gf_list_rem(sess->rtsp_commands, 0); + //this destroys stacks if any +// RP_SendFailure(sess, com, first ? e : GF_OK); + gf_rtsp_command_del(com); + first = 0; + } + /*reset session state*/ + gf_rtsp_session_reset(sess->session, 1); + sess->flags &= ~RTSP_WAIT_REPLY; +} + + +void RP_DelSession(RTSPSession *sess) +{ + RP_ResetSession(sess, GF_OK); + gf_list_del(sess->rtsp_commands); + gf_rtsp_response_del(sess->rtsp_rsp); + gf_rtsp_session_del(sess->session); + if (sess->control) free(sess->control); + if (sess->session_id) free(sess->session_id); + free(sess); +} + + diff --git a/modules/rtp_in/rtp_signaling.c b/modules/rtp_in/rtp_signaling.c new file mode 100644 index 0000000..84cf824 --- /dev/null +++ b/modules/rtp_in/rtp_signaling.c @@ -0,0 +1,823 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / RTP input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "rtp_in.h" + + +Bool channel_is_valid(RTPClient *rtp, RTPStream *ch) +{ + u32 i=0; + RTPStream *st; + while ((st = (RTPStream *)gf_list_enum(rtp->channels, &i))) { + if (st == ch) return 1; + } + return 0; +} + +void RP_StopChannel(RTPStream *ch) +{ + if (!ch || !ch->rtsp) return; + + ch->flags &= ~RTP_SKIP_NEXT_COM; + //ch->status = RTP_Disconnected; + //remove interleaved + if (gf_rtp_is_interleaved(ch->rtp_ch)) { + gf_rtsp_unregister_interleave(ch->rtsp->session, gf_rtp_get_low_interleave_id(ch->rtp_ch)); + } +} + +/*this prevent sending teardown on session with running channels*/ +Bool RP_SessionActive(RTPStream *ch) +{ + RTPStream *ach; + u32 i, count; + i = count = 0; + while ((ach = (RTPStream *)gf_list_enum(ch->owner->channels, &i))) { + if (ach->rtsp != ch->rtsp) continue; + /*count only active channels*/ + if (ach->status == RTP_Running) count++; + } + return count ? 1 : 0; +} + +static void RP_QueueCommand(RTSPSession *sess, RTPStream *ch, GF_RTSPCommand *com, Bool needs_sess_id) +{ + if (needs_sess_id) { + com->Session = sess->session_id; + } + if (gf_mx_try_lock(sess->owner->mx)) { + gf_list_add(sess->rtsp_commands, com); + gf_mx_v(sess->owner->mx); + } else { + gf_list_add(sess->rtsp_commands, com); + } +} + + + +/* + channel setup functions + */ + +void RP_Setup(RTPStream *ch) +{ + u16 def_first_port; + const char *opt; + GF_RTSPCommand *com; + GF_RTSPTransport *trans; + + com = gf_rtsp_command_new(); + com->method = strdup(GF_RTSP_SETUP); + + def_first_port = 0; + opt = gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(ch->owner->service), "Streaming", "ForceFirstPort"); + if (opt) def_first_port = atoi(opt); + + opt = gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(ch->owner->service), "Streaming", "ForceMulticastIP"); + + //setup ports if unicast non interleaved or multicast + if (gf_rtp_is_unicast(ch->rtp_ch) && (ch->owner->transport_mode != 1) && !gf_rtp_is_interleaved(ch->rtp_ch) ) { + gf_rtp_set_ports(ch->rtp_ch, def_first_port); + } else if (opt) { + gf_rtp_set_ports(ch->rtp_ch, def_first_port); + } + + trans = gf_rtsp_transport_clone(gf_rtp_get_transport(ch->rtp_ch)); + + /*some servers get confused when trying to resetup on the same remote ports, so reset info*/ + trans->port_first = trans->port_last = 0; + trans->SSRC = 0; + + /*override transport: */ + /*1: multicast forced*/ + opt = gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(ch->owner->service), "Streaming", "ForceMulticastIP"); + if (opt) { + trans->IsUnicast = 0; + trans->destination = strdup(opt); + opt = gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(ch->owner->service), "Streaming", "ForceMulticastTTL"); + trans->TTL = opt ? atoi(opt) : 127; + if (trans->Profile) free(trans->Profile); + trans->Profile = strdup(GF_RTSP_PROFILE_RTP_AVP); + if (!(ch->rtsp->flags & RTSP_DSS_SERVER) ) { + trans->port_first = trans->client_port_first; + trans->port_last = trans->client_port_last; + /*this is correct but doesn't work with DSS: the server expects "client_port" to indicate + the multicast port, not "port" - this will send both*/ + //trans->client_port_first = trans->client_port_last = 0; + } + gf_rtp_setup_transport(ch->rtp_ch, trans, NULL); + } + /*2: RTP over RTSP forced*/ + else if (ch->rtsp->flags & RTSP_FORCE_INTER) { + if (trans->Profile) free(trans->Profile); + trans->Profile = strdup(GF_RTSP_PROFILE_RTP_AVP_TCP); + gf_rtp_setup_transport(ch->rtp_ch, trans, NULL); + } + + if (trans->source) { + free(trans->source); + trans->source = NULL; + } + + /*turn off interleaving in case of re-setup, some servers don't like it (we still signal it + through RTP/AVP/TCP profile so it's OK)*/ + trans->IsInterleaved = 0; + gf_list_add(com->Transports, trans); + if (strlen(ch->control)) com->ControlString = strdup(ch->control); + + com->user_data = ch; + ch->status = RTP_WaitingForAck; + + RP_QueueCommand(ch->rtsp, ch, com, 1); +} + +/*filter setup if no session (rtp only)*/ +GF_Err RP_SetupChannel(RTPStream *ch, ChannelDescribe *ch_desc) +{ + GF_Err resp; + + /*assign ES_ID of the channel*/ + if (ch_desc && !ch->ES_ID && ch_desc->ES_ID) ch->ES_ID = ch_desc->ES_ID; + + ch->status = RTP_Setup; + + /*assign channel handle if not done*/ + if (ch_desc && ch->channel) { + assert(ch->channel == ch_desc->channel); + } else if (!ch->channel) { + assert(ch_desc); + assert(ch_desc->channel); + ch->channel = ch_desc->channel; + } + + /*no session , setup for pure rtp*/ + if (!ch->rtsp) { + ch->flags |= RTP_CONNECTED; + /*init rtp*/ + resp = RP_InitStream(ch, 0), + /*send confirmation to user*/ + RP_ConfirmChannelConnect(ch, resp); + } else { + RP_Setup(ch); + } + return GF_OK; +} + +void RP_ProcessSetup(RTSPSession *sess, GF_RTSPCommand *com, GF_Err e) +{ + RTPStream *ch; + u32 i; + GF_RTSPTransport *trans; + + ch = (RTPStream *)com->user_data; + if (e) goto exit; + + switch (sess->rtsp_rsp->ResponseCode) { + case NC_RTSP_OK: + break; + case NC_RTSP_Not_Found: + e = GF_STREAM_NOT_FOUND; + goto exit; + default: + e = GF_SERVICE_ERROR; + goto exit; + } + e = GF_SERVICE_ERROR; + if (!ch) goto exit; + + /*assign session ID*/ + if (!sess->rtsp_rsp->Session) { + e = GF_SERVICE_ERROR; + goto exit; + } + if (!sess->session_id) sess->session_id = strdup(sess->rtsp_rsp->Session); + assert(!ch->session_id); + + /*transport setup: break at the first correct transport */ + i=0; + while ((trans = (GF_RTSPTransport *)gf_list_enum(sess->rtsp_rsp->Transports, &i))) { + /*copy over previous ports (hack for some servers overriding client ports)*/ + const char *opt = gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(ch->owner->service), "Streaming", "ForceClientPorts"); + if (opt && !stricmp(opt, "yes")) + gf_rtp_get_ports(ch->rtp_ch, &trans->client_port_first, &trans->client_port_last); + + e = gf_rtp_setup_transport(ch->rtp_ch, trans, gf_rtsp_get_server_name(sess->session)); + if (!e) break; + } + if (e) goto exit; + + e = RP_InitStream(ch, 0); + if (e) goto exit; + ch->status = RTP_Connected; + + //in case this is TCP channel, setup callbacks + ch->flags &= ~RTP_INTERLEAVED; + if (gf_rtp_is_interleaved(ch->rtp_ch)) { + ch->flags |= RTP_INTERLEAVED; + gf_rtsp_set_interleave_callback(sess->session, RP_DataOnTCP); + } + +exit: + /*confirm only on first connect, otherwise this is a re-SETUP of the rtsp session, not the channel*/ + if (! (ch->flags & RTP_CONNECTED) ) { + ch->flags |= RTP_CONNECTED; + RP_ConfirmChannelConnect(ch, e); + } + com->user_data = NULL; +} + + + +/* + session/channel describe functions + */ +/*filter describe commands in case of ESD URLs*/ +Bool RP_PreprocessDescribe(RTSPSession *sess, GF_RTSPCommand *com) +{ + RTPStream *ch; + ChannelDescribe *ch_desc; + /*not a channel describe*/ + if (!com->user_data) { + gf_term_on_message(sess->owner->service, GF_OK, "Connecting..."); + return 1; + } + + ch_desc = (ChannelDescribe *)com->user_data; + ch = RP_FindChannel(sess->owner, NULL, ch_desc->ES_ID, ch_desc->esd_url, 0); + if (!ch) return 1; + + /*channel has been described already, skip describe and send setup directly*/ + RP_SetupChannel(ch, ch_desc); + + if (ch_desc->esd_url) free(ch_desc->esd_url); + free(ch_desc); + return 0; +} + +/*process describe reply*/ +GF_Err RP_ProcessDescribe(RTSPSession *sess, GF_RTSPCommand *com, GF_Err e) +{ + RTPStream *ch; + ChannelDescribe *ch_desc; + + ch = NULL; + ch_desc = (ChannelDescribe *)com->user_data; + if (e) goto exit; + + switch (sess->rtsp_rsp->ResponseCode) { + //TODO handle all 3xx codes (redirections) + case NC_RTSP_Multiple_Choice: + e = ch_desc ? GF_STREAM_NOT_FOUND : GF_URL_ERROR; + goto exit; + case NC_RTSP_Not_Found: + e = GF_URL_ERROR; + goto exit; + case NC_RTSP_OK: + break; + default: + //we should have a basic error code mapping here + e = GF_SERVICE_ERROR; + goto exit; + } + + ch = NULL; + if (ch_desc) { + ch = RP_FindChannel(sess->owner, ch_desc->channel, ch_desc->ES_ID, ch_desc->esd_url, 0); + } else { + gf_term_on_message(sess->owner->service, GF_OK, "Connected"); + } + + /*error on loading SDP is done internally*/ + RP_LoadSDP(sess->owner, sess->rtsp_rsp->body, sess->rtsp_rsp->Content_Length, ch); + + if (!ch_desc) goto exit; + if (!ch) { + e = GF_STREAM_NOT_FOUND; + goto exit; + } + e = RP_SetupChannel(ch, ch_desc); + +exit: + com->user_data = NULL; + if (e) { + if (!ch_desc) { + sess->connect_error = e; + return e; + } else if (ch) { + RP_ConfirmChannelConnect(ch, e); + } else { + gf_term_on_connect(sess->owner->service, ch_desc->channel, e); + } + } + if (ch_desc) free(ch_desc); + return GF_OK; +} + +/*send describe*/ +void RP_Describe(RTSPSession *sess, char *esd_url, LPNETCHANNEL channel) +{ + const char *opt; + RTPStream *ch; + ChannelDescribe *ch_desc; + GF_RTSPCommand *com; + + /*locate the channel by URL - if we have one, this means the channel is already described + this happens when 2 ESD with URL use the same RTSP service - skip describe and send setup*/ + if (esd_url || channel) { + ch = RP_FindChannel(sess->owner, channel, 0, esd_url, 0); + if (ch) { + if (!ch->channel) ch->channel = channel; + switch (ch->status) { + case RTP_Connected: + case RTP_Running: + RP_ConfirmChannelConnect(ch, GF_OK); + return; + default: + break; + } + ch_desc = (ChannelDescribe *)malloc(sizeof(ChannelDescribe)); + ch_desc->esd_url = esd_url ? strdup(esd_url) : NULL; + ch_desc->channel = channel; + RP_SetupChannel(ch, ch_desc); + + if (esd_url) free(ch_desc->esd_url); + free(ch_desc); + return; + } + /*channel not found, send describe on service*/ + } + + /*send describe*/ + com = gf_rtsp_command_new(); + com->method = strdup(GF_RTSP_DESCRIBE); + + if (channel || esd_url) { + com->Accept = strdup("application/sdp"); + com->ControlString = esd_url ? strdup(esd_url) : NULL; + + ch_desc = (ChannelDescribe *)malloc(sizeof(ChannelDescribe)); + ch_desc->esd_url = esd_url ? strdup(esd_url) : NULL; + ch_desc->channel = channel; + + com->user_data = ch_desc; + } else { + //always accept both SDP and IOD + com->Accept = strdup("application/sdp, application/mpeg4-iod"); +// com->Accept = strdup("application/sdp"); + } + + /*need better tuning ...*/ + opt = (char *) gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(sess->owner->service), "Network", "Bandwidth"); + if (opt && !stricmp(opt, "yes")) com->Bandwidth = atoi(opt); + + RP_QueueCommand(sess, NULL, com, 0); +} + +/* + channel control functions + */ +/*remove command if session is using aggregated control*/ +Bool RP_PreprocessUserCom(RTSPSession *sess, GF_RTSPCommand *com) +{ + ChannelControl *ch_ctrl; + RTPStream *ch; + GF_Err e; + Bool skip_it; + + ch_ctrl = NULL; + if (strcmp(com->method, GF_RTSP_TEARDOWN)) ch_ctrl = (ChannelControl *)com->user_data; + if (!ch_ctrl || !ch_ctrl->ch) return 1; + ch = ch_ctrl->ch; + + if (!ch->channel || !channel_is_valid(sess->owner, ch)) { + free(ch_ctrl); + com->user_data = NULL; + return 0; + } + + assert(ch->rtsp == sess); + assert(ch->channel==ch_ctrl->com.base.on_channel); + + skip_it = 0; + if (!com->Session) { + /*re-SETUP failed*/ + if (!strcmp(com->method, GF_RTSP_PLAY) || !strcmp(com->method, GF_RTSP_PAUSE)) { + e = GF_SERVICE_ERROR; + goto err_exit; + } + /*this is a stop, no need for SessionID just skip*/ + skip_it = 1; + } + /*check if aggregation discards this command*/ + if (skip_it || ( (sess->flags & RTSP_AGG_CONTROL) && (ch->flags & RTP_SKIP_NEXT_COM) )) { + ch->flags &= ~RTP_SKIP_NEXT_COM; + gf_term_on_command(sess->owner->service, &ch_ctrl->com, GF_OK); + free(ch_ctrl); + com->user_data = NULL; + return 0; + } + return 1; + +err_exit: + gf_rtsp_reset_aggregation(ch->rtsp->session); + ch->status = RTP_Disconnected; + ch->check_rtp_time = 0; + gf_term_on_command(sess->owner->service, &ch_ctrl->com, e); + free(ch_ctrl); + com->user_data = NULL; + return 0; +} + +static void SkipCommandOnSession(RTPStream *ch) +{ + u32 i; + RTPStream *a_ch; + if (!ch || (ch->flags & RTP_SKIP_NEXT_COM) || !(ch->rtsp->flags & RTSP_AGG_CONTROL) ) return; + i=0; + while ((a_ch = (RTPStream *)gf_list_enum(ch->owner->channels, &i))) { + if ((ch == a_ch) || (a_ch->rtsp != ch->rtsp) ) continue; + a_ch->flags |= RTP_SKIP_NEXT_COM; + } +} + + +void RP_ProcessUserCommand(RTSPSession *sess, GF_RTSPCommand *com, GF_Err e) +{ + ChannelControl *ch_ctrl; + RTPStream *ch, *agg_ch; + u32 i, count; + GF_RTPInfo *info; + + + ch_ctrl = (ChannelControl *)com->user_data; + ch = ch_ctrl->ch; + if (ch) { + if (!ch->channel || !channel_is_valid(sess->owner, ch)) { + free(ch_ctrl); + com->user_data = NULL; + return; + } + + assert(ch->channel==ch_ctrl->com.base.on_channel); + } + + /*some consistency checking: on interleaved sessions, some servers do NOT reply to the + teardown. If our command is STOP just skip the error notif*/ + if (e) { + if (!strcmp(com->method, GF_RTSP_TEARDOWN)) { + goto process_reply; + } else { + /*spec is not really clear about what happens if the server doesn't support + non aggregated operations. Since this happen only on pause/play, we consider + that no error occured and wait for next play*/ + if (sess->rtsp_rsp->ResponseCode == NC_RTSP_Only_Aggregate_Operation_Allowed) { + sess->flags |= RTSP_AGG_ONLY; + sess->rtsp_rsp->ResponseCode = NC_RTSP_OK; + } else { + goto err_exit; + } + } + } + + switch (sess->rtsp_rsp->ResponseCode) { + //handle all 3xx codes (redirections) + case NC_RTSP_Method_Not_Allowed: + e = GF_NOT_SUPPORTED; + goto err_exit; + case NC_RTSP_OK: + break; + default: + //we should have a basic error code mapping here + e = GF_SERVICE_ERROR; + goto err_exit; + } + +process_reply: + + gf_term_on_command(sess->owner->service, &ch_ctrl->com, GF_OK); + + if ( (ch_ctrl->com.command_type==GF_NET_CHAN_PLAY) + || (ch_ctrl->com.command_type==GF_NET_CHAN_SET_SPEED) + || (ch_ctrl->com.command_type==GF_NET_CHAN_RESUME) ) { + + //auto-detect any aggregated control if not done yet + if (gf_list_count(sess->rtsp_rsp->RTP_Infos) > 1) { + sess->flags |= RTSP_AGG_CONTROL; + } + + //process all RTP infos + count = gf_list_count(sess->rtsp_rsp->RTP_Infos); + for (i=0;i<count; i++) { + info = (GF_RTPInfo*)gf_list_get(sess->rtsp_rsp->RTP_Infos, i); + agg_ch = RP_FindChannel(sess->owner, NULL, 0, info->url, 0); + + if (!agg_ch || (agg_ch->rtsp != sess) ) continue; + /*channel is already playing*/ + if (agg_ch->status == RTP_Running) { + // gf_rtp_set_info_rtp(agg_ch->rtp_ch, info->seq, info->rtp_time, info->ssrc); + // agg_ch->check_rtp_time = 1; + continue; + } + + /*if play/seeking we must send update RTP/NPT link*/ + if (ch_ctrl->com.command_type != GF_NET_CHAN_RESUME) { + agg_ch->check_rtp_time = 1; + } + /*this is used to discard RTP packets re-sent on resume*/ + else { + agg_ch->check_rtp_time = 2; + } + /* reset the buffers */ + RP_InitStream(agg_ch, 1); + + gf_rtp_set_info_rtp(agg_ch->rtp_ch, info->seq, info->rtp_time, info->ssrc); + agg_ch->status = RTP_Running; + + /*skip next play command on this channel if aggregated control*/ + if (ch!=agg_ch && (ch->rtsp->flags & RTSP_AGG_CONTROL) ) agg_ch->flags |= RTP_SKIP_NEXT_COM; + + + if (gf_rtp_is_interleaved(agg_ch->rtp_ch)) { + gf_rtsp_register_interleave(sess->session, + agg_ch, + gf_rtp_get_low_interleave_id(agg_ch->rtp_ch), + gf_rtp_get_hight_interleave_id(agg_ch->rtp_ch)); + } + } + /*no rtp info (just in case), no time mapped - set to 0 and specify we're not interactive*/ + if (!i) { + ch->current_start = 0.0; + ch->check_rtp_time = 1; + RP_InitStream(ch, 1); + ch->status = RTP_Running; + if (gf_rtp_is_interleaved(ch->rtp_ch)) { + gf_rtsp_register_interleave(sess->session, + ch, gf_rtp_get_low_interleave_id(ch->rtp_ch), gf_rtp_get_hight_interleave_id(ch->rtp_ch)); + } + } + ch->flags &= ~RTP_SKIP_NEXT_COM; + } else if (ch_ctrl->com.command_type == GF_NET_CHAN_PAUSE) { + if (ch) { + SkipCommandOnSession(ch); + ch->flags &= ~RTP_SKIP_NEXT_COM; + + } + } else if (ch_ctrl->com.command_type == GF_NET_CHAN_STOP) { + } + free(ch_ctrl); + com->user_data = NULL; + return; + + +err_exit: + gf_term_on_command(sess->owner->service, &ch_ctrl->com, e); + if (ch) { + ch->status = RTP_Disconnected; + gf_rtsp_reset_aggregation(ch->rtsp->session); + ch->check_rtp_time = 0; + } + free(ch_ctrl); + com->user_data = NULL; +} + +#if 0 +static void RP_FlushAndTearDown(RTSPSession *sess) +{ + GF_RTSPCommand *com; + gf_mx_p(sess->owner->mx); + + while (gf_list_count(sess->rtsp_commands)) { + com = (GF_RTSPCommand *)gf_list_get(sess->rtsp_commands, 0); + gf_list_rem(sess->rtsp_commands, 0); + gf_rtsp_command_del(com); + } + if (sess->flags & RTSP_WAIT_REPLY) { + GF_Err e; + while (1) { + e = gf_rtsp_get_response(sess->session, sess->rtsp_rsp); + if (e!= GF_IP_NETWORK_EMPTY) break; + } + sess->flags &= ~RTSP_WAIT_REPLY; + } + gf_mx_v(sess->owner->mx); + + + /*no private stack on teardown - shutdown now*/ + com = gf_rtsp_command_new(); + com->method = strdup(GF_RTSP_TEARDOWN); + RP_QueueCommand(sess, NULL, com, 1); +} +#endif + + +void RP_UserCommand(RTSPSession *sess, RTPStream *ch, GF_NetworkCommand *command) +{ + RTPStream *a_ch; + ChannelControl *ch_ctrl; + u32 i; + Bool needs_setup = 0; + GF_RTSPCommand *com; + GF_RTSPRange *range; + + switch (command->command_type) { + case GF_NET_CHAN_PLAY: + case GF_NET_CHAN_RESUME: + needs_setup = 1; + break; + case GF_NET_CHAN_PAUSE: + case GF_NET_CHAN_STOP: + break; + default: + gf_term_on_command(sess->owner->service, command, GF_NOT_SUPPORTED); + return; + } + + + /*we may need to re-setup stream/session*/ + if (needs_setup) { + if (ch->status == RTP_Disconnected) { + if (sess->flags & RTSP_AGG_CONTROL) { + i=0; + while ((a_ch = (RTPStream *)gf_list_enum(sess->owner->channels, &i))) { + if (a_ch->rtsp != sess) continue; + if (a_ch->status == RTP_Disconnected) + RP_Setup(a_ch); + } + } else { + RP_Setup(ch); + } + } + } + + com = gf_rtsp_command_new(); + range = NULL; + + if ( (command->command_type==GF_NET_CHAN_PLAY) || (command->command_type==GF_NET_CHAN_RESUME) ) { + + range = gf_rtsp_range_new(); + range->start = ch->range_start; + range->end = ch->range_end; + + com->method = strdup(GF_RTSP_PLAY); + + /*specify pause range on resume - this is not mandatory but most servers need it*/ + if (command->command_type==GF_NET_CHAN_RESUME) { + range->start = ch->current_start; + + ch->stat_start_time -= ch->stat_stop_time; + ch->stat_start_time += gf_sys_clock(); + ch->stat_stop_time = 0; + } else { + range->start = ch->range_start; + if (command->play.start_range>=0) range->start += command->play.start_range; + range->end = ch->range_start; + if (command->play.end_range >=0) { + range->end += command->play.end_range; + if (range->end > ch->range_end) range->end = ch->range_end; + } + + ch->stat_start_time = gf_sys_clock(); + ch->stat_stop_time = 0; + } + /*if aggregated the command is sent once, so store info at session level*/ + if (ch->flags & RTP_SKIP_NEXT_COM) { + ch->current_start = ch->rtsp->last_range; + } else { + ch->rtsp->last_range = range->start; + ch->current_start = range->start; + } + /*some RTSP servers don't accept Range=npt:0.0- (for ex, broadcast only...), so skip it if: + - a range was given in initial describe + - the command is not a RESUME + */ + if (!(ch->flags & RTP_HAS_RANGE) && (command->command_type != GF_NET_CHAN_RESUME) ) { + gf_rtsp_range_del(range); + com->Range = NULL; + } else { + com->Range = range; + } + + if (sess->flags & RTSP_AGG_CONTROL) + SkipCommandOnSession(ch); + else if (strlen(ch->control)) + com->ControlString = strdup(ch->control); + + if (RP_SessionActive(ch)) { + if (!com->ControlString && ch->control) com->ControlString = strdup(ch->control); + } else { + if (com->ControlString) { + free(com->ControlString); + com->ControlString=NULL; + } + } + + } else if (command->command_type==GF_NET_CHAN_PAUSE) { + com->method = strdup(GF_RTSP_PAUSE); + if (ch) { + range = gf_rtsp_range_new(); + /*update current time*/ + ch->current_start += gf_rtp_get_current_time(ch->rtp_ch); + ch->stat_stop_time = gf_sys_clock(); + range->start = ch->current_start; + range->end = -1.0; + com->Range = range; + } + } + else if (command->command_type==GF_NET_CHAN_STOP) { + ch->current_start = 0; + ch->stat_stop_time = gf_sys_clock(); + + ch->status = RTP_Connected; + RP_InitStream(ch, 1); + + /*if server only support aggregation on pause, skip the command or issue + a teardown if last active stream*/ + if (ch->rtsp->flags & RTSP_AGG_ONLY) { + RP_StopChannel(ch); + if (com) gf_rtsp_command_del(com); + if (!RP_SessionActive(ch)) + RP_Teardown(sess, ch); + return; + } + /* otherwise send a PAUSE on the stream */ + else { + range = gf_rtsp_range_new(); + range->start = 0; + range->end = -1; + com->method = strdup(GF_RTSP_PAUSE); + com->Range = range; + /*only pause the specified stream*/ + if (ch->control) com->ControlString = strdup(ch->control); + } + } else { + gf_term_on_command(sess->owner->service, command, GF_NOT_SUPPORTED); + gf_rtsp_command_del(com); + return; + } + + ch_ctrl = (ChannelControl *)malloc(sizeof(ChannelControl)); + ch_ctrl->ch = ch; + memcpy(&ch_ctrl->com, command, sizeof(GF_NetworkCommand)); + com->user_data = ch_ctrl; + + RP_QueueCommand(sess, ch, com, 1); + return; +} + + +/* + session/channel teardown functions + */ +void RP_ProcessTeardown(RTSPSession *sess, GF_RTSPCommand *com, GF_Err e) +{ + RTPStream *ch = (RTPStream *)com->user_data; + if (ch) { + if (ch->session_id) free(ch->session_id); + ch->session_id = NULL; + } else { + if (sess->session_id) free(sess->session_id); + sess->session_id = NULL; + } +} + +void RP_Teardown(RTSPSession *sess, RTPStream *ch) +{ + GF_RTSPCommand *com; + + /*we need a session id*/ + if (!sess->session_id) return; + /*ignore teardown on channels*/ + if ((sess->flags & RTSP_AGG_CONTROL) && ch) return; + + com = gf_rtsp_command_new(); + com->method = strdup(GF_RTSP_TEARDOWN); + /*this only works in RTSP2*/ + if (ch && ch->control) { + com->ControlString = strdup(ch->control); + com->user_data = ch; + } + + RP_QueueCommand(sess, ch, com, 1); +} + diff --git a/modules/rtp_in/rtp_stream.c b/modules/rtp_in/rtp_stream.c new file mode 100644 index 0000000..f22cfc4 --- /dev/null +++ b/modules/rtp_in/rtp_stream.c @@ -0,0 +1,427 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / RTP input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "rtp_in.h" +#include <gpac/internal/ietf_dev.h> + + +void RP_ConfirmChannelConnect(RTPStream *ch, GF_Err e) +{ + GF_NetworkCommand com; + + /*in case the channel has been disconnected while SETUP was issued&processed. We also could + clean up the command stack*/ + if (!ch->channel) return; + + gf_term_on_connect(ch->owner->service, ch->channel, e); + if (e != GF_OK || !ch->rtp_ch) return; + + /*success, overwrite SL config*/ + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.command_type = GF_NET_CHAN_RECONFIG; + com.base.on_channel = ch->channel; + + gf_rtp_depacketizer_get_slconfig(ch->depacketizer, &com.cfg.sl_config); + /*reconfig*/ + gf_term_on_command(ch->owner->service, &com, GF_OK); + + /*ISMACryp config*/ + if (ch->depacketizer->flags & GF_RTP_HAS_ISMACRYP) { + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.base.on_channel = ch->channel; + com.command_type = GF_NET_CHAN_DRM_CFG; + com.drm_cfg.scheme_type = ch->depacketizer->isma_scheme; + com.drm_cfg.scheme_version = 1; + /*not transported in SDP!!!*/ + com.drm_cfg.scheme_uri = NULL; + com.drm_cfg.kms_uri = ch->depacketizer->key; + gf_term_on_command(ch->owner->service, &com, GF_OK); + } +} + +GF_Err RP_InitStream(RTPStream *ch, Bool ResetOnly) +{ + gf_rtp_depacketizer_reset(ch->depacketizer, !ResetOnly); + + if (!ResetOnly) { + const char *ip_ifce = NULL; + u32 reorder_size = 0; + if (!ch->owner->transport_mode) { + const char *sOpt = gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(ch->owner->service), "Streaming", "ReorderSize"); + if (sOpt) reorder_size = atoi(sOpt); + else reorder_size = 10; + + + ip_ifce = gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(ch->owner->service), "Network", "DefaultMCastInterface"); + if (!ip_ifce) { + const char *mob_on = gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(ch->owner->service), "Network", "MobileIPEnabled"); + if (mob_on && !strcmp(mob_on, "yes")) + ip_ifce = gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(ch->owner->service), "Network", "MobileIP"); + } + } + return gf_rtp_initialize(ch->rtp_ch, RTP_BUFFER_SIZE, 0, 0, reorder_size, 200, (char *)ip_ifce); + } + //just reset the sockets + gf_rtp_reset_buffers(ch->rtp_ch); + return GF_OK; +} + +void RP_DeleteStream(RTPStream *ch) +{ + if (ch->rtsp) { + if ((ch->status == RTP_Running)) { + RP_Teardown(ch->rtsp, ch); + ch->status = RTP_Disconnected; + } + RP_RemoveStream(ch->owner, ch); + } else { + RP_FindChannel(ch->owner, ch->channel, 0, NULL, 1); + } + + if (ch->depacketizer) gf_rtp_depacketizer_del(ch->depacketizer); + if (ch->rtp_ch) gf_rtp_del(ch->rtp_ch); + if (ch->control) free(ch->control); + if (ch->session_id) free(ch->session_id); + free(ch); +} + + +static void rtp_sl_packet_cbk(void *udta, char *payload, u32 size, GF_SLHeader *hdr, GF_Err e) +{ + RTPStream *ch = (RTPStream *)udta; + if (ch->owner->first_packet_drop && (hdr->packetSequenceNumber >= ch->owner->first_packet_drop) ) { + if ( (hdr->packetSequenceNumber - ch->owner->first_packet_drop) % ch->owner->frequency_drop) + gf_term_on_sl_packet(ch->owner->service, ch->channel, payload, size, hdr, e); + } else { + gf_term_on_sl_packet(ch->owner->service, ch->channel, payload, size, hdr, e); + } +} + + +RTPStream *RP_NewStream(RTPClient *rtp, GF_SDPMedia *media, GF_SDPInfo *sdp, RTPStream *input_stream) +{ + GF_RTSPRange *range; + RTPStream *tmp; + GF_RTPMap *map; + u32 i, ESID, ssrc, rtp_seq, rtp_time; + Bool force_bcast = 0; + Double Start, End; + Float CurrentTime; + u32 s_port_first, s_port_last; + GF_X_Attribute *att; + char *ctrl; + GF_SDPConnection *conn; + GF_RTSPTransport trans; + + //extract all relevant info from the GF_SDPMedia + Start = 0.0; + End = -1.0; + CurrentTime = 0.0f; + ESID = 0; + ctrl = NULL; + range = NULL; + s_port_first = s_port_last = 0; + ssrc = rtp_seq = rtp_time = 0; + i=0; + while ((att = (GF_X_Attribute*)gf_list_enum(media->Attributes, &i))) { + if (!stricmp(att->Name, "control")) ctrl = att->Value; + else if (!stricmp(att->Name, "gpac-broadcast")) force_bcast = 1; + else if (!stricmp(att->Name, "mpeg4-esid") && att->Value) ESID = atoi(att->Value); + else if (!stricmp(att->Name, "range") && !range) range = gf_rtsp_range_parse(att->Value); + else if (!stricmp(att->Name, "x-stream-state") ) { + sscanf(att->Value, "server-port=%d-%d;ssrc=%X;npt=%g;seq=%d;rtptime=%d", + &s_port_first, &s_port_last, &ssrc, &CurrentTime, &rtp_seq, &rtp_time); + } + } + + if (range) { + Start = range->start; + End = range->end; + gf_rtsp_range_del(range); + } + + /*check connection*/ + conn = sdp->c_connection; + if (!conn) conn = (GF_SDPConnection*)gf_list_get(media->Connections, 0); + + if (!conn) { + /*RTSP RFC recommends an empty "c= " line but some server don't send it. Use session info (o=)*/ + if (!sdp->o_net_type || !sdp->o_add_type || strcmp(sdp->o_net_type, "IN")) return NULL; + if (strcmp(sdp->o_add_type, "IP4") && strcmp(sdp->o_add_type, "IP6")) return NULL; + } else { + if (strcmp(conn->net_type, "IN")) return NULL; + if (strcmp(conn->add_type, "IP4") && strcmp(conn->add_type, "IP6")) return NULL; + } + /*do we support transport*/ + if (strcmp(media->Profile, "RTP/AVP") && strcmp(media->Profile, "RTP/AVP/TCP") + && strcmp(media->Profile, "RTP/SAVP") && strcmp(media->Profile, "RTP/SAVP/TCP") + ) return NULL; + + /*check RTP map. For now we only support 1 RTPMap*/ + if (media->fmt_list || (gf_list_count(media->RTPMaps) > 1)) return NULL; + + /*check payload type*/ + map = (GF_RTPMap*)gf_list_get(media->RTPMaps, 0); + + /*this is an ESD-URL setup, we likely have namespace conflicts so overwrite given ES_ID + by the app one (client side), but keep control (server side) if provided*/ + if (input_stream) { + ESID = input_stream->ES_ID; + if (!ctrl) ctrl = input_stream->control; + tmp = input_stream; + } else { + tmp = RP_FindChannel(rtp, NULL, ESID, NULL, 0); + if (tmp) return NULL; + + GF_SAFEALLOC(tmp, RTPStream); + tmp->owner = rtp; + } + + /*create an RTP channel*/ + tmp->rtp_ch = gf_rtp_new(); + if (ctrl) tmp->control = strdup(ctrl); + tmp->ES_ID = ESID; + + memset(&trans, 0, sizeof(GF_RTSPTransport)); + trans.Profile = media->Profile; + trans.source = conn ? conn->host : sdp->o_address; + trans.IsUnicast = gf_sk_is_multicast_address(trans.source) ? 0 : 1; + if (!trans.IsUnicast) { + trans.port_first = media->PortNumber; + trans.port_last = media->PortNumber + 1; + trans.TTL = conn ? conn->TTL : 0; + } else { + trans.client_port_first = media->PortNumber; + trans.client_port_last = media->PortNumber + 1; + trans.port_first = s_port_first; + trans.port_last = s_port_last; + } + + if (gf_rtp_setup_transport(tmp->rtp_ch, &trans, NULL) != GF_OK) { + RP_DeleteStream(tmp); + return NULL; + } + /*setup depacketizer*/ + tmp->depacketizer = gf_rtp_depacketizer_new(media, rtp_sl_packet_cbk, tmp); + if (!tmp->depacketizer) { + RP_DeleteStream(tmp); + return NULL; + } + /*setup channel*/ + gf_rtp_setup_payload(tmp->rtp_ch, map); + +// tmp->status = NM_Disconnected; + + ctrl = (char *) gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), "Streaming", "DisableRTCP"); + if (!ctrl || stricmp(ctrl, "yes")) tmp->flags |= RTP_ENABLE_RTCP; + + /*setup NAT keep-alive*/ + ctrl = (char *) gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), "Streaming", "NATKeepAlive"); + if (ctrl) gf_rtp_enable_nat_keepalive(tmp->rtp_ch, atoi(ctrl)); + + tmp->range_start = Start; + tmp->range_end = End; + if (End != -1.0) tmp->flags |= RTP_HAS_RANGE; + + if (force_bcast) tmp->flags |= RTP_FORCE_BROADCAST; + + if (s_port_first) { + tmp->current_start = (Double) CurrentTime; + tmp->check_rtp_time = 1; + gf_rtp_set_info_rtp(tmp->rtp_ch, rtp_seq, rtp_time, ssrc); + tmp->status = RTP_SessionResume; + } + return tmp; +} + + + + +void RP_ProcessRTP(RTPStream *ch, char *pck, u32 size) +{ + GF_NetworkCommand com; + GF_Err e; + GF_RTPHeader hdr; + u32 PayloadStart; + ch->rtp_bytes += size; + + /*first decode RTP*/ + e = gf_rtp_decode_rtp(ch->rtp_ch, pck, size, &hdr, &PayloadStart); + + /*corrupted or NULL data*/ + if (e || (PayloadStart >= size)) { + //gf_term_on_sl_packet(ch->owner->service, ch->channel, NULL, 0, NULL, GF_CORRUPTED_DATA); + return; + } + + /*if we must notify some timing, do it now. If the channel has no range, this should NEVER be called*/ + if (ch->check_rtp_time /*&& gf_rtp_is_active(ch->rtp_ch)*/) { + Double ch_time; + + /*it may happen that we still receive packets from a previous "play" request. If this is the case, + filter until we reach the indicated rtptime*/ + if (ch->rtp_ch->rtp_time + && (ch->rtp_ch->rtp_first_SN > hdr.SequenceNumber) + && (ch->rtp_ch->rtp_time < hdr.TimeStamp) + ) { + GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTP] Rejecting too early packet (TS %d vs signaled rtp time %d - diff %d ms)\n", + hdr.TimeStamp, ch->rtp_ch->rtp_time, ((hdr.TimeStamp - ch->rtp_ch->rtp_time)*1000) / ch->rtp_ch->TimeScale)); + return; + } + + ch_time = gf_rtp_get_current_time(ch->rtp_ch); + + /*this is the first packet on the channel (no PAUSE)*/ + if (ch->check_rtp_time == 1) { + /*Note: in a SEEK with RTSP, the rtp-info time given by the server is + the rtp time of the desired range. But the server may (and should) send from + the previous I frame on video, so the time of the first rtp packet after + a SEEK can actually be less than CurrentStart. We don't drop these + packets in order to see the maximum video. We could drop it, this would mean + wait for next RAP...*/ + + memset(&com, 0, sizeof(com)); + com.command_type = GF_NET_CHAN_MAP_TIME; + com.base.on_channel = ch->channel; + com.map_time.media_time = ch->current_start + ch_time; + com.map_time.timestamp = hdr.TimeStamp; + com.map_time.reset_buffers = 1; + gf_term_on_command(ch->owner->service, &com, GF_OK); + + GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTP] Mapping RTP Time seq %d TS %d - rtp info seq %d TS %d\n", + hdr.SequenceNumber, hdr.TimeStamp, ch->rtp_ch->rtp_first_SN, ch->rtp_ch->rtp_time + )); + +// if (ch->depacketizer->payt==GF_RTP_PAYT_H264_AVC) ch->depacketizer->flags |= GF_RTP_AVC_WAIT_RAP; + } + /*this is RESUME on channel, filter packet based on time (darwin seems to send + couple of packet before) + do not fetch if we're below 10 ms or <0, because this means we already have + this packet - as the PAUSE is issued with the RTP currentTime*/ + else if (ch_time <= 0.021) { + return; + } + ch->check_rtp_time = 0; + } + + gf_rtp_depacketizer_process(ch->depacketizer, &hdr, pck + PayloadStart, size - PayloadStart); + + /*last check: signal EOS if we're close to end range in case the server do not send RTCP BYE*/ + if ((ch->flags & RTP_HAS_RANGE) && !(ch->flags & RTP_EOS) ) { + /*also check last CTS*/ + Double ts = (Double) ((u32) ch->depacketizer->sl_hdr.compositionTimeStamp - hdr.TimeStamp); + ts /= gf_rtp_get_clockrate(ch->rtp_ch); + if (ABSDIFF(ch->range_end, (ts + ch->current_start + gf_rtp_get_current_time(ch->rtp_ch)) ) < 0.2) { + ch->flags |= RTP_EOS; + ch->stat_stop_time = gf_sys_clock(); + gf_term_on_sl_packet(ch->owner->service, ch->channel, NULL, 0, NULL, GF_EOS); + } + } +} + +void RP_ProcessRTCP(RTPStream *ch, char *pck, u32 size) +{ + GF_Err e; + + if (ch->status == RTP_Connected) return; + + ch->rtcp_bytes += size; + + e = gf_rtp_decode_rtcp(ch->rtp_ch, pck, size); + + if (e == GF_EOS) { + ch->flags |= RTP_EOS; + ch->stat_stop_time = gf_sys_clock(); + gf_term_on_sl_packet(ch->owner->service, ch->channel, NULL, 0, NULL, GF_EOS); + } +} + +GF_Err RP_DataOnTCP(GF_RTSPSession *sess, void *cbk, char *buffer, u32 bufferSize, Bool IsRTCP) +{ + RTPStream *ch = (RTPStream *) cbk; + if (!ch) return GF_OK; + if (IsRTCP) { + RP_ProcessRTCP(ch, buffer, bufferSize); + } else { + RP_ProcessRTP(ch, buffer, bufferSize); + } + return GF_OK; +} + + +static GF_Err SendTCPData(void *par, char *pck, u32 pck_size) +{ + return GF_OK; +} + + +void RP_ReadStream(RTPStream *ch) +{ + u32 size, tot_size; + + if (!ch->rtp_ch) return; + + /*NOTE: A weird bug on windows wrt to select(): if both RTP and RTCP are in the same loop + there is a hudge packet drop on RTP. We therefore split RTP and RTCP reading, this is not a big + deal as the RTCP traffic is far less than RTP, and we should never have more than one RTCP + packet reading per RTP reading loop + NOTE2: a better implementation would be to use select() to get woken up... + */ + + tot_size = 0; + while (1) { + size = gf_rtp_read_rtp(ch->rtp_ch, ch->buffer, RTP_BUFFER_SIZE); + if (!size) break; + tot_size += size; + RP_ProcessRTP(ch, ch->buffer, size); + } + + while (1) { + size = gf_rtp_read_rtcp(ch->rtp_ch, ch->buffer, RTP_BUFFER_SIZE); + if (!size) break; + tot_size += size; + RP_ProcessRTCP(ch, ch->buffer, size); + } + + /*and send the report*/ + if (ch->flags & RTP_ENABLE_RTCP) gf_rtp_send_rtcp_report(ch->rtp_ch, SendTCPData, ch); + + if (tot_size) ch->owner->udp_time_out = 0; + + /*detect timeout*/ + if (ch->owner->udp_time_out) { + if (!ch->last_udp_time) { + ch->last_udp_time = gf_sys_clock(); + } else { + u32 diff = gf_sys_clock() - ch->last_udp_time; + if (diff >= ch->owner->udp_time_out) { + char szMessage[1024]; + sprintf(szMessage, "No data received in %d ms", diff); + gf_term_on_message(ch->owner->service, GF_IP_UDP_TIMEOUT, szMessage); + ch->status = RTP_Unavailable; + } + } + } +} + diff --git a/modules/rtp_in/sdp_fetch.c b/modules/rtp_in/sdp_fetch.c new file mode 100644 index 0000000..455f3e9 --- /dev/null +++ b/modules/rtp_in/sdp_fetch.c @@ -0,0 +1,179 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / RTP input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "rtp_in.h" + + +void RP_SDPFromData(RTPClient *rtp, char *s_url, RTPStream *stream) +{ + char *url; + char buf[2000]; + u32 size; + + url = strstr(s_url, ","); + if (!url) { + gf_term_on_connect(rtp->service, NULL, GF_URL_ERROR); + return; + } + url += 1; + if (strstr(url, ";base64")) { + //decode + size = gf_base64_decode(url, strlen(url), buf, 2000); + buf[size] = 0; + url = buf; + } + RP_LoadSDP(rtp, url, strlen(url), stream); +} + +void RP_SDPFromFile(RTPClient *rtp, char *file_name, RTPStream *stream) +{ + FILE *_sdp; + char *sdp_buf; + u32 sdp_size; + + sdp_buf = NULL; + + if (file_name && strstr(file_name, "file://")) file_name += strlen("file://"); + if (!file_name || !(_sdp = fopen(file_name, "rt")) ) { + gf_term_on_connect(rtp->service, NULL, GF_URL_ERROR); + return; + } + + fseek(_sdp, 0, SEEK_END); + sdp_size = ftell(_sdp); + fseek(_sdp, 0, SEEK_SET); + sdp_buf = (char*)malloc(sdp_size); + fread(sdp_buf, sdp_size, 1, _sdp); + RP_LoadSDP(rtp, sdp_buf, sdp_size, stream); + + fclose(_sdp); + free(sdp_buf); +} + +void SDP_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + RTPClient *rtp = (RTPClient *)cbk; + SDPFetch *sdp = rtp->sdp_temp; + + gf_term_download_update_stats(rtp->dnload); + + e = param->error; + switch (param->msg_type) { + case GF_NETIO_GET_METHOD: + if (sdp->original_url) + param->name = "POST"; + return; + case GF_NETIO_GET_CONTENT: + if (sdp->original_url) { + char szBody[4096], *opt; + opt = (char *) gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), "Network", "MobileIP"); + sprintf(szBody, "ipadd\n%s\n\nurl\n%s\n\n", opt, sdp->original_url); + param->data = szBody; + param->size = strlen(szBody); + } + return; + case GF_NETIO_DATA_TRANSFERED: + if (sdp->original_url) { + u32 sdp_size; + gf_dm_sess_get_stats(rtp->dnload, NULL, NULL, &sdp_size, NULL, NULL, NULL); + if (!sdp_size) + break; + } + + { + const char *szFile = gf_dm_sess_get_cache_name(rtp->dnload); + if (!szFile) { + e = GF_SERVICE_ERROR; + } else { + e = GF_OK; + RP_SDPFromFile(rtp, (char *) szFile, sdp->chan); + free(sdp->remote_url); + if (sdp->original_url) free(sdp->original_url); + free(sdp); + rtp->sdp_temp = NULL; + return; + } + } + default: + if (e == GF_OK) return; + } + + if (sdp->original_url) { + char *url = sdp->original_url; + free(sdp->remote_url); + free(sdp); + rtp->sdp_temp = NULL; + gf_term_on_message(rtp->service, e, "Error fetching session state - restarting"); + RP_ConnectServiceEx(gf_term_get_service_interface(rtp->service), rtp->service, url, 1); + free(url); + return; + } + + /*error*/ + if (sdp->chan) { + gf_term_on_connect(rtp->service, sdp->chan->channel, e); + } else { + gf_term_on_connect(rtp->service, NULL, e); + rtp->sdp_temp = NULL; + } + free(sdp->remote_url); + if (sdp->original_url) free(sdp->original_url); + free(sdp); + rtp->sdp_temp = NULL; +} + +void RP_FetchSDP(RTPClient *rtp, char *url, RTPStream *stream, char *original_url) +{ + u32 flags = 0; + SDPFetch *sdp; + /*if local URL get file*/ + if (strstr(url, "data:application/sdp")) { + RP_SDPFromData(rtp, url, stream); + return; + } + if (!strnicmp(url, "file://", 7) || !strstr(url, "://")) { + RP_SDPFromFile(rtp, url, stream); + return; + } + + sdp = (SDPFetch*)malloc(sizeof(SDPFetch)); + memset(sdp, 0, sizeof(SDPFetch)); + sdp->client = rtp; + sdp->remote_url = strdup(url); + sdp->chan = stream; + if (original_url) { + sdp->original_url = strdup(original_url); + } + + /*otherwise setup download*/ + if (rtp->dnload) gf_term_download_del(rtp->dnload); + rtp->dnload = NULL; + + rtp->sdp_temp = sdp; + rtp->dnload = gf_term_download_new(rtp->service, url, flags, SDP_NetIO, rtp); + if (!rtp->dnload) gf_term_on_connect(rtp->service, NULL, GF_NOT_SUPPORTED); + /*service confirm is done once fetched*/ +} + diff --git a/modules/rtp_in/sdp_load.c b/modules/rtp_in/sdp_load.c new file mode 100644 index 0000000..3318114 --- /dev/null +++ b/modules/rtp_in/sdp_load.c @@ -0,0 +1,553 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / RTP input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "rtp_in.h" +#include <gpac/internal/ietf_dev.h> + + +GF_Err RP_SetupSDP(RTPClient *rtp, GF_SDPInfo *sdp, RTPStream *stream) +{ + GF_Err e; + GF_SDPMedia *media; + Double Start, End; + u32 i; + char *sess_ctrl, *session_id, *url; + GF_X_Attribute *att; + GF_RTSPRange *range; + RTPStream *ch; + RTSPSession *migrate_sess = NULL; + + Start = 0.0; + End = -1.0; + + sess_ctrl = NULL; + range = NULL; + session_id = url = NULL; + + i=0; + while ((att = (GF_X_Attribute*)gf_list_enum(sdp->Attributes, &i))) { + //session-level control string. Keep it in the current session if any + if (!strcmp(att->Name, "control") && att->Value) sess_ctrl = att->Value; + //NPT range only for now + else if (!strcmp(att->Name, "range") && !range) range = gf_rtsp_range_parse(att->Value); + /*session migration*/ + else if (!strcmp(att->Name, "x-session-name")) url = att->Value; + else if (!strcmp(att->Name, "x-session-id")) session_id = att->Value; + } + if (range) { + Start = range->start; + End = range->end; + gf_rtsp_range_del(range); + } + + if (url) { + migrate_sess = RP_NewSession(rtp, url); + if (migrate_sess && session_id) { + migrate_sess->session_id = strdup(session_id); + } + sess_ctrl = url; + } + + //setup all streams + i=0; + while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) { + ch = RP_NewStream(rtp, media, sdp, stream); + //do not generate error if the channel is not created, just assume + //1 - this is not an MPEG-4 configured channel -> not needed + //2 - this is a 2nd describe and the channel was already created + if (!ch) continue; + + e = RP_AddStream(rtp, ch, sess_ctrl); + if (e) { + RP_DeleteStream(ch); + return e; + } + + if (!(ch->flags & RTP_HAS_RANGE)) { + ch->range_start = Start; + ch->range_end = End; + if (End > 0) ch->flags |= RTP_HAS_RANGE; + } + + /*force interleaving whenever needed*/ + if (ch->rtsp) { + switch (ch->depacketizer->sl_map.StreamType) { + case GF_STREAM_VISUAL: + case GF_STREAM_AUDIO: + if ((rtp->transport_mode==1) && ! (ch->rtsp->flags & RTSP_FORCE_INTER) ) { + gf_rtsp_set_buffer_size(ch->rtsp->session, RTSP_TCP_BUFFER_SIZE); + ch->rtsp->flags |= RTSP_FORCE_INTER; + } + break; + default: + if (rtp->transport_mode && ! (ch->rtsp->flags & RTSP_FORCE_INTER) ) { + gf_rtsp_set_buffer_size(ch->rtsp->session, RTSP_TCP_BUFFER_SIZE); + ch->rtsp->flags |= RTSP_FORCE_INTER; + } + break; + } + } + + } + return GF_OK; +} + +/*load iod from data:application/mpeg4-iod;base64*/ +GF_Err RP_SDPLoadIOD(RTPClient *rtp, char *iod_str) +{ + char buf[2000]; + u32 size; + + if (rtp->session_desc) return GF_SERVICE_ERROR; + /*the only IOD format we support*/ + iod_str += 1; + if (!strnicmp(iod_str, "data:application/mpeg4-iod;base64", strlen("data:application/mpeg4-iod;base64"))) { + char *buf64; + u32 size64; + + buf64 = strstr(iod_str, ","); + if (!buf64) return GF_URL_ERROR; + buf64 += 1; + size64 = strlen(buf64) - 1; + + size = gf_base64_decode(buf64, size64, buf, 2000); + if (!size) return GF_SERVICE_ERROR; + } else if (!strnicmp(iod_str, "data:application/mpeg4-iod;base16", strlen("data:application/mpeg4-iod;base16"))) { + char *buf16; + u32 size16; + + buf16 = strstr(iod_str, ","); + if (!buf16) return GF_URL_ERROR; + buf16 += 1; + size16 = strlen(buf16) - 1; + + size = gf_base16_decode(buf16, size16, buf, 2000); + if (!size) return GF_SERVICE_ERROR; + } else { + return GF_NOT_SUPPORTED; + } + + gf_odf_desc_read(buf, size, &rtp->session_desc); + return GF_OK; +} + + +static u32 get_stream_type_from_hint(u32 ht) +{ + switch (ht) { + case GF_MEDIA_OBJECT_VIDEO: return GF_STREAM_VISUAL; + case GF_MEDIA_OBJECT_AUDIO: return GF_STREAM_AUDIO; + case GF_MEDIA_OBJECT_TEXT: return GF_STREAM_TEXT; + default: return 0; + } +} + +static GF_ObjectDescriptor *RP_GetChannelOD(RTPStream *ch, u32 ch_idx) +{ + GF_ESD *esd; + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + + if (!ch->ES_ID) ch->ES_ID = ch_idx + 1; + od->objectDescriptorID = ch->ES_ID; + esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = gf_rtp_get_clockrate(ch->rtp_ch); + esd->slConfig->useRandomAccessPointFlag = 1; + esd->slConfig->useTimestampsFlag = 1; + esd->ESID = ch->ES_ID; + esd->OCRESID = 0; + + esd->decoderConfig->streamType = ch->depacketizer->sl_map.StreamType; + esd->decoderConfig->objectTypeIndication = ch->depacketizer->sl_map.ObjectTypeIndication; + if (ch->depacketizer->sl_map.config) { + esd->decoderConfig->decoderSpecificInfo->data = (char*)malloc(sizeof(char) * ch->depacketizer->sl_map.configSize); + memcpy(esd->decoderConfig->decoderSpecificInfo->data, ch->depacketizer->sl_map.config, sizeof(char) * ch->depacketizer->sl_map.configSize); + esd->decoderConfig->decoderSpecificInfo->dataLength = ch->depacketizer->sl_map.configSize; + } + gf_list_add(od->ESDescriptors, esd); + return od; +} + +GF_Descriptor *RP_EmulateIOD(RTPClient *rtp, const char *sub_url) +{ + GF_ObjectDescriptor *the_od; + RTPStream *a_str, *ch; + u32 i; + + if (rtp->media_type==GF_MEDIA_OBJECT_INTERACT) return NULL; + if (rtp->media_type==GF_MEDIA_OBJECT_UPDATES) return NULL; + + /*single object generation*/ + a_str = NULL; + if (sub_url || ((rtp->media_type != GF_MEDIA_OBJECT_SCENE) && (rtp->media_type != GF_MEDIA_OBJECT_UNDEF)) ) { + i=0; + while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) { + if (ch->depacketizer->sl_map.StreamType != get_stream_type_from_hint(rtp->media_type)) continue; + + if (!sub_url || (ch->control && strstr(sub_url, ch->control)) ) { + the_od = RP_GetChannelOD(ch, i-1); + if (!the_od) continue; + return (GF_Descriptor *) the_od; + } + if (!a_str) a_str = ch; + } + if (a_str) { + the_od = RP_GetChannelOD(a_str, gf_list_find(rtp->channels, a_str) ); + return (GF_Descriptor *) the_od; + } + return NULL; + } + return NULL; +} + + +void RP_SetupObjects(RTPClient *rtp) +{ + GF_ObjectDescriptor *od; + RTPStream *ch; + u32 i; + + /*add everything*/ + i=0; + while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) { + if (ch->control && !strnicmp(ch->control, "data:", 5)) continue; + + if (!rtp->media_type) { + od = RP_GetChannelOD(ch, i); + if (!od) continue; + gf_term_add_media(rtp->service, (GF_Descriptor*)od, 1); + } else if (rtp->media_type==ch->depacketizer->sl_map.StreamType) { + od = RP_GetChannelOD(ch, i); + if (!od) continue; + gf_term_add_media(rtp->service, (GF_Descriptor*)od, 1); + rtp->media_type = 0; + break; + } + } + gf_term_add_media(rtp->service, NULL, 0); +} + +void RP_LoadSDP(RTPClient *rtp, char *sdp_text, u32 sdp_len, RTPStream *stream) +{ + GF_Err e; + u32 i; + GF_SDPInfo *sdp; + Bool is_isma_1, has_iod; + char *iod_str; + GF_X_Attribute *att; + + is_isma_1 = 0; + iod_str = NULL; + sdp = gf_sdp_info_new(); + e = gf_sdp_info_parse(sdp, sdp_text, sdp_len); + + if (e == GF_OK) e = RP_SetupSDP(rtp, sdp, stream); + + /*root SDP, attach service*/ + if (! stream) { + /*look for IOD*/ + if (e==GF_OK) { + i=0; + while ((att = (GF_X_Attribute*)gf_list_enum(sdp->Attributes, &i))) { + if (!iod_str && !strcmp(att->Name, "mpeg4-iod") ) iod_str = att->Value; + if (!is_isma_1 && !strcmp(att->Name, "isma-compliance") ) { + if (!stricmp(att->Value, "1,1.0,1")) is_isma_1 = 1; + } + } + + /*force iod reconstruction with ISMA to use proper clock dependencies*/ + if (is_isma_1) iod_str = NULL; + + /*some folks have weird notions of MPEG-4 systems, they use hardcoded IOD + with AAC ESD even when streaming AMR...*/ + if (iod_str) { + RTPStream *ch; + i=0; + while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) { + if ((ch->depacketizer->payt==GF_RTP_PAYT_AMR) || (ch->depacketizer->payt==GF_RTP_PAYT_AMR_WB) ) { + iod_str = NULL; + break; + } + } + } + if (!iod_str) { + RTPStream *ch; + Bool needs_iod = 0; + i=0; + while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) { + if ((ch->depacketizer->payt==GF_RTP_PAYT_MPEG4) && (ch->depacketizer->sl_map.StreamType==GF_STREAM_SCENE) + || ((ch->depacketizer->payt==GF_RTP_PAYT_3GPP_DIMS) && (ch->depacketizer->sl_map.StreamType==GF_STREAM_SCENE)) + ) { + needs_iod = 1; + break; + } + } + if (needs_iod) { + rtp->session_desc = (GF_Descriptor *)RP_GetChannelOD(ch, 0); + } + } + + if (iod_str) e = RP_SDPLoadIOD(rtp, iod_str); + } + /*attach service*/ + has_iod = rtp->session_desc ? 1 : 0; + gf_term_on_connect(rtp->service, NULL, e); + if (!e && !has_iod && !rtp->media_type) RP_SetupObjects(rtp); + rtp->media_type = 0; + } + /*channel SDP */ + else { + if (e) { + gf_term_on_connect(rtp->service, stream->channel, e); + stream->status = RTP_Unavailable; + } else { + /*connect*/ + RP_SetupChannel(stream, NULL); + } + } + + if (sdp) { + char *cache = (char *) gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), + "Streaming", "SessionMigrationFile"); + + if (cache && cache[0]) { + char *out = NULL; + + if (!strncmp(cache, "http://", 7)) { + char temp[20]; + char *cdir = (char *) gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), + "General", "CacheDirectory"); + + rtp->session_state = malloc(sizeof(char)*4096); + strcpy(rtp->session_state, cdir); + strcat(rtp->session_state, "/"); + sprintf(temp, "mig%08x.sdp", (u32) rtp); + strcat(rtp->session_state, temp); + + rtp->remote_session_state = strdup(cache); + } else { + rtp->session_state = strdup(cache); + } + + gf_sdp_info_write(sdp, &out); + if (out) { + FILE *f = fopen(rtp->session_state, "wb"); + if (f) { + fprintf(f, out); + fclose(f); + } else { + free(rtp->session_state); + rtp->session_state = NULL; + } + free(out); + } + } + gf_sdp_info_del(sdp); + } +} + +void MigrateSDP_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + RTPClient *rtp = (RTPClient *)cbk; + + e = param->error; + switch (param->msg_type) { + case GF_NETIO_GET_METHOD: + param->name = "POST"; + return; + case GF_NETIO_GET_CONTENT: + if (rtp->session_state) { + char szBody[4096], *opt; + u32 len1, len2; + FILE *f; + opt = (char *) gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), "Network", "MobileIP"); + sprintf(szBody, "ipadd\n%s\n\nurl\n%s\n\ndata\n", opt, gf_term_get_service_url(rtp->service) ); + len1 = strlen(szBody); + f = fopen(rtp->session_state, "r+t"); + fseek(f, 0, SEEK_END); + len2 = ftell(f); + fseek(f, 0, SEEK_SET); + len2 = fread(szBody+len1, 1, len2, f); + fclose(f); + szBody[len1+len2] = 0; + + rtp->tmp_buf = strdup(szBody); + param->data = rtp->tmp_buf; + param->size = strlen(szBody); + } + return; + + } +} + + +void RP_SaveSessionState(RTPClient *rtp) +{ + GF_Err e; + FILE *f; + char *sdp_buf; + GF_X_Attribute*att; + u32 i, j; + u32 sdp_size; + GF_SDPInfo *sdp; + RTSPSession *sess = NULL; + + if (!rtp->session_state) return; + + sdp_buf = NULL; + f = fopen(rtp->session_state, "rt"); + if (!f) { + if (strncmp(rtp->session_state, "http://", 7)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP] Cannot load session state %s\n", rtp->session_state)); + return; + } + } + fseek(f, 0, SEEK_END); + sdp_size = ftell(f); + fseek(f, 0, SEEK_SET); + sdp_buf = (char*)malloc(sdp_size); + fread(sdp_buf, sdp_size, 1, f); + fclose(f); + + + sdp = gf_sdp_info_new(); + e = gf_sdp_info_parse(sdp, sdp_buf, sdp_size); + free(sdp_buf); + + for (i=0; i<gf_list_count(rtp->channels); i++) { + GF_SDPMedia *media = NULL; + RTPStream *ch = gf_list_get(rtp->channels, i); + if (!ch->control) continue; + + for (j=0; j<gf_list_count(sdp->media_desc); j++) { + u32 k; + GF_SDPMedia *med = (GF_SDPMedia*)gf_list_get(sdp->media_desc, j); + + for (k=0; k<gf_list_count(med->Attributes); k++) { + att = (GF_X_Attribute*)gf_list_get(med->Attributes, k); + if (!stricmp(att->Name, "control") && !strcmp(att->Value, ch->control)) { + media = med; + break; + } + } + if (media) + break; + } + if (!media) continue; + + if (ch->rtp_ch->net_info.IsUnicast) { + char szPorts[4096]; + media->PortNumber = ch->rtp_ch->net_info.client_port_first; + + /*remove x-server-port extension*/ + for (j=0; j<gf_list_count(media->Attributes); j++) { + att = (GF_X_Attribute*)gf_list_get(media->Attributes, j); + if (!stricmp(att->Name, "x-stream-state") ) { + free(att->Name); + free(att->Value); + free(att); + gf_list_rem(media->Attributes, j); + } + } + ch->current_start += gf_rtp_get_current_time(ch->rtp_ch); + + GF_SAFEALLOC(att, GF_X_Attribute); + att->Name = strdup("x-stream-state"); + sprintf(szPorts, "server-port=%d-%d;ssrc=%X;npt=%g;seq=%d;rtptime=%d", + ch->rtp_ch->net_info.port_first, + ch->rtp_ch->net_info.port_last, + ch->rtp_ch->SenderSSRC, + ch->current_start, + ch->rtp_ch->rtp_first_SN, + ch->rtp_ch->rtp_time + ); + att->Value = strdup(szPorts); + gf_list_add(media->Attributes, att); + + if (ch->rtsp) + sess = ch->rtsp; + } else { + media->PortNumber = ch->rtp_ch->net_info.port_first; + } + + } + /*remove x-server-port/x-session-id extension*/ + for (j=0; j<gf_list_count(sdp->Attributes); j++) { + att = (GF_X_Attribute*)gf_list_get(sdp->Attributes, j); + if (!stricmp(att->Name, "x-session-id") || !stricmp(att->Name, "x-session-name") + ) { + free(att->Name); + free(att->Value); + free(att); + gf_list_rem(sdp->Attributes, j); + } + } + if (sess && sess->session_id) { + GF_SAFEALLOC(att, GF_X_Attribute); + att->Name = strdup("x-session-id"); + att->Value = strdup(sess->session_id); + gf_list_add(sdp->Attributes, att); + } + { + char szURL[4096]; + GF_SAFEALLOC(att, GF_X_Attribute); + att->Name = strdup("x-session-name"); + sprintf(szURL, "rtsp://%s:%d/%s", sess->session->Server, sess->session->Port, sess->session->Service); + att->Value = strdup(szURL); + gf_list_add(sdp->Attributes, att); + } + + + f = fopen(rtp->session_state, "wb"); + if (f) { + char *out = NULL; + gf_sdp_info_write(sdp, &out); + if (out) { + fprintf(f, out); + free(out); + } + fclose(f); + } + + gf_sdp_info_del(sdp); + + if (rtp->remote_session_state && rtp->session_state) { + if (rtp->dnload) gf_term_download_del(rtp->dnload); + rtp->dnload = NULL; + + rtp->dnload = gf_term_download_new(rtp->service, rtp->remote_session_state, GF_NETIO_SESSION_NOT_THREADED, MigrateSDP_NetIO, rtp); + while (1) { + char buffer[100]; + u32 read; + e = gf_dm_sess_fetch_data(rtp->dnload, buffer, 100, &read); + if (e && (e!=GF_IP_NETWORK_EMPTY)) break; + } + if (rtp->tmp_buf) free(rtp->tmp_buf); + gf_term_download_del(rtp->dnload); + rtp->dnload = NULL; + if (e<0) { + gf_term_on_message(sess->owner->service, e, "Error saving session state"); + } + } +} diff --git a/modules/saf_in/Makefile b/modules/saf_in/Makefile new file mode 100644 index 0000000..cdab347 --- /dev/null +++ b/modules/saf_in/Makefile @@ -0,0 +1,63 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/saf_in + +CFLAGS= $(OPTFLAGS) -w -I$(SRC_PATH)/include + + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g -DDEBUG +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS=saf_in.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_saf_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols saf_in.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/saf_in/saf_in.c b/modules/saf_in/saf_in.c new file mode 100644 index 0000000..e611c62 --- /dev/null +++ b/modules/saf_in/saf_in.c @@ -0,0 +1,593 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / SAF reader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/modules/service.h> +#include <gpac/modules/codec.h> +#include <gpac/constants.h> +#include <gpac/thread.h> + +typedef struct +{ + LPNETCHANNEL ch; + u32 au_sn, stream_id, ts_res, buffer_min; + GF_ESD *esd; +} SAFChannel; + +enum +{ + SAF_FILE_LOCAL, + SAF_FILE_REMOTE, + SAF_LIVE_STREAM +}; + +typedef struct +{ + GF_ClientService *service; + GF_List *channels; + Bool needs_connection; + + u32 saf_type; + + /*file downloader*/ + GF_DownloadSession * dnload; + + /*SAF buffer for both lcoal, remote and live streams*/ + char *saf_data; + u32 saf_size, alloc_size; + + /*local file playing*/ + GF_Thread *th; + FILE *stream; + u32 run_state; + u32 start_range, end_range; + Double duration; + u32 nb_playing; +} SAFIn; + +static GFINLINE SAFChannel *saf_get_channel(SAFIn *saf, u32 stream_id, LPNETCHANNEL a_ch) +{ + SAFChannel *ch; + u32 i=0; + while ((ch = (SAFChannel *)gf_list_enum(saf->channels, &i))) { + if (ch->stream_id==stream_id) return ch; + if (a_ch && (ch->ch==a_ch)) return ch; + } + return NULL; +} + +static Bool SAF_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt; + sExt = strrchr(url, '.'); + if (!sExt) return 0; + if (gf_term_check_extension(plug, "application/x-saf", "saf lsr", "SAF Rich Media", sExt)) return 1; + return 0; +} + +static void SAF_Regulate(SAFIn *read) +{ + GF_NetworkCommand com; + SAFChannel *ch; + + com.command_type = GF_NET_CHAN_BUFFER_QUERY; + /*sleep untill the buffer occupancy is too low - note that this work because all streams in this + demuxer are synchronized*/ + while (read->run_state) { + u32 min_occ = (u32) -1; + u32 i=0; + while ( (ch = (SAFChannel *)gf_list_enum(read->channels, &i))) { + com.base.on_channel = ch->ch; + gf_term_on_command(read->service, &com, GF_OK); + if (com.buffer.occupancy < ch->buffer_min) return; + if (com.buffer.occupancy) min_occ = MIN(min_occ, com.buffer.occupancy - ch->buffer_min); + } + if (min_occ == (u32) -1) break; + //fprintf(stdout, "Regulating SAF demux - sleeping for %d ms\n", min_occ); + gf_sleep(min_occ); + } +} + +static void SAF_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + Bool is_rap, go; + SAFChannel *ch; + u32 cts, au_sn, au_size, bs_pos, type, i, stream_id; + GF_BitStream *bs; + GF_SLHeader sl_hdr; + + SAFIn *read = (SAFIn *) cbk; + + e = param->error; + /*done*/ + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + if (read->stream && (read->saf_type==SAF_FILE_REMOTE)) read->saf_type = SAF_FILE_LOCAL; + return; + } else { + /*handle service message*/ + gf_term_download_update_stats(read->dnload); + if (param->msg_type!=GF_NETIO_DATA_EXCHANGE) { + if (e<0) { + if (read->needs_connection) { + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, e); + } + return; + } + if (read->needs_connection) { + u32 total_size; + gf_dm_sess_get_stats(read->dnload, NULL, NULL, &total_size, NULL, NULL, NULL); + if (!total_size) read->saf_type = SAF_LIVE_STREAM; + } + return; + } + } + if (!param->size) return; + + if (!read->run_state) return; + + if (read->alloc_size < read->saf_size + param->size) { + read->saf_data = (char*)realloc(read->saf_data, sizeof(char)*(read->saf_size + param->size) ); + read->alloc_size = read->saf_size + param->size; + } + memcpy(read->saf_data + read->saf_size, param->data, sizeof(char)*param->size); + read->saf_size += param->size; + + /*first AU not complete yet*/ + if (read->saf_size<10) return; + + bs = gf_bs_new(read->saf_data, read->saf_size, GF_BITSTREAM_READ); + bs_pos = 0; + + go = 1; + while (go) { + u32 avail = (u32) gf_bs_available(bs); + bs_pos = (u32) gf_bs_get_position(bs); + + if (avail<10) break; + + is_rap = gf_bs_read_int(bs, 1); + au_sn = gf_bs_read_int(bs, 15); + gf_bs_read_int(bs, 2); + cts = gf_bs_read_int(bs, 30); + au_size = gf_bs_read_int(bs, 16); + avail-=8; + + if (au_size > avail) break; + assert(au_size>=2); + + is_rap = 1; + + type = gf_bs_read_int(bs, 4); + stream_id = gf_bs_read_int(bs, 12); + au_size -= 2; + + ch = saf_get_channel(read, stream_id, NULL); + switch (type) { + case 1: + case 2: + case 7: + if (ch) { + gf_bs_skip_bytes(bs, au_size); + } else { + SAFChannel *first = (SAFChannel *)gf_list_get(read->channels, 0); + GF_SAFEALLOC(ch, SAFChannel); + ch->stream_id = stream_id; + ch->esd = gf_odf_desc_esd_new(0); + ch->esd->ESID = stream_id; + ch->esd->OCRESID = first ? first->stream_id : stream_id; + ch->esd->slConfig->useRandomAccessPointFlag = 1; + ch->esd->slConfig->AUSeqNumLength = 0; + ch->esd->decoderConfig->objectTypeIndication = gf_bs_read_u8(bs); + ch->esd->decoderConfig->streamType = gf_bs_read_u8(bs); + ch->ts_res = ch->esd->slConfig->timestampResolution = gf_bs_read_u24(bs); + ch->esd->decoderConfig->bufferSizeDB = gf_bs_read_u16(bs); + au_size -= 7; + if ((ch->esd->decoderConfig->objectTypeIndication == 0xFF) && (ch->esd->decoderConfig->streamType == 0xFF) ) { + u16 mimeLen = gf_bs_read_u16(bs); + gf_bs_skip_bytes(bs, mimeLen); + au_size -= mimeLen+2; + } + if (type==7) { + u16 urlLen = gf_bs_read_u16(bs); + ch->esd->URLString = (char*)malloc(sizeof(char)*(urlLen+1)); + gf_bs_read_data(bs, ch->esd->URLString, urlLen); + ch->esd->URLString[urlLen] = 0; + au_size -= urlLen+2; + } + if (au_size) { + ch->esd->decoderConfig->decoderSpecificInfo->dataLength = au_size; + ch->esd->decoderConfig->decoderSpecificInfo->data = (char*)malloc(sizeof(char)*au_size); + gf_bs_read_data(bs, ch->esd->decoderConfig->decoderSpecificInfo->data, au_size); + } + if (ch->esd->decoderConfig->streamType==4) ch->buffer_min=100; + else if (ch->esd->decoderConfig->streamType==5) ch->buffer_min=400; + else ch->buffer_min=0; + + if (read->needs_connection && (ch->esd->decoderConfig->streamType==GF_STREAM_SCENE)) { + gf_list_add(read->channels, ch); + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, GF_OK); + } else if (read->needs_connection) { + gf_odf_desc_del((GF_Descriptor *) ch->esd); + free(ch); + ch = NULL; + } else { + GF_ObjectDescriptor *od; + gf_list_add(read->channels, ch); + + od = (GF_ObjectDescriptor*)gf_odf_desc_new(GF_ODF_OD_TAG); + gf_list_add(od->ESDescriptors, ch->esd); + ch->esd = NULL; + od->objectDescriptorID = ch->stream_id; + gf_term_add_media(read->service, (GF_Descriptor*)od, 0); + + } + } + break; + case 4: + if (ch) { + bs_pos = (u32) gf_bs_get_position(bs); + memset(&sl_hdr, 0, sizeof(GF_SLHeader)); + sl_hdr.accessUnitLength = au_size; + sl_hdr.AU_sequenceNumber = au_sn; + sl_hdr.compositionTimeStampFlag = 1; + sl_hdr.compositionTimeStamp = cts; + sl_hdr.randomAccessPointFlag = is_rap; + if (read->start_range && (read->start_range*ch->ts_res>cts*1000)) { + sl_hdr.compositionTimeStamp = read->start_range*ch->ts_res/1000; + } + gf_term_on_sl_packet(read->service, ch->ch, read->saf_data+bs_pos, au_size, &sl_hdr, GF_OK); + } + gf_bs_skip_bytes(bs, au_size); + break; + case 3: + if (ch) gf_term_on_sl_packet(read->service, ch->ch, NULL, 0, NULL, GF_EOS); + break; + case 5: + go = 0; + read->run_state = 0; + i=0; + while ((ch = (SAFChannel *)gf_list_enum(read->channels, &i))) { + gf_term_on_sl_packet(read->service, ch->ch, NULL, 0, NULL, GF_EOS); + } + break; + } + } + + gf_bs_del(bs); + if (bs_pos) { + u32 remain = read->saf_size - bs_pos; + if (remain) memmove(read->saf_data, read->saf_data+bs_pos, sizeof(char)*remain); + read->saf_size = remain; + } + SAF_Regulate(read); +} + + +u32 SAF_Run(void *_p) +{ + GF_NETIO_Parameter par; + char data[1024]; + SAFIn *read = (SAFIn *)_p; + + par.msg_type = GF_NETIO_DATA_EXCHANGE; + par.data = data; + + fseek(read->stream, 0, SEEK_SET); + read->saf_size=0; + read->run_state = 1; + while (read->run_state && !feof(read->stream) ) { + par.size = fread(data, 1, 1024, read->stream); + if (!par.size) break; + SAF_NetIO(read, &par); + } + read->run_state = 2; + return 0; +} + +static void SAF_DownloadFile(GF_InputService *plug, char *url) +{ + SAFIn *read = (SAFIn*) plug->priv; + + read->dnload = gf_term_download_new(read->service, url, 0, SAF_NetIO, read); + if (!read->dnload) { + read->needs_connection = 0; + gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } + /*service confirm is done once fetched*/ +} + +typedef struct +{ + u32 stream_id; + u32 ts_res; +} StreamInfo; + +static void SAF_CheckFile(SAFIn *read) +{ + u32 nb_streams, i, cts, au_size, au_type, stream_id, ts_res; + GF_BitStream *bs; + StreamInfo si[1024]; + fseek(read->stream, 0, SEEK_SET); + bs = gf_bs_from_file(read->stream, GF_BITSTREAM_READ); + + nb_streams=0; + while (gf_bs_available(bs)) { + gf_bs_read_u16(bs); + gf_bs_read_int(bs, 2); + cts = gf_bs_read_int(bs, 30); + au_size = gf_bs_read_int(bs, 16); + au_type = gf_bs_read_int(bs, 4); + stream_id = gf_bs_read_int(bs, 12); + au_size-=2; + ts_res = 0; + for (i=0; i<nb_streams; i++) { if (si[i].stream_id==stream_id) ts_res = si[i].ts_res; } + if (!ts_res) { + if ((au_type==1) || (au_type==2) || (au_type==7)) { + gf_bs_read_u16(bs); + ts_res = gf_bs_read_u24(bs); + au_size -= 5; + si[nb_streams].stream_id = stream_id; + si[nb_streams].ts_res = ts_res; + nb_streams++; + } + } + if (ts_res && (au_type==4)) { + Double ts = cts; + ts /= ts_res; + if (ts>read->duration) read->duration = ts; + } + gf_bs_skip_bytes(bs, au_size); + } + gf_bs_del(bs); + fseek(read->stream, 0, SEEK_SET); +} + +static GF_Err SAF_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + char szURL[2048]; + char *ext; + SAFIn *read = (SAFIn *)plug->priv; + read->service = serv; + + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + + strcpy(szURL, url); + ext = strrchr(szURL, '#'); + if (ext) ext[0] = 0; + + read->needs_connection = 1; + read->duration = 0; + + read->saf_type = SAF_FILE_LOCAL; + /*remote fetch*/ + if (strnicmp(url, "file://", 7) && strstr(url, "://")) { + read->saf_type = SAF_FILE_REMOTE; + SAF_DownloadFile(plug, (char *) szURL); + return GF_OK; + } + + read->stream = fopen(szURL, "rb"); + if (!read->stream) { + gf_term_on_connect(serv, NULL, GF_URL_ERROR); + return GF_OK; + } + SAF_CheckFile(read); + read->th = gf_th_new("SAFDemux"); + /*start playing for tune-in*/ + gf_th_run(read->th, SAF_Run, read); + return GF_OK; +} + +static GF_Err SAF_CloseService(GF_InputService *plug) +{ + SAFIn *read = (SAFIn *)plug->priv; + + if (read->th) { + if (read->run_state == 1) { + read->run_state=0; + while (read->run_state!=2) gf_sleep(0); + } + gf_th_del(read->th); + read->th = NULL; + } + + if (read->stream) fclose(read->stream); + read->stream = NULL; + if (read->dnload) gf_term_download_del(read->dnload); + read->dnload = NULL; + gf_term_on_disconnect(read->service, NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *SAF_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + u32 i=0; + SAFChannel *root; + SAFIn *read = (SAFIn *)plug->priv; + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_IOD_TAG); + + od->objectDescriptorID = 1; + + while ( (root = (SAFChannel *)gf_list_enum(read->channels, &i))) { + if (root->esd && (root->esd->decoderConfig->streamType==GF_STREAM_SCENE)) break; + } + if (!root) return NULL; + + /*inline scene*/ + gf_list_add(od->ESDescriptors, root->esd); + root->esd = NULL; + return (GF_Descriptor *) od; +} + +static GF_Err SAF_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ES_ID; + SAFChannel *ch; + GF_Err e; + SAFIn *read = (SAFIn *)plug->priv; + + + ch = saf_get_channel(read, 0, channel); + if (ch) e = GF_SERVICE_ERROR; + + e = GF_STREAM_NOT_FOUND; + if (strstr(url, "ES_ID")) { + sscanf(url, "ES_ID=%d", &ES_ID); + ch = saf_get_channel(read, ES_ID, NULL); + if (ch && !ch->ch) { + ch->ch = channel; + e = GF_OK; + } + } + + gf_term_on_connect(read->service, channel, e); + return e; +} + +static GF_Err SAF_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + SAFChannel *ch; + SAFIn *read = (SAFIn *)plug->priv; + + GF_Err e = GF_STREAM_NOT_FOUND; + ch = saf_get_channel(read, 0, channel); + if (ch) { + gf_list_del_item(read->channels, ch); + if (ch->esd) gf_odf_desc_del((GF_Descriptor*)ch->esd); + free(ch); + e = GF_OK; + } + gf_term_on_disconnect(read->service, channel, e); + return GF_OK; +} + +static GF_Err SAF_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + SAFIn *read = (SAFIn *)plug->priv; + + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: + return GF_NOT_SUPPORTED; + case GF_NET_CHAN_INTERACTIVE: + return GF_OK; + case GF_NET_CHAN_BUFFER: + return GF_OK; + case GF_NET_CHAN_DURATION: + com->duration.duration = read->duration; + return GF_OK; + case GF_NET_CHAN_PLAY: + if (!read->nb_playing) { + read->start_range = (u32) (com->play.start_range*1000); + read->end_range = (u32) (com->play.end_range*1000); + /*start demuxer*/ + if ((read->saf_type == SAF_FILE_LOCAL) && (read->run_state!=1)) { + gf_th_run(read->th, SAF_Run, read); + } + } + read->nb_playing++; + return GF_OK; + case GF_NET_CHAN_STOP: + assert(read->nb_playing); + read->nb_playing--; + /*stop demuxer*/ + if (!read->nb_playing && (read->run_state==1)) { + read->run_state=0; + while (read->run_state!=2) gf_sleep(2); + } + return GF_OK; + default: + return GF_OK; + } +} + + +GF_InputService *NewSAFReader() +{ + SAFIn *reader; + GF_InputService *plug; + GF_SAFEALLOC(plug, GF_InputService); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC SAF Reader", "gpac distribution") + + plug->CanHandleURL = SAF_CanHandleURL; + plug->ConnectService = SAF_ConnectService; + plug->CloseService = SAF_CloseService; + plug->GetServiceDescriptor = SAF_GetServiceDesc; + plug->ConnectChannel = SAF_ConnectChannel; + plug->DisconnectChannel = SAF_DisconnectChannel; + plug->ServiceCommand = SAF_ServiceCommand; + + GF_SAFEALLOC(reader, SAFIn); + reader->channels = gf_list_new(); + plug->priv = reader; + return plug; +} + +void DeleteSAFReader(void *ifce) +{ + GF_InputService *plug = (GF_InputService *) ifce; + SAFIn *read = (SAFIn *)plug->priv; + + while (gf_list_count(read->channels)) { + SAFChannel *ch = (SAFChannel *)gf_list_last(read->channels); + gf_list_rem_last(read->channels); + if (ch->esd) gf_odf_desc_del((GF_Descriptor *) ch->esd); + free(ch); + } + gf_list_del(read->channels); + if (read->saf_data) free(read->saf_data); + free(read); + free(plug); +} + + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_NET_CLIENT_INTERFACE: return 1; + default: return 0; + } +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_NET_CLIENT_INTERFACE: return (GF_BaseInterface *) NewSAFReader(); + default: return NULL; + } +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_NET_CLIENT_INTERFACE: DeleteSAFReader(ifce); break; + } +} diff --git a/modules/saf_in/saf_in.def b/modules/saf_in/saf_in.def new file mode 100644 index 0000000..8f7ac7c --- /dev/null +++ b/modules/saf_in/saf_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_saf_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/sdl_out/Makefile b/modules/sdl_out/Makefile new file mode 100644 index 0000000..4cdea4b --- /dev/null +++ b/modules/sdl_out/Makefile @@ -0,0 +1,72 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/sdl_out + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include $(SDL_CFLAGS) + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +LINKFLAGS=$(SDL_LIBS) +ifeq ($(CONFIG_X11), yes) +CFLAGS+=-DGPAC_HAS_X11 +LINKFLAGS+=-lX11 +ifeq ($(X11_INC_PATH), "") +else +CFLAGS+=-I$(X11_INC_PATH) +LINKFLAGS+=-L$(X11_LIB_PATH) +endif +endif + +#common obj +OBJS=sdl_out.o audio.o video.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_sdl_out.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols sdl_out.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(LINKFLAGS) -L../../bin/gcc -lgpac + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/sdl_out/audio.c b/modules/sdl_out/audio.c new file mode 100644 index 0000000..7351f1e --- /dev/null +++ b/modules/sdl_out/audio.c @@ -0,0 +1,210 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / SDL audio and video module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "sdl_out.h" + +#define SDLAUD() SDLAudCtx *ctx = (SDLAudCtx *)dr->opaque + +void sdl_fill_audio(void *udata, Uint8 *stream, int len) +{ + GF_AudioOutput *dr = (GF_AudioOutput *)udata; + dr->FillBuffer(dr->audio_renderer, stream, len); +} + + +static GF_Err SDLAud_Setup(GF_AudioOutput *dr, void *os_handle, u32 num_buffers, u32 total_duration) +{ + u32 flags; + SDL_AudioSpec want_format, got_format; + SDLAUD(); + + /*init sdl*/ + if (!SDLOUT_InitSDL()) return GF_IO_ERR; + + flags = SDL_WasInit(SDL_INIT_AUDIO); + if (!(flags & SDL_INIT_AUDIO)) { + if (SDL_InitSubSystem(SDL_INIT_AUDIO)<0) { + SDLOUT_CloseSDL(); + return GF_IO_ERR; + } + } + /*check we can open the device*/ + memset(&want_format, 0, sizeof(SDL_AudioSpec)); + want_format.freq = 44100; + want_format.format = AUDIO_S16SYS; + want_format.channels = 2; + want_format.samples = 1024; + want_format.callback = sdl_fill_audio; + want_format.userdata = dr; + if ( SDL_OpenAudio(&want_format, &got_format) < 0 ) { + SDL_QuitSubSystem(SDL_INIT_AUDIO); + SDLOUT_CloseSDL(); + return GF_IO_ERR; + } + SDL_CloseAudio(); + ctx->is_init = 1; + ctx->num_buffers = num_buffers; + ctx->total_duration = total_duration; + return GF_OK; +} + +static void SDLAud_Shutdown(GF_AudioOutput *dr) +{ + SDLAUD(); + + if (ctx->is_running) SDL_CloseAudio(); + if (ctx->is_init) { + SDL_QuitSubSystem(SDL_INIT_AUDIO); + SDLOUT_CloseSDL(); + ctx->is_init = 0; + } +} + +static GF_Err SDLAud_ConfigureOutput(GF_AudioOutput *dr, u32 *SampleRate, u32 *NbChannels, u32 *nbBitsPerSample, u32 channel_cfg) +{ + s32 nb_samples; + SDL_AudioSpec want_format, got_format; + SDLAUD(); + + if (ctx->is_running) SDL_CloseAudio(); + ctx->is_running = 0; + + memset(&want_format, 0, sizeof(SDL_AudioSpec)); + want_format.freq = *SampleRate; + want_format.format = (*nbBitsPerSample==16) ? AUDIO_S16SYS : AUDIO_S8; + want_format.channels = *NbChannels; + want_format.callback = sdl_fill_audio; + want_format.userdata = dr; + + if (ctx->num_buffers && ctx->total_duration) { + nb_samples = want_format.freq * ctx->total_duration; + nb_samples /= (1000 * ctx->num_buffers); + if (nb_samples % 2) nb_samples++; + } else { + nb_samples = 1024; + } + + /*respect SDL need for power of 2*/ + want_format.samples = 1; + while (want_format.samples*2<nb_samples) want_format.samples *= 2; + + if ( SDL_OpenAudio(&want_format, &got_format) < 0 ) return GF_IO_ERR; + ctx->is_running = 1; + ctx->delay_ms = (got_format.samples * 1000) / got_format.freq; + ctx->total_size = got_format.samples; + *SampleRate = got_format.freq; + *NbChannels = got_format.channels; + + switch (got_format.format) { + case AUDIO_S8: + case AUDIO_U8: + *nbBitsPerSample = 8; + break; + default: + *nbBitsPerSample = 16; + break; + } + /*and play*/ + SDL_PauseAudio(0); + return GF_OK; +} + +static u32 SDLAud_GetAudioDelay(GF_AudioOutput *dr) +{ + SDLAUD(); + return ctx->delay_ms; +} + +static u32 SDLAud_GetTotalBufferTime(GF_AudioOutput *dr) +{ + SDLAUD(); + return ctx->delay_ms; +} + +static void SDLAud_SetVolume(GF_AudioOutput *dr, u32 Volume) +{ + /*not supported by SDL*/ +} + +static void SDLAud_SetPan(GF_AudioOutput *dr, u32 pan) +{ + /*not supported by SDL*/ +} + +static void SDLAud_Play(GF_AudioOutput *dr, u32 PlayType) +{ + SDL_PauseAudio(PlayType ? 0 : 1); +} + +static void SDLAud_SetPriority(GF_AudioOutput *dr, u32 priority) +{ + /*not supported by SDL*/ +} + +static GF_Err SDLAud_QueryOutputSampleRate(GF_AudioOutput *dr, u32 *desired_samplerate, u32 *NbChannels, u32 *nbBitsPerSample) +{ + /*cannot query supported formats in SDL...*/ + return GF_OK; +} + +void *SDL_NewAudio() +{ + SDLAudCtx *ctx; + GF_AudioOutput *dr; + + + ctx = malloc(sizeof(SDLAudCtx)); + memset(ctx, 0, sizeof(SDLAudCtx)); + + dr = malloc(sizeof(GF_AudioOutput)); + memset(dr, 0, sizeof(GF_AudioOutput)); + GF_REGISTER_MODULE_INTERFACE(dr, GF_AUDIO_OUTPUT_INTERFACE, "SDL Audio Output", "gpac distribution"); + + dr->opaque = ctx; + + dr->Setup = SDLAud_Setup; + dr->Shutdown = SDLAud_Shutdown; + dr->ConfigureOutput = SDLAud_ConfigureOutput; + dr->SetVolume = SDLAud_SetVolume; + dr->SetPan = SDLAud_SetPan; + dr->Play = SDLAud_Play; + dr->SetPriority = SDLAud_SetPriority; + dr->GetAudioDelay = SDLAud_GetAudioDelay; + dr->GetTotalBufferTime = SDLAud_GetTotalBufferTime; + + dr->QueryOutputSampleRate = SDLAud_QueryOutputSampleRate; + /*always threaded*/ + dr->SelfThreaded = 1; + return dr; +} + +void SDL_DeleteAudio(void *ifce) +{ + GF_AudioOutput *dr = (GF_AudioOutput*)ifce; + SDLAUD(); + + free(ctx); + free(ifce); +} + diff --git a/modules/sdl_out/cursors.c b/modules/sdl_out/cursors.c new file mode 100644 index 0000000..de25c39 --- /dev/null +++ b/modules/sdl_out/cursors.c @@ -0,0 +1,60 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / DirectX audio and video render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +static char hand_data[] = +{ + 0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,2,2,1,2,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,2,2,1,2,2,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,0,1,2,2,1,2,2,1,2,2,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,2,2,1,1,2,2,2,2,2,2,2,2,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,1,2,2,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,2,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; diff --git a/modules/sdl_out/sdl_out.c b/modules/sdl_out/sdl_out.c new file mode 100644 index 0000000..208b0b0 --- /dev/null +++ b/modules/sdl_out/sdl_out.c @@ -0,0 +1,77 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / SDL audio and video module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "sdl_out.h" + +static Bool is_init = 0; +static u32 num_users = 0; + +Bool SDLOUT_InitSDL() +{ + if (is_init) { + num_users++; + return 1; + } + if (SDL_Init(0) < 0) return 0; + is_init = 1; + num_users++; + return 1; +} + +void SDLOUT_CloseSDL() +{ + if (!is_init) return; + assert(num_users); + num_users--; + if (!num_users) SDL_Quit(); + return; +} + + +/*interface query*/ +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return 1; + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) return 1; + return 0; +} +/*interface create*/ +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) return SDL_NewVideo(); + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) return SDL_NewAudio(); + return NULL; +} +/*interface destroy*/ +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_VIDEO_OUTPUT_INTERFACE: + SDL_DeleteVideo(ifce); + break; + case GF_AUDIO_OUTPUT_INTERFACE: + SDL_DeleteAudio(ifce); + break; + } +} diff --git a/modules/sdl_out/sdl_out.def b/modules/sdl_out/sdl_out.def new file mode 100644 index 0000000..81c51d1 --- /dev/null +++ b/modules/sdl_out/sdl_out.def @@ -0,0 +1,6 @@ +LIBRARY gm_sdl_out.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/sdl_out/sdl_out.h b/modules/sdl_out/sdl_out.h new file mode 100644 index 0000000..72b7fc6 --- /dev/null +++ b/modules/sdl_out/sdl_out.h @@ -0,0 +1,79 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / SDL audio and video module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/*SDL*/ +#include <SDL.h> + +/*driver interfaces*/ +#include <gpac/modules/audio_out.h> +#include <gpac/modules/video_out.h> +#include <gpac/thread.h> + +/*SDL init routines*/ +Bool SDLOUT_InitSDL(); +void SDLOUT_CloseSDL(); + +typedef struct +{ + GF_Thread *sdl_th; + GF_Mutex *evt_mx; + u32 sdl_th_state; + Bool is_init, fullscreen; + /*fullscreen display size*/ + u32 fs_width, fs_height; + /*backbuffer size before entering fullscreen mode (used for restore)*/ + u32 store_width, store_height; + /*cursors*/ + SDL_Cursor *curs_def, *curs_hand, *curs_collide; + Bool systems_memory; + + SDL_Surface *screen; + SDL_Surface *back_buffer; + + u32 width, height; + + SDL_Surface *offscreen_gl; + + u32 output_3d_type; + void *os_handle; +} SDLVidCtx; + +void SDL_DeleteVideo(void *ifce); +void *SDL_NewVideo(); + + +/* + SDL audio +*/ +typedef struct +{ + u32 num_buffers, total_duration, delay_ms, total_size; + Bool is_init, is_running; +} SDLAudCtx; + +void SDL_DeleteAudio(void *ifce); +void *SDL_NewAudio(); + + diff --git a/modules/sdl_out/video.c b/modules/sdl_out/video.c new file mode 100644 index 0000000..b567874 --- /dev/null +++ b/modules/sdl_out/video.c @@ -0,0 +1,945 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / SDL audio and video module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "sdl_out.h" +#include <gpac/user.h> + + +#ifdef WIN32 +#include <windows.h> +#endif + + +/*cursors data*/ +static char hand_data[] = +{ + 0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,2,2,1,2,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,1,2,2,1,2,2,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,0,1,2,2,1,2,2,1,2,2,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,2,2,1,1,2,2,2,2,2,2,2,2,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,1,2,2,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,2,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + + +static char collide_data[] = +{ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,1,0,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,1,0,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + + + + +#define SDLVID() SDLVidCtx *ctx = (SDLVidCtx *)dr->opaque + +#ifdef GPAC_HAS_X11 +#include <X11/Xlib.h> +#endif + +static u32 video_modes[] = +{ + 320, 200, + 320, 240, + 400, 300, + 600, 400, + 800, 600, + 1024, 768, + 1152, 864, + 1280, 1024 +}; +static u32 nb_video_modes = 8; + +void SDLVid_SetCaption() +{ + char szName[100]; + if (SDL_VideoDriverName(szName, 100)) { + char szCap[1024]; + sprintf(szCap, "SDL Video Output (%s)", szName); + SDL_WM_SetCaption(szCap, NULL); + } else { + SDL_WM_SetCaption("SDL Video Output", NULL); + } +} + +SDL_Cursor *SDLVid_LoadCursor(char *maskdata) +{ + s32 ind, i, j; + u8 data[4*32]; + u8 mask[4*32]; + + ind = -1; + for (i=0; i<32; i++) { + for (j=0; j<32; j++) { + if (j%8) { + data[ind] <<= 1; + mask[ind] <<= 1; + } else { + ind++; + data[ind] = mask[ind] = 0; + } + switch (maskdata[j+32*i]) { + /*black*/ + case 1: + data[ind] |= 0x01; + /*white*/ + case 2: + mask[ind] |= 0x01; + break; + } + } + } + return SDL_CreateCursor(data, mask, 32, 32, 0, 0); +} + + +static void sdl_translate_key(u32 SDLkey, GF_EventKey *evt) +{ + evt->flags = 0; + evt->hw_code = SDLkey; + switch (SDLkey) { + case SDLK_BACKSPACE: evt->key_code = GF_KEY_BACKSPACE; break; + case SDLK_TAB: evt->key_code = GF_KEY_TAB; break; + case SDLK_CLEAR: evt->key_code = GF_KEY_CLEAR; break; + case SDLK_PAUSE: evt->key_code = GF_KEY_PAUSE; break; + case SDLK_ESCAPE: evt->key_code = GF_KEY_ESCAPE; break; + case SDLK_SPACE: evt->key_code = GF_KEY_SPACE; break; + + case SDLK_KP_ENTER: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_RETURN: + evt->key_code = GF_KEY_ENTER; + break; + + case SDLK_KP_MULTIPLY: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_ASTERISK: + evt->key_code = GF_KEY_STAR; + break; + case SDLK_KP_PLUS: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_PLUS: + evt->key_code = GF_KEY_PLUS; + break; + case SDLK_KP_MINUS: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_MINUS: + evt->key_code = GF_KEY_HYPHEN; + break; + case SDLK_KP_DIVIDE: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_SLASH: + evt->key_code = GF_KEY_SLASH; + break; + + case SDLK_KP0: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_0: + evt->key_code = GF_KEY_0; + break; + case SDLK_KP1: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_1: + evt->key_code = GF_KEY_1; + break; + case SDLK_KP2: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_2: + evt->key_code = GF_KEY_2; + break; + case SDLK_KP3: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_3: + evt->key_code = GF_KEY_3; + break; + case SDLK_KP4: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_4: + evt->key_code = GF_KEY_4; + break; + case SDLK_KP5: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_5: + evt->key_code = GF_KEY_5; + break; + case SDLK_KP6: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_6: + evt->key_code = GF_KEY_6; + break; + case SDLK_KP7: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_7: + evt->key_code = GF_KEY_7; + break; + case SDLK_KP8: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_8: + evt->key_code = GF_KEY_8; + break; + case SDLK_KP9: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_9: + evt->key_code = GF_KEY_9; + break; + case SDLK_KP_PERIOD: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_PERIOD: + evt->key_code = GF_KEY_FULLSTOP; + break; + case SDLK_KP_EQUALS: evt->flags = GF_KEY_EXT_NUMPAD; + case SDLK_EQUALS: + evt->key_code = GF_KEY_EQUALS; + break; + + case SDLK_EXCLAIM: evt->key_code = GF_KEY_EXCLAMATION; break; + case SDLK_QUOTEDBL: evt->key_code = GF_KEY_QUOTATION; break; + case SDLK_HASH: evt->key_code = GF_KEY_NUMBER; break; + case SDLK_DOLLAR: evt->key_code = GF_KEY_DOLLAR; break; + case SDLK_AMPERSAND: evt->key_code = GF_KEY_AMPERSAND; break; + case SDLK_QUOTE: evt->key_code = GF_KEY_APOSTROPHE; break; + case SDLK_LEFTPAREN: evt->key_code = GF_KEY_LEFTPARENTHESIS; break; + case SDLK_RIGHTPAREN: evt->key_code = GF_KEY_RIGHTPARENTHESIS; break; + case SDLK_COMMA: evt->key_code = GF_KEY_COMMA; break; + case SDLK_COLON: evt->key_code = GF_KEY_COLON; break; + case SDLK_SEMICOLON: evt->key_code = GF_KEY_SEMICOLON; break; + case SDLK_LESS: evt->key_code = GF_KEY_LESSTHAN; break; + case SDLK_GREATER: evt->key_code = GF_KEY_GREATERTHAN; break; + case SDLK_QUESTION: evt->key_code = GF_KEY_QUESTION; break; + case SDLK_AT: evt->key_code = GF_KEY_AT; break; + case SDLK_LEFTBRACKET: evt->key_code = GF_KEY_LEFTSQUAREBRACKET; break; + case SDLK_RIGHTBRACKET: evt->key_code = GF_KEY_RIGHTSQUAREBRACKET; break; + case SDLK_BACKSLASH: evt->key_code = GF_KEY_BACKSLASH; break; + case SDLK_UNDERSCORE: evt->key_code = GF_KEY_UNDERSCORE; break; + case SDLK_BACKQUOTE: evt->key_code = GF_KEY_GRAVEACCENT; break; + case SDLK_DELETE: evt->key_code = GF_KEY_DEL; break; + case SDLK_EURO: evt->key_code = GF_KEY_EURO; break; + case SDLK_UNDO: evt->key_code = GF_KEY_UNDO; break; + + case SDLK_UP: evt->key_code = GF_KEY_UP; break; + case SDLK_DOWN: evt->key_code = GF_KEY_DOWN; break; + case SDLK_RIGHT: evt->key_code = GF_KEY_RIGHT; break; + case SDLK_LEFT: evt->key_code = GF_KEY_LEFT; break; + case SDLK_INSERT: evt->key_code = GF_KEY_INSERT; break; + case SDLK_HOME: evt->key_code = GF_KEY_HOME; break; + case SDLK_END: evt->key_code = GF_KEY_END; break; + case SDLK_PAGEUP: evt->key_code = GF_KEY_PAGEUP; break; + case SDLK_PAGEDOWN: evt->key_code = GF_KEY_PAGEDOWN; break; + case SDLK_F1: evt->key_code = GF_KEY_F1; break; + case SDLK_F2: evt->key_code = GF_KEY_F2; break; + case SDLK_F3: evt->key_code = GF_KEY_F3; break; + case SDLK_F4: evt->key_code = GF_KEY_F4; break; + case SDLK_F5: evt->key_code = GF_KEY_F5; break; + case SDLK_F6: evt->key_code = GF_KEY_F6; break; + case SDLK_F7: evt->key_code = GF_KEY_F7; break; + case SDLK_F8: evt->key_code = GF_KEY_F8; break; + case SDLK_F9: evt->key_code = GF_KEY_F9; break; + case SDLK_F10: evt->key_code = GF_KEY_F10; break; + case SDLK_F11: evt->key_code = GF_KEY_F11; break; + case SDLK_F12: evt->key_code = GF_KEY_F12; break; + case SDLK_F13: evt->key_code = GF_KEY_F13; break; + case SDLK_F14: evt->key_code = GF_KEY_F14; break; + case SDLK_F15: evt->key_code = GF_KEY_F15; break; + case SDLK_NUMLOCK: evt->key_code = GF_KEY_NUMLOCK; break; + case SDLK_CAPSLOCK: evt->key_code = GF_KEY_CAPSLOCK; break; + case SDLK_SCROLLOCK: evt->key_code = GF_KEY_SCROLL; break; + + case SDLK_RSHIFT: + evt->key_code = GF_KEY_SHIFT; + evt->flags = GF_KEY_EXT_RIGHT; + break; + case SDLK_LSHIFT: + evt->key_code = GF_KEY_SHIFT; + evt->flags = GF_KEY_EXT_LEFT; + break; + case SDLK_LCTRL: + evt->key_code = GF_KEY_CONTROL; + evt->flags = GF_KEY_EXT_LEFT; + break; + case SDLK_RCTRL: + evt->key_code = GF_KEY_CONTROL; + evt->flags = GF_KEY_EXT_RIGHT; + break; + case SDLK_LALT: + evt->key_code = GF_KEY_ALT; + evt->flags = GF_KEY_EXT_LEFT; + break; + case SDLK_RALT: + evt->key_code = GF_KEY_ALT; + evt->flags = GF_KEY_EXT_RIGHT; + break; + case SDLK_LSUPER: + evt->key_code = GF_KEY_META; + evt->flags = GF_KEY_EXT_LEFT; + break; + case SDLK_RSUPER: + evt->key_code = GF_KEY_META; + evt->flags = GF_KEY_EXT_RIGHT; + break; + case SDLK_MODE: evt->key_code = GF_KEY_MODECHANGE; break; + case SDLK_COMPOSE: evt->key_code = GF_KEY_COMPOSE; break; + case SDLK_HELP: evt->key_code = GF_KEY_HELP; break; + case SDLK_PRINT: evt->key_code = GF_KEY_PRINTSCREEN; break; + +/* + SDLK_CARET = 94, + SDLK_a = 97, + SDLK_b = 98, + SDLK_c = 99, + SDLK_d = 100, + SDLK_e = 101, + SDLK_f = 102, + SDLK_g = 103, + SDLK_h = 104, + SDLK_i = 105, + SDLK_j = 106, + SDLK_k = 107, + SDLK_l = 108, + SDLK_m = 109, + SDLK_n = 110, + SDLK_o = 111, + SDLK_p = 112, + SDLK_q = 113, + SDLK_r = 114, + SDLK_s = 115, + SDLK_t = 116, + SDLK_u = 117, + SDLK_v = 118, + SDLK_w = 119, + SDLK_x = 120, + SDLK_y = 121, + SDLK_z = 122, + SDLK_DELETE = 127, + + SDLK_SYSREQ = 317, + SDLK_POWER = 320, + +*/ + + default: + if ((SDLkey>=0x30) && (SDLkey<=0x39)) evt->key_code = GF_KEY_0 + SDLkey-0x30; + else if ((SDLkey>=0x41) && (SDLkey<=0x5A)) evt->key_code = GF_KEY_A + SDLkey-0x51; + else + evt->key_code = GF_KEY_UNIDENTIFIED; + break; + } +} + +#if 0 +void SDLVid_SetHack(void *os_handle, Bool set_on) +{ + unsetenv("SDL_WINDOWID="); + if (!os_handle) return; + if (set_on) { + char buf[16]; + snprintf(buf, sizeof(buf), "%u", (u32) os_handle); + setenv("SDL_WINDOWID", buf, 1); + sprintf(buf, "SDL_WINDOWID=%u", (u32) os_handle); + putenv(buf); + } +} +#endif + +static void SDLVid_DestroyObjects(SDLVidCtx *ctx) +{ + if (ctx->back_buffer) SDL_FreeSurface(ctx->back_buffer); + ctx->back_buffer = NULL; +} + + +#define SDL_WINDOW_FLAGS SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_RESIZABLE +#define SDL_FULLSCREEN_FLAGS SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_FULLSCREEN +#define SDL_GL_WINDOW_FLAGS SDL_HWSURFACE | SDL_OPENGL | SDL_HWACCEL | SDL_RESIZABLE +#define SDL_GL_FULLSCREEN_FLAGS SDL_HWSURFACE | SDL_OPENGL | SDL_HWACCEL | SDL_FULLSCREEN + +GF_Err SDLVid_ResizeWindow(GF_VideoOutput *dr, u32 width, u32 height) +{ + SDLVID(); + GF_Event evt; + + /*lock X mutex to make sure the event queue is not being processed*/ + gf_mx_p(ctx->evt_mx); + + if (ctx->output_3d_type==1) { + u32 flags, nb_bits; + const char *opt; + if ((ctx->width==width) && (ctx->height==height) ) { + gf_mx_v(ctx->evt_mx); + return GF_OK; + } + flags = SDL_GL_WINDOW_FLAGS; + if (ctx->os_handle) flags &= ~SDL_RESIZABLE; + if (!ctx->screen) ctx->screen = SDL_SetVideoMode(width, height, 0, flags); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsDepth"); + nb_bits = opt ? atoi(opt) : 16; + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, nb_bits); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); + opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "GLNbBitsPerComponent"); + nb_bits = opt ? atoi(opt) : 5; + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, nb_bits); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, nb_bits); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, nb_bits); + + assert(width); + assert(height); + ctx->screen = SDL_SetVideoMode(width, height, 0, flags); + assert(ctx->screen); + ctx->width = width; + ctx->height = height; + evt.type = GF_EVENT_VIDEO_SETUP; + dr->on_event(dr->evt_cbk_hdl, &evt); + } else { + u32 flags = SDL_WINDOW_FLAGS; + if (ctx->os_handle) flags &= ~SDL_RESIZABLE; + ctx->screen = SDL_SetVideoMode(width, height, 0, flags); + } + gf_mx_v(ctx->evt_mx); + return ctx->screen ? GF_OK : GF_IO_ERR; +} + + +u32 SDLVid_EventProc(void *par) +{ + u32 flags, last_mouse_move; + Bool cursor_on; + SDL_Event sdl_evt; + GF_Event gpac_evt; + GF_VideoOutput *dr = (GF_VideoOutput *)par; + SDLVID(); + + flags = SDL_WasInit(SDL_INIT_VIDEO); + if (!(flags & SDL_INIT_VIDEO)) { + if (SDL_InitSubSystem(SDL_INIT_VIDEO)<0) { + ctx->sdl_th_state = 3; + return 0; + } + } + + /*note we create the window on the first resize. This is the only way to keep synchronous with X and cope with GL threading*/ + + ctx->sdl_th_state = 1; + ctx->curs_def = SDL_GetCursor(); + ctx->curs_hand = SDLVid_LoadCursor(hand_data); + ctx->curs_collide = SDLVid_LoadCursor(collide_data); + SDL_EnableUNICODE(1); + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + + last_mouse_move = SDL_GetTicks(); + cursor_on = 1; + + /*save display resolution (SDL doesn't give acees to that)*/ + dr->max_screen_width = dr->max_screen_height = 0; +#ifdef GPAC_HAS_X11 + { + Display *dpy = XOpenDisplay(NULL); + if (dpy) { + dr->max_screen_width = DisplayWidth(dpy, DefaultScreen(dpy)); + dr->max_screen_height = DisplayHeight(dpy, DefaultScreen(dpy)); + XCloseDisplay(dpy); + } + } +#endif +#ifdef WIN32 + dr->max_screen_width = GetSystemMetrics(SM_CXSCREEN); + dr->max_screen_height = GetSystemMetrics(SM_CYSCREEN); +#endif + + + if (!ctx->os_handle) SDLVid_SetCaption(); + + while (ctx->sdl_th_state==1) { + /*after much testing: we must ensure nothing is using the event queue when resizing window. + -- under X, it throws Xlib "unexpected async reply" under linux, therefore we don't wait events, + we check for events and execute them if any + -- under Win32, the SDL_SetVideoMode deadlocks, so we don't force exclusive access to events + */ +#ifndef WIN32 + gf_mx_p(ctx->evt_mx); +#endif + while (SDL_PollEvent(&sdl_evt)) { + switch (sdl_evt.type) { + case SDL_VIDEORESIZE: + gpac_evt.type = GF_EVENT_SIZE; + gpac_evt.size.width = sdl_evt.resize.w; + gpac_evt.size.height = sdl_evt.resize.h; + dr->on_event(dr->evt_cbk_hdl, &gpac_evt); + break; + case SDL_QUIT: + if (ctx->sdl_th_state==1) { + gpac_evt.type = GF_EVENT_QUIT; + dr->on_event(dr->evt_cbk_hdl, &gpac_evt); + } else { + goto exit; + } + break; + case SDL_VIDEOEXPOSE: + gpac_evt.type = GF_EVENT_REFRESH; + dr->on_event(dr->evt_cbk_hdl, &gpac_evt); + break; + + /*keyboard*/ + case SDL_KEYDOWN: + case SDL_KEYUP: + sdl_translate_key(sdl_evt.key.keysym.sym, &gpac_evt.key); + gpac_evt.type = (sdl_evt.key.type==SDL_KEYDOWN) ? GF_EVENT_KEYDOWN : GF_EVENT_KEYUP; + dr->on_event(dr->evt_cbk_hdl, &gpac_evt); + if ((sdl_evt.key.type==SDL_KEYDOWN) && sdl_evt.key.keysym.unicode) { + gpac_evt.character.unicode_char = sdl_evt.key.keysym.unicode; + gpac_evt.type = GF_EVENT_TEXTINPUT; + dr->on_event(dr->evt_cbk_hdl, &gpac_evt); + } + break; + + /*mouse*/ + case SDL_MOUSEMOTION: + last_mouse_move = SDL_GetTicks(); + gpac_evt.type = GF_EVENT_MOUSEMOVE; + gpac_evt.mouse.x = sdl_evt.motion.x; + gpac_evt.mouse.y = sdl_evt.motion.y; + dr->on_event(dr->evt_cbk_hdl, &gpac_evt); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + last_mouse_move = SDL_GetTicks(); + gpac_evt.mouse.x = sdl_evt.motion.x; + gpac_evt.mouse.y = sdl_evt.motion.y; + gpac_evt.type = (sdl_evt.type==SDL_MOUSEBUTTONUP) ? GF_EVENT_MOUSEUP : GF_EVENT_MOUSEDOWN; + switch (sdl_evt.button.button) { + case SDL_BUTTON_LEFT: + gpac_evt.mouse.button = GF_MOUSE_LEFT; + dr->on_event(dr->evt_cbk_hdl, &gpac_evt); + break; + case SDL_BUTTON_MIDDLE: + gpac_evt.mouse.button = GF_MOUSE_MIDDLE; + dr->on_event(dr->evt_cbk_hdl, &gpac_evt); + break; + case SDL_BUTTON_RIGHT: + gpac_evt.mouse.button = GF_MOUSE_RIGHT; + dr->on_event(dr->evt_cbk_hdl, &gpac_evt); + break; +#ifdef SDL_BUTTON_WHEELUP + case SDL_BUTTON_WHEELUP: + case SDL_BUTTON_WHEELDOWN: + /*SDL handling is not perfect there, it just says up/down but no info on how much + the wheel was rotated...*/ + gpac_evt.mouse.wheel_pos = (sdl_evt.button.button==SDL_BUTTON_WHEELUP) ? FIX_ONE : -FIX_ONE; + gpac_evt.type = GF_EVENT_MOUSEWHEEL; + dr->on_event(dr->evt_cbk_hdl, &gpac_evt); + break; +#endif + } + break; + } + } + +#ifndef WIN32 + gf_mx_v(ctx->evt_mx); +#endif + + /*looks like this hides the cursor for ever when switching back from FS*/ +#if 0 + if (ctx->fullscreen && (last_mouse_move + 2000 < SDL_GetTicks()) ) { + if (cursor_on) SDL_ShowCursor(0); + cursor_on = 0; + } else if (!cursor_on) { + SDL_ShowCursor(1); + cursor_on = 1; + } +#endif + + gf_sleep(5); + } + +exit: + SDLVid_DestroyObjects(ctx); + SDL_FreeCursor(ctx->curs_hand); + SDL_FreeCursor(ctx->curs_collide); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + ctx->sdl_th_state = 3; + return 0; +} + + +GF_Err SDLVid_Setup(struct _video_out *dr, void *os_handle, void *os_display, u32 init_flags) +{ + SDLVID(); + /*we don't allow SDL hack, not stable enough*/ + //if (os_handle) return GF_NOT_SUPPORTED; + ctx->os_handle = os_handle; + ctx->is_init = 0; + ctx->output_3d_type = 0; + ctx->systems_memory = (init_flags & (GF_TERM_NO_VISUAL_THREAD | GF_TERM_NO_REGULATION) ) ? 2 : 0; + if (!SDLOUT_InitSDL()) return GF_IO_ERR; + + ctx->sdl_th_state = 0; + gf_th_run(ctx->sdl_th, SDLVid_EventProc, dr); + while (!ctx->sdl_th_state) gf_sleep(10); + if (ctx->sdl_th_state==3) { + SDLOUT_CloseSDL(); + ctx->sdl_th_state = 0; + return GF_IO_ERR; + } + ctx->is_init = 1; + return GF_OK; +} + +static void SDLVid_Shutdown(GF_VideoOutput *dr) +{ + SDLVID(); + /*remove all surfaces*/ + + if (!ctx->is_init) return; + if (ctx->sdl_th_state==1) { + SDL_Event evt; + ctx->sdl_th_state = 2; + evt.type = SDL_QUIT; + SDL_PushEvent(&evt); + while (ctx->sdl_th_state != 3) gf_sleep(100); + } + SDLOUT_CloseSDL(); + ctx->is_init = 0; +} + + +GF_Err SDLVid_SetFullScreen(GF_VideoOutput *dr, u32 bFullScreenOn, u32 *screen_width, u32 *screen_height) +{ + u32 bpp, pref_bpp; + SDLVID(); + + if (ctx->fullscreen==bFullScreenOn) return GF_OK; + + /*lock to get sure the event queue is not processed under X*/ + gf_mx_p(ctx->evt_mx); + ctx->fullscreen = bFullScreenOn; + + pref_bpp = bpp = ctx->screen->format->BitsPerPixel; + + if (ctx->fullscreen) { + u32 flags; + Bool switch_res = 0; + const char *sOpt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "SwitchResolution"); + if (sOpt && !stricmp(sOpt, "yes")) switch_res = 1; + if (!dr->max_screen_width || !dr->max_screen_height) switch_res = 1; + + flags = (ctx->output_3d_type==1) ? SDL_GL_FULLSCREEN_FLAGS : SDL_FULLSCREEN_FLAGS; + ctx->store_width = *screen_width; + ctx->store_height = *screen_height; + if (switch_res) { + u32 i; + ctx->fs_width = *screen_width; + ctx->fs_height = *screen_height; + for(i=0; i<nb_video_modes; i++) { + if (ctx->fs_width<=video_modes[2*i] && ctx->fs_height<=video_modes[2*i + 1]) { + if ((pref_bpp = SDL_VideoModeOK(video_modes[2*i], video_modes[2*i+1], bpp, flags))) { + ctx->fs_width = video_modes[2*i]; + ctx->fs_height = video_modes[2*i + 1]; + break; + } + } + } + } else { + ctx->fs_width = dr->max_screen_width; + ctx->fs_height = dr->max_screen_height; + } + ctx->screen = SDL_SetVideoMode(ctx->fs_width, ctx->fs_height, pref_bpp, flags); + /*we switched bpp, clean all objects*/ + if (bpp != pref_bpp) SDLVid_DestroyObjects(ctx); + *screen_width = ctx->fs_width; + *screen_height = ctx->fs_height; + /*GL has changed*/ + if (ctx->output_3d_type==1) { + GF_Event evt; + evt.type = GF_EVENT_VIDEO_SETUP; + dr->on_event(dr->evt_cbk_hdl, &evt); + } + } else { + SDLVid_ResizeWindow(dr, ctx->store_width, ctx->store_height); + *screen_width = ctx->store_width; + *screen_height = ctx->store_height; + } + gf_mx_v(ctx->evt_mx); + if (!ctx->screen) return GF_IO_ERR; + return GF_OK; +} + +GF_Err SDLVid_SetBackbufferSize(GF_VideoOutput *dr, u32 newWidth, u32 newHeight) +{ + u32 col; + const char *opt; + SDLVID(); + + if (ctx->output_3d_type==1) return GF_BAD_PARAM; + + if (ctx->systems_memory<2) { + opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "UseHardwareMemory"); + ctx->systems_memory = (opt && !strcmp(opt, "yes")) ? 0 : 1; + } + + /*clear screen*/ + col = SDL_MapRGB(ctx->screen->format, 0, 0, 0); + SDL_FillRect(ctx->screen, NULL, col); + SDL_Flip(ctx->screen); + + if (ctx->back_buffer && ((u32) ctx->back_buffer->w==newWidth) && ((u32) ctx->back_buffer->h==newHeight)) { + return GF_OK; + } + if (ctx->back_buffer) SDL_FreeSurface(ctx->back_buffer); + ctx->back_buffer = SDL_CreateRGBSurface(ctx->systems_memory ? SDL_SWSURFACE : SDL_HWSURFACE, newWidth, newHeight, ctx->screen->format->BitsPerPixel, ctx->screen->format->Rmask, ctx->screen->format->Gmask, ctx->screen->format->Bmask, 0); + ctx->width = newWidth; + ctx->height = newHeight; + if (!ctx->back_buffer) return GF_IO_ERR; + + return GF_OK; +} + +u32 SDLVid_MapPixelFormat(SDL_PixelFormat *format) +{ + if (format->palette) return 0; + switch (format->BitsPerPixel) { + case 16: + if ((format->Rmask==0x7c00) && (format->Gmask==0x03e0) && (format->Bmask==0x001f) ) return GF_PIXEL_RGB_555; + if ((format->Rmask==0xf800) && (format->Gmask==0x07e0) && (format->Bmask==0x001f) ) return GF_PIXEL_RGB_565; + return 0; + case 24: + if (format->Rmask==0x00FF0000) return GF_PIXEL_RGB_24; + if (format->Rmask==0x000000FF) return GF_PIXEL_BGR_24; + return 0; + case 32: + if (format->Amask==0xFF000000) return GF_PIXEL_ARGB; + if (format->Rmask==0x00FF0000) return GF_PIXEL_RGB_32; + if (format->Rmask==0x000000FF) return GF_PIXEL_BGR_32; + return 0; + default: + return 0; + } +} + +static GF_Err SDLVid_LockBackBuffer(GF_VideoOutput *dr, GF_VideoSurface *video_info, u32 do_lock) +{ + SDLVID(); + if (!ctx->back_buffer) return GF_BAD_PARAM; + if (do_lock) { + if (!video_info) return GF_BAD_PARAM; + if (SDL_LockSurface(ctx->back_buffer)<0) return GF_IO_ERR; + video_info->width = ctx->back_buffer->w; + video_info->height = ctx->back_buffer->h; + video_info->pitch = ctx->back_buffer->pitch; + video_info->video_buffer = ctx->back_buffer->pixels; + video_info->pixel_format = SDLVid_MapPixelFormat(ctx->back_buffer->format); + video_info->is_hardware_memory = !ctx->systems_memory; + } else { + SDL_UnlockSurface(ctx->back_buffer); + } + return GF_OK; +} + +static GF_Err SDLVid_Flush(GF_VideoOutput *dr, GF_Window *dest) +{ + SDL_Rect rc; + SDLVID(); + /*if resizing don't process otherwise we may deadlock*/ + if (!ctx->screen) return GF_OK; + + if (ctx->output_3d_type==1) { + SDL_GL_SwapBuffers(); + return GF_OK; + } + if (!ctx->back_buffer) return GF_BAD_PARAM; + + if ((dest->w != (u32) ctx->back_buffer->w) || (dest->h != (u32) ctx->back_buffer->h)) { + GF_VideoSurface src, dst; + + SDL_LockSurface(ctx->back_buffer); + SDL_LockSurface(ctx->screen); + + src.height = ctx->back_buffer->h; + src.width = ctx->back_buffer->w; + src.pitch = ctx->back_buffer->pitch; + src.pixel_format = SDLVid_MapPixelFormat(ctx->back_buffer->format); + src.video_buffer = ctx->back_buffer->pixels; + + dst.height = ctx->screen->h; + dst.width = ctx->screen->w; + dst.pitch = ctx->screen->pitch; + dst.pixel_format = SDLVid_MapPixelFormat(ctx->screen->format); + dst.video_buffer = ctx->screen->pixels; + + gf_stretch_bits(&dst, &src, dest, NULL, 0, 0xFF, 0, NULL, NULL); + + SDL_UnlockSurface(ctx->back_buffer); + SDL_UnlockSurface(ctx->screen); + } else { + rc.x = dest->x; rc.y = dest->y; rc.w = dest->w; rc.h = dest->h; + SDL_BlitSurface(ctx->back_buffer, NULL, ctx->screen, &rc); + } + SDL_Flip(ctx->screen); + return GF_OK; +} + +static void SDLVid_SetCursor(GF_VideoOutput *dr, u32 cursor_type) +{ + SDLVID(); + switch (cursor_type) { + case GF_CURSOR_ANCHOR: + case GF_CURSOR_TOUCH: + case GF_CURSOR_ROTATE: + case GF_CURSOR_PROXIMITY: + case GF_CURSOR_PLANE: + SDL_SetCursor(ctx->curs_hand); + break; + case GF_CURSOR_COLLIDE: + SDL_SetCursor(ctx->curs_collide); + break; + default: + SDL_SetCursor(ctx->curs_def); + break; + } +} + +static GF_Err SDLVid_ProcessEvent(GF_VideoOutput *dr, GF_Event *evt) +{ + if (!evt) return GF_OK; + switch (evt->type) { + case GF_EVENT_SET_CURSOR: + SDLVid_SetCursor(dr, evt->cursor.cursor_type); + break; + case GF_EVENT_SET_CAPTION: + SDL_WM_SetCaption(evt->caption.caption, NULL); + break; + case GF_EVENT_SHOWHIDE: + /*the only way to have proper show/hide with SDL is to shutdown the video system and reset it up + which we don't want to do since the setup MUST occur in the rendering thread for some configs (openGL)*/ + return GF_NOT_SUPPORTED; + case GF_EVENT_SIZE: + SDLVid_ResizeWindow(dr, evt->size.width, evt->size.height); + break; + case GF_EVENT_VIDEO_SETUP: + { + SDLVID(); + switch (evt->setup.opengl_mode) { + case 0: + ctx->output_3d_type = 0; + return SDLVid_SetBackbufferSize(dr, evt->setup.width, evt->setup.height); + case 1: + ctx->output_3d_type = 1; + return SDLVid_ResizeWindow(dr, evt->setup.width, evt->setup.height); + case 2: + /*find a way to do that in SDL*/ + ctx->output_3d_type = 2; + return GF_NOT_SUPPORTED; + } + } + break; + } + return GF_OK; +} + + + + +void *SDL_NewVideo() +{ + SDLVidCtx *ctx; + GF_VideoOutput *driv; + + driv = malloc(sizeof(GF_VideoOutput)); + memset(driv, 0, sizeof(GF_VideoOutput)); + GF_REGISTER_MODULE_INTERFACE(driv, GF_VIDEO_OUTPUT_INTERFACE, "SDL Video Output", "gpac distribution"); + + ctx = malloc(sizeof(SDLVidCtx)); + memset(ctx, 0, sizeof(SDLVidCtx)); + ctx->sdl_th = gf_th_new("SDLVideo"); + ctx->evt_mx = gf_mx_new("SDLEvents"); + + driv->opaque = ctx; + driv->Setup = SDLVid_Setup; + driv->Shutdown = SDLVid_Shutdown; + driv->SetFullScreen = SDLVid_SetFullScreen; + driv->Flush = SDLVid_Flush; + driv->ProcessEvent = SDLVid_ProcessEvent; + /*no offscreen opengl with SDL*/ + driv->hw_caps |= GF_VIDEO_HW_OPENGL; + + /*no YUV hardware blitting in SDL (only overlays)*/ + + /*NO BLIT in SDL (we rely on GPAC to do it by soft)*/ + driv->Blit = NULL; + driv->LockBackBuffer = SDLVid_LockBackBuffer; + driv->LockOSContext = NULL; + return driv; +} + +void SDL_DeleteVideo(void *ifce) +{ + GF_VideoOutput *dr = (GF_VideoOutput *)ifce; + SDLVID(); + gf_th_del(ctx->sdl_th); + gf_mx_del(ctx->evt_mx); + free(ctx); + free(dr); +} + diff --git a/modules/sdl_out/video2d.c b/modules/sdl_out/video2d.c new file mode 100644 index 0000000..b6b91dc --- /dev/null +++ b/modules/sdl_out/video2d.c @@ -0,0 +1,28 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / SDL audio and video module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "sdl_out.h" +#include <gpac/constants.h> + + diff --git a/modules/soft_raster/Makefile b/modules/soft_raster/Makefile new file mode 100644 index 0000000..459dcbb --- /dev/null +++ b/modules/soft_raster/Makefile @@ -0,0 +1,69 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/soft_raster + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#big-endian config (needed for ARGB pixel format) +ifeq ($(IS_BIGENDIAN), yes) +CFLAGS+=-DEVG_BIG_ENDIAN +endif + + +#common obj +OBJS= ftgrays.o raster_load.o raster_565.o raster_argb.o raster_rgb.o stencil.o surface.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_soft_raster.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols rast_soft.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/soft_raster/ftgrays.c b/modules/soft_raster/ftgrays.c new file mode 100644 index 0000000..2fb9bfd --- /dev/null +++ b/modules/soft_raster/ftgrays.c @@ -0,0 +1,783 @@ +/***************************************************************************/ +/* */ +/* ftgrays.c */ +/* */ +/* A new `perfect' anti-aliasing renderer (body). */ +/* */ +/* Copyright 2000-2001, 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* This is a new anti-aliasing scan-converter for FreeType 2. The */ + /* algorithm used here is _very_ different from the one in the standard */ + /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */ + /* coverage of the outline on each pixel cell. */ + /* */ + /* It is based on ideas that I initially found in Raph Levien's */ + /* excellent LibArt graphics library (see http://www.levien.com/libart */ + /* for more information, though the web pages do not tell anything */ + /* about the renderer; you'll have to dive into the source code to */ + /* understand how it works). */ + /* */ + /* Note, however, that this is a _very_ different implementation */ + /* compared to Raph's. Coverage information is stored in a very */ + /* different way, and I don't use sorted vector paths. Also, it doesn't */ + /* use floating point values. */ + /* */ + /* This renderer has the following advantages: */ + /* */ + /* - It doesn't need an intermediate bitmap. Instead, one can supply a */ + /* callback function that will be called by the renderer to draw gray */ + /* spans on any target surface. You can thus do direct composition on */ + /* any kind of bitmap, provided that you give the renderer the right */ + /* callback. */ + /* */ + /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */ + /* each pixel cell. */ + /* */ + /* - It performs a single pass on the outline (the `standard' FT2 */ + /* renderer makes two passes). */ + /* */ + /* - It can easily be modified to render to _any_ number of gray levels */ + /* cheaply. */ + /* */ + /* - For small (< 20) pixel sizes, it is faster than the standard */ + /* renderer. */ + /* */ + /*************************************************************************/ + + +/* + GPAC version modifications: + * removed all cubic/quadratic support (present in GPAC path objects) + * moved span data memoru to dynamic allocation + * bypassed Y-sorting of cells by using an array of scanlines: a bit more consuming + in memory, but faster cell sorting (X-sorting only) +*/ + +#include "rast_soft.h" + +#include <limits.h> + +#define ErrRaster_MemoryOverflow -4 +#define ErrRaster_Invalid_Mode -2 +#define ErrRaster_Invalid_Outline -1 + + +#define GPAC_FIX_BITS 16 +#define PIXEL_BITS 8 +#define PIXEL_BITS_DIFF 8 /*GPAC_FIX_BITS - PIXEL_BITS*/ + +#define ONE_PIXEL ( 1L << PIXEL_BITS ) +#define PIXEL_MASK ( -1L << PIXEL_BITS ) +#define TRUNC( x ) ( (TCoord)((x) >> PIXEL_BITS) ) +#define SUBPIXELS( x ) ( (TPos)(x) << PIXEL_BITS ) +#define FLOOR( x ) ( (x) & -ONE_PIXEL ) +#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL ) +#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL ) +#define UPSCALE( x ) ( (x) >> ( PIXEL_BITS_DIFF) ) +#define DOWNSCALE( x ) ( (x) << ( PIXEL_BITS_DIFF) ) + + +/*************************************************************************/ +/* */ +/* TYPE DEFINITIONS */ +/* */ + +/* don't change the following types to FT_Int or FT_Pos, since we might */ +/* need to define them to "float" or "double" when experimenting with */ +/* new algorithms */ + +typedef int TCoord; /* integer scanline/pixel coordinate */ +typedef long TPos; /* sub-pixel coordinate */ + +/* determine the type used to store cell areas. This normally takes at */ +/* least PIXEL_BYTES*2 + 1. On 16-bit systems, we need to use `long' */ +/* instead of `int', otherwise bad things happen */ + +/* approximately determine the size of integers using an ANSI-C header */ +#if UINT_MAX == 0xFFFFU +typedef long TArea; +#else +typedef int TArea; +#endif + + + /* maximal number of gray spans in a call to the span callback */ +#define FT_MAX_GRAY_SPANS 64 + +typedef struct TCell_ +{ + TCoord x; + int cover; + TArea area; +} AACell; + +typedef struct +{ + AACell *cells; + int alloc, num; +} AAScanline; + + +typedef struct TRaster_ +{ + AAScanline *scanlines; + int max_lines; + TPos min_ex, max_ex, min_ey, max_ey; + TCoord ex, ey; + TPos x, y, last_ey; + TArea area; + int cover; + + EVG_Span gray_spans[FT_MAX_GRAY_SPANS]; + int num_gray_spans; + EVG_Raster_Span_Func render_span; + void *render_span_data; + +#ifdef INLINE_POINT_CONVERSION + GF_Matrix2D *mx; +#endif +} TRaster; + +#define AA_CELL_STEP_ALLOC 8 + +static GFINLINE void gray_record_cell( TRaster *raster ) +{ + if (( raster->area | raster->cover) && (raster->ey<raster->max_ey)) { + AACell *cell; + int y = raster->ey - raster->min_ey; + if (y>=0) { + AAScanline *sl = &raster->scanlines[y]; + if (sl->num >= sl->alloc) { + sl->cells = (AACell*)realloc(sl->cells, sizeof(AACell)* (sl->alloc + AA_CELL_STEP_ALLOC)); + sl->alloc += AA_CELL_STEP_ALLOC; + } + cell = &sl->cells[sl->num]; + sl->num++; + /*clip cell */ + if (raster->ex<raster->min_ex) cell->x = (TCoord) -1; + else if (raster->ex>raster->max_ex) cell->x = (TCoord) (raster->max_ex - raster->min_ex); + else cell->x = (TCoord)(raster->ex - raster->min_ex); + cell->area = raster->area; + cell->cover = raster->cover; + } + } +} + +static GFINLINE void gray_set_cell( TRaster *raster, TCoord ex, TCoord ey ) +{ + if ((raster->ex != ex) || (raster->ey != ey)) { + gray_record_cell(raster); + raster->ex = ex; + raster->ey = ey; + raster->area = 0; + raster->cover = 0; + } +} + + +#ifdef INLINE_POINT_CONVERSION + +static GFINLINE void evg_translate_point(GF_Matrix2D *mx, EVG_Vector *pt, TPos *x, TPos *y) +{ + Fixed _x, _y; + _x = pt->x; + _y = pt->y; + gf_mx2d_apply_coords(mx, &_x, &_y); +#ifdef GPAC_FIXED_POINT + *x = UPSCALE(_x); + *y = UPSCALE(_y); +#else + *x = (s32) (_x * ONE_PIXEL); + *y = (s32) (_y * ONE_PIXEL); +#endif +} +#endif + +static GFINLINE int gray_move_to( EVG_Vector* to, EVG_Raster raster) +{ + TPos x, y; + TCoord ex, ey; + + /* record current cell, if any */ + gray_record_cell(raster); + +#ifdef INLINE_POINT_CONVERSION + evg_translate_point(raster->mx, to, &x, &y); +#else + x = UPSCALE( to->x ); + y = UPSCALE( to->y ); +#endif + ex = TRUNC(x); + ey = TRUNC(y); + if ( ex < raster->min_ex ) ex = (TCoord)(raster->min_ex - 1); + raster->area = 0; + raster->cover = 0; + gray_set_cell( raster, ex, ey ); + raster->last_ey = SUBPIXELS( ey ); + + raster->x = x; + raster->y = y; + return 0; +} + +/*************************************************************************/ +/* */ +/* Render a scanline as one or more cells. */ +/* */ +static void gray_render_scanline( TRaster *raster, TCoord ey, TPos x1, TCoord y1, TPos x2, TCoord y2) +{ + TCoord ex1, ex2, fx1, fx2, delta; + long p, first; + long dx; + int incr, lift, mod, rem; + + dx = x2 - x1; + ex1 = TRUNC( x1 ); /* if (ex1 >= raster->max_ex) ex1 = raster->max_ex-1; */ + ex2 = TRUNC( x2 ); /* if (ex2 >= raster->max_ex) ex2 = raster->max_ex-1; */ + fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) ); + fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) ); + + /* trivial case. Happens often */ + if ( y1 == y2 ) { + gray_set_cell( raster, ex2, ey ); + return; + } + + /* everything is located in a single cell. That is easy! */ + if ( ex1 == ex2 ) { + delta = y2 - y1; + raster->area += (TArea)( fx1 + fx2 ) * delta; + raster->cover += delta; + return; + } + + /* ok, we'll have to render a run of adjacent cells on the same */ + /* scanline... */ + p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 ); + first = ONE_PIXEL; + incr = 1; + + if ( dx < 0 ) { + p = fx1 * ( y2 - y1 ); + first = 0; + incr = -1; + dx = -dx; + } + delta = (TCoord)( p / dx ); + mod = (TCoord)( p % dx ); + if ( mod < 0 ) { + delta--; + mod += (TCoord)dx; + } + raster->area += (TArea)( fx1 + first ) * delta; + raster->cover += delta; + + ex1 += incr; + gray_set_cell( raster, ex1, ey ); + y1 += delta; + + if ( ex1 != ex2 ) { + p = ONE_PIXEL * ( y2 - y1 + delta ); + lift = (TCoord)( p / dx ); + rem = (TCoord)( p % dx ); + if ( rem < 0 ) { + lift--; + rem += (TCoord)dx; + } + mod -= (int) dx; + + while ( ex1 != ex2 ) { + delta = lift; + mod += rem; + if ( mod >= 0 ) { + mod -= (TCoord)dx; + delta++; + } + + raster->area += (TArea)ONE_PIXEL * delta; + raster->cover += delta; + y1 += delta; + ex1 += incr; + gray_set_cell( raster, ex1, ey ); + } + } + delta = y2 - y1; + raster->area += (TArea)( fx2 + ONE_PIXEL - first ) * delta; + raster->cover += delta; +} + + +/*************************************************************************/ +/* */ +/* Render a given line as a series of scanlines. */ +static GFINLINE void gray_render_line(TRaster *raster, TPos to_x, TPos to_y, int is_line) +{ + TCoord min, max; + TCoord ey1, ey2, fy1, fy2; + TPos x, x2; + long dx, dy; + long p, first; + int delta, rem, mod, lift, incr; + + /*point filtering*/ +#if 0 + if (is_line) { + dx = (to_x - raster->x)>>PIXEL_BITS; + dy = (to_y - raster->y)>>PIXEL_BITS; + if ( dx*dx + dy*dy < 1) return; + } +#endif + + + ey1 = TRUNC( raster->last_ey ); + ey2 = TRUNC( to_y ); /* if (ey2 >= raster->max_ey) ey2 = raster->max_ey-1; */ + fy1 = (TCoord)( raster->y - raster->last_ey ); + fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) ); + + dx = to_x - raster->x; + dy = to_y - raster->y; + + /* perform vertical clipping */ + min = ey1; + max = ey2; + if ( ey1 > ey2 ) { + min = ey2; + max = ey1; + } + if ( min >= raster->max_ey || max < raster->min_ey ) goto End; + + /* everything is on a single scanline */ + if ( ey1 == ey2 ) { + gray_render_scanline( raster, ey1, raster->x, fy1, to_x, fy2 ); + goto End; + } + /* vertical line - avoid calling gray_render_scanline */ + incr = 1; + if ( dx == 0 ) { + TCoord ex = TRUNC( raster->x ); + TCoord two_fx = (TCoord)( ( raster->x - SUBPIXELS( ex ) ) << 1 ); + TPos area; + + first = ONE_PIXEL; + if ( dy < 0 ) { + first = 0; + incr = -1; + } + + delta = (int)( first - fy1 ); + raster->area += (TArea)two_fx * delta; + raster->cover += delta; + ey1 += incr; + + gray_set_cell( raster, ex, ey1 ); + + delta = (int)( first + first - ONE_PIXEL ); + area = (TArea)two_fx * delta; + while ( ey1 != ey2 ) { + raster->area += area; + raster->cover += delta; + ey1 += incr; + gray_set_cell( raster, ex, ey1 ); + } + delta = (int)( fy2 - ONE_PIXEL + first ); + raster->area += (TArea)two_fx * delta; + raster->cover += delta; + goto End; + } + /* ok, we have to render several scanlines */ + p = ( ONE_PIXEL - fy1 ) * dx; + first = ONE_PIXEL; + incr = 1; + + if ( dy < 0 ) { + p = fy1 * dx; + first = 0; + incr = -1; + dy = -dy; + } + + delta = (int)( p / dy ); + mod = (int)( p % dy ); + if ( mod < 0 ) { + delta--; + mod += (TCoord)dy; + } + x = raster->x + delta; + gray_render_scanline( raster, ey1, raster->x, fy1, x, (TCoord)first ); + + ey1 += incr; + gray_set_cell( raster, TRUNC( x ), ey1 ); + + if ( ey1 != ey2 ) { + p = ONE_PIXEL * dx; + lift = (int)( p / dy ); + rem = (int)( p % dy ); + if ( rem < 0 ) { + lift--; + rem += (int)dy; + } + mod -= (int)dy; + + while ( ey1 != ey2 ) { + delta = lift; + mod += rem; + if ( mod >= 0 ) { + mod -= (int)dy; + delta++; + } + + x2 = x + delta; + gray_render_scanline( raster, ey1, x, (TCoord)( ONE_PIXEL - first ), x2, (TCoord)first ); + x = x2; + + ey1 += incr; + gray_set_cell( raster, TRUNC( x ), ey1 ); + } + } + + gray_render_scanline( raster, ey1, x, (TCoord)( ONE_PIXEL - first ), to_x, fy2 ); + +End: + raster->x = to_x; + raster->y = to_y; + raster->last_ey = SUBPIXELS( ey2 ); +} + + + +static int EVG_Outline_Decompose(EVG_Outline *outline, TRaster *user) +{ + EVG_Vector v_last; + EVG_Vector v_start; + EVG_Vector* point; + EVG_Vector* limit; + char* tags; + int n; /* index of contour in outline */ + int first; /* index of first point in contour */ + char tag; /* current point's state */ +#ifdef INLINE_POINT_CONVERSION + TPos _x, _y; +#endif + + first = 0; + for ( n = 0; n < outline->n_contours; n++ ) { + int last; /* index of last point in contour */ + last = outline->contours[n]; + limit = outline->points + last; + + v_start = outline->points[first]; + v_last = outline->points[last]; + + point = outline->points + first; + tags = (char*) outline->tags + first; + tag = tags[0]; + gray_move_to(&v_start, user); + while ( point < limit ) { + point++; + tags++; +#ifdef INLINE_POINT_CONVERSION + evg_translate_point(user->mx, point, &_x, &_y); + gray_render_line(user, _x, _y, 1); +#else + gray_render_line(user, UPSCALE(point->x), UPSCALE( point->y), 1); +#endif + } +#ifdef INLINE_POINT_CONVERSION + evg_translate_point(user->mx, &v_start, &_x, &_y); + gray_render_line(user, _x, _y, 0); +#else + /* close the contour with a line segment */ + gray_render_line(user, UPSCALE(v_start.x), UPSCALE( v_start.y), 0); +#endif + first = last + 1; + } + return 0; +} + + + +#define SWAP_CELLS( a, b, temp ) { \ + temp = *(a); \ + *(a) = *(b); \ + *(b) = temp; \ + } + + + /* This is a non-recursive quicksort that directly process our cells */ + /* array. It should be faster than calling the stdlib qsort(), and we */ + /* can even tailor our insertion threshold... */ + +#define QSORT_THRESHOLD 9 /* below this size, a sub-array will be sorted */ + /* through a normal insertion sort */ + +static void gray_quick_sort( AACell *cells, int count ) +{ + AACell *stack[80]; /* should be enough ;-) */ + AACell **top; /* top of stack */ + AACell *base, *limit; + AACell temp; + + limit = cells + count; + base = cells; + top = stack; + + for (;;) { + int len = (int)( limit - base ); + AACell *i, *j, *pivot; + if ( len > QSORT_THRESHOLD ) { + /* we use base + len/2 as the pivot */ + pivot = base + len / 2; + SWAP_CELLS( base, pivot, temp ); + + i = base + 1; + j = limit - 1; + + /* now ensure that *i <= *base <= *j */ + if(j->x < i->x) + SWAP_CELLS( i, j, temp ); + + if(base->x < i->x) + SWAP_CELLS( base, i, temp ); + + if(j->x < base->x) + SWAP_CELLS( base, j, temp ); + + for (;;) { + int x = base->x; + do i++; while( i->x < x ); + do j--; while( x < j->x ); + + if ( i > j ) + break; + + SWAP_CELLS( i, j, temp ); + } + + SWAP_CELLS( base, j, temp ); + + /* now, push the largest sub-array */ + if ( j - base > limit - i ) { + top[0] = base; + top[1] = j; + base = i; + } else { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } else { + /* the sub-array is small, perform insertion sort */ + j = base; + i = j + 1; + + for ( ; i < limit; j = i, i++ ) { + for ( ; j[1].x < j->x; j-- ) { + SWAP_CELLS( j + 1, j, temp ); + if ( j == base ) + break; + } + } + if ( top > stack ) { + top -= 2; + base = top[0]; + limit = top[1]; + } else + break; + } + } +} + + +static void gray_hline( TRaster *raster, TCoord x, TCoord y, TPos area, int acount, Bool zero_non_zero_rule) +{ + EVG_Span* span; + int count; + int coverage; + + x += (TCoord)raster->min_ex; + if (x>=raster->max_ex) return; + y += (TCoord)raster->min_ey; + + /* compute the coverage line's coverage, depending on the */ + /* outline fill rule */ + /* */ + /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ + /* */ + coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) ); + /* use range 0..256 */ + if ( coverage < 0 ) + coverage = -coverage; + + if (zero_non_zero_rule) { + /* normal non-zero winding rule */ + if ( coverage >= 256 ) + coverage = 255; + } else { + coverage &= 511; + + if ( coverage > 256 ) + coverage = 512 - coverage; + else if ( coverage == 256 ) + coverage = 255; + } + + if ( coverage ) { + /* see if we can add this span to the current list */ + count = raster->num_gray_spans; + span = raster->gray_spans + count - 1; + if ( count > 0 && + (int)span->x + span->len == (int)x && + span->coverage == coverage ) + { + span->len = (unsigned short)( span->len + acount ); + return; + } + + if (count >= FT_MAX_GRAY_SPANS ) { + raster->render_span(y, count, raster->gray_spans, raster->render_span_data ); + raster->num_gray_spans = 0; + + count = 0; + span = raster->gray_spans; + } else + span++; + + /* add a gray span to the current list */ + span->x = (short)x; + span->len = (unsigned short)acount; + span->coverage = (unsigned char)coverage; + raster->num_gray_spans++; + } +} + +static void gray_sweep_line( TRaster *raster, AAScanline *sl, int y, Bool zero_non_zero_rule) +{ + TCoord x, cover; + TArea area; + AACell *start, *cur; + + cur = sl->cells; + cover = 0; + raster->num_gray_spans = 0; + + while (sl->num) { + start = cur; + x = start->x; + area = start->area; + cover += start->cover; + /* accumulate all start cells */ + while(--sl->num) { + ++cur ; + if (cur->x != start->x ) + break; + + area += cur->area; + cover += cur->cover; + } + + /* if the start cell has a non-null area, we must draw an */ + /* individual gray pixel there */ + if ( area && x >= 0 ) { + gray_hline( raster, x, y, cover * ( ONE_PIXEL * 2 ) - area, 1, zero_non_zero_rule); + x++; + } + if ( x < 0 ) x = 0; + + /* draw a gray span between the start cell and the current one */ + if ( cur->x > x ) + gray_hline( raster, x, y, cover * ( ONE_PIXEL * 2 ), cur->x - x, zero_non_zero_rule); + } + raster->render_span(y + raster->min_ey, raster->num_gray_spans, raster->gray_spans, raster->render_span_data ); +} + + +int evg_raster_render(EVG_Raster raster, EVG_Raster_Params* params) +{ + Bool zero_non_zero_rule; + int i, size_y; + EVG_Outline* outline = (EVG_Outline*)params->source; + /* return immediately if the outline is empty */ + if ( outline->n_points == 0 || outline->n_contours <= 0 ) return 0; + + raster->render_span = (EVG_Raster_Span_Func) params->gray_spans; + raster->render_span_data = params->user; + + /* Set up state in the raster object */ + raster->min_ex = params->clip_xMin; + raster->min_ey = params->clip_yMin; + raster->max_ex = params->clip_xMax; + raster->max_ey = params->clip_yMax; + +#ifdef INLINE_POINT_CONVERSION + raster->mx = params->mx; +#endif + + size_y = raster->max_ey - raster->min_ey; + if (raster->max_lines < size_y) { + raster->scanlines = (AAScanline*)realloc(raster->scanlines, sizeof(AAScanline)*size_y); + memset(&raster->scanlines[raster->max_lines], 0, sizeof(AAScanline)*(size_y-raster->max_lines) ); + raster->max_lines = size_y; + } + + raster->ex = raster->max_ex+1; + raster->ey = raster->max_ey+1; + raster->cover = 0; + raster->area = 0; + EVG_Outline_Decompose(outline, raster); + gray_record_cell( raster ); + + /*store odd/even rule*/ + zero_non_zero_rule = (outline->flags & GF_PATH_FILL_ZERO_NONZERO) ? 1 : 0; + + /* sort each scanline and render it*/ + for (i=0; i<size_y; i++) { + AAScanline *sl = &raster->scanlines[i]; + if (sl->num) { + if (sl->num>1) gray_quick_sort(sl->cells, sl->num); + gray_sweep_line(raster, sl, i, zero_non_zero_rule); + sl->num = 0; + } + } + +#if 0 + for (i=0; i<raster->max_lines; i++) { + free(raster->scanlines[i].cells); + } + free(raster->scanlines); + raster->scanlines = 0; + raster->max_lines = 0; +#endif + + return 0; +} + +EVG_Raster evg_raster_new() +{ + TRaster *raster; + GF_SAFEALLOC(raster , TRaster); + return raster; +} + +void evg_raster_del(EVG_Raster raster) +{ + int i; + for (i=0; i<raster->max_lines; i++) { + free(raster->scanlines[i].cells); + } + free(raster->scanlines); + free(raster); +} + +/* END */ diff --git a/modules/soft_raster/rast_soft.def b/modules/soft_raster/rast_soft.def new file mode 100644 index 0000000..7b6dded --- /dev/null +++ b/modules/soft_raster/rast_soft.def @@ -0,0 +1,6 @@ +LIBRARY gm_soft_raster.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/soft_raster/rast_soft.h b/modules/soft_raster/rast_soft.h new file mode 100644 index 0000000..7653352 --- /dev/null +++ b/modules/soft_raster/rast_soft.h @@ -0,0 +1,362 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / software 2D rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + + +#ifndef _RAST_SOFT_H_ +#define _RAST_SOFT_H_ + +#include <gpac/modules/raster2d.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +/*RGB 555 support is disabled by default*/ +//#define GF_RGB_555_SUPORT + +/*for symbian enable 4k color depth (RGB444, 4096 colors) since a good amount of devices use that*/ +#ifdef __SYMBIAN32__ +#define GF_RGB_444_SUPORT +#endif + + +typedef struct _evg_surface EVGSurface; + +/*base stencil stack*/ +#define EVGBASESTENCIL \ + u32 type; \ + void (*fill_run)(struct _evg_base_stencil *p, EVGSurface *surf, s32 x, s32 y, u32 count); \ + GF_Matrix2D pmat; \ + GF_Matrix2D smat; \ + GF_Rect frame; \ + GF_ColorMatrix cmat; \ + +typedef struct _evg_base_stencil +{ + EVGBASESTENCIL +} EVGStencil; + +/*define this macro to convert points in the outline decomposition rather than when attaching path +to surface. When point conversion is inlined in the raster, memory usage is lower (no need for temp storage)*/ +#define INLINE_POINT_CONVERSION + +#ifdef INLINE_POINT_CONVERSION +typedef struct __vec2f EVG_Vector; +#else +typedef struct +{ + s32 x; + s32 y; +} EVG_Vector; +#endif + +typedef struct +{ + s32 n_contours; + s32 n_points; + EVG_Vector *points; + u8 *tags; + s32 *contours; + s32 flags; /*same as path flags*/ +} EVG_Outline; + +typedef struct TRaster_ *EVG_Raster; + +typedef struct EVG_Span_ +{ + short x; + unsigned short len; + unsigned char coverage; +} EVG_Span; + +typedef void (*EVG_SpanFunc)(int y, int count, EVG_Span *spans, void *user); +#define EVG_Raster_Span_Func EVG_SpanFunc + +typedef struct EVG_Raster_Params_ +{ + EVG_Outline *source; +#ifdef INLINE_POINT_CONVERSION + GF_Matrix2D *mx; +#endif + EVG_SpanFunc gray_spans; + + s32 clip_xMin, clip_yMin, clip_xMax, clip_yMax; + + void *user; +} EVG_Raster_Params; + + +EVG_Raster evg_raster_new(); +void evg_raster_del(EVG_Raster raster); +int evg_raster_render(EVG_Raster raster, EVG_Raster_Params *params); + +/*the surface object - currently only ARGB/RGB32, RGB/BGR and RGB555/RGB565 supported*/ +struct _evg_surface +{ + /*surface info*/ + char *pixels; + u32 pixelFormat, BPP; + u32 width, height, stride; + Bool center_coords; + + /*color buffer for variable stencils - size of width*/ + u32 *stencil_pix_run; + + /*aliasing flag (on/off only)*/ + u8 AALevel; + /*default texture filter level*/ + u32 texture_filter; + + u32 useClipper; + GF_IRect clipper; + + GF_Rect path_bounds; + + /*complete path transform (including page flipping/recenter)*/ + GF_Matrix2D mat; + + /*stencil currently used*/ + EVGStencil *sten; + + void *raster_cbk; + /*fills line pixels without any blending operation*/ + void (*raster_fill_run_no_alpha)(void *cbk, u32 x, u32 y, u32 run_h_len, GF_Color color); + /*fills line pixels without blending operation - alpha combines both fill color and anti-aliasing blending*/ + void (*raster_fill_run_alpha)(void *cbk, u32 x, u32 y, u32 run_h_len, GF_Color color, u32 alpha); + + + /*in solid color mode to speed things*/ + u32 fill_col; + u32 fill_565; + +#ifdef GF_RGB_444_SUPORT + u32 fill_444; +#endif + +#ifdef GF_RGB_555_SUPORT + u32 fill_555; +#endif + + /*FreeType raster*/ + EVG_Raster raster; + + /*FreeType outline (path converted to ft)*/ + EVG_Outline ftoutline; + EVG_Raster_Params ftparams; + +#ifndef INLINE_POINT_CONVERSION + /*transformed point list*/ + u32 pointlen; + EVG_Vector *points; +#endif +}; + +/*solid color brush*/ +typedef struct +{ + EVGBASESTENCIL + GF_Color color; +} EVG_Brush; + +/*max number of interpolation points*/ +#define EVGGRADIENTSLOTS 12 + +/*(2^EVGGRADIENTBITS)-1 colors in gradient. Below 6 bits the gradient is crappy */ +#define EVGGRADIENTBITS 10 + +/*scale factor for linear - MUST BE greater than EVGGRADIENTBITS. 12 should be enough for decent results in both fixed point and float versions*/ +#define EVGGRADIENTSCALEBITS 12 + +/*base gradient stencil*/ +#define EVGGRADIENT \ + s32 mod; \ + u32 pre[(1<<EVGGRADIENTBITS)]; \ + u32 col[EVGGRADIENTSLOTS]; \ + Fixed pos[EVGGRADIENTSLOTS]; \ + u8 alpha; \ + +typedef struct +{ + EVGBASESTENCIL + EVGGRADIENT +} EVG_BaseGradient; + +/*linear gradient*/ +typedef struct +{ + EVGBASESTENCIL + EVGGRADIENT + + GF_Point2D start, end; + GF_Matrix2D vecmat; + s32 curp; + Fixed pos_ft; +} EVG_LinearGradient; + +/*radial gradient*/ +typedef struct +{ + EVGBASESTENCIL + EVGGRADIENT + + GF_Point2D center, focus, radius; + /*drawing state vars*/ + GF_Point2D cur_p, d_f, d_i; + Fixed rad; +} EVG_RadialGradient; + +/*texture stencil - should be reworked, is very slow*/ +typedef struct +{ + EVGBASESTENCIL + u32 width, height, stride; + u32 pixel_format, Bpp; + char *pixels; + + GF_Point2D cur_pt; + Fixed cur_y, inc_x, inc_y; + + u32 mod, filter; + u32 replace_col; + Bool cmat_is_replace; + + u8 alpha; + + /*YUV->RGB local buffer*/ + unsigned char *conv_buf; + u32 conv_size; + unsigned char *orig_buf; + u32 orig_stride, orig_format; + Bool is_converted; + + Bool owns_texture; + + u32 (*tx_get_pixel)(char *pix); +} EVG_Texture; + +#define GF_TEXTURE_FILTER_DEFAULT GF_TEXTURE_FILTER_HIGH_SPEED + + +void evg_radial_init(EVG_RadialGradient *_this) ; +void evg_bmp_init(EVGStencil *p); +void evg_set_texture_active(EVGStencil *st); + + +GF_STENCIL evg_stencil_new(GF_Raster2D *, GF_StencilType type); +void evg_stencil_delete(GF_STENCIL st); +GF_Err evg_stencil_set_matrix(GF_STENCIL st, GF_Matrix2D *mx); +GF_Err evg_stencil_set_brush_color(GF_STENCIL st, GF_Color c); +GF_Err evg_stencil_set_linear_gradient(GF_STENCIL st, Fixed start_x, Fixed start_y, Fixed end_x, Fixed end_y); +GF_Err evg_stencil_set_radial_gradient(GF_STENCIL st, Fixed cx, Fixed cy, Fixed fx, Fixed fy, Fixed x_radius, Fixed y_radius); +GF_Err evg_stencil_set_gradient_interpolation(GF_STENCIL p, Fixed *pos, GF_Color *col, u32 count); +GF_Err evg_stencil_set_gradient_mode(GF_STENCIL p, GF_GradientMode mode); + +GF_Err evg_stencil_set_alpha(GF_STENCIL st, u8 alpha); + +/*texture only*/ +GF_Err evg_stencil_set_texture(GF_STENCIL st, char *pixels, u32 width, u32 height, u32 stride, GF_PixelFormat pixelFormat, GF_PixelFormat destination_format_hint, Bool no_copy); +GF_Err evg_stencil_set_tiling(GF_STENCIL st, GF_TextureTiling mode); +GF_Err evg_stencil_set_filter(GF_STENCIL st, GF_TextureFilter filter_mode); +GF_Err evg_stencil_set_color_matrix(GF_STENCIL st, GF_ColorMatrix *cmat); +GF_Err evg_stencil_reset_color_matrix(GF_STENCIL st); +GF_Err evg_stencil_create_texture(GF_STENCIL st, u32 width, u32 height, GF_PixelFormat pixelFormat); + + + +GF_SURFACE evg_surface_new(GF_Raster2D *, Bool center_coords); +void evg_surface_delete(GF_SURFACE _this); +void evg_surface_detach(GF_SURFACE _this); +GF_Err evg_surface_attach_to_buffer(GF_SURFACE _this, char *pixels, u32 width, u32 height, u32 stride, GF_PixelFormat pixelFormat); +GF_Err evg_surface_attach_to_texture(GF_SURFACE _this, GF_STENCIL sten); +GF_Err evg_surface_attach_to_callbacks(GF_SURFACE _this, GF_RasterCallback *callbacks, u32 width, u32 height); + +GF_Err evg_surface_set_matrix(GF_SURFACE surf, GF_Matrix2D *mat); +GF_Err evg_surface_set_raster_level(GF_SURFACE surf , GF_RasterLevel RasterSetting); +GF_Err evg_surface_set_clipper(GF_SURFACE surf, GF_IRect *rc); +GF_Err evg_surface_set_path(GF_SURFACE surf, GF_Path *gp); +GF_Err evg_surface_fill(GF_SURFACE surf, GF_STENCIL stencil); +GF_Err evg_surface_clear(GF_SURFACE surf, GF_IRect *rc, u32 color); + + +/*FT raster callbacks - it's better to have a bit bigger code than using extra +get/set pixels routines on the surface*/ +void evg_argb_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_argb_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_argb_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +GF_Err evg_surface_clear_argb(GF_SURFACE surf, GF_IRect rc, GF_Color col); + +void evg_rgba_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_rgba_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_rgba_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +GF_Err evg_surface_clear_rgba(GF_SURFACE surf, GF_IRect rc, GF_Color col); + +void evg_rgb32_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_rgb32_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_rgb32_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); + +void evg_rgb_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_rgb_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_rgb_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +GF_Err evg_surface_clear_rgb(GF_SURFACE surf, GF_IRect rc, GF_Color col); + +void evg_bgr_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_bgr_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_bgr_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +GF_Err evg_surface_clear_bgr(GF_SURFACE surf, GF_IRect rc, GF_Color col); + +void evg_565_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_565_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_565_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +GF_Err evg_surface_clear_565(GF_SURFACE _this, GF_IRect rc, GF_Color col); + +#ifdef GF_RGB_444_SUPORT +void evg_444_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_444_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_444_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +GF_Err evg_surface_clear_444(GF_SURFACE surf, GF_IRect rc, GF_Color col); +#endif + +#ifdef GF_RGB_555_SUPORT +void evg_555_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_555_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_555_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +GF_Err evg_surface_clear_555(GF_SURFACE surf, GF_IRect rc, GF_Color col); +#endif + +void evg_user_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_user_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +void evg_user_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf); +GF_Err evg_surface_clear_user(GF_SURFACE surf, GF_IRect rc, GF_Color col); + + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/modules/soft_raster/raster_565.c b/modules/soft_raster/raster_565.c new file mode 100644 index 0000000..229c0f6 --- /dev/null +++ b/modules/soft_raster/raster_565.c @@ -0,0 +1,491 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Rendering sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + + +#include "rast_soft.h" + +static GFINLINE s32 +mul255(s32 a, s32 b) +{ + return ((a + 1) * b) >> 8; +} + + +/* + RGB 565 part +*/ + +static u16 overmask_565(u32 src, u16 dst, u32 alpha) +{ + u32 resr, resg, resb; + s32 srca = (src >> 24) & 0xff; + s32 srcr = (src >> 16) & 0xff; + s32 srcg = (src >> 8) & 0xff; + s32 srcb = (src >> 0) & 0xff; + + s32 dstr = (dst >> 8) & 0xf8; + s32 dstg = (dst >> 3) & 0xfc; + s32 dstb = (dst << 3) & 0xf8; + + srca = mul255(srca, alpha); + resr = mul255(srca, srcr - dstr) + dstr; + resg = mul255(srca, srcg - dstg) + dstg; + resb = mul255(srca, srcb - dstb) + dstb; + return GF_COL_565(resr, resg, resb); +} + +void overmask_565_const_run(u32 src, u16 *dst, u32 count) +{ + u32 resr, resg, resb; + u8 srca = (src >> 24) & 0xff; + u8 srcr = (src >> 16) & 0xff; + u8 srcg = (src >> 8) & 0xff; + u8 srcb = (src >> 0) & 0xff; + + while (count) { + register u16 val = *dst; + register u8 dstr = (val >> 8) & 0xf8; + register u8 dstg = (val >> 3) & 0xfc; + register u8 dstb = (val << 3) & 0xf8; + + resr = mul255(srca, srcr - dstr) + dstr; + resg = mul255(srca, srcg - dstg) + dstg; + resb = mul255(srca, srcb - dstb) + dstb; + *dst = GF_COL_565(resr, resg, resb); + dst++; + count--; + } +} + +void evg_565_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u16 col565 = surf->fill_565; + u32 col = surf->fill_col; + register u32 a, fin, col_no_a; + u16 *dst = (u16 *) (surf->pixels + y * surf->stride); + register s32 i; + u32 x, len; + u8 aa_lev = surf->AALevel; + + col_no_a = col&0x00FFFFFF; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x; + len = spans[i].len; + if (spans[i].coverage != 0xFF) { + a = mul255(0xFF, spans[i].coverage); + fin = (a<<24) | (col_no_a); + overmask_565_const_run(fin, &dst[x], len); + } else { + while (len--) { + dst[x] = col565; + x++; + } + } + } +} + +void evg_565_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u16 *dst = (u16 *) (surf->pixels + y * surf->stride); + u32 col = surf->fill_col; + register u32 a, fin, col_no_a; + register s32 i; + u8 aa_lev = surf->AALevel; + + a = (col>>24)&0xFF; + col_no_a = col&0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + fin = mul255(a, spans[i].coverage); + fin = (fin<<24) | col_no_a; + overmask_565_const_run(fin, &dst[spans[i].x], spans[i].len); + } +} + + +void evg_565_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u16 *dst = (u16 *) (surf->pixels + y * surf->stride); + register u8 spanalpha, col_a; + register s32 i; + register u32 x, len; + register u32 *col; + u8 aa_lev = surf->AALevel; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x; + len = spans[i].len; + spanalpha = spans[i].coverage; + surf->sten->fill_run(surf->sten, surf, x, y, len); + col = surf->stencil_pix_run; + while (len--) { + col_a = GF_COL_A(*col); + if (col_a) { + if ((spanalpha!=0xFF) || (col_a != 0xFF)) { + dst[x] = overmask_565(*col, dst[x], spanalpha); + } else { + dst[x] = GF_COL_TO_565(*col); + } + } + col++; + x ++; + } + } +} + +GF_Err evg_surface_clear_565(GF_SURFACE surf, GF_IRect rc, GF_Color col) +{ + register u32 x, y, w, h, st, sx, sy; + register u16 val; + EVGSurface *_this = (EVGSurface *)surf; + st = _this->stride; + + h = rc.height; + w = rc.width; + sx = rc.x; + sy = rc.y; + + /*convert to 565*/ + val = GF_COL_TO_565(col); + + for (y=0; y<h; y++) { + register u16 *data = (u16 *) (_this->pixels + (sy+y) * _this->stride + 2*sx); + for (x=0; x<w; x++) { + *data++ = val; + } + } + return GF_OK; +} + + + +/* + RGB 555 part +*/ +#ifdef GF_RGB_555_SUPORT + +static u16 overmask_555(u32 src, u16 dst, u32 alpha) +{ + u32 resr, resg, resb; + s32 srca = (src >> 24) & 0xff; + s32 srcr = (src >> 16) & 0xff; + s32 srcg = (src >> 8) & 0xff; + s32 srcb = (src >> 0) & 0xff; + + s32 dstr = (dst >> 7) & 0xf8; + s32 dstg = (dst >> 2) & 0xf8; + s32 dstb = (dst << 3) & 0xf8; + + srca = mul255(srca, alpha); + resr = mul255(srca, srcr - dstr) + dstr; + resg = mul255(srca, srcg - dstg) + dstg; + resb = mul255(srca, srcb - dstb) + dstb; + return GF_COL_555(resr, resg, resb); +} + +static void overmask_555_const_run(u32 src, u16 *dst, u32 count) +{ + u32 resr, resg, resb; + u8 srca = (src >> 24) & 0xff; + u8 srcr = (src >> 16) & 0xff; + u8 srcg = (src >> 8) & 0xff; + u8 srcb = (src >> 0) & 0xff; + + while (count) { + u16 val = *dst; + u8 dstr = (val >> 7) & 0xf8; + u8 dstg = (val >> 2) & 0xf8; + u8 dstb = (val << 3) & 0xf8; + + resr = mul255(srca, srcr - dstr) + dstr; + resg = mul255(srca, srcg - dstg) + dstg; + resb = mul255(srca, srcb - dstb) + dstb; + *dst = GF_COL_555(resr, resg, resb); + dst++; + count--; + } +} + +void evg_555_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u16 col555 = surf->fill_555; + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + u16 *dst = (u16 *) (surf->pixels + y * surf->stride); + s32 i; + u32 x, len; + u8 aa_lev = surf->AALevel; + + col_no_a = col&0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x; + len = spans[i].len; + if (spans[i].coverage != 0xFF) { + a = mul255(0xFF, spans[i].coverage); + fin = (a<<24) | col_no_a; + overmask_555_const_run(fin, &dst[x], len); + } else { + while (len--) { + dst[x] = col555; + x++; + } + } + } +} + +void evg_555_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u16 *dst = (u16 *) (surf->pixels + y * surf->stride); + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + s32 i; + u8 aa_lev = surf->AALevel; + + a = (col>>24)&0xFF; + col_no_a = col & 0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + fin = mul255(a, spans[i].coverage); + fin = (fin<<24) | col_no_a; + overmask_555_const_run(fin, &dst[spans[i].x], spans[i].len); + } +} + + +void evg_555_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u16 *dst = (u16 *) (surf->pixels + y * surf->stride); + u8 spanalpha, col_a; + s32 i; + u32 x, len; + u32 *col; + u8 aa_lev = surf->AALevel; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x; + len = spans[i].len; + spanalpha = spans[i].coverage; + surf->sten->fill_run(surf->sten, surf, x, y, len); + col = surf->stencil_pix_run; + while (len--) { + col_a = GF_COL_A(*col); + if (col_a) { + if ((spanalpha!=0xFF) || (col_a != 0xFF)) { + dst[x] = overmask_555(*col, dst[x], spanalpha); + } else { + dst[x] = GF_COL_TO_555(*col); + } + } + col++; + x ++; + } + } +} + +GF_Err evg_surface_clear_555(GF_SURFACE surf, GF_IRect rc, GF_Color col) +{ + u32 x, y, w, h, st, sx, sy; + u16 val; + EVGSurface *_this = (EVGSurface *)surf; + + st = _this->stride; + + h = rc.height; + w = rc.width; + sx = rc.x; + sy = rc.y; + + /*convert to 565*/ + val = GF_COL_TO_555(col); + + for (y=0; y<h; y++) { + u16 *data = (u16 *) (_this->pixels + (sy+y) * _this->stride + 2*sx); + for (x=0; x<w; x++) { + *data++ = val; + } + } + return GF_OK; +} + +#endif + + +/* + RGB 444 part +*/ + +#ifdef GF_RGB_444_SUPORT + +static u16 overmask_444(u32 src, u16 dst, u32 alpha) +{ + u32 resr, resg, resb; + s32 srca = (src >> 24) & 0xff; + s32 srcr = (src >> 16) & 0xff; + s32 srcg = (src >> 8) & 0xff; + s32 srcb = (src >> 0) & 0xff; + + s32 dstr = (dst >> 4) & 0xf0; + s32 dstg = (dst ) & 0xf0; + s32 dstb = (dst << 4) & 0xf0; + + srca = mul255(srca, alpha); + resr = mul255(srca, srcr - dstr) + dstr; + resg = mul255(srca, srcg - dstg) + dstg; + resb = mul255(srca, srcb - dstb) + dstb; + return GF_COL_444(resr, resg, resb); +} + +static void overmask_444_const_run(u32 src, u16 *dst, u32 count) +{ + u32 resr, resg, resb; + u8 srca = (src >> 24) & 0xff; + u8 srcr = (src >> 16) & 0xff; + u8 srcg = (src >> 8) & 0xff; + u8 srcb = (src >> 0) & 0xff; + + while (count) { + u16 val = *dst; + u8 dstr = (val >> 4) & 0xf0; + u8 dstg = (val ) & 0xf0; + u8 dstb = (val << 4) & 0xf0; + + resr = mul255(srca, srcr - dstr) + dstr; + resg = mul255(srca, srcg - dstg) + dstg; + resb = mul255(srca, srcb - dstb) + dstb; + *dst = GF_COL_444(resr, resg, resb); + dst++; + count--; + } +} + +void evg_444_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u16 col444 = surf->fill_444; + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + u16 *dst = (u16 *) (surf->pixels + y * surf->stride); + s32 i; + u32 x, len; + u8 aa_lev = surf->AALevel; + + col_no_a = col&0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x; + len = spans[i].len; + if (spans[i].coverage != 0xFF) { + a = mul255(0xFF, spans[i].coverage); + fin = (a<<24) | col_no_a; + overmask_444_const_run(fin, &dst[x], len); + } else { + while (len--) { + dst[x] = col444; + x++; + } + } + } +} + +void evg_444_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u16 *dst = (u16 *) (surf->pixels + y * surf->stride); + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + s32 i; + u8 aa_lev = surf->AALevel; + + a = (col>>24)&0xFF; + col_no_a = col & 0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + fin = mul255(a, spans[i].coverage); + fin = (fin<<24) | col_no_a; + overmask_444_const_run(fin, &dst[spans[i].x], spans[i].len); + } +} + + +void evg_444_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u16 *dst = (u16 *) (surf->pixels + y * surf->stride); + u8 spanalpha, col_a; + s32 i; + u32 x, len; + u32 *col; + u8 aa_lev = surf->AALevel; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x; + len = spans[i].len; + spanalpha = spans[i].coverage; + surf->sten->fill_run(surf->sten, surf, x, y, len); + col = surf->stencil_pix_run; + while (len--) { + col_a = GF_COL_A(*col); + if (col_a) { + if ((spanalpha!=0xFF) || (col_a != 0xFF)) { + dst[x] = overmask_444(*col, dst[x], spanalpha); + } else { + dst[x] = GF_COL_TO_444(*col); + } + } + col++; + x ++; + } + } +} + +GF_Err evg_surface_clear_444(GF_SURFACE surf, GF_IRect rc, GF_Color col) +{ + u32 x, y, w, h, st, sx, sy; + u16 val; + EVGSurface *_this = (EVGSurface *)surf; + + st = _this->stride; + + h = rc.height; + w = rc.width; + sx = rc.x; + sy = rc.y; + + val = GF_COL_TO_444(col); + + for (y=0; y<h; y++) { + u16 *data = (u16 *) (_this->pixels + (sy+y) * _this->stride + 2*sx); + for (x=0; x<w; x++) { + *data++ = val; + } + } + return GF_OK; +} + +#endif + diff --git a/modules/soft_raster/raster_argb.c b/modules/soft_raster/raster_argb.c new file mode 100644 index 0000000..0e767d8 --- /dev/null +++ b/modules/soft_raster/raster_argb.c @@ -0,0 +1,537 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / software 2D rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#include "rast_soft.h" + +/* + Note: this we force ARGB textures to be endian-dependent (eg BGRA on little-endian and ARGB on big-endian) + and we always handle pixel mem as (u32 *) for 32 bits (ARGB/XRGB), we don't need to bother about endianness + here. +*/ + +static GFINLINE s32 +mul255(s32 a, s32 b) +{ +// return (a * b) >> 8; + return ((a+1) * b) >> 8; +} + +/* + 32 bit ARGB +*/ + +static u32 overmask_argb(u32 src, u32 dst, u32 alpha) +{ + u32 resa, resr, resg, resb; + + s32 srca = (src >> 24) & 0xff; + s32 srcr = (src >> 16) & 0xff; + s32 srcg = (src >> 8) & 0xff; + s32 srcb = (src >> 0) & 0xff; + s32 dsta = (dst >> 24) & 0xff; + srca = mul255(srca, alpha); + if (dsta) { + s32 dstr = (dst >> 16) & 0xff; + s32 dstg = (dst >> 8) & 0xff; + s32 dstb = (dst >> 0) & 0xff; + resa = mul255(srca, srca) + mul255(255-srca, dsta); + resr = mul255(srca, srcr - dstr) + dstr; + resg = mul255(srca, srcg - dstg) + dstg; + resb = mul255(srca, srcb - dstb) + dstb; + return (resa << 24) | (resr << 16) | (resg << 8) | (resb); + } + /*special case for ARGB: if dst alpha is 0, consider the surface is empty and copy pixel*/ + return (srca << 24) | (srcr << 16) | (srcg << 8) | (srcb); +} + +static void overmask_argb_const_run(u32 src, u32 *dst, u32 count) +{ + u32 resa ,resr, resg, resb; + + s32 srca = (src >> 24) & 0xff; + s32 srcr = (src >> 16) & 0xff; + s32 srcg = (src >> 8) & 0xff; + s32 srcb = (src >> 0) & 0xff; + + + while (count) { + u32 val = *dst; + s32 dsta = (val >> 24) & 0xff; + /*special case for ARGB: if dst alpha is 0, consider the surface is empty and copy pixel*/ + if (dsta) { + s32 dstr = (val >> 16) & 0xff; + s32 dstg = (val >> 8) & 0xff; + s32 dstb = (val) & 0xff; + + resa = mul255(srca, srca) + mul255(255-srca, dsta); + resr = mul255(srca, srcr - dstr) + dstr; + resg = mul255(srca, srcg - dstg) + dstg; + resb = mul255(srca, srcb - dstb) + dstb; + + *dst = (resa << 24) | (resr << 16) | (resg << 8) | (resb); + } else { + *dst = src; + } + dst++; + count--; + } +} + +void evg_argb_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + u32 *dst = (u32 *) (surf->pixels + y * surf->stride); + s32 i; + u32 x, len; + u8 aa_lev = surf->AALevel; + + col_no_a = col & 0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x; + len = spans[i].len; + + if (spans[i].coverage != 0xFF) { + a = mul255(0xFF, spans[i].coverage); + fin = (a<<24) | col_no_a; + overmask_argb_const_run(fin, &dst[x], len); + } else { + while (len--) { + dst[x] = col; + x ++; + } + } + } +} + +void evg_argb_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 *dst = (u32 *) (surf->pixels + y * surf->stride); + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + u8 aa_lev = surf->AALevel; + s32 i; + + a = (col>>24)&0xFF; + col_no_a = col & 0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + fin = mul255(a, spans[i].coverage); + fin = (fin<<24) | col_no_a; + overmask_argb_const_run(fin, &dst[spans[i].x], spans[i].len); + } +} + + +void evg_argb_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 *dst = (u32 *) (surf->pixels + y * surf->stride); + u8 spanalpha, col_a; + s32 i; + u32 x, len; + u32 *col; + u8 aa_lev = surf->AALevel; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x; + len = spans[i].len; + spanalpha = spans[i].coverage; + surf->sten->fill_run(surf->sten, surf, x, y, len); + col = surf->stencil_pix_run; + while (len--) { + col_a = GF_COL_A(*col); + if (col_a) { + if ((spanalpha!=0xFF) || (col_a != 0xFF)) { + dst[x] = overmask_argb(*col, dst[x], spanalpha); + } else { + dst[x] = *col; + } + } + col++; + x ++; + } + } +} + +GF_Err evg_surface_clear_argb(GF_SURFACE surf, GF_IRect rc, GF_Color col) +{ + u32 *data; + u32 x, y, w, h, st, sx, sy; + EVGSurface *_this = (EVGSurface *)surf; + st = _this->stride; + + h = rc.height; + w = rc.width; + sx = rc.x; + sy = rc.y; + + for (y = 0; y < h; y++) { + data = (u32 *) (_this ->pixels + (sy+y)* st + 4*sx); + for (x = 0; x < w; x++) { + *data++ = col; + } + } + return GF_OK; +} + + +/* + 32 bit RGB +*/ + +static u32 overmask_rgb32(u32 src, u32 dst, u32 alpha) +{ + u32 resr, resg, resb; + + s32 srca = (src >> 24) & 0xff; + s32 srcr = (src >> 16) & 0xff; + s32 srcg = (src >> 8) & 0xff; + s32 srcb = (src >> 0) & 0xff; + s32 dstr = (dst >> 16) & 0xff; + s32 dstg = (dst >> 8) & 0xff; + s32 dstb = (dst) & 0xff; + + srca = mul255(srca, alpha); + resr = mul255(srca, srcr - dstr) + dstr; + resg = mul255(srca, srcg - dstg) + dstg; + resb = mul255(srca, srcb - dstb) + dstb; + return (0xFF << 24) | (resr << 16) | (resg << 8) | resb; +} + +GFINLINE void overmask_rgb32_const_run(u32 src, u32 *dst, u32 count) +{ + u32 val, res; + s32 srca = (src>>24) & 0xff; + u32 srcr = mul255(srca, ((src >> 16) & 0xff)) ; + u32 srcg = mul255(srca, ((src >> 8) & 0xff)) ; + u32 srcb = mul255(srca, ((src) & 0xff)) ; + u32 inva = 1 + 0xFF - srca; + + while (count) { + val = *dst; + res = 0xFF00; + res |= srcr + ((inva*((val >> 16) & 0xff))>>8); + res <<=8; + res |= srcg + ((inva*((val >> 8) & 0xff))>>8); + res <<=8; + res |= srcb + ((inva*((val) & 0xff))>>8); + *dst = res; + dst++; + count--; + } +} + +void evg_rgb32_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 col = surf->fill_col; + u32 fin, col_no_a, col2, spana; + u32 *dst = (u32 *) (surf->pixels + y * surf->stride); + s32 i; + u32 x, len; + u8 aa_lev = surf->AALevel; + + col_no_a = col & 0x00FFFFFF; + col2 = (0xFF<<24) | col_no_a; + for (i=0; i<count; i++) { + spana = spans[i].coverage; + if (spana<aa_lev) continue; + x = spans[i].x; + len = spans[i].len; + + if (spana != 0xFF) { + fin = (spana<<24) | col_no_a; + overmask_rgb32_const_run(fin, &dst[x], len); + } else { + while (len--) { + dst[x] = col2; + x ++; + } + } + } +} + +void evg_rgb32_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 *dst = (u32 *) (surf->pixels + y * surf->stride); + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + u8 aa_lev = surf->AALevel; + s32 i; + + a = (col>>24)&0xFF; + col_no_a = col & 0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + fin = mul255(a, spans[i].coverage); + fin = (fin<<24) | col_no_a; + overmask_rgb32_const_run(fin, &dst[spans[i].x], spans[i].len); + } +} + + +void evg_rgb32_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 *dst = (u32 *) (surf->pixels + y * surf->stride); + u8 spanalpha, col_a; + s32 i; + u32 x, len; + u32 *col; + u8 aa_lev = surf->AALevel; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x; + len = spans[i].len; + spanalpha = spans[i].coverage; + surf->sten->fill_run(surf->sten, surf, x, y, len); + col = surf->stencil_pix_run; + while (len--) { + col_a = GF_COL_A(*col); + if (col_a) { + if ((spanalpha!=0xFF) || (col_a != 0xFF)) { + dst[x] = overmask_rgb32(*col, dst[x], spanalpha); + } else { + dst[x] = *col; + } + } + col++; + x ++; + } + } +} + +GF_Err evg_surface_clear_rgb32(GF_SURFACE surf, GF_IRect rc, GF_Color col) +{ + u32 x, y, w, h, st, sx, sy; + EVGSurface *_this = (EVGSurface *)surf; + st = _this->stride; + + h = rc.height; + w = rc.width; + sx = rc.x; + sy = rc.y; + + col = 0xFF000000 | (col & 0x00FFFFFF); + for (y = 0; y < h; y++) { + u32 *data = (u32 *) (_this ->pixels + (y + sy) * st + 4*sx); + for (x = 0; x < w; x++) { + *data++ = col; + } + } + return GF_OK; +} + + + +/* + 32 bit RGBA +*/ + +static void overmask_rgba(u32 src, char *dst, u32 alpha) +{ + u8 srca = GF_COL_A(src); + u8 srcr = GF_COL_R(src); + u8 srcg = GF_COL_G(src); + u8 srcb = GF_COL_B(src); + u8 dsta = dst[3]; + srca = mul255(srca, alpha); + if (dsta) { + u8 dstr = dst[0]; + u8 dstg = dst[1]; + u8 dstb = dst[2]; + dst[0] = mul255(srca, srcr - dstr) + dstr; + dst[1] = mul255(srca, srcg - dstg) + dstg; + dst[2] = mul255(srca, srcb - dstb) + dstb; + dst[3] = mul255(srca, srca) + mul255(255-srca, dsta); + } else { + dst[0] = srcr; + dst[1] = srcg; + dst[2] = srcb; + dst[3] = srca; + } +} + +static void overmask_rgba_const_run(u32 src, char *dst, u32 count) +{ + u8 srca = GF_COL_A(src); + u8 srcr = GF_COL_R(src); + u8 srcg = GF_COL_G(src); + u8 srcb = GF_COL_B(src); + + while (count) { + u8 dsta = dst[3]; + /*special case for RGBA: if dst alpha is 0, consider the surface is empty and copy pixel*/ + if (dsta) { + u8 dstr = dst[0]; + u8 dstg = dst[1]; + u8 dstb = dst[2]; + dst[0] = (u8) mul255(srca, srcr - dstr) + dstr; + dst[1] = (u8) mul255(srca, srcg - dstg) + dstg; + dst[2] = (u8) mul255(srca, srcb - dstb) + dstb; + dst[3] = (u8) mul255(srca, srca) + mul255(255-srca, dsta); + } else { + dst[0] = srcr; + dst[1] = srcg; + dst[2] = srcb; + dst[3] = srca; + } + dst+=4; + count--; + } +} + +void evg_rgba_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 col = surf->fill_col; + u32 new_a, fin, col_no_a; + u8 a, r, g, b; + char *dst = (surf->pixels + y * surf->stride); + char *p; + s32 i; + u32 len; + u8 aa_lev = surf->AALevel; + + a = GF_COL_A(col); + r = GF_COL_R(col); + g = GF_COL_G(col); + b = GF_COL_B(col); + col_no_a = col & 0x00FFFFFF; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + p = dst + spans[i].x*4; + len = spans[i].len; + + if (spans[i].coverage != 0xFF) { + new_a = spans[i].coverage; + fin = (new_a<<24) | col_no_a; + overmask_rgba_const_run(fin, p, len); + } else { + while (len--) { + *(p) = r; + *(p+1) = g; + *(p+2) = b; + *(p+3) = a; + p += 4; + } + } + } +} + +void evg_rgba_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 *dst = (u32 *) (surf->pixels + y * surf->stride); + u32 a, fin, col_no_a; + u8 aa_lev = surf->AALevel; + s32 i; + + a = GF_COL_A(surf->fill_col); + col_no_a = surf->fill_col & 0x00FFFFFF; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + fin = mul255(a, spans[i].coverage); + fin = (fin<<24) | col_no_a; + overmask_rgba_const_run(fin, (char*) &dst[spans[i].x], spans[i].len); + } +} + + +void evg_rgba_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u8 *dst = surf->pixels + y * surf->stride; + u8 *p; + u8 spanalpha, col_a; + s32 i; + u32 len; + u32 *col; + u8 aa_lev = surf->AALevel; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + p = dst + spans[i].x * 4; + len = spans[i].len; + spanalpha = spans[i].coverage; + surf->sten->fill_run(surf->sten, surf, spans[i].x, y, len); + col = surf->stencil_pix_run; + while (len--) { + col_a = GF_COL_A(*col); + if (col_a) { + if ((spanalpha!=0xFF) || (col_a != 0xFF)) { + overmask_rgba(*col, p, spanalpha); + } else { + u32 _col = *col; + p[0] = GF_COL_R(_col); + p[1] = GF_COL_G(_col); + p[2] = GF_COL_B(_col); + p[3] = GF_COL_A(_col); + } + } + col++; + p += 4; + } + } +} + +GF_Err evg_surface_clear_rgba(GF_SURFACE surf, GF_IRect rc, GF_Color col) +{ + u8 *data; + u8 a, r, g, b; + u32 x, y, w, h, st, sx, sy; + EVGSurface *_this = (EVGSurface *)surf; + st = _this->stride; + + h = rc.height; + w = rc.width; + sx = 4 * rc.x; + sy = rc.y; + + a = GF_COL_A(col); + r = GF_COL_R(col); + g = GF_COL_G(col); + b = GF_COL_B(col); + + if (a) { + for (y = 0; y < h; y++) { + data = (_this ->pixels + (sy+y)* st + sx); + for (x = 0; x < w; x++) { + *(data) = r; + *(data+1) = g; + *(data+2) = b; + *(data+3) = a; + data += 4; + } + } + } else { + u32 sw = 4*w; + for (y = 0; y < h; y++) { + memset(_this ->pixels + (sy+y)* st + sx, 0, sizeof(char)*sw); + } + } + return GF_OK; +} + diff --git a/modules/soft_raster/raster_load.c b/modules/soft_raster/raster_load.c new file mode 100644 index 0000000..40b9a21 --- /dev/null +++ b/modules/soft_raster/raster_load.c @@ -0,0 +1,99 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / software 2D rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#include "rast_soft.h" + +/*we don't need any private context*/ +GF_Raster2D *EVG_LoadRenderer() +{ + GF_Raster2D *dr; + GF_SAFEALLOC(dr, GF_Raster2D); + GF_REGISTER_MODULE_INTERFACE(dr, GF_RASTER_2D_INTERFACE, "GPAC 2D Raster", "gpac distribution") + + + dr->stencil_new = evg_stencil_new; + dr->stencil_delete = evg_stencil_delete; + dr->stencil_set_matrix = evg_stencil_set_matrix; + dr->stencil_set_brush_color = evg_stencil_set_brush_color; + dr->stencil_set_gradient_mode = evg_stencil_set_gradient_mode; + dr->stencil_set_linear_gradient = evg_stencil_set_linear_gradient; + dr->stencil_set_radial_gradient = evg_stencil_set_radial_gradient; + dr->stencil_set_gradient_interpolation = evg_stencil_set_gradient_interpolation; + dr->stencil_set_alpha = evg_stencil_set_alpha; + dr->stencil_set_texture = evg_stencil_set_texture; + dr->stencil_set_tiling = evg_stencil_set_tiling; + dr->stencil_set_filter = evg_stencil_set_filter; + dr->stencil_set_color_matrix = evg_stencil_set_color_matrix; + dr->stencil_create_texture = evg_stencil_create_texture; + dr->stencil_texture_modified = NULL; + + dr->surface_new = evg_surface_new; + dr->surface_delete = evg_surface_delete; + dr->surface_attach_to_device = NULL; + dr->surface_attach_to_texture = evg_surface_attach_to_texture; + dr->surface_attach_to_buffer = evg_surface_attach_to_buffer; + dr->surface_detach = evg_surface_detach; + dr->surface_set_raster_level = evg_surface_set_raster_level; + dr->surface_set_matrix = evg_surface_set_matrix; + dr->surface_set_clipper = evg_surface_set_clipper; + dr->surface_set_path = evg_surface_set_path; + dr->surface_fill = evg_surface_fill; + dr->surface_flush = NULL; + dr->surface_clear = evg_surface_clear; + return dr; +} + +void EVG_ShutdownRenderer(GF_Raster2D *dr) +{ + free(dr); +} + +#ifndef GPAC_STANDALONE_RENDER_2D + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_RASTER_2D_INTERFACE) return 1; + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType==GF_RASTER_2D_INTERFACE) { + return (GF_BaseInterface *)EVG_LoadRenderer(); + } + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + if (ifce->InterfaceType == GF_RASTER_2D_INTERFACE) { + EVG_ShutdownRenderer((GF_Raster2D *)ifce); + } +} + +#endif diff --git a/modules/soft_raster/raster_rgb.c b/modules/soft_raster/raster_rgb.c new file mode 100644 index 0000000..36061d9 --- /dev/null +++ b/modules/soft_raster/raster_rgb.c @@ -0,0 +1,444 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / software 2D rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * + */ + +#include "rast_soft.h" + +static s32 +mul255(s32 a, s32 b) +{ + return ((a + 1) * b) >> 8; +} + + +/* + RGB part +*/ + +static void overmask_rgb(u32 src, char *dst, u32 alpha) +{ + s32 srca = (src >> 24) & 0xff; + u32 srcr = (src >> 16) & 0xff; + s32 srcg = (src >> 8) & 0xff; + s32 srcb = (src) & 0xff; + + s32 dstr = (*dst) & 0xFF; + s32 dstg = *(dst+1) & 0xFF; + s32 dstb = *(dst+2) & 0xFF; + + srca = mul255(srca, alpha); + *dst = mul255(srca, srcr - dstr) + dstr; + *(dst+1) = mul255(srca, srcg - dstg) + dstg; + *(dst+2) = mul255(srca, srcb - dstb) + dstb; +} + +static void overmask_rgb_const_run(u32 src, char *dst, u32 count) +{ + u8 srca = (src >> 24) & 0xff; + u8 srcr = (src >> 16) & 0xff; + u8 srcg = (src >> 8) & 0xff; + u8 srcb = (src) & 0xff; + + while (count) { + u8 dstr = *(dst); + u8 dstg = *(dst+1); + u8 dstb = *(dst+2); + *dst = (u8) mul255(srca, srcr - dstr) + dstr; + *(dst+1) = (u8) mul255(srca, srcg - dstg) + dstg; + *(dst+2) = (u8) mul255(srca, srcb - dstb) + dstb; + dst += 3; + count--; + } +} + +void evg_rgb_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + char *dst = surf->pixels + y * surf->stride; + char *p; + s32 i; + u32 x, len, r, g, b; + u8 aa_lev = surf->AALevel; + + r = GF_COL_R(col); + g = GF_COL_G(col); + b = GF_COL_B(col); + + col_no_a = col & 0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x * 3; + len = spans[i].len; + p = dst + x; + + if (spans[i].coverage != 0xFF) { + a = mul255(0xFF, spans[i].coverage); + fin = (a<<24) | col_no_a; + overmask_rgb_const_run(fin, p, len); + } else { + while (len--) { + *(p) = r; + *(p + 1) = g; + *(p + 2) = b; + p += 3; + } + } + } +} + +void evg_rgb_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + char *dst = surf->pixels + y * surf->stride; + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + s32 i; + u8 aa_lev = surf->AALevel; + + a = (col>>24)&0xFF; + col_no_a = col & 0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + fin = mul255(a, spans[i].coverage); + fin = (fin<<24) | (col&0x00FFFFFF); + overmask_rgb_const_run(fin, dst + 3 * spans[i].x, spans[i].len); + } +} + + +void evg_rgb_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + char *dst = surf->pixels + y * surf->stride; + u8 spanalpha, col_a; + s32 i; + u32 x, len, bpp; + u32 *col; + u8 aa_lev = surf->AALevel; + bpp = surf->BPP; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + len = spans[i].len; + spanalpha = spans[i].coverage; + surf->sten->fill_run(surf->sten, surf, spans[i].x, y, len); + col = surf->stencil_pix_run; + x = 3*spans[i].x; + while (len--) { + col_a = GF_COL_A(*col); + if (col_a) { + if ((spanalpha!=0xFF) || (col_a != 0xFF)) { + overmask_rgb(*col, dst + x, spanalpha); + } else { + dst[x] = GF_COL_R(*col); + dst[x+1] = GF_COL_G(*col); + dst[x+2] = GF_COL_B(*col); + } + } + col++; + x += 3; + } + } +} + +GF_Err evg_surface_clear_rgb(GF_SURFACE surf, GF_IRect rc, GF_Color col) +{ + u32 x, y, w, h, st, sx, sy; + u8 r, g, b; + EVGSurface *_this = (EVGSurface *)surf; + st = _this->stride; + + h = rc.height; + w = rc.width; + sx = rc.x; + sy = rc.y; + + r = GF_COL_R(col); + g = GF_COL_G(col); + b = GF_COL_B(col); + + for (y = 0; y < h; y++) { + char *data = _this ->pixels + (y + sy) * st + 3*sx; + for (x = 0; x < w; x++) { + *(data) = r; + *(data+1) = g; + *(data+2) = b; + data += 3; + } + } + return GF_OK; +} + + +/* + + BGR part +*/ + +static void overmask_bgr(u32 src, char *dst, u32 alpha) +{ + s32 srca = (src >> 24) & 0xff; + s32 srcr = (src >> 16) & 0xff; + s32 srcg = (src >> 8) & 0xff; + s32 srcb = (src) & 0xff; + + s32 dstb = *dst & 0xFF; + s32 dstg = *(dst+1) & 0xFF; + s32 dstr = *(dst+2) & 0xFF; + + srca = mul255(srca, alpha); + *(dst) = mul255(srca, srcb - dstb) + dstb; + *(dst+1) = mul255(srca, srcg - dstg) + dstg; + *(dst+2) = mul255(srca, srcr - dstr) + dstr; +} + +static void overmask_bgr_const_run(u32 src, char *dst, u32 count) +{ + u8 srca = (src >> 24) & 0xff; + u8 srcr = (src >> 16) & 0xff; + u8 srcg = (src >> 8) & 0xff; + u8 srcb = (src) & 0xff; + + while (count) { + u8 dstb = *(dst); + u8 dstg = *(dst+1); + u8 dstr = *(dst+2); + *dst = (u8) mul255(srca, srcb - dstb) + dstb; + *(dst+1) = (u8) mul255(srca, srcg - dstg) + dstg; + *(dst+2) = (u8) mul255(srca, srcr - dstr) + dstr; + dst += 3; + count--; + } +} + +void evg_bgr_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + char *dst = surf->pixels + y * surf->stride; + char *p; + s32 i; + u32 x, len, r, g, b; + u8 aa_lev = surf->AALevel; + + r = GF_COL_R(col); + g = GF_COL_G(col); + b = GF_COL_B(col); + + col_no_a = col & 0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x * 3; + len = spans[i].len; + p = dst + x; + + if (spans[i].coverage != 0xFF) { + a = mul255(0xFF, spans[i].coverage); + fin = (a<<24) | col_no_a; + overmask_bgr_const_run(fin, p, len); + } else { + while (len--) { + *(p) = b; + *(p + 1) = g; + *(p + 2) = r; + p += 3; + } + } + } +} + +void evg_bgr_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + char *dst = surf->pixels + y * surf->stride; + u32 col = surf->fill_col; + u32 a, fin, col_no_a; + s32 i; + u8 aa_lev = surf->AALevel; + + a = (col>>24)&0xFF; + col_no_a = col & 0x00FFFFFF; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + fin = mul255(a, spans[i].coverage); + fin = (fin<<24) | col_no_a; + overmask_bgr_const_run(fin, dst + 3 * spans[i].x, spans[i].len); + } +} + + +void evg_bgr_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + char *dst = surf->pixels + y * surf->stride; + u8 spanalpha, col_a; + s32 i; + u32 x, len, bpp; + u32 *col; + u8 aa_lev = surf->AALevel; + bpp = surf->BPP; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + x = spans[i].x * bpp; + len = spans[i].len; + spanalpha = spans[i].coverage; + surf->sten->fill_run(surf->sten, surf, x, y, len); + col = surf->stencil_pix_run; + while (len--) { + col_a = GF_COL_A(*col); + if (col_a) { + if ((spanalpha!=0xFF) || (col_a != 0xFF)) { + overmask_bgr(*col, dst + x, spanalpha); + } else { + dst[x] = GF_COL_B(*col); + dst[x+1] = GF_COL_G(*col); + dst[x+2] = GF_COL_R(*col); + } + } + col++; + x += 3; + } + } +} + +GF_Err evg_surface_clear_bgr(GF_SURFACE surf, GF_IRect rc, GF_Color col) +{ + u32 x, y, w, h, st, sx, sy; + u8 r, g, b; + EVGSurface *_this = (EVGSurface *)surf; + st = _this->stride; + + h = rc.height; + w = rc.width; + sx = rc.x; + sy = rc.y; + + r = GF_COL_R(col); + g = GF_COL_G(col); + b = GF_COL_B(col); + + for (y = 0; y < h; y++) { + char *data = _this ->pixels + (y+sy) * st + 3*sx; + for (x = 0; x < w; x++) { + *(data) = b; + *(data+1) = g; + *(data+2) = r; + data += 3; + } + } + return GF_OK; +} + + +/* + user-defined callbacks +*/ + +void evg_user_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 col_no_a; + s32 i; + u8 aa_lev = surf->AALevel; + + col_no_a = surf->fill_col; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + + if (spans[i].coverage != 0xFF) { + u32 a = mul255(0xFF, spans[i].coverage); + surf->raster_fill_run_alpha(surf->raster_cbk, spans[i].x, y, spans[i].len, col_no_a, a); + } else { + surf->raster_fill_run_no_alpha(surf->raster_cbk, spans[i].x, y, spans[i].len, col_no_a); + } + } +} + +void evg_user_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u32 col, a, col_no_a; + s32 i; + u8 aa_lev = surf->AALevel; + + a = (surf->fill_col>>24)&0xFF; + col_no_a = surf->fill_col | 0xFF000000; + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + col = mul255(a, spans[i].coverage); + surf->raster_fill_run_alpha(surf->raster_cbk, spans[i].x, y, spans[i].len, col_no_a, col); + } +} + +void evg_user_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) +{ + u8 spanalpha, col_a, a; + s32 i; + u32 x, len, bpp; + u32 *col; + u8 aa_lev = surf->AALevel; + bpp = surf->BPP; + + for (i=0; i<count; i++) { + if (spans[i].coverage<aa_lev) continue; + len = spans[i].len; + x = spans[i].x; + spanalpha = spans[i].coverage; + surf->sten->fill_run(surf->sten, surf, x, y, len); + col = surf->stencil_pix_run; + while (len--) { + col_a = GF_COL_A(*col); + if (col_a) { + if ((spanalpha!=0xFF) || (col_a != 0xFF)) { + a = mul255(col_a, spans[i].coverage); + surf->raster_fill_run_alpha(surf->raster_cbk, x, y, 1, *col, a); + } else { + surf->raster_fill_run_no_alpha(surf->raster_cbk, x, y, 1, *col); + } + } + col++; + x ++; + } + } +} + +GF_Err evg_surface_clear_user(GF_SURFACE surf, GF_IRect rc, GF_Color col) +{ + u32 y, w, h, sx, sy, a; + EVGSurface *_this = (EVGSurface *)surf; + + h = rc.height; + w = rc.width; + sx = rc.x; + sy = rc.y; + a = GF_COL_A(col); + if (a==0xFF) { + for (y = 0; y < h; y++) { + _this->raster_fill_run_no_alpha(_this->raster_cbk, sx, y+sy, w, col); + } + } else { + col |= 0xFF000000; + for (y = 0; y < h; y++) { + _this->raster_fill_run_alpha(_this->raster_cbk, sx, y+sy, w, col, a); + } + } + return GF_OK; +} diff --git a/modules/soft_raster/stencil.c b/modules/soft_raster/stencil.c new file mode 100644 index 0000000..9fbe9ba --- /dev/null +++ b/modules/soft_raster/stencil.c @@ -0,0 +1,855 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / software 2D rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "rast_soft.h" + +EVGStencil *evg_solid_brush(); +EVGStencil *evg_texture_brush(); +EVGStencil *evg_linear_gradient_brush(); +EVGStencil *evg_radial_gradient_brush(); + + + +GF_Color color_interpolate(u32 a, u32 b, u8 pos) +{ + u32 ca = ((a>>24) )*(u32)(0xFF-pos)+((b>>24) )*(u32)pos; + u32 cr = ((a>>16)&0xFF)*(u32)(0xFF-pos)+((b>>16)&0xFF)*(u32)pos; + u32 cg = ((a>> 8)&0xFF)*(u32)(0xFF-pos)+((b>> 8)&0xFF)*(u32)pos; + u32 cb = ((a )&0xFF)*(u32)(0xFF-pos)+((b )&0xFF)*(u32)pos; + return (((ca+(ca>>8)+1)>>8)<<24)| + (((cr+(cr>>8)+1)>>8)<<16)| + (((cg+(cg>>8)+1)>>8)<< 8)| + (((cb+(cb>>8)+1)>>8) ); +} + + + + +/* + Generic gradient tools +*/ + +#define EVGGRADIENTBUFFERSIZE (1<<EVGGRADIENTBITS) +#define EVGGRADIENTMAXINTPOS EVGGRADIENTBUFFERSIZE - 1 + +static void gradient_update(EVG_BaseGradient *_this) +{ + s32 i, c; + s32 start, end, diff; + Fixed maxPos = INT2FIX(EVGGRADIENTMAXINTPOS); + + if (_this->pos[0]>=0) { + if(_this->pos[0]>0) { + end = FIX2INT(gf_mulfix(_this->pos[0], maxPos)); + for (i=0; i<= end;i++) { + _this->pre[i] = _this->col[0]; + } + } + for (c=0; c<EVGGRADIENTSLOTS;c++) { + if (_this->pos[c]<0) break; + if (_this->pos[c+1]>=0) { + start = FIX2INT(gf_mulfix(_this->pos[c], maxPos)); + end = FIX2INT(gf_mulfix(_this->pos[c+1], maxPos)); + diff = end-start; + + if (diff) { + for (i=start;i<=end;i++) { + _this->pre[i] = color_interpolate(_this->col[c], _this->col[c+1], + (u8) ( ( (i-start) * 255) / diff) ); + } + } + } else { + start = FIX2INT(gf_mulfix(_this->pos[c+0], maxPos)); + for(i=start;i<=EVGGRADIENTMAXINTPOS;i++) { + _this->pre[i] = _this->col[c]; + } + } + } + } +} + +static u32 gradient_get_color(EVG_BaseGradient *_this, s32 pos) +{ + s32 max_pos = 1 << EVGGRADIENTBITS; + + switch (_this->mod) { + case GF_GRADIENT_MODE_SPREAD: + if (pos<0) pos = -pos; + return _this->pre[(pos & max_pos) ? EVGGRADIENTMAXINTPOS - (pos % max_pos) : pos % max_pos]; + + case GF_GRADIENT_MODE_REPEAT: + while (pos < 0) pos += max_pos; + return _this->pre[pos % max_pos]; + + case GF_GRADIENT_MODE_PAD: + default: + return _this->pre[ MIN(EVGGRADIENTMAXINTPOS, MAX((s32) 0, pos))]; + } +} + +GF_Err evg_stencil_set_gradient_interpolation(GF_STENCIL p, Fixed *pos, GF_Color *col, u32 count) +{ + EVG_BaseGradient *_this = (EVG_BaseGradient *) p; + if ( (_this->type != GF_STENCIL_LINEAR_GRADIENT) && (_this->type != GF_STENCIL_RADIAL_GRADIENT) ) return GF_BAD_PARAM; + if (count>=EVGGRADIENTSLOTS-1) return GF_OUT_OF_MEM; + memcpy(_this->col, col, sizeof(GF_Color) * count); + memcpy(_this->pos, pos, sizeof(Fixed) * count); + _this->col[count] = 0; + _this->pos[count] = -FIX_ONE; + gradient_update(_this); + return GF_OK; +} + +GF_Err evg_stencil_set_gradient_mode(GF_STENCIL p, GF_GradientMode mode) +{ + EVG_BaseGradient *_this = (EVG_BaseGradient *) p; + if ( (_this->type != GF_STENCIL_LINEAR_GRADIENT) && (_this->type != GF_STENCIL_RADIAL_GRADIENT) ) return GF_BAD_PARAM; + _this->mod = mode; + return GF_OK; +} + +/* + Generic stencil +*/ + +GF_STENCIL evg_stencil_new(GF_Raster2D *_dr, GF_StencilType type) +{ + EVGStencil *st; + switch (type) { + case GF_STENCIL_SOLID: + st = evg_solid_brush(); + break; + case GF_STENCIL_LINEAR_GRADIENT: + st = evg_linear_gradient_brush(); + break; + case GF_STENCIL_RADIAL_GRADIENT: + st = evg_radial_gradient_brush(); + break; + case GF_STENCIL_TEXTURE: + st = evg_texture_brush(); + break; + default: + return 0L; + } + if (st) { + gf_mx2d_init(st->pmat); + gf_mx2d_init(st->smat); + gf_cmx_init(&st->cmat); + } + return st; +} + +void evg_stencil_delete(GF_STENCIL st) +{ + EVGStencil *_this = (EVGStencil *) st; + switch(_this->type) { + case GF_STENCIL_SOLID: + case GF_STENCIL_LINEAR_GRADIENT: + case GF_STENCIL_RADIAL_GRADIENT: + free(_this); + return; + case GF_STENCIL_TEXTURE: + { + EVG_Texture *tx = (EVG_Texture *)_this; + /*destroy conversion buffer if any*/ + if ( tx->conv_buf) free( tx->conv_buf ); + /*destroy local texture iof any*/ + if (tx->owns_texture && tx->pixels) free(tx->pixels); + free(_this); + } + return; + } +} + +GF_Err evg_stencil_set_matrix(GF_STENCIL st, GF_Matrix2D *mx) +{ + EVGStencil *_this = (EVGStencil *)st; + if (!_this || _this->type>GF_STENCIL_TEXTURE) return GF_BAD_PARAM; + if (mx) { + gf_mx2d_copy(_this->smat, *mx); + } else { + gf_mx2d_init(_this->smat); + } + return GF_OK; +} + + +/* + Solid color stencil +*/ + +EVGStencil *evg_solid_brush() +{ + EVG_Brush *tmp; + GF_SAFEALLOC(tmp, EVG_Brush); + if (!tmp) return 0L; + tmp->fill_run = NULL; + tmp->color = 0xFF000000; + tmp->type = GF_STENCIL_SOLID; + return (EVGStencil *) tmp; +} + +GF_Err evg_stencil_set_brush_color(GF_STENCIL st, GF_Color c) +{ + EVG_Brush *_this = (EVG_Brush *) st; + if (!_this || (_this ->type != GF_STENCIL_SOLID) ) return GF_BAD_PARAM; + _this->color = c; + return GF_OK; +} + + +/* + linear gradient stencil +*/ + +static void lgb_fill_run(EVGStencil *p, EVGSurface *surf, s32 x, s32 y, u32 count) +{ + Bool has_cmat, has_a; + Fixed _res; + s32 val; + u32 col, ca; + u32 *data = surf->stencil_pix_run; + u32 shifter = (EVGGRADIENTSCALEBITS - EVGGRADIENTBITS); + EVG_LinearGradient *_this = (EVG_LinearGradient *) p; + + has_cmat = _this->cmat.identity ? 0 : 1; + has_a = (_this->alpha==0xFF) ? 0 : 1; + + /*no need to move x & y to fixed*/ + _res = (Fixed) (x * _this->smat.m[0] + y * _this->smat.m[1] + _this->smat.m[2]); + while (count) { + val = FIX2INT(_res); + _res += _this->smat.m[0]; + + col = gradient_get_color((EVG_BaseGradient *)_this, (val >> shifter) ); + if (has_a) { + ca = ((GF_COL_A(col) + 1) * _this->alpha) >> 8; + col = ( ((ca<<24) & 0xFF000000) ) | (col & 0x00FFFFFF); + } + if (has_cmat) col = gf_cmx_apply(&p->cmat, col); + *data++ = col; + count--; + } +} + +GF_Err evg_stencil_set_linear_gradient(GF_STENCIL st, Fixed start_x, Fixed start_y, Fixed end_x, Fixed end_y) +{ + GF_Matrix2D mtx; + GF_Point2D s; + Fixed f; + EVG_LinearGradient *_this = (EVG_LinearGradient *) st; + if (_this->type != GF_STENCIL_LINEAR_GRADIENT) return GF_BAD_PARAM; + + _this->start.x = start_x; + _this->start.y = start_y; + _this->end.x = end_x; + _this->end.y = end_y; + s.x = end_x - start_x; + s.y = end_y - start_y; + f = gf_v2d_len(&s); + if (f) f = gf_invfix(f); + + gf_mx2d_init(mtx); + mtx.m[2] = - _this->start.x; + mtx.m[5] = - _this->start.y; + _this->vecmat = mtx; + + gf_mx2d_init(mtx); + gf_mx2d_add_rotation(&mtx, 0, 0, - gf_atan2(s.y, s.x)); + gf_mx2d_add_matrix(&_this->vecmat, &mtx); + + gf_mx2d_init(mtx); + gf_mx2d_add_scale(&mtx, f, f); + gf_mx2d_add_matrix(&_this->vecmat, &mtx); + return GF_OK; +} + +EVGStencil *evg_linear_gradient_brush() +{ + s32 i; + EVG_LinearGradient *tmp; + GF_SAFEALLOC(tmp, EVG_LinearGradient); + if (!tmp) return 0L; + gf_mx2d_init(tmp->vecmat); + tmp->fill_run = lgb_fill_run; + tmp->type = GF_STENCIL_LINEAR_GRADIENT; + for(i=0; i<EVGGRADIENTSLOTS; i++) tmp->pos[i]=-1; + + tmp->alpha = 0xFF; + evg_stencil_set_linear_gradient((EVGStencil *)tmp, 0, 0, FIX_ONE, 0); + return (EVGStencil *) tmp; +} + + +/* + radial gradient stencil +*/ + +static void rg_fill_run(EVGStencil *p, EVGSurface *surf, s32 _x, s32 _y, u32 count) +{ + Fixed x, y, dx, dy, b, val; + Bool has_cmat, has_a; + s32 pos; + u32 col, ca; + u32 *data = surf->stencil_pix_run; + EVG_RadialGradient *_this = (EVG_RadialGradient *) p; + + x = INT2FIX(_x); + y = INT2FIX(_y); + gf_mx2d_apply_coords(&_this->smat, &x, &y); + + has_cmat = _this->cmat.identity ? 0 : 1; + has_a = (_this->alpha==0xFF) ? 0 : 1; + + dx = x - _this->d_f.x; + dy = y - _this->d_f.y; + while (count) { + b = gf_mulfix(_this->rad, gf_mulfix(dx, _this->d_f.x) + gf_mulfix(dy, _this->d_f.y)); + val = gf_mulfix(b, b) + gf_mulfix(_this->rad, gf_mulfix(dx, dx)+gf_mulfix(dy, dy)); + b += gf_sqrt(val); + pos = FIX2INT(EVGGRADIENTBUFFERSIZE*b); + + col = gradient_get_color((EVG_BaseGradient *)_this, pos); + if (has_a) { + ca = ((GF_COL_A(col) + 1) * _this->alpha) >> 8; + col = ( ((ca<<24) & 0xFF000000) ) | (col & 0x00FFFFFF); + } + if (has_cmat) col = gf_cmx_apply(&p->cmat, col); + *data++ = col; + + dx += _this->d_i.x; + dy += _this->d_i.y; + count--; + } +} + +void evg_radial_init(EVG_RadialGradient *_this) +{ + GF_Point2D p0, p1; + p0.x = p0.y = p1.y = 0; + p1.x = FIX_ONE; + + gf_mx2d_apply_point(&_this->smat, &p0); + gf_mx2d_apply_point(&_this->smat, &p1); + _this->d_i.x = p1.x - p0.x; + _this->d_i.y = p1.y - p0.y; + + _this->rad = FIX_ONE - gf_mulfix(_this->d_f.x, _this->d_f.x) - gf_mulfix(_this->d_f.y, _this->d_f.y); + if (_this->rad) { + _this->rad = gf_invfix(_this->rad); + } else { + _this->rad = EVGGRADIENTBUFFERSIZE; + } +} + +EVGStencil *evg_radial_gradient_brush() +{ + s32 i; + EVG_RadialGradient *tmp; + GF_SAFEALLOC(tmp, EVG_RadialGradient); + if (!tmp) return 0L; + + tmp->fill_run = rg_fill_run; + tmp->type = GF_STENCIL_RADIAL_GRADIENT; + for(i=0; i<EVGGRADIENTSLOTS; i++) tmp->pos[i]=-1; + + tmp->center.x = tmp->center.y = FIX_ONE/2; + tmp->focus = tmp->center; + tmp->radius = tmp->center; + tmp->alpha = 0xFF; + return (EVGStencil *) tmp; +} + + +GF_Err evg_stencil_set_radial_gradient(GF_STENCIL st, Fixed cx, Fixed cy, Fixed fx, Fixed fy, Fixed x_radius, Fixed y_radius) +{ + EVG_RadialGradient *_this = (EVG_RadialGradient *) st; + if (_this->type != GF_STENCIL_RADIAL_GRADIENT) return GF_BAD_PARAM; + + _this->center.x = cx; + _this->center.y = cy; + _this->focus.x = fx; + _this->focus.y = fy; + _this->radius.x = x_radius; + _this->radius.y = y_radius; + return GF_OK; +} + +/* + Texture stencil - this is just a crude texture mapper, it lacks precision and filtering ... +*/ + +#if 0 +static s32 +mul255(s32 a, s32 b) +{ + return ((a+1) * b) >> 8; +} +static u32 EVG_LERP(u32 c0, u32 c1, u8 t) +{ + s32 a0, r0, g0, b0; + s32 a1, r1, g1, b1; + s32 a2, r2, g2, b2; + + a0 = GF_COL_A(c0); + r0 = GF_COL_R(c0); + g0 = GF_COL_G(c0); + b0 = GF_COL_B(c0); + a1 = GF_COL_A(c1); + r1 = GF_COL_R(c1); + g1 = GF_COL_G(c1); + b1 = GF_COL_B(c1); + + a2 = a0 + mul255(t, (a1 - a0)); + r2 = r0 + mul255(t, (r1 - r0)); + g2 = g0 + mul255(t, (g1 - g0)); + b2 = b0 + mul255(t, (b1 - b0)); + return (a2<<24) | (r2<<16) | (g2<<8) | b2; +} +#endif + +static void bmp_fill_run(EVGStencil *p, EVGSurface *surf, s32 _x, s32 _y, u32 count) +{ + s32 cx; + u32 x0, y0, pix, replace_col; + Bool has_alpha, has_replace_cmat, has_cmat, repeat_s, repeat_t; + Fixed x, y, _fd; + u32 *data = surf->stencil_pix_run; + EVG_Texture *_this = (EVG_Texture *) p; + + /* reverse to texture coords*/ + x = INT2FIX(_x); + y = INT2FIX(_y); + gf_mx2d_apply_coords(&_this->smat, &x, &y); + + /*ugly hack we may have a numerical stability issue*/ + if (ABS(x) < FIX_ONE/10) x = 0; + if (ABS(y) < FIX_ONE/10) y = 0; + + _fd = INT2FIX(_this->width); + repeat_s = _this->mod & GF_TEXTURE_REPEAT_S; + if (!repeat_s && (x < - _fd)) x = 0; + while (x<0) x += _fd; + + _fd = INT2FIX(_this->height); + repeat_t = _this->mod & GF_TEXTURE_REPEAT_T; + if (!repeat_t && (y < - _fd)) y = 0; + while (y<0) y += _fd; + + y0 = (s32) FIX2INT(y); + has_alpha = (_this->alpha != 255) ? 1 : 0; + has_replace_cmat = _this->cmat_is_replace ? 1 : 0; + has_cmat = _this->cmat.identity ? 0 : 1; + replace_col = _this->replace_col; + + while (count) { + x0 = FIX2INT(x); + assert((s32)x0 >=0); + + if (repeat_s) { + x0 = (x0) % _this->width; + } else { + x0 = MIN(x0, _this->width-1); + } + x += _this->inc_x; + if (x<0) x+=INT2FIX(_this->width); + + y0 = FIX2INT(y); + assert((s32)y0 >=0); + if (repeat_t) { + y0 = (y0) % _this->height; + } else if (y0 >= _this->height) + y0 = _this->height-1; + y += _this->inc_y; + if (y<0) y+=INT2FIX(_this->height); + + pix = _this->tx_get_pixel(_this->pixels + _this->stride*y0 + _this->Bpp*x0); + + + /*bilinear filtering - disabled (too slow and not precise enough)*/ +#if 0 + if (_this->filter==GF_TEXTURE_FILTER_HIGH_QUALITY) { + u32 p00, p01, p10, p11, x1, y1; + u8 tx, ty; + + x1 = (x0+1) % _this->width; + y1 = (y0+1) % _this->height; + p00 = pix; + p01 = _this->tx_get_pixel(_this->pixels + _this->stride*y0 + _this->Bpp*x1); + p10 = _this->tx_get_pixel(_this->pixels + _this->stride*y1 + _this->Bpp*x0); + p11 = _this->tx_get_pixel(_this->pixels + _this->stride*y1 + _this->Bpp*x1); + + tx = FIX2INT(gf_muldiv(x, 255, _this->width) ); + ty = FIX2INT(gf_muldiv(y, 255, _this->height) ); + + p00 = EVG_LERP(p00, p01, tx); + p10 = EVG_LERP(p10, p11, tx); + pix = EVG_LERP(p00, p10, ty); + } +#endif + + if (has_alpha) { + cx = ((GF_COL_A(pix) + 1) * _this->alpha) >> 8; + pix = ( ((cx<<24) & 0xFF000000) ) | (pix & 0x00FFFFFF); + } + if (has_replace_cmat) { + u32 __a; + __a = GF_COL_A(pix); + __a = (u32) (_this->cmat.m[18] * __a); + pix = ((__a<<24) | (replace_col & 0x00FFFFFF)); + } else if (has_cmat) { + pix = gf_cmx_apply(&_this->cmat, pix); + } + *data++ = pix; + count--; + } +} + + +/*just a little faster...*/ +static void bmp_fill_run_straight(EVGStencil *p, EVGSurface *surf, s32 _x, s32 _y, u32 count) +{ + s32 x0, y0; + u32 pix; + u32 __a; + Bool repeat_s = 0; + Fixed x, y, _fdim; + char *pix_line; + u32 *data = surf->stencil_pix_run; + EVG_Texture *_this = (EVG_Texture *) p; + + /*get texture coords in FIXED - offset*/ + x = _this->smat.m[0]*_x + _this->smat.m[2]; + y = _this->smat.m[4]*_y + _this->smat.m[5]; + + /*ugly hack we may have a numerical stability issue*/ + if (ABS(x)< FIX_ONE/10) x = 0; + if (ABS(y)< FIX_ONE/10) y = 0; + + /* and move in absolute coords*/ + _fdim = INT2FIX(_this->width); + repeat_s = (_this->mod & GF_TEXTURE_REPEAT_S); + if (!repeat_s && (x <- _fdim)) x=0; + while (x<0) x += _fdim; + + _fdim = INT2FIX(_this->height); + if (!(_this->mod & GF_TEXTURE_REPEAT_T) && (y <- _fdim)) y = 0; + while (y<0) y += _fdim; + + y0 = FIX2INT(y); + y0 = y0 % _this->height; + pix_line = _this->pixels + _this->stride*y0; + + while (count) { + x0 = FIX2INT(x); + if (repeat_s) { + x0 = (x0) % _this->width; + } else if (x0 >= (s32) _this->width) x0 = _this->width-1; + + x += _this->inc_x; + pix = _this->tx_get_pixel(pix_line + _this->Bpp*x0); + + if (_this->replace_col) { + __a = GF_COL_A(pix); + pix = ((__a<<24) | (_this->replace_col & 0x00FFFFFF)); + } + *data++ = pix; + count--; + } +} + +void evg_bmp_init(EVGStencil *p) +{ + GF_Point2D p0, p1; + EVG_Texture *_this = (EVG_Texture *) p; + + p0.x = p0.y = p1.y = 0; + p1.x = FIX_ONE; + gf_mx2d_apply_point(&_this->smat, &p0); + gf_mx2d_apply_point(&_this->smat, &p1); + _this->inc_x = p1.x - p0.x; + _this->inc_y = p1.y - p0.y; + + _this->replace_col = 0; + _this->cmat_is_replace = 0; + if (!_this->cmat.identity + && !_this->cmat.m[0] && !_this->cmat.m[1] && !_this->cmat.m[2] && !_this->cmat.m[3] + && !_this->cmat.m[5] && !_this->cmat.m[6] && !_this->cmat.m[7] && !_this->cmat.m[8] + && !_this->cmat.m[10] && !_this->cmat.m[11] && !_this->cmat.m[12] && !_this->cmat.m[13] + && !_this->cmat.m[15] && !_this->cmat.m[16] && !_this->cmat.m[17] && !_this->cmat.m[19]) { + _this->cmat_is_replace = 1; + _this->replace_col = GF_COL_ARGB(FIX2INT(_this->cmat.m[18]*255), FIX2INT(_this->cmat.m[4]*255), FIX2INT(_this->cmat.m[9]*255), FIX2INT(_this->cmat.m[14]*255)); + } + + if ((_this->alpha == 255) && !_this->smat.m[1] && !_this->smat.m[3] && (_this->cmat.identity || _this->cmat_is_replace)) { + _this->fill_run = bmp_fill_run_straight; + } else { + _this->fill_run = bmp_fill_run; + } +} + + +EVGStencil *evg_texture_brush() +{ + EVG_Texture *tmp; + GF_SAFEALLOC(tmp, EVG_Texture); + if (!tmp) return 0L; + + tmp->fill_run = bmp_fill_run; + tmp->type = GF_STENCIL_TEXTURE; + /*default is using the surface settings*/ + tmp->filter = GF_TEXTURE_FILTER_DEFAULT; + tmp->mod = 0; + gf_cmx_init(&tmp->cmat); + tmp->alpha = 255; + return (EVGStencil *) tmp; +} + + +/*by casting to u32 the input ARGB (BGRA on little endian) we get back to 0xAARRGGBB format on all machines*/ +u32 get_pix_argb(char *pix) {return *(u32 *) pix;} +u32 get_pix_rgba(char *pix) { return GF_COL_ARGB(*(pix+3) & 0xFF, *pix & 0xFF, *(pix+1) & 0xFF, *(pix+2) & 0xFF); } +/*same as argb: xxrgb, with indianness support*/ +u32 get_pix_rgb_32(char *pix) { return ((*(u32 *) pix) | 0xFF000000); } +u32 get_pix_rgb_24(char *pix) { return GF_COL_ARGB(0xFF, *pix & 0xFF, *(pix+1) & 0xFF, *(pix+2) & 0xFF); } +u32 get_pix_bgr_24(char *pix) { return GF_COL_ARGB(0xFF, *(pix+2) & 0xFF, * (pix+1) & 0xFF, *pix & 0xFF); } +u32 get_pix_444(char *pix) { u16 val = *(u16*)pix; return GF_COL_ARGB(0xFF, (u8) ( (val >> 4) & 0xf0), (u8) ( (val) & 0xf0), (u8) ( (val << 4) & 0xf0) ); } +u32 get_pix_555(char *pix) { u16 val = *(u16*)pix; return GF_COL_ARGB(0xFF, (u8) ( (val >> 7) & 0xf8), (u8) ( (val >> 2) & 0xf8), (u8) ( (val << 3) & 0xf8) ); } +u32 get_pix_565(char *pix) { u16 val = *(u16*)pix; return GF_COL_ARGB(0xFF, (u8) ( (val >> 8) & 0xf8), (u8) ( (val >> 3) & 0xfc), (u8) ( (val << 3) & 0xf8) ); } +u32 get_pix_grey(char *pix) { u8 val = *pix; return GF_COL_ARGB(0xFF, val, val, val); } +u32 get_pix_alphagrey(char *pix) { return GF_COL_ARGB((u8) *(pix+1), (u8) *pix, (u8) *pix, (u8) *pix); } + +static void texture_set_callback(EVG_Texture *_this) +{ + switch (_this->pixel_format) { + case GF_PIXEL_RGBA: + _this->tx_get_pixel = get_pix_rgba; + return; + case GF_PIXEL_ARGB: + _this->tx_get_pixel = get_pix_argb; + return; + case GF_PIXEL_RGB_32: + _this->tx_get_pixel = get_pix_rgb_32; + return; + case GF_PIXEL_RGB_24: + _this->tx_get_pixel = get_pix_rgb_24; + return; + case GF_PIXEL_BGR_24: + _this->tx_get_pixel = get_pix_bgr_24; + return; + case GF_PIXEL_RGB_444: + _this->tx_get_pixel = get_pix_444; + return; + case GF_PIXEL_RGB_555: + _this->tx_get_pixel = get_pix_555; + return; + case GF_PIXEL_RGB_565: + _this->tx_get_pixel = get_pix_565; + return; + case GF_PIXEL_GREYSCALE: + _this->tx_get_pixel = get_pix_grey; + return; + case GF_PIXEL_ALPHAGREY: + _this->tx_get_pixel = get_pix_alphagrey; + return; + } +} + +GF_Err evg_stencil_set_texture(GF_STENCIL st, char *pixels, u32 width, u32 height, u32 stride, GF_PixelFormat pixelFormat, GF_PixelFormat destination_format_hint, Bool no_copy) +{ + EVG_Texture *_this = (EVG_Texture *) st; + if (!_this || (_this->type != GF_STENCIL_TEXTURE) || !pixels || !width || !height || !stride || _this->owns_texture) + return GF_BAD_PARAM; + + _this->pixels = 0L; + _this->is_converted = 1; + + switch (pixelFormat) { + case GF_PIXEL_ARGB: + case GF_PIXEL_RGBA: + case GF_PIXEL_RGB_32: + _this->Bpp = 4; + break; + case GF_PIXEL_RGB_24: + case GF_PIXEL_BGR_24: + _this->Bpp = 3; + break; + case GF_PIXEL_RGB_555: + case GF_PIXEL_RGB_565: + case GF_PIXEL_RGB_444: + case GF_PIXEL_ALPHAGREY: + _this->Bpp = 2; + break; + case GF_PIXEL_GREYSCALE: + _this->Bpp = 1; + break; + case GF_PIXEL_YV12: + case GF_PIXEL_IYUV: + case GF_PIXEL_I420: + _this->orig_format = GF_PIXEL_YV12; + _this->orig_buf = pixels; + _this->orig_stride = stride; + _this->is_converted = 0; + break; + case GF_PIXEL_YUVA: + _this->orig_format = GF_PIXEL_YUVA; + _this->orig_buf = pixels; + _this->orig_stride = stride; + _this->is_converted = 0; + break; + default: + /*the rest is not supported (eg BGR32)*/ + return GF_NOT_SUPPORTED; + } + _this->pixel_format = pixelFormat; + _this->width = width; + _this->height = height; + _this->stride = stride; + _this->pixels = (char *) pixels; + texture_set_callback(_this); + return GF_OK; +} + + +GF_Err evg_stencil_set_tiling(GF_STENCIL st, GF_TextureTiling mode) +{ + EVG_Texture *_this = (EVG_Texture *) st; + if (!_this || (_this->type != GF_STENCIL_TEXTURE)) return GF_BAD_PARAM; + _this->mod = mode; + return GF_OK; +} + +GF_Err evg_stencil_set_filter(GF_STENCIL st, GF_TextureFilter filter_mode) +{ + EVG_Texture *_this = (EVG_Texture *) st; + if (!_this || (_this->type != GF_STENCIL_TEXTURE)) return GF_BAD_PARAM; + _this->filter = filter_mode; + return GF_OK; +} + +GF_Err evg_stencil_set_color_matrix(GF_STENCIL st, GF_ColorMatrix *cmat) +{ + EVG_Texture *_this = (EVG_Texture *)st; + if (!_this) return GF_BAD_PARAM; + if (!cmat) gf_cmx_init(&_this->cmat); + else gf_cmx_copy(&_this->cmat, cmat); + return GF_OK; +} + +GF_Err evg_stencil_set_alpha(GF_STENCIL st, u8 alpha) +{ + EVG_Texture *_this = (EVG_Texture *)st; + if (!_this) return GF_BAD_PARAM; + if (_this->type==GF_STENCIL_SOLID) return GF_BAD_PARAM; + if (_this->type==GF_STENCIL_TEXTURE) + _this->alpha = alpha; + else + ((EVG_BaseGradient*)st)->alpha = alpha; + return GF_OK; +} + + +/*internal*/ +void evg_set_texture_active(EVGStencil *st) +{ + GF_VideoSurface src, dst; + EVG_Texture *_this = (EVG_Texture *)st; + if (_this->is_converted) return; + + /*perform YUV->RGB*/ + + if (_this->orig_format == GF_PIXEL_YV12) { + _this->Bpp = 3; + _this->pixel_format = GF_PIXEL_RGB_24; + } else { + _this->Bpp = 4; + _this->pixel_format = GF_PIXEL_ARGB; + } + if (_this->Bpp * _this->width * _this->height > _this->conv_size) { + if (_this->conv_buf) free(_this->conv_buf); + _this->conv_size = _this->Bpp * _this->width * _this->height; + _this->conv_buf = (unsigned char *) malloc(sizeof(unsigned char)*_this->conv_size); + } + + src.height = _this->height; + src.width = _this->width; + src.pitch = _this->orig_stride; + src.pixel_format = _this->orig_format; + src.video_buffer = _this->orig_buf; + + dst.width = _this->width; + dst.height = _this->height; + dst.pitch = _this->Bpp * _this->width; + dst.pixel_format = _this->pixel_format; + dst.video_buffer = _this->conv_buf; + + gf_stretch_bits(&dst, &src, NULL, NULL, 0, 0xFF, 0, NULL, NULL); + + _this->is_converted = 1; + _this->pixels = (char *) _this->conv_buf; + _this->stride = _this->Bpp * _this->width; + texture_set_callback(_this); +} + +GF_Err evg_stencil_create_texture(GF_STENCIL st, u32 width, u32 height, GF_PixelFormat pixelFormat) +{ + EVG_Texture *_this = (EVG_Texture *)st; + if (_this->orig_buf) return GF_BAD_PARAM; + _this->pixels = 0L; + _this->is_converted = 1; + + switch (pixelFormat) { + case GF_PIXEL_ARGB: + case GF_PIXEL_RGBA: + case GF_PIXEL_RGB_32: + _this->Bpp = 4; + break; + case GF_PIXEL_RGB_24: + case GF_PIXEL_BGR_24: + _this->Bpp = 3; + break; + case GF_PIXEL_RGB_555: + case GF_PIXEL_RGB_565: + case GF_PIXEL_RGB_444: + case GF_PIXEL_ALPHAGREY: + _this->Bpp = 2; + break; + case GF_PIXEL_GREYSCALE: + _this->Bpp = 1; + break; + default: + return GF_NOT_SUPPORTED; + } + _this->pixel_format = pixelFormat; + _this->width = width; + _this->height = height; + _this->stride = width*_this->Bpp; + + if (_this->pixels) free(_this->pixels); + _this->pixels = (char *) malloc(sizeof(char) * _this->stride * _this->height); + memset(_this->pixels, 0, sizeof(char) * _this->stride * _this->height); + _this->owns_texture = 1; + texture_set_callback(_this); + return GF_OK; +} diff --git a/modules/soft_raster/surface.c b/modules/soft_raster/surface.c new file mode 100644 index 0000000..9f136cc --- /dev/null +++ b/modules/soft_raster/surface.c @@ -0,0 +1,635 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / software 2D rasterizer module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + + +#include "rast_soft.h" + +static void get_surface_world_matrix(EVGSurface *_this, GF_Matrix2D *mat) +{ + gf_mx2d_init(*mat); + if (_this->center_coords) { + gf_mx2d_add_scale(mat, FIX_ONE, -FIX_ONE); + gf_mx2d_add_translation(mat, INT2FIX(_this->width / 2), INT2FIX(_this->height / 2)); + } +} + +GF_SURFACE evg_surface_new(GF_Raster2D *_dr, Bool center_coords) +{ + EVGSurface *_this; + GF_SAFEALLOC(_this, EVGSurface); + if (_this) { + _this->center_coords = center_coords; + _this->texture_filter = GF_TEXTURE_FILTER_DEFAULT; + _this->ftparams.source = &_this->ftoutline; + _this->ftparams.user = _this; + _this->raster = evg_raster_new(); + } + return _this; +} + +void evg_surface_delete(GF_SURFACE _this) +{ + EVGSurface *surf = (EVGSurface *)_this; +#ifndef INLINE_POINT_CONVERSION + if (surf->points) free(surf->points); +#endif + if (surf->stencil_pix_run) free(surf->stencil_pix_run); + evg_raster_del(surf->raster); + free(surf); +} + +GF_Err evg_surface_set_matrix(GF_SURFACE _this, GF_Matrix2D *mat) +{ + GF_Matrix2D tmp; + EVGSurface *surf = (EVGSurface *)_this; + if (!surf) return GF_BAD_PARAM; + get_surface_world_matrix(surf, &surf->mat); + if (!mat) return GF_OK; + gf_mx2d_init(tmp); + gf_mx2d_add_matrix(&tmp, mat); + gf_mx2d_add_matrix(&tmp, &surf->mat); + gf_mx2d_copy(surf->mat, tmp); + return GF_OK; +} + + +GF_Err evg_surface_attach_to_callbacks(GF_SURFACE _this, GF_RasterCallback *callbacks, u32 width, u32 height) +{ + EVGSurface *surf = (EVGSurface *)_this; + if (!surf || !width || !height || !callbacks) return GF_BAD_PARAM; + if (!callbacks->cbk || !callbacks->fill_run_alpha || !callbacks->fill_run_no_alpha) return GF_BAD_PARAM; + + surf->width = width; + surf->height = height; + if (surf->stencil_pix_run) free(surf->stencil_pix_run); + surf->stencil_pix_run = (u32 *) malloc(sizeof(u32) * (width+2)); + + surf->raster_cbk = callbacks->cbk; + surf->raster_fill_run_alpha = callbacks->fill_run_alpha; + surf->raster_fill_run_no_alpha = callbacks->fill_run_no_alpha; + evg_surface_set_matrix(surf, NULL); + return GF_OK; +} + + +GF_Err evg_surface_attach_to_buffer(GF_SURFACE _this, char *pixels, u32 width, u32 height, u32 stride, GF_PixelFormat pixelFormat) +{ + u32 BPP; + EVGSurface *surf = (EVGSurface *)_this; + if (!surf || !pixels || (pixelFormat>GF_PIXEL_YUVA)) return GF_BAD_PARAM; + + switch (pixelFormat) { +#ifdef GF_RGB_444_SUPORT + case GF_PIXEL_RGB_444: +#endif +#ifdef GF_RGB_555_SUPORT + case GF_PIXEL_RGB_555: +#endif + case GF_PIXEL_RGB_565: + BPP = 2; + break; + case GF_PIXEL_BGR_24: + case GF_PIXEL_RGB_24: + BPP = 3; + break; + case GF_PIXEL_RGB_32: + case GF_PIXEL_ARGB: + case GF_PIXEL_RGBA: + BPP = 4; + break; + case GF_PIXEL_BGR_32: + default: + return GF_NOT_SUPPORTED; + } + surf->stride = stride; + if (!surf->stencil_pix_run || (surf->width != width)) { + if (surf->stencil_pix_run) free(surf->stencil_pix_run); + surf->stencil_pix_run = (u32 *) malloc(sizeof(u32) * (width+2)); + } + surf->width = width; + surf->height = height; + surf->pixels = (char*)pixels; + surf->pixelFormat = pixelFormat; + surf->BPP = BPP; + + surf->raster_cbk = NULL; + surf->raster_fill_run_alpha = NULL; + surf->raster_fill_run_no_alpha = NULL; + evg_surface_set_matrix(_this, NULL); + return GF_OK; +} + + +GF_Err evg_surface_attach_to_texture(GF_SURFACE _this, GF_STENCIL sten) +{ + u32 BPP; + EVGSurface *surf = (EVGSurface *)_this; + EVG_Texture *tx = (EVG_Texture *) sten;; + if (!surf || (tx->type != GF_STENCIL_TEXTURE)) return GF_BAD_PARAM; + + switch (tx->pixel_format) { + case GF_PIXEL_GREYSCALE: + BPP = 1; + break; + case GF_PIXEL_ALPHAGREY: +#ifdef GF_RGB_444_SUPORT + case GF_PIXEL_RGB_444: +#endif +#ifdef GF_RGB_555_SUPORT + case GF_PIXEL_RGB_555: +#endif + case GF_PIXEL_RGB_565: + BPP = 2; + break; + case GF_PIXEL_BGR_24: + case GF_PIXEL_RGB_24: + BPP = 3; + break; + case GF_PIXEL_RGB_32: + case GF_PIXEL_ARGB: + case GF_PIXEL_RGBA: + BPP = 4; + break; + default: + return GF_NOT_SUPPORTED; + } + surf->stride = tx->stride; + if (surf->stencil_pix_run) free(surf->stencil_pix_run); + surf->stencil_pix_run = (u32 *) malloc(sizeof(u32) * (tx->width+2)); + + surf->width = tx->width; + surf->height = tx->height; + surf->pixels = tx->pixels; + surf->pixelFormat = tx->pixel_format; + surf->BPP = BPP; + surf->raster_cbk = NULL; + surf->raster_fill_run_alpha = NULL; + surf->raster_fill_run_no_alpha = NULL; + evg_surface_set_matrix(surf, NULL); + return GF_OK; +} + +void evg_surface_detach(GF_SURFACE _this) +{ + EVGSurface *surf = (EVGSurface *)_this; + surf->raster_cbk = NULL; + surf->raster_fill_run_alpha = NULL; + surf->raster_fill_run_no_alpha = NULL; +} + +GF_Err evg_surface_clear(GF_SURFACE _this, GF_IRect *rc, u32 color) +{ + GF_IRect clear; + EVGSurface *surf = (EVGSurface *)_this; + if (!surf) return GF_BAD_PARAM; + + if (rc) { + s32 _x, _y; + if (surf->center_coords) { + _x = rc->x + surf->width / 2; + _y = surf->height / 2 - rc->y; + } else { + _x = rc->x; + _y = rc->y - rc->height; + } + + clear.width = (u32) rc->width; + if (_x>=0) { + clear.x = (u32) _x; + } else { + if ( (s32) clear.width + _x < 0) return GF_BAD_PARAM; + clear.width += _x; + clear.x = 0; + } + clear.height = (u32) rc->height; + if (_y>=0) { + clear.y = _y; + } else { + if ( (s32) clear.height + _y < 0) return GF_BAD_PARAM; + clear.height += _y; + clear.y = 0; + } + } else { + clear.x = clear.y = 0; + clear.width = surf->width; + clear.height = surf->height; + } + + if (surf->raster_cbk) { + return evg_surface_clear_user(surf, clear, color); + } + + switch (surf->pixelFormat) { + case GF_PIXEL_ARGB: + case GF_PIXEL_RGB_32: + return evg_surface_clear_argb(surf, clear, color); + case GF_PIXEL_RGBA: + return evg_surface_clear_rgba(surf, clear, color); + case GF_PIXEL_BGR_24: + return evg_surface_clear_bgr(surf, clear, color); + case GF_PIXEL_RGB_24: + return evg_surface_clear_rgb(surf, clear, color); + case GF_PIXEL_RGB_565: + return evg_surface_clear_565(surf, clear, color); +#ifdef GF_RGB_444_SUPORT + case GF_PIXEL_RGB_444: + return evg_surface_clear_444(surf, clear, color); +#endif +#ifdef GF_RGB_555_SUPORT + case GF_PIXEL_RGB_555: + return evg_surface_clear_555(surf, clear, color); +#endif + default: + return GF_BAD_PARAM; + } +} + +GF_Err evg_surface_set_raster_level(GF_SURFACE _this, GF_RasterLevel RasterSetting) +{ + EVGSurface *surf = (EVGSurface *)_this; + if (!surf) return GF_BAD_PARAM; + switch (RasterSetting) { + case GF_RASTER_HIGH_QUALITY: + surf->AALevel = 1;/*don't draw pixels with 0 alpha...*/ + surf->texture_filter = GF_TEXTURE_FILTER_HIGH_QUALITY; + break; + case GF_RASTER_MID: + surf->AALevel = 90; + surf->texture_filter = GF_TEXTURE_FILTER_HIGH_QUALITY; + break; + case GF_RASTER_HIGH_SPEED: + default: + surf->AALevel = 180; + surf->texture_filter = GF_TEXTURE_FILTER_HIGH_SPEED; + break; + } + return GF_OK; +} + + +GF_Err evg_surface_set_clipper(GF_SURFACE _this , GF_IRect *rc) +{ + EVGSurface *surf = (EVGSurface *)_this; + if (!surf) return GF_BAD_PARAM; + if (rc) { + surf->clipper = *rc; + surf->useClipper = 1; + /*clipper was given in BIFS like coords, we work with bottom-min for rect, (0,0) top-left of surface*/ + if (surf->center_coords) { + surf->clipper.x += surf->width / 2; + surf->clipper.y = surf->height / 2 - rc->y; + } else { + surf->clipper.y -= rc->height; + } + + if (surf->clipper.x <=0) { + if (surf->clipper.x + (s32) surf->clipper.width < 0) return GF_BAD_PARAM; + surf->clipper.width += surf->clipper.x; + surf->clipper.x = 0; + } + if (surf->clipper.y <=0) { + if (surf->clipper.y + (s32) surf->clipper.height < 0) return GF_BAD_PARAM; + surf->clipper.height += surf->clipper.y; + surf->clipper.y = 0; + } + if (surf->clipper.x + surf->clipper.width > (s32) surf->width) { + surf->clipper.width = surf->width - surf->clipper.x; + } + if (surf->clipper.y + surf->clipper.height > (s32) surf->height) { + surf->clipper.height = surf->height - surf->clipper.y; + } + } else { + surf->useClipper = 0; + } + return GF_OK; +} + + +static Bool setup_grey_callback(EVGSurface *surf) +{ + u32 col, a; + Bool use_const = 1; + + if (surf->sten->type == GF_STENCIL_SOLID) { + col = surf->fill_col = ((EVG_Brush *)surf->sten)->color; + a = GF_COL_A(surf->fill_col); + } else { + col = a = 0; + use_const = 0; + } + + if (surf->raster_cbk) { + if (use_const) { + if (!a) return 0; + if (a!=0xFF) { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_user_fill_const_a; + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_user_fill_const; + } + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_user_fill_var; + } + return 1; + } + + switch (surf->pixelFormat) { + case GF_PIXEL_ARGB: + if (use_const) { + if (!a) return 0; + if (a!=0xFF) { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_argb_fill_const_a; + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_argb_fill_const; + } + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_argb_fill_var; + } + break; + + case GF_PIXEL_RGBA: + if (use_const) { + if (!a) return 0; + if (a!=0xFF) { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_rgba_fill_const_a; + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_rgba_fill_const; + } + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_rgba_fill_var; + } + break; + + case GF_PIXEL_RGB_32: + if (use_const) { + if (!a) return 0; + if (a!=0xFF) { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_rgb32_fill_const_a; + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_rgb32_fill_const; + } + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_rgb32_fill_var; + } + break; + case GF_PIXEL_RGB_24: + if (use_const) { + if (!a) return 0; + if (a!=0xFF) { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_rgb_fill_const_a; + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_rgb_fill_const; + } + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_rgb_fill_var; + } + break; + case GF_PIXEL_BGR_24: + if (use_const) { + if (!a) return 0; + if (a!=0xFF) { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_bgr_fill_const_a; + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_bgr_fill_const; + } + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_bgr_fill_var; + } + break; + case GF_PIXEL_RGB_565: + if (use_const) { + surf->fill_565 = GF_COL_TO_565(col); + if (!a) return 0; + if (a!=0xFF) { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_565_fill_const_a; + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_565_fill_const; + } + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_565_fill_var; + } + break; +#ifdef GF_RGB_444_SUPORT + if (use_const) { + surf->fill_444 = GF_COL_TO_444(col); + if (!a) return 0; + if (a!=0xFF) { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_444_fill_const_a; + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_444_fill_const; + } + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_444_fill_var; + } + break; +#endif +#ifdef GF_RGB_555_SUPORT + case GF_PIXEL_RGB_555: + if (use_const) { + surf->fill_555 = GF_COL_TO_555(col); + if (!a) return 0; + if (a!=0xFF) { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_555_fill_const_a; + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_555_fill_const; + } + } else { + surf->ftparams.gray_spans = (EVG_Raster_Span_Func) evg_555_fill_var; + } + break; +#endif + } + return 1; +} + + +GF_Err evg_surface_set_path(GF_SURFACE _this, GF_Path *gp) +{ +#ifndef INLINE_POINT_CONVERSION + u32 i; + GF_Point2D pt; +#endif + EVGSurface *surf = (EVGSurface *)_this; + + if (!surf) return GF_BAD_PARAM; + if (!gp || !gp->n_points) { + surf->ftoutline.n_points = 0; + surf->ftoutline.n_contours = 0; + return GF_OK; + } + gf_path_flatten(gp); + surf->ftoutline.n_points = gp->n_points; + surf->ftoutline.n_contours = gp->n_contours; + + surf->ftoutline.tags = gp->tags; + surf->ftoutline.contours = (s32*) gp->contours; + + /*store path bounds for gradient/textures*/ + gf_path_get_bounds(gp, &surf->path_bounds); + /*invert Y (ft uses min Y)*/ + surf->path_bounds.y -= surf->path_bounds.height; + + surf->ftoutline.flags = 0; + if (gp->flags & GF_PATH_FILL_ZERO_NONZERO) surf->ftoutline.flags = GF_PATH_FILL_ZERO_NONZERO; + +#ifdef INLINE_POINT_CONVERSION + surf->ftoutline.n_points = gp->n_points; + surf->ftoutline.points = gp->points; + surf->ftparams.mx = &surf->mat; +#else + if (surf->pointlen < gp->n_points) { + surf->points = realloc(surf->points, sizeof(EVG_Vector) * gp->n_points); + if (surf->points == NULL) { + surf->pointlen = 0; + return GF_OUT_OF_MEM; + } + surf->pointlen = gp->n_points; + } + surf->ftoutline.points = surf->points; + + for (i=0; i<gp->n_points; i++) { + pt = gp->points[i]; + gf_mx2d_apply_point(&surf->mat, &pt); +#ifdef GPAC_FIXED_POINT + surf->points[i].x = pt.x; + surf->points[i].y = pt.y; +#else + /*move to 16.16 representation*/ + surf->points[i].x = (u32) (pt.x * 0x10000L); + surf->points[i].y = (u32) (pt.y * 0x10000L); +#endif + } +#endif + return GF_OK; +} + +//static void gray_spans_stub(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf){} + +GF_Err evg_surface_fill(GF_SURFACE _this, GF_STENCIL stencil) +{ + GF_Rect rc; + GF_Matrix2D mat, st_mat; + Bool restore_filter; + EVGSurface *surf = (EVGSurface *)_this; + EVGStencil *sten = (EVGStencil *)stencil; + if (!surf || !stencil) return GF_BAD_PARAM; + if (!surf->ftoutline.n_points) return GF_OK; + surf->sten = sten; + + /*setup ft raster calllbacks*/ + if (!setup_grey_callback(surf)) return GF_OK; + +// surf->ftparams.gray_spans = gray_spans_stub; + + get_surface_world_matrix(surf, &mat); + + restore_filter = 0; + /*get path frame for texture convertion */ + if (sten->type != GF_STENCIL_SOLID) { + rc = surf->path_bounds; + gf_mx2d_apply_rect(&mat, &rc); + rc.x = rc.y = 0; + /*assign target frame and matrix*/ + sten->frame = rc; + gf_mx2d_copy(sten->pmat, surf->mat); + gf_mx2d_inverse(&sten->pmat); + + gf_mx2d_copy(st_mat, sten->smat); + gf_mx2d_init(sten->smat); + switch (sten->type) { + case GF_STENCIL_TEXTURE: + if (! ((EVG_Texture *)sten)->pixels) return GF_BAD_PARAM; + + if (((EVG_Texture *)sten)->mod & GF_TEXTURE_FLIP) { + if (!surf->center_coords) gf_mx2d_add_scale(&sten->smat, FIX_ONE, -FIX_ONE); + } else { + if (surf->center_coords) gf_mx2d_add_scale(&sten->smat, FIX_ONE, -FIX_ONE); + } + evg_set_texture_active(sten); + gf_mx2d_add_matrix(&sten->smat, &st_mat); + gf_mx2d_add_matrix(&sten->smat, &mat); + gf_mx2d_inverse(&sten->smat); + evg_bmp_init(sten); + if (((EVG_Texture *)sten)->filter == GF_TEXTURE_FILTER_DEFAULT) { + restore_filter = 1; + ((EVG_Texture *)sten)->filter = surf->texture_filter; + } + + break; + case GF_STENCIL_LINEAR_GRADIENT: + { + EVG_LinearGradient *lin = (EVG_LinearGradient *)sten; + gf_mx2d_add_matrix(&sten->smat, &st_mat); + gf_mx2d_add_matrix(&sten->smat, &mat); + gf_mx2d_inverse(&sten->smat); + /*and finalize matrix in gradient coord system*/ + gf_mx2d_add_matrix(&sten->smat, &lin->vecmat); + gf_mx2d_add_scale(&sten->smat, INT2FIX(1<<EVGGRADIENTSCALEBITS), INT2FIX(1<<EVGGRADIENTSCALEBITS)); + + } + break; + case GF_STENCIL_RADIAL_GRADIENT: + { + EVG_RadialGradient *rad = (EVG_RadialGradient*)sten; + gf_mx2d_copy(sten->smat, st_mat); + gf_mx2d_add_matrix(&sten->smat, &mat); + gf_mx2d_inverse(&sten->smat); + gf_mx2d_add_translation(&sten->smat, -rad->center.x, -rad->center.y); + gf_mx2d_add_scale(&sten->smat, gf_invfix(rad->radius.x), gf_invfix(rad->radius.y)); + + rad->d_f.x = gf_divfix(rad->focus.x - rad->center.x, rad->radius.x); + rad->d_f.y = gf_divfix(rad->focus.y - rad->center.y, rad->radius.y); + /*init*/ + evg_radial_init(rad); + } + break; + } + } + + if (surf->useClipper) { + surf->ftparams.clip_xMin = surf->clipper.x; + surf->ftparams.clip_yMin = surf->clipper.y; + surf->ftparams.clip_xMax = (surf->clipper.x + surf->clipper.width); + surf->ftparams.clip_yMax = (surf->clipper.y + surf->clipper.height); + } else { + surf->ftparams.clip_xMin = 0; + surf->ftparams.clip_yMin = 0; + surf->ftparams.clip_xMax = (surf->width); + surf->ftparams.clip_yMax = (surf->height); + } + + /*and call the raster*/ + evg_raster_render(surf->raster, &surf->ftparams); + + /*restore stencil matrix*/ + if (sten->type != GF_STENCIL_SOLID) { + gf_mx2d_copy(sten->smat, st_mat); + if (restore_filter) ((EVG_Texture *)sten)->filter = GF_TEXTURE_FILTER_DEFAULT; + } + surf->sten = 0L; + return GF_OK; +} + + + diff --git a/modules/svg_in/Makefile b/modules/svg_in/Makefile new file mode 100644 index 0000000..1a4a140 --- /dev/null +++ b/modules/svg_in/Makefile @@ -0,0 +1,67 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/svg_in + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +ifeq ($(CONFIG_ZLIB), local) +CFLAGS+= -I$(LOCAL_INC_PATH)/zlib +EXTRALIBS+=-L../extra_lib/lib/gcc +endif + +#common obj +OBJS= svg_in.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_svg_in.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols svg_in.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac -lz + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/svg_in/svg_in.c b/modules/svg_in/svg_in.c new file mode 100644 index 0000000..5590786 --- /dev/null +++ b/modules/svg_in/svg_in.c @@ -0,0 +1,430 @@ +/* + * GPAC Multimedia Framework + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / SVG Loader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <gpac/internal/terminal_dev.h> +#include <gpac/scene_manager.h> +#include <gpac/constants.h> +#include <zlib.h> + +#ifndef GPAC_DISABLE_SVG + +typedef struct +{ + GF_SceneLoader loader; + GF_InlineScene *inline_scene; + u8 oti; + char *file_name; + u32 file_size; + u32 sax_max_duration; + u16 base_es_id; + u32 file_pos; + gzFile src; +} SVGIn; + +static Bool svg_check_download(SVGIn *svgin) +{ + u32 size; + FILE *f = fopen(svgin->file_name, "rb"); + fseek(f, 0, SEEK_END); + size = ftell(f); + fclose(f); + if (size==svgin->file_size) return 1; + return 0; +} + +#define SVG_PROGRESSIVE_BUFFER_SIZE 4096 + +static GF_Err svgin_deflate(SVGIn *svgin, char *buffer, u32 buffer_len) +{ + GF_Err e; + char svg_data[2049]; + int err; + u32 done = 0; + z_stream d_stream; + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + d_stream.next_in = (Bytef*)buffer; + d_stream.avail_in = buffer_len; + d_stream.next_out = (Bytef*)svg_data; + d_stream.avail_out = 2048; + + err = inflateInit(&d_stream); + if (err == Z_OK) { + while (d_stream.total_in < buffer_len) { + err = inflate(&d_stream, Z_NO_FLUSH); + if (err < Z_OK) { + e = GF_NON_COMPLIANT_BITSTREAM; + break; + } + svg_data[d_stream.total_out - done] = 0; + e = gf_sm_load_string(&svgin->loader, svg_data, 0); + if (e || (err== Z_STREAM_END)) break; + done = d_stream.total_out; + d_stream.avail_out = 2048; + d_stream.next_out = (Bytef*)svg_data; + } + inflateEnd(&d_stream); + return GF_OK; + } + return GF_NON_COMPLIANT_BITSTREAM; +} + +static GF_Err SVG_ProcessData(GF_SceneDecoder *plug, char *inBuffer, u32 inBufferLength, + u16 ES_ID, u32 stream_time, u32 mmlevel) +{ + GF_Err e = GF_OK; + SVGIn *svgin = (SVGIn *)plug->privateStack; + + if (stream_time==(u32)-1) { + if (svgin->src) gzclose(svgin->src); + svgin->src = NULL; + gf_sm_load_done(&svgin->loader); + svgin->loader.fileName = NULL; + svgin->file_pos = 0; + gf_sg_reset(svgin->inline_scene->graph); + return GF_OK; + } + + switch (svgin->oti) { + /*!OTI for SVG dummy stream (dsi = file name) - GPAC internal*/ + case GPAC_OTI_PRIVATE_SCENE_SVG: + /*full doc parsing*/ + if ((svgin->sax_max_duration==(u32) -1) && svgin->file_size) { + /*init step*/ + if (!svgin->loader.fileName) { + /*not done yet*/ + if (!svg_check_download(svgin)) return GF_OK; + svgin->loader.fileName = svgin->file_name; + e = gf_sm_load_init(&svgin->loader); + } else { + e = gf_sm_load_run(&svgin->loader); + } + } + /*chunk parsing*/ + else { + u32 entry_time; + char file_buf[SVG_PROGRESSIVE_BUFFER_SIZE+2]; + /*initial load*/ + if (!svgin->src && !svgin->file_pos) { + svgin->src = gzopen(svgin->file_name, "rb"); + if (!svgin->src) return GF_URL_ERROR; + svgin->loader.fileName = svgin->file_name; + } + e = GF_OK; + entry_time = gf_sys_clock(); + + while (1) { + u32 diff; + s32 nb_read; + nb_read = gzread(svgin->src, file_buf, SVG_PROGRESSIVE_BUFFER_SIZE); + /*we may have read nothing but we still need to call parse in case the parser got suspended*/ + if (nb_read<=0) { + nb_read = 0; + e = gf_sm_load_run(&svgin->loader); + if ((e==GF_EOS) && gzeof(svgin->src)) { + gf_set_progress("SVG Parsing", svgin->file_pos, svgin->file_size); + gzclose(svgin->src); + svgin->src = NULL; + } + goto exit; + } + + file_buf[nb_read] = file_buf[nb_read+1] = 0; + + e = gf_sm_load_string(&svgin->loader, file_buf, 0); + svgin->file_pos += nb_read; + + + + /*handle decompression*/ + if (svgin->file_pos > svgin->file_size) svgin->file_size = svgin->file_pos + 1; + if (e) break; + + diff = gf_sys_clock() - entry_time; + gf_set_progress("SVG Parsing", svgin->file_pos, svgin->file_size); + if (diff > svgin->sax_max_duration) break; + } + } + break; + + /*!OTI for streaming SVG - GPAC internal*/ + case GPAC_OTI_SCENE_SVG: + e = gf_sm_load_string(&svgin->loader, inBuffer, 0); + break; + + /*!OTI for streaming SVG + gz - GPAC internal*/ + case GPAC_OTI_SCENE_SVG_GZ: + e = svgin_deflate(svgin, inBuffer, inBufferLength); + break; + + /*!OTI for DIMS (dsi = 3GPP DIMS configuration) - GPAC internal*/ + case GPAC_OTI_SCENE_DIMS: + { + u8 prev, dims_hdr; + u32 nb_bytes, pos, size; + GF_BitStream *bs = gf_bs_new(inBuffer, inBufferLength, GF_BITSTREAM_READ); +// +// FILE *f = fopen("dump.svg", "wb"); +// + while (gf_bs_available(bs)) { + pos = (u32) gf_bs_get_position(bs); + size = gf_bs_read_u16(bs); + nb_bytes = 2; + /*GPAC internal hack*/ + if (!size) { + size = gf_bs_read_u32(bs); + nb_bytes = 6; + } +// fwrite( inBuffer + pos + nb_bytes + 1, 1, size - 1, f ); + + dims_hdr = gf_bs_read_u8(bs); + prev = inBuffer[pos + nb_bytes + size]; + + inBuffer[pos + nb_bytes + size] = 0; + if (dims_hdr & GF_DIMS_UNIT_C) { + e = svgin_deflate(svgin, inBuffer + pos + nb_bytes + 1, size - 1); + } else { + e = gf_sm_load_string(&svgin->loader, inBuffer + pos + nb_bytes + 1, 0); + } + inBuffer[pos + nb_bytes + size] = prev; + gf_bs_skip_bytes(bs, size-1); + + } +// fclose(f); + gf_bs_del(bs); + } + break; + + default: + return GF_BAD_PARAM; + } + +exit: + if ((svgin->inline_scene->graph_attached!=1) && (gf_sg_get_root_node(svgin->loader.scene_graph)!=NULL) ) { + gf_inline_attach_to_compositor(svgin->inline_scene); + } + /*prepare for next playback*/ + if (e==GF_EOS) { + gf_sm_load_done(&svgin->loader); + svgin->loader.fileName = NULL; + } + return e; +} + + + +static GF_Err SVG_AttachScene(GF_SceneDecoder *plug, GF_InlineScene *scene, Bool is_scene_decoder) +{ + SVGIn *svgin = (SVGIn *)plug->privateStack; + memset(&svgin->loader, 0, sizeof(GF_SceneLoader)); + svgin->loader.is = scene; + svgin->inline_scene = scene; + svgin->loader.scene_graph = scene->graph; + svgin->loader.localPath = gf_modules_get_option((GF_BaseInterface *)plug, "General", "CacheDirectory"); + /*Warning: svgin->loader.type may be overriden in attach stream */ + svgin->loader.type = GF_SM_LOAD_SVG_DA; + svgin->loader.flags = GF_SM_LOAD_FOR_PLAYBACK; + return GF_OK; +} + +static GF_Err SVG_ReleaseScene(GF_SceneDecoder *plug) +{ + return GF_OK; +} + +static GF_Err SVG_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) +{ + const char *sOpt; + GF_BitStream *bs; + SVGIn *svgin = (SVGIn *)plug->privateStack; + if (esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; + + svgin->loader.type = GF_SM_LOAD_SVG_DA; + /* decSpecInfo is not null only when reading from an SVG file (local or distant, cached or not) */ + switch (esd->decoderConfig->objectTypeIndication) { + case GPAC_OTI_SCENE_SVG: + case GPAC_OTI_SCENE_SVG_GZ: + svgin->loader.flags |= GF_SM_LOAD_CONTEXT_STREAMING; + /*no decSpecInfo defined for streaming svg yet*/ + break; + case GPAC_OTI_SCENE_DIMS: + svgin->loader.type = GF_SM_LOAD_DIMS; + svgin->loader.flags |= GF_SM_LOAD_CONTEXT_STREAMING; + /*decSpecInfo not yet supported for DIMS svg - we need properties at the scene level to store the + various indications*/ + break; + case GPAC_OTI_PRIVATE_SCENE_SVG: + default: + if (!esd->decoderConfig->decoderSpecificInfo) return GF_NON_COMPLIANT_BITSTREAM; + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + svgin->file_size = gf_bs_read_u32(bs); + svgin->file_pos = 0; + gf_bs_del(bs); + svgin->file_name = (char *) malloc(sizeof(char)*(1 + esd->decoderConfig->decoderSpecificInfo->dataLength - sizeof(u32)) ); + memcpy(svgin->file_name, esd->decoderConfig->decoderSpecificInfo->data + sizeof(u32), esd->decoderConfig->decoderSpecificInfo->dataLength - sizeof(u32) ); + svgin->file_name[esd->decoderConfig->decoderSpecificInfo->dataLength - sizeof(u32) ] = 0; + break; + } + svgin->oti = esd->decoderConfig->objectTypeIndication; + + if (!esd->dependsOnESID) svgin->base_es_id = esd->ESID; + + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "SAXLoader", "Progressive"); + if (sOpt && !strcmp(sOpt, "yes")) { + svgin->sax_max_duration = 30; + sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "SAXLoader", "MaxDuration"); + if (sOpt) { + svgin->sax_max_duration = atoi(sOpt); + } else { + svgin->sax_max_duration = 30; + gf_modules_set_option((GF_BaseInterface *)plug, "SAXLoader", "MaxDuration", "30"); + } + } else { + svgin->sax_max_duration = (u32) -1; + } + return GF_OK; +} + +static GF_Err SVG_DetachStream(GF_BaseDecoder *plug, u16 ES_ID) +{ + SVGIn *svgin = (SVGIn *)plug->privateStack; + if (svgin->file_name) free(svgin->file_name); + svgin->file_name = NULL; + gf_sm_load_done(&svgin->loader); + return GF_OK; +} + +const char *SVG_GetName(struct _basedecoder *plug) +{ + SVGIn *svgin = (SVGIn *)plug->privateStack; + if (svgin->oti==GPAC_OTI_PRIVATE_SCENE_SVG) return ((svgin->sax_max_duration==(u32)-1) && svgin->file_size) ? "GPAC SVG SAX Parser" : "GPAC SVG Progressive Parser"; + if (svgin->oti==GPAC_OTI_SCENE_SVG) return "GPAC Streaming SVG Parser"; + if (svgin->oti==GPAC_OTI_SCENE_SVG_GZ) return "GPAC Streaming SVGZ Parser"; + if (svgin->oti==GPAC_OTI_SCENE_DIMS) return "GPAC DIMS Parser"; + return "INTERNAL ERROR"; +} + +Bool SVG_CanHandleStream(GF_BaseDecoder *ifce, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + if (StreamType==GF_STREAM_PRIVATE_SCENE) { + if (ObjectType==GPAC_OTI_PRIVATE_SCENE_SVG) return 1; + return 0; + } else if (StreamType==GF_STREAM_SCENE) { + if (ObjectType==GPAC_OTI_SCENE_SVG) return 1; + if (ObjectType==GPAC_OTI_SCENE_SVG_GZ) return 1; + if (ObjectType==GPAC_OTI_SCENE_DIMS) return 1; + return 0; + } + return 0; +} + +static GF_Err SVG_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *cap) +{ + cap->cap.valueInt = 0; + if (cap->CapCode==GF_CODEC_PADDING_BYTES) { + /* Adding one byte of padding for \r\n problems*/ + cap->cap.valueInt = 1; + return GF_OK; + } + return GF_NOT_SUPPORTED; +} + +static GF_Err SVG_SetCapabilities(GF_BaseDecoder *plug, const GF_CodecCapability capability) +{ + return GF_OK; +} + +/*interface create*/ +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + SVGIn *svgin; + GF_SceneDecoder *sdec; + + if (InterfaceType != GF_SCENE_DECODER_INTERFACE) return NULL; + + GF_SAFEALLOC(sdec, GF_SceneDecoder) + GF_REGISTER_MODULE_INTERFACE(sdec, GF_SCENE_DECODER_INTERFACE, "GPAC SVG Parser", "gpac distribution"); + + GF_SAFEALLOC(svgin, SVGIn); + sdec->privateStack = svgin; + sdec->AttachStream = SVG_AttachStream; + sdec->CanHandleStream = SVG_CanHandleStream; + sdec->DetachStream = SVG_DetachStream; + sdec->AttachScene = SVG_AttachScene; + sdec->ReleaseScene = SVG_ReleaseScene; + sdec->ProcessData = SVG_ProcessData; + sdec->GetName = SVG_GetName; + sdec->SetCapabilities = SVG_SetCapabilities; + sdec->GetCapabilities = SVG_GetCapabilities; + return (GF_BaseInterface *)sdec; +} + + +/*interface destroy*/ +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + GF_SceneDecoder *sdec = (GF_SceneDecoder *)ifce; + SVGIn *svgin = (SVGIn *) sdec->privateStack; + if (sdec->InterfaceType != GF_SCENE_DECODER_INTERFACE) return; + + free(svgin); + free(sdec); +} + +/*interface query*/ +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_SCENE_DECODER_INTERFACE) return 1; + return 0; +} +#else + + +/*interface create*/ +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + return NULL; +} + + +/*interface destroy*/ +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ +} + +/*interface query*/ +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + return 0; +} +#endif diff --git a/modules/svg_in/svg_in.def b/modules/svg_in/svg_in.def new file mode 100644 index 0000000..1f70017 --- /dev/null +++ b/modules/svg_in/svg_in.def @@ -0,0 +1,6 @@ +LIBRARY gm_svg_in.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/timedtext/Makefile b/modules/timedtext/Makefile new file mode 100644 index 0000000..76e9656 --- /dev/null +++ b/modules/timedtext/Makefile @@ -0,0 +1,62 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/timedtext + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS=timedtext_dec.o timedtext_in.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_timedtext.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols timedtext.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) -L../../bin/gcc -lgpac + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/timedtext/timedtext.def b/modules/timedtext/timedtext.def new file mode 100644 index 0000000..2ed55d2 --- /dev/null +++ b/modules/timedtext/timedtext.def @@ -0,0 +1,6 @@ +LIBRARY gm_timedtext.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/timedtext/timedtext_dec.c b/modules/timedtext/timedtext_dec.c new file mode 100644 index 0000000..f236a69 --- /dev/null +++ b/modules/timedtext/timedtext_dec.c @@ -0,0 +1,1174 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / 3GPP/MPEG4 timed text module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/internal/terminal_dev.h> +#include <gpac/internal/isomedia_dev.h> +#include <gpac/utf.h> +#include <gpac/constants.h> +#include <gpac/nodes_mpeg4.h> + + +/* + this decoder is simply a scene decoder generating its own scene graph based on input data, + this scene graph is then used as an extra graph by the renderer, and manipulated by the decoder + for any time animation handling. + Translation from text to MPEG-4 scene graph: + * all modifiers (styles, hilight, etc) are unrolled into chunks forming a unique, linear + sequence of text data (startChar, endChar) with associated styles & modifs + * chunks are mapped to classic MPEG-4/VRML text + * these chunks are then gathered in a Form node (supported by 2D and 3D renderers), with + text truncation at each newline char. + * the Form then performs all alignment of the chunks + + It could be possible to use Layout instead of form, BUT layout cannot handle new lines at the time being... + + Currently supported for 3GP text streams: + * text box positioning & filling, dynamic text box + * text color + * proper alignment (H and V) with horizontal text. Vertical text may not be properly layed out (not fully tested) + * style Records (font, size, fontstyles, and colors change) - any nb per sample supported + * hilighting (static only) with color or reverse video - any nb per sample supported + * hypertext links - any nb per sample supported + * blinking - any nb per sample supported + * complete scrolling: in, out, in+out, up, down, right and left directions. All other + modifiers are supported when scrolling + * scroll delay + + It does NOT support: + * dynamic hilighting (karaoke) + * wrap + + The decoder only accepts complete timed text units TTU(1). In band reconfig (TTU(5) is not supported, + nor fragmented TTUs (2, 3, 4). + UTF16 support should workbut MP4Box does not support it at encoding time. +*/ + +typedef struct +{ + GF_InlineScene *inlineScene; + GF_Terminal *app; + u32 PL, nb_streams; + + GF_TextConfig *cfg; + + GF_SceneGraph *sg; + + /*avoid searching the graph for things we know...*/ + M_Transform2D *tr_track, *tr_box, *tr_scroll; + M_Material2D *mat_track, *mat_box; + M_Layer2D *dlist; + M_Rectangle *rec_box, *rec_track; + + M_TimeSensor *ts_blink, *ts_scroll; + M_ScalarInterpolator *process_blink, *process_scroll; + GF_Route *time_route; + GF_List *blink_nodes; + u32 scroll_type, scroll_mode; + Fixed scroll_time, scroll_delay; + Bool is_active, use_texture; +} TTDPriv; + + +static void ttd_set_blink_fraction(GF_Node *node); +static void ttd_set_scroll_fraction(GF_Node *node); +static void TTD_ResetDisplay(TTDPriv *priv); + +/*the WORST thing about 3GP in MPEG4 is positioning of the text track...*/ +static void TTD_UpdateSizeInfo(TTDPriv *priv) +{ + u32 w, h; + Bool has_size; + s32 offset, thw, thh, vw, vh; + + has_size = gf_sg_get_scene_size_info(priv->inlineScene->graph, &w, &h); + /*no size info is given in main scene, override by associated video size if any, or by text track size*/ + if (!has_size) { + if (priv->cfg->has_vid_info && priv->cfg->video_width && priv->cfg->video_height) { + gf_sg_set_scene_size_info(priv->sg, priv->cfg->video_width, priv->cfg->video_height, 1); + } else { + gf_sg_set_scene_size_info(priv->sg, priv->cfg->text_width, priv->cfg->text_height, 1); + } + gf_sg_get_scene_size_info(priv->sg, &w, &h); + if (!w || !h) return; + gf_inline_force_scene_size(priv->inlineScene, w, h); + } + + if (!w || !h) return; + /*apply*/ + gf_sg_set_scene_size_info(priv->sg, w, h, 1); + /*make sure the scene size is big enough to contain the text track after positioning. RESULTS ARE UNDEFINED + if offsets are negative: since MPEG-4 uses centered coord system, we must assume video is aligned to top-left*/ + if (priv->cfg->has_vid_info) { + Bool set_size = 0; + vw = priv->cfg->horiz_offset; if (vw<0) vw = 0; + vh = priv->cfg->vert_offset; if (vh<0) vh = 0; + if (priv->cfg->text_width + (u32) vw > w) { + w = priv->cfg->text_width+vw; + set_size = 1; + } + if (priv->cfg->text_height + (u32) vh > h) { + h = priv->cfg->text_height+vh; + set_size = 1; + } + if (set_size) { + gf_sg_set_scene_size_info(priv->sg, w, h, 1); + gf_inline_force_scene_size(priv->inlineScene, w, h); + } + } else { + /*otherwise override (mainly used for SRT & TTXT file direct loading*/ + priv->cfg->text_width = w; + priv->cfg->text_height = h; + } + + /*ok override video size with main scene size*/ + priv->cfg->video_width = w; + priv->cfg->video_height = h; + + vw = (s32) w; + vh = (s32) h; + thw = priv->cfg->text_width / 2; + thh = priv->cfg->text_height / 2; + /*check translation, we must not get out of scene size - not supported in GPAC*/ + offset = priv->cfg->horiz_offset - vw/2 + thw; + /*safety checks ? + if (offset + thw < - vw/2) offset = - vw/2 + thw; + else if (offset - thw > vw/2) offset = vw/2 - thw; + */ + priv->tr_track->translation.x = INT2FIX(offset); + + offset = vh/2 - priv->cfg->vert_offset - thh; + /*safety checks ? + if (offset + thh > vh/2) offset = vh/2 - thh; + else if (offset - thh < -vh/2) offset = -vh/2 + thh; + */ + priv->tr_track->translation.y = INT2FIX(offset); + + gf_node_changed((GF_Node *)priv->tr_track, NULL); +} + +static GF_Err TTD_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *capability) +{ + TTDPriv *priv = (TTDPriv *)plug->privateStack; + switch (capability->CapCode) { + case GF_CODEC_WIDTH: + capability->cap.valueInt = priv->cfg->text_width; + return GF_OK; + case GF_CODEC_HEIGHT: + capability->cap.valueInt = priv->cfg->text_height; + return GF_OK; + case GF_CODEC_MEDIA_NOT_OVER: + capability->cap.valueInt = priv->is_active; + return GF_OK; + default: + capability->cap.valueInt = 0; + return GF_OK; + } +} + +static GF_Err TTD_SetCapabilities(GF_BaseDecoder *plug, const GF_CodecCapability capability) +{ + TTDPriv *priv = (TTDPriv *)plug->privateStack; + if (capability.CapCode==GF_CODEC_SHOW_SCENE) { + if (capability.cap.valueInt) { + TTD_ResetDisplay(priv); + TTD_UpdateSizeInfo(priv); + gf_inline_register_extra_graph(priv->inlineScene, priv->sg, 0); + } else { + gf_inline_register_extra_graph(priv->inlineScene, priv->sg, 1); + } + } + return GF_OK; +} + +GF_Err TTD_AttachScene(GF_SceneDecoder *plug, GF_InlineScene *scene, Bool is_scene_decoder) +{ + TTDPriv *priv = (TTDPriv *)plug->privateStack; + if (priv->nb_streams) return GF_BAD_PARAM; + /*timedtext cannot be a root scene object*/ + if (is_scene_decoder) return GF_BAD_PARAM; + priv->inlineScene = scene; + priv->app = scene->root_od->term; + return GF_OK; +} + +GF_Err TTD_ReleaseScene(GF_SceneDecoder *plug) +{ + TTDPriv *priv = (TTDPriv *)plug->privateStack; + if (priv->nb_streams) return GF_BAD_PARAM; + return GF_OK; +} + +static GFINLINE void add_child(GF_Node *n1, GF_Node *par) +{ + gf_node_list_add_child( & ((GF_ParentNode *)par)->children, n1); + gf_node_register(n1, par); +} + + +static GFINLINE GF_Node *ttd_create_node(TTDPriv *ttd, u32 tag, const char *def_name) +{ + GF_Node *n = gf_node_new(ttd->sg, tag); + if (n) { + if (def_name) gf_node_set_id(n, gf_sg_get_next_available_node_id(ttd->sg), def_name); + gf_node_init(n); + } + return n; +} + +static GF_Err TTD_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) +{ + TTDPriv *priv = (TTDPriv *)plug->privateStack; + GF_Err e; + GF_Node *root, *n1, *n2; + const char *opt; + /*no scalable, no upstream*/ + if (priv->nb_streams || esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; + if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->data) return GF_NON_COMPLIANT_BITSTREAM; + + priv->cfg = (GF_TextConfig *) gf_odf_desc_new(GF_ODF_TEXT_CFG_TAG); + e = gf_odf_get_text_config(esd->decoderConfig->decoderSpecificInfo, (u8) esd->decoderConfig->objectTypeIndication, priv->cfg); + if (e) { + gf_odf_desc_del((GF_Descriptor *) priv->cfg); + priv->cfg = NULL; + return e; + } + priv->nb_streams++; + if (!priv->cfg->timescale) priv->cfg->timescale = 1000; + + priv->sg = gf_sg_new_subscene(priv->inlineScene->graph); + + root = ttd_create_node(priv, TAG_MPEG4_OrderedGroup, NULL); + gf_sg_set_root_node(priv->sg, root); + gf_node_register(root, NULL); + /*root transform*/ + priv->tr_track = (M_Transform2D *)ttd_create_node(priv, TAG_MPEG4_Transform2D, NULL); + add_child((GF_Node *) priv->tr_track, root); + + TTD_UpdateSizeInfo(priv); + + /*txt track background*/ + n1 = ttd_create_node(priv, TAG_MPEG4_Shape, NULL); + add_child(n1, (GF_Node *) priv->tr_track); + ((M_Shape *)n1)->appearance = ttd_create_node(priv, TAG_MPEG4_Appearance, NULL); + gf_node_register(((M_Shape *)n1)->appearance, n1); + priv->mat_track = (M_Material2D *) ttd_create_node(priv, TAG_MPEG4_Material2D, NULL); + priv->mat_track->filled = 1; + priv->mat_track->transparency = 1; + ((M_Appearance *) ((M_Shape *)n1)->appearance)->material = (GF_Node *) priv->mat_track; + gf_node_register((GF_Node *) priv->mat_track, ((M_Shape *)n1)->appearance); + n2 = ttd_create_node(priv, TAG_MPEG4_Rectangle, NULL); + ((M_Rectangle *)n2)->size.x = 0; + ((M_Rectangle *)n2)->size.y = 0; + ((M_Shape *)n1)->geometry = n2; + gf_node_register(n2, n1); + priv->rec_track = (M_Rectangle *)n2; + + /*txt box background*/ + priv->tr_box = (M_Transform2D *) ttd_create_node(priv, TAG_MPEG4_Transform2D, NULL); + add_child((GF_Node*) priv->tr_box, (GF_Node*)priv->tr_track); + n1 = ttd_create_node(priv, TAG_MPEG4_Shape, NULL); + add_child(n1, (GF_Node*)priv->tr_box); + ((M_Shape *)n1)->appearance = ttd_create_node(priv, TAG_MPEG4_Appearance, NULL); + gf_node_register(((M_Shape *)n1)->appearance, n1); + priv->mat_box = (M_Material2D *) ttd_create_node(priv, TAG_MPEG4_Material2D, NULL); + priv->mat_box->filled = 1; + priv->mat_box->transparency = 1; + ((M_Appearance *) ((M_Shape *)n1)->appearance)->material = (GF_Node *)priv->mat_box; + gf_node_register((GF_Node *)priv->mat_box, ((M_Shape *)n1)->appearance); + priv->rec_box = (M_Rectangle *) ttd_create_node(priv, TAG_MPEG4_Rectangle, NULL); + priv->rec_box->size.x = 0; + priv->rec_box->size.y = 0; + ((M_Shape *)n1)->geometry = (GF_Node *) priv->rec_box; + gf_node_register((GF_Node *) priv->rec_box, n1); + + priv->dlist = (M_Layer2D *) ttd_create_node(priv, TAG_MPEG4_Layer2D, NULL); + priv->dlist->size.x = priv->cfg->text_width; + priv->dlist->size.y = priv->cfg->text_height; + add_child((GF_Node *)priv->dlist, (GF_Node *)priv->tr_box); + + priv->blink_nodes = gf_list_new(); + priv->ts_blink = (M_TimeSensor *) ttd_create_node(priv, TAG_MPEG4_TimeSensor, "TimerBlink"); + priv->ts_blink->cycleInterval = 0.25; + priv->ts_blink->startTime = 0.0; + priv->ts_blink->loop = 1; + priv->process_blink = (M_ScalarInterpolator *) ttd_create_node(priv, TAG_MPEG4_ScalarInterpolator, NULL); + /*override set_fraction*/ + priv->process_blink->on_set_fraction = ttd_set_blink_fraction; + gf_node_set_private((GF_Node *) priv->process_blink, priv); + /*route from fraction_changed to set_fraction*/ + gf_sg_route_new(priv->sg, (GF_Node *) priv->ts_blink, 6, (GF_Node *) priv->process_blink, 0); + + priv->ts_scroll = (M_TimeSensor *) ttd_create_node(priv, TAG_MPEG4_TimeSensor, "TimerScroll"); + priv->ts_scroll->cycleInterval = 0; + priv->ts_scroll->startTime = -1; + priv->ts_scroll->loop = 0; + priv->process_scroll = (M_ScalarInterpolator *) ttd_create_node(priv, TAG_MPEG4_ScalarInterpolator, NULL); + /*override set_fraction*/ + priv->process_scroll->on_set_fraction = ttd_set_scroll_fraction; + gf_node_set_private((GF_Node *) priv->process_scroll, priv); + /*route from fraction_changed to set_fraction*/ + gf_sg_route_new(priv->sg, (GF_Node *) priv->ts_scroll, 6, (GF_Node *) priv->process_scroll, 0); + + gf_node_register((GF_Node *) priv->ts_blink, NULL); + gf_node_register((GF_Node *) priv->process_blink, NULL); + gf_node_register((GF_Node *) priv->ts_scroll, NULL); + gf_node_register((GF_Node *) priv->process_scroll, NULL); + + /*option setup*/ + opt = gf_modules_get_option((GF_BaseInterface *)plug, "StreamingText", "UseTexturing"); + priv->use_texture = (opt && !strcmp(opt, "yes")) ? 1 : 0; + return e; +} + +static GF_Err TTD_DetachStream(GF_BaseDecoder *plug, u16 ES_ID) +{ + TTDPriv *priv = (TTDPriv *)plug->privateStack; + if (!priv->nb_streams) return GF_BAD_PARAM; + + gf_inline_register_extra_graph(priv->inlineScene, priv->sg, 1); + + gf_node_unregister((GF_Node *) priv->ts_blink, NULL); + gf_node_unregister((GF_Node *) priv->process_blink, NULL); + gf_node_unregister((GF_Node *) priv->ts_scroll, NULL); + gf_node_unregister((GF_Node *) priv->process_scroll, NULL); + + gf_sg_del(priv->sg); + priv->sg = NULL; + if (priv->cfg) gf_odf_desc_del((GF_Descriptor *) priv->cfg); + priv->cfg = NULL; + priv->nb_streams = 0; + gf_list_del(priv->blink_nodes); + return GF_OK; +} + +static void ttd_set_blink_fraction(GF_Node *node) +{ + M_Material2D *m; + u32 i; + TTDPriv *priv = (TTDPriv *)gf_node_get_private(node); + + Bool blink_on = 1; + if (priv->process_blink->set_fraction>FIX_ONE/2) blink_on = 0; + i=0; + while ((m = (M_Material2D*)gf_list_enum(priv->blink_nodes, &i))) { + if (m->filled != blink_on) { + m->filled = blink_on; + gf_node_changed((GF_Node *) m, NULL); + } + } +} + +static void ttd_set_scroll_fraction(GF_Node *node) +{ + Fixed frac; + TTDPriv *priv = (TTDPriv *)gf_node_get_private(node); + frac = priv->process_scroll->set_fraction; + if (frac==FIX_ONE) priv->is_active = 0; + if (!priv->tr_scroll) return; + + switch (priv->scroll_type - 1) { + case GF_TXT_SCROLL_CREDITS: + case GF_TXT_SCROLL_DOWN: + priv->tr_scroll->translation.x = 0; + if (priv->scroll_mode & GF_TXT_SCROLL_IN) { + if (frac>priv->scroll_time) { + priv->scroll_mode &= ~GF_TXT_SCROLL_IN; + priv->tr_scroll->translation.y = 0; + } else { + priv->tr_scroll->translation.y = gf_muldiv(priv->dlist->size.y, frac, priv->scroll_time) - priv->dlist->size.y; + } + } else if (priv->scroll_mode & GF_TXT_SCROLL_OUT) { + if (frac < FIX_ONE - priv->scroll_time) return; + frac -= FIX_ONE - priv->scroll_time; + if (priv->scroll_type - 1 == GF_TXT_SCROLL_DOWN) { + priv->tr_scroll->translation.y = gf_muldiv(priv->dlist->size.y, frac, priv->scroll_time); + } else { + priv->tr_scroll->translation.y = gf_muldiv(priv->dlist->size.y, frac, priv->scroll_time); + } + } + if (priv->scroll_type - 1 == GF_TXT_SCROLL_DOWN) priv->tr_scroll->translation.y *= -1; + break; + case GF_TXT_SCROLL_MARQUEE: + case GF_TXT_SCROLL_RIGHT: + priv->tr_scroll->translation.y = 0; + if (priv->scroll_mode & GF_TXT_SCROLL_IN) { + if (! (priv->scroll_mode & GF_TXT_SCROLL_OUT)) { + if (frac<priv->scroll_delay) return; + frac-=priv->scroll_delay; + } + if (frac>priv->scroll_time) { + priv->scroll_mode &= ~GF_TXT_SCROLL_IN; + priv->tr_scroll->translation.x = 0; + } else { + priv->tr_scroll->translation.x = gf_muldiv(priv->dlist->size.x, frac, priv->scroll_time) - priv->dlist->size.x; + } + } else if (priv->scroll_mode & GF_TXT_SCROLL_OUT) { + if (frac < FIX_ONE - priv->scroll_time) return; + frac -= FIX_ONE - priv->scroll_time; + priv->tr_scroll->translation.x = gf_muldiv(priv->dlist->size.x, frac, priv->scroll_time); + } + if (priv->scroll_type - 1 == GF_TXT_SCROLL_MARQUEE) priv->tr_scroll->translation.x *= -1; + break; + default: + break; + } + gf_node_changed((GF_Node *)priv->tr_scroll, NULL); +} + +static void TTD_ResetDisplay(TTDPriv *priv) +{ + gf_list_reset(priv->blink_nodes); + gf_node_unregister_children((GF_Node*)priv->dlist, priv->dlist->children); + priv->dlist->children = NULL; + gf_node_changed((GF_Node *) priv->dlist, NULL); + priv->tr_scroll = NULL; +} + +char *TTD_FindFont(GF_TextSampleDescriptor *tsd, u32 ID) +{ + u32 i; + for (i=0; i<tsd->font_count; i++) { + if (tsd->fonts[i].fontID==ID) return tsd->fonts[i].fontName; + } + return "SERIF"; +} + +static void ttd_add_item(M_Form *form) +{ + s32 *new_gr; + gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &new_gr); + (*new_gr) = gf_node_list_get_count(form->children); + gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &new_gr); + (*new_gr) = -1; + /*store line info*/ + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &new_gr); + (*new_gr) = gf_node_list_get_count(form->children); +} + +static void ttd_add_line(M_Form *form) +{ + s32 *new_gr; + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &new_gr); + (*new_gr) = -1; +} + +typedef struct +{ + u32 start_char, end_char; + GF_StyleRecord *srec; + Bool is_hilight; + u32 hilight_col; /*0 means RV*/ + GF_TextHyperTextBox *hlink; + Bool has_blink; + /*karaoke not done yet*/ + /*text wrapping is not supported - we will need to move to Layout (rather than form), and modify + layout to handle new lines and proper scrolling*/ +} TTDTextChunk; + +static void TTD_NewTextChunk(TTDPriv *priv, GF_TextSampleDescriptor *tsd, M_Form *form, u16 *utf16_txt, TTDTextChunk *tc) +{ + GF_Node *txt_model, *n2, *txt_material; + M_Text *text; + M_FontStyle *fs; + char *fontName; + char szStyle[1024]; + u32 fontSize, styleFlags, color, i, start_char; + + if (!tc->srec) { + fontName = TTD_FindFont(tsd, tsd->default_style.fontID); + fontSize = tsd->default_style.font_size; + styleFlags = tsd->default_style.style_flags; + color = tsd->default_style.text_color; + } else { + fontName = TTD_FindFont(tsd, tc->srec->fontID); + fontSize = tc->srec->font_size; + styleFlags = tc->srec->style_flags; + color = tc->srec->text_color; + } + + /*create base model for text node. It will then be cloned for each text item*/ + txt_model = ttd_create_node(priv, TAG_MPEG4_Shape, NULL); + gf_node_register(txt_model, NULL); + n2 = ttd_create_node(priv, TAG_MPEG4_Appearance, NULL); + ((M_Shape *)txt_model)->appearance = n2; + gf_node_register(n2, txt_model); + txt_material = ttd_create_node(priv, TAG_MPEG4_Material2D, NULL); + ((M_Appearance *)n2)->material = txt_material; + gf_node_register(txt_material, n2); + + ((M_Material2D *)txt_material)->filled = 1; + ((M_Material2D *)txt_material)->transparency = FIX_ONE - INT2FIX((color>>24) & 0xFF) / 255; + ((M_Material2D *)txt_material)->emissiveColor.red = INT2FIX((color>>16) & 0xFF) / 255; + ((M_Material2D *)txt_material)->emissiveColor.green = INT2FIX((color>>8) & 0xFF) / 255; + ((M_Material2D *)txt_material)->emissiveColor.blue = INT2FIX((color) & 0xFF) / 255; + /*force 0 lineWidth if blinking (the stupid MPEG-4 default values once again..)*/ + if (tc->has_blink) { + ((M_Material2D *)txt_material)->lineProps = ttd_create_node(priv, TAG_MPEG4_LineProperties, NULL); + ((M_LineProperties *)((M_Material2D *)txt_material)->lineProps)->width = 0; + gf_node_register(((M_Material2D *)txt_material)->lineProps, txt_material); + } + + n2 = ttd_create_node(priv, TAG_MPEG4_Text, NULL); + ((M_Shape *)txt_model)->geometry = n2; + gf_node_register(n2, txt_model); + text = (M_Text *) n2; + fs = (M_FontStyle *) ttd_create_node(priv, TAG_MPEG4_FontStyle, NULL); + free(fs->family.vals[0]); + + /*translate default fonts to MPEG-4/VRML names*/ + if (!stricmp(fontName, "Serif")) fs->family.vals[0] = strdup("SERIF"); + else if (!stricmp(fontName, "Sans-Serif")) fs->family.vals[0] = strdup("SANS"); + else if (!stricmp(fontName, "Monospace")) fs->family.vals[0] = strdup("TYPEWRITER"); + else fs->family.vals[0] = strdup(fontName); + + fs->size = INT2FIX(fontSize); + free(fs->style.buffer); + strcpy(szStyle, ""); + if (styleFlags & GF_TXT_STYLE_BOLD) { + if (styleFlags & GF_TXT_STYLE_ITALIC) strcpy(szStyle, "BOLDITALIC"); + else strcpy(szStyle, "BOLD"); + } else if (styleFlags & GF_TXT_STYLE_ITALIC) strcat(szStyle, "ITALIC"); + if (!strlen(szStyle)) strcpy(szStyle, "PLAIN"); + /*also underline for URLs*/ + if ((styleFlags & GF_TXT_STYLE_UNDERLINED) || (tc->hlink && tc->hlink->URL)) strcat(szStyle, " UNDERLINED"); + + if (tc->is_hilight) { + if (tc->hilight_col) { + char szTxt[50]; + sprintf(szTxt, " HIGHLIGHT#%x", tc->hilight_col); + strcat(szStyle, szTxt); + } else { + strcat(szStyle, " HIGHLIGHT#RV"); + } + } + /*a better way would be to draw the entire text box in a composite texture & bitmap but we can't really rely + on text box size (in MP4Box, it actually defaults to the entire video area) and drawing a too large texture + & bitmap could slow down rendering*/ + if (priv->use_texture) strcat(szStyle, " TEXTURED"); + + fs->style.buffer = strdup(szStyle); + fs->horizontal = (tsd->displayFlags & GF_TXT_VERTICAL) ? 0 : 1; + text->fontStyle = (GF_Node *) fs; + gf_node_register((GF_Node *)fs, (GF_Node *)text); + gf_sg_vrml_mf_reset(&text->string, GF_SG_VRML_MFSTRING); + + if (tc->hlink && tc->hlink->URL) { + SFURL *s; + M_Anchor *anc = (M_Anchor *) ttd_create_node(priv, TAG_MPEG4_Anchor, NULL); + gf_sg_vrml_mf_append(&anc->url, GF_SG_VRML_MFURL, (void **) &s); + s->OD_ID = 0; + s->url = strdup(tc->hlink->URL); + if (tc->hlink->URL_hint) anc->description.buffer = strdup(tc->hlink->URL_hint); + gf_node_list_add_child(& anc->children, txt_model); + gf_node_register(txt_model, (GF_Node *)anc); + txt_model = (GF_Node *)anc; + gf_node_register((GF_Node *)anc, NULL); + } + + start_char = tc->start_char; + for (i=tc->start_char; i<tc->end_char; i++) { + Bool new_line = 0; + if ((utf16_txt[i] == '\n') || (utf16_txt[i] == '\r') || (utf16_txt[i] == 0x85) || (utf16_txt[i] == 0x2028) || (utf16_txt[i] == 0x2029)) new_line = 1; + + if (new_line || (i+1==tc->end_char) ) { + SFString *st; + + if (i+1==tc->end_char) i++; + + if (i!=start_char) { + char szLine[5000]; + u32 len; + s16 wsChunk[5000], *sp; + + /*spliting lines, duplicate node*/ + + n2 = gf_node_clone(priv->sg, txt_model, NULL, "", 1); + if (tc->hlink && tc->hlink->URL) { + GF_Node *t = ((M_Anchor *)n2)->children->node; + text = (M_Text *) ((M_Shape *)t)->geometry; + txt_material = ((M_Appearance *) ((M_Shape *)t)->appearance)->material; + } else { + text = (M_Text *) ((M_Shape *)n2)->geometry; + txt_material = ((M_Appearance *) ((M_Shape *)n2)->appearance)->material; + } + gf_sg_vrml_mf_reset(&text->string, GF_SG_VRML_MFSTRING); + gf_node_list_add_child( &form->children, n2); + gf_node_register(n2, (GF_Node *) form); + ttd_add_item(form); + /*clone node always register by default*/ + gf_node_unregister(n2, NULL); + + if (tc->has_blink && txt_material) gf_list_add(priv->blink_nodes, txt_material); + + + memcpy(wsChunk, &utf16_txt[start_char], sizeof(s16)*(i-start_char)); + wsChunk[i-start_char] = 0; + sp = &wsChunk[0]; + len = gf_utf8_wcstombs(szLine, 5000, (const unsigned short **) &sp); + szLine[len] = 0; + + gf_sg_vrml_mf_append(&text->string, GF_SG_VRML_MFSTRING, (void **) &st); + st->buffer = strdup(szLine); + } + start_char = i+1; + if (new_line) { + ttd_add_line(form); + if ((utf16_txt[i]=='\r') && (utf16_txt[i+1]=='\n')) i++; + } + } + } + gf_node_unregister(txt_model, NULL); + return; +} + + +/*mod can be any of TextHighlight, TextKaraoke, TextHyperText, TextBlink*/ +void TTD_SplitChunks(GF_TextSample *txt, u32 nb_chars, GF_List *chunks, GF_Box *mod) +{ + TTDTextChunk *tc; + u32 start_char, end_char; + u32 i; + switch (mod->type) { + /*these 3 can be safelly typecasted to the same struct for start/end char*/ + case GF_ISOM_BOX_TYPE_HLIT: + case GF_ISOM_BOX_TYPE_HREF: + case GF_ISOM_BOX_TYPE_BLNK: + start_char = ((GF_TextHighlightBox *)mod)->startcharoffset; + end_char = ((GF_TextHighlightBox *)mod)->endcharoffset; + break; + case GF_ISOM_BOX_TYPE_KROK: + default: + return; + } + + if (end_char>nb_chars) end_char = nb_chars; + + i=0; + while ((tc = (TTDTextChunk *)gf_list_enum(chunks, &i))) { + if (tc->end_char<=start_char) continue; + /*need to split chunk at begin*/ + if (tc->start_char<start_char) { + TTDTextChunk *tc2; + tc2 = (TTDTextChunk *) malloc(sizeof(TTDTextChunk)); + memcpy(tc2, tc, sizeof(TTDTextChunk)); + tc2->start_char = start_char; + tc2->end_char = tc->end_char; + tc->end_char = start_char; + gf_list_insert(chunks, tc2, i+1); + i++; + tc = tc2; + } + /*need to split chunks at end*/ + if (tc->end_char>end_char) { + TTDTextChunk *tc2; + tc2 = (TTDTextChunk *) malloc(sizeof(TTDTextChunk)); + memcpy(tc2, tc, sizeof(TTDTextChunk)); + tc2->start_char = tc->start_char; + tc2->end_char = end_char; + tc->start_char = end_char; + gf_list_insert(chunks, tc2, i); + i++; + tc = tc2; + } + /*assign mod*/ + switch (mod->type) { + case GF_ISOM_BOX_TYPE_HLIT: + tc->is_hilight = 1; + if (txt->highlight_color) tc->hilight_col = txt->highlight_color->hil_color; + break; + case GF_ISOM_BOX_TYPE_HREF: + tc->hlink = (GF_TextHyperTextBox *) mod; + break; + case GF_ISOM_BOX_TYPE_BLNK: + tc->has_blink = 1; + break; + } + /*done*/ + if (tc->end_char==end_char) return; + } +} + + +static void TTD_ApplySample(TTDPriv *priv, GF_TextSample *txt, u32 sdi, Bool is_utf_16, u32 sample_duration) +{ + u32 i, nb_lines, start_idx, count; + s32 *id, thw, thh, tw, th, offset; + Bool vertical; + MFInt32 idx; + SFString *s; + GF_BoxRecord br; + M_Material2D *n; + M_Form *form; + u16 utf16_text[5000]; + u32 char_offset, char_count; + GF_List *chunks; + TTDTextChunk *tc; + GF_Box *a; + GF_TextSampleDescriptor *td = NULL; + + /*stop timer sensor*/ + if (gf_list_count(priv->blink_nodes)) { + priv->ts_blink->stopTime = gf_node_get_scene_time((GF_Node *) priv->ts_blink); + gf_node_changed((GF_Node *) priv->ts_blink, NULL); + } + priv->ts_scroll->stopTime = gf_node_get_scene_time((GF_Node *) priv->ts_scroll); + gf_node_changed((GF_Node *) priv->ts_scroll, NULL); + /*flush routes to avoid getting the set_fraction of the scroll sensor deactivation*/ + gf_sg_activate_routes(priv->inlineScene->graph); + + TTD_ResetDisplay(priv); + if (!sdi || !txt || !txt->len) return; + + i=0; + while ((td = (GF_TextSampleDescriptor *)gf_list_enum(priv->cfg->sample_descriptions, &i))) { + if (td->sample_index==sdi) break; + td = NULL; + } + if (!td) return; + + + vertical = (td->displayFlags & GF_TXT_VERTICAL) ? 1 : 0; + + /*set back color*/ + /*do we fill the text box or the entire text track region*/ + if (td->displayFlags & GF_TXT_FILL_REGION) { + priv->mat_box->transparency = FIX_ONE; + n = priv->mat_track; + } else { + priv->mat_track->transparency = FIX_ONE; + n = priv->mat_box; + } + + n->transparency = FIX_ONE - INT2FIX((td->back_color>>24) & 0xFF) / 255; + n->emissiveColor.red = INT2FIX((td->back_color>>16) & 0xFF) / 255; + n->emissiveColor.green = INT2FIX((td->back_color>>8) & 0xFF) / 255; + n->emissiveColor.blue = INT2FIX((td->back_color) & 0xFF) / 255; + gf_node_changed((GF_Node *) n, NULL); + + if (txt->box) { + br = txt->box->box; + } else { + br = td->default_pos; + } + if (!br.right || !br.bottom) { + br.top = br.left = 0; + br.right = priv->cfg->text_width; + br.bottom = priv->cfg->text_height; + } + thw = br.right - br.left; + thh = br.bottom - br.top; + if (!thw || !thh) { + br.top = br.left = 0; + thw = priv->cfg->text_width; + thh = priv->cfg->text_height; + } + + priv->dlist->size.x = INT2FIX(thw); + priv->dlist->size.y = INT2FIX(thh); + + /*disable backgrounds if not used*/ + if (priv->mat_track->transparency<FIX_ONE) { + if (priv->rec_track->size.x != priv->cfg->text_width) { + priv->rec_track->size.x = priv->cfg->text_width; + priv->rec_track->size.y = priv->cfg->text_height; + gf_node_changed((GF_Node *) priv->rec_track, NULL); + } + } else if (priv->rec_track->size.x) { + priv->rec_track->size.x = priv->rec_track->size.y = 0; + gf_node_changed((GF_Node *) priv->rec_box, NULL); + } + + if (priv->mat_box->transparency<FIX_ONE) { + if (priv->rec_box->size.x != priv->dlist->size.x) { + priv->rec_box->size.x = priv->dlist->size.x; + priv->rec_box->size.y = priv->dlist->size.y; + gf_node_changed((GF_Node *) priv->rec_box, NULL); + } + } else if (priv->rec_box->size.x) { + priv->rec_box->size.x = priv->rec_box->size.y = 0; + gf_node_changed((GF_Node *) priv->rec_box, NULL); + } + + form = (M_Form *) ttd_create_node(priv, TAG_MPEG4_Form, NULL); + form->size.x = INT2FIX(thw); + form->size.y = INT2FIX(thh); + + thw /= 2; + thh /= 2; + tw = priv->cfg->text_width; + th = priv->cfg->text_height; + + /*check translation, we must not get out of scene size - not supported in GPAC*/ + offset = br.left - tw/2 + thw; + if (offset + thw < - tw/2) offset = - tw/2 + thw; + else if (offset - thw > tw/2) offset = tw/2 - thw; + priv->tr_box->translation.x = INT2FIX(offset); + + offset = th/2 - br.top - thh; + if (offset + thh > th/2) offset = th/2 - thh; + else if (offset - thh < -th/2) offset = -th/2 + thh; + priv->tr_box->translation.y = INT2FIX(offset); + + gf_node_dirty_set((GF_Node *)priv->tr_box, 0, 1); + + + if (priv->scroll_type) { + priv->ts_scroll->stopTime = gf_node_get_scene_time((GF_Node *) priv->ts_scroll); + gf_node_changed((GF_Node *) priv->ts_scroll, NULL); + } + priv->scroll_mode = 0; + if (td->displayFlags & GF_TXT_SCROLL_IN) priv->scroll_mode |= GF_TXT_SCROLL_IN; + if (td->displayFlags & GF_TXT_SCROLL_OUT) priv->scroll_mode |= GF_TXT_SCROLL_OUT; + + priv->scroll_type = 0; + if (priv->scroll_mode) { + priv->scroll_type = (td->displayFlags & GF_TXT_SCROLL_DIRECTION)>>7; + priv->scroll_type ++; + } + /*no sample duration, cannot determine scroll rate, so just show*/ + if (!sample_duration) priv->scroll_type = 0; + /*no scroll*/ + if (!priv->scroll_mode) priv->scroll_type = 0; + + if (priv->scroll_type) { + priv->tr_scroll = (M_Transform2D *) ttd_create_node(priv, TAG_MPEG4_Transform2D, NULL); + gf_node_list_add_child( &priv->dlist->children, (GF_Node*)priv->tr_scroll); + gf_node_register((GF_Node *) priv->tr_scroll, (GF_Node *) priv->dlist); + gf_node_list_add_child( &priv->tr_scroll->children, (GF_Node*)form); + gf_node_register((GF_Node *) form, (GF_Node *) priv->tr_scroll); + priv->tr_scroll->translation.x = priv->tr_scroll->translation.y = (priv->scroll_mode & GF_TXT_SCROLL_IN) ? -INT2FIX(1000) : 0; + /*if no delay, text is in motion for the duration of the sample*/ + priv->scroll_time = FIX_ONE; + priv->scroll_delay = 0; + + if (txt->scroll_delay) { + priv->scroll_delay = gf_divfix(INT2FIX(txt->scroll_delay->scroll_delay), INT2FIX(sample_duration)); + if (priv->scroll_delay>FIX_ONE) priv->scroll_delay = FIX_ONE; + priv->scroll_time = (FIX_ONE - priv->scroll_delay); + } + /*if both scroll (in and out), use same scroll duration for both*/ + if ((priv->scroll_mode & GF_TXT_SCROLL_IN) && (priv->scroll_mode & GF_TXT_SCROLL_OUT)) priv->scroll_time /= 2; + + } else { + gf_node_list_add_child( &priv->dlist->children, (GF_Node*)form); + gf_node_register((GF_Node *) form, (GF_Node *) priv->dlist); + priv->tr_scroll = NULL; + } + + if (is_utf_16) { + memcpy((char *) utf16_text, txt->text, sizeof(char) * txt->len); + ((char *) utf16_text)[txt->len] = 0; + ((char *) utf16_text)[txt->len+1] = 0; + char_count = txt->len / 2; + } else { + char *p = txt->text; + char_count = gf_utf8_mbstowcs(utf16_text, 2500, (const char **) &p); + } + + chunks = gf_list_new(); + /*flatten all modifiers*/ + if (!txt->styles || !txt->styles->entry_count) { + GF_SAFEALLOC(tc, TTDTextChunk); + tc->end_char = char_count; + gf_list_add(chunks, tc); + } else { + GF_StyleRecord *srec = NULL; + char_offset = 0; + for (i=0; i<txt->styles->entry_count; i++) { + TTDTextChunk *tc; + srec = &txt->styles->styles[i]; + if (srec->startCharOffset==srec->endCharOffset) continue; + /*handle not continuous modifiers*/ + if (char_offset < srec->startCharOffset) { + GF_SAFEALLOC(tc, TTDTextChunk); + tc->start_char = char_offset; + tc->end_char = srec->startCharOffset; + gf_list_add(chunks, tc); + } + GF_SAFEALLOC(tc, TTDTextChunk); + tc->start_char = srec->startCharOffset; + tc->end_char = srec->endCharOffset; + tc->srec = srec; + gf_list_add(chunks, tc); + char_offset = srec->endCharOffset; + } + + if (srec->endCharOffset<char_count) { + GF_SAFEALLOC(tc, TTDTextChunk); + tc->start_char = char_offset; + tc->end_char = char_count; + gf_list_add(chunks, tc); + } + } + /*apply all other modifiers*/ + i=0; + while ((a = (GF_Box*)gf_list_enum(txt->others, &i))) { + TTD_SplitChunks(txt, char_count, chunks, a); + } + + while (gf_list_count(chunks)) { + tc = (TTDTextChunk*)gf_list_get(chunks, 0); + gf_list_rem(chunks, 0); + TTD_NewTextChunk(priv, td, form, utf16_text, tc); + free(tc); + } + gf_list_del(chunks); + + if (form->groupsIndex.vals[form->groupsIndex.count-1] != -1) + ttd_add_line(form); + + /*rewrite form groupIndex - group is fine (eg one child per group)*/ + idx.count = form->groupsIndex.count; + idx.vals = form->groupsIndex.vals; + form->groupsIndex.vals = NULL; + form->groupsIndex.count = 0; + + nb_lines = 0; + start_idx = 0; + for (i=0; i<idx.count; i++) { + if (idx.vals[i] == -1) { + s32 *id; + u32 j; + + /*only one item in line, no need for alignment, but still add a group (we could use the + item as a group but that would complicate the alignment generation)*/ + if (start_idx==i-1) { + gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = idx.vals[start_idx]; + gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1; + } else { + /*spread horizontal 0 pixels (eg align) all items in line*/ + gf_sg_vrml_mf_append(&form->constraints, GF_SG_VRML_MFSTRING, (void **) &s); s->buffer = strdup(vertical ? "SV 0" : "SH 0"); + for (j=start_idx; j<i; j++) { + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = idx.vals[j]; + /*also add a group for the line, for final justif*/ + gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = idx.vals[j]; + } + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1; + /*mark end of group*/ + gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1; + } + start_idx = i+1; + nb_lines ++; + } + } + free(idx.vals); + + /*finally add constraints on lines*/ + start_idx = gf_node_list_get_count(form->children) + 1; + /*horizontal alignment*/ + gf_sg_vrml_mf_append(&form->constraints, GF_SG_VRML_MFSTRING, (void **) &s); + if (vertical) { + switch (td->vert_justif) { + case 1: s->buffer = strdup("AV"); break;/*center*/ + case -1: s->buffer = strdup("AB"); break;/*bottom*/ + default: s->buffer = strdup("AT"); break;/*top*/ + } + } else { + switch (td->horiz_justif) { + case 1: s->buffer = strdup("AH"); break;/*center*/ + case -1: s->buffer = strdup("AR"); break;/*right*/ + default: s->buffer = strdup("AL"); break;/*left*/ + } + } + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = 0; + for (i=0; i<nb_lines; i++) { + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = i+start_idx; + } + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1; + + + /*vertical alignment: first align all items vertically, 0 pixel */ + gf_sg_vrml_mf_append(&form->constraints, GF_SG_VRML_MFSTRING, (void **) &s); s->buffer = strdup(vertical ? "SH 0" : "SV 0"); + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = 0; + for (i=0; i<nb_lines; i++) { + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = i+start_idx; + } + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1; + + /*define a group with every item drawn*/ + count = gf_node_list_get_count(form->children); + for (i=0; i<count; i++) { + gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = i+1; + } + gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1; + + gf_sg_vrml_mf_append(&form->constraints, GF_SG_VRML_MFSTRING, (void **) &s); + if (vertical) { + switch (td->horiz_justif) { + case 1: s->buffer = strdup("AH"); break;/*center*/ + case -1: s->buffer = strdup("AR"); break;/*right*/ + default: s->buffer = strdup("AL"); break;/*left*/ + } + } else { + switch (td->vert_justif) { + case 1: s->buffer = strdup("AV"); break;/*center*/ + case -1: s->buffer = strdup("AB"); break;/*bottom*/ + default: s->buffer = strdup("AT"); break;/*top*/ + } + } + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = 0; + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = start_idx + nb_lines; + gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1; + + + gf_node_dirty_set((GF_Node *)form, 0, 1); + gf_node_changed((GF_Node *)form, NULL); + gf_node_changed((GF_Node *) priv->dlist, NULL); + + if (gf_list_count(priv->blink_nodes)) { + /*restart time sensor*/ + priv->ts_blink->startTime = gf_node_get_scene_time((GF_Node *) priv->ts_blink); + gf_node_changed((GF_Node *) priv->ts_blink, NULL); + } + + priv->is_active = 1; + /*scroll timer also acts as AU timer*/ + priv->ts_scroll->startTime = gf_node_get_scene_time((GF_Node *) priv->ts_scroll); + priv->ts_scroll->stopTime = priv->ts_scroll->startTime - 1.0; + priv->ts_scroll->cycleInterval = sample_duration; + priv->ts_scroll->cycleInterval /= priv->cfg->timescale; + priv->ts_scroll->cycleInterval -= 0.1; + gf_node_changed((GF_Node *) priv->ts_scroll, NULL); +} + +static GF_Err TTD_ProcessData(GF_SceneDecoder*plug, char *inBuffer, u32 inBufferLength, + u16 ES_ID, u32 AU_time, u32 mmlevel) +{ + GF_BitStream *bs; + GF_Err e = GF_OK; + TTDPriv *priv = (TTDPriv *)plug->privateStack; + + bs = gf_bs_new(inBuffer, inBufferLength, GF_BITSTREAM_READ); + while (gf_bs_available(bs)) { + GF_TextSample *txt; + Bool is_utf_16; + u32 type, length, sample_index, sample_duration; + is_utf_16 = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 4); + type = gf_bs_read_int(bs, 3); + length = gf_bs_read_u16(bs); + + /*currently only full text samples are supported*/ + if (type != 1) { + gf_bs_del(bs); + return GF_NOT_SUPPORTED; + } + sample_index = gf_bs_read_u8(bs); + /*duration*/ + sample_duration = gf_bs_read_u24(bs); + length -= 8; + /*txt length is parsed with the sample*/ + txt = gf_isom_parse_texte_sample(bs); + TTD_ApplySample(priv, txt, sample_index, is_utf_16, sample_duration); + gf_isom_delete_text_sample(txt); + /*since we support only TTU(1), no need to go on*/ + break; + } + gf_bs_del(bs); + return e; +} + +Bool TTD_CanHandleStream(GF_BaseDecoder *ifce, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + TTDPriv *priv = (TTDPriv *)ifce->privateStack; + if (StreamType!=GF_STREAM_TEXT) return 0; + if (ObjectType!=0x08) return 0; + priv->PL = PL; + return 1; +} + + +void DeleteTimedTextDec(GF_BaseDecoder *plug) +{ + TTDPriv *priv = (TTDPriv *)plug->privateStack; + /*in case something went wrong*/ + if (priv->cfg) gf_odf_desc_del((GF_Descriptor *) priv->cfg); + free(priv); + free(plug); +} + +GF_BaseDecoder *NewTimedTextDec() +{ + TTDPriv *priv; + GF_SceneDecoder *tmp; + + GF_SAFEALLOC(tmp, GF_SceneDecoder); + if (!tmp) return NULL; + GF_SAFEALLOC(priv, TTDPriv); + + tmp->privateStack = priv; + tmp->AttachStream = TTD_AttachStream; + tmp->DetachStream = TTD_DetachStream; + tmp->GetCapabilities = TTD_GetCapabilities; + tmp->SetCapabilities = TTD_SetCapabilities; + tmp->ProcessData = TTD_ProcessData; + tmp->AttachScene = TTD_AttachScene; + tmp->CanHandleStream = TTD_CanHandleStream; + tmp->ReleaseScene = TTD_ReleaseScene; + GF_REGISTER_MODULE_INTERFACE(tmp, GF_SCENE_DECODER_INTERFACE, "GPAC TimedText Decoder", "gpac distribution") + return (GF_BaseDecoder *) tmp; +} + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: return 1; + case GF_NET_CLIENT_INTERFACE: return 1; + default: return 0; + } +} + + +#ifndef GPAC_READ_ONLY +void DeleteTTReader(void *ifce); +void *NewTTReader(); +#endif + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + switch (InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: return (GF_BaseInterface *)NewTimedTextDec(); +#ifndef GPAC_READ_ONLY + case GF_NET_CLIENT_INTERFACE: return (GF_BaseInterface *)NewTTReader(); +#endif + default: return NULL; + } +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_SCENE_DECODER_INTERFACE: + DeleteTimedTextDec((GF_BaseDecoder *)ifce); + break; +#ifndef GPAC_READ_ONLY + case GF_NET_CLIENT_INTERFACE: + DeleteTTReader(ifce); + break; +#endif + } +} diff --git a/modules/timedtext/timedtext_in.c b/modules/timedtext/timedtext_in.c new file mode 100644 index 0000000..d4429ed --- /dev/null +++ b/modules/timedtext/timedtext_in.c @@ -0,0 +1,382 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / 3GPP/MPEG4 timed text module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <gpac/modules/service.h> +#include <gpac/modules/codec.h> +#include <gpac/constants.h> + +#ifndef GPAC_READ_ONLY + +#include <gpac/media_tools.h> + +typedef struct +{ + GF_ClientService *service; + Bool od_done; + Bool needs_connection; + u32 status; + LPNETCHANNEL ch; + + GF_SLHeader sl_hdr; + + GF_ISOFile *mp4; + char *szFile; + u32 tt_track; + GF_ISOSample *samp; + u32 samp_num; + + u32 start_range; + /*file downloader*/ + GF_DownloadSession * dnload; +} TTIn; + + +static Bool TTIn_CanHandleURL(GF_InputService *plug, const char *url) +{ + char *sExt; + sExt = strrchr(url, '.'); + if (!sExt) return 0; + if (gf_term_check_extension(plug, "x-subtitle/srt", "srt", "SRT SubTitles", sExt)) return 1; + if (gf_term_check_extension(plug, "x-subtitle/sub", "sub", "SUB SubTitles", sExt)) return 1; + if (gf_term_check_extension(plug, "x-subtitle/ttxt", "ttxt", "3GPP TimedText", sExt)) return 1; + return 0; +} + +static Bool TTIn_is_local(const char *url) +{ + if (!strnicmp(url, "file://", 7)) return 1; + if (strstr(url, "://")) return 0; + return 1; +} + +static GF_ESD *tti_get_esd(TTIn *tti) +{ + return gf_media_map_esd(tti->mp4, tti->tt_track); +} + +static void tti_setup_object(TTIn *tti) +{ + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + GF_ESD *esd = tti_get_esd(tti); + od->objectDescriptorID = esd->ESID; + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(tti->service, (GF_Descriptor *)od, 0); +} + + +GF_Err TTIn_LoadFile(GF_InputService *plug, const char *url, Bool is_cache) +{ + GF_Err e; + GF_MediaImporter import; + + char szFILE[GF_MAX_PATH]; + TTIn *tti = (TTIn *)plug->priv; + const char *cache_dir = gf_modules_get_option((GF_BaseInterface *)plug, "General", "CacheDirectory"); + + if (cache_dir && strlen(cache_dir)) { + if (cache_dir[strlen(cache_dir)-1] != GF_PATH_SEPARATOR) { + sprintf(szFILE, "%s%csrt_%d_mp4", cache_dir, GF_PATH_SEPARATOR, (u32) tti); + } else { + sprintf(szFILE, "%ssrt_%d_mp4", cache_dir, (u32) tti); + } + } else { + sprintf(szFILE, "%d_temp_mp4", (u32) tti); + } + tti->mp4 = gf_isom_open(szFILE, GF_ISOM_OPEN_WRITE, NULL); + if (!tti->mp4) return gf_isom_last_error(NULL); + + tti->szFile = strdup(szFILE); + + memset(&import, 0, sizeof(GF_MediaImporter)); + import.dest = tti->mp4; + /*override layout from sub file*/ + import.flags = GF_IMPORT_SKIP_TXT_BOX; + import.in_name = (char *) url; + + e = gf_media_import(&import); + if (!e) { + tti->tt_track = 1; + gf_isom_text_set_streaming_mode(tti->mp4, 1); + } + return e; +} + +void TTIn_NetIO(void *cbk, GF_NETIO_Parameter *param) +{ + GF_Err e; + const char *szCache; + GF_InputService *plug = (GF_InputService *)cbk; + TTIn *tti = (TTIn *) plug->priv; + + gf_term_download_update_stats(tti->dnload); + + e = param->error; + /*done*/ + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + szCache = gf_dm_sess_get_cache_name(tti->dnload); + if (!szCache) e = GF_IO_ERR; + else { + e = TTIn_LoadFile(plug, szCache, 1); + } + } + else if (param->msg_type==GF_NETIO_DATA_EXCHANGE) + return; + + /*OK confirm*/ + if (tti->needs_connection) { + tti->needs_connection = 0; + gf_term_on_connect(tti->service, NULL, e); + if (!e && !tti->od_done) tti_setup_object(tti); + } +} + +void TTIn_download_file(GF_InputService *plug, const char *url) +{ + TTIn *tti = (TTIn *) plug->priv; + + tti->needs_connection = 1; + tti->dnload = gf_term_download_new(tti->service, url, 0, TTIn_NetIO, plug); + if (!tti->dnload) { + tti->needs_connection = 0; + gf_term_on_connect(tti->service, NULL, GF_NOT_SUPPORTED); + } + /*service confirm is done once fetched*/ +} + +static GF_Err TTIn_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + GF_Err e; + TTIn *tti = (TTIn *)plug->priv; + + tti->service = serv; + + if (tti->dnload) gf_term_download_del(tti->dnload); + tti->dnload = NULL; + + /*remote fetch*/ + if (!TTIn_is_local(url)) { + TTIn_download_file(plug, url); + return GF_OK; + } + e = TTIn_LoadFile(plug, url, 0); + gf_term_on_connect(serv, NULL, e); + if (!e && !tti->od_done) tti_setup_object(tti); + return GF_OK; +} + +static GF_Err TTIn_CloseService(GF_InputService *plug) +{ + TTIn *tti = (TTIn *)plug->priv; + if (tti->samp) gf_isom_sample_del(&tti->samp); + if (tti->mp4) gf_isom_delete(tti->mp4); + tti->mp4 = NULL; + if (tti->szFile) { + gf_delete_file(tti->szFile); + free(tti->szFile); + tti->szFile = NULL; + } + if (tti->dnload) gf_term_download_del(tti->dnload); + tti->dnload = NULL; + + gf_term_on_disconnect(tti->service, NULL, GF_OK); + return GF_OK; +} + +static GF_Descriptor *TTIn_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + TTIn *tti = (TTIn *)plug->priv; + /*visual object*/ + switch (expect_type) { + case GF_MEDIA_OBJECT_UNDEF: + case GF_MEDIA_OBJECT_UPDATES: + case GF_MEDIA_OBJECT_TEXT: + { + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + GF_ESD *esd = tti_get_esd(tti); + od->objectDescriptorID = esd->ESID; + gf_list_add(od->ESDescriptors, esd); + tti->od_done = 1; + return (GF_Descriptor *) od; + } + default: + return NULL; + } + return NULL; +} + +static GF_Err TTIn_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ES_ID; + GF_Err e; + TTIn *tti = (TTIn *)plug->priv; + + e = GF_SERVICE_ERROR; + if (tti->ch==channel) goto exit; + + e = GF_STREAM_NOT_FOUND; + ES_ID = 0; + if (strstr(url, "ES_ID")) sscanf(url, "ES_ID=%d", &ES_ID); + + if (ES_ID==1) { + tti->ch = channel; + e = GF_OK; + } + +exit: + gf_term_on_connect(tti->service, channel, e); + return e; +} + +static GF_Err TTIn_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + TTIn *tti = (TTIn *)plug->priv; + GF_Err e = GF_STREAM_NOT_FOUND; + + if (tti->ch == channel) { + tti->ch = NULL; + e = GF_OK; + } + gf_term_on_disconnect(tti->service, channel, e); + return GF_OK; +} + +static GF_Err TTIn_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + TTIn *tti = (TTIn *)plug->priv; + + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + switch (com->command_type) { + case GF_NET_CHAN_SET_PADDING: + gf_isom_set_sample_padding(tti->mp4, tti->tt_track, com->pad.padding_bytes); + return GF_OK; + case GF_NET_CHAN_DURATION: + com->duration.duration = (Double) (s64) gf_isom_get_media_duration(tti->mp4, tti->tt_track); + com->duration.duration /= gf_isom_get_media_timescale(tti->mp4, tti->tt_track); + return GF_OK; + case GF_NET_CHAN_PLAY: + tti->start_range = (com->play.start_range>0) ? (u32) (com->play.start_range * 1000) : 0; + if (tti->ch == com->base.on_channel) { + tti->samp_num = 0; + if (tti->samp) gf_isom_sample_del(&tti->samp); + } + return GF_OK; + case GF_NET_CHAN_STOP: + return GF_OK; + default: + return GF_OK; + } +} + + +static GF_Err TTIn_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + TTIn *tti = (TTIn *)plug->priv; + + *out_reception_status = GF_OK; + *sl_compressed = 0; + *is_new_data = 0; + + memset(&tti->sl_hdr, 0, sizeof(GF_SLHeader)); + tti->sl_hdr.randomAccessPointFlag = 1; + tti->sl_hdr.compositionTimeStampFlag = 1; + tti->sl_hdr.accessUnitStartFlag = tti->sl_hdr.accessUnitEndFlag = 1; + + /*fetching es data*/ + if (tti->ch == channel) { + if (tti->samp_num>=gf_isom_get_sample_count(tti->mp4, tti->tt_track)) { + *out_reception_status = GF_EOS; + return GF_OK; + } + + if (!tti->samp) { + u32 di; + if (tti->start_range) { + u32 di; + *out_reception_status = gf_isom_get_sample_for_movie_time(tti->mp4, tti->tt_track, tti->start_range, &di, GF_ISOM_SEARCH_SYNC_BACKWARD, &tti->samp, &tti->samp_num); + tti->start_range = 0; + } else { + tti->samp = gf_isom_get_sample(tti->mp4, tti->tt_track, tti->samp_num+1, &di); + } + if (!tti->samp) { + *out_reception_status = GF_CORRUPTED_DATA; + return GF_OK; + } + *is_new_data = 1; + } + tti->sl_hdr.compositionTimeStamp = tti->sl_hdr.decodingTimeStamp = tti->samp->DTS; + *out_data_ptr = tti->samp->data; + *out_data_size = tti->samp->dataLength; + *out_sl_hdr = tti->sl_hdr; + return GF_OK; + } + return GF_STREAM_NOT_FOUND; +} + +static GF_Err TTIn_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) +{ + TTIn *tti = (TTIn *)plug->priv; + + if (tti->ch == channel) { + if (!tti->samp) return GF_BAD_PARAM; + gf_isom_sample_del(&tti->samp); + tti->samp = NULL; + tti->samp_num++; + return GF_OK; + } + return GF_OK; +} + + +void *NewTTReader() +{ + TTIn *priv; + GF_InputService *plug; + GF_SAFEALLOC(plug, GF_InputService); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC SubTitle Reader", "gpac distribution") + + plug->CanHandleURL = TTIn_CanHandleURL; + plug->CanHandleURLInService = NULL; + plug->ConnectService = TTIn_ConnectService; + plug->CloseService = TTIn_CloseService; + plug->GetServiceDescriptor = TTIn_GetServiceDesc; + plug->ConnectChannel = TTIn_ConnectChannel; + plug->DisconnectChannel = TTIn_DisconnectChannel; + plug->ChannelGetSLP = TTIn_ChannelGetSLP; + plug->ChannelReleaseSLP = TTIn_ChannelReleaseSLP; + plug->ServiceCommand = TTIn_ServiceCommand; + + GF_SAFEALLOC(priv, TTIn); + plug->priv = priv; + return plug; +} + +void DeleteTTReader(void *ifce) +{ + GF_InputService *plug = (GF_InputService *) ifce; + TTIn *tti = (TTIn *)plug->priv; + free(tti); + free(plug); +} + +#endif diff --git a/modules/wav_out/Makefile b/modules/wav_out/Makefile new file mode 100644 index 0000000..3d9ba63 --- /dev/null +++ b/modules/wav_out/Makefile @@ -0,0 +1,55 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/wav_out + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include -DDISABLE_WAVE_EX + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS=wav_out.o + + +SRCS := $(OBJS:.o=.c) + +LIB=gm_wav_audio.$(DYN_LIB_SUFFIX) +LDFLAGS+=-export-symbols wav_out.def + +all: $(LIB) + + +$(LIB): $(OBJS) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) -L../../bin/gcc -lgpac $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/wav_out/wav_out.c b/modules/wav_out/wav_out.c new file mode 100644 index 0000000..df70a02 --- /dev/null +++ b/modules/wav_out/wav_out.c @@ -0,0 +1,486 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / wave audio render module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <gpac/modules/audio_out.h> +#include <windows.h> + +#define MAX_AUDIO_BUFFER 30 + +typedef struct +{ + HWAVEOUT hwo; + WAVEHDR wav_hdr[MAX_AUDIO_BUFFER]; + WAVEFORMATEX fmt; + + u32 num_buffers; + u32 buffer_size; + + Bool exit_request; + HANDLE event; + + u32 vol, pan; + u32 delay, total_length_ms; + + Bool force_config; + u32 cfg_num_buffers, cfg_duration; + + char *wav_buf; +} WAVContext; + + + +#if !defined(DISABLE_WAVE_EX) && !defined(_WIN32_WCE) +/*IF YOU CAN'T COMPILE WAVEOUT TRY TO COMMENT THIS - note waveOut & multichannel is likely not to work*/ +#define USE_WAVE_EXT +#endif + + +#ifdef USE_WAVE_EXT + +/*for channel codes*/ +#include <gpac/constants.h> + +#ifndef WAVE_FORMAT_EXTENSIBLE +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE +#endif + +#ifndef SPEAKER_FRONT_LEFT +# define SPEAKER_FRONT_LEFT 0x1 +# define SPEAKER_FRONT_RIGHT 0x2 +# define SPEAKER_FRONT_CENTER 0x4 +# define SPEAKER_LOW_FREQUENCY 0x8 +# define SPEAKER_BACK_LEFT 0x10 +# define SPEAKER_BACK_RIGHT 0x20 +# define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 +# define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 +# define SPEAKER_BACK_CENTER 0x100 +# define SPEAKER_SIDE_LEFT 0x200 +# define SPEAKER_SIDE_RIGHT 0x400 +# define SPEAKER_TOP_CENTER 0x800 +# define SPEAKER_TOP_FRONT_LEFT 0x1000 +# define SPEAKER_TOP_FRONT_CENTER 0x2000 +# define SPEAKER_TOP_FRONT_RIGHT 0x4000 +# define SPEAKER_TOP_BACK_LEFT 0x8000 +# define SPEAKER_TOP_BACK_CENTER 0x10000 +# define SPEAKER_TOP_BACK_RIGHT 0x20000 +# define SPEAKER_RESERVED 0x80000000 +#endif + +#ifndef _WAVEFORMATEXTENSIBLE_ +typedef struct { + WAVEFORMATEX Format; + union { + WORD wValidBitsPerSample; /* bits of precision */ + WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */ + WORD wReserved; /* If neither applies, set to zero. */ + } Samples; + DWORD dwChannelMask; /* which channels are */ + /* present in stream */ + GUID SubFormat; +} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE; +#endif + +const static GUID GPAC_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71} }; + +#pragma message("Using multichannel audio extensions") + +#endif + + + +#define WAVCTX() WAVContext *ctx = (WAVContext *)dr->opaque; + +static GF_Err WAV_Setup(GF_AudioOutput *dr, void *os_handle, u32 num_buffers, u32 total_duration) +{ + WAVCTX(); + + ctx->force_config = (num_buffers && total_duration) ? 1 : 0; + ctx->cfg_num_buffers = num_buffers; + if (ctx->cfg_num_buffers <= 1) ctx->cfg_num_buffers = 2; + ctx->cfg_duration = total_duration; + if (!ctx->force_config) ctx->num_buffers = 6; + + return GF_OK; +} + +static void CALLBACK WaveProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) +{ + GF_AudioOutput *dr = (GF_AudioOutput *) dwInstance; + WAVCTX(); + + if (uMsg != WOM_DONE) return; + if (ctx->exit_request) return; + SetEvent(ctx->event); +} + + +static void close_waveform(GF_AudioOutput *dr) +{ + WAVCTX(); + + if (!ctx->event) return; + + /*brute-force version, actually much safer on winCE*/ +#ifdef _WIN32_WCE + ctx->exit_request = 1; + SetEvent(ctx->event); + waveOutReset(ctx->hwo); + waveOutClose(ctx->hwo); + if (ctx->wav_buf) free(ctx->wav_buf); + ctx->wav_buf = NULL; + CloseHandle(ctx->event); + ctx->event = NULL; + ctx->exit_request = 0; +#else + ctx->exit_request = 1; + SetEvent(ctx->event); + if (ctx->hwo) { + u32 i; + Bool not_done; + MMRESULT res; + /*wait for all buffers to complete, otherwise this locks waveOutReset*/ + while (1) { + not_done = 0; + for (i=0 ; i< ctx->num_buffers; i++) { + if (! (ctx->wav_hdr[i].dwFlags & WHDR_DONE)) { + not_done = 1; + break; + } + } + if (!not_done) break; + gf_sleep(60); + } + /*waveOutReset gives unpredictable results on PocketPC, so just close right away*/ + while (1) { + res = waveOutClose(ctx->hwo); + if (res == MMSYSERR_NOERROR) break; + } + ctx->hwo = NULL; + } + if (ctx->wav_buf) free(ctx->wav_buf); + ctx->wav_buf = NULL; + CloseHandle(ctx->event); + ctx->event = NULL; + ctx->exit_request = 0; +#endif +} + +static void WAV_Shutdown(GF_AudioOutput *dr) +{ + close_waveform(dr); +} + + +/*we assume what was asked is what we got*/ +static GF_Err WAV_ConfigureOutput(GF_AudioOutput *dr, u32 *SampleRate, u32 *NbChannels, u32 *nbBitsPerSample, u32 channel_cfg) +{ + u32 i, retry; + HRESULT hr; + WAVEFORMATEX *fmt; +#ifdef USE_WAVE_EXT + WAVEFORMATEXTENSIBLE format_ex; +#endif + + WAVCTX(); + + if (!ctx) return GF_BAD_PARAM; + + /*reset*/ + close_waveform(dr); + +#ifndef USE_WAVE_EXT + if (*NbChannels>2) *NbChannels=2; +#endif + + memset (&ctx->fmt, 0, sizeof(ctx->fmt)); + ctx->fmt.cbSize = sizeof(WAVEFORMATEX); + ctx->fmt.wFormatTag = WAVE_FORMAT_PCM; + ctx->fmt.nChannels = *NbChannels; + ctx->fmt.wBitsPerSample = *nbBitsPerSample; + ctx->fmt.nSamplesPerSec = *SampleRate; + ctx->fmt.nBlockAlign = ctx->fmt.wBitsPerSample * ctx->fmt.nChannels / 8; + ctx->fmt.nAvgBytesPerSec = *SampleRate * ctx->fmt.nBlockAlign; + fmt = &ctx->fmt; + +#ifdef USE_WAVE_EXT + if (channel_cfg && ctx->fmt.nChannels>2) { + memset(&format_ex, 0, sizeof(WAVEFORMATEXTENSIBLE)); + format_ex.Format = ctx->fmt; + format_ex.Format.cbSize = 22; + format_ex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + format_ex.SubFormat = GPAC_KSDATAFORMAT_SUBTYPE_PCM; + format_ex.Samples.wValidBitsPerSample = ctx->fmt.wBitsPerSample; + format_ex.dwChannelMask = 0; + if (channel_cfg & GF_AUDIO_CH_FRONT_LEFT) format_ex.dwChannelMask |= SPEAKER_FRONT_LEFT; + if (channel_cfg & GF_AUDIO_CH_FRONT_RIGHT) format_ex.dwChannelMask |= SPEAKER_FRONT_RIGHT; + if (channel_cfg & GF_AUDIO_CH_FRONT_CENTER) format_ex.dwChannelMask |= SPEAKER_FRONT_CENTER; + if (channel_cfg & GF_AUDIO_CH_LFE) format_ex.dwChannelMask |= SPEAKER_LOW_FREQUENCY; + if (channel_cfg & GF_AUDIO_CH_BACK_LEFT) format_ex.dwChannelMask |= SPEAKER_BACK_LEFT; + if (channel_cfg & GF_AUDIO_CH_BACK_RIGHT) format_ex.dwChannelMask |= SPEAKER_BACK_RIGHT; + if (channel_cfg & GF_AUDIO_CH_BACK_CENTER) format_ex.dwChannelMask |= SPEAKER_BACK_CENTER; + if (channel_cfg & GF_AUDIO_CH_SIDE_LEFT) format_ex.dwChannelMask |= SPEAKER_SIDE_LEFT; + if (channel_cfg & GF_AUDIO_CH_SIDE_RIGHT) format_ex.dwChannelMask |= SPEAKER_SIDE_RIGHT; + fmt = (WAVEFORMATEX *) &format_ex; + } +#endif + + /* Open a waveform device for output using window callback. */ + retry = 10; + while (retry) { + hr = waveOutOpen((LPHWAVEOUT)&ctx->hwo, WAVE_MAPPER, &ctx->fmt, (DWORD) WaveProc, (DWORD) dr, + CALLBACK_FUNCTION | WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT + ); + + if (hr == MMSYSERR_NOERROR) break; + /*couldn't open audio*/ + if (hr != MMSYSERR_ALLOCATED) return GF_IO_ERR; + retry--; + } + if (hr != MMSYSERR_NOERROR) return GF_IO_ERR; + + if (!ctx->force_config) { + /*one wave buffer size*/ + ctx->buffer_size = 1024 * ctx->fmt.nBlockAlign; + ctx->num_buffers = 2; + } else { + ctx->num_buffers = ctx->cfg_num_buffers; + ctx->buffer_size = (ctx->fmt.nAvgBytesPerSec * ctx->cfg_duration) / (1000 * ctx->cfg_num_buffers); + } + + ctx->event = CreateEvent( NULL, FALSE, FALSE, NULL); + + /*make sure we're aligned*/ + while (ctx->buffer_size % ctx->fmt.nBlockAlign) ctx->buffer_size++; + + ctx->wav_buf = malloc(ctx->buffer_size*ctx->num_buffers*sizeof(char)); + memset(ctx->wav_buf, 0, ctx->buffer_size*ctx->num_buffers*sizeof(char)); + + /*setup wave headers*/ + for (i=0 ; i < ctx->num_buffers; i++) { + memset(& ctx->wav_hdr[i], 0, sizeof(WAVEHDR)); + ctx->wav_hdr[i].dwBufferLength = ctx->buffer_size; + ctx->wav_hdr[i].lpData = & ctx->wav_buf[i*ctx->buffer_size]; + ctx->wav_hdr[i].dwFlags = WHDR_DONE; + waveOutPrepareHeader(ctx->hwo, &ctx->wav_hdr[i], sizeof(WAVEHDR)); + waveOutWrite(ctx->hwo, &ctx->wav_hdr[i], sizeof(WAVEHDR)); + Sleep(1); + } + ctx->total_length_ms = 1000 * ctx->num_buffers * ctx->buffer_size / ctx->fmt.nAvgBytesPerSec; + /*initial delay is full buffer size*/ + ctx->delay = ctx->total_length_ms; + return GF_OK; +} + +static void WAV_WriteAudio(GF_AudioOutput *dr) +{ + LPWAVEHDR hdr; + HRESULT hr; + u32 i; + WAVCTX(); + + if (!ctx->hwo) return; + + WaitForSingleObject(ctx->event, INFINITE); + + if (ctx->exit_request) return; + + for (i=0; i<ctx->num_buffers; i++) { + /*get buffer*/ + hdr = &ctx->wav_hdr[i]; + + if (hdr->dwFlags & WHDR_DONE) { + waveOutUnprepareHeader(ctx->hwo, hdr, sizeof(WAVEHDR)); + + hdr->dwBufferLength = ctx->buffer_size; + /*update delay*/ + ctx->delay = 1000 * i * ctx->buffer_size / ctx->fmt.nAvgBytesPerSec; + /*fill it*/ + hdr->dwBufferLength = dr->FillBuffer(dr->audio_renderer, hdr->lpData, ctx->buffer_size); + hdr->dwFlags = 0; + hr = waveOutPrepareHeader(ctx->hwo, hdr, sizeof(WAVEHDR)); + /*write it*/ + waveOutWrite(ctx->hwo, hdr, sizeof(WAVEHDR)); + } + } +} + +static void WAV_Play(GF_AudioOutput *dr, u32 PlayType) +{ +#if 0 + u32 i; + WAVCTX(); + + switch (PlayType) { + case 0: + waveOutPause(ctx->hwo); + break; + case 2: + for (i=0; i<ctx->num_buffers; i++) { + LPWAVEHDR hdr = &ctx->wav_hdr[i]; + memset(&hdr->lpData, 0, sizeof(char)*ctx->buffer_size); + } + case 1: + waveOutRestart(ctx->hwo); + break; + } +#endif +} + +#ifndef _WIN32_WCE +static void set_vol_pan(WAVContext *ctx) +{ + WORD rV, lV; + /*in wave, volume & pan are specified as a DWORD. LOW word is LEFT channel, HIGH is right - iPaq doesn't support that*/ + lV = (WORD) (ctx->vol * ctx->pan / 100); + rV = (WORD) (ctx->vol * (100 - ctx->pan) / 100); + + waveOutSetVolume(ctx->hwo, MAKELONG(lV, rV) ); +} + +static void WAV_SetVolume(GF_AudioOutput *dr, u32 Volume) +{ + WAVCTX(); + if (Volume > 100) Volume = 100; + ctx->vol = Volume * 0xFFFF / 100; + set_vol_pan(ctx); +} + +static void WAV_SetPan(GF_AudioOutput *dr, u32 Pan) +{ + WAVCTX(); + if (Pan > 100) Pan = 100; + ctx->pan = Pan; + set_vol_pan(ctx); +} + +#else +/*too much trouble with iPaq ...*/ +static void WAV_SetVolume(GF_AudioOutput *dr, u32 Volume) { } +static void WAV_SetPan(GF_AudioOutput *dr, u32 Pan) { } + +#endif + + +static GF_Err WAV_QueryOutputSampleRate(GF_AudioOutput *dr, u32 *desired_samplerate, u32 *NbChannels, u32 *nbBitsPerSample) +{ + /*iPaq's output frequencies available*/ +#ifdef _WIN32_WCE + switch (*desired_samplerate) { + case 11025: + case 22050: + *desired_samplerate = 22050; + break; + case 8000: + case 16000: + case 32000: + *desired_samplerate = 44100; + break; + case 24000: + case 48000: + *desired_samplerate = 44100; + break; + case 44100: + *desired_samplerate = 44100; + break; + default: + break; + } + return GF_OK; +#else + return GF_OK; +#endif +} + +static u32 WAV_GetAudioDelay(GF_AudioOutput *dr) +{ + WAVCTX(); + return ctx->delay; +} + +static u32 WAV_GetTotalBufferTime(GF_AudioOutput *dr) +{ + WAVCTX(); + return ctx->total_length_ms; +} + + +void *NewWAVRender() +{ + WAVContext *ctx; + GF_AudioOutput *driv; + ctx = malloc(sizeof(WAVContext)); + memset(ctx, 0, sizeof(WAVContext)); + ctx->num_buffers = 10; + ctx->pan = 50; + ctx->vol = 100; + driv = malloc(sizeof(GF_AudioOutput)); + memset(driv, 0, sizeof(GF_AudioOutput)); + GF_REGISTER_MODULE_INTERFACE(driv, GF_AUDIO_OUTPUT_INTERFACE, "Windows MME Output", "gpac distribution") + + driv->opaque = ctx; + + driv->SelfThreaded = 0; + driv->Setup = WAV_Setup; + driv->Shutdown = WAV_Shutdown; + driv->ConfigureOutput = WAV_ConfigureOutput; + driv->GetAudioDelay = WAV_GetAudioDelay; + driv->GetTotalBufferTime = WAV_GetTotalBufferTime; + driv->SetVolume = WAV_SetVolume; + driv->SetPan = WAV_SetPan; + driv->Play = WAV_Play; + driv->QueryOutputSampleRate = WAV_QueryOutputSampleRate; + driv->WriteAudio = WAV_WriteAudio; + + return driv; +} + +void DeleteWAVRender(void *ifce) +{ + GF_AudioOutput *dr = (GF_AudioOutput *) ifce; + WAVContext *ctx = (WAVContext *)dr->opaque; + free(ctx); + free(dr); +} + +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) return 1; + return 0; +} + +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE) return NewWAVRender(); + return NULL; +} + +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_AUDIO_OUTPUT_INTERFACE: + DeleteWAVRender((GF_AudioOutput *) ifce); + break; + } +} diff --git a/modules/wav_out/wav_out.def b/modules/wav_out/wav_out.def new file mode 100644 index 0000000..8f4b53f --- /dev/null +++ b/modules/wav_out/wav_out.def @@ -0,0 +1,6 @@ +LIBRARY gm_wav_out.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/x11_out/Makefile b/modules/x11_out/Makefile new file mode 100644 index 0000000..3cac27e --- /dev/null +++ b/modules/x11_out/Makefile @@ -0,0 +1,98 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/x11_out + +CFLAGS= $(OPTFLAGS) + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +CFLAGS+=-I$(SRC_PATH)/include -DGPAC_HAVE_CONFIG_H -I../../ +# -I/usr/local/arm/3.3.2/include + + +ifeq ($(ENABLE_JOYSTICK), yes) +CFLAGS+=-DENABLE_JOYSTICK +endif + +ifeq ($(X11_INC_PATH), ) +else +CFLAGS+=-I$(X11_INC_PATH) +endif + +ifeq ($(X11_LIB_PATH), ) +else +LDFLAGS+=-L$(X11_LIB_PATH) +endif + +ifeq ($(USE_X11_XV), yes) +CFLAGS+=-DGPAC_HAS_X11_XV +LDFLAGS+=-lXv +endif + +ifeq ($(USE_X11_SHM), yes) +CFLAGS+=-DGPAC_HAS_X11_SHM +LDFLAGS+=-lXext +endif + +ifeq ($(HAS_OPENGL), yes) +ifeq ($(GPAC_USE_TINYGL), yes) +else +#CFLAGS +=-DGPAC_HAS_OPENGL +LDFLAGS+=-lGL +#LDFLAGS+=-lTinyGL -L$(SRC_PATH)../../TinyGL/lib/ +endif +endif + +#common obj +OBJS=x11_out.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_x11_out.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -lX11 -L../../bin/gcc -lgpac -o ../../bin/gcc/$@ $(OBJSPIC) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/x11_out/x11_out.c b/modules/x11_out/x11_out.c new file mode 100644 index 0000000..9629044 --- /dev/null +++ b/modules/x11_out/x11_out.c @@ -0,0 +1,1547 @@ +/* + * GPAC Multimedia Framework + * + * Authors: DINH Anh Min - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / X11 video output module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "x11_out.h" +#include <gpac/constants.h> + + +void X11_SetupWindow (GF_VideoOutput * vout); + + +#ifdef GPAC_HAS_X11_XV +static void X11_DestroyOverlay(XWindow *xwin) +{ + if (xwin->overlay) XFree(xwin->overlay); + xwin->overlay = NULL; + xwin->xv_pf_format = 0; + if (xwin->xvport>=0) { + XvUngrabPort(xwin->display, xwin->xvport, CurrentTime ); + xwin->xvport = -1; + } +} + +static Bool is_same_yuv(u32 pf, u32 a_pf) +{ + if (pf==a_pf) return 1; + return 0; + switch (pf) { + case GF_PIXEL_YV12: + case GF_PIXEL_I420: + case GF_PIXEL_IYUV: + switch (a_pf) { + case GF_PIXEL_YV12: + case GF_PIXEL_I420: + case GF_PIXEL_IYUV: + return 1; + default: + return 0; + } + case GF_PIXEL_UYVY: + case GF_PIXEL_UYNV: + case GF_PIXEL_Y422: + switch (a_pf) { + case GF_PIXEL_UYVY: + case GF_PIXEL_UYNV: + case GF_PIXEL_Y422: + return 1; + default: + return 0; + } + case GF_PIXEL_YUY2: + case GF_PIXEL_YUNV: + switch (a_pf) { + case GF_PIXEL_YUY2: + case GF_PIXEL_YUNV: + return 1; + default: + return 0; + } + } + return 0; +} +static u32 X11_GetPixelFormat(u32 pf) +{ + return GF_4CC((pf)&0xff, (pf>>8)&0xff, (pf>>16)&0xff, (pf>>24)&0xff); +} +static int X11_GetXVideoPort(GF_VideoOutput *vout, u32 pixel_format, Bool check_color) +{ + XWindow *xwin = (XWindow *)vout->opaque; + Bool has_color_key = 0; + XvAdaptorInfo *adaptors; + unsigned int i; + unsigned int nb_adaptors; + int selected_port; + + if (XvQueryExtension(xwin->display, &i, &i, &i, &i, &i ) != Success) return -1; + if (XvQueryAdaptors(xwin->display, DefaultRootWindow(xwin->display), &nb_adaptors, &adaptors) != Success) return -1; + + selected_port = -1; + for (i=0; i < nb_adaptors; i++) { + XvImageFormatValues *formats; + int j, num_formats, port; + + if( !( adaptors[i].type & XvInputMask ) || !(adaptors[i].type & XvImageMask ) ) + continue; + + /* Check for our format... */ + formats = XvListImageFormats(xwin->display, adaptors[i].base_id, &num_formats); + + for (j=0; j<num_formats && (selected_port == -1 ); j++) { + XvAttribute *attr; + int k, nb_attributes; + u32 pformat = X11_GetPixelFormat(formats[j].id); + + if( !is_same_yuv(pformat, pixel_format) ) continue; + + /* Grab first port supporting this format */ + for (port=adaptors[i].base_id; (port < (int)(adaptors[i].base_id + adaptors[i].num_ports) ) && (selected_port == -1); port++) { + if (port==xwin->xvport) continue; + + attr = XvQueryPortAttributes(xwin->display, port, &nb_attributes); + for (k=0; k<nb_attributes; k++ ) { + if (!strcmp(attr[k].name, "XV_COLORKEY")) { + const Atom ckey = XInternAtom(xwin->display, "XV_COLORKEY", False); + XvGetPortAttribute(xwin->display, port, ckey, &vout->overlay_color_key); + has_color_key = 1; + vout->overlay_color_key |= 0xFF000000; + } +/* else if (!strcmp(attr[k].name, "XV_AUTOPAINT_COLORKEY")) { + const Atom paint = XInternAtom(xwin->display, "XV_AUTOPAINT_COLORKEY", False); + XvSetPortAttribute(xwin->display, port, paint, 1); + } +*/ } + if (check_color && !has_color_key) continue; + + if (XvGrabPort(xwin->display, port, CurrentTime) == Success) { + selected_port = port; + xwin->xv_pf_format = formats[j].id; + } + } + if (selected_port == -1 ) + continue; + + } + if (formats != NULL) XFree(formats); + } + if (nb_adaptors > 0) + XvFreeAdaptorInfo(adaptors); + + return selected_port; +} +static GF_Err X11_InitOverlay(GF_VideoOutput *vout, u32 VideoWidth, u32 VideoHeight) +{ + XWindow *xwin = (XWindow *)vout->opaque; + if (xwin->overlay && (VideoWidth<=xwin->overlay->width) && (VideoHeight<=xwin->overlay->height)) { + return GF_OK; + } + + X11_DestroyOverlay(xwin); + + xwin->xvport = X11_GetXVideoPort(vout, GF_PIXEL_I420, 0); + if (xwin->xvport<0) + xwin->xvport = X11_GetXVideoPort(vout, GF_PIXEL_YUY2, 0); + + if (xwin->xvport<0) { + return GF_NOT_SUPPORTED; + } + + /* Create overlay image */ + xwin->overlay = XvCreateImage(xwin->display, xwin->xvport, xwin->xv_pf_format, NULL, VideoWidth, VideoHeight); + if (!xwin->overlay) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[X11] Xv Overlay Creation Failure\n")); + return GF_IO_ERR; + } + + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[X11] Overlay init %d x %d - pixel format %s - XV port %d\n", + VideoWidth, VideoHeight, + gf_4cc_to_str(vout->yuv_pixel_format), xwin->xvport )); + + return GF_OK; +} + +GF_Err X11_Blit(struct _video_out *vout, GF_VideoSurface *video_src, GF_Window *src, GF_Window *dest, u32 overlay_type) +{ + XvImage *overlay; + int xvport; + Drawable dst_dr; + GF_Err e; + Window cur_wnd; + XWindow *xwin = (XWindow *)vout->opaque; + + if (video_src->pixel_format != GF_PIXEL_YV12) return GF_NOT_SUPPORTED; + cur_wnd = xwin->fullscreen ? xwin->full_wnd : xwin->wnd; + /*init if needed*/ + if ((xwin->xvport<0) || !xwin->overlay) { + e = X11_InitOverlay(vout, video_src->width, video_src->height); + if (e) return e; + } + + /*different size, recreate an image*/ + if ((xwin->overlay->width != video_src->width) || (xwin->overlay->height != video_src->height)) { + if (xwin->overlay) XFree(xwin->overlay); + xwin->overlay = XvCreateImage(xwin->display, xwin->xvport, xwin->xv_pf_format, NULL, video_src->width, video_src->height); + if (!xwin->overlay) return GF_IO_ERR; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[X11] Blit surface to dest %d x %d - overlay type %s\n", dest->w, dest->h, + (overlay_type==0)? "none" : ((overlay_type==1) ? "Top-Level" : "ColorKey") + )); + + overlay = xwin->overlay; + xvport = xwin->xvport; + + overlay->data = video_src->video_buffer; + + overlay->num_planes = 3; + overlay->pitches[0] = video_src->width; + overlay->pitches[1] = xwin->overlay->pitches[2] = video_src->width/2; + overlay->offsets[0] = 0; + overlay->offsets[1] = video_src->width*video_src->height; + overlay->offsets[2] = 5*video_src->width*video_src->height/4; + + dst_dr = cur_wnd; + if (!overlay_type) { + if (!xwin->pixmap) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[X11] Back buffer not configured for Blt\n")); + return GF_BAD_PARAM; + } + dst_dr = xwin->pixmap; + } + XvPutImage(xwin->display, xvport, dst_dr, xwin->the_gc, overlay, + src->x, src->y, src->w, src->h, + dest->x, dest->y, dest->w, dest->h); + + return GF_OK; +} +#endif + + +/*Flush video */ +GF_Err X11_Flush(struct _video_out *vout, GF_Window * dest) +{ + Window cur_wnd; + /* + * write backbuffer to screen + */ + X11VID (); + + if (!xWindow->is_init) return GF_OK; + cur_wnd = xWindow->fullscreen ? xWindow->full_wnd : xWindow->wnd; + + if (xWindow->output_3d_mode==1) { +#ifdef GPAC_HAS_OPENGL + XSync(xWindow->display, False); + glFlush(); + glXSwapBuffers(xWindow->display, cur_wnd); +#endif + return GF_OK; + } + +#ifdef GPAC_HAS_X11_SHM + if (xWindow->pixmap) { + XClearWindow(xWindow->display, cur_wnd); + XSync(xWindow->display, False); + } else if (xWindow->use_shared_memory) { + XSync(xWindow->display, False); + XShmPutImage (xWindow->display, cur_wnd, xWindow->the_gc, xWindow->surface, + 0, 0, dest->x, dest->y, dest->w, dest->h, True); + } else +#endif + { + XSync(xWindow->display, False); + XRaiseWindow(xWindow->display, xWindow->wnd); + XPutImage (xWindow->display, cur_wnd, xWindow->the_gc, xWindow->surface, + 0, 0, dest->x, dest->y, dest->w, dest->h); + } + return GF_OK; +} + +//===================================== +/* + * Translate X_Key to GF_Key + */ +//===================================== +static void x11_translate_key(u32 X11Key, GF_EventKey *evt) +{ + evt->flags = 0; + evt->hw_code = X11Key & 0xFF; + switch (X11Key) { + + case XK_BackSpace: evt->key_code = GF_KEY_BACKSPACE; break; + case XK_Tab: evt->key_code = GF_KEY_TAB; break; + //case XK_Linefeed: evt->key_code = GF_KEY_LINEFEED; break; + case XK_Clear: evt->key_code = GF_KEY_CLEAR; break; + + case XK_KP_Enter: + evt->flags = GF_KEY_EXT_NUMPAD; + case XK_Return: + evt->key_code = GF_KEY_ENTER; + break; + case XK_Pause: evt->key_code = GF_KEY_PAUSE; break; + case XK_Caps_Lock: evt->key_code = GF_KEY_CAPSLOCK; break; + case XK_Scroll_Lock: evt->key_code = GF_KEY_SCROLL; break; + case XK_Escape: evt->key_code = GF_KEY_ESCAPE; break; + case XK_Delete: + evt->key_code = GF_KEY_DEL; + break; + + case XK_Kanji: evt->key_code = GF_KEY_KANJIMODE; break; + case XK_Katakana: evt->key_code = GF_KEY_JAPANESEKATAKANA; break; + case XK_Romaji: evt->key_code = GF_KEY_JAPANESEROMAJI; break; + case XK_Hiragana: evt->key_code = GF_KEY_JAPANESEHIRAGANA; break; + case XK_Kana_Lock: evt->key_code = GF_KEY_KANAMODE; break; + + case XK_Home: evt->key_code = GF_KEY_HOME; break; + case XK_Left: evt->key_code = GF_KEY_LEFT; break; + case XK_Up: evt->key_code = GF_KEY_UP; break; + case XK_Right: evt->key_code = GF_KEY_RIGHT; break; + case XK_Down: evt->key_code = GF_KEY_DOWN; break; + case XK_Page_Up: evt->key_code = GF_KEY_PAGEUP; break; + case XK_Page_Down: evt->key_code = GF_KEY_PAGEDOWN; break; + case XK_End: evt->key_code = GF_KEY_END; break; + //case XK_Begin: evt->key_code = GF_KEY_BEGIN; break; + + + case XK_Select: evt->key_code = GF_KEY_SELECT; break; + case XK_Print: evt->key_code = GF_KEY_PRINTSCREEN; break; + case XK_Execute: evt->key_code = GF_KEY_EXECUTE; break; + case XK_Insert: evt->key_code = GF_KEY_INSERT; break; + case XK_Undo: evt->key_code = GF_KEY_UNDO; break; + //case XK_Redo: evt->key_code = GF_KEY_BEGIN; break; + //case XK_Menu: evt->key_code = GF_KEY_BEGIN; break; + case XK_Find: evt->key_code = GF_KEY_FIND; break; + case XK_Cancel: evt->key_code = GF_KEY_CANCEL; break; + case XK_Help: evt->key_code = GF_KEY_HELP; break; + //case XK_Break: evt->key_code = GF_KEY_BREAK; break; + //case XK_Mode_switch: evt->key_code = GF_KEY_BEGIN; break; + case XK_Num_Lock: evt->key_code = GF_KEY_NUMLOCK; break; + + case XK_F1: evt->key_code = GF_KEY_F1; break; + case XK_F2: evt->key_code = GF_KEY_F2; break; + case XK_F3: evt->key_code = GF_KEY_F3; break; + case XK_F4: evt->key_code = GF_KEY_F4; break; + case XK_F5: evt->key_code = GF_KEY_F5; break; + case XK_F6: evt->key_code = GF_KEY_F6; break; + case XK_F7: evt->key_code = GF_KEY_F7; break; + case XK_F8: evt->key_code = GF_KEY_F8; break; + case XK_F9: evt->key_code = GF_KEY_F9; break; + case XK_F10: evt->key_code = GF_KEY_F10; break; + case XK_F11: evt->key_code = GF_KEY_F11; break; + case XK_F12: evt->key_code = GF_KEY_F12; break; + case XK_F13: evt->key_code = GF_KEY_F13; break; + case XK_F14: evt->key_code = GF_KEY_F14; break; + case XK_F15: evt->key_code = GF_KEY_F15; break; + case XK_F16: evt->key_code = GF_KEY_F16; break; + case XK_F17: evt->key_code = GF_KEY_F17; break; + case XK_F18: evt->key_code = GF_KEY_F18; break; + case XK_F19: evt->key_code = GF_KEY_F19; break; + case XK_F20: evt->key_code = GF_KEY_F20; break; + case XK_F21: evt->key_code = GF_KEY_F21; break; + case XK_F22: evt->key_code = GF_KEY_F22; break; + case XK_F23: evt->key_code = GF_KEY_F23; break; + case XK_F24: evt->key_code = GF_KEY_F24; break; + + case XK_KP_Delete: + case XK_KP_Decimal: + evt->flags = GF_KEY_EXT_NUMPAD; + evt->key_code = GF_KEY_COMMA; + break; + + case XK_KP_Insert: + case XK_KP_0: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_0; break; + case XK_KP_End: + case XK_KP_1: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_1; break; + case XK_KP_Down: + case XK_KP_2: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_2; break; + case XK_KP_Page_Down: + case XK_KP_3: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_3; break; + case XK_KP_Left: + case XK_KP_4: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_4; break; + case XK_KP_Begin: + case XK_KP_5: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_5; break; + case XK_KP_Right: + case XK_KP_6: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_6; break; + case XK_KP_Home: + case XK_KP_7: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_7; break; + case XK_KP_Up: + case XK_KP_8: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_8; break; + case XK_KP_Page_Up: + case XK_KP_9: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_9; break; + case XK_KP_Add: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_PLUS; break; + case XK_KP_Subtract: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_HYPHEN; break; + case XK_KP_Multiply: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_STAR; break; + case XK_KP_Divide: + evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_SLASH; break; + + + case XK_Shift_R: + evt->flags = GF_KEY_EXT_RIGHT; + case XK_Shift_L: + evt->key_code = GF_KEY_SHIFT; + break; + case XK_Control_R: + evt->flags = GF_KEY_EXT_RIGHT; + case XK_Control_L: + evt->key_code = GF_KEY_CONTROL; + break; + case XK_Alt_R: + evt->flags = GF_KEY_EXT_RIGHT; + case XK_Alt_L: + evt->key_code = GF_KEY_ALT; + break; + case XK_Super_R: + evt->flags = GF_KEY_EXT_RIGHT; + case XK_Super_L: + evt->key_code = GF_KEY_WIN; + break; + + case XK_Menu: evt->key_code = GF_KEY_META; break; +#ifdef XK_XKB_KEYS + case XK_ISO_Level3_Shift: evt->key_code = GF_KEY_ALTGRAPH; break; + +#endif + case '!': evt->key_code = GF_KEY_EXCLAMATION; break; + case '"': evt->key_code = GF_KEY_QUOTATION; break; + case '#': evt->key_code = GF_KEY_NUMBER; break; + case '$': evt->key_code = GF_KEY_DOLLAR; break; + case '&': evt->key_code = GF_KEY_AMPERSAND; break; + case '\'': evt->key_code = GF_KEY_APOSTROPHE; break; + case '(': evt->key_code = GF_KEY_LEFTPARENTHESIS; break; + case ')': evt->key_code = GF_KEY_RIGHTPARENTHESIS; break; + case ',': evt->key_code = GF_KEY_COMMA; break; + case ':': evt->key_code = GF_KEY_COLON; break; + case ';': evt->key_code = GF_KEY_SEMICOLON; break; + case '<': evt->key_code = GF_KEY_LESSTHAN; break; + case '>': evt->key_code = GF_KEY_GREATERTHAN; break; + case '?': evt->key_code = GF_KEY_QUESTION; break; + case '@': evt->key_code = GF_KEY_AT; break; + case '[': evt->key_code = GF_KEY_LEFTSQUAREBRACKET; break; + case ']': evt->key_code = GF_KEY_RIGHTSQUAREBRACKET; break; + case '\\': evt->key_code = GF_KEY_BACKSLASH; break; + case '_': evt->key_code = GF_KEY_UNDERSCORE; break; + case '`': evt->key_code = GF_KEY_GRAVEACCENT; break; + case ' ': evt->key_code = GF_KEY_SPACE; break; + case '/': evt->key_code = GF_KEY_SLASH; break; + case '*': evt->key_code = GF_KEY_STAR; break; + case '-': evt->key_code = GF_KEY_HYPHEN; break; + case '+': evt->key_code = GF_KEY_PLUS; break; + case '=': evt->key_code = GF_KEY_EQUALS; break; + case '^': evt->key_code = GF_KEY_CIRCUM; break; + case '{': evt->key_code = GF_KEY_LEFTCURLYBRACKET; break; + case '}': evt->key_code = GF_KEY_RIGHTCURLYBRACKET; break; + case '|': evt->key_code = GF_KEY_PIPE; break; + default: + if ((X11Key>='0') && (X11Key<='9')) evt->key_code = GF_KEY_0 + X11Key - '0'; + else if ((X11Key>='A') && (X11Key<='Z')) evt->key_code = GF_KEY_A + X11Key - 'A'; + /*DOM3: translate to A -> Z, not a -> z*/ + else if ((X11Key>='a') && (X11Key<='z')) { + evt->key_code = GF_KEY_A + X11Key - 'a'; + evt->hw_code = X11Key - 'a' + 'A'; + } + else { + evt->key_code = 0; + GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[X11] Unrecognized key %X\n", X11Key)); + } + break; + } +} + + + +/* taken from SDL + Ack! XPending() actually performs a blocking read if no events available +*/ +static int X11_Pending(Display *display) +{ + /* Flush the display connection and look to see if events are queued */ + XFlush(display); + if ( XEventsQueued(display, QueuedAlready) ) return(1); + + /* More drastic measures are required -- see if X is ready to talk */ + { + static struct timeval zero_time; /* static == 0 */ + int x11_fd; + fd_set fdset; + x11_fd = ConnectionNumber(display); + FD_ZERO(&fdset); + FD_SET(x11_fd, &fdset); + if ( select(x11_fd+1, &fdset, NULL, NULL, &zero_time) == 1 ) { + return(XPending(display)); + } + } + /* Oh well, nothing is ready .. */ + return(0); +} + +/* + * handle X11 events + * here we handle key, mouse, repaint and window sizing events + */ +static void X11_HandleEvents(GF_VideoOutput *vout) +{ + GF_Event evt; + Window the_window; + XComposeStatus state; + X11VID(); + unsigned char keybuf[32]; + XEvent xevent; +#ifdef ENABLE_JOYSTICK + struct js_event jsevent; +#endif + the_window = xWindow->fullscreen ? xWindow->full_wnd : xWindow->wnd; + XSync(xWindow->display, False); + + while (X11_Pending(xWindow->display)) { + XNextEvent(xWindow->display, &xevent); + if (xevent.xany.window!=the_window) continue; + switch (xevent.type) { + /* + * X11 window resized event + * must inform GPAC + */ + case ConfigureNotify: + if ((unsigned int) xevent.xconfigure.width != xWindow->w_width + || (unsigned int) xevent.xconfigure.height != xWindow->w_height) + { + evt.type = GF_EVENT_SIZE; + xWindow->w_width = evt.size.width = xevent.xconfigure.width; + xWindow->w_height = evt.size.height = xevent.xconfigure.height; + vout->on_event(vout->evt_cbk_hdl, &evt); + } else { + evt.type = GF_EVENT_MOVE; + evt.move.x = xevent.xconfigure.x; + evt.move.y = xevent.xconfigure.y; + vout->on_event(vout->evt_cbk_hdl, &evt); + } + break; + /* + * Windows need repaint + */ + case Expose: + if (xevent.xexpose.count > 0) break; + evt.type = GF_EVENT_REFRESH; + vout->on_event (vout->evt_cbk_hdl, &evt); + break; + + /* Have we been requested to quit (or another client message?) */ + case ClientMessage: + if ( (xevent.xclient.format == 32) && (xevent.xclient.data.l[0] == xWindow->WM_DELETE_WINDOW) ) { + evt.type = GF_EVENT_QUIT; + vout->on_event(vout->evt_cbk_hdl, &evt); + } + break; + +#ifndef ENABLE_JOYSTICK + + case KeyPress: + case KeyRelease: + x11_translate_key(XKeycodeToKeysym (xWindow->display, xevent.xkey.keycode, 0), &evt.key); + evt.type = (xevent.type ==KeyPress) ? GF_EVENT_KEYDOWN : GF_EVENT_KEYUP; + vout->on_event (vout->evt_cbk_hdl, &evt); + + if (xevent.type ==KeyPress) { + XLookupString (&xevent.xkey, (char *) keybuf, sizeof(keybuf), NULL, &state); + if (keybuf[0]) { + evt.character.unicode_char = keybuf[0]; + evt.type = GF_EVENT_TEXTINPUT; + vout->on_event (vout->evt_cbk_hdl, &evt); + } + } + break; + + case ButtonPress: + if (!xWindow->fullscreen && !xWindow->has_focus) { + xWindow->has_focus = 1; + XSetInputFocus(xWindow->display, xWindow->wnd, RevertToParent, CurrentTime); + } + case ButtonRelease: + // last_mouse_move = xevent.xbutton.time; + evt.mouse.x = xevent.xbutton.x; + evt.mouse.y = xevent.xbutton.y; + evt.type = (xevent.type == ButtonRelease) ? GF_EVENT_MOUSEUP : GF_EVENT_MOUSEDOWN; + + switch (xevent.xbutton.button) { + case Button1: + evt.mouse.button = GF_MOUSE_LEFT; + vout->on_event (vout->evt_cbk_hdl, &evt); + break; + case Button2: + evt.mouse.button = GF_MOUSE_MIDDLE; + vout->on_event (vout->evt_cbk_hdl, &evt); + break; + case Button3: + evt.mouse.button = GF_MOUSE_RIGHT; + vout->on_event (vout->evt_cbk_hdl, &evt); + break; + case Button4: + evt.type = GF_EVENT_MOUSEWHEEL; + evt.mouse.wheel_pos = FIX_ONE; + vout->on_event(vout->evt_cbk_hdl, &evt); + break; + case Button5: + evt.type = GF_EVENT_MOUSEWHEEL; + evt.mouse.wheel_pos = -FIX_ONE; + vout->on_event(vout->evt_cbk_hdl, &evt); + break; + } + if (!xWindow->fullscreen && (xevent.type==ButtonPress) ) + XSetInputFocus(xWindow->display, xWindow->wnd, RevertToNone, CurrentTime); + break; + + case MotionNotify: + evt.type = GF_EVENT_MOUSEMOVE; + evt.mouse.x = xevent.xmotion.x; + evt.mouse.y = xevent.xmotion.y; + vout->on_event (vout->evt_cbk_hdl, &evt); + break; + case PropertyNotify: + break; + case MapNotify: + break; + case CirculateNotify: + break; + case UnmapNotify: + break; + case ReparentNotify: + break; + case FocusOut: + if (!xWindow->fullscreen) xWindow->has_focus = 0; + break; + case FocusIn: + if (!xWindow->fullscreen) xWindow->has_focus = 1; + break; + + case DestroyNotify: + evt.type = GF_EVENT_QUIT; + vout->on_event(vout->evt_cbk_hdl, &evt); + break; + + default: + break; +#endif + } + } + +#ifdef ENABLE_JOYSTICK + while (read (xWindow->fd, &jsevent, sizeof(struct js_event)) > 0){ + s32 mul; + + switch (jsevent.type) { + case JS_EVENT_BUTTON: + + if (jsevent.value){ + /*pressed event*/ + if (!xWindow->fullscreen && !xWindow->has_focus ) xWindow->has_focus = 1; + if (jsevent.number<4) evt.type = GF_EVENT_MOUSEDOWN; + else evt.type = GF_EVENT_KEYDOWN; + + } else { + + if (jsevent.number<4) evt.type = GF_EVENT_MOUSEUP; + else evt.type = GF_EVENT_KEYUP; + + } + + + switch (jsevent.number){ + case 0: + evt.mouse.button = GF_MOUSE_LEFT; + evt.mouse.x = xWindow->prev_x; + evt.mouse.y = xWindow->prev_y; + vout->on_event (vout->evt_cbk_hdl, &evt); + break; + + case 1: + evt.mouse.button = GF_MOUSE_MIDDLE; + evt.mouse.x = xWindow->prev_x; + evt.mouse.y = xWindow->prev_y; + vout->on_event (vout->evt_cbk_hdl, &evt); + break; + + case 2: + evt.mouse.button = GF_MOUSE_RIGHT; + evt.mouse.x = xWindow->prev_x; + evt.mouse.y = xWindow->prev_y; + vout->on_event (vout->evt_cbk_hdl, &evt); + break; + + case 4: + case 5: + case 6: + case 7: + evt.key.key_code = GF_KEY_JOYSTICK; + evt.key.hw_code = jsevent.number; + vout->on_event (vout->evt_cbk_hdl, &evt); + break; + + default: + break; + } + + + break; + case JS_EVENT_AXIS: + if (ABS(jsevent.value)>16000) mul = 6; + else if (ABS(jsevent.value)>1024) mul = 2; + else mul = 1; + if (jsevent.number==0) { + if (jsevent.value>0) xWindow->prev_x += mul; + else if (jsevent.value<0) { + xWindow->prev_x -= mul; + // if (xWindow->prev_x <0) xWindow->prev_x=0; + } + if (vout->centered_mode==1){ + xWindow->prev_x = ((0xFFFF/2) + jsevent.value) * xWindow->w_width; + xWindow->prev_x /= 0xFFFF; + } + + } + else if (jsevent.number==1) { + if (jsevent.value>0) xWindow->prev_y += mul; + else if (jsevent.value<0) { + xWindow->prev_y -= mul; + // if (xWindow->prev_y <0) xWindow->prev_y=0; + } + if (vout->centered_mode==1) { + xWindow->prev_y = ((0xFFFF/2) + jsevent.value) * xWindow->w_height; + xWindow->prev_y /= 0xFFFF; + } + /* if (jsevent.value>0) xWindow->prev_y += mul; + else xWindow->prev_y -= mul; */ + + } + /* fprintf(stdout, "X %d - y %d\n", xWindow->prev_x, xWindow->prev_y); */ + evt.type = GF_EVENT_MOUSEMOVE; + evt.mouse.x = xWindow->prev_x; + evt.mouse.y = xWindow->prev_y; + vout->on_event (vout->evt_cbk_hdl, &evt); + break; + + case PropertyNotify: + break; + case MapNotify: + break; + case CirculateNotify: + break; + case UnmapNotify: + break; + case ReparentNotify: + break; + case FocusOut: + // if (!xWindow->fullscreen) xWindow->has_focus = 0; + break; + case FocusIn: + // if (!xWindow->fullscreen) xWindow->has_focus = 1; + break; + + case DestroyNotify: + evt.type = GF_EVENT_QUIT; + vout->on_event(vout->evt_cbk_hdl, &evt); + break; + + // case JS_EVENT_INIT: + } + } +#endif +} + + + +#ifdef GPAC_HAS_OPENGL +static GF_Err X11_SetupGL(GF_VideoOutput *vout) +{ + GF_Event evt; + XWindow *xWin = (XWindow *)vout->opaque; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[X11] Setting up GL for display %d\n", xWin->display)); + XSync(xWin->display, False); + xWin->glx_context = glXCreateContext(xWin->display,xWin->glx_visualinfo, NULL, True); + XSync(xWin->display, False); + if (!xWin->glx_context) return GF_IO_ERR; + if (xWin->output_3d_mode==2) return GF_IO_ERR; + + if ( ! glXMakeCurrent(xWin->display, xWin->fullscreen ? xWin->full_wnd : xWin->wnd, xWin->glx_context) ) return GF_IO_ERR; + XSync(xWin->display, False); + evt.type = GF_EVENT_VIDEO_SETUP; + vout->on_event (vout->evt_cbk_hdl,&evt); + xWin->is_init = 1; + return GF_OK; +} + +static GF_Err X11_SetupGLPixmap(GF_VideoOutput *vout, u32 width, u32 height) +{ + XWindow *xWin = (XWindow *)vout->opaque; + + if (xWin->glx_context) { + glXMakeCurrent(xWin->display, None, NULL); + glXDestroyContext(xWin->display, xWin->glx_context); + xWin->glx_context = NULL; + } + if (xWin->gl_offscreen) glXDestroyGLXPixmap(xWin->display, xWin->gl_offscreen); + xWin->gl_offscreen = 0; + if (xWin->gl_pixmap) XFreePixmap(xWin->display, xWin->gl_pixmap); + xWin->gl_pixmap = 0; + + if (xWin->offscreen_type) { + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[X11] Using offscreen GL context through XWindow\n")); + if (xWin->offscreen_type==2) { + XSync(xWin->display, False); + XMapWindow (xWin->display, (Window) xWin->gl_wnd); + XSync(xWin->display, False); +// XSetWMNormalHints (xWin->display, xWin->gl_wnd, Hints); + XStoreName (xWin->display, xWin->gl_wnd, "GPAC Offscreeen Window - debug mode"); + } + + XSync(xWin->display, False); + xWin->glx_context = glXCreateContext(xWin->display,xWin->glx_visualinfo, NULL, True); + XSync(xWin->display, False); + if (!xWin->glx_context) return GF_IO_ERR; + if ( ! glXMakeCurrent(xWin->display, xWin->gl_wnd, xWin->glx_context) ) return GF_IO_ERR; + XSync(xWin->display, False); + + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[X11] Using offscreen GL context through glXPixmap\n")); + xWin->gl_pixmap = XCreatePixmap(xWin->display, xWin->gl_wnd, width, height, xWin->depth); + if (!xWin->gl_pixmap) return GF_IO_ERR; + + xWin->gl_offscreen = glXCreateGLXPixmap(xWin->display, xWin->glx_visualinfo, xWin->gl_pixmap); + if (!xWin->gl_offscreen) return GF_IO_ERR; + + XSync(xWin->display, False); + xWin->glx_context = glXCreateContext(xWin->display, xWin->glx_visualinfo, 0, GL_FALSE); + XSync(xWin->display, False); + if (!xWin->glx_context) return GF_IO_ERR; + + XSync(xWin->display, False); + fprintf(stdout, "!! Activating GLContext on GLPixmap - this may crash !!\n"); + glXMakeCurrent(xWin->display, xWin->gl_offscreen, xWin->glx_context); + } + + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[X11] Offscreen GL context setup\n")); + return GF_OK; +} + +static void X11_ReleaseGL(XWindow *xWin) +{ + XSync(xWin->display, False); + if (xWin->glx_context) { + glXMakeCurrent(xWin->display, None, NULL); + glXDestroyContext(xWin->display, xWin->glx_context); + xWin->glx_context = NULL; + } + xWin->is_init = 0; + XSync(xWin->display, False); +} + +#endif + + +static void X11_ReleaseBackBuffer (GF_VideoOutput * vout) +{ + X11VID (); +#ifdef GPAC_HAS_X11_SHM + if (xWindow->shmseginfo) XShmDetach (xWindow->display, xWindow->shmseginfo); + if (xWindow->pixmap) { + XFreePixmap(xWindow->display, xWindow->pixmap); + xWindow->pixmap = 0L; + xWindow->pwidth = xWindow->pheight = 0; + } else { + if (xWindow->surface) XDestroyImage(xWindow->surface); + xWindow->surface = NULL; + } + if (xWindow->shmseginfo) { + if (xWindow->shmseginfo->shmaddr) shmdt(xWindow->shmseginfo->shmaddr); + if (xWindow->shmseginfo->shmid >= 0) + shmctl (xWindow->shmseginfo->shmid, IPC_RMID, NULL); + free (xWindow->shmseginfo); + xWindow->shmseginfo = NULL; + } +#endif + if (xWindow->surface) { + if (xWindow->surface->data) + free (xWindow->surface->data); + XFree(xWindow->surface); + xWindow->surface = NULL; + } + xWindow->is_init = 0; +#ifdef GPAC_HAS_X11_XV + X11_DestroyOverlay(xWindow); +#endif + +} + +/* + * init backbuffer + */ +GF_Err X11_InitBackBuffer (GF_VideoOutput * vout, u32 VideoWidth, u32 VideoHeight) +{ + Window cur_wnd; + u32 size; + VideoWidth = VideoWidth > 32 ? VideoWidth : 32; + VideoWidth = VideoWidth < 4096 ? VideoWidth : 4096; + VideoHeight = VideoHeight > 32 ? VideoHeight : 32; + VideoHeight = VideoHeight > 4096 ? 4096 : VideoHeight; + + X11VID (); + if (!xWindow || !VideoWidth || !VideoHeight) + return GF_BAD_PARAM; + + X11_ReleaseBackBuffer(vout); + /*WATCHOUT seems we need even width when using shared memory extensions*/ + if (xWindow->use_shared_memory && (VideoWidth%2)) + VideoWidth++; + + size = VideoWidth * VideoHeight * xWindow->bpp; + cur_wnd = xWindow->fullscreen ? xWindow->full_wnd : xWindow->wnd; + +#ifdef GPAC_HAS_X11_SHM + /*if we're using YUV blit to offscreen, we must use a pixmap*/ + if (vout->hw_caps & GF_VIDEO_HW_HAS_YUV) { + GF_SAFEALLOC(xWindow->shmseginfo, XShmSegmentInfo); + xWindow->shmseginfo->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0776); + xWindow->shmseginfo->shmaddr = shmat(xWindow->shmseginfo->shmid, 0, 0); + xWindow->shmseginfo->readOnly = False; + if (!XShmAttach (xWindow->display, xWindow->shmseginfo)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[X11] Failed to attach shared memory!\n")); + } + xWindow->pixmap = XShmCreatePixmap(xWindow->display, cur_wnd, + (unsigned char *) xWindow->shmseginfo->shmaddr, xWindow->shmseginfo, + VideoWidth, VideoHeight, xWindow->depth); + memset((unsigned char *) xWindow->shmseginfo->shmaddr, 0, sizeof(char)*size); + XSetWindowBackgroundPixmap (xWindow->display, cur_wnd, xWindow->pixmap); + xWindow->pwidth = VideoWidth; + xWindow->pheight = VideoHeight; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[X11] Using X11 Pixmap %08x\n", (u32)xWindow->pixmap)); + } else if (xWindow->use_shared_memory) { + GF_SAFEALLOC(xWindow->shmseginfo, XShmSegmentInfo); + xWindow->surface = XShmCreateImage (xWindow->display, xWindow->visual, + xWindow->depth, ZPixmap, NULL, xWindow->shmseginfo, + VideoWidth, VideoHeight); + xWindow->shmseginfo->shmid = shmget(IPC_PRIVATE, xWindow->surface->bytes_per_line * xWindow->surface->height, + IPC_CREAT | 0777); + + xWindow->surface->data = xWindow->shmseginfo->shmaddr = shmat(xWindow->shmseginfo->shmid, NULL, 0); + xWindow->shmseginfo->readOnly = False; + XShmAttach (xWindow->display, xWindow->shmseginfo); + } else +#endif + { + char *data = (char *) malloc(sizeof(char)*size); + xWindow->surface = XCreateImage (xWindow->display, xWindow->visual, + xWindow->depth, ZPixmap, + 0, + data, + VideoWidth, VideoHeight, + xWindow->bpp*8, xWindow->bpp*VideoWidth); + if (!xWindow->surface) { + free(data); + return GF_IO_ERR; + } + + } + xWindow->is_init = 1; + return GF_OK; +} + +/* + * resize buffer: note we don't resize window here + */ +GF_Err X11_ResizeBackBuffer (struct _video_out *vout, u32 newWidth, u32 newHeight) +{ + X11VID (); + u32 w = xWindow->surface ? xWindow->surface->width : xWindow->pwidth; + u32 h = xWindow->surface ? xWindow->surface->height : xWindow->pheight; + if (!xWindow->is_init || (newWidth != w) || (newHeight != h)) { + if ((newWidth >= 32) && (newHeight >= 32)) + return X11_InitBackBuffer (vout, newWidth, newHeight); + } + return GF_OK; +} + +GF_Err X11_ProcessEvent (struct _video_out * vout, GF_Event * evt) +{ + X11VID (); + + if (!xWindow->setup_done) X11_SetupWindow(vout); + + if (evt) { + + switch (evt->type) { + case GF_EVENT_SET_CURSOR: + break; + case GF_EVENT_SET_CAPTION: + if (!xWindow->par_wnd) XStoreName (xWindow->display, xWindow->wnd, evt->caption.caption); + break; + case GF_EVENT_SHOWHIDE: + break; + case GF_EVENT_SIZE: + /*if owning the window and not in fullscreen, resize it (initial scene size)*/ + xWindow->w_width = evt->size.width; + xWindow->w_height = evt->size.height; +#ifdef ENABLE_JOYSTICK + xWindow->prev_x = xWindow->w_width/2; + xWindow->prev_y = xWindow->w_height/2; +#endif + + if (!xWindow->fullscreen) { + if (xWindow->par_wnd) { + XWindowAttributes pwa; + XGetWindowAttributes(xWindow->display, xWindow->par_wnd, &pwa); + XMoveResizeWindow(xWindow->display, xWindow->wnd, pwa.x, pwa.y, evt->size.width, evt->size.height); + if (!xWindow->no_select_input) XSetInputFocus(xWindow->display, xWindow->wnd, RevertToNone, CurrentTime); + } else { + XResizeWindow (xWindow->display, xWindow->wnd, evt->size.width, evt->size.height); + } + } + break; + case GF_EVENT_VIDEO_SETUP: + switch (evt->setup.opengl_mode){ +#ifdef GPAC_HAS_OPENGL + case 1: + xWindow->output_3d_mode = 1; + return X11_SetupGL(vout); + case 2: + xWindow->output_3d_mode = 2; + return X11_SetupGLPixmap(vout, evt->setup.width, evt->setup.height); +#endif + case 0: + xWindow->output_3d_mode = 0; + return X11_ResizeBackBuffer(vout, evt->setup.width, evt->setup.height); + default: + return GF_NOT_SUPPORTED; + } + } + } else { + X11_HandleEvents(vout); + } + return GF_OK; + +} + +/* switch from/to full screen mode */ +GF_Err X11_SetFullScreen (struct _video_out * vout, u32 bFullScreenOn, u32 * screen_width, u32 * screen_height) +{ + X11VID (); + xWindow->fullscreen = bFullScreenOn; +#ifdef GPAC_HAS_OPENGL + if (xWindow->output_3d_mode==1) X11_ReleaseGL(xWindow); +#endif + + if (bFullScreenOn) { + xWindow->store_width = *screen_width; + xWindow->store_height = *screen_height; + + xWindow->w_width = vout->max_screen_width; + xWindow->w_height = vout->max_screen_height; + + XFreeGC (xWindow->display, xWindow->the_gc); + xWindow->the_gc = XCreateGC (xWindow->display, xWindow->full_wnd, 0, NULL); + + XMoveResizeWindow (xWindow->display, + (Window) xWindow->full_wnd, 0, 0, + vout->max_screen_width, + vout->max_screen_height); + *screen_width = xWindow->w_width; + *screen_height = xWindow->w_height; + XUnmapWindow (xWindow->display, xWindow->wnd); + XMapWindow (xWindow->display, xWindow->full_wnd); + XSetInputFocus(xWindow->display, xWindow->full_wnd, RevertToNone, CurrentTime); + XRaiseWindow(xWindow->display, xWindow->full_wnd); + XGrabKeyboard(xWindow->display, xWindow->full_wnd, True, GrabModeAsync, GrabModeAsync, CurrentTime); + } else { + *screen_width = xWindow->store_width; + *screen_height = xWindow->store_height; + XFreeGC (xWindow->display, xWindow->the_gc); + xWindow->the_gc = XCreateGC (xWindow->display, xWindow->wnd, 0, NULL); + XUnmapWindow (xWindow->display, xWindow->full_wnd); + XMapWindow (xWindow->display, xWindow->wnd); + XUngrabKeyboard(xWindow->display, CurrentTime); + if (xWindow->par_wnd) XSetInputFocus(xWindow->display, xWindow->wnd, RevertToNone, CurrentTime); + /*backbuffer resize will be done right after this is called */ + } +#ifdef GPAC_HAS_OPENGL + if (xWindow->output_3d_mode==1) X11_SetupGL(vout); +#endif + return GF_OK; +} + +/* + * lock video mem + */ +GF_Err X11_LockBackBuffer(struct _video_out * vout, GF_VideoSurface * vi, u32 do_lock) +{ + X11VID (); + + if (do_lock) { + if (!vi) return GF_BAD_PARAM; + if (xWindow->surface) { + vi->width = xWindow->surface->width; + vi->height = xWindow->surface->height; + vi->pitch = xWindow->surface->width*xWindow->bpp; + vi->pixel_format = xWindow->pixel_format; + vi->video_buffer = xWindow->surface->data; + } else { +#ifdef GPAC_HAS_X11_SHM + vi->width = xWindow->pwidth; + vi->height = xWindow->pheight; + vi->pitch = xWindow->pwidth*xWindow->bpp; + vi->pixel_format = xWindow->pixel_format; + vi->video_buffer = (unsigned char *) xWindow->shmseginfo->shmaddr; +#endif + } + vi->is_hardware_memory = (xWindow->use_shared_memory) ? 1 : 0; + return GF_OK; + } else { + return GF_OK; + } +} + + +static XErrorHandler old_handler = NULL; +static int selectinput_err = 0; +static int X11_BadAccess_ByPass(Display * display, + XErrorEvent * event) +{ + char msg[60]; + if (event->error_code == BadAccess) + { + selectinput_err = 1; + return 0; + } + if (old_handler != NULL) + old_handler(display, event); + else + { + XGetErrorText(display, event->error_code, (char *) &msg, 60); + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[X11] Error %s\n",msg)); + } + return 0; +} + + +static void X11_XScreenSaverState(XWindow *xwin, Bool turn_on) +{ + return; + if (turn_on) { + if (!xwin->ss_t) return; + XSetScreenSaver(xwin->display, xwin->ss_t, xwin->ss_i, xwin->ss_b, xwin->ss_e); + } else { + /* Save state information */ + XGetScreenSaver(xwin->display, &xwin->ss_t, &xwin->ss_i, &xwin->ss_b, &xwin->ss_e); + if (xwin->ss_t) + XSetScreenSaver(xwin->display, 0, xwin->ss_i, xwin->ss_b, xwin->ss_e); + } +} + +/* + * Setup X11 wnd System + */ +void +X11_SetupWindow (GF_VideoOutput * vout) +{ + X11VID (); + const char *sOpt; + + xWindow->display = XOpenDisplay (NULL); + xWindow->screennum = DefaultScreen (xWindow->display); + xWindow->screenptr = DefaultScreenOfDisplay (xWindow->display); + xWindow->visual = DefaultVisualOfScreen (xWindow->screenptr); + xWindow->depth = DefaultDepth (xWindow->display, xWindow->screennum); + + switch (xWindow->depth) { + case 8: + xWindow->pixel_format = GF_PIXEL_GREYSCALE; + break; + case 16: + xWindow->pixel_format = GF_PIXEL_RGB_565; + break; + case 24: + xWindow->pixel_format = GF_PIXEL_RGB_32; + break; + default: + xWindow->pixel_format = GF_PIXEL_GREYSCALE; + break; + } + xWindow->bpp = xWindow->depth / 8; + xWindow->bpp = xWindow->bpp == 3 ? 4 : xWindow->bpp; + +xWindow->screennum=0; + vout->max_screen_width = DisplayWidth(xWindow->display, xWindow->screennum); + vout->max_screen_height = DisplayHeight(xWindow->display, xWindow->screennum); + /* + * Full screen wnd + */ + xWindow->full_wnd = XCreateWindow (xWindow->display, + RootWindowOfScreen (xWindow->screenptr), + 0, 0, + vout->max_screen_width, + vout->max_screen_height, 0, + xWindow->depth, InputOutput, + xWindow->visual, 0, NULL); + + XSelectInput(xWindow->display, xWindow->full_wnd, + FocusChangeMask | ExposureMask | PointerMotionMask | ButtonReleaseMask | ButtonPressMask | KeyPressMask | KeyReleaseMask); + + if (!xWindow->par_wnd) { + xWindow->w_width = 320; + xWindow->w_height = 240; + xWindow->wnd = XCreateWindow (xWindow->display, + RootWindowOfScreen(xWindow->screenptr), 0, 0, + xWindow->w_width, xWindow->w_height, 0, + xWindow->depth, InputOutput, + xWindow->visual, 0, NULL); + XMapWindow (xWindow->display, (Window) xWindow->wnd); + } else { + XWindowAttributes pwa; + XGetWindowAttributes(xWindow->display, xWindow->par_wnd, &pwa); + xWindow->w_width = pwa.width; + xWindow->w_height = pwa.height; + xWindow->wnd = XCreateWindow (xWindow->display, xWindow->par_wnd, pwa.x, pwa.y, + xWindow->w_width, xWindow->w_height, 0, + xWindow->depth, InputOutput, + xWindow->visual, 0, NULL); + XMapWindow (xWindow->display, (Window) xWindow->wnd); + } + + XSync(xWindow->display, False); + XUnmapWindow (xWindow->display, (Window) xWindow->wnd); + XSync(xWindow->display, False); + old_handler = XSetErrorHandler(X11_BadAccess_ByPass); + selectinput_err = 0; + XSelectInput(xWindow->display, xWindow->wnd, + FocusChangeMask | StructureNotifyMask | PropertyChangeMask | ExposureMask | + PointerMotionMask | ButtonReleaseMask | ButtonPressMask | + KeyPressMask | KeyReleaseMask); + XSync(xWindow->display, False); + XSetErrorHandler(old_handler); + if (selectinput_err) { + XSelectInput(xWindow->display, xWindow->wnd, + StructureNotifyMask | PropertyChangeMask | ExposureMask | + KeyPressMask | KeyReleaseMask); + + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[X11] Cannot select input focus\n")); + } + XSync(xWindow->display, False); + XMapWindow (xWindow->display, (Window) xWindow->wnd); + + XSizeHints *Hints = XAllocSizeHints (); + Hints->flags = PSize | PMinSize; + Hints->min_width = 64; + Hints->min_height = 64; + Hints->max_height = 4096; + Hints->max_width = 4096; + if (!xWindow->par_wnd) { + XSetWMNormalHints (xWindow->display, xWindow->wnd, Hints); + XStoreName (xWindow->display, xWindow->wnd, "GPAC X11 Output"); + } + Hints->x = 0; + Hints->y = 0; + Hints->flags |= USPosition; + XSetWMNormalHints (xWindow->display, xWindow->full_wnd, Hints); + + xWindow->the_gc = XCreateGC (xWindow->display, xWindow->wnd, 0, NULL); + xWindow->use_shared_memory = 0; + +#ifdef GPAC_HAS_X11_SHM + sOpt = gf_modules_get_option((GF_BaseInterface *)vout, "Video", "UseHardwareMemory"); + if (sOpt && !strcmp(sOpt, "yes")) { + int XShmMajor, XShmMinor; + Bool XShmPixmaps; + if (XShmQueryVersion(xWindow->display, &XShmMajor, &XShmMinor, &XShmPixmaps)) { + xWindow->use_shared_memory = 1; + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[X11] Using X11 Shared Memory\n")); + if ((XShmPixmaps==True) && (XShmPixmapFormat(xWindow->display)==ZPixmap)) { + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[X11] X11 Shared Memory Pixmaps available\n")); + } + } + } + +#endif + +#ifdef GPAC_HAS_X11_XV + sOpt = gf_modules_get_option((GF_BaseInterface *)vout, "Video", "DisableColorKeying"); + if (sOpt && !strcmp(sOpt, "yes")) { + xWindow->xvport = X11_GetXVideoPort(vout, GF_PIXEL_I420, 0); + } else { + xWindow->xvport = X11_GetXVideoPort(vout, GF_PIXEL_I420, 1); + if (xWindow->xvport<0) { + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[X11] Hardware has no color keying\n")); + vout->overlay_color_key = 0; + xWindow->xvport = X11_GetXVideoPort(vout, GF_PIXEL_I420, 0); + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[X11] Hardware uses color key %08x\n", vout->overlay_color_key)); + } + } + if (xWindow->xvport>=0) { + XvUngrabPort(xWindow->display, xWindow->xvport, CurrentTime ); + xWindow->xvport = -1; + vout->yuv_pixel_format = X11_GetPixelFormat(xWindow->xv_pf_format); + vout->Blit = X11_Blit; + vout->hw_caps |= GF_VIDEO_HW_HAS_YUV_OVERLAY; + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[X11] Using XV YUV Overlays\n")); + +#ifdef GPAC_HAS_X11_SHM + /*if user asked for YUV->RGB on offscreen, do it (it may crash the system)*/ + sOpt = gf_modules_get_option((GF_BaseInterface *)vout, "Video", "EnableOffscreenYUV"); + if (sOpt && !strcmp(sOpt, "yes")) { + vout->hw_caps |= GF_VIDEO_HW_HAS_YUV; + GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[X11] Using XV Offscreen YUV2RGB acceleration\n")); + } +#endif + } +#endif + + XSetWindowAttributes xsw; + xsw.border_pixel = WhitePixel (xWindow->display, xWindow->screennum); + xsw.background_pixel = BlackPixel (xWindow->display, xWindow->screennum); + xsw.win_gravity = NorthWestGravity; + XChangeWindowAttributes (xWindow->display, xWindow->wnd, CWBackPixel | CWWinGravity, &xsw); + + xsw.override_redirect = True; + XChangeWindowAttributes(xWindow->display, xWindow->full_wnd, + CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWWinGravity, &xsw); + + if (!xWindow->par_wnd) { + xWindow->WM_DELETE_WINDOW = XInternAtom (xWindow->display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(xWindow->display, xWindow->wnd, &xWindow->WM_DELETE_WINDOW, 1); + } + + { + XEvent ev; + long mask; + + memset (&ev, 0, sizeof (ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = RootWindowOfScreen (xWindow->screenptr); + ev.xclient.message_type = XInternAtom (xWindow->display, "KWM_KEEP_ON_TOP", False); + ev.xclient.format = 32; + ev.xclient.data.l[0] = xWindow->full_wnd; + ev.xclient.data.l[1] = CurrentTime; + mask = SubstructureRedirectMask; + XSendEvent (xWindow->display,RootWindowOfScreen (xWindow->screenptr), False, + mask, &ev); + } + + /*openGL setup*/ +#ifdef GPAC_HAS_OPENGL + { + int attribs[64]; + int i, nb_bits; + + sOpt = gf_modules_get_option((GF_BaseInterface *)vout, "Video", "GLNbBitsPerComponent"); + nb_bits = sOpt ? atoi(sOpt) : 5; + i=0; + attribs[i++] = GLX_RGBA; + attribs[i++] = GLX_RED_SIZE; + attribs[i++] = nb_bits; + attribs[i++] = GLX_GREEN_SIZE; + attribs[i++] = nb_bits; + attribs[i++] = GLX_BLUE_SIZE; + attribs[i++] = nb_bits; + sOpt = gf_modules_get_option((GF_BaseInterface *)vout, "Video", "GLNbBitsDepth"); + nb_bits = sOpt ? atoi(sOpt) : 0; + if (nb_bits) { + attribs[i++] = GLX_DEPTH_SIZE; + attribs[i++] = nb_bits; + } + sOpt = gf_modules_get_option((GF_BaseInterface *)vout, "Video", "UseGLDoubleBuffering"); + if (!sOpt || !strcmp(sOpt, "yes")) attribs[i++] = GLX_DOUBLEBUFFER; + attribs[i++] = None; + xWindow->glx_visualinfo = glXChooseVisual(xWindow->display, xWindow->screennum, attribs); + if (!xWindow->glx_visualinfo) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[X11] Error selecting GL display\n")); + } + } + + xWindow->gl_wnd = XCreateWindow (xWindow->display, RootWindowOfScreen (xWindow->screenptr), + 0, + 0, + 200, + 200, 0, + xWindow->depth, InputOutput, + xWindow->visual, 0, NULL); + + XSync(xWindow->display, False); + XUnmapWindow(xWindow->display, (Window) xWindow->gl_wnd); + XSync(xWindow->display, False); + + sOpt = gf_modules_get_option((GF_BaseInterface *)vout, "Video", "X113DOffscreenMode"); + if (sOpt && !strcmp(sOpt, "Window")) { + xWindow->offscreen_type = 1; + } else if (sOpt && !strcmp(sOpt, "VisibleWindow")) { + xWindow->offscreen_type = 2; + XSetWMNormalHints (xWindow->display, xWindow->gl_wnd, Hints); + } else if (sOpt && !strcmp(sOpt, "Pixmap")) { + xWindow->offscreen_type = 0; + } else { + xWindow->offscreen_type = 0; + } +#endif + + + /*turn off xscreensaver*/ + X11_XScreenSaverState(xWindow, 0); + + xWindow->setup_done = 1; + XFree (Hints); +} + +GF_Err X11_Setup(struct _video_out *vout, void *os_handle, void *os_display, u32 no_proc_override) +{ + X11VID (); + /*assign window if any, NEVER display*/ + xWindow->par_wnd = (Window) os_handle; + + /*OSMOZILLA HACK*/ + if (os_display) xWindow->no_select_input = 1; + +#ifdef ENABLE_JOYSTICK + xWindow->fd = open ("/dev/input/js0", O_NONBLOCK); +#endif + /*the rest is done THROUGH THE MAIN RENDERER TRHEAD!!*/ + return GF_OK; +} + +/* Shutdown X11 */ +void X11_Shutdown (struct _video_out *vout) +{ + X11VID (); + + if (xWindow->output_3d_mode==1) { +#ifdef GPAC_HAS_OPENGL + X11_ReleaseGL(xWindow); +#endif + } else { + X11_ReleaseBackBuffer (vout); + } +#ifdef ENABLE_JOYSTICK + close(xWindow->fd); +#endif + XFreeGC (xWindow->display, xWindow->the_gc); + XUnmapWindow (xWindow->display, (Window) xWindow->wnd); + XDestroyWindow (xWindow->display, (Window) xWindow->wnd); + XDestroyWindow (xWindow->display, (Window) xWindow->full_wnd); +#ifdef GPAC_HAS_OPENGL + if (xWindow->gl_offscreen) glXDestroyGLXPixmap(xWindow->display, xWindow->gl_offscreen); + if (xWindow->gl_pixmap) XFreePixmap(xWindow->display, xWindow->gl_pixmap); + XUnmapWindow (xWindow->display, (Window) xWindow->gl_wnd); + XDestroyWindow (xWindow->display, (Window) xWindow->gl_wnd); +#endif + + /*restore xscreen saver*/ + X11_XScreenSaverState(xWindow, 1); + + XCloseDisplay (xWindow->display); + free (xWindow); +} + + + + +void *NewX11VideoOutput () +{ + GF_VideoOutput *driv; + XWindow *xWindow; + GF_SAFEALLOC(driv, GF_VideoOutput); + if (!driv) return NULL; + GF_SAFEALLOC(xWindow, XWindow); + if (!xWindow) { + free(driv); + return NULL; + } + GF_REGISTER_MODULE_INTERFACE(driv, GF_VIDEO_OUTPUT_INTERFACE, "X11 Video Output", "gpac distribution") + + driv->opaque = xWindow; + + driv->Flush = X11_Flush; + driv->SetFullScreen = X11_SetFullScreen; + driv->Setup = X11_Setup; + driv->Shutdown = X11_Shutdown; + driv->LockBackBuffer = X11_LockBackBuffer; + driv->ProcessEvent = X11_ProcessEvent; + driv->hw_caps = GF_VIDEO_HW_OPENGL; + /*fixme - needs a better detection scheme*/ + driv->hw_caps |= GF_VIDEO_HW_OPENGL_OFFSCREEN | GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA; + return (void *) driv; + +} + + +void +DeleteX11VideoOutput (GF_VideoOutput * vout) +{ + free (vout); +} + +/* + * interface query + */ +Bool +QueryInterface (u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) + return 1; + return 0; +} + + +/* + * interface create + */ +GF_BaseInterface * +LoadInterface (u32 InterfaceType) +{ + if (InterfaceType == GF_VIDEO_OUTPUT_INTERFACE) + return (GF_BaseInterface *) NewX11VideoOutput (); + return NULL; +} + + +/* + * interface destroy + */ +void +ShutdownInterface (GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) + { + case GF_VIDEO_OUTPUT_INTERFACE: + DeleteX11VideoOutput ((GF_VideoOutput *)ifce); + break; + } +} + diff --git a/modules/x11_out/x11_out.h b/modules/x11_out/x11_out.h new file mode 100644 index 0000000..5bd605a --- /dev/null +++ b/modules/x11_out/x11_out.h @@ -0,0 +1,131 @@ +/* + * GPAC Multimedia Framework + * + * Authors: DINH Anh Min - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / X11 video output module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef _X11_OUT_H +#define _X11_OUT_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#include <gpac/modules/video_out.h> +#include <gpac/thread.h> +#include <gpac/list.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> + +#if !defined(GPAC_DISABLE_3D) && !defined(GPAC_USE_OGL_ES) && !defined(GPAC_USE_TINYGL) +#define GPAC_HAS_OPENGL +#endif + +#ifdef GPAC_HAS_OPENGL +#include <GL/glx.h> +#endif + +#ifdef GPAC_HAS_X11_SHM +#include <X11/extensions/XShm.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#endif + +#ifdef GPAC_HAS_X11_XV +#include <X11/extensions/Xv.h> +#include <X11/extensions/Xvlib.h> +#endif + +#ifdef ENABLE_JOYSTICK +#include <linux/joystick.h> +#include <unistd.h> +#include <fcntl.h> +#endif + +#define X11VID() XWindow *xWindow = (XWindow *)vout->opaque; + +#define RGB555(r,g,b) (((r&248)<<7) + ((g&248)<<2) + (b>>3)) +#define RGB565(r,g,b) (((r&248)<<8) + ((g&252)<<3) + (b>>3)) + +typedef struct +{ + Window par_wnd; //main window handler passed to module, NULL otherwise + Bool setup_done, no_select_input; //setup is done + Display *display; //required by all X11 method, provide by XOpenDisplay, Mozilla wnd ... + Window wnd; //window handler created by module + Window full_wnd; //full screen + Screen *screenptr; //X11 stuff + int screennum; //... + Visual *visual; //... + GC the_gc; //graphics context + XImage *surface; //main drawing image: software mode + Pixmap pixmap; + u32 pwidth, pheight; + Atom WM_DELETE_WINDOW; //window deletion + + Bool use_shared_memory; // + /*screensaver state*/ + int ss_t, ss_b, ss_i, ss_e; + +#ifdef GPAC_HAS_X11_SHM + XShmSegmentInfo *shmseginfo; +#endif + + /*YUV overlay*/ +#ifdef GPAC_HAS_X11_XV + int xvport; + u32 xv_pf_format; + XvImage *overlay; +#endif + + Bool is_init, fullscreen, has_focus; + + /*backbuffer size before entering fullscreen mode (used for restore) */ + u32 store_width, store_height; + + u32 w_width, w_height; + u32 depth, bpp, pixel_format; + u32 output_3d_mode; + +#ifdef GPAC_HAS_OPENGL + XVisualInfo *glx_visualinfo; + GLXContext glx_context; + Pixmap gl_pixmap; + GLXPixmap gl_offscreen; + Window gl_wnd; + u32 offscreen_type; +#endif +#ifdef ENABLE_JOYSTICK + /*joystick device file descriptor*/ + u32 fd; + s32 prev_x, prev_y; +#endif +} XWindow; + +void StretchBits (void *dst, u32 dst_bpp, u32 dst_w, u32 dst_h, u32 dst_pitch, + void *src, u32 src_bpp, u32 src_w, u32 src_h, u32 src_pitch, Bool FlipIt); + + +#endif /* _X11_OUT_H */ diff --git a/modules/xvid_dec/Makefile b/modules/xvid_dec/Makefile new file mode 100644 index 0000000..dae8564 --- /dev/null +++ b/modules/xvid_dec/Makefile @@ -0,0 +1,69 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/xvid_dec + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS=xvid_dec.o + +#local xvid lib +ifeq ($(CONFIG_XVID), local) +CFLAGS+= -I$(LOCAL_INC_PATH)/xvid +EXTRALIBS+= -L../../extra_lib/lib/gcc +endif +EXTRALIBS+= -lxvidcore + +SRCS := $(OBJS:.o=.c) + +LIB=gm_xvid_dec.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +LDFLAGS+=-export-symbols xvid_dec.def +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +all: $(LIB) + + +$(LIB): $(OBJSPIC) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJSPIC) -L../../bin/gcc -lgpac $(EXTRALIBS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/xvid_dec/xvid_dec.c b/modules/xvid_dec/xvid_dec.c new file mode 100644 index 0000000..43278d3 --- /dev/null +++ b/modules/xvid_dec/xvid_dec.c @@ -0,0 +1,483 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / codec pack module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <gpac/modules/codec.h> +#include <gpac/avparse.h> +#include <gpac/constants.h> + +#include "xvid.h" + +#ifndef XVID_DEC_FRAME +#define XVID_DEC_FRAME xvid_dec_frame_t +#define XVID_DEC_PARAM xvid_dec_create_t +#else +#define XVID_USE_OLD_API +#endif + +#undef XVID_USE_OLD_API + +static Bool xvid_is_init = 0; + +typedef struct +{ + void *base_codec; + u16 base_ES_ID; + + u32 width, height, out_size, pixel_ar; + Bool first_frame; + s32 base_filters; + Float FPS; + u32 offset; + + /*demo for depth coding (auxiliary video data): the codec is pseudo-scalable with the depth data carried in + another stream - this is not very elegant ...*/ + void *depth_codec; + u16 depth_ES_ID; + char *temp_uv; + u32 yuv_size; +} XVIDDec; + +#define XVIDCTX() XVIDDec *ctx = (XVIDDec *) ifcg->privateStack + + +static GF_Err XVID_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + GF_M4VDecSpecInfo dsi; + GF_Err e; + void **codec; +#ifdef XVID_USE_OLD_API + XVID_DEC_FRAME frame; + XVID_DEC_PARAM par; +#else + xvid_dec_frame_t frame; + xvid_dec_create_t par; +#endif + + XVIDCTX(); + + if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->data) return GF_NON_COMPLIANT_BITSTREAM; + + /*locate any auxiliary video data descriptor on this stream*/ + if (esd->dependsOnESID) { + u32 i = 0; + GF_Descriptor *d = NULL; + if (esd->dependsOnESID != ctx->base_ES_ID) return GF_NOT_SUPPORTED; +#ifdef XVID_USE_OLD_API + return GF_NOT_SUPPORTED; +#endif + + while ((d = gf_list_enum(esd->extensionDescriptors, &i))) { + if (d->tag == GF_ODF_AUX_VIDEO_DATA) break; + } + if (!d) return GF_NOT_SUPPORTED; + codec = &ctx->depth_codec; + ctx->depth_ES_ID = esd->ESID; + } else { + if (ctx->base_ES_ID && ctx->base_ES_ID!=esd->ESID) return GF_NOT_SUPPORTED; + codec = &ctx->base_codec; + ctx->base_ES_ID = esd->ESID; + } + if (*codec) xvid_decore(*codec, XVID_DEC_DESTROY, NULL, NULL); + + /*decode DSI*/ + e = gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + if (e) return e; + if (!dsi.width || !dsi.height) return GF_NON_COMPLIANT_BITSTREAM; + + memset(&par, 0, sizeof(par)); + par.width = dsi.width; + par.height = dsi.height; + /*note that this may be irrelevant when used through systems (FPS is driven by systems CTS)*/ + ctx->FPS = dsi.clock_rate; + ctx->FPS /= 1000; + if (!ctx->FPS) ctx->FPS = 30.0f; + ctx->pixel_ar = (dsi.par_num<<16) | dsi.par_den; + +#ifndef XVID_USE_OLD_API + par.version = XVID_VERSION; +#endif + + if (xvid_decore(NULL, XVID_DEC_CREATE, &par, NULL) < 0) return GF_NON_COMPLIANT_BITSTREAM; + + ctx->width = par.width; + ctx->height = par.height; + *codec = par.handle; + + /*init decoder*/ + memset(&frame, 0, sizeof(frame)); + frame.bitstream = (void *) esd->decoderConfig->decoderSpecificInfo->data; + frame.length = esd->decoderConfig->decoderSpecificInfo->dataLength; +#ifndef XVID_USE_OLD_API + frame.version = XVID_VERSION; + xvid_decore(*codec, XVID_DEC_DECODE, &frame, NULL); +#else + /*don't perform error check, XviD doesn't like DSI only frame ...*/ + xvid_decore(*codec, XVID_DEC_DECODE, &frame, NULL); +#endif + + ctx->first_frame = 1; + /*output in YV12 only - let the player handle conversion*/ + if (ctx->depth_codec) { + ctx->out_size = ctx->width * ctx->height * 5 / 2; + ctx->temp_uv = malloc(sizeof(char)*ctx->width * ctx->height / 2); + } else { + ctx->yuv_size = ctx->out_size = ctx->width * ctx->height * 3 / 2; + } + return GF_OK; +} +static GF_Err XVID_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + XVIDCTX(); + if (ctx->base_ES_ID == ES_ID) { + if (ctx->base_codec) xvid_decore(ctx->base_codec, XVID_DEC_DESTROY, NULL, NULL); + ctx->base_codec = NULL; + ctx->base_ES_ID = 0; + ctx->width = ctx->height = ctx->out_size = 0; + } + else if (ctx->depth_ES_ID == ES_ID) { + if (ctx->depth_codec) xvid_decore(ctx->depth_codec, XVID_DEC_DESTROY, NULL, NULL); + ctx->depth_codec = NULL; + ctx->depth_ES_ID = 0; + if (ctx->temp_uv) free(ctx->temp_uv); + ctx->temp_uv = NULL; + } + return GF_OK; +} +static GF_Err XVID_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + XVIDCTX(); + + switch (capability->CapCode) { + case GF_CODEC_RESILIENT: + capability->cap.valueInt = 1; + break; + case GF_CODEC_WIDTH: + capability->cap.valueInt = ctx->width; + break; + case GF_CODEC_HEIGHT: + capability->cap.valueInt = ctx->height; + break; + case GF_CODEC_STRIDE: + capability->cap.valueInt = ctx->width; + break; + case GF_CODEC_FPS: + capability->cap.valueFloat = ctx->FPS; + break; + case GF_CODEC_PAR: + capability->cap.valueInt = ctx->pixel_ar; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_PIXEL_FORMAT: + capability->cap.valueInt = ctx->depth_codec ? GF_PIXEL_YUVA : GF_PIXEL_YV12; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = 1; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = 4; + break; + /*by default we use 4 bytes padding (otherwise it happens that XviD crashes on some videos...)*/ + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 32; + break; + /*XviD performs frame reordering internally*/ + case GF_CODEC_REORDER: + capability->cap.valueInt = 1; + break; + case GF_CODEC_WANTS_THREAD: + { + const char *sOpt = gf_modules_get_option((GF_BaseInterface *)ifcg, "XviD", "Threaded"); + capability->cap.valueInt = (sOpt && stricmp(sOpt, "yes")) ? 1 : 0; + } + break; + /*not known at our level...*/ + case GF_CODEC_CU_DURATION: + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} +static GF_Err XVID_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like color space changing ...) */ + return GF_NOT_SUPPORTED; +} +static GF_Err XVID_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ +#ifdef XVID_USE_OLD_API + XVID_DEC_FRAME frame; +#else + xvid_dec_frame_t frame; +#endif + void *codec; + s32 postproc, res; + XVIDCTX(); + + if (!ES_ID) { + *outBufferLength = 0; + return GF_OK; + } + + if (ES_ID == ctx->depth_ES_ID) { + codec = ctx->depth_codec; + } else { + codec = ctx->base_codec; + } + if (!codec) return GF_OK; + + if (*outBufferLength < ctx->out_size) { + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; + } + + memset(&frame, 0, sizeof(frame)); + frame.bitstream = (void *) (inBuffer + ctx->offset); + frame.length = inBufferLength - ctx->offset; + ctx->offset = 0; + + +#ifdef XVID_USE_OLD_API + frame.colorspace = XVID_CSP_I420; + frame.stride = ctx->width; + frame.image = (void *) outBuffer; +#else + frame.version = XVID_VERSION; + if (ES_ID == ctx->depth_ES_ID) { + frame.output.csp = XVID_CSP_PLANAR; + frame.output.stride[0] = ctx->width; + frame.output.plane[0] = (void *) (outBuffer + ctx->yuv_size); + frame.output.stride[1] = ctx->width/4; + frame.output.plane[1] = (void *) ctx->temp_uv; + frame.output.stride[2] = ctx->width/4; + frame.output.plane[2] = (void *) ctx->temp_uv; + } else { + frame.output.csp = XVID_CSP_I420; + frame.output.stride[0] = ctx->width; + frame.output.plane[0] = (void *) outBuffer; + } +#endif + + + + /*to check, not convinced yet by results...*/ + postproc = ctx->base_filters; + + switch (mmlevel) { + case GF_CODEC_LEVEL_SEEK: + case GF_CODEC_LEVEL_DROP: + /*turn off all post-proc*/ +#ifdef XVID_USE_OLD_API + postproc &= ~XVID_DEC_DEBLOCKY; + postproc &= ~XVID_DEC_DEBLOCKUV; +#else + postproc &= ~XVID_DEBLOCKY; + postproc &= ~XVID_DEBLOCKUV; + postproc &= ~XVID_FILMEFFECT; +#endif + break; + case GF_CODEC_LEVEL_VERY_LATE: + /*turn off post-proc*/ +#ifdef XVID_USE_OLD_API + postproc &= ~XVID_DEC_DEBLOCKY; +#else + postproc &= ~XVID_FILMEFFECT; + postproc &= ~XVID_DEBLOCKY; +#endif + break; + case GF_CODEC_LEVEL_LATE: +#ifdef XVID_USE_OLD_API + postproc &= ~XVID_DEC_DEBLOCKUV; +#else + postproc &= ~XVID_DEBLOCKUV; + postproc &= ~XVID_FILMEFFECT; +#endif + break; + } + postproc = 0; + + /*xvid may keep the first I frame and force a 1-frame delay, so we simply trick it*/ + if (ctx->first_frame) { outBuffer[0] = 'v'; outBuffer[1] = 'o'; outBuffer[2] = 'i'; outBuffer[3] = 'd'; } + + res = xvid_decore(codec, XVID_DEC_DECODE, &frame, NULL); + if (res < 0) { + *outBufferLength = 0; + return GF_NON_COMPLIANT_BITSTREAM; + } + if (0 && res <= 6) { + *outBufferLength = 0; + return GF_OK; + } + + /*dispatch nothing if seeking or droping*/ + switch (mmlevel) { + case GF_CODEC_LEVEL_SEEK: + case GF_CODEC_LEVEL_DROP: + if (ES_ID == ctx->base_ES_ID) + *outBufferLength = 0; + break; + default: + *outBufferLength = ctx->out_size; + if (ctx->first_frame) { + ctx->first_frame = 0; + if ((outBuffer[0] == 'v') && (outBuffer[1] == 'o') && (outBuffer[2] == 'i') && (outBuffer[3] == 'd')) { + *outBufferLength = 0; + return GF_OK; + } + } + if (res + 6 < frame.length) { + ctx->offset = res; + return GF_PACKED_FRAMES; + } + break; + } + + return GF_OK; +} + +static Bool XVID_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + if (StreamType != GF_STREAM_VISUAL) return 0; + switch (ObjectType) { + case 0x20: + return 1; + /*cap query*/ + case 0: + return 1; + } + return 0; +} + +static const char *XVID_GetCodecName(GF_BaseDecoder *dec) +{ +#ifdef XVID_USE_OLD_API + return "XviD Dev Version"; +#else + return "XviD 1.0"; +#endif +} + +GF_BaseDecoder *NewXVIDDec() +{ + const char *sOpt; + GF_MediaDecoder *ifcd; + XVIDDec *dec; + + GF_SAFEALLOC(ifcd, GF_MediaDecoder); + GF_SAFEALLOC(dec, XVIDDec); + GF_REGISTER_MODULE_INTERFACE(ifcd, GF_MEDIA_DECODER_INTERFACE, "XviD Decoder", "gpac distribution") + + ifcd->privateStack = dec; + + if (!xvid_is_init) { +#ifdef XVID_USE_OLD_API + XVID_INIT_PARAM init; + init.api_version = 0; + init.core_build = 0; + /*get info*/ + init.cpu_flags = XVID_CPU_CHKONLY; + xvid_init(NULL, 0, &init, NULL); + /*then init*/ + xvid_init(NULL, 0, &init, NULL); +#else + xvid_gbl_init_t init; + init.debug = 0; + init.version = XVID_VERSION; + init.cpu_flags = 0; /*autodetect*/ + xvid_global(NULL, 0, &init, NULL); +#endif + xvid_is_init = 1; + } + + /*get config*/ + dec->base_filters = 0; + sOpt = gf_modules_get_option((GF_BaseInterface *)ifcd, "XviD", "PostProc"); + if (sOpt) { +#ifndef XVID_USE_OLD_API + if (strstr(sOpt, "FilmEffect")) dec->base_filters |= XVID_FILMEFFECT; +#endif + if (strstr(sOpt, "Deblock_Y")) { +#ifdef XVID_USE_OLD_API + dec->base_filters |= XVID_DEC_DEBLOCKY; +#else + dec->base_filters |= XVID_DEBLOCKY; +#endif + } + if (strstr(sOpt, "Deblock_UV")) { +#ifdef XVID_USE_OLD_API + dec->base_filters |= XVID_DEC_DEBLOCKUV; +#else + dec->base_filters |= XVID_DEBLOCKUV; +#endif + } + } + + /*setup our own interface*/ + ifcd->AttachStream = XVID_AttachStream; + ifcd->DetachStream = XVID_DetachStream; + ifcd->GetCapabilities = XVID_GetCapabilities; + ifcd->SetCapabilities = XVID_SetCapabilities; + ifcd->GetName = XVID_GetCodecName; + ifcd->CanHandleStream = XVID_CanHandleStream; + ifcd->ProcessData = XVID_ProcessData; + return (GF_BaseDecoder *) ifcd; +} + +void DeleteXVIDDec(GF_BaseDecoder *ifcg) +{ + XVIDCTX(); + if (ctx->base_codec) xvid_decore(ctx->base_codec, XVID_DEC_DESTROY, NULL, NULL); + if (ctx->depth_codec) xvid_decore(ctx->depth_codec, XVID_DEC_DESTROY, NULL, NULL); + free(ctx); + free(ifcg); +} + + +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return 1; + return 0; +} + +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return (GF_BaseInterface *)NewXVIDDec(); + return NULL; +} + +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: + DeleteXVIDDec((GF_BaseDecoder*)ifce); + break; + } +} diff --git a/modules/xvid_dec/xvid_dec.def b/modules/xvid_dec/xvid_dec.def new file mode 100644 index 0000000..951d50e --- /dev/null +++ b/modules/xvid_dec/xvid_dec.def @@ -0,0 +1,6 @@ +LIBRARY gm_xvid_dec.dll + +EXPORTS + QueryInterface + LoadInterface + ShutdownInterface diff --git a/modules/xvid_dec/xvid_dec_wce.cpp b/modules/xvid_dec/xvid_dec_wce.cpp new file mode 100644 index 0000000..f6baab0 --- /dev/null +++ b/modules/xvid_dec/xvid_dec_wce.cpp @@ -0,0 +1,299 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / codec pack module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include <gpac/modules/codec.h> +#include <gpac/avparse.h> +#include <gpac/constants.h> + +#include "xvid_wce/xvid.h" + +typedef struct +{ + void *codec; + /*no support for scalability in XVID yet*/ + u16 ES_ID; + u32 width, height, out_size, pixel_ar; + u32 cb_size, cb_trig; + Bool first_frame; + s32 base_filters; + Float FPS; +} XVIDDec; + +#define XVIDCTX() XVIDDec *ctx = (XVIDDec *) (ifcg->privateStack) + + +static GF_Err XVID_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) +{ + GF_M4VDecSpecInfo dsi; + GF_Err e; + unsigned char *ptr; + unsigned long pitch; + + XVIDCTX(); + + if (ctx->ES_ID && ctx->ES_ID!=esd->ESID) return GF_NOT_SUPPORTED; + if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->data) return GF_NON_COMPLIANT_BITSTREAM; + + /*decode DSI*/ + e = gf_m4v_get_config((char *) esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + if (e) return e; + if (!dsi.width || !dsi.height) return GF_NON_COMPLIANT_BITSTREAM; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[XviD] Attaching Stream %d - framesize %d x %d\n", esd->ESID, dsi.width, dsi.height )); + + ctx->codec = InitCodec(dsi.width, dsi.height, GF_4CC('x', 'v', 'i', 'd')); + if (!ctx->codec) return GF_OUT_OF_MEM; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[XviD] Decoding DecoderSpecificInfo\n")); + + DecodeFrame(ctx->codec, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, ptr, ptr, ptr, pitch); + + /*note that this may be irrelevant when used through systems (FPS is driven by systems CTS)*/ + ctx->FPS = dsi.clock_rate; + ctx->FPS /= 1000; + if (!ctx->FPS) ctx->FPS = 30.0f; + ctx->width = dsi.width; + ctx->height = dsi.height; + ctx->pixel_ar = (dsi.par_num<<16) | dsi.par_den; + ctx->pixel_ar = 0; + ctx->ES_ID = esd->ESID; + ctx->first_frame = 1; + /*output in YV12 only - let the player handle conversion*/ + ctx->out_size = 3 * ctx->width * ctx->height / 2; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[XviD] Decoder setup - output size %d\n", ctx->out_size )); + return GF_OK; +} +static GF_Err XVID_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) +{ + XVIDCTX(); + if (ctx->ES_ID != ES_ID) return GF_BAD_PARAM; + if (ctx->codec) CloseCodec(ctx->codec); + ctx->codec = NULL; + ctx->ES_ID = 0; + ctx->width = ctx->height = ctx->out_size = 0; + return GF_OK; +} +static GF_Err XVID_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) +{ + XVIDCTX(); + + switch (capability->CapCode) { + case GF_CODEC_WIDTH: + capability->cap.valueInt = ctx->width; + break; + case GF_CODEC_HEIGHT: + capability->cap.valueInt = ctx->height; + break; + case GF_CODEC_STRIDE: + capability->cap.valueInt = ctx->width; + break; + case GF_CODEC_FPS: + capability->cap.valueFloat = ctx->FPS; + break; + case GF_CODEC_PAR: + capability->cap.valueInt = ctx->pixel_ar; + break; + case GF_CODEC_OUTPUT_SIZE: + capability->cap.valueInt = ctx->out_size; + break; + case GF_CODEC_PIXEL_FORMAT: + capability->cap.valueInt = GF_PIXEL_YV12; + break; + case GF_CODEC_BUFFER_MIN: + capability->cap.valueInt = ctx->cb_trig; + break; + case GF_CODEC_BUFFER_MAX: + capability->cap.valueInt = ctx->cb_size; + break; + /*by default we use 4 bytes padding (otherwise it happens that XviD crashes on some videos...)*/ + case GF_CODEC_PADDING_BYTES: + capability->cap.valueInt = 32; + break; + /*XviD performs frame reordering internally*/ + case GF_CODEC_REORDER: + capability->cap.valueInt = 1; + break; + case GF_CODEC_WANTS_THREAD: + { + const char *sOpt = gf_modules_get_option((GF_BaseInterface *)ifcg, "XviD", "Threaded"); + capability->cap.valueInt = (sOpt && stricmp(sOpt, "yes")) ? 1 : 0; + } + break; + /*not known at our level...*/ + case GF_CODEC_CU_DURATION: + default: + capability->cap.valueInt = 0; + break; + } + return GF_OK; +} +static GF_Err XVID_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capability) +{ + /*return unsupported to avoid confusion by the player (like color space changing ...) */ + return GF_NOT_SUPPORTED; +} +static GF_Err XVID_ProcessData(GF_MediaDecoder *ifcg, + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) +{ + unsigned char *pY, *pU, *pV; + u32 i, uv_w, half_h; + unsigned long pitch; + XVIDCTX(); + + /*check not using scalabilty*/ + if (ES_ID != ctx->ES_ID) return GF_BAD_PARAM; + + if (*outBufferLength < ctx->out_size) { + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; + } + + if (!DecodeFrame(ctx->codec, inBuffer, inBufferLength, pY, pU, pV, pitch)) { + *outBufferLength = 0; + return GF_NON_COMPLIANT_BITSTREAM; + } + + /*dispatch nothing if seeking or droping*/ + switch (mmlevel) { + case GF_CODEC_LEVEL_SEEK: + case GF_CODEC_LEVEL_DROP: + *outBufferLength = 0; + return GF_OK; + default: + break; + } + *outBufferLength = ctx->out_size; + for (i=0; i<ctx->height; i++) { + unsigned char *src = pY + pitch*i; + char *dst = outBuffer + ctx->width*i; + memcpy(dst, src, sizeof(char) * ctx->width); + } + outBuffer += ctx->width * ctx->height; + half_h = ctx->height/2; + uv_w = ctx->width/2; + for (i=0; i<half_h; i++) { + unsigned char *src = pU + pitch/2*i; + char *dst = outBuffer + i*uv_w; + memcpy(dst, src, sizeof(char) * uv_w); + } + outBuffer += ctx->width * ctx->height / 4; + for (i=0; i<half_h; i++) { + unsigned char *src = pV + pitch/2*i; + char *dst = outBuffer + i*uv_w; + memcpy(dst, src, sizeof(char) * uv_w); + } + + return GF_OK; +} + +static const char *XVID_GetCodecName(GF_BaseDecoder *dec) +{ + return "XviD 1.0 for WinCE"; +} + +static Bool XVID_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, u32 ObjectType, char *decSpecInfo, u32 decSpecInfoSize, u32 PL) +{ + if (StreamType != GF_STREAM_VISUAL) return 0; + switch (ObjectType) { + case 0x20: + return 1; + /*cap query*/ + case 0: + return 1; + } + return 0; +} + + + +GF_BaseDecoder *NewXVIDDec() +{ + GF_MediaDecoder *ifcd; + XVIDDec *dec; + + ifcd = (GF_MediaDecoder*) malloc(sizeof(GF_MediaDecoder)); + memset(ifcd, 0, sizeof(GF_MediaDecoder)); + + dec = (XVIDDec*) malloc(sizeof(XVIDDec)); + memset(dec, 0, sizeof(XVIDDec)); + + dec->cb_size = 4; + dec->cb_trig = 1; + /*setup our own interface*/ + ifcd->AttachStream = XVID_AttachStream; + ifcd->DetachStream = XVID_DetachStream; + ifcd->GetCapabilities = XVID_GetCapabilities; + ifcd->SetCapabilities = XVID_SetCapabilities; + ifcd->GetName = XVID_GetCodecName; + ifcd->ProcessData = XVID_ProcessData; + ifcd->CanHandleStream = XVID_CanHandleStream; + ifcd->privateStack = dec; + GF_REGISTER_MODULE_INTERFACE(ifcd, GF_MEDIA_DECODER_INTERFACE, "XviD for CE Decoder", "gpac distribution") + return (GF_BaseDecoder *) ifcd; +} + +void DeleteXVIDDec(GF_BaseDecoder *ifcg) +{ + XVIDCTX(); + if (ctx->codec) CloseCodec(ctx->codec); + free(ctx); + free(ifcg); +} + + +#ifdef __cplusplus +extern "C" { +#endif + +GF_EXPORT +Bool QueryInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return 1; + return 0; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_MEDIA_DECODER_INTERFACE) return (GF_BaseInterface *) NewXVIDDec(); + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_MEDIA_DECODER_INTERFACE: + DeleteXVIDDec((GF_BaseDecoder *)ifce); + break; + } +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/xvid_dec/xvid_wce/AUTHORS b/modules/xvid_dec/xvid_wce/AUTHORS new file mode 100644 index 0000000..71b9500 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/AUTHORS @@ -0,0 +1,36 @@ +AUTHORS +======= + +This file lists all authors of XviD MPEG4 core library. If you think +your name should appear on this list, please send us an email telling +us your name, we will be pleased to add it here. + +The lists are classified by alphabetical order. + + +Project initiators: +------------------- + +Christoph Lampert <gruel@web.de> +Michael Militzer <isibaar@xvid.org> +Peter Ross <pross@xvid.org> + + +Regular contributors: +--------------------- + +Edouard Gomez <ed.gomez@free.fr> +Pascal Massimino <skal@planet-d.net> +Radoslaw Czyz <xvid@syskin.cjb.net> + + +Spontaneous contributors: +------------------------- + +Benjamin Herrenschmidt <benh@kernel.crashing.org> +Daniel Smith <danielsmith@astroboymail.com> +Dirk Knop <dknop@stud.uni-goettingen.de> +Guillaume Morin <guillaume@morinfr.org> +MinChen <chenm001@163.com> + +Last edited: $Date: 2005/07/13 14:36:12 $ diff --git a/modules/xvid_dec/xvid_wce/CodecAPI.cpp b/modules/xvid_dec/xvid_wce/CodecAPI.cpp new file mode 100644 index 0000000..34ac6b8 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/CodecAPI.cpp @@ -0,0 +1,114 @@ +//---------------------------- +// Symbian codec API +// +//---------------------------- +#include "rules.h" +#include "global.h" +#include "xvid.h" + +#ifdef __SYMBIAN32__ +#include <e32std.h> +#include <e32base.h> +//GLDEF_C TInt E32Dll(TDllReason){ return KErrNone; } +#else +#endif + + +//---------------------------- + +struct S_decoder{ + xvid_dec_frame_t xvid_dec_frame; + dword size_x; + void *dec_handle; +}; + +#define FCC(a, b, c, d) dword((d<<24) | (c<<16) | (b<<8) | a) + +//---------------------------- + +void * InitCodec(dword sx, dword sy, dword fcc){ + + switch(fcc){ + case FCC('x', 'v', 'i', 'd'): + case FCC('X', 'V', 'I', 'D'): + case FCC('D', 'I', 'V', 'X'): + case FCC('d', 'i', 'v', 'x'): + case FCC('D', 'X', '5', '0'): + case FCC('3', 'I', 'V', '2'): + case FCC('3', 'i', 'v', '2'): + case FCC('3', 'I', 'V', 'X'): + break; + default: + return NULL; + } + S_decoder *dec = new S_decoder; + if (!dec) return NULL; + dec->size_x = sx; + + xvid_dec_frame_t &xvid_dec_frame = dec->xvid_dec_frame; + MemSet(&xvid_dec_frame, 0, sizeof(xvid_dec_frame)); + xvid_dec_frame.version = XVID_VERSION; + //xvid_dec_frame.general = 0; + //convert into true-color, we'll perform convertion to dest format ourselves + //xvid_dec_frame.output.csp = XVID_CSP_BGR; + + xvid_gbl_init_t xvid_gbl_init; + MemSet(&xvid_gbl_init, 0, sizeof(xvid_gbl_init)); + xvid_gbl_init.version = XVID_VERSION; + xvid_gbl_init.cpu_flags = 0; + //xvid_gbl_init.debug = XVID_DEBUG_ERROR | XVID_DEBUG_STARTCODE | XVID_DEBUG_HEADER; + xvid_global(NULL, 0, &xvid_gbl_init, NULL); + + xvid_dec_create_t xvid_dec_create; + MemSet(&xvid_dec_create, 0, sizeof(xvid_dec_create)); + xvid_dec_create.version = XVID_VERSION; + xvid_dec_create.width = 0; + xvid_dec_create.height = 0; +#ifdef PROFILE + xvid_dec_create.prof = &prof; +#endif + xvid_dec_create.width = sx; + xvid_dec_create.height = sy; + + int ret = xvid_decore(NULL, XVID_DEC_CREATE, &xvid_dec_create, NULL); + if(ret){ + delete dec; + return NULL; + } + dec->size_x = sx; + dec->dec_handle = xvid_dec_create.handle; + + return dec; +} + +//---------------------------- + +void CloseCodec(void *handle){ + S_decoder *dec = (S_decoder*)handle; + xvid_decore(dec->dec_handle, XVID_DEC_DESTROY, NULL, NULL); + delete dec; +} + +//---------------------------- + +int DecodeFrame(void *handle, const void *buf, dword sz_in, byte *&y, byte *&u, byte *&v, dword &pitch){ + + S_decoder *dec = (S_decoder*)handle; + + dec->xvid_dec_frame.bitstream = buf; + dec->xvid_dec_frame.length = sz_in; + xvid_decore(dec->dec_handle, XVID_DEC_DECODE, &dec->xvid_dec_frame, NULL); + + const C_xvid_image *img = dec->xvid_dec_frame.img_out; + if(!img) + return 0; + + y = img->y; + u = img->u; + v = img->v; + int mb_width = (dec->size_x + 15) / 16; + pitch = 16 * mb_width + 2 * EDGE_SIZE; + return 1; +} + +//---------------------------- diff --git a/modules/xvid_dec/xvid_wce/LICENSE b/modules/xvid_dec/xvid_wce/LICENSE new file mode 100644 index 0000000..14db8fc --- /dev/null +++ b/modules/xvid_dec/xvid_wce/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/modules/xvid_dec/xvid_wce/README b/modules/xvid_dec/xvid_wce/README new file mode 100644 index 0000000..b9ea387 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/README @@ -0,0 +1,29 @@ +1) Introduction +--------------- + +XviD is a high performance and high quality MPEG-4 video de-/encoding +solution. + +The XviD package currently consists of three parts: + +- xvidcore: the main MPEG-4 de-/encoding library, and simple example + programs +- dshow: windows direct show decoder filter which links against + xvidcore to allow MPEG-4 playback on Windows based OS. +- vfw: video for windows GUI + + +2) Documentation +---------------- + +- xvidcore/doc/README: some general information. +- xvidcore/doc/INSTALL: building and installing instructions. + + +3) Licensing: +------------ + +- XviD is licensed as a whole under the terms of the XviD license + described in the file LICENSE This is true for all files belonging + to XviD except for those which specifically carry a different + license header. \ No newline at end of file diff --git a/modules/xvid_dec/xvid_wce/ReadMe.txt b/modules/xvid_dec/xvid_wce/ReadMe.txt new file mode 100644 index 0000000..5550da6 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/ReadMe.txt @@ -0,0 +1,65 @@ +This is a port of the XviD decoder for symbian and PocketPC OS. It was found by luck on the net at http://www.lonelycatgames.com/mobile/smartmovie/ + +Symbian codec system: + +//---------------------------- +General rules: +- All codecs are located in c:\system\codecs on target device +- Each codec is single native DLL +- One codec may support one or more video formats +- Formats are identified by 32-bit fourcc code + +//---------------------------- +Codec API: + +Codec DLL need to have following functions exported: + +Function Ordinal +InitCodec 1 +CloseCodec 2 +DecodeFrame 3 + +Because Symbian DLL doesn't support exporting DLL functions by name, exported functions +must have ordinals as specified in above table. + +//---------------------------- +Detailed API definition: + +void *InitCodec(dword sx, dword sy, dword fcc); +Initialize codec, using provided fcc code and dimensions of video image. +Parameters: + [IN] sx, sy - resolution of video image (width, height) + [IN] fcc - Four-cc code of video stream +Return value: + pointer to decoder handle (passed to other functions), or NULL if fourcc code is not supported by codec + +//---------------------------- +void CloseCodec(void *handle); +Close codec, unitinialize memory, etc. +Parameters: + [IN] handle - handle value obtained by InitCodec function + +//---------------------------- +int DecodeFrame(void *handle, const void *buf, dword sz_in, const byte *&y, const byte *&u, const byte *&v, dword &pitch); +Decode single frame. The frame data may depend on previous frame data passed in by previous call to this function. +It is safe to call this function with data of non-contiguous frame, for example when seeking, provided that +the frame is a keyframe - i.e. contains data for entire image. +Parameters: + [IN] handle - handle value obtained by InitCodec function + [IN] buf - pointer to bitstream buffer + [IN] sz_in - size of 'buf' buffer data + [OUT] y, u, v - pointer references filled with YUV to YUV data of decoded frame when the call returns; these + pointers are valid until next call to DecodeFrame or CloseCodec functions + [OUT] pitch - size of image line, in bytes (for Y array; U and V arrays have half pitch of Y, because UV is + coded only for each 4x4 pixel block +Return value: + 0 - frame was not decoded (i.e. preroll frame); pointers y, u, v are not filled + 1 - frame was decoded, contains packed YUV data + 2 - frame was decoded, contains interleaved RGB data, returned in 'y' pointer + +//---------------------------- + +Note: +The source project is built in Microsoft Visual C++ 6 environment. +It is assumed that Symbian Series60 SDK is installed in C:\Symbian\6.1\Series60\Epoc32 + diff --git a/modules/xvid_dec/xvid_wce/Rules.h b/modules/xvid_dec/xvid_wce/Rules.h new file mode 100644 index 0000000..21327fd --- /dev/null +++ b/modules/xvid_dec/xvid_wce/Rules.h @@ -0,0 +1,66 @@ +#ifndef __RULES_H +#define __RULES_H + +/*---------------------------- + Copyright (c) Lonely Cat Games All rights reserved. + General types required for compilation of Insanity group of libraries. +----------------------------*/ + //warning settings for MS compiler +#ifdef _MSC_VER + +#pragma warning(disable: 4786)//trunc symbols to 255 chars +#pragma warning(disable: 4530)//exception handling +#pragma warning(disable: 4800)//exception handling +#pragma warning(disable: 4096)//exception handling +#pragma warning(disable: 4100)//unreferenced formal parameter +#pragma warning(disable: 4505)//unreferenced local function has been removed +#pragma warning(disable: 4514)//unreferenced inline function has been removed +#pragma warning(disable: 4663)//C++ error in std headers +#pragma warning(disable: 4710)//function not inlined +#pragma warning(disable: 4201)//nonstandard extension with unnamed structs +#pragma warning(disable: 4244)//conversion from ??? to ???, possible loss of data +#pragma warning(disable: 4146)//unary minus operator applied to unsigned type, result still unsigned +#pragma warning(disable: 4284)//C++... +#pragma warning(disable: 4290)//C++ Exception Specification ignored +#pragma warning(disable: 4127)//conditional expression is constant +#pragma warning(disable: 4663)//C++ language change... +#pragma warning(disable: 4702)//unreachable code +#pragma warning(disable: 4511)//copy constructor could not be generated +#pragma warning(disable: 4512)//assignment operator could not be generated +#pragma warning(disable: 4711)//function selected for automatic inline expansion + + + + //level4 warnings: +#pragma warning(3: 4189) //local variable is initialized but not referenced +#pragma warning(3: 4305) //truncation from const double to float +//#pragma warning(3: 4244) //conversion from ??? to ???, possible loss of data +#pragma warning(3: 4245) //signed/unsigned mismatch +#pragma warning(3: 4018) //signed/unsigned mismatch +#pragma warning(3: 4706) //assignment within conditional expression +#pragma warning(3: 4701) //local variable may be used without having been initialized +#pragma warning(3: 4211) //nonstandard extension used : redefined extern to static +#pragma warning(3: 4310) //cast truncates constant value + +#endif + +//---------------------------- + +typedef unsigned char byte; +typedef signed char schar; +typedef unsigned short word; +typedef unsigned long dword; +typedef unsigned long ulong; + +#ifndef _SIZE_T_DEFINED_ +#define _SIZE_T_DEFINED_ +typedef unsigned size_t; +#endif + +#ifndef NULL + #define NULL 0 +#endif + +//---------------------------- + +#endif diff --git a/modules/xvid_dec/xvid_wce/TODO b/modules/xvid_dec/xvid_wce/TODO new file mode 100644 index 0000000..a954d9e --- /dev/null +++ b/modules/xvid_dec/xvid_wce/TODO @@ -0,0 +1,52 @@ +TODO +==== + +This file lists the TODO items stll pending for the 1.0 release. + + +Active Items to work on: +------------------------ + +* update vfw & dshow decoder frontends + - GUI isn't perfect for vfw +* update/fix CBR plugin + - misses target bitrate, bitrate burst in static motion/high motion + transitions +* bug hunting (ed.gomez/syskin) + - xvidcore is still a place where bugs feel confortable +* docbook writing (every developer should contribute) + - New API has to be documented. +* two pass code should not introduce quality regressions (every tester) + - first feedback proved kfthresholding was hurting quality, a + decision on its behavior has to be settled. +* MMX MPEG4 quantization is unprecise. This is probably an inherent + problem in some MMX opcode precision. + + +Already completed items: +------------------------ + +* remove divx4 api (ed.gomez) +* remove VOP_TYPE enumerations (peter) +* remove HINTed ME stuff (ed.gomez) +* xvid_image_t/xvid_gbl_convert_t (peter) +* xvid_global structs (peter) +* errors codes (peter) +* xvid_decoder structs (peter) +* apply encoder api changes "HEAPS" (peter) +* rawdec (use xvid_decraw instead) (ed.gomez) +* Support for GMC 3 warp points (christoph) +* New Qpel code (michael) +* ME splitting and ME improvements (syskin) +* New unix build process (ed.gomez) +* New thread/instance safe sse2 code (p.massimino) +* INSTALL guide for Unix and Win32 (ed.gomez) +* dshow static link to libxvidcore.lib (peter) +* update/fix Lumimasking (syskin) +* trellis for mpeg and relaxed optimization for big levels (skal) +* thread safe mpeg quantizing (michael) +* Interlacing for bvop and svop (syskin) +* YV12/I420/USER clarification (christoph) +* vfw and dshow link dynamically to xvidcore.dll (syskin) + +Last edited: $Date: 2005/07/13 14:36:16 $ diff --git a/modules/xvid_dec/xvid_wce/bitstream.cpp b/modules/xvid_dec/xvid_wce/bitstream.cpp new file mode 100644 index 0000000..20d7be0 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/bitstream.cpp @@ -0,0 +1,1104 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Bitstream reader/writer - + * + * Copyright (C) 2001-2003 Peter Ross <pross@xvid.org> + * 2003 Cristoph Lampert <gruel@web.de> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: bitstream.cpp,v 1.1.1.1 2005/07/13 14:36:12 jeanlf Exp $ + * + ****************************************************************************/ + +#include "bitstream.h" +#include "quant_matrix.h" + +#ifdef WIN32 +#include <stdio.h> +#endif + +//---------------------------- + +inline dword ByteSwap(dword a){ + return (a<<24) | ((a&0xff00) << 8) | ((a&0xff0000) >> 8) | (a>>24); +} + +//---------------------------- + +void Bitstream::Init(const void *bitstream, dword _length){ + + dword adjbitstream = (dword)bitstream; + + /* + * Start the stream on a dword boundary, by rounding down to the + * previous dword and skipping the intervening bytes. + */ + long bitpos = ((sizeof(dword)-1) & (long)bitstream); + adjbitstream = adjbitstream - bitpos; + start = tail = (dword *) adjbitstream; + + if(_length){ + dword tmp = *start; +#ifndef ARCH_IS_BIG_ENDIAN + tmp = ByteSwap(tmp); +#endif + bufa = tmp; + if(_length>4){ + tmp = *(start + 1); +#ifndef ARCH_IS_BIG_ENDIAN + tmp = ByteSwap(tmp); +#endif + bufb = tmp; + }else + bufb = 0; + }else + bufa = bufb = 0; + + buf = 0; + pos = initpos = bitpos*8; + length = _length; +} + +//--------------------------- + +/* reads n bits from bitstream without changing the stream pos */ + +dword Bitstream::ShowBits(dword bits){ + + int nbit = (bits + pos) - 32; + if(nbit > 0) + return ((bufa & (0xffffffff >> pos)) << nbit) | (bufb >> (32 - nbit)); + return (bufa & (0xffffffff >> pos)) >> (32 - pos - bits); +} + +//--------------------------- +// skip n bits forward in bitstream +void Bitstream::Skip(dword bits){ + + pos += bits; + if(pos >= 32){ + dword tmp; + + bufa = bufb; + tmp = *((dword *) tail + 2); +#ifndef ARCH_IS_BIG_ENDIAN + tmp = ByteSwap(tmp); +#endif + bufb = tmp; + tail++; + pos -= 32; + } +} + +//--------------------------- +/* show nbits from next byte alignment */ +dword Bitstream::ShowBitsFromByteAlign(int bits){ + + int bspos = pos + NumBitsToByteAlign(); + int nbit = (bits + bspos) - 32; + + if (bspos >= 32) { + return bufb >> (32 - nbit); + } else if (nbit > 0) { + return ((bufa & (0xffffffff >> bspos)) << nbit) | (bufb >> (32 - nbit)); + } else { + return (bufa & (0xffffffff >> bspos)) >> (32 - bspos - bits); + } + +} + +//--------------------------- +/* read n bits from bitstream */ +dword Bitstream::GetBits(dword n){ + dword ret = ShowBits(n); + Skip(n); + return ret; +} + +//--------------------------- + +static const dword intra_dc_threshold_table[] = { + 32, /* never use */ + 13, + 15, + 17, + 19, + 21, + 23, + 1, +}; + +//---------------------------- + +void Bitstream::get_matrix(byte * matrix){ + + int i = 0; + int last, value = 0; + + do { + last = value; + value = GetBits(8); + matrix[scan_tables[0][i++]] = value; + } + while (value != 0 && i < 64); + i--; /* fix little bug at coeff not full */ + + while (i < 64) { + matrix[scan_tables[0][i++]] = last; + } +} + +//---------------------------- +/* + * for PVOP addbits == fcode - 1 + * for BVOP addbits == max(fcode,bcode) - 1 + * returns mbpos + */ +int S_decoder::read_video_packet_header(Bitstream *bs, const int addbits, int *quant, int *fcode_forward, int *fcode_backward, int *intra_dc_threshold){ + + int startcode_bits = NUMBITS_VP_RESYNC_MARKER + addbits; + int mbnum_bits = log2bin(mb_width * mb_height - 1); + int mbnum; + int hec = 0; + + bs->Skip(bs->NumBitsToByteAlign()); + bs->Skip(startcode_bits); + + DPRINTF(XVID_DEBUG_STARTCODE, "<video_packet_header>\n"); + + if (shape != VIDOBJLAY_SHAPE_RECTANGULAR) + { + hec = bs->GetBit(); /* header_extension_code */ + if (hec && !(sprite_enable == SPRITE_STATIC /* && current_coding_type = I_VOP */)) + { + bs->Skip(13); /* vop_width */ + READ_MARKER(); + bs->Skip(13); /* vop_height */ + READ_MARKER(); + bs->Skip(13); /* vop_horizontal_mc_spatial_ref */ + READ_MARKER(); + bs->Skip(13); /* vop_vertical_mc_spatial_ref */ + READ_MARKER(); + } + } + + mbnum = bs->GetBits(mbnum_bits); /* macroblock_number */ + DPRINTF(XVID_DEBUG_HEADER, "mbnum %i\n", mbnum); + + if (shape != VIDOBJLAY_SHAPE_BINARY_ONLY) + { + *quant = bs->GetBits(quant_bits); /* quant_scale */ + DPRINTF(XVID_DEBUG_HEADER, "quant %i\n", *quant); + } + + if (shape == VIDOBJLAY_SHAPE_RECTANGULAR) + hec = bs->GetBit(); /* header_extension_code */ + + + DPRINTF(XVID_DEBUG_HEADER, "header_extension_code %i\n", hec); + if (hec) + { + int time_base; + int time_increment = 0; + int coding_type; + + for (time_base=0; bs->GetBit()!=0; time_base++); /* modulo_time_base */ + READ_MARKER(); + if (time_inc_bits) + time_increment = (bs->GetBits(time_inc_bits)); /* vop_time_increment */ + READ_MARKER(); + //DPRINTF(XVID_DEBUG_HEADER,"time %i:%i\n", time_base, time_increment); + + coding_type = bs->GetBits(2); + DPRINTF(XVID_DEBUG_HEADER,"coding_type %i\n", coding_type); + + if (shape != VIDOBJLAY_SHAPE_RECTANGULAR) + { + bs->Skip(1); /* change_conv_ratio_disable */ + if (coding_type != I_VOP) + bs->Skip(1); /* vop_shape_coding_type */ + } + + if (shape != VIDOBJLAY_SHAPE_BINARY_ONLY) + { + *intra_dc_threshold = intra_dc_threshold_table[bs->GetBits(3)]; + + if (sprite_enable == SPRITE_GMC && coding_type == S_VOP && + sprite_warping_points > 0) + { + /* TODO: sprite trajectory */ + } + if(reduced_resolution_enable && shape == VIDOBJLAY_SHAPE_RECTANGULAR && (coding_type == P_VOP || coding_type == I_VOP)){ + bs->Skip(1); /* XXX: vop_reduced_resolution */ + } + + if (coding_type != I_VOP && fcode_forward) + { + *fcode_forward = bs->GetBits(3); + DPRINTF(XVID_DEBUG_HEADER,"fcode_forward %i\n", *fcode_forward); + } + + if (coding_type == B_VOP && fcode_backward) + { + *fcode_backward = bs->GetBits(3); + DPRINTF(XVID_DEBUG_HEADER,"fcode_backward %i\n", *fcode_backward); + } + } + + } + + if (newpred_enable) { + int vop_id; + int vop_id_for_prediction; + + vop_id = bs->GetBits(MIN(time_inc_bits + 3, 15)); + DPRINTF(XVID_DEBUG_HEADER, "vop_id %i\n", vop_id); + if (bs->GetBit()) /* vop_id_for_prediction_indication */ + { + vop_id_for_prediction = bs->GetBits(MIN(time_inc_bits + 3, 15)); + DPRINTF(XVID_DEBUG_HEADER, "vop_id_for_prediction %i\n", vop_id_for_prediction); + } + READ_MARKER(); + } + + return mbnum; +} + +//---------------------------- +/* vol estimation header */ +void S_decoder::read_vol_complexity_estimation_header(Bitstream * bs){ + + ESTIMATION * e = &estimation; + + e->method = bs->GetBits(2); /* estimation_method */ + DPRINTF(XVID_DEBUG_HEADER,"+ complexity_estimation_header; method=%i\n", e->method); + + if (e->method == 0 || e->method == 1){ + if(!bs->GetBit()){ + //shape_complexity_estimation_disable + e->opaque = bs->GetBit(); /* opaque */ + e->transparent = bs->GetBit(); /* transparent */ + e->intra_cae = bs->GetBit(); /* intra_cae */ + e->inter_cae = bs->GetBit(); /* inter_cae */ + e->no_update = bs->GetBit(); /* no_update */ + e->upsampling = bs->GetBit(); /* upsampling */ + } + + if (!bs->GetBit()) /* texture_complexity_estimation_set_1_disable */ + { + e->intra_blocks = bs->GetBit(); /* intra_blocks */ + e->inter_blocks = bs->GetBit(); /* inter_blocks */ + e->inter4v_blocks = bs->GetBit(); /* inter4v_blocks */ + e->not_coded_blocks = bs->GetBit(); /* not_coded_blocks */ + } + } + + READ_MARKER(); + + if (!bs->GetBit()){ + //texture_complexity_estimation_set_2_disable + e->dct_coefs = bs->GetBit(); /* dct_coefs */ + e->dct_lines = bs->GetBit(); /* dct_lines */ + e->vlc_symbols = bs->GetBit(); /* vlc_symbols */ + e->vlc_bits = bs->GetBit(); /* vlc_bits */ + } + + if (!bs->GetBit()){ + //motion_compensation_complexity_disable + e->apm = bs->GetBit(); /* apm */ + e->npm = bs->GetBit(); /* npm */ + e->interpolate_mc_q = bs->GetBit(); /* interpolate_mc_q */ + e->forw_back_mc_q = bs->GetBit(); /* forw_back_mc_q */ + e->halfpel2 = bs->GetBit(); /* halfpel2 */ + e->halfpel4 = bs->GetBit(); /* halfpel4 */ + } + + READ_MARKER(); + + if (e->method == 1){ + if (!bs->GetBit()){ + //version2_complexity_estimation_disable + e->sadct = bs->GetBit(); /* sadct */ + e->quarterpel = bs->GetBit(); /* quarterpel */ + } + } +} + +//---------------------------- +/* vop estimation header */ +void S_decoder::read_vop_complexity_estimation_header(Bitstream * bs, int coding_type){ + + ESTIMATION * e = &estimation; + + if (e->method == 0 || e->method == 1) + { + if (coding_type == I_VOP) { + if (e->opaque) bs->Skip(8); /* dcecs_opaque */ + if (e->transparent) bs->Skip(8); /* */ + if (e->intra_cae) bs->Skip(8); /* */ + if (e->inter_cae) bs->Skip(8); /* */ + if (e->no_update) bs->Skip(8); /* */ + if (e->upsampling) bs->Skip(8); /* */ + if (e->intra_blocks) bs->Skip(8); /* */ + if (e->not_coded_blocks) bs->Skip(8); /* */ + if (e->dct_coefs) bs->Skip(8); /* */ + if (e->dct_lines) bs->Skip(8); /* */ + if (e->vlc_symbols) bs->Skip(8); /* */ + if (e->vlc_bits) bs->Skip(8); /* */ + if (e->sadct) bs->Skip(8); /* */ + } + + if (coding_type == P_VOP) { + if (e->opaque) bs->Skip(8); /* */ + if (e->transparent) bs->Skip(8); /* */ + if (e->intra_cae) bs->Skip(8); /* */ + if (e->inter_cae) bs->Skip(8); /* */ + if (e->no_update) bs->Skip(8); /* */ + if (e->upsampling) bs->Skip(8); /* */ + if (e->intra_blocks) bs->Skip(8); /* */ + if (e->not_coded_blocks) bs->Skip(8); /* */ + if (e->dct_coefs) bs->Skip(8); /* */ + if (e->dct_lines) bs->Skip(8); /* */ + if (e->vlc_symbols) bs->Skip(8); /* */ + if (e->vlc_bits) bs->Skip(8); /* */ + if (e->inter_blocks) bs->Skip(8); /* */ + if (e->inter4v_blocks) bs->Skip(8); /* */ + if (e->apm) bs->Skip(8); /* */ + if (e->npm) bs->Skip(8); /* */ + if (e->forw_back_mc_q) bs->Skip(8); /* */ + if (e->halfpel2) bs->Skip(8); /* */ + if (e->halfpel4) bs->Skip(8); /* */ + if (e->sadct) bs->Skip(8); /* */ + if (e->quarterpel) bs->Skip(8); /* */ + } + if (coding_type == B_VOP) { + if (e->opaque) bs->Skip(8); /* */ + if (e->transparent) bs->Skip(8); /* */ + if (e->intra_cae) bs->Skip(8); /* */ + if (e->inter_cae) bs->Skip(8); /* */ + if (e->no_update) bs->Skip(8); /* */ + if (e->upsampling) bs->Skip(8); /* */ + if (e->intra_blocks) bs->Skip(8); /* */ + if (e->not_coded_blocks) bs->Skip(8); /* */ + if (e->dct_coefs) bs->Skip(8); /* */ + if (e->dct_lines) bs->Skip(8); /* */ + if (e->vlc_symbols) bs->Skip(8); /* */ + if (e->vlc_bits) bs->Skip(8); /* */ + if (e->inter_blocks) bs->Skip(8); /* */ + if (e->inter4v_blocks) bs->Skip(8); /* */ + if (e->apm) bs->Skip(8); /* */ + if (e->npm) bs->Skip(8); /* */ + if (e->forw_back_mc_q) bs->Skip(8); /* */ + if (e->halfpel2) bs->Skip(8); /* */ + if (e->halfpel4) bs->Skip(8); /* */ + if (e->interpolate_mc_q) bs->Skip(8); /* */ + if (e->sadct) bs->Skip(8); /* */ + if (e->quarterpel) bs->Skip(8); /* */ + } + + if (coding_type == S_VOP && sprite_enable == SPRITE_STATIC) { + if (e->intra_blocks) bs->Skip(8); /* */ + if (e->not_coded_blocks) bs->Skip(8); /* */ + if (e->dct_coefs) bs->Skip(8); /* */ + if (e->dct_lines) bs->Skip(8); /* */ + if (e->vlc_symbols) bs->Skip(8); /* */ + if (e->vlc_bits) bs->Skip(8); /* */ + if (e->inter_blocks) bs->Skip(8); /* */ + if (e->inter4v_blocks) bs->Skip(8); /* */ + if (e->apm) bs->Skip(8); /* */ + if (e->npm) bs->Skip(8); /* */ + if (e->forw_back_mc_q) bs->Skip(8); /* */ + if (e->halfpel2) bs->Skip(8); /* */ + if (e->halfpel4) bs->Skip(8); /* */ + if (e->interpolate_mc_q) bs->Skip(8); /* */ + } + } +} + +//---------------------------- +// decode headers +// returns coding_type, or -1 if error + +#define VIDOBJ_START_CODE_MASK 0x0000001f +#define VIDOBJLAY_START_CODE_MASK 0x0000000f + +int S_decoder::BitstreamReadHeaders(Bitstream * bs, bool &rounding, bool *reduced_resolution, dword *quant, dword *fcode_forward, + dword *fcode_backward, dword *intra_dc_threshold, WARPPOINTS *gmc_warp){ + + dword vol_ver_id; + dword coding_type; + dword start_code; + dword time_incr = 0; + int time_increment = 0; + int resize = 0; + + while((bs->Pos() >> 3) < bs->length){ + bs->ByteAlign(); + start_code = bs->ShowBits(32); + + switch(start_code){ + case VISOBJSEQ_START_CODE: + { + DPRINTF(XVID_DEBUG_STARTCODE, "<visual_object_sequence>\n"); + + bs->Skip(32); //visual_object_sequence_start_code + int profile = bs->GetBits(8); //profile_and_level_indication + + DPRINTF(XVID_DEBUG_HEADER, "profile_and_level_indication %i\n", profile); + } + break; + case VISOBJSEQ_STOP_CODE: + { + //visual_object_sequence_stop_code + bs->Skip(32); + DPRINTF(XVID_DEBUG_STARTCODE, "</visual_object_sequence>\n"); + + } + break; + case VISOBJ_START_CODE: + { + DPRINTF(XVID_DEBUG_STARTCODE, "<visual_object>\n"); + + bs->Skip(32); //visual_object_start_code + + int visobj_ver_id; + //is_visual_object_identified + if(bs->GetBit()){ + visobj_ver_id = bs->GetBits(4); /* visual_object_ver_id */ + DPRINTF(XVID_DEBUG_HEADER,"visobj_ver_id %i\n", visobj_ver_id); + bs->Skip(3); //visual_object_priority + }else{ + visobj_ver_id = 1; + } + + if(bs->ShowBits(4) != VISOBJ_TYPE_VIDEO){ + //visual_object_type + DPRINTF(XVID_DEBUG_ERROR, "visual_object_type != video\n"); + return -1; + } + bs->Skip(4); + //video_signal_type + + if(bs->GetBit()){ + DPRINTF(XVID_DEBUG_HEADER,"+ video_signal_type\n"); + bs->Skip(3); /* video_format */ + bs->Skip(1); /* video_range */ + if (bs->GetBit()) /* color_description */ + { + DPRINTF(XVID_DEBUG_HEADER,"+ color_description"); + bs->Skip(8); /* color_primaries */ + bs->Skip(8); /* transfer_characteristics */ + bs->Skip(8); /* matrix_coefficients */ + } + } + } + break; + case GRPOFVOP_START_CODE: + { + DPRINTF(XVID_DEBUG_STARTCODE, "<group_of_vop>\n"); + + bs->Skip(32); + { + int hours, minutes, seconds; + + hours = bs->GetBits(5); + minutes = bs->GetBits(6); + READ_MARKER(); + seconds = bs->GetBits(6); + //DPRINTF(XVID_DEBUG_HEADER, "time %ih%im%is\n", hours,minutes,seconds); + } + bs->Skip(1); /* closed_gov */ + bs->Skip(1); /* broken_link */ + } + break; + case VOP_START_CODE: + { + + DPRINTF(XVID_DEBUG_STARTCODE, "<vop>\n"); + + bs->Skip(32); /* vop_start_code */ + + coding_type = bs->GetBits(2); /* vop_coding_type */ + DPRINTF(XVID_DEBUG_HEADER, "coding_type %i\n", coding_type); + + /*********************** for decode B-frame time ***********************/ + while (bs->GetBit() != 0) /* time_base */ + time_incr++; + + READ_MARKER(); + + if (time_inc_bits) { + time_increment = (bs->GetBits(time_inc_bits)); /* vop_time_increment */ + } + + DPRINTF(XVID_DEBUG_HEADER, "time_base %i\n", time_incr); + DPRINTF(XVID_DEBUG_HEADER, "time_increment %i\n", time_increment); + + //DPRINTF(XVID_DEBUG_TIMECODE, "%c %i:%i\n", coding_type == I_VOP ? 'I' : coding_type == P_VOP ? 'P' : coding_type == B_VOP ? 'B' : 'S', time_incr, time_increment); + if (coding_type != B_VOP) { + last_time_base = time_base; + time_base += time_incr; + time = time_increment; + +#if 0 + time_base * time_inc_resolution + + time_increment; +#endif + time_pp = (dword) + (time_inc_resolution + time - last_non_b_time)%time_inc_resolution; + last_non_b_time = time; + } else { + time = time_increment; +#if 0 + (last_time_base + + time_incr) * time_inc_resolution + time_increment; +#endif + time_bp = (dword) + (time_inc_resolution + last_non_b_time - time)%time_inc_resolution; + } + DPRINTF(XVID_DEBUG_HEADER,"time_pp=%i\n", time_pp); + DPRINTF(XVID_DEBUG_HEADER,"time_bp=%i\n", time_bp); + + READ_MARKER(); + + if (!bs->GetBit()) /* vop_coded */ + { + DPRINTF(XVID_DEBUG_HEADER, "vop_coded==false\n"); + return N_VOP; + } + + if (newpred_enable) { + int vop_id; + int vop_id_for_prediction; + + vop_id = bs->GetBits(MIN(time_inc_bits + 3, 15)); + DPRINTF(XVID_DEBUG_HEADER, "vop_id %i\n", vop_id); + if (bs->GetBit()) /* vop_id_for_prediction_indication */ + { + vop_id_for_prediction = bs->GetBits(MIN(time_inc_bits + 3, 15)); + DPRINTF(XVID_DEBUG_HEADER, "vop_id_for_prediction %i\n", vop_id_for_prediction); + } + READ_MARKER(); + } + + //fix a little bug by MinChen <chenm002@163.com> + if((shape != VIDOBJLAY_SHAPE_BINARY_ONLY) && + ((coding_type == P_VOP) || (coding_type == S_VOP && sprite_enable == SPRITE_GMC))){ + rounding = !!bs->GetBit(); //rounding_type + DPRINTF(XVID_DEBUG_HEADER, "rounding %i\n", rounding); + } + + if(reduced_resolution_enable && shape == VIDOBJLAY_SHAPE_RECTANGULAR && (coding_type == P_VOP || coding_type == I_VOP)) { + + *reduced_resolution = !!bs->GetBit(); + DPRINTF(XVID_DEBUG_HEADER, "reduced_resolution %i\n", *reduced_resolution); + }else{ + *reduced_resolution = false; + } + + if (shape != VIDOBJLAY_SHAPE_RECTANGULAR) { + if(!(sprite_enable == SPRITE_STATIC && coding_type == I_VOP)) { + + dword width, height; + dword horiz_mc_ref, vert_mc_ref; + + width = bs->GetBits(13); + READ_MARKER(); + height = bs->GetBits(13); + READ_MARKER(); + horiz_mc_ref = bs->GetBits(13); + READ_MARKER(); + vert_mc_ref = bs->GetBits(13); + READ_MARKER(); + + DPRINTF(XVID_DEBUG_HEADER, "width %i\n", width); + DPRINTF(XVID_DEBUG_HEADER, "height %i\n", height); + DPRINTF(XVID_DEBUG_HEADER, "horiz_mc_ref %i\n", horiz_mc_ref); + DPRINTF(XVID_DEBUG_HEADER, "vert_mc_ref %i\n", vert_mc_ref); + } + + bs->Skip(1); /* change_conv_ratio_disable */ + if (bs->GetBit()) /* vop_constant_alpha */ + { + bs->Skip(8); /* vop_constant_alpha_value */ + } + } + + if (shape != VIDOBJLAY_SHAPE_BINARY_ONLY) { + + if (!complexity_estimation_disable) + { + read_vop_complexity_estimation_header(bs, coding_type); + } + + /* intra_dc_vlc_threshold */ + *intra_dc_threshold = + intra_dc_threshold_table[bs->GetBits(3)]; + + top_field_first = 0; + alternate_vertical_scan = 0; + + if(interlacing){ + top_field_first = bs->GetBit(); + DPRINTF(XVID_DEBUG_HEADER, "interlace top_field_first %i\n", top_field_first); + alternate_vertical_scan = bs->GetBit(); + DPRINTF(XVID_DEBUG_HEADER, "interlace alternate_vertical_scan %i\n", alternate_vertical_scan); + + } + } + + if ((sprite_enable == SPRITE_STATIC || sprite_enable== SPRITE_GMC) && coding_type == S_VOP) { + + int i; + + for (i = 0 ; i < sprite_warping_points; i++) + { + int length; + int x = 0, y = 0; + + /* sprite code borowed from ffmpeg; thx Michael Niedermayer <michaelni@gmx.at> */ + length = bs->bs_get_spritetrajectory(); + if(length){ + x= bs->GetBits(length); + if ((x >> (length - 1)) == 0) /* if MSB not set it is negative*/ + x = - (x ^ ((1 << length) - 1)); + } + READ_MARKER(); + + length = bs->bs_get_spritetrajectory(); + if(length){ + y = bs->GetBits(length); + if ((y >> (length - 1)) == 0) /* if MSB not set it is negative*/ + y = - (y ^ ((1 << length) - 1)); + } + READ_MARKER(); + + gmc_warp->duv[i].x = x; + gmc_warp->duv[i].y = y; + + //DPRINTF(XVID_DEBUG_HEADER,"sprite_warping_point[%i] xy=(%i,%i)\n", i, x, y); + } + + if (sprite_brightness_change) + { + /* XXX: brightness_change_factor() */ + } + if (sprite_enable == SPRITE_STATIC) + { + /* XXX: todo */ + } + + } + + if ((*quant = bs->GetBits(quant_bits)) < 1) /* vop_quant */ + *quant = 1; + DPRINTF(XVID_DEBUG_HEADER, "quant %i\n", *quant); + + if (coding_type != I_VOP) { + *fcode_forward = bs->GetBits(3); /* fcode_forward */ + DPRINTF(XVID_DEBUG_HEADER, "fcode_forward %i\n", *fcode_forward); + } + + if (coding_type == B_VOP) { + *fcode_backward = bs->GetBits(3); /* fcode_backward */ + DPRINTF(XVID_DEBUG_HEADER, "fcode_backward %i\n", *fcode_backward); + } + if(!scalability){ + if ((shape != VIDOBJLAY_SHAPE_RECTANGULAR) && + (coding_type != I_VOP)) { + bs->Skip(1); /* vop_shape_coding_type */ + } + } + return coding_type; + + } + break; + case USERDATA_START_CODE: + { + char tmp[256]; + int i; + + bs->Skip(32); /* user_data_start_code */ + + tmp[0] = bs->ShowBits(8); + + for(i = 1; i < 256; i++){ + tmp[i] = (bs->ShowBits(16) & 0xFF); + + if(tmp[i] == 0) + break; + + bs->Skip(8); + } + + //DPRINTF(XVID_DEBUG_STARTCODE, "<user_data>: %s\n", tmp); + + /* read xvid bitstream version */ +#ifdef WIN32 + { + char packed; + int version, build; + if(MemCmp(tmp, "XviD", 4) == 0) { + sscanf(tmp, "XviD%d", &bs_version); + DPRINTF(XVID_DEBUG_HEADER, "xvid bitstream version=%i", bs_version); + } + + /* divx detection */ + i = sscanf(tmp, "DivX%dBuild%d%c", &version, &build, &packed); + if (i < 2) + i = sscanf(tmp, "DivX%db%d%c", &version, &build, &packed); + if (i >= 2){ + packed_mode = (i == 3 && packed == 'p'); + //DPRINTF(XVID_DEBUG_HEADER, "divx version=%i, build=%i packed=%i\n", version, build, packed_mode); + } + } +#endif + } + break; + default: + switch(start_code & ~VIDOBJ_START_CODE_MASK){ + case VIDOBJ_START_CODE: + { + + DPRINTF(XVID_DEBUG_STARTCODE, "<video_object>\n"); + DPRINTF(XVID_DEBUG_HEADER, "vo id %i\n", start_code & VIDOBJ_START_CODE_MASK); + //video_object_start_code + bs->Skip(32); + } + break; + case VIDOBJLAY_START_CODE: + { + + DPRINTF(XVID_DEBUG_STARTCODE, "<video_object_layer>\n"); + DPRINTF(XVID_DEBUG_HEADER, "vol id %i\n", start_code & VIDOBJLAY_START_CODE_MASK); + + bs->Skip(32); //video_object_layer_start_code + bs->Skip(1); //random_accessible_vol + + bs->Skip(8); //video_object_type_indication + + if(bs->GetBit()){ + //is_object_layer_identifier + DPRINTF(XVID_DEBUG_HEADER, "+ is_object_layer_identifier\n"); + vol_ver_id = bs->GetBits(4); /* video_object_layer_verid */ + DPRINTF(XVID_DEBUG_HEADER,"ver_id %i\n", vol_ver_id); + bs->Skip(3); /* video_object_layer_priority */ + }else{ + vol_ver_id = 1; + } + + aspect_ratio = bs->GetBits(4); + + if(aspect_ratio == VIDOBJLAY_AR_EXTPAR){ + //aspect_ratio_info + DPRINTF(XVID_DEBUG_HEADER, "+ aspect_ratio_info\n"); + par_width = bs->GetBits(8); /* par_width */ + par_height = bs->GetBits(8); /* par_height */ + } + + if(bs->GetBit()){ + //vol_control_parameters + DPRINTF(XVID_DEBUG_HEADER, "+ vol_control_parameters\n"); + bs->Skip(2); /* chroma_format */ + low_delay = (bs->GetBit()!=0); + DPRINTF(XVID_DEBUG_HEADER, "low_delay %i\n", (int)low_delay); + if (bs->GetBit()) /* vbv_parameters */ + { + unsigned int bitrate; + unsigned int buffer_size; + unsigned int occupancy; + + DPRINTF(XVID_DEBUG_HEADER,"+ vbv_parameters\n"); + + bitrate = bs->GetBits(15) << 15; /* first_half_bit_rate */ + READ_MARKER(); + bitrate |= bs->GetBits(15); /* latter_half_bit_rate */ + READ_MARKER(); + + buffer_size = bs->GetBits(15) << 3; /* first_half_vbv_buffer_size */ + READ_MARKER(); + buffer_size |= bs->GetBits(3); /* latter_half_vbv_buffer_size */ + + occupancy = bs->GetBits(11) << 15; /* first_half_vbv_occupancy */ + READ_MARKER(); + occupancy |= bs->GetBits(15); /* latter_half_vbv_occupancy */ + READ_MARKER(); + + DPRINTF(XVID_DEBUG_HEADER,"bitrate %d (unit=400 bps)\n", bitrate); + DPRINTF(XVID_DEBUG_HEADER,"buffer_size %d (unit=16384 bits)\n", buffer_size); + DPRINTF(XVID_DEBUG_HEADER,"occupancy %d (unit=64 bits)\n", occupancy); + } + }else{ + low_delay = low_delay_default; + } + + //video_object_layer_shape + shape = bs->GetBits(2); + + DPRINTF(XVID_DEBUG_HEADER, "shape %i\n", shape); + if(shape != VIDOBJLAY_SHAPE_RECTANGULAR){ + DPRINTF(XVID_DEBUG_ERROR,"non-rectangular shapes are not supported\n"); + } + + if(shape == VIDOBJLAY_SHAPE_GRAYSCALE && vol_ver_id != 1){ + //video_object_layer_shape_extension + bs->Skip(4); + } + + READ_MARKER(); + + /********************** for decode B-frame time ***********************/ + time_inc_resolution = bs->GetBits(16); /* vop_time_increment_resolution */ + DPRINTF(XVID_DEBUG_HEADER,"vop_time_increment_resolution %i\n", time_inc_resolution); + +#if 0 + time_inc_resolution--; +#endif + + if(time_inc_resolution > 0){ + time_inc_bits = log2bin(time_inc_resolution-1); + }else{ +#if 0 + time_inc_bits = 0; +#endif + /* for "old" xvid compatibility, set time_inc_bits = 1 */ + time_inc_bits = 1; + } + + READ_MARKER(); + + if(bs->GetBit()){ + //fixed_vop_rate + DPRINTF(XVID_DEBUG_HEADER, "+ fixed_vop_rate\n"); + bs->Skip(time_inc_bits); //fixed_vop_time_increment + } + + if(shape != VIDOBJLAY_SHAPE_BINARY_ONLY){ + + if(shape == VIDOBJLAY_SHAPE_RECTANGULAR){ + dword _width, _height; + + READ_MARKER(); + _width = bs->GetBits(13); //video_object_layer_width + READ_MARKER(); + _height = bs->GetBits(13); //video_object_layer_height + READ_MARKER(); + + DPRINTF(XVID_DEBUG_HEADER, "width %i\n", _width); + DPRINTF(XVID_DEBUG_HEADER, "height %i\n", _height); + + if(width != _width || height != _height){ + if(fixed_dimensions){ + DPRINTF(XVID_DEBUG_ERROR, "decoder width/height does not match bitstream\n"); + return -1; + } + resize = 1; + width = _width; + height = _height; + } + } + + interlacing = !!bs->GetBit(); + DPRINTF(XVID_DEBUG_HEADER, "interlacing %i\n", interlacing); + + if(!bs->GetBit()){ + //obmc_disable + DPRINTF(XVID_DEBUG_ERROR, "obmc_disabled==false not supported\n"); + /* TODO */ + /* fucking divx4.02 has this enabled */ + } + + sprite_enable = bs->GetBits((vol_ver_id == 1 ? 1 : 2)); + + if(sprite_enable == SPRITE_STATIC || sprite_enable == SPRITE_GMC){ + int low_latency_sprite_enable; + + if(sprite_enable != SPRITE_GMC){ + int sprite_width; + int sprite_height; + int sprite_left_coord; + int sprite_top_coord; + sprite_width = bs->GetBits(13); /* sprite_width */ + READ_MARKER(); + sprite_height = bs->GetBits(13); /* sprite_height */ + READ_MARKER(); + sprite_left_coord = bs->GetBits(13); /* sprite_left_coordinate */ + READ_MARKER(); + sprite_top_coord = bs->GetBits(13); /* sprite_top_coordinate */ + READ_MARKER(); + } + sprite_warping_points = bs->GetBits(6); /* no_of_sprite_warping_points */ + sprite_warping_accuracy = bs->GetBits(2); /* sprite_warping_accuracy */ + sprite_brightness_change = bs->GetBits(1); /* brightness_change */ + if (sprite_enable != SPRITE_GMC) + { + low_latency_sprite_enable = bs->GetBits(1); /* low_latency_sprite_enable */ + } + } + + if(vol_ver_id != 1 && shape != VIDOBJLAY_SHAPE_RECTANGULAR){ + //sadct_disable + bs->Skip(1); + } + + if(bs->GetBit()){ + //not_8_bit + DPRINTF(XVID_DEBUG_HEADER, "not_8_bit==true (ignored)\n"); + quant_bits = bs->GetBits(4); /* quant_precision */ + bs->Skip(4); /* bits_per_pixel */ + }else{ + quant_bits = 5; + } + + if(shape == VIDOBJLAY_SHAPE_GRAYSCALE){ + bs->Skip(1); /* no_gray_quant_update */ + bs->Skip(1); /* composition_method */ + bs->Skip(1); /* linear_composition */ + } + + quant_type = bs->GetBit(); + DPRINTF(XVID_DEBUG_HEADER, "quant_type %i\n", quant_type); + + if(quant_type){ + if (bs->GetBit()) //load_intra_quant_mat + { + byte matrix[64]; + + DPRINTF(XVID_DEBUG_HEADER, "load_intra_quant_mat\n"); + + bs->get_matrix(matrix); + set_intra_matrix(mpeg_quant_matrices, matrix); + }else + set_intra_matrix(mpeg_quant_matrices, get_default_intra_matrix()); + + if (bs->GetBit()) /* load_inter_quant_mat */ + { + byte matrix[64]; + + DPRINTF(XVID_DEBUG_HEADER, "load_inter_quant_mat\n"); + + bs->get_matrix(matrix); + set_inter_matrix(mpeg_quant_matrices, matrix); + }else + set_inter_matrix(mpeg_quant_matrices, get_default_inter_matrix()); + + if (shape == VIDOBJLAY_SHAPE_GRAYSCALE) { + DPRINTF(XVID_DEBUG_ERROR, "greyscale matrix not supported\n"); + return -1; + } + + } + + if(vol_ver_id != 1){ + quarterpel = bs->GetBit(); + DPRINTF(XVID_DEBUG_HEADER,"quarterpel %i\n", quarterpel); + }else + quarterpel = 0; + + + complexity_estimation_disable = bs->GetBit(); + if(!complexity_estimation_disable){ + read_vol_complexity_estimation_header(bs); + } + + bs->Skip(1); //resync_marker_disable + + if(bs->GetBit()){ + //data_partitioned + DPRINTF(XVID_DEBUG_ERROR, "data_partitioned not supported\n"); + bs->Skip(1); /* reversible_vlc */ + } + + if(vol_ver_id != 1){ + newpred_enable = !!bs->GetBit(); + if (newpred_enable) /* newpred_enable */ + { + DPRINTF(XVID_DEBUG_HEADER, "+ newpred_enable\n"); + bs->Skip(2); /* requested_upstream_message_type */ + bs->Skip(1); /* newpred_segment_type */ + } + reduced_resolution_enable = !!bs->GetBit(); //reduced_resolution_vop_enable + DPRINTF(XVID_DEBUG_HEADER, "reduced_resolution_enable %i\n", reduced_resolution_enable); + }else{ + newpred_enable = false; + reduced_resolution_enable = false; + } + + scalability = (bs->GetBit()!=0); + if(scalability){ + DPRINTF(XVID_DEBUG_ERROR, "scalability not supported\n"); + bs->Skip(1); /* hierarchy_type */ + bs->Skip(4); /* ref_layer_id */ + bs->Skip(1); /* ref_layer_sampling_direc */ + bs->Skip(5); /* hor_sampling_factor_n */ + bs->Skip(5); /* hor_sampling_factor_m */ + bs->Skip(5); /* vert_sampling_factor_n */ + bs->Skip(5); /* vert_sampling_factor_m */ + bs->Skip(1); /* enhancement_type */ + if(shape == VIDOBJLAY_SHAPE_BINARY /* && hierarchy_type==0 */) { + bs->Skip(1); /* use_ref_shape */ + bs->Skip(1); /* use_ref_texture */ + bs->Skip(5); /* shape_hor_sampling_factor_n */ + bs->Skip(5); /* shape_hor_sampling_factor_m */ + bs->Skip(5); /* shape_vert_sampling_factor_n */ + bs->Skip(5); /* shape_vert_sampling_factor_m */ + } + return -1; + } + }else{ + //shape == BINARY_ONLY + if(vol_ver_id != 1){ + scalability = (bs->GetBit()!=0); + if(scalability){ + DPRINTF(XVID_DEBUG_ERROR, "scalability not supported\n"); + bs->Skip(4); /* ref_layer_id */ + bs->Skip(5); /* hor_sampling_factor_n */ + bs->Skip(5); /* hor_sampling_factor_m */ + bs->Skip(5); /* vert_sampling_factor_n */ + bs->Skip(5); /* vert_sampling_factor_m */ + return -1; + } + } + bs->Skip(1); /* resync_marker_disable */ + + } + + return (resize ? -3 : -2); //VOL + + } + break; + default: + { + //start_code == ? + //if(bs->ShowBits(24) == 0x000001) + if((start_code&0x00ffffff) == 0x000001){ + DPRINTF(XVID_DEBUG_STARTCODE, "<unknown: %x>\n", bs->ShowBits(32)); + } + bs->Skip(8); + } + } + } + } + +#if 0 + DPRINTF("*** WARNING: no vop_start_code found"); +#endif + return -1; //ignore it +} + +//---------------------------- + diff --git a/modules/xvid_dec/xvid_wce/bitstream.h b/modules/xvid_dec/xvid_wce/bitstream.h new file mode 100644 index 0000000..db27fbe --- /dev/null +++ b/modules/xvid_dec/xvid_wce/bitstream.h @@ -0,0 +1,116 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Bitstream reader/writer inlined functions and constants- + * + * Copyright (C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: bitstream.h,v 1.1.1.1 2005/07/13 14:36:12 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _BITSTREAM_H_ +#define _BITSTREAM_H_ + +#include "portab.h" +#include "decoder.h" + + +/***************************************************************************** + * Constants + ****************************************************************************/ + +/* comment any #defs we dont use */ + +#define VIDOBJ_START_CODE 0x00000100 /* ..0x0000011f */ +#define VIDOBJLAY_START_CODE 0x00000120 /* ..0x0000012f */ +#define VISOBJSEQ_START_CODE 0x000001b0 +#define VISOBJSEQ_STOP_CODE 0x000001b1 /* ??? */ +#define USERDATA_START_CODE 0x000001b2 +#define GRPOFVOP_START_CODE 0x000001b3 +/*#define VIDSESERR_ERROR_CODE 0x000001b4 */ +#define VISOBJ_START_CODE 0x000001b5 +#define VOP_START_CODE 0x000001b6 +/*#define STUFFING_START_CODE 0x000001c3 */ + + +#define VISOBJ_TYPE_VIDEO 1 +/*#define VISOBJ_TYPE_STILLTEXTURE 2 */ +/*#define VISOBJ_TYPE_MESH 3 */ +/*#define VISOBJ_TYPE_FBA 4 */ +/*#define VISOBJ_TYPE_3DMESH 5 */ + + +#define VIDOBJLAY_TYPE_SIMPLE 1 +/*#define VIDOBJLAY_TYPE_SIMPLE_SCALABLE 2 */ +/*#define VIDOBJLAY_TYPE_CORE 3 */ +/*#define VIDOBJLAY_TYPE_MAIN 4 */ +/*#define VIDOBJLAY_TYPE_NBIT 5 */ +/*#define VIDOBJLAY_TYPE_ANIM_TEXT 6 */ +/*#define VIDOBJLAY_TYPE_ANIM_MESH 7 */ +/*#define VIDOBJLAY_TYPE_SIMPLE_FACE 8 */ +/*#define VIDOBJLAY_TYPE_STILL_SCALABLE 9 */ +#define VIDOBJLAY_TYPE_ART_SIMPLE 10 +/*#define VIDOBJLAY_TYPE_CORE_SCALABLE 11 */ +/*#define VIDOBJLAY_TYPE_ACE 12 */ +/*#define VIDOBJLAY_TYPE_ADVANCED_SCALABLE_TEXTURE 13 */ +/*#define VIDOBJLAY_TYPE_SIMPLE_FBA 14 */ +/*#define VIDEOJLAY_TYPE_SIMPLE_STUDIO 15*/ +/*#define VIDEOJLAY_TYPE_CORE_STUDIO 16*/ +#define VIDOBJLAY_TYPE_ASP 17 +/*#define VIDOBJLAY_TYPE_FGS 18*/ + + +/*#define VIDOBJLAY_AR_SQUARE 1 */ +/*#define VIDOBJLAY_AR_625TYPE_43 2 */ +/*#define VIDOBJLAY_AR_525TYPE_43 3 */ +/*#define VIDOBJLAY_AR_625TYPE_169 8 */ +/*#define VIDOBJLAY_AR_525TYPE_169 9 */ +#define VIDOBJLAY_AR_EXTPAR 15 + + +#define VIDOBJLAY_SHAPE_RECTANGULAR 0 +#define VIDOBJLAY_SHAPE_BINARY 1 +#define VIDOBJLAY_SHAPE_BINARY_ONLY 2 +#define VIDOBJLAY_SHAPE_GRAYSCALE 3 + + +#define SPRITE_NONE 0 +#define SPRITE_STATIC 1 +#define SPRITE_GMC 2 + + + +#define READ_MARKER() bs->Skip(1) +//#define WRITE_MARKER() BitstreamPutBit(bs, 1) + +/* vop coding types */ +/* intra, prediction, backward, sprite, not_coded */ +#define I_VOP 0 +#define P_VOP 1 +#define B_VOP 2 +#define S_VOP 3 +#define N_VOP 4 + +/* resync-specific */ +#define NUMBITS_VP_RESYNC_MARKER 17 +#define RESYNC_MARKER 1 + +extern const dword scan_tables[3][64]; + + +#endif /* _BITSTREAM_H_ */ diff --git a/modules/xvid_dec/xvid_wce/decoder.cpp b/modules/xvid_dec/xvid_wce/decoder.cpp new file mode 100644 index 0000000..ae16b4d --- /dev/null +++ b/modules/xvid_dec/xvid_wce/decoder.cpp @@ -0,0 +1,1448 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Decoder Module - + * + * Copyright(C) 2002 MinChen <chenm001@163.com> + * 2002-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: decoder.cpp,v 1.4 2008/08/20 13:57:54 jeanlf Exp $ + * + ****************************************************************************/ + +#include "quant.h" +#include "quant_matrix.h" +#include "interpolate8x8.h" +#include "reduced.h" +#include "mbprediction.h" +#include "gmc.h" +#include "mem_align.h" + +#ifdef __SYMBIAN32__ +#include <e32base.h> +#endif + +//---------------------------- + +# define DECLARE_ALIGNED_MATRIX(name,sizex,sizey,type,alignment) \ + type name##_storage[(sizex)*(sizey)+(alignment)-1]; \ + type * name = (type *) (((int) name##_storage+(alignment - 1)) & ~((int)(alignment)-1)) + +//---------------------------- +//---------------------------- +/* K = 4 */ +static const dword roundtab_76[16] = { 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1 }; + +/* K = 1 */ +const dword roundtab_79[4] = { 0, 1, 0, 0 }; + +//---------------------------- + +int S_decoder::Resize(){ + //free existing + image_destroy(&cur, edged_width, edged_height); + image_destroy(&refn[0], edged_width, edged_height); + image_destroy(&refn[1], edged_width, edged_height); + image_destroy(&tmp, edged_width, edged_height); + image_destroy(&qtmp, edged_width, edged_height); + + image_destroy(&gmc, edged_width, edged_height); + + if(last_mbs) + xvid_free(last_mbs); + if(mbs) + xvid_free(mbs); + + //realloc + mb_width = (width + 15) / 16; + mb_height = (height + 15) / 16; + + edged_width = 16 * mb_width + 2 * EDGE_SIZE; + edged_height = 16 * mb_height + 2 * EDGE_SIZE; + + if(image_create(&cur, edged_width, edged_height)){ + xvid_free(this); + return XVID_ERR_MEMORY; + } + + if(image_create(&refn[0], edged_width, edged_height)){ + image_destroy(&cur, edged_width, edged_height); + xvid_free(this); + return XVID_ERR_MEMORY; + } + + //support B-frame to reference last 2 frame + if(image_create(&refn[1], edged_width, edged_height)){ + image_destroy(&cur, edged_width, edged_height); + image_destroy(&refn[0], edged_width, edged_height); + xvid_free(this); + return XVID_ERR_MEMORY; + } + if(image_create(&tmp, edged_width, edged_height)){ + image_destroy(&cur, edged_width, edged_height); + image_destroy(&refn[0], edged_width, edged_height); + image_destroy(&refn[1], edged_width, edged_height); + xvid_free(this); + return XVID_ERR_MEMORY; + } + + if(image_create(&qtmp, edged_width, edged_height)){ + image_destroy(&cur, edged_width, edged_height); + image_destroy(&refn[0], edged_width, edged_height); + image_destroy(&refn[1], edged_width, edged_height); + image_destroy(&tmp, edged_width, edged_height); + xvid_free(this); + return XVID_ERR_MEMORY; + } + + if(image_create(&gmc, edged_width, edged_height)){ + image_destroy(&qtmp, edged_width, edged_height); + image_destroy(&cur, edged_width, edged_height); + image_destroy(&refn[0], edged_width, edged_height); + image_destroy(&refn[1], edged_width, edged_height); + image_destroy(&tmp, edged_width, edged_height); + xvid_free(this); + return XVID_ERR_MEMORY; + } + + mbs = (MACROBLOCK*)xvid_malloc(sizeof(MACROBLOCK) * mb_width * mb_height, CACHE_LINE); + if(mbs == NULL){ + image_destroy(&cur, edged_width, edged_height); + image_destroy(&refn[0], edged_width, edged_height); + image_destroy(&refn[1], edged_width, edged_height); + image_destroy(&tmp, edged_width, edged_height); + image_destroy(&qtmp, edged_width, edged_height); + xvid_free(this); + return XVID_ERR_MEMORY; + } + MemSet(mbs, 0, sizeof(MACROBLOCK) * mb_width * mb_height); + + //for skip MB flag + last_mbs = (MACROBLOCK*)xvid_malloc(sizeof(MACROBLOCK) * mb_width * mb_height, CACHE_LINE); + if(!last_mbs){ + xvid_free(mbs); + image_destroy(&cur, edged_width, edged_height); + image_destroy(&refn[0], edged_width, edged_height); + image_destroy(&refn[1], edged_width, edged_height); + image_destroy(&tmp, edged_width, edged_height); + image_destroy(&qtmp, edged_width, edged_height); + xvid_free(this); + return XVID_ERR_MEMORY; + } + MemSet(last_mbs, 0, sizeof(MACROBLOCK) * mb_width * mb_height); + + return 0; +} + +//---------------------------- + +int decoder_create(xvid_dec_create_t *create){ + + if(XVID_VERSION_MAJOR(create->version) != 1) /* v1.x.x */ + return XVID_ERR_VERSION; + + S_decoder *dec = (S_decoder*)xvid_malloc(sizeof(S_decoder), CACHE_LINE); + //S_decoder *dec = new(ELeave) S_decoder(create); + if(!dec) + return XVID_ERR_MEMORY; + + return dec->Init(create); +} + +//---------------------------- + +S_decoder::S_decoder(xvid_dec_create_t *create): +#ifdef PROFILE + prof(*create->prof), +#endif + time_inc_resolution(0), + fixed_time_inc(0), + time_inc_bits(0), + shape(0), + quant_bits(0), + quant_type(0), + mpeg_quant_matrices(0), + quarterpel(0), + complexity_estimation_disable(0), + interlacing(false), + top_field_first(0), + alternate_vertical_scan(0), + aspect_ratio(0), + par_width(0), + par_height(0), + sprite_enable(0), + sprite_warping_points(0), + sprite_warping_accuracy(0), + sprite_brightness_change(0), + newpred_enable(0), + reduced_resolution_enable(false), + bs_version(0), + width(0), + height(0), + edged_width(0), + edged_height(0), + mb_width(0), + mb_height(0), + mbs(0), + last_mbs(0), + last_coding_type(0), + frames(0), + time(0), + time_base(0), + last_time_base(0), + last_non_b_time(0), + time_pp(0), + time_bp(0), + fixed_dimensions(false), + scalability(false), + low_delay(false), + low_delay_default(false), + last_reduced_resolution(false), + packed_mode(false) +{ + MemSet(&p_fmv, 0, sizeof(p_fmv)); + MemSet(&p_bmv, 0, sizeof(p_bmv)); +} + +//---------------------------- + +S_decoder::~S_decoder(){ + + xvid_free(last_mbs); + xvid_free(mbs); + + //image based GMC + image_destroy(&gmc, edged_width, edged_height); + + image_destroy(&refn[0], edged_width, edged_height); + image_destroy(&refn[1], edged_width, edged_height); + image_destroy(&tmp, edged_width, edged_height); + image_destroy(&qtmp, edged_width, edged_height); + image_destroy(&cur, edged_width, edged_height); + xvid_free(mpeg_quant_matrices); +} + +//---------------------------- + +int S_decoder::Init(xvid_dec_create_t *create){ + + mpeg_quant_matrices = (dword*)xvid_malloc(sizeof(dword) * 64 * 8, CACHE_LINE); + if(!mpeg_quant_matrices){ + delete this; + return XVID_ERR_MEMORY; + } + + create->handle = this; + + width = create->width; + height = create->height; + + cur.Null(); + refn[0].Null(); + refn[1].Null(); + tmp.Null(); + qtmp.Null(); + + //image based GMC + gmc.Null(); + + + mbs = NULL; + last_mbs = NULL; + + init_mpeg_matrix(mpeg_quant_matrices); + + //For B-frame support (used to save reference frame's time + frames = 0; + time = time_base = last_time_base = 0; + low_delay = false; + packed_mode = false; + + fixed_dimensions = (width > 0 && height > 0); + + init_vlc_tables(); + idct_int32_init(); + + if(fixed_dimensions) + return Resize(); + return 0; +} + +//---------------------------- + +static const int dquant_table[4] = { + -1, -2, 1, 2 +}; + +//---------------------------- + +static dword get_dc_scaler(dword quant, dword lum){ + + if(quant < 5) + return 8; + if(quant < 25 && !lum) + return (quant + 13) / 2; + if(quant < 9) + return 2 * quant; + if(quant < 25) + return quant + 8; + if(lum) + return 2 * quant - 16; + return quant - 6; +} + +//--------------------------- + +#ifdef _ARM_ +extern"C" +void XVID_ClearMatrix(void *dst); + +#else +void XVID_ClearMatrix(void *dst); +#ifndef USE_ARM_ASM +inline void XVID_ClearMatrix(void *dst){ MemSet(dst, 0, 64 * sizeof(int)); } +#endif +#endif + +//-------------------------- +/* decode an intra macroblock */ +void S_decoder::MBIntra(MACROBLOCK *pMB, dword x_pos, dword y_pos, dword acpred_flag, dword cbp, Bitstream *bs, + dword quant, dword intra_dc_threshold, dword bound, bool reduced_resolution){ + + DECLARE_ALIGNED_MATRIX(block, 6, 64, int, CACHE_LINE); + DECLARE_ALIGNED_MATRIX(data, 6, 64, int, CACHE_LINE); + + int i; + + dword stride = edged_width; + const dword stride2 = stride / 2; + byte *pY_Cur, *pU_Cur, *pV_Cur; + + if(reduced_resolution) { + pY_Cur = cur.y + (y_pos << 5) * stride + (x_pos << 5); + pU_Cur = cur.u + (y_pos << 4) * stride2 + (x_pos << 4); + pV_Cur = cur.v + (y_pos << 4) * stride2 + (x_pos << 4); + }else{ + pY_Cur = cur.y + (y_pos << 4) * stride + (x_pos << 4); + pU_Cur = cur.u + (y_pos << 3) * stride2 + (x_pos << 3); + pV_Cur = cur.v + (y_pos << 3) * stride2 + (x_pos << 3); + } + + MemSet(block, 0, 6 * 64 * sizeof(int)); //clear + + const dword iQuant = pMB->quant; + for(i = 0; i < 6; i++){ + dword iDcScaler = get_dc_scaler(iQuant, i < 4); + int predictors[8]; + int start_coeff; + + predict_acdc(mbs, x_pos, y_pos, mb_width, i, &block[i * 64], iQuant, iDcScaler, predictors, bound); + if(!acpred_flag) + pMB->acpred_directions[i] = 0; + + if(quant < intra_dc_threshold){ + PROF_S(PROF_1); + int dc_size; + int dc_dif; + + dc_size = i < 4 ? bs->get_dc_size_lum() : bs->get_dc_size_chrom(); + dc_dif = dc_size ? bs->get_dc_dif(dc_size) : 0; + + if (dc_size > 8) { + bs->Skip(1); /* marker */ + } + + block[i * 64 + 0] = dc_dif; + start_coeff = 1; + + DPRINTF(XVID_DEBUG_COEFF,"block[0] %i\n", dc_dif); + PROF_E(PROF_1); + }else{ + start_coeff = 0; + } + if(cbp & (1 << (5 - i))){ + //coded + int direction = alternate_vertical_scan ? 2 : pMB->acpred_directions[i]; + get_intra_block(bs, &block[i * 64], direction, start_coeff); + } + add_acdc(pMB, i, &block[i * 64], iDcScaler, predictors); + if(quant_type == 0){ + dequant_h263_intra(&data[i * 64], &block[i * 64], iQuant, iDcScaler, mpeg_quant_matrices); + }else{ + dequant_mpeg_intra(&data[i * 64], &block[i * 64], iQuant, iDcScaler, mpeg_quant_matrices); + } + InverseDiscreteCosineTransform(&data[i * 64]); + } + + dword next_block = stride * 8; + if(interlacing && pMB->field_dct){ + next_block = stride; + stride *= 2; + } + if(reduced_resolution){ + next_block*=2; + copy_upsampled_8x8_16to8(pY_Cur, &data[0 * 64], stride); + copy_upsampled_8x8_16to8(pY_Cur + 16, &data[1 * 64], stride); + copy_upsampled_8x8_16to8(pY_Cur + next_block, &data[2 * 64], stride); + copy_upsampled_8x8_16to8(pY_Cur + 16 + next_block, &data[3 * 64], stride); + copy_upsampled_8x8_16to8(pU_Cur, &data[4 * 64], stride2); + copy_upsampled_8x8_16to8(pV_Cur, &data[5 * 64], stride2); + }else{ + transfer_16to8copy(pY_Cur, &data[0 * 64], stride); + transfer_16to8copy(pY_Cur + 8, &data[1 * 64], stride); + transfer_16to8copy(pY_Cur + next_block, &data[2 * 64], stride); + transfer_16to8copy(pY_Cur + 8 + next_block, &data[3 * 64], stride); + transfer_16to8copy(pU_Cur, &data[4 * 64], stride2); + transfer_16to8copy(pV_Cur, &data[5 * 64], stride2); + } +} + +//---------------------------- + +void S_decoder::mb_decode(const dword cbp, Bitstream * bs, byte * pY_Cur, byte * pU_Cur, byte * pV_Cur, bool reduced_resolution, const MACROBLOCK *pMB){ + + DECLARE_ALIGNED_MATRIX(block, 1, 64, int, CACHE_LINE); + DECLARE_ALIGNED_MATRIX(data, 6, 64, int, CACHE_LINE); + + int i; + int stride = edged_width; + const int stride2 = stride/2; + + const dword iQuant = pMB->quant; + const int direction = alternate_vertical_scan ? 2 : 0; + const quant_interFuncPtr dequant = quant_type == 0 ? dequant_h263_inter : dequant_mpeg_inter; + for(i = 0; i < 6; i++){ + if(cbp & (1 << (5 - i))){ + //coded + XVID_ClearMatrix(block); + get_inter_block(bs, block, direction); + dequant(&data[i * 64], block, iQuant, mpeg_quant_matrices); + InverseDiscreteCosineTransform(&data[i * 64]); + } + } + + int next_block = stride * (reduced_resolution ? 16 : 8); + if(interlacing && pMB->field_dct){ + next_block = stride; + stride *= 2; + } + + if(reduced_resolution){ + if(cbp & 32) + add_upsampled_8x8_16to8(pY_Cur, &data[0 * 64], stride); + if(cbp & 16) + add_upsampled_8x8_16to8(pY_Cur + 16, &data[1 * 64], stride); + if(cbp & 8) + add_upsampled_8x8_16to8(pY_Cur + next_block, &data[2 * 64], stride); + if(cbp & 4) + add_upsampled_8x8_16to8(pY_Cur + 16 + next_block, &data[3 * 64], stride); + if(cbp & 2) + add_upsampled_8x8_16to8(pU_Cur, &data[4 * 64], stride2); + if(cbp & 1) + add_upsampled_8x8_16to8(pV_Cur, &data[5 * 64], stride2); + }else{ + if(cbp & 32) + transfer_16to8add(pY_Cur, &data[0 * 64], stride); + if(cbp & 16) + transfer_16to8add(pY_Cur + 8, &data[1 * 64], stride); + if(cbp & 8) + transfer_16to8add(pY_Cur + next_block, &data[2 * 64], stride); + if(cbp & 4) + transfer_16to8add(pY_Cur + 8 + next_block, &data[3 * 64], stride); + if(cbp & 2) + transfer_16to8add(pU_Cur, &data[4 * 64], stride2); + if(cbp & 1) + transfer_16to8add(pV_Cur, &data[5 * 64], stride2); + } +} + +//---------------------------- + +void S_decoder::DecodeInterMacroBlock(const MACROBLOCK * pMB, dword x_pos, dword y_pos, dword cbp, Bitstream *bs, + bool rounding, bool reduced_resolution, int ref){ + + dword stride = edged_width; + dword stride2 = stride / 2; + dword i; + + byte *pY_Cur, *pU_Cur, *pV_Cur; + + int uv_dx, uv_dy; + VECTOR mv[4]; //local copy of mvs + + if(reduced_resolution){ + pY_Cur = cur.y + (y_pos << 5) * stride + (x_pos << 5); + pU_Cur = cur.u + (y_pos << 4) * stride2 + (x_pos << 4); + pV_Cur = cur.v + (y_pos << 4) * stride2 + (x_pos << 4); + for (i = 0; i < 4; i++) { + mv[i].x = RRV_MV_SCALEUP(pMB->mvs[i].x); + mv[i].y = RRV_MV_SCALEUP(pMB->mvs[i].y); + } + }else{ + pY_Cur = cur.y + (y_pos << 4) * stride + (x_pos << 4); + pU_Cur = cur.u + (y_pos << 3) * stride2 + (x_pos << 3); + pV_Cur = cur.v + (y_pos << 3) * stride2 + (x_pos << 3); + for(i = 0; i < 4; i++) + mv[i] = pMB->mvs[i]; + } + + if(pMB->mode != MODE_INTER4V){ + //INTER, INTER_Q, NOT_CODED, FORWARD, BACKWARD + + uv_dx = mv[0].x; + uv_dy = mv[0].y; + if (quarterpel) { + uv_dx /= 2; + uv_dy /= 2; + } + uv_dx = (uv_dx >> 1) + roundtab_79[uv_dx & 0x3]; + uv_dy = (uv_dy >> 1) + roundtab_79[uv_dy & 0x3]; + + if(reduced_resolution) + interpolate32x32_switch(cur.y, refn[0].y, 32*x_pos, 32*y_pos, mv[0].x, mv[0].y, stride, rounding); + else + if(quarterpel) + interpolate16x16_quarterpel(cur.y, refn[ref].y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos, 16*y_pos, mv[0].x, mv[0].y, stride, rounding); + else + interpolate16x16_switch(cur.y, refn[ref].y, 16*x_pos, 16*y_pos, mv[0].x, mv[0].y, stride, rounding); + }else{ + //MODE_INTER4V + + if(quarterpel) { + uv_dx = (mv[0].x / 2) + (mv[1].x / 2) + (mv[2].x / 2) + (mv[3].x / 2); + uv_dy = (mv[0].y / 2) + (mv[1].y / 2) + (mv[2].y / 2) + (mv[3].y / 2); + }else{ + uv_dx = mv[0].x + mv[1].x + mv[2].x + mv[3].x; + uv_dy = mv[0].y + mv[1].y + mv[2].y + mv[3].y; + } + + uv_dx = (uv_dx >> 3) + roundtab_76[uv_dx & 0xf]; + uv_dy = (uv_dy >> 3) + roundtab_76[uv_dy & 0xf]; + + if(reduced_resolution){ + interpolate16x16_switch(cur.y, refn[0].y, 32*x_pos, 32*y_pos, mv[0].x, mv[0].y, stride, rounding); + interpolate16x16_switch(cur.y, refn[0].y, 32*x_pos + 16, 32*y_pos, mv[1].x, mv[1].y, stride, rounding); + interpolate16x16_switch(cur.y, refn[0].y, 32*x_pos, 32*y_pos + 16, mv[2].x, mv[2].y, stride, rounding); + interpolate16x16_switch(cur.y, refn[0].y, 32*x_pos + 16, 32*y_pos + 16, mv[3].x, mv[3].y, stride, rounding); + interpolate16x16_switch(cur.u, refn[0].u, 16*x_pos, 16*y_pos, uv_dx, uv_dy, stride2, rounding); + interpolate16x16_switch(cur.v, refn[0].v, 16*x_pos, 16*y_pos, uv_dx, uv_dy, stride2, rounding); + }else + if(quarterpel){ + interpolate8x8_quarterpel(cur.y, refn[0].y , qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos, 16*y_pos, mv[0].x, mv[0].y, stride, rounding); + interpolate8x8_quarterpel(cur.y, refn[0].y , qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos + 8, 16*y_pos, mv[1].x, mv[1].y, stride, rounding); + interpolate8x8_quarterpel(cur.y, refn[0].y , qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos, 16*y_pos + 8, mv[2].x, mv[2].y, stride, rounding); + interpolate8x8_quarterpel(cur.y, refn[0].y , qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos + 8, 16*y_pos + 8, mv[3].x, mv[3].y, stride, rounding); + } else { + interpolate8x8_switch(cur.y, refn[0].y , 16*x_pos, 16*y_pos, mv[0].x, mv[0].y, stride, rounding); + interpolate8x8_switch(cur.y, refn[0].y , 16*x_pos + 8, 16*y_pos, mv[1].x, mv[1].y, stride, rounding); + interpolate8x8_switch(cur.y, refn[0].y , 16*x_pos, 16*y_pos + 8, mv[2].x, mv[2].y, stride, rounding); + interpolate8x8_switch(cur.y, refn[0].y , 16*x_pos + 8, 16*y_pos + 8, mv[3].x, mv[3].y, stride, rounding); + } + } + //chroma + if(reduced_resolution){ + interpolate16x16_switch(cur.u, refn[0].u, 16*x_pos, 16*y_pos, uv_dx, uv_dy, stride2, rounding); + interpolate16x16_switch(cur.v, refn[0].v, 16*x_pos, 16*y_pos, uv_dx, uv_dy, stride2, rounding); + }else{ + interpolate8x8_switch(cur.u, refn[ref].u, 8*x_pos, 8*y_pos, uv_dx, uv_dy, stride2, rounding); + interpolate8x8_switch(cur.v, refn[ref].v, 8*x_pos, 8*y_pos, uv_dx, uv_dy, stride2, rounding); + } + if(cbp) + mb_decode(cbp, bs, pY_Cur, pU_Cur, pV_Cur, reduced_resolution, pMB); +} + +//---------------------------- + +inline int gmc_sanitize(int value, int quarterpel, int fcode){ + + int length = 1 << (fcode+4); + +#if 0 + if (quarterpel) value *= 2; +#endif + + if (value < -length) + return -length; + else if (value >= length) + return length-1; + else return value; +} + +//---------------------------- + +void S_decoder::mbgmc(MACROBLOCK *pMB, dword x_pos, dword y_pos, dword fcode, dword cbp, Bitstream * bs, bool rounding){ + + const dword stride = edged_width; + const dword stride2 = stride / 2; + + byte *const pY_Cur=cur.y + (y_pos << 4) * stride + (x_pos << 4); + byte *const pU_Cur=cur.u + (y_pos << 3) * stride2 + (x_pos << 3); + byte *const pV_Cur=cur.v + (y_pos << 3) * stride2 + (x_pos << 3); + + NEW_GMC_DATA *gmc_data = &new_gmc_data; + + pMB->mvs[0] = pMB->mvs[1] = pMB->mvs[2] = pMB->mvs[3] = pMB->amv; + + //this is where the calculations are done + gmc_data->predict_16x16(gmc_data, cur.y + y_pos*16*stride + x_pos*16, refn[0].y, stride, stride, x_pos, y_pos, rounding); + gmc_data->predict_8x8(gmc_data, cur.u + y_pos*8*stride2 + x_pos*8, refn[0].u, cur.v + y_pos*8*stride2 + x_pos*8, refn[0].v, stride2, stride2, x_pos, y_pos, rounding); + gmc_data->get_average_mv(gmc_data, &pMB->amv, x_pos, y_pos, quarterpel); + + pMB->amv.x = gmc_sanitize(pMB->amv.x, quarterpel, fcode); + pMB->amv.y = gmc_sanitize(pMB->amv.y, quarterpel, fcode); + + pMB->mvs[0] = pMB->mvs[1] = pMB->mvs[2] = pMB->mvs[3] = pMB->amv; + + if(cbp) + mb_decode(cbp, bs, pY_Cur, pU_Cur, pV_Cur, 0, pMB); +} + +//---------------------------- + +void S_decoder::I_Frame(Bitstream * bs, bool reduced_resolution, int quant, int intra_dc_threshold){ + + dword bound; + dword x, y; + //dword mb_width = mb_width; + //dword mb_height = mb_height; + + if (reduced_resolution) { + mb_width = (width + 31) / 32; + mb_height = (height + 31) / 32; + } + + bound = 0; + + for (y = 0; y < mb_height; y++) { + for (x = 0; x < mb_width; x++) { + dword mcbpc; + dword cbpc; + dword acpred_flag; + dword cbpy; + dword cbp; + + while(bs->ShowBits(9) == 1) + bs->Skip(9); + + if(bs->check_resync_marker(0)){ + bound = read_video_packet_header(bs, 0, &quant, NULL, NULL, &intra_dc_threshold); + x = bound % mb_width; + y = bound / mb_width; + } + MACROBLOCK *mb = &mbs[y * mb_width + x]; + + //DPRINTF(XVID_DEBUG_MB, "macroblock (%i,%i) %08x\n", x, y, bs->ShowBits(32)); + + mcbpc = bs->get_mcbpc_intra(); + mb->mode = mcbpc & 7; + cbpc = (mcbpc >> 4); + + acpred_flag = bs->GetBit(); + + cbpy = bs->GetCbpy(1); + cbp = (cbpy << 2) | cbpc; + + if (mb->mode == MODE_INTRA_Q) { + quant += dquant_table[bs->GetBits(2)]; + if (quant > 31) { + quant = 31; + } else if (quant < 1) { + quant = 1; + } + } + mb->quant = quant; + mb->mvs[0].x = mb->mvs[0].y = + mb->mvs[1].x = mb->mvs[1].y = + mb->mvs[2].x = mb->mvs[2].y = + mb->mvs[3].x = mb->mvs[3].y =0; + + if(interlacing){ + mb->field_dct = bs->GetBit(); + DPRINTF(XVID_DEBUG_MB,"deci: field_dct: %i\n", mb->field_dct); + } + MBIntra(mb, x, y, acpred_flag, cbp, bs, quant, intra_dc_threshold, bound, reduced_resolution); + } + /* +#ifdef XVID_CSP_SLICE + if(out_frm) + output_slice(&cur, edged_width,width,out_frm,0,y,mb_width); +#endif + */ + } +} + +//---------------------------- + +void S_decoder::GetMotionVector(Bitstream * bs, int x, int y, int k, VECTOR *ret_mv, int fcode, int bound){ + + const int scale_fac = 1 << (fcode - 1); + const int high = (32 * scale_fac) - 1; + const int low = ((-32) * scale_fac); + const int range = (64 * scale_fac); + + const VECTOR pmv = get_pmv2(mbs, mb_width, bound, x, y, k); + VECTOR mv; + + mv.x = bs->GetMoveVector(fcode); + mv.y = bs->GetMoveVector(fcode); + + //DPRINTF(XVID_DEBUG_MV,"mv_diff (%i,%i) pred (%i,%i) result (%i,%i)\n", mv.x, mv.y, pmv.x, pmv.y, mv.x+pmv.x, mv.y+pmv.y); + + mv.x += pmv.x; + mv.y += pmv.y; + + if(mv.x < low){ + mv.x += range; + }else + if(mv.x > high){ + mv.x -= range; + } + + if(mv.y < low){ + mv.y += range; + }else + if(mv.y > high){ + mv.y -= range; + } + + ret_mv->x = mv.x; + ret_mv->y = mv.y; +} + +//---------------------------- +// for P_VOP set gmc_warp to NULL +void S_decoder::P_Frame(Bitstream *bs, int rounding, bool reduced_resolution, int quant, int fcode, + int intra_dc_threshold, const WARPPOINTS *gmc_warp){ + + //dword mb_width = mb_width; + //dword mb_height = mb_height; + + if(reduced_resolution){ + mb_width = (width + 31) / 32; + mb_height = (height + 31) / 32; + } + + SetEdges(refn[0]); + + if(gmc_warp){ + //accuracy: 0==1/2, 1=1/4, 2=1/8, 3=1/16 + generate_GMCparameters(sprite_warping_points, sprite_warping_accuracy, gmc_warp, width, height, &new_gmc_data); + //image warping is done block-based in decoder_mbgmc(), now + } + + PROF_S(PROF_FRM_P); + dword bound = 0; + + for(dword y = 0; y < mb_height; y++){ + //int cp_mb = 0, st_mb = 0; + for(dword x = 0; x < mb_width; x++){ + //skip stuffing + while(bs->ShowBits(10) == 1) + bs->Skip(10); + + if(bs->check_resync_marker(fcode - 1)){ + bound = read_video_packet_header(bs, fcode - 1, &quant, &fcode, NULL, &intra_dc_threshold); + x = bound % mb_width; + y = bound / mb_width; + } + MACROBLOCK *mb = &mbs[y * mb_width + x]; + + //DPRINTF(XVID_DEBUG_MB, "macroblock (%i,%i) %08x\n", x, y, bs->ShowBits(32)); + + if(!(bs->GetBit())){ + //block _is_ coded + int mcsel = 0; //mcsel: '0'=local motion, '1'=GMC + + //cp_mb++; + dword mcbpc = bs->GetMcbpcInter(); + mb->mode = mcbpc & 7; + dword cbpc = (mcbpc >> 4); + + DPRINTF(XVID_DEBUG_MB, "mode %i\n", mb->mode); + DPRINTF(XVID_DEBUG_MB, "cbpc %i\n", cbpc); + + dword intra = (mb->mode == MODE_INTRA || mb->mode == MODE_INTRA_Q); + + dword acpred_flag = 0; + if(gmc_warp && (mb->mode == MODE_INTER || mb->mode == MODE_INTER_Q)) + mcsel = bs->GetBit(); + else + if(intra) + acpred_flag = bs->GetBit(); + + dword cbpy = bs->GetCbpy(intra); + //DPRINTF(XVID_DEBUG_MB, "cbpy %i mcsel %i \n", cbpy,mcsel); + + dword cbp = (cbpy << 2) | cbpc; + + if(mb->mode == MODE_INTER_Q || mb->mode == MODE_INTRA_Q){ + int dquant = dquant_table[bs->GetBits(2)]; + DPRINTF(XVID_DEBUG_MB, "dquant %i\n", dquant); + quant += dquant; + if (quant > 31) { + quant = 31; + } else if (quant < 1) { + quant = 1; + } + DPRINTF(XVID_DEBUG_MB, "quant %i\n", quant); + } + mb->quant = quant; + + if(interlacing){ + if((cbp || intra) && !mcsel){ + mb->field_dct = bs->GetBit(); + DPRINTF(XVID_DEBUG_MB,"decp: field_dct: %i\n", mb->field_dct); + } + + if(mb->mode == MODE_INTER || mb->mode == MODE_INTER_Q){ + mb->field_pred = bs->GetBit(); + DPRINTF(XVID_DEBUG_MB, "decp: field_pred: %i\n", mb->field_pred); + + if(mb->field_pred){ + mb->field_for_top = bs->GetBit(); + DPRINTF(XVID_DEBUG_MB,"decp: field_for_top: %i\n", mb->field_for_top); + mb->field_for_bot = bs->GetBit(); + DPRINTF(XVID_DEBUG_MB,"decp: field_for_bot: %i\n", mb->field_for_bot); + } + } + } + if(mcsel){ + mbgmc(mb, x, y, fcode, cbp, bs, rounding); + PROF_E(PROF_0); + continue; + + }else + if(mb->mode == MODE_INTER || mb->mode == MODE_INTER_Q){ + if(interlacing && mb->field_pred){ + GetMotionVector(bs, x, y, 0, &mb->mvs[0], fcode, bound); + GetMotionVector(bs, x, y, 0, &mb->mvs[1], fcode, bound); + }else{ + GetMotionVector(bs, x, y, 0, &mb->mvs[0], fcode, bound); + mb->mvs[1] = mb->mvs[2] = mb->mvs[3] = mb->mvs[0]; + } + }else + if(mb->mode == MODE_INTER4V){ + GetMotionVector(bs, x, y, 0, &mb->mvs[0], fcode, bound); + GetMotionVector(bs, x, y, 1, &mb->mvs[1], fcode, bound); + GetMotionVector(bs, x, y, 2, &mb->mvs[2], fcode, bound); + GetMotionVector(bs, x, y, 3, &mb->mvs[3], fcode, bound); + }else{ /* MODE_INTRA, MODE_INTRA_Q */ + mb->mvs[0].x = mb->mvs[1].x = mb->mvs[2].x = mb->mvs[3].x = 0; + mb->mvs[0].y = mb->mvs[1].y = mb->mvs[2].y = mb->mvs[3].y = 0; + MBIntra(mb, x, y, acpred_flag, cbp, bs, quant, intra_dc_threshold, bound, reduced_resolution); + continue; + } + PROF_S(PROF_5); + DecodeInterMacroBlock(mb, x, y, cbp, bs, rounding, reduced_resolution, 0); + PROF_E(PROF_5); + }else + if(gmc_warp){ + //a not coded S(GMC)-VOP macroblock + mb->mode = MODE_NOT_CODED_GMC; + mbgmc(mb, x, y, fcode, 0x00, bs, rounding); + + /* +#ifdef XVID_CSP_SLICE + if(out_frm && cp_mb > 0) { + output_slice(&cur, edged_width,width,out_frm,st_mb,y,cp_mb); + cp_mb = 0; + } + st_mb = x+1; +#endif + */ + }else{ + //not coded P_VOP macroblock + mb->mode = MODE_NOT_CODED; + + mb->mvs[0].x = mb->mvs[1].x = mb->mvs[2].x = mb->mvs[3].x = 0; + mb->mvs[0].y = mb->mvs[1].y = mb->mvs[2].y = mb->mvs[3].y = 0; + + DecodeInterMacroBlock(mb, x, y, 0, bs, rounding, reduced_resolution, 0); + /* +#ifdef XVID_CSP_SLICE + if(out_frm && cp_mb > 0) { + output_slice(&cur, edged_width,width,out_frm,st_mb,y,cp_mb); + cp_mb = 0; + } + st_mb = x+1; +#endif + */ + } + } + /* +#ifdef XVID_CSP_SLICE + if(out_frm && cp_mb > 0) + output_slice(&cur, edged_width,width,out_frm,st_mb,y,cp_mb); +#endif + */ + } + PROF_E(PROF_FRM_P); +} + +//---------------------------- +/* decode B-frame motion vector */ +static void get_b_motion_vector(Bitstream * bs, VECTOR * mv, int fcode, const VECTOR pmv){ + + const int scale_fac = 1 << (fcode - 1); + const int high = (32 * scale_fac) - 1; + const int low = ((-32) * scale_fac); + const int range = (64 * scale_fac); + + int mv_x = bs->GetMoveVector(fcode); + int mv_y = bs->GetMoveVector(fcode); + + mv_x += pmv.x; + mv_y += pmv.y; + + if (mv_x < low) + mv_x += range; + else if (mv_x > high) + mv_x -= range; + + if (mv_y < low) + mv_y += range; + else if (mv_y > high) + mv_y -= range; + + mv->x = mv_x; + mv->y = mv_y; +} + +//---------------------------- +/* decode an B-frame direct & interpolate macroblock */ +void S_decoder::BFrameInterpolateMBInter(const IMAGE &forward, const IMAGE &backward, const MACROBLOCK *pMB, dword x_pos, dword y_pos, Bitstream *bs, int direct){ + + PROF_S(PROF_BINT_MBI); + dword stride = edged_width; + dword stride2 = stride / 2; + int uv_dx, uv_dy; + int b_uv_dx, b_uv_dy; + byte *pY_Cur, *pU_Cur, *pV_Cur; + const dword cbp = pMB->cbp; + + pY_Cur = cur.y + (y_pos << 4) * stride + (x_pos << 4); + pU_Cur = cur.u + (y_pos << 3) * stride2 + (x_pos << 3); + pV_Cur = cur.v + (y_pos << 3) * stride2 + (x_pos << 3); + + if(!direct){ + uv_dx = pMB->mvs[0].x; + uv_dy = pMB->mvs[0].y; + + b_uv_dx = pMB->b_mvs[0].x; + b_uv_dy = pMB->b_mvs[0].y; + + if(quarterpel){ + uv_dx /= 2; + uv_dy /= 2; + b_uv_dx /= 2; + b_uv_dy /= 2; + } + uv_dx = (uv_dx >> 1) + roundtab_79[uv_dx & 0x3]; + uv_dy = (uv_dy >> 1) + roundtab_79[uv_dy & 0x3]; + + b_uv_dx = (b_uv_dx >> 1) + roundtab_79[b_uv_dx & 0x3]; + b_uv_dy = (b_uv_dy >> 1) + roundtab_79[b_uv_dy & 0x3]; + }else{ + if(quarterpel) { + uv_dx = (pMB->mvs[0].x / 2) + (pMB->mvs[1].x / 2) + (pMB->mvs[2].x / 2) + (pMB->mvs[3].x / 2); + uv_dy = (pMB->mvs[0].y / 2) + (pMB->mvs[1].y / 2) + (pMB->mvs[2].y / 2) + (pMB->mvs[3].y / 2); + b_uv_dx = (pMB->b_mvs[0].x / 2) + (pMB->b_mvs[1].x / 2) + (pMB->b_mvs[2].x / 2) + (pMB->b_mvs[3].x / 2); + b_uv_dy = (pMB->b_mvs[0].y / 2) + (pMB->b_mvs[1].y / 2) + (pMB->b_mvs[2].y / 2) + (pMB->b_mvs[3].y / 2); + } else { + uv_dx = pMB->mvs[0].x + pMB->mvs[1].x + pMB->mvs[2].x + pMB->mvs[3].x; + uv_dy = pMB->mvs[0].y + pMB->mvs[1].y + pMB->mvs[2].y + pMB->mvs[3].y; + b_uv_dx = pMB->b_mvs[0].x + pMB->b_mvs[1].x + pMB->b_mvs[2].x + pMB->b_mvs[3].x; + b_uv_dy = pMB->b_mvs[0].y + pMB->b_mvs[1].y + pMB->b_mvs[2].y + pMB->b_mvs[3].y; + } + + uv_dx = (uv_dx >> 3) + roundtab_76[uv_dx & 0xf]; + uv_dy = (uv_dy >> 3) + roundtab_76[uv_dy & 0xf]; + b_uv_dx = (b_uv_dx >> 3) + roundtab_76[b_uv_dx & 0xf]; + b_uv_dy = (b_uv_dy >> 3) + roundtab_76[b_uv_dy & 0xf]; + } + + if(quarterpel){ + if(!direct){ + interpolate16x16_quarterpel(cur.y, forward.y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos, 16*y_pos, pMB->mvs[0].x, pMB->mvs[0].y, stride, 0); + }else{ + interpolate8x8_quarterpel(cur.y, forward.y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos, 16*y_pos, pMB->mvs[0].x, pMB->mvs[0].y, stride, 0); + interpolate8x8_quarterpel(cur.y, forward.y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos + 8, 16*y_pos, pMB->mvs[1].x, pMB->mvs[1].y, stride, 0); + interpolate8x8_quarterpel(cur.y, forward.y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos, 16*y_pos + 8, pMB->mvs[2].x, pMB->mvs[2].y, stride, 0); + interpolate8x8_quarterpel(cur.y, forward.y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos + 8, 16*y_pos + 8, pMB->mvs[3].x, pMB->mvs[3].y, stride, 0); + } + }else{ + interpolate8x8_switch(cur.y, forward.y, 16 * x_pos, 16 * y_pos, pMB->mvs[0].x, pMB->mvs[0].y, stride, 0); + interpolate8x8_switch(cur.y, forward.y, 16 * x_pos + 8, 16 * y_pos, pMB->mvs[1].x, pMB->mvs[1].y, stride, 0); + interpolate8x8_switch(cur.y, forward.y, 16 * x_pos, 16 * y_pos + 8, pMB->mvs[2].x, pMB->mvs[2].y, stride, 0); + interpolate8x8_switch(cur.y, forward.y, 16 * x_pos + 8, 16 * y_pos + 8, pMB->mvs[3].x, pMB->mvs[3].y, stride, 0); + } + + interpolate8x8_switch(cur.u, forward.u, 8 * x_pos, 8 * y_pos, uv_dx, uv_dy, stride2, 0); + interpolate8x8_switch(cur.v, forward.v, 8 * x_pos, 8 * y_pos, uv_dx, uv_dy, stride2, 0); + + if(quarterpel){ + if(!direct){ + interpolate16x16_quarterpel(tmp.y, backward.y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos, 16*y_pos, pMB->b_mvs[0].x, pMB->b_mvs[0].y, stride, 0); + }else{ + interpolate8x8_quarterpel(tmp.y, backward.y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos, 16*y_pos, pMB->b_mvs[0].x, pMB->b_mvs[0].y, stride, 0); + interpolate8x8_quarterpel(tmp.y, backward.y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos + 8, 16*y_pos, pMB->b_mvs[1].x, pMB->b_mvs[1].y, stride, 0); + interpolate8x8_quarterpel(tmp.y, backward.y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos, 16*y_pos + 8, pMB->b_mvs[2].x, pMB->b_mvs[2].y, stride, 0); + interpolate8x8_quarterpel(tmp.y, backward.y, qtmp.y, qtmp.y + 64, qtmp.y + 128, 16*x_pos + 8, 16*y_pos + 8, pMB->b_mvs[3].x, pMB->b_mvs[3].y, stride, 0); + } + }else{ + interpolate8x8_switch(tmp.y, backward.y, 16 * x_pos, 16 * y_pos, pMB->b_mvs[0].x, pMB->b_mvs[0].y, stride, 0); + interpolate8x8_switch(tmp.y, backward.y, 16 * x_pos + 8, 16 * y_pos, pMB->b_mvs[1].x, pMB->b_mvs[1].y, stride, 0); + interpolate8x8_switch(tmp.y, backward.y, 16 * x_pos, 16 * y_pos + 8, pMB->b_mvs[2].x, pMB->b_mvs[2].y, stride, 0); + interpolate8x8_switch(tmp.y, backward.y, 16 * x_pos + 8, 16 * y_pos + 8, pMB->b_mvs[3].x, pMB->b_mvs[3].y, stride, 0); + } + + interpolate8x8_switch(tmp.u, backward.u, 8 * x_pos, 8 * y_pos, b_uv_dx, b_uv_dy, stride2, 0); + interpolate8x8_switch(tmp.v, backward.v, 8 * x_pos, 8 * y_pos, b_uv_dx, b_uv_dy, stride2, 0); + + interpolate8x8_avg2(cur.y + (16 * y_pos * stride) + 16 * x_pos, cur.y + (16 * y_pos * stride) + 16 * x_pos, + tmp.y + (16 * y_pos * stride) + 16 * x_pos, stride, 1, 8); + + interpolate8x8_avg2(cur.y + (16 * y_pos * stride) + 16 * x_pos + 8, cur.y + (16 * y_pos * stride) + 16 * x_pos + 8, + tmp.y + (16 * y_pos * stride) + 16 * x_pos + 8, stride, 1, 8); + + interpolate8x8_avg2(cur.y + ((16 * y_pos + 8) * stride) + 16 * x_pos, cur.y + ((16 * y_pos + 8) * stride) + 16 * x_pos, + tmp.y + ((16 * y_pos + 8) * stride) + 16 * x_pos, stride, 1, 8); + + interpolate8x8_avg2(cur.y + ((16 * y_pos + 8) * stride) + 16 * x_pos + 8, cur.y + ((16 * y_pos + 8) * stride) + 16 * x_pos + 8, + tmp.y + ((16 * y_pos + 8) * stride) + 16 * x_pos + 8, stride, 1, 8); + + interpolate8x8_avg2(cur.u + (8 * y_pos * stride2) + 8 * x_pos, cur.u + (8 * y_pos * stride2) + 8 * x_pos, + tmp.u + (8 * y_pos * stride2) + 8 * x_pos, stride2, 1, 8); + + interpolate8x8_avg2(cur.v + (8 * y_pos * stride2) + 8 * x_pos, cur.v + (8 * y_pos * stride2) + 8 * x_pos, + tmp.v + (8 * y_pos * stride2) + 8 * x_pos, stride2, 1, 8); + + if(cbp) + mb_decode(cbp, bs, pY_Cur, pU_Cur, pV_Cur, 0, pMB); + PROF_E(PROF_BINT_MBI); +} + +//---------------------------- +/* for decode B-frame dbquant */ +static int get_dbquant(Bitstream * bs){ + if (!bs->GetBit()) /* '0' */ + return (0); + else if (!bs->GetBit()) /* '10' */ + return (-2); + else /* '11' */ + return (2); +} + +//---------------------------- +/* + * decode B-frame mb_type + * bit ret_value + * 1 0 + * 01 1 + * 001 2 + * 0001 3 + */ +static int get_mbtype(Bitstream * bs){ + int mb_type; + + for (mb_type = 0; mb_type <= 3; mb_type++) + if (bs->GetBit()) + return (mb_type); + + return -1; +} + +//---------------------------- + +void S_decoder::B_Frame(Bitstream *bs, int quant, int fcode_forward, int fcode_backward){ + + dword x, y; + VECTOR mv; + const VECTOR zeromv = {0,0}; + const int64_t TRB = time_pp - time_bp, TRD = time_pp; + int i; + + PROF_S(PROF_FRM_B); + SetEdges(refn[0]); + SetEdges(refn[1]); + + for(y = 0; y < mb_height; y++){ + //initialize Pred Motion Vector + p_fmv = p_bmv = zeromv; + for(x = 0; x < mb_width; x++){ + MACROBLOCK *mb = &mbs[y * mb_width + x]; + MACROBLOCK *last_mb = &last_mbs[y * mb_width + x]; + const int fcode_max = (fcode_forward>fcode_backward) ? fcode_forward : fcode_backward; + + if(bs->check_resync_marker(fcode_max - 1)){ + dword intra_dc_threshold; /* fake variable */ + int bound = read_video_packet_header(bs, fcode_max - 1, &quant, &fcode_forward, &fcode_backward, (int*)&intra_dc_threshold); + x = bound % mb_width; + y = bound / mb_width; + //reset predicted macroblocks + p_fmv = p_bmv = zeromv; + } + + mv = mb->b_mvs[0] = mb->b_mvs[1] = mb->b_mvs[2] = mb->b_mvs[3] = mb->mvs[0] = mb->mvs[1] = mb->mvs[2] = mb->mvs[3] = zeromv; + mb->quant = quant; + + /* + * skip if the co-located P_VOP macroblock is not coded + * if not codec in co-located S_VOP macroblock is _not_ + * automatically skipped + */ + + if(last_mb->mode == MODE_NOT_CODED){ + mb->cbp = 0; + mb->mode = MODE_FORWARD; + DecodeInterMacroBlock(mb, x, y, mb->cbp, bs, 0, 0, 1); + continue; + } + + if(!bs->GetBit()){ + //modb=='0' + const byte modb2 = bs->GetBit(); + + mb->mode = get_mbtype(bs); + + if (!modb2) /* modb=='00' */ + mb->cbp = bs->GetBits(6); + else + mb->cbp = 0; + + if (mb->mode && mb->cbp) { + quant += get_dbquant(bs); + if (quant > 31) + quant = 31; + else if (quant < 1) + quant = 1; + } + mb->quant = quant; + + if(interlacing){ + if (mb->cbp) { + mb->field_dct = bs->GetBit(); + DPRINTF(XVID_DEBUG_MB,"decp: field_dct: %i\n", mb->field_dct); + } + + if (mb->mode) { + mb->field_pred = bs->GetBit(); + DPRINTF(XVID_DEBUG_MB, "decp: field_pred: %i\n", mb->field_pred); + + if (mb->field_pred) { + mb->field_for_top = bs->GetBit(); + DPRINTF(XVID_DEBUG_MB,"decp: field_for_top: %i\n", mb->field_for_top); + mb->field_for_bot = bs->GetBit(); + DPRINTF(XVID_DEBUG_MB,"decp: field_for_bot: %i\n", mb->field_for_bot); + } + } + } + + }else{ + mb->mode = MODE_DIRECT_NONE_MV; + mb->cbp = 0; + } + + switch(mb->mode){ + case MODE_DIRECT: + get_b_motion_vector(bs, &mv, 1, zeromv); + //flow... + case MODE_DIRECT_NONE_MV: + for(i=0; i<4; i++){ + mb->mvs[i].x = (int)((int(TRB) * last_mb->mvs[i].x) / (int)TRD + mv.x); + mb->b_mvs[i].x = (int) ((mv.x == 0) ? + (int(TRB - TRD) * last_mb->mvs[i].x) / (int)TRD : + mb->mvs[i].x - last_mb->mvs[i].x); + mb->mvs[i].y = (int) ((int(TRB) * last_mb->mvs[i].y) / (int)TRD + mv.y); + mb->b_mvs[i].y = (int) ((mv.y == 0) ? + (int(TRB - TRD) * last_mb->mvs[i].y) / int(TRD) : + int(mb->mvs[i].y - last_mb->mvs[i].y)); + } + BFrameInterpolateMBInter(refn[1], refn[0], mb, x, y, bs, 1); + break; + + case MODE_INTERPOLATE: + get_b_motion_vector(bs, &mb->mvs[0], fcode_forward, p_fmv); + p_fmv = mb->mvs[1] = mb->mvs[2] = mb->mvs[3] = mb->mvs[0]; + + get_b_motion_vector(bs, &mb->b_mvs[0], fcode_backward, p_bmv); + p_bmv = mb->b_mvs[1] = mb->b_mvs[2] = mb->b_mvs[3] = mb->b_mvs[0]; + + BFrameInterpolateMBInter(refn[1], refn[0], mb, x, y, bs, 0); + break; + + case MODE_BACKWARD: + get_b_motion_vector(bs, &mb->mvs[0], fcode_backward, p_bmv); + p_bmv = mb->mvs[1] = mb->mvs[2] = mb->mvs[3] = mb->mvs[0]; + + DecodeInterMacroBlock(mb, x, y, mb->cbp, bs, 0, 0, 0); + break; + + case MODE_FORWARD: + get_b_motion_vector(bs, &mb->mvs[0], fcode_forward, p_fmv); + p_fmv = mb->mvs[1] = mb->mvs[2] = mb->mvs[3] = mb->mvs[0]; + + DecodeInterMacroBlock(mb, x, y, mb->cbp, bs, 0, 0, 1); + break; + + default: + DPRINTF(XVID_DEBUG_ERROR,"Not supported B-frame mb_type = %i\n", mb->mode); + } + } + } + PROF_E(PROF_FRM_B); +} + +//---------------------------- +/* perform post processing if necessary, and output the image */ +/* +void S_decoder::Output(IMAGE *img, xvid_dec_frame_t *frame, xvid_dec_stats_t *stats, int coding_type){ + + //image_output(img, width, height, edged_width, (byte**)frame->output.plane, (unsigned int*)frame->output.stride, frame->output.csp, interlacing); + yv12_to_bgr((byte*)frame->output.plane, frame->output.stride, img->y, img->u, img->v, edged_width, edged_width/2, width, height, false); + + if(stats){ + stats->type = coding2type(coding_type); + stats->data.vop.time_base = (int)time_base; + stats->data.vop.time_increment = 0; //XXX: todo + } +} +*/ + +//---------------------------- + +int S_decoder::Decode(xvid_dec_frame_t *frame, xvid_dec_stats_t *stats){ + + PROF_S(PROF_DECODE); + + Bitstream bs; + bool rounding; + bool reduced_resolution; + dword quant; + dword fcode_forward; + dword fcode_backward; + dword intra_dc_threshold; + WARPPOINTS gmc_warp; + int coding_type; + + frame->img_out = NULL; + + if(XVID_VERSION_MAJOR(frame->version) != 1 || (stats && XVID_VERSION_MAJOR(stats->version) != 1)){ //v1.x.x + PROF_E(PROF_DECODE); + return XVID_ERR_VERSION; + } + + //start_global_timer(); + + low_delay_default = (frame->general & XVID_LOWDELAY); + if((frame->general & XVID_DISCONTINUITY)) + frames = 0; + /* +#ifdef XVID_CSP_SLICE + out_frm = (frame->output.csp == XVID_CSP_SLICE) ? &frame->output : NULL; +#endif + */ + + if(frame->length < 0){ + //decoder flush + int ret; + //if not decoding "low_delay/packed", and this isn't low_delay and + // we have a reference frame, then outout the reference frame + if(!(low_delay_default && packed_mode) && !low_delay && frames>0){ + //Output(&refn[0], frame, stats, last_coding_type); + frame->img_out = &refn[0]; + frames = 0; + ret = 0; + } else { + if (stats) stats->type = XVID_TYPE_NOTHING; + ret = XVID_ERR_END; + } + //stop_global_timer(); + PROF_E(PROF_DECODE); + return ret; + } + + bs.Init(frame->bitstream, frame->length); + + //XXX: 0x7f is only valid whilst decoding vfw xvid/divx5 avi's + if(low_delay_default && frame->length == 1 && bs.ShowBits(8) == 0x7f){ + //image_output(&refn[0], width, height, edged_width, (byte**)frame->output.plane, (unsigned int*)frame->output.stride, frame->output.csp, interlacing); + if(stats) + stats->type = XVID_TYPE_NOTHING; + PROF_E(PROF_DECODE); + return 1; //one byte consumed + } + + bool success = false; + bool output = false; + bool seen_something = false; + + /* + cur.Clear(width, height, edged_width, 0, 0, 0); + refn[0].Clear(width, height, edged_width, 0, 0, 0); + refn[1].Clear(width, height, edged_width, 0, 0, 0); + */ + +repeat: + + coding_type = BitstreamReadHeaders(&bs, rounding, &reduced_resolution, &quant, &fcode_forward, &fcode_backward, &intra_dc_threshold, &gmc_warp); + + //DPRINTF(XVID_DEBUG_HEADER, "coding_type=%i, packed=%i, time=%lli, time_pp=%i, time_bp=%i\n", coding_type, packed_mode, time, time_pp, time_bp); + + if(coding_type == -1){ + //nothing + if(success) + goto done; + if(stats) + stats->type = XVID_TYPE_NOTHING; + PROF_E(PROF_DECODE); + return bs.Pos()/8; + } + + if(coding_type == -2 || coding_type == -3){ + //vol and/or resize + + if(coding_type == -3) + Resize(); + /* + if(stats){ + stats->type = XVID_TYPE_VOL; + stats->data.vol.general = 0; + //XXX: if (interlacing) stats->data.vol.general |= ++INTERLACING; + stats->data.vol.width = width; + stats->data.vol.height = height; + stats->data.vol.par = aspect_ratio; + stats->data.vol.par_width = par_width; + stats->data.vol.par_height = par_height; + return bs.Pos()/8; //number of bytes consumed + } + */ + goto repeat; + } + p_bmv.x = p_bmv.y = p_fmv.y = p_fmv.y = 0; + + //packed_mode: special-N_VOP treament + if(packed_mode && coding_type == N_VOP){ + if(low_delay_default && frames > 0){ + //Output(&refn[0], frame, stats, last_coding_type); + frame->img_out = &refn[0]; + output = true; + } + //ignore otherwise + }else + if(coding_type != B_VOP){ + switch(coding_type){ + case I_VOP: + PROF_S(PROF_FRM_I); + I_Frame(&bs, reduced_resolution, quant, intra_dc_threshold); + PROF_E(PROF_FRM_I); + break; + case P_VOP: + P_Frame(&bs, rounding, reduced_resolution, quant, fcode_forward, intra_dc_threshold, NULL); + break; + case S_VOP: + //not called (or very rare) + P_Frame(&bs, rounding, reduced_resolution, quant, fcode_forward, intra_dc_threshold, &gmc_warp); + break; + case N_VOP: + //XXX: not_coded vops are not used for forward prediction we should not swap(last_mbs,mbs) + cur.Copy(&refn[0], edged_width, height); + break; + } + if(reduced_resolution) + cur.deblock_rrv(edged_width, mbs, (width + 31) / 32, (height + 31) / 32, mb_width, 16, 0); + + //note: for packed_mode, output is performed when the special-N_VOP is decoded + if(!(low_delay_default && packed_mode)){ + if(low_delay){ + //Output(&cur, frame, stats, coding_type); + frame->img_out = &cur; + output = true; + }else + if(frames > 0){ + //is the reference frame valid? + // output the reference frame + //Output(&refn[0], frame, stats, last_coding_type); + frame->img_out = &refn[1]; + output = true; + } + } + refn[0].Swap(&refn[1]); + cur.Swap(&refn[0]); + Swap(mbs, last_mbs); + last_reduced_resolution = reduced_resolution; + last_coding_type = coding_type; + + frames++; + seen_something = true; + }else{ + //B_VOP + if(low_delay){ + DPRINTF(XVID_DEBUG_ERROR, "warning: bvop found in low_delay==1 stream\n"); + low_delay = true; + } + + if(frames < 2){ + /* attemping to decode a bvop without atleast 2 reference frames */ + cur.Print(edged_width, height, 16, 16, "broken b-frame, mising ref frames"); + }else + if(time_pp <= time_bp){ + //this occurs when dx50_bvop_compatibility==0 sequences are decoded in vfw + cur.Print(edged_width, height, 16, 16, "broken b-frame"); + }else{ + B_Frame(&bs, quant, fcode_forward, fcode_backward); + } + //Output(&cur, frame, stats, coding_type); + frame->img_out = &cur; + output = true; + frames++; + } + + bs.ByteAlign(); + + //low_delay_default mode: repeat in packed_mode + if(low_delay_default && packed_mode && !output && !success){ + success = true; + goto repeat; + } +done: + //low_delay_default mode: if we've gotten here without outputting anything, + // then output the recently decoded frame, or print an error message + if(low_delay_default && !output){ + if(packed_mode && seen_something){ + //output the recently decoded frame + //Output(&refn[0], frame, stats, last_coding_type); + frame->img_out = &refn[0]; + }else{ + cur.Clear(width, height, edged_width, 0, 128, 128); + cur.Print(edged_width, height, 16, 16, "warning: nothing to output"); + cur.Print(edged_width, height, 16, 64, "bframe decoder lag"); + + //Output(&cur, frame, stats, P_VOP); + frame->img_out = &cur; + if(stats) + stats->type = XVID_TYPE_NOTHING; + } + } + + PROF_E(PROF_DECODE); + //number of bytes consumed + return bs.Pos() / 8; +} + +//---------------------------- + diff --git a/modules/xvid_dec/xvid_wce/decoder.h b/modules/xvid_dec/xvid_wce/decoder.h new file mode 100644 index 0000000..a52bb17 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/decoder.h @@ -0,0 +1,269 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Decoder related header - + * + * Copyright(C) 2002-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: decoder.h,v 1.1.1.1 2005/07/13 14:36:13 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _DECODER_H_ +#define _DECODER_H_ + +#include "xvid.h" +#include "portab.h" +#include "global.h" +#include "image.h" +#include "vlc_codes.h" +#include "Interpolate8x8.h" + +/***************************************************************************** + * Structures + ****************************************************************************/ + +/* complexity estimation toggles */ +struct ESTIMATION{ + int method; + + int opaque; + int transparent; + int intra_cae; + int inter_cae; + int no_update; + int upsampling; + + int intra_blocks; + int inter_blocks; + int inter4v_blocks; + int gmc_blocks; + int not_coded_blocks; + + int dct_coefs; + int dct_lines; + int vlc_symbols; + int vlc_bits; + + int apm; + int npm; + int interpolate_mc_q; + int forw_back_mc_q; + int halfpel2; + int halfpel4; + + int sadct; + int quarterpel; + + ESTIMATION(){ + MemSet(this, 0, sizeof(ESTIMATION)); + } +}; + +//---------------------------- + +struct S_decoder{ +private: +#ifdef PROFILE + mutable C_profiler &prof; +#endif + + int Resize(); + +//---------------------------- + void SetEdges(IMAGE &img) const; + +//---------------------------- + void I_Frame(Bitstream * bs, bool reduced_resolution, int quant, int intra_dc_threshold); + void P_Frame(Bitstream * bs, int rounding, bool reduced_resolution, int quant, int fcode, int intra_dc_threshold, const WARPPOINTS *const gmc_warp); + void MBIntra(MACROBLOCK *pMB, dword x_pos, dword y_pos, dword acpred_flag, dword cbp, + Bitstream *bs, dword quant, dword intra_dc_threshold, dword bound, bool reduced_resolution); + + void mb_decode(const dword cbp, Bitstream * bs, byte * pY_Cur, byte * pU_Cur, byte * pV_Cur, bool reduced_resolution, const MACROBLOCK * pMB); + + void DecodeInterMacroBlock(const MACROBLOCK *pMB, dword x_pos, dword y_pos, dword cbp, Bitstream *bs, + bool rounding, bool reduced_resolution, int ref); + + void mbgmc(MACROBLOCK *pMB, dword x_pos, dword y_pos, dword fcode, dword cbp, Bitstream *bs, bool rounding); + + void BFrameInterpolateMBInter(const IMAGE &forward, const IMAGE &backward, const MACROBLOCK *pMB, dword x_pos, dword y_pos, Bitstream *bs, int direct); + + void B_Frame(Bitstream * bs, int quant, int fcode_forward, int fcode_backward); + void GetMotionVector(Bitstream *bs, int x, int y, int k, VECTOR *ret_mv, int fcode, int bound); + //void Output(IMAGE *img, xvid_dec_frame_t *frame, xvid_dec_stats_t *stats, int coding_type); + + int read_video_packet_header(Bitstream *bs, const int addbits, int *quant, int *fcode_forward, int *fcode_backward, int *intra_dc_threshold); + void read_vol_complexity_estimation_header(Bitstream * bs); + int BitstreamReadHeaders(Bitstream * bs, bool &rounding, bool *reduced_resolution, dword *quant, dword *fcode_forward, + dword *fcode_backward, dword *intra_dc_threshold, WARPPOINTS *gmc_warp); + void read_vop_complexity_estimation_header(Bitstream * bs, int coding_type); + + int get_coeff(Bitstream *bs, int *run, int *last, int intra, int short_video_header); + void get_intra_block(Bitstream * bs, int *block, int direction, int coeff); + void get_inter_block(Bitstream * bs, int *block, int direction); + + void idct_int32_init(); + void InverseDiscreteCosineTransform(int *block) const; + + friend int decoder_create(xvid_dec_create_t *create); + + int Init(xvid_dec_create_t *create); + +#ifdef PROFILE + void interpolate8x8_quarterpel(byte *cur, byte *refn, byte *refh, byte *refv, byte *refhv, dword x, dword y, int dx, int dy, dword stride, bool rounding){ + ::interpolate8x8_quarterpel(cur, refn, refh, refv, refhv, x, y, dx, dy, stride, rounding); + } + void interpolate16x16_switch(byte *cur, const byte *refn, dword x, dword y, int dx, int dy, dword stride, bool rounding){ + interpolate8x8_switch(cur, refn, x, y, dx, dy, stride, rounding); + interpolate8x8_switch(cur, refn, x+8, y, dx, dy, stride, rounding); + interpolate8x8_switch(cur, refn, x, y+8, dx, dy, stride, rounding); + interpolate8x8_switch(cur, refn, x+8, y+8, dx, dy, stride, rounding); + } + void interpolate8x8_switch(byte *cur, const byte *refn, dword x, dword y, int dx, int dy, dword stride, bool rounding){ + const byte *src = refn + ((y + (dy>>1)) * stride + x + (dx>>1)); + byte *dst = cur + (y * stride + x); + + int code = ((dx & 1) << 1) + (dy & 1); + //PROF_S(PROF_4+MIN(3, code)); + switch(code){ + case 0: + transfer8x8_copy(dst, src, stride); + break; + case 1: + interpolate8x8_halfpel_v(dst, src, stride, rounding); + break; + case 2: + interpolate8x8_halfpel_h(dst, src, stride, rounding); + break; + default: + interpolate8x8_halfpel_hv(dst, src, stride, rounding); + break; + } + //PROF_E(PROF_4+MIN(3, code)); + } +#endif +public: + S_decoder(xvid_dec_create_t *create); + ~S_decoder(); + //vol bitstream + int time_inc_resolution; + int fixed_time_inc; + dword time_inc_bits; + + dword shape; + dword quant_bits; + dword quant_type; + dword *mpeg_quant_matrices; + int quarterpel; + int complexity_estimation_disable; + ESTIMATION estimation; + + dword top_field_first; + dword alternate_vertical_scan; + + int aspect_ratio; + int par_width; + int par_height; + + int sprite_enable; + int sprite_warping_points; + int sprite_warping_accuracy; + int sprite_brightness_change; + + bool interlacing; + bool newpred_enable; + bool reduced_resolution_enable; + + /* The bitstream version if it's a XviD stream */ + int bs_version; + + /* image */ + + dword width; + dword height; + dword edged_width; + dword edged_height; + + IMAGE cur; + IMAGE refn[2]; /* 0 -- last I or P VOP */ + + /* 1 -- first I or P */ + IMAGE tmp; /* bframe interpolation, and post processing tmp buffer */ + IMAGE qtmp; /* quarter pel tmp buffer */ + + /* macroblock */ + + dword mb_width; + dword mb_height; + MACROBLOCK *mbs; + + //for B-frame & low_delay==0 + // XXX: should move frame based stuff into a DECODER_FRAMEINFO struct + MACROBLOCK *last_mbs; /* last MB */ + int last_coding_type; /* last coding type value */ + int frames; /* total frame number */ + VECTOR p_fmv, p_bmv; /* pred forward & backward motion vector */ + int64_t time; /* for record time */ + int64_t time_base; + int64_t last_time_base; + int64_t last_non_b_time; + dword time_pp; + dword time_bp; + bool fixed_dimensions; + bool scalability; + bool low_delay; //low_delay flage (1 means no B_VOP) + bool low_delay_default; //default value for low_delay flag + bool last_reduced_resolution; //last reduced_resolution value + bool packed_mode; //bframes packed bitstream? (1 = yes) + + //for GMC: central place for all parameters + + IMAGE gmc; /* gmc tmp buffer, remove for blockbased compensation */ + GMC_DATA gmc_data; + NEW_GMC_DATA new_gmc_data; + + /* +#ifdef XVID_CSP_SLICE + xvid_image_t* out_frm; //This is used for slice rendering +#endif + */ + + struct REVERSE_EVENT{ + byte len; + EVENT event; + }; + //decoding tables + REVERSE_EVENT DCT3D[2][4096]; + VLC coeff_VLC[2][2][64][64]; + + typedef int t_clip_val; + t_clip_val iclip[1024]; //clipping table + + void init_vlc_tables(); + int Decode(xvid_dec_frame_t * frame, xvid_dec_stats_t * stats); +}; + +/***************************************************************************** + * Decoder prototypes + ****************************************************************************/ + +void init_decoder(dword cpu_flags); + +int decoder_create(xvid_dec_create_t * param); + + +#endif diff --git a/modules/xvid_dec/xvid_wce/font.cpp b/modules/xvid_dec/xvid_wce/font.cpp new file mode 100644 index 0000000..4c6e03d --- /dev/null +++ b/modules/xvid_dec/xvid_wce/font.cpp @@ -0,0 +1,598 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Font rendering to frame buffer functions - + * + * Copyright(C) 2002-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: font.cpp,v 1.1.1.1 2005/07/13 14:36:13 jeanlf Exp $ + * + ****************************************************************************/ + +#include "image.h" + +#define FONT_WIDTH 4 +#define FONT_HEIGHT 6 + +//---------------------------- + +static const char ascii33[33][FONT_WIDTH*FONT_HEIGHT] = { + + /* ! */ + {0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,0,0, + 0,0,1,0}, + + /* " */ + {0,1,0,1, + 0,1,0,1, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0}, + + /* # */ + {0,1,1,0, + 1,1,1,1, + 0,1,1,0, + 0,1,1,0, + 1,1,1,1, + 0,1,1,0}, + + /* $ */ + {0,1,1,0, + 1,0,1,1, + 1,1,1,0, + 0,1,1,1, + 1,1,0,1, + 0,1,1,0}, + + /* % */ + {1,1,0,1, + 1,0,0,1, + 0,0,1,0, + 0,1,0,0, + 1,0,0,1, + 1,0,1,1}, + + /* & */ + {0,1,1,0, + 1,0,0,0, + 0,1,0,1, + 1,0,1,0, + 1,0,1,0, + 0,1,0,1}, + + /* ' */ + {0,0,1,0, + 0,0,1,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0}, + + /* ( */ + {0,0,1,0, + 0,1,0,0, + 0,1,0,0, + 0,1,0,0, + 0,1,0,0, + 0,0,1,0}, + + /* ) */ + {0,1,0,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,1,0,0}, + + /* * */ + {0,0,0,0, + 1,0,0,1, + 0,1,1,0, + 1,1,1,1, + 0,1,1,0, + 1,0,0,1}, + + /* + */ + {0,0,0,0, + 0,0,1,0, + 0,0,1,0, + 0,1,1,1, + 0,0,1,0, + 0,0,1,0}, + + /* , */ + {0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,1,1,0, + 0,0,1,0}, + + /* - */ + {0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 1,1,1,1, + 0,0,0,0, + 0,0,0,0}, + + /* . */ + {0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,1,1,0, + 0,1,1,0}, + + /* / */ + {0,0,0,1, + 0,0,0,1, + 0,0,1,0, + 0,1,0,0, + 1,0,0,0, + 1,0,0,0}, + + /* 0 */ + {0,1,1,0, + 1,0,0,1, + 1,0,1,1, + 1,1,0,1, + 1,0,0,1, + 0,1,1,0}, + + /* 1 */ + {0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0}, + + /* 2 */ + {0,1,1,0, + 1,0,0,1, + 0,0,1,0, + 0,1,0,0, + 1,0,0,0, + 1,1,1,1}, + + /* 3 */ + {0,1,1,0, + 1,0,0,1, + 0,0,1,0, + 0,0,0,1, + 1,0,0,1, + 0,1,1,0}, + + /* 4 */ + {0,0,1,0, + 0,1,1,0, + 1,0,1,0, + 1,1,1,1, + 0,0,1,0, + 0,0,1,0}, + + /* 5 */ + {1,1,1,1, + 1,0,0,0, + 1,1,1,0, + 0,0,0,1, + 1,0,0,1, + 0,1,1,0}, + + /* 6 */ + {0,1,1,1, + 1,0,0,0, + 1,1,1,0, + 1,0,0,1, + 1,0,0,1, + 0,1,1,0}, + + /* 7 */ + {1,1,1,0, + 0,0,0,1, + 0,0,0,1, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0}, + + /* 8 */ + {0,1,1,0, + 1,0,0,1, + 0,1,1,0, + 1,0,0,1, + 1,0,0,1, + 0,1,1,0}, + + /* 9 */ + {0,1,1,0, + 1,0,0,1, + 1,0,0,1, + 0,1,1,1, + 0,0,0,1, + 1,1,1,0}, + + /* : */ + {0,0,0,0, + 0,0,0,0, + 0,0,1,0, + 0,0,0,0, + 0,0,1,0, + 0,0,0,0}, + + /* ; */ + {0,0,0,0, + 0,0,1,0, + 0,0,0,0, + 0,0,0,0, + 0,1,1,0, + 0,0,1,0}, + + /* < */ + {0,0,0,1, + 0,0,1,0, + 0,1,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1}, + + /* = */ + {0,0,0,0, + 1,1,1,1, + 0,0,0,0, + 0,0,0,0, + 1,1,1,1, + 0,0,0,0}, + + /* > */ + {0,1,0,0, + 0,0,1,0, + 0,0,0,1, + 0,0,0,1, + 0,0,1,0, + 0,1,0,0}, + + /* ? */ + {0,1,1,0, + 1,0,0,1, + 0,0,1,0, + 0,0,1,0, + 0,0,0,0, + 0,0,1,0}, + + /* @ */ + {0,1,1,0, + 1,0,0,1, + 1,0,1,1, + 1,0,1,1, + 1,0,0,0, + 0,1,1,0}, + +}; + + +static const char ascii65[26][FONT_WIDTH*FONT_HEIGHT] = { + /* A */ + {0,1,1,0, + 1,0,0,1, + 1,0,0,1, + 1,1,1,1, + 1,0,0,1, + 1,0,0,1}, + + /* B */ + {1,1,1,0, + 1,0,0,1, + 1,1,1,0, + 1,0,0,1, + 1,0,0,1, + 1,1,1,0}, + + /* C */ + {0,1,1,0, + 1,0,0,1, + 1,0,0,0, + 1,0,0,0, + 1,0,0,1, + 0,1,1,0}, + + /* D */ + {1,1,0,0, + 1,0,1,0, + 1,0,0,1, + 1,0,0,1, + 1,0,1,0, + 1,1,0,0}, + + /* E */ + {1,1,1,1, + 1,0,0,0, + 1,1,1,0, + 1,0,0,0, + 1,0,0,0, + 1,1,1,1}, + + /* F */ + {1,1,1,1, + 1,0,0,0, + 1,1,1,0, + 1,0,0,0, + 1,0,0,0, + 1,0,0,0}, + + /* G */ + {0,1,1,1, + 1,0,0,0, + 1,0,1,1, + 1,0,0,1, + 1,0,0,1, + 0,1,1,0}, + + /* H */ + {1,0,0,1, + 1,0,0,1, + 1,1,1,1, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1}, + + /* I */ + {0,1,1,1, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,1,1,1}, + + /* J */ + {0,1,1,1, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 1,0,1,0, + 0,1,0,0}, + + /* K */ + {1,0,0,1, + 1,0,0,1, + 1,1,1,0, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1}, + + /* L */ + {1,0,0,0, + 1,0,0,0, + 1,0,0,0, + 1,0,0,0, + 1,0,0,0, + 1,1,1,1}, + + /* M */ + {1,0,0,1, + 1,1,1,1, + 1,1,1,1, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1}, + + /* N */ + {1,0,0,1, + 1,1,0,1, + 1,1,0,1, + 1,0,1,1, + 1,0,1,1, + 1,0,0,1}, + + /* 0 */ + {0,1,1,0, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1, + 0,1,1,0}, + + /* P */ + {1,1,1,0, + 1,0,0,1, + 1,1,1,0, + 1,0,0,0, + 1,0,0,0, + 1,0,0,0}, + + /* Q */ + {0,1,1,0, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1, + 1,0,1,0, + 0,1,0,1}, + + + /* R */ + {1,1,1,0, + 1,0,0,1, + 1,1,1,0, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1}, + + /* S */ + {0,1,1,0, + 1,0,0,1, + 0,1,0,0, + 0,0,1,0, + 1,0,0,1, + 0,1,1,0}, + + /* T */ + {0,1,1,1, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0}, + + /* U */ + {1,0,0,1, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1, + 1,1,1,1}, + + /* V */ + {1,0,0,1, + 1,0,0,1, + 1,0,0,1, + 0,1,1,0, + 0,1,1,0, + 0,1,1,0}, + + /* W */ + {1,0,0,1, + 1,0,0,1, + 1,0,0,1, + 1,1,1,1, + 1,1,1,1, + 1,0,0,1}, + + /* X */ + {1,0,0,1, + 1,0,0,1, + 0,1,1,0, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1}, + + /* Y */ + {1,0,0,1, + 1,0,0,1, + 0,1,0,0, + 0,0,1,0, + 0,1,0,0, + 1,0,0,0}, + + /* Z */ + {1,1,1,1, + 0,0,0,1, + 0,0,1,0, + 0,1,0,0, + 1,0,0,0, + 1,1,1,1}, + +}; + + + +static const char ascii91[6][FONT_WIDTH*FONT_HEIGHT] = { + /* [ */ + {0,1,1,0, + 0,1,0,0, + 0,1,0,0, + 0,1,0,0, + 0,1,0,0, + 0,1,1,0}, + + /* '\' */ + {1,0,0,0, + 1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1, + 0,0,0,1}, + + /* ] */ + {0,1,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,1,1,0}, + + /* ^ */ + {0,1,0,1, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0}, + + /* _ */ + {0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 1,1,1,1}, + + /* ` */ + {0,1,0,0, + 0,0,1,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0} +}; + +//---------------------------- + +#define FONT_ZOOM 2 + +void IMAGE::draw_num(const int stride, const int height, const char * font, const int x, const int y){ + + for(int j = 0; j < FONT_ZOOM * FONT_HEIGHT && y+j < height; j++){ + for(int i = 0; i < FONT_ZOOM * FONT_WIDTH && x+i < stride; i++){ + if(font[(j/FONT_ZOOM)*FONT_WIDTH + (i/FONT_ZOOM)]){ + int offset = (y+j)*stride + (x+i); + int offset2 =((y+j)/2)*(stride/2) + ((x+i)/2); + IMAGE::y[offset] = 255; + IMAGE::u[offset2] = 127; + IMAGE::v[offset2] = 127; + } + } + } +} + +//---------------------------- + +void IMAGE::Print(int edged_width, int height, int x, int y, const char *buf){ + + int i; + + for(i = 0; buf[i]; i++) { + const char * font; + + if (buf[i] >= '!' && buf[i] <= '@') + font = ascii33[buf[i]-'!']; + else if (buf[i] >= 'A' && buf[i] <= 'Z') + font = ascii65[buf[i]-'A']; + else if (buf[i] >= '[' && buf[i] <= '`') + font = ascii91[buf[i]-'[']; + else if (buf[i] >= 'a' && buf[i] <= 'z') + font = ascii65[buf[i]-'a']; + else + continue; + draw_num(edged_width, height, font, x + i*FONT_ZOOM*(FONT_WIDTH+1), y); + } +} diff --git a/modules/xvid_dec/xvid_wce/global.h b/modules/xvid_dec/xvid_wce/global.h new file mode 100644 index 0000000..d801a0d --- /dev/null +++ b/modules/xvid_dec/xvid_wce/global.h @@ -0,0 +1,355 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Global definitions - + * + * Copyright(C) 2002 Michael Militzer <isibaar@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: global.h,v 1.1.1.1 2005/07/13 14:36:14 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _GLOBAL_H_ +#define _GLOBAL_H_ + +#include "xvid.h" +#include "portab.h" + +//---------------------------- + +void MemSet(void *dst, byte c, dword len); +int MemCmp(const void *mem1, const void *mem2, dword len); +void MemCpy(void *dst, const void *src, dword len); + +template<class T> +inline void Swap(T &l, T &r){ T tmp = r; r = l; l = tmp; } + +#ifndef __SYMBIAN32__ + +enum TLeave{ ELeave }; +void *operator new(size_t sz, TLeave); +inline void operator delete(void *vp, TLeave){ operator delete(vp); } + +#endif + +//---------------------------- +// Fatal error - display message, and exit program immediately. +void Fatal(const char *msg, dword code = 0); + +//---------------------------- +#ifndef assert + +#ifdef NDEBUG +#define assert(exp) ((void)0) +#else + +#define assert(exp) if(!(exp)){\ + Fatal(#exp, __LINE__); } + +#endif +#endif + +//---------------------------- +/* --- macroblock modes --- */ + +#define MODE_INTER 0 +#define MODE_INTER_Q 1 +#define MODE_INTER4V 2 +#define MODE_INTRA 3 +#define MODE_INTRA_Q 4 +#define MODE_NOT_CODED 16 +#define MODE_NOT_CODED_GMC 17 + +/* --- bframe specific --- */ + +#define MODE_DIRECT 0 +#define MODE_INTERPOLATE 1 +#define MODE_BACKWARD 2 +#define MODE_FORWARD 3 +#define MODE_DIRECT_NONE_MV 4 +#define MODE_DIRECT_NO4V 5 + + +/* + * vop coding types + * intra, prediction, backward, sprite, not_coded + */ +#define I_VOP 0 +#define P_VOP 1 +#define B_VOP 2 +#define S_VOP 3 +#define N_VOP 4 + +//---------------------------- +// convert mpeg-4 coding type i/p/b/s_VOP to XVID_TYPE_xxx +inline int coding2type(int coding_type){ + return coding_type + 1; +} + +//---------------------------- +// convert XVID_TYPE_xxx to bitstream coding type i/p/b/s_VOP +inline int type2coding(int xvid_type){ + return xvid_type - 1; +} + + +typedef struct +{ + int x; + int y; +} +VECTOR; + + + +typedef struct +{ + VECTOR duv[3]; +} +WARPPOINTS; + +/* save all warping parameters for GMC once and for all, instead of + recalculating for every block. This is needed for encoding&decoding + When switching to incremental calculations, this will get much shorter +*/ + +/* we don't include WARPPOINTS wp here, but in FRAMEINFO itself */ + +struct GMC_DATA{ + int num_wp; /* [input]: 0=none, 1=translation, 2,3 = warping */ + /* a value of -1 means: "structure not initialized!" */ + int s; /* [input]: calc is done with 1/s pel resolution */ + + int W; + int H; + + int ss; + int smask; + int sigma; + + int r; + int rho; + + int i0s; + int j0s; + int i1s; + int j1s; + int i2s; + int j2s; + + int i1ss; + int j1ss; + int i2ss; + int j2ss; + + int alpha; + int beta; + int Ws; + int Hs; + + int dxF, dyF, dxG, dyG; + int Fo, Go; + int cFo, cGo; + + GMC_DATA(){ + MemSet(this, 0, sizeof(GMC_DATA)); + } +}; + +struct NEW_GMC_DATA{ + /* 0=none, 1=translation, 2,3 = warping + * a value of -1 means: "structure not initialized!" */ + int num_wp; + + /* {0,1,2,3} => {1/2,1/4,1/8,1/16} pel */ + int accuracy; + + /* sprite size * 16 */ + int sW, sH; + + /* gradient, calculated from warp points */ + int dU[2], dV[2], Uo, Vo, Uco, Vco; + + void (*predict_16x16)(const NEW_GMC_DATA * const This, + byte *dst, const byte *src, + int dststride, int srcstride, int x, int y, int rounding); + void (*predict_8x8) (const NEW_GMC_DATA * const This, + byte *uDst, const byte *uSrc, + byte *vDst, const byte *vSrc, + int dststride, int srcstride, int x, int y, int rounding); + void (*get_average_mv)(const NEW_GMC_DATA * const Dsp, VECTOR * const mv, + int x, int y, int qpel); + + NEW_GMC_DATA(){ + MemSet(this, 0, sizeof(NEW_GMC_DATA)); + } +}; + +//---------------------------- + +struct IMAGE: public C_xvid_image{ +private: + void draw_num(const int stride, const int height, const char * font, const int x, const int y); +public: + void Print(int edged_width, int height, int x, int y, const char *fmt); + void Swap(IMAGE *image2); + void Copy(const IMAGE * image2, dword edged_width, dword height); + void Clear(int width, int height, int edged_width, int y, int u, int v); + void deblock_rrv(int edged_width, const struct MACROBLOCK * mbs, int mb_width, int mb_height, int mb_stride, int block, int flags); + + inline void Null(){ + y = u = v = 0; + } +}; + +//---------------------------- + +struct Bitstream{ + dword bufa; + dword bufb; + dword buf; + dword pos; //bit position in currently cached 2 dwords (0-31) + dword *tail; + dword *start; + dword length; + dword initpos; + + void Init(const void *bitstream, dword length); + dword ShowBits(dword bits); + void get_matrix(byte *matrix); + void Skip(dword bits); + +//---------------------------- +// number of bits to next byte alignment + inline dword NumBitsToByteAlign() const{ + dword n = (32 - pos) & 7; + return n == 0 ? 8 : n; + } + dword ShowBitsFromByteAlign(int bits); + +//---------------------------- +// move forward to the next byte boundary + void ByteAlign(){ + dword remainder = pos & 7; + if (remainder) { + Skip(8 - remainder); + } + } + +//---------------------------- +// bitstream length (unit bits) + inline dword Pos() const{ + return((dword)(8*((dword)tail - (dword)start) + pos - initpos)); + } + + dword GetBits(const dword n); + +//---------------------------- +// read single bit from bitstream + inline dword GetBit(){ + return GetBits(1); + } + +//---------------------------- + + int GetMcbpcInter(); + int GetCbpy(int intra); + int GetMoveVector(int fcode); + +//---------------------------- + + int check_resync_marker(int addbits); + int bs_get_spritetrajectory(); + int get_mcbpc_intra(); + int get_dc_dif(dword dc_size); + int get_dc_size_lum(); + int get_dc_size_chrom(); +}; + + +#define MBPRED_SIZE 15 + + +struct MACROBLOCK{ + /* decoder/encoder */ + VECTOR mvs[4]; + + int pred_values[6][MBPRED_SIZE]; + int acpred_directions[6]; + + int mode; + int quant; /* absolute quant */ + + int field_dct; + int field_pred; + int field_for_top; + int field_for_bot; + + /* encoder specific */ + + VECTOR mv16; + VECTOR pmvs[4]; + VECTOR qmvs[4]; /* mvs in quarter pixel resolution */ + + int sad8[4]; /* SAD values for inter4v-VECTORs */ + int sad16; /* SAD value for inter-VECTOR */ + + int dquant; + int cbp; + + /* bframe stuff */ + + VECTOR b_mvs[4]; + VECTOR b_qmvs[4]; + + int mb_type; + + /* + * stuff for block based ME (needed for Qpel ME) + * backup of last integer ME vectors/sad + */ + + VECTOR amv; /* average motion vectors from GMC */ + int mcsel; + +/* This structure has become way to big! What to do? Split it up? */ + +}; + +//---------------------------- + +inline dword log2bin(dword value){ + int n = 0; + + while (value) { + value >>= 1; + n++; + } + return n; +} + +//---------------------------- + +/* useful macros */ + +#define MIN(X, Y) ((X)<(Y)?(X):(Y)) +#define MAX(X, Y) ((X)>(Y)?(X):(Y)) +#define ABS(X) (((X)>0)?(X):-(X)) +#define SIGN(X) (((X)>0)?1:-1) +#define CLIP(X,AMIN,AMAX) (((X)<(AMIN)) ? (AMIN) : ((X)>(AMAX)) ? (AMAX) : (X)) + +#endif /* _GLOBAL_H_ */ diff --git a/modules/xvid_dec/xvid_wce/gmc.cpp b/modules/xvid_dec/xvid_wce/gmc.cpp new file mode 100644 index 0000000..935a0d8 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/gmc.cpp @@ -0,0 +1,396 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - GMC interpolation module - + * + * Copyright(C) 2002-2003 Pascal Massimino <skal@planet-d.net> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: gmc.cpp,v 1.1.1.1 2005/07/13 14:36:14 jeanlf Exp $ + * + ****************************************************************************/ + +#include "portab.h" +#include "global.h" +#include "gmc.h" + +/* ************************************************************ + * Pts = 2 or 3 + * + * Warning! *src is the global frame pointer (that is: adress + * of pixel 0,0), not the macroblock one. + * Conversely, *dst is the macroblock top-left adress. + */ +static void Predict_16x16_C(const NEW_GMC_DATA * const This, byte *dst, const byte *src, int dststride, int srcstride, int x, int y, int rounding){ + + const int W = This->sW; + const int H = This->sH; + const int rho = 3 - This->accuracy; + const int Rounder = ( (1<<7) - (rounding<<(2*rho)) ) << 16; + + const int dUx = This->dU[0]; + const int dVx = This->dV[0]; + const int dUy = This->dU[1]; + const int dVy = This->dV[1]; + + int Uo = This->Uo + 16*(dUy*y + dUx*x); + int Vo = This->Vo + 16*(dVy*y + dVx*x); + + int i, j; + + dst += 16; + for (j=16; j>0; --j) { + int U = Uo, V = Vo; + Uo += dUy; Vo += dVy; + for (i=-16; i<0; ++i) { + unsigned int f0, f1, ri = 16, rj = 16; + int Offset; + int u = ( U >> 16 ) << rho; + int v = ( V >> 16 ) << rho; + + U += dUx; V += dVx; + + if (u > 0 && u <= W) { ri = MTab[u&15]; Offset = u>>4; } + else if (u > W) Offset = W>>4; + else Offset = -1; + + if (v > 0 && v <= H) { rj = MTab[v&15]; Offset += (v>>4)*srcstride; } + else if (v > H) Offset += (H>>4)*srcstride; + else Offset -= srcstride; + + f0 = src[Offset + 0]; + f0 |= src[Offset + 1] << 16; + f1 = src[Offset + srcstride + 0]; + f1 |= src[Offset + srcstride + 1] << 16; + f0 = (ri*f0)>>16; + f1 = (ri*f1) & 0x0fff0000; + f0 |= f1; + f0 = (rj*f0 + Rounder) >> 24; + + dst[i] = (byte)f0; + } + dst += dststride; + } +} + +//---------------------------- + +static void Predict_8x8_C(const NEW_GMC_DATA * const This, byte *uDst, const byte *uSrc, + byte *vDst, const byte *vSrc, int dststride, int srcstride, int x, int y, int rounding){ + + const int W = This->sW >> 1; + const int H = This->sH >> 1; + const int rho = 3-This->accuracy; + const int Rounder = ( 128 - (rounding<<(2*rho)) ) << 16; + + const int dUx = This->dU[0]; + const int dVx = This->dV[0]; + const int dUy = This->dU[1]; + const int dVy = This->dV[1]; + + int Uo = This->Uco + 8*(dUy*y + dUx*x); + int Vo = This->Vco + 8*(dVy*y + dVx*x); + + int i, j; + + uDst += 8; + vDst += 8; + for (j=8; j>0; --j) { + int U = Uo, V = Vo; + Uo += dUy; Vo += dVy; + + for (i=-8; i<0; ++i) { + int Offset; + dword f0, f1, ri, rj; + int u, v; + + u = ( U >> 16 ) << rho; + v = ( V >> 16 ) << rho; + U += dUx; V += dVx; + + if (u > 0 && u <= W) { + ri = MTab[u&15]; + Offset = u>>4; + } else { + ri = 16; + if (u>W) Offset = W>>4; + else Offset = -1; + } + + if (v > 0 && v <= H) { + rj = MTab[v&15]; + Offset += (v>>4)*srcstride; + } else { + rj = 16; + if (v>H) Offset += (H>>4)*srcstride; + else Offset -= srcstride; + } + + f0 = uSrc[Offset + 0]; + f0 |= uSrc[Offset + 1] << 16; + f1 = uSrc[Offset + srcstride + 0]; + f1 |= uSrc[Offset + srcstride + 1] << 16; + f0 = (ri*f0)>>16; + f1 = (ri*f1) & 0x0fff0000; + f0 |= f1; + f0 = (rj*f0 + Rounder) >> 24; + + uDst[i] = (byte)f0; + + f0 = vSrc[Offset + 0]; + f0 |= vSrc[Offset + 1] << 16; + f1 = vSrc[Offset + srcstride + 0]; + f1 |= vSrc[Offset + srcstride + 1] << 16; + f0 = (ri*f0)>>16; + f1 = (ri*f1) & 0x0fff0000; + f0 |= f1; + f0 = (rj*f0 + Rounder) >> 24; + + vDst[i] = (byte)f0; + } + uDst += dststride; + vDst += dststride; + } +} + +//---------------------------- + +static void get_average_mv_C(const NEW_GMC_DATA * const Dsp, VECTOR * const mv, int x, int y, int qpel){ + + int i, j; + int vx = 0, vy = 0; + int uo = Dsp->Uo + 16*(Dsp->dU[1]*y + Dsp->dU[0]*x); + int vo = Dsp->Vo + 16*(Dsp->dV[1]*y + Dsp->dV[0]*x); + for (j=16; j>0; --j) + { + int U, V; + U = uo; uo += Dsp->dU[1]; + V = vo; vo += Dsp->dV[1]; + for (i=16; i>0; --i) + { + int u,v; + u = U >> 16; U += Dsp->dU[0]; vx += u; + v = V >> 16; V += Dsp->dV[0]; vy += v; + } + } + vx -= (256*x+120) << (5+Dsp->accuracy); /* 120 = 15*16/2 */ + vy -= (256*y+120) << (5+Dsp->accuracy); + + mv->x = RSHIFT( vx, 8+Dsp->accuracy - qpel ); + mv->y = RSHIFT( vy, 8+Dsp->accuracy - qpel ); +} + +//---------------------------- +/* ************************************************************ + * simplified version for 1 warp point + */ +static void Predict_1pt_16x16_C(const NEW_GMC_DATA * const This, byte *Dst, const byte *Src, int dststride, int srcstride, int x, int y, int rounding){ + + const int W = This->sW; + const int H = This->sH; + const int rho = 3-This->accuracy; + const int Rounder = ( 128 - (rounding<<(2*rho)) ) << 16; + + + int uo = This->Uo + (x<<8); /* ((16*x)<<4) */ + int vo = This->Vo + (y<<8); + const dword ri = MTab[uo & 15]; + const dword rj = MTab[vo & 15]; + int i, j; + + int Offset; + if ((dword)vo<=(dword)H) Offset = (vo>>4)*srcstride; + else if (vo>H) Offset = ( H>>4)*srcstride; + else Offset =-16*srcstride; + if ((dword)uo<=(dword)W) Offset += (uo>>4); + else if (uo>W) Offset += ( W>>4); + else Offset -= 16; + + Dst += 16; + + for(j=16; j>0; --j, Offset+=srcstride-16) + { + for(i=-16; i<0; ++i, ++Offset) + { + dword f0, f1; + f0 = Src[ Offset +0 ]; + f0 |= Src[ Offset +1 ] << 16; + f1 = Src[ Offset+srcstride +0 ]; + f1 |= Src[ Offset+srcstride +1 ] << 16; + f0 = (ri*f0)>>16; + f1 = (ri*f1) & 0x0fff0000; + f0 |= f1; + f0 = ( rj*f0 + Rounder ) >> 24; + Dst[i] = (byte)f0; + } + Dst += dststride; + } +} + +//---------------------------- + +static void Predict_1pt_8x8_C(const NEW_GMC_DATA * const This, byte *uDst, const byte *uSrc, + byte *vDst, const byte *vSrc, int dststride, int srcstride, int x, int y, int rounding){ + + const int W = This->sW >> 1; + const int H = This->sH >> 1; + const int rho = 3-This->accuracy; + const int Rounder = ( 128 - (rounding<<(2*rho)) ) << 16; + + int uo = This->Uco + (x<<7); + int vo = This->Vco + (y<<7); + const dword rri = MTab[uo & 15]; + const dword rrj = MTab[vo & 15]; + int i, j; + + int Offset; + if ((dword)vo<=(dword)H) Offset = (vo>>4)*srcstride; + else if (vo>H) Offset = ( H>>4)*srcstride; + else Offset =-8*srcstride; + if ((dword)uo<=(dword)W) Offset += (uo>>4); + else if (uo>W) Offset += (W>>4); + else Offset -= 8; + + uDst += 8; + vDst += 8; + for(j=8; j>0; --j, Offset+=srcstride-8) + { + for(i=-8; i<0; ++i, Offset++) + { + dword f0, f1; + f0 = uSrc[ Offset + 0 ]; + f0 |= uSrc[ Offset + 1 ] << 16; + f1 = uSrc[ Offset + srcstride + 0 ]; + f1 |= uSrc[ Offset + srcstride + 1 ] << 16; + f0 = (rri*f0)>>16; + f1 = (rri*f1) & 0x0fff0000; + f0 |= f1; + f0 = ( rrj*f0 + Rounder ) >> 24; + uDst[i] = (byte)f0; + + f0 = vSrc[ Offset + 0 ]; + f0 |= vSrc[ Offset + 1 ] << 16; + f1 = vSrc[ Offset + srcstride + 0 ]; + f1 |= vSrc[ Offset + srcstride + 1 ] << 16; + f0 = (rri*f0)>>16; + f1 = (rri*f1) & 0x0fff0000; + f0 |= f1; + f0 = ( rrj*f0 + Rounder ) >> 24; + vDst[i] = (byte)f0; + } + uDst += dststride; + vDst += dststride; + } +} + +//---------------------------- + +static void get_average_mv_1pt_C(const NEW_GMC_DATA *const Dsp, VECTOR * const mv, int x, int y, int qpel){ + + mv->x = RSHIFT(Dsp->Uo<<qpel, 3); + mv->y = RSHIFT(Dsp->Vo<<qpel, 3); +} + +//---------------------------- + +void generate_GMCparameters(int nb_pts, int accuracy, const WARPPOINTS *pts, int width, int height, NEW_GMC_DATA *gmc){ + + gmc->sW = width << 4; + gmc->sH = height << 4; + gmc->accuracy = accuracy; + gmc->num_wp = nb_pts; + + //reduce the number of points, if possible + if(nb_pts<3 || (pts->duv[2].x==-pts->duv[1].y && pts->duv[2].y==pts->duv[1].x)){ + if(nb_pts<2 || (pts->duv[1].x==0 && pts->duv[1].y==0)){ + if(nb_pts<1 || (pts->duv[0].x==0 && pts->duv[0].y==0)){ + nb_pts = 0; + }else + nb_pts = 1; + }else + nb_pts = 2; + }else + nb_pts = 3; + + //now, nb_pts stores the actual number of points required for interpolation + if(nb_pts<=1){ + if(nb_pts==1){ + /* store as 4b fixed point */ + gmc->Uo = pts->duv[0].x << accuracy; + gmc->Vo = pts->duv[0].y << accuracy; + gmc->Uco = ((pts->duv[0].x>>1) | (pts->duv[0].x&1)) << accuracy; /* DIV2RND() */ + gmc->Vco = ((pts->duv[0].y>>1) | (pts->duv[0].y&1)) << accuracy; /* DIV2RND() */ + }else{ /* zero points?! */ + gmc->Uo = gmc->Vo = 0; + gmc->Uco = gmc->Vco = 0; + } + + gmc->predict_16x16 = Predict_1pt_16x16_C; + gmc->predict_8x8 = Predict_1pt_8x8_C; + gmc->get_average_mv = get_average_mv_1pt_C; + }else{ /* 2 or 3 points */ + const int rho = 3 - accuracy; /* = {3,2,1,0} for Acc={0,1,2,3} */ + int Alpha = log2bin(width-1); + int Ws = 1 << Alpha; + + gmc->dU[0] = 16*Ws + RDIV( 8*Ws*pts->duv[1].x, width ); /* dU/dx */ + gmc->dV[0] = RDIV( 8*Ws*pts->duv[1].y, width ); /* dV/dx */ + + /* disabled, because possibly buggy? */ + +#if 0 + if (nb_pts==2) { + gmc->dU[1] = -gmc->dV[0]; /* -Sin */ + gmc->dV[1] = gmc->dU[0] ; /* Cos */ + } + else +#endif + { + const int Beta = log2bin(height-1); + const int Hs = 1<<Beta; + gmc->dU[1] = RDIV( 8*Hs*pts->duv[2].x, height ); /* dU/dy */ + gmc->dV[1] = 16*Hs + RDIV( 8*Hs*pts->duv[2].y, height ); /* dV/dy */ + if (Beta>Alpha) { + gmc->dU[0] <<= (Beta-Alpha); + gmc->dV[0] <<= (Beta-Alpha); + Alpha = Beta; + Ws = Hs; + } + else { + gmc->dU[1] <<= Alpha - Beta; + gmc->dV[1] <<= Alpha - Beta; + } + } + /* upscale to 16b fixed-point */ + gmc->dU[0] <<= (16-Alpha - rho); + gmc->dU[1] <<= (16-Alpha - rho); + gmc->dV[0] <<= (16-Alpha - rho); + gmc->dV[1] <<= (16-Alpha - rho); + + gmc->Uo = ( pts->duv[0].x <<(16+ accuracy)) + (1<<15); + gmc->Vo = ( pts->duv[0].y <<(16+ accuracy)) + (1<<15); + gmc->Uco = ((pts->duv[0].x-1)<<(17+ accuracy)) + (1<<17); + gmc->Vco = ((pts->duv[0].y-1)<<(17+ accuracy)) + (1<<17); + gmc->Uco = (gmc->Uco + gmc->dU[0] + gmc->dU[1])>>2; + gmc->Vco = (gmc->Vco + gmc->dV[0] + gmc->dV[1])>>2; + + gmc->predict_16x16 = Predict_16x16_C; + gmc->predict_8x8 = Predict_8x8_C; + gmc->get_average_mv = get_average_mv_C; + } +} + +//---------------------------- \ No newline at end of file diff --git a/modules/xvid_dec/xvid_wce/gmc.h b/modules/xvid_dec/xvid_wce/gmc.h new file mode 100644 index 0000000..a729ef0 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/gmc.h @@ -0,0 +1,45 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - GMC interpolation module header - + * + * Copyright(C) 2002-2003 Pascal Massimino <skal@planet-d.net> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: gmc.h,v 1.1.1.1 2005/07/13 14:36:14 jeanlf Exp $ + * + ****************************************************************************/ + +#include "portab.h" +#include "global.h" + + +#define RDIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) +#define RSHIFT(a,b) ( (a)>0 ? ((a) + (1<<((b)-1)))>>(b) : ((a) + (1<<((b)-1))-1)>>(b)) + +#define MLT(i) (((16-(i))<<16) + (i)) +static const dword MTab[16] = { + MLT( 0), MLT( 1), MLT( 2), MLT( 3), MLT( 4), MLT( 5), MLT( 6), MLT( 7), + MLT( 8), MLT( 9), MLT(10), MLT(11), MLT(12), MLT(13), MLT(14), MLT(15) +}; +#undef MLT + + +/* ************************************************************* + * Warning! It's Accuracy being passed, not 'resolution'! + */ +void generate_GMCparameters(int nb_pts, int accuracy, const WARPPOINTS *pts, int width, int height, NEW_GMC_DATA *gmc); + diff --git a/modules/xvid_dec/xvid_wce/idct.cpp b/modules/xvid_dec/xvid_wce/idct.cpp new file mode 100644 index 0000000..fa11bf7 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/idct.cpp @@ -0,0 +1,210 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Inverse DCT - + * + * These routines are from Independent JPEG Group's free JPEG software + * Copyright (C) 1991-1998, Thomas G. Lane (see the file README.IJG) + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: idct.cpp,v 1.2 2006/12/13 15:12:27 jeanlf Exp $ + * + ****************************************************************************/ + +/* Copyright (C) 1996, MPEG Software Simulation Group. All Rights Reserved. */ + +/* + * Disclaimer of Warranty + * + * These software programs are available to the user without any license fee or + * royalty on an "as is" basis. The MPEG Software Simulation Group disclaims + * any and all warranties, whether express, implied, or statuary, including any + * implied warranties or merchantability or of fitness for a particular + * purpose. In no event shall the copyright-holder be liable for any + * incidental, punitive, or consequential damages of any kind whatsoever + * arising from the use of these programs. + * + * This disclaimer of warranty extends to the user of these programs and user's + * customers, employees, agents, transferees, successors, and assigns. + * + * The MPEG Software Simulation Group does not represent or warrant that the + * programs furnished hereunder are free of infringement of any third-party + * patents. + * + * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware, + * are subject to royalty fees to patent holders. Many of these patents are + * general enough such that they are unavoidable regardless of implementation + * design. + * + * MPEG2AVI + * -------- + * v0.16B33 renamed the initialization function to init_idct_int32() + * v0.16B32 removed the unused idct_row() and idct_col() functions + * v0.16B3 changed var declarations to static, to enforce data align + * v0.16B22 idct_FAST() renamed to idct_int32() + * also merged idct_FAST() into a single function, to help VC++ + * optimize it. + * + * v0.14 changed int to long, to avoid confusion when compiling on x86 + * platform ( in VC++ "int" -> 32bits ) + */ + +/**********************************************************/ +/* inverse two dimensional DCT, Chen-Wang algorithm */ +/* (cf. IEEE ASSP-32, pp. 803-816, Aug. 1984) */ +/* 32-bit integer arithmetic (8 bit coefficients) */ +/* 11 mults, 29 adds per DCT */ +/* sE, 18.8.91 */ +/**********************************************************/ +/* coefficients extended to 12 bit for IEEE1180-1990 */ +/* compliance sE, 2.1.94 */ +/**********************************************************/ + +/* this code assumes >> to be a two's-complement arithmetic */ +/* right shift: (-2)>>1 == -1 , (-3)>>1 == -2 */ + +#include "Decoder.h" + +const int __W1 = 2841, //2048*sqrt(2)*cos(1*pi/16) + __W2 = 2676, // 2048*sqrt(2)*cos(2*pi/16) + __W3 = 2408, // 2048*sqrt(2)*cos(3*pi/16) + __W5 = 1609, // 2048*sqrt(2)*cos(5*pi/16) + __W6 = 1108, // 2048*sqrt(2)*cos(6*pi/16) + __W7 = 565; // 2048*sqrt(2)*cos(7*pi/16) + +//---------------------------- +/* two dimensional inverse discrete cosine transform */ +//idct_int32_init() must be called before the first call to this function! +void S_decoder::InverseDiscreteCosineTransform(int *block) const{ + + const t_clip_val *iclp = iclip + 512; +#if defined USE_ARM_ASM + void InverseDiscreteCosineTransform_ARM(int *block, const int *iclip); + InverseDiscreteCosineTransform_ARM(block, iclp); +#else + int i; + //idct rows + for(i = 8; i--; block += 8){ + int X0 = block[0]; + int X1, X2, X3, X4, X5, X6, X7; + if(!((X1 = block[4]) | (X2 = block[6]) | (X3 = block[2]) | (X4 = block[1]) | (X5 = block[7]) | (X6 = block[5]) | (X7 = block[3]))){ + block[0] = block[1] = block[2] = block[3] = block[4] = block[5] = block[6] = block[7] = X0 << 3; + continue; + } + //for proper rounding in the fourth stage + X0 = (X0 << 11) + 128; + X1 <<= 11; + + //first stage + int X8 = __W7 * (X4 + X5); + X4 = X8 + (__W1 - __W7) * X4; + X5 = X8 - (__W1 + __W7) * X5; + X8 = __W3 * (X6 + X7); + X6 = X8 - (__W3 - __W5) * X6; + X7 = X8 - (__W3 + __W5) * X7; + + //second stage + X8 = X0 + X1; + X0 -= X1; + X1 = __W6 * (X3 + X2); + X2 = X1 - (__W2 + __W6) * X2; + X3 = X1 + (__W2 - __W6) * X3; + X1 = X4 + X6; + X4 -= X6; + X6 = X5 + X7; + X5 -= X7; + + //third stage + X7 = X8 + X3; + X8 -= X3; + X3 = X0 + X2; + X0 -= X2; + X2 = (181 * (X4 + X5) + 128) >> 8; + X4 = (181 * (X4 - X5) + 128) >> 8; + + //fourth stage + block[0] = ((X7 + X1) >> 8); + block[1] = ((X3 + X2) >> 8); + block[2] = ((X0 + X4) >> 8); + block[3] = ((X8 + X6) >> 8); + block[4] = ((X8 - X6) >> 8); + block[5] = ((X0 - X4) >> 8); + block[6] = ((X3 - X2) >> 8); + block[7] = ((X7 - X1) >> 8); + } + block -= 8*8; + //idct columns + for(i = 8; i--; ++block){ + int X0 = block[8 * 0]; + int X1, X2, X3, X4, X5, X6, X7; + //shortcut + if(!((X1 = block[8 * 4]) | (X2 = block[8 * 6]) | (X3 = block[8 * 2]) | (X4 = block[8 * 1]) | (X5 = block[8 * 7]) | (X6 = block[8 * 5]) | (X7 = block[8 * 3]))){ + block[8 * 0] = block[8 * 1] = block[8 * 2] = block[8 * 3] = block[8 * 4] = block[8 * 5] = block[8 * 6] = block[8 * 7] = iclp[(X0 + 32) >> 6]; + continue; + } + X0 = (X0 << 8) + 8192; + X1 <<= 8; + + //first stage + int X8 = __W7 * (X4 + X5) + 4; + X4 = (X8 + (__W1 - __W7) * X4) >> 3; + X5 = (X8 - (__W1 + __W7) * X5) >> 3; + X8 = __W3 * (X6 + X7) + 4; + X6 = (X8 - (__W3 - __W5) * X6) >> 3; + X7 = (X8 - (__W3 + __W5) * X7) >> 3; + + //second stage + X8 = X0 + X1; + X0 -= X1; + X1 = __W6 * (X3 + X2) + 4; + X2 = (X1 - (__W2 + __W6) * X2) >> 3; + X3 = (X1 + (__W2 - __W6) * X3) >> 3; + X1 = X4 + X6; + X4 -= X6; + X6 = X5 + X7; + X5 -= X7; + + //third stage + X7 = X8 + X3; + X8 -= X3; + X3 = X0 + X2; + X0 -= X2; + X2 = (181 * (X4 + X5) + 128) >> 8; + X4 = (181 * (X4 - X5) + 128) >> 8; + + //fourth stage + block[8 * 0] = iclp[(X7 + X1) >> 14]; + block[8 * 1] = iclp[(X3 + X2) >> 14]; + block[8 * 2] = iclp[(X0 + X4) >> 14]; + block[8 * 3] = iclp[(X8 + X6) >> 14]; + block[8 * 4] = iclp[(X8 - X6) >> 14]; + block[8 * 5] = iclp[(X0 - X4) >> 14]; + block[8 * 6] = iclp[(X3 - X2) >> 14]; + block[8 * 7] = iclp[(X7 - X1) >> 14]; + } +#endif +} + +//---------------------------- + +void S_decoder::idct_int32_init(){ + + t_clip_val *iclp = iclip + 512; + for(int i = -512; i < 512; i++) + iclp[i] = (i < -256) ? -256 : ((i > 255) ? 255 : i); +} + +//---------------------------- diff --git a/modules/xvid_dec/xvid_wce/image.cpp b/modules/xvid_dec/xvid_wce/image.cpp new file mode 100644 index 0000000..0ad1c93 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/image.cpp @@ -0,0 +1,356 @@ +/************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Image management functions - + * + * Copyright(C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: image.cpp,v 1.1.1.1 2005/07/13 14:36:14 jeanlf Exp $ + * + ****************************************************************************/ +//#include <math.h> + +#include "portab.h" +#include "global.h" /* XVID_CSP_XXX's */ +#include "xvid.h" /* XVID_CSP_XXX's */ +#include "image.h" +#include "interpolate8x8.h" +#include "reduced.h" +#include "decoder.h" +#include "mem_align.h" + +#define SAFETY 64 +#define EDGE_SIZE2 (EDGE_SIZE/2) + +//---------------------------- + +int image_create(IMAGE * image, dword edged_width, dword edged_height){ + + const dword edged_width2 = edged_width / 2; + const dword edged_height2 = edged_height / 2; + + image->y = (unsigned char*)xvid_malloc(edged_width * (edged_height + 1) + SAFETY, CACHE_LINE); + if (image->y == NULL) { + return -1; + } + MemSet(image->y, 0, edged_width * (edged_height + 1) + SAFETY); + + image->u = (unsigned char*)xvid_malloc(edged_width2 * edged_height2 + SAFETY, CACHE_LINE); + if (image->u == NULL) { + xvid_free(image->y); + image->y = NULL; + return -1; + } + MemSet(image->u, 0, edged_width2 * edged_height2 + SAFETY); + + image->v = (unsigned char*)xvid_malloc(edged_width2 * edged_height2 + SAFETY, CACHE_LINE); + if (image->v == NULL) { + xvid_free(image->u); + image->u = NULL; + xvid_free(image->y); + image->y = NULL; + return -1; + } + MemSet(image->v, 0, edged_width2 * edged_height2 + SAFETY); + + image->y += EDGE_SIZE * edged_width + EDGE_SIZE; + image->u += EDGE_SIZE2 * edged_width2 + EDGE_SIZE2; + image->v += EDGE_SIZE2 * edged_width2 + EDGE_SIZE2; + + return 0; +} + +//---------------------------- + +void image_destroy(IMAGE * image, dword edged_width, dword edged_height){ + + const dword edged_width2 = edged_width / 2; + + if(image->y){ + xvid_free(image->y - (EDGE_SIZE * edged_width + EDGE_SIZE)); + image->y = NULL; + } + if(image->u){ + xvid_free(image->u - (EDGE_SIZE2 * edged_width2 + EDGE_SIZE2)); + image->u = NULL; + } + if(image->v){ + xvid_free(image->v - (EDGE_SIZE2 * edged_width2 + EDGE_SIZE2)); + image->v = NULL; + } +} + +//---------------------------- + +void IMAGE::Swap(IMAGE *image2){ + ::Swap(y, image2->y); + ::Swap(u, image2->u); + ::Swap(v, image2->v); +} + +//---------------------------- + +#ifdef _ARM_ + +extern"C" +void XVID_MemCpy(void *dst, const void *src, dword count); + +#else + +void XVID_MemCpy(void *dst, const void *src, dword count){ + assert(!(dword(dst)&3)); + assert(!(dword(count)&3)); +#ifdef USE_ARM_ASM + int t0, t1, t2, t3; + asm volatile( + "subs %2, %2, #16\n blt .mc_no16\n\t" + "\n.mc_loop16:\n\t" + //the order of regs in {} is not important, now it's ordered as to avoid compiler warnings + "ldmia %1!, {%6, %3, %5, %4}\n\t" + "stmia %0!, {%6, %3, %5, %4}\n\t" + "subs %2, %2, #16\n bge .mc_loop16\n\t" + "\n.mc_no16:\n\t" + "adds %2, %2, #16\n\t" + "beq .mc_end\n\t" + "\n.mc_loop4:\n\t" + "ldr %3, [%1], #4\n\t" + "str %3, [%0], #4\n\t" + "subs %2, %2, #4\n bne .mc_loop4\n\t" + "\n.mc_end:\n\t" + : "+r"(dst), "+r"(src), "+r"(count), "&=r"(t0), "&=r"(t1), "&=r"(t2), "&=r"(t3) + : + ); +#else + MemCpy(dst, src, count); +#endif +} + +#endif + +//--------------------------- + +void IMAGE::Copy(const IMAGE * image2, dword edged_width, dword height){ + XVID_MemCpy(y, image2->y, edged_width * height); + XVID_MemCpy(u, image2->u, edged_width * height / 4); + XVID_MemCpy(v, image2->v, edged_width * height / 4); +} + +//---------------------------- + +inline void XVID_Set16bytes(void *dst, dword val){ + assert(!(dword(dst)&3)); + val |= val<<8; + val |= val<<16; + ((dword*)dst)[0] = val; + ((dword*)dst)[1] = val; + ((dword*)dst)[2] = val; + ((dword*)dst)[3] = val; +} + +//--------------------------- + +inline void XVID_Set8bytes(void *dst, dword val){ + assert(!(dword(dst)&3)); + val |= val<<8; + val |= val<<16; + ((dword*)dst)[0] = val; + ((dword*)dst)[1] = val; +} + +//-------------------------- + +void S_decoder::SetEdges(IMAGE &img) const{ + + const dword edged_width2 = edged_width / 2; + + assert(EDGE_SIZE==16); + + byte *dst = img.y - (EDGE_SIZE + EDGE_SIZE * edged_width); + const byte *src = img.y; + + //according to the Standard Clause 7.6.4, padding is done starting at 16 + // * pixel width and height multiples + dword width = (S_decoder::width+15)&~15; + dword height = (S_decoder::height+15)&~15; + dword width2 = width/2; + + dword i; + for(i = 0; i < EDGE_SIZE; i++){ + XVID_Set16bytes(dst, *src); + XVID_MemCpy(dst + EDGE_SIZE, src, width); + XVID_Set16bytes(dst + edged_width - EDGE_SIZE, *(src + width - 1)); + dst += edged_width; + } + for(i = 0; i < height; i++){ + XVID_Set16bytes(dst, *src); + XVID_Set16bytes(dst + edged_width - EDGE_SIZE, src[width - 1]); + dst += edged_width; + src += edged_width; + } + + src -= edged_width; + for(i = 0; i < EDGE_SIZE; i++){ + XVID_Set16bytes(dst, *src); + XVID_MemCpy(dst + EDGE_SIZE, src, width); + XVID_Set16bytes(dst + edged_width - EDGE_SIZE, *(src + width - 1)); + dst += edged_width; + } + + //U + dst = img.u - (EDGE_SIZE2 + EDGE_SIZE2 * edged_width2); + src = img.u; + + for (i = 0; i < EDGE_SIZE2; i++) { + XVID_Set8bytes(dst, *src); + XVID_MemCpy(dst + EDGE_SIZE2, src, width2); + XVID_Set8bytes(dst + edged_width2 - EDGE_SIZE2, *(src + width2 - 1)); + dst += edged_width2; + } + + for (i = 0; i < height / 2; i++) { + XVID_Set8bytes(dst, *src); + XVID_Set8bytes(dst + edged_width2 - EDGE_SIZE2, src[width2 - 1]); + dst += edged_width2; + src += edged_width2; + } + src -= edged_width2; + for (i = 0; i < EDGE_SIZE2; i++) { + XVID_Set8bytes(dst, *src); + XVID_MemCpy(dst + EDGE_SIZE2, src, width2); + XVID_Set8bytes(dst + edged_width2 - EDGE_SIZE2, *(src + width2 - 1)); + dst += edged_width2; + } + + //V + dst = img.v - (EDGE_SIZE2 + EDGE_SIZE2 * edged_width2); + src = img.v; + + for(i = 0; i < EDGE_SIZE2; i++){ + XVID_Set8bytes(dst, *src); + XVID_MemCpy(dst + EDGE_SIZE2, src, width2); + XVID_Set8bytes(dst + edged_width2 - EDGE_SIZE2, *(src + width2 - 1)); + dst += edged_width2; + } + + for(i = 0; i < height / 2; i++){ + XVID_Set8bytes(dst, *src); + XVID_Set8bytes(dst + edged_width2 - EDGE_SIZE2, src[width2 - 1]); + dst += edged_width2; + src += edged_width2; + } + src -= edged_width2; + for(i = 0; i < EDGE_SIZE2; i++){ + XVID_Set8bytes(dst, *src); + XVID_MemCpy(dst + EDGE_SIZE2, src, width2); + XVID_Set8bytes(dst + edged_width2 - EDGE_SIZE2, *(src + width2 - 1)); + dst += edged_width2; + } +} + +//---------------------------- + +void IMAGE::Clear(int width, int height, int edged_width, int y, int u, int v){ + + byte * p; + int i; + + p = IMAGE::y; + for(i = 0; i < height; i++){ + MemSet(p, y, width); + p += edged_width; + } + + p = IMAGE::u; + for(i = 0; i < height/2; i++){ + MemSet(p, u, width/2); + p += edged_width/2; + } + + p = IMAGE::v; + for(i = 0; i < height/2; i++){ + MemSet(p, v, width/2); + p += edged_width/2; + } +} + +//---------------------------- +/* reduced resolution deblocking filter + block = block size (16=rrv, 8=full resolution) + flags = XVID_DEC_YDEBLOCK|XVID_DEC_UVDEBLOCK +*/ +void IMAGE::deblock_rrv(int edged_width, const MACROBLOCK * mbs, int mb_width, int mb_height, int mb_stride, int block, int flags){ + + const int edged_width2 = edged_width /2; + const int nblocks = block / 8; //skals code uses 8pixel block uints + int i,j; + + for (j = 1; j < mb_height*2; j++) //horizontal deblocking + for (i = 0; i < mb_width*2; i++) + { + if(mbs[(j-1)/2*mb_stride + (i/2)].mode != MODE_NOT_CODED || + mbs[(j+0)/2*mb_stride + (i/2)].mode != MODE_NOT_CODED) + { + hfilter_31(IMAGE::y + (j*block - 1)*edged_width + i*block, + IMAGE::y + (j*block + 0)*edged_width + i*block, nblocks); + } + } + + for (j = 0; j < mb_height*2; j++) // vertical deblocking + for (i = 1; i < mb_width*2; i++) + { + if (mbs[(j/2)*mb_stride + (i-1)/2].mode != MODE_NOT_CODED || + mbs[(j/2)*mb_stride + (i+0)/2].mode != MODE_NOT_CODED) + { + vfilter_31(IMAGE::y + (j*block)*edged_width + i*block - 1, + IMAGE::y + (j*block)*edged_width + i*block + 0, + edged_width, nblocks); + } + } + + + //chroma + + for (j = 1; j < mb_height; j++) //orizontal deblocking + for (i = 0; i < mb_width; i++) + { + if (mbs[(j-1)*mb_stride + i].mode != MODE_NOT_CODED || + mbs[(j+0)*mb_stride + i].mode != MODE_NOT_CODED) + { + hfilter_31(IMAGE::u + (j*block - 1)*edged_width2 + i*block, + IMAGE::u + (j*block + 0)*edged_width2 + i*block, nblocks); + hfilter_31(IMAGE::v + (j*block - 1)*edged_width2 + i*block, + IMAGE::v + (j*block + 0)*edged_width2 + i*block, nblocks); + } + } + + for (j = 0; j < mb_height; j++) //ertical deblocking + for (i = 1; i < mb_width; i++) + { + if (mbs[j*mb_stride + i - 1].mode != MODE_NOT_CODED || + mbs[j*mb_stride + i + 0].mode != MODE_NOT_CODED) + { + vfilter_31(IMAGE::u + (j*block)*edged_width2 + i*block - 1, + IMAGE::u + (j*block)*edged_width2 + i*block + 0, + edged_width2, nblocks); + vfilter_31(IMAGE::v + (j*block)*edged_width2 + i*block - 1, + IMAGE::v + (j*block)*edged_width2 + i*block + 0, + edged_width2, nblocks); + } + } +} + +//---------------------------- diff --git a/modules/xvid_dec/xvid_wce/image.h b/modules/xvid_dec/xvid_wce/image.h new file mode 100644 index 0000000..9ff217e --- /dev/null +++ b/modules/xvid_dec/xvid_wce/image.h @@ -0,0 +1,37 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Image related header - + * + * Copyright(C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: image.h,v 1.1.1.1 2005/07/13 14:36:14 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _IMAGE_H_ +#define _IMAGE_H_ + +#include "portab.h" +#include "global.h" +#include "xvid.h" + +int image_create(IMAGE * image, dword edged_width, dword edged_height); +void image_destroy(IMAGE * image, dword edged_width, dword edged_height); + + +#endif /* _IMAGE_H_ */ diff --git a/modules/xvid_dec/xvid_wce/interpolate8x8.cpp b/modules/xvid_dec/xvid_wce/interpolate8x8.cpp new file mode 100644 index 0000000..ea32053 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/interpolate8x8.cpp @@ -0,0 +1,872 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - 8x8 block-based halfpel interpolation - + * + * Copyright(C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: interpolate8x8.cpp,v 1.1.1.1 2005/07/13 14:36:15 jeanlf Exp $ + * + ****************************************************************************/ + +#include "portab.h" +#include "global.h" +#include "interpolate8x8.h" + +//---------------------------- + +void interpolate8x8_switch(byte *cur, const byte *refn, dword x, dword y, int dx, int dy, dword stride, bool rounding){ + + const byte *src = refn + ((y + (dy>>1)) * stride + x + (dx>>1)); + byte *dst = cur + (y * stride + x); + + switch(((dx & 1) << 1) + (dy & 1)){ + case 0: + transfer8x8_copy(dst, src, stride); + break; + case 1: + interpolate8x8_halfpel_v(dst, src, stride, rounding); + break; + case 2: + interpolate8x8_halfpel_h(dst, src, stride, rounding); + break; + default: + interpolate8x8_halfpel_hv(dst, src, stride, rounding); + break; + } +} + +//---------------------------- + +void interpolate8x8_quarterpel(byte *cur, byte *refn, byte *refh, byte *refv, byte *refhv, dword x, dword y, int dx, int dy, dword stride, bool rounding){ + + const int xRef = x*4 + dx; + const int yRef = y*4 + dy; + + byte *src, *dst; + byte *halfpel_h, *halfpel_v, *halfpel_hv; + int x_int, y_int, x_frac, y_frac; + + x_int = xRef/4; + if(xRef < 0 && xRef % 4) + x_int--; + + x_frac = xRef - (4*x_int); + + y_int = yRef/4; + if (yRef < 0 && yRef % 4) + y_int--; + + y_frac = yRef - (4*y_int); + + src = refn + y_int * stride + x_int; + halfpel_h = refh; + halfpel_v = refv; + halfpel_hv = refhv; + + dst = cur + y * stride + x; + + switch((y_frac << 2) | (x_frac)) { + + case 0: + transfer8x8_copy(dst, src, stride); + break; + + case 1: + interpolate8x8_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(dst, src, halfpel_h, stride, rounding, 8); + break; + + case 2: + interpolate8x8_lowpass_h(dst, src, stride, rounding); + break; + + case 3: + interpolate8x8_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(dst, src + 1, halfpel_h, stride, rounding, 8); + break; + + case 4: + interpolate8x8_lowpass_v(halfpel_v, src, stride, rounding); + interpolate8x8_avg2(dst, src, halfpel_v, stride, rounding, 8); + break; + + case 5: + interpolate8x8_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src, halfpel_h, stride, rounding, 9); + interpolate8x8_lowpass_v(halfpel_hv, halfpel_v, stride, rounding); + interpolate8x8_avg2(dst, halfpel_v, halfpel_hv, stride, rounding, 8); + break; + + case 6: + interpolate8x8_lowpass_hv(halfpel_hv, halfpel_h, src, stride, rounding); + interpolate8x8_avg2(dst, halfpel_h, halfpel_hv, stride, rounding, 8); + break; + + case 7: + interpolate8x8_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src + 1, halfpel_h, stride, rounding, 9); + interpolate8x8_lowpass_v(halfpel_hv, halfpel_v, stride, rounding); + interpolate8x8_avg2(dst, halfpel_v, halfpel_hv, stride, rounding, 8); + break; + + case 8: + interpolate8x8_lowpass_v(dst, src, stride, rounding); + break; + + case 9: + interpolate8x8_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src, halfpel_h, stride, rounding, 9); + interpolate8x8_lowpass_v(dst, halfpel_v, stride, rounding); + break; + + case 10: + interpolate8x8_lowpass_hv(dst, halfpel_h, src, stride, rounding); + break; + + case 11: + interpolate8x8_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src + 1, halfpel_h, stride, rounding, 9); + interpolate8x8_lowpass_v(dst, halfpel_v, stride, rounding); + break; + + case 12: + interpolate8x8_lowpass_v(halfpel_v, src, stride, rounding); + interpolate8x8_avg2(dst, src+stride, halfpel_v, stride, rounding, 8); + break; + + case 13: + interpolate8x8_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src, halfpel_h, stride, rounding, 9); + interpolate8x8_lowpass_v(halfpel_hv, halfpel_v, stride, rounding); + interpolate8x8_avg2(dst, halfpel_v+stride, halfpel_hv, stride, rounding, 8); + break; + + case 14: + interpolate8x8_lowpass_hv(halfpel_hv, halfpel_h, src, stride, rounding); + interpolate8x8_avg2(dst, halfpel_h+stride, halfpel_hv, stride, rounding, 8); + break; + + case 15: + interpolate8x8_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src + 1, halfpel_h, stride, rounding, 9); + interpolate8x8_lowpass_v(halfpel_hv, halfpel_v, stride, rounding); + interpolate8x8_avg2(dst, halfpel_hv, halfpel_v + stride, stride, rounding, 8); + break; + } +} + +//---------------------------- + +void interpolate16x16_quarterpel(byte *cur, byte *refn, byte *refh, byte *refv, byte *refhv, dword x, dword y, int dx, int dy, dword stride, bool rounding){ + + const int xRef = x*4 + dx; + const int yRef = y*4 + dy; + + byte *src, *dst; + byte *halfpel_h, *halfpel_v, *halfpel_hv; + int x_int, y_int, x_frac, y_frac; + + x_int = xRef/4; + if (xRef < 0 && xRef % 4) + x_int--; + + x_frac = xRef - (4*x_int); + + y_int = yRef/4; + if (yRef < 0 && yRef % 4) + y_int--; + + y_frac = yRef - (4*y_int); + + src = refn + y_int * stride + x_int; + halfpel_h = refh; + halfpel_v = refv; + halfpel_hv = refhv; + + dst = cur + y * stride + x; + + switch((y_frac << 2) | (x_frac)){ + case 0: + transfer16x16_copy(dst, src, stride); + break; + + case 1: + interpolate16x16_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(dst, src, halfpel_h, stride, rounding, 8); + interpolate8x8_avg2(dst+8, src+8, halfpel_h+8, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride, src+8*stride, halfpel_h+8*stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride+8, src+8*stride+8, halfpel_h+8*stride+8, stride, rounding, 8); + break; + + case 2: + interpolate16x16_lowpass_h(dst, src, stride, rounding); + break; + + case 3: + interpolate16x16_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(dst, src + 1, halfpel_h, stride, rounding, 8); + interpolate8x8_avg2(dst+8, src + 8 + 1, halfpel_h+8, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride, src + 8*stride + 1, halfpel_h+8*stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride+8, src+8*stride+8 + 1, halfpel_h+8*stride+8, stride, rounding, 8); + break; + + case 4: + interpolate16x16_lowpass_v(halfpel_v, src, stride, rounding); + interpolate8x8_avg2(dst, src, halfpel_v, stride, rounding, 8); + interpolate8x8_avg2(dst+8, src+8, halfpel_v+8, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride, src+8*stride, halfpel_v+8*stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride+8, src+8*stride+8, halfpel_v+8*stride+8, stride, rounding, 8); + break; + + case 5: + interpolate16x16_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src, halfpel_h, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8, src + 8, halfpel_h+8, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride, src + 8*stride, halfpel_h+8*stride, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride+8, src+8*stride+8, halfpel_h+8*stride+8, stride, rounding, 9); + + interpolate16x16_lowpass_v(halfpel_hv, halfpel_v, stride, rounding); + interpolate8x8_avg2(dst, halfpel_hv, halfpel_v, stride, rounding, 8); + interpolate8x8_avg2(dst+8, halfpel_hv+8, halfpel_v+8, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride, halfpel_hv+8*stride, halfpel_v+8*stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride+8, halfpel_hv+8*stride+8, halfpel_v+8*stride+8, stride, rounding, 8); + break; + + case 6: + interpolate16x16_lowpass_hv(halfpel_hv, halfpel_h, src, stride, rounding); + interpolate8x8_avg2(dst, halfpel_h, halfpel_hv, stride, rounding, 8); + interpolate8x8_avg2(dst+8, halfpel_h+8, halfpel_hv+8, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride, halfpel_h+8*stride, halfpel_hv+8*stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride+8, halfpel_h+8*stride+8, halfpel_hv+8*stride+8, stride, rounding, 8); + break; + + case 7: + interpolate16x16_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src+1, halfpel_h, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8, src+1 + 8, halfpel_h+8, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride, src+1 + 8*stride, halfpel_h+8*stride, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride+8, src+1+8*stride+8, halfpel_h+8*stride+8, stride, rounding, 9); + + interpolate16x16_lowpass_v(halfpel_hv, halfpel_v, stride, rounding); + interpolate8x8_avg2(dst, halfpel_hv, halfpel_v, stride, rounding, 8); + interpolate8x8_avg2(dst+8, halfpel_hv+8, halfpel_v+8, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride, halfpel_hv+8*stride, halfpel_v+8*stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride+8, halfpel_hv+8*stride+8, halfpel_v+8*stride+8, stride, rounding, 8); + break; + + case 8: + interpolate16x16_lowpass_v(dst, src, stride, rounding); + break; + + case 9: + interpolate16x16_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src, halfpel_h, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8, src + 8, halfpel_h+8, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride, src + 8*stride, halfpel_h+8*stride, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride+8, src+8*stride+8, halfpel_h+8*stride+8, stride, rounding, 9); + interpolate16x16_lowpass_v(dst, halfpel_v, stride, rounding); + break; + + case 10: + interpolate16x16_lowpass_hv(dst, halfpel_h, src, stride, rounding); + break; + + case 11: + interpolate16x16_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src+1, halfpel_h, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8, src+1 + 8, halfpel_h+8, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride, src+1 + 8*stride, halfpel_h+8*stride, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride+8, src+1+8*stride+8, halfpel_h+8*stride+8, stride, rounding, 9); + interpolate16x16_lowpass_v(dst, halfpel_v, stride, rounding); + break; + + case 12: + interpolate16x16_lowpass_v(halfpel_v, src, stride, rounding); + interpolate8x8_avg2(dst, src+stride, halfpel_v, stride, rounding, 8); + interpolate8x8_avg2(dst+8, src+stride+8, halfpel_v+8, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride, src+stride+8*stride, halfpel_v+8*stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride+8, src+stride+8*stride+8, halfpel_v+8*stride+8, stride, rounding, 8); + break; + + case 13: + interpolate16x16_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src, halfpel_h, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8, src + 8, halfpel_h+8, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride, src + 8*stride, halfpel_h+8*stride, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride+8, src+8*stride+8, halfpel_h+8*stride+8, stride, rounding, 9); + + interpolate16x16_lowpass_v(halfpel_hv, halfpel_v, stride, rounding); + interpolate8x8_avg2(dst, halfpel_hv, halfpel_v+stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8, halfpel_hv+8, halfpel_v+stride+8, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride, halfpel_hv+8*stride, halfpel_v+stride+8*stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride+8, halfpel_hv+8*stride+8, halfpel_v+stride+8*stride+8, stride, rounding, 8); + break; + + case 14: + interpolate16x16_lowpass_hv(halfpel_hv, halfpel_h, src, stride, rounding); + interpolate8x8_avg2(dst, halfpel_h+stride, halfpel_hv, stride, rounding, 8); + interpolate8x8_avg2(dst+8, halfpel_h+stride+8, halfpel_hv+8, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride, halfpel_h+stride+8*stride, halfpel_hv+8*stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride+8, halfpel_h+stride+8*stride+8, halfpel_hv+8*stride+8, stride, rounding, 8); + break; + + case 15: + interpolate16x16_lowpass_h(halfpel_h, src, stride, rounding); + interpolate8x8_avg2(halfpel_v, src+1, halfpel_h, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8, src+1 + 8, halfpel_h+8, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride, src+1 + 8*stride, halfpel_h+8*stride, stride, rounding, 9); + interpolate8x8_avg2(halfpel_v+8*stride+8, src+1+8*stride+8, halfpel_h+8*stride+8, stride, rounding, 9); + + interpolate16x16_lowpass_v(halfpel_hv, halfpel_v, stride, rounding); + interpolate8x8_avg2(dst, halfpel_hv, halfpel_v+stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8, halfpel_hv+8, halfpel_v+stride+8, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride, halfpel_hv+8*stride, halfpel_v+stride+8*stride, stride, rounding, 8); + interpolate8x8_avg2(dst+8*stride+8, halfpel_hv+8*stride+8, halfpel_v+stride+8*stride+8, stride, rounding, 8); + break; + } +} + +//---------------------------- + +void interpolate8x8_avg2(byte *dst, const byte *src1, const byte *src2, dword stride, bool rounding, dword height){ + + const int round = 1 - rounding; + for(dword i = 0; i < height; i++){ + dst[0] = (src1[0] + src2[0] + round) >> 1; + dst[1] = (src1[1] + src2[1] + round) >> 1; + dst[2] = (src1[2] + src2[2] + round) >> 1; + dst[3] = (src1[3] + src2[3] + round) >> 1; + dst[4] = (src1[4] + src2[4] + round) >> 1; + dst[5] = (src1[5] + src2[5] + round) >> 1; + dst[6] = (src1[6] + src2[6] + round) >> 1; + dst[7] = (src1[7] + src2[7] + round) >> 1; + + dst += stride; + src1 += stride; + src2 += stride; + } +} + +//---------------------------- + +#ifndef _ARM_ + +void interpolate8x8_halfpel_h(byte *dst, const byte *src, dword stride, bool rounding){ + +#if defined USE_ARM_ASM && 1 + int y, tmp, tmp1, tmp2, tmp3; + if(rounding){ + asm volatile( + "mov %7, #8\n\t" + "\n.ihh_loop:\n\t" + "ldrb %2, [%1, #8]\n\t" + //7+8 + "ldrb %3, [%1, #7]\n\t" + "add %4, %2, %3\n\t" + "mov %4, %4, asr #1\n\t" + //6+7 + "ldrb %2, [%1, #6]\n\t" + "add %5, %2, %3\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + //5+6 + "ldrb %3, [%1, #5]\n\t" + "add %5, %2, %3\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + //4+5 + "ldrb %2, [%1, #4]\n\t" + "add %5, %2, %3\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + + "str %4, [%0, #4]\n\t" + + //3+4 + "ldrb %3, [%1, #3]\n\t" + "add %4, %2, %3\n\t" + "mov %4, %4, asr #1\n\t" + //2+3 + "ldrb %2, [%1, #2]\n\t" + "add %5, %2, %3\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + //1+2 + "ldrb %3, [%1, #1]\n\t" + "add %5, %2, %3\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + //0+1 + "ldrb %2, [%1, #0]\n\t" + "add %5, %2, %3\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + + "str %4, [%0, #0]\n\t" + + "add %0, %0, %6\n add %1, %1, %6\n\t" + "subs %7, %7, #1\n bne .ihh_loop\n\t" + : "+r"(dst), "+r"(src), "&=r"(tmp), "&=r"(tmp1), "&=r"(tmp2), "&=r"(tmp3) + : "r"(stride), "%r"(y) + ); + }else{ + asm volatile( + "mov %7, #8\n\t" + "\n.ihh_loop1:\n\t" + "ldrb %2, [%1, #8]\n\t" + //7+8 + "ldrb %3, [%1, #7]\n\t" + "add %4, %2, %3\n\t" + "add %4, %4, #1\n\t" + "mov %4, %4, asr #1\n\t" + //6+7 + "ldrb %2, [%1, #6]\n\t" + "add %5, %2, %3\n\t" + "add %5, %5, #1\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + //5+6 + "ldrb %3, [%1, #5]\n\t" + "add %5, %2, %3\n\t" + "add %5, %5, #1\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + //4+5 + "ldrb %2, [%1, #4]\n\t" + "add %5, %2, %3\n\t" + "add %5, %5, #1\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + + "str %4, [%0, #4]\n\t" + + //3+4 + "ldrb %3, [%1, #3]\n\t" + "add %4, %2, %3\n\t" + "add %4, %4, #1\n\t" + "mov %4, %4, asr #1\n\t" + //2+3 + "ldrb %2, [%1, #2]\n\t" + "add %5, %2, %3\n\t" + "add %5, %5, #1\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + //1+2 + "ldrb %3, [%1, #1]\n\t" + "add %5, %2, %3\n\t" + "add %5, %5, #1\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + //0+1 + "ldrb %2, [%1, #0]\n\t" + "add %5, %2, %3\n\t" + "add %5, %5, #1\n\t" + "mov %5, %5, asr #1\n\t" + "orr %4, %5, %4, asl #8\n\t" + + "str %4, [%0, #0]\n\t" + + "add %0, %0, %6\n add %1, %1, %6\n\t" + "subs %7, %7, #1\n bne .ihh_loop1\n\t" + : "+r"(dst), "+r"(src), "&=r"(tmp), "&=r"(tmp1), "&=r"(tmp2), "&=r"(tmp3) + : "r"(stride), "%r"(y) + ); + } +#else + if(rounding){ + for(dword j = 0; j < 8*stride; j+=stride){ + dst[j + 0] = byte((src[j + 0] + src[j + 1] )>>1); + dst[j + 1] = byte((src[j + 1] + src[j + 2] )>>1); + dst[j + 2] = byte((src[j + 2] + src[j + 3] )>>1); + dst[j + 3] = byte((src[j + 3] + src[j + 4] )>>1); + dst[j + 4] = byte((src[j + 4] + src[j + 5] )>>1); + dst[j + 5] = byte((src[j + 5] + src[j + 6] )>>1); + dst[j + 6] = byte((src[j + 6] + src[j + 7] )>>1); + dst[j + 7] = byte((src[j + 7] + src[j + 8] )>>1); + } + }else{ + for(dword j = 0; j < 8*stride; j+=stride){ + //forward or backwards? Who knows ... + dst[j + 0] = byte((src[j + 0] + src[j + 1] + 1)>>1); + dst[j + 1] = byte((src[j + 1] + src[j + 2] + 1)>>1); + dst[j + 2] = byte((src[j + 2] + src[j + 3] + 1)>>1); + dst[j + 3] = byte((src[j + 3] + src[j + 4] + 1)>>1); + dst[j + 4] = byte((src[j + 4] + src[j + 5] + 1)>>1); + dst[j + 5] = byte((src[j + 5] + src[j + 6] + 1)>>1); + dst[j + 6] = byte((src[j + 6] + src[j + 7] + 1)>>1); + dst[j + 7] = byte((src[j + 7] + src[j + 8] + 1)>>1); + } + } +#endif +} + +#endif + +//---------------------------- + +#ifndef _ARM_ + +void interpolate8x8_halfpel_v(byte *dst, const byte *src, dword stride, bool rounding){ + +#if defined USE_ARM_ASM && 1 + int y, tmp, tmp1, tmp2, tmp3, tmp4; + if(rounding){ + asm volatile( + "mov %7, #8\n\t" + "\n.ihv_loop:\n\t" + "add %5, %1, %6\n\t" + //3 + "ldrb %2, [%1, #3]\n ldrb %3, [%5, #3]\n\t" + "add %4, %2, %3\n mov %4, %4, asr #1\n\t" + //2 + "ldrb %2, [%1, #2]\n ldrb %3, [%5, #2]\n\t" + "add %2, %2, %3\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + //1 + "ldrb %2, [%1, #1]\n ldrb %3, [%5, #1]\n\t" + "add %2, %2, %3\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + //0 + "ldrb %2, [%1, #0]\n ldrb %3, [%5, #0]\n\t" + "add %2, %2, %3\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + + "str %4, [%0, #0]\n\t" + + //7 + "ldrb %2, [%1, #7]\n ldrb %3, [%5, #7]\n\t" + "add %4, %2, %3\n mov %4, %4, asr #1\n\t" + //6 + "ldrb %2, [%1, #6]\n ldrb %3, [%5, #6]\n\t" + "add %2, %2, %3\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + //5 + "ldrb %2, [%1, #5]\n ldrb %3, [%5, #5]\n\t" + "add %2, %2, %3\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + //4 + "ldrb %2, [%1, #4]\n ldrb %3, [%5, #4]\n\t" + "add %2, %2, %3\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + + "str %4, [%0, #4]\n\t" + + "add %0, %0, %6\n add %1, %1, %6\n\t" + "subs %7, %7, #1\n bne .ihv_loop\n\t" + : "+r"(dst), "+r"(src), "&=r"(tmp), "&=r"(tmp1), "&=r"(tmp2), "&=r"(tmp3) + : "r"(stride), "%r"(y) + ); + }else{ + asm volatile( + "mov %7, #8\n\t" + "\n.ihv_loop1:\n\t" + "add %5, %1, %6\n\t" + //3 + "ldrb %2, [%1, #3]\n ldrb %3, [%5, #3]\n\t" + "add %4, %2, %3\n add %4, %4, #1\n mov %4, %4, asr #1\n\t" + //2 + "ldrb %2, [%1, #2]\n ldrb %3, [%5, #2]\n\t" + "add %2, %2, %3\n add %2, %2, #1\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + //1 + "ldrb %2, [%1, #1]\n ldrb %3, [%5, #1]\n\t" + "add %2, %2, %3\n add %2, %2, #1\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + //0 + "ldrb %2, [%1, #0]\n ldrb %3, [%5, #0]\n\t" + "add %2, %2, %3\n add %2, %2, #1\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + + "str %4, [%0, #0]\n\t" + + //7 + "ldrb %2, [%1, #7]\n ldrb %3, [%5, #7]\n\t" + "add %4, %2, %3\n add %4, %4, #1\n mov %4, %4, asr #1\n\t" + //6 + "ldrb %2, [%1, #6]\n ldrb %3, [%5, #6]\n\t" + "add %2, %2, %3\n add %2, %2, #1\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + //5 + "ldrb %2, [%1, #5]\n ldrb %3, [%5, #5]\n\t" + "add %2, %2, %3\n add %2, %2, #1\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + //4 + "ldrb %2, [%1, #4]\n ldrb %3, [%5, #4]\n\t" + "add %2, %2, %3\n add %2, %2, #1\n mov %2, %2, asr #1\n\t" + "orr %4, %2, %4, asl #8\n\t" + + "str %4, [%0, #4]\n\t" + + "add %0, %0, %6\n add %1, %1, %6\n\t" + "subs %7, %7, #1\n bne .ihv_loop1\n\t" + : "+r"(dst), "+r"(src), "&=r"(tmp), "&=r"(tmp1), "&=r"(tmp2), "&=r"(tmp3) + : "r"(stride), "%r"(y) + ); + } +#else + if(rounding){ + for(dword j = 0; j < 8*stride; j+=stride){ /* forward is better. Some automatic prefetch perhaps. */ + dst[j + 0] = byte((src[j + 0] + src[j + stride + 0] )>>1); + dst[j + 1] = byte((src[j + 1] + src[j + stride + 1] )>>1); + dst[j + 2] = byte((src[j + 2] + src[j + stride + 2] )>>1); + dst[j + 3] = byte((src[j + 3] + src[j + stride + 3] )>>1); + dst[j + 4] = byte((src[j + 4] + src[j + stride + 4] )>>1); + dst[j + 5] = byte((src[j + 5] + src[j + stride + 5] )>>1); + dst[j + 6] = byte((src[j + 6] + src[j + stride + 6] )>>1); + dst[j + 7] = byte((src[j + 7] + src[j + stride + 7] )>>1); + } + }else{ + for(dword j = 0; j < 8*stride; j+=stride){ + dst[j + 0] = byte((src[j + 0] + src[j + stride + 0] + 1)>>1); + dst[j + 1] = byte((src[j + 1] + src[j + stride + 1] + 1)>>1); + dst[j + 2] = byte((src[j + 2] + src[j + stride + 2] + 1)>>1); + dst[j + 3] = byte((src[j + 3] + src[j + stride + 3] + 1)>>1); + dst[j + 4] = byte((src[j + 4] + src[j + stride + 4] + 1)>>1); + dst[j + 5] = byte((src[j + 5] + src[j + stride + 5] + 1)>>1); + dst[j + 6] = byte((src[j + 6] + src[j + stride + 6] + 1)>>1); + dst[j + 7] = byte((src[j + 7] + src[j + stride + 7] + 1)>>1); + } + } +#endif +} +#endif + +//---------------------------- + +#ifndef _ARM_ //PPC + +#ifndef USE_ARM_ASM //implemented in asm for ARM + +void interpolate8x8_halfpel_hv(byte *dst, const byte *src, dword stride, bool rounding){ + + if(rounding){ + for(dword j = 0; j < 8*stride; j+=stride){ + dst[j + 0] = (byte)((src[j+0] + src[j+1] + src[j+stride+0] + src[j+stride+1] +1)>>2); + dst[j + 1] = (byte)((src[j+1] + src[j+2] + src[j+stride+1] + src[j+stride+2] +1)>>2); + dst[j + 2] = (byte)((src[j+2] + src[j+3] + src[j+stride+2] + src[j+stride+3] +1)>>2); + dst[j + 3] = (byte)((src[j+3] + src[j+4] + src[j+stride+3] + src[j+stride+4] +1)>>2); + dst[j + 4] = (byte)((src[j+4] + src[j+5] + src[j+stride+4] + src[j+stride+5] +1)>>2); + dst[j + 5] = (byte)((src[j+5] + src[j+6] + src[j+stride+5] + src[j+stride+6] +1)>>2); + dst[j + 6] = (byte)((src[j+6] + src[j+7] + src[j+stride+6] + src[j+stride+7] +1)>>2); + dst[j + 7] = (byte)((src[j+7] + src[j+8] + src[j+stride+7] + src[j+stride+8] +1)>>2); + } + }else{ + for(dword j = 0; j < 8*stride; j+=stride){ + dst[j + 0] = (byte)((src[j+0] + src[j+1] + src[j+stride+0] + src[j+stride+1] +2)>>2); + dst[j + 1] = (byte)((src[j+1] + src[j+2] + src[j+stride+1] + src[j+stride+2] +2)>>2); + dst[j + 2] = (byte)((src[j+2] + src[j+3] + src[j+stride+2] + src[j+stride+3] +2)>>2); + dst[j + 3] = (byte)((src[j+3] + src[j+4] + src[j+stride+3] + src[j+stride+4] +2)>>2); + dst[j + 4] = (byte)((src[j+4] + src[j+5] + src[j+stride+4] + src[j+stride+5] +2)>>2); + dst[j + 5] = (byte)((src[j+5] + src[j+6] + src[j+stride+5] + src[j+stride+6] +2)>>2); + dst[j + 6] = (byte)((src[j+6] + src[j+7] + src[j+stride+6] + src[j+stride+7] +2)>>2); + dst[j + 7] = (byte)((src[j+7] + src[j+8] + src[j+stride+7] + src[j+stride+8] +2)>>2); + } + } +} +#endif +#endif + +//---------------------------- + +void interpolate16x16_lowpass_h(byte *dst, byte *src, int stride, int rounding){ + + int round_add = 16 - rounding; + for(int i = 0; i < 17; i++){ + + dst[0] = CLIP(((7 * ((src[0]<<1) - src[2]) + 23 * src[1] + 3 * src[3] - src[4] + round_add) >> 5), 0, 255); + dst[1] = CLIP(((19 * src[1] + 20 * src[2] - src[5] + 3 * (src[4] - src[0] - (src[3]<<1)) + round_add) >> 5), 0, 255); + dst[2] = CLIP(((20 * (src[2] + src[3]) + (src[0]<<1) + 3 * (src[5] - ((src[1] + src[4])<<1)) - src[6] + round_add) >> 5), 0, 255); + + dst[3] = CLIP(((20 * (src[3] + src[4]) + 3 * ((src[6] + src[1]) - ((src[2] + src[5])<<1)) - (src[0] + src[7]) + round_add) >> 5), 0, 255); + dst[4] = CLIP(((20 * (src[4] + src[5]) - 3 * (((src[3] + src[6])<<1) - (src[2] + src[7])) - (src[1] + src[8]) + round_add) >> 5), 0, 255); + dst[5] = CLIP(((20 * (src[5] + src[6]) - 3 * (((src[4] + src[7])<<1) - (src[3] + src[8])) - (src[2] + src[9]) + round_add) >> 5), 0, 255); + dst[6] = CLIP(((20 * (src[6] + src[7]) - 3 * (((src[5] + src[8])<<1) - (src[4] + src[9])) - (src[3] + src[10]) + round_add) >> 5), 0, 255); + dst[7] = CLIP(((20 * (src[7] + src[8]) - 3 * (((src[6] + src[9])<<1) - (src[5] + src[10])) - (src[4] + src[11]) + round_add) >> 5), 0, 255); + dst[8] = CLIP(((20 * (src[8] + src[9]) - 3 * (((src[7] + src[10])<<1) - (src[6] + src[11])) - (src[5] + src[12]) + round_add) >> 5), 0, 255); + dst[9] = CLIP(((20 * (src[9] + src[10]) - 3 * (((src[8] + src[11])<<1) - (src[7] + src[12])) - (src[6] + src[13]) + round_add) >> 5), 0, 255); + dst[10] = CLIP(((20 * (src[10] + src[11]) - 3 * (((src[9] + src[12])<<1) - (src[8] + src[13])) - (src[7] + src[14]) + round_add) >> 5), 0, 255); + dst[11] = CLIP(((20 * (src[11] + src[12]) - 3 * (((src[10] + src[13])<<1) - (src[9] + src[14])) - (src[8] + src[15]) + round_add) >> 5), 0, 255); + dst[12] = CLIP(((20 * (src[12] + src[13]) - 3 * (((src[11] + src[14])<<1) - (src[10] + src[15])) - (src[9] + src[16]) + round_add) >> 5), 0, 255); + + dst[13] = CLIP(((20 * (src[13] + src[14]) + (src[16]<<1) + 3 * (src[11] - ((src[12] + src[15]) << 1)) - src[10] + round_add) >> 5), 0, 255); + dst[14] = CLIP(((19 * src[15] + 20 * src[14] + 3 * (src[12] - src[16] - (src[13] << 1)) - src[11] + round_add) >> 5), 0, 255); + dst[15] = CLIP(((23 * src[15] + 7 * ((src[16]<<1) - src[14]) + 3 * src[13] - src[12] + round_add) >> 5), 0, 255); + + dst += stride; + src += stride; + } +} + +//---------------------------- + +void interpolate8x8_lowpass_h(byte *dst, byte *src, int stride, int rounding){ + + int round_add = 16 - rounding; + for(int i = 0; i < 9; i++){ + + dst[0] = CLIP(((7 * ((src[0]<<1) - src[2]) + 23 * src[1] + 3 * src[3] - src[4] + round_add) >> 5), 0, 255); + dst[1] = CLIP(((19 * src[1] + 20 * src[2] - src[5] + 3 * (src[4] - src[0] - (src[3]<<1)) + round_add) >> 5), 0, 255); + dst[2] = CLIP(((20 * (src[2] + src[3]) + (src[0]<<1) + 3 * (src[5] - ((src[1] + src[4])<<1)) - src[6] + round_add) >> 5), 0, 255); + dst[3] = CLIP(((20 * (src[3] + src[4]) + 3 * ((src[6] + src[1]) - ((src[2] + src[5])<<1)) - (src[0] + src[7]) + round_add) >> 5), 0, 255); + dst[4] = CLIP(((20 * (src[4] + src[5]) - 3 * (((src[3] + src[6])<<1) - (src[2] + src[7])) - (src[1] + src[8]) + round_add) >> 5), 0, 255); + dst[5] = CLIP(((20 * (src[5] + src[6]) + (src[8]<<1) + 3 * (src[3] - ((src[4] + src[7]) << 1)) - src[2] + round_add) >> 5), 0, 255); + dst[6] = CLIP(((19 * src[7] + 20 * src[6] + 3 * (src[4] - src[8] - (src[5] << 1)) - src[3] + round_add) >> 5), 0, 255); + dst[7] = CLIP(((23 * src[7] + 7 * ((src[8]<<1) - src[6]) + 3 * src[5] - src[4] + round_add) >> 5), 0, 255); + + dst += stride; + src += stride; + } +} + +//---------------------------- + +void interpolate16x16_lowpass_v(byte *dst, byte *src, int stride, int rounding){ + + int round_add = 16 - rounding; + for(int i = 0; i < 17; i++){ + int src0 = src[0]; + int src1 = src[stride]; + int src2 = src[2 * stride]; + int src3 = src[3 * stride]; + int src4 = src[4 * stride]; + int src5 = src[5 * stride]; + int src6 = src[6 * stride]; + int src7 = src[7 * stride]; + int src8 = src[8 * stride]; + int src9 = src[9 * stride]; + int src10 = src[10 * stride]; + int src11 = src[11 * stride]; + int src12 = src[12 * stride]; + int src13 = src[13 * stride]; + int src14 = src[14 * stride]; + int src15 = src[15 * stride]; + int src16 = src[16 * stride]; + + + dst[0] = CLIP(((7 * ((src0<<1) - src2) + 23 * src1 + 3 * src3 - src4 + round_add) >> 5), 0, 255); + dst[stride] = CLIP(((19 * src1 + 20 * src2 - src5 + 3 * (src4 - src0 - (src3<<1)) + round_add) >> 5), 0, 255); + dst[2*stride] = CLIP(((20 * (src2 + src3) + (src0<<1) + 3 * (src5 - ((src1 + src4)<<1)) - src6 + round_add) >> 5), 0, 255); + + dst[3*stride] = CLIP(((20 * (src3 + src4) + 3 * ((src6 + src1) - ((src2 + src5)<<1)) - (src0 + src7) + round_add) >> 5), 0, 255); + dst[4*stride] = CLIP(((20 * (src4 + src5) - 3 * (((src3 + src6)<<1) - (src2 + src7)) - (src1 + src8) + round_add) >> 5), 0, 255); + dst[5*stride] = CLIP(((20 * (src5 + src6) - 3 * (((src4 + src7)<<1) - (src3 + src8)) - (src2 + src9) + round_add) >> 5), 0, 255); + dst[6*stride] = CLIP(((20 * (src6 + src7) - 3 * (((src5 + src8)<<1) - (src4 + src9)) - (src3 + src10) + round_add) >> 5), 0, 255); + dst[7*stride] = CLIP(((20 * (src7 + src8) - 3 * (((src6 + src9)<<1) - (src5 + src10)) - (src4 + src11) + round_add) >> 5), 0, 255); + dst[8*stride] = CLIP(((20 * (src8 + src9) - 3 * (((src7 + src10)<<1) - (src6 + src11)) - (src5 + src12) + round_add) >> 5), 0, 255); + dst[9*stride] = CLIP(((20 * (src9 + src10) - 3 * (((src8 + src11)<<1) - (src7 + src12)) - (src6 + src13) + round_add) >> 5), 0, 255); + dst[10*stride] = CLIP(((20 * (src10 + src11) - 3 * (((src9 + src12)<<1) - (src8 + src13)) - (src7 + src14) + round_add) >> 5), 0, 255); + dst[11*stride] = CLIP(((20 * (src11 + src12) - 3 * (((src10 + src13)<<1) - (src9 + src14)) - (src8 + src15) + round_add) >> 5), 0, 255); + dst[12*stride] = CLIP(((20 * (src12 + src13) - 3 * (((src11 + src14)<<1) - (src10 + src15)) - (src9 + src16) + round_add) >> 5), 0, 255); + + dst[13*stride] = CLIP(((20 * (src13 + src14) + (src16<<1) + 3 * (src11 - ((src12 + src15) << 1)) - src10 + round_add) >> 5), 0, 255); + dst[14*stride] = CLIP(((19 * src15 + 20 * src14 + 3 * (src12 - src16 - (src13 << 1)) - src11 + round_add) >> 5), 0, 255); + dst[15*stride] = CLIP(((23 * src15 + 7 * ((src16<<1) - src14) + 3 * src13 - src12 + round_add) >> 5), 0, 255); + + dst++; + src++; + } +} + +//---------------------------- + +void interpolate8x8_lowpass_v(byte *dst, byte *src, int stride, int rounding){ + + int round_add = 16 - rounding; + for(int i = 0; i < 9; i++){ + int src0 = src[0]; + int src1 = src[stride]; + int src2 = src[2 * stride]; + int src3 = src[3 * stride]; + int src4 = src[4 * stride]; + int src5 = src[5 * stride]; + int src6 = src[6 * stride]; + int src7 = src[7 * stride]; + int src8 = src[8 * stride]; + + dst[0] = CLIP(((7 * ((src0<<1) - src2) + 23 * src1 + 3 * src3 - src4 + round_add) >> 5), 0, 255); + dst[stride] = CLIP(((19 * src1 + 20 * src2 - src5 + 3 * (src4 - src0 - (src3 << 1)) + round_add) >> 5), 0, 255); + dst[2 * stride] = CLIP(((20 * (src2 + src3) + (src0<<1) + 3 * (src5 - ((src1 + src4) <<1 )) - src6 + round_add) >> 5), 0, 255); + dst[3 * stride] = CLIP(((20 * (src3 + src4) + 3 * ((src6 + src1) - ((src2 + src5)<<1)) - (src0 + src7) + round_add) >> 5), 0, 255); + dst[4 * stride] = CLIP(((20 * (src4 + src5) + 3 * ((src2 + src7) - ((src3 + src6)<<1)) - (src1 + src8) + round_add) >> 5), 0, 255); + dst[5 * stride] = CLIP(((20 * (src5 + src6) + (src8<<1) + 3 * (src3 - ((src4 + src7) << 1)) - src2 + round_add) >> 5), 0, 255); + dst[6 * stride] = CLIP(((19 * src7 + 20 * src6 - src3 + 3 * (src4 - src8 - (src5 << 1)) + round_add) >> 5), 0, 255); + dst[7 * stride] = CLIP(((7 * ((src8<<1) - src6) + 23 * src7 + 3 * src5 - src4 + round_add) >> 5), 0, 255); + + dst++; + src++; + } +} + +//---------------------------- + +void interpolate16x16_lowpass_hv(byte *dst1, byte *dst2, byte *src, int stride, int rounding){ + + byte round_add = 16 - rounding; + byte *h_ptr = dst2; + for(int i = 0; i < 17; i++){ + + h_ptr[0] = CLIP(((7 * ((src[0]<<1) - src[2]) + 23 * src[1] + 3 * src[3] - src[4] + round_add) >> 5), 0, 255); + h_ptr[1] = CLIP(((19 * src[1] + 20 * src[2] - src[5] + 3 * (src[4] - src[0] - (src[3]<<1)) + round_add) >> 5), 0, 255); + h_ptr[2] = CLIP(((20 * (src[2] + src[3]) + (src[0]<<1) + 3 * (src[5] - ((src[1] + src[4])<<1)) - src[6] + round_add) >> 5), 0, 255); + + h_ptr[3] = CLIP(((20 * (src[3] + src[4]) + 3 * ((src[6] + src[1]) - ((src[2] + src[5])<<1)) - (src[0] + src[7]) + round_add) >> 5), 0, 255); + h_ptr[4] = CLIP(((20 * (src[4] + src[5]) - 3 * (((src[3] + src[6])<<1) - (src[2] + src[7])) - (src[1] + src[8]) + round_add) >> 5), 0, 255); + h_ptr[5] = CLIP(((20 * (src[5] + src[6]) - 3 * (((src[4] + src[7])<<1) - (src[3] + src[8])) - (src[2] + src[9]) + round_add) >> 5), 0, 255); + h_ptr[6] = CLIP(((20 * (src[6] + src[7]) - 3 * (((src[5] + src[8])<<1) - (src[4] + src[9])) - (src[3] + src[10]) + round_add) >> 5), 0, 255); + h_ptr[7] = CLIP(((20 * (src[7] + src[8]) - 3 * (((src[6] + src[9])<<1) - (src[5] + src[10])) - (src[4] + src[11]) + round_add) >> 5), 0, 255); + h_ptr[8] = CLIP(((20 * (src[8] + src[9]) - 3 * (((src[7] + src[10])<<1) - (src[6] + src[11])) - (src[5] + src[12]) + round_add) >> 5), 0, 255); + h_ptr[9] = CLIP(((20 * (src[9] + src[10]) - 3 * (((src[8] + src[11])<<1) - (src[7] + src[12])) - (src[6] + src[13]) + round_add) >> 5), 0, 255); + h_ptr[10] = CLIP(((20 * (src[10] + src[11]) - 3 * (((src[9] + src[12])<<1) - (src[8] + src[13])) - (src[7] + src[14]) + round_add) >> 5), 0, 255); + h_ptr[11] = CLIP(((20 * (src[11] + src[12]) - 3 * (((src[10] + src[13])<<1) - (src[9] + src[14])) - (src[8] + src[15]) + round_add) >> 5), 0, 255); + h_ptr[12] = CLIP(((20 * (src[12] + src[13]) - 3 * (((src[11] + src[14])<<1) - (src[10] + src[15])) - (src[9] + src[16]) + round_add) >> 5), 0, 255); + + h_ptr[13] = CLIP(((20 * (src[13] + src[14]) + (src[16]<<1) + 3 * (src[11] - ((src[12] + src[15]) << 1)) - src[10] + round_add) >> 5), 0, 255); + h_ptr[14] = CLIP(((19 * src[15] + 20 * src[14] + 3 * (src[12] - src[16] - (src[13] << 1)) - src[11] + round_add) >> 5), 0, 255); + h_ptr[15] = CLIP(((23 * src[15] + 7 * ((src[16]<<1) - src[14]) + 3 * src[13] - src[12] + round_add) >> 5), 0, 255); + + h_ptr += stride; + src += stride; + } + interpolate16x16_lowpass_v(dst1, dst2, stride, rounding); + +} + +//---------------------------- + +void interpolate8x8_lowpass_hv(byte *dst1, byte *dst2, byte *src, int stride, int rounding){ + + byte round_add = 16 - rounding; + byte *h_ptr = dst2; + for(int i = 0; i < 9; i++){ + + h_ptr[0] = CLIP(((7 * ((src[0]<<1) - src[2]) + 23 * src[1] + 3 * src[3] - src[4] + round_add) >> 5), 0, 255); + h_ptr[1] = CLIP(((19 * src[1] + 20 * src[2] - src[5] + 3 * (src[4] - src[0] - (src[3]<<1)) + round_add) >> 5), 0, 255); + h_ptr[2] = CLIP(((20 * (src[2] + src[3]) + (src[0]<<1) + 3 * (src[5] - ((src[1] + src[4])<<1)) - src[6] + round_add) >> 5), 0, 255); + h_ptr[3] = CLIP(((20 * (src[3] + src[4]) + 3 * ((src[6] + src[1]) - ((src[2] + src[5])<<1)) - (src[0] + src[7]) + round_add) >> 5), 0, 255); + h_ptr[4] = CLIP(((20 * (src[4] + src[5]) - 3 * (((src[3] + src[6])<<1) - (src[2] + src[7])) - (src[1] + src[8]) + round_add) >> 5), 0, 255); + h_ptr[5] = CLIP(((20 * (src[5] + src[6]) + (src[8]<<1) + 3 * (src[3] - ((src[4] + src[7]) << 1)) - src[2] + round_add) >> 5), 0, 255); + h_ptr[6] = CLIP(((19 * src[7] + 20 * src[6] + 3 * (src[4] - src[8] - (src[5] << 1)) - src[3] + round_add) >> 5), 0, 255); + h_ptr[7] = CLIP(((23 * src[7] + 7 * ((src[8]<<1) - src[6]) + 3 * src[5] - src[4] + round_add) >> 5), 0, 255); + + h_ptr += stride; + src += stride; + } + interpolate8x8_lowpass_v(dst1, dst2, stride, rounding); +} + +//---------------------------- diff --git a/modules/xvid_dec/xvid_wce/interpolate8x8.h b/modules/xvid_dec/xvid_wce/interpolate8x8.h new file mode 100644 index 0000000..5e735ee --- /dev/null +++ b/modules/xvid_dec/xvid_wce/interpolate8x8.h @@ -0,0 +1,107 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Interpolation related header - + * + * Copyright(C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: interpolate8x8.h,v 1.1.1.1 2005/07/13 14:36:15 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _INTERPOLATE8X8_H_ +#define _INTERPOLATE8X8_H_ + +#include "mem_transfer.h" + +typedef void (INTERPOLATE8X8)(byte *dst, const byte *src, dword stride, bool rounding); +typedef INTERPOLATE8X8 *INTERPOLATE8X8_PTR; + +typedef void (INTERPOLATE8X8_AVG2)(byte *dst, const byte *src1, const byte *src2, dword stride, bool rounding, dword height); +typedef INTERPOLATE8X8_AVG2 *INTERPOLATE8X8_AVG2_PTR; + +typedef void (INTERPOLATE8X8_AVG4)(byte *dst, const byte *src1, const byte *src2, const byte *src3, const byte *src4, dword stride, bool rounding); +typedef INTERPOLATE8X8_AVG4 *INTERPOLATE8X8_AVG4_PTR; + +typedef void (INTERPOLATE_LOWPASS) (byte *dst, + byte *src, + int stride, + int rounding); + +typedef INTERPOLATE_LOWPASS *INTERPOLATE_LOWPASS_PTR; + +typedef void (INTERPOLATE_LOWPASS_HV) (byte *dst1, + byte *dst2, + byte *src, + int stride, + int rounding); + +typedef INTERPOLATE_LOWPASS_HV *INTERPOLATE_LOWPASS_HV_PTR; + +typedef void (INTERPOLATE8X8_6TAP_LOWPASS)(byte *dst, byte *src, int stride, bool rounding); + +typedef INTERPOLATE8X8_6TAP_LOWPASS *INTERPOLATE8X8_6TAP_LOWPASS_PTR; + +#ifdef _ARM_ //PPC +extern"C"{ +#endif + +INTERPOLATE8X8 interpolate8x8_halfpel_h; +INTERPOLATE8X8 interpolate8x8_halfpel_hv; +INTERPOLATE8X8 interpolate8x8_halfpel_v; + +#ifdef _ARM_ +} +#endif + + +INTERPOLATE8X8_AVG2 interpolate8x8_avg2; + +INTERPOLATE_LOWPASS interpolate8x8_lowpass_h; +INTERPOLATE_LOWPASS interpolate8x8_lowpass_v; + +INTERPOLATE_LOWPASS interpolate16x16_lowpass_h; +INTERPOLATE_LOWPASS interpolate16x16_lowpass_v; + +INTERPOLATE_LOWPASS_HV interpolate8x8_lowpass_hv; +INTERPOLATE_LOWPASS_HV interpolate16x16_lowpass_hv; + +//---------------------------- +// Notes: +// x, y is always multiply of 4, so writing to 'cur' is always dword aligned +// stride is always multiply of 4 +void interpolate8x8_switch(byte *cur, const byte *refn, dword x, dword y, int dx, int dy, dword stride, bool rounding); + +inline void interpolate16x16_switch(byte *cur, const byte *refn, dword x, dword y, int dx, int dy, dword stride, bool rounding){ + + interpolate8x8_switch(cur, refn, x, y, dx, dy, stride, rounding); + interpolate8x8_switch(cur, refn, x+8, y, dx, dy, stride, rounding); + interpolate8x8_switch(cur, refn, x, y+8, dx, dy, stride, rounding); + interpolate8x8_switch(cur, refn, x+8, y+8, dx, dy, stride, rounding); +} + +inline void interpolate32x32_switch(byte *cur, const byte *refn, dword x, dword y, int dx, int dy, dword stride, bool rounding){ + interpolate16x16_switch(cur, refn, x, y, dx, dy, stride, rounding); + interpolate16x16_switch(cur, refn, x+16, y, dx, dy, stride, rounding); + interpolate16x16_switch(cur, refn, x, y+16, dx, dy, stride, rounding); + interpolate16x16_switch(cur, refn, x+16, y+16, dx, dy, stride, rounding); +} + +void interpolate8x8_quarterpel(byte *cur, byte *refn, byte *refh, byte *refv, byte *refhv, dword x, dword y, int dx, int dy, dword stride, bool rounding); +void interpolate16x16_quarterpel(byte *cur, byte *refn, byte *refh, byte *refv, byte *refhv, dword x, dword y, int dx, int dy, dword stride, bool rounding); + +#endif diff --git a/modules/xvid_dec/xvid_wce/mbcoding.cpp b/modules/xvid_dec/xvid_wce/mbcoding.cpp new file mode 100644 index 0000000..7d3b9f5 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/mbcoding.cpp @@ -0,0 +1,936 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - MB coding - + * + * Copyright (C) 2002 Michael Militzer <isibaar@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: mbcoding.cpp,v 1.1.1.1 2005/07/13 14:36:15 jeanlf Exp $ + * + ****************************************************************************/ + +#include "portab.h" +#include "global.h" +#include "bitstream.h" +#include "vlc_codes.h" + +//---------------------------- +/***************************************************************************** + * VLC tables and other constant arrays + ****************************************************************************/ + +struct VLC_TABLE{ + VLC vlc; + EVENT event; +}; + +static const VLC_TABLE coeff_tab[2][102] = { + /* intra = 0 */ + { + {{ 2, 2}, {0, 0, 1}}, + {{15, 4}, {0, 0, 2}}, + {{21, 6}, {0, 0, 3}}, + {{23, 7}, {0, 0, 4}}, + {{31, 8}, {0, 0, 5}}, + {{37, 9}, {0, 0, 6}}, + {{36, 9}, {0, 0, 7}}, + {{33, 10}, {0, 0, 8}}, + {{32, 10}, {0, 0, 9}}, + {{ 7, 11}, {0, 0, 10}}, + {{ 6, 11}, {0, 0, 11}}, + {{32, 11}, {0, 0, 12}}, + {{ 6, 3}, {0, 1, 1}}, + {{20, 6}, {0, 1, 2}}, + {{30, 8}, {0, 1, 3}}, + {{15, 10}, {0, 1, 4}}, + {{33, 11}, {0, 1, 5}}, + {{80, 12}, {0, 1, 6}}, + {{14, 4}, {0, 2, 1}}, + {{29, 8}, {0, 2, 2}}, + {{14, 10}, {0, 2, 3}}, + {{81, 12}, {0, 2, 4}}, + {{13, 5}, {0, 3, 1}}, + {{35, 9}, {0, 3, 2}}, + {{13, 10}, {0, 3, 3}}, + {{12, 5}, {0, 4, 1}}, + {{34, 9}, {0, 4, 2}}, + {{82, 12}, {0, 4, 3}}, + {{11, 5}, {0, 5, 1}}, + {{12, 10}, {0, 5, 2}}, + {{83, 12}, {0, 5, 3}}, + {{19, 6}, {0, 6, 1}}, + {{11, 10}, {0, 6, 2}}, + {{84, 12}, {0, 6, 3}}, + {{18, 6}, {0, 7, 1}}, + {{10, 10}, {0, 7, 2}}, + {{17, 6}, {0, 8, 1}}, + {{ 9, 10}, {0, 8, 2}}, + {{16, 6}, {0, 9, 1}}, + {{ 8, 10}, {0, 9, 2}}, + {{22, 7}, {0, 10, 1}}, + {{85, 12}, {0, 10, 2}}, + {{21, 7}, {0, 11, 1}}, + {{20, 7}, {0, 12, 1}}, + {{28, 8}, {0, 13, 1}}, + {{27, 8}, {0, 14, 1}}, + {{33, 9}, {0, 15, 1}}, + {{32, 9}, {0, 16, 1}}, + {{31, 9}, {0, 17, 1}}, + {{30, 9}, {0, 18, 1}}, + {{29, 9}, {0, 19, 1}}, + {{28, 9}, {0, 20, 1}}, + {{27, 9}, {0, 21, 1}}, + {{26, 9}, {0, 22, 1}}, + {{34, 11}, {0, 23, 1}}, + {{35, 11}, {0, 24, 1}}, + {{86, 12}, {0, 25, 1}}, + {{87, 12}, {0, 26, 1}}, + {{ 7, 4}, {1, 0, 1}}, + {{25, 9}, {1, 0, 2}}, + {{ 5, 11}, {1, 0, 3}}, + {{15, 6}, {1, 1, 1}}, + {{ 4, 11}, {1, 1, 2}}, + {{14, 6}, {1, 2, 1}}, + {{13, 6}, {1, 3, 1}}, + {{12, 6}, {1, 4, 1}}, + {{19, 7}, {1, 5, 1}}, + {{18, 7}, {1, 6, 1}}, + {{17, 7}, {1, 7, 1}}, + {{16, 7}, {1, 8, 1}}, + {{26, 8}, {1, 9, 1}}, + {{25, 8}, {1, 10, 1}}, + {{24, 8}, {1, 11, 1}}, + {{23, 8}, {1, 12, 1}}, + {{22, 8}, {1, 13, 1}}, + {{21, 8}, {1, 14, 1}}, + {{20, 8}, {1, 15, 1}}, + {{19, 8}, {1, 16, 1}}, + {{24, 9}, {1, 17, 1}}, + {{23, 9}, {1, 18, 1}}, + {{22, 9}, {1, 19, 1}}, + {{21, 9}, {1, 20, 1}}, + {{20, 9}, {1, 21, 1}}, + {{19, 9}, {1, 22, 1}}, + {{18, 9}, {1, 23, 1}}, + {{17, 9}, {1, 24, 1}}, + {{ 7, 10}, {1, 25, 1}}, + {{ 6, 10}, {1, 26, 1}}, + {{ 5, 10}, {1, 27, 1}}, + {{ 4, 10}, {1, 28, 1}}, + {{36, 11}, {1, 29, 1}}, + {{37, 11}, {1, 30, 1}}, + {{38, 11}, {1, 31, 1}}, + {{39, 11}, {1, 32, 1}}, + {{88, 12}, {1, 33, 1}}, + {{89, 12}, {1, 34, 1}}, + {{90, 12}, {1, 35, 1}}, + {{91, 12}, {1, 36, 1}}, + {{92, 12}, {1, 37, 1}}, + {{93, 12}, {1, 38, 1}}, + {{94, 12}, {1, 39, 1}}, + {{95, 12}, {1, 40, 1}} + }, + /* intra = 1 */ + { + {{ 2, 2}, {0, 0, 1}}, + {{15, 4}, {0, 0, 3}}, + {{21, 6}, {0, 0, 6}}, + {{23, 7}, {0, 0, 9}}, + {{31, 8}, {0, 0, 10}}, + {{37, 9}, {0, 0, 13}}, + {{36, 9}, {0, 0, 14}}, + {{33, 10}, {0, 0, 17}}, + {{32, 10}, {0, 0, 18}}, + {{ 7, 11}, {0, 0, 21}}, + {{ 6, 11}, {0, 0, 22}}, + {{32, 11}, {0, 0, 23}}, + {{ 6, 3}, {0, 0, 2}}, + {{20, 6}, {0, 1, 2}}, + {{30, 8}, {0, 0, 11}}, + {{15, 10}, {0, 0, 19}}, + {{33, 11}, {0, 0, 24}}, + {{80, 12}, {0, 0, 25}}, + {{14, 4}, {0, 1, 1}}, + {{29, 8}, {0, 0, 12}}, + {{14, 10}, {0, 0, 20}}, + {{81, 12}, {0, 0, 26}}, + {{13, 5}, {0, 0, 4}}, + {{35, 9}, {0, 0, 15}}, + {{13, 10}, {0, 1, 7}}, + {{12, 5}, {0, 0, 5}}, + {{34, 9}, {0, 4, 2}}, + {{82, 12}, {0, 0, 27}}, + {{11, 5}, {0, 2, 1}}, + {{12, 10}, {0, 2, 4}}, + {{83, 12}, {0, 1, 9}}, + {{19, 6}, {0, 0, 7}}, + {{11, 10}, {0, 3, 4}}, + {{84, 12}, {0, 6, 3}}, + {{18, 6}, {0, 0, 8}}, + {{10, 10}, {0, 4, 3}}, + {{17, 6}, {0, 3, 1}}, + {{ 9, 10}, {0, 8, 2}}, + {{16, 6}, {0, 4, 1}}, + {{ 8, 10}, {0, 5, 3}}, + {{22, 7}, {0, 1, 3}}, + {{85, 12}, {0, 1, 10}}, + {{21, 7}, {0, 2, 2}}, + {{20, 7}, {0, 7, 1}}, + {{28, 8}, {0, 1, 4}}, + {{27, 8}, {0, 3, 2}}, + {{33, 9}, {0, 0, 16}}, + {{32, 9}, {0, 1, 5}}, + {{31, 9}, {0, 1, 6}}, + {{30, 9}, {0, 2, 3}}, + {{29, 9}, {0, 3, 3}}, + {{28, 9}, {0, 5, 2}}, + {{27, 9}, {0, 6, 2}}, + {{26, 9}, {0, 7, 2}}, + {{34, 11}, {0, 1, 8}}, + {{35, 11}, {0, 9, 2}}, + {{86, 12}, {0, 2, 5}}, + {{87, 12}, {0, 7, 3}}, + {{ 7, 4}, {1, 0, 1}}, + {{25, 9}, {0, 11, 1}}, + {{ 5, 11}, {1, 0, 6}}, + {{15, 6}, {1, 1, 1}}, + {{ 4, 11}, {1, 0, 7}}, + {{14, 6}, {1, 2, 1}}, + {{13, 6}, {0, 5, 1}}, + {{12, 6}, {1, 0, 2}}, + {{19, 7}, {1, 5, 1}}, + {{18, 7}, {0, 6, 1}}, + {{17, 7}, {1, 3, 1}}, + {{16, 7}, {1, 4, 1}}, + {{26, 8}, {1, 9, 1}}, + {{25, 8}, {0, 8, 1}}, + {{24, 8}, {0, 9, 1}}, + {{23, 8}, {0, 10, 1}}, + {{22, 8}, {1, 0, 3}}, + {{21, 8}, {1, 6, 1}}, + {{20, 8}, {1, 7, 1}}, + {{19, 8}, {1, 8, 1}}, + {{24, 9}, {0, 12, 1}}, + {{23, 9}, {1, 0, 4}}, + {{22, 9}, {1, 1, 2}}, + {{21, 9}, {1, 10, 1}}, + {{20, 9}, {1, 11, 1}}, + {{19, 9}, {1, 12, 1}}, + {{18, 9}, {1, 13, 1}}, + {{17, 9}, {1, 14, 1}}, + {{ 7, 10}, {0, 13, 1}}, + {{ 6, 10}, {1, 0, 5}}, + {{ 5, 10}, {1, 1, 3}}, + {{ 4, 10}, {1, 2, 2}}, + {{36, 11}, {1, 3, 2}}, + {{37, 11}, {1, 4, 2}}, + {{38, 11}, {1, 15, 1}}, + {{39, 11}, {1, 16, 1}}, + {{88, 12}, {0, 14, 1}}, + {{89, 12}, {1, 0, 8}}, + {{90, 12}, {1, 5, 2}}, + {{91, 12}, {1, 6, 2}}, + {{92, 12}, {1, 17, 1}}, + {{93, 12}, {1, 18, 1}}, + {{94, 12}, {1, 19, 1}}, + {{95, 12}, {1, 20, 1}} + } +}; + +//---------------------------- +/* constants taken from momusys/vm_common/inlcude/max_level.h */ +static const byte max_level[2][2][64] = { + { + /* intra = 0, last = 0 */ + { + 12, 6, 4, 3, 3, 3, 3, 2, + 2, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + /* intra = 0, last = 1 */ + { + 3, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + } + }, + { + /* intra = 1, last = 0 */ + { + 27, 10, 5, 4, 3, 3, 3, 3, + 2, 2, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + /* intra = 1, last = 1 */ + { + 8, 3, 2, 2, 2, 2, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + } + } +}; + +//---------------------------- + +static const byte max_run[2][2][64] = { + { + /* intra = 0, last = 0 */ + { + 0, 26, 10, 6, 2, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* intra = 0, last = 1 */ + { + 0, 40, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + } + }, + { + /* intra = 1, last = 0 */ + { + 0, 14, 9, 7, 3, 2, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* intra = 1, last = 1 */ + { + 0, 20, 6, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + } + } +}; + +/****************************************************************** + * encoder tables * + ******************************************************************/ + +static const VLC sprite_trajectory_len[15] = { + { 0x00 , 2}, + { 0x02 , 3}, { 0x03, 3}, { 0x04, 3}, { 0x05, 3}, { 0x06, 3}, + { 0x0E , 4}, { 0x1E, 5}, { 0x3E, 6}, { 0x7E, 7}, { 0xFE, 8}, + { 0x1FE, 9}, {0x3FE,10}, {0x7FE,11}, {0xFFE,12} }; + + +/****************************************************************** + * decoder tables * + ******************************************************************/ + +static const VLC mcbpc_intra_table[64] = { + {-1, 0}, {20, 6}, {36, 6}, {52, 6}, {4, 4}, {4, 4}, {4, 4}, {4, 4}, + {19, 3}, {19, 3}, {19, 3}, {19, 3}, {19, 3}, {19, 3}, {19, 3}, {19, 3}, + {35, 3}, {35, 3}, {35, 3}, {35, 3}, {35, 3}, {35, 3}, {35, 3}, {35, 3}, + {51, 3}, {51, 3}, {51, 3}, {51, 3}, {51, 3}, {51, 3}, {51, 3}, {51, 3}, + {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, + {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, + {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, + {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1}, {3, 1} +}; + +//---------------------------- + +static const VLC mcbpc_inter_table[257] = { + {VLC_ERROR, 0}, {255, 9}, {52, 9}, {36, 9}, {20, 9}, {49, 9}, {35, 8}, {35, 8}, + {19, 8}, {19, 8}, {50, 8}, {50, 8}, {51, 7}, {51, 7}, {51, 7}, {51, 7}, + {34, 7}, {34, 7}, {34, 7}, {34, 7}, {18, 7}, {18, 7}, {18, 7}, {18, 7}, + {33, 7}, {33, 7}, {33, 7}, {33, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, {4, 6}, + {48, 6}, {48, 6}, {48, 6}, {48, 6}, {48, 6}, {48, 6}, {48, 6}, {48, 6}, + {3, 5}, {3, 5}, {3, 5}, {3, 5}, {3, 5}, {3, 5}, {3, 5}, {3, 5}, + {3, 5}, {3, 5}, {3, 5}, {3, 5}, {3, 5}, {3, 5}, {3, 5}, {3, 5}, + {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, + {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, + {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, + {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, {32, 4}, + {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, + {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, + {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, + {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, {16, 4}, + {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, + {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, + {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, + {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, + {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, + {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, + {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, + {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, {2, 3}, + {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, + {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, + {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, + {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, + {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, + {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, + {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, + {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, + {0, 1} +}; + +//---------------------------- + +static const VLC cbpy_table[64] = { + {-1, 0}, {-1, 0}, {6, 6}, {9, 6}, {8, 5}, {8, 5}, {4, 5}, {4, 5}, + {2, 5}, {2, 5}, {1, 5}, {1, 5}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, + {12, 4}, {12, 4}, {12, 4}, {12, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, + {14, 4}, {14, 4}, {14, 4}, {14, 4}, {5, 4}, {5, 4}, {5, 4}, {5, 4}, + {13, 4}, {13, 4}, {13, 4}, {13, 4}, {3, 4}, {3, 4}, {3, 4}, {3, 4}, + {11, 4}, {11, 4}, {11, 4}, {11, 4}, {7, 4}, {7, 4}, {7, 4}, {7, 4}, + {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, + {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2} +}; + +//---------------------------- + +static const VLC TMNMVtab0[] = { + {3, 4}, {-3, 4}, {2, 3}, {2, 3}, {-2, 3}, {-2, 3}, {1, 2}, + {1, 2}, {1, 2}, {1, 2}, {-1, 2}, {-1, 2}, {-1, 2}, {-1, 2} +}; + +//---------------------------- + +static const VLC TMNMVtab1[] = { + {12, 10}, {-12, 10}, {11, 10}, {-11, 10}, + {10, 9}, {10, 9}, {-10, 9}, {-10, 9}, + {9, 9}, {9, 9}, {-9, 9}, {-9, 9}, + {8, 9}, {8, 9}, {-8, 9}, {-8, 9}, + {7, 7}, {7, 7}, {7, 7}, {7, 7}, + {7, 7}, {7, 7}, {7, 7}, {7, 7}, + {-7, 7}, {-7, 7}, {-7, 7}, {-7, 7}, + {-7, 7}, {-7, 7}, {-7, 7}, {-7, 7}, + {6, 7}, {6, 7}, {6, 7}, {6, 7}, + {6, 7}, {6, 7}, {6, 7}, {6, 7}, + {-6, 7}, {-6, 7}, {-6, 7}, {-6, 7}, + {-6, 7}, {-6, 7}, {-6, 7}, {-6, 7}, + {5, 7}, {5, 7}, {5, 7}, {5, 7}, + {5, 7}, {5, 7}, {5, 7}, {5, 7}, + {-5, 7}, {-5, 7}, {-5, 7}, {-5, 7}, + {-5, 7}, {-5, 7}, {-5, 7}, {-5, 7}, + {4, 6}, {4, 6}, {4, 6}, {4, 6}, + {4, 6}, {4, 6}, {4, 6}, {4, 6}, + {4, 6}, {4, 6}, {4, 6}, {4, 6}, + {4, 6}, {4, 6}, {4, 6}, {4, 6}, + {-4, 6}, {-4, 6}, {-4, 6}, {-4, 6}, + {-4, 6}, {-4, 6}, {-4, 6}, {-4, 6}, + {-4, 6}, {-4, 6}, {-4, 6}, {-4, 6}, + {-4, 6}, {-4, 6}, {-4, 6}, {-4, 6} +}; + +//---------------------------- + +static const VLC TMNMVtab2[] = { + {32, 12}, {-32, 12}, {31, 12}, {-31, 12}, + {30, 11}, {30, 11}, {-30, 11}, {-30, 11}, + {29, 11}, {29, 11}, {-29, 11}, {-29, 11}, + {28, 11}, {28, 11}, {-28, 11}, {-28, 11}, + {27, 11}, {27, 11}, {-27, 11}, {-27, 11}, + {26, 11}, {26, 11}, {-26, 11}, {-26, 11}, + {25, 11}, {25, 11}, {-25, 11}, {-25, 11}, + {24, 10}, {24, 10}, {24, 10}, {24, 10}, + {-24, 10}, {-24, 10}, {-24, 10}, {-24, 10}, + {23, 10}, {23, 10}, {23, 10}, {23, 10}, + {-23, 10}, {-23, 10}, {-23, 10}, {-23, 10}, + {22, 10}, {22, 10}, {22, 10}, {22, 10}, + {-22, 10}, {-22, 10}, {-22, 10}, {-22, 10}, + {21, 10}, {21, 10}, {21, 10}, {21, 10}, + {-21, 10}, {-21, 10}, {-21, 10}, {-21, 10}, + {20, 10}, {20, 10}, {20, 10}, {20, 10}, + {-20, 10}, {-20, 10}, {-20, 10}, {-20, 10}, + {19, 10}, {19, 10}, {19, 10}, {19, 10}, + {-19, 10}, {-19, 10}, {-19, 10}, {-19, 10}, + {18, 10}, {18, 10}, {18, 10}, {18, 10}, + {-18, 10}, {-18, 10}, {-18, 10}, {-18, 10}, + {17, 10}, {17, 10}, {17, 10}, {17, 10}, + {-17, 10}, {-17, 10}, {-17, 10}, {-17, 10}, + {16, 10}, {16, 10}, {16, 10}, {16, 10}, + {-16, 10}, {-16, 10}, {-16, 10}, {-16, 10}, + {15, 10}, {15, 10}, {15, 10}, {15, 10}, + {-15, 10}, {-15, 10}, {-15, 10}, {-15, 10}, + {14, 10}, {14, 10}, {14, 10}, {14, 10}, + {-14, 10}, {-14, 10}, {-14, 10}, {-14, 10}, + {13, 10}, {13, 10}, {13, 10}, {13, 10}, + {-13, 10}, {-13, 10}, {-13, 10}, {-13, 10} +}; + +//---------------------------- + +static const VLC dc_lum_tab[] = { + {0, 0}, {4, 3}, {3, 3}, {0, 3}, + {2, 2}, {2, 2}, {1, 2}, {1, 2}, +}; + +//--------------------------- +#define LEVELOFFSET 32 + +int Bitstream::bs_get_spritetrajectory(){ + + for(int i = 0; i < 12; i++){ + if((int)ShowBits(sprite_trajectory_len[i].len) == sprite_trajectory_len[i].code){ + Skip(sprite_trajectory_len[i].len); + return i; + } + } + return -1; +} + +//---------------------------- + +void S_decoder::init_vlc_tables(){ + + dword i, j, intra, last, run, run_esc, level, level_esc, escape, escape_len, offset; + + for (intra = 0; intra < 2; intra++) + for (i = 0; i < 4096; i++) + DCT3D[intra][i].event.level = 0; + + for (intra = 0; intra < 2; intra++) { + for (last = 0; last < 2; last++) { + for (run = 0; run < 63 + last; run++) { + for (level = 0; level < (dword)(32 << intra); level++) { + offset = !intra * LEVELOFFSET; + coeff_VLC[intra][last][level + offset][run].len = 128; + } + } + } + } + + for (intra = 0; intra < 2; intra++) { + for (i = 0; i < 102; i++) { + offset = !intra * LEVELOFFSET; + + for (j = 0; j < (dword)(1 << (12 - coeff_tab[intra][i].vlc.len)); j++) { + DCT3D[intra][(coeff_tab[intra][i].vlc.code << (12 - coeff_tab[intra][i].vlc.len)) | j].len = coeff_tab[intra][i].vlc.len; + DCT3D[intra][(coeff_tab[intra][i].vlc.code << (12 - coeff_tab[intra][i].vlc.len)) | j].event = coeff_tab[intra][i].event; + } + + coeff_VLC[intra][coeff_tab[intra][i].event.last][coeff_tab[intra][i].event.level + offset][coeff_tab[intra][i].event.run].code + = coeff_tab[intra][i].vlc.code << 1; + coeff_VLC[intra][coeff_tab[intra][i].event.last][coeff_tab[intra][i].event.level + offset][coeff_tab[intra][i].event.run].len + = coeff_tab[intra][i].vlc.len + 1; + + if (!intra) { + coeff_VLC[intra][coeff_tab[intra][i].event.last][offset - coeff_tab[intra][i].event.level][coeff_tab[intra][i].event.run].code + = (coeff_tab[intra][i].vlc.code << 1) | 1; + coeff_VLC[intra][coeff_tab[intra][i].event.last][offset - coeff_tab[intra][i].event.level][coeff_tab[intra][i].event.run].len + = coeff_tab[intra][i].vlc.len + 1; + } + } + } + + for (intra = 0; intra < 2; intra++) { + for (last = 0; last < 2; last++) { + for (run = 0; run < 63 + last; run++) { + for (level = 1; level < (dword)(32 << intra); level++) { + + if (level <= max_level[intra][last][run] && run <= max_run[intra][last][level]) + continue; + + offset = !intra * LEVELOFFSET; + level_esc = level - max_level[intra][last][run]; + run_esc = run - 1 - max_run[intra][last][level]; + + if (level_esc <= max_level[intra][last][run] && run <= max_run[intra][last][level_esc]) { + escape = ESCAPE1; + escape_len = 7 + 1; + run_esc = run; + } else { + if (run_esc <= max_run[intra][last][level] && level <= max_level[intra][last][run_esc]) { + escape = ESCAPE2; + escape_len = 7 + 2; + level_esc = level; + } else { + if (!intra) { + coeff_VLC[intra][last][level + offset][run].code + = (ESCAPE3 << 21) | (last << 20) | (run << 14) | (1 << 13) | ((level & 0xfff) << 1) | 1; + coeff_VLC[intra][last][level + offset][run].len = 30; + coeff_VLC[intra][last][offset - level][run].code + = (ESCAPE3 << 21) | (last << 20) | (run << 14) | (1 << 13) | ((-(int)level & 0xfff) << 1) | 1; + coeff_VLC[intra][last][offset - level][run].len = 30; + } + continue; + } + } + + coeff_VLC[intra][last][level + offset][run].code + = (escape << coeff_VLC[intra][last][level_esc + offset][run_esc].len) + | coeff_VLC[intra][last][level_esc + offset][run_esc].code; + coeff_VLC[intra][last][level + offset][run].len + = coeff_VLC[intra][last][level_esc + offset][run_esc].len + escape_len; + + if (!intra) { + coeff_VLC[intra][last][offset - level][run].code + = (escape << coeff_VLC[intra][last][level_esc + offset][run_esc].len) + | coeff_VLC[intra][last][level_esc + offset][run_esc].code | 1; + coeff_VLC[intra][last][offset - level][run].len + = coeff_VLC[intra][last][level_esc + offset][run_esc].len + escape_len; + } + } + + if (!intra) { + coeff_VLC[intra][last][0][run].code + = (ESCAPE3 << 21) | (last << 20) | (run << 14) | (1 << 13) | ((-32 & 0xfff) << 1) | 1; + coeff_VLC[intra][last][0][run].len = 30; + } + } + } + } + + /* init sprite_trajectory tables + * even if GMC is not specified (it might be used later...) */ + /* + { + dword k; + int l; + sprite_trajectory_code[0+16384].code = 0; + sprite_trajectory_code[0+16384].len = 0; + for (k=0;k<14;k++) { + int limit = (1<<k); + + for (l=-(2*limit-1); l <= -limit; l++) { + sprite_trajectory_code[l+16384].code = (2*limit-1)+l; + sprite_trajectory_code[l+16384].len = k+1; + } + + for (l=limit; l<= 2*limit-1; l++) { + sprite_trajectory_code[l+16384].code = l; + sprite_trajectory_code[l+16384].len = k+1; + } + } + } + */ +} + + +/*************************************************************** + * decoding stuff starts here * + ***************************************************************/ + + +/* + * for IVOP addbits == 0 + * for PVOP addbits == fcode - 1 + * for BVOP addbits == max(fcode,bcode) - 1 + * returns true or false + */ +int Bitstream::check_resync_marker(int addbits){ + + dword nbitsresyncmarker = NUMBITS_VP_RESYNC_MARKER + addbits; + dword nbits = NumBitsToByteAlign(); + dword code = ShowBits(nbits); + + if(code == (((dword)1 << (nbits - 1)) - 1)){ + return ShowBitsFromByteAlign(nbitsresyncmarker) == RESYNC_MARKER; + } + return 0; +} + +//---------------------------- + +int Bitstream::get_mcbpc_intra(){ + + dword index; + + index = ShowBits(9); + index >>= 3; + + Skip(mcbpc_intra_table[index].len); + + return mcbpc_intra_table[index].code; + +} + +//---------------------------- + +int Bitstream::GetMcbpcInter(){ + + dword index = MIN(ShowBits(9), 256); + Skip(mcbpc_inter_table[index].len); + + return mcbpc_inter_table[index].code; +} + +//---------------------------- + +int Bitstream::GetCbpy(int intra){ + + dword index = ShowBits(6); + Skip(cbpy_table[index].len); + int cbpy = cbpy_table[index].code; + if(!intra) + cbpy = 15 - cbpy; + return cbpy; +} + +//---------------------------- + +static int get_mv_data(Bitstream * bs){ + + if(bs->GetBit()) + return 0; + + dword index = bs->ShowBits(12); + + if(index >= 512){ + index = (index >> 8) - 2; + bs->Skip(TMNMVtab0[index].len); + return TMNMVtab0[index].code; + } + + if (index >= 128) { + index = (index >> 2) - 32; + bs->Skip(TMNMVtab1[index].len); + return TMNMVtab1[index].code; + } + + index -= 4; + + bs->Skip(TMNMVtab2[index].len); + return TMNMVtab2[index].code; +} + +//---------------------------- + +int Bitstream::GetMoveVector(int fcode){ + + int scale_fac = 1 << (fcode - 1); + int data = get_mv_data(this); + + if(scale_fac == 1 || data == 0) + return data; + + int res = GetBits(fcode - 1); + int mv = ((ABS(data) - 1) * scale_fac) + res + 1; + + return data < 0 ? -mv : mv; +} + +//---------------------------- + +int Bitstream::get_dc_dif(dword dc_size){ + + int code = GetBits(dc_size); + int msb = code >> (dc_size - 1); + + if (msb == 0) + return (-1 * (code ^ ((1 << dc_size) - 1))); + + return code; + +} + +//---------------------------- + +int Bitstream::get_dc_size_lum(){ + + int code = ShowBits(11); + + for(int i = 11; i > 3; i--) { + if (code == 1) { + Skip(i); + return i + 1; + } + code >>= 1; + } + + Skip(dc_lum_tab[code].len); + return dc_lum_tab[code].code; + +} + +//---------------------------- + +int Bitstream::get_dc_size_chrom(){ + + dword code, i; + + code = ShowBits(12); + + for (i = 12; i > 2; i--) { + if (code == 1) { + Skip(i); + return i; + } + code >>= 1; + } + + return 3 - GetBits(2); + +} + +//---------------------------- + +int S_decoder::get_coeff(Bitstream *bs, int *run, int *last, int intra, int short_video_header){ + + dword mode; + int level; + REVERSE_EVENT *reverse_event; + + if(short_video_header) /* inter-VLCs will be used for both intra and inter blocks */ + intra = 0; + + if(bs->ShowBits(7) != ESCAPE){ + reverse_event = &DCT3D[intra][bs->ShowBits(12)]; + + if((level = reverse_event->event.level) == 0) + goto error; + + *last = reverse_event->event.last; + *run = reverse_event->event.run; + + bs->Skip(reverse_event->len); + + return bs->GetBits(1) ? -level : level; + } + + bs->Skip(7); + + if(short_video_header){ + //escape mode 4 - H.263 type, only used if short_video_header = 1 + *last = bs->GetBit(); + *run = bs->GetBits(6); + level = bs->GetBits(8); + if (level == 0 || level == 128) + DPRINTF(XVID_DEBUG_ERROR, "Illegal LEVEL for ESCAPE mode 4: %d\n", level); + return (level << 24) >> 24; + } + + mode = bs->ShowBits(2); + if(mode < 3){ + bs->Skip((mode == 2) ? 2 : 1); + + reverse_event = &DCT3D[intra][bs->ShowBits(12)]; + + if((level = reverse_event->event.level) == 0) + goto error; + *last = reverse_event->event.last; + *run = reverse_event->event.run; + bs->Skip(reverse_event->len); + + if(mode < 2) /* first escape mode, level is offset */ + level += max_level[intra][*last][*run]; + else /* second escape mode, run is offset */ + *run += max_run[intra][*last][level] + 1; + return bs->GetBits(1) ? -level : level; + } + //third escape mode - fixed length codes + bs->Skip(2); + *last = bs->GetBits(1); + *run = bs->GetBits(6); + bs->Skip(1); //marker + level = bs->GetBits(12); + bs->Skip(1); //marker + + return (level << 20) >> 20; + +error: + *run = VLC_ERROR; + return 0; +} + +//---------------------------- + +void S_decoder::get_intra_block(Bitstream *bs, int *block, int direction, int coeff){ + + const dword *scan = scan_tables[direction]; + int last; + do{ + int run; + int level = get_coeff(bs, &run, &last, 1, 0); + if(run == -1){ + DPRINTF(XVID_DEBUG_ERROR,"fatal: invalid run"); + break; + } + coeff += run; + block[scan[coeff]] = level; + + //DPRINTF(XVID_DEBUG_COEFF,"block[%i] %i\n", scan[coeff], level); + + if(level < -2047 || level > 2047){ + DPRINTF(XVID_DEBUG_ERROR,"warning: intra_overflow %i\n", level); + } + coeff++; + } while(!last); +} + +//---------------------------- + +void S_decoder::get_inter_block(Bitstream * bs, int *block, int direction){ + + const dword *scan = scan_tables[direction]; + int p = 0; + int last; + do{ + int run; + int level = get_coeff(bs, &run, &last, 0, 0); + if(run == -1){ + DPRINTF(XVID_DEBUG_ERROR,"fatal: invalid run"); + break; + } + p += run; + block[scan[p]] = level; + + //DPRINTF(XVID_DEBUG_COEFF,"block[%i] %i\n", scan[p], level); + + if(level < -2047 || level > 2047){ + DPRINTF(XVID_DEBUG_ERROR,"warning: inter overflow %i\n", level); + } + p++; + } while(!last); +} + +//---------------------------- + diff --git a/modules/xvid_dec/xvid_wce/mbprediction.cpp b/modules/xvid_dec/xvid_wce/mbprediction.cpp new file mode 100644 index 0000000..ecea2ef --- /dev/null +++ b/modules/xvid_dec/xvid_wce/mbprediction.cpp @@ -0,0 +1,341 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Prediction module - + * + * Copyright (C) 2001-2003 Michael Militzer <isibaar@xvid.org> + * 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: mbprediction.cpp,v 1.1.1.1 2005/07/13 14:36:15 jeanlf Exp $ + * + ****************************************************************************/ + +#include "global.h" +#include "mbprediction.h" + +//---------------------------- + +#if 0 + +#define Divide(a,b) (((a)>0) ? ((a)+((b)>>1))/(b) : ((a)-((b)>>1))/(b)) + +#else + +#define RET_DIV(a, N) return (a*N+32768) >> 16; +#define RET_SHR(a, N, S) return (a+N) >> S; + +static int Divide(int a, int b){ + switch(b){ + case 1: return a; + case 2: return a >> 1; + case 4: RET_SHR(a, 2, 2); + case 8: RET_SHR(a, 4, 3); + case 16: RET_SHR(a, 8, 4); + case 32: RET_SHR(a, 16, 5); + case 3: RET_DIV(a, 21845); + case 5: RET_DIV(a, 13107); + case 6: RET_DIV(a, 10923); + case 7: RET_DIV(a, 9362); + case 9: RET_DIV(a, 7282); + case 10: RET_DIV(a, 6554); + case 11: RET_DIV(a, 5958); + case 12: RET_DIV(a, 5461); + case 13: RET_DIV(a, 5041); + case 14: RET_DIV(a, 4681); + case 15: RET_DIV(a, 4369); + case 17: RET_DIV(a, 3855); + case 18: RET_DIV(a, 3641); + case 19: RET_DIV(a, 3449); + case 20: RET_DIV(a, 3277); + case 21: RET_DIV(a, 3121); + case 22: RET_DIV(a, 2979); + case 23: RET_DIV(a, 2849); + case 24: RET_DIV(a, 2731); + case 25: RET_DIV(a, 2621); + case 26: RET_DIV(a, 2521); + case 27: RET_DIV(a, 2427); + case 28: RET_DIV(a, 2341); + case 29: RET_DIV(a, 2260); + case 30: RET_DIV(a, 2185); + case 31: RET_DIV(a, 2114); + } + return ((a>0) ? (a+(b>>1))/b : (a-(b>>1))/b); +} +#endif + +//---------------------------- + +inline int rescale(int predict_quant, int current_quant, int coeff){ + + if(!coeff) + return 0; + return Divide(coeff * predict_quant, current_quant); +} + +//---------------------------- + +static const int default_acdc_values[15] = { + 1024, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 +}; + +//---------------------------- +/* get dc/ac prediction direction for a single block and place + predictor values into MB->pred_values[j][..] +*/ +void predict_acdc(MACROBLOCK *pMBs, dword x, dword y, dword mb_width, dword block, int qcoeff[64], dword current_quant, int iDcScaler, int predictors[8], int bound){ + + const int mbpos = (y * mb_width) + x; + int *left, *top, *diag, *current; + + int left_quant = current_quant; + int top_quant = current_quant; + + const int *pLeft = default_acdc_values; + const int *pTop = default_acdc_values; + const int *pDiag = default_acdc_values; + + dword index = x + y * mb_width; /* current macroblock */ + int *acpred_direction = &pMBs[index].acpred_directions[block]; + dword i; + + left = top = diag = current = 0; + + /* grab left,top and diag macroblocks */ + + /* left macroblock */ + + if (x && mbpos >= bound + 1 && + (pMBs[index - 1].mode == MODE_INTRA || + pMBs[index - 1].mode == MODE_INTRA_Q)) { + + left = pMBs[index - 1].pred_values[0]; + left_quant = pMBs[index - 1].quant; + } + /* top macroblock */ + + if (mbpos >= bound + (int)mb_width && + (pMBs[index - mb_width].mode == MODE_INTRA || + pMBs[index - mb_width].mode == MODE_INTRA_Q)) { + + top = pMBs[index - mb_width].pred_values[0]; + top_quant = pMBs[index - mb_width].quant; + } + /* diag macroblock */ + + if (x && mbpos >= bound + (int)mb_width + 1 && + (pMBs[index - 1 - mb_width].mode == MODE_INTRA || + pMBs[index - 1 - mb_width].mode == MODE_INTRA_Q)) { + + diag = pMBs[index - 1 - mb_width].pred_values[0]; + } + + current = pMBs[index].pred_values[0]; + + /* now grab pLeft, pTop, pDiag _blocks_ */ + + switch(block){ + case 0: + if(left) + pLeft = left + MBPRED_SIZE; + if(top) + pTop = top + (MBPRED_SIZE << 1); + if(diag) + pDiag = diag + 3 * MBPRED_SIZE; + break; + + case 1: + pLeft = current; + left_quant = current_quant; + if(top){ + pTop = top + 3 * MBPRED_SIZE; + pDiag = top + (MBPRED_SIZE << 1); + } + break; + + case 2: + if(left){ + pLeft = left + 3 * MBPRED_SIZE; + pDiag = left + MBPRED_SIZE; + } + pTop = current; + top_quant = current_quant; + break; + + case 3: + pLeft = current + (MBPRED_SIZE << 1); + left_quant = current_quant; + pTop = current + MBPRED_SIZE; + top_quant = current_quant; + pDiag = current; + break; + + case 4: + if(left) + pLeft = left + (MBPRED_SIZE << 2); + if(top) + pTop = top + (MBPRED_SIZE << 2); + if(diag) + pDiag = diag + (MBPRED_SIZE << 2); + break; + + case 5: + if(left) + pLeft = left + 5 * MBPRED_SIZE; + if(top) + pTop = top + 5 * MBPRED_SIZE; + if(diag) + pDiag = diag + 5 * MBPRED_SIZE; + break; + } + + /* + * determine ac prediction direction & ac/dc predictor place rescaled ac/dc + * predictions into predictors[] for later use + */ + + if(ABS(pLeft[0] - pDiag[0]) < ABS(pDiag[0] - pTop[0])){ + //vertical + *acpred_direction = 1; + predictors[0] = Divide(pTop[0], iDcScaler); + for(i = 1; i < 8; i++) + predictors[i] = rescale(top_quant, current_quant, pTop[i]); + }else{ + //horizontal + *acpred_direction = 2; + predictors[0] = Divide(pLeft[0], iDcScaler); + for(i = 1; i < 8; i++) + predictors[i] = rescale(left_quant, current_quant, pLeft[i + 7]); + } +} + +//---------------------------- +/* decoder: add predictors to dct_codes[] and + store current coeffs to pred_values[] for future prediction +*/ +void add_acdc(MACROBLOCK *pMB, dword block, int dct_codes[64], dword iDcScaler, int predictors[8]){ + + byte acpred_direction = pMB->acpred_directions[block]; + int *pCurrent = pMB->pred_values[block]; + + DPRINTF(XVID_DEBUG_COEFF,"predictor[0] %i\n", predictors[0]); + + dct_codes[0] += predictors[0]; /* dc prediction */ + pCurrent[0] = dct_codes[0] * iDcScaler; + + if(acpred_direction == 1){ + for(int i = 1; i < 8; i++){ + int level = dct_codes[i] + predictors[i]; + //DPRINTF(XVID_DEBUG_COEFF,"predictor[%i] %i\n",i, predictors[i]); + dct_codes[i] = level; + pCurrent[i] = level; + pCurrent[i + 7] = dct_codes[i * 8]; + } + }else + if(acpred_direction == 2){ + for(int i = 1; i < 8; i++){ + int level = dct_codes[i * 8] + predictors[i]; + //DPRINTF(XVID_DEBUG_COEFF,"predictor[%i] %i\n",i*8, predictors[i]); + dct_codes[i * 8] = level; + pCurrent[i + 7] = level; + pCurrent[i] = dct_codes[i]; + } + }else{ + for(int i = 1; i < 8; i++) { + pCurrent[i] = dct_codes[i]; + pCurrent[i + 7] = dct_codes[i * 8]; + } + } +} + +//---------------------------- + +static const VECTOR zeroMV = { 0, 0 }; + +VECTOR get_pmv2(const MACROBLOCK * const mbs, const int mb_width, const int bound, const int x, const int y, const int block){ + + int lx, ly, lz; /* left */ + int tx, ty, tz; /* top */ + int rx, ry, rz; /* top-right */ + int lpos, tpos, rpos; + int num_cand = 0, last_cand = 1; + + VECTOR pmv[4]; /* left neighbour, top neighbour, top-right neighbour */ + + switch(block){ + case 0: + lx = x - 1; ly = y; lz = 1; + tx = x; ty = y - 1; tz = 2; + rx = x + 1; ry = y - 1; rz = 2; + break; + case 1: + lx = x; ly = y; lz = 0; + tx = x; ty = y - 1; tz = 3; + rx = x + 1; ry = y - 1; rz = 2; + break; + case 2: + lx = x - 1; ly = y; lz = 3; + tx = x; ty = y; tz = 0; + rx = x; ry = y; rz = 1; + break; + default: + lx = x; ly = y; lz = 2; + tx = x; ty = y; tz = 0; + rx = x; ry = y; rz = 1; + } + + lpos = lx + ly * mb_width; + rpos = rx + ry * mb_width; + tpos = tx + ty * mb_width; + + if(lpos >= bound && lx >= 0){ + num_cand++; + pmv[1] = mbs[lpos].mvs[lz]; + }else + pmv[1] = zeroMV; + + if(tpos >= bound){ + num_cand++; + last_cand = 2; + pmv[2] = mbs[tpos].mvs[tz]; + }else + pmv[2] = zeroMV; + + if(rpos >= bound && rx < mb_width){ + num_cand++; + last_cand = 3; + pmv[3] = mbs[rpos].mvs[rz]; + }else + pmv[3] = zeroMV; + + //if there're more than one candidate, we return the median vector + if(num_cand > 1){ + //set median + pmv[0].x = + MIN(MAX(pmv[1].x, pmv[2].x), + MIN(MAX(pmv[2].x, pmv[3].x), MAX(pmv[1].x, pmv[3].x))); + pmv[0].y = + MIN(MAX(pmv[1].y, pmv[2].y), + MIN(MAX(pmv[2].y, pmv[3].y), MAX(pmv[1].y, pmv[3].y))); + return pmv[0]; + } + + return pmv[last_cand]; //no point calculating median mv +} + +//---------------------------- diff --git a/modules/xvid_dec/xvid_wce/mbprediction.h b/modules/xvid_dec/xvid_wce/mbprediction.h new file mode 100644 index 0000000..9528589 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/mbprediction.h @@ -0,0 +1,54 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Prediction header - + * + * Copyright(C) 2002-2003 xvid team <xvid-devel@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: mbprediction.h,v 1.1.1.1 2005/07/13 14:36:15 jeanlf Exp $ + * + ****************************************************************************/ + + +#ifndef _MBPREDICTION_H_ +#define _MBPREDICTION_H_ + +#include "portab.h" +#include "decoder.h" +#include "global.h" + +#define MIN(X, Y) ((X)<(Y)?(X):(Y)) +#define MAX(X, Y) ((X)>(Y)?(X):(Y)) + +/* very large value */ +#define MV_MAX_ERROR (4096 * 256) + +#define MVequal(A,B) ( ((A).x)==((B).x) && ((A).y)==((B).y) ) + +void add_acdc(MACROBLOCK *pMB, dword block, int dct_codes[64], dword iDcScaler, int predictors[8]); + +void predict_acdc(MACROBLOCK * pMBs, dword x, dword y, dword mb_width, dword block, int qcoeff[64], dword current_quant, int iDcScaler, int predictors[8], int bound); + +VECTOR +get_pmv2(const MACROBLOCK * const mbs, + const int mb_width, + const int bound, + const int x, + const int y, + const int block); + +#endif /* _MBPREDICTION_H_ */ diff --git a/modules/xvid_dec/xvid_wce/mem_align.cpp b/modules/xvid_dec/xvid_wce/mem_align.cpp new file mode 100644 index 0000000..9894d7b --- /dev/null +++ b/modules/xvid_dec/xvid_wce/mem_align.cpp @@ -0,0 +1,131 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Aligned Memory Allocator - + * + * Copyright(C) 2002-2003 Edouard Gomez <ed.gomez@free.fr> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: mem_align.cpp,v 1.2 2008/08/20 13:57:54 jeanlf Exp $ + * + ****************************************************************************/ + +#include "mem_align.h" +#include "global.h" + +#ifdef __SYMBIAN32__ +#include <e32std.h> +#endif + +/***************************************************************************** + * xvid_malloc + * + * This function allocates 'size' bytes (usable by the user) on the heap and + * takes care of the requested 'alignment'. + * In order to align the allocated memory block, the xvid_malloc allocates + * 'size' bytes + 'alignment' bytes. So try to keep alignment very small + * when allocating small pieces of memory. + * + * NB : a block allocated by xvid_malloc _must_ be freed with xvid_free + * (the libc free will return an error) + * + * Returned value : - NULL on error + * - Pointer to the allocated aligned block + * + ****************************************************************************/ + +#include "Rules.h" + +void *xvid_malloc(long size, dword alignment){ + + byte *mem_ptr; + + + if (!alignment) { + + /* We have not to satisfy any alignment */ + //mem_ptr = (byte*)malloc(size + 1); + //mem_ptr = new(ELeave) byte[size+1]; + mem_ptr = new byte[size+1]; + if(mem_ptr) { + + /* Store (mem_ptr - "real allocated memory") in *(mem_ptr-1) */ + *mem_ptr = (byte)1; + + /* Return the mem_ptr pointer */ + return ((void *)(mem_ptr+1)); + } + } else { + byte *tmp; + + /* Allocate the required size memory + alignment so we + * can realign the data if necessary */ + //tmp = (byte *) malloc(size + alignment); + //tmp = new(ELeave) byte[size + alignment]; + tmp = new byte[size + alignment]; + if(tmp) { + + /* Align the tmp pointer */ + mem_ptr = + (byte *) ((dword)(tmp + alignment - 1) & (~(dword)(alignment - 1))); + + /* Special case where malloc have already satisfied the alignment + * We must add alignment to mem_ptr because we must store + * (mem_ptr - tmp) in *(mem_ptr-1) + * If we do not add alignment to mem_ptr then *(mem_ptr-1) points + * to a forbidden memory space */ + if (mem_ptr == tmp) + mem_ptr += alignment; + + /* (mem_ptr - tmp) is stored in *(mem_ptr-1) so we are able to retrieve + * the real malloc block allocated and free it in xvid_free */ + *(mem_ptr - 1) = (byte) (mem_ptr - tmp); + + /* Return the aligned pointer */ + return ((void *)mem_ptr); + } + } + return 0; +} + +/***************************************************************************** + * xvid_free + * + * Free a previously 'xvid_malloc' allocated block. Does not free NULL + * references. + * + * Returned value : None. + * + ****************************************************************************/ + +void xvid_free(void *mem_ptr){ + + if(!mem_ptr) + return; + + /* Aligned pointer */ + byte *ptr = (byte*)mem_ptr; + + /* *(ptr - 1) holds the offset to the real allocated block + * we sub that offset os we free the real pointer */ + ptr -= *(ptr - 1); + + /* Free the memory */ + //free(ptr); + delete[] ptr; +} + +//---------------------------- diff --git a/modules/xvid_dec/xvid_wce/mem_align.h b/modules/xvid_dec/xvid_wce/mem_align.h new file mode 100644 index 0000000..da44425 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/mem_align.h @@ -0,0 +1,35 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Aligned Memory Allocator header - + * + * Copyright(C) 2002-2003 Edouard Gomez <ed.gomez@free.fr> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: mem_align.h,v 1.1.1.1 2005/07/13 14:36:15 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _MEM_ALIGN_H_ +#define _MEM_ALIGN_H_ + +#include "portab.h" +#include "Rules.h" + +void *xvid_malloc(long size, dword alignment); +void xvid_free(void *mem_ptr); + +#endif /* _MEM_ALIGN_H_ */ diff --git a/modules/xvid_dec/xvid_wce/mem_transfer.cpp b/modules/xvid_dec/xvid_wce/mem_transfer.cpp new file mode 100644 index 0000000..4aedeba --- /dev/null +++ b/modules/xvid_dec/xvid_wce/mem_transfer.cpp @@ -0,0 +1,281 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - 8bit<->16bit transfer - + * + * Copyright(C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: mem_transfer.cpp,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#include "global.h" +#include "mem_transfer.h" + +/***************************************************************************** + * + * All these functions are used to transfer data from a 8 bit data array + * to a 16 bit data array. + * + * This is typically used during motion compensation, that's why some + * functions also do the addition/substraction of another buffer during the + * so called transfer. + * + ****************************************************************************/ + +//---------------------------- +/* + * SRC - the source buffer + * DST - the destination buffer + * + * Then the function does the 8->16 bit transfer and this serie of operations : + * + * SRC (16bit) = SRC + * DST (8bit) = max(min(SRC, 255), 0) + */ +void transfer_16to8copy(byte *dst, const int *src, dword stride){ + + for(int j = 0; j < 8; j++){ + for(int i = 0; i < 8; i++){ + int pixel = *src++; + + if(pixel < 0) + pixel = 0; + //else + if(pixel > 255) + pixel = 255; + dst[i] = byte(pixel); + } + dst += stride; + } +} + +//---------------------------- +/* + * SRC - the source buffer + * DST - the destination buffer + * + * Then the function does the 16->8 bit transfer and this serie of operations : + * + * SRC (16bit) = SRC + * DST (8bit) = max(min(DST+SRC, 255), 0) + */ +void transfer_16to8add(byte *dst, const int *src, dword stride){ + + for(int j = 0; j < 8; j++){ + for(int i = 0; i < 8; i++){ + int pixel = dst[i] + *src++; + + if(pixel < 0) + pixel = 0; + //else + if(pixel > 255) + pixel = 255; + dst[i] = byte(pixel); + } + //src += 8; + dst += stride; + } +} + +#ifndef _ARM_ +//---------------------------- +/* + * SRC - the source buffer + * DST - the destination buffer + * + * Then the function does the 8->8 bit transfer and this serie of operations : + * + * SRC (8bit) = SRC + * DST (8bit) = SRC + */ +void transfer8x8_copy(byte *dst, const byte *src, dword stride){ + + assert(!(stride&3)); + assert(!(dword(dst)&3)); +#if defined USE_ARM_ASM && 1 + int y, tmp, tmp1; + +#define NL "add %0, %0, %4\n add %1, %1, %4\n\t" + asm volatile( + "orr %2, %1, %4\n\t" + "tst %2, #3\n bne .tc_no_dw\n\t" + //dword version + "\n.tc_dw_loop:\n\t" +#define COPY_QW "ldmia %1, { %2, %3 }\n stmia %0, { %2, %3 }\n\t" + COPY_QW NL COPY_QW NL COPY_QW NL COPY_QW NL COPY_QW NL COPY_QW NL COPY_QW NL COPY_QW + "b .tc_end\n\t" + + "\n.tc_no_dw:\n\t" + "tst %2, #1\n bne .tc_no_w\n\t" + //word version + "\n.tc_w_loop:\n\t" +#define COPY_W \ + "ldrh %2, [%1, #0]\n strh %2, [%0, #0]\n\t" \ + "ldrh %2, [%1, #2]\n strh %2, [%0, #2]\n\t" \ + "ldrh %2, [%1, #4]\n strh %2, [%0, #4]\n\t" \ + "ldrh %2, [%1, #6]\n strh %2, [%0, #6]\n\t" + COPY_W NL COPY_W NL COPY_W NL COPY_W NL COPY_W NL COPY_W NL COPY_W NL COPY_W + "b .tc_end\n\t" + + "\n.tc_no_w:\n\t" + "mov %5, #8\n\t" + "\n.tc_b_loop:\n\t" + "ldrb %2, [%1, #0]\n strb %2, [%0, #0]\n\t" + "ldrb %2, [%1, #1]\n strb %2, [%0, #1]\n\t" + "ldrb %2, [%1, #2]\n strb %2, [%0, #2]\n\t" + "ldrb %2, [%1, #3]\n strb %2, [%0, #3]\n\t" + "ldrb %2, [%1, #4]\n strb %2, [%0, #4]\n\t" + "ldrb %2, [%1, #5]\n strb %2, [%0, #5]\n\t" + "ldrb %2, [%1, #6]\n strb %2, [%0, #6]\n\t" + "ldrb %2, [%1, #7]\n strb %2, [%0, #7]\n\t" + NL + "subs %5, %5, #1\n bne .tc_b_loop\n\t" + "\n.tc_end:\n\t" + : "+r"(dst), "+r"(src), "&=r"(tmp), "&=r"(tmp1) + : "r"(stride), "r"(y) + ); +#else + if(!(dword(src)&3) && !(dword(dst)&3)){ + for(dword y = 8; y--; ){ + ((dword*)dst)[0] = ((dword*)src)[0]; + ((dword*)dst)[1] = ((dword*)src)[1]; + + src += stride; + dst += stride; + } + }else + if(!(dword(src)&1) && !(dword(dst)&1)){ + for(dword y = 8; y--; ){ + ((word*)dst)[0] = ((word*)src)[0]; + ((word*)dst)[1] = ((word*)src)[1]; + ((word*)dst)[2] = ((word*)src)[2]; + ((word*)dst)[3] = ((word*)src)[3]; + + src += stride; + dst += stride; + } + }else{ + for(dword y = 8; y--; ){ + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; + + src += stride; + dst += stride; + } + } +#endif +} +#endif + +//---------------------------- +#ifndef PROFILE + +#ifdef __SYMBIAN32__ + +#include <e32base.h> + +void MemSet(void *dst, byte c, dword len){ + Mem::Fill(dst, len, c); +} + +//---------------------------- + +void MemCpy(void *dst, const void *src, dword len){ + Mem::Copy(dst, src, len); +} + +//---------------------------- + +int MemCmp(const void *mem1, const void *mem2, dword len){ + return Mem::Compare((byte*)mem1, len, (byte*)mem2, len); +} + +//---------------------------- + +dword StrLen(const char *cp){ return User::StringLength((const byte*)cp); } + +//---------------------------- + +void Fatal(const char *msg, dword code){ + + int len = StrLen(msg); + TBuf16<20> desc; desc.Copy(TPtr8((byte*)msg, Min(len, 20), len)); + User::Panic(desc, code); +} + + +#else + +//---------------------------- +#include <memory.h> +#include <stdio.h> + +void MemSet(void *dst, byte c, dword len){ + memset(dst, c, len); +} + +//---------------------------- + +void MemCpy(void *dst, const void *src, dword len){ + memcpy(dst, src, len); +} + +//---------------------------- + +int MemCmp(const void *mem1, const void *mem2, dword len){ + return memcmp(mem1, mem2, len); +} + +//---------------------------- + +void *operator new(size_t sz, TLeave){ + void *vp = new byte[sz]; + if(!vp){ + //todo: fatal error + Fatal("Not enough memory", sz); + } + return vp; +} + +//---------------------------- +#include <windows.h> + +void Fatal(const char *msg, dword code){ + +#ifdef _WIN32_WCE + wchar_t buf[256]; + swprintf(buf, L"%i (%i)", msg, code); + MessageBox(NULL, buf, L"Fatal error", MB_OK | MB_ICONERROR); +#else + char buf[256]; + sprintf(buf, "%i (%i)", msg, code); + MessageBox(NULL, buf, "Fatal error", MB_OK | MB_ICONERROR); +#endif + exit(1); +} + +//---------------------------- +#endif +#endif +//---------------------------- diff --git a/modules/xvid_dec/xvid_wce/mem_transfer.h b/modules/xvid_dec/xvid_wce/mem_transfer.h new file mode 100644 index 0000000..07aa771 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/mem_transfer.h @@ -0,0 +1,61 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - 8<->16 bit buffer transfer header - + * + * Copyright(C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: mem_transfer.h,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _MEM_TRANSFER_H +#define _MEM_TRANSFER_H + +#include "Rules.h" + +/***************************************************************************** + * transfer16to8 API + ****************************************************************************/ +void transfer_16to8copy(byte *dst, const int *src, dword stride); + +/***************************************************************************** + * transfer16to8 + addition op API + ****************************************************************************/ +void transfer_16to8add(byte *dst, const int *src, dword stride); + +/***************************************************************************** + * transfer8to8 + no op + ****************************************************************************/ +#ifdef _ARM_ +extern"C" +#endif +void transfer8x8_copy(byte *const dst, const byte * const src, const dword stride); + +//---------------------------- + +inline void transfer16x16_copy(byte * const dst, const byte * const src, const dword stride){ + + transfer8x8_copy(dst, src, stride); + transfer8x8_copy(dst + 8, src + 8, stride); + transfer8x8_copy(dst + 8*stride, src + 8*stride, stride); + transfer8x8_copy(dst + 8*stride + 8, src + 8*stride + 8, stride); +} + +//---------------------------- + +#endif diff --git a/modules/xvid_dec/xvid_wce/note b/modules/xvid_dec/xvid_wce/note new file mode 100644 index 0000000..2e50121 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/note @@ -0,0 +1,4 @@ +This is not original version of XviD codec. +To obtain official source code for XviD, please visit www.xvid.org. +This is a modified version of the original software. + diff --git a/modules/xvid_dec/xvid_wce/portab.h b/modules/xvid_dec/xvid_wce/portab.h new file mode 100644 index 0000000..a4e2d6c --- /dev/null +++ b/modules/xvid_dec/xvid_wce/portab.h @@ -0,0 +1,166 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Portable macros, types and inlined assembly - + * + * Copyright(C) 2002 Michael Militzer <isibaar@xvid.org> + * 2002-2003 Peter Ross <pross@xvid.org> + * 2002-2003 Edouard Gomez <ed.gomez@free.fr> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: portab.h,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _PORTAB_H_ +#define _PORTAB_H_ + +#include "Rules.h" + +#define ARCH_IS_LITTLE_ENDIAN +#define ARCH_IS_GENERIC + + +/***************************************************************************** + * Common things + ****************************************************************************/ + +/* Buffer size for msvc implementation because it outputs to DebugOutput */ +#ifdef _DEBUG +extern dword xvid_debug; +#define DPRINTF_BUF_SZ 1024 +#endif + +/***************************************************************************** + * Types used in XviD sources + ****************************************************************************/ + +/*---------------------------------------------------------------------------- + | For MSVC + *---------------------------------------------------------------------------*/ + +//#if defined(_MSC_VER) || defined (__WATCOMC__) +#define int64_t int + +/*---------------------------------------------------------------------------- + | For all other compilers, use the standard header file + | (compiler should be ISO C99 compatible, perhaps ISO C89 is enough) + *---------------------------------------------------------------------------*/ + +//#endif + +/***************************************************************************** + * Some things that are only architecture dependant + ****************************************************************************/ + +#define CACHE_LINE 32 + +/***************************************************************************** + * MSVC compiler specific macros, functions + ****************************************************************************/ + +#ifdef _MSC_VER + +/*---------------------------------------------------------------------------- + | Common msvc stuff + *---------------------------------------------------------------------------*/ + + /* + * This function must be declared/defined all the time because MSVC does + * not support C99 variable arguments macros. + * + * Btw, if the MS compiler does its job well, it should remove the nop + * DPRINTF function when not compiling in _DEBUG mode + */ +#if defined _DEBUG && defined _WINDOWS + +void DPRINTF(int level, char *fmt, int p=0); + +#else + +inline void DPRINTF(int level, char *fmt, int p=0){} + +#endif + + +/***************************************************************************** + * GNU CC compiler stuff + ****************************************************************************/ + +#elif defined __GNUC__ || defined __ICC /* Compiler test */ + +/*---------------------------------------------------------------------------- + | Common gcc stuff + *---------------------------------------------------------------------------*/ + +/* + * As gcc is (mostly) C99 compliant, we define DPRINTF only if it's realy needed + * and it's a macro calling fprintf directly + */ +# ifdef _DEBUG + + /* Needed for all debuf fprintf calls */ +# include <stdio.h> +# include <stdarg.h> + + inline void DPRINTF(int level, char *format, int p=0){ + va_list args; + va_start(args, format); + if(xvid_debug & level){ + vfprintf(stderr, format, args); + } + } + +# else /* _DEBUG */ +inline void DPRINTF(int level, char *format, int p=0) {} +# endif /* _DEBUG */ + +#else //Compiler test + + /* + * Ok we know nothing about the compiler, so we fallback to ANSI C + * features, so every compiler should be happy and compile the code. + * + * This is (mostly) equivalent to ARCH_IS_GENERIC. + */ + +#ifdef _DEBUG + /* Needed for all debuf fprintf calls */ +# include <stdio.h> +# include <stdarg.h> + +static __inline void DPRINTF(int level, char *format, int p=0){ + va_list args; + va_start(args, format); + if(xvid_debug & level) { + vfprintf(stderr, format, args); + } +} + +# else //_DEBUG +inline void DPRINTF(int level, char *format, int p=0){} +#endif //_DEBUG + +#define ByteSwap(a) \ + ((a) = ((a) << 24) | (((a) & 0xff00) << 8) | (((a) >> 8) & 0xff00) | (((a) >> 24) & 0xff)) + +#define DECLARE_ALIGNED_MATRIX(name, sizex, sizey, type, alignment) \ + type name[(sizex)*(sizey)] + +#endif //Compiler test + + +#endif /* PORTAB_H */ diff --git a/modules/xvid_dec/xvid_wce/qpel.inl b/modules/xvid_dec/xvid_wce/qpel.inl new file mode 100644 index 0000000..e4a6b74 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/qpel.inl @@ -0,0 +1,146 @@ +static void FUNC_H(byte *Dst, const byte *Src, int H, int BpS, int Rnd){ + while(H-->0) { + int i, k; + int Sums[SIZE] = { 0 }; + for(i=0; i<=SIZE; ++i) + for(k=0; k<SIZE; ++k) + Sums[k] += TABLE[i][k] * Src[i]; + + for(i=0; i<SIZE; ++i) { + int C = ( Sums[i] + 16-Rnd ) >> 5; + if (C<0) C = 0; else if (C>255) C = 255; + STORE(Dst[i], C); + } + Src += BpS; + Dst += BpS; + } +} + +static void FUNC_V(byte *Dst, const byte *Src, int W, int BpS, int Rnd){ + while(W-->0) { + int i, k; + int Sums[SIZE] = { 0 }; + const byte *S = Src++; + byte *D = Dst++; + for(i=0; i<=SIZE; ++i) { + for(k=0; k<SIZE; ++k) + Sums[k] += TABLE[i][k] * S[0]; + S += BpS; + } + + for(i=0; i<SIZE; ++i) { + int C = ( Sums[i] + 16-Rnd )>>5; + if (C<0) C = 0; else if (C>255) C = 255; + STORE(D[0], C); + D += BpS; + } + } +} + +static +void FUNC_HA(byte *Dst, const byte *Src, int H, int BpS, int Rnd) +{ + while(H-->0) { + int i, k; + int Sums[SIZE] = { 0 }; + for(i=0; i<=SIZE; ++i) + for(k=0; k<SIZE; ++k) + Sums[k] += TABLE[i][k] * Src[i]; + + for(i=0; i<SIZE; ++i) { + int C = ( Sums[i] + 16-Rnd ) >> 5; + if (C<0) C = 0; else if (C>255) C = 255; + C = (C+Src[i]+1-Rnd) >> 1; + STORE(Dst[i], C); + } + Src += BpS; + Dst += BpS; + } +} + +static +void FUNC_HA_UP(byte *Dst, const byte *Src, int H, int BpS, int Rnd) +{ + while(H-->0) { + int i, k; + int Sums[SIZE] = { 0 }; + for(i=0; i<=SIZE; ++i) + for(k=0; k<SIZE; ++k) + Sums[k] += TABLE[i][k] * Src[i]; + + for(i=0; i<SIZE; ++i) { + int C = ( Sums[i] + 16-Rnd ) >> 5; + if (C<0) C = 0; else if (C>255) C = 255; + C = (C+Src[i+1]+1-Rnd) >> 1; + STORE(Dst[i], C); + } + Src += BpS; + Dst += BpS; + } +} + +static +void FUNC_VA(byte *Dst, const byte *Src, int W, int BpS, int Rnd) +{ + while(W-->0) { + int i, k; + int Sums[SIZE] = { 0 }; + const byte *S = Src; + byte *D = Dst; + + for(i=0; i<=SIZE; ++i) { + for(k=0; k<SIZE; ++k) + Sums[k] += TABLE[i][k] * S[0]; + S += BpS; + } + + S = Src; + for(i=0; i<SIZE; ++i) { + int C = ( Sums[i] + 16-Rnd )>>5; + if (C<0) C = 0; else if (C>255) C = 255; + C = ( C+S[0]+1-Rnd ) >> 1; + STORE(D[0], C); + D += BpS; + S += BpS; + } + Src++; + Dst++; + } +} + +static +void FUNC_VA_UP(byte *Dst, const byte *Src, int W, int BpS, int Rnd) +{ + while(W-->0) { + int i, k; + int Sums[SIZE] = { 0 }; + const byte *S = Src; + byte *D = Dst; + + for(i=0; i<=SIZE; ++i) { + for(k=0; k<SIZE; ++k) + Sums[k] += TABLE[i][k] * S[0]; + S += BpS; + } + + S = Src + BpS; + for(i=0; i<SIZE; ++i) { + int C = ( Sums[i] + 16-Rnd )>>5; + if (C<0) C = 0; else if (C>255) C = 255; + C = ( C+S[0]+1-Rnd ) >> 1; + STORE(D[0], C); + D += BpS; + S += BpS; + } + Dst++; + Src++; + } +} + +#undef STORE +#undef FUNC_H +#undef FUNC_V +#undef FUNC_HA +#undef FUNC_VA +#undef FUNC_HA_UP +#undef FUNC_VA_UP diff --git a/modules/xvid_dec/xvid_wce/qpel_tab.cpp b/modules/xvid_dec/xvid_wce/qpel_tab.cpp new file mode 100644 index 0000000..a9352d8 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/qpel_tab.cpp @@ -0,0 +1,103 @@ +#include "portab.h" + +//---------------------------- +// Quarterpel FIR definition + +static const int FIR_Tab_16[17][16] = { + { 14, -3, 2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 23, 19, -6, 3, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { -7, 20, 20, -6, 3, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 3, -6, 20, 20, -6, 3, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { -1, 3, -6, 20, 20, -6, 3, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, -1, 3, -6, 20, 20, -6, 3, -1, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, -1, 3, -6, 20, 20, -6, 3, -1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, -1, 3, -6, 20, 20, -6, 3, -1, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, -1, 3, -6, 20, 20, -6, 3, -1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, -1, 3, -6, 20, 20, -6, 3, -1, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, -1, 3, -6, 20, 20, -6, 3, -1, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, -1, 3, -6, 20, 20, -6, 3, -1, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, -6, 20, 20, -6, 3, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, -6, 20, 20, -6, 3 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, -6, 20, 20, -7 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, -6, 19, 23 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 2, -3, 14 } +}; + +static const int FIR_Tab_8[9][8] = { + { 14, -3, 2, -1, 0, 0, 0, 0 }, + { 23, 19, -6, 3, -1, 0, 0, 0 }, + { -7, 20, 20, -6, 3, -1, 0, 0 }, + { 3, -6, 20, 20, -6, 3, -1, 0 }, + { -1, 3, -6, 20, 20, -6, 3, -1 }, + { 0, -1, 3, -6, 20, 20, -6, 3 }, + { 0, 0, -1, 3, -6, 20, 20, -7 }, + { 0, 0, 0, -1, 3, -6, 19, 23 }, + { 0, 0, 0, 0, -1, 2, -3, 14 } +}; + + +//---------------------------- + +/* Implementation + ****************************************************************************/ + +/* 16x? filters */ + +#define SIZE 16 +#define TABLE FIR_Tab_16 + +#define STORE(d,s) (d) = (s) +#define FUNC_H H_Pass_16_C +#define FUNC_V V_Pass_16_C +#define FUNC_HA H_Pass_Avrg_16_C +#define FUNC_VA V_Pass_Avrg_16_C +#define FUNC_HA_UP H_Pass_Avrg_Up_16_C +#define FUNC_VA_UP V_Pass_Avrg_Up_16_C + +#include "qpel.inl" + +/* note: B-frame always uses Rnd=0... */ +#define STORE(d,s) (d) = ( (s)+(d)+1 ) >> 1 +#define FUNC_H H_Pass_16_Add_C +#define FUNC_V V_Pass_16_Add_C +#define FUNC_HA H_Pass_Avrg_16_Add_C +#define FUNC_VA V_Pass_Avrg_16_Add_C +#define FUNC_HA_UP H_Pass_Avrg_Up_16_Add_C +#define FUNC_VA_UP V_Pass_Avrg_Up_16_Add_C + +#include "qpel.inl" + +#undef SIZE +#undef TABLE + +/* 8x? filters */ + +#define SIZE 8 +#define TABLE FIR_Tab_8 + +#define STORE(d,s) (d) = (s) +#define FUNC_H H_Pass_8_C +#define FUNC_V V_Pass_8_C +#define FUNC_HA H_Pass_Avrg_8_C +#define FUNC_VA V_Pass_Avrg_8_C +#define FUNC_HA_UP H_Pass_Avrg_Up_8_C +#define FUNC_VA_UP V_Pass_Avrg_Up_8_C + +#include "qpel.inl" + +/* note: B-frame always uses Rnd=0... */ +#define STORE(d,s) (d) = ( (s)+(d)+1 ) >> 1 +#define FUNC_H H_Pass_8_Add_C +#define FUNC_V V_Pass_8_Add_C +#define FUNC_HA H_Pass_Avrg_8_Add_C +#define FUNC_VA V_Pass_Avrg_8_Add_C +#define FUNC_HA_UP H_Pass_Avrg_Up_8_Add_C +#define FUNC_VA_UP V_Pass_Avrg_Up_8_Add_C + +#include "qpel.inl" + +#undef SIZE +#undef TABLE + +//---------------------------- + diff --git a/modules/xvid_dec/xvid_wce/quant.h b/modules/xvid_dec/xvid_wce/quant.h new file mode 100644 index 0000000..bcf8da4 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/quant.h @@ -0,0 +1,54 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - (de)Quantization related header - + * + * Copyright(C) 2003 Edouard Gomez <ed.gomez@free.fr> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: quant.h,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _QUANT_H_ +#define _QUANT_H_ + +#include "portab.h" + +/***************************************************************************** + * Common API for Intra (de)Quant functions + ****************************************************************************/ + +typedef void (quant_intraFunc)(int *coeff, const int *data, dword quant, dword dcscalar, const dword *mpeg_quant_matrices); + +typedef quant_intraFunc *quant_intraFuncPtr; + +/* DeQuant functions */ +quant_intraFunc dequant_h263_intra; +quant_intraFunc dequant_mpeg_intra; + +/***************************************************************************** + * Common API for Inter (de)Quant functions + ****************************************************************************/ + +typedef void (quant_interFunc)(int *coeff, const int* data, dword quant, const dword *mpeg_quant_matrices); + +typedef quant_interFunc *quant_interFuncPtr; + +quant_interFunc dequant_h263_inter; +quant_interFunc dequant_mpeg_inter; + +#endif /* _QUANT_H_ */ diff --git a/modules/xvid_dec/xvid_wce/quant_h263.cpp b/modules/xvid_dec/xvid_wce/quant_h263.cpp new file mode 100644 index 0000000..167bfbb --- /dev/null +++ b/modules/xvid_dec/xvid_wce/quant_h263.cpp @@ -0,0 +1,85 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - MPEG4 Quantization H263 implementation - + * + * Copyright(C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: quant_h263.cpp,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#include "global.h" +#include "quant.h" + +//---------------------------- +/* dequantize intra-block & clamp to [-2048,2047] + */ +void dequant_h263_intra(int *data, const int *coeff, dword quant, dword dcscalar, const dword *mpeg_quant_matrices){ + + const int quant_m_2 = quant << 1; + const int quant_add = (quant & 1 ? quant : quant - 1); + + data[0] = *coeff++ * dcscalar; + if(data[0] < -2048){ + data[0] = -2048; + }//else + if(data[0] > 2047){ + data[0] = 2047; + } + for(int i = 1; i < 64; i++){ + int acLevel = *coeff++; + + if(acLevel == 0){ + data[i] = 0; + }else + if(acLevel < 0){ + acLevel = quant_m_2 * -acLevel + quant_add; + data[i] = (acLevel <= 2048 ? -acLevel : -2048); + }else{ + acLevel = quant_m_2 * acLevel + quant_add; + data[i] = (acLevel <= 2047 ? acLevel : 2047); + } + } +} + +//---------------------------- +/* dequantize inter-block & clamp to [-2048,2047] + */ + +void dequant_h263_inter(int *data, const int *coeff, dword quant, const dword *mpeg_quant_matrices){ + + const dword quant_m_2 = quant << 1; + const dword quant_add = (quant & 1 ? quant : quant - 1); + + for(int i = 0; i < 64; i++){ + int acLevel = *coeff++; + + if(acLevel == 0){ + data[i] = 0; + }else + if(acLevel < 0){ + acLevel = acLevel * quant_m_2 - quant_add; + data[i] = (acLevel >= -2048 ? acLevel : -2048); + }else{ + acLevel = acLevel * quant_m_2 + quant_add; + data[i] = (acLevel <= 2047 ? acLevel : 2047); + } + } +} + +//---------------------------- diff --git a/modules/xvid_dec/xvid_wce/quant_matrix.cpp b/modules/xvid_dec/xvid_wce/quant_matrix.cpp new file mode 100644 index 0000000..7e5ba5d --- /dev/null +++ b/modules/xvid_dec/xvid_wce/quant_matrix.cpp @@ -0,0 +1,115 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Quantization matrix management code - + * + * Copyright(C) 2002 Michael Militzer <isibaar@xvid.org> + * 2002 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: quant_matrix.cpp,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#include "quant_matrix.h" + +#define FIX(X) (((X)==1) ? 0xFFFF : ((1UL << 16) / (X) + 1)) +#define FIXL(X) ((1UL << 16) / (X) - 1) + +/***************************************************************************** + * Default matrices + ****************************************************************************/ + +static const byte default_intra_matrix[64] = { + 8, 17, 18, 19, 21, 23, 25, 27, + 17, 18, 19, 21, 23, 25, 27, 28, + 20, 21, 22, 23, 24, 26, 28, 30, + 21, 22, 23, 24, 26, 28, 30, 32, + 22, 23, 24, 26, 28, 30, 32, 35, + 23, 24, 26, 28, 30, 32, 35, 38, + 25, 26, 28, 30, 32, 35, 38, 41, + 27, 28, 30, 32, 35, 38, 41, 45 +}; + +//---------------------------- + +static const byte default_inter_matrix[64] = { + 16, 17, 18, 19, 20, 21, 22, 23, + 17, 18, 19, 20, 21, 22, 23, 24, + 18, 19, 20, 21, 22, 23, 24, 25, + 19, 20, 21, 22, 23, 24, 26, 27, + 20, 21, 22, 23, 25, 26, 27, 28, + 21, 22, 23, 24, 26, 27, 28, 30, + 22, 23, 24, 26, 27, 28, 30, 31, + 23, 24, 25, 27, 28, 30, 31, 33 +}; + +//---------------------------- + +const byte *get_default_intra_matrix(){ + return default_intra_matrix; +} + +//---------------------------- + +const byte *get_default_inter_matrix(){ + return default_inter_matrix; +} + +//---------------------------- + +void set_intra_matrix(dword *mpeg_quant_matrices, const byte *matrix){ + + dword *intra_matrix = mpeg_quant_matrices + 0*64; + dword *intra_matrix1 = mpeg_quant_matrices + 1*64; + dword *intra_matrix_fix = mpeg_quant_matrices + 2*64; + dword *intra_matrix_fixl = mpeg_quant_matrices + 3*64; + + for(int i = 0; i < 64; i++) { + intra_matrix[i] = (!i) ? 8 : matrix[i]; + intra_matrix1[i] = (intra_matrix[i]>>1); + intra_matrix1[i] += ((intra_matrix[i] == 1) ? 1: 0); + intra_matrix_fix[i] = FIX(intra_matrix[i]); + intra_matrix_fixl[i] = FIXL(intra_matrix[i]); + } +} + +//---------------------------- + +void set_inter_matrix(dword *mpeg_quant_matrices, const byte *matrix){ + + dword *inter_matrix = mpeg_quant_matrices + 4*64; + dword *inter_matrix1 = mpeg_quant_matrices + 5*64; + dword *inter_matrix_fix = mpeg_quant_matrices + 6*64; + dword *inter_matrix_fixl = mpeg_quant_matrices + 7*64; + + for(int i = 0; i < 64; i++){ + inter_matrix1[i] = ((inter_matrix[i] = matrix[i])>>1); + inter_matrix1[i] += ((inter_matrix[i] == 1) ? 1: 0); + inter_matrix_fix[i] = FIX(inter_matrix[i]); + inter_matrix_fixl[i] = FIXL(inter_matrix[i]); + } +} + +//---------------------------- + +void init_mpeg_matrix(dword *mpeg_quant_matrices){ + + set_intra_matrix(mpeg_quant_matrices, default_intra_matrix); + set_inter_matrix(mpeg_quant_matrices, default_inter_matrix); +} + +//---------------------------- diff --git a/modules/xvid_dec/xvid_wce/quant_matrix.h b/modules/xvid_dec/xvid_wce/quant_matrix.h new file mode 100644 index 0000000..562b9ef --- /dev/null +++ b/modules/xvid_dec/xvid_wce/quant_matrix.h @@ -0,0 +1,44 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Quantization matrix management header - + * + * Copyright(C) 2002 Michael Militzer <isibaar@xvid.org> + * 2002 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: quant_matrix.h,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _QUANT_MATRIX_H_ +#define _QUANT_MATRIX_H_ + +#include "portab.h" +#include "Rules.h" + +void init_mpeg_matrix(dword *mpeg_quant_matrices); + +void set_intra_matrix(dword *mpeg_quant_matrices, const byte *matrix); +void set_inter_matrix(dword *mpeg_quant_matrices, const byte *matrix); + +inline const dword *get_intra_matrix(const dword *mpeg_quant_matrices){ return (mpeg_quant_matrices + 0*64); } +inline const dword *get_inter_matrix(const dword *mpeg_quant_matrices){ return(mpeg_quant_matrices + 4*64); } + +const byte *get_default_intra_matrix(void); +const byte *get_default_inter_matrix(void); + +#endif /* _QUANT_MATRIX_H_ */ diff --git a/modules/xvid_dec/xvid_wce/quant_mpeg.cpp b/modules/xvid_dec/xvid_wce/quant_mpeg.cpp new file mode 100644 index 0000000..5ce7ed6 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/quant_mpeg.cpp @@ -0,0 +1,98 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - MPEG4 Quantization related header - + * + * Copyright(C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: quant_mpeg.cpp,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#include "global.h" +#include "quant.h" +#include "quant_matrix.h" + +//---------------------------- +/* dequantize intra-block & clamp to [-2048,2047] + * + * data[i] = (coeff[i] * default_intra_matrix[i] * quant2) >> 4; + */ +void dequant_mpeg_intra(int *data, const int *coeff, dword quant, dword dcscalar, const dword *mpeg_quant_matrices){ + + const dword *intra_matrix = get_intra_matrix(mpeg_quant_matrices); + + data[0] = coeff[0] * dcscalar; + if(data[0] < -2048){ + data[0] = -2048; + }else + if(data[0] > 2047){ + data[0] = 2047; + } + + for(int i = 1; i < 64; i++){ + if(coeff[i] == 0){ + data[i] = 0; + }else + if(coeff[i] < 0){ + int level = -coeff[i]; + + level = (level * intra_matrix[i] * quant) >> 3; + data[i] = (level <= 2048 ? -level : -2048); + }else{ + dword level = coeff[i]; + + level = (level * intra_matrix[i] * quant) >> 3; + data[i] = (level <= 2047 ? level : 2047); + } + } +} + +//---------------------------- +/* dequantize inter-block & clamp to [-2048,2047] + * data = ((2 * coeff + SIGN(coeff)) * inter_matrix[i] * quant) / 16 + */ + +void dequant_mpeg_inter(int *data, const int *coeff, dword quant, const dword *mpeg_quant_matrices){ + + dword sum = 0; + const dword *inter_matrix = get_inter_matrix(mpeg_quant_matrices); + + for(int i = 0; i < 64; i++){ + if(coeff[i] == 0){ + data[i] = 0; + }else + if(coeff[i] < 0){ + int level = -coeff[i]; + + level = ((2 * level + 1) * inter_matrix[i] * quant) >> 4; + data[i] = (level <= 2048 ? -level : -2048); + }else{ + dword level = coeff[i]; + level = ((2 * level + 1) * inter_matrix[i] * quant) >> 4; + data[i] = (level <= 2047 ? level : 2047); + } + sum ^= data[i]; + } + /* mismatch control */ + if ((sum & 1) == 0) { + data[63] ^= 1; + } +} + +//---------------------------- + diff --git a/modules/xvid_dec/xvid_wce/reduced.cpp b/modules/xvid_dec/xvid_wce/reduced.cpp new file mode 100644 index 0000000..0c13a70 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/reduced.cpp @@ -0,0 +1,195 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * Reduced-Resolution utilities + * + * Copyright(C) 2002 Pascal Massimino <skal@planet-d.net> + * + * This file is part of XviD, a free MPEG-4 video encoder/decoder + * + * XviD is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Under section 8 of the GNU General Public License, the copyright + * holders of XVID explicitly forbid distribution in the following + * countries: + * + * - Japan + * - United States of America + * + * Linking XviD statically or dynamically with other modules is making a + * combined work based on XviD. Thus, the terms and conditions of the + * GNU General Public License cover the whole combination. + * + * As a special exception, the copyright holders of XviD give you + * permission to link XviD with independent modules that communicate with + * XviD solely through the VFW1.1 and DShow interfaces, regardless of the + * license terms of these independent modules, and to copy and distribute + * the resulting combined work under terms of your choice, provided that + * every copy of the combined work is accompanied by a complete copy of + * the source code of XviD (the version of XviD used to produce the + * combined work), being distributed under the terms of the GNU General + * Public License plus this exception. An independent module is a module + * which is not derived from or based on XviD. + * + * Note that people who make modified versions of XviD are not obligated + * to grant this special exception for their modified versions; it is + * their choice whether to do so. The GNU General Public License gives + * permission to release a modified version without this exception; this + * exception also makes it possible to release a modified version which + * carries forward this exception. + * + * $Id: reduced.cpp,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#include "portab.h" +#include "global.h" +#include "reduced.h" + +/*---------------------------------------------------------------------------- + * Upsampling (1/3/3/1) filter + *--------------------------------------------------------------------------*/ + +#define ADD(dst,src) (dst) = CLIP((dst)+(src), 0, 255) + +inline void Filter_31(byte *Dst1, byte *Dst2, const int *Src1, const int *Src2){ + + /* Src[] is assumed to be >=0. So we can use ">>2" instead of "/2" */ + int a = (3*Src1[0]+ Src2[0]+2) >> 2; + int b = ( Src1[0]+3*Src2[0]+2) >> 2; + Dst1[0] = CLIP(a, 0, 255); + Dst2[0] = CLIP(b, 0, 255); +} + +//---------------------------- + +inline void Filter_9331(byte *Dst1, byte *Dst2, const int *Src1, const int *Src2){ + + /* Src[] is assumed to be >=0. So we can use ">>4" instead of "/16" */ + int a = (9*Src1[0]+ 3*Src1[1]+ 3*Src2[0] + 1*Src2[1] + 8) >> 4; + int b = (3*Src1[0]+ 9*Src1[1]+ 1*Src2[0] + 3*Src2[1] + 8) >> 4; + int c = (3*Src1[0]+ 1*Src1[1]+ 9*Src2[0] + 3*Src2[1] + 8) >> 4; + int d = (1*Src1[0]+ 3*Src1[1]+ 3*Src2[0] + 9*Src2[1] + 8) >> 4; + Dst1[0] = CLIP(a, 0, 255); + Dst1[1] = CLIP(b, 0, 255); + Dst2[0] = CLIP(c, 0, 255); + Dst2[1] = CLIP(d, 0, 255); +} + +//---------------------------- + +void copy_upsampled_8x8_16to8(byte *Dst, const int *Src, int BpS){ + int x, y; + + Dst[0] = CLIP(Src[0], 0, 255); + for(x=0; x<7; ++x) + Filter_31(Dst+2*x+1, Dst+2*x+2, Src+x, Src+x+1); + Dst[15] = CLIP(Src[7], 0, 255); + Dst += BpS; + for(y=0; y<7; ++y) { + byte *const Dst2 = Dst + BpS; + Filter_31(Dst, Dst2, Src, Src+8); + for(x=0; x<7; ++x) + Filter_9331(Dst+2*x+1, Dst2+2*x+1, Src+x, Src+x+8); + Filter_31(Dst+15, Dst2+15, Src+7, Src+7+8); + Src += 8; + Dst += 2*BpS; + } + Dst[0] = CLIP(Src[0], 0, 255); + for(x=0; x<7; ++x) Filter_31(Dst+2*x+1, Dst+2*x+2, Src+x, Src+x+1); + Dst[15] = CLIP(Src[7], 0, 255); +} + +//---------------------------- + +inline void Filter_Add_31(byte *Dst1, byte *Dst2, const int *Src1, const int *Src2){ + + /* Here, we must use "/4", since Src[] is in [-256, 255] */ + int a = (3*Src1[0]+ Src2[0] + 2) / 4; + int b = ( Src1[0]+3*Src2[0] + 2) / 4; + ADD(Dst1[0], a); + ADD(Dst2[0], b); +} + +//---------------------------- + +inline void Filter_Add_9331(byte *Dst1, byte *Dst2, const int *Src1, const int *Src2){ + + int a = (9*Src1[0]+ 3*Src1[1]+ 3*Src2[0] + 1*Src2[1] + 8) / 16; + int b = (3*Src1[0]+ 9*Src1[1]+ 1*Src2[0] + 3*Src2[1] + 8) / 16; + int c = (3*Src1[0]+ 1*Src1[1]+ 9*Src2[0] + 3*Src2[1] + 8) / 16; + int d = (1*Src1[0]+ 3*Src1[1]+ 3*Src2[0] + 9*Src2[1] + 8) / 16; + ADD(Dst1[0], a); + ADD(Dst1[1], b); + ADD(Dst2[0], c); + ADD(Dst2[1], d); +} + +//---------------------------- + +void add_upsampled_8x8_16to8(byte *Dst, const int *Src, const int BpS){ + + int x, y; + + ADD(Dst[0], Src[0]); + for(x=0; x<7; ++x) + Filter_Add_31(Dst+2*x+1, Dst+2*x+2, Src+x, Src+x+1); + ADD(Dst[15], Src[7]); + Dst += BpS; + for(y=0; y<7; ++y) { + byte *const Dst2 = Dst + BpS; + Filter_Add_31(Dst, Dst2, Src, Src+8); + for(x=0; x<7; ++x) + Filter_Add_9331(Dst+2*x+1, Dst2+2*x+1, Src+x, Src+x+8); + Filter_Add_31(Dst+15, Dst2+15, Src+7, Src+7+8); + Src += 8; + Dst += 2*BpS; + } + ADD(Dst[0], Src[0]); + for(x=0; x<7; ++x) Filter_Add_31(Dst+2*x+1, Dst+2*x+2, Src+x, Src+x+1); + ADD(Dst[15], Src[7]); +} +#undef ADD + +/*---------------------------------------------------------------------------- + * horizontal and vertical deblocking + *--------------------------------------------------------------------------*/ + +void hfilter_31(byte *Src1, byte *Src2, int Nb_Blks){ + + Nb_Blks *= 8; + while(Nb_Blks-->0) { + byte a = ( 3*Src1[0] + 1*Src2[0] + 2 ) >> 2; + byte b = ( 1*Src1[0] + 3*Src2[0] + 2 ) >> 2; + *Src1++ = a; + *Src2++ = b; + } +} + +//---------------------------- + +void vfilter_31(byte *Src1, byte *Src2, const int BpS, int Nb_Blks){ + Nb_Blks *= 8; + while(Nb_Blks-->0) { + byte a = ( 3*Src1[0] + 1*Src2[0] + 2 ) >> 2; + byte b = ( 1*Src1[0] + 3*Src2[0] + 2 ) >> 2; + *Src1 = a; + *Src2 = b; + Src1 += BpS; + Src2 += BpS; + } +} + +//---------------------------- diff --git a/modules/xvid_dec/xvid_wce/reduced.h b/modules/xvid_dec/xvid_wce/reduced.h new file mode 100644 index 0000000..34a1716 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/reduced.h @@ -0,0 +1,54 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Reduced VOP header - + * + * Copyright(C) 2002 Pascal Massimino <skal@planet-d.net> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: reduced.h,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _REDUCED_H_ +#define _REDUCED_H_ + +#include "portab.h" + +/* decoding */ +typedef void (COPY_UPSAMPLED_8X8_16TO8)(byte *Dst, const int *Src, int BpS); +typedef void (ADD_UPSAMPLED_8X8_16TO8)(byte *Dst, const int *Src, int BpS); + +/* deblocking: Note: "Nb"_Blks is the number of 8-pixels blocks to process */ +typedef void HFILTER_31(byte *Src1, byte *Src2, int Nb_Blks); +typedef void VFILTER_31(byte *Src1, byte *Src2, int BpS, int Nb_Blks); + +/* encoding: WARNING! These read 1 pixel outside of the input 16x16 block! */ +typedef void FILTER_18X18_TO_8X8(int *Dst, const byte *Src, int BpS); +typedef void FILTER_DIFF_18X18_TO_8X8(int *Dst, const byte *Src, int BpS); + + +extern COPY_UPSAMPLED_8X8_16TO8 copy_upsampled_8x8_16to8; +extern ADD_UPSAMPLED_8X8_16TO8 add_upsampled_8x8_16to8; + +extern VFILTER_31 vfilter_31; + +extern HFILTER_31 hfilter_31; + +/* rrv motion vector scale-up */ +#define RRV_MV_SCALEUP(a) ( (a)>0 ? 2*(a)-1 : (a)<0 ? 2*(a)+1 : (a) ) + +#endif /* _REDUCED_H_ */ diff --git a/modules/xvid_dec/xvid_wce/vlc_codes.h b/modules/xvid_dec/xvid_wce/vlc_codes.h new file mode 100644 index 0000000..9f159c5 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/vlc_codes.h @@ -0,0 +1,50 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Variable Length Code header - + * + * Copyright(C) 2002 Michael Militzer <isibaar@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: vlc_codes.h,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ +#ifndef _VLC_CODES_H_ +#define _VLC_CODES_H_ + +#include "portab.h" + +#define VLC_ERROR (-1) + +#define ESCAPE 3 +#define ESCAPE1 6 +#define ESCAPE2 14 +#define ESCAPE3 15 + +struct VLC{ + int code; + byte len; +}; + +struct EVENT{ + byte last; + byte run; + char level; +}; + + + +#endif /* _VLC_CODES_H */ diff --git a/modules/xvid_dec/xvid_wce/xvid.cpp b/modules/xvid_dec/xvid_wce/xvid.cpp new file mode 100644 index 0000000..8724fa3 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/xvid.cpp @@ -0,0 +1,185 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - Native API implementation - + * + * Copyright(C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: xvid.cpp,v 1.1.1.1 2005/07/13 14:36:16 jeanlf Exp $ + * + ****************************************************************************/ + +#include "xvid.h" +#include "decoder.h" +#include "interpolate8x8.h" +#include "reduced.h" +#include "mem_transfer.h" +#include "quant.h" + +#ifdef _DEBUG +dword xvid_debug = 0; +#endif + +/***************************************************************************** + * XviD Init Entry point + * + * Well this function initialize all internal function pointers according + * to the CPU features forced by the library client or autodetected (depending + * on the XVID_CPU_FORCE flag). It also initializes vlc coding tables and all + * image colorspace transformation tables. + * + * Returned value : XVID_ERR_OK + * + API_VERSION in the input XVID_INIT_PARAM structure + * + core build " " " " " + * + ****************************************************************************/ + + +static int xvid_gbl_init(xvid_gbl_init_t * init){ + //unsigned int cpu_flags; + + if (XVID_VERSION_MAJOR(init->version) != 1) + return XVID_ERR_VERSION; + +#if defined(_DEBUG) + xvid_debug = init->debug; +#endif + + return 0; +} + +/***************************************************************************** + * XviD Global Entry point + * + * Well this function initialize all internal function pointers according + * to the CPU features forced by the library client or autodetected (depending + * on the XVID_CPU_FORCE flag). It also initializes vlc coding tables and all + * image colorspace transformation tables. + * + ****************************************************************************/ + +int xvid_global(void *handle, int opt, void *param1, void *param2){ + + switch(opt){ + case XVID_GBL_INIT : + return xvid_gbl_init((xvid_gbl_init_t*)param1); + /* + case XVID_GBL_INFO : + return xvid_gbl_info((xvid_gbl_info_t*)param1); + case XVID_GBL_CONVERT: + return xvid_gbl_convert((xvid_gbl_convert_t*)param1); + */ + + default : + return XVID_ERR_FAIL; + } +} + +/***************************************************************************** + * XviD Native decoder entry point + * + * This function is just a wrapper to all the option cases. + * + * Returned values : XVID_ERR_FAIL when opt is invalid + * else returns the wrapped function result + * + ****************************************************************************/ +int xvid_decore(void *handle, int opt, void *param1, void *param2){ + + switch (opt) { + case XVID_DEC_CREATE: + return decoder_create((xvid_dec_create_t *) param1); + + case XVID_DEC_DESTROY: + delete (S_decoder*)handle; + return 0; + + case XVID_DEC_DECODE: + return ((S_decoder*)handle)->Decode((xvid_dec_frame_t *) param1, (xvid_dec_stats_t*) param2); + + default: + return XVID_ERR_FAIL; + } +} + +//---------------------------- + +extern const dword scan_tables[3][64]; +const dword scan_tables[3][64] = { + /* zig_zag_scan */ + { 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63}, + + /* horizontal_scan */ + { 0, 1, 2, 3, 8, 9, 16, 17, + 10, 11, 4, 5, 6, 7, 15, 14, + 13, 12, 19, 18, 24, 25, 32, 33, + 26, 27, 20, 21, 22, 23, 28, 29, + 30, 31, 34, 35, 40, 41, 48, 49, + 42, 43, 36, 37, 38, 39, 44, 45, + 46, 47, 50, 51, 56, 57, 58, 59, + 52, 53, 54, 55, 60, 61, 62, 63}, + + /* vertical_scan */ + { 0, 8, 16, 24, 1, 9, 2, 10, + 17, 25, 32, 40, 48, 56, 57, 49, + 41, 33, 26, 18, 3, 11, 4, 12, + 19, 27, 34, 42, 50, 58, 35, 43, + 51, 59, 20, 28, 5, 13, 6, 14, + 21, 29, 36, 44, 52, 60, 37, 45, + 53, 61, 22, 30, 7, 15, 23, 31, + 38, 46, 54, 62, 39, 47, 55, 63} +}; + +//---------------------------- + +#ifdef WIN32 +# include <windows.h> +# include <stdio.h> + +#ifdef _DEBUG +# define snprintf _snprintf +# define vsnprintf _vsnprintf + +void DPRINTF(int level, char *fmt, int p){ + if(xvid_debug & level){ + //va_list args; + char buf[DPRINTF_BUF_SZ]; + //va_start(args, fmt); + //vsprintf(buf, fmt, args); + sprintf(buf, fmt, p); + OutputDebugString(buf); + //fprintf(stderr, "%s", buf); + } +} +#endif + +#elif defined _WINDOWS + +inline void DPRINTF(int level, char *fmt, int p){ +} + +#endif + +//---------------------------- + diff --git a/modules/xvid_dec/xvid_wce/xvid.h b/modules/xvid_dec/xvid_wce/xvid.h new file mode 100644 index 0000000..4f00a22 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/xvid.h @@ -0,0 +1,502 @@ +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - XviD Main header file - + * + * Copyright(C) 2001-2003 Peter Ross <pross@xvid.org> + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY ; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: xvid.h,v 1.2 2006/12/13 15:12:27 jeanlf Exp $ + * + ****************************************************************************/ + +#ifndef _XVID_H_ +#define _XVID_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "Rules.h" + + +#ifdef PROFILE +#include <Profiler.h> +#define PROF_S(n) prof.MarkS(n) +#define PROF_E(n) prof.MarkE(n) + +enum{ + PROF_TICK, + PROF_CONV, + PROF_UPD, + PROF_DECODE, + PROF_DRAWTXT, + PROF_FRM_I, + PROF_FRM_P, + PROF_BINT_MBI, + PROF_FRM_B, + PROF_0, + PROF_1, + PROF_2, + PROF_3, + PROF_4, + PROF_5, + PROF_6, + PROF_7, +}; + +#else +#define PROF_S(n) +#define PROF_E(n) +#endif + +#ifdef __MARM__ +//#define USE_ARM_ASM +#endif + +/***************************************************************************** + * versioning + ****************************************************************************/ + +/* versioning + version takes the form "$major.$minor.$patch" + $patch is incremented when there is no api change + $minor is incremented when the api is changed, but remains backwards compatible + $major is incremented when the api is changed significantly + + when initialising an xvid structure, you must always zero it, and set the version field. + memset(&struct,0,sizeof(struct)); + struct.version = XVID_VERSION; + + XVID_UNSTABLE is defined only during development. + */ + +#define XVID_MAKE_VERSION(a,b,c) ((((a)&0xff)<<16) | (((b)&0xff)<<8) | ((c)&0xff)) +#define XVID_VERSION_MAJOR(a) ((char)(((a)>>16) & 0xff)) +#define XVID_VERSION_MINOR(a) ((char)(((a)>> 8) & 0xff)) +#define XVID_VERSION_PATCH(a) ((char)(((a)>> 0) & 0xff)) + +#define XVID_MAKE_API(a,b) ((((a)&0xff)<<16) | (((b)&0xff)<<0)) +#define XVID_API_MAJOR(a) (((a)>>16) & 0xff) +#define XVID_API_MINOR(a) (((a)>> 0) & 0xff) + +#define XVID_VERSION XVID_MAKE_VERSION(1,0,-127) +#define XVID_API XVID_MAKE_API(4, 0) + +#define EDGE_SIZE 16 + +#define XVID_UNSTABLE + +/* Bitstream Version + * this will be writen into the bitstream to allow easy detection of xvid + * encoder bugs in the decoder, without this it might not possible to + * automatically distinquish between a file which has been encoded with an + * old & buggy XVID from a file which has been encoded with a bugfree version + * see the infamous interlacing bug ... + * + * this MUST be increased if an encoder bug is fixed, increasing it too often + * doesnt hurt but not increasing it could cause difficulty for decoders in the + * future + */ +#define XVID_BS_VERSION "0023" + + +/***************************************************************************** + * error codes + ****************************************************************************/ + + /* all functions return values <0 indicate error */ + +#define XVID_ERR_FAIL -1 /* general fault */ +#define XVID_ERR_MEMORY -2 /* memory allocation error */ +#define XVID_ERR_FORMAT -3 /* file format error */ +#define XVID_ERR_VERSION -4 /* structure version not supported */ +#define XVID_ERR_END -5 /* encoder only; end of stream reached */ + + + +/***************************************************************************** + * xvid_image_t + ****************************************************************************/ + +/* colorspace values */ + +//#define XVID_CSP_USER (1<< 0) /* 4:2:0 planar */ +//#define XVID_CSP_I420 (1<< 1) /* 4:2:0 packed(planar win32) */ +//#define XVID_CSP_YV12 (1<< 2) /* 4:2:0 packed(planar win32) */ +//#define XVID_CSP_YUY2 (1<< 3) /* 4:2:2 packed */ +//#define XVID_CSP_UYVY (1<< 4) /* 4:2:2 packed */ +//#define XVID_CSP_YVYU (1<< 5) /* 4:2:2 packed */ +//#define XVID_CSP_BGRA (1<< 6) /* 32-bit bgra packed */ +//#define XVID_CSP_ABGR (1<< 7) /* 32-bit abgr packed */ +//#define XVID_CSP_RGBA (1<< 8) /* 32-bit rgba packed */ +//#define XVID_CSP_BGR (1<< 9) /* 24-bit bgr packed */ +//#define XVID_CSP_RGB555 (1<<10) /* 16-bit rgb555 packed */ +//#define XVID_CSP_RGB565 (1<<11) /* 16-bit rgb565 packed */ +//#define XVID_CSP_SLICE (1<<12) /* decoder only: 4:2:0 planar, per slice rendering */ +//#define XVID_CSP_INTERNAL (1<<13) /* decoder only: 4:2:0 planar, returns ptrs to internal buffers */ +//#define XVID_CSP_NULL (1<<14) /* decoder only: dont output anything */ +//#define XVID_CSP_VFLIP (1<<31) /* vertical flip mask */ + +/* xvid_image_t + for non-planar colorspaces use only plane[0] and stride[0] + four plane reserved for alpha*/ +/* +typedef struct { + int csp; // [in] colorspace; or with XVID_CSP_VFLIP to perform vertical flip + void *plane; // [in] image plane ptrs + int stride; // [in] image stride; "bytes per row" +} xvid_image_t; +*/ + +/* video-object-sequence profiles */ +#define XVID_PROFILE_S_L0 0x08 /* simple */ +#define XVID_PROFILE_S_L1 0x01 +#define XVID_PROFILE_S_L2 0x02 +#define XVID_PROFILE_S_L3 0x03 +#define XVID_PROFILE_ARTS_L1 0x91 /* advanced realtime simple */ +#define XVID_PROFILE_ARTS_L2 0x92 +#define XVID_PROFILE_ARTS_L3 0x93 +#define XVID_PROFILE_ARTS_L4 0x94 +#define XVID_PROFILE_AS_L0 0xf0 /* advanced simple */ +#define XVID_PROFILE_AS_L1 0xf1 +#define XVID_PROFILE_AS_L2 0xf2 +#define XVID_PROFILE_AS_L3 0xf3 +#define XVID_PROFILE_AS_L4 0xf4 + +/* aspect ratios */ +#define XVID_PAR_11_VGA 1 /* 1:1 vga (square), default if AR is not precised (ie: ==0) */ +#define XVID_PAR_43_PAL 2 /* 4:3 pal (12:11 625-line) */ +#define XVID_PAR_43_NTSC 3 /* 4:3 ntsc (10:11 525-line) */ +#define XVID_PAR_169_PAL 4 /* 16:9 pal (16:11 625-line) */ +#define XVID_PAR_169_NTSC 5 /* 16:9 ntsc (40:33 525-line) */ +#define XVID_PAR_EXT 15 /* extended par; use par_width, par_height */ + +/* frame type flags */ +#define XVID_TYPE_VOL -1 /* decoder only: vol was decoded */ +#define XVID_TYPE_NOTHING 0 /* decoder only (encoder stats): nothing was decoded/encoded */ +#define XVID_TYPE_AUTO 0 /* encoder: automatically determine coding type */ +#define XVID_TYPE_IVOP 1 /* intra frame */ +#define XVID_TYPE_PVOP 2 /* predicted frame */ +#define XVID_TYPE_BVOP 3 /* bidirectionally encoded */ +#define XVID_TYPE_SVOP 4 /* predicted+sprite frame */ + + +/***************************************************************************** + * xvid_global() + ****************************************************************************/ + +/* cpu_flags definitions (make sure to sync this with cpuid.asm for ia32) */ + +//#define XVID_CPU_FORCE (1<<31) /* force passed cpu flags */ +//#define XVID_CPU_ASM (1<< 7) /* native assembly */ +/* ARCH_IS_IA32 */ +//#define XVID_CPU_MMX (1<< 0) /* mmx : pentiumMMX,k6 */ +//#define XVID_CPU_MMXEXT (1<< 1) /* mmx-ext : pentium2, athlon */ +//#define XVID_CPU_SSE (1<< 2) /* sse : pentium3, athlonXP */ +//#define XVID_CPU_SSE2 (1<< 3) /* sse2 : pentium4, athlon64 */ +//#define XVID_CPU_3DNOW (1<< 4) /* 3dnow : k6-2 */ +//#define XVID_CPU_3DNOWEXT (1<< 5) /* 3dnow-ext : athlon */ +//#define XVID_CPU_TSC (1<< 6) /* tsc : Pentium */ +/* ARCH_IS_PPC */ +//#define XVID_CPU_ALTIVEC (1<< 0) /* altivec */ + + +#define XVID_DEBUG_ERROR (1<< 0) +#define XVID_DEBUG_STARTCODE (1<< 1) +#define XVID_DEBUG_HEADER (1<< 2) +#define XVID_DEBUG_TIMECODE (1<< 3) +#define XVID_DEBUG_MB (1<< 4) +#define XVID_DEBUG_COEFF (1<< 5) +#define XVID_DEBUG_MV (1<< 6) +#define XVID_DEBUG_RC (1<< 7) +#define XVID_DEBUG_DEBUG (1<<31) + +/* XVID_GBL_INIT param1 */ +typedef struct { + int version; + unsigned int cpu_flags; /* [in:opt] zero = autodetect cpu; XVID_CPU_FORCE|{cpu features} = force cpu features */ + int debug; /* [in:opt] debug level */ +} xvid_gbl_init_t; + + +/* XVID_GBL_INFO param1 */ +#if 0 +typedef struct { + int version; + int actual_version; // [out] returns the actual xvidcore version + const char * build; // [out] if !null, points to description of this xvid core build + unsigned int cpu_flags; // [out] detected cpu features + int num_threads; // [out] detected number of cpus/threads +} xvid_gbl_info_t; +#endif + + +#define XVID_GBL_INIT 0 /* initialize xvidcore; must be called before using xvid_decore, or xvid_encore) */ +//#define XVID_GBL_INFO 1 /* return some info about xvidcore, and the host computer */ +//#define XVID_GBL_CONVERT 2 /* colorspace conversion utility */ + +int xvid_global(void *handle, int opt, void *param1, void *param2); + + +class C_xvid_image{ +public: + byte *y; + byte *u; + byte *v; + + C_xvid_image(): + y(NULL), + u(NULL), + v(NULL) + {} +}; + +void * InitCodec(dword sx, dword sy, dword fcc); +void CloseCodec(void *handle); +//int DecodeFrame(void *handle, const void *buf, dword sz_in, byte **y, byte **u, byte **v, dword *pitch); +int DecodeFrame(void *handle, const void *buf, dword sz_in, byte *&y, byte *&u, byte *&v, dword &pitch); + +/***************************************************************************** + * xvid_decore() + ****************************************************************************/ + +#define XVID_DEC_CREATE 0 /* create decore instance; return 0 on success */ +#define XVID_DEC_DESTROY 1 /* destroy decore instance: return 0 on success */ +#define XVID_DEC_DECODE 2 /* decode a frame: returns number of bytes consumed >= 0 */ + +int xvid_decore(void *handle, int opt, void *param1, void *param2); + +/* XVID_DEC_CREATE param 1 + image width & height may be specified here when the dimensions are + known in advance. */ +typedef struct { + int version; + int width; /* [in:opt] image width */ + int height; /* [in:opt] image width */ + void *handle; /* [out] decore context handle */ +#ifdef PROFILE + C_profiler *prof; +#endif +} xvid_dec_create_t; + + +/* XVID_DEC_DECODE param1 */ +/* general flags */ +#define XVID_LOWDELAY (1<<0) /* lowdelay mode */ +#define XVID_DISCONTINUITY (1<<1) /* indicates break in stream */ + +typedef struct { + int version; + int general; /* [in:opt] general flags */ + const void *bitstream; /* [in] bitstream (read from)*/ + int length; /* [in] bitstream length */ + //xvid_image_t output; /* [in] output image (written to) */ + const C_xvid_image *img_out; +} xvid_dec_frame_t; + + +/* XVID_DEC_DECODE param2 :: optional */ +typedef struct +{ + int version; + + int type; /* [out] output data type */ + union { + struct { /* type>0 {XVID_TYPE_IVOP,XVID_TYPE_PVOP,XVID_TYPE_BVOP,XVID_TYPE_SVOP} */ + int general; /* [out] flags */ + int time_base; /* [out] time base */ + int time_increment; /* [out] time increment */ + + /* XXX: external deblocking stuff */ + int * qscale; /* [out] pointer to quantizer table */ + int qscale_stride; /* [out] quantizer scale stride */ + + } vop; + struct { /* XVID_TYPE_VOL */ + int general; /* [out] flags */ + int width; /* [out] width */ + int height; /* [out] height */ + int par; /* [out] picture aspect ratio (refer to XVID_PAR_xxx above) */ + int par_width; /* [out] aspect ratio width */ + int par_height; /* [out] aspect ratio height */ + } vol; + } data; +} xvid_dec_stats_t; + + + +#define XVID_ZONE_QUANT (1<<0) +#define XVID_ZONE_WEIGHT (1<<1) + +typedef struct +{ + int frame; + int mode; + int increment; + int base; +} xvid_enc_zone_t; + + + +/***************************************************************************** + xvid plugin system -- internals + + xvidcore will call XVID_PLG_INFO and XVID_PLG_CREATE during XVID_ENC_CREATE + before encoding each frame xvidcore will call XVID_PLG_BEFORE + after encoding each frame xvidcore will call XVID_PLG_AFTER + xvidcore will call XVID_PLG_DESTROY during XVID_ENC_DESTROY + ****************************************************************************/ + + +#define XVID_PLG_CREATE (1<<0) +#define XVID_PLG_DESTROY (1<<1) +#define XVID_PLG_INFO (1<<2) +#define XVID_PLG_BEFORE (1<<3) +#define XVID_PLG_FRAME (1<<4) +#define XVID_PLG_AFTER (1<<5) + +/* xvid_plg_info_t.flags */ +#define XVID_REQORIGINAL (1<<0) /* plugin requires a copy of the original (uncompressed) image */ +#define XVID_REQPSNR (1<<1) /* plugin requires psnr between the uncompressed and compressed image*/ +#define XVID_REQDQUANTS (1<<2) /* plugin requires access to the dquant table */ + + +typedef struct +{ + int version; + int flags; /* [in:opt] plugin flags */ +} xvid_plg_info_t; + + +typedef struct +{ + int version; + + int num_zones; /* [out] */ + xvid_enc_zone_t * zones; /* [out] */ + + int width; /* [out] */ + int height; /* [out] */ + int mb_width; /* [out] */ + int mb_height; /* [out] */ + int fincr; /* [out] */ + int fbase; /* [out] */ + + void * param; /* [out] */ +} xvid_plg_create_t; + + +typedef struct +{ + int version; + + int num_frames; /* [out] total frame encoded */ +} xvid_plg_destroy_t; + + + +/***************************************************************************** + xvid plugin system -- external + + the application passes xvid an array of "xvid_plugin_t" at XVID_ENC_CREATE. the array + indicates the plugin function pointer and plugin-specific data. + xvidcore handles the rest. example: + + xvid_enc_create_t create; + xvid_enc_plugin_t plugins[2]; + + plugins[0].func = xvid_psnr_func; + plugins[0].param = NULL; + plugins[1].func = xvid_cbr_func; + plugins[1].param = &cbr_data; + + create.num_plugins = 2; + create.plugins = plugins; + + ****************************************************************************/ + +typedef int (xvid_plugin_func)(void * handle, int opt, void * param1, void * param2); + +typedef struct +{ + xvid_plugin_func * func; + void * param; +} xvid_enc_plugin_t; + + +xvid_plugin_func xvid_plugin_single; /* single-pass rate control */ +xvid_plugin_func xvid_plugin_2pass1; /* two-pass rate control: first pass */ +xvid_plugin_func xvid_plugin_2pass2; /* two-pass rate control: second pass */ + +xvid_plugin_func xvid_plugin_lumimasking; /* lumimasking */ + +xvid_plugin_func xvid_plugin_psnr; /* write psnr values to stdout */ +xvid_plugin_func xvid_plugin_dump; /* dump before and after yuvpgms */ + + +/* single pass rate control + * CBR and Constant quantizer modes */ +typedef struct +{ + int version; + + int bitrate; /* [in] bits per second */ + int reaction_delay_factor; /* [in] */ + int averaging_period; /* [in] */ + int buffer; /* [in] */ +} xvid_plugin_single_t; + + +typedef struct { + int version; + + char * filename; +} xvid_plugin_2pass1_t; + + +#define XVID_PAYBACK_BIAS 0 /* payback with bias */ +#define XVID_PAYBACK_PROP 1 /* payback proportionally */ + +typedef struct { + int version; + + int bitrate; /* [in] bits per second */ + char * filename; /* [in] first pass stats filename */ + + int keyframe_boost; /* [in] keyframe boost percentage: [0..100] */ + int curve_compression_high; /* [in] percentage of compression performed on the high part of the curve (above average) */ + int curve_compression_low; /* [in] percentage of compression performed on the low part of the curve (below average) */ + int overflow_control_strength;/* [in] Payback delay expressed in number of frames */ + int max_overflow_improvement; /* [in] percentage of allowed range for a frame that gets bigger because of overflow bonus */ + int max_overflow_degradation; /* [in] percentage of allowed range for a frame that gets smaller because of overflow penalty */ + + int kfreduction; /* [in] maximum bitrate reduction applied to an iframe under the kfthreshold distance limit */ + int kfthreshold; /* [in] if an iframe is closer to the next iframe than this distance, a quantity of bits + * is substracted from its bit allocation. The reduction is computed as multiples of + * kfreduction/kthreshold. It reaches kfreduction when the distance == kfthreshold, + * 0 for 1<distance<kfthreshold */ + + int container_frame_overhead; /* [in] How many bytes the controller has to compensate per frame due to container format overhead */ +} xvid_plugin_2pass2_t; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/xvid_dec/xvid_wce/xvid_ppc.asm b/modules/xvid_dec/xvid_wce/xvid_ppc.asm new file mode 100644 index 0000000..93be723 --- /dev/null +++ b/modules/xvid_dec/xvid_wce/xvid_ppc.asm @@ -0,0 +1,592 @@ + + AREA XVID,CODE,READONLY + EXPORT XVID_ClearMatrix + EXPORT XVID_MemCpy + EXPORT interpolate8x8_halfpel_hv + EXPORT interpolate8x8_halfpel_h + EXPORT interpolate8x8_halfpel_v + EXPORT transfer8x8_copy + +;---------------------------- +XVID_ClearMatrix + + mov r1, #0 + mov r2, #0 + mov r3, #0 + mov r12, #0 + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + stmia r0!, { r1, r2, r3, r12 } + + mov pc, lr + +;---------------------------- +XVID_MemCpy + + stmfd sp!, { r4 - r6 } + + subs r2, r2, #16 + blt mc_no16 +mc_loop16 + ;the order of regs in {} is not important, now it's ordered as to avoid compiler warnings + ldmia r1!, { r3 - r6 } + stmia r0!, { r3 - r6 } + subs r2, r2, #16 + bge mc_loop16 +mc_no16 + adds r2, r2, #16 + beq mc_end +mc_loop4 + ldr r3, [r1], #4 + str r3, [r0], #4 + subs r2, r2, #4 + bne mc_loop4 +mc_end + + ldmfd sp!, { r4 - r6 } + mov pc, lr + +;---------------------------- +interpolate8x8_halfpel_hv + + stmfd sp!, { r4 - r9, r14 } + + tst r3, r3 + movne r14, #1 + moveq r14, #2 + + mov r3, #8 +ihhv_loop + add r4, r1, r2 + ldrb r5, [r1, #8] + ldrb r7, [r4, #8] + add r5, r5, r7 + ;7+8 + ldrb r6, [r1, #7] + ldrb r7, [r4, #7] + add r6, r6, r7 + add r7, r5, r6 + add r7, r7, r14 + mov r8, r7, asr #2 + ;6+7 + ldrb r5, [r1, #6] + ldrb r7, [r4, #6] + add r5, r5, r7 + add r7, r5, r6 + add r7, r7, r14 + mov r7, r7, asr #2 + orr r8, r7, r8, asl #8 + ;5+6 + ldrb r6, [r1, #5] + ldrb r7, [r4, #5] + add r6, r6, r7 + add r7, r5, r6 + add r7, r7, r14 + mov r7, r7, asr #2 + orr r8, r7, r8, asl #8 + ;4+5 + ldrb r5, [r1, #4] + ldrb r7, [r4, #4] + add r5, r5, r7 + add r7, r5, r6 + add r7, r7, r14 + mov r7, r7, asr #2 + orr r8, r7, r8, asl #8 + + str r8, [r0, #4] + + ;3+4 + ldrb r6, [r1, #3] + ldrb r7, [r4, #3] + add r6, r6, r7 + add r7, r5, r6 + add r7, r7, r14 + mov r8, r7, asr #2 + ;2+3 + ldrb r5, [r1, #2] + ldrb r7, [r4, #2] + add r5, r5, r7 + add r7, r5, r6 + add r7, r7, r14 + mov r7, r7, asr #2 + orr r8, r7, r8, asl #8 + ;1+2 + ldrb r6, [r1, #1] + ldrb r7, [r4, #1] + add r6, r6, r7 + add r7, r5, r6 + add r7, r7, r14 + mov r7, r7, asr #2 + orr r8, r7, r8, asl #8 + ;0+1 + ldrb r5, [r1, #0] + ldrb r7, [r4, #0] + add r5, r5, r7 + add r7, r5, r6 + add r7, r7, r14 + mov r7, r7, asr #2 + orr r8, r7, r8, asl #8 + + str r8, [r0, #0] + + add r0, r0, r2 + add r1, r1, r2 + subs r3, r3, #1 + bne ihhv_loop + + ldmfd sp!, { r4 - r9, r14 } + mov pc, lr + +;---------------------------- +interpolate8x8_halfpel_h + + stmfd sp!, { r4 - r6 } + + ands r3, r3, #1 + beq i_hp_h_no_round + + mov r6, #8 +ihh_loop + ldrb r12, [r1, #8] + ;7+8 + ldrb r3, [r1, #7] + add r4, r12, r3 + mov r4, r4, asr #1 + ;6+7 + ldrb r12, [r1, #6] + add r5, r12, r3 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + ;5+6 + ldrb r3, [r1, #5] + add r5, r12, r3 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + ;4+5 + ldrb r12, [r1, #4] + add r5, r12, r3 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + + str r4, [r0, #4] + + ;3+4 + ldrb r3, [r1, #3] + add r4, r12, r3 + mov r4, r4, asr #1 + ;2+3 + ldrb r12, [r1, #2] + add r5, r12, r3 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + ;1+2 + ldrb r3, [r1, #1] + add r5, r12, r3 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + ;0+1 + ldrb r12, [r1, #0] + add r5, r12, r3 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + + str r4, [r0, #0] + + add r0, r0, r2 + add r1, r1, r2 + subs r6, r6, #1 + bne ihh_loop + b i_hp_h_end + +i_hp_h_no_round + + mov r6, #8 +ihh_loop1 + ldrb r12, [r1, #8] + ;7+8 + ldrb r3, [r1, #7] + add r4, r12, r3 + add r4, r4, #1 + mov r4, r4, asr #1 + ;6+7 + ldrb r12, [r1, #6] + add r5, r12, r3 + add r5, r5, #1 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + ;5+6 + ldrb r3, [r1, #5] + add r5, r12, r3 + add r5, r5, #1 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + ;4+5 + ldrb r12, [r1, #4] + add r5, r12, r3 + add r5, r5, #1 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + + str r4, [r0, #4] + + ;3+4 + ldrb r3, [r1, #3] + add r4, r12, r3 + add r4, r4, #1 + mov r4, r4, asr #1 + ;2+3 + ldrb r12, [r1, #2] + add r5, r12, r3 + add r5, r5, #1 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + ;1+2 + ldrb r3, [r1, #1] + add r5, r12, r3 + add r5, r5, #1 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + ;0+1 + ldrb r12, [r1, #0] + add r5, r12, r3 + add r5, r5, #1 + mov r5, r5, asr #1 + orr r4, r5, r4, asl #8 + + str r4, [r0, #0] + + add r0, r0, r2 + add r1, r1, r2 + subs r6, r6, #1 + bne ihh_loop1 + +i_hp_h_end + ldmfd sp!, { r4 - r6 } + mov pc, lr + +;---------------------------- +interpolate8x8_halfpel_v + + stmfd sp!, { r4 - r6 } + + ands r3, r3, #1 + beq i_hp_v_no_round + + mov r6, #8 +ihv_loop + add r5, r1, r2 + ;3 + ldrb r12, [r1, #3] + ldrb r3, [r5, #3] + add r4, r12, r3 + mov r4, r4, asr #1 + ;2 + ldrb r12, [r1, #2] + ldrb r3, [r5, #2] + add r12, r12, r3 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + ;1 + ldrb r12, [r1, #1] + ldrb r3, [r5, #1] + add r12, r12, r3 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + ;0 + ldrb r12, [r1, #0] + ldrb r3, [r5, #0] + add r12, r12, r3 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + + str r4, [r0, #0] + + ;7 + ldrb r12, [r1, #7] + ldrb r3, [r5, #7] + add r4, r12, r3 + mov r4, r4, asr #1 + ;6 + ldrb r12, [r1, #6] + ldrb r3, [r5, #6] + add r12, r12, r3 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + ;5 + ldrb r12, [r1, #5] + ldrb r3, [r5, #5] + add r12, r12, r3 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + ;4 + ldrb r12, [r1, #4] + ldrb r3, [r5, #4] + add r12, r12, r3 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + + str r4, [r0, #4] + + add r0, r0, r2 + add r1, r1, r2 + subs r6, r6, #1 + bne ihv_loop + b i_hp_v_end + +i_hp_v_no_round + mov r6, #8 +ihv_loop1 + add r5, r1, r2 + ;3 + ldrb r12, [r1, #3] + ldrb r3, [r5, #3] + add r4, r12, r3 + add r4, r4, #1 + mov r4, r4, asr #1 + ;2 + ldrb r12, [r1, #2] + ldrb r3, [r5, #2] + add r12, r12, r3 + add r12, r12, #1 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + ;1 + ldrb r12, [r1, #1] + ldrb r3, [r5, #1] + add r12, r12, r3 + add r12, r12, #1 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + ;0 + ldrb r12, [r1, #0] + ldrb r3, [r5, #0] + add r12, r12, r3 + add r12, r12, #1 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + + str r4, [r0, #0] + + ;7 + ldrb r12, [r1, #7] + ldrb r3, [r5, #7] + add r4, r12, r3 + add r4, r4, #1 + mov r4, r4, asr #1 + ;6 + ldrb r12, [r1, #6] + ldrb r3, [r5, #6] + add r12, r12, r3 + add r12, r12, #1 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + ;5 + ldrb r12, [r1, #5] + ldrb r3, [r5, #5] + add r12, r12, r3 + add r12, r12, #1 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + ;4 + ldrb r12, [r1, #4] + ldrb r3, [r5, #4] + add r12, r12, r3 + add r12, r12, #1 + mov r12, r12, asr #1 + orr r4, r12, r4, asl #8 + + str r4, [r0, #4] + + add r0, r0, r2 + add r1, r1, r2 + subs r6, r6, #1 + bne ihv_loop1 +i_hp_v_end + ldmfd sp!, { r4 - r6 } + mov pc, lr + +;---------------------------- +transfer8x8_copy + + orr r12, r1, r2 + tst r12, #3 + bne tc_no_dw + ;dword version +;tc_dw_loop + ldmia r1, { r12, r3 } + stmia r0, { r12, r3 } + add r0, r0, r2 + add r1, r1, r2 + + ldmia r1, { r12, r3 } + stmia r0, { r12, r3 } + add r0, r0, r2 + add r1, r1, r2 + + ldmia r1, { r12, r3 } + stmia r0, { r12, r3 } + add r0, r0, r2 + add r1, r1, r2 + + ldmia r1, { r12, r3 } + stmia r0, { r12, r3 } + add r0, r0, r2 + add r1, r1, r2 + + ldmia r1, { r12, r3 } + stmia r0, { r12, r3 } + add r0, r0, r2 + add r1, r1, r2 + + ldmia r1, { r12, r3 } + stmia r0, { r12, r3 } + add r0, r0, r2 + add r1, r1, r2 + + ldmia r1, { r12, r3 } + stmia r0, { r12, r3 } + add r0, r0, r2 + add r1, r1, r2 + + ldmia r1, { r12, r3 } + stmia r0, { r12, r3 } + + mov pc, lr + +tc_no_dw + tst r12, #1 + bne tc_no_w + ;word version +;tc_w_loop + ldrh r12, [r1, #0] + strh r12, [r0, #0] + ldrh r12, [r1, #2] + strh r12, [r0, #2] + ldrh r12, [r1, #4] + strh r12, [r0, #4] + ldrh r12, [r1, #6] + strh r12, [r0, #6] + add r0, r0, r2 + add r1, r1, r2 + + ldrh r12, [r1, #0] + strh r12, [r0, #0] + ldrh r12, [r1, #2] + strh r12, [r0, #2] + ldrh r12, [r1, #4] + strh r12, [r0, #4] + ldrh r12, [r1, #6] + strh r12, [r0, #6] + add r0, r0, r2 + add r1, r1, r2 + + ldrh r12, [r1, #0] + strh r12, [r0, #0] + ldrh r12, [r1, #2] + strh r12, [r0, #2] + ldrh r12, [r1, #4] + strh r12, [r0, #4] + ldrh r12, [r1, #6] + strh r12, [r0, #6] + add r0, r0, r2 + add r1, r1, r2 + + ldrh r12, [r1, #0] + strh r12, [r0, #0] + ldrh r12, [r1, #2] + strh r12, [r0, #2] + ldrh r12, [r1, #4] + strh r12, [r0, #4] + ldrh r12, [r1, #6] + strh r12, [r0, #6] + add r0, r0, r2 + add r1, r1, r2 + + ldrh r12, [r1, #0] + strh r12, [r0, #0] + ldrh r12, [r1, #2] + strh r12, [r0, #2] + ldrh r12, [r1, #4] + strh r12, [r0, #4] + ldrh r12, [r1, #6] + strh r12, [r0, #6] + add r0, r0, r2 + add r1, r1, r2 + + ldrh r12, [r1, #0] + strh r12, [r0, #0] + ldrh r12, [r1, #2] + strh r12, [r0, #2] + ldrh r12, [r1, #4] + strh r12, [r0, #4] + ldrh r12, [r1, #6] + strh r12, [r0, #6] + add r0, r0, r2 + add r1, r1, r2 + + ldrh r12, [r1, #0] + strh r12, [r0, #0] + ldrh r12, [r1, #2] + strh r12, [r0, #2] + ldrh r12, [r1, #4] + strh r12, [r0, #4] + ldrh r12, [r1, #6] + strh r12, [r0, #6] + add r0, r0, r2 + add r1, r1, r2 + + ldrh r12, [r1, #0] + strh r12, [r0, #0] + ldrh r12, [r1, #2] + strh r12, [r0, #2] + ldrh r12, [r1, #4] + strh r12, [r0, #4] + ldrh r12, [r1, #6] + strh r12, [r0, #6] + + mov pc, lr + +tc_no_w + mov r3, #8 +tc_b_loop + ldrb r12, [r1, #0] + strb r12, [r0, #0] + ldrb r12, [r1, #1] + strb r12, [r0, #1] + ldrb r12, [r1, #2] + strb r12, [r0, #2] + ldrb r12, [r1, #3] + strb r12, [r0, #3] + ldrb r12, [r1, #4] + strb r12, [r0, #4] + ldrb r12, [r1, #5] + strb r12, [r0, #5] + ldrb r12, [r1, #6] + strb r12, [r0, #6] + ldrb r12, [r1, #7] + strb r12, [r0, #7] + add r0, r0, r2 + add r1, r1, r2 + + subs r3, r3, #1 + bne tc_b_loop + + mov pc, lr + +;---------------------------- + + END \ No newline at end of file diff --git a/regression_tests/_mozilla_ie_action.html b/regression_tests/_mozilla_ie_action.html new file mode 100644 index 0000000..a83f654 --- /dev/null +++ b/regression_tests/_mozilla_ie_action.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta content="text/html; charset=ISO-8859-1" + http-equiv="content-type"> + <title> + + + + + + +Your browser doesn't support GPAC ... + + +
    + + + + + +
    + + + + + +
    + + + + + + + diff --git a/regression_tests/_mozilla_ie_simple.html b/regression_tests/_mozilla_ie_simple.html new file mode 100644 index 0000000..e880aa9 --- /dev/null +++ b/regression_tests/_mozilla_ie_simple.html @@ -0,0 +1,22 @@ + + + + + + + + + + +Your browser doesn't support GPAC ... + + +
    + + + + + + + diff --git a/regression_tests/_ppc_action.html b/regression_tests/_ppc_action.html new file mode 100644 index 0000000..d7f92cd --- /dev/null +++ b/regression_tests/_ppc_action.html @@ -0,0 +1,35 @@ + + + + + + + + + + + +Your browser doesn't support GPAC ... + + +
    + + + + + +
    + + + + + +
    + + + + + + + diff --git a/regression_tests/_ppc_simple.html b/regression_tests/_ppc_simple.html new file mode 100644 index 0000000..bbf8121 --- /dev/null +++ b/regression_tests/_ppc_simple.html @@ -0,0 +1,25 @@ + + + + + + + + + + +Your browser doesn't support GPAC ... + + +
    + + + + + + + diff --git a/regression_tests/auxiliary_files/count_arabic.mp3 b/regression_tests/auxiliary_files/count_arabic.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..12fbb2a419adeaf60304979abc2bddd1aa801625 GIT binary patch literal 59872 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 B62kxh literal 0 HcmV?d00001 diff --git a/regression_tests/auxiliary_files/count_english.mp3 b/regression_tests/auxiliary_files/count_english.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..8f7db4e2f1de26248aeaa620e6a642fee435075a GIT binary patch literal 60060 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 literal 0 HcmV?d00001 diff --git a/regression_tests/auxiliary_files/count_french.mp3 b/regression_tests/auxiliary_files/count_french.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f320621bf3d67227dd3992759556edaab69c0105 GIT binary patch literal 60060 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

    The suite contains tests for:

    +
      +
    • all BIFS nodes supported by the GPAC renderer
    • +
    • all BIFS commands, extended BIFS commands and OD Commands
    • +
    • Stream Management (MediaControl, MediaSegments and segmentDescriptor usage)
    • +
    • Most BIFS Advanced Text and Graphics nodes
    • +
    + +

    Test sequences are provided in the BT format, and need MP4Box to be encoded or translated to XMT-A. +
    The GPAC Regression Tests are part of the
    GPAC source code and are also available on GPAC CVS.

    + +
    + +
    + + + + + +

    +
    +
      + +
    +
    +
    + + +

    +
    +
      + +
    +
    +
    + + + +
  1. + + + + +
  2. +
    +
    + diff --git a/regression_tests/auxiliary_files/index2sh.xslt b/regression_tests/auxiliary_files/index2sh.xslt new file mode 100644 index 0000000..eaea85c --- /dev/null +++ b/regression_tests/auxiliary_files/index2sh.xslt @@ -0,0 +1,71 @@ + + + + + + + +#!/bin/sh +#xslt path: + + + + + + +if [ ! -e .mp4 ]; then MP4Box -mp4 . +fi + +MP4Client -bmp - .mp4 -2d + + +echo "Creating HTML for " +if [ ! -e .html ]; then +xsltproc -o .html --stringparam filename --stringparam next --stringparam previous --stringparam snapshot /svg2html.xslt .svg +fi +#echo "done." + + + +if [ ! -e .mp4 ]; then +MP4Box -mp4 . +fi +if [ ! -e .xmt ]; then +MP4Box -xmt . +fi + +MP4Client -bmp - .mp4 -3d -2d + + +echo "Creating HTML for " +if [ ! -e .html ]; then +xsltproc --stringparam filename --stringparam use3d true --stringparam next --stringparam previous --stringparam snapshot -o .html /xmt2html.xslt .xmt +fi +#echo "done." + + + +if [ ! -e .x3d ]; then +MP4Box -x3d . +fi + +echo "Creating HTML for " +if [ ! -e .html ]; then +xsltproc --stringparam filename --stringparam use3d true --stringparam next --stringparam previous -o .html /x3d2html.xslt .x3d +#echo "done." +fi + + + + + diff --git a/regression_tests/auxiliary_files/logo.jpg b/regression_tests/auxiliary_files/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bad06d875c4e2a81f30e1314f902975391b9faab GIT binary patch literal 13781 zcmbVycT^M6*XGQI6@L$e&2iRTkpSDZq`gD$xJ5a=AM1d-shYxoM8YqeH}d=fa)9-@P+aK z&Uyj%^FS03MNM@LILA&!%}#Z;1jGVV|L1Y;+&QZMY*go{DNpC9si`SP)YSiZMNN74 zzsG+a|L2VVr*Sq&RRRE1wEy#Kfd4fpcc7uAqd(8U$ixPmqoUl6hMJa^hK6!x1m!b; zhMo4}Rr%X=mrNhiUkkW=<7ImBd7(R>`#8)d@xltu&!QO^Ik~ubctu3T#3dvZm6TOf zZ>nkEy{DslUr*oM!qUq6p^dGJtJ@QI4^OY>fkD9`p<&@Mv9IFdUneAHyv@wY&UyDf zx1_YJyrQzIx~8!S(cIG7*51)SFo+r&{xUK;H9a#sH@~om#^Tn0Y;6AA+TJ1j{&RSA z{1^OpLIqI&_pJO+gZ?*r*eUj$qoJXuq5qFPROdn{8#Ox(?NxcYi?>bb9|v5zcH`yw z%XiX?Kld>RDVX6ooS#iHatbSAMF{^f>Ax)czcVQM|I4EPHt4_YIYR@?)KrvZL(L9A z0Ww**` z8lVbeag41_8FH?@Qy{76Ia+S}HZ#+OF%XkqE}^a?pKRwlB9rzhNz6EhQLeP|@yk?I zBE9}U4Oa1^4hH{(%n4brr z7&7Mib;LwmIM>iYio&h)t+R>{>f2Zd(<L$MnzTw-(Iv4*F1^A+kaCaUU zCby4P^T_LL^pfz%9-o7@$RV3zwp#lT;@8y)>KBZ8^d*(FKIcwXa+%sFVhtAiW!h>{ zdZ?!@dT=*EfiocHQ>4JMgWYZ18L%*c<4kk1(VC7Zf$4n@N+>oRa-OaaTr!oQ{@i9% zP%1W`XE>JsPju!6l;8V(pH*c-eu{?D|p4+ugdAwXy*A^ z*BBn>57z$(OE4SI4c0G=oD{x(r+#ef@)^)UcMAv{ZWX94O?0H*4Y>LeXL_Z-<7u|g zmWn7HoqQ@>UjiH$=6Q;ho)F4uOvy7JXseZ~3k$QgRs5J|HSIJQISwhQkSIk)3D?;a7u#n-2muG-r7t|X!5d-0j6oi@KNk4Aa z9XXy0?bIeH)qm&6l`u-zs92nS7MC*1qc=b+#IZX%h5%kTIk|AJznrE{Eu1CmIvyf95_aM!!a<9@Y387$}x=l<#v?65>Zfn}8ySP- zC3CX@IY8c+g#Z=q0A;=z3aYrL#;cT=IVA$`6id!SR`9l zpOb6Dunz++)D`OC&PcaXtJG^*Y761%bvk9{u^(ni#hXi$Qd4Z_TC$rF&~JN@j;W&o zZTYPVeU@XNLEhixwy(mUmD^_B*RnCM)(=Q7R#l{2Q^{H9s+K5TXLPGV?%ypSc>U$e z>C~8)=v?l7dPa4Z=}+7ObU;HA)b9*fvct~rv7X)tT|bTiV}z~g@2AQz_4aVtSV^4$ zeBc>i9sQHcj%4zz9#GKt@fFL;gh;L#w`XD>dr=@S(9@#UN=z(2+vP$n&ohz3_Twh`|$fFVBFut^&2o8qV8cH@d zyaFj5=hqX_E0-k)+n1j`aO8}XC0dZ`$V!wcNC0bQ}u)_re`x2>yJ_IC*}%nr2wF6VGl zS66FV)L{y(Zsxu-l~tHT+B=bo;2|oU$cEa#Cj8u{Pw3zHSLWX{+0oS$8YB|x^eIK+ z=C^I5kDmznyEDF7B5JVKe@N^^v_=TQZpaqeU-Z(Em3W)LG@QFU;$7k4z%})A=7>Qr zUC2A(-Y2HV=Un}ApQI{sFOQ)jF>CO?Mr4a$0@4kch-4>P5|RcAX+c5-$Y(;}>ZklQ zt31<7gVWx&s|7&M84w$S>bdS&9CI&wQ{25Z;pw$5DMbQjAiEwvazSiQ;S8YJC*>lY zWaE{|OrRK+2@c+G@Y&BBLS8yrkU7u@TQ4wDZ5auw*%{>g+`{bo8*%Ew&sGa?ERP5m z1-%~TPiE<54w%Ms579=Z_w}&+xG5m{uxls(c|LB`Jo7+O?yap3o2P_$r?-^oTzX<7 z9JO};3_wG14T-A6f%q=DAxr%HK)A+?+>C#s;^&1#iQhF@yLW!YR`Q{z5Bh?--C}E z=C`n0kGK1?7)^`y8As(}T`p&j=x9_6XEfT%$aq73>bSh$wCNG8P*)tb`Wlesx?F>k z9B8Tefn}!`h3}t0|$C#$|;&u<@dok3Dz?u-`p<^n2xGOH}TkcL3ytJM&_DJT}tIzVE zm}N_%RC}ob78d&3tH?<|@&zx-^02fee}$fFmR<70&BtZ4ra}-7FKOck7J>|J<;F*m z8M?pn#xi;G*9eZT4=SJ+9KM29$|pY2*HH>#KjBjtah`^>uuI` zcmbTlBmCf1m%&HkO|T9t;N82bir<*t?pTZOGAMuKaWbA47(;t~PuBF21fR{LbM@sm zOtliw-DDvXd7>(YMfGsKMN{7KeG%T|3;;#a_OKLCW5z!??$P*ON4!dSq5G*VsTaaa zyy-?GD2+dw&$dMmKa}5dyyWR~MO!3g(uvNNv-OQHG(8|bW|5T*H9uO+pQD)AC3e1T zDl=o!NZt^oy#LKmiF2h`N*G=Y*Ev9|;D%$`a(N(v)^#{XU^d{@Q!P>dLbX>?EIC6#X6^Q9pQATKEn?ze`PA~Sp@=nM=|!1TzIXoyFeBV#FGrBLT+UqlB^ z+<)^=Mb?jB81A&=ud^@GQhY8tV}Sep^$!Z#vM2In6tcy=55821$%!Yc!YfZUX<4v4B13V`dJA@Fc#PQAYCCXPud!}tO?Kk)1UVVf+9@A1|W43ujyJFH7hyk{(YdW#U6S^dxkw&C2yZuJg8ejhI&zNTJSvDq4~ZU39Fzv!9YDJ$bSczrH|c6t6qoH}e_pXOyfmc$LyA_aD6>?e z&rGVz>^#PaMLtT6YFeQ7q$fo`sQOMFXQjS>a;b>7Dk9&uCEIJxTc_pw89*if?tzhg z(%=TTtcZX2mE~HXw^=n8!&I%2QU3P0j2KV9X+Wl}q2g>-oEFD<0N^@s0_3NofL`iK z4ssA2?^v`Heg+()NX?a?5QgNjO1wnMX$WFv*qWO&=bBbRCMcYDBFrb0v0u#bV_MR! zxbp)dqxdg+Phq0-lyYTsa^zI@S|it<_UZk|?-%u<2qF`H$s)erpKHvCTNn{OJ-q~b;}bGH9K5f;T7do_BXS4PAz6CGIn-; z zEha3{gwXyotWZ$tSv_r^dHqZ&*ZKKD$@EyX*Vv*-seQGGM~}7Zvdw-GgBCM*y8ajC z7Y}V}?IX!F8wHV}f}99p&8RX2z*#?dF|t}4!Hz}J(Kf_R5tA9+Wq-1zlX8P6$2!-nEb z6)5_*MTrV7XMl@TApT&Gl4~^$p)C}=3dLuYSYJVDQOkTApYX@=*C2wSjo)5hgSM=W zaK61%Y-Ncje>g9p;d%zx-`9HN!>HyIu!LXdj})ja>T|f1YC5p1ftof}94GyHQYUU= zSf!&fC+YLNBBP5#B{FvP?JqCyRAFC+ps;dCpfWag9k} zAB)6$ZtElGoJ#3mAYPKc0RDhr6brCD-CC|0xwQ>4v-Uc<2&gfddXYM*Mu_$Q$rvayhh zTI>d=1~7haO?q>@E1V6+t^WK|7T9$3BJwZDhael$Lon)VbbxV=wvZDDu;nXYx@15D=va0D+j`!w>XR z6lx{_9AX0bgV)PtIFy_BvPaRu`l|^xHZmXSH;suLVq}Ijj2|fOzPDBnGX7$c;iF{T zu|5qNx_IN^Z#2$72YRkY3v8FJjepQUTUC83FQ2)RQgVOufo6w05#5Yr0CW0J){-JQ zh}x6BFz9*%wYP0$989}-twd(-eVxbrjRnU=c{3|Z>y+aWz8h*;uF~~oXF$E2i3*)& zcxrDzl>x^Vd6ypnt+2NK-9BShfA(b;L_p{2dy5t zvm=ABOrIma`bz)~o>*Nb>NbSw%T?t6yrN21kdNzZ{U;v{K8sBa6?VA^%AjtHgCVCX z9KwY6O2wDao#moaRx35n%~GZ|l120-=jJ<)b3oQOeCv=J%<~Pl;5r}S;-xwfZlt?0 zL|<`3LX2A{UnqHCyx`$I6}7A?+)k%hN5hg7DUu&7x;5BZQ>&rz$6i zfX}QiN?=OgD`&nXzgG1~`PE%wq@zX7+oLon)qPoKbs3r%Z;2MqqOo2Wb>H%G>?VFdhowo=3@asmos=ZfB@=&(dkI;1IuLeYGdjCQ4 zOO5djE^^vY*n*mAfNiCi#EibYRg;&AJs%=mIOkY*@6%|t|Ie3^k^u$x`wL(?VghCy z78Y)dil4woV$u~kip30OgNCwklFv-7pBaC$4@A(|QTNf1Pb6PX*FYtu8toF1}LyQIhCl-3+r#d36k5gRY z#HYAR?=7U4IbACe2PCT)0l>+n+(yLWc@%KCr;4FEVI-IIKlZ@*FTezObIarUCtpbh z*y$9gR^3rM1EifZPSfx?8spCeuZ=%J@#cnr&YdarcJ+-L(tW?|MGP!c z{r-f3A((w@)XBy{qK0{um%~+pAhPO_7kaa^ZuQYlcuzz1+ShrN+|+V7Y(i#lT2H8R zOiEqeS?{LOR9O6$?EXzknyyc=rV!qDTPbOGA{mbGOVLol?EZVdN4^ZZ(%A#L<9p{zp4h9`eF}Kz#!?j~Y9b_sX4CF8^3yVAYf3nf=$i5J%oIyMJXQ zAkgNA@ShQ-ldJd_SgrgK?@hZakHm;gz0}kIpt?>C1n)b-w;kz;@`7<6os%ZmqpE}) zjTWc{3!u^_^nXy0z6LIsMu(wwU+&+8E`jQ$w(K<%{7_T%mdd0dFEKSo?a`Z#M$9wr zfWoX3Mv(D zSrc#3M0-1gUoIW;8ZrF#nQpaBd~z1kwcZ~hk+rfb0;)hjs+?xqcB&NdffhT_3!FdY zgazW=_gPOhzyUM7>|g`n^?fJ&NwL$x@ddL7%lPvUhu-mk{@_7{Yaz1`OQ& zg`-bKF;y4syx4v|P5+u66`AqLzA~Aas+aP!g4Zkgt$#QYxgZ$(qG4lDG-RKrqPy&p zx8sFF0qDBet6ze%?(L1UWIo4xAD7`z2=7EXC{EC-_5hEyh02c=w^&MsJjwaa>y$Uqn^kR-87p zv7OWXr)Ez(+r^!Fr%3chjH)WjhgZWbtUuQ{$mwvrwqJ728Nf?Xp28O)xV z!s#M34OAaEuILhZH|i(gr&KWQ?@!*17V?wIiLjP{R$`~E*XkuaEhbv2_k7_qhx>rQ z6W?ZHDdxvUHi7JyWv(}sGbf=bDZy6)EogHQeC24<}PjGQWmMz8fF5tVyLTweC?Iz@Q$+6CWD|I+j6V1rI_ z*8UJV3a(G?fnv60V?rF{Nw2kdh|bgdu5Ec3zoBnD%`W_52CU3P-RVmFx7A|BcV9g^ zvWjBsqIySW-c|t6!B=s$A?{@!4p*nvd`q$4bd6`93JWD;ZAp8%py$KWJG@el zQ&M!mKM-@41e7#f+VvgQVh0i@rgJmiw{Q=Amj*pDd^9wQ~KSfKUyi z>xx^V<0c-$!W^qS~ei{dNr})^Sp2)9Gc>Roe!0+F3a=H%$3s>aPAG$hQ8k^d> zI&=99>wPOjv8^pFtvVT=6%nzHjG$pf^)7{-4TCa4FtR>DF^?|W#7PSe-3 zf3M-_`wwWr486=^=+o}+d$}Y5>vPIBbN(h#x4Ex4cWILu3ka7?K}!k`Um>-3g_V3g z4sVG$l_q-H^Zw?N`}VljtA}PzQB(PZ{&wbzup@3!GxEhX45!%HvhDEA~}+UzNHA09>f4 zc@BpKJg9NT32GL8KVNcV??c3UM>GPyGqcD+`R9Ly5P9=XpLjVo6%j8xz!ytxW?frA zzf)T<{B$5-yYY+FsBFS2%XCB6JuDMAef4UFok2wS#0$W<@OAt3cA_rf$0#}EfzzST zvdbp`*4C$~?qtQf=S`qH1F$OlYo}L;x>FS|UNq2?r;*f0A@ZGfo=lg5Y1l78-dsUI z$s=367t}7`^@q>%P3TXbNL4?Ix|6B~$kB5Tb8knQpNUEm_qyW} z@F6&v{lQAF*Hci2oy5AguK-RXT=(q#^EE*4J%K2GK$V(5;5$nexJU_2+u$D`JcYp& z5hjItQl|%@TyO7!LRdS0tb0#4VPi7Q&ftMlT+J|v&$(~_b4ZyfaR0F8FS=$Mn*N4Wp3*dz$hF-%nY>614pAUs#VYv8z`jVo-M z)Cll)M6@RF@2|Y|`o@`#IiS+9)dJkCy3(KBxKS3YL{zlDu`S4ae}P6p=ON2G3rnUY z>Wl%%-f_d>f2t?6M4>KFkK&fCq&(cL%)P=o0`CNBjZ3ze0nuEU)@4`grNz}I{7Tww z@*4hYdoLwc0$(h}{u%U*1}R+E`qp&6tlInprR!~%t!LJ_WT}>J`!qXAn;Zr8q*I{o zgWfe}X`^JVL%8C%7))JFwT}5vwVp_s10S_$UTe2_ebwpn<84xY-~@X9_!g-c?jB+4 zY)*h;3p>j^Ld=GRKW{t8O`B=Ldpo`--l&uKJQtWkhu1mJ{Pa$K93=~rhF)0M>5!R! z4Wq}skU$i%g~Cl|h?;fV*4`V-+vwGVFy(@(1w#mxAkTiq>(nq~O4jB`-NYw%i=UZl zNK-vBiyQ&P`ycG7M4U>I=dFsG>7x-=&7l0L(b`B5Jix_wtOE327fE?nG#D;2ty3b! zIqS2(X3?7b^bZYhT<_47Gt`QZ7FS+?N4A#I?QYySP8{98T{UDVRsL&K?yXg@X=kHh z!=HoWr=inv_{w2h9xYFs$G;sk`v&Nh#Kst$#s74$WoMK2CEF5BjZBQpldTmu&LbBf z{}HBpTL={0^flx&JV`cP!x8UjgCrmaCJ;s?_IB9#q=u_6NZ037oTo<0y!NX)IT%aU z27^6nO3*rf=mt;dlx3E4lSIe2r3fa$8GL?%>Fj=Xrvh{|IBK^U4)1 zVD=tU{ARMg9gyz|-Jvkkq0JaL=OHKWX57jSDp><WmfdxMJv;a?W%I_g;QCy_hE1?_8>70Mds?Q>4AU+d%L5}yrwDq%5Hm8 z)x3q{l5(M6w3piTf7MGkmrLB#SD|=mLwxHPSNa!V4`7J_0_H29zOY^-GtJ-i<{GC1 zFiuX)6gUL}iQSI4M`0|cf7Av>ODj^sJG5H zR^Z>bpZ?2 zoC}5WTcuUQ+?a5ukUGBw7woTMLecRlDK(O3+oXGtEJLc)VtDvST0ShzuhfoG?H!mu zIXcKz==S+2eZ&_BP_@h)0`u`;kv+5UzajZ)A*NKV1HSMe?$hqhGr*t-?+M+zS51r{ zbp=hJ*9O%TGl;6B%r4FFlk24c)JXq$(KwsW>QQ7X6okR827J@YZCJ#=LJW zGg{JM#oQ`il_>k?RP8d)&wXA|$-|-zk?jLf&375%%Pvk^%^+&OsaS&~ z!o|_k0VFN#;vZe%pH_v?CgXKOuW`|fy5-waSKhyiy=1^|wRJlkE&C^>$a>E2x`y`p z0k2o9+I&)%W%Z8QuckULGX~isdvD7^Z<4NANBSq`j4|P4{|^@y0N_KU6h#Z;Gx^eJ z+b(z!wpih0eqRS=g;d?B{}tc2pPi zt58CvS&5fwegsX8d8W0LQ7ZLH!_8`KQu$-7p+&OykbrR%nPYbi!8J4y>)3Pd;$;%lM_5f4a@xZu%7tq8o+KHx_-`I7o{W0hf-Cz?UVL zu*NiSPnbF={1+d2U`5E9VHZn=nN|*8VNz6cLsUDztw}#lx#N`F;4a>nr5QzCBP2Ns3NYS2fMGn!D^0a{1sgZbz>jk(U%*Hk<2#C6$r2u*8RiEPM#7 zvw#sNer2YA^%5t}V>~qZ{32JXq*^}=(Wwq|a}UX< zJ+&T%v^4Cc;OCE5NcoXhBFEP*`w01Pp^h1@CwwfEM5o2V>?=6i@bgKEVX7Y2rR$aX zg=F7fZo0EnntRXhqi1rv38htx`UA$F-p^Q{6dkxdQz`j8I_qZNkg2C4ZZ_YxIoDgK z?g`>EIS{)$*Kbvt*gu=i#~xS9o4I4|d#O^*NB>f6sXNad)1>6Ad|N}?FK2-0S{f42 zk|qzX3BvqNRbYXW*A#~Zq@h9Dq?_O~a zK=hh#P>OXg;*_Fc^$kEiYbBj(M-KK#-S$9p+7Q)cJ9ord8O!o6RBHzfZQCb z^>mN}9dX~_y%4spoAsfxL#u4FzK4)Yq1JT$VJ1%})HAM7@m)XVteV z#E-fVLqx9Ea&Eni$ghSCzcRnQ34-jvUa-{bt}7KpZ4BctOaH@dqx>djP338eT(8Rn z(`4a)?(~eK@?PblxJu~eumM58rs|IzKn<+phxu9ZojVq~9_9gmfFU(^$aP9mPQwzT zDOCqHoL+?4fi58-&oPioM0HYe7wg65Z$-(GETIVp6}(=ehwTLVC=ET9JRFp+&Ax`< zrS?dwo5wk0!$KX_EmN8N@lFz@`7&eK$z^;p=zM0CDlPXQC34EWVAUtRWl9?(m>AP1 zm%fg%HY&JpY}hk4g_g;f^nmB{AkPOFv&vZOYh_WS>Q52u1# z^mWHZ>%APppW{=Igk@cvJ`TpG7ULRtLhDb?H~394#K#l)K1kPeR3^dfs;|96q^OO` z{MKmXkooL#>lc9{&*@ibDN!H^?`yp$f`KgB^`Q&%2(s(@Ew7$8jA48@J>Xbbuz+CT z7!BP&NYH|2%s`3Pa?Oot*tCzM>tz&cz3trI7l$}*!m*VUd94rr*}eaQ-7$;;GUhP4 zA`E95&LFgod?j+)kz+g!H-YxUMNADklP7!HrmYNR`}{vVkz26UgT2YJPQX5`A}-Az zT`;gC(0yKCONbPk?Q&Z;4F6?l@Ms=(uX@=FJzg2Uqo7GT`lFb^R+C7#Cs|VyYiR<7 zth4ewdysvp-_`I*dom&^(Kk~d!)WB*w{q7EFDac3Hwykv`jx^dy{WQ;UoH7hQM^GK znwZgx=CwwpmCQk*L#w!Fo8_@FDH{@_N?vW{jDc@6s#zbBKEOR#lg@zVpF_|~oD}Fdm5oZ&)tM2B{!6lO5{%rh5;n=Y_DJ@c*f=Bn@ zp0bQEdeGA)!VmkdqIFJ7EEKJ7lbV8oP}z*9?}Rs##yJ&pZ(p;s?R}`&v^dg^qodS zh1gW#;Gl*>56bMnfcI(jT!8nflg}>@#Xx&+Qf-5m=)5_YM7U&vh-PI2{|*KWY;=>h z(*h99v0c?ZuJzlDH-XNs2&f{KH(2&(`Qzu zi2>2=q=du#6YEg#Lu>p`vdGUZlJ*7b*VS%ti&{IDx8+gEIIQGsCFkP1I&z`lUGk1l8rktSBzZ~|RRStr{r&FCY|5v7;%1a|&x}{o3*BzyZP7ZTZm;g8T zut3|1#VA|;BQXwfC3nuZhQZ!Xa;N5+5lyzdrghdAtO{V7dZu+c`X-seSn*NirsR_8 zBr7Y=in?Ml#j9HBiG0nckH}*=C-WZK%v^QqsUGDMPgJ^Yi)+Dmv^zJcks{2D_2PQRts*#*_GL|N&}0dF%MrMt0~H)ms(ZJ&DU5%n2@JX4dDp!Dksj{{ zi&VSX=+9NX=d0`(>BlGyF9Njiz#=XvW?d0eb=p31hqJ#B>P{1QqeK z=_B%_j4{-wu}I(xjY0L;o1~OB^`7f!TSBnM!s+ zi3^CL3npj%d*w)@+DeBW{ezM;xL;{zf-q~e3h-SL&YK!@{Rj_qgE!M}{TsB9FiwSu zS8MZbmy7XpNRH@j?Mqrd=978)PdYn6hV@4+nSG!Ait;?~%x~Kt3bxIIzi;XP=I;M$ zoy<%T7^8DpqYq=Frj;XFNLWPUM~WMrsuLCG@+e5;=>9PLPpV(RWq4~_7{`Q4NCOt3)ucZ z(eT3%>{pE%ylm{O)Fyjc=eOw6I})`bo<<*wU9%JV%DwGeKtufOd{WQV-D5#U&=gY# zAJP(;6*h=~-oj@mMbvw>nyJ4Y&ho58Nx!&9q4kUr3cD#D3zl27dQOc=baQ2-+N)bkHpKVrQUiPA?8&5eShfFCPuYGVEk_!{UMyL&#Aw%De=^aG`r5`lEkB;hzSo!+v7LUtI$2+lr zODly4q`n)b5E#b_|JImC@^40!2nb{|!E{!w_OEBdFZ=eoj}`xXN>kE_+UrI8VuS&d z7e}KnH>H0KyQpKb(K`x#l+W8-YAY8jxB4q*U`WX#BcM(ojfPYJeIomEt zj6ij-lT&!W{t&=V(F)#`Sqe2&00Q*bC^}Vs>)p)i{uEX7QbY4ss#QZ0;zN?kdO8xq z8TunK7xhPk)5)RE0jl|K;b512818duVttV0On#->+qYenH}%@}%|f0%^J%R*5ie=J I0XWdLnuaxNe;I3Q-}c5*HtGB7YVATcvKGc-CiHXti7F)%Qfg45gp000Mc zNliru*8&_2C^exJuAKk?010qNS#tmY9>D+r9>D>_X;f1H000DMK}|sb0I`n?{9y$E z069WQL_t(|+U^o8+d46hbP2&;mh<5NV@;4Z(2`D=K1FELg@thYuZ5 z25<^~h!ybz5r;tpMJW=xfj~$|@4emJ-p{{wxI5W7Ik%+{eSX*TtmmA2b92gi-@W$Q zZEcGBU4EC}<#+jA{y!DYP(Q$6NZ!t!LaM1rSX59T>d4CzG!_*Jb@lZ;^fxi+n3XHz zMIw=s;&Mfl(`j+qY`V)|e_hpHS7$?i7X$`60!NLqg`}rjQ^$?7@**RxNjKeO8S3{M z4EfCyfTn5Ag>Sx5)n{j`EBEhLHx(AjqNhz8Ay+DsEP8#sv8gE*z>hX}bOcZi2M1o* ziQbz_Q6jna9LPbijc<}WvH(oX>4u|<2W2V zJ``jq@*Gs`k#b!xi;v6~r(NnKj*f1%I=Eb?I4G!99vPWWbGdoi#>OjQV}lb?F(@KD>$7x`5a;HR}~r(XW};}!b4y77F6V;IkDmIJ^X(N*mP z9vuY?W%B?e2}ngkGn85SnI3>t6d78t9_daph(ty^pWjFY2Ifs%x$@uFzx?unp|+zZ z`Bf2M=gyrx%eHMYjzBP4S5@_5dw%|8zTF-mqG`Sao)tytNHOpv0k9X~Gjt$507(KX zLopFdSI=n^1c*QcjU+DaVnjy9`lh0y?4$(?3|VW|*oWHAr2J|KaPYb3LYoT;t~0l^ zJlgonFVln$hY&y)fOdi|rLKZ4rh0-eBoq^X1f2xlNp%BEvlxOy{0~V4CK;FnAiMx& zb{&nbb5OgP%e5#+j?9rnMQw{3Gv-j%-~LuJ)b=Lddr0NfXP+rA{o@~_;X#%*oj-rK zz+zD#uqA{t2|$tnD+x9NjI6hv#L-s*@Q5S!CS0l~DVQX10p5cECPIxoo-H&Z<13R^ z`{9l6ypt2VU_tv(+keOiaQN_HzWG1@Nv_GxzFk*QafhR=ElK2Xh-C0g2y7+^FcC2L zNV%?SAy9CgOhoEk+8+Xt)ro*O3JBw&Yg(wDBrq}5=u!iRW8(w{R>@*xcd5pV`8s*k zs@zedMp=gnhan-r{*4<&hLb1%So!0R_t1L%c)rP`LcoJ|0*{Uoik$>}*EJA;xa1)v zBXee*O@fb=O}GptOFve!#es<^Bn*k}#|Az+8eHdaq%A_Bfer|`s2)3ZM@&}M_UWrv z4~aJz5&~?SF=IW{^Go=8eH4JrMSwGvts1}pTUZqTjr0OYb)cwW-VCbcaJot;`VFq@ z5zx4Lk@BL71guU<0B@4h(+<=dbxkncq z8F9qG+o^8%iH{h3WIW_En?NssUIr}(MSxRWE?|81)zA^2Rjhd0D8Vc zLFEJoQ~3!AR5pNpS+DP`9uV*(_?QgL@0q`GrA&p|4xQjqu2#l}+c3l>lFoZ_$Uyt94H(W5jqNHS;ys62i= z^5-|-TxV@+x=&;@1_0^`8Bpm!Z>T4L$0TS5&@L&J^r_HLQ(0_mm3G94?b67| z&%z=j&zF{#ra?)OvVl&5&pf$KYCL?8vew=cZqmi4zTTejm;d;9(lu{gh7DwQ|c zY%-z6B0xjc7<{ZqaO^<#Ul7Wn?RKHVYE3g}G$Zt*M_>N*`0+s-VB+`x61}D9#~(+0 z{ii?u4+ML+O5tTt>1L|JrRV{0ovwEQj{~I=nT*O&t1WGzp_-_1<34Pzt=&6q>eP~# zUw*mu@ZrPq#>U3T6DLkA10eV$uq1G7)c^~Sok#5Q(Pz@#vwa7|bB-N5_UMWgE7ne) zJh?M^-??+A>Z=_)c8nZ9e$JWP+y^6yi=z|tdNJ8YfLkXxZgA6W&Eo=zbd8Pk^FH_> zr11OimnYAiTQC?{4R{PtbM$ER|J{A}-vRvj3Wr0aasiFqJXX2u=44%{bsd}deCjN? z`hwtKbKLmxyEP`$+wYVd^RF5>wkQ7r(&^K zo0^(B?bNALmzdYuyLYd2|2N-6huQ59wjMjSG_|%iIKpNV;y8zlfzWr@`)R43-VePj zG+mvRprF%>ckjM8dgjdHUo8QUyF2pMTd^0mY*}ZmuD)I7a0mk7`b^;Q%iE~#I$rNO zwsN^N)bq~M(a{D@Oia#oQ>ShQ;16VEWEkDu_qpeuTd`%!mW>XFL+GxqCqefm2xJQY zp}=>Y!}V?R=g(i8nVI=FxWCDhecyWHjZr1Xj=db7n>#1Iy*(h#CQwUv#!^G)(C~v$&e8H`H>@YgAxDBi_`v4rzCl4jrBbPM zTuVz!0szDD6#TKp0={|);JA9I?tAq&9J|F5S%2k9bkVV6=k^p8wG9+*1L6TTPMW0Tw6;EO zsjq)lY&5Dd;LK=WOn{FG0PLm!LF!ysn6)D*>2Ugl34dR^cCF82=x7KTT<)BsM~`mN zXf)A?1in-QY~s%K6W`G?nXEBAJ$?1N@4owmzkT?heDX;~&ZbRIN0pV$AEwdB18JI% zJpi^5Y9*Kn8zI%WR4Wh|tU*Da*a8C9zBoWl#D05#*JsS&M@S_1cN7<|66*B`d@dQ` zli;Hl!zCUaQG}lx5Maw0F{0Iykg((CMT^$0TD2R$NU-_D#ZE!A;(yjV)F59HBw zXMx8(lRTB8I8a{7Z_;2l9E!wjf;DC%Jk{~dhD^sO8fE*r%#_Y&zd!>y`!Te9Rk7_ zrWar%g0F3V5{H0*fC7m4-`%)z<0b!|uv>QS*`uAAm33Ym9o=X$nPQzC9pQW+0j|{1 zkxzA#fH7N%-FEQ8K7xWhYuePe)7pD-*0Yiz8$IpKIsWMU)D{hjt|W~ zCj^_>n>TO%EsuPg_SDWp`5tP0c!4%O+4K0M@<|n)y-(^_J0ZsbCxD2pM7BE z%EzC7{`vn76!=I`Ebzbs5BxJQFtE^5*RVH_z>E)|mC*v#)4G8K9|^p>bI}72C&ag2hx~W4jw#MIeq%{;QIRdDX53g9jKnV zei`R9MEzet>EAFI&x>TsmMxlfAAfu@@8rpGVAE(66Oc2MaJMmYQi~k|USo4}gvH^g z{-&s?PjQLg9$^3K)gkp~&McyJx>PwatV?!mpo^j&J{{H7!9$%72(VYCq~wpAG2@Mr zk`lineShWYr=Q*$9Uc7(>Z@n#3-IIx$lroEA|fIR7c5xt_rZK#B-0lxC{MWSu9r1& zaVJ{1TnDzD&Sgt3VPir$LX#=7`Q*uE|6IH{xi9ziivXxztyoQX$7#T6G(J0E-vneUDs%aPDVzCts^=4Crf(z7F}p)wT>Wx-ep?} z)m`9R$`PB*0j8RorC+XCG11?9`b+?{_7<;>C-vd0v2{xw-Zava>H-+PgQ24}rD-+&tH{ z(iwLWp{K=j4O~ZFGqeh-3JQ+>(B59Q#>ad4>;brP`BFm>p{rLj@97$ zb5l~TOr1V`69iF%GM4R4z=eZ1O+OeL8@m@kwR)N%gv9loJv21*=NoRgVOMHus;-}p z&}T_WNog6edi8p}T3y7LMA~U*fKM>bntj~Oag3UJbK8Of5DqXV=mUf=Xfa;ez*dq(qK(ZJ^#Q14}3V(Mnv++BadW5EbuKv^#-;8u*(=+ zt_^;7_`dt@+c(tDjOES`KKRHsa^z{Ss{<1NDY7{m9!iTv+;ri>bG}~RD*-Cbor@^> z;fFg#PN$FwJW^yna_Li@P)J=44>t&dgI~|g%)F)nlsn1F%F;}kGUXE}-T5fGXX_G1 zUA{u0uo0S&@85LOP0d67%vd5KA`En7e#Z0y$o9;2lbvL+Sd>5i{qK+aa&3Z-Tm&Fv01nl2-hQ=MOt;3wG}Ial|6a3Zjb^9~N6L~VOB_(@w?O@V)MByFR;!h= z*=(I^x7%qT$i+*SF1-!j;#akPAIY-Ko3#y%joS^Op>;;Il8EZIIl`b6I%P zN51|#)!Vf_62N)r&?JdioG7Ac4%^ZvykrZxBR#m(N`=D6PfXkcF0^c@jm9RZ2lCNJ zAAOV0=QjZfoS+mC!6^_3TBlB(`j-O-4h-2UU2l^4=RX%JV`Kl%2(1)yl@!8&1sjf- zrlYd|{qM!zuIrHidAoK^qK(D`5&Vu^nvL6`7UDsg1Oi7(WMoYThjZw`2Os>^)S*lP za^{U3IdT(}dJ`f59Ieny{1k|Q2B!vX0Y-mi%915*ZBFN5Yfw-X*qp`%4`G0?V6IW* zSu9dVPR`gLe)vJ<>AG$SaC-A*wWYN+jbk**$&4KMMyNBNT&Gtrm0DZW>L0Ja{`#Du zHkKE0noOpTA-Fn&2oM+;mJ9bI(~?BsyZ^a2(`1NrVM6dy3TQ zL{sxp3U}?g&eJvB5}^3I@6sVE&J>~GCrIjC%EjYR?V+J9>V$;sl`B^^ z4Yi@X$^7~An?piEzKDs5q2T!4Ew|iqEe3n~EjK;&R5JuCXB`SfJ6;uym&PMZ7&vig zy*{Sl;6aal0&WRVapXv>xw$z35kN@wT>nl{J=f-I`FuxJP*A}gcieGysEy={K%f_P z+ikbeOP4OC;F)P?BC1iqJJiqRi;TMW-k&HQ?=nh6MPh)2iId1|76)^=so#C~U4*;q zvA*=h?b`!StXr3=&&v}qlQH4dyLhP+azzbFrNt2uaUnZ9yHArk2U=c#{dNAnefxw^ z!c`L|PQ1RburRZzs3;XQxvi~DnwpvlaUfp=f`Y(R&=PnInt^tuQfW&{N-9cDPR<8C z+uGWC42aPTWU)a2`Kt&JC$Csha(3OiBCbw1#ca0=u_r)t1(-NNmB2O8bjGi0-Fg7lNulq>#7w1iy4{lY_AoTI7))JFnS##I07U0dFLgETN)?~) z+%aX!q4z)fXvv5XBd&QMg%*bV{PWMrr%#`rOw;s20PA|aUavx^Ln-HY8vDn>oCNCX z80sX5>)Z+PkTE4C0$yQAh|t{JtoS-M_E3<~I1w=6V2qF#(7SMH za$!`*74F>(!pZ^@Kt*Q#dYu3obNJz*$c@X_O6331` zGZf%&-@ZNmg%@5}2&H;qZEbC)*=&{r=x7QzD!pCmjsWC>P`vgJgHMtGlnW}qbm`Ju zfj}_5si`RkWdGW=YxlkK$}1PI^?O6$s@pPhWVx-NV1kvVc{n!`MfJ{_3atSi{KK@? zUw+vGA1j#G>J>;|PMtcX1b2Mr zYp=cbUQ0{M+Oo2;SqLBmJW}N5@i|thRN8f9J7-gL9jqu`Knf(0R8>_?X>4qKZOfJ| zo94}%w;ZaBz-#&bNTTP=Icn!{>|}N{hYKSzWiA|f9c^tHB}a~gv0bN^2dKMpMQqjS zQs56fW{x~u|C3p`s%0`g)RRA7s~3Ow-FFine)!?1Dk>^&h7unIC7g!V~CDm$nS1 zZ9-F0F1DUIGn>q4?xea?++zHM@SYtV9SQpUeAG(pG0m+P0i+5=5?p{!X4}STQ<*vR zJHY4~jpoW=y&#nQfH&TF;~@z8UIt(!7;Gl+Bq5ke#snmA%qE|ky1o;53^Mx8+lpfN zjri=@vo9e+02$V2W@feymVJU^VlFqa;KGKTrlDbk23oVsYxE+3&14!z{v(;jjJc75 zS*fJ{^Yzz*lFKeHFHgQ=#foL;&!2w=jH+US&qe^YbJ7zq2|$*71U3Q~{T+QqM<(E& zGKP@?|HnOd?%ZQfKKY~tjvF#EG6toI%;)inn3!M>z{Jkb+8Wlb)e3rDgM|PpmeggP zYNLAQ&zD6-Q3|!1qApw*Xz+yq{;hC)8r*3(3bxolW>y96$Uu4nOaP*d-&NW%K}g{H zDsT-s1gnp%T)9$HUS9t1!Pr!)`rB_SnVHa$;&_czhtVic~h+9vwZi7MfP8Yin!A0r*@d;G|3UM=>dI6z{Izz?bis ze>v>2re$RXcqM?`SQKL{PN4KU`Vbz!?OXv*7-yJopk-Elroovv2`1h z-;;lLcMtOK#C^Gig@suV$UgAQGtUfck0IEe3mA}|85eOTetf4>5k6{Ef_DNK400y$ z_=4n3>}X_dfC&IbOM{?kC22%Ypt=KwZQeh#$;Wivo)Sg4BZ?*@7Tm!?jTRAjnQ+shLSsv_&aEat4m8upRKE_`-!?bp?5$8UEkM< zj8>Br|0MA&n7`-mMrpn9~OCb3bVcM$qOLylp)+* zvT0A=g80k<0!$q@Zrr>9Gn&CbIn8FD0t*Lf-m>7`>kFY=u4UFI6xHoox?Bnv&T2AI zI^DpmpZxsu&sT!?31kD?UH$BCjGuJ-o(RAs1Ct2u_8st)dkB3;#vJ6|P!7X`2M<0! zU-GI#@(=amY^_#{ zk{j?mxpZ&&{<~h<4e+?#Pb%pN$a8mu@RoAcOO?TCX=xb=?|b_|;BP=405D)i{Uq>Y z%*J&%dV$r800Nn;gWN%rY+sUXOVqz?hSJ|(0zl9w_~@gL?m(k^sLk&#U!SKOzf9o0 zk-(D|a2FSlL|_ua*J{Gm3X#M6K8QNI`b&WDm>ASo-K*1w4Lk4v=*4@v>s|!li$pr+ zu9^5VlWCTwCW>n4-}=upXU-&BES6DryItsSqGu-fSlGZNfp_`t=4ZnWJb+Hq(a~#gs#nA>kjrb~pX^Q)=TW^o7|3w~ zb4?9pZtmYAAgKE@tXAs?U$S_T0Jsxo$tQur?SHo4lAgjF0hrI>E!QxU%y8Y%9Xobp z!B6`&lgh#8CzG38k{*Dm7HB$uzjtr1H51C*5Hz(3f08Dy~6)>8VCl0Lt{i4!Lz z<>logp&AMCC8Bqi<HInZX{@ZL@*M z2EH#%L*~8M2_Vj0d^FHFM&QULc@U!NJuD>FM4HARjl* zs*H*%Bj=jo?a!Q4&&`w7Rx8y|RW-St`d1 zJHS1)5`3)^NP%Xrq_wp*2>!QE`I_sF9h)NHaKy}=%`r~oJVBj6;Bas_mBWS&^G<+} zv^1mDU^r<~zb`Oz=1gf~Vq%mpzx7ntv$=c(C^HUl7x;N< zF1ia2-FX9lJOh%ju&@wl1_$``PYkI#a9|49pU+qW(B%P0?DS%>p}eD`q`A4-dlfKh z$r5W#US26J7F);*VK2}*$&esGxL&U;%gvqO?+et`)rqB2X((wBZzS*ryf1aUzl8AA zN6=3qpjpj{e6k{_I5s6n91zF_Z57$w!5z+@DwX}Y7Kbu z0&X^$@<<4@rUhaFzk39m_w5_aHJgVCP>NWpyFgL|PFT^zU3qwT@x&=pSU00)CBUR5 zOB!TBL1$1kfWgN{C)H#1DJ6h9%>Lt#8UDTmwEg*DOxD#Z-VDqZ^?L)@*AnlkKfxUd zn1D0i#T^OYJ2AvTeiNX0$BtQGykH?g0KA_7iB;!@9YUeSEEeZZpFW*+b}}mgCOq;; zL;Zydr!5=~jTeF9<=`X%I@h4nG$+Pniv4Ee#w>qc!e+B^&@Na^L$MJ7pZnwT*}!HS z3}Oa@{&W+&bN_y{7I@GM_G?VN^w6O&O>XWKzS%4%lR;ntSQvO2q*7B}TU#Cwz;_P- z&X^XarnYhd0$QxDOcdlrDXK?8A`aJPiz@o?!-f95NI*b<16*LQnX@WtxYVU&*o0T9aDXY!@~WH$Hw?c{OSC*Tcu zPt}VrL7_Jyz-NnGF87%LRVPkJnvNaIbZWIx%)Ct$0X)V@BNRH?RH~v$lO|R9O@KSz zd8b%wG+wX@1a>kP4N??c3W%GFukIf(rXxGs=h-eQmC6Q&MvF>&ScR<*-W~An`T_iD z;`tg7db)pKst9xn_c8_9?r3Qlg9;p(9+u0% z24SV=@f_OF(6VuvnZKa4`F$cl%!CO)7>&k!t1B^~nYa<{Vjq4GDFk#*jb?VD;0hu3st?UXF-}@VG&jM*_@w>7@={YHFcXBr=-`5?~b& z4}!datgS6PQX;wG@b>M&zPwawYH9}rgDvi&Xm_QZ%`uetm{7^WW#rom&Ggumo2FZ1p;uH}E#Z>#qGz z4hH$!1jOHa^8%i#1Lkj$=^*;AgZK9-LFIRwHpS>GE1wcO91_yrQWpj!CI}-fk4KkA zMiwtzxbS;#*Y*n1_?vFZca9iw+Qi}5$@HX{5JHjwpel_<6~N^#Ps`8u+Q&V0>eOnJ z$yDS^z3)rV=dJtK-=NP^$B?&%? zQW6kgk;TQGTd-h(*HybdbwXsUT)EjK5@|^@xubK|uxTY18(bTwZ|O#thXxXeg50 z#!QadsudNt?!No(`#oLvkw+fsm^5k9If+D~^#^cYK=j4s_qEl4pN(yCiKi+6ZA^9D zb=RGE;)y3brjGgX>8Ecn6cpSqwpdhXV@Ps`R-Vh|da8S?K3D12MrLFjb#S>pXFvCu z0L!;+(^XegywwpD)C4!BG5A<*E_N!Y?oEzm21BH6|NbYBuURu4Uf%6^@2juAx)a*P zWnehko4fa=RC?+JVzvU@{mt7wG5GF)cSj1S|Jz@9;e~&DFg54rx86#t`s%AyoW{nn za@Q`c*sfBldwX7TKfzkD*jW=5)n3rh@b>1-n|)5(=hsNof_L973LQQAUwWa?OfnCj zJE`uiWEEF;7U%NY+eepf-u&#hOP9Kx0Xu8ftWqcyD66x@of~DADu0@A{*c9848eRB zf2#)k+|#E|-wVw@x0%(BJ@G_v>9%c;I2#*p02}g<9dUt@x@?RKU)Mg3TrO1>9IR81 zAHUum1bZHO=%KbjBLI>cUVH7bFg|{VRiS7= zRRQUW$Q`c9hgVoEQgK_`QnA_m_|Dn0S@*C3!<0>%Htk=4=BWh$LXRFj`jgYAPqXewO27U(S4z|O*s80a7HYM@N`?)|o!Lokr1`vj&QyNqV77aTuR5s$Dd0|O8RWqu3fAf zPyEo(a7RZ@&I^LJwqeSvuD{EEGM7!Ujj?Ju9I7}Vz+R_T9}7^ccg~wP&oJl&Kyv^3 z^$o*szkQ=tE-%s%UVtP6=PK}|a-}+5sP4xfp9u~PeUR10^LrW~#pS+G~x~)z#L<#>VTxhk0mA5jGQ%Vi$Eaw{OHl6ecG+1&jf%Q(tD2`%bh%PW&~YT zmCkoMh0JBjJQfmg5DDydnZCSye4tXPm>(H=;hVa;uFMtt_U*HQp{nZY>H?aYnnr>V z1>T;82|63VoJU%~Ge0kGWQa2z!S2T!Fmnm9g9 zbIIg{crBsWbtE!aacG-OY-w*F306`ppE?M;cpfhG z_(+1UB}jn4FH|aRH7O~@V`t7>Q`gXN8hPR&CBT|BYp93*_{S!lP}pi}Z%+df1fzI> znP<=)2{;ai$gbBX8ER`sl{YkSca)TL?cSZApItd6Zt%QZnZq)oFNatNmwb2NlZvW<5{1HAmXuVX zOikU$6$rjrw{Bg(x3cRu0g!AzaKJHp_UsCWL}IdOG@~iKUd3?*g`}X*5`qLoRtv7p zUH+#v6{Mu3 zoJvSYc<%e}zdwxcS-N*`OlEQMQ~C=Rp5Qb#jt4i-C+m8wXoL^-?vbS)oudi;l@f{V zaza8iCqDjP5Lte@Wy_ZSXXhFy0wDR}c+wHHt^;YTT?b|KI#l0&lyH|QgK)}MhO`HB^EiPUpHX0)W zt_FJ?1h|swF8v660|CC4IIj~3Xke9|j*0nStHZHz_wL;T0)8(84wS6;!yjG>Id$ss zFpVYxxjBXFMaj0U+;wwuP)SP>y^36}T{ddePdv4HZRpssc~drQXghxVxMJdyf>*YyFpKaZ`_2t08K+Qm6 zHDDfqlyfB|$CGEx;&a>DMuR7iAy2@Tdk{}5C9_=MmIG>0m>U~o%`GjrLfazdMvJ9A zG%!&2;?}K)Z@cZbi$DOi!{LatSS$h(AT}VGCEpXEzSjRprBbO=tyUkNJ9qA1pyl$v zOQWJ>Giz(p>DJbVI`Z_|=yZv+!Jy(Iw@r1YEyMY1&;#HF4rHnmmvXsQ ztJF|YF$zeqz*JisbLa5k(Tnf7C+yib-?RsXhNeR+LDJmZ+?DU&mu8?pK_Hv^M>&$y z)6=PY?zzYD@rNJoofH)0oY~wwsXaS;v7^2HB~xDB9I4r?LS4Gp&6A2ZO7ODRm+zhdOZJ0Co24+0Mk3?(z+} z%R1&OX~b7|Nkh}vs3{p4R5VmOttU^St@bF*`SX;mriKy&N$?y=^iJeCQbcreNSEwB zM+87ayG1IMt#ZVO3MxAKUlN(@gU>(z-0wdAUj+e>yz|aGGDl(I{HEi_R|S=pjt{Zf zcq#_?Wc`TOxfxlMoc(H-NGQRuVU#p6ky1{WKmsTu(3)TK*kr!ka7p{t+3mmCC4 z@JZl<;o9KlsVP9ZXg;4(w6#%&++0dmR79C8Dkz@8(6vg3SSQg-ksyMTqo<^z?%fN) z!H$;U!%I{N2^$?Wy&Ysw_hcM&0wCGAaid7k(lROMz=72fd3jSKOeTeE!bmRy;8KHH z37r`(sGgG_$psLd%b-x&HMyLk;fhX~jH08WD1K@x)dHU3e04Q-0)Q$hE9)Gr>3m@y zGLWG>#WB&*)OBOVP-A0bsYoimmFo6p zYy@ZmVjYW#aag0H^CnE6{#TR9^xeDfzB_0oAImi%fXlF)C247bp!D=tW#51QP?Sa! zs&YEHjCq7T+$AQ0jq1Jng2SU9F}%0Nhq6tp>ZD(t}z6 zSRuGvnLt2E;eXUX8Wq}(roElA0(qPr9i3UxJJYYgZ_zoqnYNJXUY;#xQ2_&Z<_s!4 zq+6;`(C6dg>g4h9M^`@f*cxcn}moSuBAIn%&&Z6X$EZl}CCc1Qe#;usnv}_c6n;stPX@vDuTdver+UG-=zeUAuZEaT;tH5)Xg{0>sd&vuAIni;DgvsjQq3snICF z8*s?gFHA2$u8YTA&7B(}Q0%22XE`Ok0CO`8YzWc|;JmxcO|*NY$aDQKPR&BD&NWfJ zJpgj~<>2ni)oPPTG}_6nVE%ISy}d>z+gxTfTXCXNc68wn^MG0O$%Fc zavlh-s|$}b8YMUe=vsbG^)BgTf+XhthD`V0R{}88u#$Zk%=}4YY+|IkiGa@Qpw&_u z7)aFz2e*fh8S^nj_+PD9vEtISY10N~%auOLkP`sQ{{8!vAFp4Z5EB^qczsUJjj;_4 z0nl0yBjA|?z~D1UfJ-QuIh&lD;123w;HE3uVf_jOp!F)1B_b{D zR7ga`#;BN>pI5C~HK-d)`zpU00$_RWx#wgeyFIa`wsxg9H+S~%y1IxElSw3VIyty* zX9J%k0I4Jym>Xh}Lhi1Ck0j{Ktf^LtodmeUs#zqWFNcO&8iInF!{XyFj+;2~LxDi> zb4*N3J6poWp^{%60Z7@obLZ%!2B_gW#@CCUe$>gqT{?>Qb3Xl{0(x>@7g*er*gfnDVP802Zho z(!c)ds~amWTo^UnY)+H6v?NINdYKZ4ptRXguEHJ!AnWac^Z;ZU)=tm`0!jsCd1d3k2R`SVj$Mq`Gkqa)E? zUmqp6Si}meRiLt1DDV!Qo`LBB`a=K=ya83;nu%MR1*|TS3jvYG|mw)L}R7zObXrW#|PG4U?!q(BD z7F(?nna#!prLc+|j$YLODVP8VY_u=35lS}NXu&8HQU*AJI!%1OiB_rfvZ$zXr$TYI zysGK~fZhac_{!~=o9tb*rs@;i4uMRWGiU=Qj#M!RZ)qN5Xi=5QQN9?v9JC^UiL z;kBxekV>vpTH4gsmRnz6Up;2bn2sq^rZg>HytoBQdcP)R4z>K|3BZ)YhY$0stE)w| zwY9>^%1S{+MTM}rxtW`mmZlgrYE*)V%S97O6i%yEX0_R5Xetq(&o}Z#B0Z1C(*Xet zHk+-zq@<*_prAk#A0KZ^Pfxc-M@L&jLPE?#)nW6y{4T%C@AA9+F2Bp~GLZ8B0BzNd UJg<7qoB#j-07*qoM6N<$f*ib`bN~PV literal 0 HcmV?d00001 diff --git a/regression_tests/auxiliary_files/nefertiti.wrl b/regression_tests/auxiliary_files/nefertiti.wrl new file mode 100644 index 0000000..2abf78c --- /dev/null +++ b/regression_tests/auxiliary_files/nefertiti.wrl @@ -0,0 +1,649 @@ +#VRML V2.0 utf8 + +# Produced by 3D Studio MAX VRML 2.0 exporter, Version 2, Beta 0 +# MAX File: nefertiti.max, Date: Tue Sep 29 17:32:41 1998 + +Viewpoint { position 0 -1 10 } + +DEF _____01-ROOT Transform { + translation -0.1337 -2.107 -1.839 + children [ + Shape { + appearance Appearance { + material Material { + diffuseColor 0.9804 0.9804 0.9804 + } + } + geometry DEF FACES IndexedFaceSet { + ccw TRUE + solid FALSE + creaseAngle 2 + coord DEF _____01-COORD Coordinate { point [ + -2.116 1.757 -1.271, -2.036 1.587 -1.231, -2.136 1.607 -1.391, + -2.206 1.837 -1.361, -2.216 1.867 -1.161, -2.116 1.697 -1.111, + -2.066 1.457 -1.131, -1.946 1.607 -0.9606, -1.966 1.387 -1.011, + -2.076 1.337 -1.251, -2.096 1.397 -1.301, -2.396 1.687 -1.561, + -2.306 1.477 -1.521, -2.466 1.527 -1.711, -2.446 1.737 -1.651, + -2.396 1.877 -1.491, -2.326 1.217 -1.511, -2.186 1.437 -1.431, + -2.086 1.257 -1.391, -2.096 1.127 -1.411, -2.196 0.9375 -1.391, + -2.386 1.017 -1.521, -2.486 1.217 -1.691, -2.056 0.9875 -1.251, + -1.976 1.267 -1.141, -1.876 1.167 -1.121, -1.996 0.8775 -1.001, + -2.116 0.8775 -1.201, -1.946 1.267 -1.001, -1.916 1.297 -0.9206, + -1.966 1.197 -0.9206, -1.886 1.127 -0.9906, -1.946 1.287 -1.081, + -1.986 1.017 -0.8106, -1.926 1.127 -0.7606, -1.896 0.9375 -0.6006, + -1.976 0.7975 -0.6606, -1.926 0.9575 -0.9206, -2.136 0.7675 -0.8906, + -1.996 0.5875 -0.6606, -2.136 0.5575 -0.9806, -2.226 0.7575 -1.111, + -1.906 0.3475 -0.6006, -1.856 0.5175 -0.5106, -1.746 0.2775 -0.5806, + -1.906 0.2275 -0.7406, -2.036 0.3375 -0.9706, -2.046 0.4775 -0.7806, + -1.866 0.7975 -0.5406, -2.296 0.8575 -1.351, -2.196 0.6175 -1.211, + -2.236 0.6575 -1.421, -2.346 0.8575 -1.581, -2.386 0.9575 -1.541, + -2.116 0.4775 -1.171, -1.776 0.2875 -0.9306, -1.846 0.4775 -1.181, + -1.566 0.2275 -0.7506, -1.566 0.4375 -0.9906, -1.636 0.7175 -1.341, + -1.986 0.7375 -1.431, -1.756 1.027 -1.601, -2.136 1.097 -1.701, + -2.276 1.537 -1.751, -2.536 1.687 -1.771, -2.466 1.217 -1.751, + -1.966 1.377 -1.751, -2.076 1.717 -1.721, -2.316 1.877 -1.631, + -2.546 1.537 -1.741, -2.546 1.747 -1.671, -2.556 1.937 -1.611, + -2.536 1.897 -1.581, -2.206 1.947 -1.611, -2.326 2.037 -1.421, + -2.316 2.027 -1.231, -2.176 2.067 -1.291, -2.356 1.927 -1.331, + -2.186 1.907 -1.061, -1.976 1.957 -1.001, -2.106 1.857 -1.041, + -1.866 0.9475 -0.3906, -1.936 1.367 -0.2506, -1.946 1.297 -0.0005862, + -1.886 0.8775 0.4194, -1.806 0.4475 0.2294, -1.786 0.1875 0.1694, + -1.806 0.3175 -0.2706, -1.366 0.3575 -2.181, -1.406 0.05749 -1.281, + -1.556 -0.1525 -0.7106, -1.516 -0.6325 -1.151, -1.146 -0.6925 -1.741, + -1.086 -0.2125 -1.911, -1.786 -0.1825 0.03941, -1.766 -0.1525 -0.3606, + -1.706 -0.09251 0.3694, -1.706 -0.3625 0.2894, -1.616 -1.023 -0.1406, + -1.646 -0.8525 -0.4206, -1.576 -0.8825 -0.6006, -1.556 -1.053 -0.4206, + -1.346 -1.333 -0.3906, -1.486 -1.393 -0.9606, -0.7163 -0.6425 -2.381, + -1.036 -1.113 -2.021, -0.7363 -1.093 -2.401, -0.0763 -0.5625 -2.381, + -0.5963 -0.5225 -2.471, -0.5563 -0.4825 -2.571, -1.056 -0.03251 -2.361, + -0.0963 -1.093 -2.371, -0.1063 -1.473 -2.511, -0.7963 -1.483 -2.531, + -1.366 -1.963 -1.861, -1.386 -2.313 -2.071, -0.9363 -1.943 -2.681, + -0.7963 -1.913 -2.791, -0.8963 -3.243 -3.621, -1.706 -3.973 -3.811, + -0.9963 -4.103 -4.111, -0.2363 -4.093 -4.191, -0.2063 -3.353 -3.741, + -0.1663 -2.603 -3.231, -0.8963 -2.513 -3.141, -1.126 -2.563 -3.001, + -1.386 -3.223 -3.371, -0.1363 -2.063 -2.891, -1.826 -3.313 -2.851, + -2.236 -3.693 -2.581, -2.136 -3.713 -3.151, -1.536 -2.833 -2.451, + -1.946 -3.423 -2.331, -1.926 -3.633 -2.031, -1.956 -4.303 -1.764, + -2.226 -3.873 -2.171, -1.676 -3.073 -2.071, -1.666 -3.253 -1.851, + -1.696 -4.023 -1.671, -1.426 -4.363 -1.411, -1.276 -5.023 -1.261, + -1.666 -4.683 -1.501, -1.486 -3.683 -1.491, -1.246 -4.053 -1.211, + -1.216 -4.623 -1.281, -1.376 -3.073 -1.221, -1.476 -2.773 -1.491, + -1.406 -2.483 -1.201, -1.166 -2.893 -0.7706, -1.046 -3.473 -0.8406, + -1.526 -2.563 -1.731, -1.446 -2.233 -1.511, -0.8263 -5.193 -1.071, + -0.9163 -4.373 -1.001, -0.6763 -4.483 -1.001, -0.5263 -5.313 -1.121, + -0.3563 -5.333 -1.131, -0.3663 -4.643 -1.021, -0.1763 -4.753 -1.051, + -0.1963 -5.383 -1.131, -0.4363 -3.893 -0.7006, -0.8863 -3.613 -0.7806, + -0.4763 -3.393 -0.5006, -0.1263 -3.533 -0.5006, -0.1463 -4.033 -0.7306, + -1.456 -1.853 -1.241, -1.386 -1.483 -1.611, -1.486 -1.443 -1.231, + -1.316 -2.193 -0.8206, -0.5163 -2.813 -0.2806, -1.036 -3.023 -0.7006, + -1.196 -2.353 -0.6606, -0.8663 -2.103 -0.1806, -0.5563 -1.883 0.2294, + -0.0963 -2.933 -0.2106, -1.346 -1.533 -0.5406, -1.096 -1.573 -0.1606, + -0.0563 -1.943 0.3194, -0.0363 -1.683 0.7494, -0.5663 -1.643 0.5794, + -1.146 -1.393 0.3994, -1.516 -1.173 -0.2506, -1.286 -1.353 0.1994, + -1.386 -1.193 0.4494, -0.5363 -1.483 1.049, -0.9463 -1.283 1.249, + -0.5563 -1.473 1.399, -0.0063 -1.513 1.649, -1.566 -0.8925 0.3994, + -1.396 -0.7425 0.8494, -1.636 0.04749 0.7194, -1.476 -0.1825 0.9894, + -1.296 -0.3925 1.199, -1.146 -0.7725 1.289, -0.9763 -1.073 1.419, + -0.6263 -1.363 1.779, -0.2763 -1.483 1.829, -0.7363 -1.173 1.769, + -0.5263 -1.183 2.009, -0.3963 -1.373 1.969, 0.0137 -1.353 2.069, + -0.3563 -1.223 2.149, -0.3663 -0.9425 2.139, 0.0237 -1.013 2.219, + 0.0237 -1.233 2.209, -0.9063 -0.8825 1.549, -0.9963 -0.5625 1.519, + -0.7563 -0.6625 1.699, -0.6463 -0.8825 1.859, -1.736 0.2775 0.4994, + -1.846 0.8575 0.7094, -1.696 0.3575 0.8394, -1.576 0.3675 1.109, + -1.386 -0.1125 1.169, -1.756 0.8475 1.009, -1.536 0.8375 1.299, + -1.326 0.4575 1.419, -1.056 0.08749 1.569, -1.106 0.6175 1.629, + -0.8663 0.3575 1.769, -0.8263 0.05749 1.839, -1.026 -0.2525 1.579, + -0.2263 -0.6525 2.109, -0.3863 -0.6325 2.069, -0.2463 -0.5525 2.179, + 0.0337 -0.6925 2.129, -0.4063 -0.5325 2.129, -0.4963 -0.4525 2.019, + -0.4163 -0.3925 2.099, -0.2663 -0.4225 2.199, -0.0963 -0.4525 2.209, + 0.0437 -0.5625 2.209, -0.1563 -0.2725 2.139, 0.0537 -0.3125 2.179, + 0.0437 -0.4525 2.229, -0.5263 -0.5725 1.919, -0.7363 -0.3125 1.709, + -0.5263 0.02749 1.999, -0.4263 -0.08251 2.099, -0.4263 -0.1925 2.079, + -0.4063 -0.2825 2.039, -0.2963 -0.2825 2.109, -0.3363 -0.1525 2.159, + -0.0563 -0.1425 2.299, -0.1863 -0.1425 2.229, -0.1263 0.02749 2.299, + 0.0637 -0.002507 2.319, 0.0537 -0.1425 2.299, -0.3363 -0.04251 2.209, + -0.0863 0.1175 2.279, -0.2563 0.4275 2.069, -0.0263 0.3575 2.229, + 0.0737 0.3275 2.229, 0.0637 0.1175 2.259, -0.6963 0.1875 1.899, + -0.4263 0.4675 1.969, -0.0763 0.6175 2.109, -0.3663 0.5675 2.149, + -0.2263 0.6175 2.279, -0.0963 0.5975 2.359, -0.0163 0.5075 2.409, + -0.4363 0.6075 2.049, -0.4963 0.6675 1.899, -0.3963 0.7375 2.139, + -0.2663 0.6775 2.319, -0.0963 0.6675 2.489, -0.1363 0.8275 2.439, + -0.0063 0.8675 2.529, 0.0937 0.7575 2.569, 0.0837 0.4875 2.439, + -1.306 0.8475 1.509, -1.456 1.227 1.339, -1.206 1.167 1.509, + -0.9263 0.8775 1.709, -0.8763 1.187 1.649, -1.126 1.447 1.479, + -0.8663 1.457 1.549, -0.5663 1.487 1.569, -0.4163 1.357 1.749, + -0.5063 1.017 1.849, -0.2963 0.9775 1.999, -0.2063 0.9075 2.229, + -0.0763 1.177 2.229, -0.3263 1.697 1.629, -0.0663 2.097 1.749, + 0.1037 1.417 2.229, 0.1237 2.127 1.839, -1.386 1.727 1.249, + -1.466 1.567 1.219, -1.646 1.747 0.9494, -1.346 1.887 1.359, + -1.296 1.767 1.359, -1.206 1.647 1.409, -1.096 1.607 1.479, + -1.146 1.707 1.479, -0.9963 1.697 1.529, -0.8663 1.597 1.529, + -0.6463 1.607 1.549, -0.7663 1.707 1.549, -0.5263 1.727 1.479, + -0.4563 1.667 1.539, -0.5063 1.947 1.539, -0.3563 2.047 1.509, + -1.646 1.277 1.149, -1.816 1.417 0.8194, -1.906 1.207 0.5394, + -1.906 1.537 0.2594, -1.836 1.747 0.4994, -1.806 1.717 0.7294, + -1.436 2.037 1.149, -1.716 1.877 0.8394, -1.596 2.197 1.099, + -1.186 2.287 1.359, -1.786 2.037 0.7494, -1.886 1.877 0.009414, + -1.796 2.277 0.4294, -1.656 2.317 1.039, -0.9863 2.337 1.459, + -0.8663 2.057 1.589, -1.086 2.037 1.549, -0.9963 2.527 1.529, + -0.6863 2.507 1.579, -0.7163 2.307 1.479, -0.4863 2.207 1.479, + -0.3563 2.367 1.649, -0.3163 2.517 1.709, -0.6463 2.677 1.629, + -0.5563 2.997 1.479, -0.1163 2.867 1.549, -1.036 2.737 1.529, + -1.326 2.467 1.369, -1.386 2.627 1.359, -1.026 3.027 1.369, + 0.1337 2.747 1.589, 0.1437 3.517 1.219, -0.3963 3.517 1.239, + -1.026 3.427 1.129, -1.436 3.257 0.9294, -1.386 2.897 1.179, + -1.696 2.607 0.7894, -1.836 2.567 0.06941, -1.706 2.937 0.5194, + -1.916 2.047 -0.7406, 1.934 0.7375 0.6094, 1.854 0.7275 0.9194, + 1.944 1.297 0.7194, 2.004 1.917 -0.8406, 2.044 1.827 -1.101, + 1.994 1.477 -1.061, 2.004 1.237 -0.3506, 2.004 1.747 -0.09059, + 2.024 1.167 -0.1006, 2.014 1.407 0.1594, 1.964 1.627 0.3994, + 1.964 2.157 0.3294, 1.994 2.447 -0.03059, 1.694 3.157 0.8494, + 1.914 2.817 0.4294, 1.634 2.797 1.099, 1.624 2.527 1.279, + 1.904 2.487 0.6994, 1.854 2.207 0.9494, 1.284 2.957 1.309, + 1.304 3.357 1.069, 0.6837 3.487 1.209, 0.8237 2.957 1.439, + 0.3837 2.847 1.539, 1.554 2.377 1.299, 1.794 2.087 1.009, + 1.404 2.207 1.289, 1.624 1.937 1.069, 1.234 2.457 1.469, + 0.9037 2.627 1.589, 1.284 2.667 1.469, 0.9237 2.457 1.539, + 0.5937 2.337 1.629, 0.2937 2.087 1.739, 0.5637 2.487 1.689, + 0.5637 2.017 1.489, 1.294 1.957 1.489, 1.534 1.797 1.289, + 0.7137 1.907 1.509, 0.7037 2.167 1.449, 0.9437 2.257 1.439, + 1.074 1.997 1.539, 1.214 2.267 1.399, 1.874 1.757 0.7494, + 1.954 1.917 0.6494, 1.944 1.597 0.6294, 1.804 1.637 0.8594, + 2.004 1.077 0.4394, 1.954 0.7575 0.3194, 0.7437 1.447 1.539, + 0.5937 1.327 1.719, 0.6437 1.627 1.509, 1.624 1.467 1.139, + 1.294 1.367 1.419, 1.384 1.567 1.339, 1.784 1.167 1.059, + 1.654 0.7375 1.219, 1.604 1.127 1.259, 1.354 1.087 1.439, + 0.5137 1.667 1.609, 0.7137 1.687 1.449, 0.9537 1.647 1.509, + 1.184 1.627 1.469, 1.044 1.537 1.479, 1.334 1.627 1.419, + 1.474 1.677 1.289, 0.8237 1.557 1.509, 1.034 1.397 1.499, + 1.274 1.527 1.419, 1.554 1.637 1.169, 1.754 0.2475 0.7494, + 0.6637 0.9775 1.819, 0.4637 0.9575 1.979, 0.1937 0.8575 2.519, + 0.3137 0.8175 2.429, 0.2637 1.167 2.219, 0.3837 0.8875 2.209, + 0.4337 0.6575 2.299, 0.5537 0.7075 2.119, 0.6337 0.6275 1.869, + 1.064 0.8175 1.659, 0.9737 0.2975 1.719, 1.224 0.5375 1.569, + 0.5537 0.4375 1.939, 1.034 1.127 1.599, 1.434 0.7575 1.439, + 1.424 0.3675 1.349, 0.1737 0.4975 2.409, 0.2637 0.6575 2.479, + 0.1637 0.3475 2.229, 0.3837 0.5975 2.259, 0.2537 0.5875 2.349, + 0.5137 0.5375 2.129, 0.3937 0.4075 2.049, 0.5837 0.5775 2.019, + 0.2237 0.6075 2.099, 0.6337 -0.01251 1.969, 0.8037 0.1375 1.859, + 0.9237 -0.002507 1.789, 0.2437 0.01749 2.289, 0.2137 0.1075 2.269, + 0.4437 -0.07251 2.189, 0.2537 -0.2825 2.129, 0.2937 -0.1625 2.219, + 0.4337 -0.1725 2.139, 0.5237 -0.2225 2.059, 0.5237 -0.1125 2.079, + 0.1637 -0.1525 2.289, 0.3537 -0.4425 2.179, 0.3837 -0.3025 2.089, + 0.4937 -0.3125 2.019, 0.4937 -0.4225 2.079, 0.8037 -0.7125 1.659, + 0.5837 -0.6125 1.889, 0.8037 -0.3625 1.669, 0.5737 -0.4825 1.989, + 1.084 -0.3225 1.529, 1.034 -0.6325 1.469, 0.6837 -0.9225 1.829, + 0.4137 -0.9725 2.119, 0.4537 -0.6625 2.049, 0.3237 -0.5725 2.169, + 0.1837 -0.4625 2.199, 0.4837 -0.5625 2.109, 0.2937 -0.6725 2.099, + 1.324 -0.4725 1.129, 1.164 -0.8425 1.229, 1.514 -0.2825 0.9094, + 1.434 -0.2025 1.099, 1.674 -0.06251 0.6294, 1.144 0.01749 1.509, + 1.654 0.2675 1.029, 1.774 0.1675 0.4094, 1.844 0.3275 0.1394, + 1.804 0.06749 0.07941, 1.714 -0.2025 0.2794, 0.7537 -1.223 1.729, + 0.5537 -1.213 1.979, 0.9737 -1.133 1.369, 0.9237 -0.9425 1.499, + 0.9237 -1.343 1.199, 0.4037 -1.403 1.949, 0.2737 -1.503 1.819, + 0.5337 -1.513 1.369, 0.3837 -1.243 2.129, 0.6237 -1.403 1.749, + 1.694 -0.4725 0.1994, 1.524 -0.9925 0.3194, 1.384 -0.8325 0.7794, + 1.324 -1.283 0.3794, 1.074 -1.463 0.3394, 1.534 -1.123 -0.2206, + 1.564 -0.9525 -0.5006, 1.464 -1.153 -0.5006, 0.4937 -1.513 1.019, + 0.4837 -1.673 0.5494, 1.424 -1.263 -0.3306, 1.204 -1.433 0.1394, + 1.234 -1.413 -0.4606, 0.9837 -1.643 -0.2106, 0.4437 -1.913 0.1994, + 0.2337 -3.413 -0.5206, 0.7137 -2.153 -0.2206, 1.004 -2.423 -0.7206, + 1.124 -2.273 -0.8806, 1.214 -1.613 -0.6106, 1.344 -1.483 -1.031, + 0.7737 -3.533 -0.8906, 0.6137 -3.663 -0.8206, 0.9337 -2.963 -0.8206, + 0.8037 -3.083 -0.7506, 1.184 -2.563 -1.271, 0.3237 -2.843 -0.3006, + 0.8537 -1.173 -2.071, 1.204 -1.563 -1.681, 1.004 -0.7625 -1.801, + 1.414 -0.7225 -1.231, 1.324 -1.533 -1.301, 1.144 -2.043 -1.931, + 1.224 -2.323 -1.581, 1.264 -1.943 -1.311, 1.214 -2.863 -1.561, + 0.9137 -4.123 -1.271, 0.5837 -4.423 -1.041, 0.3337 -4.513 -1.031, + 0.1437 -3.913 -0.7206, 0.0137 -4.653 -1.031, -0.0463 -5.343 -1.141, + 0.1237 -5.333 -1.141, 0.4337 -5.233 -1.101, 0.8437 -4.693 -1.331, + 0.8837 -5.093 -1.321, 1.344 -4.123 -1.751, 1.584 -4.413 -1.854, + 1.284 -4.773 -1.581, 1.164 -3.773 -1.561, 1.354 -3.353 -1.931, + 1.264 -2.653 -1.801, 1.124 -2.393 -2.141, 1.364 -3.173 -2.151, + 1.114 -3.153 -1.281, 1.064 -4.443 -1.471, 1.004 -3.303 -3.431, + 1.244 -4.073 -3.891, 1.724 -3.833 -3.251, 1.854 -3.823 -2.691, + 1.854 -4.003 -2.281, 1.594 -3.533 -2.421, 1.224 -2.923 -2.521, + 0.6737 -1.993 -2.721, 0.8037 -2.623 -3.051, 1.584 -3.743 -2.121, + 1.464 -3.423 -2.941, 0.5237 -1.953 -2.821, 0.5737 -2.563 -3.181, + 0.4937 -3.283 -3.661, 0.5137 -4.153 -4.151, 0.5437 -1.133 -2.431, + 0.5637 -1.523 -2.571, 0.4337 -0.5525 -2.501, 0.3937 -0.5125 -2.591, + 1.274 0.2775 -2.251, 0.9237 -0.09251 -2.411, 0.9737 -0.2825 -1.961, + 0.5537 -0.6825 -2.411, 1.734 -0.2625 -0.4506, 1.504 -0.2525 -0.7906, + 1.484 -0.9825 -0.6806, 1.724 0.1675 -0.6706, 1.774 -0.2925 -0.05059, + 1.804 0.1975 -0.3606, 1.534 0.1275 -0.8306, 1.854 0.3975 -0.6106, + 1.884 0.6775 -0.6406, 1.534 0.3375 -1.071, 1.334 -0.03251 -1.351, + 1.604 0.6175 -1.421, 1.734 0.9175 -1.691, 1.944 1.177 -1.021, + 1.954 1.007 -0.8606, 1.904 0.8275 -0.4906, 1.924 0.8175 -0.7006, + 1.994 1.257 -1.111, 2.164 1.557 -1.221, 2.164 1.717 -1.151, + 2.234 1.927 -1.401, 2.244 1.767 -1.181, 2.374 1.877 -1.351, + 2.264 1.727 -1.281, 2.404 1.777 -1.451, 2.564 1.737 -1.711, + 2.434 1.717 -1.621, 2.244 1.697 -1.481, 2.464 1.577 -1.781, + 2.564 1.587 -1.801, 2.374 1.887 -1.541, 2.244 1.807 -1.731, + 2.094 1.587 -1.831, 2.344 1.727 -1.751, 2.584 1.767 -1.741, + 2.544 1.527 -1.901, 2.114 0.9575 -1.811, 2.314 0.7075 -1.701, + 2.444 1.057 -1.881, 2.314 1.327 -1.641, 2.464 1.367 -1.841, + 2.464 1.057 -1.821, 2.364 0.8675 -1.641, 2.364 0.8075 -1.661, + 2.544 1.377 -1.871, 1.954 1.247 -1.851, 2.274 1.387 -1.871, + 1.954 0.6075 -1.531, 1.814 0.3575 -1.271, 2.194 0.5175 -1.541, + 1.744 0.1775 -1.021, 1.874 0.1075 -0.8406, 2.004 0.2075 -1.071, + 2.084 0.3375 -1.281, 2.114 0.4175 -1.091, 2.164 0.4775 -1.321, + 2.214 0.6175 -1.231, 2.034 0.3475 -0.8906, 1.994 0.4575 -0.7606, + 2.274 0.7075 -1.471, 2.104 0.7375 -1.311, 2.174 0.7975 -1.501, + 1.984 0.6675 -0.7606, 1.894 0.2275 -0.7006, 1.994 0.7475 -1.101, + 1.934 0.8375 -1.021, 1.884 1.047 -1.221, 1.904 1.007 -1.091, + 2.134 0.6275 -1.001, 1.994 1.067 -1.021, 2.004 0.8875 -0.9106, + 2.094 1.207 -1.361, 1.964 1.157 -1.181, 1.994 1.137 -1.241, + 1.974 1.137 -1.101, 2.114 1.257 -1.411, 2.084 1.127 -1.501, + 2.044 0.8575 -1.361, 2.084 0.9975 -1.521, 2.194 1.297 -1.541, + 2.164 1.467 -1.501, 2.064 1.457 -1.341, 2.314 1.067 -1.631, + 2.414 1.537 -1.681, 2.094 1.327 -1.241, 2.154 1.617 -1.381] + } + coordIndex [ + 1, 0, 2, -1, + 2, 0, 3, -1, 3, 0, 4, -1, 4, 0, 5, -1, 5, 0, 1, -1, + 5, 6, 7, -1, 7, 6, 8, -1, 8, 6, 9, -1, 9, 6, 10, -1, + 10, 6, 1, -1, 1, 6, 5, -1, 3, 11, 2, -1, 2, 11, 12, -1, + 12, 11, 13, -1, 13, 11, 14, -1, 14, 11, 15, -1, 15, 11, 3, -1, + 12, 16, 17, -1, 17, 16, 10, -1, 10, 16, 18, -1, 18, 16, 19, -1, + 19, 16, 20, -1, 20, 16, 21, -1, 21, 16, 22, -1, 22, 16, 12, -1, + 1, 2, 10, -1, 17, 10, 2, -1, 17, 2, 12, -1, 9, 23, 24, -1, + 24, 23, 25, -1, 25, 23, 26, -1, 26, 23, 27, -1, 27, 23, 20, -1, + 20, 23, 19, -1, 19, 23, 18, -1, 18, 23, 9, -1, 18, 9, 10, -1, + 8, 28, 29, -1, 29, 28, 30, -1, 30, 28, 31, -1, 31, 28, 25, -1, + 25, 28, 32, -1, 32, 28, 8, -1, 25, 32, 24, -1, 24, 32, 9, -1, + 9, 32, 8, -1, 31, 33, 30, -1, 30, 33, 34, -1, 34, 33, 35, -1, + 35, 33, 36, -1, 36, 33, 37, -1, 37, 33, 31, -1, 30, 34, 29, -1, + 26, 38, 36, -1, 36, 38, 39, -1, 39, 38, 40, -1, 40, 38, 41, -1, + 41, 38, 26, -1, 36, 37, 26, -1, 31, 25, 37, -1, 37, 25, 26, -1, + 26, 27, 41, -1, 39, 42, 43, -1, 43, 42, 44, -1, 44, 42, 45, -1, + 45, 42, 46, -1, 46, 42, 47, -1, 47, 42, 39, -1, 43, 48, 39, -1, + 36, 39, 48, -1, 36, 48, 35, -1, 21, 49, 20, -1, 20, 49, 27, -1, + 27, 49, 41, -1, 41, 49, 50, -1, 50, 49, 51, -1, 51, 49, 52, -1, + 52, 49, 53, -1, 53, 49, 21, -1, 39, 40, 47, -1, 47, 40, 46, -1, + 40, 41, 50, -1, 51, 54, 50, -1, 50, 54, 40, -1, 40, 54, 46, -1, + 46, 54, 55, -1, 55, 54, 56, -1, 56, 54, 51, -1, 45, 46, 55, -1, + 45, 55, 57, -1, 45, 57, 44, -1, 57, 55, 58, -1, 56, 58, 55, -1, + 58, 56, 59, -1, 61, 60, 62, -1, 62, 60, 52, -1, 52, 60, 51, -1, + 51, 60, 56, -1, 56, 60, 59, -1, 59, 60, 61, -1, 64, 63, 65, -1, + 65, 63, 62, -1, 62, 63, 66, -1, 66, 63, 67, -1, 67, 63, 68, -1, + 68, 63, 64, -1, 66, 62, 61, -1, 14, 69, 13, -1, 13, 69, 22, -1, + 22, 69, 65, -1, 65, 69, 64, -1, 64, 69, 70, -1, 70, 69, 14, -1, + 52, 53, 65, -1, 53, 21, 65, -1, 22, 65, 21, -1, 13, 22, 12, -1, + 52, 65, 62, -1, 72, 71, 70, -1, 70, 71, 64, -1, 64, 71, 68, -1, + 68, 71, 73, -1, 73, 71, 74, -1, 74, 71, 75, -1, 75, 71, 72, -1, + 68, 73, 67, -1, 76, 73, 74, -1, 76, 74, 75, -1, 70, 14, 72, -1, + 72, 14, 15, -1, 4, 77, 3, -1, 3, 77, 15, -1, 15, 77, 72, -1, + 72, 77, 75, -1, 75, 77, 4, -1, 79, 78, 80, -1, 80, 78, 5, -1, + 5, 78, 4, -1, 4, 78, 75, -1, 75, 78, 76, -1, 76, 78, 79, -1, + 7, 79, 80, -1, 7, 80, 5, -1, 8, 29, 7, -1, 29, 81, 7, -1, + 7, 81, 82, -1, 82, 81, 83, -1, 83, 81, 84, -1, 84, 81, 85, -1, + 85, 81, 86, -1, 86, 81, 87, -1, 87, 81, 48, -1, 48, 81, 35, -1, + 35, 81, 34, -1, 34, 81, 29, -1, 61, 88, 59, -1, 58, 59, 88, -1, + 58, 89, 57, -1, 57, 89, 90, -1, 90, 89, 91, -1, 91, 89, 92, -1, + 92, 89, 93, -1, 93, 89, 88, -1, 88, 89, 58, -1, 87, 48, 43, -1, + 87, 43, 44, -1, 90, 44, 57, -1, 95, 94, 44, -1, 44, 94, 87, -1, + 87, 94, 86, -1, 86, 94, 96, -1, 96, 94, 97, -1, 97, 94, 98, -1, + 98, 94, 99, -1, 99, 94, 95, -1, 95, 44, 90, -1, 91, 100, 90, -1, + 90, 100, 99, -1, 99, 100, 101, -1, 101, 100, 102, -1, + 102, 100, 103, -1, 103, 100, 91, -1, 90, 99, 95, -1, + 93, 104, 92, -1, 92, 104, 105, -1, 105, 104, 106, -1, + 106, 104, 107, -1, 107, 104, 108, -1, 108, 104, 93, -1, + 109, 108, 110, -1, 93, 110, 108, -1, 110, 93, 88, -1, + 108, 109, 107, -1, 106, 111, 112, -1, 106, 113, 105, -1, + 105, 113, 114, -1, 114, 113, 115, -1, 115, 113, 116, -1, + 116, 113, 117, -1, 117, 113, 112, -1, 112, 113, 106, -1, + 111, 106, 107, -1, 119, 118, 120, -1, 120, 118, 121, -1, + 121, 118, 122, -1, 122, 118, 123, -1, 123, 118, 124, -1, + 124, 118, 125, -1, 125, 118, 126, -1, 126, 118, 119, -1, + 125, 116, 117, -1, 125, 117, 124, -1, 123, 124, 117, -1, + 123, 117, 127, -1, 112, 127, 117, -1, 129, 128, 130, -1, + 130, 128, 126, -1, 126, 128, 125, -1, 125, 128, 131, -1, + 131, 128, 132, -1, 132, 128, 129, -1, 134, 133, 135, -1, + 135, 133, 132, -1, 132, 133, 136, -1, 136, 133, 137, -1, + 137, 133, 138, -1, 138, 133, 134, -1, 131, 116, 125, -1, + 115, 116, 131, -1, 115, 131, 136, -1, 132, 136, 131, -1, + 135, 132, 129, -1, 119, 130, 126, -1, 140, 139, 141, -1, + 141, 139, 138, -1, 138, 139, 142, -1, 142, 139, 143, -1, + 143, 139, 144, -1, 144, 139, 140, -1, 143, 145, 142, -1, + 142, 145, 146, -1, 146, 145, 147, -1, 147, 145, 148, -1, + 148, 145, 149, -1, 149, 145, 143, -1, 146, 150, 137, -1, + 137, 150, 136, -1, 136, 150, 115, -1, 115, 150, 114, -1, + 114, 150, 151, -1, 151, 150, 146, -1, 138, 142, 137, -1, + 146, 137, 142, -1, 134, 141, 138, -1, 140, 152, 144, -1, + 143, 144, 152, -1, 143, 152, 153, -1, 154, 153, 152, -1, + 154, 152, 155, -1, 154, 155, 156, -1, 154, 156, 157, -1, + 158, 157, 156, -1, 158, 156, 159, -1, 158, 160, 157, -1, + 157, 160, 154, -1, 154, 160, 161, -1, 161, 160, 162, -1, + 162, 160, 163, -1, 163, 160, 164, -1, 164, 160, 158, -1, + 153, 154, 161, -1, 153, 161, 149, -1, 153, 149, 143, -1, + 146, 147, 151, -1, 151, 165, 166, -1, 166, 165, 167, -1, + 167, 165, 103, -1, 103, 165, 168, -1, 168, 165, 147, -1, + 147, 165, 151, -1, 151, 166, 114, -1, 105, 114, 166, -1, + 103, 91, 167, -1, 166, 167, 91, -1, 166, 91, 92, -1, + 166, 92, 105, -1, 162, 169, 170, -1, 170, 169, 171, -1, + 171, 169, 172, -1, 172, 169, 173, -1, 173, 169, 174, -1, + 174, 169, 162, -1, 148, 170, 171, -1, 148, 171, 168, -1, + 148, 168, 147, -1, 162, 170, 161, -1, 148, 161, 170, -1, + 161, 148, 149, -1, 176, 175, 168, -1, 168, 175, 103, -1, + 103, 175, 102, -1, 102, 175, 176, -1, 168, 171, 176, -1, + 172, 176, 171, -1, 176, 172, 173, -1, 163, 174, 162, -1, + 177, 173, 174, -1, 173, 177, 178, -1, 173, 178, 179, -1, + 173, 179, 180, -1, 173, 180, 176, -1, 101, 102, 181, -1, + 101, 181, 98, -1, 98, 182, 183, -1, 183, 182, 180, -1, + 180, 182, 176, -1, 176, 182, 102, -1, 102, 182, 181, -1, + 181, 182, 98, -1, 178, 184, 179, -1, 179, 184, 180, -1, + 180, 184, 185, -1, 185, 184, 186, -1, 186, 184, 187, -1, + 187, 184, 178, -1, 101, 98, 99, -1, 183, 188, 98, -1, + 98, 188, 97, -1, 183, 180, 185, -1, 97, 189, 96, -1, + 96, 189, 190, -1, 190, 189, 191, -1, 191, 189, 192, -1, + 192, 189, 193, -1, 193, 189, 194, -1, 194, 189, 185, -1, + 185, 189, 183, -1, 183, 189, 188, -1, 188, 189, 97, -1, + 196, 195, 186, -1, 186, 195, 185, -1, 185, 195, 197, -1, + 197, 195, 198, -1, 198, 195, 199, -1, 199, 195, 196, -1, + 196, 187, 200, -1, 200, 201, 199, -1, 199, 201, 198, -1, + 198, 201, 202, -1, 202, 201, 203, -1, 203, 201, 204, -1, + 204, 201, 200, -1, 187, 196, 186, -1, 196, 200, 199, -1, + 185, 197, 194, -1, 194, 205, 193, -1, 193, 205, 206, -1, + 206, 205, 207, -1, 207, 205, 208, -1, 208, 205, 197, -1, + 197, 205, 194, -1, 202, 208, 198, -1, 197, 198, 208, -1, + 190, 209, 96, -1, 96, 209, 86, -1, 86, 209, 85, -1, + 85, 209, 84, -1, 84, 209, 210, -1, 210, 209, 211, -1, + 211, 209, 190, -1, 213, 212, 191, -1, 191, 212, 211, -1, + 211, 212, 214, -1, 214, 212, 215, -1, 215, 212, 216, -1, + 216, 212, 213, -1, 216, 217, 218, -1, 218, 217, 219, -1, + 219, 217, 220, -1, 220, 217, 221, -1, 221, 217, 192, -1, + 192, 217, 213, -1, 213, 217, 216, -1, 191, 211, 190, -1, + 192, 213, 191, -1, 206, 192, 193, -1, 192, 206, 221, -1, + 203, 222, 202, -1, 202, 222, 223, -1, 223, 222, 224, -1, + 224, 222, 225, -1, 225, 222, 203, -1, 223, 226, 227, -1, + 227, 226, 228, -1, 228, 226, 229, -1, 229, 226, 224, -1, + 224, 226, 223, -1, 231, 230, 224, -1, 224, 230, 229, -1, + 229, 230, 232, -1, 232, 230, 233, -1, 233, 230, 234, -1, + 234, 230, 231, -1, 231, 224, 225, -1, 227, 235, 223, -1, + 223, 235, 208, -1, 208, 235, 207, -1, 202, 223, 208, -1, + 207, 236, 206, -1, 206, 236, 221, -1, 221, 236, 220, -1, + 220, 236, 237, -1, 237, 236, 238, -1, 238, 236, 239, -1, + 239, 236, 240, -1, 240, 236, 228, -1, 228, 236, 227, -1, + 227, 236, 235, -1, 235, 236, 207, -1, 229, 241, 228, -1, + 228, 241, 240, -1, 240, 241, 239, -1, 239, 241, 242, -1, + 242, 241, 232, -1, 232, 241, 229, -1, 233, 243, 232, -1, + 232, 243, 244, -1, 244, 243, 245, -1, 245, 243, 246, -1, + 246, 243, 247, -1, 247, 243, 233, -1, 245, 248, 244, -1, + 244, 248, 242, -1, 242, 248, 238, -1, 238, 248, 237, -1, + 242, 238, 239, -1, 244, 242, 232, -1, 245, 249, 248, -1, + 248, 249, 237, -1, 237, 249, 250, -1, 250, 249, 251, -1, + 251, 249, 252, -1, 252, 249, 253, -1, 253, 249, 246, -1, + 246, 249, 245, -1, 255, 254, 237, -1, 237, 254, 220, -1, + 220, 254, 219, -1, 219, 254, 255, -1, 255, 237, 250, -1, + 257, 256, 258, -1, 258, 256, 259, -1, 259, 256, 260, -1, + 260, 256, 251, -1, 251, 256, 250, -1, 250, 261, 255, -1, + 255, 261, 262, -1, 262, 261, 263, -1, 263, 261, 264, -1, + 264, 261, 257, -1, 257, 261, 250, -1, 257, 258, 264, -1, + 260, 265, 259, -1, 259, 265, 258, -1, 258, 265, 264, -1, + 264, 265, 266, -1, 266, 265, 267, -1, 267, 265, 268, -1, + 251, 252, 260, -1, 269, 260, 252, -1, 268, 265, 260, -1, + 268, 260, 269, -1, 218, 270, 216, -1, 216, 270, 215, -1, + 215, 270, 271, -1, 271, 270, 272, -1, 272, 270, 273, -1, + 273, 270, 218, -1, 273, 274, 272, -1, 272, 274, 275, -1, + 275, 274, 276, -1, 276, 274, 277, -1, 277, 274, 278, -1, + 278, 274, 279, -1, 279, 274, 273, -1, 255, 262, 219, -1, + 218, 219, 273, -1, 262, 273, 219, -1, 279, 273, 262, -1, + 279, 262, 280, -1, 263, 280, 262, -1, 266, 281, 263, -1, + 263, 281, 280, -1, 266, 263, 264, -1, 266, 282, 281, -1, + 281, 282, 280, -1, 280, 282, 278, -1, 278, 282, 283, -1, + 283, 282, 284, -1, 284, 282, 285, -1, 285, 282, 267, -1, + 267, 282, 266, -1, 267, 268, 285, -1, 280, 278, 279, -1, + 211, 214, 210, -1, 286, 284, 285, -1, 288, 287, 289, -1, + 289, 287, 290, -1, 290, 287, 291, -1, 291, 287, 292, -1, + 292, 287, 288, -1, 275, 293, 292, -1, 292, 293, 294, -1, + 294, 293, 295, -1, 295, 293, 296, -1, 296, 293, 276, -1, + 276, 293, 275, -1, 277, 297, 276, -1, 276, 297, 296, -1, + 296, 297, 298, -1, 299, 297, 300, -1, 300, 297, 277, -1, + 291, 292, 294, -1, 295, 296, 298, -1, 299, 300, 301, -1, + 283, 301, 300, -1, 283, 300, 278, -1, 302, 283, 284, -1, + 302, 301, 283, -1, 275, 288, 272, -1, 271, 272, 288, -1, + 288, 303, 271, -1, 271, 303, 215, -1, 215, 303, 214, -1, + 214, 303, 304, -1, 304, 303, 289, -1, 289, 303, 288, -1, + 275, 292, 288, -1, 278, 300, 277, -1, 210, 305, 84, -1, + 84, 305, 83, -1, 83, 305, 306, -1, 306, 305, 307, -1, + 307, 305, 308, -1, 308, 305, 304, -1, 304, 305, 210, -1, + 289, 308, 304, -1, 290, 309, 289, -1, 289, 309, 310, -1, + 310, 309, 311, -1, 311, 309, 312, -1, 312, 309, 290, -1, + 289, 310, 308, -1, 307, 308, 310, -1, 310, 313, 307, -1, + 307, 313, 314, -1, 314, 313, 315, -1, 315, 313, 316, -1, + 316, 313, 311, -1, 311, 313, 310, -1, 318, 317, 319, -1, + 319, 317, 312, -1, 312, 317, 320, -1, 320, 317, 321, -1, + 321, 317, 322, -1, 322, 317, 318, -1, 301, 323, 318, -1, + 318, 323, 322, -1, 322, 323, 321, -1, 321, 323, 324, -1, + 324, 323, 302, -1, 302, 323, 301, -1, 312, 290, 319, -1, + 284, 324, 302, -1, 284, 325, 324, -1, 324, 325, 326, -1, + 326, 325, 327, -1, 327, 325, 328, -1, 328, 325, 286, -1, + 286, 325, 284, -1, 324, 326, 321, -1, 320, 321, 326, -1, + 326, 329, 320, -1, 320, 329, 330, -1, 330, 329, 331, -1, + 331, 329, 332, -1, 332, 329, 327, -1, 327, 329, 326, -1, + 320, 330, 312, -1, 330, 311, 312, -1, 311, 330, 316, -1, + 316, 330, 331, -1, 286, 333, 328, -1, 334, 328, 333, -1, + 335, 328, 334, -1, 328, 335, 327, -1, 332, 327, 335, -1, + 332, 335, 336, -1, 332, 336, 337, -1, 332, 337, 338, -1, + 332, 338, 331, -1, 331, 339, 316, -1, 316, 339, 315, -1, + 315, 339, 340, -1, 340, 339, 341, -1, 341, 339, 338, -1, + 338, 339, 331, -1, 341, 338, 337, -1, 315, 340, 342, -1, + 315, 342, 314, -1, 307, 314, 306, -1, 83, 306, 314, -1, + 83, 314, 82, -1, 342, 82, 314, -1, 82, 342, 7, -1, + 79, 7, 342, -1, 214, 304, 210, -1, 344, 343, 345, -1, + 347, 346, 348, -1, 349, 348, 346, -1, 346, 350, 349, -1, + 351, 349, 350, -1, 351, 350, 352, -1, 353, 352, 350, -1, + 354, 350, 346, -1, 354, 346, 355, -1, 357, 356, 358, -1, + 358, 359, 360, -1, 357, 358, 360, -1, 355, 357, 360, -1, + 354, 355, 360, -1, 361, 354, 360, -1, 359, 361, 360, -1, + 362, 359, 358, -1, 362, 358, 356, -1, 362, 356, 363, -1, + 362, 363, 364, -1, 362, 364, 365, -1, 366, 365, 364, -1, + 364, 334, 366, -1, 334, 333, 366, -1, 286, 366, 333, -1, + 361, 359, 367, -1, 368, 361, 367, -1, 367, 369, 368, -1, + 370, 368, 369, -1, 371, 369, 367, -1, 365, 372, 373, -1, + 362, 365, 373, -1, 359, 362, 373, -1, 367, 359, 373, -1, + 371, 367, 373, -1, 372, 371, 373, -1, 371, 372, 374, -1, + 375, 374, 372, -1, 286, 376, 377, -1, 366, 286, 377, -1, + 365, 366, 377, -1, 372, 365, 377, -1, 375, 372, 377, -1, + 376, 375, 377, -1, 376, 378, 375, -1, 369, 379, 380, -1, + 378, 381, 382, -1, 375, 378, 382, -1, 374, 375, 382, -1, + 383, 374, 382, -1, 384, 383, 382, -1, 381, 384, 382, -1, + 383, 384, 385, -1, 374, 383, 385, -1, 371, 374, 385, -1, + 369, 371, 385, -1, 379, 369, 385, -1, 384, 379, 385, -1, + 368, 386, 387, -1, 361, 368, 387, -1, 354, 361, 387, -1, + 350, 354, 387, -1, 353, 350, 387, -1, 386, 353, 387, -1, + 353, 386, 388, -1, 389, 388, 386, -1, 369, 380, 370, -1, + 386, 368, 370, -1, 389, 386, 370, -1, 380, 389, 370, -1, + 389, 345, 388, -1, 345, 343, 390, -1, 388, 345, 390, -1, + 353, 388, 390, -1, 352, 353, 390, -1, 351, 352, 390, -1, + 391, 351, 390, -1, 343, 391, 390, -1, 393, 392, 394, -1, + 396, 395, 397, -1, 389, 395, 398, -1, 345, 389, 398, -1, + 344, 345, 398, -1, 399, 344, 398, -1, 400, 399, 398, -1, + 395, 400, 398, -1, 400, 395, 401, -1, 396, 401, 395, -1, + 378, 402, 381, -1, 378, 376, 402, -1, 402, 393, 394, -1, + 403, 381, 394, -1, 405, 404, 406, -1, 408, 407, 397, -1, + 394, 392, 409, -1, 403, 394, 409, -1, 404, 403, 409, -1, + 406, 404, 409, -1, 410, 406, 409, -1, 392, 410, 409, -1, + 410, 396, 411, -1, 406, 410, 411, -1, 405, 406, 411, -1, + 407, 405, 411, -1, 397, 407, 411, -1, 396, 397, 411, -1, + 397, 395, 412, -1, 408, 397, 412, -1, 380, 408, 412, -1, + 389, 380, 412, -1, 395, 389, 412, -1, 286, 285, 376, -1, + 413, 343, 344, -1, 415, 414, 393, -1, 416, 285, 268, -1, + 416, 417, 418, -1, 285, 416, 418, -1, 376, 285, 418, -1, + 402, 376, 418, -1, 393, 402, 418, -1, 415, 393, 418, -1, + 419, 415, 418, -1, 417, 419, 418, -1, 417, 420, 421, -1, + 421, 415, 419, -1, 417, 421, 419, -1, 421, 422, 415, -1, + 414, 415, 422, -1, 414, 422, 423, -1, 422, 424, 423, -1, + 425, 423, 424, -1, 426, 424, 422, -1, 414, 423, 427, -1, + 393, 414, 427, -1, 392, 393, 427, -1, 410, 392, 427, -1, + 396, 410, 427, -1, 401, 396, 427, -1, 423, 401, 427, -1, + 423, 425, 428, -1, 401, 423, 428, -1, 400, 401, 428, -1, + 399, 400, 428, -1, 429, 399, 428, -1, 425, 429, 428, -1, + 268, 269, 430, -1, 268, 430, 431, -1, 269, 252, 430, -1, + 432, 430, 252, -1, 416, 268, 431, -1, 417, 416, 431, -1, + 420, 417, 431, -1, 433, 420, 431, -1, 434, 433, 431, -1, + 430, 434, 431, -1, 435, 420, 433, -1, 435, 436, 437, -1, + 420, 435, 437, -1, 421, 420, 437, -1, 422, 421, 437, -1, + 426, 422, 437, -1, 436, 426, 437, -1, 432, 436, 438, -1, + 430, 432, 438, -1, 434, 430, 438, -1, 433, 434, 438, -1, + 435, 433, 438, -1, 436, 435, 438, -1, 426, 436, 439, -1, + 424, 426, 440, -1, 441, 424, 440, -1, 439, 441, 440, -1, + 426, 439, 440, -1, 246, 442, 443, -1, 253, 246, 443, -1, + 252, 253, 443, -1, 432, 252, 443, -1, 436, 432, 443, -1, + 439, 436, 443, -1, 444, 439, 443, -1, 442, 444, 443, -1, + 446, 445, 447, -1, 447, 448, 449, -1, 449, 439, 444, -1, + 447, 449, 444, -1, 446, 447, 444, -1, 442, 446, 444, -1, + 247, 233, 450, -1, 246, 247, 450, -1, 442, 246, 450, -1, + 446, 442, 450, -1, 445, 446, 450, -1, 233, 445, 450, -1, + 445, 451, 452, -1, 447, 445, 452, -1, 448, 447, 452, -1, + 453, 448, 452, -1, 454, 453, 452, -1, 451, 454, 452, -1, + 456, 455, 457, -1, 458, 456, 457, -1, 454, 458, 457, -1, + 453, 454, 457, -1, 448, 453, 457, -1, 449, 448, 457, -1, + 439, 449, 457, -1, 441, 439, 457, -1, 459, 441, 457, -1, + 460, 459, 457, -1, 455, 460, 457, -1, 462, 461, 463, -1, + 461, 455, 456, -1, 463, 461, 456, -1, 458, 463, 456, -1, + 231, 225, 464, -1, 234, 231, 465, -1, 233, 234, 465, -1, + 445, 233, 465, -1, 451, 445, 465, -1, 464, 451, 465, -1, + 231, 464, 465, -1, 464, 463, 466, -1, 451, 464, 466, -1, + 454, 451, 466, -1, 458, 454, 466, -1, 463, 458, 466, -1, + 225, 203, 467, -1, 464, 225, 467, -1, 463, 464, 467, -1, + 462, 463, 467, -1, 203, 462, 467, -1, 468, 459, 460, -1, + 460, 469, 468, -1, 468, 470, 471, -1, 470, 472, 413, -1, + 471, 429, 473, -1, 468, 471, 473, -1, 459, 468, 473, -1, + 441, 459, 473, -1, 424, 441, 473, -1, 425, 424, 473, -1, + 429, 425, 473, -1, 429, 471, 474, -1, 399, 429, 474, -1, + 344, 399, 474, -1, 413, 344, 474, -1, 470, 413, 474, -1, + 471, 470, 474, -1, 413, 472, 475, -1, 343, 413, 475, -1, + 391, 343, 475, -1, 476, 391, 475, -1, 477, 476, 475, -1, + 478, 477, 475, -1, 472, 478, 475, -1, 479, 461, 480, -1, + 462, 480, 461, -1, 479, 481, 482, -1, 461, 479, 482, -1, + 455, 461, 482, -1, 460, 455, 482, -1, 469, 460, 482, -1, + 481, 469, 482, -1, 483, 481, 479, -1, 485, 484, 200, -1, + 187, 486, 485, -1, 204, 200, 487, -1, 203, 204, 487, -1, + 462, 203, 487, -1, 480, 462, 487, -1, 484, 480, 487, -1, + 200, 484, 487, -1, 485, 200, 187, -1, 484, 485, 488, -1, + 480, 484, 488, -1, 479, 480, 488, -1, 483, 479, 488, -1, + 486, 483, 488, -1, 485, 486, 488, -1, 490, 489, 491, -1, + 492, 490, 491, -1, 483, 492, 491, -1, 481, 483, 491, -1, + 469, 481, 491, -1, 468, 469, 491, -1, 470, 468, 491, -1, + 472, 470, 491, -1, 478, 472, 491, -1, 489, 478, 491, -1, + 492, 483, 493, -1, 494, 489, 490, -1, 492, 494, 490, -1, + 496, 495, 494, -1, 187, 178, 497, -1, 486, 187, 497, -1, + 483, 486, 497, -1, 493, 483, 497, -1, 498, 493, 497, -1, + 178, 498, 497, -1, 499, 494, 500, -1, 501, 499, 500, -1, + 502, 501, 500, -1, 493, 502, 500, -1, 492, 493, 500, -1, + 494, 492, 500, -1, 496, 494, 499, -1, 496, 499, 501, -1, + 503, 502, 493, -1, 503, 493, 498, -1, 503, 498, 178, -1, + 503, 178, 177, -1, 177, 174, 503, -1, 163, 504, 174, -1, + 502, 503, 505, -1, 505, 506, 502, -1, 507, 502, 506, -1, + 501, 502, 508, -1, 509, 501, 508, -1, 507, 509, 508, -1, + 502, 507, 508, -1, 511, 510, 512, -1, 512, 513, 511, -1, + 504, 511, 513, -1, 512, 514, 507, -1, 512, 507, 506, -1, + 512, 506, 513, -1, 515, 513, 506, -1, 174, 504, 515, -1, + 503, 174, 515, -1, 505, 503, 515, -1, 506, 505, 515, -1, + 504, 513, 515, -1, 517, 516, 518, -1, 517, 518, 519, -1, + 517, 519, 520, -1, 509, 520, 519, -1, 516, 517, 521, -1, + 522, 521, 517, -1, 514, 522, 523, -1, 507, 514, 523, -1, + 509, 507, 523, -1, 520, 509, 523, -1, 517, 520, 523, -1, + 522, 517, 523, -1, 524, 522, 514, -1, 526, 525, 510, -1, + 526, 510, 511, -1, 526, 511, 527, -1, 164, 158, 528, -1, + 163, 164, 528, -1, 504, 163, 528, -1, 511, 504, 528, -1, + 527, 511, 528, -1, 529, 527, 528, -1, 158, 529, 528, -1, + 158, 159, 530, -1, 158, 530, 529, -1, 527, 529, 530, -1, + 527, 530, 531, -1, 527, 531, 532, -1, 527, 532, 526, -1, + 525, 526, 532, -1, 525, 532, 533, -1, 534, 533, 532, -1, + 536, 535, 537, -1, 524, 538, 539, -1, 535, 539, 538, -1, + 522, 524, 540, -1, 521, 522, 540, -1, 541, 521, 540, -1, + 542, 541, 540, -1, 539, 542, 540, -1, 524, 539, 540, -1, + 510, 525, 543, -1, 512, 510, 543, -1, 514, 512, 543, -1, + 524, 514, 543, -1, 538, 524, 543, -1, 525, 538, 543, -1, + 533, 534, 544, -1, 525, 533, 544, -1, 538, 525, 544, -1, + 535, 538, 544, -1, 537, 535, 544, -1, 534, 537, 544, -1, + 546, 545, 547, -1, 549, 548, 550, -1, 550, 551, 542, -1, + 541, 542, 551, -1, 541, 551, 552, -1, 551, 553, 552, -1, + 535, 536, 554, -1, 539, 535, 554, -1, 542, 539, 554, -1, + 550, 542, 554, -1, 549, 550, 554, -1, 536, 549, 554, -1, + 550, 548, 555, -1, 551, 550, 555, -1, 553, 551, 555, -1, + 545, 553, 555, -1, 547, 545, 555, -1, 548, 547, 555, -1, + 112, 556, 127, -1, 123, 127, 556, -1, 123, 556, 557, -1, + 553, 557, 556, -1, 553, 556, 552, -1, 545, 546, 558, -1, + 553, 545, 558, -1, 557, 553, 558, -1, 123, 557, 558, -1, + 122, 123, 558, -1, 121, 122, 558, -1, 559, 121, 558, -1, + 546, 559, 558, -1, 111, 107, 560, -1, 112, 560, 561, -1, + 556, 112, 561, -1, 552, 556, 561, -1, 541, 552, 561, -1, + 521, 541, 561, -1, 516, 521, 561, -1, 560, 516, 561, -1, + 560, 112, 111, -1, 562, 107, 563, -1, 565, 564, 566, -1, + 566, 562, 565, -1, 563, 565, 562, -1, 562, 566, 567, -1, + 107, 562, 567, -1, 560, 107, 567, -1, 516, 560, 567, -1, + 518, 516, 567, -1, 566, 518, 567, -1, 569, 568, 495, -1, + 509, 519, 570, -1, 501, 509, 570, -1, 496, 501, 570, -1, + 495, 496, 570, -1, 569, 495, 570, -1, 519, 569, 570, -1, + 568, 569, 571, -1, 495, 568, 572, -1, 494, 495, 572, -1, + 489, 494, 572, -1, 478, 489, 572, -1, 477, 478, 572, -1, + 573, 477, 572, -1, 571, 573, 572, -1, 568, 571, 572, -1, + 569, 574, 571, -1, 573, 571, 575, -1, 573, 575, 576, -1, + 564, 577, 578, -1, 566, 564, 578, -1, 518, 566, 578, -1, + 519, 518, 578, -1, 569, 519, 578, -1, 574, 569, 578, -1, + 577, 574, 578, -1, 577, 564, 579, -1, 580, 579, 564, -1, + 582, 581, 583, -1, 584, 582, 583, -1, 576, 584, 583, -1, + 573, 576, 583, -1, 477, 573, 583, -1, 476, 477, 583, -1, + 391, 476, 583, -1, 351, 391, 583, -1, 349, 351, 583, -1, + 348, 349, 583, -1, 581, 348, 583, -1, 585, 348, 581, -1, + 348, 586, 587, -1, 348, 587, 347, -1, 588, 347, 589, -1, + 590, 588, 589, -1, 591, 590, 589, -1, 586, 591, 589, -1, + 587, 586, 589, -1, 347, 587, 589, -1, 590, 591, 592, -1, + 593, 590, 592, -1, 594, 593, 592, -1, 595, 594, 592, -1, + 591, 595, 592, -1, 593, 594, 596, -1, 597, 593, 596, -1, + 588, 590, 598, -1, 588, 598, 599, -1, 601, 600, 599, -1, + 590, 593, 602, -1, 598, 590, 602, -1, 599, 598, 602, -1, + 601, 599, 602, -1, 603, 601, 602, -1, 597, 603, 602, -1, + 593, 597, 602, -1, 605, 604, 606, -1, 608, 607, 609, -1, + 609, 610, 606, -1, 611, 606, 610, -1, 605, 606, 611, -1, + 597, 596, 612, -1, 603, 597, 612, -1, 606, 603, 612, -1, + 609, 606, 612, -1, 608, 609, 612, -1, 596, 608, 612, -1, + 604, 580, 613, -1, 601, 603, 614, -1, 600, 601, 614, -1, + 613, 600, 614, -1, 604, 613, 614, -1, 606, 604, 614, -1, + 603, 606, 614, -1, 579, 580, 615, -1, 616, 579, 615, -1, + 617, 616, 615, -1, 605, 617, 615, -1, 604, 605, 615, -1, + 580, 604, 615, -1, 577, 579, 616, -1, 616, 618, 577, -1, + 574, 577, 618, -1, 619, 571, 574, -1, 619, 574, 618, -1, + 619, 618, 620, -1, 616, 617, 621, -1, 618, 616, 621, -1, + 620, 618, 621, -1, 622, 620, 621, -1, 623, 622, 621, -1, + 617, 623, 621, -1, 622, 623, 624, -1, 625, 620, 622, -1, + 626, 625, 622, -1, 611, 610, 627, -1, 605, 611, 627, -1, + 617, 605, 627, -1, 623, 617, 627, -1, 624, 623, 627, -1, + 628, 624, 627, -1, 629, 628, 627, -1, 610, 629, 627, -1, + 630, 584, 576, -1, 630, 576, 626, -1, 575, 626, 576, -1, + 625, 626, 631, -1, 620, 625, 631, -1, 619, 620, 631, -1, + 571, 619, 631, -1, 575, 571, 631, -1, 626, 575, 631, -1, + 632, 624, 628, -1, 633, 632, 634, -1, 635, 633, 634, -1, + 630, 632, 633, -1, 624, 632, 636, -1, 622, 624, 636, -1, + 626, 622, 636, -1, 630, 626, 636, -1, 632, 630, 636, -1, + 637, 581, 582, -1, 633, 635, 638, -1, 630, 633, 638, -1, + 584, 630, 638, -1, 582, 584, 638, -1, 637, 582, 638, -1, + 635, 637, 638, -1, 639, 585, 640, -1, 641, 639, 640, -1, + 634, 641, 640, -1, 640, 585, 642, -1, 634, 640, 642, -1, + 635, 634, 642, -1, 637, 635, 642, -1, 581, 637, 642, -1, + 585, 581, 642, -1, 644, 643, 639, -1, 644, 639, 645, -1, + 646, 644, 645, -1, 629, 646, 645, -1, 628, 629, 645, -1, + 632, 628, 645, -1, 634, 632, 645, -1, 641, 634, 645, -1, + 639, 641, 645, -1, 647, 607, 648, -1, 647, 648, 643, -1, + 649, 643, 648, -1, 609, 607, 650, -1, 610, 609, 650, -1, + 629, 610, 650, -1, 646, 629, 650, -1, 644, 646, 650, -1, + 643, 644, 650, -1, 647, 643, 650, -1, 607, 647, 650, -1, + 594, 595, 651, -1, 596, 594, 651, -1, 608, 596, 651, -1, + 607, 608, 651, -1, 648, 607, 651, -1, 595, 648, 651, -1, + 649, 586, 652, -1, 643, 649, 652, -1, 639, 643, 652, -1, + 585, 639, 652, -1, 348, 585, 652, -1, 586, 348, 652, -1, + 586, 649, 653, -1, 591, 586, 653, -1, 595, 591, 653, -1, + 648, 595, 653, -1, 649, 648, 653, -1, 319, 290, 291, -1, + 319, 291, 294, -1, 319, 294, 295, -1, 319, 295, 318, -1, + 318, 298, 301, -1, 301, 298, 299, -1, 381, 403, 404, -1, + 405, 384, 404, -1, 379, 384, 405, -1, 379, 405, 407, -1, + 380, 379, 407, -1, 394, 381, 402, -1, 408, 380, 407, -1, + 318, 295, 298, -1, 299, 298, 297, -1, 381, 404, 384, -1, + 257, 250, 256, -1] + } + } + ] +} diff --git a/regression_tests/auxiliary_files/sky.jpg b/regression_tests/auxiliary_files/sky.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8122837b023ef68e0e6325394d4079521c5bdb20 GIT binary patch literal 6496 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 VeN_Q9wtrf6o&WP?A@OtmKLOwH$!Y)q literal 0 HcmV?d00001 diff --git a/regression_tests/auxiliary_files/subtitle.srt b/regression_tests/auxiliary_files/subtitle.srt new file mode 100644 index 0000000..c0a7139 --- /dev/null +++ b/regression_tests/auxiliary_files/subtitle.srt @@ -0,0 +1,23 @@ +1 +00:00:01,262 --> 00:00:02,787 +This is a sub-title +on 2 lines + +2 +00:00:03,418 --> 00:00:04,817 +with italic support + +3 +00:00:05,986 --> 00:00:06,419 +and also bold + +4 +00:00:07,223 --> 00:00:08,487 +and even bold +italic lines... + +5 +00:00:09,223 --> 00:00:10,487 +and unicode: é ï ö Ä + + diff --git a/regression_tests/auxiliary_files/subtitle_fr.srt b/regression_tests/auxiliary_files/subtitle_fr.srt new file mode 100644 index 0000000..29785ac --- /dev/null +++ b/regression_tests/auxiliary_files/subtitle_fr.srt @@ -0,0 +1,23 @@ +1 +00:00:01,262 --> 00:00:02,787 +Ceci est un sous-titre +sur 2 lignes + +2 +00:00:03,418 --> 00:00:04,817 +avec support de l'italique + +3 +00:00:05,986 --> 00:00:06,419 +et du gras + +4 +00:00:07,223 --> 00:00:08,487 +et même du gras +italique ... + +5 +00:00:09,223 --> 00:00:10,487 +et de l'unicode: é ï ö Ä + + diff --git a/regression_tests/auxiliary_files/svg2html.xslt b/regression_tests/auxiliary_files/svg2html.xslt new file mode 100644 index 0000000..8535e30 --- /dev/null +++ b/regression_tests/auxiliary_files/svg2html.xslt @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + <xsl:value-of select = "$title"/> + + + + +

    + +

    + +
    +
    + +
    +

    Description

    + +
    +
    + + +
    +

    Snapshots

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    At time T = At time T = At time T = At time T = At time T = At time T =
    {concat('Snapshot #1 at time ',$snapshot1)}{concat('Snapshot #2 at time ',$snapshot2)}{concat('Snapshot #3 at time ',$snapshot3)}{concat('Snapshot #4 at time ',$snapshot4)}{concat('Snapshot #5 at time ',$snapshot5)}{concat('Snapshot #6 at time ',$snapshot6)}
    +
    +
    +
    + + +
    +
    diff --git a/regression_tests/auxiliary_files/x3d2html.xslt b/regression_tests/auxiliary_files/x3d2html.xslt new file mode 100644 index 0000000..0a1dc08 --- /dev/null +++ b/regression_tests/auxiliary_files/x3d2html.xslt @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + <xsl:value-of select = "$title"/> + + + + +

    + +

    + +
    +
    +
    +

    Description

    + +
    +
    +

    Download

    + +
    +
    + + +
    +

    Snapshots

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    At time T = At time T = At time T = At time T = At time T = At time T =
    {concat('Snapshot #1 at time ',$snapshot1)}{concat('Snapshot #2 at time ',$snapshot2)}{concat('Snapshot #3 at time ',$snapshot3)}{concat('Snapshot #4 at time ',$snapshot4)}{concat('Snapshot #5 at time ',$snapshot5)}{concat('Snapshot #6 at time ',$snapshot6)}
    +
    +
    +
    + + +
    + + + + +

    + +

    + + + +
    + +

    + +

    +
    +
    +
    +
    diff --git a/regression_tests/auxiliary_files/xmt2html.xslt b/regression_tests/auxiliary_files/xmt2html.xslt new file mode 100644 index 0000000..18b7b36 --- /dev/null +++ b/regression_tests/auxiliary_files/xmt2html.xslt @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + <xsl:value-of select = "$title"/> + + + + +

    + +

    + +
    +
    +
    +

    Download

    + +
    +
    +

    Description

    + +
    +
    + + +
    +

    Snapshots

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    At time T = At time T = At time T = At time T = At time T = At time T =
    {concat('Snapshot #1 at time ',$snapshot1)}{concat('Snapshot #2 at time ',$snapshot2)}{concat('Snapshot #3 at time ',$snapshot3)}{concat('Snapshot #4 at time ',$snapshot4)}{concat('Snapshot #5 at time ',$snapshot5)}{concat('Snapshot #6 at time ',$snapshot6)}
    +
    +
    +
    +

    XMT Code

    + +Your browser does not support inline objects. You cannot view the XMT code, use the download link instead. + +
    +
    + + +
    + + + + +

    + + + +

    + + + +
    + +

    + + + +

    +
    +
    +
    + + + + + + See also .html + + + + + + +
    diff --git a/regression_tests/bifs-2D-background-background2D-bind.bt b/regression_tests/bifs-2D-background-background2D-bind.bt new file mode 100644 index 0000000..257c324 --- /dev/null +++ b/regression_tests/bifs-2D-background-background2D-bind.bt @@ -0,0 +1,71 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + DEF B1 Background2D { + backColor $FFFFFF + } + DEF B2 Background2D { + backColor 1 0 0 + } + WorldInfo { + info [ + "This shows 2 Background2D nodes and the bind mechanism" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:37:08 $ - $Revision: 1.4 $" + "(C) 2002-2006 GPAC Team" + ] + title "Background Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + DEF TS TouchSensor {} + ] + } + DEF C2 Conditional { + buffer { + REPLACE B1.set_bind BY TRUE + } + } + DEF C1 Conditional { + buffer { + REPLACE B2.set_bind BY TRUE + } + } + ] +} + +ROUTE TS.isOver TO C1.activate +ROUTE TS.isOver TO C2.reverseActivate diff --git a/regression_tests/bifs-2D-background-background2D-image.bt b/regression_tests/bifs-2D-background-background2D-image.bt new file mode 100644 index 0000000..f645c90 --- /dev/null +++ b/regression_tests/bifs-2D-background-background2D-image.bt @@ -0,0 +1,97 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + DEF B1 Background2D { + url [od:10] + } + DEF B2 Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Background2D with image" "and binding mechanism" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Background Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + DEF TS TouchSensor {} + ] + } + DEF C2 Conditional { + buffer { + REPLACE B1.set_bind BY TRUE + } + } + DEF C1 Conditional { + buffer { + REPLACE B2.set_bind BY TRUE + } + } + ] +} + +ROUTE TS.isOver TO C1.activate +ROUTE TS.isOver TO C2.reverseActivate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ] +} + +AT 1000 { + REPLACE B2.set_bind BY TRUE +} + +AT 2000 { + REPLACE B1.set_bind BY TRUE +} + diff --git a/regression_tests/bifs-2D-background-background2D-layer2D.bt b/regression_tests/bifs-2D-background-background2D-layer2D.bt new file mode 100644 index 0000000..36f5fac --- /dev/null +++ b/regression_tests/bifs-2D-background-background2D-layer2D.bt @@ -0,0 +1,92 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + DEF B1 Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Background2D within a layer2D" "Note that the background is stretched to the layer size" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.4 $" "(C) 2002-2004 GPAC Team"] + title "Background Test" + } + Transform2D { + translation 100 0 + children [ + DEF L Layer2D { + size 400 300 + children [ + Shape { + appearance Appearance { + material Material2D { + filled TRUE + transparency 0 + } + } + geometry DEF RC Rectangle { + size 200 100 + } + } + ] + background Background2D { + url [od:10] + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF PI PositionInterpolator2D { + key [0 0.75 1] + keyValue [400 300 0 0 0 0] + } + ] +} + +ROUTE TS.fraction_changed TO PI.set_fraction +ROUTE PI.value_changed TO L.size + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-background-background2D-movie.bt b/regression_tests/bifs-2D-background-background2D-movie.bt new file mode 100644 index 0000000..bb52476 --- /dev/null +++ b/regression_tests/bifs-2D-background-background2D-movie.bt @@ -0,0 +1,94 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + DEF B1 Background2D { + url [od:10] + } + DEF B2 Background2D { + backColor 1 1 1 + } + MediaControl { + url [od:10] + loop TRUE + } + WorldInfo { + info ["This shows Background2D with image" "and binding mechanism" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Background Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + DEF TS TouchSensor {} + ] + } + DEF C2 Conditional { + buffer { + REPLACE B1.set_bind BY TRUE + } + } + DEF C1 Conditional { + buffer { + REPLACE B2.set_bind BY TRUE + } + } + ] +} + +ROUTE TS.isOver TO C1.activate +ROUTE TS.isOver TO C2.reverseActivate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-background-background2D-url-change.bt b/regression_tests/bifs-2D-background-background2D-url-change.bt new file mode 100644 index 0000000..b20208c --- /dev/null +++ b/regression_tests/bifs-2D-background-background2D-url-change.bt @@ -0,0 +1,105 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 200 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + DEF B1 Background2D { + url [od:20] + } + WorldInfo { + info ["This shows Background2D with dynamic image change" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Background Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + DEF TS TouchSensor {} + ] + } + DEF C1 Conditional { + buffer { + REPLACE B1.url BY ["od:20"] + } + } + DEF RC1 Conditional { + buffer { + REPLACE B1.url BY ["od:10"] + } + } + ] +} + +ROUTE TS.isOver TO C1.activate +ROUTE TS.isOver TO RC1.reverseActivate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 20 + esDescr [ + ES_Descriptor { + ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + +AT 2000 { + REPLACE B1.url BY ["od:20"] +} + +AT 4000 { + REPLACE B1.url BY ["od:10"] +} + diff --git a/regression_tests/bifs-2D-interactivity-discsensor.bt b/regression_tests/bifs-2D-interactivity-discsensor.bt new file mode 100644 index 0000000..9a2cc57 --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-discsensor.bt @@ -0,0 +1,129 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the use of the disc sensor which computes the angle in the mouse motion between mouse down and mouse up." + "It also uses the auto-offset field, which allows storing reusing the angle from the previous mouse up." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "DiscSensor" + } + DEF T1 Transform2D { + scale 1 2 + scaleOrientation 0.75 + translation -150 0 + children [ + DEF S Shape { + appearance Appearance { + texture ImageTexture { + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry Rectangle { + size 100 100 + } + } + DEF DS1 DiscSensor {} + ] + } + Transform2D { + translation -150 0 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Auto-offset on"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 24 + } + } + } + ] + } + DEF T2 Transform2D { + scale 1 2 + scaleOrientation 0.75 + translation 150 0 + children [ + USE S + DEF DS2 DiscSensor { + autoOffset FALSE + } + ] + } + Transform2D { + translation 150 0 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Auto-offset off"] + fontStyle USE FS + } + } + ] + } + ] +} + +ROUTE DS1.rotation_changed TO T1.rotationAngle +ROUTE DS2.rotation_changed TO T2.rotationAngle + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-interactivity-htk-sensor.bt b/regression_tests/bifs-2D-interactivity-htk-sensor.bt new file mode 100644 index 0000000..fa54dbd --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-htk-sensor.bt @@ -0,0 +1,175 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of String Sensor" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002 ENST"] + title "InputSensor Test - StringSensor device" + } + Transform2D { + translation 0 90 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["HTKSensor"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 30 + } + } + } + ] + } + Transform2D { + translation -50 20 + children [ + Shape { + appearance USE APP + geometry Text { + string ["spoken Text"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation 90 20 + children [ + Shape { + appearance USE APP + geometry DEF T2 Text { + string [""] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -50 -20 + children [ + Shape { + appearance USE APP + geometry Text { + string ["text index in dict"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 90 -20 + children [ + Shape { + appearance USE APP + geometry DEF T3 Text { + string [""] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -50 -50 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Recogn Score"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 90 -50 + children [ + Shape { + appearance USE APP + geometry DEF T4 Text { + string [""] + fontStyle USE FS + } + } + ] + } + DEF VN3 Valuator { + Factor2 0 + Factor3 0 + } + DEF VN4 Valuator { + Factor2 0 + Factor3 0 + } + InputSensor { + url [od:10] + buffer { + REPLACE T2.string[0] BY "" + REPLACE VN3.inSFInt32 BY 0 + REPLACE VN4.inSFFloat BY 0 + } + } + ] +} + +ROUTE VN3.outMFString TO T3.string +ROUTE VN4.outMFString TO T4.string + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 5 + decConfigDescr DecoderConfigDescriptor { + streamType 10 + decSpecificInfo UIConfig { + deviceName "HTKSensor" + uiData "HTK:BLEU vcl bb ll eu sp;ROUGE rr ou jj sp;VERT vv ai rr sp" + } + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-interactivity-keysensor.bt b/regression_tests/bifs-2D-interactivity-keysensor.bt new file mode 100644 index 0000000..9bc2e7b --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-keysensor.bt @@ -0,0 +1,810 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 600 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows usage of the InputSensor node to detect keys and trigger of events" + "The InputSensor in this case has a url which points to a specific pseudo-media object. This object contains one stream, called interaction stream." + "This stream is made of Device Data Frame whose content is specialized depending on the kind of input device." + "The InputSensor node in the scene specifies how to dispatch the content of the DDF." + "For each piece of information in the DDF, a REPLACE command is associated." + "The value of the target property is replaced by the associated content from the DDF." + "" + "If the configuration of the stream says 'KeySensor', like here, the input device is a keyboard and the DDF content is as follows:" + "- the key code for the pressed key (0 if none)," + "- the key code for the released key (0 if none)," + "- the key code for the action key (F1, ...) pressed (0 if none)," + "- the key code for the action key (F1, ...) released (0 for none)," + "- and the state of the modifier keys (CTRL, ALT and Shift)." + "In this example, the target nodes are Valuator nodes which cast the key code into the char representation, and the result is displayed." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "InputSensor Node for detecting keys (KeySensor)" + } + DEF N33 Switch { + choice [ + Group { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 1 + filled TRUE + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + ] + } + Group { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.5 0.5 1 + filled TRUE + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + ] + } + Group { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.5 1 1 + filled TRUE + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + ] + } + Group { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0.5 1 + filled TRUE + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + ] + } + Group { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + ] + } + Group { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + ] + } + Group { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 1 + filled TRUE + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + ] + } + Group { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + ] + } + ] + } + DEF N10 Conditional { + buffer { + REPLACE N33.whichChoice BY 0 + } + } + Group { + children [ + DEF N12 TouchSensor {} + Transform2D { + translation -195 -120 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 1 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + Shape { + appearance DEF N29 Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["F1"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] + } + DEF N20 Conditional { + buffer { + REPLACE N33.whichChoice BY 1 + } + } + Group { + children [ + DEF N19 TouchSensor {} + Transform2D { + translation -195 0 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.5 0.5 1 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + Shape { + appearance USE N29 + geometry Text { + string ["F2"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] + } + DEF N23 Conditional { + buffer { + REPLACE N33.whichChoice BY 2 + } + } + Group { + children [ + DEF N9 TouchSensor {} + Transform2D { + translation -195 120 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.5 1 1 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + Shape { + appearance USE N29 + geometry Text { + string ["F3"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] + } + DEF N14 Conditional { + buffer { + REPLACE N33.whichChoice BY 3 + } + } + Group { + children [ + DEF N31 TouchSensor {} + Transform2D { + translation -195 240 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0.5 1 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + Shape { + appearance USE N29 + geometry Text { + string ["F4"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] + } + DEF N15 Conditional { + buffer { + REPLACE N33.whichChoice BY 4 + } + } + Group { + children [ + DEF N25 TouchSensor {} + Transform2D { + translation 195 -120 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + Shape { + appearance USE N29 + geometry Text { + string ["F5"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] + } + DEF N16 Conditional { + buffer { + REPLACE N33.whichChoice BY 5 + } + } + Group { + children [ + DEF N11 TouchSensor {} + Transform2D { + translation 195 0 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + Shape { + appearance USE N29 + geometry Text { + string ["F6"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] + } + DEF N17 Conditional { + buffer { + REPLACE N33.whichChoice BY 6 + } + } + Group { + children [ + DEF N18 TouchSensor {} + Transform2D { + translation 195 120 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 1 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + Shape { + appearance USE N29 + geometry Text { + string ["F7"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] + } + DEF N27 Conditional { + buffer { + REPLACE N33.whichChoice BY 7 + } + } + Group { + children [ + DEF N13 TouchSensor {} + Transform2D { + translation 195 240 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + Shape { + appearance USE N29 + geometry Text { + string ["F8"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] + } + Transform2D { + translation 0 -120 + children [ + Shape { + appearance USE N29 + geometry Text { + string ["KeySensor"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 30 + } + } + } + ] + } + Transform2D { + translation -50 -150 + children [ + Shape { + appearance USE N29 + geometry Text { + string ["KeyPress"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation 90 -150 + children [ + Shape { + appearance USE N29 + geometry DEF N32 Text { + string ["0"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -50 -170 + children [ + Shape { + appearance USE N29 + geometry Text { + string ["KeyRelease"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation 90 -170 + children [ + Shape { + appearance USE N29 + geometry DEF N30 Text { + string ["0"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -50 -190 + children [ + Shape { + appearance USE N29 + geometry Text { + string ["ActionKeyPress"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation 90 -190 + children [ + Shape { + appearance USE N29 + geometry DEF N28 Text { + string ["0"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -50 -210 + children [ + Shape { + appearance USE N29 + geometry Text { + string ["ActionKeyRelease"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation 90 -210 + children [ + Shape { + appearance USE N29 + geometry DEF N26 Text { + string ["0"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -50 -230 + children [ + Shape { + appearance USE N29 + geometry Text { + string ["shiftKey_changed"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation 90 -230 + children [ + Shape { + appearance USE N29 + geometry DEF N24 Text { + string ["0"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -50 -250 + children [ + Shape { + appearance USE N29 + geometry Text { + string ["controlKey_changed"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation 90 -250 + children [ + Shape { + appearance USE N29 + geometry DEF N22 Text { + string ["0"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -50 -270 + children [ + Shape { + appearance USE N29 + geometry Text { + string ["altKey_changed"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation 90 -270 + children [ + Shape { + appearance USE N29 + geometry DEF N21 Text { + string ["0"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + DEF N3 Valuator {} + DEF N7 Valuator { + Offset1 -1 + } + DEF N6 Valuator {} + DEF N5 Valuator {} + DEF N4 Valuator {} + DEF N2 Valuator {} + DEF N1 Valuator {} + DEF N0 Valuator {} + InputSensor { + url [od:10] + buffer { + REPLACE N6.inSFInt32 BY 0 + REPLACE N5.inSFInt32 BY 0 + REPLACE N3.inSFInt32 BY 0 + REPLACE N4.inSFInt32 BY 0 + REPLACE N2.inSFBool BY TRUE + REPLACE N1.inSFBool BY TRUE + REPLACE N0.inSFBool BY TRUE + } + } + Group { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + ] + } + ] +} + +ROUTE N6.outMFString TO N32.string +ROUTE N5.outMFString TO N30.string +ROUTE N4.outMFString TO N26.string +ROUTE N2.outMFString TO N24.string +ROUTE N1.outMFString TO N22.string +ROUTE N0.outMFString TO N21.string +ROUTE N3.outSFInt32 TO N7.inSFInt32 +ROUTE N3.outMFString TO N28.string +ROUTE N7.outSFInt32 TO N33.whichChoice +ROUTE N12.isActive TO N10.activate +ROUTE N19.isActive TO N20.activate +ROUTE N9.isActive TO N23.activate +ROUTE N31.isActive TO N14.activate +ROUTE N25.isActive TO N15.activate +ROUTE N11.isActive TO N16.activate +ROUTE N18.isActive TO N17.activate +ROUTE N13.isActive TO N27.activate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 5 + decConfigDescr DecoderConfigDescriptor { + streamType 10 + decSpecificInfo UIConfig { + deviceName "KeySensor" + } + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-interactivity-mousesensor.bt b/regression_tests/bifs-2D-interactivity-mousesensor.bt new file mode 100644 index 0000000..bad6754 --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-mousesensor.bt @@ -0,0 +1,182 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 600 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows usage of the InputSensor node to detect keys and trigger of events" + "The InputSensor in this case has a url which points to a specific pseudo-media object. This object contains one stream, called interaction stream." + "This stream is made of Device Data Frame whose content is specialized depending on the kind of input device." + "The InputSensor node in the scene specifies how to dispatch the content of the DDF." + "For each piece of information in the DDF, a REPLACE command is associated." + "The value of the target property is replaced by the associated content from the DDF." + "" + "If the configuration of the stream says 'MouseSensor', like here, the input device is a mouse and the DDF content is as follows:" + "- the mouse position," + "- the status of the left button," + "- the status of the middle button," + "- the status of the right button," + "- and the status of the wheel." + "This configuration allows doing different things than the TouchSensor because the TouchSensor cannot give information about the buttons, or the wheel. On the other hand, it can be triggered based on sibling geometry, which is not the case of the InputSensor node in MouseSensor mode." + "In this example, the result of the InputSensor node triggers ECMAScript actions." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "InputSensor Node for mouse events (MouseSensor)" + } + Transform2D { + translation -120 -100 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT1 Text { + string ["Left Down" ""] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation 0 -100 + children [ + Shape { + appearance USE APP + geometry DEF TXT2 Text { + string ["Middle Down" ""] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 120 -100 + children [ + Shape { + appearance USE APP + geometry DEF TXT3 Text { + string ["Right Down" ""] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance USE APP + geometry DEF TXT4 Text { + string [""] + fontStyle USE FS + } + } + ] + } + DEF SC Script { + eventIn SFBool set_left + eventIn SFBool set_middle + eventIn SFBool set_right + eventIn SFFloat set_wheel + field SFFloat wheel_pos 0 + field SFNode t1 USE TXT1 + field SFNode t2 USE TXT2 + field SFNode t3 USE TXT3 + field SFNode t4 USE TXT4 + url ["javascript:function set_left(value, timestamp) {t1.string[1] = '' + value;}function set_middle(value, timestamp) {t2.string[1] = '' + value;}function set_right(value, timestamp) {t3.string[1] = '' + value;}function set_wheel(value, timestamp) {wheel_pos += value;t4.string[0] = 'Wheel Pos ' + wheel_pos;}" ] + } + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + texture ImageTexture { + url [od:2] + } + } + geometry Bitmap {} + + } + ] + } + InputSensor { + url [od:10] + buffer { + REPLACE TR.translation BY 0 0 + REPLACE SC.set_left BY FALSE + REPLACE SC.set_middle BY FALSE + REPLACE SC.set_right BY FALSE + REPLACE SC.set_wheel BY 0 + } + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 2 + esDescr [ + ES_Descriptor { + ES_ID 3 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 5 + decConfigDescr DecoderConfigDescriptor { + streamType 10 + decSpecificInfo UIConfig { + deviceName "Mouse" + } + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-interactivity-nested-sensors.bt b/regression_tests/bifs-2D-interactivity-nested-sensors.bt new file mode 100644 index 0000000..fec681e --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-nested-sensors.bt @@ -0,0 +1,123 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows a mixed usage of TouchSensor and PlaneSensor2D nodes on a single shape." + "In some circumstances, the object can be dragged and in some other, it can be clicked." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Nested Sensors" + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["PlaneSensor2D and TouchSensor:" "Drag the shape on the blue part" "and click on the center"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 20 + } + } + } + ] + } + DEF T1 Transform2D { + children [ + DEF TS TouchSensor {} + DEF PS PlaneSensor2D { + maxPosition 200 200 + minPosition -200 -200 + } + Shape { + appearance DEF A1 Appearance { + material DEF M1 Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Circle { + radius 60 + } + } + Transform2D { + children [ + Shape { + appearance Appearance { + material DEF M2 Material2D { + emissiveColor 1 0 1 + filled TRUE + } + } + geometry Circle { + radius 40 + } + } + DEF TS2 TouchSensor {} + ] + } + ] + } + DEF C1 Conditional { + buffer { + REPLACE M1.filled BY FALSE + } + } + DEF C2 Conditional { + buffer { + REPLACE M1.filled BY TRUE + } + } + DEF C3 Conditional { + buffer { + REPLACE M2.emissiveColor BY 0 1 0 + } + } + DEF C4 Conditional { + buffer { + REPLACE M2.emissiveColor BY 1 0 1 + } + } + ] +} + +ROUTE TS.isOver TO C1.activate +ROUTE TS.isOver TO C2.reverseActivate +ROUTE TS2.isActive TO C3.activate +ROUTE TS2.isActive TO C4.reverseActivate +ROUTE PS.translation_changed TO T1.translation + diff --git a/regression_tests/bifs-2D-interactivity-planesensor2D.bt b/regression_tests/bifs-2D-interactivity-planesensor2D.bt new file mode 100644 index 0000000..f1fa0f9 --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-planesensor2D.bt @@ -0,0 +1,124 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the use of the PlaneSensor2D node, and its auto-offset property." + "The PlaneSensor2D is used to detect mouse motion between mouse down and mouse up." + "This allows to move objects between mouse down and mouse up." + "The auto-offset is used to automatically store the mouse position at mouse release and use it as the offset to the next translation." + "This tests shows two images (clipped by circles) which can be dragged." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team"] + title "PlaneSensor2D for dragging objects" + } + DEF T1 Transform2D { + translation -150 0 + children [ + DEF S Shape { + appearance Appearance { + texture ImageTexture { + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry Circle { + radius 100 + } + } + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Auto-offset on"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 24 + } + } + } + DEF PS1 PlaneSensor2D { + maxPosition 200 200 + minPosition -200 -200 + offset -150 0 + } + ] + } + DEF T2 Transform2D { + translation 150 0 + children [ + USE S + Shape { + appearance USE APP + geometry Text { + string ["Auto-offset off"] + fontStyle USE FS + } + } + DEF PS2 PlaneSensor2D { + autoOffset FALSE + maxPosition 200 200 + minPosition -200 -200 + offset 150 0 + } + ] + } + ] +} + +ROUTE PS1.translation_changed TO T1.translation +ROUTE PS2.translation_changed TO T2.translation + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-interactivity-proximitysensor2D.bt b/regression_tests/bifs-2D-interactivity-proximitysensor2D.bt new file mode 100644 index 0000000..d4d77c9 --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-proximitysensor2D.bt @@ -0,0 +1,89 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the usage of the ProximitySensor2D node" + "Two ProximitySensor2D nodes are placed on a single shape. The Shape is a 300x300 rectangle, and the ProximitySensor2D nodes define two areas centered on the rectangle: a 50x50 rectangle and a 200x200 rectangle." + "When the mouse enters or exits an area, an action is triggered." + "Here, nothing happens when the mouse enters the object, then it turns red if the mouse enters the first area, and it turns empty if the mouse enters the second area." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "ProximitySensor2D" + } + DEF T1 Transform2D { + children [ + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 0.5 0.6 0.4 + filled TRUE + } + } + geometry Rectangle { + size 300 300 + } + } + DEF PSOUT ProximitySensor2D { + size 200 200 + } + DEF PSIN ProximitySensor2D { + size 50 50 + } + ] + } + DEF CIN Conditional { + buffer { + REPLACE M1.filled BY FALSE + } + } + DEF RCIN Conditional { + buffer { + REPLACE M1.filled BY TRUE + } + } + DEF COUT Conditional { + buffer { + REPLACE M1.emissiveColor BY 1 0 0 + } + } + DEF RCOUT Conditional { + buffer { + REPLACE M1.emissiveColor BY 0.5 0.6 0.4 + } + } + ] +} + +ROUTE PSOUT.isActive TO COUT.activate +ROUTE PSOUT.isActive TO RCOUT.reverseActivate +ROUTE PSIN.isActive TO CIN.activate +ROUTE PSIN.isActive TO RCIN.reverseActivate + diff --git a/regression_tests/bifs-2D-interactivity-stringsensor.bt b/regression_tests/bifs-2D-interactivity-stringsensor.bt new file mode 100644 index 0000000..58afaed --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-stringsensor.bt @@ -0,0 +1,153 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows usage of the InputSensor node to detect keys and trigger of events" + "The InputSensor in this case has a url which points to a specific pseudo-media object. This object contains one stream, called interaction stream." + "This stream is made of Device Data Frame whose content is specialized depending on the kind of input device." + "The InputSensor node in the scene specifies how to dispatch the content of the DDF." + "For each piece of information in the DDF, a REPLACE command is associated." + "The value of the target property is replaced by the associated content from the DDF." + "" + "If the configuration of the stream says 'StringSensor', like here, the input device is a keyboard and the DDF content is as follows:" + "- the string being edited/input," + "and the previous string after final editing (return key)" + "In this example, the target nodes are Text nodes which display the string being edited and the final one." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "InputSensor Node for detecting string input (StringSensor)" + } + Transform2D { + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["StringSensor"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 30 + } + } + } + ] + } + Transform2D { + translation -50 -30 + children [ + Shape { + appearance USE APP + geometry Text { + string ["enteredText"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation 90 -30 + children [ + Shape { + appearance USE APP + geometry DEF N3 Text { + string [""] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -50 -50 + children [ + Shape { + appearance USE APP + geometry Text { + string ["finalText"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 90 -50 + children [ + Shape { + appearance USE APP + geometry DEF N2 Text { + string [""] + fontStyle USE FS + } + } + ] + } + InputSensor { + url [od:10] + buffer { + REPLACE N3.string[0] BY "" + REPLACE N2.string[0] BY "" + } + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 5 + decConfigDescr DecoderConfigDescriptor { + streamType 10 + decSpecificInfo UIConfig { + deviceName "StringSensor" + } + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-interactivity-touchsensor-4states.bt b/regression_tests/bifs-2D-interactivity-touchsensor-4states.bt new file mode 100644 index 0000000..9c79d84 --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-touchsensor-4states.bt @@ -0,0 +1,103 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 140 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This shows how to make a 4 states button" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" + "(C) 2002-2006 GPAC Team" + ] + title "4 States Button" + } + Transform2D { + scale 0.5 0.5 + children [ + Transform2D { + translation 0 60 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + DEF TS1 TouchSensor {} + ] + } + Transform2D { + translation -100 -50 + children [ + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 1 1 0 + filled TRUE + } + } + geometry Circle { + radius 50 + } + } + ] + } + DEF OverC Conditional { + buffer { + REPLACE M1.emissiveColor BY 1 1 0 + } + } + DEF OverRC Conditional { + buffer { + REPLACE M1.emissiveColor BY 0 1 1 + } + } + DEF C Conditional { + buffer { + REPLACE M1.emissiveColor BY 1 0 0 + } + } + DEF RC Conditional { + buffer { + REPLACE M1.emissiveColor BY 0 0 1 + } + } + ] + } + ] +} + +ROUTE TS1.isActive TO C.activate +ROUTE TS1.isActive TO RC.reverseActivate +ROUTE TS1.isOver TO OverC.activate +ROUTE TS1.isOver TO OverRC.reverseActivate + diff --git a/regression_tests/bifs-2D-interactivity-touchsensor-hitpoint.bt b/regression_tests/bifs-2D-interactivity-touchsensor-hitpoint.bt new file mode 100644 index 0000000..71e9d46 --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-touchsensor-hitpoint.bt @@ -0,0 +1,91 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 254 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 254 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to retrieve the mouse position when it is moved." + "The TouchSensor node generates the hitPoint event, which is a 3D point, when the mouse moves. This can be used in scripts to trigger actions." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "TouchSensor & hitPoint" + } + Transform2D { + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Rectangle {} + + } + ] + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 198 198 + } + } + DEF TS TouchSensor {} + ] + } + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APP + geometry DEF TXT Text { + string [""] + fontStyle FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + DEF V Valuator { + Factor3 0 + } + ] +} + +ROUTE TS.hitPoint_changed TO V.inSFVec3f +ROUTE V.outMFString TO TXT.string + diff --git a/regression_tests/bifs-2D-interactivity-touchsensor-isactive-exposedfield.bt b/regression_tests/bifs-2D-interactivity-touchsensor-isactive-exposedfield.bt new file mode 100644 index 0000000..d5b695a --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-touchsensor-isactive-exposedfield.bt @@ -0,0 +1,99 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 140 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows a TouchSensor node used to modify the filled property of 2 Material2D nodes." + "cf bifs-2D-interactivity-touchsensor-isactive" + "The difference with the above test is the event propagation. Instead of routing the TouchSensor isActive event to both Material2D node, the event is only sent to one Material2D node, but since the emissiveColor field is an exposedField, it generate in turns a new event and that event is routed to the second Material2D node." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "TouchSensor, isActive & Event Propagation" + + } + Transform2D { + scale 0.5 0.5 + children [ + Transform2D { + translation 0 60 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + DEF TS1 TouchSensor {} + DEF TS2 TouchSensor {} + ] + } + Transform2D { + translation -100 -50 + children [ + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 1 1 0 + } + } + geometry Circle { + radius 50 + } + } + ] + } + Transform2D { + translation 100 -50 + children [ + Shape { + appearance Appearance { + material DEF M2 Material2D { + emissiveColor 0 1 1 + } + } + geometry Circle { + radius 50 + } + } + ] + } + ] + } + ] +} + +ROUTE TS1.isActive TO M1.filled +ROUTE M1.filled TO M2.filled + diff --git a/regression_tests/bifs-2D-interactivity-touchsensor-isactive.bt b/regression_tests/bifs-2D-interactivity-touchsensor-isactive.bt new file mode 100644 index 0000000..01ddc8f --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-touchsensor-isactive.bt @@ -0,0 +1,98 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 140 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows a TouchSensor node used to modify the filled property of 2 Material2D nodes." + "The TouchSensor node sends an isActive event each time a Mouse Button is pressed. The value of the event is TRUE if the Mouse is over a sibling shape of the TouchSensor node. Otherwise it is FALSE." + "In this example, the event value is routed to the filled property of 2 different circles. So, when the mouse is clicked on the square, both circled are filled and when the mouse is released, both circles become empty." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "TouchSensor & isActive" + } + Transform2D { + scale 0.5 0.5 + children [ + Transform2D { + translation 0 60 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + DEF TS1 TouchSensor {} + DEF TS2 TouchSensor {} + ] + } + Transform2D { + translation -100 -50 + children [ + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 1 1 0 + } + } + geometry Circle { + radius 50 + } + } + ] + } + Transform2D { + translation 100 -50 + children [ + Shape { + appearance Appearance { + material DEF M2 Material2D { + emissiveColor 0 1 1 + } + } + geometry Circle { + radius 50 + } + } + ] + } + ] + } + ] +} + +ROUTE TS1.isActive TO M1.filled +ROUTE TS2.isActive TO M2.filled + diff --git a/regression_tests/bifs-2D-interactivity-touchsensor-isover.bt b/regression_tests/bifs-2D-interactivity-touchsensor-isover.bt new file mode 100644 index 0000000..2a4251d --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-touchsensor-isover.bt @@ -0,0 +1,59 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 260 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows a TouchSensor node used to modify the filled property of a Material2D node." + "The TouchSensor node sends an isOver event when the Mouse enters or exits the region delimited by a Shape node, sibling of the TouchSensor node. The value is TRUE, when it enters, and FALSE when it leaves." + "In this example, the TouchSensor has only one sibling a rectangle, which turns empty when the mouse is not over it." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "TouchSensor & isOver" + } + Transform2D { + children [ + DEF TS TouchSensor {} + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 0 0 1 + } + } + geometry Rectangle { + size 200 100 + } + } + ] + } + ] +} + +ROUTE TS.isOver TO M1.filled + diff --git a/regression_tests/bifs-2D-interactivity-touchsensor-move_over.bt b/regression_tests/bifs-2D-interactivity-touchsensor-move_over.bt new file mode 100644 index 0000000..d8fcb70 --- /dev/null +++ b/regression_tests/bifs-2D-interactivity-touchsensor-move_over.bt @@ -0,0 +1,268 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 254 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 254 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how a TouchSensor node and a TimeSensor can be used to make a new Event called MoveOver, which is TRUE when the mouse moves over a Shape or FALSE when the mouse has not moved over the Shape for a certain period of time." + "This is an example to show how new events can be created based on existing tools and events (no scripting). This particular event simulates the Lingo MouseWithin event. " + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" + "(C) 2002-2006 GPAC Team" + ] + title "Move Over" + } + Transform2D { + children [ + Shape { + appearance Appearance { + material DEF M Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + DEF TS TouchSensor {} + ] + } + DEF V_ISOVER_TO_FACTOR Valuator { + } + DEF V_HITPOINT_CHANGED_TO_ONE Valuator { + Factor1 0 + Offset1 1 + } + DEF DUMMY_TIMER TimeSensor { enabled FALSE } + DEF T TimeSensor { + cycleInterval 1 + loop TRUE + startTime -1 + } + DEF STOP_TIME_OFFSETER Valuator { Offset1 0.1 } # minimum duration between two mouse move + + DEF START_TIMER Conditional { + buffer { + REPLACE DUMMY_TIMER.startTime BY 0 + REPLACE T.startTime BY 0 + } + } + DEF VALUATOR Valuator { + } + DEF V_MOVE_OVER Valuator { + } + DEF OUT Conditional { + buffer { + REPLACE T.stopTime BY 0 + } + } + DEF C Conditional { + buffer { + REPLACE M.emissiveColor BY 0 1 0 + } + } + DEF RC Conditional { + buffer { + REPLACE M.emissiveColor BY 1 0 0 + } + } + Transform2D { + translation 20 60 + children [ + Transform2D { + translation -80 0 + children [ + Transform2D { + translation 0 20 + children [ + Shape { + appearance DEF TEXT_APP Appearance { material Material2D { emissiveColor 0 0 0 filled TRUE } } + geometry Text { + string "Mouse Over & " + fontStyle DEF TEXT_STYLE FontStyle { size 10 family "SANS" justify ["MIDDLE" "MIDDLE" ] } + } + } + ] + } + Shape { + appearance USE TEXT_APP + geometry DEF MOUSE_OVER_TEXT Text { + fontStyle USE TEXT_STYLE + } + } + ] + } + Transform2D { + translation -10 0 + children [ + Transform2D { + translation 0 20 + children [ + Shape { + appearance USE TEXT_APP + geometry Text { + string [ " Mouse has " " moved recently =" ] + fontStyle USE TEXT_STYLE + } + } + ] + } + Shape { + appearance USE TEXT_APP + geometry DEF TIMER_STATUS_TEXT Text { + fontStyle USE TEXT_STYLE + } + } + ] + } + Transform2D { + translation 60 0 + children [ + Transform2D { + translation 0 20 + children [ + Shape { + appearance USE TEXT_APP + geometry Text { + string [ "Result" "Move Over" ] + fontStyle USE TEXT_STYLE + } + } + ] + } + Shape { + appearance USE TEXT_APP + geometry DEF RESULT_TEXT Text { + fontStyle USE TEXT_STYLE + } + } + ] + } + ] + } +# Transform2D { +# translation -60 -80 +# children [ +# Transform2D { +# translation 0 0 +# children [ +# Transform2D { +# translation 0 20 +# children [ +# Shape { +# appearance USE TEXT_APP +# geometry Text { +# string "Start Time" +# fontStyle USE TEXT_STYLE +# } +# } +# ] +# } +# Shape { +# appearance USE TEXT_APP +# geometry DEF START_TIME_TEXT Text { +# fontStyle USE TEXT_STYLE +# } +# } +# ] +# } +# Transform2D { +# translation 60 0 +# children [ +# Transform2D { +# translation 0 20 +# children [ +# Shape { +# appearance USE TEXT_APP +# geometry Text { +# string "Stop Time" +# fontStyle USE TEXT_STYLE +# } +# } +# ] +# } +# Shape { +# appearance USE TEXT_APP +# geometry DEF STOP_TIME_TEXT Text { +# fontStyle USE TEXT_STYLE +# } +# } +# ] +# } +# ] +# } +# DEF V_START_TIME Valuator {Factor1 100} +# DEF V_STOP_TIME Valuator {Factor1 100} + ] +} + +# These two routes do the following: +# read the startTime field of a DUMMY_TIMER (which is equal to NOW because, has been replaced by 0) +# offset this value by the appropriate delay +# set the stoptime value of the real timer +ROUTE DUMMY_TIMER.startTime TO STOP_TIME_OFFSETER.inSFTime +ROUTE STOP_TIME_OFFSETER.outSFTime TO T.stopTime + +# This is a route to force the stop of the timer when the mouse exits the shape +ROUTE TS.isOver TO OUT.reverseActivate + +# These 2 routes convert the isOver (a boolean) into a float +# and stores the value in the factor of a valuator +ROUTE TS.isOver TO V_ISOVER_TO_FACTOR.inSFBool +ROUTE V_ISOVER_TO_FACTOR.outSFFloat TO V_MOVE_OVER.Factor1 + +# Display the status of the IsOver boolean +ROUTE V_ISOVER_TO_FACTOR.outMFString TO MOUSE_OVER_TEXT.string + +#These 2 routes create an event (Boolean = TRUE) whenever the mouse moves +#and activates a conditional +ROUTE TS.hitPoint_changed TO V_HITPOINT_CHANGED_TO_ONE.inSFVec3f +ROUTE V_HITPOINT_CHANGED_TO_ONE.outSFBool TO START_TIMER.activate + +# When the timer is active, it means the mouse is moving, we route this event to the move_over valuator +ROUTE T.isActive TO V_MOVE_OVER.inSFBool + +# Display of the status of the timer +ROUTE T.isActive TO VALUATOR.inSFBool +ROUTE VALUATOR.outMFString TO TIMER_STATUS_TEXT.string + +# Trigger one action when the result is TRUE +ROUTE V_MOVE_OVER.outSFBool TO C.activate +# Trigger one action when the result is FALSE +ROUTE V_MOVE_OVER.outSFBool TO RC.reverseActivate + +# Display of the final result +ROUTE V_MOVE_OVER.outMFString TO RESULT_TEXT.string + +# Display of the startTime of the Timer +#ROUTE T.startTime TO V_START_TIME.inSFTime +#ROUTE V_START_TIME.outMFString TO START_TIME_TEXT.string + +# Display of the stopTime of the Timer +#ROUTE T.stopTime TO V_STOP_TIME.inSFTime +#ROUTE V_STOP_TIME.outMFString TO STOP_TIME_TEXT.string \ No newline at end of file diff --git a/regression_tests/bifs-2D-painting-colortransform-alpha.bt b/regression_tests/bifs-2D-painting-colortransform-alpha.bt new file mode 100644 index 0000000..5569742 --- /dev/null +++ b/regression_tests/bifs-2D-painting-colortransform-alpha.bt @@ -0,0 +1,101 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to modify the color components on a graphical object using the ColorTransform node." + "A group made of a circle and a rectangle is reused under a ColorTransform node which animates only the Red-Blue component from 0 to 1" + "Note: the transparency is specified for a set of objects but is applied individually, that is why the overlapping region of the circle and of the rectangle gets black faster than the rest of the group." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "ColorTranform" + } + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 1 + filled TRUE + } + } + geometry Rectangle { + size 200 50 + } + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 1 + filled TRUE + } + } + geometry Circle { + radius 50 + } + } + ] + } + DEF CT ColorTransform { + mrr 0 + mgg 0 + mbb 0 + maa 0 + ta 1 + children [ + DEF MX TransformMatrix2D { + mxx 0.5 + mxy 0.5 + myy 0.5 + ty -100 + children [ + USE TR + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF CI PositionInterpolator2D { + key [0 0.5 1] + keyValue [0 0 100 100 0 0] + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO CT.ta + diff --git a/regression_tests/bifs-2D-painting-colortransform-bitmap.bt b/regression_tests/bifs-2D-painting-colortransform-bitmap.bt new file mode 100644 index 0000000..908351a --- /dev/null +++ b/regression_tests/bifs-2D-painting-colortransform-bitmap.bt @@ -0,0 +1,109 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to modify the color components of an image using the ColorTransform node." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "ColorTranform on an image" + } + DEF TR Transform2D { + children [ + DEF S Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 1 + filled TRUE + } + texture ImageTexture { + url ["od:2"] + } + } + geometry Rectangle { size 200 80} + } + ] + } + DEF CT ColorTransform { + mrr 1 + mgg 1 + mbb 0.2 + tr 0 + children [ + Transform2D { + translation 0 -100 + children [ + USE S + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF CI PositionInterpolator2D { + key [0 0.5 1] + keyValue [0 0 100 100 0 0] + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1 0] + } + ] +} + +ROUTE TS.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO TR.translation +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO CT.mrb +ROUTE TS.fraction_changed TO CT.maa + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 2 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ] +} diff --git a/regression_tests/bifs-2D-painting-colortransform-color.bt b/regression_tests/bifs-2D-painting-colortransform-color.bt new file mode 100644 index 0000000..2746a73 --- /dev/null +++ b/regression_tests/bifs-2D-painting-colortransform-color.bt @@ -0,0 +1,102 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to modify the color components on a graphical object using the ColorTransform node." + "A group made of a circle and a rectangle is reused under a ColorTransform node which sets the color components to 0 and the alpha component is animated from 0 to 1" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "ColorTranform and color changes" + } + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 1 + filled TRUE + } + } + geometry Rectangle { + size 200 50 + } + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 1 + filled TRUE + } + } + geometry Circle { + radius 50 + } + } + ] + } + DEF CT ColorTransform { + mrr 0 + mgg 0 + mbb 0 + maa 0 + ta 1 + children [ + DEF MX TransformMatrix2D { + mxx 0.5 + mxy 0.5 + myy 0.5 + ty -100 + children [ + USE TR + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF CI PositionInterpolator2D { + key [0 0.5 1] + keyValue [0 0 100 100 0 0] + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1 0] + } + ] +} + +ROUTE TS.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO TR.translation +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO CT.mrb + diff --git a/regression_tests/bifs-2D-painting-lineproperties.bt b/regression_tests/bifs-2D-painting-lineproperties.bt new file mode 100644 index 0000000..0e3ba88 --- /dev/null +++ b/regression_tests/bifs-2D-painting-lineproperties.bt @@ -0,0 +1,168 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 500 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to set basic outline properties, using the LineProperties node" + "The properties are width, lineColor and lineStyle." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" + "(C) 2002-2006 GPAC Team" + ] + title "Setting simple line properties - the LineProperties node" + } + DEF TR1 Transform2D { + translation -190 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0 0 1 + lineStyle 0 + width 15 + } + } + } + geometry DEF LS IndexedLineSet2D { + coord Coordinate2D { + point [-50 0 0 50 50 0] + } + } + } + ] + } + Transform2D { + translation -190 -30 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["lineStyle 0"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 16 + } + } + } + ] + } + DEF TR2 Transform2D { + translation -65 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + lineColor 1 0 0 + lineStyle 1 + width 15 + } + } + } + geometry USE LS + } + ] + } + Transform2D { + translation -65 -30 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["lineStyle 1"] + fontStyle USE FS + } + } + ] + } + DEF TR3 Transform2D { + translation 65 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 0 1 1 + lineStyle 2 + width 15 + } + } + } + geometry USE LS + } + ] + } + Transform2D { + translation 65 -30 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["lineStyle 2"] + fontStyle USE FS + } + } + ] + } + DEF TR4 Transform2D { + translation 190 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 0 1 0 + lineStyle 3 + width 15 + } + } + } + geometry USE LS + } + ] + } + Transform2D { + translation 190 -30 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["lineStyle 3"] + fontStyle USE FS + } + } + ] + } + ] +} diff --git a/regression_tests/bifs-2D-painting-material2D.bt b/regression_tests/bifs-2D-painting-material2D.bt new file mode 100644 index 0000000..329dd82 --- /dev/null +++ b/regression_tests/bifs-2D-painting-material2D.bt @@ -0,0 +1,154 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to specify the filling and simple outlining of 2D shapes" + "It uses the Material2D node. The filled property indicates if the shape is filled or not. The emissiveColor property specifies the fill color if filled or the outline color if not filled. Transparency can be set using the transparency property." + "In this simple example, the width and other properties of the outline cannot be set. To set more properties, use the LineProperties or XLineProperties nodes." + "cf bifs-2D-painting-lineproperties" + "cf bifs-2D-painting-xlineproperties-cap" + "cf bifs-2D-painting-xlineproperties-compositetexture2D" + "cf bifs-2D-painting-xlineproperties-dash" + "cf bifs-2D-painting-xlineproperties-imagetexture" + "cf bifs-2D-painting-xlineproperties-join" + "cf bifs-2D-painting-xlineproperties-lineargradient" + "cf bifs-2D-painting-xlineproperties-radialgradient" + "cf bifs-2D-painting-xlineproperties-scalable" + "cf bifs-2D-painting-xlineproperties-transparent" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.4 $" + "(C) 2002-2006 GPAC Team"] + title "Material2D properties" + } + Transform2D { + translation -180 0 + children [ + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Rectangle { + size 80 80 + } + } + ] + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 1 + } + } + geometry DEF REC Rectangle { + size 150 100 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Not filled, Color 1 0 1"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 14 + } + } + } + ] + } + ] + } + Transform2D { + children [ + USE TR + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 1 + filled TRUE + } + } + geometry USE REC + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["filled, Color 1 0 1"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 180 0 + children [ + USE TR + Shape { + appearance Appearance { + material DEF M Material2D { + emissiveColor 1 0 1 + filled TRUE + transparency 0.6 + } + } + geometry USE REC + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["filled, Color 1 0 1" "transparency 0.6"] + fontStyle USE FS + } + } + ] + } + ] + } + ] +} diff --git a/regression_tests/bifs-2D-painting-xlineproperties-cap.bt b/regression_tests/bifs-2D-painting-xlineproperties-cap.bt new file mode 100644 index 0000000..c98e188 --- /dev/null +++ b/regression_tests/bifs-2D-painting-xlineproperties-cap.bt @@ -0,0 +1,211 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 500 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the usage of cap styles of the XLineProperties node" + "Moving the mouse over the 'Anim' button will superpose the different lines to highlight the linecap differences" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Changing line caps using the XLineProperties node" + } + DEF TR1 Transform2D { + translation -190 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 0 0 1 + lineCap 2 + width 15 + } + } + } + geometry DEF LS IndexedLineSet2D { + coord Coordinate2D { + point [-50 0 0 50 50 0] + } + } + } + ] + } + Transform2D { + translation -190 -30 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Cap square"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 16 + } + } + } + ] + } + DEF TR2 Transform2D { + translation -65 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 1 0 0 + lineCap 1 + width 15 + } + } + } + geometry USE LS + } + ] + } + Transform2D { + translation -65 -30 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Cap round"] + fontStyle USE FS + } + } + ] + } + DEF TR3 Transform2D { + translation 65 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 0 1 1 + lineCap 3 + width 15 + } + } + } + geometry USE LS + } + ] + } + Transform2D { + translation 65 -30 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Cap triangle"] + fontStyle USE FS + } + } + ] + } + DEF TR4 Transform2D { + translation 190 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 0 1 0 + width 15 + } + } + } + geometry USE LS + } + ] + } + Transform2D { + translation 190 -30 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Cap 'butt'"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 225 -75 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 1 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Anim"] + fontStyle USE FS + } + } + DEF TS TouchSensor {} + ] + } + DEF C Conditional { + buffer { + REPLACE TR1.translation BY 0 0 + REPLACE TR2.translation BY 0 0 + REPLACE TR3.translation BY 0 0 + REPLACE TR4.translation BY 0 0 + } + } + DEF RC Conditional { + buffer { + REPLACE TR1.translation BY -190 0 + REPLACE TR2.translation BY -65 0 + REPLACE TR3.translation BY 65 0 + REPLACE TR4.translation BY 190 0 + } + } + ] +} + +ROUTE TS.isOver TO C.activate +ROUTE TS.isOver TO RC.reverseActivate + diff --git a/regression_tests/bifs-2D-painting-xlineproperties-compositetexture2D.bt b/regression_tests/bifs-2D-painting-xlineproperties-compositetexture2D.bt new file mode 100644 index 0000000..71bf4a4 --- /dev/null +++ b/regression_tests/bifs-2D-painting-xlineproperties-compositetexture2D.bt @@ -0,0 +1,153 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This shows line texturing with a composite texture using the CompositeTexture2D node using variable width and different line alignment" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Complex line texturing with the XLineProperties node" + } + Transform2D { + translation 0 150 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Line width varying between 0 and 100"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -140 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps DEF XLP XLineProperties { + width 20 + textureTransform TextureTransform { scale 8 8} + texture DEF IM CompositeTexture2D { + pixelWidth 32 + pixelHeight 32 + children [ + Shape { + appearance Appearance { + material Material2D { emissiveColor 0 1 0 filled TRUE } + } + geometry DEF C Circle { + radius 6 + } + } + ] + } + } + } + } + geometry Rectangle { + size 150 150 + } + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Line centered on shape"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 160 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps DEF XLP2 XLineProperties { + isCenterAligned FALSE + width 0 + texture USE IM + } + } + } + geometry Rectangle { + size 150 150 + } + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Line inside shape"] + fontStyle USE FS + } + } + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 100 0] + } + DEF SI2 ScalarInterpolator { + key [0 0.5 1] + keyValue [6 10 6] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO XLP.width +ROUTE SI.value_changed TO XLP2.width +ROUTE TS.fraction_changed TO SI2.set_fraction +ROUTE SI2.value_changed TO C.radius + diff --git a/regression_tests/bifs-2D-painting-xlineproperties-dash.bt b/regression_tests/bifs-2D-painting-xlineproperties-dash.bt new file mode 100644 index 0000000..d873544 --- /dev/null +++ b/regression_tests/bifs-2D-painting-xlineproperties-dash.bt @@ -0,0 +1,142 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the usage of line dashing using the XLineProperties node." + "Two properties are demonstrated: dash pattern and dash offset. The offset is animated on the left, the dash length is animated on the right." + "" + "GPAC Regression Tests" "$Date: 2007/09/14 16:42:18 $ - $Revision: 1.4 $" + "(C) 2002-2006 GPAC Team" + ] + title "Animating the line dashes using the XLineProperties node" + } + Transform2D { + translation 140 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps DEF XLP1 XLineProperties { + lineColor 0 0 1 + lineStyle 6 + isScalable TRUE + lineCap 1 + width 20 + dashes [0.1 80] + } + } + } + geometry DEF LS Curve2D { + fineness 1 + type [2] + point Coordinate2D { + point [-100 0 -50 200 50 -200 100 0] + } + } + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["dash pattern animation"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] + } + Transform2D { + translation -140 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps DEF XLP2 XLineProperties { + lineColor 0 0 1 + lineStyle 6 + isScalable TRUE + lineCap 1 + width 20 + dashes [3 80] + } + } + } + geometry USE LS + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["dash offset animation"] + fontStyle USE FS + } + } + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval 3 + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 16 0] + } + DEF CI CoordinateInterpolator2D { + key [0 0.5 1] + keyValue [0.2 0 80 0 17.5 0 80 0 0.2 0 80 0] + } + DEF V Valuator { + Factor2 0 + Factor3 0 + Factor4 0 + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO XLP2.dashOffset +ROUTE TS.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO V.inMFVec2f +ROUTE V.outMFFloat TO XLP1.dashes + diff --git a/regression_tests/bifs-2D-painting-xlineproperties-imagetexture.bt b/regression_tests/bifs-2D-painting-xlineproperties-imagetexture.bt new file mode 100644 index 0000000..5c4b010 --- /dev/null +++ b/regression_tests/bifs-2D-painting-xlineproperties-imagetexture.bt @@ -0,0 +1,159 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 560 + pixelHeight 420 + } + } + } + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This shows line texturing with an image using variable width and different line alignment" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Line texturing with Images - the XLineProperties node" + } + Transform2D { + translation 0 140 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Line width varying between 0 and 100"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -140 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps DEF XLP XLineProperties { + width 20 + texture DEF IM ImageTexture { + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + } + } + geometry Rectangle { + size 150 150 + } + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Line centered on shape"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 140 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps DEF XLP2 XLineProperties { + isCenterAligned FALSE + width 0 + texture USE IM + } + } + } + geometry Rectangle { + size 150 150 + } + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Line inside shape"] + fontStyle USE FS + } + } + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 100 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO XLP.width +ROUTE SI.value_changed TO XLP2.width + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-painting-xlineproperties-join.bt b/regression_tests/bifs-2D-painting-xlineproperties-join.bt new file mode 100644 index 0000000..ecba234 --- /dev/null +++ b/regression_tests/bifs-2D-painting-xlineproperties-join.bt @@ -0,0 +1,215 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the usage of line join styles of the XLineProperties node" + "Moving the mouse over the 'Anim' button will superpose the different lines to highlight the linejoin differences" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Changing line caps using the XLineProperties node" + } + DEF TR1 Transform2D { + translation -200 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 0 0 1 + width 20 + } + } + } + geometry DEF LS IndexedLineSet2D { + coord Coordinate2D { + point [-50 0 0 50 50 0] + } + } + } + ] + } + Transform2D { + translation -200 -50 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Join Miter" "miterLimit: default"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 16 + } + } + } + ] + } + DEF TR3 Transform2D { + translation 70 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 0 1 0 + lineJoin 1 + width 20 + } + } + } + geometry USE LS + } + ] + } + Transform2D { + translation 70 -50 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Join Round"] + fontStyle USE FS + } + } + ] + } + + + DEF TR4 Transform2D { + translation 200 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 1 0 1 + lineJoin 2 + width 20 + } + } + } + geometry USE LS + } + ] + } + Transform2D { + translation 200 -50 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Join Bevel"] + fontStyle USE FS + } + } + ] + } + + DEF TR2 Transform2D { + translation -70 0 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 1 0 0 + miterLimit 0.4 + width 20 + } + } + } + geometry USE LS + } + ] + } + Transform2D { + translation -70 -50 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Join Miter" "miterLimit: 0.4"] + fontStyle USE FS + } + } + ] + } + + Transform2D { + translation 280 -75 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 1 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Anim"] + fontStyle USE FS + } + } + DEF TS TouchSensor {} + ] + } + DEF C Conditional { + buffer { + REPLACE TR1.translation BY 0 0 + REPLACE TR2.translation BY 0 0 + REPLACE TR3.translation BY 0 0 + REPLACE TR4.translation BY 0 0 + } + } + DEF RC Conditional { + buffer { + REPLACE TR1.translation BY -190 0 + REPLACE TR2.translation BY -65 0 + REPLACE TR3.translation BY 65 0 + REPLACE TR4.translation BY 190 0 + } + } + ] +} + +ROUTE TS.isOver TO C.activate +ROUTE TS.isOver TO RC.reverseActivate + diff --git a/regression_tests/bifs-2D-painting-xlineproperties-lineargradient.bt b/regression_tests/bifs-2D-painting-xlineproperties-lineargradient.bt new file mode 100644 index 0000000..f416069 --- /dev/null +++ b/regression_tests/bifs-2D-painting-xlineproperties-lineargradient.bt @@ -0,0 +1,122 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows line texturing with a linear gradient with different spread method and animated end point" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team"] + title "Linear Gradient and Lines using XLineProperties" + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Move over and click" "to change the spread method"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + TransformMatrix2D { + mxy 0.5 + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + lineProps XLineProperties { + width 40 + texture DEF GL LinearGradient { + endPoint 0 0.5 + key [0 0.5 1] + keyValue [0 0 1 1 0 0 0 1 0] + } + } + } + } + geometry DEF R Rectangle { + size 200 200 + } + } + DEF TS TouchSensor {} + ] + } + ] + } + DEF C Conditional { + buffer { + REPLACE GL.spreadMethod BY 2 + } + } + DEF RC Conditional { + buffer { + REPLACE GL.spreadMethod BY 0 + } + } + DEF C2 Conditional { + buffer { + REPLACE GL.spreadMethod BY 1 + } + } + DEF RC2 Conditional { + buffer { + REPLACE GL.spreadMethod BY 2 + } + } + DEF TIME TimeSensor { + cycleInterval 4 + loop TRUE + } + DEF CI PositionInterpolator2D { + key [0 0.5 1] + keyValue [0 0.5 1 0.5 0 0.5] + } + ] +} + +ROUTE TS.isOver TO C.activate +ROUTE TS.isOver TO RC.reverseActivate +ROUTE TS.isActive TO C2.activate +ROUTE TS.isActive TO RC2.reverseActivate +ROUTE TIME.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO GL.endPoint + diff --git a/regression_tests/bifs-2D-painting-xlineproperties-radialgradient.bt b/regression_tests/bifs-2D-painting-xlineproperties-radialgradient.bt new file mode 100644 index 0000000..b403696 --- /dev/null +++ b/regression_tests/bifs-2D-painting-xlineproperties-radialgradient.bt @@ -0,0 +1,124 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows line texturing with a radial gradient with different spread method and animated focal point" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team"] + title "Radial Gradient on lines using XLineProperties node" + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Move over and click" "to change the spread method"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + DEF TR TransformMatrix2D { + mxy 1 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + isCenterAligned TRUE + width 40 + texture DEF GR RadialGradient { + focalPoint 0 0.5 + key [0 0.5 1] + keyValue [0 0 1 1 0 0 0 1 0] + } + } + } + } + geometry DEF R Rectangle { + size 200 200 + } + } + DEF TS TouchSensor {} + ] + } + DEF C Conditional { + buffer { + REPLACE GR.spreadMethod BY 2 + } + } + DEF RC Conditional { + buffer { + REPLACE GR.spreadMethod BY 0 + } + } + DEF C2 Conditional { + buffer { + REPLACE GR.spreadMethod BY 1 + } + } + DEF RC2 Conditional { + buffer { + REPLACE GR.spreadMethod BY 2 + } + } + DEF TIME TimeSensor { + cycleInterval 4 + loop TRUE + } + DEF CI PositionInterpolator2D { + key [0 0.25 0.5 0.75 1] + keyValue [0 0.5 0.5 1 1 0.5 0.5 0 0 0.5] + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1 0] + } + ] +} + +ROUTE TS.isOver TO C.activate +ROUTE TS.isOver TO RC.reverseActivate +ROUTE TS.isActive TO C2.activate +ROUTE TS.isActive TO RC2.reverseActivate +ROUTE TIME.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO GR.focalPoint +ROUTE TIME.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO TR.mxy + diff --git a/regression_tests/bifs-2D-painting-xlineproperties-scalable.bt b/regression_tests/bifs-2D-painting-xlineproperties-scalable.bt new file mode 100644 index 0000000..62cd528 --- /dev/null +++ b/regression_tests/bifs-2D-painting-xlineproperties-scalable.bt @@ -0,0 +1,123 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the usage of scalable flag in the XLineProperties node" + "We see that the right line's width changes during the animation while the left one does not change." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2004 GPAC Team" + ] + title "Scalability of the outline - the XLineProperties node" + } + DEF TR1 Transform2D { + scale 2 2 + translation -150 -50 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0 0 1 + width 2 + } + } + } + geometry DEF LS IndexedLineSet2D { + coord Coordinate2D { + point [-50 0 0 50 50 0] + } + } + } + ] + } + Transform2D { + translation -150 -50 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Not scalable"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 16 + } + } + } + ] + } + DEF TR2 Transform2D { + scale 2 2 + translation 150 -50 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps XLineProperties { + lineColor 0 0 1 + width 2 + } + } + } + geometry USE LS + } + ] + } + Transform2D { + translation 150 -50 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Scalable"] + fontStyle USE FS + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 4 + loop TRUE + } + DEF PI PositionInterpolator2D { + key [0 0.5 1] + keyValue [1 1 3 3 1 1] + } + ] +} + +ROUTE TS.fraction_changed TO PI.set_fraction +ROUTE PI.value_changed TO TR1.scale +ROUTE PI.value_changed TO TR2.scale + diff --git a/regression_tests/bifs-2D-painting-xlineproperties-transparent.bt b/regression_tests/bifs-2D-painting-xlineproperties-transparent.bt new file mode 100644 index 0000000..0f7f027 --- /dev/null +++ b/regression_tests/bifs-2D-painting-xlineproperties-transparent.bt @@ -0,0 +1,176 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows usage of line transparency and line alignment" + "The outline or its outside is centered on the shape depending on the value of the isCenterAligned." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Positioning lines and setting transparency using the XLineProperties node" + } + Transform2D { + translation -100 100 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + lineProps XLineProperties { + lineColor 0 0 1 + width 10 + } + } + } + geometry DEF RC Rectangle { + size 150 100 + } + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance DEF TXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["No line transparency" "CenterAligned TRUE"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + ] + } + Transform2D { + translation 100 100 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + lineProps XLineProperties { + lineColor 0 0 1 + isCenterAligned FALSE + width 10 + } + } + } + geometry USE RC + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["No line transparency" "CenterAligned FALSE"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation -100 -80 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + lineProps XLineProperties { + lineColor 0 0 1 + transparency 0.5 + width 10 + } + } + } + geometry USE RC + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["Line transparency: 0.5" "CenterAligned TRUE"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 100 -80 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + lineProps XLineProperties { + lineColor 0 0 1 + isCenterAligned FALSE + transparency 0.5 + width 10 + } + } + } + geometry USE RC + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["Line transparency: 0.5" "CenterAligned FALSE"] + fontStyle USE FS + } + } + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-clipper2D.bt b/regression_tests/bifs-2D-positioning-clipper2D.bt new file mode 100644 index 0000000..9e9a623 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-clipper2D.bt @@ -0,0 +1,316 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + url [od:5] + } + WorldInfo { + info ["This shows combination of Clipper2D objects" "with different modes" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Clipper2D Test" + } + Transform2D { + scale 0.5 0.5 + translation -200 100 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 0 + filled TRUE + } + } + geometry Rectangle { + size 200 200 + } + } + DEF PS PlaneSensor2D { + maxPosition 150 150 + minPosition -150 -150 + } + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Translate" "round" "cliper"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 40 + } + } + } + ] + } + Transform2D { + scale 0.5 0.5 + translation -80 100 + children [ + DEF S1 Switch { + whichChoice 0 + choice [ + Group { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Round Clipper" "inside = FALSE"] + fontStyle USE FS + } + } + DEF TS1 TouchSensor {} + ] + } + Group { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Round Clipper" "inside = TRUE"] + fontStyle USE FS + } + } + DEF RTS1 TouchSensor {} + ] + } + ] + } + ] + } + Transform2D { + scale 0.5 0.5 + translation -80 0 + children [ + DEF SX1 Switch { + whichChoice 0 + choice [ + Group { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["XOR = FALSE"] + fontStyle USE FS + } + } + DEF TSX1 TouchSensor {} + ] + } + Group { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["XOR = TRUE"] + fontStyle USE FS + } + } + DEF RTSX1 TouchSensor {} + ] + } + ] + } + ] + } + Transform2D { + scale 0.5 0.5 + translation -200 -120 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 1 + filled TRUE + } + } + geometry Circle { + radius 100 + } + } + DEF DS DiscSensor {} + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Rotate" "square" "cliper"] + fontStyle USE FS + } + } + ] + } + Transform2D { + scale 0.5 0.5 + translation -80 -120 + children [ + DEF S2 Switch { + whichChoice 0 + choice [ + Group { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Rect Clipper" "inside = FALSE"] + fontStyle USE FS + } + } + DEF TS2 TouchSensor {} + ] + } + Group { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Rect Clipper" "inside = TRUE"] + fontStyle USE FS + } + } + DEF RTS2 TouchSensor {} + ] + } + ] + } + ] + } + Transform2D { + translation 150 0 + children [ + DEF CLIP_SQ Clipper2D { + inside FALSE + children [ + DEF CLIP_RD Clipper2D { + inside FALSE + children [ + Shape { + appearance Appearance { + texture ImageTexture { + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry Circle { + radius 100 + } + } + ] + geometry Circle { + radius 50 + } + transform DEF TR_RD Transform2D {} + + } + ] + geometry Rectangle { + size 200 75 + } + transform DEF TR_SQ Transform2D {} + + } + ] + } + DEF C1 Conditional { + buffer { + REPLACE CLIP_RD.inside BY TRUE + REPLACE S1.whichChoice BY 1 + } + } + DEF RC1 Conditional { + buffer { + REPLACE CLIP_RD.inside BY FALSE + REPLACE S1.whichChoice BY 0 + } + } + DEF CX1 Conditional { + buffer { + REPLACE CLIP_RD.XOR BY TRUE + REPLACE SX1.whichChoice BY 1 + } + } + DEF RCX1 Conditional { + buffer { + REPLACE CLIP_RD.XOR BY FALSE + REPLACE SX1.whichChoice BY 0 + } + } + DEF C2 Conditional { + buffer { + REPLACE CLIP_SQ.inside BY TRUE + REPLACE S2.whichChoice BY 1 + } + } + DEF RC2 Conditional { + buffer { + REPLACE CLIP_SQ.inside BY FALSE + REPLACE S2.whichChoice BY 0 + } + } + ] +} + +ROUTE PS.translation_changed TO TR_RD.translation +ROUTE DS.rotation_changed TO TR_SQ.rotationAngle +ROUTE TS1.isActive TO C1.activate +ROUTE RTS1.isActive TO RC1.activate +ROUTE TSX1.isActive TO CX1.activate +ROUTE RTSX1.isActive TO RCX1.activate +ROUTE TS2.isActive TO C2.activate +ROUTE RTS2.isActive TO RC2.activate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 5 + esDescr [ + ES_Descriptor { + ES_ID 10 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-positioning-form-align-center.bt b/regression_tests/bifs-2D-positioning-form-align-center.bt new file mode 100644 index 0000000..d8f9332 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-form-align-center.bt @@ -0,0 +1,162 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Form node" "with horizontal and vertical center alignment" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Form Test" + } + Transform2D { + translation -100 0 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 100 200 + } + } + Transform2D { + translation 0 -120 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Constraints: AT AB AH "] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Form { + size 100 200 + groups [1 -1 2 -1] + constraints ["AT" "AB" "AH"] + groupsIndex [0 1 -1 0 2 -1 1 2 -1] + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 30 + } + } + DEF S2 Transform2D { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 60 20 + } + } + ] + } + Transform2D { + translation -20 -20 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + ] + } + ] + } + ] + } + ] + } + Transform2D { + translation 75 0 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 200 100 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Constraints: AL AR AV"] + fontStyle USE FS + } + } + ] + } + Form { + size 200 100 + groups [1 -1 2 -1] + constraints ["AL" "AR" "AV"] + groupsIndex [0 1 -1 0 2 -1 1 2 -1] + children [ + USE S1 + USE S2 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-form-align-horiz.bt b/regression_tests/bifs-2D-positioning-form-align-horiz.bt new file mode 100644 index 0000000..f8c7234 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-form-align-horiz.bt @@ -0,0 +1,129 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Form node" "with horizontal alignment" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Form Test" + } + Transform2D { + translation -75 0 + children [ + DEF FORM Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 100 200 + } + } + Transform2D { + translation 0 -120 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Constraints: AL AL 10"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Form { + size 100 200 + groups [1 -1 2 -1] + constraints ["AL" "AL 10"] + groupsIndex [0 1 -1 1 2 -1] + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 30 + } + } + DEF S2 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + ] + } + ] + } + Transform2D { + translation 75 0 + children [ + USE FORM + Transform2D { + translation 0 -120 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Constraints: AR AR 10"] + fontStyle USE FS + } + } + ] + } + Form { + size 100 200 + groups [1 -1 2 -1] + constraints ["AR" "AR 10"] + groupsIndex [0 1 -1 1 2 -1] + children [ + USE S1 + USE S2 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-form-align-vert.bt b/regression_tests/bifs-2D-positioning-form-align-vert.bt new file mode 100644 index 0000000..f6e6018 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-form-align-vert.bt @@ -0,0 +1,129 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Form node" "with vertical alignment" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Form Test" + } + Transform2D { + translation 0 75 + children [ + DEF FORM Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 200 100 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Constraints: AT AT 10"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Form { + size 200 100 + groups [1 -1 2 -1] + constraints ["AT" "AT 10"] + groupsIndex [0 1 -1 1 2 -1] + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 30 + } + } + DEF S2 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + ] + } + ] + } + Transform2D { + translation 0 -75 + children [ + USE FORM + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Constraints: AB AB 10"] + fontStyle USE FS + } + } + ] + } + Form { + size 200 100 + groups [1 -1 2 -1] + constraints ["AB" "AB 10"] + groupsIndex [0 1 -1 1 2 -1] + children [ + USE S1 + USE S2 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-form-spread-horiz.bt b/regression_tests/bifs-2D-positioning-form-spread-horiz.bt new file mode 100644 index 0000000..adb335e --- /dev/null +++ b/regression_tests/bifs-2D-positioning-form-spread-horiz.bt @@ -0,0 +1,182 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Form node" "with different horizontal spreads" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Form Test" + } + Transform2D { + translation 0 130 + children [ + DEF FORM Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 200 100 + } + } + Transform2D { + translation 0 -65 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Constraints: AL SH 10"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Form { + size 200 100 + groups [1 -1 2 -1 3 -1] + constraints ["AL" "SH 10"] + groupsIndex [0 1 -1 1 2 3 -1] + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 30 + } + } + DEF S2 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 60 40 + } + } + ] + } + ] + } + Transform2D { + children [ + USE FORM + Transform2D { + translation 0 -65 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Constraints: AL AR SH"] + fontStyle USE FS + } + } + ] + } + Form { + size 200 100 + groups [1 -1 2 -1 3 -1] + constraints ["AL" "AR" "SH"] + groupsIndex [0 1 -1 0 3 -1 1 2 3 -1] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 -130 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 110 100 + } + } + Transform2D { + translation 0 -65 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Constraints: SHin"] + fontStyle USE FS + } + } + ] + } + Form { + size 110 100 + groups [1 -1 2 -1] + constraints ["SHin"] + groupsIndex [1 2 -1] + children [ + Transform2D { + children [ + USE S1 + USE S2 + ] + } + USE S3 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-form-spread-vert.bt b/regression_tests/bifs-2D-positioning-form-spread-vert.bt new file mode 100644 index 0000000..31cce0a --- /dev/null +++ b/regression_tests/bifs-2D-positioning-form-spread-vert.bt @@ -0,0 +1,170 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Form node" "with different vertical spreads" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Form Test" + } + Transform2D { + translation -120 20 + children [ + DEF FORM Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 100 200 + } + } + Transform2D { + translation 0 -120 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Constraints:" "AT SV 10"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Form { + size 100 200 + groups [1 -1 2 -1 3 -1] + constraints ["AT" "SV 10"] + groupsIndex [0 1 -1 1 2 3 -1] + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 30 + } + } + DEF S2 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 60 40 + } + } + ] + } + ] + } + Transform2D { + translation 0 20 + children [ + USE FORM + Transform2D { + translation 0 -120 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Constraints:" "AT AB SV"] + fontStyle USE FS + } + } + ] + } + Form { + size 100 200 + groups [1 -1 2 -1 3 -1] + constraints ["AT" "AB" "SV"] + groupsIndex [0 1 -1 0 3 -1 1 2 3 -1] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 120 20 + children [ + USE FORM + Transform2D { + translation 0 -120 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Constraints:" "SVin"] + fontStyle USE FS + } + } + ] + } + Form { + size 100 200 + groups [1 -1 2 -1 3 -1] + constraints ["SVin"] + groupsIndex [1 2 3 -1] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layer2D.bt b/regression_tests/bifs-2D-positioning-layer2D.bt new file mode 100644 index 0000000..630cbd2 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layer2D.bt @@ -0,0 +1,129 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { backColor 1 1 1 } + WorldInfo { + info [ + "This test shows the usage of the Layer2D node." + "A 2D layer is defined with a size 200x200, its content is a grey square and a grey ellipse." + "Within the layer, the content is clipped along the 200x200 rectangle and filled with a red background." + "Behind this layer, a rotated blue rectangle is displayed." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Layer2D - Rectangular Axis Aligned Clipping" + } + Transform2D { + translation 125 125 + children [ + Shape { + appearance Appearance { material Material2D { filled TRUE emissiveColor 1 1 0 } } + geometry Rectangle { size 50 50 } + } + Shape { + appearance Appearance { material Material2D { filled TRUE emissiveColor 0 0 0 } } + geometry Text { string "Click Me" fontStyle FontStyle { justify [ "MIDDLE" "MIDDLE" ] size 10 }} + } + DEF S TouchSensor {} + ] + } + Transform2D { + rotationAngle 0.57 + children [ + Shape { + appearance Appearance { + material Material2D { + filled TRUE + emissiveColor 0 0 1 + } + } + geometry Rectangle { + size 100 400 + } + } + ] + } + DEF ROT Transform2D { + translation 0 0 + children [ + Layer2D { + size 200 200 + children [ + Background2D { backColor 1 0 0 } + Transform2D { + translation -100 0 + children [ + Shape { + appearance Appearance { + material Material2D { + filled TRUE + } + } + geometry Ellipse { + radius 100 400 + } + } + ] + } + Transform2D { + translation 25 0 + children [ + Shape { + appearance Appearance { + material Material2D { + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + ] + } + ] + } + ] + } + DEF C Conditional { + buffer { + REPLACE TS.startTime BY 0 + } + } + DEF TS TimeSensor { + cycleInterval 5 + startTime -1 + } + DEF SI ScalarInterpolator { + key [0 1] + keyValue [0 1.57] + } + ] +} + +ROUTE S.isActive TO C.activate +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO ROT.rotationAngle + diff --git a/regression_tests/bifs-2D-positioning-layer2d-in-layer2d.bt b/regression_tests/bifs-2D-positioning-layer2d-in-layer2d.bt new file mode 100644 index 0000000..07c5148 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layer2d-in-layer2d.bt @@ -0,0 +1,111 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFE + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + DEF B1 Background2D {backColor 1 1 1} + WorldInfo { + title "Nested Clipping - Layer2D" + info [ + "This test shows nested Layer2D nodes." + "Both layer sizes are animated" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + } + + Transform2D { + translation 100 0 + children [ + DEF L1 Layer2D { + size 300 300 + background Background2D {url 10} + children [ + Shape { + appearance Appearance { material Material2D { filled TRUE } } + geometry DEF RC Rectangle {size 100 75} + } + Transform2D { + translation -150 0 + children [ + DEF L2 Layer2D { + size 100 100 + background Background2D {backColor 0 0 1} + children [ + Shape { + appearance Appearance { material Material2D { emissiveColor 1 0 0 filled TRUE } } + geometry Circle {radius 20} + } + ] + } + ] + } + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval 2.0 + loop TRUE + } + DEF PI1 PositionInterpolator2D { + key [0.0 1.0] + keyValue [400.0 300.0 0.0 0.0] + } + DEF PI2 PositionInterpolator2D { + key [0.0 1.0] + keyValue [100.0 100.0 0.0 0.0] + } + ] +} + + +ROUTE TS.fraction_changed TO PI1.set_fraction +ROUTE PI1.value_changed TO L1.size +ROUTE TS.fraction_changed TO PI2.set_fraction +ROUTE PI2.value_changed TO L2.size + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "./auxiliary_files/sky.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-positioning-layout-horiz-ltr-nowrap.bt b/regression_tests/bifs-2D-positioning-layout-horiz-ltr-nowrap.bt new file mode 100644 index 0000000..fe57b32 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-horiz-ltr-nowrap.bt @@ -0,0 +1,403 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 800 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different horizontal justification" "going left to right without line wrap" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -250 160 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 200 60 + } + } + Transform2D { + translation 0 -50 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + size 200 60 + justify ["BEGIN" "BEGIN"] + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + ] + } + ] + } + Transform2D { + translation -250 60 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment BEGIN FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["BEGIN" "FIRST"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation -250 -40 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["BEGIN" "MIDDLE"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation -250 -140 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["BEGIN" "END"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["MIDDLE" "BEGIN"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 60 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment MIDDLE FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["MIDDLE" "FIRST"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 -40 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["MIDDLE" "MIDDLE"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 -140 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["MIDDLE" "END"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 250 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["END" "BEGIN"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 250 60 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment END FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["END" "FIRST"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 250 -40 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["END" "MIDDLE"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 250 -140 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment END END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["END" "END"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-horiz-ltr-wrap-btt.bt b/regression_tests/bifs-2D-positioning-layout-horiz-ltr-wrap-btt.bt new file mode 100644 index 0000000..28d8317 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-horiz-ltr-wrap-btt.bt @@ -0,0 +1,445 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 650 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different horizontal justification" "going left to right with line wrap" "in direction bottom to top and 1.1 line spacing" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -200 260 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 140 120 + } + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment" "BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "BEGIN"] + topToBottom FALSE + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance USE APPTEXT + geometry Text { + string ["Text#2"] + fontStyle USE FS + } + } + DEF S4 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + ] + } + ] + } + Transform2D { + translation -200 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "FIRST"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -200 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "MIDDLE"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -200 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "END"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 260 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "BEGIN"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "FIRST"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "MIDDLE"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "END"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 260 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "BEGIN"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "FIRST"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "MIDDLE"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "END"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-horiz-ltr-wrap-ttb.bt b/regression_tests/bifs-2D-positioning-layout-horiz-ltr-wrap-ttb.bt new file mode 100644 index 0000000..4b415b7 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-horiz-ltr-wrap-ttb.bt @@ -0,0 +1,434 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 650 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different horizontal justification" "going left to right with line wrap" "in direction top to bottom and 1.1 line spacing" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -200 260 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 140 120 + } + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment" "BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "BEGIN"] + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance USE APPTEXT + geometry Text { + string ["Text#2"] + fontStyle USE FS + } + } + DEF S4 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + ] + } + ] + } + Transform2D { + translation -200 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "FIRST"] + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -200 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "MIDDLE"] + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -200 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "END"] + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 260 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "BEGIN"] + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "FIRST"] + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "MIDDLE"] + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "END"] + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 260 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "BEGIN"] + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "FIRST"] + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "MIDDLE"] + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "END"] + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-horiz-rtl-nowrap.bt b/regression_tests/bifs-2D-positioning-layout-horiz-rtl-nowrap.bt new file mode 100644 index 0000000..4c06488 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-horiz-rtl-nowrap.bt @@ -0,0 +1,415 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 800 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different horizontal justification" "going right to left without line wrap" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -250 160 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 200 60 + } + } + Transform2D { + translation 0 -50 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + size 200 60 + justify ["BEGIN" "BEGIN"] + leftToRight FALSE + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + ] + } + ] + } + Transform2D { + translation -250 60 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment BEGIN FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["BEGIN" "FIRST"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation -250 -40 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["BEGIN" "MIDDLE"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation -250 -140 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["BEGIN" "END"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["MIDDLE" "BEGIN"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 60 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment MIDDLE FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["MIDDLE" "FIRST"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 -40 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["MIDDLE" "MIDDLE"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 -140 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["MIDDLE" "END"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 250 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["END" "BEGIN"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 250 60 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment END FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["END" "FIRST"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 250 -40 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["END" "MIDDLE"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 250 -140 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment END END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 200 60 + justify ["END" "END"] + leftToRight FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-horiz-rtl-wrap-btt.bt b/regression_tests/bifs-2D-positioning-layout-horiz-rtl-wrap-btt.bt new file mode 100644 index 0000000..817f801 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-horiz-rtl-wrap-btt.bt @@ -0,0 +1,469 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 650 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different horizontal justification" "going right to left with line wrap" "in direction bottom to top and 1.1 line spacing" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -200 260 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 140 120 + } + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment" "BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "BEGIN"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance USE APPTEXT + geometry Text { + string ["Text#2"] + fontStyle USE FS + } + } + DEF S4 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + ] + } + ] + } + Transform2D { + translation -200 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "FIRST"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -200 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "MIDDLE"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -200 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "END"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 260 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "BEGIN"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "FIRST"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "MIDDLE"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "END"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 260 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "BEGIN"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "FIRST"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "MIDDLE"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "END"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-horiz-rtl-wrap-ttb.bt b/regression_tests/bifs-2D-positioning-layout-horiz-rtl-wrap-ttb.bt new file mode 100644 index 0000000..0406589 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-horiz-rtl-wrap-ttb.bt @@ -0,0 +1,457 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 650 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different horizontal justification" "going right to left with line wrap" "in direction top to bottom and 1.1 line spacing" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -200 260 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 140 120 + } + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment" "BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "BEGIN"] + leftToRight FALSE + spacing 1.1 + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance USE APPTEXT + geometry Text { + string ["Text#2"] + fontStyle USE FS + } + } + DEF S4 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + ] + } + ] + } + Transform2D { + translation -200 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "FIRST"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -200 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "MIDDLE"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -200 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["BEGIN" "END"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 260 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "BEGIN"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "FIRST"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "MIDDLE"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["MIDDLE" "END"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 260 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "BEGIN"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 100 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END FIRST"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "FIRST"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 -60 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "MIDDLE"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 200 -220 + children [ + USE BOUNDS + Transform2D { + translation 0 -80 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 140 120 + justify ["END" "END"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-horiz-text.bt b/regression_tests/bifs-2D-positioning-layout-horiz-text.bt new file mode 100644 index 0000000..da026b3 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-horiz-text.bt @@ -0,0 +1,84 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 650 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing text splitting at word boundaries" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Text Test" + } + Transform2D { + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 160 160 + } + } + Transform2D { + translation 0 -100 + children [ + DEF S Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Sample text use to check if text splitting is ok with layout: aei pqg dtS..."] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + wrap TRUE + size 160 160 + justify ["BEGIN" "END"] + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample text used to check if text splitting is ok with layout: aei pqg dtS..."] + fontStyle USE FS + } + } + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-scroll-child.bt b/regression_tests/bifs-2D-positioning-layout-scroll-child.bt new file mode 100644 index 0000000..fe4e35e --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-scroll-child.bt @@ -0,0 +1,87 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 800 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node used in another layout" "both perform scrolling" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.4 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { size 300 200 } + } + Layout { + size 300 200 + smoothScroll TRUE + loop TRUE + scrollVertical FALSE + scrollRate 0.1 + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { size 100 200 } + } + Layout { + wrap TRUE + size 100 200 + smoothScroll TRUE + loop TRUE + scrollRate 0.2 + children [ + DEF N0 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + USE N0 + USE N0 + ] + } + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-scroll-full.bt b/regression_tests/bifs-2D-positioning-layout-scroll-full.bt new file mode 100644 index 0000000..3b42b4f --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-scroll-full.bt @@ -0,0 +1,344 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 800 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing scrolling in different direction" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -250 160 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 200 60 + } + } + Transform2D { + translation 0 -50 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["scroll smooth vertical"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + wrap TRUE + size 200 60 + justify ["BEGIN" "BEGIN"] + smoothScroll TRUE + loop TRUE + scrollRate 0.1 + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + ] + } + ] + } + Transform2D { + translation -250 60 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["scroll smooth vertical inverse"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 200 60 + justify ["BEGIN" "BEGIN"] + smoothScroll TRUE + loop TRUE + scrollRate -0.1 + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation -250 -40 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["scroll smooth horizontal"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 200 60 + justify ["BEGIN" "BEGIN"] + smoothScroll TRUE + loop TRUE + scrollVertical FALSE + scrollRate 0.1 + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation -250 -140 + children [ + USE BOUNDS + Transform2D { + translation 0 -50 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["scroll smooth horizontal inverse"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 200 60 + justify ["BEGIN" "BEGIN"] + smoothScroll TRUE + loop TRUE + scrollVertical FALSE + scrollRate -0.1 + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 120 + children [ + DEF BOUNDS2 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 100 150 + } + } + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["scroll smooth vertical"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 100 150 + horizontal FALSE + justify ["BEGIN" "BEGIN"] + smoothScroll TRUE + loop TRUE + scrollRate 0.1 + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 200 120 + children [ + USE BOUNDS2 + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["scroll smooth vertical inverse"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 100 150 + horizontal FALSE + justify ["BEGIN" "BEGIN"] + smoothScroll TRUE + loop TRUE + scrollRate -0.1 + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 -80 + children [ + USE BOUNDS2 + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["scroll smooth horizontal"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 100 150 + horizontal FALSE + justify ["BEGIN" "BEGIN"] + smoothScroll TRUE + loop TRUE + scrollVertical FALSE + scrollRate 0.1 + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 200 -80 + children [ + USE BOUNDS2 + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["scroll smooth horizontal inverse"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 100 150 + horizontal FALSE + justify ["BEGIN" "BEGIN"] + smoothScroll TRUE + loop TRUE + scrollVertical FALSE + scrollRate -0.1 + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-scroll-modes-horiz.bt b/regression_tests/bifs-2D-positioning-layout-scroll-modes-horiz.bt new file mode 100644 index 0000000..95163cf --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-scroll-modes-horiz.bt @@ -0,0 +1,349 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows an horizontal Layout node" "with different scrolling modes, direction and rates" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team", "(C) 2006 ENST ATG Conformance Streams"] + title "Layout Test" + } + Transform2D { + translation 0 40 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 200 60 + } + } + DEF LAY Layout { + wrap TRUE + size 200 60 + justify ["BEGIN" "BEGIN"] + smoothScroll TRUE + loop TRUE + scrollVertical FALSE + scrollRate 0.1 + scrollMode -1 + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + ] + } + Transform2D { + translation 0 -50 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT_MODE Text { + string ["mode: scroll-in"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 16 + } + } + } + ] + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry DEF TXT_ALIGN Text { + string ["align: begin"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry DEF TXT_DIR Text { + string ["scroll dir: horizontal"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 0 -110 + children [ + Shape { + appearance USE APPTEXT + geometry DEF TXT_RATE Text { + string ["scroll rate: 0.1"] + fontStyle USE FS + } + } + ] + } + ] + } + + Transform2D { + translation -160 80 + children [ + DEF S Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.7 0.7 0.7 + filled TRUE + lineProps LineProperties { lineColor 0 0 0 } + } + } + geometry Rectangle {size 80 20} + } + Shape { + appearance USE APPTEXT + geometry Text { + string ["Begin"] + fontStyle USE FS + } + } + DEF TS_BE TouchSensor {} + ] + } + Transform2D { + translation -160 60 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Middle"] + fontStyle USE FS + } + } + DEF TS_ME TouchSensor {} + ] + } + Transform2D { + translation -160 40 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["End"] + fontStyle USE FS + } + } + DEF TS_EE TouchSensor {} + ] + } + + Transform2D { + translation -160 0 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Scroll In"] + fontStyle USE FS + } + } + DEF TS_SI TouchSensor {} + ] + } + Transform2D { + translation -160 -20 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Scroll InOut"] + fontStyle USE FS + } + } + DEF TS_SIO TouchSensor {} + ] + } + Transform2D { + translation -160 -40 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Scroll Out"] + fontStyle USE FS + } + } + DEF TS_SO TouchSensor {} + ] + } + + Transform2D { + translation 160 80 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Rate>0"] + fontStyle USE FS + } + } + DEF TS_RP TouchSensor {} + ] + } + Transform2D { + translation 160 60 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Rate<0"] + fontStyle USE FS + } + } + DEF TS_RN TouchSensor {} + ] + } + + Transform2D { + translation 160 30 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Horiz"] + fontStyle USE FS + } + } + DEF TS_SH TouchSensor {} + ] + } + Transform2D { + translation 160 10 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Vert"] + fontStyle USE FS + } + } + DEF TS_SV TouchSensor {} + ] + } + + DEF C_BE Conditional { buffer { + REPLACE LAY.justify BY ["BEGIN", "BEGIN"] + REPLACE TXT_ALIGN.string BY ["align: begin"] + } } + DEF C_ME Conditional { buffer { + REPLACE LAY.justify BY ["MIDDLE", "BEGIN"] + REPLACE TXT_ALIGN.string BY ["align: middle"] + } } + DEF C_EE Conditional { buffer { + REPLACE LAY.justify BY ["END", "BEGIN"] + REPLACE TXT_ALIGN.string BY ["align: end"] + } } + + DEF C_SI Conditional { buffer { + REPLACE LAY.scrollMode BY -1 + REPLACE TXT_MODE.string BY ["mode: scroll-in"] + } } + DEF C_SIO Conditional { buffer { + REPLACE LAY.scrollMode BY 0 + REPLACE TXT_MODE.string BY ["mode: scroll-in-out"] + } } + DEF C_SO Conditional { buffer { + REPLACE LAY.scrollMode BY 1 + REPLACE TXT_MODE.string BY ["mode: scroll-out"] + } } + DEF C_RP Conditional { buffer { + REPLACE LAY.scrollRate BY 0.1 + REPLACE TXT_RATE.string BY ["scroll rate: 0.1"] + } } + DEF C_RN Conditional { buffer { + REPLACE LAY.scrollRate BY -0.1 + REPLACE TXT_RATE.string BY ["scroll rate: -0.1"] + } } + DEF C_SH Conditional { buffer { + REPLACE LAY.scrollVertical BY FALSE + REPLACE TXT_DIR.string BY ["scroll dir: horizontal"] + } } + DEF C_SV Conditional { buffer { + REPLACE LAY.scrollVertical BY TRUE + REPLACE TXT_DIR.string BY ["scroll dir: vertical"] + } } + ] +} + +ROUTE TS_BE.isActive TO C_BE.activate +ROUTE TS_ME.isActive TO C_ME.activate +ROUTE TS_EE.isActive TO C_EE.activate + +ROUTE TS_SI.isActive TO C_SI.activate +ROUTE TS_SIO.isActive TO C_SIO.activate +ROUTE TS_SO.isActive TO C_SO.activate + +ROUTE TS_RP.isActive TO C_RP.activate +ROUTE TS_RN.isActive TO C_RN.activate + +ROUTE TS_SH.isActive TO C_SH.activate +ROUTE TS_SV.isActive TO C_SV.activate + diff --git a/regression_tests/bifs-2D-positioning-layout-scroll-modes-vert.bt b/regression_tests/bifs-2D-positioning-layout-scroll-modes-vert.bt new file mode 100644 index 0000000..a10da74 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-scroll-modes-vert.bt @@ -0,0 +1,350 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows a vertical Layout node" "with different scrolling modes, direction and rates" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team", "(C) 2006 ENST ATG Conformance Streams"] + title "Layout Test" + } + Transform2D { + translation 0 80 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 60 200 + } + } + DEF LAY Layout { + wrap TRUE + size 60 200 + justify ["BEGIN" "BEGIN"] + horizontal FALSE + smoothScroll TRUE + loop TRUE + scrollVertical FALSE + scrollRate 0.1 + scrollMode -1 + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + ] + } + Transform2D { + translation 0 -120 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT_MODE Text { + string ["mode: scroll-in"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 16 + } + } + } + ] + } + Transform2D { + translation 0 -140 + children [ + Shape { + appearance USE APPTEXT + geometry DEF TXT_ALIGN Text { + string ["align: begin"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 0 -160 + children [ + Shape { + appearance USE APPTEXT + geometry DEF TXT_DIR Text { + string ["scroll dir: horizontal"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 0 -180 + children [ + Shape { + appearance USE APPTEXT + geometry DEF TXT_RATE Text { + string ["scroll rate: 0.1"] + fontStyle USE FS + } + } + ] + } + ] + } + + Transform2D { + translation -110 180 + children [ + DEF S Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.7 0.7 0.7 + filled TRUE + lineProps LineProperties { lineColor 0 0 0 } + } + } + geometry Rectangle {size 80 20} + } + Shape { + appearance USE APPTEXT + geometry Text { + string ["Begin"] + fontStyle USE FS + } + } + DEF TS_BE TouchSensor {} + ] + } + Transform2D { + translation -110 160 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Middle"] + fontStyle USE FS + } + } + DEF TS_ME TouchSensor {} + ] + } + Transform2D { + translation -110 140 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["End"] + fontStyle USE FS + } + } + DEF TS_EE TouchSensor {} + ] + } + + Transform2D { + translation -110 100 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Scroll In"] + fontStyle USE FS + } + } + DEF TS_SI TouchSensor {} + ] + } + Transform2D { + translation -110 80 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Scroll InOut"] + fontStyle USE FS + } + } + DEF TS_SIO TouchSensor {} + ] + } + Transform2D { + translation -110 60 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Scroll Out"] + fontStyle USE FS + } + } + DEF TS_SO TouchSensor {} + ] + } + + Transform2D { + translation 110 180 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Rate>0"] + fontStyle USE FS + } + } + DEF TS_RP TouchSensor {} + ] + } + Transform2D { + translation 110 160 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Rate<0"] + fontStyle USE FS + } + } + DEF TS_RN TouchSensor {} + ] + } + + Transform2D { + translation 110 130 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Horiz"] + fontStyle USE FS + } + } + DEF TS_SH TouchSensor {} + ] + } + Transform2D { + translation 110 110 + children [ + USE S + Shape { + appearance USE APPTEXT + geometry Text { + string ["Vert"] + fontStyle USE FS + } + } + DEF TS_SV TouchSensor {} + ] + } + + DEF C_BE Conditional { buffer { + REPLACE LAY.justify BY ["BEGIN", "BEGIN"] + REPLACE TXT_ALIGN.string BY ["align: begin"] + } } + DEF C_ME Conditional { buffer { + REPLACE LAY.justify BY ["MIDDLE", "BEGIN"] + REPLACE TXT_ALIGN.string BY ["align: middle"] + } } + DEF C_EE Conditional { buffer { + REPLACE LAY.justify BY ["END", "BEGIN"] + REPLACE TXT_ALIGN.string BY ["align: end"] + } } + + DEF C_SI Conditional { buffer { + REPLACE LAY.scrollMode BY -1 + REPLACE TXT_MODE.string BY ["mode: scroll-in"] + } } + DEF C_SIO Conditional { buffer { + REPLACE LAY.scrollMode BY 0 + REPLACE TXT_MODE.string BY ["mode: scroll-in-out"] + } } + DEF C_SO Conditional { buffer { + REPLACE LAY.scrollMode BY 1 + REPLACE TXT_MODE.string BY ["mode: scroll-out"] + } } + DEF C_RP Conditional { buffer { + REPLACE LAY.scrollRate BY 0.1 + REPLACE TXT_RATE.string BY ["scroll rate: 0.1"] + } } + DEF C_RN Conditional { buffer { + REPLACE LAY.scrollRate BY -0.1 + REPLACE TXT_RATE.string BY ["scroll rate: -0.1"] + } } + DEF C_SH Conditional { buffer { + REPLACE LAY.scrollVertical BY FALSE + REPLACE TXT_DIR.string BY ["scroll dir: horizontal"] + } } + DEF C_SV Conditional { buffer { + REPLACE LAY.scrollVertical BY TRUE + REPLACE TXT_DIR.string BY ["scroll dir: vertical"] + } } + ] +} + +ROUTE TS_BE.isActive TO C_BE.activate +ROUTE TS_ME.isActive TO C_ME.activate +ROUTE TS_EE.isActive TO C_EE.activate + +ROUTE TS_SI.isActive TO C_SI.activate +ROUTE TS_SIO.isActive TO C_SIO.activate +ROUTE TS_SO.isActive TO C_SO.activate + +ROUTE TS_RP.isActive TO C_RP.activate +ROUTE TS_RN.isActive TO C_RN.activate + +ROUTE TS_SH.isActive TO C_SH.activate +ROUTE TS_SV.isActive TO C_SV.activate + diff --git a/regression_tests/bifs-2D-positioning-layout-scroll-on-off.bt b/regression_tests/bifs-2D-positioning-layout-scroll-on-off.bt new file mode 100644 index 0000000..57e53c5 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-scroll-on-off.bt @@ -0,0 +1,218 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing scrolling with scrool pause/play through scrollRate field" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation 0 -40 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 200 60 + } + } + Transform2D { + translation 0 -50 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["scroll smooth vertical"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 16 + } + } + } + ] + } + DEF LAY Layout { + wrap TRUE + size 200 60 + justify ["BEGIN" "BEGIN"] + smoothScroll TRUE + loop TRUE + scrollRate 0 + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + ] + } + ] + } + Transform2D { + translation -100 40 + children [ + Shape { + appearance DEF TXT_APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT Text { + string ["ScrollRate: 0"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 0 40 + children [ + DEF BACK_RC Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 1 + filled TRUE + lineProps LineProperties { + lineColor 0 0 0 + } + } + } + geometry Rectangle { + size 60 25 + } + } + Shape { + appearance USE TXT_APP + geometry Text { + string ["UP"] + fontStyle USE FS + } + } + DEF TS_UP TouchSensor {} + ] + } + Transform2D { + translation 60 40 + children [ + USE BACK_RC + Shape { + appearance USE TXT_APP + geometry Text { + string ["DOWN"] + fontStyle USE FS + } + } + DEF TS_DOWN TouchSensor {} + ] + } + + DEF C_UP Conditional { + buffer { + REPLACE LAY.scrollRate BY 0.1 + REPLACE TXT.string[0] BY "ScrollRate 0.1" + } + } + DEF RC_UP Conditional { + buffer { + REPLACE LAY.scrollRate BY 0 + REPLACE TXT.string[0] BY "ScrollRate 0" + } + } + DEF C2_UP Conditional { + buffer { + REPLACE LAY.scrollRate BY 0.2 + REPLACE TXT.string[0] BY "ScrollRate 0.2" + } + } + DEF RC2_UP Conditional { + buffer { + REPLACE LAY.scrollRate BY 0.1 + REPLACE TXT.string[0] BY "ScrollRate 0.1" + } + } + + DEF C_DOWN Conditional { + buffer { + REPLACE LAY.scrollRate BY -0.1 + REPLACE TXT.string[0] BY "ScrollRate -0.1" + } + } + DEF RC_DOWN Conditional { + buffer { + REPLACE LAY.scrollRate BY 0 + REPLACE TXT.string[0] BY "ScrollRate 0" + } + } + DEF C2_DOWN Conditional { + buffer { + REPLACE LAY.scrollRate BY -0.2 + REPLACE TXT.string[0] BY "ScrollRate -0.2" + } + } + DEF RC2_DOWN Conditional { + buffer { + REPLACE LAY.scrollRate BY -0.1 + REPLACE TXT.string[0] BY "ScrollRate -0.1" + } + } + ] +} + +ROUTE TS_UP.isOver TO C_UP.activate +ROUTE TS_UP.isOver TO RC_UP.reverseActivate +ROUTE TS_UP.isActive TO C2_UP.activate +ROUTE TS_UP.isActive TO RC2_UP.reverseActivate +ROUTE TS_DOWN.isOver TO C_DOWN.activate +ROUTE TS_DOWN.isOver TO RC_DOWN.reverseActivate +ROUTE TS_DOWN.isActive TO C2_DOWN.activate +ROUTE TS_DOWN.isActive TO RC2_DOWN.reverseActivate diff --git a/regression_tests/bifs-2D-positioning-layout-vert-btt-nowrap.bt b/regression_tests/bifs-2D-positioning-layout-vert-btt-nowrap.bt new file mode 100644 index 0000000..b611cbe --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-vert-btt-nowrap.bt @@ -0,0 +1,340 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 350 + pixelHeight 650 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different vertical justification" "going bottom to top without column wrap" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -120 230 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 100 150 + } + } + Transform2D { + translation 0 -90 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment" "BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["BEGIN" "BEGIN"] + topToBottom FALSE + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + ] + } + ] + } + Transform2D { + translation 0 230 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["BEGIN" "MIDDLE"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 120 230 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["BEGIN" "END"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation -120 10 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["MIDDLE" "BEGIN"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 10 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["MIDDLE" "MIDDLE"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 120 10 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["MIDDLE" "END"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation -120 -210 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["END" "BEGIN"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 -210 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["END" "MIDDLE"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 120 -210 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["END" "END"] + topToBottom FALSE + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-vert-btt-wrap-ltr.bt b/regression_tests/bifs-2D-positioning-layout-vert-btt-wrap-ltr.bt new file mode 100644 index 0000000..71d9f12 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-vert-btt-wrap-ltr.bt @@ -0,0 +1,372 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 500 + pixelHeight 500 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different vertical justification" "going from bottom to top with column wrap" "in direction left to right and 1.1 column spacing" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -170 160 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 150 100 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment" "BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "BEGIN"] + topToBottom FALSE + spacing 1.1 + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + DEF S4 Shape { + appearance USE APPTEXT + geometry Text { + string ["#2"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "MIDDLE"] + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "END"] + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -170 0 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "BEGIN"] + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "MIDDLE"] + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 0 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "END"] + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -170 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "BEGIN"] + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "MIDDLE"] + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "END"] + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-vert-btt-wrap-rtl.bt b/regression_tests/bifs-2D-positioning-layout-vert-btt-wrap-rtl.bt new file mode 100644 index 0000000..779e57f --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-vert-btt-wrap-rtl.bt @@ -0,0 +1,381 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 500 + pixelHeight 500 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different vertical justification" "going bottom to top with column wrap" "in direction right to left and 1.1 column spacing" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -170 160 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 150 100 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment" "BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "BEGIN"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + DEF S4 Shape { + appearance USE APPTEXT + geometry Text { + string ["#2"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "MIDDLE"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "END"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -170 0 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "BEGIN"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "MIDDLE"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 0 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "END"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -170 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "BEGIN"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "MIDDLE"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "END"] + leftToRight FALSE + topToBottom FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-vert-ttb-nowrap.bt b/regression_tests/bifs-2D-positioning-layout-vert-ttb-nowrap.bt new file mode 100644 index 0000000..a7cf1ba --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-vert-ttb-nowrap.bt @@ -0,0 +1,331 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 350 + pixelHeight 650 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different vertical justification" "going top to bottom without column wrap" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -120 230 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 100 150 + } + } + Transform2D { + translation 0 -90 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment" "BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["BEGIN" "BEGIN"] + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + ] + } + ] + } + Transform2D { + translation 0 230 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["BEGIN" "MIDDLE"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 120 230 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["BEGIN" "END"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation -120 10 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["MIDDLE" "BEGIN"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 10 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["MIDDLE" "MIDDLE"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 120 10 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["MIDDLE" "END"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation -120 -210 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["END" "BEGIN"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 0 -210 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["END" "MIDDLE"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + Transform2D { + translation 120 -210 + children [ + USE BOUNDS + Transform2D { + translation 0 -90 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END END"] + fontStyle USE FS + } + } + ] + } + Layout { + size 100 150 + horizontal FALSE + justify ["END" "END"] + children [ + USE S1 + USE S2 + USE S3 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-vert-ttb-wrap-ltr.bt b/regression_tests/bifs-2D-positioning-layout-vert-ttb-wrap-ltr.bt new file mode 100644 index 0000000..915a78f --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-vert-ttb-wrap-ltr.bt @@ -0,0 +1,363 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 500 + pixelHeight 500 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different vertical justification" "going top to bottom with column wrap" "in direction left to right and 1.1 column spacing" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:07 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -170 160 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 150 100 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment" "BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "BEGIN"] + spacing 1.1 + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + DEF S4 Shape { + appearance USE APPTEXT + geometry Text { + string ["#2"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "MIDDLE"] + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "END"] + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -170 0 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "BEGIN"] + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "MIDDLE"] + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 0 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "END"] + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -170 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "BEGIN"] + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "MIDDLE"] + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "END"] + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-layout-vert-ttb-wrap-rtl.bt b/regression_tests/bifs-2D-positioning-layout-vert-ttb-wrap-rtl.bt new file mode 100644 index 0000000..ba9ac3c --- /dev/null +++ b/regression_tests/bifs-2D-positioning-layout-vert-ttb-wrap-rtl.bt @@ -0,0 +1,372 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 500 + pixelHeight 500 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Layout node" "performing different vertical justification" "going top to bottom with column wrap" "in direction right to left and 1.1 column spacing" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Layout Test" + } + Transform2D { + translation -170 160 + children [ + DEF BOUNDS Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 150 100 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance DEF APPTEXT Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Alignment" "BEGIN BEGIN"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE"] + size 16 + } + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "BEGIN"] + leftToRight FALSE + spacing 1.1 + children [ + DEF S1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + DEF S2 Shape { + appearance USE APPTEXT + geometry Text { + string ["Sample Text"] + fontStyle USE FS + } + } + DEF S3 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } + DEF S4 Shape { + appearance USE APPTEXT + geometry Text { + string ["#2"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "MIDDLE"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "BEGIN END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["BEGIN" "END"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -170 0 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "BEGIN"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "MIDDLE"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 0 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "MIDDLE END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["MIDDLE" "END"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation -170 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END BEGIN"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "BEGIN"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 0 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END MIDDLE"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "MIDDLE"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + Transform2D { + translation 170 -160 + children [ + USE BOUNDS + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE APPTEXT + geometry Text { + string ["Alignment" "END END"] + fontStyle USE FS + } + } + ] + } + Layout { + wrap TRUE + size 150 100 + horizontal FALSE + justify ["END" "END"] + leftToRight FALSE + spacing 1.1 + children [ + USE S1 + USE S2 + USE S3 + USE S4 + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-orderedgroup.bt b/regression_tests/bifs-2D-positioning-orderedgroup.bt new file mode 100644 index 0000000..8725f1a --- /dev/null +++ b/regression_tests/bifs-2D-positioning-orderedgroup.bt @@ -0,0 +1,86 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 0 + } + WorldInfo { + info [ + "This test shows the usage of the OrderedGroup node." + "The OrderedGroup node is similar to the Group node but allows for more possibilities." + "The difference is the addition of the 'order' property which explicitely gives the display order of the children of the OrderedGroup." + "Order varies from 0 to N-1, where N is the number of children. [3 2 1 0] will display the children in reverse order." + "This test shows how to interactively change the order based on a click." + "If you click on the rectangle or the circle, and keep the mouse button down, the display order is reversed. If you release the button, the display order goes back to initial." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "OrderedGroup" + } + DEF OG OrderedGroup { + order [1 0] + children [ + DEF S1 Shape { + appearance DEF A1 Appearance { + material Material2D { + emissiveColor 1 1 1 + filled TRUE + } + } + geometry Rectangle { + size 160 60 + } + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 1 + filled TRUE + } + } + geometry Circle { + radius 40 + } + } + DEF TS TouchSensor {} + ] + } + DEF C Conditional { + buffer { + REPLACE OG.order BY [0 1] + } + } + DEF RC Conditional { + buffer { + REPLACE OG.order BY [1 0] + } + } + ] +} + +ROUTE TS.isActive TO C.activate +ROUTE TS.isActive TO RC.reverseActivate + diff --git a/regression_tests/bifs-2D-positioning-pathlayout.bt b/regression_tests/bifs-2D-positioning-pathlayout.bt new file mode 100644 index 0000000..0293a8d --- /dev/null +++ b/regression_tests/bifs-2D-positioning-pathlayout.bt @@ -0,0 +1,164 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows PathLayout usage" "with text and graphics layout" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "PathLayout Test" + } + Transform2D { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + filled TRUE + } + } + geometry DEF LS Curve2D { + fineness 1 + type [2] + point DEF C2D Coordinate2D { + point [-100 0 -50 400 50 -400 100 0] + } + } + } + DEF TOUCH TouchSensor {} + ] + } + DEF PL PathLayout { + alignment [0 1] + pathOffset 0.25 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["text and "] + fontStyle DEF FS FontStyle { + size 20 + } + } + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 30 20 + } + } + DEF TSSTOP TouchSensor {} + ] + } + Shape { + appearance USE APP + geometry Text { + string ["along a path"] + fontStyle USE FS + } + } + ] + geometry USE LS + } + ] + } + Transform2D { + translation 0 -140 + children [ + Shape { + appearance USE APP + geometry Text { + string ["move over shape and click" "to change the layout wrapping mode"] + fontStyle FontStyle { + justify ["MIDDLE"] + size 20 + } + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 20 + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [-0.5 1.5 -0.5] + } + DEF C1 Conditional { + buffer { + REPLACE PL.wrapMode BY 1 + } + } + DEF RC1 Conditional { + buffer { + REPLACE PL.wrapMode BY 0 + } + } + DEF C2 Conditional { + buffer { + REPLACE PL.wrapMode BY 2 + } + } + DEF RC2 Conditional { + buffer { + REPLACE PL.wrapMode BY 1 + } + } + DEF C3 Conditional { + buffer { + REPLACE TS.enabled BY FALSE + } + } + DEF RC3 Conditional { + buffer { + REPLACE TS.enabled BY TRUE + } + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO PL.pathOffset +ROUTE TSSTOP.isOver TO C3.activate +ROUTE TSSTOP.isOver TO RC3.reverseActivate +ROUTE TOUCH.isOver TO C1.activate +ROUTE TOUCH.isOver TO RC1.reverseActivate +ROUTE TOUCH.isActive TO C2.activate +ROUTE TOUCH.isActive TO RC2.reverseActivate + diff --git a/regression_tests/bifs-2D-positioning-transform2D.bt b/regression_tests/bifs-2D-positioning-transform2D.bt new file mode 100644 index 0000000..427f3a9 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-transform2D.bt @@ -0,0 +1,133 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 450 + pixelHeight 450 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to apply affine transformations on a rectangle." + "A graphical object can be moved, rotated or scaled using the Transform2D node." + "The object (or objects) to be transformed is (are) placed in the children property of the Transform2D node." + "The Transform2D node indicates the transformation from the local coordinate system to the global coordinate system. It applies to any graphical object." + "Possible transformations are limited to translation, rotation (including with a center different from the origin) and scaling (including along different axes that original system) using respectively the translation, rotationAngle, center, scale, scaleOrientation." + "For skewing, you should see the TransformMatrix2D node." + "cf bifs-2D-positioning-transformmatrix2D" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team"] + title "Apply transformations on local coordinate systems - Transform2D node" + } + Transform2D { + translation -100 150 + children [ + Transform2D { + children [ + DEF RECT Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry Rectangle { + size 150 100 + } + } + ] + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Rectangle" "translation only"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 12 + } + } + } + ] + } + ] + } + Transform2D { + translation 100 0 + children [ + Transform2D { + scale 1 1.5 + scaleOrientation 1 + children [ + USE RECT + ] + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Scale 1.0 1.5" "Scale Orientation 1.0"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation -100 -100 + children [ + Transform2D { + rotationAngle 1 + scale 1 1.5 + children [ + USE RECT + ] + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Scale 1.0 1.5" "rotation 1.0"] + fontStyle USE FS + } + } + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-positioning-transformmatrix2D.bt b/regression_tests/bifs-2D-positioning-transformmatrix2D.bt new file mode 100644 index 0000000..ad87b05 --- /dev/null +++ b/regression_tests/bifs-2D-positioning-transformmatrix2D.bt @@ -0,0 +1,179 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 550 + pixelHeight 450 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to apply affine transformations on a rectangle." + "The TransformMatrix2D works like the Transform2D node, but it requires giving the coefficients of a 3x2 matrix." + "It allows for skewing and for simple animations of skewing." + "Click on the transformed rectangles to start transformation animations." + "cf bifs-2D-positioning-transform2D" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.4 $" + "(C) 2002-2006 GPAC Team" + ] + title "Apply transformations on local coordinate systems - TransformMatrix2D node" + } + TransformMatrix2D { + tx 175 + ty 80 + children [ + DEF N1 Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 200 100 + } + } + Shape { + appearance DEF N7 Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["translated rectangle"] + fontStyle DEF N0 FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 15 + } + } + } + ] + } + DEF N12 TransformMatrix2D { + mxx 0.707107 + mxy 0.707107 + tx -150 + myx -0.707107 + myy 0.707107 + ty 80 + children [ + USE N1 + Shape { + appearance USE N7 + geometry Text { + string ["translated" "and" "Pi/4-rotated rectangle"] + fontStyle USE N0 + } + } + DEF N17 TouchSensor {} + DEF N4 TimeSensor { + cycleInterval 2 + startTime -1 + } + DEF N9 ScalarInterpolator { + key [0 1] + keyValue [0.707107 1] + } + DEF N8 ScalarInterpolator { + key [0 1] + keyValue [0.707107 0] + } + DEF N6 ScalarInterpolator { + key [0 1] + keyValue [-0.707107 0] + } + DEF N5 ScalarInterpolator { + key [0 1] + keyValue [0.707107 1] + } + ] + } + DEF N11 TransformMatrix2D { + mxy 1 + tx 120 + ty -80 + children [ + USE N1 + Shape { + appearance USE N7 + geometry Text { + string ["translated" "and" "Pi/4-X-Skewed rectangle"] + fontStyle USE N0 + } + } + DEF N15 TouchSensor {} + DEF N3 TimeSensor { + cycleInterval 2 + startTime -1 + } + DEF N16 ScalarInterpolator { + key [0 1] + keyValue [1 0] + } + ] + } + DEF N10 TransformMatrix2D { + tx -50 + myx 1 + ty -50 + children [ + USE N1 + Shape { + appearance USE N7 + geometry Text { + string ["translated" "and" "Pi/4-Y-Skewed rectangle"] + fontStyle USE N0 + } + } + DEF N14 TouchSensor {} + DEF N2 TimeSensor { + cycleInterval 2 + startTime -1 + } + DEF N13 ScalarInterpolator { + key [0 1] + keyValue [1 0] + } + ] + } + ] +} + +ROUTE N17.touchTime TO N4.startTime +ROUTE N4.fraction_changed TO N9.set_fraction +ROUTE N9.value_changed TO N12.mxx +ROUTE N4.fraction_changed TO N8.set_fraction +ROUTE N8.value_changed TO N12.mxy +ROUTE N4.fraction_changed TO N6.set_fraction +ROUTE N6.value_changed TO N12.myx +ROUTE N4.fraction_changed TO N5.set_fraction +ROUTE N5.value_changed TO N12.myy +ROUTE N15.touchTime TO N3.startTime +ROUTE N3.fraction_changed TO N16.set_fraction +ROUTE N16.value_changed TO N11.mxy +ROUTE N14.touchTime TO N2.startTime +ROUTE N2.fraction_changed TO N13.set_fraction +ROUTE N13.value_changed TO N10.myx + diff --git a/regression_tests/bifs-2D-shapes-all.bt b/regression_tests/bifs-2D-shapes-all.bt new file mode 100644 index 0000000..44d61c2 --- /dev/null +++ b/regression_tests/bifs-2D-shapes-all.bt @@ -0,0 +1,267 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 460 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "The MPEG-4 primitive to display graphical objects on screen is the Shape node." + "The Shape node is allowed to have a child node contained in its geometry property." + "This test shows the possible 2D nodes allowed in this geometry property:" + "* IndexedLineSet2D: this node is used to draw polylines. This geometry is not considered closed, even if last point is equal to first point. As such, it cannot be filled. The origin of its local coordinate system is the point of coordinate (0,0)." + "* Circle: its only property is its radius. The origin of its local coordinate system is the center of the circle." + "* Rectangle: its only property is its size (WxH). The origin of its local coordinate system is the center of the rectangle." + "* Curve2D (middle row): this node is used to display complex paths using Bezier Curves. It has two properties: type, points. Type gives the drawing types (segments or bezier), points gives the list of points to be consumed when reading the types. The node XCurve2D allows more drawing types (elliptical arcs ...):" + "cf bifs-2D-shapes-xcurve2D" + "* IndexedFaceSet2D: this node is similar to the IndexedLineSet2D node. It is used to display polygons (i.e. a list of segments implicitely closed by the segment [last point, first point]). The origin of its local coordinate system is the point of coordinate (0,0)." + "cf bifs-2D-shapes-indexfaceset2D" + "* PointSet2D: a list of points to be displayed (not really useful)" + "" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 15:19:18 $ - $Revision: 1.5 $" + "(C) 2002-2006 GPAC Team" + ] + title "Basic 2D Geometry nodes" + } + Transform2D { + translation -150 150 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + } + } + geometry IndexedLineSet2D { + coord Coordinate2D { + point [-50 0 0 50 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["IndexedLineSet2D" "[-50 0 0 50 50 0]"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 12 + } + } + } + ] + } + ] + } + Transform2D { + translation 0 150 + children [ + Shape { + appearance USE APP + geometry Circle { + radius 50 + } + } + Transform2D { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Circle" "radius 50"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 150 150 + children [ + Shape { + appearance USE APP + geometry Rectangle { + size 100 50 + } + } + Transform2D { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Rectangle" "Size 100 50"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -40 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Curve2D Points:" "-50 0, -100 50, 0 20, 10 30, 40 80, 50 0"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -150 0 + children [ + Shape { + appearance USE APP + geometry Curve2D { + point DEF C2D Coordinate2D { + point [-50 0 -100 50 0 20 10 30 40 80 50 0] + } + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["no type"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 3] + point USE C2D + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["type [2 3]"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 170 0 + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [1 3 1] + point USE C2D + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["type [1 3 1]"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation -150 -150 + children [ + Shape { + appearance USE APP + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-50 0 -25 25 0 80 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["IndexedFaceSet2D" "-50 0, -25, 25, 0 80, 50 0"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance USE APP + geometry PointSet2D { + coord Coordinate2D { + point [-50 0 -25 25 0 80 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["PointSet2D" "-50 0, -25, 25, 0 80, 50 0"] + fontStyle USE FS + } + } + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-shapes-indexfaceset2D.bt b/regression_tests/bifs-2D-shapes-indexfaceset2D.bt new file mode 100644 index 0000000..49711fa --- /dev/null +++ b/regression_tests/bifs-2D-shapes-indexfaceset2D.bt @@ -0,0 +1,123 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 420 + pixelHeight 420 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows a bit more how to use the IndexedFaceSet2D node with different color fill modes." + "This node can hold a Coordinate2D node to list points and use the coordIndex field to form faces (-1 separated indexes)." + "Each face can have different color. If colorPerVertex is TRUE each point has a separate color and the result is that a face is filled using color interpolation." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "IndexedFaceSet2D and Color Modes" + } + Transform2D { + scale 0.8 0.8 + translation -120 120 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + lineProps DEF LP LineProperties { + width 6 + } + } + } + geometry IndexedFaceSet2D { + colorIndex [0 1 2 3 4 5] + coordIndex [0 1 2 3 4 5] + color Color { + color [0 0 1 0 1 0 1 0 0 1 1 0 1 0 1 0 1 1] + } + coord Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + Transform2D { + scale 0.8 0.8 + translation 120 120 + children [ + Shape { + appearance Appearance { + material Material2D { + filled TRUE + } + } + geometry IndexedFaceSet2D { + colorIndex [0 1 2 3 4 5] + coordIndex [0 1 2 3 4 5] + color Color { + color [0 0 1 0 1 0 1 0 0 1 1 0 1 0 1 0 1 1] + } + coord Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + Transform2D { + scale 0.8 0.8 + translation -120 -120 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps USE LP + } + } + geometry DEF IFS2 IndexedFaceSet2D { + coord Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + Transform2D { + scale 0.8 0.8 + translation 120 -120 + children [ + Shape { + appearance Appearance { + material Material2D { + filled TRUE + } + } + geometry USE IFS2 + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-shapes-indexlineset2D.bt b/regression_tests/bifs-2D-shapes-indexlineset2D.bt new file mode 100644 index 0000000..3fe8886 --- /dev/null +++ b/regression_tests/bifs-2D-shapes-indexlineset2D.bt @@ -0,0 +1,100 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 420 + pixelHeight 420 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows a bit more how to use the IndexedLineSet2D node with different line color modes." + "Similarly to the IndexedFaceSet2D, one can give a color to each point." + "The result will be a line with a gradient color" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team"] + title "IndexedLineSet2D" + } + Transform2D { + scale 0.5 0.5 + translation -120 80 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0.1 0.1 0.8 + width 10 + } + } + } + geometry IndexedLineSet2D { + colorIndex [1 0 2 1 -1 1 0 2 1] + coordIndex [0 1 2 0 -1 3 4 5 3] + color Color { + color [1 1 0 1 0 1 0 1 1] + } + coord DEF COORD Coordinate2D { + point [-100 -100 0 -10 100 -100 100 100 0 10 -100 100] + } + } + } + ] + } + Transform2D { + scale 0.5 0.5 + translation 120 80 + children [ + Shape { + appearance USE APP + geometry IndexedLineSet2D { + colorIndex [1 0 2 -1 0 2 1] + colorPerVertex FALSE + coordIndex [0 1 2 0 -1 3 4 5 3] + color Color { + color [1 1 0 1 0 1 0 1 1] + } + coord USE COORD + } + } + ] + } + Transform2D { + scale 0.5 0.5 + translation 0 -80 + children [ + Shape { + appearance USE APP + geometry IndexedLineSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 0 -1 3 4 5 3] + coord USE COORD + } + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-shapes-pointset2D.bt b/regression_tests/bifs-2D-shapes-pointset2D.bt new file mode 100644 index 0000000..53b77ef --- /dev/null +++ b/regression_tests/bifs-2D-shapes-pointset2D.bt @@ -0,0 +1,56 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelWidth 450 + pixelHeight 450 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This shows the use of PointSet2D which is to display individual pixels on the screen." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "PointSet2D" + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + } + } + geometry PointSet2D { + coord DEF COORD Coordinate2D { + point [-0.25 -0.25 0.25 -0.25 0.25 0.25 -0.25 0.25] + } + } + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-shapes-xcurve2D.bt b/regression_tests/bifs-2D-shapes-xcurve2D.bt new file mode 100644 index 0000000..225d35e --- /dev/null +++ b/regression_tests/bifs-2D-shapes-xcurve2D.bt @@ -0,0 +1,188 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This shows the use of XCurve2D." + "This node is an extension of the Curve2D node. It allows more drawing types: quadratic Bezier curves and elliptical arcs." + "Both XCurve2D and Curve2D are similar to IndexedFaceSet2D and IndexedLineSet2D because they can be used to represent polygons and polylines. The difference is that it is not possible to assign a color or texture per point, nor a color per closed path. " + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "XCurve2D" + } + Transform2D { + translation -150 140 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0 0 1 + width 8 + } + } + } + geometry XCurve2D { + type [4] + point Coordinate2D { + point [0 0 40 0 40 0 40 40] + } + } + } + Transform2D { + translation 40 -60 + children [ + Shape { + appearance DEF TXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["anti-clockwise arc to" "point [0 0 40 0 40 0 40 40]"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + ] + } + Transform2D { + translation 50 140 + children [ + Shape { + appearance USE APP + geometry XCurve2D { + type [5] + point Coordinate2D { + point [0 0 40 0 40 0 40 40] + } + } + } + Transform2D { + translation 40 -60 + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["clockwise arc to" "point [0 0 40 0 40 0 40 40]"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation -150 0 + children [ + Shape { + appearance USE APP + geometry XCurve2D { + type [7] + point Coordinate2D { + point [0 0 40 40 80 0] + } + } + } + Transform2D { + translation 40 -30 + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["Quadratic Bezier arc to" "point [0 0 40 40 80 0 ]"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 50 0 + children [ + Shape { + appearance USE APP + geometry XCurve2D { + type [7 6] + point Coordinate2D { + point [0 0 40 40 80 0] + } + } + } + Transform2D { + translation 40 -30 + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["Quadratic Bezier arc to" "with close path"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation -50 -120 + children [ + Shape { + appearance USE APP + geometry XCurve2D { + type [7 1] + point Coordinate2D { + point [0 0 40 40 80 0 0 0] + } + } + } + Transform2D { + translation 40 -30 + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["Quadratic Bezier arc to" "without close path (lineTo origin)"] + fontStyle USE FS + } + } + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-texturing-compositetexture2D-background.bt b/regression_tests/bifs-2D-texturing-compositetexture2D-background.bt new file mode 100644 index 0000000..2e67628 --- /dev/null +++ b/regression_tests/bifs-2D-texturing-compositetexture2D-background.bt @@ -0,0 +1,116 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the usage of the CompositeTexture node and how its background can be changed dynamically." + "Here, the texture is a circle repeated on the X and Y directions. Just one circle object is used here, the result of the drawing is translated in each directions, the object is not duplicated." + "When the user moves the mouse over a circle, the background is changed. When the user clicks on a circle, the background is also changed." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "CompositeTexture and Background2D nodes" + } + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 0 1 0 + } + texture CompositeTexture2D { + pixelWidth 64 + pixelHeight 64 + children [ + DEF B1 Background2D { backColor 0 1 1 } + DEF B2 Background2D { backColor 1 1 0 } + DEF B3 Background2D { backColor 1 0 0 } + DEF S Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + transparency 0.5 + } + } + geometry Circle { + radius 24 + } + } + DEF TS TouchSensor {} + ] + } + textureTransform TextureTransform { scale 8 4 } + } + geometry Rectangle { size 300 150 } + } + + Transform2D { + translation 140 -110 + children [ + USE S + Transform2D { + translation 0 -50 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Composite Texture Pattern" "Black color with 0.5 transparency"] + fontStyle DEF FS FontStyle { + family ["SANS"] + justify ["MIDDLE" "BEGIN"] + size 20 + } + } + } + ] + } + ] + } + Transform2D { + translation -150 160 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Move mouse over texture" "to change texture background"] + fontStyle USE FS + } + } + ] + } + ] +} + +ROUTE TS.isOver TO B2.set_bind +ROUTE TS.isActive TO B3.set_bind + diff --git a/regression_tests/bifs-2D-texturing-compositetexture2D-bitmap.bt b/regression_tests/bifs-2D-texturing-compositetexture2D-bitmap.bt new file mode 100644 index 0000000..3f7c1bd --- /dev/null +++ b/regression_tests/bifs-2D-texturing-compositetexture2D-bitmap.bt @@ -0,0 +1,89 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the usage of the CompositeTexture node." + "It allows defining a texture made of vector graphics objects, and to use the texture like images or video. It enables using hardware acceleration for texture operations." + "In mixed 3D/2D environment, it allows mapping 2D graphics on 3D objects." + "Texture in general may be repeated without the complexity of duplicating and translating the objects." + "Here, the texture is an animated circle on a red background. The resulting texture is used on a Bitmap node." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "CompositeTexture and Bitmap nodes" + } + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 0 1 0 + } + texture CompositeTexture2D { + pixelWidth 100 + pixelHeight 100 + background Background2D { backColor 1 0 0} + children [ + DEF S Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE +# transparency 0.5 + lineProps LineProperties { width 0 } + } + } + geometry DEF C Circle { + radius 20 + } + } + ] + } + } + geometry Bitmap {scale 2 2} + } + + Transform2D { + translation 200 0 + children [ + USE S + ] + } + DEF TS TimeSensor { + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [25 10 25] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO C.radius + diff --git a/regression_tests/bifs-2D-texturing-compositetexture2D-transparent.bt b/regression_tests/bifs-2D-texturing-compositetexture2D-transparent.bt new file mode 100644 index 0000000..e04a8e6 --- /dev/null +++ b/regression_tests/bifs-2D-texturing-compositetexture2D-transparent.bt @@ -0,0 +1,131 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + DEF B Background2D { + backColor 1 1 0 + } + WorldInfo { + info [ + "This test shows how transparency applies to composite textures." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "CompositeTexture and Transparency" + } + Transform2D { + translation -20 80 + rotationAngle 0.57 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { size 380 40 } + } + ] + } + Transform2D { + translation -20 80 + children [ + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 0 1 0 + } + texture CompositeTexture2D { + pixelWidth 100 + pixelHeight 100 + children [ + DEF S Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + transparency 0.5 + } + } + geometry Circle { + radius 50 + } + } + DEF TS TouchSensor {} + ] + } + textureTransform TextureTransform { + scale 8 4 + } + } + #geometry Curve2D { type [2] point Coordinate2D { point [-300 0 -150 -500 150 500 300 0] } } + geometry Rectangle { size 300 150 } + } + ] + } + Transform2D { + translation 140 -100 + children [ + USE S + Transform2D { + translation 0 -50 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Composite Texture Pattern" "Black Circle Half-Transparent"] + fontStyle DEF FS FontStyle { + family ["SANS"] + justify ["MIDDLE" "BEGIN"] + size 20 + } + } + } + ] + } + ] + } + DEF C Conditional { + buffer { + REPLACE B.backColor BY 1 0 0 + } + } + DEF RC Conditional { + buffer { + REPLACE B.backColor BY 1 1 0 + } + } + ] +} + +ROUTE TS.isActive TO C.activate +ROUTE TS.isActive TO RC.reverseActivate + diff --git a/regression_tests/bifs-2D-texturing-gradients-text.bt b/regression_tests/bifs-2D-texturing-gradients-text.bt new file mode 100644 index 0000000..86567ca --- /dev/null +++ b/regression_tests/bifs-2D-texturing-gradients-text.bt @@ -0,0 +1,137 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test uses a Viewport node, Gradient nodes and animation and TransformMatrix2D node in a text mirroring-like effect." + "" + "GPAC Regression Tests" "$Date: 2008/10/31 18:08:50 $ - $Revision: 1.7 $" + "(C) 2002-2006 GPAC Team" + ] + title "Text Mirroring Effect" + } + Viewport { + size 200 200 + description "Initial Viewport" + } +#if 0 + Viewport { + position -30 20 + size 50 50 + description "Text Center View" + } + Viewport { + position 75 0 + size 50 50 + description "Blending Zone View" + } +#endif + + DEF TR Transform2D { + children [ + DEF PS PlaneSensor2D { + maxPosition 100 100 + minPosition -100 0 + } + Transform2D { + translation 2 -2 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + transparency 0 + filled TRUE + } + } + geometry DEF T Text { + string ["Gradient" "Text Demo"] + fontStyle FontStyle { + justify ["MIDDLE" "END"] + size 30 + style "BOLD" + } + } + } + ] + } + DEF TESTSHAPE Shape { + appearance Appearance { + texture RadialGradient { + focalPoint 0.35 0.55 + key [0 0.7 1] + keyValue [0 1 0.5 0 0.5 1 0 1 1] + radius 0.8 + } + } + geometry USE T + } + ] + } + TransformMatrix2D { + mxy 1.5 + myy -1 + children [ + USE TR + ] + } + Transform2D { + translation 0 -100 + children [ + Shape { + appearance Appearance { + texture RadialGradient { + center 0.5 1 + focalPoint 0.5 1 + key [0 0.5 1] + keyValue [1 1 0 0.8 0 0.2 0.8 0 0.2] + opacity [0.8 1 1] + radius 1.2 + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + DEF TIME TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF PI PositionInterpolator2D { + key [0 0.5 1] + keyValue [1 1 1 0.75 1 1] + } + ] +} + +ROUTE PS.translation_changed TO TR.translation +ROUTE TIME.fraction_changed TO PI.set_fraction +ROUTE PI.value_changed TO TR.scale + diff --git a/regression_tests/bifs-2D-texturing-gradients-transparent.bt b/regression_tests/bifs-2D-texturing-gradients-transparent.bt new file mode 100644 index 0000000..58a5add --- /dev/null +++ b/regression_tests/bifs-2D-texturing-gradients-transparent.bt @@ -0,0 +1,120 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This shows usage of transparency with gradient" + "Move the rectangle over the circle to see that the gradient is not uniformly transparent." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Gradient and Transparency" + } + DEF TR Transform2D { + translation 200 50 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + width 0 + } + } + texture DEF GL LinearGradient { + endPoint 1 1 + key [0 0.4 1] + keyValue [0 0 1 1 0 1 0 1 1] + spreadMethod 1 + } + } + geometry Rectangle { + size 200 200 + } + } + DEF PS PlaneSensor2D { + maxPosition 300 300 + minPosition -300 -300 + offset 200 50 + } + ] + } + Transform2D { + scale 1 1.2 + translation -50 0 + children [ + Shape { + appearance Appearance { + texture RadialGradient { + focalPoint 0.75 0.5 + key [0 0.6 1] + keyValue [1 1 1 1 0 0 1 1 0] + opacity [1 1 0.3] + } + } + geometry Circle { + radius 100 + } + } + ] + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Drag square around screen"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF C PositionInterpolator2D { + key [0 0.5 1] + keyValue [1 1 1 0 1 1] + } + ] +} + +ROUTE TS.fraction_changed TO C.set_fraction +ROUTE C.value_changed TO GL.endPoint +ROUTE PS.translation_changed TO TR.translation + diff --git a/regression_tests/bifs-2D-texturing-imagetexture-shapes.bt b/regression_tests/bifs-2D-texturing-imagetexture-shapes.bt new file mode 100644 index 0000000..7abc7f5 --- /dev/null +++ b/regression_tests/bifs-2D-texturing-imagetexture-shapes.bt @@ -0,0 +1,282 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 440 + pixelHeight 440 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows image displayed on the basic 2D shapes." + "Image is considered as a texture and described in the ImageTexture node." + "The url of this node points to the image using any internet protocol (http, rtsp) or using MPEG-4 OD." + "The ImageTexture node is associated to the geometry as a children of the Shape node. The image is then clipped along the bounding box of the shape." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Displaying Images as a texture on any shape" + } + Transform2D { + translation -150 150 + children [ + Shape { + appearance DEF APP Appearance { + texture ImageTexture { + url [od:10] + } + } + geometry IndexedLineSet2D { + coord Coordinate2D { + point [-50 0 0 50 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["IndexedLineSet2D" "[-50 0 0 50 50 0]"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 12 + } + } + } + ] + } + ] + } + Transform2D { + translation 0 150 + children [ + Shape { + appearance USE APP + geometry Circle { + radius 50 + } + } + Transform2D { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Circle" "radius 50"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 150 150 + children [ + Shape { + appearance USE APP + geometry Rectangle { + size 100 50 + } + } + Transform2D { + translation 0 -40 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Rectangle" "Size 100 50"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -40 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Curve2D Points:" "-50 0, -100 50, 0 20, 10 30, 40 80, 50 0"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -150 0 + children [ + Shape { + appearance USE APP + geometry Curve2D { + point DEF C2D Coordinate2D { + point [-50 0 -100 50 0 20 10 30 40 80 50 0] + } + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["no type"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 3] + point USE C2D + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["type [2 3]"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 170 0 + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [1 3 1] + point USE C2D + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["type [1 3 1]"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation -150 -150 + children [ + Shape { + appearance USE APP + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-50 0 -25 25 0 80 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["IndexedFaceSet2D" "-50 0, -25, 25, 0 80, 50 0"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance USE APP + geometry PointSet2D { + coord Coordinate2D { + point [-50 0 -25 25 0 80 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["PointSet2D" "-50 0, -25, 25, 0 80, 50 0"] + fontStyle USE FS + } + } + ] + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-texturing-lineargradient-simple.bt b/regression_tests/bifs-2D-texturing-lineargradient-simple.bt new file mode 100644 index 0000000..ae295b7 --- /dev/null +++ b/regression_tests/bifs-2D-texturing-lineargradient-simple.bt @@ -0,0 +1,75 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 500 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to fill object with linear gradients." + "Here the start and end point of the gradient are close" + "You can click on the rectangle to move the start and the end of gradient." + "You can also drag it." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Linear Gradient" + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + width 0 + } + } + texture DEF GL LinearGradient { + key [0 0.45 0.5 0.55 1] + keyValue [0.2118 0.447 0.039 0.2118 0.447 0.039 0.2627 0.933 0 0.2392 0.3098 0.04313 0.2392 0.3098 0.04313] + startPoint -1 0 + } + } + geometry DEF R Rectangle { + size 400 20 + } + } + DEF TS TouchSensor {} + ] + } + DEF SC Script { + eventIn SFVec3f set_frac + eventIn SFBool set_down + field SFNode grad USE GL + field SFBool isDown FALSE + url ["javascript:function set_down(value, timestamp) {isDown = value;}function set_frac(value, timestamp) {if (!isDown) return;pos = (value.x + 200)/400;grad.startPoint.x = pos - 1;grad.endPoint.x = 1 + pos;print('pos ' + pos);}" ] + } + ] +} + +ROUTE TS.isActive TO SC.set_down +ROUTE TS.hitPoint_changed TO SC.set_frac + diff --git a/regression_tests/bifs-2D-texturing-lineargradient-spread.bt b/regression_tests/bifs-2D-texturing-lineargradient-spread.bt new file mode 100644 index 0000000..fe3f212 --- /dev/null +++ b/regression_tests/bifs-2D-texturing-lineargradient-spread.bt @@ -0,0 +1,113 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the different methods for spreading the gradients when the size of the object to be filled is large" + "The end point of the gradient is also animated to change the gradient aspect." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Linear Gradient Spread Methods" + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Move over and click" "to change the spread method"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF GL LinearGradient { + endPoint 0 0.5 + key [0 0.5 1] + keyValue [0 0 1 1 0 0 0 1 0] + } + } + geometry Circle { + radius 100 + } + } + DEF TS TouchSensor {} + ] + } + DEF C Conditional { + buffer { + REPLACE GL.spreadMethod BY 1 + } + } + DEF RC Conditional { + buffer { + REPLACE GL.spreadMethod BY 0 + } + } + DEF C2 Conditional { + buffer { + REPLACE GL.spreadMethod BY 2 + } + } + DEF RC2 Conditional { + buffer { + REPLACE GL.spreadMethod BY 1 + } + } + DEF TIME TimeSensor { + cycleInterval 4 + loop TRUE + } + DEF CI PositionInterpolator2D { + key [0 0.5 1] + keyValue [0 0.5 1 0.5 0 0.5] + } + ] +} + +ROUTE TS.isOver TO C.activate +ROUTE TS.isOver TO RC.reverseActivate +ROUTE TS.isActive TO C2.activate +ROUTE TS.isActive TO RC2.reverseActivate +ROUTE TIME.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO GL.endPoint + diff --git a/regression_tests/bifs-2D-texturing-movietexture-shapes.bt b/regression_tests/bifs-2D-texturing-movietexture-shapes.bt new file mode 100644 index 0000000..5f226eb --- /dev/null +++ b/regression_tests/bifs-2D-texturing-movietexture-shapes.bt @@ -0,0 +1,285 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 440 + pixelHeight 440 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows a video displayed on the basic 2D shapes." + "Video, like image, is considered as a texture and described in the MovieTexture node." + "The url of this node points to the video using any internet protocol (http, rtsp) or using MPEG-4 OD." + "The MovieTexture node is associated to the geometry as a children of the Shape node. The video is then clipped along the bounding box of the shape." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.4 $" + "(C) 2002-2006 GPAC Team" + ] + title "Displaying Videos as a texture on any shape" + } + Transform2D { + translation -150 150 + children [ + Shape { + appearance DEF APP Appearance { + texture MovieTexture { + loop TRUE + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry IndexedLineSet2D { + coord Coordinate2D { + point [-50 0 0 50 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Text { + string ["IndexedLineSet2D" "[-50 0 0 50 50 0]"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 12 + } + } + } + ] + } + ] + } + Transform2D { + translation 0 150 + children [ + Shape { + appearance USE APP + geometry Circle { + radius 50 + } + } + Transform2D { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Circle" "radius 50"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 150 150 + children [ + Shape { + appearance USE APP + geometry Rectangle { + size 100 50 + } + } + Transform2D { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Rectangle" "Size 100 50"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -40 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Curve2D Points:" "-50 0, -100 50, 0 20, 10 30, 40 80, 50 0"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -150 0 + children [ + Shape { + appearance USE APP + geometry Curve2D { + point DEF C2D Coordinate2D { + point [-50 0 -100 50 0 20 10 30 40 80 50 0] + } + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["no type"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 3] + point USE C2D + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["type [2 3]"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 170 0 + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [1 3 1] + point USE C2D + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["type [1 3 1]"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation -150 -150 + children [ + Shape { + appearance USE APP + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-50 0 -25 25 0 80 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["IndexedFaceSet2D" "-50 0, -25, 25, 0 80, 50 0"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance USE APP + geometry PointSet2D { + coord Coordinate2D { + point [-50 0 -25 25 0 80 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["PointSet2D" "-50 0, -25, 25, 0 80, 50 0"] + fontStyle USE FS + } + } + ] + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-texturing-pixeltexture.bt b/regression_tests/bifs-2D-texturing-pixeltexture.bt new file mode 100644 index 0000000..9b8872a --- /dev/null +++ b/regression_tests/bifs-2D-texturing-pixeltexture.bt @@ -0,0 +1,191 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + DEF B Background2D { + backColor 1 0 0 + } + WorldInfo { + info [ + "This test shows the usage of the PixelTexture node." + "The PixelTexture allows to define a non-vectorial synthetic texture. The texture is defined by giving its size, the color depth in byte, and the color components (ARGB) for each pixel in the texture" + "In this scene, 4 circles are filled with a 4x4-pixels texture whose color depth are respectively 1 byte, 2 bytes, 3 and 4 bytes." + "1 byte corresponds to 8-bits grey scale" + "2 bytes corresponds to 8-bits grey scale + 8-bits transparency" + "3 bytes corresponds to 24-bits RGB" + "4 bytes corresponds to 32-bits ARGB" + "The background color of the scene is animated to see the effect on the texture." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Synthetic Non Vectorial Texture" + } + Transform2D { + translation -80 90 + children [ + Shape { + appearance Appearance { + texture PixelTexture { + image 4 4 1 + 0xFF 0xFF 0x00 0x00 + 0xFF 0xFF 0x00 0x00 + 0x00 0x00 0xFF 0xFF + 0x00 0x00 0xFF 0xFF + } + } + geometry Circle { + radius 50 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance DEF TA Appearance { + material Material2D { + emissiveColor 1 1 1 + filled TRUE + } + } + geometry Text { + string [ "Color Depth:" "1 Byte" ] + fontStyle DEF FS FontStyle { + size 20 + justify [ "MIDDLE" "MIDDLE" ] + family "SANS" + } + } + } + ] + } + ] + } + Transform2D { + translation 80 90 + children [ + Shape { + appearance Appearance { + texture PixelTexture { + image 4 4 2 + 0x00FF 0x00FF 0x0000 0x0000 + 0x00FF 0x00FF 0x0000 0x0000 + 0x0000 0x0000 0x00FF 0x00FF + 0x0000 0x0000 0x00FF 0x00FF + } + } + geometry Circle { + radius 50 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TA + geometry Text { + string [ "Color Depth:" "2 Bytes" ] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation -80 -60 + children [ + Shape { + appearance Appearance { + texture PixelTexture { + image 4 4 3 + 0xFFFFFF 0xFFFFFF 0x00FF00 0x00FF00 + 0xFFFFFF 0xFFFFFF 0x00FF00 0x00FF00 + 0xFF00FF 0xFF00FF 0x000000 0x000000 + 0xFF00FF 0xFF00FF 0x000000 0x000000 + } + } + geometry Circle { + radius 50 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TA + geometry Text { + string [ "Color Depth:" "3 Bytes" ] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 80 -60 + children [ + Shape { + appearance Appearance { + texture PixelTexture { + image 4 4 4 + 0xFFFFFF00 0xFFFFFF00 0x00FF00FF 0x00FF00FF + 0xFFFFFF00 0xFFFFFF00 0x00FF00FF 0x00FF00FF + 0xFF00FFFF 0xFF00FFFF 0x00000000 0x00000000 + 0xFF00FFFF 0xFF00FFFF 0x00000000 0x00000000 + } + } + geometry Circle { + radius 50 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TA + geometry Text { + string [ "Color Depth:" "4 Bytes" ] + fontStyle USE FS + } + } + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF CI ColorInterpolator { + key [0 0.5 1] + keyValue [1 0 0 0 0 1 1 0 0] + } + ] +} + +ROUTE TS.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO B.backColor + diff --git a/regression_tests/bifs-2D-texturing-radialgradient-simple.bt b/regression_tests/bifs-2D-texturing-radialgradient-simple.bt new file mode 100644 index 0000000..1365479 --- /dev/null +++ b/regression_tests/bifs-2D-texturing-radialgradient-simple.bt @@ -0,0 +1,94 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to fill object with radial gradients" + "The focal point can be moved to see the effect on the gradient." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Radial Gradient Focal Point" + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Drag mouse on Shape" "to change the focal point"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF GR RadialGradient { + focalPoint 0.5 0.5 + key [0 0.5 1] + keyValue [0 0 1 0 1 1 0 1 0] + radius 0.25 + spreadMethod 1 + } + } + geometry DEF R Rectangle { + size 200 200 + } + } + DEF PS PlaneSensor2D { + maxPosition 100 100 + minPosition -100 -100 + } + ] + } + DEF V Valuator { + Factor1 0.01 + Factor2 0.01 + Factor3 0 + Factor4 0 + Offset1 0.5 + Offset2 0.5 + } + ] +} + +ROUTE PS.translation_changed TO V.inSFVec2f +ROUTE V.outSFVec2f TO GR.focalPoint + diff --git a/regression_tests/bifs-2D-texturing-radialgradient-spread.bt b/regression_tests/bifs-2D-texturing-radialgradient-spread.bt new file mode 100644 index 0000000..2428dc7 --- /dev/null +++ b/regression_tests/bifs-2D-texturing-radialgradient-spread.bt @@ -0,0 +1,118 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows the different methods for spreading radial gradients when the size of the object to be filled is large" + "The focal point of the gradient is also animated to change the gradient aspect over time." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Radial Gradient Spread Methods" + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Move over and click" "to change the spread method"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + width 0 + } + } + texture DEF GR RadialGradient { + focalPoint 0 0.5 + key [0 0.5 1] + keyValue [0 0 1 1 0 0 0 1 0] + } + } + geometry DEF R Rectangle { + size 200 200 + } + } + DEF TS TouchSensor {} + ] + } + DEF C Conditional { + buffer { + REPLACE GR.spreadMethod BY 2 + } + } + DEF RC Conditional { + buffer { + REPLACE GR.spreadMethod BY 0 + } + } + DEF C2 Conditional { + buffer { + REPLACE GR.spreadMethod BY 1 + } + } + DEF RC2 Conditional { + buffer { + REPLACE GR.spreadMethod BY 2 + } + } + DEF TIME TimeSensor { + cycleInterval 4 + loop TRUE + } + DEF CI PositionInterpolator2D { + key [0 0.25 0.5 0.75 1] + keyValue [0 0.5 0.5 1 1 0.5 0.5 0 0 0.5] + } + ] +} + +ROUTE TS.isOver TO C.activate +ROUTE TS.isOver TO RC.reverseActivate +ROUTE TS.isActive TO C2.activate +ROUTE TS.isActive TO RC2.reverseActivate +ROUTE TIME.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO GR.focalPoint + diff --git a/regression_tests/bifs-2D-texturing-texturetransform-base.bt b/regression_tests/bifs-2D-texturing-texturetransform-base.bt new file mode 100644 index 0000000..d668e9e --- /dev/null +++ b/regression_tests/bifs-2D-texturing-texturetransform-base.bt @@ -0,0 +1,234 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how a texture can be modified: either changing its positionning in the shape or its transparency" + "The positioning is given using the TextureTransform node and the transparency using the Material2D node." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "TextureTransform" + } + Transform2D { + translation -180 0 + children [ + Shape { + appearance Appearance { + texture DEF TEXTURE ImageTexture { + url [od:10] + } + } + geometry DEF REC Rectangle { + size 150 100 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["No Transform"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 14 + } + } + } + ] + } + ] + } + Transform2D { + translation 0 140 + children [ + Shape { + appearance Appearance { + texture USE TEXTURE + textureTransform TextureTransform { + rotation 0.78 + } + } + geometry USE REC + } + Transform2D { + translation 0 -60 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["TextureTransform: rotation PI/4"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 10 + children [ + Shape { + appearance Appearance { + texture USE TEXTURE + textureTransform TextureTransform { + scale 0.5 1.5 + } + } + geometry USE REC + } + Transform2D { + translation 0 -60 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["TextureTransform: scale 0.5 1.5"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -120 + children [ + Shape { + appearance Appearance { + texture USE TEXTURE + textureTransform TextureTransform { + translation 0.5 0 + } + } + geometry USE REC + } + Transform2D { + translation 0 -60 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["TextureTransform: translation 0.5 0"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 180 100 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 1 + filled TRUE + transparency 0.8 + } + texture USE TEXTURE + } + geometry USE REC + } + Transform2D { + translation 0 -60 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Material Transparency 0.8"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 180 -100 + children [ + Shape { + appearance Appearance { + texture USE TEXTURE + textureTransform TextureTransform { + center 0.5 0.5 + rotation 0.78 + } + } + geometry Circle { + radius 60 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Rotated On a Circle"] + fontStyle USE FS + } + } + ] + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-texturing-texturetransform-interact.bt b/regression_tests/bifs-2D-texturing-texturetransform-interact.bt new file mode 100644 index 0000000..a066d93 --- /dev/null +++ b/regression_tests/bifs-2D-texturing-texturetransform-interact.bt @@ -0,0 +1,231 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how to apply transformations to texture independantly of the transformation on the shape." + "The TextureTransform node is used here." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Transforming Textures" + } + Transform2D { + translation -150 100 + children [ + Shape { + appearance Appearance { + texture DEF IMG ImageTexture { + url [od:10] + } + textureTransform DEF TXT1 TextureTransform { + center 0.5 0.5 + } + } + geometry DEF REC Rectangle { + size 200 100 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Drag mouse to change" "rotation angle"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 14 + } + } + } + ] + } + DEF PS1 PlaneSensor2D { + maxPosition 400 400 + minPosition -400 -400 + } + ] + } + Transform2D { + translation -150 -100 + children [ + Shape { + appearance Appearance { + texture USE IMG + textureTransform DEF TXT2 TextureTransform { + center 0.5 0.5 + rotation 0.78 + } + } + geometry USE REC + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Drag mouse to change" "rotation center"] + fontStyle USE FS + } + } + ] + } + DEF PS2 PlaneSensor2D { + maxPosition 100 50 + minPosition -100 -50 + } + ] + } + Transform2D { + translation 150 100 + children [ + Shape { + appearance Appearance { + texture USE IMG + textureTransform DEF TXT3 TextureTransform {} + + } + geometry USE REC + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Drag mouse to" "translate texture"] + fontStyle USE FS + } + } + ] + } + DEF PS3 PlaneSensor2D { + maxPosition 400 400 + minPosition -400 -400 + } + ] + } + Transform2D { + translation 150 -100 + children [ + Shape { + appearance Appearance { + texture USE IMG + textureTransform DEF TXT4 TextureTransform { + rotation 0.78 + } + } + geometry USE REC + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Drag mouse to change" "texture scaling"] + fontStyle USE FS + } + } + ] + } + DEF PS4 PlaneSensor2D { + maxPosition 100 100 + minPosition -100 -100 + } + ] + } + DEF V1 Valuator { + Factor1 0.01 + } + DEF V2 Valuator { + Factor1 0.005 + Factor2 0.01 + Offset1 0.5 + Offset2 0.5 + } + DEF V3 Valuator { + Factor1 -0.005 + Factor2 -0.01 + } + DEF V4 Valuator { + Factor1 0.1 + Factor2 0.1 + } + DEF TS TimeSensor { + cycleInterval 4 + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1.56 0] + } + ] +} + +ROUTE PS1.translation_changed TO V1.inSFVec2f +ROUTE V1.outSFFloat TO TXT1.rotation +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO TXT2.rotation +ROUTE PS2.translation_changed TO V2.inSFVec2f +ROUTE V2.outSFVec2f TO TXT2.center +ROUTE PS3.translation_changed TO V3.inSFVec2f +ROUTE V3.outSFVec2f TO TXT3.translation +ROUTE PS4.translation_changed TO V4.inSFVec2f +ROUTE V4.outSFVec2f TO TXT4.scale + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-texturing-texturetransform-transformmatrix2D.bt b/regression_tests/bifs-2D-texturing-texturetransform-transformmatrix2D.bt new file mode 100644 index 0000000..48936ef --- /dev/null +++ b/regression_tests/bifs-2D-texturing-texturetransform-transformmatrix2D.bt @@ -0,0 +1,242 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows a texture being transformed (e.g. skewed) and this transformation is animated." + "The TransformMatrix2D is used here to allow skewing." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "ImageTexture" + } + Transform2D { + translation -180 100 + children [ + Shape { + appearance Appearance { + texture DEF TEXTURE ImageTexture { + url [od:10] + } + textureTransform DEF MX1 TransformMatrix2D {} + + } + geometry DEF REC Rectangle { + size 150 100 + } + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Texture horizontal scaling"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 14 + } + } + } + ] + } + ] + } + Transform2D { + translation -180 -100 + children [ + Shape { + appearance Appearance { + texture USE TEXTURE + textureTransform DEF MX2 TransformMatrix2D {} + + } + geometry USE REC + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Texture vertical scaling"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 100 + children [ + Shape { + appearance Appearance { + texture USE TEXTURE + textureTransform DEF MX3 TransformMatrix2D {} + + } + geometry USE REC + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Texture horizontal skewing"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -100 + children [ + Shape { + appearance Appearance { + texture USE TEXTURE + textureTransform DEF MX4 TransformMatrix2D { + mxy 1 + } + } + geometry USE REC + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Texture vertical skewing"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 180 100 + children [ + Shape { + appearance Appearance { + texture USE TEXTURE + textureTransform DEF MX5 TransformMatrix2D {} + + } + geometry USE REC + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Texture horizontal translating"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 180 -100 + children [ + Shape { + appearance Appearance { + texture USE TEXTURE + textureTransform DEF MX6 TransformMatrix2D {} + + } + geometry USE REC + } + Transform2D { + translation 0 -70 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Texture vertical translating"] + fontStyle USE FS + } + } + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 2 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO MX1.mxx +ROUTE SI.value_changed TO MX2.myy +ROUTE SI.value_changed TO MX3.mxy +ROUTE SI.value_changed TO MX4.myx +ROUTE SI.value_changed TO MX5.tx +ROUTE SI.value_changed TO MX6.ty + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-2D-viewport-complete.bt b/regression_tests/bifs-2D-viewport-complete.bt new file mode 100644 index 0000000..30f6aeb --- /dev/null +++ b/regression_tests/bifs-2D-viewport-complete.bt @@ -0,0 +1,715 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows usage of the viewport per layer" "Translated from SVG" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "SVG Test Suite - (C) W3C 2002"] + title "Animation Stream" + } + Background2D { + backColor 1 1 1 + } + Transform2D { + translation -225 225 + children [ + Transform2D { + translation 0 -30 + children [ + Transform2D { + translation 10 -30 + children [ + Shape { + appearance DEF N0 Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["BIFS to fit"] + fontStyle DEF N13 FontStyle { + family ["SANS"] + size 9 + } + } + } + ] + } + Transform2D { + translation 20 -40 + children [ + DEF N4 Transform2D { + children [ + Transform2D { + translation 15 -20 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + lineProps LineProperties { + lineColor 1 0 0 + } + } + } + geometry Rectangle { + size 29 39 + } + } + ] + } + Transform2D { + translation 0 -5 + children [ + Transform2D { + translation 15 -15 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 0 + filled TRUE + } + } + geometry Circle { + radius 10 + } + } + ] + } + Transform2D { + translation 12 -12 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Circle { + radius 1.5 + } + } + ] + } + Transform2D { + translation 17 -12 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Circle { + radius 1.5 + } + } + ] + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + lineProps LineProperties { + width 2 + } + } + } + geometry Curve2D { + type [2] + point Coordinate2D { + point [10 -19 15 -22.75 15 -22.75 20 -19] + } + } + } + ] + } + ] + } + ] + } + Transform2D { + translation 10 -110 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["Viewport 1"] + fontStyle USE N13 + } + } + ] + } + Transform2D { + translation 10 -120 + children [ + DEF N12 Transform2D { + translation 25 -15 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0 0 1 + } + } + } + geometry Rectangle { + size 49 29 + } + } + ] + } + ] + } + Transform2D { + translation 10 -180 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["Viewport 2"] + fontStyle USE N13 + } + } + ] + } + Transform2D { + translation 20 -190 + children [ + DEF N10 Transform2D { + translation 15 -30 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0 0 1 + } + } + } + geometry Rectangle { + size 29 59 + } + } + ] + } + ] + } + Transform2D { + translation 100 -60 + children [ + DEF N7 Transform2D { + translation 0 30 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["-------------- fit= 1 --------------"] + fontStyle USE N13 + } + } + ] + } + Transform2D { + children [ + DEF N14 Transform2D { + translation 15 15 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["alignment" "[ -1 * ]"] + fontStyle DEF N1 FontStyle { + family ["SANS"] + justify ["MIDDLE"] + size 9 + } + } + } + ] + } + USE N12 + Transform2D { + translation 25 -15 + children [ + Layer2D { + size 50 30 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [-1 0] + fit 1 + } + } + ] + } + ] + } + Transform2D { + translation 70 0 + children [ + DEF N9 Transform2D { + translation 15 15 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["alignment" "[ 0 * ]"] + fontStyle USE N1 + } + } + ] + } + USE N12 + Transform2D { + translation 25 -15 + children [ + Layer2D { + size 50 30 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [0 0] + fit 1 + } + } + ] + } + ] + } + Transform2D { + translation 0 -70 + children [ + DEF N5 Transform2D { + translation 15 15 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["alignment" "[ 1 * ]"] + fontStyle USE N1 + } + } + ] + } + USE N12 + Transform2D { + translation 25 -15 + children [ + Layer2D { + size 50 30 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [1 0] + fit 1 + } + } + ] + } + ] + } + ] + } + Transform2D { + translation 250 -60 + children [ + USE N7 + Transform2D { + children [ + DEF N11 Transform2D { + translation 15 15 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["alignment" "[ * -1 ]"] + fontStyle USE N1 + } + } + ] + } + USE N10 + Transform2D { + translation 15 -30 + children [ + Layer2D { + size 30 60 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [0 -1] + fit 1 + } + } + ] + } + Transform2D { + translation 50 0 + children [ + DEF N3 Transform2D { + translation 15 15 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["alignment" "[ * 0 ]"] + fontStyle USE N1 + } + } + ] + } + USE N10 + Transform2D { + translation 15 -30 + children [ + Layer2D { + size 30 60 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [0 0] + fit 1 + } + } + ] + } + ] + } + Transform2D { + translation 100 0 + children [ + DEF N2 Transform2D { + translation 15 15 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["alignment" "[ * 1 ]"] + fontStyle USE N1 + } + } + ] + } + USE N10 + Transform2D { + translation 15 -30 + children [ + Layer2D { + size 30 60 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [0 1] + fit 1 + } + } + ] + } + ] + } + ] + } + ] + } + Transform2D { + translation 100 -220 + children [ + DEF N6 Transform2D { + translation 0 30 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["---------- fit = 2 ----------"] + fontStyle USE N13 + } + } + ] + } + USE N14 + USE N10 + Transform2D { + translation 15 -30 + children [ + Layer2D { + size 30 60 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [-1 0] + fit 2 + } + } + ] + } + Transform2D { + translation 50 0 + children [ + USE N9 + USE N10 + Transform2D { + translation 15 -30 + children [ + Layer2D { + size 30 60 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [0 0] + fit 2 + } + } + ] + } + ] + } + Transform2D { + translation 100 0 + children [ + USE N5 + USE N10 + Transform2D { + translation 15 -30 + children [ + Layer2D { + size 30 60 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [1 0] + fit 2 + } + } + ] + } + ] + } + ] + } + Transform2D { + translation 250 -190 + children [ + Transform2D { + translation 0 -30 + children [ + USE N6 + Transform2D { + children [ + USE N11 + USE N12 + Transform2D { + translation 25 -15 + children [ + Layer2D { + size 50 30 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [0 -1] + fit 2 + } + } + ] + } + ] + } + Transform2D { + translation 70 0 + children [ + USE N3 + USE N12 + Transform2D { + translation 25 -15 + children [ + Layer2D { + size 50 30 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [0 0] + fit 2 + } + } + ] + } + ] + } + Transform2D { + translation 140 0 + children [ + USE N2 + USE N12 + Transform2D { + translation 25 -15 + children [ + Layer2D { + size 50 30 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + alignment [0 1] + fit 2 + } + } + ] + } + ] + } + ] + } + ] + } + Transform2D { + translation 175 -300 + children [ + Transform2D { + translation 0 -30 + children [ + Transform2D { + translation 0 30 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["---------- fit = 0 ----------"] + fontStyle USE N13 + } + } + ] + } + Transform2D { + children [ + DEF N8 Transform2D { + translation 15 15 + children [ + Shape { + appearance USE N0 + geometry Text { + string ["alignment" "[ * * ]"] + fontStyle USE N1 + } + } + ] + } + USE N12 + Transform2D { + translation 25 -15 + children [ + Layer2D { + size 50 30 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + } + } + ] + } + ] + } + Transform2D { + translation 100 0 + children [ + USE N8 + USE N10 + Transform2D { + translation 15 -30 + children [ + Layer2D { + size 30 60 + children [ + USE N4 + ] + viewport Viewport { + position 15 -20 + size 30 40 + } + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-2D-viewport-simple.bt b/regression_tests/bifs-2D-viewport-simple.bt new file mode 100644 index 0000000..54c3827 --- /dev/null +++ b/regression_tests/bifs-2D-viewport-simple.bt @@ -0,0 +1,88 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + DEF B1 Background2D { + url [od:10] + } + WorldInfo { + info ["This shows usage of Viewport in the scene" "viewport can be changed through the player's viewport list" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Viewport Test" + } + DEF VP Viewport { + size 200 200 + orientation 0.5 + alignment [0 0] + fit 2 + description "basic Viewport" + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture RadialGradient { + focalPoint 0.5 0.5 + key [0 1] + keyValue [0 1 0 0 1 1] + radius 0.8 + } + } + geometry Rectangle { + size 100 100 + } + } + DEF TS TouchSensor {} + ] + } + ] +} + +ROUTE TS.isOver TO VP.set_bind + +AT 0 { + REPLACE VP.set_bind BY FALSE +} + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 22 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-background-images.bt b/regression_tests/bifs-3D-background-images.bt new file mode 100644 index 0000000..9805472 --- /dev/null +++ b/regression_tests/bifs-3D-background-images.bt @@ -0,0 +1,87 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + includeInlineProfileLevelFlag true + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + WorldInfo { + info ["This shows Background with transparent texture on all back planes" "" "GPAC Regression Tests" "$Date: 2008/06/26 07:55:39 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Background Test" + } + Viewpoint {} + Background { + groundAngle [0.56 1.2] + groundColor [0 0 0, 0.1 0.8 0.8, 0.5 1 0] + skyAngle [0.5 1.2 1.8] + skyColor [0 0 0.5, 0.2 0.4 0.2, 0.4 0.1 0.0, 0.5 0.5 0.2] + frontUrl ["20"] + backUrl ["20"] + leftUrl ["20"] + rightUrl ["20"] + topUrl ["20"] + bottomUrl ["20"] + } + +#if 0 + Transform { + children [ + Shape { + appearance Appearance { + material Material { + diffuseColor 1 1 1 + } + texture ImageTexture { url ["./auxiliary_files/logo.jpg"] } + } + geometry Box { + size 3 2 5 + } + } + ] + } +#endif + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 20 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.png" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-background.bt b/regression_tests/bifs-3D-background.bt new file mode 100644 index 0000000..8500f6f --- /dev/null +++ b/regression_tests/bifs-3D-background.bt @@ -0,0 +1,56 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + includeInlineProfileLevelFlag true + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + WorldInfo { + info ["This shows a background node" "with ground and sky" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Background Test" + } + Viewpoint {} + Background { + groundAngle [0.56 1.2] + groundColor [0 0 0, 0.1 0.8 0.8, 0.5 1 0] + skyAngle [0.5 1.2 1.8] + skyColor [0 0 0.5, 0.2 0.4 0.2, 0.4 0.1 0.0, 0.5 0.5 0.2] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { + diffuseColor 1 1 1 + } + texture ImageTexture { url ["./auxiliary_files/logo.jpg"] } + } + geometry Box { + size 3 2 5 + } + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-3D-interactivity-collision-proxy.bt b/regression_tests/bifs-3D-interactivity-collision-proxy.bt new file mode 100644 index 0000000..d288452 --- /dev/null +++ b/regression_tests/bifs-3D-interactivity-collision-proxy.bt @@ -0,0 +1,80 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 200 + } + } + } + ] +} + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "Collision Test" + info ["This shows the collision node " "used with a sphere proxy" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + DEF C Collision { + proxy Shape { geometry Sphere { radius 6 } } + children [ + Shape { + appearance Appearance { + material DEF MAT Material { diffuseColor 0 1 0 } + } + geometry Box {size 20 2 0.1} + } + ] + } + DEF S Script { + eventIn SFTime on_collide + eventOut MFString outText + eventOut SFColor outCol + url "javascript: + function on_collide(value) { + v = new SFColor(1, 0, 0); + outCol = v; + outText[0] = 'collision time: ' + value; + } + " + } + + Transform { + translation 0 2 0 + children [ + Shape { + appearance Appearance { + material Material2D { emissiveColor 0 0 0 filled TRUE } + } + geometry DEF T Text { + string "no collision" + fontStyle FontStyle { + size 2 + justify "MIDDLE" + } + } + } + ] + } + + ] +} + +ROUTE C.collideTime TO S.on_collide +ROUTE S.outText TO T.string +ROUTE S.outCol TO MAT.diffuseColor + diff --git a/regression_tests/bifs-3D-interactivity-collision.bt b/regression_tests/bifs-3D-interactivity-collision.bt new file mode 100644 index 0000000..9a74e32 --- /dev/null +++ b/regression_tests/bifs-3D-interactivity-collision.bt @@ -0,0 +1,79 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 200 + } + } + } + ] +} + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "Collision Test" + info ["This shows the collision node " "used without proxy" "" "GPAC Regression Tests" "$Date: 2008/06/26 07:55:39 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + DEF C Collision { + children [ + DEF B Shape { + appearance Appearance { + material DEF MAT Material { diffuseColor 0 1 0 } + } + geometry Box {size 20 0.5 0.1} + } + ] + } + DEF S Script { + eventIn SFTime on_collide + eventOut MFString outText + eventOut SFColor outCol + url "javascript: + function on_collide(value) { + v = new SFColor(1, 0, 0); + outCol = v; + outText[0] = 'collision time: ' + value; + } + " + } + + Transform { + translation 0 0 0.055 + children [ + Shape { + appearance Appearance { + material Material2D { emissiveColor 0 0 0 filled TRUE } + } + geometry DEF T Text { + string "no collision" + fontStyle FontStyle { + size 2 + justify "MIDDLE" + } + } + } + ] + } + + ] +} + +ROUTE C.collideTime TO S.on_collide +ROUTE S.outText TO T.string +ROUTE S.outCol TO MAT.diffuseColor + diff --git a/regression_tests/bifs-3D-interactivity-cylindersensor.bt b/regression_tests/bifs-3D-interactivity-cylindersensor.bt new file mode 100644 index 0000000..8992761 --- /dev/null +++ b/regression_tests/bifs-3D-interactivity-cylindersensor.bt @@ -0,0 +1,90 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + WorldInfo { + title "CylinderSensor test" + info ["This shows a cylinderSensor" "exercising isActive, rotationChanged and trackPoint_changed events" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + Background2D {backColor 1 1 1} + Viewpoint {} + DEF TF Transform { + translation 2 0 0 + children [ + DEF CS CylinderSensor { + } + Shape { + appearance Appearance { + material Material { + diffuseColor 1 0 0 + } + } + geometry Box { + size 2 2 1 + } + } + ] + } + + Transform { + translation -5 1 -10 + children [ + Shape { + appearance Appearance { + material DEF M Material { + diffuseColor 1 0 0.245 + } + } + geometry Sphere { radius 2 } + } + ] + } + Transform { + translation 0 -3 0 + children [ + Shape { + #appearance Appearance { material Material2D { filled TRUE emissiveColor 0 1 0} } + appearance Appearance { material Material {emissiveColor 0 1 0 diffuseColor 0 0 1} } + geometry DEF TXT Text { string "Trackpoint" fontStyle FontStyle {size 0.8 justify "MIDDLE" } } + } + ] + } + DEF V Valuator{} + + DEF C Conditional { + buffer { REPLACE M.diffuseColor BY 0 0.25 0.65 } + } + DEF RC Conditional { + buffer { REPLACE M.diffuseColor BY 1 0 0.245} + } + + ] +} +ROUTE CS.isActive TO C.activate +ROUTE CS.isActive TO RC.reverseActivate +ROUTE CS.rotation_changed TO TF.rotation + +ROUTE CS.trackPoint_changed TO V.inSFVec3f +ROUTE V.outMFString TO TXT.string diff --git a/regression_tests/bifs-3D-interactivity-planesensor.bt b/regression_tests/bifs-3D-interactivity-planesensor.bt new file mode 100644 index 0000000..7157084 --- /dev/null +++ b/regression_tests/bifs-3D-interactivity-planesensor.bt @@ -0,0 +1,93 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + Viewpoint {} + WorldInfo { + title "PlaneSensor test" + info ["This shows a planeSensor" "exercising isActive, translationChanged and trackPoint_changed events" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + Background2D {backColor 1 1 1} + + DEF TF Transform { + translation 2 0 0 + children [ + DEF PS PlaneSensor { + maxPosition 2 2 + minPosition -2 -2 + offset 2 0 0 + } + Shape { + appearance Appearance { + material Material { + diffuseColor 1 0 0 + } + } + geometry Box { + size 2 2 1 + } + } + ] + } + + Transform { + translation -5 1 -10 + children [ + Shape { + appearance Appearance { + material DEF M Material { + diffuseColor 1 0 0.245 + } + } + geometry Sphere { radius 2 } + } + ] + } + Transform { + translation 0 -3 0 + children [ + Shape { + appearance Appearance { material Material2D { filled TRUE emissiveColor 0 0 0} } + geometry DEF TXT Text { string "Trackpoint" fontStyle FontStyle {size 0.8 justify "MIDDLE"} } + } + ] + } + DEF V Valuator{} + + DEF C Conditional { + buffer { REPLACE M.diffuseColor BY 0 0.25 0.65 } + } + DEF RC Conditional { + buffer { REPLACE M.diffuseColor BY 1 0 0.245} + } + + ] +} +ROUTE PS.isActive TO C.activate +ROUTE PS.isActive TO RC.reverseActivate +ROUTE PS.translation_changed TO TF.translation + +ROUTE PS.trackPoint_changed TO V.inSFVec3f +ROUTE V.outMFString TO TXT.string diff --git a/regression_tests/bifs-3D-interactivity-proximitysensor.bt b/regression_tests/bifs-3D-interactivity-proximitysensor.bt new file mode 100644 index 0000000..66c74fc --- /dev/null +++ b/regression_tests/bifs-3D-interactivity-proximitysensor.bt @@ -0,0 +1,80 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "ProximitySensor test" + info ["This shows a ProximitySensor" "exercising isActive event" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + Viewpoint {position 0 0 10} + DEF Box Transform { + children [ + DEF PS ProximitySensor { + center 0 0 0 + size 8 8 8 + } + Shape { + appearance Appearance { + material DEF M1 Material { + diffuseColor 1 0 0 + } + } + geometry Box { + size 2 2 2 + } + } + ] + } + + + DEF TS TimeSensor { + cycleInterval 5.0 + loop TRUE + startTime -1 + } + + DEF Animation OrientationInterpolator { + key [0.0, + 0.20, + 0.40, + 0.60, + 0.80, + 1.0] + keyValue [0 1 0 0, + 0 1 0 1.57079, + 0 1 0 3.14159, + 0 1 0 4.71238, + 0 1 0 5.52456, + 0 1 0 6.28318] + } + ] +} + +ROUTE PS.enterTime TO TS.startTime +ROUTE PS.exitTime TO TS.stopTime + +ROUTE TS.fraction_changed TO Animation.set_fraction +ROUTE Animation.value_changed TO Box.rotation diff --git a/regression_tests/bifs-3D-interactivity-spheresensor.bt b/regression_tests/bifs-3D-interactivity-spheresensor.bt new file mode 100644 index 0000000..9447374 --- /dev/null +++ b/regression_tests/bifs-3D-interactivity-spheresensor.bt @@ -0,0 +1,90 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + Viewpoint {} + WorldInfo { + title "SphereSensor test" + info ["This shows a SphereSensor" "exercising isActive, rotationChanged and trackPoint_changed events" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + Background2D {backColor 1 1 1} + + DEF TF Transform { + translation 2 0 0 + children [ + DEF CS SphereSensor { + } + Shape { + appearance Appearance { + material Material { + diffuseColor 1 0 0 + } + } + geometry Box { + size 4 4 1 + } + } + ] + } + + Transform { + translation -5 1 -10 + children [ + Shape { + appearance Appearance { + material DEF M Material { + diffuseColor 1 0 0.245 + } + } + geometry Sphere { radius 2 } + } + ] + } + Transform { + translation 0 -3 0 + children [ + Shape { + appearance Appearance { material Material2D { filled TRUE emissiveColor 0 0 0} } + geometry DEF TXT Text { string "Trackpoint" fontStyle FontStyle {size 0.8 justify "MIDDLE"} } + } + ] + } + DEF V Valuator{} + + DEF C Conditional { + buffer { REPLACE M.diffuseColor BY 0 0.25 0.65 } + } + DEF RC Conditional { + buffer { REPLACE M.diffuseColor BY 1 0 0.245} + } + + ] +} +ROUTE CS.isActive TO C.activate +ROUTE CS.isActive TO RC.reverseActivate +ROUTE CS.rotation_changed TO TF.rotation + +ROUTE CS.trackPoint_changed TO V.inSFVec3f +ROUTE V.outMFString TO TXT.string diff --git a/regression_tests/bifs-3D-interactivity-visibilitysensor.bt b/regression_tests/bifs-3D-interactivity-visibilitysensor.bt new file mode 100644 index 0000000..25dbe7b --- /dev/null +++ b/regression_tests/bifs-3D-interactivity-visibilitysensor.bt @@ -0,0 +1,100 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + + Viewpoint {position 0 0 10} + + WorldInfo { + title "VisibilitySensor test" + info ["This shows a VisibiliitySensor" "controling spinning of a cube" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + + Transform { + translation 10 0 0 + children [ + DEF PS VisibilitySensor { + center 0 0 0 + size 8 8 8 + } + Shape { + appearance Appearance { + material Material { + diffuseColor 1 1 0 + } + } + geometry Box { + size 8 8 8 + } + } + ] + } + + + DEF Box Transform { + children [ + Shape { + appearance Appearance { + material DEF M1 Material { + diffuseColor 1 0 0 + } + } + geometry Box { + size 2 2 2 + } + } + ] + } + + + DEF TS TimeSensor { + cycleInterval 5.0 + loop TRUE + startTime -1 + } + + DEF Animation OrientationInterpolator { + key [0.0, + 0.20, + 0.40, + 0.60, + 0.80, + 1.0] + keyValue [0 1 0 0, + 0 1 0 1.57079, + 0 1 0 3.14159, + 0 1 0 4.71238, + 0 1 0 5.52456, + 0 1 0 6.28318] + } + ] +} + +ROUTE PS.enterTime TO TS.startTime +ROUTE PS.exitTime TO TS.stopTime + +ROUTE TS.fraction_changed TO Animation.set_fraction +ROUTE Animation.value_changed TO Box.rotation diff --git a/regression_tests/bifs-3D-lighting-directionalLight.bt b/regression_tests/bifs-3D-lighting-directionalLight.bt new file mode 100644 index 0000000..e9063de --- /dev/null +++ b/regression_tests/bifs-3D-lighting-directionalLight.bt @@ -0,0 +1,63 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +Group { +children [ + NavigationInfo {type ["ANY" "EXAMINE"] headlight FALSE} + + WorldInfo { + title "DirectionalLight test" + info ["This shows a directional light" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + DirectionalLight { + color 1 0 0 + direction 0 1 1 + } + DEF BB Group { + children [ + DEF B Shape { + appearance Appearance { material Material {}} + geometry Box { size 1 1 1} + } + Transform { + translation 2 0 0 + children [ USE B] + } + Transform { + translation -2 0 0 + children [ USE B] + } + ] + } + Transform { + translation 0 2 0 + children [ USE BB] + } + Transform { + translation 0 -2 0 + children [ USE BB] + } + ] +} diff --git a/regression_tests/bifs-3D-lighting-fog.bt b/regression_tests/bifs-3D-lighting-fog.bt new file mode 100644 index 0000000..a5d4d69 --- /dev/null +++ b/regression_tests/bifs-3D-lighting-fog.bt @@ -0,0 +1,56 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + WorldInfo { + title "Fog test" + info ["This shows a fog" "with varying visibility range" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + DEF AFOG Fog {color 0.2 0.2 0.2 visibilityRange 4} + Transform { + children [ + Inline { url "./auxiliary_files/nefertiti.wrl"} + DirectionalLight { color 1 0 0 direction 1 1 -1 } + DEF TOUCH TouchSensor{} + ] + } + DEF TS TimeSensor {cycleInterval 6.0 loop TRUE } + DEF SI ScalarInterpolator{ + key [0 0.5 1] + keyValue [16 0 16] + } + DEF V Valuator { + Factor1 -1 + Offset1 1 + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO AFOG.visibilityRange + +ROUTE TOUCH.isOver TO V.inSFBool +ROUTE V.outSFBool TO AFOG.set_bind + diff --git a/regression_tests/bifs-3D-lighting-pointlight.bt b/regression_tests/bifs-3D-lighting-pointlight.bt new file mode 100644 index 0000000..b1ef0e0 --- /dev/null +++ b/regression_tests/bifs-3D-lighting-pointlight.bt @@ -0,0 +1,71 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +Group { +children [ + NavigationInfo {type ["ANY" "EXAMINE"] headlight FALSE} + + WorldInfo { + title "PointLight test" + info ["This shows a point light" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + DEF SP PointLight { + location 0.0 0.0 2.0 + color 0 0 1 + attenuation 1 0.5 0 + } + DEF BB Group { + children [ + DEF B Shape { + appearance Appearance { material Material {}} + geometry Box { size 1 1 1} + } + Transform { + translation 2 0 0 + children [ USE B] + } + Transform { + translation -2 0 0 + children [ USE B] + } + ] + } + Transform { + translation 0 2 0 + children [ USE BB] + } + Transform { + translation 0 -2 0 + children [ USE BB] + } + DEF TS TimeSensor { loop TRUE cycleInterval 2} + DEF CI PositionInterpolator { + key [0 0.5 1] + keyValue [1 0 0 1 1 0 1 0 0] + } + ] +} + +ROUTE TS.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO SP.attenuation diff --git a/regression_tests/bifs-3D-lighting-spotlight.bt b/regression_tests/bifs-3D-lighting-spotlight.bt new file mode 100644 index 0000000..d812be1 --- /dev/null +++ b/regression_tests/bifs-3D-lighting-spotlight.bt @@ -0,0 +1,72 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +Group { +children [ + NavigationInfo {type ["ANY" "EXAMINE"] headlight FALSE} + + WorldInfo { + title "SpotLight test" + info ["This shows a spot light" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + DEF SL SpotLight { + location 0.0 0.0 2.0 + color 0 0 1 + direction 0 0 -1 + } + DEF BB Group { + children [ + DEF B Shape { + appearance Appearance { material Material {}} + geometry Box { size 1 1 1} + } + Transform { + translation 2 0 0 + children [ USE B] + } + Transform { + translation -2 0 0 + children [ USE B] + } + ] + } + Transform { + translation 0 2 0 + children [ USE BB] + } + Transform { + translation 0 -2 0 + children [ USE BB] + } + + DEF TS TimeSensor { loop TRUE cycleInterval 2} + DEF CI PositionInterpolator { + key [0 0.5 1] + keyValue [-2 0 -1 2 0 -1 -2 0 -1] + } + ] +} + +ROUTE TS.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO SL.direction diff --git a/regression_tests/bifs-3D-positioning-billboard-viewer-alignment.bt b/regression_tests/bifs-3D-positioning-billboard-viewer-alignment.bt new file mode 100644 index 0000000..f9c1a1a --- /dev/null +++ b/regression_tests/bifs-3D-positioning-billboard-viewer-alignment.bt @@ -0,0 +1,62 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "Billboard Test" + info ["This shows a Billboard in viewer-align mode" "The green box coordinate system should onlyt be a translation of the viewer one" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + Viewpoint {position 0 0 10} + NavigationInfo { type ["EXAMINE" "ANY"] } + Billboard { + axisOfRotation 0 0 0 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { + diffuseColor 0 1 0 + shininess 0.5 + } + } + geometry Box {size 0.5 0.5 0.5} + } + ] + } + Transform { + translation -1 0 0 + children [ + Shape { + appearance Appearance { + material Material { + diffuseColor 1 0 0 + } + } + geometry Box {size 0.5 0.5 0.5} + } + ] + } + ] +} diff --git a/regression_tests/bifs-3D-positioning-billboard.bt b/regression_tests/bifs-3D-positioning-billboard.bt new file mode 100644 index 0000000..c05da35 --- /dev/null +++ b/regression_tests/bifs-3D-positioning-billboard.bt @@ -0,0 +1,61 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + WorldInfo { + title "Billboard Test" + info ["This shows a Billboard" "The green box should rotate to face the viewer" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Viewpoint {position 0 0 10} + Billboard { + axisOfRotation 0 1 0 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { + diffuseColor 0 1 0 + shininess 0.5 + } + } + geometry Box {size 0.5 0.5 0.5} + } + ] + } + Transform { + translation -1 0 0 + children [ + Shape { + appearance Appearance { + material Material { + diffuseColor 1 0 0 + } + } + geometry Box {size 0.5 0.5 0.5} + } + ] + } + ] +} diff --git a/regression_tests/bifs-3D-positioning-gravity.bt b/regression_tests/bifs-3D-positioning-gravity.bt new file mode 100644 index 0000000..f653e61 --- /dev/null +++ b/regression_tests/bifs-3D-positioning-gravity.bt @@ -0,0 +1,71 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +Group { + children [ + NavigationInfo {avatarSize [0.25 1.6 8] } + Viewpoint {position 0 0 50} + WorldInfo { + title "Gravity test" + info ["This shows an rectangle used as ground" "with an jumpable obstacle" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + Group { + children [ + Background2D { backColor 1 1 1} + DEF TR Transform { + rotation 1 0 0 1.57 + translation 0 -8 -10 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { + diffuseColor 0 1 0 + shininess 0.5 + } + } + geometry Rectangle {size 50 100} + } + ] + } + Transform { + translation 0 -8 -10 + children [ + DEF B Shape { + appearance Appearance { + texture ImageTexture { url "./auxiliary_files/logo.jpg"} + } + geometry Box { size 2 2 2 } + } + ] + } + Transform { + translation -10 -7 -10 + children [ + USE B Shape + ] + } + ] + } + ] +} diff --git a/regression_tests/bifs-3D-positioning-layer3D-views.bt b/regression_tests/bifs-3D-positioning-layer3D-views.bt new file mode 100644 index 0000000..89c09d1 --- /dev/null +++ b/regression_tests/bifs-3D-positioning-layer3D-views.bt @@ -0,0 +1,160 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 200 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + + Background2D {backColor 1 1 1 url "od:10"} + MediaControl {url "od:10" loop TRUE} + + WorldInfo { + title "Layer3D test" + info ["This shows several Layer3D nodes" "offering different views of the same scene" "navigation is not enabled on all layers" "" "GPAC Regression Tests" "$Date: 2008/11/24 14:58:25 $ - $Revision: 1.6 $" "(C) 2002-2004 GPAC Team"] + } + Transform2D { + translation -100 0 + children [ + Shape { + appearance DEF TXTAPP Appearance { + material Material2D { emissiveColor 1 0 0 filled TRUE } + } + geometry Text { + string ["This text" "is behind"] + fontStyle FontStyle { + size 26 + justify ["MIDDLE", "BEGIN"] + } + } + } + Layer3D { + #SHORTCUT inline viewpoint otherwise the rotation will make all models rotate + viewpoint Viewpoint {position 0 -100 1000 } + # this forbids navigation in layer + navigationInfo NavigationInfo {type ["None"]} + size 200 200 + children [ + DEF TR Transform { + children [ + DEF NEF Inline { url "auxiliary_files/nefertiti.wrl" } + ] + } + ] + } + Shape { + appearance USE TXTAPP + geometry Text { + string ["This text" "is in front"] + fontStyle FontStyle { + size 26 + justify ["MIDDLE", "END"] + } + } + } + ] + } + + Transform2D { + translation 100 50 + children [ + Layer3D { + viewpoint Viewpoint {position 0 -100 1000 } + navigationInfo NavigationInfo {type ["EXAMINE"] } + size 200 100 + children [ + Background2D {url "20" } + USE NEF + ] + } + ] + } + Transform2D { + translation 100 -50 + children [ + Layer3D { + viewpoint Viewpoint {position 0 -100 1000 } + navigationInfo NavigationInfo {headlight FALSE} + size 200 100 + children [ + Background2D { backColor 0 0 1 } + DEF DL DirectionalLight { color 0.8 0.2 0.2 direction 0 0 -1 } + USE NEF + ] + } + ] + } + + + DEF TS TimeSensor { cycleInterval 8.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [0 1 0 0, 0 1 0 3.14, 0 1 0 6.26] + } + DEF OI_L PositionInterpolator { + key [0 0.25 0.5 0.75 1] + keyValue [-1 0 0, 0 0 -1, 1 0 0, 0 0 -1, -1 0 0] + } + ] +} + +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation + +ROUTE TS.fraction_changed TO OI_L.set_fraction +ROUTE OI_L.value_changed TO DL.direction + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 10 + OCR_ES_ID 10 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 20 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.png" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-positioning-layer3D.bt b/regression_tests/bifs-3D-positioning-layer3D.bt new file mode 100644 index 0000000..3e0f790 --- /dev/null +++ b/regression_tests/bifs-3D-positioning-layer3D.bt @@ -0,0 +1,186 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + + Background2D {url "./auxiliary_files/sky.jpg" } + + WorldInfo { + title "Layer3D test" + info ["This shows a Layer3D using the entire window" "and layer2D/layer3D defining a 2D control panel" "" "GPAC Regression Tests" "$Date: 2008/11/24 14:58:25 $ - $Revision: 1.5 $" "(C) 2002-2004 GPAC Team"] + } + + Switch { + whichChoice 0 + choice [ + Layer3D { + size 400 200 + navigationInfo DEF NAV NavigationInfo {type ["Walk","ANY"] } + children [ + DEF BACK Background {frontUrl "./auxiliary_files/logo.png" } + Inline {url "./auxiliary_files/nefertiti.wrl"} + ] + } + ] + } + Layer2D { + size -1 -1 + children [ + Transform2D { + translation 0 100 + children [ + Shape { + appearance DEF TXTAPP Appearance {material Material2D { emissiveColor 0 0 0 filled TRUE } } + geometry Text { + string ["This is a Layer3D displaying" "an inline scene with a Layer2D interface"] + fontStyle DEF FS FontStyle { + size 18 + justify ["MIDDLE", "BEGIN"] + } + } + } + ] + } + Transform2D { + translation -150 -60 + children [ + Shape { + appearance Appearance { material DEF MAT_NAV Material2D {filled TRUE emissiveColor 1 0 0} } + geometry Rectangle { size 20 20 } + } + DEF TS_NAV TouchSensor {} + Transform2D { + scale 0.8 0.8 + translation 0 -20 + children [ + Shape { + appearance USE TXTAPP + geometry DEF TXT_NAV Text { + string ["Walk"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 150 -60 + children [ + Layer3D { + size 40 40 + navigationInfo NavigationInfo {type ["Examine"] } + viewpoint Viewpoint {position 0 0 40} + children [ + Background2D {backColor 1 0 0} + DEF TR Transform { + children [ + Shape { + appearance Appearance { +# material Material { emissiveColor 0 1 0} + texture ImageTexture { url './auxiliary_files/logo.jpg"} + } + geometry Box { size 20 20 20} + } + DEF TS_BACK TouchSensor {} + ] + } + ] + } + Transform2D { + scale 0.8 0.8 + translation 0 -20 + children [ + Shape { + appearance USE TXTAPP + geometry DEF TXT_BACK Text { + string ["Background Off"] + fontStyle USE FS + } + } + ] + } + ] + } + ] + } + DEF SC Script { + eventIn SFBool set_nav + eventIn SFBool set_back + field SFNode the_nav USE NAV + field SFNode the_back USE BACK + field SFNode navmat USE MAT_NAV + field SFNode navtxt USE TXT_NAV + field SFNode backtxt USE TXT_BACK + url "javascript: + function initialize(value) { + nav_set = 0; + back_set = 0; + } + function set_nav(value) { + if (!value) return; + if (nav_set) { + nav_set = 0; + navmat.emissiveColor = new SFColor(1, 0, 0); + navtxt.string[0] = 'Walk'; + the_nav.type[0] = 'Walk'; + } else { + navmat.emissiveColor = new SFColor(0, 1, 0); + navtxt.string[0] = 'Examine'; + the_nav.type[0] = 'Examine'; + nav_set = 1; + } + } + function set_back(value) { + if (!value) return; + if (back_set) { + the_back.set_bind = false; + backtxt.string[0] = 'Background Off'; + back_set = 0; + } else { + backtxt.string[0] = 'Background On'; + the_back.set_bind = true; + back_set = 1; + } + } + " + } + + DEF TIME TimeSensor { cycleInterval 2.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26] + } + ] +} + + +ROUTE TS_NAV.isActive TO SC.set_nav +ROUTE TS_BACK.isActive TO SC.set_back +ROUTE TIME.fraction_changed TO OI.set_fraction +#ROUTE OI.value_changed TO TR.rotation + + +AT 100 { REPLACE BACK.set_bind BY 0 } diff --git a/regression_tests/bifs-3D-positioning-lod.bt b/regression_tests/bifs-3D-positioning-lod.bt new file mode 100644 index 0000000..a6070ef --- /dev/null +++ b/regression_tests/bifs-3D-positioning-lod.bt @@ -0,0 +1,63 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + DirectionalLight { + color 1 1 1 + } + WorldInfo { + title "LOD test" + info ["This shows LOD with 2 level of details" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + LOD { + range [ 4 ] + center 0 0 0 + level [ + Shape { + appearance Appearance { + material Material { + diffuseColor 1 0 0 + } + } + geometry Sphere { radius 1 } + } + Shape { + appearance Appearance { + material Material { + diffuseColor 0 1 0 + } + } + geometry Box { size 1 1 1 } + } + ] + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-positioning-transform.bt b/regression_tests/bifs-3D-positioning-transform.bt new file mode 100644 index 0000000..c4e0524 --- /dev/null +++ b/regression_tests/bifs-3D-positioning-transform.bt @@ -0,0 +1,64 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "Transform test" + info ["This shows 3D transform on a box" "using DEF/USE and oriented scaling" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + DEF TR Transform { + translation -50 0 0 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { + diffuseColor 1 1 0 + } + } + geometry Box {size 50 50 50} + } + ] + } + DEF TS TimeSensor { cycleInterval 2.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26] + } + Transform { + translation 100 0 0 + scale 0.5 0.5 1 + scaleOrientation 1 0 0 0.2 + children [ + USE TR + ] + + } + ] +} + +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation diff --git a/regression_tests/bifs-3D-shapes-box-transparent.bt b/regression_tests/bifs-3D-shapes-box-transparent.bt new file mode 100644 index 0000000..43286a8 --- /dev/null +++ b/regression_tests/bifs-3D-shapes-box-transparent.bt @@ -0,0 +1,118 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0x01 + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0x01 + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric TRUE + pixelWidth 300 + pixelHeight 200 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + Background2D {backColor 1 1 1} + WorldInfo { + title "Bitmap Test" + info ["This shows dragable bitmap with outscale" "with movieTexture in Meter Metrics" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + + DEF TR Transform { + children [ + Shape { + appearance Appearance { + material DEF MAT Material{} + texture ImageTexture { url 5} + } + geometry Box {size 50 50 50} + } + ] + } + + Transform2D { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + texture MovieTexture { + url 10 + loop TRUE + } + } + geometry Bitmap {scale 2 2} + } + DEF PS PlaneSensor2D { + maxPosition 200 200 + minPosition -200 -200 + } + ] + } + ] + } + DEF TS TimeSensor { cycleInterval 10.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.25 0.5 0.75 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26, 1 1 1 3.14, 1 1 1 6.26] + } + DEF PI PositionInterpolator { + key [0 0.25 0.5 0.75 1] + keyValue [-180 120 0, 180 120 0, 180 -120 0, -180 -120 0, -180 120 0] + } + ] +} + +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation +ROUTE TS.fraction_changed TO PI.set_fraction +ROUTE PI.value_changed TO TR.translation +ROUTE TS.fraction_changed TO MAT.transparency + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 5 + esDescr [ + ES_Descriptor { + ES_ID 10 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-shapes-box.bt b/regression_tests/bifs-3D-shapes-box.bt new file mode 100644 index 0000000..06c67cf --- /dev/null +++ b/regression_tests/bifs-3D-shapes-box.bt @@ -0,0 +1,45 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "Box test" + info ["This shows a box" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + rotation 1 1 1 1.57 + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry Box {size 120 100 80} + } + ] + } + ] +} diff --git a/regression_tests/bifs-3D-shapes-cone.bt b/regression_tests/bifs-3D-shapes-cone.bt new file mode 100644 index 0000000..62824cd --- /dev/null +++ b/regression_tests/bifs-3D-shapes-cone.bt @@ -0,0 +1,58 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "Cone test" + info ["This shows a cone" "with and without cap" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + translation -70 0 0 + rotation 1 0 0 4 + children [ + Shape { + appearance Appearance { + material Material {diffuseColor 1.0 0.0 0.0 } + } + geometry Cone {height 150 bottomRadius 25} + } + ] + } + Transform { + translation 70 0 0 + rotation 1 0 0 4 + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry Cone {height 150 bottomRadius 25 bottom FALSE} + } + ] + } + ] +} diff --git a/regression_tests/bifs-3D-shapes-cylinder.bt b/regression_tests/bifs-3D-shapes-cylinder.bt new file mode 100644 index 0000000..5cba704 --- /dev/null +++ b/regression_tests/bifs-3D-shapes-cylinder.bt @@ -0,0 +1,92 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 380 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "Cylinder test" + info ["This shows a cylinder" "with and without cap and side" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + translation -100 100 0 + children [ + Shape { + appearance Appearance { + material Material {diffuseColor 1.0 0.0 0.0 } + } + geometry Cylinder {height 150 radius 25} + } + ] + } + Transform { + translation 0 100 0 + rotation 1 0 0 0.78 + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry Cylinder {height 150 radius 25 top FALSE} + } + ] + } + Transform { + translation -100 -100 0 + rotation 1 0 0 -0.78 + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry Cylinder {height 150 radius 25 bottom FALSE} + } + ] + } + Transform { + translation 0 -100 0 + rotation 1 0 0 1.2 + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry Cylinder {height 150 radius 25 bottom FALSE top FALSE} + } + ] + } + Transform { + translation 100 0 0 + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry Cylinder {height 150 radius 25 side FALSE } + } + ] + } + ] +} diff --git a/regression_tests/bifs-3D-shapes-elevationgrid.bt b/regression_tests/bifs-3D-shapes-elevationgrid.bt new file mode 100644 index 0000000..b22a907 --- /dev/null +++ b/regression_tests/bifs-3D-shapes-elevationgrid.bt @@ -0,0 +1,74 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +Group { + children [ + NavigationInfo {type ["ANY" "EXAMINE"]} + Background2D { backColor 1 0 1} + Viewpoint { position 0 0 300 } + WorldInfo { + title "ElevationGrid test" + info ["This shows an ElevationGrid" "with interaction and texturing" "" "GPAC Regression Tests" "$Date: 2008/06/26 07:55:39 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + Transform { + translation -100 0 0 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { diffuseColor 1 0 0 } + texture DEF TX ImageTexture {url "auxiliary_files/logo.jpg"} + } + geometry ElevationGrid { + creaseAngle 2 + xSpacing 15 + zSpacing 8 + xDimension 12 + zDimension 11 + height [ 17, 14, 16, 12, 14, 21, 20, 11, 9, 16, 13, 13, + 22, 14, 17, 12, 15, 21, 17, 10, 14, 12, 19, 16, + 17, 11, 13, 11, 9, 13, 11, 11, 17, 9, 15, 11, + 9, 8, 6, 10, 7, 12, 15, 8, 11, 5, 7, 9, + 0, 0, 0, 0, 0, 5, 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 2, 0, -2, -6,-12,-11, -7, -2, 0, 1, 6, + 9, 3, -1, -4, -9,-15,-13,-11, -4, -1, 3, 9, + 11, 8, 1, -5,-11,-15,-11, -9, -7, -1, 5, 7, + 9, 8, -2,-10, -9, -9,-11,-13, -9, 1, 8, 6 ] + } + } + ] + } + DEF TS TouchSensor {} + DEF C1 Conditional { buffer { REPLACE MAT.diffuseColor BY 0 0 1} } + DEF RC1 Conditional { buffer { REPLACE MAT.diffuseColor BY 1 0 0} } + DEF C2 Conditional { buffer { REPLACE TX.url BY "./auxiliary_files/logo.png"} } + DEF RC2 Conditional { buffer { REPLACE TX.url BY ""} } + ] +} + +ROUTE TS.isOver TO C1.activate +ROUTE TS.isOver TO RC1.reverseActivate +ROUTE TS.isActive TO C2.activate +ROUTE TS.isActive TO RC2.reverseActivate diff --git a/regression_tests/bifs-3D-shapes-extrusion.bt b/regression_tests/bifs-3D-shapes-extrusion.bt new file mode 100644 index 0000000..1bea80f --- /dev/null +++ b/regression_tests/bifs-3D-shapes-extrusion.bt @@ -0,0 +1,99 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +Group { + children [ + + Background2D { backColor 1 1 1} + DEF VIEWPOINT Viewpoint { + description "one" + position 0 0 10 + orientation 0 1 0 0 + } + WorldInfo { + title "Extrusion test" + info ["This shows an Extrusion" "with interaction and texturing" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + + DEF TR Transform { + translation 0 -1 0 + children [ + Shape { + appearance Appearance {material Material {diffuseColor 1.0 0.8 0.0} texture DEF TX ImageTexture{} } + geometry Extrusion { + creaseAngle 1 + endCap FALSE + beginCap TRUE + solid FALSE + crossSection [ + # Circle + 1.00 0.00, 0.92 -0.38, + 0.71 -0.71, 0.38 -0.92, + 0.00 -1.00, -0.38 -0.92, + -0.71 -0.71, -0.92 -0.38, + -1.00 -0.00, -0.92 0.38, + -0.71 0.71, -0.38 0.92, + 0.00 1.00, 0.38 0.92, + 0.71 0.71, 0.92 0.38, + 1.00 0.00 + ] + spine [ + # Straight-line + 0.0 0.0 0.0, 0.0 0.4 0.0, + 0.0 0.8 0.0, 0.0 1.2 0.0, + 0.0 1.6 0.0, 0.0 2.0 0.0, + 0.0 2.4 0.0, 0.0 2.8 0.0, + 0.0 3.2 0.0, 0.0 3.6 0.0, + 0.0 4.0 0.0 + ] + scale [ + 1.8 1.8, 1.95 1.95, + 2.0 2.0, 1.95 1.95 + 1.8 1.8, 1.5 1.5 + 1.2 1.2, 1.05 1.05, + 1.0 1.0, 1.05 1.05, + 1.15 1.15, + ] + } + } + DEF TOUCH TouchSensor {} + ] + } + + DEF TS TimeSensor { cycleInterval 8.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26] + } + DEF C Conditional { buffer { REPLACE TX.url BY "./auxiliary_files/logo.png"} } + DEF RC Conditional { buffer { REPLACE TX.url BY ""} } + ] +} + +ROUTE TOUCH.isActive TO C.activate +ROUTE TOUCH.isActive TO RC.reverseActivate + +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation diff --git a/regression_tests/bifs-3D-shapes-indexedfaceset.bt b/regression_tests/bifs-3D-shapes-indexedfaceset.bt new file mode 100644 index 0000000..fbae195 --- /dev/null +++ b/regression_tests/bifs-3D-shapes-indexedfaceset.bt @@ -0,0 +1,66 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "IndexedFaceSet test" + info ["This shows an IFS" "with and without colorPerVertex" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + translation -1.5 0 0 + children [ + Shape { + geometry IndexedFaceSet { + coord DEF COORD Coordinate { + point [-1 -1 1 1 -1 1 1 1 1 -1 1 1 1 1 -1 -1 1 -1 -1 -1 -1 1 -1 -1] + } + coordIndex [0 1 2 3 -1 1 7 4 2 -1 7 6 5 4 -1 0 3 5 6 -1 3 2 4 5 -1 6 7 1 0 -1] + color DEF COL Color { + color [ 1 0 0, 0 1 0, 0 0 1, 1 1 0, 1 0 1, 0 1 1, 1 0.5 0.5, 0.5 0.5 1] + } + colorPerVertex TRUE + } + } + ] + } + Transform { + translation 1.5 0 0 + children [ + Shape { + geometry IndexedFaceSet { + coord USE COORD + coordIndex [0 1 2 3 -1 1 7 4 2 -1 7 6 5 4 -1 0 3 5 6 -1 3 2 4 5 -1 6 7 1 0 -1] + color USE COL + colorPerVertex FALSE + colorIndex [0 1 2 3 4 5 6 7] + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-shapes-indexedlineset.bt b/regression_tests/bifs-3D-shapes-indexedlineset.bt new file mode 100644 index 0000000..02e1795 --- /dev/null +++ b/regression_tests/bifs-3D-shapes-indexedlineset.bt @@ -0,0 +1,99 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "IndexedLineSet test" + info ["This shows an ILS" "with and without color per vertex" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + translation -1.5 0 0 + children [ + Shape { + geometry IndexedLineSet { + coord DEF COORD Coordinate { + point [ + # the top of the cube + -1.0 1.0 1.0, + 1.0 1.0 1.0, + 1.0 1.0 -1.0, + -1.0 1.0 -1.0, + # around the bottom of the cube + -1.0 -1.0 1.0, + 1.0 -1.0 1.0, + 1.0 -1.0 -1.0, + -1.0 -1.0 -1.0 + ] + } + coordIndex [ + # top + 0, 1, 2, 3, 0, -1, + # bottom + 4, 5, 6, 7, 4, -1, + # vertical edges + 0, 4, -1, + 1, 5, -1, + 2, 6, -1, + 3, 7 + ] + color Color { + color [1 0 0, 0 1 0, 0 0 1, 1 1 0] + } + colorPerVertex TRUE + colorIndex [0, 1, 2, 3, 0, -1, 0, 1, 2, 3, 0, -1, 0, 3, -1, 0, 2, -1, 0, 1, -1, 1 3] + } + } + ] + } + Transform { + translation 1.5 0 0 + children [ + Shape { + geometry IndexedLineSet { + coord USE COORD + coordIndex [ + # top + 0, 1, 2, 3, 0, -1, + # bottom + 4, 5, 6, 7, 4, -1, + # vertical edges + 0, 4, -1, + 1, 5, -1, + 2, 6, -1, + 3, 7 + ] + color Color { + color [1 0 0, 0 1 0, 0 0 1, 1 1 0, 0 1 1, 1 0 1] + } + colorPerVertex FALSE + colorIndex [0, 1, 2, 3, 4, 5] + } + } + ] + } + ] +} diff --git a/regression_tests/bifs-3D-shapes-nonlineardeformer.bt b/regression_tests/bifs-3D-shapes-nonlineardeformer.bt new file mode 100644 index 0000000..3010f57 --- /dev/null +++ b/regression_tests/bifs-3D-shapes-nonlineardeformer.bt @@ -0,0 +1,796 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + + WorldInfo { + info ["This shows NonLinearDeformer" "in different modes and differnet axis." "Deformation is controled through the NLD.extend field" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "NonLinearDeformer Test" + } + Viewpoint { position 0 -1 10 } + NavigationInfo {type ["EXAMINE"]} + Transform { + translation -0.1337 -2.107 -1.839 + children [ + + Shape { + appearance Appearance { + material Material { + diffuseColor 0.9804 0.9804 0.9804 + } + } + geometry DEF NLD NonLinearDeformer { + type 0 + param 1 + axis 0 1 0 + extend [0 0.5 0.5 1 1 0.2] + geometry IndexedFaceSet { + ccw TRUE + solid FALSE + creaseAngle 2 + coord Coordinate { point [ + -2.116 1.757 -1.271, -2.036 1.587 -1.231, -2.136 1.607 -1.391, + -2.206 1.837 -1.361, -2.216 1.867 -1.161, -2.116 1.697 -1.111, + -2.066 1.457 -1.131, -1.946 1.607 -0.9606, -1.966 1.387 -1.011, + -2.076 1.337 -1.251, -2.096 1.397 -1.301, -2.396 1.687 -1.561, + -2.306 1.477 -1.521, -2.466 1.527 -1.711, -2.446 1.737 -1.651, + -2.396 1.877 -1.491, -2.326 1.217 -1.511, -2.186 1.437 -1.431, + -2.086 1.257 -1.391, -2.096 1.127 -1.411, -2.196 0.9375 -1.391, + -2.386 1.017 -1.521, -2.486 1.217 -1.691, -2.056 0.9875 -1.251, + -1.976 1.267 -1.141, -1.876 1.167 -1.121, -1.996 0.8775 -1.001, + -2.116 0.8775 -1.201, -1.946 1.267 -1.001, -1.916 1.297 -0.9206, + -1.966 1.197 -0.9206, -1.886 1.127 -0.9906, -1.946 1.287 -1.081, + -1.986 1.017 -0.8106, -1.926 1.127 -0.7606, -1.896 0.9375 -0.6006, + -1.976 0.7975 -0.6606, -1.926 0.9575 -0.9206, -2.136 0.7675 -0.8906, + -1.996 0.5875 -0.6606, -2.136 0.5575 -0.9806, -2.226 0.7575 -1.111, + -1.906 0.3475 -0.6006, -1.856 0.5175 -0.5106, -1.746 0.2775 -0.5806, + -1.906 0.2275 -0.7406, -2.036 0.3375 -0.9706, -2.046 0.4775 -0.7806, + -1.866 0.7975 -0.5406, -2.296 0.8575 -1.351, -2.196 0.6175 -1.211, + -2.236 0.6575 -1.421, -2.346 0.8575 -1.581, -2.386 0.9575 -1.541, + -2.116 0.4775 -1.171, -1.776 0.2875 -0.9306, -1.846 0.4775 -1.181, + -1.566 0.2275 -0.7506, -1.566 0.4375 -0.9906, -1.636 0.7175 -1.341, + -1.986 0.7375 -1.431, -1.756 1.027 -1.601, -2.136 1.097 -1.701, + -2.276 1.537 -1.751, -2.536 1.687 -1.771, -2.466 1.217 -1.751, + -1.966 1.377 -1.751, -2.076 1.717 -1.721, -2.316 1.877 -1.631, + -2.546 1.537 -1.741, -2.546 1.747 -1.671, -2.556 1.937 -1.611, + -2.536 1.897 -1.581, -2.206 1.947 -1.611, -2.326 2.037 -1.421, + -2.316 2.027 -1.231, -2.176 2.067 -1.291, -2.356 1.927 -1.331, + -2.186 1.907 -1.061, -1.976 1.957 -1.001, -2.106 1.857 -1.041, + -1.866 0.9475 -0.3906, -1.936 1.367 -0.2506, -1.946 1.297 -0.0005862, + -1.886 0.8775 0.4194, -1.806 0.4475 0.2294, -1.786 0.1875 0.1694, + -1.806 0.3175 -0.2706, -1.366 0.3575 -2.181, -1.406 0.05749 -1.281, + -1.556 -0.1525 -0.7106, -1.516 -0.6325 -1.151, -1.146 -0.6925 -1.741, + -1.086 -0.2125 -1.911, -1.786 -0.1825 0.03941, -1.766 -0.1525 -0.3606, + -1.706 -0.09251 0.3694, -1.706 -0.3625 0.2894, -1.616 -1.023 -0.1406, + -1.646 -0.8525 -0.4206, -1.576 -0.8825 -0.6006, -1.556 -1.053 -0.4206, + -1.346 -1.333 -0.3906, -1.486 -1.393 -0.9606, -0.7163 -0.6425 -2.381, + -1.036 -1.113 -2.021, -0.7363 -1.093 -2.401, -0.0763 -0.5625 -2.381, + -0.5963 -0.5225 -2.471, -0.5563 -0.4825 -2.571, -1.056 -0.03251 -2.361, + -0.0963 -1.093 -2.371, -0.1063 -1.473 -2.511, -0.7963 -1.483 -2.531, + -1.366 -1.963 -1.861, -1.386 -2.313 -2.071, -0.9363 -1.943 -2.681, + -0.7963 -1.913 -2.791, -0.8963 -3.243 -3.621, -1.706 -3.973 -3.811, + -0.9963 -4.103 -4.111, -0.2363 -4.093 -4.191, -0.2063 -3.353 -3.741, + -0.1663 -2.603 -3.231, -0.8963 -2.513 -3.141, -1.126 -2.563 -3.001, + -1.386 -3.223 -3.371, -0.1363 -2.063 -2.891, -1.826 -3.313 -2.851, + -2.236 -3.693 -2.581, -2.136 -3.713 -3.151, -1.536 -2.833 -2.451, + -1.946 -3.423 -2.331, -1.926 -3.633 -2.031, -1.956 -4.303 -1.764, + -2.226 -3.873 -2.171, -1.676 -3.073 -2.071, -1.666 -3.253 -1.851, + -1.696 -4.023 -1.671, -1.426 -4.363 -1.411, -1.276 -5.023 -1.261, + -1.666 -4.683 -1.501, -1.486 -3.683 -1.491, -1.246 -4.053 -1.211, + -1.216 -4.623 -1.281, -1.376 -3.073 -1.221, -1.476 -2.773 -1.491, + -1.406 -2.483 -1.201, -1.166 -2.893 -0.7706, -1.046 -3.473 -0.8406, + -1.526 -2.563 -1.731, -1.446 -2.233 -1.511, -0.8263 -5.193 -1.071, + -0.9163 -4.373 -1.001, -0.6763 -4.483 -1.001, -0.5263 -5.313 -1.121, + -0.3563 -5.333 -1.131, -0.3663 -4.643 -1.021, -0.1763 -4.753 -1.051, + -0.1963 -5.383 -1.131, -0.4363 -3.893 -0.7006, -0.8863 -3.613 -0.7806, + -0.4763 -3.393 -0.5006, -0.1263 -3.533 -0.5006, -0.1463 -4.033 -0.7306, + -1.456 -1.853 -1.241, -1.386 -1.483 -1.611, -1.486 -1.443 -1.231, + -1.316 -2.193 -0.8206, -0.5163 -2.813 -0.2806, -1.036 -3.023 -0.7006, + -1.196 -2.353 -0.6606, -0.8663 -2.103 -0.1806, -0.5563 -1.883 0.2294, + -0.0963 -2.933 -0.2106, -1.346 -1.533 -0.5406, -1.096 -1.573 -0.1606, + -0.0563 -1.943 0.3194, -0.0363 -1.683 0.7494, -0.5663 -1.643 0.5794, + -1.146 -1.393 0.3994, -1.516 -1.173 -0.2506, -1.286 -1.353 0.1994, + -1.386 -1.193 0.4494, -0.5363 -1.483 1.049, -0.9463 -1.283 1.249, + -0.5563 -1.473 1.399, -0.0063 -1.513 1.649, -1.566 -0.8925 0.3994, + -1.396 -0.7425 0.8494, -1.636 0.04749 0.7194, -1.476 -0.1825 0.9894, + -1.296 -0.3925 1.199, -1.146 -0.7725 1.289, -0.9763 -1.073 1.419, + -0.6263 -1.363 1.779, -0.2763 -1.483 1.829, -0.7363 -1.173 1.769, + -0.5263 -1.183 2.009, -0.3963 -1.373 1.969, 0.0137 -1.353 2.069, + -0.3563 -1.223 2.149, -0.3663 -0.9425 2.139, 0.0237 -1.013 2.219, + 0.0237 -1.233 2.209, -0.9063 -0.8825 1.549, -0.9963 -0.5625 1.519, + -0.7563 -0.6625 1.699, -0.6463 -0.8825 1.859, -1.736 0.2775 0.4994, + -1.846 0.8575 0.7094, -1.696 0.3575 0.8394, -1.576 0.3675 1.109, + -1.386 -0.1125 1.169, -1.756 0.8475 1.009, -1.536 0.8375 1.299, + -1.326 0.4575 1.419, -1.056 0.08749 1.569, -1.106 0.6175 1.629, + -0.8663 0.3575 1.769, -0.8263 0.05749 1.839, -1.026 -0.2525 1.579, + -0.2263 -0.6525 2.109, -0.3863 -0.6325 2.069, -0.2463 -0.5525 2.179, + 0.0337 -0.6925 2.129, -0.4063 -0.5325 2.129, -0.4963 -0.4525 2.019, + -0.4163 -0.3925 2.099, -0.2663 -0.4225 2.199, -0.0963 -0.4525 2.209, + 0.0437 -0.5625 2.209, -0.1563 -0.2725 2.139, 0.0537 -0.3125 2.179, + 0.0437 -0.4525 2.229, -0.5263 -0.5725 1.919, -0.7363 -0.3125 1.709, + -0.5263 0.02749 1.999, -0.4263 -0.08251 2.099, -0.4263 -0.1925 2.079, + -0.4063 -0.2825 2.039, -0.2963 -0.2825 2.109, -0.3363 -0.1525 2.159, + -0.0563 -0.1425 2.299, -0.1863 -0.1425 2.229, -0.1263 0.02749 2.299, + 0.0637 -0.002507 2.319, 0.0537 -0.1425 2.299, -0.3363 -0.04251 2.209, + -0.0863 0.1175 2.279, -0.2563 0.4275 2.069, -0.0263 0.3575 2.229, + 0.0737 0.3275 2.229, 0.0637 0.1175 2.259, -0.6963 0.1875 1.899, + -0.4263 0.4675 1.969, -0.0763 0.6175 2.109, -0.3663 0.5675 2.149, + -0.2263 0.6175 2.279, -0.0963 0.5975 2.359, -0.0163 0.5075 2.409, + -0.4363 0.6075 2.049, -0.4963 0.6675 1.899, -0.3963 0.7375 2.139, + -0.2663 0.6775 2.319, -0.0963 0.6675 2.489, -0.1363 0.8275 2.439, + -0.0063 0.8675 2.529, 0.0937 0.7575 2.569, 0.0837 0.4875 2.439, + -1.306 0.8475 1.509, -1.456 1.227 1.339, -1.206 1.167 1.509, + -0.9263 0.8775 1.709, -0.8763 1.187 1.649, -1.126 1.447 1.479, + -0.8663 1.457 1.549, -0.5663 1.487 1.569, -0.4163 1.357 1.749, + -0.5063 1.017 1.849, -0.2963 0.9775 1.999, -0.2063 0.9075 2.229, + -0.0763 1.177 2.229, -0.3263 1.697 1.629, -0.0663 2.097 1.749, + 0.1037 1.417 2.229, 0.1237 2.127 1.839, -1.386 1.727 1.249, + -1.466 1.567 1.219, -1.646 1.747 0.9494, -1.346 1.887 1.359, + -1.296 1.767 1.359, -1.206 1.647 1.409, -1.096 1.607 1.479, + -1.146 1.707 1.479, -0.9963 1.697 1.529, -0.8663 1.597 1.529, + -0.6463 1.607 1.549, -0.7663 1.707 1.549, -0.5263 1.727 1.479, + -0.4563 1.667 1.539, -0.5063 1.947 1.539, -0.3563 2.047 1.509, + -1.646 1.277 1.149, -1.816 1.417 0.8194, -1.906 1.207 0.5394, + -1.906 1.537 0.2594, -1.836 1.747 0.4994, -1.806 1.717 0.7294, + -1.436 2.037 1.149, -1.716 1.877 0.8394, -1.596 2.197 1.099, + -1.186 2.287 1.359, -1.786 2.037 0.7494, -1.886 1.877 0.009414, + -1.796 2.277 0.4294, -1.656 2.317 1.039, -0.9863 2.337 1.459, + -0.8663 2.057 1.589, -1.086 2.037 1.549, -0.9963 2.527 1.529, + -0.6863 2.507 1.579, -0.7163 2.307 1.479, -0.4863 2.207 1.479, + -0.3563 2.367 1.649, -0.3163 2.517 1.709, -0.6463 2.677 1.629, + -0.5563 2.997 1.479, -0.1163 2.867 1.549, -1.036 2.737 1.529, + -1.326 2.467 1.369, -1.386 2.627 1.359, -1.026 3.027 1.369, + 0.1337 2.747 1.589, 0.1437 3.517 1.219, -0.3963 3.517 1.239, + -1.026 3.427 1.129, -1.436 3.257 0.9294, -1.386 2.897 1.179, + -1.696 2.607 0.7894, -1.836 2.567 0.06941, -1.706 2.937 0.5194, + -1.916 2.047 -0.7406, 1.934 0.7375 0.6094, 1.854 0.7275 0.9194, + 1.944 1.297 0.7194, 2.004 1.917 -0.8406, 2.044 1.827 -1.101, + 1.994 1.477 -1.061, 2.004 1.237 -0.3506, 2.004 1.747 -0.09059, + 2.024 1.167 -0.1006, 2.014 1.407 0.1594, 1.964 1.627 0.3994, + 1.964 2.157 0.3294, 1.994 2.447 -0.03059, 1.694 3.157 0.8494, + 1.914 2.817 0.4294, 1.634 2.797 1.099, 1.624 2.527 1.279, + 1.904 2.487 0.6994, 1.854 2.207 0.9494, 1.284 2.957 1.309, + 1.304 3.357 1.069, 0.6837 3.487 1.209, 0.8237 2.957 1.439, + 0.3837 2.847 1.539, 1.554 2.377 1.299, 1.794 2.087 1.009, + 1.404 2.207 1.289, 1.624 1.937 1.069, 1.234 2.457 1.469, + 0.9037 2.627 1.589, 1.284 2.667 1.469, 0.9237 2.457 1.539, + 0.5937 2.337 1.629, 0.2937 2.087 1.739, 0.5637 2.487 1.689, + 0.5637 2.017 1.489, 1.294 1.957 1.489, 1.534 1.797 1.289, + 0.7137 1.907 1.509, 0.7037 2.167 1.449, 0.9437 2.257 1.439, + 1.074 1.997 1.539, 1.214 2.267 1.399, 1.874 1.757 0.7494, + 1.954 1.917 0.6494, 1.944 1.597 0.6294, 1.804 1.637 0.8594, + 2.004 1.077 0.4394, 1.954 0.7575 0.3194, 0.7437 1.447 1.539, + 0.5937 1.327 1.719, 0.6437 1.627 1.509, 1.624 1.467 1.139, + 1.294 1.367 1.419, 1.384 1.567 1.339, 1.784 1.167 1.059, + 1.654 0.7375 1.219, 1.604 1.127 1.259, 1.354 1.087 1.439, + 0.5137 1.667 1.609, 0.7137 1.687 1.449, 0.9537 1.647 1.509, + 1.184 1.627 1.469, 1.044 1.537 1.479, 1.334 1.627 1.419, + 1.474 1.677 1.289, 0.8237 1.557 1.509, 1.034 1.397 1.499, + 1.274 1.527 1.419, 1.554 1.637 1.169, 1.754 0.2475 0.7494, + 0.6637 0.9775 1.819, 0.4637 0.9575 1.979, 0.1937 0.8575 2.519, + 0.3137 0.8175 2.429, 0.2637 1.167 2.219, 0.3837 0.8875 2.209, + 0.4337 0.6575 2.299, 0.5537 0.7075 2.119, 0.6337 0.6275 1.869, + 1.064 0.8175 1.659, 0.9737 0.2975 1.719, 1.224 0.5375 1.569, + 0.5537 0.4375 1.939, 1.034 1.127 1.599, 1.434 0.7575 1.439, + 1.424 0.3675 1.349, 0.1737 0.4975 2.409, 0.2637 0.6575 2.479, + 0.1637 0.3475 2.229, 0.3837 0.5975 2.259, 0.2537 0.5875 2.349, + 0.5137 0.5375 2.129, 0.3937 0.4075 2.049, 0.5837 0.5775 2.019, + 0.2237 0.6075 2.099, 0.6337 -0.01251 1.969, 0.8037 0.1375 1.859, + 0.9237 -0.002507 1.789, 0.2437 0.01749 2.289, 0.2137 0.1075 2.269, + 0.4437 -0.07251 2.189, 0.2537 -0.2825 2.129, 0.2937 -0.1625 2.219, + 0.4337 -0.1725 2.139, 0.5237 -0.2225 2.059, 0.5237 -0.1125 2.079, + 0.1637 -0.1525 2.289, 0.3537 -0.4425 2.179, 0.3837 -0.3025 2.089, + 0.4937 -0.3125 2.019, 0.4937 -0.4225 2.079, 0.8037 -0.7125 1.659, + 0.5837 -0.6125 1.889, 0.8037 -0.3625 1.669, 0.5737 -0.4825 1.989, + 1.084 -0.3225 1.529, 1.034 -0.6325 1.469, 0.6837 -0.9225 1.829, + 0.4137 -0.9725 2.119, 0.4537 -0.6625 2.049, 0.3237 -0.5725 2.169, + 0.1837 -0.4625 2.199, 0.4837 -0.5625 2.109, 0.2937 -0.6725 2.099, + 1.324 -0.4725 1.129, 1.164 -0.8425 1.229, 1.514 -0.2825 0.9094, + 1.434 -0.2025 1.099, 1.674 -0.06251 0.6294, 1.144 0.01749 1.509, + 1.654 0.2675 1.029, 1.774 0.1675 0.4094, 1.844 0.3275 0.1394, + 1.804 0.06749 0.07941, 1.714 -0.2025 0.2794, 0.7537 -1.223 1.729, + 0.5537 -1.213 1.979, 0.9737 -1.133 1.369, 0.9237 -0.9425 1.499, + 0.9237 -1.343 1.199, 0.4037 -1.403 1.949, 0.2737 -1.503 1.819, + 0.5337 -1.513 1.369, 0.3837 -1.243 2.129, 0.6237 -1.403 1.749, + 1.694 -0.4725 0.1994, 1.524 -0.9925 0.3194, 1.384 -0.8325 0.7794, + 1.324 -1.283 0.3794, 1.074 -1.463 0.3394, 1.534 -1.123 -0.2206, + 1.564 -0.9525 -0.5006, 1.464 -1.153 -0.5006, 0.4937 -1.513 1.019, + 0.4837 -1.673 0.5494, 1.424 -1.263 -0.3306, 1.204 -1.433 0.1394, + 1.234 -1.413 -0.4606, 0.9837 -1.643 -0.2106, 0.4437 -1.913 0.1994, + 0.2337 -3.413 -0.5206, 0.7137 -2.153 -0.2206, 1.004 -2.423 -0.7206, + 1.124 -2.273 -0.8806, 1.214 -1.613 -0.6106, 1.344 -1.483 -1.031, + 0.7737 -3.533 -0.8906, 0.6137 -3.663 -0.8206, 0.9337 -2.963 -0.8206, + 0.8037 -3.083 -0.7506, 1.184 -2.563 -1.271, 0.3237 -2.843 -0.3006, + 0.8537 -1.173 -2.071, 1.204 -1.563 -1.681, 1.004 -0.7625 -1.801, + 1.414 -0.7225 -1.231, 1.324 -1.533 -1.301, 1.144 -2.043 -1.931, + 1.224 -2.323 -1.581, 1.264 -1.943 -1.311, 1.214 -2.863 -1.561, + 0.9137 -4.123 -1.271, 0.5837 -4.423 -1.041, 0.3337 -4.513 -1.031, + 0.1437 -3.913 -0.7206, 0.0137 -4.653 -1.031, -0.0463 -5.343 -1.141, + 0.1237 -5.333 -1.141, 0.4337 -5.233 -1.101, 0.8437 -4.693 -1.331, + 0.8837 -5.093 -1.321, 1.344 -4.123 -1.751, 1.584 -4.413 -1.854, + 1.284 -4.773 -1.581, 1.164 -3.773 -1.561, 1.354 -3.353 -1.931, + 1.264 -2.653 -1.801, 1.124 -2.393 -2.141, 1.364 -3.173 -2.151, + 1.114 -3.153 -1.281, 1.064 -4.443 -1.471, 1.004 -3.303 -3.431, + 1.244 -4.073 -3.891, 1.724 -3.833 -3.251, 1.854 -3.823 -2.691, + 1.854 -4.003 -2.281, 1.594 -3.533 -2.421, 1.224 -2.923 -2.521, + 0.6737 -1.993 -2.721, 0.8037 -2.623 -3.051, 1.584 -3.743 -2.121, + 1.464 -3.423 -2.941, 0.5237 -1.953 -2.821, 0.5737 -2.563 -3.181, + 0.4937 -3.283 -3.661, 0.5137 -4.153 -4.151, 0.5437 -1.133 -2.431, + 0.5637 -1.523 -2.571, 0.4337 -0.5525 -2.501, 0.3937 -0.5125 -2.591, + 1.274 0.2775 -2.251, 0.9237 -0.09251 -2.411, 0.9737 -0.2825 -1.961, + 0.5537 -0.6825 -2.411, 1.734 -0.2625 -0.4506, 1.504 -0.2525 -0.7906, + 1.484 -0.9825 -0.6806, 1.724 0.1675 -0.6706, 1.774 -0.2925 -0.05059, + 1.804 0.1975 -0.3606, 1.534 0.1275 -0.8306, 1.854 0.3975 -0.6106, + 1.884 0.6775 -0.6406, 1.534 0.3375 -1.071, 1.334 -0.03251 -1.351, + 1.604 0.6175 -1.421, 1.734 0.9175 -1.691, 1.944 1.177 -1.021, + 1.954 1.007 -0.8606, 1.904 0.8275 -0.4906, 1.924 0.8175 -0.7006, + 1.994 1.257 -1.111, 2.164 1.557 -1.221, 2.164 1.717 -1.151, + 2.234 1.927 -1.401, 2.244 1.767 -1.181, 2.374 1.877 -1.351, + 2.264 1.727 -1.281, 2.404 1.777 -1.451, 2.564 1.737 -1.711, + 2.434 1.717 -1.621, 2.244 1.697 -1.481, 2.464 1.577 -1.781, + 2.564 1.587 -1.801, 2.374 1.887 -1.541, 2.244 1.807 -1.731, + 2.094 1.587 -1.831, 2.344 1.727 -1.751, 2.584 1.767 -1.741, + 2.544 1.527 -1.901, 2.114 0.9575 -1.811, 2.314 0.7075 -1.701, + 2.444 1.057 -1.881, 2.314 1.327 -1.641, 2.464 1.367 -1.841, + 2.464 1.057 -1.821, 2.364 0.8675 -1.641, 2.364 0.8075 -1.661, + 2.544 1.377 -1.871, 1.954 1.247 -1.851, 2.274 1.387 -1.871, + 1.954 0.6075 -1.531, 1.814 0.3575 -1.271, 2.194 0.5175 -1.541, + 1.744 0.1775 -1.021, 1.874 0.1075 -0.8406, 2.004 0.2075 -1.071, + 2.084 0.3375 -1.281, 2.114 0.4175 -1.091, 2.164 0.4775 -1.321, + 2.214 0.6175 -1.231, 2.034 0.3475 -0.8906, 1.994 0.4575 -0.7606, + 2.274 0.7075 -1.471, 2.104 0.7375 -1.311, 2.174 0.7975 -1.501, + 1.984 0.6675 -0.7606, 1.894 0.2275 -0.7006, 1.994 0.7475 -1.101, + 1.934 0.8375 -1.021, 1.884 1.047 -1.221, 1.904 1.007 -1.091, + 2.134 0.6275 -1.001, 1.994 1.067 -1.021, 2.004 0.8875 -0.9106, + 2.094 1.207 -1.361, 1.964 1.157 -1.181, 1.994 1.137 -1.241, + 1.974 1.137 -1.101, 2.114 1.257 -1.411, 2.084 1.127 -1.501, + 2.044 0.8575 -1.361, 2.084 0.9975 -1.521, 2.194 1.297 -1.541, + 2.164 1.467 -1.501, 2.064 1.457 -1.341, 2.314 1.067 -1.631, + 2.414 1.537 -1.681, 2.094 1.327 -1.241, 2.154 1.617 -1.381] + } + coordIndex [ + 1, 0, 2, -1, + 2, 0, 3, -1, 3, 0, 4, -1, 4, 0, 5, -1, 5, 0, 1, -1, + 5, 6, 7, -1, 7, 6, 8, -1, 8, 6, 9, -1, 9, 6, 10, -1, + 10, 6, 1, -1, 1, 6, 5, -1, 3, 11, 2, -1, 2, 11, 12, -1, + 12, 11, 13, -1, 13, 11, 14, -1, 14, 11, 15, -1, 15, 11, 3, -1, + 12, 16, 17, -1, 17, 16, 10, -1, 10, 16, 18, -1, 18, 16, 19, -1, + 19, 16, 20, -1, 20, 16, 21, -1, 21, 16, 22, -1, 22, 16, 12, -1, + 1, 2, 10, -1, 17, 10, 2, -1, 17, 2, 12, -1, 9, 23, 24, -1, + 24, 23, 25, -1, 25, 23, 26, -1, 26, 23, 27, -1, 27, 23, 20, -1, + 20, 23, 19, -1, 19, 23, 18, -1, 18, 23, 9, -1, 18, 9, 10, -1, + 8, 28, 29, -1, 29, 28, 30, -1, 30, 28, 31, -1, 31, 28, 25, -1, + 25, 28, 32, -1, 32, 28, 8, -1, 25, 32, 24, -1, 24, 32, 9, -1, + 9, 32, 8, -1, 31, 33, 30, -1, 30, 33, 34, -1, 34, 33, 35, -1, + 35, 33, 36, -1, 36, 33, 37, -1, 37, 33, 31, -1, 30, 34, 29, -1, + 26, 38, 36, -1, 36, 38, 39, -1, 39, 38, 40, -1, 40, 38, 41, -1, + 41, 38, 26, -1, 36, 37, 26, -1, 31, 25, 37, -1, 37, 25, 26, -1, + 26, 27, 41, -1, 39, 42, 43, -1, 43, 42, 44, -1, 44, 42, 45, -1, + 45, 42, 46, -1, 46, 42, 47, -1, 47, 42, 39, -1, 43, 48, 39, -1, + 36, 39, 48, -1, 36, 48, 35, -1, 21, 49, 20, -1, 20, 49, 27, -1, + 27, 49, 41, -1, 41, 49, 50, -1, 50, 49, 51, -1, 51, 49, 52, -1, + 52, 49, 53, -1, 53, 49, 21, -1, 39, 40, 47, -1, 47, 40, 46, -1, + 40, 41, 50, -1, 51, 54, 50, -1, 50, 54, 40, -1, 40, 54, 46, -1, + 46, 54, 55, -1, 55, 54, 56, -1, 56, 54, 51, -1, 45, 46, 55, -1, + 45, 55, 57, -1, 45, 57, 44, -1, 57, 55, 58, -1, 56, 58, 55, -1, + 58, 56, 59, -1, 61, 60, 62, -1, 62, 60, 52, -1, 52, 60, 51, -1, + 51, 60, 56, -1, 56, 60, 59, -1, 59, 60, 61, -1, 64, 63, 65, -1, + 65, 63, 62, -1, 62, 63, 66, -1, 66, 63, 67, -1, 67, 63, 68, -1, + 68, 63, 64, -1, 66, 62, 61, -1, 14, 69, 13, -1, 13, 69, 22, -1, + 22, 69, 65, -1, 65, 69, 64, -1, 64, 69, 70, -1, 70, 69, 14, -1, + 52, 53, 65, -1, 53, 21, 65, -1, 22, 65, 21, -1, 13, 22, 12, -1, + 52, 65, 62, -1, 72, 71, 70, -1, 70, 71, 64, -1, 64, 71, 68, -1, + 68, 71, 73, -1, 73, 71, 74, -1, 74, 71, 75, -1, 75, 71, 72, -1, + 68, 73, 67, -1, 76, 73, 74, -1, 76, 74, 75, -1, 70, 14, 72, -1, + 72, 14, 15, -1, 4, 77, 3, -1, 3, 77, 15, -1, 15, 77, 72, -1, + 72, 77, 75, -1, 75, 77, 4, -1, 79, 78, 80, -1, 80, 78, 5, -1, + 5, 78, 4, -1, 4, 78, 75, -1, 75, 78, 76, -1, 76, 78, 79, -1, + 7, 79, 80, -1, 7, 80, 5, -1, 8, 29, 7, -1, 29, 81, 7, -1, + 7, 81, 82, -1, 82, 81, 83, -1, 83, 81, 84, -1, 84, 81, 85, -1, + 85, 81, 86, -1, 86, 81, 87, -1, 87, 81, 48, -1, 48, 81, 35, -1, + 35, 81, 34, -1, 34, 81, 29, -1, 61, 88, 59, -1, 58, 59, 88, -1, + 58, 89, 57, -1, 57, 89, 90, -1, 90, 89, 91, -1, 91, 89, 92, -1, + 92, 89, 93, -1, 93, 89, 88, -1, 88, 89, 58, -1, 87, 48, 43, -1, + 87, 43, 44, -1, 90, 44, 57, -1, 95, 94, 44, -1, 44, 94, 87, -1, + 87, 94, 86, -1, 86, 94, 96, -1, 96, 94, 97, -1, 97, 94, 98, -1, + 98, 94, 99, -1, 99, 94, 95, -1, 95, 44, 90, -1, 91, 100, 90, -1, + 90, 100, 99, -1, 99, 100, 101, -1, 101, 100, 102, -1, + 102, 100, 103, -1, 103, 100, 91, -1, 90, 99, 95, -1, + 93, 104, 92, -1, 92, 104, 105, -1, 105, 104, 106, -1, + 106, 104, 107, -1, 107, 104, 108, -1, 108, 104, 93, -1, + 109, 108, 110, -1, 93, 110, 108, -1, 110, 93, 88, -1, + 108, 109, 107, -1, 106, 111, 112, -1, 106, 113, 105, -1, + 105, 113, 114, -1, 114, 113, 115, -1, 115, 113, 116, -1, + 116, 113, 117, -1, 117, 113, 112, -1, 112, 113, 106, -1, + 111, 106, 107, -1, 119, 118, 120, -1, 120, 118, 121, -1, + 121, 118, 122, -1, 122, 118, 123, -1, 123, 118, 124, -1, + 124, 118, 125, -1, 125, 118, 126, -1, 126, 118, 119, -1, + 125, 116, 117, -1, 125, 117, 124, -1, 123, 124, 117, -1, + 123, 117, 127, -1, 112, 127, 117, -1, 129, 128, 130, -1, + 130, 128, 126, -1, 126, 128, 125, -1, 125, 128, 131, -1, + 131, 128, 132, -1, 132, 128, 129, -1, 134, 133, 135, -1, + 135, 133, 132, -1, 132, 133, 136, -1, 136, 133, 137, -1, + 137, 133, 138, -1, 138, 133, 134, -1, 131, 116, 125, -1, + 115, 116, 131, -1, 115, 131, 136, -1, 132, 136, 131, -1, + 135, 132, 129, -1, 119, 130, 126, -1, 140, 139, 141, -1, + 141, 139, 138, -1, 138, 139, 142, -1, 142, 139, 143, -1, + 143, 139, 144, -1, 144, 139, 140, -1, 143, 145, 142, -1, + 142, 145, 146, -1, 146, 145, 147, -1, 147, 145, 148, -1, + 148, 145, 149, -1, 149, 145, 143, -1, 146, 150, 137, -1, + 137, 150, 136, -1, 136, 150, 115, -1, 115, 150, 114, -1, + 114, 150, 151, -1, 151, 150, 146, -1, 138, 142, 137, -1, + 146, 137, 142, -1, 134, 141, 138, -1, 140, 152, 144, -1, + 143, 144, 152, -1, 143, 152, 153, -1, 154, 153, 152, -1, + 154, 152, 155, -1, 154, 155, 156, -1, 154, 156, 157, -1, + 158, 157, 156, -1, 158, 156, 159, -1, 158, 160, 157, -1, + 157, 160, 154, -1, 154, 160, 161, -1, 161, 160, 162, -1, + 162, 160, 163, -1, 163, 160, 164, -1, 164, 160, 158, -1, + 153, 154, 161, -1, 153, 161, 149, -1, 153, 149, 143, -1, + 146, 147, 151, -1, 151, 165, 166, -1, 166, 165, 167, -1, + 167, 165, 103, -1, 103, 165, 168, -1, 168, 165, 147, -1, + 147, 165, 151, -1, 151, 166, 114, -1, 105, 114, 166, -1, + 103, 91, 167, -1, 166, 167, 91, -1, 166, 91, 92, -1, + 166, 92, 105, -1, 162, 169, 170, -1, 170, 169, 171, -1, + 171, 169, 172, -1, 172, 169, 173, -1, 173, 169, 174, -1, + 174, 169, 162, -1, 148, 170, 171, -1, 148, 171, 168, -1, + 148, 168, 147, -1, 162, 170, 161, -1, 148, 161, 170, -1, + 161, 148, 149, -1, 176, 175, 168, -1, 168, 175, 103, -1, + 103, 175, 102, -1, 102, 175, 176, -1, 168, 171, 176, -1, + 172, 176, 171, -1, 176, 172, 173, -1, 163, 174, 162, -1, + 177, 173, 174, -1, 173, 177, 178, -1, 173, 178, 179, -1, + 173, 179, 180, -1, 173, 180, 176, -1, 101, 102, 181, -1, + 101, 181, 98, -1, 98, 182, 183, -1, 183, 182, 180, -1, + 180, 182, 176, -1, 176, 182, 102, -1, 102, 182, 181, -1, + 181, 182, 98, -1, 178, 184, 179, -1, 179, 184, 180, -1, + 180, 184, 185, -1, 185, 184, 186, -1, 186, 184, 187, -1, + 187, 184, 178, -1, 101, 98, 99, -1, 183, 188, 98, -1, + 98, 188, 97, -1, 183, 180, 185, -1, 97, 189, 96, -1, + 96, 189, 190, -1, 190, 189, 191, -1, 191, 189, 192, -1, + 192, 189, 193, -1, 193, 189, 194, -1, 194, 189, 185, -1, + 185, 189, 183, -1, 183, 189, 188, -1, 188, 189, 97, -1, + 196, 195, 186, -1, 186, 195, 185, -1, 185, 195, 197, -1, + 197, 195, 198, -1, 198, 195, 199, -1, 199, 195, 196, -1, + 196, 187, 200, -1, 200, 201, 199, -1, 199, 201, 198, -1, + 198, 201, 202, -1, 202, 201, 203, -1, 203, 201, 204, -1, + 204, 201, 200, -1, 187, 196, 186, -1, 196, 200, 199, -1, + 185, 197, 194, -1, 194, 205, 193, -1, 193, 205, 206, -1, + 206, 205, 207, -1, 207, 205, 208, -1, 208, 205, 197, -1, + 197, 205, 194, -1, 202, 208, 198, -1, 197, 198, 208, -1, + 190, 209, 96, -1, 96, 209, 86, -1, 86, 209, 85, -1, + 85, 209, 84, -1, 84, 209, 210, -1, 210, 209, 211, -1, + 211, 209, 190, -1, 213, 212, 191, -1, 191, 212, 211, -1, + 211, 212, 214, -1, 214, 212, 215, -1, 215, 212, 216, -1, + 216, 212, 213, -1, 216, 217, 218, -1, 218, 217, 219, -1, + 219, 217, 220, -1, 220, 217, 221, -1, 221, 217, 192, -1, + 192, 217, 213, -1, 213, 217, 216, -1, 191, 211, 190, -1, + 192, 213, 191, -1, 206, 192, 193, -1, 192, 206, 221, -1, + 203, 222, 202, -1, 202, 222, 223, -1, 223, 222, 224, -1, + 224, 222, 225, -1, 225, 222, 203, -1, 223, 226, 227, -1, + 227, 226, 228, -1, 228, 226, 229, -1, 229, 226, 224, -1, + 224, 226, 223, -1, 231, 230, 224, -1, 224, 230, 229, -1, + 229, 230, 232, -1, 232, 230, 233, -1, 233, 230, 234, -1, + 234, 230, 231, -1, 231, 224, 225, -1, 227, 235, 223, -1, + 223, 235, 208, -1, 208, 235, 207, -1, 202, 223, 208, -1, + 207, 236, 206, -1, 206, 236, 221, -1, 221, 236, 220, -1, + 220, 236, 237, -1, 237, 236, 238, -1, 238, 236, 239, -1, + 239, 236, 240, -1, 240, 236, 228, -1, 228, 236, 227, -1, + 227, 236, 235, -1, 235, 236, 207, -1, 229, 241, 228, -1, + 228, 241, 240, -1, 240, 241, 239, -1, 239, 241, 242, -1, + 242, 241, 232, -1, 232, 241, 229, -1, 233, 243, 232, -1, + 232, 243, 244, -1, 244, 243, 245, -1, 245, 243, 246, -1, + 246, 243, 247, -1, 247, 243, 233, -1, 245, 248, 244, -1, + 244, 248, 242, -1, 242, 248, 238, -1, 238, 248, 237, -1, + 242, 238, 239, -1, 244, 242, 232, -1, 245, 249, 248, -1, + 248, 249, 237, -1, 237, 249, 250, -1, 250, 249, 251, -1, + 251, 249, 252, -1, 252, 249, 253, -1, 253, 249, 246, -1, + 246, 249, 245, -1, 255, 254, 237, -1, 237, 254, 220, -1, + 220, 254, 219, -1, 219, 254, 255, -1, 255, 237, 250, -1, + 257, 256, 258, -1, 258, 256, 259, -1, 259, 256, 260, -1, + 260, 256, 251, -1, 251, 256, 250, -1, 250, 261, 255, -1, + 255, 261, 262, -1, 262, 261, 263, -1, 263, 261, 264, -1, + 264, 261, 257, -1, 257, 261, 250, -1, 257, 258, 264, -1, + 260, 265, 259, -1, 259, 265, 258, -1, 258, 265, 264, -1, + 264, 265, 266, -1, 266, 265, 267, -1, 267, 265, 268, -1, + 251, 252, 260, -1, 269, 260, 252, -1, 268, 265, 260, -1, + 268, 260, 269, -1, 218, 270, 216, -1, 216, 270, 215, -1, + 215, 270, 271, -1, 271, 270, 272, -1, 272, 270, 273, -1, + 273, 270, 218, -1, 273, 274, 272, -1, 272, 274, 275, -1, + 275, 274, 276, -1, 276, 274, 277, -1, 277, 274, 278, -1, + 278, 274, 279, -1, 279, 274, 273, -1, 255, 262, 219, -1, + 218, 219, 273, -1, 262, 273, 219, -1, 279, 273, 262, -1, + 279, 262, 280, -1, 263, 280, 262, -1, 266, 281, 263, -1, + 263, 281, 280, -1, 266, 263, 264, -1, 266, 282, 281, -1, + 281, 282, 280, -1, 280, 282, 278, -1, 278, 282, 283, -1, + 283, 282, 284, -1, 284, 282, 285, -1, 285, 282, 267, -1, + 267, 282, 266, -1, 267, 268, 285, -1, 280, 278, 279, -1, + 211, 214, 210, -1, 286, 284, 285, -1, 288, 287, 289, -1, + 289, 287, 290, -1, 290, 287, 291, -1, 291, 287, 292, -1, + 292, 287, 288, -1, 275, 293, 292, -1, 292, 293, 294, -1, + 294, 293, 295, -1, 295, 293, 296, -1, 296, 293, 276, -1, + 276, 293, 275, -1, 277, 297, 276, -1, 276, 297, 296, -1, + 296, 297, 298, -1, 299, 297, 300, -1, 300, 297, 277, -1, + 291, 292, 294, -1, 295, 296, 298, -1, 299, 300, 301, -1, + 283, 301, 300, -1, 283, 300, 278, -1, 302, 283, 284, -1, + 302, 301, 283, -1, 275, 288, 272, -1, 271, 272, 288, -1, + 288, 303, 271, -1, 271, 303, 215, -1, 215, 303, 214, -1, + 214, 303, 304, -1, 304, 303, 289, -1, 289, 303, 288, -1, + 275, 292, 288, -1, 278, 300, 277, -1, 210, 305, 84, -1, + 84, 305, 83, -1, 83, 305, 306, -1, 306, 305, 307, -1, + 307, 305, 308, -1, 308, 305, 304, -1, 304, 305, 210, -1, + 289, 308, 304, -1, 290, 309, 289, -1, 289, 309, 310, -1, + 310, 309, 311, -1, 311, 309, 312, -1, 312, 309, 290, -1, + 289, 310, 308, -1, 307, 308, 310, -1, 310, 313, 307, -1, + 307, 313, 314, -1, 314, 313, 315, -1, 315, 313, 316, -1, + 316, 313, 311, -1, 311, 313, 310, -1, 318, 317, 319, -1, + 319, 317, 312, -1, 312, 317, 320, -1, 320, 317, 321, -1, + 321, 317, 322, -1, 322, 317, 318, -1, 301, 323, 318, -1, + 318, 323, 322, -1, 322, 323, 321, -1, 321, 323, 324, -1, + 324, 323, 302, -1, 302, 323, 301, -1, 312, 290, 319, -1, + 284, 324, 302, -1, 284, 325, 324, -1, 324, 325, 326, -1, + 326, 325, 327, -1, 327, 325, 328, -1, 328, 325, 286, -1, + 286, 325, 284, -1, 324, 326, 321, -1, 320, 321, 326, -1, + 326, 329, 320, -1, 320, 329, 330, -1, 330, 329, 331, -1, + 331, 329, 332, -1, 332, 329, 327, -1, 327, 329, 326, -1, + 320, 330, 312, -1, 330, 311, 312, -1, 311, 330, 316, -1, + 316, 330, 331, -1, 286, 333, 328, -1, 334, 328, 333, -1, + 335, 328, 334, -1, 328, 335, 327, -1, 332, 327, 335, -1, + 332, 335, 336, -1, 332, 336, 337, -1, 332, 337, 338, -1, + 332, 338, 331, -1, 331, 339, 316, -1, 316, 339, 315, -1, + 315, 339, 340, -1, 340, 339, 341, -1, 341, 339, 338, -1, + 338, 339, 331, -1, 341, 338, 337, -1, 315, 340, 342, -1, + 315, 342, 314, -1, 307, 314, 306, -1, 83, 306, 314, -1, + 83, 314, 82, -1, 342, 82, 314, -1, 82, 342, 7, -1, + 79, 7, 342, -1, 214, 304, 210, -1, 344, 343, 345, -1, + 347, 346, 348, -1, 349, 348, 346, -1, 346, 350, 349, -1, + 351, 349, 350, -1, 351, 350, 352, -1, 353, 352, 350, -1, + 354, 350, 346, -1, 354, 346, 355, -1, 357, 356, 358, -1, + 358, 359, 360, -1, 357, 358, 360, -1, 355, 357, 360, -1, + 354, 355, 360, -1, 361, 354, 360, -1, 359, 361, 360, -1, + 362, 359, 358, -1, 362, 358, 356, -1, 362, 356, 363, -1, + 362, 363, 364, -1, 362, 364, 365, -1, 366, 365, 364, -1, + 364, 334, 366, -1, 334, 333, 366, -1, 286, 366, 333, -1, + 361, 359, 367, -1, 368, 361, 367, -1, 367, 369, 368, -1, + 370, 368, 369, -1, 371, 369, 367, -1, 365, 372, 373, -1, + 362, 365, 373, -1, 359, 362, 373, -1, 367, 359, 373, -1, + 371, 367, 373, -1, 372, 371, 373, -1, 371, 372, 374, -1, + 375, 374, 372, -1, 286, 376, 377, -1, 366, 286, 377, -1, + 365, 366, 377, -1, 372, 365, 377, -1, 375, 372, 377, -1, + 376, 375, 377, -1, 376, 378, 375, -1, 369, 379, 380, -1, + 378, 381, 382, -1, 375, 378, 382, -1, 374, 375, 382, -1, + 383, 374, 382, -1, 384, 383, 382, -1, 381, 384, 382, -1, + 383, 384, 385, -1, 374, 383, 385, -1, 371, 374, 385, -1, + 369, 371, 385, -1, 379, 369, 385, -1, 384, 379, 385, -1, + 368, 386, 387, -1, 361, 368, 387, -1, 354, 361, 387, -1, + 350, 354, 387, -1, 353, 350, 387, -1, 386, 353, 387, -1, + 353, 386, 388, -1, 389, 388, 386, -1, 369, 380, 370, -1, + 386, 368, 370, -1, 389, 386, 370, -1, 380, 389, 370, -1, + 389, 345, 388, -1, 345, 343, 390, -1, 388, 345, 390, -1, + 353, 388, 390, -1, 352, 353, 390, -1, 351, 352, 390, -1, + 391, 351, 390, -1, 343, 391, 390, -1, 393, 392, 394, -1, + 396, 395, 397, -1, 389, 395, 398, -1, 345, 389, 398, -1, + 344, 345, 398, -1, 399, 344, 398, -1, 400, 399, 398, -1, + 395, 400, 398, -1, 400, 395, 401, -1, 396, 401, 395, -1, + 378, 402, 381, -1, 378, 376, 402, -1, 402, 393, 394, -1, + 403, 381, 394, -1, 405, 404, 406, -1, 408, 407, 397, -1, + 394, 392, 409, -1, 403, 394, 409, -1, 404, 403, 409, -1, + 406, 404, 409, -1, 410, 406, 409, -1, 392, 410, 409, -1, + 410, 396, 411, -1, 406, 410, 411, -1, 405, 406, 411, -1, + 407, 405, 411, -1, 397, 407, 411, -1, 396, 397, 411, -1, + 397, 395, 412, -1, 408, 397, 412, -1, 380, 408, 412, -1, + 389, 380, 412, -1, 395, 389, 412, -1, 286, 285, 376, -1, + 413, 343, 344, -1, 415, 414, 393, -1, 416, 285, 268, -1, + 416, 417, 418, -1, 285, 416, 418, -1, 376, 285, 418, -1, + 402, 376, 418, -1, 393, 402, 418, -1, 415, 393, 418, -1, + 419, 415, 418, -1, 417, 419, 418, -1, 417, 420, 421, -1, + 421, 415, 419, -1, 417, 421, 419, -1, 421, 422, 415, -1, + 414, 415, 422, -1, 414, 422, 423, -1, 422, 424, 423, -1, + 425, 423, 424, -1, 426, 424, 422, -1, 414, 423, 427, -1, + 393, 414, 427, -1, 392, 393, 427, -1, 410, 392, 427, -1, + 396, 410, 427, -1, 401, 396, 427, -1, 423, 401, 427, -1, + 423, 425, 428, -1, 401, 423, 428, -1, 400, 401, 428, -1, + 399, 400, 428, -1, 429, 399, 428, -1, 425, 429, 428, -1, + 268, 269, 430, -1, 268, 430, 431, -1, 269, 252, 430, -1, + 432, 430, 252, -1, 416, 268, 431, -1, 417, 416, 431, -1, + 420, 417, 431, -1, 433, 420, 431, -1, 434, 433, 431, -1, + 430, 434, 431, -1, 435, 420, 433, -1, 435, 436, 437, -1, + 420, 435, 437, -1, 421, 420, 437, -1, 422, 421, 437, -1, + 426, 422, 437, -1, 436, 426, 437, -1, 432, 436, 438, -1, + 430, 432, 438, -1, 434, 430, 438, -1, 433, 434, 438, -1, + 435, 433, 438, -1, 436, 435, 438, -1, 426, 436, 439, -1, + 424, 426, 440, -1, 441, 424, 440, -1, 439, 441, 440, -1, + 426, 439, 440, -1, 246, 442, 443, -1, 253, 246, 443, -1, + 252, 253, 443, -1, 432, 252, 443, -1, 436, 432, 443, -1, + 439, 436, 443, -1, 444, 439, 443, -1, 442, 444, 443, -1, + 446, 445, 447, -1, 447, 448, 449, -1, 449, 439, 444, -1, + 447, 449, 444, -1, 446, 447, 444, -1, 442, 446, 444, -1, + 247, 233, 450, -1, 246, 247, 450, -1, 442, 246, 450, -1, + 446, 442, 450, -1, 445, 446, 450, -1, 233, 445, 450, -1, + 445, 451, 452, -1, 447, 445, 452, -1, 448, 447, 452, -1, + 453, 448, 452, -1, 454, 453, 452, -1, 451, 454, 452, -1, + 456, 455, 457, -1, 458, 456, 457, -1, 454, 458, 457, -1, + 453, 454, 457, -1, 448, 453, 457, -1, 449, 448, 457, -1, + 439, 449, 457, -1, 441, 439, 457, -1, 459, 441, 457, -1, + 460, 459, 457, -1, 455, 460, 457, -1, 462, 461, 463, -1, + 461, 455, 456, -1, 463, 461, 456, -1, 458, 463, 456, -1, + 231, 225, 464, -1, 234, 231, 465, -1, 233, 234, 465, -1, + 445, 233, 465, -1, 451, 445, 465, -1, 464, 451, 465, -1, + 231, 464, 465, -1, 464, 463, 466, -1, 451, 464, 466, -1, + 454, 451, 466, -1, 458, 454, 466, -1, 463, 458, 466, -1, + 225, 203, 467, -1, 464, 225, 467, -1, 463, 464, 467, -1, + 462, 463, 467, -1, 203, 462, 467, -1, 468, 459, 460, -1, + 460, 469, 468, -1, 468, 470, 471, -1, 470, 472, 413, -1, + 471, 429, 473, -1, 468, 471, 473, -1, 459, 468, 473, -1, + 441, 459, 473, -1, 424, 441, 473, -1, 425, 424, 473, -1, + 429, 425, 473, -1, 429, 471, 474, -1, 399, 429, 474, -1, + 344, 399, 474, -1, 413, 344, 474, -1, 470, 413, 474, -1, + 471, 470, 474, -1, 413, 472, 475, -1, 343, 413, 475, -1, + 391, 343, 475, -1, 476, 391, 475, -1, 477, 476, 475, -1, + 478, 477, 475, -1, 472, 478, 475, -1, 479, 461, 480, -1, + 462, 480, 461, -1, 479, 481, 482, -1, 461, 479, 482, -1, + 455, 461, 482, -1, 460, 455, 482, -1, 469, 460, 482, -1, + 481, 469, 482, -1, 483, 481, 479, -1, 485, 484, 200, -1, + 187, 486, 485, -1, 204, 200, 487, -1, 203, 204, 487, -1, + 462, 203, 487, -1, 480, 462, 487, -1, 484, 480, 487, -1, + 200, 484, 487, -1, 485, 200, 187, -1, 484, 485, 488, -1, + 480, 484, 488, -1, 479, 480, 488, -1, 483, 479, 488, -1, + 486, 483, 488, -1, 485, 486, 488, -1, 490, 489, 491, -1, + 492, 490, 491, -1, 483, 492, 491, -1, 481, 483, 491, -1, + 469, 481, 491, -1, 468, 469, 491, -1, 470, 468, 491, -1, + 472, 470, 491, -1, 478, 472, 491, -1, 489, 478, 491, -1, + 492, 483, 493, -1, 494, 489, 490, -1, 492, 494, 490, -1, + 496, 495, 494, -1, 187, 178, 497, -1, 486, 187, 497, -1, + 483, 486, 497, -1, 493, 483, 497, -1, 498, 493, 497, -1, + 178, 498, 497, -1, 499, 494, 500, -1, 501, 499, 500, -1, + 502, 501, 500, -1, 493, 502, 500, -1, 492, 493, 500, -1, + 494, 492, 500, -1, 496, 494, 499, -1, 496, 499, 501, -1, + 503, 502, 493, -1, 503, 493, 498, -1, 503, 498, 178, -1, + 503, 178, 177, -1, 177, 174, 503, -1, 163, 504, 174, -1, + 502, 503, 505, -1, 505, 506, 502, -1, 507, 502, 506, -1, + 501, 502, 508, -1, 509, 501, 508, -1, 507, 509, 508, -1, + 502, 507, 508, -1, 511, 510, 512, -1, 512, 513, 511, -1, + 504, 511, 513, -1, 512, 514, 507, -1, 512, 507, 506, -1, + 512, 506, 513, -1, 515, 513, 506, -1, 174, 504, 515, -1, + 503, 174, 515, -1, 505, 503, 515, -1, 506, 505, 515, -1, + 504, 513, 515, -1, 517, 516, 518, -1, 517, 518, 519, -1, + 517, 519, 520, -1, 509, 520, 519, -1, 516, 517, 521, -1, + 522, 521, 517, -1, 514, 522, 523, -1, 507, 514, 523, -1, + 509, 507, 523, -1, 520, 509, 523, -1, 517, 520, 523, -1, + 522, 517, 523, -1, 524, 522, 514, -1, 526, 525, 510, -1, + 526, 510, 511, -1, 526, 511, 527, -1, 164, 158, 528, -1, + 163, 164, 528, -1, 504, 163, 528, -1, 511, 504, 528, -1, + 527, 511, 528, -1, 529, 527, 528, -1, 158, 529, 528, -1, + 158, 159, 530, -1, 158, 530, 529, -1, 527, 529, 530, -1, + 527, 530, 531, -1, 527, 531, 532, -1, 527, 532, 526, -1, + 525, 526, 532, -1, 525, 532, 533, -1, 534, 533, 532, -1, + 536, 535, 537, -1, 524, 538, 539, -1, 535, 539, 538, -1, + 522, 524, 540, -1, 521, 522, 540, -1, 541, 521, 540, -1, + 542, 541, 540, -1, 539, 542, 540, -1, 524, 539, 540, -1, + 510, 525, 543, -1, 512, 510, 543, -1, 514, 512, 543, -1, + 524, 514, 543, -1, 538, 524, 543, -1, 525, 538, 543, -1, + 533, 534, 544, -1, 525, 533, 544, -1, 538, 525, 544, -1, + 535, 538, 544, -1, 537, 535, 544, -1, 534, 537, 544, -1, + 546, 545, 547, -1, 549, 548, 550, -1, 550, 551, 542, -1, + 541, 542, 551, -1, 541, 551, 552, -1, 551, 553, 552, -1, + 535, 536, 554, -1, 539, 535, 554, -1, 542, 539, 554, -1, + 550, 542, 554, -1, 549, 550, 554, -1, 536, 549, 554, -1, + 550, 548, 555, -1, 551, 550, 555, -1, 553, 551, 555, -1, + 545, 553, 555, -1, 547, 545, 555, -1, 548, 547, 555, -1, + 112, 556, 127, -1, 123, 127, 556, -1, 123, 556, 557, -1, + 553, 557, 556, -1, 553, 556, 552, -1, 545, 546, 558, -1, + 553, 545, 558, -1, 557, 553, 558, -1, 123, 557, 558, -1, + 122, 123, 558, -1, 121, 122, 558, -1, 559, 121, 558, -1, + 546, 559, 558, -1, 111, 107, 560, -1, 112, 560, 561, -1, + 556, 112, 561, -1, 552, 556, 561, -1, 541, 552, 561, -1, + 521, 541, 561, -1, 516, 521, 561, -1, 560, 516, 561, -1, + 560, 112, 111, -1, 562, 107, 563, -1, 565, 564, 566, -1, + 566, 562, 565, -1, 563, 565, 562, -1, 562, 566, 567, -1, + 107, 562, 567, -1, 560, 107, 567, -1, 516, 560, 567, -1, + 518, 516, 567, -1, 566, 518, 567, -1, 569, 568, 495, -1, + 509, 519, 570, -1, 501, 509, 570, -1, 496, 501, 570, -1, + 495, 496, 570, -1, 569, 495, 570, -1, 519, 569, 570, -1, + 568, 569, 571, -1, 495, 568, 572, -1, 494, 495, 572, -1, + 489, 494, 572, -1, 478, 489, 572, -1, 477, 478, 572, -1, + 573, 477, 572, -1, 571, 573, 572, -1, 568, 571, 572, -1, + 569, 574, 571, -1, 573, 571, 575, -1, 573, 575, 576, -1, + 564, 577, 578, -1, 566, 564, 578, -1, 518, 566, 578, -1, + 519, 518, 578, -1, 569, 519, 578, -1, 574, 569, 578, -1, + 577, 574, 578, -1, 577, 564, 579, -1, 580, 579, 564, -1, + 582, 581, 583, -1, 584, 582, 583, -1, 576, 584, 583, -1, + 573, 576, 583, -1, 477, 573, 583, -1, 476, 477, 583, -1, + 391, 476, 583, -1, 351, 391, 583, -1, 349, 351, 583, -1, + 348, 349, 583, -1, 581, 348, 583, -1, 585, 348, 581, -1, + 348, 586, 587, -1, 348, 587, 347, -1, 588, 347, 589, -1, + 590, 588, 589, -1, 591, 590, 589, -1, 586, 591, 589, -1, + 587, 586, 589, -1, 347, 587, 589, -1, 590, 591, 592, -1, + 593, 590, 592, -1, 594, 593, 592, -1, 595, 594, 592, -1, + 591, 595, 592, -1, 593, 594, 596, -1, 597, 593, 596, -1, + 588, 590, 598, -1, 588, 598, 599, -1, 601, 600, 599, -1, + 590, 593, 602, -1, 598, 590, 602, -1, 599, 598, 602, -1, + 601, 599, 602, -1, 603, 601, 602, -1, 597, 603, 602, -1, + 593, 597, 602, -1, 605, 604, 606, -1, 608, 607, 609, -1, + 609, 610, 606, -1, 611, 606, 610, -1, 605, 606, 611, -1, + 597, 596, 612, -1, 603, 597, 612, -1, 606, 603, 612, -1, + 609, 606, 612, -1, 608, 609, 612, -1, 596, 608, 612, -1, + 604, 580, 613, -1, 601, 603, 614, -1, 600, 601, 614, -1, + 613, 600, 614, -1, 604, 613, 614, -1, 606, 604, 614, -1, + 603, 606, 614, -1, 579, 580, 615, -1, 616, 579, 615, -1, + 617, 616, 615, -1, 605, 617, 615, -1, 604, 605, 615, -1, + 580, 604, 615, -1, 577, 579, 616, -1, 616, 618, 577, -1, + 574, 577, 618, -1, 619, 571, 574, -1, 619, 574, 618, -1, + 619, 618, 620, -1, 616, 617, 621, -1, 618, 616, 621, -1, + 620, 618, 621, -1, 622, 620, 621, -1, 623, 622, 621, -1, + 617, 623, 621, -1, 622, 623, 624, -1, 625, 620, 622, -1, + 626, 625, 622, -1, 611, 610, 627, -1, 605, 611, 627, -1, + 617, 605, 627, -1, 623, 617, 627, -1, 624, 623, 627, -1, + 628, 624, 627, -1, 629, 628, 627, -1, 610, 629, 627, -1, + 630, 584, 576, -1, 630, 576, 626, -1, 575, 626, 576, -1, + 625, 626, 631, -1, 620, 625, 631, -1, 619, 620, 631, -1, + 571, 619, 631, -1, 575, 571, 631, -1, 626, 575, 631, -1, + 632, 624, 628, -1, 633, 632, 634, -1, 635, 633, 634, -1, + 630, 632, 633, -1, 624, 632, 636, -1, 622, 624, 636, -1, + 626, 622, 636, -1, 630, 626, 636, -1, 632, 630, 636, -1, + 637, 581, 582, -1, 633, 635, 638, -1, 630, 633, 638, -1, + 584, 630, 638, -1, 582, 584, 638, -1, 637, 582, 638, -1, + 635, 637, 638, -1, 639, 585, 640, -1, 641, 639, 640, -1, + 634, 641, 640, -1, 640, 585, 642, -1, 634, 640, 642, -1, + 635, 634, 642, -1, 637, 635, 642, -1, 581, 637, 642, -1, + 585, 581, 642, -1, 644, 643, 639, -1, 644, 639, 645, -1, + 646, 644, 645, -1, 629, 646, 645, -1, 628, 629, 645, -1, + 632, 628, 645, -1, 634, 632, 645, -1, 641, 634, 645, -1, + 639, 641, 645, -1, 647, 607, 648, -1, 647, 648, 643, -1, + 649, 643, 648, -1, 609, 607, 650, -1, 610, 609, 650, -1, + 629, 610, 650, -1, 646, 629, 650, -1, 644, 646, 650, -1, + 643, 644, 650, -1, 647, 643, 650, -1, 607, 647, 650, -1, + 594, 595, 651, -1, 596, 594, 651, -1, 608, 596, 651, -1, + 607, 608, 651, -1, 648, 607, 651, -1, 595, 648, 651, -1, + 649, 586, 652, -1, 643, 649, 652, -1, 639, 643, 652, -1, + 585, 639, 652, -1, 348, 585, 652, -1, 586, 348, 652, -1, + 586, 649, 653, -1, 591, 586, 653, -1, 595, 591, 653, -1, + 648, 595, 653, -1, 649, 648, 653, -1, 319, 290, 291, -1, + 319, 291, 294, -1, 319, 294, 295, -1, 319, 295, 318, -1, + 318, 298, 301, -1, 301, 298, 299, -1, 381, 403, 404, -1, + 405, 384, 404, -1, 379, 384, 405, -1, 379, 405, 407, -1, + 380, 379, 407, -1, 394, 381, 402, -1, 408, 380, 407, -1, + 318, 295, 298, -1, 299, 298, 297, -1, 381, 404, 384, -1, + 257, 250, 256, -1] + } + } + } + ] + } + + DEF TS TimeSensor {cycleInterval 4 loop TRUE} + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [1 2 1] + } + + Billboard { + axisOfRotation 0 0 0 + children [ + + Transform { + translation -2 2 0 + children [ + Shape { + appearance Appearance {material Material2D { emissiveColor 1 0 0 filled TRUE}} + geometry Rectangle { size 4 1} + } + Shape { + appearance Appearance {material Material2D { emissiveColor 1 1 1 filled TRUE}} + geometry DEF TEXT Text { string "Taper" fontStyle DEF FS FontStyle { size 0.8 justify ["MIDDLE" "MIDDLE"] style "TEXTURED"}} + } + DEF TS_MODE TouchSensor {} + ] + } + Transform { + translation 2 2 0 + children [ + Shape { + appearance Appearance {material Material2D { emissiveColor 0 0 1 filled TRUE}} + geometry Rectangle { size 4 1} + } + Shape { + appearance Appearance {material Material2D { emissiveColor 1 1 1 filled TRUE}} + geometry DEF TEXT_AXIS Text { string "Y Axis" fontStyle USE FS} + } + DEF TS_AXIS TouchSensor {} + ] + } + ] + } + + +DEF SC Script { + eventIn SFBool change_mode + eventIn SFBool change_axis + field SFNode deform USE NLD + field SFNode inter USE SI + field SFNode txt USE TEXT + field SFNode txt_axis USE TEXT_AXIS + url ["javascript: + function initialize() { + def_mode = 0; + def_axis = 0; + } + function change_mode(value, timestamp) { + if (!value) return; + def_mode++; + if (def_mode==3) def_mode = 0; + deform.type = def_mode; + switch (def_mode) { + case 0: + txt.string[0] = 'Taper'; + deform.extend[1] = 0.5; + deform.extend[3] = 1; + deform.extend[5] = 0.2; + inter.keyValue = new MFFloat(1, 2, 1); + break; + case 1: + txt.string[0] = 'Twister'; + deform.extend[1] = 0; + deform.extend[3] = 6.18; + deform.extend[5] = 0.0; + inter.keyValue = new MFFloat(-1, 1, -1); + break; + case 2: + txt.string[0] = 'Bender'; + deform.extend[1] = 0; + deform.extend[3] = 3.14; + deform.extend[5] = 0; + inter.keyValue = new MFFloat(-1, 1, -1); + break; + } + } + function change_axis(value, timestamp) { + if (!value) return; + def_axis++; + if (def_axis==3) def_axis = 0; + switch (def_axis) { + case 0: + txt_axis.string[0] = 'Y Axis'; + deform.axis = new SFVec3f(0, 1, 0); + break; + case 1: + txt_axis.string[0] = 'X Axis'; + deform.axis = new SFVec3f(1, 0, 0); + break; + case 2: + txt_axis.string[0] = 'Z Axis'; + deform.axis = new SFVec3f(0, 0, 1); + break; + } + } + "] +} + + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO NLD.param + +ROUTE TS_MODE.isActive TO SC.change_mode +ROUTE TS_AXIS.isActive TO SC.change_axis diff --git a/regression_tests/bifs-3D-shapes-pointset.bt b/regression_tests/bifs-3D-shapes-pointset.bt new file mode 100644 index 0000000..4704b48 --- /dev/null +++ b/regression_tests/bifs-3D-shapes-pointset.bt @@ -0,0 +1,46 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetrics false + pixelWidth 450 + pixelHeight 450 + } + } + } + ] +} + +Group { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows PointSet" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "PointSet Test" + } + Shape { + appearance Appearance { + material Material { diffuseColor 1 0 0} + } + geometry PointSet { + coord DEF COORD Coordinate { + point [-1 1 1, 1 1 1, 1 1 -1, -1 1 -1, -1 -1 1, 1 -1 1, 1 -1 -1, -1 -1 -1] + } + } + } + ] +} + + diff --git a/regression_tests/bifs-3D-texturing-box-transparent.bt b/regression_tests/bifs-3D-texturing-box-transparent.bt new file mode 100644 index 0000000..b4992ed --- /dev/null +++ b/regression_tests/bifs-3D-texturing-box-transparent.bt @@ -0,0 +1,73 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "Box test" + info ["This shows a 3D cube with transparency" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + translation 0 0 -50 + children [ + Shape { + appearance Appearance { + material Material { + emissiveColor 1 0 1 + diffuseColor 1 1 1 + } + } + geometry Box {size 150 100 10} + } + ] + } + + DEF TR Transform { + translation -50 0 0 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { + diffuseColor 1 1 0 + shininess 0.5 + transparency 0.5 + } + } + geometry Box {size 50 50 50} + } + ] + } + DEF TS TimeSensor { cycleInterval 2.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26] + } + ] +} + +ROUTE TS.fraction_changed TO MAT.transparency +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation diff --git a/regression_tests/bifs-3D-texturing-box-video.bt b/regression_tests/bifs-3D-texturing-box-video.bt new file mode 100644 index 0000000..44ebcc1 --- /dev/null +++ b/regression_tests/bifs-3D-texturing-box-video.bt @@ -0,0 +1,184 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric FALSE + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { +children [ + + Background2D { + backColor 1 1 1 + } + Viewpoint {} + WorldInfo { + info ["This shows spinning cubes with image and video textures" "GPAC Regression Tests" "$Date: 2008/11/24 14:58:25 $ - $Revision: 1.5 $" "(C) 2002-2004 GPAC Team"] + title "Cube, Texture Test" + } + + DEF TR Transform { + rotation 1 1 1 0.5 + children [ + Transform { + children [ + DEF Red Transform { + children [ + Shape { + appearance DEF Red-App Appearance { + material Material { + diffuseColor 1.0 0.0 0.0 + } + texture ImageTexture { url "5" } + } + geometry Box { } + } + ] + } + Transform { translation 2.0 2.0 0.0 children [ USE Red ] } + Transform { translation -2.0 2.0 0.0 children [USE Red ] } + Transform { translation 2.0 -2.0 0.0 children [USE Red ]} + Transform { translation -2.0 -2.0 0.0 children [USE Red ]} + Transform { translation 0.0 -2.0 2.0 children [USE Red ]} + Transform { translation 0.0 2.0 2.0 children [USE Red ]} + Transform { translation 2.0 0.0 2.0 children [USE Red ]} + Transform { translation -2.0 0.0 2.0 children [USE Red ]} + Transform { translation 0.0 -2.0 -2.0 children [USE Red ]} + Transform { translation 0.0 2.0 -2.0 children [USE Red ]} + Transform { translation 2.0 0.0 -2.0 children [USE Red ]} + Transform { translation -2.0 0.0 -2.0 children [USE Red ]} + ] + } + + Transform { + children [ + DEF Blue Transform { + translation 2.0 0.0 0.0 + children [ + Shape { + appearance DEF Blue-App Appearance { + material Material { + diffuseColor 0.0 0.0 1.0 + } + texture DEF M2 MovieTexture { url "10" loop TRUE } + } + geometry Box { } + } + ] + } + Transform { translation -4.0 0.0 0.0 children [USE Blue] } + Transform { translation -2.0 0.0 2.0 children [USE Blue] } + Transform { translation -2.0 0.0 -2.0 children [USE Blue] } + Transform { translation -2.0 2.0 0.0 children [USE Blue] } + Transform { translation -2.0 -2.0 0.0 children [USE Blue] } + Transform { translation 0.0 2.0 2.0 children [USE Blue] } + Transform { translation 0.0 2.0 -2.0 children [USE Blue] } + Transform { translation -4.0 2.0 2.0 children [USE Blue] } + Transform { translation -4.0 2.0 -2.0 children [USE Blue] } + Transform { translation 0.0 -2.0 2.0 children [USE Blue] } + Transform { translation 0.0 -2.0 -2.0 children [USE Blue] } + Transform { translation -4.0 -2.0 2.0 children [USE Blue] } + Transform { translation -4.0 -2.0 -2.0 children [USE Blue] } + ] + } + + DEF Clock TimeSensor { + cycleInterval 3.0 + loop FALSE + } + + DEF Trigger TimeSensor { + loop TRUE + cycleInterval 5.0 + } + + DEF RedScale PositionInterpolator { + key [ 0.0, 0.5, 1.0 ] + keyValue [ + 1.0 1.0 1.0, + 0.0001 0.0001 0.0001, + 1.0 1.0 1.0, + ] + } + + DEF BlueScale PositionInterpolator { + key [ 0.0, 0.25, 0.5, 0.75, 1.0 ] + keyValue [ + 1.0 1.0 1.0, + 0.0001 0.0001 0.0001, + 1.0 1.0 1.0, + 0.0001 0.0001 0.0001, + 1.0 1.0 1.0, + ] + } + ] + } + + DEF TS TimeSensor { cycleInterval 2.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26] + } + +] +} + +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation +ROUTE Trigger.cycleTime TO Clock.startTime +ROUTE Clock.fraction_changed TO RedScale.set_fraction +ROUTE Clock.fraction_changed TO BlueScale.set_fraction +ROUTE RedScale.value_changed TO Red.scale +ROUTE BlueScale.value_changed TO Blue.scale + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 5 + esDescr [ + ES_Descriptor { + ES_ID 10 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-texturing-box.bt b/regression_tests/bifs-3D-texturing-box.bt new file mode 100644 index 0000000..d592ef6 --- /dev/null +++ b/regression_tests/bifs-3D-texturing-box.bt @@ -0,0 +1,69 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "Box test" + info ["This shows a box" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + rotation 1 1 1 1.57 + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + texture ImageTexture { url "5" } + } + geometry Box {size 120 100 80} + } + ] + } + ] +} + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 5 + esDescr [ + ES_Descriptor { + ES_ID 10 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-texturing-compositetexture3D-bitmap.bt b/regression_tests/bifs-3D-texturing-compositetexture3D-bitmap.bt new file mode 100644 index 0000000..895ab1f --- /dev/null +++ b/regression_tests/bifs-3D-texturing-compositetexture3D-bitmap.bt @@ -0,0 +1,113 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + + WorldInfo { + title "Box test" + info ["This shows a bitmap using" "a compositetexture3D" "the 3D texture is a spinning box with an image texture" "" "GPAC Regression Tests" "$Date: 2008/07/22 10:54:58 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + Background2D{url 20} + + Shape { + appearance Appearance { + material DEF MAT Material2D { emissiveColor 1 1 0 filled FALSE transparency 0} + texture CompositeTexture3D { + pixelWidth 128 + pixelHeight 128 + children [ + DEF BACK Background2D {backColor 0 1 0} + DEF TR2 Transform { + rotation 1 1 1 -0.65 + children [ + Shape { + appearance Appearance { + material DEF MAT2 Material { emissiveColor 0 0 1 } + texture ImageTexture {url 10} + } + geometry Box {size 50 50 50} + } + DEF TOUCH TouchSensor{} + ] + } + ] + } + } +# geometry Bitmap{} + geometry Circle {radius 60} + } + + DEF TS TimeSensor { cycleInterval 2.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26] + } + + ] +} + +ROUTE TS.fraction_changed TO OI.set_fraction +#ROUTE TS.fraction_changed TO MAT2.transparency +ROUTE OI.value_changed TO TR2.rotation +ROUTE TOUCH.isActive TO BACK.set_bind + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 10 + muxInfo MuxInfo { + fileName "./auxiliary_files/logo.png" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 20 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "./auxiliary_files/sky.jpg" + } + } + ] + } + ] +} + +#take care of bind delay +AT 100 { +# REPLACE BACK.set_bind BY FALSE +} diff --git a/regression_tests/bifs-3D-texturing-compositetexture3D-box.bt b/regression_tests/bifs-3D-texturing-compositetexture3D-box.bt new file mode 100644 index 0000000..58bca32 --- /dev/null +++ b/regression_tests/bifs-3D-texturing-compositetexture3D-box.bt @@ -0,0 +1,106 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + + Background2D { backColor 1 1 1} + + WorldInfo { + title "Composite Texture test" + info ["This shows a box" "with a 3D composite texture" "the texture is a spinning sphere with an image texture" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + + DEF TR Transform { + translation 0 0 0 + children [ + Shape { + appearance Appearance { + texture CompositeTexture3D { + pixelWidth 128 + pixelHeight 128 + children [ + #MPEG-4 is stupid here, background2D can't be encoded in the CT3D.background field + Background2D {url "auxiliary_files/sky.jpg" backColor 1 0 0} + DEF TR2 Transform { + scale 1 0.5 1 + children [ + Shape { + appearance Appearance { + material DEF MAT2 Material { diffuseColor 0 0 1 } + texture ImageTexture { url "10"} + } + geometry Sphere {radius 60} + } + ] + } + ] + } + } + geometry Box {size 64 64 64 } + } + ] + } + + DEF TS TimeSensor { cycleInterval 2.0 loop TRUE } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 0.8 0] + } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26] + } + ] +} + +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation +ROUTE OI.value_changed TO TR2.rotation +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO MAT2.transparency + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-texturing-cone-transparent.bt b/regression_tests/bifs-3D-texturing-cone-transparent.bt new file mode 100644 index 0000000..0c54f1b --- /dev/null +++ b/regression_tests/bifs-3D-texturing-cone-transparent.bt @@ -0,0 +1,68 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "Cone test" + info ["This shows cones with and without cap" "with transparency - objects should appear behing each-other" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + translation -70 0 0 + rotation 1 0 0 4 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { diffuseColor 1.0 0.0 0.0 transparency 0.5} + } + geometry Cone {height 150 bottomRadius 25} + } + ] + } + Transform { + translation 70 0 0 + rotation 1 0 0 4 + children [ + Shape { + appearance Appearance { +# material USE MAT + material Material { diffuseColor 1.0 0.0 1.0} + } + geometry Cone {height 150 bottomRadius 25 bottom FALSE} + } + ] + } + DEF TS TimeSensor {loop TRUE cycleInterval 6} + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO MAT.transparency + diff --git a/regression_tests/bifs-3D-texturing-cone.bt b/regression_tests/bifs-3D-texturing-cone.bt new file mode 100644 index 0000000..5d953c2 --- /dev/null +++ b/regression_tests/bifs-3D-texturing-cone.bt @@ -0,0 +1,81 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "Cone test" + info ["This shows a cone" "with and without cap" "and a texture map" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + translation -70 0 0 + rotation 1 0 0 4 + children [ + Shape { + appearance DEF APP Appearance { + material Material {diffuseColor 1.0 0.0 0.0 } + texture ImageTexture { url "5" } + } + geometry Cone {height 150 bottomRadius 25} + } + ] + } + Transform { + translation 70 0 0 + rotation 1 0 0 4 + children [ + Shape { + appearance USE APP + geometry Cone {height 150 bottomRadius 25 bottom FALSE} + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 5 + esDescr [ + ES_Descriptor { + ES_ID 10 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-texturing-cylinder-transparent.bt b/regression_tests/bifs-3D-texturing-cylinder-transparent.bt new file mode 100644 index 0000000..e5478f0 --- /dev/null +++ b/regression_tests/bifs-3D-texturing-cylinder-transparent.bt @@ -0,0 +1,93 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 380 + pixelHeight 300 + } + } + } + ] +} + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "Cylinder test" + info ["This shows a cylinder" "with and without cap and side and transparent material" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + translation -100 100 0 + children [ + Shape { + appearance DEF APP Appearance { + material DEF MAT Material {diffuseColor 1.0 0.0 0.0 } + } + geometry Cylinder {height 150 radius 25} + } + ] + } + Transform { + translation 0 100 0 + rotation 1 0 0 0.78 + children [ + Shape { + appearance USE APP + geometry Cylinder {height 150 radius 25 top FALSE} + } + ] + } + Transform { + translation -100 -100 0 + rotation 1 0 0 -0.78 + children [ + Shape { + appearance USE APP + geometry Cylinder {height 150 radius 25 bottom FALSE} + } + ] + } + Transform { + translation 0 -100 0 + rotation 1 0 0 1.2 + children [ + Shape { + appearance USE APP + geometry Cylinder {height 150 radius 25 bottom FALSE top FALSE} + } + ] + } + Transform { + translation 100 0 0 + children [ + Shape { + appearance USE APP + geometry Cylinder {height 150 radius 25 side FALSE } + } + ] + } + DEF TS TimeSensor {loop TRUE cycleInterval 6} + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO MAT.transparency + diff --git a/regression_tests/bifs-3D-texturing-cylinder.bt b/regression_tests/bifs-3D-texturing-cylinder.bt new file mode 100644 index 0000000..fdc798c --- /dev/null +++ b/regression_tests/bifs-3D-texturing-cylinder.bt @@ -0,0 +1,107 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 380 + pixelHeight 300 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "Cylinder test" + info ["This shows a cylinder" "with and without cap and side and a texture map" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + translation -100 100 0 + children [ + Shape { + appearance DEF APP Appearance { + material Material {diffuseColor 1.0 0.0 0.0 } + texture ImageTexture { url "5" } + } + geometry Cylinder {height 150 radius 25} + } + ] + } + Transform { + translation 0 100 0 + rotation 1 0 0 0.78 + children [ + Shape { + appearance USE APP + geometry Cylinder {height 150 radius 25 top FALSE} + } + ] + } + Transform { + translation -100 -100 0 + rotation 1 0 0 -0.78 + children [ + Shape { + appearance USE APP + geometry Cylinder {height 150 radius 25 bottom FALSE} + } + ] + } + Transform { + translation 0 -100 0 + rotation 1 0 0 1.2 + children [ + Shape { + appearance USE APP + geometry Cylinder {height 150 radius 25 bottom FALSE top FALSE} + } + ] + } + Transform { + translation 100 0 0 + children [ + Shape { + appearance USE APP + geometry Cylinder {height 150 radius 25 side FALSE } + } + ] + } + ] +} + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 5 + esDescr [ + ES_Descriptor { + ES_ID 10 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} diff --git a/regression_tests/bifs-3D-texturing-transform-box.bt b/regression_tests/bifs-3D-texturing-transform-box.bt new file mode 100644 index 0000000..7b44a38 --- /dev/null +++ b/regression_tests/bifs-3D-texturing-transform-box.bt @@ -0,0 +1,85 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "Texture Transform test" + info ["This shows a 3D cube" "mapped with an alpha PNG and texture transform" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + DEF TR Transform { + rotation 1 1 1 0.75 + children [ + Shape { + appearance Appearance { + material DEF MAT Material {} + textureTransform DEF MX TextureTransform { scale 4 4 } + texture ImageTexture { url "10"} + } + geometry Box {size 64 64 64 } + } + ] + } + + DEF TS TimeSensor { cycleInterval 2.0 loop TRUE } + DEF PI PositionInterpolator2D { + key [0 0.25 0.5 0.75 1] + keyValue [1 1, 4 1, 4 4, 1 4, 1 1] + } + DEF CI ColorInterpolator { + key [0 0.5 1] + keyValue [1 1 1, 0 0 1, 1 1 1] + } + ] +} + +ROUTE TS.fraction_changed TO PI.set_fraction +ROUTE PI.value_changed TO MX.scale +ROUTE TS.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO MAT.diffuseColor + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-texturing-transform-matrix-box.bt b/regression_tests/bifs-3D-texturing-transform-matrix-box.bt new file mode 100644 index 0000000..9bf53af --- /dev/null +++ b/regression_tests/bifs-3D-texturing-transform-matrix-box.bt @@ -0,0 +1,91 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "Texture Transform test" + info ["This shows a 3D cube" "mapped with an alpha PNG and texture transform" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + DEF TR Transform { + rotation 1 1 1 0.75 + children [ + Shape { + appearance Appearance { + material DEF MAT Material {} + textureTransform DEF MX TransformMatrix2D {mxx 3 myy 2} + texture ImageTexture { url "10"} + } + geometry Box {size 64 64 64 } + } + ] + } + + DEF TS TimeSensor { cycleInterval 8.0 loop TRUE } + DEF SI ScalarInterpolator { + key [0 0.25 0.5 0.75 1] + keyValue [1 1 1 6 1] + } + DEF SI2 ScalarInterpolator { + key [0 0.25 0.5 0.75 1] + keyValue [1 6 1 1 1] + } + DEF CI ColorInterpolator { + key [0 0.5 1] + keyValue [1 1 1, 0 0 1, 1 1 1] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO MX.mxy +ROUTE TS.fraction_changed TO SI2.set_fraction +ROUTE SI2.value_changed TO MX.myx +ROUTE TS.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO MAT.diffuseColor + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-3D-viewpoint-anim.bt b/regression_tests/bifs-3D-viewpoint-anim.bt new file mode 100644 index 0000000..0ed10b8 --- /dev/null +++ b/regression_tests/bifs-3D-viewpoint-anim.bt @@ -0,0 +1,84 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 200 + } + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "Viewpoint animation test test" + info ["This shows a sphere" "and dynamic viewpoint" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + DEF VP Viewpoint {position -200 0 100 orientation 0 1 0 -1.0 } + + Transform { + translation 0 0 -50 + children [ + Shape { + appearance Appearance { + material Material { + diffuseColor 1 1 1 + } + } + geometry Box {size 150 100 10} + } + ] + } + + DEF TR Transform { + translation -50 0 0 + scale 0.75 1 1 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { + diffuseColor 1 1 0 + shininess 0.5 + } + } + geometry Sphere {radius 25 } + } + ] + } + DEF TS TimeSensor { cycleInterval 4.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [0 0 1 0, 0 0 1 3.14, 0 0 1 6.26] + } + DEF OI2 OrientationInterpolator { + key [0 0.5 1] + keyValue [0 1 0 -1, 0 1 0 1, 0 1 0 -1] + } + DEF PI PositionInterpolator { + key [0 0.5 1] + keyValue [-200 0 100, 200 0 100, -200 0 100] + } + ] +} + +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation +ROUTE TS.fraction_changed TO OI2.set_fraction +ROUTE OI2.value_changed TO VP.orientation +ROUTE TS.fraction_changed TO PI.set_fraction +ROUTE PI.value_changed TO VP.position diff --git a/regression_tests/bifs-3D-viewpoint-bind-jump.bt b/regression_tests/bifs-3D-viewpoint-bind-jump.bt new file mode 100644 index 0000000..c091612 --- /dev/null +++ b/regression_tests/bifs-3D-viewpoint-bind-jump.bt @@ -0,0 +1,114 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +Group { + children [ + WorldInfo { + title "Viewpoint bind test" + info ["This shows sensors triggering viewpoint binding" "Viewpoint switching shall not be animated" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + DEF VP1 Viewpoint {description "Front View" position 0 0 300} + DEF VP2 Viewpoint {description "Above View" position 0 300 30 orientation 1 0 0 -1.2 } + + DEF TR Transform { + translation 50 0 0 + children [ + Shape { + appearance Appearance { + material Material { + diffuseColor 0 1 1 + } + } + geometry DEF BOX Box {size 50 50 50} + } + DEF TS2 TouchSensor {} + ] + } + + Transform { + translation -80 0 20 + children [ + Shape { + appearance Appearance { + material Material2D { emissiveColor 1 0 0 filled TRUE } + } + geometry DEF RC_RED Rectangle {size 100 100} + } + ] + } + + Shape { + appearance Appearance { + material Material2D { emissiveColor 1 1 1 filled TRUE } + } + geometry DEF RC_WHITE Rectangle { size 200 200 } + } + + + Transform { + translation -30 0 0 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { + diffuseColor 1 1 0 + } + } + geometry DEF SPHERE Sphere {radius 30} + } + DEF TOUCH TouchSensor {} + ] + } + DEF TS TimeSensor { enabled FALSE cycleInterval 2.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26] + } + + DEF C Conditional { buffer { REPLACE MAT.diffuseColor BY 0 0 1 } } + DEF RC Conditional { buffer { REPLACE MAT.diffuseColor BY 1 1 0 } } + + DEF SETVP1 Conditional { + buffer { + REPLACE VP1.set_bind BY TRUE + REPLACE ROUTE MVP BY TOUCH.isActive TO SETVP2.activate + } + } + DEF SETVP2 Conditional { + buffer { + REPLACE VP2.set_bind BY TRUE + REPLACE ROUTE MVP BY TOUCH.isActive TO SETVP1.activate + } + } + + ] +} + +DEF MVP ROUTE TOUCH.isActive TO SETVP2.activate +ROUTE TOUCH.isOver TO TS.enabled +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation +ROUTE TS2.isActive TO C.activate +ROUTE TS2.isActive TO RC.reverseActivate diff --git a/regression_tests/bifs-3D-viewpoint-bind.bt b/regression_tests/bifs-3D-viewpoint-bind.bt new file mode 100644 index 0000000..55c4c38 --- /dev/null +++ b/regression_tests/bifs-3D-viewpoint-bind.bt @@ -0,0 +1,115 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + useNames true + } + } + } + ] +} + +Group { + children [ + WorldInfo { + title "Viewpoint bind test" + info ["This shows sensors triggering viewpoint binding" "Viewpoint switching shall be animated" "" "GPAC Regression Tests" "$Date: 2008/11/24 14:58:25 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + + DEF VP1 Viewpoint {description "Front View" position 0 0 200 jump FALSE fieldOfView 0.39} + DEF VP2 Viewpoint {description "Above View" position 0 300 30 orientation 1 0 0 -1.2 jump FALSE} + + DEF TR Transform { + translation 50 0 0 + children [ + Shape { + appearance Appearance { + material Material { + diffuseColor 0 1 1 + } + } + geometry DEF BOX Box {size 50 50 50} + } + DEF TS2 TouchSensor {} + ] + } + + Transform { + translation -80 0 20 + children [ + Shape { + appearance Appearance { + material Material2D { emissiveColor 1 0 0 filled TRUE } + } + geometry DEF RC_RED Rectangle {size 100 100} + } + ] + } + + Shape { + appearance Appearance { + material Material2D { emissiveColor 1 1 1 filled TRUE } + } + geometry DEF RC_WHITE Rectangle { size 200 200 } + } + + + Transform { + translation -30 0 0 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { + diffuseColor 1 1 0 + } + } + geometry DEF SPHERE Sphere {radius 30} + } + DEF TOUCH TouchSensor {} + ] + } + DEF TS TimeSensor { enabled FALSE cycleInterval 2.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26] + } + + DEF C Conditional { buffer { REPLACE MAT.diffuseColor BY 0 0 1 } } + DEF RC Conditional { buffer { REPLACE MAT.diffuseColor BY 1 1 0 } } + + DEF SETVP1 Conditional { + buffer { + REPLACE VP1.set_bind BY TRUE + REPLACE ROUTE MVP BY TOUCH.isActive TO SETVP2.activate + } + } + DEF SETVP2 Conditional { + buffer { + REPLACE VP2.set_bind BY TRUE + REPLACE ROUTE MVP BY TOUCH.isActive TO SETVP1.activate + } + } + + ] +} + +DEF MVP ROUTE TOUCH.isActive TO SETVP2.activate +ROUTE TOUCH.isOver TO TS.enabled +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation +ROUTE TS2.isActive TO C.activate +ROUTE TS2.isActive TO RC.reverseActivate diff --git a/regression_tests/bifs-3D-viewpoint-ortho-bind.bt b/regression_tests/bifs-3D-viewpoint-ortho-bind.bt new file mode 100644 index 0000000..ff1804e --- /dev/null +++ b/regression_tests/bifs-3D-viewpoint-ortho-bind.bt @@ -0,0 +1,114 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + title "Viewpoint bind test" + info ["This shows sensors triggering viewpoint binding" "Viewpoint switching shall be animated" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + DEF VP1 Viewpoint {description "Front View" position 0 0 200 jump FALSE} + DEF VP2 Viewpoint {description "Above View" position 0 300 30 orientation 1 0 0 -1.2 jump FALSE} + + DEF TR Transform { + translation 50 0 0 + children [ + Shape { + appearance Appearance { + material Material { + diffuseColor 0 1 1 + } + } + geometry DEF BOX Box {size 50 50 50} + } + DEF TS2 TouchSensor {} + ] + } + + Transform { + translation -80 0 20 + children [ + Shape { + appearance Appearance { + material Material2D { emissiveColor 1 0 0 filled TRUE } + } + geometry DEF RC_RED Rectangle {size 100 100} + } + ] + } + + Shape { + appearance Appearance { + material Material2D { emissiveColor 1 1 1 filled TRUE } + } + geometry DEF RC_WHITE Rectangle { size 200 200 } + } + + + Transform { + translation -30 0 0 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { + diffuseColor 1 1 0 + } + } + geometry DEF SPHERE Sphere {radius 30} + } + DEF TOUCH TouchSensor {} + ] + } + DEF TS TimeSensor { enabled FALSE cycleInterval 2.0 loop TRUE } + DEF OI OrientationInterpolator { + key [0 0.5 1] + keyValue [1 1 1 0, 1 1 1 3.14, 1 1 1 6.26] + } + + DEF C Conditional { buffer { REPLACE MAT.diffuseColor BY 0 0 1 } } + DEF RC Conditional { buffer { REPLACE MAT.diffuseColor BY 1 1 0 } } + + DEF SETVP1 Conditional { + buffer { + REPLACE VP1.set_bind BY TRUE + REPLACE ROUTE MVP BY TOUCH.isActive TO SETVP2.activate + } + } + DEF SETVP2 Conditional { + buffer { + REPLACE VP2.set_bind BY TRUE + REPLACE ROUTE MVP BY TOUCH.isActive TO SETVP1.activate + } + } + + ] +} + +DEF MVP ROUTE TOUCH.isActive TO SETVP2.activate +ROUTE TOUCH.isOver TO TS.enabled +ROUTE TS.fraction_changed TO OI.set_fraction +ROUTE OI.value_changed TO TR.rotation +ROUTE TS2.isActive TO C.activate +ROUTE TS2.isActive TO RC.reverseActivate diff --git a/regression_tests/bifs-bitmap-image-meter-metrics.bt b/regression_tests/bifs-bitmap-image-meter-metrics.bt new file mode 100644 index 0000000..088253f --- /dev/null +++ b/regression_tests/bifs-bitmap-image-meter-metrics.bt @@ -0,0 +1,80 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelWidth 400 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows dragable bitmap with scale 0.75 0.75" "in Meter Metrics" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Bitmap Test" + } + Transform2D { + children [ + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + texture ImageTexture { + url [od:10] + } + } + geometry Bitmap { + scale 0.75 0.75 + } + } + DEF PS PlaneSensor2D { + maxPosition 1 1 + minPosition -1 -1 + } + ] + } + ] + } + ] +} + +ROUTE PS.translation_changed TO TR.translation + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-bitmap-image-pixel-metrics.bt b/regression_tests/bifs-bitmap-image-pixel-metrics.bt new file mode 100644 index 0000000..6dfb121 --- /dev/null +++ b/regression_tests/bifs-bitmap-image-pixel-metrics.bt @@ -0,0 +1,89 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 0 + } + WorldInfo { + info [ + "This test shows an Image (the Osmo4 logo), which has an alpha component being display on a yellow background." + "The geometry node use to display the image is a Bitmap node with a scale of 0.75 0.75" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:08 $ - $Revision: 1.4 $" + "(C) 2002-2004 GPAC Team" + ] + title "Bitmap and Transparent Images" + } + Transform2D { + children [ + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + texture ImageTexture { + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry Bitmap { + scale 0.75 0.75 + } + } + DEF PS PlaneSensor2D { + maxPosition 200 200 + minPosition -200 -200 + } + ] + } + ] + } + ] +} + +ROUTE PS.translation_changed TO TR.translation + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.png" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-bitmap-image-resizing.bt b/regression_tests/bifs-bitmap-image-resizing.bt new file mode 100644 index 0000000..4590288 --- /dev/null +++ b/regression_tests/bifs-bitmap-image-resizing.bt @@ -0,0 +1,153 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows an image whose size is animated." + "The result is displayed on top of a static image." + "Both textures use a Bitmap node to allow efficient handling of the textures." + "The resizing of the image is not done using a Transform2D node (or TransformMatrix2D) but by changing the size property of the Bitmap node." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.4 $" + "(C) 2002-2006 GPAC Team" + ] + title "Image Resizing" + } + Transform2D { + translation 0 128 + children [ + DEF TR Transform2D { + children [ + Transform2D { + translation -128 0 + children [ + DEF S Shape { + appearance Appearance { + texture ImageTexture { + url [od:12] + repeatS FALSE + repeatT FALSE + } + } + geometry Bitmap {} + + } + ] + } + Transform2D { + children [ + USE S + ] + } + Transform2D { + translation 128 0 + children [ + USE S + ] + } + ] + } + ] + } + Transform2D { + children [ + USE TR + ] + } + Transform2D { + translation 0 -128 + children [ + USE TR + ] + } + Transform2D { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + texture ImageTexture { + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry DEF BMP Bitmap { + scale 0.75 0.75 + } + } + ] + } + ] + } + DEF TS TimeSensor { + loop TRUE + } + DEF PI PositionInterpolator2D { + key [0 0.5 1] + keyValue [0.75 0.75 0.25 0.25 0.75 0.75] + } + ] +} + +ROUTE TS.fraction_changed TO PI.set_fraction +ROUTE PI.value_changed TO BMP.scale + +RAP AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 12 + esDescr [ + ES_Descriptor { + ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-bitmap-movie-materialkey.bt b/regression_tests/bifs-bitmap-movie-materialkey.bt new file mode 100644 index 0000000..3dd9cd2 --- /dev/null +++ b/regression_tests/bifs-bitmap-movie-materialkey.bt @@ -0,0 +1,101 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + DEF B Background2D { + backColor 0 0 1 + } + WorldInfo { + info [ + "This test shows how to do Color Keying on Video." + "The MaterialKey node allows defining a key color or color range and to make that color transparent in the texture. The degree of transparency can be specified using the transparency property." + + "" + "GPAC Regression Tests" "$Date: 2008/05/19 15:28:17 $ - $Revision: 1.6 $" + "(C) 2002-2004 GPAC Team" + ] + title "Color Keying on Video using the MaterialKey node" + } + Transform2D { + scale 2 2 + children [ + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + texture MovieTexture { + url [od:10] + loop FALSE + } + material DEF MK MaterialKey { + isKeyed TRUE + lowThreshold 0.4 + highThreshold 0.4 + keyColor 1 1 0 + transparency 0 + } + } + geometry Bitmap { + scale 1 1 + } + } + ] + } + ] + } + DEF TS TimeSensor {loop TRUE cycleInterval 10.0} + DEF CI ColorInterpolator { + key [0 1] + keyValue [1 1 1, 0 0 1] + } + ] +} + +#ROUTE TS.fraction_changed TO MK.highThreshold +ROUTE TS.fraction_changed TO CI.set_fraction +ROUTE CI.value_changed TO B.backColor + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-bitmap-movie.bt b/regression_tests/bifs-bitmap-movie.bt new file mode 100644 index 0000000..5fad0a4 --- /dev/null +++ b/regression_tests/bifs-bitmap-movie.bt @@ -0,0 +1,84 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "The Bitmap node is a geometry node which defines a rectangle on which the texture is mapped. The difference with a Rectangle node is that its size is given by the texture, hence no resampling of the texture is needed during the display. Hardware accelerated operations can be used." + "This test shows a dragable video. The Bitmap node allows efficient draging and display of the video." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.4 $" + "(C) 2002-2006 GPAC Team" + ] + title "Efficient Display of Video using the Bitmap node" + } + Transform2D { + scale 2 2 + children [ + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + texture MovieTexture { + url [od:10] + loop FALSE + } + } + geometry Bitmap { + scale 1 1 + } + } + ] + } + ] + } + ] +} + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-bitmap-video-resizing.bt b/regression_tests/bifs-bitmap-video-resizing.bt new file mode 100644 index 0000000..6509472 --- /dev/null +++ b/regression_tests/bifs-bitmap-video-resizing.bt @@ -0,0 +1,152 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows a video whose size is animated, the result is displayed on top of a static image." + "Both textures use a Bitmap node to allow efficient handling of the textures." + "The resizing of the video is not done using a Transform2D node (or TransformMatrix2D) but by changing the size property of the Bitmap node." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.5 $" + "(C) 2002-2006 GPAC Team" + ] + title "Bitmap Video Resizing" + } + Transform2D { + translation 0 128 + children [ + DEF TR Transform2D { + children [ + Transform2D { + translation -128 0 + children [ + DEF S Shape { + appearance Appearance { + texture ImageTexture { + url [od:12] + repeatS FALSE + repeatT FALSE + } + } + geometry Bitmap {} + + } + ] + } + Transform2D { + children [ + USE S + ] + } + Transform2D { + translation 128 0 + children [ + USE S + ] + } + ] + } + ] + } + Transform2D { + children [ + USE TR + ] + } + Transform2D { + translation 0 -128 + children [ + USE TR + ] + } + Transform2D { + children [ + Transform2D { + children [ + Shape { + appearance Appearance { + texture MovieTexture { + loop TRUE + url [od:10] + } + } + geometry DEF BMP Bitmap { + scale 0.75 0.75 + } + } + ] + } + ] + } + DEF TS TimeSensor { + loop TRUE + } + DEF PI PositionInterpolator2D { + key [0 0.5 1] + keyValue [2 2 0.25 0.25 2 2] + } + ] +} + +ROUTE TS.fraction_changed TO PI.set_fraction +ROUTE PI.value_changed TO BMP.scale + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 12 + esDescr [ + ES_Descriptor { + ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-command-animated-osmo4logo.bt b/regression_tests/bifs-command-animated-osmo4logo.bt new file mode 100644 index 0000000..a6f726c --- /dev/null +++ b/regression_tests/bifs-command-animated-osmo4logo.bt @@ -0,0 +1,315 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 0 1 1 + } + WorldInfo { + info ["This shows the Osmo4 logo" "animated through BIFS commands" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Osmo4 Logo" + } + Shape { + appearance Appearance { + texture LinearGradient { + endPoint 0 1 + key [0 0.33 1] + keyValue [0 0.5 0.5 0 0.75 0.75 0 0.75 1] + } + } + geometry Rectangle { + size 300 300 + } + } + Switch { + choice [ + DEF BCIRCLE Shape { + appearance DEF ABLACK Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Circle { + radius 60 + } + } + DEF BTRIANGLE Shape { + appearance USE ABLACK + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coord Coordinate2D { + point [-40 0 40 0 0 -60] + } + } + } + DEF RCIRCLE Shape { + appearance Appearance { + material DEF RMAT Material2D { + lineProps DEF LP XLineProperties { + lineColor 1 0 0 + width 20 + } + } + } + geometry Circle { + radius 100 + } + } + ] + } + DEF MOV Transform2D { + children [ + DEF LOGO Transform2D {} + ] + } + ] +} + + +AT 1000 { + APPEND TO LOGO.children DEF BR Transform2D { + scale 0 0 + translation -150 -150 + children [ + USE BCIRCLE + ] + } + APPEND TO MOV.children DEF TIME TimeSensor { + cycleInterval 1.5 + } + APPEND TO MOV.children DEF POS PositionInterpolator2D { + key [0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1] + keyValue [-150 -150 -135 135 120 120 105 -105 -90 -90 -75 75 60 60 45 -45 -30 -30 -15 15 0 0] + } + APPEND TO MOV.children DEF SCALE PositionInterpolator2D { + key [0 1] + keyValue [0.1 0.1 1 1] + } + INSERT ROUTE DEF R1 TIME.fraction_changed TO POS.set_fraction + INSERT ROUTE DEF R2 TIME.fraction_changed TO SCALE.set_fraction + INSERT ROUTE DEF R3 POS.value_changed TO BR.translation + INSERT ROUTE DEF R4 SCALE.value_changed TO BR.scale +} + +AT 2500 { + DELETE ROUTE R1 + DELETE ROUTE R2 + DELETE ROUTE R3 + DELETE ROUTE R4 + DELETE TIME + DELETE POS + DELETE SCALE + REPLACE BR.translation BY 0 0 + REPLACE BR.scale BY 1 1 + APPEND TO LOGO.children DEF BT Transform2D { + scale 9 0.5 + translation 0 20 + children [ + USE BTRIANGLE + ] + } + APPEND TO MOV.children DEF TIME2 TimeSensor { + cycleInterval 1.5 + } + APPEND TO MOV.children DEF POS_BT PositionInterpolator2D { + key [0 1] + keyValue [0 20 0 90] + } + APPEND TO MOV.children DEF POS_BR PositionInterpolator2D { + key [0 1] + keyValue [0 0 0 -30] + } + APPEND TO MOV.children DEF SCALE_BR PositionInterpolator2D { + key [0 0.7 1] + keyValue [1 1 1.4 1.4 1 1] + } + APPEND TO MOV.children DEF SCALE_BT PositionInterpolator2D { + key [0 0.7 1] + keyValue [9 0.5 5 0.7 1 1] + } + INSERT ROUTE DEF R1 TIME2.fraction_changed TO POS_BT.set_fraction + INSERT ROUTE DEF R2 TIME2.fraction_changed TO POS_BR.set_fraction + INSERT ROUTE DEF R3 TIME2.fraction_changed TO SCALE_BT.set_fraction + INSERT ROUTE DEF R4 TIME2.fraction_changed TO SCALE_BR.set_fraction + INSERT ROUTE DEF R5 POS_BT.value_changed TO BT.translation + INSERT ROUTE DEF R6 POS_BR.value_changed TO BR.translation + INSERT ROUTE DEF R7 SCALE_BT.value_changed TO BT.scale + INSERT ROUTE DEF R8 SCALE_BR.value_changed TO BR.scale +} + +AT 4000 { + DELETE ROUTE R1 + DELETE ROUTE R2 + DELETE ROUTE R3 + DELETE ROUTE R4 + DELETE ROUTE R5 + DELETE ROUTE R6 + DELETE ROUTE R7 + DELETE ROUTE R8 + DELETE TIME2 + DELETE POS_BT + DELETE SCALE_BT + DELETE POS_BR + DELETE SCALE_BR + REPLACE BT.translation BY 0 90 + REPLACE BR.translation BY 0 -30 + REPLACE BR.scale BY 1 1 + REPLACE BT.scale BY 1 1 + REPLACE LP.width BY 0 + APPEND TO LOGO.children DEF RC Transform2D { + children [ + USE RCIRCLE + ] + } + APPEND TO MOV.children DEF TIME3 TimeSensor { + cycleInterval 1.5 + } + APPEND TO MOV.children DEF WIDTH ScalarInterpolator { + key [0 0.5 0.75 1] + keyValue [0 25 15 20] + } + APPEND TO MOV.children DEF COL ColorInterpolator { + key [0 1] + keyValue [0 0 0 1 0 0] + } + INSERT ROUTE DEF R1 TIME3.fraction_changed TO WIDTH.set_fraction + INSERT ROUTE DEF R2 TIME3.fraction_changed TO COL.set_fraction + INSERT ROUTE DEF R3 WIDTH.value_changed TO LP.width + INSERT ROUTE DEF R4 COL.value_changed TO LP.lineColor +} + +AT 5500 { + DELETE ROUTE R1 + DELETE ROUTE R2 + DELETE ROUTE R3 + DELETE ROUTE R4 + DELETE TIME3 + DELETE WIDTH + DELETE COL + REPLACE LP.width BY 20 + REPLACE LP.transparency BY 0 + REPLACE LP.lineColor BY 1 0 0 + APPEND TO MOV.children Transform2D { + translation 110 38 + children [ + Shape { + appearance Appearance { + material DEF TXTMAT Material2D { + emissiveColor 0.1 0 0.1 + filled TRUE + transparency 1 + } + } + geometry Text { + string ["sm"] + fontStyle DEF FS FontStyle { + justify ["BEGIN" "BEGIN"] + size 160 + } + } + } + ] + } + APPEND TO MOV.children Transform2D { + scale 0.3 0.3 + translation 340 -70 + children [ + USE LOGO + ] + } + APPEND TO MOV.children Transform2D { + translation 380 38 + children [ + Shape { + appearance Appearance { + material USE TXTMAT + } + geometry Text { + string ["4"] + fontStyle USE FS + } + } + ] + } + APPEND TO MOV.children DEF TIME4 TimeSensor { + cycleInterval 1.5 + } + APPEND TO MOV.children DEF POS_MOV PositionInterpolator2D { + key [0 0.5 1] + keyValue [0 0 75 0 -75 0] + } + APPEND TO MOV.children DEF SCALE_MOV PositionInterpolator2D { + key [0 0.5 1] + keyValue [1 1 0.4 0.4 0.4 0.4] + } + APPEND TO MOV.children DEF ALPHA2 ScalarInterpolator { + key [0 0.9 1] + keyValue [0.95 0.5 0] + } + INSERT ROUTE DEF R1 TIME4.fraction_changed TO POS_MOV.set_fraction + INSERT ROUTE DEF R2 TIME4.fraction_changed TO SCALE_MOV.set_fraction + INSERT ROUTE DEF R3 TIME4.fraction_changed TO ALPHA2.set_fraction + INSERT ROUTE DEF R4 POS_MOV.value_changed TO MOV.translation + INSERT ROUTE DEF R5 SCALE_MOV.value_changed TO MOV.scale + INSERT ROUTE DEF R6 ALPHA2.value_changed TO TXTMAT.transparency +} + +AT 7000 { + DELETE ROUTE R1 + DELETE ROUTE R2 + DELETE ROUTE R3 + DELETE ROUTE R4 + DELETE ROUTE R5 + DELETE ROUTE R6 + DELETE TIME4 + DELETE ALPHA2 + DELETE POS_MOV + DELETE SCALE_MOV + REPLACE TXTMAT.transparency BY 0.5 + REPLACE MOV.translation BY -75 0 + REPLACE MOV.scale BY 0.4 0.4 + + APPEND TO LOGO.children Transform2D { + children [ + DEF TS TimeSensor { + cycleInterval 4 + loop TRUE + } + DEF PI PositionInterpolator2D { + key [0 0.25 0.5 0.75 1] + keyValue [1 1 -1 1 1 1 1 -1 1 1] + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 0.8 0] + } + ] + } + INSERT ROUTE TS.fraction_changed TO PI.set_fraction + INSERT ROUTE TS.fraction_changed TO SI.set_fraction + INSERT ROUTE PI.value_changed TO LOGO.scale + INSERT ROUTE SI.value_changed TO TXTMAT.transparency +} + diff --git a/regression_tests/bifs-command-delete-index.bt b/regression_tests/bifs-command-delete-index.bt new file mode 100644 index 0000000..933ecff --- /dev/null +++ b/regression_tests/bifs-command-delete-index.bt @@ -0,0 +1,58 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows indexed value deletion" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Indexed Value delete test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + Transform2D { + scale 0.5 0.5 + children [ + DEF S Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + DELETE COORD.point[0] +} + diff --git a/regression_tests/bifs-command-delete-node.bt b/regression_tests/bifs-command-delete-node.bt new file mode 100644 index 0000000..eb6477f --- /dev/null +++ b/regression_tests/bifs-command-delete-node.bt @@ -0,0 +1,58 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows node deletion" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Node delete test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + Transform2D { + scale 0.5 0.5 + children [ + DEF S Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + DELETE MAT +} + diff --git a/regression_tests/bifs-command-delete-route.bt b/regression_tests/bifs-command-delete-route.bt new file mode 100644 index 0000000..59aa2f0 --- /dev/null +++ b/regression_tests/bifs-command-delete-route.bt @@ -0,0 +1,67 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows route deletion" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Route delete test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + DEF TS TimeSensor { + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +DEF R1 ROUTE SI.value_changed TO MAT.transparency + +AT 2500 { + DELETE ROUTE R1 +} + diff --git a/regression_tests/bifs-command-global-qp.bt b/regression_tests/bifs-command-global-qp.bt new file mode 100644 index 0000000..8269eea --- /dev/null +++ b/regression_tests/bifs-command-global-qp.bt @@ -0,0 +1,66 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +DEF OG OrderedGroup { + children [ + WorldInfo { + info ["This shows GlobalQuantizer usage" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "GlobalQuantizer test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + ] +} + + +AT 100 { + GLOBALQP QuantizationParameter { + position2DQuant TRUE + position2DMin -100 -100 + position2DMax 100 100 + position2DNbBits 2 + } +} + +AT 1000 { + APPEND TO OG.children DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } +} + diff --git a/regression_tests/bifs-command-insert-index.bt b/regression_tests/bifs-command-insert-index.bt new file mode 100644 index 0000000..fd51f0d --- /dev/null +++ b/regression_tests/bifs-command-insert-index.bt @@ -0,0 +1,58 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows indexed value insertion" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Indexed Value insert test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + Transform2D { + scale 0.5 0.5 + children [ + DEF S Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + INSERT AT COORD.point[0] -50 -50 +} + diff --git a/regression_tests/bifs-command-insert-node.bt b/regression_tests/bifs-command-insert-node.bt new file mode 100644 index 0000000..b34c875 --- /dev/null +++ b/regression_tests/bifs-command-insert-node.bt @@ -0,0 +1,74 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +DEF OG OrderedGroup { + children [ + WorldInfo { + info ["This shows node insertion" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Node insert test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + DEF S Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + INSERT AT OG.children[0] Transform2D { + scale 0.5 0.5 + translation -100 -50 + children [ + USE S + ] + } +} + +AT 4000 { + APPEND TO OG.children Transform2D { + scale 0.5 0.5 + translation 100 50 + children [ + USE S + ] + } +} + diff --git a/regression_tests/bifs-command-insert-nodedef.bt b/regression_tests/bifs-command-insert-nodedef.bt new file mode 100644 index 0000000..3bfd59a --- /dev/null +++ b/regression_tests/bifs-command-insert-nodedef.bt @@ -0,0 +1,64 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +DEF OG OrderedGroup { + children [ + WorldInfo { + info ["This shows node insertion" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Node insert test" + } + Background2D { + backColor 1 1 1 + } + Transform2D { + scale 0.5 0.5 + children [ + DEF S Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + INSERT AT OG.children[0] DEF TR2 Transform2D { + scale 0.5 0.5 + translation -100 -50 + children [ + USE S + ] + } +} + diff --git a/regression_tests/bifs-command-insert-route.bt b/regression_tests/bifs-command-insert-route.bt new file mode 100644 index 0000000..fced44e --- /dev/null +++ b/regression_tests/bifs-command-insert-route.bt @@ -0,0 +1,67 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows route insertion" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "route insert test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + DEF TS TimeSensor { + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +DEF R1 ROUTE SI.value_changed TO MAT.transparency + +AT 2000 { + INSERT ROUTE SI.value_changed TO TR.rotationAngle +} + diff --git a/regression_tests/bifs-command-multiple-replace-field.bt b/regression_tests/bifs-command-multiple-replace-field.bt new file mode 100644 index 0000000..a4d750a --- /dev/null +++ b/regression_tests/bifs-command-multiple-replace-field.bt @@ -0,0 +1,66 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + includeInlineProfileLevelFlag true + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows multiple field replacement" "through BIFS extended commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "multiple field replace test" + } + DEF N0 Background2D { + backColor 1 1 1 + } + DEF N1 Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF N2 Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF N3 Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 1000 { + MULTIPLEREPLACE N2 { + emissiveColor 0 1 0 + transparency 0.9 + lineProps LineProperties { + lineColor 0 0 1 + width 2 + } + } +} + diff --git a/regression_tests/bifs-command-multiple-replace-index.bt b/regression_tests/bifs-command-multiple-replace-index.bt new file mode 100644 index 0000000..1fd07bd --- /dev/null +++ b/regression_tests/bifs-command-multiple-replace-index.bt @@ -0,0 +1,62 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows multiple indexed field replacement" "through BIFS Extended commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "multiple indexed field replace test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + MULTIPLEINDREPLACE COORD.point [ + 0 BY -20 0 + 2 BY 50 50 + 5 BY -50 -80 + ] +} + diff --git a/regression_tests/bifs-command-node-delete-ex.bt b/regression_tests/bifs-command-node-delete-ex.bt new file mode 100644 index 0000000..1ae1f39 --- /dev/null +++ b/regression_tests/bifs-command-node-delete-ex.bt @@ -0,0 +1,125 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows extended node deletion" "through BIFS extended commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Node delete test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF OG OrderedGroup { + order [2 1 3] + children [ + DEF TR Transform2D { + scale 0.5 0.5 + children [ + DEF S Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + Transform2D { + translation -50 40 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 1 + filled TRUE + } + } + geometry Circle { + radius 30 + } + } + ] + } + Transform2D { + translation 50 -50 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 1 + filled TRUE + } + } + geometry Rectangle { + size 100 50 + } + } + ] + } + ] + } + Transform2D { + translation 0 -100 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT Text { + string ["test"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 24 + } + } + } + ] + } + DEF SC Script { + eventIn SFTime set_text + field SFNode t USE TXT + field SFNode g USE OG + url ["javascript: function set_text(value, ts) {t.string[0] = 'order: ' + g.order;}" ] + } + DEF TS TimeSensor { + loop TRUE + } + ] +} + +ROUTE TS.cycleTime TO SC.set_text + +AT 2000 { + XDELETE TR +} + diff --git a/regression_tests/bifs-command-proto-delete.bt b/regression_tests/bifs-command-proto-delete.bt new file mode 100644 index 0000000..278cedf --- /dev/null +++ b/regression_tests/bifs-command-proto-delete.bt @@ -0,0 +1,93 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +PROTO MYPROTO [ + exposedField SFVec2f translation 0 0 +] { + Transform2D { + translation IS translation + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + ] + } +} +PROTO MYOTHERPROTO [ + exposedField SFVec2f translation 0 0 +] { + Transform2D { + translation IS translation + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 100 50 + } + } + ] + } +} +DEF ORD OrderedGroup { + children [ + DEF BACK Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows proto declaration deletion" "through BIFS Extended commands" "The player should complain about unknown proto" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Proto Delete test" + } + MYPROTO {} + ] +} + + +AT 1000 { + DELETEPROTO ALL +} + +AT 2000 { + INSERT AT ORD.children[2] MYPROTO { + translation -100 0 + } +} + +AT 3000 { + INSERT AT ORD.children[2] MYOTHERPROTO { + translation 100 0 + } +} + diff --git a/regression_tests/bifs-command-proto-insert.bt b/regression_tests/bifs-command-proto-insert.bt new file mode 100644 index 0000000..1e3b534 --- /dev/null +++ b/regression_tests/bifs-command-proto-insert.bt @@ -0,0 +1,83 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +DEF ORD OrderedGroup { + children [ + DEF BACK Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows proto declaration insertion" "through BIFS Extended commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Proto Insert test" + } + DEF TR Transform2D { + scale 0.5 0.5 + translation -100 0 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 1000 { + INSERTPROTO [ + PROTO MYPROTO [ + exposedField SFVec2f translation 0 0 + ] { + Transform2D { + translation IS translation + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + ] + } + } + ] + INSERT AT ORD.children[0] MYPROTO { + translation 100 0 + } +} + diff --git a/regression_tests/bifs-command-protolist-delete.bt b/regression_tests/bifs-command-protolist-delete.bt new file mode 100644 index 0000000..d419fc5 --- /dev/null +++ b/regression_tests/bifs-command-protolist-delete.bt @@ -0,0 +1,93 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +PROTO MYPROTO [ + exposedField SFVec2f translation 0 0 +] { + Transform2D { + translation IS translation + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } + ] + } +} +PROTO MYOTHERPROTO [ + exposedField SFVec2f translation 0 0 +] { + Transform2D { + translation IS translation + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 100 50 + } + } + ] + } +} +DEF ORD OrderedGroup { + children [ + DEF BACK Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows proto declaration list deletion" "through BIFS Extended commands" "The player should complain about unknown nodes" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Proto List Delete test" + } + MYPROTO {} + ] +} + + +AT 1000 { + DELETEPROTO [MYPROTO] +} + +AT 2000 { + INSERT AT ORD.children[0] MYPROTO { + translation -100 0 + } +} + +AT 3000 { + INSERT AT ORD.children[0] MYOTHERPROTO { + translation 100 0 + } +} + diff --git a/regression_tests/bifs-command-quantification.bt b/regression_tests/bifs-command-quantification.bt new file mode 100644 index 0000000..0d54474 --- /dev/null +++ b/regression_tests/bifs-command-quantification.bt @@ -0,0 +1,186 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows quantification of IFS2D" "with several values" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Quantification Test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + QuantizationParameter { + position2DQuant TRUE + position2DMin -100 -100 + position2DMax 100 100 + position2DNbBits 9 + colorQuant FALSE + } + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + REPLACE TR BY DEF TR1 Transform2D { + scale 0.5 0.5 + children [ + QuantizationParameter { + position2DQuant TRUE + position2DMin -100 -100 + position2DMax 100 100 + position2DNbBits 8 + colorQuant FALSE + } + Shape { + appearance Appearance { + material USE MAT + } + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } +} + +AT 4000 { + REPLACE TR1 BY DEF TR2 Transform2D { + scale 0.5 0.5 + children [ + QuantizationParameter { + position2DQuant TRUE + position2DMin -100 -100 + position2DMax 100 100 + position2DNbBits 6 + colorQuant FALSE + } + Shape { + appearance Appearance { + material USE MAT + } + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } +} + +AT 6000 { + REPLACE TR2 BY DEF TR3 Transform2D { + scale 0.5 0.5 + children [ + QuantizationParameter { + position2DQuant TRUE + position2DMin -100 -100 + position2DMax 100 100 + position2DNbBits 4 + colorQuant FALSE + } + Shape { + appearance Appearance { + material USE MAT + } + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } +} + +AT 8000 { + REPLACE TR3 BY DEF TR4 Transform2D { + scale 0.5 0.5 + children [ + QuantizationParameter { + position2DQuant TRUE + position2DMin -100 -100 + position2DMax 100 100 + position2DNbBits 2 + colorQuant FALSE + } + Shape { + appearance Appearance { + material USE MAT + } + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } +} + +AT 10000 { + REPLACE TR4 BY DEF TR5 Transform2D { + scale 0.5 0.5 + children [ + QuantizationParameter { + position2DQuant TRUE + position2DMin -100 -100 + position2DMax 100 100 + position2DNbBits 1 + colorQuant FALSE + } + Shape { + appearance Appearance { + material USE MAT + } + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } +} + diff --git a/regression_tests/bifs-command-replace-field.bt b/regression_tests/bifs-command-replace-field.bt new file mode 100644 index 0000000..d5bb9e6 --- /dev/null +++ b/regression_tests/bifs-command-replace-field.bt @@ -0,0 +1,67 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info [ + "This test shows field replacement using BIFS commands." + "At initialization, the scene shows a polygon filled in red" + "At 2s, the scene is modified and the polygon is not filled, just the border is diplayed." + "The command used in this sequence replaces the content of the field and not just a single value" + "It applies to both SFField and MFField, for replacement of single value in an MFField, use Indexed Replacement" + "cf bifs-command-replace-index" + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.3 $" + "(C) 2002-2004 GPAC Team"] + title "Field Replacement" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + REPLACE MAT.filled BY FALSE +} + diff --git a/regression_tests/bifs-command-replace-index.bt b/regression_tests/bifs-command-replace-index.bt new file mode 100644 index 0000000..6f31dbe --- /dev/null +++ b/regression_tests/bifs-command-replace-index.bt @@ -0,0 +1,58 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows indexed value replacement" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Indexed Value replace test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + REPLACE COORD.point[2] BY 100 100 +} + diff --git a/regression_tests/bifs-command-replace-node-null.bt b/regression_tests/bifs-command-replace-node-null.bt new file mode 100644 index 0000000..735ff3c --- /dev/null +++ b/regression_tests/bifs-command-replace-node-null.bt @@ -0,0 +1,65 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info [ + "This test shows a BIFS command replacing a node by the NULL node" + "Initially, the scene has a white background but at 2s, the Background2D node is replaced by a NULL node. Since the Background2D node is not used anymore, it is deleted." + "Replacing with a NULL node does not change the index of the following siblings." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Node Replacement with a NULL node" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + REPLACE BACK BY NULL +} + diff --git a/regression_tests/bifs-command-replace-node.bt b/regression_tests/bifs-command-replace-node.bt new file mode 100644 index 0000000..3ce1e78 --- /dev/null +++ b/regression_tests/bifs-command-replace-node.bt @@ -0,0 +1,67 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info [ + "This test shows node replacement through BIFS commands" + "Initally, the scene has a white background, but at 2s the Background2D node is replaced by a new node defining a yellow background." + "The same test could be done replacing the field backColor of the Background2D node, this would be a field replacement." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Node Replacement" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + +AT 2000 { + REPLACE BACK BY Background2D { + backColor 0 1 1 + } +} + diff --git a/regression_tests/bifs-command-replace-route.bt b/regression_tests/bifs-command-replace-route.bt new file mode 100644 index 0000000..70323ed --- /dev/null +++ b/regression_tests/bifs-command-replace-route.bt @@ -0,0 +1,67 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows route replacement" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "route replace test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + DEF TS TimeSensor { + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +DEF R1 ROUTE SI.value_changed TO MAT.transparency + +AT 2000 { + REPLACE ROUTE R1 BY SI.value_changed TO TR.rotationAngle +} + diff --git a/regression_tests/bifs-command-replace-scene-null.bt b/regression_tests/bifs-command-replace-scene-null.bt new file mode 100644 index 0000000..96a0faf --- /dev/null +++ b/regression_tests/bifs-command-replace-scene-null.bt @@ -0,0 +1,37 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + objectTypeIndication 1 + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 0 + } + WorldInfo { + info ["This shows scene replacement" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "scene replace test" + } + ] +} + + +AT 2000 { + REPLACE SCENE BY NULL + +} + diff --git a/regression_tests/bifs-command-replace-scene.bt b/regression_tests/bifs-command-replace-scene.bt new file mode 100644 index 0000000..7208b0e --- /dev/null +++ b/regression_tests/bifs-command-replace-scene.bt @@ -0,0 +1,76 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 0 + } + WorldInfo { + info [ + "This test shows scene replacement using BIFS commands." + "At initialization, the scene is empty, only a yellow background." + "At 2s, the whole scene is replaced by a new one containing a red polygon." + "All resources belonging to the previous scene are reclaimed." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.3 $" + "(C) 2002-2004 GPAC Team"] + title "Scene Replacement" + } + ] +} + + +AT 2000 { + REPLACE SCENE BY OrderedGroup { + children [ + WorldInfo { + info ["This shows scene replacement" "through BIFS commands" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "scene replace test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] + } + +} + diff --git a/regression_tests/bifs-command-route-add-children.bt b/regression_tests/bifs-command-route-add-children.bt new file mode 100644 index 0000000..9de3bfa --- /dev/null +++ b/regression_tests/bifs-command-route-add-children.bt @@ -0,0 +1,79 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +DEF OG OrderedGroup { + children [ + WorldInfo { + info ["This shows node routing" "through addChildren field" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Node Routing test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR1 Transform2D { + translation -100 0 + } + DEF TR2 Transform2D { + translation 100 0 + } + ] +} + +ROUTE TR1.children TO TR2.addChildren + +AT 2000 { + APPEND TO TR1.children Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 100 50 + } + } +} + +AT 4000 { + REPLACE M1.filled BY FALSE +} + +AT 6000 { + REPLACE TR1.children[0] BY NULL +} + +AT 8000 { + APPEND TO TR1.children Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } +} + diff --git a/regression_tests/bifs-command-route-children.bt b/regression_tests/bifs-command-route-children.bt new file mode 100644 index 0000000..a29b410 --- /dev/null +++ b/regression_tests/bifs-command-route-children.bt @@ -0,0 +1,79 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 300 + } + } + } + ] +} + +DEF OG OrderedGroup { + children [ + WorldInfo { + info ["This shows node routing" "through children field" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Node duplicate test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR1 Transform2D { + translation -100 0 + } + DEF TR2 Transform2D { + translation 100 0 + } + ] +} + +ROUTE TR1.children TO TR2.children + +AT 2000 { + APPEND TO TR1.children Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 100 50 + } + } +} + +AT 4000 { + REPLACE M1.filled BY FALSE +} + +AT 6000 { + REPLACE TR1.children[0] BY NULL +} + +AT 8000 { + APPEND TO TR1.children Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 25 + } + } +} + diff --git a/regression_tests/bifs-command-route-node-exposedfield.bt b/regression_tests/bifs-command-route-node-exposedfield.bt new file mode 100644 index 0000000..e08426c --- /dev/null +++ b/regression_tests/bifs-command-route-node-exposedfield.bt @@ -0,0 +1,80 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +DEF OG OrderedGroup { + children [ + WorldInfo { + info ["This shows exposed field routing" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Field Routing Test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + Transform2D { + translation -100 0 + children [ + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 100 50 + } + } + ] + } + Transform2D { + translation 100 0 + children [ + Shape { + appearance Appearance { + material DEF M2 Material2D { + emissiveColor 1 1 0 + } + } + geometry Circle { + radius 50 + } + } + ] + } + ] +} + +ROUTE M1.filled TO M2.filled + +AT 2000 { + REPLACE M1.filled BY TRUE +} + +AT 4000 { + REPLACE M1.filled BY FALSE +} + +AT 6000 { + REPLACE M2.filled BY TRUE +} + diff --git a/regression_tests/bifs-command-route-node.bt b/regression_tests/bifs-command-route-node.bt new file mode 100644 index 0000000..1d217f3 --- /dev/null +++ b/regression_tests/bifs-command-route-node.bt @@ -0,0 +1,76 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +DEF OG OrderedGroup { + children [ + WorldInfo { + info ["This shows node routing" "through exposed fields" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Node routing test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + Transform2D { + translation -100 0 + children [ + Shape { + appearance DEF APP1 Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 100 50 + } + } + ] + } + Transform2D { + translation 100 0 + children [ + Shape { + appearance DEF APP2 Appearance { + material Material2D { + emissiveColor 1 1 0 + filled TRUE + } + } + geometry Circle { + radius 50 + } + } + ] + } + ] +} + +ROUTE APP1.material TO APP2.material + +AT 2000 { + REPLACE APP1.material BY Material2D { + emissiveColor 1 0 1 + filled TRUE + } +} + diff --git a/regression_tests/bifs-command-route-remove-children.bt b/regression_tests/bifs-command-route-remove-children.bt new file mode 100644 index 0000000..03384ea --- /dev/null +++ b/regression_tests/bifs-command-route-remove-children.bt @@ -0,0 +1,93 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +DEF OG OrderedGroup { + children [ + WorldInfo { + info ["This shows node deletion" "through removeChildren field" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Node delete test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR1 Transform2D { + translation -100 0 + } + DEF TR2 Transform2D { + translation 100 0 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 0 + filled TRUE + } + } + geometry Circle { + radius 50 + } + } + ] + } + ] +} + +DEF R1 ROUTE TR1.children TO TR2.addChildren + +AT 2000 { + APPEND TO TR1.children Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 100 50 + } + } +} + +AT 3000 { + DELETE ROUTE R1 + INSERT ROUTE TR1.children TO TR2.removeChildren +} + +AT 4000 { + APPEND TO TR1.children Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 1 + filled TRUE + } + } + geometry Circle { + radius 20 + } + } +} + +AT 100000 { + DELETE ROUTE R1 +} + diff --git a/regression_tests/bifs-externproto-forestgump-lib.bt b/regression_tests/bifs-externproto-forestgump-lib.bt new file mode 100644 index 0000000..80fbde8 --- /dev/null +++ b/regression_tests/bifs-externproto-forestgump-lib.bt @@ -0,0 +1,463 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO FORESTGUMP [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFFloat lineWidth 3 + exposedField SFColor lineColor 0.121569 0.101961 0.0901961 + exposedField SFTime runTime 1 + exposedField SFBool loop TRUE + exposedField SFTime start 0 +] { + Transform2D { + rotationAngle IS rotation + scale IS scale + translation IS translation + children [ + DEF MYSWITCH Switch { + whichChoice 0 + choice [ + Transform2D { + scale 0.0899561 0.0900365 + translation 260.243 42.9024 + children [ + Transform2D { + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + geometry Curve2D { + type [1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-2827 -11 -2864 -2 -2864 -2 -2883 -2 -2883 -2 -2892 -2 -2901 -11 -2911 -11 -2920 -11 -2929 -20 -2939 -29 -2939 -29 -2957 -48 -2957 -48 -2966 -57 -2957 -57 -2966 -76 -2966 -85 -2976 -95 -2976 -104 -2976 -104 -2976 -132 -2976 -132 -2976 -132 -2976 -160 -2976 -160 -2966 -178 -2957 -188 -2957 -188 -2957 -188 -2939 -197 -2939 -197 -2939 -197 -2920 -197 -2920 -197 -2920 -197 -2911 -206 -2911 -206 -2911 -206 -2892 -206 -2892 -206 -2892 -206 -2873 -206 -2873 -206 -2873 -206 -2855 -215 -2855 -215 -2855 -215 -2836 -215 -2836 -215 -2836 -215 -2827 -206 -2827 -206 -2827 -206 -2808 -197 -2808 -197 -2799 -188 -2799 -188 -2790 -178 -2790 -178 -2790 -169 -2790 -169 -2780 -160 -2780 -160 -2771 -150 -2771 -150 -2771 -141 -2771 -141 -2771 -132 -2771 -132 -2771 -122 -2771 -122 -2771 -113 -2771 -113 -2771 -104 -2771 -104 -2771 -95 -2771 -95 -2771 -85 -2771 -85 -2771 -76 -2780 -57 -2780 -48 -2780 -48 -2790 -29 -2790 -29 -2790 -29 -2808 -20 -2808 -20 -2808 -20 -2818 -11 -2818 -11 -2827 -2 -2827 -2 -2836 -2 -2836 -2 -2846 -2 -2855 -2 -2873 -2 -2883 -2] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [1 2 2 1 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-2883 -225 -2883 -262 -2883 -262 -2883 -281 -2883 -281 -2883 -290 -2883 -299 -2883 -308 -2883 -327 -2883 -327 -2892 -336 -2892 -336 -2892 -336 -2892 -383 -2892 -392 -2892 -401 -2892 -429 -2892 -439 -2892 -439 -2892 -467 -2892 -467 -2892 -467 -2892 -485 -2892 -485 -2901 -485 -2901 -485 -2901 -494 -2901 -494 -2901 -504 -2901 -513 -2901 -532 -2901 -541] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-3004 -755 -2994 -727 -2985 -708 -2976 -690 -2976 -680 -2976 -643 -2976 -643 -2957 -625 -3004 -653 -3004 -653 -3004 -653 -3115 -718 -3115 -708 -3115 -662 -3032 -541 -2957 -541 -2939 -541 -2901 -522 -2883 -541 -2883 -541 -2855 -560 -2855 -560 -2846 -569 -2818 -587 -2818 -606 -2818 -625 -2790 -662 -2790 -708 -2790 -773 -2799 -848 -2799 -913 -2799 -931 -2808 -931 -2808 -950 -2808 -959 -2790 -931 -2790 -931 -2780 -904 -2743 -857 -2734 -829 -2734 -820 -2697 -773 -2706 -773] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-2985 -346 -2994 -346 -3022 -374 -3032 -374 -3041 -374 -3050 -383 -3050 -392 -3050 -392 -3134 -392 -3143 -392 -3152 -392 -3134 -364 -3134 -364 -3134 -364 -3106 -346 -3106 -346 -3097 -336 -2985 -253 -2957 -253 -2939 -253 -2920 -271 -2911 -271 -2901 -271 -2873 -281 -2864 -281 -2836 -271 -2790 -234 -2771 -225 -2762 -225 -2734 -206 -2725 -197 -2715 -188 -2669 -160 -2669 -160 -2660 -160 -2660 -150 -2660 -169 -2660 -206 -2641 -262 -2641 -299] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0900093 0.0899546 + translation 190.415 42.8184 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [1 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-2083 -20 -2120 -20 -2120 -20 -2130 -20 -2130 -20 -2139 -20 -2139 -20 -2148 -20 -2148 -20 -2148 -20 -2167 -20 -2167 -20 -2167 -20 -2195 -30 -2195 -30 -2195 -30 -2204 -48 -2213 -58 -2213 -58 -2232 -67 -2241 -76 -2241 -76 -2241 -113 -2241 -113 -2241 -113 -2250 -132 -2250 -141 -2250 -151 -2241 -169 -2241 -169 -2241 -179 -2223 -188 -2223 -188 -2223 -188 -2213 -216 -2204 -216 -2195 -216 -2195 -225 -2185 -225 -2185 -225 -2157 -225 -2157 -225 -2157 -225 -2139 -225 -2139 -225 -2139 -225 -2120 -225 -2120 -225 -2120 -225 -2111 -216 -2111 -216 -2111 -216 -2092 -216 -2092 -216 -2092 -216 -2074 -206 -2074 -206 -2074 -206 -2064 -197 -2064 -197 -2064 -197 -2055 -179 -2055 -179 -2046 -179 -2046 -179 -2046 -169 -2046 -169 -2037 -160 -2037 -160 -2037 -151 -2037 -151 -2037 -141 -2037 -141 -2037 -132 -2037 -132 -2037 -123 -2037 -123 -2037 -113 -2037 -113 -2037 -104 -2037 -104 -2037 -95 -2037 -95 -2037 -95 -2037 -76 -2037 -76 -2037 -76 -2055 -39 -2055 -39 -2055 -39 -2074 -30 -2074 -30 -2074 -30 -2092 -20 -2092 -20 -2092 -20 -2102 -11 -2102 -11 -2111 -11 -2111 -11 -2120 -11 -2120 -11 -2120 -2 -2120 -2 -2157 -2 -2157 -2] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [1 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-2130 -244 -2130 -290 -2130 -318 -2130 -355 -2130 -383 -2130 -402 -2139 -420 -2139 -439 -2139 -448 -2139 -476 -2139 -485 -2139 -495 -2139 -504 -2139 -513 -2139 -523 -2139 -523 -2139 -532 -2139 -532 -2139 -541 -2139 -541 -2130 -551 -2130 -551] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-2306 -792 -2269 -764 -2232 -718 -2204 -690 -2195 -681 -2204 -690 -2213 -690 -2241 -681 -2325 -671 -2343 -653 -2343 -653 -2362 -644 -2362 -644 -2371 -634 -2278 -569 -2278 -569 -2241 -560 -2213 -551 -2176 -551 -2139 -551 -2027 -578 -2027 -616 -2027 -625 -2018 -625 -2018 -634 -1999 -671 -1999 -709 -1999 -755 -1999 -811 -2074 -904 -2111 -922 -2148 -941 -2139 -950 -2102 -950 -2064 -950 -2009 -950 -1971 -950] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-2223 -402 -2260 -420 -2325 -439 -2353 -411 -2371 -392 -2381 -374 -2381 -355 -2381 -318 -2371 -290 -2343 -281 -2306 -262 -2232 -244 -2185 -244 -2176 -244 -2139 -225 -2130 -225 -2120 -225 -2046 -234 -2027 -234 -1981 -234 -1897 -253 -1878 -299 -1869 -318 -1851 -346 -1851 -383 -1851 -411 -1860 -448 -1860 -467] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0899291 0.0900198 + translation 123.698 44.4248 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [1 2 1 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-1283 -11 -1320 -11 -1320 -11 -1330 -11 -1330 -11 -1339 -11 -1339 -11 -1348 -11 -1348 -11 -1348 -11 -1367 -11 -1367 -11 -1367 -20 -1367 -20 -1376 -20 -1376 -20 -1376 -20 -1413 -39 -1413 -39 -1413 -39 -1432 -76 -1432 -76 -1432 -85 -1441 -104 -1441 -113 -1441 -123 -1451 -141 -1451 -151 -1451 -169 -1451 -160 -1441 -169 -1441 -169 -1432 -188 -1432 -188 -1432 -188 -1423 -206 -1423 -206 -1423 -206 -1404 -216 -1404 -216 -1395 -216 -1386 -225 -1376 -225 -1376 -225 -1348 -225 -1348 -225 -1348 -225 -1330 -225 -1330 -225 -1330 -225 -1311 -225 -1311 -225 -1311 -225 -1293 -216 -1293 -216 -1293 -216 -1283 -206 -1283 -206 -1283 -206 -1265 -197 -1265 -197 -1265 -197 -1255 -188 -1255 -188 -1255 -188 -1246 -169 -1246 -169 -1237 -169 -1237 -169 -1237 -160 -1237 -160 -1237 -151 -1237 -151 -1237 -141 -1237 -141 -1237 -132 -1237 -132 -1237 -123 -1237 -123 -1237 -113 -1237 -113 -1237 -104 -1237 -104 -1237 -95 -1237 -95 -1237 -85 -1237 -85 -1237 -85 -1237 -67 -1237 -67 -1237 -67 -1255 -48 -1255 -39 -1255 -39 -1265 -20 -1265 -20 -1265 -20 -1283 -11 -1283 -11 -1283 -11 -1302 -2 -1302 -2 -1311 -2 -1311 -2 -1320 -2 -1320 -2 -1330 -2 -1339 -2 -1358 -2 -1367 -2] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [1 2 1 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-1311 -234 -1311 -271 -1311 -271 -1311 -281 -1311 -281 -1311 -299 -1311 -318 -1311 -355 -1311 -374 -1311 -392 -1311 -430 -1311 -448 -1311 -457 -1311 -485 -1311 -495 -1311 -495 -1311 -513 -1311 -513 -1311 -523 -1311 -523 -1311 -532 -1311 -532 -1311 -541 -1311 -560 -1311 -578 -1311 -560] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-1572 -662 -1534 -709 -1469 -755 -1441 -783 -1423 -802 -1451 -764 -1451 -764 -1460 -736 -1506 -578 -1469 -560 -1451 -550 -1330 -597 -1320 -597 -1293 -606 -1274 -616 -1274 -662 -1274 -690 -1274 -727 -1283 -746 -1283 -755 -1293 -792 -1302 -811 -1339 -885 -1274 -904 -1274 -987] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2] + point Coordinate2D { + point [-1609 -541 -1618 -467 -1599 -504 -1590 -448 -1562 -309 -1423 -253 -1283 -253 -1237 -253 -1144 -281 -1144 -327 -1144 -346 -1135 -355 -1144 -374 -1153 -392 -1227 -467 -1227 -485] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0899947 0.0899257 + translation 62.0964 37.7238 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 1 2 1 2 1] + point Coordinate2D { + point [-511 -30 -549 -30 -567 -30 -577 -30 -586 -30 -604 -48 -604 -48 -614 -48 -623 -67 -623 -76 -623 -86 -642 -76 -642 -95 -642 -95 -642 -123 -642 -123 -642 -132 -642 -141 -642 -151 -642 -160 -623 -179 -623 -179 -623 -179 -614 -188 -614 -188 -614 -188 -595 -188 -595 -188 -595 -188 -586 -197 -586 -197 -586 -197 -567 -197 -567 -197 -567 -197 -549 -207 -549 -207 -549 -207 -539 -197 -539 -197 -539 -197 -521 -197 -521 -197 -521 -197 -502 -188 -502 -188 -493 -179 -493 -179 -484 -169 -484 -169 -474 -169 -474 -169 -474 -160 -474 -160 -465 -160 -465 -160 -465 -151 -465 -151 -465 -141 -465 -141 -465 -132 -465 -132 -456 -132 -456 -132 -456 -114 -456 -114 -456 -86 -465 -86 -465 -86 -465 -86 -474 -67 -474 -58 -474 -48 -484 -48 -484 -39 -484 -39 -493 -21 -493 -21 -502 -21 -502 -21 -511 -11 -511 -11 -521 -2 -521 -2 -558 -2 -511 -30 -521 -2] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 1 2 2] + point Coordinate2D { + point [-539 -225 -539 -262 -549 -281 -549 -318 -549 -337 -558 -355 -558 -374 -558 -383 -558 -411 -558 -430 -558 -439 -558 -448 -558 -467 -558 -476 -558 -504 -567 -513 -558 -523] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 1 2 2 2 2 2] + point Coordinate2D { + point [-865 -551 -865 -569 -846 -653 -846 -672 -846 -690 -837 -699 -837 -709 -837 -718 -828 -718 -828 -709 -828 -681 -763 -606 -735 -588 -716 -579 -651 -541 -632 -541 -577 -541 -521 -532 -465 -560 -446 -569 -400 -616 -381 -634 -381 -634 -353 -699 -372 -699 -400 -699 -428 -672 -428 -672 -437 -672 -437 -672 -474 -653 -474 -653 -484 -653 -493 -662 -493 -672 -493 -690 -502 -755 -502 -774 -502 -783 -502 -792 -493 -811 -484 -830 -474 -848 -474 -830] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2] + point Coordinate2D { + point [-1023 -365 -976 -318 -930 -300 -874 -272 -865 -272 -837 -253 -828 -253 -716 -216 -577 -262 -456 -262 -428 -262 -335 -318 -363 -346 -372 -355 -437 -346 -456 -346 -456 -346 -511 -337 -511 -337 -530 -337 -539 -318 -549 -318] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0899962 0.0899977 + translation -16.1993 42.7489 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [279 -20 260 -20 232 -1 223 -1 214 -1 205 -20 195 -20 186 -20 167 -29 167 -39 167 -48 149 -66 149 -76 139 -94 139 -94 139 -113 139 -132 130 -141 130 -159 130 -169 139 -178 139 -187 139 -187 149 -206 149 -206 149 -206 158 -225 158 -225 158 -225 177 -234 177 -234 177 -234 195 -234 195 -234 195 -234 214 -234 214 -234 214 -234 232 -234 232 -234 242 -234 251 -225 260 -225 270 -225 270 -225 279 -215 279 -215 288 -215 288 -215 298 -206 298 -206 307 -197 307 -197 316 -187 316 -187 316 -178 316 -178 325 -169 325 -169 335 -159 335 -159 335 -141 335 -141 335 -132 335 -122 335 -113 335 -94 325 -85 325 -76 325 -76 325 -48 325 -48 325 -48 316 -29 316 -29 316 -29 298 -20 298 -20 288 -11 288 -11 279 -11 279 -11 270 -11 260 -11 232 -11 223 -11] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 1 2 1 2] + point Coordinate2D { + point [232 -252 232 -280 214 -327 214 -345 214 -364 205 -411 205 -429 205 -438 205 -466 205 -476 205 -494 205 -494 205 -513 205 -513 195 -531 195 -531 195 -569 195 -569] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 1 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [56 -848 65 -857 130 -913 149 -931 158 -941 186 -968 149 -913 149 -913 130 -866 130 -773 130 -717 130 -680 112 -662 112 -624 112 -578 139 -522 195 -522 260 -522 409 -531 409 -615 409 -615 428 -634 400 -634 372 -634 298 -615 279 -624 260 -634 279 -736 279 -755] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-56 -327 -46 -290 -37 -243 -28 -197 -28 -187 0 -159 9 -141 9 -132 19 -141 19 -150 28 -169 112 -243 149 -243 167 -243 214 -262 251 -262 279 -262 298 -252 316 -271 325 -280 353 -299 353 -299 353 -299 381 -327 391 -327 446 -355 325 -373 316 -373 279 -383 205 -373 177 -345] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0900707 0.0899924 + translation -85.7923 41.9814 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [1060 -20 1060 -20 1014 -2 1004 -2 995 -2 986 -11 976 -11 976 -11 949 -20 949 -20 939 -20 921 -48 921 -58 921 -58 902 -86 902 -86 902 -86 902 -104 902 -104 902 -113 902 -123 902 -132 902 -132 911 -151 911 -151 911 -151 921 -169 921 -169 921 -169 939 -188 939 -188 939 -188 949 -197 949 -197 949 -197 967 -206 967 -206 967 -206 986 -206 995 -206 1004 -206 1004 -206 1014 -206 1014 -206 1023 -206 1023 -206 1032 -206 1032 -206 1051 -197 1051 -197 1060 -197 1060 -197 1069 -188 1069 -188 1079 -179 1079 -179 1079 -169 1079 -169 1088 -169 1088 -169 1088 -169 1088 -151 1088 -151 1088 -141 1097 -123 1097 -104 1097 -86 1097 -86 1088 -67 1088 -67 1079 -48 1079 -48 1079 -48 1069 -39 1069 -39 1060 -30 1060 -30 1051 -30 1051 -30 1042 -20 1032 -20 1014 -11 1004 -11] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 1 2 1 2] + point Coordinate2D { + point [995 -225 995 -253 1014 -318 1014 -346 1014 -365 1014 -411 1014 -430 1014 -439 1014 -467 1014 -476 1014 -485 1014 -485 1014 -504 1014 -504 1014 -513 1014 -523 1014 -541 1023 -541] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 1 2 2 2 2 2 2] + point Coordinate2D { + point [809 -932 846 -932 893 -913 921 -913 958 -913 930 -895 930 -895 921 -895 893 -895 865 -774 865 -764 837 -699 921 -569 976 -541 1014 -523 1088 -578 1097 -588 1107 -597 1144 -625 1153 -634 1209 -690 1069 -644 1051 -662 1032 -681 1116 -792 1153 -792] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2] + point Coordinate2D { + point [688 -532 688 -485 716 -374 753 -355 800 -327 883 -262 939 -262 976 -262 1032 -281 1069 -262 1125 -234 1190 -309 1209 -327 1237 -355 1181 -383 1162 -402 1144 -420 1107 -448 1107 -476] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0899064 0.08998 + translation -164.619 43.1904 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 2 1 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [1878 -20 1851 -11 1841 -1 1813 -1 1804 -1 1804 -11 1795 -11 1785 -11 1767 -39 1767 -48 1758 -57 1739 -66 1739 -85 1739 -85 1739 -113 1739 -113 1739 -122 1748 -132 1748 -141 1748 -141 1758 -159 1758 -169 1758 -169 1767 -187 1767 -187 1767 -187 1776 -206 1776 -206 1776 -206 1785 -215 1785 -215 1785 -215 1804 -225 1804 -225 1804 -225 1823 -225 1823 -225 1823 -225 1841 -225 1841 -225 1841 -225 1860 -215 1860 -215 1869 -215 1869 -215 1878 -206 1878 -206 1888 -197 1888 -197 1897 -187 1897 -187 1906 -178 1906 -178 1916 -169 1916 -169 1916 -159 1916 -159 1925 -150 1925 -150 1925 -150 1925 -132 1925 -132 1925 -122 1925 -122 1934 -113 1934 -113 1934 -113 1934 -85 1925 -76 1925 -76 1906 -66 1906 -57 1906 -48 1906 -39 1897 -29 1897 -29 1878 -20 1878 -20 1869 -20 1869 -20 1860 -11 1860 -11 1851 -11 1841 -11 1823 -1 1804 -1] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 1 2 1 2] + point Coordinate2D { + point [1832 -234 1832 -271 1841 -327 1841 -364 1841 -383 1841 -411 1841 -429 1841 -429 1841 -466 1841 -466 1841 -466 1841 -485 1841 -485 1851 -485 1851 -485 1851 -494 1851 -494 1841 -494 1841 -494 1841 -531 1841 -531] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [1758 -959 1767 -931 1804 -894 1813 -866 1813 -857 1851 -792 1851 -792 1851 -792 1851 -792 1851 -792 1851 -792 1841 -801 1841 -801 1813 -829 1776 -708 1776 -699 1776 -662 1767 -606 1776 -578 1776 -578 1776 -550 1776 -550 1776 -550 1785 -559 1795 -559 1841 -559 1916 -550 1953 -513 1962 -504 1999 -485 1999 -485 1999 -485 2018 -466 2018 -466 2018 -466 2037 -457 2037 -457 2037 -457 2027 -457 2027 -457 2009 -476 1944 -606 1944 -652 1944 -652 1934 -680 1934 -680 1934 -680 1962 -662 1962 -662 1971 -652 2018 -615 2037 -606 2037 -606 2092 -597 2074 -597] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2] + point Coordinate2D { + point [1674 -476 1646 -448 1618 -411 1590 -383 1581 -373 1572 -345 1572 -345 1553 -327 1692 -262 1730 -252 1776 -243 1767 -225 1804 -243 1813 -243 1897 -243 1906 -243 1944 -243 1990 -271 1999 -280 2027 -308 2092 -383 2092 -411] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.089974 0.090028 + translation -247.069 37.0465 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 2 1 2 1 2 1] + point Coordinate2D { + point [2715 -4 2687 -4 2660 -13 2641 -13 2641 -13 2622 -23 2622 -23 2613 -32 2585 -69 2585 -69 2585 -78 2576 -106 2576 -116 2576 -134 2567 -125 2576 -143 2576 -143 2576 -171 2576 -171 2576 -171 2594 -190 2594 -190 2594 -190 2604 -209 2604 -209 2604 -209 2613 -218 2613 -218 2613 -218 2632 -218 2632 -218 2632 -218 2650 -218 2650 -218 2650 -218 2669 -218 2669 -218 2669 -218 2687 -209 2687 -209 2687 -209 2706 -209 2706 -209 2715 -199 2715 -199 2725 -199 2725 -199 2734 -199 2734 -199 2743 -190 2743 -190 2753 -190 2753 -190 2762 -181 2762 -181 2762 -171 2762 -171 2771 -171 2771 -171 2771 -162 2771 -162 2780 -153 2780 -153 2780 -153 2780 -134 2780 -134 2780 -125 2780 -97 2780 -88 2780 -78 2771 -69 2771 -60 2771 -60 2762 -50 2762 -50 2762 -50 2753 -32 2753 -32 2753 -23 2753 -23 2743 -13 2743 -13 2734 -13 2734 -13 2715 5 2715 -4 2734 -13] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 1 2 1 2] + point Coordinate2D { + point [2660 -255 2660 -274 2660 -311 2660 -329 2660 -348 2660 -376 2660 -395 2660 -413 2660 -413 2660 -432 2660 -432 2660 -450 2660 -460 2660 -497 2660 -506] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [2464 -822 2483 -794 2520 -748 2539 -720 2539 -711 2576 -683 2567 -683 2529 -683 2483 -711 2446 -711 2427 -711 2474 -636 2474 -636 2492 -608 2539 -571 2567 -553 2576 -553 2585 -534 2594 -534 2622 -525 2641 -506 2669 -506 2697 -506 2734 -506 2762 -506 2780 -506 2873 -553 2883 -571 2883 -571 2892 -590 2892 -599 2892 -599 2901 -627 2901 -627 2901 -627 2911 -608 2911 -608 2920 -581 2957 -534 2957 -506] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2] + point Coordinate2D { + point [2641 -292 2585 -311 2529 -320 2464 -320 2427 -320 2483 -274 2483 -274 2529 -227 2687 -227 2799 -227 2836 -227 2920 -227 2948 -236 2966 -246 3004 -264 3004 -274 3013 -292 3050 -320 3050 -329] + } + } + } + ] + } + ] + } + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval IS runTime + loop IS loop + startTime IS start + } + DEF VAL Valuator { + Factor1 8 + } + ROUTE TS.fraction_changed TO VAL.inSFFloat + ROUTE VAL.outSFInt32 TO MYSWITCH.whichChoice +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This file is used as a proto library by another file." + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team" + ] + title "ExternProto Library File" + } + Shape { + appearance Appearance { material Material2D { filled TRUE emissiveColor 0 0 0 } } + geometry Text { + string [ + "This file is used as a proto library by another file." + "" + "GPAC Regression Tests" + "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" + "(C) 2002-2004 GPAC Team" + ] + fontStyle FontStyle { + size 25 + justify [ "MIDDLE" "MIDDLE" ] + } + } + } + ] +} + + diff --git a/regression_tests/bifs-externproto-forestgump.bt b/regression_tests/bifs-externproto-forestgump.bt new file mode 100644 index 0000000..20b4798 --- /dev/null +++ b/regression_tests/bifs-externproto-forestgump.bt @@ -0,0 +1,96 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +EXTERNPROTO FORESTGUMP [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFFloat lineWidth 3 + exposedField SFColor lineColor 0.121569 0.101961 0.0901961 + exposedField SFTime runTime 1 + exposedField SFBool loop TRUE + exposedField SFTime start 0 +] ""od:20"" + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["Each animated logo is an instance of a single proto" "defined in an external library" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Complexe proto Test" + } + FORESTGUMP { + translation -75 -75 + scale 1.1 1.1 + rotation 0.75 + runTime 0.5 + } + FORESTGUMP { + translation -100 50 + scale 1 1.5 + runTime 0.75 + start 2 + } + FORESTGUMP { + translation 0 10 + scale 1.8 1.8 + start 4 + } + DEF ANIM FORESTGUMP { + translation 75 -75 + rotation -1.25 + runTime 0.8 + start 6 + } + DEF TIMER TimeSensor { + cycleInterval 2 + loop TRUE + startTime 6 + } + DEF SI ScalarInterpolator { + key [0 1] + keyValue [0 6.283] + } + ] +} + +ROUTE TIMER.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO ANIM.rotation + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 20 + URLstring "bifs-externproto-forestgump-lib.mp4" + } + ] +} + diff --git a/regression_tests/bifs-externproto-mfurl-lib.bt b/regression_tests/bifs-externproto-mfurl-lib.bt new file mode 100644 index 0000000..0045ae7 --- /dev/null +++ b/regression_tests/bifs-externproto-mfurl-lib.bt @@ -0,0 +1,52 @@ + +PROTO testURL [ + exposedField MFString theURL [""] +] { + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF MYTEXT ImageTexture { + url IS theURL + } + } + geometry Bitmap {} + + } + ] + } +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This file is used as a proto library by another file." + "GPAC Regression Tests" + "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" + "(C) 2002-2004 GPAC Team" + ] + title "ExternProto Library File" + } + Shape { + appearance Appearance { material Material2D { filled TRUE emissiveColor 0 0 0 } } + geometry Text { + string [ + "This file is used as a proto library by another file." + "" + "GPAC Regression Tests" + "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" + "(C) 2002-2004 GPAC Team" + ] + fontStyle FontStyle { + size 25 + justify [ "MIDDLE" "MIDDLE" ] + } + } + } + ] +} + + diff --git a/regression_tests/bifs-externproto-mfurl.bt b/regression_tests/bifs-externproto-mfurl.bt new file mode 100644 index 0000000..151bbdc --- /dev/null +++ b/regression_tests/bifs-externproto-mfurl.bt @@ -0,0 +1,69 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +EXTERNPROTO testURL [ + exposedField MFString theURL [""] +] ""od:20"" + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This is a proto with an MF URL ISed field" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "ExternProto with URL Test" + } + DEF testInstance testURL { + theURL ["10"] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 20 + URLstring "bifs-externproto-mfurl-lib.mp4" + } + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 3 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-externproto-nood-lib.bt b/regression_tests/bifs-externproto-nood-lib.bt new file mode 100644 index 0000000..6926dba --- /dev/null +++ b/regression_tests/bifs-externproto-nood-lib.bt @@ -0,0 +1,109 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO GEOMETRY_PROTO [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] { + Transform2D { + translation IS translation + children [ + DEF S Shape { + geometry IS obj + appearance Appearance { + material Material2D { + emissiveColor IS Color + filled IS filled + transparency IS transparency + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + } + Transform2D { + translation -100 0 + children [ + USE S + ] + } + ] + } +} +PROTO GEO_PRO [ + exposedField SFVec2f translation 0 0 + exposedField SFNode obj NULL +] { + Transform2D { + translation IS translation + children [ + DEF S Shape { + geometry IS obj + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + } + ] + } +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This file is used as a proto library by another file." + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team" + ] + title "ExternProto Library File" + } + Shape { + appearance Appearance { material Material2D { filled TRUE emissiveColor 0 0 0 } } + geometry Text { + string [ + "This file is used as a proto library by another file." + "" + "GPAC Regression Tests" + "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" + "(C) 2002-2004 GPAC Team" + ] + fontStyle FontStyle { + size 25 + justify [ "MIDDLE" "MIDDLE" ] + } + } + } + ] +} diff --git a/regression_tests/bifs-externproto-nood.bt b/regression_tests/bifs-externproto-nood.bt new file mode 100644 index 0000000..f5dd172 --- /dev/null +++ b/regression_tests/bifs-externproto-nood.bt @@ -0,0 +1,70 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +EXTERNPROTO EXPR1 [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFColor color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] "bifs-externproto-nood-lib.bt#GEOMETRY_PROTO" + +EXTERNPROTO EXPR2 [ + exposedField SFVec2f translation 0 0 + exposedField SFNode obj NULL +] "bifs-externproto-nood-lib.bt#GEO_PRO" + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of extern proto" "with vrml-like addressing (no MPEG-4 ODs)" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.4 $" "(C) 2002-2004 GPAC Team"] + title "Simple ExternProto Test" + } + EXPR1 { + translation 20 50 + color 0 1 0 + obj DEF C Circle { + radius 75 + } + } + EXPR2 { + translation -50 -50 + obj USE C + } + ] +} + + diff --git a/regression_tests/bifs-externproto-simple-lib.bt b/regression_tests/bifs-externproto-simple-lib.bt new file mode 100644 index 0000000..4b22e75 --- /dev/null +++ b/regression_tests/bifs-externproto-simple-lib.bt @@ -0,0 +1,90 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO GEOMETRY_PROTO [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] { + Transform2D { + translation IS translation + children [ + DEF S Shape { + geometry IS obj + appearance Appearance { + material Material2D { + emissiveColor IS Color + filled IS filled + transparency IS transparency + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + } + Transform2D { + translation -100 0 + children [ + USE S + ] + } + ] + } +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This file is used as a proto library by another file." + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team" + ] + title "ExternProto Library File" + } + Shape { + appearance Appearance { material Material2D { filled TRUE emissiveColor 0 0 0 } } + geometry Text { + string [ + "This file is used as a proto library by another file." + "" + "GPAC Regression Tests" + "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" + "(C) 2002-2004 GPAC Team" + ] + fontStyle FontStyle { + size 25 + justify [ "MIDDLE" "MIDDLE" ] + } + } + } + ] +} diff --git a/regression_tests/bifs-externproto-simple.bt b/regression_tests/bifs-externproto-simple.bt new file mode 100644 index 0000000..1b57e7d --- /dev/null +++ b/regression_tests/bifs-externproto-simple.bt @@ -0,0 +1,81 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +EXTERNPROTO GEOMETRY_PROTO [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] ""od:20"" + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of extern proto with regular addressing by proto ID" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Simple ExternProto Test" + } + DEF G GEOMETRY_PROTO { + translation 200 0 + scale 1 1.5 + rotation 0.78 + Color 1 0 1 + transparency 0.75 + lineColor 1 0 0 + lineWidth 2 + obj Circle { + radius 75 + } + } + Transform2D { + translation -300 0 + children [ + USE G + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 20 + URLstring "bifs-externproto-simple-lib.mp4" + } + ] +} + diff --git a/regression_tests/bifs-game-arrange.bt b/regression_tests/bifs-game-arrange.bt new file mode 100644 index 0000000..3aa3699 --- /dev/null +++ b/regression_tests/bifs-game-arrange.bt @@ -0,0 +1,452 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This file demonstrates how to use ECMAScript to make games in BIFS." + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team" + ] + title "Arrange" + } + Transform2D { + translation 0 150 + children [ + DEF StartTS TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.5 0.5 0.5 + filled TRUE + } + } + geometry Rectangle { + size 100 30 + } + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Text { + string ["Start Game"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + DEF TimeSensorTick TimeSensor { + enabled FALSE + loop TRUE + } + Transform2D { + translation 0 75 + children [ + Shape { + appearance DEF TA Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry DEF TextField Text { + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 22 + } + } + } + ] + } + Switch { + choice [ + DEF Hole Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 1 + filled TRUE + } + } + geometry DEF R Rectangle { + size 50 50 + } + } + ] + } + Transform2D { + translation 0 -50 + children [ + DEF Table Form { + size 150 150 + groups [1 -1 2 -1 3 -1 4 -1 5 -1 6 -1 7 -1 8 -1 9 -1 1 2 3 -1 4 5 6 -1 7 8 9 -1] + constraints ["SHin" "SHin" "SHin" "SVin"] + groupsIndex [1 2 3 -1 4 5 6 -1 7 8 9 -1 10 11 12 -1] + children [ + DEF a_1_1 Switch { + whichChoice 0 + choice [ + Transform2D { + children [ + DEF TS_1_1 TouchSensor {} + Shape { + appearance DEF RA Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry USE R + } + Shape { + appearance USE TA + geometry Text { + string ["1"] + fontStyle USE FS + } + } + ] + } + USE Hole + ] + } + DEF a_1_2 Switch { + whichChoice 0 + choice [ + Transform2D { + children [ + DEF TS_1_2 TouchSensor {} + Shape { + appearance USE RA + geometry USE R + } + Shape { + appearance USE TA + geometry Text { + string ["2"] + fontStyle USE FS + } + } + ] + } + USE Hole + ] + } + DEF a_1_3 Switch { + whichChoice 0 + choice [ + Transform2D { + children [ + DEF TS_1_3 TouchSensor {} + Shape { + appearance USE RA + geometry USE R + } + Shape { + appearance USE TA + geometry Text { + string ["3"] + fontStyle USE FS + } + } + ] + } + USE Hole + ] + } + DEF a_2_1 Switch { + whichChoice 0 + choice [ + Transform2D { + children [ + DEF TS_2_1 TouchSensor {} + Shape { + appearance USE RA + geometry USE R + } + Shape { + appearance USE TA + geometry Text { + string ["4"] + fontStyle USE FS + } + } + ] + } + USE Hole + ] + } + DEF a_2_2 Switch { + whichChoice 0 + choice [ + Transform2D { + children [ + DEF TS_2_2 TouchSensor {} + Shape { + appearance USE RA + geometry USE R + } + Shape { + appearance USE TA + geometry Text { + string ["5"] + fontStyle USE FS + } + } + ] + } + USE Hole + ] + } + DEF a_2_3 Switch { + whichChoice 0 + choice [ + Transform2D { + children [ + DEF TS_2_3 TouchSensor {} + Shape { + appearance USE RA + geometry USE R + } + Shape { + appearance USE TA + geometry Text { + string ["6"] + fontStyle USE FS + } + } + ] + } + USE Hole + ] + } + DEF a_3_1 Switch { + whichChoice 0 + choice [ + Transform2D { + children [ + DEF TS_3_1 TouchSensor {} + Shape { + appearance USE RA + geometry USE R + } + Shape { + appearance USE TA + geometry Text { + string ["7"] + fontStyle USE FS + } + } + ] + } + USE Hole + ] + } + DEF a_3_2 Switch { + whichChoice 0 + choice [ + Transform2D { + children [ + DEF TS_3_2 TouchSensor {} + Shape { + appearance USE RA + geometry USE R + } + Shape { + appearance USE TA + geometry Text { + string ["8"] + fontStyle USE FS + } + } + ] + } + USE Hole + ] + } + DEF a_3_3 Switch { + whichChoice 1 + choice [ + Transform2D { + children [ + DEF TS_3_3 TouchSensor {} + Shape { + appearance USE RA + geometry USE R + } + Shape { + appearance USE TA + geometry Text { + string ["9"] + fontStyle USE FS + } + } + ] + } + USE Hole + ] + } + ] + } + ] + } + DEF SC Script { + eventIn SFVec2f move + eventIn SFBool startGame + eventIn SFTime tickTime + field SFNode txtField USE TextField + field SFNode table USE Table + field SFInt32 gsize 3 + field SFInt32 ghrow 2 + field SFInt32 ghcol 2 + field SFInt32 gtime 0 + field SFInt32 gmoves 0 + field SFInt32 gshuffling 0 + field SFInt32 size 3 + field SFBool isStarted FALSE + eventOut SFBool hasWin + url ["javascript: + function r(low,hi) {return Math.floor((hi-low)*Math.random()+low); } + function r1(hi) {return Math.floor((hi-1)*Math.random()+1); } + function r0(hi) {return Math.floor((hi)*Math.random()); } + function startGame(value) {if (value) {shuffle();gtime = 0;gmoves = 0;isStarted = !isStarted; hasWin = false;}} + function stopGame(){isStarted = !isStarted;} + function tickTime(value){showStatus();gtime++;} + function checkWin(){if (!isStarted) return; if (!isHole(gsize-1,gsize-1)) return;for (i=0;idest;i--) {setValue(row,i,getValue(row,i-1));setHole(row,i-1);}}} + function shiftHoleCol(src,dest,col) {src = parseInt(src);dest = parseInt(dest);if (src < dest) {for (i=src;idest;i--){ setValue(i,col,getValue(i-1,col));setHole(i-1,col);}}} + function moveRowCol(r,c) { if (!isStarted && !gshuffling) { return; }if (isHole(r,c)) {return; }hc = getHoleInRow(r);if (hc != -1) {shiftHoleRow(hc,c,r);gmoves++;checkWin();return;} hr = getHoleInCol(c);if (hr != -1){shiftHoleCol(hr,r,c);gmoves++;checkWin();return;} } + function move(value) {moveRowCol(value.x-1,value.y-1);} + function shuffle() {gshuffling = true;for (i=0;i=40) { + blclr=0; + ballN=3; + for (ib=0;ib<5;ib++){ + for (ia=0;ia<8;ia++){ + chc(ib*8+ia+1,ib); + blsta[ib*8+ia]=ib; + } + } + } + starter.whichChoice=-1; + gameFLG=1; + loadFLG=1; + ballX=209; + ballY=-270; + ballDX=-3; + ballDY=3; + tmpRL=193; + missFLG=0; +} +function set_X(value) { + if (loadFLG==1) { + tmpRL=value.x + 197; + if (tmpRL<21) { + tmpRL=21; + } + if (tmpRL>373) { + tmpRL=373; + } + } +} +function outCHK() { + if (ballX<16) { + ballX=32-ballX; + ballDX=-ballDX; + } + if (ballX>401) { + ballX=802-ballX; + ballDX=-ballDX; + } + if (ballY>-16) { + ballY=-32-ballY; + ballDY=-ballDY; + } + if (ballY<=-272) { + if (missFLG==0) { + tmpX=(ballDX/ballDY)*(-272-ballY)+ballX; + if (tmpX>=tmpRL-12) { + if (tmpX<=tmpRL+42) { + ballY=-272; + ballDY=-ballDY; + ballX=tmpX; + ballRD=tmpX-tmpRL; + ballDX=8*Math.abs(ballDX)/ballDX; + if (ballRD<-4) { + ballDX=-15; + } + if (ballRD>36) { + ballDX=15; + } + if (ballRD>=14) { + if (ballRD<=16) { + ballDX=-2; + } + } + if (ballRD>=17) { + if (ballRD<=20) { + ballDX=2; + } + } + if (ballRD>=0) { + if (ballRD<=4) { + ballDX=-4; + } + } + if (ballRD>=28) { + if (ballRD<=32) { + ballDX=4; + } + } + if (ballRD>=-4) { + if (ballRD<=-1) { + ballDX=-11; + } + } + if (ballRD>=33) { + if (ballRD<=36) { + ballDX=11; + } + } + } + } + if (ballDY<0) { + missFLG=1; + } + } + else { + if (ballY<-290) { + missFLG=0; + ballN=ballN-1; + gameEnd(); + } + } + } +} +function blkCHK() { + tmpY=ballY-4; + tmpY=-tmpY; + tmpX=ballX+4; + if (tmpY>=48) { + if (tmpY<=147) { + if (tmpX>=29) { + if (tmpX<=396) { + ia=Math.floor((tmpX-29)/46); + ib=Math.floor((tmpY-48)/20); + ic=ib*8+ia; + if (blsta[ic]<=4) { + tmpbc=blsta[ic]+1; + blsta[ic]=tmpbc; + chc(ic+1,tmpbc); + if (tmpbc==5) { + blclr=blclr+1; + } + if (blclr>=40) { + gameEnd(); + } + if (ballDX>0) { + iy=(-ballDY/ballDX)*(29+46*ia-tmpX)+tmpY; + if (iy>48+20*ib+18) { + tmpY1=48+20*ib+18; + tmpX1=(ballDX/-ballDY)*(48+20*ib+18-tmpY)+tmpX; + ballX=tmpX1-4; + ballY=-(tmpY1+4); + ballDY=-ballDY; + } + else { + if (iy<44+20*ib) { + tmpY1=48+20*ib; + tmpX1=(ballDX/-ballDY)*(48+20*ib-tmpY)+tmpX; + ballX=tmpX1-4; + ballY=-(tmpY1+4); + ballDY=-ballDY; + } + else { + tmpX1=29+46*ia; + tmpY1=(-ballDY/ballDX)*(29+46*ia-tmpX)+tmpY; + ballX=tmpX1-4; + ballY=-(tmpY1+4); + ballDX=-ballDX; + } + } + } + else { + iy=(-ballDY/ballDX)*(29+46*ia+44-tmpX)+tmpY; + if (iy>48+20*ib+18) { + tmpY1=48+20*ib+18; + tmpX1=(ballDX/-ballDY)*(48+20*ib+18-tmpY)+tmpX; + ballX=tmpX1-4; + ballY=-(tmpY1+4); + ballDY=-ballDY; + } + else { + if (iy<44+20*ib) { + tmpY1=48+20*ib; + tmpX1=(ballDX/-ballDY)*(48+20*ib-tmpY)+tmpX; + ballX=tmpX1-4; + ballY=-(tmpY1+4); + ballDY=-ballDY; + } + else { + tmpX1=29+46*ia+44; + tmpY1=(-ballDY/ballDX)*(29+46*ia+44-tmpX)+tmpY; + ballX=tmpX1-4; + ballY=-(tmpY1+4); + ballDX=-ballDX; + } + } + } + } + } + } + } + } +} +function gameEnd() { + gameFLG=0; + loadFLG=0; + timer.enabled=false; + starter.whichChoice=0; + if (ballN<=0) { + blclr=40; + } +} +function initialize() { + for (ib=0;ib<5;ib++){ + for (ia=0;ia<8;ia++){ + blsta[ib*8+ia]=ib; + } + } +} +function chc(bno,bcl) { + monBlock=table.children[bno-1].children[0].appearance.material; + monBlock.emissiveColor=blcol[bcl]; +} + +" + ] + } + DEF N9 Conditional { + buffer { + REPLACE N0.enabled BY TRUE + } + } + ] +} + +ROUTE N7.isActive TO N8.startGame +ROUTE N7.isActive TO N9.activate +ROUTE N0.cycleTime TO N8.tickTime +ROUTE N1.hitPoint_changed TO N8.set_X + diff --git a/regression_tests/bifs-game-bubble.bt b/regression_tests/bifs-game-bubble.bt new file mode 100644 index 0000000..05f6786 --- /dev/null +++ b/regression_tests/bifs-game-bubble.bt @@ -0,0 +1,433 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + useNames true + } + } + } + ] +} + +PROTO Bubble [ + exposedField SFInt32 index 0 + exposedField SFInt32 state 0 + exposedField SFInt32 display 0 + exposedField SFVec2f position 0 0 + + exposedField SFFloat size 9 + exposedField SFColor color 0 0 0 + exposedField SFFloat red 0 + exposedField SFFloat green 0 + exposedField SFFloat blue 0 + + exposedField SFFloat strokeWidth 2 + exposedField SFFloat strokeTransparency 0.3 + exposedField SFBool alignStroke FALSE + + eventOut SFInt32 isOver + eventOut SFInt32 isActive +] { + Switch { + whichChoice IS display + choice [ + Transform2D { + translation IS position + children [ + ColorTransform { + mrr 0.7 + mgg 0.5 + mbb 0.9 + maa 0.6 + ta 0.274 + tr IS red + tg IS green + tb IS blue + children [ + Shape { + geometry DEF C Circle { radius IS size } + appearance Appearance { + material Material2D { + filled TRUE + } + texture RadialGradient { + center 0.5 0.5 + focalPoint 0.5 0.5 + key [ 0 1 ] + keyValue [ 1 1 1 0 0 0 ] + } + } + } + ] + } + Shape { + geometry USE C + appearance Appearance { + material Material2D { + filled FALSE + lineProps XLineProperties { + width 2 + lineColor 0.2 0.2 0.2 + transparency IS strokeTransparency + isCenterAligned IS alignStroke + } + } + } + } + DEF TS TouchSensor {} + DEF Vindex Valuator { + inSFInt32 IS index + } + DEF VOver Valuator { + outSFInt32 IS isOver + } + DEF VActive Valuator { + outSFInt32 IS isActive + } + ] + } + ] + } + ROUTE Vindex.outSFFloat TO VOver.Factor1 + ROUTE Vindex.outSFFloat TO VActive.Factor1 + ROUTE TS.isOver TO VOver.inSFBool + ROUTE TS.isActive TO VActive.inSFBool +} +OrderedGroup { + children [ + WorldInfo { + title "Classic Bubble Game" + info [ + "This test shows the classic bubble game." + "It features:" + " - a PROTO which defines a single Bubble node. This node takes as input the index of the bubble in the bubbleGrid. The bubble outputs this index on separate events: one when the mouse enters the bubble and one when a click on the bubble happens. This PROTO does not use any scripting for that." + " - a Script node which handles the initialization (colors, size, index) and dynamic positionning of the bubbles. It is also responsible for the game logic." + "The number of of lines and columns in the bubble grid can be easily modified in the Script node." + "" + "GPAC Regression Tests" "$Date: 2008/11/24 14:58:25 $ - $Revision: 1.6 $" + "(C) 2002-2006 GPAC Team" + ] + } + DEF VIEWPORT Viewport { + size 410 300 + fit 1 + } + Background2D { backColor 1 1 1 } + DEF CANVAS_TRANSFORM Transform2D { + children [ + Shape { + geometry DEF CANVAS Rectangle { size 0 0 } + appearance Appearance { + material Material2D { + filled TRUE + emissiveColor 1 1 1 + lineProps XLineProperties { + width 2 + isScalable FALSE + lineColor 0 0 0 + } + } + } + } + DEF BUBBLE_ROOT Transform2D {} + ] + } + DEF TEXT_TRANSFORM Transform2D { + children [ + Shape { + geometry DEF SCORE_TEXT Text { + fontStyle FontStyle { + size 20 + family "SANS" + justify [ "BEGIN" "FIRST" ] + } + } + appearance Appearance { + material Material2D { + filled TRUE + emissiveColor 0 0 0 + } + } + } + ] + } + DEF Vover Valuator {} + DEF Vclick Valuator {} + DEF BUBBLE_SCRIPT Script { + field SFNode score_text USE SCORE_TEXT + field SFNode bubble_root USE BUBBLE_ROOT + + field SFNode canvas USE CANVAS + field SFNode canvas_pos USE CANVAS_TRANSFORM + field SFNode viewport USE VIEWPORT + field SFNode trans_text USE TEXT_TRANSFORM + + field SFNode val_over USE Vover + field SFNode val_click USE Vclick + + eventIn SFInt32 bubbleOnClick + eventIn SFInt32 bubbleMouseOver + + url [ "javascript: + function initialize() { + bubbleGrid = new Array(); + nbLines = 15; + nbCols = 26; + bubble_radius = 9; + score = 0; + neutral_state = 0; + selected_state = 1; + exploded_state = 2; + empty_state = 4; + + for (i=0; i=0) && + isColorEqual(bubbleGrid[line*nbCols+curcol].color,color) && + (bubbleGrid[line*nbCols+curcol].state == neutral_state)) { +// print('propagate before l: '+line+' c: '+curcol); + set_selected(bubbleGrid[line*nbCols+curcol]); + curcol--; + res++; + } + + //after the bubble on the same line + colmin = ++curcol; + curcol = column+1; + while ( (curcol0) { + if (isColorEqual(bubbleGrid[(line-1)*nbCols+k].color, color)&& + (bubbleGrid[(line-1)*nbCols+k].state == neutral_state)) + res = res + propagate(line-1, k, color); + } + if (line0) explode(line-1, column, color); + if (column>0) explode(line, column-1, color); + if (column=0; k--) { + if (k-lg>=0) { + if (bubbleGrid[(k-lg)*nbCols+j].state != empty_state) { + movebubble(k-lg, j, k, j); + } else { + set_empty(bubbleGrid[k*nbCols+j]); + } + } else { + set_empty(bubbleGrid[k*nbCols+j]); + } + } + } + } + } + score += exploded * exploded; + score_text.string[0] = 'Score: '+score; + } + + function movebubble(lsrc, csrc, ldst, cdst) { +// print('Move src l: '+lsrc+' c: '+csrc); +// print('Move dst l: '+ldst+' c: '+cdst); + // move a bubble from the (lsrc, csrc) position to the (ldst, cdst) position + // and let the src empty + bubbleGrid[ldst*nbCols+cdst].color = bubbleGrid[lsrc*nbCols+csrc].color; + bubbleGrid[ldst*nbCols+cdst].red = bubbleGrid[lsrc*nbCols+csrc].red; + bubbleGrid[ldst*nbCols+cdst].green = bubbleGrid[lsrc*nbCols+csrc].green; + bubbleGrid[ldst*nbCols+cdst].blue = bubbleGrid[lsrc*nbCols+csrc].blue; + bubbleGrid[ldst*nbCols+cdst].state = bubbleGrid[lsrc*nbCols+csrc].state; + if (bubbleGrid[ldst*nbCols+cdst].state != empty_state) + set_visible(bubbleGrid[ldst*nbCols+cdst]); + set_empty(bubbleGrid[lsrc*nbCols+csrc]); + } + + function movecolumn(colnumber, delta) { + for (i=0; i=0; k--) movecolumn(k, +1); + } else { + for (k=j+1; k0&&x>0&&mines[y-1][x-1]) count++; + if (y>0&&mines[y-1][x]) count++; + if (y>0&&x0&&mines[y][x-1]) count++; + if (x0&&mines[y+1][x-1]) count++; + if (y=0&&y=0&&x +// All rights reserved. +// +// Redistribution and use in source form, with or without modification, is +// permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer, +// without modification, immediately at the beginning of the file. +// 2. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by Kelly Yancey +// and a reference to the URL http://www.posi.net/software/othello/ +// 3. The name of the author may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// picture numbers +function initialize() { +EMPTY = 0; // empty square +WHITE = 1; // white piece +BLACK = 2; // black piece +WHITETRANS = 3; // white transparent cursor piece +BLACKTRANS = 4; // black transparent cursor piece +NUMBASE = 5; // beginning of numeric pictures + +// size of the pictures used to represent pieces (all pictures must be the same size) +piecewidth = 42; +pieceheight = 42; + +// size of the board +width = 8; +height = 8; + +// player settings +human = BLACK; +showcursor = true; // display cursor showing user where they can go +showflips = false; // display counts showing how many pieces they can flip by putting a piece in a given square + +// AI settings +weightsquares = 1; // whether AI prefers squares that yield higher flip counts +edgesensitive = 1; // whether AI is sensitive to the importance of edge squares + +// some internal global variables +board = new Array(width); +score = new Array(); +turn = BLACK; + + +human = BLACK; +showcursor = true; +showflips = true; +weightsquares = true; +edgesensitive = true; + +InitializeBoard(); + +Play(0); + +} + +function piece(x,y) { +// object definition for 'piece' type + this.imagenum = x * width + y; + this.player = EMPTY; + // how many pieces each player could flip by taking this square + this.flips = new Array(); + this.flips[WHITE] = 0; + this.flips[BLACK] = 0; + // how valuable this location is to each player + this.value = new Array(); + this.value[WHITE] = 0; + this.value[BLACK] = 0; +} + + +function SetPieceImage(x, y, src) { +// routine to set the image associated with the given piece + if(images.children[board[x][y].imagenum].children[1] != src) { + // we explicitely check to see if we are changing the image source to prevent + // unnecessary redrawing of images which do not change + images.children[board[x][y].imagenum].children[1] = src; + } +} + +function InitializeBoard() { +// routine to initialize the state of the game and draw the board + // build the board array + for(x = 0; x < width; x++) + board[x] = new Array (height); + + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + // place initial pieces + if((x == 3 && y == 3) || (x == 4 && y == 4)) player = WHITE; + else if((x == 3 && y == 4) || (x == 4 && y == 3)) player = BLACK; + else player = EMPTY; + board[x][y] = new piece(x,y); + board[x][y].player = player; + } + } + // initialize scores + score[WHITE] = 2; + score[BLACK] = 2; +} + +function ResetBoard() { +// routine to reset all of the pieces on the board + + // reset the board + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + board[x][y].player = EMPTY; + SetPieceImage(x, y, picture[EMPTY]); + } + } + + // reset scores + score[WHITE] = 0; + score[BLACK] = 0; + + // put initial pieces back on the board + RawPutPiece(3, 3, WHITE); + RawPutPiece(4, 4, WHITE); + RawPutPiece(3, 4, BLACK); + RawPutPiece(4, 3, BLACK); + + turn = BLACK; +} + +function NumFlips(x, y, player) { + count = 0; + + for(deltay = -1; deltay <= 1; deltay++) { + for(deltax = -1; deltax <= 1; deltax++) { + for(distance = 1;; distance++) { + posx = x + (distance * deltax); + posy = y + (distance * deltay); + // stop if we go off the board + if(posx < 0 || posx >= width || posy < 0 || posy >= height) + break; + // stop when we reach an empty square + if(board[posx][posy].player == EMPTY) + break; + // only update the flip count when we reach another of the + // player's pieces + if(board[posx][posy].player == player) { + count += distance - 1; + break; + } + } + } + } + return(count); +} + +function CalcFlipCounts() { + + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + board[x][y].flips[WHITE] = 0; + board[x][y].flips[BLACK] = 0; + + if(board[x][y].player != EMPTY) continue; + + board[x][y].flips[WHITE] = NumFlips(x, y, WHITE); + board[x][y].flips[BLACK] = NumFlips(x, y, BLACK); + } + } +} + +function RawPutPiece(x, y, player) { + other = OtherPlayer(player); + if(board[x][y].player == other) score[other]--; + + board[x][y].player = player; + SetPieceImage(x, y, picture[player]); + score[player]++; +} + +function FlipPieces(x, y, player) { + count = 0; + + // put a piece down at the desired location + RawPutPiece(x, y, player); + + for(deltay = -1; deltay <= 1; deltay++) { + for(deltax = -1; deltax <= 1; deltax++) { + for(distance = 1;; distance++) { + posx = x + (distance * deltax); + posy = y + (distance * deltay); + // stop if we go off the board + if(posx < 0 || posx >= width || posy < 0 || posy >= height) + break; + // stop when we reach an empty square + if(board[posx][posy].player == EMPTY) + break; + if(board[posx][posy].player == player) { + // backtrack, flipping pieces + for(distance--; distance > 0; distance--) { + posx = x + (distance * deltax); + posy = y + (distance * deltay); + RawPutPiece(posx, posy, player); + } + break; + } + } + } + } + return(count); +} + +function AnyMoves(player) { + + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + if(board[x][y].player != EMPTY) continue; + if(NumFlips(x, y, player) > 0) return(true); + } + } + return(false); +} + +function CanPutPiece(x, y, player) { +// determine whether the player can put a piece at the given position + if(turn != player) + return(false); + if(board[x][y].player != EMPTY) + return(false); + return(NumFlips(x, y, player) > 0); +} + +function CheckPutPiece(x, y) { + if(! showcursor) return; + if(! CanPutPiece(x, y, human)) return; + if(human == WHITE) over = WHITETRANS; + else over = BLACKTRANS; + + SetPieceImage(x, y, picture[over]); +} + +function RestorePiece(x, y) { + if(showflips && RawShowFlipCount(x, y, human)) return; + SetPieceImage(x, y, picture[board[x][y].player]); +} + +function PutPiece(x, y, timestamp) { + if(! CanPutPiece(x, y, human)) return; + + FlipPieces(x, y, human); + DoneTurn(timestamp); +} + +function RawShowFlipCount(x, y, player) { + if(board[x][y].player != EMPTY) return(false); + if((flips = board[x][y].flips[player]) == 0) return(false); + SetPieceImage(x, y, picture[NUMBASE + flips]); + return(true); +} + +function ShowFlipCounts(player) { + + CalcFlipCounts(); + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + RawShowFlipCount(x, y, player); + } + } +} + +function HideFlipCounts() { + + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + if(board[x][y].player == EMPTY) + SetPieceImage(x, y, picture[EMPTY]); + } + } +} + +function OtherPlayer(player) { + return((player == WHITE)? BLACK: WHITE); +} + +function DoneTurn(timestamp) { + moves = AnyMoves(turn); + turn = OtherPlayer(turn); + + // check whether the new player has any moves + if(! AnyMoves(turn)) { + if(! moves) return(GameOver()); + + // XXX inform user the player has no move + // and switch players again + turn = OtherPlayer(turn); + } + + // add a slight delay before the computer takes it's turn so game feels + // 'warmer'; when computer is playing itself, add larger delay so user + // can watch + if(turn != human) { + HideFlipCounts(); + timer.cycleInterval = ((human == EMPTY)? 1: 0.150); + timer.startTime = timestamp; + timer.enabled = true; + } else if(showflips) ShowFlipCounts(human); +} + +function Rate(x, y, player) { + + if(board[x][y].player != EMPTY) return(0); + if(x < 0 || x >= width || y < 0 || y >= height) return(0); + + rating = board[x][y].flips[player]; + + if(! weightsquares) + rating = (rating > 0)? 1: 0; + + if(edgesensitive && rating > 0) { + // increase all non-zero weightings by 3 so we have room + // to wait 'less ideal' squares below the baseline + rating += 10; + + // raise edge ratings 4 points, corners are raised 8 + if(x == 0 || x == width - 1) rating += 4; + if(y == 0 || y == height - 1) rating += 4; + // lower next-to-edge ratings by 5 points, next-to-corner by 10 + if(x == 1 || x == width - 2) rating -= 5; + if(y == 1 || y == height - 2) rating -= 5; + + // we cannot rule out a move because of bad location; we must + // always go somewhere + if(rating < 1) rating = 1; + } + return(rating); +} + +function OthelloAI(player, timestamp) { + best = 0; + numbest = 0; + + // rank each position on the board by the potential flip count + CalcFlipCounts(); + + // apply AI rating algorithm + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + rating = Rate(x, y, player); + + // store the rating back into the board + board[x][y].value[player] = rating; + + if(rating == best) + numbest++; + else if(rating > best) { + best = rating; + numbest = 1; + } + } + } + + while(numbest > 0) { + // pick a square to put our piece + pick = Math.floor(Math.random() * numbest); + count = 0; + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + rating = board[x][y].value[player]; + if(rating == best) { + if(count == pick) { + FlipPieces(x, y, player); + DoneTurn(timestamp); + return; + } + else count++; + } + } + } + } + + // if we make it here, then there was nowhere to go + DoneTurn(timestamp); +} + +function Play(timestamp) { + // black always goes first + if(human != BLACK) + OthelloAI(BLACK,timestamp); + else if(showflips) + ShowFlipCounts(human); +} + +function GameOver() { + turn = EMPTY; +} + +function timeOut(value, timestamp) { + if (!value) { + OthelloAI(turn, timestamp); + } +} + +function startGame(value,timestamp) { + if (value) { + Play(timestamp); + } +} + +function click(value, timestamp) { + PutPiece(value.x, value.y, timestamp); +} + +function mouseOver(value) { + CheckPutPiece(value.x, value.y); +} + +function mouseOut(value) { + RestorePiece(value.x, value.y); +} + + + " + } + ] +} +ROUTE StartTS.isActive TO SC.startGame +ROUTE TimeOutSensor.isActive TO SC.timeOut + +ROUTE TS00.isActive TO C00_Click.activate +ROUTE TS00.isOver TO C00_Over.activate +ROUTE TS00.isOver TO C00_Out.reverseActivate +ROUTE V00_Over.outSFVec2f TO SC.mouseOver +ROUTE V00_Out.outSFVec2f TO SC.mouseOut +ROUTE V00_Click.outSFVec2f TO SC.click +ROUTE TS01.isActive TO C01_Click.activate +ROUTE TS01.isOver TO C01_Over.activate +ROUTE TS01.isOver TO C01_Out.reverseActivate +ROUTE V01_Over.outSFVec2f TO SC.mouseOver +ROUTE V01_Out.outSFVec2f TO SC.mouseOut +ROUTE V01_Click.outSFVec2f TO SC.click +ROUTE TS02.isActive TO C02_Click.activate +ROUTE TS02.isOver TO C02_Over.activate +ROUTE TS02.isOver TO C02_Out.reverseActivate +ROUTE V02_Over.outSFVec2f TO SC.mouseOver +ROUTE V02_Out.outSFVec2f TO SC.mouseOut +ROUTE V02_Click.outSFVec2f TO SC.click +ROUTE TS03.isActive TO C03_Click.activate +ROUTE TS03.isOver TO C03_Over.activate +ROUTE TS03.isOver TO C03_Out.reverseActivate +ROUTE V03_Over.outSFVec2f TO SC.mouseOver +ROUTE V03_Out.outSFVec2f TO SC.mouseOut +ROUTE V03_Click.outSFVec2f TO SC.click +ROUTE TS04.isActive TO C04_Click.activate +ROUTE TS04.isOver TO C04_Over.activate +ROUTE TS04.isOver TO C04_Out.reverseActivate +ROUTE V04_Over.outSFVec2f TO SC.mouseOver +ROUTE V04_Out.outSFVec2f TO SC.mouseOut +ROUTE V04_Click.outSFVec2f TO SC.click +ROUTE TS05.isActive TO C05_Click.activate +ROUTE TS05.isOver TO C05_Over.activate +ROUTE TS05.isOver TO C05_Out.reverseActivate +ROUTE V05_Over.outSFVec2f TO SC.mouseOver +ROUTE V05_Out.outSFVec2f TO SC.mouseOut +ROUTE V05_Click.outSFVec2f TO SC.click +ROUTE TS06.isActive TO C06_Click.activate +ROUTE TS06.isOver TO C06_Over.activate +ROUTE TS06.isOver TO C06_Out.reverseActivate +ROUTE V06_Over.outSFVec2f TO SC.mouseOver +ROUTE V06_Out.outSFVec2f TO SC.mouseOut +ROUTE V06_Click.outSFVec2f TO SC.click +ROUTE TS07.isActive TO C07_Click.activate +ROUTE TS07.isOver TO C07_Over.activate +ROUTE TS07.isOver TO C07_Out.reverseActivate +ROUTE V07_Over.outSFVec2f TO SC.mouseOver +ROUTE V07_Out.outSFVec2f TO SC.mouseOut +ROUTE V07_Click.outSFVec2f TO SC.click +ROUTE TS10.isActive TO C10_Click.activate +ROUTE TS10.isOver TO C10_Over.activate +ROUTE TS10.isOver TO C10_Out.reverseActivate +ROUTE V10_Over.outSFVec2f TO SC.mouseOver +ROUTE V10_Out.outSFVec2f TO SC.mouseOut +ROUTE V10_Click.outSFVec2f TO SC.click +ROUTE TS11.isActive TO C11_Click.activate +ROUTE TS11.isOver TO C11_Over.activate +ROUTE TS11.isOver TO C11_Out.reverseActivate +ROUTE V11_Over.outSFVec2f TO SC.mouseOver +ROUTE V11_Out.outSFVec2f TO SC.mouseOut +ROUTE V11_Click.outSFVec2f TO SC.click +ROUTE TS12.isActive TO C12_Click.activate +ROUTE TS12.isOver TO C12_Over.activate +ROUTE TS12.isOver TO C12_Out.reverseActivate +ROUTE V12_Over.outSFVec2f TO SC.mouseOver +ROUTE V12_Out.outSFVec2f TO SC.mouseOut +ROUTE V12_Click.outSFVec2f TO SC.click +ROUTE TS13.isActive TO C13_Click.activate +ROUTE TS13.isOver TO C13_Over.activate +ROUTE TS13.isOver TO C13_Out.reverseActivate +ROUTE V13_Over.outSFVec2f TO SC.mouseOver +ROUTE V13_Out.outSFVec2f TO SC.mouseOut +ROUTE V13_Click.outSFVec2f TO SC.click +ROUTE TS14.isActive TO C14_Click.activate +ROUTE TS14.isOver TO C14_Over.activate +ROUTE TS14.isOver TO C14_Out.reverseActivate +ROUTE V14_Over.outSFVec2f TO SC.mouseOver +ROUTE V14_Out.outSFVec2f TO SC.mouseOut +ROUTE V14_Click.outSFVec2f TO SC.click +ROUTE TS15.isActive TO C15_Click.activate +ROUTE TS15.isOver TO C15_Over.activate +ROUTE TS15.isOver TO C15_Out.reverseActivate +ROUTE V15_Over.outSFVec2f TO SC.mouseOver +ROUTE V15_Out.outSFVec2f TO SC.mouseOut +ROUTE V15_Click.outSFVec2f TO SC.click +ROUTE TS16.isActive TO C16_Click.activate +ROUTE TS16.isOver TO C16_Over.activate +ROUTE TS16.isOver TO C16_Out.reverseActivate +ROUTE V16_Over.outSFVec2f TO SC.mouseOver +ROUTE V16_Out.outSFVec2f TO SC.mouseOut +ROUTE V16_Click.outSFVec2f TO SC.click +ROUTE TS17.isActive TO C17_Click.activate +ROUTE TS17.isOver TO C17_Over.activate +ROUTE TS17.isOver TO C17_Out.reverseActivate +ROUTE V17_Over.outSFVec2f TO SC.mouseOver +ROUTE V17_Out.outSFVec2f TO SC.mouseOut +ROUTE V17_Click.outSFVec2f TO SC.click +ROUTE TS20.isActive TO C20_Click.activate +ROUTE TS20.isOver TO C20_Over.activate +ROUTE TS20.isOver TO C20_Out.reverseActivate +ROUTE V20_Over.outSFVec2f TO SC.mouseOver +ROUTE V20_Out.outSFVec2f TO SC.mouseOut +ROUTE V20_Click.outSFVec2f TO SC.click +ROUTE TS21.isActive TO C21_Click.activate +ROUTE TS21.isOver TO C21_Over.activate +ROUTE TS21.isOver TO C21_Out.reverseActivate +ROUTE V21_Over.outSFVec2f TO SC.mouseOver +ROUTE V21_Out.outSFVec2f TO SC.mouseOut +ROUTE V21_Click.outSFVec2f TO SC.click +ROUTE TS22.isActive TO C22_Click.activate +ROUTE TS22.isOver TO C22_Over.activate +ROUTE TS22.isOver TO C22_Out.reverseActivate +ROUTE V22_Over.outSFVec2f TO SC.mouseOver +ROUTE V22_Out.outSFVec2f TO SC.mouseOut +ROUTE V22_Click.outSFVec2f TO SC.click +ROUTE TS23.isActive TO C23_Click.activate +ROUTE TS23.isOver TO C23_Over.activate +ROUTE TS23.isOver TO C23_Out.reverseActivate +ROUTE V23_Over.outSFVec2f TO SC.mouseOver +ROUTE V23_Out.outSFVec2f TO SC.mouseOut +ROUTE V23_Click.outSFVec2f TO SC.click +ROUTE TS24.isActive TO C24_Click.activate +ROUTE TS24.isOver TO C24_Over.activate +ROUTE TS24.isOver TO C24_Out.reverseActivate +ROUTE V24_Over.outSFVec2f TO SC.mouseOver +ROUTE V24_Out.outSFVec2f TO SC.mouseOut +ROUTE V24_Click.outSFVec2f TO SC.click +ROUTE TS25.isActive TO C25_Click.activate +ROUTE TS25.isOver TO C25_Over.activate +ROUTE TS25.isOver TO C25_Out.reverseActivate +ROUTE V25_Over.outSFVec2f TO SC.mouseOver +ROUTE V25_Out.outSFVec2f TO SC.mouseOut +ROUTE V25_Click.outSFVec2f TO SC.click +ROUTE TS26.isActive TO C26_Click.activate +ROUTE TS26.isOver TO C26_Over.activate +ROUTE TS26.isOver TO C26_Out.reverseActivate +ROUTE V26_Over.outSFVec2f TO SC.mouseOver +ROUTE V26_Out.outSFVec2f TO SC.mouseOut +ROUTE V26_Click.outSFVec2f TO SC.click +ROUTE TS27.isActive TO C27_Click.activate +ROUTE TS27.isOver TO C27_Over.activate +ROUTE TS27.isOver TO C27_Out.reverseActivate +ROUTE V27_Over.outSFVec2f TO SC.mouseOver +ROUTE V27_Out.outSFVec2f TO SC.mouseOut +ROUTE V27_Click.outSFVec2f TO SC.click +ROUTE TS30.isActive TO C30_Click.activate +ROUTE TS30.isOver TO C30_Over.activate +ROUTE TS30.isOver TO C30_Out.reverseActivate +ROUTE V30_Over.outSFVec2f TO SC.mouseOver +ROUTE V30_Out.outSFVec2f TO SC.mouseOut +ROUTE V30_Click.outSFVec2f TO SC.click +ROUTE TS31.isActive TO C31_Click.activate +ROUTE TS31.isOver TO C31_Over.activate +ROUTE TS31.isOver TO C31_Out.reverseActivate +ROUTE V31_Over.outSFVec2f TO SC.mouseOver +ROUTE V31_Out.outSFVec2f TO SC.mouseOut +ROUTE V31_Click.outSFVec2f TO SC.click +ROUTE TS32.isActive TO C32_Click.activate +ROUTE TS32.isOver TO C32_Over.activate +ROUTE TS32.isOver TO C32_Out.reverseActivate +ROUTE V32_Over.outSFVec2f TO SC.mouseOver +ROUTE V32_Out.outSFVec2f TO SC.mouseOut +ROUTE V32_Click.outSFVec2f TO SC.click +ROUTE TS33.isActive TO C33_Click.activate +ROUTE TS33.isOver TO C33_Over.activate +ROUTE TS33.isOver TO C33_Out.reverseActivate +ROUTE V33_Over.outSFVec2f TO SC.mouseOver +ROUTE V33_Out.outSFVec2f TO SC.mouseOut +ROUTE V33_Click.outSFVec2f TO SC.click +ROUTE TS34.isActive TO C34_Click.activate +ROUTE TS34.isOver TO C34_Over.activate +ROUTE TS34.isOver TO C34_Out.reverseActivate +ROUTE V34_Over.outSFVec2f TO SC.mouseOver +ROUTE V34_Out.outSFVec2f TO SC.mouseOut +ROUTE V34_Click.outSFVec2f TO SC.click +ROUTE TS35.isActive TO C35_Click.activate +ROUTE TS35.isOver TO C35_Over.activate +ROUTE TS35.isOver TO C35_Out.reverseActivate +ROUTE V35_Over.outSFVec2f TO SC.mouseOver +ROUTE V35_Out.outSFVec2f TO SC.mouseOut +ROUTE V35_Click.outSFVec2f TO SC.click +ROUTE TS36.isActive TO C36_Click.activate +ROUTE TS36.isOver TO C36_Over.activate +ROUTE TS36.isOver TO C36_Out.reverseActivate +ROUTE V36_Over.outSFVec2f TO SC.mouseOver +ROUTE V36_Out.outSFVec2f TO SC.mouseOut +ROUTE V36_Click.outSFVec2f TO SC.click +ROUTE TS37.isActive TO C37_Click.activate +ROUTE TS37.isOver TO C37_Over.activate +ROUTE TS37.isOver TO C37_Out.reverseActivate +ROUTE V37_Over.outSFVec2f TO SC.mouseOver +ROUTE V37_Out.outSFVec2f TO SC.mouseOut +ROUTE V37_Click.outSFVec2f TO SC.click +ROUTE TS40.isActive TO C40_Click.activate +ROUTE TS40.isOver TO C40_Over.activate +ROUTE TS40.isOver TO C40_Out.reverseActivate +ROUTE V40_Over.outSFVec2f TO SC.mouseOver +ROUTE V40_Out.outSFVec2f TO SC.mouseOut +ROUTE V40_Click.outSFVec2f TO SC.click +ROUTE TS41.isActive TO C41_Click.activate +ROUTE TS41.isOver TO C41_Over.activate +ROUTE TS41.isOver TO C41_Out.reverseActivate +ROUTE V41_Over.outSFVec2f TO SC.mouseOver +ROUTE V41_Out.outSFVec2f TO SC.mouseOut +ROUTE V41_Click.outSFVec2f TO SC.click +ROUTE TS42.isActive TO C42_Click.activate +ROUTE TS42.isOver TO C42_Over.activate +ROUTE TS42.isOver TO C42_Out.reverseActivate +ROUTE V42_Over.outSFVec2f TO SC.mouseOver +ROUTE V42_Out.outSFVec2f TO SC.mouseOut +ROUTE V42_Click.outSFVec2f TO SC.click +ROUTE TS43.isActive TO C43_Click.activate +ROUTE TS43.isOver TO C43_Over.activate +ROUTE TS43.isOver TO C43_Out.reverseActivate +ROUTE V43_Over.outSFVec2f TO SC.mouseOver +ROUTE V43_Out.outSFVec2f TO SC.mouseOut +ROUTE V43_Click.outSFVec2f TO SC.click +ROUTE TS44.isActive TO C44_Click.activate +ROUTE TS44.isOver TO C44_Over.activate +ROUTE TS44.isOver TO C44_Out.reverseActivate +ROUTE V44_Over.outSFVec2f TO SC.mouseOver +ROUTE V44_Out.outSFVec2f TO SC.mouseOut +ROUTE V44_Click.outSFVec2f TO SC.click +ROUTE TS45.isActive TO C45_Click.activate +ROUTE TS45.isOver TO C45_Over.activate +ROUTE TS45.isOver TO C45_Out.reverseActivate +ROUTE V45_Over.outSFVec2f TO SC.mouseOver +ROUTE V45_Out.outSFVec2f TO SC.mouseOut +ROUTE V45_Click.outSFVec2f TO SC.click +ROUTE TS46.isActive TO C46_Click.activate +ROUTE TS46.isOver TO C46_Over.activate +ROUTE TS46.isOver TO C46_Out.reverseActivate +ROUTE V46_Over.outSFVec2f TO SC.mouseOver +ROUTE V46_Out.outSFVec2f TO SC.mouseOut +ROUTE V46_Click.outSFVec2f TO SC.click +ROUTE TS47.isActive TO C47_Click.activate +ROUTE TS47.isOver TO C47_Over.activate +ROUTE TS47.isOver TO C47_Out.reverseActivate +ROUTE V47_Over.outSFVec2f TO SC.mouseOver +ROUTE V47_Out.outSFVec2f TO SC.mouseOut +ROUTE V47_Click.outSFVec2f TO SC.click +ROUTE TS50.isActive TO C50_Click.activate +ROUTE TS50.isOver TO C50_Over.activate +ROUTE TS50.isOver TO C50_Out.reverseActivate +ROUTE V50_Over.outSFVec2f TO SC.mouseOver +ROUTE V50_Out.outSFVec2f TO SC.mouseOut +ROUTE V50_Click.outSFVec2f TO SC.click +ROUTE TS51.isActive TO C51_Click.activate +ROUTE TS51.isOver TO C51_Over.activate +ROUTE TS51.isOver TO C51_Out.reverseActivate +ROUTE V51_Over.outSFVec2f TO SC.mouseOver +ROUTE V51_Out.outSFVec2f TO SC.mouseOut +ROUTE V51_Click.outSFVec2f TO SC.click +ROUTE TS52.isActive TO C52_Click.activate +ROUTE TS52.isOver TO C52_Over.activate +ROUTE TS52.isOver TO C52_Out.reverseActivate +ROUTE V52_Over.outSFVec2f TO SC.mouseOver +ROUTE V52_Out.outSFVec2f TO SC.mouseOut +ROUTE V52_Click.outSFVec2f TO SC.click +ROUTE TS53.isActive TO C53_Click.activate +ROUTE TS53.isOver TO C53_Over.activate +ROUTE TS53.isOver TO C53_Out.reverseActivate +ROUTE V53_Over.outSFVec2f TO SC.mouseOver +ROUTE V53_Out.outSFVec2f TO SC.mouseOut +ROUTE V53_Click.outSFVec2f TO SC.click +ROUTE TS54.isActive TO C54_Click.activate +ROUTE TS54.isOver TO C54_Over.activate +ROUTE TS54.isOver TO C54_Out.reverseActivate +ROUTE V54_Over.outSFVec2f TO SC.mouseOver +ROUTE V54_Out.outSFVec2f TO SC.mouseOut +ROUTE V54_Click.outSFVec2f TO SC.click +ROUTE TS55.isActive TO C55_Click.activate +ROUTE TS55.isOver TO C55_Over.activate +ROUTE TS55.isOver TO C55_Out.reverseActivate +ROUTE V55_Over.outSFVec2f TO SC.mouseOver +ROUTE V55_Out.outSFVec2f TO SC.mouseOut +ROUTE V55_Click.outSFVec2f TO SC.click +ROUTE TS56.isActive TO C56_Click.activate +ROUTE TS56.isOver TO C56_Over.activate +ROUTE TS56.isOver TO C56_Out.reverseActivate +ROUTE V56_Over.outSFVec2f TO SC.mouseOver +ROUTE V56_Out.outSFVec2f TO SC.mouseOut +ROUTE V56_Click.outSFVec2f TO SC.click +ROUTE TS57.isActive TO C57_Click.activate +ROUTE TS57.isOver TO C57_Over.activate +ROUTE TS57.isOver TO C57_Out.reverseActivate +ROUTE V57_Over.outSFVec2f TO SC.mouseOver +ROUTE V57_Out.outSFVec2f TO SC.mouseOut +ROUTE V57_Click.outSFVec2f TO SC.click +ROUTE TS60.isActive TO C60_Click.activate +ROUTE TS60.isOver TO C60_Over.activate +ROUTE TS60.isOver TO C60_Out.reverseActivate +ROUTE V60_Over.outSFVec2f TO SC.mouseOver +ROUTE V60_Out.outSFVec2f TO SC.mouseOut +ROUTE V60_Click.outSFVec2f TO SC.click +ROUTE TS61.isActive TO C61_Click.activate +ROUTE TS61.isOver TO C61_Over.activate +ROUTE TS61.isOver TO C61_Out.reverseActivate +ROUTE V61_Over.outSFVec2f TO SC.mouseOver +ROUTE V61_Out.outSFVec2f TO SC.mouseOut +ROUTE V61_Click.outSFVec2f TO SC.click +ROUTE TS62.isActive TO C62_Click.activate +ROUTE TS62.isOver TO C62_Over.activate +ROUTE TS62.isOver TO C62_Out.reverseActivate +ROUTE V62_Over.outSFVec2f TO SC.mouseOver +ROUTE V62_Out.outSFVec2f TO SC.mouseOut +ROUTE V62_Click.outSFVec2f TO SC.click +ROUTE TS63.isActive TO C63_Click.activate +ROUTE TS63.isOver TO C63_Over.activate +ROUTE TS63.isOver TO C63_Out.reverseActivate +ROUTE V63_Over.outSFVec2f TO SC.mouseOver +ROUTE V63_Out.outSFVec2f TO SC.mouseOut +ROUTE V63_Click.outSFVec2f TO SC.click +ROUTE TS64.isActive TO C64_Click.activate +ROUTE TS64.isOver TO C64_Over.activate +ROUTE TS64.isOver TO C64_Out.reverseActivate +ROUTE V64_Over.outSFVec2f TO SC.mouseOver +ROUTE V64_Out.outSFVec2f TO SC.mouseOut +ROUTE V64_Click.outSFVec2f TO SC.click +ROUTE TS65.isActive TO C65_Click.activate +ROUTE TS65.isOver TO C65_Over.activate +ROUTE TS65.isOver TO C65_Out.reverseActivate +ROUTE V65_Over.outSFVec2f TO SC.mouseOver +ROUTE V65_Out.outSFVec2f TO SC.mouseOut +ROUTE V65_Click.outSFVec2f TO SC.click +ROUTE TS66.isActive TO C66_Click.activate +ROUTE TS66.isOver TO C66_Over.activate +ROUTE TS66.isOver TO C66_Out.reverseActivate +ROUTE V66_Over.outSFVec2f TO SC.mouseOver +ROUTE V66_Out.outSFVec2f TO SC.mouseOut +ROUTE V66_Click.outSFVec2f TO SC.click +ROUTE TS67.isActive TO C67_Click.activate +ROUTE TS67.isOver TO C67_Over.activate +ROUTE TS67.isOver TO C67_Out.reverseActivate +ROUTE V67_Over.outSFVec2f TO SC.mouseOver +ROUTE V67_Out.outSFVec2f TO SC.mouseOut +ROUTE V67_Click.outSFVec2f TO SC.click +ROUTE TS70.isActive TO C70_Click.activate +ROUTE TS70.isOver TO C70_Over.activate +ROUTE TS70.isOver TO C70_Out.reverseActivate +ROUTE V70_Over.outSFVec2f TO SC.mouseOver +ROUTE V70_Out.outSFVec2f TO SC.mouseOut +ROUTE V70_Click.outSFVec2f TO SC.click +ROUTE TS71.isActive TO C71_Click.activate +ROUTE TS71.isOver TO C71_Over.activate +ROUTE TS71.isOver TO C71_Out.reverseActivate +ROUTE V71_Over.outSFVec2f TO SC.mouseOver +ROUTE V71_Out.outSFVec2f TO SC.mouseOut +ROUTE V71_Click.outSFVec2f TO SC.click +ROUTE TS72.isActive TO C72_Click.activate +ROUTE TS72.isOver TO C72_Over.activate +ROUTE TS72.isOver TO C72_Out.reverseActivate +ROUTE V72_Over.outSFVec2f TO SC.mouseOver +ROUTE V72_Out.outSFVec2f TO SC.mouseOut +ROUTE V72_Click.outSFVec2f TO SC.click +ROUTE TS73.isActive TO C73_Click.activate +ROUTE TS73.isOver TO C73_Over.activate +ROUTE TS73.isOver TO C73_Out.reverseActivate +ROUTE V73_Over.outSFVec2f TO SC.mouseOver +ROUTE V73_Out.outSFVec2f TO SC.mouseOut +ROUTE V73_Click.outSFVec2f TO SC.click +ROUTE TS74.isActive TO C74_Click.activate +ROUTE TS74.isOver TO C74_Over.activate +ROUTE TS74.isOver TO C74_Out.reverseActivate +ROUTE V74_Over.outSFVec2f TO SC.mouseOver +ROUTE V74_Out.outSFVec2f TO SC.mouseOut +ROUTE V74_Click.outSFVec2f TO SC.click +ROUTE TS75.isActive TO C75_Click.activate +ROUTE TS75.isOver TO C75_Over.activate +ROUTE TS75.isOver TO C75_Out.reverseActivate +ROUTE V75_Over.outSFVec2f TO SC.mouseOver +ROUTE V75_Out.outSFVec2f TO SC.mouseOut +ROUTE V75_Click.outSFVec2f TO SC.click +ROUTE TS76.isActive TO C76_Click.activate +ROUTE TS76.isOver TO C76_Over.activate +ROUTE TS76.isOver TO C76_Out.reverseActivate +ROUTE V76_Over.outSFVec2f TO SC.mouseOver +ROUTE V76_Out.outSFVec2f TO SC.mouseOut +ROUTE V76_Click.outSFVec2f TO SC.click +ROUTE TS77.isActive TO C77_Click.activate +ROUTE TS77.isOver TO C77_Over.activate +ROUTE TS77.isOver TO C77_Out.reverseActivate +ROUTE V77_Over.outSFVec2f TO SC.mouseOver +ROUTE V77_Out.outSFVec2f TO SC.mouseOut +ROUTE V77_Click.outSFVec2f TO SC.click diff --git a/regression_tests/bifs-interpolation-colorinterpolator.bt b/regression_tests/bifs-interpolation-colorinterpolator.bt new file mode 100644 index 0000000..9dbc4aa --- /dev/null +++ b/regression_tests/bifs-interpolation-colorinterpolator.bt @@ -0,0 +1,63 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 240 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows color interpolation" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Color Interpolator" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + DEF TS TimeSensor { + loop TRUE + } + DEF SI ColorInterpolator { + key [0 0.5 1] + keyValue [1 0 0 0 1 0 1 0 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +DEF R1 ROUTE SI.value_changed TO MAT.emissiveColor + diff --git a/regression_tests/bifs-interpolation-coordinateinterpolator2D.bt b/regression_tests/bifs-interpolation-coordinateinterpolator2D.bt new file mode 100644 index 0000000..c511964 --- /dev/null +++ b/regression_tests/bifs-interpolation-coordinateinterpolator2D.bt @@ -0,0 +1,64 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows scaling interpolation" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Position Interpolator" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF SI CoordinateInterpolator2D { + key [0 0.25 0.5 0.75 1] + keyValue [-100 0 -50 100 50 100 100 0 50 -100 -50 -100 100 0 -50 100 50 100 -100 0 50 -100 -50 -100 -100 0 -50 100 50 100 100 0 50 -100 -50 -100 -100 0 50 100 -50 100 100 0 -50 -100 50 -100 -100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO COORD.point + diff --git a/regression_tests/bifs-interpolation-positionanimator.bt b/regression_tests/bifs-interpolation-positionanimator.bt new file mode 100644 index 0000000..e8d378c --- /dev/null +++ b/regression_tests/bifs-interpolation-positionanimator.bt @@ -0,0 +1,63 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0x01 + sceneProfileLevelIndication 0x01 + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFE + graphicsProfileLevelIndication 0x01 + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 200 + } + } + } + ] +} + +Group { + children [ + Viewpoint {position 0 0 100 } + WorldInfo { + title "PositionAnimator test" + info ["This shows PositionAnimator using bezier spline interpolation" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + DEF BACK Background2D { backColor 1 1 1} + DEF TR Transform { + scale 0.5 0.5 0.5 + rotation 1 1 1 0.75 + children [ + Shape { + appearance Appearance { material DEF MAT Material { diffuseColor 1 0 0} } + geometry Box {size 50 50 50} + } + ] + } + DEF TS TimeSensor { + cycleInterval 4.0 + loop TRUE + } + DEF V Valuator { + Factor2 0 + Offset2 0.5 + } + DEF SI PositionAnimator { + key [0 0.25 0.5 0.75 1] + + keyValueType 1 + keyValue [-50 0 -50, 0 50 0, 50 0 -50] + keyType 4 + keySpline [0.75 0, 0.25 1] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +DEF R1 ROUTE SI.value_changed TO TR.translation diff --git a/regression_tests/bifs-interpolation-positionanimator2D.bt b/regression_tests/bifs-interpolation-positionanimator2D.bt new file mode 100644 index 0000000..3421b46 --- /dev/null +++ b/regression_tests/bifs-interpolation-positionanimator2D.bt @@ -0,0 +1,65 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0x01 + sceneProfileLevelIndication 0x01 + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFE + graphicsProfileLevelIndication 0x01 + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + title "PositionAnimator2D test" + info ["This shows PositionAnimator2D using bezier spline interpolation" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + DEF BACK Background2D { backColor 1 1 1} + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { material DEF MAT Material2D { emissiveColor 1 0 0 filled TRUE } } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0, -50 100, 50 100, 100 0, 50 -100, -50 -100] + } + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 4.0 + loop TRUE + } + DEF V Valuator { + Factor2 0 + Offset2 0.5 + } + DEF SI PositionAnimator2D { + key [0 0.25 0.5 0.75 1] + + keyValueType 1 + keyValue [-50 0 0 50 50 0] + keyType 4 + keySpline [0.75 0, 0.25 1] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +DEF R1 ROUTE SI.value_changed TO TR.translation diff --git a/regression_tests/bifs-interpolation-positioninterpolator-position.bt b/regression_tests/bifs-interpolation-positioninterpolator-position.bt new file mode 100644 index 0000000..273548a --- /dev/null +++ b/regression_tests/bifs-interpolation-positioninterpolator-position.bt @@ -0,0 +1,94 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + NavigationInfo {type ["ANY" "EXAMINE"]} + WorldInfo { + info ["This shows position interpolation" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "PositionInterpolator" + } + Transform { + children [ + Shape { + appearance Appearance { + texture MovieTexture { + loop TRUE + url [od:10] + } + } + geometry Box { + size 100 100 100 + } + } + ] + } + DEF TR Transform { + children [ + Shape { + appearance Appearance { + material Material {diffuseColor 0 1 0 } + } + geometry Box { + size 100 100 100 + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 10 + loop TRUE + } + DEF SI PositionInterpolator { + key [0 0.25 0.5 0.75 1] + keyValue [100 100 100, -100 100 -100, -100 -100 -100, 100 -100 100, 100 100 100] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO TR.translation + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-interpolation-positioninterpolator-size.bt b/regression_tests/bifs-interpolation-positioninterpolator-size.bt new file mode 100644 index 0000000..a56ed3e --- /dev/null +++ b/regression_tests/bifs-interpolation-positioninterpolator-size.bt @@ -0,0 +1,58 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +Group { + children [ + WorldInfo { + info ["This shows 3D size interpolation" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "PositionInterpolator" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform { + scale 0.5 0.5 0.5 + rotation 1 1 1 0.75 + children [ + Shape { + appearance Appearance { + material DEF MAT Material {diffuseColor 1 0 0 } + } + geometry Box {size 100 100 50} + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF SI PositionInterpolator { + key [0 0.25 0.5 0.75 1] + keyValue [1 1 1, 1 2 0.25, 1 1 1, 2 1 1.75, 1 1 1] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO TR.scale + diff --git a/regression_tests/bifs-interpolation-positioninterpolator2D-position.bt b/regression_tests/bifs-interpolation-positioninterpolator2D-position.bt new file mode 100644 index 0000000..4971f01 --- /dev/null +++ b/regression_tests/bifs-interpolation-positioninterpolator2D-position.bt @@ -0,0 +1,96 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows 2D position interpolation" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "PositionInterpolator2D" + } + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + texture MovieTexture { + loop TRUE + url [od:10] + } + } + geometry Rectangle { + size 100 100 + } + } + ] + } + DEF TR2 Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 10 + loop TRUE + } + DEF SI PositionInterpolator2D { + key [0 0.25 0.5 0.75 1] + keyValue [100 100 -100 100 -100 -100 100 -100 100 100] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO TR2.translation + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-interpolation-positioninterpolator2D-size.bt b/regression_tests/bifs-interpolation-positioninterpolator2D-size.bt new file mode 100644 index 0000000..814650b --- /dev/null +++ b/regression_tests/bifs-interpolation-positioninterpolator2D-size.bt @@ -0,0 +1,64 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows 2D size interpolation" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "PositionInterpolator2D" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + DEF SI PositionInterpolator2D { + key [0 0.25 0.5 0.75 1] + keyValue [1 1 2 1 1 1 1 2 1 1] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO TR.scale + diff --git a/regression_tests/bifs-interpolation-scalaranimator.bt b/regression_tests/bifs-interpolation-scalaranimator.bt new file mode 100644 index 0000000..f1203b5 --- /dev/null +++ b/regression_tests/bifs-interpolation-scalaranimator.bt @@ -0,0 +1,66 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0x01 + sceneProfileLevelIndication 0x01 + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFE + graphicsProfileLevelIndication 0x01 + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + title "ScalarAnimator test" + info ["This shows ScalarAnimator using bezier spline interpolation" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + DEF BACK Background2D { backColor 1 1 1} + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { material DEF MAT Material2D { emissiveColor 1 0 0 filled TRUE } } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0, -50 100, 50 100, 100 0, 50 -100, -50 -100] + } + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 4.0 + loop TRUE + } + DEF V Valuator { + Factor2 0 + Offset2 0.5 + } + DEF SI ScalarAnimator { +# key [0 0.25 0.5 0.75 1] + + keyValueType 2 + keyValue [0.5 0.0 1 0.5] + keyType 2 + keySpline [0.4 0.5, 0.15 0.15] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +DEF R1 ROUTE SI.value_changed TO V.inSFFloat +ROUTE V.outSFVec2f TO TR.scale diff --git a/regression_tests/bifs-interpolation-scalarinterpolator.bt b/regression_tests/bifs-interpolation-scalarinterpolator.bt new file mode 100644 index 0000000..1785d39 --- /dev/null +++ b/regression_tests/bifs-interpolation-scalarinterpolator.bt @@ -0,0 +1,63 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This shows transparency interpolation" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Scalar Interpolator" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + DEF TS TimeSensor { + loop TRUE + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +DEF R1 ROUTE SI.value_changed TO MAT.transparency + diff --git a/regression_tests/bifs-interpolation-timesensor-enabled.bt b/regression_tests/bifs-interpolation-timesensor-enabled.bt new file mode 100644 index 0000000..894a3ed --- /dev/null +++ b/regression_tests/bifs-interpolation-timesensor-enabled.bt @@ -0,0 +1,82 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of timesensor" "with enabling/disabling" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "TimeSensor enabled test" + } + Transform2D { + translation -100 0 + children [ + DEF N3 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 0 + filled TRUE + } + } + geometry Rectangle { + size 80 80 + } + } + ] + } + DEF N2 TimeSensor { + cycleInterval 10 + enabled FALSE + loop TRUE + stopTime -1 + } + DEF N0 PositionInterpolator2D { + key [0 1] + keyValue [100 -100 100 100] + } + DEF N4 Transform2D { + translation 100 -100 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.5 0.5 0.5 + filled TRUE + } + } + geometry Rectangle { + size 80 80 + } + } + ] + } + ] +} + +ROUTE N3.isOver TO N2.enabled +ROUTE N2.fraction_changed TO N0.set_fraction +ROUTE N0.value_changed TO N4.translation + diff --git a/regression_tests/bifs-interpolation-timesensor-starttime_norestart.bt b/regression_tests/bifs-interpolation-timesensor-starttime_norestart.bt new file mode 100644 index 0000000..1ca29b2 --- /dev/null +++ b/regression_tests/bifs-interpolation-timesensor-starttime_norestart.bt @@ -0,0 +1,81 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of timesensor start time" "Animation shall not be restartable until over" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:09 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "TimeSensor startTime test" + } + Transform2D { + translation -100 0 + children [ + DEF N3 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 0 + filled TRUE + } + } + geometry Rectangle { + size 80 80 + } + } + ] + } + DEF N2 TimeSensor { + cycleInterval 2 + startTime 2 + stopTime -1 + } + DEF N0 PositionInterpolator2D { + key [0 1] + keyValue [100 -100 100 100] + } + DEF N4 Transform2D { + translation 100 -100 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.5 0.5 0.5 + filled TRUE + } + } + geometry Rectangle { + size 80 80 + } + } + ] + } + ] +} + +ROUTE N3.touchTime TO N2.startTime +ROUTE N2.fraction_changed TO N0.set_fraction +ROUTE N0.value_changed TO N4.translation + diff --git a/regression_tests/bifs-interpolation-timesensor-starttime_restart.bt b/regression_tests/bifs-interpolation-timesensor-starttime_restart.bt new file mode 100644 index 0000000..ff0912f --- /dev/null +++ b/regression_tests/bifs-interpolation-timesensor-starttime_restart.bt @@ -0,0 +1,82 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of timesensor start time" "Animation can be restarted even if not over" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "TimeSensor startTime test" + } + Transform2D { + translation -100 0 + children [ + DEF N3 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 0 + filled TRUE + } + } + geometry Rectangle { + size 80 80 + } + } + ] + } + DEF N2 TimeSensor { + cycleInterval 2 + startTime 2 + stopTime -1 + } + DEF N0 PositionInterpolator2D { + key [0 1] + keyValue [100 -100 100 100] + } + DEF N4 Transform2D { + translation 100 -100 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.5 0.5 0.5 + filled TRUE + } + } + geometry Rectangle { + size 80 80 + } + } + ] + } + ] +} + +ROUTE N3.touchTime TO N2.stopTime +ROUTE N3.touchTime TO N2.startTime +ROUTE N2.fraction_changed TO N0.set_fraction +ROUTE N0.value_changed TO N4.translation + diff --git a/regression_tests/bifs-interpolation-valuator-sftime.bt b/regression_tests/bifs-interpolation-valuator-sftime.bt new file mode 100644 index 0000000..ac679a9 --- /dev/null +++ b/regression_tests/bifs-interpolation-valuator-sftime.bt @@ -0,0 +1,58 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage Valuator" "with SFTime to SFString formatting" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Valuator Test" + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT Text { + fontStyle DEF FONT FontStyle { + justify ["MIDDLE"] + size 20 + } + } + } + DEF V Valuator { + } + DEF TS TimeSensor { + cycleInterval 2 + loop TRUE + } + ] +} + +ROUTE TS.time TO V.inSFTime +ROUTE V.outMFString TO TXT.string + diff --git a/regression_tests/bifs-linking-anchor-mp4-next.bt b/regression_tests/bifs-linking-anchor-mp4-next.bt new file mode 100644 index 0000000..c7cc4a0 --- /dev/null +++ b/regression_tests/bifs-linking-anchor-mp4-next.bt @@ -0,0 +1,67 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + DEF B1 Background2D { + backColor 1 0 1 + } + WorldInfo { + info ["This shows Anchor with MP4 hyperlinking" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Anchor Test" + } + Anchor { + url ["bifs-linking-anchor-mp4-prev.mp4"] + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 1 0 + filled TRUE + } + } + geometry Rectangle { + size 200 150 + } + } + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Click here to open" "prev.mp4"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-linking-anchor-mp4-prev.bt b/regression_tests/bifs-linking-anchor-mp4-prev.bt new file mode 100644 index 0000000..859fa60 --- /dev/null +++ b/regression_tests/bifs-linking-anchor-mp4-prev.bt @@ -0,0 +1,67 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + DEF B1 Background2D { + backColor 0 1 1 + } + WorldInfo { + info ["This shows Anchor with MP4 hyperlinking" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Anchor Test" + } + Anchor { + url ["bifs-linking-anchor-mp4-next.mp4"] + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 100 + } + } + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Click here to open" "next.mp4"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-linking-anchor-viewpoint.bt b/regression_tests/bifs-linking-anchor-viewpoint.bt new file mode 100644 index 0000000..fe2142f --- /dev/null +++ b/regression_tests/bifs-linking-anchor-viewpoint.bt @@ -0,0 +1,92 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 200 + pixelHeight 200 + } + } + } + ] +} + +Group { + children [ + WorldInfo { + title "Anchor and Viewpoints test" + info ["This shows anchors triggering viewpoint binding" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + + DEF VP1 Viewpoint {description "Front View" position 0 0 300} + DEF VP2 Viewpoint {description "Above View" position 0 300 30 orientation 1 0 0 -1.2 } + + DEF TR Transform { + translation 50 0 0 + children [ + Anchor { + url "#VP2" + children [ + Shape { + appearance Appearance { + material Material { + diffuseColor 0 1 1 + } + } + geometry DEF BOX Box {size 50 50 50} + } + ] + } + ] + } + + Transform { + translation -80 0 20 + children [ + Shape { + appearance Appearance { + material Material2D { emissiveColor 1 0 0 filled TRUE } + } + geometry DEF RC_RED Rectangle {size 100 100} + } + ] + } + + Anchor { + url "bifs-3D-viewpoint-bind.mp4#VP2" + children [ + Shape { + appearance Appearance { + material Material2D { emissiveColor 1 1 1 filled TRUE } + } + geometry DEF RC_WHITE Rectangle { size 200 200 } + } + ] + } + + Transform { + translation -30 0 0 + children [ + Shape { + appearance Appearance { + material DEF MAT Material { + diffuseColor 1 1 0 + } + } + geometry DEF SPHERE Sphere {radius 30} + } + ] + } + ] +} diff --git a/regression_tests/bifs-linking-anchor-www.bt b/regression_tests/bifs-linking-anchor-www.bt new file mode 100644 index 0000000..56b42ff --- /dev/null +++ b/regression_tests/bifs-linking-anchor-www.bt @@ -0,0 +1,67 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 255 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + DEF B1 Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows Anchor with WWW hyperlinking" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Anchor Test" + } + Anchor { + url ["http://www.chiariglione.org/mpeg/"] + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 200 100 + } + } + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Click here to go" "to MPEG Web Site"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-linking-animationstream.bt b/regression_tests/bifs-linking-animationstream.bt new file mode 100644 index 0000000..e0a5369 --- /dev/null +++ b/regression_tests/bifs-linking-animationstream.bt @@ -0,0 +1,136 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + includeInlineProfileLevelFlag true + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows an AnimationStream node" "using a BIFS-command stream" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Animation Stream" + } + DEF N0 Transform2D { + translation 100 0 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 100 100 + } + } + ] + } + AnimationStream { + loop TRUE + url [od:10] + } + Transform2D { + translation 0 -100 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Position modified" "through animationStream"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + ] +} + + +RAP AT 0 IN 5 { + REPLACE N0.translation BY 100 0 +} + +RAP AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 5 + dependsOn_ES_ID 1 + OCR_ES_ID 5 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + nodeIDbits 1 + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + slConfigDescr SLConfigDescriptor { + useAccessUnitStartFlag true + useAccessUnitEndFlag true + useTimeStampsFlag true + timeStampResolution 1000 + OCRResolution 1000 + timeStampLength 32 + } + } + ] + } + ] +} + +RAP AT 500 IN 5 { + REPLACE N0.translation BY 100 100 +} + +RAP AT 1000 IN 5 { + REPLACE N0.translation BY -100 100 +} + +RAP AT 1500 IN 5 { + REPLACE N0.translation BY -100 0 +} + +RAP AT 2000 IN 5 { + REPLACE N0.translation BY 100 0 +} + diff --git a/regression_tests/bifs-linking-inline-direct-inline.bt b/regression_tests/bifs-linking-inline-direct-inline.bt new file mode 100644 index 0000000..fcebe9c --- /dev/null +++ b/regression_tests/bifs-linking-inline-direct-inline.bt @@ -0,0 +1,75 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["Inlined scene with planeSensor2D" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Inline Test" + } + Background2D { + backColor 1 1 0 + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Circle { + radius 100 + } + } + ] + } + Transform2D { + children [ + DEF T1 Transform2D { + children [ + DEF PS PlaneSensor2D { + maxPosition 150 150 + minPosition -150 -150 + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.5 0.6 0.4 + filled TRUE + } + } + geometry DEF REC Rectangle { + size 50 100 + } + } + ] + } + ] + } + ] +} + +ROUTE PS.translation_changed TO T1.translation + diff --git a/regression_tests/bifs-linking-inline-direct.bt b/regression_tests/bifs-linking-inline-direct.bt new file mode 100644 index 0000000..4884329 --- /dev/null +++ b/regression_tests/bifs-linking-inline-direct.bt @@ -0,0 +1,51 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 0 + } + WorldInfo { + info ["This shows usage of the inline node" "with rotation and scaling on the inlined scene" "Inline scene is referenced without OD" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Inline Test" + } + Transform2D { + rotationAngle 0.78 + scale 1.5 1 + children [ + Inline { + url ["bifs-linking-inline-direct-inline.mp4"] + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-linking-inline-od-inline.bt b/regression_tests/bifs-linking-inline-od-inline.bt new file mode 100644 index 0000000..91cc6c9 --- /dev/null +++ b/regression_tests/bifs-linking-inline-od-inline.bt @@ -0,0 +1,76 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["Inlined scene with planeSensor2D" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Inline Test" + } + + Background2D { + backColor 1 1 0 + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Circle { + radius 100 + } + } + ] + } + Transform2D { + children [ + DEF T1 Transform2D { + children [ + DEF PS PlaneSensor2D { + maxPosition 150 150 + minPosition -150 -150 + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0.5 0.6 0.4 + filled TRUE + } + } + geometry DEF REC Rectangle { + size 50 100 + } + } + ] + } + ] + } + ] +} + +ROUTE PS.translation_changed TO T1.translation + diff --git a/regression_tests/bifs-linking-inline-od.bt b/regression_tests/bifs-linking-inline-od.bt new file mode 100644 index 0000000..fac7d66 --- /dev/null +++ b/regression_tests/bifs-linking-inline-od.bt @@ -0,0 +1,60 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of the inline node" "with rotation and scaling on the inlined scene" "Inline scene is referenced through an OD" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Inline Test" + } + Transform2D { + rotationAngle 0.78 + scale 1.5 1 + children [ + Inline { + url [od:8] + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + URLstring "bifs-linking-inline-od-inline.mp4" + } + ] +} + diff --git a/regression_tests/bifs-linking-inline-rtsp-no-od.bt b/regression_tests/bifs-linking-inline-rtsp-no-od.bt new file mode 100644 index 0000000..d9f3c14 --- /dev/null +++ b/regression_tests/bifs-linking-inline-rtsp-no-od.bt @@ -0,0 +1,48 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of the inline node" "using remote (rtsp) scene for inline" "Inline scene is referenced with OD" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Inline Test" + } + Transform2D { + children [ + Inline { + url ["rtsp://a1749.q.kamai.net/7/1749/1416/3c964c64/neo.qtv.apple.com/secure/may/preview/classic_cars_300.mp4"] + } + ] + } + ] +} + diff --git a/regression_tests/bifs-linking-inline-rtsp.bt b/regression_tests/bifs-linking-inline-rtsp.bt new file mode 100644 index 0000000..91eb29c --- /dev/null +++ b/regression_tests/bifs-linking-inline-rtsp.bt @@ -0,0 +1,59 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of the inline node" "using remote (rtsp) scene for inline" "Inline scene is referenced with OD" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.5 $" "(C) 2002-2004 GPAC Team"] + title "Inline Test" + } + Transform2D { + children [ + Inline { + url [od:8] + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 +# URLstring "rtsp://a1749.q.kamai.net/7/1749/1416/3c964c64/neo.qtv.apple.com/secure/may/preview/civ3_700.mp4" + URLstring "rtsp://kara/bhusa.mp4" + } + ] +} + diff --git a/regression_tests/bifs-linking-inline-segment-inline.bt b/regression_tests/bifs-linking-inline-segment-inline.bt new file mode 100644 index 0000000..bcc6c16 --- /dev/null +++ b/regression_tests/bifs-linking-inline-segment-inline.bt @@ -0,0 +1,74 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] + ociDescr [ + SegmentDescriptor { startTime 0 duration 4 name "Begin" } + SegmentDescriptor { startTime 4 duration 2 name "Middle" } + SegmentDescriptor { startTime 6 duration 4 name "End" } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of MediaSensor" "with media segments defined" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Media Sensor Test #2" + } + Shape { + appearance Appearance { + texture DEF MOV MovieTexture { + loop TRUE + stopTime -1 + url [od:8] + repeatS FALSE + repeatT FALSE + } + } + geometry Bitmap {} + } + ] +} + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-linking-inline-segment.bt b/regression_tests/bifs-linking-inline-segment.bt new file mode 100644 index 0000000..afbd4e8 --- /dev/null +++ b/regression_tests/bifs-linking-inline-segment.bt @@ -0,0 +1,41 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of the inline node" "with rotation and scaling on the inlined scene" "Inline scene is referenced through an OD" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Inline Test" + } + Transform2D { + children [ + Inline { + url ["bifs-linking-inline-segment-inline.mp4#Middle"] + } + ] + } + ] +} diff --git a/regression_tests/bifs-media-audiobuffer.bt b/regression_tests/bifs-media-audiobuffer.bt new file mode 100644 index 0000000..57d868c --- /dev/null +++ b/regression_tests/bifs-media-audiobuffer.bt @@ -0,0 +1,203 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + objectTypeIndication 1 + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 200 + } + } + } + ES_Descriptor { + ES_ID 2 + OCR_ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows audiobuffer" "with loop and pitch" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "AudioBuffer Test" + } + Sound2D { + source DEF AUDBUF AudioBuffer { + loop TRUE + numChan 2 + length 2 + children [ + AudioSource { + url [od:10] + stopTime -1 + } + ] + } + } + Transform2D { + translation 0 -50 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 100 50 + } + } + Shape { + appearance DEF TXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["restart"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 16 + } + } + } + DEF TS1 TouchSensor {} + ] + } + DEF C1 Conditional { + buffer { + REPLACE AUDBUF.stopTime BY 0 + REPLACE AUDBUF.startTime BY 0 + } + } + Transform2D { + translation -100 50 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 1 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance USE TXTAPP + geometry Text { + string ["pitch" "0.5"] + fontStyle USE FS + } + } + DEF TS2 TouchSensor {} + ] + } + Transform2D { + translation 0 50 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance USE TXTAPP + geometry Text { + string ["pitch" "1.0"] + fontStyle USE FS + } + } + DEF TS3 TouchSensor {} + ] + } + Transform2D { + translation 100 50 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance USE TXTAPP + geometry Text { + string ["pitch" "2"] + fontStyle USE FS + } + } + DEF TS4 TouchSensor {} + ] + } + DEF C2 Conditional { + buffer { + REPLACE AUDBUF.pitch BY 0.5 + } + } + DEF C3 Conditional { + buffer { + REPLACE AUDBUF.pitch BY 1 + } + } + DEF C4 Conditional { + buffer { + REPLACE AUDBUF.pitch BY 2 + } + } + ] +} + +ROUTE TS1.isActive TO C1.activate +ROUTE TS2.isActive TO C2.activate +ROUTE TS3.isActive TO C3.activate +ROUTE TS4.isActive TO C4.activate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_audio.aac" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-audioclip-urlchanged.bt b/regression_tests/bifs-media-audioclip-urlchanged.bt new file mode 100644 index 0000000..7b93a75 --- /dev/null +++ b/regression_tests/bifs-media-audioclip-urlchanged.bt @@ -0,0 +1,145 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows AudioClip" "and direct modification of URL" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "AudioClip Test" + } + DEF SD Sound2D { + source DEF AS AudioClip { + loop TRUE + url [od:20] + } + } + Transform2D { + translation -50 0 + children [ + DEF TS1 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Sound1"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + DEF C1 Conditional { + buffer { + REPLACE AS.url BY ["od:20"] + } + } + Transform2D { + translation 50 0 + children [ + DEF TS2 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance USE APP + geometry Text { + string ["Sound2"] + fontStyle USE FS + } + } + ] + } + DEF C2 Conditional { + buffer { + REPLACE AS.url BY ["od:10"] + } + } + ] +} + +ROUTE TS1.isActive TO C1.activate +ROUTE TS2.isActive TO C2.activate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_german.mp3" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 20 + esDescr [ + ES_Descriptor { + ES_ID 21 + OCR_ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/count_arabic.mp3" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-audioclip.bt b/regression_tests/bifs-media-audioclip.bt new file mode 100644 index 0000000..b4d14b2 --- /dev/null +++ b/regression_tests/bifs-media-audioclip.bt @@ -0,0 +1,133 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows AudioClip" "controled through startTime and stopTime" "audio clock is independent from BIFS clock, thus audio restarts from begining" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "AudioClip Test" + } + DEF SD Sound2D { + source DEF AS AudioClip { + loop TRUE + url [od:10] + } + } + Transform2D { + translation -50 0 + children [ + DEF TS1 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Stop"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + DEF C1 Conditional { + buffer { + REPLACE AS.stopTime BY 0 + } + } + Transform2D { + translation 50 0 + children [ + DEF TS2 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance USE APP + geometry Text { + string ["Start"] + fontStyle USE FS + } + } + ] + } + DEF C2 Conditional { + buffer { + REPLACE AS.startTime BY 0 + } + } + ] +} + +ROUTE TS1.isActive TO C1.activate +ROUTE TS2.isActive TO C2.activate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_audio.aac" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-audiosource-mixing.bt b/regression_tests/bifs-media-audiosource-mixing.bt new file mode 100644 index 0000000..8a9c861 --- /dev/null +++ b/regression_tests/bifs-media-audiosource-mixing.bt @@ -0,0 +1,194 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +PROTO AudioControler [ + exposedField MFString the_url [] + exposedField MFString comment [] + exposedField SFVec2f position 0 0 + exposedField SFColor color 0 0 0 + exposedField SFFloat vol 1.0 + exposedField SFInt32 numChannels 2 +] { + Transform2D { + translation IS position + children [ + Transform2D { + children [ + Shape { + appearance Appearance { material Material2D {emissiveColor IS color filled TRUE} } + geometry Rectangle { size 200 20 } + } + DEF TS TouchSensor {} + ] + } + Transform2D { + translation -120 0 + children [ + Shape { + appearance DEF TXTAPP Appearance { material DEF MAT Material2D {emissiveColor 0 0 0 filled TRUE} } + geometry DEF TXT Text { string ["volume", "0"] fontStyle DEF FS FontStyle { size 12 justify ["MIDDLE"]} } + } + + ] + } + Transform2D { + translation 0 20 + children [ + Shape { + appearance USE TXTAPP + geometry Text { string IS comment fontStyle USE FS} + } + + ] + } + DEF SND Sound2D { + intensity IS vol + source AudioSource { + url IS the_url + startTime 0 + numChan IS numChannels + } + } + MediaControl { + url IS the_url + loop TRUE + } + ] + } + DEF SC Script { + eventIn SFVec3f set_frac + eventIn SFBool set_down + field SFNode s USE SND + field SFNode t USE TXT + field SFBool isDown FALSE + url ["javascript: + function initialize() { + t.string[1] = '' + s.intensity; + } + function set_down(value, timestamp) {isDown = value;} + function set_frac(value, timestamp) { + if (!isDown) return; + pos = (value.x + 100)/200; + s.intensity = pos; + t.string[1] = '' + pos; + } + " + ] + } + ROUTE TS.isActive TO SC.set_down + ROUTE TS.hitPoint_changed TO SC.set_frac + +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows MediaControl" "controling audio playback speed" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Media Control Test" + } + + AudioControler { + position 0 100 + color 1 0 0 + the_url ["od:10"] + numChannels 2 + vol 1 + comment ["Audio #1 - running on its own timeline"] + } + AudioControler { + position 0 33 + color 0 1 0 + the_url ["od:11"] + numChannels 2 + vol 0 + comment ["Audio #2 - running on its own timeline"] + } + AudioControler { + position 0 -33 + color 0 0 1 + the_url ["od:11"] + numChannels 2 + vol 0 + comment ["Audio #3 reusing Audio #2 object"] + } + AudioControler { + position 0 -100 + color 1 0 1 + the_url ["od:12"] + numChannels 2 + vol 0 + comment ["Audio #4 - running on Audio #2 timeline"] + } + ] +} + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 10 + OCR_ES_ID 10 + muxInfo MuxInfo { + fileName "auxiliary_files/count_english.mp3" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 11 + esDescr [ + ES_Descriptor { + ES_ID 11 + OCR_ES_ID 11 + muxInfo MuxInfo { + fileName "auxiliary_files/count_english.mp3" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 12 + esDescr [ + ES_Descriptor { + ES_ID 12 + OCR_ES_ID 11 + muxInfo MuxInfo { + fileName "auxiliary_files/count_english.mp3" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-audiosource-urlchanged.bt b/regression_tests/bifs-media-audiosource-urlchanged.bt new file mode 100644 index 0000000..31c7514 --- /dev/null +++ b/regression_tests/bifs-media-audiosource-urlchanged.bt @@ -0,0 +1,146 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows AudioSource" "and direct modification of URL" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "AudioSource Test" + } + DEF SD Sound2D { + source DEF AS AudioSource { + url [od:20] + } + } + Transform2D { + translation -50 0 + children [ + DEF TS1 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Sound1"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + DEF C1 Conditional { + buffer { + REPLACE AS.startTime BY 0 + REPLACE AS.url BY ["od:20"] + } + } + Transform2D { + translation 50 0 + children [ + DEF TS2 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance USE APP + geometry Text { + string ["Sound2"] + fontStyle USE FS + } + } + ] + } + DEF C2 Conditional { + buffer { + REPLACE AS.startTime BY 0 + REPLACE AS.url BY ["od:10"] + } + } + ] +} + +ROUTE TS1.isActive TO C1.activate +ROUTE TS2.isActive TO C2.activate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_english.mp3" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 20 + esDescr [ + ES_Descriptor { + ES_ID 21 + OCR_ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/count_french.mp3" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-audiosource.bt b/regression_tests/bifs-media-audiosource.bt new file mode 100644 index 0000000..f4284b3 --- /dev/null +++ b/regression_tests/bifs-media-audiosource.bt @@ -0,0 +1,133 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows AudioSource" "controled through startTime and stopTime" "audio clock is independent from BIFS clock, thus audio restarts from begining" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "AudioSource Test" + } + DEF SD Sound2D { + source DEF AS AudioSource { + url [od:10] + stopTime 4 + } + } + Transform2D { + translation -50 0 + children [ + DEF TS1 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Stop"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + DEF C1 Conditional { + buffer { + REPLACE AS.stopTime BY 0 + } + } + Transform2D { + translation 50 0 + children [ + DEF TS2 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance USE APP + geometry Text { + string ["Start"] + fontStyle USE FS + } + } + ] + } + DEF C2 Conditional { + buffer { + REPLACE AS.startTime BY 0 + } + } + ] +} + +ROUTE TS1.isActive TO C1.activate +ROUTE TS2.isActive TO C2.activate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_italian.mp3" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-imagetexture-OD-reuse.bt b/regression_tests/bifs-media-imagetexture-OD-reuse.bt new file mode 100644 index 0000000..ab62590 --- /dev/null +++ b/regression_tests/bifs-media-imagetexture-OD-reuse.bt @@ -0,0 +1,89 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows two ImageTexture nodes" "using the same OD" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Image Texture test" + } + Transform2D { + translation -80 0 + children [ + Shape { + appearance Appearance { + texture ImageTexture { + url [od:10] + } + } + geometry Rectangle { + size 100 50 + } + } + ] + } + Transform2D { + translation 80 0 + children [ + Shape { + appearance Appearance { + material Material2D { filled TRUE transparency 0.5 } + texture ImageTexture { + url [od:10] + } + } + geometry Circle { + radius 50 + } + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-imagetexture-no-od.bt b/regression_tests/bifs-media-imagetexture-no-od.bt new file mode 100644 index 0000000..1b0f32c --- /dev/null +++ b/regression_tests/bifs-media-imagetexture-no-od.bt @@ -0,0 +1,60 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 254 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 254 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 0 1 + } + WorldInfo { + info [ + "This test shows how a URL in an ImageTexture node can be a regular URL (http, file, ...) and not an OD URL." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" + "(C) 2002-2006 GPAC Team" + ] + title "Image referenced by a non-OD URL" + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF TXT ImageTexture { + url ["auxiliary_files/logo.jpg"] + } + } + geometry Bitmap {} + + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-media-imagetexture-object-scale.bt b/regression_tests/bifs-media-imagetexture-object-scale.bt new file mode 100644 index 0000000..2950755 --- /dev/null +++ b/regression_tests/bifs-media-imagetexture-object-scale.bt @@ -0,0 +1,97 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 440 + pixelHeight 440 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows image texture" "with object scaling and texture transform" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Image Texture Test" + } + DEF TR Transform2D { + rotationAngle 0.78 + children [ + Shape { + geometry IndexedLineSet2D { + coord Coordinate2D { + point [0 200 0 -200] + } + } + } + Shape { + appearance Appearance { + texture ImageTexture { + url [od:10] + } + textureTransform DEF TX TextureTransform { + translation 0 0.5 + } + } + geometry Rectangle { + size 200 200 + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 4 + loop TRUE + } + DEF PI PositionInterpolator2D { + key [0 0.5 1] + keyValue [-0.5 -0.5 0.5 0.5 -0.5 -0.5] + } + DEF SI PositionInterpolator2D { + key [0 0.5 1] + keyValue [0.5 0.5 1.5 1.5 0.5 0.5] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO TR.scale + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-imagetexture-transparent.bt b/regression_tests/bifs-media-imagetexture-transparent.bt new file mode 100644 index 0000000..cc59870 --- /dev/null +++ b/regression_tests/bifs-media-imagetexture-transparent.bt @@ -0,0 +1,275 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 440 + pixelHeight 440 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 0 1 1 + } + WorldInfo { + info ["This shows basic 2D shapes" "with transparent image texture mapping" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Image Texture Test" + } + Transform2D { + translation -150 150 + children [ + Shape { + appearance DEF APP Appearance { + texture ImageTexture { + url [od:10] + } + } + geometry IndexedLineSet2D { + coord Coordinate2D { + point [-50 0 0 50 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance DEF TEXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["IndexedLineSet2D" "[-50 0 0 50 50 0]"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 12 + } + } + } + ] + } + ] + } + Transform2D { + translation 0 150 + children [ + Shape { + appearance USE APP + geometry Circle { + radius 50 + } + } + Transform2D { + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Circle" "radius 50"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 150 150 + children [ + Shape { + appearance USE APP + geometry Rectangle { + size 100 50 + } + } + Transform2D { + translation 0 -40 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Rectangle" "Size 100 50"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -40 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["Curve2D Points:" "-50 0, -100 50, 0 20, 10 30, 40 80, 50 0"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -150 0 + children [ + Shape { + appearance USE APP + geometry Curve2D { + point DEF C2D Coordinate2D { + point [-50 0 -100 50 0 20 10 30 40 80 50 0] + } + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["no type"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 3] + point USE C2D + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["type [2 3]"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 170 0 + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [1 3 1] + point USE C2D + } + } + Transform2D { + translation 0 -10 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["type [1 3 1]"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation -150 -150 + children [ + Shape { + appearance USE APP + geometry IndexedFaceSet2D { + coord Coordinate2D { + point [-50 0 -25 25 0 80 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["IndexedFaceSet2D" "-50 0, -25, 25, 0 80, 50 0"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -150 + children [ + Shape { + appearance USE APP + geometry PointSet2D { + coord Coordinate2D { + point [-50 0 -25 25 0 80 50 0] + } + } + } + Transform2D { + translation 0 -20 + children [ + Shape { + appearance USE TEXTAPP + geometry Text { + string ["PointSet2D" "-50 0, -25, 25, 0 80, 50 0"] + fontStyle USE FS + } + } + ] + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.png" + useDataReference true + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-imagetexture-url-change.bt b/regression_tests/bifs-media-imagetexture-url-change.bt new file mode 100644 index 0000000..0f53e3c --- /dev/null +++ b/regression_tests/bifs-media-imagetexture-url-change.bt @@ -0,0 +1,95 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 254 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 254 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 0 1 + } + WorldInfo { + info ["This shows basic 2D shapes" "with dynamic URL change" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Image Texture Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF TXT ImageTexture { + url [od:10] + } + } + geometry Bitmap {} + + } + DEF TS TouchSensor {} + ] + } + DEF C1 Conditional { + buffer { + REPLACE TXT.url BY ["od:20"] + } + } + DEF RC1 Conditional { + buffer { + REPLACE TXT.url BY ["od:10"] + } + } + ] +} + +ROUTE TS.isActive TO C1.activate +ROUTE TS.isActive TO RC1.reverseActivate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 20 + esDescr [ + ES_Descriptor { + ES_ID 3 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 4 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.png" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-movietexture-control.bt b/regression_tests/bifs-media-movietexture-control.bt new file mode 100644 index 0000000..fca2f5e --- /dev/null +++ b/regression_tests/bifs-media-movietexture-control.bt @@ -0,0 +1,148 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 180 + pixelHeight 200 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows MovieTexture with playback control" "through usage of start/stop fields of movieTexture" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "MovieTexture Test" + } + Transform2D { + children [ + DEF TR Transform2D { + translation 0 30 + children [ + Shape { + appearance Appearance { + texture DEF MT MovieTexture { + loop TRUE + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry Bitmap {} + + } + ] + } + ] + } + Transform2D { + translation -50 -70 + children [ + DEF TS1 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Stop"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + DEF C1 Conditional { + buffer { + REPLACE MT.stopTime BY 0 + } + } + Transform2D { + translation 50 -70 + children [ + DEF TS2 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + Shape { + appearance USE APP + geometry Text { + string ["Start"] + fontStyle USE FS + } + } + ] + } + DEF C2 Conditional { + buffer { + REPLACE MT.startTime BY 0 + } + } + ] +} + +ROUTE TS1.isActive TO C1.activate +ROUTE TS2.isActive TO C2.activate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-movietexture-no-od.bt b/regression_tests/bifs-media-movietexture-no-od.bt new file mode 100644 index 0000000..1320a16 --- /dev/null +++ b/regression_tests/bifs-media-movietexture-no-od.bt @@ -0,0 +1,65 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info [ + "This test shows how a URL in a MovieTexture node can be a regular URL (http, file, ...) and not an OD URL." + "" + "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.7 $" + "(C) 2002-2006 GPAC Team" + ] + title "Video referenced by a non-OD URL" + } + Sound2D { + source AudioClip { + loop TRUE + url ["auxiliary_files/enst_audio.aac"] + #url ["rtsp://tasmanie/bhusa.mp4"] + } + } + Shape { + appearance Appearance { + texture DEF MT MovieTexture { + loop TRUE + url ["auxiliary_files/enst_video.h264"] + #url ["rtsp://tasmanie/bhusa.mp4"] + } + } + geometry Bitmap {} + + } + ] +} + + diff --git a/regression_tests/bifs-media-movietexture-od-joinsession.bt b/regression_tests/bifs-media-movietexture-od-joinsession.bt new file mode 100644 index 0000000..99c090b --- /dev/null +++ b/regression_tests/bifs-media-movietexture-od-joinsession.bt @@ -0,0 +1,93 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 180 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows MovieTexture nodes" "using the same OD and looping" "with different start and stop times" "one movieTexture joins the session of the other" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "MovieTexture Test" + } + Transform2D { + translation -100 0 + children [ + Shape { + appearance DEF APP Appearance { + texture MovieTexture { + loop TRUE + startTime 4 + url [od:10] + } + } + geometry Circle { + radius 50 + } + } + ] + } + Transform2D { + translation 100 0 + children [ + Shape { + appearance Appearance { + texture MovieTexture { + loop TRUE + stopTime -1 + url [od:10] + } + } + geometry Rectangle { + size 100 50 + } + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-movietexture-od-leave-session.bt b/regression_tests/bifs-media-movietexture-od-leave-session.bt new file mode 100644 index 0000000..e8c1138 --- /dev/null +++ b/regression_tests/bifs-media-movietexture-od-leave-session.bt @@ -0,0 +1,93 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 180 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows MovieTexture nodes" "using the same OD and looping" "with different start and stop times" "one movieTexture leaves the session of the other" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "MovieTexture Test" + } + Transform2D { + translation -100 0 + children [ + Shape { + appearance DEF APP Appearance { + texture MovieTexture { + loop TRUE + stopTime -1 + url [od:10] + } + } + geometry Circle { + radius 50 + } + } + ] + } + Transform2D { + translation 100 0 + children [ + Shape { + appearance Appearance { + texture MovieTexture { + loop TRUE + stopTime 8 + url [od:10] + } + } + geometry Rectangle { + size 100 50 + } + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-movietexture-owns-OCR.bt b/regression_tests/bifs-media-movietexture-owns-OCR.bt new file mode 100644 index 0000000..9ed503f --- /dev/null +++ b/regression_tests/bifs-media-movietexture-owns-OCR.bt @@ -0,0 +1,75 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 180 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows MovieTexture node on bitmap" "looping (visual object de-synchronized from BIFS)" "Video shall restart when done playing" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "MovieTexture Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture MovieTexture { + loop TRUE + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry Bitmap {} + + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-movietexture-shares-OCR.bt b/regression_tests/bifs-media-movietexture-shares-OCR.bt new file mode 100644 index 0000000..9ee42bf --- /dev/null +++ b/regression_tests/bifs-media-movietexture-shares-OCR.bt @@ -0,0 +1,74 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 180 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows MovieTexture node on bitmap" "looping (visual object synchronized on BIFS)" "results at end of video are undefined" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "MovieTexture Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture MovieTexture { + loop TRUE + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry Bitmap {} + + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-movietexture-url-change.bt b/regression_tests/bifs-media-movietexture-url-change.bt new file mode 100644 index 0000000..e193a38 --- /dev/null +++ b/regression_tests/bifs-media-movietexture-url-change.bt @@ -0,0 +1,158 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 255 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 180 + pixelHeight 200 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows dynamic URL change of MovieTexture" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.5 $" "(C) 2002-2004 GPAC Team"] + title "MovieTexture Test" + } + Transform2D { + children [ + DEF TR Transform2D { + translation 0 30 + children [ + Shape { + appearance Appearance { + texture DEF MT MovieTexture { + loop TRUE + url [od:20] + } + } + geometry Bitmap {} + + } + ] + } + ] + } + Transform2D { + translation -50 -70 + children [ + DEF TS1 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 80 30 + } + } + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Movie 1"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + DEF C1 Conditional { + buffer { + REPLACE MT.url BY ["od:20"] + } + } + Transform2D { + translation 50 -70 + children [ + DEF TS2 TouchSensor {} + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Rectangle { + size 80 30 + } + } + Shape { + appearance USE APP + geometry Text { + string ["Movie 2"] + fontStyle USE FS + } + } + ] + } + DEF C2 Conditional { + buffer { + REPLACE MT.url BY ["od:10"] + } + } + ] +} + +ROUTE TS1.isActive TO C1.activate +ROUTE TS2.isActive TO C2.activate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 20 + esDescr [ + ES_Descriptor { + ES_ID 21 + OCR_ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-sound-spatialize.bt b/regression_tests/bifs-media-sound-spatialize.bt new file mode 100644 index 0000000..d49595c --- /dev/null +++ b/regression_tests/bifs-media-sound-spatialize.bt @@ -0,0 +1,77 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0x01 + sceneProfileLevelIndication 0x01 + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFE + graphicsProfileLevelIndication 0x01 + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric FALSE + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "Sound Test" + info ["This shows Sound node" "with distance volume control and spatialization on" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + + Sound { + location 0 0 1 + spatialize TRUE + source AudioClip { + url [ "od:10" ] + loop TRUE + } + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1 0 0 } + } + geometry Box { size 2 2 2} + } + ] + } + ] +} + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "./auxiliary_files/count_spanish.mp3" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-media-sound.bt b/regression_tests/bifs-media-sound.bt new file mode 100644 index 0000000..381b59c --- /dev/null +++ b/regression_tests/bifs-media-sound.bt @@ -0,0 +1,77 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0x01 + sceneProfileLevelIndication 0x01 + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFE + graphicsProfileLevelIndication 0x01 + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric FALSE + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + es_id 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +Group { + children [ + + WorldInfo { + title "Sound Test" + info ["This shows Sound node" "with distance volume control" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + + Sound { + location 0 0 1 + spatialize FALSE + source AudioClip { + url [ "od:10" ] + loop TRUE + } + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1 0 0 } + } + geometry Box { size 2 2 2} + } + ] + } + ] +} + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "./auxiliary_files/count_english.mp3" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-misc-UTF16-input.bt b/regression_tests/bifs-misc-UTF16-input.bt new file mode 100644 index 0000000..93db2f8 --- /dev/null +++ b/regression_tests/bifs-misc-UTF16-input.bt @@ -0,0 +1,55 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 260 + pixelHeight 450 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows non ASCII Text characters" "Input BT file is in UTF-16 Little Endian format" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "MP4Box Test" + } + Transform2D { + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Non-Ansi:" "" "é à è ù ü ä ë ï ö ü ÿ" "" "" "XML escapes:" "" "& ' \\" > <"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 26 + } + } + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-misc-cyclic-graph.bt b/regression_tests/bifs-misc-cyclic-graph.bt new file mode 100644 index 0000000..687c2ac --- /dev/null +++ b/regression_tests/bifs-misc-cyclic-graph.bt @@ -0,0 +1,55 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows a cyclic scene graph" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2005 GPAC Team"] + title "Cyclic Scene Graph Test" + } + DEF TR Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D {emissiveColor 1 0 0 filled TRUE } + } + geometry Circle { radius 60 } + } + Transform2D { + scale 0.98 0.98 + translation 1 0 + children [ + ColorTransform { + mrr 0.95 + children [ USE TR ] + } + ] + } + ] + } + ] +} + diff --git a/regression_tests/bifs-misc-hc-proto-pathextrusion.bt b/regression_tests/bifs-misc-hc-proto-pathextrusion.bt new file mode 100644 index 0000000..fd5da2f --- /dev/null +++ b/regression_tests/bifs-misc-hc-proto-pathextrusion.bt @@ -0,0 +1,103 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + + +EXTERNPROTO PathExtrusion [ + exposedField SFNode geometry NULL + exposedField MFVec3f spine [] + exposedField SFBool beginCap TRUE + exposedField SFBool endCap TRUE + exposedField SFFloat creaseAngle 1.0 + exposedField MFRotation orientation [] + exposedField MFVec2f scale [] + exposedField SFBool txAlongSpine FALSE +] +[ "urn:inet:gpac:builtin:PathExtrusion"] + + + +Group { + children [ + + WorldInfo { + info ["This shows GPAC PathExtrusion HardcodedProto" "The text is extruded along a spline to get a solid text in 3D" "GPAC Regression Tests" "$Date: 2008/05/19 15:28:18 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "PathExtrusion HardcodedProto" + } + NavigationInfo { type ["EXAMINE", "ANY"] headlight FALSE} + Background2D {backColor 0 0 0 url "./auxiliary_files/logo.jpg"} + DEF VIEWPOINT Viewpoint { + description "one" + position 0 0 10 + } + + DirectionalLight { + direction -1 -1 -1 + } + +Shape { + appearance Appearance { + material Material { + diffuseColor 1.0 0.8 0.0 +# specularColor 1 1 1 +# shininess 0.3 + } +# texture ImageTexture { url "../../auxiliary_files/logo.jpg"} + } + + geometry PathExtrusion { +# geometry Curve2D { fineness 1.0 point Coordinate2D { point [0 0, 0.25 1, 0.75 1, 1 0, 1.5 0, 0.8 -2, 0 0] } type [2 0 3] } +# geometry Rectangle { size 0.5 1.0 } + + geometry Text { + string ["G"] + fontStyle FontStyle { + size 2.0 + justify ["MIDDLE", "MIDDLE"] + } + } + + creaseAngle 0.5 + endCap FALSE + beginCap TRUE + txAlongSpine FALSE + spine [ + # Straight-line + 0.0 0.0 0.0, 0.0 1 0, +# 0.0 0.0 -0.8, 0.0 0.0 -1.2, +# 0.0 0.0 -1.6, 0.0 0.0 -2.0 + ] +# scale [ +# 1.8 1.8, 1.95 1.95, +# 2.0 2.0, 1.95 1.95 +# 1.8 1.8, 1.5 1.5 +# 1.2 1.2, 1.05 1.05, +# 1.0 1.0, 1.05 1.05, +# 1.15 1.15, +# ] + } +} + +] +} + diff --git a/regression_tests/bifs-misc-hc-proto-planarextrusion.bt b/regression_tests/bifs-misc-hc-proto-planarextrusion.bt new file mode 100644 index 0000000..c61f267 --- /dev/null +++ b/regression_tests/bifs-misc-hc-proto-planarextrusion.bt @@ -0,0 +1,87 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + + +EXTERNPROTO PlanarExtrusion [ + exposedField SFNode geometry NULL + exposedField SFNode spine NULL + exposedField SFBool beginCap TRUE + exposedField SFBool endCap TRUE + exposedField SFFloat creaseAngle 1.0 + exposedField MFFloat orientationKeys [] + exposedField MFRotation orientation [] + exposedField MFFloat scaleKeys [] + exposedField MFVec2f scale [] + exposedField SFBool txAlongSpine FALSE +] +[ "urn:inet:gpac:builtin:PlanarExtrusion"] + + + +Group { + children [ + WorldInfo { + info ["This shows GPAC PlanarExtrusion HardcodedProto" "A circle is extruded along another circle to get a nice 3D torus" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "PlanarExtrusion HardcodedProto" + } + + Background2D { backColor 1 1 1} + DEF VIEWPOINT Viewpoint { + description "one" + position 0 0 10 + } + + +Shape { + appearance Appearance { + material Material { + diffuseColor 1.0 0.8 0.0 + } +# texture ImageTexture { url "../../auxiliary_files/logo.jpg"} + } + + geometry PlanarExtrusion { + geometry Circle { radius 1} +# geometry Curve2D { +# type [2] +# point Coordinate2D { +# point [0 0 0.75 2 1.5 0 2 2] +# } +# } + spine Circle {radius 5} +# spine Rectangle {size 20 10} + creaseAngle 1 + endCap FALSE + beginCap FALSE +# scaleKeys [0 0.1 0.25 0.5 0.75 0.9 1] +# scale [0 0, 0 0, 1 1, 2 2.5, 1 1, 0 0, 0 0] +# orientationKeys [0 0.25 0.5 0.75 1] +# orientation [0 1 0 0, 0 1 1 0.25, 0 0 1 0.25, 0 1 1 0.25, 0 1 0 0] + txAlongSpine TRUE + } +} + +] +} + diff --git a/regression_tests/bifs-misc-hc-proto-planeclipper.bt b/regression_tests/bifs-misc-hc-proto-planeclipper.bt new file mode 100644 index 0000000..db35666 --- /dev/null +++ b/regression_tests/bifs-misc-hc-proto-planeclipper.bt @@ -0,0 +1,69 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + ODProfileLevelIndication 0xFF + sceneProfileLevelIndication 0xFE + audioProfileLevelIndication 0xFF + visualProfileLevelIndication 0xFF + graphicsProfileLevelIndication 0xFE + + esdescr [ + ES_Descriptor { + es_id 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric false + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + + +EXTERNPROTO PlaneClipper [ + exposedField SFVec3f plane_normal 1 0 0 + exposedField SFFloat plane_distance 0.0 + exposedField MFNode children [] +] +[ "urn:inet:gpac:builtin:PlaneClipper"] + + + +Group { + children [ + WorldInfo { + info ["This shows GPAC PlaneClipper HardcodedProto" "The plane clipper normal and distance are interpolated to cut the mesh at different places" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "PlaneClipper HardcodedProto" + } + Background2D { backColor 1 1 0} + DEF VIEWPOINT Viewpoint { + description "one" + position 0 0 10 + } + DEF PC PlaneClipper { + children [ + Inline { url "./auxiliary_files/nefertiti.wrl" } + ] + } + DEF TS TimeSensor { cycleInterval 4.0 loop TRUE} + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [-3 3 -3] + } + DEF TS2 TimeSensor { cycleInterval 12.0 loop TRUE} + DEF PI PositionInterpolator { +# key [0 0.333 0.334 0.666 0.667 1] +# keyValue [1 0 0 1 0 0 0 1 0 0 1 0 0 0 -1 0 0 -1] + key [0 0.33 0.666 1] + keyValue [1 0 0, 0 1 0, 0 0 -1, 1 1 0] + } + ] +} + +ROUTE TS.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO PC.plane_distance +ROUTE TS2.fraction_changed TO PI.set_fraction +ROUTE PI.value_changed TO PC.plane_normal diff --git a/regression_tests/bifs-misc-non-linear-parsing-conditional.bt b/regression_tests/bifs-misc-non-linear-parsing-conditional.bt new file mode 100644 index 0000000..bf5dc40 --- /dev/null +++ b/regression_tests/bifs-misc-non-linear-parsing-conditional.bt @@ -0,0 +1,69 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This tests encoding of non-linear declared nodes used in conditionals" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Non-linear conditional test" + } + DEF N0 Conditional { + buffer { + REPLACE N1.point BY [-75 0 -40 75 40 75 75 0 40 -75 -40 -75] + } + } + DEF N2 Conditional { + buffer { + REPLACE N1.point BY [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + DELETE ROUTE R0 + DELETE ROUTE R1 + } + } + Transform2D { + scale 0.5 0.5 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF N1 Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + DEF N3 TouchSensor {} + ] + } + ] +} + +DEF R0 ROUTE N3.isActive TO N0.activate +DEF R1 ROUTE N3.isActive TO N2.reverseActivate + diff --git a/regression_tests/bifs-misc-non-linear-parsing-use.bt b/regression_tests/bifs-misc-non-linear-parsing-use.bt new file mode 100644 index 0000000..7bf33cb --- /dev/null +++ b/regression_tests/bifs-misc-non-linear-parsing-use.bt @@ -0,0 +1,60 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + info ["This tests encoding of non-linear declared nodes used in file" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Non-linear USE test" + } + DEF BACK Background2D { + backColor 1 1 1 + } + DEF TR Transform2D { + scale 0.5 0.5 + children [ + Transform2D { + translation 150 100 + children [ + USE S1 + ] + } + DEF S1 Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + coord DEF COORD Coordinate2D { + point [-100 0 -50 100 50 100 100 0 50 -100 -50 -100] + } + } + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-misc-srt-import-3gpp-control-share-ocr.bt b/regression_tests/bifs-misc-srt-import-3gpp-control-share-ocr.bt new file mode 100644 index 0000000..a155f92 --- /dev/null +++ b/regression_tests/bifs-misc-srt-import-3gpp-control-share-ocr.bt @@ -0,0 +1,64 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 0 1 + } + WorldInfo { + info ["This shows usage of SRT importer" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "SRT importing Test" + } + AnimationStream { + url [od:5] + startTime 5 + stopTime 10 + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 5 + esDescr [ + ES_Descriptor { + ES_ID 4 + OCR_ES_ID 1 + muxInfo MuxInfo { + fileName "auxiliary_files/subtitle.srt" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-misc-srt-import-3gpp-control.bt b/regression_tests/bifs-misc-srt-import-3gpp-control.bt new file mode 100644 index 0000000..f416779 --- /dev/null +++ b/regression_tests/bifs-misc-srt-import-3gpp-control.bt @@ -0,0 +1,64 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 0 1 + } + WorldInfo { + info ["This shows usage of SRT importer" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "SRT importing Test" + } + AnimationStream { + url [od:5] + startTime 5 + stopTime 10 + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 5 + esDescr [ + ES_Descriptor { + ES_ID 4 + OCR_ES_ID 4 + muxInfo MuxInfo { + fileName "auxiliary_files/subtitle.srt" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-misc-srt-import-3gpp.bt b/regression_tests/bifs-misc-srt-import-3gpp.bt new file mode 100644 index 0000000..2e0c8b0 --- /dev/null +++ b/regression_tests/bifs-misc-srt-import-3gpp.bt @@ -0,0 +1,64 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 0 1 + } + WorldInfo { + info ["This shows usage of SRT importer" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "SRT importing Test" + } + AnimationStream { + url [od:5] + startTime 0 + stopTime 0 + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 5 + esDescr [ + ES_Descriptor { + ES_ID 4 + OCR_ES_ID 1 + muxInfo MuxInfo { + fileName "auxiliary_files/subtitle.srt" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-misc-srt-import.bt b/regression_tests/bifs-misc-srt-import.bt new file mode 100644 index 0000000..72b53c5 --- /dev/null +++ b/regression_tests/bifs-misc-srt-import.bt @@ -0,0 +1,75 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 240 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of SRT importer" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "SRT importing Test" + } + Shape { + appearance Appearance { + material Material2D {emissiveColor 0 0 0 filled TRUE } + } + geometry DEF TXT Text { + fontStyle DEF FONT FontStyle { + justify ["MIDDLE"] + size 30 + } + } + } + AnimationStream { + url [od:5] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 5 + esDescr [ + ES_Descriptor { + ES_ID 4 + OCR_ES_ID 1 + muxInfo MuxInfo { + fileName "auxiliary_files/subtitle.srt" + textNode "TXT" + fontNode "FONT" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-od-remove-esd.bt b/regression_tests/bifs-od-remove-esd.bt new file mode 100644 index 0000000..42ba515 --- /dev/null +++ b/regression_tests/bifs-od-remove-esd.bt @@ -0,0 +1,78 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This tests ESDRemove command" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "ESD Remove" + } + Transform2D { + children [ + Shape { + appearance DEF APP Appearance { + texture ImageTexture { + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry Circle { + radius 100 + } + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + +AT 2000 { + REMOVE ESD FROM 10 [20] +} + diff --git a/regression_tests/bifs-od-remove-od.bt b/regression_tests/bifs-od-remove-od.bt new file mode 100644 index 0000000..6a35767 --- /dev/null +++ b/regression_tests/bifs-od-remove-od.bt @@ -0,0 +1,78 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This tests ODRemove command" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "OD Remove test" + } + Transform2D { + children [ + Shape { + appearance DEF APP Appearance { + texture ImageTexture { + url [od:10] + repeatS FALSE + repeatT FALSE + } + } + geometry Circle { + radius 100 + } + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + +AT 2000 { + REMOVE OD [10] +} + diff --git a/regression_tests/bifs-od-update-od.bt b/regression_tests/bifs-od-update-od.bt new file mode 100644 index 0000000..00756bc --- /dev/null +++ b/regression_tests/bifs-od-update-od.bt @@ -0,0 +1,89 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This tests ODUpdate command" "by sending a new OD with the same ID" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "OD Update" + } + Transform2D { + translation -150 0 + children [ + Shape { + appearance Appearance { + texture ImageTexture { + url [od:10] + } + } + geometry Circle { + radius 100 + } + } + ] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 3 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + +AT 2000 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 4 + muxInfo MuxInfo { + fileName "auxiliary_files/sky.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-proto-conditional.bt b/regression_tests/bifs-proto-conditional.bt new file mode 100644 index 0000000..8a15970 --- /dev/null +++ b/regression_tests/bifs-proto-conditional.bt @@ -0,0 +1,107 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO GEOMETRY_PROTO [ + eventIn SFBool act + eventIn SFBool reverseAct + exposedField SFVec2f translation 0 0 + exposedField SFFloat rotation 0 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] { + DEF TR Transform2D { + rotationAngle IS rotation + scale 2 1 + translation IS translation + children [ + DEF S Shape { + geometry IS obj + appearance Appearance { + material Material2D { + emissiveColor IS Color + filled IS filled + transparency IS transparency + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + } + ] + } + DEF C Conditional { + activate IS act + buffer { + REPLACE TR.scale BY 1 1 + } + } + DEF RC Conditional { + reverseActivate IS reverseAct + buffer { + REPLACE TR.scale BY 2 1 + } + } +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of a conditional inside a proto" "getting events outside the proto" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Simple proto Test" + } + DEF PT1 GEOMETRY_PROTO { + translation 100 0 + rotation 0.78 + Color 1 0 1 + transparency 0.75 + lineColor 1 0 0 + lineWidth 2 + obj Circle { + radius 75 + } + } + DEF PT2 Transform2D { + translation -100 0 + children [ + GEOMETRY_PROTO { + Color 1 0 0 + obj Rectangle { + size 50 50 + } + } + DEF TS TouchSensor {} + ] + } + ] +} + +ROUTE TS.isActive TO PT1.act +ROUTE TS.isActive TO PT1.reverseAct + diff --git a/regression_tests/bifs-proto-delete-def.bt b/regression_tests/bifs-proto-delete-def.bt new file mode 100644 index 0000000..bc7b02f --- /dev/null +++ b/regression_tests/bifs-proto-delete-def.bt @@ -0,0 +1,91 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO GEOMETRY_PROTO [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] { + Transform2D { + rotationAngle IS rotation + scale IS scale + translation IS translation + children [ + DEF S Shape { + geometry IS obj + appearance Appearance { + material Material2D { + emissiveColor IS Color + filled IS filled + transparency IS transparency + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + } + Transform2D { + translation -100 0 + children [ + USE S + ] + } + ] + } +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows deleteion of a proto instance by ID" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Simple proto Test" + } + DEF G GEOMETRY_PROTO { + translation 200 0 + scale 1 1.5 + rotation 0.78 + Color 1 0 1 + transparency 0.75 + lineColor 1 0 0 + lineWidth 2 + obj Circle { + radius 75 + } + } + ] +} + + +AT 2000 { + REPLACE G BY NULL +} + diff --git a/regression_tests/bifs-proto-delete-index.bt b/regression_tests/bifs-proto-delete-index.bt new file mode 100644 index 0000000..2a43fd2 --- /dev/null +++ b/regression_tests/bifs-proto-delete-index.bt @@ -0,0 +1,95 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO GEOMETRY_PROTO [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] { + Transform2D { + rotationAngle IS rotation + scale IS scale + translation IS translation + children [ + DEF S Shape { + geometry IS obj + appearance Appearance { + material Material2D { + emissiveColor IS Color + filled IS filled + transparency IS transparency + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + } + Transform2D { + translation -100 0 + children [ + USE S + ] + } + ] + } +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows deleteion of a proto instance by index" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Proto Delete Test" + } + DEF TR Transform2D { + children [ + DEF G GEOMETRY_PROTO { + translation 200 0 + scale 1 1.5 + rotation 0.78 + Color 1 0 1 + transparency 0.75 + lineColor 1 0 0 + lineWidth 2 + obj Circle { + radius 75 + } + } + ] + } + ] +} + + +AT 2000 { + REPLACE TR.children[0] BY NULL +} + diff --git a/regression_tests/bifs-proto-forestgump.bt b/regression_tests/bifs-proto-forestgump.bt new file mode 100644 index 0000000..18054e4 --- /dev/null +++ b/regression_tests/bifs-proto-forestgump.bt @@ -0,0 +1,478 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 250 + pixelHeight 250 + } + } + } + ] +} + +PROTO FORESTGUMP [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFFloat lineWidth 3 + exposedField SFColor lineColor 0.121569 0.101961 0.0901961 + exposedField SFTime runTime 1 + exposedField SFBool loop TRUE + exposedField SFTime start 0 +] { + Transform2D { + rotationAngle IS rotation + scale IS scale + translation IS translation + children [ + DEF MYSWITCH Switch { + whichChoice 0 + choice [ + Transform2D { + scale 0.0899561 0.0900365 + translation 260.243 42.9024 + children [ + Transform2D { + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + geometry Curve2D { + type [1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-2827 -11 -2864 -2 -2864 -2 -2883 -2 -2883 -2 -2892 -2 -2901 -11 -2911 -11 -2920 -11 -2929 -20 -2939 -29 -2939 -29 -2957 -48 -2957 -48 -2966 -57 -2957 -57 -2966 -76 -2966 -85 -2976 -95 -2976 -104 -2976 -104 -2976 -132 -2976 -132 -2976 -132 -2976 -160 -2976 -160 -2966 -178 -2957 -188 -2957 -188 -2957 -188 -2939 -197 -2939 -197 -2939 -197 -2920 -197 -2920 -197 -2920 -197 -2911 -206 -2911 -206 -2911 -206 -2892 -206 -2892 -206 -2892 -206 -2873 -206 -2873 -206 -2873 -206 -2855 -215 -2855 -215 -2855 -215 -2836 -215 -2836 -215 -2836 -215 -2827 -206 -2827 -206 -2827 -206 -2808 -197 -2808 -197 -2799 -188 -2799 -188 -2790 -178 -2790 -178 -2790 -169 -2790 -169 -2780 -160 -2780 -160 -2771 -150 -2771 -150 -2771 -141 -2771 -141 -2771 -132 -2771 -132 -2771 -122 -2771 -122 -2771 -113 -2771 -113 -2771 -104 -2771 -104 -2771 -95 -2771 -95 -2771 -85 -2771 -85 -2771 -76 -2780 -57 -2780 -48 -2780 -48 -2790 -29 -2790 -29 -2790 -29 -2808 -20 -2808 -20 -2808 -20 -2818 -11 -2818 -11 -2827 -2 -2827 -2 -2836 -2 -2836 -2 -2846 -2 -2855 -2 -2873 -2 -2883 -2] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [1 2 2 1 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-2883 -225 -2883 -262 -2883 -262 -2883 -281 -2883 -281 -2883 -290 -2883 -299 -2883 -308 -2883 -327 -2883 -327 -2892 -336 -2892 -336 -2892 -336 -2892 -383 -2892 -392 -2892 -401 -2892 -429 -2892 -439 -2892 -439 -2892 -467 -2892 -467 -2892 -467 -2892 -485 -2892 -485 -2901 -485 -2901 -485 -2901 -494 -2901 -494 -2901 -504 -2901 -513 -2901 -532 -2901 -541] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-3004 -755 -2994 -727 -2985 -708 -2976 -690 -2976 -680 -2976 -643 -2976 -643 -2957 -625 -3004 -653 -3004 -653 -3004 -653 -3115 -718 -3115 -708 -3115 -662 -3032 -541 -2957 -541 -2939 -541 -2901 -522 -2883 -541 -2883 -541 -2855 -560 -2855 -560 -2846 -569 -2818 -587 -2818 -606 -2818 -625 -2790 -662 -2790 -708 -2790 -773 -2799 -848 -2799 -913 -2799 -931 -2808 -931 -2808 -950 -2808 -959 -2790 -931 -2790 -931 -2780 -904 -2743 -857 -2734 -829 -2734 -820 -2697 -773 -2706 -773] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-2985 -346 -2994 -346 -3022 -374 -3032 -374 -3041 -374 -3050 -383 -3050 -392 -3050 -392 -3134 -392 -3143 -392 -3152 -392 -3134 -364 -3134 -364 -3134 -364 -3106 -346 -3106 -346 -3097 -336 -2985 -253 -2957 -253 -2939 -253 -2920 -271 -2911 -271 -2901 -271 -2873 -281 -2864 -281 -2836 -271 -2790 -234 -2771 -225 -2762 -225 -2734 -206 -2725 -197 -2715 -188 -2669 -160 -2669 -160 -2660 -160 -2660 -150 -2660 -169 -2660 -206 -2641 -262 -2641 -299] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0900093 0.0899546 + translation 190.415 42.8184 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [1 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-2083 -20 -2120 -20 -2120 -20 -2130 -20 -2130 -20 -2139 -20 -2139 -20 -2148 -20 -2148 -20 -2148 -20 -2167 -20 -2167 -20 -2167 -20 -2195 -30 -2195 -30 -2195 -30 -2204 -48 -2213 -58 -2213 -58 -2232 -67 -2241 -76 -2241 -76 -2241 -113 -2241 -113 -2241 -113 -2250 -132 -2250 -141 -2250 -151 -2241 -169 -2241 -169 -2241 -179 -2223 -188 -2223 -188 -2223 -188 -2213 -216 -2204 -216 -2195 -216 -2195 -225 -2185 -225 -2185 -225 -2157 -225 -2157 -225 -2157 -225 -2139 -225 -2139 -225 -2139 -225 -2120 -225 -2120 -225 -2120 -225 -2111 -216 -2111 -216 -2111 -216 -2092 -216 -2092 -216 -2092 -216 -2074 -206 -2074 -206 -2074 -206 -2064 -197 -2064 -197 -2064 -197 -2055 -179 -2055 -179 -2046 -179 -2046 -179 -2046 -169 -2046 -169 -2037 -160 -2037 -160 -2037 -151 -2037 -151 -2037 -141 -2037 -141 -2037 -132 -2037 -132 -2037 -123 -2037 -123 -2037 -113 -2037 -113 -2037 -104 -2037 -104 -2037 -95 -2037 -95 -2037 -95 -2037 -76 -2037 -76 -2037 -76 -2055 -39 -2055 -39 -2055 -39 -2074 -30 -2074 -30 -2074 -30 -2092 -20 -2092 -20 -2092 -20 -2102 -11 -2102 -11 -2111 -11 -2111 -11 -2120 -11 -2120 -11 -2120 -2 -2120 -2 -2157 -2 -2157 -2] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [1 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-2130 -244 -2130 -290 -2130 -318 -2130 -355 -2130 -383 -2130 -402 -2139 -420 -2139 -439 -2139 -448 -2139 -476 -2139 -485 -2139 -495 -2139 -504 -2139 -513 -2139 -523 -2139 -523 -2139 -532 -2139 -532 -2139 -541 -2139 -541 -2130 -551 -2130 -551] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-2306 -792 -2269 -764 -2232 -718 -2204 -690 -2195 -681 -2204 -690 -2213 -690 -2241 -681 -2325 -671 -2343 -653 -2343 -653 -2362 -644 -2362 -644 -2371 -634 -2278 -569 -2278 -569 -2241 -560 -2213 -551 -2176 -551 -2139 -551 -2027 -578 -2027 -616 -2027 -625 -2018 -625 -2018 -634 -1999 -671 -1999 -709 -1999 -755 -1999 -811 -2074 -904 -2111 -922 -2148 -941 -2139 -950 -2102 -950 -2064 -950 -2009 -950 -1971 -950] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-2223 -402 -2260 -420 -2325 -439 -2353 -411 -2371 -392 -2381 -374 -2381 -355 -2381 -318 -2371 -290 -2343 -281 -2306 -262 -2232 -244 -2185 -244 -2176 -244 -2139 -225 -2130 -225 -2120 -225 -2046 -234 -2027 -234 -1981 -234 -1897 -253 -1878 -299 -1869 -318 -1851 -346 -1851 -383 -1851 -411 -1860 -448 -1860 -467] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0899291 0.0900198 + translation 123.698 44.4248 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [1 2 1 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-1283 -11 -1320 -11 -1320 -11 -1330 -11 -1330 -11 -1339 -11 -1339 -11 -1348 -11 -1348 -11 -1348 -11 -1367 -11 -1367 -11 -1367 -20 -1367 -20 -1376 -20 -1376 -20 -1376 -20 -1413 -39 -1413 -39 -1413 -39 -1432 -76 -1432 -76 -1432 -85 -1441 -104 -1441 -113 -1441 -123 -1451 -141 -1451 -151 -1451 -169 -1451 -160 -1441 -169 -1441 -169 -1432 -188 -1432 -188 -1432 -188 -1423 -206 -1423 -206 -1423 -206 -1404 -216 -1404 -216 -1395 -216 -1386 -225 -1376 -225 -1376 -225 -1348 -225 -1348 -225 -1348 -225 -1330 -225 -1330 -225 -1330 -225 -1311 -225 -1311 -225 -1311 -225 -1293 -216 -1293 -216 -1293 -216 -1283 -206 -1283 -206 -1283 -206 -1265 -197 -1265 -197 -1265 -197 -1255 -188 -1255 -188 -1255 -188 -1246 -169 -1246 -169 -1237 -169 -1237 -169 -1237 -160 -1237 -160 -1237 -151 -1237 -151 -1237 -141 -1237 -141 -1237 -132 -1237 -132 -1237 -123 -1237 -123 -1237 -113 -1237 -113 -1237 -104 -1237 -104 -1237 -95 -1237 -95 -1237 -85 -1237 -85 -1237 -85 -1237 -67 -1237 -67 -1237 -67 -1255 -48 -1255 -39 -1255 -39 -1265 -20 -1265 -20 -1265 -20 -1283 -11 -1283 -11 -1283 -11 -1302 -2 -1302 -2 -1311 -2 -1311 -2 -1320 -2 -1320 -2 -1330 -2 -1339 -2 -1358 -2 -1367 -2] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [1 2 1 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [-1311 -234 -1311 -271 -1311 -271 -1311 -281 -1311 -281 -1311 -299 -1311 -318 -1311 -355 -1311 -374 -1311 -392 -1311 -430 -1311 -448 -1311 -457 -1311 -485 -1311 -495 -1311 -495 -1311 -513 -1311 -513 -1311 -523 -1311 -523 -1311 -532 -1311 -532 -1311 -541 -1311 -560 -1311 -578 -1311 -560] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-1572 -662 -1534 -709 -1469 -755 -1441 -783 -1423 -802 -1451 -764 -1451 -764 -1460 -736 -1506 -578 -1469 -560 -1451 -550 -1330 -597 -1320 -597 -1293 -606 -1274 -616 -1274 -662 -1274 -690 -1274 -727 -1283 -746 -1283 -755 -1293 -792 -1302 -811 -1339 -885 -1274 -904 -1274 -987] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2] + point Coordinate2D { + point [-1609 -541 -1618 -467 -1599 -504 -1590 -448 -1562 -309 -1423 -253 -1283 -253 -1237 -253 -1144 -281 -1144 -327 -1144 -346 -1135 -355 -1144 -374 -1153 -392 -1227 -467 -1227 -485] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0899947 0.0899257 + translation 62.0964 37.7238 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 1 2 1 2 1] + point Coordinate2D { + point [-511 -30 -549 -30 -567 -30 -577 -30 -586 -30 -604 -48 -604 -48 -614 -48 -623 -67 -623 -76 -623 -86 -642 -76 -642 -95 -642 -95 -642 -123 -642 -123 -642 -132 -642 -141 -642 -151 -642 -160 -623 -179 -623 -179 -623 -179 -614 -188 -614 -188 -614 -188 -595 -188 -595 -188 -595 -188 -586 -197 -586 -197 -586 -197 -567 -197 -567 -197 -567 -197 -549 -207 -549 -207 -549 -207 -539 -197 -539 -197 -539 -197 -521 -197 -521 -197 -521 -197 -502 -188 -502 -188 -493 -179 -493 -179 -484 -169 -484 -169 -474 -169 -474 -169 -474 -160 -474 -160 -465 -160 -465 -160 -465 -151 -465 -151 -465 -141 -465 -141 -465 -132 -465 -132 -456 -132 -456 -132 -456 -114 -456 -114 -456 -86 -465 -86 -465 -86 -465 -86 -474 -67 -474 -58 -474 -48 -484 -48 -484 -39 -484 -39 -493 -21 -493 -21 -502 -21 -502 -21 -511 -11 -511 -11 -521 -2 -521 -2 -558 -2 -511 -30 -521 -2] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 1 2 2] + point Coordinate2D { + point [-539 -225 -539 -262 -549 -281 -549 -318 -549 -337 -558 -355 -558 -374 -558 -383 -558 -411 -558 -430 -558 -439 -558 -448 -558 -467 -558 -476 -558 -504 -567 -513 -558 -523] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 1 2 2 2 2 2] + point Coordinate2D { + point [-865 -551 -865 -569 -846 -653 -846 -672 -846 -690 -837 -699 -837 -709 -837 -718 -828 -718 -828 -709 -828 -681 -763 -606 -735 -588 -716 -579 -651 -541 -632 -541 -577 -541 -521 -532 -465 -560 -446 -569 -400 -616 -381 -634 -381 -634 -353 -699 -372 -699 -400 -699 -428 -672 -428 -672 -437 -672 -437 -672 -474 -653 -474 -653 -484 -653 -493 -662 -493 -672 -493 -690 -502 -755 -502 -774 -502 -783 -502 -792 -493 -811 -484 -830 -474 -848 -474 -830] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2] + point Coordinate2D { + point [-1023 -365 -976 -318 -930 -300 -874 -272 -865 -272 -837 -253 -828 -253 -716 -216 -577 -262 -456 -262 -428 -262 -335 -318 -363 -346 -372 -355 -437 -346 -456 -346 -456 -346 -511 -337 -511 -337 -530 -337 -539 -318 -549 -318] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0899962 0.0899977 + translation -16.1993 42.7489 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [279 -20 260 -20 232 -1 223 -1 214 -1 205 -20 195 -20 186 -20 167 -29 167 -39 167 -48 149 -66 149 -76 139 -94 139 -94 139 -113 139 -132 130 -141 130 -159 130 -169 139 -178 139 -187 139 -187 149 -206 149 -206 149 -206 158 -225 158 -225 158 -225 177 -234 177 -234 177 -234 195 -234 195 -234 195 -234 214 -234 214 -234 214 -234 232 -234 232 -234 242 -234 251 -225 260 -225 270 -225 270 -225 279 -215 279 -215 288 -215 288 -215 298 -206 298 -206 307 -197 307 -197 316 -187 316 -187 316 -178 316 -178 325 -169 325 -169 335 -159 335 -159 335 -141 335 -141 335 -132 335 -122 335 -113 335 -94 325 -85 325 -76 325 -76 325 -48 325 -48 325 -48 316 -29 316 -29 316 -29 298 -20 298 -20 288 -11 288 -11 279 -11 279 -11 270 -11 260 -11 232 -11 223 -11] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 1 2 1 2] + point Coordinate2D { + point [232 -252 232 -280 214 -327 214 -345 214 -364 205 -411 205 -429 205 -438 205 -466 205 -476 205 -494 205 -494 205 -513 205 -513 195 -531 195 -531 195 -569 195 -569] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 1 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [56 -848 65 -857 130 -913 149 -931 158 -941 186 -968 149 -913 149 -913 130 -866 130 -773 130 -717 130 -680 112 -662 112 -624 112 -578 139 -522 195 -522 260 -522 409 -531 409 -615 409 -615 428 -634 400 -634 372 -634 298 -615 279 -624 260 -634 279 -736 279 -755] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [-56 -327 -46 -290 -37 -243 -28 -197 -28 -187 0 -159 9 -141 9 -132 19 -141 19 -150 28 -169 112 -243 149 -243 167 -243 214 -262 251 -262 279 -262 298 -252 316 -271 325 -280 353 -299 353 -299 353 -299 381 -327 391 -327 446 -355 325 -373 316 -373 279 -383 205 -373 177 -345] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0900707 0.0899924 + translation -85.7923 41.9814 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [1060 -20 1060 -20 1014 -2 1004 -2 995 -2 986 -11 976 -11 976 -11 949 -20 949 -20 939 -20 921 -48 921 -58 921 -58 902 -86 902 -86 902 -86 902 -104 902 -104 902 -113 902 -123 902 -132 902 -132 911 -151 911 -151 911 -151 921 -169 921 -169 921 -169 939 -188 939 -188 939 -188 949 -197 949 -197 949 -197 967 -206 967 -206 967 -206 986 -206 995 -206 1004 -206 1004 -206 1014 -206 1014 -206 1023 -206 1023 -206 1032 -206 1032 -206 1051 -197 1051 -197 1060 -197 1060 -197 1069 -188 1069 -188 1079 -179 1079 -179 1079 -169 1079 -169 1088 -169 1088 -169 1088 -169 1088 -151 1088 -151 1088 -141 1097 -123 1097 -104 1097 -86 1097 -86 1088 -67 1088 -67 1079 -48 1079 -48 1079 -48 1069 -39 1069 -39 1060 -30 1060 -30 1051 -30 1051 -30 1042 -20 1032 -20 1014 -11 1004 -11] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 1 2 1 2] + point Coordinate2D { + point [995 -225 995 -253 1014 -318 1014 -346 1014 -365 1014 -411 1014 -430 1014 -439 1014 -467 1014 -476 1014 -485 1014 -485 1014 -504 1014 -504 1014 -513 1014 -523 1014 -541 1023 -541] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 1 2 2 2 2 2 2] + point Coordinate2D { + point [809 -932 846 -932 893 -913 921 -913 958 -913 930 -895 930 -895 921 -895 893 -895 865 -774 865 -764 837 -699 921 -569 976 -541 1014 -523 1088 -578 1097 -588 1107 -597 1144 -625 1153 -634 1209 -690 1069 -644 1051 -662 1032 -681 1116 -792 1153 -792] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2] + point Coordinate2D { + point [688 -532 688 -485 716 -374 753 -355 800 -327 883 -262 939 -262 976 -262 1032 -281 1069 -262 1125 -234 1190 -309 1209 -327 1237 -355 1181 -383 1162 -402 1144 -420 1107 -448 1107 -476] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.0899064 0.08998 + translation -164.619 43.1904 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 2 1 2 2 2 2 2 1 2 1 2] + point Coordinate2D { + point [1878 -20 1851 -11 1841 -1 1813 -1 1804 -1 1804 -11 1795 -11 1785 -11 1767 -39 1767 -48 1758 -57 1739 -66 1739 -85 1739 -85 1739 -113 1739 -113 1739 -122 1748 -132 1748 -141 1748 -141 1758 -159 1758 -169 1758 -169 1767 -187 1767 -187 1767 -187 1776 -206 1776 -206 1776 -206 1785 -215 1785 -215 1785 -215 1804 -225 1804 -225 1804 -225 1823 -225 1823 -225 1823 -225 1841 -225 1841 -225 1841 -225 1860 -215 1860 -215 1869 -215 1869 -215 1878 -206 1878 -206 1888 -197 1888 -197 1897 -187 1897 -187 1906 -178 1906 -178 1916 -169 1916 -169 1916 -159 1916 -159 1925 -150 1925 -150 1925 -150 1925 -132 1925 -132 1925 -122 1925 -122 1934 -113 1934 -113 1934 -113 1934 -85 1925 -76 1925 -76 1906 -66 1906 -57 1906 -48 1906 -39 1897 -29 1897 -29 1878 -20 1878 -20 1869 -20 1869 -20 1860 -11 1860 -11 1851 -11 1841 -11 1823 -1 1804 -1] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 1 2 1 2] + point Coordinate2D { + point [1832 -234 1832 -271 1841 -327 1841 -364 1841 -383 1841 -411 1841 -429 1841 -429 1841 -466 1841 -466 1841 -466 1841 -485 1841 -485 1851 -485 1851 -485 1851 -494 1851 -494 1841 -494 1841 -494 1841 -531 1841 -531] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [1758 -959 1767 -931 1804 -894 1813 -866 1813 -857 1851 -792 1851 -792 1851 -792 1851 -792 1851 -792 1851 -792 1841 -801 1841 -801 1813 -829 1776 -708 1776 -699 1776 -662 1767 -606 1776 -578 1776 -578 1776 -550 1776 -550 1776 -550 1785 -559 1795 -559 1841 -559 1916 -550 1953 -513 1962 -504 1999 -485 1999 -485 1999 -485 2018 -466 2018 -466 2018 -466 2037 -457 2037 -457 2037 -457 2027 -457 2027 -457 2009 -476 1944 -606 1944 -652 1944 -652 1934 -680 1934 -680 1934 -680 1962 -662 1962 -662 1971 -652 2018 -615 2037 -606 2037 -606 2092 -597 2074 -597] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2] + point Coordinate2D { + point [1674 -476 1646 -448 1618 -411 1590 -383 1581 -373 1572 -345 1572 -345 1553 -327 1692 -262 1730 -252 1776 -243 1767 -225 1804 -243 1813 -243 1897 -243 1906 -243 1944 -243 1990 -271 1999 -280 2027 -308 2092 -383 2092 -411] + } + } + } + ] + } + ] + } + Transform2D { + scale 0.089974 0.090028 + translation -247.069 37.0465 + children [ + Transform2D { + children [ + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 2 1 2 1 2 1] + point Coordinate2D { + point [2715 -4 2687 -4 2660 -13 2641 -13 2641 -13 2622 -23 2622 -23 2613 -32 2585 -69 2585 -69 2585 -78 2576 -106 2576 -116 2576 -134 2567 -125 2576 -143 2576 -143 2576 -171 2576 -171 2576 -171 2594 -190 2594 -190 2594 -190 2604 -209 2604 -209 2604 -209 2613 -218 2613 -218 2613 -218 2632 -218 2632 -218 2632 -218 2650 -218 2650 -218 2650 -218 2669 -218 2669 -218 2669 -218 2687 -209 2687 -209 2687 -209 2706 -209 2706 -209 2715 -199 2715 -199 2725 -199 2725 -199 2734 -199 2734 -199 2743 -190 2743 -190 2753 -190 2753 -190 2762 -181 2762 -181 2762 -171 2762 -171 2771 -171 2771 -171 2771 -162 2771 -162 2780 -153 2780 -153 2780 -153 2780 -134 2780 -134 2780 -125 2780 -97 2780 -88 2780 -78 2771 -69 2771 -60 2771 -60 2762 -50 2762 -50 2762 -50 2753 -32 2753 -32 2753 -23 2753 -23 2743 -13 2743 -13 2734 -13 2734 -13 2715 5 2715 -4 2734 -13] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 1 2 1 2] + point Coordinate2D { + point [2660 -255 2660 -274 2660 -311 2660 -329 2660 -348 2660 -376 2660 -395 2660 -413 2660 -413 2660 -432 2660 -432 2660 -450 2660 -460 2660 -497 2660 -506] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2 2 2 2 2 2 2 2] + point Coordinate2D { + point [2464 -822 2483 -794 2520 -748 2539 -720 2539 -711 2576 -683 2567 -683 2529 -683 2483 -711 2446 -711 2427 -711 2474 -636 2474 -636 2492 -608 2539 -571 2567 -553 2576 -553 2585 -534 2594 -534 2622 -525 2641 -506 2669 -506 2697 -506 2734 -506 2762 -506 2780 -506 2873 -553 2883 -571 2883 -571 2892 -590 2892 -599 2892 -599 2901 -627 2901 -627 2901 -627 2911 -608 2911 -608 2920 -581 2957 -534 2957 -506] + } + } + } + Shape { + appearance USE APP + geometry Curve2D { + type [2 2 2 2 2 2] + point Coordinate2D { + point [2641 -292 2585 -311 2529 -320 2464 -320 2427 -320 2483 -274 2483 -274 2529 -227 2687 -227 2799 -227 2836 -227 2920 -227 2948 -236 2966 -246 3004 -264 3004 -274 3013 -292 3050 -320 3050 -329] + } + } + } + ] + } + ] + } + ] + } + ] + } + DEF TS TimeSensor { + cycleInterval IS runTime + loop IS loop + startTime IS start + } + DEF VAL Valuator { + Factor1 8 + } + DEF R1 ROUTE TS.fraction_changed TO VAL.inSFFloat + ROUTE VAL.outSFInt32 TO MYSWITCH.whichChoice +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["Each animated logo is an instance of a single proto" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Complexe proto Test" + } + FORESTGUMP { + translation -75 -75 + scale 1.1 1.1 + rotation 0.75 + runTime 0.5 + } + FORESTGUMP { + translation -100 50 + scale 1 1.5 + runTime 0.75 + start 2 + } + FORESTGUMP { + translation 0 10 + scale 1.8 1.8 + start 4 + } + DEF ANIM FORESTGUMP { + translation 75 -75 + rotation -1.25 + runTime 0.8 + start 6 + } + DEF TIMER TimeSensor { + cycleInterval 2 + loop TRUE + startTime 6 + } + DEF SI ScalarInterpolator { + key [0 1] + keyValue [0 6.283] + } + ] +} + +ROUTE TIMER.fraction_changed TO SI.set_fraction +ROUTE SI.value_changed TO ANIM.rotation + diff --git a/regression_tests/bifs-proto-mfurl.bt b/regression_tests/bifs-proto-mfurl.bt new file mode 100644 index 0000000..3aa8908 --- /dev/null +++ b/regression_tests/bifs-proto-mfurl.bt @@ -0,0 +1,78 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +PROTO testURL [ + exposedField MFString theURL [""] +] { + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF MYTEXT ImageTexture { + url IS theURL + } + } + geometry Bitmap {} + + } + ] + } +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This is a proto with an MF URL ISed field" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Proto with URL Test" + } + DEF testInstance testURL { + theURL ["10"] + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/logo.jpg" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-proto-multiple.bt b/regression_tests/bifs-proto-multiple.bt new file mode 100644 index 0000000..d4300ce --- /dev/null +++ b/regression_tests/bifs-proto-multiple.bt @@ -0,0 +1,180 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +PROTO PaletteElement [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFColor color 0 0 0 + exposedField SFFloat proto_ident 0 + eventOut SFBool isOver + eventOut SFInt32 active_proto +] { + Transform2D { + scale IS scale + translation IS translation + children [ + DEF TS TouchSensor { + isOver IS isOver + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor IS color + filled TRUE + lineProps LineProperties { + width 3 + } + } + } + geometry IndexedFaceSet2D { + coordIndex [0 1 2 3 4 5 -1] + coord Coordinate2D { + point [100 0 50 86.6 -50 86.6 -100 0 -50 -86.6 50 -86.6] + } + } + } + ] + } + DEF V Valuator { + outSFInt32 IS active_proto + Factor1 0 + Factor2 0 + Factor3 0 + Factor4 0 + Offset1 IS proto_ident + } + ROUTE TS.isActive TO V.inSFBool +} +PROTO Palette [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + eventOut SFColor selectedColor + eventOut MFString active +] { + DEF TR Transform2D { + scale IS scale + translation IS translation + children [ + DEF RedPaletteElement PaletteElement { + color 1 0 0 + proto_ident 1 + } + DEF GreenPaletteElement PaletteElement { + translation 150 86.6 + color 0 1 0 + proto_ident 2 + } + DEF BluePaletteElement PaletteElement { + translation -150 86.6 + color 0 0 1 + proto_ident 3 + } + ] + } + DEF ColorValuator Valuator { + outSFColor IS selectedColor + } + DEF RConditional Conditional { + buffer { + REPLACE ColorValuator.inSFColor BY 1 0 0 + } + } + DEF GConditional Conditional { + buffer { + REPLACE ColorValuator.inSFColor BY 0 1 0 + } + } + DEF BConditional Conditional { + buffer { + REPLACE ColorValuator.inSFColor BY 0 0 1 + } + } + DEF V2 Valuator { + outMFString IS active + } + ROUTE RedPaletteElement.isOver TO RConditional.activate + ROUTE GreenPaletteElement.isOver TO GConditional.activate + ROUTE BluePaletteElement.isOver TO BConditional.activate + ROUTE RedPaletteElement.active_proto TO V2.inSFInt32 + ROUTE GreenPaletteElement.active_proto TO V2.inSFInt32 + ROUTE BluePaletteElement.active_proto TO V2.inSFInt32 +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows a Proto using Protos" "Each palette element is a proto" "The whole palette is another proto" "with routes and conditionals" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Nested Proto test" + } + Transform2D { + translation 0 100 + children [ + Shape { + appearance Appearance { + material DEF Material2DNode Material2D { + filled TRUE + lineProps LineProperties { + width 3 + } + } + } + geometry Rectangle { + size 10 10 + } + } + ] + } + Transform2D { + scale 0.5 0.5 + translation 0 -50 + children [ + DEF P Palette {} + ] + } + Transform2D { + translation 0 50 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT Text { + string ["Active Palette"] + fontStyle FontStyle { + size 20 + } + } + } + ] + } + ] +} + +ROUTE P.selectedColor TO Material2DNode.emissiveColor +ROUTE P.active TO TXT.string + diff --git a/regression_tests/bifs-proto-nested.bt b/regression_tests/bifs-proto-nested.bt new file mode 100644 index 0000000..153de9a --- /dev/null +++ b/regression_tests/bifs-proto-nested.bt @@ -0,0 +1,132 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ] +} + +PROTO Palette [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + eventOut SFColor selectedColor +] { + PROTO PaletteElement [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFColor color 0 0 0 + eventOut SFBool isOver + ] { + Transform2D { + scale IS scale + translation IS translation + children [ + TouchSensor { + isOver IS isOver + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor IS color + filled TRUE + lineProps LineProperties { + width 3 + } + } + } + geometry IndexedFaceSet2D { + coordIndex [0 1 2 3 4 5 -1] + coord Coordinate2D { + point [100 0 50 86.6 -50 86.6 -100 0 -50 -86.6 50 -86.6] + } + } + } + ] + } + } + DEF TR Transform2D { + scale IS scale + translation IS translation + children [ + DEF RedPaletteElement PaletteElement { + color 1 0 0 + } + ] + } + DEF ColorValuator Valuator { + outSFColor IS selectedColor + } + DEF RConditional Conditional { + buffer { + REPLACE ColorValuator.inSFColor BY 1 0 0 + } + } + DEF Conditional Conditional { + buffer { + REPLACE ColorValuator.inSFColor BY 0.8 0.8 0.8 + } + } + ROUTE RedPaletteElement.isOver TO RConditional.activate + ROUTE RedPaletteElement.isOver TO Conditional.reverseActivate +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows a Proto using DEF/USE Protos" "declared inside the proto itself" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Nested Proto test" + } + Transform2D { + translation 0 100 + children [ + Shape { + appearance Appearance { + material DEF Material2DNode Material2D { + filled TRUE + lineProps LineProperties { + width 3 + } + } + } + geometry Rectangle { + size 10 10 + } + } + ] + } + Transform2D { + scale 0.5 0.5 + translation -100 -50 + children [ + DEF P Palette {} + ] + } + Transform2D { + scale 0.5 0.5 + translation 50 -50 + children [ + Palette {} + ] + } + ] +} + +ROUTE P.selectedColor TO Material2DNode.emissiveColor + diff --git a/regression_tests/bifs-proto-route.bt b/regression_tests/bifs-proto-route.bt new file mode 100644 index 0000000..bf4ee24 --- /dev/null +++ b/regression_tests/bifs-proto-route.bt @@ -0,0 +1,110 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO GEOMETRY_PROTO [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] { + Transform2D { + rotationAngle IS rotation + scale IS scale + translation IS translation + children [ + DEF S Shape { + geometry IS obj + appearance Appearance { + material DEF M Material2D { + emissiveColor IS Color + filled IS filled + transparency IS transparency + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + } + DEF TOUCH TouchSensor {} + ] + } + DEF TS TimeSensor { + loop TRUE + } + DEF C Conditional { + buffer { + DELETE ROUTE RTS + } + } + DEF RC Conditional { + buffer { + INSERT ROUTE DEF RTS TS.fraction_changed TO M.transparency + } + } + DEF RTS ROUTE TS.fraction_changed TO M.transparency + ROUTE TOUCH.isActive TO C.activate + ROUTE TOUCH.isActive TO RC.reverseActivate +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of ROUTES inside a proto" "with deletion/insertion through conditionals" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Simple proto Test" + } + GEOMETRY_PROTO { + translation 200 0 + scale 1 1.5 + rotation 0.78 + Color 1 0 1 + transparency 0.75 + lineColor 1 0 0 + lineWidth 2 + obj DEF C Circle { + radius 75 + } + } + Transform2D { + translation -300 0 + children [ + GEOMETRY_PROTO { + translation 200 0 + scale 1 1.5 + rotation 0.78 + Color 0 0 1 + obj USE C + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-proto-sftime-protocode.bt b/regression_tests/bifs-proto-sftime-protocode.bt new file mode 100644 index 0000000..98d1ec1 --- /dev/null +++ b/regression_tests/bifs-proto-sftime-protocode.bt @@ -0,0 +1,99 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO GEOMETRY_PROTO [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] { + DEF TR Transform2D { + scale IS scale + translation IS translation + children [ + Shape { + geometry IS obj + appearance Appearance { + material Material2D { + emissiveColor IS Color + filled IS filled + transparency IS transparency + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + } + DEF TS TimeSensor { + cycleInterval 4 + loop TRUE + } + ] + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1.76 0] + } + ROUTE TS.fraction_changed TO SI.set_fraction + ROUTE SI.value_changed TO TR.rotationAngle +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["The animation start time embedded in proto body" "Animation shall begin at node creation time" "if objects rotations are in sync the player is broken" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Proto SFTime Test" + } + DEF ROOT Transform2D { + children [ + GEOMETRY_PROTO { + translation -120 0 + Color 0 1 1 + obj Rectangle { + size 200 100 + } + } + ] + } + ] +} + + +AT 2000 { + APPEND TO ROOT.children GEOMETRY_PROTO { + translation 120 0 + Color 1 0 1 + lineColor 1 0 0 + obj Rectangle { + size 200 100 + } + } +} + diff --git a/regression_tests/bifs-proto-sftime-protointerface.bt b/regression_tests/bifs-proto-sftime-protointerface.bt new file mode 100644 index 0000000..07b4dd2 --- /dev/null +++ b/regression_tests/bifs-proto-sftime-protointerface.bt @@ -0,0 +1,104 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO GEOMETRY_PROTO [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFTime start 0 + exposedField SFNode obj NULL +] { + DEF TR Transform2D { + scale IS scale + translation IS translation + children [ + Shape { + geometry IS obj + appearance Appearance { + material Material2D { + emissiveColor IS Color + filled IS filled + transparency IS transparency + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + } + DEF TS TimeSensor { + cycleInterval 4 + loop TRUE + startTime IS start + } + ] + } + DEF SI ScalarInterpolator { + key [0 0.5 1] + keyValue [0 1.5708 0] + } + ROUTE TS.fraction_changed TO SI.set_fraction + ROUTE SI.value_changed TO TR.rotationAngle +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["The animation start time is part of proto interface" "Second object appears at 2 sec and starts moving at 4 sec(2+2)" "If objects rotations are not in sync the player is broken" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Proto SFTime Test" + } + DEF ROOT Transform2D { + children [ + GEOMETRY_PROTO { + translation -120 0 + Color 0 1 1 + obj Rectangle { + size 200 100 + } + } + ] + } + ] +} + + +AT 2000 { + APPEND TO ROOT.children GEOMETRY_PROTO { + translation 120 0 + Color 1 0 1 + transparency 0.75 + lineColor 1 0 0 + lineWidth 2 + start 2 + obj Rectangle { + size 200 100 + } + } +} + diff --git a/regression_tests/bifs-proto-simple.bt b/regression_tests/bifs-proto-simple.bt new file mode 100644 index 0000000..bfa2add --- /dev/null +++ b/regression_tests/bifs-proto-simple.bt @@ -0,0 +1,88 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO GEOMETRY_PROTO [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] { + Transform2D { + rotationAngle IS rotation + scale IS scale + translation IS translation + children [ + Shape { + geometry IS obj + appearance Appearance { + material Material2D { + emissiveColor IS Color + filled IS filled + transparency IS transparency + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + } + ] + } +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows simple proto usage" "The shapes are all instances of a single proto" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Simple proto Test" + } + GEOMETRY_PROTO { + translation -100 0 + Color 0 1 1 + obj Rectangle { + size 100 100 + } + } + GEOMETRY_PROTO { + translation 100 0 + scale 1 1.5 + rotation 0.78 + Color 1 0 1 + transparency 0.75 + lineColor 1 0 0 + lineWidth 2 + obj Circle { + radius 75 + } + } + ] +} + + diff --git a/regression_tests/bifs-proto-use.bt b/regression_tests/bifs-proto-use.bt new file mode 100644 index 0000000..414565d --- /dev/null +++ b/regression_tests/bifs-proto-use.bt @@ -0,0 +1,93 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +PROTO GEOMETRY_PROTO [ + exposedField SFVec2f translation 0 0 + exposedField SFVec2f scale 1 1 + exposedField SFFloat rotation 0 + exposedField SFColor Color 1 1 1 + exposedField SFBool filled TRUE + exposedField SFFloat transparency 0 + exposedField SFColor lineColor 0 0 0 + exposedField SFFloat lineWidth 0 + exposedField SFNode obj NULL +] { + Transform2D { + rotationAngle IS rotation + scale IS scale + translation IS translation + children [ + DEF S Shape { + geometry IS obj + appearance Appearance { + material Material2D { + emissiveColor IS Color + filled IS filled + transparency IS transparency + lineProps LineProperties { + lineColor IS lineColor + width IS lineWidth + } + } + } + } + Transform2D { + translation -100 0 + children [ + USE S + ] + } + ] + } +} +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of DEF/USE inside a proto" "and DEF/USE of a proto" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Simple proto Test" + } + DEF G GEOMETRY_PROTO { + translation 200 0 + scale 1 1.5 + rotation 0.78 + Color 1 0 1 + transparency 0.75 + lineColor 1 0 0 + lineWidth 2 + obj Circle { + radius 75 + } + } + Transform2D { + translation -300 0 + children [ + USE G + ] + } + ] +} + + diff --git a/regression_tests/bifs-script-char-to-int.bt b/regression_tests/bifs-script-char-to-int.bt new file mode 100644 index 0000000..b559ead --- /dev/null +++ b/regression_tests/bifs-script-char-to-int.bt @@ -0,0 +1,87 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows script sending eventOuts" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Script eventOut test" + } + Transform2D { + translation -150 -120 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TEXT Text { + string ["What"] + fontStyle FontStyle { + justify ["BEGIN" "BEGIN"] + size 20 + } + } + } + ] + } + Transform2D { + translation 150 -120 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TEXT_TEST Text { + string ["T"] + fontStyle FontStyle { + justify ["BEGIN" "BEGIN"] + size 20 + } + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 0.1 + loop TRUE + } + DEF SCRIPT Script { + eventIn SFTime act + field SFInt32 int_val 0 + field SFNode txt USE TEXT + field SFNode txt2 USE TEXT_TEST + url ["javascript: function initialize(value, timestamp) {int_val = txt.string[0].charCodeAt(0);txt2.string[0] = 'char val: ' + String.fromCharCode(int_val) + ' ' + int_val;}" ] + } + ] +} + + diff --git a/regression_tests/bifs-script-child-create.bt b/regression_tests/bifs-script-child-create.bt new file mode 100644 index 0000000..4a276b1 --- /dev/null +++ b/regression_tests/bifs-script-child-create.bt @@ -0,0 +1,61 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows script creating a new node in an MF field" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Script Node Creation test" + } + Transform2D { + children [ + DEF TS TouchSensor {} + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + ] + } + DEF TR Transform2D { + translation -150 -120 + } + DEF SCRIPT Script { + eventIn SFBool act + field SFNode t USE TR + url ["javascript: function act(value, timestamp) {if (!value) return;t.children[0] = new SFNode('Shape');t.children[0].geometry = new SFNode('Rectangle');t.children[0].geometry.size = new SFVec2f(100, 50);t.children[0].appearance = new SFNode('Appearance');t.children[0].appearance.material = new SFNode('Material2D');t.children[0].appearance.material.filled = true;t.children[0].appearance.material.emissiveColor = new SFColor(0, 0, 1);}" ] + } + ] +} + +ROUTE TS.isActive TO SCRIPT.act + diff --git a/regression_tests/bifs-script-date.bt b/regression_tests/bifs-script-date.bt new file mode 100644 index 0000000..7875605 --- /dev/null +++ b/regression_tests/bifs-script-date.bt @@ -0,0 +1,64 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 260 + pixelHeight 70 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows script used to retrieve system time" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Script Date() test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT Text { + string ["MPEG4 time on your system"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + DEF TIMER TimeSensor { + loop TRUE + } + DEF SC Script { + eventIn SFTime set_time + field SFNode str USE TXT + url ["javascript:function set_time(value, text) {today = new Date();the_day = today.getDate();the_weekday = today.getDay();the_month = today.getMonth();the_year = today.getYear();the_hour = today.getHours();the_minute = today.getMinutes();the_second = today.getSeconds();am_pm = 0;the_initials = 'a.m.';if (the_year < 1900) the_year = the_year + 1900;if ((the_hour >=2) && (the_hour <=11)) {am_pm = the_hour;the_initials = 'a.m.';} else if (the_hour == 0) {am_pm = 12;the_initials = 'a.m.';} else if (the_hour == 12) {am_pm = 12;the_initials = 'p.m.';} else if (the_hour >=13) {am_pm = the_hour - 12;the_initials = 'p.m.';}if (the_minute <=9) the_minute = '0' + (the_minute);if (the_second <=9) the_second = '0' + (the_second);if (the_month == '0') the_month = 'January';else if (the_month == '1') the_month = 'February';else if (the_month == '2') the_month = 'March';else if (the_month == '3') the_month = 'April';else if (the_month == '4') the_month = 'May';else if (the_month == '5') the_month = 'June';else if (the_month == '6') the_month = 'July';else if (the_month == '7') the_month = 'August';else if (the_month == '8') the_month = 'September';else if (the_month == '9') the_month = 'October';else if (the_month == '10') the_month = 'November';else if (the_month == '11') the_month = 'December';if (the_weekday == '0') the_weekday = 'Sunday';else if (the_weekday == '1') the_weekday = 'Monday';else if (the_weekday == '2') the_weekday = 'Tuesday';else if (the_weekday == '3') the_weekday = 'Wednesday';else if (the_weekday == '4') the_weekday = 'Thursday';else if (the_weekday == '5') the_weekday = 'Friday';else if (the_weekday == '6') the_weekday = 'Saturday';if (the_day == '1') the_day = '1st';else if (the_day == '2') the_day = '2nd';else if (the_day == '3') the_day = '3rd';else if (the_day == '4') the_day = '4th';else if (the_day == '21') the_day = '21st';else if (the_day == '22') the_day = '23rd';else if (the_day == '31') the_day = '31st';else the_day = the_day + 'th';str.string[1] = the_weekday + ' ' + the_day + ' ' + the_month + ', ' + the_year;str.string[2] = am_pm + ':' + the_minute + ':' + the_second + ' ' + the_initials;}function initialize() {set_time(0, 0);}" ] + } + ] +} + +ROUTE TIMER.cycleTime TO SC.set_time + diff --git a/regression_tests/bifs-script-event-out.bt b/regression_tests/bifs-script-event-out.bt new file mode 100644 index 0000000..58efb3d --- /dev/null +++ b/regression_tests/bifs-script-event-out.bt @@ -0,0 +1,85 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows script sending eventOuts" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Script eventOut test" + } + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry DEF IFS IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 4 5] + coord Coordinate2D { + point [-120 0 -50 100 50 100 120 0 50 -100 -50 -100] + } + } + } + Transform2D { + translation -150 -120 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TEXT Text { + string [""] + fontStyle FontStyle { + justify ["BEGIN" "BEGIN"] + size 20 + } + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 0.1 + loop TRUE + } + DEF SCRIPT Script { + eventIn SFTime act + field SFInt32 count 0 + eventOut SFFloat transp + eventOut MFString txt + url ["javascript: function act(value, timestamp) {count++;if (count>= 20) count = 0;transp = count / 20;txt[0] = 'transparency: ' + transp; }" ] + } + ] +} + +ROUTE TS.cycleTime TO SCRIPT.act +ROUTE SCRIPT.transp TO MAT.transparency +ROUTE SCRIPT.txt TO TEXT.string + diff --git a/regression_tests/bifs-script-initialize.bt b/regression_tests/bifs-script-initialize.bt new file mode 100644 index 0000000..b903f81 --- /dev/null +++ b/regression_tests/bifs-script-initialize.bt @@ -0,0 +1,86 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows script Initialize usage" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Script initialize test" + } + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry DEF IFS IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 4 5] + coord Coordinate2D { + point [-120 0 -50 100 50 100 120 0 50 -100 -50 -100] + } + } + } + Transform2D { + translation -150 -120 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TEXT Text { + string [""] + fontStyle FontStyle { + family ["SANS"] + justify ["BEGIN" "BEGIN"] + size 20 + } + } + } + ] + } + DEF TS TimeSensor { + cycleInterval 0.2 + loop TRUE + } + DEF SCRIPT Script { + eventIn SFTime act + field SFInt32 count 0 + eventOut SFFloat transp + eventOut MFString txt + url ["javascript: function initialize(value, timestamp) {my_global_string = 'global int = ';my_global_int = 10;}function act(value, timestamp) {count++;if (count>= 20) count = 0;transp = count / 20;txt[0] = 'transparency: ' + transp; txt[1] = my_global_string + my_global_int; }" ] + } + ] +} + +ROUTE TS.cycleTime TO SCRIPT.act +ROUTE SCRIPT.transp TO MAT.transparency +ROUTE SCRIPT.txt TO TEXT.string + diff --git a/regression_tests/bifs-script-load-url.bt b/regression_tests/bifs-script-load-url.bt new file mode 100644 index 0000000..459c5e4 --- /dev/null +++ b/regression_tests/bifs-script-load-url.bt @@ -0,0 +1,67 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows script loading URL" "" "GPAC Regression Tests" "$Date: 2008/07/18 17:27:50 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Script URL loading" + } + Transform2D { + children [ + DEF TS TouchSensor {} + Shape { + appearance Appearance { + material DEF MAT Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 50 50 + } + } + ] + } + DEF TR Transform2D { + translation -150 -120 + } + DEF SCRIPT Script { + eventIn SFBool act + field SFNode t USE TR + url ["javascript: + function act(value, timestamp) { + if (!value) return; + str = new MFString('http://gpac.sourceforge.net'); + Browser.loadURL(str) ; + } + " ] + } + ] +} + +ROUTE TS.isActive TO SCRIPT.act + diff --git a/regression_tests/bifs-script-node-access.bt b/regression_tests/bifs-script-node-access.bt new file mode 100644 index 0000000..83e91eb --- /dev/null +++ b/regression_tests/bifs-script-node-access.bt @@ -0,0 +1,86 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows script modifying nodes directly" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "Node Modification Script test" + } + Shape { + appearance Appearance { + material DEF MAT Material2D { + filled TRUE + } + } + geometry DEF IFS IndexedFaceSet2D { + colorIndex [0 1 2 3 4 5] + coordIndex [0 1 2 3 4 5] + color Color { + color [0 0 1 0 1 0 1 0 0 1 1 0 1 0 1 0 1 1] + } + coord Coordinate2D { + point [-120 0 -50 100 50 100 120 0 50 -100 -50 -100] + } + } + } + DEF TS TimeSensor { + loop TRUE + } + Transform2D { + translation -150 -120 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TEXT Text { + string [""] + fontStyle FontStyle { + family ["SANS"] + justify ["BEGIN" "BEGIN"] + size 20 + } + } + } + ] + } + DEF SCRIPT Script { + eventIn SFTime act + field SFNode n USE IFS + field SFNode t USE TS + field SFNode txt USE TEXT + field SFInt32 roll 0 + url ["javascript: function act(value, timestamp) {roll++;count = n.colorIndex.length;first = n.colorIndex[0];for (i=0; i <"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 26 + } + } + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-text-vrml-alignment.bt b/regression_tests/bifs-text-vrml-alignment.bt new file mode 100644 index 0000000..dc506b5 --- /dev/null +++ b/regression_tests/bifs-text-vrml-alignment.bt @@ -0,0 +1,161 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 1 + visualProfileLevelIndication 1 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSv2Config { + isCommandStream true + pixelMetric true + pixelWidth 480 + pixelHeight 360 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows VRML text basline alignment" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "VRML Text alignmentSRT importing Test" + } + Transform2D { + translation -200 150 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Conformance Test"] + fontStyle FontStyle { + family ["Courier"] + justify ["BEGIN" "MIDDLE"] + size 15 + style "BOLD" + } + } + } + ] + } + Transform2D { + translation -200 40 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Sample from VRML 97 Spec (Section 6.20.1)" "FontStyle Parameters:" " family \"Courier\"" " size 20" " spacing 1.5" " justify [ \"BEGIN\" \"FIRST\" ]" "Text Position is (0,0)" "Thin lines are drawn at Y = 20, Y = -10, Y =-40." "Thick lines are drawn at Y = 0, Y = -30, Y =-60." "According to VRML, thick lines should match baseline."] + fontStyle FontStyle { + family ["Courier"] + justify ["BEGIN" "MIDDLE"] + size 15 + } + } + } + ] + } + Transform2D { + translation -215 -80 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Text { + string ["(0,0)"] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 15 + } + } + } + ] + } + Transform2D { + translation -200 -80 + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + width 2 + } + } + } + geometry IndexedLineSet2D { + coordIndex [0 1 -1 2 3 -1 4 5 -1] + coord Coordinate2D { + point [0 0 400 0 0 -30 400 -30 0 -60 400 -60] + } + } + } + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties {} + + } + } + geometry IndexedLineSet2D { + coordIndex [0 1 -1 2 3 -1 4 5 -1 6 7 -1 8 9 -1] + coord Coordinate2D { + point [0 20 400 20 0 -10 400 -10 0 -40 400 -40 0 20 0 -60 400 20 400 -60] + } + } + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Text { + string ["This is a line of text." "This is the 2nd line of text." "This is the third."] + fontStyle FontStyle { + family ["Courier"] + justify ["BEGIN" "FIRST"] + size 20 + spacing 1.5 + } + } + } + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Circle { + radius 2 + } + } + ] + } + ] +} + + diff --git a/regression_tests/bifs-timeline-mediacontrol-OCR.bt b/regression_tests/bifs-timeline-mediacontrol-OCR.bt new file mode 100644 index 0000000..c22f60c --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-OCR.bt @@ -0,0 +1,245 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 400 + pixelHeight 400 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows media streams synchronized to an empty OCR stream" "and mediaControl controling the OCR playback" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Animation Stream" + } + Transform2D { + translation 0 160 + children [ + Shape { + appearance DEF TXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Click on video to switch streams"] + fontStyle DEF TXTFT FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 16 + } + } + } + ] + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF MT MovieTexture { + url [od:12] + } + } + geometry Bitmap {} + + } + DEF TS TouchSensor {} + ] + } + Transform2D { + translation -120 -160 + children [ + Shape { + appearance Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 40 20 + } + } + Shape { + appearance USE TXTAPP + geometry DEF TXT Text { + string ["Stop"] + fontStyle USE TXTFT + } + } + DEF TS2 TouchSensor {} + ] + } + Transform2D { + translation 40 -160 + children [ + Transform2D { + translation -30 0 + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["OCR Time:"] + fontStyle USE TXTFT + } + } + ] + } + Transform2D { + translation 60 0 + children [ + Shape { + appearance USE TXTAPP + geometry DEF OCR_TIME Text { + string ["Stop"] + fontStyle USE TXTFT + } + } + ] + } + ] + } + Transform2D { + translation 40 -180 + children [ + Transform2D { + translation -30 0 + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["OCR Duration:"] + fontStyle USE TXTFT + } + } + ] + } + Transform2D { + translation 60 0 + children [ + Shape { + appearance USE TXTAPP + geometry DEF OCR_DUR Text { + string ["Stop"] + fontStyle USE TXTFT + } + } + ] + } + ] + } + DEF C Conditional { + buffer { + REPLACE MT.url BY ["od:12"] + REPLACE ROUTE R1 BY TS.isActive TO RC.activate + } + } + DEF RC Conditional { + buffer { + REPLACE MT.url BY ["od:10"] + REPLACE ROUTE R1 BY TS.isActive TO C.activate + } + } + DEF MC MediaControl { + url [od:8] + loop TRUE + preRoll FALSE + } + DEF MS MediaSensor { + url [od:8] + } + DEF VMS Valuator {} + DEF VMS2 Valuator {} + DEF C2 Conditional { + buffer { + REPLACE MC.mediaSpeed BY 0 + REPLACE TXT.string BY ["Play"] + REPLACE ROUTE R2 BY TS2.isActive TO RC2.activate + } + } + DEF RC2 Conditional { + buffer { + REPLACE MC.mediaSpeed BY 1 + REPLACE TXT.string BY ["Stop"] + REPLACE ROUTE R2 BY TS2.isActive TO C2.activate + } + } + ] +} + +DEF R1 ROUTE TS.isActive TO C.activate +DEF R2 ROUTE TS2.isActive TO C2.activate +ROUTE MS.mediaCurrentTime TO VMS.inSFTime +ROUTE VMS.outMFString TO OCR_TIME.string +ROUTE MS.mediaDuration TO VMS2.inSFTime +ROUTE VMS2.outMFString TO OCR_DUR.string + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 8 + OCR_ES_ID 8 + decConfigDescr DecoderConfigDescriptor { + objectTypeIndication 1 + streamType 2 + } + muxInfo MuxInfo { + duration 10000 + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 5 + OCR_ES_ID 8 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 12 + esDescr [ + ES_Descriptor { + ES_ID 4 + OCR_ES_ID 8 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-audio-speed.bt b/regression_tests/bifs-timeline-mediacontrol-audio-speed.bt new file mode 100644 index 0000000..08859d8 --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-audio-speed.bt @@ -0,0 +1,214 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows MediaControl" "controling audio playback speed" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Media Control Test" + } + Sound2D { + source AudioSource { + url [od:10] + stopTime -1 + numChan 2 + } + } + DEF MC MediaControl { + url [od:10] + loop TRUE + } + Transform2D { + translation -100 0 + children [ + DEF TS1 TouchSensor {} + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Circle { + radius 30 + } + } + Transform2D { + children [ + Shape { + appearance DEF TXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT_FAST Text { + string ["Faster" "x 2"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + ] + } + Transform2D { + translation 100 0 + children [ + DEF TS2 TouchSensor {} + Shape { + appearance Appearance { + material DEF M2 Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Circle { + radius 30 + } + } + Transform2D { + children [ + Shape { + appearance USE TXTAPP + geometry DEF TXT_SLOW Text { + string ["Slower" "x 0.5"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + children [ + DEF TS3 TouchSensor {} + Shape { + appearance Appearance { + material DEF M3 Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 30 + } + } + Transform2D { + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["Mute"] + fontStyle USE FS + } + } + ] + } + ] + } + DEF C1 Conditional { + buffer { + REPLACE MC.mediaSpeed BY 2 + REPLACE TXT_FAST.string BY ["Faster", "x 2"] + REPLACE M1.emissiveColor BY 1 0.5 0.5 + } + } + DEF AC1 Conditional { + buffer { + REPLACE MC.mediaSpeed BY 4 + REPLACE TXT_FAST.string BY ["Faster", "x 4"] + REPLACE M1.emissiveColor BY 1 0.5 0.5 + } + } + DEF C2 Conditional { + buffer { + REPLACE MC.mediaSpeed BY 0.5 + REPLACE TXT_SLOW.string BY ["Slower", "x 0.5"] + REPLACE M2.emissiveColor BY 0.5 0.5 1 + } + } + DEF AC2 Conditional { + buffer { + REPLACE MC.mediaSpeed BY 0.25 + REPLACE TXT_SLOW.string BY ["Slower", "x 0.25"] + REPLACE M2.emissiveColor BY 0.5 0.5 1 + } + } + DEF C3 Conditional { + buffer { + REPLACE MC.mute BY TRUE + REPLACE M3.emissiveColor BY 0.5 1 0.5 + } + } + DEF RESET Conditional { + buffer { + REPLACE MC.mediaSpeed BY 1 + REPLACE MC.mute BY FALSE + REPLACE M1.emissiveColor BY 1 0 0 + REPLACE M2.emissiveColor BY 0 0 1 + REPLACE M3.emissiveColor BY 0 1 0 + } + } + ] +} + +ROUTE TS1.isOver TO C1.activate +ROUTE TS1.isOver TO RESET.reverseActivate +ROUTE TS1.isActive TO AC1.activate +ROUTE TS1.isActive TO C1.reverseActivate +ROUTE TS2.isOver TO C2.activate +ROUTE TS2.isOver TO RESET.reverseActivate +ROUTE TS2.isActive TO AC2.activate +ROUTE TS2.isActive TO C2.reverseActivate +ROUTE TS3.isOver TO C3.activate +ROUTE TS3.isOver TO RESET.reverseActivate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_english.mp3" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-audio.bt b/regression_tests/bifs-timeline-mediacontrol-audio.bt new file mode 100644 index 0000000..e3d0ad5 --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-audio.bt @@ -0,0 +1,352 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows complex scripting and MediaControl" "providing complete control of the media" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.4 $" "(C) 2002-2004 GPAC Team"] + title "Media Control Test" + } + Sound2D { + source AudioSource { + url [od:10] + stopTime -1 + } + } + DEF MC MediaControl { + url [od:10] + preRoll FALSE + } + DEF MS MediaSensor { + url [od:10] + } + DEF PANEL Transform2D { + translation 0 80 + children [ + DEF TRPAUSE Transform2D { + translation -100 0 + children [ + DEF PAUSE TouchSensor {} + DEF WHICHBUT Switch { + whichChoice 0 + choice [ + Shape { + appearance DEF BUTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 4 5 6 7 -1] + coord Coordinate2D { + point [-8 7.5 -3 7.5 -3 -7.5 -8 -7.5 0 7.5 5 7.5 5 -7.5 0 -7.5] + } + } + } + Shape { + appearance USE BUTAPP + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coord Coordinate2D { + point [-8 7.5 -8 -7.5 5 0] + } + } + } + ] + } + ] + } + DEF TRSTOP Transform2D { + translation -60 0 + children [ + DEF STOP TouchSensor {} + Shape { + appearance USE BUTAPP + geometry Rectangle { + size 15 15 + } + } + ] + } + DEF TRFF Transform2D { + translation -20 0 + children [ + DEF FASTF TouchSensor {} + Shape { + appearance USE BUTAPP + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 4 5 6 7 -1] + coord Coordinate2D { + point [-8 7.5 -5 0 -8 -7.5 2 0 -2 7.5 1 0 -2 -7.5 8 0] + } + } + } + ] + } + DEF TRLOOP Transform2D { + translation 20 0 + children [ + DEF LOOP TouchSensor {} + Shape { + appearance Appearance { + material DEF MAT_LOOP Material2D { + emissiveColor 0 0 0 + } + } + geometry Circle { + radius 6 + } + } + ] + } + Transform2D { + translation 0 -160 + children [ + DEF TS TouchSensor {} + Transform2D { + children [ + Shape { + appearance USE BUTAPP + geometry DEF PROGBAR Rectangle { + size 200 15 + } + } + ] + } + DEF CURS Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0 0 1 + width 4 + } + } + } + geometry Rectangle { + size 6 15 + } + } + ] + } + ] + } + DEF TIMER Transform2D { + translation 80 0 + children [ + DEF RTIME TouchSensor {} + Shape { + appearance USE BUTAPP + geometry DEF TIME Text { + string [""] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + ] + } + DEF SC Script { + eventIn SFTime set_cursor + eventIn SFVec3f set_seek + eventIn SFBool restart + eventIn SFBool rev_time + eventIn SFBool pause + eventIn SFBool stop + eventIn SFBool set_loop + eventIn SFBool set_ff + eventIn SFTime set_duration + field SFNode loop USE MAT_LOOP + field SFNode app USE CURS + field SFNode ctrl USE MC + field SFNode bar USE PROGBAR + field SFNode txt USE TIME + field SFNode but USE WHICHBUT + field SFNode pan USE PANEL + field SFTime curSeek 0 + field SFInt32 isDown 0 + field SFBool reverse FALSE + field SFBool isPaused FALSE + field SFBool isLooping FALSE + field SFTime duration -1 + eventOut SFFloat fraction_changed + url ["javascript: + function set_duration(value, timestamp) { + duration = value; + } + function set_loop(value, timestamp) { + if (!value) return; + isLooping = !isLooping; + loop.filled = isLooping; + ctrl.loop = isLooping; + } + function pause(value, timestamp) { + if (!value) return; + if (isPaused) { + ctrl.mediaSpeed = 1; + but.whichChoice = 0; + } else { + ctrl.mediaSpeed = 0; + but.whichChoice = 1; + ctrl.mediaStartTime = -1; + } + isPaused = !isPaused; + } + function stop(value, timestamp) { + if (!value) return; + txt.string[0] = ''; + app.translation.x = - bar.size.x / 2; + ctrl.mediaStartTime = 0; + ctrl.mediaSpeed = 0; + but.whichChoice = 1; + isPaused = 1; + } + function rev_time(value, timestamp) { + if (value) reverse = !reverse; + } + function set_time(value, timestamp) { + timing = new String(''); + if (duration>=0) { + if (!isLooping && value + 0.1 >=duration) { + stop(true, timestamp); + return; + } + } + if (isPaused) return; + if (duration>=0 && reverse) { + value = duration - value; + if (value<0) value=0; + timing += '-'; + } else { + timing += ' '; + } + hours = 0;mins = 0;secs = 0; + if (value) { + hours = Math.floor(value/3600); + value -= hours*3600; + mins = Math.floor(value / 60); + value -= mins*60; + secs = Math.floor(value); + } + if (hours<10) timing += '0'; + timing += hours + ':'; + if (mins<10) timing += '0'; + timing += mins + ':'; + if (secs<10) timing += '0'; + timing += secs; + txt.string[0] = timing; + } + function set_cursor(value, timestamp) { + set_time(value, timestamp); + if (duration<0) return; + if (ctrl.mediaSpeed && isDown != 1) { + if (value>duration) value = duration; + pos = value; + pos /= duration; + app.translation.x = bar.size.x * (pos - 0.5); + fraction_changed = pos; + } + } + function set_seek(myval, timestamp) { + if (duration<0) return; + pos = myval[0]; + pos /= bar.size.x; + pos += 0.5; + if (pos>1) pos = 0; + curSeek = duration; + curSeek *= pos; + if (isDown == 1) { + app.translation.x = bar.size.x*(pos - 0.5); + } else if (isDown==2) { + isDown = 0; + } + } + function restart(myval, timestamp) { + if (myval) { + ctrl.mediaSpeed = 0; + isDown = 1; + } else if (isDown == 1) { + ctrl.mediaStartTime = curSeek; + ctrl.mediaSpeed = 1; + isDown = 2; + but.whichChoice = 0; + isPaused = FALSE; + } + } + function set_ff(value, timestamp) { + ctrl.mediaStartTime = -1; + if (value) { + ctrl.mediaSpeed = 2; + } else { + ctrl.mediaSpeed = 1; + } + } + " ] + } + ] +} + +ROUTE MS.mediaCurrentTime TO SC.set_cursor +ROUTE TS.hitPoint_changed TO SC.set_seek +ROUTE TS.isActive TO SC.restart +ROUTE RTIME.isActive TO SC.rev_time +ROUTE PAUSE.isActive TO SC.pause +ROUTE STOP.isActive TO SC.stop +ROUTE LOOP.isActive TO SC.set_loop +ROUTE FASTF.isActive TO SC.set_ff +ROUTE MS.mediaDuration TO SC.set_duration +ROUTE FASTF.isActive TO SC.set_ff + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_audio.aac" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-complete.bt b/regression_tests/bifs-timeline-mediacontrol-complete.bt new file mode 100644 index 0000000..4f0d168 --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-complete.bt @@ -0,0 +1,266 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 240 + pixelHeight 200 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows complex scripting and MediaControl" "providing complete control of the media" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.4 $" "(C) 2002-2004 GPAC Team"] + title "Media Control Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF MT MovieTexture { + url [od:8] + } + } + geometry Bitmap {} + + } + ] + } + Sound2D { + source AudioSource { + url [od:10] + stopTime -1 + } + } + DEF MC MediaControl { + url [od:10] + preRoll FALSE + } + DEF MS MediaSensor { + url [od:8] + } + DEF PANEL Transform2D { + translation 0 85 + children [ + DEF TRPAUSE Transform2D { + translation -100 0 + children [ + DEF PAUSE TouchSensor {} + DEF WHICHBUT Switch { + whichChoice 0 + choice [ + Shape { + appearance DEF BUTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 4 5 6 7 -1] + coord Coordinate2D { + point [-8 7.5 -3 7.5 -3 -7.5 -8 -7.5 0 7.5 5 7.5 5 -7.5 0 -7.5] + } + } + } + Shape { + appearance USE BUTAPP + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coord Coordinate2D { + point [-8 7.5 -8 -7.5 5 0] + } + } + } + ] + } + ] + } + DEF TRSTOP Transform2D { + translation -60 0 + children [ + DEF STOP TouchSensor {} + Shape { + appearance USE BUTAPP + geometry Rectangle { + size 15 15 + } + } + ] + } + DEF TRFF Transform2D { + translation -20 0 + children [ + DEF FASTF TouchSensor {} + Shape { + appearance USE BUTAPP + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 4 5 6 7 -1] + coord Coordinate2D { + point [-8 7.5 -5 0 -8 -7.5 2 0 -2 7.5 1 0 -2 -7.5 8 0] + } + } + } + ] + } + DEF TRLOOP Transform2D { + translation 20 0 + children [ + DEF LOOP TouchSensor {} + Shape { + appearance Appearance { + material DEF MAT_LOOP Material2D { + emissiveColor 0 0 0 + } + } + geometry Circle { + radius 6 + } + } + ] + } + Transform2D { + translation 0 -170 + children [ + DEF TS TouchSensor {} + Transform2D { + children [ + Shape { + appearance USE BUTAPP + geometry DEF PROGBAR Rectangle { + size 200 15 + } + } + ] + } + DEF CURS Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0 0 1 + width 4 + } + } + } + geometry Rectangle { + size 6 15 + } + } + ] + } + ] + } + DEF TIMER Transform2D { + translation 80 0 + children [ + DEF RTIME TouchSensor {} + Shape { + appearance USE BUTAPP + geometry DEF TIME Text { + string [""] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + ] + } + DEF SC Script { + eventIn SFTime set_cursor + eventIn SFVec3f set_seek + eventIn SFBool restart + eventIn SFBool rev_time + eventIn SFBool pause + eventIn SFBool stop + eventIn SFBool set_loop + eventIn SFBool set_ff + eventIn SFTime set_duration + field SFNode loop USE MAT_LOOP + field SFNode app USE CURS + field SFNode ctrl USE MC + field SFNode bar USE PROGBAR + field SFNode txt USE TIME + field SFNode but USE WHICHBUT + field SFNode pan USE PANEL + field SFTime curSeek 0 + field SFInt32 isDown 0 + field SFBool reverse FALSE + field SFBool isPaused FALSE + field SFBool isLooping FALSE + field SFTime duration -1 + eventOut SFFloat fraction_changed + url ["javascript:function set_duration(value, timestamp) {duration = value;}function set_loop(value, timestamp) {if (!value) return;isLooping = !isLooping;loop.filled = isLooping;ctrl.loop = isLooping;}function pause(value, timestamp) {if (!value) return;if (isPaused) {ctrl.mediaSpeed = 1;but.whichChoice = 0;} else {ctrl.mediaSpeed = 0;but.whichChoice = 1;ctrl.mediaStartTime = -1;}isPaused = !isPaused;}function stop(value, timestamp) {if (!value) return;txt.string[0] = '';app.translation.x = - bar.size.x / 2;ctrl.mediaStartTime = 0;ctrl.mediaSpeed = 0;but.whichChoice = 1;isPaused = 1;}function rev_time(value, timestamp) {if (value) reverse = !reverse;}function set_time(value, timestamp) {timing = new String('');if (duration>=0) { if (!isLooping && value + 0.1 >=duration) {stop(true, timestamp);return;}}if (isPaused) return;if (duration>=0 && reverse) {value = duration - value;if (value<0) value=0;timing += '-';} else {timing += ' ';}hours = 0;mins = 0;secs = 0;if (value) {hours = Math.floor(value/3600);value -= hours*3600;mins = Math.floor(value / 60);value -= mins*60;secs = Math.floor(value);}if (hours<10) timing += '0';timing += hours + ':';if (mins<10) timing += '0';timing += mins + ':';if (secs<10) timing += '0';timing += secs;txt.string[0] = timing;}function set_cursor(value, timestamp) {set_time(value, timestamp);if (duration<0) return;if (ctrl.mediaSpeed && isDown != 1) {if (value>duration) value = duration;pos = value;pos /= duration;app.translation.x = bar.size.x * (pos - 0.5);fraction_changed = pos;} }function set_seek(myval, timestamp) {if (duration<0) return;pos = myval[0]; pos /= bar.size.x;pos += 0.5;if (pos>1) pos = 0;curSeek = duration;curSeek *= pos;if (isDown == 1) {app.translation.x = bar.size.x*(pos - 0.5);} else if (isDown==2) {isDown = 0;}}function restart(myval, timestamp) {if (myval) {ctrl.mediaSpeed = 0;isDown = 1;} else if (isDown == 1) {ctrl.mediaStartTime = curSeek;ctrl.mediaSpeed = 1;isDown = 2;but.whichChoice = 0;isPaused = FALSE;}}function set_ff(value, timestamp) {ctrl.mediaStartTime = -1;if (value) {ctrl.mediaSpeed = 4;} else {ctrl.mediaSpeed = 1;}}" ] + } + ] +} + +ROUTE MS.mediaCurrentTime TO SC.set_cursor +ROUTE TS.hitPoint_changed TO SC.set_seek +ROUTE TS.isActive TO SC.restart +ROUTE RTIME.isActive TO SC.rev_time +ROUTE PAUSE.isActive TO SC.pause +ROUTE STOP.isActive TO SC.stop +ROUTE LOOP.isActive TO SC.set_loop +ROUTE FASTF.isActive TO SC.set_ff +ROUTE MS.mediaDuration TO SC.set_duration +ROUTE FASTF.isActive TO SC.set_ff + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 21 + OCR_ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_audio.aac" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-deactivation.bt b/regression_tests/bifs-timeline-mediacontrol-deactivation.bt new file mode 100644 index 0000000..6f72756 --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-deactivation.bt @@ -0,0 +1,105 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 240 + pixelHeight 200 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows MediaControl and mediaSensor" "used to watch deactivation of a node" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Media Control Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF MT MovieTexture { + url [od:8] + } + } + geometry Bitmap {} + + } + DEF TS TouchSensor {} + ] + } + DEF MC MediaControl { + url [od:8] + mediaStartTime 2 + mediaStopTime 4 + preRoll FALSE + } + DEF MS MediaSensor { + url [od:8] + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT Text { + string [""] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + DEF V Valuator {} + ] +} + +ROUTE MS.isActive TO V.inSFBool +ROUTE V.outMFString TO TXT.string + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 21 + OCR_ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-inline-av.bt b/regression_tests/bifs-timeline-mediacontrol-inline-av.bt new file mode 100644 index 0000000..cc3f5cc --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-inline-av.bt @@ -0,0 +1,96 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + OCR_ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + title "Simple Audio-video scene" + info [ + "This simple scene contains an audio stream and a video stream." + "It is used by other regression tests." + "" + "GPAC Regression Tests" + "$Date: 2007/07/31 13:12:35 $ - $Revision: 1.4 $" + "(C) 2002-2004 GPAC Team" + ] + } + Background2D {} + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF MT MovieTexture { + url [od:8] + } + } + geometry Bitmap {} + + } + ] + } + Sound2D { + source AudioSource { + url [od:10] + stopTime -1 + } + } + ] +} + + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 1 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_audio.aac" + } + } + ] + } + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 21 + OCR_ES_ID 1 + muxInfo MuxInfo { + fileName "auxiliary_files/enst_video.h264" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-inline-segments.bt b/regression_tests/bifs-timeline-mediacontrol-inline-segments.bt new file mode 100644 index 0000000..d5e2f45 --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-inline-segments.bt @@ -0,0 +1,66 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + OCR_ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + objectTypeIndication 1 + streamType 3 + decSpecificInfo BIFSConfig { + nodeIDbits 10 + routeIDbits 10 + isCommandStream true + pixelMetric true + pixelWidth 600 + pixelHeight 400 + } + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows inline scene with segments" "and mediaControl on each of the inline segments" "" "GPAC Regression Tests" "$Date: 2008/08/21 17:05:41 $ - $Revision: 1.4 $" "(C) 2002-2004 GPAC Team"] + title "MediaControl and Inline" + } + Inline { + url ["bifs-timeline-mediacontrol-seg-inline.mp4"] + } + MediaSensor { + url ["bifs-timeline-mediacontrol-seg-inline.mp4"] + } + MediaControl { + url ["bifs-timeline-mediacontrol-seg-inline.mp4#red" "bifs-timeline-mediacontrol-seg-inline.mp4#blue" "bifs-timeline-mediacontrol-seg-inline.mp4#green"] + loop TRUE + } + Transform2D { + translation 0 -100 + children [ + Shape { + appearance Appearance { + material Material2D { + filled TRUE + emissiveColor 0 0 0 + } + } + geometry Text { + string ["Colors should alternate between" "red, blue and green in order"] + fontStyle FontStyle { + justify ["MIDDLE"] + size 30 + } + } + } + ] + } + + ] +} + + diff --git a/regression_tests/bifs-timeline-mediacontrol-inline.bt b/regression_tests/bifs-timeline-mediacontrol-inline.bt new file mode 100644 index 0000000..20e10e1 --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-inline.bt @@ -0,0 +1,231 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 240 + pixelHeight 200 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows complex scripting and MediaControl" "providing complete control of an inline scene" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Media Control Test" + } + Inline { + url [od:8] + } + DEF MC MediaControl { + url [od:8] + preRoll FALSE + } + DEF MS MediaSensor { + url [od:8] + } + DEF PANEL Transform2D { + translation 0 80 + children [ + DEF TRPAUSE Transform2D { + translation -100 0 + children [ + DEF PAUSE TouchSensor {} + DEF WHICHBUT Switch { + whichChoice 0 + choice [ + Shape { + appearance DEF BUTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 4 5 6 7 -1] + coord Coordinate2D { + point [-8 7.5 -3 7.5 -3 -7.5 -8 -7.5 0 7.5 5 7.5 5 -7.5 0 -7.5] + } + } + } + Shape { + appearance USE BUTAPP + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coord Coordinate2D { + point [-8 7.5 -8 -7.5 5 0] + } + } + } + ] + } + ] + } + DEF TRSTOP Transform2D { + translation -60 0 + children [ + DEF STOP TouchSensor {} + Shape { + appearance USE BUTAPP + geometry Rectangle { + size 15 15 + } + } + ] + } + DEF TRFF Transform2D { + translation -20 0 + children [ + DEF FASTF TouchSensor {} + Shape { + appearance USE BUTAPP + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 4 5 6 7 -1] + coord Coordinate2D { + point [-8 7.5 -5 0 -8 -7.5 2 0 -2 7.5 1 0 -2 -7.5 8 0] + } + } + } + ] + } + DEF TRLOOP Transform2D { + translation 20 0 + children [ + DEF LOOP TouchSensor {} + Shape { + appearance Appearance { + material DEF MAT_LOOP Material2D { + emissiveColor 0 0 0 + } + } + geometry Circle { + radius 6 + } + } + ] + } + Transform2D { + translation 0 -160 + children [ + DEF TS TouchSensor {} + Transform2D { + children [ + Shape { + appearance USE BUTAPP + geometry DEF PROGBAR Rectangle { + size 200 15 + } + } + ] + } + DEF CURS Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0 0 1 + width 4 + } + } + } + geometry Rectangle { + size 6 15 + } + } + ] + } + ] + } + DEF TIMER Transform2D { + translation 80 0 + children [ + DEF RTIME TouchSensor {} + Shape { + appearance USE BUTAPP + geometry DEF TIME Text { + string [""] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + ] + } + DEF SC Script { + eventIn SFTime set_cursor + eventIn SFVec3f set_seek + eventIn SFBool restart + eventIn SFBool rev_time + eventIn SFBool pause + eventIn SFBool stop + eventIn SFBool set_loop + eventIn SFBool set_ff + eventIn SFTime set_duration + field SFNode loop USE MAT_LOOP + field SFNode app USE CURS + field SFNode ctrl USE MC + field SFNode bar USE PROGBAR + field SFNode txt USE TIME + field SFNode but USE WHICHBUT + field SFNode pan USE PANEL + field SFTime curSeek 0 + field SFInt32 isDown 0 + field SFBool reverse FALSE + field SFBool isPaused FALSE + field SFBool isLooping FALSE + field SFTime duration -1 + field SFBool reset_startTime FALSE + eventOut SFFloat fraction_changed + url ["javascript:function set_duration(value, timestamp) {duration = value;}function set_loop(value, timestamp) {if (!value) return;isLooping = !isLooping;loop.filled = isLooping;ctrl.loop = isLooping;}function pause(value, timestamp) {if (!value) return;if (isPaused) {ctrl.mediaSpeed = 1;but.whichChoice = 0;if (ctrl.mediaStartTime == 0) reset_startTime = TRUE;} else {ctrl.mediaSpeed = 0;but.whichChoice = 1;ctrl.mediaStartTime = -1;}isPaused = !isPaused;}function stop(value, timestamp) {if (!value || isPaused) return;txt.string[0] = '';app.translation.x = - bar.size.x / 2;ctrl.mediaStartTime = 0;ctrl.mediaSpeed = 0;but.whichChoice = 1;isPaused = 1;}function rev_time(value, timestamp) {if (value) reverse = !reverse;}function set_time(value, timestamp) {timing = new String('');if (isPaused) return;if (duration>=0) { if (value + 0.1 >=duration) {if (!isLooping) {stop(true, timestamp);} else {ctrl.mediaStartTime = 0;reset_startTime = TRUE;}return;}}if (reset_startTime) {ctrl.mediaStartTime = -1;reset_startTime = FALSE;}if (duration>=0 && reverse) {value = duration - value;if (value<0) value=0;timing += '-';} else {timing += ' ';}hours = 0;mins = 0;secs = 0;if (value) {hours = Math.floor(value/3600);value -= hours*3600;mins = Math.floor(value / 60);value -= mins*60;secs = Math.floor(value);}if (hours<10) timing += '0';timing += hours + ':';if (mins<10) timing += '0';timing += mins + ':';if (secs<10) timing += '0';timing += secs;txt.string[0] = timing;}function set_cursor(value, timestamp) {set_time(value, timestamp);if (duration<0) return;if (ctrl.mediaSpeed && isDown != 1) {if (value>duration) value = duration;pos = value;pos /= duration;app.translation.x = bar.size.x * (pos - 0.5);fraction_changed = pos;} }function set_seek(myval, timestamp) {if (duration<0) return;pos = myval[0]; pos /= bar.size.x;pos += 0.5;if (pos>1) pos = 0;curSeek = duration;curSeek *= pos;if (isDown == 1) {app.translation.x = bar.size.x*(pos - 0.5);} else if (isDown==2) {isDown = 0;}}function restart(myval, timestamp) {if (myval) {ctrl.mediaSpeed = 0;isDown = 1;} else if (isDown == 1) {ctrl.mediaStartTime = curSeek;ctrl.mediaSpeed = 1;isDown = 2;but.whichChoice = 0;isPaused = FALSE;}}function set_ff(value, timestamp) {ctrl.mediaStartTime = -1;if (value) {ctrl.mediaSpeed = 8;} else {ctrl.mediaSpeed = 1;}}" ] + } + ] +} + +ROUTE MS.mediaCurrentTime TO SC.set_cursor +ROUTE TS.hitPoint_changed TO SC.set_seek +ROUTE TS.isActive TO SC.restart +ROUTE RTIME.isActive TO SC.rev_time +ROUTE PAUSE.isActive TO SC.pause +ROUTE STOP.isActive TO SC.stop +ROUTE LOOP.isActive TO SC.set_loop +ROUTE FASTF.isActive TO SC.set_ff +ROUTE MS.mediaDuration TO SC.set_duration +ROUTE FASTF.isActive TO SC.set_ff + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + URLstring "bifs-timeline-mediacontrol-inline-av.mp4" + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-rtsp.bt b/regression_tests/bifs-timeline-mediacontrol-rtsp.bt new file mode 100644 index 0000000..af2a462 --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-rtsp.bt @@ -0,0 +1,340 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 320 + pixelHeight 220 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows complex scripting and MediaControl" "providing complete control of an inline scene" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Media Control Test" + } + Inline { + url [od:8] + } + DEF MC MediaControl { + url [od:8] + preRoll FALSE + } + DEF MS MediaSensor { + url [od:8] + } + DEF PANEL Transform2D { + translation 0 100 + children [ + DEF TRPAUSE Transform2D { + translation -100 0 + children [ + DEF PAUSE TouchSensor {} + DEF WHICHBUT Switch { + whichChoice 0 + choice [ + Shape { + appearance DEF BUTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 4 5 6 7 -1] + coord Coordinate2D { + point [-8 7.5 -3 7.5 -3 -7.5 -8 -7.5 0 7.5 5 7.5 5 -7.5 0 -7.5] + } + } + } + Shape { + appearance USE BUTAPP + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coord Coordinate2D { + point [-8 7.5 -8 -7.5 5 0] + } + } + } + ] + } + ] + } + DEF TRSTOP Transform2D { + translation -60 0 + children [ + DEF STOP TouchSensor {} + Shape { + appearance USE BUTAPP + geometry Rectangle { + size 15 15 + } + } + ] + } + DEF TRFF Transform2D { + translation -20 0 + children [ + DEF FASTF TouchSensor {} + Shape { + appearance USE BUTAPP + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 4 5 6 7 -1] + coord Coordinate2D { + point [-8 7.5 -5 0 -8 -7.5 2 0 -2 7.5 1 0 -2 -7.5 8 0] + } + } + } + ] + } + DEF TRLOOP Transform2D { + translation 20 0 + children [ + DEF LOOP TouchSensor {} + Shape { + appearance Appearance { + material DEF MAT_LOOP Material2D { + emissiveColor 0 0 0 + } + } + geometry Circle { + radius 6 + } + } + ] + } + Transform2D { + translation 0 -200 + children [ + DEF TS TouchSensor {} + Transform2D { + children [ + Shape { + appearance USE BUTAPP + geometry DEF PROGBAR Rectangle { + size 200 15 + } + } + ] + } + DEF CURS Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0 0 1 + width 4 + } + } + } + geometry Rectangle { + size 6 15 + } + } + ] + } + ] + } + DEF TIMER Transform2D { + translation 80 0 + children [ + DEF RTIME TouchSensor {} + Shape { + appearance USE BUTAPP + geometry DEF TIME Text { + string [""] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + ] + } + DEF SC Script { + eventIn SFTime set_cursor + eventIn SFVec3f set_seek + eventIn SFBool restart + eventIn SFBool rev_time + eventIn SFBool pause + eventIn SFBool stop + eventIn SFBool set_loop + eventIn SFBool set_ff + eventIn SFTime set_duration + field SFNode loop USE MAT_LOOP + field SFNode app USE CURS + field SFNode ctrl USE MC + field SFNode bar USE PROGBAR + field SFNode txt USE TIME + field SFNode but USE WHICHBUT + field SFNode pan USE PANEL + field SFTime curSeek 0 + field SFInt32 isDown 0 + field SFBool reverse FALSE + field SFBool isPaused FALSE + field SFBool isLooping FALSE + field SFTime duration -1 + field SFBool reset_startTime FALSE + eventOut SFFloat fraction_changed + url ["javascript: + function set_duration(value, timestamp) { + duration = value; + } + function set_loop(value, timestamp) { + if (!value) return; + isLooping = !isLooping; + loop.filled = isLooping; + ctrl.loop = isLooping; + } + function pause(value, timestamp) { + if (!value) return; + if (isPaused) { + ctrl.mediaSpeed = 1; + but.whichChoice = 0; + if (ctrl.mediaStartTime == 0) reset_startTime = TRUE; + } else { + ctrl.mediaSpeed = 0; + but.whichChoice = 1; + ctrl.mediaStartTime = -1; + } + isPaused = !isPaused; + } + function stop(value, timestamp) { + if (!value || isPaused) return; + txt.string[0] = ''; + app.translation.x = - bar.size.x / 2; + ctrl.mediaStartTime = 0; + ctrl.mediaSpeed = 0; + but.whichChoice = 1; + isPaused = 1; + } + function rev_time(value, timestamp) {if (value) reverse = !reverse;} + function set_time(value, timestamp) { + timing = new String(''); + if (isPaused) return; + if (duration>=0.1) { + if (value + 0.1 >=duration) { + if (!isLooping) { + stop(true, timestamp); + } else { + ctrl.mediaStartTime = 0; + reset_startTime = TRUE; + } + return; + } + } + if (reset_startTime) { + ctrl.mediaStartTime = -1; + reset_startTime = FALSE; + } + if (duration>=0 && reverse) { + value = duration - value; + if (value<0) value=0; + timing += '-'; + } else { + timing += ' '; + } + hours = 0; + mins = 0; + secs = 0; + if (value) { + hours = Math.floor(value/3600);value -= hours*3600;mins = Math.floor(value / 60); + value -= mins*60;secs = Math.floor(value); + } + if (hours<10) timing += '0';timing += hours + ':';if (mins<10) timing += '0';timing += mins + ':';if (secs<10) timing += '0';timing += secs;txt.string[0] = timing; + } + function set_cursor(value, timestamp) { + set_time(value, timestamp); + if (duration<0) return; + if (ctrl.mediaSpeed && isDown != 1) { + if (value>duration) value = duration; + pos = value; + pos /= duration; + app.translation.x = bar.size.x * (pos - 0.5);fraction_changed = pos; + } + } + function set_seek(myval, timestamp) { + if (duration<0) return; + pos = myval[0]; + pos /= bar.size.x;pos += 0.5; + if (pos>1) pos = 0; + curSeek = duration; + curSeek *= pos; + if (isDown == 1) { + app.translation.x = bar.size.x*(pos - 0.5); + } else if (isDown==2) {isDown = 0;} + } + function restart(myval, timestamp) { + if (myval) { + ctrl.mediaSpeed = 0; + isDown = 1; + } else if (isDown == 1) { + ctrl.mediaStartTime = curSeek; + ctrl.mediaSpeed = 1; + isDown = 2; + but.whichChoice = 0; + isPaused = FALSE; + } + } + function set_ff(value, timestamp) { + ctrl.mediaStartTime = -1; + if (value) { + ctrl.mediaSpeed = 8; + } else { + ctrl.mediaSpeed = 1; + } + } + " + ] + } + ] +} + +ROUTE MS.mediaCurrentTime TO SC.set_cursor +ROUTE TS.hitPoint_changed TO SC.set_seek +ROUTE TS.isActive TO SC.restart +ROUTE RTIME.isActive TO SC.rev_time +ROUTE PAUSE.isActive TO SC.pause +ROUTE STOP.isActive TO SC.stop +ROUTE LOOP.isActive TO SC.set_loop +ROUTE FASTF.isActive TO SC.set_ff +ROUTE MS.mediaDuration TO SC.set_duration +ROUTE FASTF.isActive TO SC.set_ff + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + URLstring "rtsp://a1749.q.kamai.net/7/1749/1416/3c964c64/neo.qtv.apple.com/secure/may/preview/civ3_700.mp4" + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-seg-inline.bt b/regression_tests/bifs-timeline-mediacontrol-seg-inline.bt new file mode 100644 index 0000000..f385bbf --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-seg-inline.bt @@ -0,0 +1,81 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 254 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 254 + graphicsProfileLevelIndication 254 + ODProfileLevelIndication 254 + includeInlineProfileLevelFlag true + esDescr [ + ES_Descriptor { + ES_ID 5 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 576 + pixelHeight 432 + } + } + } + ] + ociDescr [ + SegmentDescriptor { + startTime 0 + duration 3 + name "red" + } + SegmentDescriptor { + startTime 3 + duration 3 + name "green" + } + SegmentDescriptor { + startTime 6 + duration 3 + name "blue" + } + ] +} + +OrderedGroup { + children [ + WorldInfo { + title "Simple BIFS stream with chapters" + info [ + "This simple scene contains a BIFS stream with the definition of 3 segments." + "It is used by other regression tests." + "" + "GPAC Regression Tests" + "$Date: 2007/07/31 13:12:35 $ - $Revision: 1.3 $" + "(C) 2002-2004 GPAC Team" + ] + } + Shape { + appearance Appearance { + material DEF M Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Rectangle { + size 200 100 + } + } + ] +} + + +AT 3000 { + REPLACE M.emissiveColor BY 0 1 0 +} + +AT 6000 { + REPLACE M.emissiveColor BY 0 0 1 +} + +AT 9000 { + REPLACE M.emissiveColor BY 0 0 1 +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-segments.bt b/regression_tests/bifs-timeline-mediacontrol-segments.bt new file mode 100644 index 0000000..91cc3ec --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-segments.bt @@ -0,0 +1,154 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 240 + pixelHeight 200 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows chapter selection" "through segment switching in MediaControl" "" "GPAC Regression Tests" "$Date: 2008/08/28 16:17:57 $ - $Revision: 1.4 $" "(C) 2002-2004 GPAC Team"] + title "Media Control Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF MT MovieTexture { + url [od:8] + } + } + geometry Bitmap {} + + } + DEF TS TouchSensor {} + ] + } +#if 0 + Sound2D { + source DEF ASRC AudioSource { + url [od:10] + } + } +#endif + DEF MC MediaControl { + url ["od:8#Begin" "od:8#End"] + loop TRUE + preRoll FALSE + } + DEF MS MediaSensor { + url [od:8] + } + Transform2D { + translation 0 -80 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry DEF TXT Text { + string [""] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + DEF V Valuator {} + DEF C Conditional { + buffer { + REPLACE MC.url BY ["od:8#Middle"] + } + } + DEF RC Conditional { + buffer { + REPLACE MC.url BY ["od:8#Begin" "od:8#End"] + } + } + ] +} + +ROUTE MS.mediaCurrentTime TO V.inSFTime +ROUTE V.outMFString TO TXT.string +ROUTE TS.isOver TO C.activate +ROUTE TS.isOver TO RC.reverseActivate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 21 + OCR_ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + ociDescr [ + SegmentDescriptor { + startTime 0 + duration 4 + name "Begin" + } + SegmentDescriptor { + startTime 4 + duration 2 + name "Middle" + } + SegmentDescriptor { + startTime 6 + duration 4 + name "End" + } + ] + } +#if 0 + ObjectDescriptor { + objectDescriptorID 10 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/count_english.mp3" + } + } + ] + } +#endif + ] +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-video.bt b/regression_tests/bifs-timeline-mediacontrol-video.bt new file mode 100644 index 0000000..de0bddc --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-video.bt @@ -0,0 +1,248 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 240 + pixelHeight 200 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows complete control of media" "through scripting and MediaControl" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.4 $" "(C) 2002-2004 GPAC Team"] + title "Media Control Test" + } + Transform2D { + children [ + Shape { + appearance Appearance { + texture DEF MT MovieTexture { + url [od:8] + } + } + geometry Bitmap {} + + } + ] + } + DEF MC MediaControl { + url [od:8] + preRoll FALSE + } + DEF MS MediaSensor { + url [od:8] + } + DEF PANEL Transform2D { + translation 0 80 + children [ + DEF TRPAUSE Transform2D { + translation -100 0 + children [ + DEF PAUSE TouchSensor {} + DEF WHICHBUT Switch { + whichChoice 0 + choice [ + Shape { + appearance DEF BUTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 4 5 6 7 -1] + coord Coordinate2D { + point [-8 7.5 -3 7.5 -3 -7.5 -8 -7.5 0 7.5 5 7.5 5 -7.5 0 -7.5] + } + } + } + Shape { + appearance USE BUTAPP + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coord Coordinate2D { + point [-8 7.5 -8 -7.5 5 0] + } + } + } + ] + } + ] + } + DEF TRSTOP Transform2D { + translation -60 0 + children [ + DEF STOP TouchSensor {} + Shape { + appearance USE BUTAPP + geometry Rectangle { + size 15 15 + } + } + ] + } + DEF TRFF Transform2D { + translation -20 0 + children [ + DEF FASTF TouchSensor {} + Shape { + appearance USE BUTAPP + geometry IndexedFaceSet2D { + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 4 5 6 7 -1] + coord Coordinate2D { + point [-8 7.5 -5 0 -8 -7.5 2 0 -2 7.5 1 0 -2 -7.5 8 0] + } + } + } + ] + } + DEF TRLOOP Transform2D { + translation 20 0 + children [ + DEF LOOP TouchSensor {} + Shape { + appearance Appearance { + material DEF MAT_LOOP Material2D { + emissiveColor 0 0 0 + } + } + geometry Circle { + radius 6 + } + } + ] + } + Transform2D { + translation 0 -160 + children [ + DEF TS TouchSensor {} + Transform2D { + children [ + Shape { + appearance USE BUTAPP + geometry DEF PROGBAR Rectangle { + size 200 15 + } + } + ] + } + DEF CURS Transform2D { + children [ + Shape { + appearance Appearance { + material Material2D { + lineProps LineProperties { + lineColor 0 0 1 + width 4 + } + } + } + geometry Rectangle { + size 6 15 + } + } + ] + } + ] + } + DEF TIMER Transform2D { + translation 80 0 + children [ + DEF RTIME TouchSensor {} + Shape { + appearance USE BUTAPP + geometry DEF TIME Text { + string [""] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + ] + } + DEF SC Script { + eventIn SFTime set_cursor + eventIn SFVec3f set_seek + eventIn SFBool restart + eventIn SFBool rev_time + eventIn SFBool pause + eventIn SFBool stop + eventIn SFBool set_loop + eventIn SFBool set_ff + eventIn SFTime set_duration + field SFNode loop USE MAT_LOOP + field SFNode app USE CURS + field SFNode ctrl USE MC + field SFNode bar USE PROGBAR + field SFNode txt USE TIME + field SFNode but USE WHICHBUT + field SFNode pan USE PANEL + field SFTime curSeek 0 + field SFInt32 isDown 0 + field SFBool reverse FALSE + field SFBool isPaused FALSE + field SFBool isLooping FALSE + field SFTime duration -1 + eventOut SFFloat fraction_changed + url ["javascript:function set_duration(value, timestamp) {duration = value;}function set_loop(value, timestamp) {if (!value) return;isLooping = !isLooping;loop.filled = isLooping;ctrl.loop = isLooping;}function pause(value, timestamp) {if (!value) return;if (isPaused) {ctrl.mediaSpeed = 1;but.whichChoice = 0;} else {ctrl.mediaSpeed = 0;but.whichChoice = 1;ctrl.mediaStartTime = -1;}isPaused = !isPaused;}function stop(value, timestamp) {if (!value) return;txt.string[0] = '';app.translation.x = - bar.size.x / 2;ctrl.mediaStartTime = 0;ctrl.mediaSpeed = 0;but.whichChoice = 1;isPaused = 1;}function rev_time(value, timestamp) {if (value) reverse = !reverse;}function set_time(value, timestamp) {timing = new String('');if (duration>=0) { if (!isLooping && value + 0.1 >=duration) {stop(true, timestamp);print('script stop ' + value + ' ' + duration);return;}}if (isPaused) return;if (duration>=0 && reverse) {value = duration - value;if (value<0) value=0;timing += '-';} else {timing += ' ';}hours = 0;mins = 0;secs = 0;if (value) {hours = Math.floor(value/3600);value -= hours*3600;mins = Math.floor(value / 60);value -= mins*60;secs = Math.floor(value);}if (hours<10) timing += '0';timing += hours + ':';if (mins<10) timing += '0';timing += mins + ':';if (secs<10) timing += '0';timing += secs;txt.string[0] = timing;}function set_cursor(value, timestamp) {set_time(value, timestamp);if (duration<0) return;if (ctrl.mediaSpeed && isDown != 1) {if (value>duration) value = duration;pos = value;pos /= duration;app.translation.x = bar.size.x * (pos - 0.5);fraction_changed = pos;} }function set_seek(myval, timestamp) {if (duration<0) return;pos = myval[0]; pos /= bar.size.x;pos += 0.5;if (pos>1) pos = 0;curSeek = duration;curSeek *= pos;if (isDown == 1) {app.translation.x = bar.size.x*(pos - 0.5);} else if (isDown==2) {isDown = 0;}}function restart(myval, timestamp) {if (myval) {ctrl.mediaSpeed = 0;isDown = 1;} else if (isDown == 1) {ctrl.mediaStartTime = curSeek;ctrl.mediaSpeed = 1;isDown = 2;but.whichChoice = 0;isPaused = FALSE;}}function set_ff(value, timestamp) {ctrl.mediaStartTime = -1;if (value) {ctrl.mediaSpeed = 4;} else {ctrl.mediaSpeed = 1;}}" ] + } + ] +} + +ROUTE MS.mediaCurrentTime TO SC.set_cursor +ROUTE TS.hitPoint_changed TO SC.set_seek +ROUTE TS.isActive TO SC.restart +ROUTE RTIME.isActive TO SC.rev_time +ROUTE PAUSE.isActive TO SC.pause +ROUTE STOP.isActive TO SC.stop +ROUTE LOOP.isActive TO SC.set_loop +ROUTE FASTF.isActive TO SC.set_ff +ROUTE MS.mediaDuration TO SC.set_duration +ROUTE FASTF.isActive TO SC.set_ff + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 21 + OCR_ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediacontrol-videospeed.bt b/regression_tests/bifs-timeline-mediacontrol-videospeed.bt new file mode 100644 index 0000000..5189d32 --- /dev/null +++ b/regression_tests/bifs-timeline-mediacontrol-videospeed.bt @@ -0,0 +1,202 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 200 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows MediaControl" "controling video playback speed" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Media Control Test" + } + DEF MC MediaControl { + url [od:8] + loop TRUE + } + Transform2D { + translation -100 -65 + children [ + DEF TS1 TouchSensor {} + Shape { + appearance Appearance { + material DEF M1 Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry Circle { + radius 30 + } + } + Transform2D { + children [ + Shape { + appearance DEF TXTAPP Appearance { + material Material2D { + emissiveColor 0 0 0 + filled TRUE + } + } + geometry Text { + string ["Speed" "x4"] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 18 + } + } + } + ] + } + ] + } + Transform2D { + translation 100 -65 + children [ + DEF TS2 TouchSensor {} + Shape { + appearance Appearance { + material DEF M2 Material2D { + emissiveColor 0 0 1 + filled TRUE + } + } + geometry Circle { + radius 30 + } + } + Transform2D { + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["Speed" "x0.5"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 -65 + children [ + DEF TS3 TouchSensor {} + Shape { + appearance Appearance { + material DEF M3 Material2D { + emissiveColor 0 1 0 + filled TRUE + } + } + geometry Circle { + radius 30 + } + } + Transform2D { + children [ + Shape { + appearance USE TXTAPP + geometry Text { + string ["Mute"] + fontStyle USE FS + } + } + ] + } + ] + } + Transform2D { + translation 0 30 + children [ + Shape { + appearance Appearance { + texture MovieTexture { + url [od:8] + } + } + geometry Bitmap {} + + } + ] + } + DEF C1 Conditional { + buffer { + REPLACE MC.mediaSpeed BY 4 + REPLACE M1.emissiveColor BY 1 0.5 0.5 + } + } + DEF C2 Conditional { + buffer { + REPLACE MC.mediaSpeed BY 0.5 + REPLACE M2.emissiveColor BY 0.5 0.5 1 + } + } + DEF C3 Conditional { + buffer { + REPLACE MC.mute BY TRUE + REPLACE M3.emissiveColor BY 0.5 1 0.5 + } + } + DEF RESET Conditional { + buffer { + REPLACE MC.mediaSpeed BY 1 + REPLACE MC.mute BY FALSE + REPLACE M1.emissiveColor BY 1 0 0 + REPLACE M2.emissiveColor BY 0 0 1 + REPLACE M3.emissiveColor BY 0 1 0 + } + } + ] +} + +ROUTE TS1.isOver TO C1.activate +ROUTE TS1.isOver TO RESET.reverseActivate +ROUTE TS2.isOver TO C2.activate +ROUTE TS2.isOver TO RESET.reverseActivate +ROUTE TS3.isOver TO C3.activate +ROUTE TS3.isOver TO RESET.reverseActivate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 21 + OCR_ES_ID 21 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediasensor-segment-switch.bt b/regression_tests/bifs-timeline-mediasensor-segment-switch.bt new file mode 100644 index 0000000..c07547a --- /dev/null +++ b/regression_tests/bifs-timeline-mediasensor-segment-switch.bt @@ -0,0 +1,226 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of MediaSensor" "with media segments defined" "and dynamic changes of watched segments" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Media Sensor Test #3" + } + Shape { + appearance Appearance { + texture DEF MOV MovieTexture { + loop TRUE + stopTime -1 + url [od:8] + repeatS FALSE + repeatT FALSE + } + } + geometry Bitmap {} + + } + DEF MS MediaSensor { + url [od:8] + } + DEF MSALL MediaSensor { + url [od:8] + } + DEF VAL Valuator {} + DEF VAL2 Valuator {} + Transform2D { + translation 50 -90 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry DEF TXT Text { + string [""] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -50 -90 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Media Time:"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -60 -120 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Segment Start Time:"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 70 -120 + children [ + Shape { + appearance USE APP + geometry DEF START Text { + string [""] + fontStyle USE FS + } + } + ] + } + DEF VAL3 Valuator {} + Transform2D { + translation -50 120 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Segment Name:"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 50 120 + children [ + Shape { + appearance USE APP + geometry DEF NAME Text { + string [""] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -70 90 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Segment Duration: "] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 60 90 + children [ + Shape { + appearance USE APP + geometry DEF TXT2 Text { + string [""] + fontStyle USE FS + } + } + ] + } + DEF S Script { + eventIn SFBool switch_seg + field SFNode ms_node USE MS + field SFInt32 num_seg 0 + url ["javascript:function switch_seg(value, timestamp) {if (value) return;switch (num_seg) {case 0:ms_node.url[0] = '8#End';break;case 1:ms_node.url[0] = '8#Middle';break;case 2:ms_node.url[0] = '8#Begin';break;case 3:ms_node.url = new MFString('8#Begin', '8#Middle');break;case 4:ms_node.url = new MFString('8#Begin', '8#End');break;case 5:ms_node.url = new MFString('8#Middle', '8#End');break;}num_seg++;}" ] + } + DEF C Conditional { + buffer { + REPLACE TXT.string BY [""] + REPLACE TXT2.string BY [""] + REPLACE NAME.string BY [""] + REPLACE START.string BY [""] + } + } + ] +} + +ROUTE MS.mediaCurrentTime TO VAL.inSFTime +ROUTE VAL.outMFString TO TXT.string +ROUTE MS.mediaDuration TO VAL2.inSFTime +ROUTE VAL2.outMFString TO TXT2.string +ROUTE MS.info TO NAME.string +ROUTE MS.streamObjectStartTime TO VAL3.inSFTime +ROUTE VAL3.outMFString TO START.string +ROUTE MS.isActive TO C.reverseActivate +ROUTE MSALL.isActive TO S.switch_seg + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + duration 8930 + } + } + ] + ociDescr [ + SegmentDescriptor { + startTime 0 + duration 4 + name "Begin" + } + SegmentDescriptor { + startTime 4 + duration 2 + name "Middle" + } + SegmentDescriptor { + startTime 6 + duration 4 + name "End" + } + ] + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediasensor-segment.bt b/regression_tests/bifs-timeline-mediasensor-segment.bt new file mode 100644 index 0000000..f70d6f8 --- /dev/null +++ b/regression_tests/bifs-timeline-mediasensor-segment.bt @@ -0,0 +1,215 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of MediaSensor" "with media segments defined" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Media Sensor Test #2" + } + Shape { + appearance Appearance { + texture DEF MOV MovieTexture { + loop TRUE + stopTime -1 + url [od:8] + repeatS FALSE + repeatT FALSE + } + } + geometry Bitmap {} + + } + DEF MS MediaSensor { + url ["od:8#Begin" "od:8#End"] + } + DEF VAL Valuator {} + DEF VAL2 Valuator {} + Transform2D { + translation 50 -90 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry DEF TXT Text { + string [""] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -50 -90 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Media Time:"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -60 -120 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Segment Start Time:"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 70 -120 + children [ + Shape { + appearance USE APP + geometry DEF START Text { + string [""] + fontStyle USE FS + } + } + ] + } + DEF VAL3 Valuator {} + Transform2D { + translation -50 120 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Segment Name:"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 50 120 + children [ + Shape { + appearance USE APP + geometry DEF NAME Text { + string [""] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -70 90 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Segment Duration: "] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 60 90 + children [ + Shape { + appearance USE APP + geometry DEF TXT2 Text { + string [""] + fontStyle USE FS + } + } + ] + } + DEF C Conditional { + buffer { + REPLACE TXT.string BY [""] + REPLACE TXT2.string BY [""] + REPLACE NAME.string BY [""] + REPLACE START.string BY [""] + } + } + ] +} + +ROUTE MS.mediaCurrentTime TO VAL.inSFTime +ROUTE VAL.outMFString TO TXT.string +ROUTE MS.mediaDuration TO VAL2.inSFTime +ROUTE VAL2.outMFString TO TXT2.string +ROUTE MS.info TO NAME.string +ROUTE MS.streamObjectStartTime TO VAL3.inSFTime +ROUTE VAL3.outMFString TO START.string +ROUTE MS.isActive TO C.reverseActivate + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + ociDescr [ + SegmentDescriptor { + startTime 0 + duration 4 + name "Begin" + } + SegmentDescriptor { + startTime 4 + duration 2 + name "Middle" + } + SegmentDescriptor { + startTime 6 + duration 4 + name "End" + } + ] + } + ] +} + diff --git a/regression_tests/bifs-timeline-mediasensor.bt b/regression_tests/bifs-timeline-mediasensor.bt new file mode 100644 index 0000000..88a1923 --- /dev/null +++ b/regression_tests/bifs-timeline-mediasensor.bt @@ -0,0 +1,137 @@ +InitialObjectDescriptor { + objectDescriptorID 1 + audioProfileLevelIndication 255 + visualProfileLevelIndication 254 + sceneProfileLevelIndication 1 + graphicsProfileLevelIndication 1 + ODProfileLevelIndication 1 + esDescr [ + ES_Descriptor { + ES_ID 1 + decConfigDescr DecoderConfigDescriptor { + streamType 3 + decSpecificInfo BIFSConfig { + isCommandStream true + pixelMetric true + pixelWidth 300 + pixelHeight 300 + } + } + } + ES_Descriptor { + ES_ID 2 + decConfigDescr DecoderConfigDescriptor { + streamType 1 + } + } + ] +} + +OrderedGroup { + children [ + Background2D { + backColor 1 1 1 + } + WorldInfo { + info ["This shows usage of MediaSensor" "without media segments defined" "" "GPAC Regression Tests" "$Date: 2007/07/27 09:46:10 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + title "Media Sensor Test #1" + } + Shape { + appearance Appearance { + texture DEF MOV MovieTexture { + loop TRUE + stopTime -1 + url [od:8] + repeatS FALSE + repeatT FALSE + } + } + geometry Bitmap {} + + } + DEF MS MediaSensor { + url [od:8] + } + DEF VAL Valuator {} + DEF VAL2 Valuator {} + Transform2D { + translation 50 -90 + children [ + Shape { + appearance DEF APP Appearance { + material Material2D { + emissiveColor 1 0 0 + filled TRUE + } + } + geometry DEF TXT Text { + string [""] + fontStyle DEF FS FontStyle { + justify ["MIDDLE" "MIDDLE"] + size 20 + } + } + } + ] + } + Transform2D { + translation -50 -90 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Media Time:"] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation -60 90 + children [ + Shape { + appearance USE APP + geometry Text { + string ["Media Duration: "] + fontStyle USE FS + } + } + ] + } + Transform2D { + translation 60 90 + children [ + Shape { + appearance USE APP + geometry DEF TXT2 Text { + string [""] + fontStyle USE FS + } + } + ] + } + ] +} + +ROUTE MS.mediaCurrentTime TO VAL.inSFTime +ROUTE VAL.outMFString TO TXT.string +ROUTE MS.mediaDuration TO VAL2.inSFTime +ROUTE VAL2.outMFString TO TXT2.string + +AT 0 { + UPDATE OD [ + ObjectDescriptor { + objectDescriptorID 8 + esDescr [ + ES_Descriptor { + ES_ID 20 + OCR_ES_ID 20 + muxInfo MuxInfo { + fileName "auxiliary_files/count_video.cmp" + } + } + ] + } + ] +} + diff --git a/regression_tests/build-navigator-sh b/regression_tests/build-navigator-sh new file mode 100755 index 0000000..94a3791 --- /dev/null +++ b/regression_tests/build-navigator-sh @@ -0,0 +1,11 @@ +#!/bin/sh + + +xsltproc -o index.html auxiliary_files/index2html.xslt index.xml + +xsltproc -o create_htmls.sh --stringparam xslt_path auxiliary_files auxiliary_files/index2sh.xslt index.xml + +chmod +x create_htmls.sh + +./create_htmls.sh + diff --git a/regression_tests/build-navigator-w32.bat b/regression_tests/build-navigator-w32.bat new file mode 100644 index 0000000..3f2e298 --- /dev/null +++ b/regression_tests/build-navigator-w32.bat @@ -0,0 +1,14 @@ +@ECHO OFF + +SET XALAN="C:\Program Files\Java\xalan-j_2_7_0\xalan.jar" + +@echo Creating HTML index +java -jar %XALAN% -IN index.xml -XSL auxiliary_files\index2html.xslt -OUT index.html +@echo done. + +@echo Creating Batch file for creation of HTML files +java -jar %XALAN% -IN index.xml -XSL auxiliary_files\index2batch.xslt -OUT create_htmls.bat -PARAM xalan_path %XALAN% -PARAM xslt_path auxiliary_files +@echo done. + +call create_htmls.bat + diff --git a/regression_tests/build-shell b/regression_tests/build-shell new file mode 100644 index 0000000..3530109 --- /dev/null +++ b/regression_tests/build-shell @@ -0,0 +1,49 @@ +#!/bin/sh + +set -e + +case $1 in +"xmt" ) + echo "Converting BT to XMT-A" + find . -name "*.bt" | sort | while read FILE; do + echo "Processing $FILE" + MP4Box -xmt -quiet "$(basename "$FILE")" + done + ;; +"xmp4" ) + echo "Converting XMT-A to MP4" + find . -name "*.xmt" | sort | while read FILE; do + echo "Processing $FILE" + MP4Box -mp4 -quiet "$(basename "$FILE")" + done + ;; +"x3d" ) + echo "Converting X3DV to X3D" + find . -name "*.x3dv" | sort | while read FILE; do + echo "Processing $FILE" + MP4Box -x3d -quiet "$(basename "$FILE")" + done + ;; +"clean" ) + echo "Removing XMT and MP4 files" + rm -rf *.xmt + rm -rf *.mp4 + ;; +"help" ) + echo "GPAC Regression Tests Build Script usage: 00-do-build [opt]" + echo " no op / opt = "mp4": builds MP4 file sfrom BT scripts" + echo " opt = "xmt": builds XMT-A files from BT scripts" + echo " opt = "xmp4": builds MP4 files from XMT-A scripts" + echo " opt = "clean": deletes all XMT-A and MP4 files" + echo " opt = "help": prints this screen." + ;; +* ) + echo "Converting BT to MP4" + find . -name "*.bt" | sort | while read FILE; do + echo "Processing $FILE" + MP4Box -mp4 -quiet "$(basename "$FILE")" + done + ;; + +esac + diff --git a/regression_tests/build-w32.bat b/regression_tests/build-w32.bat new file mode 100644 index 0000000..abb4298 --- /dev/null +++ b/regression_tests/build-w32.bat @@ -0,0 +1,54 @@ +@ECHO OFF + +IF "%1" == "" GOTO BTTOMP4 +IF "%1" == "mp4" GOTO BTTOMP4 +IF "%1" == "xmt" GOTO BTTOXMT +IF "%1" == "xmp4" GOTO XMTTOMP4 +IF "%1" == "x3d" GOTO X3DVTOX3D +IF "%1" == "clean" GOTO REGCLEAN +IF "%1" == "help" GOTO GETHELP + +:BTTOMP4 +for %%a in (*.bt) DO ( +echo %%a +MP4Box -mp4 -quiet %%a +) +GOTO DONE + +:BTTOXMT +for %%a in (*.bt) DO ( +echo %%a +MP4Box -xmt -quiet %%a +) +GOTO DONE + +:XMTTOMP4 +for %%a in (*.xmt) DO ( +MP4Box -mp4 -quiet %%a +echo %a +) +GOTO DONE + +:X3DVTOX3D +for %%a in (*.x3dv) DO MP4Box -x3d %%a +GOTO DONE + +:REGCLEAN +DEL /S *.mp4 +DEL /S *.xmt +DEL /S bifs*.html +DEL /S x3d*.html +GOTO DONE + +:GETHELP +echo GPAC Regression Tests Build Script usage: 00-do-w32 [opt] +echo no op / opt = "mp4": builds MP4 file sfrom BT scripts +echo opt = "xmt": builds XMT-A files from BT scripts +echo opt = "xmp4": builds MP4 files from XMT-A scripts +echo opt = "clean": deletes all XMT-A and MP4 files +echo opt = "help": prints this screen. +GOTO DONE + + +:DONE + diff --git a/regression_tests/index.css b/regression_tests/index.css new file mode 100644 index 0000000..f7fd2b6 --- /dev/null +++ b/regression_tests/index.css @@ -0,0 +1,134 @@ +body { + background-color:#E8E5E5; + border: 0; margin: 0; padding: 0; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 12px; + margin-top: 15px; + margin-left: 15px; + margin-right: 15px; +} + +a { text-decoration:none; } + +#title { + font-size: 16px; + font-weight:bold; + text-align: left; + margin: 0; + padding: 0; + color: #D50751; +} + +#nav { + text-align: left; + +} + +#nav ul { + margin: 0; + padding: 0; + border-bottom: 1px solid black; + margin-bottom: 15px; +} + +#nav li { + display: inline; + list-style-type: none; + font-size: 14px; + font-weight:bold; + text-align: center; +} + +#content { + width: 95%; +} + +a { text-decoration:none; } + +h2 { + color:#000000; + font-weight:bold; + margin-left: 23px; + width: 95%; + font-size: 14px; + font-weight:bold; +} + +div#description h2, div#contentview h2, div#codeview h2 { + border-bottom: 1px solid black; +} + +div#content h3 { + border-bottom: 1px solid black; +} + +#left { + width: 49%; + float: left; + border-right: 1px solid black; +} + +#right { + width: 50%; + float: right; +} + +#description { + width: 95%; + text-align: left; +} + +#downloadbar, #downloadbar h2 { + color:#FFFFFF; + margin: 0; + padding: 0; + background-color:#A51129; + text-align: center; + position: relative; + width: 95%; +} + +#downloadbar ul +{ + text-align: center; +} + +#downloadbar li +{ + display: inline; + list-style-type: none; + padding-right: 20px; +} + +#downloadbar a:link, #downloadbar a:visited { + display:inline; + padding:0; + color:#FFFFFF; + background-color: transparent; +} + +#contentview { + text-align: left; +} + +#snapshotview { + clear: both; +} + +#codeview { + clear: both; + width: 100%; + height: 100%; +} + +#codeview object { + width: 100%; + height: 100%; +} + +#category li { + display: inline; + list-style-type: none; + margin-right: 20px; +} + diff --git a/regression_tests/index.xml b/regression_tests/index.xml new file mode 100644 index 0000000..0874bab --- /dev/null +++ b/regression_tests/index.xml @@ -0,0 +1,373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/regression_tests/x3d-2D-Arc2d.x3dv b/regression_tests/x3d-2D-Arc2d.x3dv new file mode 100644 index 0000000..3ef3961 --- /dev/null +++ b/regression_tests/x3d-2D-Arc2d.x3dv @@ -0,0 +1,23 @@ +#X3D V3.0 + +Group { + children [ + WorldInfo { + title "Arc2D X3D test" + info ["This shows an Arc2D" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry Arc2D { + radius 1 + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-2D-ArcClose2d.x3dv b/regression_tests/x3d-2D-ArcClose2d.x3dv new file mode 100644 index 0000000..80d3ee5 --- /dev/null +++ b/regression_tests/x3d-2D-ArcClose2d.x3dv @@ -0,0 +1,24 @@ +#X3D V3.0 + +Group { + children [ + WorldInfo { + title "ArcClose2D X3D test" + info ["This shows a ArcClose2D" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry ArcClose2D { + radius 1 + closureType "CHORD" + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-2D-Disk2d.x3dv b/regression_tests/x3d-2D-Disk2d.x3dv new file mode 100644 index 0000000..6ef901e --- /dev/null +++ b/regression_tests/x3d-2D-Disk2d.x3dv @@ -0,0 +1,24 @@ +#X3D V3.0 + +Group { + children [ + WorldInfo { + title "Disk2D X3D test" + info ["This shows a a Disk2D" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry Disk2D { + outerRadius 1 + innerRadius 0.4 + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-2D-Polyline2d.x3dv b/regression_tests/x3d-2D-Polyline2d.x3dv new file mode 100644 index 0000000..39d8584 --- /dev/null +++ b/regression_tests/x3d-2D-Polyline2d.x3dv @@ -0,0 +1,23 @@ +#X3D V3.0 + +Group { + children [ + WorldInfo { + title "Polyline2D X3D test" + info ["This shows a Polyline2D" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.3 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry Polyline2D { + lineSegments [0 0, 0.2 0.2, 0.4 0, 0.6 0.2, 0.8 0, 1 0.2] + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-2D-Polypoint2d.x3dv b/regression_tests/x3d-2D-Polypoint2d.x3dv new file mode 100644 index 0000000..1962c3f --- /dev/null +++ b/regression_tests/x3d-2D-Polypoint2d.x3dv @@ -0,0 +1,23 @@ +#X3D V3.0 + +Group { + children [ + WorldInfo { + title "Polypoint2D X3D test" + info ["This shows a Polypoint2D" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry Polypoint2D { + point [0 0, 0.2 0.2, 0.4 0, 0.6 0.2, 0.8 0, 1 0.2] + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-2D-TriangleSet2d.x3dv b/regression_tests/x3d-2D-TriangleSet2d.x3dv new file mode 100644 index 0000000..c208b23 --- /dev/null +++ b/regression_tests/x3d-2D-TriangleSet2d.x3dv @@ -0,0 +1,24 @@ +#X3D V3.0 + +Group { + children [ + Viewpoint { position 0 0 2} + WorldInfo { + title "TriangleSet2D X3D test" + info ["This shows a TriangleSet2D" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry TriangleSet2D { + vertices [0 0, 0.2 0.2, 0.4 0, 0.6 0.2, 0.8 0, 1 0.2] + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-3D-IndexedTriangleFanSet.x3dv b/regression_tests/x3d-3D-IndexedTriangleFanSet.x3dv new file mode 100644 index 0000000..608dc9f --- /dev/null +++ b/regression_tests/x3d-3D-IndexedTriangleFanSet.x3dv @@ -0,0 +1,28 @@ +#X3D V3.0 + +Group { + children [ + Viewpoint { position 0 0 2} + WorldInfo { + title "IndexedTriangleFanSet X3D test" + info ["This shows a IndexedTriangleFanSet" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry IndexedTriangleFanSet { + color Color { color [1 0 0, 0 1 0, 0 0 1, 1 1 0, 1 0 1, 1 1 0, 1 1 1] } + coord Coordinate { point [0 0 0, 0 0.5 0, 0.1 0.4 -0.5, 0.2 0.3 0, 0.3 0.2 -0.5, 0.4 0.1 0, 0.5 0 -0.5] } + solid FALSE + ccw FALSE + index [0 1 2 3 -1 0 4 5 6] + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-3D-IndexedTriangleSet.x3dv b/regression_tests/x3d-3D-IndexedTriangleSet.x3dv new file mode 100644 index 0000000..a84eb7a --- /dev/null +++ b/regression_tests/x3d-3D-IndexedTriangleSet.x3dv @@ -0,0 +1,27 @@ +#X3D V3.0 + +Group { + children [ + Viewpoint { position 0 0 2} + WorldInfo { + title "IndexedTriangleSet X3D test" + info ["This shows a IndexedTriangleSet" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry IndexedTriangleSet { + color Color { color [1 0 0, 0 1 0, 0 0 1, 0 0 1, 0 1 0, 1 0 0] } + coord Coordinate { point [0 0 0, 0.2 0.2 0.2, 0.4 0 0, 0.6 0.2 0.2, 0.8 0 0, 1 0.2 0.2] } + solid FALSE + index [0 1 2 1 2 3 2 3 4 3 4 5] + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-3D-IndexedTriangleStripSet.x3dv b/regression_tests/x3d-3D-IndexedTriangleStripSet.x3dv new file mode 100644 index 0000000..d0f24ee --- /dev/null +++ b/regression_tests/x3d-3D-IndexedTriangleStripSet.x3dv @@ -0,0 +1,28 @@ +#X3D V3.0 + +Group { + children [ + Viewpoint { position 0 0 2} + WorldInfo { + title "IndexedTriangleStripSet X3D test" + info ["This shows a IndexedTriangleStripSet" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry IndexedTriangleStripSet { + color Color { color [1 0 0, 0 1 0, 0 0 1, 1 1 0, 1 0 1, 0 1 1, 1 1 1, 0.5 0.5 0] } + coord Coordinate { point [0 0 0, 0 0.5 0, 0.3 0 0, 0.3 0.5 -0.5, 0.6 0.0 0, 0.6 0.5 0, 0.6 0.5 -0.5, 0.8 0.0 0] } + solid FALSE + ccw FALSE + index [0 1 2 3 -1 4 5 6 7] + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-3D-LineSet.x3dv b/regression_tests/x3d-3D-LineSet.x3dv new file mode 100644 index 0000000..23925ec --- /dev/null +++ b/regression_tests/x3d-3D-LineSet.x3dv @@ -0,0 +1,27 @@ +#X3D V3.0 + +Group { + children [ + Viewpoint { position 0 0 2} + WorldInfo { + title "LineSet X3D test" + info ["This shows a LineSet" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry LineSet { + color Color { color [1 0 0, 0 1 0, 0 0 1, 1 1 0, 1 0 1, 1 1 0, 1 1 1] } + coord Coordinate { point [0 0 0, 0 0.5 0, 0.1 0.4 -0.5, 0.2 0.3 0, 0.3 0.2 -0.5, 0.4 0.1 0, 0.5 0 -0.5] } + #vertexCount [7] + vertexCount [3 4] + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-3D-TriangleFanSet.x3dv b/regression_tests/x3d-3D-TriangleFanSet.x3dv new file mode 100644 index 0000000..db2414e --- /dev/null +++ b/regression_tests/x3d-3D-TriangleFanSet.x3dv @@ -0,0 +1,28 @@ +#X3D V3.0 + +Group { + children [ + Viewpoint { position 0 0 2} + WorldInfo { + title "TriangleFanSet X3D test" + info ["This shows a TriangleFanSet" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry TriangleFanSet { + fanCount [7] + color Color { color [1 0 0, 0 1 0, 0 0 1, 1 1 0, 1 0 1, 1 1 0, 1 1 1] } + coord Coordinate { point [0 0 0, 0 0.5 0, 0.1 0.4 -0.5, 0.2 0.3 0, 0.3 0.2 -0.5, 0.4 0.1 0, 0.5 0 -0.5] } + solid FALSE + ccw FALSE + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-3D-TriangleSet.x3dv b/regression_tests/x3d-3D-TriangleSet.x3dv new file mode 100644 index 0000000..8e415fb --- /dev/null +++ b/regression_tests/x3d-3D-TriangleSet.x3dv @@ -0,0 +1,26 @@ +#X3D V3.0 + +Group { + children [ + Viewpoint { position 0 0 2} + WorldInfo { + title "TriangleSet test" + info ["This shows a box" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry TriangleSet { + color Color { color [1 0 0, 0 1 0, 0 0 1, 0 0 1, 0 1 0, 1 0 0] } + coord Coordinate { point [0 0 0, 0.2 0.2 0.2, 0.4 0 0, 0.6 0.2 0.2, 0.8 0 0, 1 0.2 0.2] } + solid FALSE + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-3D-TriangleStripSet.x3dv b/regression_tests/x3d-3D-TriangleStripSet.x3dv new file mode 100644 index 0000000..7dfbf89 --- /dev/null +++ b/regression_tests/x3d-3D-TriangleStripSet.x3dv @@ -0,0 +1,28 @@ +#X3D V3.0 + +Group { + children [ + Viewpoint { position 0 0 2} + WorldInfo { + title "TriangleStripSet X3D test" + info ["This shows a TriangleStripSet" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + } + geometry TriangleStripSet { + stripCount [6] + color Color { color [1 0 0, 0 1 0, 0 0 1, 1 1 0, 1 0 1, 1 1 0] } + coord Coordinate { point [0 0 0, 0 0.5 0, 0.3 0 0, 0.3 0.5 -0.5, 0.6 0.0 0, 0.6 0.5 0] } + solid FALSE + ccw FALSE + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-misc-ColorRGBA.x3dv b/regression_tests/x3d-misc-ColorRGBA.x3dv new file mode 100644 index 0000000..58b12c4 --- /dev/null +++ b/regression_tests/x3d-misc-ColorRGBA.x3dv @@ -0,0 +1,42 @@ +#X3D V3.0 + +Group { + children [ + WorldInfo { + info ["This shows IndexedFaceSets" "with RGBA color per vertex" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + title "ColorRGBA X3D Test test" + } + Transform { + translation -1.5 0 0 + children [ + Shape { + geometry IndexedFaceSet { + coordIndex [0 1 2 3 -1 1 7 4 2 -1 7 6 5 4 -1 0 3 5 6 -1 3 2 4 5 -1 6 7 1 0 -1] + color DEF COL ColorRGBA { + color [1 0 0 1, 0 1 0 0.2, 0 0 1 1, 1 1 0 0.5, 1 0 1 0.8, 0 1 1 0.2, 1 0.5 0.5 0.4, 0.5 0.5 1 0.7] + } + coord DEF COORD Coordinate { + point [-1 -1 1 1 -1 1 1 1 1 -1 1 1 1 1 -1 -1 1 -1 -1 -1 -1 1 -1 -1] + } + } + } + ] + } + Transform { + translation 1.5 0 0 + children [ + Shape { + geometry IndexedFaceSet { + colorIndex [0 1 2 3 4 5 6 7] + colorPerVertex FALSE + coordIndex [0 1 2 3 -1 1 7 4 2 -1 7 6 5 4 -1 0 3 5 6 -1 3 2 4 5 -1 6 7 1 0 -1] + color USE COL + coord USE COORD + } + } + ] + } + ] +} + + diff --git a/regression_tests/x3d-misc-HatchStyle.x3dv b/regression_tests/x3d-misc-HatchStyle.x3dv new file mode 100644 index 0000000..cc7503b --- /dev/null +++ b/regression_tests/x3d-misc-HatchStyle.x3dv @@ -0,0 +1,31 @@ +#X3D V3.0 + +Group { + children [ + Background2D { backColor 1 1 1} + WorldInfo { + title "HatchStyle X3D test" + info ["This shows a box" "with hatching properties" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2004 GPAC Team"] + } + + Transform { + children [ + Shape { + appearance Appearance { + material Material { diffuseColor 1.0 0.0 0.0 } + fillProperties FillProperties { + filled TRUE + hatchStyle 5 + hatchColor 0 0 1 + } + } + geometry Box { + size 1 1 0.5 + bboxCenter 0 0 0 + bboxSize 1 1 1 + } + } + ] + } + ] +} diff --git a/regression_tests/x3d-misc-KeySensor.x3dv b/regression_tests/x3d-misc-KeySensor.x3dv new file mode 100644 index 0000000..6eb8b4f --- /dev/null +++ b/regression_tests/x3d-misc-KeySensor.x3dv @@ -0,0 +1,73 @@ +#X3D V3.0 + +Group { + children [ + Group { + children [ + Group { + children [ + WorldInfo { + info ["This shows usage of X3D KeySensor" "and triggering of events" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002-2003 ENST"] + title "KeySensor X3D Test" + } + Shape { + appearance Appearance { + material Material { + diffuseColor 1 1 1 + } + } + geometry DEF TEXT Text { + string ["Hit any key", ""] + fontStyle FontStyle { + justify ["MIDDLE" "MIDDLE"] + } + } + } + DEF SC Script { + inputOnly SFString keyUp + inputOnly SFString keyDown + inputOnly SFInt32 sysKeyUp + inputOnly SFInt32 sysKeyDown + inputOnly SFBool altKey + inputOnly SFBool ctrlKey + inputOnly SFBool shiftKey + initializeOnly SFNode txt USE TEXT + url ["javascript: + function keyUp(value) { + txt.string[0] = 'Key up: ' + value; + } + function keyDown(value) { + txt.string[0] = 'Key down: ' + value; + } + function sysKeyUp(value) { + txt.string[0] = 'System Key up: ' + value; + } + function sysKeyDown(value) { + txt.string[0] = 'System Key down: ' + value; + } + function altKey(value) { + txt.string[1] = value ? 'ALT key pressed' : ''; + } + function shiftKey(value) { + txt.string[1] = value ? 'SHIFT key pressed' : ''; + } + function ctrlKey(value) { + txt.string[1] = value ? 'CTRL key pressed' : ''; + } + " + ] + } + DEF KS KeySensor {} + ] + } + ] + } + ] +} +ROUTE KS.keyPress TO SC.keyDown +ROUTE KS.keyRelease TO SC.keyUp +ROUTE KS.actionKeyPress TO SC.sysKeyDown +ROUTE KS.actionKeyRelease TO SC.sysKeyUp +ROUTE KS.altKey TO SC.altKey +ROUTE KS.controlKey TO SC.ctrlKey +ROUTE KS.shiftKey TO SC.shiftKey diff --git a/regression_tests/x3d-misc-StringSensor.x3dv b/regression_tests/x3d-misc-StringSensor.x3dv new file mode 100644 index 0000000..4c5d099 --- /dev/null +++ b/regression_tests/x3d-misc-StringSensor.x3dv @@ -0,0 +1,83 @@ +#X3D V3.0 + +Group { + children [ + WorldInfo { + title "StringSensor X3D Test" + info ["This shows usage of X3D StringSensor" "" "GPAC Regression Tests" "$Date: 2007/07/27 11:30:48 $ - $Revision: 1.2 $" "(C) 2002 ENST"] + } + + Transform { + translation 0 1 0 + children [ + Shape { + appearance DEF APP Appearance { + material Material { + diffuseColor 1 1 1 + } + } + geometry Text { + string [ "StringSensor" ] + fontStyle DEF FS FontStyle { + justify [ "MIDDLE" "MIDDLE" ] + } + } + } + ] + } + Transform { + translation -4 -1 0 + children [ + Shape { + appearance USE APP + geometry Text { + string [ "Edit:" ] + fontStyle USE FS + } + } + ] + } + Transform { + translation 1.8 -1 0 + children [ + Shape { + appearance USE APP + geometry DEF N3 Text { + string [""] + fontStyle USE FS + } + } + ] + } + Transform { + translation -4 -2.2 0 + children [ + Shape { + appearance USE APP + geometry Text { + string [ "Final:" ] + fontStyle USE FS + } + } + ] + } + Transform { + translation 1.8 -2.2 0 + children [ + Shape { + appearance USE APP + geometry DEF N2 Text { + string [""] + fontStyle USE FS + } + } + ] + } + DEF STR StringSensor { + } + ] +} + +ROUTE STR.enteredText TO N3.string +ROUTE STR.finalText TO N2.string + diff --git a/run_configure.sh b/run_configure.sh new file mode 100755 index 0000000..391d058 --- /dev/null +++ b/run_configure.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# calls ./configure and prints out build dependency for platform +# +# Shell-script based on code suplied in [1] +# [1] Guide du nouveau responsable Debian. Copyright © 1998-2002 Josip Rodin + +strace -f -o /tmp/log ./configure +echo "" +echo "" +echo "GPAC Build dependency for your platform" + for x in `dpkg -S $(grep open /tmp/log|\ + perl -pe 's!.* open\(\"([^\"]*).*!$1!' |\ + grep "^/"| sort | uniq|\ + grep -v "^\(/tmp\|/dev\|/proc\)" ) 2>/dev/null|\ + cut -f1 -d":"| sort | uniq`; \ + do \ + echo -n "$x (>=" `dpkg -s $x|grep ^Version|cut -f2 -d":"` "), "; \ + done +echo "" +echo "" diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..de6533b --- /dev/null +++ b/src/Makefile @@ -0,0 +1,234 @@ +include ../config.mak + +vpath %.c $(SRC_PATH)/src + +CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +## libgpac objects gathering: src/utils +LIBGPAC_UTILS=utils/os_divers.o utils/os_net.o utils/os_module.o utils/os_thread.o utils/list.o utils/base_encoding.o utils/bitstream.o utils/color.o utils/configfile.o utils/downloader.o utils/error.o utils/math.o utils/path2d.o utils/path2d_stroker.o utils/module.o utils/token.o utils/uni_bidi.o utils/url.o utils/utf.o utils/xml_parser.o + +## libgpac objects gathering: src/ietf +LIBGPAC_IETF=ietf/rtcp.o ietf/rtp.o ietf/rtp_packetizer.o ietf/rtp_pck_3gpp.o ietf/rtp_pck_mpeg12.o ietf/rtp_pck_mpeg4.o ietf/rtsp_command.o ietf/rtsp_common.o ietf/rtsp_response.o ietf/rtsp_session.o ietf/sdp.o ietf/rtp_depacketizer.o + +## libgpac objects gathering: src/bifs +LIBGPAC_BIFS=bifs/arith_decoder.o bifs/bifs_codec.o bifs/bifs_node_tables.o bifs/com_dec.o bifs/com_enc.o bifs/conditional.o bifs/field_decode.o bifs/field_encode.o bifs/memory_decoder.o bifs/predictive_mffield.o bifs/quantize.o bifs/script_dec.o bifs/script_enc.o bifs/unquantize.o + +## libgpac objects gathering: src/isomedia +LIBGPAC_ISOM=isomedia/avc_ext.o isomedia/box_code_3gpp.o isomedia/box_code_apple.o isomedia/box_code_base.o isomedia/box_code_isma.o isomedia/box_code_meta.o isomedia/box_dump.o isomedia/box_funcs.o isomedia/data_map.o isomedia/hint_track.o isomedia/hinting.o isomedia/isma_sample.o isomedia/isom_intern.o isomedia/isom_read.o isomedia/isom_store.o isomedia/isom_write.o isomedia/media.o isomedia/media_odf.o isomedia/meta.o isomedia/movie_fragments.o isomedia/sample_descs.o isomedia/stbl_read.o isomedia/stbl_write.o isomedia/track.o isomedia/tx3g.o + +## libgpac objects gathering: src/odf +LIBGPAC_ODF=odf/desc_private.o odf/descriptors.o odf/ipmpx_code.o odf/ipmpx_dump.o odf/ipmpx_parse.o odf/oci_codec.o odf/odf_code.o odf/odf_codec.o odf/odf_command.o odf/odf_dump.o odf/odf_parse.o odf/qos.o odf/slc.o + +## libgpac objects gathering: src/scenegraph +LIBGPAC_SCENE=scenegraph/base_scenegraph.o scenegraph/mpeg4_animators.o scenegraph/commands.o scenegraph/mpeg4_nodes.o scenegraph/mpeg4_valuator.o scenegraph/vrml_interpolators.o scenegraph/vrml_proto.o scenegraph/vrml_route.o scenegraph/vrml_script.o scenegraph/vrml_smjs.o scenegraph/vrml_tools.o scenegraph/x3d_nodes.o scenegraph/svg_attributes.o scenegraph/svg_types.o scenegraph/svg_smjs.o scenegraph/smil_anim.o scenegraph/smil_timing.o scenegraph/svg_properties.o scenegraph/dom_events.o scenegraph/dom_smjs.o scenegraph/xbl_process.o scenegraph/xml_ns.o + +## libgpac objects gathering: src/mcrypt +LIBGPAC_MCRYPT=mcrypt/cbc.o mcrypt/cfb.o mcrypt/ctr.o mcrypt/des.o mcrypt/ecb.o mcrypt/g_crypt.o mcrypt/ncfb.o mcrypt/nofb.o mcrypt/ofb.o mcrypt/rijndael-128.o mcrypt/rijndael-192.o mcrypt/rijndael-256.o mcrypt/stream.o mcrypt/tripledes.o mcrypt/sha1.o + +## libgpac objects gathering: src/media tools +LIBGPAC_MEDIATOOLS=media_tools/av_parsers.o media_tools/avilib.o media_tools/gpac_ogg.o media_tools/img.o media_tools/ismacryp.o media_tools/isom_hinter.o media_tools/isom_tools.o media_tools/media_export.o media_tools/media_import.o media_tools/mpeg2_ps.o media_tools/text_import.o media_tools/saf.o media_tools/mpegts.o media_tools/vobsub.o + +## libgpac objects gathering: src/scene_manager +LIBGPAC_SCENEMANAGER=scene_manager/loader_bt.o scene_manager/loader_isom.o scene_manager/loader_qt.o scene_manager/loader_xmt.o scene_manager/scene_dump.o scene_manager/scene_manager.o scene_manager/scene_stats.o scene_manager/swf_parse.o scene_manager/swf_bifs.o scene_manager/text_to_bifs.o scene_manager/encode_cbk.o scene_manager/encode_isom.o scene_manager/loader_svg.o + +## libgpac objects gathering: src/terminal +LIBGPAC_TERMINAL=terminal/channel.o terminal/clock.o terminal/decoder.o terminal/term_node_init.o terminal/inline.o terminal/input_sensor.o terminal/media_control.o terminal/media_manager.o terminal/media_memory.o terminal/media_object.o terminal/media_sensor.o terminal/network_service.o terminal/object_browser.o terminal/object_manager.o terminal/terminal.o terminal/svg_external.o + +## libgpac objects gathering: src/compositor +LIBGPAC_COMPOSITOR=compositor/audio_input.o compositor/audio_mixer.o compositor/audio_render.o compositor/bindable.o compositor/camera.o compositor/compositor.o compositor/compositor_2d.o compositor/compositor_3d.o compositor/compositor_node_init.o compositor/drawable.o compositor/events.o compositor/font_engine.o compositor/hardcoded_protos.o compositor/mesh.o compositor/mesh_collide.o compositor/mesh_tesselate.o compositor/mpeg4_animstream.o compositor/mpeg4_audio.o compositor/mpeg4_background.o compositor/mpeg4_background2d.o compositor/mpeg4_bitmap.o compositor/mpeg4_composite.o compositor/mpeg4_form.o compositor/mpeg4_geometry_2d.o compositor/mpeg4_geometry_3d.o compositor/mpeg4_geometry_ifs2d.o compositor/mpeg4_geometry_ils2d.o compositor/mpeg4_gradients.o compositor/mpeg4_grouping.o compositor/mpeg4_grouping_2d.o compositor/mpeg4_grouping_3d.o compositor/mpeg4_layer_2d.o compositor/mpeg4_layer_3d.o compositor/mpeg4_layout.o compositor/mpeg4_lighting.o compositor/mpeg4_path_layout.o compositor/mpeg4_sensors.o compositor/mpeg4_sound.o compositor/mpeg4_text.o compositor/mpeg4_textures.o compositor/mpeg4_timesensor.o compositor/mpeg4_viewport.o compositor/navigate.o compositor/offscreen_cache.o compositor/svg_base.o compositor/svg_font.o compositor/svg_geometry.o compositor/svg_grouping.o compositor/svg_media.o compositor/svg_paint_servers.o compositor/svg_text.o compositor/texturing.o compositor/texturing_gl.o compositor/visual_manager.o compositor/visual_manager_2d.o compositor/visual_manager_2d_draw.o compositor/visual_manager_3d.o compositor/visual_manager_3d_gl.o compositor/x3d_geometry.o + +## libgpac objects gathering: src/laser +LIBGPAC_LASER= +ifeq ($(DISABLE_SVG), no) +LIBGPAC_LASER=laser/lsr_enc.o laser/lsr_dec.o laser/lsr_tables.o +endif + +## libgpac objects gathering: la totale ... +OBJS=$(LIBGPAC_UTILS) $(LIBGPAC_MCRYPT) $(LIBGPAC_SCENE) $(LIBGPAC_IETF) $(LIBGPAC_BIFS) $(LIBGPAC_ISOM) $(LIBGPAC_ODF) $(LIBGPAC_MEDIATOOLS) $(LIBGPAC_SCENEMANAGER) $(LIBGPAC_TERMINAL) $(LIBGPAC_COMPOSITOR) $(LIBGPAC_LASER) + +CFLAGS+=-I../ -DGPAC_HAVE_CONFIG_H +NEED_LOCAL_LIB="no" +LINKLIBS= +SCENEGRAPH_CFLAGS= +MEDIATOOLS_CFLAGS= + +#1 - zlib support +ifeq ($(CONFIG_ZLIB), local) +CFLAGS+= -I$(LOCAL_INC_PATH)/zlib +NEED_LOCAL_LIB="yes" +endif +LINKLIBS+=-lz + +#2 - ssl support +ifeq ($(HAS_OPENSSL), yes) +LINKLIBS+=$(SSL_LIBS) +endif + +#3 - spidermonkey support +ifeq ($(CONFIG_JS),no) +else +SCENEGRAPH_CFLAGS+=$(JS_FLAGS) +ifeq ($(CONFIG_JS),local) +NEED_LOCAL_LIB="yes" +endif +LINKLIBS+= $(JS_LIBS) +endif + +#4 - JPEG support +ifeq ($(CONFIG_JPEG), no) +else +LINKLIBS+= -ljpeg +#local lib +ifeq ($(CONFIG_JPEG), local) +NEED_LOCAL_LIB="yes" +MEDIATOOLS_CFLAGS+=-I$(LOCAL_INC_PATH)/jpeg +endif +endif + +#5 - PNG support +ifeq ($(CONFIG_PNG), no) +else +LINKLIBS+= -lpng +ifeq ($(CONFIG_PNG), local) +NEED_LOCAL_LIB="yes" +MEDIATOOLS_CFLAGS+=-I$(LOCAL_INC_PATH)/png +endif +endif + + +ifneq ($(prefix), /usr/local) +EXTRALIBS+=-L$(prefix)/lib +else +ifneq ($(prefix), /usr) +EXTRALIBS+=-L$(prefix)/lib +endif +endif + +## libgpac compositor compilation options +COMPOSITOR_CFLAGS= +## SVG disabled +ifeq ($(DISABLE_SVG), yes) +COMPOSITOR_CFLAGS+=-DGPAC_DISABLE_SVG +endif + +# OpenGL support +ifeq ($(HAS_OPENGL),yes) +EXTRALIBS+= $(OGL_LIBS) +COMPOSITOR_CFLAGS+=$(OGL_INCLS) +endif + +ifeq ($(TRISCOPE_MODE), yes) +COMPOSITOR_CFLAGS+=-DGPAC_TRISCOPE_MODE +EXTRALIBS+= -Lcompositor/triscope_renoir +#EXTRALIBS+= -lViRen_linux -lViRenDrivers_linux -lToolTraceManager -lToolMemoryManager -lOsalRealtime -lOsalFile -lOsalUriHandler -lOsalCharStar -lOsalMath +EXTRALIBS+= -lViRen_linux -lViRenDrivers_linux -lEmulRen_linux -lToolTraceManager -lToolMemoryManager -lOsalRealtime -lOsalFile -lOsalUriHandler -lOsalCharStar -lOsalMath +LIBGPAC_COMPOSITOR+=compositor/triscope_renoir/triscope_renoir.o compositor/chrono.o +#special malloc for renoir memory +LIBGPAC_COMPOSITOR+=compositor/triscope_renoir/gltx_malloc.o +endif + +ifeq ($(GPAC_USE_TINYGL), yes) +COMPOSITOR_CFLAGS+=-I$(SRC_PATH)/../../TinyGL/include +endif +ifeq ($(ENABLE_JOYSTICK), yes) +COMPOSITOR_CFLAGS+=-DENABLE_JOYSTICK +endif + + +## libgpac media tools compilation options +ifeq ($(GPACREADONLY), yes) +MEDIATOOLS_CFLAGS=-DGPAC_READ_ONLY +endif + +ifeq ($(NEED_LOCAL_LIB), "yes") +EXTRALIBS+=-L../extra_lib/lib/gcc +endif +EXTRALIBS+=$(LINKLIBS) + +ifeq ($(GPAC_ENST), yes) +OBJS+=$(LIBGPAC_ENST) +EXTRALIBS+=-liconv +endif + + + +##libgpac library output +LIB=libgpac.$(DYN_LIB_SUFFIX) +ifeq ($(CONFIG_WIN32),yes) +#LDFLAGS+=-export-symbols libgpac.def +else +ifeq ($(CONFIG_DARWIN),yes) +LDFLAGS+=-install_name $(prefix)/lib/$(LIB) +endif +EXTRALIBS+=$(GPAC_SH_FLAGS) +endif + +ifeq ($(WANT_PIC),yes) +OBJSPIC=$(OBJS:.o=.opic) +else +OBJSPIC=$(OBJS) +endif + +LD_SONAME="-Wl,-soname,$(LIB)" +ifeq ($(CONFIG_DARWIN), yes) +LD_SONAME="" +endif + + +SRCS := $(OBJS:.o=.c) + +all: lib + +lib: $(LIB) + +#there's a bunch of warnings in there, get rid of them +mcrypt: CFLAGS= $(OPTFLAGS) -w -I$(SRC_PATH)/include +mcrypt: $(LIBGPAC_MCRYPT) + +scenegraph: CFLAGS += $(SCENEGRAPH_CFLAGS) +scenegraph: $(LIBGPAC_SCENE) + +mediatools: CFLAGS += $(MEDIATOOLS_CFLAGS) +mediatools: $(LIBGPAC_MEDIATOOLS) + +compositor: CFLAGS+=$(COMPOSITOR_CFLAGS) +compositor: $(LIBGPAC_COMPOSITOR) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +$(LIB): $(LIBGPAC_UTILS) $(LIBGPAC_IETF) $(LIBGPAC_BIFS) $(LIBGPAC_ODF) $(LIBGPAC_LASER) $(LIBGPAC_ISOM) $(LIBGPAC_SCENEMANAGER) $(LIBGPAC_TERMINAL) compositor scenegraph mediatools mcrypt $(OBJSPIC) + ar cr ../bin/gcc/libgpac_static.a $(OBJS) + ranlib ../bin/gcc/libgpac_static.a + $(CC) $(SHFLAGS) $(LD_SONAME) $(LDFLAGS) -o ../bin/gcc/$@ $(OBJSPIC) $(EXTRALIBS) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +clean: + rm -f $(OBJS) $(LIB) + rm -rf ../bin/gcc/libgpac_static.a + +distclean: clean + rm -f Makefile.bak .depend + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/src/bifs/arith_decoder.c b/src/bifs/arith_decoder.c new file mode 100644 index 0000000..99dd17f --- /dev/null +++ b/src/bifs/arith_decoder.c @@ -0,0 +1,281 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "quant.h" + +/* + Adaptive Arithmethic Decoder from Annex G (Normative) + + Original code: + sac.c, part of tmndecode (H.263 decoder) + + Copyright (C) 1996 Telenor R&D, Norway + Karl Olav Lillevold + + Karl Olav Lillevold + Telenor Research and Development + P.O.Box 83 tel.: +47 63 84 84 00 + N-2007 Kjeller, Norway fax.: +47 63 81 00 76 + + Robert Danielsen e-mail: Robert.Danielsen@nta.no + Telenor Research and Development www: http://www.nta.no/brukere/DVC/ + P.O.Box 83 tel.: +47 63 84 84 00 + N-2007 Kjeller, Norway fax.: +47 63 81 00 76 + + Original license: GNU Lesser General Public License + + This is basically the code from IM1 (the spec is not up-to-date with ref soft) +*/ + + + + +#define AAM_Q1 16384 +#define AAM_Q2 32768 +#define AAM_Q3 49152 +#define AAM_TOP 65535 +#define AAM_ZEROMAX 22 +#define AAM_MAX_FREQ 16383 + +struct _aamodel +{ + s32 nb_symb; + s32 *cumul_freq; + s32 *freq; +}; + +struct _aadecoder +{ + u32 low, high, code_value; + s32 zero_run; + Bool Bit; + GF_BitStream *bs; + Bool needs_flush; + u32 read_bits, used_bits; + /*amount of fake bits (bit read after end of stream)*/ + u32 skip_bits; +}; + + + +GF_AAModel *gp_bifs_aa_model_new() +{ + GF_AAModel *tmp; + GF_SAFEALLOC(tmp, GF_AAModel); + return tmp; +} + +void gp_bifs_aa_model_del(GF_AAModel *model) +{ + if (model->cumul_freq) free(model->cumul_freq); + if (model->freq) free(model->freq); + free(model); +} + +void gp_bifs_aa_model_init(GF_AAModel *model, u32 nbBits) +{ + s32 i; + model->nb_symb = 1<cumul_freq) free(model->cumul_freq); + if (model->freq) free(model->freq); + model->freq = (s32*)malloc(sizeof(s32) * model->nb_symb); + model->cumul_freq = (s32*)malloc(sizeof(s32) * (model->nb_symb+1)); + + for(i=0; inb_symb; i++) { + model->freq[i]=1; + model->cumul_freq[i] = model->nb_symb - i; + } + model->cumul_freq[model->nb_symb] = 0; +} + +void UpdateAAModel(GF_AAModel *model, s32 symbol) +{ + s32 sum, i; + + /*The model is rescaled to avoid overflow*/ + if (model->cumul_freq[0] == AAM_MAX_FREQ) { + sum = 0; + for (i=model->nb_symb-1; i>=0; i--) { + model->freq[i] = (model->freq[i]+1)/2; + sum += model->freq[i]; + model->cumul_freq[i] = sum; + } + model->cumul_freq[model->nb_symb] = 0; + } + model->freq[symbol] ++; + model->cumul_freq[symbol] ++; + while (symbol>0) model->cumul_freq[--symbol] ++; +} + + +void gf_bs_rewind_bits(GF_BitStream *bs, u64 nbBits); + +GF_AADecoder *gp_bifs_aa_dec_new(GF_BitStream *bs) +{ + GF_AADecoder *tmp; + GF_SAFEALLOC(tmp, GF_AADecoder); + tmp->bs = bs; + return tmp; +} +void gp_bifs_aa_dec_del(GF_AADecoder *dec) +{ + free(dec); +} + +static Bool bit_out_psc_layer(GF_AADecoder *dec) +{ + s32 v; + if (gf_bs_bits_available(dec->bs) ) { + v = gf_bs_read_int(dec->bs, 1) ? 1 : 0; + dec->read_bits++; + } else { + v = (dec->zero_run == AAM_MAX_FREQ) ? 1 : 0; + dec->skip_bits++; + } + + if (dec->zero_run == AAM_ZEROMAX) { + if (!v) return 0; + v = gf_bs_read_int(dec->bs, 1) ? 1 : 0; + dec->zero_run = 0; + dec->read_bits++; + dec->used_bits++; + } + dec->Bit = v; + dec->needs_flush = 1; + + if (!dec->Bit) + dec->zero_run++; + else + dec->zero_run = 0; + + return 1; +} + +void gp_bifs_aa_dec_resync(GF_AADecoder *dec) +{ + u32 rewind; + if (!dec->needs_flush) return; + rewind = dec->read_bits - 1 - (dec->used_bits+1); + /*magic number from IM1 (spec is wrong there)*/ + rewind = 14; + if (dec->skip_bits < rewind) gf_bs_rewind_bits(dec->bs, rewind - dec->skip_bits); + + dec->needs_flush = 0; + dec->code_value = 0; + dec->low = 0; + dec->high = AAM_TOP; + dec->zero_run = 0; + dec->used_bits = dec->read_bits = 0; +} + +void gp_bifs_aa_dec_flush(GF_AADecoder *dec) +{ + if (!bit_out_psc_layer(dec)) return; + dec->code_value = 2 * dec->code_value + dec->Bit; + if (!bit_out_psc_layer(dec)) return; + dec->code_value = (2 * dec->code_value + dec->Bit) & AAM_TOP; + + dec->low = 0; + dec->high = AAM_TOP; + dec->zero_run = 0; +} + +void gp_bifs_aa_dec_resync_bit(GF_AADecoder *dec) +{ + if (dec->needs_flush && (dec->skip_bits < 16)) + gf_bs_rewind_bits(dec->bs, 16 - dec->skip_bits); + + dec->needs_flush = 0; +} + +s32 gp_bifs_aa_dec_get_bit(GF_AADecoder *dec) +{ + /*get flag*/ + s32 res = dec->code_value >> 15; + /*get next bit*/ + bit_out_psc_layer(dec); + dec->code_value = (2 * dec->code_value + dec->Bit) & AAM_TOP; + return res; +} + +void gp_bifs_aa_dec_reset(GF_AADecoder *dec) +{ + s32 i; + dec->code_value = 0; + dec->low = 0; + dec->high = AAM_TOP; + dec->zero_run = 0; + for (i=0; i < 16; i++) { + if (!bit_out_psc_layer(dec)) return; + dec->code_value = 2 * dec->code_value + dec->Bit; + } + dec->used_bits = 0; +} + + +static s32 AADec_Dec(GF_AADecoder *dec, GF_AAModel *model) +{ + u32 sac_index, len; + s32 sum; + + len = dec->high - dec->low + 1; + sum = (-1 + (dec->code_value - dec->low + 1) * model->cumul_freq[0]) / len; + + for (sac_index = 1; model->cumul_freq[sac_index] > sum; sac_index++) { + } + dec->high = dec->low - 1 + (len * model->cumul_freq[sac_index-1]) / model->cumul_freq[0]; + dec->low += (len * model->cumul_freq[sac_index]) / model->cumul_freq[0]; + + for ( ; ; ) { + if (dec->high < AAM_Q2) { + } else if (dec->low >= AAM_Q2) { + dec->code_value -= AAM_Q2; + dec->low -= AAM_Q2; + dec->high -= AAM_Q2; + } + else if (dec->low >= AAM_Q1 && dec->high < AAM_Q3) { + dec->code_value -= AAM_Q1; + dec->low -= AAM_Q1; + dec->high -= AAM_Q1; + } else { + break; + } + + dec->low *= 2; + dec->high = 2 * dec->high + 1; + if (!bit_out_psc_layer(dec)) return -1; + dec->code_value = 2 * dec->code_value + dec->Bit; + dec->used_bits++; + } + return (sac_index - 1); +} + +s32 gp_bifs_aa_decode(GF_AADecoder *dec, GF_AAModel *model) +{ + s32 v; + v = AADec_Dec(dec, model); + UpdateAAModel(model, v); + return v; +} + diff --git a/src/bifs/bifs_codec.c b/src/bifs/bifs_codec.c new file mode 100644 index 0000000..61f6f04 --- /dev/null +++ b/src/bifs/bifs_codec.c @@ -0,0 +1,455 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + + +static GF_Err ParseConfig(GF_BitStream *bs, BIFSStreamInfo *info, u32 version) +{ + Bool hasSize, cmd_stream; + + if (version==2) { + info->config.Use3DMeshCoding = gf_bs_read_int(bs, 1); + info->config.UsePredictiveMFField = gf_bs_read_int(bs, 1); + } + info->config.NodeIDBits = gf_bs_read_int(bs, 5); + info->config.RouteIDBits = gf_bs_read_int(bs, 5); + if (version==2) { + info->config.ProtoIDBits = gf_bs_read_int(bs, 5); + } + cmd_stream = gf_bs_read_int(bs, 1); + + if (cmd_stream) { + info->config.PixelMetrics = gf_bs_read_int(bs, 1); + hasSize = gf_bs_read_int(bs, 1); + if (hasSize) { + info->config.Width = gf_bs_read_int(bs, 16); + info->config.Height = gf_bs_read_int(bs, 16); + } + gf_bs_align(bs); + + if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; + } else { + info->config.BAnimRAP = gf_bs_read_int(bs, 1); + info->config.elementaryMasks = gf_list_new(); + while (1) { + /*u32 node_id = */gf_bs_read_int(bs, info->config.NodeIDBits); + /*this assumes only FDP, BDP and IFS2D (no elem mask)*/ + if (gf_bs_read_int(bs, 1) == 0) break; + } + gf_bs_align(bs); + if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) return GF_NOT_SUPPORTED; + return GF_OK; + } +} + +static void bifs_info_del(BIFSStreamInfo *info) +{ + while (1) { + BIFSElementaryMask *em = (BIFSElementaryMask *)gf_list_last(info->config.elementaryMasks); + if (!em) break; + gf_list_rem_last(info->config.elementaryMasks); + free(em); + } + free(info); +} + +GF_EXPORT +GF_BifsDecoder *gf_bifs_decoder_new(GF_SceneGraph *scenegraph, Bool command_dec) +{ + GF_BifsDecoder *tmp; + GF_SAFEALLOC(tmp, GF_BifsDecoder); + + tmp->QPs = gf_list_new(); + tmp->streamInfo = gf_list_new(); + tmp->info = NULL; + + tmp->pCurrentProto = NULL; + tmp->scenegraph = scenegraph; + tmp->command_buffers = gf_list_new(); + if (command_dec) { + tmp->dec_memory_mode = 1; + tmp->force_keep_qp = 1; + } + tmp->current_graph = NULL; + return tmp; +} + + +BIFSStreamInfo *gf_bifs_dec_get_stream(GF_BifsDecoder * codec, u16 ESID) +{ + u32 i; + BIFSStreamInfo *ptr; + + i=0; + while ((ptr = (BIFSStreamInfo *) gf_list_enum(codec->streamInfo, &i))) { + if(ptr->ESID==ESID) return ptr; + } + return NULL; +} + +GF_EXPORT +GF_Err gf_bifs_decoder_configure_stream(GF_BifsDecoder * codec, u16 ESID, char *DecoderSpecificInfo, u32 DecoderSpecificInfoLength, u32 objectTypeIndication) +{ + GF_BitStream *bs; + BIFSStreamInfo *pInfo; + GF_Err e; + + if (!DecoderSpecificInfo) { + /* Hack for T-DMB non compliant streams */ + GF_SAFEALLOC(pInfo, BIFSStreamInfo); + pInfo->ESID = ESID; + pInfo->config.PixelMetrics = 1; + pInfo->config.version = (objectTypeIndication==2) ? 1 : 2; + return gf_list_add(codec->streamInfo, pInfo); + } +// gf_mx_p(codec->mx); + if (gf_bifs_dec_get_stream(codec, ESID) != NULL) { +// gf_mx_v(codec->mx); + return GF_BAD_PARAM; + } + + + bs = gf_bs_new(DecoderSpecificInfo, DecoderSpecificInfoLength, GF_BITSTREAM_READ); + GF_SAFEALLOC(pInfo, BIFSStreamInfo); + pInfo->ESID = ESID; + + pInfo->config.version = objectTypeIndication; + /*parse config with indicated oti*/ + e = ParseConfig(bs, pInfo, (u32) objectTypeIndication); + if (e) { + pInfo->ESID = ESID; + /*some content indicates a wrong OTI, so try to parse with v1 or v2*/ + gf_bs_seek(bs, 0); + /*try with reverse config*/ + e = ParseConfig(bs, pInfo, (objectTypeIndication==2) ? 1 : 2); + pInfo->config.version = (objectTypeIndication==2) ? 1 : 2; + } + + if (e && (e != GF_ODF_INVALID_DESCRIPTOR)) { + free(pInfo); + gf_bs_del(bs); + return GF_BIFS_UNKNOWN_VERSION; + } + gf_bs_del(bs); + + //first stream, configure size + if (!codec->ignore_size && !gf_list_count(codec->streamInfo)) { + gf_sg_set_scene_size_info(codec->scenegraph, pInfo->config.Width, pInfo->config.Height, pInfo->config.PixelMetrics); + } + + gf_list_add(codec->streamInfo, pInfo); +// gf_mx_v(codec->mx); + return GF_OK; +} + +GF_EXPORT +void gf_bifs_decoder_ignore_size_info(GF_BifsDecoder *codec) +{ + if (codec) codec->ignore_size = 1; +} + + +GF_EXPORT +GF_Err gf_bifs_decoder_remove_stream(GF_BifsDecoder *codec, u16 ESID) +{ + u32 i; + BIFSStreamInfo *ptr; + + i=0; + while ((ptr = (BIFSStreamInfo*)gf_list_enum(codec->streamInfo, &i))) { + if(ptr->ESID==ESID) { + free(ptr); + gf_list_rem(codec->streamInfo, i-1); + return GF_OK; + } + } + return GF_BAD_PARAM; +} + +GF_EXPORT +void gf_bifs_decoder_del(GF_BifsDecoder *codec) +{ + assert(gf_list_count(codec->QPs)==0); + gf_list_del(codec->QPs); + + /*destroy all config*/ + while (gf_list_count(codec->streamInfo)) { + BIFSStreamInfo *p = (BIFSStreamInfo*)gf_list_get(codec->streamInfo, 0); + bifs_info_del(p); + gf_list_rem(codec->streamInfo, 0); + } + gf_list_del(codec->streamInfo); + + while (gf_list_count(codec->command_buffers)) { + CommandBufferItem *cbi = (CommandBufferItem *)gf_list_get(codec->command_buffers, 0); + free(cbi); + gf_list_rem(codec->command_buffers, 0); + } + gf_list_del(codec->command_buffers); + +// gf_mx_del(codec->mx); + free(codec); +} + + +void BD_EndOfStream(void *co) +{ + ((GF_BifsDecoder *) co)->LastError = GF_IO_ERR; +} + +void gf_bs_set_eos_callback(GF_BitStream *bs, void (*EndOfStream)(void *par), void *par); + +GF_EXPORT +GF_Err gf_bifs_decode_au(GF_BifsDecoder *codec, u16 ESID, char *data, u32 data_length, Double ts_offset) +{ + GF_BitStream *bs; + GF_Err e; + + if (!codec || !data || codec->dec_memory_mode) return GF_BAD_PARAM; + +// gf_mx_p(codec->mx); + codec->info = gf_bifs_dec_get_stream(codec, ESID); + if (!codec->info) { +// gf_mx_v(codec->mx); + return GF_BAD_PARAM; + } + /*setup current scene graph*/ + codec->current_graph = codec->scenegraph; + codec->cts_offset = ts_offset; + + bs = gf_bs_new(data, data_length, GF_BITSTREAM_READ); + gf_bs_set_eos_callback(bs, BD_EndOfStream, codec); + + if (codec->info->config.elementaryMasks) { + e = GF_NOT_SUPPORTED; + } else { + e = gf_bifs_dec_command(codec, bs); + } + gf_bs_del(bs); + /*reset current config*/ + codec->info = NULL; + codec->current_graph = NULL; +// gf_mx_v(codec->mx); + return e; +} + + +void gf_bifs_decoder_set_time_offset(GF_BifsDecoder *codec, Double ts) +{ + codec->cts_offset = ts; +} + +GF_Node *gf_bifs_enc_find_node(GF_BifsEncoder *codec, u32 nodeID) +{ + if (codec->current_proto_graph) return gf_sg_find_node(codec->current_proto_graph, nodeID); + assert(codec->scene_graph); + return gf_sg_find_node(codec->scene_graph, nodeID); +} + + +GF_EXPORT +GF_BifsEncoder *gf_bifs_encoder_new(GF_SceneGraph *graph) +{ + GF_BifsEncoder * tmp; + GF_SAFEALLOC(tmp, GF_BifsEncoder); + if (!tmp) return NULL; + tmp->QPs = gf_list_new(); + tmp->streamInfo = gf_list_new(); + tmp->info = NULL; + tmp->encoded_nodes = gf_list_new(); + tmp->scene_graph = graph; + return tmp; +} + +static BIFSStreamInfo *BE_GetStream(GF_BifsEncoder * codec, u16 ESID) +{ + u32 i; + BIFSStreamInfo *ptr; + + i=0; + while ((ptr = (BIFSStreamInfo*)gf_list_enum(codec->streamInfo, &i))) { + if(ptr->ESID==ESID) return ptr; + } + return NULL; +} + +GF_EXPORT +void gf_bifs_encoder_del(GF_BifsEncoder *codec) +{ + assert(gf_list_count(codec->QPs)==0); + gf_list_del(codec->QPs); + /*destroy all config*/ + while (gf_list_count(codec->streamInfo)) { + BIFSStreamInfo *p = (BIFSStreamInfo*)gf_list_get(codec->streamInfo, 0); + bifs_info_del(p); + gf_list_rem(codec->streamInfo, 0); + } + gf_list_del(codec->streamInfo); + gf_list_del(codec->encoded_nodes); +// gf_mx_del(codec->mx); + free(codec); +} + +GF_EXPORT +GF_Err gf_bifs_encoder_new_stream(GF_BifsEncoder *codec, u16 ESID, GF_BIFSConfig *cfg, Bool encodeNames, Bool has_predictive) +{ + u32 i, count; + BIFSStreamInfo *pInfo; + +// gf_mx_p(codec->mx); + if (BE_GetStream(codec, ESID) != NULL) { +// gf_mx_v(codec->mx); + return GF_BAD_PARAM; + } + + GF_SAFEALLOC(pInfo, BIFSStreamInfo); + pInfo->ESID = ESID; + codec->UseName = encodeNames; + pInfo->config.Height = cfg->pixelHeight; + pInfo->config.Width = cfg->pixelWidth; + pInfo->config.NodeIDBits = cfg->nodeIDbits; + pInfo->config.RouteIDBits = cfg->routeIDbits; + pInfo->config.ProtoIDBits = cfg->protoIDbits; + pInfo->config.PixelMetrics = cfg->pixelMetrics; + pInfo->config.version = (has_predictive || cfg->protoIDbits) ? 2 : 1; + pInfo->config.UsePredictiveMFField = has_predictive; + + if (cfg->elementaryMasks) { + pInfo->config.elementaryMasks = gf_list_new(); + count = gf_list_count(cfg->elementaryMasks); + for (i=0; ielementaryMasks, i); + GF_SAFEALLOC(bem, BIFSElementaryMask); + if (em->node_id) bem->node = gf_sg_find_node(codec->scene_graph, em->node_id); + else if (em->node_name) bem->node = gf_sg_find_node_by_name(codec->scene_graph, em->node_name); + bem->node_id = em->node_id; + gf_list_add(pInfo->config.elementaryMasks, bem); + } + } + + gf_list_add(codec->streamInfo, pInfo); +// gf_mx_v(codec->mx); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_bifs_encode_au(GF_BifsEncoder *codec, u16 ESID, GF_List *command_list, char **out_data, u32 *out_data_length) +{ + GF_BitStream *bs; + GF_Err e; + + if (!codec || !command_list || !out_data || !out_data_length) return GF_BAD_PARAM; + +// gf_mx_p(codec->mx); + codec->info = BE_GetStream(codec, ESID); + if (!codec->info) { +// gf_mx_v(codec->mx); + return GF_BAD_PARAM; + } + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + if (codec->info->config.elementaryMasks) { + e = GF_NOT_SUPPORTED; + } else { + e = gf_bifs_enc_commands(codec, command_list, bs); + } + gf_bs_align(bs); + gf_bs_get_content(bs, out_data, out_data_length); + gf_bs_del(bs); +// gf_mx_v(codec->mx); + return e; +} + +GF_EXPORT +GF_Err gf_bifs_encoder_get_config(GF_BifsEncoder *codec, u16 ESID, char **out_data, u32 *out_data_length) +{ + GF_BitStream *bs; + + if (!codec || !out_data || !out_data_length) return GF_BAD_PARAM; + +// gf_mx_p(codec->mx); + codec->info = BE_GetStream(codec, ESID); + if (!codec->info) { +// gf_mx_v(codec->mx); + return GF_BAD_PARAM; + } + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + if (codec->info->config.version==2) { + gf_bs_write_int(bs, codec->info->config.Use3DMeshCoding ? 1 : 0, 1); + gf_bs_write_int(bs, codec->info->config.UsePredictiveMFField ? 1 : 0, 1); + } + gf_bs_write_int(bs, codec->info->config.NodeIDBits, 5); + gf_bs_write_int(bs, codec->info->config.RouteIDBits, 5); + if (codec->info->config.version==2) { + gf_bs_write_int(bs, codec->info->config.ProtoIDBits, 5); + } + if (codec->info->config.elementaryMasks) { + u32 i, count; + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, codec->info->config.BAnimRAP, 1); + count = gf_list_count(codec->info->config.elementaryMasks); + for (i=0; iinfo->config.elementaryMasks, i); + if (em->node) gf_bs_write_int(bs, gf_node_get_id((GF_Node*)em->node), codec->info->config.NodeIDBits); + else gf_bs_write_int(bs, em->node_id, codec->info->config.NodeIDBits); + gf_bs_write_int(bs, (i+1==count) ? 0 : 1, 1); + } + } else { + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, codec->info->config.PixelMetrics ? 1 : 0, 1); + if (codec->info->config.Width || codec->info->config.Height) { + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, codec->info->config.Width, 16); + gf_bs_write_int(bs, codec->info->config.Height, 16); + } else { + gf_bs_write_int(bs, 0, 1); + } + } + + gf_bs_align(bs); + gf_bs_get_content(bs, out_data, out_data_length); + gf_bs_del(bs); +// gf_mx_v(codec->mx); + return GF_OK; +} + +GF_EXPORT +u8 gf_bifs_encoder_get_version(GF_BifsEncoder *codec, u16 ESID) +{ + u8 ret = 0; +// gf_mx_p(codec->mx); + codec->info = BE_GetStream(codec, ESID); + if (codec->info) ret = codec->info->config.version; +// gf_mx_v(codec->mx); + return ret; +} + diff --git a/src/bifs/bifs_node_tables.c b/src/bifs/bifs_node_tables.c new file mode 100644 index 0000000..9664a76 --- /dev/null +++ b/src/bifs/bifs_node_tables.c @@ -0,0 +1,920 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + DO NOT MOFIFY - File generated on GMT Thu Aug 07 11:43:26 2008 + + BY MPEG4Gen for GPAC Version 0.4.5-DEV +*/ + + + +#include + + +u32 ALL_GetNodeType(const u32 *table, const u32 count, u32 NodeTag, u32 Version) +{ + u32 i = 0; + while (i= SFWorldNode_V1_Count) return 0; + return SFWorldNode_V1_TypeToTag[NodeType]; + case NDT_SF3DNode: + if (NodeType >= SF3DNode_V1_Count) return 0; + return SF3DNode_V1_TypeToTag[NodeType]; + case NDT_SF2DNode: + if (NodeType >= SF2DNode_V1_Count) return 0; + return SF2DNode_V1_TypeToTag[NodeType]; + case NDT_SFStreamingNode: + if (NodeType >= SFStreamingNode_V1_Count) return 0; + return SFStreamingNode_V1_TypeToTag[NodeType]; + case NDT_SFAppearanceNode: + if (NodeType >= SFAppearanceNode_V1_Count) return 0; + return SFAppearanceNode_V1_TypeToTag[NodeType]; + case NDT_SFAudioNode: + if (NodeType >= SFAudioNode_V1_Count) return 0; + return SFAudioNode_V1_TypeToTag[NodeType]; + case NDT_SFBackground3DNode: + if (NodeType >= SFBackground3DNode_V1_Count) return 0; + return SFBackground3DNode_V1_TypeToTag[NodeType]; + case NDT_SFBackground2DNode: + if (NodeType >= SFBackground2DNode_V1_Count) return 0; + return SFBackground2DNode_V1_TypeToTag[NodeType]; + case NDT_SFGeometryNode: + if (NodeType >= SFGeometryNode_V1_Count) return 0; + return SFGeometryNode_V1_TypeToTag[NodeType]; + case NDT_SFColorNode: + if (NodeType >= SFColorNode_V1_Count) return 0; + return SFColorNode_V1_TypeToTag[NodeType]; + case NDT_SFTextureNode: + if (NodeType >= SFTextureNode_V1_Count) return 0; + return SFTextureNode_V1_TypeToTag[NodeType]; + case NDT_SFCoordinateNode: + if (NodeType >= SFCoordinateNode_V1_Count) return 0; + return SFCoordinateNode_V1_TypeToTag[NodeType]; + case NDT_SFCoordinate2DNode: + if (NodeType >= SFCoordinate2DNode_V1_Count) return 0; + return SFCoordinate2DNode_V1_TypeToTag[NodeType]; + case NDT_SFExpressionNode: + if (NodeType >= SFExpressionNode_V1_Count) return 0; + return SFExpressionNode_V1_TypeToTag[NodeType]; + case NDT_SFFaceDefMeshNode: + if (NodeType >= SFFaceDefMeshNode_V1_Count) return 0; + return SFFaceDefMeshNode_V1_TypeToTag[NodeType]; + case NDT_SFFaceDefTablesNode: + if (NodeType >= SFFaceDefTablesNode_V1_Count) return 0; + return SFFaceDefTablesNode_V1_TypeToTag[NodeType]; + case NDT_SFFaceDefTransformNode: + if (NodeType >= SFFaceDefTransformNode_V1_Count) return 0; + return SFFaceDefTransformNode_V1_TypeToTag[NodeType]; + case NDT_SFFAPNode: + if (NodeType >= SFFAPNode_V1_Count) return 0; + return SFFAPNode_V1_TypeToTag[NodeType]; + case NDT_SFFDPNode: + if (NodeType >= SFFDPNode_V1_Count) return 0; + return SFFDPNode_V1_TypeToTag[NodeType]; + case NDT_SFFITNode: + if (NodeType >= SFFITNode_V1_Count) return 0; + return SFFITNode_V1_TypeToTag[NodeType]; + case NDT_SFFogNode: + if (NodeType >= SFFogNode_V1_Count) return 0; + return SFFogNode_V1_TypeToTag[NodeType]; + case NDT_SFFontStyleNode: + if (NodeType >= SFFontStyleNode_V1_Count) return 0; + return SFFontStyleNode_V1_TypeToTag[NodeType]; + case NDT_SFTopNode: + if (NodeType >= SFTopNode_V1_Count) return 0; + return SFTopNode_V1_TypeToTag[NodeType]; + case NDT_SFLinePropertiesNode: + if (NodeType >= SFLinePropertiesNode_V1_Count) return 0; + return SFLinePropertiesNode_V1_TypeToTag[NodeType]; + case NDT_SFMaterialNode: + if (NodeType >= SFMaterialNode_V1_Count) return 0; + return SFMaterialNode_V1_TypeToTag[NodeType]; + case NDT_SFNavigationInfoNode: + if (NodeType >= SFNavigationInfoNode_V1_Count) return 0; + return SFNavigationInfoNode_V1_TypeToTag[NodeType]; + case NDT_SFNormalNode: + if (NodeType >= SFNormalNode_V1_Count) return 0; + return SFNormalNode_V1_TypeToTag[NodeType]; + case NDT_SFTextureCoordinateNode: + if (NodeType >= SFTextureCoordinateNode_V1_Count) return 0; + return SFTextureCoordinateNode_V1_TypeToTag[NodeType]; + case NDT_SFTextureTransformNode: + if (NodeType >= SFTextureTransformNode_V1_Count) return 0; + return SFTextureTransformNode_V1_TypeToTag[NodeType]; + case NDT_SFViewpointNode: + if (NodeType >= SFViewpointNode_V1_Count) return 0; + return SFViewpointNode_V1_TypeToTag[NodeType]; + case NDT_SFVisemeNode: + if (NodeType >= SFVisemeNode_V1_Count) return 0; + return SFVisemeNode_V1_TypeToTag[NodeType]; + default: + return 0; + } +} + + +u32 NDT_V1_GetNumBits(u32 NDT_Tag) +{ + switch (NDT_Tag) { + case NDT_SFWorldNode: + return SFWorldNode_V1_NUMBITS; + case NDT_SF3DNode: + return SF3DNode_V1_NUMBITS; + case NDT_SF2DNode: + return SF2DNode_V1_NUMBITS; + case NDT_SFStreamingNode: + return SFStreamingNode_V1_NUMBITS; + case NDT_SFAppearanceNode: + return SFAppearanceNode_V1_NUMBITS; + case NDT_SFAudioNode: + return SFAudioNode_V1_NUMBITS; + case NDT_SFBackground3DNode: + return SFBackground3DNode_V1_NUMBITS; + case NDT_SFBackground2DNode: + return SFBackground2DNode_V1_NUMBITS; + case NDT_SFGeometryNode: + return SFGeometryNode_V1_NUMBITS; + case NDT_SFColorNode: + return SFColorNode_V1_NUMBITS; + case NDT_SFTextureNode: + return SFTextureNode_V1_NUMBITS; + case NDT_SFCoordinateNode: + return SFCoordinateNode_V1_NUMBITS; + case NDT_SFCoordinate2DNode: + return SFCoordinate2DNode_V1_NUMBITS; + case NDT_SFExpressionNode: + return SFExpressionNode_V1_NUMBITS; + case NDT_SFFaceDefMeshNode: + return SFFaceDefMeshNode_V1_NUMBITS; + case NDT_SFFaceDefTablesNode: + return SFFaceDefTablesNode_V1_NUMBITS; + case NDT_SFFaceDefTransformNode: + return SFFaceDefTransformNode_V1_NUMBITS; + case NDT_SFFAPNode: + return SFFAPNode_V1_NUMBITS; + case NDT_SFFDPNode: + return SFFDPNode_V1_NUMBITS; + case NDT_SFFITNode: + return SFFITNode_V1_NUMBITS; + case NDT_SFFogNode: + return SFFogNode_V1_NUMBITS; + case NDT_SFFontStyleNode: + return SFFontStyleNode_V1_NUMBITS; + case NDT_SFTopNode: + return SFTopNode_V1_NUMBITS; + case NDT_SFLinePropertiesNode: + return SFLinePropertiesNode_V1_NUMBITS; + case NDT_SFMaterialNode: + return SFMaterialNode_V1_NUMBITS; + case NDT_SFNavigationInfoNode: + return SFNavigationInfoNode_V1_NUMBITS; + case NDT_SFNormalNode: + return SFNormalNode_V1_NUMBITS; + case NDT_SFTextureCoordinateNode: + return SFTextureCoordinateNode_V1_NUMBITS; + case NDT_SFTextureTransformNode: + return SFTextureTransformNode_V1_NUMBITS; + case NDT_SFViewpointNode: + return SFViewpointNode_V1_NUMBITS; + case NDT_SFVisemeNode: + return SFVisemeNode_V1_NUMBITS; + default: + return 0; + } +} + +u32 NDT_V1_GetNodeType(u32 NDT_Tag, u32 NodeTag) +{ + if(!NDT_Tag || !NodeTag) return 0; + switch(NDT_Tag) { + case NDT_SFWorldNode: + return ALL_GetNodeType(SFWorldNode_V1_TypeToTag, SFWorldNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SF3DNode: + return ALL_GetNodeType(SF3DNode_V1_TypeToTag, SF3DNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SF2DNode: + return ALL_GetNodeType(SF2DNode_V1_TypeToTag, SF2DNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFStreamingNode: + return ALL_GetNodeType(SFStreamingNode_V1_TypeToTag, SFStreamingNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFAppearanceNode: + return ALL_GetNodeType(SFAppearanceNode_V1_TypeToTag, SFAppearanceNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFAudioNode: + return ALL_GetNodeType(SFAudioNode_V1_TypeToTag, SFAudioNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFBackground3DNode: + return ALL_GetNodeType(SFBackground3DNode_V1_TypeToTag, SFBackground3DNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFBackground2DNode: + return ALL_GetNodeType(SFBackground2DNode_V1_TypeToTag, SFBackground2DNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFGeometryNode: + return ALL_GetNodeType(SFGeometryNode_V1_TypeToTag, SFGeometryNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFColorNode: + return ALL_GetNodeType(SFColorNode_V1_TypeToTag, SFColorNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFTextureNode: + return ALL_GetNodeType(SFTextureNode_V1_TypeToTag, SFTextureNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFCoordinateNode: + return ALL_GetNodeType(SFCoordinateNode_V1_TypeToTag, SFCoordinateNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFCoordinate2DNode: + return ALL_GetNodeType(SFCoordinate2DNode_V1_TypeToTag, SFCoordinate2DNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFExpressionNode: + return ALL_GetNodeType(SFExpressionNode_V1_TypeToTag, SFExpressionNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFFaceDefMeshNode: + return ALL_GetNodeType(SFFaceDefMeshNode_V1_TypeToTag, SFFaceDefMeshNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFFaceDefTablesNode: + return ALL_GetNodeType(SFFaceDefTablesNode_V1_TypeToTag, SFFaceDefTablesNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFFaceDefTransformNode: + return ALL_GetNodeType(SFFaceDefTransformNode_V1_TypeToTag, SFFaceDefTransformNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFFAPNode: + return ALL_GetNodeType(SFFAPNode_V1_TypeToTag, SFFAPNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFFDPNode: + return ALL_GetNodeType(SFFDPNode_V1_TypeToTag, SFFDPNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFFITNode: + return ALL_GetNodeType(SFFITNode_V1_TypeToTag, SFFITNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFFogNode: + return ALL_GetNodeType(SFFogNode_V1_TypeToTag, SFFogNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFFontStyleNode: + return ALL_GetNodeType(SFFontStyleNode_V1_TypeToTag, SFFontStyleNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFTopNode: + return ALL_GetNodeType(SFTopNode_V1_TypeToTag, SFTopNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFLinePropertiesNode: + return ALL_GetNodeType(SFLinePropertiesNode_V1_TypeToTag, SFLinePropertiesNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFMaterialNode: + return ALL_GetNodeType(SFMaterialNode_V1_TypeToTag, SFMaterialNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFNavigationInfoNode: + return ALL_GetNodeType(SFNavigationInfoNode_V1_TypeToTag, SFNavigationInfoNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFNormalNode: + return ALL_GetNodeType(SFNormalNode_V1_TypeToTag, SFNormalNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFTextureCoordinateNode: + return ALL_GetNodeType(SFTextureCoordinateNode_V1_TypeToTag, SFTextureCoordinateNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFTextureTransformNode: + return ALL_GetNodeType(SFTextureTransformNode_V1_TypeToTag, SFTextureTransformNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFViewpointNode: + return ALL_GetNodeType(SFViewpointNode_V1_TypeToTag, SFViewpointNode_V1_Count, NodeTag, GF_BIFS_V1); + case NDT_SFVisemeNode: + return ALL_GetNodeType(SFVisemeNode_V1_TypeToTag, SFVisemeNode_V1_Count, NodeTag, GF_BIFS_V1); + default: + return 0; + } +} + + + + +u32 NDT_V2_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType) +{ + if (!NodeType) return 0; + /* adjust according to the table version */ + /* v2: 0 reserved for extensions, 1 reserved for protos */ + if (NodeType == 1) return 0; + NodeType -= 2; + switch (Context_NDT_Tag) { + case NDT_SFWorldNode: + if (NodeType >= SFWorldNode_V2_Count) return 0; + return SFWorldNode_V2_TypeToTag[NodeType]; + case NDT_SF3DNode: + if (NodeType >= SF3DNode_V2_Count) return 0; + return SF3DNode_V2_TypeToTag[NodeType]; + case NDT_SF2DNode: + if (NodeType >= SF2DNode_V2_Count) return 0; + return SF2DNode_V2_TypeToTag[NodeType]; + case NDT_SFGeometryNode: + if (NodeType >= SFGeometryNode_V2_Count) return 0; + return SFGeometryNode_V2_TypeToTag[NodeType]; + case NDT_SFMaterialNode: + if (NodeType >= SFMaterialNode_V2_Count) return 0; + return SFMaterialNode_V2_TypeToTag[NodeType]; + case NDT_SFBAPNode: + if (NodeType >= SFBAPNode_V2_Count) return 0; + return SFBAPNode_V2_TypeToTag[NodeType]; + case NDT_SFBDPNode: + if (NodeType >= SFBDPNode_V2_Count) return 0; + return SFBDPNode_V2_TypeToTag[NodeType]; + case NDT_SFBodyDefTableNode: + if (NodeType >= SFBodyDefTableNode_V2_Count) return 0; + return SFBodyDefTableNode_V2_TypeToTag[NodeType]; + case NDT_SFBodySegmentConnectionHintNode: + if (NodeType >= SFBodySegmentConnectionHintNode_V2_Count) return 0; + return SFBodySegmentConnectionHintNode_V2_TypeToTag[NodeType]; + case NDT_SFPerceptualParameterNode: + if (NodeType >= SFPerceptualParameterNode_V2_Count) return 0; + return SFPerceptualParameterNode_V2_TypeToTag[NodeType]; + default: + return 0; + } +} + + +u32 NDT_V2_GetNumBits(u32 NDT_Tag) +{ + switch (NDT_Tag) { + case NDT_SFWorldNode: + return SFWorldNode_V2_NUMBITS; + case NDT_SF3DNode: + return SF3DNode_V2_NUMBITS; + case NDT_SF2DNode: + return SF2DNode_V2_NUMBITS; + case NDT_SFGeometryNode: + return SFGeometryNode_V2_NUMBITS; + case NDT_SFMaterialNode: + return SFMaterialNode_V2_NUMBITS; + case NDT_SFBAPNode: + return SFBAPNode_V2_NUMBITS; + case NDT_SFBDPNode: + return SFBDPNode_V2_NUMBITS; + case NDT_SFBodyDefTableNode: + return SFBodyDefTableNode_V2_NUMBITS; + case NDT_SFBodySegmentConnectionHintNode: + return SFBodySegmentConnectionHintNode_V2_NUMBITS; + case NDT_SFPerceptualParameterNode: + return SFPerceptualParameterNode_V2_NUMBITS; + default: + return 1; + } +} + +u32 NDT_V2_GetNodeType(u32 NDT_Tag, u32 NodeTag) +{ + if(!NDT_Tag || !NodeTag) return 0; + switch(NDT_Tag) { + case NDT_SFWorldNode: + return ALL_GetNodeType(SFWorldNode_V2_TypeToTag, SFWorldNode_V2_Count, NodeTag, GF_BIFS_V2); + case NDT_SF3DNode: + return ALL_GetNodeType(SF3DNode_V2_TypeToTag, SF3DNode_V2_Count, NodeTag, GF_BIFS_V2); + case NDT_SF2DNode: + return ALL_GetNodeType(SF2DNode_V2_TypeToTag, SF2DNode_V2_Count, NodeTag, GF_BIFS_V2); + case NDT_SFGeometryNode: + return ALL_GetNodeType(SFGeometryNode_V2_TypeToTag, SFGeometryNode_V2_Count, NodeTag, GF_BIFS_V2); + case NDT_SFMaterialNode: + return ALL_GetNodeType(SFMaterialNode_V2_TypeToTag, SFMaterialNode_V2_Count, NodeTag, GF_BIFS_V2); + case NDT_SFBAPNode: + return ALL_GetNodeType(SFBAPNode_V2_TypeToTag, SFBAPNode_V2_Count, NodeTag, GF_BIFS_V2); + case NDT_SFBDPNode: + return ALL_GetNodeType(SFBDPNode_V2_TypeToTag, SFBDPNode_V2_Count, NodeTag, GF_BIFS_V2); + case NDT_SFBodyDefTableNode: + return ALL_GetNodeType(SFBodyDefTableNode_V2_TypeToTag, SFBodyDefTableNode_V2_Count, NodeTag, GF_BIFS_V2); + case NDT_SFBodySegmentConnectionHintNode: + return ALL_GetNodeType(SFBodySegmentConnectionHintNode_V2_TypeToTag, SFBodySegmentConnectionHintNode_V2_Count, NodeTag, GF_BIFS_V2); + case NDT_SFPerceptualParameterNode: + return ALL_GetNodeType(SFPerceptualParameterNode_V2_TypeToTag, SFPerceptualParameterNode_V2_Count, NodeTag, GF_BIFS_V2); + default: + return 0; + } +} + + + + +u32 NDT_V3_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType) +{ + if (!NodeType) return 0; + /* adjust according to the table version */ + /* v3: 0 reserved for extensions */ + NodeType -= 1; + switch (Context_NDT_Tag) { + case NDT_SFWorldNode: + if (NodeType >= SFWorldNode_V3_Count) return 0; + return SFWorldNode_V3_TypeToTag[NodeType]; + case NDT_SF3DNode: + if (NodeType >= SF3DNode_V3_Count) return 0; + return SF3DNode_V3_TypeToTag[NodeType]; + case NDT_SF2DNode: + if (NodeType >= SF2DNode_V3_Count) return 0; + return SF2DNode_V3_TypeToTag[NodeType]; + case NDT_SFTemporalNode: + if (NodeType >= SFTemporalNode_V3_Count) return 0; + return SFTemporalNode_V3_TypeToTag[NodeType]; + default: + return 0; + } +} + + +u32 NDT_V3_GetNumBits(u32 NDT_Tag) +{ + switch (NDT_Tag) { + case NDT_SFWorldNode: + return SFWorldNode_V3_NUMBITS; + case NDT_SF3DNode: + return SF3DNode_V3_NUMBITS; + case NDT_SF2DNode: + return SF2DNode_V3_NUMBITS; + case NDT_SFTemporalNode: + return SFTemporalNode_V3_NUMBITS; + default: + return 0; + } +} + +u32 NDT_V3_GetNodeType(u32 NDT_Tag, u32 NodeTag) +{ + if(!NDT_Tag || !NodeTag) return 0; + switch(NDT_Tag) { + case NDT_SFWorldNode: + return ALL_GetNodeType(SFWorldNode_V3_TypeToTag, SFWorldNode_V3_Count, NodeTag, GF_BIFS_V3); + case NDT_SF3DNode: + return ALL_GetNodeType(SF3DNode_V3_TypeToTag, SF3DNode_V3_Count, NodeTag, GF_BIFS_V3); + case NDT_SF2DNode: + return ALL_GetNodeType(SF2DNode_V3_TypeToTag, SF2DNode_V3_Count, NodeTag, GF_BIFS_V3); + case NDT_SFTemporalNode: + return ALL_GetNodeType(SFTemporalNode_V3_TypeToTag, SFTemporalNode_V3_Count, NodeTag, GF_BIFS_V3); + default: + return 0; + } +} + + + + +u32 NDT_V4_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType) +{ + if (!NodeType) return 0; + /* adjust according to the table version */ + /* v4: 0 reserved for extensions */ + NodeType -= 1; + switch (Context_NDT_Tag) { + case NDT_SFWorldNode: + if (NodeType >= SFWorldNode_V4_Count) return 0; + return SFWorldNode_V4_TypeToTag[NodeType]; + case NDT_SF3DNode: + if (NodeType >= SF3DNode_V4_Count) return 0; + return SF3DNode_V4_TypeToTag[NodeType]; + case NDT_SF2DNode: + if (NodeType >= SF2DNode_V4_Count) return 0; + return SF2DNode_V4_TypeToTag[NodeType]; + case NDT_SFTextureNode: + if (NodeType >= SFTextureNode_V4_Count) return 0; + return SFTextureNode_V4_TypeToTag[NodeType]; + default: + return 0; + } +} + + +u32 NDT_V4_GetNumBits(u32 NDT_Tag) +{ + switch (NDT_Tag) { + case NDT_SFWorldNode: + return SFWorldNode_V4_NUMBITS; + case NDT_SF3DNode: + return SF3DNode_V4_NUMBITS; + case NDT_SF2DNode: + return SF2DNode_V4_NUMBITS; + case NDT_SFTextureNode: + return SFTextureNode_V4_NUMBITS; + default: + return 0; + } +} + +u32 NDT_V4_GetNodeType(u32 NDT_Tag, u32 NodeTag) +{ + if(!NDT_Tag || !NodeTag) return 0; + switch(NDT_Tag) { + case NDT_SFWorldNode: + return ALL_GetNodeType(SFWorldNode_V4_TypeToTag, SFWorldNode_V4_Count, NodeTag, GF_BIFS_V4); + case NDT_SF3DNode: + return ALL_GetNodeType(SF3DNode_V4_TypeToTag, SF3DNode_V4_Count, NodeTag, GF_BIFS_V4); + case NDT_SF2DNode: + return ALL_GetNodeType(SF2DNode_V4_TypeToTag, SF2DNode_V4_Count, NodeTag, GF_BIFS_V4); + case NDT_SFTextureNode: + return ALL_GetNodeType(SFTextureNode_V4_TypeToTag, SFTextureNode_V4_Count, NodeTag, GF_BIFS_V4); + default: + return 0; + } +} + + + + +u32 NDT_V5_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType) +{ + if (!NodeType) return 0; + /* adjust according to the table version */ + /* v5: 0 reserved for extensions */ + NodeType -= 1; + switch (Context_NDT_Tag) { + case NDT_SFWorldNode: + if (NodeType >= SFWorldNode_V5_Count) return 0; + return SFWorldNode_V5_TypeToTag[NodeType]; + case NDT_SF3DNode: + if (NodeType >= SF3DNode_V5_Count) return 0; + return SF3DNode_V5_TypeToTag[NodeType]; + case NDT_SF2DNode: + if (NodeType >= SF2DNode_V5_Count) return 0; + return SF2DNode_V5_TypeToTag[NodeType]; + case NDT_SFAppearanceNode: + if (NodeType >= SFAppearanceNode_V5_Count) return 0; + return SFAppearanceNode_V5_TypeToTag[NodeType]; + case NDT_SFGeometryNode: + if (NodeType >= SFGeometryNode_V5_Count) return 0; + return SFGeometryNode_V5_TypeToTag[NodeType]; + case NDT_SFTextureNode: + if (NodeType >= SFTextureNode_V5_Count) return 0; + return SFTextureNode_V5_TypeToTag[NodeType]; + case NDT_SFDepthImageNode: + if (NodeType >= SFDepthImageNode_V5_Count) return 0; + return SFDepthImageNode_V5_TypeToTag[NodeType]; + case NDT_SFBlendListNode: + if (NodeType >= SFBlendListNode_V5_Count) return 0; + return SFBlendListNode_V5_TypeToTag[NodeType]; + case NDT_SFFrameListNode: + if (NodeType >= SFFrameListNode_V5_Count) return 0; + return SFFrameListNode_V5_TypeToTag[NodeType]; + case NDT_SFLightMapNode: + if (NodeType >= SFLightMapNode_V5_Count) return 0; + return SFLightMapNode_V5_TypeToTag[NodeType]; + case NDT_SFSurfaceMapNode: + if (NodeType >= SFSurfaceMapNode_V5_Count) return 0; + return SFSurfaceMapNode_V5_TypeToTag[NodeType]; + case NDT_SFViewMapNode: + if (NodeType >= SFViewMapNode_V5_Count) return 0; + return SFViewMapNode_V5_TypeToTag[NodeType]; + case NDT_SFParticleInitializerNode: + if (NodeType >= SFParticleInitializerNode_V5_Count) return 0; + return SFParticleInitializerNode_V5_TypeToTag[NodeType]; + case NDT_SFInfluenceNode: + if (NodeType >= SFInfluenceNode_V5_Count) return 0; + return SFInfluenceNode_V5_TypeToTag[NodeType]; + case NDT_SFDepthTextureNode: + if (NodeType >= SFDepthTextureNode_V5_Count) return 0; + return SFDepthTextureNode_V5_TypeToTag[NodeType]; + case NDT_SFSBBoneNode: + if (NodeType >= SFSBBoneNode_V5_Count) return 0; + return SFSBBoneNode_V5_TypeToTag[NodeType]; + case NDT_SFSBMuscleNode: + if (NodeType >= SFSBMuscleNode_V5_Count) return 0; + return SFSBMuscleNode_V5_TypeToTag[NodeType]; + case NDT_SFSBSegmentNode: + if (NodeType >= SFSBSegmentNode_V5_Count) return 0; + return SFSBSegmentNode_V5_TypeToTag[NodeType]; + case NDT_SFSBSiteNode: + if (NodeType >= SFSBSiteNode_V5_Count) return 0; + return SFSBSiteNode_V5_TypeToTag[NodeType]; + case NDT_SFBaseMeshNode: + if (NodeType >= SFBaseMeshNode_V5_Count) return 0; + return SFBaseMeshNode_V5_TypeToTag[NodeType]; + case NDT_SFSubdivSurfaceSectorNode: + if (NodeType >= SFSubdivSurfaceSectorNode_V5_Count) return 0; + return SFSubdivSurfaceSectorNode_V5_TypeToTag[NodeType]; + default: + return 0; + } +} + + +u32 NDT_V5_GetNumBits(u32 NDT_Tag) +{ + switch (NDT_Tag) { + case NDT_SFWorldNode: + return SFWorldNode_V5_NUMBITS; + case NDT_SF3DNode: + return SF3DNode_V5_NUMBITS; + case NDT_SF2DNode: + return SF2DNode_V5_NUMBITS; + case NDT_SFAppearanceNode: + return SFAppearanceNode_V5_NUMBITS; + case NDT_SFGeometryNode: + return SFGeometryNode_V5_NUMBITS; + case NDT_SFTextureNode: + return SFTextureNode_V5_NUMBITS; + case NDT_SFDepthImageNode: + return SFDepthImageNode_V5_NUMBITS; + case NDT_SFBlendListNode: + return SFBlendListNode_V5_NUMBITS; + case NDT_SFFrameListNode: + return SFFrameListNode_V5_NUMBITS; + case NDT_SFLightMapNode: + return SFLightMapNode_V5_NUMBITS; + case NDT_SFSurfaceMapNode: + return SFSurfaceMapNode_V5_NUMBITS; + case NDT_SFViewMapNode: + return SFViewMapNode_V5_NUMBITS; + case NDT_SFParticleInitializerNode: + return SFParticleInitializerNode_V5_NUMBITS; + case NDT_SFInfluenceNode: + return SFInfluenceNode_V5_NUMBITS; + case NDT_SFDepthTextureNode: + return SFDepthTextureNode_V5_NUMBITS; + case NDT_SFSBBoneNode: + return SFSBBoneNode_V5_NUMBITS; + case NDT_SFSBMuscleNode: + return SFSBMuscleNode_V5_NUMBITS; + case NDT_SFSBSegmentNode: + return SFSBSegmentNode_V5_NUMBITS; + case NDT_SFSBSiteNode: + return SFSBSiteNode_V5_NUMBITS; + case NDT_SFBaseMeshNode: + return SFBaseMeshNode_V5_NUMBITS; + case NDT_SFSubdivSurfaceSectorNode: + return SFSubdivSurfaceSectorNode_V5_NUMBITS; + default: + return 0; + } +} + +u32 NDT_V5_GetNodeType(u32 NDT_Tag, u32 NodeTag) +{ + if(!NDT_Tag || !NodeTag) return 0; + switch(NDT_Tag) { + case NDT_SFWorldNode: + return ALL_GetNodeType(SFWorldNode_V5_TypeToTag, SFWorldNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SF3DNode: + return ALL_GetNodeType(SF3DNode_V5_TypeToTag, SF3DNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SF2DNode: + return ALL_GetNodeType(SF2DNode_V5_TypeToTag, SF2DNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFAppearanceNode: + return ALL_GetNodeType(SFAppearanceNode_V5_TypeToTag, SFAppearanceNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFGeometryNode: + return ALL_GetNodeType(SFGeometryNode_V5_TypeToTag, SFGeometryNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFTextureNode: + return ALL_GetNodeType(SFTextureNode_V5_TypeToTag, SFTextureNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFDepthImageNode: + return ALL_GetNodeType(SFDepthImageNode_V5_TypeToTag, SFDepthImageNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFBlendListNode: + return ALL_GetNodeType(SFBlendListNode_V5_TypeToTag, SFBlendListNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFFrameListNode: + return ALL_GetNodeType(SFFrameListNode_V5_TypeToTag, SFFrameListNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFLightMapNode: + return ALL_GetNodeType(SFLightMapNode_V5_TypeToTag, SFLightMapNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFSurfaceMapNode: + return ALL_GetNodeType(SFSurfaceMapNode_V5_TypeToTag, SFSurfaceMapNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFViewMapNode: + return ALL_GetNodeType(SFViewMapNode_V5_TypeToTag, SFViewMapNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFParticleInitializerNode: + return ALL_GetNodeType(SFParticleInitializerNode_V5_TypeToTag, SFParticleInitializerNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFInfluenceNode: + return ALL_GetNodeType(SFInfluenceNode_V5_TypeToTag, SFInfluenceNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFDepthTextureNode: + return ALL_GetNodeType(SFDepthTextureNode_V5_TypeToTag, SFDepthTextureNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFSBBoneNode: + return ALL_GetNodeType(SFSBBoneNode_V5_TypeToTag, SFSBBoneNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFSBMuscleNode: + return ALL_GetNodeType(SFSBMuscleNode_V5_TypeToTag, SFSBMuscleNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFSBSegmentNode: + return ALL_GetNodeType(SFSBSegmentNode_V5_TypeToTag, SFSBSegmentNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFSBSiteNode: + return ALL_GetNodeType(SFSBSiteNode_V5_TypeToTag, SFSBSiteNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFBaseMeshNode: + return ALL_GetNodeType(SFBaseMeshNode_V5_TypeToTag, SFBaseMeshNode_V5_Count, NodeTag, GF_BIFS_V5); + case NDT_SFSubdivSurfaceSectorNode: + return ALL_GetNodeType(SFSubdivSurfaceSectorNode_V5_TypeToTag, SFSubdivSurfaceSectorNode_V5_Count, NodeTag, GF_BIFS_V5); + default: + return 0; + } +} + + + + +u32 NDT_V6_GetNodeTag(u32 Context_NDT_Tag, u32 NodeType) +{ + if (!NodeType) return 0; + /* adjust according to the table version */ + /* v6: 0 reserved for extensions */ + NodeType -= 1; + switch (Context_NDT_Tag) { + case NDT_SFWorldNode: + if (NodeType >= SFWorldNode_V6_Count) return 0; + return SFWorldNode_V6_TypeToTag[NodeType]; + case NDT_SF3DNode: + if (NodeType >= SF3DNode_V6_Count) return 0; + return SF3DNode_V6_TypeToTag[NodeType]; + case NDT_SF2DNode: + if (NodeType >= SF2DNode_V6_Count) return 0; + return SF2DNode_V6_TypeToTag[NodeType]; + case NDT_SFGeometryNode: + if (NodeType >= SFGeometryNode_V6_Count) return 0; + return SFGeometryNode_V6_TypeToTag[NodeType]; + case NDT_SFTextureNode: + if (NodeType >= SFTextureNode_V6_Count) return 0; + return SFTextureNode_V6_TypeToTag[NodeType]; + case NDT_SFFontStyleNode: + if (NodeType >= SFFontStyleNode_V6_Count) return 0; + return SFFontStyleNode_V6_TypeToTag[NodeType]; + case NDT_SFLinePropertiesNode: + if (NodeType >= SFLinePropertiesNode_V6_Count) return 0; + return SFLinePropertiesNode_V6_TypeToTag[NodeType]; + case NDT_SFTextureTransformNode: + if (NodeType >= SFTextureTransformNode_V6_Count) return 0; + return SFTextureTransformNode_V6_TypeToTag[NodeType]; + case NDT_SFViewportNode: + if (NodeType >= SFViewportNode_V6_Count) return 0; + return SFViewportNode_V6_TypeToTag[NodeType]; + default: + return 0; + } +} + + +u32 NDT_V6_GetNumBits(u32 NDT_Tag) +{ + switch (NDT_Tag) { + case NDT_SFWorldNode: + return SFWorldNode_V6_NUMBITS; + case NDT_SF3DNode: + return SF3DNode_V6_NUMBITS; + case NDT_SF2DNode: + return SF2DNode_V6_NUMBITS; + case NDT_SFGeometryNode: + return SFGeometryNode_V6_NUMBITS; + case NDT_SFTextureNode: + return SFTextureNode_V6_NUMBITS; + case NDT_SFFontStyleNode: + return SFFontStyleNode_V6_NUMBITS; + case NDT_SFLinePropertiesNode: + return SFLinePropertiesNode_V6_NUMBITS; + case NDT_SFTextureTransformNode: + return SFTextureTransformNode_V6_NUMBITS; + case NDT_SFViewportNode: + return SFViewportNode_V6_NUMBITS; + default: + return 0; + } +} + +u32 NDT_V6_GetNodeType(u32 NDT_Tag, u32 NodeTag) +{ + if(!NDT_Tag || !NodeTag) return 0; + switch(NDT_Tag) { + case NDT_SFWorldNode: + return ALL_GetNodeType(SFWorldNode_V6_TypeToTag, SFWorldNode_V6_Count, NodeTag, GF_BIFS_V6); + case NDT_SF3DNode: + return ALL_GetNodeType(SF3DNode_V6_TypeToTag, SF3DNode_V6_Count, NodeTag, GF_BIFS_V6); + case NDT_SF2DNode: + return ALL_GetNodeType(SF2DNode_V6_TypeToTag, SF2DNode_V6_Count, NodeTag, GF_BIFS_V6); + case NDT_SFGeometryNode: + return ALL_GetNodeType(SFGeometryNode_V6_TypeToTag, SFGeometryNode_V6_Count, NodeTag, GF_BIFS_V6); + case NDT_SFTextureNode: + return ALL_GetNodeType(SFTextureNode_V6_TypeToTag, SFTextureNode_V6_Count, NodeTag, GF_BIFS_V6); + case NDT_SFFontStyleNode: + return ALL_GetNodeType(SFFontStyleNode_V6_TypeToTag, SFFontStyleNode_V6_Count, NodeTag, GF_BIFS_V6); + case NDT_SFLinePropertiesNode: + return ALL_GetNodeType(SFLinePropertiesNode_V6_TypeToTag, SFLinePropertiesNode_V6_Count, NodeTag, GF_BIFS_V6); + case NDT_SFTextureTransformNode: + return ALL_GetNodeType(SFTextureTransformNode_V6_TypeToTag, SFTextureTransformNode_V6_Count, NodeTag, GF_BIFS_V6); + case NDT_SFViewportNode: + return ALL_GetNodeType(SFViewportNode_V6_TypeToTag, SFViewportNode_V6_Count, NodeTag, GF_BIFS_V6); + default: + return 0; + } +} + + + +u32 gf_bifs_ndt_get_node_type(u32 NDT_Tag, u32 NodeType, u32 Version) +{ + switch (Version) { + case GF_BIFS_V1: + return NDT_V1_GetNodeTag(NDT_Tag, NodeType); + case GF_BIFS_V2: + return NDT_V2_GetNodeTag(NDT_Tag, NodeType); + case GF_BIFS_V3: + return NDT_V3_GetNodeTag(NDT_Tag, NodeType); + case GF_BIFS_V4: + return NDT_V4_GetNodeTag(NDT_Tag, NodeType); + case GF_BIFS_V5: + return NDT_V5_GetNodeTag(NDT_Tag, NodeType); + case GF_BIFS_V6: + return NDT_V6_GetNodeTag(NDT_Tag, NodeType); + default: + return 0; + } +} + +u32 gf_bifs_get_ndt_bits(u32 NDT_Tag, u32 Version) +{ + switch (Version) { + case GF_BIFS_V1: + return NDT_V1_GetNumBits(NDT_Tag); + case GF_BIFS_V2: + return NDT_V2_GetNumBits(NDT_Tag); + case GF_BIFS_V3: + return NDT_V3_GetNumBits(NDT_Tag); + case GF_BIFS_V4: + return NDT_V4_GetNumBits(NDT_Tag); + case GF_BIFS_V5: + return NDT_V5_GetNumBits(NDT_Tag); + case GF_BIFS_V6: + return NDT_V6_GetNumBits(NDT_Tag); + default: + return 0; + } +} + +u32 gf_bifs_get_node_type(u32 NDT_Tag, u32 NodeTag, u32 Version) +{ + switch (Version) { + case GF_BIFS_V1: + return NDT_V1_GetNodeType(NDT_Tag, NodeTag); + case GF_BIFS_V2: + return NDT_V2_GetNodeType(NDT_Tag, NodeTag); + case GF_BIFS_V3: + return NDT_V3_GetNodeType(NDT_Tag, NodeTag); + case GF_BIFS_V4: + return NDT_V4_GetNodeType(NDT_Tag, NodeTag); + case GF_BIFS_V5: + return NDT_V5_GetNodeType(NDT_Tag, NodeTag); + case GF_BIFS_V6: + return NDT_V6_GetNodeType(NDT_Tag, NodeTag); + default: + return 0; + } +}u32 GetChildrenNDT(GF_Node *node) +{ + if (!node) return 0; + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_Anchor: + return NDT_SF3DNode; + case TAG_MPEG4_AudioBuffer: + return NDT_SFAudioNode; + case TAG_MPEG4_AudioDelay: + return NDT_SFAudioNode; + case TAG_MPEG4_AudioFX: + return NDT_SFAudioNode; + case TAG_MPEG4_AudioMix: + return NDT_SFAudioNode; + case TAG_MPEG4_AudioSource: + return NDT_SFAudioNode; + case TAG_MPEG4_AudioSwitch: + return NDT_SFAudioNode; + case TAG_MPEG4_Billboard: + return NDT_SF3DNode; + case TAG_MPEG4_Collision: + return NDT_SF3DNode; + case TAG_MPEG4_CompositeTexture2D: + return NDT_SF2DNode; + case TAG_MPEG4_CompositeTexture3D: + return NDT_SF3DNode; + case TAG_MPEG4_Form: + return NDT_SF2DNode; + case TAG_MPEG4_Group: + return NDT_SF3DNode; + case TAG_MPEG4_Layer2D: + return NDT_SF2DNode; + case TAG_MPEG4_Layer3D: + return NDT_SF3DNode; + case TAG_MPEG4_Layout: + return NDT_SF2DNode; + case TAG_MPEG4_OrderedGroup: + return NDT_SF3DNode; + case TAG_MPEG4_Transform: + return NDT_SF3DNode; + case TAG_MPEG4_Transform2D: + return NDT_SF2DNode; + case TAG_MPEG4_TemporalTransform: + return NDT_SF3DNode; + case TAG_MPEG4_TemporalGroup: + return NDT_SFTemporalNode; + case TAG_MPEG4_Clipper2D: + return NDT_SF2DNode; + case TAG_MPEG4_ColorTransform: + return NDT_SF3DNode; + case TAG_MPEG4_PathLayout: + return NDT_SF2DNode; + case TAG_MPEG4_TransformMatrix2D: + return NDT_SF2DNode; + default: + return 0; + } +} + diff --git a/src/bifs/com_dec.c b/src/bifs/com_dec.c new file mode 100644 index 0000000..d41d1fe --- /dev/null +++ b/src/bifs/com_dec.c @@ -0,0 +1,1162 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include "quant.h" + +GF_Err BD_DecMFFieldList(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); +GF_Err BD_DecMFFieldVec(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); + + + +void gf_bifs_dec_name(GF_BitStream *bs, char *name) +{ + u32 i = 0; + while (1) { + name[i] = gf_bs_read_int(bs, 8); + if (!name[i]) break; + i++; + } +} + +static GF_Err BD_DecProtoDelete(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + u32 ID, flag, count; + GF_Proto *proto; + + flag = gf_bs_read_int(bs, 1); + if (flag) { + flag = gf_bs_read_int(bs, 1); + while (flag) { + ID = gf_bs_read_int(bs, codec->info->config.ProtoIDBits); + proto = gf_sg_find_proto(codec->current_graph, ID, NULL); + if (proto) gf_sg_proto_del(proto); + flag = gf_bs_read_int(bs, 1); + } + } else { + flag = gf_bs_read_int(bs, 5); + count = gf_bs_read_int(bs, flag); + while (count) { + ID = gf_bs_read_int(bs, codec->info->config.ProtoIDBits); + proto = gf_sg_find_proto(codec->current_graph, ID, NULL); + if (proto) gf_sg_proto_del(proto); + count--; + } + } + return GF_OK; +} + + +static GF_Err BD_DecMultipleIndexReplace(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + u32 ID, ind, field_ind, NumBits, lenpos, lennum, count, pos; + GF_Node *node, *new_node; + GF_Err e; + GF_FieldInfo field, sffield; + + ID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + node = gf_sg_find_node(codec->current_graph, ID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN)-1); + ind = gf_bs_read_int(bs, NumBits); + e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + e = gf_node_get_field(node, field_ind, &field); + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + + lenpos = gf_bs_read_int(bs, 5); + lennum = gf_bs_read_int(bs, 5); + count = gf_bs_read_int(bs, lennum); + + + /*cf index value replace */ + if (field.fieldType == GF_SG_VRML_MFNODE) { + while (count) { + pos = gf_bs_read_int(bs, lenpos); + /*first decode*/ + new_node = gf_bifs_dec_node(codec, bs, field.NDTtype); + if (!new_node) return codec->LastError; + e = gf_node_register(new_node, node); + if (e) return e; + /*then replace*/ + e = gf_node_replace_child(node, (GF_ChildNodeItem**) field.far_ptr, pos, new_node); + count--; + } + if (!e) gf_bifs_check_field_change(node, &field); + return e; + } + /*Not a node list*/ + memcpy(&sffield, &field, sizeof(GF_FieldInfo)); + sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + + while (count) { + pos = gf_bs_read_int(bs, lenpos); + + if (pos && pos >= ((GenMFField *)field.far_ptr)->count) { + pos = ((GenMFField *)field.far_ptr)->count - 1; + } + + e = gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, & sffield.far_ptr, pos); + if (e) return e; + e = gf_bifs_dec_sf_field(codec, bs, node, &sffield); + if (e) break; + count--; + } + if (!e) gf_bifs_check_field_change(node, &field); + return e; +} + +static GF_Err BD_DecMultipleReplace(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + GF_Err e; + u32 NodeID, flag; + GF_Node *node; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + + flag = gf_bs_read_int(bs, 1); + if (flag) { + e = gf_bifs_dec_node_mask(codec, bs, node, 0); + } else { + e = gf_bifs_dec_node_list(codec, bs, node, 0); + } + return e; +} + + +static GF_Err BD_DecGlobalQuantizer(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + GF_Node *node; + node = gf_bifs_dec_node(codec, bs, NDT_SFWorldNode); + + /*reset global QP*/ + if (codec->scenegraph->global_qp) { + gf_node_unregister(codec->scenegraph->global_qp, NULL); + codec->scenegraph->global_qp = NULL; + } + codec->ActiveQP = NULL; + + if (!node || (gf_node_get_tag(node) != TAG_MPEG4_QuantizationParameter)) { + if (node) gf_node_unregister(node, NULL); + return codec->LastError; + } + + /*register global QP*/ + codec->scenegraph->global_qp = node; + gf_node_register(node, NULL); + codec->ActiveQP = (M_QuantizationParameter *) node; + codec->ActiveQP->isLocal = 0; + return GF_OK; +} + +static GF_Err BD_DecNodeDeleteEx(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + u32 NodeID; + GF_Node *node; + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + return gf_node_replace(node, NULL, 1); +} + +static GF_Err BD_DecOperandReplace(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + s32 pos; + GF_FieldInfo field; + GF_FieldInfo src_field; + GF_Err e; + u32 NodeID, NumBits, ind, field_ind, type, src_type, dst_type; + GF_Node *node, *src; + void *src_ptr, *dst_ptr; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN)-1); + ind = gf_bs_read_int(bs, NumBits); + e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + e = gf_node_get_field(node, field_ind, &field); + if (e) return e; + + dst_type = field.fieldType; + dst_ptr = field.far_ptr; + pos = -2; + if (! gf_sg_vrml_is_sf_field(field.fieldType)) { + type = gf_bs_read_int(bs, 2); + switch (type) { + /*no index*/ + case 0: + break; + /*specified*/ + case 1: + pos = gf_bs_read_int(bs, 8); + break; + /*first*/ + case 2: + pos = 0; + break; + /*last*/ + case 3: + /*-1 means append*/ + pos = ((GenMFField *)field.far_ptr)->count; + if (!pos) + return GF_NON_COMPLIANT_BITSTREAM; + pos -= 1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + if (pos>-2) { + dst_type = gf_sg_vrml_get_sf_type(field.fieldType); + e = gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &dst_ptr, pos); + if (e) return e; + } + } + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + src = gf_sg_find_node(codec->current_graph, NodeID); + if (!src) return GF_NON_COMPLIANT_BITSTREAM; + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(src, GF_SG_FIELD_CODING_IN)-1); + ind = gf_bs_read_int(bs, NumBits); + e = gf_bifs_get_field_index(src, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + e = gf_node_get_field(src, field_ind, &src_field); + if (e) return e; + + src_type = src_field.fieldType; + src_ptr = src_field.far_ptr; + pos = -2; + if (! gf_sg_vrml_is_sf_field(src_field.fieldType)) { + type = gf_bs_read_int(bs, 2); + switch (type) { + /*no index*/ + case 0: + break; + /*specified*/ + case 1: + pos = gf_bs_read_int(bs, 8); + break; + /*first*/ + case 2: + pos = 0; + break; + /*last*/ + case 3: + /*-1 means append*/ + pos = ((GenMFField *)src_field.far_ptr)->count; + if (!pos) + return GF_NON_COMPLIANT_BITSTREAM; + pos -= 1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + if (pos>-2) { + src_type = gf_sg_vrml_get_sf_type(src_field.fieldType); + e = gf_sg_vrml_mf_get_item(src_field.far_ptr, src_field.fieldType, &src_ptr, pos); + if (e) return e; + } + } + if (src_type!=dst_type) return GF_NON_COMPLIANT_BITSTREAM; + gf_sg_vrml_field_copy(dst_ptr, src_ptr, src_type); + return GF_OK; +} + +static GF_Err BD_DecExtendedUpdate(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + u32 type; + + type = gf_bs_read_int(bs, 8); + switch (type) { + case 0: + return gf_bifs_dec_proto_list(codec, bs, NULL); + case 1: + return BD_DecProtoDelete(codec, bs); + case 2: + return gf_sg_delete_all_protos(codec->current_graph); + case 3: + return BD_DecMultipleIndexReplace(codec, bs); + case 4: + return BD_DecMultipleReplace(codec, bs); + case 5: + return BD_DecGlobalQuantizer(codec, bs); + case 6: + return BD_DecNodeDeleteEx(codec, bs); + case 7: + return BD_DecOperandReplace(codec, bs); + default: + return GF_BIFS_UNKNOWN_VERSION; + } +} + +/*inserts a node in a container (node.children)*/ +static GF_Err BD_DecNodeInsert(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + u32 NodeID, NDT; + s32 type, pos; + GF_Node *node, *def; + GF_Err e; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + def = gf_sg_find_node(codec->current_graph, NodeID); + if (!def) return GF_NON_COMPLIANT_BITSTREAM; + + NDT = gf_bifs_get_child_table(def); + if (!NDT) return GF_NON_COMPLIANT_BITSTREAM; + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + pos = gf_bs_read_int(bs, 8); + break; + case 2: + pos = 0; + break; + case 3: + /*-1 means append*/ + pos = -1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + + node = gf_bifs_dec_node(codec, bs, NDT); + if (!node) return codec->LastError; + + e = gf_node_register(node, def); + if (e) return e; + e = gf_node_insert_child(def, node, pos); + if (!e) { + GF_FieldInfo field; + /*get it by name in case no add/removeChildren*/ + e = gf_node_get_field_by_name(def, "children", &field); + if (e) return e; + gf_bifs_check_field_change(def, &field); + } + return e; +} + +/*NB This can insert a node as well (but usually not in the .children field)*/ +static GF_Err BD_DecIndexInsert(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + GF_Err e; + u32 NodeID; + u32 NumBits, ind, field_ind; + u8 type; + s32 pos; + GF_Node *def, *node; + GF_FieldInfo field, sffield; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + def = gf_sg_find_node(codec->current_graph, NodeID); + if (!def) return GF_NON_COMPLIANT_BITSTREAM; + /*index insertion uses IN mode for field index*/ + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(def, GF_SG_FIELD_CODING_IN)-1); + ind = gf_bs_read_int(bs, NumBits); + + e = gf_bifs_get_field_index(def, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + pos = gf_bs_read_int(bs, 16); + break; + case 2: + pos = 0; + break; + case 3: + pos = -1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + + e = gf_node_get_field(def, field_ind, &field); + if (e) return e; + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + + memcpy(&sffield, &field, sizeof(GF_FieldInfo)); + sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + + /*rescale the MFField and parse the SFField*/ + if (field.fieldType==GF_SG_VRML_MFNODE) { + node = gf_bifs_dec_node(codec, bs, field.NDTtype); + if (!node) return codec->LastError; + + e = gf_node_register(node, def); + if (e) return e; + /*this is generic MFNode container*/ + if (pos== -1) { + e = gf_node_list_add_child( (GF_ChildNodeItem **) field.far_ptr, node); + } else { + e = gf_node_list_insert_child((GF_ChildNodeItem **) field.far_ptr, node, pos); + } + + if (!e) gf_bifs_check_field_change(def, &field); + } else { + if (pos == -1) { + e = gf_sg_vrml_mf_append(field.far_ptr, field.fieldType, & sffield.far_ptr); + } else { + /*insert is 0-based*/ + e = gf_sg_vrml_mf_insert(field.far_ptr, field.fieldType, & sffield.far_ptr, pos); + } + if (e) return e; + e = gf_bifs_dec_sf_field(codec, bs, def, &sffield); + if (!e) gf_bifs_check_field_change(def, &field); + } + return e; +} + + +static GF_Err BD_DecInsert(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + u8 type; + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + return BD_DecNodeInsert(codec, bs); + /*Extended BIFS updates*/ + case 1: + return BD_DecExtendedUpdate(codec, bs); + case 2: + return BD_DecIndexInsert(codec, bs); + case 3: + return gf_bifs_dec_route(codec, bs, 1); + default: + return GF_NON_COMPLIANT_BITSTREAM; + } +} + + +static GF_Err BD_DecIndexDelete(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + u32 NodeID, NumBits, SF_type, ind, field_ind; + s32 pos; + u8 type; + GF_Node *node; + GF_Err e; + GF_FieldInfo field; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN) - 1); + ind = gf_bs_read_int(bs, NumBits); + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + pos = (u32) gf_bs_read_int(bs, 16); + break; + case 2: + pos = 0; + break; + case 3: + pos = -1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + e = gf_node_get_field(node, field_ind, &field); + if (e) return e; + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + + SF_type = gf_sg_vrml_get_sf_type(field.fieldType); + + /*special handling in case of a node*/ + if (SF_type == GF_SG_VRML_SFNODE) { + e = gf_node_replace_child(node, (GF_ChildNodeItem**) field.far_ptr, pos, NULL); + } else { + e = gf_sg_vrml_mf_remove(field.far_ptr, field.fieldType, pos); + } + /*deletion -> node has changed*/ + if (!e) gf_bifs_check_field_change(node, &field); + + return e; +} + +static GF_Err BD_DecDelete(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + u8 type; + u32 ID; + GF_Node *n; + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + ID = 1+gf_bs_read_int(bs, codec->info->config.NodeIDBits); + n = gf_sg_find_node(codec->current_graph, ID); +#ifdef MPEG4_STRICT + if (!n) return GF_NON_COMPLIANT_BITSTREAM; +#else + if (!n) return GF_OK; +#endif + /*this is a delete of a DEF node, remove ALL INSTANCES*/ + return gf_node_replace(n, NULL, 0); + case 2: + return BD_DecIndexDelete(codec, bs); + case 3: + ID = 1+gf_bs_read_int(bs, codec->info->config.RouteIDBits); + /*don't complain if route can't be deleted (not present)*/ + gf_sg_route_del_by_id(codec->current_graph, ID); + return GF_OK; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + return GF_OK; +} + + +static GF_Err BD_DecNodeReplace(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + u32 NodeID; + GF_Node *node, *new_node; + GF_Err e; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + /*this is delete / new on a DEF node: replace ALL instances*/ + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + + /*and just parse a new GF_Node - it is encoded in SFWorldNode table */ + new_node = gf_bifs_dec_node(codec, bs, NDT_SFWorldNode); + if (!new_node && codec->LastError) return codec->LastError; + + e = gf_node_replace(node, new_node, 0); + return e; +} + +static GF_Err BD_DecFieldReplace(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + GF_Err e; + u32 NodeID, ind, field_ind, NumBits; + GF_Node *node, *prev_node; + GF_ChildNodeItem *prev_child; + GF_FieldInfo field; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN)-1); + ind = gf_bs_read_int(bs, NumBits); + e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + + e = gf_node_get_field(node, field_ind, &field); + + prev_node = NULL; + prev_child = NULL; + /*store prev SF node*/ + if (field.fieldType == GF_SG_VRML_SFNODE) { + prev_node = *((GF_Node **) field.far_ptr); + } + /*store prev MFNode content*/ + else if (field.fieldType == GF_SG_VRML_MFNODE) { + prev_child = * ((GF_ChildNodeItem **) field.far_ptr); + * ((GF_ChildNodeItem **) field.far_ptr) = NULL; + } + /*regular field*/ + else if (!gf_sg_vrml_is_sf_field(field.fieldType)) { + gf_sg_vrml_mf_reset(field.far_ptr, field.fieldType); + } + + /*parse the field*/ + codec->is_com_dec = 1; + e = gf_bifs_dec_field(codec, bs, node, &field); + codec->is_com_dec = 0; + /*remove prev nodes*/ + if (field.fieldType == GF_SG_VRML_SFNODE) { + if (prev_node) e = gf_node_unregister(prev_node, node); + } else if (field.fieldType == GF_SG_VRML_MFNODE) { + gf_node_unregister_children(node, prev_child); + } + if (!e) gf_bifs_check_field_change(node, &field); + return e; +} + +static GF_Err BD_DecIndexValueReplace(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + GF_Node *new_node; + u32 NodeID, ind, field_ind, NumBits, pos; + u8 type; + GF_Node *node; + GF_Err e; + GF_FieldInfo field, sffield; + + /*get the node*/ + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN)-1); + ind = gf_bs_read_int(bs, NumBits); + e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + + e = gf_node_get_field(node, field_ind, &field); + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + pos = gf_bs_read_int(bs, 16); + break; + case 2: + pos = 0; + break; + case 3: + pos = ((GenMFField *) field.far_ptr)->count - 1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + + /*if MFNode remove the child and parse new node*/ + if (field.fieldType == GF_SG_VRML_MFNODE) { + /*get the new node*/ + new_node = gf_bifs_dec_node(codec, bs, field.NDTtype); + if (codec->LastError) { + e = codec->LastError; + goto exit; + } + if (new_node) { + e = gf_node_register(new_node, node); + if (e) return e; + } + /*replace prev node*/ + e = gf_node_replace_child(node, (GF_ChildNodeItem**) field.far_ptr, pos, new_node); + if (!e) gf_bifs_check_field_change(node, &field); + } + /*erase the field item*/ + else { + memcpy(&sffield, &field, sizeof(GF_FieldInfo)); + sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + + /*make sure this is consistent*/ + if (pos && pos >= ((GenMFField *)field.far_ptr)->count) { + pos = ((GenMFField *)field.far_ptr)->count - 1; + } + + e = gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, & sffield.far_ptr, pos); + if (e) return e; + e = gf_bifs_dec_sf_field(codec, bs, node, &sffield); + if (!e) gf_bifs_check_field_change(node, &field); + } + +exit: + return e; +} + +static GF_Err BD_DecRouteReplace(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + GF_Err e; + u32 RouteID, numBits, ind, node_id, fromID, toID; + char name[1000], *ptr; + GF_Route *r; + GF_Node *OutNode, *InNode; + + RouteID = 1+gf_bs_read_int(bs, codec->info->config.RouteIDBits); + + r = gf_sg_route_find(codec->current_graph, RouteID); +#ifdef MPEG4_STRICT + if (!r) return GF_NON_COMPLIANT_BITSTREAM; + ptr = gf_sg_route_get_name(r); + gf_sg_route_del(r); +#else + ptr = NULL; + if (r) { + ptr = gf_sg_route_get_name(r); +// gf_sg_route_del(r); + } +#endif + + if (ptr) strcpy(name, ptr); + + /*origin*/ + node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + OutNode = gf_sg_find_node(codec->current_graph, node_id); + if (!OutNode) return GF_NON_COMPLIANT_BITSTREAM; + numBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(OutNode, GF_SG_FIELD_CODING_OUT) - 1); + ind = gf_bs_read_int(bs, numBits); + e = gf_bifs_get_field_index(OutNode, ind, GF_SG_FIELD_CODING_OUT, &fromID); + if (e) return e; + + /*target*/ + node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + InNode = gf_sg_find_node(codec->current_graph, node_id); + if (!InNode) return GF_NON_COMPLIANT_BITSTREAM; + numBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(InNode, GF_SG_FIELD_CODING_IN) - 1); + ind = gf_bs_read_int(bs, numBits); + e = gf_bifs_get_field_index(InNode, ind, GF_SG_FIELD_CODING_IN, &toID); + if (e) return e; + + if (r) { + if (r->FromNode->sgprivate->interact) + gf_list_del_item(r->FromNode->sgprivate->interact->routes, r); + + r->is_setup = 0; + r->lastActivateTime = 0; + + r->FromNode = OutNode; + r->FromField.fieldIndex = fromID; + r->ToNode = InNode; + r->ToField.fieldIndex = toID; + + if (!r->FromNode->sgprivate->interact) GF_SAFEALLOC(r->FromNode->sgprivate->interact, struct _node_interactive_ext); + if (!r->FromNode->sgprivate->interact->routes) r->FromNode->sgprivate->interact->routes = gf_list_new(); + gf_list_add(r->FromNode->sgprivate->interact->routes, r); + } else { + r = gf_sg_route_new(codec->current_graph, OutNode, fromID, InNode, toID); + if (!r) return GF_OUT_OF_MEM; + gf_sg_route_set_id(r, RouteID); + if (ptr) e = gf_sg_route_set_name(r, name); + } + return e; +} + + +static GF_Err BD_DecReplace(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + u8 type; + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + return BD_DecNodeReplace(codec, bs); + case 1: + return BD_DecFieldReplace(codec, bs); + case 2: + return BD_DecIndexValueReplace(codec, bs); + case 3: + return BD_DecRouteReplace(codec, bs); + } + return GF_OK; +} + + +/*if parent is non-NULL, we are in a proto code parsing, otherwise this is a top-level proto*/ +GF_Err gf_bifs_dec_proto_list(GF_BifsDecoder * codec, GF_BitStream *bs, GF_List *proto_list) +{ + u8 flag, field_type, event_type, useQuant, useAnim, f; + u32 i, NbRoutes, ID, numProtos, numFields, count, qpsftype, QP_Type, NumBits, Anim_Type; + GF_Node *node; + char name[1000]; + GF_ProtoFieldInterface *proto_field; + GF_Proto *proto, *ParentProto; + GF_Err e; + u32 hasMinMax; + void *qp_min_value, *qp_max_value; + GF_SceneGraph *rootSG; + GF_FieldInfo field; + + NumBits = qpsftype = 0; + //store proto at codec level + rootSG = codec->current_graph; + ParentProto = codec->pCurrentProto; + e = GF_OK; + + numProtos = 0; + proto = NULL; + flag = gf_bs_read_int(bs, 1); + while (flag) { + + if (!codec->info->config.ProtoIDBits) return GF_NON_COMPLIANT_BITSTREAM; + + /*1- proto interface declaration*/ + ID = gf_bs_read_int(bs, codec->info->config.ProtoIDBits); + + if (codec->UseName) { + gf_bifs_dec_name(bs, name); + } else { + sprintf(name, "Proto%d", numProtos); + } + /*create a proto in the current graph*/ + proto = gf_sg_proto_new(codec->current_graph, ID, name, proto_list ? 1 : 0); + if (proto_list) gf_list_add(proto_list, proto); + + /*during parsing, this proto is the current active one - all nodes/proto defined/declared + below it will belong to its namespace*/ + codec->current_graph = gf_sg_proto_get_graph(proto); + codec->pCurrentProto = proto; + + numFields = 0; + flag = gf_bs_read_int(bs, 1); + while (flag) { + event_type = gf_bs_read_int(bs, 2); + field_type = gf_bs_read_int(bs, 6); + + if (codec->UseName) { + gf_bifs_dec_name(bs, name); + } else { + sprintf(name, "_field%d", numFields); + } + + /*create field interface*/ + proto_field = gf_sg_proto_field_new(proto, field_type, event_type, name); + + /*get field info */ + gf_sg_proto_field_get_field(proto_field, &field); + + switch (event_type) { + case GF_SG_EVENT_EXPOSED_FIELD: + case GF_SG_EVENT_FIELD: + /*parse default value except nodes ...*/ + if (gf_sg_vrml_is_sf_field(field_type)) { + e = gf_bifs_dec_sf_field(codec, bs, NULL, &field); + } else { + f = 0; + if (codec->info->config.UsePredictiveMFField) { + f = gf_bs_read_int(bs, 1); + /*predictive encoding of proto field is not possible since QP info is not present yet*/ + assert(!f); + } + /*reserved*/ + f = gf_bs_read_int(bs, 1); + if (!f) { + if (gf_bs_read_int(bs, 1)) { + e = BD_DecMFFieldList(codec, bs, NULL, &field); + } else { + e = BD_DecMFFieldVec(codec, bs, NULL, &field); + } + } + } + if (e) goto exit; + + break; + } + + flag = gf_bs_read_int(bs, 1); + numFields++; + } + + /*2- parse proto code*/ + flag = gf_bs_read_int(bs, 1); + + /*externProto*/ + if (flag) { + memset(&field, 0, sizeof(GF_FieldInfo)); + field.far_ptr = gf_sg_proto_get_extern_url(proto); + field.fieldType = GF_SG_VRML_MFURL; + field.name = "ExternProto"; + + if (codec->info->config.UsePredictiveMFField) { + flag = gf_bs_read_int(bs, 1); + assert(!flag); + } + /*reserved*/ + flag = gf_bs_read_int(bs, 1); + + /*list or vector*/ + flag = gf_bs_read_int(bs, 1); + if (flag) { + e = BD_DecMFFieldList(codec, bs, NULL, &field); + } else { + e = BD_DecMFFieldVec(codec, bs, NULL, &field); + } + if (e) goto exit; + } + /*get proto code*/ + else { + /*parse sub-proto list - subprotos are ALWAYS registered with parent proto graph*/ + e = gf_bifs_dec_proto_list(codec, bs, NULL); + if (e) goto exit; + + flag = 1; + + while (flag) { + /*parse all nodes in SFWorldNode table*/ + node = gf_bifs_dec_node(codec, bs, NDT_SFWorldNode); + if (!node) { + if (codec->LastError) { + e = codec->LastError; + goto exit; + } else { + flag = gf_bs_read_int(bs, 1); + continue; + } + } + e = gf_node_register(node, NULL); + if (e) goto exit; + + gf_sg_proto_add_node_code(proto, node); + flag = gf_bs_read_int(bs, 1); + } + + /*routes*/ + flag = gf_bs_read_int(bs, 1); + if (flag) { + flag = gf_bs_read_int(bs, 1); + if (flag) { + /*list route*/ + while (flag) { + e = gf_bifs_dec_route(codec, bs, 0); + if (e) goto exit; + flag = gf_bs_read_int(bs, 1); + } + } else { + /*vector*/ + i = gf_bs_read_int(bs, 5); + NbRoutes = gf_bs_read_int(bs, i); + for (i=0; icurrent_graph = rootSG; + + /*3- parse anim and Quantization stuff*/ + useQuant = gf_bs_read_int(bs, 1); + useAnim = gf_bs_read_int(bs, 1); + + count = gf_sg_proto_get_field_count(proto); + for (i=0; icurrent_graph = rootSG; + } + /*restore original parent proto at codec level*/ + codec->pCurrentProto = ParentProto; + return e; +} + + + +GF_Err gf_bifs_dec_route(GF_BifsDecoder * codec, GF_BitStream *bs, Bool is_insert) +{ + GF_Err e; + u8 flag; + GF_Route *r; + GF_Node *InNode, *OutNode; + u32 RouteID, outField, inField, numBits, ind, node_id; + char name[1000]; + + RouteID = 0; + + flag = gf_bs_read_int(bs, 1); + /*def'ed route*/ + if (flag) { + RouteID = 1 + gf_bs_read_int(bs, codec->info->config.RouteIDBits); + if (codec->UseName) gf_bifs_dec_name(bs, name); + } + /*origin*/ + node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + OutNode = gf_sg_find_node(codec->current_graph, node_id); + if (!OutNode) return GF_SG_UNKNOWN_NODE; + + numBits = gf_node_get_num_fields_in_mode(OutNode, GF_SG_FIELD_CODING_OUT) - 1; + numBits = gf_get_bit_size(numBits); + ind = gf_bs_read_int(bs, numBits); + e = gf_bifs_get_field_index(OutNode, ind, GF_SG_FIELD_CODING_OUT, &outField); + + /*target*/ + node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + InNode = gf_sg_find_node(codec->current_graph, node_id); + if (!InNode) return GF_SG_UNKNOWN_NODE; + + numBits = gf_node_get_num_fields_in_mode(InNode, GF_SG_FIELD_CODING_IN) - 1; + numBits = gf_get_bit_size(numBits); + ind = gf_bs_read_int(bs, numBits); + e = gf_bifs_get_field_index(InNode, ind, GF_SG_FIELD_CODING_IN, &inField); + if (e) return e; + + r = gf_sg_route_new(codec->current_graph, OutNode, outField, InNode, inField); + if (!r) return GF_OUT_OF_MEM; + if (RouteID) { + e = gf_sg_route_set_id(r, RouteID); + if (!e && codec->UseName) e = gf_sg_route_set_name(r, name); + } + return e; +} + + +GF_Err BD_DecSceneReplace(GF_BifsDecoder * codec, GF_BitStream *bs, GF_List *proto_list) +{ + u8 flag; + u32 i, nbR; + GF_Err e; + GF_Node *root; + + /*Reset the existing scene / scene graph, protos and route lists*/ + if (!proto_list) gf_sg_reset(codec->current_graph); + + /*reserved*/ + i = gf_bs_read_int(bs, 6); + + codec->UseName = gf_bs_read_int(bs, 1); + /*parse PROTOS*/ + e = gf_bifs_dec_proto_list(codec, bs, proto_list); + if (e) goto exit; + + assert(codec->pCurrentProto==NULL); + /*Parse the top node - always of type SFTopNode*/ + root = gf_bifs_dec_node(codec, bs, NDT_SFTopNode); + if (!root && codec->LastError) { + e = codec->LastError; + goto exit; + } + + if (root) { + e = gf_node_register(root, NULL); + if (e) goto exit; + } + gf_sg_set_root_node(codec->current_graph, root); + + /*Parse the routes*/ + flag = gf_bs_read_int(bs, 1); + + + if (flag) { + flag = gf_bs_read_int(bs, 1); + if (flag) { + /*list*/ + while (flag) { + e = gf_bifs_dec_route(codec, bs, 0); + if (e) goto exit; + flag = gf_bs_read_int(bs, 1); + } + } else { + /*vector*/ + i = gf_bs_read_int(bs, 5); + nbR = gf_bs_read_int(bs, i); + for (i=0; iLastError = GF_OK; + + codec->ActiveQP = (M_QuantizationParameter*)codec->scenegraph->global_qp; + + while (1) { + u8 type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + e = BD_DecInsert(codec, bs); + break; + case 1: + e = BD_DecDelete(codec, bs); + break; + case 2: + e = BD_DecReplace(codec, bs); + break; + case 3: + e = BD_DecSceneReplace(codec, bs, NULL); + break; + } + if (e) return e; + if (! gf_bs_read_int(bs, 1)) break; + } + + while (gf_list_count(codec->QPs)) { + gf_bifs_dec_qp_remove(codec, 1); + } + gf_bifs_flush_command_list(codec); + + return GF_OK; +} + diff --git a/src/bifs/com_enc.c b/src/bifs/com_enc.c new file mode 100644 index 0000000..f7a8748 --- /dev/null +++ b/src/bifs/com_enc.c @@ -0,0 +1,885 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include "quant.h" + +GF_Err BE_EncProtoList(GF_BifsEncoder *codec, GF_List *protoList, GF_BitStream *bs); + + +void gf_bifs_enc_name(GF_BifsEncoder *codec, GF_BitStream *bs, char *name) +{ + u32 i = 0; + while (name[i]) { + gf_bs_write_int(bs, name[i], 8); + i++; + } + gf_bs_write_int(bs, 0, 8); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[BIFS] DEF\t\t%d\t\t%s\n", 8*i, name)); +} + +static GF_Err BE_MultipleIndexedReplace(GF_BifsEncoder * codec, GF_Command *com, GF_BitStream *bs) +{ + u32 i,nbBits, count, maxPos, nbBitsPos; + GF_FieldInfo field; + GF_Err e; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *)gf_list_get(com->command_fields, 0); + + + gf_bs_write_int(bs, gf_node_get_id(com->node)-1, codec->info->config.NodeIDBits); + nbBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(com->node, GF_SG_FIELD_CODING_IN)-1); + gf_bifs_field_index_by_mode(com->node, inf->fieldIndex, GF_SG_FIELD_CODING_IN, &i); + GF_BIFS_WRITE_INT(codec, bs, i, nbBits, "field", NULL); + + gf_node_get_field(com->node, inf->fieldIndex, &field); + field.fieldType = inf->fieldType; + + count = gf_list_count(com->command_fields); + maxPos = 0; + for (i=0; icommand_fields, i); + if (maxPos < (u32) inf->pos) maxPos = inf->pos; + } + nbBitsPos = gf_get_bit_size(maxPos); + GF_BIFS_WRITE_INT(codec, bs, nbBitsPos, 5, "nbBitsPos", NULL); + + nbBits = gf_get_bit_size(count); + GF_BIFS_WRITE_INT(codec, bs, nbBits, 5, "nbBits", NULL); + GF_BIFS_WRITE_INT(codec, bs, count, nbBits, "count", NULL); + + for (i=0; icommand_fields, i); + GF_BIFS_WRITE_INT(codec, bs, inf->pos, nbBitsPos, "idx", NULL); + field.far_ptr = inf->field_ptr; + e = gf_bifs_enc_field(codec, bs, com->node, &field); + if (e) return e; + } + return GF_OK; +} + +static GF_Err BE_MultipleReplace(GF_BifsEncoder * codec, GF_Command *com, GF_BitStream *bs) +{ + u32 i, j, nbBits, count, numFields, allField; + Bool use_list; + GF_FieldInfo field; + GF_Err e; + + gf_bs_write_int(bs, gf_node_get_id(com->node)-1, codec->info->config.NodeIDBits); + + count = gf_list_count(com->command_fields); + use_list = 1; + numFields = gf_node_get_num_fields_in_mode(com->node, GF_SG_FIELD_CODING_DEF); + nbBits = gf_get_bit_size(numFields - 1); + if (count < 1+count*(1+nbBits)) use_list = 0; + GF_BIFS_WRITE_INT(codec, bs, use_list ? 0 : 1, 1, "isMask", NULL); + + for (i=0; inode, i, GF_SG_FIELD_CODING_DEF, &allField); + for (j=0; jcommand_fields, j); + if (inf->fieldIndex==allField) break; + inf = NULL; + } + if (!inf) { + if (!use_list) GF_BIFS_WRITE_INT(codec, bs, 0, 1, "Mask", NULL); + continue; + } + /*common case*/ + gf_node_get_field(com->node, inf->fieldIndex, &field); + if (use_list) { + /*not end flag*/ + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "end", NULL); + } else { + /*mask flag*/ + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "Mask", NULL); + } + if (use_list) GF_BIFS_WRITE_INT(codec, bs, i, nbBits, "field", (char*)field.name); + field.far_ptr = inf->field_ptr; + e = gf_bifs_enc_field(codec, bs, com->node, &field); + if (e) return e; + } + /*end flag*/ + if (use_list) GF_BIFS_WRITE_INT(codec, bs, 1, 1, "end", NULL); + return GF_OK; +} + +static GF_Err BE_GlobalQuantizer(GF_BifsEncoder * codec, GF_Command *com, GF_BitStream *bs) +{ + GF_Err e; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *)gf_list_get(com->command_fields, 0); + if (inf->new_node) ((M_QuantizationParameter *)inf->new_node)->isLocal = 0; + e = gf_bifs_enc_node(codec, inf->new_node, NDT_SFWorldNode, bs); + if (e) return e; + + /*reset global QP*/ + if (codec->scene_graph->global_qp) { + gf_node_unregister(codec->scene_graph->global_qp, NULL); + codec->scene_graph->global_qp = NULL; + } + codec->ActiveQP = NULL; + + /*no QP*/ + if (!inf->new_node) return GF_OK; + + /*register global QP*/ + codec->scene_graph->global_qp = inf->new_node; + gf_node_register(inf->new_node, NULL); + codec->ActiveQP = (M_QuantizationParameter *) inf->new_node; + codec->ActiveQP->isLocal = 0; + return GF_OK; +} + +static GF_Err BE_EncProtoDelete(GF_BifsEncoder * codec, GF_Command *com, GF_BitStream *bs) +{ + u32 nbBits, i; + Bool use_list = 0; + nbBits = gf_get_bit_size(com->del_proto_list_size); + if (nbBits+5>com->del_proto_list_size) use_list = 1; + GF_BIFS_WRITE_INT(codec, bs, use_list, 1, "isList", NULL); + if (!use_list) { + GF_BIFS_WRITE_INT(codec, bs, nbBits, 5, "len", NULL); + GF_BIFS_WRITE_INT(codec, bs, com->del_proto_list_size, nbBits, "len", NULL); + } + for (i=0; idel_proto_list_size; i++) { + if (use_list) GF_BIFS_WRITE_INT(codec, bs, 1, 1, "moreProto", NULL); + GF_BIFS_WRITE_INT(codec, bs, com->del_proto_list[i], codec->info->config.ProtoIDBits, "protoID", NULL); + } + if (use_list) GF_BIFS_WRITE_INT(codec, bs, 0, 1, "moreProto", NULL); + return GF_OK; +} + +static GF_Err BE_ExtendedUpdate(GF_BifsEncoder * codec, GF_Command *com, GF_BitStream *bs) +{ + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "Insert", NULL); + GF_BIFS_WRITE_INT(codec, bs, 1, 2, "ExtendedUpdate", NULL); + switch (com->tag) { + case GF_SG_PROTO_INSERT: + GF_BIFS_WRITE_INT(codec, bs, 0, 8, "MultipleReplace", NULL); + return BE_EncProtoList(codec, com->new_proto_list, bs); + case GF_SG_PROTO_DELETE: + GF_BIFS_WRITE_INT(codec, bs, 1, 8, "ProtoDelete", NULL); + return BE_EncProtoDelete(codec, com, bs); + case GF_SG_PROTO_DELETE_ALL: + GF_BIFS_WRITE_INT(codec, bs, 2, 8, "DeleteAllProtos", NULL); + return GF_OK; + case GF_SG_MULTIPLE_INDEXED_REPLACE: + GF_BIFS_WRITE_INT(codec, bs, 3, 8, "MultipleReplace", NULL); + return BE_MultipleIndexedReplace(codec, com, bs); + case GF_SG_MULTIPLE_REPLACE: + GF_BIFS_WRITE_INT(codec, bs, 4, 8, "MultipleReplace", NULL); + return BE_MultipleReplace(codec, com, bs); + case GF_SG_GLOBAL_QUANTIZER: + GF_BIFS_WRITE_INT(codec, bs, 5, 8, "GlobalQuantizer", NULL); + return BE_GlobalQuantizer(codec, com, bs); + case GF_SG_NODE_DELETE_EX: + GF_BIFS_WRITE_INT(codec, bs, 6, 8, "MultipleReplace", NULL); + GF_BIFS_WRITE_INT(codec, bs, gf_node_get_id(com->node) - 1, codec->info->config.NodeIDBits, "NodeID", NULL); + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + +/*inserts a node in a container (node.children)*/ +GF_Err BE_NodeInsert(GF_BifsEncoder *codec, GF_Command *com, GF_BitStream *bs) +{ + u32 NDT; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *)gf_list_get(com->command_fields, 0); + + GF_BIFS_WRITE_INT(codec, bs, gf_node_get_id(com->node) - 1, codec->info->config.NodeIDBits, "NodeID", NULL); + + NDT = gf_bifs_get_child_table(com->node); + + switch (inf->pos) { + case 0: + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "FIRST", "idx"); + break; + case -1: + GF_BIFS_WRITE_INT(codec, bs, 3, 2, "LAST", "idx"); + break; + default: + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "pos", "idx"); + GF_BIFS_WRITE_INT(codec, bs, inf->pos, 8, "pos", NULL); + break; + } + return gf_bifs_enc_node(codec, inf->new_node, NDT, bs); +} + +GF_Err BE_IndexInsert(GF_BifsEncoder *codec, GF_Command *com, GF_BitStream *bs) +{ + GF_Err e; + u32 NumBits, ind; + GF_FieldInfo field, sffield; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *)gf_list_get(com->command_fields, 0); + + GF_BIFS_WRITE_INT(codec, bs, gf_node_get_id(com->node) - 1, codec->info->config.NodeIDBits, "NodeID", NULL); + + /*index insertion uses IN mode for field index*/ + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(com->node, GF_SG_FIELD_CODING_IN)-1); + gf_bifs_field_index_by_mode(com->node, inf->fieldIndex, GF_SG_FIELD_CODING_IN, &ind); + GF_BIFS_WRITE_INT(codec, bs, ind, NumBits, "field", NULL); + + switch (inf->pos) { + case 0: + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "FIRST", "idx"); + break; + case -1: + GF_BIFS_WRITE_INT(codec, bs, 3, 2, "LAST", "idx"); + break; + default: + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "pos", "idx"); + GF_BIFS_WRITE_INT(codec, bs, inf->pos, 16, "pos", NULL); + break; + } + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + if (gf_sg_vrml_is_sf_field(field.fieldType)) + return GF_NON_COMPLIANT_BITSTREAM; + + memcpy(&sffield, &field, sizeof(GF_FieldInfo)); + sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + sffield.far_ptr = inf->field_ptr; + + /*rescale the MFField and parse the SFField*/ + if (field.fieldType==GF_SG_VRML_MFNODE) { + return gf_bifs_enc_node(codec, inf->new_node, field.NDTtype, bs); + } else { + return gf_bifs_enc_sf_field(codec, bs, com->node, &sffield); + } +} + + +GF_Err BE_IndexDelete(GF_BifsEncoder *codec, GF_Command *com, GF_BitStream *bs) +{ + u32 NumBits, ind; + GF_Err e; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *)gf_list_get(com->command_fields, 0); + + GF_BIFS_WRITE_INT(codec, bs, gf_node_get_id(com->node) - 1, codec->info->config.NodeIDBits, "NodeID", NULL); + + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(com->node, GF_SG_FIELD_CODING_IN) - 1); + e = gf_bifs_field_index_by_mode(com->node, inf->fieldIndex, GF_SG_FIELD_CODING_IN, &ind); + if (e) return e; + GF_BIFS_WRITE_INT(codec, bs, ind, NumBits, "field", NULL); + + switch (inf->pos) { + case 0: + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "FIRST", "idw"); + break; + case -1: + GF_BIFS_WRITE_INT(codec, bs, 3, 2, "LAST", "idx"); + break; + default: + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "pos", "idx"); + GF_BIFS_WRITE_INT(codec, bs, inf->pos, 16, "pos", NULL); + break; + } + return GF_OK; +} + + +GF_Err BE_NodeReplace(GF_BifsEncoder *codec, GF_Command *com, GF_BitStream *bs) +{ + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *)gf_list_get(com->command_fields, 0); + GF_BIFS_WRITE_INT(codec, bs, gf_node_get_id(com->node) - 1, codec->info->config.NodeIDBits, "NodeID", NULL); + return gf_bifs_enc_node(codec, inf->new_node, NDT_SFWorldNode, bs); +} + +GF_Err BE_FieldReplace(GF_BifsEncoder *codec, GF_Command *com, GF_BitStream *bs) +{ + GF_Err e; + u32 ind, NumBits; + GF_FieldInfo field; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *)gf_list_get(com->command_fields, 0); + + GF_BIFS_WRITE_INT(codec, bs, gf_node_get_id(com->node) - 1, codec->info->config.NodeIDBits, "NodeID", NULL); + + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(com->node, GF_SG_FIELD_CODING_IN)-1); + gf_bifs_field_index_by_mode(com->node, inf->fieldIndex, GF_SG_FIELD_CODING_IN, &ind); + GF_BIFS_WRITE_INT(codec, bs, ind, NumBits, "field", NULL); + + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + field.far_ptr = inf->field_ptr; + + /* Warning: To be changed when proper solution is found */ + if (gf_sg_vrml_get_sf_type(field.fieldType) == GF_SG_VRML_SFSCRIPT) codec->is_encoding_command = 1; + + e = gf_bifs_enc_field(codec, bs, com->node, &field); + + codec->is_encoding_command = 0; + return e; +} + +GF_Err BE_IndexFieldReplace(GF_BifsEncoder *codec, GF_Command *com, GF_BitStream *bs) +{ + u32 ind, NumBits; + GF_Err e; + GF_FieldInfo field, sffield; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *)gf_list_get(com->command_fields, 0); + + GF_BIFS_WRITE_INT(codec, bs, gf_node_get_id(com->node) - 1, codec->info->config.NodeIDBits, "NodeID", NULL); + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(com->node, GF_SG_FIELD_CODING_IN)-1); + gf_bifs_field_index_by_mode(com->node, inf->fieldIndex, GF_SG_FIELD_CODING_IN, &ind); + GF_BIFS_WRITE_INT(codec, bs, ind, NumBits, "field", NULL); + + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (gf_sg_vrml_is_sf_field(field.fieldType)) + return GF_NON_COMPLIANT_BITSTREAM; + + switch (inf->pos) { + case 0: + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "FIRST", "idx"); + break; + case -1: + GF_BIFS_WRITE_INT(codec, bs, 3, 2, "LAST", "idx"); + break; + default: + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "pos", "idx"); + GF_BIFS_WRITE_INT(codec, bs, inf->pos, 16, "pos", NULL); + break; + } + + if (field.fieldType == GF_SG_VRML_MFNODE) { + e = gf_bifs_enc_node(codec, inf->new_node, field.NDTtype, bs); + } else { + memcpy(&sffield, &field, sizeof(GF_FieldInfo)); + sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + sffield.far_ptr = inf->field_ptr; + e = gf_bifs_enc_sf_field(codec, bs, com->node, &sffield); + } + return e; +} + +GF_Err BE_RouteReplace(GF_BifsEncoder *codec, GF_Command *com, GF_BitStream *bs, Bool isInsert) +{ + GF_Err e; + GF_Node *n; + u32 numBits, ind; + + if (isInsert) { + GF_BIFS_WRITE_INT(codec, bs, com->RouteID ? 1 : 0, 1, "isDEF", NULL); + if (com->RouteID) { + GF_BIFS_WRITE_INT(codec, bs, com->RouteID-1, codec->info->config.RouteIDBits, "RouteID", NULL); + if (codec->UseName) gf_bifs_enc_name(codec, bs, com->def_name); + } + } else { + GF_BIFS_WRITE_INT(codec, bs, com->RouteID - 1, codec->info->config.RouteIDBits, "RouteID", NULL); + } + + /*origin*/ + GF_BIFS_WRITE_INT(codec, bs, com->fromNodeID - 1, codec->info->config.NodeIDBits, "outNodeID", NULL); + n = gf_bifs_enc_find_node(codec, com->fromNodeID); + numBits = gf_node_get_num_fields_in_mode(n, GF_SG_FIELD_CODING_OUT) - 1; + numBits = gf_get_bit_size(numBits); + e = gf_bifs_field_index_by_mode(n, com->fromFieldIndex, GF_SG_FIELD_CODING_OUT, &ind); + if (e) return e; + GF_BIFS_WRITE_INT(codec, bs, ind, numBits, "outField", NULL); + + /*target*/ + GF_BIFS_WRITE_INT(codec, bs, com->toNodeID - 1, codec->info->config.NodeIDBits, "inNodeID", NULL); + n = gf_bifs_enc_find_node(codec, com->toNodeID); + numBits = gf_node_get_num_fields_in_mode(n, GF_SG_FIELD_CODING_IN) - 1; + numBits = gf_get_bit_size(numBits); + e = gf_bifs_field_index_by_mode(n, com->toFieldIndex, GF_SG_FIELD_CODING_IN, &ind); + GF_BIFS_WRITE_INT(codec, bs, ind, numBits, "inField", NULL); + return e; +} + + +GF_Err BE_EncProtoList(GF_BifsEncoder *codec, GF_List *protoList, GF_BitStream *bs) +{ + u8 useQuant, useAnim; + u32 i, j, nbRoutes, nbBits, numProtos, numFields, count; + GF_Node *node; + GF_ProtoFieldInterface *proto_field; + GF_Proto *proto, *prev_proto; + GF_Route *r; + GF_Err e; + GF_SceneGraph *rootSG; + GF_FieldInfo field; + + e = GF_OK; + if (!protoList || !gf_list_count(protoList)) { + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "moreProto", NULL); + return GF_OK; + } + if (!codec->info->config.ProtoIDBits) + return GF_NON_COMPLIANT_BITSTREAM; + + /*store state*/ + rootSG = codec->current_proto_graph; + prev_proto = codec->encoding_proto; + + numProtos = gf_list_count(protoList); + for (i=0; iencoding_proto = proto; + codec->current_proto_graph = proto->sub_graph; + + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "moreProto", NULL); + + /*1- proto interface declaration*/ + GF_BIFS_WRITE_INT(codec, bs, proto->ID, codec->info->config.ProtoIDBits, "protoID", NULL); + + if (codec->UseName) gf_bifs_enc_name(codec, bs, proto->Name); + + numFields = gf_list_count(proto->proto_fields); + for (j=0; jproto_fields, j); + + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "moreField", NULL); + GF_BIFS_WRITE_INT(codec, bs, proto_field->EventType, 2, "eventType", NULL); + GF_BIFS_WRITE_INT(codec, bs, proto_field->FieldType, 6, "fieldType", NULL); + + if (codec->UseName) gf_bifs_enc_name(codec, bs, proto_field->FieldName); + switch (proto_field->EventType) { + case GF_SG_EVENT_EXPOSED_FIELD: + case GF_SG_EVENT_FIELD: + gf_sg_proto_field_get_field(proto_field, &field); + if (gf_sg_vrml_is_sf_field(field.fieldType)) { + e = gf_bifs_enc_sf_field(codec, bs, NULL, &field); + } else { + if (codec->info->config.UsePredictiveMFField) GF_BIFS_WRITE_INT(codec, bs, 0, 1, "usePredictive", NULL); + e = gf_bifs_enc_mf_field(codec, bs, NULL, &field); + } + if (e) goto exit; + break; + } + if (proto_field->QP_Type) useQuant = 1; + if (proto_field->Anim_Type) useAnim = 1; + } + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "moreField", NULL); + + GF_BIFS_WRITE_INT(codec, bs, proto->ExternProto.count ? 1 : 0, 1, "externProto", NULL); + /*externProto*/ + if (proto->ExternProto.count) { + memset(&field, 0, sizeof(GF_FieldInfo)); + field.far_ptr = &proto->ExternProto; + field.fieldType = GF_SG_VRML_MFURL; + field.name = "ExternProto"; + + if (codec->info->config.UsePredictiveMFField) GF_BIFS_WRITE_INT(codec, bs, 0, 1, "usePredictive", NULL); + e = gf_bifs_enc_mf_field(codec, bs, NULL, &field); + if (e) goto exit; + } else { + /*encode sub-proto list*/ + e = BE_EncProtoList(codec, proto->sub_graph->protos, bs); + if (e) goto exit; + + count = gf_list_count(proto->node_code); + /*BIFS cannot encode empty protos ! We therefore encode a NULL node instead*/ + if (!count) { + gf_bifs_enc_node(codec, NULL, NDT_SFWorldNode, bs); + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "moreNodes", NULL); + } else { + for (j=0; jnode_code, j); + e = gf_bifs_enc_node(codec, node, NDT_SFWorldNode, bs); + if (e) goto exit; + GF_BIFS_WRITE_INT(codec, bs, (j+1==count) ? 0 : 1, 1, "moreNodes", NULL); + } + } + + /*encode routes routes*/ + nbRoutes = count = gf_list_count(proto->sub_graph->Routes); + for (j=0; jsub_graph->Routes, j); + if (r->IS_route) nbRoutes--; + } + + GF_BIFS_WRITE_INT(codec, bs, nbRoutes ? 1 : 0, 1, "hasRoute", NULL); + if (nbRoutes) { + nbBits = gf_get_bit_size(nbRoutes); + if (nbBits + 5 > nbRoutes) { + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "isList", NULL); + /*list*/ + for (j=0; jsub_graph->Routes, j); + if (r->IS_route) continue; + e = gf_bifs_enc_route(codec, r, bs); + if (e) goto exit; + nbRoutes--; + GF_BIFS_WRITE_INT(codec, bs, nbRoutes ? 1 : 0, 1, "moreRoute", NULL); + } + } else { + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "isList", NULL); + GF_BIFS_WRITE_INT(codec, bs, nbBits, 5, "nbBits", NULL); + GF_BIFS_WRITE_INT(codec, bs, nbRoutes, nbBits, "length", NULL); + for (j=0; jsub_graph->Routes, j); + if (r->IS_route) continue; + e = gf_bifs_enc_route(codec, r, bs); + if (e) goto exit; + } + } + } + } + + /*anim and Quantization stuff*/ + GF_BIFS_WRITE_INT(codec, bs, useQuant, 1, "useQuant", NULL); + GF_BIFS_WRITE_INT(codec, bs, useAnim, 1, "useAnim", NULL); + + if (!useAnim && !useQuant) continue; + + count = gf_sg_proto_get_field_count(proto); + for (j=0; jQP_Type, 4, "QPType", NULL); + if (proto_field->QP_Type==QC_LINEAR_SCALAR) GF_BIFS_WRITE_INT(codec, bs, proto_field->NumBits, 5, "nbBits", NULL); + GF_BIFS_WRITE_INT(codec, bs, proto_field->hasMinMax, 1, "hasMinMax", NULL); + if (proto_field->hasMinMax) { + field.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + switch (field.fieldType) { + case GF_SG_VRML_SFINT32: + case GF_SG_VRML_SFTIME: + break; + default: + field.fieldType = GF_SG_VRML_SFFLOAT; + break; + } + field.name = "QPMinValue"; + field.far_ptr = proto_field->qp_min_value; + gf_bifs_enc_sf_field(codec, bs, NULL, &field); + + field.name = "QPMaxValue"; + field.far_ptr = proto_field->qp_max_value; + gf_bifs_enc_sf_field(codec, bs, NULL, &field); + } + } + + /*anim - not supported yet*/ + if (useAnim && ( (field.eventType == GF_SG_EVENT_IN) || (field.eventType == GF_SG_EVENT_EXPOSED_FIELD) )) { + e = GF_NOT_SUPPORTED; + goto exit; + } + } + } + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "moreProto", NULL); + +exit: + /*restore scene graph state*/ + codec->encoding_proto = prev_proto; + codec->current_proto_graph = rootSG; + return e; +} + + + +GF_Err gf_bifs_enc_route(GF_BifsEncoder *codec, GF_Route *r, GF_BitStream *bs) +{ + GF_Err e; + u32 numBits, ind; + + if (!r) return GF_BAD_PARAM; + + GF_BIFS_WRITE_INT(codec, bs, r->ID ? 1: 0, 1, "isDEF", NULL); + /*def'ed route*/ + if (r->ID) { + GF_BIFS_WRITE_INT(codec, bs, r->ID-1, codec->info->config.RouteIDBits, "routeID", NULL); + if (codec->UseName) gf_bifs_enc_name(codec, bs, r->name); + } + /*origin*/ + GF_BIFS_WRITE_INT(codec, bs, gf_node_get_id(r->FromNode) - 1, codec->info->config.NodeIDBits, "outNodeID", NULL); + numBits = gf_node_get_num_fields_in_mode(r->FromNode, GF_SG_FIELD_CODING_OUT) - 1; + numBits = gf_get_bit_size(numBits); + e = gf_bifs_field_index_by_mode(r->FromNode, r->FromField.fieldIndex, GF_SG_FIELD_CODING_OUT, &ind); + if (e) return e; + GF_BIFS_WRITE_INT(codec, bs, ind, numBits, "outField", NULL); + + /*target*/ + GF_BIFS_WRITE_INT(codec, bs, gf_node_get_id(r->ToNode) - 1, codec->info->config.NodeIDBits, "inNodeID", NULL); + numBits = gf_node_get_num_fields_in_mode(r->ToNode, GF_SG_FIELD_CODING_IN) - 1; + numBits = gf_get_bit_size(numBits); + e = gf_bifs_field_index_by_mode(r->ToNode, r->ToField.fieldIndex, GF_SG_FIELD_CODING_IN, &ind); + GF_BIFS_WRITE_INT(codec, bs, ind, numBits, "inField", NULL); + return e; +} + +GF_Err BE_SceneReplaceEx(GF_BifsEncoder *codec, GF_Command *com, GF_BitStream *bs, GF_List *routes) +{ + u32 i, nbR, nbBits; + GF_Err e; + + /*reserved*/ + GF_BIFS_WRITE_INT(codec, bs, 0, 6, "reserved", NULL); + GF_BIFS_WRITE_INT(codec, bs, codec->UseName ? 1 : 0, 1, "useName", NULL); + + if (gf_list_count(com->new_proto_list)) { + e = BE_EncProtoList(codec, com->new_proto_list, bs); + if (e) goto exit; + } else { + e = BE_EncProtoList(codec, com->in_scene->protos, bs); + if (e) goto exit; + } + + /*NULL root is valid for ProtoLibraries*/ + e = gf_bifs_enc_node(codec, com->node, NDT_SFTopNode, bs); + if (e || !gf_list_count(routes) ) { + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "hasRoute", NULL); + return codec->LastError = e; + } + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "hasRoute", NULL); + nbR = gf_list_count(routes); + nbBits = gf_get_bit_size(nbR); + if (nbBits + 5 > nbR) { + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "isList", NULL); + /*list*/ + for (i=0; iLastError = e; +} + + +GF_Err BE_SceneReplace(GF_BifsEncoder *codec, GF_SceneGraph *graph, GF_BitStream *bs) +{ + u32 i, nbR, nbBits; + GF_Err e; + + /*reserved*/ + GF_BIFS_WRITE_INT(codec, bs, 0, 6, "reserved", NULL); + GF_BIFS_WRITE_INT(codec, bs, codec->UseName ? 1 : 0, 1, "useName", NULL); + + /*assign current graph*/ + codec->scene_graph = graph; + + e = BE_EncProtoList(codec, codec->scene_graph ? codec->scene_graph->protos : NULL, bs); + if (e) goto exit; + + /*NULL root is valid for ProtoLibraries*/ + e = gf_bifs_enc_node(codec, graph ? graph->RootNode : NULL, NDT_SFTopNode, bs); + if (e || !graph || !gf_list_count(graph->Routes) ) { + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "hasRoute", NULL); + return codec->LastError = e; + } + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "hasRoute", NULL); + nbR = gf_list_count(graph->Routes); + nbBits = gf_get_bit_size(nbR); + if (nbBits + 5 > nbR) { + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "isList", NULL); + /*list*/ + for (i=0; iRoutes, i), bs); + if (e) goto exit; + GF_BIFS_WRITE_INT(codec, bs, (i+1==nbR) ? 0 : 1, 1, "moreRoute", NULL); + } + } else { + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "isList", NULL); + GF_BIFS_WRITE_INT(codec, bs, nbBits, 5, "nbBits", NULL); + GF_BIFS_WRITE_INT(codec, bs, nbR, nbBits, "nbRoutes", NULL); + for (i=0; iRoutes, i), bs); + if (e) goto exit; + } + } + +exit: + return codec->LastError = e; +} + + +GF_Err gf_bifs_enc_commands(GF_BifsEncoder *codec, GF_List *comList, GF_BitStream *bs) +{ + u32 i; + u32 count; + GF_List *routes; + GF_Err e = GF_OK; + + routes = NULL; + + codec->LastError = GF_OK; + count = gf_list_count(comList); + + for (i=0; itag) { + case GF_SG_SCENE_REPLACE: + { + /*reset node context*/ + while (gf_list_count(codec->encoded_nodes)) gf_list_rem(codec->encoded_nodes, 0); + GF_BIFS_WRITE_INT(codec, bs, 3, 2, "SceneReplace", NULL); + + if (!com->aggregated) { + routes = gf_list_new(); + /*now the trick: get all following InsertRoutes and convert as routes*/ + for (; itag!=GF_SG_ROUTE_INSERT) break; + GF_SAFEALLOC(r, GF_Route); + r->FromField.fieldIndex = rcom->fromFieldIndex; + r->FromNode = gf_sg_find_node(codec->scene_graph, rcom->fromNodeID); + r->ToField.fieldIndex = rcom->toFieldIndex; + r->ToNode = gf_sg_find_node(codec->scene_graph, rcom->toNodeID); + r->ID = rcom->RouteID; + r->name = rcom->def_name; + gf_list_add(routes, r); + } + e = BE_SceneReplaceEx(codec, com, bs, routes); + + while (gf_list_count(routes)) { + GF_Route *r = (GF_Route*)gf_list_get(routes, 0); + gf_list_rem(routes, 0); + free(r); + } + gf_list_del(routes); + } else { + e = BE_SceneReplaceEx(codec, com, bs, codec->scene_graph->Routes); + } + } + break; + /*replace commands*/ + case GF_SG_NODE_REPLACE: + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "Replace", NULL); + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "Node", NULL); + e = BE_NodeReplace(codec, com, bs); + break; + case GF_SG_FIELD_REPLACE: + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "Replace", NULL); + GF_BIFS_WRITE_INT(codec, bs, 1, 2, "Field", NULL); + e = BE_FieldReplace(codec, com, bs); + break; + case GF_SG_INDEXED_REPLACE: + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "Replace", NULL); + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "FieldIndex", NULL); + e = BE_IndexFieldReplace(codec, com, bs); + break; + case GF_SG_ROUTE_REPLACE: + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "Replace", NULL); + GF_BIFS_WRITE_INT(codec, bs, 3, 2, "Route", NULL); + e = BE_RouteReplace(codec, com, bs, 0); + break; + case GF_SG_NODE_INSERT: + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "Insert", NULL); + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "Node", NULL); + e = BE_NodeInsert(codec, com, bs); + break; + case GF_SG_INDEXED_INSERT: + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "Insert", NULL); + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "FieldIndex", NULL); + e = BE_IndexInsert(codec, com, bs); + break; + case GF_SG_ROUTE_INSERT: + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "Insert", NULL); + GF_BIFS_WRITE_INT(codec, bs, 3, 2, "Route", NULL); + e = BE_RouteReplace(codec, com, bs, 1); + break; + case GF_SG_NODE_DELETE: + GF_BIFS_WRITE_INT(codec, bs, 1, 2, "Delete", NULL); + GF_BIFS_WRITE_INT(codec, bs, 0, 2, "Node", NULL); + GF_BIFS_WRITE_INT(codec, bs, gf_node_get_id(com->node) - 1, codec->info->config.NodeIDBits, "NodeID", NULL); + break; + case GF_SG_INDEXED_DELETE: + GF_BIFS_WRITE_INT(codec, bs, 1, 2, "Delete", NULL); + GF_BIFS_WRITE_INT(codec, bs, 2, 2, "FieldIndex", NULL); + e = BE_IndexDelete(codec, com, bs); + break; + case GF_SG_ROUTE_DELETE: + GF_BIFS_WRITE_INT(codec, bs, 1, 2, "Delete", NULL); + GF_BIFS_WRITE_INT(codec, bs, 3, 2, "Route", NULL); + GF_BIFS_WRITE_INT(codec, bs, com->RouteID - 1, codec->info->config.RouteIDBits, "RouteID", NULL); + break; + + default: + e = BE_ExtendedUpdate(codec, com, bs); + break; + } + if (e) break; + + GF_BIFS_WRITE_INT(codec, bs, (i+1==count) ? 0 : 1, 1, "moreCommands", NULL); + } + + while (gf_list_count(codec->QPs)) gf_bifs_enc_qp_remove(codec, 1); + return e; +} + + +GF_EXPORT +GF_Err gf_bifs_encoder_get_rap(GF_BifsEncoder *codec, char **out_data, u32 *out_data_length) +{ + GF_BitStream *bs; + GF_Err e; + GF_List *ctx_bck; + + /*reset context for RAP encoding*/ + ctx_bck = codec->encoded_nodes; + codec->encoded_nodes = gf_list_new(); + + if (!codec->info) codec->info = (BIFSStreamInfo*)gf_list_get(codec->streamInfo, 0); + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + GF_BIFS_WRITE_INT(codec, bs, 3, 2, "SceneReplace", NULL); + e = BE_SceneReplace(codec, codec->scene_graph, bs); + if (e == GF_OK) { + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "moreCommands", NULL); + gf_bs_get_content(bs, out_data, out_data_length); + } + gf_bs_del(bs); + + /*restore context*/ + gf_list_del(codec->encoded_nodes); + codec->encoded_nodes = ctx_bck; + + return e; +} + diff --git a/src/bifs/conditional.c b/src/bifs/conditional.c new file mode 100644 index 0000000..5ecd527 --- /dev/null +++ b/src/bifs/conditional.c @@ -0,0 +1,177 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include + +/*private stack for conditional*/ +typedef struct +{ + /*BIFS decoder*/ + GF_BifsDecoder *codec; + /*BIFS config of original stream carrying the conditional*/ + BIFSStreamInfo *info; +} ConditionalStack; + +void Conditional_PreDestroy(GF_Node *n, void *eff, Bool is_destroy) +{ + if (is_destroy) { + ConditionalStack *priv = (ConditionalStack*)gf_node_get_private(n); + if (priv) free(priv); + } +} + +void Conditional_BufferReplaced(GF_BifsDecoder *codec, GF_Node *n) +{ + ConditionalStack *priv = (ConditionalStack*)gf_node_get_private(n); + if (!priv || (gf_node_get_tag(n) != TAG_MPEG4_Conditional)) return; + priv->info = codec->info; +} + +static void Conditional_execute(M_Conditional *node) +{ + GF_Err e; + char *buffer; + u32 len; + GF_BitStream *bs; + GF_BifsDecoder *codec; + GF_Proto *prevproto; + GF_SceneGraph *prev_graph, *cur_graph; + ConditionalStack *priv = (ConditionalStack*)gf_node_get_private((GF_Node*)node); + if (!priv) return; + + /*set the codec working graph to the node one (to handle conditional in protos)*/ + prev_graph = priv->codec->current_graph; + cur_graph = priv->codec->current_graph = gf_node_get_graph((GF_Node*)node); + assert(priv->codec->current_graph); + + priv->codec->info = priv->info; + prevproto = priv->codec->pCurrentProto; + priv->codec->pCurrentProto = NULL; + if (priv->codec->current_graph->pOwningProto) priv->codec->pCurrentProto = priv->codec->current_graph->pOwningProto->proto_interface; + + /*set isActive - to clarify in the specs*/ + node->isActive = 1; + gf_node_event_out_str((GF_Node *)node, "isActive"); + if (!node->buffer.bufferSize) return; + + /*we may replace ourselves*/ + buffer = (char*)node->buffer.buffer; + len = node->buffer.bufferSize; + node->buffer.buffer = NULL; + node->buffer.bufferSize = 0; + bs = gf_bs_new(buffer, len, GF_BITSTREAM_READ); + codec = priv->codec; + codec->cts_offset = gf_node_get_scene_time((GF_Node*)node); + /*a conditional may destroy/replace itself - to prevent that, protect node by a register/unregister ...*/ + gf_node_register((GF_Node*)node, NULL); +#ifdef GF_SELF_REPLACE_ENABLE + /*and a conditional may destroy the entire scene!*/ + cur_graph->graph_has_been_reset = 0; +#endif + e = gf_bifs_dec_command(codec, bs); + gf_bs_del(bs); +#ifdef GF_SELF_REPLACE_ENABLE + if (cur_graph->graph_has_been_reset) { + return; + } +#endif + if (node->buffer.buffer) { + free(buffer); + } else { + node->buffer.buffer = buffer; + node->buffer.bufferSize = len; + } + //set isActive - to clarify in the specs +// node->isActive = 0; + gf_node_unregister((GF_Node*)node, NULL); + codec->cts_offset = 0; + codec->pCurrentProto = prevproto; + codec->current_graph = prev_graph; +} + +void Conditional_OnActivate(GF_Node *n) +{ + M_Conditional *node = (M_Conditional *)n; + if (! node->activate) return; + Conditional_execute(node); +} + +void Conditional_OnReverseActivate(GF_Node *n) +{ + M_Conditional *node = (M_Conditional *)n; + if (node->reverseActivate) return; + Conditional_execute(node); +} + +void SetupConditional(GF_BifsDecoder *codec, GF_Node *node) +{ + ConditionalStack *priv; + if (gf_node_get_tag(node) != TAG_MPEG4_Conditional) return; + priv = (ConditionalStack*)malloc(sizeof(ConditionalStack)); + + /*needed when initializing extern protos*/ + if (!codec->info) codec->info = (BIFSStreamInfo*)gf_list_get(codec->streamInfo, 0); + if (!codec->info) return; + + priv->info = codec->info; + priv->codec = codec; + gf_node_set_callback_function(node, Conditional_PreDestroy); + gf_node_set_private(node, priv); + ((M_Conditional *)node)->on_activate = Conditional_OnActivate; + ((M_Conditional *)node)->on_reverseActivate = Conditional_OnReverseActivate; +} + +/*this is ugly but we have no choice, we need to clone the conditional stack because of externProto*/ +void BIFS_SetupConditionalClone(GF_Node *node, GF_Node *orig) +{ + M_Conditional *ptr; + u32 i; + ConditionalStack *priv_orig, *priv; + priv_orig = (ConditionalStack*)gf_node_get_private(orig); + /*looks we're not in BIFS*/ + if (!priv_orig) { + GF_Command *ori_com; + M_Conditional *c_orig, *c_dest; + c_orig = (M_Conditional *)orig; + c_dest = (M_Conditional *)node; + gf_node_init(node); + /*and clone all commands*/ + i=0; + while ((ori_com = (GF_Command*)gf_list_enum(c_orig->buffer.commandList, &i))) { + GF_Command *dest_com = gf_sg_command_clone(ori_com, gf_node_get_graph(node), 1); + if (dest_com) gf_list_add(c_dest->buffer.commandList, dest_com); + } + return; + } + priv = (ConditionalStack*)malloc(sizeof(ConditionalStack)); + priv->codec = priv_orig->codec; + priv->info = priv_orig->info; + gf_node_set_callback_function(node, Conditional_PreDestroy); + gf_node_set_private(node, priv); + ptr = (M_Conditional *)node; + ptr->on_activate = Conditional_OnActivate; + ptr->on_reverseActivate = Conditional_OnReverseActivate; +} diff --git a/src/bifs/field_decode.c b/src/bifs/field_decode.c new file mode 100644 index 0000000..c59b324 --- /dev/null +++ b/src/bifs/field_decode.c @@ -0,0 +1,894 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include "quant.h" +#include "script.h" + + +void SFCommandBufferChanged(GF_BifsDecoder * codec, GF_Node *node) +{ + void Conditional_BufferReplaced(GF_BifsDecoder * codec, GF_Node *node); + + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_Conditional: + Conditional_BufferReplaced(codec, node); + break; + } +} + + +//startTimes, stopTimes and co are coded as relative to their AU timestamp when received +//on the wire. If from scripts or within proto the offset doesn't apply +void BD_OffsetSFTime(GF_BifsDecoder * codec, Double *time) +{ + if ((!codec->is_com_dec && codec->pCurrentProto) || codec->dec_memory_mode) return; + *time += codec->cts_offset; +} + +void BD_CheckSFTimeOffset(GF_BifsDecoder *codec, GF_Node *node, GF_FieldInfo *inf) +{ + if (gf_node_get_tag(node) != TAG_ProtoNode) { + if (!stricmp(inf->name, "startTime") || !stricmp(inf->name, "stopTime")) + BD_OffsetSFTime(codec, (Double *)inf->far_ptr); + } else if (gf_sg_proto_field_is_sftime_offset(node, inf)) { + BD_OffsetSFTime(codec, (Double *)inf->far_ptr); + } +} + + +Fixed BD_ReadSFFloat(GF_BifsDecoder * codec, GF_BitStream *bs) +{ + if (codec->ActiveQP && codec->ActiveQP->useEfficientCoding) + return gf_bifs_dec_mantissa_float(codec, bs); + + return FLT2FIX(gf_bs_read_float(bs)); +} + + +GF_Err gf_bifs_dec_sf_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +{ + GF_Err e; + GF_Node *new_node; + u32 size, length, w, h, i; + char *buffer; + + //blindly call unquantize. return is OK, error or GF_EOS + if (codec->ActiveQP && node) { + e = gf_bifs_dec_unquant_field(codec, bs, node, field); + if (e != GF_EOS) return e; + } + //not quantized, use normal scheme + switch (field->fieldType) { + case GF_SG_VRML_SFBOOL: + * ((SFBool *) field->far_ptr) = (SFBool) gf_bs_read_int(bs, 1); + break; + case GF_SG_VRML_SFCOLOR: + ((SFColor *)field->far_ptr)->red = BD_ReadSFFloat(codec, bs);; + ((SFColor *)field->far_ptr)->green = BD_ReadSFFloat(codec, bs); + ((SFColor *)field->far_ptr)->blue = BD_ReadSFFloat(codec, bs); + break; + case GF_SG_VRML_SFFLOAT: + *((SFFloat *)field->far_ptr) = BD_ReadSFFloat(codec, bs); + break; + case GF_SG_VRML_SFINT32: + *((SFInt32 *)field->far_ptr) = (s32) gf_bs_read_int(bs, 32); + break; + case GF_SG_VRML_SFTIME: + *((SFTime *)field->far_ptr) = gf_bs_read_double(bs); + if (node) BD_CheckSFTimeOffset(codec, node, field); + break; + case GF_SG_VRML_SFVEC2F: + ((SFVec2f *)field->far_ptr)->x = BD_ReadSFFloat(codec, bs); + ((SFVec2f *)field->far_ptr)->y = BD_ReadSFFloat(codec, bs); + break; + case GF_SG_VRML_SFVEC3F: + ((SFVec3f *)field->far_ptr)->x = BD_ReadSFFloat(codec, bs); + ((SFVec3f *)field->far_ptr)->y = BD_ReadSFFloat(codec, bs); + ((SFVec3f *)field->far_ptr)->z = BD_ReadSFFloat(codec, bs); + break; + case GF_SG_VRML_SFROTATION: + ((SFRotation *)field->far_ptr)->x = BD_ReadSFFloat(codec, bs); + ((SFRotation *)field->far_ptr)->y = BD_ReadSFFloat(codec, bs); + ((SFRotation *)field->far_ptr)->z = BD_ReadSFFloat(codec, bs); + ((SFRotation *)field->far_ptr)->q = BD_ReadSFFloat(codec, bs); + break; + case GF_SG_VRML_SFSTRING: + size = gf_bs_read_int(bs, 5); + length = gf_bs_read_int(bs, size); + if (gf_bs_available(bs) < length) return GF_NON_COMPLIANT_BITSTREAM; + + if ( ((SFString *)field->far_ptr)->buffer ) free( ((SFString *)field->far_ptr)->buffer); + ((SFString *)field->far_ptr)->buffer = (char *)malloc(sizeof(char)*(length+1)); + memset(((SFString *)field->far_ptr)->buffer , 0, length+1); + for (i=0; ifar_ptr)->buffer[i] = gf_bs_read_int(bs, 8); + } + break; + case GF_SG_VRML_SFURL: + { + SFURL *url = (SFURL *) field->far_ptr; + size = gf_bs_read_int(bs, 1); + if (size) { + if (url->url) free(url->url ); + url->url = NULL; + length = gf_bs_read_int(bs, 10); + url->OD_ID = length; + } else { + if ( url->OD_ID ) url->OD_ID = (u32) -1; + size = gf_bs_read_int(bs, 5); + length = gf_bs_read_int(bs, size); + if (gf_bs_available(bs) < length) return GF_NON_COMPLIANT_BITSTREAM; + buffer = NULL; + if (length) { + buffer = (char *)malloc(sizeof(char)*(length+1)); + memset(buffer, 0, length+1); + for (i=0; iurl) free( url->url); + /*if URL is empty set it to NULL*/ + if (buffer && strlen(buffer)) { + url->url = buffer; + } else { + free(buffer); + url->url = NULL; + } + } + } + break; + case GF_SG_VRML_SFIMAGE: + if (((SFImage *)field->far_ptr)->pixels) free(((SFImage *)field->far_ptr)->pixels); + w = gf_bs_read_int(bs, 12); + h = gf_bs_read_int(bs, 12); + length = gf_bs_read_int(bs, 2); + + if (length > 3) length = 3; + length += 1; + size = w * h * length; + if (gf_bs_available(bs) < size) return GF_NON_COMPLIANT_BITSTREAM; + ((SFImage *)field->far_ptr)->width = w; + ((SFImage *)field->far_ptr)->height = h; + ((SFImage *)field->far_ptr)->numComponents = length; + ((SFImage *)field->far_ptr)->pixels = (unsigned char *)malloc(sizeof(char)*size); + //WARNING: Buffers are NOT ALIGNED IN THE BITSTREAM + for (i=0; ifar_ptr)->pixels[i] = gf_bs_read_int(bs, 8); + } + break; + case GF_SG_VRML_SFCOMMANDBUFFER: + { + SFCommandBuffer *sfcb = (SFCommandBuffer *)field->far_ptr; + if (sfcb->buffer) { + free(sfcb->buffer); + sfcb->buffer = NULL; + } + while (gf_list_count(sfcb->commandList)) { + GF_Command *com = (GF_Command*)gf_list_get(sfcb->commandList, 0); + gf_list_rem(sfcb->commandList, 0); + gf_sg_command_del(com); + } + + size = gf_bs_read_int(bs, 5); + length = gf_bs_read_int(bs, size); + if (gf_bs_available(bs) < length) return GF_NON_COMPLIANT_BITSTREAM; + + sfcb->bufferSize = length; + if (length) { + sfcb->buffer = (unsigned char *)malloc(sizeof(char)*(length)); + //WARNING Buffers are NOT ALIGNED IN THE BITSTREAM + for (i=0; ibuffer[i] = gf_bs_read_int(bs, 8); + } + } + //notify the node - this is needed in case an enhencement layer replaces the buffer, in which case + //the # ID Bits may change + SFCommandBufferChanged(codec, node); + + /* + 1 - memory mode, register command buffer for later parsing + 2 - InputSensor only works on decompressed commands + */ + if (codec->dec_memory_mode || (node->sgprivate->tag==TAG_MPEG4_InputSensor)) { + CommandBufferItem *cbi = (CommandBufferItem *)malloc(sizeof(CommandBufferItem)); + cbi->node = node; + cbi->cb = sfcb; + gf_list_add(codec->command_buffers, cbi); + } + } + break; + case GF_SG_VRML_SFNODE: + //for nodes the field ptr is a ptr to the field, which is a node ptr ;) + new_node = gf_bifs_dec_node(codec, bs, field->NDTtype); + if (new_node) { + e = gf_node_register(new_node, node); + if (e) return e; + } + //it may happen that new_node is NULL (this is valid for a proto declaration) + *((GF_Node **) field->far_ptr) = new_node; + break; + case GF_SG_VRML_SFSCRIPT: + codec->LastError = SFScript_Parse(codec, (SFScript*)field->far_ptr, bs, node); + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + return codec->LastError; +} + +GF_Err BD_DecMFFieldList(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +{ + GF_Node *new_node; + GF_Err e; + u8 endFlag, qp_local, qp_on, initial_qp; + GF_ChildNodeItem *last = NULL; + u32 nbF; + + GF_FieldInfo sffield; + + memset(&sffield, 0, sizeof(GF_FieldInfo)); + sffield.fieldIndex = field->fieldIndex; + sffield.fieldType = gf_sg_vrml_get_sf_type(field->fieldType); + sffield.NDTtype = field->NDTtype; + + nbF = 0; + qp_on = qp_local = 0; + initial_qp = codec->ActiveQP ? 1 : 0; + + endFlag = gf_bs_read_int(bs, 1); + while (!endFlag) { + e = GF_OK;; + if (field->fieldType != GF_SG_VRML_MFNODE) { + e = gf_sg_vrml_mf_append(field->far_ptr, field->fieldType, & sffield.far_ptr); + e = gf_bifs_dec_sf_field(codec, bs, node, &sffield); + } else { + new_node = gf_bifs_dec_node(codec, bs, field->NDTtype); + //append + if (new_node) { + e = gf_node_register(new_node, node); + if (e) return e; + + //regular coding + if (node) { + //special case for QP, register as the current QP + if (gf_node_get_tag(new_node) == TAG_MPEG4_QuantizationParameter) { + qp_local = ((M_QuantizationParameter *)new_node)->isLocal; + //we have a QP in the same scope, remove previous + if (qp_on) gf_bifs_dec_qp_remove(codec, 0); + e = gf_bifs_dec_qp_set(codec, new_node); + if (e) return e; + qp_on = 1; + if (qp_local) qp_local = 2; + if (codec->force_keep_qp) { + e = gf_node_list_add_child_last( field->far_ptr, new_node, &last); + } else { + gf_node_register(new_node, NULL); + gf_node_unregister(new_node, node); + } + } else + //this is generic MFNode container + e = gf_node_list_add_child_last(field->far_ptr, new_node, &last); + + } + //proto coding: directly add the child + else if (codec->pCurrentProto) { + //TO DO: what happens if this is a QP node on the interface ? + e = gf_node_list_add_child_last( (GF_ChildNodeItem **)field->far_ptr, new_node, &last); + } + } else { + return codec->LastError; + } + } + if (e) return e; + + endFlag = gf_bs_read_int(bs, 1); + + //according to the spec, the QP applies to the current node itself, + //not just children. If IsLocal is TRUE remove the node + if (qp_on && qp_local) { + if (qp_local == 2) { + qp_local = 1; + } else { + //ask to get rid of QP and reactivate if we had a QP when entering + gf_bifs_dec_qp_remove(codec, initial_qp); + qp_local = 0; + qp_on = 0; + } + } + nbF += 1; + } + /*finally delete the QP if any (local or not) as we get out of this node + and reactivate previous one*/ + if (qp_on) gf_bifs_dec_qp_remove(codec, initial_qp); + /*this is for QP 14*/ + gf_bifs_dec_qp14_set_length(codec, nbF); + return GF_OK; +} + +GF_Err BD_DecMFFieldVec(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +{ + GF_Err e; + u32 NbBits, nbFields; + u32 i; + GF_ChildNodeItem *last; + u8 qp_local, qp_on, initial_qp; + GF_Node *new_node; + GF_FieldInfo sffield; + + memset(&sffield, 0, sizeof(GF_FieldInfo)); + sffield.fieldIndex = field->fieldIndex; + sffield.fieldType = gf_sg_vrml_get_sf_type(field->fieldType); + sffield.NDTtype = field->NDTtype; + + initial_qp = qp_local = qp_on = 0; + + //vector description - alloc the MF size before + NbBits = gf_bs_read_int(bs, 5); + nbFields = gf_bs_read_int(bs, NbBits); + + if (codec->ActiveQP) { + initial_qp = 1; + /*this is for QP 14*/ + gf_bifs_dec_qp14_set_length(codec, nbFields); + } + + if (field->fieldType != GF_SG_VRML_MFNODE) { + e = gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, nbFields); + if (e) return e; + + for (i=0;ifar_ptr, field->fieldType, & sffield.far_ptr, i); + if (e) return e; + e = gf_bifs_dec_sf_field(codec, bs, node, &sffield); + } + } else { + last = NULL; + for (i=0;iNDTtype); + if (new_node) { + e = gf_node_register(new_node, node); + if (e) return e; + + if (node) { + /*special case for QP, register as the current QP*/ + if (gf_node_get_tag(new_node) == TAG_MPEG4_QuantizationParameter) { + qp_local = ((M_QuantizationParameter *)new_node)->isLocal; + /*we have a QP in the same scope, remove previous + NB: we assume this is the right behaviour, the spec doesn't say + whether QP is cumulative or not*/ + if (qp_on) gf_bifs_dec_qp_remove(codec, 0); + + e = gf_bifs_dec_qp_set(codec, new_node); + if (e) return e; + qp_on = 1; + if (qp_local) qp_local = 2; + if (codec->force_keep_qp) { + e = gf_node_list_add_child_last(field->far_ptr, new_node, &last); + } else { + gf_node_register(new_node, NULL); + gf_node_unregister(new_node, node); + } + } else { + e = gf_node_list_add_child_last(field->far_ptr, new_node, &last); + } + } + /*proto coding*/ + else if (codec->pCurrentProto) { + /*TO DO: what happens if this is a QP node on the interface ?*/ + e = gf_node_list_add_child_last( (GF_ChildNodeItem **)field->far_ptr, new_node, &last); + } + } else { + return codec->LastError ? codec->LastError : GF_NON_COMPLIANT_BITSTREAM; + } + } + /*according to the spec, the QP applies to the current node itself, not just children. + If IsLocal is TRUE remove the node*/ + if (qp_on && qp_local) { + if (qp_local == 2) { + qp_local = 1; + } else { + //ask to get rid of QP and reactivate if we had a QP when entering the node + gf_bifs_dec_qp_remove(codec, initial_qp); + qp_local = 0; + } + } + } + /*finally delete the QP if any (local or not) as we get out of this node*/ + if (qp_on) gf_bifs_dec_qp_remove(codec, 1); + return GF_OK; +} + + +void gf_bifs_check_field_change(GF_Node *node, GF_FieldInfo *field) +{ + if ((field->fieldType==GF_SG_VRML_MFNODE) || (field->fieldType==GF_SG_VRML_MFNODE)) node->sgprivate->flags |= GF_SG_CHILD_DIRTY; + /*signal node modif*/ + gf_node_changed(node, field); + /*Notify eventOut in all cases to handle protos*/ + gf_node_event_out(node, field->fieldIndex); + /*and propagate eventIn if any*/ + if (field->on_event_in) { + field->on_event_in(node); + } else if ((gf_node_get_tag(node) == TAG_MPEG4_Script) && (field->eventType==GF_SG_EVENT_IN)) { + gf_sg_script_event_in(node, field); + } + +} + +GF_Err gf_bifs_dec_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +{ + GF_Err e; + u8 flag; + +// if (codec->LastError) return codec->LastError; + + assert(node); +// if (field->fieldType == GF_SG_VRML_UNKNOWN) return GF_NON_COMPLIANT_BITSTREAM; + + if (gf_sg_vrml_is_sf_field(field->fieldType)) { + e = gf_bifs_dec_sf_field(codec, bs, node, field); + if (e) return e; + } else { + /*clean up the eventIn field if not done*/ + if (field->eventType == GF_SG_EVENT_IN) { + if (field->fieldType == GF_SG_VRML_MFNODE) { + gf_node_unregister_children(node, * (GF_ChildNodeItem **)field->far_ptr); + * (GF_ChildNodeItem **)field->far_ptr = NULL; + } else { + //remove all items of the MFField + gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType); + } + } + + /*predictiveMFField*/ + if (codec->info->config.UsePredictiveMFField) { + flag = gf_bs_read_int(bs, 1); + if (flag) return gf_bifs_dec_pred_mf_field(codec, bs, node, field); + } + + /*reserved*/ + flag = gf_bs_read_int(bs, 1); + if (!flag) { + /*destroy the field content...*/ + if (field->fieldType != GF_SG_VRML_MFNODE) { + e = gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType); + if (e) return e; + } + /*List description - alloc is dynamic*/ + flag = gf_bs_read_int(bs, 1); + if (flag) { + e = BD_DecMFFieldList(codec, bs, node, field); + } else { + e = BD_DecMFFieldVec(codec, bs, node, field); + } + if (e) return e; + } + } + return GF_OK; +} + + +GF_Err BD_SetProtoISed(GF_BifsDecoder * codec, u32 protofield, GF_Node *n, u32 nodefield) +{ + /*take care of conditional execution in proto*/ + if (codec->current_graph->pOwningProto) { + return gf_sg_proto_instance_set_ised((GF_Node *) codec->current_graph->pOwningProto, protofield, n, nodefield); + } + /*regular ISed fields*/ + else { + return gf_sg_proto_field_set_ised(codec->pCurrentProto, protofield, n, nodefield); + } +} + +GF_Err gf_bifs_dec_node_list(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, Bool is_proto) +{ + u8 flag; + GF_Err e; + u32 numBitsALL, numBitsDEF, field_all, field_ref, numProtoBits; + GF_FieldInfo field; + + e = GF_OK; + + numProtoBits = numBitsALL = 0; + if (codec->pCurrentProto) { + numProtoBits = gf_get_bit_size(gf_sg_proto_get_field_count(codec->pCurrentProto) - 1); + numBitsALL = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_ALL)-1); + } + numBitsDEF = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_DEF)-1); + + flag = gf_bs_read_int(bs, 1); + while (!flag) { + if (codec->pCurrentProto) { + //IS'ed flag + flag = gf_bs_read_int(bs, 1); + if (flag) { + //get field index in ALL mode for node + field_ref = gf_bs_read_int(bs, numBitsALL); + //get field index in ALL mode for proto + field_all = gf_bs_read_int(bs, numProtoBits); + e = gf_node_get_field(node, field_ref, &field); + if (e) return e; + e = BD_SetProtoISed(codec, field_all, node, field_ref); + if (e) return e; + flag = gf_bs_read_int(bs, 1); + continue; + } + } + + //fields are coded in DEF mode + field_ref = gf_bs_read_int(bs, numBitsDEF); + e = gf_bifs_get_field_index(node, field_ref, GF_SG_FIELD_CODING_DEF, &field_all); + if (e) return e; + e = gf_node_get_field(node, field_all, &field); + if (e) return e; + e = gf_bifs_dec_field(codec, bs, node, &field); + if (e) return e; + flag = gf_bs_read_int(bs, 1); + + if (is_proto) gf_sg_proto_mark_field_loaded(node, &field); + } + return codec->LastError; +} + +GF_Err gf_bifs_dec_node_mask(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, Bool is_proto) +{ + u32 i, numFields, numProtoFields, index, flag, nbBits; + GF_Err e; + GF_FieldInfo field; + + //proto coding + if (codec->pCurrentProto) { + numFields = gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_ALL); + numProtoFields = gf_sg_proto_get_field_count(codec->pCurrentProto); + nbBits = gf_get_bit_size(numProtoFields-1); + + for (i=0; istartTime); + BD_OffsetSFTime(codec, & ((M_AnimationStream*)node)->stopTime); + break; + case TAG_MPEG4_AudioBuffer: + BD_OffsetSFTime(codec, & ((M_AudioBuffer*)node)->startTime); + BD_OffsetSFTime(codec, & ((M_AudioBuffer*)node)->stopTime); + break; + case TAG_MPEG4_AudioClip: + BD_OffsetSFTime(codec, & ((M_AudioClip*)node)->startTime); + BD_OffsetSFTime(codec, & ((M_AudioClip*)node)->stopTime); + break; + case TAG_MPEG4_AudioSource: + BD_OffsetSFTime(codec, & ((M_AudioSource*)node)->startTime); + BD_OffsetSFTime(codec, & ((M_AudioSource*)node)->stopTime); + break; + case TAG_MPEG4_MovieTexture: + BD_OffsetSFTime(codec, & ((M_MovieTexture*)node)->startTime); + BD_OffsetSFTime(codec, & ((M_MovieTexture*)node)->stopTime); + break; + case TAG_MPEG4_TimeSensor: + BD_OffsetSFTime(codec, & ((M_TimeSensor*)node)->startTime); + BD_OffsetSFTime(codec, & ((M_TimeSensor*)node)->stopTime); + break; + case TAG_ProtoNode: + { + u32 i, nbFields; + GF_FieldInfo inf; + nbFields = gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_ALL); + for (i=0; iinfo) { + codec->LastError = GF_BAD_PARAM; + return NULL; + } +#endif + + + BVersion = GF_BIFS_V1; + node_flag = 0; + + /*this is a USE statement*/ + if (gf_bs_read_int(bs, 1)) { + nodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + /*NULL node is encoded as USE with ID = all bits to 1*/ + if (nodeID == (u32) (1<info->config.NodeIDBits)) + return NULL; + //find node and return it + new_node = gf_sg_find_node(codec->current_graph, nodeID); + + if (!new_node) { + codec->LastError = GF_SG_UNKNOWN_NODE; + } else { + /*restore QP14 length*/ + switch (gf_node_get_tag(new_node)) { + case TAG_MPEG4_Coordinate: + { + u32 nbCoord = ((M_Coordinate *)new_node)->point.count; + gf_bifs_dec_qp14_enter(codec, 1); + gf_bifs_dec_qp14_set_length(codec, nbCoord); + gf_bifs_dec_qp14_enter(codec, 0); + } + break; + case TAG_MPEG4_Coordinate2D: + { + u32 nbCoord = ((M_Coordinate2D *)new_node)->point.count; + gf_bifs_dec_qp14_enter(codec, 1); + gf_bifs_dec_qp14_set_length(codec, nbCoord); + gf_bifs_dec_qp14_enter(codec, 0); + } + break; + } + } + return new_node; + } + + //this is a new node + nodeID = 0; + name[0] = 0; + node_tag = 0; + proto = NULL; + + //browse all node groups + while (1) { + NDTBits = gf_bifs_get_ndt_bits(NDT_Tag, BVersion); + /*this happens in replacescene where no top-level node is present (externProto)*/ + if ((BVersion==1) && (NDTBits > 8 * gf_bs_available(bs)) ) { + codec->LastError = GF_OK; + return NULL; + } + + node_type = gf_bs_read_int(bs, NDTBits); + if (node_type) break; + + //increment BIFS version + BVersion += 1; + //not supported + if (BVersion > GF_BIFS_NUM_VERSION) { + codec->LastError = GF_BIFS_UNKNOWN_VERSION; + return NULL; + } + } + if (BVersion==2 && node_type==1) { + ProtoID = gf_bs_read_int(bs, codec->info->config.ProtoIDBits); + /*look in current graph for the proto - this may be a proto graph*/ + proto = gf_sg_find_proto(codec->current_graph, ProtoID, NULL); + /*this was in proto so look in main scene*/ + if (!proto && codec->current_graph != codec->scenegraph) + proto = gf_sg_find_proto(codec->scenegraph, ProtoID, NULL); + + if (!proto) { + codec->LastError = GF_SG_UNKNOWN_NODE; + return NULL; + } + } else { + node_tag = gf_bifs_ndt_get_node_type(NDT_Tag, node_type, BVersion); + } + + /*special handling of 3D mesh*/ + if ((node_tag == TAG_MPEG4_IndexedFaceSet) && codec->info->config.Use3DMeshCoding) { + if (gf_bs_read_int(bs, 1)) { + nodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + if (codec->UseName) gf_bifs_dec_name(bs, name); + } + /*parse the 3DMesh node*/ + return NULL; + } + /*unknow node*/ + if (!node_tag && !proto) { + codec->LastError = GF_SG_UNKNOWN_NODE; + return NULL; + } + + + /*DEF'd flag*/ + if (gf_bs_read_int(bs, 1)) { + if (!codec->info->config.NodeIDBits) { + codec->LastError = GF_NON_COMPLIANT_BITSTREAM; + return NULL; + } + nodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + if (codec->UseName) gf_bifs_dec_name(bs, name); + } + + new_node = NULL; + skip_init = 0; + + /*don't check node IDs duplicate since VRML may use them...*/ +#if 0 + /*if a node with same DEF is already in the scene, use it + we don't do that in memory mode because commands may force replacement + of a node with a new node with same ID, and we want to be able to dump it (otherwise we would + dump a USE)*/ + if (nodeID && !codec->dec_memory_mode) { + new_node = gf_sg_find_node(codec->current_graph, nodeID); + if (new_node) { + if (proto) { + if ((gf_node_get_tag(new_node) != TAG_ProtoNode) || (gf_node_get_proto(new_node) != proto)) { + codec->LastError = GF_NON_COMPLIANT_BITSTREAM; + return NULL; + } + skip_init = 1; + } else { + if (gf_node_get_tag(new_node) != node_tag) { + codec->LastError = GF_NON_COMPLIANT_BITSTREAM; + return NULL; + } + skip_init = 1; + } + } + } +#endif + + if (!new_node) { + if (proto) { + skip_init = 1; + /*create proto interface*/ + new_node = gf_sg_proto_create_instance(codec->current_graph, proto); + } else { + new_node = gf_node_new(codec->current_graph, node_tag); + } + } + if (!new_node) { + codec->LastError = GF_NOT_SUPPORTED; + return NULL; + } + + /*VRML: "The transformation hierarchy shall be a directed acyclic graph; results are undefined if a node + in the transformation hierarchy is its own ancestor" + that's good, because the scene graph can't handle cyclic graphs (destroy will never be called). + We therefore only register the node once parsed*/ + if (nodeID) { + if (strlen(name)) { + gf_node_set_id(new_node, nodeID, name); + } else { + gf_node_set_id(new_node, nodeID, NULL); + } + } + + + /*update default time fields except in proto parsing*/ + if (!codec->pCurrentProto) UpdateTimeNode(codec, new_node); + /*nodes are only init outside protos */ + else skip_init = 1; + + /*if coords were not stored for QP14 before coding this node, reset QP14 it when leaving*/ + reset_qp14 = !codec->coord_stored; + + /*QP 14 is a special quant mode for IndexFace/Line(2D)Set to quantize the + coordonate(2D) child, based on the first field parsed + we must check the type of the node and notfy the QP*/ + switch (node_tag) { + case TAG_MPEG4_Coordinate: + case TAG_MPEG4_Coordinate2D: + gf_bifs_dec_qp14_enter(codec, 1); + } + + if (gf_bs_read_int(bs, 1)) { + e = gf_bifs_dec_node_mask(codec, bs, new_node, proto ? 1 : 0); + } else { + e = gf_bifs_dec_node_list(codec, bs, new_node, proto ? 1 : 0); + } + if (codec->coord_stored && reset_qp14) + gf_bifs_dec_qp14_reset(codec); + + if (e) { + codec->LastError = e; + /*register*/ + gf_node_register(new_node, NULL); + /*unregister (deletes)*/ + gf_node_unregister(new_node, NULL); + return NULL; + } + + if (!skip_init) + gf_node_init(new_node); + + switch (node_tag) { + case TAG_MPEG4_Coordinate: + case TAG_MPEG4_Coordinate2D: + gf_bifs_dec_qp14_enter(codec, 0); + break; + case TAG_MPEG4_Script: + /*load script if in main graph (useless to load in proto declaration)*/ + if (codec->scenegraph == codec->current_graph) { + gf_sg_script_load(new_node); + } + break; + /*conditionals must be init*/ + case TAG_MPEG4_Conditional: + SetupConditional(codec, new_node); + break; + } + + /*if new node is a proto and we're in the top scene, load proto code*/ + if (proto && (codec->scenegraph == codec->current_graph)) { + codec->LastError = gf_sg_proto_load_code(new_node); + } + return new_node; +} + diff --git a/src/bifs/field_encode.c b/src/bifs/field_encode.c new file mode 100644 index 0000000..44f34bb --- /dev/null +++ b/src/bifs/field_encode.c @@ -0,0 +1,602 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include +#include "quant.h" +#include "script.h" + + +GF_Err gf_bifs_field_index_by_mode(GF_Node *node, u32 all_ind, u8 indexMode, u32 *outField) +{ + GF_Err e; + u32 i, count, temp; + count = gf_node_get_num_fields_in_mode(node, indexMode); + for (i=0; iActiveQP && codec->ActiveQP->useEfficientCoding) { + gf_bifs_enc_mantissa_float(codec, val, bs); + } else { + gf_bs_write_float(bs, FIX2FLT(val)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[BIFS] SFFloat\t\t32\t\t%g\t\t%s\n", FIX2FLT(val), com ? com : "") ); + } +} + + +GF_Err gf_bifs_enc_sf_field(GF_BifsEncoder *codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +{ + GF_Err e; + + if (node) { + e = gf_bifs_enc_quant_field(codec, bs, node, field); + if (e != GF_EOS) return e; + } + switch (field->fieldType) { + case GF_SG_VRML_SFBOOL: + GF_BIFS_WRITE_INT(codec, bs, * ((SFBool *)field->far_ptr), 1, "SFBool", NULL); + break; + case GF_SG_VRML_SFCOLOR: + BE_WriteSFFloat(codec, ((SFColor *)field->far_ptr)->red, bs, "color.red"); + BE_WriteSFFloat(codec, ((SFColor *)field->far_ptr)->green, bs, "color.green"); + BE_WriteSFFloat(codec, ((SFColor *)field->far_ptr)->blue, bs, "color.blue"); + break; + case GF_SG_VRML_SFFLOAT: + BE_WriteSFFloat(codec, * ((SFFloat *)field->far_ptr), bs, NULL); + break; + case GF_SG_VRML_SFINT32: + GF_BIFS_WRITE_INT(codec, bs, * ((SFInt32 *)field->far_ptr), 32, "SFInt32", NULL); + break; + case GF_SG_VRML_SFROTATION: + BE_WriteSFFloat(codec, ((SFRotation *)field->far_ptr)->x, bs, "rot.x"); + BE_WriteSFFloat(codec, ((SFRotation *)field->far_ptr)->y, bs, "rot.y"); + BE_WriteSFFloat(codec, ((SFRotation *)field->far_ptr)->z, bs, "rot.z"); + BE_WriteSFFloat(codec, ((SFRotation *)field->far_ptr)->q, bs, "rot.theta"); + break; + + case GF_SG_VRML_SFSTRING: + { + u32 i; + char *str = (char *) ((SFString*)field->far_ptr)->buffer; + u32 len = str ? strlen(str) : 0; + u32 val = gf_get_bit_size(len); + GF_BIFS_WRITE_INT(codec, bs, val, 5, "nbBits", NULL); + GF_BIFS_WRITE_INT(codec, bs, len, val, "length", NULL); + for (i=0; ifar_ptr)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[BIFS] SFTime\t\t%d\t\t%g\n", 64, *((SFTime *)field->far_ptr))); + break; + + case GF_SG_VRML_SFVEC2F: + BE_WriteSFFloat(codec, ((SFVec2f *)field->far_ptr)->x, bs, "vec2f.x"); + BE_WriteSFFloat(codec, ((SFVec2f *)field->far_ptr)->y, bs, "vec2f.y"); + break; + + case GF_SG_VRML_SFVEC3F: + BE_WriteSFFloat(codec, ((SFVec3f *)field->far_ptr)->x, bs, "vec3f.x"); + BE_WriteSFFloat(codec, ((SFVec3f *)field->far_ptr)->y, bs, "vec3f.y"); + BE_WriteSFFloat(codec, ((SFVec3f *)field->far_ptr)->z, bs, "vec3f.z"); + break; + + case GF_SG_VRML_SFURL: + { + SFURL *url = (SFURL *) field->far_ptr; + GF_BIFS_WRITE_INT(codec, bs, (url->OD_ID>0) ? 1 : 0, 1, "hasODID", "SFURL"); + if (url->OD_ID>0) { + GF_BIFS_WRITE_INT(codec, bs, url->OD_ID, 10, "ODID", "SFURL"); + } else { + u32 i; + u32 len = url->url ? strlen(url->url) : 0; + u32 val = gf_get_bit_size(len); + GF_BIFS_WRITE_INT(codec, bs, val, 5, "nbBits", NULL); + GF_BIFS_WRITE_INT(codec, bs, len, val, "length", NULL); + for (i=0; iurl[i], 8); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[BIFS] string\t\t%d\t\t%s\t\t//SFURL\n", 8*len, url->url)); + } + } + break; + case GF_SG_VRML_SFIMAGE: + { + u32 size, i; + SFImage *img = (SFImage *)field->far_ptr; + GF_BIFS_WRITE_INT(codec, bs, img->width, 12, "width", "SFImage"); + GF_BIFS_WRITE_INT(codec, bs, img->height, 12, "height", "SFImage"); + GF_BIFS_WRITE_INT(codec, bs, img->numComponents - 1, 2, "nbComp", "SFImage"); + size = img->width * img->height * img->numComponents; + for (i=0; ipixels[i], 8); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[BIFS] pixels\t\t%d\t\tnot dumped\t\t//SFImage\n", 8*size)); + } + break; + + case GF_SG_VRML_SFCOMMANDBUFFER: + { + SFCommandBuffer *cb = (SFCommandBuffer *) field->far_ptr; + if (cb->buffer) free(cb->buffer); + cb->buffer = NULL; + cb->bufferSize = 0; + if (gf_list_count(cb->commandList)) { + u32 i, nbBits; + GF_BitStream *bs_cond = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[BIFS] /*SFCommandBuffer*/\n" )); + e = gf_bifs_enc_commands(codec, cb->commandList, bs_cond); + if (!e) gf_bs_get_content(bs_cond, (char**)&cb->buffer, &cb->bufferSize); + gf_bs_del(bs_cond); + if (e) return e; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[BIFS] /*End SFCommandBuffer*/\n")); + nbBits = gf_get_bit_size(cb->bufferSize); + GF_BIFS_WRITE_INT(codec, bs, nbBits, 5, "NbBits", NULL); + GF_BIFS_WRITE_INT(codec, bs, cb->bufferSize, nbBits, "BufferSize", NULL); + for (i=0; ibufferSize; i++) GF_BIFS_WRITE_INT(codec, bs, cb->buffer[i], 8, "buffer byte", NULL); + } + /*empty command buffer*/ + else { + GF_BIFS_WRITE_INT(codec, bs, 0, 5, "NbBits", NULL); + } + } + break; + + case GF_SG_VRML_SFNODE: + return gf_bifs_enc_node(codec, *((GF_Node **)field->far_ptr), field->NDTtype, bs); + + case GF_SG_VRML_SFSCRIPT: + codec->LastError = SFScript_Encode(codec, (SFScript *)field->far_ptr, bs, node); + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + return codec->LastError; +} + + +GF_Err gf_bifs_enc_mf_field(GF_BifsEncoder *codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +{ + GF_ChildNodeItem *list = NULL; + GF_Err e; + u32 nbBits; + Bool use_list; + Bool qp_local, qp_on, initial_qp; + u32 nbF, i; + GF_FieldInfo sffield; + + nbF = 0; + if (field->fieldType != GF_SG_VRML_MFNODE) { + nbF = field->far_ptr ? ((GenMFField *)field->far_ptr)->count : 0; + if (!nbF && (field->fieldType == GF_SG_VRML_MFSCRIPT)) + nbF = 1; + } else if (field->far_ptr) { + list = *((GF_ChildNodeItem **)field->far_ptr); + nbF = gf_node_list_get_count(list); + } + /*reserved*/ + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "reserved", NULL); + if (!nbF) { + /*is list*/ + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "isList", NULL); + /*end flag*/ + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "end", NULL); + return GF_OK; + } + + /*do we work in list or vector*/ + use_list = 0; + nbBits = gf_get_bit_size(nbF); + if (nbBits + 5 > nbF + 1) use_list = 1; + + GF_BIFS_WRITE_INT(codec, bs, use_list, 1, "isList", NULL); + if (!use_list) { + GF_BIFS_WRITE_INT(codec, bs, nbBits, 5, "nbBits", NULL); + GF_BIFS_WRITE_INT(codec, bs, nbF, nbBits, "length", NULL); + } + + memset(&sffield, 0, sizeof(GF_FieldInfo)); + sffield.fieldIndex = field->fieldIndex; + sffield.fieldType = gf_sg_vrml_get_sf_type(field->fieldType); + sffield.NDTtype = field->NDTtype; + + initial_qp = qp_on = qp_local = 0; + initial_qp = codec->ActiveQP ? 1 : 0; + for (i=0; ifieldType != GF_SG_VRML_MFNODE) { + gf_sg_vrml_mf_get_item(field->far_ptr, field->fieldType, &sffield.far_ptr, i); + e = gf_bifs_enc_sf_field(codec, bs, node, &sffield); + } else { + assert(list); + e = gf_bifs_enc_node(codec, list->node, field->NDTtype, bs); + + /*activate QP*/ + if (list->node->sgprivate->tag == TAG_MPEG4_QuantizationParameter) { + qp_local = ((M_QuantizationParameter *)list->node)->isLocal; + if (qp_on) gf_bifs_enc_qp_remove(codec, 0); + e = gf_bifs_enc_qp_set(codec, list->node); + if (e) return e; + qp_on = 1; + if (qp_local) qp_local = 2; + } + list = list->next; + } + + if (e) return e; + + if (qp_on && qp_local) { + if (qp_local == 2) qp_local -= 1; + else { + gf_bifs_enc_qp_remove(codec, initial_qp); + qp_local = qp_on = 0; + } + } + } + + if (use_list) GF_BIFS_WRITE_INT(codec, bs, 1, 1, "end", NULL); + if (qp_on) gf_bifs_enc_qp_remove(codec, initial_qp); + /*for QP14*/ + gf_bifs_enc_qp14_set_length(codec, nbF); + return GF_OK; +} + + +GF_Err gf_bifs_enc_field(GF_BifsEncoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +{ + assert(node); + if (field->fieldType == GF_SG_VRML_UNKNOWN) + return GF_NON_COMPLIANT_BITSTREAM; + + if (gf_sg_vrml_is_sf_field(field->fieldType)) { + return gf_bifs_enc_sf_field(codec, bs, node, field); + } + + /*TO DO : PMF support*/ + + if (codec->info->config.UsePredictiveMFField) { + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "usePredictive", NULL); + } + return gf_bifs_enc_mf_field(codec, bs, node, field); +} + +/*we assume a node field is not ISed several times (that's stated as "undefined behaviour" in VRML*/ +GF_Route *gf_bifs_enc_is_field_ised(GF_BifsEncoder *codec, GF_Node *node, u32 fieldIndex) +{ + GF_Route *r; + u32 i; + if (!codec->encoding_proto) return NULL; + + if (node->sgprivate->interact && node->sgprivate->interact->routes) { + i=0; + while ((r = (GF_Route*)gf_list_enum(node->sgprivate->interact->routes, &i))) { + if (!r->IS_route) continue; + if ((r->ToNode == node) && (r->ToField.fieldIndex==fieldIndex)) return r; + else if ((r->FromNode == node) && (r->FromField.fieldIndex==fieldIndex)) return r; + } + } + + i=0; + while ((r = (GF_Route*)gf_list_enum(codec->encoding_proto->sub_graph->Routes, &i))) { + if (!r->IS_route) continue; + if ((r->ToNode == node) && (r->ToField.fieldIndex==fieldIndex)) return r; + else if ((r->FromNode == node) && (r->FromField.fieldIndex==fieldIndex)) return r; + } + return NULL; +} + +/**/ +GF_Err EncNodeFields(GF_BifsEncoder * codec, GF_BitStream *bs, GF_Node *node) +{ + u8 mode; + GF_Route *isedField; + GF_Node *clone; + GF_Err e; + s32 *enc_fields; + u32 numBitsALL, numBitsDEF, allInd, count, i, nbBitsProto, nbFinal; + Bool use_list, nodeIsFDP = 0; + GF_FieldInfo field, clone_field; + + + e = GF_OK; + + if (codec->encoding_proto) { + mode = GF_SG_FIELD_CODING_ALL; + nbBitsProto = gf_get_bit_size(gf_sg_proto_get_field_count(codec->encoding_proto) - 1); + numBitsALL = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_ALL) - 1); + } else { + mode = GF_SG_FIELD_CODING_DEF; + nbBitsProto = 0; + numBitsALL = 0; + } + count = gf_node_get_num_fields_in_mode(node, mode); + if (node->sgprivate->tag==TAG_MPEG4_Script) count = 3; + + if (!count) { + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "isMask", NULL); + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "end", NULL); + return GF_OK; + } + + if (node->sgprivate->tag == TAG_ProtoNode) { + clone = gf_sg_proto_create_instance(node->sgprivate->scenegraph, ((GF_ProtoInstance *)node)->proto_interface);; + } else { + clone = gf_node_new(node->sgprivate->scenegraph, node->sgprivate->tag); + } + if (clone) gf_node_register(clone, NULL); + + numBitsDEF = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_DEF) - 1); + + enc_fields = (s32*)malloc(sizeof(s32) * count); + nbFinal = 0; + for (i=0; iencoding_proto) { + isedField = gf_bifs_enc_is_field_ised(codec, node, allInd); + if (isedField) { + enc_fields[i] = allInd; + nbFinal ++; + continue; + } + } + /*common case*/ + gf_node_get_field(node, allInd, &field); + /*if event don't encode (happens when encoding protos)*/ + if ((field.eventType == GF_SG_EVENT_IN) || (field.eventType == GF_SG_EVENT_OUT)) continue; + /*if field is default skip*/ + switch (field.fieldType) { + case GF_SG_VRML_SFNODE: + if (* (GF_Node **) field.far_ptr) { enc_fields[i] = allInd; nbFinal++; } + break; + case GF_SG_VRML_MFNODE: + if (* (GF_ChildNodeItem **) field.far_ptr) { enc_fields[i] = allInd; nbFinal++; } + break; + case GF_SG_VRML_SFCOMMANDBUFFER: + { + SFCommandBuffer *cb = (SFCommandBuffer *)field.far_ptr; + if (gf_list_count(cb->commandList)) { enc_fields[i] = allInd; nbFinal++; } + } + break; + case GF_SG_VRML_MFSCRIPT: + enc_fields[i] = allInd; nbFinal++; + break; + default: + gf_node_get_field(clone, allInd, &clone_field); + if (!gf_sg_vrml_field_equal(clone_field.far_ptr, field.far_ptr, field.fieldType)) { enc_fields[i] = allInd; nbFinal++; } + break; + } + } + if (clone) gf_node_unregister(clone, NULL); + + use_list = 1; + /* patch for FDP node : */ + /* cannot use default field sorting due to spec "mistake", so use list to imply inversion between field 2 and field 3 of FDP*/ + if (node->sgprivate->tag == TAG_MPEG4_FDP) { + s32 s4SwapValue = enc_fields[2]; + enc_fields[2] = enc_fields[3]; + enc_fields[3] = s4SwapValue; + nodeIsFDP = 1; + use_list = 1; + } + /*number of bits in mask node is count*1, in list node is 1+nbFinal*(1+numBitsDEF) */ + else if (count < 1+nbFinal*(1+numBitsDEF)) + use_list = 0; + + GF_BIFS_WRITE_INT(codec, bs, use_list ? 0 : 1, 1, "isMask", NULL); + + for (i=0; iencoding_proto) { + isedField = gf_bifs_enc_is_field_ised(codec, node, allInd); + if (isedField) { + if (use_list) { + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "end", NULL); + } else { + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "Mask", NULL); + } + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "isedField", NULL); + if (use_list) GF_BIFS_WRITE_INT(codec, bs, allInd, numBitsALL, "nodeField", NULL); + + if (isedField->ToNode == node) { + GF_BIFS_WRITE_INT(codec, bs, isedField->FromField.fieldIndex, nbBitsProto, "protoField", NULL); + } else { + GF_BIFS_WRITE_INT(codec, bs, isedField->ToField.fieldIndex, nbBitsProto, "protoField", NULL); + } + continue; + } + } + /*common case*/ + gf_node_get_field(node, allInd, &field); + if (use_list) { + /*not end flag*/ + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "end", NULL); + } else { + /*mask flag*/ + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "Mask", NULL); + } + /*not ISed field*/ + if (codec->encoding_proto) GF_BIFS_WRITE_INT(codec, bs, 0, 1, "isedField", NULL); + if (use_list) { + if (codec->encoding_proto || nodeIsFDP) { + u32 ind=0; + /*for proto, we're in ALL mode and we need DEF mode*/ + /*for FDP, encoding requires to get def id from all id as fields 2 and 3 are reversed*/ + gf_bifs_field_index_by_mode(node, allInd, GF_SG_FIELD_CODING_DEF, &ind); + GF_BIFS_WRITE_INT(codec, bs, ind, numBitsDEF, "field", (char*)field.name); + } else { + GF_BIFS_WRITE_INT(codec, bs, i, numBitsDEF, "field", (char*)field.name); + } + } + e = gf_bifs_enc_field(codec, bs, node, &field); + if (e) goto exit; + } + /*end flag*/ + if (use_list) GF_BIFS_WRITE_INT(codec, bs, 1, 1, "end", NULL); +exit: + free(enc_fields); + return e; +} + +Bool BE_NodeIsUSE(GF_BifsEncoder * codec, GF_Node *node) +{ + u32 i, count; + if (!node || !gf_node_get_id(node) ) return 0; + count = gf_list_count(codec->encoded_nodes); + for (i=0; iencoded_nodes, i) == node) return 1; + } + gf_list_add(codec->encoded_nodes, node); + return 0; +} + +GF_Err gf_bifs_enc_node(GF_BifsEncoder * codec, GF_Node *node, u32 NDT_Tag, GF_BitStream *bs) +{ + u32 NDTBits, node_type, node_tag, BVersion, node_id; + const char *node_name; + Bool flag, reset_qp14; + GF_Node *new_node; + GF_Err e; + + assert(codec->info); + + /*NULL node is a USE of maxID*/ + if (!node) { + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "USE", NULL); + GF_BIFS_WRITE_INT(codec, bs, (1<info->config.NodeIDBits) - 1 , codec->info->config.NodeIDBits, "NodeID", "NULL"); + return GF_OK; + } + + flag = BE_NodeIsUSE(codec, node); + GF_BIFS_WRITE_INT(codec, bs, flag ? 1 : 0, 1, "USE", (char*)gf_node_get_class_name(node)); + + if (flag) { + gf_bs_write_int(bs, gf_node_get_id(node) - 1, codec->info->config.NodeIDBits); + new_node = gf_bifs_enc_find_node(codec, gf_node_get_id(node) ); + if (!new_node) + return codec->LastError = GF_SG_UNKNOWN_NODE; + + /*restore QP14 length*/ + switch (gf_node_get_tag(new_node)) { + case TAG_MPEG4_Coordinate: + { + u32 nbCoord = ((M_Coordinate *)new_node)->point.count; + gf_bifs_enc_qp14_enter(codec, 1); + gf_bifs_enc_qp14_set_length(codec, nbCoord); + gf_bifs_enc_qp14_enter(codec, 0); + } + break; + case TAG_MPEG4_Coordinate2D: + { + u32 nbCoord = ((M_Coordinate2D *)new_node)->point.count; + gf_bifs_enc_qp14_enter(codec, 1); + gf_bifs_enc_qp14_set_length(codec, nbCoord); + gf_bifs_enc_qp14_enter(codec, 0); + } + break; + } + return GF_OK; + } + + BVersion = GF_BIFS_V1; + node_tag = node->sgprivate->tag; + while (1) { + node_type = gf_bifs_get_node_type(NDT_Tag, node_tag, BVersion); + NDTBits = gf_bifs_get_ndt_bits(NDT_Tag, BVersion); + if (BVersion==2 && (node_tag==TAG_ProtoNode)) node_type = 1; + GF_BIFS_WRITE_INT(codec, bs, node_type, NDTBits, "ndt", NULL); + if (node_type) break; + + BVersion += 1; + if (BVersion > GF_BIFS_NUM_VERSION) return codec->LastError = GF_BIFS_UNKNOWN_VERSION; + } + if (BVersion==2 && node_type==1) { + GF_Proto *proto = ((GF_ProtoInstance *)node)->proto_interface; + GF_BIFS_WRITE_INT(codec, bs, proto->ID, codec->info->config.ProtoIDBits, "protoID", NULL); + } + + /*special handling of 3D mesh*/ + + /*DEF'd node*/ + node_name = gf_node_get_name_and_id(node, &node_id); + GF_BIFS_WRITE_INT(codec, bs, node_id ? 1 : 0, 1, "DEF", NULL); + if (node_id) { + GF_BIFS_WRITE_INT(codec, bs, node_id - 1, codec->info->config.NodeIDBits, "NodeID", NULL); + if (codec->UseName) gf_bifs_enc_name(codec, bs, (char*) node_name ); + } + + /*no updates of time fields for now - NEEDED FOR A LIVE ENCODER*/ + + /*if coords were not stored for QP14 before coding this node, reset QP14 it when leaving*/ + reset_qp14 = !codec->coord_stored; + + /*QP14 case*/ + switch (node_tag) { + case TAG_MPEG4_Coordinate: + case TAG_MPEG4_Coordinate2D: + gf_bifs_enc_qp14_enter(codec, 1); + } + + e = EncNodeFields(codec, bs, node); + if (e) return e; + + if (codec->coord_stored && reset_qp14) + gf_bifs_enc_qp14_reset(codec); + + switch (node_tag) { + case TAG_MPEG4_Coordinate: + case TAG_MPEG4_Coordinate2D: + gf_bifs_enc_qp14_enter(codec, 0); + break; + } + return GF_OK; +} + + diff --git a/src/bifs/memory_decoder.c b/src/bifs/memory_decoder.c new file mode 100644 index 0000000..8bdf187 --- /dev/null +++ b/src/bifs/memory_decoder.c @@ -0,0 +1,920 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include "quant.h" + +GF_Err ParseMFFieldList(GF_BifsDecoder *codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); +GF_Err ParseMFFieldVec(GF_BifsDecoder *codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); + + +static void BM_SetCommandNode(GF_Command *com, GF_Node *node) +{ + com->node = node; + gf_node_register(node, NULL); +} + +static GF_Err BM_ParseMultipleIndexedReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u32 ID, ind, field_ind, NumBits, lenpos, lennum, count; + GF_Node *node; + GF_Err e; + GF_Command *com; + GF_CommandField *inf; + GF_FieldInfo field; + + ID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + node = gf_sg_find_node(codec->current_graph, ID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN)-1); + ind = gf_bs_read_int(bs, NumBits); + e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + e = gf_node_get_field(node, field_ind, &field); + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + + lenpos = gf_bs_read_int(bs, 5); + lennum = gf_bs_read_int(bs, 5); + count = gf_bs_read_int(bs, lennum); + + com = gf_sg_command_new(codec->current_graph, GF_SG_MULTIPLE_INDEXED_REPLACE); + BM_SetCommandNode(com, node); + field.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + + while (count) { + inf = gf_sg_command_field_new(com); + inf->pos = gf_bs_read_int(bs, lenpos); + inf->fieldIndex = field.fieldIndex; + inf->fieldType = field.fieldType; + + if (field.fieldType==GF_SG_VRML_SFNODE) { + inf->new_node = gf_bifs_dec_node(codec, bs, field.NDTtype); + if (codec->LastError) goto err; + inf->field_ptr = &inf->new_node; + gf_node_register(inf->new_node, node); + } else { + field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); + e = gf_bifs_dec_sf_field(codec, bs, node, &field); + if (e) goto err; + } + count--; + } +err: + if (e) gf_sg_command_del(com); + else gf_list_add(com_list, com); + return e; +} + +static GF_Err BM_ParseMultipleReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u32 i, numFields, index, flag, nbBits, field_ref, fieldind; + GF_Err e; + GF_FieldInfo field; + u32 NodeID; + GF_Node *node; + GF_Command *com; + GF_CommandField *inf; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + + e = GF_OK; + com = gf_sg_command_new(codec->current_graph, GF_SG_MULTIPLE_REPLACE); + BM_SetCommandNode(com, node); + flag = gf_bs_read_int(bs, 1); + if (flag) { + numFields = gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_DEF); + for (i=0; ifieldType = field.fieldType; + inf->fieldIndex = field.fieldIndex; + if (inf->fieldType==GF_SG_VRML_SFNODE) { + field.far_ptr = inf->field_ptr = &inf->new_node; + } else if (inf->fieldType==GF_SG_VRML_MFNODE) { + field.far_ptr = inf->field_ptr = &inf->node_list; + } else { + field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); + } + e = gf_bifs_dec_field(codec, bs, node, &field); + if (e) goto exit; + } + } else { + flag = gf_bs_read_int(bs, 1); + nbBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_DEF)-1); + while (!flag) { + field_ref = gf_bs_read_int(bs, nbBits); + e = gf_bifs_get_field_index(node, field_ref, GF_SG_FIELD_CODING_DEF, &fieldind); + if (e) goto exit; + e = gf_node_get_field(node, fieldind, &field); + if (e) goto exit; + inf = gf_sg_command_field_new(com); + inf->fieldType = field.fieldType; + inf->fieldIndex = field.fieldIndex; + if (inf->fieldType==GF_SG_VRML_SFNODE) { + field.far_ptr = inf->field_ptr = &inf->new_node; + } else if (inf->fieldType==GF_SG_VRML_MFNODE) { + field.far_ptr = inf->field_ptr = &inf->node_list; + } else { + field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); + } + e = gf_bifs_dec_field(codec, bs, node, &field); + if (e) goto exit; + flag = gf_bs_read_int(bs, 1); + } + } + + +exit: + if (e) gf_sg_command_del(com); + else gf_list_add(com_list, com); + return e; +} + +static GF_Err BM_ParseGlobalQuantizer(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + GF_Node *node; + GF_Command *com; + GF_CommandField *inf; + node = gf_bifs_dec_node(codec, bs, NDT_SFWorldNode); + + /*reset global QP*/ + if (codec->scenegraph->global_qp) { + gf_node_unregister(codec->scenegraph->global_qp, NULL); + } + codec->ActiveQP = NULL; + codec->scenegraph->global_qp = NULL; + + if (node && (gf_node_get_tag(node) != TAG_MPEG4_QuantizationParameter)) { + gf_node_unregister(node, NULL); + return GF_NON_COMPLIANT_BITSTREAM; + } + + /*register global QP*/ + codec->ActiveQP = (M_QuantizationParameter *) node; + codec->ActiveQP->isLocal = 0; + codec->scenegraph->global_qp = node; + if (node) { + /*register TWICE: once for the command, and for the scenegraph globalQP*/ + node->sgprivate->num_instances = 2; + } + com = gf_sg_command_new(codec->current_graph, GF_SG_GLOBAL_QUANTIZER); + inf = gf_sg_command_field_new(com); + inf->new_node = node; + inf->field_ptr = &inf->new_node; + inf->fieldType = GF_SG_VRML_SFNODE; + gf_list_add(com_list, com); + return GF_OK; +} + +static GF_Err BM_ParseProtoDelete(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u32 flag, count; + GF_Command *com = gf_sg_command_new(codec->current_graph, GF_SG_PROTO_DELETE); + flag = gf_bs_read_int(bs, 1); + if (flag) { + count = 0; + flag = gf_bs_read_int(bs, 1); + while (flag) { + com->del_proto_list = (u32*)realloc(com->del_proto_list, sizeof(u32) * (com->del_proto_list_size+1)); + com->del_proto_list[count] = gf_bs_read_int(bs, codec->info->config.ProtoIDBits); + com->del_proto_list_size++; + flag = gf_bs_read_int(bs, 1); + } + } else { + flag = gf_bs_read_int(bs, 5); + com->del_proto_list_size = gf_bs_read_int(bs, flag); + com->del_proto_list = (u32*)realloc(com->del_proto_list, sizeof(u32) * (com->del_proto_list_size)); + flag = 0; + while (flagdel_proto_list_size) { + com->del_proto_list[flag] = gf_bs_read_int(bs, codec->info->config.ProtoIDBits); + flag++; + } + } + gf_list_add(com_list, com); + return GF_OK; +} + +static GF_Err BM_ParseExtendedUpdates(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u32 type = gf_bs_read_int(bs, 8); + GF_Err e; + + switch (type) { + case 0: + { + GF_Command *com = gf_sg_command_new(codec->current_graph, GF_SG_PROTO_INSERT); + e = gf_bifs_dec_proto_list(codec, bs, com->new_proto_list); + if (e) gf_sg_command_del(com); + else gf_list_add(com_list, com); + } + return e; + case 1: + return BM_ParseProtoDelete(codec, bs, com_list); + case 2: + { + GF_Command *com = gf_sg_command_new(codec->current_graph, GF_SG_PROTO_DELETE_ALL); + return gf_list_add(com_list, com); + } + case 3: + return BM_ParseMultipleIndexedReplace(codec, bs, com_list); + case 4: + return BM_ParseMultipleReplace(codec, bs, com_list); + case 5: + return BM_ParseGlobalQuantizer(codec, bs, com_list); + case 6: + { + GF_Command *com; + u32 ID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + GF_Node *n = gf_sg_find_node(codec->current_graph, ID); + if (!n) return GF_OK; + com = gf_sg_command_new(codec->current_graph, GF_SG_NODE_DELETE_EX); + BM_SetCommandNode(com, n); + gf_list_add(com_list, com); + } + return GF_OK; + default: + return GF_BIFS_UNKNOWN_VERSION; + } +} + +/*inserts a node in a container (node.children)*/ +GF_Err BM_ParseNodeInsert(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u32 NodeID, NDT; + GF_Command *com; + GF_CommandField *inf; + s32 type, pos; + GF_Node *node, *def; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + def = gf_sg_find_node(codec->current_graph, NodeID); + if (!def) return GF_NON_COMPLIANT_BITSTREAM; + NDT = gf_bifs_get_child_table(def); + if (!NDT) return GF_NON_COMPLIANT_BITSTREAM; + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + pos = gf_bs_read_int(bs, 8); + break; + case 2: + pos = 0; + break; + case 3: + /*-1 means append*/ + pos = -1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + node = gf_bifs_dec_node(codec, bs, NDT); + if (!codec->LastError) { + com = gf_sg_command_new(codec->current_graph, GF_SG_NODE_INSERT); + BM_SetCommandNode(com, def); + inf = gf_sg_command_field_new(com); + inf->pos = pos; + inf->new_node = node; + inf->field_ptr = &inf->new_node; + inf->fieldType = GF_SG_VRML_SFNODE; + gf_list_add(com_list, com); + /*register*/ + gf_node_register(node, def); + } + return codec->LastError; +} + +/*NB This can insert a node as well (but usually not in the .children field)*/ +GF_Err BM_ParseIndexInsert(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + GF_Err e; + u32 NodeID; + u32 NumBits, ind, field_ind; + u8 type; + GF_Command *com; + GF_CommandField *inf; + s32 pos; + GF_Node *def, *node; + GF_FieldInfo field, sffield; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + def = gf_sg_find_node(codec->current_graph, NodeID); + if (!def) return GF_NON_COMPLIANT_BITSTREAM; + /*index insertion uses IN mode for field index*/ + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(def, GF_SG_FIELD_CODING_IN)-1); + ind = gf_bs_read_int(bs, NumBits); + + e = gf_bifs_get_field_index(def, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + pos = gf_bs_read_int(bs, 16); + break; + case 2: + pos = 0; + break; + case 3: + pos = -1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + + e = gf_node_get_field(def, field_ind, &field); + if (e) return e; + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + + memcpy(&sffield, &field, sizeof(GF_FieldInfo)); + sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + + /*rescale the MFField and parse the SFField*/ + if (field.fieldType==GF_SG_VRML_MFNODE) { + node = gf_bifs_dec_node(codec, bs, field.NDTtype); + if (!codec->LastError) { + com = gf_sg_command_new(codec->current_graph, GF_SG_INDEXED_INSERT); + BM_SetCommandNode(com, def); + inf = gf_sg_command_field_new(com); + inf->pos = pos; + inf->fieldIndex = field_ind; + inf->fieldType = sffield.fieldType; + inf->new_node = node; + inf->field_ptr = &inf->new_node; + gf_list_add(com_list, com); + /*register*/ + gf_node_register(node, def); + } + } else { + com = gf_sg_command_new(codec->current_graph, GF_SG_INDEXED_INSERT); + BM_SetCommandNode(com, def); + inf = gf_sg_command_field_new(com); + inf->pos = pos; + inf->fieldIndex = field_ind; + inf->fieldType = sffield.fieldType; + sffield.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(sffield.fieldType); + codec->LastError = gf_bifs_dec_sf_field(codec, bs, def, &sffield); + gf_list_add(com_list, com); + } + return codec->LastError; +} + + +GF_Err BM_ParseRouteInsert(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + GF_Err e; + u8 flag; + GF_Command *com; + GF_Node *InNode, *OutNode; + u32 RouteID, outField, inField, numBits, ind, node_id; + char name[1000]; + + RouteID = 0; + + flag = gf_bs_read_int(bs, 1); + /*def'ed route*/ + if (flag) { + RouteID = 1 + gf_bs_read_int(bs, codec->info->config.RouteIDBits); + if (codec->UseName) gf_bifs_dec_name(bs, name); + } + /*origin*/ + node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + OutNode = gf_sg_find_node(codec->current_graph, node_id); + if (!OutNode) return GF_SG_UNKNOWN_NODE; + + numBits = gf_node_get_num_fields_in_mode(OutNode, GF_SG_FIELD_CODING_OUT) - 1; + numBits = gf_get_bit_size(numBits); + ind = gf_bs_read_int(bs, numBits); + e = gf_bifs_get_field_index(OutNode, ind, GF_SG_FIELD_CODING_OUT, &outField); + + /*target*/ + node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + InNode = gf_sg_find_node(codec->current_graph, node_id); + if (!InNode) return GF_SG_UNKNOWN_NODE; + + numBits = gf_node_get_num_fields_in_mode(InNode, GF_SG_FIELD_CODING_IN) - 1; + numBits = gf_get_bit_size(numBits); + ind = gf_bs_read_int(bs, numBits); + e = gf_bifs_get_field_index(InNode, ind, GF_SG_FIELD_CODING_IN, &inField); + if (e) return e; + + com = gf_sg_command_new(codec->current_graph, GF_SG_ROUTE_INSERT); + com->RouteID = RouteID; + if (codec->UseName) com->def_name = strdup( name); + com->fromNodeID = gf_node_get_id(OutNode); + com->fromFieldIndex = outField; + com->toNodeID = gf_node_get_id(InNode); + com->toFieldIndex = inField; + gf_list_add(com_list, com); + return codec->LastError; +} + + +GF_Err BM_ParseInsert(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u8 type; + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + return BM_ParseNodeInsert(codec, bs, com_list); + case 1: + return BM_ParseExtendedUpdates(codec, bs, com_list); + case 2: + return BM_ParseIndexInsert(codec, bs, com_list); + case 3: + return BM_ParseRouteInsert(codec, bs, com_list); + default: + return GF_NON_COMPLIANT_BITSTREAM; + } +} + + +GF_Err BM_ParseIndexDelete(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u32 NodeID, NumBits, ind, field_ind; + s32 pos; + GF_Command *com; + u8 type; + GF_Node *node; + GF_Err e; + GF_CommandField *inf; + GF_FieldInfo field; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN) - 1); + ind = gf_bs_read_int(bs, NumBits); + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + pos = (u32) gf_bs_read_int(bs, 16); + break; + case 2: + pos = 0; + break; + case 3: + pos = -1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + e = gf_node_get_field(node, field_ind, &field); + if (e) return e; + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + com = gf_sg_command_new(codec->current_graph, GF_SG_INDEXED_DELETE); + BM_SetCommandNode(com, node); + inf = gf_sg_command_field_new(com); + inf->pos = pos; + inf->fieldIndex = field.fieldIndex; + inf->fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + gf_list_add(com_list, com); + return codec->LastError; +} + + + +GF_Err BM_ParseDelete(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u8 type; + u32 ID; + GF_Command *com; + GF_Node *n; + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + ID = 1+gf_bs_read_int(bs, codec->info->config.NodeIDBits); + n = gf_sg_find_node(codec->current_graph, ID); + if (!n) return GF_OK; + com = gf_sg_command_new(codec->current_graph, GF_SG_NODE_DELETE); + BM_SetCommandNode(com, n); + gf_list_add(com_list, com); + return GF_OK; + case 2: + return BM_ParseIndexDelete(codec, bs, com_list); + case 3: + com = gf_sg_command_new(codec->current_graph, GF_SG_ROUTE_DELETE); + com->RouteID = 1+gf_bs_read_int(bs, codec->info->config.RouteIDBits); + gf_list_add(com_list, com); + return GF_OK; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + return GF_OK; +} + + +GF_Err BM_ParseNodeReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u32 NodeID; + GF_Command *com; + GF_Node *node; + GF_CommandField *inf; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + /*this is delete / new on a DEF node: replace ALL instances*/ + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + + com = gf_sg_command_new(codec->current_graph, GF_SG_NODE_REPLACE); + BM_SetCommandNode(com, node); + inf = gf_sg_command_field_new(com); + inf->new_node = gf_bifs_dec_node(codec, bs, NDT_SFWorldNode); + inf->fieldType = GF_SG_VRML_SFNODE; + inf->field_ptr = &inf->new_node; + gf_list_add(com_list, com); + gf_node_register(inf->new_node, NULL); + return codec->LastError; +} + +GF_Err BM_ParseFieldReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + GF_Err e; + GF_Command *com; + u32 NodeID, ind, field_ind, NumBits; + GF_Node *node; + GF_FieldInfo field; + GF_CommandField *inf; + + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN)-1); + ind = gf_bs_read_int(bs, NumBits); + e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + + e = gf_node_get_field(node, field_ind, &field); + + com = gf_sg_command_new(codec->current_graph, GF_SG_FIELD_REPLACE); + BM_SetCommandNode(com, node); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = field_ind; + inf->fieldType = field.fieldType; + if (inf->fieldType == GF_SG_VRML_SFNODE) { + field.far_ptr = inf->field_ptr = &inf->new_node; + } else if (inf->fieldType == GF_SG_VRML_MFNODE) { + field.far_ptr = inf->field_ptr = &inf->node_list; + } else { + field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(field.fieldType); + } + /*parse the field*/ + codec->LastError = gf_bifs_dec_field(codec, bs, node, &field); + + /*register nodes*/ +#if 0 + if (inf->fieldType == GF_SG_VRML_SFNODE) { + gf_node_register(inf->new_node, com->node); + } else if (inf->fieldType == GF_SG_VRML_MFNODE) { + GF_Node *p; + u32 i=0; + while ((p = gf_list_enum(inf->node_list, &i))) { + gf_node_register(p, com->node); + } + } +#endif + gf_list_add(com_list, com); + return codec->LastError; +} + +GF_Err BM_ParseIndexValueReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u32 NodeID, ind, field_ind, NumBits; + s32 type, pos; + GF_Command *com; + GF_Node *node; + GF_Err e; + GF_FieldInfo field, sffield; + GF_CommandField *inf; + + /*get the node*/ + NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + + node = gf_sg_find_node(codec->current_graph, NodeID); + if (!node) return GF_NON_COMPLIANT_BITSTREAM; + NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN)-1); + ind = gf_bs_read_int(bs, NumBits); + e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind); + if (e) return e; + + e = gf_node_get_field(node, field_ind, &field); + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + pos = gf_bs_read_int(bs, 16); + break; + case 2: + pos = 0; + break; + case 3: + pos = ((GenMFField *) field.far_ptr)->count - 1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + + com = gf_sg_command_new(codec->current_graph, GF_SG_INDEXED_REPLACE); + BM_SetCommandNode(com, node); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = field.fieldIndex; + inf->pos = pos; + + if (field.fieldType == GF_SG_VRML_MFNODE) { + inf->fieldType = GF_SG_VRML_SFNODE; + inf->new_node = gf_bifs_dec_node(codec, bs, field.NDTtype); + inf->field_ptr = &inf->new_node; + if (inf->new_node) gf_node_register(inf->new_node, com->node); + } else { + memcpy(&sffield, &field, sizeof(GF_FieldInfo)); + sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + inf->fieldType = sffield.fieldType; + sffield.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(sffield.fieldType); + codec->LastError = gf_bifs_dec_sf_field(codec, bs, node, &sffield); + } + gf_list_add(com_list, com); + return codec->LastError; +} + +GF_Err BM_ParseRouteReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + GF_Err e; + GF_Command *com; + u32 RouteID, numBits, ind, node_id, fromID, toID; + GF_Route *r; + GF_Node *OutNode, *InNode; + + RouteID = 1+gf_bs_read_int(bs, codec->info->config.RouteIDBits); + + r = gf_sg_route_find(codec->current_graph, RouteID); + + /*origin*/ + node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + OutNode = gf_sg_find_node(codec->current_graph, node_id); + if (!OutNode) return GF_NON_COMPLIANT_BITSTREAM; + numBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(OutNode, GF_SG_FIELD_CODING_OUT) - 1); + ind = gf_bs_read_int(bs, numBits); + e = gf_bifs_get_field_index(OutNode, ind, GF_SG_FIELD_CODING_OUT, &fromID); + if (e) return e; + + /*target*/ + node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); + InNode = gf_sg_find_node(codec->current_graph, node_id); + if (!InNode) return GF_NON_COMPLIANT_BITSTREAM; + numBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(InNode, GF_SG_FIELD_CODING_IN) - 1); + ind = gf_bs_read_int(bs, numBits); + e = gf_bifs_get_field_index(InNode, ind, GF_SG_FIELD_CODING_IN, &toID); + if (e) return e; + + com = gf_sg_command_new(codec->current_graph, GF_SG_ROUTE_REPLACE); + com->RouteID = RouteID; + com->fromNodeID = gf_node_get_id(OutNode); + com->fromFieldIndex = fromID; + com->toNodeID = gf_node_get_id(InNode); + com->toFieldIndex = toID; + gf_list_add(com_list, com); + return codec->LastError; +} + + +GF_Err BM_ParseReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u8 type; + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + return BM_ParseNodeReplace(codec, bs, com_list); + case 1: + return BM_ParseFieldReplace(codec, bs, com_list); + case 2: + return BM_ParseIndexValueReplace(codec, bs, com_list); + case 3: + return BM_ParseRouteReplace(codec, bs, com_list); + } + return GF_OK; +} + +GF_Err BM_SceneReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + GF_Command *com; + GF_Node *backup_root; + GF_List *backup_routes; + GF_Err BD_DecSceneReplace(GF_BifsDecoder * codec, GF_BitStream *bs, GF_List *proto_list); + + backup_routes = codec->scenegraph->Routes; + backup_root = codec->scenegraph->RootNode; + com = gf_sg_command_new(codec->current_graph, GF_SG_SCENE_REPLACE); + codec->scenegraph->Routes = gf_list_new(); + codec->current_graph = codec->scenegraph; + codec->LastError = BD_DecSceneReplace(codec, bs, com->new_proto_list); + com->use_names = codec->UseName; + + /*restore*/ + com->node = codec->scenegraph->RootNode; + codec->scenegraph->RootNode = backup_root; + gf_list_add(com_list, com); + /*insert routes*/ + while (gf_list_count(codec->scenegraph->Routes)) { + GF_Route *r = (GF_Route*)gf_list_get(codec->scenegraph->Routes, 0); + GF_Command *ri = gf_sg_command_new(codec->current_graph, GF_SG_ROUTE_INSERT); + gf_list_rem(codec->scenegraph->Routes, 0); + ri->fromFieldIndex = r->FromField.fieldIndex; + ri->fromNodeID = gf_node_get_id(r->FromNode); + ri->toFieldIndex = r->ToField.fieldIndex; + ri->toNodeID = gf_node_get_id(r->ToNode); + if (r->ID) ri->RouteID = r->ID; + ri->def_name = r->name ? strdup(r->name) : NULL; + gf_list_add(com_list, ri); + gf_sg_route_del(r); + } + gf_list_del(codec->scenegraph->Routes); + codec->scenegraph->Routes = backup_routes; + return codec->LastError; +} + + +GF_Err BM_ParseCommand(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list) +{ + u8 go, type; + u32 count; + GF_Err e; + go = 1; + e = GF_OK; + + codec->LastError = GF_OK; + count = 0; + + while (go) { + type = gf_bs_read_int(bs, 2); + switch (type) { + case 0: + e = BM_ParseInsert(codec, bs, com_list); + break; + case 1: + e = BM_ParseDelete(codec, bs, com_list); + break; + case 2: + e = BM_ParseReplace(codec, bs, com_list); + break; + case 3: + e = BM_SceneReplace(codec, bs, com_list); + break; + } + if (e) return e; + + go = gf_bs_read_int(bs, 1); + count++; + + } + while (gf_list_count(codec->QPs)) { + gf_bifs_dec_qp_remove(codec, 1); + } + return GF_OK; +} + +void BM_EndOfStream(void *co) +{ + ((GF_BifsDecoder *) co)->LastError = GF_IO_ERR; +} + +void gf_bs_set_eos_callback(GF_BitStream *bs, void (*EndOfStream)(void *par), void *par); + + +GF_Err gf_bifs_flush_command_list(GF_BifsDecoder *codec) +{ + GF_BitStream *bs; + GF_Err e; + CommandBufferItem *cbi; + u32 NbPass = gf_list_count(codec->command_buffers); + GF_List *nextPass = gf_list_new(); + while (NbPass) { + while (gf_list_count(codec->command_buffers)) { + cbi = (CommandBufferItem *)gf_list_get(codec->command_buffers, 0); + gf_list_rem(codec->command_buffers, 0); + codec->current_graph = gf_node_get_graph(cbi->node); + e = GF_OK; + if (cbi->cb->bufferSize) { + bs = gf_bs_new((char*)cbi->cb->buffer, cbi->cb->bufferSize, GF_BITSTREAM_READ); + gf_bs_set_eos_callback(bs, BM_EndOfStream, codec); + e = BM_ParseCommand(codec, bs, cbi->cb->commandList); + gf_bs_del(bs); + } + if (!e) { + free(cbi); + continue; + } + /*this may be an error or a dependency pb - reset coimmand list and move to next pass*/ + while (gf_list_count(cbi->cb->commandList)) { + u32 i; + GF_CommandField *cf; + GF_Command *com = (GF_Command *)gf_list_get(cbi->cb->commandList, 0); + gf_list_rem(cbi->cb->commandList, 0); + cf = (GF_CommandField *) gf_list_get(com->command_fields, 0); + if (cf && cf->fieldType==GF_SG_VRML_SFCOMMANDBUFFER) { + for (i=0; icommand_buffers); i++) { + CommandBufferItem *cbi2 = (CommandBufferItem *)gf_list_get(codec->command_buffers, i); + if (cbi2->cb == cf->field_ptr) { + free(cbi2); + gf_list_rem(codec->command_buffers, i); + i--; + } + } + } + gf_sg_command_del(com); + } + gf_list_add(nextPass, cbi); + } + if (!gf_list_count(nextPass)) break; + /*prepare next pass*/ + while (gf_list_count(nextPass)) { + cbi = (CommandBufferItem *)gf_list_get(nextPass, 0); + gf_list_rem(nextPass, 0); + gf_list_add(codec->command_buffers, cbi); + } + NbPass --; + if (NbPass > gf_list_count(codec->command_buffers)) NbPass = gf_list_count(codec->command_buffers); + codec->LastError = GF_OK; + } + gf_list_del(nextPass); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_bifs_decode_command_list(GF_BifsDecoder *codec, u16 ESID, char *data, u32 data_length, GF_List *com_list) +{ + GF_BitStream *bs; + GF_Err e; + + if (!codec || !data || !codec->dec_memory_mode || !com_list) return GF_BAD_PARAM; + + codec->info = gf_bifs_dec_get_stream(codec, ESID); + if (!codec->info) return GF_BAD_PARAM; + if (codec->info->config.elementaryMasks ) return GF_NOT_SUPPORTED; + + /*root parse (not conditionals)*/ + assert(codec->scenegraph); + /*setup current scene graph*/ + codec->current_graph = codec->scenegraph; + + codec->ActiveQP = (M_QuantizationParameter*) codec->scenegraph->global_qp; + + bs = gf_bs_new(data, data_length, GF_BITSTREAM_READ); + gf_bs_set_eos_callback(bs, BM_EndOfStream, codec); + + e = BM_ParseCommand(codec, bs, com_list); + gf_bs_del(bs); + + /*decode conditionals / input sensors*/ + if (!e) { + gf_bifs_flush_command_list(codec); + } + /*if err or not reset conditionals*/ + while (gf_list_count(codec->command_buffers)) { + CommandBufferItem *cbi = (CommandBufferItem *)gf_list_get(codec->command_buffers, 0); + free(cbi); + gf_list_rem(codec->command_buffers, 0); + } + + /*reset current config*/ + codec->info = NULL; + codec->current_graph = NULL; + + + +// gf_mx_v(codec->mx); + return e; +} diff --git a/src/bifs/predictive_mffield.c b/src/bifs/predictive_mffield.c new file mode 100644 index 0000000..f4e82f6 --- /dev/null +++ b/src/bifs/predictive_mffield.c @@ -0,0 +1,448 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "quant.h" + + +u32 gf_bifs_dec_qp14_get_bits(GF_BifsDecoder *codec); + +typedef struct +{ + s32 comp_min[3]; + s32 previous_val[3]; + s32 current_val[3]; + s32 m_delta[3]; + + u32 intra_mode, intra_inter, compNbBits, num_bounds, num_comp, num_fields, QNbBits; + u8 QType; + Bool use_default; + SFVec3f BMin, BMax; + + s32 direction, orientation, inverse; + + u32 cur_field; + + GF_AAModel *models[3]; + GF_AAModel *dir_model; + GF_AADecoder *dec; +} PredMF; + + +void PMF_ResetModels(PredMF *pmf) +{ + u32 i; + for (i=0; inum_bounds; i++) { + gp_bifs_aa_model_init(pmf->models[i], pmf->compNbBits); + } + gp_bifs_aa_model_init(pmf->dir_model, 1); +} + + +Fixed PMF_UnquantizeFloat(s32 vq, Fixed BMin, Fixed BMax, u32 NbBits, Bool unit_vector) +{ + Fixed scale = 0; + Fixed width = BMax - BMin; + if (unit_vector) NbBits -= 1; + if (width > FIX_EPSILON) { + if (NbBits) { + scale = gf_divfix(width , INT2FIX( (1<current_val[i] - (1<< (pmf->QNbBits -1) ), 0 , FIX_ONE, pmf->QNbBits, 1); + tang[i]= gf_tan(gf_mulfix(GF_PI * 4, v)); + delta += gf_mulfix(tang[i], tang[i]); + } + delta = gf_divfix(pmf->direction, gf_sqrt(delta) ); + + comp[(pmf->orientation) % 3] = delta; + for (i=0; i<2; i++) + comp[(pmf->orientation + i+1)%3] = gf_mulfix(tang[i], delta); + + gf_sg_vrml_mf_get_item(field->far_ptr, field->fieldType, &slot, pmf->cur_field); + ((SFVec3f *)slot)->x = comp[0]; + ((SFVec3f *)slot)->y = comp[1]; + ((SFVec3f *)slot)->z = comp[2]; + return GF_OK; +} + +GF_Err PMF_UnquantizeRotation(PredMF *pmf, GF_FieldInfo *field) +{ + u32 i; + void *slot; + Fixed comp[4]; + Fixed tang[3]; + Fixed sine, delta = FIX_ONE; + + for (i=0; i<3; i++) { + Fixed v = PMF_UnquantizeFloat(pmf->current_val[i] - (1<<(pmf->QNbBits - 1)), 0, FIX_ONE, pmf->QNbBits, 1); + tang[i] = gf_tan(gf_mulfix(GF_PI / 4, v)); + delta += gf_mulfix(tang[i], tang[i]); + } + delta = gf_divfix(pmf->direction , gf_sqrt(delta) ); + + comp[(pmf->orientation)%4] = delta; + for (i=0; i<3; i++) + comp[(pmf->orientation + i+1)%4] = gf_mulfix(tang[i], delta); + + gf_sg_vrml_mf_get_item(field->far_ptr, field->fieldType, &slot, pmf->cur_field); + delta = 2 * gf_acos(comp[0]); + sine = gf_sin(delta / 2); + if (sine != 0) { + for(i=1; i<4; i++) + comp[i] = gf_divfix(comp[i], sine); + + ((SFRotation *)slot)->x = comp[1]; + ((SFRotation *)slot)->y = comp[2]; + ((SFRotation *)slot)->z = comp[3]; + } else { + ((SFRotation *)slot)->x = FIX_ONE; + ((SFRotation *)slot)->y = 0; + ((SFRotation *)slot)->z = 0; + } + ((SFRotation *)slot)->q = delta; + return GF_OK; +} + +GF_Err PMF_Unquantize(PredMF *pmf, GF_FieldInfo *field) +{ + void *slot; + if (pmf->QType == QC_NORMALS) { + return PMF_UnquantizeNormal(pmf, field); + } + if (pmf->QType == QC_ROTATION) { + return PMF_UnquantizeRotation(pmf, field); + } + /*regular*/ + gf_sg_vrml_mf_get_item(field->far_ptr, field->fieldType, &slot, pmf->cur_field); + switch (field->fieldType) { + case GF_SG_VRML_MFVEC3F: + ((SFVec3f *) slot)->x = PMF_UnquantizeFloat(pmf->current_val[0], pmf->BMin.x, pmf->BMax.x, pmf->QNbBits, 0); + ((SFVec3f *) slot)->y = PMF_UnquantizeFloat(pmf->current_val[1], pmf->BMin.y, pmf->BMax.y, pmf->QNbBits, 0); + ((SFVec3f *) slot)->z = PMF_UnquantizeFloat(pmf->current_val[2], pmf->BMin.z, pmf->BMax.z, pmf->QNbBits, 0); + break; + case GF_SG_VRML_MFVEC2F: + ((SFVec2f *) slot)->x = PMF_UnquantizeFloat(pmf->current_val[0], pmf->BMin.x, pmf->BMax.x, pmf->QNbBits, 0); + ((SFVec2f *) slot)->y = PMF_UnquantizeFloat(pmf->current_val[1], pmf->BMin.y, pmf->BMax.y, pmf->QNbBits, 0); + break; + case GF_SG_VRML_MFFLOAT: + *((SFFloat *) slot) = PMF_UnquantizeFloat(pmf->current_val[0], pmf->BMin.x, pmf->BMax.x, pmf->QNbBits, 0); + break; + case GF_SG_VRML_MFCOLOR: + ((SFColor *) slot)->red = PMF_UnquantizeFloat(pmf->current_val[0], pmf->BMin.x, pmf->BMax.x, pmf->QNbBits, 0); + ((SFColor *) slot)->green = PMF_UnquantizeFloat(pmf->current_val[1], pmf->BMin.y, pmf->BMax.y, pmf->QNbBits, 0); + ((SFColor *) slot)->blue = PMF_UnquantizeFloat(pmf->current_val[2], pmf->BMin.z, pmf->BMax.z, pmf->QNbBits, 0); + break; + case GF_SG_VRML_MFINT32: + switch (pmf->QType) { + case QC_LINEAR_SCALAR: + case QC_COORD_INDEX: + *((SFInt32 *) slot) = pmf->current_val[0] + (s32) pmf->BMin.x; + break; + } + break; + } + return GF_OK; +} + + +GF_Err PMF_ParsePValue(PredMF *pmf, GF_BitStream *bs, GF_FieldInfo *field) +{ + u32 i, numModel; + s32 prev_dir = 0; + switch (pmf->QType) { + case QC_NORMALS: + prev_dir = pmf->direction; + pmf->direction = gp_bifs_aa_decode(pmf->dec, pmf->dir_model); + break; + } + /*decode (one model per component)*/ + numModel = 0; + for (i=0; inum_comp; i++) { + pmf->previous_val[i]= pmf->current_val[i]; + pmf->current_val[i] = gp_bifs_aa_decode(pmf->dec, pmf->models[numModel]) + pmf->comp_min[numModel]; + numModel += (pmf->num_bounds==1) ? 0 : 1; + } + + /*compensate values*/ + switch (pmf->QType) { + case QC_NORMALS: + case QC_ROTATION: + /*NOT TESTED*/ + { + s32 temp_val[3]; + s32 diff_dir = prev_dir * (pmf->direction ? -1 : 1); + s32 inv=1; + s32 diff_ori = 0; + s32 shift = 1 << (pmf->QNbBits - 1); + + for (i=0; i<3; i++) { + pmf->previous_val[i] -= shift; + pmf->current_val[i] -= shift; + } + for (i=0; i< pmf->num_comp; i++) { + temp_val[i] = pmf->previous_val[i] + pmf->current_val[i]; + if ( abs(temp_val[i]) > shift - 1) { + diff_ori = i+1; + inv = ( temp_val[i] > 0) ? 1 : -1; + break; + } + } + if (diff_ori != 0) { + s32 k=0; + for (i=0; i< pmf->num_comp - diff_ori; i++) { + k = (i + diff_ori) % pmf->num_comp; + temp_val[i] = inv * ( pmf->previous_val[i] + pmf->current_val[i]); + } + k = diff_ori - 1; + temp_val[pmf->num_comp - diff_ori] = inv * 2 * (shift - 1) - (pmf->previous_val[k] + pmf->current_val[k]) ; + for (i = pmf->num_comp - diff_ori + 1; inum_comp; i++) { + k = (i+diff_ori-1) % pmf->num_comp; + temp_val[i] = inv * (pmf->previous_val[k] + pmf->current_val[k]); + } + } + pmf->orientation = (pmf->orientation + diff_ori) % (pmf->num_comp + 1); + pmf->direction = diff_dir * inv; + for (i=0; i< pmf->num_comp; i++) + pmf->current_val[i]= temp_val[i] + shift; + } + break; + default: + for (i=0; i< pmf->num_comp; i++) + pmf->current_val[i] += pmf->previous_val[i]; + } + /*unquantize*/ + return PMF_Unquantize(pmf, field); +} + +GF_Err PMF_ParseIValue(PredMF *pmf, GF_BitStream *bs, GF_FieldInfo *field) +{ + u32 i; + switch (pmf->QType) { + case QC_NORMALS: + i = gf_bs_read_int(bs, 1); + pmf->direction = i ? -1 : 1; + case QC_ROTATION: + pmf->orientation = gf_bs_read_int(bs, 2); + break; + } + /*read all vals*/ + for (i=0; inum_comp; i++) { + pmf->current_val[i] = gf_bs_read_int(bs, pmf->QNbBits); + } + /*reset after each I*/ + if (pmf->cur_field + 1num_fields) gp_bifs_aa_dec_reset(pmf->dec); + + return PMF_Unquantize(pmf, field); +} + +/*bit access shall be done through the AA decoder since bits may be cached there*/ +GF_Err PMF_UpdateArrayQP(PredMF *pmf, GF_BitStream *bs) +{ + u32 flag, i; + switch (pmf->intra_mode) { + case 1: + flag = gf_bs_read_int(bs, 5); + pmf->intra_inter = gf_bs_read_int(bs, flag); + case 2: + case 0: + flag = gf_bs_read_int(bs, 1); + if (flag) { + pmf->compNbBits = gf_bs_read_int(bs, 5); + } + flag = gf_bs_read_int(bs, 1); + if (flag) { + for (i=0; inum_bounds; i++) { + flag = gf_bs_read_int(bs, pmf->QNbBits + 1); + pmf->comp_min[i] = flag - (1<QNbBits); + } + } + break; + } + /*reset all models when new settings are received*/ + PMF_ResetModels(pmf); + return GF_OK; +} + + + +GF_Err gf_bifs_dec_pred_mf_field(GF_BifsDecoder *codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +{ + GF_Err e; + Bool HasQ; + u8 AType; + Fixed b_min, b_max; + u32 i, flag; + PredMF pmf; + + memset(&pmf, 0, sizeof(PredMF)); + + HasQ = gf_bifs_get_aq_info(node, field->fieldIndex, &pmf.QType, &AType, &b_min, &b_max, &pmf.QNbBits); + if (!HasQ || !pmf.QType) return GF_EOS; + + /*get NbBits for QP14 (QC_COORD_INDEX)*/ + if (pmf.QType == QC_COORD_INDEX) + pmf.QNbBits = gf_bifs_dec_qp14_get_bits(codec); + + pmf.BMin.x = pmf.BMin.y = pmf.BMin.z = b_min; + pmf.BMax.x = pmf.BMax.y = pmf.BMax.z = b_max; + + /*check is the QP is on and retrieves the bounds*/ + if (!Q_IsTypeOn(codec->ActiveQP, pmf.QType, &pmf.QNbBits, &pmf.BMin, &pmf.BMax)) return GF_EOS; + + switch (field->fieldType) { + case GF_SG_VRML_MFCOLOR: + case GF_SG_VRML_MFVEC3F: + if (pmf.QType==QC_NORMALS) { + pmf.num_comp = 2; + break; + } + case GF_SG_VRML_MFROTATION: + pmf.num_comp = 3; + break; + case GF_SG_VRML_MFVEC2F: + pmf.num_comp = 2; + break; + case GF_SG_VRML_MFFLOAT: + case GF_SG_VRML_MFINT32: + pmf.num_comp = 1; + break; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } + + + /*parse array header*/ + flag = gf_bs_read_int(bs, 5); + pmf.num_fields = gf_bs_read_int(bs, flag); + pmf.intra_mode = gf_bs_read_int(bs, 2); + switch (pmf.intra_mode) { + case 1: + flag = gf_bs_read_int(bs, 5); + pmf.intra_inter = gf_bs_read_int(bs, flag); + /*no break*/ + case 2: + case 0: + pmf.compNbBits = gf_bs_read_int(bs, 5); + if (pmf.QType==1) pmf.num_bounds = 3; + else if (pmf.QType==2) pmf.num_bounds = 2; + else pmf.num_bounds = 1; + for (i=0; ifar_ptr, field->fieldType, pmf.num_fields); + pmf.cur_field = 0; + /*parse initial I*/ + e = PMF_ParseIValue(&pmf, bs, field); + if (e) return e; + + for (pmf.cur_field=1; pmf.cur_field + +//#define QP_PI 3.1415926535897932384626433832795 + +/*Quantization Categories*/ +enum +{ + QC_NONE = 0, + QC_3DPOS = 1, + QC_2DPOS = 2, + QC_ORDER = 3, + QC_COLOR = 4, + QC_TEXTURE_COORD = 5, + QC_ANGLE = 6, + QC_SCALE = 7, + QC_INTERPOL_KEYS = 8, + QC_NORMALS = 9, + QC_ROTATION = 10, + QC_SIZE_3D = 11, + QC_SIZE_2D = 12, + QC_LINEAR_SCALAR = 13, + QC_COORD_INDEX = 14, + QC_RESERVED = 15, + QC_NOTDEF = 16, +}; + +GF_Err gf_bifs_dec_pred_mf_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); + +Bool Q_IsTypeOn(M_QuantizationParameter *qp, u32 q_type, u32 *NbBits, SFVec3f *b_min, SFVec3f *b_max); + + +/*QP14 decode*/ +u32 gf_bifs_dec_qp14_get_bits(GF_BifsDecoder *codec); +void gf_bifs_dec_qp14_enter(GF_BifsDecoder * codec, Bool Enter); +void gf_bifs_dec_qp14_reset(GF_BifsDecoder * codec); +void gf_bifs_dec_qp14_set_length(GF_BifsDecoder * codec, u32 NbElements); +/*QP decoder (un)registration*/ +GF_Err gf_bifs_dec_qp_set(GF_BifsDecoder *codec, GF_Node *qp); +GF_Err gf_bifs_dec_qp_remove(GF_BifsDecoder *codec, Bool ActivatePrev); + +SFFloat gf_bifs_dec_mantissa_float(GF_BifsDecoder * codec, GF_BitStream *bs); +GF_Err gf_bifs_dec_unquant_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); + +/*QP14 encode*/ +u32 gf_bifs_enc_qp14_get_bits(GF_BifsEncoder *codec); +void gf_bifs_enc_qp14_enter(GF_BifsEncoder *codec, Bool Enter); +void gf_bifs_enc_qp14_reset(GF_BifsEncoder *codec); +void gf_bifs_enc_qp14_set_length(GF_BifsEncoder *codec, u32 NbElements); +/*QP encoder (un)registration*/ +GF_Err gf_bifs_enc_qp_set(GF_BifsEncoder *codec, GF_Node *qp); +GF_Err gf_bifs_enc_qp_remove(GF_BifsEncoder *codec, Bool ActivatePrev); +void gf_bifs_enc_mantissa_float(GF_BifsEncoder * codec, SFFloat val, GF_BitStream *bs); +GF_Err gf_bifs_enc_quant_field(GF_BifsEncoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); + + + +/* + Predictive MFField decode - mainly IM1 code (GPL H263 AA coder used) +*/ + +typedef struct _aamodel GF_AAModel; +GF_AAModel *gp_bifs_aa_model_new(); +void gp_bifs_aa_model_del(GF_AAModel *model); +void gp_bifs_aa_model_init(GF_AAModel *model, u32 nbBits); + +typedef struct _aadecoder GF_AADecoder; +GF_AADecoder *gp_bifs_aa_dec_new(GF_BitStream *bs); +void gp_bifs_aa_dec_del(GF_AADecoder *dec); +void gp_bifs_aa_dec_flush(GF_AADecoder *dec); +/*get input bit*/ +s32 gp_bifs_aa_dec_get_bit(GF_AADecoder *dec); +/*resync after input bit has been fetched (full buffer (16 bit) rewind in source stream)*/ +void gp_bifs_aa_dec_resync_bit(GF_AADecoder *dec); +/*resync bitstream*/ +void gp_bifs_aa_dec_resync(GF_AADecoder *dec); +/*decode symbol in given model*/ +s32 gp_bifs_aa_decode(GF_AADecoder *dec, GF_AAModel *model); +/*reset decoder - called after each parsed I frame*/ +void gp_bifs_aa_dec_reset(GF_AADecoder *dec); + + +#endif diff --git a/src/bifs/quantize.c b/src/bifs/quantize.c new file mode 100644 index 0000000..f14507b --- /dev/null +++ b/src/bifs/quantize.c @@ -0,0 +1,333 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "quant.h" + + +GF_Err gf_bifs_enc_qp_set(GF_BifsEncoder *codec, GF_Node *qp) +{ + if (gf_node_get_tag(qp) != TAG_MPEG4_QuantizationParameter) return GF_BAD_PARAM; + + /*if we have an active QP, push it into the stack*/ + if (codec->ActiveQP && ((GF_Node*)codec->ActiveQP != codec->scene_graph->global_qp) ) + gf_list_insert(codec->QPs, codec->ActiveQP, 0); + + codec->ActiveQP = (M_QuantizationParameter *)qp; + return GF_OK; +} + +GF_Err gf_bifs_enc_qp_remove(GF_BifsEncoder *codec, Bool ActivatePrev) +{ + codec->ActiveQP = NULL; + if (!ActivatePrev) return GF_OK; + + if (gf_list_count(codec->QPs)) { + codec->ActiveQP = (M_QuantizationParameter*)gf_list_get(codec->QPs, 0); + gf_list_rem(codec->QPs, 0); + } else if (codec->scene_graph->global_qp) { + codec->ActiveQP = (M_QuantizationParameter *)codec->scene_graph->global_qp; + } + return GF_OK; +} + + +u32 gf_bifs_enc_qp14_get_bits(GF_BifsEncoder *codec) +{ + if (!codec->ActiveQP || !codec->coord_stored) return 0; + return (u32) ceil(log(codec->NumCoord+1) / log(2) ); +} + +void gf_bifs_enc_qp14_enter(GF_BifsEncoder *codec, Bool Enter) +{ + if (!codec->ActiveQP) return; + if (Enter) codec->storing_coord = 1; + else { + if (codec->storing_coord) codec->coord_stored = 1; + codec->storing_coord = 0; + } +} + +void gf_bifs_enc_qp14_reset(GF_BifsEncoder *codec) +{ + codec->coord_stored = 0; + codec->storing_coord = 0; + codec->NumCoord = 0; +} + +void gf_bifs_enc_qp14_set_length(GF_BifsEncoder *codec, u32 NbElements) +{ + if (!codec->ActiveQP || !codec->storing_coord || codec->coord_stored) return; + codec->NumCoord = NbElements; +} + +void gf_bifs_enc_mantissa_float(GF_BifsEncoder *codec, Fixed ft, GF_BitStream *bs) +{ + u32 mantLength, expLength, mantSign, mantissa, expSign, i, nbBits; + s32 exp; + + union + { + Float f; + s32 l; + } ft_val; + + if (ft == 0) { + gf_bs_write_int(bs, 0, 4); + return; + } + ft_val.f = FIX2FLT(ft); + + mantSign = ((ft_val.l & 0x80000000) >> 31) & 0x1; + mantissa = (ft_val.l & 0x007FFFFF) >> 9; + mantLength = 15; + expSign=0; + exp =(((ft_val.l & 0x7F800000) >> 23)-127); + expLength = 8; + + if (mantissa == 0) mantLength = 1; + + + if (exp) { + if (exp< 0) { + expSign = 1; + exp = -exp; + } + while ((exp & (1<<(--expLength)))==0) { } + exp &= ~(1<0; ++nbBits) i >>= 1; + + gf_bs_write_int(bs, nbBits+1, 4); + if (mantLength) { + gf_bs_write_int(bs, expLength, 3); + gf_bs_write_int(bs, mantSign, 1); + gf_bs_write_int(bs, mantissa, nbBits); + if(expLength) { + gf_bs_write_int(bs, expSign, 1); + gf_bs_write_int(bs, exp, expLength - 1); + } + } +} + + +//Linear Quantization for floats - go back to float to avoid overflow if nbBits more than 15... +s32 Q_Quantize(Fixed Min, Fixed Max, u32 NbBits, Fixed value) +{ + Float _v; + if (value <= Min) return 0; + if (value >= Max) return (1<x); + gf_bs_write_int(bs, newVal, NbBits); + newVal = Q_Quantize(BMin.y, BMax.y, NbBits, ((SFVec2f *)field_ptr)->y); + gf_bs_write_int(bs, newVal, NbBits); + return GF_OK; + case GF_SG_VRML_SFVEC3F: + newVal = Q_Quantize(BMin.x, BMax.x, NbBits, ((SFVec3f *)field_ptr)->x); + gf_bs_write_int(bs, newVal, NbBits); + newVal = Q_Quantize(BMin.y, BMax.y, NbBits, ((SFVec3f *)field_ptr)->y); + gf_bs_write_int(bs, newVal, NbBits); + newVal = Q_Quantize(BMin.z, BMax.z, NbBits, ((SFVec3f *)field_ptr)->z); + gf_bs_write_int(bs, newVal, NbBits); + return GF_OK; + case GF_SG_VRML_SFCOLOR: + newVal = Q_Quantize(BMin.x, BMax.x, NbBits, ((SFColor *)field_ptr)->red); + gf_bs_write_int(bs, newVal, NbBits); + newVal = Q_Quantize(BMin.y, BMax.y, NbBits, ((SFColor *)field_ptr)->green); + gf_bs_write_int(bs, newVal, NbBits); + newVal = Q_Quantize(BMin.z, BMax.z, NbBits, ((SFColor *)field_ptr)->blue); + gf_bs_write_int(bs, newVal, NbBits); + return GF_OK; + + case GF_SG_VRML_SFROTATION: + //forbidden in this Q mode + return GF_NON_COMPLIANT_BITSTREAM; + } + return GF_OK; +} + +//int in quant are either Linear Scalar fields or CoordIndex +//the quant is just a bitshifting into [0, 2^NbBits-1] +//so v = value - b_min +GF_Err Q_EncInt(GF_BifsEncoder *codec, GF_BitStream *bs, u32 QType, SFInt32 b_min, u32 NbBits, void *field_ptr) +{ + switch (QType) { + case QC_LINEAR_SCALAR: + case QC_COORD_INDEX: + gf_bs_write_int(bs, *((SFInt32 *)field_ptr) - b_min, NbBits); + return GF_OK; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } +} + +GF_Err Q_EncCoordOnUnitSphere(GF_BifsEncoder *codec, GF_BitStream *bs, u32 NbBits, u32 NbComp, Fixed *m_ft) +{ + u32 i; + u32 len = NbComp+1; + s32 orientation =-1; + Fixed maxTmp = - FIX_MAX; + for (i=0; i maxTmp) { + maxTmp = ABS(m_ft[i]); + orientation = i; + } + } + if(NbComp==2) gf_bs_write_int(bs, ((m_ft[orientation]>0) ? 0 : 1), 1); + gf_bs_write_int(bs, orientation, 2); + for (i=0; i=0 ? v : -v)); + s32 qv = (1<<(NbBits-1)) + (v>=0 ? 1 : -1) * qdt; + gf_bs_write_int(bs, qv, NbBits); + } + return GF_OK; +} + +GF_Err Q_EncNormal(GF_BifsEncoder *codec, GF_BitStream *bs, u32 NbBits, void *field_ptr) +{ + Fixed comp[3]; + SFVec3f v = * (SFVec3f *)field_ptr; + gf_vec_norm(&v); + comp[0] = v.x; + comp[1] = v.y; + comp[2] = v.z; + return Q_EncCoordOnUnitSphere(codec, bs, NbBits, 2, comp); +} + +GF_Err Q_EncRotation(GF_BifsEncoder *codec, GF_BitStream *bs, u32 NbBits, void *field_ptr) +{ + GF_Vec4 quat; + Fixed comp[4]; + + /*get quaternion*/ + quat = gf_quat_from_rotation(*(SFRotation *)field_ptr); + comp[0] = quat.q; + comp[1] = quat.x; + comp[2] = quat.y; + comp[3] = quat.z; + return Q_EncCoordOnUnitSphere(codec, bs, NbBits, 3, comp); +} + +GF_Err gf_bifs_enc_quant_field(GF_BifsEncoder *codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +{ + Bool HasQ; + u8 QType, AType; + u32 NbBits; + Fixed b_min, b_max; + SFVec3f BMin, BMax; + GF_Err e; + + /*check QP*/ + if (!codec->ActiveQP) return GF_EOS; + /*check FieldType*/ + switch (field->fieldType) { + case GF_SG_VRML_SFINT32: + case GF_SG_VRML_SFFLOAT: + case GF_SG_VRML_SFROTATION: + case GF_SG_VRML_SFVEC2F: + case GF_SG_VRML_SFVEC3F: + break; + case GF_SG_VRML_SFCOLOR: + break; + default: + return GF_EOS; + } + + /*check NDT*/ + HasQ = gf_bifs_get_aq_info(node, field->fieldIndex, &QType, &AType, &b_min, &b_max, &NbBits); + if (!HasQ || !QType) return GF_EOS; + + /*get NbBits for QP14 (QC_COORD_INDEX)*/ + if (QType == QC_COORD_INDEX) { + NbBits = gf_bifs_enc_qp14_get_bits(codec); + /*QP14 is always on, not having NbBits set means the coord field is set after the index field, hence not decodable*/ + if (!NbBits) + return GF_NON_COMPLIANT_BITSTREAM; + } + + BMin.x = BMin.y = BMin.z = b_min; + BMax.x = BMax.y = BMax.z = b_max; + + /*check is the QP is on and retrieves the bounds*/ + if (!Q_IsTypeOn(codec->ActiveQP, QType, &NbBits, &BMin, &BMax)) return GF_EOS; + + /*ok the field is Quantized, dequantize*/ + switch (QType) { + //these are all SFFloat quantized on n fields + case QC_3DPOS: + case QC_2DPOS: + case QC_ORDER: + case QC_COLOR: + case QC_TEXTURE_COORD: + case QC_ANGLE: + case QC_SCALE: + case QC_INTERPOL_KEYS: + case QC_SIZE_3D: + case QC_SIZE_2D: + e = Q_EncFloat(codec, bs, field->fieldType, BMin, BMax, NbBits, field->far_ptr); + break; + //SFInt types + case QC_LINEAR_SCALAR: + case QC_COORD_INDEX: + e = Q_EncInt(codec, bs, QType, (SFInt32) b_min, NbBits, field->far_ptr); + break; + //normalized fields (normals and vectors) + case QC_NORMALS: + //normal quant is only for SFVec3F + if (field->fieldType != GF_SG_VRML_SFVEC3F) return GF_NON_COMPLIANT_BITSTREAM; + e = Q_EncNormal(codec, bs, NbBits, field->far_ptr); + break; + case QC_ROTATION: + //normal quant is only for SFVec3F + if (field->fieldType != GF_SG_VRML_SFROTATION) return GF_NON_COMPLIANT_BITSTREAM; + e = Q_EncRotation(codec, bs, NbBits, field->far_ptr); + break; + default: + return GF_BAD_PARAM; + } + return e; +} diff --git a/src/bifs/script.h b/src/bifs/script.h new file mode 100644 index 0000000..fdf6d69 --- /dev/null +++ b/src/bifs/script.h @@ -0,0 +1,105 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _SFSCRIPT_H +#define _SFSCRIPT_H + +#include + +#define NUMBITS_STATEMENT 3 +#define NUMBITS_EXPR_TYPE 6 +#define MAX_NUM_EXPR 100 + +enum +{ + ST_IF=0, + ST_FOR=1, + ST_WHILE=2, + ST_RETURN=3, + ST_BREAK=5, + ST_CONTINUE=6, + ST_COMPOUND_EXPR=4, + ST_SWITCH=7 +}; + +enum +{ + ET_CURVED_EXPR=0, + ET_NEGATIVE=1, + ET_NOT=2, + ET_ONESCOMP=3, + ET_INCREMENT=4, + ET_DECREMENT=5, + ET_POST_INCREMENT=6, + ET_POST_DECREMENT=7, + ET_CONDTEST=8, + ET_STRING=9, + ET_NUMBER=10, + ET_IDENTIFIER=11, + ET_FUNCTION_CALL=12, + ET_NEW=13, ET_OBJECTCONSTRUCT=13, + ET_OBJECT_MEMBER_ACCESS=14, + ET_OBJECT_METHOD_CALL=15, + ET_ARRAY_DEREFERENCE=16, + ET_ASSIGN=17, + ET_PLUSEQ=18, + ET_MINUSEQ=19, + ET_MULTIPLYEQ=20, + ET_DIVIDEEQ=21, + ET_MODEQ=22, + ET_ANDEQ=23, + ET_OREQ=24, + ET_XOREQ=25, + ET_LSHIFTEQ=26, + ET_RSHIFTEQ=27, + ET_RSHIFTFILLEQ=28, + ET_EQ=29, + ET_NE=30, + ET_LT=31, + ET_LE=32, + ET_GT=33, + ET_GE=34, + ET_PLUS=35, + ET_MINUS=36, + ET_MULTIPLY=37, + ET_DIVIDE=38, + ET_MOD=39, + ET_LAND=40, + ET_LOR=41, + ET_AND=42, + ET_OR=43, + ET_XOR=44, + ET_LSHIFT=45, + ET_RSHIFT=46, + ET_RSHIFTFILL=47, + ET_BOOLEAN=48, + ET_VAR=49, + ET_FUNCTION_ASSIGN=50, + NUMBER_OF_EXPR_TYPE=51 +}; + +GF_Err SFScript_Parse(GF_BifsDecoder *codec, SFScript *script_field, GF_BitStream *bs, GF_Node *n); +GF_Err SFScript_Encode(GF_BifsEncoder *codec, SFScript *script_field, GF_BitStream *bs, GF_Node *n); + +#endif diff --git a/src/bifs/script_dec.c b/src/bifs/script_dec.c new file mode 100644 index 0000000..4494e6f --- /dev/null +++ b/src/bifs/script_dec.c @@ -0,0 +1,778 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "script.h" + +#define BINOP_MINVAL ET_EQ + +typedef struct +{ + GF_Node *script; + GF_BifsDecoder *codec; + GF_BitStream *bs; + char *string; + u32 length; + GF_List *identifiers; + char *new_line; + u32 indent; +} ScriptParser; + + +void SFS_Identifier(ScriptParser *parser); +void SFS_Arguments(ScriptParser *parser, Bool is_var); +void SFS_StatementBlock(ScriptParser *parser, Bool functBody); +void SFS_Statement(ScriptParser *parser); +void SFS_IfStatement(ScriptParser *parser); +void SFS_SwitchStatement(ScriptParser *parser); +void SFS_ForStatement(ScriptParser *parser); +void SFS_WhileStatement(ScriptParser *parser); +void SFS_ReturnStatement(ScriptParser *parser); +void SFS_CompoundExpression(ScriptParser *parser); +void SFS_OptionalExpression(ScriptParser *parser); +void SFS_Expression(ScriptParser *parser); +void SFS_NewObject(ScriptParser *parser); +void SFS_ArrayDeref(ScriptParser *parser); +void SFS_FunctionCall(ScriptParser *parser); +void SFS_ObjectMemberAccess(ScriptParser *parser); +void SFS_ObjectMethodCall(ScriptParser *parser); +void SFS_Params(ScriptParser *parser); +void SFS_GetNumber(ScriptParser *parser); +void SFS_GetString(ScriptParser *parser); +void SFS_GetBoolean(ScriptParser *parser); + +#define PARSER_STEP_ALLOC 500 + +static void SFS_AddString(ScriptParser *parser, char *str) +{ + char *new_str; + if (!str) return; + if (strlen(parser->string) + strlen(str) >= parser->length) { + parser->length += PARSER_STEP_ALLOC; + new_str = (char *)malloc(sizeof(char)*parser->length); + strcpy(new_str, parser->string); + free(parser->string); + parser->string = new_str; + } + strcat(parser->string, str); +} + +static void SFS_AddInt(ScriptParser *parser, s32 val) +{ + char msg[500]; + sprintf(msg, "%d", val); + SFS_AddString(parser, msg); +} +static void SFS_AddChar(ScriptParser *parser, char c) +{ + char msg[2]; + sprintf(msg, "%c", c); + SFS_AddString(parser, msg); +} + + +GF_Err ParseScriptField(ScriptParser *parser) +{ + GF_ScriptField *field; + GF_Err e; + u32 eventType, fieldType; + char name[1000]; + GF_FieldInfo info; + + eventType = gf_bs_read_int(parser->bs, 2); + fieldType = gf_bs_read_int(parser->bs, 6); + gf_bifs_dec_name(parser->bs, name); + field = gf_sg_script_field_new(parser->script, eventType, fieldType, name); + if (!field) return GF_NON_COMPLIANT_BITSTREAM; + + //save the name in the list of identifiers + gf_list_add(parser->identifiers, strdup(name)); + + if (parser->codec->pCurrentProto) { + Bool isISfield = gf_bs_read_int(parser->bs, 1); + if (isISfield) { + u32 numProtoField = gf_sg_proto_get_field_count(parser->codec->pCurrentProto); + u32 numBits = gf_get_bit_size(numProtoField - 1); + u32 field_all = gf_bs_read_int(parser->bs, numBits); + e = gf_sg_script_field_get_info(field, &info); + if (e) return e; + e = gf_sg_proto_field_set_ised(parser->codec->pCurrentProto, field_all, parser->script, info.fieldIndex); + return e; + } + } + /*get default value*/ + if (eventType == GF_SG_SCRIPT_TYPE_FIELD) { + if (gf_bs_read_int(parser->bs, 1)) { + e = gf_sg_script_field_get_info(field, &info); + if (e) return e; + gf_bifs_dec_field(parser->codec, parser->bs, parser->script, &info); + } + } + + return parser->codec->LastError; +} + +static void SFS_IncIndent(ScriptParser *pars) { pars->indent++; } +static void SFS_DecIndent(ScriptParser *pars) { pars->indent--; } +static void SFS_Space(ScriptParser *pars) { if (pars->new_line) SFS_AddString(pars, " ");} +static void SFS_Indent(ScriptParser *pars) +{ + u32 i; + if (pars->new_line) { + for (i=0; iindent; i++) SFS_AddString(pars, " "); + } +} +static GFINLINE void SFS_Line(ScriptParser *parser) +{ + if (parser->new_line) { + SFS_AddString(parser, parser->new_line); + } +} + + +GF_Err SFScript_Parse(GF_BifsDecoder *codec, SFScript *script_field, GF_BitStream *bs, GF_Node *n) +{ + GF_Err e; + u32 i, count, nbBits; + char *ptr; + ScriptParser parser; + Bool has_fields = 0; + e = GF_OK; + if (gf_node_get_tag(n) != TAG_MPEG4_Script) return GF_NON_COMPLIANT_BITSTREAM; + + parser.codec = codec; + parser.script = n; + parser.bs = bs; + parser.length = 500; + parser.string = (char *) malloc(sizeof(char)* parser.length); + parser.string[0] = 0; + parser.identifiers = gf_list_new(); + parser.new_line = (char *) (codec->dec_memory_mode ? "\n" : NULL); + parser.indent = 0; + + //first parse fields + + if (gf_bs_read_int(bs, 1)) { + //endFlag + while (!gf_bs_read_int(bs, 1)){ + e = ParseScriptField(&parser); + if (e) goto exit; + else has_fields = 1; + } + } else { + nbBits = gf_bs_read_int(bs, 4); + count = gf_bs_read_int(bs, nbBits); + for (i=0; iscript_text) free(script_field->script_text); + script_field->script_text = (unsigned char *) strdup(parser.string); + +exit: + //clean up + while (gf_list_count(parser.identifiers)) { + ptr = (char *)gf_list_get(parser.identifiers, 0); + free(ptr); + gf_list_rem(parser.identifiers, 0); + } + gf_list_del(parser.identifiers); + if (parser.string) free(parser.string); + return e; +} + + + +void SFS_Identifier(ScriptParser *parser) +{ + u32 index; + char name[500]; + + if (parser->codec->LastError) return; + + //received + if (gf_bs_read_int(parser->bs, 1)) { + index = gf_bs_read_int(parser->bs, gf_get_bit_size(gf_list_count(parser->identifiers) - 1)); + SFS_AddString(parser, (char *)gf_list_get(parser->identifiers, index)); + } + //parse + else{ + gf_bifs_dec_name(parser->bs, name); + gf_list_add(parser->identifiers, strdup(name)); + SFS_AddString(parser, name); + } +} + +void SFS_Arguments(ScriptParser *parser, Bool is_var) +{ + u32 val; + if (parser->codec->LastError) return; + if (!is_var) SFS_AddString(parser, "("); + + val = gf_bs_read_int(parser->bs, 1); + while (val) { + SFS_Identifier(parser); + val = gf_bs_read_int(parser->bs, 1); + if (val) SFS_AddString(parser, ","); + } + if (!is_var) SFS_AddString(parser, ")"); +} + +void SFS_StatementBlock(ScriptParser *parser, Bool funcBody) +{ + if (parser->codec->LastError) return; + + if (gf_bs_read_int(parser->bs, 1)) { + SFS_AddString(parser, "{"); + SFS_IncIndent(parser); + + while (gf_bs_read_int(parser->bs, 1)) { + SFS_Line(parser); + SFS_Indent(parser); + SFS_Statement(parser); + } + SFS_DecIndent(parser); + SFS_Line(parser); + SFS_Indent(parser); + SFS_AddString(parser, "}"); + } else if (funcBody) { + SFS_AddString(parser, "{"); + SFS_Statement(parser); + SFS_AddString(parser, "}"); + } else { + SFS_Statement(parser); + } +} + + +void SFS_Statement(ScriptParser *parser) +{ + u32 val; + if (parser->codec->LastError) return; + + val = gf_bs_read_int(parser->bs, NUMBITS_STATEMENT); + switch (val) { + case ST_IF: + SFS_IfStatement(parser); + break; + case ST_FOR: + SFS_ForStatement(parser); + break; + case ST_WHILE: + SFS_WhileStatement(parser); + break; + case ST_RETURN: + SFS_ReturnStatement(parser); + break; + case ST_BREAK: + SFS_AddString(parser, "break;"); + break; + case ST_CONTINUE: + SFS_AddString(parser, "continue;"); + break; + case ST_COMPOUND_EXPR: + SFS_CompoundExpression(parser); + SFS_AddString(parser, ";"); + break; + case ST_SWITCH: + SFS_SwitchStatement(parser); + break; + } +} + +void SFS_IfStatement(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + SFS_AddString(parser, "if ("); + SFS_CompoundExpression(parser); + SFS_AddString(parser, ") "); + SFS_StatementBlock(parser, 0); + //has else + if (gf_bs_read_int(parser->bs, 1)) { + SFS_Line(parser); + SFS_Indent(parser); + SFS_AddString(parser, "else "); + SFS_StatementBlock(parser, 0); + } +} + +void SFS_SwitchStatement(ScriptParser *parser) +{ + u32 numBits, caseValue; + + if (parser->codec->LastError) return; + SFS_AddString(parser, "switch ("); + SFS_CompoundExpression(parser); + SFS_AddString(parser, ")"); + SFS_AddString(parser, "{"); + SFS_Line(parser); + + numBits = gf_bs_read_int(parser->bs, 5); + do { + SFS_Indent(parser); + SFS_AddString(parser, "case "); + caseValue = gf_bs_read_int(parser->bs, numBits); + SFS_AddInt(parser, caseValue); + SFS_AddString(parser, ":"); + SFS_Line(parser); + SFS_Indent(parser); + SFS_StatementBlock(parser, 0); + SFS_Line(parser); + } + while (gf_bs_read_int(parser->bs, 1)); + + //default + if (gf_bs_read_int(parser->bs, 1)) { + SFS_AddString(parser, "default:"); + SFS_Line(parser); + SFS_StatementBlock(parser, 0); + } + SFS_AddString(parser, "}"); +} + +void SFS_ForStatement(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + SFS_AddString(parser, "for ("); + SFS_OptionalExpression(parser); + SFS_AddString(parser, ";"); + SFS_OptionalExpression(parser); + SFS_AddString(parser, ";"); + SFS_OptionalExpression(parser); + SFS_AddString(parser, ")"); + + SFS_StatementBlock(parser, 0); +} + +void SFS_WhileStatement(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + SFS_AddString(parser, "while ("); + SFS_CompoundExpression(parser); + SFS_AddString(parser, ")"); + + SFS_StatementBlock(parser, 0); +} + +void SFS_ReturnStatement(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + SFS_AddString(parser, "return"); + if (gf_bs_read_int(parser->bs, 1)) { + SFS_AddString(parser, " "); + SFS_CompoundExpression(parser); + } + SFS_AddString(parser, ";"); + SFS_Line(parser); +} + +void SFS_CompoundExpression(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + SFS_Expression(parser); + if (! gf_bs_read_int(parser->bs, 1)) return; + SFS_AddString(parser, ","); + SFS_CompoundExpression(parser); +} + +void SFS_OptionalExpression(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + if (gf_bs_read_int(parser->bs, 1)) { + SFS_CompoundExpression(parser); + } +} + + +void SFS_Expression(ScriptParser *parser) +{ + u32 val = gf_bs_read_int(parser->bs, NUMBITS_EXPR_TYPE); + if (parser->codec->LastError) return; + + switch(val) { + case ET_CURVED_EXPR: + SFS_AddString(parser, "("); + SFS_CompoundExpression(parser); + SFS_AddString(parser, ")"); + break; + case ET_NEGATIVE: + SFS_AddString(parser, "-"); + SFS_Expression(parser); + break; + case ET_NOT: + SFS_AddString(parser, "!"); + SFS_Expression(parser); + break; + case ET_ONESCOMP: + SFS_AddString(parser, "~"); + SFS_Expression(parser); + break; + case ET_INCREMENT: + SFS_AddString(parser, "++"); + SFS_Expression(parser); + break; + case ET_DECREMENT: + SFS_AddString(parser, "--"); + SFS_Expression(parser); + break; + case ET_POST_INCREMENT: + SFS_Expression(parser); + SFS_AddString(parser, "++"); + break; + case ET_POST_DECREMENT: + SFS_Expression(parser); + SFS_AddString(parser, "--"); + break; + case ET_CONDTEST: + SFS_Expression(parser); + SFS_AddString(parser, " ? "); + SFS_Expression(parser); + SFS_AddString(parser, " : "); + SFS_Expression(parser); + break; + case ET_STRING: + SFS_AddString(parser, "'"); + SFS_GetString(parser); + SFS_AddString(parser, "'"); + break; + case ET_NUMBER: + SFS_GetNumber(parser); + break; + case ET_IDENTIFIER: + SFS_Identifier(parser); + break; + case ET_FUNCTION_CALL: + SFS_FunctionCall(parser); + break; + case ET_NEW: + SFS_NewObject(parser); + break; + case ET_OBJECT_MEMBER_ACCESS: + SFS_ObjectMemberAccess(parser); + break; + case ET_OBJECT_METHOD_CALL: + SFS_ObjectMethodCall(parser); + break; + case ET_ARRAY_DEREFERENCE: + SFS_ArrayDeref(parser); + break; + + case ET_MULTIPLY: + SFS_Expression(parser); + SFS_AddString(parser, "*"); + SFS_Expression(parser); + break; + case ET_DIVIDE: + SFS_Expression(parser); + SFS_AddString(parser, "/"); + SFS_Expression(parser); + break; + case ET_MOD: + SFS_Expression(parser); + SFS_AddString(parser, "%"); + SFS_Expression(parser); + break; + case ET_PLUS: + SFS_Expression(parser); + SFS_AddString(parser, "+"); + SFS_Expression(parser); + break; + case ET_MINUS: + SFS_Expression(parser); + SFS_AddString(parser, "-"); + SFS_Expression(parser); + break; + case ET_LSHIFT: + SFS_Expression(parser); + SFS_AddString(parser, "<<"); + SFS_Expression(parser); + break; + case ET_RSHIFT: + SFS_Expression(parser); + SFS_AddString(parser, ">>"); + SFS_Expression(parser); + break; + case ET_RSHIFTFILL: + SFS_Expression(parser); + SFS_AddString(parser, ">>>"); + SFS_Expression(parser); + break; + case ET_AND: + SFS_Expression(parser); + SFS_AddString(parser, "&"); + SFS_Expression(parser); + break; + case ET_XOR: + SFS_Expression(parser); + SFS_AddString(parser, "^"); + SFS_Expression(parser); + break; + case ET_OR: + SFS_Expression(parser); + SFS_AddString(parser, "|"); + SFS_Expression(parser); + break; + case ET_LT: + SFS_Expression(parser); + SFS_AddString(parser, "<"); + SFS_Expression(parser); + break; + case ET_LE: + SFS_Expression(parser); + SFS_AddString(parser, "<="); + SFS_Expression(parser); + break; + case ET_GT: + SFS_Expression(parser); + SFS_AddString(parser, ">"); + SFS_Expression(parser); + break; + case ET_GE: + SFS_Expression(parser); + SFS_AddString(parser, ">="); + SFS_Expression(parser); + break; + case ET_EQ: + SFS_Expression(parser); + SFS_AddString(parser, "=="); + SFS_Expression(parser); + break; + case ET_NE: + SFS_Expression(parser); + SFS_AddString(parser, "!="); + SFS_Expression(parser); + break; + case ET_LAND: + SFS_Expression(parser); + SFS_AddString(parser, "&&"); + SFS_Expression(parser); + break; + case ET_LOR: + SFS_Expression(parser); + SFS_AddString(parser, "||"); + SFS_Expression(parser); + break; + case ET_ASSIGN: + SFS_Expression(parser); + SFS_AddString(parser, "="); + SFS_Expression(parser); + break; + case ET_PLUSEQ: + SFS_Expression(parser); + SFS_AddString(parser, "+="); + SFS_Expression(parser); + break; + case ET_MINUSEQ: + SFS_Expression(parser); + SFS_AddString(parser, "-="); + SFS_Expression(parser); + break; + case ET_MULTIPLYEQ: + SFS_Expression(parser); + SFS_AddString(parser, "*="); + SFS_Expression(parser); + break; + case ET_DIVIDEEQ: + SFS_Expression(parser); + SFS_AddString(parser, "/="); + SFS_Expression(parser); + break; + case ET_MODEQ: + SFS_Expression(parser); + SFS_AddString(parser, "%="); + SFS_Expression(parser); + break; + case ET_LSHIFTEQ: + SFS_Expression(parser); + SFS_AddString(parser, "<<="); + SFS_Expression(parser); + break; + case ET_RSHIFTEQ: + SFS_Expression(parser); + SFS_AddString(parser, ">>="); + SFS_Expression(parser); + break; + case ET_RSHIFTFILLEQ: + SFS_Expression(parser); + SFS_AddString(parser, ">>>="); + SFS_Expression(parser); + break; + case ET_ANDEQ: + SFS_Expression(parser); + SFS_AddString(parser, "&="); + SFS_Expression(parser); + break; + case ET_XOREQ: + SFS_Expression(parser); + SFS_AddString(parser, "^="); + SFS_Expression(parser); + break; + case ET_OREQ: + SFS_Expression(parser); + SFS_AddString(parser, "|="); + SFS_Expression(parser); + break; + case ET_BOOLEAN: + SFS_GetBoolean(parser); + break; + case ET_VAR: + SFS_AddString(parser, "var "); + SFS_Arguments(parser, 1); + break; + case ET_FUNCTION_ASSIGN: + SFS_AddString(parser, "function "); + SFS_Arguments(parser, 0); + SFS_StatementBlock(parser, 1); + break; + default: + assert(0); + break; + } +} + +void SFS_NewObject(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + SFS_AddString(parser, "new "); + SFS_Identifier(parser); + SFS_AddString(parser, "("); + SFS_Params(parser); + SFS_AddString(parser, ") "); +} + +void SFS_ArrayDeref(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + SFS_Expression(parser); + SFS_AddString(parser, "["); + SFS_CompoundExpression(parser); + SFS_AddString(parser, "]"); +} + +void SFS_FunctionCall(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + SFS_Identifier(parser); + SFS_AddString(parser, "("); + SFS_Params(parser); + SFS_AddString(parser, ")"); +} + +void SFS_ObjectMemberAccess(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + SFS_Expression(parser); + SFS_AddString(parser, "."); + SFS_Identifier(parser); +} + + +void SFS_ObjectMethodCall(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + SFS_Expression(parser); + SFS_AddString(parser, "."); + SFS_Identifier(parser); + SFS_AddString(parser, "("); + SFS_Params(parser); + SFS_AddString(parser, ")"); +} + +void SFS_Params(ScriptParser *parser) +{ + u32 val; + if (parser->codec->LastError) return; + val = gf_bs_read_int(parser->bs, 1); + while (val) { + SFS_Expression(parser); + val = gf_bs_read_int(parser->bs, 1); + if(val) SFS_AddString(parser, ","); + } +} + +void SFS_GetNumber(ScriptParser *parser) +{ + u32 val, nbBits; + + if (parser->codec->LastError) return; + // integer + if (gf_bs_read_int(parser->bs, 1)) { + nbBits = gf_bs_read_int(parser->bs, 5); + val = gf_bs_read_int(parser->bs, nbBits); + SFS_AddInt(parser, val); + return; + } + // real number + val = gf_bs_read_int(parser->bs, 4); + while ( val != 15) { + if (val>=0 && val<=9) { + SFS_AddChar(parser, (char) (val + '0') ); + } else if (val==10) { + SFS_AddChar(parser, '.'); + } else if (val==11) { + SFS_AddChar(parser, 'E'); + } else if (val==12){ + SFS_AddChar(parser, '-'); + } + val = gf_bs_read_int(parser->bs, 4); + } +} + +void SFS_GetString(ScriptParser *parser) +{ + char name[1000]; + if (parser->codec->LastError) return; + gf_bifs_dec_name(parser->bs, name); + SFS_AddString(parser, name); +} + +void SFS_GetBoolean(ScriptParser *parser) +{ + if (parser->codec->LastError) return; + if (gf_bs_read_int(parser->bs, 1)) { + SFS_AddString(parser, "true"); + } else { + SFS_AddString(parser, "false"); + } +} diff --git a/src/bifs/script_enc.c b/src/bifs/script_enc.c new file mode 100644 index 0000000..5c48006 --- /dev/null +++ b/src/bifs/script_enc.c @@ -0,0 +1,1773 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + script encoder is IM1 version +*/ + +#include "script.h" + +#include + +typedef struct +{ + GF_Node *script; + GF_BifsEncoder *codec; + GF_BitStream *bs; + GF_List *identifiers; + GF_Err err; + + char *cur_buf; + char token[500]; + u32 token_code; + + u32 cur_line; + + Bool emul; + + char expr_toks[500]; + u32 expr_toks_len; + GF_List *id_buf; +} ScriptEnc; + + +#define SFE_WRITE_INT(sc_enc, val, nbBits, str1, str2) \ + if (!sc_enc->emul) GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, val, nbBits, str1, str2); \ + + +static GF_Err EncScriptFields(ScriptEnc *sc_enc) +{ + u32 nbFields, nbBits, eType, nbBitsProto, i; + Bool use_list; + GF_Err e; + GF_FieldInfo info; + + nbFields = gf_node_get_num_fields_in_mode(sc_enc->script, GF_SG_FIELD_CODING_ALL) - 3; + use_list = 1; + nbBits = gf_get_bit_size(nbFields); + if (nbFields+1 > 4 + gf_get_bit_size(nbFields)) use_list = 0; + if (!nbFields) { + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "Script::isList", NULL); + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "end", NULL); + return GF_OK; + } + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, use_list ? 1 : 0, 1, "Script::isList", NULL); + if (!use_list) { + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, nbBits, 4, "nbBits", NULL); + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, nbFields, nbBits, "count", NULL); + } + + nbBitsProto = 0; + if (sc_enc->codec->encoding_proto) nbBitsProto = gf_get_bit_size(gf_sg_proto_get_field_count(sc_enc->codec->encoding_proto) - 1); + + for (i=0; icodec, sc_enc->bs, 0, 1, "end", NULL); + + gf_node_get_field(sc_enc->script, i+3, &info); + switch (info.eventType) { + case GF_SG_EVENT_IN: + eType = GF_SG_SCRIPT_TYPE_EVENT_IN; + break; + case GF_SG_EVENT_OUT: + eType = GF_SG_SCRIPT_TYPE_EVENT_OUT; + break; + default: + eType = GF_SG_SCRIPT_TYPE_FIELD; + break; + } + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, eType, 2, "eventType", NULL); + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, info.fieldType, 6, "fieldType", NULL); + gf_bifs_enc_name(sc_enc->codec, sc_enc->bs, (char *) info.name); + /*this is an identifier for script*/ + gf_list_add(sc_enc->identifiers, strdup(info.name)); + + if (sc_enc->codec->encoding_proto) { + GF_Route *isedField = gf_bifs_enc_is_field_ised(sc_enc->codec, sc_enc->script, i+3); + if (isedField) { + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "isedField", NULL); + + if (isedField->ToNode == sc_enc->script) { + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, isedField->FromField.fieldIndex, nbBitsProto, "protoField", NULL); + } else { + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, isedField->ToField.fieldIndex, nbBitsProto, "protoField", NULL); + } + continue; + } + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 0, 1, "isedField", NULL); + } + /*default value*/ + if (eType == GF_SG_SCRIPT_TYPE_FIELD) { + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, (info.far_ptr) ? 1 : 0, 1, "hasInitialValue", NULL); + if (info.far_ptr) { + e = gf_bifs_enc_field(sc_enc->codec, sc_enc->bs, sc_enc->script, &info); + if (e) return e; + } + } + } + if (use_list) GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "end", NULL); + return GF_OK; +} + + + +enum +{ + TOK_FUNCTION, + TOK_IF, + TOK_ELSE, + TOK_FOR, + TOK_WHILE, + TOK_RETURN, + TOK_BREAK, + TOK_CONTINUE, + TOK_NEW, + TOK_SWITCH, + TOK_CASE, + TOK_DEFAULT, + TOK_VAR, + NUMBER_OF_KEYWORD, + TOK_LEFT_BRACE = NUMBER_OF_KEYWORD, + TOK_RIGHT_BRACE, + TOK_LEFT_CURVE, + TOK_RIGHT_CURVE, + TOK_LEFT_BRACKET, + TOK_RIGHT_BRACKET, + TOK_PERIOD, + TOK_NOT, + TOK_ONESCOMP, + TOK_NEGATIVE, + TOK_INCREMENT, + TOK_DECREMENT, + TOK_MULTIPLY, + TOK_DIVIDE, + TOK_MOD, + TOK_PLUS, + TOK_MINUS, + TOK_LSHIFT, + TOK_RSHIFT, + TOK_RSHIFTFILL, + TOK_LT, + TOK_LE, + TOK_GT, + TOK_GE, + TOK_EQ, + TOK_NE, + TOK_AND, + TOK_XOR, + TOK_OR, + TOK_LAND, + TOK_LOR, + TOK_CONDTEST, + TOK_ASSIGN, + TOK_PLUSEQ, + TOK_MINUSEQ, + TOK_MULTIPLYEQ, + TOK_DIVIDEEQ, + TOK_MODEQ, + TOK_LSHIFTEQ, + TOK_RSHIFTEQ, + TOK_RSHIFTFILLEQ, + TOK_ANDEQ, + TOK_XOREQ, + TOK_OREQ, + TOK_COMMA, + TOK_SEMICOLON, + TOK_CONDSEP, + TOK_IDENTIFIER, + TOK_STRING, + TOK_NUMBER, + TOK_EOF, + TOK_BOOLEAN +}; + +const char *tok_names[] = +{ + "function", + "if", + "else", + "for", + "while", + "return", + "break", + "continue", + "new", + "switch", + "case", + "default", + "var", + "{", + "}", + "(", + ")", + "[", + "]", + ".", + "!", + "~", + "-", + "++", + "--", + "*", + "/", + "%", + "+", + "-", + "<<", + ">>", + ">>>", + "<", + "<=", + ">", + ">=", + "==", + "!=", + "&", + "^", + "|", + "&&", + "||", + "?", + "=", + "+=", + "-=", + "*=", + "/=", + "%=", + "<<=", + ">>=", + ">>>=", + "&=", + "^=", + "|=", + ",", + ";", + ":", + "identifier", + "string", + "number", + "boolean", + "end of script" +}; + +const char* sc_keywords [] = +{ + "function", + "if", + "else", + "for", + "while", + "return", + "break", + "continue", + "new", + "switch", + "case", + "default", + "var" +}; + +Bool SFE_GetNumber(ScriptEnc *sc_enc) +{ + u32 i = 0; + Bool exp = 0; + while ( isdigit(sc_enc->cur_buf[i]) + || (toupper(sc_enc->cur_buf[i])=='X') + || ((toupper(sc_enc->cur_buf[i]) >='A') && (toupper(sc_enc->cur_buf[i])<='F')) + || (sc_enc->cur_buf[i]=='.') + || (tolower(sc_enc->cur_buf[i])=='e') + || (exp && (sc_enc->cur_buf[i] == '-')) + ) { + sc_enc->token[i] = sc_enc->cur_buf[i]; + if (tolower(sc_enc->cur_buf[i])=='e') exp = 1; + i++; + if (!sc_enc->cur_buf[i]) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: Invalid number syntax (%s)\n", sc_enc->cur_buf)); + sc_enc->err = GF_BAD_PARAM; + return 0; + } + } + sc_enc->token[i] = 0; + sc_enc->cur_buf += i; + sc_enc->token_code = TOK_NUMBER; + return 1; +} + +Bool SFE_NextToken(ScriptEnc *sc_enc) +{ + u32 i; + if (sc_enc->err) return 0; + while (strchr(" \t\r\n", sc_enc->cur_buf[0])) { + if (sc_enc->cur_buf[0]=='\n') sc_enc->cur_line ++; + sc_enc->cur_buf++; + } + if ((sc_enc->cur_buf[0] == '/') && (sc_enc->cur_buf[1] == '*')) { + sc_enc->cur_buf += 2; + while ((sc_enc->cur_buf[0] != '*') || (sc_enc->cur_buf[1] != '/')) { + sc_enc->cur_buf++; + if (!sc_enc->cur_buf[0] || !sc_enc->cur_buf[1]) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: cannot find closing comment */\n")); + sc_enc->err = GF_BAD_PARAM; + return 0; + } + } + sc_enc->cur_buf+=2; + return SFE_NextToken(sc_enc); + } + i = 0; + /*get a name*/ + if (isalpha(sc_enc->cur_buf[i]) || (sc_enc->cur_buf[i]=='_')) { + while (isalnum(sc_enc->cur_buf[i]) || (sc_enc->cur_buf[i]=='_')) { + sc_enc->token[i] = sc_enc->cur_buf[i]; + i++; + } + sc_enc->token[i] = 0; + sc_enc->cur_buf += i; + sc_enc->token_code = TOK_IDENTIFIER; + /*check keyword*/ + for (i=0; itoken, sc_keywords[i])) { + sc_enc->token_code = i; + return 1; + } + } + if (!stricmp(sc_enc->token, "TRUE") || !stricmp(sc_enc->token, "FALSE") ) { + sc_enc->token_code = TOK_BOOLEAN; + } + return 1; + } + /*get a number*/ + if (isdigit(sc_enc->cur_buf[i])) return SFE_GetNumber(sc_enc); + /*get a string*/ + if ((sc_enc->cur_buf[i]=='\'') || (sc_enc->cur_buf[i]=='\"') + || ((sc_enc->cur_buf[i]=='\\') && (sc_enc->cur_buf[i+1]=='\"')) + ) { + char end; + Bool skip_last = 0; + end = sc_enc->cur_buf[i]; + if (sc_enc->cur_buf[i]=='\\') { + skip_last = 1; + sc_enc->cur_buf++; + } + while (sc_enc->cur_buf[i+1] != end) { + sc_enc->token[i] = sc_enc->cur_buf[i+1]; + i++; + } + sc_enc->token[i] = 0; + sc_enc->cur_buf += i+2; + if (skip_last) sc_enc->cur_buf++; + sc_enc->token_code = TOK_STRING; + return 1; + } + /*all other codes*/ + switch (sc_enc->cur_buf[i]) { + case '.': + if (isdigit(sc_enc->cur_buf[i+1])) { + SFE_GetNumber(sc_enc); + return 1; + } else { + sc_enc->token_code = TOK_PERIOD; + } + break; + case '!': + if (sc_enc->cur_buf[i+1] == '=') { + sc_enc->token_code = TOK_NE; + sc_enc->cur_buf ++; + } else { + sc_enc->token_code = TOK_NOT; + } + break; + case '=': + if (sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_EQ; + sc_enc->cur_buf ++; + } else { + sc_enc->token_code = TOK_ASSIGN; + } + break; + case '+': + if(sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_PLUSEQ; + sc_enc->cur_buf ++; + } else if(sc_enc->cur_buf[i+1]=='+') { + sc_enc->token_code = TOK_INCREMENT; + sc_enc->cur_buf++; + } else { + sc_enc->token_code = TOK_PLUS; + } + break; + case '-': + if(sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_MINUSEQ; + sc_enc->cur_buf++; + } else if(sc_enc->cur_buf[i+1] == '-') { + sc_enc->token_code = TOK_DECREMENT; + sc_enc->cur_buf++; + } else { + sc_enc->token_code = TOK_MINUS; + } + break; + case '*': + if(sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_MULTIPLYEQ; + sc_enc->cur_buf++; + } else { + sc_enc->token_code = TOK_MULTIPLY; + } + break; + case '/': + if(sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_DIVIDEEQ; + sc_enc->cur_buf++; + } else { + sc_enc->token_code = TOK_DIVIDE; + } + break; + case '%': + if(sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_MODEQ; + sc_enc->cur_buf++; + } else { + sc_enc->token_code = TOK_MOD; + } + break; + case '&': + if(sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_ANDEQ; + sc_enc->cur_buf++; + } else if(sc_enc->cur_buf[i+1]=='&') { + sc_enc->token_code = TOK_LAND; + sc_enc->cur_buf++; + } else { + sc_enc->token_code = TOK_AND; + } + break; + case '|': + if(sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_OREQ; + sc_enc->cur_buf++; + } else if(sc_enc->cur_buf[i+1]=='|') { + sc_enc->token_code = TOK_LOR; + sc_enc->cur_buf++; + } else { + sc_enc->token_code = TOK_OR; + } + break; + case '^': + if(sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_XOREQ; + sc_enc->cur_buf++; + } else { + sc_enc->token_code = TOK_XOR; + } + break; + case '<': + if (sc_enc->cur_buf[i+1]=='<') { + if(sc_enc->cur_buf[i+2]=='=') { + sc_enc->token_code = TOK_LSHIFTEQ; + sc_enc->cur_buf += 1; + } else { + sc_enc->token_code = TOK_LSHIFT; + } + sc_enc->cur_buf += 1; + } else if(sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_LE; + sc_enc->cur_buf ++; + } else { + sc_enc->token_code = TOK_LT; + } + break; + case '>': + if (sc_enc->cur_buf[i+1]=='>') { + if (sc_enc->cur_buf[i+2]=='=') { + sc_enc->token_code = TOK_RSHIFTEQ; + sc_enc->cur_buf ++; + } else if(sc_enc->cur_buf[i+2]=='>') { + if(sc_enc->cur_buf[i+3]=='=') { + sc_enc->token_code = TOK_RSHIFTFILLEQ; + sc_enc->cur_buf ++; + } else { + sc_enc->token_code = TOK_RSHIFTFILL; + } + sc_enc->cur_buf ++; + } else { + sc_enc->token_code = TOK_RSHIFT; + } + sc_enc->cur_buf ++; + } else if (sc_enc->cur_buf[i+1]=='=') { + sc_enc->token_code = TOK_GE; + sc_enc->cur_buf ++; + } else { + sc_enc->token_code = TOK_GT; + } + break; + case '?': + sc_enc->token_code = TOK_CONDTEST; + break; + case ':': + sc_enc->token_code = TOK_CONDSEP; + break; + case '~': + sc_enc->token_code = TOK_ONESCOMP; + break; + case ',': + sc_enc->token_code = TOK_COMMA; + break; + case ';': + sc_enc->token_code = TOK_SEMICOLON; + break; + case '{': + sc_enc->token_code = TOK_LEFT_BRACE; + break; + case '}': + sc_enc->token_code = TOK_RIGHT_BRACE; + break; + case '(': + sc_enc->token_code = TOK_LEFT_CURVE; + break; + case ')': + sc_enc->token_code = TOK_RIGHT_CURVE; + break; + case '[': + sc_enc->token_code = TOK_LEFT_BRACKET; + break; + case ']': + sc_enc->token_code = TOK_RIGHT_BRACKET; + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: Unrecognized symbol %c\n", sc_enc->cur_buf[i])); + sc_enc->err = GF_BAD_PARAM; + return 0; + } + sc_enc->cur_buf ++; + return 1; +} + +Bool SFE_CheckToken(ScriptEnc *sc_enc, u32 token) +{ + if (sc_enc->token_code != token) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: Bad token (expecting \"%s\" got \"%s\")\n", tok_names[token] , tok_names[sc_enc->token_code])); + return 0; + } + return 1; +} + +void SFE_PutIdentifier(ScriptEnc *sc_enc, char *id) +{ + u32 i; + u32 nbBits, length; + char *str; + + if (sc_enc->emul) return; + + i=0; + while ((str = (char *)gf_list_enum(sc_enc->identifiers, &i))) { + if (strcmp(str, id)) continue; + + nbBits = 0; + length = gf_list_count(sc_enc->identifiers) - 1; + while (length > 0) { length >>= 1; nbBits ++; } + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "received", str); + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, i-1, nbBits, "identifierCode", str); + return; + } + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 0, 1, "received", id); + gf_list_add(sc_enc->identifiers, strdup(id)); + gf_bifs_enc_name(sc_enc->codec, sc_enc->bs, id); +} + + +void SFE_Arguments(ScriptEnc *sc_enc) +{ + while (1) { + if (!SFE_NextToken(sc_enc)) return; + if (sc_enc->token_code == TOK_RIGHT_CURVE) break; + else if (sc_enc->token_code == TOK_COMMA) continue; + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "hasArgument", NULL); + SFE_PutIdentifier(sc_enc, sc_enc->token); + } + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 0, 1, "hasArgument", NULL); +} + +void SFE_StatementBlock(ScriptEnc *sc_enc); +void SFE_Statement(ScriptEnc *sc_enc); + +void SFE_PutInteger(ScriptEnc *sc_enc, char *str) +{ + u32 nbBits, val = 0; + if (sc_enc->emul) return; + if ((str[0]=='0') && (str[1]=='x' || str[1]=='X')) { + val = strtoul(sc_enc->token, (char **) NULL, 16); + } else if (str[0]=='0' && isdigit(str[1])) { + val = strtoul(str, (char **) NULL, 8); + } else if (isdigit(str[0])) { + val = strtoul(str, (char **) NULL, 10); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: %s is not an integer\n", str)); + sc_enc->err = GF_BAD_PARAM; + return; + } + nbBits = gf_get_bit_size(val); + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, nbBits, 5, "nbBitsInteger", NULL); + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, val, nbBits, "value", sc_enc->token); +} + +u32 SFE_LoadExpression(ScriptEnc *sc_enc, u32 *expr_sep) +{ + Bool is_var = 0; + u32 close_code, open_code; + u32 count = 0; + u32 nbExpr = 1; + u32 nbIndir = 0; + expr_sep[0] = 0; + + sc_enc->expr_toks_len = 0; + + while ( (sc_enc->token_code != TOK_SEMICOLON) && (sc_enc->token_code != TOK_RIGHT_CURVE) ) { + switch (sc_enc->token_code) { + case TOK_CONDTEST: + nbIndir ++; + break; + case TOK_CONDSEP: + if (nbIndir > 0) nbIndir--; + /*'case'*/ + else { + goto break_loop; + } + break; + case TOK_IDENTIFIER: + case TOK_NUMBER: + case TOK_STRING: + case TOK_BOOLEAN: + gf_list_add(sc_enc->id_buf, strdup(sc_enc->token)); + break; + case TOK_FUNCTION: + goto break_loop; + } + + if (sc_enc->token_code==TOK_VAR) is_var = 1; + if (!is_var || (sc_enc->token_code!=TOK_COMMA)) { + sc_enc->expr_toks[sc_enc->expr_toks_len] = sc_enc->token_code; + sc_enc->expr_toks_len++; + } + + open_code = sc_enc->token_code; + close_code = 0; + if (sc_enc->token_code == TOK_LEFT_CURVE) close_code = TOK_RIGHT_CURVE; + else if (sc_enc->token_code == TOK_LEFT_BRACKET) close_code = TOK_RIGHT_BRACKET; + else if (sc_enc->token_code == TOK_LEFT_BRACE) close_code = TOK_RIGHT_BRACE; + + /*other expr*/ + if ((sc_enc->token_code == TOK_COMMA) && (sc_enc->expr_toks[0] != TOK_VAR) ){ + expr_sep[nbExpr++] = sc_enc->expr_toks_len - 1; + } + /*sub-expr*/ + else if (close_code) { + count++; + do { + SFE_NextToken(sc_enc); + if ((sc_enc->token_code == TOK_IDENTIFIER) || (sc_enc->token_code == TOK_NUMBER) + || (sc_enc->token_code == TOK_STRING) || (sc_enc->token_code == TOK_BOOLEAN) ) { + gf_list_add(sc_enc->id_buf, strdup(sc_enc->token)); + } + sc_enc->expr_toks[sc_enc->expr_toks_len] = sc_enc->token_code; + sc_enc->expr_toks_len++; + if (sc_enc->token_code == open_code) count++; + else if (sc_enc->token_code == close_code) count--; + } while ( (sc_enc->token_code != close_code) || count); + } + SFE_NextToken(sc_enc); + } + +break_loop: + if (sc_enc->err) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: end of compoundExpression not found\n")); + return 0; + } + expr_sep[nbExpr] = sc_enc->expr_toks_len; + if ((sc_enc->token_code == TOK_IDENTIFIER) || (sc_enc->token_code == TOK_NUMBER) + || (sc_enc->token_code == TOK_STRING) || (sc_enc->token_code == TOK_BOOLEAN) ) { + gf_list_add(sc_enc->id_buf, strdup(sc_enc->token)); + } + + if ((sc_enc->token_code != TOK_CONDSEP) && (sc_enc->token_code != TOK_RIGHT_BRACE) && (sc_enc->expr_toks[0] != TOK_VAR)) { + sc_enc->expr_toks[sc_enc->expr_toks_len] = sc_enc->token_code; + sc_enc->expr_toks_len++; + } + return nbExpr; +} + +u32 MoveToToken(ScriptEnc *sc_enc, u32 endTok, u32 cur, u32 end); + +u32 SFE_ScanExpression(ScriptEnc *sc_enc, u32 start, u32 end, u32 *expr_sep) +{ + u32 curTok; + u32 n = start; + u32 nbExpr = 1; + + expr_sep[0] = start; + while (nexpr_toks[n++]; + if (curTok == TOK_LEFT_CURVE) { + n = MoveToToken(sc_enc, TOK_RIGHT_CURVE, n-1, end); + n++; + } else if (curTok == TOK_LEFT_BRACKET) { + n = MoveToToken(sc_enc, TOK_RIGHT_BRACKET, n-1, end); + n++; + } else if (curTok == TOK_COMMA) { + expr_sep[nbExpr++] = n-1; + } + } + expr_sep[nbExpr] = end; + return nbExpr; +} + +u32 SFE_Expression(ScriptEnc *sc_enc, u32 start, u32 end, Bool memberAccess); + +void SFE_CompoundExpression(ScriptEnc *sc_enc, u32 start, u32 end, u32 isPar) +{ + u32 nbExp, i; + /*filled by indexes of ',' expressions in the expr_tok buffer*/ + u32 expr_sep[MAX_NUM_EXPR]; + + if (sc_enc->err) return; + + if (end==0) { + /*load expressions , eg "a ? ((a>b) ? 1 : 0) : 0" */ + nbExp = SFE_LoadExpression(sc_enc, expr_sep); + } else { + /*load sub-expression from loaded expression set*/ + nbExp = SFE_ScanExpression(sc_enc, start, end, expr_sep); + } + + SFE_Expression(sc_enc, expr_sep[0], expr_sep[1], 0); + for (i=1; itoken_code != TOK_SEMICOLON) { + SFE_WRITE_INT(sc_enc, 1, 1, "hasCompoundExpression", NULL); + SFE_CompoundExpression(sc_enc, 0, 0, 0); + } else { + SFE_WRITE_INT(sc_enc, 0, 1, "hasCompoundExpression", NULL); + } +} + +void SFE_IfStatement(ScriptEnc *sc_enc) +{ + char *buf_bck; + u32 tok_bck; + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_LEFT_CURVE); + SFE_NextToken(sc_enc); + SFE_CompoundExpression(sc_enc, 0, 0, 0); + SFE_CheckToken(sc_enc, TOK_RIGHT_CURVE); + SFE_StatementBlock(sc_enc); + + buf_bck = sc_enc->cur_buf; + tok_bck = sc_enc->token_code; + SFE_NextToken(sc_enc); + if (sc_enc->token_code == TOK_ELSE) { + SFE_WRITE_INT(sc_enc, 1, 1, "hasELSEStatement", NULL); + SFE_StatementBlock(sc_enc); + } else { + SFE_WRITE_INT(sc_enc, 0, 1, "hasELSEStatement", NULL); + sc_enc->cur_buf = buf_bck; + sc_enc->token_code = tok_bck; + } +} + +void SFE_ForStatement(ScriptEnc *sc_enc) +{ + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_LEFT_CURVE); + + SFE_NextToken(sc_enc); + SFE_OptionalExpression(sc_enc); + SFE_CheckToken(sc_enc, TOK_SEMICOLON); + + SFE_NextToken(sc_enc); + SFE_OptionalExpression(sc_enc); + SFE_CheckToken(sc_enc, TOK_SEMICOLON); + + SFE_NextToken(sc_enc); + SFE_OptionalExpression(sc_enc); + SFE_CheckToken(sc_enc, TOK_RIGHT_CURVE); + + SFE_StatementBlock(sc_enc); +} + +void SFE_WhileStatement(ScriptEnc *sc_enc) +{ + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_LEFT_CURVE); + SFE_NextToken(sc_enc); + SFE_CompoundExpression(sc_enc, 0, 0, 0); + SFE_CheckToken(sc_enc, TOK_RIGHT_CURVE); + + SFE_StatementBlock(sc_enc); +} + +void SFE_ReturnStatement(ScriptEnc *sc_enc) +{ + SFE_NextToken(sc_enc); + if (sc_enc->token_code != TOK_SEMICOLON) { + SFE_WRITE_INT(sc_enc, 1, 1, "returnValue", NULL); + SFE_CompoundExpression(sc_enc, 0, 0, 0); + } else { + SFE_WRITE_INT(sc_enc, 0, 1, "returnValue", NULL); + } +} + +void SFE_CaseBlock(ScriptEnc *sc_enc) +{ + SFE_WRITE_INT(sc_enc, 1, 1, "isCompoundStatement", NULL); + SFE_NextToken(sc_enc); + if (sc_enc->token_code == TOK_LEFT_BRACE) { + SFE_NextToken(sc_enc); + while (sc_enc->token_code != TOK_RIGHT_BRACE) { + SFE_WRITE_INT(sc_enc, 1, 1, "hasStatement", NULL); + SFE_Statement(sc_enc); + SFE_NextToken(sc_enc); + } + SFE_NextToken(sc_enc); + } + while ((sc_enc->token_code != TOK_CASE) && (sc_enc->token_code != TOK_DEFAULT) && (sc_enc->token_code != TOK_RIGHT_BRACE)) { + SFE_WRITE_INT(sc_enc, 1, 1, "hasStatement", NULL); + SFE_Statement(sc_enc); + SFE_NextToken(sc_enc); + } + SFE_WRITE_INT(sc_enc, 0, 1, "hasStatement", NULL); +} + +u32 SFE_PutCaseInteger(ScriptEnc *sc_enc, char *str, u32 nbBits) +{ + u32 val = 0; + if ((str[0]=='0') && (str[1]=='x' || str[1]=='X')) { + val = strtoul(sc_enc->token, (char **) NULL, 16); + } else if (str[0]=='0' && isdigit(str[1])) { + val = strtoul(str, (char **) NULL, 8); + } else if (isdigit(str[0])) { + val = strtoul(str, (char **) NULL, 10); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: %s is not an integer\n", str)); + sc_enc->err = GF_BAD_PARAM; + return 0; + } + if (!sc_enc->emul) { + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, val, nbBits, "value", sc_enc->token); + } else { + nbBits = gf_get_bit_size(val); + } + return nbBits; +} + +void SFE_SwitchStatement(ScriptEnc *sc_enc) +{ + u32 nbBits, maxBits = 0; + Bool prev_emu; + char *buf_bck; + u32 tok_bck; + + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_LEFT_CURVE); + + SFE_NextToken(sc_enc); + SFE_CompoundExpression(sc_enc, 0, 0, 0); + SFE_CheckToken(sc_enc, TOK_RIGHT_CURVE); + + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_LEFT_BRACE); + + /*first pass in emul*/ + buf_bck = sc_enc->cur_buf; + tok_bck = sc_enc->token_code; + prev_emu = sc_enc->emul; + sc_enc->emul = 1; + + SFE_NextToken(sc_enc); + while (sc_enc->token_code == TOK_CASE) { + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_NUMBER); + nbBits = SFE_PutCaseInteger(sc_enc, sc_enc->token, 0); + if (maxBitstoken_code == TOK_CASE) ? 1 : 0, 1, "hasMoreCases", NULL); + } + + /*second pass in parent mode*/ + sc_enc->cur_buf = buf_bck; + sc_enc->token_code = tok_bck; + sc_enc->emul = prev_emu; + maxBits ++; + SFE_WRITE_INT(sc_enc, maxBits, 5, "caseNbBits", NULL); + + SFE_NextToken(sc_enc); + while (sc_enc->token_code == TOK_CASE) { + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_NUMBER); + SFE_PutCaseInteger(sc_enc, sc_enc->token, maxBits); + + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_CONDSEP); + + SFE_CaseBlock(sc_enc); + SFE_WRITE_INT(sc_enc, (sc_enc->token_code == TOK_CASE) ? 1 : 0, 1, "hasMoreCases", NULL); + } + + if (sc_enc->token_code == TOK_DEFAULT) { + SFE_WRITE_INT(sc_enc, 1, 1, "hasDefault", NULL); + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_CONDSEP); + SFE_CaseBlock(sc_enc); + } else { + SFE_WRITE_INT(sc_enc, 0, 1, "hasDefault", NULL); + } + SFE_CheckToken(sc_enc, TOK_RIGHT_BRACE); +} + +void SFE_Statement(ScriptEnc *sc_enc) +{ + switch (sc_enc->token_code) { + case TOK_IF: + SFE_WRITE_INT(sc_enc, ST_IF, NUMBITS_STATEMENT, "statementType", "if"); + SFE_IfStatement(sc_enc); + break; + case TOK_FOR: + SFE_WRITE_INT(sc_enc, ST_FOR, NUMBITS_STATEMENT, "statementType", "for"); + SFE_ForStatement(sc_enc); + break; + case TOK_WHILE: + SFE_WRITE_INT(sc_enc, ST_WHILE, NUMBITS_STATEMENT, "statementType", "while"); + SFE_WhileStatement(sc_enc); + break; + case TOK_RETURN: + SFE_WRITE_INT(sc_enc, ST_RETURN, NUMBITS_STATEMENT, "statementType", "return"); + SFE_ReturnStatement(sc_enc); + break; + case TOK_BREAK: + SFE_WRITE_INT(sc_enc, ST_BREAK, NUMBITS_STATEMENT, "statementType", "break"); + SFE_NextToken(sc_enc); + break; + case TOK_CONTINUE: + SFE_WRITE_INT(sc_enc, ST_CONTINUE, NUMBITS_STATEMENT, "statementType", "continue"); + SFE_NextToken(sc_enc); + break; + case TOK_SWITCH: + SFE_WRITE_INT(sc_enc, ST_SWITCH, NUMBITS_STATEMENT, "statementType", "while"); + SFE_SwitchStatement(sc_enc); + break; + default: + SFE_WRITE_INT(sc_enc, ST_COMPOUND_EXPR, NUMBITS_STATEMENT, "statementType", "compoundExpr"); + SFE_CompoundExpression(sc_enc, 0, 0, 0); + break; + } +} + +void SFE_Statements(ScriptEnc *sc_enc) +{ + while (1) { + if (!SFE_NextToken(sc_enc)) return; + if (sc_enc->token_code == TOK_RIGHT_BRACE) break; + SFE_WRITE_INT(sc_enc, 1, 1, "hasStatement", NULL); + SFE_Statement(sc_enc); + } + SFE_WRITE_INT(sc_enc, 0, 1, "hasStatement", NULL); +} + +void SFE_StatementBlock(ScriptEnc *sc_enc) +{ + if (!SFE_NextToken(sc_enc)) return; + if (sc_enc->token_code == TOK_LEFT_BRACE) { + SFE_WRITE_INT(sc_enc, 1, 1, "isCompoundStatement", NULL); + SFE_Statements(sc_enc); + } else { + SFE_WRITE_INT(sc_enc, 0, 1, "isCompoundStatement", NULL); + SFE_Statement(sc_enc); + } +} +void SFE_Function(ScriptEnc *sc_enc) +{ + char szName[1000]; + + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_FUNCTION); + + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_IDENTIFIER); + strcpy(szName, sc_enc->token); + SFE_PutIdentifier(sc_enc, sc_enc->token); + + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_LEFT_CURVE); + + SFE_Arguments(sc_enc); + SFE_StatementBlock(sc_enc); + + if (sc_enc->err) GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: Error while parsing function %s\n", szName)); +} + + +GF_Err SFScript_Encode(GF_BifsEncoder *codec, SFScript *script_field, GF_BitStream *bs, GF_Node *n) +{ + char *ptr; + ScriptEnc sc_enc; + if (gf_node_get_tag(n) != TAG_MPEG4_Script) return GF_NON_COMPLIANT_BITSTREAM; + + memset(&sc_enc, 0, sizeof(ScriptEnc)); + sc_enc.codec = codec; + sc_enc.script = n; + sc_enc.bs = bs; + sc_enc.identifiers = gf_list_new(); + sc_enc.id_buf = gf_list_new(); + sc_enc.err = GF_OK; + + if (codec->is_encoding_command) { + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "Script::isList", NULL); + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "end", NULL); + } else { + EncScriptFields(&sc_enc); + } + /*reserevd*/ + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "reserved", NULL); + + if (script_field) { + sc_enc.cur_buf = (char *)script_field->script_text; + } else if (((M_Script*)n)->url.count) { + sc_enc.cur_buf = (char *)((M_Script*)n)->url.vals[0].script_text; + } + if (sc_enc.cur_buf) { + if (!strnicmp(sc_enc.cur_buf, "javascript:", 11) + || !strnicmp(sc_enc.cur_buf, "vrmlscript:", 11) + || !strnicmp(sc_enc.cur_buf, "ECMAScript:", 11) + ) { + sc_enc.cur_buf += 11; + } else if (!strnicmp(sc_enc.cur_buf, "mpeg4script:", 12) ) { + sc_enc.cur_buf += 12; + } + } + + /*encode functions*/ + while (sc_enc.cur_buf && sc_enc.cur_buf[0] && (sc_enc.cur_buf[0]!='}')) { + if (strchr("\r\n\t ", sc_enc.cur_buf[0]) ) { + sc_enc.cur_buf++; + continue; + } + GF_BIFS_WRITE_INT(codec, bs, 1, 1, "hasFunction", NULL); + SFE_Function(&sc_enc); + if (sc_enc.err) break; + } + GF_BIFS_WRITE_INT(codec, bs, 0, 1, "hasFunction", NULL); + + //clean up + while (gf_list_count(sc_enc.identifiers)) { + ptr = (char *)gf_list_get(sc_enc.identifiers, 0); + gf_list_rem(sc_enc.identifiers, 0); + free(ptr); + } + gf_list_del(sc_enc.identifiers); + /*in case of error this is needed*/ + while (gf_list_count(sc_enc.id_buf)) { + ptr = (char *)gf_list_get(sc_enc.id_buf, 0); + gf_list_rem(sc_enc.id_buf, 0); + free(ptr); + } + gf_list_del(sc_enc.id_buf); + + return sc_enc.err; +} + + +void SFE_PutReal(ScriptEnc *sc_enc, char *str) +{ + u32 i, length = strlen(str); + for (i=0; i= '0' && c <= '9') + { SFE_WRITE_INT(sc_enc, c-'0', 4, "floatChar", "Digital"); } + else if (c == '.') + { SFE_WRITE_INT(sc_enc, 10, 4, "floatChar", "Decimal Point"); } + else if (c == 'e' || c == 'E') + { SFE_WRITE_INT(sc_enc, 11, 4, "floatChar", "Exponential");} + else if (c == '-') + {SFE_WRITE_INT(sc_enc, 12, 4, "floatChar", "Sign");} + else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: %s is not a real number\n", str)); + sc_enc->err = GF_BAD_PARAM; + return; + } + } + SFE_WRITE_INT(sc_enc, 15, 4, "floatChar", "End Symbol"); +} + +void SFE_PutNumber(ScriptEnc *sc_enc, char *str) +{ + if (strpbrk(str,".eE-") == 0) { + SFE_WRITE_INT(sc_enc, 1, 1, "isInteger", "integer"); + SFE_PutInteger(sc_enc, str); + } else { + SFE_WRITE_INT(sc_enc, 0, 1, "isInteger", "real"); + SFE_PutReal(sc_enc, str); + } +} + +void SFE_PutBoolean(ScriptEnc *sc_enc, char *str) +{ + u32 v = 1; + if (!stricmp(str, "false") || !strcmp(str, "0")) v = 0; + SFE_WRITE_INT(sc_enc, v, 1, "value", "bolean"); +} + + +#define CHECK_TOK(x) \ + if (curTok != x) { \ + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: Token %s read, %s expected\n", tok_names[curTok], tok_names[x])); \ + sc_enc->err = GF_BAD_PARAM; \ + } \ + + +u32 TOK_To_ET(u32 tok) +{ + switch(tok) { + case TOK_INCREMENT: return ET_INCREMENT; + case TOK_DECREMENT: return ET_DECREMENT; + case TOK_NOT: return ET_NOT; + case TOK_ONESCOMP: return ET_ONESCOMP; + case TOK_MULTIPLY : return ET_MULTIPLY; + case TOK_DIVIDE : return ET_DIVIDE; + case TOK_MOD : return ET_MOD; + case TOK_PLUS : return ET_PLUS; + case TOK_MINUS : return ET_MINUS; + case TOK_LSHIFT : return ET_LSHIFT; + case TOK_RSHIFT : return ET_RSHIFT; + case TOK_RSHIFTFILL : return ET_RSHIFTFILL; + case TOK_LT : return ET_LT; + case TOK_LE : return ET_LE; + case TOK_GT : return ET_GT; + case TOK_GE : return ET_GE; + case TOK_EQ : return ET_EQ; + case TOK_NE : return ET_NE; + case TOK_AND : return ET_AND; + case TOK_XOR : return ET_XOR; + case TOK_OR : return ET_OR; + case TOK_LAND : return ET_LAND; + case TOK_LOR : return ET_LOR; + case TOK_CONDTEST : return ET_CONDTEST; + case TOK_ASSIGN : return ET_ASSIGN; + case TOK_PLUSEQ : return ET_PLUSEQ; + case TOK_MINUSEQ : return ET_MINUSEQ; + case TOK_MULTIPLYEQ : return ET_MULTIPLYEQ; + case TOK_DIVIDEEQ : return ET_DIVIDEEQ; + case TOK_MODEQ : return ET_MODEQ; + case TOK_LSHIFTEQ : return ET_LSHIFTEQ; + case TOK_RSHIFTEQ : return ET_RSHIFTEQ; + case TOK_RSHIFTFILLEQ : return ET_RSHIFTFILLEQ; + case TOK_ANDEQ : return ET_ANDEQ; + case TOK_XOREQ : return ET_XOREQ; + case TOK_OREQ : return ET_OREQ; + case TOK_FUNCTION: return ET_FUNCTION_CALL; + case TOK_VAR: + return ET_VAR; + default: + assert(0); + return (u32) -1; + } +} + +static const char *expr_name[] ={ +"ET_CURVED_EXPR", +"ET_NEGATIVE", +"ET_NOT", +"ET_ONESCOMP", +"ET_INCREMENT", +"ET_DECREMENT", +"ET_POST_INCREMENT", +"ET_POST_DECREMENT", +"ET_CONDTEST", +"ET_STRING", +"ET_NUMBER", +"ET_IDENTIFIER", +"ET_FUNCTION_CALL", +"ET_NEW", +"ET_OBJECT_MEMBER_ACCESS", +"ET_OBJECT_METHOD_CALL", +"ET_ARRAY_DEREFERENCE", +"ET_ASSIGN", +"ET_PLUSEQ", +"ET_MINUSEQ", +"ET_MULTIPLYEQ", +"ET_DIVIDEEQ", +"ET_MODEQ", +"ET_ANDEQ", +"ET_OREQ", +"ET_XOREQ", +"ET_LSHIFTEQ", +"ET_RSHIFTEQ", +"ET_RSHIFTFILLEQ", +"ET_EQ", +"ET_NE", +"ET_LT", +"ET_LE", +"ET_GT", +"ET_GE", +"ET_PLUS", +"ET_MINUS", +"ET_MULTIPLY", +"ET_DIVIDE", +"ET_MOD", +"ET_LAND", +"ET_LOR", +"ET_AND", +"ET_OR", +"ET_XOR", +"ET_LSHIFT", +"ET_RSHIFT", +"ET_RSHIFTFILL", +"ET_BOOLEAN", +"ET_VAR", +"NUMBER_OF_EXPR_TYPE" +}; + +#define NUMBER_OF_RANK 15 +static s32 ET_Rank[NUMBER_OF_EXPR_TYPE] = +{ + 1,// ET_CURVED_EXPR + 2,// ET_NEGATIVE + 2,// ET_NOT + 2,// ET_ONESCOMP + 2,// ET_INCREMENT + 2,// ET_DECREMENT + 2,// ET_POST_INCREMENT + 2,// ET_POST_DECREMENT + 14,// ET_CONDTEST + 0,// ET_STRING + 0,// ET_NUMBER + 0,// ET_IDENTIFIER + 1,// ET_FUNCTION_CALL + 2,// ET_NEW + 1,// ET_OBJECT_MEMBER_ACCESS + 1,// ET_OBJECT_METHOD_CALL + 1,// ET_ARRAY_DEREFERENCE + 14,// ET_ASSIGN + 14,// ET_PLUSEQ + 14,// ET_MINUSEQ + 14,// ET_MULTIPLYEQ + 14,// ET_DIVIDEEQ + 14,// ET_MODEQ + 14,// ET_ANDEQ + 14,// ET_OREQ + 14,// ET_XOREQ + 14,// ET_LSHIFTEQ + 14,// ET_RSHIFTEQ + 14,// ET_RSHIFTFILLEQ + 8,// ET_EQ + 9,// ET_NE + 6,// ET_LT + 6,// ET_LE + 7,// ET_GT + 7,// ET_GE + 5,// ET_PLUS + 5,// ET_MINUS + 3,// ET_MULTIPLY + 3,// ET_DIVIDE + 3,// ET_MOD + 13,// ET_LAND + 14,// ET_LOR + 10,// ET_AND + 12,// ET_OR + 11,// ET_XOR + 5,// ET_LSHIFT + 6,// ET_RSHIFT + 6,// ET_RSHIFTFILL + 0, // ET_BOOLEAN + 14 // ET_VAR + +/* + 0, 0, 0, // variable, number, string + 1, 1, 1, 1, 1, // curved expr, call, member, array + 2, 2, 2, 2, 2, 2, 2, 2, // unary operator + 3, 3, 3, // multiply, divide, mod + 4, 4, // add, subtract + 5, 5, 5, // bitwise shift + 6, 6, 6, 6, // relational + 7, 7, // equality + 8, // bitwise and + 9, // bitwise xor + 10, // bitwise or + 11, // logical and + 12, // logical or + 13, // conditional + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14 // assignment + */ +}; +static s32 ET_leftAssoc[NUMBER_OF_RANK] = {1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}; + +u32 MoveToToken(ScriptEnc *sc_enc, u32 endTok, u32 cur, u32 end) +{ + u32 cnt = 0; + u32 startTok = TOK_EOF, curTok; + + if (endTok == TOK_RIGHT_CURVE) startTok = TOK_LEFT_CURVE; + else if (endTok == TOK_RIGHT_BRACKET) startTok = TOK_LEFT_BRACKET; + else if (endTok == TOK_RIGHT_BRACE) startTok = TOK_LEFT_BRACE; + else if (endTok == TOK_CONDSEP) startTok = TOK_CONDTEST; + else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: illegal MoveToToken %s\n", tok_names[endTok])); + sc_enc->err = GF_BAD_PARAM; + return (u32) -1; + } + do { + curTok = sc_enc->expr_toks[cur++]; + if (curTok == startTok) cnt++; + else if (curTok == endTok) cnt--; + } while ( (curTok != endTok || cnt) && cur < end); + if (curTok==endTok && cnt==0) return cur-1; + return (u32) -1; +} + + +void SFE_FunctionCall(ScriptEnc *sc_enc, u32 start, u32 end); +void SFE_Params(ScriptEnc *sc_enc, u32 start, u32 end); +void SFE_ConditionTest(ScriptEnc *sc_enc, u32 start, u32 op, u32 end); +void SFE_ObjectConstruct(ScriptEnc *sc_enc, u32 start, u32 op, u32 end); +void SFE_ArrayDereference(ScriptEnc *sc_enc, u32 start, u32 op, u32 end); +void SFE_ObjectMethodCall(ScriptEnc *sc_enc, u32 start, u32 op, u32 end); +void SFE_ObjectMemberAccess(ScriptEnc *sc_enc, u32 start, u32 op, u32 end); + +u32 SFE_Expression(ScriptEnc *sc_enc, u32 start, u32 end, Bool memberAccess) +{ + char *str; + u32 n = start; + u32 curPos = 0, finalPos = 0; + u32 curTok, prevTok; + u32 curExpr = 0, expr = 0; + u32 curRank, maxRank=0; + + if (sc_enc->err) return 0; + + curTok = prevTok = sc_enc->expr_toks[n]; + do { + curPos = n; + switch (curTok) { + case TOK_IDENTIFIER: + curExpr = ET_IDENTIFIER; + break; + case TOK_NUMBER: + curExpr = ET_NUMBER; + break; + case TOK_BOOLEAN: + curExpr = ET_BOOLEAN; + break; + case TOK_STRING: + curExpr = ET_STRING; + break; + case TOK_LEFT_CURVE: + if (prevTok == TOK_IDENTIFIER) curExpr = ET_FUNCTION_CALL; + else curExpr = ET_CURVED_EXPR; + n = MoveToToken(sc_enc, TOK_RIGHT_CURVE, n, end); + curTok = TOK_RIGHT_CURVE; + break; + case TOK_LEFT_BRACKET: + curExpr = ET_ARRAY_DEREFERENCE; + n = MoveToToken(sc_enc, TOK_RIGHT_BRACKET, n, end); + curTok = TOK_RIGHT_BRACKET; + break; + case TOK_PERIOD: + curTok = sc_enc->expr_toks[++n]; + CHECK_TOK(TOK_IDENTIFIER); + if (sc_enc->expr_toks[n+1] == TOK_LEFT_CURVE) { + curExpr = ET_OBJECT_METHOD_CALL; + n = MoveToToken(sc_enc, TOK_RIGHT_CURVE, n+1, end); + curTok = TOK_RIGHT_CURVE; + } else { + curExpr = ET_OBJECT_MEMBER_ACCESS; + } + break; + case TOK_NEW: + curExpr = ET_NEW; + curTok = sc_enc->expr_toks[++n]; + CHECK_TOK(TOK_IDENTIFIER); + curTok = sc_enc->expr_toks[++n]; + CHECK_TOK(TOK_LEFT_CURVE); + n = MoveToToken(sc_enc, TOK_RIGHT_CURVE, n, end); + curTok = TOK_RIGHT_CURVE; + break; + case TOK_FUNCTION: + curExpr = ET_FUNCTION_ASSIGN; + break; + case TOK_PLUS: + if ( + prevTok==TOK_RIGHT_CURVE || prevTok==TOK_RIGHT_BRACKET || + prevTok==TOK_IDENTIFIER || prevTok==TOK_NUMBER || + prevTok==TOK_STRING || prevTok==TOK_INCREMENT || + prevTok==TOK_DECREMENT + ) { + curExpr = ET_PLUS; + } else { + goto skip_token; + } + break; + case TOK_MINUS: + if ( + prevTok==TOK_RIGHT_CURVE || prevTok==TOK_RIGHT_BRACKET || + prevTok==TOK_IDENTIFIER || prevTok==TOK_NUMBER || + prevTok==TOK_STRING || prevTok==TOK_INCREMENT || + prevTok==TOK_DECREMENT + ) { + curExpr = ET_MINUS; + } else { + curExpr = ET_NEGATIVE; + curTok = TOK_NEGATIVE; + } + break; + case TOK_INCREMENT: + curExpr = ET_INCREMENT; + break; + case TOK_DECREMENT: + curExpr = ET_DECREMENT; + break; + case TOK_NOT: + curExpr = ET_NOT; + break; + case TOK_ONESCOMP: + curExpr = ET_ONESCOMP; + break; + case TOK_CONDTEST: + curExpr = ET_CONDTEST; + break; + case TOK_CONDSEP: + break; + case TOK_LEFT_BRACE: + curExpr = ET_CURVED_EXPR; + n = MoveToToken(sc_enc, TOK_RIGHT_BRACE, n, end); + curTok = TOK_RIGHT_BRACE; + break; + break; + default: + + if (curTok && (curTok != TOK_VAR) + && (curTok < TOK_MULTIPLY || curTok > TOK_OREQ)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: illegal token %s read\n", tok_names[curTok])); + sc_enc->err = GF_BAD_PARAM; + return 0; + } + curExpr = TOK_To_ET(curTok); + } + + if (curTok == TOK_CONDSEP) { + prevTok = curTok; + curTok = sc_enc->expr_toks[++n]; + continue; + } + curRank = ET_Rank[curExpr]; + if (curRank > maxRank) { + maxRank = curRank; + expr = curExpr; + finalPos = curPos; + } else if (curRank == maxRank && ET_leftAssoc[curRank]) { + expr = curExpr; + finalPos = curPos; + } + prevTok = curTok; +skip_token: + curTok = sc_enc->expr_toks[++n]; + } while (nerr = GF_BAD_PARAM; + return expr; + } + } else if (expr == ET_DECREMENT) { + if (finalPos==start) {} + else if (finalPos==end-1) expr = ET_POST_DECREMENT; + else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: illegal Decrement expression\n")); + sc_enc->err = GF_BAD_PARAM; + return expr; + } + } + + SFE_WRITE_INT(sc_enc, expr, NUMBITS_EXPR_TYPE, "expressionType", (char *) expr_name[expr]); + + switch (expr) { + case ET_MULTIPLY: + case ET_DIVIDE: + case ET_MOD: + case ET_PLUS: + case ET_MINUS: + case ET_LSHIFT: + case ET_RSHIFT: + case ET_RSHIFTFILL: + case ET_LT: + case ET_LE: + case ET_GT: + case ET_GE: + case ET_EQ: + case ET_NE: + case ET_AND: + case ET_XOR: + case ET_OR: + case ET_LAND: + case ET_LOR: + SFE_Expression(sc_enc, start, finalPos, 0); + SFE_Expression(sc_enc, finalPos+1, end, 0); + break; + case ET_ASSIGN: + case ET_PLUSEQ: + case ET_MINUSEQ: + case ET_MULTIPLYEQ: + case ET_DIVIDEEQ: + case ET_MODEQ: + case ET_LSHIFTEQ: + case ET_RSHIFTEQ: + case ET_RSHIFTFILLEQ: + case ET_ANDEQ: + case ET_XOREQ: + case ET_OREQ: + { + u32 ret = SFE_Expression(sc_enc, start, finalPos, 0); + if ( ret != ET_IDENTIFIER && ret != ET_OBJECT_MEMBER_ACCESS && ret != ET_ARRAY_DEREFERENCE ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: LeftVariable expected, %s returned\n", expr_name[ret])); + sc_enc->err = GF_BAD_PARAM; + return expr; + } + SFE_Expression(sc_enc, finalPos+1, end, 0); + } + break; + + case ET_IDENTIFIER: + str = (char *)gf_list_get(sc_enc->id_buf, 0); + gf_list_rem(sc_enc->id_buf, 0); + /*TODO: when accessing member, we should try to translate proto fields into _fieldALL when not + using USENAMEs...*/ + if (memberAccess) { + } + SFE_PutIdentifier(sc_enc, str); + free(str); + break; + case ET_NUMBER: + str = (char *)gf_list_get(sc_enc->id_buf, 0); + gf_list_rem(sc_enc->id_buf, 0); + SFE_PutNumber(sc_enc, str); + free(str); + break; + case ET_BOOLEAN: + str = (char *)gf_list_get(sc_enc->id_buf, 0); + gf_list_rem(sc_enc->id_buf, 0); + SFE_PutBoolean(sc_enc, str); + free(str); + break; + case ET_VAR: + while (1) { + str = (char *)gf_list_get(sc_enc->id_buf, 0); + if (!str) break; + gf_list_rem(sc_enc->id_buf, 0); + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "hasArgument", NULL); + SFE_PutIdentifier(sc_enc, str); + free(str); + } + GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 0, 1, "hasArgument", NULL); + break; + case ET_STRING: + str = (char *)gf_list_get(sc_enc->id_buf, 0); + gf_list_rem(sc_enc->id_buf, 0); + if (!sc_enc->emul) gf_bifs_enc_name(sc_enc->codec, sc_enc->bs, str); + free(str); + break; + case ET_NEGATIVE: + case ET_INCREMENT: + case ET_DECREMENT: + case ET_NOT: + case ET_ONESCOMP: + SFE_Expression(sc_enc, finalPos+1, end, 0); + break; + case ET_CURVED_EXPR: + SFE_CompoundExpression(sc_enc, finalPos+1, end-1, 0); + break; + case ET_POST_INCREMENT : + case ET_POST_DECREMENT : + SFE_Expression(sc_enc, start, finalPos, 0); + break; + case ET_FUNCTION_CALL: + SFE_FunctionCall(sc_enc, start, end); + break; + case ET_OBJECT_MEMBER_ACCESS : + SFE_ObjectMemberAccess(sc_enc, start, finalPos, end); + break; + case ET_OBJECT_METHOD_CALL: + SFE_ObjectMethodCall(sc_enc, start, finalPos, end); + break; + case ET_ARRAY_DEREFERENCE: + SFE_ArrayDereference(sc_enc, start, finalPos, end); + break; + case ET_NEW: + SFE_ObjectConstruct(sc_enc, start, finalPos, end); + break; + case ET_CONDTEST: + SFE_ConditionTest(sc_enc, start, finalPos, end); + break; + case ET_FUNCTION_ASSIGN: + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_LEFT_CURVE); + + SFE_Arguments(sc_enc); + SFE_StatementBlock(sc_enc); + SFE_NextToken(sc_enc); + SFE_CheckToken(sc_enc, TOK_SEMICOLON); + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: illegal expression type %s\n", expr_name[expr])); + sc_enc->err = GF_BAD_PARAM; + break; + } + return expr; +} + + +void SFE_FunctionCall(ScriptEnc *sc_enc, u32 start, u32 end) +{ + u32 curTok; + char *str; + curTok = sc_enc->expr_toks[start++]; + CHECK_TOK(TOK_IDENTIFIER); + str = (char *)gf_list_get(sc_enc->id_buf, 0); + gf_list_rem(sc_enc->id_buf, 0); + SFE_PutIdentifier(sc_enc, str); + free(str); + curTok = sc_enc->expr_toks[start++]; + CHECK_TOK(TOK_LEFT_CURVE); + SFE_Params(sc_enc, start, end-1); + curTok = sc_enc->expr_toks[end-1]; + CHECK_TOK(TOK_RIGHT_CURVE); +} + +void SFE_ObjectMemberAccess(ScriptEnc *sc_enc, u32 start, u32 op, u32 end) +{ + u32 curTok; + char *str; + + SFE_Expression(sc_enc, start, op, 1); + curTok = sc_enc->expr_toks[op]; + CHECK_TOK(TOK_PERIOD); + curTok = sc_enc->expr_toks[end-1]; + CHECK_TOK(TOK_IDENTIFIER); + str = (char *)gf_list_get(sc_enc->id_buf, 0); + gf_list_rem(sc_enc->id_buf, 0); + SFE_PutIdentifier(sc_enc, str); + free(str); +} + +void SFE_ObjectMethodCall(ScriptEnc *sc_enc, u32 start, u32 op, u32 end) +{ + u32 curTok; + char *str; + + SFE_Expression(sc_enc, start, op, 0); + curTok = sc_enc->expr_toks[op]; + CHECK_TOK(TOK_PERIOD); + curTok = sc_enc->expr_toks[op+1]; + CHECK_TOK(TOK_IDENTIFIER); + str = (char *)gf_list_get(sc_enc->id_buf, 0); + gf_list_rem(sc_enc->id_buf, 0); + SFE_PutIdentifier(sc_enc, str); + free(str); + curTok = sc_enc->expr_toks[op+2]; + CHECK_TOK(TOK_LEFT_CURVE); + SFE_Params(sc_enc, op+3, end-1); + curTok = sc_enc->expr_toks[end-1]; + CHECK_TOK(TOK_RIGHT_CURVE); +} + +void SFE_ArrayDereference(ScriptEnc *sc_enc, u32 start, u32 op, u32 end) +{ + u32 curTok; + + SFE_Expression(sc_enc, start, op, 0); + curTok = sc_enc->expr_toks[op]; + CHECK_TOK(TOK_LEFT_BRACKET); + SFE_CompoundExpression(sc_enc, op+1, end-1, 0); + curTok = sc_enc->expr_toks[end-1]; + CHECK_TOK(TOK_RIGHT_BRACKET); +} + +void SFE_ObjectConstruct(ScriptEnc *sc_enc, u32 start, u32 op, u32 end) +{ + u32 curTok; + char *str; + + curTok = sc_enc->expr_toks[start++]; + CHECK_TOK(TOK_NEW); + curTok = sc_enc->expr_toks[start++]; + CHECK_TOK(TOK_IDENTIFIER); + str = (char *)gf_list_get(sc_enc->id_buf, 0); + gf_list_rem(sc_enc->id_buf, 0); + SFE_PutIdentifier(sc_enc, str); + free(str); + curTok = sc_enc->expr_toks[start++]; + CHECK_TOK(TOK_LEFT_CURVE); + SFE_Params(sc_enc, start, end-1); + curTok = sc_enc->expr_toks[end-1]; + CHECK_TOK(TOK_RIGHT_CURVE); +} + +void SFE_ConditionTest(ScriptEnc *sc_enc, u32 start, u32 op, u32 end) +{ + u32 curTok; + + SFE_Expression(sc_enc, start, op, 0); + curTok = sc_enc->expr_toks[op]; + CHECK_TOK(TOK_CONDTEST); + start = op+1; + op = MoveToToken(sc_enc, TOK_CONDSEP, op, end-1); + SFE_Expression(sc_enc, start, op, 0); + curTok = sc_enc->expr_toks[op]; + CHECK_TOK(TOK_CONDSEP); + SFE_Expression(sc_enc, op+1, end, 0); +} + +void SFE_Params(ScriptEnc *sc_enc, u32 start, u32 end) +{ + u32 curTok; + + curTok = sc_enc->expr_toks[start]; + if (curTok != TOK_RIGHT_CURVE) { + SFE_WRITE_INT(sc_enc, 1, 1, "hasParam", NULL); + SFE_CompoundExpression(sc_enc, start, end, 1); + } else { + SFE_WRITE_INT(sc_enc, 0, 1, "hasParam", NULL); + } +} + diff --git a/src/bifs/unquantize.c b/src/bifs/unquantize.c new file mode 100644 index 0000000..f17dcc9 --- /dev/null +++ b/src/bifs/unquantize.c @@ -0,0 +1,434 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / BIFS codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "quant.h" + +u32 gf_bifs_dec_qp14_get_bits(GF_BifsDecoder *codec) +{ + if (!codec->ActiveQP || !codec->coord_stored) return 0; + return (u32) ceil(log(codec->NumCoord+1) / log(2) ); +} + +void gf_bifs_dec_qp14_enter(GF_BifsDecoder * codec, Bool Enter) +{ + if (!codec->ActiveQP) return; + if (Enter) codec->storing_coord = 1; + else { + if (codec->storing_coord) codec->coord_stored = 1; + codec->storing_coord = 0; + } +} + +void gf_bifs_dec_qp14_reset(GF_BifsDecoder * codec) +{ + codec->coord_stored = 0; + codec->storing_coord = 0; + codec->NumCoord = 0; +} + +void gf_bifs_dec_qp14_set_length(GF_BifsDecoder * codec, u32 NbElements) +{ + if (!codec->ActiveQP || !codec->storing_coord || codec->coord_stored) return; + codec->NumCoord = NbElements; +} + +GF_Err gf_bifs_dec_qp_set(GF_BifsDecoder *codec, GF_Node *qp) +{ + assert(gf_node_get_tag(qp) == TAG_MPEG4_QuantizationParameter); + + /*if we have an active QP, push it into the stack*/ + if (codec->ActiveQP && ((GF_Node*)codec->ActiveQP != codec->scenegraph->global_qp) ) + gf_list_insert(codec->QPs, codec->ActiveQP, 0); + + codec->ActiveQP = (M_QuantizationParameter *)qp; + return GF_OK; +} + +GF_Err gf_bifs_dec_qp_remove(GF_BifsDecoder *codec, Bool ActivatePrev) +{ + if (!codec->force_keep_qp && codec->ActiveQP && ((GF_Node*)codec->ActiveQP != codec->scenegraph->global_qp) ) { + gf_node_unregister((GF_Node *) codec->ActiveQP, NULL); + } + codec->ActiveQP = NULL; + if (!ActivatePrev) return GF_OK; + + if (gf_list_count(codec->QPs)) { + codec->ActiveQP = (M_QuantizationParameter*)gf_list_get(codec->QPs, 0); + gf_list_rem(codec->QPs, 0); + } else if (codec->scenegraph->global_qp) { + codec->ActiveQP = (M_QuantizationParameter *)codec->scenegraph->global_qp; + } + return GF_OK; +} + +//parses efficient float +Fixed gf_bifs_dec_mantissa_float(GF_BifsDecoder *codec, GF_BitStream *bs) +{ + u32 mantLength, expLength, mantSign, mantissa, expSign, exponent; + unsigned char exp; + + union { + Float f; + long l; + } ft_value; + + mantLength = gf_bs_read_int(bs, 4); + if (!mantLength) return 0; + + expLength = gf_bs_read_int(bs, 3); + mantSign = gf_bs_read_int(bs, 1); + mantissa = gf_bs_read_int(bs, mantLength - 1); + + expSign = exponent = 0; + exp = 0; + + exp = 127; + if (expLength) { + expSign = gf_bs_read_int(bs, 1); + exponent = gf_bs_read_int(bs, expLength-1); + exp += (1-2*expSign)*( (1 << (expLength-1) ) + exponent); + } + + ft_value.l = mantSign << 31; + ft_value.l |= (exp & 0xff) << 23; + ft_value.l |= mantissa << 9; + return FLT2FIX(ft_value.f); +} + +//check if the quant type is on in the QP, and if so retrieves NbBits and Min Max +//specified for the field +Bool Q_IsTypeOn(M_QuantizationParameter *qp, u32 q_type, u32 *NbBits, SFVec3f *b_min, SFVec3f *b_max) +{ + switch (q_type) { + case QC_3DPOS: + if (!qp->position3DQuant) return 0; + *NbBits = qp->position3DNbBits; + b_min->x = MAX(b_min->x, qp->position3DMin.x); + b_min->y = MAX(b_min->y, qp->position3DMin.y); + b_min->z = MAX(b_min->z, qp->position3DMin.z); + b_max->x = MIN(b_max->x, qp->position3DMax.x); + b_max->y = MIN(b_max->y, qp->position3DMax.y); + b_max->z = MIN(b_max->z, qp->position3DMax.z); + return 1; + case QC_2DPOS: + if (!qp->position2DQuant) return 0; + *NbBits = qp->position2DNbBits; + b_min->x = MAX(b_min->x, qp->position2DMin.x); + b_min->y = MAX(b_min->y, qp->position2DMin.y); + b_max->x = MIN(b_max->x, qp->position2DMax.x); + b_max->y = MIN(b_max->y, qp->position2DMax.y); + return 1; + case QC_ORDER: + if (!qp->drawOrderQuant) return 0; + *NbBits = qp->drawOrderNbBits; + b_min->x = MAX(b_min->x, qp->drawOrderMin); + b_max->x = MIN(b_max->x, qp->drawOrderMax); + return 1; + case QC_COLOR: + if (!qp->colorQuant) return 0; + *NbBits = qp->colorNbBits; + b_min->x = b_min->y = b_min->z = MAX(b_min->x, qp->colorMin); + b_max->x = b_max->y = b_max->z = MIN(b_max->x, qp->colorMax); + return 1; + case QC_TEXTURE_COORD: + if (!qp->textureCoordinateQuant) return 0; + *NbBits = qp->textureCoordinateNbBits; + b_min->x = b_min->y = b_min->z = MAX(b_min->x, qp->textureCoordinateMin); + b_max->x = b_max->y = b_max->z = MIN(b_max->x, qp->textureCoordinateMax); + return 1; + case QC_ANGLE: + if (!qp->angleQuant) return 0; + *NbBits = qp->angleNbBits; + b_min->x = b_min->y = b_min->z = MAX(b_min->x, qp->angleMin); + b_max->x = b_max->y = b_max->z = MIN(b_max->x, qp->angleMax); + return 1; + case QC_SCALE: + if (!qp->scaleQuant) return 0; + *NbBits = qp->scaleNbBits; + b_min->x = b_min->y = b_min->z = MAX(b_min->x, qp->scaleMin); + b_max->x = b_max->y = b_max->z = MIN(b_max->x, qp->scaleMax); + return 1; + case QC_INTERPOL_KEYS: + if (!qp->keyQuant) return 0; + *NbBits = qp->keyNbBits; + b_min->x = MAX(b_min->x, qp->keyMin); + b_min->y = MAX(b_min->y, qp->keyMin); + b_min->z = MAX(b_min->z, qp->keyMin); + b_max->x = MIN(b_max->x, qp->keyMax); + b_max->y = MIN(b_max->y, qp->keyMax); + b_max->z = MIN(b_max->z, qp->keyMax); + return 1; + case QC_NORMALS: + if (!qp->normalQuant) return 0; + *NbBits = qp->normalNbBits; + b_min->x = b_min->y = b_min->z = 0; + b_max->x = b_max->y = b_max->z = FIX_ONE; + return 1; + case QC_ROTATION: + if (!qp->normalQuant) return 0; + *NbBits = qp->normalNbBits; + b_min->x = b_min->y = b_min->z = 0; + b_max->x = b_max->y = b_max->z = FIX_ONE; + return 1; + case QC_SIZE_3D: + if (!qp->sizeQuant) return 0; + *NbBits = qp->sizeNbBits; + b_min->x = b_min->y = b_min->z = MAX(b_min->x, qp->sizeMin); + b_max->x = b_max->y = b_max->z = MIN(b_max->x, qp->sizeMax); + return 1; + case QC_SIZE_2D: + if (!qp->sizeQuant) return 0; + *NbBits = qp->sizeNbBits; + b_min->x = b_min->y = b_min->z = MAX(b_min->x, qp->sizeMin); + b_max->x = b_max->y = b_max->z = MIN(b_max->x, qp->sizeMax); + return 1; + + //cf specs, from here ALWAYS ON + case QC_LINEAR_SCALAR: + //nbBits is the one from the FCT - DO NOT CHANGE IT + return 1; + case QC_COORD_INDEX: + //nbBits has to be recomputed on the fly + return 1; + case QC_RESERVED: + *NbBits = 0; + return 1; + default: + return 0; + } +} + + +//Linear inverse Quantization for floats +Fixed Q_InverseQuantize(Fixed Min, Fixed Max, u32 NbBits, u32 value) +{ + if (!value) return Min; + if (value == (u32) ((1 << NbBits) - 1) ) return Max; + return Min + gf_muldiv(Max - Min, INT2FIX(value), INT2FIX( (1 << NbBits) - 1) ); +} + + +GF_Err Q_DecFloat(GF_BifsDecoder *codec, GF_BitStream *bs, u32 FieldType, SFVec3f BMin, SFVec3f BMax, u32 NbBits, void *field_ptr) +{ + switch (FieldType) { + case GF_SG_VRML_SFINT32: + return GF_NON_COMPLIANT_BITSTREAM; + case GF_SG_VRML_SFFLOAT: + *((SFFloat *)field_ptr) = Q_InverseQuantize(BMin.x, BMax.x, NbBits, gf_bs_read_int(bs, NbBits)); + return GF_OK; + case GF_SG_VRML_SFVEC2F: + ((SFVec2f *)field_ptr)->x = Q_InverseQuantize(BMin.x, BMax.x, NbBits, gf_bs_read_int(bs, NbBits)); + ((SFVec2f *)field_ptr)->y = Q_InverseQuantize(BMin.y, BMax.y, NbBits, gf_bs_read_int(bs, NbBits)); + return GF_OK; + case GF_SG_VRML_SFVEC3F: + ((SFVec3f *)field_ptr)->x = Q_InverseQuantize(BMin.x, BMax.x, NbBits, gf_bs_read_int(bs, NbBits)); + ((SFVec3f *)field_ptr)->y = Q_InverseQuantize(BMin.y, BMax.y, NbBits, gf_bs_read_int(bs, NbBits)); + ((SFVec3f *)field_ptr)->z = Q_InverseQuantize(BMin.z, BMax.z, NbBits, gf_bs_read_int(bs, NbBits)); + return GF_OK; + case GF_SG_VRML_SFCOLOR: + ((SFColor *)field_ptr)->red = Q_InverseQuantize(BMin.x, BMax.x, NbBits, gf_bs_read_int(bs, NbBits)); + ((SFColor *)field_ptr)->green = Q_InverseQuantize(BMin.y, BMax.y, NbBits, gf_bs_read_int(bs, NbBits)); + ((SFColor *)field_ptr)->blue = Q_InverseQuantize(BMin.z, BMax.z, NbBits, gf_bs_read_int(bs, NbBits)); + return GF_OK; + + case GF_SG_VRML_SFROTATION: + //forbidden in this Q mode + return GF_NON_COMPLIANT_BITSTREAM; + } + return GF_OK; +} + +//int in quant are either Linear Scalar fields or CoordIndex +//the quant is just a bitshifting into [0, 2^NbBits-1] +//so IntMin + ReadBit(NbBits) = value +GF_Err Q_DecInt(GF_BifsDecoder *codec, GF_BitStream *bs, u32 QType, SFInt32 b_min, u32 NbBits, void *field_ptr) +{ + switch (QType) { + case QC_LINEAR_SCALAR: + case QC_COORD_INDEX: + *((SFInt32 *)field_ptr) = gf_bs_read_int(bs, NbBits) + b_min; + return GF_OK; + default: + return GF_NON_COMPLIANT_BITSTREAM; + } +} + +//SFRotation and SFVec3f are quantized as normalized vectors ,mapped on a cube +//in the UnitSphere (R=1.0) +GF_Err Q_DecCoordOnUnitSphere(GF_BifsDecoder *codec, GF_BitStream *bs, u32 NbBits, u32 NbComp, Fixed *m_ft) +{ + u32 i, orient, value, sign; + Fixed tang[4], delta; + s32 dir; + + if (NbComp != 2 && NbComp != 3) return GF_BAD_PARAM; + + //only 2 or 3 comp in the quantized version + dir = 1; + if(NbComp == 2) dir -= 2 * gf_bs_read_int(bs, 1); + + orient = gf_bs_read_int(bs, 2); + + for(i=0; i= 0) ? 1 : -1; + m_ft[i] = sign * Q_InverseQuantize(0, 1, NbBits-1, sign*value); + } + delta = 1; + for (i=0; ix = comp[1]; + ((SFRotation *)field_ptr)->y = comp[2]; + ((SFRotation *)field_ptr)->z = comp[3]; + ((SFRotation *)field_ptr)->q = q; + return GF_OK; +} + +//parses a Normal vec +GF_Err Q_DecNormal(GF_BifsDecoder *codec, GF_BitStream *bs, u32 NbBits, void *field_ptr) +{ + Fixed comp[3]; + SFVec3f v; + GF_Err e; + e = Q_DecCoordOnUnitSphere(codec, bs, NbBits, 2, comp); + if (e) return e; + v.x = comp[0]; + v.y = comp[1]; + v.z = comp[2]; + gf_vec_norm(&v); + *((SFVec3f *)field_ptr) = v; + return GF_OK; +} + +GF_Err gf_bifs_dec_unquant_field(GF_BifsDecoder *codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +{ + Bool HasQ; + u8 QType, AType; + u32 NbBits; + Fixed b_min, b_max; + SFVec3f BMin, BMax; + GF_Err e; + + /*check QP*/ + if (!codec->ActiveQP) return GF_EOS; + /*check FieldType*/ + switch (field->fieldType) { + case GF_SG_VRML_SFINT32: + case GF_SG_VRML_SFFLOAT: + case GF_SG_VRML_SFROTATION: + case GF_SG_VRML_SFVEC2F: + case GF_SG_VRML_SFVEC3F: + break; + case GF_SG_VRML_SFCOLOR: + break; + default: + return GF_EOS; + } + + /*check NDT*/ + HasQ = gf_bifs_get_aq_info(node, field->fieldIndex, &QType, &AType, &b_min, &b_max, &NbBits); + if (!HasQ || !QType) return GF_EOS; + + /*get NbBits for QP14 (QC_COORD_INDEX)*/ + if (QType == QC_COORD_INDEX) { + NbBits = gf_bifs_dec_qp14_get_bits(codec); + /*QP14 is always on, not having NbBits set means the coord field is set after the index field, hence not decodable*/ + if (!NbBits) return GF_NON_COMPLIANT_BITSTREAM; + } + + BMin.x = BMin.y = BMin.z = b_min; + BMax.x = BMax.y = BMax.z = b_max; + + /*check is the QP is on and retrieves the bounds*/ + if (!Q_IsTypeOn(codec->ActiveQP, QType, &NbBits, &BMin, &BMax)) return GF_EOS; + + /*ok the field is Quantized, dequantize*/ + switch (QType) { + //these are all SFFloat quantized on n fields + case QC_3DPOS: + case QC_2DPOS: + case QC_ORDER: + case QC_COLOR: + case QC_TEXTURE_COORD: + case QC_ANGLE: + case QC_SCALE: + case QC_INTERPOL_KEYS: + case QC_SIZE_3D: + case QC_SIZE_2D: + e = Q_DecFloat(codec, bs, field->fieldType, BMin, BMax, NbBits, field->far_ptr); + break; + //SFInt types + case QC_LINEAR_SCALAR: + case QC_COORD_INDEX: + e = Q_DecInt(codec, bs, QType, (SFInt32) b_min, NbBits, field->far_ptr); + break; + //normalized fields (normals and vectors) + case QC_NORMALS: + //normal quant is only for SFVec3F + if (field->fieldType != GF_SG_VRML_SFVEC3F) return GF_NON_COMPLIANT_BITSTREAM; + e = Q_DecNormal(codec, bs, NbBits, field->far_ptr); + break; + case QC_ROTATION: + //normal quant is only for SFRotation + if (field->fieldType != GF_SG_VRML_SFROTATION) return GF_NON_COMPLIANT_BITSTREAM; + e = Q_DecRotation(codec, bs, NbBits, field->far_ptr); + break; + default: + return GF_BAD_PARAM; + } + + if (e) return e; + return GF_OK; +} + diff --git a/src/compositor/audio_input.c b/src/compositor/audio_input.c new file mode 100644 index 0000000..ad2e7bf --- /dev/null +++ b/src/compositor/audio_input.c @@ -0,0 +1,262 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#define MIN_RESYNC_TIME 500 + +static char *gf_audio_input_fetch_frame(void *callback, u32 *size, u32 audio_delay_ms) +{ + char *frame; + u32 obj_time, ts; + s32 drift; + GF_AudioInput *ai = (GF_AudioInput *) callback; + /*even if the stream is signaled as finished we must check it, because it may have been restarted by a mediaControl*/ + if (!ai->stream) return NULL; + + frame = gf_mo_fetch_data(ai->stream, 0, &ai->stream_finished, &ts, size); + /*invalidate scene on end of stream to refresh audio graph*/ + if (ai->stream_finished) gf_sc_invalidate(ai->compositor, NULL); + + /*no more data or not enough data, reset syncro drift*/ + if (!frame) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Audio Input] No data in audio object (eos %d)\n", ai->stream_finished)); + gf_mo_adjust_clock(ai->stream, 0); + return NULL; + } + ai->need_release = 1; + + + gf_mo_get_object_time(ai->stream, &obj_time); + obj_time += audio_delay_ms; + drift = (s32)obj_time; + drift -= (s32)ts; + + /*too early (silence insertions), skip*/ + if (drift + (s32) (audio_delay_ms + ai->is_open) < 0) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Audio Input] audio too early %d (CTS %d)\n", drift + audio_delay_ms + MIN_RESYNC_TIME, ts)); + ai->need_release = 0; + gf_mo_release_data(ai->stream, 0, 0); + return NULL; + } + /*adjust drift*/ + if (audio_delay_ms) { + /*CU is way too late, discard and fetch a new one - this usually happen when media speed is more than 1*/ + if (drift>500) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Audio Input] Audio data too late (drift %d ms) - resync forced\n", drift)); + gf_mo_release_data(ai->stream, *size, 2); + ai->need_release = 0; + return gf_audio_input_fetch_frame(callback, size, audio_delay_ms); + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Audio Input] Audio clock: delay %d - obj time %d - CTS %d - adjust drift %d\n", audio_delay_ms, obj_time - audio_delay_ms, ts, drift)); + gf_mo_adjust_clock(ai->stream, drift); + } + return frame; +} + +static void gf_audio_input_release_frame(void *callback, u32 nb_bytes) +{ + GF_AudioInput *ai = (GF_AudioInput *) callback; + if (!ai->stream) return; + gf_mo_release_data(ai->stream, nb_bytes, 1); + ai->need_release = 0; + /*as soon as we have released a frame for this audio stream, update resynbc tolerance*/ + ai->is_open = MIN_RESYNC_TIME; +} + +static Fixed gf_audio_input_get_speed(void *callback) +{ + GF_AudioInput *ai = (GF_AudioInput *) callback; + return gf_mo_get_speed(ai->stream, ai->speed); +} + +static Bool gf_audio_input_get_volume(void *callback, Fixed *vol) +{ + GF_AudioInput *ai = (GF_AudioInput *) callback; + if (ai->snd && ai->snd->GetChannelVolume) { + return ai->snd->GetChannelVolume(ai->snd->owner, vol); + } else { + vol[0] = vol[1] = vol[2] = vol[3] = vol[4] = vol[5] = ai->intensity; + return (ai->intensity==FIX_ONE) ? 0 : 1; + } +} + +static Bool gf_audio_input_is_muted(void *callback) +{ + GF_AudioInput *ai = (GF_AudioInput *) callback; + if (!ai->stream) return 1; + if (ai->is_muted) + return 1; + return gf_mo_is_muted(ai->stream); +} + +static Bool gf_audio_input_get_config(GF_AudioInterface *aifc, Bool for_recf) +{ + GF_AudioInput *ai = (GF_AudioInput *) aifc->callback; + if (!ai->stream) return 0; + /*watchout for object reuse*/ + if (aifc->samplerate && (gf_mo_get_flags(ai->stream) & GF_MO_IS_INIT)) return 1; + if (!for_recf) + return 0; + + gf_mo_get_audio_info(ai->stream, &aifc->samplerate, &aifc->bps , &aifc->chan, &aifc->ch_cfg); + + if (aifc->samplerate * aifc->chan * aifc->bps && ((aifc->chan<=2) || aifc->ch_cfg)) { + gf_mo_set_flag(ai->stream, GF_MO_IS_INIT, 1); + return 1; + } + gf_mo_set_flag(ai->stream, GF_MO_IS_INIT, 0); + return 0; +} + +GF_EXPORT +void gf_sc_audio_setup(GF_AudioInput *ai, GF_Compositor *compositor, GF_Node *node) +{ + memset(ai, 0, sizeof(GF_AudioInput)); + ai->owner = node; + ai->compositor = compositor; + ai->stream = NULL; + /*setup io interface*/ + ai->input_ifce.FetchFrame = gf_audio_input_fetch_frame; + ai->input_ifce.ReleaseFrame = gf_audio_input_release_frame; + ai->input_ifce.GetConfig = gf_audio_input_get_config; + ai->input_ifce.GetChannelVolume = gf_audio_input_get_volume; + ai->input_ifce.GetSpeed = gf_audio_input_get_speed; + ai->input_ifce.IsMuted = gf_audio_input_is_muted; + ai->input_ifce.callback = ai; + ai->intensity = FIX_ONE; + + ai->speed = FIX_ONE; +} + + + +GF_EXPORT +GF_Err gf_sc_audio_open(GF_AudioInput *ai, MFURL *url, Double clipBegin, Double clipEnd) +{ + if (ai->is_open) return GF_BAD_PARAM; + + /*get media object*/ + ai->stream = gf_mo_register(ai->owner, url, 0); + /*bad URL*/ + if (!ai->stream) return GF_NOT_SUPPORTED; + + /*store url*/ + gf_sg_vrml_field_copy(&ai->url, url, GF_SG_VRML_MFURL); + + /*request play*/ + gf_mo_play(ai->stream, clipBegin, clipEnd, 0); + + ai->stream_finished = 0; + ai->is_open = 1; + gf_mo_set_flag(ai->stream, GF_MO_IS_INIT, 0); + return GF_OK; +} + +GF_EXPORT +void gf_sc_audio_stop(GF_AudioInput *ai) +{ + if (!ai->is_open) return; + + /*we must make sure audio mixer is not using the stream otherwise we may leave it dirty (with unrelease frame)*/ + gf_mixer_lock(ai->compositor->audio_renderer->mixer, 1); + + assert(!ai->need_release); + + gf_mo_stop(ai->stream); + gf_sg_vrml_mf_reset(&ai->url, GF_SG_VRML_MFURL); + ai->is_open = 0; + gf_mo_unregister(ai->owner, ai->stream); + ai->stream = NULL; + + gf_mixer_lock(ai->compositor->audio_renderer->mixer, 0); + +} + +GF_EXPORT +void gf_sc_audio_restart(GF_AudioInput *ai) +{ + if (!ai->is_open) return; + if (ai->need_release) gf_mo_release_data(ai->stream, 0xFFFFFFFF, 2); + ai->need_release = 0; + ai->stream_finished = 0; + gf_mo_restart(ai->stream); +} + +GF_EXPORT +Bool gf_sc_audio_check_url(GF_AudioInput *ai, MFURL *url) +{ + if (!ai->stream) return url->count; + return gf_mo_url_changed(ai->stream, url); +} + +GF_EXPORT +void gf_sc_audio_register(GF_AudioInput *ai, GF_TraverseState *tr_state) +{ + /*check interface is valid*/ + if (!ai->input_ifce.FetchFrame + || !ai->input_ifce.GetChannelVolume + || !ai->input_ifce.GetConfig + || !ai->input_ifce.GetSpeed + || !ai->input_ifce.IsMuted + || !ai->input_ifce.ReleaseFrame + ) return; + + if (tr_state->audio_parent) { + /*this assume only one parent may use an audio node*/ + if (ai->register_with_parent) return; + if (ai->register_with_renderer) { + gf_sc_ar_remove_src(ai->compositor->audio_renderer, &ai->input_ifce); + ai->register_with_renderer = 0; + } + tr_state->audio_parent->add_source(tr_state->audio_parent, ai); + ai->register_with_parent = 1; + ai->snd = tr_state->sound_holder; + } else if (!ai->register_with_renderer) { + + if (ai->register_with_parent) { + ai->register_with_parent = 0; + /*if used in a parent audio group, do a complete traverse to rebuild the group*/ + gf_sc_invalidate(ai->compositor, NULL); + } + + gf_sc_ar_add_src(ai->compositor->audio_renderer, &ai->input_ifce); + ai->register_with_renderer = 1; + ai->snd = tr_state->sound_holder; + } +} + +GF_EXPORT +void gf_sc_audio_unregister(GF_AudioInput *ai) +{ + if (ai->register_with_renderer) { + ai->register_with_renderer = 0; + gf_sc_ar_remove_src(ai->compositor->audio_renderer, &ai->input_ifce); + } else { + /*if used in a parent audio group, do a complete traverse to rebuild the group*/ + gf_sc_invalidate(ai->compositor, NULL); + } +} + + diff --git a/src/compositor/audio_mixer.c b/src/compositor/audio_mixer.c new file mode 100644 index 0000000..5a35211 --- /dev/null +++ b/src/compositor/audio_mixer.c @@ -0,0 +1,760 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/*max number of channels we support in mixer*/ +#define GF_SR_MAX_CHANNELS 16 + +/* + Notes about the mixer: + 1- spatialization is out of scope for the mixer (eg that's the sound node responsability) + 2- mixing is performed by resampling input source & deinterleaving its channels into dedicated buffer. + We could directly deinterleave in the main mixer ouput buffer, but this would prevent any future + gain correction. +*/ +typedef struct +{ + GF_AudioInterface *src; + + s32 *ch_buf[GF_SR_MAX_CHANNELS]; + /*resampled buffer*/ + u32 buffer_size; + + u32 bytes_per_sec; + + Bool has_prev; + s32 last_channels[GF_SR_MAX_CHANNELS]; + + u32 in_bytes_used, out_samples_written, out_samples_to_write; + + Fixed speed; + Fixed pan[6]; +} MixerInput; + +struct __audiomix +{ + /*src*/ + GF_List *sources; + /*output config*/ + u32 sample_rate; + u32 nb_channels; + u32 bits_per_sample; + u32 channel_cfg; + GF_Mutex *mx; + /*if set forces stereo/mono*/ + Bool force_channel_out; + /*set to true by mixer when detecting an audio config change*/ + Bool must_reconfig; + Bool isEmpty; + /*set to non null if this outputs directly to the driver, in which case audio formats have to be checked*/ + struct _audio_render *ar; + + s32 *output; + u32 output_size; +}; + +GF_AudioMixer *gf_mixer_new(struct _audio_render *ar) +{ + GF_AudioMixer *am; + am = (GF_AudioMixer *) malloc(sizeof(GF_AudioMixer)); + if (!am) return NULL; + memset(am, 0, sizeof(GF_AudioMixer)); + am->mx = gf_mx_new("AudioMix"); + am->sources = gf_list_new(); + am->isEmpty = 1; + am->ar = ar; + am->sample_rate = 44100; + am->bits_per_sample = 16; + am->nb_channels = 2; + am->output = NULL; + am->output_size = 0; + return am; +} + +Bool gf_mixer_must_reconfig(GF_AudioMixer *am) +{ + return am->must_reconfig; +} + +void gf_mixer_del(GF_AudioMixer *am) +{ + gf_list_del(am->sources); + gf_mx_del(am->mx); + if (am->output) free(am->output); + free(am); +} + +void gf_mixer_remove_all(GF_AudioMixer *am) +{ + u32 j; + gf_mixer_lock(am, 1); + while (gf_list_count(am->sources)) { + MixerInput *in = (MixerInput *)gf_list_get(am->sources, 0); + gf_list_rem(am->sources, 0); + for (j=0; jch_buf[j]) free(in->ch_buf[j]); + } + free(in); + } + am->isEmpty = 1, + gf_mixer_lock(am, 0); +} + +Bool gf_mixer_is_src_present(GF_AudioMixer *am, GF_AudioInterface *ifce) +{ + MixerInput *in; + u32 i = 0; + while ((in = (MixerInput *)gf_list_enum(am->sources, &i))) { + if (in->src == ifce) return 1; + } + return 0; +} +u32 gf_mixer_get_src_count(GF_AudioMixer *am) +{ + return gf_list_count(am->sources); +} + +void gf_mixer_force_chanel_out(GF_AudioMixer *am, u32 num_channels) +{ + am->force_channel_out = 1; + am->nb_channels = num_channels; +} + +u32 gf_mixer_get_block_align(GF_AudioMixer *am) +{ + return am->nb_channels*am->bits_per_sample/8; +} + +void gf_mixer_lock(GF_AudioMixer *am, Bool lockIt) +{ + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[AudioMixer] Thread ID %d is %s the audio mixer\n", gf_th_id(), lockIt ? "locking" : "unlocking" )); + if (lockIt) { + gf_mx_p(am->mx); + } else { + gf_mx_v(am->mx); + } +} + +Bool gf_mixer_empty(GF_AudioMixer *am) +{ + return am->isEmpty; +} + +void gf_mixer_add_input(GF_AudioMixer *am, GF_AudioInterface *src) +{ + MixerInput *in; + if (gf_mixer_is_src_present(am, src)) return; + gf_mixer_lock(am, 1); + GF_SAFEALLOC(in, MixerInput); + in->src = src; + gf_list_add(am->sources, in); + am->must_reconfig = 1; + am->isEmpty = 0; + gf_mixer_lock(am, 0); +} + +void gf_mixer_remove_input(GF_AudioMixer *am, GF_AudioInterface *src) +{ + u32 i, j, count; + if (am->isEmpty) return; + gf_mixer_lock(am, 1); + count = gf_list_count(am->sources); + for (i=0; isources, i); + if (in->src != src) continue; + gf_list_rem(am->sources, i); + for (j=0; jch_buf[j]) free(in->ch_buf[j]); + } + free(in); + break; + } + am->isEmpty = gf_list_count(am->sources) ? 0 : 1; + /*we don't ask for reconfig when removing a node*/ + gf_mixer_lock(am, 0); +} + + +static GF_Err get_best_samplerate(GF_AudioMixer *am, u32 *out_sr, u32 *out_ch, u32 *out_bps) +{ + if (!am->ar) return GF_OK; + if (!am->ar->audio_out || !am->ar->audio_out->QueryOutputSampleRate) return GF_OK; + return am->ar->audio_out->QueryOutputSampleRate(am->ar->audio_out, out_sr, out_ch, out_bps); +} + +void gf_mixer_get_config(GF_AudioMixer *am, u32 *outSR, u32 *outCH, u32 *outBPS, u32 *outChCfg) +{ + (*outBPS) = am->bits_per_sample; + (*outCH) = am->nb_channels; + (*outSR) = am->sample_rate; + (*outChCfg) = am->channel_cfg; +} + +void gf_mixer_set_config(GF_AudioMixer *am, u32 outSR, u32 outCH, u32 outBPS, u32 outChCfg) +{ + if ((am->bits_per_sample == outBPS) && (am->nb_channels == outCH) + && (am->sample_rate==outSR) && (am->channel_cfg == outChCfg)) return; + + gf_mixer_lock(am, 1); + am->bits_per_sample = outBPS; + if (!am->force_channel_out) am->nb_channels = outCH; + if (get_best_samplerate(am, &outSR, &outCH, &outBPS) == GF_OK) { + am->sample_rate = outSR; + if (outCH>2) am->channel_cfg = outChCfg; + else if (outCH==2) am->channel_cfg = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT; + else am->channel_cfg = GF_AUDIO_CH_FRONT_LEFT; + } + /*if main mixer recfg output*/ + if (am->ar) am->ar->need_reconfig = 1; + gf_mixer_lock(am, 0); +} + +Bool gf_mixer_reconfig(GF_AudioMixer *am) +{ + u32 i, count, numInit, max_sample_rate, max_channels, max_bps, cfg_changed, ch_cfg; + gf_mixer_lock(am, 1); + if (am->isEmpty || !am->must_reconfig) { + gf_mixer_lock(am, 0); + return 0; + } + numInit = 0; + max_sample_rate = am->sample_rate; + max_channels = am->nb_channels; + max_bps = am->bits_per_sample; + cfg_changed = 0; + ch_cfg = 0; + + max_sample_rate = 0, + + count = gf_list_count(am->sources); + assert(count); + for (i=0; isources, i); + has_cfg = in->src->GetConfig(in->src, 1); + if (has_cfg) { + /*check same cfg...*/ + if (in->src->samplerate * in->src->chan * in->src->bps == 8*in->bytes_per_sec) { + numInit++; + continue; + } + } else continue; + /*update out cfg*/ + if ((count==1) && (max_sample_rate != in->src->samplerate)) { +// cfg_changed = 1; + max_sample_rate = in->src->samplerate; + } else if (max_sample_ratesrc->samplerate) { +// cfg_changed = 1; + max_sample_rate = in->src->samplerate; + } + if ((count==1) && (max_bps!=in->src->bps)) { + cfg_changed = 1; + max_bps = in->src->bps; + } else if (max_bpssrc->bps) { + cfg_changed = 1; + max_bps = in->src->bps; + } + if (!am->force_channel_out) { + if ((count==1) && (max_channels!=in->src->chan)) { + cfg_changed = 1; + max_channels = in->src->chan; + if (in->src->chan>2) ch_cfg |= in->src->ch_cfg; + } else if (max_channels < in->src->chan) { + cfg_changed = 1; + max_channels = in->src->chan; + if (in->src->chan>2) ch_cfg |= in->src->ch_cfg; + } + } + + numInit++; + in->bytes_per_sec = in->src->samplerate * in->src->chan * in->src->bps / 8; + /*cfg has changed, we must reconfig everything*/ + if (cfg_changed || (max_sample_rate != am->sample_rate) ) { + in->has_prev = 0; + memset(&in->last_channels, 0, sizeof(s16)*GF_SR_MAX_CHANNELS); + } + } + + if (cfg_changed || (max_sample_rate && (max_sample_rate != am->sample_rate)) ) { + if (max_channels>2) { + if (ch_cfg != am->channel_cfg) { + /*recompute num channel based on all input channels*/ + max_channels = 0; + if (ch_cfg & GF_AUDIO_CH_FRONT_LEFT) max_channels ++; + if (ch_cfg & GF_AUDIO_CH_FRONT_RIGHT) max_channels ++; + if (ch_cfg & GF_AUDIO_CH_FRONT_CENTER) max_channels ++; + if (ch_cfg & GF_AUDIO_CH_LFE) max_channels ++; + if (ch_cfg & GF_AUDIO_CH_BACK_LEFT) max_channels ++; + if (ch_cfg & GF_AUDIO_CH_BACK_RIGHT) max_channels ++; + if (ch_cfg & GF_AUDIO_CH_BACK_CENTER) max_channels ++; + if (ch_cfg & GF_AUDIO_CH_SIDE_LEFT) max_channels ++; + if (ch_cfg & GF_AUDIO_CH_SIDE_RIGHT) max_channels ++; + } + } else { + ch_cfg = GF_AUDIO_CH_FRONT_LEFT; + if (max_channels==2) ch_cfg |= GF_AUDIO_CH_FRONT_RIGHT; + } + gf_mixer_set_config(am, max_sample_rate, max_channels, max_bps, ch_cfg); + } + + if (numInit == count) am->must_reconfig = 0; + + gf_mixer_lock(am, 0); + return cfg_changed; +} + +static GFINLINE u32 get_channel_out_pos(u32 in_ch, u32 out_cfg) +{ + u32 i, cfg, pos; + pos = 0; + for (i=0; i<9; i++) { + cfg = 1<<(i); + if (out_cfg & cfg) { + if (cfg == in_ch) return pos; + pos++; + } + } + return GF_SR_MAX_CHANNELS; +} + +/*this is crude, we'd need a matrix or something*/ +static GFINLINE void gf_mixer_map_channels(s32 *inChan, u32 nb_in, u32 in_cfg, u32 nb_out, u32 out_cfg) +{ + u32 i; + if (nb_in==1) { + /*mono to stereo*/ + if (nb_out==2) { + inChan[1] = inChan[0]; + } + else if (nb_out>2) { + /*if center channel use it (we assume we always have stereo channels)*/ + if (out_cfg & GF_AUDIO_CH_FRONT_CENTER) { + inChan[2] = inChan[0]; + inChan[0] = 0; + for (i=3; i>=1; + /*done*/ + if (ch==10) return; + } + pos = get_channel_out_pos((1<>=1; + } + for (i=nb_in; inb_out) { + s32 bckup[GF_SR_MAX_CHANNELS]; + u32 pos; + u32 cfg = in_cfg; + u32 ch = 0; + memcpy(bckup, inChan, sizeof(s32)*nb_in); + for (i=0; i>=1; + /*done*/ + if (ch==10) return; + } + pos = get_channel_out_pos( (1<>=1; + } + } +} + + +static void gf_mixer_fetch_input(GF_AudioMixer *am, MixerInput *in, u32 audio_delay) +{ + u32 i, j, in_ch, out_ch, prev, next, src_samp, ratio, src_size; + Bool use_prev; + s16 *in_s16; + s8 *in_s8; + s32 frac, inChan[GF_SR_MAX_CHANNELS], inChanNext[GF_SR_MAX_CHANNELS]; + + in_s8 = (s8 *) in->src->FetchFrame(in->src->callback, &src_size, audio_delay); + if (!in_s8) { + in->has_prev = 0; + /*done, stop fill*/ + in->out_samples_to_write = 0; + return; + } + + ratio = (u32) (in->src->samplerate * FIX2INT(255*in->speed) / am->sample_rate); + src_samp = (u32) (src_size * 8 / in->src->bps / in->src->chan); + in_ch = in->src->chan; + out_ch = am->nb_channels; + if (in->src->bps == 8) { + in_s16 = NULL; + } else { + in_s16 = (s16 *) in_s8; + in_s8 = NULL; + } + + /*just in case, if only 1 sample available in src, copy over and discard frame since we cannot + interpolate audio*/ + if (src_samp==1) { + in->has_prev = 1; + for (j=0; jlast_channels[j] = in_s16 ? in_s16[j] : in_s8[j]; + in->in_bytes_used = src_size; + return; + } + + /*while space to fill and input data, convert*/ + use_prev = in->has_prev; + i = 0; + next = prev = 0; + while (1) { + prev = (u32) (i*ratio + 1) / 255; + if (prev>=src_samp) break; + + next = prev+1; + frac = (i*ratio) - 255*prev; + if (frac && (next==src_samp)) break; + if (use_prev && prev) use_prev = 0; + + if (in_s16) { + for (j=0; jlast_channels[j] : in_s16[in_ch*prev + j]; + inChanNext[j] = in_s16[in_ch*next + j]; + inChan[j] = (frac*inChanNext[j] + (255-frac)*inChan[j]) / 255; + } + } else { + for (j=0; jlast_channels[j] : in_s8[in_ch*prev + j]; + inChanNext[j] = in_s8[in_ch*next + j]; + inChan[j] = (frac*inChanNext[j] + (255-frac)*inChan[j]) / 255; + } + } + + gf_mixer_map_channels(inChan, in_ch, in->src->ch_cfg, out_ch, am->channel_cfg); + + for (j=0; jch_buf[j] + in->out_samples_written) = (s32) (inChan[j] * FIX2INT(100*in->pan[j]) / 100 ); + } + + in->out_samples_written ++; + if (in->out_samples_written == in->out_samples_to_write) break; + i++; + } + + if (!(ratio%255)) { + in->has_prev = 0; + if (next==src_samp) { + in->in_bytes_used = src_size; + } else { + in->in_bytes_used = MIN(src_size, prev*in->src->bps * in->src->chan / 8); + } + } else { + in->has_prev = 1; + if (next==src_samp) { + for (j=0; jlast_channels[j] = inChanNext[j]; + in->in_bytes_used = src_size; + } else { + in->in_bytes_used = prev*in->src->bps * in->src->chan / 8; + if (in->in_bytes_used>src_size) { + in->in_bytes_used = src_size; + for (j=0; jlast_channels[j] = inChanNext[j]; + } else { + for (j=0; jlast_channels[j] = in_s16 ? in_s16[in_ch*prev + j] : in_s8[in_ch*prev + j]; + } + } + } + /*cf below, make sure we call release*/ + in->in_bytes_used += 1; +} + + +u32 gf_mixer_get_output(GF_AudioMixer *am, void *buffer, u32 buffer_size) +{ + MixerInput *in, *single_source; + Fixed pan[6]; + Bool is_muted; + u32 i, j, count, size, in_size, nb_samples, delay, nb_written; + s32 *out_mix, nb_act_src; + char *data, *ptr; + + /*the config has changed we don't write to output since settings change*/ + if (gf_mixer_reconfig(am)) return 0; + + gf_mixer_lock(am, 1); + count = gf_list_count(am->sources); + if (!count) { + memset(buffer, 0, buffer_size); + gf_mixer_lock(am, 0); + return 0; + } + delay = 0; + if (am->ar && !am->ar->disable_resync) delay = am->ar->audio_delay; + + single_source = NULL; + if (count!=1) goto do_mix; + if (am->force_channel_out) goto do_mix; + single_source = (MixerInput *) gf_list_get(am->sources, 0); + /*if cfg changed or unknown return*/ + if (!single_source->src->GetConfig(single_source->src, 0)) { + am->must_reconfig = 1; + gf_mixer_reconfig(am); + memset(buffer, 0, buffer_size); + gf_mixer_lock(am, 0); + return 0; + } + /*this happens if input SR cannot be mapped to output audio hardware*/ + if (single_source->src->samplerate != am->sample_rate) goto do_mix; + /*note we don't check output cfg: if the number of channel is the same then the channel cfg is the + same*/ + if (single_source->src->chan != am->nb_channels) goto do_mix; + if (single_source->src->GetSpeed(single_source->src->callback)!=FIX_ONE) goto do_mix; + if (single_source->src->GetChannelVolume(single_source->src->callback, pan)) goto do_mix; + +single_source_mix: + + ptr = (char *)buffer; + in_size = buffer_size; + is_muted = single_source->src->IsMuted(single_source->src->callback); + + while (buffer_size) { + data = single_source->src->FetchFrame(single_source->src->callback, &size, delay); + if (!data || !size) break; + /*don't copy more than possible*/ + if (size > buffer_size) size = buffer_size; + if (is_muted) { + memset(ptr, 0, size); + } else { + memcpy(ptr, data, size); + } + buffer_size -= size; + ptr += size; + single_source->src->ReleaseFrame(single_source->src->callback, size); + delay = 0; + } + + /*not completely filled*/ + if (buffer_size) { + if (!data) { + GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[AudioMixer] not enough input data (%d still to fill)\n", buffer_size)); + } + memset(ptr, 0, buffer_size); + } + gf_mixer_lock(am, 0); + return (in_size - buffer_size); + +do_mix: + nb_act_src = 0; + nb_samples = buffer_size / (am->nb_channels * am->bits_per_sample / 8); + /*step 1, cfg*/ + if (am->output_sizeoutput) free(am->output); + am->output = (s32*)malloc(sizeof(s32) * buffer_size); + am->output_size = buffer_size; + } + + single_source = NULL; + for (i=0; isources, i); + if (in->src->IsMuted(in->src->callback)) continue; + + if (in->buffer_size < nb_samples) { + for (j=0; jch_buf[j]) free(in->ch_buf[j]); + in->ch_buf[j] = (s32 *) malloc(sizeof(s32) * nb_samples); + } + in->buffer_size = nb_samples; + } + in->speed = in->src->GetSpeed(in->src->callback); + if (in->speed<0) in->speed *= -1; + + in->out_samples_written = 0; + in->in_bytes_used = 0; + + /*if cfg unknown or changed (AudioBuffer child...) invalidate cfg settings*/ + if (!in->src->GetConfig(in->src, 0)) { + nb_act_src = 0; + am->must_reconfig = 1; + /*if main mixer reconfig asap*/ + if (am->ar) gf_mixer_reconfig(am); + break; + } else if (in->speed==0) { + in->out_samples_to_write = 0; + } else { + assert(in->src->samplerate); + in->out_samples_to_write = nb_samples; + if (in->src->IsMuted(in->src->callback)) { + memset(in->pan, 0, sizeof(Fixed)*6); + } else { + if (!in->src->GetChannelVolume(in->src->callback, in->pan)) { + /*track first active source with same cfg as mixer*/ + if (!single_source && (in->src->samplerate == am->sample_rate) + && (in->src->chan == am->nb_channels) && (in->speed == FIX_ONE) + ) + single_source = in; + } + } + nb_act_src ++; + } + } + if (!nb_act_src) { + memset(buffer, 0, sizeof(char)*buffer_size); + gf_mixer_lock(am, 0); + return 0; + } + + /*if only one active source in native format, process as single source (direct copy) + this is needed because mediaControl on an audio object doesn't deactivate it (eg the audio + object is still present in the mixer). this opt is typically usefull for language selection + content (cf mp4menu)*/ + if ((nb_act_src==1) && single_source) goto single_source_mix; + + /*step 2, fill all buffers*/ + while (nb_act_src) { + u32 nb_to_fill = 0; + /*fill*/ + for (i=0; isources, i); + if (in->out_samples_to_write>in->out_samples_written) { + gf_mixer_fetch_input(am, in, in->out_samples_written ? 0 : delay); + if (in->out_samples_to_write>in->out_samples_written) nb_to_fill++; + } + } + /*release - this is done in 2 steps in case 2 audio object use the same source...*/ + for (i=0; isources, i); + if (in->in_bytes_used) in->src->ReleaseFrame(in->src->callback, in->in_bytes_used-1); + in->in_bytes_used = 0; + } + if (!nb_to_fill) break; + } + /*step 3, mix the final buffer*/ + memset(am->output, 0, sizeof(s32) * buffer_size); + + nb_written = 0; + for (i=0; ioutput; + in = (MixerInput *)gf_list_get(am->sources, i); + if (!in->out_samples_to_write) continue; + /*only write what has been filled in the source buffer (may be less than output size)*/ + for (j=0; jout_samples_written; j++) { + u32 k; + for (k=0; knb_channels; k++) { + (*out_mix) += * (in->ch_buf[k] + j); + out_mix += 1; + } + } + if (nb_written < in->out_samples_written) nb_written = in->out_samples_written; + } + + if (!nb_written) { + memset(buffer, 0, sizeof(char)*buffer_size); + gf_mixer_lock(am, 0); + return 0; + } + + out_mix = am->output; + if (am->bits_per_sample==16) { + s16 *out_s16 = (s16 *) buffer; + for (i=0; inb_channels; j++) { + s32 samp = (*out_mix / nb_act_src); + if (samp > GF_SHORT_MAX) samp = GF_SHORT_MAX; + else if (samp < GF_SHORT_MIN) samp = GF_SHORT_MIN; + (*out_s16) = samp; + out_s16 += 1; + out_mix += 1; + } + } + } else { + s8 *out_s8 = (s8 *) buffer; + for (i=0; inb_channels; j++) { + s32 samp = (*out_mix / nb_act_src); + if (samp > 127) samp = 127; + else if (samp < -128) samp = -128; + (*out_s8) = samp; + out_s8 += 1; + out_mix += 1; + } + } + } + + nb_written *= am->nb_channels*am->bits_per_sample/8; + if (buffer_size > nb_written) + memset((char *)buffer + nb_written, 0, sizeof(char)*(buffer_size-nb_written)); + + gf_mixer_lock(am, 0); + return nb_written; +} + + diff --git a/src/compositor/audio_render.c b/src/compositor/audio_render.c new file mode 100644 index 0000000..793f1bc --- /dev/null +++ b/src/compositor/audio_render.c @@ -0,0 +1,341 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + + +static GF_Err gf_ar_setup_output_format(GF_AudioRenderer *ar) +{ + GF_Err e; + u32 freq, nb_bits, nb_chan, ch_cfg; + gf_mixer_get_config(ar->mixer, &freq, &nb_chan, &nb_bits, &ch_cfg); + + /*user disabled multichannel audio*/ + if (ar->disable_multichannel && (nb_chan>2) ) nb_chan = 2; + + e = ar->audio_out->ConfigureOutput(ar->audio_out, &freq, &nb_chan, &nb_bits, ch_cfg); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[AudioRender] reconfigure error %e\n", e)); + if (nb_chan>2) { + nb_chan=2; + e = ar->audio_out->ConfigureOutput(ar->audio_out, &freq, &nb_chan, &nb_bits, ch_cfg); + } + if (e) return e; + } + gf_mixer_set_config(ar->mixer, freq, nb_chan, nb_bits, ch_cfg); + ar->audio_delay = ar->audio_out->GetAudioDelay(ar->audio_out); + + ar->audio_out->SetVolume(ar->audio_out, ar->volume); + ar->audio_out->SetPan(ar->audio_out, ar->pan); + + return GF_OK; +} + +static u32 gf_ar_fill_output(void *ptr, char *buffer, u32 buffer_size) +{ + GF_AudioRenderer *ar = (GF_AudioRenderer *) ptr; + if (!ar->need_reconfig) { + return gf_mixer_get_output(ar->mixer, buffer, buffer_size); + } + return 0; +} + +u32 gf_ar_proc(void *p) +{ + GF_AudioRenderer *ar = (GF_AudioRenderer *) p; + + ar->audio_th_state = 1; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[AudioRender] Entering audio thread ID %d\n", gf_th_id() )); + + gf_mixer_lock(ar->mixer, 1); + ar->need_reconfig = 1; + gf_sc_ar_reconfig(ar); + gf_mixer_lock(ar->mixer, 0); + + while (ar->audio_th_state == 1) { + //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[AudioRender] Audio simulation step\n")); + + /*THIS IS NEEDED FOR SYMBIAN - if no yield here, the audio module always grabs the + main mixer mutex and it takes forever before it can be grabed by another thread, + for instance when reconfiguring scene*/ + gf_sleep(0); + + gf_mixer_lock(ar->mixer, 1); + if (ar->Frozen || gf_mixer_empty(ar->mixer) ) { + gf_mixer_lock(ar->mixer, 0); + gf_sleep(33); + } else { + if (ar->need_reconfig) gf_sc_ar_reconfig(ar); + ar->audio_out->WriteAudio(ar->audio_out); + gf_mixer_lock(ar->mixer, 0); + } + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[AudioRender] Exiting audio thread\n")); + ar->audio_out->Shutdown(ar->audio_out); + ar->audio_th_state = 3; + return 0; +} + + +GF_AudioRenderer *gf_sc_ar_load(GF_User *user) +{ + const char *sOpt; + u32 i, count; + u32 num_buffers, total_duration; + GF_Err e; + GF_AudioRenderer *ar; + ar = (GF_AudioRenderer *) malloc(sizeof(GF_AudioRenderer)); + memset(ar, 0, sizeof(GF_AudioRenderer)); + + num_buffers = total_duration = 0; + sOpt = gf_cfg_get_key(user->config, "Audio", "ForceConfig"); + if (sOpt && !stricmp(sOpt, "yes")) { + sOpt = gf_cfg_get_key(user->config, "Audio", "NumBuffers"); + num_buffers = sOpt ? atoi(sOpt) : 6; + sOpt = gf_cfg_get_key(user->config, "Audio", "TotalDuration"); + total_duration = sOpt ? atoi(sOpt) : 400; + } + + sOpt = gf_cfg_get_key(user->config, "Audio", "NoResync"); + ar->disable_resync = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(user->config, "Audio", "DisableMultiChannel"); + ar->disable_multichannel = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + + ar->mixer = gf_mixer_new(ar); + ar->user = user; + + sOpt = gf_cfg_get_key(user->config, "Audio", "Volume"); + ar->volume = sOpt ? atoi(sOpt) : 75; + sOpt = gf_cfg_get_key(user->config, "Audio", "Pan"); + ar->pan = sOpt ? atoi(sOpt) : 50; + + if (! (user->init_flags & GF_TERM_NO_AUDIO) ) { + + /*get a prefered compositor*/ + sOpt = gf_cfg_get_key(user->config, "Audio", "DriverName"); + if (sOpt) { + ar->audio_out = (GF_AudioOutput *) gf_modules_load_interface_by_name(user->modules, sOpt, GF_AUDIO_OUTPUT_INTERFACE); + if (!ar->audio_out) { + ar->audio_out = NULL; + sOpt = NULL; + } + } + if (!ar->audio_out) { + count = gf_modules_get_count(ar->user->modules); + for (i=0; iaudio_out = (GF_AudioOutput *) gf_modules_load_interface(ar->user->modules, i, GF_AUDIO_OUTPUT_INTERFACE); + if (!ar->audio_out) continue; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[AudioRender] Audio output module %s loaded\n", ar->audio_out->module_name)); + /*check that's a valid audio compositor*/ + if (ar->audio_out->SelfThreaded) { + if (ar->audio_out->SetPriority) break; + } else { + if (ar->audio_out->WriteAudio) break; + } + gf_modules_close_interface((GF_BaseInterface *)ar->audio_out); + ar->audio_out = NULL; + } + } + + /*if not init we run with a NULL audio compositor*/ + if (ar->audio_out) { + ar->audio_out->FillBuffer = gf_ar_fill_output; + ar->audio_out->audio_renderer = ar; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[AudioRender] Setting up audio module %s\n", ar->audio_out->module_name)); + e = ar->audio_out->Setup(ar->audio_out, ar->user->os_window_handler, num_buffers, total_duration); + + /*if audio module is not threaded, reconfigure it from our own thread*/ +// if (e==GF_OK) e = gf_ar_setup_output_format(ar); + + if (e != GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("Could not setup audio out %s\n", ar->audio_out->module_name)); + gf_modules_close_interface((GF_BaseInterface *)ar->audio_out); + ar->audio_out = NULL; + } else { + /*remember the module we use*/ + gf_cfg_set_key(user->config, "Audio", "DriverName", ar->audio_out->module_name); + if (!ar->audio_out->SelfThreaded) { + ar->th = gf_th_new("AudioRenderer"); + gf_th_run(ar->th, gf_ar_proc, ar); + } else { + gf_ar_setup_output_format(ar); + if (ar->audio_out->SetPriority) ar->audio_out->SetPriority(ar->audio_out, GF_THREAD_PRIORITY_REALTIME); + } + } + } + if (!ar->audio_out) gf_cfg_set_key(user->config, "Audio", "DriverName", "No Audio Output Available"); + } + + /*init compositor timer*/ + ar->startTime = gf_sys_clock(); + return ar; +} + +void gf_sc_ar_del(GF_AudioRenderer *ar) +{ + if (!ar) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[AudioRender] Destroying compositor\n")); + /*resume if paused (might cause deadlock otherwise)*/ + if (ar->Frozen) gf_sc_ar_control(ar, 1); + /*stop and shutdown*/ + if (ar->audio_out) { + /*kill audio thread*/ + if (!ar->audio_out->SelfThreaded) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[AudioRender] stoping audio thread\n")); + ar->audio_th_state = 2; + while (ar->audio_th_state != 3) { + gf_sleep(33); + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[AudioRender] audio thread stopped\n")); + gf_th_del(ar->th); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[AudioRender] audio thread destroyed\n")); + } + /*lock access before shutdown and emulate a reconfig (avoids mixer lock from self-threaded modules)*/ + ar->need_reconfig = 1; + gf_mixer_lock(ar->mixer, 1); + if (ar->audio_out->SelfThreaded) ar->audio_out->Shutdown(ar->audio_out); + gf_modules_close_interface((GF_BaseInterface *)ar->audio_out); + gf_mixer_lock(ar->mixer, 0); + } + gf_mixer_del(ar->mixer); + free(ar); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[AudioRender] Renderer destroyed\n")); +} + + +void gf_sc_ar_reset(GF_AudioRenderer *ar) +{ + gf_mixer_remove_all(ar->mixer); +} + +static void gf_ar_freeze_intern(GF_AudioRenderer *ar, Bool DoFreeze, Bool for_reconfig, Bool reset_hw_buffer) +{ + gf_mixer_lock(ar->mixer, 1); + if (DoFreeze) { + if (!ar->Frozen) { + ar->FreezeTime = gf_sys_clock(); + if (!for_reconfig && ar->audio_out && ar->audio_out->Play) ar->audio_out->Play(ar->audio_out, 0); + ar->Frozen = 1; + } + } else { + if (ar->Frozen) { + if (!for_reconfig && ar->audio_out && ar->audio_out->Play) ar->audio_out->Play(ar->audio_out, reset_hw_buffer ? 2 : 1); + ar->Frozen = 0; + ar->startTime += gf_sys_clock() - ar->FreezeTime; + } + } + gf_mixer_lock(ar->mixer, 0); +} + +void gf_sc_ar_control(GF_AudioRenderer *ar, u32 PauseType) +{ + gf_ar_freeze_intern(ar, !PauseType, 0, (PauseType==2) ? 1 : 0); +} + +void gf_sc_ar_set_volume(GF_AudioRenderer *ar, u32 Volume) +{ + char sOpt[10]; + gf_mixer_lock(ar->mixer, 1); + ar->volume = MIN(Volume, 100); + if (ar->audio_out) ar->audio_out->SetVolume(ar->audio_out, ar->volume); + sprintf(sOpt, "%d", ar->volume); + gf_cfg_set_key(ar->user->config, "Audio", "Volume", sOpt); + + gf_mixer_lock(ar->mixer, 0); +} +void gf_sc_ar_set_pan(GF_AudioRenderer *ar, u32 Balance) +{ + gf_mixer_lock(ar->mixer, 1); + ar->pan = MIN(Balance, 100); + if (ar->audio_out) ar->audio_out->SetPan(ar->audio_out, ar->pan); + gf_mixer_lock(ar->mixer, 0); +} + + +void gf_sc_ar_add_src(GF_AudioRenderer *ar, GF_AudioInterface *source) +{ + Bool recfg; + if (!ar) return; + /*lock mixer*/ + gf_mixer_lock(ar->mixer, 1); + gf_mixer_add_input(ar->mixer, source); + /*if changed reconfig*/ + recfg = gf_mixer_reconfig(ar->mixer); + if (!ar->need_reconfig) ar->need_reconfig = recfg; + + if (!gf_mixer_empty(ar->mixer) && ar->audio_out && ar->audio_out->Play) + ar->audio_out->Play(ar->audio_out, 1); + /*unlock mixer*/ + gf_mixer_lock(ar->mixer, 0); +} + +void gf_sc_ar_remove_src(GF_AudioRenderer *ar, GF_AudioInterface *source) +{ + if (ar) { + gf_mixer_remove_input(ar->mixer, source); + if (gf_mixer_empty(ar->mixer) && ar->audio_out && ar->audio_out->Play) + ar->audio_out->Play(ar->audio_out, 0); + } +} + + +void gf_sc_ar_set_priority(GF_AudioRenderer *ar, u32 priority) +{ + if (ar->audio_out && ar->audio_out->SelfThreaded) { + ar->audio_out->SetPriority(ar->audio_out, priority); + } else { + gf_th_set_priority(ar->th, priority); + } +} + +void gf_sc_ar_reconfig(GF_AudioRenderer *ar) +{ + if (!ar->need_reconfig || !ar->audio_out) return; + /*lock mixer*/ + gf_mixer_lock(ar->mixer, 1); + + gf_ar_freeze_intern(ar, 1, 1, 0); + + ar->need_reconfig = 0; + gf_ar_setup_output_format(ar); + + gf_ar_freeze_intern(ar, 0, 1, 0); + + /*unlock mixer*/ + gf_mixer_lock(ar->mixer, 0); +} + +u32 gf_sc_ar_get_delay(GF_AudioRenderer *ar) +{ + return ar->audio_out->GetAudioDelay(ar->audio_out); +} + + +u32 gf_sc_ar_get_clock(GF_AudioRenderer *ar) +{ + if (ar->Frozen) return ar->FreezeTime - ar->startTime; + return gf_sys_clock() - ar->startTime; +} diff --git a/src/compositor/bindable.c b/src/compositor/bindable.c new file mode 100644 index 0000000..c48983a --- /dev/null +++ b/src/compositor/bindable.c @@ -0,0 +1,244 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" + +GF_List *Bindable_GetStack(GF_Node *bindable) +{ + void *st; + if (!bindable) return 0; + st = gf_node_get_private(bindable); + switch (gf_node_get_tag(bindable)) { + case TAG_MPEG4_Background2D: return ((Background2DStack*)st)->reg_stacks; + case TAG_MPEG4_Viewport: + case TAG_MPEG4_NavigationInfo: + case TAG_X3D_NavigationInfo: + return ((ViewStack*)st)->reg_stacks; +#ifndef GPAC_DISABLE_3D + case TAG_MPEG4_Background: case TAG_X3D_Background: return ((BackgroundStack*)st)->reg_stacks; + case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: + case TAG_MPEG4_Fog: case TAG_X3D_Fog: + return ((ViewStack*)st)->reg_stacks; +#endif + default: return NULL; + } +} + +Bool Bindable_GetIsBound(GF_Node *bindable) +{ + if (!bindable) return 0; + switch (gf_node_get_tag(bindable)) { + case TAG_MPEG4_Background2D: return ((M_Background2D*)bindable)->isBound; + case TAG_MPEG4_Viewport: return ((M_Viewport*)bindable)->isBound; + case TAG_MPEG4_Background: case TAG_X3D_Background: return ((M_Background*)bindable)->isBound; + case TAG_MPEG4_NavigationInfo: case TAG_X3D_NavigationInfo: return ((M_NavigationInfo*)bindable)->isBound; + case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: return ((M_Viewpoint*)bindable)->isBound; + case TAG_MPEG4_Fog: case TAG_X3D_Fog: return ((M_Fog*)bindable)->isBound; + default: return 0; + } +} + +void Bindable_SetIsBound(GF_Node *bindable, Bool val) +{ + Bool has_bind_time = 0; + if (!bindable) return; + switch (gf_node_get_tag(bindable)) { + case TAG_MPEG4_Background2D: + if ( ((M_Background2D*)bindable)->isBound == val) return; + ((M_Background2D*)bindable)->isBound = val; + break; + case TAG_MPEG4_Viewport: + if ( ((M_Viewport*)bindable)->isBound == val) return; + ((M_Viewport*)bindable)->isBound = val; + ((M_Viewport*)bindable)->bindTime = gf_node_get_scene_time(bindable); + has_bind_time = 1; + break; + case TAG_X3D_Background: + if ( ((X_Background*)bindable)->isBound == val) return; + ((X_Background*)bindable)->isBound = val; + ((X_Background*)bindable)->bindTime = gf_node_get_scene_time(bindable); + has_bind_time = 1; + break; + case TAG_MPEG4_Background: + if ( ((M_Background*)bindable)->isBound == val) return; + ((M_Background*)bindable)->isBound = val; + break; + case TAG_X3D_NavigationInfo: + if ( ((X_NavigationInfo*)bindable)->isBound == val) return; + ((X_NavigationInfo*)bindable)->isBound = val; + ((X_NavigationInfo*)bindable)->bindTime = gf_node_get_scene_time(bindable); + has_bind_time = 1; + break; + case TAG_MPEG4_NavigationInfo: + if ( ((M_NavigationInfo*)bindable)->isBound == val) return; + ((M_NavigationInfo*)bindable)->isBound = val; + break; + case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: + if ( ((M_Viewpoint*)bindable)->isBound == val) return; + ((M_Viewpoint*)bindable)->isBound = val; + ((M_Viewpoint*)bindable)->bindTime = gf_node_get_scene_time(bindable); + has_bind_time = 1; + break; + case TAG_X3D_Fog: + if ( ((X_Fog*)bindable)->isBound == val) return; + ((X_Fog*)bindable)->isBound = val; + ((X_Fog*)bindable)->bindTime = gf_node_get_scene_time(bindable); + has_bind_time = 1; + break; + case TAG_MPEG4_Fog: + if ( ((M_Fog*)bindable)->isBound == val) return; + ((M_Fog*)bindable)->isBound = val; + break; + default: + return; + } + gf_node_event_out_str(bindable, "isBound"); + if (has_bind_time) gf_node_event_out_str(bindable, "bindTime"); + /*force invalidate of the bindable stack's owner*/ + gf_node_dirty_set(bindable, 0, 1); +} + + +Bool Bindable_GetSetBind(GF_Node *bindable) +{ + if (!bindable) return 0; + switch (gf_node_get_tag(bindable)) { + case TAG_MPEG4_Background2D: return ((M_Background2D*)bindable)->set_bind; + case TAG_MPEG4_Viewport: return ((M_Viewport*)bindable)->set_bind; + case TAG_MPEG4_Background: case TAG_X3D_Background: return ((M_Background*)bindable)->set_bind; + case TAG_MPEG4_NavigationInfo: case TAG_X3D_NavigationInfo: return ((M_NavigationInfo*)bindable)->set_bind; + case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: return ((M_Viewpoint*)bindable)->set_bind; + case TAG_MPEG4_Fog: case TAG_X3D_Fog: return ((M_Fog*)bindable)->set_bind; + default: return 0; + } +} + +void Bindable_SetSetBind(GF_Node *bindable, Bool val) +{ + if (!bindable) return; + switch (gf_node_get_tag(bindable)) { + case TAG_MPEG4_Background2D: + ((M_Background2D*)bindable)->set_bind = val; + ((M_Background2D*)bindable)->on_set_bind(bindable); + break; + case TAG_MPEG4_Viewport: + ((M_Viewport*)bindable)->set_bind = val; + ((M_Viewport*)bindable)->on_set_bind(bindable); + break; + case TAG_MPEG4_Background: case TAG_X3D_Background: + ((M_Background*)bindable)->set_bind = val; + ((M_Background*)bindable)->on_set_bind(bindable); + break; + case TAG_MPEG4_NavigationInfo: case TAG_X3D_NavigationInfo: + ((M_NavigationInfo*)bindable)->set_bind = val; + ((M_NavigationInfo*)bindable)->on_set_bind(bindable); + break; + case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: + ((M_Viewpoint*)bindable)->set_bind = val; + ((M_Viewpoint*)bindable)->on_set_bind(bindable); + break; + case TAG_MPEG4_Fog: case TAG_X3D_Fog: + ((M_Fog*)bindable)->set_bind = val; + ((M_Fog*)bindable)->on_set_bind(bindable); + break; + default: return; + } +} + +void Bindable_OnSetBind(GF_Node *bindable, GF_List *stack_list) +{ + u32 i; + Bool on_top, is_bound, set_bind; + GF_Node *node; + GF_List *stack; + + set_bind = Bindable_GetSetBind(bindable); + is_bound = Bindable_GetIsBound(bindable); + + if (!set_bind && !is_bound) return; + if (set_bind && is_bound) return; + + i=0; + while ((stack = (GF_List*)gf_list_enum(stack_list, &i))) { + on_top = (gf_list_get(stack, 0)==bindable) ? 1 : 0; + + if (!set_bind) { + if (is_bound) Bindable_SetIsBound(bindable, 0); + if (on_top && (gf_list_count(stack)>1)) { + gf_list_rem(stack, 0); + gf_list_add(stack, bindable); + node = (GF_Node*)gf_list_get(stack, 0); + Bindable_SetIsBound(node, 1); + } + } else { + if (!is_bound) Bindable_SetIsBound(bindable, 1); + if (!on_top) { + /*push old top one down and unbind*/ + node = (GF_Node*)gf_list_get(stack, 0); + Bindable_SetIsBound(node, 0); + /*insert new top*/ + gf_list_del_item(stack, bindable); + gf_list_insert(stack, bindable, 0); + } + } + } + /*force invalidate of the bindable stack's owner*/ + gf_node_dirty_set(bindable, 0, 1); + /*and redraw scene*/ + gf_sc_invalidate(gf_sc_get_compositor(bindable), NULL); +} + +void BindableStackDelete(GF_List *stack) +{ + while (gf_list_count(stack)) { + GF_List *bind_stack_list; + GF_Node *bindable = (GF_Node*)gf_list_get(stack, 0); + gf_list_rem(stack, 0); + bind_stack_list = Bindable_GetStack(bindable); + if (bind_stack_list) { + gf_list_del_item(bind_stack_list, stack); + assert(gf_list_find(bind_stack_list, stack)<0); + } + } + gf_list_del(stack); +} + + +void PreDestroyBindable(GF_Node *bindable, GF_List *stack_list) +{ + Bool is_bound = Bindable_GetIsBound(bindable); + Bindable_SetIsBound(bindable, 0); + + while (gf_list_count(stack_list)) { + GF_Node *stack_top; + GF_List *stack = (GF_List*)gf_list_get(stack_list, 0); + gf_list_rem(stack_list, 0); + gf_list_del_item(stack, bindable); + if (is_bound) { + stack_top = (GF_Node*)gf_list_get(stack, 0); + if (stack_top) Bindable_SetSetBind(stack_top, 1); + } + } +} + diff --git a/src/compositor/camera.c b/src/compositor/camera.c new file mode 100644 index 0000000..2e6165f --- /dev/null +++ b/src/compositor/camera.c @@ -0,0 +1,416 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#include + + +GF_Camera *new_camera() +{ + GF_Camera *tmp; + GF_SAFEALLOC(tmp, GF_Camera); + tmp->speed = 1; + + return tmp; +} +void delete_camera(GF_Camera *cam) +{ + if (cam) free(cam); +} + +void camera_invalidate(GF_Camera *cam) +{ + /*forces recompute of default viewpoint*/ + cam->had_viewpoint = 2; + cam->had_nav_info = 1; + cam->flags = CAM_IS_DIRTY; + cam->navigate_mode = GF_NAVIGATE_NONE; +} + +static void camera_frustum_from_matrix(GF_Camera *cam, GF_Matrix *mx) +{ + u32 i; + + cam->planes[FRUS_LEFT_PLANE].normal.x = mx->m[3] + mx->m[0]; + cam->planes[FRUS_LEFT_PLANE].normal.y = mx->m[7] + mx->m[4]; + cam->planes[FRUS_LEFT_PLANE].normal.z = mx->m[11] + mx->m[8]; + cam->planes[FRUS_LEFT_PLANE].d = mx->m[15] + mx->m[12]; + + cam->planes[FRUS_RIGHT_PLANE].normal.x = mx->m[3] - mx->m[0]; + cam->planes[FRUS_RIGHT_PLANE].normal.y = mx->m[7] - mx->m[4]; + cam->planes[FRUS_RIGHT_PLANE].normal.z = mx->m[11] - mx->m[8]; + cam->planes[FRUS_RIGHT_PLANE].d = mx->m[15] - mx->m[12]; + + cam->planes[FRUS_BOTTOM_PLANE].normal.x = mx->m[3] + mx->m[1]; + cam->planes[FRUS_BOTTOM_PLANE].normal.y = mx->m[7] + mx->m[5]; + cam->planes[FRUS_BOTTOM_PLANE].normal.z = mx->m[11] + mx->m[9]; + cam->planes[FRUS_BOTTOM_PLANE].d = mx->m[15] + mx->m[13]; + + cam->planes[FRUS_TOP_PLANE].normal.x = mx->m[3] - mx->m[1]; + cam->planes[FRUS_TOP_PLANE].normal.y = mx->m[7] - mx->m[5]; + cam->planes[FRUS_TOP_PLANE].normal.z = mx->m[11] - mx->m[9]; + cam->planes[FRUS_TOP_PLANE].d = mx->m[15] - mx->m[13]; + + cam->planes[FRUS_FAR_PLANE].normal.x = mx->m[3] - mx->m[2]; + cam->planes[FRUS_FAR_PLANE].normal.y = mx->m[7] - mx->m[6]; + cam->planes[FRUS_FAR_PLANE].normal.z = mx->m[11] - mx->m[10]; + cam->planes[FRUS_FAR_PLANE].d = mx->m[15] - mx->m[14]; + + cam->planes[FRUS_NEAR_PLANE].normal.x = mx->m[3] + mx->m[2]; + cam->planes[FRUS_NEAR_PLANE].normal.y = mx->m[7] + mx->m[6]; + cam->planes[FRUS_NEAR_PLANE].normal.z = mx->m[11] + mx->m[10]; + cam->planes[FRUS_NEAR_PLANE].d = mx->m[15] + mx->m[14]; + + for (i=0; i<6; ++i) { +#ifdef GPAC_FIXED_POINT + /*after some testing, it's just safer to move back to float here, the smallest drift will + result in completely wrong culling...*/ + Float vx, vy, vz, nor; + vx = FIX2FLT(cam->planes[i].normal.x); + vy = FIX2FLT(cam->planes[i].normal.y); + vz = FIX2FLT(cam->planes[i].normal.z); + nor = (Float) sqrt(vx*vx + vy*vy + vz*vz); + vx /= nor; vy /= nor; vz /= nor; + cam->planes[i].d = FLT2FIX (FIX2FLT(cam->planes[i].d) / nor); + cam->planes[i].normal.x = FLT2FIX(vx); + cam->planes[i].normal.y = FLT2FIX(vy); + cam->planes[i].normal.z = FLT2FIX(vz); +#else + Float len = (Float)(1.0f / gf_vec_len(cam->planes[i].normal)); + cam->planes[i].normal = gf_vec_scale(cam->planes[i].normal, len); + cam->planes[i].d *= len; +#endif + + /*compute p-vertex idx*/ + cam->p_idx[i] = gf_plane_get_p_vertex_idx(&cam->planes[i]); + } +} + +/*based on Copyright (C) 1995 Stephen Chenney (schenney@cs.berkeley.edu)*/ +SFRotation camera_get_orientation(SFVec3f pos, SFVec3f target, SFVec3f up) +{ + SFVec3f dir, tmp, v, axis, new_y; + SFVec4f norm, inv_norm, y_quat, ny_quat, rot_y, rot; + + gf_vec_diff(dir, target, pos); + gf_vec_norm(&dir); + tmp = gf_vec_scale(dir, gf_vec_dot(up, dir)); + gf_vec_diff(v, up, tmp); + gf_vec_norm(&v); + axis.x = dir.y; axis.y = -dir.x; axis.z = 0; + + if (gf_vec_dot(axis, axis) < FIX_EPSILON) { + if (dir.z> 0) { + norm.x = 0; norm.y = FIX_ONE; norm.z = 0; norm.q = 0; + } else { + norm.x = 0; norm.y = 0; norm.z = 0; norm.q = FIX_ONE; + } + } else { + gf_vec_norm(&axis); + norm = gf_quat_from_axis_cos(axis, -dir.z); + } + /* Find the inverse rotation. */ + inv_norm.x = -norm.x; inv_norm.y = -norm.y; inv_norm.z = -norm.z; inv_norm.q = norm.q; + /* Rotate the y. */ + y_quat.x = y_quat.z = y_quat.q = 0; y_quat.y = FIX_ONE; + ny_quat = gf_quat_multiply(&norm, &y_quat); + ny_quat = gf_quat_multiply(&ny_quat, &inv_norm); + + new_y.x = ny_quat.x; new_y.y = ny_quat.y; new_y.z = ny_quat.z; + + tmp = gf_vec_cross(new_y, v); + + if (gf_vec_dot(tmp, tmp) < FIX_EPSILON) { + /* The old and new may be pointing in the same or opposite. Need + ** to generate a vector perpendicular to the old or new y. + */ + tmp.x = 0; tmp.y = -v.z; tmp.z = v.y; + if (gf_vec_dot(tmp, tmp) < FIX_EPSILON) { + tmp.x = v.z; tmp.y = 0; tmp.z = -v.x; + } + } + gf_vec_norm(&tmp); + + rot_y = gf_quat_from_axis_cos(tmp, gf_vec_dot(new_y, v)); + + /* rot_y holds the rotation about the initial camera direction needed + ** to align the up vectors in the final position. + */ + + /* Put the 2 rotations together. */ + rot = gf_quat_multiply(&rot_y, &norm); + return gf_quat_to_rotation(&rot); +} + +void camera_update(GF_Camera *cam, GF_Matrix2D *user_transform, Bool center_coords) +{ + Fixed vlen, h, w, ar; + SFVec3f corner, center; + + if (! (cam->flags & CAM_IS_DIRTY)) return; + + ar = gf_divfix(cam->width, cam->height); + if (cam->is_3D) { + /*setup perspective*/ + gf_mx_perspective(&cam->projection, cam->fieldOfView, ar, cam->z_near, cam->z_far); + /*setup modelview*/ + gf_mx_lookat(&cam->modelview, cam->position, cam->target, cam->up); + + if (!center_coords) { + gf_mx_add_scale(&cam->modelview, 1, -1, 1); + gf_mx_add_translation(&cam->modelview, -cam->width / 2, -cam->height / 2, 0); + } + + /*compute center and radius - CHECK ME!*/ + vlen = cam->z_far - cam->z_near; + h = gf_mulfix(vlen , gf_tan(cam->fieldOfView / 2)); + w = gf_mulfix(h, ar); + center.x = 0; center.y = 0; center.z = cam->z_near + vlen / 2; + corner.x = w; corner.y = h; corner.z = vlen; + gf_vec_diff(corner, corner, center); + cam->radius = gf_vec_len(corner); + gf_vec_diff(cam->center, cam->target, cam->position); + gf_vec_norm(&cam->center); + cam->center = gf_vec_scale(cam->center, cam->z_near + vlen/2); + gf_vec_add(cam->center, cam->center, cam->position); + } else { + GF_BBox b; + Fixed hw, hh; + hw = cam->width / 2; + hh = cam->height / 2; + cam->z_near = -INT2FIX(512); + cam->z_far = INT2FIX(512); + + /*setup ortho*/ + gf_mx_ortho(&cam->projection, -hw, hw, -hh, hh, cam->z_near, cam->z_far); + /*setup modelview*/ + gf_mx_init(cam->modelview); + if (!center_coords) { + gf_mx_add_scale(&cam->modelview, 1, -1, 1); + gf_mx_add_translation(&cam->modelview, -hw, -hh, 0); + } + if (user_transform) gf_mx_add_matrix_2d(&cam->modelview, user_transform); + if (cam->flags & CAM_HAS_VIEWPORT) gf_mx_add_matrix(&cam->modelview, &cam->viewport); + + /*compute center & radius*/ + b.max_edge.x = hw; + b.max_edge.y = hh; + b.min_edge.x = -hw; + b.min_edge.y = -hh; + b.min_edge.z = b.max_edge.z = (cam->z_near+cam->z_far) / 2; + gf_bbox_refresh(&b); + cam->center = b.center; + cam->radius = b.radius; + } + /*compute frustum planes*/ + gf_mx_copy(cam->unprojection, cam->projection); + gf_mx_add_matrix_4x4(&cam->unprojection, &cam->modelview); + camera_frustum_from_matrix(cam, &cam->unprojection); + /*also compute reverse PM for unprojections*/ + gf_mx_inverse_4x4(&cam->unprojection); + cam->flags &= ~CAM_IS_DIRTY; +} + +void camera_set_vectors(GF_Camera *cam, SFVec3f pos, SFRotation ori, Fixed fov) +{ + Fixed sin_a, cos_a, icos_a, tmp; + + cam->fieldOfView = fov; + cam->last_pos = cam->position; + cam->position = pos; + /*compute up & target vectors in local system*/ + sin_a = gf_sin(ori.q); + cos_a = gf_cos(ori.q); + icos_a = FIX_ONE - cos_a; + tmp = gf_mulfix(icos_a, ori.z); + cam->target.x = gf_mulfix(ori.x, tmp) + gf_mulfix(sin_a, ori.y); + cam->target.y = gf_mulfix(ori.y, tmp) - gf_mulfix(sin_a, ori.x); + cam->target.z = gf_mulfix(ori.z, tmp) + cos_a; + gf_vec_norm(&cam->target); + cam->target = gf_vec_scale(cam->target, -cam->vp_dist); + gf_vec_add(cam->target, cam->target, pos); + tmp = gf_mulfix(icos_a, ori.y); + cam->up.x = gf_mulfix(ori.x, tmp) - gf_mulfix(sin_a, ori.z); + cam->up.y = gf_mulfix(ori.y, tmp) + cos_a; + cam->up.z = gf_mulfix(ori.z, tmp) + gf_mulfix(sin_a, ori.x); + gf_vec_norm(&cam->up); + cam->flags |= CAM_IS_DIRTY; +} + +void camera_reset_viewpoint(GF_Camera *cam, Bool animate) +{ + if (!animate || (cam->had_viewpoint==2) ) { + camera_set_vectors(cam, cam->vp_position, cam->vp_orientation, cam->vp_fov); + cam->last_pos = cam->vp_position; + return; + } + if (cam->is_3D) { + cam->start_pos = cam->position; + cam->start_ori = camera_get_orientation(cam->position, cam->target, cam->up); + cam->start_fov = cam->fieldOfView; + cam->end_pos = cam->vp_position; + cam->end_ori = cam->vp_orientation; + cam->end_fov = cam->vp_fov; + + cam->flags |= CAM_IS_DIRTY; + cam->anim_start = 0; + cam->anim_len = 1000; + } else { + cam->start_zoom = FIX_ONE; + cam->start_trans.x = cam->start_trans.y = 0; + cam->start_rot.x = cam->start_rot.y = 0; + cam->flags |= CAM_IS_DIRTY; + /*no animation on 3D viewports*/ + cam->anim_start = cam->anim_len = 0; + } +} + +void camera_move_to(GF_Camera *cam, SFVec3f pos, SFVec3f target, SFVec3f up) +{ + if (!cam->anim_len) { + cam->start_pos = cam->position; + cam->start_ori = camera_get_orientation(cam->position, cam->target, cam->up); + cam->start_fov = cam->fieldOfView; + } + cam->end_pos = pos; + cam->end_ori = camera_get_orientation(pos, target, up); + cam->end_fov = cam->fieldOfView; + + cam->flags |= CAM_IS_DIRTY; + cam->anim_start = 0; + cam->anim_len = 100; +} + +void camera_stop_anim(GF_Camera *cam) +{ + cam->anim_len = 0; +} + +void camera_jump(GF_Camera *cam) +{ + /*no "double-jump" :)*/ + if (cam->jumping) return; + cam->anim_start = 0; + cam->anim_len = 1000; + cam->jumping = 1; + cam->flags |= CAM_IS_DIRTY; +} + + +Bool camera_animate(GF_Camera *cam) +{ + u32 now; + Fixed frac; + if (!cam->anim_len) return 0; + + if (cam->jumping) { + if (!cam->anim_start) { + cam->anim_start = gf_sys_clock(); + cam->dheight = 0; + return 1; + } + cam->position.y -= cam->dheight; + cam->target.y -= cam->dheight; + + now = gf_sys_clock() - cam->anim_start; + if (now > cam->anim_len) { + cam->anim_len = 0; + cam->jumping = 0; + cam->flags |= CAM_IS_DIRTY; + return 1; + } + frac = FLT2FIX ( ((Float) now) / cam->anim_len); + if (frac>FIX_ONE / 2) frac = FIX_ONE - frac; + cam->dheight = gf_mulfix(cam->avatar_size.y, frac); + cam->position.y += cam->dheight; + cam->target.y += cam->dheight; + cam->flags |= CAM_IS_DIRTY; + return 1; + } + + if (!cam->anim_start) { + cam->anim_start = gf_sys_clock(); + now = 0; + frac = 0; + } else { + now = gf_sys_clock() - cam->anim_start; + if (now > cam->anim_len) { + cam->anim_len = 0; + if (cam->is_3D) { + camera_set_vectors(cam, cam->end_pos, cam->end_ori, cam->end_fov); + } else { + cam->flags |= CAM_IS_DIRTY; + } + if (cam->flags & CF_STORE_VP) { + cam->flags &= ~CF_STORE_VP; + cam->vp_position = cam->position; + cam->vp_fov = cam->fieldOfView; + cam->vp_orientation = camera_get_orientation(cam->position, cam->target, cam->up); + } + return 1; + } else { + frac = FLT2FIX( ((Float) now) / cam->anim_len); + } + } + + if (cam->is_3D) { + SFVec3f pos, dif; + SFRotation rot; + Fixed fov; + rot = gf_sg_sfrotation_interpolate(cam->start_ori, cam->end_ori, frac); + gf_vec_diff(dif, cam->end_pos, cam->start_pos); + dif = gf_vec_scale(dif, frac); + gf_vec_add(pos, cam->start_pos, dif); + fov = gf_mulfix(cam->end_fov - cam->start_fov, frac) + cam->start_fov; + camera_set_vectors(cam, pos, rot, fov); + } + return 1; +} + + +SFVec3f camera_get_pos_dir(GF_Camera *cam) +{ + SFVec3f v; + gf_vec_diff(v, cam->position, cam->target); + gf_vec_norm(&v); + return v; +} +SFVec3f camera_get_target_dir(GF_Camera *cam) +{ + SFVec3f v; + gf_vec_diff(v, cam->target, cam->position); + gf_vec_norm(&v); + return v; +} +SFVec3f camera_get_right_dir(GF_Camera *cam) +{ + SFVec3f v, pos; + pos = camera_get_pos_dir(cam); + v = gf_vec_cross(cam->up, pos ); + gf_vec_norm(&v); + return v; +} + + diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c new file mode 100644 index 0000000..009aff2 --- /dev/null +++ b/src/compositor/compositor.c @@ -0,0 +1,2530 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +/*for user and terminal API (options and InputSensor)*/ +#include +#include +#include + +#ifdef GPAC_TRISCOPE_MODE +#include "../src/compositor/triscope_renoir/triscope_renoir.h" +#endif + +#include "nodes_stacks.h" + +#include "visual_manager.h" +#include "texturing.h" + +#ifdef TRISCOPE_TEST_FRAMERATE +#include "chrono.h" +#endif + +#define SC_DEF_WIDTH 320 +#define SC_DEF_HEIGHT 240 + + +/*macro for size event format/send*/ +#define GF_USER_SETSIZE(_user, _w, _h) \ + { \ + GF_Event evt; \ + if (_user->EventProc) { \ + evt.type = GF_EVENT_SIZE; \ + evt.size.width = _w; \ + evt.size.height = _h; \ + _user->EventProc(_user->opaque, &evt); \ + } \ + } + +void gf_sc_simulation_tick(GF_Compositor *compositor); + + +GF_Err gf_sc_set_output_size(GF_Compositor *compositor, u32 Width, u32 Height) +{ + gf_sc_lock(compositor, 1); + /*FIXME: need to check for max resolution*/ + compositor->display_width = Width; + compositor->display_height = Height; + compositor->recompute_ar = 1; + compositor->draw_next_frame = 1; + gf_sc_lock(compositor, 0); + return GF_OK; +} + +static void gf_sc_set_fullscreen(GF_Compositor *compositor) +{ + GF_Err e; + if (!compositor->video_out->SetFullScreen) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Switching fullscreen %s\n", compositor->fullscreen ? "off" : "on")); + /*move to FS*/ + compositor->fullscreen = !compositor->fullscreen; + if (compositor->fullscreen && (compositor->scene_width>compositor->scene_height) +#ifndef GPAC_DISABLE_3D + && !compositor->visual->type_3d +#endif + ) { + e = compositor->video_out->SetFullScreen(compositor->video_out, 2, &compositor->display_width, &compositor->display_height); + } else { + e = compositor->video_out->SetFullScreen(compositor->video_out, compositor->fullscreen, &compositor->display_width, &compositor->display_height); + } + if (e) { + GF_USER_MESSAGE(compositor->user, "Compositor", "Cannot switch to fullscreen", e); + compositor->fullscreen = 0; + e = compositor->video_out->SetFullScreen(compositor->video_out, 0, &compositor->display_width, &compositor->display_height); + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] recomputing aspect ratio\n")); + compositor->recompute_ar = 1; + /*force signaling graphics reset*/ + compositor->reset_graphics = 1; +} + + +/*this is needed for: +- audio: since the audio compositor may not be threaded, it must be reconfigured by another thread otherwise +we lock the audio module +- video: this is typical to OpenGL&co: multithreaded is forbidden, so resizing/fullscreen MUST be done by the same +thread accessing the HW ressources +*/ +static void gf_sc_reconfig_task(GF_Compositor *compositor) +{ + GF_Event evt; + u32 width,height; + + /*reconfig audio if needed (non-threaded compositors)*/ + if (compositor->audio_renderer && !compositor->audio_renderer->th) gf_sc_ar_reconfig(compositor->audio_renderer); + + if (compositor->msg_type) { + + compositor->msg_type |= GF_SR_IN_RECONFIG; + + /*scene size has been overriden*/ + if (compositor->msg_type & GF_SR_CFG_OVERRIDE_SIZE) { + assert(!(compositor->override_size_flags & 2)); + compositor->msg_type &= ~GF_SR_CFG_OVERRIDE_SIZE; + compositor->override_size_flags |= 2; + width = compositor->scene_width; + height = compositor->scene_height; + compositor->has_size_info = 1; + gf_sc_set_size(compositor, width, height); + GF_USER_SETSIZE(compositor->user, width, height); + } + /*size changed from scene cfg: resize window first*/ + if (compositor->msg_type & GF_SR_CFG_SET_SIZE) { + u32 fs_width, fs_height; + Bool restore_fs = compositor->fullscreen; + compositor->msg_type &= ~GF_SR_CFG_SET_SIZE; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Changing display size to %d x %d\n", compositor->new_width, compositor->new_height)); + fs_width = fs_height = 0; + if (restore_fs) { + fs_width = compositor->display_width; + fs_height = compositor->display_height; + } + evt.type = GF_EVENT_SIZE; + evt.size.width = compositor->new_width; + evt.size.height = compositor->new_height; + compositor->new_width = compositor->new_height = 0; + /*send resize event*/ + if (!(compositor->msg_type & GF_SR_CFG_WINDOWSIZE_NOTIF)) { + compositor->video_out->ProcessEvent(compositor->video_out, &evt); + } + compositor->msg_type &= ~GF_SR_CFG_WINDOWSIZE_NOTIF; + + if (restore_fs) { + //gf_sc_set_fullscreen(compositor); + compositor->display_width = fs_width; + compositor->display_height = fs_height; + compositor->recompute_ar = 1; + } else { + gf_sc_set_output_size(compositor, evt.size.width, evt.size.height); + } + compositor->reset_graphics = 1; + + } + /*aspect ratio modif*/ + if (compositor->msg_type & GF_SR_CFG_AR) { + compositor->msg_type &= ~GF_SR_CFG_AR; + compositor->recompute_ar = 1; + } + /*fullscreen on/off request*/ + if (compositor->msg_type & GF_SR_CFG_FULLSCREEN) { + compositor->msg_type &= ~GF_SR_CFG_FULLSCREEN; + gf_sc_set_fullscreen(compositor); + compositor->draw_next_frame = 1; + } + compositor->msg_type &= ~GF_SR_IN_RECONFIG; + } + + /*3D driver changed message, recheck extensions*/ + if (compositor->reset_graphics) { +#ifndef GPAC_DISABLE_3D + compositor->offscreen_width = compositor->offscreen_height = 0; +#endif + evt.type = GF_EVENT_SYS_COLORS; + if (compositor->user->EventProc && compositor->user->EventProc(compositor->user->opaque, &evt) ) { + u32 i; + for (i=0; i<28; i++) { + compositor->sys_colors[i] = evt.sys_cols.sys_colors[i] & 0x00FFFFFF; + } + } + } +} + +Bool gf_sc_draw_frame(GF_Compositor *compositor) +{ + gf_sc_simulation_tick(compositor); + return compositor->draw_next_frame; +} + +static u32 gf_sc_proc(void *par) +{ + GF_Compositor *compositor = (GF_Compositor *) par; + compositor->video_th_state = 1; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Compositor] Entering thread ID %d\n", gf_th_id() )); + + + +#ifdef GPAC_TRISCOPE_MODE +/* scene creation*/ + compositor->RenoirHandler = CreateRenoirScene(); +#endif + + while (compositor->video_th_state == 1) { + if (compositor->is_hidden) + gf_sleep(compositor->frame_duration); + else + + gf_sc_simulation_tick(compositor); + + } + /*destroy video out here if w're using openGL, to avoid threading issues*/ + compositor->video_out->Shutdown(compositor->video_out); + gf_modules_close_interface((GF_BaseInterface *)compositor->video_out); + compositor->video_out = NULL; + compositor->video_th_state = 3; + return 0; +} + +/*forces graphics redraw*/ +GF_EXPORT +void gf_sc_reset_graphics(GF_Compositor *compositor) +{ + if (compositor) { + Bool locked = gf_mx_try_lock(compositor->mx); + compositor->reset_graphics = 1; + if (locked) gf_mx_v(compositor->mx); + } +} + +static void gf_sc_reset_framerate(GF_Compositor *compositor) +{ + u32 i; + for (i=0; iframe_time[i] = 0; + compositor->current_frame = 0; +} + +static Bool gf_sc_on_event(void *cbck, GF_Event *event); + +static Bool gf_sc_set_check_raster2d(GF_Raster2D *ifce) +{ + /*check base*/ + if (!ifce->stencil_new || !ifce->surface_new) return 0; + /*if these are not set we cannot draw*/ + if (!ifce->surface_clear || !ifce->surface_set_path || !ifce->surface_fill) return 0; + /*check we can init a surface with the current driver (the rest is optional)*/ + if (ifce->surface_attach_to_buffer) return 1; + return 0; +} + + +static GF_Err gf_sc_load(GF_Compositor *compositor) +{ + compositor->strike_bank = gf_list_new(); + compositor->visuals = gf_list_new(); + + GF_SAFEALLOC(compositor->traverse_state, GF_TraverseState); + compositor->traverse_state->vrml_sensors = gf_list_new(); + compositor->traverse_state->use_stack = gf_list_new(); + compositor->sensors = gf_list_new(); + compositor->previous_sensors = gf_list_new(); + compositor->hit_use_stack = gf_list_new(); + compositor->prev_hit_use_stack = gf_list_new(); + compositor->focus_ancestors = gf_list_new(); + compositor->focus_use_stack = gf_list_new(); + + /*setup main visual*/ + compositor->visual = visual_new(compositor); + compositor->visual->GetSurfaceAccess = compositor_2d_get_video_access; + compositor->visual->ReleaseSurfaceAccess = compositor_2d_release_video_access; + + compositor->visual->DrawBitmap = compositor_2d_draw_bitmap; + + + gf_list_add(compositor->visuals, compositor->visual); + + compositor->zoom = compositor->scale_x = compositor->scale_y = FIX_ONE; + + /*create a drawable for focus highlight*/ + compositor->focus_highlight = drawable_new(); + /*associate a dummy node for traversing*/ + compositor->focus_highlight->node = gf_node_new(NULL, TAG_UndefinedNode); + gf_node_register(compositor->focus_highlight->node, NULL); + gf_node_set_callback_function(compositor->focus_highlight->node, drawable_traverse_focus); + + +#ifndef GPAC_DISABLE_3D + /*default collision mode*/ + compositor->collide_mode = GF_COLLISION_DISPLACEMENT; //GF_COLLISION_NORMAL + compositor->gravity_on = 1; + + /*create default unit sphere and box for bounds*/ + compositor->unit_bbox = new_mesh(); + mesh_new_unit_bbox(compositor->unit_bbox); +#endif + + return GF_OK; +} + + +static GF_Compositor *gf_sc_create(GF_User *user) +{ + const char *sOpt; + GF_Compositor *tmp; + + GF_SAFEALLOC(tmp, GF_Compositor); + if (!tmp) return NULL; + tmp->user = user; + + /*load video out*/ + sOpt = gf_cfg_get_key(user->config, "Video", "DriverName"); + if (sOpt) { + tmp->video_out = (GF_VideoOutput *) gf_modules_load_interface_by_name(user->modules, sOpt, GF_VIDEO_OUTPUT_INTERFACE); + if (tmp->video_out) { + tmp->video_out->evt_cbk_hdl = tmp; + tmp->video_out->on_event = gf_sc_on_event; + /*init hw*/ + if (tmp->video_out->Setup(tmp->video_out, user->os_window_handler, user->os_display, user->init_flags) != GF_OK) { + gf_modules_close_interface((GF_BaseInterface *)tmp->video_out); + tmp->video_out = NULL; + } + } else { + sOpt = NULL; + } + } + + if (!tmp->video_out) { + u32 i, count; + count = gf_modules_get_count(user->modules); + for (i=0; ivideo_out = (GF_VideoOutput *) gf_modules_load_interface(user->modules, i, GF_VIDEO_OUTPUT_INTERFACE); + if (!tmp->video_out) continue; + tmp->video_out->evt_cbk_hdl = tmp; + tmp->video_out->on_event = gf_sc_on_event; + /*init hw*/ + if (tmp->video_out->Setup(tmp->video_out, user->os_window_handler, user->os_display, user->init_flags)==GF_OK) { + gf_cfg_set_key(user->config, "Video", "DriverName", tmp->video_out->module_name); + break; + } + gf_modules_close_interface((GF_BaseInterface *)tmp->video_out); + tmp->video_out = NULL; + } + } + if (!tmp->video_out ) { + free(tmp); + return NULL; + } +#ifdef ENABLE_JOYSTICK + sOpt = gf_cfg_get_key(user->config, "General", "JoystickCenteredMode"); + if (sOpt) { + if (!stricmp(sOpt, "no")) tmp->video_out->centered_mode=0; + else tmp->video_out->centered_mode=1; + } else tmp->video_out->centered_mode=1; +#endif + + + /*try to load a raster driver*/ + sOpt = gf_cfg_get_key(user->config, "Compositor", "Raster2D"); + if (sOpt) { + tmp->rasterizer = (GF_Raster2D *) gf_modules_load_interface_by_name(user->modules, sOpt, GF_RASTER_2D_INTERFACE); + if (!tmp->rasterizer) { + sOpt = NULL; + } else if (!gf_sc_set_check_raster2d(tmp->rasterizer)) { + gf_modules_close_interface((GF_BaseInterface *)tmp->rasterizer); + tmp->rasterizer = NULL; + sOpt = NULL; + } + } + if (!tmp->rasterizer) { + u32 i, count; + count = gf_modules_get_count(user->modules); + for (i=0; irasterizer = (GF_Raster2D *) gf_modules_load_interface(user->modules, i, GF_RASTER_2D_INTERFACE); + if (!tmp->rasterizer) continue; + if (gf_sc_set_check_raster2d(tmp->rasterizer)) break; + gf_modules_close_interface((GF_BaseInterface *)tmp->rasterizer); + tmp->rasterizer = NULL; + } + if (tmp->rasterizer) gf_cfg_set_key(user->config, "Compositor", "Raster2D", tmp->rasterizer->module_name); + } + if (!tmp->rasterizer) { + tmp->video_out->Shutdown(tmp->video_out); + gf_modules_close_interface((GF_BaseInterface *)tmp->video_out); + free(tmp); + return NULL; + } + + /*and init*/ + if (gf_sc_load(tmp) != GF_OK) { + gf_modules_close_interface((GF_BaseInterface *)tmp->rasterizer); + tmp->video_out->Shutdown(tmp->video_out); + gf_modules_close_interface((GF_BaseInterface *)tmp->video_out); + free(tmp); + return NULL; + } + + tmp->mx = gf_mx_new("Compositor"); + tmp->textures = gf_list_new(); + tmp->frame_rate = 30.0; + tmp->frame_duration = 33; + tmp->time_nodes = gf_list_new(); +#ifdef GF_SR_EVENT_QUEUE + tmp->events = gf_list_new(); + tmp->ev_mx = gf_mx_new("EventQueue"); +#endif + +#ifdef GF_SR_USE_VIDEO_CACHE + tmp->cached_groups = gf_list_new(); + tmp->cached_groups_queue = gf_list_new(); +#endif + + gf_sc_reset_framerate(tmp); + tmp->font_manager = gf_font_manager_new(user); + + tmp->extra_scenes = gf_list_new(); + tmp->interaction_level = GF_INTERACT_NORMAL | GF_INTERACT_INPUT_SENSOR | GF_INTERACT_NAVIGATION; + return tmp; +} + +GF_Compositor *gf_sc_new(GF_User *user, Bool self_threaded, GF_Terminal *term) +{ + GF_Compositor *tmp = gf_sc_create(user); + if (!tmp) return NULL; + tmp->term = term; + + /**/ + tmp->audio_renderer = gf_sc_ar_load(user); + if (!tmp->audio_renderer) GF_USER_MESSAGE(user, "", "NO AUDIO RENDERER", GF_OK); + + gf_mx_p(tmp->mx); + + /*run threaded*/ + if (self_threaded) { + tmp->VisualThread = gf_th_new("Compositor"); + gf_th_run(tmp->VisualThread, gf_sc_proc, tmp); + while (tmp->video_th_state!=1) { + gf_sleep(10); + if (tmp->video_th_state==3) { + gf_mx_v(tmp->mx); + gf_sc_del(tmp); + return NULL; + } + } + } else { +#ifdef GPAC_TRISCOPE_MODE +/* scene creation*/ + tmp->RenoirHandler = CreateRenoirScene(); +#endif + } + + /*set default size if owning output*/ + if (!tmp->user->os_window_handler) { + gf_sc_set_size(tmp, SC_DEF_WIDTH, SC_DEF_HEIGHT); + } + + gf_mx_v(tmp->mx); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI, ("[RTI]\tCompositor Cycle Log\tNetworks\tDecoders\tFrame\tDirect Draw\tVisual Config\tEvent\tRoute\tSMIL Timing\tTime node\tTexture\tSMIL Anim\tTraverse setup\tTraverse (and direct Draw)\tTraverse (and direct Draw) without anim\tIndirect Draw\tTraverse And Draw (Indirect or Not)\tFlush\tCycle\n")); + + return tmp; +} + + +void gf_sc_del(GF_Compositor *compositor) +{ + if (!compositor) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Destroying\n")); + gf_sc_lock(compositor, 1); + + if (compositor->VisualThread) { + compositor->video_th_state = 2; + while (compositor->video_th_state!=3) gf_sleep(10); + gf_th_del(compositor->VisualThread); + } + if (compositor->video_out) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Closing video output\n")); + compositor->video_out->Shutdown(compositor->video_out); + gf_modules_close_interface((GF_BaseInterface *)compositor->video_out); + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Closing visual compositor\n")); + + gf_node_unregister(compositor->focus_highlight->node, NULL); + drawable_del_ex(compositor->focus_highlight, compositor); + + if (compositor->selected_text) free(compositor->selected_text); + if (compositor->sel_buffer) free(compositor->sel_buffer); + + visual_del(compositor->visual); + gf_list_del(compositor->sensors); + gf_list_del(compositor->previous_sensors); + gf_list_del(compositor->visuals); + gf_list_del(compositor->strike_bank); + gf_list_del(compositor->hit_use_stack); + gf_list_del(compositor->prev_hit_use_stack); + gf_list_del(compositor->focus_ancestors); + gf_list_del(compositor->focus_use_stack); + + + gf_list_del(compositor->traverse_state->vrml_sensors); + gf_list_del(compositor->traverse_state->use_stack); + free(compositor->traverse_state); + +#ifndef GPAC_DISABLE_3D + if (compositor->unit_bbox) mesh_free(compositor->unit_bbox); +#endif + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Unloading visual compositor module\n")); + + if (compositor->audio_renderer) gf_sc_ar_del(compositor->audio_renderer); + +#ifdef GF_SR_EVENT_QUEUE + gf_mx_p(compositor->ev_mx); + while (gf_list_count(compositor->events)) { + GF_Event *ev = (GF_Event *)gf_list_get(compositor->events, 0); + gf_list_rem(compositor->events, 0); + free(ev); + } + gf_mx_v(compositor->ev_mx); + gf_mx_del(compositor->ev_mx); + gf_list_del(compositor->events); +#endif + + if (compositor->font_manager) gf_font_manager_del(compositor->font_manager); + +#ifdef GF_SR_USE_VIDEO_CACHE + gf_list_del(compositor->cached_groups); + gf_list_del(compositor->cached_groups_queue); +#endif + + gf_list_del(compositor->textures); + gf_list_del(compositor->time_nodes); + gf_list_del(compositor->extra_scenes); + gf_sc_lock(compositor, 0); + gf_mx_del(compositor->mx); + free(compositor); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Destroyed\n")); +} + +void gf_sc_set_fps(GF_Compositor *compositor, Double fps) +{ + if (fps) { + compositor->frame_rate = fps; + compositor->frame_duration = (u32) (1000 / fps); + gf_sc_reset_framerate(compositor); + } +} + +u32 gf_sc_get_audio_buffer_length(GF_Compositor *compositor) +{ + if (!compositor || !compositor->audio_renderer || !compositor->audio_renderer->audio_out) return 0; + return compositor->audio_renderer->audio_out->GetTotalBufferTime(compositor->audio_renderer->audio_out); +} + + +static void gf_sc_pause(GF_Compositor *compositor, u32 PlayState) +{ + if (!compositor || !compositor->audio_renderer) return; + if (!compositor->paused && !PlayState) return; + if (compositor->paused && (PlayState==GF_STATE_PAUSED)) return; + + /*step mode*/ + if (PlayState==GF_STATE_STEP_PAUSE) { + compositor->step_mode = 1; + /*resume for next step*/ + if (compositor->paused && compositor->term) gf_term_set_option(compositor->term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + } else { + if (compositor->audio_renderer) gf_sc_ar_control(compositor->audio_renderer, (compositor->paused && (PlayState==0xFF)) ? 2 : compositor->paused); + compositor->paused = (PlayState==GF_STATE_PAUSED) ? 1 : 0; + } +} + +u32 gf_sc_get_clock(GF_Compositor *compositor) +{ + return gf_sc_ar_get_clock(compositor->audio_renderer); +} + +static GF_Err gf_sc_set_scene_size(GF_Compositor *compositor, u32 Width, u32 Height) +{ + if (!Width || !Height) { + if (compositor->override_size_flags) { + /*specify a small size to detect biggest bitmap but not 0 in case only audio..*/ + compositor->scene_height = SC_DEF_HEIGHT; + compositor->scene_width = SC_DEF_WIDTH; + } else { + /*use current res*/ + compositor->scene_width = compositor->display_width ? compositor->display_width : compositor->new_width; + compositor->scene_height = compositor->display_height ? compositor->display_height : compositor->new_height; + } + } else { + compositor->scene_height = Height; + compositor->scene_width = Width; + } + return GF_OK; +} + +Bool gf_sc_get_size(GF_Compositor *compositor, u32 *Width, u32 *Height) +{ + *Height = compositor->scene_height; + *Width = compositor->scene_width; + return 1; +} + +#ifndef GPAC_DISABLE_SVG +GF_EXPORT +Fixed gf_sc_svg_convert_length_to_display(GF_Compositor *compositor, SVG_Length *length) +{ + /* Assuming the environment is 90dpi*/ + u32 dpi = 90; + if (!length) return 0; + + switch (length->type) { + case SVG_NUMBER_PERCENTAGE: + break; + case SVG_NUMBER_EMS: + break; + case SVG_NUMBER_EXS: + break; + case SVG_NUMBER_VALUE: + break; + case SVG_NUMBER_PX: + return length->value; + case SVG_NUMBER_CM: + return gf_mulfix(length->value, dpi*FLT2FIX(0.39)); + break; + case SVG_NUMBER_MM: + return gf_mulfix(length->value, dpi*FLT2FIX(0.039)); + case SVG_NUMBER_IN: + return length->value * dpi; + case SVG_NUMBER_PT: + return (dpi * length->value) / 12; + case SVG_NUMBER_PC: + return (dpi*length->value) / 6; + case SVG_NUMBER_INHERIT: + break; + } + return length->value; +} +#endif + + +void compositor_set_ar_scale(GF_Compositor *compositor, Fixed scaleX, Fixed scaleY) +{ + compositor->trans_x = gf_muldiv(compositor->trans_x, scaleX, compositor->scale_x); + compositor->trans_y = gf_muldiv(compositor->trans_y, scaleY, compositor->scale_y); + + compositor->scale_x = scaleX; + compositor->scale_y = scaleY; + compositor->zoom_changed = 1; + + compositor_2d_set_user_transform(compositor, compositor->zoom, compositor->trans_x, compositor->trans_y, 1); +} + +static void gf_sc_reset(GF_Compositor *compositor) +{ + Bool direct_draw; + + GF_VisualManager *visual; + u32 i=0; + while ((visual = (GF_VisualManager *)gf_list_enum(compositor->visuals, &i))) { + /*reset display list*/ + visual->cur_context = visual->context; + if (visual->cur_context) visual->cur_context->drawable = NULL; + while (visual->prev_nodes) { + struct _drawable_store *cur = visual->prev_nodes; + visual->prev_nodes = cur->next; + free(cur); + } + visual->last_prev_entry = NULL; + visual->to_redraw.count = 0; + + /*reset the surface as well*/ + if (visual->raster_surface) compositor->rasterizer->surface_delete(visual->raster_surface); + visual->raster_surface = NULL; + } + + gf_list_reset(compositor->sensors); + gf_list_reset(compositor->previous_sensors); + + /*reset traverse state*/ + direct_draw = compositor->traverse_state->direct_draw; + gf_list_del(compositor->traverse_state->vrml_sensors); + gf_list_del(compositor->traverse_state->use_stack); + memset(compositor->traverse_state, 0, sizeof(GF_TraverseState)); + compositor->traverse_state->vrml_sensors = gf_list_new(); + compositor->traverse_state->use_stack = gf_list_new(); + gf_mx2d_init(compositor->traverse_state->transform); + gf_cmx_init(&compositor->traverse_state->color_mat); + compositor->traverse_state->direct_draw = direct_draw; + + assert(!compositor->visual->overlays); + + compositor->reset_graphics = 0; + compositor->trans_x = compositor->trans_y = 0; + compositor->zoom = FIX_ONE; + compositor->grab_node = NULL; + compositor->grab_use = NULL; + compositor->focus_node = NULL; + compositor->focus_text_type = 0; + compositor->frame_number = 0; + compositor->video_memory = 0; + compositor->rotation = 0; + + gf_list_reset(compositor->focus_ancestors); + gf_list_reset(compositor->focus_use_stack); + +#ifdef GF_SR_USE_VIDEO_CACHE + gf_list_reset(compositor->cached_groups); + compositor->video_cache_current_size = 0; + gf_list_reset(compositor->cached_groups_queue); +#endif + + /*force resetup in case we're switching coord system*/ + compositor->root_visual_setup = 0; + compositor_set_ar_scale(compositor, compositor->scale_x, compositor->scale_x); +} + +GF_Err gf_sc_set_scene(GF_Compositor *compositor, GF_SceneGraph *scene_graph) +{ + u32 width, height; + Bool do_notif; + + if (!compositor) return GF_BAD_PARAM; + + gf_sc_lock(compositor, 1); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, (scene_graph ? "[Compositor] Attaching new scene\n" : "[Compositor] Detaching scene\n")); + + if (compositor->audio_renderer && (compositor->scene != scene_graph)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Reseting audio compositor\n")); + gf_sc_ar_reset(compositor->audio_renderer); + } + +#ifdef GF_SR_EVENT_QUEUE + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Reseting event queue\n")); + gf_mx_p(compositor->ev_mx); + while (gf_list_count(compositor->events)) { + GF_Event *ev = (GF_Event*)gf_list_get(compositor->events, 0); + gf_list_rem(compositor->events, 0); + free(ev); + } +#endif + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Reseting compositor module\n")); + /*reset main surface*/ + gf_sc_reset(compositor); + + /*set current graph*/ + compositor->scene = scene_graph; + do_notif = 0; + if (scene_graph) { +#ifndef GPAC_DISABLE_SVG + SVG_Length *w, *h; + Bool is_svg = 0; +#endif + const char *opt; + u32 tag; + GF_Node *top_node; + Bool had_size_info = compositor->has_size_info; + /*get pixel size if any*/ + gf_sg_get_scene_size_info(compositor->scene, &width, &height); + compositor->has_size_info = (width && height) ? 1 : 0; + if (compositor->has_size_info != had_size_info) compositor->scene_width = compositor->scene_height = 0; + + /*default back color is black*/ + if (! (compositor->user->init_flags & GF_TERM_WINDOWLESS)) compositor->back_color = 0xFF000000; + + top_node = gf_sg_get_root_node(compositor->scene); + tag = 0; + if (top_node) tag = gf_node_get_tag(top_node); + +#ifndef GPAC_DISABLE_SVG + w = h = NULL; + if ((tag>=GF_NODE_RANGE_FIRST_SVG) && (tag<=GF_NODE_RANGE_LAST_SVG)) { + GF_FieldInfo info; + is_svg = 1; + if (gf_node_get_attribute_by_tag(top_node, TAG_SVG_ATT_width, 0, 0, &info)==GF_OK) + w = info.far_ptr; + if (gf_node_get_attribute_by_tag(top_node, TAG_SVG_ATT_height, 0, 0, &info)==GF_OK) + h = info.far_ptr; + } + /*default back color is white*/ + if (is_svg && ! (compositor->user->init_flags & GF_TERM_WINDOWLESS)) compositor->back_color = 0xFFFFFFFF; + + /*hack for SVG where size is set in % - negotiate a canvas size*/ + if (!compositor->has_size_info && w && h) { + do_notif = 1; + if (w->type!=SVG_NUMBER_PERCENTAGE) { + width = FIX2INT(gf_sc_svg_convert_length_to_display(compositor, w) ); + } else { + width = SC_DEF_WIDTH; + do_notif = 0; + } + if (h->type!=SVG_NUMBER_PERCENTAGE) { + height = FIX2INT(gf_sc_svg_convert_length_to_display(compositor, h) ); + } else { + height = SC_DEF_HEIGHT; + do_notif = 0; + } + } + /*we consider that SVG has no size onfo per say, everything is handled by the viewBox if any*/ + if (is_svg) compositor->has_size_info = 0; +#endif + /*default back color is key color*/ + if (compositor->user->init_flags & GF_TERM_WINDOWLESS) { + opt = gf_cfg_get_key(compositor->user->config, "Compositor", "ColorKey"); + if (opt) { + u32 r, g, b, a; + sscanf(opt, "%02X%02X%02X%02X", &a, &r, &g, &b); + compositor->back_color = GF_COL_ARGB(0xFF, r, g, b); + } + } + + /*set scene size only if different, otherwise keep scaling/FS*/ + if ( !width || (compositor->scene_width!=width) || !height || (compositor->scene_height!=height)) { + do_notif = do_notif || compositor->has_size_info || (!compositor->scene_width && !compositor->scene_height); + gf_sc_set_scene_size(compositor, width, height); + + /*get actual size in pixels*/ + width = compositor->scene_width; + height = compositor->scene_height; + + opt = gf_cfg_get_key(compositor->user->config, "Compositor", "ScreenWidth"); + if (opt) width = atoi(opt); + opt = gf_cfg_get_key(compositor->user->config, "Compositor", "ScreenHeight"); + if (opt) height = atoi(opt); + + if (!compositor->user->os_window_handler) { + /*only notify user if we are attached to a window*/ + //do_notif = 0; + if (compositor->video_out->max_screen_width && (width > compositor->video_out->max_screen_width)) + width = compositor->video_out->max_screen_width; + if (compositor->video_out->max_screen_height && (height > compositor->video_out->max_screen_height)) + height = compositor->video_out->max_screen_height; + + gf_sc_set_size(compositor,width, height); + } + } + } + + gf_sc_reset_framerate(compositor); +#ifdef GF_SR_EVENT_QUEUE + gf_mx_v(compositor->ev_mx); +#endif + + gf_sc_lock(compositor, 0); + if (scene_graph) + compositor->draw_next_frame = 1; + /*here's a nasty trick: the app may respond to this by calling a gf_sc_set_size from a different + thread, but in an atomic way (typically happen on Win32 when changing the window size). WE MUST + NOTIFY THE SIZE CHANGE AFTER RELEASING THE RENDERER MUTEX*/ + if (do_notif && compositor->user->EventProc) { + GF_Event evt; + /*wait for user ack*/ + compositor->draw_next_frame = 0; + + evt.type = GF_EVENT_SCENE_SIZE; + evt.size.width = width; + evt.size.height = height; + compositor->user->EventProc(compositor->user->opaque, &evt); + } + return GF_OK; +} + +void gf_sc_lock_audio(GF_Compositor *compositor, Bool doLock) +{ + if (compositor->audio_renderer) { + gf_mixer_lock(compositor->audio_renderer->mixer, doLock); + } +} + +GF_EXPORT +void gf_sc_lock(GF_Compositor *compositor, Bool doLock) +{ + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Compositor] Thread ID %d is %s the scene\n", gf_th_id(), doLock ? "locking" : "unlocking" )); + if (doLock) + gf_mx_p(compositor->mx); + else { + gf_mx_v(compositor->mx); + } +} + +GF_Err gf_sc_set_size(GF_Compositor *compositor, u32 NewWidth, u32 NewHeight) +{ + Bool lock_ok; + if (!NewWidth || !NewHeight) { + compositor->override_size_flags &= ~2; + return GF_OK; + } + /*EXTRA CARE HERE: the caller (user app) is likely a different thread than the compositor one, and depending on window + manager we may get called here as a result of a message sent to user but not yet returned */ + lock_ok = gf_mx_try_lock(compositor->mx); + + compositor->new_width = NewWidth; + compositor->new_height = NewHeight; + compositor->msg_type |= GF_SR_CFG_SET_SIZE; + + /*if same size only request for video setup */ + if ((compositor->display_width == NewWidth) && (compositor->display_height == NewHeight) ) + compositor->msg_type |= GF_SR_CFG_WINDOWSIZE_NOTIF; + + if (lock_ok) gf_sc_lock(compositor, 0); + + return GF_OK; +} + +void gf_sc_reload_config(GF_Compositor *compositor) +{ + const char *sOpt; + + /*changing drivers needs exclusive access*/ + gf_sc_lock(compositor, 1); + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "ForceSceneSize"); + if (sOpt && ! stricmp(sOpt, "yes")) { + compositor->override_size_flags = 1; + } else { + compositor->override_size_flags = 0; + } + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "AntiAlias"); + if (sOpt) { + if (! stricmp(sOpt, "None")) gf_sc_set_option(compositor, GF_OPT_ANTIALIAS, GF_ANTIALIAS_NONE); + else if (! stricmp(sOpt, "Text")) gf_sc_set_option(compositor, GF_OPT_ANTIALIAS, GF_ANTIALIAS_TEXT); + else gf_sc_set_option(compositor, GF_OPT_ANTIALIAS, GF_ANTIALIAS_FULL); + } else { + gf_cfg_set_key(compositor->user->config, "Compositor", "AntiAlias", "All"); + gf_sc_set_option(compositor, GF_OPT_ANTIALIAS, GF_ANTIALIAS_FULL); + } + + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "FocusHighlightFill"); + if (sOpt) sscanf(sOpt, "%x", &compositor->highlight_fill); + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "FocusHighlightStroke"); + if (sOpt) sscanf(sOpt, "%x", &compositor->highlight_stroke); + else compositor->highlight_stroke = 0xFF000000; + + compositor->text_sel_color = 0xFFAAAAFF; + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "TextSelectHighlight"); + if (sOpt) sscanf(sOpt, "%x", &compositor->text_sel_color); + if (!compositor->text_sel_color) compositor->text_sel_color = 0xFFAAAAFF; + + /*load options*/ + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "DirectDraw"); + if (sOpt && ! stricmp(sOpt, "yes")) + compositor->traverse_state->direct_draw = 1; + else + compositor->traverse_state->direct_draw = 0; + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "ScalableZoom"); + compositor->scalable_zoom = (!sOpt || !stricmp(sOpt, "yes") ) ? 1 : 0; + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "DisableYUV"); + compositor->enable_yuv_hw = (sOpt && !stricmp(sOpt, "yes") ) ? 0 : 1; + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "DisablePartialHardwareBlit"); + compositor->disable_partial_hw_blit = (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0; + + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "StressMode"); + gf_sc_set_option(compositor, GF_OPT_STRESS_MODE, (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0); + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "HighSpeed"); + gf_sc_set_option(compositor, GF_OPT_HIGHSPEED, (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0); + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "BoundingVolume"); + if (sOpt) { + if (! stricmp(sOpt, "Box")) gf_sc_set_option(compositor, GF_OPT_DRAW_BOUNDS, GF_BOUNDS_BOX); + else if (! stricmp(sOpt, "AABB")) gf_sc_set_option(compositor, GF_OPT_DRAW_BOUNDS, GF_BOUNDS_AABB); + else gf_sc_set_option(compositor, GF_OPT_DRAW_BOUNDS, GF_BOUNDS_NONE); + } else { + gf_cfg_set_key(compositor->user->config, "Compositor", "BoundingVolume", "None"); + gf_sc_set_option(compositor, GF_OPT_DRAW_BOUNDS, GF_BOUNDS_NONE); + } + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "TextureTextMode"); + if (sOpt && !stricmp(sOpt, "Always")) compositor->texture_text_mode = GF_TEXTURE_TEXT_ALWAYS; + else if (sOpt && !stricmp(sOpt, "Never")) compositor->texture_text_mode = GF_TEXTURE_TEXT_NEVER; + else compositor->texture_text_mode = GF_TEXTURE_TEXT_DEFAULT; + + if (compositor->audio_renderer) { + sOpt = gf_cfg_get_key(compositor->user->config, "Audio", "NoResync"); + compositor->audio_renderer->disable_resync = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + + sOpt = gf_cfg_get_key(compositor->user->config, "Audio", "DisableMultiChannel"); + compositor->audio_renderer->disable_multichannel = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + } + +#ifdef GF_SR_USE_VIDEO_CACHE + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "VideoCacheSize"); + compositor->video_cache_max_size = sOpt ? atoi(sOpt) : 0; + compositor->video_cache_max_size *= 1024; + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "CacheScale"); + compositor->cache_scale = sOpt ? atoi(sOpt) : 100; + if (!compositor->cache_scale) compositor->cache_scale = 100; + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "CacheTolerance"); + compositor->cache_tolerance = sOpt ? atoi(sOpt) : 30; +#endif + +#ifndef GPAC_DISABLE_3D + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "ForceOpenGL"); + compositor->force_opengl_2d = (sOpt && !strcmp(sOpt, "yes")) ? 1 : 0; + + + /*currently: + - no tesselator for GL-ES, so use raster outlines. + - no support for npow2 textures, and no support for DrawPixels + */ +#ifndef GPAC_USE_OGL_ES + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "RasterOutlines"); + compositor->raster_outlines = (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0; + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "EmulatePOW2"); + compositor->emul_pow2 = (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0; + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "BitmapCopyPixels"); + compositor->bitmap_use_pixels = (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0; +#else + compositor->raster_outlines = 1; + compositor->emul_pow2 = 1; + compositor->bitmap_use_pixels = 0; +#endif + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "PolygonAA"); + compositor->poly_aa = (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0; + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "BackFaceCulling"); + if (sOpt && !stricmp(sOpt, "Off")) compositor->backcull = GF_BACK_CULL_OFF; + else if (sOpt && !stricmp(sOpt, "Alpha")) compositor->backcull = GF_BACK_CULL_ALPHA; + else compositor->backcull = GF_BACK_CULL_ON; + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "Wireframe"); + if (sOpt && !stricmp(sOpt, "WireOnly")) compositor->wiremode = GF_WIREFRAME_ONLY; + else if (sOpt && !stricmp(sOpt, "WireOnSolid")) compositor->wiremode = GF_WIREFRAME_SOLID; + else compositor->wiremode = GF_WIREFRAME_NONE; + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "DrawNormals"); + if (sOpt && !stricmp(sOpt, "PerFace")) compositor->draw_normals = GF_NORMALS_FACE; + else if (sOpt && !stricmp(sOpt, "PerVertex")) compositor->draw_normals = GF_NORMALS_VERTEX; + else compositor->draw_normals = GF_NORMALS_NONE; + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "DisableGLUScale"); + compositor->disable_glu_scale = (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0; + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "DisableRectExt"); + compositor->disable_rect_ext = (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0; + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "DisableGLCulling"); + compositor->disable_gl_cull = (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0; + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "DisableYUVGL"); + compositor->disable_yuvgl = (sOpt && !stricmp(sOpt, "yes") ) ? 1 : 0; + +#endif + +#ifdef GPAC_TRISCOPE_MODE + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "3dsDepthBuffGain"); + if (sOpt) sscanf(sOpt, "%f", &((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthGain); else ((GF_RenoirHandler *) compositor->RenoirHandler)->MapDepthGain = 1; + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "3dsDepthBuffOffset"); + if (sOpt) sscanf(sOpt, "%f", &((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthOffset); else ((GF_RenoirHandler *) compositor->RenoirHandler)->MapDepthOffset = 0; + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "FlatDepthBuffGain"); + if (sOpt) sscanf(sOpt, "%f", &((GF_RenoirHandler *)compositor->RenoirHandler)->FlatDepthGain); else ((GF_RenoirHandler *) compositor->RenoirHandler)->FlatDepthGain = 1.0; + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "OGLDepthBuffGain"); + if (sOpt) sscanf(sOpt, "%f", &compositor->OGLDepthGain); else compositor->OGLDepthGain = 1.0; + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "OGLDepthBuffOffset"); + if (sOpt) sscanf(sOpt, "%f", &compositor->OGLDepthOffset); else compositor->OGLDepthOffset = 0; +#endif + + + /*RECT texture support - we must reload HW*/ + compositor->reset_graphics = 1; + + compositor->draw_next_frame = 1; + + gf_sc_lock(compositor, 0); +} + +GF_Err gf_sc_set_option(GF_Compositor *compositor, u32 type, u32 value) +{ + GF_Err e; + gf_sc_lock(compositor, 1); + + e = GF_OK; + switch (type) { + case GF_OPT_PLAY_STATE: + gf_sc_pause(compositor, value); + break; + case GF_OPT_AUDIO_VOLUME: gf_sc_ar_set_volume(compositor->audio_renderer, value); break; + case GF_OPT_AUDIO_PAN: gf_sc_ar_set_pan(compositor->audio_renderer, value); break; + case GF_OPT_OVERRIDE_SIZE: + compositor->override_size_flags = value ? 1 : 0; + compositor->draw_next_frame = 1; + break; + case GF_OPT_STRESS_MODE: + compositor->stress_mode = value; + break; + case GF_OPT_ANTIALIAS: + compositor->antiAlias = value; + compositor->draw_next_frame = 1; + break; + case GF_OPT_HIGHSPEED: + compositor->high_speed = value; + compositor->draw_next_frame = 1; + break; + case GF_OPT_DRAW_BOUNDS: + compositor->draw_bvol = value; + compositor->draw_next_frame = 1; + break; + case GF_OPT_TEXTURE_TEXT: + compositor->texture_text_mode = value; + compositor->draw_next_frame = 1; + break; + case GF_OPT_ASPECT_RATIO: + compositor->aspect_ratio = value; + compositor->msg_type |= GF_SR_CFG_AR; + break; + case GF_OPT_INTERACTION_LEVEL: + compositor->interaction_level = value; + compositor->draw_next_frame = 1; + break; + case GF_OPT_REFRESH: + compositor->reset_graphics = value; + break; + case GF_OPT_FULLSCREEN: + if (compositor->fullscreen != value) compositor->msg_type |= GF_SR_CFG_FULLSCREEN; + compositor->draw_next_frame = 1; + break; + case GF_OPT_ORIGINAL_VIEW: + compositor_2d_set_user_transform(compositor, FIX_ONE, 0, 0, 0); + gf_sc_set_size(compositor, compositor->scene_width, compositor->scene_height); + break; + case GF_OPT_VISIBLE: + compositor->is_hidden = !value; + if (compositor->video_out->ProcessEvent) { + GF_Event evt; + evt.type = GF_EVENT_SHOWHIDE; + evt.show.show_type = value ? 1 : 0; + e = compositor->video_out->ProcessEvent(compositor->video_out, &evt); + } + compositor->draw_next_frame = 1; + break; + case GF_OPT_FREEZE_DISPLAY: + compositor->freeze_display = value; + compositor->draw_next_frame = 1; + break; + case GF_OPT_RELOAD_CONFIG: + gf_sc_reload_config(compositor); + compositor->draw_next_frame = 1; + break; + case GF_OPT_DIRECT_DRAW: + compositor->traverse_state->direct_draw = value ? 1 : 0; + /*force redraw*/ + compositor->draw_next_frame = 1; + break; + case GF_OPT_SCALABLE_ZOOM: + compositor->scalable_zoom = value; + /*emulate size message to force AR recompute*/ + compositor->msg_type |= GF_SR_CFG_AR; + break; + case GF_OPT_USE_OPENGL: + if (compositor->force_opengl_2d != value) { + compositor->force_opengl_2d = value; + /*force resetup*/ + compositor->root_visual_setup = 0; + /*force texture setup when switching to OpenGL*/ + if (value) compositor->reset_graphics = 1; + /*force redraw*/ + compositor->draw_next_frame = 1; + } + break; + case GF_OPT_YUV_HARDWARE: + compositor->enable_yuv_hw = value; + break; + case GF_OPT_NAVIGATION_TYPE: + compositor_2d_set_user_transform(compositor, FIX_ONE, 0, 0, 0); +#ifndef GPAC_DISABLE_3D + compositor_3d_reset_camera(compositor); +#endif + break; + case GF_OPT_NAVIGATION: + if (compositor->navigation_disabled) { + e = GF_NOT_SUPPORTED; + } else +#ifndef GPAC_DISABLE_3D + if (compositor->visual->type_3d || compositor->active_layer) { + GF_Camera *cam = compositor_3d_get_camera(compositor); + if (cam->navigation_flags & NAV_ANY) { + /*if not specifying mode, try to (un)bind top*/ + if (!value) { + if (compositor->active_layer) { + compositor_layer3d_bind_camera(compositor->active_layer, 0, value); + } else { + GF_Node *n = (GF_Node*)gf_list_get(compositor->visual->navigation_stack, 0); + if (n) Bindable_SetSetBind(n, 0); + else cam->navigate_mode = value; + } + } else { + cam->navigate_mode = value; + } + break; + } + } else +#endif + { + if ((value!=GF_NAVIGATE_NONE) && (value!=GF_NAVIGATE_SLIDE) && (value!=GF_NAVIGATE_EXAMINE)) { + e = GF_NOT_SUPPORTED; + break; + } + compositor->navigate_mode = value; + break; + } + e = GF_NOT_SUPPORTED; + break; + + case GF_OPT_HEADLIGHT: +#ifndef GPAC_DISABLE_3D + if (compositor->visual->type_3d || compositor->active_layer) { + GF_Camera *cam = compositor_3d_get_camera(compositor); + if (cam->navigation_flags & NAV_ANY) { + if (value) + cam->navigation_flags |= NAV_HEADLIGHT; + else + cam->navigation_flags &= ~NAV_HEADLIGHT; + break; + } + } +#endif + e = GF_NOT_SUPPORTED; + break; + +#ifndef GPAC_DISABLE_3D + + case GF_OPT_EMULATE_POW2: compositor->emul_pow2 = value; break; + case GF_OPT_POLYGON_ANTIALIAS: compositor->poly_aa = value; break; + case GF_OPT_BACK_CULL: compositor->backcull = value; break; + case GF_OPT_WIREFRAME: compositor->wiremode = value; break; + case GF_OPT_NORMALS: compositor->draw_normals = value; break; +#ifndef GPAC_USE_OGL_ES + case GF_OPT_RASTER_OUTLINES: compositor->raster_outlines = value; break; +#endif + + case GF_OPT_NO_RECT_TEXTURE: + if (value != compositor->disable_rect_ext) { + compositor->disable_rect_ext = value; + /*RECT texture support - we must reload HW*/ + compositor->reset_graphics = 1; + } + break; + case GF_OPT_BITMAP_COPY: compositor->bitmap_use_pixels = value; break; + case GF_OPT_COLLISION: compositor->collide_mode = value; break; + case GF_OPT_GRAVITY: + { + GF_Camera *cam = compositor_3d_get_camera(compositor); + compositor->gravity_on = value; + /*force collision pass*/ + cam->last_pos.z -= 1; + compositor->draw_next_frame = 1; + } + break; +#endif + + case GF_OPT_VIDEO_CACHE_SIZE: +#ifdef GF_SR_USE_VIDEO_CACHE + compositor_set_cache_memory(compositor, 1024*value); +#else + e = GF_NOT_SUPPORTED; +#endif + break; + + default: + e = GF_BAD_PARAM; + break; + } + gf_sc_lock(compositor, 0); + return e; +} + +u32 gf_sc_get_option(GF_Compositor *compositor, u32 type) +{ + switch (type) { + case GF_OPT_PLAY_STATE: return compositor->paused ? 1 : 0; + case GF_OPT_OVERRIDE_SIZE: return (compositor->override_size_flags & 1) ? 1 : 0; + case GF_OPT_IS_FINISHED: + { + if (compositor->interaction_sensors) return 0; + if (gf_list_count(compositor->time_nodes)) return 0; + return 1; + } + case GF_OPT_STRESS_MODE: return compositor->stress_mode; + case GF_OPT_AUDIO_VOLUME: return compositor->audio_renderer->volume; + case GF_OPT_AUDIO_PAN: return compositor->audio_renderer->pan; + case GF_OPT_ANTIALIAS: return compositor->antiAlias; + case GF_OPT_HIGHSPEED: return compositor->high_speed; + case GF_OPT_ASPECT_RATIO: return compositor->aspect_ratio; + case GF_OPT_FULLSCREEN: return compositor->fullscreen; + case GF_OPT_INTERACTION_LEVEL: return compositor->interaction_level; + case GF_OPT_VISIBLE: return !compositor->is_hidden; + case GF_OPT_FREEZE_DISPLAY: return compositor->freeze_display; + case GF_OPT_TEXTURE_TEXT: return compositor->texture_text_mode; + case GF_OPT_USE_OPENGL: return compositor->force_opengl_2d; + + case GF_OPT_DIRECT_DRAW: return compositor->traverse_state->direct_draw ? 1 : 0; + case GF_OPT_SCALABLE_ZOOM: return compositor->scalable_zoom; + case GF_OPT_YUV_HARDWARE: return compositor->enable_yuv_hw; + case GF_OPT_YUV_FORMAT: return compositor->enable_yuv_hw ? compositor->video_out->yuv_pixel_format : 0; + case GF_OPT_NAVIGATION_TYPE: +#ifndef GPAC_DISABLE_3D + if (compositor->visual->type_3d || compositor->active_layer) { + GF_Camera *cam = compositor_3d_get_camera(compositor); + if (!(cam->navigation_flags & NAV_ANY)) return GF_NAVIGATE_TYPE_NONE; + return ((cam->is_3D || compositor->active_layer) ? GF_NAVIGATE_TYPE_3D : GF_NAVIGATE_TYPE_2D); + } else +#endif + { + return compositor->navigation_disabled ? GF_NAVIGATE_TYPE_NONE : GF_NAVIGATE_TYPE_2D; + } + case GF_OPT_NAVIGATION: +#ifndef GPAC_DISABLE_3D + if (compositor->visual->type_3d || compositor->active_layer) { + GF_Camera *cam = compositor_3d_get_camera(compositor); + return cam->navigate_mode; + } +#endif + return compositor->navigate_mode; + + case GF_OPT_HEADLIGHT: return 0; + case GF_OPT_COLLISION: return GF_COLLISION_NONE; + case GF_OPT_GRAVITY: return 0; + + case GF_OPT_VIDEO_CACHE_SIZE: +#ifdef GF_SR_USE_VIDEO_CACHE + return compositor->video_cache_max_size/1024; +#else + return 0; +#endif + break; + default: return 0; + } +} + +void gf_sc_map_point(GF_Compositor *compositor, s32 X, s32 Y, Fixed *bifsX, Fixed *bifsY) +{ + /*coordinates are in user-like OS....*/ + X = X - compositor->display_width/2; + Y = compositor->display_height/2 - Y; + *bifsX = INT2FIX(X); + *bifsY = INT2FIX(Y); +} + + +GF_Err gf_sc_get_screen_buffer(GF_Compositor *compositor, GF_VideoSurface *framebuffer, u32 depth_dump_mode) +{ + GF_Err e; + if (!compositor || !framebuffer) return GF_BAD_PARAM; + gf_mx_p(compositor->mx); + +#ifndef GPAC_DISABLE_3D + if (compositor->visual->type_3d) e = compositor_3d_get_screen_buffer(compositor, framebuffer, depth_dump_mode); + else +#endif + /*no depth dump in 2D mode*/ + if (depth_dump_mode) e = GF_NOT_SUPPORTED; + else e = compositor->video_out->LockBackBuffer(compositor->video_out, framebuffer, 1); + + if (e != GF_OK) gf_mx_v(compositor->mx); + return e; +} + +GF_Err gf_sc_release_screen_buffer(GF_Compositor *compositor, GF_VideoSurface *framebuffer) +{ + GF_Err e; + if (!compositor || !framebuffer) return GF_BAD_PARAM; + +#ifndef GPAC_DISABLE_3D + if (compositor->visual->type_3d) e = compositor_3d_release_screen_buffer(compositor, framebuffer); + else +#endif + e = compositor->video_out->LockBackBuffer(compositor->video_out, framebuffer, 0); + + gf_mx_v(compositor->mx); + return e; +} + +Double gf_sc_get_fps(GF_Compositor *compositor, Bool absoluteFPS) +{ + Double fps; + u32 ind, num, frames, run_time; + + /*start from last frame and get first frame time*/ + ind = compositor->current_frame; + frames = 0; + run_time = compositor->frame_time[ind]; + for (num=0; numframe_time[ind]; + } else { + run_time += MAX(compositor->frame_time[ind], compositor->frame_duration); + } + frames++; + if (frames==GF_SR_FPS_COMPUTE_SIZE) break; + if (!ind) { + ind = GF_SR_FPS_COMPUTE_SIZE; + } else { + ind--; + } + } + if (!run_time) return (Double) compositor->frame_rate; + fps = 1000*frames; + fps /= run_time; + return fps; +} + +void gf_sc_register_time_node(GF_Compositor *compositor, GF_TimeNode *tn) +{ + /*may happen with DEF/USE */ + if (tn->is_registered) return; + if (tn->needs_unregister) return; + gf_list_add(compositor->time_nodes, tn); + tn->is_registered = 1; +} +void gf_sc_unregister_time_node(GF_Compositor *compositor, GF_TimeNode *tn) +{ + gf_list_del_item(compositor->time_nodes, tn); +} + + + +GF_Node *gf_sc_pick_node(GF_Compositor *compositor, s32 X, s32 Y) +{ + return NULL; +} + +static void gf_sc_forward_event(GF_Compositor *compositor, GF_Event *ev) +{ + GF_USER_SENDEVENT(compositor->user, ev); + + if ((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) { + u32 now; + GF_Event event; + /*emulate doubleclick*/ + now = gf_sys_clock(); + if (now - compositor->last_click_time < DOUBLECLICK_TIME_MS) { + event.type = GF_EVENT_DBLCLICK; + event.mouse.key_states = compositor->key_states; + event.mouse.x = ev->mouse.x; + event.mouse.y = ev->mouse.y; + GF_USER_SENDEVENT(compositor->user, &event); + } + compositor->last_click_time = now; + } +} + +static void gf_sc_setup_root_visual(GF_Compositor *compositor, GF_Node *top_node) +{ + if (top_node && !compositor->root_visual_setup) { + u32 node_tag; +#ifndef GPAC_DISABLE_3D + Bool was_3d = compositor->visual->type_3d; +#endif + compositor->root_visual_setup = 1; + compositor->visual->center_coords = 1; + + compositor->traverse_state->pixel_metrics = 1; + compositor->traverse_state->min_hsize = INT2FIX(MIN(compositor->scene_width, compositor->scene_height)) / 2; + + node_tag = gf_node_get_tag(top_node); + switch (node_tag) { + case TAG_MPEG4_OrderedGroup: + case TAG_MPEG4_Layer2D: +#ifndef GPAC_DISABLE_3D + compositor->visual->type_3d = 0; + compositor->visual->camera.is_3D = 0; +#endif + compositor->traverse_state->pixel_metrics = gf_sg_use_pixel_metrics(compositor->scene); + break; + case TAG_MPEG4_Group: + case TAG_MPEG4_Layer3D: +#ifndef GPAC_DISABLE_3D + compositor->visual->type_3d = 2; + compositor->visual->camera.is_3D = 1; +#endif + compositor->traverse_state->pixel_metrics = gf_sg_use_pixel_metrics(compositor->scene); + break; + case TAG_X3D_Group: +#ifndef GPAC_DISABLE_3D + compositor->visual->type_3d = 3; +#endif + compositor->traverse_state->pixel_metrics = gf_sg_use_pixel_metrics(compositor->scene); + break; +#ifndef GPAC_DISABLE_SVG + case TAG_SVG_svg: +#ifndef GPAC_DISABLE_3D + compositor->visual->type_3d = 0; + compositor->visual->camera.is_3D = 0; +#endif + compositor->visual->center_coords = 0; + compositor->root_visual_setup = 2; + break; +#endif + } + + /*!! by default we don't set the focus on the content - this is conform to SVG and avoids displaying the + focus box for MPEG-4 !! */ + + /*setup OpenGL & camera mode*/ +#ifndef GPAC_DISABLE_3D + /*request for OpenGL drawing in 2D*/ + if (compositor->force_opengl_2d && !compositor->visual->type_3d) + compositor->visual->type_3d = 1; + + if (! (compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL)) { + compositor->visual->type_3d = 0; + compositor->visual->camera.is_3D = 0; + } + compositor->visual->camera.is_3D = (compositor->visual->type_3d>1) ? 1 : 0; + camera_invalidate(&compositor->visual->camera); +#endif + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Main scene setup - pixel metrics %d - center coords %d\n", compositor->traverse_state->pixel_metrics, compositor->visual->center_coords)); + +#ifndef GPAC_DISABLE_3D + /*change in 2D/3D config, force AR recompute/video restup*/ + if (was_3d != compositor->visual->type_3d) compositor->recompute_ar = was_3d ? 1 : 2; +#endif + + compositor->recompute_ar = 1; + } + +#ifndef GPAC_DISABLE_LOG + compositor->visual_config_time = 0; +#endif + if (compositor->recompute_ar) { +#ifndef GPAC_DISABLE_LOG + u32 time=0; + + if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_RTI)) { + time = gf_sys_clock(); + } +#endif + +#ifndef GPAC_DISABLE_3D + if (compositor->visual->type_3d) { + compositor_3d_set_aspect_ratio(compositor); + gf_sc_load_opengl_extensions(compositor); + } + else +#endif + compositor_2d_set_aspect_ratio(compositor); + + compositor->draw_next_frame = 0; + +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_RTI)) { + compositor->visual_config_time = gf_sys_clock() - time; + } +#endif + } +} + +static void gf_sc_draw_scene(GF_Compositor *compositor) +{ + u32 flags; + + GF_Node *top_node = gf_sg_get_root_node(compositor->scene); + + if (!top_node && !compositor->visual->last_had_back && !compositor->visual->cur_context) { + //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Scene has no root node, nothing to draw\n")); + return; + } + + gf_sc_setup_root_visual(compositor, top_node); + +#ifdef GF_SR_USE_VIDEO_CACHE + if (!compositor->video_cache_max_size) + compositor->traverse_state->in_group_cache = 1; +#endif + + flags = compositor->traverse_state->direct_draw; + + + visual_draw_frame(compositor->visual, top_node, compositor->traverse_state, 1); + + +#ifdef GPAC_TRISCOPE_MODE + /*here comes renoir rendering*/ + RenderRenoirScene (compositor->RenoirHandler); +#endif + compositor->traverse_state->direct_draw = flags; +#ifdef GF_SR_USE_VIDEO_CACHE + gf_list_reset(compositor->cached_groups_queue); +#endif + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Drawing done\n")); + + /*only send the resize notification once the frame has been dra*/ + if (compositor->recompute_ar) { + compositor_send_resize_event(compositor, 0, 0, 0, 1); + compositor->recompute_ar = 0; + } + compositor->zoom_changed = 0; +} + + +#ifndef GPAC_DISABLE_LOG +extern u32 time_spent_in_anim; +#endif + +void gf_sc_simulation_tick(GF_Compositor *compositor) +{ + GF_SceneGraph *sg; + u32 in_time, end_time, i, count; + Bool frame_drawn; +#ifndef GPAC_DISABLE_LOG + s32 event_time, route_time, smil_timing_time, time_node_time, texture_time, traverse_time, flush_time; +#endif +#ifdef TRISCOPE_TEST_FRAMERATE + double drawing_time; + static u32 timer_count=0; +#endif + /*lock compositor for the whole cycle*/ + gf_sc_lock(compositor, 1); + + /*first thing to do, let the video output handle user event if it is not threaded*/ + compositor->video_out->ProcessEvent(compositor->video_out, NULL); + + if (compositor->freeze_display) { + gf_sc_lock(compositor, 0); + gf_sleep(compositor->frame_duration); + return; + } + + gf_sc_reconfig_task(compositor); + + /* if there is no scene, we draw a black screen to flush the screen */ + if (!compositor->scene && !gf_list_count(compositor->extra_scenes) ) { + gf_sc_draw_scene(compositor); + gf_sc_lock(compositor, 0); + gf_sleep(compositor->frame_duration); + return; + } + + in_time = gf_sys_clock(); +#ifdef TRISCOPE_TEST_FRAMERATE + TIMER_START; +#endif + if (compositor->reset_graphics) + compositor->draw_next_frame = 1; + +#ifdef GF_SR_EVENT_QUEUE + /*process pending user events*/ +#ifndef GPAC_DISABLE_LOG + event_time = gf_sys_clock(); +#endif + gf_mx_p(compositor->ev_mx); + while (gf_list_count(compositor->events)) { + GF_Event *ev = (GF_Event*)gf_list_get(compositor->events, 0); + gf_list_rem(compositor->events, 0); + if (!gf_sc_exec_event(compositor, ev)) { + gf_sc_forward_event(compositor, ev); + } + free(ev); + } + gf_mx_v(compositor->ev_mx); +#ifndef GPAC_DISABLE_LOG + event_time = gf_sys_clock() - event_time; +#endif +#else + event_time = 0; +#endif + + gf_term_sample_clocks(compositor->term); + + /*setup root visual before triggering animations in case they in turn use the viewport*/ +// if (!compositor->root_visual_setup) +// gf_sc_setup_root_visual(compositor, gf_sg_get_root_node(compositor->scene)); + +#ifndef GPAC_DISABLE_LOG + route_time = gf_sys_clock(); +#endif + /*execute all routes before updating textures, otherwise nodes inside composite texture may never see their + dirty flag set*/ + gf_sg_activate_routes(compositor->scene); + i = 0; + while ((sg = (GF_SceneGraph*)gf_list_enum(compositor->extra_scenes, &i))) { + gf_sg_activate_routes(sg); + } +#ifndef GPAC_DISABLE_LOG + route_time = gf_sys_clock() - route_time; +#endif + +#ifndef GPAC_DISABLE_SVG +#if SVG_FIXME + { /* Experimental (Not SVG compliant system events (i.e. battery, cpu ...) triggered to the root node)*/ + GF_Node *root = gf_sg_get_root_node(compositor->scene); + GF_DOM_Event evt; + if (gf_dom_listener_count(root)) { + u32 i, count; + count = gf_dom_listener_count(root); + for (i=0;ievent.type == GF_EVENT_CPU) { + GF_SystemRTInfo sys_rti; + if (gf_sys_get_rti(500, &sys_rti, GF_RTI_ALL_PROCESSES_TIMES)) { + evt.type = GF_EVENT_CPU; + evt.cpu_percentage = sys_rti.total_cpu_usage; + //printf("%d\n",sys_rti.total_cpu_usage); + gf_dom_event_fire(root, NULL, &evt); + } + } else if (l->event.type == GF_EVENT_BATTERY) { //&& l->observer.target == (SVG_SA_Element *)node) { + evt.type = GF_EVENT_BATTERY; + gf_sys_get_battery_state(&evt.onBattery, &evt.batteryState, &evt.batteryLevel); + gf_dom_event_fire(root, NULL, &evt); + } + } + } + } +#endif + +#ifndef GPAC_DISABLE_LOG + smil_timing_time = gf_sys_clock(); +#endif + if (gf_smil_notify_timed_elements(compositor->scene)) { + compositor->draw_next_frame = 1; + } + i = 0; + while ((sg = (GF_SceneGraph*)gf_list_enum(compositor->extra_scenes, &i))) { + if (gf_smil_notify_timed_elements(sg)) { + compositor->draw_next_frame = 1; + } + } + +#ifndef GPAC_DISABLE_LOG + smil_timing_time = gf_sys_clock() - smil_timing_time; +#endif + +#endif + +#ifndef GPAC_DISABLE_LOG + time_node_time = gf_sys_clock(); +#endif + /*update all timed nodes */ + count = gf_list_count(compositor->time_nodes); + for (i=0; itime_nodes, i); + if (!tn->needs_unregister) tn->UpdateTimeNode(tn); + if (tn->needs_unregister) { + tn->is_registered = 0; + tn->needs_unregister = 0; + gf_list_rem(compositor->time_nodes, i); + i--; + count--; + continue; + } + } +#ifndef GPAC_DISABLE_LOG + time_node_time = gf_sys_clock() - time_node_time; +#endif + +#ifndef GPAC_DISABLE_LOG + texture_time = gf_sys_clock(); +#endif + /*update all textures*/ + count = gf_list_count(compositor->textures); + for (i=0; itextures, i); + /*signal graphics reset before updating*/ + if (compositor->reset_graphics && txh->tx_io) gf_sc_texture_reset(txh); + txh->update_texture_fcnt(txh); + } + compositor->reset_graphics = 0; + +#ifndef GPAC_DISABLE_LOG + texture_time = gf_sys_clock() - texture_time; +#endif + + + frame_drawn = (compositor->draw_next_frame==1) ? 1 : 0; + + /*if invalidated, draw*/ + if (compositor->draw_next_frame) { + GF_Window rc; +#ifndef GPAC_DISABLE_LOG + traverse_time = gf_sys_clock(); + time_spent_in_anim = 0; +#endif + + /*video flush only*/ + if (compositor->draw_next_frame==2) { + compositor->draw_next_frame = 0; + } else { + compositor->draw_next_frame = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Redrawing scene\n")); + gf_sc_draw_scene(compositor); +#ifndef GPAC_DISABLE_LOG + traverse_time = gf_sys_clock() - traverse_time; +#endif + } + /*and flush*/ +#ifndef GPAC_DISABLE_LOG + flush_time = gf_sys_clock(); +#endif + + if(compositor->user->init_flags & GF_TERM_INIT_HIDE) compositor->skip_flush = 1; + + if (!compositor->skip_flush) { + rc.x = rc.y = 0; + rc.w = compositor->display_width; + rc.h = compositor->display_height; + compositor->video_out->Flush(compositor->video_out, &rc); + } else { + compositor->skip_flush = 0; + } +#ifndef GPAC_DISABLE_LOG + flush_time = gf_sys_clock() - flush_time; +#endif + + visual_2d_draw_overlays(compositor->visual); + compositor->last_had_overlays = compositor->visual->has_overlays; + + if (compositor->stress_mode) { + compositor->draw_next_frame = 1; + compositor->reset_graphics = 1; + } + compositor->reset_fonts = 0; + } else { +#ifndef GPAC_DISABLE_LOG + traverse_time = 0; + time_spent_in_anim = 0; + flush_time = 0; + compositor->traverse_setup_time = 0; + compositor->traverse_and_direct_draw_time = 0; + compositor->indirect_draw_time = 0; +#endif + } + + /*release all textures - we must release them to handle a same OD being used by several textures*/ + count = gf_list_count(compositor->textures); + for (i=0; itextures, i); + gf_sc_texture_release_stream(txh); + if (frame_drawn && txh->tx_io && !(txh->flags & GF_SR_TEXTURE_USED)) + gf_sc_texture_reset(txh); + /*remove the use flag*/ + txh->flags &= ~GF_SR_TEXTURE_USED; + } + + end_time = gf_sys_clock() - in_time; + +#ifdef TRISCOPE_TEST_FRAMERATE +TIMER_STOP; +drawing_time=TIMER_ELAPSED; +printf("frame: %d ", timer_count) ; +printf(" %f sec\n", drawing_time/1000000.0) ; +timer_count++; +#endif + + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI, ("[RTI]\tCompositor Cycle Log\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", + compositor->networks_time, + compositor->decoders_time, + compositor->frame_number, + compositor->traverse_state->direct_draw, + compositor->visual_config_time, + event_time, + route_time, + smil_timing_time, + time_node_time, + texture_time, + time_spent_in_anim, + compositor->traverse_setup_time, + compositor->traverse_and_direct_draw_time, + compositor->traverse_and_direct_draw_time - time_spent_in_anim, + compositor->indirect_draw_time, + traverse_time, + flush_time, + end_time)); + + gf_sc_lock(compositor, 0); + + compositor->current_frame = (compositor->current_frame+1) % GF_SR_FPS_COMPUTE_SIZE; + compositor->frame_time[compositor->current_frame] = end_time; + + compositor->frame_number++; + + /*step mode on, pause and return*/ + if (compositor->step_mode) { + compositor->step_mode = 0; + if (compositor->term) gf_term_set_option(compositor->term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + return; + } + /*not threaded, let the owner decide*/ + if ((compositor->user->init_flags & GF_TERM_NO_VISUAL_THREAD) || !compositor->frame_duration) return; + + /*compute sleep time till next frame, otherwise we'll kill the CPU*/ + i = end_time / compositor->frame_duration + 1; +// while (i * compositor->frame_duration < end_time) i++; + in_time = i * compositor->frame_duration - end_time; + gf_sleep(in_time); +} + +Bool gf_sc_visual_is_registered(GF_Compositor *compositor, GF_VisualManager *visual) +{ + GF_VisualManager *tmp; + u32 i = 0; + while ((tmp = (GF_VisualManager *)gf_list_enum(compositor->visuals, &i))) { + if (tmp == visual) return 1; + } + return 0; +} + +void gf_sc_visual_register(GF_Compositor *compositor, GF_VisualManager *visual) +{ + if (gf_sc_visual_is_registered(compositor, visual)) return; + gf_list_add(compositor->visuals, visual); +} + +void gf_sc_visual_unregister(GF_Compositor *compositor, GF_VisualManager *visual) +{ + gf_list_del_item(compositor->visuals, visual); +} + +void gf_sc_traverse_subscene(GF_Compositor *compositor, GF_Node *inline_parent, GF_SceneGraph *subscene, void *rs) +{ + Fixed min_hsize; + Bool use_pm, prev_pm, prev_coord; + SFVec2f prev_vp; + s32 flip_coords; + u32 h, w, tag; + GF_Matrix2D transf; + GF_SceneGraph *in_scene; + GF_Node *inline_root = gf_sg_get_root_node(subscene); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + if (!inline_root) return; + + flip_coords = 0; + in_scene = gf_node_get_graph(inline_root); + w = h = 0; + + /*check parent/child doc orientation*/ + + /*check child doc*/ + tag = gf_node_get_tag(inline_root); + if (tag < GF_NODE_RANGE_LAST_VRML) { + u32 new_tag = 0; + use_pm = gf_sg_use_pixel_metrics(in_scene); + if (gf_node_get_tag(inline_parent)>GF_NODE_RANGE_LAST_VRML) { + /*moving from SVG to VRML-based, need positive translation*/ + flip_coords = 1; + + /*if our root nodes are not LayerXD ones, insert one. This prevents mixing + of bindable stacks and gives us free 2D/3D integration*/ + if (tag==TAG_MPEG4_OrderedGroup) { + new_tag = TAG_MPEG4_Layer2D; + } else if ((tag==TAG_MPEG4_Group) || (tag==TAG_X3D_Group)) { + new_tag = TAG_MPEG4_Layer3D; + } + } +#ifndef GPAC_DISABLE_3D + /*if the inlined root node is a 3D one except Layer3D and we are not in a 3D context, insert + a Layer3D at the root*/ + else if (!tr_state->visual->type_3d && ((tag==TAG_MPEG4_Group) || (tag==TAG_X3D_Group))) { + new_tag = TAG_MPEG4_Layer3D; + } +#endif + + /*create new root*/ + if (new_tag) { + GF_SceneGraph *sg = gf_node_get_graph(inline_root); + GF_Node *new_root = gf_node_new(sg, new_tag); + gf_node_register(new_root, NULL); + gf_sg_set_root_node(sg, new_root); + gf_node_list_add_child(& ((GF_ParentNode*)new_root)->children, inline_root); + gf_node_register(inline_root, new_root); + gf_node_unregister(inline_root, NULL); + inline_root = new_root; + gf_node_init(new_root); + } + + gf_sg_get_scene_size_info(in_scene, &w, &h); + } else { + use_pm = 1; + if (gf_node_get_tag(inline_parent)min_hsize; + prev_pm = tr_state->pixel_metrics; + prev_vp = tr_state->vp_size; + prev_coord = tr_state->fliped_coords; + gf_mx2d_init(transf); + + /*compute center<->top-left transform*/ + if (flip_coords) + gf_mx2d_add_scale(&transf, FIX_ONE, -FIX_ONE); + + /*if scene size is given in the child document, scale to fit the entire vp*/ + if (w && h) + gf_mx2d_add_scale(&transf, tr_state->vp_size.x/w, tr_state->vp_size.y/h); + if (flip_coords) { + gf_mx2d_add_translation(&transf, flip_coords * tr_state->vp_size.x/2, tr_state->vp_size.y/2); + tr_state->fliped_coords = !tr_state->fliped_coords; + } + + /*if scene size is given in the child document, scale back vp to take into account the above scale + otherwise the scene won't be properly clipped*/ + if (w && h) { + tr_state->vp_size.x = INT2FIX(w); + tr_state->vp_size.y = INT2FIX(h); + } + + + /*compute pixel<->meter transform*/ + if (use_pm != tr_state->pixel_metrics) { + /*override aspect ratio if any size info is given in the scene*/ + if (w && h) { + Fixed scale = INT2FIX( MIN(w, h) / 2); + if (scale) tr_state->min_hsize = scale; + } + if (!use_pm) { + gf_mx2d_add_scale(&transf, tr_state->min_hsize, tr_state->min_hsize); + } else { + Fixed inv_scale = gf_invfix(tr_state->min_hsize); + gf_mx2d_add_scale(&transf, inv_scale, inv_scale); + } + tr_state->pixel_metrics = use_pm; + } + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + GF_Matrix mx_bck, mx; + gf_mx_copy(mx_bck, tr_state->model_matrix); + gf_mx_from_mx2d(&mx, &transf); + /*copy over z scale*/ + mx.m[10] = mx.m[5]; + gf_mx_add_matrix(&mx, &tr_state->model_matrix); + gf_mx_copy(tr_state->model_matrix, mx); + + if (tr_state->traversing_mode==TRAVERSE_SORT) { + visual_3d_matrix_push(tr_state->visual); + visual_3d_matrix_add(tr_state->visual, mx.m); + gf_node_traverse(inline_root, rs); + visual_3d_matrix_pop(tr_state->visual); + } else { + gf_node_traverse(inline_root, rs); + } + gf_mx_copy(tr_state->model_matrix, mx_bck); + + } else +#endif + { + GF_Matrix2D mx_bck; + gf_mx2d_copy(mx_bck, tr_state->transform); + gf_mx2d_pre_multiply(&tr_state->transform, &transf); + gf_node_traverse(inline_root, rs); + gf_mx2d_copy(tr_state->transform, mx_bck); + } + + tr_state->pixel_metrics = prev_pm; + tr_state->min_hsize = min_hsize; + tr_state->vp_size = prev_vp; + tr_state->fliped_coords = prev_coord; +} + + +static Bool gf_sc_handle_event_intern(GF_Compositor *compositor, GF_Event *event, Bool from_user) +{ +#ifdef GF_SR_EVENT_QUEUE + GF_Event *ev; +#else + Bool ret; +#endif + + if (compositor->term && (compositor->interaction_level & GF_INTERACT_INPUT_SENSOR) && (event->type<=GF_EVENT_MOUSEWHEEL)) { + GF_Event evt = *event; + gf_term_mouse_input(compositor->term, &evt.mouse); + } + +/* if (!compositor->interaction_level || (compositor->interaction_level==GF_INTERACT_INPUT_SENSOR) ) { + if (!from_user) { + GF_USER_SENDEVENT(compositor->user, event); + } + return 0; + } +*/ +#ifdef GF_SR_EVENT_QUEUE + switch (event->type) { + case GF_EVENT_MOUSEMOVE: + { + u32 i, count; + gf_mx_p(compositor->ev_mx); + count = gf_list_count(compositor->events); + for (i=0; ievents, i); + if (ev->type == GF_EVENT_MOUSEMOVE) { + ev->mouse = event->mouse; + gf_mx_v(compositor->ev_mx); + return 1; + } + } + gf_mx_v(compositor->ev_mx); + } + default: + ev = (GF_Event *)malloc(sizeof(GF_Event)); + ev->type = event->type; + if (event->type<=GF_EVENT_MOUSEWHEEL) { + ev->mouse = event->mouse; + } else if (event->type==GF_EVENT_TEXTINPUT) { + ev->character = event->character; + } else { + ev->key = event->key; + } + gf_mx_p(compositor->ev_mx); + gf_list_add(compositor->events, ev); + gf_mx_v(compositor->ev_mx); + break; + } + return 0; +#else + gf_sc_lock(compositor, 1); + ret = gf_sc_exec_event(compositor, event); + gf_sc_lock(compositor, 0); + if (!ret && !from_user) { + gf_sc_forward_event(compositor, event); + } + return ret; +#endif +} + + +static Bool gf_sc_on_event_ex(GF_Compositor *compositor , GF_Event *event, Bool from_user) +{ + /*not assigned yet*/ + if (!compositor || !compositor->visual) return 0; + /*we're reconfiguring the video output, cancel all messages*/ + if (compositor->msg_type & GF_SR_IN_RECONFIG) return 0; + + switch (event->type) { + case GF_EVENT_MOVE: + case GF_EVENT_REFRESH: + if (!compositor->draw_next_frame) { + /*when refreshing the window in 3D or with overlays we redraw the scene */ + if (compositor->last_had_overlays +#ifndef GPAC_DISABLE_3D + || compositor->visual->type_3d +#endif + ) { + compositor->draw_next_frame = 1; + } + /*reflush only*/ + else + compositor->draw_next_frame = 2; + } + break; + case GF_EVENT_VIDEO_SETUP: + gf_sc_reset_graphics(compositor); + break; + case GF_EVENT_SIZE: + /*resize message from plugin: if we own the output, resize*/ + if (!compositor->user->os_window_handler) { + /*EXTRA CARE HERE: the caller (video output) is likely a different thread than the compositor one, and the + compositor may be locked on the video output (flush or whatever)!! + */ + Bool lock_ok = gf_mx_try_lock(compositor->mx); + compositor->new_width = event->size.width; + compositor->new_height = event->size.height; + compositor->msg_type |= GF_SR_CFG_SET_SIZE; + if (lock_ok) gf_sc_lock(compositor, 0); + } + /*otherwise let the user decide*/ + else { + return GF_USER_SENDEVENT(compositor->user, event); + } + break; + + case GF_EVENT_KEYDOWN: + case GF_EVENT_KEYUP: + switch (event->key.key_code) { + case GF_KEY_SHIFT: + if (event->type==GF_EVENT_KEYDOWN) { + compositor->key_states |= GF_KEY_MOD_SHIFT; + } else { + compositor->key_states &= ~GF_KEY_MOD_SHIFT; + } + break; + case GF_KEY_CONTROL: + if (event->type==GF_EVENT_KEYDOWN) { + compositor->key_states |= GF_KEY_MOD_CTRL; + } else { + compositor->key_states &= ~GF_KEY_MOD_CTRL; + } + break; + case GF_KEY_ALT: + if (event->type==GF_EVENT_KEYDOWN) { + compositor->key_states |= GF_KEY_MOD_ALT; + } else { + compositor->key_states &= ~GF_KEY_MOD_ALT; + } + break; + +#ifdef GPAC_TRISCOPE_MODE + case GF_KEY_JOYSTICK: + if (event->type==GF_EVENT_KEYUP) { + const char *sOpt; + switch (event->key.hw_code) { + case 4: + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "3dsDepthBuffGain"); + if (sOpt) sscanf(sOpt, "%f", &((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthGain); else ((GF_RenoirHandler *) compositor->RenoirHandler)->MapDepthGain = 1; + ((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthGain -= 5; + sprintf(sOpt, "%f", ((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthGain); + gf_cfg_set_key(compositor->user->config, "Compositor", "3dsDepthBuffGain", sOpt); + /*we'll change it also for flat objects, thus both will be the same*/ + gf_cfg_set_key(compositor->user->config, "Compositor", "FlatDepthBuffGain", sOpt); + gf_cfg_save(compositor->user->config); + return 0; + + case 5: + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "3dsDepthBuffOffset"); + if (sOpt) sscanf(sOpt, "%f", &((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthOffset); else ((GF_RenoirHandler *) compositor->RenoirHandler)->MapDepthOffset = 0; + ((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthOffset -= 5; + sprintf(sOpt, "%f", ((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthOffset); + gf_cfg_set_key(compositor->user->config, "Compositor", "3dsDepthBuffOffset", sOpt); + gf_cfg_save(compositor->user->config); + return 0; + + case 6: + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "3dsDepthBuffGain"); + if (sOpt) sscanf(sOpt, "%f", &((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthGain); else ((GF_RenoirHandler *) compositor->RenoirHandler)->MapDepthGain = 1; + ((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthGain += 5; + sprintf(sOpt, "%f", ((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthGain); + gf_cfg_set_key(compositor->user->config, "Compositor", "3dsDepthBuffGain", sOpt); + /*we'll change it also for flat objects, thus both will be the same*/ + gf_cfg_set_key(compositor->user->config, "Compositor", "FlatDepthBuffGain", sOpt); + gf_cfg_save(compositor->user->config); + return 0; + + case 7: + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "3dsDepthBuffOffset"); + if (sOpt) sscanf(sOpt, "%f", &((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthOffset); else ((GF_RenoirHandler *) compositor->RenoirHandler)->MapDepthOffset = 0; + ((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthOffset += 5; + sprintf(sOpt, "%f", ((GF_RenoirHandler *)compositor->RenoirHandler)->MapDepthOffset); + gf_cfg_set_key(compositor->user->config, "Compositor", "3dsDepthBuffOffset", sOpt); + gf_cfg_save(compositor->user->config); + return 0; + + default: + break; + } + } + break; +#endif + } + + event->key.flags |= compositor->key_states; + /*key sensor*/ + if (compositor->term && (compositor->interaction_level & GF_INTERACT_INPUT_SENSOR) ) { + gf_term_keyboard_input(compositor->term, event->key.key_code, event->key.hw_code, (event->type==GF_EVENT_KEYUP) ? 1 : 0); + } + + return gf_sc_handle_event_intern(compositor, event, from_user); + + case GF_EVENT_TEXTINPUT: + if (compositor->term && (compositor->interaction_level & GF_INTERACT_INPUT_SENSOR) ) + gf_term_string_input(compositor->term , event->character.unicode_char); + + return gf_sc_handle_event_intern(compositor, event, from_user); + /*switch fullscreen off!!!*/ + case GF_EVENT_SHOWHIDE: + gf_sc_set_option(compositor, GF_OPT_FULLSCREEN, !compositor->fullscreen); + break; + + case GF_EVENT_SET_CAPTION: + compositor->video_out->ProcessEvent(compositor->video_out, event); + break; + + case GF_EVENT_MOUSEMOVE: + event->mouse.button = 0; + case GF_EVENT_MOUSEDOWN: + case GF_EVENT_MOUSEUP: + case GF_EVENT_MOUSEWHEEL: + event->mouse.key_states = compositor->key_states; + return gf_sc_handle_event_intern(compositor, event, from_user); + + /*when we process events we don't forward them to the user*/ + default: + return GF_USER_SENDEVENT(compositor->user, event); + } + /*if we get here, event has been consumed*/ + return 1; +} + +static Bool gf_sc_on_event(void *cbck, GF_Event *event) +{ + return gf_sc_on_event_ex((GF_Compositor *)cbck, event, 0); +} + +Bool gf_sc_user_event(GF_Compositor *compositor, GF_Event *event) +{ + switch (event->type) { + case GF_EVENT_SHOWHIDE: + case GF_EVENT_MOVE: + case GF_EVENT_SET_CAPTION: + compositor->video_out->ProcessEvent(compositor->video_out, event); + return 0; + default: + return gf_sc_on_event_ex(compositor, event, 1); + } +} + +void gf_sc_register_extra_graph(GF_Compositor *compositor, GF_SceneGraph *extra_scene, Bool do_remove) +{ + gf_sc_lock(compositor, 1); + if (do_remove) gf_list_del_item(compositor->extra_scenes, extra_scene); + else if (gf_list_find(compositor->extra_scenes, extra_scene)<0) gf_list_add(compositor->extra_scenes, extra_scene); + gf_sc_lock(compositor, 0); +} + + +u32 gf_sc_get_audio_delay(GF_Compositor *compositor) +{ + return compositor->audio_renderer ? compositor->audio_renderer->audio_delay : 0; +} + + +Bool gf_sc_script_action(GF_Compositor *compositor, u32 type, GF_Node *n, GF_JSAPIParam *param) +{ + switch (type) { + case GF_JSAPI_OP_GET_SCALE: param->val = compositor->zoom; return 1; + case GF_JSAPI_OP_SET_SCALE: compositor_2d_set_user_transform(compositor, param->val, compositor->trans_x, compositor->trans_y, 0); return 1; + case GF_JSAPI_OP_GET_ROTATION: param->val = gf_divfix(180*compositor->rotation, GF_PI); return 1; + case GF_JSAPI_OP_SET_ROTATION: compositor->rotation = gf_mulfix(GF_PI, param->val/180); compositor_2d_set_user_transform(compositor, compositor->zoom, compositor->trans_x, compositor->trans_y, 0); return 1; + case GF_JSAPI_OP_GET_TRANSLATE: param->pt.x = compositor->trans_x; param->pt.y = compositor->trans_y; return 1; + case GF_JSAPI_OP_SET_TRANSLATE: compositor_2d_set_user_transform(compositor, compositor->zoom, param->pt.x, param->pt.y, 0); return 1; + /*FIXME - better SMIL timelines support*/ + case GF_JSAPI_OP_GET_TIME: param->time = gf_node_get_scene_time(n); return 1; + case GF_JSAPI_OP_SET_TIME: /*seek_to(param->time);*/ return 0; + /*FIXME - this will not work for inline docs, we will have to store the clipper at the level*/ + case GF_JSAPI_OP_GET_VIEWPORT: +#ifndef GPAC_DISABLE_SVG + if (compositor_svg_get_viewport(n, ¶m->rc)) return 1; +#endif + param->rc = gf_rect_center(compositor->traverse_state->vp_size.x, compositor->traverse_state->vp_size.y); + if (!compositor->visual->center_coords) { + param->rc.x = 0; + param->rc.y = 0; + } + return 1; + case GF_JSAPI_OP_SET_FOCUS: compositor->focus_node = param->node; return 1; + case GF_JSAPI_OP_GET_FOCUS: param->node = compositor->focus_node; return 1; + + /*same routine: traverse tree from root to target, collecting both bounds and transform matrix*/ + case GF_JSAPI_OP_GET_LOCAL_BBOX: + case GF_JSAPI_OP_GET_SCREEN_BBOX: + case GF_JSAPI_OP_GET_TRANSFORM: + { + GF_TraverseState tr_state; + memset(&tr_state, 0, sizeof(tr_state)); + tr_state.traversing_mode = TRAVERSE_GET_BOUNDS; + tr_state.visual = compositor->visual; + tr_state.vp_size = compositor->traverse_state->vp_size; + tr_state.for_node = n; + tr_state.ignore_strike = 1; + gf_mx2d_init(tr_state.transform); + gf_mx2d_init(tr_state.mx_at_node); + + + if (type==GF_JSAPI_OP_GET_LOCAL_BBOX) { + GF_SAFEALLOC(tr_state.svg_props, SVGPropertiesPointers); + gf_svg_properties_init_pointers(tr_state.svg_props); + tr_state.abort_bounds_traverse=1; + gf_node_traverse(n, &tr_state); + gf_svg_properties_reset_pointers(tr_state.svg_props); + free(tr_state.svg_props); + } else { + gf_node_traverse(gf_sg_get_root_node(compositor->scene), &tr_state); + } + if (!tr_state.bounds.height && !tr_state.bounds.width && !tr_state.bounds.x && !tr_state.bounds.height) + tr_state.abort_bounds_traverse=0; + + gf_mx2d_pre_multiply(&tr_state.mx_at_node, &compositor->traverse_state->transform); + + if (type==GF_JSAPI_OP_GET_LOCAL_BBOX) { + gf_bbox_from_rect(¶m->bbox, &tr_state.bounds); + } else if (type==GF_JSAPI_OP_GET_TRANSFORM) { + gf_mx_from_mx2d(¶m->mx, &tr_state.mx_at_node); + } else { + gf_mx2d_apply_rect(&tr_state.mx_at_node, &tr_state.bounds); + gf_bbox_from_rect(¶m->bbox, &tr_state.bounds); + } + if (!tr_state.abort_bounds_traverse) param->bbox.is_set = 0; + } + return 1; + case GF_JSAPI_OP_LOAD_URL: + { + GF_Node *target; + char *sub_url = strrchr(param->uri.url, '#'); + if (!sub_url) return 0; + target = gf_sg_find_node_by_name(gf_node_get_graph(n), sub_url+1); + if (target && (gf_node_get_tag(target)==TAG_MPEG4_Viewport) ) { + ((M_Viewport *)target)->set_bind = 1; + ((M_Viewport *)target)->on_set_bind(n); + return 1; + } + return 0; + } + case GF_JSAPI_OP_GET_FPS: + param->time = gf_sc_get_fps(compositor, 0); + return 1; + case GF_JSAPI_OP_GET_SPEED: + param->time = 0; +#ifndef GPAC_DISABLE_3D + if (compositor->visual->type_3d==2) { + param->time = FIX2FLT(compositor->visual->camera.speed); + } +#endif + return 1; + case GF_JSAPI_OP_PAUSE_SVG: + { + u32 tag = gf_node_get_tag(n); + switch(tag) { +#ifndef GPAC_DISABLE_SVG + case TAG_SVG_audio: svg_pause_audio(n, 1); break; + case TAG_SVG_video: svg_pause_video(n, 1); break; + case TAG_SVG_animation: svg_pause_animation(n, 1); break; +#endif + } + } + return 1; + case GF_JSAPI_OP_RESUME_SVG: + { + u32 tag = gf_node_get_tag(n); + switch(tag) { +#ifndef GPAC_DISABLE_SVG + case TAG_SVG_audio: svg_pause_audio(n, 0); break; + case TAG_SVG_video: svg_pause_video(n, 0); break; + case TAG_SVG_animation: svg_pause_animation(n, 0); break; +#endif + } + } + return 1; + } + return 0; +} + +Bool gf_sc_pick_in_clipper(GF_TraverseState *tr_state, GF_Rect *clip) +{ +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + SFVec3f pos; + GF_Matrix mx; + GF_Ray r; + gf_mx_copy(mx, tr_state->model_matrix); + gf_mx_inverse(&mx); + r = tr_state->ray; + gf_mx_apply_ray(&mx, &r); + if (!compositor_get_2d_plane_intersection(&r, &pos)) return 0; + if ( (pos.x < clip->x) || (pos.y > clip->y) + || (pos.x > clip->x + clip->width) || (pos.y < clip->y - clip->height) ) return 0; + + } else +#endif + { + GF_Rect rc = *clip; + GF_Point2D pt; + gf_mx2d_apply_rect(&tr_state->transform, &rc); + pt.x = tr_state->ray.orig.x; + pt.y = tr_state->ray.orig.y; + + if ( (pt.x < rc.x) || (pt.y > rc.y) + || (pt.x > rc.x + rc.width) || (pt.y < rc.y - rc.height) ) return 0; + } + return 1; +} + + +Bool gf_sc_has_text_selection(GF_Compositor *compositor) +{ + return (compositor->store_text_state==GF_SC_TSEL_FROZEN) ? 1 : 0; +} + +const char *gf_sc_get_selected_text(GF_Compositor *compositor) +{ + const u16 *srcp; + u32 len; + if (compositor->store_text_state != GF_SC_TSEL_FROZEN) return NULL; + + gf_sc_lock(compositor, 1); + + compositor->traverse_state->traversing_mode = TRAVERSE_GET_TEXT; + if (compositor->sel_buffer) { + free(compositor->sel_buffer); + compositor->sel_buffer = NULL; + } + compositor->sel_buffer_len = 0; + compositor->sel_buffer_alloc = 0; + gf_node_traverse(compositor->text_selection, compositor->traverse_state); + compositor->traverse_state->traversing_mode = 0; + compositor->sel_buffer[compositor->sel_buffer_len]=0; + srcp = compositor->sel_buffer; + if (compositor->selected_text) free(compositor->selected_text); + compositor->selected_text = malloc(sizeof(char)*2*compositor->sel_buffer_len); + len = gf_utf8_wcstombs(compositor->selected_text, 2*compositor->sel_buffer_len, &srcp); + if ((s32)len<0) len = 0; + compositor->selected_text[len] = 0; + gf_sc_lock(compositor, 0); + + return compositor->selected_text; +} + + +void gf_sc_check_focus_upon_destroy(GF_Node *n) +{ + GF_Compositor *compositor = gf_sc_get_compositor(n); + if (compositor && (compositor->focus_node==n)) { + compositor->focus_node = NULL; + compositor->focus_text_type = 0; + compositor->focus_uses_dom_events = 0; + gf_list_reset(compositor->focus_ancestors); + gf_list_reset(compositor->focus_use_stack); + } +} + diff --git a/src/compositor/compositor_2d.c b/src/compositor/compositor_2d.c new file mode 100644 index 0000000..6f5d664 --- /dev/null +++ b/src/compositor/compositor_2d.c @@ -0,0 +1,858 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "visual_manager.h" +#include "nodes_stacks.h" +#include + +#ifdef GPAC_TRISCOPE_MODE +#include "../src/compositor/triscope_renoir/triscope_renoir.h" +#endif + +GF_Err compositor_2d_get_video_access(GF_VisualManager *visual) +{ + GF_Err e; + GF_Compositor *compositor = visual->compositor; + + if (!visual->raster_surface) return GF_BAD_PARAM; + compositor->hw_locked = 0; + e = GF_IO_ERR; + + /*try from device*/ + if (compositor->rasterizer->surface_attach_to_device && compositor->video_out->LockOSContext) { + compositor->hw_context = compositor->video_out->LockOSContext(compositor->video_out, 1); + if (compositor->hw_context) { + e = compositor->rasterizer->surface_attach_to_device(visual->raster_surface, compositor->hw_context, compositor->vp_width, compositor->vp_height); + if (!e) { + visual->is_attached = 1; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Video surface handle attached to raster\n")); + return GF_OK; + } + compositor->video_out->LockOSContext(compositor->video_out, 0); + GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor2D] Cannot attach video surface handle to raster: %s\n", gf_error_to_string(e) )); + } + } + + /*TODO - collect hw accelerated blit routines if any*/ + + if (compositor->video_out->LockBackBuffer(compositor->video_out, &compositor->hw_surface, 1)==GF_OK) { + compositor->hw_locked = 1; + e = compositor->rasterizer->surface_attach_to_buffer(visual->raster_surface, compositor->hw_surface.video_buffer, + compositor->hw_surface.width, + compositor->hw_surface.height, + compositor->hw_surface.pitch, + (GF_PixelFormat) compositor->hw_surface.pixel_format); + if (!e) { + visual->is_attached = 1; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Video surface memory attached to raster\n")); +#ifdef GPAC_TRISCOPE_MODE + /*CALL SNOUTPUT FOR RENOIR*/ + SetRenoirOutput(compositor); +#endif + return GF_OK; + } + GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor2D] Cannot attach video surface memory to raster: %s\n", gf_error_to_string(e) )); + compositor->video_out->LockBackBuffer(compositor->video_out, &compositor->hw_surface, 0); + } + compositor->hw_locked = 0; + visual->is_attached = 0; + return e; +} + +void compositor_2d_release_video_access(GF_VisualManager *visual) +{ + GF_Compositor *compositor = visual->compositor; + if (visual->is_attached) { + compositor->rasterizer->surface_detach(visual->raster_surface); + visual->is_attached = 0; + } + if (compositor->hw_context) { + compositor->video_out->LockOSContext(compositor->video_out, 0); + compositor->hw_context = NULL; + } else if (compositor->hw_locked) { + compositor->video_out->LockBackBuffer(compositor->video_out, &compositor->hw_surface, 0); + compositor->hw_locked = 0; + } +} + + +static Bool compositor_2d_draw_bitmap_ex(GF_VisualManager *visual, GF_TextureHandler *txh, DrawableContext *ctx, GF_IRect *clip, GF_Rect *unclip, u8 alpha, GF_ColorKey *col_key, GF_TraverseState *tr_state, Bool force_soft_blt) +{ + GF_VideoSurface video_src; + Fixed w_scale, h_scale, tmp; + GF_Err e; + Bool use_soft_stretch, use_blit, flush_video; + u32 overlay_type; + GF_Window src_wnd, dst_wnd; + u32 output_width, output_height, hw_caps; + GF_IRect clipped_final = *clip; + GF_Rect final = *unclip; + + if (!txh->data) return 1; + + if (!visual->compositor->has_size_info && !(visual->compositor->msg_type & GF_SR_CFG_OVERRIDE_SIZE) + && (visual->compositor->override_size_flags & 1) + && !(visual->compositor->override_size_flags & 2) + ) { + if ( (visual->compositor->scene_width < txh->width) + || (visual->compositor->scene_height < txh->height)) { + visual->compositor->scene_width = txh->width; + visual->compositor->scene_height = txh->height; + visual->compositor->msg_type |= GF_SR_CFG_OVERRIDE_SIZE; + return 1; + } + } + + /*this should never happen but we check for float rounding safety*/ + if (final.width<=0 || final.height <=0) return 1; + + w_scale = final.width / txh->width; + h_scale = final.height / txh->height; + + /*use entire video surface for un-centering coord system*/ + output_width = visual->compositor->vp_width; + output_height = visual->compositor->vp_height; + + /*take care of pixel rounding for odd width/height and make sure we strictly draw in the clipped bounds*/ + if (visual->center_coords) { + clipped_final.x += output_width / 2; + final.x += INT2FIX( output_width / 2 ); + + clipped_final.y = output_height/ 2 - clipped_final.y; + final.y = INT2FIX( output_height / 2) - final.y; + + } else { + final.y -= final.height; + clipped_final.y -= clipped_final.height; + } + + /*make sure we lie in the final rect (this is needed for directdraw mode)*/ + if (clipped_final.x<0) { + clipped_final.width += clipped_final.x; + clipped_final.x = 0; + if (clipped_final.width <= 0) return 0; + } + if (clipped_final.y<0) { + clipped_final.height += clipped_final.y; + clipped_final.y = 0; + if (clipped_final.height <= 0) return 0; + } + if (clipped_final.x + clipped_final.width > (s32) output_width) { + clipped_final.width = output_width - clipped_final.x; + clipped_final.x = output_width - clipped_final.width; + } + if (clipped_final.y + clipped_final.height > (s32) output_height) { + clipped_final.height = output_height - clipped_final.y; + clipped_final.y = output_height - clipped_final.height; + } + /*needed in direct drawing since clipping is not performed*/ + if (clipped_final.width<=0 || clipped_final.height <=0) + return 0; + + /*set dest window*/ + dst_wnd.x = (u32) clipped_final.x; + dst_wnd.y = (u32) clipped_final.y; + dst_wnd.w = (u32) clipped_final.width; + dst_wnd.h = (u32) clipped_final.height; + + +#ifdef GPAC_FIXED_POINT +#define ROUND_FIX(_v) \ + _v = FIX2INT(tmp); +#else +#define ROUND_FIX(_v) \ + _v = FIX2INT(tmp); \ + tmp -= INT2FIX(_v); \ + if (tmp>99*FIX_ONE/100) { _v++; tmp = 0; } \ + if (ABS(tmp) > FIX_EPSILON) use_blit = 0; +#endif + + use_blit = 1; + /*compute SRC window*/ + src_wnd.x = src_wnd.y = 0; + tmp = gf_divfix(INT2FIX(clipped_final.x) - final.x, w_scale); + if (tmp<0) tmp=0; + ROUND_FIX(src_wnd.x); + + tmp = gf_divfix(INT2FIX(clipped_final.y) - final.y, h_scale); + if (tmp<0) tmp=0; + ROUND_FIX(src_wnd.y); + + tmp = gf_divfix(INT2FIX(clip->width), w_scale); + ROUND_FIX(src_wnd.w); + + tmp = gf_divfix(INT2FIX(clip->height), h_scale); + ROUND_FIX(src_wnd.h); + +#undef ROUND_FIX + + if (src_wnd.w>txh->width) src_wnd.w=txh->width; + if (src_wnd.h>txh->height) src_wnd.h=txh->height; + + if (!src_wnd.w || !src_wnd.h) return 1; + /*make sure we lie in src bounds*/ + if (src_wnd.x + src_wnd.w>txh->width) src_wnd.w = txh->width - src_wnd.x; + if (src_wnd.y + src_wnd.h>txh->height) src_wnd.h = txh->height - src_wnd.y; + + /*can we use hardware blitter ?*/ + hw_caps = visual->compositor->video_out->hw_caps; + overlay_type = 0; + flush_video = 0; + use_soft_stretch = 1; + if (!force_soft_blt) { + + /*avoid partial redraw that don't come close to src pixels with the bliter, this leads to ugly artefacts - + fall back to rasterizer*/ +// if (!(ctx->flags & CTX_TEXTURE_DIRTY) && !use_blit && (src_wnd.x || src_wnd.y) ) +// return 0; + + switch (txh->pixelformat) { + case GF_PIXEL_RGB_24: + case GF_PIXEL_BGR_24: + use_soft_stretch = 0; + break; + case GF_PIXEL_YV12: + case GF_PIXEL_IYUV: + case GF_PIXEL_I420: + if (hw_caps & GF_VIDEO_HW_HAS_YUV) use_soft_stretch = 0; + else if (hw_caps & GF_VIDEO_HW_HAS_YUV_OVERLAY) overlay_type = 1; + break; + default: + break; + } + /*disable based on settings*/ + if (!visual->compositor->enable_yuv_hw + || (ctx->col_mat || (alpha!=0xFF) || !visual->compositor->video_out->Blit) + ) { + use_soft_stretch = 1; + overlay_type = 0; + } + if (visual->compositor->disable_partial_hw_blit && ((src_wnd.w!=txh->width) || (src_wnd.h!=txh->height) )) { + use_soft_stretch = 1; + } + + /*disable HW color keying - not compatible with MPEG-4 MaterialKey*/ + if (col_key) { + use_soft_stretch = 1; + overlay_type = 0; + } + + if (overlay_type) { + /*no more than one overlay is supported at the current time*/ + if (visual->overlays) { + ctx->drawable->flags &= ~DRAWABLE_IS_OVERLAY; + overlay_type = 0; + } + /*direct draw or not last context: we must queue the overlay*/ + else if (tr_state->direct_draw || (ctx->next && ctx->next->drawable)) { + overlay_type = 2; + } + /*OK we can overlay this video - if full display, don't flush*/ + if (overlay_type==1) { + if (dst_wnd.w==visual->compositor->display_width) flush_video = 0; + else if (dst_wnd.h==visual->compositor->display_height) flush_video = 0; + else flush_video = visual->has_modif; + } + /*if no color keying, we cannot queue the overlay*/ + else if (!visual->compositor->video_out->overlay_color_key) { + overlay_type = 0; + } + } + + } + + video_src.height = txh->height; + video_src.width = txh->width; + video_src.pitch = txh->stride; + video_src.pixel_format = txh->pixelformat; + video_src.video_buffer = txh->data; + if (overlay_type) { + if (overlay_type==2) { + GF_IRect o_rc; + GF_OverlayStack *ol, *first; + + /*queue overlay in order*/ + GF_SAFEALLOC(ol, GF_OverlayStack); + ol->ctx = ctx; + ol->dst = dst_wnd; + ol->src = src_wnd; + first = visual->overlays; + if (first) { + while (first->next) first = first->next; + first->next = ol; + } else { + visual->overlays = ol; + } + + if (visual->center_coords) { + o_rc.x = dst_wnd.x - output_width/2; + o_rc.y = output_height/2- dst_wnd.y; + } else { + o_rc.x = dst_wnd.x; + o_rc.y = dst_wnd.y; + } + o_rc.width = dst_wnd.w; + o_rc.height = dst_wnd.h; + visual_2d_clear(visual, &o_rc, visual->compositor->video_out->overlay_color_key); + visual->has_overlays = 1; + /*mark drawable as overlay*/ + ctx->drawable->flags |= DRAWABLE_IS_OVERLAY; + + /*prevents this context from being removed in direct draw mode by requesting a new one + but not allocating it*/ + if (tr_state->direct_draw) + visual_2d_get_drawable_context(visual); + return 1; + } + /*top level overlay*/ + if (flush_video) { + GF_Window rc; + rc.x = rc.y = 0; + rc.w = visual->compositor->display_width; + rc.h = visual->compositor->display_height; + + visual_2d_release_raster(visual); + visual->compositor->video_out->Flush(visual->compositor->video_out, &rc); + visual_2d_init_raster(visual); + } + visual->compositor->skip_flush = 1; + + e = visual->compositor->video_out->Blit(visual->compositor->video_out, &video_src, &src_wnd, &dst_wnd, 1); + if (!e) { + /*mark drawable as overlay*/ + ctx->drawable->flags |= DRAWABLE_IS_OVERLAY; + visual->has_overlays = 1; + return 1; + } + GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor2D] Error during overlay blit - trying with soft one\n")); + visual->compositor->skip_flush = 0; + } + + /*most graphic cards can't perform bliting on locked surface - force unlock by releasing the hardware*/ + visual_2d_release_raster(visual); + + if (!use_soft_stretch) { + e = visual->compositor->video_out->Blit(visual->compositor->video_out, &video_src, &src_wnd, &dst_wnd, 0); + /*HW pb, try soft*/ + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor2D] Error during hardware blit - trying with soft one\n")); + use_soft_stretch = 1; + if (!visual->compositor->video_memory) { + GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor2D] Reconfiguring video output to use video memory\n")); + visual->compositor->video_memory = 1; + visual->compositor->root_visual_setup = 0; + } + } + } + if (use_soft_stretch) { + GF_VideoSurface backbuffer; + e = visual->compositor->video_out->LockBackBuffer(visual->compositor->video_out, &backbuffer, 1); + if (!e) { + gf_stretch_bits(&backbuffer, &video_src, &dst_wnd, &src_wnd, 0, alpha, 0, col_key, ctx->col_mat); + e = visual->compositor->video_out->LockBackBuffer(visual->compositor->video_out, &backbuffer, 0); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor2D] Cannot lock back buffer - Error %s\n", gf_error_to_string(e) )); + } + } + visual->has_modif = 1; + visual_2d_init_raster(visual); + return 1; +} + + + +Bool compositor_2d_draw_bitmap(GF_VisualManager *visual, GF_TraverseState *tr_state, DrawableContext *ctx, GF_ColorKey *col_key) +{ + u8 alpha = 0xFF; + + + if (!ctx->aspect.fill_texture) return 1; + /*check if texture is ready*/ + if (!ctx->aspect.fill_texture->data) return 0; + if (ctx->transform.m[0]<0) return 0; + /*check if the <0 value is due to a flip in he scene description or + due to bifs<->svg... context switching*/ + if (ctx->transform.m[4]<0) { + if (!(ctx->flags & CTX_FLIPED_COORDS)) return 0; + } else { + if (ctx->flags & CTX_FLIPED_COORDS) return 0; + } + if (ctx->transform.m[1] || ctx->transform.m[3]) return 0; + if ((ctx->flags & CTX_HAS_APPEARANCE) && ctx->appear && ((M_Appearance*)ctx->appear)->textureTransform) + return 0; + + alpha = GF_COL_A(ctx->aspect.fill_color); + /*THIS IS A HACK, will not work when setting filled=0, transparency and XLineProps*/ + if (!alpha) alpha = GF_COL_A(ctx->aspect.line_color); + + ctx->aspect.fill_texture->flags |= GF_SR_TEXTURE_USED; + if (!alpha) return 1; + + switch (ctx->aspect.fill_texture->pixelformat) { + case GF_PIXEL_RGB_24: + case GF_PIXEL_BGR_24: + case GF_PIXEL_RGB_555: + case GF_PIXEL_RGB_565: + case GF_PIXEL_ARGB: + case GF_PIXEL_RGBA: + case GF_PIXEL_YV12: + case GF_PIXEL_IYUV: + case GF_PIXEL_I420: + case GF_PIXEL_YUVA: +#ifdef GPAC_TRISCOPE_MODE + case GF_PIXEL_RGBDS: +#endif + break; + /*the rest has to be displayed through brush for now, we only use YUV and RGB pool*/ + default: + return 0; + } + + /*direct drawing, no clippers */ + if (tr_state->direct_draw) { + +#ifdef GPAC_TRISCOPE_MODE +#ifdef TRISCOPE_DEBUG + printf("\nEntering SetRenoirTexture...\n"); +#endif + return SetRenoirTexture(visual, ctx->aspect.fill_texture, ctx, &ctx->bi->clip, &ctx->bi->unclip, alpha, col_key, tr_state, 0); + +#else + return compositor_2d_draw_bitmap_ex(visual, ctx->aspect.fill_texture, ctx, &ctx->bi->clip, &ctx->bi->unclip, alpha, col_key, tr_state, 0); + +#endif + + } + /*draw bitmap for all dirty rects*/ + else { + u32 i; + GF_IRect clip; + for (i=0; ivisual->to_redraw.count; i++) { + /*there's an opaque region above, don't draw*/ +#ifdef TRACK_OPAQUE_REGIONS + if (tr_state->visual->draw_node_index < tr_state->visual->to_redraw.opaque_node_index[i]) continue; +#endif + clip = ctx->bi->clip; + gf_irect_intersect(&clip, &tr_state->visual->to_redraw.list[i]); + if (clip.width && clip.height) { + if (!compositor_2d_draw_bitmap_ex(visual, ctx->aspect.fill_texture, ctx, &clip, &ctx->bi->unclip, alpha, col_key, tr_state, 0)) + return 0; + } + } + } + return 1; +} + + +GF_Err compositor_2d_set_aspect_ratio(GF_Compositor *compositor) +{ + Double ratio; + GF_Event evt; + GF_Err e; + Fixed scaleX, scaleY; + + compositor->output_width = compositor->scene_width; + compositor->output_height = compositor->scene_height; + compositor->vp_x = compositor->vp_y = 0; + scaleX = scaleY = FIX_ONE; + + /*force complete clean*/ + compositor->traverse_state->invalidate_all = 1; + + if (!compositor->has_size_info && !(compositor->override_size_flags & 2) ) { + compositor->output_width = compositor->display_width; + compositor->output_height = compositor->display_height; + compositor->vp_width = compositor->visual->width = compositor->output_width; + compositor->vp_height = compositor->visual->height = compositor->output_height; + } else { + compositor->vp_width = compositor->display_width; + compositor->vp_height = compositor->display_height; + + switch (compositor->aspect_ratio) { + case GF_ASPECT_RATIO_FILL_SCREEN: + break; + case GF_ASPECT_RATIO_16_9: + compositor->vp_width = compositor->display_width; + compositor->vp_height = 9 * compositor->display_width / 16; + if (compositor->vp_height>compositor->display_height) { + compositor->vp_height = compositor->display_height; + compositor->vp_width = 16 * compositor->display_height / 9; + } + break; + case GF_ASPECT_RATIO_4_3: + compositor->vp_width = compositor->display_width; + compositor->vp_height = 3 * compositor->display_width / 4; + if (compositor->vp_height>compositor->display_height) { + compositor->vp_height = compositor->display_height; + compositor->vp_width = 4 * compositor->display_height / 3; + } + break; + default: + ratio = compositor->scene_height; + ratio /= compositor->scene_width; + if (compositor->vp_width * ratio > compositor->vp_height) { + compositor->vp_width = compositor->vp_height * compositor->scene_width; + compositor->vp_width /= compositor->scene_height; + } + else { + compositor->vp_height = compositor->vp_width * compositor->scene_height; + compositor->vp_height /= compositor->scene_width; + } + break; + } + compositor->vp_x = (compositor->display_width - compositor->vp_width) / 2; + compositor->vp_y = (compositor->display_height - compositor->vp_height) / 2; + + scaleX = gf_divfix(INT2FIX(compositor->vp_width), INT2FIX(compositor->scene_width)); + if (!scaleX) scaleX = FIX_ONE; + scaleY = gf_divfix(INT2FIX(compositor->vp_height), INT2FIX(compositor->scene_height)); + if (!scaleY) scaleY = FIX_ONE; + + if (!compositor->scalable_zoom) { + compositor->output_width = compositor->scene_width; + compositor->output_height = compositor->scene_height; + compositor->vp_width = FIX2INT(gf_divfix(INT2FIX(compositor->display_width), scaleX)); + compositor->vp_height = FIX2INT(gf_divfix(INT2FIX(compositor->display_height), scaleY)); + + compositor->vp_x = (compositor->vp_width - compositor->output_width) / 2; + compositor->vp_y = (compositor->vp_height - compositor->output_height) / 2; + + scaleX = scaleY = FIX_ONE; + } else { +/* + compositor->output_width = compositor->vp_width; + compositor->output_height = compositor->vp_height; +*/ + compositor->output_width = compositor->display_width; + compositor->output_height = compositor->display_height; + compositor->vp_width = compositor->display_width; + compositor->vp_height = compositor->display_height; + } + compositor->visual->width = compositor->output_width; + compositor->visual->height = compositor->output_height; + } + + /*resize hardware surface*/ + evt.type = GF_EVENT_VIDEO_SETUP; + evt.setup.width = compositor->vp_width; + evt.setup.height = compositor->vp_height; + evt.setup.opengl_mode = 0; + evt.setup.system_memory = compositor->video_memory ? 0 : 1; + + e = compositor->video_out->ProcessEvent(compositor->video_out, &evt); + if (e) return e; + + if (compositor->has_size_info) { + compositor->traverse_state->vp_size.x = INT2FIX(compositor->scene_width); + compositor->traverse_state->vp_size.y = INT2FIX(compositor->scene_height); + } else { + compositor->traverse_state->vp_size.x = INT2FIX(compositor->output_width); + compositor->traverse_state->vp_size.y = INT2FIX(compositor->output_height); + } + /*set scale factor*/ + compositor_set_ar_scale(compositor, scaleX, scaleY); + return GF_OK; +} + +void compositor_send_resize_event(GF_Compositor *compositor, Fixed old_z, Fixed old_tx, Fixed old_ty, Bool is_resize) +{ +#ifndef GPAC_DISABLE_SVG + GF_Node *root; + root = gf_sg_get_root_node(compositor->scene); + /*if root node is DOM, sent a resize event*/ + if (root /* && (gf_node_get_tag(root) >= GF_NODE_FIRST_DOM_NODE_TAG) */) { + GF_DOM_Event evt; + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.prev_scale = compositor->scale_x*old_z; + evt.new_scale = compositor->scale_x*compositor->zoom; + evt.bubbles = 1; + + if (is_resize) { + evt.type = GF_EVENT_RESIZE; + evt.screen_rect.width = INT2FIX(compositor->display_width); + evt.screen_rect.height = INT2FIX(compositor->display_height); + } else if (evt.prev_scale == evt.new_scale) { + /*cannot get params for scroll events*/ + evt.type = GF_EVENT_SCROLL; + } else { + evt.screen_rect.x = INT2FIX(compositor->vp_x); + evt.screen_rect.y = INT2FIX(compositor->vp_y); + evt.screen_rect.width = INT2FIX(compositor->output_width); + evt.screen_rect.height = INT2FIX(compositor->output_height); + evt.prev_translate.x = old_tx; + evt.prev_translate.y = old_ty; + evt.new_translate.x = compositor->trans_x; + evt.new_translate.y = compositor->trans_y; + evt.type = GF_EVENT_ZOOM; + evt.bubbles = 0; + } + gf_dom_event_fire(gf_sg_get_root_node(compositor->scene), &evt); + } +#endif +} +void compositor_2d_set_user_transform(GF_Compositor *compositor, Fixed zoom, Fixed tx, Fixed ty, Bool is_resize) +{ + Fixed ratio; + Fixed old_tx, old_ty, old_z; + + gf_sc_lock(compositor, 1); + old_tx = tx; + old_ty = ty; + old_z = compositor->zoom; + + if (zoom <= 0) zoom = FIX_ONE/1000; + compositor->trans_x = tx; + compositor->trans_y = ty; + + if (zoom != compositor->zoom) { + ratio = gf_divfix(zoom, compositor->zoom); + compositor->trans_x = gf_mulfix(compositor->trans_x, ratio); + compositor->trans_y = gf_mulfix(compositor->trans_y, ratio); + compositor->zoom = zoom; + compositor->zoom_changed = 1; + + /*recenter visual*/ + if (!compositor->visual->center_coords) { + Fixed c_x, c_y, nc_x, nc_y; + c_x = INT2FIX(compositor->display_width/2); + nc_y = c_y = INT2FIX(compositor->display_height/2); + nc_x = gf_mulfix(c_x, ratio); + nc_y = gf_mulfix(c_y, ratio); + compositor->trans_x -= (nc_x-c_x); + compositor->trans_y -= (nc_y-c_y); + } + } + gf_mx2d_init(compositor->traverse_state->transform); + gf_mx2d_add_scale(&compositor->traverse_state->transform, gf_mulfix(compositor->zoom,compositor->scale_x), gf_mulfix(compositor->zoom,compositor->scale_y)); + + gf_mx2d_add_translation(&compositor->traverse_state->transform, compositor->trans_x, compositor->trans_y); + if (compositor->rotation) gf_mx2d_add_rotation(&compositor->traverse_state->transform, 0, 0, compositor->rotation); + + if (!compositor->visual->center_coords) { + gf_mx2d_add_translation(&compositor->traverse_state->transform, INT2FIX(compositor->vp_x), INT2FIX(compositor->vp_y)); + } + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Changing Zoom (%g) and Pan (%g %g)\n", FIX2FLT(compositor->zoom), FIX2FLT(compositor->trans_x) , FIX2FLT(compositor->trans_y))); + + + compositor->draw_next_frame = 1; + compositor->traverse_state->invalidate_all = 1; + + /*for zoom&pan, send the event right away. For resize/scroll, wait for the frame to be drawn before sending it + otherwise viewport info of SVG nodes won't be correct*/ + if (!is_resize) compositor_send_resize_event(compositor, old_z, old_tx, old_ty, 0); + gf_sc_lock(compositor, 0); +} + + + +GF_Rect compositor_2d_update_clipper(GF_TraverseState *tr_state, GF_Rect this_clip, Bool *need_restore, GF_Rect *original, Bool for_layer) +{ + GF_Rect clip, orig; + if (for_layer) { + orig = tr_state->layer_clipper; + *need_restore = tr_state->has_layer_clip; + } else { + orig = tr_state->clipper; + *need_restore = tr_state->has_clip; + } + *original = orig; + + clip = this_clip; + if (*need_restore) { +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + GF_Matrix mx; + gf_mx_copy(mx, tr_state->model_matrix); + gf_mx_inverse(&mx); + gf_mx_apply_rect(&mx, &orig); + } else +#endif + { + GF_Matrix2D mx2d; + gf_mx2d_copy(mx2d, tr_state->transform); + gf_mx2d_inverse(&mx2d); + gf_mx2d_apply_rect(&mx2d, &orig); + } + + if (clip.x < orig.x) { + clip.width -= (orig.x - clip.x); + clip.x = orig.x; + } + if (clip.x + clip.width > orig.x + orig.width) { + clip.width = orig.x + orig.width - clip.x; + } + if (clip.y > orig.y) { + clip.height -= (clip.y - orig.y); + clip.y = orig.y; + } + if (clip.y - clip.height < orig.y - orig.height) { + clip.height = clip.y - orig.y + orig.height; + } + } + if (for_layer) { + tr_state->layer_clipper = clip; + tr_state->has_layer_clip = 1; + } else { + tr_state->clipper = clip; +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + /*retranslate to world coords*/ + gf_mx_apply_rect(&tr_state->model_matrix, &tr_state->clipper); + /*if 2D, also update with user zoom and translation*/ + if (!tr_state->camera->is_3D) + gf_mx_apply_rect(&tr_state->camera->modelview, &tr_state->clipper); + } else +#endif + + gf_mx2d_apply_rect(&tr_state->transform, &tr_state->clipper); + + tr_state->has_clip = 1; + } + return clip; +} + + +/*overlay management*/ +Bool visual_2d_overlaps_overlay(GF_VisualManager *visual, DrawableContext *ctx, GF_TraverseState *tr_state) +{ + u32 res = 0; + GF_OverlayStack *ol; + GF_Compositor *compositor = visual->compositor; + if (compositor->visual != visual) return 0; + + ol = visual->overlays; + while (ol) { + u32 i; + GF_IRect clip; + if (ctx == ol->ctx) { + ol = ol->next; + continue; + } + clip = ctx->bi->clip; + if (!ol->ra.count && !gf_irect_overlaps(&ol->ctx->bi->clip, &clip)) { + ol = ol->next; + continue; + } + /*check previsously drawn areas*/ + for (i=0; ira.count; i++) { + /*we have drawn something here, don't draw*/ + if (gf_irect_inside(&ol->ra.list[i], &clip)) + break; + } + res++; + if (ira.count) { + ol = ol->next; + continue; + } + + /*note that we add the entire cliper, not the intersection with the overlay one. This is a simple way + to handle the case where Drawble2 overlaps Drawable1 overlaps Overlay but Drawable2 doesn't overlaps Overlay + by adding the entire drawable cliper, we will postpone drawing of all interconnected regions touching the overlay*/ + ra_union_rect(&ol->ra, &clip); + ol = ol->next; + } + return res ? 1 : 0; +} + +void visual_2d_flush_overlay_areas(GF_VisualManager *visual, GF_TraverseState *tr_state) +{ + DrawableContext *ctx; + GF_OverlayStack *ol; + GF_Compositor *compositor = visual->compositor; + if (compositor->visual != visual) return; + + /*draw all overlays*/ + tr_state->traversing_mode = TRAVERSE_DRAW_2D; + ol = visual->overlays; + while (ol) { + u32 i; + Bool needs_draw = 1; + GF_IRect the_clip, vid_clip; + + ra_refresh(&ol->ra); + + for (i=0; ira.count; i++) { + the_clip = ol->ra.list[i]; + + /*draw all objects above this overlay*/ + ctx = ol->ctx->next; + while (ctx && ctx->drawable) { + if (gf_irect_overlaps(&ctx->bi->clip, &the_clip)) { + GF_IRect prev_clip = ctx->bi->clip; + + if (needs_draw) { + /*if first object above is not transparent and completely covers the overlay skip video redraw*/ + if ((ctx->flags & CTX_IS_TRANSPARENT) || !gf_irect_inside(&prev_clip, &the_clip)) { + vid_clip = ol->ra.list[i]; + gf_irect_intersect(&vid_clip, &ol->ctx->bi->clip); + compositor_2d_draw_bitmap_ex(visual, ol->ctx->aspect.fill_texture, ol->ctx, &vid_clip, &ol->ctx->bi->unclip, 0xFF, NULL, tr_state, 1); + } + needs_draw = 0; + } + gf_irect_intersect(&ctx->bi->clip, &the_clip); + tr_state->ctx = ctx; + + if (ctx->drawable->flags & DRAWABLE_USE_TRAVERSE_DRAW) { + gf_node_traverse(ctx->drawable->node, tr_state); + } else { + drawable_draw(ctx->drawable, tr_state); + } + ctx->bi->clip = prev_clip; + } + ctx = ctx->next; + } + } + ol = ol->next; + } +} + +void visual_2d_draw_overlays(GF_VisualManager *visual) +{ + GF_Err e; + GF_TextureHandler *txh; + GF_VideoSurface video_src; + + while (1) { + GF_OverlayStack *ol = visual->overlays; + if (!ol) return; + visual->overlays = ol->next; + + txh = ol->ctx->aspect.fill_texture; + video_src.height = txh->height; + video_src.width = txh->width; + video_src.pitch = txh->stride; + video_src.pixel_format = txh->pixelformat; + video_src.video_buffer = txh->data; + + e = visual->compositor->video_out->Blit(visual->compositor->video_out, &video_src, &ol->src, &ol->dst, 2); + if (e) GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Visual2D] Error %s during overlay update\n", gf_error_to_string(e) )); + + ra_del(&ol->ra); + free(ol); + } +} + diff --git a/src/compositor/compositor_3d.c b/src/compositor/compositor_3d.c new file mode 100644 index 0000000..7fe828a --- /dev/null +++ b/src/compositor/compositor_3d.c @@ -0,0 +1,256 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "visual_manager.h" +#include "texturing.h" +#include "nodes_stacks.h" +#include + +#ifndef GPAC_DISABLE_3D + +#ifdef GPAC_USE_TINYGL +#include "../../TinyGL/include/GL/oscontext.h" +//#include +#endif + +GF_Err compositor_3d_set_aspect_ratio(GF_Compositor *compositor) +{ + GF_Event evt; + Double ratio; + Fixed scaleX, scaleY; + + if (!compositor->display_height || !compositor->display_width) return GF_OK; + + compositor->visual->camera.flags |= CAM_IS_DIRTY; + + compositor->output_width = compositor->vp_width = compositor->display_width; + compositor->output_height = compositor->vp_height = compositor->display_height; + compositor->vp_x = 0; + compositor->vp_y = 0; + + scaleX = scaleY = FIX_ONE; + if (!compositor->has_size_info) { + compositor->visual->width = compositor->vp_width; + compositor->visual->height = compositor->vp_height; + } else { + + switch (compositor->aspect_ratio) { + case GF_ASPECT_RATIO_FILL_SCREEN: + break; + case GF_ASPECT_RATIO_16_9: + compositor->vp_height = 9 * compositor->vp_width / 16; + break; + case GF_ASPECT_RATIO_4_3: + compositor->vp_height = 3 * compositor->vp_width / 4; + break; + default: + ratio = compositor->scene_height; + ratio /= compositor->scene_width; + if (compositor->vp_width * ratio > compositor->vp_height) { + compositor->vp_width = compositor->vp_height * compositor->scene_width; + compositor->vp_width /= compositor->scene_height; + } + else { + compositor->vp_height = compositor->vp_width * compositor->scene_height; + compositor->vp_height /= compositor->scene_width; + } + break; + } + compositor->vp_x = (compositor->display_width - compositor->vp_width) / 2; + compositor->vp_y = (compositor->display_height - compositor->vp_height) / 2; + /*update size info for main visual*/ + if (compositor->visual) { + compositor->visual->width = compositor->scene_width; + compositor->visual->height = compositor->scene_height; + } + /*scaling is still needed for bitmap*/ + scaleX = gf_divfix(INT2FIX(compositor->vp_width), INT2FIX(compositor->scene_width)); + scaleY = gf_divfix(INT2FIX(compositor->vp_height), INT2FIX(compositor->scene_height)); + } + + if (compositor->has_size_info) { + compositor->traverse_state->vp_size.x = INT2FIX(compositor->scene_width); + compositor->traverse_state->vp_size.y = INT2FIX(compositor->scene_height); + } else { + compositor->traverse_state->vp_size.x = INT2FIX(compositor->output_width); + compositor->traverse_state->vp_size.y = INT2FIX(compositor->output_height); + } + compositor_set_ar_scale(compositor, scaleX, scaleY); + + + /*and resetup OpenGL*/ + evt.type = GF_EVENT_VIDEO_SETUP; + evt.setup.width = compositor->display_width; + evt.setup.height = compositor->display_height; + evt.setup.back_buffer = 1; +#ifdef GPAC_USE_TINYGL + evt.setup.opengl_mode = 0; +#else + evt.setup.opengl_mode = 1; +#endif + compositor->video_out->ProcessEvent(compositor->video_out, &evt); + +#ifdef GPAC_USE_TINYGL + { + u32 bpp; + GF_VideoSurface bb; + GF_Err e = compositor->video_out->LockBackBuffer(compositor->video_out, &bb, 1); + if (e) return e; + switch (bb.pixel_format) { + case GF_PIXEL_RGB_32: + case GF_PIXEL_ARGB: + bpp = 32; + break; + case GF_PIXEL_RGB_24: + case GF_PIXEL_BGR_24: + bpp = 24; + break; + case GF_PIXEL_RGB_565: + case GF_PIXEL_RGB_555: + bpp = 16; + break; + default: + e = GF_NOT_SUPPORTED; + bpp = 0; + break; + } + compositor->tgl_ctx = ostgl_create_context(bb.width, bb.height, bpp, &bb.video_buffer, 1); + if (compositor->tgl_ctx) ostgl_make_current(compositor->tgl_ctx, 0); + compositor->video_out->LockBackBuffer(compositor->video_out, &bb, 0); + } +#endif + + compositor->reset_graphics=0; + return GF_OK; +} + + +GF_Camera *compositor_3d_get_camera(GF_Compositor *compositor) +{ + if (compositor->active_layer) { + return compositor_layer3d_get_camera(compositor->active_layer); + } else { + return &compositor->visual->camera; + } +} + +void compositor_3d_reset_camera(GF_Compositor *compositor) +{ + GF_Camera *cam = compositor_3d_get_camera(compositor); + camera_reset_viewpoint(cam, 1); + gf_sc_invalidate(compositor, NULL); +} + +void compositor_3d_draw_bitmap(Drawable *stack, DrawAspect2D *asp, GF_TraverseState *tr_state, Fixed width, Fixed height, Fixed bmp_scale_x, Fixed bmp_scale_y) +{ + u8 alpha; +#if !defined(GPAC_USE_OGL_ES) && !defined(GPAC_USE_TINYGL) + Fixed x, y; + Fixed sx, sy; + char *data; + u32 format; +#endif + GF_TextureHandler *txh; + GF_Compositor *compositor = tr_state->visual->compositor; + + + if (!asp->fill_texture) return; + txh = asp->fill_texture; + if (!txh || !txh->tx_io || !txh->width || !txh->height) return; + + alpha = GF_COL_A(asp->fill_color); + /*texture is available in hw, use it - if blending, force using texture*/ + if (!gf_sc_texture_needs_reload(txh) || (alpha != 0xFF) || !compositor->bitmap_use_pixels) { + visual_3d_set_state(tr_state->visual, V3D_STATE_LIGHT, 0); + visual_3d_enable_antialias(tr_state->visual, 0); + if (alpha && (alpha != 0xFF)) { + visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color); + gf_sc_texture_set_blend_mode(txh, TX_MODULATE); + } else if (gf_sc_texture_is_transparent(txh)) { + gf_sc_texture_set_blend_mode(txh, TX_REPLACE); + } else { + visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 0); + } + /*ignore texture transform for bitmap*/ + tr_state->mesh_num_textures = gf_sc_texture_enable(txh, NULL); + if (tr_state->mesh_num_textures) { + /*we must check the w & h passed are correct because of bitmap node initialization*/ + if (width && height) { + if (!stack->mesh) { + SFVec2f size; + size.x = width; + size.y = height; + + stack->mesh = new_mesh(); + mesh_new_rectangle(stack->mesh, size); + } + } + if (stack->mesh) { + visual_3d_mesh_paint(tr_state, stack->mesh); + } + gf_sc_texture_disable(txh); + tr_state->mesh_num_textures = 0; + return; + } + } + + /*otherwise use glDrawPixels*/ +#if !defined(GPAC_USE_OGL_ES) && !defined(GPAC_USE_TINYGL) + data = gf_sc_texture_get_data(txh, &format); + if (!data) return; + + x = INT2FIX(txh->width) / -2; + y = INT2FIX(txh->height) / 2; + + { + Fixed g[16]; + + sx = bmp_scale_x; if (sx<0) sx = FIX_ONE; + sy = bmp_scale_y; if (sy<0) sy = FIX_ONE; + compositor_adjust_scale(txh->owner, &sx, &sy); + + /*add top level scale if any*/ + sx = gf_mulfix(sx, compositor->scale_x); + sy = gf_mulfix(sy, compositor->scale_y); + + /*get & apply current transform scale*/ + visual_3d_matrix_get(tr_state->visual, V3D_MATRIX_MODELVIEW, g); + + if (g[0]<0) g[0] *= -FIX_ONE; + if (g[5]<0) g[5] *= -FIX_ONE; + sx = gf_mulfix(sx, g[0]); + sy = gf_mulfix(sy, g[5]); + x = gf_mulfix(x, sx); + y = gf_mulfix(y, sy); + + } + visual_3d_draw_image(tr_state->visual, x, y, txh->width, txh->height, format, data, sx, sy); +#endif +} + + +#endif + diff --git a/src/compositor/compositor_node_init.c b/src/compositor/compositor_node_init.c new file mode 100644 index 0000000..f9a3f69 --- /dev/null +++ b/src/compositor/compositor_node_init.c @@ -0,0 +1,228 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" + +#include +#include + +void gf_sc_on_node_init(GF_Compositor *compositor, GF_Node *node) +{ + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_AnimationStream: compositor_init_animationstream(compositor, node); break; + case TAG_MPEG4_AudioBuffer: compositor_init_audiobuffer(compositor, node); break; + case TAG_MPEG4_AudioSource: compositor_init_audiosource(compositor, node); break; + case TAG_MPEG4_AudioClip: case TAG_X3D_AudioClip: compositor_init_audioclip(compositor, node); break; + case TAG_MPEG4_TimeSensor: case TAG_X3D_TimeSensor: compositor_init_timesensor(compositor, node); break; + case TAG_MPEG4_ImageTexture: case TAG_X3D_ImageTexture: compositor_init_imagetexture(compositor, node); break; + case TAG_MPEG4_PixelTexture: case TAG_X3D_PixelTexture: compositor_init_pixeltexture(compositor, node); break; + case TAG_MPEG4_MovieTexture: case TAG_X3D_MovieTexture: compositor_init_movietexture(compositor, node); break; + + case TAG_MPEG4_Background2D: compositor_init_background2d(compositor, node); break; + case TAG_MPEG4_Bitmap: compositor_init_bitmap(compositor, node); break; + case TAG_MPEG4_ColorTransform: compositor_init_colortransform(compositor, node); break; + case TAG_MPEG4_Circle: compositor_init_circle(compositor, node); break; + case TAG_MPEG4_Curve2D: compositor_init_curve2d(compositor, node); break; + case TAG_MPEG4_XCurve2D: compositor_init_curve2d(compositor, node); break; + case TAG_MPEG4_Ellipse: compositor_init_ellipse(compositor, node); break; + case TAG_MPEG4_OrderedGroup: compositor_init_orderedgroup(compositor, node); break; + case TAG_MPEG4_PointSet2D: compositor_init_pointset2d(compositor, node); break; + case TAG_MPEG4_Transform2D: compositor_init_transform2d(compositor, node); break; + case TAG_MPEG4_TransformMatrix2D: compositor_init_transformmatrix2d(compositor, node); break; + case TAG_MPEG4_LineProperties: compositor_init_lineprops(compositor, node); break; + case TAG_MPEG4_XLineProperties: compositor_init_lineprops(compositor, node); break; + case TAG_MPEG4_Viewport: compositor_init_viewport(compositor, node); break; + + case TAG_MPEG4_IndexedLineSet2D: compositor_init_indexed_line_set2d(compositor, node); break; + case TAG_MPEG4_IndexedFaceSet2D: compositor_init_indexed_face_set2d(compositor, node); break; + + case TAG_MPEG4_Sound2D: compositor_init_sound2d(compositor, node); break; + + case TAG_MPEG4_LinearGradient: compositor_init_linear_gradient(compositor, node); break; + case TAG_MPEG4_RadialGradient: compositor_init_radial_gradient(compositor, node); break; + + case TAG_MPEG4_CompositeTexture2D: compositor_init_compositetexture2d(compositor, node); break; + case TAG_MPEG4_MatteTexture: compositor_init_mattetexture(compositor, node); break; + + case TAG_MPEG4_Form: compositor_init_form(compositor, node); break; + case TAG_MPEG4_Layer2D: compositor_init_layer2d(compositor, node); break; + case TAG_MPEG4_Layout: compositor_init_layout(compositor, node); break; + case TAG_MPEG4_PathLayout: compositor_init_path_layout(compositor, node); break; + + + /*sensors*/ + case TAG_MPEG4_Anchor: case TAG_X3D_Anchor: compositor_init_anchor(compositor, node); break; + case TAG_MPEG4_DiscSensor: compositor_init_disc_sensor(compositor, node); break; + case TAG_MPEG4_PlaneSensor2D: compositor_init_plane_sensor2d(compositor, node); break; + case TAG_MPEG4_ProximitySensor2D: compositor_init_proximity_sensor2d(compositor, node); break; + case TAG_MPEG4_TouchSensor: case TAG_X3D_TouchSensor: compositor_init_touch_sensor(compositor, node); break; + + case TAG_MPEG4_Group: case TAG_X3D_Group: compositor_init_group(compositor, node); break; + case TAG_MPEG4_Rectangle: case TAG_X3D_Rectangle2D: compositor_init_rectangle(compositor, node); break; + case TAG_MPEG4_Shape: case TAG_X3D_Shape: compositor_init_shape(compositor, node); break; + case TAG_MPEG4_Switch: case TAG_X3D_Switch: compositor_init_switch(compositor, node); break; + + case TAG_MPEG4_Text: case TAG_X3D_Text: compositor_init_text(compositor, node); break; + +#ifndef GPAC_DISABLE_3D + case TAG_MPEG4_Background: case TAG_X3D_Background: compositor_init_background(compositor, node); break; + + case TAG_MPEG4_CylinderSensor: case TAG_X3D_CylinderSensor: compositor_init_cylinder_sensor(compositor, node); break; + case TAG_MPEG4_PlaneSensor: case TAG_X3D_PlaneSensor: compositor_init_plane_sensor(compositor, node); break; + case TAG_MPEG4_ProximitySensor: case TAG_X3D_ProximitySensor: compositor_init_proximity_sensor(compositor, node); break; + case TAG_MPEG4_SphereSensor: case TAG_X3D_SphereSensor: compositor_init_sphere_sensor(compositor, node); break; + case TAG_MPEG4_VisibilitySensor: case TAG_X3D_VisibilitySensor: compositor_init_visibility_sensor(compositor, node); break; + + case TAG_MPEG4_Box: case TAG_X3D_Box: compositor_init_box(compositor, node); break; + case TAG_MPEG4_Cone: case TAG_X3D_Cone: compositor_init_cone(compositor, node); break; + case TAG_MPEG4_Cylinder: case TAG_X3D_Cylinder: compositor_init_cylinder(compositor, node); break; + case TAG_MPEG4_ElevationGrid: case TAG_X3D_ElevationGrid: compositor_init_elevation_grid(compositor, node); break; + case TAG_MPEG4_Extrusion: case TAG_X3D_Extrusion: compositor_init_extrusion(compositor, node); break; + case TAG_MPEG4_IndexedFaceSet: case TAG_X3D_IndexedFaceSet: compositor_init_ifs(compositor, node); break; + case TAG_MPEG4_IndexedLineSet: case TAG_X3D_IndexedLineSet: compositor_init_ils(compositor, node); break; + case TAG_MPEG4_PointSet: case TAG_X3D_PointSet: compositor_init_point_set(compositor, node); break; + case TAG_MPEG4_Sphere: case TAG_X3D_Sphere: compositor_init_sphere(compositor, node); break; + + case TAG_MPEG4_Billboard: case TAG_X3D_Billboard: compositor_init_billboard(compositor, node); break; + case TAG_MPEG4_Collision: case TAG_X3D_Collision: compositor_init_collision(compositor, node); break; + case TAG_MPEG4_LOD: case TAG_X3D_LOD: compositor_init_lod(compositor, node); break; + case TAG_MPEG4_Transform: case TAG_X3D_Transform: compositor_init_transform(compositor, node); break; + + case TAG_MPEG4_Sound: case TAG_X3D_Sound: compositor_init_sound(compositor, node); break; + + case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: compositor_init_viewpoint(compositor, node); break; + case TAG_MPEG4_NavigationInfo: case TAG_X3D_NavigationInfo: compositor_init_navigation_info(compositor, node); break; + case TAG_MPEG4_Fog: case TAG_X3D_Fog: compositor_init_fog(compositor, node); break; + + case TAG_MPEG4_DirectionalLight: case TAG_X3D_DirectionalLight: compositor_init_directional_light(compositor, node); break; + case TAG_MPEG4_PointLight: case TAG_X3D_PointLight: compositor_init_point_light(compositor, node); break; + case TAG_MPEG4_SpotLight: case TAG_X3D_SpotLight: compositor_init_spot_light(compositor, node); break; + + case TAG_MPEG4_NonLinearDeformer: compositor_init_non_linear_deformer(compositor, node); break; + + case TAG_MPEG4_Layer3D: compositor_init_layer3d(compositor, node); break; + case TAG_MPEG4_CompositeTexture3D: compositor_init_compositetexture3d(compositor, node); break; +#endif + + + /*X3D nodes*/ + case TAG_X3D_StaticGroup: compositor_init_static_group(compositor, node); break; + case TAG_X3D_Disk2D: compositor_init_disk2d(compositor, node); break; + case TAG_X3D_Arc2D: case TAG_X3D_ArcClose2D: compositor_init_arc2d(compositor, node); break; + case TAG_X3D_Polyline2D: compositor_init_polyline2d(compositor, node); break; + case TAG_X3D_TriangleSet2D: compositor_init_triangle_set2d(compositor, node); break; + +#ifndef GPAC_DISABLE_3D + case TAG_X3D_Polypoint2D: compositor_init_polypoint2d(compositor, node); break; + case TAG_X3D_LineSet: compositor_init_lineset(compositor, node); break; + case TAG_X3D_TriangleSet: compositor_init_triangle_set(compositor, node); break; + case TAG_X3D_TriangleStripSet: compositor_init_triangle_strip_set(compositor, node); break; + case TAG_X3D_TriangleFanSet: compositor_init_triangle_fan_set(compositor, node); break; + case TAG_X3D_IndexedTriangleFanSet: compositor_init_indexed_triangle_fan_set(compositor, node); break; + case TAG_X3D_IndexedTriangleStripSet: compositor_init_indexed_triangle_strip_set(compositor, node); break; + case TAG_X3D_IndexedTriangleSet: compositor_init_indexed_triangle_set(compositor, node); break; +#endif + + +#ifndef GPAC_DISABLE_SVG + /* SVG Part */ + case TAG_SVG_svg: compositor_init_svg_svg(compositor, node); break; + case TAG_SVG_g: compositor_init_svg_g(compositor, node); break; + case TAG_SVG_switch: compositor_init_svg_switch(compositor, node); break; + case TAG_SVG_rect: compositor_init_svg_rect(compositor, node); break; + case TAG_SVG_path: compositor_init_svg_path(compositor, node); break; + case TAG_SVG_circle: compositor_init_svg_circle(compositor, node); break; + case TAG_SVG_ellipse: compositor_init_svg_ellipse(compositor, node); break; + case TAG_SVG_line: compositor_init_svg_line(compositor, node); break; + case TAG_SVG_polyline: compositor_init_svg_polyline(compositor, node); break; + case TAG_SVG_polygon: compositor_init_svg_polygon(compositor, node); break; + case TAG_SVG_a: compositor_init_svg_a(compositor, node); break; + + case TAG_SVG_linearGradient: compositor_init_svg_linearGradient(compositor, node); break; + case TAG_SVG_radialGradient: compositor_init_svg_radialGradient(compositor, node); break; + case TAG_SVG_solidColor: compositor_init_svg_solidColor(compositor, node); break; + case TAG_SVG_stop: compositor_init_svg_stop(compositor, node); break; + + case TAG_SVG_text: compositor_init_svg_text(compositor, node); break; + case TAG_SVG_tspan: compositor_init_svg_tspan(compositor, node); break; + case TAG_SVG_textArea: compositor_init_svg_textarea(compositor, node); break; + case TAG_SVG_tbreak: compositor_init_svg_tbreak(compositor, node); break; + + case TAG_SVG_image: compositor_init_svg_image(compositor, node); break; + case TAG_SVG_video: compositor_init_svg_video(compositor, node); break; + case TAG_SVG_audio: compositor_init_svg_audio(compositor, node, 0); break; + + /*SVG font support - note that we initialize the font when parsing the font-face element, not the font element*/ + case TAG_SVG_font_face: compositor_init_svg_font(compositor, node); break; + case TAG_SVG_missing_glyph: + case TAG_SVG_glyph: + compositor_init_svg_glyph(compositor, node); + break; + case TAG_SVG_font_face_uri: + compositor_init_svg_font_face_uri(compositor, node); break; + + case TAG_SVG_use: compositor_init_svg_use(compositor, node); break; + case TAG_SVG_animation: compositor_init_svg_animation(compositor, node); break; + case TAG_SVG_foreignObject: compositor_init_svg_foreign_object(compositor, node); break; +#endif + + case TAG_ProtoNode: compositor_init_hardcoded_proto(compositor, node); break; + + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] node %s will not be rendered\n", gf_node_get_class_name(node))); + break; + } +} + +GF_EXPORT +void gf_sc_invalidate(GF_Compositor *compositor, GF_Node *byObj) +{ + + if (!byObj) { + compositor->draw_next_frame = 1; + return; + } + switch (gf_node_get_tag(byObj)) { + case TAG_MPEG4_AnimationStream: compositor_animationstream_modified(byObj); break; + case TAG_MPEG4_AudioBuffer: compositor_audiobuffer_modified(byObj); break; + case TAG_MPEG4_AudioSource: compositor_audiosource_modified(byObj); break; + case TAG_MPEG4_AudioClip: case TAG_X3D_AudioClip: compositor_audioclip_modified(byObj); break; + case TAG_MPEG4_TimeSensor: case TAG_X3D_TimeSensor: compositor_timesensor_modified(byObj); break; + case TAG_MPEG4_ImageTexture: case TAG_X3D_ImageTexture: compositor_imagetexture_modified(byObj); break; + case TAG_MPEG4_MovieTexture: case TAG_X3D_MovieTexture: compositor_movietexture_modified(byObj); break; + + case TAG_MPEG4_Background2D: compositor_background2d_modified(byObj); break; +#ifndef GPAC_DISABLE_3D + case TAG_MPEG4_Background: case TAG_X3D_Background: compositor_background_modified(byObj); break; +#endif + case TAG_MPEG4_Layout: compositor_layout_modified(compositor, byObj); break; + + default: + /*for all nodes, invalidate parent graph - note we do that for sensors as well to force recomputing + sensor list cached at grouping node level*/ + gf_node_dirty_set(byObj, 0, 1); + compositor->draw_next_frame = 1; + break; + } +} diff --git a/src/compositor/drawable.c b/src/compositor/drawable.c new file mode 100644 index 0000000..4d60815 --- /dev/null +++ b/src/compositor/drawable.c @@ -0,0 +1,1384 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "drawable.h" +#include "visual_manager.h" +#include "nodes_stacks.h" + + + +/*default draw routine*/ +void drawable_draw(Drawable *drawable, GF_TraverseState *tr_state) +{ + visual_2d_texture_path(tr_state->visual, tr_state->ctx->drawable->path, tr_state->ctx, tr_state); + visual_2d_draw_path(tr_state->visual, tr_state->ctx->drawable->path, tr_state->ctx, NULL, NULL, tr_state); +} + +/*default point_over routine*/ +void drawable_pick(Drawable *drawable, GF_TraverseState *tr_state) +{ + DrawAspect2D asp; + GF_Matrix2D inv_2d; + StrikeInfo2D *si; + Fixed x, y; + u32 i, count; + GF_Compositor *compositor = tr_state->visual->compositor; + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + visual_3d_drawable_pick(drawable->node, tr_state, NULL, drawable->path); + return; + } +#endif + gf_mx2d_copy(inv_2d, tr_state->transform); + gf_mx2d_inverse(&inv_2d); + x = tr_state->ray.orig.x; + y = tr_state->ray.orig.y; + gf_mx2d_apply_coords(&inv_2d, &x, &y); + + memset(&asp, 0, sizeof(DrawAspect2D)); + drawable_get_aspect_2d_mpeg4(drawable->node, &asp, tr_state); + + /*MPEG-4 picking is always on regardless of color properties*/ + if (/*tr_state->ctx->aspect.fill_texture */ + /* (tr_state->pick_typepath, x, y)) { + goto picked; + } + } + + if (asp.pen_props.width || asp.line_texture ) { + si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, &asp, tr_state->appear, NULL, 0, NULL); + if (si && si->outline && gf_path_point_over(si->outline, x, y)) { + goto picked; + } + } + return; + +picked: + compositor->hit_local_point.x = x; + compositor->hit_local_point.y = y; + compositor->hit_local_point.z = 0; + + gf_mx_from_mx2d(&compositor->hit_world_to_local, &tr_state->transform); + gf_mx_from_mx2d(&compositor->hit_local_to_world, &inv_2d); + + gf_list_reset(compositor->hit_use_stack); + compositor->hit_node = drawable->node; + compositor->hit_use_dom_events = 0; + compositor->hit_normal.x = compositor->hit_normal.y = 0; compositor->hit_normal.z = FIX_ONE; + compositor->hit_texcoords.x = gf_divfix(x - drawable->path->bbox.x, drawable->path->bbox.width); + compositor->hit_texcoords.y = FIX_ONE - gf_divfix(drawable->path->bbox.y - y, drawable->path->bbox.height); + + if (compositor_is_composite_texture(tr_state->appear)) { + compositor->hit_appear = tr_state->appear; + } else { + compositor->hit_appear = NULL; + } + + gf_list_reset(tr_state->visual->compositor->sensors); + count = gf_list_count(tr_state->vrml_sensors); + for (i=0; ivisual->compositor->sensors, gf_list_get(tr_state->vrml_sensors, i)); + } +} + +Drawable *drawable_new() +{ + Drawable *tmp; + GF_SAFEALLOC(tmp, Drawable) + tmp->path = gf_path_new(); + /*allocate a default visual container*/ + GF_SAFEALLOC(tmp->dri, DRInfo); + /*allocate a default bounds container*/ + GF_SAFEALLOC(tmp->dri->current_bounds, BoundInfo); + return tmp; +} + +void drawable_reset_bounds(Drawable *dr, GF_VisualManager *visual) +{ + DRInfo *dri; + BoundInfo *bi, *_cur; + + dri = dr->dri; + while (dri) { + if (dri->visual != visual) { + dri = dri->next; + continue; + } + /*destroy previous bounds only, since current ones are always used during traversing*/ + bi = dri->previous_bounds; + while (bi) { + _cur = bi; + bi = bi->next; + free(_cur); + } + dri->previous_bounds = NULL; + return; + } +} + +void drawable_del_ex(Drawable *dr, GF_Compositor *compositor) +{ + StrikeInfo2D *si; + Bool is_reg = 0; + DRInfo *dri, *cur; + BoundInfo *bi, *_cur; + + /*remove node from all visuals it's on*/ + dri = dr->dri; + while (dri) { + is_reg = compositor ? gf_sc_visual_is_registered(compositor, dri->visual) : 0; + + bi = dri->current_bounds; + while (bi) { + _cur = bi; + if (is_reg) ra_add(&dri->visual->to_redraw, &bi->clip); + bi = bi->next; + free(_cur); + } + bi = dri->previous_bounds; + while (bi) { + _cur = bi; + if (is_reg) ra_add(&dri->visual->to_redraw, &bi->clip); + bi = bi->next; + free(_cur); + } + if (is_reg) visual_2d_drawable_delete(dri->visual, dr); + cur = dri; + dri = dri->next; + free(cur); + } + if (compositor) compositor->draw_next_frame = 1; + + /*remove path object*/ + if (dr->path) gf_path_del(dr->path); + +#ifndef GPAC_DISABLE_3D + if (dr->mesh) mesh_free(dr->mesh); +#endif + + si = dr->outline; + while (si) { + StrikeInfo2D *next = si->next; + /*remove from main strike list*/ + if (compositor) gf_list_del_item(compositor->strike_bank, si); + delete_strikeinfo2d(si); + si = next; + } + free(dr); +} + +void drawable_del(Drawable *dr) +{ + GF_Compositor *compositor = gf_sc_get_compositor(dr->node); + drawable_del_ex(dr, compositor); +} +void drawable_node_del(GF_Node *node) +{ + drawable_del( (Drawable *)gf_node_get_private(node) ); +} + +Drawable *drawable_stack_new(GF_Compositor *compositor, GF_Node *node) +{ + Drawable *stack = drawable_new(); + stack->node = node; + gf_node_set_private(node, stack); + return stack; +} + +static BoundInfo *drawable_check_alloc_bounds(struct _drawable_context *ctx, GF_VisualManager *visual) +{ + DRInfo *dri, *prev; + BoundInfo *bi, *_prev; + + /*get bounds info for this visual manager*/ + prev = NULL; + dri = ctx->drawable->dri; + while (dri) { + if (dri->visual == visual) break; + if (!dri->visual) { + dri->visual = visual; + break; + } + prev = dri; + dri = dri->next; + } + if (!dri) { + GF_SAFEALLOC(dri, DRInfo); + dri->visual = visual; + if (prev) prev->next = dri; + else ctx->drawable->dri = dri; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Allocating new bound info storage on visual %08x for drawable %s\n", visual, gf_node_get_class_name(ctx->drawable->node))); + } + + /*get available bound info slot*/ + _prev = NULL; + bi = dri->current_bounds; + while (bi) { + if (!bi->clip.width) break; + _prev = bi; + bi = bi->next; + } + if (!bi) { + GF_SAFEALLOC(bi, BoundInfo); + if (_prev) { +// assert(!_prev->next); + _prev->next = bi; + } + else { +// assert(!dri->current_bounds); + dri->current_bounds = bi; + } + //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Allocating new bound info for drawable %s\n", gf_node_get_class_name(ctx->drawable->node))); + } + /*reset next bound info*/ + if (bi->next) bi->next->clip.width = 0; + return bi; +} + +void drawable_reset_group_highlight(GF_TraverseState *tr_state, GF_Node *n) +{ + Drawable *hlight = tr_state->visual->compositor->focus_highlight; + if (hlight && (n == gf_node_get_private(hlight->node))) gf_node_set_private(hlight->node, NULL); +} + +void drawable_mark_modified(Drawable *drawable, GF_TraverseState *tr_state) +{ + /*mark drawable as modified*/ + drawable->flags |= tr_state->visual->bounds_tracker_modif_flag; + /*and remove overlay flag*/ + drawable->flags &= ~DRAWABLE_IS_OVERLAY; + + drawable_reset_group_highlight(tr_state, drawable->node); +} + +/*move current bounds to previous bounds*/ +Bool drawable_flush_bounds(Drawable *drawable, GF_VisualManager *on_visual, u32 draw_mode) +{ + Bool was_drawn; + DRInfo *dri; + BoundInfo *tmp; + + /*reset node modified flag*/ + drawable->flags &= ~DRAWABLE_HAS_CHANGED; + if (drawable->flags & DRAWABLE_HAS_CHANGED_IN_LAST_TRAVERSE) { + drawable->flags |= DRAWABLE_HAS_CHANGED; + drawable->flags &= ~DRAWABLE_HAS_CHANGED_IN_LAST_TRAVERSE; + } + + dri = drawable->dri; + while (dri) { + if (dri->visual == on_visual) break; + dri = dri->next; + } + if (!dri) return 0; + + was_drawn = (dri->current_bounds && dri->current_bounds->clip.width) ? 1 : 0; + + if (draw_mode) { + /*permanent direct drawing mode, destroy previous bounds*/ + if (draw_mode==1) { + if (dri->previous_bounds) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Destroying previous bounds info for drawable %s\n", gf_node_get_class_name(drawable->node))); + while (dri->previous_bounds) { + BoundInfo *bi = dri->previous_bounds; + dri->previous_bounds = bi->next; + free(bi); + } + } + } + } + /*indirect drawing, flush bounds*/ + else { + tmp = dri->previous_bounds; + dri->previous_bounds = dri->current_bounds; + dri->current_bounds = tmp; + } + /*reset first allocated bound*/ + if (dri->current_bounds) dri->current_bounds->clip.width = 0; + + drawable->flags &= ~DRAWABLE_DRAWN_ON_VISUAL; + return was_drawn; +} + +/* + return 1 if same bound is found in previous list (and remove it from the list) + return 0 otherwise +*/ + +Bool drawable_has_same_bounds(struct _drawable_context *ctx, GF_VisualManager *visual) +{ + DRInfo *dri; + BoundInfo *bi; + + dri = ctx->drawable->dri; + while (dri) { + if (dri->visual == visual) break; + dri = dri->next; + } + if (!dri) return 0; + + bi = dri->previous_bounds; + while (bi) { + if ( + /*if 0, end of bounds used in the previous pass*/ + bi->clip.width + /*we need the same Appearance || parent */ + && (bi->extra_check == ctx->appear) + /*we need exact same cliper*/ + && (bi->clip.x==ctx->bi->clip.x) && (bi->clip.y==ctx->bi->clip.y) + && (bi->clip.width==ctx->bi->clip.width) && (bi->clip.height==ctx->bi->clip.height) + /*only check x and y (if w or h have changed, object has changed -> bounds info has been reset*/ + && (bi->unclip.x==ctx->bi->unclip.x) && (bi->unclip.y==ctx->bi->unclip.y) + ) { + /*remove*/ + bi->clip.width = 0; + return 1; + } + bi = bi->next; + } + return 0; +} + +/* + return any previous bounds related to the same visual in @rc if any + if nothing found return 0 +*/ +Bool drawable_get_previous_bound(Drawable *drawable, GF_IRect *rc, GF_VisualManager *visual) +{ + DRInfo *dri; + BoundInfo *bi; + + dri = drawable->dri; + while (dri) { + if (dri->visual == visual) break; + dri = dri->next; + } + if (!dri) return 0; + + bi = dri->previous_bounds; + while (bi) { + if (bi->clip.width) { + *rc = bi->clip; + bi->clip.width = 0; + return 1; + } + bi = bi->next; + } + return 0; +} + + + +DrawableContext *NewDrawableContext() +{ + DrawableContext *tmp; + GF_SAFEALLOC(tmp, DrawableContext); + return tmp; +} +void DeleteDrawableContext(DrawableContext *ctx) +{ + drawctx_reset(ctx); + free(ctx); +} +void drawctx_reset(DrawableContext *ctx) +{ + DrawableContext *next = ctx->next; + if (ctx->col_mat) free(ctx->col_mat); + memset(ctx, 0, sizeof(DrawableContext)); + ctx->next = next; + + /*by default all nodes are transparent*/ + ctx->flags |= CTX_IS_TRANSPARENT; + + /*BIFS has default value for 2D appearance ...*/ + ctx->aspect.fill_color = 0xFFCCCCCC; + ctx->aspect.line_color = 0xFFCCCCCC; + ctx->aspect.pen_props.width = FIX_ONE; + ctx->aspect.pen_props.cap = GF_LINE_CAP_FLAT; + ctx->aspect.pen_props.join = GF_LINE_JOIN_BEVEL; + ctx->aspect.pen_props.miterLimit = 4*FIX_ONE; + +} + +void drawctx_update_info(DrawableContext *ctx, GF_VisualManager *visual) +{ + DRInfo *dri; + Bool moved, need_redraw, drawn; + need_redraw = (ctx->flags & CTX_REDRAW_MASK) ? 1 : 0; + + drawn = 0; + dri = ctx->drawable->dri; + while (dri) { + if (dri->visual==visual) { + if (dri->current_bounds && dri->current_bounds->clip.width) drawn = 1; + break; + } + dri = dri->next; + } + if (drawn) { + ctx->drawable->flags |= DRAWABLE_DRAWN_ON_VISUAL; + /*node has been modified, do not check bounds, just assumed it moved*/ + if (ctx->drawable->flags & DRAWABLE_HAS_CHANGED) { + moved = 1; + } else { + /*check if same bounds are used*/ + moved = !drawable_has_same_bounds(ctx, visual); + } + + if (need_redraw || moved) ctx->flags |= CTX_REDRAW_MASK; + } + + /*in all cases reset dirty flag of appearance and its sub-nodes*/ + //if (ctx->flags & CTX_HAS_APPEARANCE) gf_node_dirty_reset(ctx->appear); +} + + +u32 drawable_get_aspect_2d_mpeg4(GF_Node *node, DrawAspect2D *asp, GF_TraverseState *tr_state) +{ + M_Material2D *m = NULL; + M_LineProperties *LP; + M_XLineProperties *XLP; + u32 ret = 0; + + asp->pen_props.cap = GF_LINE_CAP_FLAT; + asp->pen_props.join = GF_LINE_JOIN_MITER; + asp->pen_props.align = GF_PATH_LINE_CENTER; + asp->pen_props.miterLimit = 4*FIX_ONE; + asp->line_color = 0xFFCCCCCC; + asp->pen_props.width = 0; + + if (tr_state->appear == NULL) goto check_default; + + if ( ((M_Appearance *) tr_state->appear)->texture ) { + asp->fill_texture = gf_sc_texture_get_handler( ((M_Appearance *) tr_state->appear)->texture ); + } + + m = (M_Material2D *) ((M_Appearance *)tr_state->appear)->material; + if ( m == NULL) { + asp->fill_color &= 0x00FFFFFF; + goto check_default; + } + switch (gf_node_get_tag((GF_Node *) m) ) { + case TAG_MPEG4_Material2D: + break; + case TAG_MPEG4_Material: + case TAG_X3D_Material: + { + M_Material *mat = (M_Material *)m; + asp->pen_props.width = 0; + asp->fill_color = GF_COL_ARGB_FIXED(FIX_ONE, mat->diffuseColor.red, mat->diffuseColor.green, mat->diffuseColor.blue); + if (!tr_state->color_mat.identity) + asp->fill_color = gf_cmx_apply(&tr_state->color_mat, asp->fill_color); + } + return 0; + default: + return 0; + } + + asp->fill_color = GF_COL_ARGB_FIXED(FIX_ONE-m->transparency, m->emissiveColor.red, m->emissiveColor.green, m->emissiveColor.blue); + if (!tr_state->color_mat.identity) + asp->fill_color = gf_cmx_apply(&tr_state->color_mat, asp->fill_color); + + asp->line_color = asp->fill_color; + if (!m->filled) asp->fill_color = 0; + + if (m->lineProps == NULL) { +check_default: + /*this is a bug in the spec: by default line width is 1.0, but in meterMetrics this means half of the screen :)*/ + asp->pen_props.width = FIX_ONE; + if (!tr_state->pixel_metrics) asp->pen_props.width = gf_divfix(asp->pen_props.width, tr_state->min_hsize); + if (m && m->transparency==FIX_ONE) { + asp->pen_props.width = 0; + } else { + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_IndexedLineSet2D: + asp->fill_color &= 0x00FFFFFF; + break; + case TAG_MPEG4_PointSet2D: + asp->fill_color |= FIX2INT(255 * (m ? (FIX_ONE - m->transparency) : FIX_ONE)) << 24; + asp->pen_props.width = 0; + break; + default: + if (GF_COL_A(asp->fill_color)) asp->pen_props.width = 0; + /*spec is unclear about that*/ + //else if (!m && ctx->aspect.fill_texture) ctx->aspect.pen_props.width = 0; + break; + } + } + return 0; + } + LP = NULL; + XLP = NULL; + switch (gf_node_get_tag((GF_Node *) m->lineProps) ) { + case TAG_MPEG4_LineProperties: + LP = (M_LineProperties *) m->lineProps; + break; + case TAG_MPEG4_XLineProperties: + XLP = (M_XLineProperties *) m->lineProps; + break; + default: + asp->pen_props.width = 0; + return 0; + } + if (m->lineProps && gf_node_dirty_get(m->lineProps)) + ret = CTX_APP_DIRTY; + + if (LP) { + asp->pen_props.dash = (u8) LP->lineStyle; + asp->line_color = GF_COL_ARGB_FIXED(FIX_ONE-m->transparency, LP->lineColor.red, LP->lineColor.green, LP->lineColor.blue); + asp->pen_props.width = LP->width; + if (!tr_state->color_mat.identity) { + asp->line_color = gf_cmx_apply(&tr_state->color_mat, asp->line_color); + } + return ret; + } + + asp->pen_props.dash = (u8) XLP->lineStyle; + asp->line_color = GF_COL_ARGB_FIXED(FIX_ONE-XLP->transparency, XLP->lineColor.red, XLP->lineColor.green, XLP->lineColor.blue); + asp->pen_props.width = XLP->width; + if (!tr_state->color_mat.identity) { + asp->line_color = gf_cmx_apply(&tr_state->color_mat, asp->line_color); + } + + asp->line_scale = XLP->isScalable ? FIX_ONE : 0; + asp->pen_props.align = XLP->isCenterAligned ? GF_PATH_LINE_CENTER : GF_PATH_LINE_INSIDE; + asp->pen_props.cap = (u8) XLP->lineCap; + asp->pen_props.join = (u8) XLP->lineJoin; + asp->pen_props.miterLimit = XLP->miterLimit; + asp->pen_props.dash_offset = XLP->dashOffset; + + /*dash settings strutc is the same as MFFloat from XLP, typecast without storing*/ + if (XLP->dashes.count) { + asp->pen_props.dash_set = (GF_DashSettings *) &XLP->dashes; + } else { + asp->pen_props.dash_set = NULL; + } + asp->line_texture = gf_sc_texture_get_handler(XLP->texture); + return ret; +} + +static Bool check_transparent_skip(DrawableContext *ctx, Bool skipFill) +{ + /*if texture, cannot skip*/ + if (ctx->aspect.fill_texture) return 0; + if (! GF_COL_A(ctx->aspect.fill_color) && !GF_COL_A(ctx->aspect.line_color) ) return 1; + if (ctx->aspect.pen_props.width == 0) { + if (skipFill) return 1; + if (!GF_COL_A(ctx->aspect.fill_color) ) return 1; + } + return 0; +} + + +DrawableContext *drawable_init_context_mpeg4(Drawable *drawable, GF_TraverseState *tr_state) +{ + DrawableContext *ctx; + Bool skipFill; + assert(tr_state->visual); + + /*switched-off geometry nodes are not drawn*/ + if (tr_state->switched_off) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Drawable is switched off - skipping\n")); + return NULL; + } + + //Get a empty context from the current visual + ctx = visual_2d_get_drawable_context(tr_state->visual); + if (!ctx) return NULL; + + ctx->drawable = drawable; + + /*usually set by colorTransform or changes in OrderedGroup*/ + if (tr_state->invalidate_all) ctx->flags |= CTX_APP_DIRTY; + + ctx->aspect.fill_texture = NULL; + if (tr_state->appear) { + ctx->appear = tr_state->appear; + if (gf_node_dirty_get(tr_state->appear)) + ctx->flags |= CTX_APP_DIRTY; + } + /*todo cliper*/ + + /*FIXME - only needed for texture*/ + if (!tr_state->color_mat.identity) { + GF_SAFEALLOC(ctx->col_mat, GF_ColorMatrix); + gf_cmx_copy(ctx->col_mat, &tr_state->color_mat); + } + + /*IndexedLineSet2D and PointSet2D ignores fill flag and texturing*/ + skipFill = 0; + ctx->aspect.fill_texture = NULL; + switch (gf_node_get_tag(ctx->drawable->node) ) { + case TAG_MPEG4_IndexedLineSet2D: + skipFill = 1; + break; + default: + break; + } + + ctx->flags |= drawable_get_aspect_2d_mpeg4(drawable->node, &ctx->aspect, tr_state); + + /*Update texture info - draw even if texture not created (this may happen if the media is removed)*/ + if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->needs_refresh) ctx->flags |= CTX_TEXTURE_DIRTY; + + /*not clear in the spec: what happens when a transparent node is in form/layout ?? this may + completely break layout of children. We consider the node should be drawn*/ + if (!tr_state->parent && check_transparent_skip(ctx, skipFill)) { + visual_2d_remove_last_context(tr_state->visual); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Drawable is fully transparent - skipping\n")); + return NULL; + } + ctx->flags |= CTX_HAS_APPEARANCE; + + /*we are drawing on a fliped coord surface, remember to flip the texture*/ + if (tr_state->fliped_coords) + ctx->flags |= CTX_FLIPED_COORDS; + +#ifdef GPAC_TRISCOPE_MODE + ctx->depth = tr_state->depth; +#endif + return ctx; +} + +static Bool drawable_finalize_end(struct _drawable_context *ctx, GF_TraverseState *tr_state) +{ + /*if direct draw we can remove the context*/ + Bool res = tr_state->direct_draw; + /*copy final transform, once all parent layout is done*/ + gf_mx2d_copy(ctx->transform, tr_state->transform); + + /*setup clipper and register bounds & sensors*/ + gf_irect_intersect(&ctx->bi->clip, &tr_state->visual->top_clipper); + if (!ctx->bi->clip.width || !ctx->bi->clip.height) { + ctx->bi->clip.width = 0; + /*remove if this is the last context*/ + if (tr_state->visual->cur_context == ctx) tr_state->visual->cur_context->drawable = NULL; + + return res; + } + + /*keep track of node drawn, whether direct or indirect drawing*/ + if (!(ctx->drawable->flags & DRAWABLE_REGISTERED_WITH_VISUAL) ) { + struct _drawable_store *it; + GF_SAFEALLOC(it, struct _drawable_store); + it->drawable = ctx->drawable; + if (tr_state->visual->last_prev_entry) { + tr_state->visual->last_prev_entry->next = it; + tr_state->visual->last_prev_entry = it; + } else { + tr_state->visual->prev_nodes = tr_state->visual->last_prev_entry = it; + } + //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Registering new drawn node %s on visual\n", gf_node_get_class_name(it->drawable->node))); + ctx->drawable->flags |= DRAWABLE_REGISTERED_WITH_VISUAL; + } + + /*we are in direct draw mode, draw ...*/ + if (res) { + /*if over an overlay we cannot remove the context and cannot draw directly*/ + if (visual_2d_overlaps_overlay(tr_state->visual, ctx, tr_state)) + return 0; + + assert(!tr_state->traversing_mode); + tr_state->traversing_mode = TRAVERSE_DRAW_2D; + tr_state->ctx = ctx; + + if (ctx->drawable->flags & DRAWABLE_USE_TRAVERSE_DRAW) { + gf_node_allow_cyclic_traverse(ctx->drawable->node); + gf_node_traverse(ctx->drawable->node, tr_state); + } else { + drawable_draw(ctx->drawable, tr_state); + } + + tr_state->ctx = NULL; + tr_state->traversing_mode = TRAVERSE_SORT; + } + /*if the drawable is an overlay, always mark it as dirty to avoid flickering*/ + else if (ctx->drawable->flags & DRAWABLE_IS_OVERLAY) { + ctx->flags |= CTX_APP_DIRTY; + } + /*if direct draw we can remove the context*/ + return res; +} + +void drawable_check_bounds(struct _drawable_context *ctx, GF_VisualManager *visual) +{ + if (!ctx->bi) { + ctx->bi = drawable_check_alloc_bounds(ctx, visual); + assert(ctx->bi); + ctx->bi->extra_check = ctx->appear; + } +} + +void drawable_compute_line_scale(GF_TraverseState *tr_state, DrawAspect2D *asp) +{ + GF_Rect rc; + rc.x = rc.y = 0; + rc.width = rc.height = FIX_ONE; +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) + gf_mx_apply_rect(&tr_state->model_matrix, &rc); + else +#endif + gf_mx2d_apply_rect(&tr_state->transform, &rc); + + asp->line_scale = MAX(gf_divfix(tr_state->visual->compositor->scale_x, rc.width), gf_divfix(tr_state->visual->compositor->scale_y, rc.height)); +} + +void drawable_finalize_sort_ex(DrawableContext *ctx, GF_TraverseState *tr_state, GF_Rect *orig_bounds, Bool skip_focus) +{ + Bool can_remove; + Fixed pw; + GF_Rect unclip, store_orig_bounds; + + drawable_check_bounds(ctx, tr_state->visual); + + if (orig_bounds) { + store_orig_bounds = *orig_bounds; + } else { + gf_path_get_bounds(ctx->drawable->path, &store_orig_bounds); + } + ctx->bi->unclip = store_orig_bounds; + gf_mx2d_apply_rect(&tr_state->transform, &ctx->bi->unclip); + + /*apply pen width*/ + if (ctx->aspect.pen_props.width) { + StrikeInfo2D *si = NULL; + + if (!ctx->aspect.line_scale) + drawable_compute_line_scale(tr_state, &ctx->aspect); + +#if 0 + /*if pen is not scalable, apply user/viewport transform so that original aspect is kept*/ + if (!ctx->aspect.line_scale) { + GF_Point2D pt; + pt.x = tr_state->transform.m[0] + tr_state->transform.m[1]; + pt.y = tr_state->transform.m[3] + tr_state->transform.m[4]; + ctx->aspect.line_scale = gf_divfix(FLT2FIX(1.41421356f) , gf_v2d_len(&pt)); + } +#endif + + /*get strike info & outline for exact bounds compute. If failure use default offset*/ + si = drawable_get_strikeinfo(tr_state->visual->compositor, ctx->drawable, &ctx->aspect, tr_state->appear, ctx->drawable->path, ctx->flags, NULL); + if (si && si->outline) { + gf_path_get_bounds(si->outline, &ctx->bi->unclip); + gf_mx2d_apply_rect(&tr_state->transform, &ctx->bi->unclip); + } else { + pw = gf_mulfix(ctx->aspect.pen_props.width, ctx->aspect.line_scale); + ctx->bi->unclip.x -= pw/2; + ctx->bi->unclip.y += pw/2; + ctx->bi->unclip.width += pw; + ctx->bi->unclip.height += pw; + } + } + + if (ctx->bi->unclip.width && ctx->bi->unclip.height) { + unclip = ctx->bi->unclip; + if (! (ctx->flags & CTX_NO_ANTIALIAS)) { + /*grow of 2 pixels (-1 and +1) to handle AA, but ONLY on cliper otherwise we will modify layout/form */ + pw = (tr_state->pixel_metrics) ? FIX_ONE : 2*FIX_ONE/tr_state->visual->width; + unclip.x -= pw; + unclip.y += pw; + unclip.width += 2*pw; + unclip.height += 2*pw; + } + ctx->bi->clip = gf_rect_pixelize(&unclip); + } else { + ctx->bi->clip.width = 0; + } + + can_remove = drawable_finalize_end(ctx, tr_state); + if (ctx->drawable && !skip_focus) + drawable_check_focus_highlight(ctx->drawable->node, tr_state, &store_orig_bounds); + + /*remove if this is the last context*/ +// if (can_remove && (tr_state->visual->cur_context == ctx)) +// tr_state->visual->cur_context->drawable = NULL; +} + +void drawable_finalize_sort(struct _drawable_context *ctx, GF_TraverseState *tr_state, GF_Rect *orig_bounds) +{ + drawable_finalize_sort_ex(ctx, tr_state, orig_bounds, 0); +} + + +void drawable_check_focus_highlight(GF_Node *node, GF_TraverseState *tr_state, GF_Rect *orig_bounds) +{ + DrawableContext *hl_ctx; + Drawable *hlight; + GF_Node *prev_node; + u32 prev_mode; + GF_Rect *bounds; + GF_Matrix2D cur; + GF_Compositor *compositor = tr_state->visual->compositor; + + if (compositor->focus_node!=node) return; + if (compositor->focus_used) { + u32 count = gf_list_count(tr_state->use_stack); + if (!count || (gf_list_get(tr_state->use_stack, count-1)!=compositor->focus_used) ) + return; + } + + hlight = compositor->focus_highlight; + if (!hlight) return; + + /*check if focus node has changed*/ + prev_node = gf_node_get_private(hlight->node); + if (prev_node != node) { + /*this is a grouping node, get its bounds*/ + if (!orig_bounds) { + gf_mx2d_copy(cur, tr_state->transform); + gf_mx2d_init(tr_state->transform); + prev_mode = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + tr_state->bounds.width = tr_state->bounds.height = 0; + tr_state->bounds.x = tr_state->bounds.y = 0; + + gf_sc_get_nodes_bounds(node, ((GF_ParentNode *)node)->children, tr_state, NULL); + + tr_state->traversing_mode = prev_mode; + gf_mx2d_copy(tr_state->transform, cur); + bounds = &tr_state->bounds; + } else { + bounds = orig_bounds; + } + gf_node_set_private(hlight->node, node); + + drawable_reset_path(hlight); + gf_path_reset(hlight->path); + gf_path_add_rect(hlight->path, bounds->x, bounds->y, bounds->width, bounds->height); + } + hl_ctx = visual_2d_get_drawable_context(tr_state->visual); + hl_ctx->drawable = hlight; + hl_ctx->aspect.fill_color = compositor->highlight_fill; + hl_ctx->aspect.line_color = compositor->highlight_stroke; + hl_ctx->aspect.line_scale = 0; + hl_ctx->aspect.pen_props.width = FIX_ONE; + hl_ctx->aspect.pen_props.join = GF_LINE_JOIN_BEVEL; + hl_ctx->aspect.pen_props.dash = GF_DASH_STYLE_DOT; + + /*editing this node - move to solid stroke*/ + if (compositor->edited_text) { + hl_ctx->aspect.pen_props.width = 2*FIX_ONE; + hl_ctx->aspect.pen_props.dash = 1; + hl_ctx->aspect.line_color = compositor->highlight_stroke; + } + + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + gf_mx2d_copy(hl_ctx->transform, tr_state->transform); + visual_3d_draw_2d_with_aspect(hl_ctx->drawable, tr_state, &hl_ctx->aspect, 1); + return; + } +#endif + gf_mx2d_copy(hl_ctx->transform, tr_state->transform); + drawable_finalize_sort_ex(hl_ctx, tr_state, NULL, 1); +} + + +void drawable_traverse_focus(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + if (is_destroy) return; + if (tr_state->traversing_mode == TRAVERSE_DRAW_2D) + visual_2d_draw_path(tr_state->visual, tr_state->ctx->drawable->path, tr_state->ctx, NULL, NULL, tr_state); +} + + +void delete_strikeinfo2d(StrikeInfo2D *info) +{ + if (info->outline) gf_path_del(info->outline); +#ifndef GPAC_DISABLE_3D + if (info->mesh_outline) mesh_free(info->mesh_outline); +#endif + free(info); +} + +static u32 drawable_get_lineprops_last_update_time(GF_Node *node) +{ + LinePropStack *st = (LinePropStack *)gf_node_get_private(node); + if (!st) return 0; + if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { + st->last_mod_time ++; + gf_node_dirty_clear(node, 0); + } + return st->last_mod_time; +} + +StrikeInfo2D *drawable_get_strikeinfo(GF_Compositor *compositor, Drawable *drawable, DrawAspect2D *asp, GF_Node *appear, GF_Path *path, u32 svg_flags, GF_TraverseState *tr_state) +{ + StrikeInfo2D *si, *prev; + GF_Node *lp; + u32 now; + if (!asp->pen_props.width) return NULL; + if (path && !path->n_points) return NULL; + + lp = NULL; + if (appear && (gf_node_get_tag(appear) < GF_NODE_RANGE_LAST_X3D) ) { + lp = ((M_Appearance *)appear)->material; + if (lp) lp = ((M_Material2D *) lp)->lineProps; + } + + prev = NULL; + si = drawable->outline; + while (si) { + /*note this includes default LP (NULL)*/ + if ((si->lineProps == lp) && (!path || (path==si->original)) ) break; + if (!si->lineProps) { + gf_list_del_item(compositor->strike_bank, si); + if (si->outline) gf_path_del(si->outline); +#ifndef GPAC_DISABLE_3D + if (si->mesh_outline) mesh_free(si->mesh_outline); +#endif + if (prev) prev->next = si->next; + else drawable->outline = si->next; + free(si); + si = prev ? prev->next : drawable->outline; + continue; + } + prev = si; + si = si->next; + } + /*not found, add*/ + if (!si) { + GF_SAFEALLOC(si, StrikeInfo2D); + si->lineProps = lp; + si->drawable = drawable; + + if (drawable->outline) { + prev = drawable->outline; + while (prev->next) prev = prev->next; + prev->next = si; + } else { + drawable->outline = si; + } + gf_list_add(compositor->strike_bank, si); + } + + /*3D drawing of outlines*/ +#ifndef GPAC_DISABLE_3D + if (tr_state && !asp->line_scale) { + drawable_compute_line_scale(tr_state, asp); + } +#endif + + /*picking*/ + if (!asp->line_scale) return si; + + /*node changed or outline not build*/ + now = lp ? drawable_get_lineprops_last_update_time(lp) : si->last_update_time; + if (!si->outline || (now!=si->last_update_time) || (si->line_scale != asp->line_scale) || (si->path_length != asp->pen_props.path_length) || (svg_flags & CTX_SVG_OUTLINE_GEOMETRY_DIRTY)) { + u32 i; + Fixed w = asp->pen_props.width; + Fixed dash_o = asp->pen_props.dash_offset; + si->last_update_time = now; + si->line_scale = asp->line_scale; + if (si->outline) gf_path_del(si->outline); +#ifndef GPAC_DISABLE_3D + if (si->mesh_outline) { + mesh_free(si->mesh_outline); + si->mesh_outline = NULL; + } +#endif + /*apply scale whether scalable or not (if not scalable, scale is still needed for scalable zoom)*/ + asp->pen_props.width = gf_mulfix(asp->pen_props.width, asp->line_scale); + if (asp->pen_props.dash != GF_DASH_STYLE_SVG) + asp->pen_props.dash_offset = gf_mulfix(asp->pen_props.dash_offset, asp->pen_props.width); + + if (asp->pen_props.dash_set) { + for(i=0; ipen_props.dash_set->num_dash; i++) { + asp->pen_props.dash_set->dashes[i] = gf_mulfix(asp->pen_props.dash_set->dashes[i], asp->line_scale); + } + } + + if (path) { + si->outline = gf_path_get_outline(path, asp->pen_props); + si->original = path; + } else { + si->outline = gf_path_get_outline(drawable->path, asp->pen_props); + } + /*restore*/ + asp->pen_props.width = w; + asp->pen_props.dash_offset = dash_o; + if (asp->pen_props.dash_set) { + for(i=0; ipen_props.dash_set->num_dash; i++) { + asp->pen_props.dash_set->dashes[i] = gf_divfix(asp->pen_props.dash_set->dashes[i], asp->line_scale); + } + } + } + + return si; +} + +void drawable_reset_path_outline(Drawable *st) +{ + StrikeInfo2D *si = st->outline; + while (si) { + if (si->outline) gf_path_del(si->outline); + si->outline = NULL; +#ifndef GPAC_DISABLE_3D + if (si->mesh_outline) mesh_free(si->mesh_outline); + si->mesh_outline = NULL; +#endif + si->original = NULL; + si = si->next; + } +#ifndef GPAC_DISABLE_3D + if (st->mesh) { + mesh_free(st->mesh); + st->mesh = NULL; + } +#endif +} + +void drawable_reset_path(Drawable *st) +{ + drawable_reset_path_outline(st); + if (st->path) gf_path_reset(st->path); +#ifndef GPAC_DISABLE_3D + if (st->mesh) { + mesh_free(st->mesh); + st->mesh = NULL; + } +#endif +} + + +static void DestroyLineProps(GF_Node *n, void *rs, Bool is_destroy) +{ + StrikeInfo2D *si, *cur, *prev; + u32 i; + LinePropStack *st; + if (!is_destroy) return; + + st = (LinePropStack *)gf_node_get_private(n); + i = 0; + + while ((si = (StrikeInfo2D*)gf_list_enum(st->compositor->strike_bank, &i))) { + if (si->lineProps == n) { + /*remove from node*/ + if (si->drawable) { + assert(si->drawable->outline); + cur = si->drawable->outline; + prev = NULL; + while (cur) { + if (cur!=si) { + prev = cur; + cur = cur->next; + continue; + } + if (prev) prev->next = cur->next; + else si->drawable->outline = cur->next; + break; + } + } + i--; + gf_list_rem(st->compositor->strike_bank, i); + delete_strikeinfo2d(si); + } + } + + free(st); + +} + +void compositor_init_lineprops(GF_Compositor *compositor, GF_Node *node) +{ + LinePropStack *st = (LinePropStack *)malloc(sizeof(LinePropStack)); + st->compositor = compositor; + st->last_mod_time = 1; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroyLineProps); +} + +#ifdef GPAC_DISABLE_SVG + +Bool drawable_get_aspect_2d_svg(GF_Node *node, DrawAspect2D *asp, GF_TraverseState *tr_state) +{ + return 0; +} + +#else + +Bool drawable_get_aspect_2d_svg(GF_Node *node, DrawAspect2D *asp, GF_TraverseState *tr_state) +{ + Bool ret = 0; + SVGPropertiesPointers *props = tr_state->svg_props; + Fixed clamped_opacity = FIX_ONE; + Fixed clamped_solid_opacity = FIX_ONE; + Fixed clamped_fill_opacity = (props->fill_opacity->value < 0 ? 0 : (props->fill_opacity->value > FIX_ONE ? FIX_ONE : props->fill_opacity->value)); + Fixed clamped_stroke_opacity = (props->stroke_opacity->value < 0 ? 0 : (props->stroke_opacity->value > FIX_ONE ? FIX_ONE : props->stroke_opacity->value)); + + if (props->opacity) { + clamped_opacity = (props->opacity->value < 0 ? 0 : (props->opacity->value > FIX_ONE ? FIX_ONE : props->opacity->value)); + if (clamped_opacity!=FIX_ONE) { + clamped_fill_opacity = gf_mulfix(clamped_fill_opacity, clamped_opacity); + clamped_stroke_opacity = gf_mulfix(clamped_stroke_opacity, clamped_opacity); + } + } + asp->fill_color = 0; + + if (props->fill->type==SVG_PAINT_URI) { + if (props->fill->iri.type != XMLRI_ELEMENTID) { + /* trying to resolve the IRI to the Paint Server */ + XMLRI *iri = &props->fill->iri; + GF_SceneGraph *sg = gf_node_get_graph(node); + GF_Node *n = gf_sg_find_node_by_name(sg, &(iri->string[1])); + if (n) { + iri->type = XMLRI_ELEMENTID; + iri->target = n; + gf_node_register_iri(sg, iri); + free(iri->string); + iri->string = NULL; + } + } + /* If paint server not found, paint is equivalent to none */ + if (props->fill->iri.type == XMLRI_ELEMENTID) { + asp->fill_color = GF_COL_ARGB_FIXED(clamped_opacity, 0, 0, 0); + switch (gf_node_get_tag((GF_Node *)props->fill->iri.target)) { + case TAG_SVG_solidColor: + { + SVGAllAttributes all_atts; + gf_svg_flatten_attributes((SVG_Element*)props->fill->iri.target, &all_atts); + + gf_node_traverse(props->fill->iri.target, tr_state); + + if (gf_node_dirty_get(props->fill->iri.target)) + ret = 1; + + if (all_atts.solid_color) { + if (all_atts.solid_opacity) { + Fixed val = all_atts.solid_opacity->value; + clamped_solid_opacity = MIN(FIX_ONE, MAX(0, val) ); + clamped_solid_opacity = gf_mulfix(clamped_solid_opacity, clamped_opacity); + } + asp->fill_color = GF_COL_ARGB_FIXED(clamped_solid_opacity, all_atts.solid_color->color.red, all_atts.solid_color->color.green, all_atts.solid_color->color.blue); + } + } + break; + case TAG_SVG_linearGradient: + case TAG_SVG_radialGradient: + asp->fill_texture = gf_sc_texture_get_handler((GF_Node *)props->fill->iri.target); + break; + /*FIXME*/ + default: + break; + } + } + } else if (props->fill->type == SVG_PAINT_COLOR) { + if (props->fill->color.type == SVG_COLOR_CURRENTCOLOR) { + asp->fill_color = GF_COL_ARGB_FIXED(clamped_fill_opacity, props->color->color.red, props->color->color.green, props->color->color.blue); + } else if (props->fill->color.type == SVG_COLOR_RGBCOLOR) { + asp->fill_color = GF_COL_ARGB_FIXED(clamped_fill_opacity, props->fill->color.red, props->fill->color.green, props->fill->color.blue); + } else if (props->fill->color.type >= SVG_COLOR_ACTIVE_BORDER) { + asp->fill_color = tr_state->visual->compositor->sys_colors[props->fill->color.type - 3]; + asp->fill_color |= ((u32) (clamped_fill_opacity*255) ) << 24; + } + } + if (!tr_state->color_mat.identity) + asp->fill_color = gf_cmx_apply(&tr_state->color_mat, asp->fill_color); + + asp->line_color = 0; + asp->pen_props.width = (props->stroke->type != SVG_PAINT_NONE) ? props->stroke_width->value : 0; + if (props->stroke->type==SVG_PAINT_URI) { + if (props->stroke->iri.type != XMLRI_ELEMENTID) { + /* trying to resolve the IRI to the Paint Server */ + XMLRI *iri = &props->stroke->iri; + GF_SceneGraph *sg = gf_node_get_graph(node); + GF_Node *n = gf_sg_find_node_by_name(sg, &(iri->string[1])); + if (n) { + iri->type = XMLRI_ELEMENTID; + iri->target = n; + gf_node_register_iri(sg, iri); + free(iri->string); + iri->string = NULL; + } + } + /* Paint server not found, stroke is equivalent to none */ + if (props->stroke->iri.type == XMLRI_ELEMENTID) { + switch (gf_node_get_tag((GF_Node *)props->stroke->iri.target)) { + case TAG_SVG_solidColor: + { + SVGAllAttributes all_atts; + gf_svg_flatten_attributes((SVG_Element*)props->stroke->iri.target, &all_atts); + + gf_node_traverse(props->stroke->iri.target, tr_state); + + if (gf_node_dirty_get(props->stroke->iri.target)) + ret = 1; + + if (all_atts.solid_color) { + if (all_atts.solid_opacity) { + Fixed val = all_atts.solid_opacity->value; + clamped_solid_opacity = MIN(FIX_ONE, MAX(0, val) ); + } + asp->line_color = GF_COL_ARGB_FIXED(clamped_solid_opacity, all_atts.solid_color->color.red, all_atts.solid_color->color.green, all_atts.solid_color->color.blue); + } + } + break; + case TAG_SVG_linearGradient: + case TAG_SVG_radialGradient: + asp->line_texture = gf_sc_texture_get_handler((GF_Node *)props->stroke->iri.target); + break; + default: + break; + } + } + } else if (props->stroke->type == SVG_PAINT_COLOR) { + if (props->stroke->color.type == SVG_COLOR_CURRENTCOLOR) { + asp->line_color = GF_COL_ARGB_FIXED(clamped_stroke_opacity, props->color->color.red, props->color->color.green, props->color->color.blue); + } else if (props->stroke->color.type == SVG_COLOR_RGBCOLOR) { + asp->line_color = GF_COL_ARGB_FIXED(clamped_stroke_opacity, props->stroke->color.red, props->stroke->color.green, props->stroke->color.blue); + } else if (props->stroke->color.type >= SVG_COLOR_ACTIVE_BORDER) { + asp->line_color = tr_state->visual->compositor->sys_colors[SVG_COLOR_ACTIVE_BORDER - 3]; + asp->line_color |= ((u32) (clamped_stroke_opacity*255)) << 24; + } + } + if (!tr_state->color_mat.identity) + asp->line_color = gf_cmx_apply(&tr_state->color_mat, asp->line_color); + + if (props->stroke_dasharray->type != SVG_STROKEDASHARRAY_NONE) { + asp->pen_props.dash = GF_DASH_STYLE_SVG; + asp->pen_props.dash_offset = props->stroke_dashoffset->value; + asp->pen_props.dash_set = (GF_DashSettings *) &(props->stroke_dasharray->array); + } + asp->line_scale = (props->vector_effect && (*props->vector_effect == SVG_VECTOREFFECT_NONSCALINGSTROKE)) ? 0 : FIX_ONE; + + asp->pen_props.cap = (u8) *props->stroke_linecap; + asp->pen_props.join = (u8) *props->stroke_linejoin; + asp->pen_props.miterLimit = props->stroke_miterlimit->value; + + if (!tr_state->color_mat.identity) { + asp->fill_color = gf_cmx_apply(&tr_state->color_mat, asp->fill_color); + asp->line_color = gf_cmx_apply(&tr_state->color_mat, asp->line_color); + } + return ret; +} + +static Bool svg_appearance_flag_dirty(u32 flags) +{ +#if 0 + /* fill-related */ + if (flags & GF_SG_SVG_FILL_DIRTY) return 1; + if (flags & GF_SG_SVG_FILLOPACITY_DIRTY) return 1; + if (flags & GF_SG_SVG_FILLRULE_DIRTY) return 1; + + /* stroke-related */ + if (flags & GF_SG_SVG_STROKE_DIRTY) return 1; + if (flags & GF_SG_SVG_STROKEDASHARRAY_DIRTY) return 1; + if (flags & GF_SG_SVG_STROKEDASHOFFSET_DIRTY) return 1; + if (flags & GF_SG_SVG_STROKELINECAP_DIRTY) return 1; + if (flags & GF_SG_SVG_STROKELINEJOIN_DIRTY) return 1; + if (flags & GF_SG_SVG_STROKEMITERLIMIT_DIRTY) return 1; + if (flags & GF_SG_SVG_STROKEOPACITY_DIRTY) return 1; + if (flags & GF_SG_SVG_STROKEWIDTH_DIRTY) return 1; + if (flags & GF_SG_SVG_VECTOREFFECT_DIRTY) return 1; + + /* gradients stops and solidcolor do not affect appearance directly */ + return 0; +#else + if (flags & + (GF_SG_SVG_FILL_DIRTY | GF_SG_SVG_FILLOPACITY_DIRTY | GF_SG_SVG_FILLRULE_DIRTY + | GF_SG_SVG_STROKE_DIRTY | GF_SG_SVG_STROKEDASHARRAY_DIRTY + | GF_SG_SVG_STROKEDASHOFFSET_DIRTY | GF_SG_SVG_STROKELINECAP_DIRTY + | GF_SG_SVG_STROKELINEJOIN_DIRTY | GF_SG_SVG_STROKEMITERLIMIT_DIRTY + | GF_SG_SVG_STROKEOPACITY_DIRTY | GF_SG_SVG_STROKEWIDTH_DIRTY + | GF_SG_SVG_VECTOREFFECT_DIRTY) ) + return 1; + return 0; +#endif +} + +DrawableContext *drawable_init_context_svg(Drawable *drawable, GF_TraverseState *tr_state) +{ + DrawableContext *ctx; + assert(tr_state->visual); + + /*switched-off geometry nodes are not drawn*/ + if (tr_state->switched_off) return NULL; + + //Get a empty context from the current visual + ctx = visual_2d_get_drawable_context(tr_state->visual); + if (!ctx) return NULL; + + gf_mx2d_copy(ctx->transform, tr_state->transform); + + ctx->drawable = drawable; + + if (tr_state->invalidate_all || svg_appearance_flag_dirty(tr_state->svg_flags)) ctx->flags |= CTX_APP_DIRTY; + if (tr_state->svg_flags & (GF_SG_SVG_STROKEDASHARRAY_DIRTY | + GF_SG_SVG_STROKEDASHOFFSET_DIRTY | + GF_SG_SVG_STROKELINECAP_DIRTY | + GF_SG_SVG_STROKELINEJOIN_DIRTY | + GF_SG_SVG_STROKEMITERLIMIT_DIRTY | + GF_SG_SVG_STROKEWIDTH_DIRTY | + GF_SG_SVG_VECTOREFFECT_DIRTY )) ctx->flags |= CTX_SVG_OUTLINE_GEOMETRY_DIRTY; + + ctx->aspect.fill_texture = NULL; + + /*FIXME - only needed for texture*/ + if (!tr_state->color_mat.identity) { + GF_SAFEALLOC(ctx->col_mat, GF_ColorMatrix); + gf_cmx_copy(ctx->col_mat, &tr_state->color_mat); + } + + switch (gf_node_get_tag(ctx->drawable->node) ) { + case TAG_SVG_image: + case TAG_SVG_video: + ctx->aspect.fill_texture = gf_sc_texture_get_handler(ctx->drawable->node); + break; + case TAG_SVG_line: + case TAG_SVG_polyline: + break; + default: + break; + } + + if (drawable_get_aspect_2d_svg(drawable->node, &ctx->aspect, tr_state)) + ctx->flags |= CTX_APP_DIRTY; + + if (ctx->drawable->path) { + if (*tr_state->svg_props->fill_rule == SVG_FILLRULE_NONZERO) { + ctx->drawable->path->flags |= GF_PATH_FILL_ZERO_NONZERO; + } else { + ctx->drawable->path->flags &= ~GF_PATH_FILL_ZERO_NONZERO; + } + } + + /*Update texture info - draw even if texture not created (this may happen if the media is removed)*/ + if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->needs_refresh) ctx->flags |= CTX_TEXTURE_DIRTY; + + /*we are drawing on a centered coord surface, remember to flip the texture*/ + if (tr_state->fliped_coords) + ctx->flags |= CTX_FLIPED_COORDS; + return ctx; +} + + + +#endif //SVG diff --git a/src/compositor/drawable.h b/src/compositor/drawable.h new file mode 100644 index 0000000..53b03e5 --- /dev/null +++ b/src/compositor/drawable.h @@ -0,0 +1,316 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef DRAWABLE_H +#define DRAWABLE_H + +#include + + +typedef struct _drawable Drawable; +typedef struct _drawable_context DrawableContext; + +typedef struct _bound_info +{ + /*cliped bounds in pixels - needed to track static objects with animated cliping*/ + GF_IRect clip; + /*uncliped bounds - needed to track moving objects fully contained in visual and for image bliting*/ + GF_Rect unclip; + /* extra_check: + for MPEG-4: pointer to appearance node (due to DEF/USE) in order to detect same bounds and appearance node change + for SVG: currently not used, should be needed for + */ + void *extra_check; + + /**/ + struct _bound_info *next; +} BoundInfo; + +typedef struct _dirty_rect_info +{ + /*the visual manager for which we're storing the bounds of this node*/ + GF_VisualManager *visual; + /*the current location of the node on the visual manager collected during traverse step*/ + struct _bound_info *current_bounds; + /*the location of the node on the visual manager at the previous frame*/ + struct _bound_info *previous_bounds; + + /**/ + struct _dirty_rect_info *next; +} DRInfo; + +enum { + /*user defined flags*/ + + /*if flag set, the node callback function will be used to draw the node. Otherwise, + high-level draw operations on the drawable & drawable context will be used*/ + DRAWABLE_USE_TRAVERSE_DRAW = 1, + + /*bounds tracker flags, INTERNAL TO RENDERER */ + + /*flag set by drawable_mark_modified during a TRAVERSE_SORT pass when geometry's node has been modified. This forces clearing of the node + and skips bounds checking. + Flag is cleared by the compositor*/ + DRAWABLE_HAS_CHANGED = 1<<1, + /*same flag as above except set when picking/getting bounds out of the main scene traversal routine (user event, script)*/ + DRAWABLE_HAS_CHANGED_IN_LAST_TRAVERSE = 1<<2, + + /*flag set if node has been drawn for the current visual manager*/ + DRAWABLE_DRAWN_ON_VISUAL = 1<<3, + /*set if node is registered in previous node drawn list of the current visual manager + the flag is only set during a visual_draw_frame pass*/ + DRAWABLE_REGISTERED_WITH_VISUAL = 1<<4, + + /*drawable is an overlay surface*/ + DRAWABLE_IS_OVERLAY = 1<<5, + + /**/ + DRAWABLE_IS_CACHED = 1<<6, +}; + +struct _drawable +{ +#ifndef GPAC_DISABLE_3D + /*3D object for drawable if needed - NOT ALLOCATED BY DEFAULT + DO NOT CHANGE THE LOCATION IN THE STRUCTURE, IT SHALL BE FIRST TO BE TYPECASTED TO + A Drawable3D when drawing OpenGL*/ + GF_Mesh *mesh; +#endif + /*set of drawable flags*/ + u32 flags; + + /*node owning the drawable*/ + GF_Node *node; + + /*bounds collector*/ + struct _dirty_rect_info *dri; + + /*graphic path - !! CREATED BY DEFAULT !! */ + GF_Path *path; + /*cached outlines*/ + struct _strikeinfo2d *outline; +}; + +/*construction destruction*/ +Drawable *drawable_new(); +void drawable_del(Drawable *dr); +void drawable_del_ex(Drawable *dr, GF_Compositor *compositor); + +/*cleans up the drawable attached to the node*/ +void drawable_node_del(GF_Node *node); + + +/* +decide whether drawing is needed or not based on visual settings and parent node - must be called +at the end of each TRAVERSE_SORT of drawable nodes +if orig_bounds is NULL, function uses the bounds of the drawable's path +*/ +void drawable_finalize_sort(DrawableContext *ctx, GF_TraverseState *tr_state, GF_Rect *orig_bounds); +/*same as drawable_finalize_sort but skips focus check*/ +void drawable_finalize_sort_ex(DrawableContext *ctx, GF_TraverseState *tr_state, GF_Rect *orig_bounds, Bool skip_focus); + +/*base constructor for geometry objects that work without overloading the drawable stuff*/ +Drawable *drawable_stack_new(GF_Compositor *compositor, GF_Node *node); +/*reset all paths (main path and any outline) of the stack*/ +void drawable_reset_path(Drawable *st); +/*reset all paths outlines (only) of the stack*/ +void drawable_reset_path_outline(Drawable *st); + +/*mark the drawable as modified - this shall be caleed whenever the node geometry is rebuilt +in order to signal this change to the bounds tracker algorithm*/ +void drawable_mark_modified(Drawable *st, GF_TraverseState *tr_state); + +/*checks if the current object is the focused one, and insert the focus drawable at the current pos*/ +void drawable_check_focus_highlight(GF_Node *node, GF_TraverseState *tr_state, GF_Rect *orig_bounds); + +/*reset the highlight state (bounds) if associated with the current node. This is automatically called +whenever reseting a drawable but must be called when a grouping node is modified*/ +void drawable_reset_group_highlight(GF_TraverseState *tr_state, GF_Node *n); + +/*move current bounds to previous bounds for given target visual manager - called BEFORE updating the visual manager +returns 1 if nod was draw last frame on this visual manager, 0 otherwise*/ +Bool drawable_flush_bounds(Drawable *node, GF_VisualManager *on_visual, u32 draw_mode); + +/* + return 1 if same bound is found in previous list (and remove it from the list) + return 0 otherwise +*/ +Bool drawable_has_same_bounds(DrawableContext *ctx, GF_VisualManager *visual); + +/* + return any previous bounds related to the same visual manager in @rc if any + if nothing found return 0 +*/ +Bool drawable_get_previous_bound(Drawable *node, GF_IRect *rc, GF_VisualManager *visual); + +/*reset bounds array (current and previous) on the given visual manager*/ +void drawable_reset_bounds(Drawable *dr, GF_VisualManager *visual); +/*setup clip/uncli pointers for the drawable context*/ +void drawable_check_bounds(DrawableContext *ctx, GF_VisualManager *visual); + +/*the focus drawable routine of the focus object*/ +void drawable_traverse_focus(GF_Node *node, void *rs, Bool is_destroy); + +struct _draw_aspect_2d +{ + /*including alpha*/ + GF_Color fill_color, line_color; + Fixed line_scale; + GF_PenSettings pen_props; + /*texture fill handler*/ + struct _gf_sc_texture_handler *fill_texture; + /*texture stroke handler*/ + struct _gf_sc_texture_handler *line_texture; +}; + +/*fills the 2D drawing properties according to the traversing state - MPEG4/X3D only*/ +u32 drawable_get_aspect_2d_mpeg4(GF_Node *node, DrawAspect2D *asp, GF_TraverseState *tr_state); +/*fills the 2D drawing properties according to the traversing state - SVG only*/ +Bool drawable_get_aspect_2d_svg(GF_Node *node, DrawAspect2D *asp, GF_TraverseState *tr_state); + + +enum +{ + /*set whenever appearance changed*/ + CTX_APP_DIRTY = 1, + /*set whenever texture data changed*/ + CTX_TEXTURE_DIRTY = 1<<1, + + /*set when context is an MPEG-4/VRML node*/ + CTX_HAS_APPEARANCE = 1<<2, + /*set if node completely fits its bounds (flat rect and bitmap) then non transparent*/ + CTX_IS_TRANSPARENT = 1<<3, + /*set if node is text data*/ + CTX_IS_TEXT = 1<<4, + /*set if node is background*/ + CTX_IS_BACKGROUND = 1<<5, + /*turn antialiasing off when drawing*/ + CTX_NO_ANTIALIAS = 1<<6, + /*indicates path has been textured, in which case FILL is skiped*/ + CTX_PATH_FILLED = 1<<7, + /*indicates path outline has been textured, in which case STRIKE is skiped*/ + CTX_PATH_STROKE = 1<<8, + /*indicates SVG path outline geometry has been modified*/ + CTX_SVG_OUTLINE_GEOMETRY_DIRTY = 1<<9, + /*indicates the context is in a flip coord state (used for image and text flip)*/ + CTX_FLIPED_COORDS = 1<<10, +}; + +#define CTX_REDRAW_MASK 0x00000003 + +struct _drawable_context +{ + /*next context allocated, or NULL*/ + struct _drawable_context *next; + + /*any of the above flags*/ + u16 flags; + /*only used by text when splitting strings into chars / substrings*/ + s16 sub_path_index; + + /*drawable using this context*/ + Drawable *drawable; + + /*pointer to clipped and uncliped (for sensors) rect in pixels.*/ + BoundInfo *bi; + + /*draw info*/ + DrawAspect2D aspect; + /*transform matrix from top*/ + GF_Matrix2D transform; + /*color matrix, NULL if none/identity*/ + GF_ColorMatrix *col_mat; + + /* extra check data for bounds matching + for MPEG-4: appearance node + for SVG: parent node if any + */ + GF_Node *appear; + + /*depth z-axis info for 2d scenes in triscope mode*/ +#ifdef GPAC_TRISCOPE_MODE + Fixed depth; +#endif + +}; + +DrawableContext *NewDrawableContext(); +void DeleteDrawableContext(DrawableContext *); +void drawctx_reset(DrawableContext *ctx); +void drawctx_update_info(DrawableContext *ctx, GF_VisualManager *visual); + +/*inits context - may return NULL if the node doesn't have to be drawn*/ +DrawableContext *drawable_init_context_mpeg4(Drawable *node, GF_TraverseState *tr_state); +/*inits context for SVG - may return NULL if the node doesn't have to be drawn*/ +DrawableContext *drawable_init_context_svg(Drawable *drawable, GF_TraverseState *tr_state); + +/*base draw function for 2D objects (texturing, fill and strike) + in 2D, the current context is passed in the traversing state +*/ +void drawable_draw(Drawable *drawable, GF_TraverseState *tr_state); + +/*base picking function*/ +void drawable_pick(Drawable *drawable, GF_TraverseState *tr_state); + +/*SVG picking function (uses SVG pointrer events)*/ +void svg_drawable_pick(GF_Node *node, Drawable *drawable, GF_TraverseState *tr_state); + +/*stored at compositor level and in each drawable node*/ +typedef struct _strikeinfo2d +{ + struct _strikeinfo2d *next; + /*vectorial outline*/ + GF_Path *outline; + /*drawable using this outline*/ + Drawable *drawable; + /*lineprops used to build outline (MPEG-4 only)*/ + GF_Node *lineProps; + /*last modif time of the lineprops node*/ + u32 last_update_time; + /*user+world->local scaling for non-scalable outlines*/ + Fixed line_scale; + /*SVG path length*/ + Fixed path_length; + /*set only for text, indicates sub-path outline*/ + GF_Path *original; + + +#ifndef GPAC_DISABLE_3D + /*3D drawing*/ + Bool is_vectorial; + GF_Mesh *mesh_outline; +#endif +} StrikeInfo2D; + +void delete_strikeinfo2d(StrikeInfo2D *info); +/*get strike and manage any scale change&co. This avoids recomputing outline at each frame...*/ +StrikeInfo2D *drawable_get_strikeinfo(GF_Compositor *compositor, Drawable *drawable, DrawAspect2D *asp, GF_Node *appear, GF_Path *path, u32 svg_flags, GF_TraverseState *tr_state); + + +void drawable_compute_line_scale(GF_TraverseState *tr_state, DrawAspect2D *asp); + +Bool svg_drawable_is_over(Drawable *drawable, Fixed x, Fixed y, DrawAspect2D *asp, GF_TraverseState *tr_state, GF_Rect *glyph_rc); + +#endif diff --git a/src/compositor/events.c b/src/compositor/events.c new file mode 100644 index 0000000..473df43 --- /dev/null +++ b/src/compositor/events.c @@ -0,0 +1,1513 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "visual_manager.h" +#include "nodes_stacks.h" +#include "mpeg4_grouping.h" +#include +#include +#include + +static GF_Node *browse_parent_for_focus(GF_Compositor *compositor, GF_Node *elt, Bool prev_focus); +u32 gf_sc_focus_switch_ring_ex(GF_Compositor *compositor, Bool move_prev, GF_Node *focus, Bool force_focus); + +static void gf_sc_reset_collide_cursor(GF_Compositor *compositor) +{ + if (compositor->sensor_type == GF_CURSOR_COLLIDE) { + GF_Event evt; + compositor->sensor_type = evt.cursor.cursor_type = GF_CURSOR_NORMAL; + evt.type = GF_EVENT_SET_CURSOR; + compositor->video_out->ProcessEvent(compositor->video_out, &evt); + } +} + + +static Bool exec_text_selection(GF_Compositor *compositor, GF_Event *event) +{ + if (event->type>GF_EVENT_MOUSEMOVE) return 0; + + if (compositor->edited_text) + return 0; + if (compositor->text_selection) + return 1; + switch (event->type) { + case GF_EVENT_MOUSEMOVE: + if (compositor->text_selection) + return 1; + break; + case GF_EVENT_MOUSEDOWN: + if (compositor->hit_text) { + compositor->text_selection = compositor->hit_text; + /*return 0: the event may be consumed by the tree*/ + return 0; + } + break; + } + return 0; +} + +static void flush_text_node_edit(GF_Compositor *compositor, Bool final_flush) +{ + u8 *txt; + u32 len; + if (!compositor->edited_text) return; + + if (final_flush && compositor->sel_buffer_len) { + memmove(&compositor->sel_buffer[compositor->caret_pos], &compositor->sel_buffer[compositor->caret_pos+1], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos)); + compositor->sel_buffer_len--; + compositor->sel_buffer[compositor->sel_buffer_len] = 0; + } + + if (*compositor->edited_text) { + free(*compositor->edited_text); + *compositor->edited_text = NULL; + } + if (compositor->sel_buffer_len) { + const u16 *lptr; + txt = malloc(sizeof(char)*2*compositor->sel_buffer_len); + lptr = compositor->sel_buffer; + len = gf_utf8_wcstombs(txt, 2*compositor->sel_buffer_len, &lptr); + txt[len] = 0; + *compositor->edited_text = strdup(txt); + free(txt); + } + gf_node_dirty_set(compositor->focus_node, 0, (compositor->focus_text_type==2)); + gf_node_set_private(compositor->focus_highlight->node, NULL); + compositor->draw_next_frame = 1; + + if (final_flush) { + if (compositor->sel_buffer) free(compositor->sel_buffer); + compositor->sel_buffer = NULL; + compositor->sel_buffer_len = compositor->sel_buffer_alloc = 0; + compositor->edited_text = NULL; + } +} + + +GF_Err gf_sc_paste_text(GF_Compositor *compositor, const char *text) +{ + u16 *conv_buf; + u32 len; + if (!compositor->sel_buffer || !compositor->edited_text) return GF_BAD_PARAM; + if (!text) return GF_OK; + len = strlen(text); + if (!len) return GF_OK; + + gf_sc_lock(compositor, 1); + + conv_buf = malloc(sizeof(u16)*len); + len = gf_utf8_mbstowcs(conv_buf, len, &text); + + compositor->sel_buffer_alloc += len; + if (compositor->sel_buffer_len == compositor->sel_buffer_alloc) + compositor->sel_buffer_alloc++; + + compositor->sel_buffer = realloc(compositor->sel_buffer, sizeof(u16)*compositor->sel_buffer_alloc); + memmove(&compositor->sel_buffer[compositor->caret_pos+len], &compositor->sel_buffer[compositor->caret_pos], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos)); + memcpy(&compositor->sel_buffer[compositor->caret_pos], conv_buf, sizeof(u16)*len); + free(conv_buf); + compositor->sel_buffer_len += len; + compositor->caret_pos += len; + compositor->sel_buffer[compositor->sel_buffer_len]=0; + flush_text_node_edit(compositor, 0); + gf_sc_lock(compositor, 0); + + return GF_OK; +} +static Bool load_text_node(GF_Compositor *compositor, u32 cmd_type) +{ + char **res = NULL; + u32 prev_pos, pos=0; + s32 caret_pos; + Bool append; + Bool delete_cr = 0; + + caret_pos = -1; + + append = 0; + switch (cmd_type) { + /*load last text chunk*/ + case 0: + pos = 0; + break; + /*load prev text chunk*/ + case 4: + delete_cr = 1; + case 1: + if (compositor->dom_text_pos ==0) return 0; + pos = compositor->dom_text_pos - 1; + break; + /*load next text chunk*/ + case 2: + pos = compositor->dom_text_pos + 1; + caret_pos = 0; + break; + /*split text chunk/append new*/ + case 3: + append=1; + pos = compositor->dom_text_pos; + caret_pos = 0; + break; + } + prev_pos = compositor->dom_text_pos; + + compositor->dom_text_pos = 0; + + if (compositor->focus_text_type==3) { + MFString *mf = &((M_Text*)compositor->focus_node)->string; + + if (append) { + gf_sg_vrml_mf_append(mf, GF_SG_VRML_MFSTRING, (void **)res); + compositor->dom_text_pos = mf->count; + } else if (!cmd_type) { + compositor->dom_text_pos = mf->count; + } else if (pos <= mf->count) { + compositor->dom_text_pos = pos; + } else { + compositor->dom_text_pos = prev_pos; + return 0; + } + res = &mf->vals[compositor->dom_text_pos-1]; + } else { +#ifndef GPAC_DISABLE_SVG + GF_ChildNodeItem *child = ((GF_ParentNode *) compositor->focus_node)->children; + + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_DOMText: + break; + default: + child = child->next; + continue; + } + compositor->dom_text_pos++; + if (!cmd_type) res = &((GF_DOMText *)child->node)->textContent; + else if (pos==compositor->dom_text_pos) { + if (append) { + u16 end; + u16 *srcp; + u32 len; + GF_DOMText *cur, *ntext; + GF_ChildNodeItem *children = ((GF_ParentNode *) compositor->focus_node)->children; + GF_Node *t = gf_node_new(gf_node_get_graph(child->node), TAG_SVG_tbreak); + + gf_node_init(t); + gf_node_register(t, compositor->focus_node); + + pos = gf_node_list_find_child(children, child->node); + + /*we're only inserting a tbreak*/ + if (!compositor->caret_pos) { + gf_node_list_insert_child(&children, t, pos); + res = &((GF_DOMText *)child->node)->textContent; + } else { + gf_node_list_insert_child(&children, t, pos+1); + ntext = (GF_DOMText*) gf_node_new(gf_node_get_graph(child->node), TAG_DOMText); + gf_node_init(t); + gf_node_list_insert_child(&children, (GF_Node *)ntext, pos+2); + gf_node_register((GF_Node*)ntext, compositor->focus_node); + + cur = (GF_DOMText*) child->node; + + free(cur->textContent); + end = compositor->sel_buffer[compositor->caret_pos]; + compositor->sel_buffer[compositor->caret_pos] = 0; + len = gf_utf8_wcslen(compositor->sel_buffer); + cur->textContent = malloc(sizeof(char)*(len+1)); + srcp = compositor->sel_buffer; + len = gf_utf8_wcstombs(cur->textContent, len, &srcp); + cur->textContent[len] = 0; + compositor->sel_buffer[compositor->caret_pos] = end; + + if (compositor->caret_pos+1sel_buffer_len) { + len = gf_utf8_wcslen(compositor->sel_buffer + compositor->caret_pos + 1); + ntext->textContent = malloc(sizeof(char)*(len+1)); + srcp = compositor->sel_buffer + compositor->caret_pos + 1; + len = gf_utf8_wcstombs(ntext->textContent, len, &srcp); + ntext->textContent[len] = 0; + } else { + ntext->textContent = strdup(""); + } + res = &ntext->textContent; + compositor->dom_text_pos ++; + compositor->edited_text = NULL; + } + + } else { + if (delete_cr && child->next) { + GF_Node *tbreak = child->next->node; + GF_ChildNodeItem *children = ((GF_ParentNode *) compositor->focus_node)->children; + gf_node_list_del_child(&children, tbreak); + gf_node_unregister(tbreak, compositor->focus_node); + if (child->next && (gf_node_get_tag(child->next->node)==TAG_DOMText) ) { + GF_DOMText *n1 = (GF_DOMText *)child->node; + GF_DOMText *n2 = (GF_DOMText *)child->next->node; + + if (compositor->edited_text) { + flush_text_node_edit(compositor, 1); + } + caret_pos = strlen(n1->textContent); + if (n2->textContent) { + n1->textContent = realloc(n1->textContent, sizeof(char)*(strlen(n1->textContent)+strlen(n2->textContent)+1)); + strcat(n1->textContent, n2->textContent); + } + gf_node_list_del_child(&children, (GF_Node*)n2); + gf_node_unregister((GF_Node*)n2, compositor->focus_node); + compositor->edited_text = NULL; + } + } + res = &((GF_DOMText *)child->node)->textContent; + } +/* if (1) { + GF_ChildNodeItem *child = ((GF_ParentNode *) compositor->focus_node)->children; + fprintf(stdout, "Dumping text tree:\n"); + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_SVG_tbreak: fprintf(stdout, "\ttbreak\n"); break; + case TAG_DOMText: fprintf(stdout, "\ttext: %s\n", ((GF_DOMText *)child->node)->textContent); break; + } + child = child->next; + } + } +*/ + break; + } + child = child->next; + continue; + } + /*load of an empty text*/ + if (!res && !cmd_type) { + GF_DOMText *t = gf_dom_add_text_node(compositor->focus_node, strdup("")); + res = &t->textContent; + } + + if (!res) { + compositor->dom_text_pos = prev_pos; + return 0; + } +#endif + } + + if (compositor->edited_text) { + flush_text_node_edit(compositor, 1); + } + + if (*res) { + const char *src = *res; + compositor->sel_buffer_alloc = 2+strlen(src); + compositor->sel_buffer = realloc(compositor->sel_buffer, sizeof(u16)*compositor->sel_buffer_alloc); + + if (caret_pos>=0) { + compositor->sel_buffer_len = gf_utf8_mbstowcs(compositor->sel_buffer, compositor->sel_buffer_alloc, &src); + memmove(&compositor->sel_buffer[caret_pos+1], &compositor->sel_buffer[caret_pos], sizeof(u16)*(compositor->sel_buffer_len-caret_pos)); + compositor->sel_buffer[caret_pos]='|'; + compositor->caret_pos = caret_pos; + + } else { + compositor->sel_buffer_len = gf_utf8_mbstowcs(compositor->sel_buffer, compositor->sel_buffer_alloc, &src); + compositor->sel_buffer[compositor->sel_buffer_len]='|'; + compositor->caret_pos = compositor->sel_buffer_len; + } + compositor->sel_buffer_len++; + compositor->sel_buffer[compositor->sel_buffer_len]=0; + } else { + compositor->sel_buffer_alloc = 2; + compositor->sel_buffer = malloc(sizeof(u16)*2); + compositor->sel_buffer[0] = '|'; + compositor->sel_buffer[1] = 0; + compositor->caret_pos = 0; + compositor->sel_buffer_len = 1; + } + compositor->edited_text = res; + flush_text_node_edit(compositor, 0); + return 1; +} + +static void exec_text_input(GF_Compositor *compositor, GF_Event *event) +{ + Bool is_end = 0; + + if (!event) { + load_text_node(compositor, 0); + return; + } else if (event->type==GF_EVENT_TEXTINPUT) { + switch (event->character.unicode_char) { + case '\r': + case '\n': + case '\t': + case '\b': + return; + default: + if (compositor->sel_buffer_len + 1 == compositor->sel_buffer_alloc) { + compositor->sel_buffer_alloc += 10; + compositor->sel_buffer = realloc(compositor->sel_buffer, sizeof(u16)*compositor->sel_buffer_alloc); + } + memmove(&compositor->sel_buffer[compositor->caret_pos+1], &compositor->sel_buffer[compositor->caret_pos], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos)); + compositor->sel_buffer[compositor->caret_pos] = event->character.unicode_char; + compositor->sel_buffer_len++; + compositor->caret_pos++; + compositor->sel_buffer[compositor->sel_buffer_len] = 0; + break; + } + } else if (event->type==GF_EVENT_KEYDOWN) { + u32 prev_caret = compositor->caret_pos; + switch (event->key.key_code) { + /*end of edit mode*/ + case GF_KEY_ESCAPE: + is_end = 1; + break; + case GF_KEY_LEFT: + if (compositor->caret_pos) { + if (event->key.flags & GF_KEY_MOD_CTRL) { + while (compositor->caret_pos && (compositor->sel_buffer[compositor->caret_pos] != ' ')) + compositor->caret_pos--; + } else { + compositor->caret_pos--; + } + } + else { + load_text_node(compositor, 1); + return; + } + break; + case GF_KEY_RIGHT: + if (compositor->caret_pos+1sel_buffer_len) { + if (event->key.flags & GF_KEY_MOD_CTRL) { + while ((compositor->caret_pos+1sel_buffer_len) + && (compositor->sel_buffer[compositor->caret_pos] != ' ')) + compositor->caret_pos++; + } else { + compositor->caret_pos++; + } + } + else { + load_text_node(compositor, 2); + return; + } + break; + case GF_KEY_HOME: + compositor->caret_pos = 0; + break; + case GF_KEY_END: + compositor->caret_pos = compositor->sel_buffer_len-1; + break; + case GF_KEY_TAB: + if (!load_text_node(compositor, (event->key.flags & GF_KEY_MOD_SHIFT) ? 1 : 2)) { + is_end = 1; + break; + } + return; + case GF_KEY_BACKSPACE: + if (compositor->caret_pos) { + if (compositor->sel_buffer_len) compositor->sel_buffer_len--; + memmove(&compositor->sel_buffer[compositor->caret_pos-1], &compositor->sel_buffer[compositor->caret_pos], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos+1)); + compositor->sel_buffer[compositor->sel_buffer_len]=0; + compositor->caret_pos--; + flush_text_node_edit(compositor, 0); + } else { + load_text_node(compositor, 4); + } + return; + case GF_KEY_DEL: + if (compositor->caret_pos+1sel_buffer_len) { + if (compositor->sel_buffer_len) compositor->sel_buffer_len--; + memmove(&compositor->sel_buffer[compositor->caret_pos+1], &compositor->sel_buffer[compositor->caret_pos+2], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos-1)); + compositor->sel_buffer[compositor->sel_buffer_len]=0; + flush_text_node_edit(compositor, 0); + return; + } + break; + case GF_KEY_ENTER: + load_text_node(compositor, 3); + return; + default: + return; + } + if (!is_end) { + if (compositor->caret_pos==prev_caret) return; + memmove(&compositor->sel_buffer[prev_caret], &compositor->sel_buffer[prev_caret+1], sizeof(u16)*(compositor->sel_buffer_len-prev_caret)); + memmove(&compositor->sel_buffer[compositor->caret_pos+1], &compositor->sel_buffer[compositor->caret_pos], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos)); + compositor->sel_buffer[compositor->caret_pos]='|'; + } + } else { + return; + } + + flush_text_node_edit(compositor, is_end); +} + +static Bool hit_node_editable(GF_Compositor *compositor, Bool check_focus_node) +{ +#ifndef GPAC_DISABLE_SVG + SVGAllAttributes atts; +#endif + u32 tag; + GF_Node *text = check_focus_node ? compositor->focus_node : compositor->hit_node; + if (!text) return 0; + if (compositor->hit_node==compositor->focus_node) return compositor->focus_text_type ? 1 : 0; + + tag = gf_node_get_tag(text); + if ( (tag==TAG_MPEG4_Text) || (tag==TAG_X3D_Text)) { + M_FontStyle *fs = (M_FontStyle *) ((M_Text *)text)->fontStyle; + if (!fs) return 0; + if (!strstr(fs->style.buffer, "editable") && !strstr(fs->style.buffer, "EDITABLE")) return 0; + compositor->focus_text_type = 3; + compositor->focus_node = text; + return 1; + } +#ifndef GPAC_DISABLE_SVG + if (tag <= GF_NODE_FIRST_DOM_NODE_TAG) return 0; + gf_svg_flatten_attributes((SVG_Element *)text, &atts); + if (!atts.editable || !*atts.editable) return 0; + switch (tag) { + case TAG_SVG_text: + case TAG_SVG_textArea: + compositor->focus_text_type = 1; + break; + case TAG_SVG_tspan: + compositor->focus_text_type = 2; + break; + default: + return 0; + } + if (compositor->focus_node != text) { + GF_DOM_Event evt; + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.bubbles = 1; + evt.type = GF_EVENT_FOCUSOUT; + gf_dom_event_fire(compositor->focus_node, &evt); + + compositor->focus_node = text; + evt.type = GF_EVENT_FOCUSIN; + gf_dom_event_fire(compositor->focus_node, &evt); + compositor->focus_uses_dom_events = 1; + } + compositor->hit_node = NULL; +#endif + return 1; +} + +static GF_Node *get_parent_focus(GF_Node *node, GF_List *hit_use_stack, u32 cur_idx) +{ + GF_Node *parent; +#ifndef GPAC_DISABLE_SVG + GF_FieldInfo info; +#endif + if (!node) return NULL; + +#ifndef GPAC_DISABLE_SVG + if (gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_focusable, 0, 0, &info)==GF_OK) { + if ( *(SVG_Focusable*)info.far_ptr == SVG_FOCUSABLE_TRUE) return node; + } +#endif + parent = gf_node_get_parent(node, 0); + if (cur_idx) { + GF_Node *n = gf_list_get(hit_use_stack, cur_idx-1); + if (n==node) { + parent = gf_list_get(hit_use_stack, cur_idx-2); + if (cur_idx>1) cur_idx-=2; + else cur_idx=0; + } + } + return get_parent_focus(parent, hit_use_stack, cur_idx); +} + +static Bool exec_event_dom(GF_Compositor *compositor, GF_Event *event) +{ +#ifndef GPAC_DISABLE_SVG + GF_DOM_Event evt; + u32 cursor_type; + Bool ret = 0; + + cursor_type = GF_CURSOR_NORMAL; + /*all mouse events*/ + if ((event->type<=GF_EVENT_MOUSEWHEEL) + // && (gf_node_get_dom_event_filter(gf_sg_get_root_node(compositor->scene)) & GF_DOM_EVENT_MOUSE) + ) { + Fixed X = compositor->hit_world_point.x; + Fixed Y = compositor->hit_world_point.y; + if (compositor->hit_node) { + GF_Node *focus; + Bool hit_changed = 0; + GF_Node *current_use = gf_list_last(compositor->hit_use_stack); + cursor_type = compositor->sensor_type; + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.clientX = evt.screenX = FIX2INT(X); + evt.clientY = evt.screenY = FIX2INT(Y); + evt.bubbles = 1; + evt.cancelable = 1; + evt.key_flags = compositor->key_states; + + /*first check if the hit node is not the grab (previous) node - this may happen regardless of the + mouse actions (animations, ...)*/ + if ((compositor->grab_node != compositor->hit_node) || (compositor->grab_use != current_use) ) { + /*mouse out*/ + if (compositor->grab_node) { + evt.relatedTarget = compositor->hit_node; + evt.type = GF_EVENT_MOUSEOUT; + ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->prev_hit_use_stack); + /*prepare mouseOver*/ + evt.relatedTarget = compositor->grab_node; + } + compositor->grab_node = compositor->hit_node; + compositor->grab_use = current_use; + + /*mouse over*/ + evt.type = GF_EVENT_MOUSEOVER; + ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack); + hit_changed = 1; + } + switch (event->type) { + case GF_EVENT_MOUSEMOVE: + evt.cancelable = 0; + if (!hit_changed) { + evt.type = GF_EVENT_MOUSEMOVE; + ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack); + } + compositor->num_clicks = 0; + break; + case GF_EVENT_MOUSEDOWN: + if ((compositor->grab_x != X) || (compositor->grab_y != Y)) compositor->num_clicks = 0; + evt.type = GF_EVENT_MOUSEDOWN; + compositor->num_clicks ++; + evt.button = event->mouse.button; + evt.detail = compositor->num_clicks; + + ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack); + compositor->grab_x = X; + compositor->grab_y = Y; + + /*change focus*/ + focus = get_parent_focus(compositor->grab_node, compositor->hit_use_stack, gf_list_count(compositor->hit_use_stack)); + if (focus) gf_sc_focus_switch_ring_ex(compositor, 0, focus, 1); + else if (compositor->focus_node) gf_sc_focus_switch_ring_ex(compositor, 0, NULL, 1); + + break; + case GF_EVENT_MOUSEUP: + evt.type = GF_EVENT_MOUSEUP; + evt.button = event->mouse.button; + evt.detail = compositor->num_clicks; + ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack); + if ((compositor->grab_x == X) && (compositor->grab_y == Y)) { + evt.type = GF_EVENT_CLICK; + ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack); + } + break; + case GF_EVENT_MOUSEWHEEL: + evt.type = GF_EVENT_MOUSEWHEEL; + evt.button = event->mouse.button; + evt.new_scale = event->mouse.wheel_pos; + ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->hit_use_stack); + break; + } + cursor_type = evt.has_ui_events ? GF_CURSOR_TOUCH : GF_CURSOR_NORMAL; + } else { + /*mouse out*/ + if (compositor->grab_node) { + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.clientX = evt.screenX = FIX2INT(X); + evt.clientY = evt.screenY = FIX2INT(Y); + evt.bubbles = 1; + evt.cancelable = 1; + evt.key_flags = compositor->key_states; + evt.type = GF_EVENT_MOUSEOUT; + ret += gf_dom_event_fire_ex(compositor->grab_node, &evt, compositor->prev_hit_use_stack); + } + + /*reset focus*/ + if (compositor->focus_node && (event->type==GF_EVENT_MOUSEDOWN)) + gf_sc_focus_switch_ring_ex(compositor, 0, NULL, 1); + + compositor->grab_node = NULL; + compositor->grab_use = NULL; + + /*dispatch event to root SVG*/ + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.clientX = evt.screenX = FIX2INT(X); + evt.clientY = evt.screenY = FIX2INT(Y); + evt.bubbles = 1; + evt.cancelable = 1; + evt.key_flags = compositor->key_states; + evt.type = event->type; + ret += gf_dom_event_fire_ex(gf_sg_get_root_node(compositor->scene), &evt, compositor->hit_use_stack); + } + if (compositor->sensor_type != cursor_type) { + GF_Event c_evt; + c_evt.type = GF_EVENT_SET_CURSOR; + c_evt.cursor.cursor_type = cursor_type; + compositor->video_out->ProcessEvent(compositor->video_out, &c_evt); + compositor->sensor_type = cursor_type; + } + } + else if ((event->type>=GF_EVENT_KEYUP) && (event->type<=GF_EVENT_LONGKEYPRESS)) { + GF_Node *target; + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.key_flags = event->key.flags; + evt.bubbles = 1; + evt.cancelable = 1; + evt.type = event->type; + evt.detail = event->key.key_code; + evt.key_hw_code = event->key.hw_code; + target = compositor->focus_node; + if (!target) target = gf_sg_get_root_node(compositor->scene); + ret += gf_dom_event_fire(target, &evt); + + if (event->type==GF_EVENT_KEYDOWN) { + switch (event->key.key_code) { + case GF_KEY_ENTER: + evt.type = GF_EVENT_ACTIVATE; + evt.detail = 0; + ret += gf_dom_event_fire(target, &evt); + break; + } + } + } else if (event->type == GF_EVENT_TEXTINPUT) { + GF_Node *target; + switch (event->character.unicode_char) { + case '\r': + case '\n': + case '\t': + case '\b': + break; + default: + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.key_flags = event->key.flags; + evt.bubbles = 1; + evt.cancelable = 1; + evt.type = event->type; + evt.detail = event->character.unicode_char; + target = compositor->focus_node; + if (!target) target = gf_sg_get_root_node(compositor->scene); + ret += gf_dom_event_fire(target, &evt); + break; + } + } + return ret; +#else + return 0; +#endif +} + +static Bool exec_event_vrml(GF_Compositor *compositor, GF_Event *ev) +{ + GF_SensorHandler *hs, *hs_grabbed; + GF_List *tmp; + u32 i, count, stype; + + /*composite texture*/ + if (!gf_list_count(compositor->sensors) && compositor->hit_appear) + return compositor_compositetexture_handle_event(compositor, ev); + + hs_grabbed = NULL; + stype = GF_CURSOR_NORMAL; + count = gf_list_count(compositor->previous_sensors); + for (i=0; iprevious_sensors, i); + if (gf_list_find(compositor->sensors, hs) < 0) { + /*that's a bit ugly but we need coords if pointer out of the shape when sensor grabbed...*/ + if (compositor->grabbed_sensor) { + hs_grabbed = hs; + } else { + hs->OnUserEvent(hs, 0, ev, compositor); + } + } + } + + count = gf_list_count(compositor->sensors); + for (i=0; isensors, i); + hs->OnUserEvent(hs, 1, ev, compositor); + stype = gf_node_get_tag(hs->sensor); + if (hs==hs_grabbed) hs_grabbed = NULL; + } + /*switch sensors*/ + tmp = compositor->sensors; + compositor->sensors = compositor->previous_sensors; + compositor->previous_sensors = tmp; + + /*check if we have a grabbed sensor*/ + if (hs_grabbed) { + hs_grabbed->OnUserEvent(hs_grabbed, 0, ev, compositor); + gf_list_reset(compositor->previous_sensors); + gf_list_add(compositor->previous_sensors, hs_grabbed); + stype = gf_node_get_tag(hs_grabbed->sensor); + } + + /*and set cursor*/ + if (compositor->sensor_type != GF_CURSOR_COLLIDE) { + switch (stype) { + case TAG_MPEG4_Anchor: + case TAG_X3D_Anchor: + stype = GF_CURSOR_ANCHOR; + break; + case TAG_MPEG4_PlaneSensor2D: + case TAG_MPEG4_PlaneSensor: + case TAG_X3D_PlaneSensor: + stype = GF_CURSOR_PLANE; break; + case TAG_MPEG4_CylinderSensor: + case TAG_X3D_CylinderSensor: + case TAG_MPEG4_DiscSensor: + case TAG_MPEG4_SphereSensor: + case TAG_X3D_SphereSensor: + stype = GF_CURSOR_ROTATE; break; + case TAG_MPEG4_ProximitySensor2D: + case TAG_MPEG4_ProximitySensor: + case TAG_X3D_ProximitySensor: + stype = GF_CURSOR_PROXIMITY; break; + case TAG_MPEG4_TouchSensor: + case TAG_X3D_TouchSensor: + stype = GF_CURSOR_TOUCH; break; + default: stype = GF_CURSOR_NORMAL; break; + } + if ((stype != GF_CURSOR_NORMAL) || (compositor->sensor_type != stype)) { + GF_Event evt; + evt.type = GF_EVENT_SET_CURSOR; + evt.cursor.cursor_type = stype; + compositor->video_out->ProcessEvent(compositor->video_out, &evt); + compositor->sensor_type = stype; + } + } else { + gf_sc_reset_collide_cursor(compositor); + } + return count ? 1 : 0; +} + + +static Bool exec_vrml_key_event(GF_Compositor *compositor, GF_Node *node, GF_Event *ev, Bool is_focus_out) +{ + GF_SensorHandler *hdl = NULL; + GF_ChildNodeItem *child; + u32 ret = 0; + if (!node) node = compositor->focus_node; + if (!node) return 0; + + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_Text: + case TAG_X3D_Text: + return 0; + case TAG_MPEG4_Layout: + hdl = compositor_mpeg4_layout_get_sensor_handler(node); + break; + case TAG_MPEG4_Anchor: + case TAG_X3D_Anchor: + hdl = compositor_mpeg4_get_sensor_handler(node); + break; + } + child = ((GF_ParentNode*)node)->children; + if (hdl) { + ret++; + hdl->OnUserEvent(hdl, is_focus_out ? 0 : 1, ev, compositor); + } else { + while (child) { + hdl = compositor_mpeg4_get_sensor_handler(child->node); + if (hdl) { + ret++; + hdl->OnUserEvent(hdl, is_focus_out ? 0 : 1, ev, compositor); + } + child = child->next; + } + } + return ret ? 1 : 0; +} + + + +Bool visual_execute_event(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children) +{ + Bool ret; + Bool reset_sel = 0; + GF_List *temp_stack; + GF_Compositor *compositor = visual->compositor; + tr_state->traversing_mode = TRAVERSE_PICK; +#ifndef GPAC_DISABLE_3D + tr_state->layer3d = NULL; +#endif + + /*preprocess text selection and edition*/ + if ((ev->typemouse.button==GF_MOUSE_LEFT)) { + if (compositor->text_selection) { + if (ev->type==GF_EVENT_MOUSEUP) { + if (compositor->store_text_state==GF_SC_TSEL_ACTIVE) + compositor->store_text_state = GF_SC_TSEL_FROZEN; + else { + reset_sel = 1; + } + } + else if (ev->type==GF_EVENT_MOUSEDOWN) { + reset_sel = 1; + } + } else if (compositor->edited_text) { + if (ev->type==GF_EVENT_MOUSEDOWN) + reset_sel = 1; + } + if (ev->type==GF_EVENT_MOUSEUP) { + if (hit_node_editable(compositor, 0)) { + compositor->text_selection = NULL; + exec_text_input(compositor, NULL); + return 1; + } +#if 0 + else if (!compositor->focus_node) { + gf_sc_focus_switch_ring_ex(compositor, 0, gf_sg_get_root_node(compositor->scene), 1); + } +#endif + } + if (reset_sel) { + flush_text_node_edit(compositor, 1); + + compositor->store_text_state = GF_SC_TSEL_RELEASED; + compositor->text_selection = NULL; + if (compositor->selected_text) free(compositor->selected_text); + compositor->selected_text = NULL; + if (compositor->sel_buffer) free(compositor->sel_buffer); + compositor->sel_buffer = NULL; + compositor->sel_buffer_alloc = 0; + compositor->sel_buffer_len = 0; + + compositor->draw_next_frame = 1; + } else if (compositor->store_text_state == GF_SC_TSEL_RELEASED) { + compositor->store_text_state = GF_SC_TSEL_NONE; + } + + } + + /*pick node*/ + compositor->hit_appear = NULL; + compositor->hit_text = NULL; + temp_stack = compositor->prev_hit_use_stack; + compositor->prev_hit_use_stack = compositor->hit_use_stack; + compositor->hit_use_stack = temp_stack; + +#ifndef GPAC_DISABLE_3D + if (visual->type_3d) + visual_3d_pick_node(visual, tr_state, ev, children); + else +#endif + visual_2d_pick_node(visual, tr_state, ev, children); + + gf_list_reset(tr_state->vrml_sensors); + + if (exec_text_selection(compositor, ev)) + return 1; + + if (compositor->hit_use_dom_events) { + ret = exec_event_dom(compositor, ev); + if (ret) return 1; + /*no vrml sensors above*/ + if (!gf_list_count(compositor->sensors) && !gf_list_count(compositor->previous_sensors)) return 0; + } + return exec_event_vrml(compositor, ev); +} + +u32 gf_sc_svg_focus_navigate(GF_Compositor *compositor, u32 key_code) +{ +#ifndef GPAC_DISABLE_SVG + SVGAllAttributes atts; + GF_DOM_Event evt; + u32 ret = 0; + GF_Node *n; + SVG_Focus *focus = NULL; + + /*only for dom-based nodes*/ + if (!compositor->focus_node || !compositor->focus_uses_dom_events) return 0; + + n=NULL; + gf_svg_flatten_attributes((SVG_Element *)compositor->focus_node, &atts); + switch (key_code) { + case GF_KEY_LEFT: focus = atts.nav_left; break; + case GF_KEY_RIGHT: focus = atts.nav_right; break; + case GF_KEY_UP: focus = atts.nav_up; break; + case GF_KEY_DOWN: focus = atts.nav_down; break; + default: return 0; + } + if (!focus) return 0; + + if (focus->type==SVG_FOCUS_SELF) return 0; + if (focus->type==SVG_FOCUS_AUTO) return 0; + if (!focus->target.target) { + if (!focus->target.string) return 0; + focus->target.target = gf_sg_find_node_by_name(compositor->scene, focus->target.string+1); + } + n = focus->target.target; + + ret = 0; + if (n != compositor->focus_node) { + /*the event is already handled, even though no listeners may be present*/ + ret = 1; + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.bubbles = 1; + if (compositor->focus_node) { + evt.type = GF_EVENT_FOCUSOUT; + gf_dom_event_fire(compositor->focus_node, &evt); + } + if (n) { + evt.relatedTarget = n; + evt.type = GF_EVENT_FOCUSIN; + gf_dom_event_fire(n, &evt); + } + compositor->focus_node = n; + //invalidate in case we draw focus rect + gf_sc_invalidate(compositor, NULL); + } + return ret; +#else + return 0; +#endif /*GPAC_DISABLE_SVG*/ +} + +/*focus management*/ +static Bool is_focus_target(GF_Node *elt) +{ +#ifndef GPAC_DISABLE_SVG + u32 i, count; +#endif + u32 tag = gf_node_get_tag(elt); + switch (tag) { +#ifndef GPAC_DISABLE_SVG + case TAG_SVG_a: +#endif + case TAG_MPEG4_Anchor: + case TAG_X3D_Anchor: + return 1; + default: + break; + } + if (tag<=GF_NODE_FIRST_DOM_NODE_TAG) return 0; + +#ifndef GPAC_DISABLE_SVG + count = gf_dom_listener_count(elt); + for (i=0; itype) { + case GF_EVENT_FOCUSIN: + case GF_EVENT_FOCUSOUT: + case GF_EVENT_ACTIVATE: + return 1; + } + } + } +#endif /*GPAC_DISABLE_SVG*/ + return 0; +} + +#define CALL_SET_FOCUS(__child) { \ + gf_list_add(compositor->focus_ancestors, elt); \ + n = set_focus(compositor, __child, current_focus, prev_focus); \ + if (n) return n; \ + gf_list_rem_last(compositor->focus_ancestors); \ + return NULL; \ + } \ + + +static void rebuild_focus_ancestor(GF_Compositor *compositor, GF_Node *elt) +{ + gf_list_reset(compositor->focus_ancestors); + while (elt) { + GF_Node *par = gf_node_get_parent(elt, 0); + if (!par) break; + gf_list_insert(compositor->focus_ancestors, par, 0); + elt = par; + } +} + +static GF_Node *set_focus(GF_Compositor *compositor, GF_Node *elt, Bool current_focus, Bool prev_focus) +{ + u32 tag; + GF_ChildNodeItem *child = NULL; + GF_Node *use_node = NULL; + GF_Node *anim_node = NULL; + GF_Node *n; + + if (!elt) return NULL; + + tag = gf_node_get_tag(elt); + if (tag <= GF_NODE_FIRST_DOM_NODE_TAG) { + switch (tag) { + case TAG_MPEG4_Group: case TAG_X3D_Group: + case TAG_MPEG4_Transform: case TAG_X3D_Transform: + case TAG_MPEG4_Billboard: case TAG_X3D_Billboard: + case TAG_MPEG4_Collision: case TAG_X3D_Collision: + case TAG_MPEG4_LOD: case TAG_X3D_LOD: + case TAG_MPEG4_OrderedGroup: + case TAG_MPEG4_Transform2D: + case TAG_MPEG4_TransformMatrix2D: + case TAG_MPEG4_ColorTransform: + case TAG_MPEG4_Layer3D: + case TAG_MPEG4_Layer2D: + case TAG_MPEG4_PathLayout: + case TAG_MPEG4_Form: + case TAG_MPEG4_Anchor: case TAG_X3D_Anchor: + if (!current_focus) { + /*get the base grouping stack (*/ + BaseGroupingStack *grp = (BaseGroupingStack*)gf_node_get_private(elt); + if (grp && (grp->flags & (GROUP_HAS_SENSORS | GROUP_IS_ANCHOR) )) + return elt; + } + break; + case TAG_MPEG4_Switch: case TAG_X3D_Switch: + { + s32 i, wc; + GF_ChildNodeItem *child; + if (tag==TAG_X3D_Switch) { + child = ((X_Switch*)elt)->children; + wc = ((X_Switch*)elt)->whichChoice; + } else { + child = ((M_Switch*)elt)->choice; + wc = ((M_Switch*)elt)->whichChoice; + } + if (wc==-1) return NULL; + i=0; + while (child) { + if (i==wc) CALL_SET_FOCUS(child->node); + child = child->next; + } + return NULL; + } + + case TAG_MPEG4_Text: case TAG_X3D_Text: + if (!current_focus) { + M_FontStyle *fs = (M_FontStyle *) ((M_Text *)elt)->fontStyle; + if (!fs) return NULL; + if (!strstr(fs->style.buffer, "editable") && !strstr(fs->style.buffer, "EDITABLE")) return NULL; + compositor->focus_text_type = 3; + return elt; + } + return NULL; + case TAG_MPEG4_Layout: + if (!current_focus && (compositor_mpeg4_layout_get_sensor_handler(elt)!=NULL)) + return elt; + break; + + case TAG_ProtoNode: + CALL_SET_FOCUS(gf_node_get_proto_root(elt)); + + case TAG_MPEG4_Inline: case TAG_X3D_Inline: + CALL_SET_FOCUS(gf_inline_get_subscene_root(elt)); + + case TAG_MPEG4_Shape: case TAG_X3D_Shape: + gf_list_add(compositor->focus_ancestors, elt); + n = set_focus(compositor, ((M_Shape*)elt)->geometry, current_focus, prev_focus); + if (n) return n; + n = set_focus(compositor, ((M_Shape*)elt)->appearance, current_focus, prev_focus); + if (n) return n; + gf_list_rem_last(compositor->focus_ancestors); + return NULL; + + case TAG_MPEG4_Appearance: case TAG_X3D_Appearance: + CALL_SET_FOCUS(((M_Appearance*)elt)->texture); + + case TAG_MPEG4_CompositeTexture2D: case TAG_MPEG4_CompositeTexture3D: + /*CompositeTextures are not grouping nodes per say*/ + child = ((GF_ParentNode*)elt)->children; + while (child) { + if (compositor_mpeg4_get_sensor_handler(child->node) != NULL) + return elt; + child = child->next; + } + break; + + default: + return NULL; + } + + child = ((GF_ParentNode*)elt)->children; + } else { +#ifndef GPAC_DISABLE_SVG + SVGAllAttributes atts; + + if (tag==TAG_SVG_defs) return NULL; + + gf_svg_flatten_attributes((SVG_Element *)elt, &atts); + + if (atts.display && (*atts.display==SVG_DISPLAY_NONE)) return NULL; + + if (!current_focus) { + Bool is_auto = 1; + if (atts.focusable) { + if (*atts.focusable==SVG_FOCUSABLE_TRUE) return elt; + if (*atts.focusable==SVG_FOCUSABLE_FALSE) is_auto = 0; + } + if (is_auto && is_focus_target(elt)) return elt; + + if (atts.editable && *atts.editable) { + switch (tag) { + case TAG_SVG_text: + case TAG_SVG_textArea: + compositor->focus_text_type = 1; + return elt; + case TAG_SVG_tspan: + compositor->focus_text_type = 2; + return elt; + default: + break; + } + } + } + + if (prev_focus) { + if (atts.nav_prev) { + switch (atts.nav_prev->type) { + case SVG_FOCUS_SELF: + /*focus locked on element*/ + return elt; + case SVG_FOCUS_IRI: + if (!atts.nav_prev->target.target) { + if (!atts.nav_prev->target.string) return NULL; + atts.nav_prev->target.target = gf_sg_find_node_by_name(compositor->scene, atts.nav_prev->target.string+1); + } + if (atts.nav_prev->target.target) { + rebuild_focus_ancestor(compositor, atts.nav_prev->target.target); + return atts.nav_prev->target.target; + } + default: + break; + } + } + } else { + /*check next*/ + if (atts.nav_next) { + switch (atts.nav_next->type) { + case SVG_FOCUS_SELF: + /*focus locked on element*/ + return elt; + case SVG_FOCUS_IRI: + if (!atts.nav_next->target.target) { + if (!atts.nav_next->target.string) return NULL; + atts.nav_next->target.target = gf_sg_find_node_by_name(compositor->scene, atts.nav_next->target.string+1); + } + if (atts.nav_next->target.target) { + rebuild_focus_ancestor(compositor, atts.nav_next->target.target); + return atts.nav_next->target.target; + } + default: + break; + } + } + } + if (atts.xlink_href) { + switch (tag) { + case TAG_SVG_use: + use_node = compositor_svg_get_xlink_resource_node(elt, atts.xlink_href); + break; + case TAG_SVG_animation: + anim_node = compositor_svg_get_xlink_resource_node(elt, atts.xlink_href); + break; + } + } +#endif /*GPAC_DISABLE_SVG*/ + child = ((GF_ParentNode *)elt)->children; + } + + if (prev_focus) { + u32 count, i; + /*check all children except if current focus*/ + if (current_focus) return NULL; + gf_list_add(compositor->focus_ancestors, elt); + count = gf_node_list_get_count(child); + for (i=count; i>0; i--) { + /*get in the subtree*/ + n = gf_node_list_get_child( ((GF_ParentNode *)elt)->children, i-1); + n = set_focus(compositor, n, 0, 1); + if (n) return n; + } + } else { + /*check all children */ + gf_list_add(compositor->focus_ancestors, elt); + while (child) { + /*get in the subtree*/ + n = set_focus(compositor, child->node, 0, 0); + if (n) return n; + child = child->next; + } + } + if (use_node) { + gf_list_add(compositor->focus_use_stack, use_node); + gf_list_add(compositor->focus_use_stack, elt); + n = set_focus(compositor, use_node, 0, 0); + if (n) { + compositor->focus_used = elt; + return n; + } + gf_list_rem_last(compositor->focus_use_stack); + gf_list_rem_last(compositor->focus_use_stack); + } else if (anim_node) { + n = set_focus(compositor, anim_node, 0, 0); + if (n) return n; + } + + gf_list_rem_last(compositor->focus_ancestors); + + return NULL; +} + +static GF_Node *browse_parent_for_focus(GF_Compositor *compositor, GF_Node *elt, Bool prev_focus) +{ + u32 tag; + s32 idx = 0; + GF_ChildNodeItem *child; + GF_Node *n; + GF_Node *par; + + par = gf_list_last(compositor->focus_ancestors); + if (!par) return NULL; + + tag = gf_node_get_tag(par); + if (tag <= GF_NODE_FIRST_DOM_NODE_TAG) { + switch (tag) { + case TAG_MPEG4_Group: case TAG_X3D_Group: + case TAG_MPEG4_Transform: case TAG_X3D_Transform: + case TAG_MPEG4_Billboard: case TAG_X3D_Billboard: + case TAG_MPEG4_Collision: case TAG_X3D_Collision: + case TAG_MPEG4_LOD: case TAG_X3D_LOD: + case TAG_MPEG4_OrderedGroup: + case TAG_MPEG4_Transform2D: + case TAG_MPEG4_TransformMatrix2D: + case TAG_MPEG4_ColorTransform: + case TAG_MPEG4_Layer3D: + case TAG_MPEG4_Layer2D: + case TAG_MPEG4_Layout: + case TAG_MPEG4_PathLayout: + case TAG_MPEG4_Form: + case TAG_MPEG4_Anchor: case TAG_X3D_Anchor: + case TAG_MPEG4_CompositeTexture2D: case TAG_MPEG4_CompositeTexture3D: + child = ((GF_ParentNode*)par)->children; + break; + /*for all other node, locate parent*/ + default: + gf_list_rem_last(compositor->focus_ancestors); + return browse_parent_for_focus(compositor, par, prev_focus); + } + } else { + child = ((GF_ParentNode *)par)->children; + } + + /*locate element*/ + idx = gf_node_list_find_child(child, elt); + + /*use or animation*/ + if (idx<0) { + /*up one level*/ + gf_list_rem_last(compositor->focus_ancestors); +#ifndef GPAC_DISABLE_SVG + if (tag==TAG_SVG_use) { + gf_list_rem_last(compositor->focus_use_stack); + gf_list_rem_last(compositor->focus_use_stack); + if (compositor->focus_used == par) compositor->focus_used = NULL; + } +#endif + return browse_parent_for_focus(compositor, (GF_Node*)par, prev_focus); + } + + if (prev_focus) { + u32 i, count; + count = gf_node_list_get_count(child); + /*!! this may happen when walking up PROTO nodes !!*/ + for (i=idx; i>0; i--) { + n = gf_node_list_get_child(child, i-1); + /*get in the subtree*/ + n = set_focus(compositor, n, 0, 1); + if (n) return n; + } + } else { + /*!! this may happen when walking up PROTO nodes !!*/ + while (child) { + if (idx<0) { + /*get in the subtree*/ + n = set_focus(compositor, child->node, 0, 0); + if (n) return n; + } + idx--; + child = child->next; + } + } + /*up one level*/ + gf_list_rem_last(compositor->focus_ancestors); + return browse_parent_for_focus(compositor, (GF_Node*)par, prev_focus); +} + +u32 gf_sc_focus_switch_ring_ex(GF_Compositor *compositor, Bool move_prev, GF_Node *focus, Bool force_focus) +{ + Bool current_focus = 1; + Bool prev_uses_dom_events; + u32 ret = 0; + GF_List *cloned_use = NULL; + GF_Node *n, *prev, *prev_use; + + prev = compositor->focus_node; + prev_use = compositor->focus_used; + compositor->focus_text_type = 0; + prev_uses_dom_events = compositor->focus_uses_dom_events; + compositor->focus_uses_dom_events = 0; + + if (!compositor->focus_node) { + compositor->focus_node = gf_sg_get_root_node(compositor->scene); + gf_list_reset(compositor->focus_ancestors); + if (!compositor->focus_node) return 0; + current_focus = 0; + } + + if (compositor->focus_used) { + u32 i, count; + cloned_use = gf_list_new(); + count = gf_list_count(compositor->focus_use_stack); + for (i=0; ifocus_use_stack, i)); + } + } + + /*get focus in current doc order*/ + if (force_focus) { + gf_list_reset(compositor->focus_ancestors); + n = focus; + } else { + n = set_focus(compositor, compositor->focus_node, current_focus, move_prev); + if (!n) n = browse_parent_for_focus(compositor, compositor->focus_node, move_prev); + + if (!n) { + if (!prev) n = gf_sg_get_root_node(compositor->scene); + gf_list_reset(compositor->focus_ancestors); + } + } + + if (n && gf_node_get_tag(n)>=GF_NODE_FIRST_DOM_NODE_TAG) { + compositor->focus_uses_dom_events = 1; + } + compositor->focus_node = n; + + ret = 0; +#ifndef GPAC_DISABLE_SVG + if ((prev != compositor->focus_node) || (prev_use != compositor->focus_used)) { + GF_DOM_Event evt; + GF_Event ev; + /*the event is already handled, even though no listeners may be present*/ + ret = 1; + memset(&evt, 0, sizeof(GF_DOM_Event)); + memset(&ev, 0, sizeof(GF_Event)); + ev.type = GF_EVENT_KEYDOWN; + ev.key.key_code = move_prev ? GF_KEY_LEFT : GF_KEY_RIGHT; + + if (prev) { + if (prev_uses_dom_events) { + evt.bubbles = 1; + evt.type = GF_EVENT_FOCUSOUT; + gf_dom_event_fire_ex(prev, &evt, cloned_use); + } else { + exec_vrml_key_event(compositor, prev, &ev, 1); + } + } + if (compositor->focus_node) { + if (compositor->focus_uses_dom_events) { + evt.bubbles = 1; + evt.type = GF_EVENT_FOCUSIN; + gf_dom_event_fire_ex(compositor->focus_node, &evt, compositor->focus_use_stack); + } else { + exec_vrml_key_event(compositor, NULL, &ev, 0); + } + } + /*invalidate in case we draw focus rect*/ + gf_sc_invalidate(compositor, NULL); + } +#endif /*GPAC_DISABLE_SVG*/ + if (cloned_use) gf_list_del(cloned_use); + + if (hit_node_editable(compositor, 1)) { + compositor->text_selection = NULL; + exec_text_input(compositor, NULL); + } + return ret; +} + +u32 gf_sc_focus_switch_ring(GF_Compositor *compositor, Bool move_prev) +{ + return gf_sc_focus_switch_ring_ex(compositor, move_prev, NULL, 0); +} + +Bool gf_sc_execute_event(GF_Compositor *compositor, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children) +{ + /*filter mouse events and other events (key...)*/ + if (ev->type>GF_EVENT_MOUSEWHEEL) { + Bool ret = 0; + /*send text edit*/ + if (compositor->edited_text) { + exec_text_input(compositor, ev); + return 1; + } + /*FIXME - this is not working for mixed docs*/ + if (compositor->focus_uses_dom_events) + ret = exec_event_dom(compositor, ev); + else + ret = exec_vrml_key_event(compositor, compositor->focus_node, ev, 0); + + + if (ev->type==GF_EVENT_KEYDOWN) { + switch (ev->key.key_code) { + case GF_KEY_ENTER: + if (0&&compositor->focus_text_type) { + exec_text_input(compositor, NULL); + ret = 1; + } + break; + case GF_KEY_TAB: + ret += gf_sc_focus_switch_ring(compositor, (ev->key.flags & GF_KEY_MOD_SHIFT) ? 1 : 0); + break; + case GF_KEY_UP: + case GF_KEY_DOWN: + case GF_KEY_LEFT: + case GF_KEY_RIGHT: + ret += gf_sc_svg_focus_navigate(compositor, ev->key.key_code); + break; + } + } + return ret; + } + /*pick even, call visual handler*/ + return visual_execute_event(compositor->visual, tr_state, ev, children); +} + +Bool gf_sc_exec_event(GF_Compositor *compositor, GF_Event *evt) +{ + if (evt->type<=GF_EVENT_MOUSEWHEEL) { + if (compositor->visual->center_coords) { + evt->mouse.x = evt->mouse.x - compositor->display_width/2; + evt->mouse.y = compositor->display_height/2 - evt->mouse.y; + } + } + + /*process regular events except if navigation is grabbed*/ + if ( (compositor->navigation_state<2) && (compositor->interaction_level & GF_INTERACT_NORMAL) && gf_sc_execute_event(compositor, compositor->traverse_state, evt, NULL)) { + compositor->navigation_state = 0; + return 1; + } +#ifndef GPAC_DISABLE_3D + /*remember active layer on mouse click - may be NULL*/ + if ((evt->type==GF_EVENT_MOUSEDOWN) && (evt->mouse.button==GF_MOUSE_LEFT)) compositor->active_layer = compositor->traverse_state->layer3d; +#endif + /*process navigation events*/ + if (compositor->interaction_level & GF_INTERACT_NAVIGATION) return compositor_handle_navigation(compositor, evt); + return 0; +} + + diff --git a/src/compositor/font_engine.c b/src/compositor/font_engine.c new file mode 100644 index 0000000..6343ed7 --- /dev/null +++ b/src/compositor/font_engine.c @@ -0,0 +1,1422 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean Le Feuvre + * + * Copyright (c) ENST 2007-200X + * All rights reserved + * + * This file is part of GPAC / Scene Rendering sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include "visual_manager.h" +#include "nodes_stacks.h" + +#include "texturing.h" + +struct _gf_ft_mgr +{ + GF_FontReader *reader; + + GF_Font *font, *default_font; + GF_Path *line_path; + + u32 *id_buffer; + u32 id_buffer_size; +}; + + +GF_FontManager *gf_font_manager_new(GF_User *user) +{ + char *def_font = "SERIF"; + u32 i, count; + GF_FontManager *font_mgr; + GF_FontReader *ifce; + const char *opt; + + ifce = NULL; + opt = gf_cfg_get_key(user->config, "FontEngine", "FontReader"); + if (opt) { + ifce = (GF_FontReader *) gf_modules_load_interface_by_name(user->modules, opt, GF_FONT_READER_INTERFACE); + if (ifce && ifce->init_font_engine(ifce) != GF_OK) { + gf_modules_close_interface((GF_BaseInterface *)ifce); + ifce = NULL; + } + } + + if (!ifce) { + count = gf_modules_get_count(user->modules); + for (i=0; imodules, i, GF_FONT_READER_INTERFACE); + if (!ifce) continue; + + if (ifce->init_font_engine(ifce) != GF_OK) { + gf_modules_close_interface((GF_BaseInterface *)ifce); + ifce = NULL; + continue; + } + + gf_cfg_set_key(user->config, "FontEngine", "FontReader", ifce->module_name); + break; + } + } + GF_SAFEALLOC(font_mgr, GF_FontManager); + font_mgr->reader = ifce; + font_mgr->id_buffer_size = 20; + font_mgr->id_buffer = malloc(sizeof(u32)*font_mgr->id_buffer_size); + gf_font_manager_set_font(font_mgr, &def_font, 1, 0); + font_mgr->default_font = font_mgr->font; + + font_mgr->line_path= gf_path_new(); + gf_path_add_move_to(font_mgr->line_path, -FIX_ONE/2, FIX_ONE/2); + gf_path_add_line_to(font_mgr->line_path, FIX_ONE/2, FIX_ONE/2); + gf_path_add_line_to(font_mgr->line_path, FIX_ONE/2, -FIX_ONE/2); + gf_path_add_line_to(font_mgr->line_path, -FIX_ONE/2, -FIX_ONE/2); + gf_path_close(font_mgr->line_path); + return font_mgr; +} + +void gf_font_predestroy(GF_Font *font) +{ + if (font->spans) { + while (gf_list_count(font->spans)) { + GF_TextSpan *ts = gf_list_get(font->spans, 0); + gf_list_rem(font->spans, 0); + gf_node_dirty_set(ts->user, 0, 0); + ts->user=NULL; + } + gf_list_del(font->spans); + font->spans = NULL; + } +} + +void gf_font_del(GF_Font *font) +{ + gf_font_predestroy(font); + if (!font->get_glyphs) { + GF_Glyph *glyph; + glyph = font->glyph; + while (glyph) { + GF_Glyph *next = glyph->next; + gf_path_del(glyph->path); + free(glyph); + glyph = next; + } + } + free(font->name); + free(font); +} + + +void gf_font_manager_del(GF_FontManager *fm) +{ + GF_Font *font; + if (fm->reader) { + fm->reader->shutdown_font_engine(fm->reader); + gf_modules_close_interface((GF_BaseInterface *)fm->reader); + } + + font = fm->font; + while (font) { + GF_Font *next = font->next; + gf_font_del(font); + font = next; + } + free(fm->id_buffer); + gf_path_del(fm->line_path); + free(fm); +} + +GF_Err gf_font_manager_register_font(GF_FontManager *fm, GF_Font *font) +{ + if (fm->font) { + GF_Font *a_font = fm->font; + while (a_font->next) a_font = a_font->next; + a_font->next = font; + } else { + fm->font = font; + } + font->ft_mgr = fm; + if (!font->spans) font->spans = gf_list_new(); + return GF_OK; +} + +GF_Err gf_font_manager_unregister_font(GF_FontManager *fm, GF_Font *font) +{ + GF_Font *prev_font, *a_font; + + prev_font = NULL; + a_font = fm->font; + while (a_font) { + if (a_font==font) break; + prev_font = a_font; + a_font = a_font->next; + } + if (prev_font) { + prev_font->next = font->next; + } else { + fm->font = font->next; + } + gf_font_predestroy(font); + return GF_OK; +} + + + +GF_Font *gf_font_manager_set_font_ex(GF_FontManager *fm, char **alt_fonts, u32 nb_fonts, u32 styles, Bool check_only) +{ + u32 i; + GF_Err e; + Bool has_italic = (styles & GF_FONT_ITALIC) ? 1 : 0; + Bool has_smallcaps = (styles & GF_FONT_SMALLCAPS) ? 1 : 0; + GF_Font *the_font = NULL; + + for (i=0; ifont; + while (font) { + if ((check_only || !font->not_loaded) && font->name && !stricmp(font->name, alt_fonts[i])) { + s32 fw; + s32 w; + u32 diff; + Bool ft_has_smallcaps; + Bool ft_has_weight; + + if (check_only) return font; + + ft_has_weight = (font->styles & GF_FONT_WEIGHT_MASK) ? 1 : 0; + if (font->styles == styles) { + the_font = font; + break; + } + /*check we have the same font variant*/ + ft_has_smallcaps = (font->styles & GF_FONT_SMALLCAPS) ? 1 : 0; + if (ft_has_smallcaps != has_smallcaps) { + font = font->next; + continue; + } + /*check if we have an oblique/italic match*/ + if (has_italic) { + if (! (font->styles & (GF_FONT_OBLIQUE|GF_FONT_ITALIC))) { + font = font->next; + continue; + } + /*if italic force it*/ + if (font->styles & GF_FONT_ITALIC) best_font = font; + /*if oblic use it if no italic found*/ + else if (!best_font) best_font = font; + else { + font = font->next; + continue; + } + } + + /*compute min weight diff*/ + fw = font->styles>>10; + w = styles>>10; + diff = ABS(fw - w); + if (ft_has_weight) { + if (diffstyles & GF_FONT_STYLE_MASK) == (styles & GF_FONT_STYLE_MASK) ) { + weight_diff = diff; + best_font = font; + font = font->next; + continue; + } + } + } + font = font->next; + } + if (the_font) break; + if (fm->reader) { + e = fm->reader->set_font(fm->reader, alt_fonts[i], styles); + if (!e) { + GF_SAFEALLOC(the_font, GF_Font); + fm->reader->get_font_info(fm->reader, &the_font->name, &the_font->em_size, &the_font->ascent, &the_font->descent, &the_font->underline, &the_font->line_spacing, &the_font->max_advance_h, &the_font->max_advance_v); + the_font->styles = styles; + if (!the_font->name) the_font->name = strdup(alt_fonts[i]); + + if (fm->font) { + font = fm->font; + while (font->next) font = font->next; + font->next = the_font; + } else { + fm->font = the_font; + } + the_font->ft_mgr = fm; + return the_font; + } + } + if (best_font) { + the_font = best_font; + break; + } + } + + /*check for font alias*/ + if (the_font && the_font->get_alias) { + return the_font->get_alias(the_font->udta); + + } + + if (!the_font) { + if (check_only) return NULL; + the_font = fm->default_font; + } + /*embeded font*/ + if (the_font && !the_font->get_glyphs) + fm->reader->set_font(fm->reader, the_font->name, the_font->styles); + + return the_font; +} +GF_Font *gf_font_manager_set_font(GF_FontManager *fm, char **alt_fonts, u32 nb_fonts, u32 styles) +{ + return gf_font_manager_set_font_ex(fm, alt_fonts, nb_fonts, styles, 0); +} + +static GF_Glyph *gf_font_get_glyph(GF_FontManager *fm, GF_Font *font, u32 name) +{ + GF_Glyph *glyph = font->glyph; + while (glyph) { + if (glyph->ID==name) return glyph; + glyph = glyph->next; + } + /*load glyph*/ + + if (font->load_glyph) { + glyph = font->load_glyph(font->udta, name); + } else { + if (!fm->reader) return NULL; +// fm->reader->set_font(fm->reader, font->name, font->styles); + glyph = fm->reader->load_glyph(fm->reader, name); + } + if (!glyph) return NULL; + + if (!font->glyph) font->glyph = glyph; + else { + GF_Glyph *a_glyph = font->glyph; + while (a_glyph->next) a_glyph = a_glyph->next; + a_glyph->next = glyph; + } + /*space character - this may need adjustment for other empty glyphs*/ + if (!glyph->path->n_points) { + glyph->path->bbox.x = 0; + glyph->path->bbox.width = INT2FIX(font->max_advance_h); + glyph->path->bbox.y = INT2FIX(font->ascent); + glyph->path->bbox.height = INT2FIX(font->ascent - font->descent); + } + return glyph; +} + + +GF_TextSpan *gf_font_manager_create_span(GF_FontManager *fm, GF_Font *font, char *text, Fixed font_size, Bool needs_x_offset, Bool needs_y_offset, Bool needs_rotate, const char *xml_lang, Bool fliped_text, u32 styles, GF_Node *user) +{ + GF_Err e; + Bool is_rtl; + u32 len, i; + GF_TextSpan *span; + + if (!user || !strlen(text)) return NULL; + + len = fm->id_buffer_size; + if (font->get_glyphs) + e = font->get_glyphs(font->udta, text, fm->id_buffer, &len, xml_lang, &is_rtl); + else + e = fm->reader->get_glyphs(fm->reader, text, fm->id_buffer, &len, xml_lang, &is_rtl); + + if (e==GF_BUFFER_TOO_SMALL) { + fm->id_buffer_size = len; + fm->id_buffer = realloc(fm->id_buffer, sizeof(u32) * len); + if (!fm->id_buffer) return NULL; + + if (font->get_glyphs) + e = font->get_glyphs(font->udta, text, fm->id_buffer, &len, xml_lang, &is_rtl); + else + e = fm->reader->get_glyphs(fm->reader, text, fm->id_buffer, &len, xml_lang, &is_rtl); + } + if (e) return NULL; + + GF_SAFEALLOC(span, GF_TextSpan); + span->font = font; + span->font_size = font_size; + if (font->em_size) + span->font_scale = font_size / font->em_size; + span->x_scale = span->y_scale = FIX_ONE; +// span->lang = xml_lang; + if (fliped_text) span->flags |= GF_TEXT_SPAN_FLIP; + if (styles & GF_FONT_UNDERLINED) span->flags |= GF_TEXT_SPAN_UNDERLINE; + span->nb_glyphs = len; + span->glyphs = malloc(sizeof(void *)*len); + if (needs_x_offset) { + span->dx = malloc(sizeof(Fixed)*len); + memset(span->dx, 0, sizeof(Fixed)*len); + } + if (needs_y_offset) { + span->dy = malloc(sizeof(Fixed)*len); + memset(span->dy, 0, sizeof(Fixed)*len); + } + if (needs_rotate) { + span->rot = malloc(sizeof(Fixed)*len); + memset(span->rot, 0, sizeof(Fixed)*len); + } + + for (i=0; iglyphs[i] = gf_font_get_glyph(fm, font, fm->id_buffer[i]); + } + span->user = user; + if (span->font->spans) + gf_list_add(font->spans, span); + if (is_rtl) span->flags |= GF_TEXT_SPAN_RIGHT_TO_LEFT; + return span; +} + + + +typedef struct _span_internal +{ + /*zoom when texture was computed*/ + Fixed last_zoom; + /*texture handler for bitmap text*/ + GF_TextureHandler *txh; + /*texture path (rectangle)*/ + GF_Path *path; + +#ifndef GPAC_DISABLE_3D + /*span mesh (built out of the # glyphs)*/ + GF_Mesh *mesh; + /*span texture mesh (rectangle)*/ + GF_Mesh *tx_mesh; + /*span outline*/ + GF_Mesh *outline; +#endif +} GF_SpanExtensions; + + +void gf_font_manager_delete_span(GF_FontManager *fm, GF_TextSpan *span) +{ + if (span->user && span->font->spans) gf_list_del_item(span->font->spans, span); + + free(span->glyphs); + if (span->dx) free(span->dx); + if (span->dy) free(span->dy); + if (span->rot) free(span->rot); + + if (span->ext) { + if (span->ext->path) gf_path_del(span->ext->path); +#ifndef GPAC_DISABLE_3D + if (span->ext->mesh) mesh_free(span->ext->mesh); + if (span->ext->tx_mesh) mesh_free(span->ext->tx_mesh); + if (span->ext->outline) mesh_free(span->ext->outline); +#endif + if (span->ext->txh) { + gf_sc_texture_destroy(span->ext->txh); + if (span->ext->txh->data) free(span->ext->txh->data); + free(span->ext->txh); + } + free(span->ext); + } + free(span); +} + +void gf_font_manager_refresh_span_bounds(GF_TextSpan *span) +{ + u32 i; + Fixed descent, ascent, bline; + Fixed min_x, min_y, width, max_y; + + if (!span->nb_glyphs) { + span->bounds.width = span->bounds.height = 0; + return; + } + descent = 0; + if (span->font->descent<0) descent = -span->font_scale * span->font->descent; + ascent = span->font->ascent * span->font_scale; + + /*if fliped text (SVG), the min_y is at the ascent side*/ + if (span->flags & GF_TEXT_SPAN_FLIP) { + Fixed tmp = ascent; + ascent = descent; + descent = tmp; + } + + bline = span->font->baseline * span->font_scale; + + width = (span->flags & GF_TEXT_SPAN_HORIZONTAL) ? 0 : span->font->max_advance_h*span->font_scale; + + min_x = span->dx ? FIX_MAX : span->off_x; + min_y = span->dy ? FIX_MAX : span->off_y - descent; + max_y = span->dy ? -FIX_MAX : span->off_y + ascent; + + /*adjust start_x for first glyph*/ + if (span->glyphs[0] && span->glyphs[0]->path) { + min_x += gf_mulfix(span->glyphs[0]->path->bbox.x, span->font_scale); + } + span->bounds = gf_rect_center(0, 0); + + for (i=0;inb_glyphs; i++) { + Fixed g_width; + GF_Rect rc; + + /*compute glyph size*/ + if (!span->glyphs[i]) g_width = span->font->max_advance_h * span->font_scale; + /*if last glyph of the span, increase by width only*/ + else if (i+1==span->nb_glyphs) g_width = span->glyphs[i]->width * span->font_scale; + /*otherwise increase by the horizontal advance*/ + else g_width = span->glyphs[i]->horiz_advance * span->font_scale; + + if (span->dy) { + if (span->dy[i] - descent < min_y) min_y = span->dy[i] - descent; + if (span->dy[i] + ascent > max_y) max_y = span->dy[i] + ascent; + } + else if (span->glyphs[i]) { + Fixed size = span->glyphs[i]->height * span->font_scale; + if (size > max_y-min_y) max_y = size + min_y; + } + + if (span->dx) { + rc.x = span->dx[i]; + } else { + rc.x = min_x; + if (span->flags & GF_TEXT_SPAN_HORIZONTAL) + min_x += g_width; + } + rc.y = span->dy ? span->dy[i] + ascent : max_y; + rc.width = g_width; + rc.height = max_y - min_y; + + if (span->rot) { + GF_Matrix2D tr; + gf_mx2d_init(tr); + gf_mx2d_add_rotation(&tr, rc.x, rc.y - ascent - bline, span->rot[i]); + gf_mx2d_apply_rect(&tr, &rc); + } + gf_rect_union(&span->bounds, &rc); + } +} + + + + +GF_Path *gf_font_span_create_path(GF_TextSpan *span) +{ + u32 i; + GF_Matrix2D mat; + Fixed dx, dy; + GF_Path *path = gf_path_new(); + + gf_mx2d_init(mat); + mat.m[0] = gf_mulfix(span->font_scale, span->x_scale); + mat.m[4] = gf_mulfix(span->font_scale, span->y_scale); + if (span->flags & GF_TEXT_SPAN_FLIP) gf_mx2d_add_scale(&mat, FIX_ONE, -FIX_ONE); + + dx = gf_divfix(span->off_x, mat.m[0]); + dy = gf_divfix(span->off_y, mat.m[4]); + + for (i=0; inb_glyphs; i++) { + if (!span->glyphs[i]) { + if (span->flags & GF_TEXT_SPAN_HORIZONTAL) { + dx += INT2FIX(span->font->max_advance_h); + } else { + dy -= INT2FIX(span->font->max_advance_v); + } + } else { + if (span->dx) dx = gf_divfix(span->dx[i], mat.m[0]); + if (span->dy) dy = gf_divfix(span->dy[i], mat.m[4]); + + + if (span->glyphs[i]->utf_name != ' ') { + GF_Matrix2D mx; + gf_mx2d_init(mx); + if (span->rot) { + gf_mx2d_add_rotation(&mx, 0, 0, -span->rot[i]); + } + gf_mx2d_add_translation(&mx, dx, dy); + gf_path_add_subpath(path, span->glyphs[i]->path, &mx); + } + + if (span->flags & GF_TEXT_SPAN_HORIZONTAL) { + dx += INT2FIX(span->glyphs[i]->horiz_advance); + } else { + dy -= INT2FIX(span->glyphs[i]->vert_advance); + } + } + } + + for (i=0; in_points; i++) { + gf_mx2d_apply_point(&mat, &path->points[i]); + } + return path; +} + +static void span_alloc_extensions(GF_TextSpan *span) +{ + if (span->ext) return; + GF_SAFEALLOC(span->ext, GF_SpanExtensions); +} + +#ifndef GPAC_DISABLE_3D +static void span_build_mesh(GF_TextSpan *span) +{ + span_alloc_extensions(span); + span->ext->tx_mesh = new_mesh(); + mesh_set_vertex(span->ext->tx_mesh, span->bounds.x, span->bounds.y-span->bounds.height, 0, 0, 0, FIX_ONE, 0, FIX_ONE); + mesh_set_vertex(span->ext->tx_mesh, span->bounds.x+span->bounds.width, span->bounds.y-span->bounds.height, 0, 0, 0, FIX_ONE, FIX_ONE, FIX_ONE); + mesh_set_vertex(span->ext->tx_mesh, span->bounds.x+span->bounds.width, span->bounds.y, 0, 0, 0, FIX_ONE, FIX_ONE, 0); + mesh_set_vertex(span->ext->tx_mesh, span->bounds.x, span->bounds.y, 0, 0, 0, FIX_ONE, 0, 0); + mesh_set_triangle(span->ext->tx_mesh, 0, 1, 2); + mesh_set_triangle(span->ext->tx_mesh, 0, 2, 3); + span->ext->tx_mesh->flags |= MESH_IS_2D; + span->ext->tx_mesh->mesh_type = MESH_TRIANGLES; + mesh_update_bounds(span->ext->tx_mesh); +} +#endif + +/*don't build too large textures*/ +#define MAX_TX_SIZE 512 +/*and don't build too small ones otherwise result is as crap as non-textured*/ +#define MIN_TX_SIZE 32 + +static Bool span_setup_texture(GF_Compositor *compositor, GF_TextSpan *span, Bool for_3d, GF_TraverseState *tr_state) +{ + GF_Path *span_path; + GF_Rect bounds; + Fixed cx, cy, sx, sy, max, min; + u32 tw, th; + GF_Matrix2D mx; + GF_STENCIL stencil, brush; + GF_SURFACE surface; + u32 width, height; + Fixed scale; + GF_Raster2D *raster = compositor->rasterizer; + + span_alloc_extensions(span); + + /*something failed*/ + if (span->ext->txh && !span->ext->txh->data) return 0; + + if (span->ext->txh && span->ext->txh->data) { + if (span->ext->last_zoom == compositor->zoom) { + +#ifndef GPAC_DISABLE_3D + if (for_3d && !span->ext->tx_mesh) span_build_mesh(span); +#endif + return 1; + } + } + span->ext->last_zoom = compositor->zoom; + + bounds = span->bounds; + + /*check not too big, but also not really small (meter metrics)*/ + max = INT2FIX(MAX_TX_SIZE); + min = INT2FIX(MIN_TX_SIZE); + scale = compositor->zoom; + if (!tr_state->pixel_metrics) { + scale = gf_mulfix(scale, tr_state->min_hsize); + } + if ((gf_mulfix(scale, bounds.width)>max) || (gf_mulfix(scale, bounds.height)>max)) { + scale = MIN(gf_divfix(max, bounds.width), gf_divfix(max, bounds.height)); + } + else if ((gf_mulfix(scale, bounds.width)=MAX_TX_SIZE) break; + width *=2; + } + th = FIX2INT( gf_ceil(gf_mulfix(scale, bounds.height)) ); + height = MIN_TX_SIZE; + while (height=MAX_TX_SIZE) break; + } + /*and get scaling*/ + sx = gf_divfix( INT2FIX(width), bounds.width); + sy = gf_divfix( INT2FIX(height), bounds.height); + + if (span->ext->txh && (width == span->ext->txh->width) && (height==span->ext->txh->height)) return 1; + + if (span->ext->path) gf_path_del(span->ext->path); + span->ext->path = NULL; +#ifndef GPAC_DISABLE_3D + if (span->ext->tx_mesh) mesh_free(span->ext->tx_mesh); + span->ext->tx_mesh = NULL; +#endif + + if (span->ext->txh) { + gf_sc_texture_destroy(span->ext->txh); + if (span->ext->txh->data) free(span->ext->txh->data); + free(span->ext->txh); + } + GF_SAFEALLOC(span->ext->txh, GF_TextureHandler); + gf_sc_texture_setup(span->ext->txh, compositor, NULL); + gf_sc_texture_allocate(span->ext->txh); + stencil = gf_sc_texture_get_stencil(span->ext->txh); + if (!stencil) stencil = raster->stencil_new(raster, GF_STENCIL_TEXTURE); + + /*FIXME - make it work with alphagrey...*/ + span->ext->txh->width = width; + span->ext->txh->height = height; + span->ext->txh->stride = 4*width; + span->ext->txh->pixelformat = GF_PIXEL_RGBA; + span->ext->txh->transparent = 1; + span->ext->txh->flags |= GF_SR_TEXTURE_NO_GL_FLIP; + + surface = raster->surface_new(raster, 1); + if (!surface) { + gf_sc_texture_release(span->ext->txh); + return 0; + } + span->ext->txh->data = (char *) malloc(sizeof(char)*span->ext->txh->stride*span->ext->txh->height); + memset(span->ext->txh->data, 0, sizeof(char)*span->ext->txh->stride*span->ext->txh->height); + + raster->stencil_set_texture(stencil, span->ext->txh->data, span->ext->txh->width, span->ext->txh->height, span->ext->txh->stride, span->ext->txh->pixelformat, span->ext->txh->pixelformat, 1); + raster->surface_attach_to_texture(surface, stencil); + + brush = raster->stencil_new(raster, GF_STENCIL_SOLID); + raster->stencil_set_brush_color(brush, 0xFF000000); + + cx = bounds.x + bounds.width/2; + cy = bounds.y - bounds.height/2; + + gf_mx2d_init(mx); + gf_mx2d_add_translation(&mx, -cx, -cy); + gf_mx2d_add_scale(&mx, sx, sy); +// gf_mx2d_add_scale(&mx, 99*FIX_ONE/100, 99*FIX_ONE/100); + + raster->surface_set_matrix(surface, &mx); + raster->surface_set_raster_level(surface, GF_RASTER_HIGH_QUALITY); + span_path = gf_font_span_create_path(span); + raster->surface_set_path(surface, span_path); + + raster->surface_fill(surface, brush); + raster->stencil_delete(brush); + raster->surface_delete(surface); + gf_path_del(span_path); + + if (span->font->baseline) { + Fixed dy = gf_mulfix(span->font->baseline, span->font_scale); + bounds.y += dy; + span->bounds.y += dy; + } + span->ext->path = gf_path_new(); + gf_path_add_move_to(span->ext->path, bounds.x, bounds.y-bounds.height); + gf_path_add_line_to(span->ext->path, bounds.x+bounds.width, bounds.y-bounds.height); + gf_path_add_line_to(span->ext->path, bounds.x+bounds.width, bounds.y); + gf_path_add_line_to(span->ext->path, bounds.x, bounds.y); + gf_path_close(span->ext->path); + + gf_sc_texture_set_stencil(span->ext->txh, stencil); + gf_sc_texture_set_data(span->ext->txh); + +#ifndef GPAC_DISABLE_3D + gf_sc_texture_set_blend_mode(span->ext->txh, TX_BLEND); + if (for_3d) span_build_mesh(span); +#endif + return 1; +} + + +#ifndef GPAC_DISABLE_3D + +static void span_fill_3d(GF_TextSpan *span, GF_TraverseState *tr_state) +{ + span_alloc_extensions(span); + if (!span->ext->mesh) { + GF_Path *path = gf_font_span_create_path(span); + span->ext->mesh = new_mesh(); + mesh_from_path(span->ext->mesh, path); + gf_path_del(path); + } + + visual_3d_mesh_paint(tr_state, span->ext->mesh); +} + +static void span_strike_3d(GF_TextSpan *span, GF_TraverseState *tr_state, DrawAspect2D *asp, Bool vect_outline) +{ + span_alloc_extensions(span); + if (!span->ext->outline) { + GF_Path *path = gf_font_span_create_path(span); + span->ext->outline = new_mesh(); +#ifndef GPAC_USE_OGL_ES + if (vect_outline) { + GF_Path *outline = gf_path_get_outline(path, asp->pen_props); + TesselatePath(span->ext->outline, outline, asp->line_texture ? 2 : 1); + gf_path_del(outline); + } else { + mesh_get_outline(span->ext->outline, path); + } +#else + /*VECTORIAL TEXT OUTLINE NOT SUPPORTED ON OGL-ES AT CURRENT TIME*/ + vect_outline = 0; + mesh_get_outline(span->ext->outline, path); +#endif + gf_path_del(path); + } + if (vect_outline) { + visual_3d_mesh_paint(tr_state, span->ext->outline); + } else { + visual_3d_mesh_strike(tr_state, span->ext->outline, asp->pen_props.width, asp->line_scale, asp->pen_props.dash); + } +} + + +void gf_font_spans_draw_3d(GF_List *spans, GF_TraverseState *tr_state, DrawAspect2D *asp, u32 text_hl, Bool force_texturing) +{ + u32 i; + SFColorRGBA hl_color; + GF_TextSpan *span; + Bool fill_2d, vect_outline, can_texture_text; + GF_Compositor *compositor = (GF_Compositor*)tr_state->visual->compositor; + + vect_outline = !compositor->raster_outlines; + + visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 0); + + fill_2d = 0; + if (!asp) { + if (!visual_3d_setup_appearance(tr_state)) return; + } else { + fill_2d = (asp->fill_color) ? 1 : 0; + } + memset(&hl_color, 0, sizeof(SFColorRGBA)); + + if (text_hl && (fill_2d || !asp) ) { + /*reverse video: highlighting uses the text color, and text color is inverted (except alpha channel) + the ideal impl would be to use the background color for the text, but since the text may be + displayed over anything non uniform this would require clipping the highlight rect with the text + which is too onerous (and not supported anyway) */ + if (text_hl == 0x00FFFFFF) { + if (!asp) { + if (tr_state->appear) { + SFColor c, rc; + c = ((M_Material *) ((M_Appearance *) tr_state->appear)->material)->diffuseColor; + hl_color = gf_sg_sfcolor_to_rgba(c); + hl_color.alpha = ((M_Material *) ((M_Appearance *) tr_state->appear)->material)->transparency; + /*invert diffuse color and resetup*/ + rc.red = FIX_ONE - c.red; + rc.green = FIX_ONE - c.green; + rc.blue = FIX_ONE - c.blue; + ((M_Material *) ((M_Appearance *) tr_state->appear)->material)->diffuseColor = rc; + visual_3d_setup_appearance(tr_state); + ((M_Material *) ((M_Appearance *) tr_state->appear)->material)->diffuseColor = c; + } else { + hl_color.red = hl_color.green = hl_color.blue = 0; + hl_color.alpha = FIX_ONE; + } + } else { + hl_color.alpha = FIX_ONE; + hl_color.red = INT2FIX( GF_COL_R(asp->fill_color) ) / 255; + hl_color.green = INT2FIX( GF_COL_G(asp->fill_color) ) / 255; + hl_color.blue = INT2FIX( GF_COL_B(asp->fill_color) ) / 255; + if (GF_COL_A(asp->fill_color) ) { + u8 r = GF_COL_R(asp->fill_color); + u8 g = GF_COL_G(asp->fill_color); + u8 b = GF_COL_B(asp->fill_color); + asp->fill_color = GF_COL_ARGB(GF_COL_A(asp->fill_color), 255-r, 255-g, 255-b); + } + } + } else { + hl_color.red = INT2FIX(GF_COL_R(text_hl)) / 255; + hl_color.green = INT2FIX(GF_COL_G(text_hl)) / 255; + hl_color.blue = INT2FIX(GF_COL_B(text_hl)) / 255; + hl_color.alpha = INT2FIX(GF_COL_A(text_hl)) / 255; + } + if (asp && !asp->fill_color) text_hl = 0; + } + + /*setup texture*/ + visual_3d_setup_texture(tr_state, FIX_ONE); + can_texture_text = 0; + if (fill_2d || !asp) { + /*check if we can use text texturing*/ + if (force_texturing || (compositor->texture_text_mode != GF_TEXTURE_TEXT_NEVER) ) { + if (fill_2d && asp->pen_props.width) { + can_texture_text = 0; + } else { + can_texture_text = tr_state->mesh_num_textures ? 0 : 1; + } + } + } + + visual_3d_enable_antialias(tr_state->visual, compositor->antiAlias); + if (fill_2d || !asp || tr_state->mesh_num_textures) { + if (fill_2d) visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color); + + i = tr_state->text_split_idx ? tr_state->text_split_idx-1 : 0; + while ((span = (GF_TextSpan *)gf_list_enum(spans, &i))) { + if (text_hl) { + visual_3d_fill_rect(tr_state->visual, span->bounds, hl_color); + if (fill_2d) + visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color); + else + visual_3d_setup_appearance(tr_state); + } + + if (can_texture_text && span_setup_texture(tr_state->visual->compositor, span, 1, tr_state)) { + tr_state->mesh_num_textures = 1; + gf_sc_texture_enable(span->ext->txh, NULL); + visual_3d_mesh_paint(tr_state, span->ext->tx_mesh); + gf_sc_texture_disable(span->ext->txh); + } else { + span_fill_3d(span, tr_state); + } + + if (tr_state->text_split_idx) break; + } + + /*reset texturing in case of line texture*/ + if (!asp) visual_3d_disable_texture(tr_state); + } + + visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 0); + + if (asp && asp->pen_props.width) { + if (!asp->line_scale) { + drawable_compute_line_scale(tr_state, asp); + } + asp->pen_props.width = gf_divfix(asp->pen_props.width, asp->line_scale); + visual_3d_set_2d_strike(tr_state, asp); + + if (tr_state->text_split_idx) { + span = (GF_TextSpan *)gf_list_get(spans, tr_state->text_split_idx-1); + span_strike_3d(span, tr_state, asp, vect_outline); + } else { + i=0; + while ((span = (GF_TextSpan *)gf_list_enum(spans, &i))) { + span_strike_3d(span, tr_state, asp, vect_outline); + } + } + } +} + + +#endif + + + +static void gf_font_span_draw_2d(GF_TraverseState *tr_state, GF_TextSpan *span, DrawableContext *ctx, GF_Rect *bounds) +{ + u32 flags, i; + Bool flip_text; + Fixed dx, dy, sx, sy, lscale, bline; + Bool needs_texture = (ctx->aspect.fill_texture || ctx->aspect.line_texture) ? 1 : 0; + GF_Matrix2D mx, tx; + + gf_mx2d_copy(mx, ctx->transform); + + flags = ctx->flags; + dx = span->off_x; + dy = span->off_y; + sx = gf_mulfix(span->font_scale, span->x_scale); + sy = gf_mulfix(span->font_scale, span->y_scale); + + flip_text = (ctx->flags & CTX_FLIPED_COORDS) ? tr_state->visual->center_coords : !tr_state->visual->center_coords; + + bline = span->font->baseline*span->font_scale; + lscale = ctx->aspect.line_scale; + ctx->aspect.line_scale = gf_divfix(ctx->aspect.line_scale, span->font_scale); + + for (i=0; inb_glyphs; i++) { + if (!span->glyphs[i]) { + if (span->flags & GF_TEXT_SPAN_HORIZONTAL) { + dx += sx * span->font->max_advance_h; + } else { + dy -= sy * span->font->max_advance_v; + } + continue; + } + ctx->transform.m[0] = sx; + ctx->transform.m[4] = flip_text ? -sy : sy; + ctx->transform.m[1] = ctx->transform.m[3] = 0; + ctx->transform.m[2] = span->dx ? span->dx[i] : dx; + ctx->transform.m[5] = span->dy ? span->dy[i] : dy; + ctx->transform.m[5] += bline; + if (span->rot) { + gf_mx2d_add_rotation(&ctx->transform, ctx->transform.m[2], ctx->transform.m[5], span->rot[i]); + } + + gf_mx2d_add_matrix(&ctx->transform, &mx); + + if (needs_texture) { + tx.m[0] = sx; + tx.m[4] = sy; + tx.m[1] = tx.m[3] = 0; + tx.m[2] = span->dx ? span->dx[i] : dx; + tx.m[5] = span->dy ? span->dy[i] : dy; + tx.m[5] += bline; + if (span->rot) { + gf_mx2d_add_rotation(&tx, tx.m[2], tx.m[5], span->rot[i]); + } + gf_mx2d_inverse(&tx); + + visual_2d_texture_path_extended(tr_state->visual, span->glyphs[i]->path, ctx->aspect.fill_texture, ctx, bounds ? bounds : &span->bounds, &tx, tr_state); + visual_2d_draw_path_extended(tr_state->visual, span->glyphs[i]->path, ctx, NULL, NULL, tr_state, bounds ? bounds : &span->bounds, &tx); + } else { + visual_2d_draw_path(tr_state->visual, span->glyphs[i]->path, ctx, NULL, NULL, tr_state); + } + ctx->flags = flags; + + if (span->flags & GF_TEXT_SPAN_HORIZONTAL) { + dx += sx * span->glyphs[i]->horiz_advance; + } else { + dy -= sy * span->glyphs[i]->vert_advance; + } + } + gf_mx2d_copy(ctx->transform, mx); + ctx->aspect.line_scale = lscale; +} + +void gf_font_underline_span(GF_TraverseState *tr_state, GF_TextSpan *span, DrawableContext *ctx) +{ + GF_Matrix2D mx, m; + u32 col; + Fixed sx, width, diff; + if (span->dx || span->dy) return; + + gf_mx2d_copy(mx, ctx->transform); + sx = gf_mulfix(span->font_scale, span->x_scale); + diff = span->font_scale * span->font->underline; + if (span->flags & GF_TEXT_SPAN_FLIP) + diff = sx * (span->font->descent - span->font->underline); + else + diff = sx * (- span->font->ascent + span->font->underline); + + gf_mx2d_init(m); + gf_mx2d_add_scale(&m, span->bounds.width, FIX_ONE); + gf_mx2d_add_translation(&m, span->bounds.x + span->bounds.width / 2, span->bounds.y+diff); + gf_mx2d_pre_multiply(&ctx->transform, &m); + + col = ctx->aspect.fill_color; + width = ctx->aspect.pen_props.width; + ctx->aspect.pen_props.width = 0; + ctx->flags &= ~CTX_PATH_FILLED; + if (span->anchor) ctx->aspect.fill_color = 0xFF0000FF; + + visual_2d_draw_path(tr_state->visual, span->font->ft_mgr->line_path, ctx, NULL, NULL, tr_state); + ctx->aspect.fill_color = col; + + gf_mx2d_copy(ctx->transform, mx); + ctx->aspect.pen_props.width = width; +} + +#if 0 +static u32 col_reverse_video(u32 col) +{ + u32 a, r, g, b; + a = GF_COL_A(col); + r = GF_COL_R(col); + g = GF_COL_G(col); + b = GF_COL_B(col); + return GF_COL_ARGB(a, 255-r, 255-g, 255-b); +} +#endif + +static GF_Rect font_get_sel_rect(GF_TraverseState *tr_state) +{ + GF_Vec s, e; + GF_Rect rc; + GF_Compositor *compositor = tr_state->visual->compositor; + + e.x = compositor->end_sel.x; + e.y = compositor->end_sel.y; + e.z = 0; + gf_mx_apply_vec(&compositor->hit_local_to_world, &e); + + s.x = compositor->start_sel.x; + s.y = compositor->start_sel.y; + s.z = 0; + gf_mx_apply_vec(&compositor->hit_local_to_world, &s); + + if (s.x > e.x) { + rc.x = e.x; + rc.width = s.x - e.x; + } else { + rc.x = s.x; + rc.width = e.x - s.x; + } + if (s.y > e.y) { + rc.y = s.y; + rc.height = s.y - e.y; + } else { + rc.y = e.y; + rc.height = e.y - s.y; + } + if (!rc.height) rc.height = FIX_ONE; + return rc; +} + +static void gf_font_spans_select(GF_TextSpan *span, GF_TraverseState *tr_state, DrawableContext *ctx, Bool has_more_spans, Bool first_span, GF_Rect *rc) +{ + GF_Matrix2D mx; + u32 flags, i, color; + Bool has_selection = 0; + Fixed dx, dy, sx, sy, width, ascent, descent; + GF_Compositor *compositor = tr_state->visual->compositor; + + if (first_span) rc->width = 0; + + if (!(span->flags & GF_TEXT_SPAN_SELECTED) ) return; + + + dx = gf_mulfix(span->off_x, span->x_scale); + dy = gf_mulfix(span->off_y, span->y_scale); + sx = gf_mulfix(span->font_scale, span->x_scale); + sy = gf_mulfix(span->font_scale, span->y_scale); + ascent = span->font->ascent*sy; + descent = span->font->descent*sy; + + width = 0; + flags = 0; + if (ctx) { + width = ctx->aspect.pen_props.width; + ctx->aspect.pen_props.width = 0; + flags = ctx->flags; + gf_mx2d_copy(mx, ctx->transform); + } + + /*compute sel rectangle*/ + if (!rc->width) { + *rc = font_get_sel_rect(tr_state); + } + + color = compositor->text_sel_color; + + for (i=0; inb_glyphs; i++) { + GF_Rect g_rc; + Bool end_of_line = 0; + Fixed advance = sx * span->glyphs[i]->horiz_advance; + if (span->dx) dx = span->dx[i]; + if (span->dy) dy = span->dy[i]; + if (dx + advance/2 < rc->x) { + dx += advance; + continue; + } + + g_rc.height = ascent-descent; + + if (dx + advance/2 > rc->x + rc->width) { + u32 j; + Bool has_several_lines = 0; + if (!span->dy) break; + if (!has_selection) continue; + + for (j=i+1; jnb_glyphs; j++) { + if (span->dy[j] > span->dy[i]) { + if (span->flags & GF_TEXT_SPAN_FLIP) + g_rc.y = span->dy[j]-descent; + else + g_rc.y = span->dy[j]+ascent; + g_rc.x = span->bounds.x; + g_rc.width = span->bounds.width; + if (gf_rect_overlaps(g_rc, *rc)) { + has_several_lines =1; + break; + } + } + } + if (has_more_spans && dyy) has_several_lines =1; + + if (!has_several_lines) break; + end_of_line = 1; + /*move selection rect to include start of line - FIXME this depends on ltr/rtl*/ + rc->width = rc->width+rc->x - span->bounds.x; + rc->x = span->bounds.x; + } + + if (span->flags & GF_TEXT_SPAN_FLIP) + g_rc.y = dy-descent; + else + g_rc.y = dy+ascent; + g_rc.x = dx; + g_rc.width = advance; + + + if (!end_of_line && !gf_rect_overlaps(g_rc, *rc)) + continue; +/* + if (dy < rc.y-rc.height) { + if (!span->dx) break; + continue; + } +*/ + has_selection = 1; + if (ctx) { + if (span->rot) { + gf_mx2d_init(ctx->transform); + gf_mx2d_add_rotation(&ctx->transform, g_rc.x , g_rc.y, span->rot[i]); + gf_mx2d_add_matrix(&ctx->transform, &mx); + } + visual_2d_fill_rect(tr_state->visual, ctx, &g_rc, color, 0, tr_state); + ctx->flags = flags; + compositor->store_text_state = GF_SC_TSEL_ACTIVE; + } else { + if (!compositor->sel_buffer_alloc || compositor->sel_buffer_len == compositor->sel_buffer_alloc) { + if (!compositor->sel_buffer_alloc) compositor->sel_buffer_alloc ++; + compositor->sel_buffer_alloc = 2*compositor->sel_buffer_alloc; + compositor->sel_buffer = realloc(compositor->sel_buffer, sizeof(u16)*compositor->sel_buffer_alloc); + } + compositor->sel_buffer[compositor->sel_buffer_len] = span->glyphs[i]->utf_name; + compositor->sel_buffer_len++; + } + + dx += advance; + } + if (ctx) { + gf_mx2d_copy(ctx->transform, mx); + ctx->aspect.pen_props.width = width; + } + if (has_selection && has_more_spans && dyy) { + rc->width = rc->width+rc->x - span->bounds.x; + rc->x = span->bounds.x; + } +} + +void gf_font_spans_get_selection(GF_Node *node, GF_List *spans, GF_TraverseState *tr_state) +{ + GF_TextSpan *span; + u32 i, count; + GF_Rect rc; + count = gf_list_count(spans); + for (i=0; ivisual->compositor; + u32 i, count; + GF_TextSpan *span; + DrawableContext *ctx = tr_state->ctx; + GF_Rect rc; + + use_texture_text = 0; + if (force_texture_text || (compositor->texture_text_mode==GF_TEXTURE_TEXT_ALWAYS) ) { + use_texture_text = !ctx->aspect.fill_texture && !ctx->aspect.pen_props.width; + } + + is_rv = 0; + if (hl_color) { + /*reverse video: highlighting uses the text color, and text color is inverted (except alpha channel) + the ideal impl would be to use the background color for the text, but since the text may be + displayed over anything non uniform this would require clipping the highlight rect with the text + which is too onerous (and not supported anyway) */ + if (hl_color==0x00FFFFFF) { + u32 a, r, g, b; + hl_color = tr_state->ctx->aspect.fill_color; + + a = GF_COL_A(tr_state->ctx->aspect.fill_color); + if (a) { + r = GF_COL_R(tr_state->ctx->aspect.fill_color); + g = GF_COL_G(tr_state->ctx->aspect.fill_color); + b = GF_COL_B(tr_state->ctx->aspect.fill_color); + tr_state->ctx->aspect.fill_color = GF_COL_ARGB(a, 255-r, 255-g, 255-b); + } + is_rv = 1; + } + if (GF_COL_A(hl_color) == 0) hl_color = 0; + } + + count = gf_list_count(spans); + i=ctx->sub_path_index ? ctx->sub_path_index-1 : 0; + for(;itext_selection) + gf_font_spans_select(span, tr_state, ctx, (i+1visual, ctx, &span->bounds, hl_color, 0, tr_state); + + if (use_texture_text && span_setup_texture(compositor, span, 0, tr_state)) { + visual_2d_texture_path_text(tr_state->visual, ctx, span->ext->path, &span->bounds, span->ext->txh, tr_state); + } else { + gf_font_span_draw_2d(tr_state, span, ctx, bounds); + } + if (span->anchor || (span->flags & GF_TEXT_SPAN_UNDERLINE) ) gf_font_underline_span(tr_state, span, ctx); + if (ctx->sub_path_index) break; + } + if (is_rv) tr_state->ctx->aspect.fill_color = hl_color; +} + +void gf_font_spans_pick(GF_Node *node, GF_List *spans, GF_TraverseState *tr_state, GF_Rect *node_bounds, Bool use_dom_events, Drawable *drawable) +{ + u32 i, count, j; + Fixed dx, dy; +#ifndef GPAC_DISABLE_3D + GF_Matrix inv_mx; +#endif + GF_TextSpan *span; + DrawAspect2D asp; + GF_Matrix2D inv_2d; + Fixed x, y; + GF_Compositor *compositor = tr_state->visual->compositor; + + if (compositor->text_selection) { + if (compositor->text_selection != tr_state->text_parent) return; + if (compositor->store_text_state==GF_SC_TSEL_FROZEN) return; + } + + /*TODO: pick the real glyph and not just the bounds of the text span*/ + count = gf_list_count(spans); + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + GF_Ray r; + SFVec3f local_pt; + + r = tr_state->ray; + gf_mx_copy(inv_mx, tr_state->model_matrix); + gf_mx_inverse(&inv_mx); + gf_mx_apply_ray(&inv_mx, &r); + + if (!compositor_get_2d_plane_intersection(&r, &local_pt)) return; + + x = local_pt.x; + y = local_pt.y; + } else +#endif + { + gf_mx2d_copy(inv_2d, tr_state->transform); + gf_mx2d_inverse(&inv_2d); + x = tr_state->ray.orig.x; + y = tr_state->ray.orig.y; + gf_mx2d_apply_coords(&inv_2d, &x, &y); + } + + if (use_dom_events) { + memset(&asp, 0, sizeof(DrawAspect2D)); + drawable_get_aspect_2d_svg(node, &asp, tr_state); + } + + dx = dy = 0; + span = NULL; + for (i=0; i=span->bounds.x) + && (y<=span->bounds.y) + && (x<=span->bounds.x+span->bounds.width) + && (y>=span->bounds.y-span->bounds.height)) { + } else { + continue; + } + + dx = span->off_x; + dy = span->off_y; + for (j=0; jnb_glyphs; j++) { + GF_Rect rc; + loc_x = x - (span->dx ? span->dx[j] : dx); + loc_y = y - (span->dy ? span->dy[j] : dy); + loc_x = gf_divfix(loc_x, span->font_scale); + loc_y = gf_divfix(loc_y, span->font_scale) + span->font->baseline; + if (span->flags & GF_TEXT_SPAN_FLIP) loc_y = - loc_y; + + gf_path_get_bounds(span->glyphs[j]->path, &rc); + rc.height = INT2FIX(span->font->ascent + span->font->descent); + + if (0&&span->rot) { + GF_Matrix2D r; + gf_mx2d_init(r); + gf_mx2d_add_rotation(&r, 0, 0, span->rot[i]); + gf_mx2d_apply_coords(&r, &loc_x, &loc_y); + } + + if (use_dom_events) { + if (svg_drawable_is_over(drawable, loc_x, loc_y, &asp, tr_state, &rc)) + goto picked; + } else { + if ( (loc_x >= rc.x) && (loc_y <= rc.y) && (loc_x <= rc.x + rc.width) && (loc_y >= rc.y - rc.height) ) { + goto picked; + } +// if (gf_path_point_over(span->glyphs[j]->path, loc_x, loc_y)) goto picked; + } + + if (span->flags & GF_TEXT_SPAN_HORIZONTAL) { + dx += span->font_scale * span->glyphs[j]->horiz_advance; + } else { + dy -= span->font_scale * span->glyphs[j]->vert_advance; + } + } + } + return; + +picked: + compositor->hit_local_point.x = x; + compositor->hit_local_point.y = y; + compositor->hit_local_point.z = 0; + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + gf_mx_copy(compositor->hit_world_to_local, tr_state->model_matrix); + gf_mx_copy(compositor->hit_local_to_world, inv_mx); + } else +#endif + { + gf_mx_from_mx2d(&compositor->hit_world_to_local, &tr_state->transform); + gf_mx_from_mx2d(&compositor->hit_local_to_world, &inv_2d); + } + + compositor->hit_node = node; + if (span->anchor) compositor->hit_node = span->anchor; + + compositor->end_sel.x = compositor->hit_world_point.x; + compositor->end_sel.y = compositor->hit_world_point.y; + if (compositor->text_selection) { + compositor->draw_next_frame = 1; + span->flags |= GF_TEXT_SPAN_SELECTED; + } else { + compositor->start_sel = compositor->end_sel; + } + + compositor->hit_text = tr_state->text_parent; + compositor->hit_use_dom_events = use_dom_events; + compositor->hit_normal.x = compositor->hit_normal.y = 0; compositor->hit_normal.z = FIX_ONE; + compositor->hit_texcoords.x = gf_divfix(x, node_bounds->width) + FIX_ONE/2; + compositor->hit_texcoords.y = gf_divfix(y, node_bounds->height) + FIX_ONE/2; + + if (compositor_is_composite_texture(tr_state->appear)) { + compositor->hit_appear = tr_state->appear; + } else { + compositor->hit_appear = NULL; + } + + gf_list_reset(tr_state->visual->compositor->sensors); + count = gf_list_count(tr_state->vrml_sensors); + for (i=0; ivisual->compositor->sensors, gf_list_get(tr_state->vrml_sensors, i)); + } +} diff --git a/src/compositor/gl_inc.h b/src/compositor/gl_inc.h new file mode 100644 index 0000000..2e54ef6 --- /dev/null +++ b/src/compositor/gl_inc.h @@ -0,0 +1,198 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GL_INC_H_ +#define _GL_INC_H_ + +#ifdef WIN32 +#include +#endif + +#ifndef GPAC_DISABLE_3D + +#ifdef GPAC_USE_OGL_ES + +#ifndef GPAC_FIXED_POINT +#error "OpenGL ES defined without fixed-point support - unsupported." +#endif +#include "GLES/egl.h" + +#else + + +#ifndef GPAC_USE_TINYGL +#include +#else +#include "../../../TinyGL/include/GL/gl.h" +#endif + + +#ifdef GPAC_HAS_GLU +#include +#endif + +#endif + +/*redefine all ext needed*/ + +/*BGRA pixel format*/ +#ifndef GL_BGRA_EXT +#define GL_BGRA_EXT 0x80E1 +#endif + +#ifndef GL_BGR_EXT +#define GL_BGR_EXT 0x80E0 +#endif + +/*rectangle texture (non-pow2) - same as GL_TEXTURE_RECTANGLE_NV*/ +#ifndef GL_TEXTURE_RECTANGLE_EXT +#define GL_TEXTURE_RECTANGLE_EXT 0x84F5 +#endif + +#ifndef GL_RESCALE_NORMAL +#define GL_RESCALE_NORMAL 0x803A +#endif + +#ifndef YCBCR_MESA +#define YCBCR_MESA 0x8757 +#endif + +#ifndef YCBCR_422_APPLE +#define YCBCR_422_APPLE 0x85B9 +#endif + +#ifndef UNSIGNED_SHORT_8_8_MESA +#define UNSIGNED_SHORT_8_8_MESA 0x85BA /* same as Apple's */ +#define UNSIGNED_SHORT_8_8_REV_MESA 0x85BB /* same as Apple's */ +#endif + + +#ifndef GL_TEXTURE0_ARB + +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +#define GL_EXT_abgr 1 +#define GL_EXT_blend_color 1 +#define GL_EXT_blend_minmax 1 +#define GL_EXT_blend_subtract 1 +#define GL_EXT_texture_env_combine 1 +#define GL_EXT_texture_env_add 1 +#define GL_ABGR_EXT 0x8000 +#define GL_CONSTANT_COLOR_EXT 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 +#define GL_CONSTANT_ALPHA_EXT 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 +#define GL_BLEND_COLOR_EXT 0x8005 +#define GL_FUNC_ADD_EXT 0x8006 +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#define GL_BLEND_EQUATION_EXT 0x8009 +#define GL_FUNC_SUBTRACT_EXT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#define GL_LOGIC_OP GL_INDEX_LOGIC_OP +#define GL_TEXTURE_COMPONENTS GL_TEXTURE_INTERNAL_FORMAT + +#endif + +#ifndef COMBINE_RGB_ARB +#define COMBINE_RGB_ARB 0x8571 +#define COMBINE_ALPHA_ARB 0x8572 +#define INTERPOLATE_ARB 0x8575 +#define COMBINE_ARB 0x8570 +#define SOURCE0_RGB_ARB 0x8580 +#define SOURCE1_RGB_ARB 0x8581 +#define SOURCE2_RGB_ARB 0x8582 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#endif + + + +#endif /*GPAC_DISABLE_3D*/ + +#endif /*_GL_INC_H_*/ + diff --git a/src/compositor/hardcoded_protos.c b/src/compositor/hardcoded_protos.c new file mode 100644 index 0000000..ddb1ac0 --- /dev/null +++ b/src/compositor/hardcoded_protos.c @@ -0,0 +1,749 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" +#include "visual_manager.h" + +#include "offscreen_cache.h" +#include "mpeg4_grouping.h" + +#ifndef GPAC_DISABLE_3D + +/*PathExtrusion hardcoded proto*/ + +typedef struct +{ + GF_Node *geometry; + MFVec3f *spine; + Bool beginCap; + Bool endCap; + Fixed creaseAngle; + MFRotation *orientation; + MFVec2f *scale; + Bool txAlongSpine; +} PathExtrusion; + +static Bool PathExtrusion_GetNode(GF_Node *node, PathExtrusion *path_ext) +{ + GF_FieldInfo field; + memset(path_ext, 0, sizeof(PathExtrusion)); + if (gf_node_get_field(node, 0, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFNODE) return 0; + path_ext->geometry = * (GF_Node **) field.far_ptr; + if (gf_node_get_field(node, 1, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFVEC3F) return 0; + path_ext->spine = (MFVec3f *) field.far_ptr; + if (gf_node_get_field(node, 2, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFBOOL) return 0; + path_ext->beginCap = *(SFBool *) field.far_ptr; + if (gf_node_get_field(node, 3, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFBOOL) return 0; + path_ext->endCap = *(SFBool *) field.far_ptr; + if (gf_node_get_field(node, 4, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFFLOAT) return 0; + path_ext->creaseAngle = *(SFFloat *) field.far_ptr; + if (gf_node_get_field(node, 5, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFROTATION) return 0; + path_ext->orientation = (MFRotation *) field.far_ptr; + if (gf_node_get_field(node, 6, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFVEC2F) return 0; + path_ext->scale = (MFVec2f *) field.far_ptr; + if (gf_node_get_field(node, 7, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFBOOL) return 0; + path_ext->txAlongSpine = *(SFBool *) field.far_ptr; + return 1; +} + +static void TraversePathExtrusion(GF_Node *node, void *rs, Bool is_destroy) +{ + PathExtrusion path_ext; + Drawable *stack_2d; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + Drawable3D *stack = (Drawable3D *)gf_node_get_private(node); + + if (is_destroy) { + drawable_3d_del(node); + return; + } + if (!PathExtrusion_GetNode(node, &path_ext)) return; + if (!path_ext.geometry) return; + + + if (gf_node_dirty_get(node)) { + u32 mode = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + gf_node_traverse(path_ext.geometry, tr_state); + tr_state->traversing_mode = mode; + + + switch (gf_node_get_tag(path_ext.geometry) ) { + case TAG_MPEG4_Circle: + case TAG_MPEG4_Ellipse: + case TAG_MPEG4_Rectangle: + case TAG_MPEG4_Curve2D: + case TAG_MPEG4_XCurve2D: + case TAG_MPEG4_IndexedFaceSet2D: + case TAG_MPEG4_IndexedLineSet2D: + stack_2d = (Drawable*)gf_node_get_private(path_ext.geometry); + if (!stack_2d) return; + mesh_extrude_path(stack->mesh, stack_2d->path, path_ext.spine, path_ext.creaseAngle, path_ext.beginCap, path_ext.endCap, path_ext.orientation, path_ext.scale, path_ext.txAlongSpine); + break; + case TAG_MPEG4_Text: + compositor_extrude_text(path_ext.geometry, tr_state, stack->mesh, path_ext.spine, path_ext.creaseAngle, path_ext.beginCap, path_ext.endCap, path_ext.orientation, path_ext.scale, path_ext.txAlongSpine); + break; + } + } + + if (tr_state->traversing_mode==TRAVERSE_DRAW_3D) { + visual_3d_draw(tr_state, stack->mesh); + } else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + tr_state->bbox = stack->mesh->bounds; + } +} + +static void compositor_init_path_extrusion(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraversePathExtrusion); +} + + +/*PlanarExtrusion hardcoded proto*/ +typedef struct +{ + GF_Node *geometry; + GF_Node *spine; + Bool beginCap; + Bool endCap; + Fixed creaseAngle; + MFFloat *orientationKeys; + MFRotation *orientation; + MFFloat *scaleKeys; + MFVec2f *scale; + Bool txAlongSpine; +} PlanarExtrusion; + +static Bool PlanarExtrusion_GetNode(GF_Node *node, PlanarExtrusion *path_ext) +{ + GF_FieldInfo field; + memset(path_ext, 0, sizeof(PathExtrusion)); + if (gf_node_get_field(node, 0, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFNODE) return 0; + path_ext->geometry = * (GF_Node **) field.far_ptr; + if (gf_node_get_field(node, 1, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFNODE) return 0; + path_ext->spine = * (GF_Node **) field.far_ptr; + if (gf_node_get_field(node, 2, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFBOOL) return 0; + path_ext->beginCap = *(SFBool *) field.far_ptr; + if (gf_node_get_field(node, 3, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFBOOL) return 0; + path_ext->endCap = *(SFBool *) field.far_ptr; + if (gf_node_get_field(node, 4, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFFLOAT) return 0; + path_ext->creaseAngle = *(SFFloat *) field.far_ptr; + if (gf_node_get_field(node, 5, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFFLOAT) return 0; + path_ext->orientationKeys = (MFFloat *) field.far_ptr; + if (gf_node_get_field(node, 6, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFROTATION) return 0; + path_ext->orientation = (MFRotation *) field.far_ptr; + if (gf_node_get_field(node, 7, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFFLOAT) return 0; + path_ext->scaleKeys = (MFFloat *) field.far_ptr; + if (gf_node_get_field(node, 8, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFVEC2F) return 0; + path_ext->scale = (MFVec2f *) field.far_ptr; + if (gf_node_get_field(node, 9, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFBOOL) return 0; + path_ext->txAlongSpine = *(SFBool *) field.far_ptr; + return 1; +} + +static void TraversePlanarExtrusion(GF_Node *node, void *rs, Bool is_destroy) +{ + PlanarExtrusion plane_ext; + Drawable *stack_2d; + u32 i, j, k; + MFVec3f spine_vec; + SFVec3f d; + Fixed spine_len; + GF_Rect bounds; + GF_Path *geo, *spine; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + Drawable3D *stack = (Drawable3D *)gf_node_get_private(node); + + if (is_destroy) { + drawable_3d_del(node); + return; + } + + if (!PlanarExtrusion_GetNode(node, &plane_ext)) return; + if (!plane_ext.geometry || !plane_ext.spine) return; + + + if (gf_node_dirty_get(node)) { + u32 cur, nb_pts; + u32 mode = tr_state->traversing_mode; + geo = spine = NULL; + + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + gf_node_traverse(plane_ext.geometry, tr_state); + gf_node_traverse(plane_ext.spine, tr_state); + tr_state->traversing_mode = mode; + + switch (gf_node_get_tag(plane_ext.geometry) ) { + case TAG_MPEG4_Circle: + case TAG_MPEG4_Ellipse: + case TAG_MPEG4_Rectangle: + case TAG_MPEG4_Curve2D: + case TAG_MPEG4_XCurve2D: + case TAG_MPEG4_IndexedFaceSet2D: + case TAG_MPEG4_IndexedLineSet2D: + stack_2d = (Drawable*)gf_node_get_private(plane_ext.geometry); + if (stack_2d) geo = stack_2d->path; + break; + default: + return; + } + switch (gf_node_get_tag(plane_ext.spine) ) { + case TAG_MPEG4_Circle: + case TAG_MPEG4_Ellipse: + case TAG_MPEG4_Rectangle: + case TAG_MPEG4_Curve2D: + case TAG_MPEG4_XCurve2D: + case TAG_MPEG4_IndexedFaceSet2D: + case TAG_MPEG4_IndexedLineSet2D: + stack_2d = (Drawable*)gf_node_get_private(plane_ext.spine); + if (stack_2d) spine = stack_2d->path; + break; + default: + return; + } + if (!geo || !spine) return; + + mesh_reset(stack->mesh); + gf_path_flatten(spine); + gf_path_get_bounds(spine, &bounds); + gf_path_flatten(geo); + gf_path_get_bounds(geo, &bounds); + + cur = 0; + for (i=0; in_contours; i++) { + nb_pts = 1 + spine->contours[i] - cur; + spine_vec.vals = NULL; + gf_sg_vrml_mf_alloc(&spine_vec, GF_SG_VRML_MFVEC3F, nb_pts); + spine_len = 0; + for (j=cur; jpoints[j].x; + spine_vec.vals[j].y = spine->points[j].y; + spine_vec.vals[j].z = 0; + if (j) { + gf_vec_diff(d, spine_vec.vals[j], spine_vec.vals[j-1]); + spine_len += gf_vec_len(d); + } + } + cur += nb_pts; + if (!plane_ext.orientation->count && !plane_ext.scale->count) { + mesh_extrude_path_ext(stack->mesh, geo, &spine_vec, plane_ext.creaseAngle, + bounds.x, bounds.y-bounds.height, bounds.width, bounds.height, + plane_ext.beginCap, plane_ext.endCap, NULL, NULL, plane_ext.txAlongSpine); + } + /*interpolate orientation and scale along subpath line*/ + else { + MFRotation ori; + MFVec2f scale; + Fixed cur_len, frac; + + ori.vals = NULL; + gf_sg_vrml_mf_alloc(&ori, GF_SG_VRML_MFROTATION, nb_pts); + scale.vals = NULL; + gf_sg_vrml_mf_alloc(&scale, GF_SG_VRML_MFVEC2F, nb_pts); + cur_len = 0; + if (!plane_ext.orientation->count) ori.vals[0].y = FIX_ONE; + if (!plane_ext.scale->count) scale.vals[0].x = scale.vals[0].y = FIX_ONE; + for (j=0; jcount && (plane_ext.orientation->count == plane_ext.orientationKeys->count)) { + + frac = gf_divfix(cur_len , spine_len); + if (frac < plane_ext.orientationKeys->vals[0]) ori.vals[j] = plane_ext.orientation->vals[0]; + else if (frac >= plane_ext.orientationKeys->vals[plane_ext.orientationKeys->count-1]) ori.vals[j] = plane_ext.orientation->vals[plane_ext.orientationKeys->count-1]; + else { + for (k=1; kcount; k++) { + Fixed kDiff = plane_ext.orientationKeys->vals[k] - plane_ext.orientationKeys->vals[k-1]; + if (!kDiff) continue; + if (frac < plane_ext.orientationKeys->vals[k-1]) continue; + if (frac > plane_ext.orientationKeys->vals[k]) continue; + frac = gf_divfix(frac - plane_ext.orientationKeys->vals[k-1], kDiff); + break; + } + ori.vals[j] = gf_sg_sfrotation_interpolate(plane_ext.orientation->vals[k-1], plane_ext.orientation->vals[k], frac); + } + } + + if (plane_ext.scale->count == plane_ext.scaleKeys->count) { + frac = gf_divfix(cur_len , spine_len); + if (frac <= plane_ext.scaleKeys->vals[0]) scale.vals[j] = plane_ext.scale->vals[0]; + else if (frac >= plane_ext.scaleKeys->vals[plane_ext.scaleKeys->count-1]) scale.vals[j] = plane_ext.scale->vals[plane_ext.scale->count-1]; + else { + for (k=1; kcount; k++) { + Fixed kDiff = plane_ext.scaleKeys->vals[k] - plane_ext.scaleKeys->vals[k-1]; + if (!kDiff) continue; + if (frac < plane_ext.scaleKeys->vals[k-1]) continue; + if (frac > plane_ext.scaleKeys->vals[k]) continue; + frac = gf_divfix(frac - plane_ext.scaleKeys->vals[k-1], kDiff); + break; + } + scale.vals[j].x = gf_mulfix(plane_ext.scale->vals[k].x - plane_ext.scale->vals[k-1].x, frac) + plane_ext.scale->vals[k-1].x; + scale.vals[j].y = gf_mulfix(plane_ext.scale->vals[k].y - plane_ext.scale->vals[k-1].y, frac) + plane_ext.scale->vals[k-1].y; + } + } + } + + mesh_extrude_path_ext(stack->mesh, geo, &spine_vec, plane_ext.creaseAngle, + bounds.x, bounds.y-bounds.height, bounds.width, bounds.height, + plane_ext.beginCap, plane_ext.endCap, &ori, &scale, plane_ext.txAlongSpine); + + gf_sg_vrml_mf_reset(&ori, GF_SG_VRML_MFROTATION); + gf_sg_vrml_mf_reset(&scale, GF_SG_VRML_MFVEC2F); + } + + gf_sg_vrml_mf_reset(&spine_vec, GF_SG_VRML_MFVEC3F); + } + mesh_update_bounds(stack->mesh); + gf_mesh_build_aabbtree(stack->mesh); + } + + if (tr_state->traversing_mode==TRAVERSE_DRAW_3D) { + visual_3d_draw(tr_state, stack->mesh); + } else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + tr_state->bbox = stack->mesh->bounds; + } +} + +void compositor_init_planar_extrusion(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraversePlanarExtrusion); +} + +/*PlaneClipper hardcoded proto*/ +typedef struct +{ + BASE_NODE + CHILDREN + + GF_Plane plane; +} PlaneClipper; + +typedef struct +{ + GROUPING_NODE_STACK_3D + PlaneClipper pc; +} PlaneClipperStack; + +static Bool PlaneClipper_GetNode(GF_Node *node, PlaneClipper *pc) +{ + GF_FieldInfo field; + memset(pc, 0, sizeof(PlaneClipper)); + pc->sgprivate = node->sgprivate; + + if (gf_node_get_field(node, 0, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFVEC3F) return 0; + pc->plane.normal = * (SFVec3f *) field.far_ptr; + if (gf_node_get_field(node, 1, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFFLOAT) return 0; + pc->plane.d = * (SFFloat *) field.far_ptr; + if (gf_node_get_field(node, 2, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFNODE) return 0; + pc->children = *(GF_ChildNodeItem **) field.far_ptr; + return 1; +} + + +static void TraversePlaneClipper(GF_Node *node, void *rs, Bool is_destroy) +{ + PlaneClipperStack *stack = (PlaneClipperStack *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + group_3d_delete(node); + return; + } + + if (gf_node_dirty_get(node)) { + PlaneClipper_GetNode(node, &stack->pc); + } + + if (tr_state->num_clip_planes==MAX_USER_CLIP_PLANES) { + group_3d_traverse((GF_Node*)&stack->pc, (GroupingNode*)stack, tr_state); + return; + } + + if (tr_state->traversing_mode == TRAVERSE_SORT) { + visual_3d_set_clip_plane(tr_state->visual, stack->pc.plane); + tr_state->num_clip_planes++; + + group_3d_traverse((GF_Node*)&stack->pc, (GroupingNode*)stack, tr_state); + visual_3d_reset_clip_plane(tr_state->visual); + tr_state->num_clip_planes--; + } else { + tr_state->clip_planes[tr_state->num_clip_planes] = stack->pc.plane; + gf_mx_apply_plane(&tr_state->model_matrix, &tr_state->clip_planes[tr_state->num_clip_planes]); + tr_state->num_clip_planes++; + + group_3d_traverse((GF_Node*)&stack->pc, (GroupingNode*)stack, tr_state); + + tr_state->num_clip_planes--; + } + +} + +void compositor_init_plane_clipper(GF_Compositor *compositor, GF_Node *node) +{ + PlaneClipper pc; + if (PlaneClipper_GetNode(node, &pc)) { + PlaneClipperStack *stack; + GF_SAFEALLOC(stack, PlaneClipperStack); + //SetupGroupingNode(stack, compositor->compositor, node, & pc.children); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraversePlaneClipper); + /*we're a grouping node, force bounds rebuild as soon as loaded*/ + gf_node_dirty_set(node, GF_SG_CHILD_DIRTY, 0); + + stack->pc = pc; + } +} + +#endif + + +/*OffscreenGroup hardcoded proto*/ +typedef struct +{ + BASE_NODE + CHILDREN + + Bool offscreen; + Fixed opacity; +} OffscreenGroup; + +typedef struct +{ + GROUPING_NODE_STACK_2D + +#ifndef GF_SR_USE_VIDEO_CACHE + struct _group_cache *cache; +#endif + + OffscreenGroup og; +} OffscreenGroupStack; + +static Bool OffscreenGroup_GetNode(GF_Node *node, OffscreenGroup *og) +{ + GF_FieldInfo field; + memset(og, 0, sizeof(OffscreenGroup)); + og->sgprivate = node->sgprivate; + + if (gf_node_get_field(node, 0, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFNODE) return 0; + og->children = *(GF_ChildNodeItem **) field.far_ptr; + + if (gf_node_get_field(node, 1, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFBOOL) return 0; + og->offscreen = * (SFBool *) field.far_ptr; + + if (gf_node_get_field(node, 2, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFFLOAT) return 0; + og->opacity = * (SFFloat *) field.far_ptr; + return 1; +} + + +static void TraverseOffscreenGroup(GF_Node *node, void *rs, Bool is_destroy) +{ + OffscreenGroupStack *stack = (OffscreenGroupStack *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + if (stack->cache) group_cache_del(stack->cache); + free(stack); + return; + } + + if (tr_state->traversing_mode==TRAVERSE_SORT) { + if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { + OffscreenGroup_GetNode(node, &stack->og); + + if (stack->og.offscreen) { + stack->flags |= GROUP_IS_CACHED | GROUP_PERMANENT_CACHE; + if (!stack->cache) { + stack->cache = group_cache_new(tr_state->visual->compositor, (GF_Node*)&stack->og); + } + stack->cache->opacity = stack->og.opacity; + stack->cache->drawable->flags |= DRAWABLE_HAS_CHANGED; + } else { + if (stack->cache) group_cache_del(stack->cache); + stack->cache = NULL; + stack->flags &= ~(GROUP_IS_CACHED|GROUP_PERMANENT_CACHE); + } + gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); + /*flag is not set for PROTO*/ + gf_node_dirty_set(node, GF_SG_CHILD_DIRTY, 0); + } + } + group_2d_traverse((GF_Node *)&stack->og, (GroupingNode2D*)stack, tr_state); +} + +void compositor_init_offscreen_group(GF_Compositor *compositor, GF_Node *node) +{ + OffscreenGroup og; + if (OffscreenGroup_GetNode(node, &og)) { + OffscreenGroupStack *stack; + GF_SAFEALLOC(stack, OffscreenGroupStack); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseOffscreenGroup); + stack->og = og; + if (og.offscreen) stack->flags |= GROUP_IS_CACHED; + } +} + + +/*DepthGroup hardcoded proto*/ +typedef struct +{ + BASE_NODE + CHILDREN + + Fixed depth; +} DepthGroup; + +typedef struct +{ + GROUPING_NODE_STACK_2D + DepthGroup dg; +} DepthGroupStack; + +static Bool DepthGroup_GetNode(GF_Node *node, DepthGroup *dg) +{ + GF_FieldInfo field; + memset(dg, 0, sizeof(DepthGroup)); + dg->sgprivate = node->sgprivate; + + if (gf_node_get_field(node, 0, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFNODE) return 0; + dg->children = *(GF_ChildNodeItem **) field.far_ptr; + + if (gf_node_get_field(node, 1, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFFLOAT) return 0; + dg->depth = * (SFFloat *) field.far_ptr; + return 1; +} + + +static void TraverseDepthGroup(GF_Node *node, void *rs, Bool is_destroy) +{ + Fixed depth; + DepthGroupStack *stack = (DepthGroupStack *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + free(stack); + return; + } + + if (tr_state->traversing_mode==TRAVERSE_SORT) { + if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { + //DepthGroup_GetNode(node, &stack->dg); /*lets place it below*/ + gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); + /*flag is not set for PROTO*/ + gf_node_dirty_set(node, GF_SG_CHILD_DIRTY, 0); + } + } + DepthGroup_GetNode(node, &stack->dg); + depth = tr_state->depth; + tr_state->depth += stack->dg.depth; + group_2d_traverse((GF_Node *)&stack->dg, (GroupingNode2D*)stack, tr_state); + tr_state->depth = depth; +} + +void compositor_init_depth_group(GF_Compositor *compositor, GF_Node *node) +{ + DepthGroup dg; + if (DepthGroup_GetNode(node, &dg)) { + DepthGroupStack *stack; + GF_SAFEALLOC(stack, DepthGroupStack); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseDepthGroup); + stack->dg = dg; + } +} + + + +/*PathExtrusion hardcoded proto*/ + +typedef struct +{ + BASE_NODE + + GF_Node *point; + Fixed fineness; + MFInt32 type; + MFInt32 index; +} IndexedCurve2D; + +static Bool IndexedCurve2D_GetNode(GF_Node *node, IndexedCurve2D *ic2d) +{ + GF_FieldInfo field; + memset(ic2d, 0, sizeof(IndexedCurve2D)); + + ic2d->sgprivate = node->sgprivate; + + if (gf_node_get_field(node, 0, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFNODE) return 0; + ic2d->point = * (GF_Node **) field.far_ptr; + + if (gf_node_get_field(node, 1, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_SFFLOAT) return 0; + ic2d->fineness = *(SFFloat *) field.far_ptr; + + if (gf_node_get_field(node, 2, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFINT32) return 0; + ic2d->type = *(MFInt32 *) field.far_ptr; + + if (gf_node_get_field(node, 3, &field) != GF_OK) return 0; + if (field.fieldType != GF_SG_VRML_MFINT32) return 0; + ic2d->index = *(MFInt32 *) field.far_ptr; + + return 1; +} + +void curve2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state, MFInt32 *idx); + +static void TraverseIndexedCurve2D(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + IndexedCurve2D ic2d; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + Drawable *stack = (Drawable *)gf_node_get_private(node); + + if (is_destroy) { + drawable_node_del(node); + return; + } + + if (gf_node_dirty_get(node)) { + if (!IndexedCurve2D_GetNode(node, &ic2d)) return; + curve2d_check_changes((GF_Node*) &ic2d, stack, tr_state, &ic2d.index); + } + + switch (tr_state->traversing_mode) { +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + if (!stack->mesh) { + stack->mesh = new_mesh(); + mesh_from_path(stack->mesh, stack->path); + } + visual_3d_draw_2d(stack, tr_state); + return; +#endif + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + drawable_finalize_sort(ctx, tr_state, NULL); + return; + } +} + +static void compositor_init_idx_curve2d(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, TraverseIndexedCurve2D); +} + + +/*hardcoded proto loading - this is mainly used for module development and testing...*/ +void compositor_init_hardcoded_proto(GF_Compositor *compositor, GF_Node *node) +{ + MFURL *proto_url; + GF_Proto *proto; + u32 i; + + proto = gf_node_get_proto(node); + if (!proto) return; + proto_url = gf_sg_proto_get_extern_url(proto); + + for (i=0; icount; i++) { + const char *url = proto_url->vals[0].url; +#ifndef GPAC_DISABLE_3D + if (!strcmp(url, "urn:inet:gpac:builtin:PathExtrusion")) { + compositor_init_path_extrusion(compositor, node); + return; + } + if (!strcmp(url, "urn:inet:gpac:builtin:PlanarExtrusion")) { + compositor_init_planar_extrusion(compositor, node); + return; + } + if (!strcmp(url, "urn:inet:gpac:builtin:PlaneClipper")) { + compositor_init_plane_clipper(compositor, node); + return; + } +#endif + if (!strcmp(url, "urn:inet:gpac:builtin:TextureText")) { + compositor_init_texture_text(compositor, node); + return; + } + if (!strcmp(url, "urn:inet:gpac:builtin:OffscreenGroup")) { + compositor_init_offscreen_group(compositor, node); + return; + } + if (!strcmp(url, "urn:inet:gpac:builtin:DepthGroup")) { + compositor_init_depth_group(compositor, node); + return; + } + if (!strcmp(url, "urn:inet:gpac:builtin:IndexedCurve2D")) { + compositor_init_idx_curve2d(compositor, node); + return; + } + } + +} + diff --git a/src/compositor/mesh.c b/src/compositor/mesh.c new file mode 100644 index 0000000..f8cf13f --- /dev/null +++ b/src/compositor/mesh.c @@ -0,0 +1,2195 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#include +#include +#include + +#ifndef GPAC_DISABLE_3D +/*for GPAC_HAS_GLU*/ +#include "gl_inc.h" + +/*when highspeeds, amount of subdivisions for bezier and ellipse will be devided by this factor*/ +#define HIGH_SPEED_RATIO 2 + + +/*size alloc for meshes doubles memory at each realloc rather than using a fix-size increment + (this really speeds up large meshes constructing). Final memory usage is adjusted when updating mesh bounds +*/ +#define MESH_CHECK_VERTEX(m) \ + if (m->v_count == m->v_alloc) { \ + m->v_alloc *= 2; \ + m->vertices = (GF_Vertex *)realloc(m->vertices, sizeof(GF_Vertex)*m->v_alloc); \ + } \ + +#define MESH_CHECK_IDX(m) \ + if (m->i_count == m->i_alloc) { \ + m->i_alloc *= 2; \ + m->indices = (IDX_TYPE*)realloc(m->indices, sizeof(IDX_TYPE)*m->i_alloc); \ + } \ + + + +static void del_aabb_node(AABBNode *node) +{ + if (node->pos) del_aabb_node(node->pos); + if (node->neg) del_aabb_node(node->neg); + free(node); +} + +void mesh_reset(GF_Mesh *mesh) +{ + mesh->v_count = 0; + mesh->i_count = 0; + mesh->flags = 0; + mesh->mesh_type = 0; + memset(&mesh->bounds.min_edge, 0, sizeof(SFVec3f)); + memset(&mesh->bounds.max_edge, 0, sizeof(SFVec3f)); + if (mesh->aabb_root) del_aabb_node(mesh->aabb_root); + mesh->aabb_root = NULL; + if (mesh->aabb_indices) free(mesh->aabb_indices); + mesh->aabb_indices = NULL; +} + +void mesh_free(GF_Mesh *mesh) +{ + if (mesh->vertices) free(mesh->vertices); + if (mesh->indices) free(mesh->indices); + if (mesh->aabb_root) del_aabb_node(mesh->aabb_root); + mesh->aabb_root = NULL; + if (mesh->aabb_indices) free(mesh->aabb_indices); + free(mesh); +} + +GF_Mesh *new_mesh() +{ + GF_Mesh *mesh = (GF_Mesh *)malloc(sizeof(GF_Mesh)); + if (mesh) { + memset(mesh, 0, sizeof(GF_Mesh)); + mesh->v_alloc = 8; + mesh->vertices = (GF_Vertex*)malloc(sizeof(GF_Vertex)*mesh->v_alloc); + mesh->i_alloc = 8; + mesh->indices = (IDX_TYPE*)malloc(sizeof(IDX_TYPE)*mesh->i_alloc); + } + return mesh; +} + +static void mesh_fit_alloc(GF_Mesh *m) +{ + if (m->v_count && (m->v_count < m->v_alloc)) { + m->v_alloc = m->v_count; + m->vertices = (GF_Vertex *)realloc(m->vertices, sizeof(GF_Vertex)*m->v_alloc); + } + if (m->i_count && (m->i_count < m->i_alloc)) { + m->i_alloc = m->i_count; + m->indices = (IDX_TYPE*)realloc(m->indices, sizeof(IDX_TYPE)*m->i_alloc); + } +} + + +void mesh_update_bounds(GF_Mesh *mesh) +{ + u32 i; + Fixed mx, my, mz, Mx, My, Mz; + mx = my = mz = FIX_MAX; + Mx = My = Mz = FIX_MIN; + + mesh_fit_alloc(mesh); + + for (i=0; iv_count; i++) { + SFVec3f *v = &mesh->vertices[i].pos; + if (mx>v->x) mx=v->x; + if (my>v->y) my=v->y; + if (mz>v->z) mz=v->z; + if (Mxx) Mx=v->x; + if (Myy) My=v->y; + if (Mzz) Mz=v->z; + } + mesh->bounds.min_edge.x = mx; mesh->bounds.min_edge.y = my; mesh->bounds.min_edge.z = mz; + mesh->bounds.max_edge.x = Mx; mesh->bounds.max_edge.y = My; mesh->bounds.max_edge.z = Mz; + gf_bbox_refresh(&mesh->bounds); +} + +void mesh_clone(GF_Mesh *dest, GF_Mesh *orig) +{ + if (dest->v_allocv_alloc) { + dest->v_alloc = orig->v_alloc; + dest->vertices = (GF_Vertex *)realloc(dest->vertices, sizeof(GF_Vertex)*dest->v_alloc); + } + dest->v_count = orig->v_count; + memcpy(dest->vertices, orig->vertices, sizeof(GF_Vertex)*dest->v_count); + + if (dest->i_alloc < orig->i_alloc) { + dest->i_alloc = orig->i_alloc; + dest->indices = (IDX_TYPE*)realloc(dest->indices, sizeof(IDX_TYPE)*dest->i_alloc); + } + dest->i_count = orig->i_count; + memcpy(dest->indices, orig->indices, sizeof(IDX_TYPE)*dest->i_count); + + dest->mesh_type = orig->mesh_type; + dest->flags = orig->flags; + dest->bounds = orig->bounds; + /*and reset AABB*/ + if (dest->aabb_root) del_aabb_node(dest->aabb_root); + dest->aabb_root = NULL; + if (dest->aabb_indices) free(dest->aabb_indices); + dest->aabb_indices = NULL; +} + + +static GFINLINE GF_Vertex set_vertex(Fixed x, Fixed y, Fixed z, Fixed nx, Fixed ny, Fixed nz, Fixed u, Fixed v) +{ + SFVec3f nor; + GF_Vertex res; + res.pos.x = x; res.pos.y = y; res.pos.z = z; + nor.x = nx; nor.y = ny; nor.z = nz; gf_vec_norm(&nor); + MESH_SET_NORMAL(res, nor); + + res.texcoords.x = u; res.texcoords.y = v; +#ifdef MESH_USE_SFCOLOR + res.color.blue = res.color.red = res.color.green = res.color.alpha = FIX_ONE; +#else + res.color = 0xFFFFFFFF; +#endif + return res; +} +void mesh_set_vertex(GF_Mesh *mesh, Fixed x, Fixed y, Fixed z, Fixed nx, Fixed ny, Fixed nz, Fixed u, Fixed v) +{ + MESH_CHECK_VERTEX(mesh); + mesh->vertices[mesh->v_count] = set_vertex(x, y, z, nx, ny, nz, u, v); + mesh->v_count++; +} + +void mesh_set_vertex_v(GF_Mesh *mesh, SFVec3f pt, SFVec3f nor, SFVec2f tx, SFColorRGBA col) +{ + MESH_CHECK_VERTEX(mesh); + mesh->vertices[mesh->v_count].pos = pt; + mesh->vertices[mesh->v_count].texcoords = tx; + mesh->vertices[mesh->v_count].color = MESH_MAKE_COL(col); + gf_vec_norm(&nor); + MESH_SET_NORMAL(mesh->vertices[mesh->v_count], nor); + mesh->v_count++; +} + +void mesh_set_vertex_vx(GF_Mesh *mesh, GF_Vertex *vx) +{ + MESH_CHECK_VERTEX(mesh); + mesh->vertices[mesh->v_count] = *vx; + mesh->v_count++; +} + +void mesh_set_point(GF_Mesh *mesh, Fixed x, Fixed y, Fixed z, SFColorRGBA col) +{ + MESH_CHECK_VERTEX(mesh); + mesh->vertices[mesh->v_count].pos.x = x; + mesh->vertices[mesh->v_count].pos.y = y; + mesh->vertices[mesh->v_count].pos.z = z; + mesh->vertices[mesh->v_count].normal.x = mesh->vertices[mesh->v_count].normal.y = mesh->vertices[mesh->v_count].normal.z = 0; + mesh->vertices[mesh->v_count].texcoords.x = mesh->vertices[mesh->v_count].texcoords.y = 0; + mesh->vertices[mesh->v_count].color = MESH_MAKE_COL(col); + mesh->v_count++; +} +void mesh_set_index(GF_Mesh *mesh, u32 idx) +{ + MESH_CHECK_IDX(mesh); + mesh->indices[mesh->i_count] = (IDX_TYPE) idx; + mesh->i_count++; +} +void mesh_set_triangle(GF_Mesh *mesh, u32 v1_idx, u32 v2_idx, u32 v3_idx) +{ + mesh_set_index(mesh, v1_idx); + mesh_set_index(mesh, v2_idx); + mesh_set_index(mesh, v3_idx); +} +void mesh_set_line(GF_Mesh *mesh, u32 v1_idx, u32 v2_idx) +{ + mesh_set_index(mesh, v1_idx); + mesh_set_index(mesh, v2_idx); +} + +void mesh_recompute_normals(GF_Mesh *mesh) +{ + u32 i; + if (mesh->mesh_type) return; + + for (i=0; ii_count; i+=3) { + SFVec3f v1, v2, v3; + gf_vec_diff(v1, mesh->vertices[mesh->indices[i+1]].pos, mesh->vertices[mesh->indices[i]].pos); + gf_vec_diff(v2, mesh->vertices[mesh->indices[i+2]].pos, mesh->vertices[mesh->indices[i]].pos); + v3 = gf_vec_cross(v1, v2); + gf_vec_norm(&v3); + MESH_SET_NORMAL(mesh->vertices[mesh->indices[i]], v3); + MESH_SET_NORMAL(mesh->vertices[mesh->indices[i+1]], v3); + MESH_SET_NORMAL(mesh->vertices[mesh->indices[i+2]], v3); + } +} + +void mesh_generate_tex_coords(GF_Mesh *mesh, GF_Node *__texCoords) +{ + u32 i; + X_TextureCoordinateGenerator *txgen = (X_TextureCoordinateGenerator *)__texCoords; + + if (!strcmp(txgen->mode.buffer, "SPHERE-LOCAL")) { + for (i=0; iv_count; i++) { + GF_Vertex *vx = &mesh->vertices[i]; + vx->texcoords.x = (vx->normal.x+FIX_ONE) / 2; + vx->texcoords.y = (vx->normal.y+FIX_ONE) / 2; + } + } + else if (!strcmp(txgen->mode.buffer, "COORD")) { + for (i=0; iv_count; i++) { + GF_Vertex *vx = &mesh->vertices[i]; + vx->texcoords.x = vx->pos.x; + vx->texcoords.y = vx->pos.y; + } + } +} + + +void mesh_new_box(GF_Mesh *mesh, SFVec3f size) +{ + Fixed hx = size.x / 2; + Fixed hy = size.y / 2; + Fixed hz = size.z / 2; + + mesh_reset(mesh); + /*back face (horiz flip of texcoords)*/ + mesh_set_vertex(mesh, hx, -hy, -hz, 0, 0, -FIX_ONE, 0, 0); + mesh_set_vertex(mesh, -hx, -hy, -hz, 0, 0, -FIX_ONE, FIX_ONE, 0); + mesh_set_vertex(mesh, -hx, hy, -hz, 0, 0, -FIX_ONE, FIX_ONE, FIX_ONE); + mesh_set_vertex(mesh, hx, hy, -hz, 0, 0, -FIX_ONE, 0, FIX_ONE); + mesh_set_triangle(mesh, 0, 1, 2); mesh_set_triangle(mesh, 0, 2, 3); + /*top face*/ + mesh_set_vertex(mesh, -hx, hy, hz, 0, FIX_ONE, 0, 0, 0); + mesh_set_vertex(mesh, hx, hy, hz, 0, FIX_ONE, 0, FIX_ONE, 0); + mesh_set_vertex(mesh, hx, hy, -hz, 0, FIX_ONE, 0, FIX_ONE, FIX_ONE); + mesh_set_vertex(mesh, -hx, hy, -hz, 0, FIX_ONE, 0, 0, FIX_ONE); + mesh_set_triangle(mesh, 4, 5, 6); mesh_set_triangle(mesh, 4, 6, 7); + /*front face*/ + mesh_set_vertex(mesh, -hx, -hy, hz, 0, 0, FIX_ONE, 0, 0); + mesh_set_vertex(mesh, hx, -hy, hz, 0, 0, FIX_ONE, FIX_ONE, 0); + mesh_set_vertex(mesh, hx, hy, hz, 0, 0, FIX_ONE, FIX_ONE, FIX_ONE); + mesh_set_vertex(mesh, -hx, hy, hz, 0, 0, FIX_ONE, 0, FIX_ONE); + mesh_set_triangle(mesh, 8, 9, 10); mesh_set_triangle(mesh, 8, 10, 11); + /*left face*/ + mesh_set_vertex(mesh, -hx, -hy, -hz, -FIX_ONE, 0, 0, 0, 0); + mesh_set_vertex(mesh, -hx, -hy, hz, -FIX_ONE, 0, 0, FIX_ONE, 0); + mesh_set_vertex(mesh, -hx, hy, hz, -FIX_ONE, 0, 0, FIX_ONE, FIX_ONE); + mesh_set_vertex(mesh, -hx, hy, -hz, -FIX_ONE, 0, 0, 0, FIX_ONE); + mesh_set_triangle(mesh, 12, 13, 14); mesh_set_triangle(mesh, 12, 14, 15); + /*bottom face*/ + mesh_set_vertex(mesh, -hx, -hy, -hz, 0, -FIX_ONE, 0, 0, 0); + mesh_set_vertex(mesh, hx, -hy, -hz, 0, -FIX_ONE, 0, FIX_ONE, 0); + mesh_set_vertex(mesh, hx, -hy, hz, 0, -FIX_ONE, 0, FIX_ONE, FIX_ONE); + mesh_set_vertex(mesh, -hx, -hy, hz, 0, -FIX_ONE, 0, 0, FIX_ONE); + mesh_set_triangle(mesh, 16, 17, 18); mesh_set_triangle(mesh, 16, 18, 19); + /*right face*/ + mesh_set_vertex(mesh, hx, -hy, hz, FIX_ONE, 0, 0, 0, 0); + mesh_set_vertex(mesh, hx, -hy, -hz, FIX_ONE, 0, 0, FIX_ONE, 0); + mesh_set_vertex(mesh, hx, hy, -hz, FIX_ONE, 0, 0, FIX_ONE, FIX_ONE); + mesh_set_vertex(mesh, hx, hy, hz, FIX_ONE, 0, 0, 0, FIX_ONE); + mesh_set_triangle(mesh, 20, 21, 22); mesh_set_triangle(mesh, 20, 22, 23); + + + mesh->flags |= MESH_IS_SOLID; + mesh->bounds.min_edge.x = -hx; mesh->bounds.min_edge.y = -hy; mesh->bounds.min_edge.z = -hz; + mesh->bounds.max_edge.x = hx; mesh->bounds.max_edge.y = hy; mesh->bounds.max_edge.z = hz; + gf_bbox_refresh(&mesh->bounds); + gf_mesh_build_aabbtree(mesh); +} + +void mesh_new_unit_bbox(GF_Mesh *mesh) +{ + SFColorRGBA col; + Fixed s = FIX_ONE/2; + + memset(&col, 0, sizeof(SFColor)); + col.alpha = 1; + mesh_reset(mesh); + mesh->mesh_type = MESH_LINESET; + mesh_set_point(mesh, -s, -s, -s, col); + mesh_set_point(mesh, s, -s, -s, col); + mesh_set_point(mesh, s, s, -s, col); + mesh_set_point(mesh, -s, s, -s, col); + mesh_set_point(mesh, -s, -s, s, col); + mesh_set_point(mesh, s, -s, s, col); + mesh_set_point(mesh, s, s, s, col); + mesh_set_point(mesh, -s, s, s, col); + + mesh_set_line(mesh, 0, 1); mesh_set_line(mesh, 1, 2); mesh_set_line(mesh, 2, 3); mesh_set_line(mesh, 3, 0); + mesh_set_line(mesh, 4, 5); mesh_set_line(mesh, 5, 6); mesh_set_line(mesh, 6, 7); mesh_set_line(mesh, 7, 4); + mesh_set_line(mesh, 0, 4); + mesh_set_line(mesh, 1, 5); + mesh_set_line(mesh, 2, 6); + mesh_set_line(mesh, 3, 7); + gf_bbox_refresh(&mesh->bounds); +} + + +static void compute_cylinder(Fixed height, Fixed radius, s32 numFacets, SFVec3f *coords, SFVec2f *texcoords) +{ + Fixed angle, t, u; + s32 i; + t = height / 2; + for (i=0; iv_count-4, mesh->v_count-1, mesh->v_count-3); + mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-2, mesh->v_count-1); + } + } + + /*top*/ + mesh_set_vertex(mesh, coords[0].x, coords[0].y, coords[0].z, + coords[0].x, 0, coords[0].z, + texcoords[0].x - FIX_ONE, FIX_ONE); + + /*bottom*/ + mesh_set_vertex(mesh, coords[0].x, -1*coords[0].y, coords[0].z, + coords[0].x, 0, coords[0].z, + texcoords[0].x - FIX_ONE, 0); + + mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-1, mesh->v_count-3); + mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-2, mesh->v_count-1); + } + + if (bottom) { + Fixed angle = 0; + Fixed aincr = GF_2PI / nfacets; + + mesh_set_vertex(mesh, 0, -height/2, 0, 0, -FIX_ONE, 0, FIX_ONE/2, FIX_ONE/2); + c_idx = mesh->v_count-1; + for (i=0; iv_count-2, mesh->v_count-1); + } + + mesh_set_vertex(mesh, coords[0].x, -1*coords[0].y, coords[0].z, + 0, -FIX_ONE, 0, + (FIX_ONE + gf_sin(angle))/2, FIX_ONE - (FIX_ONE + gf_cos(angle))/2); + mesh_set_triangle(mesh, c_idx, mesh->v_count-2, mesh->v_count-1); + } + + if (top) { + Fixed aincr = GF_2PI / nfacets; + Fixed angle = GF_PI + aincr; + + mesh_set_vertex(mesh, 0, height/2, 0, 0, FIX_ONE, 0, FIX_ONE/2, FIX_ONE/2); + c_idx = mesh->v_count-1; + for (i=nfacets; i>0; --i, angle += aincr) { + + mesh_set_vertex(mesh, coords[i - 1].x, coords[i - 1].y, coords[i - 1].z, + 0, FIX_ONE, 0, + (FIX_ONE + gf_sin(angle))/2, FIX_ONE - (FIX_ONE + gf_cos(angle))/2); + if (i) mesh_set_triangle(mesh, c_idx, mesh->v_count-2, mesh->v_count-1); + } + mesh_set_vertex(mesh, coords[nfacets - 1].x, coords[nfacets - 1].y, coords[nfacets - 1].z, + 0, FIX_ONE, 0, + (FIX_ONE + gf_sin(angle))/2, FIX_ONE - (FIX_ONE + gf_cos(angle))/2); + mesh_set_triangle(mesh, c_idx, mesh->v_count-2, mesh->v_count-1); + } + free(texcoords); + free(coords); + + if (top && bottom && side) mesh->flags |= MESH_IS_SOLID; + + mesh->bounds.min_edge.x = mesh->bounds.min_edge.z = -radius; + mesh->bounds.max_edge.x = mesh->bounds.max_edge.z = radius; + mesh->bounds.max_edge.y = (side || (top && bottom)) ? height/2 : 0; + mesh->bounds.min_edge.y = -mesh->bounds.max_edge.y; + gf_bbox_refresh(&mesh->bounds); + + gf_mesh_build_aabbtree(mesh); +} + +#define CONE_SUBDIV 24 +void mesh_new_cone(GF_Mesh *mesh, Fixed height, Fixed radius, Bool bottom, Bool side, Bool low_res) +{ + u32 nfacets, i, c_idx; + SFVec3f *coords; + SFVec2f *texcoords; + + mesh_reset(mesh); + if (!bottom && !side) return; + + nfacets = CONE_SUBDIV; + if (low_res) nfacets /= HIGH_SPEED_RATIO; + coords = (SFVec3f*)malloc(sizeof(SFVec3f) * nfacets); + texcoords = (SFVec2f*)malloc(sizeof(SFVec2f) * nfacets); + + compute_cylinder(height, radius, nfacets, coords, texcoords); + + if (side) { + Fixed Ny = gf_muldiv(radius, radius, height); + + for (i = 0; i < nfacets; ++i) { + /*top*/ + mesh_set_vertex(mesh, 0, coords[i].y, 0, + coords[i].x, Ny, coords[i].z, + texcoords[i].x, FIX_ONE); + + /*base*/ + mesh_set_vertex(mesh, coords[i].x, -1*coords[i].y, coords[i].z, + coords[i].x, Ny, coords[i].z, + texcoords[i].x, 0); + if (i) { + mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-1, mesh->v_count-3); + } + } + /*top*/ + mesh_set_vertex(mesh, 0, coords[0].y, 0, coords[0].x, Ny, coords[0].z, texcoords[0].x - FIX_ONE, FIX_ONE); + /*base*/ + mesh_set_vertex(mesh, coords[0].x, -1*coords[0].y, coords[0].z, + coords[0].x, Ny, coords[0].z, + texcoords[0].x - FIX_ONE, 0); + + mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-1, mesh->v_count-3); + } + + if (bottom) { + Fixed angle = 0; + Fixed aincr = GF_2PI / nfacets; + + mesh_set_vertex(mesh, 0, -height/2, 0, 0, -FIX_ONE, 0, FIX_ONE/2, FIX_ONE/2); + c_idx = mesh->v_count - 1; + for (i=0; iv_count-2, mesh->v_count-1); + } + mesh_set_vertex(mesh, coords[0].x, -1*coords[0].y, coords[0].z, + 0, -FIX_ONE, 0, + (FIX_ONE + gf_sin(angle))/2, FIX_ONE - (FIX_ONE + gf_cos(angle))/2); + mesh_set_triangle(mesh, c_idx, mesh->v_count-2, mesh->v_count-1); + } + free(texcoords); + free(coords); + + if (bottom && side) mesh->flags |= MESH_IS_SOLID; + + mesh->bounds.min_edge.x = mesh->bounds.min_edge.z = -radius; + mesh->bounds.max_edge.x = mesh->bounds.max_edge.z = radius; + mesh->bounds.max_edge.y = height/2; + mesh->bounds.min_edge.y = -mesh->bounds.max_edge.y; + gf_bbox_refresh(&mesh->bounds); + + gf_mesh_build_aabbtree(mesh); + +} + +void compute_sphere(Fixed radius, SFVec3f *coords, SFVec2f *texcoords, u32 num_steps) +{ + Fixed r, angle, x, y, z; + u32 i, j; + + for (i=0; iv_count-3, mesh->v_count-4, mesh->v_count-2); + mesh_set_triangle(mesh, mesh->v_count-3, mesh->v_count-2, mesh->v_count-1); + } + + } + mesh_set_vertex(mesh, coords[n + num_steps].x, coords[n + num_steps].y, coords[n + num_steps].z, + coords[n + num_steps].x, coords[n + num_steps].y, coords[n + num_steps].z, + 0/*FIX_ONE*/, texcoords[n + num_steps].y); + mesh_set_vertex(mesh, coords[n].x, coords[n].y, coords[n].z, + coords[n].x, coords[n].y, coords[n].z, + 0/*FIX_ONE*/, texcoords[n].y); + mesh_set_triangle(mesh, mesh->v_count-3, mesh->v_count-4, mesh->v_count-2); + mesh_set_triangle(mesh, mesh->v_count-3, mesh->v_count-2, mesh->v_count-1); + } + + free(coords); + free(texcoords); + mesh->flags |= MESH_IS_SOLID; + + mesh->bounds.min_edge.x = mesh->bounds.min_edge.y = mesh->bounds.min_edge.z = -radius; + mesh->bounds.max_edge.x = mesh->bounds.max_edge.y = mesh->bounds.max_edge.z = radius; + gf_bbox_refresh(&mesh->bounds); + + if (radius != FIX_ONE) gf_mesh_build_aabbtree(mesh); +} + + +void mesh_new_rectangle(GF_Mesh *mesh, SFVec2f size) +{ + Fixed hx = size.x / 2; + Fixed hy = size.y / 2; + mesh_reset(mesh); + + mesh_set_vertex(mesh, -hx, -hy, 0, 0, 0, FIX_ONE, 0, 0); + mesh_set_vertex(mesh, hx, -hy, 0, 0, 0, FIX_ONE, FIX_ONE, 0); + mesh_set_vertex(mesh, hx, hy, 0, 0, 0, FIX_ONE, FIX_ONE, FIX_ONE); + mesh_set_vertex(mesh, -hx, hy, 0, 0, 0, FIX_ONE, 0, FIX_ONE); + mesh_set_triangle(mesh, 0, 1, 2); mesh_set_triangle(mesh, 0, 2, 3); + + mesh->flags |= MESH_IS_2D; + + mesh->bounds.min_edge.x = -hx; mesh->bounds.min_edge.y = -hy; mesh->bounds.min_edge.z = 0; + mesh->bounds.max_edge.x = hx; mesh->bounds.max_edge.y = hy; mesh->bounds.max_edge.z = 0; + gf_bbox_refresh(&mesh->bounds); +} + +#define ELLIPSE_SUBDIV 32 +void mesh_new_ellipse(GF_Mesh *mesh, Fixed a_dia, Fixed b_dia, Bool low_res) +{ + Fixed step, cur, end, cosa, sina; + a_dia /= 2; + b_dia /= 2; + + /*no begin/end draw since we always use generic 2D node drawing methods*/ + end = GF_2PI; + step = end / ELLIPSE_SUBDIV; + if (low_res) step *= HIGH_SPEED_RATIO; + + mesh_reset(mesh); + + /*center*/ + mesh_set_vertex(mesh, 0, 0, 0, 0, 0, FIX_ONE, FIX_ONE/2, FIX_ONE/2); + for (cur=0; curv_count-2, mesh->v_count-1); + } + mesh_set_vertex(mesh, a_dia, 0, 0, 0, 0, FIX_ONE, FIX_ONE, FIX_ONE/2); + mesh_set_triangle(mesh, 0, mesh->v_count-2, mesh->v_count-1); + + mesh->flags |= MESH_IS_2D; + mesh->bounds.min_edge.x = -a_dia; mesh->bounds.min_edge.y = -b_dia; mesh->bounds.min_edge.z = 0; + mesh->bounds.max_edge.x = a_dia; mesh->bounds.max_edge.y = b_dia; mesh->bounds.max_edge.z = 0; + gf_bbox_refresh(&mesh->bounds); +} + +void mesh_from_path_intern(GF_Mesh *mesh, GF_Path *path, Bool make_ccw) +{ + u32 i, nbPts; + Fixed w, h; + GF_Rect bounds; + Bool isCW = 0; + + gf_path_flatten(path); + gf_path_get_bounds(path, &bounds); + + mesh_reset(mesh); + if (path->n_contours==1) { + u32 type = gf_polygone2d_get_convexity(path->points, path->n_points); + switch (type) { + /*degenrated polygon - skip*/ + case GF_POLYGON_CONVEX_LINE: + return; + case GF_POLYGON_CONVEX_CW: + isCW = make_ccw; + case GF_POLYGON_CONVEX_CCW: + w = bounds.width; + h = bounds.height; + + /*add all vertices*/ + for (i=0; in_points-1; i++) { + GF_Point2D pt = path->points[i]; + mesh_set_vertex(mesh, pt.x, pt.y, 0, 0, 0, FIX_ONE, gf_divfix(pt.x - bounds.x, w), gf_divfix(bounds.y - pt.y, h)); + } + nbPts = path->n_points - 1; + /*take care of already closed path*/ + if ( (path->points[i].x != path->points[0].x) || (path->points[i].y != path->points[0].y)) { + GF_Point2D pt = path->points[i]; + mesh_set_vertex(mesh, pt.x, pt.y, 0, 0, 0, FIX_ONE, gf_divfix(pt.x - bounds.x, w), gf_divfix(bounds.y - pt.y, h)); + nbPts = path->n_points; + } + /*make it CCW*/ + for (i=1; ibounds.min_edge.x = bounds.x; mesh->bounds.min_edge.y = bounds.y-bounds.height; mesh->bounds.min_edge.z = 0; + mesh->bounds.max_edge.x = bounds.x+bounds.width; mesh->bounds.max_edge.y = bounds.y; mesh->bounds.max_edge.z = 0; + gf_bbox_refresh(&mesh->bounds); + return; + default: + break; + } + } + /*we need to tesselate the path*/ +#ifndef GPAC_USE_OGL_ES + TesselatePath(mesh, path, 0); +#endif +} + +void mesh_from_path(GF_Mesh *mesh, GF_Path *path) +{ + mesh_from_path_intern(mesh, path, 1); +} + + +void mesh_get_outline(GF_Mesh *mesh, GF_Path *path) +{ + u32 i, j, cur, nb_pts; + mesh_reset(mesh); + + mesh->mesh_type = MESH_LINESET; + mesh->flags |= (MESH_IS_2D | MESH_NO_TEXTURE); + + gf_path_flatten(path); + + cur = 0; + for (i=0; in_contours; i++) { + nb_pts = 1+path->contours[i] - cur; + for (j=0; jpoints[j+cur]; + if (j) mesh_set_line(mesh, mesh->v_count-1, mesh->v_count); + mesh_set_vertex(mesh, pt.x, pt.y, 0, 0, 0, FIX_ONE, 0, 0); + } + cur += nb_pts; + } + mesh_update_bounds(mesh); +} + +#define COL_TO_RGBA(res, col) { res.red = col.red; res.green = col.green; res.blue = col.blue; res.alpha = FIX_ONE; } + + +#define MESH_GET_COL(thecol, index) {\ + if (colorRGB && ((u32) index < colorRGB->color.count) ) COL_TO_RGBA(thecol, colorRGB->color.vals[index]) \ + else if (colorRGBA && (u32) index < colorRGBA->color.count) thecol = colorRGBA->color.vals[index]; \ + } \ + +void mesh_new_ils(GF_Mesh *mesh, GF_Node *__coord, MFInt32 *coordIndex, GF_Node *__color, MFInt32 *colorIndex, Bool colorPerVertex, Bool do_close) +{ + u32 i, n, count, c_count, col_count; + u32 index; + u32 first_idx, last_idx; + Bool move_to; + SFVec3f pt; + SFColorRGBA colRGBA; + Bool has_color, has_coord; + M_Coordinate2D *coord2D = (M_Coordinate2D*) __coord; + M_Coordinate *coord = (M_Coordinate*) __coord; + M_Color *colorRGB = (M_Color *) __color; + X_ColorRGBA *colorRGBA = (X_ColorRGBA *) __color; + + if (__coord && (gf_node_get_tag(__coord) == TAG_MPEG4_Coordinate2D)) { + coord = NULL; + } else { + coord2D = NULL; + } + + colRGBA.red = colRGBA.green = colRGBA.blue = colRGBA.alpha = 0; + + if (!coord2D && !coord) return; + c_count = coord2D ? coord2D->point.count : coord->point.count; + if (!c_count) return; + + count = coordIndex->count; + has_coord = count ? 1 : 0; + if (!has_coord) count = c_count; + + if (!colorIndex->vals) colorIndex = coordIndex; + col_count = colorIndex->count ? colorIndex->count : c_count; + /*not enough color indices, use coord ones*/ + if (colorPerVertex && (col_countcolor.count) ? 1 : 0; + } else { + colorRGBA = NULL; + has_color = (colorRGB->color.count) ? 1 : 0; + } + } + + mesh_reset(mesh); + mesh->mesh_type = MESH_LINESET; + if (has_color) mesh->flags |= MESH_HAS_COLOR; + + n = 0; + if (has_color && !colorPerVertex) { + index = colorIndex->count ? colorIndex->vals[0] : 0; + if ((u32) index < col_count) MESH_GET_COL(colRGBA, index); + } + move_to = 1; + + first_idx = last_idx = 0; + for (i=0; ivals[i] == -1) { + /*close color per vertex*/ + if (!move_to && do_close && !gf_vec_equal(mesh->vertices[first_idx].pos, mesh->vertices[last_idx].pos) ) { + mesh_set_line(mesh, last_idx, first_idx); + } + move_to = 1; + n++; + if (has_color && !colorPerVertex) { + if (ncount) index = colorIndex->vals[n]; + else if (ncount) index = colorIndex->vals[i]; + else if (ivals[i]; + else index = i; + if (index < c_count) { + if (coord2D) { + pt.x = coord2D->point.vals[index].x; + pt.y = coord2D->point.vals[index].y; + pt.z = 0; + } else { + pt = coord->point.vals[index]; + } + mesh_set_point(mesh, pt.x, pt.y, pt.z, colRGBA); + last_idx = mesh->v_count - 1; + if (move_to) { + first_idx = last_idx; + move_to = 0; + } else { + mesh_set_line(mesh, last_idx-1, last_idx); + } + } + } + } + /*close color per vertex*/ + if (do_close && !gf_vec_equal(mesh->vertices[first_idx].pos, mesh->vertices[last_idx].pos) ) { + mesh_set_line(mesh, last_idx, first_idx); + } + if (coord2D) mesh->flags |= MESH_IS_2D; + if (colorRGBA) mesh->flags |= MESH_HAS_ALPHA; + mesh_update_bounds(mesh); +} + +void mesh_new_ps(GF_Mesh *mesh, GF_Node *__coord, GF_Node *__color) +{ + u32 c_count, i; + Bool has_color; + SFVec3f pt; + SFColorRGBA colRGBA; + M_Coordinate2D *coord2D = (M_Coordinate2D*) __coord; + M_Coordinate *coord = (M_Coordinate*) __coord; + M_Color *colorRGB = (M_Color *) __color; + X_ColorRGBA *colorRGBA = (X_ColorRGBA *) __color; + + if (__coord && (gf_node_get_tag(__coord) == TAG_MPEG4_Coordinate2D)) { + coord = NULL; + } else { + coord2D = NULL; + } + + if (!coord2D && !coord) return; + c_count = coord2D ? coord2D->point.count : coord->point.count; + if (!c_count) return; + + mesh_reset(mesh); + mesh->mesh_type = MESH_POINTSET; + + has_color = 0; + if (__color) { + if (gf_node_get_tag(__color)==TAG_X3D_ColorRGBA) { + colorRGB = NULL; + has_color = (colorRGBA->color.count) ? 1 : 0; + } else { + colorRGBA = NULL; + has_color = (colorRGB->color.count) ? 1 : 0; + } + } + if (has_color) mesh->flags |= MESH_HAS_COLOR; + + colRGBA.red = colRGBA.green = colRGBA.blue = colRGBA.alpha = FIX_ONE; + + for (i=0; ipoint.vals[i].x; + pt.y = coord2D->point.vals[i].y; + pt.z = 0; + } else { + pt = coord->point.vals[i]; + } + mesh_set_point(mesh, pt.x, pt.y, pt.z, colRGBA); + mesh_set_index(mesh, mesh->v_count-1); + } + mesh_update_bounds(mesh); +} + +/*structures used for normal smoothing*/ +struct face_info +{ + /*face normal*/ + SFVec3f nor; + u32 idx_alloc; + /*nb pts in face*/ + u32 idx_count; + /*indexes of face vertices in the pt_info structure*/ + u32 *idx; +}; +/*for each pt in the mesh*/ +struct pt_info +{ + u32 face_alloc; + /*number of faces this point belongs to*/ + u32 face_count; + /*idx of face in face_info structure*/ + u32 *faces; +}; + +void register_point_in_face(struct face_info *fi, u32 pt_index) +{ + if (fi->idx_count==fi->idx_alloc) { + fi->idx_alloc += 10; + fi->idx = (u32*)realloc(fi->idx, sizeof(u32)*fi->idx_alloc); + } + fi->idx[fi->idx_count] = pt_index; + fi->idx_count++; +} + +void register_face_in_point(struct pt_info *pi, u32 face_index) +{ + if (pi->face_count==pi->face_alloc) { + pi->face_alloc += 10; + pi->faces = (u32*)realloc(pi->faces, sizeof(u32)*pi->face_alloc); + } + pi->faces[pi->face_count] = face_index; + pi->face_count++; +} + +static GFINLINE SFVec3f smooth_face_normals(struct pt_info *pts, u32 nb_pts, struct face_info *faces, u32 nb_faces, + u32 pt_idx_in_face, u32 face_idx, Fixed creaseAngleCos) +{ + u32 i=0; + SFVec3f nor; + struct face_info *fi = &faces[face_idx]; + struct pt_info *pi = &pts[fi->idx[pt_idx_in_face]]; + + nor = fi->nor; + /*for each face adjacent this point/face*/ + for (i=0; iface_count; i++) { + /*current face, skip*/ + if (pi->faces[i]==face_idx) continue; + if (gf_vec_dot(fi->nor, faces[pi->faces[i]].nor) > creaseAngleCos) { + gf_vec_add(nor, nor, faces[pi->faces[i]].nor); + } + + } + gf_vec_norm(&nor); + return nor; +} + + +#define GET_IDX(idx, array) \ + if (idxcount && (array->vals[idx]>=0) ) index = array->vals[idx]; \ + else if (idxpoint.count : coord->point.count; + if (!c_count) return; + + if (normal && normalIndex) { + if (!normalIndex->vals) normalIndex = coordIndex; + nor_count = normalIndex->count ? normalIndex->count : c_count; + has_normal = normal->vector.count ? 1 : 0; + } else { + nor_count = 0; + nor.x = nor.y = 0; + nor.z = FIX_ONE; + has_normal = 0; + } + + has_tex = txcoord ? 1 : 0; + if (has_tex && !texCoordIndex->vals) texCoordIndex = coordIndex; + + mesh_reset(mesh); + memset(&colRGBA, 0, sizeof(SFColorRGBA)); + /*compute bounds - note we assume all points in the IFS coordinate are used*/ + s_axis = t_axis = 0; + if (!has_tex) { + for (i=0; ipoint.vals[i].x, coord2D->point.vals[i].y, 0, colRGBA); + else mesh_set_point(mesh, coord->point.vals[i].x, coord->point.vals[i].y, coord->point.vals[i].z, colRGBA); + } + mesh_update_bounds(mesh); + gf_vec_diff(bounds, mesh->bounds.max_edge, mesh->bounds.min_edge); + center = mesh->bounds.min_edge; + if ( (bounds.x >= bounds.y) && (bounds.x >= bounds.z) ) { + s_axis = 0; + t_axis = (bounds.y >= bounds.z) ? 1 : 2; + } + else if ( (bounds.y >= bounds.x) && (bounds.y >= bounds.z) ) { + s_axis = 1; + t_axis = (bounds.x >= bounds.z) ? 0 : 2; + } + else { + s_axis = 2; + t_axis = (bounds.x >= bounds.y) ? 0 : 1; + } + } + + has_color = 0; + if (__color) { + if (gf_node_get_tag(__color)==TAG_X3D_ColorRGBA) { + colorRGB = NULL; + has_color = (colorRGBA->color.count) ? 1 : 0; + } else { + colorRGBA = NULL; + has_color = (colorRGB->color.count) ? 1 : 0; + } + } + n = 0; + if (has_color) { + if (!colorPerVertex) { + index = colorIndex->count ? colorIndex->vals[0] : 0; + MESH_GET_COL(colRGBA, index); + } else { + if (!colorIndex->vals) colorIndex = coordIndex; + } + } + col_count = colorIndex->count ? colorIndex->count : c_count; + + if (has_normal && !normalPerVertex) { + index = normalIndex->count ? normalIndex->vals[0] : 0; + if (index < nor_count) nor = normal->vector.vals[index]; + } + + move_to = 1; + count = coordIndex->count; + has_coord = count ? 1 : 0; + if (!has_coord) count = c_count; + + smooth_normals = (!has_normal && coord && (creaseAngle > FIX_EPSILON)) ? 1 : 0; + + /*build face list*/ + if (!has_coord) { + face_count = 1; + } else { + u32 pt_p_face; + face_count = 0; + pt_p_face = 0; + for (i=0; ivals[i] == -1) face_count++; + } + /*don't forget last face*/ + if (coordIndex->vals[count-1] != -1) face_count++; + } + + faces = (GF_Mesh**)malloc(sizeof(GF_Mesh *)*face_count); + for (i=0; iflags = MESH_IS_2D; + } + faces_info = NULL; + pts_info = NULL; + + /*alloc face & normals tables*/ + if (smooth_normals) { + faces_info = (struct face_info*)malloc(sizeof(struct face_info)*face_count); + memset(faces_info, 0, sizeof(struct face_info)*face_count); + pts_info = (struct pt_info*)malloc(sizeof(struct pt_info)*c_count); + memset(pts_info, 0, sizeof(struct pt_info)*c_count); + } + + cur_face = 0; + first_idx = last_idx = 0; + for (i=0; ivals[i] == -1) { + move_to = 1; + n++; + if (has_color && !colorPerVertex) { + GET_IDX(n, colorIndex); + MESH_GET_COL(colRGBA, index); + } + if (has_normal && !normalPerVertex) { + GET_IDX(n, normalIndex); + if (index < nor_count) nor = normal->vector.vals[index]; + } + if (faces[cur_face]->v_count<3) faces[cur_face]->v_count=0; + /*compute face normal - watchout for colinear vectors*/ + else if (smooth_normals) { + SFVec3f v1, v2, fn; + u32 k=2; + gf_vec_diff(v1, faces[cur_face]->vertices[1].pos, faces[cur_face]->vertices[0].pos); + while (kv_count) { + gf_vec_diff(v2, faces[cur_face]->vertices[k].pos, faces[cur_face]->vertices[0].pos); + fn = gf_vec_cross(v1, v2); + if (gf_vec_len(fn)) { + gf_vec_norm(&fn); + faces_info[cur_face].nor = fn; + break; + } + k++; + } + } + cur_face++; + } else { + if (has_color && colorPerVertex) { + GET_IDX(i, colorIndex); + MESH_GET_COL(colRGBA, index); + } + if (has_normal && normalPerVertex) { + GET_IDX(i, normalIndex); + if (index < normal->vector.count) { + nor = normal->vector.vals[index]; + } + } + + if (has_coord) index = coordIndex->vals[i]; + else index = i; + if (index < c_count) { + if (coord2D) { + pt.x = coord2D->point.vals[index].x; + pt.y = coord2D->point.vals[index].y; + pt.z = 0; + } else { + pt = coord->point.vals[index]; + } + /*update face to point and point to face structures*/ + if (smooth_normals) { + register_point_in_face(&faces_info[cur_face], index); + register_face_in_point(&pts_info[index], cur_face); + } + } + + if (has_tex) { + GET_IDX(i, texCoordIndex); + if (index < txcoord->point.count) tx = txcoord->point.vals[index]; + } else { + SFVec3f v; + gf_vec_diff(v, pt, center); + tx.x = tx.y = 0; + if (s_axis==0) tx.x = gf_divfix(v.x, bounds.x); + else if (s_axis==1) tx.x = gf_divfix(v.y, bounds.y); + else if (s_axis==2) tx.x = gf_divfix(v.z, bounds.z); + if (t_axis==0) tx.y = gf_divfix(v.x, bounds.x); + else if (t_axis==1) tx.y = gf_divfix(v.y, bounds.y); + else if (t_axis==2) tx.y = gf_divfix(v.z, bounds.z); + } + + mesh_set_vertex_v(faces[cur_face], pt, nor, tx, colRGBA); + } + } + + /*generate normals*/ + if (!has_normal && coord) { + u32 j; + if (smooth_normals) { + Fixed cosCrease; + /*we only support 0->PI, whatever exceeds is smoothest*/ + if (creaseAngle>GF_PI) cosCrease = -FIX_ONE; + else cosCrease = gf_cos(creaseAngle); + + for (i=0; iv_count; j++) { + SFVec3f n = smooth_face_normals(pts_info, c_count, faces_info, face_count, j, i, cosCrease); + MESH_SET_NORMAL(faces[i]->vertices[j], n); + } } + + if (faces_info) { + for (i=0; iflags |= MESH_IS_SMOOTHED; + } else { + for (i=0; ivertices[1].pos, faces[i]->vertices[0].pos); + gf_vec_diff(v2, faces[i]->vertices[2].pos, faces[i]->vertices[0].pos); + n = gf_vec_cross(v1, v2); + if (!n.x && !n.y && !n.z) n.z = FIX_ONE; + else gf_vec_norm(&n); + for (j=0; jv_count; j++) + MESH_SET_NORMAL(faces[i]->vertices[j], n); + } + } + } + + mesh_reset(mesh); + mesh->mesh_type = MESH_TRIANGLES; + if (has_color) mesh->flags |= MESH_HAS_COLOR; + + for (i=0; iv_count) TesselateFaceMesh(mesh, faces[i]); + mesh_free(faces[i]); + } + free(faces); + mesh_update_bounds(mesh); + + if (!coord2D) gf_mesh_build_aabbtree(mesh); + + if (colorRGBA) mesh->flags |= MESH_HAS_ALPHA; + if (gen_tex_coords) mesh_generate_tex_coords(mesh, __texCoords); +} + +void mesh_new_ifs2d(GF_Mesh *mesh, GF_Node *node) +{ + M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node; + mesh_new_ifs_intern(mesh, ifs2D->coord, &ifs2D->coordIndex, + ifs2D->color, &ifs2D->colorIndex, ifs2D->colorPerVertex, + NULL, NULL, 0, ifs2D->texCoord, &ifs2D->texCoordIndex, 0); + + mesh->flags |= MESH_IS_2D; +} + +void mesh_new_ifs(GF_Mesh *mesh, GF_Node *node) +{ + M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node; + mesh_new_ifs_intern(mesh, ifs->coord, &ifs->coordIndex, ifs->color, &ifs->colorIndex, ifs->colorPerVertex, + ifs->normal, &ifs->normalIndex, ifs->normalPerVertex, ifs->texCoord, &ifs->texCoordIndex, ifs->creaseAngle); + + if (ifs->solid) mesh->flags |= MESH_IS_SOLID; + if (!ifs->ccw) mesh->flags |= MESH_IS_CW; +} + +void mesh_new_elevation_grid(GF_Mesh *mesh, GF_Node *node) +{ + u32 i, j, face_count, pt_count, zDimension, xDimension, cur_face, idx, pt_idx; + Bool has_normal, has_txcoord, has_color, smooth_normals; + GF_Mesh **faces; + GF_Vertex vx; + SFVec3f v1, v2, n; + struct face_info *faces_info; + struct pt_info *pts_info; + M_ElevationGrid *eg = (M_ElevationGrid *) node; + M_Normal *norm = (M_Normal *)eg->normal; + M_Color *colorRGB = (M_Color *)eg->color; + X_ColorRGBA *colorRGBA = (X_ColorRGBA *)eg->color; + SFColorRGBA rgba; + M_TextureCoordinate *txc = (M_TextureCoordinate *)eg->texCoord; + + mesh_reset(mesh); + if (!eg->height.count || (eg->xDimension<2) || (eg->zDimension<2)) return; + + memset(&vx, 0, sizeof(GF_Vertex)); + memset(&rgba, 0, sizeof(SFColorRGBA)); + has_txcoord = txc ? txc->point.count : 0; + has_normal = norm ? norm->vector.count : 0; + has_color = 0; + if (eg->color) { + if (gf_node_get_tag(eg->color)==TAG_X3D_ColorRGBA) { + colorRGB = NULL; + has_color = colorRGBA->color.count ? 1 : 0; + } else { + colorRGBA = NULL; + has_color = colorRGB->color.count ? 1 : 0; + } + } + face_count = (eg->xDimension-1) * (eg->zDimension-1); + pt_count = eg->xDimension * eg->zDimension; + if (pt_count>eg->height.count) return; + + smooth_normals = (!has_normal && (eg->creaseAngle > FIX_EPSILON)) ? 1 : 0; + + faces = NULL; + faces_info = NULL; + pts_info = NULL; + + zDimension = (u32) eg->zDimension; + xDimension = (u32) eg->xDimension; + cur_face = 0; + pt_idx = 0; + + /*basic case: nothing specified but the points*/ + if (!smooth_normals && !has_color && !has_normal && !has_txcoord) { + for (j=0; jzSpacing * j; + vx.pos.y = eg->height.vals[i +j*xDimension]; + vx.pos.x = eg->xSpacing * i; + vx.texcoords.x = INT2FIX(i) / (xDimension - 1); + vx.texcoords.y = INT2FIX(j) / (zDimension - 1); + + mesh_set_vertex_vx(mesh, &vx); + } + } + for (j=0; jvertices[i+z0].pos, mesh->vertices[i+z0+1].pos); + gf_vec_diff(v2, mesh->vertices[i+z1+1].pos, mesh->vertices[i+z0+1].pos); + n = gf_vec_cross(v1, v2); + gf_vec_norm(&n); + MESH_SET_NORMAL(mesh->vertices[i+z0], n); MESH_SET_NORMAL(mesh->vertices[i+z0+1], n); + MESH_SET_NORMAL(mesh->vertices[i+z1], n); MESH_SET_NORMAL(mesh->vertices[i+z1+1], n); + } + } + mesh->mesh_type = MESH_TRIANGLES; + mesh_update_bounds(mesh); + if (!eg->ccw) mesh->flags |= MESH_IS_CW; + if (eg->solid) mesh->flags |= MESH_IS_SOLID; + gf_mesh_build_aabbtree(mesh); + return; + } + + /*alloc face & normals tables*/ + if (smooth_normals) { + faces = (GF_Mesh **)malloc(sizeof(GF_Mesh *)*face_count); + faces_info = (struct face_info*)malloc(sizeof(struct face_info)*face_count); + memset(faces_info, 0, sizeof(struct face_info)*face_count); + pts_info = (struct pt_info*)malloc(sizeof(struct pt_info)*pt_count); + memset(pts_info, 0, sizeof(struct pt_info)*pt_count); + faces[cur_face] = new_mesh(); + } + + for (j=0; jcolorPerVertex) { + idx = i + j * (xDimension-1); + MESH_GET_COL(rgba, idx); + vx.color = MESH_MAKE_COL(rgba); + } + /*get face normal*/ + if (has_normal && !eg->normalPerVertex) { + idx = i + j * (xDimension-1); + if (idxvector.count) { + SFVec3f n = norm->vector.vals[idx]; + gf_vec_norm(&n); + MESH_SET_NORMAL(vx, n); + } + } + + for (k=0; k<2; k++) { + vx.pos.z = eg->zSpacing * (j+k); + for (l=0; l<2; l++) { + + vx.pos.y = eg->height.vals[(i+l) +(j+k)*xDimension]; + vx.pos.x = eg->xSpacing * (i+l); + + /*get color per vertex*/ + if (has_color && eg->colorPerVertex) { + idx = i+l + (j+k) * xDimension; + MESH_GET_COL(rgba, idx); + vx.color = MESH_MAKE_COL(rgba); + } + /*get tex coord*/ + if (!has_txcoord) { + vx.texcoords.x = INT2FIX(i+l) / (xDimension - 1); + vx.texcoords.y = INT2FIX(j+k) / (zDimension - 1); + } else { + idx = (i+l) +(j+k)*xDimension; + if (idxpoint.count) vx.texcoords = txc->point.vals[idx]; + } + /*get normal per vertex*/ + if (has_normal && eg->normalPerVertex) { + idx = (i+l) + (j+k) * xDimension; + if (idxvector.count) { + SFVec3f n = norm->vector.vals[idx]; + gf_vec_norm(&n); + MESH_SET_NORMAL(vx, n); + } + } + /*update face to point and point to face structures*/ + if (smooth_normals) { + mesh_set_vertex_vx(faces[cur_face], &vx); + register_point_in_face(&faces_info[cur_face], (i+l) + (j+k)*xDimension); + register_face_in_point(&pts_info[(i+l) + (j+k)*xDimension], cur_face); + } else { + mesh_set_vertex_vx(mesh, &vx); + } + } + + } + + /*compute face normal*/ + if (smooth_normals) { + mesh_set_triangle(faces[cur_face], 0, 2, 3); + mesh_set_triangle(faces[cur_face], 0, 3, 1); + gf_vec_diff(v1, faces[cur_face]->vertices[0].pos, faces[cur_face]->vertices[1].pos); + gf_vec_diff(v2, faces[cur_face]->vertices[3].pos, faces[cur_face]->vertices[1].pos); + faces_info[cur_face].nor = gf_vec_cross(v1, v2); + gf_vec_norm(&faces_info[cur_face].nor); + /*done with face*/ + cur_face++; + if (cur_facevertices[pt_idx+0].pos, mesh->vertices[pt_idx+1].pos); + gf_vec_diff(v2, mesh->vertices[pt_idx+3].pos, mesh->vertices[pt_idx+1].pos); + n = gf_vec_cross(v1, v2); + gf_vec_norm(&n); + MESH_SET_NORMAL(mesh->vertices[pt_idx+0], n); MESH_SET_NORMAL(mesh->vertices[pt_idx+1], n); + MESH_SET_NORMAL(mesh->vertices[pt_idx+2], n); MESH_SET_NORMAL(mesh->vertices[pt_idx+3], n); + } + pt_idx+=4; + } + } + } + + /*generate normals*/ + if (smooth_normals) { + Fixed cosCrease; + /*we only support 0->PI, whatever exceeds is smoothest*/ + if (eg->creaseAngle>GF_PI) cosCrease = -FIX_ONE; + else cosCrease = gf_cos(eg->creaseAngle); + + for (i=0; iv_count; j++) { + SFVec3f n = smooth_face_normals(pts_info, pt_count, faces_info, face_count, j, i, cosCrease); + MESH_SET_NORMAL(faces[i]->vertices[j], n); + } } + + if (faces_info) { + for (i=0; iflags |= MESH_IS_SMOOTHED; + + + for (i=0; iv_count) { + u32 init_idx; + GF_Mesh *face = faces[i]; + init_idx = mesh->v_count; + /*quads only*/ + mesh_set_vertex_vx(mesh, &face->vertices[0]); + mesh_set_vertex_vx(mesh, &face->vertices[1]); + mesh_set_vertex_vx(mesh, &face->vertices[2]); + mesh_set_vertex_vx(mesh, &face->vertices[3]); + mesh_set_triangle(mesh, init_idx, init_idx + 2, init_idx + 3); + mesh_set_triangle(mesh, init_idx, init_idx + 3, init_idx + 1); + } + mesh_free(faces[i]); + } + + /*destroy faces*/ + free(faces); + } + + mesh->mesh_type = MESH_TRIANGLES; + if (has_color) mesh->flags |= MESH_HAS_COLOR; + mesh_update_bounds(mesh); + if (!eg->ccw) mesh->flags |= MESH_IS_CW; + if (eg->solid) mesh->flags |= MESH_IS_SOLID; + if (colorRGBA) mesh->flags |= MESH_HAS_ALPHA; + gf_mesh_build_aabbtree(mesh); +} + + +typedef struct +{ + SFVec3f yaxis, zaxis, xaxis; +} SCP; + +typedef struct +{ + SFVec3f pt, yaxis, zaxis, xaxis; + u32 max_idx; +} SCPInfo; + +#define REGISTER_POINT_FACE(FACE_IDX) \ + { u32 fidx; \ + fidx = FACE_IDX; \ + mesh_set_vertex_vx(faces[fidx], &vx); \ + if (smooth_normals) { \ + register_point_in_face(&faces_info[fidx], pidx); \ + register_face_in_point(&pts_info[pidx], fidx); \ + } \ + } \ + + +#define NEAR_ZERO(__x) (ABS(__x)<=FIX_EPSILON) + +static void mesh_extrude_path_intern(GF_Mesh *mesh, GF_Path *path, MFVec3f *thespine, Fixed creaseAngle, Fixed min_cx, Fixed min_cy, Fixed width_cx, Fixed width_cy, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool tx_along_spine) +{ + GF_Mesh **faces; + GF_Vertex vx; + struct face_info *faces_info; + struct pt_info *pts_info; + GF_Matrix mx; + SCP *SCPs, SCPbegin, SCPend; + SCPInfo *SCPi; + Bool smooth_normals, spine_closed, check_first_spine_vec, do_close; + u32 i, j, k, nb_scp, nb_spine, face_count, pt_count, faces_per_cross, begin_face, end_face, face_spines, pts_per_cross, cur_pts_in_cross,cur, nb_pts, convexity; + SFVec3f *spine, v1, v2, n, spine_vec; + Fixed cross_len, spine_len, cur_cross, cur_spine; + SFRotation r; + SFVec2f scale; + + if (!path->n_contours) return; + if (path->n_points<2) return; + if (thespine->count<2) return; + + spine_closed = 0; + if (gf_vec_equal(thespine->vals[0], thespine->vals[thespine->count-1])) spine_closed = 1; + if (spine_closed && (thespine->count==2)) return; + + gf_path_flatten(path); + + memset(&vx, 0, sizeof(GF_Vertex)); + pts_per_cross = 0; + cross_len = 0; + cur = 0; + for (i=0; in_contours; i++) { + nb_pts = 1 + path->contours[i] - cur; + pts_per_cross += nb_pts; + v1.z = 0; + for (j=1; jpoints[j+cur].x - path->points[j-1+cur].x; + v1.y = path->points[j+cur].y - path->points[j-1+cur].y; + cross_len += gf_vec_len(v1); + } + } + + faces_per_cross = pts_per_cross - path->n_contours; + begin_face = end_face = 0; + face_spines = face_count = (thespine->count-1)*faces_per_cross; + if (begin_cap) { + begin_face = face_count; + face_count ++; + } + if (end_cap) { + end_face = face_count; + face_count ++; + } + + pt_count = pts_per_cross * thespine->count; + smooth_normals = NEAR_ZERO(creaseAngle) ? 0 : 1; + + faces = (GF_Mesh**)malloc(sizeof(GF_Mesh *)*face_count); + for (i=0; ivals; + nb_spine = thespine->count; + SCPs = (SCP *)malloc(sizeof(SCP) * nb_spine); + memset(SCPs, 0, sizeof(SCP) * nb_spine); + SCPi = (SCPInfo *) malloc(sizeof(SCPInfo) * nb_spine); + memset(SCPi, 0, sizeof(SCPInfo) * nb_spine); + + /*collect all # SCPs: + 1- if a spine has identical consecutive points with # orientation, these points use the same SCPs + 2- if 2 segs of the spine are colinear, they also use the same SCP + */ + SCPi[0].pt = spine[0]; + SCPi[0].max_idx = 0; + nb_scp=1; + spine_len = 0; + check_first_spine_vec = 1; + for (i=1; i= FIX_ONE-FIX_EPSILON) alpha = GF_PI2; + else if (spine_vec.x <= -FIX_ONE+FIX_EPSILON) alpha = -GF_PI2; + else alpha = gf_asin(spine_vec.x); + cos_a = gf_cos(alpha); + sin_a = spine_vec.x; + sin_g = 0; + if (NEAR_ZERO(cos_a)) gamma = 0; + else { + Fixed __abs; + gamma = gf_acos(gf_divfix(spine_vec.y, cos_a)); + sin_g = gf_sin(gamma); + __abs = gf_divfix(spine_vec.z, cos_a) + sin_g; + if (ABS(__abs) > ABS(sin_g) ) gamma *= -1; + } + cos_g = gf_cos(gamma); + if (NEAR_ZERO(cos_g)) { + cos_g = 0; + sin_g = FIX_ONE; + } else { + sin_g = gf_sin(gamma); + } + SCPi[0].yaxis.y = gf_mulfix(cos_a, sin_g); + SCPi[0].yaxis.z = gf_mulfix(cos_a, cos_g); + SCPi[0].yaxis.x = sin_a; + SCPi[0].zaxis.y = -gf_mulfix(sin_a, sin_g); + SCPi[0].zaxis.z = -gf_mulfix(sin_a, cos_g); + SCPi[0].zaxis.x = cos_a; + } + if (! NEAR_ZERO(spine_vec.z) ) { + if (spine_vec.z >= FIX_ONE-FIX_EPSILON) alpha = GF_PI2; + else if (spine_vec.z <= -FIX_ONE+FIX_EPSILON) alpha = -GF_PI2; + else alpha = gf_asin(spine_vec.z); + cos_a = gf_cos(alpha); + sin_a = spine_vec.z; + sin_g = 0; + if (NEAR_ZERO(cos_a) ) gamma = 0; + else { + Fixed __abs; + gamma = gf_acos(gf_divfix(spine_vec.y, cos_a)); + sin_g = gf_sin(gamma); + __abs = gf_divfix(spine_vec.x, cos_a) + sin_g; + if (ABS(__abs) > ABS(sin_g) ) gamma *= -1; + } + cos_g = gf_cos(gamma); + sin_g = gf_sin(gamma); + SCPi[0].yaxis.y = gf_mulfix(cos_a, sin_g); + SCPi[0].yaxis.x = gf_mulfix(cos_a, cos_g); + SCPi[0].yaxis.z = sin_a; + SCPi[0].zaxis.y = -gf_mulfix(sin_a, sin_g); + SCPi[0].zaxis.x = -gf_mulfix(sin_a, cos_g); + SCPi[0].zaxis.z = cos_a; + } + } + for (i=0; ixaxis = gf_vec_cross(curSCP->yaxis, curSCP->zaxis); + gf_vec_norm(&curSCP->xaxis); gf_vec_norm(&curSCP->yaxis); gf_vec_norm(&curSCP->zaxis); + + if (spine_ori && (icount)) r = spine_ori->vals[i]; + if (spine_scale && (icount)) scale = spine_scale->vals[i]; + + gf_mx_init(mx); + gf_mx_add_rotation(&mx, r.q, r.x, r.y, r.z); + gf_mx_apply_vec(&mx, &curSCP->xaxis); + gf_mx_apply_vec(&mx, &curSCP->yaxis); + gf_mx_apply_vec(&mx, &curSCP->zaxis); + } + + vx.texcoords.y = gf_divfix(cur_spine, spine_len); + + cur_pts_in_cross = 0; + cur_face_in_cross = 0; + cur = 0; + for (j=0; jn_contours; j++) { + Bool subpath_closed; + nb_pts = 1+path->contours[j] - cur; + cur_cross = 0; + subpath_closed = 0; + if ((path->points[cur].x==path->points[path->contours[j]].x) && (path->points[cur].y==path->points[path->contours[j]].y)) + subpath_closed = 1; + + for (k=0; kpoints[k+cur].x; + v1.z = path->points[k+cur].y; + + if (tx_along_spine) { + vx.texcoords.x = gf_divfix(cur_cross, cross_len); + } else { + vx.texcoords.x = gf_divfix(v1.x - min_cx, width_cx); + vx.texcoords.y = gf_divfix(v1.z - min_cy, width_cy); + } + + /*handle closed cross-section*/ + if (subpath_closed && (k+1==nb_pts)) { + pidx = cur_pts_in_cross + i*pts_per_cross; + if (do_close) pidx = cur_pts_in_cross; + + v1.x = path->points[cur].x; + v1.z = path->points[cur].y; + } + v1.x = gf_mulfix(v1.x, scale.x); + v1.z = gf_mulfix(v1.z, scale.y); + v1.y = 0; + vx.pos.x = gf_mulfix(v1.x, curSCP->xaxis.x) + gf_mulfix(v1.y, curSCP->yaxis.x) + gf_mulfix(v1.z, curSCP->zaxis.x) + spine[i].x; + vx.pos.y = gf_mulfix(v1.x, curSCP->xaxis.y) + gf_mulfix(v1.y, curSCP->yaxis.y) + gf_mulfix(v1.z, curSCP->zaxis.y) + spine[i].y; + vx.pos.z = gf_mulfix(v1.x, curSCP->xaxis.z) + gf_mulfix(v1.y, curSCP->yaxis.z) + gf_mulfix(v1.z, curSCP->zaxis.z) + spine[i].z; + + /*in current spine*/ + if (i+1points[k+1+cur].x - path->points[k+cur].x; + v1.y = path->points[k+1+cur].y - path->points[k+cur].y; + cur_cross += gf_vec_len(v1); + } + + } + cur_face_in_cross += nb_pts-1; + cur_pts_in_cross += nb_pts; + cur += nb_pts; + } + if (i+1vertices[1].pos, faces[i]->vertices[0].pos); + gf_vec_diff(v2, faces[i]->vertices[3].pos, faces[i]->vertices[0].pos); + n = gf_vec_cross(v1, v2); + gf_vec_norm(&n); + for (j=0; jv_count; j++) { + MESH_SET_NORMAL(faces[i]->vertices[j], n); + if (smooth_normals) faces_info[i].nor = n; + } + } + + + /*generate begin cap*/ + if (begin_face) { + SFVec3f n; + scale.x = scale.y = FIX_ONE; + if (spine_scale && spine_scale->count) scale = spine_scale->vals[0]; + + /*get first SCP after rotation*/ + SCPbegin = SCPs[0]; + + n = SCPbegin.yaxis; + gf_vec_norm(&n); + MESH_SET_NORMAL(vx, n); + + convexity = gf_polygone2d_get_convexity(path->points, path->n_points); + switch (convexity) { + case GF_POLYGON_CONVEX_CCW: + case GF_POLYGON_COMPLEX_CCW: + gf_vec_rev(vx.normal); + gf_vec_rev(n); + break; + } + + if (smooth_normals) { + faces_info[begin_face].nor = n; + assert(gf_vec_len(n)); + } + cur_pts_in_cross = 0; + cur = 0; + for (i=0; in_contours; i++) { + Bool subpath_closed = 0; + nb_pts = 1 + path->contours[i] - cur; + if ((path->points[cur].x==path->points[path->contours[i]].x) && (path->points[cur].y==path->points[path->contours[i]].y)) + subpath_closed = 1; + + for (j=0; jpoints[j+cur].x; + v1.z = path->points[j+cur].y; + vx.texcoords.x = gf_divfix(v1.x - min_cx, width_cx); + vx.texcoords.y = gf_divfix(v1.z - min_cy, width_cy); + /*handle closed cross-section*/ + if (subpath_closed && (j+1==nb_pts)) { + pidx = (pts_per_cross-1-cur_pts_in_cross); + v1.x = path->points[cur].x; + v1.z = path->points[cur].y; + } + v1.x = gf_mulfix(v1.x , scale.x); + v1.z = gf_mulfix(v1.z , scale.y); + v1.y = 0; + vx.pos.x = gf_mulfix(v1.x, SCPbegin.xaxis.x) + gf_mulfix(v1.y, SCPbegin.yaxis.x) + gf_mulfix(v1.z, SCPbegin.zaxis.x) + spine[0].x; + vx.pos.y = gf_mulfix(v1.x, SCPbegin.xaxis.y) + gf_mulfix(v1.y, SCPbegin.yaxis.y) + gf_mulfix(v1.z, SCPbegin.zaxis.y) + spine[0].y; + vx.pos.z = gf_mulfix(v1.x, SCPbegin.xaxis.z) + gf_mulfix(v1.y, SCPbegin.yaxis.z) + gf_mulfix(v1.z, SCPbegin.zaxis.z) + spine[0].z; + REGISTER_POINT_FACE(begin_face); + } + cur_pts_in_cross += nb_pts; + cur += nb_pts; + } + } + + /*generate end cap*/ + if (end_face) { + SFVec3f n; + scale.x = scale.y = FIX_ONE; + if (spine_scale && (nb_spine-1count)) scale = spine_scale->vals[nb_spine-1]; + /*get last SCP after rotation*/ + SCPend = SCPs[nb_spine-1]; + + n = SCPend.yaxis; + gf_vec_norm(&n); + MESH_SET_NORMAL(vx, n); + convexity = gf_polygone2d_get_convexity(path->points, path->n_points); + switch (convexity) { + case GF_POLYGON_CONVEX_CCW: + case GF_POLYGON_COMPLEX_CCW: + gf_vec_rev(vx.normal); + gf_vec_rev(n); + break; + } + + if (smooth_normals) { + faces_info[end_face].nor = n; + assert(gf_vec_len(n)); + } + cur_pts_in_cross = 0; + + cur = 0; + for (i=0; in_contours; i++) { + Bool subpath_closed = 0; + nb_pts = 1 + path->contours[i] - cur; + if ((path->points[cur].x==path->points[path->contours[i]].x) && (path->points[cur].y==path->points[path->contours[i]].y)) + subpath_closed = 1; + + for (j=0; jpoints[j+cur].x; + v1.z = path->points[j+cur].y; + vx.texcoords.x = gf_divfix(v1.x - min_cx, width_cx); + vx.texcoords.y = gf_divfix(v1.z - min_cy, width_cy); + /*handle closed cross-section*/ + if (subpath_closed && (j+1==nb_pts)) { + pidx = cur_pts_in_cross + (nb_spine-1)*pts_per_cross; + v1.x = path->points[cur].x; + v1.z = path->points[cur].y; + } + v1.x = gf_mulfix(v1.x, scale.x); + v1.z = gf_mulfix(v1.z, scale.y); + v1.y = 0; + vx.pos.x = gf_mulfix(v1.x, SCPend.xaxis.x) + gf_mulfix(v1.y, SCPend.yaxis.x) + gf_mulfix(v1.z, SCPend.zaxis.x) + spine[nb_spine-1].x; + vx.pos.y = gf_mulfix(v1.x, SCPend.xaxis.y) + gf_mulfix(v1.y, SCPend.yaxis.y) + gf_mulfix(v1.z, SCPend.zaxis.y) + spine[nb_spine-1].y; + vx.pos.z = gf_mulfix(v1.x, SCPend.xaxis.z) + gf_mulfix(v1.y, SCPend.yaxis.z) + gf_mulfix(v1.z, SCPend.zaxis.z) + spine[nb_spine-1].z; + + REGISTER_POINT_FACE(end_face); + } + cur_pts_in_cross += nb_pts; + cur += nb_pts; + } + } + + + if (smooth_normals) { + Fixed cosCrease; + /*we only support 0->PI, whatever exceeds is smoothest*/ + if (creaseAngle>GF_PI) cosCrease = -FIX_ONE; + else cosCrease = gf_cos(creaseAngle); + + for (i=0; iv_count; j++) { + SFVec3f n = smooth_face_normals(pts_info, pt_count, faces_info, face_count, j, i, cosCrease); + MESH_SET_NORMAL(faces[i]->vertices[j], n); + } } + + if (faces_info) { + for (i=0; iflags |= MESH_IS_SMOOTHED; + } + + mesh->mesh_type = MESH_TRIANGLES; + + for (i=0; iv_count) { + u32 init_idx; + GF_Mesh *face = faces[i]; + init_idx = mesh->v_count; + /*quads only*/ + mesh_set_vertex_vx(mesh, &face->vertices[0]); + mesh_set_vertex_vx(mesh, &face->vertices[1]); + mesh_set_vertex_vx(mesh, &face->vertices[2]); + mesh_set_vertex_vx(mesh, &face->vertices[3]); + mesh_set_triangle(mesh, init_idx + 0, init_idx + 1, init_idx + 3); + mesh_set_triangle(mesh, init_idx + 0, init_idx + 3, init_idx + 2); + } + mesh_free(faces[i]); + } + if (begin_face) { + if (path->n_contours>1) { +#ifdef GPAC_HAS_GLU + u32 *ptsPerFace = malloc(sizeof(u32)*path->n_contours); + /*we reversed begin cap!!!*/ + cur = 0; + for (i=0; in_contours; i++) { + nb_pts = 1+path->contours[i] - cur; + cur+=nb_pts; + ptsPerFace[i] = nb_pts; + } + TesselateFaceMeshComplex(mesh, faces[begin_face], path->n_contours, ptsPerFace); + free(ptsPerFace); +#endif + } else { + TesselateFaceMesh(mesh, faces[begin_face]); + } + mesh_free(faces[begin_face]); + } + if (end_face) { + if (path->n_contours>1) { +#ifdef GPAC_HAS_GLU + u32 *ptsPerFace = malloc(sizeof(u32)*path->n_contours); + cur = 0; + for (i=0; in_contours; i++) { + nb_pts = 1+path->contours[i] - cur; + cur+=nb_pts; + ptsPerFace[i] = nb_pts; + } + TesselateFaceMeshComplex(mesh, faces[end_face], path->n_contours, ptsPerFace); + free(ptsPerFace); +#endif + } else { + TesselateFaceMesh(mesh, faces[end_face]); + } + mesh_free(faces[end_face]); + } + free(faces); + free(SCPs); + + /*FIXME: this is correct except we need to handle path cw/ccw - until then no possibility + to get correct lighting*/ +/* + if (path->subpathlen && path->subpath[0]->closed && ((begin_face && end_face) || spine_closed)) + mesh->flags |= MESH_IS_SOLID; + else + mesh->flags &= ~MESH_IS_SOLID; +*/ +} + +void mesh_extrude_path_ext(GF_Mesh *mesh, GF_Path *path, MFVec3f *thespine, Fixed creaseAngle, Fixed min_cx, Fixed min_cy, Fixed width_cx, Fixed width_cy, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool tx_along_spine) +{ + mesh_extrude_path_intern(mesh, path, thespine, creaseAngle, min_cx, min_cy, width_cx, width_cy, begin_cap, end_cap, spine_ori, spine_scale, tx_along_spine); +} + +void mesh_extrude_path(GF_Mesh *mesh, GF_Path *path, MFVec3f *thespine, Fixed creaseAngle, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool tx_along_spine) +{ + GF_Rect rc; + gf_path_get_bounds(path, &rc); + mesh_extrude_path_intern(mesh, path, thespine, creaseAngle, rc.x, rc.y-rc.height, rc.width, rc.height, begin_cap, end_cap, spine_ori, spine_scale, tx_along_spine); + mesh_update_bounds(mesh); + gf_mesh_build_aabbtree(mesh); +} + +void mesh_new_extrusion(GF_Mesh *mesh, GF_Node *node) +{ + GF_Path *path; + u32 i; + M_Extrusion *ext = (M_Extrusion *)node; + + mesh_reset(mesh); + path = gf_path_new(); + gf_path_add_move_to(path, ext->crossSection.vals[0].x, ext->crossSection.vals[0].y); + for (i=1; icrossSection.count; i++) { + gf_path_add_line_to(path, ext->crossSection.vals[i].x, ext->crossSection.vals[i].y); + } + + mesh_extrude_path(mesh, path, &ext->spine, ext->creaseAngle, ext->beginCap, ext->endCap, &ext->orientation, &ext->scale, 1); + gf_path_del(path); + + mesh_update_bounds(mesh); + if (!ext->ccw) mesh->flags |= MESH_IS_CW; +} + + +#endif /* GPAC_DISABLE_3D*/ + diff --git a/src/compositor/mesh_collide.c b/src/compositor/mesh_collide.c new file mode 100644 index 0000000..673dbae --- /dev/null +++ b/src/compositor/mesh_collide.c @@ -0,0 +1,598 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include + +#ifndef GPAC_DISABLE_3D + +/* + AABB tree syntax&construction code is a quick extract from OPCODE (c) Pierre Terdiman, http://www.codercorner.com/Opcode.htm +*/ + +typedef struct +{ + /*max tree depth, 0 is unlimited*/ + u32 max_depth; + /*min triangles at node to split. 0 is full split (one triangle per leaf)*/ + u32 min_tri_limit; + /*one of the above type*/ + u32 split_type; + u32 depth, nb_nodes; +} AABSplitParams; + + + +static GFINLINE void update_node_bounds(GF_Mesh *mesh, AABBNode *node) +{ + u32 i, j; + Fixed mx, my, mz, Mx, My, Mz; + mx = my = mz = FIX_MAX; + Mx = My = Mz = FIX_MIN; + for (i=0; inb_idx; i++) { + IDX_TYPE *idx = &mesh->indices[3*node->indices[i]]; + for (j=0; j<3; j++) { + SFVec3f *v = &mesh->vertices[idx[j]].pos; + if (mx>v->x) mx=v->x; if (Mxx) Mx=v->x; + if (my>v->y) my=v->y; if (Myy) My=v->y; + if (mz>v->z) mz=v->z; if (Mzz) Mz=v->z; + } + } + node->min.x = mx; node->min.y = my; node->min.z = mz; + node->max.x = Mx; node->max.y = My; node->max.z = Mz; +} + +static GFINLINE u32 gf_vec_main_axis(SFVec3f v) +{ + Fixed *vals = &v.x; + u32 m = 0; + if(vals[1] > vals[m]) m = 1; + if(vals[2] > vals[m]) m = 2; + return m; +} + +static GFINLINE Fixed tri_get_center(GF_Mesh *mesh, u32 tri_idx, u32 axis) +{ + SFVec3f v; + IDX_TYPE *idx = &mesh->indices[3*tri_idx]; + /*compute center*/ + gf_vec_add(v, mesh->vertices[idx[0]].pos, mesh->vertices[idx[1]].pos); + gf_vec_add(v, v, mesh->vertices[idx[2]].pos); + v = gf_vec_scale(v, FIX_ONE/3); + return ((Fixed *)&v.x)[axis]; +} + +static GFINLINE u32 aabb_split(GF_Mesh *mesh, AABBNode *node, u32 axis) +{ + SFVec3f v; + Fixed split_at; + u32 num_pos, i; + + gf_vec_add(v, node->max, node->min); + v = gf_vec_scale(v, FIX_ONE/2); + + split_at = ((Fixed *)&v.x)[axis]; + + num_pos = 0; + + for (i=0; inb_idx; i++) { + IDX_TYPE idx = node->indices[i]; + Fixed tri_val = tri_get_center(mesh, idx, axis); + + if (tri_val > split_at) { + /*swap*/ + IDX_TYPE tmp_idx = node->indices[i]; + node->indices[i] = node->indices[num_pos]; + node->indices[num_pos] = tmp_idx; + num_pos++; + } + } + return num_pos; +} + +static void mesh_subdivide_aabbtree(GF_Mesh *mesh, AABBNode *node, AABSplitParams *aab_par) +{ + Bool do_split; + u32 axis, num_pos, i, j; + SFVec3f extend; + + /*update mesh depth*/ + aab_par->depth ++; + + /*done*/ + if (node->nb_idx==1) return; + if (node->nb_idx <= aab_par->min_tri_limit) return; + if (aab_par->max_depth == aab_par->depth) { + aab_par->depth --; + return; + } + do_split = 1; + + gf_vec_diff(extend, node->max, node->max); + extend = gf_vec_scale(extend, FIX_ONE/2); + axis = gf_vec_main_axis(extend); + + num_pos = 0; + if (aab_par->split_type==AABB_LONGEST) { + num_pos = aabb_split(mesh, node, axis); + } + else if (aab_par->split_type==AABB_BALANCED) { + Fixed res[3]; + num_pos = aabb_split(mesh, node, 0); + res[0] = gf_divfix(INT2FIX(num_pos), INT2FIX(node->nb_idx)) - FIX_ONE/2; res[0] = gf_mulfix(res[0], res[0]); + num_pos = aabb_split(mesh, node, 1); + res[1] = gf_divfix(INT2FIX(num_pos), INT2FIX(node->nb_idx)) - FIX_ONE/2; res[1] = gf_mulfix(res[1], res[1]); + num_pos = aabb_split(mesh, node, 2); + res[2] = gf_divfix(INT2FIX(num_pos), INT2FIX(node->nb_idx)) - FIX_ONE/2; res[2] = gf_mulfix(res[2], res[2]);; + + axis = 0; + if (res[1] < res[axis]) axis = 1; + if (res[2] < res[axis]) axis = 2; + num_pos = aabb_split(mesh, node, axis); + } + else if (aab_par->split_type==AABB_BEST_AXIS) { + u32 sorted[] = { 0, 1, 2 }; + Fixed *Keys = (Fixed *) &extend.x; + for (j=0; j<3; j++) { + for (i=0; i<2; i++) { + if (Keys[sorted[i]] < Keys[sorted[i+1]]) { + u32 tmp = sorted[i]; + sorted[i] = sorted[i+1]; + sorted[i+1] = tmp; + } + } + } + axis = 0; + do_split = 0; + while (!do_split && (axis!=3)) { + num_pos = aabb_split(mesh, node, sorted[axis]); + // Check the subdivision has been successful + if (!num_pos || (num_pos==node->nb_idx)) axis++; + else do_split = 1; + } + } + else if (aab_par->split_type==AABB_SPLATTER) { + SFVec3f means, vars; + means.x = means.y = means.z = 0; + for (i=0; inb_idx; i++) { + IDX_TYPE idx = node->indices[i]; + means.x += tri_get_center(mesh, idx, 0); + means.y += tri_get_center(mesh, idx, 1); + means.z += tri_get_center(mesh, idx, 2); + } + means = gf_vec_scale(means, gf_invfix(node->nb_idx)); + + vars.x = vars.y = vars.z = 0; + for (i=0; inb_idx; i++) { + IDX_TYPE idx = node->indices[i]; + Fixed cx = tri_get_center(mesh, idx, 0); + Fixed cy = tri_get_center(mesh, idx, 1); + Fixed cz = tri_get_center(mesh, idx, 2); + vars.x += gf_mulfix(cx - means.x, cx - means.x); + vars.y += gf_mulfix(cy - means.y, cy - means.y); + vars.z += gf_mulfix(cz - means.z, cz - means.z); + } + vars = gf_vec_scale(vars, gf_invfix(node->nb_idx-1) ); + axis = gf_vec_main_axis(vars); + num_pos = aabb_split(mesh, node, axis); + } + else if (aab_par->split_type==AABB_FIFTY) { + do_split = 0; + } + + if (!num_pos || (num_pos==node->nb_idx)) do_split = 0; + + if (!do_split) { + if (aab_par->split_type==AABB_FIFTY) { + num_pos = node->nb_idx/2; + } else { + return; + } + } + aab_par->nb_nodes += 2; + + GF_SAFEALLOC(node->pos, AABBNode); + node->pos->indices = &node->indices[0]; + node->pos->nb_idx = num_pos; + update_node_bounds(mesh, node->pos); + mesh_subdivide_aabbtree(mesh, node->pos, aab_par); + + GF_SAFEALLOC(node->neg, AABBNode); + node->neg->indices = &node->indices[num_pos]; + node->neg->nb_idx = node->nb_idx - num_pos; + update_node_bounds(mesh, node->neg); + mesh_subdivide_aabbtree(mesh, node->neg, aab_par); + + aab_par->depth --; +} + + +void gf_mesh_build_aabbtree(GF_Mesh *mesh) +{ + u32 i, nb_idx; + AABSplitParams pars; + + memset(&pars, 0, sizeof(pars)); + pars.min_tri_limit = 8; + pars.max_depth = 0; + pars.split_type = AABB_BALANCED; + + if (mesh->i_count <= pars.min_tri_limit) return; + + nb_idx = mesh->i_count / 3; + mesh->aabb_indices = (IDX_TYPE*)malloc(sizeof(IDX_TYPE) * nb_idx); + for (i=0; iaabb_indices[i] = i; + + GF_SAFEALLOC(mesh->aabb_root, AABBNode); + mesh->aabb_root->min = mesh->bounds.min_edge; + mesh->aabb_root->max = mesh->bounds.max_edge; + mesh->aabb_root->indices = mesh->aabb_indices; + mesh->aabb_root->nb_idx = nb_idx; + pars.nb_nodes = 1; + pars.depth = 0; + mesh_subdivide_aabbtree(mesh, mesh->aabb_root, &pars); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Mesh] AABB tree done - %d nodes depth %d - size %d bytes\n", pars.nb_nodes, pars.depth, sizeof(AABBNode)*pars.nb_nodes)); +} + + +static void ray_hit_triangle_get_u_v(GF_Ray *ray, GF_Vec *v0, GF_Vec *v1, GF_Vec *v2, Fixed *u, Fixed *v) +{ + Fixed det; + GF_Vec edge1, edge2, tvec, pvec, qvec; + /* find vectors for two edges sharing vert0 */ + gf_vec_diff(edge1, *v1, *v0); + gf_vec_diff(edge2, *v2, *v0); + /* begin calculating determinant - also used to calculate U parameter */ + pvec = gf_vec_cross(ray->dir, edge2); + /* if determinant is near zero, ray lies in plane of triangle */ + det = gf_vec_dot(edge1, pvec); + /* calculate distance from vert0 to ray origin */ + gf_vec_diff(tvec, ray->orig, *v0); + /* calculate U parameter and test bounds */ + (*u) = gf_divfix(gf_vec_dot(tvec, pvec), det); + /* prepare to test V parameter */ + qvec = gf_vec_cross(tvec, edge1); + /* calculate V parameter and test bounds */ + (*v) = gf_divfix(gf_vec_dot(ray->dir, qvec), det); +} + +Bool gf_mesh_aabb_ray_hit(GF_Mesh *mesh, AABBNode *n, GF_Ray *ray, Fixed *closest, SFVec3f *outPoint, SFVec3f *outNormal, SFVec2f *outTexCoords) +{ + Bool inters; + Fixed dist; + SFVec3f v1, v2; + u32 i, inters_idx; + + /*check bbox intersection*/ + inters = gf_ray_hit_box(ray, n->min, n->max, NULL); + if (!inters) return 0; + + if (n->pos) { + /*we really want to check all possible intersections to get the closest point on ray*/ + Bool res = gf_mesh_aabb_ray_hit(mesh, n->pos, ray, closest, outPoint, outNormal, outTexCoords); + res += gf_mesh_aabb_ray_hit(mesh, n->neg, ray, closest, outPoint, outNormal, outTexCoords); + return res; + + } + + + inters_idx = 0; + inters = 0; + dist = (*closest); + + /*leaf, check for all faces*/ + for (i=0; inb_idx; i++) { + Fixed res; + IDX_TYPE *idx = &mesh->indices[3*n->indices[i]]; + if (gf_ray_hit_triangle(ray, + &mesh->vertices[idx[0]].pos, &mesh->vertices[idx[1]].pos, &mesh->vertices[idx[2]].pos, + &res)) { + if ((res>0) && (resdir, dist); + gf_vec_add(*outPoint, ray->orig, *outPoint); + } + if (outNormal) { + IDX_TYPE *idx = &mesh->indices[3*n->indices[inters_idx]];; + if (mesh->flags & MESH_IS_SMOOTHED) { + gf_vec_diff(v1, mesh->vertices[idx[1]].pos, mesh->vertices[idx[0]].pos); + gf_vec_diff(v2, mesh->vertices[idx[2]].pos, mesh->vertices[idx[0]].pos); + *outNormal = gf_vec_cross(v1, v2); + gf_vec_norm(outNormal); + } else { + MESH_GET_NORMAL((*outNormal), mesh->vertices[idx[0]]); + } + } + if (outTexCoords) { + IDX_TYPE *idx = &mesh->indices[3*n->indices[inters_idx]];; + ray_hit_triangle_get_u_v(ray, + &mesh->vertices[idx[0]].pos, &mesh->vertices[idx[1]].pos, &mesh->vertices[idx[2]].pos, + &outTexCoords->x, &outTexCoords->y); + } + } + return inters; +} + +Bool gf_mesh_intersect_ray(GF_Mesh *mesh, GF_Ray *ray, SFVec3f *outPoint, SFVec3f *outNormal, SFVec2f *outTexCoords) +{ + Bool inters; + u32 i, inters_idx; + Fixed closest; + /*no intersection on linesets/pointsets*/ + if (mesh->mesh_type != MESH_TRIANGLES) return 0; + + /*use aabbtree*/ + if (mesh->aabb_root) { + closest = FIX_MAX; + return gf_mesh_aabb_ray_hit(mesh, mesh->aabb_root, ray, &closest, outPoint, outNormal, outTexCoords); + } + + /*check bbox intersection*/ + inters = gf_ray_hit_box(ray, mesh->bounds.min_edge, mesh->bounds.max_edge, NULL); + if (!inters) return 0; + + inters_idx = 0; + inters = 0; + closest = FIX_MAX; + for (i=0; ii_count; i+=3) { + Fixed res; + IDX_TYPE *idx = &mesh->indices[i]; + if (gf_ray_hit_triangle(ray, + &mesh->vertices[idx[0]].pos, &mesh->vertices[idx[1]].pos, &mesh->vertices[idx[2]].pos, + &res)) { + if ((res>0) && (resdir, closest); + gf_vec_add(*outPoint, ray->orig, *outPoint); + } + if (outNormal) { + IDX_TYPE *idx = &mesh->indices[inters_idx]; + if (mesh->flags & MESH_IS_SMOOTHED) { + SFVec3f v1, v2; + gf_vec_diff(v1, mesh->vertices[idx[1]].pos, mesh->vertices[idx[0]].pos); + gf_vec_diff(v2, mesh->vertices[idx[2]].pos, mesh->vertices[idx[0]].pos); + *outNormal = gf_vec_cross(v1, v2); + gf_vec_norm(outNormal); + } else { + MESH_GET_NORMAL((*outNormal), mesh->vertices[idx[0]]); + } + } + if (outTexCoords) { + SFVec2f txres; + IDX_TYPE *idx = &mesh->indices[inters_idx]; + txres.x = txres.y = 0; + txres.x += mesh->vertices[idx[0]].texcoords.x; + txres.x += mesh->vertices[idx[1]].texcoords.x; + txres.x += mesh->vertices[idx[2]].texcoords.x; + txres.y += mesh->vertices[idx[0]].texcoords.y; + txres.y += mesh->vertices[idx[1]].texcoords.y; + txres.y += mesh->vertices[idx[2]].texcoords.y; + outTexCoords->x = txres.x / 3; + outTexCoords->y = txres.y / 3; + } + } + return inters; +} + + +static GFINLINE Bool mesh_collide_triangle(GF_Ray *ray, SFVec3f *v0, SFVec3f *v1, SFVec3f *v2, Fixed *dist) +{ + Fixed u, v, det; + SFVec3f edge1, edge2, tvec, pvec, qvec; + /* find vectors for two edges sharing vert0 */ + gf_vec_diff(edge1, *v1, *v0); + gf_vec_diff(edge2, *v2, *v0); + /* begin calculating determinant - also used to calculate U parameter */ + pvec = gf_vec_cross(ray->dir, edge2); + /* if determinant is near zero, ray lies in plane of triangle */ + det = gf_vec_dot(edge1, pvec); + if (ABS(det) < FIX_EPSILON) return 0; + /* calculate distance from vert0 to ray origin */ + gf_vec_diff(tvec, ray->orig, *v0); + /* calculate U parameter and test bounds */ + u = gf_divfix(gf_vec_dot(tvec, pvec), det); + if ((u < 0) || (u > FIX_ONE)) return 0; + /* prepare to test V parameter */ + qvec = gf_vec_cross(tvec, edge1); + /* calculate V parameter and test bounds */ + v = gf_divfix(gf_vec_dot(ray->dir, qvec), det); + if ((v < 0) || (u + v > FIX_ONE)) return 0; + /* calculate t, ray intersects triangle */ + *dist = gf_divfix(gf_vec_dot(edge2, qvec), det); + return 1; +} + + +static GFINLINE Bool sphere_box_overlap(SFVec3f sc, Fixed sq_rad, SFVec3f bmin, SFVec3f bmax) +{ + Fixed tmp, s, d, ext; + d = 0; + tmp = sc.x - (bmin.x + bmax.x)/2; + ext = (bmax.x-bmin.x)/2; + s = tmp + ext; + if (s<0) d += gf_mulfix(s, s); + else { + s = tmp - ext; + if (s>0) d += gf_mulfix(s, s); + } + tmp = sc.y - (bmin.y + bmax.y)/2; + ext = (bmax.y-bmin.y)/2; + s = tmp + ext; + if (s<0) d += gf_mulfix(s, s); + else { + s = tmp - ext; + if (s>0) d += gf_mulfix(s, s); + } + tmp = sc.z - (bmin.z + bmax.z)/2; + ext = (bmax.z-bmin.z)/2; + s = tmp + ext; + if (s<0) d += gf_mulfix(s, s); + else { + s = tmp - ext; + if (s>0) d += gf_mulfix(s, s); + } + return (d<=sq_rad) ? 1 : 0; +} + +Bool gf_mesh_closest_face_aabb(GF_Mesh *mesh, AABBNode *node, SFVec3f pos, Fixed min_dist, Fixed min_sq_dist, Fixed *min_col_dist, SFVec3f *outPoint) +{ + GF_Ray r; + u32 i; + SFVec3f v1, v2, n, resn; + Bool inters, has_inter, need_norm = 0; + Fixed d; + if (!sphere_box_overlap(pos, min_sq_dist, node->min, node->max)) return 0; + if (node->pos) { + if (gf_mesh_closest_face_aabb(mesh, node->pos, pos, min_dist, min_sq_dist, min_col_dist, outPoint)) return 1; + return gf_mesh_closest_face_aabb(mesh, node->neg, pos, min_dist, min_sq_dist, min_col_dist, outPoint); + } + + need_norm = (mesh->flags & MESH_IS_SMOOTHED) ? 1 : 0, + r.orig = pos; + has_inter = 0; + for (i=0; inb_idx; i++) { + IDX_TYPE *idx = &mesh->indices[3*node->indices[i]]; + if (need_norm) { + gf_vec_diff(v1, mesh->vertices[idx[1]].pos, mesh->vertices[idx[0]].pos); + gf_vec_diff(v2, mesh->vertices[idx[2]].pos, mesh->vertices[idx[0]].pos); + n = gf_vec_cross(v1, v2); + gf_vec_norm(&n); + } else { + MESH_GET_NORMAL(n, mesh->vertices[idx[0]]); + } + + /*intersect inverse normal from position to face with face*/ + r.dir = n; + gf_vec_rev(r.dir); + inters = mesh_collide_triangle(&r, + &mesh->vertices[idx[0]].pos, &mesh->vertices[idx[1]].pos, &mesh->vertices[idx[2]].pos, + &d); + + if (inters) { + /*we're behind the face, get inverse normal*/ + if (d<0) { + d*= -1; + n = r.dir; + } + if (d<=(*min_col_dist)) { + has_inter = 1; + (*min_col_dist) = d; + resn = n; + } + } + } + if (has_inter) { + resn = gf_vec_scale(resn, -(*min_col_dist)); + gf_vec_add(*outPoint, pos, resn); + } + return has_inter; +} + +Bool gf_mesh_closest_face(GF_Mesh *mesh, SFVec3f pos, Fixed min_dist, SFVec3f *outPoint) +{ + GF_Ray r; + u32 i; + SFVec3f v1, v2, n, resn; + Bool inters, has_inter, need_norm; + Fixed d, dmax; + + /*check bounds*/ + gf_vec_diff(v1, mesh->bounds.center, pos); + if (gf_vec_len(v1)>min_dist+mesh->bounds.radius) return 0; + + if (mesh->aabb_root) { + d = min_dist * min_dist; + dmax = min_dist; + return gf_mesh_closest_face_aabb(mesh, mesh->aabb_root, pos, min_dist, d, &dmax, outPoint); + } + + need_norm = (mesh->flags & MESH_IS_SMOOTHED) ? 1 : 0, + + r.orig = pos; + has_inter = 0; + dmax = min_dist; + for (i=0; ii_count; i+=3) { + IDX_TYPE *idx = &mesh->indices[i]; + if (need_norm) { + gf_vec_diff(v1, mesh->vertices[idx[1]].pos, mesh->vertices[idx[0]].pos); + gf_vec_diff(v2, mesh->vertices[idx[2]].pos, mesh->vertices[idx[0]].pos); + n = gf_vec_cross(v1, v2); + gf_vec_norm(&n); + } else { + MESH_GET_NORMAL(n, mesh->vertices[idx[0]]); + n.x = mesh->vertices[idx[0]].normal.x; + n.y = mesh->vertices[idx[0]].normal.y; + n.z = mesh->vertices[idx[0]].normal.z; + } + + d = -gf_vec_dot(mesh->vertices[idx[0]].pos, n); + d += gf_vec_dot(r.orig, n); + if (fabs(d)>min_dist) continue; + + /*intersect inverse normal from position to face with face*/ + r.dir = n; + gf_vec_rev(r.dir); + inters = mesh_collide_triangle(&r, + &mesh->vertices[idx[0]].pos, &mesh->vertices[idx[1]].pos, &mesh->vertices[idx[2]].pos, + &d); + + if (inters) { + /*we're behind the face, get inverse normal*/ + if (d<0) { + d*= -1; + n = r.dir; + } + if (d<=dmax) { + has_inter = 1; + dmax = d; + resn = n; + } + } + } + if (has_inter) { + resn = gf_vec_scale(resn, -dmax); + gf_vec_add(*outPoint, pos, resn); + } + return has_inter; +} + +#endif /*GPAC_DISABLE_3D*/ diff --git a/src/compositor/mesh_tesselate.c b/src/compositor/mesh_tesselate.c new file mode 100644 index 0000000..8078149 --- /dev/null +++ b/src/compositor/mesh_tesselate.c @@ -0,0 +1,489 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#ifndef GPAC_DISABLE_3D + +/*for GPAC_HAS_GLU*/ +#include "gl_inc.h" + +#ifndef CALLBACK +#define CALLBACK +#endif + + +#ifdef GPAC_HAS_GLU + + +typedef struct +{ + /*for tesselation*/ + GLUtesselator *tess_obj; + GF_Mesh *mesh; + + /*vertex indices: we cannot use a static array because reallocating the array will likely change memory + address of indices, hence break triangulator*/ + GF_List *vertex_index; +} MeshTess; + +static void CALLBACK mesh_tess_begin(GLenum which) { assert(which==GL_TRIANGLES); } +static void CALLBACK mesh_tess_end(void) { } +static void CALLBACK mesh_tess_error(GLenum error_code) { GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Mesh] Tesselate error %s\n", gluErrorString(error_code))); } +/*only needed to force GL_TRIANGLES*/ +static void CALLBACK mesh_tess_edgeflag(GLenum flag) { } + +static void CALLBACK mesh_tess_vertex(void *vertexData, void *user_data) +{ + MeshTess *tess = (MeshTess *) user_data; + mesh_set_index(tess->mesh, *(u32*)vertexData); +} + +static void CALLBACK mesh_tess_combine(GLdouble coords[3], void* vertex_data[4], GLfloat weight[4], void** out_data, void *user_data) +{ + u32 i, idx; + u32 *new_idx; + SFVec3f n; + SFVec2f tx; + SFColor col; + + MeshTess *tess = (MeshTess *) user_data; + + col.red = col.green = col.blue = 0; + if (tess->mesh->flags & MESH_HAS_COLOR) { + for (i=0; i<4; i++) { + if (weight[i]) { + SFColorRGBA rgba; + Fixed _weight = FLT2FIX(weight[i]); + idx = * (u32 *) vertex_data[i]; + + MESH_GET_COLOR(rgba, tess->mesh->vertices[idx]); + + col.red += gf_mulfix(_weight, rgba.red); + col.green += gf_mulfix(_weight, rgba.green); + col.blue += gf_mulfix(_weight, rgba.blue); + } + } + } + + n.x = n.y = n.z = 0; + if (tess->mesh->flags & MESH_IS_2D) { + n.z = FIX_ONE; + } else { + for (i=0; i<4; i++) { + if (weight[i]) { + Fixed _weight = FLT2FIX(weight[i]); + SFVec3f _n; + idx = * (u32 *) vertex_data[i]; + MESH_GET_NORMAL(_n, tess->mesh->vertices[idx]); + n.x += gf_mulfix(_weight, _n.x); + n.y += gf_mulfix(_weight, _n.y); + n.z += gf_mulfix(_weight, _n.z); + } + } + } + tx.x = tx.y = 0; + if (!(tess->mesh->flags & MESH_NO_TEXTURE)) { + for (i=0; i<4; i++) { + if (weight[i]) { + Fixed _weight = FLT2FIX(weight[i]); + idx = * (u32 *) vertex_data[i]; + tx.x += gf_mulfix(_weight, tess->mesh->vertices[idx].texcoords.x); + tx.y += gf_mulfix(_weight, tess->mesh->vertices[idx].texcoords.y); + } + } + } + + new_idx = (u32 *) malloc(sizeof(u32)); + gf_list_add(tess->vertex_index, new_idx); + *new_idx = tess->mesh->v_count; + mesh_set_vertex(tess->mesh, FLT2FIX( (Float) coords[0]), FLT2FIX( (Float) coords[1]), FLT2FIX( (Float) coords[2]), n.x, n.y, n.z, tx.x, tx.y); + *out_data = new_idx; +} + +void TesselatePath(GF_Mesh *mesh, GF_Path *path, u32 outline_style) +{ + u32 i, j, cur, nb_pts; + u32 *idx; + Fixed w, h, min_y; + GF_Rect rc; + GLdouble vertex[3]; + MeshTess *tess; + if (!mesh || !path || !path->n_contours) return; + tess = malloc(sizeof(MeshTess)); + if (!tess) return; + memset(tess, 0, sizeof(MeshTess)); + tess->tess_obj = gluNewTess(); + if (!tess->tess_obj) { + free(tess); + return; + } + tess->vertex_index = gf_list_new(); + + mesh_reset(mesh); + mesh->flags |= MESH_IS_2D; + if (outline_style==1) mesh->flags |= MESH_NO_TEXTURE; + + tess->mesh = mesh; + gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex); + gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin); + gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end); + gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine); + gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error); + gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag); + + if (path->flags & GF_PATH_FILL_ZERO_NONZERO) gluTessProperty(tess->tess_obj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO); + + gluTessBeginPolygon(tess->tess_obj, tess); + gluTessNormal(tess->tess_obj, 0, 0, 1); + + gf_path_flatten(path); + gf_path_get_bounds(path, &rc); + + w = rc.width; + h = rc.height; + min_y = rc.y - h; + vertex[2] = 0; + /*since we're not sure whether subpaths overlaps or not, tesselate everything*/ + cur = 0; + for (i=0; in_contours; i++) { + nb_pts = 1+path->contours[i]-cur; + + gluTessBeginContour(tess->tess_obj); + + for (j=0; jpoints[cur+j]; + Fixed u = gf_divfix(pt.x - rc.x, w); + Fixed v = gf_divfix(pt.y - min_y, h); + + idx = (u32 *) malloc(sizeof(u32)); + *idx = mesh->v_count; + gf_list_add(tess->vertex_index, idx); + mesh_set_vertex(mesh, pt.x, pt.y, 0, 0, 0, FIX_ONE, u, v); + + vertex[0] = (Double) FIX2FLT(pt.x); + vertex[1] = (Double) FIX2FLT(pt.y); + gluTessVertex(tess->tess_obj, vertex, idx); + } + gluTessEndContour(tess->tess_obj); + cur+=nb_pts; + } + + gluTessEndPolygon(tess->tess_obj); + + gluDeleteTess(tess->tess_obj); + + while (gf_list_count(tess->vertex_index)) { + u32 *idx = gf_list_get(tess->vertex_index, 0); + gf_list_rem(tess->vertex_index, 0); + free(idx); + } + gf_list_del(tess->vertex_index); + free(tess); + + mesh->bounds.min_edge.x = rc.x; + mesh->bounds.min_edge.y = rc.y-rc.height; + mesh->bounds.max_edge.x = rc.x+rc.width; + mesh->bounds.max_edge.y = rc.y; + mesh->bounds.min_edge.z = mesh->bounds.max_edge.z = 0; + gf_bbox_refresh(&mesh->bounds); +} + +#else + +void TesselatePath(GF_Mesh *mesh, GF_Path *path, u32 outline_style) { } + +#endif + + + +#define GetPoint2D(pt, apt) \ + if (!direction) { pt.x = - apt.pos.z; pt.y = apt.pos.y; } \ + else if (direction==1) { pt.x = apt.pos.z; pt.y = apt.pos.x; } \ + else if (direction==2) { pt.x = apt.pos.x; pt.y = apt.pos.y; } \ + + +#define ConvCompare(delta) \ + ( (delta.x > 0) ? -1 : \ + (delta.x < 0) ? 1 : \ + (delta.y > 0) ? -1 : \ + (delta.y < 0) ? 1 : \ + 0 ) + +#define ConvGetPointDelta(delta, pprev, pcur ) \ + /* Given a previous point 'pprev', read a new point into 'pcur' */ \ + /* and return delta in 'delta'. */ \ + GetPoint2D(pcur, pts[iread]); iread++; \ + delta.x = pcur.x - pprev.x; \ + delta.y = pcur.y - pprev.y; \ + +#define ConvCross(p, q) gf_mulfix(p.x,q.y) - gf_mulfix(p.y,q.x); + +#define ConvCheckTriple \ + if ( (thisDir = ConvCompare(dcur)) == -curDir ) { \ + ++dirChanges; \ + /* if ( dirChanges > 2 ) return NotConvex; */ \ + } \ + curDir = thisDir; \ + cross = ConvCross(dprev, dcur); \ + if ( cross > 0 ) { \ + if ( angleSign == -1 ) return GF_POLYGON_COMPLEX; \ + angleSign = 1; \ + } \ + else if (cross < 0) { \ + if (angleSign == 1) return GF_POLYGON_COMPLEX; \ + angleSign = -1; \ + } \ + pSecond = pThird; \ + dprev.x = dcur.x; \ + dprev.y = dcur.y; \ + +u32 polygon_check_convexity(GF_Vertex *pts, u32 len, u32 direction) +{ + s32 curDir, thisDir = 0, dirChanges = 0, angleSign = 0; + u32 iread; + Fixed cross; + GF_Point2D pSecond, pThird, pSaveSecond; + GF_Point2D dprev, dcur; + + if (len<3) return GF_POLYGON_CONVEX_LINE; + + pSecond.x = pSecond.y = 0; + pThird = pSecond; + + GetPoint2D(pThird, pts[0]); + /* Get different point, return if less than 3 diff points. */ + if (len < 3 ) return GF_POLYGON_CONVEX_LINE; + iread = 1; + ConvGetPointDelta(dprev, pThird, pSecond); + pSaveSecond = pSecond; + /*initial direction */ + curDir = ConvCompare(dprev); + while ( iread < len) { + /* Get different point, break if no more points */ + ConvGetPointDelta(dcur, pSecond, pThird ); + if ( (dcur.x == 0) && (dcur.y == 0) ) continue; + /* Check current three points */ + ConvCheckTriple; + } + + /* Must check for direction changes from last vertex back to first */ + /* Prepare for 'ConvexCheckTriple' */ + GetPoint2D(pThird, pts[0]); + dcur.x = pThird.x - pSecond.x; + dcur.y = pThird.y - pSecond.y; + if ( ConvCompare(dcur) ) ConvCheckTriple; + + /* and check for direction changes back to second vertex */ + dcur.x = pSaveSecond.x - pSecond.x; + dcur.y = pSaveSecond.y - pSecond.y; + /* Don't care about 'pThird' now */ + ConvCheckTriple; + + /* Decide on polygon type given accumulated status */ + if ( dirChanges > 2 ) return GF_POLYGON_COMPLEX; + if ( angleSign > 0 ) return GF_POLYGON_CONVEX_CCW; + if ( angleSign < 0 ) return GF_POLYGON_CONVEX_CW; + return GF_POLYGON_CONVEX_LINE; +} + + +void TesselateFaceMesh(GF_Mesh *dest, GF_Mesh *orig) +{ + u32 poly_type, i, nb_pts, init_idx, direction; + Fixed max_nor_coord, c; + SFVec3f nor; +#ifdef GPAC_HAS_GLU + u32 *idx; + GLdouble vertex[3]; + MeshTess *tess; +#endif + + /*get normal*/ + if (orig->flags & MESH_IS_2D) { + nor.x = nor.y = 0; nor.z = FIX_ONE; + } else { + MESH_GET_NORMAL(nor, orig->vertices[0]); + } + + /*select projection direction*/ + direction = 0; + max_nor_coord = ABS(nor.x); + c = ABS(nor.y); + if (c>max_nor_coord) { + direction = 1; + max_nor_coord = c; + } + c = ABS(nor.z); + if (c>max_nor_coord) direction = 2; + + /*if this is a convex polygone don't triangulate*/ + poly_type = polygon_check_convexity(orig->vertices, orig->v_count, direction); + switch (poly_type) { + case GF_POLYGON_CONVEX_LINE: + /*do NOT try to make face CCW otherwise we loose front/back faces...*/ + case GF_POLYGON_CONVEX_CW: + case GF_POLYGON_CONVEX_CCW: + init_idx = dest->v_count; + nb_pts = orig->v_count; + for (i=0; ivertices[i]); + } + nb_pts -= 1; + for (i=1; itess_obj = gluNewTess(); + if (!tess->tess_obj) { + free(tess); + return; + } + tess->vertex_index = gf_list_new(); + + tess->mesh = dest; + gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex); + gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin); + gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end); + gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine); + gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error); + gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag); + + gluTessBeginPolygon(tess->tess_obj, tess); + gluTessBeginContour(tess->tess_obj); + + + for (i=0; iv_count; i++) { + idx = (u32 *) malloc(sizeof(u32)); + *idx = dest->v_count; + gf_list_add(tess->vertex_index, idx); + mesh_set_vertex_vx(dest, &orig->vertices[i]); + + vertex[0] = (Double) FIX2FLT(orig->vertices[i].pos.x); + vertex[1] = (Double) FIX2FLT(orig->vertices[i].pos.y); + vertex[2] = (Double) FIX2FLT(orig->vertices[i].pos.z); + gluTessVertex(tess->tess_obj, vertex, idx); + } + + gluTessEndContour(tess->tess_obj); + gluTessEndPolygon(tess->tess_obj); + gluDeleteTess(tess->tess_obj); + + while (gf_list_count(tess->vertex_index)) { + u32 *idx = gf_list_get(tess->vertex_index, 0); + gf_list_rem(tess->vertex_index, 0); + free(idx); + } + gf_list_del(tess->vertex_index); + free(tess); +#endif +} + + + +#ifdef GPAC_HAS_GLU + +void TesselateFaceMeshComplex(GF_Mesh *dest, GF_Mesh *orig, u32 nbFaces, u32 *ptsPerFaces) +{ + u32 i, cur_pt_faces, cur_face; + u32 *idx; + GLdouble vertex[3]; + MeshTess *tess; + + /*tesselate it*/ + tess = malloc(sizeof(MeshTess)); + if (!tess) return; + memset(tess, 0, sizeof(MeshTess)); + tess->tess_obj = gluNewTess(); + if (!tess->tess_obj) { + free(tess); + return; + } + tess->vertex_index = gf_list_new(); + + tess->mesh = dest; + gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex); + gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin); + gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end); + gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine); + gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error); + gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag); + + gluTessBeginPolygon(tess->tess_obj, tess); + gluTessBeginContour(tess->tess_obj); + + + cur_pt_faces = 0; + cur_face = 0; + for (i=0; iv_count; i++) { + + if (i>= cur_pt_faces + ptsPerFaces[cur_face]) { + cur_pt_faces += ptsPerFaces[cur_face]; + cur_face++; + if (cur_face>=nbFaces) break; + gluTessEndContour(tess->tess_obj); + gluTessBeginContour(tess->tess_obj); + } + + idx = (u32 *) malloc(sizeof(u32)); + *idx = dest->v_count; + gf_list_add(tess->vertex_index, idx); + mesh_set_vertex_vx(dest, &orig->vertices[i]); + + vertex[0] = (Double) FIX2FLT(orig->vertices[i].pos.x); + vertex[1] = (Double) FIX2FLT(orig->vertices[i].pos.y); + vertex[2] = (Double) FIX2FLT(orig->vertices[i].pos.z); + gluTessVertex(tess->tess_obj, vertex, idx); + } + + gluTessEndContour(tess->tess_obj); + gluTessEndPolygon(tess->tess_obj); + gluDeleteTess(tess->tess_obj); + + while (gf_list_count(tess->vertex_index)) { + u32 *idx = gf_list_get(tess->vertex_index, 0); + gf_list_rem(tess->vertex_index, 0); + free(idx); + } + gf_list_del(tess->vertex_index); + free(tess); +} +#endif + + +#endif /*GPAC_DISABLE_3D*/ + + diff --git a/src/compositor/mpeg4_animstream.c b/src/compositor/mpeg4_animstream.c new file mode 100644 index 0000000..3b9e4a4 --- /dev/null +++ b/src/compositor/mpeg4_animstream.c @@ -0,0 +1,196 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" + +#include + +typedef struct +{ + GF_Compositor *compositor; + GF_TimeNode time_handle; + Double start_time; + GF_MediaObject *stream; + MFURL current_url; +} AnimationStreamStack; + +static void animationstream_destroy(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + M_AnimationStream *as = (M_AnimationStream *)node; + AnimationStreamStack *st = (AnimationStreamStack *) gf_node_get_private(node); + + if (st->time_handle.is_registered) { + gf_sc_unregister_time_node(st->compositor, &st->time_handle); + } + if (st->stream && as->isActive) { + gf_mo_set_flag(st->stream, GF_MO_DISPLAY_REMOVE, 1); + gf_mo_stop(st->stream); + } + gf_sg_vrml_mf_reset(&st->current_url, GF_SG_VRML_MFURL); + free(st); + } +} + + +static void animationstream_check_url(AnimationStreamStack *stack, M_AnimationStream *as) +{ + if (!stack->stream) { + gf_sg_vrml_mf_reset(&stack->current_url, GF_SG_VRML_MFURL); + gf_sg_vrml_field_copy(&stack->current_url, &as->url, GF_SG_VRML_MFURL); + stack->stream = gf_mo_register((GF_Node *)as, &as->url, 0); + gf_sc_invalidate(stack->compositor, NULL); + + /*if changed while playing trigger*/ + if (as->isActive) { + gf_mo_play(stack->stream, 0, -1, 0); + gf_mo_set_speed(stack->stream, as->speed); + } + return; + } + /*check change*/ + if (gf_mo_url_changed(stack->stream, &as->url)) { + gf_sg_vrml_mf_reset(&stack->current_url, GF_SG_VRML_MFURL); + gf_sg_vrml_field_copy(&stack->current_url, &as->url, GF_SG_VRML_MFURL); + /*if changed while playing stop old source*/ + if (as->isActive) { + gf_mo_set_flag(stack->stream, GF_MO_DISPLAY_REMOVE, 1); + gf_mo_stop(stack->stream); + } + gf_mo_unregister((GF_Node *)as, stack->stream); + + stack->stream = gf_mo_register((GF_Node *)as, &as->url, 0); + /*if changed while playing play new source*/ + if (as->isActive) { + gf_mo_play(stack->stream, 0, -1, 0); + gf_mo_set_speed(stack->stream, as->speed); + } + gf_sc_invalidate(stack->compositor, NULL); + } +} + +static Fixed animationstream_get_speed(AnimationStreamStack *stack, M_AnimationStream *as) +{ + return gf_mo_get_speed(stack->stream, as->speed); +} +static Bool animationstream_get_loop(AnimationStreamStack *stack, M_AnimationStream *as) +{ + return gf_mo_get_loop(stack->stream, as->loop); +} + +static void animationstream_activate(AnimationStreamStack *stack, M_AnimationStream *as) +{ + animationstream_check_url(stack, as); + as->isActive = 1; + gf_node_event_out_str((GF_Node*)as, "isActive"); + + gf_mo_play(stack->stream, 0, -1, 0); + gf_mo_set_speed(stack->stream, as->speed); +} + +static void animationstream_deactivate(AnimationStreamStack *stack, M_AnimationStream *as) +{ + if (as->isActive) { + as->isActive = 0; + gf_node_event_out_str((GF_Node*)as, "isActive"); + } + if (stack->stream) { + if (gf_mo_url_changed(stack->stream, &as->url)) + gf_mo_set_flag(stack->stream, GF_MO_DISPLAY_REMOVE, 1); + gf_mo_stop(stack->stream); + } + stack->time_handle.needs_unregister = 1; + gf_sc_invalidate(stack->compositor, NULL); +} + +static void animationstream_update_time(GF_TimeNode *st) +{ + Double time; + M_AnimationStream *as = (M_AnimationStream *)st->udta; + AnimationStreamStack *stack = (AnimationStreamStack *)gf_node_get_private(st->udta); + + /*not active, store start time and speed*/ + if ( ! as->isActive) { + stack->start_time = as->startTime; + } + time = gf_node_get_scene_time(st->udta); + + if ((time < stack->start_time) || (stack->start_time < 0)) return; + + if (animationstream_get_speed(stack, as) && as->isActive) { + //if stoptime is reached (>startTime) deactivate + if ((as->stopTime > stack->start_time) && (time >= as->stopTime) ) { + animationstream_deactivate(stack, as); + return; + } + if (gf_mo_is_done(stack->stream)) { + if (animationstream_get_loop(stack, as)) { + gf_mo_restart(stack->stream); + } else if (gf_mo_should_deactivate(stack->stream)) { + animationstream_deactivate(stack, as); + } + } + } + + /*we're (about to be) active: VRML: + "A time-dependent node is inactive until its startTime is reached. When time now becomes greater than or + equal to startTime, an isActive TRUE event is generated and the time-dependent node becomes active */ + if (!as->isActive && !st->needs_unregister) animationstream_activate(stack, as); +} + + +void compositor_init_animationstream(GF_Compositor *compositor, GF_Node *node) +{ + AnimationStreamStack *st; + GF_SAFEALLOC(st, AnimationStreamStack); + st->compositor = compositor; + st->time_handle.UpdateTimeNode = animationstream_update_time; + st->time_handle.udta = node; + + gf_node_set_private(node, st); + gf_node_set_callback_function(node, animationstream_destroy); + + gf_sc_register_time_node(compositor, &st->time_handle); +} + + + +void compositor_animationstream_modified(GF_Node *node) +{ + M_AnimationStream *as = (M_AnimationStream *)node; + AnimationStreamStack *st = (AnimationStreamStack *) gf_node_get_private(node); + if (!st) return; + + /*update state if we're active*/ + if (as->isActive) + animationstream_update_time(&st->time_handle); + + /*check URL change*/ + animationstream_check_url(st, as); + + if (!st->time_handle.is_registered && !st->time_handle.needs_unregister) + gf_sc_register_time_node(st->compositor, &st->time_handle); + else + st->time_handle.needs_unregister = 0; +} diff --git a/src/compositor/mpeg4_audio.c b/src/compositor/mpeg4_audio.c new file mode 100644 index 0000000..1a1773e --- /dev/null +++ b/src/compositor/mpeg4_audio.c @@ -0,0 +1,610 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "nodes_stacks.h" + +typedef struct +{ + GF_AudioInput input; + GF_TimeNode time_handle; + Double start_time; + Bool set_duration, failure; +} AudioClipStack; + + +static void audioclip_activate(AudioClipStack *st, M_AudioClip *ac) +{ + if (gf_sc_audio_open(&st->input, &ac->url, 0, -1) != GF_OK) { + st->failure = 1; + return; + } + ac->isActive = 1; + gf_node_event_out_str((GF_Node *)ac, "isActive"); + + gf_mo_set_speed(st->input.stream, st->input.speed); + /*traverse all graph to get parent audio group*/ + gf_sc_invalidate(st->input.compositor, NULL); +} + +static void audioclip_deactivate(AudioClipStack *st, M_AudioClip *ac) +{ + gf_sc_audio_stop(&st->input); + ac->isActive = 0; + gf_node_event_out_str((GF_Node *)ac, "isActive"); + + st->time_handle.needs_unregister = 1; +} + +static void audioclip_traverse(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + M_AudioClip *ac = (M_AudioClip *)node; + AudioClipStack *st = (AudioClipStack *)gf_node_get_private(node); + + if (is_destroy) { + gf_sc_audio_stop(&st->input); + gf_sc_audio_unregister(&st->input); + if (st->time_handle.is_registered) { + gf_sc_unregister_time_node(st->input.compositor, &st->time_handle); + } + free(st); + return; + } + if (st->failure) return; + + /*check end of stream*/ + if (st->input.stream && st->input.stream_finished) { + if (gf_mo_get_loop(st->input.stream, ac->loop)) { + gf_sc_audio_restart(&st->input); + } else if (ac->isActive && gf_mo_should_deactivate(st->input.stream)) { + /*deactivate*/ + audioclip_deactivate(st, ac); + } + } + if (ac->isActive) { + gf_sc_audio_register(&st->input, (GF_TraverseState*)rs); + } + if (st->set_duration && st->input.stream) { + ac->duration_changed = gf_mo_get_duration(st->input.stream); + gf_node_event_out_str(node, "duration_changed"); + st->set_duration = 0; + } + + /*store mute flag*/ + st->input.is_muted = tr_state->switched_off; +} + +static void audioclip_update_time(GF_TimeNode *tn) +{ + Double time; + M_AudioClip *ac = (M_AudioClip *)tn->udta; + AudioClipStack *st = (AudioClipStack *)gf_node_get_private(tn->udta); + + if (st->failure) return; + if (! ac->isActive) { + st->start_time = ac->startTime; + st->input.speed = ac->pitch; + } + time = gf_node_get_scene_time(tn->udta); + if ((timestart_time) || (st->start_time<0)) return; + + if (ac->isActive) { + if ( (ac->stopTime > st->start_time) && (time>=ac->stopTime)) { + audioclip_deactivate(st, ac); + return; + } + } + if (!ac->isActive) audioclip_activate(st, ac); +} + + +void compositor_init_audioclip(GF_Compositor *compositor, GF_Node *node) +{ + AudioClipStack *st; + GF_SAFEALLOC(st, AudioClipStack); + gf_sc_audio_setup(&st->input, compositor, node); + + st->time_handle.UpdateTimeNode = audioclip_update_time; + st->time_handle.udta = node; + st->set_duration = 1; + + gf_node_set_private(node, st); + gf_node_set_callback_function(node, audioclip_traverse); + gf_sc_register_time_node(compositor, &st->time_handle); +} + + +void compositor_audioclip_modified(GF_Node *node) +{ + M_AudioClip *ac = (M_AudioClip *)node; + AudioClipStack *st = (AudioClipStack *) gf_node_get_private(node); + if (!st) return; + + st->failure = 0; + + /*MPEG4 spec is not clear about that , so this is not forbidden*/ + if (st->input.is_open && st->input.is_open) { + if (gf_sc_audio_check_url(&st->input, &ac->url)) { + gf_sc_audio_stop(&st->input); + gf_sc_audio_open(&st->input, &ac->url, 0, -1); + /*force unregister to resetup audio cfg*/ + gf_sc_audio_unregister(&st->input); + gf_sc_invalidate(st->input.compositor, NULL); + } + } + + //update state if we're active + if (ac->isActive) { + audioclip_update_time(&st->time_handle); + /*we're no longer active fon't check for reactivation*/ + if (!ac->isActive) return; + } + + /*make sure we are still registered*/ + if (!st->time_handle.is_registered && !st->time_handle.needs_unregister) + gf_sc_register_time_node(st->input.compositor, &st->time_handle); + else + st->time_handle.needs_unregister = 0; +} + + +typedef struct +{ + GF_AudioInput input; + GF_TimeNode time_handle; + Bool is_active; + Double start_time; +} AudioSourceStack; + +static void audiosource_activate(AudioSourceStack *st, M_AudioSource *as) +{ + if (gf_sc_audio_open(&st->input, &as->url, 0, -1) != GF_OK) + return; + st->is_active = 1; + gf_mo_set_speed(st->input.stream, st->input.speed); + /*traverse all graph to get parent audio group*/ + gf_sc_invalidate(st->input.compositor, NULL); +} + +static void audiosource_deactivate(AudioSourceStack *st, M_AudioSource *as) +{ + gf_sc_audio_stop(&st->input); + st->is_active = 0; + st->time_handle.needs_unregister = 1; +} + +static void audiosource_traverse(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_TraverseState*tr_state = (GF_TraverseState*)rs; + M_AudioSource *as = (M_AudioSource *)node; + AudioSourceStack *st = (AudioSourceStack *)gf_node_get_private(node); + + + if (is_destroy) { + gf_sc_audio_stop(&st->input); + gf_sc_audio_unregister(&st->input); + if (st->time_handle.is_registered) { + gf_sc_unregister_time_node(st->input.compositor, &st->time_handle); + } + free(st); + return; + } + + + /*check end of stream*/ + if (st->input.stream && st->input.stream_finished) { + if (gf_mo_get_loop(st->input.stream, 0)) { + gf_sc_audio_restart(&st->input); + } else if (st->is_active && gf_mo_should_deactivate(st->input.stream)) { + /*deactivate*/ + audiosource_deactivate(st, as); + } + } + if (st->is_active) { + gf_sc_audio_register(&st->input, (GF_TraverseState*)rs); + } + + /*store mute flag*/ + st->input.is_muted = tr_state->switched_off; +} + +static void audiosource_update_time(GF_TimeNode *tn) +{ + Double time; + M_AudioSource *as = (M_AudioSource *)tn->udta; + AudioSourceStack *st = (AudioSourceStack *)gf_node_get_private(tn->udta); + + if (! st->is_active) { + st->start_time = as->startTime; + st->input.speed = as->speed; + } + time = gf_node_get_scene_time(tn->udta); + if ((timestart_time) || (st->start_time<0)) return; + + if (st->input.input_ifce.GetSpeed(st->input.input_ifce.callback) && st->is_active) { + if ( (as->stopTime > st->start_time) && (time>=as->stopTime)) { + audiosource_deactivate(st, as); + return; + } + } + if (!st->is_active) audiosource_activate(st, as); +} + + +void compositor_init_audiosource(GF_Compositor *compositor, GF_Node *node) +{ + AudioSourceStack *st; + GF_SAFEALLOC(st, AudioSourceStack); + gf_sc_audio_setup(&st->input, compositor, node); + + st->time_handle.UpdateTimeNode = audiosource_update_time; + st->time_handle.udta = node; + + gf_node_set_private(node, st); + gf_node_set_callback_function(node, audiosource_traverse); + gf_sc_register_time_node(compositor, &st->time_handle); +} + + +void compositor_audiosource_modified(GF_Node *node) +{ + M_AudioSource *as = (M_AudioSource *)node; + AudioSourceStack *st = (AudioSourceStack *) gf_node_get_private(node); + if (!st) return; + + /*MPEG4 spec is not clear about that , so this is not forbidden*/ + if (st->input.is_open&& st->input.is_open) { + if (gf_sc_audio_check_url(&st->input, &as->url)) { + gf_sc_audio_stop(&st->input); + gf_sc_audio_open(&st->input, &as->url, 0, -1); + /*force unregister to resetup audio cfg*/ + gf_sc_audio_unregister(&st->input); + gf_sc_invalidate(st->input.compositor, NULL); + } + } + + //update state if we're active + if (st->is_active) { + audiosource_update_time(&st->time_handle); + if (!st->is_active) return; + } + + /*make sure we are still registered*/ + if (!st->time_handle.is_registered && !st->time_handle.needs_unregister) + gf_sc_register_time_node(st->input.compositor, &st->time_handle); + else + st->time_handle.needs_unregister = 0; +} + + +typedef struct +{ + AUDIO_GROUP_NODE + + GF_TimeNode time_handle; + Double start_time; + Bool set_duration; + /*AudioBuffer mixes its children*/ + GF_AudioMixer *am; + Bool is_init, is_muted; + /*buffer audio data*/ + char *buffer; + u32 buffer_size; + + Bool done; + /*read/write position in buffer and associated read time (CTS)*/ + u32 read_pos, write_pos, cur_cts; + /*list of audio children after a traverse*/ + GF_List *new_inputs; +} AudioBufferStack; + + + +/*we have no choice but always browsing the children, since a src can be replaced by a new one +without the parent being modified. We just collect the src and check against the current mixer inputs +to reset the mixer or not - the spec is not clear about that btw, shall rebuffering happen if a source is modified or not ...*/ +static void audiobuffer_traverse(GF_Node *node, void *rs, Bool is_destroy) +{ + u32 j; + Bool update_mixer; + GF_ChildNodeItem *l; + GF_AudioGroup *parent; + AudioBufferStack *st = (AudioBufferStack *)gf_node_get_private(node); + M_AudioBuffer *ab = (M_AudioBuffer *)node; + GF_TraverseState*tr_state = (GF_TraverseState*) rs; + + if (is_destroy) { + gf_sc_audio_unregister(&st->output); + if (st->time_handle.is_registered) + gf_sc_unregister_time_node(st->output.compositor, &st->time_handle); + + gf_mixer_del(st->am); + if (st->buffer) free(st->buffer); + gf_list_del(st->new_inputs); + free(st); + return; + } + parent = tr_state->audio_parent; + tr_state->audio_parent = (GF_AudioGroup *) st; + l = ab->children; + while (l) { + gf_node_traverse(l->node, tr_state); + l = l->next; + } + + gf_mixer_lock(st->am, 1); + + /*if no new inputs don't change mixer config*/ + update_mixer = gf_list_count(st->new_inputs) ? 1 : 0; + + if (gf_mixer_get_src_count(st->am) == gf_list_count(st->new_inputs)) { + u32 count = gf_list_count(st->new_inputs); + update_mixer = 0; + for (j=0; jnew_inputs, j); + if (!gf_mixer_is_src_present(st->am, &cur->input_ifce)) { + update_mixer = 1; + break; + } + } + } + + if (update_mixer) { + gf_mixer_remove_all(st->am); + gf_mixer_force_chanel_out(st->am, ab->numChan); + } + + while (gf_list_count(st->new_inputs)) { + GF_AudioInput *src = (GF_AudioInput *)gf_list_get(st->new_inputs, 0); + gf_list_rem(st->new_inputs, 0); + if (update_mixer) gf_mixer_add_input(st->am, &src->input_ifce); + } + + gf_mixer_lock(st->am, 0); + tr_state->audio_parent = parent; + + /*Note the audio buffer is ALWAYS registered untill destroyed since buffer filling shall happen even when inactive*/ + if (!st->output.register_with_parent || !st->output.register_with_renderer) + gf_sc_audio_register(&st->output, tr_state); + + /*store mute flag*/ + st->is_muted = tr_state->switched_off; +} + + + +static void audiobuffer_activate(AudioBufferStack *st, M_AudioBuffer *ab) +{ + ab->isActive = 1; + gf_node_event_out_str((GF_Node *)ab, "isActive"); + /*rerender all graph to get parent audio group*/ + gf_sc_invalidate(st->output.compositor, NULL); + st->done = 0; + st->read_pos = 0; +} + +static void audiobuffer_deactivate(AudioBufferStack *st, M_AudioBuffer *ab) +{ + ab->isActive = 0; + gf_node_event_out_str((GF_Node *)ab, "isActive"); + st->time_handle.needs_unregister = 1; +} + +static void audiobuffer_update_time(GF_TimeNode *tn) +{ + Double time; + M_AudioBuffer *ab = (M_AudioBuffer *)tn->udta; + AudioBufferStack *st = (AudioBufferStack *)gf_node_get_private(tn->udta); + + if (! ab->isActive) { + st->start_time = ab->startTime; + } + time = gf_node_get_scene_time(tn->udta); + if ((timestart_time) || (st->start_time<0)) return; + + if (ab->isActive) { + if ( (ab->stopTime > st->start_time) && (time>=ab->stopTime)) { + audiobuffer_deactivate(st, ab); + return; + } + /*THIS IS NOT NORMATIVE*/ + if ( !ab->loop && st->done) { + audiobuffer_deactivate(st, ab); + return; + } + } + if (!ab->isActive) audiobuffer_activate(st, ab); +} + + + + +static char *audiobuffer_fetch_frame(void *callback, u32 *size, u32 audio_delay_ms) +{ + u32 blockAlign; + AudioBufferStack *st = (AudioBufferStack *) gf_node_get_private( ((GF_AudioInput *) callback)->owner); + M_AudioBuffer *ab = (M_AudioBuffer*)st->output.owner; + + if (!st->is_init) return NULL; + if (!st->buffer) { + st->done = 0; + st->buffer_size = (u32) ceil(FIX2FLT(ab->length) * st->output.input_ifce.bps*st->output.input_ifce.samplerate*st->output.input_ifce.chan/8); + blockAlign = gf_mixer_get_block_align(st->am); + /*BLOCK ALIGN*/ + while (st->buffer_size%blockAlign) st->buffer_size++; + st->buffer = (char*)malloc(sizeof(char) * st->buffer_size); + memset(st->buffer, 0, sizeof(char) * st->buffer_size); + st->read_pos = st->write_pos = 0; + } + if (st->done) return NULL; + + /*even if not active, fill the buffer*/ + if (st->write_pos < st->buffer_size) { + u32 written; + while (1) { + /*just try to completely fill it*/ + written = gf_mixer_get_output(st->am, st->buffer + st->write_pos, st->buffer_size - st->write_pos); + if (!written) break; + st->write_pos += written; + assert(st->write_pos<=st->buffer_size); + } + } + /*not playing*/ + if (! ab->isActive) return NULL; + *size = st->write_pos - st->read_pos; + return st->buffer + st->read_pos; +} + +static void audiobuffer_release_frame(void *callback, u32 nb_bytes) +{ + AudioBufferStack *st = (AudioBufferStack *) gf_node_get_private( ((GF_AudioInput *) callback)->owner); + st->read_pos += nb_bytes; + assert(st->read_pos<=st->write_pos); + if (st->read_pos==st->write_pos) { + if (st->write_posbuffer_size) { + /*reading faster than buffering - let's still attempt to fill the buffer*/ +#if 0 + st->write_pos = st->buffer_size; + GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[AudioBuffer] done playing before buffer filling done\n")); +#endif + } else if ( ((M_AudioBuffer*)st->output.owner)->loop) { + st->read_pos = 0; + } else { + st->done = 1; + } + } +} + + +static Fixed audiobuffer_get_speed(void *callback) +{ + M_AudioBuffer *ab = (M_AudioBuffer *) ((GF_AudioInput *) callback)->owner; + return ab->pitch; +} + +static Bool audiobuffer_get_volume(void *callback, Fixed *vol) +{ + GF_AudioInput *ai = (GF_AudioInput *) callback; + if (ai->snd->GetChannelVolume) { + return ai->snd->GetChannelVolume(ai->snd->owner, vol); + } else { + vol[0] = vol[1] = vol[2] = vol[3] = vol[4] = vol[5] = FIX_ONE; + return 0; + } +} + +static Bool audiobuffer_is_muted(void *callback) +{ + AudioBufferStack *st = (AudioBufferStack *) gf_node_get_private( ((GF_AudioInput *) callback)->owner); + return st->is_muted; +} + +static Bool audiobuffer_get_config(GF_AudioInterface *aifc, Bool for_reconf) +{ + AudioBufferStack *st = (AudioBufferStack *) gf_node_get_private( ((GF_AudioInput *) aifc->callback)->owner); + + if (gf_mixer_must_reconfig(st->am)) { + if (gf_mixer_reconfig(st->am)) { + if (st->buffer) free(st->buffer); + st->buffer = NULL; + st->buffer_size = 0; + } + + gf_mixer_get_config(st->am, &aifc->samplerate, &aifc->chan, &aifc->bps, &aifc->ch_cfg); + st->is_init = (aifc->samplerate && aifc->chan && aifc->bps) ? 1 : 0; + assert(st->is_init); + if (!st->is_init) aifc->samplerate = aifc->chan = aifc->bps = aifc->ch_cfg = 0; + /*this will force invalidation*/ + return (for_reconf && st->is_init) ? 1 : 0; + } + return st->is_init; +} + +void audiobuffer_add_source(GF_AudioGroup *_this, GF_AudioInput *src) +{ + AudioBufferStack *st = (AudioBufferStack *)_this; + if (!src) return; + /*just collect the input, reconfig is done once all children are rendered*/ + gf_list_add(st->new_inputs, src); +} + + +void setup_audiobufer(GF_AudioInput *ai, GF_Compositor *compositor, GF_Node *node) +{ + memset(ai, 0, sizeof(GF_AudioInput)); + ai->owner = node; + ai->compositor = compositor; + /*NEVER used for audio buffer*/ + ai->stream = NULL; + /*setup io interface*/ + ai->input_ifce.FetchFrame = audiobuffer_fetch_frame; + ai->input_ifce.ReleaseFrame = audiobuffer_release_frame; + ai->input_ifce.GetConfig = audiobuffer_get_config; + ai->input_ifce.GetChannelVolume = audiobuffer_get_volume; + ai->input_ifce.GetSpeed = audiobuffer_get_speed; + ai->input_ifce.IsMuted = audiobuffer_is_muted; + ai->input_ifce.callback = ai; + ai->speed = FIX_ONE; +} + +void compositor_init_audiobuffer(GF_Compositor *compositor, GF_Node *node) +{ + AudioBufferStack *st; + GF_SAFEALLOC(st, AudioBufferStack); + + /*use our private input*/ + setup_audiobufer(&st->output, compositor, node); + st->add_source = audiobuffer_add_source; + + st->time_handle.UpdateTimeNode = audiobuffer_update_time; + st->time_handle.udta = node; + st->set_duration = 1; + + st->am = gf_mixer_new(NULL); + st->new_inputs = gf_list_new(); + + gf_node_set_private(node, st); + gf_node_set_callback_function(node, audiobuffer_traverse); + gf_sc_register_time_node(compositor, &st->time_handle); +} + + +void compositor_audiobuffer_modified(GF_Node *node) +{ + M_AudioBuffer *ab = (M_AudioBuffer *)node; + AudioBufferStack *st = (AudioBufferStack *) gf_node_get_private(node); + if (!st) return; + + //update state if we're active + if (ab->isActive) + audiobuffer_update_time(&st->time_handle); + + /*make sure we are still registered*/ + if (!st->time_handle.is_registered && !st->time_handle.needs_unregister) + gf_sc_register_time_node(st->output.compositor, &st->time_handle); + else + st->time_handle.needs_unregister = 0; +} + diff --git a/src/compositor/mpeg4_background.c b/src/compositor/mpeg4_background.c new file mode 100644 index 0000000..043ae7b --- /dev/null +++ b/src/compositor/mpeg4_background.c @@ -0,0 +1,537 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" + +#ifndef GPAC_DISABLE_3D + +#include "texturing.h" +#include "visual_manager.h" + +/*for background textures - the offset is to prevent lines on cube map edges*/ +#define PLANE_HSIZE FLT2FIX(0.5025f) +#define PLANE_HSIZE_LOW FLT2FIX(0.5f) + + +static Bool back_use_texture(MFURL *url) +{ + if (!url->count) return 0; + if (url->vals[0].OD_ID > 0) return 1; + if (url->vals[0].url && strlen(url->vals[0].url)) return 1; + return 0; +} + +static void UpdateBackgroundTexture(GF_TextureHandler *txh) +{ + gf_sc_texture_update_frame(txh, 0); + /*restart texture if needed (movie background controled by MediaControl)*/ + if (txh->stream_finished && gf_mo_get_loop(txh->stream, 0)) gf_sc_texture_restart(txh); +} + + +static Bool back_gf_sc_texture_enabled(MFURL *url, GF_TextureHandler *txh) +{ + Bool use_texture = back_use_texture(url); + if (use_texture) { + /*texture not ready*/ + if (!txh->tx_io) { + use_texture = 0; + gf_sc_invalidate(txh->compositor, NULL); + } + gf_sc_texture_set_blend_mode(txh, gf_sc_texture_is_transparent(txh) ? TX_REPLACE : TX_DECAL); + } + return use_texture; +} + +static void back_check_gf_sc_texture_change(GF_TextureHandler *txh, MFURL *url) +{ + /*if open and changed, stop and play*/ + if (txh->is_open) { + if (! gf_sc_texture_check_url_change(txh, url)) return; + gf_sc_texture_stop(txh); + gf_sc_texture_play(txh, url); + return; + } + /*if not open and changed play*/ + if (url->count) gf_sc_texture_play(txh, url); +} + +static void DestroyBackground(GF_Node *node) +{ + BackgroundStack *ptr = (BackgroundStack *) gf_node_get_private(node); + PreDestroyBindable(node, ptr->reg_stacks); + gf_list_del(ptr->reg_stacks); + + if (ptr->sky_mesh) mesh_free(ptr->sky_mesh); + if (ptr->ground_mesh) mesh_free(ptr->ground_mesh); + + gf_sg_vrml_mf_reset(&ptr->ground_ang, GF_SG_VRML_MFFLOAT); + gf_sg_vrml_mf_reset(&ptr->sky_ang, GF_SG_VRML_MFFLOAT); + gf_sg_vrml_mf_reset(&ptr->ground_col, GF_SG_VRML_MFCOLOR); + gf_sg_vrml_mf_reset(&ptr->sky_col, GF_SG_VRML_MFCOLOR); + + mesh_free(ptr->front_mesh); + mesh_free(ptr->back_mesh); + mesh_free(ptr->top_mesh); + mesh_free(ptr->bottom_mesh); + mesh_free(ptr->left_mesh); + mesh_free(ptr->right_mesh); + + + gf_sc_texture_destroy(&ptr->txh_front); + gf_sc_texture_destroy(&ptr->txh_back); + gf_sc_texture_destroy(&ptr->txh_top); + gf_sc_texture_destroy(&ptr->txh_bottom); + gf_sc_texture_destroy(&ptr->txh_left); + gf_sc_texture_destroy(&ptr->txh_right); + + free(ptr); +} + +#define COL_TO_RGBA(res, col) { res.red = col.red; res.green = col.green; res.blue = col.blue; res.alpha = FIX_ONE; } + +#define DOME_STEP_V 32 +#define DOME_STEP_H 16 + +static void back_build_dome(GF_Mesh *mesh, MFFloat *angles, MFColor *color, Bool ground_dome) +{ + u32 i, j, last_idx, ang_idx, new_idx; + Bool pad; + u32 step_div_h; + GF_Vertex vx; + SFColorRGBA start_col, end_col, fcol; + Fixed start_angle, next_angle, angle, r, frac, first_angle; + + start_angle = 0; + mesh_reset(mesh); + + start_col.red = start_col.green = start_col.blue = 0; + end_col = start_col; + if (color->count) { + COL_TO_RGBA(start_col, color->vals[0]); + end_col = start_col; + if (color->count>1) COL_TO_RGBA(end_col, color->vals[1]); + } + + start_col.alpha = end_col.alpha = FIX_ONE; + vx.texcoords.x = vx.texcoords.y = 0; + vx.color = MESH_MAKE_COL(start_col); + vx.pos.x = vx.pos.z = 0; vx.pos.y = FIX_ONE; + vx.normal.x = vx.normal.z = 0; + vx.normal.y = -MESH_NORMAL_UNIT; + + mesh_set_vertex_vx(mesh, &vx); + last_idx = 0; + ang_idx = 0; + + pad = 1; + next_angle = first_angle = 0; + if (angles->count) { + next_angle = angles->vals[0]; + first_angle = 7*next_angle/8; + pad = 0; + } + + step_div_h = DOME_STEP_H; + i=0; + if (ground_dome) { + step_div_h *= 2; + i=1; + } + + for (; i= next_angle) { + if (ang_idx+1<=angles->count) { + start_angle = next_angle; + next_angle = angles->vals[ang_idx+1]; + if (next_angle>GF_PI) next_angle=GF_PI; + start_col = end_col; + ang_idx++; + if (ang_idx+1count) { + COL_TO_RGBA(end_col, color->vals[ang_idx+1]); + } else { + pad = 1; + } + } else { + if (ground_dome) break; + pad = 1; + } + } + + if (pad) { + fcol = end_col; + } else { + frac = gf_divfix(angle - start_angle, next_angle - start_angle) ; + fcol.red = gf_mulfix(end_col.red - start_col.red, frac) + start_col.red; + fcol.green = gf_mulfix(end_col.green - start_col.green, frac) + start_col.green; + fcol.blue = gf_mulfix(end_col.blue - start_col.blue, frac) + start_col.blue; + fcol.alpha = FIX_ONE; + } + vx.color = MESH_MAKE_COL(fcol); + + vx.pos.y = gf_sin(GF_PI2 - angle); + r = gf_sqrt(FIX_ONE - gf_mulfix(vx.pos.y, vx.pos.y)); + + new_idx = mesh->v_count; + for (j = 0; j < step_div_h; j++) { + SFVec3f n; + Fixed lon = 2 * GF_PI * j / step_div_h; + vx.pos.x = gf_mulfix(gf_sin(lon), r); + vx.pos.z = gf_mulfix(gf_cos(lon), r); + n = gf_vec_scale(vx.pos, FIX_ONE /*-FIX_ONE*/); + gf_vec_norm(&n); + MESH_SET_NORMAL(vx, n); + mesh_set_vertex_vx(mesh, &vx); + + if (j) { + if (i>1) { + mesh_set_triangle(mesh, last_idx+j, new_idx+j, new_idx+j-1); + mesh_set_triangle(mesh, last_idx+j, new_idx+j-1, last_idx+j-1); + } else { + mesh_set_triangle(mesh, 0, new_idx+j, new_idx+j-1); + } + } + } + if (i>1) { + mesh_set_triangle(mesh, last_idx, new_idx, new_idx+step_div_h-1); + mesh_set_triangle(mesh, last_idx, new_idx+step_div_h-1, last_idx+step_div_h-1); + } else { + mesh_set_triangle(mesh, 0, new_idx, new_idx+step_div_h-1); + } + last_idx = new_idx; + } + + if (!ground_dome) { + new_idx = mesh->v_count; + vx.pos.x = vx.pos.z = 0; vx.pos.y = -FIX_ONE; + vx.normal.x = vx.normal.z = 0; + vx.normal.y = MESH_NORMAL_UNIT; + mesh_set_vertex_vx(mesh, &vx); + + for (j=1; j < step_div_h; j++) { + mesh_set_triangle(mesh, last_idx+j-1, last_idx+j, new_idx); + } + mesh_set_triangle(mesh, last_idx+step_div_h-1, last_idx, new_idx); + } + + mesh->flags |= MESH_HAS_COLOR | MESH_NO_TEXTURE; + mesh_update_bounds(mesh); +} + +static void back_draw_texture(GF_TraverseState *tr_state, GF_TextureHandler *txh, GF_Mesh *mesh) +{ + if (gf_sc_texture_enable(txh, NULL)) { + tr_state->mesh_num_textures = 1; + visual_3d_mesh_paint(tr_state, mesh); + gf_sc_texture_disable(txh); + tr_state->mesh_num_textures = 0; + } +} + +static void TraverseBackground(GF_Node *node, void *rs, Bool is_destroy) +{ + M_Background *bck; + BackgroundStack *st; + SFColor bcol; + SFVec4f res; + Fixed scale; + Bool has_sky, has_ground, front_tx, back_tx, top_tx, bottom_tx, right_tx, left_tx; + GF_Matrix mx; + GF_Compositor *compositor; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + DestroyBackground(node); + return; + } + + gf_node_dirty_clear(node, 0); + bck = (M_Background *)node; + st = (BackgroundStack *) gf_node_get_private(node); + compositor = (GF_Compositor*)st->compositor; + + + assert(tr_state->backgrounds); + + /*first traverse, bound if needed*/ + if (gf_list_find(tr_state->backgrounds, node) < 0) { + gf_list_add(tr_state->backgrounds, node); + assert(gf_list_find(st->reg_stacks, tr_state->backgrounds)==-1); + gf_list_add(st->reg_stacks, tr_state->backgrounds); + /*only bound if we're on top*/ + if (gf_list_get(tr_state->backgrounds, 0) == bck) { + if (!bck->isBound) Bindable_SetIsBound(node, 1); + } + + /*check streams*/ + if (back_use_texture(&bck->frontUrl) && !st->txh_front.is_open) gf_sc_texture_play(&st->txh_front, &bck->frontUrl); + if (back_use_texture(&bck->bottomUrl) && !st->txh_bottom.is_open) gf_sc_texture_play(&st->txh_bottom, &bck->bottomUrl); + if (back_use_texture(&bck->backUrl) && !st->txh_back.is_open) gf_sc_texture_play(&st->txh_back, &bck->backUrl); + if (back_use_texture(&bck->topUrl) && !st->txh_top.is_open) gf_sc_texture_play(&st->txh_top, &bck->topUrl); + if (back_use_texture(&bck->rightUrl) && !st->txh_right.is_open) gf_sc_texture_play(&st->txh_right, &bck->rightUrl); + if (back_use_texture(&bck->leftUrl) && !st->txh_left.is_open) gf_sc_texture_play(&st->txh_left, &bck->leftUrl); + + /*in any case don't draw the first time (since the background could have been declared last)*/ + gf_sc_invalidate(st->compositor, NULL); + return; + } + if (!bck->isBound) return; + if (tr_state->traversing_mode != TRAVERSE_BINDABLE) return; + + front_tx = back_gf_sc_texture_enabled(&bck->frontUrl, &st->txh_front); + back_tx = back_gf_sc_texture_enabled(&bck->backUrl, &st->txh_back); + top_tx = back_gf_sc_texture_enabled(&bck->topUrl, &st->txh_top); + bottom_tx = back_gf_sc_texture_enabled(&bck->bottomUrl, &st->txh_bottom); + right_tx = back_gf_sc_texture_enabled(&bck->rightUrl, &st->txh_right); + left_tx = back_gf_sc_texture_enabled(&bck->leftUrl, &st->txh_left); + + has_sky = ((bck->skyColor.count>1) && bck->skyAngle.count) ? 1 : 0; + has_ground = ((bck->groundColor.count>1) && bck->groundAngle.count) ? 1 : 0; + bcol.red = bcol.green = bcol.blue = 0; + if (bck->skyColor.count) bcol = bck->skyColor.vals[0]; + + /*if we clear the main visual clear it entirely - ONLY IF NOT IN LAYER*/ + if ((tr_state->visual == compositor->visual) && (tr_state->visual->back_stack == tr_state->backgrounds)) { + visual_3d_clear(tr_state->visual, bcol, FIX_ONE); + if (!has_sky && !has_ground && !front_tx && !back_tx && !top_tx && !bottom_tx && !left_tx && !right_tx) { + return; + } + } + + /*undo translation*/ + res.x = res.y = res.z = 0; res.q = FIX_ONE; + gf_mx_apply_vec_4x4(&tr_state->camera->unprojection, &res); + assert(res.q); + res.x = gf_divfix(res.x, res.q); + res.y = gf_divfix(res.y, res.q); + res.z = gf_divfix(res.z, res.q); + /*NB: we don't support local rotation of the background ...*/ + + /*enable background state (turn off all quality options)*/ + visual_3d_set_background_state(tr_state->visual, 1); + + if (has_sky) { + if (!st->sky_mesh) { + st->sky_mesh = new_mesh(); + back_build_dome(st->sky_mesh, &bck->skyAngle, &bck->skyColor, 0); + } + visual_3d_matrix_push(tr_state->visual); + gf_mx_init(mx); + gf_mx_add_translation(&mx, res.x, res.y, res.z); + + /*CHECKME - not sure why, we need to scale less in fixed point otherwise z-far clipping occur - probably some + rounding issues...*/ +#ifdef GPAC_FIXED_POINT + scale = (tr_state->camera->z_far/10)*8; +#else + scale = 9*tr_state->camera->z_far/10; +#endif + gf_mx_add_scale(&mx, scale, scale, scale); + + visual_3d_matrix_add(tr_state->visual, mx.m); + visual_3d_mesh_paint(tr_state, st->sky_mesh); + visual_3d_matrix_pop(tr_state->visual); + } + + if (has_ground) { + if (!st->ground_mesh) { + st->ground_mesh = new_mesh(); + back_build_dome(st->ground_mesh, &bck->groundAngle, &bck->groundColor, 1); + } + visual_3d_matrix_push(tr_state->visual); + gf_mx_init(mx); + gf_mx_add_translation(&mx, res.x, res.y, res.z); + /*cf above*/ +#ifdef GPAC_FIXED_POINT + scale = (tr_state->camera->z_far/100)*70; +#else + scale = 85*tr_state->camera->z_far/100; +#endif + gf_mx_add_scale(&mx, scale, -scale, scale); + visual_3d_matrix_add(tr_state->visual, mx.m); + visual_3d_mesh_paint(tr_state, st->ground_mesh); + visual_3d_matrix_pop(tr_state->visual); + } + + if (front_tx || back_tx || left_tx || right_tx || top_tx || bottom_tx) { + visual_3d_matrix_push(tr_state->visual); + gf_mx_init(mx); + gf_mx_add_translation(&mx, res.x, res.y, res.z); +#ifdef GPAC_FIXED_POINT + scale = (tr_state->camera->z_far/100)*99; + gf_mx_add_scale(&mx, scale, scale, scale); +#else + gf_mx_add_scale(&mx, tr_state->camera->z_far, tr_state->camera->z_far, tr_state->camera->z_far); +#endif + visual_3d_matrix_add(tr_state->visual, mx.m); + + visual_3d_enable_antialias(tr_state->visual, 1); + + if (front_tx) back_draw_texture(tr_state, &st->txh_front, st->front_mesh); + if (back_tx) back_draw_texture(tr_state, &st->txh_back, st->back_mesh); + if (top_tx) back_draw_texture(tr_state, &st->txh_top, st->top_mesh); + if (bottom_tx) back_draw_texture(tr_state, &st->txh_bottom, st->bottom_mesh); + if (left_tx) back_draw_texture(tr_state, &st->txh_left, st->left_mesh); + if (right_tx) back_draw_texture(tr_state, &st->txh_right, st->right_mesh); + + visual_3d_matrix_pop(tr_state->visual); + } + + /*enable background state (turn off all quality options)*/ + visual_3d_set_background_state(tr_state->visual, 0); +} + + +static void back_set_bind(GF_Node *node) +{ + BackgroundStack *st = (BackgroundStack *)gf_node_get_private(node); + Bindable_OnSetBind(node, st->reg_stacks); + /*and redraw scene*/ + gf_sc_invalidate(st->compositor, NULL); +} + +void compositor_init_background(GF_Compositor *compositor, GF_Node *node) +{ + BackgroundStack *ptr; + GF_SAFEALLOC(ptr, BackgroundStack); + + ptr->compositor = compositor; + ptr->reg_stacks = gf_list_new(); + ((M_Background *)node)->on_set_bind = back_set_bind; + + + /*build texture cube*/ + ptr->front_mesh = new_mesh(); + mesh_set_vertex(ptr->front_mesh, -PLANE_HSIZE, -PLANE_HSIZE, -PLANE_HSIZE_LOW, 0, 0, FIX_ONE, 0, 0); + mesh_set_vertex(ptr->front_mesh, PLANE_HSIZE, -PLANE_HSIZE, -PLANE_HSIZE_LOW, 0, 0, FIX_ONE, FIX_ONE, 0); + mesh_set_vertex(ptr->front_mesh, PLANE_HSIZE, PLANE_HSIZE, -PLANE_HSIZE_LOW, 0, 0, FIX_ONE, FIX_ONE, FIX_ONE); + mesh_set_vertex(ptr->front_mesh, -PLANE_HSIZE, PLANE_HSIZE, -PLANE_HSIZE_LOW, 0, 0, FIX_ONE, 0, FIX_ONE); + mesh_set_triangle(ptr->front_mesh, 0, 1, 2); mesh_set_triangle(ptr->front_mesh, 0, 2, 3); + mesh_update_bounds(ptr->front_mesh); + + ptr->back_mesh = new_mesh(); + mesh_set_vertex(ptr->back_mesh, -PLANE_HSIZE, -PLANE_HSIZE, PLANE_HSIZE_LOW, 0, 0, -FIX_ONE, FIX_ONE, 0); + mesh_set_vertex(ptr->back_mesh, PLANE_HSIZE, -PLANE_HSIZE, PLANE_HSIZE_LOW, 0, 0, -FIX_ONE, 0, 0); + mesh_set_vertex(ptr->back_mesh, PLANE_HSIZE, PLANE_HSIZE, PLANE_HSIZE_LOW, 0, 0, -FIX_ONE, 0, FIX_ONE); + mesh_set_vertex(ptr->back_mesh, -PLANE_HSIZE, PLANE_HSIZE, PLANE_HSIZE_LOW, 0, 0, -FIX_ONE, FIX_ONE, FIX_ONE); + mesh_set_triangle(ptr->back_mesh, 0, 1, 2); mesh_set_triangle(ptr->back_mesh, 0, 2, 3); + mesh_update_bounds(ptr->back_mesh); + + ptr->top_mesh = new_mesh(); + mesh_set_vertex(ptr->top_mesh, -PLANE_HSIZE, PLANE_HSIZE_LOW, PLANE_HSIZE, 0, -FIX_ONE, 0, 0, 0); + mesh_set_vertex(ptr->top_mesh, PLANE_HSIZE, PLANE_HSIZE_LOW, PLANE_HSIZE, 0, -FIX_ONE, 0, 0, FIX_ONE); + mesh_set_vertex(ptr->top_mesh, PLANE_HSIZE, PLANE_HSIZE_LOW, -PLANE_HSIZE, 0, -FIX_ONE, 0, FIX_ONE, FIX_ONE); + mesh_set_vertex(ptr->top_mesh, -PLANE_HSIZE, PLANE_HSIZE_LOW, -PLANE_HSIZE, 0, -FIX_ONE, 0, FIX_ONE, 0); + mesh_set_triangle(ptr->top_mesh, 0, 1, 2); mesh_set_triangle(ptr->top_mesh, 0, 2, 3); + mesh_update_bounds(ptr->top_mesh); + + ptr->bottom_mesh = new_mesh(); + mesh_set_vertex(ptr->bottom_mesh, -PLANE_HSIZE, -PLANE_HSIZE_LOW, -PLANE_HSIZE, 0, FIX_ONE, 0, FIX_ONE, FIX_ONE); + mesh_set_vertex(ptr->bottom_mesh, PLANE_HSIZE, -PLANE_HSIZE_LOW, -PLANE_HSIZE, 0, FIX_ONE, 0, FIX_ONE, 0); + mesh_set_vertex(ptr->bottom_mesh, PLANE_HSIZE, -PLANE_HSIZE_LOW, PLANE_HSIZE, 0, FIX_ONE, 0, 0, 0); + mesh_set_vertex(ptr->bottom_mesh, -PLANE_HSIZE, -PLANE_HSIZE_LOW, PLANE_HSIZE, 0, FIX_ONE, 0, 0, FIX_ONE); + mesh_set_triangle(ptr->bottom_mesh, 0, 1, 2); mesh_set_triangle(ptr->bottom_mesh, 0, 2, 3); + mesh_update_bounds(ptr->bottom_mesh); + + ptr->left_mesh = new_mesh(); + mesh_set_vertex(ptr->left_mesh, -PLANE_HSIZE_LOW, -PLANE_HSIZE, -PLANE_HSIZE, FIX_ONE, 0, 0, FIX_ONE, 0); + mesh_set_vertex(ptr->left_mesh, -PLANE_HSIZE_LOW, -PLANE_HSIZE, PLANE_HSIZE, FIX_ONE, 0, 0, 0, 0); + mesh_set_vertex(ptr->left_mesh, -PLANE_HSIZE_LOW, PLANE_HSIZE, PLANE_HSIZE, FIX_ONE, 0, 0, 0, FIX_ONE); + mesh_set_vertex(ptr->left_mesh, -PLANE_HSIZE_LOW, PLANE_HSIZE, -PLANE_HSIZE, FIX_ONE, 0, 0, FIX_ONE, FIX_ONE); + mesh_set_triangle(ptr->left_mesh, 0, 1, 2); mesh_set_triangle(ptr->left_mesh, 0, 2, 3); + mesh_update_bounds(ptr->left_mesh); + + ptr->right_mesh = new_mesh(); + mesh_set_vertex(ptr->right_mesh, PLANE_HSIZE_LOW, -PLANE_HSIZE, PLANE_HSIZE, -FIX_ONE, 0, 0, FIX_ONE, 0); + mesh_set_vertex(ptr->right_mesh, PLANE_HSIZE_LOW, -PLANE_HSIZE, -PLANE_HSIZE, -FIX_ONE, 0, 0, 0, 0); + mesh_set_vertex(ptr->right_mesh, PLANE_HSIZE_LOW, PLANE_HSIZE, -PLANE_HSIZE, -FIX_ONE, 0, 0, 0, FIX_ONE); + mesh_set_vertex(ptr->right_mesh, PLANE_HSIZE_LOW, PLANE_HSIZE, PLANE_HSIZE, -FIX_ONE, 0, 0, FIX_ONE, FIX_ONE); + mesh_set_triangle(ptr->right_mesh, 0, 1, 2); mesh_set_triangle(ptr->right_mesh, 0, 2, 3); + mesh_update_bounds(ptr->right_mesh); + + + gf_sc_texture_setup(&ptr->txh_back, compositor, node); + ptr->txh_back.update_texture_fcnt = UpdateBackgroundTexture; + gf_sc_texture_setup(&ptr->txh_front, compositor, node); + ptr->txh_front.update_texture_fcnt = UpdateBackgroundTexture; + gf_sc_texture_setup(&ptr->txh_top, compositor, node); + ptr->txh_top.update_texture_fcnt = UpdateBackgroundTexture; + gf_sc_texture_setup(&ptr->txh_bottom, compositor, node); + ptr->txh_bottom.update_texture_fcnt = UpdateBackgroundTexture; + gf_sc_texture_setup(&ptr->txh_left, compositor, node); + ptr->txh_left.update_texture_fcnt = UpdateBackgroundTexture; + gf_sc_texture_setup(&ptr->txh_right, compositor, node); + ptr->txh_right.update_texture_fcnt = UpdateBackgroundTexture; + + gf_node_set_private(node, ptr); + gf_node_set_callback_function(node, TraverseBackground); + +} + +void compositor_background_modified(GF_Node *node) +{ + M_Background *bck = (M_Background *)node; + BackgroundStack *st = (BackgroundStack *) gf_node_get_private(node); + if (!st) return; + + if (!gf_sg_vrml_field_equal(&bck->skyColor, &st->sky_col, GF_SG_VRML_MFCOLOR) + || !gf_sg_vrml_field_equal(&bck->skyAngle, &st->sky_ang, GF_SG_VRML_MFFLOAT) + ) { + + if (st->sky_mesh) mesh_free(st->sky_mesh); + st->sky_mesh = NULL; + gf_sg_vrml_field_copy(&st->sky_col, &bck->skyColor, GF_SG_VRML_MFCOLOR); + gf_sg_vrml_field_copy(&st->sky_ang, &bck->skyAngle, GF_SG_VRML_MFFLOAT); + } + if (!gf_sg_vrml_field_equal(&bck->groundColor, &st->ground_col, GF_SG_VRML_MFCOLOR) + || !gf_sg_vrml_field_equal(&bck->groundAngle, &st->ground_ang, GF_SG_VRML_MFFLOAT) + ) { + + if (st->ground_mesh) mesh_free(st->ground_mesh); + st->ground_mesh = NULL; + gf_sg_vrml_field_copy(&st->ground_col, &bck->groundColor, GF_SG_VRML_MFCOLOR); + gf_sg_vrml_field_copy(&st->ground_ang, &bck->groundAngle, GF_SG_VRML_MFFLOAT); + } + + back_check_gf_sc_texture_change(&st->txh_front, &bck->frontUrl); + back_check_gf_sc_texture_change(&st->txh_back, &bck->backUrl); + back_check_gf_sc_texture_change(&st->txh_top, &bck->topUrl); + back_check_gf_sc_texture_change(&st->txh_bottom, &bck->bottomUrl); + back_check_gf_sc_texture_change(&st->txh_left, &bck->leftUrl); + back_check_gf_sc_texture_change(&st->txh_right, &bck->rightUrl); + + + gf_sc_invalidate(st->compositor, NULL); +} + +#endif /*GPAC_DISABLE_3D*/ diff --git a/src/compositor/mpeg4_background2d.c b/src/compositor/mpeg4_background2d.c new file mode 100644 index 0000000..131f58f --- /dev/null +++ b/src/compositor/mpeg4_background2d.c @@ -0,0 +1,413 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "mpeg4_grouping.h" +#include "visual_manager.h" +#include "texturing.h" +#include "nodes_stacks.h" + + +#define B2D_PLANE_HSIZE FLT2FIX(0.5025f) + +typedef struct +{ + DrawableContext ctx; + BoundInfo bi; +} BackgroundStatus; + +static void DestroyBackground2D(GF_Node *node) +{ + Background2DStack *stack = (Background2DStack *) gf_node_get_private(node); + + PreDestroyBindable(node, stack->reg_stacks); + gf_list_del(stack->reg_stacks); + + while (gf_list_count(stack->status_stack)) { + BackgroundStatus *status = (BackgroundStatus *)gf_list_get(stack->status_stack, 0); + gf_list_rem(stack->status_stack, 0); + free(status); + } + gf_list_del(stack->status_stack); + + drawable_del(stack->drawable); + gf_sc_texture_destroy(&stack->txh); +#ifndef GPAC_DISABLE_3D + if (stack->mesh) mesh_free(stack->mesh); +#endif + free(stack); +} + +static void b2D_new_status(Background2DStack *bck, M_Background2D*back) +{ + BackgroundStatus *status; + + GF_SAFEALLOC(status, BackgroundStatus); + gf_mx2d_init(status->ctx.transform); + status->ctx.drawable = bck->drawable; + status->ctx.flags = CTX_IS_BACKGROUND; + status->ctx.bi = &status->bi; + status->ctx.aspect.fill_color = GF_COL_ARGB_FIXED(FIX_ONE, back->backColor.red, back->backColor.green, back->backColor.blue); + status->ctx.aspect.fill_texture = &bck->txh; + gf_list_add(bck->status_stack, status); +} + +static BackgroundStatus *b2d_get_status(Background2DStack *stack, GF_List *background_stack) +{ + u32 i, count; + if (!background_stack) return NULL; + + count = gf_list_count(stack->reg_stacks); + for (i=0; ireg_stacks, i); + if (bind_stack == background_stack) { + return gf_list_get(stack->status_stack, i); + } + } + return NULL; +} + + +static Bool back_use_texture(M_Background2D *bck) +{ + if (!bck->url.count) return 0; + if (bck->url.vals[0].OD_ID > 0) return 1; + if (bck->url.vals[0].url && strlen(bck->url.vals[0].url)) return 1; + return 0; +} + +static void DrawBackground2D_2D(DrawableContext *ctx, GF_TraverseState *tr_state) +{ + Background2DStack *stack = (Background2DStack *) gf_node_get_private(ctx->drawable->node); + + if (!ctx->bi->clip.width || !ctx->bi->clip.height) return; + + ctx->flags &= ~CTX_PATH_FILLED; + + if (back_use_texture((M_Background2D *)ctx->drawable->node)) { + + if (!tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx, NULL)) { + /*set target rect*/ + gf_path_reset(stack->drawable->path); + gf_path_add_rect_center(stack->drawable->path, + ctx->bi->unclip.x + ctx->bi->unclip.width/2, + ctx->bi->unclip.y - ctx->bi->unclip.height/2, + ctx->bi->unclip.width, ctx->bi->unclip.height); + + /*draw texture*/ + visual_2d_texture_path(tr_state->visual, stack->drawable->path, ctx, tr_state); + } + /*if (stack->txh.hwtx) ctx->flags |= CTX_APP_DIRTY; + else */ + ctx->flags &= ~(CTX_APP_DIRTY | CTX_TEXTURE_DIRTY); + } else { + + /*direct drawing, draw without clippers */ + if (tr_state->direct_draw) { + /*directly clear with specified color*/ + visual_2d_clear(tr_state->visual, &ctx->bi->clip, ctx->aspect.fill_color); + } else { + u32 i; + GF_IRect clip; + for (i=0; ivisual->to_redraw.count; i++) { + /*there's an opaque region above, don't draw*/ +#ifdef TRACK_OPAQUE_REGIONS + if (tr_state->visual->draw_node_indexvisual->to_redraw.opaque_node_index[i]) continue; +#endif + clip = ctx->bi->clip; + gf_irect_intersect(&clip, &tr_state->visual->to_redraw.list[i]); + if (clip.width && clip.height) { + visual_2d_clear(tr_state->visual, &clip, ctx->aspect.fill_color); + } + } + } + ctx->flags &= ~(CTX_APP_DIRTY | CTX_TEXTURE_DIRTY); + } + tr_state->visual->has_modif = 1; +} + +#ifndef GPAC_DISABLE_3D +static Bool back_texture_enabled(M_Background2D *bck, GF_TextureHandler *txh) +{ + Bool use_texture = back_use_texture(bck); + if (use_texture) { + /*texture not ready*/ + if (!txh->tx_io) { + use_texture = 0; + gf_sc_invalidate(txh->compositor, NULL); + } + gf_sc_texture_set_blend_mode(txh, gf_sc_texture_is_transparent(txh) ? TX_REPLACE : TX_DECAL); + } + return use_texture; +} + +static void DrawBackground2D_3D(M_Background2D *bck, Background2DStack *st, GF_TraverseState *tr_state) +{ + GF_Matrix mx; + Bool use_texture; + + use_texture = back_texture_enabled(bck, &st->txh); + + visual_3d_set_background_state(tr_state->visual, 1); + + visual_3d_matrix_push(tr_state->visual); + +/* visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_TEXTURE); + gf_sc_texture_get_transform(&st->txh, NULL, &mx); + visual_3d_matrix_load(tr_state->visual, mx.m); +*/ + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_MODELVIEW); + + /*little opt: if we clear the main visual clear it entirely */ + if (! tr_state->is_layer) { + visual_3d_clear(tr_state->visual, bck->backColor, FIX_ONE); + if (!use_texture) { + visual_3d_matrix_pop(tr_state->visual); + visual_3d_set_background_state(tr_state->visual, 0); + return; + } + /*we need a hack here because main vp is always traversed before main background, and in the case of a + 2D viewport it modifies the modelview matrix, which we don't want ...*/ + visual_3d_matrix_reset(tr_state->visual); + } + if (!use_texture || (!tr_state->is_layer && st->txh.transparent) ) visual_3d_set_material_2d(tr_state->visual, bck->backColor, FIX_ONE); + if (use_texture) { + visual_3d_set_state(tr_state->visual, V3D_STATE_COLOR, ! tr_state->is_layer); + tr_state->mesh_num_textures = gf_sc_texture_enable(&st->txh, NULL); + if (!tr_state->mesh_num_textures) visual_3d_set_material_2d(tr_state->visual, bck->backColor, FIX_ONE); + } + + /*create mesh object if needed*/ + if (!st->mesh) { + st->mesh = new_mesh(); + mesh_set_vertex(st->mesh, -B2D_PLANE_HSIZE, -B2D_PLANE_HSIZE, 0, 0, 0, FIX_ONE, 0, 0); + mesh_set_vertex(st->mesh, B2D_PLANE_HSIZE, -B2D_PLANE_HSIZE, 0, 0, 0, FIX_ONE, FIX_ONE, 0); + mesh_set_vertex(st->mesh, B2D_PLANE_HSIZE, B2D_PLANE_HSIZE, 0, 0, 0, FIX_ONE, FIX_ONE, FIX_ONE); + mesh_set_vertex(st->mesh, -B2D_PLANE_HSIZE, B2D_PLANE_HSIZE, 0, 0, 0, FIX_ONE, 0, FIX_ONE); + mesh_set_triangle(st->mesh, 0, 1, 2); mesh_set_triangle(st->mesh, 0, 2, 3); + st->mesh->flags |= MESH_IS_2D; + } + + gf_mx_init(mx); + if (tr_state->camera->is_3D) { + Fixed sx, sy; + /*reset matrix*/ + visual_3d_matrix_reset(tr_state->visual); + sx = sy = 2 * gf_mulfix(gf_tan(tr_state->camera->fieldOfView/2), tr_state->camera->z_far); + if (tr_state->camera->width > tr_state->camera->height) { + sx = gf_muldiv(sx, tr_state->camera->width, tr_state->camera->height); + } else { + sy = gf_muldiv(sy, tr_state->camera->height, tr_state->camera->width); + } + gf_mx_add_scale(&mx, sx, sy, FIX_ONE); +#ifdef GPAC_FIXED_POINT + gf_mx_add_translation(&mx, 0, 0, - (tr_state->camera->z_far/100)*99); +#else + gf_mx_add_translation(&mx, 0, 0, -0.995f*tr_state->camera->z_far); +#endif + } else { + gf_mx_add_scale(&mx, tr_state->bbox.max_edge.x - tr_state->bbox.min_edge.x, + tr_state->bbox.max_edge.y - tr_state->bbox.min_edge.y, + FIX_ONE); + /*when in layer2D, DON'T MOVE BACKGROUND TO ZFAR*/ +#ifdef GPAC_FIXED_POINT + if (!tr_state->is_layer) gf_mx_add_translation(&mx, 0, 0, -(tr_state->camera->z_far/100)*99); +#else + if (!tr_state->is_layer) gf_mx_add_translation(&mx, 0, 0, -0.999f*tr_state->camera->z_far); +#endif + } + visual_3d_matrix_add(tr_state->visual, mx.m); + visual_3d_mesh_paint(tr_state, st->mesh); + if (tr_state->mesh_num_textures) { + gf_sc_texture_disable(&st->txh); + tr_state->mesh_num_textures = 0; + } + + visual_3d_matrix_pop(tr_state->visual); + visual_3d_set_background_state(tr_state->visual, 0); +} +#endif + +static void TraverseBackground2D(GF_Node *node, void *rs, Bool is_destroy) +{ + u32 col; + BackgroundStatus *status; + M_Background2D *bck; + Background2DStack *stack = (Background2DStack *) gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + DestroyBackground2D(node); + return; + } + + bck = (M_Background2D *)node; + + /*special case for background in Layer2D: the background is seen as a regular drawable, so + RENDER_BINDABLE is not used*/ + if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { + DrawBackground2D_2D(tr_state->ctx, tr_state); + return; + } + else if (tr_state->traversing_mode==TRAVERSE_PICK) { + return; + } + + /*first traverse, bound if needed*/ + if (gf_list_find(tr_state->backgrounds, node) < 0) { + gf_list_add(tr_state->backgrounds, node); + assert(gf_list_find(stack->reg_stacks, tr_state->backgrounds)==-1); + gf_list_add(stack->reg_stacks, tr_state->backgrounds); + b2D_new_status(stack, bck); + + /*only bound if we're on top*/ + if (gf_list_get(tr_state->backgrounds, 0) == bck) { + if (!bck->isBound) { + Bindable_SetIsBound(node, 1); + } + } + /*open the stream if any*/ + if (back_use_texture(bck) && !stack->txh.is_open) gf_sc_texture_play(&stack->txh, &bck->url); + /*in any case don't draw the first time (since the background could have been declared last)*/ + gf_sc_invalidate(stack->txh.compositor, NULL); + return; + } + if (!bck->isBound) return; + + status = b2d_get_status(stack, tr_state->backgrounds); + if (!status) return; + + if (gf_node_dirty_get(node)) { + status->ctx.flags |= CTX_APP_DIRTY; + gf_node_dirty_clear(node, 0); + + + col = GF_COL_ARGB_FIXED(FIX_ONE, bck->backColor.red, bck->backColor.green, bck->backColor.blue); + if (col != status->ctx.aspect.fill_color) { + status->ctx.aspect.fill_color = col; + status->ctx.flags |= CTX_APP_DIRTY; + } + } + + if (back_use_texture(bck) ) { + if (stack->txh.tx_io && !(status->ctx.flags & CTX_APP_DIRTY) && stack->txh.needs_refresh) + status->ctx.flags |= CTX_TEXTURE_DIRTY; + } + + + if (tr_state->traversing_mode != TRAVERSE_BINDABLE) return; + + /*3D mode*/ +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + DrawBackground2D_3D(bck, stack, tr_state); + } else +#endif + DrawBackground2D_2D(&status->ctx, tr_state); +} + + +static void b2D_set_bind(GF_Node *node) +{ + Background2DStack *stack = (Background2DStack *)gf_node_get_private(node); + Bindable_OnSetBind(node, stack->reg_stacks); +} + +DrawableContext *b2d_get_context(M_Background2D *node, GF_List *from_stack) +{ + Background2DStack *stack = (Background2DStack *)gf_node_get_private((GF_Node *)node); + BackgroundStatus *status = b2d_get_status(stack, from_stack); + if (status) { + status->ctx.bi = &status->bi; + return &status->ctx; + } + return NULL; +} + + + +static void UpdateBackgroundTexture(GF_TextureHandler *txh) +{ + gf_sc_texture_update_frame(txh, 0); + /*restart texture if needed (movie background controled by MediaControl)*/ + if (txh->stream_finished && gf_mo_get_loop(txh->stream, 0)) + gf_sc_texture_restart(txh); +} + +void compositor_init_background2d(GF_Compositor *compositor, GF_Node *node) +{ + Background2DStack *ptr; + GF_SAFEALLOC(ptr, Background2DStack); + + ptr->status_stack = gf_list_new(); + ptr->reg_stacks = gf_list_new(); + /*setup drawable object for background*/ + ptr->drawable = drawable_stack_new(compositor, node); + ptr->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW; + ((M_Background2D *)node)->on_set_bind = b2D_set_bind; + + + gf_sc_texture_setup(&ptr->txh, compositor, node); + ptr->txh.update_texture_fcnt = UpdateBackgroundTexture; + ptr->txh.flags = GF_SR_TEXTURE_REPEAT_S | GF_SR_TEXTURE_REPEAT_T; + + gf_node_set_private(node, ptr); + gf_node_set_callback_function(node, TraverseBackground2D); +} + +void compositor_background2d_modified(GF_Node *node) +{ + M_Background2D *bck = (M_Background2D *)node; + Background2DStack *st = (Background2DStack *) gf_node_get_private(node); + if (!st) return; + + /*dirty node and parents in order to trigger parent visual redraw*/ + gf_node_dirty_set(node, 0, 1); + + /*if open and changed, stop and play*/ + if (st->txh.is_open) { + if (! gf_sc_texture_check_url_change(&st->txh, &bck->url)) return; + gf_sc_texture_stop(&st->txh); + gf_sc_texture_play(&st->txh, &bck->url); + return; + } + /*if not open and changed play*/ + if (bck->url.count) + gf_sc_texture_play(&st->txh, &bck->url); + gf_sc_invalidate(st->txh.compositor, NULL); +} + +Bool compositor_background_transparent(GF_Node *node) +{ + if (node && (gf_node_get_tag(node) == TAG_MPEG4_Background2D)) { + Background2DStack *st; + if (!((M_Background2D *)node)->isBound) return 1; + + st = (Background2DStack *) gf_node_get_private(node); + if (st->txh.transparent) return 1; + return 0; + } + /*consider all other background nodes transparent*/ + return 1; +} diff --git a/src/compositor/mpeg4_bitmap.c b/src/compositor/mpeg4_bitmap.c new file mode 100644 index 0000000..76eb2a6 --- /dev/null +++ b/src/compositor/mpeg4_bitmap.c @@ -0,0 +1,266 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "visual_manager.h" +#include "texturing.h" + +typedef struct _bitmap_stack +{ + Drawable *graph; + /*cached size for 3D mode*/ + SFVec2f size; + u32 prev_tx_w, prev_tx_h; + GF_Rect rc; +} BitmapStack; + + + +static void Bitmap_BuildGraph(GF_Node *node, BitmapStack *st, GF_TraverseState *tr_state, GF_Rect *out_rc, Bool notify_changes) +{ + GF_TextureHandler *txh; + Fixed sx, sy; + SFVec2f size; + + M_Bitmap *bmp = (M_Bitmap *)node; + + if (!tr_state->appear) return; + if (! ((M_Appearance *)tr_state->appear)->texture) return; + txh = gf_sc_texture_get_handler( ((M_Appearance *)tr_state->appear)->texture ); + /*bitmap not ready*/ + if (!txh || !txh->tx_io || !txh->width || !txh->height) { + if (notify_changes) gf_node_dirty_set(node, 0, 1); + return; + } + /*no change in scale and same texture size*/ + if (!gf_node_dirty_get(node) && (st->prev_tx_w == txh->width) && (st->prev_tx_h == txh->height)) { + *out_rc = st->rc; + return; + } + st->prev_tx_w = txh->width; + st->prev_tx_h = txh->height; + + sx = bmp->scale.x; if (sx<0) sx = FIX_ONE; + sy = bmp->scale.y; if (sy<0) sy = FIX_ONE; + + compositor_adjust_scale(txh->owner, &sx, &sy); + + /*check size change*/ + size.x = txh->width*sx; + size.y = txh->height*sy; + /*if we have a PAR update it!!*/ + if (txh->pixel_ar) { + u32 n = (txh->pixel_ar>>16) & 0xFFFF; + u32 d = (txh->pixel_ar) & 0xFFFF; + size.x = ( (txh->width * n) / d) * sx; + } + + + /*we're in meter metrics*/ + if (!tr_state->pixel_metrics) { + size.x = gf_divfix(size.x, tr_state->min_hsize); + size.y = gf_divfix(size.y, tr_state->min_hsize); + } + *out_rc = st->rc = gf_rect_center(size.x, size.y); + + gf_node_dirty_clear(node, 0); + + if ((st->size.x==size.x) && (st->size.y==size.y)) return; + st->size = size; + + /*change in size*/ + if (notify_changes) gf_node_dirty_set(node, 0, 1); + + /*get size with scale*/ + drawable_reset_path(st->graph); + gf_path_add_rect_center(st->graph->path, 0, 0, st->size.x, st->size.y); +} + +#ifndef GPAC_DISABLE_3D +static void draw_bitmap_3d(GF_Node *node, GF_TraverseState *tr_state) +{ + DrawAspect2D asp; + + BitmapStack *st = (BitmapStack *)gf_node_get_private(node); + M_Bitmap *bmp = (M_Bitmap *)node; + + /*no choice but to update the graph since a bitmap may be used with several textures ...*/ + Bitmap_BuildGraph(node, st, tr_state, &tr_state->bounds, 0); + + memset(&asp, 0, sizeof(DrawAspect2D)); + drawable_get_aspect_2d_mpeg4(node, &asp, tr_state); + + compositor_3d_draw_bitmap(st->graph, &asp, tr_state, st->size.x, st->size.y, bmp->scale.x, bmp->scale.y); +} +#endif + +static void draw_bitmap_2d(GF_Node *node, GF_TraverseState *tr_state) +{ + GF_ColorKey *key, keyColor; + GF_Compositor *compositor; + DrawableContext *ctx = tr_state->ctx; + BitmapStack *st = (BitmapStack *) gf_node_get_private(node); + + + compositor = tr_state->visual->compositor; + /*bitmaps are NEVER rotated (forbidden in spec). In case a rotation was done we still display (reset the skew components)*/ + ctx->transform.m[1] = ctx->transform.m[3] = 0; + + /*check for material key materialKey*/ + key = NULL; + if (ctx->appear) { + M_Appearance *app = (M_Appearance *)ctx->appear; + if ( app->material && (gf_node_get_tag((GF_Node *)app->material)==TAG_MPEG4_MaterialKey) ) { + M_MaterialKey*mk = (M_MaterialKey*)app->material; + if (mk->isKeyed) { + keyColor.r = FIX2INT(mk->keyColor.red * 255); + keyColor.g = FIX2INT(mk->keyColor.green * 255); + keyColor.b = FIX2INT(mk->keyColor.blue * 255); + keyColor.alpha = FIX2INT( (FIX_ONE - mk->transparency) * 255); + keyColor.low = FIX2INT(mk->lowThreshold * 255); + keyColor.high = FIX2INT(mk->highThreshold * 255); + key = &keyColor; + + } + } + } + + /*no HW, fall back to the graphics driver*/ + if (!tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx, key)) { + GF_Matrix2D _mat; + GF_Rect rc = gf_rect_center(ctx->bi->unclip.width, ctx->bi->unclip.height); + gf_mx2d_copy(_mat, ctx->transform); + gf_mx2d_inverse(&_mat); + gf_mx2d_apply_rect(&_mat, &rc); + + drawable_reset_path(st->graph); + gf_path_add_rect_center(st->graph->path, 0, 0, rc.width, rc.height); + ctx->flags |= CTX_NO_ANTIALIAS; + visual_2d_texture_path(tr_state->visual, st->graph->path, ctx, tr_state); + return; + } + +} + +static void TraverseBitmap(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_Rect rc; + DrawableContext *ctx; + BitmapStack *st = (BitmapStack *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + drawable_del(st->graph); + free(st); + return; + } + + switch (tr_state->traversing_mode) { + case TRAVERSE_DRAW_2D: + draw_bitmap_2d(node, tr_state); + return; +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + draw_bitmap_3d(node, tr_state); + return; +#endif + case TRAVERSE_PICK: + drawable_pick(st->graph, tr_state); + return; + case TRAVERSE_GET_BOUNDS: + Bitmap_BuildGraph(node, st, tr_state, &tr_state->bounds, +#ifndef GPAC_DISABLE_3D + tr_state->visual->type_3d ? 1 : 0 +#else + 0 +#endif + ); + + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + break; + default: + return; + } + + memset(&rc, 0, sizeof(rc)); + Bitmap_BuildGraph(node, st, tr_state, &rc, 1); + + ctx = drawable_init_context_mpeg4(st->graph, tr_state); + if (!ctx || !ctx->aspect.fill_texture ) { + visual_2d_remove_last_context(tr_state->visual); + return; + } + + /*even if set this is not true*/ + ctx->aspect.pen_props.width = 0; + ctx->flags |= CTX_NO_ANTIALIAS; + + ctx->flags &= ~CTX_IS_TRANSPARENT; + /*if clipper then transparent*/ + + if (ctx->aspect.fill_texture->transparent) { + ctx->flags |= CTX_IS_TRANSPARENT; + } else { + M_Appearance *app = (M_Appearance *)ctx->appear; + if ( app->material && (gf_node_get_tag((GF_Node *)app->material)==TAG_MPEG4_MaterialKey) ) { + if (((M_MaterialKey*)app->material)->isKeyed) { + if (((M_MaterialKey*)app->material)->transparency==FIX_ONE) { + visual_2d_remove_last_context(tr_state->visual); + return; + } + ctx->flags |= CTX_IS_TRANSPARENT; + } + } + else if (!tr_state->color_mat.identity) { + ctx->flags |= CTX_IS_TRANSPARENT; + } else { + u8 alpha = GF_COL_A(ctx->aspect.fill_color); + /*THIS IS A HACK, will not work when setting filled=0, transparency and XLineProps*/ + if (!alpha) alpha = GF_COL_A(ctx->aspect.line_color); + if (alpha < 0xFF) ctx->flags |= CTX_IS_TRANSPARENT; + } + } + + /*bounds are stored when building graph*/ + drawable_finalize_sort(ctx, tr_state, &rc); +} + + +void compositor_init_bitmap(GF_Compositor *compositor, GF_Node *node) +{ + BitmapStack *st; + GF_SAFEALLOC(st, BitmapStack); + st->graph = drawable_new(); + st->graph->node = node; + st->graph->flags = DRAWABLE_USE_TRAVERSE_DRAW; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, TraverseBitmap); +} + diff --git a/src/compositor/mpeg4_composite.c b/src/compositor/mpeg4_composite.c new file mode 100644 index 0000000..a08e0bf --- /dev/null +++ b/src/compositor/mpeg4_composite.c @@ -0,0 +1,622 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "visual_manager.h" +#include "texturing.h" + +#ifdef GPAC_USE_TINYGL +//#include +#include "../../TinyGL/include/GL/oscontext.h" +#endif + +#ifdef TRISCOPE_TEST_MALLOC +#include "gltx_malloc.h" +#endif + + + +typedef struct +{ + GF_TextureHandler txh; + Fixed sx, sy; + /*the visual object handling the texture*/ + GF_VisualManager *visual; + Bool first, unsupported; + +#ifdef GPAC_USE_TINYGL + ostgl_context *tgl_ctx; +#endif +} CompositeTextureStack; + +static void composite_traverse(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(node); + /*unregister visual*/ + gf_sc_visual_unregister(st->visual->compositor, st->visual); + visual_del(st->visual); +#ifndef TRISCOPE_TEST_MALLOC + if (st->txh.data) free(st->txh.data); +#else + if (st->txh.data) gltx_free(st->txh.data); + printf("Renoir object texture freed: Total %d Max %d\n",gltx_total_mem(),gltx_max_alloc()) ; +#endif + /*destroy texture*/ + gf_sc_texture_destroy(&st->txh); +#ifdef GPAC_USE_TINYGL + if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx); +#endif + free(st); + } else { + gf_node_traverse_children(node, rs); + } +} + +static Bool composite_do_bindable(GF_Node *n, GF_TraverseState *tr_state, Bool force_check) +{ + GF_Node *btop; + Bool ret = 0; + switch (gf_node_get_tag(n)) { +#ifndef GPAC_DISABLE_3D + case TAG_MPEG4_CompositeTexture3D: + { + M_CompositeTexture3D*c3d = (M_CompositeTexture3D*)n; + if (force_check || gf_node_dirty_get(c3d->background)) { gf_node_traverse(c3d->background, tr_state); ret = 1; } + btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0); + if (btop != c3d->background) { + gf_node_unregister(c3d->background, n); + gf_node_register(btop, n); + c3d->background = btop; + gf_node_event_out_str(n, "background"); + ret = 1; + } + if (force_check || gf_node_dirty_get(c3d->viewpoint)) { gf_node_traverse(c3d->viewpoint, tr_state); ret = 1; } + btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); + if (btop != c3d->viewpoint) { + gf_node_unregister(c3d->viewpoint, n); + gf_node_register(btop, n); + c3d->viewpoint = btop; + gf_node_event_out_str(n, "viewpoint"); + ret = 1; + } + + if (force_check || gf_node_dirty_get(c3d->fog)) { gf_node_traverse(c3d->fog, tr_state); ret = 1; } + btop = (GF_Node*)gf_list_get(tr_state->fogs, 0); + if (btop != c3d->fog) { + gf_node_unregister(c3d->fog, n); + gf_node_register(btop, n); + c3d->fog = btop; + gf_node_event_out_str(n, "fog"); + ret = 1; + } + + if (force_check || gf_node_dirty_get(c3d->navigationInfo)) { gf_node_traverse(c3d->navigationInfo, tr_state); ret = 1; } + btop = (GF_Node*)gf_list_get(tr_state->navigations, 0); + if (btop != c3d->navigationInfo) { + gf_node_unregister(c3d->navigationInfo, n); + gf_node_register(btop, n); + c3d->navigationInfo = btop; + gf_node_event_out_str(n, "navigationInfo"); + ret = 1; + } + return ret; + } +#endif + case TAG_MPEG4_CompositeTexture2D: + { + M_CompositeTexture2D *c2d = (M_CompositeTexture2D*)n; + if (force_check || gf_node_dirty_get(c2d->background)) { gf_node_traverse(c2d->background, tr_state); ret = 1; } + btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0); + if (btop != c2d->background) { + gf_node_unregister(c2d->background, n); + gf_node_register(btop, n); + c2d->background = btop; + gf_node_event_out_str(n, "background"); + ret = 1; + } + + if (force_check || gf_node_dirty_get(c2d->viewport)) { gf_node_traverse(c2d->viewport, tr_state); ret = 1; } + btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); + if (btop != c2d->viewport) { + gf_node_unregister(c2d->viewport, n); + gf_node_register(btop, n); + c2d->viewport = btop; + gf_node_event_out_str(n, "viewport"); + ret = 1; + } + + return ret; + } + } + return 0; +} + +static void composite_update(GF_TextureHandler *txh) +{ + s32 w, h; + GF_STENCIL stencil; + GF_Err e; + M_Background2D *back; + GF_TraverseState *tr_state; + Bool invalidate_all; + u32 new_pixel_format; + GF_Compositor *compositor = (GF_Compositor *)txh->compositor; + CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(txh->owner); + GF_Raster2D *raster = st->visual->compositor->rasterizer; + + if (st->unsupported) return; + + +/* + if (compositor->recompute_ar) { + gf_node_dirty_set(txh->owner, 0, 0); + return; + } +*/ + if (!gf_node_dirty_get(txh->owner)) { + txh->needs_refresh = 0; + return; + } + gf_node_dirty_clear(st->txh.owner, 0); + + new_pixel_format = 0; + back = gf_list_get(st->visual->back_stack, 0); + if (back && back->isBound) new_pixel_format = GF_PIXEL_RGB_24; + else new_pixel_format = GF_PIXEL_RGBA; + + +#ifdef GPAC_USE_TINYGL + /*TinyGL pixel format is fixed at compile time, we cannot override it !*/ + new_pixel_format = GF_PIXEL_RGBA; +#else + + /*in OpenGL_ES, only RGBA can be safelly used with glReadPixels*/ +#ifdef GPAC_USE_OGL_ES + new_pixel_format = GF_PIXEL_RGBA; +#else + /*no alpha support in offscreen rendering*/ + if (!(compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA)) + new_pixel_format = GF_PIXEL_RGB_24; +#endif + +#endif + + /*in triscope all our images are RGB+Depth+bitshape*/ + +#ifdef GPAC_TRISCOPE_MODE + if (st->visual->type_3d) new_pixel_format = GF_PIXEL_RGBDS; +#endif + + +#ifndef GPAC_DISABLE_3D + if (st->visual->type_3d>1) { + w = ((M_CompositeTexture3D*)txh->owner)->pixelWidth; + h = ((M_CompositeTexture3D*)txh->owner)->pixelHeight; + } else +#endif + { + w = ((M_CompositeTexture2D*)txh->owner)->pixelWidth; + h = ((M_CompositeTexture2D*)txh->owner)->pixelHeight; + } + if (w<0) w = 0; + if (h<0) h = 0; + + if (!w || !h) return; + invalidate_all = 0; + + /*rebuild stencil*/ + if (!txh->tx_io + || (w != (s32) txh->width) || ( h != (s32) txh->height) + || (new_pixel_format != txh->pixelformat) + ) { + + Bool needs_stencil = 1; + if (txh->tx_io) { +#ifdef GPAC_USE_TINYGL + if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx); +#endif + gf_sc_texture_release(txh); + if (txh->data) free(txh->data); + txh->data = NULL; + } + + /*we don't use rect ext because of no support for texture transforms*/ + if (1 +#ifndef GPAC_DISABLE_3D + || compositor->gl_caps.npot_texture +#endif + ) { + st->txh.width = w; + st->txh.height = h; + st->sx = st->sy = FIX_ONE; + } else { + st->txh.width = 2; + while (st->txh.width<(u32)w) st->txh.width*=2; + st->txh.height = 2; + while (st->txh.height<(u32)h) st->txh.height*=2; + + st->sx = INT2FIX(st->txh.width) / w; + st->sy = INT2FIX(st->txh.height) / h; + } + + gf_sc_texture_allocate(txh); + txh->pixelformat = new_pixel_format; + if (new_pixel_format==GF_PIXEL_RGBA) { + txh->stride = txh->width * 4; + txh->transparent = 1; + } else if (new_pixel_format==GF_PIXEL_RGB_565) { + txh->stride = txh->width * 2; + txh->transparent = 0; +#ifdef GPAC_TRISCOPE_MODE + } else if (new_pixel_format==GF_PIXEL_RGBDS) { + txh->stride = txh->width * 4; + txh->transparent = 1; +#endif + } + /*RGB24*/ + else { + txh->stride = txh->width * 3; + txh->transparent = 0; + } + + st->visual->width = txh->width; + st->visual->height = txh->height; + + stencil = raster->stencil_new(raster, GF_STENCIL_TEXTURE); + /*TODO - add support for compositeTexture3D when root is 2D visual*/ +#ifndef GPAC_DISABLE_3D + if (st->visual->type_3d) { + GF_Compositor *compositor = st->visual->compositor; + /*figure out what to do if main visual (eg video out) is not in OpenGL ...*/ + if (!compositor->visual->type_3d) { + /*create an offscreen window for OpenGL rendering*/ + if ((compositor->offscreen_width < st->txh.width) || (compositor->offscreen_height < st->txh.height)) { +#ifndef GPAC_USE_TINYGL + GF_Event evt; + compositor->offscreen_width = MAX(compositor->offscreen_width, st->txh.width); + compositor->offscreen_height = MAX(compositor->offscreen_height, st->txh.height); + + evt.type = GF_EVENT_VIDEO_SETUP; + evt.setup.width = compositor->offscreen_width; + evt.setup.height = compositor->offscreen_height; + evt.setup.back_buffer = 0; + evt.setup.opengl_mode = 2; + e = compositor->video_out->ProcessEvent(compositor->video_out, &evt); + if (e) { + gf_sc_texture_release(txh); + st->unsupported = 1; + return; + } +#endif + } + } else { + needs_stencil = 0; + } + } +#endif + + if (needs_stencil) { +#ifndef TRISCOPE_TEST_MALLOC + txh->data = (char*)malloc(sizeof(unsigned char) * txh->stride * txh->height); +#else + if ( txh->data = (char*)gltx_malloc(sizeof(unsigned char) * txh->stride * txh->height) ) { + printf("\nRenoir object texture allocation: %d bytes\n", sizeof(unsigned char) * txh->stride * txh->height ) ; + printf("Renoir object texture allocated: Total %d Max %d\n",gltx_total_mem(),gltx_max_alloc()) ; + } else printf( "Renoir object texture could not be allocated!"); +#endif + memset(txh->data, 0, sizeof(unsigned char) * txh->stride * txh->height); + e = raster->stencil_set_texture(stencil, txh->data, txh->width, txh->height, txh->stride, txh->pixelformat, txh->pixelformat, 0); +#ifdef GPAC_TRISCOPE_MODE + e = GF_OK; +#endif + if (e) { + raster->stencil_delete(stencil); + gf_sc_texture_release(txh); + free(txh->data); + txh->data = NULL; + return; + } +#ifdef GPAC_USE_TINYGL + if (st->visual->type_3d && !compositor->visual->type_3d) { + st->tgl_ctx = ostgl_create_context(txh->width, txh->height, txh->transparent ? 32 : 24, &txh->data, 1); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Creating TinyGL Offscreen context 0x%08x (%d %d - pf %s)\n", st->tgl_ctx, txh->width, txh->width, gf_4cc_to_str(txh->pixelformat))); + } +#endif + + } + invalidate_all = 1; + gf_sc_texture_set_stencil(txh, stencil); + } + if (!txh->tx_io) return; + + stencil = gf_sc_texture_get_stencil(txh); + if (!stencil) return; + +#ifdef GPAC_USE_TINYGL + if (st->tgl_ctx) ostgl_make_current(st->tgl_ctx, 0); +#endif + + GF_SAFEALLOC(tr_state, GF_TraverseState); + tr_state->vrml_sensors = gf_list_new(); + tr_state->visual = st->visual; + tr_state->invalidate_all = invalidate_all; + + if (st->visual->compositor->traverse_state->direct_draw) { + tr_state->direct_draw = 1; + } + + gf_mx2d_init(tr_state->transform); + gf_cmx_init(&tr_state->color_mat); + + tr_state->backgrounds = st->visual->back_stack; + tr_state->viewpoints = st->visual->view_stack; + tr_state->pixel_metrics = gf_sg_use_pixel_metrics(gf_node_get_graph(st->txh.owner)); + tr_state->min_hsize = INT2FIX( MIN(txh->width, txh->height) ) / 2; + tr_state->vp_size.x = INT2FIX(txh->width); + tr_state->vp_size.y = INT2FIX(txh->height); + + composite_do_bindable(st->txh.owner, tr_state, st->first); + st->first = 0; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Entering draw cycle\n")); + + txh->needs_refresh = visual_draw_frame(st->visual, st->txh.owner, tr_state, 0); + + /*set active viewport in image coordinates top-left=(0, 0), not in BIFS*/ + if (gf_list_count(st->visual->view_stack)) { + M_Viewport *vp = (M_Viewport *)gf_list_get(st->visual->view_stack, 0); + + if (vp->isBound) { + SFVec2f size = vp->size; + if (size.x >=0 && size.y>=0) { + /*FIXME - we need tracking of VP changes*/ + txh->needs_refresh = 1; + } + } + } + + if (txh->needs_refresh) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] First 4 pixel %x %x %x %x\n", txh->data[0], txh->data[4], txh->data[8], txh->data[12])); +#ifndef GPAC_DISABLE_3D + if (st->visual->camera.is_3D) { + if (st->visual->compositor->visual->type_3d) { +#ifndef GPAC_USE_TINYGL + gf_sc_copy_to_texture(&st->txh); +#else + /*in TinyGL we only need to push associated bitmap to the texture*/ + gf_sc_texture_push_image(&st->txh, 0, 0); +#endif + } else { + +#ifndef GPAC_USE_TINYGL + gf_sc_copy_to_stencil(&st->txh); + +#else +#ifdef GPAC_TRISCOPE_MODE + if (txh->pixelformat==GF_PIXEL_RGBDS) gf_get_tinygl_depth(&st->txh); +#endif +#endif + } + } else +#endif + { + if (raster->stencil_texture_modified) raster->stencil_texture_modified(stencil); + gf_sc_texture_set_stencil(txh, stencil); + } + gf_sc_invalidate(st->txh.compositor, NULL); + } + gf_list_del(tr_state->vrml_sensors); + free(tr_state); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Leaving draw cycle\n")); +} + + +static GF_Err composite_get_video_access(GF_VisualManager *visual) +{ + GF_STENCIL stencil; + GF_Err e; + CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(visual->offscreen); + + if (!st->txh.tx_io || !visual->raster_surface) return GF_BAD_PARAM; + stencil = gf_sc_texture_get_stencil(&st->txh); + if (!stencil) return GF_BAD_PARAM; + e = visual->compositor->rasterizer->surface_attach_to_texture(visual->raster_surface, stencil); + if (!e) visual->is_attached = 1; + return e; +} + +static void composite_release_video_access(GF_VisualManager *visual) +{ + visual->compositor->rasterizer->surface_detach(visual->raster_surface); +} + +void compositor_init_compositetexture2d(GF_Compositor *compositor, GF_Node *node) +{ + M_CompositeTexture2D *c2d = (M_CompositeTexture2D *)node; + CompositeTextureStack *st; + GF_SAFEALLOC(st, CompositeTextureStack); + gf_sc_texture_setup(&st->txh, compositor, node); + st->txh.update_texture_fcnt = composite_update; + +// st->txh.flags = GF_SR_TEXTURE_NO_GL_FLIP; + if ((c2d->repeatSandT==1) || (c2d->repeatSandT==3) ) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S; + if (c2d->repeatSandT>1) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T; + + /*create composite visual*/ + st->visual = visual_new(compositor); + st->visual->offscreen = node; + st->visual->GetSurfaceAccess = composite_get_video_access; + st->visual->ReleaseSurfaceAccess = composite_release_video_access; + st->visual->raster_surface = compositor->rasterizer->surface_new(compositor->rasterizer, 1); + + st->first = 1; + st->visual->compositor = compositor; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, composite_traverse); + gf_sc_visual_register(compositor, st->visual); +} + + +#ifndef GPAC_DISABLE_3D +void compositor_init_compositetexture3d(GF_Compositor *compositor, GF_Node *node) +{ + M_CompositeTexture3D *c3d = (M_CompositeTexture3D *)node; + CompositeTextureStack *st; + GF_SAFEALLOC(st, CompositeTextureStack); + gf_sc_texture_setup(&st->txh, compositor, node); + st->txh.update_texture_fcnt = composite_update; + +// st->txh.flags = GF_SR_TEXTURE_NO_GL_FLIP; + if (c3d->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S; + if (c3d->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T; + + /*create composite visual*/ + st->visual = visual_new(compositor); + st->visual->offscreen = node; + st->visual->GetSurfaceAccess = composite_get_video_access; + st->visual->ReleaseSurfaceAccess = composite_release_video_access; + + st->first = 1; + st->visual->compositor = compositor; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, composite_traverse); + gf_sc_visual_register(compositor, st->visual); + +#ifdef GPAC_USE_TINYGL + st->visual->type_3d = 2; + st->visual->camera.is_3D = 1; +#else + if (! (compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN)) { + st->visual->type_3d = 0; + st->visual->camera.is_3D = 0; + } else { + st->visual->type_3d = 2; + st->visual->camera.is_3D = 1; + } +#endif + camera_invalidate(&st->visual->camera); +} +#endif + +GF_TextureHandler *compositor_get_composite_texture(GF_Node *node) +{ + CompositeTextureStack *st = (CompositeTextureStack*) gf_node_get_private(node); + return &st->txh; +} + +Bool compositor_compositetexture_handle_event(GF_Compositor *compositor, GF_Event *ev) +{ + GF_Matrix mx; + GF_TraverseState *tr_state; + GF_ChildNodeItem *children, *l; + Bool res; + u32 flags; + SFVec3f txcoord; + CompositeTextureStack *stack; + M_Appearance *ap = (M_Appearance *)compositor->hit_appear; + assert(ap && ap->texture); + + if (ev->type > GF_EVENT_MOUSEMOVE) return 0; + + stack = gf_node_get_private(ap->texture); + + txcoord.x = compositor->hit_texcoords.x; + txcoord.y = compositor->hit_texcoords.y; + txcoord.z = 0; + flags = stack->txh.flags; + stack->txh.flags |= GF_SR_TEXTURE_NO_GL_FLIP; + if (gf_sc_texture_get_transform(&stack->txh, ap->textureTransform, &mx)) { + /*tx coords are inverted when mapping, thus applying directly the matrix will give us the + untransformed coords*/ + gf_mx_apply_vec(&mx, &txcoord); + while (txcoord.x<0) txcoord.x += FIX_ONE; while (txcoord.x>FIX_ONE) txcoord.x -= FIX_ONE; + while (txcoord.y<0) txcoord.y += FIX_ONE; while (txcoord.y>FIX_ONE) txcoord.y -= FIX_ONE; + } + stack->txh.flags = flags; + /*convert to tx space*/ + ev->mouse.x = FIX2INT( (txcoord.x - FIX_ONE/2) * stack->visual->width); + ev->mouse.y = FIX2INT( (txcoord.y - FIX_ONE/2) * stack->visual->height); + + + GF_SAFEALLOC(tr_state, GF_TraverseState); + tr_state->vrml_sensors = gf_list_new(); + tr_state->visual = stack->visual; + tr_state->traversing_mode = TRAVERSE_PICK; + tr_state->pixel_metrics = gf_sg_use_pixel_metrics(gf_node_get_graph(ap->texture)); + tr_state->vp_size.x = INT2FIX(stack->txh.width); + tr_state->vp_size.y = INT2FIX(stack->txh.height); + + gf_mx2d_init(tr_state->transform); +#ifndef GPAC_DISABLE_3D + gf_mx_init(tr_state->model_matrix); +#endif + /*collect sensors*/ + l = children = ((M_CompositeTexture2D*)ap->texture)->children; + while (l) { + GF_SensorHandler *hsens = compositor_mpeg4_get_sensor_handler(l->node); + if (hsens) gf_list_add(tr_state->vrml_sensors, hsens); + l = l->next; + } + + res = visual_execute_event(stack->visual, tr_state, ev, children); + gf_list_del(tr_state->vrml_sensors); + free(tr_state); + return res; +} + +void compositor_adjust_scale(GF_Node *node, Fixed *sx, Fixed *sy) +{ + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_CompositeTexture2D: + case TAG_MPEG4_CompositeTexture3D: + { + CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(node); + (*sx) = gf_divfix(*sx, st->sx); + (*sy) = gf_divfix(*sy, st->sy); + break; + } + default: + return; + } +} + +Bool compositor_is_composite_texture(GF_Node *appear) +{ + u32 tag; + if (!appear) return 0; + tag = gf_node_get_tag(appear); + if ((tag==TAG_MPEG4_Appearance) || (tag==TAG_X3D_Appearance)) { + M_Appearance *ap = (M_Appearance *)appear; + if (!ap->texture) return 0; + switch (gf_node_get_tag(((M_Appearance *)appear)->texture)) { + case TAG_MPEG4_CompositeTexture2D: + case TAG_MPEG4_CompositeTexture3D: + return 1; + } + } + return 0; +} diff --git a/src/compositor/mpeg4_form.c b/src/compositor/mpeg4_form.c new file mode 100644 index 0000000..7212613 --- /dev/null +++ b/src/compositor/mpeg4_form.c @@ -0,0 +1,674 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "mpeg4_grouping.h" +#include "visual_manager.h" + +typedef struct +{ + PARENT_NODE_STACK_2D + + GF_List *grouplist; + GF_Rect clip; +} FormStack; + +/*storage of group info*/ +typedef struct +{ + GF_List *children; + GF_Rect origin, final; +} FormGroup; + + +static void form_reset(FormStack *st) +{ + while (gf_list_count(st->grouplist)) { + FormGroup * fg = (FormGroup *) gf_list_get(st->grouplist, 0); + gf_list_rem(st->grouplist, 0); + gf_list_del(fg->children); + free(fg); + } +} + +static FormGroup *form_new_group(FormStack *st) +{ + FormGroup *fg; + GF_SAFEALLOC(fg, FormGroup); + memset(&fg->final, 0, sizeof(GF_Rect)); + memset(&fg->origin, 0, sizeof(GF_Rect)); + fg->children = gf_list_new(); + gf_list_add(st->grouplist, fg); + return fg; +} + +static GFINLINE FormGroup *form_get_group(FormStack *st, u32 i) +{ + return (FormGroup *) gf_list_get(st->grouplist, i); +} + +static void fg_compute_bounds(FormGroup *fg) +{ + ChildGroup *cg; + u32 i=0; + memset(&fg->origin, 0, sizeof(GF_Rect)); + while ((cg = (ChildGroup *)gf_list_enum(fg->children, &i))) { + gf_rect_union(&fg->origin, &cg->final); + } + fg->final = fg->origin; +} +static void fg_update_bounds(FormGroup *fg) +{ + ChildGroup *cg; + u32 i=0; + Fixed x, y; + x = fg->final.x - fg->origin.x; + y = fg->final.y - fg->origin.y; + while ((cg = (ChildGroup *)gf_list_enum(fg->children, &i))) { + cg->final.x += x; + cg->final.y += y; + } + fg_compute_bounds(fg); +} + + +static void shin_apply(FormStack *st, u32 *group_idx, u32 count); +static void sh_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count); +static void svin_apply(FormStack *st, u32 *group_idx, u32 count); +static void sv_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count); +static void al_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count); +static void ar_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count); +static void at_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count); +static void ab_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count); +static void ah_apply(FormStack *st, u32 *group_idx, u32 count); +static void av_apply(FormStack *st, u32 *group_idx, u32 count); + +static void form_apply(FormStack *st, const char *constraint, u32 *group_idx, u32 count) +{ + Float val; + if (!constraint || !strlen(constraint)) return; + + /*SHin*/ + if (!strnicmp(constraint, "SHin", 4)) { + shin_apply(st, group_idx, count); + return; + } + /*SH*/ + if (!strnicmp(constraint, "SH", 2)) { + if (sscanf(constraint, "SH %f", &val)==1) { + sh_apply(st, FLT2FIX(val), group_idx, count); + } else { + sh_apply(st, -FIX_ONE, group_idx, count); + } + return; + } + /*SVin*/ + if (!strnicmp(constraint, "SVin", 4)) { + svin_apply(st, group_idx, count); + return; + } + /*SV*/ + if (!strnicmp(constraint, "SV", 2)) { + if (sscanf(constraint, "SV %f", &val)==1) { + sv_apply(st, FLT2FIX(val), group_idx, count); + } else { + sv_apply(st, -FIX_ONE, group_idx, count); + } + return; + } + /*AL*/ + if (!strnicmp(constraint, "AL", 2)) { + if (sscanf(constraint, "AL %f", &val)==1) { + al_apply(st, FLT2FIX(val), group_idx, count); + } else { + al_apply(st, -FIX_ONE, group_idx, count); + } + return; + } + /*AR*/ + if (!strnicmp(constraint, "AR", 2)) { + if (sscanf(constraint, "AR %f", &val)==1) { + ar_apply(st, FLT2FIX(val), group_idx, count); + } else { + ar_apply(st, -FIX_ONE, group_idx, count); + } + return; + } + /*AT*/ + if (!strnicmp(constraint, "AT", 2)) { + if (sscanf(constraint, "AT %f", &val)==1) { + at_apply(st, FLT2FIX(val), group_idx, count); + } else { + at_apply(st, -FIX_ONE, group_idx, count); + } + return; + } + /*AB*/ + if (!strnicmp(constraint, "AB", 2)) { + if (sscanf(constraint, "AB %f", &val)==1) { + ab_apply(st, FLT2FIX(val), group_idx, count); + } else { + ab_apply(st, -FIX_ONE, group_idx, count); + } + return; + } + /*AH*/ + if (!strnicmp(constraint, "AH", 2)) { + ah_apply(st, group_idx, count); + return; + } + /*AV*/ + if (!strnicmp(constraint, "AV", 2)) { + av_apply(st, group_idx, count); + return; + } +} + + +#ifndef MAX_FORM_GROUP_INDEX +#define MAX_FORM_GROUP_INDEX 100 +#endif + +/*define to 1 to let the form object clip its children (not specified in spec)*/ +#define FORM_CLIPS 1 + +static void TraverseForm(GF_Node *n, void *rs, Bool is_destroy) +{ +#if FORM_CLIPS + GF_Rect clip, prev_clipper; + Bool had_clip; + GF_IRect prev_clip; +#endif + Bool recompute_form; + u32 idx[MAX_FORM_GROUP_INDEX]; + u32 i, index, last_ind, j; + u32 mode_bckup; + ParentNode2D *parent_bck; + FormGroup *fg; + ChildGroup *cg; + M_Form *fm = (M_Form *) n; + FormStack *st = (FormStack *)gf_node_get_private(n); + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + form_reset(st); + gf_list_del(st->grouplist); + parent_node_predestroy((ParentNode2D *)st); + free(st); + return; + } + /*update cliper*/ + if ((gf_node_dirty_get(n) & GF_SG_NODE_DIRTY)) { + /*get visual size*/ + visual_get_size_info(tr_state, &st->clip.width, &st->clip.height); + /*setup bounds in local coord system*/ + if (fm->size.x>=0) st->clip.width = fm->size.x; + if (fm->size.y>=0) st->clip.height = fm->size.y; + st->bounds = st->clip = gf_rect_center(st->clip.width, st->clip.height); + } + recompute_form = 0; + if (gf_node_dirty_get(n)) recompute_form = 1; + +#if FORM_CLIPS + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + tr_state->bounds = st->clip; +#ifndef GPAC_DISABLE_3D + gf_bbox_from_rect(&tr_state->bbox, &st->clip); +#endif + return; + } +#endif + + if (recompute_form) { + GF_Rect bounds; + GF_LOG(GF_LOG_COMPOSE, GF_LOG_DEBUG, ("[Form] Recomputing positions\n")); + + parent_node_reset((ParentNode2D*)st); + + bounds.width = bounds.height = 0; + + /*init traversing state*/ + parent_bck = tr_state->parent; + mode_bckup = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + tr_state->parent = (ParentNode2D *) st; + + parent_node_traverse(n, (ParentNode2D *)st, tr_state); + + /*restore traversing state*/ + tr_state->parent = parent_bck; + tr_state->traversing_mode = mode_bckup; + + /*center all nodes*/ + i=0; + while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) { + cg->final.x = - cg->final.width/2; + cg->final.y = cg->final.height/2; + } + /*build groups*/ + form_reset(st); + fg = form_new_group(st); + fg->origin = fg->final = st->clip; + fg = NULL; + for (i=0; igroups.count; i++) { + if (!fg) { + fg = form_new_group(st); + } + if (fm->groups.vals[i]==-1) { + fg_compute_bounds(fg); + fg = NULL; + continue; + } + /*broken form*/ + if ((u32) fm->groups.vals[i]>gf_list_count(st->groups)) goto err_exit; + cg = (ChildGroup *)gf_list_get(st->groups, fm->groups.vals[i]-1); + gf_list_add(fg->children, cg); + } + + last_ind = 0; + for (i=0; iconstraints.count; i++) { + index = 0; + while (1) { + if (last_ind+index > fm->groupsIndex.count) goto err_exit; + if (fm->groupsIndex.vals[last_ind+index]==-1) break; + if (index>MAX_FORM_GROUP_INDEX) goto err_exit; + idx[index] = fm->groupsIndex.vals[last_ind+index]; + index++; + } + /*apply*/ + form_apply(st, fm->constraints.vals[i], idx, index); + index++; + last_ind += index; + + /*refresh all group bounds*/ + j=1; + while ((fg = (FormGroup*)gf_list_enum(st->grouplist, &j))) { + fg_compute_bounds(fg); + gf_rect_union(&bounds, &fg->final); + } + /*done*/ + if (last_ind>=fm->groupsIndex.count) break; + } + form_reset(st); + +#if !FORM_CLIPS + st->clip = bounds; +#endif + } + + /*check picking*/ + if ((tr_state->traversing_mode==TRAVERSE_PICK) && !gf_sc_pick_in_clipper(tr_state, &st->clip)) + return; + +#if !FORM_CLIPS + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + tr_state->bounds = st->clip; + gf_bbox_from_rect(&tr_state->bbox, &st->clip); + return; + } +#endif + /*According to the spec clipping is not required on form so we don't do it*/ +#if FORM_CLIPS + /*update clipper*/ + if (tr_state->traversing_mode==TRAVERSE_SORT) { + prev_clip = tr_state->visual->top_clipper; + clip = compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 0); + if (tr_state->has_clip) { + tr_state->visual->top_clipper = gf_rect_pixelize(&tr_state->clipper); + gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip); + } + + i=0; + while ((cg = gf_list_enum(st->groups, &i))) { + parent_node_child_traverse(cg, tr_state); + } + + tr_state->visual->top_clipper = prev_clip; + if (had_clip) tr_state->clipper = prev_clipper; + tr_state->has_clip = had_clip; + } else +#endif + { + i=0; + while ((cg = gf_list_enum(st->groups, &i))) { + parent_node_child_traverse(cg, tr_state); + } + } + + return; + +err_exit: + parent_node_reset((ParentNode2D*)st); + form_reset(st); +} + +void compositor_init_form(GF_Compositor *compositor, GF_Node *node) +{ + FormStack *stack; + GF_SAFEALLOC(stack, FormStack); + + parent_node_setup((ParentNode2D*)stack); + stack->grouplist = gf_list_new(); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseForm); +} + + +/* + FORM CONSTRAINTS +*/ + + +static void shin_apply(FormStack *st, u32 *group_idx, u32 count) +{ + u32 i, len; + Fixed tot_len, inter_space; + tot_len = 0; + inter_space = st->clip.width; + + len = 0; + for (i=0; ifinal.width; + len++; + } + } + inter_space -= tot_len; + inter_space /= (len+1); + + for (i=0; ifinal.x = st->clip.x + inter_space; + } else { + form_get_group(st, group_idx[i])->final.x = + form_get_group(st, group_idx[i-1])->final.x + form_get_group(st, group_idx[i-1])->final.width + + inter_space; + } + fg_update_bounds(form_get_group(st, group_idx[i])); + } +} + +static void sh_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count) +{ + u32 i, k; + GF_Rect *l, *r; + Fixed inter_space, tot_len; + + tot_len = 0; + if (space == -FIX_ONE) { + r = &form_get_group(st, group_idx[count-1])->final; + l = &form_get_group(st, group_idx[0])->final; + inter_space = r->x - l->x; + if(group_idx[0] != 0) inter_space -= l->width; + for (i=1; ifinal.width; + inter_space -= tot_len; + inter_space /= (count-1); + } else { + inter_space = space; + } + + k = count - 1; + if (space != -1) k += 1; + for (i=1; ifinal; + r = &form_get_group(st, group_idx[i])->final; + r->x = l->x + inter_space; + if(group_idx[i-1] != 0) r->x += l->width; + fg_update_bounds(form_get_group(st, group_idx[i])); + } +} + +static void svin_apply(FormStack *st, u32 *group_idx, u32 count) +{ + u32 i, len; + Fixed tot_len, inter_space; + tot_len = 0; + inter_space = st->clip.height; + len = 0; + for (i=0; ifinal.height; + len++; + } + } + inter_space -= tot_len; + inter_space /= (len+1); + + for (i=0; ifinal.y = st->clip.y - inter_space; + } else { + form_get_group(st, group_idx[i])->final.y = + form_get_group(st, group_idx[i-1])->final.y - form_get_group(st, group_idx[i-1])->final.height - + inter_space; + } + fg_update_bounds(form_get_group(st, group_idx[i])); + } +} + +static void sv_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count) +{ + u32 i, k; + Fixed tot_len, inter_space; + GF_Rect *t, *b; + + tot_len = 0; + if(space > -FIX_ONE) { + inter_space = space; + } else { + t = &form_get_group(st, group_idx[count-1])->final; + b = &form_get_group(st, group_idx[0])->final; + inter_space = b->y - t->y; + if (group_idx[0]!=0) inter_space -= t->height; + for (i=1; ifinal.height; + inter_space -= tot_len; + inter_space /= count-1; + } + + k = count-1; + if (space > -1) k += 1; + for (i=1; ifinal.y = form_get_group(st, group_idx[i-1])->final.y - inter_space; + if (group_idx[i-1] != 0) { + form_get_group(st, group_idx[i])->final.y -= form_get_group(st, group_idx[i-1])->final.height; + } + fg_update_bounds(form_get_group(st, group_idx[i])); + } +} + +static void al_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count) +{ + Fixed min_x; + GF_Rect *rc; + u32 i, start; + + start = 0; + min_x = form_get_group(st, group_idx[0])->final.x; + if (space>-FIX_ONE) { + start = 1; + min_x += space; + } else { + for (i=1; ifinal; + if (group_idx[i]==0) { + min_x = rc->x; + break; + } + if (rc->x < min_x) min_x = rc->x; + } + } + for (i=start; ifinal.x = min_x; + fg_update_bounds(form_get_group(st, group_idx[i])); + } +} + +static void ar_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count) +{ + Fixed max_x; + u32 i, start; + GF_Rect *rc; + + start = 0; + rc = &form_get_group(st, group_idx[0])->final; + max_x = rc->x + rc->width; + + if(space>-FIX_ONE) { + max_x -= space; + start = 1; + } else { + for (i=1; ifinal; + if (group_idx[i]==0) { + max_x = rc->x + rc->width; + break; + } + if (rc->x + rc->width > max_x) max_x = rc->x + rc->width; + } + } + for (i=start; ifinal; + rc->x = max_x - rc->width; + fg_update_bounds(form_get_group(st, group_idx[i])); + } +} + +static void at_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count) +{ + Fixed max_y; + u32 i, start; + GF_Rect *rc; + + start = 0; + max_y = form_get_group(st, group_idx[0])->final.y; + if(space>-FIX_ONE) { + start = 1; + max_y -= space; + } else { + for (i=1; ifinal; + if (group_idx[i]==0) { + max_y = rc->y; + break; + } + if (rc->y > max_y) max_y = rc->y; + } + } + + for (i=start; ifinal.y = max_y; + fg_update_bounds(form_get_group(st, group_idx[i])); + } +} + +static void ab_apply(FormStack *st, Fixed space, u32 *group_idx, u32 count) +{ + Fixed min_y; + u32 i, start; + GF_Rect *rc; + + start = 0; + rc = &form_get_group(st, group_idx[0])->final; + min_y = rc->y - rc->height; + if(space>-FIX_ONE) { + start = 1; + min_y += space; + } else { + for (i=1; ifinal; + if (group_idx[i]==0) { + min_y = rc->y - rc->height; + break; + } + if (rc->y - rc->height < min_y) min_y = rc->y - rc->height; + } + } + for (i=start; ifinal; + rc->y = min_y + rc->height; + fg_update_bounds(form_get_group(st, group_idx[i])); + } +} + +static void ah_apply(FormStack *st, u32 *group_idx, u32 count) +{ + GF_Rect *rc; + u32 i; + Fixed left, right, center; + left = right = center = 0; + + for (i=0; ifinal; + if(group_idx[i] == 0) { + center = rc->x + rc->width / 2; + break; + } + if (left > rc->x) left = rc->x; + if (right < rc->x + rc->width) right = rc->x + rc->width; + center = (left+right)/2; + } + + for (i=0; ifinal; + rc->x = center - rc->width/2; + fg_update_bounds(form_get_group(st, group_idx[i])); + } +} + +static void av_apply(FormStack *st, u32 *group_idx, u32 count) +{ + u32 i; + Fixed top, bottom, center; + GF_Rect *rc; + top = bottom = center = 0; + + for (i=0; ifinal; + if (group_idx[i] == 0) { + center = rc->y - rc->height / 2; + break; + } + if (top < rc->y) top = rc->y; + if (bottom > rc->y - rc->height) bottom = rc->y - rc->height; + center = (top+bottom)/2; + } + + for (i=0; ifinal; + rc->y = center + rc->height/2; + fg_update_bounds(form_get_group(st, group_idx[i])); + } +} diff --git a/src/compositor/mpeg4_geometry_2d.c b/src/compositor/mpeg4_geometry_2d.c new file mode 100644 index 0000000..bb07cfc --- /dev/null +++ b/src/compositor/mpeg4_geometry_2d.c @@ -0,0 +1,683 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "visual_manager.h" + +Bool compositor_get_2d_plane_intersection(GF_Ray *ray, SFVec3f *res) +{ + GF_Plane p; + Fixed t, t2; + if (!ray->dir.x && !ray->dir.y) { + res->x = ray->orig.x; + res->y = ray->orig.y; + return 1; + } + p.normal.x = p.normal.y = 0; p.normal.z = FIX_ONE; + p.d = 0; + t2 = gf_vec_dot(p.normal, ray->dir); + if (t2 == 0) return 0; + t = - gf_divfix(gf_vec_dot(p.normal, ray->orig) + p.d, t2); + if (t<0) return 0; + *res = gf_vec_scale(ray->dir, t); + gf_vec_add(*res, ray->orig, *res); + return 1; +} + +/* + Shape +*/ +static void TraverseShape(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_TraverseState *tr_state; + M_Shape *shape; + if (is_destroy ) return; + + tr_state = (GF_TraverseState *)rs; +#ifndef GPAC_DISABLE_3D + if (tr_state->traversing_mode==TRAVERSE_LIGHTING) return; +#endif + + shape = (M_Shape *) node; + if (!shape->geometry) return; + + /*reset this node dirty flag (because bitmap may trigger bounds invalidation on the fly)*/ + gf_node_dirty_clear(node, 0); + + + /*check traverse mode, and take care of switch-off flag*/ + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + GF_Node *m; + tr_state->appear = (GF_Node *) shape->appearance; + + /*this is done regardless of switch flag*/ + gf_node_traverse((GF_Node *) shape->geometry, tr_state); + + if (tr_state->appear) { + /*apply line width*/ + m = ((M_Appearance *)tr_state->appear)->material; + if (m && (gf_node_get_tag(m)==TAG_MPEG4_Material2D) ) { + DrawAspect2D asp; + Fixed width = 0; + asp.line_scale = FIX_ONE; + m = ((M_Material2D *)m)->lineProps; + if (m) { + switch (gf_node_get_tag(m)) { + case TAG_MPEG4_LineProperties: + width = ((M_LineProperties *) m)->width; + drawable_compute_line_scale(tr_state, &asp); + break; + case TAG_MPEG4_XLineProperties: + if ( ((M_XLineProperties *) m)->isCenterAligned) + width = ((M_XLineProperties *) m)->width; + if ( ((M_XLineProperties *) m)->isScalable) + drawable_compute_line_scale(tr_state, &asp); + break; + } + width = gf_mulfix(width, asp.line_scale); + tr_state->bounds.width += width; + tr_state->bounds.height += width; + tr_state->bounds.y += width/2; + tr_state->bounds.x -= width/2; + } + } + tr_state->appear = NULL; + } + } else { + if (tr_state->switched_off) return; + + tr_state->appear = (GF_Node *) shape->appearance; + + switch (tr_state->traversing_mode) { + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) + visual_3d_register_context(tr_state, shape->geometry); + else +#endif + gf_node_traverse((GF_Node *) shape->geometry, tr_state); + break; + case TRAVERSE_PICK: + gf_node_traverse((GF_Node *) shape->geometry, tr_state); + break; +#ifndef GPAC_DISABLE_3D + /*if we're here we passed culler already*/ + case TRAVERSE_DRAW_3D: + gf_node_traverse((GF_Node *) shape->geometry, tr_state); + break; + case TRAVERSE_COLLIDE: + visual_3d_drawable_collide(shape->geometry, tr_state); + break; +#endif + } + + tr_state->appear = NULL; + } +} + +void compositor_init_shape(GF_Compositor *compositor, GF_Node *node) +{ + gf_node_set_callback_function(node, TraverseShape); +} + +static void circle_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) +{ + if (gf_node_dirty_get(node)) { + Fixed a = ((M_Circle *) node)->radius * 2; + drawable_reset_path(stack); + gf_path_add_ellipse(stack->path, 0, 0, a, a); + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); + } +} +static void TraverseCircle(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + drawable_node_del(node); + return; + } + + circle_check_changes(node, stack, tr_state); + + switch (tr_state->traversing_mode) { +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + if (!stack->mesh) { + Fixed a = ((M_Circle *) node)->radius * 2; + stack->mesh = new_mesh(); + mesh_new_ellipse(stack->mesh, a, a, tr_state->visual->compositor->high_speed); + } + visual_3d_draw_2d(stack, tr_state); + return; +#endif + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + drawable_finalize_sort(ctx, tr_state, NULL); + return; + } +} + +void compositor_init_circle(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, TraverseCircle); +} + +static void ellipse_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) +{ + if (gf_node_dirty_get(node)) { + drawable_reset_path(stack); + gf_path_add_ellipse(stack->path, 0, 0, ((M_Ellipse *) node)->radius.x*2, ((M_Ellipse *) node)->radius.y*2); + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); + } +} +static void TraverseEllipse(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + drawable_node_del(node); + return; + } + + ellipse_check_changes(node, stack, tr_state); + + switch (tr_state->traversing_mode) { +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + if (!stack->mesh) { + stack->mesh = new_mesh(); + mesh_new_ellipse(stack->mesh, ((M_Ellipse *) node)->radius.x * 2, ((M_Ellipse *) node)->radius.y * 2, tr_state->visual->compositor->high_speed); + } + visual_3d_draw_2d(stack, tr_state); + return; +#endif + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + drawable_finalize_sort(ctx, tr_state, NULL); + break; + } +} + +void compositor_init_ellipse(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, TraverseEllipse); +} + +static void compositor_2d_draw_rectangle(GF_TraverseState *tr_state) +{ + DrawableContext *ctx = tr_state->ctx; + + if (ctx->aspect.fill_texture) { + Bool res; + + /*get image size WITHOUT line size*/ + if (ctx->aspect.pen_props.width) { + GF_Rect orig_unclip; + GF_IRect orig_clip; + orig_unclip = ctx->bi->unclip; + orig_clip = ctx->bi->clip; + + gf_path_get_bounds(ctx->drawable->path, &ctx->bi->unclip); + gf_mx2d_apply_rect(&ctx->transform, &ctx->bi->unclip); + ctx->bi->clip = gf_rect_pixelize(&ctx->bi->unclip); + gf_irect_intersect(&ctx->bi->clip, &orig_clip); + + res = tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx, NULL); + + /*strike path*/ + ctx->bi->unclip = orig_unclip; + ctx->bi->clip = orig_clip; + if (res) { + ctx->flags |= CTX_PATH_FILLED; + visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state); + } + } else { + res = tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx, NULL); + } + /*if failure retry with raster*/ + if (res) return; + } + + visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state); + visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state); +} + +static void rectangle_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) +{ + /*if modified update node - we don't update for other traversing mode in order not to mess up the dirty + rect tracking (otherwise we would miss geometry changes with same bounds)*/ + if (gf_node_dirty_get(node)) { + drawable_reset_path(stack); + gf_path_add_rect_center(stack->path, 0, 0, ((M_Rectangle *) node)->size.x, ((M_Rectangle *) node)->size.y); + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); + } +} +static void TraverseRectangle(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + drawable_node_del(node); + return; + } + + rectangle_check_changes(node, stack, tr_state); + + switch (tr_state->traversing_mode) { + case TRAVERSE_DRAW_2D: + compositor_2d_draw_rectangle(tr_state); + return; +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + if (!stack->mesh) { + stack->mesh = new_mesh(); + mesh_new_rectangle(stack->mesh, ((M_Rectangle *) node)->size); + } + visual_3d_draw_2d(stack, tr_state); + return; +#endif + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + break; + default: + return; + } + + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + + /*if alpha or not filled, transparent*/ + if (GF_COL_A(ctx->aspect.fill_color) != 0xFF) { + } + /*if texture transparent, transparent*/ + else if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->transparent) { + } + /*if rotated, transparent (doesn't fill bounds)*/ + else if (ctx->transform.m[1] || ctx->transform.m[3]) { + } + /*TODO check matrix for alpha*/ + else if (!tr_state->color_mat.identity) { + } + /*otherwsie, not transparent*/ + else { + ctx->flags &= ~CTX_IS_TRANSPARENT; + } + drawable_finalize_sort(ctx, tr_state, NULL); +} + +void compositor_init_rectangle(GF_Compositor *compositor, GF_Node *node) +{ + Drawable *stack = drawable_stack_new(compositor, node); + stack->flags = DRAWABLE_USE_TRAVERSE_DRAW; + gf_node_set_callback_function(node, TraverseRectangle); +} + + +#define CHECK_VALID_C2D(nbPts) if (!idx && cur_index+nbPts>=pt_count) { gf_path_reset(stack->path); return; } +//#define CHECK_VALID_C2D(nbPts) +#define GET_IDX(_i) ((idx && idx->count>_i) ? idx->vals[_i] : _i) + +void curve2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state, MFInt32 *idx) +{ + M_Curve2D *c2D; + SFVec2f orig, ct_orig, ct_end, end; + u32 cur_index, i, remain, type_count, pt_count; + SFVec2f *pts; + M_Coordinate2D *coord; + + c2D = (M_Curve2D *)node; + coord = (M_Coordinate2D *)c2D->point; + drawable_reset_path(stack); + if (!coord) return; + + stack->path->fineness = c2D->fineness; + if (tr_state->visual->compositor->high_speed) stack->path->fineness /= 2; + + + pts = coord->point.vals; + if (!pts) + return; + + cur_index = c2D->type.count ? 1 : 0; + /*if the first type is a moveTo skip initial moveTo*/ + i=0; + if (cur_index) { + while (c2D->type.vals[i]==0) i++; + } + ct_orig = orig = pts[ GET_IDX(i) ]; + + gf_path_add_move_to(stack->path, orig.x, orig.y); + + pt_count = coord->point.count; + type_count = c2D->type.count; + for (; itype.vals[i]) { + /*moveTo, 1 point*/ + case 0: + CHECK_VALID_C2D(0); + orig = pts[ GET_IDX(cur_index) ]; + if (i) gf_path_add_move_to(stack->path, orig.x, orig.y); + cur_index += 1; + break; + /*lineTo, 1 point*/ + case 1: + CHECK_VALID_C2D(0); + end = pts[ GET_IDX(cur_index) ]; + gf_path_add_line_to(stack->path, end.x, end.y); + orig = end; + cur_index += 1; + break; + /*curveTo, 3 points*/ + case 2: + CHECK_VALID_C2D(2); + ct_orig = pts[ GET_IDX(cur_index) ]; + ct_end = pts[ GET_IDX(cur_index+1) ]; + end = pts[ GET_IDX(cur_index+2) ]; + gf_path_add_cubic_to(stack->path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y); + cur_index += 3; + ct_orig = ct_end; + orig = end; + break; + /*nextCurveTo, 2 points (cf spec)*/ + case 3: + CHECK_VALID_C2D(1); + ct_orig.x = 2*orig.x - ct_orig.x; + ct_orig.y = 2*orig.y - ct_orig.y; + ct_end = pts[ GET_IDX(cur_index) ]; + end = pts[ GET_IDX(cur_index+1) ]; + gf_path_add_cubic_to(stack->path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y); + cur_index += 2; + ct_orig = ct_end; + orig = end; + break; + + /*all XCurve2D specific*/ + + /*CW and CCW ArcTo*/ + case 4: + case 5: + CHECK_VALID_C2D(2); + ct_orig = pts[ GET_IDX(cur_index) ]; + ct_end = pts[ GET_IDX(cur_index+1) ]; + end = pts[ GET_IDX(cur_index+2) ]; + gf_path_add_arc_to(stack->path, end.x, end.y, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, (c2D->type.vals[i]==5) ? 1 : 0); + cur_index += 3; + ct_orig = ct_end; + orig = end; + break; + /*ClosePath*/ + case 6: + gf_path_close(stack->path); + break; + /*quadratic CurveTo, 2 points*/ + case 7: + CHECK_VALID_C2D(1); + ct_end = pts[ GET_IDX(cur_index) ]; + end = pts[ GET_IDX(cur_index+1) ]; + gf_path_add_quadratic_to(stack->path, ct_end.x, ct_end.y, end.x, end.y); + cur_index += 2; + ct_orig = ct_end; + orig = end; + break; + } + } + + /*what's left is an N-bezier spline*/ + if (!idx && (pt_count > cur_index) ) { + /*first moveto*/ + if (!cur_index) cur_index++; + + remain = pt_count - cur_index; + + if (remain>1) + gf_path_add_bezier(stack->path, &pts[cur_index], remain); + } + + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); +} + +static void TraverseCurve2D(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + drawable_node_del(node); + return; + } + if (gf_node_dirty_get(node)) { + curve2d_check_changes(node, stack, tr_state, NULL); + } + + switch (tr_state->traversing_mode) { +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + if (!stack->mesh) { + stack->mesh = new_mesh(); + mesh_from_path(stack->mesh, stack->path); + } + visual_3d_draw_2d(stack, tr_state); + return; +#endif + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + drawable_finalize_sort(ctx, tr_state, NULL); + return; + } +} + +void compositor_init_curve2d(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, TraverseCurve2D); +} + + + +/* + Note on point set 2D: this is a very bad node and should be avoided in DEF/USE, since the size + of the rectangle representing the pixel shall always be 1 pixel w/h, therefore + the path object is likely not the same depending on transformation context... + +*/ + +static void get_point_size(GF_Matrix2D *mat, Fixed *w, Fixed *h) +{ + GF_Point2D pt; + pt.x = mat->m[0] + mat->m[1]; + pt.y = mat->m[3] + mat->m[4]; + *w = *h = gf_divfix(FLT2FIX(1.41421356f) , gf_v2d_len(&pt)); +} + +static void pointset2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) +{ + u32 i; + Fixed w, h; + M_Coordinate2D *coord; + + if (!gf_node_dirty_get(node)) return; + coord = (M_Coordinate2D *) ((M_PointSet2D *)node)->coord; + + drawable_reset_path(stack); + + get_point_size(&tr_state->transform, &w, &h); + /*for PS2D don't add to avoid too much antialiasing, just try to fill the given pixel*/ + for (i=0; i < coord->point.count; i++) + gf_path_add_rect(stack->path, coord->point.vals[i].x, coord->point.vals[i].y, w, h); + + stack->path->flags |= GF_PATH_FILL_ZERO_NONZERO; + + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); +} + +static void PointSet2D_Draw(GF_Node *node, GF_TraverseState *tr_state) +{ + GF_Path *path; + Fixed alpha, w, h; + u32 i; + SFColor col; + DrawableContext *ctx = tr_state->ctx; + M_PointSet2D *ps2D = (M_PointSet2D *)node; + M_Coordinate2D *coord = (M_Coordinate2D*) ps2D->coord; + M_Color *color = (M_Color *) ps2D->color; + + /*never outline PS2D*/ + ctx->flags |= CTX_PATH_STROKE; + if (!color || color->color.countpoint.count) { + /*no texturing*/ + visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state); + return; + } + + get_point_size(&ctx->transform, &w, &h); + + path = gf_path_new(); + alpha = INT2FIX(GF_COL_A(ctx->aspect.line_color)) / 255; + for (i = 0; i < coord->point.count; i++) { + col = color->color.vals[i]; + ctx->aspect.line_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + gf_path_add_rect_center(path, coord->point.vals[i].x, coord->point.vals[i].y, w, h); + visual_2d_draw_path(tr_state->visual, path, ctx, NULL, NULL, tr_state); + gf_path_reset(path); + ctx->flags &= ~CTX_PATH_FILLED; + } + gf_path_del(path); +} + +static void TraversePointSet2D(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + M_PointSet2D *ps2D = (M_PointSet2D *)node; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + drawable_node_del(node); + return; + } + + if (!ps2D->coord) return; + + pointset2d_check_changes(node, stack, tr_state); + + switch (tr_state->traversing_mode) { + case TRAVERSE_DRAW_2D: + PointSet2D_Draw(node, tr_state); + return; +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + { + DrawAspect2D asp; + if (!stack->mesh) { + stack->mesh = new_mesh(); + mesh_new_ps(stack->mesh, ps2D->coord, ps2D->color); + } + memset(&asp, 0, sizeof(DrawAspect2D)); + drawable_get_aspect_2d_mpeg4(node, &asp, tr_state); + visual_3d_set_material_2d_argb(tr_state->visual, asp.fill_color); + visual_3d_mesh_paint(tr_state, stack->mesh); + return; + } +#endif + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_PICK: + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + drawable_finalize_sort(ctx, tr_state, NULL); + break; + default: + return; + } +} + + +void compositor_init_pointset2d(GF_Compositor *compositor, GF_Node *node) +{ + Drawable *stack = drawable_stack_new(compositor, node); + stack->flags = DRAWABLE_USE_TRAVERSE_DRAW; + gf_node_set_callback_function(node, TraversePointSet2D); +} diff --git a/src/compositor/mpeg4_geometry_3d.c b/src/compositor/mpeg4_geometry_3d.c new file mode 100644 index 0000000..ac4163f --- /dev/null +++ b/src/compositor/mpeg4_geometry_3d.c @@ -0,0 +1,523 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "visual_manager.h" +#include "nodes_stacks.h" + +#ifndef GPAC_DISABLE_3D + +#include + + +void drawable_3d_base_traverse(GF_Node *n, void *rs, Bool is_destroy, void (*build_shape)(GF_Node*,Drawable3D *,GF_TraverseState *) ) +{ + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + Drawable3D *stack = (Drawable3D*)gf_node_get_private(n); + + if (is_destroy) { + drawable_3d_del(n); + return; + } + if (gf_node_dirty_get(n)) { + mesh_reset(stack->mesh); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Rebuilding mesh %s\n", gf_node_get_class_name(n))); + build_shape(n, stack, tr_state); + gf_node_dirty_clear(n, 0); + } + switch (tr_state->traversing_mode) { + case TRAVERSE_DRAW_3D: + visual_3d_draw(tr_state, stack->mesh); + drawable3d_check_focus_highlight(n, tr_state, &stack->mesh->bounds); + break; + case TRAVERSE_GET_BOUNDS: + tr_state->bbox = stack->mesh->bounds; + break; + case TRAVERSE_PICK: + visual_3d_drawable_pick(n, tr_state, stack->mesh, NULL); + return; + } +} + +static void build_shape_box(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state) +{ + mesh_new_box(stack->mesh, ((M_Box *)n)->size); +} + +static void TraverseBox(GF_Node *n, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(n, rs, is_destroy, build_shape_box); +} + +void compositor_init_box(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseBox); +} + +static void build_shape_cone(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state) +{ + M_Cone *co = (M_Cone *)n; + mesh_new_cone(stack->mesh, co->height, co->bottomRadius, co->bottom, co->side, tr_state->visual->compositor->high_speed); +} + +static void TraverseCone(GF_Node *n, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(n, rs, is_destroy, build_shape_cone); +} + +void compositor_init_cone(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseCone); +} + +static void build_shape_cylinder(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state) +{ + M_Cylinder *cy = (M_Cylinder *)n; + mesh_new_cylinder(stack->mesh, cy->height, cy->radius, cy->bottom, cy->side, cy->top, tr_state->visual->compositor->high_speed); +} + +static void TraverseCylinder(GF_Node *n, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(n, rs, is_destroy, build_shape_cylinder); +} + +void compositor_init_cylinder(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseCylinder); +} + +static void build_shape_sphere(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state) +{ + M_Sphere *sp = (M_Sphere *)n; + mesh_new_sphere(stack->mesh, sp->radius, tr_state->visual->compositor->high_speed); +} + +static void TraverseSphere(GF_Node *n, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(n, rs, is_destroy, build_shape_sphere); +} + +void compositor_init_sphere(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseSphere); +} + +static void build_shape_point_set(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state) +{ + M_PointSet *ps = (M_PointSet *)n; + mesh_new_ps(stack->mesh, ps->coord, ps->color); +} +static void TraversePointSet(GF_Node *n, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(n, rs, is_destroy, build_shape_point_set); +} + +void compositor_init_point_set(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraversePointSet); +} + +static void build_shape_ifs(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state) +{ + mesh_new_ifs(stack->mesh, n); +} +static void TraverseIFS(GF_Node *n, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(n, rs, is_destroy, build_shape_ifs); +} + +static void IFS_SetColorIndex(GF_Node *node) +{ + M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node; + gf_sg_vrml_field_copy(&ifs->colorIndex, &ifs->set_colorIndex, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&ifs->set_colorIndex, GF_SG_VRML_MFINT32); +} + +static void IFS_SetCoordIndex(GF_Node *node) +{ + M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node; + gf_sg_vrml_field_copy(&ifs->coordIndex, &ifs->set_coordIndex, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&ifs->set_coordIndex, GF_SG_VRML_MFINT32); +} + +static void IFS_SetNormalIndex(GF_Node *node) +{ + M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node; + gf_sg_vrml_field_copy(&ifs->normalIndex, &ifs->set_normalIndex, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&ifs->set_normalIndex, GF_SG_VRML_MFINT32); +} + +static void IFS_SetTexCoordIndex(GF_Node *node) +{ + M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node; + gf_sg_vrml_field_copy(&ifs->texCoordIndex, &ifs->set_texCoordIndex, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&ifs->set_texCoordIndex, GF_SG_VRML_MFINT32); +} + +void compositor_init_ifs(GF_Compositor *compositor, GF_Node *node) +{ + M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node; + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseIFS); + ifs->on_set_colorIndex = IFS_SetColorIndex; + ifs->on_set_coordIndex = IFS_SetCoordIndex; + ifs->on_set_normalIndex = IFS_SetNormalIndex; + ifs->on_set_texCoordIndex = IFS_SetTexCoordIndex; +} + +static void build_shape_ils(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state) +{ + M_IndexedLineSet *ils = (M_IndexedLineSet *)n; + mesh_new_ils(stack->mesh, ils->coord, &ils->coordIndex, ils->color, &ils->colorIndex, ils->colorPerVertex, 0); +} + +static void TraverseILS(GF_Node *n, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(n, rs, is_destroy, build_shape_ils); +} + +static void ILS_SetColorIndex(GF_Node *node) +{ + M_IndexedLineSet *ils = (M_IndexedLineSet *)node; + gf_sg_vrml_field_copy(&ils->colorIndex, &ils->set_colorIndex, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&ils->set_colorIndex, GF_SG_VRML_MFINT32); +} + +static void ILS_SetCoordIndex(GF_Node *node) +{ + M_IndexedLineSet *ils = (M_IndexedLineSet *)node; + gf_sg_vrml_field_copy(&ils->coordIndex, &ils->set_coordIndex, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&ils->set_coordIndex, GF_SG_VRML_MFINT32); +} + +void compositor_init_ils(GF_Compositor *compositor, GF_Node *node) +{ + M_IndexedLineSet *ils = (M_IndexedLineSet *)node; + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseILS); + ils->on_set_colorIndex = ILS_SetColorIndex; + ils->on_set_coordIndex = ILS_SetCoordIndex; +} + + +static void build_shape_elevation_grid(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state) +{ + mesh_new_elevation_grid(stack->mesh, n); +} + +static void TraverseElevationGrid(GF_Node *n, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(n, rs, is_destroy, build_shape_elevation_grid); +} + +static void ElevationGrid_SetHeight(GF_Node *node) +{ + M_ElevationGrid *eg = (M_ElevationGrid *)node; + gf_sg_vrml_field_copy(&eg->height, &eg->set_height, GF_SG_VRML_MFFLOAT); + gf_sg_vrml_mf_reset(&eg->set_height, GF_SG_VRML_MFFLOAT); +} + +void compositor_init_elevation_grid(GF_Compositor *compositor, GF_Node *node) +{ + M_ElevationGrid *eg = (M_ElevationGrid *)node; + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseElevationGrid); + eg->on_set_height = ElevationGrid_SetHeight; +} + +static void build_shape_extrusion(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state) +{ + mesh_new_extrusion(stack->mesh, n); +} + +static void TraverseExtrusion(GF_Node *n, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(n, rs, is_destroy, build_shape_extrusion); +} + +static void Extrusion_SetCrossSection(GF_Node *node) +{ + M_Extrusion *eg = (M_Extrusion *)node; + gf_sg_vrml_field_copy(&eg->crossSection, &eg->set_crossSection, GF_SG_VRML_MFVEC2F); + gf_sg_vrml_mf_reset(&eg->set_crossSection, GF_SG_VRML_MFVEC2F); +} +static void Extrusion_SetOrientation(GF_Node *node) +{ + M_Extrusion *eg = (M_Extrusion *)node; + gf_sg_vrml_field_copy(&eg->orientation, &eg->set_orientation, GF_SG_VRML_MFROTATION); + gf_sg_vrml_mf_reset(&eg->set_orientation, GF_SG_VRML_MFROTATION); +} +static void Extrusion_SetScale(GF_Node *node) +{ + M_Extrusion *eg = (M_Extrusion *)node; + gf_sg_vrml_field_copy(&eg->scale, &eg->set_scale, GF_SG_VRML_MFVEC2F); + gf_sg_vrml_mf_reset(&eg->set_scale, GF_SG_VRML_MFVEC2F); +} +static void Extrusion_SetSpine(GF_Node *node) +{ + M_Extrusion *eg = (M_Extrusion *)node; + gf_sg_vrml_field_copy(&eg->spine, &eg->set_spine, GF_SG_VRML_MFVEC3F); + gf_sg_vrml_mf_reset(&eg->set_spine, GF_SG_VRML_MFVEC3F); +} +void compositor_init_extrusion(GF_Compositor *compositor, GF_Node *node) +{ + M_Extrusion *ext = (M_Extrusion *)node; + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseExtrusion); + ext->on_set_crossSection = Extrusion_SetCrossSection; + ext->on_set_orientation = Extrusion_SetOrientation; + ext->on_set_scale = Extrusion_SetScale; + ext->on_set_spine = Extrusion_SetSpine; +} + + +/* + NonLinearDeformer + + NOTE: AFX spec is just hmm, well, hmm. NonLinearDeformer.extend interpretation differ from type to + type within the spec, and between the spec and the ref soft. This is GPAC interpretation + * all params are specified with default transform axis (Z axis) + * NLD.type = 0 (taper): + * taping radius = NLD.param + * extend = N * [diff, perc] with + - diff : relative position along taper axis (for default, diff=0: z min, diff=1: z max) + - perc: mult ratio for base taper radius + extend works like key/keyValue for a scalar interpolator + final radius: r(z) = LinearInterp(extend[min key], extend[min key + 1]) * param + + * NLD.type = 1 (twister): + * twisting angle = NLD.param + * extend = N * [diff, perc] with + - diff : relative position along twister axis (for default, diff=0: z min, diff=1: z max) + - perc: mult ratio for base twister angle + extend works like key/keyValue for a scalar interpolator + final angle: a(z) = LinearInterp(extend[min key], extend[min key + 1]) * param + + * NLD.type = 2 (bender): + * bending curvature = NLD.param + * extend = N * [diff, perc] with + - diff : relative position along bender axis (for default, diff=0: z min, diff=1: z max) + - perc: mult ratio for base bender curvature + extend works like key/keyValue for a scalar interpolator + final curvature: c(z) = LinearInterp(extend[min key], extend[min key + 1]) * param + + Another pb of NLD is that the spec says nothing about object/axis alignment: should we center + the object at 0,0,0 (local coords) or not? the results are quite different. Here we don't + recenter the object before transform +*/ + +static Bool NLD_GetMatrix(M_NonLinearDeformer *nld, GF_Matrix *mx) +{ + SFVec3f v1, v2; + SFRotation r; + Fixed l1, l2, dot; + + /*compute rotation matrix from NLD axis to 0 0 1*/ + v1 = nld->axis; + gf_vec_norm(&v1); + v2.x = v2.y = 0; v2.z = FIX_ONE; + if (gf_vec_equal(v1, v2)) return 0; + + l1 = gf_vec_len(v1); + l2 = gf_vec_len(v2); + dot = gf_divfix(gf_vec_dot(v1, v2), gf_mulfix(l1, l2)); + + r.x = gf_mulfix(v1.y, v2.z) - gf_mulfix(v2.y, v1.z); + r.y = gf_mulfix(v1.z, v2.x) - gf_mulfix(v2.z, v1.x); + r.z = gf_mulfix(v1.x, v2.y) - gf_mulfix(v2.x, v1.y); + r.q = gf_atan2(gf_sqrt(FIX_ONE - gf_mulfix(dot, dot)), dot); + gf_mx_init(*mx); + gf_mx_add_rotation(mx, r.q, r.x, r.y, r.z); + return 1; +} + +static GFINLINE void NLD_GetKey(M_NonLinearDeformer *nld, Fixed frac, Fixed *f_min, Fixed *min, Fixed *f_max, Fixed *max) +{ + u32 i, count; + count = nld->extend.count; + if (count%2) count--; + + *f_min = 0; + *min = 0; + for (i=0; i=nld->extend.vals[i]) { + *f_min = nld->extend.vals[i]; + *min = nld->extend.vals[i+1]; + } + if ((i+2extend.vals[i+2])) { + *f_max = nld->extend.vals[i+2]; + *max = nld->extend.vals[i+3]; + return; + } + } + if (count) { + *f_max = nld->extend.vals[count-2]; + *max = nld->extend.vals[count-1]; + } else { + *f_max = FIX_ONE; + *max = GF_PI; + } +} + +static void NLD_Apply(M_NonLinearDeformer *nld, GF_Mesh *mesh) +{ + u32 i; + GF_Matrix mx; + SFVec3f n; + Fixed param, z_min, z_max, v_min, v_max, f_min, f_max, frac, val, a_cos, a_sin; + Bool needs_transform = NLD_GetMatrix(nld, &mx); + + param = nld->param; + if (!param) param = 1; + + if (mesh->bounds.min_edge.z == mesh->bounds.max_edge.z) return; + + z_min = FIX_MAX; + z_max = -FIX_MAX; + if (needs_transform) { + for (i=0; iv_count; i++) { + gf_mx_apply_vec(&mx, &mesh->vertices[i].pos); + MESH_GET_NORMAL(n, mesh->vertices[i]); + gf_mx_rotate_vector(&mx, &n); + MESH_SET_NORMAL(mesh->vertices[i], n); + + if (mesh->vertices[i].pos.zvertices[i].pos.z; + if (mesh->vertices[i].pos.z>z_max) z_max = mesh->vertices[i].pos.z; + } + } else { + z_min = mesh->bounds.min_edge.z; + z_max = mesh->bounds.max_edge.z; + } + + for (i=0; iv_count; i++) { + SFVec3f old = mesh->vertices[i].pos; + frac = gf_divfix(old.z - z_min, z_max - z_min); + NLD_GetKey(nld, frac, &f_min, &v_min, &f_max, &v_max); + if (f_max == f_min) { + val = v_min; + } else { + frac = gf_divfix(frac-f_min, f_max - f_min); + val = gf_mulfix(v_max - v_min, frac) + v_min; + } + val = gf_mulfix(val, param); + + switch (nld->type) { + /*taper*/ + case 0: + mesh->vertices[i].pos.x = gf_mulfix(mesh->vertices[i].pos.x, val); + mesh->vertices[i].pos.y = gf_mulfix(mesh->vertices[i].pos.y, val); + MESH_GET_NORMAL(old, mesh->vertices[i]); + n=old; + n.x = gf_mulfix(n.x, val); + n.y = gf_mulfix(n.y, val); + gf_vec_norm(&n); + MESH_SET_NORMAL(mesh->vertices[i], n); + break; + /*twist*/ + case 1: + a_cos = gf_cos(val); + a_sin = gf_sin(val); + mesh->vertices[i].pos.x = gf_mulfix(a_cos, old.x) - gf_mulfix(a_sin, old.y); + mesh->vertices[i].pos.y = gf_mulfix(a_sin, old.x) + gf_mulfix(a_cos, old.y); + MESH_GET_NORMAL(old, mesh->vertices[i]); + n=old; + n.x = gf_mulfix(a_cos, old.x) - gf_mulfix(a_sin, old.y); + n.y = gf_mulfix(a_sin, old.x) + gf_mulfix(a_cos, old.y); + gf_vec_norm(&n); + MESH_SET_NORMAL(mesh->vertices[i], n); + break; + /*bend*/ + case 2: + a_cos = gf_cos(val); + a_sin = gf_sin(val); + mesh->vertices[i].pos.x = gf_mulfix(a_sin, old.z) + gf_mulfix(a_cos, old.x); + mesh->vertices[i].pos.z = gf_mulfix(a_cos, old.z) - gf_mulfix(a_sin, old.x); + MESH_GET_NORMAL(old, mesh->vertices[i]); + n=old; + n.x = gf_mulfix(a_sin, old.z) + gf_mulfix(a_cos, old.x); + n.z = gf_mulfix(a_cos, old.z) - gf_mulfix(a_sin, old.x); + gf_vec_norm(&n); + MESH_SET_NORMAL(mesh->vertices[i], n); + break; + /*pinch, not standard (taper on X dim only)*/ + case 3: + mesh->vertices[i].pos.x = gf_mulfix(mesh->vertices[i].pos.x, val); + MESH_GET_NORMAL(old, mesh->vertices[i]); + n=old; + n.x = gf_mulfix(n.x, val); + gf_vec_norm(&n); + MESH_SET_NORMAL(mesh->vertices[i], n); + break; + } + } + if (needs_transform) { + gf_mx_inverse(&mx); + for (i=0; iv_count; i++) { + gf_mx_apply_vec(&mx, &mesh->vertices[i].pos); + MESH_GET_NORMAL(n, mesh->vertices[i]); + gf_mx_rotate_vector(&mx, &n); + MESH_SET_NORMAL(mesh->vertices[i], n); + } + } + mesh_update_bounds(mesh); + gf_mesh_build_aabbtree(mesh); +} + +static void build_shape_nld(GF_Node *n, Drawable3D *stack, GF_TraverseState *tr_state) +{ + M_NonLinearDeformer *nld = (M_NonLinearDeformer*)n; + Drawable3D *geo_st = (Drawable3D *)gf_node_get_private(nld->geometry); + + if (!nld->geometry) return; + if (!geo_st) return; + + mesh_clone(stack->mesh, geo_st->mesh); + /*apply deforms*/ + NLD_Apply(nld, stack->mesh); +} + +static void TraverseNonLinearDeformer(GF_Node *n, void *rs, Bool is_destroy) +{ + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + /*traverse geometry for get_bounds to make sure geometry is up to date*/ + if (!is_destroy && (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS)) { + gf_node_traverse(((M_NonLinearDeformer*)n)->geometry, tr_state); + } + + drawable_3d_base_traverse(n, rs, is_destroy, build_shape_nld); +} + +void compositor_init_non_linear_deformer(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseNonLinearDeformer); +} + +#endif /*GPAC_DISABLE_3D*/ diff --git a/src/compositor/mpeg4_geometry_ifs2d.c b/src/compositor/mpeg4_geometry_ifs2d.c new file mode 100644 index 0000000..58e44cc --- /dev/null +++ b/src/compositor/mpeg4_geometry_ifs2d.c @@ -0,0 +1,342 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "visual_manager.h" + +static void ifs2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) +{ + u32 i; + SFVec2f *pts; + u32 ci_count, c_count; + Bool started; + M_IndexedFaceSet2D *ifs2D; + M_Coordinate2D *coord; + + if (! gf_node_dirty_get(node)) return; + + ifs2D = (M_IndexedFaceSet2D *)node; + coord = (M_Coordinate2D *)ifs2D->coord; + drawable_reset_path(stack); + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); + + + c_count = coord->point.count; + ci_count = ifs2D->coordIndex.count; + pts = coord->point.vals; + + if (ci_count > 0) { + started = 0; + for (i=0; i < ci_count; i++) { + if (ifs2D->coordIndex.vals[i] == -1) { + gf_path_close(stack->path); + started = 0; + } else if (!started) { + started = 1; + gf_path_add_move_to_vec(stack->path, &pts[ifs2D->coordIndex.vals[i]]); + } else { + gf_path_add_line_to_vec(stack->path, &pts[ifs2D->coordIndex.vals[i]]); + } + } + if (started) gf_path_close(stack->path); + } else if (c_count) { + gf_path_add_move_to_vec(stack->path, &pts[0]); + for (i=1; i < c_count; i++) { + gf_path_add_line_to_vec(stack->path, &pts[i]); + } + gf_path_close(stack->path); + } +} + + +static void IFS2D_Draw(GF_Node *node, GF_TraverseState *tr_state) +{ + u32 i, count, ci_count; + u32 start_pts, j, ind_col, num_col; + SFVec2f center, end; + SFColor col_cen; + GF_STENCIL grad; + u32 *colors; + GF_Path *path; + SFVec2f start; + SFVec2f *pts; + SFColor col; + Fixed alpha; + GF_Raster2D *raster; + DrawableContext *ctx = tr_state->ctx; + M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node; + M_Coordinate2D *coord = (M_Coordinate2D*) ifs2D->coord; + M_Color *color = (M_Color *) ifs2D->color; + + col.red = col.green = col.blue = 0; + /*simple case, no color specified*/ + if (!ifs2D->color) { + visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state); + visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state); + return; + } + + /*if default face use first color*/ + ci_count = ifs2D->coordIndex.count; + pts = coord->point.vals; + + if (ci_count == 0) { + col = (ifs2D->colorIndex.count > 0) ? color->color.vals[ifs2D->colorIndex.vals[0]] : color->color.vals[0]; + + alpha = INT2FIX(GF_COL_A(ctx->aspect.fill_color)) / 255; + if (!alpha || !ctx->aspect.pen_props.width) { + alpha = INT2FIX(GF_COL_A(ctx->aspect.line_color)) / 255; + ctx->aspect.line_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + } else { + ctx->aspect.fill_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + } + visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state); + visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state); + return; + } + + /*we have color per faces so we need N path :(*/ + if (! ifs2D->colorPerVertex) { + path = gf_path_new(); + + count = 0; + i = 0; + while (1) { + gf_path_reset(path); + start = pts[ifs2D->coordIndex.vals[i]]; + gf_path_add_move_to(path, start.x, start.y); + i++; + + while (ifs2D->coordIndex.vals[i] != -1) { + start = pts[ifs2D->coordIndex.vals[i]]; + gf_path_add_line_to(path, start.x, start.y); + i++; + if (i >= ci_count) break; + } + /*close in ALL cases because even if the start/end points are the same the line join needs to be present*/ + gf_path_close(path); + + col = (ifs2D->colorIndex.count > 0) ? color->color.vals[ifs2D->colorIndex.vals[count]] : color->color.vals[count]; + + alpha = INT2FIX(GF_COL_A(ctx->aspect.fill_color)) / 255; + if (!alpha) { + alpha = INT2FIX(GF_COL_A(ctx->aspect.line_color)) / 255; + ctx->aspect.line_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + } else { + ctx->aspect.fill_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + } + + visual_2d_texture_path(tr_state->visual, path, ctx, tr_state); + visual_2d_draw_path(tr_state->visual, path, ctx, NULL, NULL, tr_state); + count++; + i++; + if (i >= ci_count) break; + ctx->flags &= ~CTX_PATH_FILLED; + ctx->flags &= ~CTX_PATH_STROKE; + } + gf_path_del(path); + return; + } + + /*final case, color per vertex means gradient fill/strike*/ + raster = tr_state->visual->compositor->rasterizer; + grad = raster->stencil_new(raster, GF_STENCIL_VERTEX_GRADIENT); + /*not supported, fill default*/ + if (!grad) { + visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state); + visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state); + return; + } + + + path = gf_path_new(); + + ind_col = 0; + i = 0; + while (1) { + gf_path_reset(path); + start = pts[ifs2D->coordIndex.vals[i]]; + center = start; + gf_path_add_move_to(path, start.x, start.y); + start_pts = i; + num_col = 1; + i+=1; + while (ifs2D->coordIndex.vals[i] != -1) { + end = pts[ifs2D->coordIndex.vals[i]]; + gf_path_add_line_to(path, end.x, end.y); + i++; + center.x += end.x; + center.y += end.y; + num_col ++; + if (i >= ci_count) break; + } + gf_path_close(path); + num_col++; + + alpha = INT2FIX(GF_COL_A(ctx->aspect.fill_color) ) / 255; + + colors = (u32*)malloc(sizeof(u32) * num_col); + col_cen.blue = col_cen.red = col_cen.green = 0; + for (j=0; jcolorIndex.count > ind_col + j) { + col = color->color.vals[ifs2D->colorIndex.vals[ind_col + j]]; + } else if (ci_count > ind_col + j) { + col = color->color.vals[ifs2D->coordIndex.vals[ind_col + j]]; + } + colors[j] = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + col_cen.blue += col.blue; + col_cen.green += col.green; + col_cen.red += col.red; + } + colors[num_col-1] = colors[0]; + + if (ifs2D->colorIndex.count > ind_col) { + col = color->color.vals[ifs2D->colorIndex.vals[ind_col]]; + } else if (ci_count > ind_col) { + col = color->color.vals[ifs2D->coordIndex.vals[ind_col]]; + } + col_cen.blue += col.blue; + col_cen.green += col.green; + col_cen.red += col.red; + + raster->stencil_set_vertex_path(grad, path); + raster->stencil_set_vertex_colors(grad, colors, num_col); + + free(colors); + + col_cen.blue /= num_col; + col_cen.green /= num_col; + col_cen.red /= num_col; + center.x /= num_col; + center.y /= num_col; + raster->stencil_set_vertex_center(grad, center.x, center.y, GF_COL_ARGB_FIXED(alpha, col_cen.red, col_cen.green, col_cen.blue) ); + + raster->stencil_set_matrix(grad, &ctx->transform); + + /*draw*/ + visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, grad, grad, tr_state); + + raster->stencil_delete(grad); + + //goto next point + i++; + ind_col += num_col + 1; + if (i >= ci_count) break; + grad = raster->stencil_new(raster, GF_STENCIL_VERTEX_GRADIENT); + ctx->flags &= ~CTX_PATH_FILLED; + ctx->flags &= ~CTX_PATH_STROKE; + } + gf_path_del(path); +} + +static void TraverseIFS2D(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + drawable_node_del(node); + return; + } + if (!ifs2D->coord) return; + + ifs2d_check_changes(node, stack, tr_state); + + switch (tr_state->traversing_mode) { + case TRAVERSE_DRAW_2D: + IFS2D_Draw(node, tr_state); + return; +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + { + DrawAspect2D asp; + + if (!stack->mesh) { + stack->mesh = new_mesh(); + mesh_new_ifs2d(stack->mesh, node); + } + + memset(&asp, 0, sizeof(DrawAspect2D)); + drawable_get_aspect_2d_mpeg4(node, &asp, tr_state); + if (ifs2D->color && !GF_COL_A(asp.fill_color) ) { + /*use special func to disable outline recompute and handle recompute ourselves*/ + StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, stack, &asp, tr_state->appear, NULL, 0, tr_state); + if (!si->mesh_outline) { + si->mesh_outline = new_mesh(); + mesh_new_ils(si->mesh_outline, ifs2D->coord, &ifs2D->coordIndex, ifs2D->color, &ifs2D->colorIndex, ifs2D->colorPerVertex, 1); + } + visual_3d_mesh_strike(tr_state, si->mesh_outline, asp.pen_props.width, asp.line_scale, asp.pen_props.dash); + } else { + visual_3d_draw_2d_with_aspect(stack, tr_state, &asp, 0); + } + return; + } +#endif + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + drawable_finalize_sort(ctx, tr_state, NULL); + return; + } +} + +static void IFS2D_SetColorIndex(GF_Node *node) +{ + M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node; + gf_sg_vrml_field_copy(&ifs2D->colorIndex, &ifs2D->set_colorIndex, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&ifs2D->set_colorIndex, GF_SG_VRML_MFINT32); +} + +static void IFS2D_SetCoordIndex(GF_Node *node) +{ + M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node; + gf_sg_vrml_field_copy(&ifs2D->coordIndex, &ifs2D->set_coordIndex, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&ifs2D->set_coordIndex, GF_SG_VRML_MFINT32); +} + +void compositor_init_indexed_face_set2d(GF_Compositor *compositor, GF_Node *node) +{ + M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node; + Drawable *stack = drawable_stack_new(compositor, node); + stack->flags = DRAWABLE_USE_TRAVERSE_DRAW; + gf_node_set_callback_function(node, TraverseIFS2D); + ifs2D->on_set_colorIndex = IFS2D_SetColorIndex; + ifs2D->on_set_coordIndex = IFS2D_SetCoordIndex; +} + diff --git a/src/compositor/mpeg4_geometry_ils2d.c b/src/compositor/mpeg4_geometry_ils2d.c new file mode 100644 index 0000000..136e776 --- /dev/null +++ b/src/compositor/mpeg4_geometry_ils2d.c @@ -0,0 +1,308 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "visual_manager.h" + +static void ils2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) +{ + u32 i; + Bool started; + SFVec2f *pts; + M_IndexedLineSet2D *ils2D; + M_Coordinate2D *coord; + + if (! gf_node_dirty_get(node)) return; + + drawable_reset_path(stack); + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); + + ils2D = (M_IndexedLineSet2D *)node; + coord = (M_Coordinate2D *)ils2D->coord; + + pts = coord->point.vals; + if (ils2D->coordIndex.count > 0) { + started = 0; + for (i=0; i < ils2D->coordIndex.count; i++) { + /*NO close on ILS2D*/ + if (ils2D->coordIndex.vals[i] == -1) { + started = 0; + } else if (!started) { + started = 1; + gf_path_add_move_to(stack->path, pts[ils2D->coordIndex.vals[i]].x, pts[ils2D->coordIndex.vals[i]].y); + } else { + gf_path_add_line_to(stack->path, pts[ils2D->coordIndex.vals[i]].x, pts[ils2D->coordIndex.vals[i]].y); + } + } + } else if (coord->point.count) { + gf_path_add_move_to(stack->path, pts[0].x, pts[0].y); + for (i=1; i < coord->point.count; i++) { + gf_path_add_line_to(stack->path, pts[i].x, pts[i].y); + } + } + stack->path->flags |= GF_PATH_FILL_ZERO_NONZERO; +} + +static void ILS2D_Draw(GF_Node *node, GF_TraverseState *tr_state) +{ + GF_Path *path; + SFVec2f *pts; + SFColor col; + Fixed alpha; + u32 i, count, col_ind, ind, end_at; + u32 linear[2], *colors; + SFVec2f start, end; + u32 j, num_col; + GF_STENCIL grad; + GF_Raster2D *raster; + DrawableContext *ctx = tr_state->ctx; + M_IndexedLineSet2D *ils2D = (M_IndexedLineSet2D *)node; + M_Coordinate2D *coord = (M_Coordinate2D*) ils2D->coord; + M_Color *color = (M_Color *) ils2D->color; + + end.x = end.y = 0; + if (!coord->point.count) return; + + if (! ils2D->color) { + /*no texturing*/ + visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state); + return; + } + + alpha = INT2FIX(GF_COL_A(ctx->aspect.line_color)) / 255; + pts = coord->point.vals; + + if (!ils2D->colorPerVertex || (color->color.count<2) ) { + count = 0; + end_at = ils2D->coordIndex.count; + if (!end_at) end_at = coord->point.count; + ind = ils2D->coordIndex.count ? ils2D->coordIndex.vals[0] : 0; + i=1; + path = gf_path_new(); + gf_path_add_move_to(path, pts[ind].x, pts[ind].y); + + for (; i<=end_at; i++) { + if ((i==end_at) || (ils2D->coordIndex.count && ils2D->coordIndex.vals[i] == -1)) { + + /*draw current*/ + col_ind = (ils2D->colorIndex.count) ? ils2D->colorIndex.vals[count] : count; + if (col_ind>=color->color.count) col_ind=color->color.count-1; + col = color->color.vals[col_ind]; + ctx->aspect.line_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + + visual_2d_draw_path(tr_state->visual, path, ctx, NULL, NULL, tr_state); + + i++; + if (i>=end_at) break; + gf_path_reset(path); + + ind = ils2D->coordIndex.count ? ils2D->coordIndex.vals[i] : i; + gf_path_add_move_to(path, pts[ind].x, pts[ind].y); + + if (ils2D->coordIndex.count) count++; + continue; + } else { + ind = ils2D->coordIndex.count ? ils2D->coordIndex.vals[i] : i; + gf_path_add_line_to(path, pts[ind].x, pts[ind].y); + } + } + gf_path_del(path); + return; + } + + raster = NULL; + end_at = ils2D->coordIndex.count; + if (!end_at) end_at = coord->point.count; + count = 0; + col_ind = 0; + ind = 0; + i=0; + path = gf_path_new(); + while (1) { + gf_path_reset(path); + ind = ils2D->coordIndex.count ? ils2D->coordIndex.vals[i] : i; + start = pts[ind]; + num_col = 1; + i++; + gf_path_add_move_to(path, start.x, start.y); + + if (ils2D->coordIndex.count) { + while (ils2D->coordIndex.vals[i] != -1) { + end = pts[ils2D->coordIndex.vals[i]]; + gf_path_add_line_to(path, end.x, end.y); + i++; + num_col++; + if (i >= ils2D->coordIndex.count) break; + } + } else { + while (ivisual->compositor->rasterizer; + /*use linear gradient*/ + if (num_col==2) { + Fixed pos[2]; + grad = raster->stencil_new(raster, GF_STENCIL_LINEAR_GRADIENT); + if (ils2D->colorIndex.count) { + col = color->color.vals[ils2D->colorIndex.vals[col_ind]]; + linear[0] = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + col = color->color.vals[ils2D->colorIndex.vals[col_ind+1]]; + linear[1] = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + } else if (ils2D->coordIndex.count) { + col = color->color.vals[ils2D->coordIndex.vals[col_ind]]; + linear[0] = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + col = color->color.vals[ils2D->coordIndex.vals[col_ind+1]]; + linear[1] = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + } else { + col = color->color.vals[col_ind]; + linear[0] = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + col = color->color.vals[col_ind+1]; + linear[1] = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + } + pos[0] = 0; pos[1] = FIX_ONE; + raster->stencil_set_linear_gradient(grad, start.x, start.y, end.x, end.y); + raster->stencil_set_gradient_interpolation(grad, pos, linear, 2); + } else { + grad = raster->stencil_new(raster, GF_STENCIL_VERTEX_GRADIENT); + if (grad) { + raster->stencil_set_vertex_path(grad, path); + + colors = (u32*)malloc(sizeof(u32) * num_col); + for (j=0; jcolorIndex.count>0) { + col = color->color.vals[ils2D->colorIndex.vals[col_ind+j]]; + } else if (ils2D->coordIndex.count) { + col = color->color.vals[ils2D->coordIndex.vals[col_ind+j]]; + } else { + col = color->color.vals[col_ind+j]; + } + colors[j] = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); + } + raster->stencil_set_vertex_colors(grad, colors, num_col); + free(colors); + } + } + raster->stencil_set_matrix(grad, &ctx->transform); + visual_2d_draw_path(tr_state->visual, path, ctx, NULL, grad, tr_state); + if (grad) raster->stencil_delete(grad); + + i ++; + col_ind += num_col + 1; + if (i >= ils2D->coordIndex.count) break; + ctx->flags &= ~CTX_PATH_STROKE; + } + gf_path_del(path); +} + + +static void TraverseILS2D(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + M_IndexedLineSet2D *ils2D = (M_IndexedLineSet2D *)node; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + drawable_node_del(node); + return; + } + + if (!ils2D->coord) return; + + ils2d_check_changes(node, stack, tr_state); + + switch (tr_state->traversing_mode) { + case TRAVERSE_DRAW_2D: + ILS2D_Draw(node, tr_state); + return; +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + if (!stack->mesh) { + stack->mesh = new_mesh(); + mesh_new_ils(stack->mesh, ils2D->coord, &ils2D->coordIndex, ils2D->color, &ils2D->colorIndex, ils2D->colorPerVertex, 0); + } + if (ils2D->color) { + DrawAspect2D asp; + memset(&asp, 0, sizeof(DrawAspect2D)); + drawable_get_aspect_2d_mpeg4(node, &asp, tr_state); + + visual_3d_mesh_strike(tr_state, stack->mesh, asp.pen_props.width, asp.line_scale, asp.pen_props.dash); + } else { + visual_3d_draw_2d(stack, tr_state); + } + return; +#endif + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + /*ILS2D are NEVER filled*/ + ctx->aspect.fill_color &= 0x00FFFFFF; + drawable_finalize_sort(ctx, tr_state, NULL); + return; + default: + return; + } +} + +static void ILS2D_SetColorIndex(GF_Node *node) +{ + M_IndexedLineSet2D *ils2D = (M_IndexedLineSet2D *)node; + gf_sg_vrml_field_copy(&ils2D->colorIndex, &ils2D->set_colorIndex, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&ils2D->set_colorIndex, GF_SG_VRML_MFINT32); +} + +static void ILS2D_SetCoordIndex(GF_Node *node) +{ + M_IndexedLineSet2D *ils2D = (M_IndexedLineSet2D *)node; + gf_sg_vrml_field_copy(&ils2D->coordIndex, &ils2D->set_coordIndex, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&ils2D->set_coordIndex, GF_SG_VRML_MFINT32); +} + +void compositor_init_indexed_line_set2d(GF_Compositor *compositor, GF_Node *node) +{ + M_IndexedLineSet2D *ils2D = (M_IndexedLineSet2D *)node; + Drawable *stack = drawable_stack_new(compositor, node); + stack->flags = DRAWABLE_USE_TRAVERSE_DRAW; + gf_node_set_callback_function(node, TraverseILS2D); + ils2D->on_set_colorIndex = ILS2D_SetColorIndex; + ils2D->on_set_coordIndex = ILS2D_SetCoordIndex; +} diff --git a/src/compositor/mpeg4_gradients.c b/src/compositor/mpeg4_gradients.c new file mode 100644 index 0000000..b0c7de9 --- /dev/null +++ b/src/compositor/mpeg4_gradients.c @@ -0,0 +1,597 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" +#include "texturing.h" + + +#define GRAD_TEXTURE_SIZE 128 +#define GRAD_TEXTURE_HSIZE 64 + +/*linear/radial gradient*/ +typedef struct +{ + GF_TextureHandler txh; + char *tx_data; + Bool transparent; + Bool no_rgb_support; +} GradientStack; + +void GradientGetMatrix(GF_Node *transform, GF_Matrix2D *mat) +{ + gf_mx2d_init(*mat); + if (transform) { + switch (gf_node_get_tag(transform) ) { + case TAG_MPEG4_Transform2D: + { + M_Transform2D *tr = (M_Transform2D *)transform; + gf_mx2d_add_scale_at(mat, 0, 0, tr->scale.x, tr->scale.y, tr->scaleOrientation); + gf_mx2d_add_rotation(mat, tr->center.x, tr->center.y, tr->rotationAngle); + gf_mx2d_add_translation(mat, tr->translation.x, tr->translation.y); + } + break; + case TAG_MPEG4_TransformMatrix2D: + { + M_TransformMatrix2D *tm = (M_TransformMatrix2D*)transform; + gf_mx2d_init(*mat); + mat->m[0] = tm->mxx; mat->m[1] = tm->mxy; mat->m[2] = tm->tx; + mat->m[3] = tm->myx; mat->m[4] = tm->myy; mat->m[5] = tm->ty; + } + break; + default: + break; + } + } +} + +static void DestroyGradient(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + GradientStack *st = (GradientStack *) gf_node_get_private(node); + gf_sc_texture_destroy(&st->txh); + if (st->tx_data) free(st->tx_data); + free(st); + } +} + +static void UpdateLinearGradient(GF_TextureHandler *txh) +{ + u32 i, *cols; + Fixed a; + Bool const_a; + GF_STENCIL stencil; + M_LinearGradient *lg = (M_LinearGradient *) txh->owner; + GradientStack *st = (GradientStack *) gf_node_get_private(txh->owner); + + if (!gf_node_dirty_get(txh->owner)) { + txh->needs_refresh = 0; + return; + } + if (lg->key.count > lg->keyValue.count) return; + + if (!txh->tx_io) gf_sc_texture_allocate(txh); + + stencil = gf_sc_texture_get_stencil(txh); + if (!stencil) stencil = txh->compositor->rasterizer->stencil_new(txh->compositor->rasterizer, GF_STENCIL_LINEAR_GRADIENT); + /*set stencil even if assigned, this invalidates the associated bitmap state in 3D*/ + gf_sc_texture_set_stencil(txh, stencil); + + gf_node_dirty_clear(txh->owner, 0); + txh->needs_refresh = 1; + + st->txh.transparent = 0; + const_a = (lg->opacity.count == 1) ? 1 : 0; + cols = (u32*)malloc(sizeof(u32) * lg->key.count); + for (i=0; ikey.count; i++) { + a = (const_a ? lg->opacity.vals[0] : lg->opacity.vals[i]); + cols[i] = GF_COL_ARGB_FIXED(a, lg->keyValue.vals[i].red, lg->keyValue.vals[i].green, lg->keyValue.vals[i].blue); + if (a != FIX_ONE) txh->transparent = 1; + } + txh->compositor->rasterizer->stencil_set_gradient_interpolation(stencil, lg->key.vals, cols, lg->key.count); + free(cols); + txh->compositor->rasterizer->stencil_set_gradient_mode(stencil, (GF_GradientMode) lg->spreadMethod); +} + + +static void LG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat, Bool for_3d) +{ + GF_STENCIL stencil; + M_LinearGradient *lg = (M_LinearGradient *) txh->owner; + + stencil = gf_sc_texture_get_stencil(txh); + if (!stencil) return; + + if (lg->key.count<2) return; + if (lg->key.count != lg->keyValue.count) return; + + /*create gradient brush if needed*/ + if (!txh->tx_io) return; + + GradientGetMatrix((GF_Node *) lg->transform, mat); + + /*translate to the center of the bounds*/ + gf_mx2d_add_translation(mat, gf_divfix(bounds->x, bounds->width), gf_divfix(bounds->y - bounds->height, bounds->height)); + /*scale back to object coordinates - the gradient is still specified in texture coordinates + i order to avoid overflows in fixed point*/ + gf_mx2d_add_scale(mat, bounds->width, bounds->height); + + txh->compositor->rasterizer->stencil_set_linear_gradient(stencil, lg->startPoint.x, lg->startPoint.y, lg->endPoint.x, lg->endPoint.y); +} + +static void BuildLinearGradientTexture(GF_TextureHandler *txh) +{ + u32 i; + SFVec2f start, end; + u32 *cols; + Fixed a; + Bool const_a; + GF_Matrix2D mat; + GF_STENCIL stenc; + GF_SURFACE surface; + GF_STENCIL texture2D; + GF_Path *path; + GF_Err e; + Bool transparent; + M_LinearGradient *lg = (M_LinearGradient *) txh->owner; + GradientStack *st = (GradientStack *) gf_node_get_private(txh->owner); + GF_Raster2D *raster = txh->compositor->rasterizer; + + if (!txh->tx_io) return; + + + if (st->tx_data) { + free(st->tx_data); + st->tx_data = NULL; + } + + if (lg->key.count<2) return; + if (lg->key.count != lg->keyValue.count) return; + + start = lg->startPoint; + end = lg->endPoint; + + transparent = (lg->opacity.count==1) ? (lg->opacity.vals[0]!=FIX_ONE) : 1; + + /*init our 2D graphics stuff*/ + texture2D = raster->stencil_new(raster, GF_STENCIL_TEXTURE); + if (!texture2D) return; + surface = raster->surface_new(raster, 1); + if (!surface) { + raster->stencil_delete(texture2D); + return; + } + + if (st->no_rgb_support) transparent = 1; + if (st->tx_data && (st->transparent != transparent)) { + free(st->tx_data); + st->tx_data = NULL; + } + + if (transparent) { + if (!st->tx_data) { + st->tx_data = (char *) malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4); + } else { + memset(st->tx_data, 0, sizeof(char)*txh->stride*txh->height); + } + e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, GF_PIXEL_ARGB, GF_PIXEL_ARGB, 1); + } else { + if (!st->tx_data) { + st->tx_data = (char *) malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*3); + } + e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 3*GRAD_TEXTURE_SIZE, GF_PIXEL_RGB_24, GF_PIXEL_RGB_24, 1); + /*try with ARGB (it actually is needed for GDIplus module since GDIplus cannot handle native RGB texture (it works in BGR)*/ + if (e) { + /*remember for later use*/ + st->no_rgb_support = 1; + transparent = 1; + free(st->tx_data); + st->tx_data = (char *) malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4); + e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, GF_PIXEL_ARGB, GF_PIXEL_ARGB, 1); + } + } + st->transparent = transparent; + + if (e) { + free(st->tx_data); + raster->stencil_delete(texture2D); + raster->surface_delete(surface); + return; + } + e = raster->surface_attach_to_texture(surface, texture2D); + if (e) { + raster->stencil_delete(texture2D); + raster->surface_delete(surface); + return; + } + + /*create & setup gradient*/ + stenc = raster->stencil_new(raster, GF_STENCIL_LINEAR_GRADIENT); + if (!stenc) { + raster->stencil_delete(texture2D); + raster->surface_delete(surface); + return; + } + /*move line to object space*/ + start.x *= GRAD_TEXTURE_SIZE; + end.x *= GRAD_TEXTURE_SIZE; + start.y *= GRAD_TEXTURE_SIZE; + end.y *= GRAD_TEXTURE_SIZE; + raster->stencil_set_linear_gradient(stenc, start.x, start.y, end.x, end.y); + const_a = (lg->opacity.count == 1) ? 1 : 0; + cols = (u32*)malloc(sizeof(u32) * lg->key.count); + for (i=0; ikey.count; i++) { + a = (const_a ? lg->opacity.vals[0] : lg->opacity.vals[i]); + cols[i] = GF_COL_ARGB_FIXED(a, lg->keyValue.vals[i].red, lg->keyValue.vals[i].green, lg->keyValue.vals[i].blue); + } + raster->stencil_set_gradient_interpolation(stenc, lg->key.vals, cols, lg->key.count); + free(cols); + raster->stencil_set_gradient_mode(stenc, (GF_GradientMode)lg->spreadMethod); + + /*fill surface*/ + path = gf_path_new(); + gf_path_add_move_to(path, -INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE)); + gf_path_add_line_to(path, INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE)); + gf_path_add_line_to(path, INT2FIX(GRAD_TEXTURE_HSIZE), INT2FIX(GRAD_TEXTURE_HSIZE)); + gf_path_add_line_to(path, -INT2FIX(GRAD_TEXTURE_HSIZE), INT2FIX(GRAD_TEXTURE_HSIZE)); + gf_path_close(path); + + /*add gradient transform*/ + GradientGetMatrix(lg->transform, &mat); + + /*move transform to object space*/ + mat.m[2] *= GRAD_TEXTURE_SIZE; + mat.m[5] *= GRAD_TEXTURE_SIZE; + /*translate to the center of the bounds*/ + gf_mx2d_add_translation(&mat, -INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE)); + /*back to GL bottom->up order*/ + gf_mx2d_add_scale(&mat, FIX_ONE, -FIX_ONE); + raster->stencil_set_matrix(stenc, &mat); + + raster->surface_set_raster_level(surface, GF_RASTER_HIGH_QUALITY); + raster->surface_set_path(surface, path); + raster->surface_fill(surface, stenc); + raster->stencil_delete(stenc); + raster->surface_delete(surface); + raster->stencil_delete(texture2D); + gf_path_del(path); + + txh->data = st->tx_data; + txh->width = GRAD_TEXTURE_SIZE; + txh->height = GRAD_TEXTURE_SIZE; + txh->transparent = transparent; + if (transparent) { + u32 j; + txh->stride = GRAD_TEXTURE_SIZE*4; + + /*back to RGBA texturing*/ + txh->pixelformat = GF_PIXEL_RGBA; + for (i=0; iheight; i++) { + char *data = txh->data + i*txh->stride; + for (j=0; jwidth; j++) { + u32 val = *(u32 *) &data[4*j]; + data[4*j] = (val>>16) & 0xFF; + data[4*j+1] = (val>>8) & 0xFF; + data[4*j+2] = (val) & 0xFF; + data[4*j+3] = (val>>24) & 0xFF; + } + } + } else { + txh->stride = GRAD_TEXTURE_SIZE*3; + txh->pixelformat = GF_PIXEL_RGB_24; + } + txh->flags |= GF_SR_TEXTURE_NO_GL_FLIP; + gf_sc_texture_set_data(txh); +} + +void compositor_init_linear_gradient(GF_Compositor *compositor, GF_Node *node) +{ + GradientStack *st; + GF_SAFEALLOC(st, GradientStack); + + gf_sc_texture_setup(&st->txh, compositor, node); + st->txh.update_texture_fcnt = UpdateLinearGradient; + st->txh.compute_gradient_matrix = LG_ComputeMatrix; + + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroyGradient); +} + + +static void BuildRadialGradientTexture(GF_TextureHandler *txh) +{ + u32 i; + SFVec2f center, focal; + u32 *cols; + Fixed a, radius; + Bool const_a; + GF_Matrix2D mat; + GF_STENCIL stenc; + GF_SURFACE surface; + GF_STENCIL texture2D; + GF_Path *path; + GF_Err e; + Bool transparent; + M_RadialGradient *rg = (M_RadialGradient*) txh->owner; + GradientStack *st = (GradientStack *) gf_node_get_private(txh->owner); + GF_Raster2D *raster = txh->compositor->rasterizer; + + + if (!txh->tx_io) return; + + if (st->tx_data) { + free(st->tx_data); + st->tx_data = NULL; + } + + if (rg->key.count<2) return; + if (rg->key.count != rg->keyValue.count) return; + + transparent = (rg->opacity.count==1) ? ((rg->opacity.vals[0]!=FIX_ONE) ? 1 : 0) : 1; + + /*init our 2D graphics stuff*/ + texture2D = raster->stencil_new(raster, GF_STENCIL_TEXTURE); + if (!texture2D) return; + surface = raster->surface_new(raster, 1); + if (!surface) { + raster->stencil_delete(texture2D); + return; + } + + if (st->no_rgb_support) transparent = 1; + if (st->tx_data && (st->transparent != transparent)) { + free(st->tx_data); + st->tx_data = NULL; + } + + if (transparent) { + if (!st->tx_data) { + st->tx_data = (char *) malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4); + } else { + memset(st->tx_data, 0, sizeof(char)*txh->stride*txh->height); + } + e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, GF_PIXEL_ARGB, GF_PIXEL_ARGB, 1); + } else { + if (!st->tx_data) { + st->tx_data = (char *) malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*3); + } + e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 3*GRAD_TEXTURE_SIZE, GF_PIXEL_RGB_24, GF_PIXEL_RGB_24, 1); + /*try with ARGB (it actually is needed for GDIplus module since GDIplus cannot handle native RGB texture (it works in BGR)*/ + if (e) { + /*remember for later use*/ + st->no_rgb_support = 1; + transparent = 1; + free(st->tx_data); + st->tx_data = (char *) malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4); + e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, GF_PIXEL_ARGB, GF_PIXEL_ARGB, 1); + } + } + st->transparent = transparent; + + if (e) { + free(st->tx_data); + raster->stencil_delete(texture2D); + raster->surface_delete(surface); + return; + } + e = raster->surface_attach_to_texture(surface, texture2D); + if (e) { + raster->stencil_delete(texture2D); + raster->surface_delete(surface); + return; + } + + /*create & setup gradient*/ + stenc = raster->stencil_new(raster, GF_STENCIL_RADIAL_GRADIENT); + if (!stenc) { + raster->stencil_delete(texture2D); + raster->surface_delete(surface); + } + + center = rg->center; + focal = rg->focalPoint; + radius = rg->radius; + + /*move circle to object space*/ + center.x *= GRAD_TEXTURE_SIZE; + center.y *= GRAD_TEXTURE_SIZE; + focal.x *= GRAD_TEXTURE_SIZE; + focal.y *= GRAD_TEXTURE_SIZE; + radius *= GRAD_TEXTURE_SIZE; + + raster->stencil_set_radial_gradient(stenc, center.x, center.y, focal.x, focal.y, radius, radius); + + const_a = (rg->opacity.count == 1) ? 1 : 0; + cols = (u32*) malloc(sizeof(u32) * rg->key.count); + for (i=0; ikey.count; i++) { + a = (const_a ? rg->opacity.vals[0] : rg->opacity.vals[i]); + cols[i] = GF_COL_ARGB_FIXED(a, rg->keyValue.vals[i].red, rg->keyValue.vals[i].green, rg->keyValue.vals[i].blue); + } + raster->stencil_set_gradient_interpolation(stenc, rg->key.vals, cols, rg->key.count); + free(cols); + raster->stencil_set_gradient_mode(stenc, (GF_GradientMode)rg->spreadMethod); + + /*fill surface*/ + path = gf_path_new(); + gf_path_add_move_to(path, -INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE)); + gf_path_add_line_to(path, INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE)); + gf_path_add_line_to(path, INT2FIX(GRAD_TEXTURE_HSIZE), INT2FIX(GRAD_TEXTURE_HSIZE)); + gf_path_add_line_to(path, -INT2FIX(GRAD_TEXTURE_HSIZE), INT2FIX(GRAD_TEXTURE_HSIZE)); + gf_path_close(path); + + /*add gradient transform*/ + GradientGetMatrix(rg->transform, &mat); + /*move transform to object space*/ + mat.m[2] *= GRAD_TEXTURE_SIZE; + mat.m[5] *= GRAD_TEXTURE_SIZE; + /*translate to the center of the bounds*/ + gf_mx2d_add_translation(&mat, -INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE)); + /*back to GL bottom->up order*/ + gf_mx2d_add_scale(&mat, FIX_ONE, -FIX_ONE); + raster->stencil_set_matrix(stenc, &mat); + + raster->surface_set_raster_level(surface, GF_RASTER_HIGH_QUALITY); + raster->surface_set_path(surface, path); + raster->surface_fill(surface, stenc); + raster->stencil_delete(stenc); + raster->surface_delete(surface); + raster->stencil_delete(texture2D); + gf_path_del(path); + + txh->data = st->tx_data; + txh->width = GRAD_TEXTURE_SIZE; + txh->height = GRAD_TEXTURE_SIZE; + txh->transparent = transparent; + if (transparent) { + u32 j; + txh->stride = GRAD_TEXTURE_SIZE*4; + + /*back to RGBA texturing*/ + txh->pixelformat = GF_PIXEL_RGBA; + for (i=0; iheight; i++) { + char *data = txh->data + i*txh->stride; + for (j=0; jwidth; j++) { + u32 val = *(u32 *) &data[4*j]; + data[4*j] = (val>>16) & 0xFF; + data[4*j+1] = (val>>8) & 0xFF; + data[4*j+2] = (val) & 0xFF; + data[4*j+3] = (val>>24) & 0xFF; + } + } + } else { + txh->stride = GRAD_TEXTURE_SIZE*3; + txh->pixelformat = GF_PIXEL_RGB_24; + } +// tx_set_blend_enable(txh, 1); + txh->flags |= GF_SR_TEXTURE_NO_GL_FLIP; + gf_sc_texture_set_data(txh); + return; +} + +static void UpdateRadialGradient(GF_TextureHandler *txh) +{ + Bool const_a; + GF_STENCIL stencil; + u32 i, *cols; + Fixed a; + M_RadialGradient *rg = (M_RadialGradient*) txh->owner; + GradientStack *st = (GradientStack *) gf_node_get_private(txh->owner); + + if (!gf_node_dirty_get(txh->owner)) { + txh->needs_refresh = 0; + return; + } + if (rg->key.count > rg->keyValue.count) return; + + if (!txh->tx_io) gf_sc_texture_allocate(txh); + + stencil = gf_sc_texture_get_stencil(txh); + if (!stencil)stencil = txh->compositor->rasterizer->stencil_new(txh->compositor->rasterizer, GF_STENCIL_RADIAL_GRADIENT); + /*set stencil even if assigned, this invalidates the associated bitmap state in 3D*/ + gf_sc_texture_set_stencil(txh, stencil); + + gf_node_dirty_clear(txh->owner, 0); + txh->needs_refresh = 1; + + st->txh.transparent = 0; + for (i=0; iopacity.count; i++) { + if (rg->opacity.vals[i] != FIX_ONE) { + st->txh.transparent = 1; + break; + } + } + + const_a = (rg->opacity.count == 1) ? 1 : 0; + cols = (u32*)malloc(sizeof(u32) * rg->key.count); + for (i=0; ikey.count; i++) { + a = (const_a ? rg->opacity.vals[0] : rg->opacity.vals[i]); + cols[i] = GF_COL_ARGB_FIXED(a, rg->keyValue.vals[i].red, rg->keyValue.vals[i].green, rg->keyValue.vals[i].blue); + } + txh->compositor->rasterizer->stencil_set_gradient_interpolation(stencil, rg->key.vals, cols, rg->key.count); + free(cols); + + txh->compositor->rasterizer->stencil_set_gradient_mode(stencil, (GF_GradientMode) rg->spreadMethod); + +} + +static void RG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat, Bool for_3d) +{ + GF_STENCIL stencil; + M_RadialGradient *rg = (M_RadialGradient *) txh->owner; + + if (rg->key.count<2) return; + if (rg->key.count != rg->keyValue.count) return; + + if (!txh->tx_io) return; + + stencil = gf_sc_texture_get_stencil(txh); + if (!stencil) return; + + GradientGetMatrix((GF_Node *) rg->transform, mat); + + txh->compositor->rasterizer->stencil_set_radial_gradient(stencil, rg->center.x, rg->center.y, rg->focalPoint.x, rg->focalPoint.y, rg->radius, rg->radius); + /*move to center of bounds*/ + gf_mx2d_add_translation(mat, gf_divfix(bounds->x, bounds->width), gf_divfix(bounds->y - bounds->height, bounds->height)); + /*scale back to object coordinates - the gradient is still specified in texture coordinates + i order to avoid overflows in fixed point*/ + gf_mx2d_add_scale(mat, bounds->width, bounds->height); +} + + +void compositor_init_radial_gradient(GF_Compositor *compositor, GF_Node *node) +{ + GradientStack *st; + GF_SAFEALLOC(st, GradientStack); + + gf_sc_texture_setup(&st->txh, compositor, node); + st->txh.update_texture_fcnt = UpdateRadialGradient; + st->txh.compute_gradient_matrix = RG_ComputeMatrix; + + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroyGradient); +} + +GF_TextureHandler *compositor_mpeg4_get_gradient_texture(GF_Node *node) +{ + GradientStack *st = (GradientStack*) gf_node_get_private(node); + return &st->txh; +} + +void compositor_gradient_update(GF_TextureHandler *txh) +{ + switch (gf_node_get_tag(txh->owner) ) { + case TAG_MPEG4_RadialGradient: + BuildRadialGradientTexture(txh); + break; + case TAG_MPEG4_LinearGradient: + BuildLinearGradientTexture(txh); + break; +#ifndef GPAC_DISABLE_SVG + case TAG_SVG_linearGradient: + case TAG_SVG_radialGradient: + compositor_svg_build_gradient_texture(txh); + break; +#endif + } +} + diff --git a/src/compositor/mpeg4_grouping.c b/src/compositor/mpeg4_grouping.c new file mode 100644 index 0000000..ba44156 --- /dev/null +++ b/src/compositor/mpeg4_grouping.c @@ -0,0 +1,826 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "nodes_stacks.h" +#include "mpeg4_grouping.h" +#include "visual_manager.h" + +/*This is the generic routine for child traversing*/ +void group_2d_traverse(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state) +{ + u32 backup; +#ifdef GF_SR_USE_VIDEO_CACHE + Bool group_cached; +#endif + GF_List *sensor_backup; + GF_ChildNodeItem *child; + + backup = gf_node_dirty_get(node); + if (backup & GF_SG_CHILD_DIRTY) { + u32 ntag = gf_node_get_tag(node); + group->flags &= ~GROUP_HAS_SENSORS; + drawable_reset_group_highlight(tr_state, node); + + /*never performs bounds recompute on the fly in 2D since we don't cull 2D groups + but still mark the group as empty*/ + group->bounds.width = 0; + /*special case for anchor which is a parent node acting as a sensor*/ + if ((ntag==TAG_MPEG4_Anchor) || (ntag==TAG_X3D_Anchor)) { + group->flags |= GROUP_HAS_SENSORS | GROUP_IS_ANCHOR; + } else { + child = ((GF_ParentNode *)node)->children; + while (child) { + if (compositor_mpeg4_is_sensor_node(child->node)) { + group->flags |= GROUP_HAS_SENSORS; + break; + } + child = child->next; + } + } + } + /*sub-tree not dirty and getting bounds, direct copy */ + else if ((tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) && group->bounds.width) { + tr_state->bounds = group->bounds; + return; + } + + +#ifdef GF_SR_USE_VIDEO_CACHE + group_cached = group_2d_cache_traverse(node, group, tr_state); +#endif + + /*now here is the trick: ExternProtos may not be loaded at this point, in which case we can't + perform proper culling. Unloaded ExternProto signal themselves by invalidating their parent + graph to get a new traversal. We must therefore reset the CHILD_DIRTY flag before computing + bounds otherwise we'll never re-invalidate the subgraph anymore*/ + gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY); + + +#ifdef GF_SR_USE_VIDEO_CACHE + if (group_cached) return; +#endif + + /*no culling in 2d*/ + + /*picking: collect sensors*/ + sensor_backup = NULL; + if ((tr_state->traversing_mode==TRAVERSE_PICK) && (group->flags & GROUP_HAS_SENSORS) ) { + GF_SensorHandler *hsens; + /*reset sensor stack if any sensors at this level*/ + sensor_backup = tr_state->vrml_sensors; + tr_state->vrml_sensors = gf_list_new(); + + if (group->flags & GROUP_IS_ANCHOR) { + GF_SensorHandler *gf_sc_anchor_get_handler(GF_Node *n); + + hsens = gf_sc_anchor_get_handler(node); + if (hsens) gf_list_add(tr_state->vrml_sensors, hsens); + } else { + /*add sensor(s) to traversing state*/ + child = ((GF_ParentNode *)node)->children; + while (child) { + hsens = compositor_mpeg4_get_sensor_handler(child->node); + if (hsens) gf_list_add(tr_state->vrml_sensors, hsens); + child = child->next; + } + } + } + + + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + child = ((GF_ParentNode *)node)->children; + backup = tr_state->text_split_mode; + if (tr_state->text_split_mode && (gf_node_list_get_count(child)>1) ) tr_state->text_split_mode = 0; + group->flags &= ~GROUP_SKIP_CULLING; + group->bounds.width = group->bounds.height = 0; + tr_state->bounds.width = tr_state->bounds.height = 0; +#ifndef GPAC_DISABLE_3D + tr_state->bbox.is_set = 0; +#endif + while (child) { + gf_node_traverse(child->node, tr_state); + if (tr_state->disable_cull) { + group->flags |= GROUP_SKIP_CULLING; + tr_state->disable_cull = 0; + } + /*handle 3D nodes in 2D groups*/ +#ifndef GPAC_DISABLE_3D + if (tr_state->bbox.is_set) { + gf_rect_from_bbox(&tr_state->bounds, &tr_state->bbox); + tr_state->bbox.is_set = 0; + } +#endif + gf_rect_union(&group->bounds, &tr_state->bounds); + tr_state->bounds.width = tr_state->bounds.height = 0; + child = child->next; + } + + tr_state->bounds = group->bounds; + + if (group->flags & GROUP_SKIP_CULLING) + tr_state->disable_cull = 1; + tr_state->text_split_mode = backup; + } + /*TRAVERSE_SORT */ + else if (tr_state->traversing_mode==TRAVERSE_SORT) { + Bool prev_inv = tr_state->invalidate_all; +#ifdef GF_SR_USE_VIDEO_CACHE + DrawableContext *first_ctx = tr_state->visual->cur_context; + Bool skip_first_ctx = (first_ctx && first_ctx->drawable) ? 1 : 0; + u32 cache_too_small = 0; + u32 traverse_time = gf_sys_clock(); + u32 last_cache_idx = gf_list_count(tr_state->visual->compositor->cached_groups_queue); + tr_state->cache_too_small = 0; +#endif + + if (backup & GF_SG_VRML_COLOR_DIRTY) { + tr_state->invalidate_all = 1; + gf_node_dirty_clear(node, GF_SG_VRML_COLOR_DIRTY); + } + + child = ((GF_ParentNode *)node)->children; + while (child) { + gf_node_traverse(child->node, tr_state); + child = child->next; +#ifdef GF_SR_USE_VIDEO_CACHE + if (tr_state->cache_too_small) + cache_too_small++; +#endif + } + + tr_state->invalidate_all = prev_inv; + +#ifdef GF_SR_USE_VIDEO_CACHE + if (cache_too_small) { + tr_state->cache_too_small = 1; + } else { + /*get the traversal time for each group*/ + traverse_time = gf_sys_clock() - traverse_time; + group->traverse_time += traverse_time; + /*record the traversal information and turn cache on if possible*/ + group_2d_cache_evaluate(node, group, tr_state, first_ctx, skip_first_ctx, last_cache_idx); + } +#endif + + drawable_check_focus_highlight(node, tr_state, NULL); + } + else { + if (!strcmp(gf_node_get_log_name(node), "CLIP219_DL")) + child = ((GF_ParentNode *)node)->children; + child = ((GF_ParentNode *)node)->children; + while (child) { + gf_node_traverse(child->node, tr_state); + child = child->next; + } + + } + + if (sensor_backup) { + /*destroy current traversing state sensors and restore previous*/ + gf_list_del(tr_state->vrml_sensors); + tr_state->vrml_sensors = sensor_backup; + } + +} + +/*This is the routine for OrderedGroup child traversing*/ +void group_2d_traverse_with_order(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state, u32 *positions) +{ + u32 i, count; + Bool backup; + GF_List *sensor_backup; + GF_Node *child; + GF_ChildNodeItem *list; +#ifdef GF_SR_USE_VIDEO_CACHE + Bool group_cached; +#endif + + backup = gf_node_dirty_get(node); + if (backup & GF_SG_CHILD_DIRTY) { + /*never trigger bounds recompute in 2D since we don't cull 2D groups*/ + u32 ntag = gf_node_get_tag(node); + group->flags &= ~GROUP_HAS_SENSORS; + drawable_reset_group_highlight(tr_state, node); + /*special case for anchor which is a parent node acting as a sensor*/ + if ((ntag==TAG_MPEG4_Anchor) || (ntag==TAG_X3D_Anchor)) { + group->flags |= GROUP_HAS_SENSORS | GROUP_IS_ANCHOR; + } else { + list = ((GF_ParentNode *)node)->children; + count = gf_node_list_get_count(list); + for (i=0; iflags |= GROUP_HAS_SENSORS; + break; + } + } + } + } + /*not parent (eg form, layout...) sub-tree not dirty and getting bounds, direct copy */ + else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + tr_state->bounds = group->bounds; + return; + } + +#ifdef GF_SR_USE_VIDEO_CACHE + group_cached = group_2d_cache_traverse(node, group, tr_state); +#endif + + /*now here is the trick: ExternProtos may not be loaded at this point, in which case we can't + perform proper culling. Unloaded ExternProto signal themselves by invalidating their parent + graph to get a new traversal. We must therefore reset the CHILD_DIRTY flag before computing + bounds otherwise we'll never re-invalidate the subgraph anymore*/ + gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY); + + +#ifdef GF_SR_USE_VIDEO_CACHE + if (group_cached) return; +#endif + + /*picking: collect sensors*/ + sensor_backup = NULL; + if ((tr_state->traversing_mode==TRAVERSE_PICK) && (group->flags & GROUP_HAS_SENSORS) ) { + GF_SensorHandler *hsens; + /*reset sensor stack if any sensors at this level*/ + sensor_backup = tr_state->vrml_sensors; + tr_state->vrml_sensors = gf_list_new(); + + + /*add sensor(s) to traversing state*/ + list = ((GF_ParentNode *)node)->children; + count = gf_node_list_get_count(list); + for (i=0; ivrml_sensors, hsens); + } + } + + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + list = ((GF_ParentNode *)node)->children; + backup = tr_state->text_split_mode; + if (tr_state->text_split_mode && (gf_node_list_get_count(list)>1) ) tr_state->text_split_mode = 0; + group->flags &= ~GROUP_SKIP_CULLING; + group->bounds.width = group->bounds.height = 0; + tr_state->bounds.width = tr_state->bounds.height = 0; +#ifndef GPAC_DISABLE_3D + tr_state->bbox.is_set = 0; +#endif + count = gf_node_list_get_count(list); + for (i=0; idisable_cull) { + group->flags |= GROUP_SKIP_CULLING; + tr_state->disable_cull = 0; + } + /*handle 3D nodes in 2D groups*/ +#ifndef GPAC_DISABLE_3D + if (tr_state->bbox.is_set) { + gf_rect_from_bbox(&tr_state->bounds, &tr_state->bbox); + tr_state->bbox.is_set = 0; + } +#endif + gf_rect_union(&group->bounds, &tr_state->bounds); + tr_state->bounds.width = tr_state->bounds.height = 0; + } + tr_state->bounds = group->bounds; + + if (group->flags & GROUP_SKIP_CULLING) + tr_state->disable_cull = 1; + tr_state->text_split_mode = backup; + + /*TRAVERSE_SORT */ + } else if (tr_state->traversing_mode==TRAVERSE_SORT) { + Bool prev_inv = tr_state->invalidate_all; +#ifdef GF_SR_USE_VIDEO_CACHE + DrawableContext *first_ctx = tr_state->visual->cur_context; + u32 cache_too_small = 0; + Bool skip_first_ctx = (first_ctx && first_ctx->drawable) ? 1 : 0; + u32 traverse_time = gf_sys_clock(); + u32 last_cache_idx = gf_list_count(tr_state->visual->compositor->cached_groups_queue); + tr_state->cache_too_small = 0; +#endif + + if (backup & GF_SG_VRML_COLOR_DIRTY) { + tr_state->invalidate_all = 1; + gf_node_dirty_clear(node, GF_SG_VRML_COLOR_DIRTY); + } + + list = ((GF_ParentNode *)node)->children; + count = gf_node_list_get_count(list); + for (i=0; icache_too_small) + cache_too_small++; +#endif + } + tr_state->invalidate_all = prev_inv; + +#ifdef GF_SR_USE_VIDEO_CACHE + if (cache_too_small) { + tr_state->cache_too_small = 1; + } else { + /*get the traversal time for each group*/ + traverse_time = gf_sys_clock() - traverse_time; + group->traverse_time += traverse_time; + /*record the traversal information and turn cache on if possible*/ + group_2d_cache_evaluate(node, group, tr_state, first_ctx, skip_first_ctx, last_cache_idx); + } +#endif + + drawable_check_focus_highlight(node, tr_state, NULL); + } else { + list = ((GF_ParentNode *)node)->children; + count = gf_node_list_get_count(list); + for (i=0; ivrml_sensors); + tr_state->vrml_sensors = sensor_backup; + } +} + +/* + * 3D Grouping tools + */ + +#ifndef GPAC_DISABLE_3D + +void group_3d_setup(GroupingNode *group, GF_Node *node) +{ + memset(group, 0, sizeof(GroupingNode)); +} + +void group_3d_delete(GF_Node *node) +{ + GroupingNode *group = (GroupingNode *)gf_node_get_private(node); + + free(group); +} + +GroupingNode *group_3d_new(GF_Node *node) +{ + GroupingNode *st; + GF_SAFEALLOC(st, GroupingNode); + gf_node_set_private(node, st); + return st; +} + +/*returns 2 if local light, 1 if global light and 0 if not a light*/ +static u32 get_light_type(GF_Node *n) +{ + switch (gf_node_get_tag(n)) { + case TAG_MPEG4_DirectionalLight: + case TAG_X3D_DirectionalLight: + return 2; + case TAG_MPEG4_PointLight: + case TAG_MPEG4_SpotLight: + return 1; + default: + return 0; + } +} +/*This is the generic routine for child traversing*/ +void group_3d_traverse(GF_Node *node, GroupingNode *group, GF_TraverseState *tr_state) +{ + u32 mode_back; + Bool split_text_backup, do_lights; + DirectionalLightContext *dl; + GF_List *sensor_backup; + GF_SensorHandler *hsens; + GF_ChildNodeItem *l; + + if (gf_node_dirty_get(node) & GF_SG_CHILD_DIRTY) { + /*need to recompute bounds*/ + if (tr_state->traversing_mode!=TRAVERSE_GET_BOUNDS) { + /*traverse subtree to recompute bounds*/ + mode_back = tr_state->traversing_mode; + tr_state->traversing_mode=TRAVERSE_GET_BOUNDS; + group_3d_traverse(node, group, tr_state); + tr_state->traversing_mode = mode_back; + } + /*we're recomputing bounds*/ + else { + u32 ntag = gf_node_get_tag(node); + group->flags &= ~(GROUP_HAS_SENSORS | GROUP_HAS_LIGHTS); + + /*special case for anchor which is a parent node acting as a sensor*/ + if ((ntag==TAG_MPEG4_Anchor) || (ntag==TAG_X3D_Anchor)) group->flags |= GROUP_HAS_SENSORS; + + l = ((GF_ParentNode*)node)->children; + while (l) { + hsens = compositor_mpeg4_get_sensor_handler(l->node); + if (hsens) { + group->flags |= GROUP_HAS_SENSORS; + break; + } + else if (get_light_type(l->node)) { + group->flags |= GROUP_HAS_LIGHTS; + break; + } + l = l->next; + } + + /*now here is the trick: ExternProtos may not be loaded at this point, in which case we can't + perform proper culling. Unloaded ExternProto signal themselves by invalidating their parent + graph to get a new traversal. We must therefore reset the CHILD_DIRTY flag before computing + bounds otherwise we'll never re-invalidate the subgraph anymore*/ + gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY); + } + } + /*not parent (eg form, layout...) sub-tree not dirty and getting bounds, direct copy */ + else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + tr_state->bbox = group->bbox; + gf_node_dirty_clear(node, 0); + return; + } + + gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); + + mode_back=tr_state->cull_flag; + /*if culling not disabled*/ + if (!(group->flags & GROUP_SKIP_CULLING) + /*for geometry AND lights*/ + && (tr_state->traversing_mode==TRAVERSE_SORT) + /*do cull*/ + && !visual_3d_node_cull(tr_state, &group->bbox, 0)) { + + tr_state->cull_flag = mode_back; + return; + } + + + /*picking: collect sensors*/ + sensor_backup = NULL; + if ((tr_state->traversing_mode==TRAVERSE_PICK) && (group->flags & GROUP_HAS_SENSORS) ) { + /*reset sensor stack if any sensors at this level*/ + sensor_backup = tr_state->vrml_sensors; + tr_state->vrml_sensors = gf_list_new(); + + l = ((GF_ParentNode*)node)->children; + while (l) { + hsens = compositor_mpeg4_get_sensor_handler(l->node); + if (hsens && hsens->IsEnabled(l->node)) + gf_list_add(tr_state->vrml_sensors, hsens); + + l = l->next; + } + } + + /*turn on local lights and global ones*/ + do_lights = 0; + if (group->flags & GROUP_HAS_LIGHTS) { + /*turn on global lights*/ + if (tr_state->traversing_mode==TRAVERSE_LIGHTING) { + l = ((GF_ParentNode*)node)->children; + while (l) { + if (get_light_type(l->node)==2) gf_node_traverse(l->node, tr_state); + l = l->next; + } + } + /*turn on local lights*/ + else if (tr_state->traversing_mode==TRAVERSE_SORT) { + do_lights = 1; + tr_state->traversing_mode = TRAVERSE_DRAW_3D; + tr_state->local_light_on = 1; + + l = ((GF_ParentNode*)node)->children; + while (l) { + if (get_light_type(l->node)==1) { + /*store lights for alpha draw*/ + dl = (DirectionalLightContext*)malloc(sizeof(DirectionalLightContext)); + dl->dlight = l->node; + memcpy(&dl->light_matrix, &tr_state->model_matrix, sizeof(GF_Matrix)); + gf_list_add(tr_state->local_lights, dl); + /*and turn them on for non-alpha draw*/ + gf_node_traverse(dl->dlight, tr_state); + } + l = l->next; + } + tr_state->traversing_mode = TRAVERSE_SORT; + } + } + + + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + l = ((GF_ParentNode*)node)->children; + split_text_backup = tr_state->text_split_mode; + if (tr_state->text_split_mode && (gf_node_list_get_count(l)>1) ) tr_state->text_split_mode = 0; + group->bbox.is_set = tr_state->bbox.is_set = 0; + tr_state->bounds.width = 0; + group->flags &= ~GROUP_SKIP_CULLING; + + while (l) { + gf_node_traverse(l->node, tr_state); + if (tr_state->disable_cull) { + group->flags |= GROUP_SKIP_CULLING; + tr_state->disable_cull = 0; + } + /*handle 2D nodes in 3D groups*/ + if (tr_state->bounds.width) { + gf_bbox_from_rect(&tr_state->bbox, &tr_state->bounds); + tr_state->bounds.width = 0; + } + + if (tr_state->bbox.is_set) { + gf_bbox_union(&group->bbox, &tr_state->bbox); + } + tr_state->bbox.is_set = 0; + l = l->next; + } + tr_state->bbox = group->bbox; + if (group->flags & GROUP_SKIP_CULLING) + tr_state->disable_cull = 1; + tr_state->text_split_mode = split_text_backup; + } else { + l = ((GF_ParentNode*)node)->children; + while (l) { + gf_node_traverse(l->node, tr_state); + l = l->next; + } + + if (tr_state->traversing_mode==TRAVERSE_SORT) + drawable3d_check_focus_highlight(node, tr_state, NULL); + } + tr_state->cull_flag = mode_back; + + if (sensor_backup) { + /*destroy current traversing state sensors and restore previous*/ + gf_list_del(tr_state->vrml_sensors); + tr_state->vrml_sensors = sensor_backup; + } + + /*remove dlights*/ + if (do_lights) { + u32 lcount; + tr_state->traversing_mode = TRAVERSE_DRAW_3D; + tr_state->local_light_on = 0; + while ( (lcount = gf_list_count(tr_state->local_lights)) ) { + dl = (DirectionalLightContext*)gf_list_get(tr_state->local_lights, lcount-1); + gf_list_rem(tr_state->local_lights, lcount-1); + gf_node_traverse(dl->dlight, tr_state); + free(dl); + } + /*and back to sort mode*/ + tr_state->traversing_mode = TRAVERSE_SORT; + } +} + + +#endif + + +/* + * 2D ParentNode tools - used by all nodes performing children layout + */ + + +void parent_node_setup(ParentNode2D *group) +{ + group->groups = gf_list_new(); +} + +void parent_node_predestroy(ParentNode2D *group) +{ + /*just in case*/ + parent_node_reset(group); + gf_list_del(group->groups); +} + +void parent_node_reset(ParentNode2D *group) +{ + while (gf_list_count(group->groups)) { + ChildGroup *cg = (ChildGroup *)gf_list_get(group->groups, 0); + gf_list_rem(group->groups, 0); + free(cg); + } +} + +void parent_node_start_group(ParentNode2D *group, GF_Node *n, Bool discardable) +{ + ChildGroup *cg; + if (!n) { + cg = gf_list_last(group->groups); + if (!cg) return; + n = cg->child; + } + GF_SAFEALLOC(cg, ChildGroup); + cg->child = n; + cg->discardable = discardable; + gf_list_add(group->groups, cg); +} + +void parent_node_end_group(ParentNode2D *group, GF_Rect *bounds) +{ + ChildGroup *cg = (ChildGroup *)gf_list_last(group->groups); + if (!cg) return; + /*don't override splitted text info*/ + if (cg->ascent || cg->descent) return; + cg->original = *bounds; + cg->final = cg->original; +} + +void parent_node_end_text_group(ParentNode2D *group, GF_Rect *bounds, Fixed ascent, Fixed descent, u32 text_split_idx) +{ + ChildGroup *cg = (ChildGroup *)gf_list_last(group->groups); + if (!cg) return; + cg->text_split_idx = text_split_idx; + cg->ascent = ascent; + cg->descent = descent; + cg->final = cg->original = *bounds; +} + + + +void parent_node_traverse(GF_Node *node, ParentNode2D *group, GF_TraverseState *tr_state) +{ + Bool split_text_backup; + GF_List *sensor_backup; + GF_ChildNodeItem *l; + + if (gf_node_dirty_get(node) & GF_SG_CHILD_DIRTY) { + /*parent groups must recompute their bounds themselves since they modify children layout*/ + u32 ntag = gf_node_get_tag(node); + group->flags &= ~GROUP_HAS_SENSORS; + /*special case for anchor which is a parent node acting as a sensor*/ + if ((ntag==TAG_MPEG4_Anchor) || (ntag==TAG_X3D_Anchor)) { + group->flags |= GROUP_HAS_SENSORS | GROUP_IS_ANCHOR; + } else { + l = ((GF_ParentNode *)node)->children; + while (l) { + if (compositor_mpeg4_is_sensor_node(l->node)) { + group->flags |= GROUP_HAS_SENSORS; + break; + } + l = l->next; + } + } + + /*now here is the trick: ExternProtos may not be loaded at this point, in which case we can't + perform proper culling. Unloaded ExternProto signal themselves by invalidating their parent + graph to get a new traversal. We must therefore reset the CHILD_DIRTY flag before computing + bounds otherwise we'll never re-invalidate the subgraph anymore*/ + gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY); + } + gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); + + /*no culling in 2D*/ + + /*picking: collect sensors*/ + sensor_backup = NULL; + if ((tr_state->traversing_mode==TRAVERSE_PICK) && (group->flags & GROUP_HAS_SENSORS) ) { + GF_SensorHandler *hsens; + /*reset sensor stack if any sensors at this level*/ + sensor_backup = tr_state->vrml_sensors; + tr_state->vrml_sensors = gf_list_new(); + + + /*add sensor(s) to traversing state*/ + l = ((GF_ParentNode *)node)->children; + while (l) { + hsens = compositor_mpeg4_get_sensor_handler(l->node); + if (hsens) gf_list_add(tr_state->vrml_sensors, hsens); + l = l->next; + } + } + + + split_text_backup = tr_state->text_split_mode; + + group->flags &= ~GROUP_SKIP_CULLING; + tr_state->bounds.width = tr_state->bounds.height = 0; +#ifndef GPAC_DISABLE_3D + tr_state->bbox.is_set = 0; +#endif + + l = ((GF_ParentNode *)node)->children; + while (l) { + parent_node_start_group(group, l->node, 0); + + tr_state->bounds.width = tr_state->bounds.height = 0; + + gf_node_traverse(l->node, tr_state); + + /*handle 3D nodes in 2D groups*/ +#ifndef GPAC_DISABLE_3D + if (tr_state->bbox.is_set) { + gf_rect_from_bbox(&tr_state->bounds, &tr_state->bbox); + tr_state->bbox.is_set = 0; + } +#endif + parent_node_end_group(group, &tr_state->bounds); + l = l->next; + } + tr_state->text_split_mode = split_text_backup; + + if (sensor_backup) { + /*destroy current traversing state sensors and restore previous*/ + gf_list_del(tr_state->vrml_sensors); + tr_state->vrml_sensors = sensor_backup; + } +} + +/*final drawing of each group*/ +void parent_node_child_traverse(ChildGroup *cg, GF_TraverseState *tr_state) +{ + Fixed dx, dy; + + dx = cg->final.x - cg->original.x + cg->scroll_x; + dy = cg->final.y - cg->original.y + cg->scroll_y; + tr_state->text_split_idx = cg->text_split_idx; + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + GF_Matrix mx_bckup; + + gf_mx_copy(mx_bckup, tr_state->model_matrix); + gf_mx_add_translation(&tr_state->model_matrix, dx, dy, 0); + + if (tr_state->traversing_mode==TRAVERSE_SORT) { + GF_Matrix mx; + gf_mx_init(mx); + gf_mx_add_translation(&mx, dx, dy, 0); + visual_3d_matrix_push(tr_state->visual); + visual_3d_matrix_add(tr_state->visual, mx.m); + gf_node_traverse(cg->child, tr_state); + visual_3d_matrix_pop(tr_state->visual); + } else { + gf_node_traverse(cg->child, tr_state); + } + gf_mx_copy(tr_state->model_matrix, mx_bckup); + } else +#endif + { + GF_Matrix2D mx2d; + gf_mx2d_copy(mx2d, tr_state->transform); + gf_mx2d_init(tr_state->transform); + gf_mx2d_add_translation(&tr_state->transform, dx, dy); + gf_mx2d_add_matrix(&tr_state->transform, &mx2d); + gf_node_traverse(cg->child, tr_state); + gf_mx2d_copy(tr_state->transform, mx2d); + } + tr_state->text_split_idx = 0; +} + +void parent_node_child_traverse_matrix(ChildGroup *cg, GF_TraverseState *tr_state, GF_Matrix2D *mat2D) +{ + if (!mat2D) return; + + tr_state->text_split_idx = cg->text_split_idx; +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + GF_Matrix mx, mx_bckup; + gf_mx_from_mx2d(&mx, mat2D); + gf_mx_copy(mx_bckup, tr_state->model_matrix); + gf_mx_add_matrix(&tr_state->model_matrix, &mx); + if (tr_state->traversing_mode==TRAVERSE_SORT) { + visual_3d_matrix_push(tr_state->visual); + visual_3d_matrix_add(tr_state->visual, mx.m); + gf_node_traverse(cg->child, tr_state); + visual_3d_matrix_pop(tr_state->visual); + } else { + gf_node_traverse(cg->child, tr_state); + } + gf_mx_copy(tr_state->model_matrix, mx_bckup); + } else +#endif + { + GF_Matrix2D mx2d; + + gf_mx2d_copy(mx2d, tr_state->transform); + gf_mx2d_pre_multiply(&tr_state->transform, mat2D); + gf_node_traverse(cg->child, tr_state); + gf_mx2d_copy(tr_state->transform, mx2d); + } + tr_state->text_split_idx = 0; +} diff --git a/src/compositor/mpeg4_grouping.h b/src/compositor/mpeg4_grouping.h new file mode 100644 index 0000000..8f979b8 --- /dev/null +++ b/src/compositor/mpeg4_grouping.h @@ -0,0 +1,187 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef MPEG4_GROUPING_H +#define MPEG4_GROUPING_H + +#include + +enum +{ + GROUP_HAS_SENSORS = 1, + GROUP_SKIP_CULLING = 1<<1, + GROUP_HAS_LIGHTS = 1<<2, + GROUP_IS_ANCHOR = 1<<3, + /*set if group has been detected as a good candidate for caching*/ + GROUP_IS_CACHABLE = 1<<4, + /*set if group is cached*/ + GROUP_IS_CACHED = 1<<5, + GROUP_PERMANENT_CACHE = 1<<6, +}; + +/*this is only used for type-casting of the common 2D/3D stacks to get the flags*/ +typedef struct +{ + u32 flags; +} BaseGroupingStack; + + + +#ifdef GF_SR_USE_VIDEO_CACHE + + +#define GROUPING_NODE_STACK_2D \ + u32 flags; \ + GF_Rect bounds; \ + struct _group_cache *cache; \ + u16 traverse_time; \ + u8 changed; \ + u8 nb_stats_frame; \ + /*cache candidates are sorted by priority*/ \ + Fixed priority; \ + /*size of offscreen cache in kbytes*/ \ + u32 cached_size; \ + /*number of objects in cache - for debug purposes only*/ \ + u32 nb_objects; \ + + +#else + +#define GROUPING_NODE_STACK_2D \ + u32 flags; \ + GF_Rect bounds; \ + +#endif + +typedef struct _mpeg4_group2d +{ + GROUPING_NODE_STACK_2D +} GroupingNode2D; + +/*traverse all children of the node */ +void group_2d_traverse(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state); + +/*traverse all children of the node with the given traversing order*/ +void group_2d_traverse_with_order(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state, u32 *positions); + +#ifdef GF_SR_USE_VIDEO_CACHE +/*traverse the grouping node - returns 1 if the group is cached, in which case children nodes should not need +to be traversed in SORT mode - this function takes care of zoom changes & stats resetup if needed*/ +Bool group_2d_cache_traverse(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state); +/*record the traversal information and turn cache on if possible*/ +void group_2d_cache_evaluate(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state, struct _drawable_context *first_child, Bool skip_first_child, u32 last_cache_idx); +#endif + +void group_2d_destroy(GF_Node *node, GroupingNode2D *group); + +#ifndef GPAC_DISABLE_3D + +/* + @children: pointer to children field + @sensors: cached list of sensors at this level, NULL if none + @lights: cached list of lights (local and global) at this level, NULL if none + @bbox: bounding box of sub-tree + @dont_cull: if set, traversing always occur (needed for audio subgraph) +*/ + +#define GROUPING_NODE_STACK_3D \ + u32 flags; \ + GF_BBox bbox; \ + +typedef struct _parent_node_3d +{ + GROUPING_NODE_STACK_3D +} GroupingNode; + +GroupingNode *group_3d_new(GF_Node *node); +void group_3d_delete(GF_Node *node); + + +/*traverse all children of the node */ +void group_3d_traverse(GF_Node *n, GroupingNode *group, GF_TraverseState *tr_state); + +#endif + + + +/* + * ParentNode (Form, Layout, PathLayout, ...) tools +*/ + + +typedef struct +{ + /*the associated child (can be a group or a shape)*/ + GF_Node *child; + + /*child bounds before and after placement*/ + GF_Rect original, final; + + /*layout run-time scroll*/ + Fixed scroll_x, scroll_y; + + /*if text node, ascent and descent of the text is stored for baseline alignment*/ + Fixed ascent, descent; + u32 text_split_idx; + Bool discardable; +} ChildGroup; + + + +#define PARENT_NODE_STACK_2D \ + GROUPING_NODE_STACK_2D \ + /*list of ChildGroup drawn (can be fully transparents) - used for post placement*/ \ + GF_List *groups; + + +typedef struct _parent_node_2d +{ + PARENT_NODE_STACK_2D +} ParentNode2D; + +/*performs stack init (allocate all base stuff of stack)*/ +void parent_node_setup(ParentNode2D *ptr); +/*performs stack init (frees all base stuff of stack, but not the stack)*/ +void parent_node_predestroy(ParentNode2D *gr); +/*reset children list*/ +void parent_node_reset(ParentNode2D *gr); +/*creates a new group for the given node. If the node is NULL, the previous group's node is used*/ +void parent_node_start_group(ParentNode2D *group, GF_Node *n, Bool discardable); +/*sets the current group bounds*/ +void parent_node_end_group(ParentNode2D *group, GF_Rect *bounds); +/*sets the current group bounds + font properties*/ +void parent_node_end_text_group(ParentNode2D *group, GF_Rect *bounds, Fixed ascent, Fixed descent, u32 split_text_idx); + +/*traverse the parent group's children for bounds compute. Starts a new ChildGroup for each child*/ +void parent_node_traverse(GF_Node *node, ParentNode2D *group, GF_TraverseState *tr_state); + +/*updates the matrix (translations only, computed between final and original bounds) and traverse the given child*/ +void parent_node_child_traverse(ChildGroup *cg, GF_TraverseState *tr_state); + +/*updates the matrix (position assigned by caller) and traverse the given child. Do not traverse if the matrix is NULL*/ +void parent_node_child_traverse_matrix(ChildGroup *cg, GF_TraverseState *tr_state, GF_Matrix2D *mat2D); + + +#endif /*MPEG4_GROUPING_H*/ + diff --git a/src/compositor/mpeg4_grouping_2d.c b/src/compositor/mpeg4_grouping_2d.c new file mode 100644 index 0000000..2cc137a --- /dev/null +++ b/src/compositor/mpeg4_grouping_2d.c @@ -0,0 +1,401 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" +#include "mpeg4_grouping.h" +#include "visual_manager.h" + +typedef struct +{ + s32 last_switch; +} SwitchStack; + +static void TraverseSwitch(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_ChildNodeItem *l; + u32 i, count; + Bool prev_switch; + GF_ChildNodeItem *children; + s32 whichChoice; + GF_Node *child; + SwitchStack *st = (SwitchStack *)gf_node_get_private(node); + GF_TraverseState *tr_state; + tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + gf_sc_check_focus_upon_destroy(node); + free(st); + return; + } + + + if (gf_node_get_name(node)) { + node = node; + } + /*WARNING: X3D/MPEG4 NOT COMPATIBLE*/ + if (gf_node_get_tag(node)==TAG_MPEG4_Switch) { + children = ((M_Switch *)node)->choice; + whichChoice = ((M_Switch *)node)->whichChoice; + } else { + children = ((X_Switch *)node)->children; + whichChoice = ((X_Switch *)node)->whichChoice; + } + + if (tr_state->traversing_mode!=TRAVERSE_GET_BOUNDS) { + + count = gf_node_list_get_count(children); + + prev_switch = tr_state->switched_off; + /*check changes in choice field*/ + if ((gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) || (st->last_switch != whichChoice) ) { + tr_state->switched_off = 1; + i=0; + l = children; + while (l) { + // if ((s32) i!=whichChoice) gf_node_traverse(l->node, tr_state); + if ((s32) i == st->last_switch) gf_node_traverse(l->node, tr_state); + l = l->next; + i++; + } + tr_state->switched_off = 0; + st->last_switch = whichChoice; + } + + gf_node_dirty_clear(node, 0); + + /*no need to check for sensors since a sensor is active for the whole parent group, that is for switch itself + CSQ: switch cannot be used to switch sensors, too bad...*/ + tr_state->switched_off = prev_switch; + } + + if (whichChoice>=0) { + child = (GF_Node*)gf_node_list_get_child(children, whichChoice); + gf_node_traverse(child, tr_state); + } +} + +void compositor_init_switch(GF_Compositor *compositor, GF_Node *node) +{ + SwitchStack *st = (SwitchStack *)malloc(sizeof(SwitchStack)); + st->last_switch = -1; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, TraverseSwitch); +} + + +/*transform2D*/ +typedef struct +{ + GROUPING_NODE_STACK_2D + GF_Matrix2D mat; + Bool is_identity; +} Transform2DStack; + +static void traverse_transform(GF_Node *node, Transform2DStack *stack, GF_TraverseState *tr_state) +{ + /*note we don't clear dirty flag, this is done in traversing*/ + if (stack->is_identity) { + group_2d_traverse(node, (GroupingNode2D *)stack, tr_state); + } +#ifndef GPAC_DISABLE_3D + else if (tr_state->visual->type_3d) { + GF_Matrix mx_bckup; + gf_mx_copy(mx_bckup, tr_state->model_matrix); + + gf_mx_add_matrix_2d(&tr_state->model_matrix, &stack->mat); + + if (tr_state->traversing_mode == TRAVERSE_SORT) { + GF_Matrix tmp; + gf_mx_from_mx2d(&tmp, &stack->mat); + visual_3d_matrix_push(tr_state->visual); + visual_3d_matrix_add(tr_state->visual, tmp.m); + group_2d_traverse(node, (GroupingNode2D *)stack, tr_state); + visual_3d_matrix_pop(tr_state->visual); + } else { + group_2d_traverse(node, (GroupingNode2D *)stack, tr_state); + } + + gf_mx_copy(tr_state->model_matrix, mx_bckup); + } +#endif + else { + GF_Matrix2D bckup; + gf_mx2d_copy(bckup, tr_state->transform); + gf_mx2d_pre_multiply(&tr_state->transform, &stack->mat); + + group_2d_traverse(node, (GroupingNode2D *)stack, tr_state); + + gf_mx2d_copy(tr_state->transform, bckup); + } + + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + gf_mx2d_apply_rect(&stack->mat, &tr_state->bounds); + } +} + + +static void TraverseTransform2D(GF_Node *node, void *rs, Bool is_destroy) +{ + M_Transform2D *tr = (M_Transform2D *)node; + Transform2DStack *ptr = (Transform2DStack *)gf_node_get_private(node); + GF_TraverseState *tr_state; + + if (is_destroy) { + gf_sc_check_focus_upon_destroy(node); + group_2d_destroy(node, (GroupingNode2D*)ptr); + free(ptr); + return; + } + + tr_state = (GF_TraverseState *) rs; + + if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { + gf_mx2d_init(ptr->mat); + ptr->is_identity = 1; + if ((tr->scale.x != FIX_ONE) || (tr->scale.y != FIX_ONE)) { + gf_mx2d_add_scale_at(&ptr->mat, tr->scale.x, tr->scale.y, 0, 0, tr->scaleOrientation); + ptr->is_identity = 0; + } + if (tr->rotationAngle) { + gf_mx2d_add_rotation(&ptr->mat, tr->center.x, tr->center.y, tr->rotationAngle); + ptr->is_identity = 0; + } + if (tr->translation.x || tr->translation.y) { + ptr->is_identity = 0; + gf_mx2d_add_translation(&ptr->mat, tr->translation.x, tr->translation.y); + } + gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); + } + traverse_transform(node, ptr, tr_state); +} + +void compositor_init_transform2d(GF_Compositor *compositor, GF_Node *node) +{ + Transform2DStack *stack; + GF_SAFEALLOC(stack, Transform2DStack); + + gf_mx2d_init(stack->mat); + stack->is_identity = 1; + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseTransform2D); +} + +void tr_mx2d_get_matrix(GF_Node *n, GF_Matrix2D *mat) +{ + M_TransformMatrix2D *tr = (M_TransformMatrix2D*)n; + gf_mx2d_init(*mat); + mat->m[0] = tr->mxx; + mat->m[1] = tr->mxy; + mat->m[2] = tr->tx; + mat->m[3] = tr->myx; + mat->m[4] = tr->myy; + mat->m[5] = tr->ty; +} + + +/*TransformMatrix2D*/ +static void TraverseTransformMatrix2D(GF_Node *node, void *rs, Bool is_destroy) +{ + Transform2DStack *ptr = (Transform2DStack *) gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + gf_sc_check_focus_upon_destroy(node); + group_2d_destroy(node, (GroupingNode2D*)ptr); + free(ptr); + return; + } + + if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { + M_TransformMatrix2D *tr = (M_TransformMatrix2D*)node; + tr_mx2d_get_matrix(node, &ptr->mat); + if ((tr->mxx==FIX_ONE) && (tr->mxy==0) && (tr->tx==0) + && (tr->myx==0) && (tr->myy==FIX_ONE) && (tr->ty==0) ) + ptr->is_identity = 1; + else + ptr->is_identity = 0; + gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); + } + traverse_transform(node, ptr, tr_state); +} + + +void compositor_init_transformmatrix2d(GF_Compositor *compositor, GF_Node *node) +{ + Transform2DStack *stack; + GF_SAFEALLOC(stack, Transform2DStack); + + gf_mx2d_init(stack->mat); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseTransformMatrix2D); +} + + +typedef struct +{ + GROUPING_NODE_STACK_2D + GF_ColorMatrix cmat; +} ColorTransformStack; + +/*ColorTransform*/ +static void TraverseColorTransform(GF_Node *node, void *rs, Bool is_destroy) +{ + Bool c_changed; + M_ColorTransform *tr = (M_ColorTransform *)node; + ColorTransformStack *ptr = (ColorTransformStack *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + Bool prev_inv; + + if (is_destroy) { + gf_sc_check_focus_upon_destroy(node); + group_2d_destroy(node, (GroupingNode2D*)ptr); + free(ptr); + return; + } + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + group_2d_traverse(node, (GroupingNode2D *) ptr, tr_state); + return; + } + + prev_inv = tr_state->invalidate_all; + c_changed = 0; + if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { + gf_cmx_set(&ptr->cmat, + tr->mrr , tr->mrg, tr->mrb, tr->mra, tr->tr, + tr->mgr , tr->mgg, tr->mgb, tr->mga, tr->tg, + tr->mbr, tr->mbg, tr->mbb, tr->mba, tr->tb, + tr->mar, tr->mag, tr->mab, tr->maa, tr->ta); + c_changed = 1; + gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); + } + + if ((tr_state->traversing_mode==TRAVERSE_SORT) + && !tr->maa && !tr->mar && !tr->mag && !tr->mab && !tr->ta) + return; + + /*if modified redraw all nodes*/ + if (c_changed) tr_state->invalidate_all = 1; + + /*note we don't clear dirty flag, this is done in traversing*/ + if (ptr->cmat.identity) { + group_2d_traverse(node, (GroupingNode2D *) ptr, tr_state); + } else { + GF_ColorMatrix gf_cmx_bck; + gf_cmx_copy(&gf_cmx_bck, &tr_state->color_mat); + gf_cmx_multiply(&tr_state->color_mat, &ptr->cmat); + + + group_2d_traverse(node, (GroupingNode2D *) ptr, tr_state); + /*restore traversing state*/ + gf_cmx_copy(&tr_state->color_mat, &gf_cmx_bck); + } + tr_state->invalidate_all = prev_inv; +} + +void compositor_init_colortransform(GF_Compositor *compositor, GF_Node *node) +{ + ColorTransformStack *stack; + GF_SAFEALLOC(stack, ColorTransformStack); + + gf_cmx_init(&stack->cmat); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseColorTransform); +} + + +struct og_pos +{ + Fixed priority; + u32 position; +}; +typedef struct +{ + GROUPING_NODE_STACK_2D + u32 *positions; +} OrderedGroupStack; + + +static s32 compare_priority(const void* elem1, const void* elem2) +{ + struct og_pos *p1, *p2; + p1 = (struct og_pos *)elem1; + p2 = (struct og_pos *)elem2; + if (p1->priority < p2->priority) return -1; + if (p1->priority > p2->priority) return 1; + return 0; +} + + +static void TraverseOrderedGroup(GF_Node *node, void *rs, Bool is_destroy) +{ + u32 i, count; + struct og_pos *priorities; + Bool invalidate_backup; + OrderedGroupStack *stack = (OrderedGroupStack *) gf_node_get_private(node); + M_OrderedGroup *og = (M_OrderedGroup *) node; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + gf_sc_check_focus_upon_destroy(node); + group_2d_destroy(node, (GroupingNode2D*)stack); + if (stack->positions) free(stack->positions); + free(stack); + return; + } + + if (!og->order.count || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) { + group_2d_traverse(node, (GroupingNode2D*)stack, tr_state); + return; + } + + invalidate_backup = tr_state->invalidate_all; + /*check whether the OrderedGroup node has changed*/ + if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { + if (stack->positions) free(stack->positions); + count = gf_node_list_get_count(og->children); + priorities = (struct og_pos*)malloc(sizeof(struct og_pos)*count); + for (i=0; iorder.count) ? og->order.vals[i] : 0; + } + qsort(priorities, count, sizeof(struct og_pos), compare_priority); + + stack->positions = (u32*)malloc(sizeof(u32) * count); + for (i=0; ipositions[i] = priorities[i].position; + free(priorities); + + tr_state->invalidate_all = 1; + gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); + } + group_2d_traverse_with_order(node, (GroupingNode2D*)stack, tr_state, stack->positions); + tr_state->invalidate_all = invalidate_backup; +} + +void compositor_init_orderedgroup(GF_Compositor *compositor, GF_Node *node) +{ + OrderedGroupStack *ptr; + GF_SAFEALLOC(ptr, OrderedGroupStack); + gf_node_set_private(node, ptr); + gf_node_set_callback_function(node, TraverseOrderedGroup); +} diff --git a/src/compositor/mpeg4_grouping_3d.c b/src/compositor/mpeg4_grouping_3d.c new file mode 100644 index 0000000..a4d0497 --- /dev/null +++ b/src/compositor/mpeg4_grouping_3d.c @@ -0,0 +1,409 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" + +#include "mpeg4_grouping.h" +#include "visual_manager.h" + +#ifdef GPAC_DISABLE_3D + +static void TraverseGroup(GF_Node *node, void *rs, Bool is_destroy) +{ + GroupingNode2D *group = (GroupingNode2D *) gf_node_get_private(node); + if (is_destroy) { + gf_sc_check_focus_upon_destroy(node); + free(group); + } else { + group_2d_traverse(node, group, (GF_TraverseState*)rs); + } +} + +void compositor_init_group(GF_Compositor *compositor, GF_Node *node) +{ + GroupingNode2D *ptr; + GF_SAFEALLOC(ptr, GroupingNode2D); + gf_node_set_private(node, ptr); + gf_node_set_callback_function(node, TraverseGroup); +} + +void compositor_init_static_group(GF_Compositor *compositor, GF_Node *node) +{ + compositor_init_group(compositor, node); +} + +#else + +static void TraverseGroup(GF_Node *node, void *rs, Bool is_destroy) +{ + GroupingNode *group = (GroupingNode *) gf_node_get_private(node); + if (is_destroy) { + gf_sc_check_focus_upon_destroy(node); + group_3d_delete(node); + } else { + group_3d_traverse(node, group, (GF_TraverseState*)rs); + } +} + +void compositor_init_group(GF_Compositor *compositor, GF_Node *node) +{ + group_3d_new(node); + gf_node_set_callback_function(node, TraverseGroup); +} + + +/*currently static group use the same drawing as all grouping nodes, without any opts. +since we already do a lot of caching (lights, sensors) at the grouping node level, I'm not sure +we could get anything better trying to optimize the rest, since what is not cached in the base grouping +node always involves frustum culling, and caching that would require caching partial model matrix (from +the static group to each leaf)*/ +void compositor_init_static_group(GF_Compositor *compositor, GF_Node *node) +{ + group_3d_new(node); + gf_node_set_callback_function(node, TraverseGroup); +} + + +void TraverseCollision(GF_Node *node, void *rs, Bool is_destroy) +{ + u32 collide_flags; + SFVec3f last_point; + Fixed last_dist; + M_Collision *col = (M_Collision *)node; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + GroupingNode *group = (GroupingNode *) gf_node_get_private(node); + + if (is_destroy) { + gf_sc_check_focus_upon_destroy(node); + group_3d_delete(node); + return; + } + + if (tr_state->traversing_mode != TRAVERSE_COLLIDE) { + group_3d_traverse(node, group, tr_state); + } else if (col->collide) { + + collide_flags = tr_state->camera->collide_flags; + last_dist = tr_state->camera->collide_dist; + tr_state->camera->collide_flags &= 0; + tr_state->camera->collide_dist = FIX_MAX; + last_point = tr_state->camera->collide_point; + + if (col->proxy) { + /*always check bounds to update any dirty node*/ + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + gf_node_traverse(col->proxy, rs); + + tr_state->traversing_mode = TRAVERSE_COLLIDE; + gf_node_traverse(col->proxy, rs); + } else { + group_3d_traverse(node, group, (GF_TraverseState*)rs); + } + if (tr_state->camera->collide_flags & CF_COLLISION) { + col->collideTime = gf_node_get_scene_time(node); + gf_node_event_out_str(node, "collideTime"); + /*if not closer restore*/ + if (collide_flags && (last_distcamera->collide_dist)) { + tr_state->camera->collide_flags = collide_flags; + tr_state->camera->collide_dist = last_dist; + tr_state->camera->collide_point = last_point; + } + } else { + tr_state->camera->collide_flags = collide_flags; + tr_state->camera->collide_dist = last_dist; + } + } +} + +void compositor_init_collision(GF_Compositor *compositor, GF_Node *node) +{ + group_3d_new(node); + gf_node_set_callback_function(node, TraverseCollision); +} + +/*for transform, transform2D & transformMatrix2D*/ +typedef struct +{ + GROUPING_NODE_STACK_3D + GF_Matrix mx; + Bool has_scale; +} TransformStack; + +static void DestroyTransform(GF_Node *n) +{ + TransformStack *ptr = (TransformStack *)gf_node_get_private(n); + gf_sc_check_focus_upon_destroy(n); + free(ptr); +} + +static void NewTransformStack(GF_Compositor *compositor, GF_Node *node, GF_ChildNodeItem **children) +{ + TransformStack *st; + GF_SAFEALLOC(st, TransformStack); + + gf_mx_init(st->mx); + gf_node_set_private(node, st); +} + +#define TRANS_PUSH_MX \ + if (tr_state->traversing_mode == TRAVERSE_SORT) { \ + visual_3d_matrix_push(tr_state->visual); \ + visual_3d_matrix_add(tr_state->visual, st->mx.m); \ + } \ + +#define TRANS_POP_MX if (tr_state->traversing_mode==TRAVERSE_SORT) visual_3d_matrix_pop(tr_state->visual); + +static void TraverseTransform(GF_Node *n, void *rs, Bool is_destroy) +{ + GF_Matrix gf_mx_bckup; + TransformStack *st = (TransformStack *)gf_node_get_private(n); + M_Transform *tr = (M_Transform *)n; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + DestroyTransform(n); + return; + } + + /*note we don't clear dirty flag, this is done in traversing*/ + if (gf_node_dirty_get(n) & GF_SG_NODE_DIRTY) { + Bool scale_rot, recenter; + gf_mx_init(st->mx); + if (tr->translation.x || tr->translation.y || tr->translation.z) + gf_mx_add_translation(&st->mx, tr->translation.x, tr->translation.y, tr->translation.z); + recenter = (tr->center.x || tr->center.y || tr->center.z) ? 1 : 0; + if (recenter) + gf_mx_add_translation(&st->mx, tr->center.x, tr->center.y, tr->center.z); + + if (tr->rotation.q) gf_mx_add_rotation(&st->mx, tr->rotation.q, tr->rotation.x, tr->rotation.y, tr->rotation.z); + scale_rot = (tr->scaleOrientation.q) ? 1 : 0; + if (scale_rot) + gf_mx_add_rotation(&st->mx, tr->scaleOrientation.q, tr->scaleOrientation.x, tr->scaleOrientation.y, tr->scaleOrientation.z); + if ((tr->scale.x != FIX_ONE) || (tr->scale.y != FIX_ONE) || (tr->scale.z != FIX_ONE)) + gf_mx_add_scale(&st->mx, tr->scale.x, tr->scale.y, tr->scale.z); + if (scale_rot) + gf_mx_add_rotation(&st->mx, -tr->scaleOrientation.q, tr->scaleOrientation.x, tr->scaleOrientation.y, tr->scaleOrientation.z); + if (recenter) + gf_mx_add_translation(&st->mx, -tr->center.x, -tr->center.y, -tr->center.z); + + st->has_scale = ((tr->scale.x != FIX_ONE) || (tr->scale.y != FIX_ONE) || (tr->scale.z != FIX_ONE)) ? 1 : 0; + } + + gf_mx_copy(gf_mx_bckup, tr_state->model_matrix); + gf_mx_add_matrix(&tr_state->model_matrix, &st->mx); + + TRANS_PUSH_MX + + /*note we don't clear dirty flag, this is done in traversing*/ + group_3d_traverse(n, (GroupingNode *) st, tr_state); + + TRANS_POP_MX + + + gf_mx_copy(tr_state->model_matrix, gf_mx_bckup); + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) + gf_mx_apply_bbox(&st->mx, &tr_state->bbox); +} + +void compositor_init_transform(GF_Compositor *compositor, GF_Node *node) +{ + NewTransformStack(compositor, node, & ((M_Transform *)node)->children); + gf_node_set_callback_function(node, TraverseTransform); + +} + + +static void TraverseBillboard(GF_Node *n, void *rs, Bool is_destroy) +{ + GF_Matrix gf_mx_bckup; + TransformStack *st = (TransformStack *)gf_node_get_private(n); + M_Billboard *bb = (M_Billboard *)n; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + DestroyTransform(n); + return; + } + + /*can't cache the matrix here*/ + gf_mx_init(st->mx); + if (tr_state->camera->is_3D) { + SFVec3f z, axis; + Fixed axis_len; + SFVec3f user_pos = tr_state->camera->position; + + gf_mx_apply_vec(&tr_state->model_matrix, &user_pos); + gf_vec_norm(&user_pos); + axis = bb->axisOfRotation; + axis_len = gf_vec_len(axis); + if (axis_lencamera->position, tr_state->camera->target); + gf_vec_norm(&t); + x = gf_vec_cross(tr_state->camera->up, t); + gf_vec_norm(&x); + gf_mx_rotate_vector(&tr_state->model_matrix, &x); + gf_vec_norm(&x); + /*get user's up in local coord*/ + y = tr_state->camera->up; + gf_mx_rotate_vector(&tr_state->model_matrix, &y); + gf_vec_norm(&y); + z = gf_vec_cross(x, y); + gf_vec_norm(&z); + + gf_mx_rotation_matrix_from_vectors(&st->mx, x, y, z); + gf_mx_inverse(&st->mx); + } else { + SFVec3f tmp; + Fixed d, cosw, sinw, angle; + gf_vec_norm(&axis); + /*map eye & z into plane with normal axis through 0.0*/ + d = -gf_vec_dot(axis, user_pos); + tmp = gf_vec_scale(axis, d); + gf_vec_add(user_pos, user_pos, tmp); + gf_vec_norm(&user_pos); + + z.x = z.y = 0; z.z = FIX_ONE; + d = -gf_vec_dot(axis, z); + tmp = gf_vec_scale(axis, d); + gf_vec_add(z, z, tmp); + gf_vec_norm(&z); + + cosw = gf_vec_dot(user_pos, z); + tmp = gf_vec_cross(user_pos, z); + sinw = gf_vec_len(tmp); + angle = gf_acos(cosw); + gf_vec_norm(&tmp); + if ((sinw>0) && (gf_vec_dot(axis, tmp) > 0)) gf_vec_rev(axis); + gf_mx_add_rotation(&st->mx, angle, axis.x, axis.y, axis.z); + } + } + + gf_mx_copy(gf_mx_bckup, tr_state->model_matrix); + gf_mx_add_matrix(&tr_state->model_matrix, &st->mx); + + TRANS_PUSH_MX + + /*note we don't clear dirty flag, this is done in traversing*/ + group_3d_traverse(n, (GroupingNode *) st, tr_state); + + TRANS_POP_MX + + gf_mx_copy(tr_state->model_matrix, gf_mx_bckup); + + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) gf_mx_apply_bbox(&st->mx, &tr_state->bbox); +} + +void compositor_init_billboard(GF_Compositor *compositor, GF_Node *node) +{ + NewTransformStack(compositor, node, & ((M_Billboard *)node)->children); + gf_node_set_callback_function(node, TraverseBillboard); + +} + +static void TraverseLOD(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_ChildNodeItem *children; + MFFloat *ranges; + SFVec3f pos, usr; + u32 which_child, nb_children; + Fixed dist; + Bool do_all; + GF_Matrix mx; + SFVec3f center; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + s32 *prev_child = (s32 *)gf_node_get_private(node); + + if (is_destroy) { + free(prev_child); + gf_sc_check_focus_upon_destroy(node); + return; + } + + + + /*WARNING: X3D/MPEG4 NOT COMPATIBLE*/ + if (gf_node_get_tag(node) == TAG_MPEG4_LOD) { + children = ((M_LOD *) node)->level; + ranges = &((M_LOD *) node)->range; + center = ((M_LOD *) node)->center; + } else { + children = ((X_LOD *) node)->children; + ranges = &((X_LOD *) node)->range; + center = ((X_LOD *) node)->center; + } + + if (!children) return; + nb_children = gf_node_list_get_count(children); + + /*can't cache the matric here*/ + usr = tr_state->camera->position; + pos = center; + gf_mx_copy(mx, tr_state->model_matrix); + gf_mx_inverse(&mx); + gf_mx_apply_vec(&mx, &usr); + gf_vec_diff(pos, pos, usr); + dist = gf_vec_len(pos); + for (which_child=0; which_childcount; which_child++) { + if (distvals[which_child]) break; + } + if (which_child>=nb_children) which_child = nb_children-1; + + /*check if we're traversing the same child or not for audio rendering*/ + do_all = 0; + if (gf_node_dirty_get(node)) { + gf_node_dirty_clear(node, 0); + do_all = 1; + } else if ((s32) which_child != *prev_child) { + *prev_child = which_child; + do_all = 1; + } + + if (do_all) { + u32 i; + Bool prev_switch = tr_state->switched_off; + GF_ChildNodeItem *l = children; + tr_state->switched_off = 1; + i=0; + while (l) { + if (i!=which_child) gf_node_traverse(l->node, rs); + l = l->next; + } + tr_state->switched_off = prev_switch; + } + gf_node_traverse(gf_node_list_get_child(children, which_child), rs); +} + +void compositor_init_lod(GF_Compositor *compositor, GF_Node *node) +{ + s32 *stack = (s32*)malloc(sizeof(s32)); + *stack = -1; + gf_node_set_callback_function(node, TraverseLOD); + gf_node_set_private(node, stack); +} + +#endif /*GPAC_DISABLE_3D*/ diff --git a/src/compositor/mpeg4_layer_2d.c b/src/compositor/mpeg4_layer_2d.c new file mode 100644 index 0000000..a885339 --- /dev/null +++ b/src/compositor/mpeg4_layer_2d.c @@ -0,0 +1,379 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "mpeg4_grouping.h" +#include "visual_manager.h" + + +typedef struct +{ + GROUPING_NODE_STACK_2D + GF_List *backs; + GF_List *views; + Bool first; + GF_Rect clip; +} Layer2DStack; + + + +static void l2d_CheckBindables(GF_Node *n, GF_TraverseState *tr_state, Bool force_traverse) +{ + GF_Node *btop; + M_Layer2D *l2d; + l2d = (M_Layer2D *)n; + if (force_traverse) gf_node_traverse(l2d->background, tr_state); + btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0); + if (btop != l2d->background) { + gf_node_unregister(l2d->background, n); + gf_node_register(btop, n); + l2d->background = btop; + gf_node_event_out_str(n, "background"); + } + if (force_traverse) gf_node_traverse(l2d->viewport, tr_state); + btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); + if (btop != l2d->viewport) { + gf_node_unregister(l2d->viewport, n); + gf_node_register(btop, n); + l2d->viewport = btop; + gf_node_event_out_str(n, "viewport"); + } +} + + +#if VIEWPORT_CLIPS +static void rect_intersect(GF_Rect *rc1, GF_Rect *rc2) +{ + if (! gf_rect_overlaps(*rc1, *rc2)) { + rc1->width = rc1->height = 0; + return; + } + if (rc2->x > rc1->x) { + rc1->width -= rc2->x - rc1->x; + rc1->x = rc2->x; + } + if (rc2->x + rc2->width < rc1->x + rc1->width) { + rc1->width = rc2->width + rc2->x - rc1->x; + } + if (rc2->y < rc1->y) { + rc1->height -= rc1->y - rc2->y; + rc1->y = rc2->y; + } + if (rc2->y - rc2->height > rc1->y - rc1->height) { + rc1->height = rc1->y - rc2->y + rc2->height; + } +} +#endif + +static void TraverseLayer2D(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_List *oldb, *oldv; + GF_Node *viewport; + GF_Node *back; + Bool prev_layer; + GF_Matrix2D backup; + SFVec2f prev_vp; + +#ifndef GPAC_DISABLE_3D + GF_Matrix mx3d; + GF_List *oldf, *oldn; + GF_List *node_list_backup; + GF_Rect prev_clipper; + Bool had_clip; +#endif + + M_Layer2D *l = (M_Layer2D *)node; + Layer2DStack *st = (Layer2DStack *) gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + gf_list_del(st->backs); + gf_list_del(st->views); + group_2d_destroy(node, (GroupingNode2D*)st); + free(st); + return; + } + + /*layers can only be used in a 2D context*/ +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d && tr_state->camera->is_3D) return; +#endif + + /*layer2D maintains its own stacks*/ + oldb = tr_state->backgrounds; + oldv = tr_state->viewpoints; + tr_state->backgrounds = st->backs; + tr_state->viewpoints = st->views; + prev_layer = tr_state->is_layer; + tr_state->is_layer = 1; +#ifndef GPAC_DISABLE_3D + oldf = tr_state->fogs; + oldn = tr_state->navigations; + tr_state->fogs = tr_state->navigations = NULL; +#endif + + l2d_CheckBindables(node, tr_state, st->first); + + back = (GF_Node*)gf_list_get(st->backs, 0); + + viewport = (GF_Node*)gf_list_get(st->views, 0); + + if (tr_state->traversing_mode == TRAVERSE_SORT) { + /*override group bounds*/ + visual_get_size_info(tr_state, &st->clip.width, &st->clip.height); + /*setup bounds in local coord system*/ + if (l->size.x>=0) st->clip.width = l->size.x; + if (l->size.y>=0) st->clip.height = l->size.y; + st->clip = gf_rect_center(st->clip.width, st->clip.height); + st->bounds = st->clip; + //gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); + } + + prev_vp = tr_state->vp_size; + tr_state->vp_size.x = st->clip.width; + tr_state->vp_size.y = st->clip.height; + + switch (tr_state->traversing_mode) { + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + tr_state->layer_clipper = compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 1); + + visual_3d_matrix_push(tr_state->visual); + gf_mx_copy(mx3d, tr_state->model_matrix); + + /*setup clipping*/ + visual_3d_set_clipper_2d(tr_state->visual, tr_state->layer_clipper); + + /*apply background BEFORE viewport*/ + if (back) { + tr_state->traversing_mode = TRAVERSE_BINDABLE; + gf_bbox_from_rect(&tr_state->bbox, &st->clip); + gf_node_traverse(back, tr_state); + } + + /*sort all children without transform, and use current transform when flushing contexts*/ + gf_mx_init(tr_state->model_matrix); + + /*apply viewport*/ + if (viewport) { + tr_state->traversing_mode = TRAVERSE_BINDABLE; + tr_state->bounds = st->clip; + gf_node_traverse(viewport, tr_state); + visual_3d_matrix_add(tr_state->visual, tr_state->model_matrix.m); + } + + + node_list_backup = tr_state->visual->alpha_nodes_to_draw; + tr_state->visual->alpha_nodes_to_draw = gf_list_new(); + tr_state->traversing_mode = TRAVERSE_SORT; + /*reset cull flag*/ + tr_state->cull_flag = 0; + group_2d_traverse(node, (GroupingNode2D *)st, tr_state); + + visual_3d_flush_contexts(tr_state->visual, tr_state); + tr_state->traversing_mode = TRAVERSE_SORT; + + assert(!gf_list_count(tr_state->visual->alpha_nodes_to_draw)); + gf_list_del(tr_state->visual->alpha_nodes_to_draw); + tr_state->visual->alpha_nodes_to_draw = node_list_backup; + + + visual_3d_matrix_pop(tr_state->visual); + gf_mx_copy(tr_state->model_matrix, mx3d); + + visual_3d_reset_clipper_2d(tr_state->visual); + + tr_state->has_layer_clip = had_clip; + if (had_clip) { + tr_state->layer_clipper = prev_clipper; + visual_3d_set_clipper_2d(tr_state->visual, tr_state->layer_clipper); + } + } else +#endif + { + GF_IRect prev_clip; + GF_Rect rc; + + gf_mx2d_copy(backup, tr_state->transform); + + prev_clip = tr_state->visual->top_clipper; + rc = st->clip; + + /*get clipper in world coordinate*/ + gf_mx2d_apply_rect(&tr_state->transform, &rc); + + if (viewport) { + tr_state->traversing_mode = TRAVERSE_BINDABLE; + tr_state->bounds = st->clip; + gf_node_traverse(viewport, tr_state); +#if VIEWPORT_CLIPS + /*move viewport box in world coordinate*/ + gf_mx2d_apply_rect(&backup, &tr_state->bounds); + /*and intersect with layer clipper*/ + rect_intersect(&rc, &tr_state->bounds); +#endif + } + + tr_state->visual->top_clipper = gf_rect_pixelize(&rc); + gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip); + + tr_state->traversing_mode = TRAVERSE_SORT; + if (back && Bindable_GetIsBound(back) ) { + DrawableContext *ctx; + + ctx = b2d_get_context((M_Background2D*) back, st->backs); + gf_mx2d_init(ctx->transform); + ctx->bi->clip = tr_state->visual->top_clipper; + ctx->bi->unclip = rc; + + if (tr_state->direct_draw) { + tr_state->ctx = ctx; + tr_state->traversing_mode = TRAVERSE_DRAW_2D; + gf_node_traverse(back, tr_state); + tr_state->traversing_mode = TRAVERSE_SORT; + tr_state->ctx = NULL; + } else { + DrawableContext *back_ctx = visual_2d_get_drawable_context(tr_state->visual); + + gf_node_traverse(back, tr_state); + + back_ctx->flags = ctx->flags; + back_ctx->flags &= ~CTX_IS_TRANSPARENT; + back_ctx->flags |= CTX_IS_BACKGROUND; + back_ctx->aspect = ctx->aspect; + back_ctx->drawable = ctx->drawable; + drawable_check_bounds(back_ctx, tr_state->visual); + back_ctx->bi->clip = ctx->bi->clip; + back_ctx->bi->unclip = ctx->bi->unclip; + } + /*keep track of node drawn*/ + if (!(ctx->drawable->flags & DRAWABLE_REGISTERED_WITH_VISUAL) ) { + struct _drawable_store *it; + GF_SAFEALLOC(it, struct _drawable_store); + it->drawable = ctx->drawable; + if (tr_state->visual->last_prev_entry) { + tr_state->visual->last_prev_entry->next = it; + tr_state->visual->last_prev_entry = it; + } else { + tr_state->visual->prev_nodes = tr_state->visual->last_prev_entry = it; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Layer2D] Registering new drawn node %s on visual\n", gf_node_get_class_name(it->drawable->node))); + ctx->drawable->flags |= DRAWABLE_REGISTERED_WITH_VISUAL; + } + } + + group_2d_traverse(node, (GroupingNode2D *)st, tr_state); + tr_state->visual->top_clipper = prev_clip; + gf_mx2d_copy(tr_state->transform, backup); + } + break; + + /*check picking - we must fall in our 2D clipper*/ + case TRAVERSE_PICK: + if (gf_sc_pick_in_clipper(tr_state, &st->clip)) { + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + /*apply viewport*/ + if (viewport) { + gf_mx_copy(mx3d, tr_state->model_matrix); + tr_state->traversing_mode = TRAVERSE_BINDABLE; + tr_state->bounds = st->clip; + gf_node_traverse(viewport, tr_state); + tr_state->traversing_mode = TRAVERSE_PICK; + group_2d_traverse(node, (GroupingNode2D *)st, tr_state); + gf_mx_copy(tr_state->model_matrix, mx3d); + } else { + group_2d_traverse(node, (GroupingNode2D *)st, tr_state); + } + } else +#endif + { + if (viewport) { + gf_mx2d_copy(backup, tr_state->transform); + tr_state->traversing_mode = TRAVERSE_BINDABLE; + tr_state->bounds = st->clip; + gf_node_traverse(viewport, tr_state); + tr_state->traversing_mode = TRAVERSE_PICK; + group_2d_traverse(node, (GroupingNode2D *)st, tr_state); + gf_mx2d_copy(tr_state->transform, backup); + } else { + group_2d_traverse(node, (GroupingNode2D *)st, tr_state); + } + } + } + break; + case TRAVERSE_GET_BOUNDS: + tr_state->bounds = st->clip; +#ifndef GPAC_DISABLE_3D + gf_bbox_from_rect(&tr_state->bbox, &st->clip); +#endif + break; + + case TRAVERSE_DRAW_2D: + group_2d_traverse(node, (GroupingNode2D *)st, tr_state); + break; + +#ifndef GPAC_DISABLE_3D + /*drawing a layer means drawing all sub-elements as a whole (no depth sorting with parents)*/ + case TRAVERSE_DRAW_3D: + assert(0); + break; +#endif + } + + /*restore traversing state*/ + tr_state->vp_size = prev_vp; + tr_state->backgrounds = oldb; + tr_state->viewpoints = oldv; + tr_state->is_layer = prev_layer; +#ifndef GPAC_DISABLE_3D + tr_state->fogs = oldf; + tr_state->navigations = oldn; +#endif + + /*in case we missed bindables*/ + if (st->first) { + st->first = 0; + gf_sc_invalidate(tr_state->visual->compositor, NULL); + } +} + +void compositor_init_layer2d(GF_Compositor *compositor, GF_Node *node) +{ + Layer2DStack *stack; + GF_SAFEALLOC(stack, Layer2DStack); + + stack->backs = gf_list_new(); + stack->views = gf_list_new(); + stack->first = 1; + + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseLayer2D); +} + + diff --git a/src/compositor/mpeg4_layer_3d.c b/src/compositor/mpeg4_layer_3d.c new file mode 100644 index 0000000..bb62559 --- /dev/null +++ b/src/compositor/mpeg4_layer_3d.c @@ -0,0 +1,710 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "mpeg4_grouping.h" +#include "texturing.h" +#include "visual_manager.h" + +#ifndef GPAC_DISABLE_3D + +#ifdef GPAC_USE_TINYGL +//#include +#include "../../TinyGL/include/GL/oscontext.h" + +#endif + +typedef struct +{ + GROUPING_NODE_STACK_3D + Bool first; + GF_Rect clip, vp; + GF_VisualManager *visual; + + Bool unsupported; + GF_TextureHandler txh; + Drawable *drawable; +#ifdef GPAC_USE_TINYGL + ostgl_context *tgl_ctx; +#endif + Fixed scale_x, scale_y; +} Layer3DStack; + + +static void DestroyLayer3D(GF_Node *node) +{ + Layer3DStack *st = (Layer3DStack *) gf_node_get_private(node); + GF_Compositor *compositor = st->visual->compositor; + + drawable_del(st->drawable); + +#ifdef GPAC_USE_TINYGL + if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx); +#endif + + if (st->txh.data) free(st->txh.data); + /*destroy texture*/ + gf_sc_texture_destroy(&st->txh); + + BindableStackDelete(st->visual->back_stack); + st->visual->back_stack = NULL; + BindableStackDelete(st->visual->view_stack); + st->visual->view_stack = NULL; + BindableStackDelete(st->visual->navigation_stack); + st->visual->navigation_stack = NULL; + BindableStackDelete(st->visual->fog_stack); + st->visual->fog_stack = NULL; + visual_del(st->visual); + + + if (compositor && compositor->active_layer == node) compositor->active_layer = NULL; + free(st); +} + +static void l3d_CheckBindables(GF_Node *n, GF_TraverseState *tr_state, Bool force_traverse) +{ + GF_Node *btop; + u32 mode; + M_Layer3D *l3d; + l3d = (M_Layer3D *)n; + + mode = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + + if (force_traverse) gf_node_traverse(l3d->background, tr_state); + btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0); + if (btop != l3d->background) { + gf_node_unregister(l3d->background, n); + gf_node_register(btop, n); + l3d->background = btop; + gf_node_event_out_str(n, "background"); + } + if (force_traverse) gf_node_traverse(l3d->viewpoint, tr_state); + btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); + if (btop != l3d->viewpoint) { + gf_node_unregister(l3d->viewpoint, n); + gf_node_register(btop, n); + l3d->viewpoint = btop; + gf_node_event_out_str(n, "viewpoint"); + } + if (force_traverse) gf_node_traverse(l3d->navigationInfo, tr_state); + btop = (GF_Node*)gf_list_get(tr_state->navigations, 0); + if (btop != l3d->navigationInfo) { + gf_node_unregister(l3d->navigationInfo, n); + gf_node_register(btop, n); + l3d->navigationInfo = btop; + gf_node_event_out_str(n, "navigationInfo"); + } + if (force_traverse) gf_node_traverse(l3d->fog, tr_state); + btop = (GF_Node*)gf_list_get(tr_state->fogs, 0); + if (btop != l3d->fog) { + gf_node_unregister(l3d->fog, n); + gf_node_register(btop, n); + l3d->fog = btop; + gf_node_event_out_str(n, "fog"); + } + tr_state->traversing_mode = mode; +} + + +u32 layer3d_setup_offscreen(GF_Node *node, Layer3DStack *st, GF_TraverseState *tr_state, Fixed width, Fixed height) +{ + GF_Err e; + GF_STENCIL stencil; + u32 new_pixel_format, w, h; + GF_Compositor *compositor = (GF_Compositor *)st->visual->compositor; + + if (st->unsupported) return 0; + +#ifndef GPAC_USE_TINYGL + /*no support for offscreen rendering*/ + if (!(compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN)) { + st->unsupported = 1; + return 0; + } +#endif + +/* + if (tr_state->visual->compositor->recompute_ar) { + gf_node_dirty_set(node, 0, 0); + return 0; + } +*/ + + new_pixel_format = GF_PIXEL_RGBA; +#ifndef GPAC_USE_TINYGL +// if (!compositor_background_transparent(gf_list_get(tr_state->backgrounds, 0)) ) +// new_pixel_format = GF_PIXEL_RGB_24; + + /*in OpenGL_ES, only RGBA can be safelly used with glReadPixels*/ +#ifdef GPAC_USE_OGL_ES + new_pixel_format = GF_PIXEL_RGBA; +#else + /*no support for alpha in offscreen rendering*/ + if (!(compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA)) + new_pixel_format = GF_PIXEL_RGB_24; +#endif + +#endif + +#ifdef GPAC_TRISCOPE_MODE + new_pixel_format = GF_PIXEL_RGBDS; +#endif + + w = (u32) FIX2INT(gf_ceil(width)); + h = (u32) FIX2INT(gf_ceil(height)); + + /*1- some implementation of glReadPixel crash if w||h are not multiple of 4*/ + /*2- some implementation of glReadPixel don't behave properly here when texture is not a power of 2*/ + w = gf_get_next_pow2(w); + if (w>1024) w = 1024; + h = gf_get_next_pow2(h); + if (h>1024) h = 1024; + + + if (!w || !h) return 0; + + if (st->txh.tx_io + && (new_pixel_format == st->txh.pixelformat) + && (w == st->txh.width) + && (h == st->txh.height) + && (compositor->offscreen_width >= w) + && (compositor->offscreen_height >= h) + ) + return 2; + + if (st->txh.tx_io) { +#ifdef GPAC_USE_TINYGL + if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx); +#endif + gf_sc_texture_release(&st->txh); + if (st->txh.data) free(st->txh.data); + st->txh.data = NULL; + } + + st->vp = gf_rect_center(INT2FIX(w), INT2FIX(h) ); + + st->txh.width = w; + st->txh.height = h; + + gf_sc_texture_allocate(&st->txh); + st->txh.pixelformat = new_pixel_format; + + if (new_pixel_format==GF_PIXEL_RGBA) { + st->txh.stride = w * 4; + st->txh.transparent = 1; + } else { + st->txh.stride = w * 3; + st->txh.transparent = 0; + } +#ifdef GPAC_TRISCOPE_MODE + if (new_pixel_format==GF_PIXEL_RGBDS) { + st->txh.stride = w * 4; + st->txh.transparent = 0; + } +#endif + + st->visual->width = w; + st->visual->height = h; + + + stencil = compositor->rasterizer->stencil_new(compositor->rasterizer, GF_STENCIL_TEXTURE); + +#ifndef GPAC_USE_TINYGL + /*create an offscreen window for OpenGL rendering*/ + if ((compositor->offscreen_width < w) || (compositor->offscreen_height < h)) { + GF_Err e; + GF_Event evt; + compositor->offscreen_width = MAX(compositor->offscreen_width, w); + compositor->offscreen_height = MAX(compositor->offscreen_height, h); + + evt.type = GF_EVENT_VIDEO_SETUP; + evt.setup.width = tr_state->visual->compositor->offscreen_width; + evt.setup.height = tr_state->visual->compositor->offscreen_height; + evt.setup.back_buffer = 0; + evt.setup.opengl_mode = 2; + e = compositor->video_out->ProcessEvent(compositor->video_out, &evt); + if (e) { + gf_sc_texture_release(&st->txh); + st->unsupported = 1; + return 0; + } + } +#endif + st->txh.data = (char*)malloc(sizeof(unsigned char) * st->txh.stride * st->txh.height); + memset(st->txh.data, 0, sizeof(unsigned char) * st->txh.stride * st->txh.height); + e = compositor->rasterizer->stencil_set_texture(stencil, st->txh.data, st->txh.width, st->txh.height, st->txh.stride, st->txh.pixelformat, st->txh.pixelformat, 0); +#ifdef GPAC_TRISCOPE_MODE + e = GF_OK; +#endif + + if (e) { + compositor->rasterizer->stencil_delete(stencil); + gf_sc_texture_release(&st->txh); + free(st->txh.data); + st->txh.data = NULL; + } +#ifdef GPAC_USE_TINYGL + /*create TinyGL offscreen context*/ + st->tgl_ctx = ostgl_create_context(st->txh.width, st->txh.height, st->txh.transparent ? 32 : 24, &st->txh.data, 1); + + st->scale_x = st->scale_y = FIX_ONE; +#else + st->scale_x = INT2FIX(w) / tr_state->visual->compositor->offscreen_width; + st->scale_y = INT2FIX(h) / tr_state->visual->compositor->offscreen_height; +#endif + + gf_sc_texture_set_stencil(&st->txh, stencil); + drawable_reset_path(st->drawable); + gf_path_add_rect_center(st->drawable->path, 0, 0, st->clip.width, st->clip.height); + return 1; +} + +static void layer3d_draw_2d(GF_Node *node, GF_TraverseState *tr_state) +{ + DrawableContext *ctx = tr_state->ctx; + if (tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx, NULL)) + return; + + visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state); +} + +static void layer3d_setup_clip(Layer3DStack *st, GF_TraverseState *tr_state, Bool prev_cam, GF_Rect rc) +{ + Fixed sw, sh; + + if (tr_state->visual->compositor->visual==tr_state->visual) { + sw = INT2FIX(tr_state->visual->compositor->display_width); + sh = INT2FIX(tr_state->visual->compositor->display_height); + } else { + sw = INT2FIX(tr_state->visual->width); + sh = INT2FIX(tr_state->visual->height); + } + + if ((tr_state->traversing_mode==TRAVERSE_SORT) && !prev_cam) { + rc.x = rc.y = 0; + st->visual->camera.vp = rc; + } else { + st->visual->camera.vp = rc; + st->visual->camera.vp.x += sw/2; + st->visual->camera.vp.y -= st->visual->camera.vp.height; + st->visual->camera.vp.y += sh/2; + } + + tr_state->camera->width = tr_state->camera->vp.width; + tr_state->camera->height = tr_state->camera->vp.height; + + if (!tr_state->pixel_metrics) { + if (tr_state->camera->height > tr_state->camera->width) { + tr_state->camera->height = 2 * gf_divfix(tr_state->camera->height, tr_state->camera->width); + tr_state->camera->width = 2*FIX_ONE; + } else { + tr_state->camera->width = 2 * gf_divfix(tr_state->camera->width, tr_state->camera->height); + tr_state->camera->height = 2*FIX_ONE; + } + } + + /*setup bounds*/ + tr_state->bbox.max_edge.x = tr_state->camera->width/2; + tr_state->bbox.min_edge.x = -tr_state->bbox.max_edge.x; + tr_state->bbox.max_edge.y = tr_state->camera->height/2; + tr_state->bbox.min_edge.y = -tr_state->bbox.max_edge.y; + tr_state->bbox.max_edge.z = tr_state->bbox.min_edge.z = 0; + tr_state->bbox.is_set = 1; + +} + +static void TraverseLayer3D(GF_Node *node, void *rs, Bool is_destroy) +{ + Bool prev_layer, changed = 0; + GF_List *oldb, *oldv, *oldf, *oldn; + GF_Rect rc; + u32 cur_lights; + GF_List *node_list_backup; + GF_BBox bbox_backup; + GF_Matrix model_backup; + GF_Matrix2D mx2d_backup; + GF_Camera *prev_cam; + GF_VisualManager *old_visual; + M_Layer3D *l = (M_Layer3D *)node; + Layer3DStack *st = (Layer3DStack *) gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + DestroyLayer3D(node); + return; + } + if (st->unsupported) return; + + if (gf_node_dirty_get(node)) { + + /*main visual in pixel metrics, use output width*/ + if (tr_state->pixel_metrics && (tr_state->visual->compositor->visual==tr_state->visual)) { + st->clip.width = INT2FIX(tr_state->visual->compositor->vp_width); + st->clip.height = INT2FIX(tr_state->visual->compositor->vp_height); + } else { + visual_get_size_info(tr_state, &st->clip.width, &st->clip.height); + } + /*setup bounds in local coord system*/ + if (l->size.x>=0) st->clip.width = l->size.x; + if (l->size.y>=0) st->clip.height = l->size.y; + st->clip = gf_rect_center(st->clip.width, st->clip.height); + + changed = 1; + } + + switch (tr_state->traversing_mode) { + case TRAVERSE_GET_BOUNDS: + tr_state->bounds = st->clip; + gf_bbox_from_rect(&tr_state->bbox, &st->clip); + return; + case TRAVERSE_PICK: + case TRAVERSE_SORT: + /*layers can only be used in a 2D context*/ + if (tr_state->camera && tr_state->camera->is_3D) return; + break; + case TRAVERSE_DRAW_2D: + layer3d_draw_2d(node, tr_state); + return; + case TRAVERSE_DRAW_3D: + default: + return; + } + + /*layer3D maintains its own stacks*/ + oldb = tr_state->backgrounds; + oldv = tr_state->viewpoints; + oldf = tr_state->fogs; + oldn = tr_state->navigations; + tr_state->backgrounds = st->visual->back_stack; + tr_state->viewpoints = st->visual->view_stack; + tr_state->navigations = st->visual->navigation_stack; + tr_state->fogs = st->visual->fog_stack; + prev_layer = tr_state->is_layer; + tr_state->is_layer = 1; + + prev_cam = tr_state->camera; + tr_state->camera = &st->visual->camera; + old_visual = tr_state->visual; + + bbox_backup = tr_state->bbox; + gf_mx_copy(model_backup, tr_state->model_matrix); + gf_mx2d_copy(mx2d_backup, tr_state->transform); + + + /*compute viewport in visual coordinate*/ + rc = st->clip; + if (prev_cam) { + gf_mx_apply_rect(&tr_state->model_matrix, &rc); + + gf_mx_apply_rect(&prev_cam->modelview, &rc); + if (tr_state->camera->flags & CAM_HAS_VIEWPORT) + gf_mx_apply_rect(&prev_cam->viewport, &rc); +#if 0 + if (tr_state->visual->compositor->visual==tr_state->visual) { + GF_Matrix mx; + gf_mx_init(mx); + gf_mx_add_scale(&mx, tr_state->visual->compositor->scale_x, tr_state->visual->compositor->scale_y, FIX_ONE); + gf_mx_apply_rect(&mx, &rc); + } +#endif + } else { + gf_mx2d_apply_rect(&tr_state->transform, &rc); + +/* if (tr_state->visual->compositor->visual==tr_state->visual) { + gf_mx2d_init(mx2d_backup); + gf_mx2d_add_scale(&mx2d_backup, tr_state->visual->compositor->scale_x, tr_state->visual->compositor->scale_y); + gf_mx2d_apply_rect(&mx2d_backup, &rc); + } +*/ + /*switch visual*/ + tr_state->visual = st->visual; + } + + + /*check bindables*/ + gf_mx_init(tr_state->model_matrix); + l3d_CheckBindables(node, tr_state, st->first); + if (prev_cam) gf_mx_copy(tr_state->model_matrix, model_backup); + + + /*drawing a layer means drawing all subelements as a whole (no depth sorting with parents)*/ + if (tr_state->traversing_mode==TRAVERSE_SORT) { + + if (gf_node_dirty_get(node)) changed = 1; + gf_node_dirty_clear(node, GF_SG_NODE_DIRTY|GF_SG_VRML_BINDABLE_DIRTY); + + /*!! we were in a 2D mode, setup associated texture !!*/ + if (!prev_cam) { + switch (layer3d_setup_offscreen(node, st, tr_state, rc.width, rc.height)) { + case 0: + goto l3d_exit; + case 2: + if (!changed && !(st->visual->camera.flags & CAM_IS_DIRTY) && !st->visual->camera.anim_len) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Layer3D] No changes found , skipping 3D draw\n")); + goto layer3d_unchanged_2d; + } + default: + break; + } +#ifdef GPAC_USE_TINYGL + if (st->tgl_ctx) ostgl_make_current(st->tgl_ctx, 0); +#endif + + /*note that we don't backup the state as a layer3D cannot be declared in a layer3D*/ + tr_state->layer3d = node; + + rc = st->vp; + /*setup GL*/ + visual_3d_setup(tr_state->visual); + + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_PROJECTION); + visual_3d_matrix_reset(tr_state->visual); + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_TEXTURE); + visual_3d_matrix_reset(tr_state->visual); + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_MODELVIEW); + visual_3d_matrix_reset(tr_state->visual); + } else { + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_PROJECTION); + visual_3d_matrix_push(tr_state->visual); + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_TEXTURE); + visual_3d_matrix_push(tr_state->visual); + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_MODELVIEW); + visual_3d_matrix_push(tr_state->visual); + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Layer3D] Redrawing\n")); + + layer3d_setup_clip(st, tr_state, prev_cam ? 1 : 0, rc); + + cur_lights = tr_state->visual->num_lights; + /*this will init projection. Note that we're binding the viewpoint in the current pixelMetrics context + even if the viewpoint was declared in an inline below + if no previous camera, we're using offscreen rendering, force clear */ + visual_3d_init_draw(tr_state, prev_cam ? 1 : 2); + + visual_3d_check_collisions(tr_state, l->children); + tr_state->traversing_mode = TRAVERSE_SORT; + + /*shortcut node list*/ + node_list_backup = tr_state->visual->alpha_nodes_to_draw; + tr_state->visual->alpha_nodes_to_draw = gf_list_new(); + + /*reset cull flag*/ + tr_state->cull_flag = 0; + group_3d_traverse(node, (GroupingNode *)st, tr_state); + + visual_3d_flush_contexts(tr_state->visual, tr_state); + + gf_list_del(tr_state->visual->alpha_nodes_to_draw); + tr_state->visual->alpha_nodes_to_draw = node_list_backup; + + while (cur_lights < tr_state->visual->num_lights) { + visual_3d_remove_last_light(tr_state->visual); + } + + tr_state->traversing_mode = TRAVERSE_SORT; + + if (prev_cam) { + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_PROJECTION); + visual_3d_matrix_pop(tr_state->visual); + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_TEXTURE); + visual_3d_matrix_pop(tr_state->visual); + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_MODELVIEW); + visual_3d_matrix_pop(tr_state->visual); + } + + + /*!! we were in a 2D mode, create drawable context!!*/ + if (!prev_cam) { + DrawableContext *ctx; + + /*with TinyGL we draw directly to the offscreen buffer*/ +#ifndef GPAC_USE_TINYGL + gf_sc_copy_to_stencil(&st->txh); +#else +/*with TinyGL, need to recover depth for triscope*/ +#ifdef GPAC_TRISCOPE_MODE + if (st->txh.pixelformat==GF_PIXEL_RGBDS) gf_get_tinygl_depth(&st->txh); +#endif +#endif + + if (tr_state->visual->compositor->rasterizer->stencil_texture_modified) + tr_state->visual->compositor->rasterizer->stencil_texture_modified(gf_sc_texture_get_stencil(&st->txh) ); + gf_sc_texture_set_stencil(&st->txh, gf_sc_texture_get_stencil(&st->txh) ); + changed = 1; + +layer3d_unchanged_2d: + + /*restore visual*/ + tr_state->visual = old_visual; + tr_state->layer3d = NULL; + tr_state->appear = NULL; + // tr_state->camera = prev_cam; + + ctx = drawable_init_context_mpeg4(st->drawable, tr_state); + if (!ctx) return; + ctx->aspect.fill_texture = &st->txh; + ctx->flags |= CTX_NO_ANTIALIAS; + if (changed) ctx->flags |= CTX_APP_DIRTY; + if (st->txh.transparent) ctx->flags |= CTX_IS_TRANSPARENT; + drawable_finalize_sort(ctx, tr_state, NULL); + } + } + /*check picking - we must fall in our 2D clipper except when mvt is grabbed on layer*/ + else if (!gf_node_dirty_get(node) && (tr_state->traversing_mode==TRAVERSE_PICK)) { + GF_Ray prev_r; + SFVec3f start, end; + SFVec4f res; + Fixed in_x, in_y; + Bool do_pick = 0; + + if (!prev_cam) rc = st->vp; + + layer3d_setup_clip(st, tr_state, prev_cam ? 1 : 0, rc); + + if (tr_state->visual->compositor->active_layer==node) { + do_pick = (tr_state->visual->compositor->grabbed_sensor || tr_state->visual->compositor->navigation_state) ? 1 : 0; + } + + if (!prev_cam) gf_mx_from_mx2d(&tr_state->model_matrix, &tr_state->transform); + + if (!do_pick && !gf_list_count(tr_state->visual->compositor->sensors)) + do_pick = gf_sc_pick_in_clipper(tr_state, &st->clip); + + if (!do_pick) goto l3d_exit; + + prev_r = tr_state->ray; + + compositor_get_2d_plane_intersection(&tr_state->ray, &start); + + gf_mx_inverse(&tr_state->model_matrix); + gf_mx_apply_vec(&tr_state->model_matrix, &start); + + + if (tr_state->visual->compositor->visual==tr_state->visual) { + start.x = gf_mulfix(start.x, tr_state->visual->compositor->scale_x); + start.y = gf_mulfix(start.y, tr_state->visual->compositor->scale_y); + } else if (!prev_cam) { + start.x = gf_muldiv(start.x, st->visual->camera.width, st->clip.width); + start.y = gf_muldiv(start.y, st->visual->camera.height, st->clip.height); + } + + visual_3d_setup_projection(tr_state); + in_x = 2 * gf_divfix(start.x, st->visual->camera.width); + in_y = 2 * gf_divfix(start.y, st->visual->camera.height); + + res.x = in_x; res.y = in_y; res.z = -FIX_ONE; res.q = FIX_ONE; + gf_mx_apply_vec_4x4(&st->visual->camera.unprojection, &res); + if (!res.q) goto l3d_exit; + start.x = gf_divfix(res.x, res.q); + start.y = gf_divfix(res.y, res.q); + start.z = gf_divfix(res.z, res.q); + + res.x = in_x; res.y = in_y; res.z = FIX_ONE; res.q = FIX_ONE; + gf_mx_apply_vec_4x4(&st->visual->camera.unprojection, &res); + if (!res.q) goto l3d_exit; + end.x = gf_divfix(res.x, res.q); + end.y = gf_divfix(res.y, res.q); + end.z = gf_divfix(res.z, res.q); + tr_state->ray = gf_ray(start, end); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Layer3D] Picking: cast ray\n\tOrigin %.4f %.4f %.4f - End %.4f %.4f %.4f\n\tDir %.4f %.4f %.4f\n", + FIX2FLT(tr_state->ray.orig.x), FIX2FLT(tr_state->ray.orig.y), FIX2FLT(tr_state->ray.orig.z), + FIX2FLT(end.x), FIX2FLT(end.y), FIX2FLT(end.z), + FIX2FLT(tr_state->ray.dir.x), FIX2FLT(tr_state->ray.dir.y), FIX2FLT(tr_state->ray.dir.z))); + + group_3d_traverse(node, (GroupingNode *)st, tr_state); + tr_state->ray = prev_r; + + /*store info if navigation allowed - we just override any layer3D picked first since we are picking 2D + objects*/ + if (tr_state->camera->navigate_mode || (tr_state->camera->navigation_flags & NAV_ANY)) + tr_state->layer3d = node; + + tr_state->traversing_mode = TRAVERSE_PICK; + } + +l3d_exit: + /*restore camera*/ + tr_state->camera = prev_cam; + if (prev_cam) visual_3d_set_viewport(tr_state->visual, tr_state->camera->vp); + tr_state->visual = old_visual; + + /*restore traversing state*/ + tr_state->backgrounds = oldb; + tr_state->viewpoints = oldv; + tr_state->fogs = oldf; + tr_state->navigations = oldn; + tr_state->bbox = bbox_backup; + tr_state->is_layer = prev_layer; + gf_mx_copy(tr_state->model_matrix, model_backup); + gf_mx2d_copy(tr_state->transform, mx2d_backup); + + /*in case we missed bindables*/ + if (st->first) { + st->first = 0; + gf_node_dirty_set(node, 0, 0); + gf_sc_invalidate(tr_state->visual->compositor, NULL); + } +} + +void compositor_init_layer3d(GF_Compositor *compositor, GF_Node *node) +{ + Layer3DStack *stack; + GF_SAFEALLOC(stack, Layer3DStack); + + stack->visual = visual_new(compositor); + stack->visual->type_3d = 2; + stack->visual->camera.is_3D = 1; + stack->visual->camera.visibility = 0; + stack->visual->camera.speed = FIX_ONE; + camera_invalidate(&stack->visual->camera); + stack->first = 1; + + stack->txh.compositor = compositor; + stack->drawable = drawable_new(); + stack->drawable->node = node; + stack->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW; + + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseLayer3D); +} + +GF_Camera *compositor_layer3d_get_camera(GF_Node *node) +{ + Layer3DStack *st = (Layer3DStack *) gf_node_get_private(node); + return &st->visual->camera; +} + +void compositor_layer3d_bind_camera(GF_Node *node, Bool do_bind, u32 nav_value) +{ + Layer3DStack *st = (Layer3DStack *) gf_node_get_private(node); + GF_Node *n = (GF_Node*)gf_list_get(st->visual->navigation_stack, 0); + if (n) Bindable_SetSetBind(n, do_bind); + else st->visual->camera.navigate_mode = nav_value; +} + +#endif /*GPAC_DISABLE_3D*/ diff --git a/src/compositor/mpeg4_layout.c b/src/compositor/mpeg4_layout.c new file mode 100644 index 0000000..9fa23d8 --- /dev/null +++ b/src/compositor/mpeg4_layout.c @@ -0,0 +1,806 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" +#include "mpeg4_grouping.h" +#include "visual_manager.h" + +typedef struct +{ + PARENT_NODE_STACK_2D + + Bool is_scrolling; + u32 start_scroll_type; + Double start_time, pause_time; + GF_List *lines; + GF_Rect clip; + Fixed last_scroll, prev_rate, scroll_rate, scale_scroll, scroll_len, scroll_min, scroll_max; + + /*for keyboard navigation*/ + GF_SensorHandler hdl; + s32 key_scroll; +} LayoutStack; + +typedef struct +{ + Fixed width, height, ascent, descent; + u32 first_child, nb_children; +} LineInfo; + +static void layout_reset_lines(LayoutStack *st) +{ + while (gf_list_count(st->lines)) { + LineInfo *li = (LineInfo *)gf_list_get(st->lines, 0); + gf_list_rem(st->lines, 0); + free(li); + } +} + +static LineInfo *new_line_info(LayoutStack *st) +{ + LineInfo *li; + GF_SAFEALLOC(li, LineInfo); + + gf_list_add(st->lines, li); + return li; +} +static GFINLINE LineInfo *get_line_info(LayoutStack *st, u32 i) +{ + return (LineInfo *) gf_list_get(st->lines, i); +} + + +enum +{ + L_FIRST, + L_BEGIN, + L_MIDDLE, + L_END, + L_JUSTIFY, +}; + +static u32 get_justify(M_Layout *l, u32 i) +{ + if (l->justify.count <= i) return L_BEGIN; + if (!strcmp(l->justify.vals[i], "END")) return L_END; + if (!strcmp(l->justify.vals[i], "MIDDLE")) return L_MIDDLE; + if (!strcmp(l->justify.vals[i], "FIRST")) return L_FIRST; + if (!strcmp(l->justify.vals[i], "SPREAD")) return L_JUSTIFY; + if (!strcmp(l->justify.vals[i], "JUSTIFY")) return L_JUSTIFY; + return L_BEGIN; +} + +static void get_lines_info(LayoutStack *st, M_Layout *l) +{ + Fixed prev_discard_width; + u32 i, count; + LineInfo *li; + Fixed max_w, max_h; + + max_w = st->clip.width; + max_h = st->clip.height; + layout_reset_lines(st); + + count = gf_list_count(st->groups); + if (!count) return; + + li = new_line_info(st); + li->first_child = 0; + prev_discard_width = 0; + + for (i=0; igroups, i); + if (!l->horizontal) { + /*check if exceed column size or not - if so, move to next column or clip given wrap mode*/ + if (cg->final.height + li->height > max_h) { + if (l->wrap) { + li = new_line_info(st); + li->first_child = i; + } + } + if (cg->final.width > li->width) li->width = cg->final.width; + li->height += cg->final.height; + li->nb_children ++; + } else { + if (i && (cg->final.width + li->width> max_w)) { + if (l->wrap) { + if (!li->ascent) { + li->ascent = li->height; + li->descent = 0; + } + /*previous word is discardable (' ')*/ + if (prev_discard_width) { + li->width -= prev_discard_width; + li->nb_children--; + } + if (cg->discardable && (i+1==count)) break; + + li = new_line_info(st); + li->first_child = i; + if (cg->discardable) { + li->first_child++; + continue; + } + } + } + + /*get ascent/descent for text or height for non-text*/ + if (cg->ascent) { + if (li->ascent < cg->ascent) li->ascent = cg->ascent; + if (li->descent < cg->descent) li->descent = cg->descent; + if (li->height < li->ascent + li->descent) li->height = li->ascent + li->descent; + } else if (cg->final.height > li->height) { + li->height = cg->final.height; + } + li->width += cg->final.width; + li->nb_children ++; + + prev_discard_width = cg->discardable ? cg->final.width : 0; + + } + } +} + + +static void layout_justify(LayoutStack *st, M_Layout *l) +{ + u32 first, minor, major, i, k, nbLines; + Fixed current_top, current_left, h; + LineInfo *li; + ChildGroup *cg, *prev; + get_lines_info(st, l); + major = get_justify(l, 0); + minor = get_justify(l, 1); + + st->scroll_len = 0; + nbLines = gf_list_count(st->lines); + if (l->horizontal) { + if (l->wrap && !l->topToBottom) { + li = (LineInfo*)gf_list_get(st->lines, 0); + current_top = st->clip.y - st->clip.height; + if (li) current_top += li->height; + } else { + current_top = st->clip.y; + } + + /*for each line perform adjustment*/ + for (k=0; klines, k); + first = li->first_child; + if (!l->leftToRight) first += li->nb_children - 1; + + if (!l->topToBottom && k) current_top += li->height; + + /*set major alignment (X) */ + cg = (ChildGroup *)gf_list_get(st->groups, first); + switch (major) { + case L_END: + cg->final.x = st->clip.x + st->clip.width - li->width; + break; + case L_MIDDLE: + cg->final.x = st->clip.x + (st->clip.width - li->width)/2; + break; + case L_FIRST: + case L_BEGIN: + cg->final.x = st->clip.x; + break; + case L_JUSTIFY: + cg->final.x = st->clip.x; + if (li->nb_children>1) { + cg = (ChildGroup *)gf_list_get(st->groups, li->nb_children-1); + spacing = (st->clip.width - li->width) / (li->nb_children-1) ; + if (spacing<0) spacing = 0; + /*disable spacing for last text line*/ + else if (cg->ascent && (k+1==nbLines) ) + spacing = 0; + + } + break; + } + + + /*for each in the run */ + i = first; + while (1) { + cg = (ChildGroup *)gf_list_get(st->groups, i); + h = MAX(li->ascent, li->height); + switch (minor) { + case L_FIRST: + cg->final.y = current_top - h; + if (cg->ascent) { + cg->final.y += cg->ascent; + } else { + cg->final.y += cg->final.height; + } + break; + case L_MIDDLE: + cg->final.y = current_top - (h - cg->final.height)/2; + break; + case L_END: + cg->final.y = current_top; + break; + case L_BEGIN: + default: + cg->final.y = current_top - h + cg->final.height; + break; + } + /*update left for non-first children in line*/ + if (i != first) { + if (l->leftToRight) { + prev = (ChildGroup *)gf_list_get(st->groups, i-1); + } else { + prev = (ChildGroup *)gf_list_get(st->groups, i+1); + } + cg->final.x = prev->final.x + prev->final.width + spacing; + } + i += l->leftToRight ? +1 : -1; + if (l->leftToRight && (i==li->first_child + li->nb_children)) + break; + else if (!l->leftToRight && (i==li->first_child - 1)) + break; + } + if (l->topToBottom) { + current_top -= gf_mulfix(l->spacing, li->height); + } else { + current_top += gf_mulfix(l->spacing - FIX_ONE, li->height); + } + if (l->scrollVertical) { + st->scroll_len += li->height; + } else { + if (st->scroll_len < li->width) st->scroll_len = li->width; + } + } + return; + } + + /*Vertical aligment*/ + li = (LineInfo*)gf_list_get(st->lines, 0); + if (l->wrap && !l->leftToRight) { + current_left = st->clip.x + st->clip.width; + if (li) current_left -= li->width; + } else { + current_left = st->clip.x; + } + + /*for all columns in run*/ + for (k=0; klines, k); + + first = li->first_child; + if (!l->topToBottom) first += li->nb_children - 1; + + /*set major alignment (Y) */ + cg = (ChildGroup *)gf_list_get(st->groups, first); + switch (major) { + case L_END: + cg->final.y = st->clip.y - st->clip.height + li->height; + break; + case L_MIDDLE: + cg->final.y = st->clip.y - st->clip.height/2 + li->height/2; + break; + case L_FIRST: + case L_BEGIN: + cg->final.y = st->clip.y; + break; + case L_JUSTIFY: + cg->final.y = st->clip.y; + if (li->nb_children>1) { + spacing = (st->clip.height - li->height) / (li->nb_children-1) ; + if (spacing<0) spacing = 0; + } + break; + } + + /*for each in the run */ + i = first; + while (1) { + cg = (ChildGroup *)gf_list_get(st->groups, i); + switch (minor) { + case L_MIDDLE: + cg->final.x = current_left + li->width/2 - cg->final.width/2; + break; + case L_END: + cg->final.x = current_left + li->width - cg->final.width; + break; + case L_BEGIN: + case L_FIRST: + default: + cg->final.x = current_left; + break; + } + /*update top for non-first children in line*/ + if (i != first) { + if (l->topToBottom) { + prev = (ChildGroup *)gf_list_get(st->groups, i-1); + } else { + prev = (ChildGroup *)gf_list_get(st->groups, i+1); + } + cg->final.y = prev->final.y - prev->final.height + spacing; + } + i += l->topToBottom ? +1 : -1; + if (l->topToBottom && (i==li->first_child + li->nb_children)) + break; + else if (!l->topToBottom && (i==li->first_child - 1)) + break; + } + if (l->leftToRight) { + current_left += gf_mulfix(l->spacing, li->width); + } else if (k < nbLines - 1) { + li = (LineInfo*)gf_list_get(st->lines, k+1); + current_left -= gf_mulfix(l->spacing, li->width); + } + if (l->scrollVertical) { + if (st->scroll_len < li->height) st->scroll_len = li->height; + } else { + st->scroll_len += li->width; + } + } +} + +static void layout_setup_scroll_bounds(LayoutStack *st, M_Layout *l) +{ + u32 minor_justify = 0; + + st->scroll_min = st->scroll_max = 0; + + if (l->horizontal) minor_justify = l->scrollVertical ? 1 : 0; + else minor_justify = l->scrollVertical ? 0 : 1; + + /*update scroll-out max limit*/ + if (l->scrollMode != -1) { + /*set max limit*/ + switch( get_justify(l, minor_justify)) { + case L_END: + if (l->scrollVertical) { + if (st->scale_scroll<0) st->scroll_max = - st->scroll_len; + else st->scroll_max = st->clip.height; + } else { + if (st->scale_scroll<0) st->scroll_max = - st->clip.width; + else st->scroll_max = st->scroll_len; + } + break; + case L_MIDDLE: + if (l->scrollVertical) { + if (st->scale_scroll<0) st->scroll_max = - (st->clip.height + st->scroll_len)/2; + else st->scroll_max = (st->clip.height + st->scroll_len)/2; + } else { + if (st->scale_scroll<0) st->scroll_max = - (st->clip.width + st->scroll_len)/2; + else st->scroll_max = (st->clip.width + st->scroll_len)/2; + } + break; + default: + if (l->scrollVertical) { + if (st->scale_scroll<0) st->scroll_max = - st->clip.height; + else st->scroll_max = st->scroll_len; + } else { + if (st->scale_scroll<0) st->scroll_max = - st->scroll_len; + else st->scroll_max = st->clip.width; + } + break; + } + } + /*scroll-in only*/ + else { + st->scroll_max = 0; + } + + /*scroll-out only*/ + if (l->scrollMode==1) { + st->scroll_min = 0; + return; + } + + /*when vertically scrolling an horizontal layout, don't use vertical justification, only justify top/bottom lines*/ + if (l->horizontal && l->scrollVertical) { + if (st->scale_scroll<0) { + st->scroll_min = st->scroll_len; + } else { + st->scroll_min = - st->clip.height; + } + return; + } + + /*update scroll-in offset*/ + switch( get_justify(l, minor_justify)) { + case L_END: + if (l->scrollVertical) { + if (st->scale_scroll<0) st->scroll_min = st->clip.height; + else st->scroll_min = - st->scroll_len; + } else { + if (st->scale_scroll<0) st->scroll_min = st->scroll_len; + else st->scroll_min = -st->clip.width; + } + break; + case L_MIDDLE: + if (l->scrollVertical) { + if (st->scale_scroll<0) st->scroll_min = (st->clip.height + st->scroll_len)/2; + else st->scroll_min = - (st->clip.height + st->scroll_len)/2; + } else { + if (st->scale_scroll<0) st->scroll_min = (st->clip.width + st->scroll_len)/2; + else st->scroll_min = - (st->clip.width + st->scroll_len)/2; + } + break; + default: + if (l->scrollVertical) { + if (st->scale_scroll<0) st->scroll_min = st->scroll_len; + else st->scroll_min = - st->clip.height; + } else { + if (st->scale_scroll<0) st->scroll_min = st->clip.width; + else st->scroll_min = - st->scroll_len; + } + break; + } +} + + +static void layout_scroll(GF_TraverseState *tr_state, LayoutStack *st, M_Layout *l) +{ + u32 i, nb_lines; + Fixed scrolled, rate, ellapsed, scroll_diff; + Bool smooth, do_scroll, stop_anim; + Double time = 0; + ChildGroup *cg; + + /*not scrolling*/ + if (!st->scale_scroll && !st->is_scrolling && !st->key_scroll) return; + + if (st->key_scroll) { + if (!st->is_scrolling) { + layout_setup_scroll_bounds(st, l); + st->is_scrolling = 1; + } + + scrolled = st->last_scroll + INT2FIX(st->key_scroll); + } else { + + time = gf_node_get_scene_time((GF_Node *)l); + + // if (st->scale_scroll && (st->prev_rate!=st->scale_scroll)) st->start_scroll_type = 1; + + /*if scroll rate changed to previous non-zero value, this is a + scroll restart, don't re-update bounds*/ + if ((st->start_scroll_type==2) && (st->prev_rate==st->scale_scroll)) st->start_scroll_type = 0; + + if (st->start_scroll_type) { + st->start_time = time; + st->is_scrolling = 1; + st->prev_rate = st->scale_scroll; + + /*continuous restart: use last scroll to update the start time. We must recompute scroll bounds + since switching from scroll_rate >0 to <0 changes the bounds !*/ + if ((st->start_scroll_type==2) && st->scale_scroll) { + Fixed cur_pos = st->scroll_min + st->last_scroll; + layout_setup_scroll_bounds(st, l); + cur_pos -= st->scroll_min; + st->start_time = time - FIX2FLT(gf_divfix(cur_pos, st->scale_scroll)); + } else { + layout_setup_scroll_bounds(st, l); + } + st->last_scroll = 0; + st->start_scroll_type = 0; + } + + /*handle pause/resume*/ + rate = st->scale_scroll; + if (!rate) { + if (!st->pause_time) { + st->pause_time = time; + } else { + time = st->pause_time; + } + rate = st->prev_rate; + } else if (st->pause_time) { + st->start_time += (time - st->pause_time); + st->pause_time = 0; + } + + /*compute advance in pixels for smooth scroll*/ + ellapsed = FLT2FIX((Float) (time - st->start_time)); + scrolled = gf_mulfix(ellapsed, rate); + } + + smooth = l->smoothScroll; + /*if the scroll is in the same direction as the layout, there is no notion of line or column to scroll + so move to smooth mode*/ + if (!l->horizontal && l->scrollVertical) smooth = 1; + else if (l->horizontal && !l->scrollVertical) smooth = 1; + + + stop_anim = 0; + /*compute scroll diff for non-smooth mode*/ + do_scroll = 1; + if (smooth) { + do_scroll = 1; + } else { + Fixed dim; + scroll_diff = scrolled - st->last_scroll; + do_scroll = 0; + + nb_lines = gf_list_count(st->lines); + for (i=0; i < nb_lines; i++) { + LineInfo *li = (LineInfo*)gf_list_get(st->lines, i); + dim = l->scrollVertical ? li->height : li->width; + if (st->key_scroll) { + if (st->key_scroll<0) dim = -dim; + scrolled = dim + st->last_scroll; + /*in key mode we must handle the min ourselves since we can go below the scroll limit*/ + if (st->scroll_min > st->scroll_len + scrolled) { + scrolled = st->scroll_min - st->scroll_len; + } else { + do_scroll = 1; + } + break; + } else if (ABS(scroll_diff) >= dim) { + if (scroll_diff<0) scroll_diff = -dim; + else scroll_diff = dim; + do_scroll = 1; + break; + } + } + } + + scroll_diff = st->scroll_max - st->scroll_min; + if ((scroll_diff<0) && (scrolled<=scroll_diff)) { + stop_anim = 1; + scrolled = scroll_diff; + } + else if ((scroll_diff>0) && (scrolled>=scroll_diff)) { + stop_anim = 1; + scrolled = scroll_diff; + } + + if (do_scroll) + st->last_scroll = scrolled; + else + scrolled = st->last_scroll; + + i=0; + while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) { + if (l->scrollVertical) { + cg->scroll_y = st->scroll_min + scrolled; + cg->scroll_x = 0; + } else { + cg->scroll_x = st->scroll_min + scrolled; + cg->scroll_y = 0; + } + } + + if (st->key_scroll) { + st->key_scroll = 0; + return; + } + /*draw next frame*/ + if (!stop_anim) { + gf_sc_invalidate(tr_state->visual->compositor, NULL); + return; + } + + /*done*/ + if (!l->loop) return; + + /*restart*/ + st->start_time = time; + gf_sc_invalidate(tr_state->visual->compositor, NULL); +} + + +static void TraverseLayout(GF_Node *node, void *rs, Bool is_destroy) +{ + Bool recompute_layout; + u32 i; + ChildGroup *cg; + GF_IRect prev_clip; + Bool mode_bckup, had_clip; + ParentNode2D *parent_bck; + GF_Rect clip, prev_clipper; + M_Layout *l = (M_Layout *)node; + LayoutStack *st = (LayoutStack *) gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + layout_reset_lines(st); + parent_node_predestroy((ParentNode2D *)st); + gf_list_del(st->lines); + free(st); + return; + } + + /*note we don't clear dirty flag, this is done in traversing*/ + if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { + + /*TO CHANGE IN BIFS - scroll_rate is quite unusable*/ + st->scale_scroll = st->scroll_rate = l->scrollRate; + /*move to pixel metrics*/ + if (visual_get_size_info(tr_state, &st->clip.width, &st->clip.height)) { + st->scale_scroll = gf_mulfix(st->scale_scroll, l->scrollVertical ? st->clip.height : st->clip.width); + } + /*setup bounds in local coord system*/ + if (l->size.x>=0) st->clip.width = l->size.x; + if (l->size.y>=0) st->clip.height = l->size.y; + st->bounds = st->clip = gf_rect_center(st->clip.width, st->clip.height); + + if (st->scale_scroll && !st->start_scroll_type) st->start_scroll_type = 1; + + drawable_reset_group_highlight(tr_state, node); + } + + /*don't waste time traversing is pick ray not in clipper*/ + if ((tr_state->traversing_mode==TRAVERSE_PICK) && !gf_sc_pick_in_clipper(tr_state, &st->clip)) + goto layout_exit; + + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + tr_state->bounds = st->clip; +#ifndef GPAC_DISABLE_3D + gf_bbox_from_rect(&tr_state->bbox, &st->clip); +#endif + goto layout_exit; + } + + recompute_layout = 0; + if (gf_node_dirty_get(node)) recompute_layout = 1; + + /*setup clipping*/ + prev_clip = tr_state->visual->top_clipper; + if (tr_state->traversing_mode==TRAVERSE_SORT) { + clip = compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 0); + if (tr_state->has_clip) { + tr_state->visual->top_clipper = gf_rect_pixelize(&tr_state->clipper); + gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip); + } + } + if (recompute_layout) { + GF_LOG(GF_LOG_COMPOSE, GF_LOG_DEBUG, ("[Layout] recomputing positions\n")); + + parent_node_reset((ParentNode2D*)st); + + /*setup traversing state*/ + parent_bck = tr_state->parent; + mode_bckup = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + tr_state->parent = (ParentNode2D *) st; + + if (l->wrap) tr_state->text_split_mode = 1; + parent_node_traverse(node, (ParentNode2D *)st, tr_state); + /*restore traversing state*/ + tr_state->parent = parent_bck; + tr_state->traversing_mode = mode_bckup; + if (l->wrap) tr_state->text_split_mode = 0; + + /*center all nodes*/ + i=0; + while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) { + cg->final.x = - cg->final.width/2; + cg->final.y = cg->final.height/2; + } + + /*apply justification*/ + layout_justify(st, l); + + /*prepare initial scroll bounds*/ +// layout_setup_scroll_bounds(st, l); + } + + /*scroll*/ + layout_scroll(tr_state, st, l); + + i=0; + while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) { + parent_node_child_traverse(cg, tr_state); + } + tr_state->visual->top_clipper = prev_clip; + if (tr_state->traversing_mode==TRAVERSE_SORT) { + if (had_clip) tr_state->clipper = prev_clipper; + tr_state->has_clip = had_clip; + + drawable_check_focus_highlight(node, tr_state, &st->clip); + } + +layout_exit: + tr_state->text_split_mode = 0; +} + +static void OnLayout(GF_SensorHandler *sh, Bool is_over, GF_Event *ev, GF_Compositor *compositor) +{ + Bool vertical = ((M_Layout *)sh->sensor)->scrollVertical; + LayoutStack *st = (LayoutStack *) gf_node_get_private(sh->sensor); + + if (!is_over) { + st->is_scrolling = 0; + st->key_scroll = 0; + return; + } + if (ev->type!=GF_EVENT_KEYDOWN) { + st->is_scrolling = 0; + st->key_scroll = 0; + return; + } + + switch (ev->key.key_code) { + case GF_KEY_LEFT: + if (vertical) return; + st->key_scroll = -1; + break; + case GF_KEY_RIGHT: + if (vertical) return; + st->key_scroll = +1; + break; + case GF_KEY_UP: + if (!vertical) return; + st->key_scroll = +1; + break; + case GF_KEY_DOWN: + if (!vertical) return; + st->key_scroll = -1; + break; + default: + st->key_scroll = 0; + return; + } + gf_sc_invalidate(compositor, NULL); +} + +static Bool layout_is_enabled(GF_Node *node) +{ + M_Layout *l = (M_Layout *)node; + if (l->scrollRate != 0) return 0; + return 1; +} + +void compositor_init_layout(GF_Compositor *compositor, GF_Node *node) +{ + LayoutStack *stack; + GF_SAFEALLOC(stack, LayoutStack); + + parent_node_setup((ParentNode2D*)stack); + stack->lines = gf_list_new(); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseLayout); + stack->hdl.sensor = node; + stack->hdl.IsEnabled = layout_is_enabled; + stack->hdl.OnUserEvent = OnLayout; +} + +void compositor_layout_modified(GF_Compositor *compositor, GF_Node *node) +{ + LayoutStack *st = (LayoutStack *) gf_node_get_private(node); + /*if modif other than scrollrate restart scroll*/ + if (st->scroll_rate == ((M_Layout*)node)->scrollRate) { + st->start_scroll_type = 1; + } + /*if modif on scroll rate only, indicate continous restart*/ + else if (((M_Layout*)node)->scrollRate) { + st->start_scroll_type = 2; + } + gf_node_dirty_set(node, GF_SG_NODE_DIRTY, 0); + gf_sc_invalidate(compositor, NULL); +} + +GF_SensorHandler *compositor_mpeg4_layout_get_sensor_handler(GF_Node *node) +{ + LayoutStack *st = (LayoutStack *) gf_node_get_private(node); + return &st->hdl; +} + diff --git a/src/compositor/mpeg4_lighting.c b/src/compositor/mpeg4_lighting.c new file mode 100644 index 0000000..fbfd592 --- /dev/null +++ b/src/compositor/mpeg4_lighting.c @@ -0,0 +1,135 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" + +#ifndef GPAC_DISABLE_3D + +#include "visual_manager.h" + + +static void TraverseSpotLight(GF_Node *n, void *rs, Bool is_destroy) +{ + M_SpotLight *sl = (M_SpotLight *)n; + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy || !sl->on) return; + + /*store local bounds for culling*/ + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + /*how crude - refine to only have the light cone :)*/ + SFVec3f size; + size.x = size.y = size.z = sl->radius; + gf_vec_add(tr_state->bbox.max_edge, sl->location, size); + gf_vec_diff(tr_state->bbox.min_edge, sl->location, size); + gf_bbox_refresh(&tr_state->bbox); + return; + } + else if (tr_state->traversing_mode == TRAVERSE_LIGHTING) { + visual_3d_matrix_push(tr_state->visual); + visual_3d_matrix_add(tr_state->visual, tr_state->model_matrix.m); + + visual_3d_add_spot_light(tr_state->visual, sl->ambientIntensity, sl->attenuation, sl->beamWidth, + sl->color, sl->cutOffAngle, sl->direction, sl->intensity, sl->location); + + visual_3d_matrix_pop(tr_state->visual); + } +} + +void compositor_init_spot_light(GF_Compositor *compositor, GF_Node *node) +{ + /*no need for a stck*/ + gf_node_set_callback_function(node, TraverseSpotLight); +} + +static void TraversePointLight(GF_Node *n, void *rs, Bool is_destroy) +{ + M_PointLight *pl = (M_PointLight *)n; + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy || !pl->on) return; + + /*store local bounds for culling*/ + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + SFVec3f size; + size.x = size.y = size.z = pl->radius; + gf_vec_add(tr_state->bbox.max_edge, pl->location, size); + gf_vec_diff(tr_state->bbox.min_edge, pl->location, size); + gf_bbox_refresh(&tr_state->bbox); + return; + } + else if (tr_state->traversing_mode == TRAVERSE_LIGHTING) { + visual_3d_matrix_push(tr_state->visual); + visual_3d_matrix_add(tr_state->visual, tr_state->model_matrix.m); + + visual_3d_add_point_light(tr_state->visual, pl->ambientIntensity, pl->attenuation, pl->color, + pl->intensity, pl->location); + + visual_3d_matrix_pop(tr_state->visual); + } +} + +void compositor_init_point_light(GF_Compositor *compositor, GF_Node *node) +{ + /*no need for a stck*/ + gf_node_set_callback_function(node, TraversePointLight); +} + + +static void TraverseDirectionalLight(GF_Node *n, void *rs, Bool is_destroy) +{ + Bool *stack = (Bool*)gf_node_get_private(n); + M_DirectionalLight *dl = (M_DirectionalLight *)n; + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + free(stack); + return; + } + if (tr_state->switched_off || !dl->on) return; + + /*1- DL only lights the parent group, no need for culling it*/ + /*DL is set dynamically while traversing, the only mode that interest us is draw*/ + if (tr_state->traversing_mode) return; + + if (tr_state->local_light_on) { + *stack = visual_3d_add_directional_light(tr_state->visual, dl->ambientIntensity, dl->color, dl->intensity, dl->direction); + } else { + if (*stack) visual_3d_remove_last_light(tr_state->visual); + *stack = 0; + } +} + +void compositor_init_directional_light(GF_Compositor *compositor, GF_Node *node) +{ + /*our stack is just a boolean used to store whether the light was turned on successfully*/ + Bool *stack = (Bool*)malloc(sizeof(Bool)); + *stack = 0; + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseDirectionalLight); +} + +#endif /*GPAC_DISABLE_3D*/ diff --git a/src/compositor/mpeg4_path_layout.c b/src/compositor/mpeg4_path_layout.c new file mode 100644 index 0000000..d88c29f --- /dev/null +++ b/src/compositor/mpeg4_path_layout.c @@ -0,0 +1,272 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" +#include "mpeg4_grouping.h" +#include "visual_manager.h" + +typedef struct +{ + PARENT_NODE_STACK_2D + + GF_Node *last_geom; + GF_PathIterator *iter; +} PathLayoutStack; + + + + +static void TraversePathLayout(GF_Node *node, void *rs, Bool is_destroy) +{ + u32 i, count, minor, major, int_bck; + Fixed length, offset, length_after_point; + Bool res; + u32 mode_bckup; + ChildGroup *cg; +#ifndef GPAC_DISABLE_3D + GF_Matrix mat; +#endif + GF_Matrix2D mx2d; + ParentNode2D *parent_bck; + PathLayoutStack *gr = (PathLayoutStack*) gf_node_get_private(node); + M_PathLayout *pl = (M_PathLayout *)node; + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + parent_node_predestroy((ParentNode2D *)gr); + if (gr->iter) gf_path_iterator_del(gr->iter); + free(gr); + return; + } + if (!pl->geometry) return; + + /*only low-level primitives allowed*/ + switch (gf_node_get_tag((GF_Node *) pl->geometry)) { + case TAG_MPEG4_Rectangle: return; + case TAG_MPEG4_Circle: return; + case TAG_MPEG4_Ellipse: return; + } + + /*store traversing state*/ +#ifndef GPAC_DISABLE_3D + gf_mx_copy(mat, tr_state->model_matrix); + gf_mx_init(tr_state->model_matrix); +#endif + + gf_mx2d_copy(mx2d, tr_state->transform); + gf_mx2d_init(tr_state->transform); + + parent_bck = tr_state->parent; + tr_state->parent = NULL; + + /*check geom changes*/ + if ((pl->geometry != gr->last_geom) || gf_node_dirty_get(pl->geometry)) { + if (gr->iter) gf_path_iterator_del(gr->iter); + gr->iter = NULL; + + int_bck = tr_state->switched_off; + mode_bckup = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + tr_state->switched_off = 1; + gf_node_traverse((GF_Node *)pl->geometry, tr_state); + tr_state->traversing_mode = mode_bckup; + tr_state->switched_off = int_bck; + } + + if (!gr->iter) { + Drawable *dr = NULL; + /*get the drawable */ + switch (gf_node_get_tag(pl->geometry) ) { + case TAG_MPEG4_Circle: + case TAG_MPEG4_Curve2D: + case TAG_MPEG4_XCurve2D: + case TAG_MPEG4_Ellipse: + case TAG_MPEG4_IndexedLineSet2D: + case TAG_MPEG4_IndexedFaceSet2D: + case TAG_MPEG4_Rectangle: + case TAG_X3D_Disk2D: + case TAG_X3D_Arc2D: + case TAG_X3D_Polyline2D: + case TAG_X3D_TriangleSet2D: + dr = (Drawable *) gf_node_get_private( (GF_Node *) pl->geometry); + break; + default: + break; + } + /*init iteration*/ + if (!dr || !dr->path) return; + gr->iter = gf_path_iterator_new(dr->path); + if (!gr->iter) return; + } + + tr_state->parent = (ParentNode2D *) gr; + int_bck = tr_state->text_split_mode; + tr_state->text_split_mode = 2; + mode_bckup = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + parent_node_traverse(node, (ParentNode2D *) gr, tr_state); + tr_state->text_split_mode = int_bck; + tr_state->traversing_mode = mode_bckup; + + /*restore traversing state*/ +#ifndef GPAC_DISABLE_3D + gf_mx_copy(tr_state->model_matrix, mat); +#endif + tr_state->parent = parent_bck; + gf_mx2d_copy(tr_state->transform, mx2d); + + count = gf_list_count(gr->groups); + + length = gf_path_iterator_get_length(gr->iter); + /*place all children*/ + offset = gf_mulfix(length, pl->pathOffset); + + major = pl->alignment.count ? pl->alignment.vals[0] : 0; + minor = (pl->alignment.count==2) ? pl->alignment.vals[1] : 0; + + if (pl->wrapMode==1) { + while (offset<0) offset += length; + } + + for (i=0; igroups, i); + if (cg->original.width>length) break; + + /*first set our center and baseline*/ + gf_mx2d_init(mx2d); + + /*major align*/ + switch (major) { + case 2: + if (cg->ascent) gf_mx2d_add_translation(&mx2d, -cg->original.x - cg->original.width, 0); + else gf_mx2d_add_translation(&mx2d, -cg->original.width/2, 0); + length_after_point = 0; + break; + case 1: + length_after_point = cg->original.width/2; + if (cg->ascent) gf_mx2d_add_translation(&mx2d, -cg->original.x - cg->original.width / 2, 0); + break; + default: + case 0: + if (cg->ascent) gf_mx2d_add_translation(&mx2d, cg->original.x, 0); + else gf_mx2d_add_translation(&mx2d, cg->original.width/2, 0); + length_after_point = cg->original.width; + break; + } + + /*if wrapping and out of path, restart*/ + if ((pl->wrapMode==1) && (offset+length_after_point>=length)) { + offset += length_after_point; + offset -= length; + i--; + continue; + } + /*if not wrapping and not yet in path skip */ + if (!pl->wrapMode && (offset+length_after_point < 0)) { + parent_node_child_traverse_matrix(cg, (GF_TraverseState *)rs, NULL); + goto next; + } + + /*minor justify*/ + switch (minor) { + /*top alignment*/ + case 3: + if (cg->ascent) + gf_mx2d_add_translation(&mx2d, 0, -1 * cg->ascent); + else + gf_mx2d_add_translation(&mx2d, 0, -1 * cg->original.height / 2); + + break; + /*baseline*/ + case 1: + /*move to bottom align if not text*/ + if (!cg->ascent) + gf_mx2d_add_translation(&mx2d, 0, cg->original.height / 2); + break; + /*middle*/ + case 2: + /*if text use (asc+desc) /2 as line height since glyph height differ*/ + if (cg->ascent) + gf_mx2d_add_translation(&mx2d, 0, cg->descent - (cg->ascent + cg->descent) / 2); + break; + /*bottomline alignment*/ + case 0: + default: + if (cg->ascent) + gf_mx2d_add_translation(&mx2d, 0, cg->descent); + else + gf_mx2d_add_translation(&mx2d, 0, cg->original.height / 2); + + break; + } + res = gf_path_iterator_get_transform(gr->iter, offset, (Bool) (pl->wrapMode==2), &mx2d, 1, length_after_point); + if (!res) break; + + parent_node_child_traverse_matrix(cg, (GF_TraverseState *)rs, &mx2d); + +next: + if (i+1groups, i+1); + + /*update offset according to major alignment */ + switch (major) { + case 2: + if (cg_next->ascent) offset += gf_mulfix(pl->spacing , cg_next->original.x); + offset += gf_mulfix(pl->spacing , cg_next->original.width); + break; + case 1: + if (cg->ascent) offset += gf_mulfix(pl->spacing, cg->original.x) / 2; + offset += gf_mulfix(pl->spacing, cg->original.width) / 2; + offset += cg_next->original.width / 2; + break; + default: + case 0: + if (cg->ascent) offset += gf_mulfix(pl->spacing, cg->original.x); + offset += gf_mulfix(pl->spacing , cg->original.width); + break; + } + } + /*wrap*/ + if ((pl->wrapMode==1) && (offset>=length)) offset-=length; + } + + /*undrawn nodes*/ + for (;igroups, i); + parent_node_child_traverse_matrix(cg, (GF_TraverseState *)rs, NULL); + } + + parent_node_reset((ParentNode2D *) gr); +} + +void compositor_init_path_layout(GF_Compositor *compositor, GF_Node *node) +{ + PathLayoutStack *stack; + GF_SAFEALLOC(stack, PathLayoutStack); + + parent_node_setup((ParentNode2D*)stack); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraversePathLayout); +} + diff --git a/src/compositor/mpeg4_sensors.c b/src/compositor/mpeg4_sensors.c new file mode 100644 index 0000000..ff70bec --- /dev/null +++ b/src/compositor/mpeg4_sensors.c @@ -0,0 +1,1288 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "visual_manager.h" +/*for anchor*/ +#include "mpeg4_grouping.h" +/*for anchor processing, which needs to be filtered at the inline scene level*/ +#include + +/*for event DOM filtering type ...*/ +#include + + +static void mpeg4_sensor_deleted(GF_Node *node, GF_SensorHandler *hdl) +{ + GF_Compositor *compositor = gf_sc_get_compositor(node); + if (compositor) { + gf_list_del_item(compositor->previous_sensors, hdl); + if (compositor->interaction_sensors) compositor->interaction_sensors--; +#ifndef GPAC_DISABLE_SVG + gf_sg_unregister_event_type(gf_node_get_graph(node), GF_DOM_EVENT_MOUSE|GF_DOM_EVENT_KEY); +#endif + } +} + +static void mpeg4_sensor_created(GF_Compositor *compositor, GF_Node *node) +{ + compositor->interaction_sensors--; +#ifndef GPAC_DISABLE_SVG + gf_sg_register_event_type(gf_node_get_graph(node), GF_DOM_EVENT_MOUSE|GF_DOM_EVENT_KEY); +#endif +} + + +typedef struct +{ + GROUPING_NODE_STACK_2D + + Bool enabled, active, over; + GF_SensorHandler hdl; + GF_Compositor *compositor; +} AnchorStack; + +static void TraverseAnchor(GF_Node *node, void *rs, Bool is_destroy) +{ + AnchorStack *st = (AnchorStack *) gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + mpeg4_sensor_deleted(node, &st->hdl); + gf_sc_check_focus_upon_destroy(node); + free(st); + return; + } + + if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { + MFURL *url; + if (gf_node_get_tag(node)==TAG_MPEG4_Anchor) { + url = & ((M_Anchor *)node)->url; + } else { + url = & ((X_Anchor *)node)->url; + } + st->enabled = 0; + if (url->count && url->vals[0].url && strlen(url->vals[0].url) ) + st->enabled = 1; + + if (!tr_state->visual->compositor->user->EventProc) { + st->enabled = 0; + } + gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); + } + + group_2d_traverse(node, (GroupingNode2D*)st, tr_state); +} + +static void anchor_activation(GF_Node *node, AnchorStack *st, GF_Compositor *compositor) +{ + GF_Event evt; + MFURL *url; + u32 i; + if (gf_node_get_tag(node)==TAG_MPEG4_Anchor) { + url = & ((M_Anchor *)node)->url; + evt.navigate.param_count = ((M_Anchor *)node)->parameter.count; + evt.navigate.parameters = (const char **) ((M_Anchor *)node)->parameter.vals; + } else { + url = & ((X_Anchor *)node)->url; + evt.navigate.param_count = ((X_Anchor *)node)->parameter.count; + evt.navigate.parameters = (const char **) ((X_Anchor *)node)->parameter.vals; + } + evt.type = GF_EVENT_NAVIGATE; + i=0; + while (icount) { + evt.navigate.to_url = url->vals[i].url; + if (!evt.navigate.to_url) break; + /*current scene navigation*/ + if (evt.navigate.to_url[0] == '#') { + GF_Node *bindable; + evt.navigate.to_url++; + bindable = gf_sg_find_node_by_name(gf_node_get_graph(node), (char *) evt.navigate.to_url); + if (bindable) { + Bindable_SetSetBind(bindable, 1); + break; + } + } else if (compositor->term) { + if (gf_inline_process_anchor(node, &evt)) + break; + } else if (compositor->user->EventProc) { + if (compositor->user->EventProc(compositor->user->opaque, &evt)) + break; + } + i++; + } +} + +static void OnAnchor(GF_SensorHandler *sh, Bool is_over, GF_Event *ev, GF_Compositor *compositor) +{ + GF_Event evt; + MFURL *url; + AnchorStack *st = (AnchorStack *) gf_node_get_private(sh->sensor); + + if ((ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) st->active = 1; + else if ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) st->active = 1; + else if (st->active && ( + /*mouse*/ ((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) + || /*mouse*/((ev->type==GF_EVENT_KEYUP) && (ev->key.key_code==GF_KEY_ENTER)) + ) ) { + anchor_activation(sh->sensor, st, compositor); + } else if (is_over && !st->over) { + st->over = 1; + if (compositor->user->EventProc) { + evt.type = GF_EVENT_NAVIGATE_INFO; + if (gf_node_get_tag(sh->sensor)==TAG_MPEG4_Anchor) { + evt.navigate.to_url = ((M_Anchor *)sh->sensor)->description.buffer; + url = & ((M_Anchor *)sh->sensor)->url; + } else { + evt.navigate.to_url = ((X_Anchor *)sh->sensor)->description.buffer; + url = & ((X_Anchor *)sh->sensor)->url; + } + if (!evt.navigate.to_url || !strlen(evt.navigate.to_url)) evt.navigate.to_url = url->vals[0].url; + compositor->user->EventProc(compositor->user->opaque, &evt); + } + } else if (!is_over) { + st->over = 0; + } +} + +static Bool anchor_is_enabled(GF_Node *node) +{ + AnchorStack *st = (AnchorStack *) gf_node_get_private(node); + return st->enabled; +} + +static void on_activate_anchor(GF_Node *node) +{ + AnchorStack *st = (AnchorStack *) gf_node_get_private(node); + if (!((M_Anchor *)node)->on_activate) return; + + anchor_activation(node, st, st->compositor); +} + +GF_SensorHandler *gf_sc_anchor_get_handler(GF_Node *n) +{ + AnchorStack *st = (AnchorStack *) gf_node_get_private(n); + return &st->hdl; +} + + +void compositor_init_anchor(GF_Compositor *compositor, GF_Node *node) +{ + AnchorStack *stack; + GF_SAFEALLOC(stack, AnchorStack); + + stack->hdl.IsEnabled = anchor_is_enabled; + stack->hdl.OnUserEvent = OnAnchor; + stack->hdl.sensor = node; + if (gf_node_get_tag(node)==TAG_MPEG4_Anchor) { + ((M_Anchor *)node)->on_activate = on_activate_anchor; + } + stack->compositor = compositor; + mpeg4_sensor_created(compositor, node); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseAnchor); +} + + +typedef struct +{ + GF_SensorHandler hdl; + GF_Compositor *compositor; + Fixed start_angle; + GF_Matrix initial_matrix; +} DiscSensorStack; + +static void DestroyDiscSensor(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + DiscSensorStack *st = (DiscSensorStack *) gf_node_get_private(node); + mpeg4_sensor_deleted(node, &st->hdl); + free(st); + } +} + +static Bool ds_is_enabled(GF_Node *n) +{ + M_DiscSensor *ds = (M_DiscSensor *)n; + return (ds->enabled || ds->isActive); +} + + +static void OnDiscSensor(GF_SensorHandler *sh, Bool is_over, GF_Event *ev, GF_Compositor *compositor) +{ + Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0; + M_DiscSensor *ds = (M_DiscSensor *)sh->sensor; + DiscSensorStack *stack = (DiscSensorStack *) gf_node_get_private(sh->sensor); + + if (ds->isActive && + (!ds->enabled + || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) + || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER))) ) + ) ) { + if (ds->autoOffset) { + ds->offset = ds->rotation_changed; + /*that's an exposedField*/ + gf_node_event_out_str(sh->sensor, "offset"); + } + ds->isActive = 0; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 0; + } else if (is_mouse) { + if (!ds->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) { + /*store inverse matrix*/ + gf_mx_copy(stack->initial_matrix, compositor->hit_local_to_world); + stack->start_angle = gf_atan2(compositor->hit_local_point.y, compositor->hit_local_point.x); + ds->isActive = 1; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 1; + } + else if (ds->isActive) { + GF_Ray loc_ray; + Fixed rot; + SFVec3f res; + loc_ray = compositor->hit_world_ray; + gf_mx_apply_ray(&stack->initial_matrix, &loc_ray); + compositor_get_2d_plane_intersection(&loc_ray, &res); + + rot = gf_atan2(res.y, res.x) - stack->start_angle + ds->offset; + if (ds->minAngle < ds->maxAngle) { + /*FIXME this doesn't work properly*/ + if (rot < ds->minAngle) rot = ds->minAngle; + if (rot > ds->maxAngle) rot = ds->maxAngle; + } + ds->rotation_changed = rot; + gf_node_event_out_str(sh->sensor, "rotation_changed"); + ds->trackPoint_changed.x = res.x; + ds->trackPoint_changed.y = res.y; + gf_node_event_out_str(sh->sensor, "trackPoint_changed"); + } + } else { + if (!ds->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) { + ds->isActive = 1; + stack->start_angle = ds->offset; + gf_node_event_out_str(sh->sensor, "isActive"); + } + else if (ds->isActive && (ev->type==GF_EVENT_KEYDOWN)) { + Fixed res; + Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? GF_PI/8 : GF_PI/64; + res = stack->start_angle; + switch (ev->key.key_code) { + case GF_KEY_LEFT: + case GF_KEY_UP: + res += -diff; + break; + case GF_KEY_RIGHT: + case GF_KEY_DOWN: + res += diff; + break; + case GF_KEY_HOME: + res = ds->offset; + break; + default: return; + } + if (ds->minAngle < ds->maxAngle) { + /*FIXME this doesn't work properly*/ + if (res < ds->minAngle) res = ds->minAngle; + if (res > ds->maxAngle) res = ds->maxAngle; + } + stack->start_angle = res; + ds->rotation_changed = res; + gf_node_event_out_str(sh->sensor, "rotation_changed"); + } + } +} + +static GF_SensorHandler *disc_sensor_get_handler(GF_Node *n) +{ + DiscSensorStack *st = (DiscSensorStack *)gf_node_get_private(n); + return &st->hdl; +} + +void compositor_init_disc_sensor(GF_Compositor *compositor, GF_Node *node) +{ + DiscSensorStack *st; + GF_SAFEALLOC(st, DiscSensorStack); + + st->hdl.IsEnabled = ds_is_enabled; + st->hdl.OnUserEvent = OnDiscSensor; + st->hdl.sensor = node; + st->compositor = compositor; + mpeg4_sensor_created(compositor, node); + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroyDiscSensor); +} + + +typedef struct +{ + SFVec2f start_drag; + GF_Matrix initial_matrix; + GF_Compositor *compositor; + GF_SensorHandler hdl; +} PS2DStack; + +static void DestroyPlaneSensor2D(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + PS2DStack *st = (PS2DStack *) gf_node_get_private(node); + mpeg4_sensor_deleted(node, &st->hdl); + free(st); + } +} + +static Bool ps2D_is_enabled(GF_Node *n) +{ + M_PlaneSensor2D *ps2d = (M_PlaneSensor2D *)n; + return (ps2d->enabled || ps2d->isActive); +} + +static void OnPlaneSensor2D(GF_SensorHandler *sh, Bool is_over, GF_Event *ev, GF_Compositor *compositor) +{ + Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0; + M_PlaneSensor2D *ps = (M_PlaneSensor2D *)sh->sensor; + PS2DStack *stack = (PS2DStack *) gf_node_get_private(sh->sensor); + + + if (ps->isActive && + (!ps->enabled + || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) + || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER))) ) + ) ) { + if (ps->autoOffset) { + ps->offset = ps->translation_changed; + gf_node_event_out_str(sh->sensor, "offset"); + } + ps->isActive = 0; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 0; + } else if (is_mouse) { + if (!ps->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) { + gf_mx_copy(stack->initial_matrix, compositor->hit_local_to_world); + stack->start_drag.x = compositor->hit_local_point.x - ps->offset.x; + stack->start_drag.y = compositor->hit_local_point.y - ps->offset.y; + ps->isActive = 1; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 1; + } else if (ps->isActive) { + GF_Ray loc_ray; + SFVec3f res; + loc_ray = compositor->hit_world_ray; + gf_mx_apply_ray(&stack->initial_matrix, &loc_ray); + + compositor_get_2d_plane_intersection(&loc_ray, &res); + ps->trackPoint_changed.x = res.x; + ps->trackPoint_changed.y = res.y; + gf_node_event_out_str(sh->sensor, "trackPoint_changed"); + + res.x -= stack->start_drag.x; + res.y -= stack->start_drag.y; + /*clip*/ + if (ps->minPosition.x <= ps->maxPosition.x) { + if (res.x < ps->minPosition.x) res.x = ps->minPosition.x; + if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x; + } + if (ps->minPosition.y <= ps->maxPosition.y) { + if (res.y < ps->minPosition.y) res.y = ps->minPosition.y; + if (res.y > ps->maxPosition.y) res.y = ps->maxPosition.y; + } + ps->translation_changed.x = res.x; + ps->translation_changed.y = res.y; + gf_node_event_out_str(sh->sensor, "translation_changed"); + } + } else { + if (!ps->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) { + ps->isActive = 1; + stack->start_drag = ps->offset; + gf_node_event_out_str(sh->sensor, "isActive"); + } + else if (ps->isActive && (ev->type==GF_EVENT_KEYDOWN)) { + SFVec2f res; + Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? 5*FIX_ONE : FIX_ONE; + if (!gf_sg_use_pixel_metrics(gf_node_get_graph(sh->sensor))) + diff = gf_divfix(diff, compositor->vp_width/2); + res = stack->start_drag; + switch (ev->key.key_code) { + case GF_KEY_LEFT: res.x += -diff; break; + case GF_KEY_RIGHT: res.x += diff; break; + case GF_KEY_UP: res.y += diff; break; + case GF_KEY_DOWN: res.y += -diff; break; + case GF_KEY_HOME: res = ps->offset; break; + default: return; + } + /*clip*/ + if (ps->minPosition.x <= ps->maxPosition.x) { + if (res.x < ps->minPosition.x) res.x = ps->minPosition.x; + if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x; + } + if (ps->minPosition.y <= ps->maxPosition.y) { + if (res.y < ps->minPosition.y) res.y = ps->minPosition.y; + if (res.y > ps->maxPosition.y) res.y = ps->maxPosition.y; + } + stack->start_drag = res; + ps->translation_changed = res; + gf_node_event_out_str(sh->sensor, "translation_changed"); + } + } +} + +static GF_SensorHandler *plane_sensor2d_get_handler(GF_Node *n) +{ + PS2DStack *st = (PS2DStack *)gf_node_get_private(n); + return &st->hdl; +} + +void compositor_init_plane_sensor2d(GF_Compositor *compositor, GF_Node *node) +{ + PS2DStack *st; + GF_SAFEALLOC(st, PS2DStack); + + st->hdl.IsEnabled = ps2D_is_enabled; + st->hdl.OnUserEvent = OnPlaneSensor2D; + st->hdl.sensor = node; + st->compositor = compositor; + mpeg4_sensor_created(compositor, node); + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroyPlaneSensor2D); +} + + +typedef struct +{ + Double last_time; + GF_Compositor *compositor; + GF_SensorHandler hdl; +} Prox2DStack; + +static void DestroyProximitySensor2D(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + Prox2DStack *st = (Prox2DStack *) gf_node_get_private(node); + mpeg4_sensor_deleted(node, &st->hdl); + free(st); + } +} + +static Bool prox2D_is_enabled(GF_Node *n) +{ + return ((M_ProximitySensor2D *) n)->enabled; +} + +static Bool prox2D_is_in_sensor(Prox2DStack *st, M_ProximitySensor2D *ps, Fixed X, Fixed Y) +{ + if (X < ps->center.x - ps->size.x/2) return 0; + if (X > ps->center.x + ps->size.x/2) return 0; + if (Y < ps->center.y - ps->size.y/2) return 0; + if (Y > ps->center.y + ps->size.y/2) return 0; + return 1; +} + +static void OnProximitySensor2D(GF_SensorHandler *sh, Bool is_over, GF_Event *ev, GF_Compositor *compositor) +{ + M_ProximitySensor2D *ps = (M_ProximitySensor2D *)sh->sensor; + Prox2DStack *stack = (Prox2DStack *) gf_node_get_private(sh->sensor); + + assert(ps->enabled); + + if (is_over) { + stack->last_time = gf_node_get_scene_time(sh->sensor); + if (prox2D_is_in_sensor(stack, ps, compositor->hit_local_point.x, compositor->hit_local_point.y)) { + ps->position_changed.x = compositor->hit_local_point.x; + ps->position_changed.y = compositor->hit_local_point.y; + gf_node_event_out_str(sh->sensor, "position_changed"); + + if (!ps->isActive) { + ps->isActive = 1; + gf_node_event_out_str(sh->sensor, "isActive"); + ps->enterTime = stack->last_time; + gf_node_event_out_str(sh->sensor, "enterTime"); + } + return; + } + } + /*either we're not over the shape or we're not in sensor*/ + if (ps->isActive) { + ps->exitTime = stack->last_time; + gf_node_event_out_str(sh->sensor, "exitTime"); + ps->isActive = 0; + gf_node_event_out_str(sh->sensor, "isActive"); + } +} + +static GF_SensorHandler *proximity_sensor2d_get_handler(GF_Node *n) +{ + Prox2DStack *st = (Prox2DStack *)gf_node_get_private(n); + return &st->hdl; +} + + +void compositor_init_proximity_sensor2d(GF_Compositor *compositor, GF_Node *node) +{ + Prox2DStack *st; + GF_SAFEALLOC(st, Prox2DStack); + + st->hdl.IsEnabled = prox2D_is_enabled; + st->hdl.OnUserEvent = OnProximitySensor2D; + st->hdl.sensor = node; + st->compositor = compositor; + mpeg4_sensor_created(compositor, node); + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroyProximitySensor2D); +} + +typedef struct +{ + GF_SensorHandler hdl; + Bool mouse_down; + GF_Compositor *compositor; +} TouchSensorStack; + +static void DestroyTouchSensor(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + TouchSensorStack *st = (TouchSensorStack *) gf_node_get_private(node); + mpeg4_sensor_deleted(node, &st->hdl); + free(st); + } +} + +static Bool ts_is_enabled(GF_Node *n) +{ + return ((M_TouchSensor *) n)->enabled; +} + +static void OnTouchSensor(GF_SensorHandler *sh, Bool is_over, GF_Event *ev, GF_Compositor *compositor) +{ + Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL); + M_TouchSensor *ts = (M_TouchSensor *)sh->sensor; + + /*this is not specified in VRML, however we consider that a de-enabled sensor will not sent deactivation events*/ + if (!ts->enabled) { + if (ts->isActive) compositor->grabbed_sensor = 0; + return; + } + + /*isActive becomes false, send touch time*/ + if (ts->isActive) { + if ( + /*mouse*/ ((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT) ) + || /*keyboard*/ ((ev->type==GF_EVENT_KEYUP) && (ev->key.key_code==GF_KEY_ENTER) ) + ) { + ts->touchTime = gf_node_get_scene_time(sh->sensor); + gf_node_event_out_str(sh->sensor, "touchTime"); + ts->isActive = 0; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 0; + } + } + if (is_over != ts->isOver) { + ts->isOver = is_over; + gf_node_event_out_str(sh->sensor, "isOver"); + } + if (!ts->isActive && is_over) { + if (/*mouse*/ ((ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) + || /*keyboard*/ ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) + ) { + ts->isActive = 1; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 1; + } + } + if (is_over && is_mouse) { + /*THIS IS NOT CONFORMANT, the hitpoint should be in the touchsensor coordinate system, eg we + should store the matrix from TS -> shape and apply that ...*/ + ts->hitPoint_changed = compositor->hit_local_point; + gf_node_event_out_str(sh->sensor, "hitPoint_changed"); + ts->hitNormal_changed = compositor->hit_normal; + gf_node_event_out_str(sh->sensor, "hitNormal_changed"); + ts->hitTexCoord_changed = compositor->hit_texcoords; + gf_node_event_out_str(sh->sensor, "hitTexCoord_changed"); + } +} + +static GF_SensorHandler *touch_sensor_get_handler(GF_Node *n) +{ + TouchSensorStack *ts = (TouchSensorStack *)gf_node_get_private(n); + return &ts->hdl; +} + + +void compositor_init_touch_sensor(GF_Compositor *compositor, GF_Node *node) +{ + TouchSensorStack *st; + GF_SAFEALLOC(st, TouchSensorStack); + + st->hdl.IsEnabled = ts_is_enabled; + st->hdl.OnUserEvent = OnTouchSensor; + st->hdl.sensor = node; + st->compositor = compositor; + mpeg4_sensor_created(compositor, node); + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroyTouchSensor); +} + +#ifndef GPAC_DISABLE_3D + +void TraverseProximitySensor(GF_Node *node, void *rs, Bool is_destroy) +{ + SFVec3f user_pos, dist, up; + SFRotation ori; + GF_Matrix mx; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + M_ProximitySensor *ps = (M_ProximitySensor *)node; + if (is_destroy) return; + + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + /*work with twice bigger bbox to get sure we're notify when culled out*/ + gf_vec_add(tr_state->bbox.max_edge, ps->center, ps->size); + gf_vec_diff(tr_state->bbox.min_edge, ps->center, ps->size); + gf_bbox_refresh(&tr_state->bbox); + return; + } else if (!ps->enabled || (tr_state->traversing_mode != TRAVERSE_SORT) ) return; + + /*TODO FIXME - find a way to cache inverted matrix*/ + gf_mx_copy(mx, tr_state->model_matrix); + gf_mx_inverse(&mx); + /*get use pos in local coord system*/ + user_pos = tr_state->camera->position; + gf_mx_apply_vec(&mx, &user_pos); + gf_vec_diff(dist, user_pos, ps->center); + + if (dist.x<0) dist.x *= -1; + if (dist.y<0) dist.y *= -1; + if (dist.z<0) dist.z *= -1; + + if ((2*dist.x <= ps->size.x) + && (2*dist.y <= ps->size.y) + && (2*dist.z <= ps->size.z) ) { + + if (!ps->isActive) { + ps->isActive = 1; + gf_node_event_out_str(node, "isActive"); + ps->enterTime = gf_node_get_scene_time(node); + gf_node_event_out_str(node, "enterTime"); + } + if ((ps->position_changed.x != user_pos.x) + || (ps->position_changed.y != user_pos.y) + || (ps->position_changed.z != user_pos.z) ) + { + ps->position_changed = user_pos; + gf_node_event_out_str(node, "position_changed"); + } + dist = tr_state->camera->target; + gf_mx_apply_vec(&mx, &dist); + up = tr_state->camera->up; + gf_mx_apply_vec(&mx, &up); + ori = camera_get_orientation(user_pos, dist, tr_state->camera->up); + if ((ori.q != ps->orientation_changed.q) + || (ori.x != ps->orientation_changed.x) + || (ori.y != ps->orientation_changed.y) + || (ori.z != ps->orientation_changed.z) ) { + ps->orientation_changed = ori; + gf_node_event_out_str(node, "orientation_changed"); + } + } else if (ps->isActive) { + ps->isActive = 0; + gf_node_event_out_str(node, "isActive"); + ps->exitTime = gf_node_get_scene_time(node); + gf_node_event_out_str(node, "exitTime"); + } +} + +void compositor_init_proximity_sensor(GF_Compositor *compositor, GF_Node *node) +{ + gf_node_set_callback_function(node, TraverseProximitySensor); +} + + +typedef struct +{ + SFVec3f start_drag; + GF_Plane tracker; + GF_Matrix initial_matrix; + GF_Compositor *compositor; + GF_SensorHandler hdl; +} PSStack; + +static void DestroyPlaneSensor(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + PSStack *st = (PSStack *) gf_node_get_private(node); + mpeg4_sensor_deleted(node, &st->hdl); + free(st); + } +} + +static Bool ps_is_enabled(GF_Node *n) +{ + M_PlaneSensor *ps = (M_PlaneSensor *)n; + return (ps->enabled || ps->isActive); +} + +static void OnPlaneSensor(GF_SensorHandler *sh, Bool is_over, GF_Event *ev, GF_Compositor *compositor) +{ + Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0; + M_PlaneSensor *ps = (M_PlaneSensor *)sh->sensor; + PSStack *stack = (PSStack *) gf_node_get_private(sh->sensor); + + + if (ps->isActive && + ( /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) + || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER))) ) + ) ) { + if (ps->autoOffset) { + ps->offset = ps->translation_changed; + gf_node_event_out_str(sh->sensor, "offset"); + } + ps->isActive = 0; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 0; + } + /*mouse*/ + else if (is_mouse) { + if (!ps->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT) ) { + gf_mx_copy(stack->initial_matrix, compositor->hit_local_to_world); + gf_vec_diff(stack->start_drag, compositor->hit_local_point, ps->offset); + stack->tracker.normal.x = stack->tracker.normal.y = 0; stack->tracker.normal.z = FIX_ONE; + stack->tracker.d = - gf_vec_dot(stack->start_drag, stack->tracker.normal); + ps->isActive = 1; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 1; + } + else if (ps->isActive) { + GF_Ray loc_ray; + SFVec3f res; + loc_ray = compositor->hit_world_ray; + gf_mx_apply_ray(&stack->initial_matrix, &loc_ray); + gf_plane_intersect_line(&stack->tracker, &loc_ray.orig, &loc_ray.dir, &res); + ps->trackPoint_changed = res; + gf_node_event_out_str(sh->sensor, "trackPoint_changed"); + + gf_vec_diff(res, res, stack->start_drag); + /*clip*/ + if (ps->minPosition.x <= ps->maxPosition.x) { + if (res.x < ps->minPosition.x) res.x = ps->minPosition.x; + if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x; + } + if (ps->minPosition.y <= ps->maxPosition.y) { + if (res.y < ps->minPosition.y) res.y = ps->minPosition.y; + if (res.y > ps->maxPosition.y) res.y = ps->maxPosition.y; + } + ps->translation_changed = res; + gf_node_event_out_str(sh->sensor, "translation_changed"); + } + } else { + if (!ps->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) { + ps->isActive = 1; + stack->start_drag = ps->offset; + gf_node_event_out_str(sh->sensor, "isActive"); + } + else if (ps->isActive && (ev->type==GF_EVENT_KEYDOWN)) { + SFVec3f res; + Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? 5*FIX_ONE : FIX_ONE; + if (!gf_sg_use_pixel_metrics(gf_node_get_graph(sh->sensor))) + diff = gf_divfix(diff, compositor->vp_width/2); + + res = stack->start_drag; + switch (ev->key.key_code) { + case GF_KEY_LEFT: res.x -= diff; break; + case GF_KEY_RIGHT: res.x += diff; break; + case GF_KEY_UP: res.y += diff; break; + case GF_KEY_DOWN: res.y -= diff; break; + case GF_KEY_HOME: res = ps->offset; break; + default: return; + } + /*clip*/ + if (ps->minPosition.x <= ps->maxPosition.x) { + if (res.x < ps->minPosition.x) res.x = ps->minPosition.x; + if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x; + } + if (ps->minPosition.y <= ps->maxPosition.y) { + if (res.y < ps->minPosition.y) res.y = ps->minPosition.y; + if (res.y > ps->maxPosition.y) res.y = ps->maxPosition.y; + } + stack->start_drag = res; + ps->translation_changed = res; + gf_node_event_out_str(sh->sensor, "translation_changed"); + } + } +} + +static GF_SensorHandler *plane_sensor_get_handler(GF_Node *n) +{ + PSStack *st = (PSStack *)gf_node_get_private(n); + return &st->hdl; +} + +void compositor_init_plane_sensor(GF_Compositor *compositor, GF_Node *node) +{ + PSStack *st; + GF_SAFEALLOC(st, PSStack); + + st->hdl.IsEnabled = ps_is_enabled; + st->hdl.OnUserEvent = OnPlaneSensor; + st->hdl.sensor = node; + st->compositor = compositor; + mpeg4_sensor_created(compositor, node); + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroyPlaneSensor); +} + +typedef struct +{ + GF_SensorHandler hdl; + GF_Compositor *compositor; + GF_Matrix init_matrix; + Bool disk_mode; + SFVec3f grab_start; + GF_Plane yplane, zplane, xplane; +} CylinderSensorStack; + +static void DestroyCylinderSensor(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + CylinderSensorStack *st = (CylinderSensorStack *) gf_node_get_private(node); + mpeg4_sensor_deleted(node, &st->hdl); + free(st); + } +} + +static Bool cs_is_enabled(GF_Node *n) +{ + M_CylinderSensor *cs = (M_CylinderSensor *)n; + return (cs->enabled || cs->isActive); +} + +static void OnCylinderSensor(GF_SensorHandler *sh, Bool is_over, GF_Event *ev, GF_Compositor *compositor) +{ + Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0; + M_CylinderSensor *cs = (M_CylinderSensor *)sh->sensor; + CylinderSensorStack *st = (CylinderSensorStack *) gf_node_get_private(sh->sensor); + + if (cs->isActive && (!cs->enabled + || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) + || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)))) + ) ) { + if (cs->autoOffset) { + cs->offset = cs->rotation_changed.q; + gf_node_event_out_str(sh->sensor, "offset"); + } + cs->isActive = 0; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 0; + } + else if (is_mouse) { + if (!cs->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) { + GF_Ray r; + SFVec3f yaxis; + Fixed acute, reva; + SFVec3f bearing; + + gf_mx_copy(st->init_matrix, compositor->hit_world_to_local); + /*get initial angle & check disk mode*/ + r = compositor->hit_world_ray; + gf_vec_add(r.dir, r.orig, r.dir); + gf_mx_apply_vec(&compositor->hit_world_to_local, &r.orig); + gf_mx_apply_vec(&compositor->hit_world_to_local, &r.dir); + gf_vec_diff(bearing, r.orig, r.dir); + gf_vec_norm(&bearing); + yaxis.x = yaxis.z = 0; + yaxis.y = FIX_ONE; + acute = gf_vec_dot(bearing, yaxis); + if (acute < -FIX_ONE) acute = -FIX_ONE; + else if (acute > FIX_ONE) acute = FIX_ONE; + acute = gf_acos(acute); + reva = ABS(GF_PI - acute); + if (revadisk_mode = (acute < cs->diskAngle) ? 1 : 0; + + st->grab_start = compositor->hit_local_point; + /*cos we're lazy*/ + st->yplane.d = 0; st->yplane.normal.x = st->yplane.normal.z = st->yplane.normal.y = 0; + st->zplane = st->xplane = st->yplane; + st->xplane.normal.x = FIX_ONE; + st->yplane.normal.y = FIX_ONE; + st->zplane.normal.z = FIX_ONE; + + cs->rotation_changed.x = 0; + cs->rotation_changed.y = FIX_ONE; + cs->rotation_changed.z = 0; + + cs->isActive = 1; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 1; + } + else if (cs->isActive) { + GF_Ray r; + Fixed radius, rot; + SFVec3f dir1, dir2, cx; + + if (is_over) { + cs->trackPoint_changed = compositor->hit_local_point; + gf_node_event_out_str(sh->sensor, "trackPoint_changed"); + } else { + GF_Plane project_to; + r = compositor->hit_world_ray; + gf_mx_apply_ray(&st->init_matrix, &r); + + /*no intersection, use intersection with "main" fronting plane*/ + if ( ABS(r.dir.z) > ABS(r.dir.y)) { + if (ABS(r.dir.x) > ABS(r.dir.x)) project_to = st->xplane; + else project_to = st->zplane; + } else project_to = st->yplane; + if (!gf_plane_intersect_line(&project_to, &r.orig, &r.dir, &compositor->hit_local_point)) return; + } + + dir1.x = compositor->hit_local_point.x; dir1.y = 0; dir1.z = compositor->hit_local_point.z; + if (st->disk_mode) { + radius = FIX_ONE; + } else { + radius = gf_vec_len(dir1); + } + gf_vec_norm(&dir1); + dir2.x = st->grab_start.x; dir2.y = 0; dir2.z = st->grab_start.z; + gf_vec_norm(&dir2); + cx = gf_vec_cross(dir2, dir1); + gf_vec_norm(&cx); + if (gf_vec_len(cx)autoOffset) rot += cs->offset; + + if (cs->minAngle < cs->maxAngle) { + if (rot < cs->minAngle) rot = cs->minAngle; + else if (rot > cs->maxAngle) rot = cs->maxAngle; + } + cs->rotation_changed.q = rot; + gf_node_event_out_str(sh->sensor, "rotation_changed"); + } + } else { + if (!cs->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) { + cs->isActive = 1; + cs->rotation_changed.q = cs->offset; + cs->rotation_changed.x = cs->rotation_changed.z = 0; + cs->rotation_changed.y = FIX_ONE; + gf_node_event_out_str(sh->sensor, "isActive"); + } + else if (cs->isActive && (ev->type==GF_EVENT_KEYDOWN)) { + SFFloat res; + Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? GF_PI/8 : GF_PI/64; + + res = cs->rotation_changed.q; + switch (ev->key.key_code) { + case GF_KEY_LEFT: res -= diff; break; + case GF_KEY_RIGHT: res += diff; break; + case GF_KEY_HOME: res = cs->offset; break; + default: return; + } + /*clip*/ + if (cs->minAngle <= cs->maxAngle) { + if (res < cs->minAngle) res = cs->minAngle; + if (res > cs->maxAngle) res = cs->maxAngle; + } + cs->rotation_changed.q = res; + gf_node_event_out_str(sh->sensor, "rotation_changed"); + } + } +} + +static GF_SensorHandler *cylinder_sensor_get_handler(GF_Node *n) +{ + CylinderSensorStack *st = (CylinderSensorStack *)gf_node_get_private(n); + return &st->hdl; +} + +void compositor_init_cylinder_sensor(GF_Compositor *compositor, GF_Node *node) +{ + CylinderSensorStack *st; + GF_SAFEALLOC(st, CylinderSensorStack); + + st->hdl.IsEnabled = cs_is_enabled; + st->hdl.OnUserEvent = OnCylinderSensor; + st->hdl.sensor = node; + st->compositor = compositor; + mpeg4_sensor_created(compositor, node); + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroyCylinderSensor); +} + + +typedef struct +{ + GF_SensorHandler hdl; + GF_Compositor *compositor; + Fixed radius; + /*center in world coords */ + SFVec3f grab_vec, center; +} SphereSensorStack; + +static void DestroySphereSensor(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + SphereSensorStack *st = (SphereSensorStack *) gf_node_get_private(node); + mpeg4_sensor_deleted(node, &st->hdl); + free(st); + } +} + +static Bool sphere_is_enabled(GF_Node *n) +{ + M_SphereSensor *ss = (M_SphereSensor *)n; + return (ss->enabled || ss->isActive); +} + +static void OnSphereSensor(GF_SensorHandler *sh, Bool is_over, GF_Event *ev, GF_Compositor *compositor) +{ + Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0; + M_SphereSensor *sphere = (M_SphereSensor *)sh->sensor; + SphereSensorStack *st = (SphereSensorStack *) gf_node_get_private(sh->sensor); + + + if (sphere->isActive && (!sphere->enabled + || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT)) + || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)))) + ) ) { + if (sphere->autoOffset) { + sphere->offset = sphere->rotation_changed; + gf_node_event_out_str(sh->sensor, "offset"); + } + sphere->isActive = 0; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 0; + } + else if (is_mouse) { + if (!sphere->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) { + st->center.x = st->center.y = st->center.z = 0; + gf_mx_apply_vec(&compositor->hit_local_to_world, &st->center); + st->radius = gf_vec_len(compositor->hit_local_point); + if (!st->radius) st->radius = FIX_ONE; + st->grab_vec = gf_vec_scale(compositor->hit_local_point, gf_invfix(st->radius)); + + sphere->isActive = 1; + gf_node_event_out_str(sh->sensor, "isActive"); + compositor->grabbed_sensor = 1; + } + else if (sphere->isActive) { + SFVec3f vec, axis; + SFVec4f q1, q2; + SFRotation r; + Fixed cl; + if (is_over) { + sphere->trackPoint_changed = compositor->hit_local_point; + gf_node_event_out_str(sh->sensor, "trackPoint_changed"); + } else { + GF_Ray r; + r = compositor->hit_world_ray; + gf_mx_apply_ray(&compositor->hit_world_to_local, &r); + if (!gf_ray_hit_sphere(&r, NULL, st->radius, &compositor->hit_local_point)) { + vec.x = vec.y = vec.z = 0; + /*doesn't work properly...*/ + compositor->hit_local_point = gf_closest_point_to_line(r.orig, r.dir, vec); + } + } + + vec = gf_vec_scale(compositor->hit_local_point, gf_invfix(st->radius)); + axis = gf_vec_cross(st->grab_vec, vec); + cl = gf_vec_len(axis); + + if (cl < -FIX_ONE) cl = -FIX_ONE; + else if (cl > FIX_ONE) cl = FIX_ONE; + r.q = gf_asin(cl); + if (gf_vec_dot(st->grab_vec, vec) < 0) r.q += GF_PI / 2; + + gf_vec_norm(&axis); + r.x = axis.x; r.y = axis.y; r.z = axis.z; + q1 = gf_quat_from_rotation(r); + if (sphere->autoOffset) { + q2 = gf_quat_from_rotation(sphere->offset); + q1 = gf_quat_multiply(&q1, &q2); + } + sphere->rotation_changed = gf_quat_to_rotation(&q1); + gf_node_event_out_str(sh->sensor, "rotation_changed"); + } + } else { + if (!sphere->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) { + sphere->isActive = 1; + sphere->rotation_changed = sphere->offset; + gf_node_event_out_str(sh->sensor, "isActive"); + } + else if (sphere->isActive && (ev->type==GF_EVENT_KEYDOWN)) { + SFVec4f res, rot; + Fixed diff = GF_PI/64; + + res = sphere->rotation_changed; + switch (ev->key.key_code) { + case GF_KEY_LEFT: + diff = -diff; + case GF_KEY_RIGHT: + rot.x = 0; rot.y = FIX_ONE; rot.z = 0; rot.q = diff; + res = gf_quat_from_rotation(res); + rot = gf_quat_from_rotation(rot); + rot = gf_quat_multiply(&rot, &res); + res = gf_quat_to_rotation(&rot); + break; + case GF_KEY_DOWN: + diff = -diff; + case GF_KEY_UP: + if (ev->key.flags & GF_KEY_MOD_SHIFT) { + rot.x = 0; rot.z = FIX_ONE; + } else { + rot.x = FIX_ONE; rot.z = 0; + } + rot.y = 0; rot.q = diff; + res = gf_quat_from_rotation(res); + rot = gf_quat_from_rotation(rot); + rot = gf_quat_multiply(&rot, &res); + res = gf_quat_to_rotation(&rot); + break; + case GF_KEY_HOME: res = sphere->offset; break; + default: return; + } + sphere->rotation_changed = res; + gf_node_event_out_str(sh->sensor, "rotation_changed"); + } + } + +} + +static GF_SensorHandler *sphere_get_handler(GF_Node *n) +{ + SphereSensorStack *st = (SphereSensorStack *)gf_node_get_private(n); + return &st->hdl; +} + +void compositor_init_sphere_sensor(GF_Compositor *compositor, GF_Node *node) +{ + SphereSensorStack *st; + GF_SAFEALLOC(st, SphereSensorStack); + + st->hdl.IsEnabled = sphere_is_enabled; + st->hdl.OnUserEvent = OnSphereSensor; + st->hdl.sensor = node; + st->compositor = compositor; + mpeg4_sensor_created(compositor, node); + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroySphereSensor); +} + +void TraverseVisibilitySensor(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + M_VisibilitySensor *vs = (M_VisibilitySensor *)node; + + if (is_destroy || !vs->enabled) return; + + if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + /*work with twice bigger bbox to get sure we're notify when culled out*/ + gf_vec_add(tr_state->bbox.max_edge, vs->center, vs->size); + gf_vec_diff(tr_state->bbox.min_edge, vs->center, vs->size); + gf_bbox_refresh(&tr_state->bbox); + + } else if (tr_state->traversing_mode==TRAVERSE_SORT) { + Bool visible; + u32 cull_flag; + GF_BBox bbox; + SFVec3f s; + s = gf_vec_scale(vs->size, FIX_ONE/2); + /*cull with normal bbox*/ + gf_vec_add(bbox.max_edge, vs->center, s); + gf_vec_diff(bbox.min_edge, vs->center, s); + gf_bbox_refresh(&bbox); + cull_flag = tr_state->cull_flag; + tr_state->cull_flag = CULL_INTERSECTS; + visible = visual_3d_node_cull(tr_state, &bbox, 0); + tr_state->cull_flag = cull_flag; + + if (visible && !vs->isActive) { + vs->isActive = 1; + gf_node_event_out_str(node, "isActive"); + vs->enterTime = gf_node_get_scene_time(node); + gf_node_event_out_str(node, "enterTime"); + } + else if (!visible && vs->isActive) { + vs->isActive = 0; + gf_node_event_out_str(node, "isActive"); + vs->exitTime = gf_node_get_scene_time(node); + gf_node_event_out_str(node, "exitTime"); + } + } +} + +void compositor_init_visibility_sensor(GF_Compositor *compositor, GF_Node *node) +{ + gf_node_set_callback_function(node, TraverseVisibilitySensor); +} + +#endif + +GF_SensorHandler *compositor_mpeg4_get_sensor_handler(GF_Node *n) +{ + GF_SensorHandler *hs; + + switch (gf_node_get_tag(n)) { + /*anchor is not considered as a child sensor node when picking sensors*/ + case TAG_MPEG4_Anchor: + case TAG_X3D_Anchor: + hs = gf_sc_anchor_get_handler(n); + break; + case TAG_MPEG4_DiscSensor: + hs = disc_sensor_get_handler(n); + break; + case TAG_MPEG4_PlaneSensor2D: + hs = plane_sensor2d_get_handler(n); + break; + case TAG_MPEG4_ProximitySensor2D: + hs = proximity_sensor2d_get_handler(n); + break; + case TAG_MPEG4_TouchSensor: + case TAG_X3D_TouchSensor: + hs = touch_sensor_get_handler(n); + break; +#ifndef GPAC_DISABLE_3D + case TAG_MPEG4_CylinderSensor: + case TAG_X3D_CylinderSensor: + hs = cylinder_sensor_get_handler(n); + break; + case TAG_MPEG4_PlaneSensor: + case TAG_X3D_PlaneSensor: + hs = plane_sensor_get_handler(n); + break; + case TAG_MPEG4_SphereSensor: + case TAG_X3D_SphereSensor: + hs = sphere_get_handler(n); + break; +#endif + default: return NULL; + } + if (hs && hs->IsEnabled(n)) return hs; + return NULL; +} + +Bool compositor_mpeg4_is_sensor_node(GF_Node *node) +{ + GF_SensorHandler *sh = compositor_mpeg4_get_sensor_handler(node); + if (sh && sh->IsEnabled(node)) return 1; + return 0; +} diff --git a/src/compositor/mpeg4_sound.c b/src/compositor/mpeg4_sound.c new file mode 100644 index 0000000..24d4202 --- /dev/null +++ b/src/compositor/mpeg4_sound.c @@ -0,0 +1,283 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "visual_manager.h" + +typedef struct +{ + GF_SoundInterface snd_ifce; + SFVec3f pos; +} Sound2DStack; + +/*sound2D wraper - spacialization is not supported yet*/ +static void TraverseSound2D(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_TraverseState *tr_state = (GF_TraverseState*) rs; + M_Sound2D *snd = (M_Sound2D *)node; + Sound2DStack *st = (Sound2DStack *)gf_node_get_private(node); + + if (is_destroy) { + free(st); + return; + } + if (!snd->source) return; + + /*this implies no DEF/USE for real location...*/ + st->pos.x = snd->location.x; + st->pos.y = snd->location.y; + st->pos.z = 0; +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) + gf_mx_apply_vec(&tr_state->model_matrix, &st->pos); + else +#endif + gf_mx2d_apply_coords(&tr_state->transform, &st->pos.x, &st->pos.y); + + + tr_state->sound_holder = &st->snd_ifce; + gf_node_traverse((GF_Node *) snd->source, tr_state); + tr_state->sound_holder = NULL; + /*never cull Sound2d*/ + tr_state->disable_cull = 1; +} +static Bool SND2D_GetChannelVolume(GF_Node *node, Fixed *vol) +{ + Fixed volume = ((M_Sound2D *)node)->intensity; + vol[0] = vol[1] = vol[2] = vol[3] = vol[4] = vol[5] = volume; + return (volume==FIX_ONE) ? 0 : 1; +} + +static u8 SND2D_GetPriority(GF_Node *node) +{ + return 255; +} + +void compositor_init_sound2d(GF_Compositor *compositor, GF_Node *node) +{ + Sound2DStack *snd; + GF_SAFEALLOC(snd, Sound2DStack); + snd->snd_ifce.GetPriority = SND2D_GetPriority; + snd->snd_ifce.GetChannelVolume = SND2D_GetChannelVolume; + snd->snd_ifce.owner = node; + gf_node_set_private(node, snd); + gf_node_set_callback_function(node, TraverseSound2D); +} + +#ifndef GPAC_DISABLE_3D + +static Fixed snd_compute_gain(Fixed min_b, Fixed min_f, Fixed max_b, Fixed max_f, SFVec3f pos) +{ + Fixed sqpos_x, sqpos_z; + Fixed y_pos, x_pos, dist_ellip, viewp_dist, dist_from_foci_min, dist_from_foci_max, d_min, d_max, sqb_min, sqb_max; + Fixed a_in = (min_f+min_b)/2; + Fixed b_in = gf_sqrt(gf_mulfix(min_b, min_f)); + Fixed alpha_min = (min_f-min_b)/2; + Fixed dist_foci_min = (min_f-min_b); + Fixed a_out = (max_f+max_b)/2; //first ellipse axis + Fixed b_out = gf_sqrt(gf_mulfix(max_b, max_f)); + Fixed alpha_max = (max_f-max_b)/2; //origo from focus + Fixed dist_foci_max = (max_f-max_b); + Fixed x_min = 0; + Fixed x_max = 0; + Fixed y_min = 0; + Fixed y_max = 0; + Fixed k = (ABS(pos.z) >= FIX_EPSILON) ? gf_divfix(pos.x, pos.z) : 0; + + sqpos_x = gf_mulfix(pos.x, pos.x); + sqpos_z = gf_mulfix(pos.z, pos.z); + + dist_from_foci_min = gf_sqrt(sqpos_z + sqpos_x) + gf_sqrt( gf_mulfix(pos.z - dist_foci_min, pos.z - dist_foci_min) + sqpos_x); + dist_from_foci_max = gf_sqrt(sqpos_z + sqpos_x) + gf_sqrt( gf_mulfix(pos.z - dist_foci_max, pos.z - dist_foci_max) + sqpos_x); + d_min = min_f+min_b; + d_max = max_f+max_b; + if(dist_from_foci_max > d_max) return 0; + else if (dist_from_foci_min <= d_min) return FIX_ONE; + + sqb_min = gf_mulfix(b_in, b_in); + sqb_max = gf_mulfix(b_out, b_out); + + if (ABS(pos.z) > FIX_ONE/10000) { + s32 sign = (pos.z>0) ? 1 : -1; + Fixed a_in_k_sq, a_out_k_sq; + a_in_k_sq = gf_mulfix(a_in, k); + a_in_k_sq = gf_mulfix(a_in_k_sq, a_in_k_sq); + + x_min = gf_mulfix(alpha_min, sqb_min) + sign*gf_mulfix( gf_mulfix(a_in, b_in), gf_sqrt(a_in_k_sq + sqb_min - gf_mulfix( gf_mulfix(alpha_min, k), gf_mulfix(alpha_min, k)))); + x_min = gf_divfix(x_min, sqb_min + a_in_k_sq); + y_min = gf_mulfix(k, x_min); + + a_out_k_sq = gf_mulfix(a_out, k); + a_out_k_sq = gf_mulfix(a_out_k_sq, a_out_k_sq); + + x_max = gf_mulfix(alpha_max, sqb_max) + sign*gf_mulfix( gf_mulfix(a_out, b_out), gf_sqrt( a_out_k_sq + sqb_max - gf_mulfix( gf_mulfix(alpha_max, k), gf_mulfix(alpha_max, k)))); + x_max = gf_divfix(x_max, sqb_max + a_out_k_sq); + y_max = gf_mulfix(k, x_max); + } else { + x_min = x_max = 0; + y_min = gf_mulfix(b_in, gf_sqrt(FIX_ONE - gf_mulfix( gf_divfix(alpha_min,a_in), gf_divfix(alpha_min,a_in)) ) ); + y_max = gf_mulfix(b_out, gf_sqrt(FIX_ONE - gf_mulfix( gf_divfix(alpha_max,a_out), gf_divfix(alpha_max,a_out)) ) ); + } + + y_pos = gf_sqrt(sqpos_x) - y_min; + x_pos = pos.z - x_min; + x_max -= x_min; + y_max -= y_min; + dist_ellip = gf_sqrt( gf_mulfix(y_max, y_max) + gf_mulfix(x_max, x_max)); + viewp_dist = gf_sqrt( gf_mulfix(y_pos, y_pos) + gf_mulfix(x_pos, x_pos)); + viewp_dist = gf_divfix(viewp_dist, dist_ellip); + + return FLT2FIX ( (Float) pow(10.0,- FIX2FLT(viewp_dist))); +} + + +typedef struct +{ + GF_SoundInterface snd_ifce; + GF_Matrix mx; + SFVec3f last_pos; + Bool identity; + /*local system*/ + Fixed intensity; + Fixed lgain, rgain; +} SoundStack; + +static void TraverseSound(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_TraverseState *tr_state = (GF_TraverseState*) rs; + M_Sound *snd = (M_Sound *)node; + SoundStack *st = (SoundStack *)gf_node_get_private(node); + + if (is_destroy) { + free(st); + return; + } + if (!snd->source) return; + + tr_state->sound_holder = &st->snd_ifce; + + /*forward in case we're switched off*/ + if (tr_state->switched_off) { + gf_node_traverse((GF_Node *) snd->source, tr_state); + } + else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { + /*we can't cull sound since*/ + tr_state->disable_cull = 1; + } else if (tr_state->traversing_mode==TRAVERSE_SORT) { + GF_Matrix mx; + SFVec3f usr, snd_dir, pos; + Fixed mag, ang; + /*this implies no DEF/USE for real location...*/ + gf_mx_copy(st->mx, tr_state->model_matrix); + gf_mx_copy(mx, tr_state->model_matrix); + gf_mx_inverse(&mx); + + snd_dir = snd->direction; + gf_vec_norm(&snd_dir); + + /*get user location*/ + usr = tr_state->camera->position; + gf_mx_apply_vec(&mx, &usr); + + /*recenter to ellipse focal*/ + gf_vec_diff(usr, usr, snd->location); + mag = gf_vec_len(usr); + if (!mag) mag = FIX_ONE/10; + ang = gf_divfix(gf_vec_dot(snd_dir, usr), mag); + + usr.z = gf_mulfix(ang, mag); + usr.x = gf_sqrt(gf_mulfix(mag, mag) - gf_mulfix(usr.z, usr.z)); + usr.y = 0; + if (!gf_vec_equal(usr, st->last_pos)) { + st->intensity = snd_compute_gain(snd->minBack, snd->minFront, snd->maxBack, snd->maxFront, usr); + st->intensity = gf_mulfix(st->intensity, snd->intensity); + st->last_pos = usr; + } + st->identity = (st->intensity==FIX_ONE) ? 1 : 0; + + if (snd->spatialize) { + Fixed ang, sign; + SFVec3f cross; + pos = snd->location; + gf_mx_apply_vec(&tr_state->model_matrix, &pos); + gf_vec_diff(pos, pos, tr_state->camera->position); + gf_vec_diff(usr, tr_state->camera->target, tr_state->camera->position); + gf_vec_norm(&pos); + gf_vec_norm(&usr); + + ang = gf_acos(gf_vec_dot(usr, pos)); + /*get orientation*/ + cross = gf_vec_cross(usr, pos); + sign = gf_vec_dot(cross, tr_state->camera->up); + if (sign>0) ang *= -1; + ang = (FIX_ONE + gf_sin(ang)) / 2; + st->lgain = (FIX_ONE - gf_mulfix(ang, ang)); + st->rgain = FIX_ONE - gf_mulfix(FIX_ONE - ang, FIX_ONE - ang); + /*renorm between 0 and 1*/ + st->lgain = gf_mulfix(st->lgain, 4*st->intensity/3); + st->rgain = gf_mulfix(st->rgain, 4*st->intensity/3); + + if (st->identity && ((st->lgain!=FIX_ONE) || (st->rgain!=FIX_ONE))) st->identity = 0; + } else { + st->lgain = st->rgain = FIX_ONE; + } + gf_node_traverse((GF_Node *) snd->source, tr_state); + } + + tr_state->sound_holder = NULL; +} +static Bool SND_GetChannelVolume(GF_Node *node, Fixed *vol) +{ + M_Sound *snd = (M_Sound *)node; + SoundStack *st = (SoundStack *)gf_node_get_private(node); + + vol[2] = vol[3] = vol[4] = vol[5] = st->intensity; + if (snd->spatialize) { + vol[0] = st->lgain; + vol[1] = st->rgain; + } else { + vol[0] = vol[1] = st->intensity; + } + return !st->identity; +} +static u8 SND_GetPriority(GF_Node *node) +{ + return (u8) ((M_Sound *)node)->priority*255; +} + +void compositor_init_sound(GF_Compositor *compositor, GF_Node *node) +{ + SoundStack *snd; + GF_SAFEALLOC(snd, SoundStack); + snd->snd_ifce.GetChannelVolume = SND_GetChannelVolume; + snd->snd_ifce.GetPriority = SND_GetPriority; + snd->snd_ifce.owner = node; + gf_node_set_private(node, snd); + gf_node_set_callback_function(node, TraverseSound); +} + +#endif + diff --git a/src/compositor/mpeg4_text.c b/src/compositor/mpeg4_text.c new file mode 100644 index 0000000..0b06cdb --- /dev/null +++ b/src/compositor/mpeg4_text.c @@ -0,0 +1,692 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "nodes_stacks.h" +#include "visual_manager.h" +#include "mpeg4_grouping.h" +#include "texturing.h" +#include +#include + +/*default value when no fontStyle*/ +#define FSFAMILY (fs && fs->family.count) ? (const char *)fs->family.vals[0] : "" + +/*here it's tricky since it depends on our metric system...*/ +#define FSSIZE (fs ? fs->size : -1) +#define FSSTYLE (fs && fs->style.buffer) ? (const char *)fs->style.buffer : "" +#define FSMAJOR ( (fs && fs->justify.count && fs->justify.vals[0]) ? (const char *)fs->justify.vals[0] : "FIRST") +#define FSMINOR ( (fs && (fs->justify.count>1) && fs->justify.vals[1]) ? (const char *)fs->justify.vals[1] : "FIRST") +#define FSHORIZ (fs ? fs->horizontal : 1) +#define FSLTR (fs ? fs->leftToRight : 1) +#define FSTTB (fs ? fs->topToBottom : 1) +#define FSLANG (fs ? fs->language : "") +#define FSSPACE (fs ? fs->spacing : 1) + + +/*exported to access the strike list ...*/ +typedef struct +{ + Drawable *graph; + Fixed ascent, descent; + GF_List *spans; + GF_Rect bounds; + u32 texture_text_flag; + GF_Compositor *compositor; +} TextStack; + +void text_clean_paths(GF_Compositor *compositor, TextStack *stack) +{ + GF_TextSpan *span; + /*delete all path objects*/ + while (gf_list_count(stack->spans)) { + span = (GF_TextSpan*) gf_list_get(stack->spans, 0); + gf_list_rem(stack->spans, 0); + gf_font_manager_delete_span(compositor->font_manager, span); + } + stack->bounds.width = stack->bounds.height = 0; + drawable_reset_path(stack->graph); +} + + +static void build_text_split(TextStack *st, M_Text *txt, GF_TraverseState *tr_state) +{ + u32 i, j, k, len, styles, idx, first_char; + Bool split_words = 0; + GF_Font *font; + GF_TextSpan *tspan; + GF_FontManager *ft_mgr = tr_state->visual->compositor->font_manager; + Fixed fontSize, start_y; + M_FontStyle *fs = (M_FontStyle *)txt->fontStyle; + + fontSize = FSSIZE; + if (fontSize <= 0) { + fontSize = INT2FIX(12); + if (!tr_state->pixel_metrics) fontSize = gf_divfix(fontSize, tr_state->visual->compositor->output_width); + } + + styles = 0; + if (fs && fs->style.buffer) { + if (strstr(fs->style.buffer, "BOLD") || strstr(fs->style.buffer, "bold")) styles |= GF_FONT_WEIGHT_BOLD; + if (strstr(fs->style.buffer, "ITALIC") || strstr(fs->style.buffer, "italic")) styles |= GF_FONT_ITALIC; + if (strstr(fs->style.buffer, "UNDERLINED") || strstr(fs->style.buffer, "underlined")) styles |= GF_FONT_UNDERLINED; + } + + font = gf_font_manager_set_font(ft_mgr, fs ? fs->family.vals : NULL, fs ? fs->family.count : 0, styles); + if (!font) return; + + st->ascent = (fontSize*font->ascent) / font->em_size; + st->descent = -(fontSize*font->descent) / font->em_size; + + if (!strcmp(FSMINOR, "MIDDLE")) { + start_y = (st->descent + st->ascent)/2; + } + else if (!strcmp(FSMINOR, "BEGIN")) { + start_y = st->descent; + } + else if (!strcmp(FSMINOR, "END")) { + start_y = st->descent + st->ascent; + } + else { + start_y = st->ascent; + } + + st->bounds.width = st->bounds.x = st->bounds.height = 0; + idx = 0; + split_words = (tr_state->text_split_mode==1) ? 1 : 0; + + for (i=0; i < txt->string.count; i++) { + + char *str = txt->string.vals[i]; + if (!str || !strlen(str)) continue; + + tspan = gf_font_manager_create_span(ft_mgr, font, str, fontSize, 0, 0, 0, NULL, 0, styles, (GF_Node*)txt); + if (!tspan) continue; + + len = tspan->nb_glyphs; + tspan->flags |= GF_TEXT_SPAN_HORIZONTAL; + + first_char = 0; + for (j=0; jglyphs[j]) continue; + + /*we currently only split sentences at spaces*/ + if (tspan->glyphs[j]->utf_name == (unsigned short) ' ') is_space = 1; + if (split_words && (j+1!=len) && !is_space) + continue; + + span = (GF_TextSpan*) malloc(sizeof(GF_TextSpan)); + memcpy(span, tspan, sizeof(GF_TextSpan)); + + span->nb_glyphs = split_words ? (j - first_char) : 1; + if (split_words && !is_space) span->nb_glyphs++; + span->glyphs = malloc(sizeof(void *)*span->nb_glyphs); + + span->bounds.height = st->ascent + st->descent; + span->bounds.y = start_y; + span->bounds.x = 0; + span->bounds.width = 0; + + if (split_words) { + for (k=0; knb_glyphs; k++) { + span->glyphs[k] = tspan->glyphs[FSLTR ? (first_char+k) : (len - first_char - k - 1)]; + span->bounds.width += tspan->font_scale * (span->glyphs[k] ? span->glyphs[k]->horiz_advance : tspan->font->max_advance_h); + } + } else { + span->glyphs[0] = tspan->glyphs[FSLTR ? j : (len - j - 1) ]; + span->glyphs[0] = tspan->glyphs[j]; + span->bounds.width = tspan->font_scale * (span->glyphs[0] ? span->glyphs[0]->horiz_advance : tspan->font->max_advance_h); + } + + gf_list_add(st->spans, span); + + /*request a context (first one is always valid when entering sort phase)*/ + if (idx) parent_node_start_group(tr_state->parent, NULL, 0); + + idx++; + parent_node_end_text_group(tr_state->parent, &span->bounds, st->ascent, st->descent, idx); + + if (is_space && split_words) { + span = (GF_TextSpan*) malloc(sizeof(GF_TextSpan)); + memcpy(span, tspan, sizeof(GF_TextSpan)); + span->nb_glyphs = 1; + span->glyphs = malloc(sizeof(void *)); + + gf_list_add(st->spans, span); + span->bounds.height = st->ascent + st->descent; + span->bounds.y = start_y; + span->bounds.x = 0; + k = (j - first_char); + span->glyphs[0] = tspan->glyphs[FSLTR ? (first_char+k) : (len - first_char - k - 1)]; + span->bounds.width = tspan->font_scale * (span->glyphs[0] ? span->glyphs[0]->horiz_advance : tspan->font->max_advance_h); + parent_node_start_group(tr_state->parent, NULL, 1); + idx++; + parent_node_end_text_group(tr_state->parent, &span->bounds, st->ascent, st->descent, idx); + } + first_char = j+1; + } + gf_font_manager_delete_span(ft_mgr, tspan); + } +} + + +static void build_text(TextStack *st, M_Text *txt, GF_TraverseState *tr_state) +{ + u32 i, j, int_major, k, styles, count; + Fixed fontSize, start_x, start_y, line_spacing, tot_width, tot_height, max_scale, space; + GF_Font *font; + Bool horizontal; + GF_FontManager *ft_mgr = tr_state->visual->compositor->font_manager; + M_FontStyle *fs = (M_FontStyle *)txt->fontStyle; + + fontSize = FSSIZE; + if (fontSize <= 0) { + fontSize = INT2FIX(12); + if (!tr_state->pixel_metrics) fontSize = gf_divfix(fontSize, tr_state->visual->compositor->output_width); + } + horizontal = FSHORIZ; + start_x = start_y = 0; + + styles = 0; + if (fs && fs->style.buffer) { + if (strstr(fs->style.buffer, "BOLD") || strstr(fs->style.buffer, "bold")) styles |= GF_FONT_WEIGHT_BOLD; + if (strstr(fs->style.buffer, "ITALIC") || strstr(fs->style.buffer, "italic")) styles |= GF_FONT_ITALIC; + if (strstr(fs->style.buffer, "UNDERLINED") || strstr(fs->style.buffer, "underlined")) styles |= GF_FONT_UNDERLINED; + } + + font = gf_font_manager_set_font(ft_mgr, fs ? fs->family.vals : NULL, fs ? fs->family.count : 0, styles); + if (!font) return; + + /*NOTA: we could use integer maths here but we have a risk of overflow with large fonts, so use fixed maths*/ + st->ascent = gf_muldiv(fontSize, INT2FIX(font->ascent), INT2FIX(font->em_size)); + st->descent = -gf_muldiv(fontSize, INT2FIX(font->descent), INT2FIX(font->em_size)); + space = gf_muldiv(fontSize, INT2FIX(font->line_spacing), INT2FIX(font->em_size)) ; + line_spacing = gf_mulfix(FSSPACE, fontSize); + + tot_width = tot_height = 0; + for (i=0; i < txt->string.count; i++) { + GF_TextSpan *tspan; + u32 size; + char *str = txt->string.vals[i]; + if (!str) continue; + + tspan = gf_font_manager_create_span(ft_mgr, font, txt->string.vals[i], fontSize, 0, 0, 0, NULL, 0, styles, (GF_Node*)txt); + if (!tspan) continue; + + if (horizontal) tspan->flags |= GF_TEXT_SPAN_HORIZONTAL; + + if ((horizontal && !FSLTR) || (!horizontal && !FSTTB)) { + for (k=0; knb_glyphs/2; k++) { + GF_Glyph *g = tspan->glyphs[k]; + tspan->glyphs[k] = tspan->glyphs[tspan->nb_glyphs-1-k]; + tspan->glyphs[tspan->nb_glyphs-k-1] = g; + } + } + + size = 0; + for (j=0; jnb_glyphs; j++) { + if (horizontal) { + size += tspan->glyphs[j] ? tspan->glyphs[j]->horiz_advance : tspan->font->max_advance_h; + } else { + size += tspan->glyphs[j] ? tspan->glyphs[j]->vert_advance : tspan->font->max_advance_v; + } + } + gf_list_add(st->spans, tspan); + + if (horizontal) { + tspan->bounds.width = tspan->font_scale * size; + /*apply length*/ + if ((txt->length.count>i) && (txt->length.vals[i]>0)) { + tspan->x_scale = gf_divfix(txt->length.vals[i], tspan->bounds.width); + tspan->bounds.width = txt->length.vals[i]; + } + if (tot_width < tspan->bounds.width ) tot_width = tspan->bounds.width; + } else { + tspan->bounds.height = tspan->font_scale * size; + + /*apply length*/ + if ((txt->length.count>i) && (txt->length.vals[i]>0)) { + tspan->y_scale = gf_divfix(txt->length.vals[i], tspan->bounds.height); + tspan->bounds.height = txt->length.vals[i]; + } + if (tot_height < tspan->bounds.height) tot_height = tspan->bounds.height; + } + } + + max_scale = FIX_ONE; + if (horizontal) { + if ((txt->maxExtent > 0) && (tot_width>txt->maxExtent)) { + max_scale = gf_divfix(txt->maxExtent, tot_width); + tot_width = txt->maxExtent; + } + tot_height = (txt->string.count-1) * line_spacing + (st->ascent + st->descent); + st->bounds.height = tot_height; + + if (!strcmp(FSMINOR, "MIDDLE")) { + if (FSTTB) { + start_y = tot_height/2; + st->bounds.y = start_y; + } else { + start_y = st->descent + st->ascent - tot_height/2; + st->bounds.y = tot_height/2; + } + } + else if (!strcmp(FSMINOR, "BEGIN")) { + if (FSTTB) { + start_y = st->descent; + start_y = 0; + st->bounds.y = start_y; + } else { + st->bounds.y = st->bounds.height; + start_y = st->descent + st->ascent; + } + } + else if (!strcmp(FSMINOR, "END")) { + if (FSTTB) { + start_y = tot_height; + st->bounds.y = start_y; + } else { + start_y = -tot_height + 2*st->descent + st->ascent; + st->bounds.y = start_y - (st->descent + st->ascent) + tot_height; + } + } + else { + start_y = st->ascent; + st->bounds.y = FSTTB ? start_y : (tot_height - st->descent); + } + } else { + if ((txt->maxExtent > 0) && (tot_height>txt->maxExtent) ) { + max_scale = gf_divfix(txt->maxExtent, tot_height); + tot_height = txt->maxExtent; + } + tot_width = txt->string.count * line_spacing; + st->bounds.width = tot_width; + + if (!strcmp(FSMINOR, "MIDDLE")) { + if (FSLTR) { + start_x = -tot_width/2; + st->bounds.x = start_x; + } else { + start_x = tot_width/2 - line_spacing; + st->bounds.x = - tot_width + line_spacing; + } + } + else if (!strcmp(FSMINOR, "END")) { + if (FSLTR) { + start_x = -tot_width; + st->bounds.x = start_x; + } else { + start_x = tot_width-line_spacing; + st->bounds.x = 0; + } + } + else { + if (FSLTR) { + start_x = 0; + st->bounds.x = start_x; + } else { + start_x = -line_spacing; + st->bounds.x = -tot_width; + } + } + } + + + /*major-justification*/ + if (!strcmp(FSMAJOR, "MIDDLE") ) { + int_major = 0; + } else if (!strcmp(FSMAJOR, "END") ) { + int_major = 1; + } else { + int_major = 2; + } + + st->bounds.width = st->bounds.height = 0; + + count = gf_list_count(st->spans); + for (i=0; i < count; i++) { + GF_TextSpan *span = gf_list_get(st->spans, i); + switch (int_major) { + /*major-justification MIDDLE*/ + case 0: + if (horizontal) { + start_x = -span->bounds.width/2; + } else { + //start_y = FSTTB ? span->bounds.height/2 : (-span->bounds.height/2 + space); + start_y = span->bounds.height/2; + } + break; + /*major-justification END*/ + case 1: + if (horizontal) { + start_x = (FSLTR) ? -span->bounds.width : 0; + } else { + //start_y = FSTTB ? span->bounds.height : (-span->bounds.height + space); + start_y = FSTTB ? span->bounds.height : 0; + } + break; + /*BEGIN, FIRST or default*/ + default: + if (horizontal) { + start_x = (FSLTR) ? 0 : -span->bounds.width; + } else { + //start_y = FSTTB ? 0 : space; + start_y = FSTTB ? 0 : span->bounds.height; + } + break; + } + span->off_x = start_x; + span->bounds.x = start_x; + if (horizontal) { + span->off_y = start_y - st->ascent; + span->x_scale = gf_mulfix(span->x_scale, max_scale); + span->bounds.y = start_y; + } else { + span->y_scale = gf_mulfix(span->y_scale, max_scale); + span->off_y = start_y - gf_mulfix(st->ascent, span->y_scale); + span->bounds.y = start_y; + } + span->off_x = gf_mulfix(span->off_x, max_scale); + span->off_y = gf_mulfix(span->off_y, max_scale); + + if (horizontal) { + start_y += FSTTB ? -line_spacing : line_spacing; + span->bounds.height = st->descent + st->ascent; + } else { + start_x += FSLTR ? line_spacing : -line_spacing; + span->bounds.width = line_spacing; + } + gf_rect_union(&st->bounds, &span->bounds); + } +} + +static void text_get_draw_opt(GF_Node *node, TextStack *st, Bool *force_texture, u32 *hl_color) +{ + const char *fs_style; + char *hlight; + M_FontStyle *fs = (M_FontStyle *) ((M_Text *) node)->fontStyle; + + *hl_color = 0; + + fs_style = FSSTYLE; + hlight = NULL; + hlight = strstr(fs_style, "HIGHLIGHT"); + if (hlight) hlight = strchr(hlight, '#'); + if (hlight) { + hlight += 1; + if (!strnicmp(hlight, "RV", 2)) *hl_color = 0x00FFFFFF; + else { + sscanf(hlight, "%x", hl_color); + if (strlen(hlight)!=8) *hl_color |= 0xFF000000; + } + } + *force_texture = st->texture_text_flag; + if (strstr(fs_style, "TEXTURED")) *force_texture = 1; +} + + +#ifndef GPAC_DISABLE_3D + + +static Bool text_is_3d_material(GF_TraverseState *tr_state) +{ + GF_Node *__mat; + if (!tr_state->appear) return 0; + __mat = ((M_Appearance *)tr_state->appear)->material; + if (!__mat) return 0; + if (gf_node_get_tag(__mat)==TAG_MPEG4_Material2D) return 0; + return 1; +} + +static void text_draw_3d(GF_TraverseState *tr_state, GF_Node *node, TextStack *st) +{ + DrawAspect2D the_asp, *asp; + Bool is_3d, force_texture; + u32 hl_color; + + is_3d = text_is_3d_material(tr_state); + asp = NULL; + if (!is_3d) { + memset(&the_asp, 0, sizeof(DrawAspect2D)); + asp = &the_asp; + drawable_get_aspect_2d_mpeg4(node, asp, tr_state); + } + text_get_draw_opt(node, st, &force_texture, &hl_color); + gf_font_spans_draw_3d(st->spans, tr_state, asp, hl_color, force_texture); +} + +#endif + + + + +void text_draw_2d(GF_Node *node, GF_TraverseState *tr_state) +{ + Bool force_texture; + u32 hl_color; + TextStack *st = (TextStack *) gf_node_get_private((GF_Node *) node); + + if (!GF_COL_A(tr_state->ctx->aspect.fill_color) && !tr_state->ctx->aspect.pen_props.width) return; + + text_get_draw_opt(node, st, &force_texture, &hl_color); + + tr_state->text_parent = node; + gf_font_spans_draw_2d(st->spans, tr_state, hl_color, force_texture, &st->bounds); + tr_state->text_parent = NULL; +} + + + +static void text_check_changes(GF_Node *node, TextStack *stack, GF_TraverseState *tr_state) +{ + if (gf_node_dirty_get(node) || tr_state->visual->compositor->reset_fonts) { + text_clean_paths(tr_state->visual->compositor, stack); + build_text(stack, (M_Text*)node, tr_state); + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack->graph, tr_state); + } +} + + +static void Text_Traverse(GF_Node *n, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + M_Text *txt = (M_Text *) n; + TextStack *st = (TextStack *) gf_node_get_private(n); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + text_clean_paths(gf_sc_get_compositor(n), st); + drawable_del(st->graph); + gf_list_del(st->spans); + free(st); + return; + } + + if (!txt->string.count) return; + + if (tr_state->text_split_mode) { + gf_node_dirty_clear(n, 0); + text_clean_paths(tr_state->visual->compositor, st); + build_text_split(st, txt, tr_state); + return; + } + + text_check_changes(n, st, tr_state); + + switch (tr_state->traversing_mode) { + case TRAVERSE_DRAW_2D: + text_draw_2d(n, tr_state); + return; +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + text_draw_3d(tr_state, n, st); + return; +#endif + case TRAVERSE_PICK: + tr_state->text_parent = n; + gf_font_spans_pick(n, st->spans, tr_state, &st->bounds, 0, NULL); + tr_state->text_parent = NULL; + return; + case TRAVERSE_GET_BOUNDS: + tr_state->bounds = st->bounds; + return; + case TRAVERSE_GET_TEXT: + tr_state->text_parent = n; + gf_font_spans_get_selection(n, st->spans, tr_state); + tr_state->text_parent = NULL; + return; + case TRAVERSE_SORT: + break; + default: + return; + } + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + + ctx = drawable_init_context_mpeg4(st->graph, tr_state); + if (!ctx) return; + ctx->sub_path_index = tr_state->text_split_idx; + + ctx->flags |= CTX_IS_TEXT; + if (!GF_COL_A(ctx->aspect.fill_color)) { + /*override line join*/ + ctx->aspect.pen_props.join = GF_LINE_JOIN_MITER; + ctx->aspect.pen_props.cap = GF_LINE_CAP_FLAT; + } + + /*if text selection mode, we must force redraw of the entire text span because we don't + if glyphs have been (un)selected*/ + if (!tr_state->direct_draw && + /*text selection on*/ + (tr_state->visual->compositor->text_selection + /*text sel release*/ + || (tr_state->visual->compositor->store_text_state==GF_SC_TSEL_RELEASED)) + ) { + GF_TextSpan *span; + u32 i = 0; + Bool unselect = (tr_state->visual->compositor->store_text_state==GF_SC_TSEL_RELEASED) ? 1 : 0; + while ((span = gf_list_enum(st->spans, &i))) { + if (span->flags & GF_TEXT_SPAN_SELECTED) { + if (unselect) span->flags &= ~GF_TEXT_SPAN_SELECTED; + ctx->flags |= CTX_APP_DIRTY; + } + } + } + + if (ctx->sub_path_index) { + GF_TextSpan *span = (GF_TextSpan *)gf_list_get(st->spans, ctx->sub_path_index-1); + if (span) drawable_finalize_sort(ctx, tr_state, &span->bounds); + } else { + drawable_finalize_sort(ctx, tr_state, &st->bounds); + } +} + + +void compositor_init_text(GF_Compositor *compositor, GF_Node *node) +{ + TextStack *stack = (TextStack *)malloc(sizeof(TextStack)); + stack->graph = drawable_new(); + stack->graph->node = node; + stack->graph->flags = DRAWABLE_USE_TRAVERSE_DRAW; + stack->ascent = stack->descent = 0; + stack->spans = gf_list_new(); + stack->texture_text_flag = 0; + + stack->compositor = compositor; + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, Text_Traverse); +} + + +static void TraverseTextureText(GF_Node *node, void *rs, Bool is_destroy) +{ + TextStack *stack; + GF_Node *text; + GF_FieldInfo field; + if (is_destroy) return; + if (gf_node_get_field(node, 0, &field) != GF_OK) return; + if (field.fieldType != GF_SG_VRML_SFNODE) return; + text = *(GF_Node **)field.far_ptr; + if (!text) return; + + if (gf_node_get_field(node, 1, &field) != GF_OK) return; + if (field.fieldType != GF_SG_VRML_SFBOOL) return; + + if (gf_node_get_tag(text) != TAG_MPEG4_Text) return; + stack = (TextStack *) gf_node_get_private(text); + stack->texture_text_flag = *(SFBool*)field.far_ptr ? 1 : 0; +} + + +void compositor_init_texture_text(GF_Compositor *compositor, GF_Node *node) +{ + gf_node_set_callback_function(node, TraverseTextureText); +} + +#ifndef GPAC_DISABLE_3D +void compositor_extrude_text(GF_Node *node, GF_TraverseState *tr_state, GF_Mesh *mesh, MFVec3f *thespine, Fixed creaseAngle, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool txAlongSpine) +{ + u32 i, count; + Fixed min_cx, min_cy, width_cx, width_cy; + TextStack *st = (TextStack *) gf_node_get_private(node); + + /*rebuild text node*/ + if (gf_node_dirty_get(node)) { + ParentNode2D *parent = tr_state->parent; + tr_state->parent = NULL; + text_clean_paths(tr_state->visual->compositor, st); + drawable_reset_path(st->graph); + gf_node_dirty_clear(node, 0); + build_text(st, (M_Text *)node, tr_state); + tr_state->parent = parent; + } + + min_cx = st->bounds.x; + min_cy = st->bounds.y - st->bounds.height; + width_cx = st->bounds.width; + width_cy = st->bounds.height; + + mesh_reset(mesh); + count = gf_list_count(st->spans); + for (i=0; ispans, i); + GF_Path *span_path = gf_font_span_create_path(span); + mesh_extrude_path_ext(mesh, span_path, thespine, creaseAngle, min_cx, min_cy, width_cx, width_cy, begin_cap, end_cap, spine_ori, spine_scale, txAlongSpine); + gf_path_del(span_path); + } + mesh_update_bounds(mesh); + gf_mesh_build_aabbtree(mesh); +} + +#endif + + + diff --git a/src/compositor/mpeg4_textures.c b/src/compositor/mpeg4_textures.c new file mode 100644 index 0000000..7fdb186 --- /dev/null +++ b/src/compositor/mpeg4_textures.c @@ -0,0 +1,397 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "texturing.h" +//#include "nodes_stacks.h" + +#include +#include + + +typedef struct +{ + GF_TextureHandler txh; + + GF_TimeNode time_handle; + Bool fetch_first_frame, first_frame_fetched, is_vrml; + Double start_time; +} MovieTextureStack; + +static void movietexture_destroy(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node); + gf_sc_texture_destroy(&st->txh); + if (st->time_handle.is_registered) gf_sc_unregister_time_node(st->txh.compositor, &st->time_handle); + free(st); + } +} +static Fixed movietexture_get_speed(MovieTextureStack *stack, M_MovieTexture *mt) +{ + return gf_mo_get_speed(stack->txh.stream, mt->speed); +} +static Bool movietexture_get_loop(MovieTextureStack *stack, M_MovieTexture *mt) +{ + return gf_mo_get_loop(stack->txh.stream, mt->loop); +} +static void movietexture_activate(MovieTextureStack *stack, M_MovieTexture *mt, Double scene_time) +{ + mt->isActive = 1; + gf_node_event_out_str((GF_Node*)mt, "isActive"); + if (!stack->txh.is_open) { + scene_time -= mt->startTime; + gf_sc_texture_play_from_to(&stack->txh, &mt->url, scene_time, -1, gf_mo_get_loop(stack->txh.stream, mt->loop), 0); + } + gf_mo_set_speed(stack->txh.stream, mt->speed); +} +static void movietexture_deactivate(MovieTextureStack *stack, M_MovieTexture *mt) +{ + mt->isActive = 0; + gf_node_event_out_str((GF_Node*)mt, "isActive"); + stack->time_handle.needs_unregister = 1; + + if (stack->txh.is_open) { + gf_sc_texture_stop(&stack->txh); + } +} +static void movietexture_update(GF_TextureHandler *txh) +{ + M_MovieTexture *txnode = (M_MovieTexture *) txh->owner; + MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(txh->owner); + + /*setup texture if needed*/ + if (!txh->is_open) return; + if (!txnode->isActive && st->first_frame_fetched) return; + + /*when fetching the first frame disable resync*/ + gf_sc_texture_update_frame(txh, !txnode->isActive); + + if (txh->stream_finished) { + if (movietexture_get_loop(st, txnode)) { + gf_sc_texture_restart(txh); + } + /*if active deactivate*/ + else if (txnode->isActive && gf_mo_should_deactivate(st->txh.stream) ) { + movietexture_deactivate(st, txnode); + } + } + /*first frame is fetched*/ + if (!st->first_frame_fetched && (txh->needs_refresh) ) { + st->first_frame_fetched = 1; + txnode->duration_changed = gf_mo_get_duration(txh->stream); + gf_node_event_out_str(txh->owner, "duration_changed"); + /*stop stream if needed*/ + if (!txnode->isActive && txh->is_open) { + gf_sc_texture_stop(txh); + /*make sure the refresh flag is not cleared*/ + txh->needs_refresh = 1; + } + } + if (txh->needs_refresh) { + /*mark all subtrees using this image as dirty*/ + gf_node_dirty_parents(txh->owner); + gf_sc_invalidate(txh->compositor, NULL); + } +} + +static void movietexture_update_time(GF_TimeNode *st) +{ + Double time; + M_MovieTexture *mt = (M_MovieTexture *)st->udta; + MovieTextureStack *stack = (MovieTextureStack *)gf_node_get_private(st->udta); + + /*not active, store start time and speed*/ + if ( ! mt->isActive) { + stack->start_time = mt->startTime; + } + time = gf_node_get_scene_time(st->udta); + + if (time < stack->start_time || + /*special case if we're getting active AFTER stoptime */ + (!mt->isActive && (mt->stopTime > stack->start_time) && (time>=mt->stopTime)) + || (!stack->start_time && stack->is_vrml && !mt->loop) + ) { + /*opens stream only at first access to fetch first frame*/ + if (stack->fetch_first_frame) { + stack->fetch_first_frame = 0; + if (!stack->txh.is_open) + gf_sc_texture_play(&stack->txh, &mt->url); + } + return; + } + + if (movietexture_get_speed(stack, mt) && mt->isActive) { + /*if stoptime is reached (>startTime) deactivate*/ + if ((mt->stopTime > stack->start_time) && (time >= mt->stopTime) ) { + movietexture_deactivate(stack, mt); + return; + } + } + + /*we're (about to be) active: VRML: + "A time-dependent node is inactive until its startTime is reached. When time now becomes greater than or + equal to startTime, an isActive TRUE event is generated and the time-dependent node becomes active */ + + if (! mt->isActive) movietexture_activate(stack, mt, time); +} + +void compositor_init_movietexture(GF_Compositor *compositor, GF_Node *node) +{ + MovieTextureStack *st; + GF_SAFEALLOC(st, MovieTextureStack); + gf_sc_texture_setup(&st->txh, compositor, node); + st->txh.update_texture_fcnt = movietexture_update; + st->time_handle.UpdateTimeNode = movietexture_update_time; + st->time_handle.udta = node; + st->fetch_first_frame = 1; + st->txh.flags = 0; + if (((M_MovieTexture*)node)->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S; + if (((M_MovieTexture*)node)->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T; + + st->is_vrml = (gf_node_get_tag(node)==TAG_X3D_MovieTexture) ? 1 : 0; + + gf_node_set_private(node, st); + gf_node_set_callback_function(node, movietexture_destroy); + + gf_sc_register_time_node(compositor, &st->time_handle); +} + +GF_TextureHandler *mt_get_texture(GF_Node *node) +{ + MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node); + return &st->txh; +} + +void compositor_movietexture_modified(GF_Node *node) +{ + M_MovieTexture *mt = (M_MovieTexture *)node; + MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node); + if (!st) return; + + /*if open and changed, stop and play*/ + if (st->txh.is_open && gf_sc_texture_check_url_change(&st->txh, &mt->url)) { + gf_sc_texture_stop(&st->txh); + gf_sc_texture_play(&st->txh, &mt->url); + } + /*update state if we're active*/ + else if (mt->isActive) { + movietexture_update_time(&st->time_handle); + if (!mt->isActive) return; + } + /*reregister if needed*/ + st->time_handle.needs_unregister = 0; + if (!st->time_handle.is_registered) gf_sc_register_time_node(st->txh.compositor, &st->time_handle); +} + +typedef struct +{ + GF_TextureHandler txh; +} ImageTextureStack; + +static void imagetexture_destroy(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + ImageTextureStack *st = (ImageTextureStack *) gf_node_get_private(node); + gf_sc_texture_destroy(&st->txh); + free(st); + } +} + +static void imagetexture_update(GF_TextureHandler *txh) +{ + M_ImageTexture *txnode = (M_ImageTexture *) txh->owner; + + /*setup texture if needed*/ + if (!txh->is_open && txnode->url.count) { + gf_sc_texture_play(txh, &txnode->url); + } + gf_sc_texture_update_frame(txh, 0); + + if ( + /*URL is present but not opened - redraw till fetch*/ + /* (txh->stream && !txh->tx_io) && */ + /*image has been updated*/ + txh->needs_refresh) { + /*mark all subtrees using this image as dirty*/ + gf_node_dirty_parents(txh->owner); + gf_sc_invalidate(txh->compositor, NULL); + } +} + +void compositor_init_imagetexture(GF_Compositor *compositor, GF_Node *node) +{ + ImageTextureStack *st; + GF_SAFEALLOC(st, ImageTextureStack); + gf_sc_texture_setup(&st->txh, compositor, node); + st->txh.update_texture_fcnt = imagetexture_update; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, imagetexture_destroy); + st->txh.flags = 0; + if (((M_ImageTexture*)node)->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S; + if (((M_ImageTexture*)node)->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T; +} + +GF_TextureHandler *it_get_texture(GF_Node *node) +{ + ImageTextureStack *st = (ImageTextureStack *) gf_node_get_private(node); + return &st->txh; +} +void compositor_imagetexture_modified(GF_Node *node) +{ + M_ImageTexture *im = (M_ImageTexture *)node; + ImageTextureStack *st = (ImageTextureStack *) gf_node_get_private(node); + if (!st) return; + + /*if open and changed, stop and play*/ + if (st->txh.is_open) { + if (! gf_sc_texture_check_url_change(&st->txh, &im->url)) return; + gf_sc_texture_stop(&st->txh); + gf_sc_texture_play(&st->txh, &im->url); + return; + } + /*if not open and changed play*/ + if (im->url.count) + gf_sc_texture_play(&st->txh, &im->url); +} + + + +typedef struct +{ + GF_TextureHandler txh; + char *pixels; +} PixelTextureStack; + +static void pixeltexture_destroy(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(node); + if (st->pixels) free(st->pixels); + gf_sc_texture_destroy(&st->txh); + free(st); + } +} +static void pixeltexture_update(GF_TextureHandler *txh) +{ + u32 pix_format, stride, i; + M_PixelTexture *pt = (M_PixelTexture *) txh->owner; + PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(txh->owner); + + if (!gf_node_dirty_get(txh->owner)) return; + gf_node_dirty_clear(txh->owner, 0); + + + /*pixel texture doesn not use any media object but has data in the content. + However we still use the same texture object, just be carefull not to use media funtcions*/ + txh->transparent = 0; + stride = pt->image.width; + /*num_components are as in VRML (1->4) not as in BIFS bitstream (0->3)*/ + switch (pt->image.numComponents) { + case 1: + pix_format = GF_PIXEL_GREYSCALE; + break; + case 2: + pix_format = GF_PIXEL_ALPHAGREY; + txh->transparent = 1; + stride *= 2; + break; + case 3: + pix_format = GF_PIXEL_RGB_24; + txh->transparent = 0; + stride *= 3; + break; + case 4: + pix_format = GF_PIXEL_RGBA; + txh->transparent = 1; + stride *= 4; + break; + default: + return; + } + + if (!txh->tx_io) { + gf_sc_texture_allocate(txh); + if (!txh->tx_io) return; + } + + if (st->pixels) free(st->pixels); + st->pixels = (char*)malloc(sizeof(char) * stride * pt->image.height); + /*FIXME FOR OPENGL !!*/ + if (0) { + for (i=0; iimage.height; i++) { + memcpy(st->pixels + i * stride, pt->image.pixels + i * stride, stride); + } + } + /*revert pixel ordering...*/ + else { + for (i=0; iimage.height; i++) { + memcpy(st->pixels + i * stride, pt->image.pixels + (pt->image.height - 1 - i) * stride, stride); + } + } + + txh->width = pt->image.width; + txh->height = pt->image.height; + txh->stride = stride; + txh->pixelformat = pix_format; + txh->data = st->pixels; + + gf_sc_texture_set_data(txh); +} + +void compositor_init_pixeltexture(GF_Compositor *compositor, GF_Node *node) +{ + PixelTextureStack *st; + GF_SAFEALLOC(st, PixelTextureStack); + gf_sc_texture_setup(&st->txh, compositor, node); + st->pixels = NULL; + st->txh.update_texture_fcnt = pixeltexture_update; + + gf_node_set_private(node, st); + gf_node_set_callback_function(node, pixeltexture_destroy); + st->txh.flags = 0; + if (((M_PixelTexture*)node)->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S; + if (((M_PixelTexture*)node)->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T; +} + +GF_TextureHandler *pt_get_texture(GF_Node *node) +{ + PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(node); + return &st->txh; +} + +static void matte_update(GF_TextureHandler *txh) +{ + /*nothing to do*/ +} + +void compositor_init_mattetexture(GF_Compositor *compositor, GF_Node *node) +{ + ImageTextureStack *st; + GF_SAFEALLOC(st, ImageTextureStack); + gf_sc_texture_setup(&st->txh, compositor, node); + st->txh.flags = GF_SR_TEXTURE_MATTE; + st->txh.update_texture_fcnt = matte_update; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, imagetexture_destroy); +} diff --git a/src/compositor/mpeg4_timesensor.c b/src/compositor/mpeg4_timesensor.c new file mode 100644 index 0000000..ce700c6 --- /dev/null +++ b/src/compositor/mpeg4_timesensor.c @@ -0,0 +1,193 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" + +#include +#include + +typedef struct +{ + GF_TimeNode time_handle; + Bool store_info; + Double start_time, cycle_interval; + u32 num_cycles; + GF_Compositor *compositor; + Bool is_x3d; +} TimeSensorStack; + +static void timesensor_destroy(GF_Node *ts, void *rs, Bool is_destroy) +{ + if (is_destroy) { + TimeSensorStack *st = (TimeSensorStack *)gf_node_get_private(ts); + if (st->time_handle.is_registered) { + gf_sc_unregister_time_node(st->compositor, &st->time_handle); + } + free(st); + } +} + + +static +void timesensor_deactivate(TimeSensorStack *stack, M_TimeSensor *ts) +{ + ts->isActive = 0; + gf_node_event_out((GF_Node *) ts, 7);//"isActive" + assert(stack->time_handle.is_registered); + stack->time_handle.needs_unregister = 1; + stack->num_cycles = 0; +} + +static +void timesensor_update_time(GF_TimeNode *st) +{ + Double currentTime, cycleTime; + Fixed newFraction; + u32 inc; + M_TimeSensor *TS = (M_TimeSensor *)st->udta; + TimeSensorStack *stack = (TimeSensorStack *)gf_node_get_private(st->udta); + + if (! TS->enabled) { + if (TS->isActive) { + TS->cycleTime = gf_node_get_scene_time(st->udta); + gf_node_event_out(st->udta, 5);//"cycleTime" + timesensor_deactivate(stack, TS); + } + return; + } + + if (stack->store_info) { + stack->store_info = 0; + stack->start_time = TS->startTime; + stack->cycle_interval = TS->cycleInterval; + } + + currentTime = gf_node_get_scene_time(st->udta); + if (!TS->isActive) { + if (currentTime < stack->start_time) return; + /*special case: if we're greater than both start and stop time don't activate*/ + if (!TS->isActive && (TS->stopTime > stack->start_time) && (currentTime >= TS->stopTime)) { + stack->time_handle.needs_unregister = 1; + return; + } + if (stack->is_x3d && !TS->loop) { + if (!stack->start_time) return; + if (currentTime >= TS->startTime+stack->cycle_interval) return; + } + } + + cycleTime = currentTime - stack->start_time - stack->num_cycles * stack->cycle_interval; + newFraction = FLT2FIX ( fmod(cycleTime, stack->cycle_interval) / stack->cycle_interval ); + + if (TS->isActive) { + TS->time = currentTime; + gf_node_event_out(st->udta, 8);//"time" + + /*VRML: + "f = fmod( (now - startTime) , cycleInterval) / cycleInterval + if (f == 0.0 && now > startTime) fraction_changed = 1.0 + else fraction_changed = f" + */ + if (!newFraction && (currentTime > stack->start_time ) ) newFraction = FIX_ONE; + + /*check for deactivation - if so generate a fraction_changed AS IF NOW WAS EXACTLY STOPTIME*/ + if ((TS->stopTime > stack->start_time) && (currentTime >= TS->stopTime) ) { + cycleTime = TS->stopTime - stack->start_time - stack->num_cycles * stack->cycle_interval; + TS->fraction_changed = FLT2FIX( fmod(cycleTime, stack->cycle_interval) / stack->cycle_interval ); + /*cf above*/ + if (TS->fraction_changed < FIX_EPSILON) TS->fraction_changed = FIX_ONE; + gf_node_event_out(st->udta, 6);//"fraction_changed" + timesensor_deactivate(stack, TS); + return; + } + if (! TS->loop) { + if (cycleTime >= stack->cycle_interval) { + TS->fraction_changed = FIX_ONE; + gf_node_event_out(st->udta, 6);//"fraction_changed" + timesensor_deactivate(stack, TS); + return; + } + } + TS->fraction_changed = newFraction; + gf_node_event_out(st->udta, 6);//"fraction_changed" + } + + /*we're (about to be) active: VRML: + "A time-dependent node is inactive until its startTime is reached. When time now becomes greater than or + equal to startTime, an isActive TRUE event is generated and the time-dependent node becomes active */ + + if (!TS->isActive) { + st->needs_unregister = 0; + TS->isActive = 1; + gf_node_event_out(st->udta, 7); //"isActive" + TS->cycleTime = currentTime; + gf_node_event_out(st->udta, 5);//"cycleTime" + TS->fraction_changed = newFraction; + gf_node_event_out(st->udta, 6);//"fraction_changed" + } + + //compute cycle time + if (TS->loop && (cycleTime >= stack->cycle_interval) ) { + inc = 1 + (u32) ( (cycleTime - stack->cycle_interval ) / stack->cycle_interval ); + stack->num_cycles += inc; + cycleTime -= inc*stack->cycle_interval; + TS->cycleTime = currentTime - cycleTime; + gf_node_event_out(st->udta, 5);//"cycleTime" + } +} + +void compositor_init_timesensor(GF_Compositor *compositor, GF_Node *node) +{ + TimeSensorStack *st; + GF_SAFEALLOC(st, TimeSensorStack); + st->time_handle.UpdateTimeNode = timesensor_update_time; + st->time_handle.udta = node; + st->store_info = 1; + st->compositor = compositor; + st->is_x3d = (gf_node_get_tag(node)==TAG_X3D_TimeSensor) ? 1 : 0; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, timesensor_destroy); + /*time sensor needs to be run only if def'ed, otherwise it doesn't impact scene*/ + //if (gf_node_get_id(node)) + gf_sc_register_time_node(compositor, &st->time_handle); +} + + +void compositor_timesensor_modified(GF_Node *t) +{ + M_TimeSensor *ts = (M_TimeSensor *)t; + TimeSensorStack *stack = (TimeSensorStack *) gf_node_get_private(t); + if (!stack) return; + + if (ts->isActive) timesensor_update_time(&stack->time_handle); + + if (!ts->isActive) stack->store_info = 1; + + if (ts->enabled) { + stack->time_handle.needs_unregister = 0; + if (!stack->time_handle.is_registered) { + gf_sc_register_time_node(stack->compositor, &stack->time_handle); + } + } +} diff --git a/src/compositor/mpeg4_viewport.c b/src/compositor/mpeg4_viewport.c new file mode 100644 index 0000000..93d3237 --- /dev/null +++ b/src/compositor/mpeg4_viewport.c @@ -0,0 +1,568 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" +#include "visual_manager.h" +/*for default scene view*/ +#include +#include + + + +GF_Err gf_sc_get_viewpoint(GF_Compositor *compositor, u32 viewpoint_idx, const char **outName, Bool *is_bound) +{ + u32 count; + GF_Node *n; + if (!compositor->visual) return GF_BAD_PARAM; + count = gf_list_count(compositor->visual->view_stack); + if (!viewpoint_idx) return GF_BAD_PARAM; + if (viewpoint_idx>count) return GF_EOS; + + n = (GF_Node*)gf_list_get(compositor->visual->view_stack, viewpoint_idx-1); + switch (gf_node_get_tag(n)) { + case TAG_MPEG4_Viewport: *outName = ((M_Viewport*)n)->description.buffer; *is_bound = ((M_Viewport*)n)->isBound;return GF_OK; + case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: *outName = ((M_Viewpoint*)n)->description.buffer; *is_bound = ((M_Viewpoint*)n)->isBound; return GF_OK; + default: *outName = NULL; return GF_OK; + } +} + +GF_Err gf_sc_set_viewpoint(GF_Compositor *compositor, u32 viewpoint_idx, const char *viewpoint_name) +{ + u32 count, i; + GF_Node *n; + if (!compositor->visual) return GF_BAD_PARAM; + count = gf_list_count(compositor->visual->view_stack); + if (viewpoint_idx>count) return GF_BAD_PARAM; + if (!viewpoint_idx && !viewpoint_name) return GF_BAD_PARAM; + + if (viewpoint_idx) { + Bool bind; + n = (GF_Node*)gf_list_get(compositor->visual->view_stack, viewpoint_idx-1); + bind = Bindable_GetIsBound(n); + Bindable_SetSetBind(n, !bind); + return GF_OK; + } + for (i=0; ivisual->view_stack, viewpoint_idx-1); + switch (gf_node_get_tag(n)) { + case TAG_MPEG4_Viewport: name = ((M_Viewport*)n)->description.buffer; break; + case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: name = ((M_Viewpoint*)n)->description.buffer; break; + default: break; + } + if (name && !stricmp(name, viewpoint_name)) { + Bool bind = Bindable_GetIsBound(n); + Bindable_SetSetBind(n, !bind); + return GF_OK; + } + } + return GF_BAD_PARAM; +} + +#define VPCHANGED(__rend) { GF_Event evt; evt.type = GF_EVENT_VIEWPOINTS; GF_USER_SENDEVENT(__rend->user, &evt); } + + +static void DestroyViewStack(GF_Node *node) +{ + ViewStack *st = (ViewStack *) gf_node_get_private(node); + PreDestroyBindable(node, st->reg_stacks); + gf_list_del(st->reg_stacks); + VPCHANGED(gf_sc_get_compositor(node)); + free(st); +} + +static void viewport_set_bind(GF_Node *node) +{ + GF_Compositor *rend = gf_sc_get_compositor(node); + ViewStack *st = (ViewStack *) gf_node_get_private(node); + Bindable_OnSetBind(node, st->reg_stacks); + + gf_sc_invalidate(rend, NULL); + /*notify change of vp stack*/ + VPCHANGED(rend); + /*and dirty ourselves to force frustrum update*/ + gf_node_dirty_set(node, 0, 0); +} + + +static void TraverseViewport(GF_Node *node, void *rs, Bool is_destroy) +{ + Fixed ar, sx, sy, w, h, tx, ty; +#ifndef GPAC_DISABLE_3D + GF_Matrix mx; +#endif + GF_Matrix2D mat; + GF_Rect rc, rc_bckup; + ViewStack *st = (ViewStack *) gf_node_get_private(node); + M_Viewport *vp = (M_Viewport *) node; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + DestroyViewStack(node); + return; + } + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d>1) return; +#endif + + /*first traverse, bound if needed*/ + if (gf_list_find(tr_state->viewpoints, node) < 0) { + gf_list_add(tr_state->viewpoints, node); + assert(gf_list_find(st->reg_stacks, tr_state->viewpoints)==-1); + gf_list_add(st->reg_stacks, tr_state->viewpoints); + + if (gf_list_get(tr_state->viewpoints, 0) == vp) { + if (!vp->isBound) Bindable_SetIsBound(node, 1); + } else { + if (gf_inline_default_scene_viewpoint(node)) Bindable_SetSetBind(node, 1); + } + VPCHANGED(tr_state->visual->compositor); + /*in any case don't draw the first time (since the viewport could have been declared last)*/ + gf_sc_invalidate(tr_state->visual->compositor, NULL); + return; + } + + if (tr_state->traversing_mode != TRAVERSE_BINDABLE) return; + if (!vp->isBound) return; + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + w = tr_state->bbox.max_edge.x - tr_state->bbox.min_edge.x; + h = tr_state->bbox.max_edge.y - tr_state->bbox.min_edge.y; + } else +#endif + { + w = tr_state->bounds.width; + h = tr_state->bounds.height; + } + if (!w || !h) return; + + + /*if no parent this is the main viewport, don't update if not changed*/ +// if (!tr_state->is_layer && !gf_node_dirty_get(node)) return; + + gf_node_dirty_clear(node, 0); + + gf_mx2d_init(mat); + gf_mx2d_add_translation(&mat, vp->position.x, vp->position.y); + gf_mx2d_add_rotation(&mat, 0, 0, vp->orientation); + + //compute scaling ratio + rc = gf_rect_center(vp->size.x, vp->size.y); + ar = gf_divfix(h, w); + rc_bckup = rc; + + switch (vp->fit) { + /*covers all area and respect aspect ratio*/ + case 2: + if (gf_divfix(rc.width, w) > gf_divfix(rc.height, h)) { + rc.width = gf_muldiv(rc.width, h, rc.height); + rc.height = h; + } else { + rc.height = gf_muldiv(rc.height , w, rc.width); + rc.width = w; + } + break; + /*fits inside the area and respect AR*/ + case 1: + if (gf_divfix(rc.width, w)> gf_divfix(rc.height, h)) { + rc.height = gf_muldiv(rc.height, w, rc.width); + rc.width = w; + } else { + rc.width = gf_muldiv(rc.width, h, rc.height); + rc.height = h; + } + break; + /*fit entirely: nothing to change*/ + case 0: + rc.width = w; + rc.height = h; + break; + default: + return; + } + sx = gf_divfix(rc.width, rc_bckup.width); + sy = gf_divfix(rc.height, rc_bckup.height); + + /*viewport on root visual, remove compositor scale*/ + if (!tr_state->is_layer && (tr_state->visual->compositor->visual==tr_state->visual) ) { + sx = gf_divfix(sx, tr_state->visual->compositor->scale_x); + sy = gf_divfix(sy, tr_state->visual->compositor->scale_y); + } + + rc.x = - rc.width/2; + rc.y = rc.height/2; + + tx = ty = 0; + if (vp->fit && vp->alignment.count) { + /*left alignment*/ + if (vp->alignment.vals[0] == -1) tx = rc.width/2 - w/2; + else if (vp->alignment.vals[0] == 1) tx = w/2 - rc.width/2; + + if (vp->alignment.count>1) { + /*top-alignment*/ + if (vp->alignment.vals[1]==-1) ty = rc.height/2 - h/2; + else if (vp->alignment.vals[1]==1) ty = h/2 - rc.height/2; + } + } + + gf_mx2d_init(mat); + gf_mx2d_add_scale(&mat, sx, sy); + gf_mx2d_add_translation(&mat, tx, ty); + + gf_mx2d_add_translation(&mat, -gf_mulfix(vp->position.x,sx), -gf_mulfix(vp->position.y,sy) ); + gf_mx2d_add_rotation(&mat, 0, 0, vp->orientation); + + tr_state->bounds = rc; + tr_state->bounds.x += tx; + tr_state->bounds.y += ty; + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + /*in layers directly modify the model matrix*/ + if (tr_state->is_layer) { + gf_mx_from_mx2d(&mx, &mat); + gf_mx_add_matrix(&tr_state->model_matrix, &mx); + } + /*otherwise add to camera viewport matrix*/ + else { + gf_mx_from_mx2d(&tr_state->camera->viewport, &mat); + tr_state->camera->flags = (CAM_HAS_VIEWPORT | CAM_IS_DIRTY); + } + } else +#endif + gf_mx2d_pre_multiply(&tr_state->transform, &mat); +} + +void compositor_init_viewport(GF_Compositor *compositor, GF_Node *node) +{ + ViewStack *ptr; + GF_SAFEALLOC(ptr, ViewStack); + + ptr->reg_stacks = gf_list_new(); + + gf_node_set_private(node, ptr); + gf_node_set_callback_function(node, TraverseViewport); + ((M_Viewport*)node)->on_set_bind = viewport_set_bind; +} + + +#ifndef GPAC_DISABLE_3D + +static void viewpoint_set_bind(GF_Node *node) +{ + GF_Compositor *rend = gf_sc_get_compositor(node); + ViewStack *st = (ViewStack *) gf_node_get_private(node); + + if (!((M_Viewpoint*)node)->isBound ) + st->prev_was_bound = 0; + Bindable_OnSetBind(node, st->reg_stacks); + gf_sc_invalidate(rend, NULL); + /*notify change of vp stack*/ + VPCHANGED(rend); + /*and dirty ourselves to force frustrum update*/ + gf_node_dirty_set(node, 0, 0); +} + +static void TraverseViewpoint(GF_Node *node, void *rs, Bool is_destroy) +{ + SFVec3f pos, v1, v2; + SFRotation ori; + GF_Matrix mx; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + M_Viewpoint *vp = (M_Viewpoint*) node; + ViewStack *st = (ViewStack *) gf_node_get_private(node); + + if (is_destroy) { + DestroyViewStack(node); + return; + } + + assert(tr_state->viewpoints); +// if (!tr_state->camera->is_3D) return; + + /*first traverse, bound if needed*/ + if (gf_list_find(tr_state->viewpoints, node) < 0) { + gf_list_add(tr_state->viewpoints, node); + assert(gf_list_find(st->reg_stacks, tr_state->viewpoints)==-1); + gf_list_add(st->reg_stacks, tr_state->viewpoints); + + if (gf_list_get(tr_state->viewpoints, 0) == vp) { + if (!vp->isBound) Bindable_SetIsBound(node, 1); + } else { + if (gf_inline_default_scene_viewpoint(node)) Bindable_SetSetBind(node, 1); + } + VPCHANGED(tr_state->visual->compositor); + /*in any case don't draw the first time (since the viewport could have been declared last)*/ + if (tr_state->layer3d) gf_node_dirty_set(tr_state->layer3d, GF_SG_VRML_BINDABLE_DIRTY, 0); + gf_sc_invalidate(tr_state->visual->compositor, NULL); + } + /*not evaluating vp, return*/ + if (tr_state->traversing_mode != TRAVERSE_BINDABLE) { + /*store model matrix if changed - NOTE: we always have a 1-frame delay between VP used and real world... + we could remove this by pre-traversing the scene before applying vp, but that would mean 2 scene + traversals*/ + if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) { + if (!gf_mx_equal(&st->world_view_mx, &tr_state->model_matrix)) { + gf_mx_copy(st->world_view_mx, tr_state->model_matrix); + gf_node_dirty_set(node, 0, 0); + } + } + tr_state->disable_cull = 1; + return; + } + + /*not bound or in 2D visual*/ + if (!vp->isBound || !tr_state->navigations) return; + + if (!gf_node_dirty_get(node)) return; + gf_node_dirty_clear(node, 0); + + /*move to local system*/ + gf_mx_copy(mx, st->world_view_mx); + gf_mx_add_translation(&mx, vp->position.x, vp->position.y, vp->position.z); + gf_mx_add_rotation(&mx, vp->orientation.q, vp->orientation.x, vp->orientation.y, vp->orientation.z); + gf_mx_decompose(&mx, &pos, &v1, &ori, &v2); + /*get center*/ + v1.x = v1.y = v1.z = 0; + /*X3D specifies examine center*/ + if (gf_node_get_tag(node)==TAG_X3D_Viewpoint) v1 = ((X_Viewpoint *)node)->centerOfRotation; + gf_mx_apply_vec(&st->world_view_mx, &v1); + /*set frustrum param - animate only if not bound last frame and jump false*/ + visual_3d_viewpoint_change(tr_state, node, (!st->prev_was_bound && !vp->jump) ? 1 : 0, vp->fieldOfView, pos, ori, v1); + st->prev_was_bound = 1; +} + +void compositor_init_viewpoint(GF_Compositor *compositor, GF_Node *node) +{ + ViewStack *st; + GF_SAFEALLOC(st, ViewStack); + + st->reg_stacks = gf_list_new(); + gf_mx_init(st->world_view_mx); + gf_node_set_private(node, st); + gf_node_set_callback_function(node, TraverseViewpoint); + ((M_Viewpoint*)node)->on_set_bind = viewpoint_set_bind; +} + +#endif + +static void navinfo_set_bind(GF_Node *node) +{ + ViewStack *st = (ViewStack *) gf_node_get_private(node); + Bindable_OnSetBind(node, st->reg_stacks); + gf_sc_invalidate( gf_sc_get_compositor(node), NULL); +} + +static void TraverseNavigationInfo(GF_Node *node, void *rs, Bool is_destroy) +{ + u32 i; +#ifndef GPAC_DISABLE_3D + SFVec3f start, end; + Fixed scale; +#endif + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + M_NavigationInfo *ni = (M_NavigationInfo *) node; + ViewStack *st = (ViewStack *) gf_node_get_private(node); + + if (is_destroy) { + DestroyViewStack(node); + return; + } +#ifdef GPAC_DISABLE_3D + + /*FIXME, we only deal with one node, no bind stack for the current time*/ + for (i=0; itype.count; i++) { + if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "NONE")) { + tr_state->visual->compositor->navigation_disabled = 1; + } + } +#else + + if (!tr_state->navigations) return; + + /*first traverse, bound if needed*/ + if (gf_list_find(tr_state->navigations, node) < 0) { + gf_list_add(tr_state->navigations, node); + if (gf_list_get(tr_state->navigations, 0) == ni) { + if (!ni->isBound) Bindable_SetIsBound(node, 1); + } + assert(gf_list_find(st->reg_stacks, tr_state->navigations)==-1); + gf_list_add(st->reg_stacks, tr_state->navigations); + gf_mx_copy(st->world_view_mx, tr_state->model_matrix); + /*in any case don't draw the first time*/ + gf_sc_invalidate(tr_state->visual->compositor, NULL); + return; + } + /*not bound*/ + if (!ni->isBound) return; + /*not evaluating, return*/ + if (tr_state->traversing_mode != TRAVERSE_BINDABLE) { + if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) { + if (!gf_mx_equal(&st->world_view_mx, &tr_state->model_matrix)) { + gf_mx_copy(st->world_view_mx, tr_state->model_matrix); + gf_node_dirty_set(node, 0, 0); + } + } + return; + } + + if (!gf_node_dirty_get(node)) return; + gf_node_dirty_clear(node, 0); + + tr_state->camera->navigation_flags = 0; + tr_state->camera->navigate_mode = 0; + for (i=0; itype.count; i++) { + if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "ANY")) tr_state->camera->navigation_flags |= NAV_ANY; + if (!tr_state->camera->navigate_mode) { + if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "NONE")) tr_state->camera->navigate_mode = GF_NAVIGATE_NONE; + else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "WALK")) tr_state->camera->navigate_mode = GF_NAVIGATE_WALK; + else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "EXAMINE")) tr_state->camera->navigate_mode = GF_NAVIGATE_EXAMINE; + else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "FLY")) tr_state->camera->navigate_mode = GF_NAVIGATE_FLY; + else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "QTVR")) tr_state->camera->navigate_mode = GF_NAVIGATE_VR; + } + } + if (ni->headlight) tr_state->camera->navigation_flags |= NAV_HEADLIGHT; + + start.x = start.y = start.z = 0; + end.x = end.y = 0; + end.z = FIX_ONE; + gf_mx_apply_vec(&st->world_view_mx, &start); + gf_mx_apply_vec(&st->world_view_mx, &end); + gf_vec_diff(end, end, start); + scale = gf_vec_len(end); + + tr_state->camera->speed = gf_mulfix(scale, ni->speed); + tr_state->camera->visibility = gf_mulfix(scale, ni->visibilityLimit); + if (ni->avatarSize.count) tr_state->camera->avatar_size.x = gf_mulfix(scale, ni->avatarSize.vals[0]); + if (ni->avatarSize.count>1) tr_state->camera->avatar_size.y = gf_mulfix(scale, ni->avatarSize.vals[1]); + if (ni->avatarSize.count>2) tr_state->camera->avatar_size.z = gf_mulfix(scale, ni->avatarSize.vals[2]); + + if (0 && tr_state->pixel_metrics) { + u32 s = MAX(tr_state->visual->width, tr_state->visual->height); + s /= 2; +// tr_state->camera->speed = ni->speed; + tr_state->camera->visibility *= s; + tr_state->camera->avatar_size.x *= s; + tr_state->camera->avatar_size.y *= s; + tr_state->camera->avatar_size.z *= s; + } +#endif + +} + +void compositor_init_navigation_info(GF_Compositor *compositor, GF_Node *node) +{ + ViewStack *st; + GF_SAFEALLOC(st, ViewStack); + + st->reg_stacks = gf_list_new(); + gf_node_set_private(node, st); + gf_node_set_callback_function(node, TraverseNavigationInfo); + ((M_NavigationInfo*)node)->on_set_bind = navinfo_set_bind; +} + + +#ifndef GPAC_DISABLE_3D + +static void fog_set_bind(GF_Node *node) +{ + ViewStack *st = (ViewStack *) gf_node_get_private(node); + Bindable_OnSetBind(node, st->reg_stacks); + gf_sc_invalidate(gf_sc_get_compositor(node), NULL); +} + +static void TraverseFog(GF_Node *node, void *rs, Bool is_destroy) +{ + Fixed density, vrange; + SFVec3f start, end; + ViewStack *vp_st; + M_Viewpoint *vp; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + M_Fog *fog = (M_Fog *) node; + ViewStack *st = (ViewStack *) gf_node_get_private(node); + + if (is_destroy) { + DestroyViewStack(node); + return; + } + + if (!tr_state->fogs) return; + + /*first traverse, bound if needed*/ + if (gf_list_find(tr_state->fogs, node) < 0) { + gf_list_add(tr_state->fogs, node); + if (gf_list_get(tr_state->fogs, 0) == fog) { + if (!fog->isBound) Bindable_SetIsBound(node, 1); + } + assert(gf_list_find(st->reg_stacks, tr_state->fogs)==-1); + gf_list_add(st->reg_stacks, tr_state->fogs); + + gf_mx_copy(st->world_view_mx, tr_state->model_matrix); + /*in any case don't draw the first time*/ + gf_sc_invalidate(tr_state->visual->compositor, NULL); + return; + } + /*not evaluating, return*/ + if (tr_state->traversing_mode != TRAVERSE_BINDABLE) { + if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) + gf_mx_copy(st->world_view_mx, tr_state->model_matrix); + return; + } + /*not bound*/ + if (!fog->isBound || !fog->visibilityRange) return; + + /*fog visibility is expressed in current bound VP so get its matrix*/ + vp = (M_Viewpoint*)gf_list_get(tr_state->viewpoints, 0); + vp_st = NULL; + if (vp && vp->isBound) vp_st = (ViewStack *) gf_node_get_private((GF_Node *)vp); + + start.x = start.y = start.z = 0; + end.x = end.y = 0; end.z = fog->visibilityRange; + if (vp_st) { + gf_mx_apply_vec(&vp_st->world_view_mx, &start); + gf_mx_apply_vec(&vp_st->world_view_mx, &end); + } + gf_mx_apply_vec(&st->world_view_mx, &start); + gf_mx_apply_vec(&st->world_view_mx, &end); + gf_vec_diff(end, end, start); + vrange = gf_vec_len(end); + + density = gf_invfix(vrange); + visual_3d_set_fog(tr_state->visual, fog->fogType.buffer, fog->color, density, vrange); +} + +void compositor_init_fog(GF_Compositor *compositor, GF_Node *node) +{ + ViewStack *st; + GF_SAFEALLOC(st, ViewStack); + + st->reg_stacks = gf_list_new(); + gf_node_set_private(node, st); + gf_node_set_callback_function(node, TraverseFog); + ((M_Fog*)node)->on_set_bind = fog_set_bind; +} + +#endif diff --git a/src/compositor/navigate.c b/src/compositor/navigate.c new file mode 100644 index 0000000..05ad6d8 --- /dev/null +++ b/src/compositor/navigate.c @@ -0,0 +1,663 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "nodes_stacks.h" +#include "visual_manager.h" +#include + + +#ifndef GPAC_DISABLE_3D + +static void camera_changed(GF_Compositor *compositor, GF_Camera *cam) +{ + cam->flags |= CAM_IS_DIRTY; + gf_sc_invalidate(compositor, NULL); +} + +/*shortcut*/ +static void gf_mx_rotation_matrix(GF_Matrix *mx, SFVec3f axis_pt, SFVec3f axis, Fixed angle) +{ + gf_mx_init(*mx); + gf_mx_add_translation(mx, axis_pt.x, axis_pt.y, axis_pt.z); + gf_mx_add_rotation(mx, angle, axis.x, axis.y, axis.z); + gf_mx_add_translation(mx, -axis_pt.x, -axis_pt.y, -axis_pt.z); +} + +static void view_orbit_x(GF_Compositor *compositor, GF_Camera *cam, Fixed dx) +{ + GF_Matrix mx; + if (!dx) return; + gf_mx_rotation_matrix(&mx, cam->target, cam->up, dx); + gf_mx_apply_vec(&mx, &cam->position); + camera_changed(compositor, cam); +} +static void view_orbit_y(GF_Compositor *compositor, GF_Camera *cam, Fixed dy) +{ + GF_Matrix mx; + SFVec3f axis; + if (!dy) return; + axis = camera_get_right_dir(cam); + gf_mx_rotation_matrix(&mx, cam->target, axis, dy); + gf_mx_apply_vec(&mx, &cam->position); + /*update up vector*/ + cam->up = gf_vec_cross(camera_get_pos_dir(cam), axis); + gf_vec_norm(&cam->up); + camera_changed(compositor, cam); +} + +static void view_exam_x(GF_Compositor *compositor, GF_Camera *cam, Fixed dx) +{ + GF_Matrix mx; + if (!dx) return; + gf_mx_rotation_matrix(&mx, cam->examine_center, cam->up, dx); + gf_mx_apply_vec(&mx, &cam->position); + gf_mx_apply_vec(&mx, &cam->target); + camera_changed(compositor, cam); +} + +static void view_exam_y(GF_Compositor *compositor, GF_Camera *cam, Fixed dy) +{ + GF_Matrix mx; + SFVec3f axis; + if (!dy) return; + axis = camera_get_right_dir(cam); + gf_mx_rotation_matrix(&mx, cam->examine_center, axis, dy); + gf_mx_apply_vec(&mx, &cam->position); + gf_mx_apply_vec(&mx, &cam->target); + /*update up vector*/ + cam->up = gf_vec_cross(camera_get_pos_dir(cam), axis); + gf_vec_norm(&cam->up); + camera_changed(compositor, cam); +} + +static void view_roll(GF_Compositor *compositor, GF_Camera *cam, Fixed dd) +{ + GF_Matrix mx; + SFVec3f delta; + if (!dd) return; + gf_vec_add(delta, cam->target, cam->up); + gf_mx_rotation_matrix(&mx, cam->target, camera_get_pos_dir(cam), dd); + gf_mx_apply_vec(&mx, &delta); + gf_vec_diff(cam->up, delta, cam->target); + gf_vec_norm(&cam->up); + camera_changed(compositor, cam); +} + +static void view_pan_x(GF_Compositor *compositor, GF_Camera *cam, Fixed dx) +{ + GF_Matrix mx; + if (!dx) return; + gf_mx_rotation_matrix(&mx, cam->position, cam->up, dx); + gf_mx_apply_vec(&mx, &cam->target); + camera_changed(compositor, cam); +} +static void view_pan_y(GF_Compositor *compositor, GF_Camera *cam, Fixed dy) +{ + GF_Matrix mx; + SFVec3f axis; + if (!dy) return; + axis = camera_get_right_dir(cam); + gf_mx_rotation_matrix(&mx, cam->position, axis, dy); + gf_mx_apply_vec(&mx, &cam->target); + /*update up vector*/ + cam->up = gf_vec_cross(camera_get_pos_dir(cam), axis); + gf_vec_norm(&cam->up); + camera_changed(compositor, cam); +} + +/*for translation moves when jumping*/ +#define JUMP_SCALE_FACTOR 4 + +static void view_translate_x(GF_Compositor *compositor, GF_Camera *cam, Fixed dx) +{ + SFVec3f v; + if (!dx) return; + if (cam->jumping) dx *= JUMP_SCALE_FACTOR; + v = gf_vec_scale(camera_get_right_dir(cam), dx); + gf_vec_add(cam->target, cam->target, v); + gf_vec_add(cam->position, cam->position, v); + camera_changed(compositor, cam); +} +static void view_translate_y(GF_Compositor *compositor, GF_Camera *cam, Fixed dy) +{ + SFVec3f v; + if (!dy) return; + if (cam->jumping) dy *= JUMP_SCALE_FACTOR; + v = gf_vec_scale(cam->up, dy); + gf_vec_add(cam->target, cam->target, v); + gf_vec_add(cam->position, cam->position, v); + camera_changed(compositor, cam); +} + +static void view_translate_z(GF_Compositor *compositor, GF_Camera *cam, Fixed dz) +{ + SFVec3f v; + if (!dz) return; + if (cam->jumping) dz *= JUMP_SCALE_FACTOR; + dz = gf_mulfix(dz, cam->speed); + v = gf_vec_scale(camera_get_target_dir(cam), dz); + gf_vec_add(cam->target, cam->target, v); + gf_vec_add(cam->position, cam->position, v); + camera_changed(compositor, cam); +} + +static void view_zoom(GF_Compositor *compositor, GF_Camera *cam, Fixed z) +{ + Fixed oz; + if ((z>FIX_ONE) || (z<-FIX_ONE)) return; + oz = gf_divfix(cam->vp_fov, cam->fieldOfView); + if (ozfieldOfView = gf_divfix(cam->vp_fov, oz); + if (cam->fieldOfView>GF_PI) cam->fieldOfView=GF_PI; + camera_changed(compositor, cam); +} + +static void nav_fit_screen(GF_Compositor *compositor) +{ + GF_TraverseState tr_state; + SFVec3f pos, diff; + Fixed dist, d; + GF_Camera *cam; + GF_Node *top; + + if (gf_list_count(compositor->visual->back_stack)) return; + if (gf_list_count(compositor->visual->view_stack)) return; + + gf_mx_p(compositor->mx); + top = gf_sg_get_root_node(compositor->scene); + if (!top) { + gf_mx_v(compositor->mx); + return; + } + memset(&tr_state, 0, sizeof(GF_TraverseState)); + tr_state.traversing_mode = TRAVERSE_GET_BOUNDS; + tr_state.visual = compositor->visual; + gf_node_traverse(top, &tr_state); + if (!tr_state.bbox.is_set) { + gf_mx_v(compositor->mx); + return; + } + + cam = &compositor->visual->camera; + + /*fit is based on bounding sphere*/ + dist = gf_divfix(tr_state.bbox.radius, gf_sin(cam->fieldOfView/2) ); + gf_vec_diff(diff, cam->center, tr_state.bbox.center); + /*do not update if camera is outside the scene bounding sphere and dist is too close*/ + if (gf_vec_len(diff) > tr_state.bbox.radius + cam->radius) { + gf_vec_diff(diff, cam->vp_position, tr_state.bbox.center); + d = gf_vec_len(diff); + if (dmx); + return; + } + } + + diff = gf_vec_scale(camera_get_pos_dir(cam), dist); + gf_vec_add(pos, tr_state.bbox.center, diff); + diff = cam->position; + camera_set_vectors(cam, pos, cam->vp_orientation, cam->fieldOfView); + cam->position = diff; + camera_move_to(cam, pos, cam->target, cam->up); + cam->examine_center = tr_state.bbox.center; + cam->flags |= CF_STORE_VP; + camera_changed(compositor, cam); + gf_mx_v(compositor->mx); +} + +static Bool compositor_handle_navigation_3d(GF_Compositor *compositor, GF_Event *ev) +{ + Fixed x, y, trans_scale; + Fixed dx, dy, key_trans, key_pan, key_exam; + s32 key_inv; + u32 keys; + Bool is_pixel_metrics; + GF_Camera *cam; + + cam = NULL; + if (compositor->active_layer) { + cam = compositor_layer3d_get_camera(compositor->active_layer); + is_pixel_metrics = gf_sg_use_pixel_metrics(gf_node_get_graph(compositor->active_layer)); + } + if (!cam) { + cam = &compositor->visual->camera; + assert(compositor); + assert(compositor->scene); + is_pixel_metrics = compositor->traverse_state->pixel_metrics; + } + + if (!cam->navigate_mode) return 0; + keys = compositor->key_states; + x = y = 0; + /*renorm between -1, 1*/ + if (ev->type<=GF_EVENT_MOUSEWHEEL) { + x = gf_divfix( INT2FIX(ev->mouse.x + (s32) compositor->visual->width/2), INT2FIX(compositor->visual->width)); + y = gf_divfix( INT2FIX(ev->mouse.y + (s32) compositor->visual->height/2), INT2FIX(compositor->visual->height)); + } + + dx = (x - compositor->grab_x); + dy = (y - compositor->grab_y); + +/* trans_scale = is_pixel_metrics ? cam->width/2 : INT2FIX(10); + key_trans = is_pixel_metrics ? INT2FIX(10) : cam->avatar_size.x; +*/ + trans_scale = cam->width/2; + key_trans = cam->avatar_size.x; + + key_pan = FIX_ONE/25; + key_exam = FIX_ONE/10; + key_inv = 1; + + if (keys & GF_KEY_MOD_SHIFT) { + dx *= 4; + dy *= 4; + key_pan *= 4; + key_exam *= 4; + key_trans*=4; + } + + switch (ev->type) { + case GF_EVENT_MOUSEDOWN: + /*left*/ + if (ev->mouse.button==GF_MOUSE_LEFT) { + compositor->grab_x = x; + compositor->grab_y = y; + compositor->navigation_state = 1; + + /*change vp and examine center to current location*/ + if ((keys & GF_KEY_MOD_CTRL) && compositor->hit_square_dist) { + cam->vp_position = cam->position; + cam->vp_orientation = camera_get_orientation(cam->position, cam->target, cam->up); + cam->vp_fov = cam->fieldOfView; + cam->examine_center = compositor->hit_world_point; + camera_changed(compositor, cam); + return 1; + } + } + /*right*/ + else if (ev->mouse.button==GF_MOUSE_RIGHT) { + if (compositor->navigation_state && (cam->navigate_mode==GF_NAVIGATE_WALK)) { + camera_jump(cam); + gf_sc_invalidate(compositor, NULL); + return 1; + } + else if (keys & GF_KEY_MOD_CTRL) nav_fit_screen(compositor); + } + break; + + /* note: shortcuts are mostly the same as blaxxun contact, I don't feel like remembering 2 sets...*/ + case GF_EVENT_MOUSEMOVE: + if (!compositor->navigation_state) { + if (cam->navigate_mode==GF_NAVIGATE_GAME) { + /*init mode*/ + compositor->grab_x = x; + compositor->grab_y = y; + compositor->navigation_state = 1; + } + return 0; + } + compositor->navigation_state++; + + switch (cam->navigate_mode) { + /*FIXME- we'll likely need a "step" value for walk at some point*/ + case GF_NAVIGATE_WALK: + case GF_NAVIGATE_FLY: + view_pan_x(compositor, cam, -dx); + if (keys & GF_KEY_MOD_CTRL) view_pan_y(compositor, cam, dy); + else view_translate_z(compositor, cam, gf_mulfix(dy, trans_scale)); + break; + case GF_NAVIGATE_VR: + view_pan_x(compositor, cam, -dx); + if (keys & GF_KEY_MOD_CTRL) view_zoom(compositor, cam, dy); + else view_pan_y(compositor, cam, dy); + break; + case GF_NAVIGATE_PAN: + view_pan_x(compositor, cam, -dx); + if (keys & GF_KEY_MOD_CTRL) view_translate_z(compositor, cam, gf_mulfix(dy, trans_scale)); + else view_pan_y(compositor, cam, dy); + break; + case GF_NAVIGATE_SLIDE: + view_translate_x(compositor, cam, gf_mulfix(dx, trans_scale)); + if (keys & GF_KEY_MOD_CTRL) view_translate_z(compositor, cam, gf_mulfix(dy, trans_scale)); + else view_translate_y(compositor, cam, gf_mulfix(dy, trans_scale)); + break; + case GF_NAVIGATE_EXAMINE: + if (keys & GF_KEY_MOD_CTRL) { + view_translate_z(compositor, cam, gf_mulfix(dy, trans_scale)); + view_roll(compositor, cam, gf_mulfix(dx, trans_scale)); + } else { + view_exam_x(compositor, cam, -gf_mulfix(GF_PI, dx)); + view_exam_y(compositor, cam, gf_mulfix(GF_PI, dy)); + } + break; + case GF_NAVIGATE_ORBIT: + if (keys & GF_KEY_MOD_CTRL) { + view_translate_z(compositor, cam, gf_mulfix(dy, trans_scale)); + } else { + view_orbit_x(compositor, cam, -gf_mulfix(GF_PI, dx)); + view_orbit_y(compositor, cam, gf_mulfix(GF_PI, dy)); + } + break; + case GF_NAVIGATE_GAME: + view_pan_x(compositor, cam, -dx); + view_pan_y(compositor, cam, dy); + break; + } + compositor->grab_x = x; + compositor->grab_y = y; + return 1; + + case GF_EVENT_MOUSEWHEEL: + switch (cam->navigate_mode) { + /*FIXME- we'll likely need a "step" value for walk at some point*/ + case GF_NAVIGATE_WALK: + case GF_NAVIGATE_FLY: + view_pan_y(compositor, cam, gf_mulfix(key_pan, ev->mouse.wheel_pos)); + break; + case GF_NAVIGATE_VR: + view_zoom(compositor, cam, gf_mulfix(key_pan, ev->mouse.wheel_pos)); + break; + case GF_NAVIGATE_SLIDE: + case GF_NAVIGATE_EXAMINE: + case GF_NAVIGATE_ORBIT: + case GF_NAVIGATE_PAN: + if (is_pixel_metrics) { + view_translate_z(compositor, cam, gf_mulfix(trans_scale, ev->mouse.wheel_pos) * ((keys & GF_KEY_MOD_SHIFT) ? 4 : 1)); + } else { + view_translate_z(compositor, cam, ev->mouse.wheel_pos * ((keys & GF_KEY_MOD_SHIFT) ? 4 : 1)); + } + break; + } + return 1; + + case GF_EVENT_MOUSEUP: + if (ev->mouse.button==GF_MOUSE_LEFT) compositor->navigation_state = 0; + break; + + case GF_EVENT_KEYDOWN: + switch (ev->key.key_code) { + case GF_KEY_BACKSPACE: + gf_sc_reset_graphics(compositor); + return 1; + case GF_KEY_C: + compositor->collide_mode = compositor->collide_mode ? GF_COLLISION_NONE : GF_COLLISION_DISPLACEMENT; + return 1; + case GF_KEY_J: + if (cam->navigate_mode==GF_NAVIGATE_WALK) { + camera_jump(cam); + gf_sc_invalidate(compositor, NULL); + return 1; + } + break; + case GF_KEY_HOME: + if (!compositor->navigation_state) compositor_3d_reset_camera(compositor); + break; + case GF_KEY_END: + if (cam->navigate_mode==GF_NAVIGATE_GAME) { + cam->navigate_mode = GF_NAVIGATE_WALK; + compositor->navigation_state = 0; + return 1; + } + break; + case GF_KEY_LEFT: key_inv = -1; + case GF_KEY_RIGHT: + switch (cam->navigate_mode) { + case GF_NAVIGATE_SLIDE: + if (keys & GF_KEY_MOD_CTRL) view_pan_x(compositor, cam, key_inv * key_pan); + else view_translate_x(compositor, cam, key_inv * key_trans); + break; + case GF_NAVIGATE_EXAMINE: + if (keys & GF_KEY_MOD_CTRL) view_roll(compositor, cam, gf_mulfix(dx, trans_scale)); + else view_exam_x(compositor, cam, -key_inv * key_exam); + break; + case GF_NAVIGATE_ORBIT: + if (keys & GF_KEY_MOD_CTRL) view_translate_x(compositor, cam, key_inv * key_trans); + else view_orbit_x(compositor, cam, -key_inv * key_exam); + break; + case GF_NAVIGATE_GAME: view_translate_x(compositor, cam, key_inv * key_trans); break; + case GF_NAVIGATE_VR: view_pan_x(compositor, cam, -key_inv * key_pan); break; + /*walk/fly/pan*/ + default: + if (keys & GF_KEY_MOD_CTRL) view_translate_x(compositor, cam, key_inv * key_trans); + else view_pan_x(compositor, cam, -key_inv * key_pan); + break; + } + return 1; + case GF_KEY_DOWN: key_inv = -1; + case GF_KEY_UP: + if (keys & GF_KEY_MOD_ALT) return 0; + switch (cam->navigate_mode) { + case GF_NAVIGATE_SLIDE: + if (keys & GF_KEY_MOD_CTRL) view_translate_z(compositor, cam, key_inv * key_trans); + else view_translate_y(compositor, cam, key_inv * key_trans); + break; + case GF_NAVIGATE_EXAMINE: + if (keys & GF_KEY_MOD_CTRL) view_translate_z(compositor, cam, key_inv * key_trans); + else view_exam_y(compositor, cam, -key_inv * key_exam); + break; + case GF_NAVIGATE_ORBIT: + if (keys & GF_KEY_MOD_CTRL) view_translate_y(compositor, cam, key_inv * key_trans); + else view_orbit_y(compositor, cam, -key_inv * key_exam); + break; + case GF_NAVIGATE_PAN: + if (keys & GF_KEY_MOD_CTRL) view_translate_y(compositor, cam, key_inv * key_trans); + else view_pan_y(compositor, cam, key_inv * key_pan); + break; + case GF_NAVIGATE_GAME: view_translate_z(compositor, cam, key_inv * key_trans); break; + case GF_NAVIGATE_VR: + if (keys & GF_KEY_MOD_CTRL) view_zoom(compositor, cam, key_inv * key_pan); + else view_pan_y(compositor, cam, key_inv * key_pan); + break; + /*walk/fly*/ + default: + if (keys & GF_KEY_MOD_CTRL) view_pan_y(compositor, cam, key_inv * key_pan); + else view_translate_z(compositor, cam, key_inv * key_trans); + break; + } + return 1; + + case GF_KEY_PAGEDOWN: + if (keys & GF_KEY_MOD_CTRL) { view_zoom(compositor, cam, FIX_ONE/10); return 1; } + break; + case GF_KEY_PAGEUP: + if (keys & GF_KEY_MOD_CTRL) { view_zoom(compositor, cam, -FIX_ONE/10); return 1; } + break; + } + break; + } + return 0; +} + +#endif + +static void nav_set_zoom_trans_2d(GF_VisualManager *visual, Fixed zoom, Fixed dx, Fixed dy) +{ + compositor_2d_set_user_transform(visual->compositor, zoom, visual->compositor->trans_x + dx, visual->compositor->trans_y + dy, 0); +#ifndef GPAC_DISABLE_3D + if (visual->type_3d) camera_changed(visual->compositor, &visual->camera); +#endif +} + + +static Bool compositor_handle_navigation_2d(GF_VisualManager *visual, GF_Event *ev) +{ + Fixed x, y, dx, dy, key_trans, key_rot, zoom, new_zoom; + u32 navigation_mode; + s32 key_inv; + Bool is_pixel_metrics = visual->compositor->traverse_state->pixel_metrics; + u32 keys = visual->compositor->key_states; + + zoom = visual->compositor->zoom; + navigation_mode = visual->compositor->navigate_mode; +#ifndef GPAC_DISABLE_3D + if (visual->type_3d) navigation_mode = visual->camera.navigate_mode; +#endif + + if (!navigation_mode /*&& !(keys & GF_KEY_MOD_ALT) */) return 0; + + + x = y = 0; + /*renorm between -1, 1*/ + if (ev->type<=GF_EVENT_MOUSEWHEEL) { + x = INT2FIX(ev->mouse.x); + y = INT2FIX(ev->mouse.y); + } + dx = x - visual->compositor->grab_x; + dy = y - visual->compositor->grab_y; + if (!is_pixel_metrics) { dx /= visual->width; dy /= visual->height; } + + key_inv = 1; + key_trans = INT2FIX(2); + key_rot = GF_PI/100; + + if (keys & GF_KEY_MOD_SHIFT) { + dx *= 4; + dy *= 4; + key_rot *= 4; + key_trans*=4; + } + + if (!is_pixel_metrics) { key_trans /= visual->width;} + + switch (ev->type) { + case GF_EVENT_MOUSEDOWN: + /*left*/ + if (ev->mouse.button==GF_MOUSE_LEFT) { + visual->compositor->grab_x = x; + visual->compositor->grab_y = y; + visual->compositor->navigation_state = 1; + /*update zoom center*/ + if (keys & GF_KEY_MOD_CTRL) { + if (visual->center_coords) { + visual->compositor->trans_x -= visual->compositor->grab_x; + visual->compositor->trans_y -= visual->compositor->grab_y; + } else { + visual->compositor->trans_x -= visual->compositor->grab_x - INT2FIX(visual->width)/2; + visual->compositor->trans_y += INT2FIX(visual->height)/2 - visual->compositor->grab_y; + } + nav_set_zoom_trans_2d(visual, visual->compositor->zoom, 0, 0); + } + return 0; + } + break; + + case GF_EVENT_MOUSEUP: + if (ev->mouse.button==GF_MOUSE_LEFT) { + visual->compositor->navigation_state = 0; + return 0; + } + break; + + case GF_EVENT_MOUSEWHEEL: + if (navigation_mode != GF_NAVIGATE_SLIDE) return 0; + new_zoom = zoom + INT2FIX(ev->mouse.wheel_pos)/10; + nav_set_zoom_trans_2d(visual, new_zoom, 0, 0); + return 1; + + case GF_EVENT_MOUSEMOVE: + if (!visual->compositor->navigation_state) return 0; + visual->compositor->navigation_state++; + switch (navigation_mode) { + case GF_NAVIGATE_SLIDE: + if (keys & GF_KEY_MOD_CTRL) { + if (dy) { + new_zoom = zoom; + if (new_zoom > FIX_ONE) new_zoom += dy/20; + else new_zoom += dy/80; + nav_set_zoom_trans_2d(visual, new_zoom, 0, 0); + } + } else { + nav_set_zoom_trans_2d(visual, zoom, dx, dy); + } + break; + case GF_NAVIGATE_EXAMINE: + { + Fixed sin = gf_mulfix(GF_PI, dy) / visual->height; + visual->compositor->rotation += gf_asin(sin); + nav_set_zoom_trans_2d(visual, zoom, 0, 0); + } + break; + } + visual->compositor->grab_x = x; + visual->compositor->grab_y = y; + return 1; + case GF_EVENT_KEYDOWN: + switch (ev->key.key_code) { + case GF_KEY_BACKSPACE: + gf_sc_reset_graphics(visual->compositor); + return 1; + case GF_KEY_HOME: + if (!visual->compositor->navigation_state) { + visual->compositor->trans_x = visual->compositor->trans_y = 0; + visual->compositor->rotation = 0; + visual->compositor->zoom = FIX_ONE; + nav_set_zoom_trans_2d(visual, FIX_ONE, 0, 0); + } + return 1; + case GF_KEY_LEFT: key_inv = -1; + case GF_KEY_RIGHT: + if (navigation_mode == GF_NAVIGATE_SLIDE) { + nav_set_zoom_trans_2d(visual, zoom, key_inv*key_trans, 0); + } + else { + visual->compositor->rotation -= key_inv * key_rot; + nav_set_zoom_trans_2d(visual, zoom, 0, 0); + } + return 1; + case GF_KEY_DOWN: key_inv = -1; + case GF_KEY_UP: + if (navigation_mode == GF_NAVIGATE_SLIDE) { + if (keys & GF_KEY_MOD_CTRL) { + Fixed new_zoom = zoom; + if (new_zoom > FIX_ONE) new_zoom += key_inv*FIX_ONE/10; + else new_zoom += key_inv*FIX_ONE/20; + nav_set_zoom_trans_2d(visual, new_zoom, 0, 0); + } else { + nav_set_zoom_trans_2d(visual, zoom, 0, key_inv*key_trans); + } + } + else { + visual->compositor->rotation += key_inv*key_rot; + nav_set_zoom_trans_2d(visual, zoom, 0, 0); + } + return 1; + } + break; + } + return 0; +} + + +Bool compositor_handle_navigation(GF_Compositor *compositor, GF_Event *ev) +{ + if (!compositor->scene) return 0; +#ifndef GPAC_DISABLE_3D + if ( (compositor->visual->type_3d>1) || compositor->active_layer) { + return compositor_handle_navigation_3d(compositor, ev); + } +#endif + return compositor_handle_navigation_2d(compositor->visual, ev); +} diff --git a/src/compositor/nodes_stacks.h b/src/compositor/nodes_stacks.h new file mode 100644 index 0000000..cffc6e2 --- /dev/null +++ b/src/compositor/nodes_stacks.h @@ -0,0 +1,301 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef NODES_STACKS_H +#define NODES_STACKS_H + +#include +#include + +#ifndef GPAC_DISABLE_SVG +#include +#endif + +#include "drawable.h" + + +void compositor_init_audiosource(GF_Compositor *compositor, GF_Node *node); +void compositor_audiosource_modified(GF_Node *node); +void compositor_init_audioclip(GF_Compositor *compositor, GF_Node *node); +void compositor_audioclip_modified(GF_Node *node); +void compositor_init_audiobuffer(GF_Compositor *compositor, GF_Node *node); +void compositor_audiobuffer_modified(GF_Node *node); +void compositor_init_animationstream(GF_Compositor *compositor, GF_Node *node); +void compositor_animationstream_modified(GF_Node *node); +void compositor_init_timesensor(GF_Compositor *compositor, GF_Node *node); +void compositor_timesensor_modified(GF_Node *node); +void compositor_init_imagetexture(GF_Compositor *compositor, GF_Node *node); +void compositor_imagetexture_modified(GF_Node *node); +void compositor_init_movietexture(GF_Compositor *compositor, GF_Node *node); +void compositor_movietexture_modified(GF_Node *node); +void compositor_init_pixeltexture(GF_Compositor *compositor, GF_Node *node); + +GF_TextureHandler *it_get_texture(GF_Node *node); +GF_TextureHandler *mt_get_texture(GF_Node *node); +GF_TextureHandler *pt_get_texture(GF_Node *node); + + +/*unregister bindable from all bindable stacks listed, and bind their top if needed*/ +void PreDestroyBindable(GF_Node *bindable, GF_List *stack_list); +/*returns isBound*/ +Bool Bindable_GetIsBound(GF_Node *bindable); +/*sets isBound*/ +void Bindable_SetIsBound(GF_Node *bindable, Bool val); +/*generic on_set_bind for all bindables*/ +void Bindable_OnSetBind(GF_Node *bindable, GF_List *stack_list); +/*remove all bindable references to this stack and destroy chain*/ +void BindableStackDelete(GF_List *stack); +/*for user-modif of viewport/viewpoint*/ +void Bindable_SetSetBind(GF_Node *bindable, Bool val); + + +/*Viewport/Viewpoint/NavigationInfo/Fog stack - exported for generic bindable handling*/ +typedef struct +{ + GF_List *reg_stacks; + Bool prev_was_bound; + GF_Matrix world_view_mx; + u32 last_sim_time; +} ViewStack; + +typedef struct +{ + /*for image background*/ + GF_TextureHandler txh; + /*list of all background stacks using us*/ + GF_List *reg_stacks; + /*list of allocated background status (used to store bounds info & DrawableContext for Layer2D) + there is one status per entry in the reg_stack*/ + GF_List *status_stack; + Drawable *drawable; + +#ifndef GPAC_DISABLE_3D + GF_Mesh *mesh; + GF_BBox prev_bounds; +#endif + +} Background2DStack; + +#ifndef GPAC_DISABLE_3D +typedef struct +{ + GF_Compositor *compositor; + /*keep track of background stacks using us*/ + GF_List *reg_stacks; + GF_Mesh *sky_mesh, *ground_mesh; + MFFloat sky_col, ground_col; + MFFloat sky_ang, ground_ang; + + GF_Mesh *front_mesh, *back_mesh, *top_mesh, *bottom_mesh, *left_mesh, *right_mesh; + GF_TextureHandler txh_front, txh_back, txh_top, txh_bottom, txh_left, txh_right; +} BackgroundStack; +#endif + + +typedef struct +{ + GF_Compositor *compositor; + u32 last_mod_time; +} LinePropStack; +void compositor_init_lineprops(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_background2d(GF_Compositor *compositor, GF_Node *node); +void compositor_background2d_modified(GF_Node *node); +DrawableContext *b2d_get_context(M_Background2D *ptr, GF_List *from_stack); + + +void compositor_init_bitmap(GF_Compositor *compositor, GF_Node *node); +void compositor_init_colortransform(GF_Compositor *compositor, GF_Node *node); +void compositor_init_circle(GF_Compositor *compositor, GF_Node *node); +void compositor_init_curve2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_ellipse(GF_Compositor *compositor, GF_Node *node); +void compositor_init_group(GF_Compositor *compositor, GF_Node *node); +void compositor_init_orderedgroup(GF_Compositor *compositor, GF_Node *node); +void compositor_init_pointset2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_rectangle(GF_Compositor *compositor, GF_Node *node); +void compositor_init_shape(GF_Compositor *compositor, GF_Node *node); +void compositor_init_switch(GF_Compositor *compositor, GF_Node *node); +void compositor_init_transform2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_transformmatrix2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_indexed_line_set2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_indexed_face_set2d(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_viewport(GF_Compositor *compositor, GF_Node *node); + +void tr_mx2d_get_matrix(GF_Node *n, GF_Matrix2D *mat); + +void compositor_init_anchor(GF_Compositor *compositor, GF_Node *node); +void compositor_init_disc_sensor(GF_Compositor *compositor, GF_Node *node); +void compositor_init_plane_sensor2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_proximity_sensor2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_touch_sensor(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_group(GF_Compositor *compositor, GF_Node *node); +void compositor_init_static_group(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_sound2d(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_radial_gradient(GF_Compositor *compositor, GF_Node *node); +void compositor_init_linear_gradient(GF_Compositor *compositor, GF_Node *node); +void compositor_init_mattetexture(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_layer2d(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_form(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_layout(GF_Compositor *compositor, GF_Node *node); +void compositor_layout_modified(GF_Compositor *compositor, GF_Node *node); +GF_SensorHandler *compositor_mpeg4_layout_get_sensor_handler(GF_Node *node); + +void compositor_init_path_layout(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_compositetexture2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_compositetexture3d(GF_Compositor *compositor, GF_Node *node); +GF_TextureHandler *compositor_get_composite_texture(GF_Node *node); +void compositor_adjust_scale(GF_Node *node, Fixed *sx, Fixed *sy); +Bool compositor_is_composite_texture(GF_Node *appear); +Bool compositor_compositetexture_handle_event(GF_Compositor *compositor, GF_Event *ev); +Bool compositor_handle_navigation(GF_Compositor *compositor, GF_Event *ev); + +void compositor_init_text(GF_Compositor *compositor, GF_Node *node); + +#ifndef GPAC_DISABLE_3D + +void compositor_init_billboard(GF_Compositor *compositor, GF_Node *node); +void compositor_init_collision(GF_Compositor *compositor, GF_Node *node); +void compositor_init_lod(GF_Compositor *compositor, GF_Node *node); +void compositor_init_transform(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_proximity_sensor(GF_Compositor *compositor, GF_Node *node); +void compositor_init_plane_sensor(GF_Compositor *compositor, GF_Node *node); +void compositor_init_cylinder_sensor(GF_Compositor *compositor, GF_Node *node); +void compositor_init_sphere_sensor(GF_Compositor *compositor, GF_Node *node); +void compositor_init_visibility_sensor(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_box(GF_Compositor *compositor, GF_Node *node); +void compositor_init_cone(GF_Compositor *compositor, GF_Node *node); +void compositor_init_cylinder(GF_Compositor *compositor, GF_Node *node); +void compositor_init_sphere(GF_Compositor *compositor, GF_Node *node); +void compositor_init_point_set(GF_Compositor *compositor, GF_Node *node); +void compositor_init_ifs(GF_Compositor *compositor, GF_Node *node); +void compositor_init_ils(GF_Compositor *compositor, GF_Node *node); +void compositor_init_elevation_grid(GF_Compositor *compositor, GF_Node *node); +void compositor_init_extrusion(GF_Compositor *compositor, GF_Node *node); +void compositor_init_non_linear_deformer(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_sound(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_viewpoint(GF_Compositor *compositor, GF_Node *node); +void compositor_init_navigation_info(GF_Compositor *compositor, GF_Node *node); +void compositor_init_fog(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_spot_light(GF_Compositor *compositor, GF_Node *node); +void compositor_init_point_light(GF_Compositor *compositor, GF_Node *node); +void compositor_init_directional_light(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_layer3d(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_background(GF_Compositor *compositor, GF_Node *node); +void compositor_background_modified(GF_Node *node); +#endif + +/*X3D nodes*/ +void compositor_init_disk2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_arc2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_polyline2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_triangle_set2d(GF_Compositor *compositor, GF_Node *node); + + +#ifndef GPAC_DISABLE_3D +void compositor_init_polypoint2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_lineset(GF_Compositor *compositor, GF_Node *node); +void compositor_init_triangle_set(GF_Compositor *compositor, GF_Node *node); +void compositor_init_indexed_triangle_set(GF_Compositor *compositor, GF_Node *node); +void compositor_init_triangle_strip_set(GF_Compositor *compositor, GF_Node *node); +void compositor_init_indexed_triangle_strip_set(GF_Compositor *compositor, GF_Node *node); +void compositor_init_triangle_fan_set(GF_Compositor *compositor, GF_Node *node); +void compositor_init_indexed_triangle_fan_set(GF_Compositor *compositor, GF_Node *node); +#endif + + +#ifndef GPAC_DISABLE_SVG +void compositor_init_svg_svg(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_g(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_switch(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_rect(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_circle(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_ellipse(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_line(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_polyline(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_polygon(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_path(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_a(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_linearGradient(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_radialGradient(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_solidColor(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_stop(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_svg_image(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_video(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_audio(GF_Compositor *compositor, GF_Node *node, Bool slaved_timing); +void compositor_init_svg_use(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_animation(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_foreign_object(GF_Compositor *compositor, GF_Node *node); + + +void compositor_init_svg_text(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_tspan(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_textarea(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_tbreak(GF_Compositor *compositor, GF_Node *node); + +void compositor_init_svg_font(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_glyph(GF_Compositor *compositor, GF_Node *node); +void compositor_init_svg_font_face_uri(GF_Compositor *compositor, GF_Node *node); + +GF_TextureHandler *compositor_svg_get_gradient_texture(GF_Node *node); +GF_TextureHandler *compositor_svg_get_image_texture(GF_Node *node); + +Bool compositor_svg_get_viewport(GF_Node *n, GF_Rect *rc); +void svg_pause_animation(GF_Node *n, Bool pause); +void svg_pause_audio(GF_Node *n, Bool pause); +void svg_pause_video(GF_Node *n, Bool pause); + +#endif + +GF_TextureHandler *compositor_mpeg4_get_gradient_texture(GF_Node *node); + +/*hardcoded protos*/ +void compositor_init_hardcoded_proto(GF_Compositor *compositor, GF_Node *node); + +#ifndef GPAC_DISABLE_3D +void compositor_extrude_text(GF_Node *node, GF_TraverseState *tr_state, GF_Mesh *mesh, MFVec3f *thespine, Fixed creaseAngle, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool txAlongSpine); +#endif + +void compositor_init_texture_text(GF_Compositor *compositor, GF_Node *node); + + + +#endif /*NODES_STACKS_H*/ + diff --git a/src/compositor/offscreen_cache.c b/src/compositor/offscreen_cache.c new file mode 100644 index 0000000..80ebd85 --- /dev/null +++ b/src/compositor/offscreen_cache.c @@ -0,0 +1,805 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Yi-Zhen Zhang, Jean Le Feuvre + * + * Copyright (c) ENST 2005-200X + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "offscreen_cache.h" + +#include "visual_manager.h" +#include "mpeg4_grouping.h" +#include "texturing.h" + +#define NUM_STATS_FRAMES 2 +#define MIN_OBJECTS_IN_CACHE 2 + + +//#define CACHE_DEBUG_ALPHA +//#define CACHE_DEBUG_CENTER + +void group_cache_draw(GroupCache *cache, GF_TraverseState *tr_state) +{ + GF_TextureHandler *old_txh = tr_state->ctx->aspect.fill_texture; + /*switch the texture to our offscreen cache*/ + tr_state->ctx->aspect.fill_texture = &cache->txh; + + if (! tr_state->visual->DrawBitmap(tr_state->visual, tr_state, tr_state->ctx, NULL)) { + visual_2d_texture_path(tr_state->visual, cache->drawable->path, tr_state->ctx, tr_state); + } + tr_state->ctx->aspect.fill_texture = old_txh; +} + +GroupCache *group_cache_new(GF_Compositor *compositor, GF_Node *node) +{ + GroupCache *cache; + GF_SAFEALLOC(cache, GroupCache); + gf_sc_texture_setup(&cache->txh, compositor, node); + cache->drawable = drawable_new(); + /*draw the cache through traverse callback*/ + cache->drawable->flags |= DRAWABLE_USE_TRAVERSE_DRAW; + cache->drawable->node = node; + cache->opacity = FIX_ONE; + gf_sc_texture_allocate(&cache->txh); + return cache; +} + +void group_cache_del(GroupCache *cache) +{ + drawable_del_ex(cache->drawable, cache->txh.compositor); + if (cache->txh.data) free(cache->txh.data); + gf_sc_texture_release(&cache->txh); + gf_sc_texture_destroy(&cache->txh); + free(cache); +} + +void group_cache_setup(GroupCache *cache, GF_Rect *path_bounds, GF_IRect *pix_bounds, GF_Compositor *compositor, Bool for_gl) +{ + /*setup texture */ + cache->txh.compositor = compositor; + cache->txh.height = pix_bounds->height; + cache->txh.width = pix_bounds->width; + + cache->txh.stride = pix_bounds->width * 4; + cache->txh.pixelformat = for_gl ? GF_PIXEL_RGBA : GF_PIXEL_ARGB; + cache->txh.transparent = 1; + + if (cache->txh.data) free(cache->txh.data); +#ifdef CACHE_DEBUG_ALPHA + cache->txh.stride = pix_bounds->width * 3; + cache->txh.pixelformat = GF_PIXEL_RGB_24; + cache->txh.transparent = 0; +#endif + + cache->txh.data = (u8 *) malloc (sizeof(char) * cache->txh.stride * cache->txh.height); + memset(cache->txh.data, 0x0, sizeof(char) * cache->txh.stride * cache->txh.height); + /*the path of drawable_cache is a rectangle one that is the the bound of the object*/ + gf_path_reset(cache->drawable->path); + + /*set a rectangle to the path + Attention, we want to center the cached bitmap at the center of the screen (main visual), so we use + the local coordinate to parameterize the path*/ + gf_path_add_rect_center(cache->drawable->path, + path_bounds->x + path_bounds->width/2, + path_bounds->y - path_bounds->height/2, + path_bounds->width, path_bounds->height); +} + +Bool group_cache_traverse(GF_Node *node, GroupCache *cache, GF_TraverseState *tr_state, Bool force_recompute) +{ + DrawableContext *group_ctx = NULL; + GF_ChildNodeItem *l; + + if (!cache) return 0; + + /*do we need to recompute the cache*/ + if (cache->force_recompute) { + force_recompute = 1; + cache->force_recompute = 0; + } + else if (gf_node_dirty_get(node) & GF_SG_CHILD_DIRTY) { + force_recompute = 1; + } + + /*we need to redraw the group in an offscreen visual*/ + if (force_recompute) { + GF_Matrix2D backup; + GF_IRect rc1, rc2; + u32 type_3d; + u32 prev_flags; + GF_Rect cache_bounds; + GF_SURFACE offscreen_surface, old_surf; + GF_Raster2D *r2d = tr_state->visual->compositor->rasterizer; + DrawableContext *child_ctx; + Fixed temp_x, temp_y, scale_x, scale_y; + + /*step 1 : store current state and indicate children should not be cached*/ + tr_state->in_group_cache = 1; + prev_flags = tr_state->direct_draw; + /*store the current transform matrix, create a new one for group_cache*/ + gf_mx2d_copy(backup, tr_state->transform); + gf_mx2d_init(tr_state->transform); + + type_3d = 0; +#ifndef GPAC_DISABLE_3D + /*force 2D rendering*/ + type_3d = tr_state->visual->type_3d; + tr_state->visual->type_3d = 0; +#endif + + /*step 2: collect the bounds of all children*/ + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + cache_bounds.width = cache_bounds.height = 0; + l = ((GF_ParentNode*)node)->children; + while (l) { + tr_state->bounds.width = tr_state->bounds.height = 0; + gf_node_traverse(l->node, tr_state); + l = l->next; + gf_rect_union(&cache_bounds, &tr_state->bounds); + } + tr_state->traversing_mode = TRAVERSE_SORT; + + if (!cache_bounds.width || !cache_bounds.height) { + tr_state->in_group_cache = 0; + tr_state->direct_draw = prev_flags; + gf_mx2d_copy(tr_state->transform, backup); +#ifndef GPAC_DISABLE_3D + tr_state->visual->type_3d = type_3d; +#endif + return 0; + } + + /*step 3: insert a DrawableContext for this group in the display list*/ + group_ctx = drawable_init_context_mpeg4(cache->drawable, tr_state); + + /*step 4: now we have the bounds: + allocate the offscreen memory + create temp raster visual & attach to buffer + override the tr_state->visual->the_surface with the temp raster + add translation (shape is not always centered) + setup top clipers + */ + old_surf = tr_state->visual->raster_surface; + offscreen_surface = r2d->surface_new(r2d, tr_state->visual->center_coords); /*a new temp raster visual*/ + tr_state->visual->raster_surface = offscreen_surface; + + /*use current surface coordinate scaling to compute the cache*/ +#ifdef GF_SR_USE_VIDEO_CACHE + scale_x = tr_state->visual->compositor->cache_scale * backup.m[0] / 100; + scale_y = tr_state->visual->compositor->cache_scale * backup.m[4] / 100; +#else + scale_x = backup.m[0]; + scale_y = backup.m[4]; +#endif + + if (scale_x<0) scale_x = -scale_x; + if (scale_y<0) scale_y = -scale_y; + + cache->scale = MAX(scale_x, scale_y); + tr_state->bounds = cache_bounds; + gf_mx2d_add_scale(&tr_state->transform, scale_x, scale_y); + gf_mx2d_apply_rect(&tr_state->transform, &cache_bounds); + + rc1 = gf_rect_pixelize(&cache_bounds); + if (rc1.width % 2) rc1.width++; + if (rc1.height%2) rc1.height++; + + /* Initialize the group cache with the scaled pixelized bounds for texture but the original bounds for path*/ + group_cache_setup(cache, &tr_state->bounds, &rc1, tr_state->visual->compositor, type_3d); + + + /*attach the buffer to visual*/ + r2d->surface_attach_to_buffer(offscreen_surface, cache->txh.data, + cache->txh.width, + cache->txh.height, + cache->txh.stride, + cache->txh.pixelformat); + + + /*recompute the bounds with the final scaling used*/ + scale_x = gf_divfix(INT2FIX(rc1.width), tr_state->bounds.width); + scale_y = gf_divfix(INT2FIX(rc1.height), tr_state->bounds.height); + gf_mx2d_init(tr_state->transform); + gf_mx2d_add_scale(&tr_state->transform, scale_x, scale_y); + cache_bounds = tr_state->bounds; + gf_mx2d_apply_rect(&tr_state->transform, &cache_bounds); + + /*centered the bitmap on the visual*/ + temp_x = -cache_bounds.x; + temp_y = -cache_bounds.y; + if (tr_state->visual->center_coords) { + temp_x -= cache_bounds.width/2; + temp_y += cache_bounds.height/2; + } else { + temp_y += cache_bounds.height; + } + gf_mx2d_add_translation(&tr_state->transform, temp_x, temp_y); + + /*override top clippers*/ + rc1 = tr_state->visual->surf_rect; + rc2 = tr_state->visual->top_clipper; + tr_state->visual->surf_rect.width = cache->txh.width; + tr_state->visual->surf_rect.height = cache->txh.height; + if (tr_state->visual->center_coords) { + tr_state->visual->surf_rect.y = cache->txh.height/2; + tr_state->visual->surf_rect.x = -1 * (s32) cache->txh.width/2; + } else { + tr_state->visual->surf_rect.y = cache->txh.height; + tr_state->visual->surf_rect.x = 0; + } + tr_state->visual->top_clipper = tr_state->visual->surf_rect; + + + /*step 5: traverse subtree in direct draw mode*/ + tr_state->direct_draw = 1; + group_ctx->flags &= ~CTX_NO_ANTIALIAS; + + l = ((GF_ParentNode*)node)->children; + while (l) { + gf_node_traverse(l->node, tr_state); + l = l->next; + } + /*step 6: reset all contexts after the current group one*/ + child_ctx = group_ctx->next; + while (child_ctx && child_ctx->drawable) { + drawable_reset_bounds(child_ctx->drawable, tr_state->visual); + child_ctx->drawable = NULL; + child_ctx = child_ctx->next; + } + + /*and set ourselves as the last context on the main visual*/ + tr_state->visual->cur_context = group_ctx; + + /*restore state and destroy whatever needs to be cleaned*/ + gf_mx2d_copy(tr_state->transform, backup); + tr_state->in_group_cache = 0; + tr_state->direct_draw = prev_flags; + r2d->surface_delete(offscreen_surface); + tr_state->visual->raster_surface = old_surf; + tr_state->traversing_mode = TRAVERSE_SORT; + +#ifndef GPAC_DISABLE_3D + tr_state->visual->type_3d = type_3d; +#endif + tr_state->visual->surf_rect = rc1; + tr_state->visual->top_clipper = rc2; + + /*update texture*/ + cache->txh.transparent = 1; + gf_sc_texture_set_data(&cache->txh); + gf_sc_texture_push_image(&cache->txh, 0, type_3d ? 0 : 1); + } + /*just setup the context*/ + else { + group_ctx = drawable_init_context_mpeg4(cache->drawable, tr_state); + } + assert(group_ctx); + group_ctx->flags |= CTX_NO_ANTIALIAS; + group_ctx->aspect.fill_color = GF_COL_ARGB_FIXED(cache->opacity, FIX_ONE, FIX_ONE, FIX_ONE); + group_ctx->aspect.fill_texture = &cache->txh; + + if (!cache->opacity) { + group_ctx->drawable = NULL; + return 0; + } + + if (gf_node_dirty_get(node)) group_ctx->flags |= CTX_TEXTURE_DIRTY; + + { +#ifdef CACHE_DEBUG_CENTER + GF_Matrix2D mx; + gf_mx2d_copy(mx, tr_state->transform); + gf_mx2d_init(tr_state->transform); +#endif + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + if (!cache->drawable->mesh) { + cache->drawable->mesh = new_mesh(); + mesh_from_path(cache->drawable->mesh, cache->drawable->path); + } + visual_3d_draw_from_context(group_ctx, tr_state); + group_ctx->drawable = NULL; + } else +#endif + drawable_finalize_sort(group_ctx, tr_state, NULL); + +#ifdef CACHE_DEBUG_CENTER + gf_mx2d_copy(tr_state->transform, mx); +#endif + } + + return (force_recompute==1); +} + + +#ifdef GF_SR_USE_VIDEO_CACHE + +/*guarentee the tr_state->candidate has the lowest delta value*/ +static void group_cache_insert_entry(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state) +{ + u32 i, count; + GF_List *cache_candidates = tr_state->visual->compositor->cached_groups; + GroupingNode2D *current; + + current = NULL; + count = gf_list_count(cache_candidates); + for (i=0; ipriority >= group->priority) { + gf_list_insert(cache_candidates, group, i); + break; + } + } + if (i==count) + gf_list_add(cache_candidates, group); + + tr_state->visual->compositor->video_cache_current_size += group->cached_size; + /*log the information*/ + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE]\tAdding object %s\tObjects: %d\tSlope: %g\tSize: %d\tTime: %d\n", + gf_node_get_log_name(node), + group->nb_objects, + FIX2FLT(group->priority), + group->cached_size, + group->traverse_time)); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Status (KB): Max: %d\tUsed: %d\tNb Groups: %d\n", + tr_state->visual->compositor->video_cache_max_size, + tr_state->visual->compositor->video_cache_current_size, + gf_list_count(tr_state->visual->compositor->cached_groups) + )); +} + + +static Bool gf_cache_remove_entry(GF_Compositor *compositor, GF_Node *node, GroupingNode2D *group) +{ + u32 bytes_remove = 0; + GF_List *cache_candidates = compositor->cached_groups; + + /*auto mode*/ + if (!group) { + group = gf_list_get(cache_candidates, 0); + if (!group) return 0; + /*remove entry*/ + gf_list_rem(cache_candidates, 0); + node = NULL; + } else { + /*remove entry if present*/ + if (gf_list_del_item(cache_candidates, group)<0) + return 0; + } + + /*disable the caching flag of the group if it was marked as such*/ + if(group->flags & GROUP_IS_CACHABLE) { + group->flags &= ~GROUP_IS_CACHABLE; + /*the discarded bytes*/ + bytes_remove = group->cached_size; + } + + /*indicates cache destruction for next frame*/ + if (group->cache && (group->flags & GROUP_IS_CACHED)) { + group->flags &= ~GROUP_IS_CACHED; + /*the discarded bytes*/ + bytes_remove = group->cached_size; + } + + if (bytes_remove == 0) return 0; + + assert(compositor->video_cache_current_size >= bytes_remove); + compositor->video_cache_current_size -= bytes_remove; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Removing cache %s:\t Objects: %d\tSlope: %g\tBytes: %d\tTime: %d\n", + gf_node_get_log_name(node), + group->nb_objects, + FIX2FLT(group->priority), + group->cached_size, + FIX2FLT(group->traverse_time))); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Status (B): Max: %d\tUsed: %d\tNb Groups: %d\n", + compositor->video_cache_max_size, + compositor->video_cache_current_size, + gf_list_count(compositor->cached_groups) + )); + return 1; +} + + +/**/ +Bool group_2d_cache_traverse(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state) +{ + Bool is_dirty = gf_node_dirty_get(node) & GF_SG_CHILD_DIRTY; + Bool zoom_changed = tr_state->visual->compositor->zoom_changed; + Bool needs_recompute = 0; + + /*we are currently in a group cache, regular traversing*/ + if (tr_state->in_group_cache) return 0; + + /*draw mode*/ + if (tr_state->traversing_mode == TRAVERSE_DRAW_2D) { + /*shall never happen*/ + assert(group->cache); + /*draw it*/ + group_cache_draw(group->cache, tr_state); + return 1; + } + /*other modes than sorting, use regular traversing*/ + if (tr_state->traversing_mode != TRAVERSE_SORT) return 0; + + /*this is not an offscreen group*/ + if (!(group->flags & GROUP_IS_CACHED) ) { + Bool cache_on = 0; + + /*group cache has been turned on in the previous frame*/ + if (!is_dirty && (group->flags & GROUP_IS_CACHABLE)) { + group->flags |= GROUP_IS_CACHED; + group->flags &= ~GROUP_IS_CACHABLE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache on - size %d\n", gf_node_get_log_name(node), group->cached_size )); + cache_on = 1; + } + /*group cache has been turned off in the previous frame*/ + else if (group->cache) { + group_cache_del(group->cache); + group->cache = NULL; + group->changed = is_dirty; + group->nb_stats_frame = 0; + group->traverse_time = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache off\n", gf_node_get_log_name(node) )); + return 0; + } + + if (!cache_on) { + if (is_dirty) { + group->changed = 1; + } + /*ask for stats again*/ + else if (group->changed) { + group->changed = 0; + group->nb_stats_frame = 0; + group->traverse_time = 0; + } else if (zoom_changed) { + group->nb_stats_frame = 0; + group->traverse_time = 0; + } + if (is_dirty || (group->nb_stats_frame < NUM_STATS_FRAMES)) { + /*force direct draw mode*/ + if (!is_dirty) + tr_state->visual->compositor->traverse_state->invalidate_all = 1; + /*force redraw*/ + tr_state->visual->compositor->draw_next_frame = 1; + } + return 0; + } + } + /*cache is dirty*/ + else if (is_dirty) { + /*permanent cache, just recompute*/ + if (group->flags & GROUP_PERMANENT_CACHE) { + group->changed = 1; + group->cache->force_recompute = 1; + } + /*otherwise destroy the cache*/ + else if (group->cache) { + gf_cache_remove_entry(tr_state->visual->compositor, node, group); + group_cache_del(group->cache); + group->cache = NULL; + group->flags &= ~GROUP_IS_CACHED; + group->changed = 0; + group->nb_stats_frame = 0; + group->traverse_time = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache off due to sub-tree modifications\n", gf_node_get_log_name(node) )); + return 0; + } + } + /*zoom has changed*/ + else if (zoom_changed) { + /*permanent cache, just recompute*/ + if (group->flags & GROUP_PERMANENT_CACHE) { + group->changed = 1; + group->cache->force_recompute = 1; + } + /*otherwise check if we accept this scale ratio or if we must recompute*/ + else if (group->cache) { + Fixed scale = MAX(tr_state->transform.m[0], tr_state->transform.m[4]); + + if (100*scale >= group->cache->scale*(100 + tr_state->visual->compositor->cache_tolerance)) + zoom_changed = 1; + else if ((100+tr_state->visual->compositor->cache_tolerance)*scale <= 100*group->cache->scale) + zoom_changed = 1; + else + zoom_changed = 0; + + if (zoom_changed) { + gf_cache_remove_entry(tr_state->visual->compositor, node, group); + group_cache_del(group->cache); + group->cache = NULL; + group->flags &= ~GROUP_IS_CACHED; + group->changed = 0; + group->nb_stats_frame = 0; + group->traverse_time = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache off due to zoom changes\n", gf_node_get_log_name(node) )); + return 0; + } + } + } + + /*keep track of this cache object for later removal*/ + if (!(group->flags & GROUP_PERMANENT_CACHE)) + gf_list_add(tr_state->visual->compositor->cached_groups_queue, group); + + if (!group->cache) { + /*ALLOCATE THE CACHE*/ + group->cache = group_cache_new(tr_state->visual->compositor, node); + needs_recompute = 1; + } + + /*cache has been modified due to node changes, reset stats*/ + group_cache_traverse(node, group->cache, tr_state, needs_recompute); + return 1; +} + + +Bool group_cache_compute_stats(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state, DrawableContext *first_child, Bool skip_first_child) +{ + GF_Rect group_bounds; + DrawableContext *ctx; + u32 nb_segments, nb_objects; + u32 alpha_pixels, opaque_pixels, area_world; + u32 video_cache_max_size, cache_size, prev_cache_size; + u32 i; + GF_RectArray ra; + + /*compute stats*/ + nb_objects = 0; + nb_segments = 0; + alpha_pixels = opaque_pixels = 0; + prev_cache_size = group->cached_size; + /*reset bounds*/ + group_bounds.width = group_bounds.height = 0; + video_cache_max_size = tr_state->visual->compositor->video_cache_max_size; + + /*never cache root node - this should be refined*/ + if (gf_node_get_parent(node, 0) == NULL) goto group_reject; + if (!group->traverse_time) goto group_reject; + + ra_init(&ra); + + ctx = first_child; + if (!first_child) ctx = tr_state->visual->context; + if (skip_first_child) ctx = ctx->next; + /*compute properties for the sub display list*/ + while (ctx && ctx->drawable) { + //Fixed area; + u32 alpha_comp; + + /*get area and compute alpha/opaque coverage*/ + alpha_comp = GF_COL_A(ctx->aspect.fill_color); + + /*add to group area*/ + gf_rect_union(&group_bounds, &ctx->bi->unclip); + nb_objects++; + + /*no alpha*/ + if ((alpha_comp==0xFF) + /*no transparent texture*/ + && (!ctx->aspect.fill_texture || !ctx->aspect.fill_texture->transparent) + ) { + + ra_union_rect(&ra, &ctx->bi->clip); + } + nb_segments += ctx->drawable->path->n_points; + + ctx = ctx->next; + } + + if ( + /*TEST 1: discard visually empty groups*/ + (!group_bounds.width || !group_bounds.height) + || + /*TEST 2: discard small groups*/ + (nb_objectsvisual->compositor->cache_scale * group_bounds.width / 100; + group_bounds.height = tr_state->visual->compositor->cache_scale * group_bounds.height / 100; + cache_size = FIX2INT(group_bounds.width) * FIX2INT(group_bounds.height) * 4 /* pixelFormat is ARGB*/; + + /*TEST 5: cache is less than 10x10 pixels: discard*/ + if (cache_size < 400) goto group_reject; + /*TEST 6: cache is larger than our allowed memory: discard*/ + if (cache_size>=video_cache_max_size) { + tr_state->cache_too_small = 1; + goto group_reject; + } + + /*compute the delta value for measuring the group importance for later discard + (avg_time - Tcache) / (size_cache - drawable_gain) + */ + group->priority = INT2FIX(nb_objects*1024*group->traverse_time) / cache_size / group->nb_stats_frame; + /*OK, group is a good candidate for caching*/ + group->nb_objects = nb_objects; + group->cached_size = cache_size; + + + /*we're moving from non-cached to cached*/ + if (!(group->flags & GROUP_IS_CACHABLE)) { + group->flags |= GROUP_IS_CACHABLE; + tr_state->visual->compositor->draw_next_frame = 1; + + /*insert the candidate and then update the list in order*/ + group_cache_insert_entry(node, group, tr_state); + /*keep track of this cache object for later removal*/ + gf_list_add(tr_state->visual->compositor->cached_groups_queue, group); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning cache on during stat pass for node %s - %d kb used in all caches\n", gf_node_get_log_name(node), tr_state->visual->compositor->video_cache_current_size )); + } + /*update memory occupation*/ + else { + tr_state->visual->compositor->video_cache_current_size -= prev_cache_size; + tr_state->visual->compositor->video_cache_current_size += group->cached_size; + + if (group->cache) + group->cache->force_recompute = 1; + } + return 1; + + +group_reject: + group->nb_objects = nb_objects; + + if ((group->flags & GROUP_IS_CACHABLE) || group->cache) { + group->flags &= ~GROUP_IS_CACHABLE; + + if (group->cache) { + group_cache_del(group->cache); + group->cache = NULL; + group->flags &= ~GROUP_IS_CACHED; + } + gf_list_del_item(tr_state->visual->compositor->cached_groups, group); + tr_state->visual->compositor->video_cache_current_size -= cache_size; + } + +#if 0 + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] REJECT %s\tObjects: %d\tSlope: %g\tBytes: %d\tTime: %d\n", + gf_node_get_log_name(node), + group->nb_objects, + FIX2FLT(group->priority), + group->cached_size, + group->traverse_time + )); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Status (B): Max: %d\tUsed: %d\tNb Groups: %d\n", + tr_state->visual->compositor->video_cache_max_size, + tr_state->visual->compositor->video_cache_current_size, + gf_list_count(tr_state->visual->compositor->cached_groups) + )); +#endif + return 0; +} + + +void group_2d_cache_evaluate(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state, DrawableContext *first_child, Bool skip_first_child, u32 last_cache_idx) +{ + u32 nb_cache_added, i; + Fixed priority; + GF_Compositor *compositor = tr_state->visual->compositor; + + /*first frame is unusable for stats because lot of time is being spent building the path and allocating + the drawable contexts*/ + if (!compositor->video_cache_max_size || !compositor->frame_number || group->changed || tr_state->in_group_cache) { + group->traverse_time = 0; + return; + } + + if (group->nb_stats_frame < NUM_STATS_FRAMES) { + group->nb_stats_frame++; + tr_state->visual->compositor->draw_next_frame = 1; + return; + } + if (group->nb_stats_frame > NUM_STATS_FRAMES) return; + group->nb_stats_frame++; + + /*the way to produce the result of memory-computation optimization*/ + if (group_cache_compute_stats(node, group, tr_state, first_child, skip_first_child)) { + Fixed avg_time; + nb_cache_added = gf_list_count(compositor->cached_groups_queue) - last_cache_idx - 1; + + /*force redraw*/ + tr_state->visual->compositor->draw_next_frame = 1; + + /*update priority by adding cache priorities */ + avg_time = group->priority * group->cached_size / (1024*group->nb_objects); + + /*remove all queued cached groups of this node's children*/ + for (i=0; icached_groups_queue, last_cache_idx); + /*we have been computed the prioirity of the group using a cached subtree, update + the priority to reflect that the new cache won't use a cached subtree*/ + if (cache->cache) { + /*fixme - this assumes cache draw time is 0*/ + cache_time = cache->priority * cache->cached_size / (1024*group->nb_objects); + avg_time += cache_time; + } + gf_cache_remove_entry(compositor, NULL, cache); + cache->nb_stats_frame = 0; + cache->traverse_time = 0; + gf_list_rem(compositor->cached_groups_queue, last_cache_idx); + } + priority = INT2FIX (group->nb_objects*1024*1024*avg_time) / group->cached_size; + + /*when the memory exceeds the constraint, remove the subgroups that have the lowest deltas*/ + while (compositor->video_cache_current_size > compositor->video_cache_max_size) { + gf_cache_remove_entry(compositor, node, NULL); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Removing low priority cache - current total size %d\n", compositor->video_cache_current_size)); + } + } +} + +void compositor_set_cache_memory(GF_Compositor *compositor, u32 memory) +{ + /*when the memory exceeds the constraint, remove the subgroups that have the lowest deltas*/ + while (compositor->video_cache_current_size) { + gf_cache_remove_entry(compositor, NULL, NULL); + } + compositor->video_cache_max_size = memory; + /*and force recompute*/ + compositor->zoom_changed = 1; +} + +#endif /*GF_SR_USE_VIDEO_CACHE*/ + +void group_2d_destroy(GF_Node *node, GroupingNode2D *group) +{ +#ifdef GF_SR_USE_VIDEO_CACHE + GF_Compositor *compositor = gf_sc_get_compositor(node); + if (gf_cache_remove_entry(compositor, node, group)) { + /*simulate a zoom changed for cache recompute*/ + compositor->zoom_changed = 1; + compositor->draw_next_frame = 1; + } + if (group->cache) group_cache_del(group->cache); +#endif +} diff --git a/src/compositor/offscreen_cache.h b/src/compositor/offscreen_cache.h new file mode 100644 index 0000000..8216cce --- /dev/null +++ b/src/compositor/offscreen_cache.h @@ -0,0 +1,58 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Yi-Zhen Zhang, Jean Le Feuvre + * + * Copyright (c) ENST 2005-200X + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef OFFSCREEN_CACHE_H +#define OFFSCREEN_CACHE_H + +#include "drawable.h" + +typedef struct _group_cache +{ + /*gpac texture object*/ + GF_TextureHandler txh; + /*drawable representing the cached group*/ + Drawable *drawable; + + Fixed opacity; + Bool force_recompute; + /*user scale (zoom and AR) of the group*/ + Fixed scale; +} GroupCache; + +GroupCache *group_cache_new(GF_Compositor *compositor, GF_Node *node); +void group_cache_del(GroupCache *cache); + +/*returns 1 if cache is being recomputed due to dirty subtree*/ +Bool group_cache_traverse(GF_Node *node, GroupCache *cache, GF_TraverseState *tr_state, Bool force_recompute); + +void group_cache_draw(GroupCache *cache, GF_TraverseState *tr_state); +Fixed group_cache_check_coverage_increase(GF_Rect *ctx, GF_Rect *grp_bounds, DrawableContext *curr, DrawableContext* first_child); + + + +#endif /*OFFSCREEN_CACHE_H*/ + diff --git a/src/compositor/svg_base.c b/src/compositor/svg_base.c new file mode 100644 index 0000000..63a0b8d --- /dev/null +++ b/src/compositor/svg_base.c @@ -0,0 +1,387 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "visual_manager.h" + +#ifndef GPAC_DISABLE_SVG +#include "nodes_stacks.h" + + +/* + This is the generic routine for children traversing +*/ +void compositor_svg_traverse_children(GF_ChildNodeItem *children, GF_TraverseState *tr_state) +{ + while (children) { + gf_node_traverse(children->node, tr_state); + children = children->next; + } +} + +Bool compositor_svg_is_display_off(SVGPropertiesPointers *props) +{ + return (props->display && (*(props->display) == SVG_DISPLAY_NONE)) ? 1 : 0; +} + + +void compositor_svg_apply_local_transformation(GF_TraverseState *tr_state, SVGAllAttributes *atts, GF_Matrix2D *backup_matrix_2d, GF_Matrix *backup_matrix) +{ +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d && backup_matrix) { + GF_Matrix tmp; + Bool is_draw = (tr_state->traversing_mode==TRAVERSE_SORT) ? 1 : 0; + gf_mx_copy(*backup_matrix, tr_state->model_matrix); + + if (is_draw) visual_3d_matrix_push(tr_state->visual); + + if (atts->transform && atts->transform->is_ref) { + gf_mx_from_mx2d(&tr_state->model_matrix, &tr_state->vb_transform); + if (is_draw) { + GF_Matrix tmp; + gf_mx_init(tmp); + gf_mx_add_translation(&tmp, -tr_state->camera->width/2, tr_state->camera->height/2, 0); + gf_mx_add_scale(&tmp, FIX_ONE, -FIX_ONE, FIX_ONE); + gf_mx_add_matrix(&tmp, &tr_state->model_matrix); + visual_3d_matrix_load(tr_state->visual, tmp.m); + } + } + + if (atts->motionTransform) { + if (is_draw) { + gf_mx_from_mx2d(&tmp, atts->motionTransform); + visual_3d_matrix_add(tr_state->visual, tmp.m); + } else { + gf_mx_add_matrix_2d(&tr_state->model_matrix, atts->motionTransform); + } + } + + if (atts->transform) { + if (is_draw) { + gf_mx_from_mx2d(&tmp, &atts->transform->mat); + visual_3d_matrix_add(tr_state->visual, tmp.m); + } else { + gf_mx_add_matrix_2d(&tr_state->model_matrix, &atts->transform->mat); + } + } + return; + } +#endif + gf_mx2d_copy(*backup_matrix_2d, tr_state->transform); + + if (atts->transform && atts->transform->is_ref) + gf_mx2d_copy(tr_state->transform, tr_state->vb_transform); + + if (atts->motionTransform) + gf_mx2d_pre_multiply(&tr_state->transform, atts->motionTransform); + + if (atts->transform) + gf_mx2d_pre_multiply(&tr_state->transform, &atts->transform->mat); + +} + +void compositor_svg_restore_parent_transformation(GF_TraverseState *tr_state, GF_Matrix2D *backup_matrix_2d, GF_Matrix *backup_matrix) +{ +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d && backup_matrix) { + if (tr_state->traversing_mode==TRAVERSE_SORT) + visual_3d_matrix_pop(tr_state->visual); + gf_mx_copy(tr_state->model_matrix, *backup_matrix); + return; + } +#endif + gf_mx2d_copy(tr_state->transform, *backup_matrix_2d); +} + + +static void gf_svg_apply_inheritance_no_inheritance(SVGAllAttributes *all_atts, SVGPropertiesPointers *render_svg_props) +{ +#define CHECK_PROP(a, b) if (b) a = b; + + render_svg_props->audio_level = NULL; + CHECK_PROP(render_svg_props->display, all_atts->display); + CHECK_PROP(render_svg_props->fill, all_atts->fill); + CHECK_PROP(render_svg_props->fill_opacity, all_atts->fill_opacity); + CHECK_PROP(render_svg_props->fill_rule, all_atts->fill_rule); + CHECK_PROP(render_svg_props->solid_color, all_atts->solid_color); + CHECK_PROP(render_svg_props->solid_opacity, all_atts->solid_opacity); + CHECK_PROP(render_svg_props->stop_color, all_atts->stop_color); + CHECK_PROP(render_svg_props->stop_opacity, all_atts->stop_opacity); + CHECK_PROP(render_svg_props->stroke, all_atts->stroke); + CHECK_PROP(render_svg_props->stroke_dasharray, all_atts->stroke_dasharray); + CHECK_PROP(render_svg_props->stroke_dashoffset, all_atts->stroke_dashoffset); + CHECK_PROP(render_svg_props->stroke_linecap, all_atts->stroke_linecap); + CHECK_PROP(render_svg_props->stroke_linejoin, all_atts->stroke_linejoin); + CHECK_PROP(render_svg_props->stroke_miterlimit, all_atts->stroke_miterlimit); + CHECK_PROP(render_svg_props->stroke_opacity, all_atts->stroke_opacity); + CHECK_PROP(render_svg_props->stroke_width, all_atts->stroke_width); + CHECK_PROP(render_svg_props->visibility, all_atts->visibility); +} + +static const struct svg_11_feature { const char *name; Bool supported; } svg11_features[] = +{ + { "Animation", 1}, + { "AnimationEventsAttribute", 1}, + { "BasicClip", 0}, + { "BasicFilter", 0}, + { "BasicFont", 1}, + { "BasicGraphicsAttribute", 1}, + { "BasicPaintAttribute", 1}, + { "BasicStructure", 1}, + { "BasicText", 1}, + { "Clip", 0}, + { "ColorProfile", 0}, + { "ConditionalProcessing", 1}, + { "ContainerAttribute", 1}, + { "CoreAttribute", 1}, + { "Cursor", 0}, + { "DocumentEventsAttribute", 1}, + { "Extensibility", 1}, + { "ExternalResourcesRequired", 0}, + { "Font", 1}, + { "Filter", 0}, + { "Gradient", 1}, + { "GraphicalEventsAttribute", 1}, + { "GraphicsAttribute", 1}, + { "Hyperlinking", 1}, + { "Image", 1}, + { "Marker", 0}, + { "Mask", 0}, + { "OpacityAttribute", 1}, + { "PaintAttribute", 1}, + { "Pattern", 0}, + { "Script", 1}, + { "Scripting", 1}, + { "Shape", 1}, + { "View", 0}, /*no support for element, the rest is OK ...*/ + { "ViewportAttribute", 1}, + { "Structure", 1}, + { "Style", 0}, + { "Text", 1}, + { "View", 1}, + { "XlinkAttribute", 1}, + + { "SVG", 1}, + { "SVG-animation", 1}, + { "SVG-dynamic", 1}, + { "SVG-static", 1}, + /*we don't support SVG DOM, only uDOM*/ + { "SVGDOM", 0}, + { "SVGDOM-animation", 0}, + { "SVGDOM-dynamic", 0}, + { "SVGDOM-static", 0}, +}; +static const struct svg_12_feature { const char *name; Bool supported; } svg12_features[] = +{ + { "CoreAttribute", 1}, + { "NavigationAttribute", 1}, + { "Structure", 1}, + { "ConditionalProcessing", 1}, + { "ConditionalProcessingAttribute", 1}, + { "Image", 1}, + { "Prefetch", 1}, + { "Discard", 1}, + { "Shape", 1}, + { "Text", 1}, + { "PaintAttribute", 1}, + { "OpacityAttribute", 1}, + { "GraphicsAttribute", 1}, + { "Gradient", 1}, + { "SolidColor", 1}, + { "Hyperlinking", 1}, + { "XlinkAttribute", 1}, + { "ExternalResourcesRequired", 1}, + { "Scripting", 1}, + { "Handler", 1}, + { "Listener", 1}, + { "TimedAnimation", 1}, + { "Animation", 1}, + { "Audio", 1}, + { "Video", 1}, + { "Font", 1}, + { "Extensibility", 1}, + { "MediaAttribute", 1}, + { "TextFlow", 1}, + { "TransformedVideo", 1}, + { "ComposedVideo", 1}, + { "EditableTextAttribute", 1}, + + { "SVG-static", 1}, + { "SVG-static-DOM", 1}, + { "SVG-animated", 1}, + { "SVG-all", 1}, + { "SVG-interactive", 1}, +}; + + +Bool compositor_svg_evaluate_conditional(GF_Compositor *compositor, SVGAllAttributes *atts) +{ + u32 i, count; + Bool found; + const char *lang_3cc, *lang_2cc; + + /*process required features*/ + count = atts->requiredFeatures ? gf_list_count(*atts->requiredFeatures) : 0; + for (i=0;irequiredFeatures, i); + if (!iri->string) continue; + + if (!strnicmp(iri->string, "org.w3c.svg", 11)) { + feat = iri->string+12; + if (feat) { + if (!stricmp(feat, "animation")) {} + else if (!stricmp(feat, "dynamic")) {} + /*no support for filters, clipping & co - SVG 1.0 featureStrings are badly designed*/ + else return 0; + } + } + else if (!strnicmp(iri->string, "http://www.w3.org/TR/SVG11/feature", 34)) { + feat = iri->string+35; + if (feat) { + Bool found = 0; + u32 j, nbf; + nbf = sizeof(svg11_features) / sizeof(struct svg_11_feature); + for (j=0; jstring, "http://www.w3.org/Graphics/SVG/feature/1.2/", 43)) { + feat = iri->string+44; + if (feat) { + Bool found = 0; + u32 j, nbf; + nbf = sizeof(svg12_features) / sizeof(struct svg_12_feature); + for (j=0; jrequiredExtensions ? gf_list_count(*atts->requiredExtensions) : 0; + if (count) return 0; + + /*process system language*/ + count = atts->systemLanguage ? gf_list_count(*atts->systemLanguage) : 0; + if (count) { + found = 0; + lang_3cc = gf_cfg_get_key(compositor->user->config, "Systems", "Language3CC"); + if (!lang_3cc) lang_3cc = "und"; + lang_2cc = gf_cfg_get_key(compositor->user->config, "Systems", "Language2CC"); + if (!lang_2cc) lang_2cc = "un"; + } else { + lang_3cc = "und"; + lang_2cc = "un"; + found = 1; + } + + for (i=0;isystemLanguage, i); + /*3 char-code*/ + if (strlen(lang)==3) { + if (!stricmp(lang, lang_3cc)) { found = 1; break; } + } + /*2 char-code, only check first 2 chars - TODO FIXME*/ + else if (!strnicmp(lang, lang_2cc, 2)) { found = 1; break; } + } + if (!found) return 0; + + /*process required formats*/ + count = atts->requiredFormats ? gf_list_count(*atts->requiredFormats) : 0; + if (count) { + for (i=0; irequiredFormats, i); + char *sep = strchr(mime, ';'); + if (sep) sep[0] = 0; + opt = gf_cfg_get_key(compositor->user->config, "MimeTypes", mime); + if (sep) sep[0] = ';'; + if (!opt) return 0; + } + } + + /*process required fonts*/ + count = atts->requiredFonts ? gf_list_count(*atts->requiredFonts) : 0; + if (count) { + for (i=0; irequiredFonts, i); + if (gf_font_manager_set_font_ex(compositor->font_manager, &font, 1, 0, 1)==NULL) + return 0; + } + } + + /*OK, we can render this one*/ + return 1; +} + +Bool compositor_svg_traverse_base(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state, + SVGPropertiesPointers *backup_props, u32 *backup_flags) +{ + u32 inherited_flags_mask; + + if (atts->requiredFeatures || atts->requiredExtensions || atts->systemLanguage + || atts->requiredFonts || atts->requiredFormats) { + if (!compositor_svg_evaluate_conditional(tr_state->visual->compositor, atts)) + return 0; + } + + memcpy(backup_props, tr_state->svg_props, sizeof(SVGPropertiesPointers)); + *backup_flags = tr_state->svg_flags; + +#if 0 + // applying inheritance and determining which group of properties are being inherited + inherited_flags_mask = gf_svg_apply_inheritance(atts, tr_state->svg_props); + gf_svg_apply_animations(node, tr_state->svg_props); // including again inheritance if values are 'inherit' +#else + /* animation (including possibly inheritance) then full inheritance */ + gf_svg_apply_animations(node, tr_state->svg_props); + inherited_flags_mask = gf_svg_apply_inheritance(atts, tr_state->svg_props); +// gf_svg_apply_inheritance_no_inheritance(atts, tr_state->svg_props); +// inherited_flags_mask = 0xFFFFFFFF; +#endif + tr_state->svg_flags &= inherited_flags_mask; + tr_state->svg_flags |= gf_node_dirty_get(node); + + return 1; +} +#endif + + diff --git a/src/compositor/svg_font.c b/src/compositor/svg_font.c new file mode 100644 index 0000000..7e16c92 --- /dev/null +++ b/src/compositor/svg_font.c @@ -0,0 +1,521 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean Le Feuvre + * + * Copyright (c) ENST 2007-200X + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_SVG + +#include "visual_manager.h" +#include "nodes_stacks.h" + +typedef struct +{ + u16 *unicode; + u16 uni_len; + + GF_Glyph glyph; + GF_Font *font; +} SVG_GlyphStack; + + +/*translate string to glyph sequence*/ +static GF_Err svg_font_get_glyphs(void *udta, const char *utf_string, u32 *glyph_buffer, u32 *io_glyph_buffer_size, const char *lang, Bool *is_rtl) +{ + u32 prev_c; + u32 len; + u32 i, gl_idx; + u16 *utf_res; + GF_Node *node = (GF_Node *)udta; + GF_ChildNodeItem *child; + char *utf8 = (char*) utf_string; + + /*FIXME - use glyphs unicode attributes for glyph substitution*/ + len = utf_string ? strlen(utf_string) : 0; + if (!len) { + *io_glyph_buffer_size = 0; + return GF_OK; + } + + if (*io_glyph_buffer_size < len+1) { + *io_glyph_buffer_size = len+1; + return GF_BUFFER_TOO_SMALL; + } + + len = gf_utf8_mbstowcs((u16*) glyph_buffer, *io_glyph_buffer_size, (const char**)&utf8); + if ((s32) len < 0) return GF_IO_ERR; + /*should not happen*/ + if (utf8) return GF_IO_ERR; + + /*perform bidi relayout*/ + utf_res = (u16 *) glyph_buffer; + *is_rtl = gf_utf8_reorder_bidi(utf_res, len); + + /*move 16bit buffer to 32bit*/ + for (i=len; i>0; i--) { + glyph_buffer[i-1] = utf_res[i-1]; + } + + gl_idx = 0; + prev_c = 0; + for (i=0;ichildren; + while (child) { + u32 tag = gf_node_get_tag(child->node); + if (tag==TAG_SVG_missing_glyph) { + missing_glyph = gf_node_get_private(child->node); + } else if (tag ==TAG_SVG_glyph) { + Bool glyph_ok = 0; + SVGAllAttributes atts; + + st = gf_node_get_private(child->node); + if (!st) { + child = child->next; + continue; + } + + if (st->glyph.utf_name==glyph_buffer[i]) { + u32 j, count; + gf_svg_flatten_attributes((SVG_Element*)child->node, &atts); + if (!lang) { + glyph_ok = 1; + } else { + if (!atts.lang) { + glyph_ok = 1; + } else { + count = gf_list_count(*atts.lang); + for (j=0; juni_len>1) { + u32 j; + for (j=0; juni_len; j++) { + if (i+j>=len) break; + if (glyph_buffer[i+j] != st->unicode[j]) break; + } + if (j==st->uni_len) + break; + } + st = NULL; + } + child = child->next; + } + prev_c = glyph_buffer[i]; + + if (!st) + st = missing_glyph; + glyph_buffer[gl_idx] = st ? st->glyph.ID : 0; + if (st && st->uni_len>1) i++; + + gl_idx++; + } + *io_glyph_buffer_size = len = gl_idx; + + return GF_OK; +} + +/*loads glyph by name - returns NULL if glyph cannot be found*/ +static GF_Glyph *svg_font_load_glyph(void *udta, u32 glyph_name) +{ + GF_ChildNodeItem *child = ((GF_ParentNode *) udta)->children; + + while (child) { + if (gf_node_get_tag(child->node)==TAG_SVG_glyph) { + SVG_GlyphStack *st = gf_node_get_private(child->node); + if (st->glyph.ID==glyph_name) { + return &st->glyph; + } + } + child = child->next; + } + + return NULL; +} + +static void svg_traverse_font(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + GF_Font *font = gf_node_get_private(node); + if (font) { + gf_font_manager_unregister_font(font->ft_mgr, font); + if (font->name) free(font->name); + free(font); + } + } +} + +static void svg_font_on_load(GF_Node *handler, GF_DOM_Event *event, GF_Node *observer) +{ + GF_Compositor *compositor; + GF_Font *font; + assert(event->currentTarget->ptr_type==GF_DOM_EVENT_NODE); + assert(gf_node_get_tag((GF_Node*)event->currentTarget->ptr)==TAG_SVG_font); + font = gf_node_get_private((GF_Node*)event->currentTarget->ptr); + font->not_loaded = 0; + compositor = (GF_Compositor *)gf_node_get_private((GF_Node *)handler); + + /*brute-force signaling that all fonts have changed and texts must be recomputed*/ + compositor->reset_fonts = 1; + compositor->draw_next_frame = 1; + +} + +void compositor_init_svg_font(GF_Compositor *compositor, GF_Node *node) +{ + SVG_handlerElement *handler; + GF_Err e; + SVGAllAttributes atts; + GF_Font *font; + GF_Node *node_font = gf_node_get_parent(node, 0); + if (!node_font) return; + + if (gf_node_get_tag(node_font)!=TAG_SVG_font) return; + + gf_svg_flatten_attributes((SVG_Element*)node, &atts); + if (!atts.font_family) return; + + /*register font to font manager*/ + GF_SAFEALLOC(font, GF_Font); + e = gf_font_manager_register_font(compositor->font_manager, font); + if (e) { + free(font); + return; + } + font->ft_mgr = compositor->font_manager; + + font->get_glyphs = svg_font_get_glyphs; + font->load_glyph = svg_font_load_glyph; + font->udta = node_font; + gf_node_set_private(node_font, font); + gf_node_set_callback_function(node_font, svg_traverse_font); + font->name = strdup(atts.font_family->value); + + font->em_size = atts.units_per_em ? FIX2INT( gf_ceil(atts.units_per_em->value) ) : 1000; + /*Inconsistency between SVG 1.2 and 1.1 + when not specify, ascent and descent are computed based on font.vert-origin-y, WHICH DOES NOT EXIST + IN Tiny 1.2 !!! We assume it to be 0. + */ + font->ascent = atts.ascent ? FIX2INT( gf_ceil(atts.ascent->value) ) : 0; + if (!font->ascent) font->ascent = font->em_size; + font->descent = atts.descent ? FIX2INT( gf_ceil(atts.descent->value) ) : 0; + font->baseline = atts.alphabetic ? FIX2INT( gf_ceil(atts.alphabetic->value) ) : 0; + font->line_spacing = font->em_size; + font->styles = 0; + if (atts.font_style) { + switch (*atts.font_style) { + case SVG_FONTSTYLE_ITALIC: + font->styles |= GF_FONT_ITALIC; + break; + case SVG_FONTSTYLE_OBLIQUE: + font->styles |= GF_FONT_OBLIQUE; + break; + } + } + if (atts.font_variant && (*atts.font_variant ==SVG_FONTVARIANT_SMALLCAPS)) + font->styles |= GF_FONT_SMALLCAPS; + + if (atts.font_weight) { + switch(*atts.font_weight) { + case SVG_FONTWEIGHT_100: font->styles |= GF_FONT_WEIGHT_100; break; + case SVG_FONTWEIGHT_LIGHTER: font->styles |= GF_FONT_WEIGHT_LIGHTER; break; + case SVG_FONTWEIGHT_200: font->styles |= GF_FONT_WEIGHT_200; break; + case SVG_FONTWEIGHT_300: font->styles |= GF_FONT_WEIGHT_300; break; + case SVG_FONTWEIGHT_400: font->styles |= GF_FONT_WEIGHT_400; break; + case SVG_FONTWEIGHT_NORMAL: font->styles |= GF_FONT_WEIGHT_NORMAL; break; + case SVG_FONTWEIGHT_500: font->styles |= GF_FONT_WEIGHT_500; break; + case SVG_FONTWEIGHT_600: font->styles |= GF_FONT_WEIGHT_600; break; + case SVG_FONTWEIGHT_700: font->styles |= GF_FONT_WEIGHT_700; break; + case SVG_FONTWEIGHT_BOLD: font->styles |= GF_FONT_WEIGHT_BOLD; break; + case SVG_FONTWEIGHT_800: font->styles |= GF_FONT_WEIGHT_800; break; + case SVG_FONTWEIGHT_900: font->styles |= GF_FONT_WEIGHT_900; break; + case SVG_FONTWEIGHT_BOLDER: font->styles |= GF_FONT_WEIGHT_BOLDER; break; + } + } + + gf_svg_flatten_attributes((SVG_Element*)node_font, &atts); + font->max_advance_h = atts.horiz_adv_x ? FIX2INT( gf_ceil(atts.horiz_adv_x->value) ) : 0; + + font->not_loaded = 1; + + /*wait for onLoad event before activating the font, otherwise we may not have all the glyphs*/ + handler = gf_dom_listener_build(node_font, GF_EVENT_LOAD, 0); + handler->handle_event = svg_font_on_load; + gf_node_set_private((GF_Node *)handler, compositor); +} + + +static void svg_traverse_glyph(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + GF_Font *font; + GF_Glyph *prev_glyph, *a_glyph; + SVG_GlyphStack *st = gf_node_get_private(node); + if (st->unicode) free(st->unicode); + + font = st->font; + prev_glyph = NULL; + a_glyph = font->glyph; + while (a_glyph) { + if (a_glyph == &st->glyph) break; + prev_glyph = a_glyph; + a_glyph = a_glyph->next; + } + if (prev_glyph) { + prev_glyph->next = st->glyph.next; + } else { + font->glyph = st->glyph.next; + } + free(st); + } +} + +void compositor_init_svg_glyph(GF_Compositor *compositor, GF_Node *node) +{ + u16 utf_name[20]; + u8 *utf8; + u32 len; + GF_Rect rc; + GF_Glyph *glyph; + GF_Font *font; + SVG_GlyphStack *st; + SVGAllAttributes atts; + GF_Node *node_font = gf_node_get_parent(node, 0); + + /*locate the font node*/ + if (node_font) node_font = gf_node_get_parent(node, 0); + if (!node_font || (gf_node_get_tag(node_font)!=TAG_SVG_font) ) return; + font = gf_node_get_private(node_font); + if (!font) return; + + gf_svg_flatten_attributes((SVG_Element*)node, &atts); + + if (gf_node_get_tag(node)==TAG_SVG_missing_glyph) { + GF_SAFEALLOC(st, SVG_GlyphStack); + goto reg_common; + } + /*we must have unicode specified*/ + if (!atts.unicode) return; + + GF_SAFEALLOC(st, SVG_GlyphStack); + utf8 = *atts.unicode; + len = gf_utf8_mbstowcs(utf_name, 200, (const char **) &utf8); + /*this is a single glyph*/ + if (len==1) { + st->glyph.utf_name = utf_name[0]; + st->uni_len = 1; + } else { + st->glyph.utf_name = (u32) st; + st->unicode = malloc(sizeof(u16)*len); + st->uni_len = len; + memcpy(st->unicode, utf_name, sizeof(u16)*len); + } + +reg_common: + st->glyph.ID = (u32) st; + st->font = font; + st->glyph.horiz_advance = font->max_advance_h; + if (atts.horiz_adv_x) st->glyph.horiz_advance = FIX2INT( gf_ceil(atts.horiz_adv_x->value) ); + if (atts.d) { + st->glyph.path = atts.d; + gf_path_get_bounds(atts.d, &rc); + st->glyph.width = FIX2INT( gf_ceil(rc.width) ); + st->glyph.height = FIX2INT( gf_ceil(rc.height) ); + } + st->glyph.vert_advance = st->glyph.height; + if (!st->glyph.vert_advance) + st->glyph.vert_advance = font->max_advance_v; + + /*register glyph*/ + if (!font->glyph) { + font->glyph = &st->glyph; + } else { + glyph = font->glyph; + while (glyph->next) glyph = glyph->next; + glyph->next = &st->glyph; + } + + gf_node_set_private(node, st); + gf_node_set_callback_function(node, svg_traverse_glyph); +} + + +typedef struct +{ + GF_Font *font; + GF_Font *alias; + GF_Compositor *compositor; + GF_MediaObject *mo; +} FontURIStack; + +static Bool svg_font_uri_check(GF_Node *node, FontURIStack *st) +{ + GF_Font *font; + GF_Node *font_elt; + SVGAllAttributes atts; + gf_svg_flatten_attributes((SVG_Element*)node, &atts); + if (!atts.xlink_href) return 0; + + if (atts.xlink_href->type == XMLRI_ELEMENTID) { + if (!atts.xlink_href->target) atts.xlink_href->target = gf_sg_find_node_by_name(gf_node_get_graph(node), atts.xlink_href->string+1); + } else { + GF_SceneGraph *ext_sg; + char *font_name = strchr(atts.xlink_href->string, '#'); + if (!font_name) return 0; + if (!st->mo) { + st->mo = gf_mo_load_xlink_resource(node, 0, 0, -1); + if (!st->mo) return 0; + } + ext_sg = gf_mo_get_scenegraph(st->mo); + if (!ext_sg) return 0; + atts.xlink_href->target = gf_sg_find_node_by_name(ext_sg, font_name+1); + if (!atts.xlink_href->target) return 0; + } + font_elt = atts.xlink_href->target; + if (gf_node_get_tag(font_elt) != TAG_SVG_font) return 0; + font = gf_node_get_private(font_elt); + if (!font) return 0; + st->alias = font; + + gf_mo_is_done(st->mo); + font->not_loaded = 0; + return 1; +} + +GF_Font *svg_font_uri_get_alias(void *udta) +{ + GF_Node *node = (GF_Node *)udta; + FontURIStack *st = gf_node_get_private(node); + if (!st->alias && !svg_font_uri_check(node, st)) { + return NULL; + } + return st->alias; +} + +static GF_Err svg_font_uri_get_glyphs(void *udta, const char *utf_string, u32 *glyph_buffer, u32 *io_glyph_buffer_size, const char *lang, Bool *is_rtl) +{ + return GF_URL_ERROR; +} + +static GF_Glyph *svg_font_uri_load_glyph(void *udta, u32 glyph_name) +{ + return NULL; +} + +static void svg_traverse_font_face_uri(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + FontURIStack *st = gf_node_get_private(node); + if (st) { + gf_font_manager_unregister_font(st->font->ft_mgr, st->font); + if (st->font->name) free(st->font->name); + free(st->font); + if (st->mo) gf_mo_unload_xlink_resource(node, st->mo); + free(st); + } + } +} + +void compositor_init_svg_font_face_uri(GF_Compositor *compositor, GF_Node *node) +{ + GF_Node *par; + GF_Font *font; + FontURIStack *stack; + GF_Err e; + SVGAllAttributes atts; + + /*check parent is a font-face-src*/ + par = gf_node_get_parent(node, 0); + if (!par || (gf_node_get_tag(par)!=TAG_SVG_font_face_src)) return; + /*check parent's parent is a font-face*/ + par = gf_node_get_parent(par, 0); + if (!par || (gf_node_get_tag(par)!=TAG_SVG_font_face)) return; + + + gf_svg_flatten_attributes((SVG_Element*)node, &atts); + if (!atts.xlink_href) return; + + /*get font familly*/ + gf_svg_flatten_attributes((SVG_Element*)par, &atts); + if (!atts.font_family) return; + + /*if font with the same name exists, don't load*/ + if (gf_compositor_svg_set_font(compositor->font_manager, atts.font_family->value, 0, 1) != NULL) + return; + + /*register font to font manager*/ + GF_SAFEALLOC(font, GF_Font); + e = gf_font_manager_register_font(compositor->font_manager, font); + if (e) { + free(font); + return; + } + GF_SAFEALLOC(stack, FontURIStack); + stack->font = font; + stack->compositor = compositor; + + font->ft_mgr = compositor->font_manager; + + font->get_glyphs = svg_font_uri_get_glyphs; + font->load_glyph = svg_font_uri_load_glyph; + font->get_alias = svg_font_uri_get_alias; + font->udta = node; + font->name = strdup(atts.font_family->value); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_font_face_uri); + + font->not_loaded = 1; + svg_font_uri_check(node, stack); +} + + +#endif /*GPAC_DISABLE_SVG*/ + diff --git a/src/compositor/svg_geometry.c b/src/compositor/svg_geometry.c new file mode 100644 index 0000000..4ec7c31 --- /dev/null +++ b/src/compositor/svg_geometry.c @@ -0,0 +1,691 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "visual_manager.h" +#include "nodes_stacks.h" + +#ifdef GPAC_DISABLE_SVG + +Bool svg_drawable_is_over(Drawable *drawable, Fixed x, Fixed y, DrawAspect2D *asp, GF_TraverseState *tr_state, GF_Rect *glyph_rc) +{ + return 0; +} + +#else + +Bool svg_drawable_is_over(Drawable *drawable, Fixed x, Fixed y, DrawAspect2D *asp, GF_TraverseState *tr_state, GF_Rect *glyph_rc) +{ + Bool check_fill, check_stroke, check_over, check_outline, check_vis, inside; + GF_Rect rc; + u8 ptr_evt; + + ptr_evt = *tr_state->svg_props->pointer_events; + + if (ptr_evt==SVG_POINTEREVENTS_NONE) { + return 0; + } + + if (glyph_rc) { + rc = *glyph_rc; + } else { + gf_path_get_bounds(drawable->path, &rc); + } + inside = ( (x >= rc.x) && (y <= rc.y) && (x <= rc.x + rc.width) && (y >= rc.y - rc.height) ) ? 1 : 0; + + if (ptr_evt==SVG_POINTEREVENTS_BOUNDINGBOX) return inside; + + check_fill = check_stroke = check_over = check_outline = check_vis = 0; + /* + check_vis: if set, return FALSE when visible property is not "visible" + check_fill: + if 1, checks whether point is over path, + if 2, checks if the path is painted (even with fill-opacity=0) before + check_stroke: + if 1, checks whether point is over path outline, + if 2, checks if the path outline is painted (even with stroke-opacity=0) before + */ + switch (ptr_evt) { + case SVG_POINTEREVENTS_VISIBLE: + check_vis = 1; + check_fill = 1; + check_stroke = 1; + break; + case SVG_POINTEREVENTS_VISIBLEFILL: + check_vis = 1; + check_fill = 1; + break; + case SVG_POINTEREVENTS_VISIBLESTROKE: + check_vis = 1; + check_stroke = 1; + break; + case SVG_POINTEREVENTS_VISIBLEPAINTED: + check_vis = 1; + check_fill = 2; + check_stroke = 2; + break; + case SVG_POINTEREVENTS_FILL: + check_fill = 1; + break; + case SVG_POINTEREVENTS_STROKE: + check_stroke = 1; + break; + case SVG_POINTEREVENTS_ALL: + check_fill = 1; + check_stroke = 1; + break; + case SVG_POINTEREVENTS_PAINTED: + check_fill = 2; + check_stroke = 2; + break; + default: + return 0; + } + + /*!!watchout!! asp2D.width is 0 if stroke not visible due to painting properties - we must override this + for picking*/ + if (check_stroke==1) { + asp->pen_props.width = tr_state->svg_props->stroke_width ? tr_state->svg_props->stroke_width->value : 0; + } + if (!asp->pen_props.width) check_stroke = 0; + + if (check_stroke) { + /*rough estimation of stroke bounding box to avoid fetching the stroke each time*/ + if (!inside) { + Fixed width = asp->pen_props.width; + rc.x -= width; + rc.y += width; + rc.width += 2*width; + rc.height += 2*width; + inside = ( (x >= rc.x) && (y <= rc.y) && (x <= rc.x + rc.width) && (y >= rc.y - rc.height) ) ? 1 : 0; + if (!inside) return 0; + } + } else if (!inside) { + return 0; + } + + if (check_vis) { + if (*tr_state->svg_props->visibility!=SVG_VISIBILITY_VISIBLE) return 0; + } + + if (check_fill) { + /*painted or don't care about fill*/ + if ((check_fill!=2) || asp->fill_texture || asp->fill_color) { + if (glyph_rc) return 1; + /*point is over path*/ + if (gf_path_point_over(drawable->path, x, y)) return 1; + } + } + if (check_stroke) { + StrikeInfo2D *si; + /*not painted or don't care about stroke*/ + if ((check_stroke!=2) || asp->line_texture || asp->line_color) { + if (glyph_rc) return 1; + si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, asp, tr_state->appear, NULL, 0, NULL); + /*point is over outline*/ + if (si && si->outline && gf_path_point_over(si->outline, x, y)) + return 1; + } + } + return 0; +} + + +void svg_clone_use_stack(GF_Compositor *compositor, GF_TraverseState *tr_state) +{ + u32 i, count; + count = gf_list_count(tr_state->use_stack); + gf_list_reset(compositor->hit_use_stack); + for (i=0; iuse_stack, i); + gf_list_add(compositor->hit_use_stack, node); + } +} + +#ifndef GPAC_DISABLE_3D + +void svg_drawable_3d_pick(Drawable *drawable, GF_TraverseState *tr_state, DrawAspect2D *asp) +{ + SFVec3f local_pt, world_pt, vdiff; + SFVec3f hit_normal; + SFVec2f text_coords; + u32 i, count; + Fixed sqdist; + Bool node_is_over; + GF_Compositor *compositor; + GF_Matrix mx; + GF_Ray r; + + compositor = tr_state->visual->compositor; + + node_is_over = 0; + r = tr_state->ray; + gf_mx_copy(mx, tr_state->model_matrix); + gf_mx_inverse(&mx); + gf_mx_apply_ray(&mx, &r); + + /*if we already have a hit point don't check anything below...*/ + if (compositor->hit_square_dist && !compositor->grabbed_sensor && !tr_state->layer3d) { + GF_Plane p; + GF_BBox box; + SFVec3f hit = compositor->hit_world_point; + gf_mx_apply_vec(&mx, &hit); + p.normal = r.dir; + p.d = -1 * gf_vec_dot(p.normal, hit); + gf_bbox_from_rect(&box, &drawable->path->bbox); + + if (gf_bbox_plane_relation(&box, &p) == GF_BBOX_FRONT) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] bounding box of node %s (DEF %s) below current hit point - skipping\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node))); + return; + } + } + node_is_over = 0; + if (compositor_get_2d_plane_intersection(&r, &local_pt)) { + node_is_over = svg_drawable_is_over(drawable, local_pt.x, local_pt.y, asp, tr_state, NULL); + } + + if (!node_is_over) return; + + hit_normal.x = hit_normal.y = 0; hit_normal.z = FIX_ONE; + text_coords.x = gf_divfix(local_pt.x, drawable->path->bbox.width) + FIX_ONE/2; + text_coords.y = gf_divfix(local_pt.y, drawable->path->bbox.height) + FIX_ONE/2; + + /*check distance from user and keep the closest hitpoint*/ + world_pt = local_pt; + gf_mx_apply_vec(&tr_state->model_matrix, &world_pt); + + for (i=0; inum_clip_planes; i++) { + if (gf_plane_get_distance(&tr_state->clip_planes[i], &world_pt) < 0) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is not in clipper half space\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node))); + return; + } + } + + gf_vec_diff(vdiff, world_pt, tr_state->ray.orig); + sqdist = gf_vec_lensq(vdiff); + if (compositor->hit_square_dist && (compositor->hit_square_dist+FIX_EPSILONnode), gf_node_get_name(drawable->node), FIX2FLT(sqdist), FIX2FLT(compositor->hit_square_dist))); + return; + } + + compositor->hit_square_dist = sqdist; + + /*also stack any VRML sensors present at the current level. If the event is not catched + by a listener in the SVG tree, the event will be forwarded to the VRML tree*/ + gf_list_reset(compositor->sensors); + count = gf_list_count(tr_state->vrml_sensors); + for (i=0; isensors, gf_list_get(tr_state->vrml_sensors, i)); + } + + gf_mx_copy(compositor->hit_world_to_local, tr_state->model_matrix); + gf_mx_copy(compositor->hit_local_to_world, mx); + compositor->hit_local_point = local_pt; + compositor->hit_world_point = world_pt; + compositor->hit_world_ray = tr_state->ray; + compositor->hit_normal = hit_normal; + compositor->hit_texcoords = text_coords; + + svg_clone_use_stack(compositor, tr_state); + /*not use in SVG patterns*/ + compositor->hit_appear = NULL; + compositor->hit_node = drawable->node; + compositor->hit_use_dom_events = 1; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s (def %s) is under mouse - hit %g %g %g\n", gf_node_get_class_name(drawable->node), gf_node_get_name(drawable->node), + FIX2FLT(world_pt.x), FIX2FLT(world_pt.y), FIX2FLT(world_pt.z))); +} + +#endif + +void svg_drawable_pick(GF_Node *node, Drawable *drawable, GF_TraverseState *tr_state) +{ + DrawAspect2D asp; + GF_Matrix2D inv_2d; + Fixed x, y; + Bool picked = 0; + GF_Compositor *compositor = tr_state->visual->compositor; + SVGPropertiesPointers backup_props; + GF_Matrix2D backup_matrix; + GF_Matrix mx_3d; + SVGAllAttributes all_atts; + + if (!drawable->path) return; + + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + + memcpy(&backup_props, tr_state->svg_props, sizeof(SVGPropertiesPointers)); + gf_svg_apply_inheritance(&all_atts, tr_state->svg_props); + + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); + + memset(&asp, 0, sizeof(DrawAspect2D)); + drawable_get_aspect_2d_svg(node, &asp, tr_state); + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + svg_drawable_3d_pick(drawable, tr_state, &asp); + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + return; + } +#endif + gf_mx2d_copy(inv_2d, tr_state->transform); + gf_mx2d_inverse(&inv_2d); + x = tr_state->ray.orig.x; + y = tr_state->ray.orig.y; + gf_mx2d_apply_coords(&inv_2d, &x, &y); + + picked = svg_drawable_is_over(drawable, x, y, &asp, tr_state, NULL); + + if (picked) { + u32 count, i; + compositor->hit_local_point.x = x; + compositor->hit_local_point.y = y; + compositor->hit_local_point.z = 0; + + gf_mx_from_mx2d(&compositor->hit_world_to_local, &tr_state->transform); + gf_mx_from_mx2d(&compositor->hit_local_to_world, &inv_2d); + + compositor->hit_node = drawable->node; + compositor->hit_use_dom_events = 1; + compositor->hit_normal.x = compositor->hit_normal.y = 0; compositor->hit_normal.z = FIX_ONE; + compositor->hit_texcoords.x = gf_divfix(x, drawable->path->bbox.width) + FIX_ONE/2; + compositor->hit_texcoords.y = gf_divfix(y, drawable->path->bbox.height) + FIX_ONE/2; + svg_clone_use_stack(compositor, tr_state); + /*not use in SVG patterns*/ + compositor->hit_appear = NULL; + + /*also stack any VRML sensors present at the current level. If the event is not catched + by a listener in the SVG tree, the event will be forwarded to the VRML tree*/ + gf_list_reset(tr_state->visual->compositor->sensors); + count = gf_list_count(tr_state->vrml_sensors); + for (i=0; ivisual->compositor->sensors, gf_list_get(tr_state->vrml_sensors, i)); + } + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s is under mouse - hit %g %g 0\n", gf_node_get_log_name(drawable->node), FIX2FLT(x), FIX2FLT(y))); + } + + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); +} + +static void svg_drawable_traverse(GF_Node *node, void *rs, Bool is_destroy, + void (*rebuild_path)(GF_Node *, Drawable *, SVGAllAttributes *), + Bool is_svg_rect, Bool is_svg_path) +{ + GF_Matrix2D backup_matrix; + GF_Matrix mx_3d; + DrawableContext *ctx; + SVGPropertiesPointers backup_props; + u32 backup_flags; + Drawable *drawable = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + SVGAllAttributes all_atts; + + if (is_destroy) { +#if USE_GF_PATH + /* The path is the same as the one in the SVG node, don't delete it here */ + if (is_svg_path) drawable->path = NULL; +#endif + drawable_node_del(node); + return; + } + assert(tr_state->traversing_mode!=TRAVERSE_DRAW_2D); + + + if (tr_state->traversing_mode==TRAVERSE_PICK) { + svg_drawable_pick(node, drawable, tr_state); + return; + } + + /*flatten attributes and apply animations + inheritance*/ + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags)) + return; + + /* Recreates the path (i.e the shape) only if the node is dirty */ + if (gf_node_dirty_get(node) & GF_SG_SVG_GEOMETRY_DIRTY) { + /*the rebuild function is responsible for cleaning the path*/ + rebuild_path(node, drawable, &all_atts); + gf_node_dirty_clear(node, GF_SG_SVG_GEOMETRY_DIRTY); + drawable_mark_modified(drawable, tr_state); + } + if (drawable->path) { + if (*(tr_state->svg_props->fill_rule)==GF_PATH_FILL_ZERO_NONZERO) { + if (!(drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO)) { + drawable->path->flags |= GF_PATH_FILL_ZERO_NONZERO; + drawable_mark_modified(drawable, tr_state); + } + } else { + if (drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO) { + drawable->path->flags &= ~GF_PATH_FILL_ZERO_NONZERO; + drawable_mark_modified(drawable, tr_state); + } + } + } + + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + if (! compositor_svg_is_display_off(tr_state->svg_props)) { + DrawAspect2D asp; + gf_path_get_bounds(drawable->path, &tr_state->bounds); + if (!tr_state->ignore_strike) { + memset(&asp, 0, sizeof(DrawAspect2D)); + drawable_get_aspect_2d_svg(node, &asp, tr_state); + if (asp.pen_props.width) { + StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, &asp, NULL, drawable->path, 0, NULL); + if (si && si->outline) { + gf_path_get_bounds(si->outline, &tr_state->bounds); + } + } + } + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, NULL); + if (!tr_state->abort_bounds_traverse) + gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); + gf_sc_get_nodes_bounds(node, NULL, tr_state, NULL); + + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, NULL); + } + } else if (tr_state->traversing_mode == TRAVERSE_SORT) { + + if (!compositor_svg_is_display_off(tr_state->svg_props) && + ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { + + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); + + ctx = drawable_init_context_svg(drawable, tr_state); + if (ctx) { + if (is_svg_rect) { + if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->transparent) {} + else if (GF_COL_A(ctx->aspect.fill_color) != 0xFF) {} + else if (ctx->transform.m[1] || ctx->transform.m[3]) {} + else { + ctx->flags &= ~CTX_IS_TRANSPARENT; + } + } + + if (all_atts.pathLength && all_atts.pathLength->type==SVG_NUMBER_VALUE) + ctx->aspect.pen_props.path_length = all_atts.pathLength->value; + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + if (!drawable->mesh) { + drawable->mesh = new_mesh(); + mesh_from_path(drawable->mesh, drawable->path); + } + visual_3d_draw_from_context(ctx, tr_state); + ctx->drawable = NULL; + } else +#endif + { + drawable_finalize_sort(ctx, tr_state, NULL); + } + } + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); + } + } + + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; +} + +static GF_Err svg_rect_add_arc(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed cx, Fixed cy, Fixed rx, Fixed ry) +{ + Fixed angle, start_angle, end_angle, sweep, _vx, _vy, start_x, start_y; + s32 i, num_steps; + + if (!gp->n_points) return GF_BAD_PARAM; + + start_x = gp->points[gp->n_points-1].x; + start_y = gp->points[gp->n_points-1].y; + + //start angle and end angle + start_angle = gf_atan2(start_y-cy, start_x-cx); + end_angle = gf_atan2(end_y-cy, end_x-cx); + sweep = end_angle - start_angle; + + if (sweep<0) sweep += 2*GF_PI; + + num_steps = 16; + for (i=1; i<=num_steps; i++) { + angle = start_angle + sweep*i/num_steps; + _vx = cx + gf_mulfix(rx, gf_cos(angle)); + _vy = cy + gf_mulfix(ry, gf_sin(angle)); + gf_path_add_line_to(gp, _vx, _vy); + } + return GF_OK; +} + +static void svg_rect_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts) +{ + Fixed rx = (atts->rx ? atts->rx->value : 0); + Fixed ry = (atts->ry ? atts->ry->value : 0); + Fixed x = (atts->x ? atts->x->value : 0); + Fixed y = (atts->y ? atts->y->value : 0); + Fixed width = (atts->width ? atts->width->value : 0); + Fixed height = (atts->height ? atts->height->value : 0); + + drawable_reset_path(stack); + + /*we follow SVG 1.1 and not 1.2 !!*/ + if (rx || ry) { + Fixed cx, cy; + if (rx >= width/2) rx = width/2; + if (ry >= height/2) ry = height/2; + if (rx == 0) rx = ry; + if (ry == 0) ry = rx; + gf_path_add_move_to(stack->path, x+rx, y); + + if (width-rx!=rx) + gf_path_add_line_to(stack->path, x+width-rx, y); + + cx = x+width-rx; cy = y+ry; + svg_rect_add_arc(stack->path, x+width, y+ry, cx, cy, rx, ry); + + if (height-ry!=ry) + gf_path_add_line_to(stack->path, x+width, y+height-ry); + + cx = x+width-rx; cy = y+height-ry; + svg_rect_add_arc(stack->path, x+width-rx, y+height, cx, cy, rx, ry); + + if (width-rx!=rx) + gf_path_add_line_to(stack->path, x+rx, y+height); + + cx = x+rx; cy = y+height-ry; + svg_rect_add_arc(stack->path, x, y+height-ry, cx, cy, rx, ry); + + if (height-ry!=ry) + gf_path_add_line_to(stack->path, x, y+ry); + + cx = x+rx; cy = y+ry; + svg_rect_add_arc(stack->path, x+rx, y, cx, cy, rx, ry); + + gf_path_close(stack->path); + } else { + gf_path_add_move_to(stack->path, x, y); + gf_path_add_line_to(stack->path, x+width, y); + gf_path_add_line_to(stack->path, x+width, y+height); + gf_path_add_line_to(stack->path, x, y+height); + gf_path_close(stack->path); + } +} + +static void svg_traverse_rect(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_drawable_traverse(node, rs, is_destroy, svg_rect_rebuild, 1, 0); +} + +void compositor_init_svg_rect(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, svg_traverse_rect); +} + +static void svg_circle_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts) +{ + Fixed r = 2*(atts->r ? atts->r->value : 0); + drawable_reset_path(stack); + gf_path_add_ellipse(stack->path, (atts->cx ? atts->cx->value : 0), (atts->cy ? atts->cy->value : 0), r, r); +} + +static void svg_traverse_circle(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_drawable_traverse(node, rs, is_destroy, svg_circle_rebuild, 0, 0); +} + +void compositor_init_svg_circle(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, svg_traverse_circle); +} + +static void svg_ellipse_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts) +{ + drawable_reset_path(stack); + gf_path_add_ellipse(stack->path, (atts->cx ? atts->cx->value : 0), + (atts->cy ? atts->cy->value : 0), + (atts->rx ? 2*atts->rx->value : 0), + (atts->ry ? 2*atts->ry->value : 0)); +} +static void svg_traverse_ellipse(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_drawable_traverse(node, rs, is_destroy, svg_ellipse_rebuild, 0, 0); +} + +void compositor_init_svg_ellipse(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, svg_traverse_ellipse); +} + +static void svg_line_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts) +{ + drawable_reset_path(stack); + gf_path_add_move_to(stack->path, (atts->x1 ? atts->x1->value : 0), (atts->y1 ? atts->y1->value : 0)); + gf_path_add_line_to(stack->path, (atts->x2 ? atts->x2->value : 0), (atts->y2 ? atts->y2->value : 0)); +} +static void svg_traverse_line(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_drawable_traverse(node, rs, is_destroy, svg_line_rebuild, 0, 0); +} + +void compositor_init_svg_line(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, svg_traverse_line); +} + +static void svg_polyline_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts) +{ + u32 i, nbPoints; + drawable_reset_path(stack); + if (atts->points) + nbPoints = gf_list_count(*atts->points); + else + nbPoints = 0; + + if (nbPoints) { + SVG_Point *p = (SVG_Point *)gf_list_get(*atts->points, 0); + gf_path_add_move_to(stack->path, p->x, p->y); + for (i = 1; i < nbPoints; i++) { + p = (SVG_Point *)gf_list_get(*atts->points, i); + gf_path_add_line_to(stack->path, p->x, p->y); + } + } else { + gf_path_add_move_to(stack->path, 0, 0); + } +} +static void svg_traverse_polyline(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_drawable_traverse(node, rs, is_destroy, svg_polyline_rebuild, 0, 0); +} + +void compositor_init_svg_polyline(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, svg_traverse_polyline); +} + +static void svg_polygon_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts) +{ + u32 i, nbPoints; + drawable_reset_path(stack); + if (atts->points) + nbPoints = gf_list_count(*atts->points); + else + nbPoints = 0; + + if (nbPoints) { + SVG_Point *p = (SVG_Point *)gf_list_get(*atts->points, 0); + gf_path_add_move_to(stack->path, p->x, p->y); + for (i = 1; i < nbPoints; i++) { + p = (SVG_Point *)gf_list_get(*atts->points, i); + gf_path_add_line_to(stack->path, p->x, p->y); + } + } else { + gf_path_add_move_to(stack->path, 0, 0); + } + /*according to the spec, the polygon path is closed*/ + gf_path_close(stack->path); +} +static void svg_traverse_polygon(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_drawable_traverse(node, rs, is_destroy, svg_polygon_rebuild, 0, 0); +} + +void compositor_init_svg_polygon(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, svg_traverse_polygon); +} + + +static void svg_path_rebuild(GF_Node *node, Drawable *stack, SVGAllAttributes *atts) +{ +#if USE_GF_PATH + drawable_reset_path_outline(stack); + stack->path = atts->d; +#else + drawable_reset_path(stack); + gf_svg_path_build(stack->path, atts->d->commands, atts->d->points); +#endif +} + +static void svg_traverse_path(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_drawable_traverse(node, rs, is_destroy, svg_path_rebuild, 0, 1); +} + +void compositor_init_svg_path(GF_Compositor *compositor, GF_Node *node) +{ + Drawable *dr = drawable_stack_new(compositor, node); + gf_path_del(dr->path); + dr->path = NULL; + gf_node_set_callback_function(node, svg_traverse_path); +} + +#endif + + diff --git a/src/compositor/svg_grouping.c b/src/compositor/svg_grouping.c new file mode 100644 index 0000000..609db97 --- /dev/null +++ b/src/compositor/svg_grouping.c @@ -0,0 +1,1316 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "visual_manager.h" + +#ifndef GPAC_DISABLE_SVG +#include "nodes_stacks.h" +#include "offscreen_cache.h" + +#include +#include + +#include +#include + +/*for svg caching*/ +#include "mpeg4_grouping.h" + + +typedef struct +{ + Bool root_svg; + SVGPropertiesPointers *svg_props; + GF_Matrix2D viewbox_mx; + Drawable *vp_fill; + u32 prev_color; + /*parent VP size used to compute the vp->ViewBox matrix*/ + SFVec2f parent_vp; + /*current VP size used by all children*/ + SFVec2f vp; +} SVGsvgStack; + + +static void svg_recompute_viewport_transformation(GF_Node *node, SVGsvgStack *stack, GF_TraverseState *tr_state, SVGAllAttributes *atts) +{ + GF_Matrix2D mx; + SVG_ViewBox ext_vb, *vb; + SVG_PreserveAspectRatio par; + Fixed scale, vp_w, vp_h; + Fixed parent_width, parent_height, doc_width, doc_height; + + /*canvas size negociation has already been done when attaching the scene to the compositor*/ + if (atts->width && (atts->width->type==SVG_NUMBER_PERCENTAGE) ) { + parent_width = gf_mulfix(tr_state->vp_size.x, atts->width->value/100); + doc_width = 0; + } else if (!stack->root_svg) { + doc_width = parent_width = atts->width ? atts->width->value : 0; + } else { + parent_width = tr_state->vp_size.x; + doc_width = atts->width ? atts->width->value : 0; + } + + if (atts->height && (atts->height->type==SVG_NUMBER_PERCENTAGE) ) { + parent_height = gf_mulfix(tr_state->vp_size.y, atts->height->value/100); + doc_height = 0; + } else if (!stack->root_svg) { + doc_height = parent_height = atts->height ? atts->height->value : 0; + } else { + parent_height = tr_state->vp_size.y; + doc_height = atts->height ? atts->height->value : 0; + } + + stack->vp = stack->parent_vp = tr_state->vp_size; + + vb = atts->viewBox; + + gf_mx2d_init(mx); + + if (stack->root_svg) { + const char *frag_uri = gf_inline_get_fragment_uri(node); + if (frag_uri) { + /*SVGView*/ + if (!strncmp(frag_uri, "svgView", 7)) { + if (!strncmp(frag_uri, "svgView(viewBox(", 16)) { + Float x, y, w, h; + sscanf(frag_uri, "svgView(viewBox(%f,%f,%f,%f))", &x, &y, &w, &h); + ext_vb.x = FLT2FIX(x); + ext_vb.y = FLT2FIX(y); + ext_vb.width = FLT2FIX(w); + ext_vb.height = FLT2FIX(h); + ext_vb.is_set = 1; + vb = &ext_vb; + } + else if (!strncmp(frag_uri, "svgView(transform(", 18)) { + gf_svg_parse_transformlist(&mx, (char *) frag_uri+18); + } + } + /*fragID*/ + else { + GF_Node *target = gf_sg_find_node_by_name(gf_node_get_graph(node), (char *) frag_uri); + if (target) { + GF_Matrix2D mx; + GF_TraverseState bounds_state; + memset(&bounds_state, 0, sizeof(bounds_state)); + bounds_state.traversing_mode = TRAVERSE_GET_BOUNDS; + bounds_state.visual = tr_state->visual; + bounds_state.for_node = target; + bounds_state.svg_props = tr_state->svg_props; + gf_mx2d_init(bounds_state.transform); + gf_mx2d_init(bounds_state.mx_at_node); + gf_mx_init(tr_state->visual->compositor->hit_world_to_local); + gf_sc_get_nodes_bounds(node, ((GF_ParentNode *)node)->children, &bounds_state, NULL); + gf_mx2d_from_mx(&mx, &tr_state->visual->compositor->hit_world_to_local); + gf_mx2d_apply_rect(&mx, &bounds_state.bounds); + ext_vb.x = bounds_state.bounds.x; + ext_vb.y = bounds_state.bounds.y-bounds_state.bounds.height; + ext_vb.width = bounds_state.bounds.width; + ext_vb.height = bounds_state.bounds.height; + ext_vb.is_set = 1; + vb = &ext_vb; + } + } + } + } + gf_mx2d_init(stack->viewbox_mx); + + if (!vb) { + if (!doc_width || !doc_height) { + gf_mx2d_copy(stack->viewbox_mx, mx); + return; + } + /*width/height were specified in the doc, use them to compute a dummy viewbox*/ + ext_vb.x = 0; + ext_vb.y = 0; + ext_vb.width = doc_width; + ext_vb.height = doc_height; + ext_vb.is_set = 1; + vb = &ext_vb; + } + if ((vb->width<=0) || (vb->height<=0) ) { + gf_mx2d_copy(stack->viewbox_mx, mx); + return; + } + stack->vp.x = vb->width; + stack->vp.y = vb->height; + + /*setup default*/ + par.defer = 0; + par.meetOrSlice = SVG_MEETORSLICE_MEET; + par.align = SVG_PRESERVEASPECTRATIO_XMIDYMID; + + /*use parent (animation, image) viewport settings*/ + if (tr_state->parent_anim_atts) { + if (tr_state->parent_anim_atts->preserveAspectRatio) { + if (tr_state->parent_anim_atts->preserveAspectRatio->defer) { + if (atts->preserveAspectRatio) + par = *atts->preserveAspectRatio; + } else { + par = *tr_state->parent_anim_atts->preserveAspectRatio; + } + } + } + /*use current viewport settings*/ + else if (atts->preserveAspectRatio) { + par = *atts->preserveAspectRatio; + } + + if (par.meetOrSlice==SVG_MEETORSLICE_MEET) { + if (gf_divfix(parent_width, vb->width) > gf_divfix(parent_height, vb->height)) { + scale = gf_divfix(parent_height, vb->height); + vp_w = gf_mulfix(vb->width, scale); + vp_h = parent_height; + } else { + scale = gf_divfix(parent_width, vb->width); + vp_w = parent_width; + vp_h = gf_mulfix(vb->height, scale); + } + } else { + if (gf_divfix(parent_width, vb->width) < gf_divfix(parent_height, vb->height)) { + scale = gf_divfix(parent_height, vb->height); + vp_w = gf_mulfix(vb->width, scale); + vp_h = parent_height; + } else { + scale = gf_divfix(parent_width, vb->width); + vp_w = parent_width; + vp_h = gf_mulfix(vb->height, scale); + } + } + + if (par.align==SVG_PRESERVEASPECTRATIO_NONE) { + stack->viewbox_mx.m[0] = gf_divfix(parent_width, vb->width); + stack->viewbox_mx.m[4] = gf_divfix(parent_height, vb->height); + stack->viewbox_mx.m[2] = - gf_muldiv(vb->x, parent_width, vb->width); + stack->viewbox_mx.m[5] = - gf_muldiv(vb->y, parent_height, vb->height); + } else { + Fixed dx, dy; + stack->viewbox_mx.m[0] = stack->viewbox_mx.m[4] = scale; + stack->viewbox_mx.m[2] = - gf_mulfix(vb->x, scale); + stack->viewbox_mx.m[5] = - gf_mulfix(vb->y, scale); + + dx = dy = 0; + switch (par.align) { + case SVG_PRESERVEASPECTRATIO_XMINYMIN: + break; + case SVG_PRESERVEASPECTRATIO_XMIDYMIN: + dx = ( parent_width - vp_w) / 2; + break; + case SVG_PRESERVEASPECTRATIO_XMAXYMIN: + dx = parent_width - vp_w; + break; + case SVG_PRESERVEASPECTRATIO_XMINYMID: + dy = ( parent_height - vp_h) / 2; + break; + case SVG_PRESERVEASPECTRATIO_XMIDYMID: + dx = ( parent_width - vp_w) / 2; + dy = ( parent_height - vp_h) / 2; + break; + case SVG_PRESERVEASPECTRATIO_XMAXYMID: + dx = parent_width - vp_w; + dy = ( parent_height - vp_h) / 2; + break; + case SVG_PRESERVEASPECTRATIO_XMINYMAX: + dy = parent_height - vp_h; + break; + case SVG_PRESERVEASPECTRATIO_XMIDYMAX: + dx = (parent_width - vp_w) / 2; + dy = parent_height - vp_h; + break; + case SVG_PRESERVEASPECTRATIO_XMAXYMAX: + dx = parent_width - vp_w; + dy = parent_height - vp_h; + break; + } + gf_mx2d_add_translation(&stack->viewbox_mx, dx, dy); + + /*we need a clipper*/ + if (stack->root_svg && !tr_state->parent_anim_atts && (par.meetOrSlice==SVG_MEETORSLICE_SLICE)) { + GF_Rect rc; + rc.width = parent_width; + rc.height = parent_height; + if (!stack->root_svg) { + rc.x = 0; + rc.y = parent_height; + gf_mx2d_apply_rect(&stack->viewbox_mx, &rc); + } else { + rc.x = dx; + rc.y = dy + parent_height; + } +// tr_state->visual->top_clipper = gf_rect_pixelize(&rc); + } + } + gf_mx2d_add_matrix(&stack->viewbox_mx, &mx); +} + +static void svg_traverse_svg(GF_Node *node, void *rs, Bool is_destroy) +{ + Bool rootmost_svg; + u32 viewport_color; + SVGsvgStack *stack; + GF_Matrix2D backup_matrix, vb_bck; +#ifndef GPAC_DISABLE_3D + GF_Matrix bck_mx; +#endif + Bool is_dirty; + GF_IRect top_clip; + SFVec2f prev_vp; + SVGPropertiesPointers backup_props, *prev_props; + u32 backup_flags; + Bool invalidate_flag; + u32 styling_size = sizeof(SVGPropertiesPointers); + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + SVGAllAttributes all_atts; + stack = gf_node_get_private(node); + + if (is_destroy) { + if (stack->svg_props) { + gf_svg_properties_reset_pointers(stack->svg_props); + free(stack->svg_props); + } + gf_sc_check_focus_upon_destroy(node); + if (stack->vp_fill) drawable_del(stack->vp_fill); + free(stack); + return; + } + + prev_props = tr_state->svg_props; + /*SVG props not set: we are either the root-most of the compositor + or an inside an */ + if (!tr_state->svg_props) { + tr_state->svg_props = stack->svg_props; + if (!tr_state->svg_props) return; + } + + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) { + tr_state->svg_props = prev_props; + return; + } + + /*enable or disable navigation*/ + tr_state->visual->compositor->navigation_disabled = (all_atts.zoomAndPan && *all_atts.zoomAndPan == SVG_ZOOMANDPAN_DISABLE) ? 1 : 0; + + if (compositor_svg_is_display_off(tr_state->svg_props)) { + memcpy(tr_state->svg_props, &backup_props, styling_size); + tr_state->svg_flags = backup_flags; + return; + } + + top_clip = tr_state->visual->top_clipper; + gf_mx2d_copy(backup_matrix, tr_state->transform); + gf_mx2d_copy(vb_bck, tr_state->vb_transform); + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) gf_mx_copy(bck_mx, tr_state->model_matrix); +#endif + + invalidate_flag = tr_state->invalidate_all; + + is_dirty = gf_node_dirty_get(node); + if (is_dirty &GF_SG_CHILD_DIRTY) drawable_reset_group_highlight(tr_state, node); + gf_node_dirty_clear(node, 0); + + if ((stack->parent_vp.x != tr_state->vp_size.x) || (stack->parent_vp.y != tr_state->vp_size.y)) + is_dirty = 1; + + if (is_dirty || tr_state->visual->compositor->recompute_ar) + svg_recompute_viewport_transformation(node, stack, tr_state, &all_atts); + + gf_mx2d_copy(tr_state->vb_transform, stack->viewbox_mx); + + rootmost_svg = (stack->root_svg && !tr_state->parent_anim_atts) ? 1 : 0; + if (tr_state->traversing_mode == TRAVERSE_SORT) { + SVG_Paint *vp_fill = NULL; + Fixed vp_opacity; + + if (tr_state->parent_anim_atts) { + vp_fill = tr_state->parent_anim_atts->viewport_fill; + vp_opacity = tr_state->parent_anim_atts->viewport_fill_opacity ? tr_state->parent_anim_atts->viewport_fill_opacity->value : FIX_ONE; + } else { + vp_fill = tr_state->svg_props->viewport_fill; + vp_opacity = tr_state->svg_props->viewport_fill_opacity ? tr_state->svg_props->viewport_fill_opacity->value : FIX_ONE; + } + + if (vp_fill && (vp_fill->type != SVG_PAINT_NONE) && vp_opacity) { + Bool col_dirty = 0; + viewport_color = GF_COL_ARGB_FIXED(vp_opacity, vp_fill->color.red, vp_fill->color.green, vp_fill->color.blue); + + if (stack->prev_color != viewport_color) { + stack->prev_color = viewport_color; + col_dirty = 1; + } + + if (!rootmost_svg) { + DrawableContext *ctx; + Fixed width = tr_state->parent_anim_atts->width->value; + Fixed height = tr_state->parent_anim_atts->height->value; + + if (!stack->vp_fill) { + stack->vp_fill = drawable_new(); + stack->vp_fill->node = node; + } + if ((width != stack->vp_fill->path->bbox.width) || (height != stack->vp_fill->path->bbox.height)) { + drawable_reset_path(stack->vp_fill); + gf_path_add_rect(stack->vp_fill->path, 0, 0, width, -height); + } + + ctx = drawable_init_context_svg(stack->vp_fill, tr_state); + if (ctx) { + ctx->flags &= ~CTX_IS_TRANSPARENT; + ctx->aspect.pen_props.width = 0; + ctx->aspect.fill_color = viewport_color; + ctx->aspect.fill_texture = NULL; + if (col_dirty) ctx->flags |= CTX_APP_DIRTY; + drawable_finalize_sort(ctx, tr_state, NULL); + } + + } else if (col_dirty) { + tr_state->visual->compositor->back_color = viewport_color; + /*invalidate the entire visual*/ + tr_state->invalidate_all = 1; + } + } + } + + + if (!stack->root_svg && (all_atts.x || all_atts.y)) + gf_mx2d_add_translation(&tr_state->vb_transform, all_atts.x->value, all_atts.y->value); + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + if (tr_state->traversing_mode==TRAVERSE_SORT) { + GF_Matrix tmp; + visual_3d_matrix_push(tr_state->visual); + + gf_mx_from_mx2d(&tmp, &tr_state->vb_transform); + visual_3d_matrix_add(tr_state->visual, tmp.m); + } else { + gf_mx_add_matrix_2d(&tr_state->model_matrix, &tr_state->vb_transform); + } + } else +#endif + { + gf_mx2d_pre_multiply(&tr_state->transform, &tr_state->vb_transform); + } + + /*store VP and move it to current VP (eg, the one used to compute the vb_transform)*/ + prev_vp = tr_state->vp_size; + tr_state->vp_size = stack->vp; + + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, NULL); + } else { + compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); + } + tr_state->vp_size = prev_vp; + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + if (tr_state->traversing_mode==TRAVERSE_SORT) visual_3d_matrix_pop(tr_state->visual); + gf_mx_copy(tr_state->model_matrix, bck_mx); + } +#endif + gf_mx2d_copy(tr_state->transform, backup_matrix); + gf_mx2d_copy(tr_state->vb_transform, vb_bck); + memcpy(tr_state->svg_props, &backup_props, styling_size); + tr_state->svg_flags = backup_flags; + tr_state->visual->top_clipper = top_clip; + if (!stack->root_svg) { + tr_state->invalidate_all = invalidate_flag; + } + tr_state->svg_props = prev_props; +} + +void compositor_init_svg_svg(GF_Compositor *compositor, GF_Node *node) +{ + GF_Node *root; + SVGsvgStack *stack; + + GF_SAFEALLOC(stack, SVGsvgStack); + + root = gf_sg_get_root_node(gf_node_get_graph(node)); + stack->root_svg = (root==node) ? 1 : 0; + if (stack->root_svg) { + GF_SAFEALLOC(stack->svg_props, SVGPropertiesPointers); + gf_svg_properties_init_pointers(stack->svg_props); + } + gf_mx2d_init(stack->viewbox_mx); + + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_svg); +} + +Bool compositor_svg_get_viewport(GF_Node *n, GF_Rect *rc) +{ + SVGsvgStack *stack; + if (gf_node_get_tag(n) != TAG_SVG_svg) return 0; + stack = gf_node_get_private(n); + rc->width = stack->parent_vp.x; + rc->height = stack->parent_vp.y; + /*not supported yet*/ + rc->x = rc->y = 0; + return 1; +} + +typedef struct +{ + GROUPING_NODE_STACK_2D +#ifndef GF_SR_USE_VIDEO_CACHE + struct _group_cache *cache; +#endif + +} SVGgStack; + +static void svg_traverse_g(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_Matrix2D backup_matrix; + GF_Matrix mx_3d; + SVGPropertiesPointers backup_props; + u32 backup_flags; + u32 styling_size = sizeof(SVGPropertiesPointers); + + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + SVGAllAttributes all_atts; + + if (is_destroy) { + SVGgStack *group = gf_node_get_private(node); +#ifdef GF_SR_USE_VIDEO_CACHE + group_2d_destroy(node, group); +#else + if (group->cache) group_cache_del(group->cache); +#endif + free(group); + gf_sc_check_focus_upon_destroy(node); + return; + } + /*group cache traverse routine*/ + else if (tr_state->traversing_mode == TRAVERSE_DRAW_2D) { + SVGgStack *group = gf_node_get_private(node); + group_cache_draw(group->cache, tr_state); + return; + } + + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + + if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) + return; + + if (compositor_svg_is_display_off(tr_state->svg_props)) { +/* u32 prev_flags = tr_state->switched_off; + tr_state->switched_off = 1; + compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); + tr_state->switched_off = prev_flags;*/ + + memcpy(tr_state->svg_props, &backup_props, styling_size); + tr_state->svg_flags = backup_flags; + return; + } + + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, NULL); + } else if (tr_state->traversing_mode == TRAVERSE_SORT) { + Fixed opacity = FIX_ONE; + SVGgStack *group = gf_node_get_private(node); + + if (tr_state->parent_use_opacity) { + opacity = tr_state->parent_use_opacity->value; + tr_state->parent_use_opacity = NULL; + } + if (all_atts.opacity) { + opacity = gf_mulfix(opacity, all_atts.opacity->value); + } + if (gf_node_dirty_get(node)&GF_SG_CHILD_DIRTY) drawable_reset_group_highlight(tr_state, node); + + if (opacity < FIX_ONE) { + if (!group->cache) { + group->cache = group_cache_new(tr_state->visual->compositor, node); + group->cache->force_recompute = 1; + } + group->cache->opacity = opacity; + if (tr_state->visual->compositor->zoom_changed) + group->cache->force_recompute = 1; + group->flags |= GROUP_IS_CACHED | GROUP_PERMANENT_CACHE; +#ifdef GF_SR_USE_VIDEO_CACHE + group_2d_cache_traverse(node, group, tr_state); +#else + group_cache_traverse(node, group->cache, tr_state, group->cache->force_recompute); +#endif + gf_node_dirty_clear(node, 0); + + } else { +#ifdef GF_SR_USE_VIDEO_CACHE + Bool group_cached; + + group_cached = group_2d_cache_traverse(node, group, tr_state); + gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY); + /*group is not cached, traverse the children*/ + if (!group_cached) { + GF_ChildNodeItem *child; + DrawableContext *first_ctx = tr_state->visual->cur_context; + u32 cache_too_small = 0; + Bool skip_first_ctx = (first_ctx && first_ctx->drawable) ? 1 : 0; + u32 traverse_time = gf_sys_clock(); + u32 last_cache_idx = gf_list_count(tr_state->visual->compositor->cached_groups_queue); + tr_state->cache_too_small = 0; + + child = ((GF_ParentNode *)node)->children; + while (child) { + gf_node_traverse(child->node, tr_state); + child = child->next; + if (tr_state->cache_too_small) + cache_too_small++; + } + + if (cache_too_small) { + tr_state->cache_too_small = 1; + } else { + /*get the traversal time for each group*/ + traverse_time = gf_sys_clock() - traverse_time; + group->traverse_time += traverse_time; + /*record the traversal information and turn cache on if possible*/ + group_2d_cache_evaluate(node, group, tr_state, first_ctx, skip_first_ctx, last_cache_idx); + } + } +#else + compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); +#endif + } + drawable_check_focus_highlight(node, tr_state, NULL); + } else { + compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); + } + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); + memcpy(tr_state->svg_props, &backup_props, styling_size); + tr_state->svg_flags = backup_flags; +} + + +void compositor_init_svg_g(GF_Compositor *compositor, GF_Node *node) +{ + SVGgStack *stack; + GF_SAFEALLOC(stack, SVGgStack); + gf_node_set_private(node, stack); + + gf_node_set_callback_function(node, svg_traverse_g); +} + + +static void svg_traverse_defs(GF_Node *node, void *rs, Bool is_destroy) +{ + SVGPropertiesPointers backup_props; + u32 prev_flags, backup_flags; + u32 styling_size = sizeof(SVGPropertiesPointers); + + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + SVGAllAttributes all_atts; + + if (is_destroy) { + gf_sc_check_focus_upon_destroy(node); + return; + } + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + + if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) + return; + + prev_flags = tr_state->switched_off; + tr_state->switched_off = 1; + compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); + tr_state->switched_off = prev_flags; + + memcpy(tr_state->svg_props, &backup_props, styling_size); + tr_state->svg_flags = backup_flags; +} + + +void compositor_init_svg_defs(GF_Compositor *compositor, GF_Node *node) +{ + gf_node_set_callback_function(node, svg_traverse_defs); +} + + + + +static void svg_traverse_switch(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_Matrix2D backup_matrix; + GF_Matrix mx_3d; + SVGPropertiesPointers backup_props; + u32 backup_flags; + s32 *selected_idx = gf_node_get_private(node); + u32 styling_size = sizeof(SVGPropertiesPointers); + SVGAllAttributes all_atts; + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + free(selected_idx); + gf_sc_check_focus_upon_destroy(node); + return; + } + + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + if (gf_node_dirty_get(node)) { + u32 pos = 0; + GF_ChildNodeItem *child = ((SVG_Element*)node)->children; + *selected_idx = -1; + while (child) { + SVGAllAttributes atts; + gf_svg_flatten_attributes((SVG_Element *)child->node, &atts); + if (compositor_svg_evaluate_conditional(tr_state->visual->compositor, &atts)) { + *selected_idx = pos; + break; + } + pos++; + child = child->next; + } + drawable_reset_group_highlight(tr_state, node); + gf_node_dirty_clear(node, 0); + } + + if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) + return; + + if (compositor_svg_is_display_off(tr_state->svg_props)) { + memcpy(tr_state->svg_props, &backup_props, styling_size); + tr_state->svg_flags = backup_flags; + return; + } + + if (*selected_idx >= 0) { + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, selected_idx); + } else if (*selected_idx >= 0) { + GF_Node *child = gf_node_list_get_child(((SVG_Element *)node)->children, *selected_idx); + gf_node_traverse(child, tr_state); + } + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); + } + + memcpy(tr_state->svg_props, &backup_props, styling_size); + tr_state->svg_flags = backup_flags; +} + +void compositor_init_svg_switch(GF_Compositor *compositor, GF_Node *node) +{ + s32 *selected_idx; + GF_SAFEALLOC(selected_idx, u32); + *selected_idx = -1; + gf_node_set_private(node, selected_idx); + gf_node_set_callback_function(node, svg_traverse_switch); +} + +static void svg_traverse_a(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_Matrix2D backup_matrix; + GF_Matrix mx_3d; + SVGPropertiesPointers backup_props; + u32 styling_size = sizeof(SVGPropertiesPointers); + u32 backup_flags; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + SVGAllAttributes all_atts; + + if (is_destroy) { + gf_sc_check_focus_upon_destroy(node); + return; + } + + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + + if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) + return; + + if (compositor_svg_is_display_off(tr_state->svg_props)) { + /*u32 prev_flags = tr_state->switched_off; + tr_state->switched_off = 1; + compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); + tr_state->switched_off = prev_flags;*/ + + memcpy(tr_state->svg_props, &backup_props, styling_size); + tr_state->svg_flags = backup_flags; + return; + } + + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, NULL); + } else { + compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); + } + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); + memcpy(tr_state->svg_props, &backup_props, styling_size); + tr_state->svg_flags = backup_flags; +} + +static Bool is_timing_target(GF_Node *n) +{ + if (!n) return 0; + switch (gf_node_get_tag(n)) { + case TAG_SVG_set: + case TAG_SVG_animate: + case TAG_SVG_animateColor: + case TAG_SVG_animateTransform: + case TAG_SVG_animateMotion: + case TAG_SVG_discard: + case TAG_SVG_animation: + case TAG_SVG_video: + case TAG_SVG_audio: + return 1; + } + return 0; +} + +static void svg_a_set_view(GF_Node *handler, GF_Compositor *compositor, const char *url) +{ + gf_inline_set_fragment_uri(handler, url); + /*force recompute viewbox of root SVG - FIXME in full this should be the parent svg*/ + gf_node_dirty_set(gf_sg_get_root_node(gf_node_get_graph(handler)), 0, 0); + + compositor->trans_x = compositor->trans_y = 0; + compositor->rotation = 0; + compositor->zoom = FIX_ONE; + compositor_2d_set_user_transform(compositor, FIX_ONE, 0, 0, 0); + gf_sc_invalidate(compositor, NULL); +} + +static void svg_a_handle_event(GF_Node *handler, GF_DOM_Event *event, GF_Node *observer) +{ + GF_Compositor *compositor; + GF_Event evt; + SVG_Element *a; + SVGAllAttributes all_atts; + + if (event->event_phase & GF_DOM_EVENT_PHASE_PREVENT) return; + + assert(gf_node_get_tag((GF_Node*)event->currentTarget->ptr)==TAG_SVG_a); + a = (SVG_Element *) event->currentTarget->ptr; + gf_svg_flatten_attributes(a, &all_atts); + + compositor = (GF_Compositor *)gf_node_get_private((GF_Node *)handler); + + if (!compositor->user->EventProc) return; + if (!all_atts.xlink_href) return; + + if (event->type==GF_EVENT_MOUSEOVER) { + evt.type = GF_EVENT_NAVIGATE_INFO; + + if (all_atts.xlink_title) evt.navigate.to_url = *all_atts.xlink_title; + else if (all_atts.xlink_href->string) evt.navigate.to_url = all_atts.xlink_href->string; + else { + evt.navigate.to_url = gf_node_get_name(all_atts.xlink_href->target); + if (!evt.navigate.to_url) evt.navigate.to_url = "document internal link"; + } + + compositor->user->EventProc(compositor->user->opaque, &evt); + return; + } + + evt.type = GF_EVENT_NAVIGATE; + + if (all_atts.xlink_href->type == XMLRI_STRING) { + evt.navigate.to_url = gf_term_resolve_xlink(handler, all_atts.xlink_href->string); + if (evt.navigate.to_url) { + if (all_atts.target) { + evt.navigate.parameters = (const char **) &all_atts.target; + evt.navigate.param_count = 1; + } else { + evt.navigate.parameters = NULL; + evt.navigate.param_count = 0; + } + + if (evt.navigate.to_url[0] != '#') { + if (compositor->term) { + gf_inline_process_anchor(handler, &evt); + } else { + compositor->user->EventProc(compositor->user->opaque, &evt); + } + free((char *)evt.navigate.to_url); + return; + } + all_atts.xlink_href->target = gf_sg_find_node_by_name(gf_node_get_graph(handler), (char *) evt.navigate.to_url+1); + if (all_atts.xlink_href->target) { + all_atts.xlink_href->type = XMLRI_ELEMENTID; + free((char *)evt.navigate.to_url); + } else { + svg_a_set_view(handler, compositor, evt.navigate.to_url + 1); + free((char *)evt.navigate.to_url); + return; + } + } + } + if (!all_atts.xlink_href->target) { + return; + } + /*this is a time event*/ + if (is_timing_target(all_atts.xlink_href->target)) { + gf_smil_timing_insert_clock(all_atts.xlink_href->target, 0, gf_node_get_scene_time((GF_Node *)handler) ); + } + /*this is an implicit SVGView event*/ + else { + svg_a_set_view(handler, compositor, gf_node_get_name(all_atts.xlink_href->target)); + } +} + +void compositor_init_svg_a(GF_Compositor *compositor, GF_Node *node) +{ + SVG_handlerElement *handler; + gf_node_set_callback_function(node, svg_traverse_a); + + /*listener for onClick event*/ + handler = gf_dom_listener_build(node, GF_EVENT_CLICK, 0); + /*and overwrite handler*/ + handler->handle_event = svg_a_handle_event; + gf_node_set_private((GF_Node *)handler, compositor); + + /*listener for activate event*/ + handler = gf_dom_listener_build(node, GF_EVENT_ACTIVATE, 0); + /*and overwrite handler*/ + handler->handle_event = svg_a_handle_event; + gf_node_set_private((GF_Node *)handler, compositor); + + /*listener for mousemove event*/ + handler = gf_dom_listener_build(node, GF_EVENT_MOUSEOVER, 0); + /*and overwrite handler*/ + handler->handle_event = svg_a_handle_event; + gf_node_set_private((GF_Node *)handler, compositor); + +} + +typedef struct +{ + GF_MediaObject *resource; +// GF_Node *used_node; + GF_SceneGraph *inline_sg; + const char *fragment_id; + Bool needs_play; + u32 init_vis_state; +} SVGlinkStack; + + +static void svg_traverse_resource(GF_Node *node, void *rs, Bool is_destroy, Bool is_foreign_object) +{ + GF_Matrix2D backup_matrix; + GF_Matrix mx_3d; + GF_Matrix2D translate; + SVGPropertiesPointers backup_props; + u32 backup_flags, dirty; + Bool is_fragment; + GF_Node *used_node; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + SVGAllAttributes all_atts; + SVGlinkStack *stack = gf_node_get_private(node); + SFVec2f prev_vp; + SVG_Number *prev_opacity; + + if (is_destroy) { + if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource); + free(stack); + return; + } + + + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + if (!all_atts.xlink_href) return; + + if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) + return; + + dirty = gf_node_dirty_get(node); + if (dirty & GF_SG_CHILD_DIRTY) drawable_reset_group_highlight(tr_state, node); + + if (dirty & GF_SG_SVG_XLINK_HREF_DIRTY) { + stack->fragment_id = NULL; + stack->inline_sg = NULL; + if (all_atts.xlink_href->string && (all_atts.xlink_href->string[0]=='#')) { + stack->fragment_id = all_atts.xlink_href->string; + stack->inline_sg = gf_node_get_graph(node); + } else { + GF_MediaObject *new_res = gf_mo_load_xlink_resource(node, is_foreign_object, 0, -1); + if (new_res != stack->resource) { + if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource); + stack->resource = new_res; + } + } + } + gf_node_dirty_clear(node, 0); + + /*locate the used node - this is done at each step to handle progressive loading*/ + is_fragment = 0; + used_node = NULL; + if (!stack->inline_sg && !stack->fragment_id && all_atts.xlink_href) { + if (all_atts.xlink_href->type == XMLRI_ELEMENTID) { + used_node = all_atts.xlink_href->target; + is_fragment = 1; + } else if (stack->resource) { + stack->inline_sg = gf_mo_get_scenegraph(stack->resource); + if (!is_foreign_object) { + stack->fragment_id = strchr(all_atts.xlink_href->string, '#'); + } + } + } + if (!used_node && stack->inline_sg) { + if (stack->fragment_id) { + used_node = gf_sg_find_node_by_name(stack->inline_sg, (char *) stack->fragment_id+1); + is_fragment = 1; + } else if (is_foreign_object) { + used_node = gf_sg_get_root_node(stack->inline_sg); + } + } + if (!used_node) goto end; + + /*stack use nodes for picking*/ + gf_list_add(tr_state->use_stack, used_node); + gf_list_add(tr_state->use_stack, node); + + gf_mx2d_init(translate); + translate.m[2] = (all_atts.x ? all_atts.x->value : 0); + translate.m[5] = (all_atts.y ? all_atts.y->value : 0); + + /*update VP size (SVG 1.1)*/ + prev_vp = tr_state->vp_size; + if (all_atts.width && all_atts.height) { + tr_state->vp_size.x = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.width); + tr_state->vp_size.y = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.height); + } + + prev_opacity = tr_state->parent_use_opacity; + tr_state->parent_use_opacity = all_atts.opacity; + + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); + if (!compositor_svg_is_display_off(tr_state->svg_props)) { + gf_node_traverse(used_node, tr_state); + gf_mx2d_apply_rect(&translate, &tr_state->bounds); + } + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); + } + /*SORT mode and visible, traverse*/ + else if (!compositor_svg_is_display_off(tr_state->svg_props) + && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN)) { + + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + gf_mx_add_matrix_2d(&tr_state->model_matrix, &translate); + + if (tr_state->traversing_mode==TRAVERSE_SORT) { + GF_Matrix tmp; + gf_mx_from_mx2d(&tmp, &translate); + visual_3d_matrix_add(tr_state->visual, tmp.m); + } + } else +#endif + gf_mx2d_pre_multiply(&tr_state->transform, &translate); + + + drawable_check_focus_highlight(node, tr_state, NULL); + if (is_fragment) { + gf_node_traverse(used_node, tr_state); + } else { + gf_sc_traverse_subscene(tr_state->visual->compositor, node, stack->inline_sg, tr_state); + } + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); + + } + gf_list_rem_last(tr_state->use_stack); + gf_list_rem_last(tr_state->use_stack); + tr_state->vp_size = prev_vp; + + tr_state->parent_use_opacity = prev_opacity; + +end: + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; +} + +static void svg_traverse_use(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_traverse_resource(node, rs, is_destroy, 0); +} + +void compositor_init_svg_use(GF_Compositor *compositor, GF_Node *node) +{ + SVGlinkStack *stack; + GF_SAFEALLOC(stack, SVGlinkStack); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_use); + /*force first processing of xlink-href*/ + gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0); +} + + + +/*********************************** + * 'animation' specific functions * + ***********************************/ + +static void svg_animation_smil_update(GF_Node *node, SVGlinkStack *stack, Fixed normalized_scene_time) +{ + if (stack->init_vis_state == 3) { + stack->init_vis_state = 4; + gf_mo_resume(stack->resource); + } else if (stack->needs_play || (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY )) { + SVGAllAttributes all_atts; + Double clipBegin, clipEnd; + GF_MediaObject *new_res; + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + clipBegin = all_atts.clipBegin ? *all_atts.clipBegin : 0; + clipEnd = all_atts.clipEnd ? *all_atts.clipEnd : -1; + + if (stack->needs_play) { + gf_mo_play(stack->resource, clipBegin, clipEnd, 0); + stack->needs_play = 0; + } else { + new_res = gf_mo_load_xlink_resource(node, 1, clipBegin, clipEnd); + if (new_res != stack->resource) { + if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource); + if (all_atts.xlink_href) all_atts.xlink_href->target = NULL; + stack->resource = new_res; + stack->fragment_id = NULL; + stack->inline_sg = NULL; + } + gf_node_dirty_clear(node, 0); + } + } +} + +static void svg_reset_xlink_target(GF_Node *node) +{ + SVGAllAttributes all_atts; + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + if (all_atts.xlink_href) all_atts.xlink_href->target=NULL; +} + +static void svg_animation_smil_evaluate(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, u32 status) +{ + GF_Node *node = gf_smil_get_element(rti); + SVGlinkStack *stack = gf_node_get_private(node); + switch (status) { + case SMIL_TIMING_EVAL_UPDATE: + svg_animation_smil_update(node, stack, normalized_scene_time); + break; + case SMIL_TIMING_EVAL_FREEZE: + if (stack->resource) { + gf_mo_stop(stack->resource); + stack->needs_play = 1; + } + break; + case SMIL_TIMING_EVAL_REMOVE: + if (stack->resource) { + svg_reset_xlink_target(node); + gf_mo_unload_xlink_resource(node, stack->resource); + stack->resource = NULL; + stack->fragment_id = NULL; + stack->inline_sg = NULL; + gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0); + } + break; + case SMIL_TIMING_EVAL_REPEAT: + if (stack->resource) { + svg_reset_xlink_target(node); + stack->fragment_id = NULL; + stack->inline_sg = NULL; + gf_mo_restart(stack->resource); + } + break; + } +} + + +static void svg_traverse_animation(GF_Node *node, void *rs, Bool is_destroy) +{ + SVGAllAttributes all_atts; + GF_Matrix2D backup_matrix; + GF_Matrix backup_matrix3d; + SVGPropertiesPointers backup_props; + u32 backup_flags; + SFVec2f prev_vp; + GF_Rect rc; + GF_IRect clip, prev_clip; + SVGAllAttributes *prev_vp_atts; + GF_TraverseState *tr_state = (GF_TraverseState*)rs; + GF_Matrix2D translate; + SVGPropertiesPointers *old_props; + SVGlinkStack *stack = gf_node_get_private(node); + + if (is_destroy) { + if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource); + free(stack); + return; + } + if (!stack->inline_sg && !stack->resource) { + if (!stack->init_vis_state) { + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + if (all_atts.initialVisibility && (*all_atts.initialVisibility==SVG_INITIALVISIBILTY_ALWAYS)) { + stack->init_vis_state = 2; + svg_animation_smil_update(node, stack, 0); + } else { + stack->init_vis_state = 1; + } + } + if (!stack->inline_sg && !stack->resource) + return; + } + + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + if (!all_atts.width || !all_atts.height) return; + if (!all_atts.width->value || !all_atts.height->value) return; + + if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) + return; + + if (compositor_svg_is_display_off(tr_state->svg_props) || + *(tr_state->svg_props->visibility) == SVG_VISIBILITY_HIDDEN) { + goto end; + } + + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &backup_matrix3d); + + /*add x/y translation*/ + gf_mx2d_init(translate); + translate.m[2] = (all_atts.x ? all_atts.x->value : 0); + translate.m[5] = (all_atts.y ? all_atts.y->value : 0); +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + gf_mx_add_matrix_2d(&tr_state->model_matrix, &translate); + + if (tr_state->traversing_mode==TRAVERSE_SORT) { + GF_Matrix tmp; + gf_mx_from_mx2d(&tmp, &translate); + visual_3d_matrix_add(tr_state->visual, tmp.m); + } + } else +#endif + gf_mx2d_pre_multiply(&tr_state->transform, &translate); + + /*reset SVG props to reload a new inheritance context*/ + old_props = tr_state->svg_props; + tr_state->svg_props = NULL; + + /*store this node's attribute to compute PAR/ViewBox of the child */ + prev_vp_atts = tr_state->parent_anim_atts; + tr_state->parent_anim_atts = &all_atts; + + /*update VP size*/ + prev_vp = tr_state->vp_size; + + tr_state->vp_size.x = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.width); + tr_state->vp_size.y = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.height); + + /*setup new clipper*/ + rc.width = tr_state->vp_size.x; + rc.height = tr_state->vp_size.y; + rc.x = 0; + rc.y = tr_state->vp_size.y; + gf_mx2d_apply_rect(&tr_state->transform, &rc); + prev_clip = tr_state->visual->top_clipper; + clip = gf_rect_pixelize(&rc); +// gf_irect_intersect(&tr_state->visual->top_clipper, &clip); + + if (!stack->inline_sg && stack->resource) + stack->inline_sg = gf_mo_get_scenegraph(stack->resource); + + if (stack->inline_sg) { + gf_sc_traverse_subscene(tr_state->visual->compositor, node, stack->inline_sg, tr_state); + } + + if (stack->init_vis_state == 2) { + stack->init_vis_state = 3; + gf_mo_pause(stack->resource); + } + + tr_state->svg_props = old_props; + tr_state->visual->top_clipper = prev_clip; + + tr_state->parent_anim_atts = prev_vp_atts; + tr_state->vp_size = prev_vp; + + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &backup_matrix3d); + +end: + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; +} + +void compositor_init_svg_animation(GF_Compositor *compositor, GF_Node *node) +{ + SVGlinkStack *stack; + + GF_SAFEALLOC(stack, SVGlinkStack); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_animation); + + gf_smil_set_evaluation_callback(node, svg_animation_smil_evaluate); + + /*force first processing of xlink-href*/ + gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0); +} + +void svg_pause_animation(GF_Node *n, Bool pause) +{ + SVGlinkStack *st = gf_node_get_private(n); + if (!st) return; + if (pause) gf_mo_pause(st->resource); + else gf_mo_resume(st->resource); +} + +static void svg_traverse_foreign_object(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_traverse_resource(node, rs, is_destroy, 1); +} + +void compositor_init_svg_foreign_object(GF_Compositor *compositor, GF_Node *node) +{ + SVGlinkStack *stack; + GF_SAFEALLOC(stack, SVGlinkStack); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_foreign_object); + /*force first processing of xlink-href*/ + gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0); +} + +GF_Node *compositor_svg_get_xlink_resource_node(GF_Node *node, XMLRI *xlink) +{ + SVGlinkStack *stack; + switch (gf_node_get_tag(node)) { + case TAG_SVG_animation: + stack = gf_node_get_private(node); + return gf_sg_get_root_node(stack->inline_sg); + case TAG_SVG_use: + stack = gf_node_get_private(node); + if (stack->fragment_id) + return gf_sg_find_node_by_name(stack->inline_sg, (char *) stack->fragment_id+1); + return xlink ? xlink->target : NULL; + } + return NULL; +} + +#endif + + diff --git a/src/compositor/svg_media.c b/src/compositor/svg_media.c new file mode 100644 index 0000000..e3c01b7 --- /dev/null +++ b/src/compositor/svg_media.c @@ -0,0 +1,714 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "visual_manager.h" + +#ifndef GPAC_DISABLE_SVG +#include "nodes_stacks.h" + + +static void svg_audio_smil_evaluate_ex(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, u32 status, GF_Node *audio, GF_Node *video); +static void svg_traverse_audio_ex(GF_Node *node, void *rs, Bool is_destroy, SVGPropertiesPointers *props); + + +typedef struct +{ + GF_TextureHandler txh; + Drawable *graph; + MFURL txurl; + Bool first_frame_fetched; + GF_Node *audio; + Bool audio_dirty; + Bool stop_requested; +} SVG_video_stack; + + + +static Bool svg_video_get_transform_behavior(GF_TraverseState *tr_state, SVGAllAttributes *atts, Fixed *cx, Fixed *cy, Fixed *angle) +{ + SFVec2f pt; + if (!atts->transformBehavior) return 0; + if (*atts->transformBehavior == SVG_TRANSFORMBEHAVIOR_GEOMETRIC) + return 0; + + pt.x = atts->x ? atts->x->value : 0; + pt.y = atts->y ? atts->y->value : 0; + gf_mx2d_apply_point(&tr_state->transform, &pt); + *cx = pt.x; + *cy = pt.y; + + *angle = 0; + switch (*atts->transformBehavior) { + case SVG_TRANSFORMBEHAVIOR_PINNED: + break; + case SVG_TRANSFORMBEHAVIOR_PINNED180: + *angle = GF_PI; + break; + case SVG_TRANSFORMBEHAVIOR_PINNED270: + *angle = -GF_PI/2; + break; + case SVG_TRANSFORMBEHAVIOR_PINNED90: + *angle = GF_PI/2; + break; + } + return 1; +} + + +static void SVG_Draw_bitmap(GF_TraverseState *tr_state) +{ + DrawableContext *ctx = tr_state->ctx; + + if (!tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx, NULL)) { + visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state); + } +} + +static void SVG_Build_Bitmap_Graph(SVG_video_stack *stack, GF_TraverseState *tr_state) +{ + u32 tag; + GF_Rect rc, new_rc; + Fixed x, y, width, height, txwidth, txheight; + Fixed rectx, recty, rectwidth, rectheight; + SVGAllAttributes atts; + SVG_PreserveAspectRatio pAR; + SVG_Element *e = (SVG_Element *)stack->graph->node; + + gf_svg_flatten_attributes(e, &atts); + + tag = gf_node_get_tag(stack->graph->node); + switch (tag) { + case TAG_SVG_image: + case TAG_SVG_video: + x = (atts.x ? atts.x->value : 0); + y = (atts.y ? atts.y->value : 0); + width = (atts.width ? atts.width->value : 0); + height = (atts.height ? atts.height->value : 0); + break; + default: + return; + } + + if (!width || !height) return; + + txheight = INT2FIX(stack->txh.height); + txwidth = INT2FIX(stack->txh.width); + + if (!txwidth || !txheight) return; + + if (!atts.preserveAspectRatio) { + pAR.defer = 0; + pAR.meetOrSlice = SVG_MEETORSLICE_MEET; + pAR.align = SVG_PRESERVEASPECTRATIO_XMIDYMID; + } else { + pAR = *atts.preserveAspectRatio; + } + if (pAR.defer) { + /* TODO */ + rectwidth = width; + rectheight = height; + rectx = x+rectwidth/2; + recty = y+rectheight/2; + } else { + + if (pAR.align==SVG_PRESERVEASPECTRATIO_NONE) { + rectwidth = width; + rectheight = height; + rectx = x+rectwidth/2; + recty = y+rectheight/2; + } else { + Fixed scale, scale_w, scale_h; + scale_w = gf_divfix(width, txwidth); + scale_h = gf_divfix(height, txheight); + if (pAR.meetOrSlice==SVG_MEETORSLICE_MEET) { + if (scale_w > scale_h) { + scale = scale_h; + rectwidth = gf_mulfix(txwidth, scale); + rectheight = height; + } else { + scale = scale_w; + rectwidth = width; + rectheight = gf_mulfix(txheight, scale); + } + } else { + if (scale_w < scale_h) { + scale = scale_h; + rectwidth = gf_mulfix(txwidth, scale); + rectheight = height; + } else { + scale = scale_w; + rectwidth = width; + rectheight = gf_mulfix(txheight, scale); + } + } + + rectx = x + rectwidth/2; + recty = y + rectheight/2; + switch (pAR.align) { + case SVG_PRESERVEASPECTRATIO_XMINYMIN: + break; + case SVG_PRESERVEASPECTRATIO_XMIDYMIN: + rectx += (width - rectwidth)/ 2; + break; + case SVG_PRESERVEASPECTRATIO_XMAXYMIN: + rectx += width - rectwidth; + break; + case SVG_PRESERVEASPECTRATIO_XMINYMID: + recty += (height - rectheight)/ 2; + break; + case SVG_PRESERVEASPECTRATIO_XMIDYMID: + rectx += (width - rectwidth)/ 2; + recty += (height - rectheight) / 2; + break; + case SVG_PRESERVEASPECTRATIO_XMAXYMID: + rectx += width - rectwidth; + recty += ( txheight - rectheight) / 2; + break; + case SVG_PRESERVEASPECTRATIO_XMINYMAX: + recty += height - rectheight; + break; + case SVG_PRESERVEASPECTRATIO_XMIDYMAX: + rectx += (width - rectwidth)/ 2; + recty += height - rectheight; + break; + case SVG_PRESERVEASPECTRATIO_XMAXYMAX: + rectx += width - rectwidth; + recty += height - rectheight; + break; + } + } + } + + + gf_path_get_bounds(stack->graph->path, &rc); + drawable_reset_path(stack->graph); + gf_path_add_rect_center(stack->graph->path, rectx, recty, rectwidth, rectheight); + gf_path_get_bounds(stack->graph->path, &new_rc); + if (!gf_rect_equal(rc, new_rc)) + drawable_mark_modified(stack->graph, tr_state); + + gf_node_dirty_clear(stack->graph->node, GF_SG_SVG_GEOMETRY_DIRTY); +} + + +static void svg_play_texture(SVG_video_stack *stack, SVGAllAttributes *atts) +{ + SVGAllAttributes all_atts; + Bool lock_scene = 0; + if (stack->txh.is_open) gf_sc_texture_stop(&stack->txh); + + if (!atts) { + gf_svg_flatten_attributes((SVG_Element*)stack->txh.owner, &all_atts); + atts = &all_atts; + } + if (atts->syncBehavior) lock_scene = (*atts->syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? 1 : 0; + + gf_sc_texture_play_from_to(&stack->txh, &stack->txurl, + atts->clipBegin ? (*atts->clipBegin) : 0.0, + atts->clipEnd ? (*atts->clipEnd) : -1.0, + 0, + lock_scene); +} + +static void svg_traverse_bitmap(GF_Node *node, void *rs, Bool is_destroy) +{ + Fixed cx, cy, angle; + /*video stack is just an extension of image stack, type-casting is OK*/ + SVG_video_stack *stack = (SVG_video_stack*)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + SVGPropertiesPointers backup_props; + u32 backup_flags; + GF_Matrix2D backup_matrix; + GF_Matrix mx_3d; + DrawableContext *ctx; + SVGAllAttributes all_atts; + + + if (is_destroy) { + gf_sc_texture_destroy(&stack->txh); + gf_sg_mfurl_del(stack->txurl); + + drawable_del(stack->graph); + if (stack->audio) { + gf_node_unregister(stack->audio, NULL); + } + free(stack); + return; + } + + + /*TRAVERSE_DRAW is NEVER called in 3D mode*/ + if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { + SVG_Draw_bitmap(tr_state); + return; + } + else if (tr_state->traversing_mode==TRAVERSE_PICK) { + svg_drawable_pick(node, stack->graph, tr_state); + return; + } + + /*flatten attributes and apply animations + inheritance*/ + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags)) + return; + + if (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY) { + gf_term_get_mfurl_from_xlink(node, &stack->txurl); + stack->txh.width = stack->txh.height = 0; + + /*remove associated audio if any*/ + if (stack->audio) { + svg_audio_smil_evaluate_ex(NULL, 0, SMIL_TIMING_EVAL_REMOVE, stack->audio, stack->txh.owner); + gf_node_unregister(stack->audio, NULL); + stack->audio = NULL; + stack->audio_dirty = 1; + } + + svg_play_texture(stack, &all_atts); + gf_node_dirty_clear(node, GF_SG_SVG_XLINK_HREF_DIRTY); + } + + if (gf_node_dirty_get(node)) { + /*do not clear dirty state until the image is loaded*/ + if (stack->txh.width) { + gf_node_dirty_clear(node, 0); + SVG_Build_Bitmap_Graph((SVG_video_stack*)gf_node_get_private(node), tr_state); + } + } + + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + if (!compositor_svg_is_display_off(tr_state->svg_props)) { + gf_path_get_bounds(stack->graph->path, &tr_state->bounds); + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); + + if (svg_video_get_transform_behavior(tr_state, &all_atts, &cx, &cy, &angle)) { + GF_Matrix2D mx; + tr_state->bounds.width = INT2FIX(stack->txh.width); + tr_state->bounds.height = INT2FIX(stack->txh.height); + tr_state->bounds.x = cx - tr_state->bounds.width/2; + tr_state->bounds.y = cy + tr_state->bounds.height/2; + gf_mx2d_init(mx); + gf_mx2d_add_rotation(&mx, 0, 0, angle); + gf_mx2d_apply_rect(&mx, &tr_state->bounds); + } else { + gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); + } + + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); + } + } else if (tr_state->traversing_mode == TRAVERSE_SORT) { + if (!compositor_svg_is_display_off(tr_state->svg_props) && ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { + GF_Matrix mx_bck; + Bool restore_mx = 0; + + compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); + + ctx = drawable_init_context_svg(stack->graph, tr_state); + if (!ctx || !ctx->aspect.fill_texture ) return; + + if (svg_video_get_transform_behavior(tr_state, &all_atts, &cx, &cy, &angle)) { + drawable_reset_path(stack->graph); + gf_path_add_rect_center(stack->graph->path, cx, cy, INT2FIX(stack->txh.width), INT2FIX(stack->txh.height)); + + gf_mx2d_copy(mx_bck, tr_state->transform); + restore_mx = 1; + + gf_mx2d_init(tr_state->transform); + gf_mx2d_add_rotation(&tr_state->transform, cx, cy, angle); + } + + /*even if set this is not true*/ + ctx->aspect.pen_props.width = 0; + ctx->flags |= CTX_NO_ANTIALIAS; + + /*if rotation, transparent*/ + ctx->flags &= ~CTX_IS_TRANSPARENT; + if (ctx->transform.m[1] || ctx->transform.m[3]) { + ctx->flags |= CTX_IS_TRANSPARENT; + ctx->flags &= ~CTX_NO_ANTIALIAS; + } + else if (ctx->aspect.fill_texture->transparent) + ctx->flags |= CTX_IS_TRANSPARENT; + else if (tr_state->svg_props->opacity && (tr_state->svg_props->opacity->type==SVG_NUMBER_VALUE) && (tr_state->svg_props->opacity->value!=FIX_ONE)) { + ctx->flags = CTX_IS_TRANSPARENT; + ctx->aspect.fill_color = GF_COL_ARGB(FIX2INT(0xFF * tr_state->svg_props->opacity->value), 0, 0, 0); + } + +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + if (!stack->graph->mesh) { + stack->graph->mesh = new_mesh(); + mesh_from_path(stack->graph->mesh, stack->graph->path); + } + compositor_3d_draw_bitmap(stack->graph, &ctx->aspect, tr_state, 0, 0, FIX_ONE, FIX_ONE); + ctx->drawable = NULL; + } else +#endif + { + drawable_finalize_sort(ctx, tr_state, NULL); + } + + if (restore_mx) gf_mx2d_copy(tr_state->transform, mx_bck); + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); + } + } + if (stack->audio) svg_traverse_audio_ex(stack->audio, rs, 0, tr_state->svg_props); + + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; +} + +/*********************/ +/* SVG image element */ +/*********************/ + +static void SVG_Update_image(GF_TextureHandler *txh) +{ + MFURL *txurl = &(((SVG_video_stack *)gf_node_get_private(txh->owner))->txurl); + + /*setup texture if needed*/ + if (!txh->is_open && txurl->count) { + gf_sc_texture_play_from_to(txh, txurl, 0, -1, 0, 0); + } + + gf_sc_texture_update_frame(txh, 0); + /*URL is present but not opened - redraw till fetch*/ + if (txh->stream && (!txh->tx_io || txh->needs_refresh) ) + gf_sc_invalidate(txh->compositor, NULL); +} + +static void svg_traverse_image(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_traverse_bitmap(node, rs, is_destroy); +} + +void compositor_init_svg_image(GF_Compositor *compositor, GF_Node *node) +{ + SVG_video_stack *stack; + GF_SAFEALLOC(stack, SVG_video_stack) + stack->graph = drawable_new(); + stack->graph->flags = DRAWABLE_USE_TRAVERSE_DRAW; + stack->graph->node = node; + + gf_sc_texture_setup(&stack->txh, compositor, node); + stack->txh.update_texture_fcnt = SVG_Update_image; + stack->txh.flags = 0; + + /*force first processing of xlink-href*/ + gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0); + + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_image); +} + +/*********************/ +/* SVG video element */ +/*********************/ +static void SVG_Update_video(GF_TextureHandler *txh) +{ + GF_FieldInfo init_vis_info; + SVG_video_stack *stack = (SVG_video_stack *) gf_node_get_private(txh->owner); + + if (!txh->is_open) { + u32 tag; + SVG_InitialVisibility init_vis; + if (stack->first_frame_fetched) return; + + tag = gf_node_get_tag(txh->owner); + init_vis = SVG_INITIALVISIBILTY_WHENSTARTED; + + if (gf_node_get_attribute_by_tag(txh->owner, TAG_SVG_ATT_initialVisibility, 0, 0, &init_vis_info) == GF_OK) { + init_vis = *(SVG_InitialVisibility *)init_vis_info.far_ptr; + } + + /*opens stream only at first access to fetch first frame if needed*/ + if (init_vis == SVG_INITIALVISIBILTY_ALWAYS) { + svg_play_texture((SVG_video_stack*)stack, NULL); + gf_sc_invalidate(txh->compositor, NULL); + } + return; + } + + /*when fetching the first frame disable resync*/ + gf_sc_texture_update_frame(txh, 0); + + /* only when needs_refresh = 1, first frame is fetched */ + if (!stack->first_frame_fetched) { + if (txh->needs_refresh) { + stack->first_frame_fetched = 1; + /*stop stream if needed*/ + if (!gf_smil_timing_is_active(txh->owner)) { + gf_sc_texture_stop(txh); + //make sure the refresh flag is not cleared + txh->needs_refresh = 1; + } + } + } + + if (!stack->audio && (gf_mo_has_audio(stack->txh.stream) || stack->audio_dirty)) { + GF_FieldInfo att_vid, att_aud; + stack->audio = gf_node_new(gf_node_get_graph(stack->txh.owner), TAG_SVG_audio); + gf_node_register(stack->audio, NULL); + if (gf_node_get_attribute_by_tag(stack->txh.owner, TAG_XLINK_ATT_href, 0, 0, &att_vid)==GF_OK) { + gf_node_get_attribute_by_tag(stack->audio, TAG_XLINK_ATT_href, 1, 0, &att_aud); + gf_svg_attributes_copy(&att_aud, &att_vid, 0); + } + /*BYPASS SMIL TIMING MODULE!!*/ + compositor_init_svg_audio(stack->txh.compositor, stack->audio, 1); + stack->audio_dirty = 0; + } + + /*we have no choice but retraversing the graph until we're inactive since the movie framerate and + the compositor framerate are likely to be different */ + if (!txh->stream_finished) + gf_sc_invalidate(txh->compositor, NULL); + + if (stack->stop_requested) { + stack->stop_requested = 0; + gf_sc_texture_stop(&stack->txh); + } +} + +static void svg_video_smil_evaluate(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, u32 status) +{ + SVG_video_stack *stack = (SVG_video_stack *)gf_node_get_private(gf_smil_get_element(rti)); + + switch (status) { + case SMIL_TIMING_EVAL_UPDATE: + if (!stack->txh.is_open) { + svg_play_texture((SVG_video_stack*)stack, NULL); + } + else if (stack->txh.stream_finished && (gf_smil_get_media_duration(rti)<0) ) { + Double dur = gf_mo_get_duration(stack->txh.stream); + if (dur <= 0) { + dur = stack->txh.last_frame_time; + dur /= 1000; + } + gf_smil_set_media_duration(rti, dur); + } + break; + case SMIL_TIMING_EVAL_FREEZE: + case SMIL_TIMING_EVAL_REMOVE: + stack->stop_requested = 1; + break; + case SMIL_TIMING_EVAL_REPEAT: + gf_sc_texture_restart(&stack->txh); + break; + } + if (stack->audio) svg_audio_smil_evaluate_ex(rti, normalized_scene_time, status, stack->audio, stack->txh.owner); +} + +static void svg_traverse_video(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_traverse_bitmap(node, rs, is_destroy); +} + +void compositor_init_svg_video(GF_Compositor *compositor, GF_Node *node) +{ + SVG_video_stack *stack; + GF_SAFEALLOC(stack, SVG_video_stack) + stack->graph = drawable_new(); + stack->graph->flags = DRAWABLE_USE_TRAVERSE_DRAW; + stack->graph->node = node; + + gf_sc_texture_setup(&stack->txh, compositor, node); + stack->txh.update_texture_fcnt = SVG_Update_video; + stack->txh.flags = 0; + + /*force first processing of xlink-href*/ + gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0); + + gf_smil_set_evaluation_callback(node, svg_video_smil_evaluate); + + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_video); +} + +void svg_pause_video(GF_Node *n, Bool pause) +{ + SVG_video_stack *st = gf_node_get_private(n); + if (!st) return; + if (pause) gf_mo_pause(st->txh.stream); + else gf_mo_resume(st->txh.stream); +} + +/*********************/ +/* SVG audio element */ +/*********************/ +typedef struct +{ + GF_AudioInput input; + Bool is_active; + MFURL aurl; +} SVG_audio_stack; + +static void svg_audio_smil_evaluate_ex(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, u32 status, GF_Node *slave_audio, GF_Node *video) +{ + GF_Node *audio; + SVG_audio_stack *stack; + + audio = slave_audio; + if (!audio) audio = gf_smil_get_element(rti); + + stack = (SVG_audio_stack *)gf_node_get_private(audio); + + switch (status) { + case SMIL_TIMING_EVAL_UPDATE: + if (!stack->is_active) { + SVGAllAttributes atts; + gf_svg_flatten_attributes((SVG_Element*) (video ? video : audio), &atts); + + if (gf_sc_audio_open(&stack->input, &stack->aurl, + atts.clipBegin ? (*atts.clipBegin) : 0.0, + atts.clipEnd ? (*atts.clipEnd) : -1.0) == GF_OK) + { + gf_mo_set_speed(stack->input.stream, FIX_ONE); + stack->is_active = 1; + } + } + else if (!slave_audio && stack->input.stream_finished && (gf_smil_get_media_duration(rti) < 0) ) { + Double dur = gf_mo_get_duration(stack->input.stream); + if (dur <= 0) { + dur = gf_mo_get_last_frame_time(stack->input.stream); + dur /= 1000; + } + gf_smil_set_media_duration(rti, dur); + } + break; + case SMIL_TIMING_EVAL_REPEAT: + if (stack->is_active) + gf_sc_audio_restart(&stack->input); + break; + case SMIL_TIMING_EVAL_FREEZE: + gf_sc_audio_stop(&stack->input); + stack->is_active = 0; + break; + case SMIL_TIMING_EVAL_REMOVE: + gf_sc_audio_stop(&stack->input); + stack->is_active = 0; + break; + case SMIL_TIMING_EVAL_DEACTIVATE: + if (stack->is_active) { + gf_sc_audio_stop(&stack->input); + gf_sc_audio_unregister(&stack->input); + stack->is_active = 0; + } + break; + } +} + +static void svg_audio_smil_evaluate(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, u32 status) +{ + svg_audio_smil_evaluate_ex(rti, normalized_scene_time, status, NULL, NULL); +} + + +static void svg_traverse_audio_ex(GF_Node *node, void *rs, Bool is_destroy, SVGPropertiesPointers *props) +{ + SVGAllAttributes all_atts; + SVGPropertiesPointers backup_props; + u32 backup_flags, restore; + GF_TraverseState *tr_state = (GF_TraverseState*)rs; + SVG_audio_stack *stack = (SVG_audio_stack *)gf_node_get_private(node); + + if (is_destroy) { + gf_sc_audio_stop(&stack->input); + gf_sc_audio_unregister(&stack->input); + gf_sg_mfurl_del(stack->aurl); + free(stack); + return; + } + if (stack->is_active) { + gf_sc_audio_register(&stack->input, (GF_TraverseState*)rs); + } + + restore = 0; + if (!props) { + restore = 1; + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags)) + return; + props = tr_state->svg_props; + } + + if (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY) { + gf_term_get_mfurl_from_xlink(node, &(stack->aurl)); + gf_node_dirty_clear(node, GF_SG_SVG_XLINK_HREF_DIRTY); + } + + /*store mute flag*/ + stack->input.is_muted = 0; + if (tr_state->switched_off + || compositor_svg_is_display_off(props) + || (*(props->visibility) == SVG_VISIBILITY_HIDDEN) ) { + + stack->input.is_muted = 1; + } + + stack->input.intensity = tr_state->svg_props->computed_audio_level; + + if (restore) { + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; + } +} +static void svg_traverse_audio(GF_Node *node, void *rs, Bool is_destroy) +{ + svg_traverse_audio_ex(node, rs, is_destroy, NULL); +} + +void compositor_init_svg_audio(GF_Compositor *compositor, GF_Node *node, Bool slaved_timing) +{ + SVG_audio_stack *stack; + GF_SAFEALLOC(stack, SVG_audio_stack) + + gf_sc_audio_setup(&stack->input, compositor, node); + + /*force first processing of xlink-href*/ + gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, 0); + + if (!slaved_timing) + gf_smil_set_evaluation_callback(node, svg_audio_smil_evaluate); + + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_audio); +} + +void svg_pause_audio(GF_Node *n, Bool pause) +{ + SVG_audio_stack *st = gf_node_get_private(n); + if (!st) return; + if (pause) gf_mo_pause(st->input.stream); + else gf_mo_resume(st->input.stream); +} + +GF_TextureHandler *compositor_svg_get_image_texture(GF_Node *node) +{ + SVG_video_stack *st = (SVG_video_stack *) gf_node_get_private(node); + return &(st->txh); +} + +#endif //GPAC_DISABLE_SVG + diff --git a/src/compositor/svg_paint_servers.c b/src/compositor/svg_paint_servers.c new file mode 100644 index 0000000..7dcdffb --- /dev/null +++ b/src/compositor/svg_paint_servers.c @@ -0,0 +1,706 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "visual_manager.h" + +#ifndef GPAC_DISABLE_SVG +#include "nodes_stacks.h" +#include "texturing.h" + + + +typedef struct +{ + GF_TextureHandler txh; + Bool no_rgb_support; + Bool linear; + Bool animated; + Fixed *keys; + u32 *cols; +} SVG_GradientStack; + + +static void SVG_DestroyPaintServer(GF_Node *node) +{ + SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(node); + if (st) { + if (st->cols) free(st->cols); + if (st->keys) free(st->keys); + gf_sc_texture_destroy(&st->txh); + free(st); + } +} + + +static GF_Node *svg_copy_gradient_attributes_from(GF_Node *node, SVGAllAttributes *all_atts) +{ + GF_Node *href_node; + SVGAllAttributes all_href_atts; + GF_FieldInfo info; + + /*check gradient redirection ...*/ + href_node = node; + while (href_node && gf_node_get_attribute_by_tag(href_node, TAG_XLINK_ATT_href, 0, 0, &info)==GF_OK) { + XMLRI*iri = (XMLRI*)info.far_ptr; + + if (iri->type != XMLRI_ELEMENTID) { + GF_SceneGraph *sg = gf_node_get_graph(node); + GF_Node *n = gf_sg_find_node_by_name(sg, &(iri->string[1])); + if (n) { + iri->type = XMLRI_ELEMENTID; + iri->target = n; + gf_node_register_iri(sg, iri); + free(iri->string); + iri->string = NULL; + } else { + break; + } + } + href_node = ((XMLRI*)info.far_ptr)->target; + if (href_node == node) href_node = NULL; + } + if (href_node == node) href_node = NULL; + + if (href_node) { + gf_svg_flatten_attributes((SVG_Element *)href_node, &all_href_atts); + if (!all_atts->gradientUnits) all_atts->gradientUnits = all_href_atts.gradientUnits; + if (!all_atts->gradientTransform) all_atts->gradientTransform = all_href_atts.gradientTransform; + if (!all_atts->cx) all_atts->cx = all_href_atts.cx; + if (!all_atts->cy) all_atts->cy = all_href_atts.cy; + if (!all_atts->r) all_atts->r = all_href_atts.r; + if (!all_atts->fx) all_atts->fx = all_href_atts.fx; + if (!all_atts->fy) all_atts->fy = all_href_atts.fy; + if (!all_atts->spreadMethod) all_atts->spreadMethod = all_href_atts.spreadMethod; + if (!all_atts->x1) all_atts->x1 = all_href_atts.x1; + if (!all_atts->x2) all_atts->x2 = all_href_atts.x2; + if (!all_atts->y1) all_atts->y1 = all_href_atts.y1; + if (!all_atts->y2) all_atts->y2 = all_href_atts.y2; + } + + return href_node; +} + +static void svg_gradient_traverse(GF_Node *node, GF_TraverseState *tr_state, Bool real_traverse) +{ + GF_STENCIL stencil; + u32 count, nb_col; + Bool is_dirty, all_dirty; + Fixed alpha, max_offset; + SVGAllAttributes all_atts; + SVGPropertiesPointers backup_props_1; + u32 backup_flags_1; + GF_Node *href_node; + GF_ChildNodeItem *children; + SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(node); + + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + href_node = svg_copy_gradient_attributes_from(node, &all_atts); + compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props_1, &backup_flags_1); + + if (real_traverse && + ! (tr_state->svg_flags & (GF_SG_SVG_STOPCOLOR_OR_OPACITY_DIRTY|GF_SG_SVG_COLOR_DIRTY)) + && !gf_node_dirty_get(node) + && !st->txh.needs_refresh) + { + memcpy(tr_state->svg_props, &backup_props_1, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags_1; + return; + } + + /*for gradients we must traverse the gradient stops to trigger animations, even if the + gradient is not marked as dirty*/ + all_dirty = tr_state->svg_flags & (GF_SG_SVG_STOPCOLOR_OR_OPACITY_DIRTY|GF_SG_SVG_COLOR_DIRTY); + is_dirty = 0; + if (gf_node_dirty_get(node)) { + is_dirty = all_dirty = 1; + gf_node_dirty_clear(node, 0); + if (st->cols) free(st->cols); + st->cols = NULL; + if (st->keys) free(st->keys); + st->keys = NULL; + + st->animated = gf_node_animation_count(node) ? 1 : 0; + } + + children = ((SVG_Element *)node)->children; + if (!children && href_node) { + children = ((SVG_Element *)href_node)->children; + } + + if (!st->cols) { + count = gf_node_list_get_count(children); + st->cols = (u32*)malloc(sizeof(u32)*count); + st->keys = (Fixed*)malloc(sizeof(Fixed)*count); + } + nb_col = 0; + max_offset = 0; + while (children) { + SVGPropertiesPointers backup_props_2; + u32 backup_flags_2; + Fixed key; + GF_Node *stop = children->node; + children = children->next; + if (gf_node_get_tag((GF_Node *)stop) != TAG_SVG_stop) continue; + + gf_svg_flatten_attributes((SVG_Element*)stop, &all_atts); + compositor_svg_traverse_base(stop, &all_atts, tr_state, &backup_props_2, &backup_flags_2); + + if (gf_node_animation_count(stop)) + st->animated = 1; + + if (all_dirty || gf_node_dirty_get(stop)) { + is_dirty = 1; + gf_node_dirty_clear(stop, 0); + + alpha = FIX_ONE; + if (tr_state->svg_props->stop_opacity && (tr_state->svg_props->stop_opacity->type==SVG_NUMBER_VALUE) ) + alpha = tr_state->svg_props->stop_opacity->value; + + if (tr_state->svg_props->stop_color) { + if (tr_state->svg_props->stop_color->color.type == SVG_COLOR_CURRENTCOLOR) { + st->cols[nb_col] = GF_COL_ARGB_FIXED(alpha, tr_state->svg_props->color->color.red, tr_state->svg_props->color->color.green, tr_state->svg_props->color->color.blue); + } else { + st->cols[nb_col] = GF_COL_ARGB_FIXED(alpha, tr_state->svg_props->stop_color->color.red, tr_state->svg_props->stop_color->color.green, tr_state->svg_props->stop_color->color.blue); + } + } else { + st->cols[nb_col] = GF_COL_ARGB_FIXED(alpha, 0, 0, 0); + } + + if (all_atts.offset) { + key = all_atts.offset->value; + if (all_atts.offset->type==SVG_NUMBER_PERCENTAGE) key/=100; + } else { + key=0; + } + if (key>max_offset) max_offset=key; + else key = max_offset; + st->keys[nb_col] = key; + } else { + if (st->keys[nb_col]>max_offset) max_offset = st->keys[nb_col]; + } + + nb_col++; + + memcpy(tr_state->svg_props, &backup_props_2, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags_2; + } + + if (is_dirty) { + u32 i; + + if (!st->txh.tx_io) gf_sc_texture_allocate(&st->txh); + stencil = gf_sc_texture_get_stencil(&st->txh); + if (!stencil) stencil = st->txh.compositor->rasterizer->stencil_new(st->txh.compositor->rasterizer, st->linear ? GF_STENCIL_LINEAR_GRADIENT : GF_STENCIL_RADIAL_GRADIENT); + /*set stencil even if assigned, this invalidates the associated bitmap state in 3D*/ + gf_sc_texture_set_stencil(&st->txh, stencil); + + st->txh.transparent = 0; + for (i=0; icols[i]) != 0xFF) { + st->txh.transparent = 1; + break; + } + } + + st->txh.compositor->rasterizer->stencil_set_gradient_interpolation(stencil, st->keys, st->cols, nb_col); + st->txh.compositor->rasterizer->stencil_set_gradient_mode(stencil, /*lg->spreadMethod*/ GF_GRADIENT_MODE_PAD); + + st->txh.needs_refresh = 1; + } else { + st->txh.needs_refresh = 0; + } + + memcpy(tr_state->svg_props, &backup_props_1, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags_1; +} + +static void svg_update_gradient(SVG_GradientStack *st, GF_ChildNodeItem *children, Bool linear) +{ + SVGPropertiesPointers *svgp; + GF_Node *node = st->txh.owner; + GF_TraverseState *tr_state = st->txh.compositor->traverse_state; + + if (!gf_node_dirty_get(node)) { + if (!st->animated) return; + } + + GF_SAFEALLOC(svgp, SVGPropertiesPointers); + gf_svg_properties_init_pointers(svgp); + tr_state->svg_props = svgp; + + svg_gradient_traverse(node, tr_state, 0); + + gf_svg_properties_reset_pointers(svgp); + free(svgp); + tr_state->svg_props = NULL; +} + + +static void svg_traverse_gradient(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + SVG_DestroyPaintServer(node); + return; + } + if (tr_state->traversing_mode != TRAVERSE_SORT) return; + svg_gradient_traverse(node, tr_state, 1); +} + +#define GRAD_TEXTURE_SIZE 128 +#define GRAD_TEXTURE_HSIZE 64 + +static GF_Rect compositor_svg_get_gradient_bounds(GF_TextureHandler *txh, SVGAllAttributes *all_atts) +{ + GF_Rect rc; + if (gf_node_get_tag(txh->owner)==TAG_SVG_radialGradient) { + rc.x = rc.y = rc.width = FIX_ONE/2; + if (all_atts->r) { + rc.width = 2*all_atts->r->value; + if (all_atts->r->type==SVG_NUMBER_PERCENTAGE) rc.width /= 100; + } + if (all_atts->cx) { + rc.x = all_atts->cx->value; + if (all_atts->cx->type==SVG_NUMBER_PERCENTAGE) rc.x /= 100; + } + if (all_atts->cy) { + rc.y = all_atts->cy->value; + if (all_atts->cy->type==SVG_NUMBER_PERCENTAGE) rc.y /= 100; + } + rc.height = rc.width; + rc.x -= rc.width/2; + rc.y -= rc.height/2; + } else { + rc.x = rc.y = rc.height = 0; + rc.width = FIX_ONE; + if (all_atts->x1) { + rc.x = all_atts->x1->value; + if (all_atts->x1->type==SVG_NUMBER_PERCENTAGE) rc.x /= 100; + } + if (all_atts->y1) { + rc.y = all_atts->y1->value; + if (all_atts->y1->type==SVG_NUMBER_PERCENTAGE) rc.y /= 100; + } + if (all_atts->x2) { + rc.width = all_atts->x2->value; + if (all_atts->x2->type==SVG_NUMBER_PERCENTAGE) rc.width/= 100; + } + rc.width -= rc.x; + + if (all_atts->y2) { + rc.height = all_atts->y2->value; + if (all_atts->y2->type==SVG_NUMBER_PERCENTAGE) rc.height /= 100; + } + rc.height -= rc.y; + + if (!rc.width) rc.width = rc.height; + if (!rc.height) rc.height = rc.width; + } + rc.y += rc.height; + return rc; +} + +void compositor_svg_build_gradient_texture(GF_TextureHandler *txh) +{ + u32 i; + Fixed size; + GF_Matrix2D mat; + GF_STENCIL stencil; + GF_SURFACE surface; + GF_STENCIL texture2D; + GF_Path *path; + GF_Err e; + Bool transparent; + SVGAllAttributes all_atts; + SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(txh->owner); + GF_Raster2D *raster = txh->compositor->rasterizer; + + + if (!txh->tx_io) return; + + + if (txh->data) { + free(txh->data); + txh->data = NULL; + } + stencil = gf_sc_texture_get_stencil(txh); + if (!stencil) return; + + /*init our 2D graphics stuff*/ + texture2D = raster->stencil_new(raster, GF_STENCIL_TEXTURE); + if (!texture2D) return; + surface = raster->surface_new(raster, 1); + if (!surface) { + raster->stencil_delete(texture2D); + return; + } + + transparent = st->txh.transparent; + if (st->no_rgb_support) transparent = 1; + + if (transparent) { + if (!txh->data) { + txh->data = (char *) malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4); + } else { + memset(txh->data, 0, sizeof(char)*txh->stride*txh->height); + } + e = raster->stencil_set_texture(texture2D, txh->data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, GF_PIXEL_ARGB, GF_PIXEL_ARGB, 1); + } else { + if (!txh->data) { + txh->data = (char *) malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*3); + } + e = raster->stencil_set_texture(texture2D, txh->data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 3*GRAD_TEXTURE_SIZE, GF_PIXEL_RGB_24, GF_PIXEL_RGB_24, 1); + /*try with ARGB (it actually is needed for GDIplus module since GDIplus cannot handle native RGB texture (it works in BGR)*/ + if (e) { + /*remember for later use*/ + st->no_rgb_support = 1; + transparent = 1; + free(txh->data); + txh->data = (char *) malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4); + e = raster->stencil_set_texture(texture2D, txh->data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, GF_PIXEL_ARGB, GF_PIXEL_ARGB, 1); + } + } + + if (e) { + free(txh->data); + txh->data = NULL; + raster->stencil_delete(texture2D); + raster->surface_delete(surface); + return; + } + e = raster->surface_attach_to_texture(surface, texture2D); + if (e) { + raster->stencil_delete(texture2D); + raster->surface_delete(surface); + return; + } + + size = INT2FIX(GRAD_TEXTURE_HSIZE); + /*fill surface*/ + path = gf_path_new(); + gf_path_add_move_to(path, -size, -size); + gf_path_add_line_to(path, size, -size); + gf_path_add_line_to(path, size, size); + gf_path_add_line_to(path, -size, size); + gf_path_close(path); + + gf_mx2d_init(mat); + txh->compute_gradient_matrix(txh, NULL, &mat, 0); + + gf_svg_flatten_attributes((SVG_Element*)txh->owner, &all_atts); + + if (all_atts.gradientUnits && (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT) ) { + if (all_atts.gradientTransform) + gf_mx2d_copy(mat, all_atts.gradientTransform->mat); + + gf_mx2d_add_scale(&mat, 2*size, 2*size); + gf_mx2d_add_translation(&mat, -size, -size); + } else { + GF_Rect rc = compositor_svg_get_gradient_bounds(txh, &all_atts); + /*recenter the gradient to use full texture*/ + gf_mx2d_add_translation(&mat, -rc.x, rc.height-rc.y); + gf_mx2d_add_scale(&mat, gf_divfix(2*size, rc.width), gf_divfix(2*size , rc.height)); + gf_mx2d_add_translation(&mat, -size, -size); + } + + raster->stencil_set_matrix(stencil, &mat); + raster->surface_set_raster_level(surface, GF_RASTER_HIGH_QUALITY); + raster->surface_set_path(surface, path); + raster->surface_fill(surface, stencil); + raster->surface_delete(surface); + raster->stencil_delete(texture2D); + gf_path_del(path); + + txh->width = GRAD_TEXTURE_SIZE; + txh->height = GRAD_TEXTURE_SIZE; + txh->transparent = transparent; + txh->flags |= GF_SR_TEXTURE_NO_GL_FLIP; + + if (transparent) { + u32 j; + txh->stride = GRAD_TEXTURE_SIZE*4; + + /*back to RGBA texturing*/ + txh->pixelformat = GF_PIXEL_RGBA; + for (i=0; iheight; i++) { + char *data = txh->data + i*txh->stride; + for (j=0; jwidth; j++) { + u32 val = *(u32 *) &data[4*j]; + data[4*j] = (val>>16) & 0xFF; + data[4*j+1] = (val>>8) & 0xFF; + data[4*j+2] = (val) & 0xFF; + data[4*j+3] = (val>>24) & 0xFF; + } + } + } else { + txh->stride = GRAD_TEXTURE_SIZE*3; + txh->pixelformat = GF_PIXEL_RGB_24; + } + gf_sc_texture_set_data(txh); + return; +} + + +/* linear gradient */ + +static void SVG_UpdateLinearGradient(GF_TextureHandler *txh) +{ + SVG_Element *lg = (SVG_Element *) txh->owner; + SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(txh->owner); + + svg_update_gradient(st, lg->children, 1); +} + + +static void SVG_LG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat, Bool for_3d) +{ + GF_STENCIL stencil; + SFVec2f start, end; + SVGAllAttributes all_atts; + + if (!txh->tx_io) return; + stencil = gf_sc_texture_get_stencil(txh); + if (!stencil) return; + + gf_svg_flatten_attributes((SVG_Element*)txh->owner, &all_atts); + + /*get "transfered" attributed from xlink:href if any*/ + svg_copy_gradient_attributes_from(txh->owner, &all_atts); + + gf_mx2d_init(*mat); + + /*gradient is a texture, only update the bounds*/ + if (for_3d) { + GF_Rect rc; + if (!all_atts.gradientUnits || (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT)) + return; + + /*get gradient bounds in local coord system*/ + rc = compositor_svg_get_gradient_bounds(txh, &all_atts); + gf_mx2d_add_scale(mat, gf_divfix(rc.width, bounds->width), gf_divfix(rc.height, bounds->height)); + + return; + } + + if (all_atts.gradientTransform) + gf_mx2d_copy(*mat, all_atts.gradientTransform->mat ); + + if (all_atts.x1) { + start.x = all_atts.x1->value; + if (all_atts.x1->type==SVG_NUMBER_PERCENTAGE) start.x /= 100; + } else { + start.x = 0; + } + if (all_atts.y1) { + start.y = all_atts.y1->value; + if (all_atts.y1->type==SVG_NUMBER_PERCENTAGE) start.y /= 100; + } else { + start.y = 0; + } + if (all_atts.x2) { + end.x = all_atts.x2->value; + if (all_atts.x2->type==SVG_NUMBER_PERCENTAGE) end.x /= 100; + } else { + end.x = FIX_ONE; + } + if (all_atts.y2) { + end.y = all_atts.y2->value; + if (all_atts.y2->type==SVG_NUMBER_PERCENTAGE) end.y /= 100; + } else { + end.y = 0; + } + + txh->compositor->rasterizer->stencil_set_gradient_mode(stencil, (GF_GradientMode) all_atts.spreadMethod ? *(SVG_SpreadMethod*)all_atts.spreadMethod : 0); + + + if (bounds && (!all_atts.gradientUnits || (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT)) ) { + /*move to local coord system - cf SVG spec*/ + gf_mx2d_add_scale(mat, bounds->width, bounds->height); + gf_mx2d_add_translation(mat, bounds->x, bounds->y - bounds->height); + } + txh->compositor->rasterizer->stencil_set_linear_gradient(stencil, start.x, start.y, end.x, end.y); +} + +void compositor_init_svg_linearGradient(GF_Compositor *compositor, GF_Node *node) +{ + SVG_GradientStack *st; + GF_SAFEALLOC(st, SVG_GradientStack); + + gf_sc_texture_setup(&st->txh, compositor, node); + st->txh.update_texture_fcnt = SVG_UpdateLinearGradient; + st->txh.compute_gradient_matrix = SVG_LG_ComputeMatrix; + st->linear = 1; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, svg_traverse_gradient); +} + +/* radial gradient */ + +static void SVG_UpdateRadialGradient(GF_TextureHandler *txh) +{ + SVG_Element *rg = (SVG_Element *) txh->owner; + SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(txh->owner); + + svg_update_gradient(st, rg->children, 0); +} + +static void SVG_RG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat, Bool for_3d) +{ + GF_STENCIL stencil; + SFVec2f center, focal; + Fixed radius; + SVGAllAttributes all_atts; + + /*create gradient brush if needed*/ + if (!txh->tx_io) return; + stencil = gf_sc_texture_get_stencil(txh); + if (!stencil) return; + + gf_svg_flatten_attributes((SVG_Element*)txh->owner, &all_atts); + + /*get "transfered" attributed from xlink:href if any*/ + svg_copy_gradient_attributes_from(txh->owner, &all_atts); + + gf_mx2d_init(*mat); + + /*gradient is a texture, only update the bounds*/ + if (for_3d && bounds) { + GF_Rect rc; + if (!all_atts.gradientUnits || (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT)) + return; + + /*get gradient bounds in local coord system*/ + rc = compositor_svg_get_gradient_bounds(txh, &all_atts); + gf_mx2d_add_translation(mat, gf_divfix(rc.x-bounds->x, rc.width), gf_divfix(bounds->y - rc.y, rc.height) ); + gf_mx2d_add_scale(mat, gf_divfix(rc.width, bounds->width), gf_divfix(rc.height, bounds->height)); + + gf_mx2d_inverse(mat); + return; + } + + if (all_atts.gradientTransform) + gf_mx2d_copy(*mat, all_atts.gradientTransform->mat); + + if (all_atts.r) { + radius = all_atts.r->value; + if (all_atts.r->type==SVG_NUMBER_PERCENTAGE) radius /= 100; + } else { + radius = FIX_ONE/2; + } + if (all_atts.cx) { + center.x = all_atts.cx->value; + if (all_atts.cx->type==SVG_NUMBER_PERCENTAGE) center.x /= 100; + } else { + center.x = FIX_ONE/2; + } + if (all_atts.cy) { + center.y = all_atts.cy->value; + if (all_atts.cy->type==SVG_NUMBER_PERCENTAGE) center.y /= 100; + } else { + center.y = FIX_ONE/2; + } + + txh->compositor->rasterizer->stencil_set_gradient_mode(stencil, (GF_GradientMode) all_atts.spreadMethod ? *(SVG_SpreadMethod*)all_atts.spreadMethod : 0); + + if (all_atts.fx) { + focal.x = all_atts.fx->value; + if (all_atts.fx->type==SVG_NUMBER_PERCENTAGE) focal.x /= 100; + } else { + focal.x = center.x; + } + if (all_atts.fy) { + focal.y = all_atts.fy->value; + if (all_atts.fy->type==SVG_NUMBER_PERCENTAGE) focal.y /= 100; + } else { + focal.y = center.y; + } + + if (bounds && (!all_atts.gradientUnits || (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT)) ) { + /*move to local coord system - cf SVG spec*/ + gf_mx2d_add_scale(mat, bounds->width, bounds->height); + gf_mx2d_add_translation(mat, bounds->x, bounds->y - bounds->height); + } + txh->compositor->rasterizer->stencil_set_radial_gradient(stencil, center.x, center.y, focal.x, focal.y, radius, radius); +} + +void compositor_init_svg_radialGradient(GF_Compositor *compositor, GF_Node *node) +{ + SVG_GradientStack *st; + GF_SAFEALLOC(st, SVG_GradientStack); + + gf_sc_texture_setup(&st->txh, compositor, node); + st->txh.update_texture_fcnt = SVG_UpdateRadialGradient; + st->txh.compute_gradient_matrix = SVG_RG_ComputeMatrix; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, svg_traverse_gradient); +} + + +static void svg_traverse_PaintServer(GF_Node *node, void *rs, Bool is_destroy) +{ + SVGPropertiesPointers backup_props; + SVGAllAttributes all_atts; + u32 backup_flags; + u32 styling_size = sizeof(SVGPropertiesPointers); + SVG_Element *elt = (SVG_Element *)node; + GF_TraverseState *tr_state = (GF_TraverseState *) rs; + + if (is_destroy) { + SVG_DestroyPaintServer(node); + return; + } + + gf_svg_flatten_attributes(elt, &all_atts); + compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags); + + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + return; + } else { + compositor_svg_traverse_children(elt->children, tr_state); + } + memcpy(tr_state->svg_props, &backup_props, styling_size); + tr_state->svg_flags = backup_flags; +} +void compositor_init_svg_solidColor(GF_Compositor *compositor, GF_Node *node) +{ + gf_node_set_callback_function(node, svg_traverse_PaintServer); +} + +void compositor_init_svg_stop(GF_Compositor *compositor, GF_Node *node) +{ + gf_node_set_callback_function(node, svg_traverse_PaintServer); +} + +GF_TextureHandler *compositor_svg_get_gradient_texture(GF_Node *node) +{ + SVG_GradientStack *st = (SVG_GradientStack*) gf_node_get_private((GF_Node *)node); + return &st->txh; +} + + +#endif + + diff --git a/src/compositor/svg_text.c b/src/compositor/svg_text.c new file mode 100644 index 0000000..379b635 --- /dev/null +++ b/src/compositor/svg_text.c @@ -0,0 +1,1486 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_SVG + +#include "visual_manager.h" +#include "nodes_stacks.h" + +typedef struct +{ + Drawable *drawable; + Fixed prev_size; + u32 prev_flags; + u32 prev_anchor; + GF_List *spans; + GF_Rect bounds; +} SVG_TextStack; + +static void svg_reset_text_stack(SVG_TextStack *st) +{ + while (gf_list_count(st->spans)) { + GF_TextSpan *span = gf_list_get(st->spans, 0); + gf_list_rem(st->spans, 0); + gf_font_manager_delete_span(NULL, span); + } +} + +static void svg_update_bounds(SVG_TextStack *st) +{ + u32 i=0; + GF_TextSpan *span; + /*finally compute text bounds*/ + st->bounds.width = st->bounds.height = 0; + st->bounds.x = st->bounds.y = 0; + while ( (span = gf_list_enum(st->spans, &i)) ) { + gf_font_manager_refresh_span_bounds(span); + gf_rect_union(&st->bounds, &span->bounds); + } +} + +static void svg_finalize_sort(DrawableContext *ctx, SVG_TextStack *st, GF_TraverseState * tr_state) +{ +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) { + gf_font_spans_draw_3d(st->spans, tr_state, &ctx->aspect, 0, 0); + + drawable_check_focus_highlight(ctx->drawable->node, tr_state, &st->bounds); + ctx->drawable = NULL; + return; + } +#endif + /*if text selection mode, we must force redraw of the entire text span because we don't + if glyphs have been (un)selected*/ + if (!tr_state->direct_draw && + /*text selection on*/ + (tr_state->visual->compositor->text_selection + /*text sel release*/ + || (tr_state->visual->compositor->store_text_state==GF_SC_TSEL_RELEASED)) + ) { + GF_TextSpan *span; + u32 i = 0; + Bool unselect = (tr_state->visual->compositor->store_text_state==GF_SC_TSEL_RELEASED) ? 1 : 0; + while ((span = gf_list_enum(st->spans, &i))) { + if (span->flags & GF_TEXT_SPAN_SELECTED) { + if (unselect) span->flags &= ~GF_TEXT_SPAN_SELECTED; + ctx->flags |= CTX_APP_DIRTY; + } + } + } + drawable_finalize_sort(ctx, tr_state, &st->bounds); +} + +/*@styles indicates font styles (PLAIN, BOLD, ITALIC, BOLDITALIC and UNDERLINED, STRIKEOUT)*/ +static u32 svg_get_font_styles(GF_TraverseState * tr_state) +{ + u32 styles = 0; + switch(*tr_state->svg_props->font_style) { + case SVG_FONTSTYLE_ITALIC: + styles = GF_FONT_ITALIC; + break; + case SVG_FONTSTYLE_OBLIQUE: + styles = GF_FONT_OBLIQUE; + break; + } + if (*tr_state->svg_props->font_variant==SVG_FONTVARIANT_SMALLCAPS) + styles |= GF_FONT_SMALLCAPS; + + switch(*tr_state->svg_props->font_weight) { + case SVG_FONTWEIGHT_100: styles |= GF_FONT_WEIGHT_100; break; + case SVG_FONTWEIGHT_LIGHTER: styles |= GF_FONT_WEIGHT_LIGHTER; break; + case SVG_FONTWEIGHT_200: styles |= GF_FONT_WEIGHT_200; break; + case SVG_FONTWEIGHT_300: styles |= GF_FONT_WEIGHT_300; break; + case SVG_FONTWEIGHT_400: styles |= GF_FONT_WEIGHT_400; break; + case SVG_FONTWEIGHT_NORMAL: styles |= GF_FONT_WEIGHT_NORMAL; break; + case SVG_FONTWEIGHT_500: styles |= GF_FONT_WEIGHT_500; break; + case SVG_FONTWEIGHT_600: styles |= GF_FONT_WEIGHT_600; break; + case SVG_FONTWEIGHT_700: styles |= GF_FONT_WEIGHT_700; break; + case SVG_FONTWEIGHT_BOLD: styles |= GF_FONT_WEIGHT_BOLD; break; + case SVG_FONTWEIGHT_800: styles |= GF_FONT_WEIGHT_800; break; + case SVG_FONTWEIGHT_900: styles |= GF_FONT_WEIGHT_900; break; + case SVG_FONTWEIGHT_BOLDER: styles |= GF_FONT_WEIGHT_BOLDER; break; + } + + return styles; +} + + +GF_Font *gf_compositor_svg_set_font(GF_FontManager *fm, char *a_font, u32 styles, Bool check_only) +{ + GF_Font *font = NULL; + char *fonts[50]; + u32 nb_fonts = 0; + + while (a_font) { + char *sep; + while (strchr("\t\r\n ", a_font[0])) a_font++; + + sep = strchr(a_font, ','); + if (sep) sep[0] = 0; + + if (a_font[0] == '\'') { + char *sep_end = strchr(a_font+1, '\''); + if (sep_end) sep_end[0] = 0; + a_font++; + fonts[nb_fonts] = strdup(a_font); + nb_fonts++; + if (sep_end) sep_end[0] = '\''; + } else { + u32 skip, len = strlen(a_font)-1; + skip = 0; + while (a_font[len-skip] == ' ') skip++; + if (skip) a_font[len-skip+1] = 0; + fonts[nb_fonts] = strdup(a_font); + nb_fonts++; + if (skip) a_font[len-skip] = ' '; + } + + if (sep) { + sep[0] = ','; + a_font = sep+1; + } else { + a_font = NULL; + } + if (nb_fonts==50) break; + } + font = gf_font_manager_set_font_ex(fm, fonts, nb_fonts, styles, check_only); + while (nb_fonts) { + free(fonts[nb_fonts-1]); + nb_fonts--; + } + return font; +} + +static GF_Font *svg_set_font(GF_TraverseState * tr_state, GF_FontManager *fm) +{ + return gf_compositor_svg_set_font(fm, tr_state->svg_props->font_family->value, svg_get_font_styles(tr_state), 0); +} + + + +static void svg_apply_text_anchor(GF_TraverseState * tr_state, Fixed *width) +{ + Bool reversed=0; + if (!tr_state->svg_props->text_anchor) { + *width = 0; + return; + } + if (*width < 0) { + *width *= -1; + reversed = 1; + } + switch(*tr_state->svg_props->text_anchor) { + case SVG_TEXTANCHOR_MIDDLE: + *width = -(*width)/2; + break; + case SVG_TEXTANCHOR_END: + *width = reversed ? 0 : -(*width); + break; + case SVG_TEXTANCHOR_START: + default: + *width = reversed ? -(*width) : 0; + break; + } +} + +static GF_TextSpan *svg_get_text_span(GF_FontManager *fm, GF_Font *font, Fixed font_size, Bool x_offsets, Bool y_offsets, Bool rotate, SVGAllAttributes *atts, char *textContent, const char *lang, GF_TraverseState *tr_state) +{ + GF_TextSpan *span = NULL; + char *dup_text; + u32 i, j, len; + char prev; + + Bool preserve = (atts->xml_space && (*atts->xml_space==XML_SPACE_PRESERVE)) ? 1 : 0; + + len = strlen(textContent); + dup_text = malloc(len+1); + + switch (tr_state->last_char_type) { + case 2: + prev = 0; + break; + case 0: + case 1: + default: + prev = ' '; + break; + } + for (i = 0, j = 0; i < len; i++) { + if (textContent[i] == ' ') { + if (prev == ' ' && !preserve) { + /* ignore space */ + } else { + dup_text[j] = textContent[i]; + prev = dup_text[j]; + j++; + } + } else if (textContent[i] == '\t') { + if (prev == ' ' && !preserve) { + /* ignore space */ + } else { + dup_text[j] = ' '; + prev = dup_text[j]; + j++; + } + } else if ((textContent[i] == '\n') || + (textContent[i] == '\r') + ) { + if (prev == ' ' && preserve) { + dup_text[j] = ' '; + prev = dup_text[j]; + j++; + } else if (!i && !prev) { + prev = dup_text[j] = ' '; + j++; + } + } else if ( + (((u8) textContent[i] == 0xc2) && ((u8) textContent[i+1] == 0xa0)) + ) { + if (prev == ' ' && !preserve) { + /* ignore space */ + } else { + dup_text[j] = ' '; + prev = dup_text[j]; + j++; + } + i++; + } else { + dup_text[j] = textContent[i]; + prev = dup_text[j]; + j++; + } + } + dup_text[j] = 0; + if (!j) tr_state->last_char_type = 1; + else tr_state->last_char_type = (dup_text[j-1]==' ') ? 1 : 2; + /*SVG text is fliped by default (text y-axis is the inverse of SVG y-axis*/ + span = gf_font_manager_create_span(fm, font, dup_text, font_size, x_offsets, y_offsets, rotate, lang, 1, 0, tr_state->text_parent); + free(dup_text); + if (span) span->flags |= GF_TEXT_SPAN_HORIZONTAL; + return span; +} + + + +typedef struct +{ + GF_TextSpan *span; + u32 first_glyph, last_glyph; +} textArea_state; + +static void svg_text_area_reset_state(GF_TraverseState *tr_state) +{ + Fixed remain = 0; + u32 i, count; + count = gf_list_count(tr_state->x_anchors); + + if (tr_state->svg_props->text_align && tr_state->text_end_x) { + switch(*tr_state->svg_props->text_align) { + case SVG_TEXTALIGN_CENTER: + remain = (tr_state->max_length - tr_state->text_end_x) / 2; + break; + case SVG_TEXTALIGN_END: + remain = tr_state->max_length - tr_state->text_end_x; + break; + default: + remain = 0; + break; + } + } + + + for (i=0; ix_anchors, i); + if (remain) { + u32 j; + for (j=st->first_glyph; jlast_glyph; j++) { + st->span->dx[j] += remain; + } + tr_state->refresh_children_bounds = 1; + } + free(st); + } + gf_list_reset(tr_state->x_anchors); +} + +static void svg_text_area_queue_state(GF_TraverseState *tr_state, GF_TextSpan *span, u32 first_glyph, u32 last_glyph) +{ + textArea_state *st; + u32 i, count; + count = gf_list_count(tr_state->x_anchors); + for (i=0; ix_anchors, i); + if (st->span==span) { + st->last_glyph = last_glyph; + return; + } + } + st = malloc(sizeof(textArea_state)); + st->first_glyph = first_glyph; + st->last_glyph = last_glyph; + st->span = span; + gf_list_add(tr_state->x_anchors, st); + +} + +static void svg_text_area_apply_diff_baselines(GF_TraverseState *tr_state, Fixed diff) +{ + u32 i, count, j; + count = gf_list_count(tr_state->x_anchors); + tr_state->refresh_children_bounds++; + + for (i=0; ix_anchors, i); + + for (j=st->first_glyph; jlast_glyph; j++) { + st->span->dy[j] += diff; + } + } +} + + +static void svg_traverse_dom_text_area(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state, GF_List *spans) +{ + GF_DOMText *dom_text = (GF_DOMText *)node; + u32 word_start, word_end; + u32 i, j; + Fixed line_spacing; + GF_Font *font; + GF_FontManager *fm; + GF_TextSpan *span; + + if (!dom_text->textContent) return; + if (tr_state->svg_props->font_size->value > tr_state->max_height) return; + + fm = tr_state->visual->compositor->font_manager; + if (!fm) return; + + font = svg_set_font(tr_state, fm); + if (!font) return; + + span = svg_get_text_span(fm, font, tr_state->svg_props->font_size->value, 1, 1, 0, atts, dom_text->textContent, atts->xml_lang ? *atts->xml_lang : NULL, tr_state); + if (!span) return; + + /*first run of the line, inc text y*/ + if (tr_state->line_spacing==0) { + if (tr_state->svg_props->line_increment->type != SVG_NUMBER_AUTO) { + tr_state->line_spacing = tr_state->svg_props->line_increment->value; + } else { + tr_state->line_spacing = gf_mulfix(span->font_size, FLT2FIX(1.0) ); + } + tr_state->text_end_y += tr_state->line_spacing; + } + + line_spacing = gf_mulfix(span->font_size, FLT2FIX(1.0) ); + + word_start = word_end = 0; + i = 0; + /* boucle principale: mot par mot */ + while (inb_glyphs) { + Fixed word_size, last_char_size, offset, word_descent; + u32 break_glyph = 0; + + word_descent = (-span->font->descent) * span->font_scale; + word_start = i; + word_size = last_char_size = 0; + while (inb_glyphs) { + Fixed glyph_size; + if (span->glyphs[i]) { + /*look for word boundaries*/ + if ( (span->glyphs[i]->utf_name==' ') || (span->glyphs[i]->utf_name=='-') ) { + last_char_size = span->glyphs[i]->horiz_advance * span->font_scale; + i++; + break; + } + glyph_size = span->glyphs[i]->horiz_advance * span->font_scale; + if (word_size + glyph_size> tr_state->max_length) { + break_glyph = i; + break; + } + word_size += glyph_size; + } + i++; + } + + /* word doesn't fit on line, escape*/ + if (!word_size && !last_char_size) break; + + if (tr_state->text_end_x + word_size > tr_state->max_length) { + /* if the word doesn't fit on line, escape*/ + if (word_size > tr_state->max_length) { + word_start=break_glyph; + break; + } + svg_text_area_reset_state(tr_state); + tr_state->text_end_x = 0; + tr_state->line_spacing = line_spacing; + + tr_state->text_end_y += (tr_state->svg_props->line_increment->type == SVG_NUMBER_AUTO ? tr_state->line_spacing : tr_state->svg_props->line_increment->value); + /* out of area, abort processing*/ + if (tr_state->text_end_y > tr_state->max_height) break; + } else { + /* first word is too high for the area*/ + if (tr_state->text_end_y + word_descent > tr_state->max_height) + break; + + /* stay on current line*/ + if (line_spacing > tr_state->line_spacing) { + svg_text_area_apply_diff_baselines(tr_state, line_spacing - tr_state->line_spacing); + tr_state->text_end_y -= tr_state->line_spacing; + tr_state->text_end_y += line_spacing; + tr_state->line_spacing = line_spacing; + } + + } + word_size += last_char_size; + + + offset = tr_state->base_x + tr_state->text_end_x; + for (j=word_start; jdx[j] = offset; + span->dy[j] = tr_state->base_y + tr_state->text_end_y; + offset += (span->glyphs[j] ? span->glyphs[j]->horiz_advance : font->max_advance_h) * span->font_scale; + } + tr_state->text_end_x += word_size; +// if (tr_state->y_step < word_height) tr_state->y_step = word_height; + + svg_text_area_queue_state(tr_state, span, word_start, i); + word_start = i; + } + span->nb_glyphs = word_start; + /*add span path to list of spans*/ + gf_list_add(spans, span); +} + +static void get_domtext_width(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state) +{ + u32 i; + GF_Font *font; + Fixed block_width, *entry; + GF_FontManager *fm; + GF_TextSpan *span; + GF_DOMText *dom_text = (GF_DOMText *)node; + + if (!dom_text->textContent) return; + + fm = tr_state->visual->compositor->font_manager; + if (!fm) return; + + font = svg_set_font(tr_state, fm); + if (!font) return; + + span = svg_get_text_span(fm, font, tr_state->svg_props->font_size->value, (tr_state->count_x>1), (tr_state->count_y>1), 0, atts, dom_text->textContent, atts->xml_lang ? *atts->xml_lang : NULL, tr_state); + if (!span) return; + + i=0; + //count_x, _y: number of x- (y-) position of characters to come in the text flow + while ( (inb_glyphs) + && ( (tr_state->count_x>1) || (tr_state->count_y>1) ) + ) { + block_width = (span->glyphs[i] ? span->glyphs[i]->horiz_advance : font->max_advance_h) * span->font_scale; + + //store width in tr_state->x_anchors + entry = (Fixed*)malloc(sizeof(Fixed)); + if (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT) *entry = -block_width; + else *entry = block_width; + + gf_list_add(tr_state->x_anchors, entry); + + if (tr_state->count_x>0) tr_state->count_x--; + if (tr_state->count_y>0) tr_state->count_y--; + i++; + } + + //chars are taken one by one while there are indicated positions, then remaining chars are treated as a block + if (inb_glyphs) { + block_width = 0; + while (inb_glyphs) { + block_width += (span->glyphs[i] ? span->glyphs[i]->horiz_advance : font->max_advance_h) * span->font_scale; + i++; + } + //if last indicated position, create a new item + if ((tr_state->count_x==1)||(tr_state->count_y==1) + || !gf_list_count(tr_state->x_anchors) ) { + entry = (Fixed*)malloc(sizeof(Fixed)); + *entry = block_width; + + if (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT) *entry = -block_width; + else *entry = block_width; + + gf_list_add(tr_state->x_anchors, entry); + } else { // (count_x == 0 && count_y == 0) otherwise increment last one + Fixed *prec_lw=gf_list_last(tr_state->x_anchors); + (*prec_lw) += block_width; + } + //force counters to 0 for next spans/DOM texts + if (tr_state->count_x==1) tr_state->count_x = 0; + if (tr_state->count_y==1) tr_state->count_y = 0; + } + gf_font_manager_delete_span(fm, span); +} + +static void get_tspan_width(GF_Node *node, void *rs) +{ + SVGPropertiesPointers backup_props; + u32 backup_flags; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + SVG_Element *tspan = (SVG_Element *)node; + SVGAllAttributes atts; + GF_ChildNodeItem *child; + + gf_svg_flatten_attributes(tspan, &atts); + if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) + return; + + child = ((GF_ParentNode *) tspan)->children; + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_DOMText: + get_domtext_width(child->node, &atts, tr_state); + break; + case TAG_SVG_tspan: + get_tspan_width(child->node, tr_state); + break; + default: + break; + } + child=child->next; + } + + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; +} + +void svg_traverse_domtext(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state, GF_List *spans, GF_Node *anchor_node) +{ + GF_DOMText *dom_text = (GF_DOMText *)node; + Fixed x, y; + u32 i; + Fixed x_anchor, *ptr; + GF_Font *font; + Fixed block_width; + GF_FontManager *fm; + GF_TextSpan *span; + + if (!dom_text->textContent) return; + + if (tr_state->in_svg_text_area) { + svg_traverse_dom_text_area(node, atts, tr_state, spans); + return; + } + + fm = tr_state->visual->compositor->font_manager; + if (!fm) return; + + font = svg_set_font(tr_state, fm); + if (!font) return; + + span = svg_get_text_span(fm, font, tr_state->svg_props->font_size->value, (tr_state->count_x>1), (tr_state->count_y>1), tr_state->count_rotate, atts, dom_text->textContent, atts->xml_lang ? *atts->xml_lang : NULL, tr_state); + if (!span) return; + + i=0; + /* + if character position is given in (x, y) attributes, use it. + Otherwise add text at tr_state->text_end_x. + */ + while ((inb_glyphs) + && ( (tr_state->count_x>1) || (tr_state->count_y>1) ) + ) { + //get x and y positions + if (tr_state->count_x==0) { + x = tr_state->text_end_x; + } else { + SVG_Coordinate *xc = (SVG_Coordinate *) gf_list_get(*tr_state->text_x, tr_state->chunk_index); + x = xc->value; + (tr_state->count_x)--; + } + if (tr_state->count_y==0) { + y = tr_state->text_end_y; + } else { + SVG_Coordinate *yc = (SVG_Coordinate *) gf_list_get(*tr_state->text_y, tr_state->chunk_index); + y = yc->value; + (tr_state->count_y)--; + } + + + /*apply x-anchor*/ + ptr = (Fixed *)gf_list_get(tr_state->x_anchors, tr_state->chunk_index); + x_anchor = ptr ? *ptr : 0; + if (span->dx) span->dx[i] = x_anchor + x; + else if (!i) span->off_x = x_anchor + x; + if (span->dy) span->dy[i] = y; + else span->off_y = y; + + if (tr_state->count_rotate) { + SVG_Coordinate *rc = (SVG_Coordinate *) gf_list_get(*tr_state->text_rotate, tr_state->idx_rotate); + span->rot[i] = gf_mulfix(GF_PI/180, rc->value); + if (tr_state->idx_rotate+1count_rotate) tr_state->idx_rotate++; + } + + /*update last glyph position*/ + block_width = (span->glyphs[i] ? span->glyphs[i]->horiz_advance : font->max_advance_h) * span->font_scale; + tr_state->text_end_x = x+block_width; + tr_state->text_end_y = y; + (tr_state->chunk_index)++; + i++; + } + + /* no more positions, add remaining glyphs as a block*/ + if (inb_glyphs) { + Fixed offset; + if ((tr_state->count_x==1) && tr_state->text_x) { + SVG_Coordinate *xc = (SVG_Coordinate *) gf_list_get(*tr_state->text_x, tr_state->chunk_index); + tr_state->text_end_x = xc->value; + (tr_state->count_x)--; + } + if ((tr_state->count_y==1) && tr_state->text_y) { + SVG_Coordinate *yc = (SVG_Coordinate *) gf_list_get(*tr_state->text_y, tr_state->chunk_index); + tr_state->text_end_y = yc->value; + (tr_state->count_y)--; + } + + x = tr_state->text_end_x; + y = tr_state->text_end_y; + + /*apply x anchor*/ + ptr = (Fixed *)gf_list_get(tr_state->x_anchors, tr_state->chunk_index); + x_anchor = ptr ? *ptr : 0; + + offset = x_anchor + x - (span->dx ? span->dx[i] : span->off_x); + + if (!span->dx && (tr_state->text_x || x_anchor)) span->off_x = x_anchor + x; + if (!span->dy && tr_state->text_y) span->off_y = y; + + block_width = 0; + while (inb_glyphs) { + + if (span->rot) { + SVG_Coordinate *rc = (SVG_Coordinate *) gf_list_get(*tr_state->text_rotate, tr_state->idx_rotate); + span->rot[i] = gf_mulfix(GF_PI/180, rc->value); + if (tr_state->idx_rotate+1count_rotate) tr_state->idx_rotate++; + } + if (span->dx) span->dx[i] = offset + block_width; + if (span->dy) span->dy[i] = y; + block_width += (span->glyphs[i] ? span->glyphs[i]->horiz_advance : font->max_advance_h) * span->font_scale; + + i++; + } + tr_state->text_end_x += block_width; + } + + /*add span path to list of spans*/ + gf_list_add(spans, span); + span->anchor = anchor_node; +} + + +static void svg_compute_text_width(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state ) +{ + GF_ChildNodeItem *child; + Bool is_switch = 0; + /*compute length of all text blocks*/ + switch (gf_node_get_tag(node)) { + case TAG_DOMText: + get_domtext_width(node, atts, tr_state); + break; + case TAG_SVG_tspan: + get_tspan_width(node, tr_state); + break; + case TAG_SVG_switch: + is_switch = 1; + case TAG_SVG_a: + child = ((GF_ParentNode *)node)->children; + while (child) { + if (is_switch) { + SVGAllAttributes a_atts; + gf_svg_flatten_attributes((SVG_Element*)child->node, &a_atts); + if (compositor_svg_evaluate_conditional(tr_state->visual->compositor, &a_atts)) { + svg_compute_text_width(child->node, atts, tr_state); + break; + } + } else { + svg_compute_text_width(child->node, atts, tr_state); + } + child = child->next; + } + break; + default: + break; + } +} + +static void svg_traverse_text_block(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state, GF_List *spans) +{ + GF_ChildNodeItem *child; + Bool is_switch = 0; + switch (gf_node_get_tag(node)) { + case TAG_DOMText: + svg_traverse_domtext(node, atts, tr_state, spans, NULL); + break; + case TAG_SVG_tspan: + /*mark tspan as dirty to force rebuild*/ + gf_node_dirty_set(node, 0, 0); + gf_node_traverse(node, tr_state); + break; + case TAG_SVG_switch: + is_switch = 1; + case TAG_SVG_a: + child = ((GF_ParentNode *)node)->children; + while (child) { + if (is_switch) { + SVGAllAttributes a_atts; + gf_svg_flatten_attributes((SVG_Element*)child->node, &a_atts); + if (compositor_svg_evaluate_conditional(tr_state->visual->compositor, &a_atts)) { + svg_traverse_text_block(child->node, atts, tr_state, spans); + break; + } + } else if (gf_node_get_tag(child->node)==TAG_DOMText) { + svg_traverse_domtext(child->node, atts, tr_state, spans, node); + } + child = child->next; + } + break; + default: + break; + } +} + +static void svg_text_draw_2d(SVG_TextStack *st, GF_TraverseState *tr_state) +{ + gf_font_spans_draw_2d(st->spans, tr_state, 0, 0, &st->bounds); +} + + +static void svg_text_area_shift_bounds(SVG_TextStack *st, GF_TraverseState *tr_state) +{ + u32 i=0; + GF_TextSpan *span; + /*finally compute text bounds*/ + st->bounds.width = st->bounds.height = 0; + st->bounds.x = st->bounds.y = 0; + while ( (span = gf_list_enum(st->spans, &i)) ) { + u32 j; + for (j=0; jnb_glyphs; j++) + span->dy[j] += tr_state->base_shift; + + gf_font_manager_refresh_span_bounds(span); + gf_rect_union(&st->bounds, &span->bounds); + } +} + + +static void svg_traverse_text(GF_Node *node, void *rs, Bool is_destroy) +{ + SVGPropertiesPointers backup_props; + u32 backup_flags; + GF_Matrix2D backup_matrix; + GF_Matrix mx3d; + GF_ChildNodeItem *child; + DrawableContext *ctx; + SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + SVG_Element *text = (SVG_Element *)node; + SVGAllAttributes atts; + u32 i,imax; + Fixed * lw; + + if (is_destroy) { + drawable_del(st->drawable); + svg_reset_text_stack(st); + gf_list_del(st->spans); + free(st); + return; + } + + if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { + svg_text_draw_2d(st, tr_state); + return; + } + else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { + tr_state->text_parent = node; + gf_font_spans_get_selection(node, st->spans, tr_state); + /*and browse children*/ + child = ((GF_ParentNode *) text)->children; + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_SVG_tspan: + gf_node_traverse(child->node, tr_state); + break; + } + child = child->next; + } + tr_state->text_parent = NULL; + return; + } + + gf_svg_flatten_attributes(text, &atts); + if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) + return; + + tr_state->in_svg_text++; + tr_state->text_parent = node; + + if (tr_state->traversing_mode==TRAVERSE_PICK) { + compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); + if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE) + gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, 1, st->drawable); + + /*and browse children*/ + child = ((GF_ParentNode *) text)->children; + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_SVG_tspan: + gf_node_traverse(child->node, tr_state); + break; + } + child = child->next; + } + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); + tr_state->svg_flags = backup_flags; + tr_state->text_parent = NULL; + tr_state->in_svg_text--; + return; + } + else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { + gf_font_spans_get_selection(node, st->spans, tr_state); + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; + tr_state->text_parent = NULL; + tr_state->in_svg_text--; + return; + } + + compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); + + if ( (st->prev_size != tr_state->svg_props->font_size->value) || + (st->prev_flags != *tr_state->svg_props->font_style) || + (st->prev_anchor != *tr_state->svg_props->text_anchor) || + (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) ) + || tr_state->visual->compositor->reset_fonts + ) { + u32 mode; + child = ((GF_ParentNode *) text)->children; + + svg_reset_text_stack(st); + tr_state->text_end_x = 0; + tr_state->text_end_y = 0; + /*init the xml:space algo*/ + tr_state->last_char_type = 0; + + /*initialize x and y counters - stored at the traverse level for handling tspan & co*/ + if (atts.text_x) tr_state->count_x = gf_list_count(*atts.text_x); + else tr_state->count_x=0; + if (atts.text_y) tr_state->count_y = gf_list_count(*atts.text_y); + else tr_state->count_y=0; + if (atts.text_rotate) tr_state->count_rotate = gf_list_count(*atts.text_rotate); + else tr_state->count_rotate=0; + + /*horizontal justifiers container*/ + tr_state->x_anchors = gf_list_new(); + + /*compute length of all text blocks*/ + while (child) { + svg_compute_text_width(child->node, &atts, tr_state); + child=child->next; + } + + /*apply justification of all blocks*/ + imax=gf_list_count(tr_state->x_anchors); + for (i=0;ix_anchors, i); + svg_apply_text_anchor(tr_state, lw); + } + + /*re-initialize x and y counters for final compute*/ + if (atts.text_x) tr_state->count_x = gf_list_count(*atts.text_x); + else tr_state->count_x=0; + if (atts.text_y) tr_state->count_y = gf_list_count(*atts.text_y); + else tr_state->count_y=0; + if (atts.text_rotate) tr_state->count_rotate = gf_list_count(*atts.text_rotate); + else tr_state->count_rotate=0; + tr_state->idx_rotate = 0; + tr_state->chunk_index = 0; + + /*initialize current text position*/ + if (!tr_state->text_end_x){ + SVG_Coordinate *xc = (atts.text_x ? (SVG_Coordinate *) gf_list_get(*atts.text_x, 0) : NULL); + tr_state->text_end_x = (xc ? xc->value : 0); + } + if (!tr_state->text_end_y){ + SVG_Coordinate *yc = (atts.text_y ? (SVG_Coordinate *) gf_list_get(*atts.text_y, 0) : NULL); + tr_state->text_end_y = (yc ? yc->value : 0); + } + + /*pass x and y to children*/ + tr_state->text_x = atts.text_x; + tr_state->text_y = atts.text_y; + tr_state->text_rotate = atts.text_rotate; + + drawable_reset_path(st->drawable); + + /*switch to bounds mode, and recompute children*/ + mode = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + tr_state->last_char_type = 0; + + child = ((GF_ParentNode *) text)->children; + while (child) { + svg_traverse_text_block(child->node, &atts, tr_state, st->spans); + child = child->next; + } + tr_state->traversing_mode = mode; + gf_node_dirty_clear(node, 0); + drawable_mark_modified(st->drawable, tr_state); + st->prev_size = tr_state->svg_props->font_size->value; + st->prev_flags = *tr_state->svg_props->font_style; + st->prev_anchor = *tr_state->svg_props->text_anchor; + + while (gf_list_count(tr_state->x_anchors)) { + Fixed *f = gf_list_last(tr_state->x_anchors); + gf_list_rem_last(tr_state->x_anchors); + free(f); + } + gf_list_del(tr_state->x_anchors); + tr_state->x_anchors = NULL; + + svg_update_bounds(st); + } + + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + if (!compositor_svg_is_display_off(tr_state->svg_props)) + tr_state->bounds = st->bounds; + + } else if ((tr_state->traversing_mode == TRAVERSE_SORT) + && !compositor_svg_is_display_off(tr_state->svg_props) + && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) + ) { + ctx = drawable_init_context_svg(st->drawable, tr_state); + if (ctx) svg_finalize_sort(ctx, st, tr_state); + + /*and browse children*/ + child = ((GF_ParentNode *) text)->children; + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_SVG_tspan: + gf_node_traverse(child->node, tr_state); + break; + case TAG_SVG_switch: + gf_node_traverse(child->node, tr_state); + break; + } + child = child->next; + } + } + tr_state->in_svg_text--; + tr_state->text_parent = NULL; + + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; +} + + +void compositor_init_svg_text(GF_Compositor *compositor, GF_Node *node) +{ + SVG_TextStack *stack; + GF_SAFEALLOC(stack, SVG_TextStack); + stack->drawable = drawable_new(); + stack->drawable->node = node; + stack->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW; + stack->spans = gf_list_new(); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_text); +} + + +static void svg_traverse_tspan(GF_Node *node, void *rs, Bool is_destroy) +{ + SVGPropertiesPointers backup_props; + u32 backup_flags; + GF_Matrix2D backup_matrix; + GF_Matrix mx3d; + DrawableContext *ctx; + SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + SVG_Element *tspan = (SVG_Element *)node; + SVGAllAttributes atts; + GF_ChildNodeItem *child; + + if (is_destroy) { + drawable_del(st->drawable); + svg_reset_text_stack(st); + gf_list_del(st->spans); + free(st); + return; + } + if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { + svg_text_draw_2d(st, tr_state); + return; + } + else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { + gf_font_spans_get_selection(node, st->spans, tr_state); + /*and browse children*/ + child = ((GF_ParentNode *) tspan)->children; + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_SVG_tspan: + gf_node_traverse(child->node, tr_state); + break; + } + child = child->next; + } + return; + } + + if (!tr_state->in_svg_text && !tr_state->in_svg_text_area) return; + + gf_svg_flatten_attributes(tspan, &atts); + if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) + return; + + if (tr_state->traversing_mode==TRAVERSE_PICK) { + if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE) + gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, 1, st->drawable); + + /*and browse children*/ + child = ((GF_ParentNode *) tspan)->children; + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_SVG_tspan: + gf_node_traverse(child->node, tr_state); + break; + } + child = child->next; + } + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; + return; + } + + compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); + + if ( (st->prev_size != tr_state->svg_props->font_size->value) || + (st->prev_flags != *tr_state->svg_props->font_style) || + (st->prev_anchor != *tr_state->svg_props->text_anchor) || + (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) ) + ) { + u32 mode; + + /*tspan has been modified in the SORT stage, which means that an anim local to tspan has modified the node. + The result of the parent (text, textArea) will thus be wrong if we try to update the tspan. We therefore + keep the previous computed drawable, and invalidate the parent for next frame*/ + if (tr_state->traversing_mode==TRAVERSE_SORT) { + gf_node_dirty_set(node, 0, 1); + goto skip_changes; + } + + /*switch to bounds mode, and recompute children*/ + mode = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + + svg_reset_text_stack(st); + child = ((GF_ParentNode *) tspan)->children; + + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_DOMText: + svg_traverse_domtext(child->node, &atts, tr_state, st->spans, NULL); + break; + case TAG_SVG_tspan: + gf_node_dirty_set(child->node, 0, 0); + gf_node_traverse(child->node, tr_state); + break; + case TAG_SVG_switch: + case TAG_SVG_a: + case TAG_SVG_tbreak: + gf_node_traverse(child->node, tr_state); + break; + default: + break; + } + child = child->next; + } + tr_state->traversing_mode = mode; + gf_node_dirty_clear(node, 0); + drawable_mark_modified(st->drawable, tr_state); + st->prev_size = tr_state->svg_props->font_size->value; + st->prev_flags = *tr_state->svg_props->font_style; + st->prev_anchor = *tr_state->svg_props->text_anchor; + + svg_update_bounds(st); + } +skip_changes: + + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + if (tr_state->refresh_children_bounds) { + if (tr_state->base_shift) + svg_text_area_shift_bounds(st, tr_state); + else + svg_update_bounds(st); + child = ((GF_ParentNode *) tspan)->children; + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_SVG_tspan: + case TAG_SVG_switch: + case TAG_SVG_a: + gf_node_traverse(child->node, tr_state); + break; + default: + break; + } + child = child->next; + } + } + if (!compositor_svg_is_display_off(tr_state->svg_props)) + tr_state->bounds = st->bounds; + + } + else if ( + (tr_state->traversing_mode == TRAVERSE_SORT) + && !compositor_svg_is_display_off(tr_state->svg_props) + && ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) + ) { + child = ((GF_ParentNode *) tspan)->children; + + ctx = drawable_init_context_svg(st->drawable, tr_state); + if (ctx) svg_finalize_sort(ctx, st, tr_state); + + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_SVG_tspan: + case TAG_SVG_switch: + case TAG_SVG_a: + gf_node_traverse(child->node, tr_state); + break; + default: + break; + } + child = child->next; + } + } + + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; +} + +void compositor_init_svg_tspan(GF_Compositor *compositor, GF_Node *node) +{ + SVG_TextStack *stack; + GF_SAFEALLOC(stack, SVG_TextStack); + stack->drawable = drawable_new(); + stack->drawable->node = node; + stack->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW; + stack->spans = gf_list_new(); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_tspan); +} + + +static void svg_traverse_textArea(GF_Node *node, void *rs, Bool is_destroy) +{ + SVGPropertiesPointers backup_props; + u32 backup_flags; + GF_Matrix mx3d; + GF_Matrix2D backup_matrix; + DrawableContext *ctx = NULL; + GF_ChildNodeItem *child; + SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + SVG_Element *text = (SVG_Element *)node; + SVGAllAttributes atts; + + if (is_destroy) { + drawable_del(st->drawable); + svg_reset_text_stack(st); + gf_list_del(st->spans); + free(st); + return; + } + + if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { + svg_text_draw_2d(st, tr_state); + return; + } + else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { + tr_state->text_parent = node; + gf_font_spans_get_selection(node, st->spans, tr_state); + /*and browse children*/ + child = ((GF_ParentNode *) text)->children; + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_SVG_tspan: + gf_node_traverse(child->node, tr_state); + break; + } + child = child->next; + } + tr_state->text_parent = NULL; + return; + } + + + gf_svg_flatten_attributes(text, &atts); + if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) + return; + + tr_state->text_parent = node; + tr_state->in_svg_text_area++; + + if (tr_state->traversing_mode==TRAVERSE_PICK) { + if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE) { + compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); + gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, 1, st->drawable); + + /*and browse children*/ + child = ((GF_ParentNode *) node)->children; + while (child) { + gf_node_traverse(child->node, tr_state); + child = child->next; + } + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; + } + tr_state->in_svg_text_area--; + tr_state->text_parent = NULL; + return; + } + + compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); + + if ( (st->prev_size != tr_state->svg_props->font_size->value) || + (st->prev_flags != *tr_state->svg_props->font_style) || + (st->prev_anchor != *tr_state->svg_props->text_anchor) || + (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) ) + || tr_state->visual->compositor->reset_fonts + ) { + u32 mode; + + child = ((GF_ParentNode *) text)->children; + + svg_reset_text_stack(st); + gf_node_dirty_clear(node, 0); + drawable_mark_modified(st->drawable, tr_state); + drawable_reset_path(st->drawable); + + tr_state->max_length = (atts.width ? (atts.width->type == SVG_NUMBER_AUTO ? FIX_MAX : atts.width->value) : FIX_MAX); + tr_state->max_height = (atts.height ? (atts.height->type == SVG_NUMBER_AUTO ? FIX_MAX : atts.height->value) : FIX_MAX); + tr_state->base_x = (atts.x ? atts.x->value : 0); + tr_state->base_y = (atts.y ? atts.y->value : 0); + /*init the xml:space algo*/ + tr_state->last_char_type = 0; + /*let it initialize from first font*/ + tr_state->line_spacing = 0; + tr_state->text_end_x = 0; + tr_state->text_end_y = (tr_state->svg_props->line_increment->type == SVG_NUMBER_AUTO ? 0 : tr_state->svg_props->line_increment->value); + + tr_state->x_anchors = gf_list_new(); + + if (tr_state->svg_props->font_size && (tr_state->svg_props->font_size->value <= tr_state->max_height)) { + Fixed remain; + u32 c, refresh_to_idx, prev_refresh; + /*switch to bounds mode, and recompute children*/ + mode = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + + prev_refresh = tr_state->refresh_children_bounds; + tr_state->refresh_children_bounds = 0; + c = refresh_to_idx = 0; + child = ((GF_ParentNode *) text)->children; + while (child) { + c++; + switch (gf_node_get_tag(child->node)) { + case TAG_DOMText: + svg_traverse_dom_text_area(child->node, &atts, tr_state, st->spans); + break; + case TAG_SVG_tspan: + /*mark tspan as dirty to force rebuild*/ + gf_node_dirty_set(child->node, 0, 0); + gf_node_traverse(child->node, tr_state); + break; + case TAG_SVG_switch: + case TAG_SVG_a: + case TAG_SVG_tbreak: + gf_node_traverse(child->node, tr_state); + break; + default: + break; + } + if (tr_state->refresh_children_bounds) { + tr_state->refresh_children_bounds=0; + refresh_to_idx=c; + } + + child=child->next; + } + st->prev_size = tr_state->svg_props->font_size->value; + st->prev_flags = *tr_state->svg_props->font_style; + st->prev_anchor = *tr_state->svg_props->text_anchor; + + svg_text_area_reset_state(tr_state); + gf_list_del(tr_state->x_anchors); + tr_state->x_anchors = NULL; + + remain = 0; + if (tr_state->refresh_children_bounds) { + refresh_to_idx = (u32) -1; + tr_state->base_shift = 0; + } + if (tr_state->svg_props->display_align) { + switch (*tr_state->svg_props->display_align) { + case SVG_DISPLAYALIGN_CENTER: + remain = (tr_state->max_height-tr_state->text_end_y) / 2; + break; + case SVG_DISPLAYALIGN_AFTER: + remain = tr_state->max_height - tr_state->text_end_y; + break; + default: + remain = 0; + break; + } + if (remain<0) remain=0; + if (remain) { + refresh_to_idx = (u32) -1; + tr_state->base_shift = remain; + svg_text_area_shift_bounds(st, tr_state); + } + } + + if (refresh_to_idx) { + tr_state->refresh_children_bounds=1; + /*and retraverse in case of bounds adjustements*/ + child = ((GF_ParentNode *) text)->children; + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_DOMText: + break; + case TAG_SVG_tspan: + case TAG_SVG_switch: + case TAG_SVG_a: + gf_node_traverse(child->node, tr_state); + break; + default: + break; + } + child=child->next; + refresh_to_idx--; + if (!refresh_to_idx) break; + } + tr_state->base_shift = 0; + } + tr_state->traversing_mode = mode; + tr_state->refresh_children_bounds = prev_refresh; + } + svg_update_bounds(st); + } + + if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { + if (!compositor_svg_is_display_off(tr_state->svg_props)) + tr_state->bounds = st->bounds; + } else if ( (tr_state->traversing_mode == TRAVERSE_SORT) + && !compositor_svg_is_display_off(tr_state->svg_props) + && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) + ) { + + ctx = drawable_init_context_svg(st->drawable, tr_state); + if (ctx) svg_finalize_sort(ctx, st, tr_state); + + child = ((GF_ParentNode *) text)->children; + while (child) { + switch (gf_node_get_tag(child->node)) { + case TAG_DOMText: + break; + case TAG_SVG_tspan: + case TAG_SVG_switch: + case TAG_SVG_a: + gf_node_traverse(child->node, tr_state); + break; + default: + break; + } + child = child->next; + } + } + tr_state->in_svg_text_area--; + tr_state->text_parent = NULL; + + + compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; +} + +void compositor_init_svg_textarea(GF_Compositor *compositor, GF_Node *node) +{ + SVG_TextStack *stack; + GF_SAFEALLOC(stack, SVG_TextStack); + stack->drawable = drawable_new(); + stack->drawable->node = node; + stack->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW; + stack->spans = gf_list_new(); + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, svg_traverse_textArea); +} + +static void svg_traverse_tbreak(GF_Node *node, void *rs, Bool is_destroy) +{ + SVGPropertiesPointers backup_props; + u32 backup_flags; + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + SVGAllAttributes atts; + + if (is_destroy) return; + if (tr_state->traversing_mode!=TRAVERSE_GET_BOUNDS) return; + + gf_svg_flatten_attributes((SVG_Element*)node, &atts); + if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) + return; + + svg_text_area_reset_state(tr_state); + /*begining of a line, force a break of current fontSize*/ + if (!tr_state->text_end_x) { + if (tr_state->svg_props->line_increment->type != SVG_NUMBER_AUTO) { + tr_state->text_end_y += tr_state->svg_props->line_increment->value; + } else { + tr_state->text_end_y += tr_state->svg_props->font_size->value; + } + } + tr_state->line_spacing = 0; + tr_state->text_end_x = 0; + tr_state->last_char_type = 0; + + memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); + tr_state->svg_flags = backup_flags; +} + +void compositor_init_svg_tbreak(GF_Compositor *compositor, GF_Node *node) +{ + gf_node_set_callback_function(node, svg_traverse_tbreak); +} + +#endif /*GPAC_DISABLE_SVG*/ diff --git a/src/compositor/texturing.c b/src/compositor/texturing.c new file mode 100644 index 0000000..6c223ba --- /dev/null +++ b/src/compositor/texturing.c @@ -0,0 +1,256 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "texturing.h" +#include +#include + +#include "nodes_stacks.h" +#ifdef GPAC_TRISCOPE_MODE +#include "../src/compositor/triscope_renoir/triscope_renoir.h" +#endif + + +static void update_texture_void(GF_TextureHandler *txh) +{ +} + +GF_EXPORT +void gf_sc_texture_setup(GF_TextureHandler *txh, GF_Compositor *compositor, GF_Node *owner) +{ + memset(txh, 0, sizeof(GF_TextureHandler)); + txh->owner = owner; + txh->compositor = compositor; + /*insert texture in reverse order, so that textures in sub documents/scenes are updated before parent ones*/ + if (gf_list_find(compositor->textures, txh)<0) + gf_list_insert(compositor->textures, txh, 0); + if (!txh->update_texture_fcnt) txh->update_texture_fcnt = update_texture_void; +} + + +GF_EXPORT +void gf_sc_texture_destroy(GF_TextureHandler *txh) +{ + GF_Compositor *compositor = txh->compositor; + gf_mx_p(compositor->mx); + + if (txh->tx_io) gf_sc_texture_release(txh); + if (txh->is_open) gf_sc_texture_stop(txh); +#ifdef GPAC_TRISCOPE_MODE + /*Destroy the renoir object associated to this texture*/ + if (txh->RenoirObject) DestroyRenoirObject (txh->RenoirObject, (GF_RenoirHandler *) txh->compositor->RenoirHandler); +#endif + gf_list_del_item(txh->compositor->textures, txh); + + gf_mx_v(compositor->mx); +} + +GF_EXPORT +Bool gf_sc_texture_check_url_change(GF_TextureHandler *txh, MFURL *url) +{ + if (!txh->stream) return url->count; + return gf_mo_url_changed(txh->stream, url); +} + +GF_EXPORT +GF_Err gf_sc_texture_play_from_to(GF_TextureHandler *txh, MFURL *url, Double start_offset, Double end_offset, Bool can_loop, Bool lock_scene_timeline) +{ + if (txh->is_open) return GF_BAD_PARAM; + + /*if existing texture in cache destroy it - we don't destroy it on stop to handle MovieTexture*/ + if (txh->tx_io) gf_sc_texture_release(txh); + + /*store url*/ + gf_sg_vrml_field_copy(&txh->current_url, url, GF_SG_VRML_MFURL); + + /*get media object*/ + txh->stream = gf_mo_register(txh->owner, url, lock_scene_timeline); + /*bad/Empty URL*/ + if (!txh->stream) return GF_NOT_SUPPORTED; + /*request play*/ + gf_mo_play(txh->stream, start_offset, end_offset, can_loop); + + txh->last_frame_time = (u32) (-1); + gf_sc_invalidate(txh->compositor, NULL); + txh->is_open = 1; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_sc_texture_play(GF_TextureHandler *txh, MFURL *url) +{ + Double offset = 0; + Bool loop = 0; + if (txh->compositor->term && (txh->compositor->term->play_state!=GF_STATE_PLAYING)) { + offset = gf_node_get_scene_time(txh->owner); + loop = /*gf_mo_get_loop(gf_mo_register(txh->owner, url, 0), 0)*/ 1; + } + return gf_sc_texture_play_from_to(txh, url, offset, -1, loop, 0); +} + + +GF_EXPORT +void gf_sc_texture_stop(GF_TextureHandler *txh) +{ + if (!txh->is_open) return; + /*release texture WITHOUT droping frame*/ + if (txh->needs_release) { + gf_mo_release_data(txh->stream, 0xFFFFFFFF, -1); + txh->needs_release = 0; + } + gf_sc_invalidate(txh->compositor, NULL); + gf_mo_stop(txh->stream); + gf_sg_vrml_mf_reset(&txh->current_url, GF_SG_VRML_MFURL); + txh->is_open = 0; + + /*and deassociate object*/ + gf_mo_unregister(txh->owner, txh->stream); + txh->stream = NULL; +} + +GF_EXPORT +void gf_sc_texture_restart(GF_TextureHandler *txh) +{ + if (!txh->is_open) return; + gf_sc_texture_release_stream(txh); + txh->stream_finished = 0; + gf_mo_restart(txh->stream); +} + +GF_EXPORT +void gf_sc_texture_update_frame(GF_TextureHandler *txh, Bool disable_resync) +{ + u32 size, ts; + + /*already refreshed*/ + if (txh->needs_refresh) return; + + if (!txh->stream) { + txh->data = NULL; + return; + } + + /*should never happen!!*/ + if (txh->needs_release) gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0); + + /*check init flag*/ + if (!(gf_mo_get_flags(txh->stream) & GF_MO_IS_INIT)) { + /*if we had a texture this means the object has changed - delete texture and force next frame + composition (this will take care of OD reuse)*/ + if (txh->tx_io) { + gf_sc_texture_release(txh); + txh->data = NULL; + txh->needs_refresh = 1; + gf_sc_invalidate(txh->compositor, NULL); + return; + } + } + txh->data = gf_mo_fetch_data(txh->stream, !disable_resync, &txh->stream_finished, &ts, &size); + + /*if no frame or muted don't draw*/ + if (!txh->data || !size) return; + + /*if setup and same frame return*/ + if (txh->tx_io && (txh->stream_finished || (txh->last_frame_time==ts)) ) { + gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0); + txh->needs_release = 0; + return; + } + txh->needs_release = 1; + txh->last_frame_time = ts; + if (gf_mo_is_muted(txh->stream)) return; + + if (!txh->tx_io) { + gf_sc_texture_allocate(txh); + if (!txh->tx_io) return; + + gf_mo_get_visual_info(txh->stream, &txh->width, &txh->height, &txh->stride, &txh->pixel_ar, &txh->pixelformat); + + txh->transparent = 0; + switch (txh->pixelformat) { + case GF_PIXEL_ALPHAGREY: + case GF_PIXEL_ARGB: + case GF_PIXEL_RGBA: + case GF_PIXEL_YUVA: + txh->transparent = 1; + break; + } + + gf_mo_set_flag(txh->stream, GF_MO_IS_INIT, 1); + } + + /*try to push texture on graphics but don't complain if failure*/ + gf_sc_texture_set_data(txh); + + txh->needs_refresh = 1; + gf_sc_invalidate(txh->compositor, NULL); +} + +GF_EXPORT +void gf_sc_texture_release_stream(GF_TextureHandler *txh) +{ + if (txh->needs_release) { + assert(txh->stream); + gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0); + txh->needs_release = 0; + } + txh->needs_refresh = 0; +} + + +GF_EXPORT +GF_TextureHandler *gf_sc_texture_get_handler(GF_Node *n) +{ + if (!n) return NULL; + switch (gf_node_get_tag(n)) { + case TAG_MPEG4_ImageTexture: case TAG_X3D_ImageTexture: return it_get_texture(n); + case TAG_MPEG4_MovieTexture: case TAG_X3D_MovieTexture: return mt_get_texture(n); + case TAG_MPEG4_PixelTexture: case TAG_X3D_PixelTexture: return pt_get_texture(n); + + case TAG_MPEG4_CompositeTexture2D: + case TAG_MPEG4_CompositeTexture3D: + return compositor_get_composite_texture(n); + case TAG_MPEG4_LinearGradient: + case TAG_MPEG4_RadialGradient: + return compositor_mpeg4_get_gradient_texture(n); + + case TAG_MPEG4_MatteTexture: + { + GF_TextureHandler *hdl = gf_sc_texture_get_handler( ((M_MatteTexture*)n)->surfaceB ); + if (hdl) hdl->matteTexture = n; + return hdl; + } + +#ifndef GPAC_DISABLE_SVG + case TAG_SVG_linearGradient: + case TAG_SVG_radialGradient: + return compositor_svg_get_gradient_texture(n); + case TAG_SVG_image: + case TAG_SVG_video: + return compositor_svg_get_image_texture(n); +#endif + + default: return NULL; + } +} diff --git a/src/compositor/texturing.h b/src/compositor/texturing.h new file mode 100644 index 0000000..90e29a9 --- /dev/null +++ b/src/compositor/texturing.h @@ -0,0 +1,93 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _TEXTURING_H_ +#define _TEXTURING_H_ + +#include + +/*allocates the HW specific texture handle(s) (potentially both raster and OpenGL handlers)*/ +GF_Err gf_sc_texture_allocate(GF_TextureHandler *txh); +/*releases the hardware handle(s) and all associated system resources*/ +void gf_sc_texture_release(GF_TextureHandler *txh); +/*signals new data is available in the texture interface*/ +GF_Err gf_sc_texture_set_data(GF_TextureHandler *hdl); +/*reset the hardware handle(s) only, but keep associated resources for later hardware bind*/ +void gf_sc_texture_reset(GF_TextureHandler *hdl); + +/*push data to hardware if needed, creating the hardware handle(s)*/ +Bool gf_sc_texture_push_image(GF_TextureHandler *txh, Bool generate_mipmaps, Bool for2d); + +/*gets texture transform matrix - returns 1 if not identity +@tx_transform: texture transform node from appearance*/ +Bool gf_sc_texture_get_transform(GF_TextureHandler *txh, GF_Node *tx_transform, GF_Matrix *mx); + +/*gets the associated raster2D stencil handler*/ +GF_STENCIL gf_sc_texture_get_stencil(GF_TextureHandler *hdl); +/*sets the associated raster2D stencil handler (used by gradients)*/ +void gf_sc_texture_set_stencil(GF_TextureHandler *hdl, GF_STENCIL stencil); + +Bool gf_sc_texture_is_transparent(GF_TextureHandler *txh); + +/*ALL THE FOLLOWING ARE ONLY AVAILABLE IN 3D AND DEAL WITH OPENGL TEXTURE MANAGEMENT*/ +#ifndef GPAC_DISABLE_3D + +/*enable the texture and pushes the given texture transform on the graphics card*/ +u32 gf_sc_texture_enable(GF_TextureHandler *txh, GF_Node *tx_transform); + +/*same as gf_sc_texture_enable, but provides object bounds for correct mapping of svg gradients in UserSpaceMode*/ +u32 gf_sc_texture_enable_ex(GF_TextureHandler *txh, GF_Node *tx_transform, GF_Rect *bounds); + +/*disables the texture (unbinds it)*/ +void gf_sc_texture_disable(GF_TextureHandler *txh); +/*retrieves the internal (potentially converted for YUV) data buffer*/ +char *gf_sc_texture_get_data(GF_TextureHandler *txh, u32 *pix_format); +/*checks if the data buffer shall be pushed to graphics card*/ +Bool gf_sc_texture_needs_reload(GF_TextureHandler *hdl); + +#ifndef GPAC_USE_TINYGL +/*copy current GL window to the texture - the viewport used is the texture one (0,0,W,H) */ +void gf_sc_copy_to_texture(GF_TextureHandler *txh); +#endif + + +/*copy current GL window to the associated data buffer - the viewport used is the texture one (0,0,W,H) */ +void gf_sc_copy_to_stencil(GF_TextureHandler *txh); + +/*set blending mode*/ +enum +{ + TX_DECAL = 0, + TX_MODULATE, + TX_REPLACE, + TX_BLEND, +}; +/*set texturing blend mode*/ +void gf_sc_texture_set_blend_mode(GF_TextureHandler *txh, u32 mode); + +#endif /*GPAC_DISABLE_3D*/ + + +#endif /*_TEXTURING_H_*/ + diff --git a/src/compositor/texturing_gl.c b/src/compositor/texturing_gl.c new file mode 100644 index 0000000..b4fd8f2 --- /dev/null +++ b/src/compositor/texturing_gl.c @@ -0,0 +1,1185 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "texturing.h" +#include "visual_manager.h" +#include "nodes_stacks.h" +#include "gl_inc.h" + +#ifdef GPAC_USE_TINYGL +# define GLTEXENV glTexEnvi +# define GLTEXPARAM glTexParameteri +# define TexEnvType u32 +#elif defined (GPAC_USE_OGL_ES) +# define GLTEXENV glTexEnvx +# define GLTEXPARAM glTexParameterx +# define TexEnvType Fixed +#else +# define GLTEXENV glTexEnvf +# define GLTEXPARAM glTexParameteri +# define TexEnvType Float +#endif + + + +/*tx flags*/ +enum +{ + /*signal video data must be sent to 2D raster*/ + TX_NEEDS_RASTER_LOAD = (1<<1), + /*signal video data must be sent to 3D hw*/ + TX_NEEDS_HW_LOAD = (1<<2), + /*OpenGL texturing flags*/ + + /*these 4 are exclusives*/ + TX_MUST_SCALE = (1<<3), + TX_IS_POW2 = (1<<4), + TX_IS_RECT = (1<<5), + TX_EMULE_POW2 = (1<<6), + TX_EMULE_FIRST_LOAD = (1<<7), +}; + + +struct __texture_wrapper +{ + u32 flags; + + /*2D texturing*/ + GF_STENCIL tx_raster; + + /*3D texturing*/ +#ifndef GPAC_DISABLE_3D + /*opengl texture id*/ + u32 id; + u32 blend_mode; + u32 rescale_width, rescale_height; + char *scale_data; + char *conv_data; + Fixed conv_wscale, conv_hscale; + u32 conv_format, conv_w, conv_h; + + /*gl textures vars (gl_type: 2D texture or rectangle (NV ext) )*/ + u32 nb_comp, gl_format, gl_type, gl_dtype; +#endif +#ifdef GPAC_TRISCOPE_MODE + char *depth_data; +#endif + +}; + +GF_Err gf_sc_texture_allocate(GF_TextureHandler *txh) +{ + if (txh->tx_io) return GF_OK; + GF_SAFEALLOC(txh->tx_io, struct __texture_wrapper); + if (!txh->tx_io) return GF_OUT_OF_MEM; + return GF_OK; +} + +void gf_sc_texture_release(GF_TextureHandler *txh) +{ + if (!txh->tx_io) return; + if (txh->tx_io->tx_raster) { + txh->compositor->rasterizer->stencil_delete(txh->tx_io->tx_raster); + txh->tx_io->tx_raster = NULL; + } + +#ifndef GPAC_DISABLE_3D + if (txh->tx_io->id) glDeleteTextures(1, &txh->tx_io->id); + if (txh->tx_io->scale_data) free(txh->tx_io->scale_data); + if (txh->tx_io->conv_data) free(txh->tx_io->conv_data); +#endif + +#ifdef GPAC_TRISCOPE_MODE + if (txh->tx_io->depth_data) free(txh->tx_io->depth_data); +#endif + free(txh->tx_io); + txh->tx_io = NULL; +} + + +GF_Err gf_sc_texture_set_data(GF_TextureHandler *txh) +{ + txh->tx_io->flags |= TX_NEEDS_RASTER_LOAD | TX_NEEDS_HW_LOAD; + return GF_OK; +} + +void gf_sc_texture_reset(GF_TextureHandler *txh) +{ +#ifndef GPAC_DISABLE_3D + if (txh->tx_io->id) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Texturing] Releasing OpenGL texture %d\n", txh->tx_io->id)); + glDeleteTextures(1, &txh->tx_io->id); + txh->tx_io->id = 0; + } + txh->tx_io->flags |= TX_NEEDS_HW_LOAD; +#endif +#ifdef GPAC_TRISCOPE_MODE + /* remove renoir texture from the display list */ + if (txh->RenoirObject) { + DestroyRenoirObject(txh->RenoirObject, txh->compositor->RenoirHandler); + if (txh->tx_io->depth_data) { + free(txh->tx_io->depth_data); + txh->tx_io->depth_data = NULL; + } + txh->RenoirObject = NULL; + } + +#endif + +} + +#ifndef GPAC_DISABLE_3D + +void gf_sc_texture_set_blend_mode(GF_TextureHandler *txh, u32 mode) +{ + if (txh->tx_io) txh->tx_io->blend_mode = mode; +} + + +void tx_bind_with_mode(GF_TextureHandler *txh, Bool transparent, u32 blend_mode) +{ + if (!txh->tx_io || !txh->tx_io->id || !txh->tx_io->gl_type) return; + glEnable(txh->tx_io->gl_type); + + switch (blend_mode) { + case TX_BLEND: + if (txh->transparent) glEnable(GL_BLEND); + GLTEXENV(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + break; + case TX_REPLACE: + if (txh->transparent) glEnable(GL_BLEND); + GLTEXENV(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + break; + case TX_MODULATE: + if (txh->transparent) glEnable(GL_BLEND); +#ifdef GPAC_USE_OGL_ES + GLTEXENV(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +#else + GLTEXENV(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +#endif + break; + case TX_DECAL: + default: + if ((txh->tx_io->gl_format==GL_LUMINANCE) || (txh->tx_io->gl_format==GL_LUMINANCE_ALPHA)) { + GLTEXENV(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND/*GL_MODULATE*/); + } else { + GLTEXENV(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + } + break; + } + glBindTexture(txh->tx_io->gl_type, txh->tx_io->id); +} + +void tx_bind(GF_TextureHandler *txh) +{ + if (txh->tx_io ) + tx_bind_with_mode(txh, txh->transparent, txh->tx_io->blend_mode); +} + +void gf_sc_texture_disable(GF_TextureHandler *txh) +{ + if (txh && txh->tx_io) { + glDisable(txh->tx_io->gl_type); + if (txh->transparent) glDisable(GL_BLEND); + } +} + + +Bool tx_can_use_rect_ext(GF_Compositor *compositor, GF_TextureHandler *txh) +{ + u32 i, count; + +// compositor->gl_caps.yuv_texture = 0; + if (!compositor->gl_caps.rect_texture) return 0; + if (!compositor->disable_rect_ext) return 1; + /*this happens ONLY with text texturing*/ + if (!txh->owner) return 0; + + count = gf_node_get_parent_count(txh->owner); + + /*background 2D can use RECT ext without pb*/ + if (gf_node_get_tag(txh->owner)==TAG_MPEG4_Background2D) return 1; + /*if a bitmap is using the texture force using RECT*/ + for (i=0; iowner, 0); + if (gf_node_get_tag(n)==TAG_MPEG4_Appearance) { + count = gf_node_get_parent_count(n); + for (i=0; igeometry && (gf_node_get_tag((GF_Node *)s)==TAG_MPEG4_Shape) && (gf_node_get_tag(s->geometry)==TAG_MPEG4_Bitmap)) return 1; + } + } + } + return 0; +} + + +static Bool tx_setup_format(GF_TextureHandler *txh) +{ + Bool is_pow2, use_rect; + GF_Compositor *compositor = (GF_Compositor *)txh->compositor; + + /*in case the ID has been lost, resetup*/ + if (!txh->tx_io->id) { + glGenTextures(1, &txh->tx_io->id); + txh->tx_io->flags |= TX_EMULE_FIRST_LOAD; + } + + /*first setup, this will force recompute bounds in case used with bitmap - we could refine and only + invalidate for bitmaps only*/ + if (txh->owner && (!txh->tx_io->rescale_width || !txh->tx_io->rescale_height)) + gf_node_dirty_set(txh->owner, 0, 1); + + txh->tx_io->rescale_width = gf_get_next_pow2(txh->width); + txh->tx_io->rescale_height = gf_get_next_pow2(txh->height); + + is_pow2 = ((txh->tx_io->rescale_width==txh->width) && (txh->tx_io->rescale_height==txh->height)) ? 1 : 0; + txh->tx_io->flags = TX_IS_POW2; + txh->tx_io->gl_type = GL_TEXTURE_2D; + use_rect = tx_can_use_rect_ext(compositor, txh); + if (!is_pow2 && use_rect) { +#ifndef GPAC_USE_TINYGL + txh->tx_io->gl_type = GL_TEXTURE_RECTANGLE_EXT; +#endif + txh->tx_io->flags = TX_IS_RECT; + } + if (!use_rect && !compositor->gl_caps.npot_texture && !is_pow2) txh->tx_io->flags = TX_MUST_SCALE; + + txh->tx_io->nb_comp = txh->tx_io->gl_format = 0; + txh->tx_io->gl_dtype = GL_UNSIGNED_BYTE; + switch (txh->pixelformat) { + case GF_PIXEL_GREYSCALE: + txh->tx_io->gl_format = GL_LUMINANCE; + txh->tx_io->nb_comp = 1; + txh->tx_io->gl_type = GL_TEXTURE_2D; + if (!is_pow2) txh->tx_io->flags = TX_MUST_SCALE; + break; + case GF_PIXEL_ALPHAGREY: + txh->tx_io->gl_format = GL_LUMINANCE_ALPHA; + txh->tx_io->nb_comp = 2; + txh->tx_io->gl_type = GL_TEXTURE_2D; + if (!is_pow2) txh->tx_io->flags = TX_MUST_SCALE; + break; + case GF_PIXEL_RGB_24: + txh->tx_io->gl_format = GL_RGB; + txh->tx_io->nb_comp = 3; + break; + case GF_PIXEL_RGB_32: + case GF_PIXEL_RGBA: + txh->tx_io->gl_format = GL_RGBA; + txh->tx_io->nb_comp = 4; + break; + case GF_PIXEL_ARGB: + if (!compositor->gl_caps.bgra_texture) return 0; + txh->tx_io->gl_format = GL_BGRA_EXT; + txh->tx_io->nb_comp = 4; + break; + case GF_PIXEL_YV12: + if (!compositor->disable_yuvgl && compositor->gl_caps.yuv_texture && !(txh->tx_io->flags & TX_MUST_SCALE) ) { + txh->tx_io->gl_format = compositor->gl_caps.yuv_texture; + txh->tx_io->nb_comp = 3; + txh->tx_io->gl_dtype = UNSIGNED_SHORT_8_8_MESA; + } else { + if (!use_rect && compositor->emul_pow2) txh->tx_io->flags = TX_EMULE_POW2; + txh->tx_io->gl_format = GL_RGB; + txh->tx_io->nb_comp = 3; + } + break; + default: + return 0; + } + /*note we don't free the data if existing, since this only happen when re-setting up after context loss (same size)*/ + if ((txh->tx_io->flags == TX_MUST_SCALE) & !txh->tx_io->scale_data) { + txh->tx_io->scale_data = (char*)malloc(sizeof(char) * txh->tx_io->nb_comp*txh->tx_io->rescale_width*txh->tx_io->rescale_height); + memset(txh->tx_io->scale_data , 0, sizeof(char) * txh->tx_io->nb_comp*txh->tx_io->rescale_width*txh->tx_io->rescale_height); + } + + glEnable(txh->tx_io->gl_type); + glBindTexture(txh->tx_io->gl_type, txh->tx_io->id); + +#ifdef GPAC_USE_OGL_ES + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_WRAP_S, (txh->flags & GF_SR_TEXTURE_REPEAT_S) ? GL_REPEAT : GL_CLAMP_TO_EDGE); + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_WRAP_T, (txh->flags & GF_SR_TEXTURE_REPEAT_T) ? GL_REPEAT : GL_CLAMP_TO_EDGE); + if (txh->tx_io->gl_type == GL_TEXTURE_2D) { + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_MAG_FILTER, txh->compositor->high_speed ? GL_NEAREST : GL_LINEAR); + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_MIN_FILTER, txh->compositor->high_speed ? GL_NEAREST : GL_LINEAR); + } else { + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } +#else + +#ifndef GPAC_USE_TINYGL + if (txh->tx_io->gl_type == GL_TEXTURE_2D) { + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_WRAP_S, (txh->flags & GF_SR_TEXTURE_REPEAT_S) ? GL_REPEAT : GL_CLAMP); + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_WRAP_T, (txh->flags & GF_SR_TEXTURE_REPEAT_T) ? GL_REPEAT : GL_CLAMP); + } else +#endif + { + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_WRAP_S, GL_REPEAT); + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + if (txh->tx_io->gl_type == GL_TEXTURE_2D) { + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_MAG_FILTER, txh->compositor->high_speed ? GL_NEAREST : GL_LINEAR); + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_MIN_FILTER, txh->compositor->high_speed ? GL_NEAREST : GL_LINEAR); + } else { + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLTEXPARAM(txh->tx_io->gl_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } +#endif + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glDisable(txh->tx_io->gl_type); + return 1; +} + +char *gf_sc_texture_get_data(GF_TextureHandler *txh, u32 *pix_format) +{ + char *data = txh->tx_io->conv_data; + *pix_format = txh->tx_io->conv_format; + if (*pix_format == txh->pixelformat) return txh->data; + return data; +} + +void txh_unpack_yuv(GF_TextureHandler *txh) +{ + u32 i, j; + u8 *dst, *y, *u, *v, *p_y, *p_u, *p_v; + if (!txh->tx_io->conv_data) { + txh->tx_io->conv_data = (char*)malloc(sizeof(char) * 2 * txh->width * txh->height); + } + p_y = txh->data; + p_u = txh->data + txh->stride*txh->height; + p_v = txh->data + 5*txh->stride*txh->height/4; + + /*convert to UYVY and flip texture*/ + for (i=0; iheight; i++) { + u32 idx = txh->height-i-1; + y = p_y + idx*txh->stride; + u = p_u + (idx/2) * txh->stride/2; + v = p_v + (idx/2) * txh->stride/2; + dst = txh->tx_io->conv_data + 2*i*txh->stride; + + for (j=0; jwidth/2;j++) { + *dst = *u; + dst++; + u++; + *dst = *y; + dst++; + y++; + *dst = *v; + dst++; + v++; + *dst = *y; + dst++; + y++; + } + } + /*remove GL texture flip*/ + txh->flags |= GF_SR_TEXTURE_NO_GL_FLIP; +} + +/*note about conversion: we consider that a texture without a stream attached is generated by the compositor +hence is never flipped. Otherwise all textures attached to stream are flipped in order to match uv coords*/ +Bool tx_convert(GF_TextureHandler *txh) +{ + GF_VideoSurface src, dst; + u32 out_stride; + GF_Compositor *compositor = (GF_Compositor *)txh->compositor; + + switch (txh->pixelformat) { + case GF_PIXEL_ARGB: + if (!compositor->gl_caps.bgra_texture) return 0; + case GF_PIXEL_GREYSCALE: + case GF_PIXEL_ALPHAGREY: + case GF_PIXEL_RGB_24: + case GF_PIXEL_RGB_32: + case GF_PIXEL_RGBA: + txh->tx_io->conv_format = txh->pixelformat; + txh->tx_io->flags |= TX_NEEDS_HW_LOAD; + return 1; + case GF_PIXEL_YV12: + if (txh->tx_io->gl_format == compositor->gl_caps.yuv_texture) { + txh->tx_io->conv_format = GF_PIXEL_YVYU; + txh->tx_io->flags |= TX_NEEDS_HW_LOAD; + txh_unpack_yuv(txh); + return 1; + } + break; + default: + txh->tx_io->conv_format = 0; + return 0; + } + if (!txh->tx_io->conv_data) { + if (txh->tx_io->flags == TX_EMULE_POW2) { + /*convert video to a po of 2 WITHOUT SCALING VIDEO*/ + txh->tx_io->conv_w = gf_get_next_pow2(txh->width); + txh->tx_io->conv_h = gf_get_next_pow2(txh->height); + txh->tx_io->conv_data = (char*)malloc(sizeof(char) * 3 * txh->tx_io->conv_w * txh->tx_io->conv_h); + memset(txh->tx_io->conv_data , 0, sizeof(char) * 3 * txh->tx_io->conv_w * txh->tx_io->conv_h); + txh->tx_io->conv_wscale = INT2FIX(txh->width) / txh->tx_io->conv_w; + txh->tx_io->conv_hscale = INT2FIX(txh->height) / txh->tx_io->conv_h; + } else { + txh->tx_io->conv_data = (char*)malloc(sizeof(char) * 3 * txh->width * txh->height); + } + } + out_stride = 3 * ((txh->tx_io->flags & TX_EMULE_POW2) ? txh->tx_io->conv_w : txh->width); + + dst.width = src.width = txh->width; + dst.height = src.height = txh->height; + dst.is_hardware_memory = src.is_hardware_memory = 0; + + src.pitch = txh->stride; + src.pixel_format = txh->pixelformat; + src.video_buffer = txh->data; + + dst.pitch = out_stride; + txh->tx_io->conv_format = dst.pixel_format = GF_PIXEL_RGB_24; + dst.video_buffer = txh->tx_io->conv_data; + + /*stretch and flip*/ + gf_stretch_bits(&dst, &src, NULL, NULL, 0, 0xFF, 1, NULL, NULL); + txh->tx_io->flags |= TX_NEEDS_HW_LOAD; + txh->flags |= GF_SR_TEXTURE_NO_GL_FLIP; + return 1; +} + +#endif + + +Bool gf_sc_texture_push_image(GF_TextureHandler *txh, Bool generate_mipmaps, Bool for2d) +{ +#ifndef GPAC_DISABLE_3D + char *data; + Bool first_load = 0; + GLint tx_mode; + u32 pixel_format, w, h; +#endif + + if (for2d) { + Bool load_tx = 0; + if (!txh->tx_io->tx_raster) { + txh->tx_io->tx_raster = txh->compositor->rasterizer->stencil_new(txh->compositor->rasterizer, GF_STENCIL_TEXTURE); + if (!txh->tx_io->tx_raster) return 0; + load_tx = 1; + } + if (txh->tx_io->flags & TX_NEEDS_RASTER_LOAD) { + load_tx = 1; + txh->tx_io->flags &= ~TX_NEEDS_RASTER_LOAD; + } + if (load_tx) { + if (txh->compositor->rasterizer->stencil_set_texture(txh->tx_io->tx_raster, txh->data, txh->width, txh->height, txh->stride, (GF_PixelFormat) txh->pixelformat, (GF_PixelFormat) txh->compositor->video_out->pixel_format, 0) != GF_OK) + return 0; + } + return 1; + } + +#ifndef GPAC_DISABLE_3D + + if (! (txh->tx_io->flags & TX_NEEDS_HW_LOAD) ) return 1; + + /*in case the ID has been lost, resetup*/ + if (!txh->tx_io->id) { + glGenTextures(1, &txh->tx_io->id); + tx_setup_format(txh); + first_load = 1; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Texturing] Allocating OpenGL texture %d\n", txh->tx_io->id)); + } + if (!txh->tx_io->gl_type) return 0; + + if (txh->tx_io->flags & TX_EMULE_FIRST_LOAD) { + txh->tx_io->flags &= ~TX_EMULE_FIRST_LOAD; + first_load = 1; + } + + /*convert image*/ + tx_convert(txh); + + tx_bind(txh); + + txh->tx_io->flags &= ~TX_NEEDS_HW_LOAD; + data = gf_sc_texture_get_data(txh, &pixel_format); + if (!data) return 0; + if (txh->tx_io->flags & TX_EMULE_POW2) { + w = txh->tx_io->conv_w; + h = txh->tx_io->conv_h; + } else { + w = txh->width; + h = txh->height; + } +#ifdef GPAC_USE_OGL_ES + tx_mode = txh->tx_io->gl_format; +#else + tx_mode = txh->tx_io->nb_comp; + if (txh->tx_io->conv_format==GF_PIXEL_YVYU) { + tx_mode = txh->tx_io->gl_format; + } +#endif + +#ifdef GPAC_USE_TINYGL + glTexImage2D(txh->tx_io->gl_type, 0, tx_mode, w, h, 0, txh->tx_io->gl_format, txh->tx_io->gl_dtype, (unsigned char *) data); + +#else + + /*pow2 texture or hardware support*/ + if (! (txh->tx_io->flags & TX_MUST_SCALE) ) { + if (first_load) { + glTexImage2D(txh->tx_io->gl_type, 0, tx_mode, w, h, 0, txh->tx_io->gl_format, txh->tx_io->gl_dtype, (unsigned char *) data); + + } else { + glTexSubImage2D(txh->tx_io->gl_type, 0, 0, 0, w, h, txh->tx_io->gl_format, txh->tx_io->gl_dtype, (unsigned char *) data); + } + } else { + +#ifdef GPAC_HAS_GLU + if (!txh->compositor->disable_glu_scale) { + gluScaleImage(txh->tx_io->gl_format, txh->width, txh->height, txh->tx_io->gl_dtype, data, txh->tx_io->rescale_width, txh->tx_io->rescale_height, txh->tx_io->gl_dtype, txh->tx_io->scale_data); + } else +#endif + { + /*it appears gluScaleImage is quite slow - use ourt own resampler which is not as nice but a but faster*/ + GF_VideoSurface src, dst; + src.width = txh->width; + src.height = txh->height; + src.pitch = txh->stride; + src.pixel_format = txh->pixelformat; + src.video_buffer = txh->data; + + dst.width = txh->tx_io->rescale_width; + dst.height = txh->tx_io->rescale_height; + dst.pitch = txh->tx_io->rescale_width*txh->tx_io->nb_comp; + dst.pixel_format = txh->pixelformat; + dst.video_buffer = txh->tx_io->scale_data; + + gf_stretch_bits(&dst, &src, NULL, NULL, 0, 0xFF, 0, NULL, NULL); + } + + if (first_load) { + glTexImage2D(txh->tx_io->gl_type, 0, tx_mode, txh->tx_io->rescale_width, txh->tx_io->rescale_height, 0, txh->tx_io->gl_format, txh->tx_io->gl_dtype, txh->tx_io->scale_data); + } else { + glTexSubImage2D(txh->tx_io->gl_type, 0, 0, 0, txh->tx_io->rescale_width, txh->tx_io->rescale_height, txh->tx_io->gl_format, txh->tx_io->gl_dtype, txh->tx_io->scale_data); + } + } +#endif + return 1; + +#endif + return 0; +} + + +#ifndef GPAC_DISABLE_3D + +static Bool tx_set_image(GF_TextureHandler *txh, Bool generate_mipmaps) +{ + return gf_sc_texture_push_image(txh, generate_mipmaps, 0); +} + +#ifndef GPAC_USE_TINYGL +void gf_sc_copy_to_texture(GF_TextureHandler *txh) +{ + /*in case the ID has been lost, resetup*/ + if (!txh->tx_io->id) { + glGenTextures(1, &txh->tx_io->id); + tx_setup_format(txh); + } + + tx_bind(txh); + glCopyTexImage2D(txh->tx_io->gl_type, 0, txh->tx_io->gl_format, 0, 0, txh->width, txh->height, 0); + glDisable(txh->tx_io->gl_type); +} +#endif + +#ifndef GPAC_USE_TINYGL + +void gf_sc_copy_to_stencil(GF_TextureHandler *txh) +{ + u32 i, hy; + char *tmp=NULL; +#ifdef GPAC_TRISCOPE_MODE + const char *sOpt; + float OGL_depthGain; +#endif + + /*in case the ID has been lost, resetup*/ + if (!txh->data || !txh->tx_io->tx_raster) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[GL Texture] Copying GL backbuffer %dx%d@PF=%s to systems memory\n", txh->width, txh->height, gf_4cc_to_str(txh->pixelformat) )); + + if (txh->pixelformat==GF_PIXEL_RGBA) { + glReadPixels(0, 0, txh->width, txh->height, GL_RGBA, GL_UNSIGNED_BYTE, txh->data); + } else if (txh->pixelformat==GF_PIXEL_RGB_24) { + glReadPixels(0, 0, txh->width, txh->height, GL_RGB, GL_UNSIGNED_BYTE, txh->data); + +#ifdef GPAC_TRISCOPE_MODE + } else if (txh->pixelformat==GF_PIXEL_RGBDS) { + /*we'll work with one alpha bit (=shape). we'll take the heaviest weighted as this threshold*/ + glReadPixels(0, 0, txh->width, txh->height, GL_RGBA, GL_UNSIGNED_BYTE, txh->data); + + /*NOTES on OpenGL's z-buffer perspective inversion: + * option 1: extract float depth buffer, undoing depth perspective transform PIXEL per PIXEL and then + * convert to byte (computationally costly) + * + * option 2: use gain and offset to make up an approximation of the linear z-buffer (the original) + * it can be achieved by scaling the interval where the inflection point is located + * i.e. z' = G*z - (G - 1), the offset so that z still belongs to [0..1]* + */ + + glPixelTransferf(GL_DEPTH_SCALE, txh->compositor->OGLDepthGain); + glPixelTransferf(GL_DEPTH_BIAS, txh->compositor->OGLDepthOffset); + + /*obtain depthmap*/ /*NOTE: could txt width, height change once allocated?*/ + if (!txh->tx_io->depth_data) txh->tx_io->depth_data = (char*)malloc(sizeof(char)*txh->width*txh->height); + glReadPixels(0, 0, txh->width, txh->height, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, txh->tx_io->depth_data); + /* depth = alpha & 0xfe + shape = plan alpha & 0x01 */ + /*at the moment RENOIR WORKS WITH DSRGB - need to recombine components*/ + +#if 1 /*this corresponds to the RGBDS ordering*/ + for (i=0; iheight*txh->width; i++) { + u8 ds; + /* erase lowest-weighted depth bit */ + u8 depth = txh->tx_io->depth_data[i] & 0xfe; + /*get alpha*/ + ds = (txh->data[i*4 + 3]); + /* if heaviest-weighted alpha bit is set (>128) , turn on shape bit*/ + if (ds & 0x80) depth |= 0x01; + txh->data[i*4+3] = depth; /*insert depth onto alpha*/ + } + +#endif +#endif + } + /*flip image because of openGL*/ + tmp = (char*)malloc(sizeof(char)*txh->stride); + hy = txh->height/2; + for (i=0; idata + i*txh->stride, txh->stride); + memcpy(txh->data + i*txh->stride, txh->data + (txh->height - 1 - i) * txh->stride, txh->stride); + memcpy(txh->data + (txh->height - 1 - i) * txh->stride, tmp, txh->stride); + } + free(tmp); + +} +#else + +void gf_get_tinygl_depth(GF_TextureHandler *txh) { + + glReadPixels(0, 0, txh->width, txh->height, GL_RGBDS, GL_UNSIGNED_BYTE, txh->data); + +} +#endif + +#endif + +Bool gf_sc_texture_get_transform(GF_TextureHandler *txh, GF_Node *tx_transform, GF_Matrix *mx) +{ + GF_Matrix tmp; + Bool ret = 0; + gf_mx_init(*mx); + + /*flip image if requested*/ + if (! (txh->flags & GF_SR_TEXTURE_NO_GL_FLIP) ) { + /*flip it*/ + gf_mx_add_scale(mx, FIX_ONE, -FIX_ONE, FIX_ONE); + /*and translate it to handle repeatS/repeatT*/ + gf_mx_add_translation(mx, 0, -FIX_ONE, 0); + ret = 1; + } + + /*WATCHOUT: GL_TEXTURE_RECTANGLE coords are w, h not 1.0, 1.0*/ + if (txh->tx_io->flags & TX_IS_RECT) { + + gf_mx_add_scale(mx, INT2FIX(txh->width), INT2FIX(txh->height), FIX_ONE); + /*disable any texture transforms when using RECT textures (no repeat) ??*/ + /*tx_transform = NULL;*/ + ret = 1; + } + else if (txh->tx_io->flags & TX_EMULE_POW2) { +#ifndef GPAC_DISABLE_3D + gf_mx_add_scale(mx, txh->tx_io->conv_wscale, txh->tx_io->conv_hscale, FIX_ONE); + /*disable any texture transforms when emulating pow2 textures*/ + tx_transform = NULL; + ret = 1; +#endif + } + + if (tx_transform) { + switch (gf_node_get_tag(tx_transform)) { + case TAG_MPEG4_TextureTransform: + case TAG_X3D_TextureTransform: + { + GF_Matrix2D mat; + M_TextureTransform *tt = (M_TextureTransform *)tx_transform; + gf_mx2d_init(mat); + /*1- translate*/ + gf_mx2d_add_translation(&mat, tt->translation.x, tt->translation.y); + /*2- rotate*/ + if (fabs(tt->rotation) > FIX_EPSILON) gf_mx2d_add_rotation(&mat, tt->center.x, tt->center.y, tt->rotation); + /*3- centered-scale*/ + gf_mx2d_add_translation(&mat, -tt->center.x, -tt->center.y); + gf_mx2d_add_scale(&mat, tt->scale.x, tt->scale.y); + gf_mx2d_add_translation(&mat, tt->center.x, tt->center.y); + if (ret) { + gf_mx_from_mx2d(&tmp, &mat); + gf_mx_add_matrix(mx, &tmp); + } else { + gf_mx_from_mx2d(mx, &mat); + } + ret = 1; + } + break; + case TAG_MPEG4_TransformMatrix2D: + { + M_TransformMatrix2D *tm = (M_TransformMatrix2D *)tx_transform; + memset(tmp.m, 0, sizeof(Fixed)*16); + tmp.m[0] = tm->mxx; tmp.m[4] = tm->mxy; /*0*/ tmp.m[12] = tm->tx; + tmp.m[1] = tm->myx; tmp.m[5] = tm->myy; /*0*/ tmp.m[13] = tm->ty; + /*rest is all 0 excep both diag*/ + tmp.m[10] = tmp.m[15] = FIX_ONE; + + if (ret) { + gf_mx_add_matrix(mx, &tmp); + } else { + gf_mx_copy(*mx, tmp); + } + ret = 1; + } + break; + default: + break; + } + } + return ret; +} + +static Bool gf_sc_texture_enable_matte_texture(GF_Node *n) +{ +#ifdef GPAC_DISABLE_3D + return 0; +#else + GF_TextureHandler *b_surf; +#ifndef GPAC_USE_TINYGL + GF_TextureHandler *matte_hdl; + GF_TextureHandler *a_surf; + GF_TextureHandler *alpha_surf; + char * action ; + int tmp; + u8 texture[4]; + MFFloat coefficients ; + GF_Compositor *compositor; +#endif + M_MatteTexture *matte = (M_MatteTexture *)n; + + + b_surf = gf_sc_texture_get_handler(matte->surfaceB); + + if (!b_surf || !b_surf->tx_io) return 0; + glEnable(GL_BLEND); + tx_set_image(b_surf, 0); + +#ifdef GPAC_USE_TINYGL + tx_bind(b_surf); + return 1; +#else + + compositor = b_surf->compositor; + if (!compositor->gl_caps.glActiveTextureARB) { + tx_bind(b_surf); + return 1; + } + matte_hdl = gf_node_get_private(n); + if (!matte_hdl->tx_io) { + gf_sc_texture_allocate(matte_hdl); + } + a_surf = gf_sc_texture_get_handler(matte->surfaceA); + if (!a_surf->tx_io) a_surf = NULL; + alpha_surf = gf_sc_texture_get_handler(matte->alphaSurface); + if (!alpha_surf->tx_io) alpha_surf = NULL; + + action = (matte->operation).buffer; + glDisable(GL_TEXTURE_2D); + + /* SCALE */ + if (! strcmp(action,"SCALE") || !strcmp(action,"BIAS") ) { + TexEnvType operand; + coefficients = (matte->parameter); + if (coefficients.count < 3) { + tx_bind(b_surf); + return 1; + } + texture[0] = (u8) FIX2INT( 255 * coefficients.vals[0]); + texture[1] = (u8) FIX2INT( 255 * coefficients.vals[1]); + texture[2] = (u8) FIX2INT( 255 * coefficients.vals[2]); + texture[3] = 255; + if (coefficients.count >= 4) + texture[3] = (u8) FIX2INT( 255 * coefficients.vals[3]); + + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE0_ARB); + if (!matte_hdl->tx_io->id) { + glGenTextures(1, &matte_hdl->tx_io->id); + } + glBindTexture( GL_TEXTURE_2D, matte_hdl->tx_io->id); + glEnable( GL_DEPTH_TEST ); + glEnable( GL_TEXTURE_2D ); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + GLTEXPARAM( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR ); + GLTEXPARAM( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR ); + GLTEXPARAM( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_REPEAT ); + GLTEXPARAM( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_REPEAT ); + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,1,1,0,GL_RGBA,GL_UNSIGNED_BYTE,texture); + + operand = GL_MODULATE; + if (!strcmp(action,"BIAS")) operand = GL_ADD_SIGNED_ARB; + + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE1_ARB); + tx_bind(b_surf); + GLTEXENV(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, COMBINE_ARB); + GLTEXENV(GL_TEXTURE_ENV, COMBINE_RGB_ARB, operand); + GLTEXENV(GL_TEXTURE_ENV, SOURCE0_RGB_ARB , GL_TEXTURE1_ARB); + GLTEXENV(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV, SOURCE1_RGB_ARB, GL_TEXTURE0_ARB); + GLTEXENV(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV, COMBINE_ALPHA_ARB, GL_REPLACE ); + GLTEXENV(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_TEXTURE1_ARB ); + GLTEXENV(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + return 2; + } + /* end SCALE */ + + /* CROSS_FADE */ + if (! strcmp(action,"CROSS_FADE")) { + tmp = FIX2INT(255 * matte->fraction); + texture[0] = (unsigned char) tmp; + texture[1] = (unsigned char) tmp; + texture[2] = (unsigned char) tmp; + texture[3] = (unsigned char) 255; // donne l'alpha de l'image de sortie + + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE0_ARB); + if (!matte_hdl->tx_io->id) { + glGenTextures(1, &matte_hdl->tx_io->id); + } + glBindTexture( GL_TEXTURE_2D, matte_hdl->tx_io->id); + glEnable( GL_TEXTURE_2D ); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + GLTEXPARAM( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR ); + GLTEXPARAM( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR ); + GLTEXPARAM( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_REPEAT ); + GLTEXPARAM( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_REPEAT ); + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,1,1,0,GL_RGBA,GL_UNSIGNED_BYTE,texture); + + /* fin de la génération de la texture donnée par la fraction ! */ + + /* mélange effectif des textures ! } */ + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE1_ARB); + tx_bind(b_surf); + + if (a_surf) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE2_ARB); + tx_set_image(a_surf, 0); + tx_bind(a_surf); + GLTEXENV(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,COMBINE_ARB); + GLTEXENV(GL_TEXTURE_ENV,COMBINE_RGB_ARB,INTERPOLATE_ARB ); + GLTEXENV(GL_TEXTURE_ENV, SOURCE0_RGB_ARB , GL_TEXTURE1_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND0_RGB_ARB,GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV, SOURCE1_RGB_ARB , GL_TEXTURE2_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND1_RGB_ARB,GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV,SOURCE2_RGB_ARB, GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND2_RGB_ARB,GL_SRC_COLOR); + } + return 3; + } + /* end CROSS_FADE */ + + /*REVEAL */ + if (!strcmp(action,"REVEAL")) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE0_ARB); + tx_bind(b_surf); + + if (alpha_surf) { + tx_set_image(alpha_surf, 0); + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE1_ARB); + tx_bind(alpha_surf); + } + if (a_surf) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE2_ARB); + tx_set_image(a_surf, 0); + tx_bind(a_surf); + + GLTEXENV(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,COMBINE_ARB); + GLTEXENV(GL_TEXTURE_ENV,COMBINE_RGB_ARB,INTERPOLATE_ARB ); + GLTEXENV(GL_TEXTURE_ENV, SOURCE0_RGB_ARB , GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND0_RGB_ARB,GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV, SOURCE1_RGB_ARB , GL_TEXTURE2_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND1_RGB_ARB,GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV,SOURCE2_RGB_ARB, GL_TEXTURE1_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND2_RGB_ARB,GL_SRC_ALPHA); + + } + return 3; + } + /* end REVEAL */ + + /*INVERT */ + if (! strcmp(action,"INVERT")) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE0_ARB); + tx_bind(b_surf); + GLTEXENV(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, COMBINE_ARB); + GLTEXENV(GL_TEXTURE_ENV, COMBINE_RGB_ARB, GL_REPLACE ); + GLTEXENV(GL_TEXTURE_ENV, SOURCE0_RGB_ARB, GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_ONE_MINUS_SRC_COLOR); + + if (matte->parameter.count && matte->parameter.vals[0]) { + GLTEXENV(GL_TEXTURE_ENV, COMBINE_ALPHA_ARB, GL_REPLACE ); + GLTEXENV(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA); + } + return 1; + } + /* end INVERT */ + + /* opération REPLACE_ALPHA */ + if (!strcmp(action,"REPLACE_ALPHA")) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE0_ARB); + tx_bind(b_surf); + + if (alpha_surf) { + glEnable(GL_BLEND); + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE1_ARB); + tx_set_image(alpha_surf, 0); + tx_bind(alpha_surf); + GLTEXENV(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,COMBINE_ARB); + GLTEXENV(GL_TEXTURE_ENV,COMBINE_RGB_ARB,GL_REPLACE ); + GLTEXENV(GL_TEXTURE_ENV, SOURCE0_RGB_ARB , GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND0_RGB_ARB,GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV,COMBINE_ALPHA_ARB,GL_REPLACE ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA_ARB,GL_SRC_ALPHA); + } + return 2; + } + /* end REPLACE_ALPHA */ + + /* MULTIPLY_ALPHA */ + if (!strcmp(action,"MULTIPLY_ALPHA")) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE0_ARB); + tx_bind(b_surf); + GLTEXENV(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + + if (alpha_surf) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE1_ARB); + tx_set_image(alpha_surf, 0); + tx_bind(alpha_surf); + GLTEXENV(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,COMBINE_ARB); + GLTEXENV(GL_TEXTURE_ENV,COMBINE_RGB_ARB,GL_REPLACE ); + GLTEXENV(GL_TEXTURE_ENV, SOURCE0_RGB_ARB , GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND0_RGB_ARB,GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV,COMBINE_ALPHA_ARB,GL_MODULATE ); + GLTEXENV(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB , GL_TEXTURE1_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND1_ALPHA_ARB,GL_SRC_ALPHA); + GLTEXENV(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB , GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA_ARB,GL_SRC_ALPHA); + } + return 2; + } + /* end MULTIPLY_ALPHA */ + + /*ADD */ + if (! strcmp(action,"ADD")) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE0_ARB); + tx_bind(b_surf); + if (a_surf) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE1_ARB); + tx_set_image(a_surf, 0); + tx_bind(a_surf); + GLTEXENV(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, COMBINE_ARB); + GLTEXENV(GL_TEXTURE_ENV, COMBINE_RGB_ARB, GL_ADD_SIGNED_ARB ); + GLTEXENV(GL_TEXTURE_ENV, SOURCE0_RGB_ARB, GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV, SOURCE1_RGB_ARB, GL_TEXTURE1_ARB ); + GLTEXENV(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV, COMBINE_ALPHA_ARB, GL_REPLACE ); + GLTEXENV(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + } + return 2; + } + /*end ADD*/ + + /* ADD_SIGNED*/ + if (! strcmp(action,"ADD_SIGNED")) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE0_ARB); + tx_bind(b_surf); + if (a_surf) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE1_ARB); + tx_set_image(a_surf, 0); + tx_bind(a_surf); + GLTEXENV(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,COMBINE_ARB); + GLTEXENV(GL_TEXTURE_ENV,COMBINE_RGB_ARB,GL_ADD ); + GLTEXENV(GL_TEXTURE_ENV, SOURCE0_RGB_ARB , GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND0_RGB_ARB,GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV,SOURCE1_RGB_ARB,GL_TEXTURE1_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND1_RGB_ARB,GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV,COMBINE_ALPHA_ARB,GL_REPLACE ); + GLTEXENV(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB , GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA_ARB,GL_SRC_ALPHA); + } + return 2; + } + /* end ADD_SIGNED*/ + + /*SUBSTRACT*/ + if (! strcmp(action,"SUBSTRACT")) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE0_ARB); + tx_bind(b_surf); + if (a_surf) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE1_ARB); + tx_set_image(a_surf, 0); + tx_bind(a_surf); + GLTEXENV(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,COMBINE_ARB); + GLTEXENV(GL_TEXTURE_ENV,COMBINE_RGB_ARB,GL_SUBTRACT_ARB); + GLTEXENV(GL_TEXTURE_ENV, SOURCE0_RGB_ARB , GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND0_RGB_ARB,GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV,SOURCE1_RGB_ARB,GL_TEXTURE1_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND1_RGB_ARB,GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV,COMBINE_ALPHA_ARB,GL_REPLACE ); + GLTEXENV(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB , GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA_ARB,GL_SRC_ALPHA); + } + return 2; + } + /* end SUBTRACT*/ + + /*BLEND*/ + if (! strcmp(action,"BLEND")) { + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE0_ARB); + tx_bind(b_surf); + if (a_surf) { + GLTEXENV(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + compositor->gl_caps.glActiveTextureARB(GL_TEXTURE1_ARB); + tx_set_image(a_surf, 0); + tx_bind(a_surf); + GLTEXENV(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); + GLTEXENV(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, COMBINE_ARB); + GLTEXENV(GL_TEXTURE_ENV, COMBINE_RGB_ARB, GL_MODULATE); + GLTEXENV(GL_TEXTURE_ENV, SOURCE0_RGB_ARB, GL_TEXTURE0_ARB ); + GLTEXENV(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + GLTEXENV(GL_TEXTURE_ENV, SOURCE1_RGB_ARB, GL_TEXTURE1_ARB ); + GLTEXENV(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + } + return 2; + } + /*end BLEND */ + + tx_bind(b_surf); + return 1; + +#endif /*GPAC_USE_TINYGL*/ + +#undef GLTEXPARAM +#endif +} + + +Bool gf_sc_texture_is_transparent(GF_TextureHandler *txh) +{ + M_MatteTexture *matte; + if (!txh->matteTexture) return txh->transparent; + matte = (M_MatteTexture *)txh->matteTexture; + if (!matte->operation.buffer) return txh->transparent; + if (matte->alphaSurface) return 1; + if (!strcmp(matte->operation.buffer, "COLOR_MATRIX")) return 1; + return txh->transparent; +} + +#ifndef GPAC_DISABLE_3D + +u32 gf_sc_texture_enable_ex(GF_TextureHandler *txh, GF_Node *tx_transform, GF_Rect *bounds) +{ + GF_Matrix mx; + GF_Compositor *compositor = (GF_Compositor *)txh->compositor; + + if (txh->matteTexture) { + u32 ret = gf_sc_texture_enable_matte_texture(txh->matteTexture); + if (!ret) return 0; + visual_3d_set_matrix_mode(compositor->visual, V3D_MATRIX_TEXTURE); + if (gf_sc_texture_get_transform(txh, tx_transform, &mx)) + visual_3d_matrix_load(compositor->visual, mx.m); + else + visual_3d_matrix_reset(compositor->visual); + visual_3d_set_matrix_mode(compositor->visual, V3D_MATRIX_MODELVIEW); + return ret; + } + if (!txh || !txh->tx_io) return 0; + + if (txh->compute_gradient_matrix && gf_sc_texture_needs_reload(txh) ) { + compositor_gradient_update(txh); + } + + tx_set_image(txh, 0); + + visual_3d_set_matrix_mode(compositor->visual, V3D_MATRIX_TEXTURE); + if (bounds && txh->compute_gradient_matrix) { + GF_Matrix2D mx2d; + txh->compute_gradient_matrix(txh, bounds, &mx2d, 1); + gf_mx_from_mx2d(&mx, &mx2d); + visual_3d_matrix_load(compositor->visual, mx.m); + } + else if (gf_sc_texture_get_transform(txh, tx_transform, &mx)) { + visual_3d_matrix_load(compositor->visual, mx.m); + } else { + visual_3d_matrix_reset(compositor->visual); + } + visual_3d_set_matrix_mode(compositor->visual, V3D_MATRIX_MODELVIEW); + + txh->flags |= GF_SR_TEXTURE_USED; + tx_bind(txh); + return 1; +} + +u32 gf_sc_texture_enable(GF_TextureHandler *txh, GF_Node *tx_transform) +{ + return gf_sc_texture_enable_ex(txh, tx_transform, NULL); +} + + +#endif + +Bool gf_sc_texture_needs_reload(GF_TextureHandler *txh) +{ + return (txh->tx_io->flags & TX_NEEDS_HW_LOAD) ? 1 : 0; +} + + +GF_STENCIL gf_sc_texture_get_stencil(GF_TextureHandler *txh) +{ + return txh->tx_io->tx_raster; +} + +void gf_sc_texture_set_stencil(GF_TextureHandler *txh, GF_STENCIL stencil) +{ + txh->tx_io->tx_raster = stencil; + txh->tx_io->flags |= TX_NEEDS_HW_LOAD; +} + diff --git a/src/compositor/visual_manager.c b/src/compositor/visual_manager.c new file mode 100644 index 0000000..d2b22d9 --- /dev/null +++ b/src/compositor/visual_manager.c @@ -0,0 +1,256 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "visual_manager.h" +#include +#ifndef GPAC_DISABLE_SVG +#include +#endif + +static Bool visual_draw_bitmap_stub(GF_VisualManager *visual, GF_TraverseState *tr_state, struct _drawable_context *ctx, GF_ColorKey *col_key) +{ + return 0; +} + +GF_VisualManager *visual_new(GF_Compositor *compositor) +{ + GF_VisualManager *tmp; + GF_SAFEALLOC(tmp, GF_VisualManager); + + tmp->center_coords = 1; + tmp->compositor = compositor; + ra_init(&tmp->to_redraw); + tmp->back_stack = gf_list_new(); + tmp->view_stack = gf_list_new(); + tmp->raster_brush = compositor->rasterizer->stencil_new(compositor->rasterizer, GF_STENCIL_SOLID); + + tmp->DrawBitmap = visual_draw_bitmap_stub; + +#ifndef GPAC_DISABLE_3D + tmp->navigation_stack = gf_list_new(); + tmp->fog_stack = gf_list_new(); + tmp->alpha_nodes_to_draw = gf_list_new(); +#endif + + return tmp; +} + +void visual_del(GF_VisualManager *visual) +{ + ra_del(&visual->to_redraw); + + if (visual->raster_surface) visual->compositor->rasterizer->surface_delete(visual->raster_surface); + visual->raster_surface = NULL; + if (visual->raster_brush) visual->compositor->rasterizer->stencil_delete(visual->raster_brush); + visual->raster_brush = NULL; + + while (visual->context) { + DrawableContext *ctx = visual->context; + visual->context = ctx->next; + DeleteDrawableContext(ctx); + } + while (visual->prev_nodes) { + struct _drawable_store *cur = visual->prev_nodes; + visual->prev_nodes = cur->next; + free(cur); + } + + if (visual->back_stack) gf_list_del(visual->back_stack); + if (visual->view_stack) gf_list_del(visual->view_stack); + +#ifndef GPAC_DISABLE_3D + if (visual->navigation_stack) gf_list_del(visual->navigation_stack); + if (visual->fog_stack) gf_list_del(visual->fog_stack); + gf_list_del(visual->alpha_nodes_to_draw); +#endif + free(visual); +} + +Bool visual_get_size_info(GF_TraverseState *tr_state, Fixed *surf_width, Fixed *surf_height) +{ + Fixed w, h; +// w = tr_state->visual->width; +// h = tr_state->visual->height; + w = tr_state->vp_size.x; + h = tr_state->vp_size.y; + /*no size info, use main compositor output size*/ + if (!w || !h) { + w = INT2FIX(tr_state->visual->compositor->vp_width); + h = INT2FIX(tr_state->visual->compositor->vp_height); + } + if (tr_state->pixel_metrics) { + *surf_width = w; + *surf_height = h; + return 1; + } + if (tr_state->min_hsize) { + *surf_width = gf_divfix(w, tr_state->min_hsize); + *surf_height = gf_divfix(h, tr_state->min_hsize); + return 0; + } + if (h > w) { + *surf_width = 2*FIX_ONE; + *surf_height = gf_divfix(2*h, w); + } else { + *surf_width = gf_divfix(2*w, h); + *surf_height = 2*FIX_ONE; + } + return 0; +} + +void visual_clean_contexts(GF_VisualManager *visual) +{ + u32 i, count; + Bool is_root_visual = (visual->compositor->visual==visual) ? 1 : 0; + DrawableContext *ctx = visual->context; + while (ctx && ctx->drawable) { + /*remove visual registration flag*/ + ctx->drawable->flags &= ~DRAWABLE_REGISTERED_WITH_VISUAL; + if (is_root_visual && (ctx->flags & CTX_HAS_APPEARANCE)) + gf_node_dirty_reset(ctx->appear); + ctx = ctx->next; + } + + /*composite visual, cannot reset flags until root is done*/ + if (!is_root_visual) return; + + /*reset all flags of all appearance nodes registered on all visuals but main one (done above) + this must be done once all visuals have been drawn, otherwise we won't detect the changes + for nodes drawn on several visuals*/ + count = gf_list_count(visual->compositor->visuals); + for (i=1; icompositor->visuals, i); + ctx = a_vis->context; + while (ctx && ctx->drawable) { + if (ctx->flags & CTX_HAS_APPEARANCE) + gf_node_dirty_reset(ctx->appear); + ctx = ctx->next; + } + } +} + +Bool visual_draw_frame(GF_VisualManager *visual, GF_Node *root, GF_TraverseState *tr_state, Bool is_root_visual) +{ +#ifndef GPAC_DISABLE_3D + if (visual->type_3d) { + GF_Err e = visual_3d_draw_frame(visual, root, tr_state, is_root_visual); + visual_clean_contexts(visual); + return e; + } +#endif + return visual_2d_draw_frame(visual, root, tr_state, is_root_visual); +} + +void gf_sc_get_nodes_bounds(GF_Node *self, GF_ChildNodeItem *children, GF_TraverseState *tr_state, s32 *child_idx) +{ + u32 i; + SFVec2f size; + GF_Rect rc; + GF_Matrix2D cur_mx; + + if (tr_state->abort_bounds_traverse) { + if (self == tr_state->for_node) { + gf_mx2d_pre_multiply(&tr_state->mx_at_node, &tr_state->transform); + } + tr_state->abort_bounds_traverse=0; + gf_sc_get_nodes_bounds(self, children, tr_state, child_idx); + tr_state->abort_bounds_traverse=1; + return; + } + if (!children) return; + + size.x = size.y = -FIX_ONE; + switch (gf_node_get_tag(self)) { + case TAG_MPEG4_Layer2D: size = ((M_Layer2D *)self)->size; break; + case TAG_MPEG4_Layer3D: size = ((M_Layer3D *)self)->size; break; + case TAG_MPEG4_Form: size = ((M_Form *)self)->size; break; + } + if ((size.x>=0) && (size.y>=0)) { + tr_state->bounds = gf_rect_center(size.x, size.y); + return; + } + + gf_mx2d_copy(cur_mx, tr_state->transform); + rc = gf_rect_center(0,0); + + i = 0; + while (children) { + if (child_idx && (i != (u32) *child_idx)) { + children = children->next; + continue; + } + gf_mx2d_init(tr_state->transform); + tr_state->bounds = gf_rect_center(0,0); + + /*we hit the target node*/ + if (children->node == tr_state->for_node) + tr_state->abort_bounds_traverse = 1; + + gf_node_traverse(children->node, tr_state); + + if (tr_state->abort_bounds_traverse) { + gf_mx2d_add_matrix(&tr_state->mx_at_node, &cur_mx); + return; + } + + gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); + gf_rect_union(&rc, &tr_state->bounds); + children = children->next; + if (child_idx) + break; + } + +#ifndef GPAC_DISABLE_SVG + if (gf_node_get_tag(self)==TAG_SVG_use) { + GF_FieldInfo info; + if (gf_node_get_attribute_by_tag(self, TAG_XLINK_ATT_href, 0, 0, &info)==GF_OK) { + GF_Node *iri = ((XMLRI*)info.far_ptr)->target; + if (iri) { + gf_mx2d_init(tr_state->transform); + tr_state->bounds = gf_rect_center(0,0); + + /*we hit the target node*/ + if (iri == tr_state->for_node) + tr_state->abort_bounds_traverse = 1; + + gf_node_traverse(iri, tr_state); + + if (tr_state->abort_bounds_traverse) { + gf_mx2d_pre_multiply(&tr_state->mx_at_node, &cur_mx); + return; + } + + gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); + gf_rect_union(&rc, &tr_state->bounds); + } + } + } +#endif + + gf_mx2d_copy(tr_state->transform, cur_mx); + if (self != tr_state->for_node) { + gf_mx2d_apply_rect(&tr_state->transform, &rc); + } + tr_state->bounds = rc; +} diff --git a/src/compositor/visual_manager.h b/src/compositor/visual_manager.h new file mode 100644 index 0000000..5c90e1e --- /dev/null +++ b/src/compositor/visual_manager.h @@ -0,0 +1,156 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _VISUAL_MANAGER_H_ +#define _VISUAL_MANAGER_H_ + +#include "drawable.h" + +/*all 2D related functions and macro are locate there*/ +#include "visual_manager_2d.h" + +/*all 3D related functions and macro are locate there*/ +#include "visual_manager_3d.h" + +struct _visual_manager +{ + GF_Compositor *compositor; + +#ifndef GPAC_DISABLE_3D + /*3D type for the visual: + 0: visual is 2D + 1: visual is 2D with 3D acceleration (2D camera) + 2: visual is 3D MPEG-4 (with 3D camera) + 3: visual is 3D X3D (with 3D camera) + */ + u32 type_3d; +#endif + + + /*background stack*/ + GF_List *back_stack; + /*viewport stack*/ + GF_List *view_stack; + + /*size in pixels*/ + u32 width, height; + + + /* + * Visual Manager part for 2D drawing and dirty rect + */ + + /*the one and only dirty rect collector for this visual manager*/ + GF_RectArray to_redraw; +#ifdef TRACK_OPAQUE_REGIONS + u32 draw_node_index; +#endif + + /*display list (list of drawable context). The first context with no drawable attached to + it (ctx->drawable==NULL) marks the end of the display list*/ + DrawableContext *context, *cur_context; + + /*keeps track of nodes drawn last frame*/ + struct _drawable_store *prev_nodes, *last_prev_entry; + + /*pixel area in BIFS coords - eg area to fill with background*/ + GF_IRect surf_rect; + /*top clipper (may be different than surf_rect when a viewport is active)*/ + GF_IRect top_clipper; + + Bool last_had_back; + + /*signals that the hardware surface is attached to buffer/device/stencil*/ + Bool is_attached; + Bool center_coords; + Bool has_modif; + Bool has_overlays; + + /*gets access to graphics handle (either OS-specific or raw memory)*/ + GF_Err (*GetSurfaceAccess)(GF_VisualManager *); + /*release graphics handle*/ + void (*ReleaseSurfaceAccess)(GF_VisualManager *); + + /*draws specified texture as flat bitmap*/ + Bool (*DrawBitmap)(GF_VisualManager *visual, GF_TraverseState *tr_state, DrawableContext *ctx, GF_ColorKey *col_key); + + /*raster surface interface*/ + GF_SURFACE raster_surface; + /*raster brush interface*/ + GF_STENCIL raster_brush; + + /*node owning this visual manager (composite textures) - NULL for root visual*/ + GF_Node *offscreen; + + /*value of the flag to use to signal any geometry changes*/ + u32 bounds_tracker_modif_flag; + + u32 num_nodes_prev_frame, num_nodes_current_frame; + + /*list of video overlays sorted from first to last*/ + struct _video_overlay *overlays; + +#ifndef GPAC_DISABLE_3D + /* + * Visual Manager part for 3D drawing + */ + + /*navigation stack*/ + GF_List *navigation_stack; + /*fog stack*/ + GF_List *fog_stack; + + /*the one and only camera associated with the visual*/ + GF_Camera camera; + + /*list of transparent nodes to draw after TRAVERSE_SORT pass*/ + GF_List *alpha_nodes_to_draw; + + /*lighting stuff*/ + u32 num_lights; + u32 max_lights; + /*cliping stuff*/ + u32 num_clips; + u32 max_clips; +#endif + +}; + +/*constructor/destructor*/ +GF_VisualManager *visual_new(GF_Compositor *compositor); +void visual_del(GF_VisualManager *visual); + +/*draw cycle for the visual*/ +Bool visual_draw_frame(GF_VisualManager *visual, GF_Node *root, GF_TraverseState *tr_state, Bool is_root_visual); + +/*executes scene event (picks node if needed) - returns FALSE if no scene event handler has been called*/ +Bool visual_execute_event(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children); + +Bool visual_get_size_info(GF_TraverseState *tr_state, Fixed *surf_width, Fixed *surf_height); + +/*reset all appearance dirty state and visual registration info*/ +void visual_clean_contexts(GF_VisualManager *visual); + +#endif /*_VISUAL_MANAGER_H_*/ + diff --git a/src/compositor/visual_manager_2d.c b/src/compositor/visual_manager_2d.c new file mode 100644 index 0000000..daa2876 --- /dev/null +++ b/src/compositor/visual_manager_2d.c @@ -0,0 +1,778 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "visual_manager.h" +#include "nodes_stacks.h" + +//#define SKIP_CONTEXT + +Bool gf_irect_overlaps(GF_IRect *rc1, GF_IRect *rc2) +{ + if (! rc2->height || !rc2->width || !rc1->height || !rc1->width) return 0; + if (rc2->x+rc2->width<=rc1->x) return 0; + if (rc2->x>=rc1->x+rc1->width) return 0; + if (rc2->y-rc2->height>=rc1->y) return 0; + if (rc2->y<=rc1->y-rc1->height) return 0; + return 1; +} + +/*intersects @rc1 with @rc2 - the new @rc1 is the intersection*/ +void gf_irect_intersect(GF_IRect *rc1, GF_IRect *rc2) +{ + if (! gf_irect_overlaps(rc1, rc2)) { + rc1->width = rc1->height = 0; + return; + } + if (rc2->x > rc1->x) { + rc1->width -= rc2->x - rc1->x; + rc1->x = rc2->x; + } + if (rc2->x + rc2->width < rc1->x + rc1->width) { + rc1->width = rc2->width + rc2->x - rc1->x; + } + if (rc2->y < rc1->y) { + rc1->height -= rc1->y - rc2->y; + rc1->y = rc2->y; + } + if (rc2->y - rc2->height > rc1->y - rc1->height) { + rc1->height = rc1->y - rc2->y + rc2->height; + } +} + + +GF_Rect gf_rect_ft(GF_IRect *rc) +{ + GF_Rect rcft; + rcft.x = INT2FIX(rc->x); rcft.y = INT2FIX(rc->y); rcft.width = INT2FIX(rc->width); rcft.height = INT2FIX(rc->height); + return rcft; +} + +DrawableContext *visual_2d_get_drawable_context(GF_VisualManager *visual) +{ +#ifdef SKIP_CONTEXT + return NULL; +#endif + + if (!visual->context) { + visual->context = NewDrawableContext(); + visual->cur_context = visual->context; + drawctx_reset(visual->context); + visual->num_nodes_current_frame ++; + return visual->context; + } +// assert(visual->cur_context); + /*current context is OK*/ + if (!visual->cur_context->drawable) { + /*reset next context in display list for next call*/ + if (visual->cur_context->next) visual->cur_context->next->drawable = NULL; + drawctx_reset(visual->cur_context); + return visual->cur_context; + } + /*need a new context and next one is OK*/ + if (visual->cur_context->next) { + visual->cur_context = visual->cur_context->next; +// assert(visual->cur_context->drawable == NULL); + /*reset next context in display list for next call*/ + if (visual->cur_context->next) visual->cur_context->next->drawable = NULL; + drawctx_reset(visual->cur_context); + visual->num_nodes_current_frame ++; + return visual->cur_context; + } + /*need to create a new context*/ + visual->cur_context->next = NewDrawableContext(); + visual->cur_context = visual->cur_context->next; + drawctx_reset(visual->cur_context); + visual->num_nodes_current_frame ++; + + if (1) { + u32 i; + DrawableContext *last = visual->cur_context; + for (i=0; i<50; i++) { + last->next = malloc(sizeof(DrawableContext)); + last = last->next; + last->drawable = NULL; + last->col_mat = NULL; + } + last->next = NULL; + } + return visual->cur_context; +} + +void visual_2d_remove_last_context(GF_VisualManager *visual) +{ + assert(visual->cur_context); + visual->cur_context->drawable = NULL; +} + + +void visual_2d_drawable_delete(GF_VisualManager *visual, struct _drawable *node) +{ + /*remove drawable from visual list*/ + struct _drawable_store *it = visual->prev_nodes; + struct _drawable_store *prev = NULL; + while (it) { + if (it->drawable != node) { + prev = it; + it = prev->next; + continue; + } + if (prev) prev->next = it->next; + else visual->prev_nodes = it->next; + if (!it->next) visual->last_prev_entry = prev; + free(it); + break; + } + + /*check node isn't being tracked*/ + if (visual->compositor->grab_node==node->node) + visual->compositor->grab_node = NULL; + + if (visual->compositor->focus_node==node->node) { + visual->compositor->focus_node = NULL; + visual->compositor->focus_text_type = 0; + } +} + +Bool visual_2d_node_cull(GF_TraverseState *tr_state, GF_Rect *bounds) +{ + GF_Rect rc; + GF_IRect i_rc; + rc = *bounds; + gf_mx2d_apply_rect(&tr_state->transform, &rc); + i_rc = gf_rect_pixelize(&rc); + if (gf_irect_overlaps(&tr_state->visual->top_clipper, &i_rc)) return 1; + return 0; +} + +void visual_2d_setup_projection(GF_VisualManager *visual, GF_TraverseState *tr_state) +{ + GF_Rect rc; + + tr_state->visual = visual; + tr_state->backgrounds = visual->back_stack; + tr_state->viewpoints = visual->view_stack; + + /*setup clipper*/ + if (visual->center_coords) { + if (!visual->offscreen) { + if (visual->compositor->scalable_zoom) + rc = gf_rect_center(INT2FIX(visual->compositor->display_width), INT2FIX(visual->compositor->display_height)); + else + rc = gf_rect_center(INT2FIX(visual->compositor->output_width + 2*visual->compositor->vp_x), INT2FIX(visual->compositor->output_height + 2*visual->compositor->vp_y)); + } else { + rc = gf_rect_center(INT2FIX(visual->width), INT2FIX(visual->height)); + } + } else { + rc.x = 0; + rc.width = INT2FIX(visual->width); + rc.y = rc.height = INT2FIX(visual->height); + } + /*set top-transform to pixelMetrics*/ + if (!tr_state->pixel_metrics) gf_mx2d_add_scale(&tr_state->transform, tr_state->min_hsize, tr_state->min_hsize); + + visual->surf_rect = gf_rect_pixelize(&rc); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] output rectangle setup - width %d height %d\n", visual->surf_rect.width, visual->surf_rect.height)); + + /*setup top clipper*/ + if (visual->center_coords) { + rc = gf_rect_center(INT2FIX(visual->width), INT2FIX(visual->height)); + } else { + rc.width = INT2FIX(visual->width); + rc.height = INT2FIX(visual->height); + rc.x = 0; + rc.y = rc.height; + if (visual->compositor->visual==visual) { + rc.x += INT2FIX(visual->compositor->vp_x); + rc.y += INT2FIX(visual->compositor->vp_y); + } + } + + /*setup viewport*/ + if (gf_list_count(visual->view_stack)) { + tr_state->traversing_mode = TRAVERSE_BINDABLE; + tr_state->bounds = rc; + gf_node_traverse((GF_Node *) gf_list_get(visual->view_stack, 0), tr_state); + } + + visual->top_clipper = gf_rect_pixelize(&rc); + tr_state->clipper = rc; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Cliper setup - %d:%d@%dx%d\n", visual->top_clipper.x, visual->top_clipper.y, visual->top_clipper.width, visual->top_clipper.height)); +} + +GF_Err visual_2d_init_draw(GF_VisualManager *visual, GF_TraverseState *tr_state) +{ + GF_Err e; + u32 rem, count; + struct _drawable_store *it, *prev; + DrawableContext *ctx; + M_Background2D *bck; + u32 draw_mode; + u32 time; + + /*reset display list*/ + visual->cur_context = visual->context; + if (visual->context) visual->context->drawable = NULL; + visual->has_modif = 0; + visual->has_overlays = 0; + + visual_2d_setup_projection(visual, tr_state); + + tr_state->traversing_mode = TRAVERSE_SORT; + visual->num_nodes_current_frame = 0; + + /*setup raster surface, brush and pen */ + e = visual_2d_init_raster(visual); + if (e) + return e; + + draw_mode = 0; + if (tr_state->direct_draw) draw_mode = 1; + /*if we're requested to invalidate everything, switch to direct drawing but don't reset bounds*/ + else if (tr_state->invalidate_all) { + tr_state->direct_draw = 1; + draw_mode = 2; + } + tr_state->invalidate_all = 0; + + time = gf_sys_clock(); + /*reset prev nodes if any (previous traverse was indirect)*/ + rem = count = 0; + prev = NULL; + it = visual->prev_nodes; + while (it) { + /*node was not drawn on this visual*/ + if (!drawable_flush_bounds(it->drawable, visual, draw_mode)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Unregistering previously drawn node %s from visual\n", gf_node_get_class_name(it->drawable->node))); + + /*remove all bounds info related to this visual and unreg node */ + drawable_reset_bounds(it->drawable, visual); + + if (prev) prev->next = it->next; + else visual->prev_nodes = it->next; + if (!it->next) visual->last_prev_entry = prev; + rem++; + free(it); + it = prev ? prev->next : visual->prev_nodes; + } else { + /*mark drawable as already registered with visual*/ + it->drawable->flags |= DRAWABLE_REGISTERED_WITH_VISUAL; + prev = it; + it = it->next; + count++; + } + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Top visual initialized - %d nodes registered and %d removed - using %s rendering\n", count, rem, draw_mode ? "direct" : "dirty-rect")); + if (!draw_mode) return GF_OK; + + /*direct mode, draw background*/ + bck = (M_Background2D*) gf_list_get(visual->back_stack, 0); + if (bck && bck->isBound) { + ctx = b2d_get_context(bck, visual->back_stack); + if (ctx) { + /*force clearing entire zone, not just viewport, when using color. If texture, we MUST + use the VP clipper in order to compute offsets when blitting bitmaps*/ + if (ctx->aspect.fill_texture &&ctx->aspect.fill_texture->stream) { + ctx->bi->clip = visual->top_clipper; + } else { + ctx->bi->clip = visual->surf_rect; + } + ctx->bi->unclip = gf_rect_ft(&ctx->bi->clip); + tr_state->traversing_mode = TRAVERSE_BINDABLE; + gf_node_traverse((GF_Node *) bck, tr_state); + tr_state->traversing_mode = TRAVERSE_SORT; + } else { + visual_2d_clear(visual, NULL, 0); + } + } else { + visual_2d_clear(visual, NULL, 0); + } + return GF_OK; +} + + + +/*@rc2 fully contained in @rc1*/ +Bool gf_irect_inside(GF_IRect *rc1, GF_IRect *rc2) +{ + if (!rc1->width || !rc1->height) return 0; + if ( (rc1->x <= rc2->x) && (rc1->y >= rc2->y) && (rc1->x + rc1->width >= rc2->x + rc2->width) && (rc1->y - rc1->height <= rc2->y - rc2->height) ) + return 1; + return 0; +} + +#ifdef TRACK_OPAQUE_REGIONS + +#define CHECK_UNCHANGED 0 + +static void mark_opaque_areas(GF_VisualManager *visual) +{ + u32 i, k; +#if CHECK_UNCHANGED + Bool remove; +#endif + GF_RectArray *ra = &visual->to_redraw; + if (!ra->count) return; + ra->opaque_node_index = (u32*)realloc(ra->opaque_node_index, sizeof(u32) * ra->count); + + for (k=0; kcount; k++) { +#if CHECK_UNCHANGED + remove = 1; +#endif + ra->opaque_node_index[k] = 0; + + for (i=visual->num_contexts; i>0; i--) { + DrawableContext *ctx = visual->contexts[i-1]; + + if (!gf_irect_inside(&ctx->bi->clip, &ra->list[k]) ) { +#if CHECK_UNCHANGED + if (remove && gf_rect_overlaps(&ctx->bi->clip, ra->list[k])) { + if (ctx->need_redraw) remove = 0; + } +#endif + continue; + } + + /*which opaquely covers the given area */ + if (! (ctx->flags & CTX_IS_TRANSPARENT) ) { +#if CHECK_UNCHANGED + /*the opaque area has nothing changed above, remove the dirty rect*/ + if (remove && !ctx->need_redraw) { + u32 j = ra->count - k - 1; + if (j) { + memmove(&ra->list[j], &ra->list[j+1], sizeof(GF_IRect)*j); + memmove(&ra->opaque_node_index[j], &ra->opaque_node_index[j+1], sizeof(u32)*j); + } + ra->count--; + k--; + } + /*opaque area has something changed above, mark index */ + else +#endif + { + ra->opaque_node_index[k] = i; + } + break; +#if CHECK_UNCHANGED + } else { + if (ctx->need_redraw) remove = 0; +#endif + } + } + } +} +#endif + + + +/*clears list*/ +#define ra_clear(ra) { (ra)->count = 0; } + +/*is list empty*/ +#define ra_is_empty(ra) (!((ra)->count)) + +/*adds @rc2 to @rc1 - the new @rc1 contains the old @rc1 and @rc2*/ +void gf_irect_union(GF_IRect *rc1, GF_IRect *rc2) +{ + if (!rc1->width || !rc1->height) {*rc1=*rc2; return;} + + if (rc2->x < rc1->x) { + rc1->width += rc1->x - rc2->x; + rc1->x = rc2->x; + } + if (rc2->x + rc2->width > rc1->x+rc1->width) rc1->width = rc2->x + rc2->width - rc1->x; + if (rc2->y > rc1->y) { + rc1->height += rc2->y - rc1->y; + rc1->y = rc2->y; + } + if (rc2->y - rc2->height < rc1->y - rc1->height) rc1->height = rc1->y - rc2->y + rc2->height; +} + +/*adds rectangle to the list performing union test*/ +void ra_union_rect(GF_RectArray *ra, GF_IRect *rc) +{ + u32 i; + + for (i=0; icount; i++) { + if (gf_irect_overlaps(&ra->list[i], rc)) { + gf_irect_union(&ra->list[i], rc); + return; + } + } + ra_add(ra, rc); +} + +/*refreshes the content of the array to have only non-overlapping rects*/ +void ra_refresh(GF_RectArray *ra) +{ + u32 i, j, k; + for (i=0; icount; i++) { + for (j=i+1; jcount; j++) { + if (gf_irect_overlaps(&ra->list[i], &ra->list[j])) { + gf_irect_union(&ra->list[i], &ra->list[j]); + + /*remove rect*/ + k = ra->count - j - 1; + if (k) memmove(&ra->list[j], & ra->list[j+1], sizeof(GF_IRect)*k); + ra->count--; + + ra_refresh(ra); + return; + } + } + } +} + +/*this defines a partition of the display area in small squares of the given width. If the number of nodes +to redraw exceeds the possible number of squares on the visual, the entire area is redrawn - this avoids computing +dirty rects algo when a lot of small shapes are moving*/ +#define MIN_BBOX_SIZE 16 + +#if 0 +#define CHECK_MAX_NODE if (visual->to_redraw.count > max_nodes_allowed) redraw_all = 1; +#else +#define CHECK_MAX_NODE +#endif + + +Bool visual_2d_terminate_draw(GF_VisualManager *visual, GF_TraverseState *tr_state) +{ + u32 k, i, count, max_nodes_allowed, num_nodes, num_changed; + GF_IRect refreshRect, *check_rect; + Bool redraw_all; + M_Background2D *bck; + DrawableContext *bck_ctx, *ctx; + struct _drawable_store *it, *prev; +#ifndef TRACK_OPAQUE_REGIONS + DrawableContext *first_opaque = NULL; +#endif + Bool has_clear = 0; + Bool has_changed = 0; + + /*in direct mode the visual is always redrawn*/ + if (tr_state->direct_draw) { + /*flush pending contexts due to overlays*/ + visual_2d_flush_overlay_areas(visual, tr_state); + + visual_2d_release_raster(visual); + visual_clean_contexts(visual); + visual->num_nodes_prev_frame = visual->num_nodes_current_frame; + return 1; + } + + num_changed = 0; + + /*if the aspect ratio has changed redraw everything*/ + redraw_all = tr_state->invalidate_all; + + /*check for background changes for transparent nodes*/ + bck = NULL; + bck_ctx = NULL; + + bck = (M_Background2D*)gf_list_get(visual->back_stack, 0); + if (bck) { + if (!bck->isBound) { + if (visual->last_had_back) redraw_all = 1; + visual->last_had_back = 0; + } else { + if (!visual->last_had_back) redraw_all = 1; + visual->last_had_back = 1; + bck_ctx = b2d_get_context(bck, visual->back_stack); + if (bck_ctx->flags & CTX_REDRAW_MASK) redraw_all = 1; + } + } else if (visual->last_had_back) { + visual->last_had_back = 0; + redraw_all = 1; + } + + max_nodes_allowed = (u32) ((visual->top_clipper.width / MIN_BBOX_SIZE) * (visual->top_clipper.height / MIN_BBOX_SIZE)); + num_nodes = 0; + ctx = visual->context; + while (ctx && ctx->drawable) { + num_nodes++; + + drawctx_update_info(ctx, visual); + if (ctx->flags & CTX_REDRAW_MASK) { + num_changed ++; + +#ifndef TRACK_OPAQUE_REGIONS + if (!first_opaque && !(ctx->flags & CTX_IS_TRANSPARENT) && (ctx->flags & CTX_NO_ANTIALIAS)) + first_opaque = ctx; +#endif + /*node has changed, add to redraw area*/ + if (!redraw_all) { + ra_union_rect(&visual->to_redraw, &ctx->bi->clip); +// CHECK_MAX_NODE + } + } + ctx = ctx->next; + } + + /*garbage collection*/ + + /*clear all remaining bounds since last frames (the ones that moved or that are not drawn this frame)*/ + prev = NULL; + it = visual->prev_nodes; + while (it) { + while (drawable_get_previous_bound(it->drawable, &refreshRect, visual)) { + if (!redraw_all) { + ra_union_rect(&visual->to_redraw, &refreshRect); +// CHECK_MAX_NODE + has_clear=1; + } + } + /*if node is marked as undrawn, remove from visual*/ + if (!(it->drawable->flags & DRAWABLE_DRAWN_ON_VISUAL)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Node %s no longer on visual - unregistering it\n", gf_node_get_class_name(it->drawable->node))); + + /*remove all bounds info related to this visual and unreg node */ + drawable_reset_bounds(it->drawable, visual); + + it->drawable->flags &= ~DRAWABLE_REGISTERED_WITH_VISUAL; + + + if (prev) prev->next = it->next; + else visual->prev_nodes = it->next; + if (!it->next) visual->last_prev_entry = prev; + free(it); + it = prev ? prev->next : visual->prev_nodes; + } else { + prev = it; + it = it->next; + } + } + /*no visual */ + //if (!visual->compositor->output_width && !visual->compositor->output_height) goto exit; + +// CHECK_MAX_NODE + + if (redraw_all) { + ra_clear(&visual->to_redraw); + ra_add(&visual->to_redraw, &visual->surf_rect); + } else { + ra_refresh(&visual->to_redraw); + } +#ifdef TRACK_OPAQUE_REGIONS + /*mark opaque areas to speed up*/ + mark_opaque_areas(visual); +#endif + + /*nothing to redraw*/ + if (ra_is_empty(&visual->to_redraw) ) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] No changes found since last frame - skipping redraw\n")); + goto exit; + } + has_changed = 1; + tr_state->traversing_mode = TRAVERSE_DRAW_2D; + +#ifndef TRACK_OPAQUE_REGIONS + if (first_opaque && (visual->to_redraw.count==1) && gf_rect_equal(first_opaque->bi->clip, visual->to_redraw.list[0])) { + visual->has_modif=0; + goto skip_background; + } +#endif + + /*redraw everything*/ + if (bck_ctx) { + drawable_check_bounds(bck_ctx, visual); + tr_state->ctx = bck_ctx; +#ifdef TRACK_OPAQUE_REGIONS + visual->draw_node_index = 0; +#endif + /*force clearing entire zone, not just viewport, when using color. If texture, we MUST + use the VP clipper in order to compute offsets when blitting bitmaps*/ + if (bck_ctx->aspect.fill_texture && bck_ctx->aspect.fill_texture->stream) { + bck_ctx->bi->clip = visual->top_clipper; + } else { + bck_ctx->bi->clip = visual->surf_rect; + } + bck_ctx->bi->unclip = gf_rect_ft(&bck_ctx->bi->clip); + gf_node_traverse(bck_ctx->drawable->node, tr_state); + } else { + count = visual->to_redraw.count; + for (k=0; kto_redraw.opaque_node_index[k] > 0) continue; +#endif + rc = visual->to_redraw.list[k]; + visual_2d_clear(visual, &rc, 0); + } + } + if (!redraw_all && !has_clear) visual->has_modif=0; + +#ifndef TRACK_OPAQUE_REGIONS +skip_background: +#endif + +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_COMPOSE)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redraw %d / %d nodes (all: %s)", num_changed, num_nodes, redraw_all ? "yes" : "no")); + if (visual->to_redraw.count>1) GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("\n")); + + for (i=0; ito_redraw.count; i++) + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("\tDirtyRect #%d: %d:%d@%dx%d\n", i+1, visual->to_redraw.list[i].x, visual->to_redraw.list[i].y, visual->to_redraw.list[i].width, visual->to_redraw.list[i].height)); + } +#endif + +#ifdef TRACK_OPAQUE_REGIONS + i=0; +#endif + check_rect = (visual->to_redraw.count>1) ? NULL : &visual->to_redraw.list[0]; + ctx = visual->context; + while (ctx && ctx->drawable) { + if (!check_rect || gf_irect_overlaps(&ctx->bi->clip, check_rect)) { +#ifdef TRACK_OPAQUE_REGIONS + i++; + visual->draw_node_index = i; +#endif + tr_state->ctx = ctx; + + /*if overlay we cannot remove the context and cannot draw directly*/ + if (! visual_2d_overlaps_overlay(tr_state->visual, ctx, tr_state)) { + + if (ctx->drawable->flags & DRAWABLE_USE_TRAVERSE_DRAW) { + gf_node_traverse(ctx->drawable->node, tr_state); + } else { + drawable_draw(ctx->drawable, tr_state); + } + } + } + ctx = ctx->next; + } + /*flush pending contexts due to overlays*/ + visual_2d_flush_overlay_areas(visual, tr_state); + +exit: + /*clear dirty rects*/ + ra_clear(&visual->to_redraw); + visual_2d_release_raster(visual); + visual_clean_contexts(visual); + visual->num_nodes_prev_frame = visual->num_nodes_current_frame; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redraw done - %schanged\n", has_changed ? "" : "un")); + return has_changed; +} + +Bool visual_2d_draw_frame(GF_VisualManager *visual, GF_Node *root, GF_TraverseState *tr_state, Bool is_root_visual) +{ + GF_SceneGraph *sg; + GF_Matrix2D backup; + u32 i; + GF_Err e; +#ifndef GPAC_DISABLE_LOG + u32 itime, time = gf_sys_clock(); +#endif + + gf_mx2d_copy(backup, tr_state->transform); + visual->bounds_tracker_modif_flag = DRAWABLE_HAS_CHANGED; + + e = visual_2d_init_draw(visual, tr_state); + if (e) { + gf_mx2d_copy(tr_state->transform, backup); + return 0; + } + +#ifndef GPAC_DISABLE_LOG + itime = gf_sys_clock(); + visual->compositor->traverse_setup_time = itime - time; + time = itime; +#endif + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Traversing scene subtree (root node %s)\n", root ? gf_node_get_class_name(root) : "none")); + + if (is_root_visual) { + gf_node_traverse(root, tr_state); + + i=0; + while ((sg = (GF_SceneGraph*)gf_list_enum(visual->compositor->extra_scenes, &i))) { + gf_sc_traverse_subscene(visual->compositor, root, sg, tr_state); + } + } else { + gf_node_traverse(root, tr_state); + } + +#ifndef GPAC_DISABLE_LOG + itime = gf_sys_clock(); + visual->compositor->traverse_and_direct_draw_time = itime - time; + time = itime; +#endif + + gf_mx2d_copy(tr_state->transform, backup); + e = visual_2d_terminate_draw(visual, tr_state); + +#ifndef GPAC_DISABLE_LOG + if (!tr_state->direct_draw) { + visual->compositor->indirect_draw_time = gf_sys_clock() - time; + } +#endif + + return e; +} + + +void visual_2d_pick_node(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children) +{ + GF_Matrix2D backup; + visual->bounds_tracker_modif_flag = DRAWABLE_HAS_CHANGED_IN_LAST_TRAVERSE; + + gf_mx2d_copy(backup, tr_state->transform); + + visual_2d_setup_projection(visual, tr_state); + + visual->compositor->hit_node = NULL; + tr_state->ray.orig.x = INT2FIX(ev->mouse.x); + tr_state->ray.orig.y = INT2FIX(ev->mouse.y); + tr_state->ray.orig.z = 0; + tr_state->ray.dir.x = 0; + tr_state->ray.dir.y = 0; + tr_state->ray.dir.z = -FIX_ONE; + + visual->compositor->hit_world_point = tr_state->ray.orig; + visual->compositor->hit_world_ray = tr_state->ray; + visual->compositor->hit_square_dist = 0; + + gf_list_reset(visual->compositor->sensors); + tr_state->traversing_mode = TRAVERSE_PICK; + + /*not the root scene, use children list*/ + if (visual->compositor->visual != visual) { + while (children) { + gf_node_traverse(children->node, tr_state); + children = children->next; + } + } else { + u32 i = 0; + GF_SceneGraph *sg = visual->compositor->scene; + GF_Node *root = gf_sg_get_root_node(sg); + gf_node_traverse(root, tr_state); + while ((sg = (GF_SceneGraph*)gf_list_enum(visual->compositor->extra_scenes, &i))) { + gf_sc_traverse_subscene(visual->compositor, root, sg, tr_state); + } + } + gf_mx2d_copy(tr_state->transform, backup); +} + diff --git a/src/compositor/visual_manager_2d.h b/src/compositor/visual_manager_2d.h new file mode 100644 index 0000000..7252d25 --- /dev/null +++ b/src/compositor/visual_manager_2d.h @@ -0,0 +1,149 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _VISUAL_MANAGER_2D_ +#define _VISUAL_MANAGER_2D_ + +Bool gf_irect_overlaps(GF_IRect *rc1, GF_IRect *rc2); +/*intersects @rc1 with @rc2 - the new @rc1 is the intersection*/ +void gf_irect_intersect(GF_IRect *rc1, GF_IRect *rc2); +GF_Rect gf_rect_ft(GF_IRect *rc); +/*@rc2 fully contained in @rc1*/ +Bool gf_irect_inside(GF_IRect *rc1, GF_IRect *rc2); + +/*@rc1 equales @rc2*/ +#define gf_rect_equal(rc1, rc2) ((rc1.width == rc2.width) && (rc1.height == rc2.height) && (rc1.x == rc2.x) && (rc1.y == rc2.y)) + + +//#define TRACK_OPAQUE_REGIONS + +/*ra_: rectangle array macros to speed dirty rects*/ +#define RA_DEFAULT_STEP 10 + +typedef struct +{ + GF_IRect *list; + u32 count, alloc; +#ifdef TRACK_OPAQUE_REGIONS + /*list of nodes covering (no transparency) each rect, or 0 otherwise.*/ + u32 *opaque_node_index; +#endif +} GF_RectArray; + +#ifdef TRACK_OPAQUE_REGIONS +/*inits structure - called as a constructor*/ +#define ra_init(ra) { (ra)->count = 0; (ra)->alloc = 1; (ra)->list = (GF_IRect*)malloc(sizeof(GF_IRect)); (ra)->opaque_node_index = NULL;} +/*deletes structure - called as a destructor*/ +#define ra_del(ra) { free((ra)->list); if ((ra)->opaque_node_index) free((ra)->opaque_node_index); } +#else +#define ra_init(ra) { (ra)->count = 0; (ra)->alloc = 1; (ra)->list = (GF_IRect*)malloc(sizeof(GF_IRect));} +/*deletes structure - called as a destructor*/ +#define ra_del(ra) { free((ra)->list); } +#endif + +/*adds rect to list - expand if needed*/ +#define ra_add(ra, rc) { \ + if ((ra)->count==(ra)->alloc) { (ra)->alloc += RA_DEFAULT_STEP; (ra)->list = (GF_IRect*)realloc((ra)->list, sizeof(GF_IRect) * (ra)->alloc); } \ + (ra)->list[(ra)->count] = *rc; (ra)->count++; } + + + +/*adds rectangle to the list performing union test*/ +void ra_union_rect(GF_RectArray *ra, GF_IRect *rc); +/*refreshes the content of the array to have only non-overlapping rects*/ +void ra_refresh(GF_RectArray *ra); + + +/*adds @rc2 to @rc1 - the new @rc1 contains the old @rc1 and @rc2*/ +void gf_irect_union(GF_IRect *rc1, GF_IRect *rc2); + +struct _drawable_store +{ + struct _drawable *drawable; + struct _drawable_store *next; +}; + +/* + * Visual 2D functions + */ +Bool visual_2d_draw_frame(GF_VisualManager *visual, GF_Node *root, GF_TraverseState *tr_state, Bool is_root_visual); + +Bool visual_2d_node_cull(GF_TraverseState *tr_state, GF_Rect *bounds); + +void visual_2d_pick_node(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children); + +/*gets a drawable context on this visual*/ +DrawableContext *visual_2d_get_drawable_context(GF_VisualManager *visual); +/*remove last drawable context*/ +void visual_2d_remove_last_context(GF_VisualManager *visual); +/*signal the given drawable is being deleted*/ +void visual_2d_drawable_delete(GF_VisualManager *visual, Drawable *node); + +/*inits raster surface handler for subsequent draw*/ +GF_Err visual_2d_init_raster(GF_VisualManager *visual); +/*releases raster surface handler */ +void visual_2d_release_raster(GF_VisualManager *visual); + +/*clear given rect or all visual if no rect specified - clear color depends on visual's type: + BackColor for background nodes + 0x00000000 for composite, + compositor clear color otherwise +*/ +void visual_2d_clear(GF_VisualManager *visual, GF_IRect *clear, u32 BackColor); +/*texture the path with the given context info*/ +void visual_2d_texture_path(GF_VisualManager *visual, GF_Path *path, DrawableContext *ctx, GF_TraverseState *tr_state); +/*draw the path (fill and strike) - if brushes are NULL they are created if needed based on the context aspect +DrawPath shall always be called after TexturePath*/ +void visual_2d_draw_path(GF_VisualManager *visual, GF_Path *path, DrawableContext *ctx, GF_STENCIL brush, GF_STENCIL pen, GF_TraverseState *tr_state); +/*special texturing extension for text, using a given path (text rectangle) and texture*/ +void visual_2d_texture_path_text(GF_VisualManager *visual, DrawableContext *txt_ctx, GF_Path *path, GF_Rect *object_bounds, GF_TextureHandler *txh, GF_TraverseState *tr_state); +/*fill given rect with given color with given ctx transform and clipper (used for text hilighting only) +if rc is NULL, fills object bounds*/ +void visual_2d_fill_rect(GF_VisualManager *visual, DrawableContext *ctx, GF_Rect *rc, u32 color, u32 strike_color, GF_TraverseState *tr_state); + + +/*extended version of above function to override texture transforms - needed for proper texturing of glyphs*/ +void visual_2d_texture_path_extended(GF_VisualManager *visual, GF_Path *path, GF_TextureHandler *txh, struct _drawable_context *ctx, GF_Rect *orig_bounds, GF_Matrix2D *ext_mx, GF_TraverseState *tr_state); +void visual_2d_draw_path_extended(GF_VisualManager *visual, GF_Path *path, DrawableContext *ctx, GF_STENCIL brush, GF_STENCIL pen, GF_TraverseState *tr_state, GF_Rect *orig_bounds, GF_Matrix2D *ext_mx); + + +/*video overlay context*/ +typedef struct _video_overlay +{ + struct _video_overlay *next; + GF_Window src, dst; + DrawableContext *ctx; + GF_RectArray ra; +} GF_OverlayStack; + +/*check if the object is over an overlay. If so, adds its cliper to the list of rectangles to redraw for the overlay +and returns 1 (in which case it shouldn't be drawn)*/ +Bool visual_2d_overlaps_overlay(GF_VisualManager *visual, DrawableContext *ctx, GF_TraverseState *tr_state); +/*draw all partial overlays in software and all overlaping objects*/ +void visual_2d_flush_overlay_areas(GF_VisualManager *visual, GF_TraverseState *tr_state); +/*finally blit the overlays - MUST be called once the main visual has been flushed*/ +void visual_2d_draw_overlays(GF_VisualManager *visual); + +#endif /*_VISUAL_MANAGER_2D_*/ + diff --git a/src/compositor/visual_manager_2d_draw.c b/src/compositor/visual_manager_2d_draw.c new file mode 100644 index 0000000..57fd801 --- /dev/null +++ b/src/compositor/visual_manager_2d_draw.c @@ -0,0 +1,581 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "visual_manager.h" +#include "drawable.h" +#include "nodes_stacks.h" +#include "texturing.h" +#include + +//#define SKIP_DRAW + +GF_Err visual_2d_init_raster(GF_VisualManager *visual) +{ + GF_Raster2D *raster = visual->compositor->rasterizer; + if (!visual->raster_surface) { + visual->raster_surface = raster->surface_new(raster, visual->center_coords); + if (!visual->raster_surface) return GF_IO_ERR; + } + return visual->GetSurfaceAccess(visual); +} + +void visual_2d_release_raster(GF_VisualManager *visual) +{ + if (visual->raster_surface) { + if (visual->compositor->rasterizer->surface_flush) + visual->compositor->rasterizer->surface_flush(visual->raster_surface); + + visual->ReleaseSurfaceAccess(visual); + } +} + + +void visual_2d_clear(GF_VisualManager *visual, GF_IRect *rc, u32 BackColor) +{ +#ifdef SKIP_DRAW + return; +#endif + if (!visual->raster_surface) return; + + if (!BackColor && !visual->offscreen) BackColor = visual->compositor->back_color; + + visual->compositor->rasterizer->surface_clear(visual->raster_surface, rc, BackColor); +} + +static void draw_clipper(GF_VisualManager *visual, struct _drawable_context *ctx) +{ + GF_PenSettings clipset; + GF_Path *clippath, *cliper; + GF_Raster2D *raster = visual->compositor->rasterizer; + + if (ctx->flags & CTX_IS_BACKGROUND) return; + + memset(&clipset, 0, sizeof(GF_PenSettings)); + clipset.width = 2*FIX_ONE; + + clippath = gf_path_new(); + gf_path_add_rect_center(clippath, ctx->bi->unclip.x + ctx->bi->unclip.width/2, ctx->bi->unclip.y - ctx->bi->unclip.height/2, ctx->bi->unclip.width, ctx->bi->unclip.height); + cliper = gf_path_get_outline(clippath, clipset); + gf_path_del(clippath); + raster->surface_set_matrix(visual->raster_surface, NULL); + raster->surface_set_clipper(visual->raster_surface, NULL); + raster->surface_set_path(visual->raster_surface, cliper); + raster->stencil_set_brush_color(visual->raster_brush, 0xFF000000); + raster->surface_fill(visual->raster_surface, visual->raster_brush); + gf_path_del(cliper); +} + +static void visual_2d_fill_path(GF_VisualManager *visual, DrawableContext *ctx, GF_STENCIL stencil, GF_TraverseState *tr_state) +{ + GF_IRect clip; + GF_Raster2D *raster = visual->compositor->rasterizer; + + /*background & direct drawing : use ctx clip*/ + if ((ctx->flags & CTX_IS_BACKGROUND) || tr_state->direct_draw) { + if (ctx->bi->clip.width && ctx->bi->clip.height) { + //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s (direct draw)\n", gf_node_get_log_name(ctx->drawable->node) )); + raster->surface_set_clipper(visual->raster_surface, &ctx->bi->clip); + raster->surface_fill(visual->raster_surface, stencil); + + visual->has_modif = 1; + } + } + /*indirect drawing, draw path in all dirty areas*/ + else { + u32 i; + for (i=0; ito_redraw.count; i++) { + /*there's an opaque region above, don't draw*/ +#ifdef TRACK_OPAQUE_REGIONS + if (visual->draw_node_indexto_redraw.opaque_node_index[i]) continue; +#endif + clip = ctx->bi->clip; + gf_irect_intersect(&clip, &visual->to_redraw.list[i]); + if (clip.width && clip.height) { + //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s (indirect draw)\n", gf_node_get_log_name(ctx->drawable->node) )); + raster->surface_set_clipper(visual->raster_surface, &clip); + raster->surface_fill(visual->raster_surface, stencil); + + visual->has_modif = 1; + } + } + } +} + +static void visual_2d_set_options(GF_Compositor *compositor, GF_SURFACE rend, Bool forText, Bool no_antialias) +{ + GF_Raster2D *raster = compositor->rasterizer; + if (no_antialias) { + raster->surface_set_raster_level(rend, compositor->high_speed ? GF_RASTER_HIGH_SPEED : GF_RASTER_MID); + } else { + switch (compositor->antiAlias) { + case GF_ANTIALIAS_NONE: + raster->surface_set_raster_level(rend, GF_RASTER_HIGH_SPEED); + break; + case GF_ANTIALIAS_TEXT: + if (forText) { + raster->surface_set_raster_level(rend, GF_RASTER_HIGH_QUALITY); + } else { + raster->surface_set_raster_level(rend, compositor->high_speed ? GF_RASTER_HIGH_QUALITY : GF_RASTER_MID); + } + break; + case GF_ANTIALIAS_FULL: + default: + raster->surface_set_raster_level(rend, GF_RASTER_HIGH_QUALITY); + break; + } + } +} + + +static void visual_2d_get_texture_transform(GF_Node *__appear, GF_TextureHandler *txh, GF_Matrix2D *mat, Bool line_texture, Fixed final_width, Fixed final_height) +{ + u32 node_tag; + M_Appearance *appear; + GF_Node *txtrans = NULL; + gf_mx2d_init(*mat); + + if (!__appear || !txh) return; + appear = (M_Appearance *)__appear; + + if (!line_texture) { + if (!appear->textureTransform) return; + txtrans = appear->textureTransform; + } else { + if (gf_node_get_tag(appear->material) != TAG_MPEG4_Material2D) return; + if (gf_node_get_tag(((M_Material2D *)appear->material)->lineProps) != TAG_MPEG4_XLineProperties) return; + txtrans = ((M_XLineProperties *) ((M_Material2D *)appear->material)->lineProps)->textureTransform; + } + if (!txtrans) return; + + /*gradient doesn't need bounds info in texture transform*/ + if (txh->compute_gradient_matrix) { + final_width = final_height = FIX_ONE; + } + node_tag = gf_node_get_tag(txtrans); + if (node_tag==TAG_MPEG4_TextureTransform) { + /*VRML: Tc' = -C � S � R � C � T � Tc*/ + M_TextureTransform *txt = (M_TextureTransform *) txtrans; + SFVec2f scale = txt->scale; + if (!scale.x) scale.x = FIX_ONE/100; + if (!scale.y) scale.y = FIX_ONE/100; + + gf_mx2d_add_translation(mat, -gf_mulfix(txt->center.x, final_width), -gf_mulfix(txt->center.y, final_height) ); + gf_mx2d_add_scale(mat, scale.x, scale.y); + gf_mx2d_add_rotation(mat, 0, 0, txt->rotation); + gf_mx2d_add_translation(mat, gf_mulfix(txt->center.x, final_width), gf_mulfix(txt->center.y, final_height) ); + gf_mx2d_add_translation(mat, gf_mulfix(txt->translation.x, final_width), gf_mulfix(txt->translation.y, final_height) ); + /*and inverse the matrix (this is texture transform, cf VRML)*/ + gf_mx2d_inverse(mat); + return; + } + if (node_tag==TAG_MPEG4_TransformMatrix2D) { + tr_mx2d_get_matrix((GF_Node *) txtrans, mat); + mat->m[2] = gf_mulfix(mat->m[2], final_width); + mat->m[5] = gf_mulfix(mat->m[5], final_height); + gf_mx2d_inverse(mat); + return; + } +} + + +static void visual_2d_draw_gradient(GF_VisualManager *visual, GF_Path *path, GF_TextureHandler *txh, struct _drawable_context *ctx, GF_TraverseState *tr_state, GF_Matrix2D *ext_mx, GF_Rect *orig_bounds) +{ + GF_Rect rc; + GF_STENCIL stencil; + GF_Matrix2D g_mat, txt_mat; + GF_Raster2D *raster = visual->compositor->rasterizer; + + if (!txh) txh = ctx->aspect.fill_texture; + + gf_path_get_bounds(path, &rc); + if (!rc.width || !rc.height || !txh->tx_io) return; + + if (orig_bounds) { + txh->compute_gradient_matrix(txh, orig_bounds, &g_mat, 0); + } else { + txh->compute_gradient_matrix(txh, &rc, &g_mat, 0); + } + stencil = gf_sc_texture_get_stencil(txh); + if (!stencil) return; + + if (ctx->flags & CTX_HAS_APPEARANCE) { + visual_2d_get_texture_transform(ctx->appear, txh, &txt_mat, (txh == ctx->aspect.fill_texture) ? 0 : 1, INT2FIX(txh->width), INT2FIX(txh->height)); + gf_mx2d_add_matrix(&g_mat, &txt_mat); + } + /*move to bottom-left corner of bounds */ + if (ext_mx) gf_mx2d_add_matrix(&g_mat, ext_mx); + if (orig_bounds) gf_mx2d_add_translation(&g_mat, (orig_bounds->x), (orig_bounds->y - orig_bounds->height)); + + gf_mx2d_add_matrix(&g_mat, &ctx->transform); + + raster->stencil_set_matrix(stencil, &g_mat); + raster->stencil_set_color_matrix(stencil, ctx->col_mat); + + /*MPEG-4/VRML context or no fill info*/ + if (ctx->flags & CTX_HAS_APPEARANCE || !ctx->aspect.fill_color) + raster->stencil_set_alpha(stencil, 0xFF); + else + raster->stencil_set_alpha(stencil, GF_COL_A(ctx->aspect.fill_color) ); + + raster->surface_set_matrix(visual->raster_surface, &ctx->transform); + txh->flags |= GF_SR_TEXTURE_USED; + + raster->surface_set_path(visual->raster_surface, path); + visual_2d_fill_path(visual, ctx, stencil, tr_state); + raster->surface_set_path(visual->raster_surface, NULL); + + ctx->flags |= CTX_PATH_FILLED; +} + + + +void visual_2d_texture_path_text(GF_VisualManager *visual, DrawableContext *txt_ctx, GF_Path *path, GF_Rect *object_bounds, GF_TextureHandler *txh, GF_TraverseState *tr_state) +{ + GF_STENCIL stencil; + Fixed sS, sT; + GF_Matrix2D gf_mx2d_txt; + GF_Rect orig_rc; + u8 alpha, r, g, b; + GF_ColorMatrix cmat; + GF_Raster2D *raster = visual->compositor->rasterizer; + + stencil = gf_sc_texture_get_stencil(txh); + if (!stencil) return; + + visual_2d_set_options(visual->compositor, visual->raster_surface, 0, 1); + + /*get original bounds*/ + orig_rc = *object_bounds; + + /*get scaling ratio so that active texture view is stretched to original bounds (std 2D shape texture mapping in MPEG4)*/ + sS = gf_divfix(orig_rc.width, INT2FIX(txh->width)); + sT = gf_divfix(orig_rc.height, INT2FIX(txh->height)); + + gf_mx2d_init(gf_mx2d_txt); + gf_mx2d_add_scale(&gf_mx2d_txt, sS, sT); + + /*move to bottom-left corner of bounds */ + gf_mx2d_add_translation(&gf_mx2d_txt, (orig_rc.x), (orig_rc.y - orig_rc.height)); + + /*move to final coordinate system*/ + gf_mx2d_add_matrix(&gf_mx2d_txt, &txt_ctx->transform); + + /*set path transform, except for background2D node which is directly build in the final coord system*/ + raster->stencil_set_matrix(stencil, &gf_mx2d_txt); + + alpha = GF_COL_A(txt_ctx->aspect.fill_color); + r = GF_COL_R(txt_ctx->aspect.fill_color); + g = GF_COL_G(txt_ctx->aspect.fill_color); + b = GF_COL_B(txt_ctx->aspect.fill_color); + + /*if col do a cxmatrix*/ + if (!r && !g && !b) { + raster->stencil_set_alpha(stencil, alpha); + } else { + raster->stencil_set_alpha(stencil, 0xFF); + memset(cmat.m, 0, sizeof(Fixed) * 20); + cmat.m[4] = INT2FIX(r)/255; + cmat.m[9] = INT2FIX(g)/255; + cmat.m[14] = INT2FIX(b)/255; + cmat.m[18] = INT2FIX(alpha)/255; + cmat.identity = 0; + raster->stencil_set_color_matrix(stencil, &cmat); + } + + raster->surface_set_matrix(visual->raster_surface, &txt_ctx->transform); + txh->flags |= GF_SR_TEXTURE_USED; + + /*push path*/ + raster->surface_set_path(visual->raster_surface, path); + + visual_2d_fill_path(visual, txt_ctx, stencil, tr_state); + raster->surface_set_path(visual->raster_surface, NULL); + txt_ctx->flags |= CTX_PATH_FILLED; +} + +void visual_2d_texture_path_extended(GF_VisualManager *visual, GF_Path *path, GF_TextureHandler *txh, struct _drawable_context *ctx, GF_Rect *orig_bounds, GF_Matrix2D *ext_mx, GF_TraverseState *tr_state) +{ + Fixed sS, sT; + u32 tx_tile; + GF_STENCIL tx_raster; + GF_Matrix2D mx_texture, tex_trans; + GF_Rect rc, orig_rc; + GF_Raster2D *raster = visual->compositor->rasterizer; + + if (!txh) txh = ctx->aspect.fill_texture; + if (!txh || !txh->tx_io) return; + + + /*this is gradient draw*/ + if (txh->compute_gradient_matrix) { + visual_2d_draw_gradient(visual, path, txh, ctx, tr_state, ext_mx, orig_bounds); + return; + } + + if (!gf_sc_texture_push_image(txh, 0, 1)) return; + tx_raster = gf_sc_texture_get_stencil(txh); + + /*setup quality even for background (since quality concerns images)*/ + visual_2d_set_options(visual->compositor, visual->raster_surface, ctx->flags & CTX_IS_TEXT, ctx->flags & CTX_NO_ANTIALIAS); + + /*get original bounds*/ + if (orig_bounds) { + orig_rc = *orig_bounds; + } else { + gf_path_get_bounds(path, &orig_rc); + } + + /*get active texture window in pixels*/ + rc.width = INT2FIX(txh->width); + rc.height = INT2FIX(txh->height); + + /*get scaling ratio so that active texture view is stretched to original bounds (std 2D shape texture mapping in MPEG4)*/ + sS = orig_rc.width / txh->width; + sT = orig_rc.height / txh->height; + + gf_mx2d_init(mx_texture); + gf_mx2d_add_scale(&mx_texture, sS, sT); + + /*apply texture transform*/ + if (ctx->flags & CTX_HAS_APPEARANCE) { + visual_2d_get_texture_transform(ctx->appear, txh, &tex_trans, (txh == ctx->aspect.fill_texture) ? 0 : 1, txh->width * sS, txh->height * sT); + gf_mx2d_add_matrix(&mx_texture, &tex_trans); + } + + /*move to bottom-left corner of bounds */ + gf_mx2d_add_translation(&mx_texture, (orig_rc.x), (orig_rc.y - orig_rc.height)); + + if (ext_mx) gf_mx2d_add_matrix(&mx_texture, ext_mx); + + /*move to final coordinate system (except background which is built directly in final coord system)*/ + if (!(ctx->flags & CTX_IS_BACKGROUND) ) gf_mx2d_add_matrix(&mx_texture, &ctx->transform); + + /*set path transform, except for background2D node which is directly build in the final coord system*/ + raster->stencil_set_matrix(tx_raster, &mx_texture); + + + tx_tile = 0; + if (txh->flags & GF_SR_TEXTURE_REPEAT_S) tx_tile |= GF_TEXTURE_REPEAT_S; + if (txh->flags & GF_SR_TEXTURE_REPEAT_T) tx_tile |= GF_TEXTURE_REPEAT_T; + if (ctx->flags & CTX_FLIPED_COORDS) + tx_tile |= GF_TEXTURE_FLIP; + raster->stencil_set_tiling(tx_raster, (GF_TextureTiling) tx_tile); + + if (!(ctx->flags & CTX_IS_BACKGROUND) ) { + u8 a = GF_COL_A(ctx->aspect.fill_color); + if (!a) a = GF_COL_A(ctx->aspect.line_color); + /*texture alpha scale is the original material transparency, NOT the one after color transform*/ + raster->stencil_set_alpha(tx_raster, a ); + raster->stencil_set_color_matrix(tx_raster, ctx->col_mat); + + raster->surface_set_matrix(visual->raster_surface, &ctx->transform); + } else { + raster->surface_set_matrix(visual->raster_surface, NULL); + } + txh->flags |= GF_SR_TEXTURE_USED; + + /*push path & draw*/ + raster->surface_set_path(visual->raster_surface, path); + visual_2d_fill_path(visual, ctx, tx_raster, tr_state); + raster->surface_set_path(visual->raster_surface, NULL); + + + + ctx->flags |= CTX_PATH_FILLED; +} + +void visual_2d_texture_path(GF_VisualManager *visual, GF_Path *path, struct _drawable_context *ctx, GF_TraverseState *tr_state) +{ +#ifdef SKIP_DRAW + return; +#endif + if (!visual->raster_surface || (ctx->flags & CTX_PATH_FILLED) || !ctx->aspect.fill_texture || visual->compositor->is_hidden) return; + + /*this is ambiguous in the spec, what if the material is filled and the texture is transparent ? + let's draw, it's nicer */ +#if 0 + if (GF_COL_A(ctx->aspect.fill_color) && ctx->aspect.fill_texture->transparent) { + visual_2d_draw_path(visual, path, ctx, NULL, NULL); + ctx->flags &= ~CTX_PATH_FILLED; + } +#endif + + visual_2d_texture_path_extended(visual, path, NULL, ctx, NULL, NULL, tr_state); +} + +#define ADAPTATION_SIZE 0 + + +void visual_2d_draw_path_extended(GF_VisualManager *visual, GF_Path *path, DrawableContext *ctx, GF_STENCIL brush, GF_STENCIL pen, GF_TraverseState *tr_state, GF_Rect *orig_bounds, GF_Matrix2D *ext_mx) +{ + Bool dofill, dostrike; + GF_Raster2D *raster = visual->compositor->rasterizer; +#ifdef SKIP_DRAW + return; +#endif + + assert(visual->raster_surface); + + if ((ctx->flags & CTX_PATH_FILLED) && (ctx->flags & CTX_PATH_STROKE) ) { + if (visual->compositor->draw_bvol) draw_clipper(visual, ctx); + return; + } + + if (! (ctx->flags & CTX_IS_BACKGROUND) ) visual_2d_set_options(visual->compositor, visual->raster_surface, ctx->flags & CTX_IS_TEXT, 0); + + dofill = dostrike = 0; + if (!(ctx->flags & CTX_PATH_FILLED) && GF_COL_A(ctx->aspect.fill_color) ) { + dofill = 1; + if (!brush) { + brush = visual->raster_brush; + raster->stencil_set_brush_color(brush, ctx->aspect.fill_color); + } + } + + + /*compute width based on transform and top_level transform*/ + if (!(ctx->flags & CTX_PATH_STROKE) && ctx->aspect.pen_props.width) { + dostrike = 1; + } else if (!dofill) { + return; + } + + /*set path transform, except for background2D node which is directly build in the final coord system*/ + raster->surface_set_matrix(visual->raster_surface, (ctx->flags & CTX_IS_BACKGROUND) ? NULL : &ctx->transform); + + /*fill path*/ + if (dofill) { +#if ADAPTATION_SIZE + if ((ctx->bi->clip.widthbi->clip.heightsurface_clear(visual->raster_surface, &ctx->bi->clip, ctx->aspect.fill_color); + } else +#endif + { + /*push path*/ + raster->surface_set_path(visual->raster_surface, path); + visual_2d_fill_path(visual, ctx, brush, tr_state); + raster->surface_set_path(visual->raster_surface, NULL); + } + } + + if (dostrike) { +#if ADAPTATION_SIZE + if ((ctx->bi->clip.widthbi->clip.heightraster_brush; + raster->stencil_set_brush_color(pen, ctx->aspect.line_color); + } + + si = drawable_get_strikeinfo(visual->compositor, ctx->drawable, &ctx->aspect, ctx->appear, path, ctx->flags, NULL); + if (si && si->outline) { + if (ctx->aspect.line_texture) { + visual_2d_texture_path_extended(visual, si->outline, ctx->aspect.line_texture, ctx, orig_bounds, ext_mx, tr_state); + } else { + raster->surface_set_path(visual->raster_surface, si->outline); + visual_2d_fill_path(visual, ctx, pen, tr_state); + } + /*that's ugly, but we cannot cache path outline for IFS2D/ILS2D*/ + if (path && !(ctx->flags & CTX_IS_TEXT) && (path!=ctx->drawable->path) ) { + gf_path_del(si->outline); + si->outline = NULL; + } + } +// drawable_reset_path_outline(ctx->drawable); + } + } + + if (visual->compositor->draw_bvol) draw_clipper(visual, ctx); +} + +void visual_2d_draw_path(GF_VisualManager *visual, GF_Path *path, DrawableContext *ctx, GF_STENCIL brush, GF_STENCIL pen, GF_TraverseState *tr_state) +{ + visual_2d_draw_path_extended(visual, path, ctx, brush, pen, tr_state, NULL, NULL); +} + +void visual_2d_fill_rect(GF_VisualManager *visual, DrawableContext *ctx, GF_Rect *_rc, u32 color, u32 strike_color, GF_TraverseState *tr_state) +{ + GF_Path *path; + GF_Rect *rc; + GF_Raster2D *raster = visual->compositor->rasterizer; +#ifdef SKIP_DRAW + return; +#endif + + if (!visual->raster_surface) return; + if (!color && !strike_color) return; + + if ((ctx->flags & CTX_PATH_FILLED) && (ctx->flags & CTX_PATH_STROKE) ) { + if (visual->compositor->draw_bvol) draw_clipper(visual, ctx); + return; + } + + /*no aa*/ + visual_2d_set_options(visual->compositor, visual->raster_surface, 0, 1); + if (_rc) { + rc = _rc; + raster->surface_set_matrix(visual->raster_surface, &ctx->transform); + } + else { + rc = &ctx->bi->unclip; + raster->surface_set_matrix(visual->raster_surface, NULL); + } + + path = gf_path_new(); + gf_path_add_move_to(path, rc->x, rc->y-rc->height); + gf_path_add_line_to(path, rc->x+rc->width, rc->y-rc->height); + gf_path_add_line_to(path, rc->x+rc->width, rc->y); + gf_path_add_line_to(path, rc->x, rc->y); + gf_path_close(path); + + + if (color) { + /*push path*/ + raster->surface_set_path(visual->raster_surface, path); + raster->stencil_set_brush_color(visual->raster_brush, color); + visual_2d_fill_path(visual, ctx, visual->raster_brush, tr_state); + raster->surface_set_path(visual->raster_surface, NULL); + } + if (strike_color) { + GF_Path *outline; + GF_PenSettings pen; + memset(&pen, 0, sizeof(GF_PenSettings)); + pen.width = 1; + pen.join = GF_LINE_JOIN_BEVEL; + pen.dash = GF_DASH_STYLE_DOT; + raster->stencil_set_brush_color(visual->raster_brush, strike_color); + outline = gf_path_get_outline(path, pen); + outline->flags &= ~GF_PATH_FILL_ZERO_NONZERO; + raster->surface_set_path(visual->raster_surface, outline); + visual_2d_fill_path(visual, ctx, visual->raster_brush, tr_state); + raster->surface_set_path(visual->raster_surface, NULL); + gf_path_del(outline); + } + + gf_path_del(path); +} diff --git a/src/compositor/visual_manager_3d.c b/src/compositor/visual_manager_3d.c new file mode 100644 index 0000000..c374c65 --- /dev/null +++ b/src/compositor/visual_manager_3d.c @@ -0,0 +1,1517 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "visual_manager.h" +#include "texturing.h" +#include "nodes_stacks.h" +#include + +#ifndef GPAC_DISABLE_3D + + + +/*generic drawable 3D constructor/destructor*/ +Drawable3D *drawable_3d_new(GF_Node *node) +{ + Drawable3D *tmp; + GF_SAFEALLOC(tmp, Drawable3D); + tmp->mesh = new_mesh(); + gf_node_set_private(node, tmp); + return tmp; +} +void drawable_3d_del(GF_Node *n) +{ + Drawable3D *d = (Drawable3D *)gf_node_get_private(n); + if (d) { + if (d->mesh) mesh_free(d->mesh); + free(d); + } + gf_sc_check_focus_upon_destroy(n); +} + + +void drawable3d_check_focus_highlight(GF_Node *node, GF_TraverseState *tr_state, GF_BBox *orig_bounds) +{ + Drawable *hlight; + GF_Node *prev_node; + u32 prev_mode; + GF_BBox *bounds; + GF_Matrix cur; + GF_Compositor *compositor = tr_state->visual->compositor; + + if (compositor->focus_node!=node) return; + + hlight = compositor->focus_highlight; + if (!hlight) return; + + /*check if focus node has changed*/ + prev_node = gf_node_get_private(hlight->node); + if (prev_node != node) { + gf_node_set_private(hlight->node, node); + + drawable_reset_path(hlight); + gf_path_reset(hlight->path); + } + /*this is a grouping node, get its bounds*/ + if (!orig_bounds) { + gf_mx_copy(cur, tr_state->model_matrix); + gf_mx_init(tr_state->model_matrix); + prev_mode = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; + tr_state->bbox.is_set = 0; + +// gf_sc_get_nodes_bounds(node, ((GF_ParentNode *)node)->children, tr_state, 1); + gf_node_traverse_children(node, tr_state); + + tr_state->traversing_mode = prev_mode; + gf_mx_copy(tr_state->model_matrix, cur); + bounds = &tr_state->bbox; + } else { + bounds = orig_bounds; + } + visual_3d_draw_bbox(tr_state, bounds); +} + + +static void visual_3d_setup_traversing_state(GF_VisualManager *visual, GF_TraverseState *tr_state) +{ + tr_state->visual = visual; + tr_state->camera = &visual->camera; + tr_state->backgrounds = visual->back_stack; + tr_state->viewpoints = visual->view_stack; + tr_state->fogs = visual->fog_stack; + tr_state->navigations = visual->navigation_stack; + tr_state->color_mat.identity = 1; + tr_state->camera->vp.x = tr_state->camera->vp.y = 0; + tr_state->min_hsize = INT2FIX(MIN(visual->width, visual->height) / 2); + if (!tr_state->min_hsize) tr_state->min_hsize = FIX_ONE; + + + /*main visual, set AR*/ + if (visual->compositor->visual==visual) { + if (tr_state->visual->compositor->has_size_info) { + tr_state->camera->vp.x = INT2FIX(tr_state->visual->compositor->vp_x); + tr_state->camera->vp.y = INT2FIX(tr_state->visual->compositor->vp_y); + tr_state->camera->vp.width = INT2FIX(tr_state->visual->compositor->vp_width); + tr_state->camera->vp.height = INT2FIX(tr_state->visual->compositor->vp_height); + + /*2D ortho, scale is already present in the root user transform*/ + if (visual->type_3d==1) { + tr_state->camera->width = INT2FIX(tr_state->visual->compositor->vp_width); + tr_state->camera->height = INT2FIX(tr_state->visual->compositor->vp_height); + } else { + tr_state->camera->width = INT2FIX(tr_state->visual->width); + tr_state->camera->height = INT2FIX(tr_state->visual->height); + } + } else { + Fixed sw, sh; + sw = INT2FIX(tr_state->visual->compositor->vp_width); + sh = INT2FIX(tr_state->visual->compositor->vp_height); + /*AR changed, rebuild camera*/ + + if (tr_state->visual->compositor->recompute_ar + || (sw!=tr_state->camera->vp.width) + || (sh!=tr_state->camera->vp.height)) { + tr_state->camera->width = tr_state->camera->vp.width = INT2FIX(tr_state->visual->compositor->vp_width); + tr_state->camera->height = tr_state->camera->vp.height = INT2FIX(tr_state->visual->compositor->vp_height); + tr_state->camera->flags |= CAM_IS_DIRTY; + } + } + } + /*composite visual, no AR*/ + else { + tr_state->camera->vp.width = tr_state->camera->width = INT2FIX(visual->width); + tr_state->camera->vp.height = tr_state->camera->height = INT2FIX(visual->height); + } + + if (!tr_state->pixel_metrics) { + if (tr_state->camera->height > tr_state->camera->width) { + tr_state->camera->height = 2*gf_divfix(tr_state->camera->height , tr_state->camera->width); + tr_state->camera->width = 2*FIX_ONE; + } else { + tr_state->camera->width = 2 * gf_divfix(tr_state->camera->width, tr_state->camera->height); + tr_state->camera->height = 2 * FIX_ONE; + } + } + /*setup bounds*/ + tr_state->bbox.max_edge.x = tr_state->camera->width / 2; + tr_state->bbox.min_edge.x = -tr_state->bbox.max_edge.x; + tr_state->bbox.max_edge.y = tr_state->camera->height / 2; + tr_state->bbox.min_edge.y = -tr_state->bbox.max_edge.y; + tr_state->bbox.max_edge.z = tr_state->bbox.min_edge.z = 0; + tr_state->bbox.is_set = 1; +} + + +void visual_3d_viewpoint_change(GF_TraverseState *tr_state, GF_Node *vp, Bool animate_change, Fixed fieldOfView, SFVec3f position, SFRotation orientation, SFVec3f local_center) +{ + Fixed dist; + SFVec3f d; + + /*update znear&zfar*/ + tr_state->camera->z_near = tr_state->camera->avatar_size.x ; + + if (tr_state->camera->z_near<=0) tr_state->camera->z_near = FIX_ONE/2; + /*if pixel metrics, the default znear may be way too far and lead to weird navigation*/ + else if (tr_state->camera->z_near>=FIX_ONE) tr_state->camera->z_near = FIX_ONE/2; + tr_state->camera->z_near /= 2; + tr_state->camera->z_far = tr_state->camera->visibility; + + /*z_far is selected so that an object the size of the viewport measures + one pixel when located at the far plane. It can be found through the projection + transformation (projection matrix) of x and y + x transformation is: x'= (1/(ar*tg(fov/2)) )*x/z + y transformation is: y'=(1/(tg(fov/2)))*x/z + + therefore when z=z_far and x=max(width/2, height/2), then + x' = 1/max(vp_size.x, vp_size.y) (transformed OpenGL viewport measures one) + + this yields z_far = max(vp_size.x, vp_size.y) * max(width/2, height/2) * max(1/(ar*tg(fov/2)), 1/tg(fov/2)) + z_far = max(vp_size.x, vp_size.y) * max(width, height) / (2*min(1, ar)*tg(fov/2)) ) + + to choose a z_far so that the size is more than one pixel, then z_far' = z_far/n_pixels*/ + if (tr_state->camera->z_far<=0) { + Fixed ar = gf_divfix(tr_state->vp_size.x, tr_state->vp_size.y); + if (ar>FIX_ONE) ar = FIX_ONE; + tr_state->camera->z_far = gf_muldiv( + MAX(tr_state->vp_size.x,tr_state->vp_size.y), + MAX(tr_state->camera->width, tr_state->camera->height), + gf_mulfix(ar*2, gf_tan(fieldOfView/2)) + ); + + /*fixed-point overflow*/ + if (tr_state->camera->z_far <0) { + tr_state->camera->z_far = FIX_MAX/4; + } + } + if (vp) { + /*now check if vp is in pixel metrics. If not then: + - either it's in the main scene, there's nothing to do + - or it's in an inline, and the inline has been scaled if main scene is in pm: nothing to do*/ + if (0 && gf_sg_use_pixel_metrics(gf_node_get_graph(vp))) { + GF_Matrix mx; + gf_mx_init(mx); + gf_mx_add_scale(&mx, tr_state->min_hsize, tr_state->min_hsize, tr_state->min_hsize); + gf_mx_apply_vec(&mx, &position); + gf_mx_apply_vec(&mx, &local_center); + } + } + /*default VP setup - this is undocumented in the spec. Default VP pos is (0, 0, 10) but not really nice + in pixel metrics. We set z so that we see just the whole visual*/ + else if (tr_state->pixel_metrics) { + position.z = gf_divfix(tr_state->camera->width, 2*gf_tan(fieldOfView/2) ); + + + } + /*HACK, LET'S PUSH NEAR PLANE TO CENTER OF COORDINATES*/ +// tr_state->camera->z_near = position.z ; + + gf_vec_diff(d, position, local_center); + dist = gf_vec_len(d); + + if (!dist || (distcamera->z_near) || (dist > tr_state->camera->z_far)) { + if (dist > tr_state->camera->z_far) tr_state->camera->z_far = 2*dist; + + dist = 10 * tr_state->camera->avatar_size.x; + if ((distcamera->z_near) || (dist > tr_state->camera->z_far)) + dist = (tr_state->camera->avatar_size.x + tr_state->camera->z_far) / 5; + } + tr_state->camera->vp_dist = dist; + tr_state->camera->vp_position = position; + tr_state->camera->vp_orientation = orientation; + tr_state->camera->vp_fov = fieldOfView; + tr_state->camera->examine_center = local_center; + + camera_reset_viewpoint(tr_state->camera, animate_change); + if (tr_state->layer3d) gf_node_dirty_set(tr_state->layer3d, GF_SG_VRML_BINDABLE_DIRTY, 0); + gf_sc_invalidate(tr_state->visual->compositor, NULL); +} + + +void visual_3d_setup_projection(GF_TraverseState *tr_state) +{ + GF_Node *bindable; + u32 mode = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_BINDABLE; + + /*setup viewpoint (this directly modifies the frustum)*/ + bindable = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); + if (Bindable_GetIsBound(bindable)) { + gf_node_traverse(bindable, tr_state); + tr_state->camera->had_viewpoint = 1; + } else if (tr_state->camera->had_viewpoint) { + if (tr_state->camera->is_3D) { + SFVec3f pos, center; + SFRotation r; + Fixed fov = GF_PI/4; + /*default viewpoint*/ + pos.x = pos.y = 0; pos.z = 10 * FIX_ONE; + center.x = center.y = center.z = 0; + r.q = r.x = r.z = 0; r.y = FIX_ONE; + /*this takes care of pixelMetrics*/ + visual_3d_viewpoint_change(tr_state, NULL, 0, fov, pos, r, center); + /*initial vp compute, don't animate*/ + if (tr_state->camera->had_viewpoint == 2) { + camera_stop_anim(tr_state->camera); + camera_reset_viewpoint(tr_state->camera, 0); + } + } else { + tr_state->camera->flags &= ~CAM_HAS_VIEWPORT; + tr_state->camera->flags |= CAM_IS_DIRTY; + } + tr_state->camera->had_viewpoint = 0; + } + + camera_update(tr_state->camera, &tr_state->transform, tr_state->visual->center_coords); + + /*setup projection/modelview*/ + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_PROJECTION); + visual_3d_matrix_load(tr_state->visual, tr_state->camera->projection.m); + visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_MODELVIEW); + visual_3d_matrix_load(tr_state->visual, tr_state->camera->modelview.m); + gf_mx_init(tr_state->model_matrix); + + tr_state->traversing_mode = mode; +} + +void visual_3d_init_draw(GF_TraverseState *tr_state, u32 layer_type) +{ + u32 mode; + GF_Node *bindable; + + /*if not in layer, traverse navigation node + FIXME: we should update the nav info according to the world transform at the current viewpoint (vrml)*/ + tr_state->traversing_mode = TRAVERSE_BINDABLE; + bindable = tr_state->navigations ? (GF_Node*) gf_list_get(tr_state->navigations, 0) : NULL; + if (Bindable_GetIsBound(bindable)) { + gf_node_traverse(bindable, tr_state); + tr_state->camera->had_nav_info = 1; + } else if (tr_state->camera->had_nav_info) { + /*if no navigation specified, use default VRML one*/ + tr_state->camera->avatar_size.x = FLT2FIX(0.25f); tr_state->camera->avatar_size.y = FLT2FIX(1.6f); tr_state->camera->avatar_size.z = FLT2FIX(0.75f); + tr_state->camera->visibility = 0; + tr_state->camera->speed = FIX_ONE; + /*not specified in the spec, but by default we forbid navigation in layer*/ + if (layer_type) { + tr_state->camera->navigation_flags = NAV_HEADLIGHT; + tr_state->camera->navigate_mode = GF_NAVIGATE_NONE; + } else { + tr_state->camera->navigation_flags = NAV_ANY | NAV_HEADLIGHT; + if (tr_state->camera->is_3D) { + /*X3D is by default examine, VRML/MPEG4 is WALK*/ + tr_state->camera->navigate_mode = (tr_state->visual->type_3d==3) ? GF_NAVIGATE_EXAMINE : GF_NAVIGATE_WALK; + } else { + tr_state->camera->navigate_mode = GF_NAVIGATE_NONE; + } + } + tr_state->camera->had_nav_info = 0; + + if (tr_state->pixel_metrics) { + tr_state->camera->visibility = gf_mulfix(tr_state->camera->visibility, tr_state->min_hsize); + tr_state->camera->avatar_size.x = gf_mulfix(tr_state->camera->avatar_size.x, tr_state->min_hsize); + tr_state->camera->avatar_size.y = gf_mulfix(tr_state->camera->avatar_size.y, tr_state->min_hsize); + tr_state->camera->avatar_size.z = gf_mulfix(tr_state->camera->avatar_size.z, tr_state->min_hsize); + } + } + + /*animate current camera - if returns TRUE draw next frame*/ + if (camera_animate(tr_state->camera)) + gf_sc_invalidate(tr_state->visual->compositor, NULL); + + visual_3d_set_viewport(tr_state->visual, tr_state->camera->vp); + + /*setup projection*/ + visual_3d_setup_projection(tr_state); + + /*turn off depth buffer in 2D*/ + visual_3d_enable_depth_buffer(tr_state->visual, tr_state->camera->is_3D); + + /*set headlight if any*/ + visual_3d_enable_headlight(tr_state->visual, (tr_state->camera->navigation_flags & NAV_HEADLIGHT) ? 1 : 0, tr_state->camera); + + /*setup background*/ + mode = tr_state->traversing_mode; + tr_state->traversing_mode = TRAVERSE_BINDABLE; + bindable = (GF_Node*) gf_list_get(tr_state->backgrounds, 0); + + /*if in layer clear z buffer (even if background)*/ + if (layer_type) visual_3d_clear_depth(tr_state->visual); + + /*clear requested - do it before background drawing for layer3D (transparent background)*/ + if (layer_type==2) { + SFColor col; + col.red = INT2FIX((tr_state->visual->compositor->back_color>>16)&0xFF) / 255; + col.green = INT2FIX((tr_state->visual->compositor->back_color>>8)&0xFF) / 255; + col.blue = INT2FIX((tr_state->visual->compositor->back_color)&0xFF) / 255; + visual_3d_clear(tr_state->visual, col, 0); + } + + if (Bindable_GetIsBound(bindable)) { + gf_node_traverse(bindable, tr_state); + } + /*clear if not in layer*/ + else if (!layer_type) { + SFColor col; + col.red = INT2FIX((tr_state->visual->compositor->back_color>>16)&0xFF) / 255; + col.green = INT2FIX((tr_state->visual->compositor->back_color>>8)&0xFF) / 255; + col.blue = INT2FIX((tr_state->visual->compositor->back_color)&0xFF) / 255; + /*if composite visual, clear with alpha = 0*/ + visual_3d_clear(tr_state->visual, col, (tr_state->visual==tr_state->visual->compositor->visual) ? FIX_ONE : 0); + } + tr_state->traversing_mode = mode; +} + + +static GFINLINE Bool visual_3d_has_alpha(GF_TraverseState *tr_state, GF_Node *geom) +{ + u32 tag; + Bool is_mat3D; + Drawable3D *stack; + GF_Node *mat = tr_state->appear ? ((M_Appearance *)tr_state->appear)->material : NULL; + + is_mat3D = 0; + if (mat) { + tag = gf_node_get_tag(mat); + switch (tag) { + /*for M2D: if filled & transparent we're transparent - otherwise we must check texture*/ + case TAG_MPEG4_Material2D: + if (((M_Material2D *)mat)->filled && ((M_Material2D *)mat)->transparency) return 1; + break; + case TAG_MPEG4_Material: + case TAG_X3D_Material: + is_mat3D = 1; + if ( ((M_Material *)mat)->transparency) return 1; + break; + case TAG_MPEG4_MaterialKey: + return 1; + break; + } + } else if (tr_state->camera->is_3D && tr_state->appear) { + GF_TextureHandler *txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture); + if (txh && txh->transparent) return 1; + } + + /*check alpha texture in3D or with bitmap*/ + if (is_mat3D || (gf_node_get_tag(geom)==TAG_MPEG4_Bitmap)) { + GF_TextureHandler *txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture); + if (txh && txh->transparent) return 1; + } + /*TODO - FIXME check alpha only...*/ + if (!tr_state->color_mat.identity) return 1; + + stack = (Drawable3D *)gf_node_get_private(geom); + if (stack && stack->mesh && (stack->mesh->flags & MESH_HAS_ALPHA)) return 1; + return 0; +} + +void visual_3d_register_context(GF_TraverseState *tr_state, GF_Node *geometry) +{ + u32 i, count; + DirectionalLightContext *nl, *ol; + Drawable3DContext *ctx; + Drawable3D *drawable; + + assert(tr_state->traversing_mode == TRAVERSE_SORT); + + drawable = (Drawable3D*)gf_node_get_private(geometry); + + /*if 2D draw in declared order. Otherwise, if no alpha or node is a layer, draw directly + if mesh is not setup yet, consider it as opaque*/ + if (!tr_state->camera->is_3D || !visual_3d_has_alpha(tr_state, geometry) || !drawable->mesh) { + tr_state->traversing_mode = TRAVERSE_DRAW_3D; + /*layout/form clipper, set it in world coords only*/ + if (tr_state->has_clip) { + visual_3d_matrix_push(tr_state->visual); + visual_3d_matrix_reset(tr_state->visual); + visual_3d_set_clipper_2d(tr_state->visual, tr_state->clipper); + visual_3d_matrix_pop(tr_state->visual); + } + + gf_node_traverse(geometry, tr_state); + + /*back to SORT*/ + tr_state->traversing_mode = TRAVERSE_SORT; + + if (tr_state->has_clip) visual_3d_reset_clipper_2d(tr_state->visual); + return; + } + + + GF_SAFEALLOC(ctx, Drawable3DContext); + ctx->directional_lights = gf_list_new(); + ctx->geometry = geometry; + ctx->appearance = tr_state->appear; + + memcpy(&ctx->model_matrix, &tr_state->model_matrix, sizeof(GF_Matrix)); + ctx->color_mat.identity = tr_state->color_mat.identity; + if (!tr_state->color_mat.identity) memcpy(&ctx->color_mat, &tr_state->color_mat, sizeof(GF_ColorMatrix)); + + ctx->pixel_metrics = tr_state->pixel_metrics; + ctx->text_split_idx = tr_state->text_split_idx; + + i=0; + while ((ol = (DirectionalLightContext*)gf_list_enum(tr_state->local_lights, &i))) { + nl = (DirectionalLightContext*)malloc(sizeof(DirectionalLightContext)); + memcpy(nl, ol, sizeof(DirectionalLightContext)); + gf_list_add(ctx->directional_lights, nl); + } + ctx->clipper = tr_state->clipper; + ctx->has_clipper = tr_state->has_clip; + ctx->cull_flag = tr_state->cull_flag; + + if ((ctx->num_clip_planes = tr_state->num_clip_planes)) + memcpy(ctx->clip_planes, tr_state->clip_planes, sizeof(GF_Plane)*MAX_USER_CLIP_PLANES); + + /*get bbox and and insert from further to closest*/ + tr_state->bbox = drawable->mesh->bounds; + + gf_mx_apply_bbox(&ctx->model_matrix, &tr_state->bbox); + gf_mx_apply_bbox(&tr_state->camera->modelview, &tr_state->bbox); + ctx->zmax = tr_state->bbox.max_edge.z; + + /*we don't need an exact sorting, as long as we keep transparent nodes above -note that for + speed purposes we store in reverse-z transparent nodes*/ + count = gf_list_count(tr_state->visual->alpha_nodes_to_draw); + for (i=0; ivisual->alpha_nodes_to_draw, i); + if (next->zmax>ctx->zmax) { + gf_list_insert(tr_state->visual->alpha_nodes_to_draw, ctx, i); + return; + } + } + gf_list_add(tr_state->visual->alpha_nodes_to_draw, ctx); +} + +void visual_3d_flush_contexts(GF_VisualManager *visual, GF_TraverseState *tr_state) +{ + u32 i, idx, count; + Bool pixel_metrics = tr_state->pixel_metrics; + + tr_state->traversing_mode = TRAVERSE_DRAW_3D; + + count = gf_list_count(visual->alpha_nodes_to_draw); + for (idx=0; idxalpha_nodes_to_draw, idx); + + visual_3d_matrix_push(visual); + + /*apply directional lights*/ + tr_state->local_light_on = 1; + i=0; + while ((dl = (DirectionalLightContext*)gf_list_enum(ctx->directional_lights, &i))) { + visual_3d_matrix_push(visual); + visual_3d_matrix_add(visual, dl->light_matrix.m); + gf_node_traverse(dl->dlight, tr_state); + visual_3d_matrix_pop(visual); + } + + /*clipper, set it in world coords only*/ + if (ctx->has_clipper) { + visual_3d_matrix_push(visual); + visual_3d_matrix_reset(visual); + visual_3d_set_clipper_2d(visual, ctx->clipper); + visual_3d_matrix_pop(visual); + } + + /*clip planes, set it in world coords only*/ + for (i=0; inum_clip_planes; i++) + visual_3d_set_clip_plane(visual, ctx->clip_planes[i]); + + /*restore traversing state*/ + visual_3d_matrix_add(visual, ctx->model_matrix.m); + memcpy(&tr_state->model_matrix, &ctx->model_matrix, sizeof(GF_Matrix)); + tr_state->color_mat.identity = ctx->color_mat.identity; + if (!tr_state->color_mat.identity) memcpy(&tr_state->color_mat, &ctx->color_mat, sizeof(GF_ColorMatrix)); + tr_state->text_split_idx = ctx->text_split_idx; + tr_state->pixel_metrics = ctx->pixel_metrics; + /*restore cull flag in case we're completely inside (avoids final frustum/AABB tree culling)*/ + tr_state->cull_flag = ctx->cull_flag; + + tr_state->appear = ctx->appearance; + gf_node_traverse(ctx->geometry, tr_state); + tr_state->appear = NULL; + + /*reset directional lights*/ + tr_state->local_light_on = 0; + for (i=gf_list_count(ctx->directional_lights); i>0; i--) { + DirectionalLightContext *dl = (DirectionalLightContext*)gf_list_get(ctx->directional_lights, i-1); + gf_node_traverse(dl->dlight, tr_state); + free(dl); + } + + if (ctx->has_clipper) visual_3d_reset_clipper_2d(visual); + for (i=0; inum_clip_planes; i++) visual_3d_reset_clip_plane(visual); + + visual_3d_matrix_pop(visual); + + /*and destroy*/ + gf_list_del(ctx->directional_lights); + free(ctx); + } + tr_state->pixel_metrics = pixel_metrics; + gf_list_reset(tr_state->visual->alpha_nodes_to_draw); +} +static void visual_3d_draw_node(GF_TraverseState *tr_state, GF_Node *root_node) +{ + GF_Node *fog; + if (!tr_state->camera || !tr_state->visual) return; + + visual_3d_init_draw(tr_state, 0); + + /*main visual, handle collisions*/ + if ((tr_state->visual==tr_state->visual->compositor->visual) && tr_state->camera->is_3D) + visual_3d_check_collisions(tr_state, NULL); + + /*setup fog*/ + fog = (GF_Node*) gf_list_get(tr_state->visual->fog_stack, 0); + tr_state->traversing_mode = TRAVERSE_BINDABLE; + if (Bindable_GetIsBound(fog)) gf_node_traverse(fog, tr_state); + + /*turn global lights on*/ + if (tr_state->visual->type_3d>1) { + tr_state->traversing_mode = TRAVERSE_LIGHTING; + gf_node_traverse(root_node, tr_state); + } + + /*sort graph*/ + tr_state->traversing_mode = TRAVERSE_SORT; + gf_node_traverse(root_node, tr_state); + + /*and draw*/ + visual_3d_flush_contexts(tr_state->visual, tr_state); + + /*and turn off lights*/ + visual_3d_clear_all_lights(tr_state->visual); +} + +Bool visual_3d_draw_frame(GF_VisualManager *visual, GF_Node *root, GF_TraverseState *tr_state, Bool is_root_visual) +{ +#ifndef GPAC_DISABLE_LOG + u32 time = gf_sys_clock(); +#endif + visual_3d_setup(visual); + + /*setup our traversing state*/ + visual_3d_setup_traversing_state(visual, tr_state); + + if (is_root_visual) { + GF_SceneGraph *sg; + u32 i; + visual_3d_draw_node(tr_state, root); + + /*extra scene graphs*/ + i=0; + while ((sg = (GF_SceneGraph*)gf_list_enum(visual->compositor->extra_scenes, &i))) { + tr_state->traversing_mode = TRAVERSE_SORT; + gf_sc_traverse_subscene(visual->compositor, root, sg, tr_state); + } + } else { + visual_3d_draw_node(tr_state, root); + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI, ("[RTI] Frame\t%d\t3D drawn in \t%d\tms\n", visual->compositor->frame_number, gf_sys_clock() - time)); + + return 1; +} + +static void reset_collide_cursor(GF_Compositor *compositor) +{ + if (compositor->sensor_type == GF_CURSOR_COLLIDE) { + GF_Event evt; + compositor->sensor_type = evt.cursor.cursor_type = GF_CURSOR_NORMAL; + evt.type = GF_EVENT_SET_CURSOR; + compositor->video_out->ProcessEvent(compositor->video_out, &evt); + } +} + +void visual_3d_check_collisions(GF_TraverseState *tr_state, GF_ChildNodeItem *node_list) +{ + SFVec3f n, pos, dir; + Bool go; + Fixed diff, pos_diff; + + assert(tr_state->visual && tr_state->camera); + /*don't collide on VP animations or when modes discard collision*/ + if ((tr_state->camera->anim_len && !tr_state->camera->jumping) || !tr_state->visual->compositor->collide_mode || (tr_state->camera->navigate_mode>=GF_NAVIGATE_EXAMINE)) { + /*reset ground flag*/ + tr_state->camera->last_had_ground = 0; + /*and avoid reseting move at next collision change*/ + tr_state->camera->last_pos = tr_state->camera->position; + return; + } + /*don't collide if not moved*/ + if (gf_vec_equal(tr_state->camera->position, tr_state->camera->last_pos)) { + reset_collide_cursor(tr_state->visual->compositor); + return; + } + tr_state->traversing_mode = TRAVERSE_COLLIDE; + tr_state->camera->collide_flags = 0; + tr_state->camera->collide_dist = FIX_MAX; + tr_state->camera->ground_dist = FIX_MAX; + if ((tr_state->camera->navigate_mode==GF_NAVIGATE_WALK) && tr_state->visual->compositor->gravity_on) tr_state->camera->collide_flags |= CF_DO_GRAVITY; + + gf_vec_diff(dir, tr_state->camera->position, tr_state->camera->last_pos); + pos_diff = gf_vec_len(dir); + gf_vec_norm(&dir); + pos = tr_state->camera->position; + + diff = 0; + go = 1; + tr_state->camera->last_had_col = 0; + /*some explanation: the current collision detection algo only checks closest distance + to objects, but doesn't attempt to track object cross during a move. If we step more than + the collision detection size, we may cross an object without detecting collision. we thus + break the move in max collision size moves*/ + while (go) { + if (pos_diff>tr_state->camera->avatar_size.x) { + pos_diff-=tr_state->camera->avatar_size.x; + diff += tr_state->camera->avatar_size.x; + } else { + diff += pos_diff; + go = 0; + } + n = gf_vec_scale(dir, diff); + gf_vec_add(tr_state->camera->position, tr_state->camera->last_pos, n); + if (!node_list) { + gf_node_traverse(gf_sg_get_root_node(tr_state->visual->compositor->scene), tr_state); + } else { + while (node_list) { + gf_node_traverse(node_list->node, tr_state); + node_list = node_list->next; + } + } + if (tr_state->camera->collide_flags & CF_COLLISION) break; + tr_state->camera->collide_flags &= ~CF_DO_GRAVITY; + } + + /*gravity*/ + if (tr_state->camera->collide_flags & CF_GRAVITY) { + diff = tr_state->camera->ground_dist - tr_state->camera->avatar_size.y; + if (tr_state->camera->last_had_ground && (-diff>tr_state->camera->avatar_size.z)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Obstacle detected - too high (dist %g)\n", FIX2FLT(diff))); + tr_state->camera->position = tr_state->camera->last_pos; + tr_state->camera->flags |= CAM_IS_DIRTY; + } else { + if ((tr_state->camera->jumping && fabs(diff)>tr_state->camera->dheight) + || (!tr_state->camera->jumping && (ABS(diff)>FIX_ONE/1000) )) { + tr_state->camera->last_had_ground = 1; + n = gf_vec_scale(tr_state->camera->up, -diff); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Ground detected camera position: %g %g %g - offset: %g %g %g (dist %g)\n", + FIX2FLT(tr_state->camera->position.x), FIX2FLT(tr_state->camera->position.y), FIX2FLT(tr_state->camera->position.z), + FIX2FLT(n.x), FIX2FLT(n.y), FIX2FLT(n.z), FIX2FLT(diff))); + + gf_vec_add(tr_state->camera->position, tr_state->camera->position, n); + gf_vec_add(tr_state->camera->target, tr_state->camera->target, n); + gf_vec_add(tr_state->camera->last_pos, tr_state->camera->position, n); + tr_state->camera->flags |= CAM_IS_DIRTY; + } + } + } + /*collsion found*/ + if (tr_state->camera->collide_flags & CF_COLLISION) { + if (tr_state->visual->compositor->sensor_type != GF_CURSOR_COLLIDE) { + GF_Event evt; + tr_state->camera->last_had_col = 1; + evt.type = GF_EVENT_SET_CURSOR; + tr_state->visual->compositor->sensor_type = evt.cursor.cursor_type = GF_CURSOR_COLLIDE; + tr_state->visual->compositor->video_out->ProcessEvent(tr_state->visual->compositor->video_out, &evt); + } + + /*regular collision*/ + if (tr_state->visual->compositor->collide_mode==GF_COLLISION_NORMAL) { + tr_state->camera->position = tr_state->camera->last_pos; + tr_state->camera->flags |= CAM_IS_DIRTY; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Collision detected - restoring previous avatar position\n")); + } else { + /*camera displacement collision*/ + if (tr_state->camera->collide_dist) { + if (tr_state->camera->collide_dist>=tr_state->camera->avatar_size.x) + GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Collision] Collision distance %g greater than avatar collide size %g\n", FIX2FLT(tr_state->camera->collide_dist), FIX2FLT(tr_state->camera->avatar_size.x))); + + /*safety check due to precision, always stay below collide dist*/ + if (tr_state->camera->collide_dist>=tr_state->camera->avatar_size.x) tr_state->camera->collide_dist = tr_state->camera->avatar_size.x; + + gf_vec_diff(n, tr_state->camera->position, tr_state->camera->collide_point); + gf_vec_norm(&n); + n = gf_vec_scale(n, tr_state->camera->avatar_size.x - tr_state->camera->collide_dist); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] offseting camera: position: %g %g %g - offset: %g %g %g\n", + FIX2FLT(tr_state->camera->position.x), FIX2FLT(tr_state->camera->position.y), FIX2FLT(tr_state->camera->position.z), + FIX2FLT(n.x), FIX2FLT(n.y), FIX2FLT(n.z))); + + gf_vec_add(tr_state->camera->position, tr_state->camera->position, n); + gf_vec_add(tr_state->camera->target, tr_state->camera->target, n); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Collision detected and camera on hit point - restoring previous avatar position\n")); + tr_state->camera->position = tr_state->camera->last_pos; + } + tr_state->camera->last_pos = tr_state->camera->position; + tr_state->camera->flags |= CAM_IS_DIRTY; + } + } else { + reset_collide_cursor(tr_state->visual->compositor); + tr_state->camera->last_pos = tr_state->camera->position; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] no collision found\n")); + } + + if (tr_state->camera->flags & CAM_IS_DIRTY) visual_3d_setup_projection(tr_state); +} + +/*uncomment to disable frustum cull*/ +//#define DISABLE_VIEW_CULL + +#ifndef GPAC_DISABLE_LOG +static const char *szPlaneNames [] = +{ + "Near", "Far", "Left", "Right", "Bottom", "Top" +}; +#endif + +Bool visual_3d_node_cull(GF_TraverseState *tr_state, GF_BBox *bbox, Bool skip_near) +{ +#ifdef DISABLE_VIEW_CULL + tr_state->cull_flag = CULL_INSIDE; + return 1; +#else + GF_BBox b; + Fixed irad, rad; + GF_Camera *cam; + Bool do_sphere; + u32 i, p_idx; + SFVec3f cdiff, vertices[8]; + + if (!tr_state->camera || (tr_state->cull_flag == CULL_INSIDE)) return 1; + assert(tr_state->cull_flag != CULL_OUTSIDE); + + /*empty bounds*/ + if (!bbox->is_set) { + tr_state->cull_flag = CULL_OUTSIDE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node out (bbox not set)\n")); + return 0; + } + + /*get bbox sphere in world space*/ + b = *bbox; + gf_mx_apply_bbox_sphere(&tr_state->model_matrix, &b); + cam = tr_state->camera; + + /*if camera is inside bbox consider we intersect*/ + if (gf_bbox_point_inside(&b, &cam->position)) { + tr_state->cull_flag = CULL_INTERSECTS; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node intersect (camera in box test)\n")); + return 1; + } + /*first check: sphere vs frustum sphere intersection, this will discard far objects quite fast*/ + gf_vec_diff(cdiff, cam->center, b.center); + rad = b.radius + cam->radius; + if (gf_vec_len(cdiff) > rad) { + tr_state->cull_flag = CULL_OUTSIDE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node out (sphere-sphere test)\n")); + return 0; + } + + /*second check: sphere vs frustum planes intersection, if any intersection is detected switch + to n/p vertex check.*/ + rad = b.radius; + irad = -b.radius; + do_sphere = 1; + + /*skip near/far tests in ortho mode, and near in 3D*/ + i = (tr_state->camera->is_3D) ? (skip_near ? 1 : 0) : 2; + for (; i<6; i++) { + Fixed d; + if (do_sphere) { + d = gf_plane_get_distance(&cam->planes[i], &b.center); + if (dcull_flag = CULL_OUTSIDE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node out (sphere-planes test) plane %s\n", szPlaneNames[i])); + return 0; + } + /*intersect, move to n-p vertex test*/ + if (dmodel_matrix, &b); + /*get box vertices*/ + gf_bbox_get_vertices(b.min_edge, b.max_edge, vertices); + do_sphere = 0; + } else { + continue; + } + } + p_idx = cam->p_idx[i]; + /*check p-vertex: if not in plane, we're out (since p-vertex is the closest point to the plane)*/ + d = gf_plane_get_distance(&cam->planes[i], &vertices[p_idx]); + if (d<0) { + tr_state->cull_flag = CULL_OUTSIDE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node out (p-vertex test) plane %s - Distance %g\n", szPlaneNames[i], FIX2FLT(d) )); + return 0; + } + + /*check n-vertex: if not in plane, we're intersecting - don't check for near and far planes*/ + if (i>1) { + d = gf_plane_get_distance(&cam->planes[i], &vertices[7-p_idx]); + if (d<0) { + tr_state->cull_flag = CULL_INTERSECTS; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node intersect (n-vertex test) plane %s - Distance %g\n", szPlaneNames[i], FIX2FLT(d) )); + return 1; + } + } + } + + tr_state->cull_flag = CULL_INSIDE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Culling] Node inside (%s test)\n", do_sphere ? "sphere-planes" : "n-p vertex")); + return 1; +#endif +} + +void visual_3d_pick_node(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children) +{ + Fixed in_x, in_y, x, y; + SFVec3f start, end; + SFVec4f res; + + visual_3d_setup_traversing_state(visual, tr_state); + visual_3d_setup_projection(tr_state); + + x = INT2FIX(ev->mouse.x); y = INT2FIX(ev->mouse.y); + + /*if coordinate system is not centered, move to centered coord before applying camera transform + because the (un)projection matrices include this transform*/ + if (!visual->center_coords) { + x = x - INT2FIX(tr_state->camera->width)/2; + y = INT2FIX(tr_state->camera->height)/2 - y; + } + + + /*main visual with AR*/ + if ((visual->compositor->visual == visual) && visual->compositor->has_size_info) { + Fixed scale = gf_divfix(INT2FIX(visual->width), INT2FIX(visual->compositor->vp_width)); + x = gf_mulfix(x, scale); + scale = gf_divfix(INT2FIX(visual->height), INT2FIX(visual->compositor->vp_height)); + y = gf_mulfix(y, scale); + } + + start.z = visual->camera.z_near; + end.z = visual->camera.z_far; + if (!tr_state->camera->is_3D && !tr_state->pixel_metrics) { + start.x = end.x = gf_divfix(x, tr_state->min_hsize); + start.y = end.y = gf_divfix(y, tr_state->min_hsize); + } else { + start.x = end.x = x; + start.y = end.y = y; + } + + /*unproject to world coords*/ + in_x = 2*x/ (s32) visual->width; + in_y = 2*y/ (s32) visual->height; + + res.x = in_x; res.y = in_y; res.z = -FIX_ONE; res.q = FIX_ONE; + gf_mx_apply_vec_4x4(&visual->camera.unprojection, &res); + if (!res.q) return; + start.x = gf_divfix(res.x, res.q); start.y = gf_divfix(res.y, res.q); start.z = gf_divfix(res.z, res.q); + + res.x = in_x; res.y = in_y; res.z = FIX_ONE; res.q = FIX_ONE; + gf_mx_apply_vec_4x4(&visual->camera.unprojection, &res); + if (!res.q) return; + end.x = gf_divfix(res.x, res.q); end.y = gf_divfix(res.y, res.q); end.z = gf_divfix(res.z, res.q); + + tr_state->ray = gf_ray(start, end); + /*also update hit info world ray in case we have a grabbed sensor with mouse off*/ + visual->compositor->hit_world_ray = tr_state->ray; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Picking] cast ray\n\tOrigin %.4f %.4f %.4f - End %.4f %.4f %.4f\n\tDir %.4f %.4f %.4f\n", + FIX2FLT(tr_state->ray.orig.x), FIX2FLT(tr_state->ray.orig.y), FIX2FLT(tr_state->ray.orig.z), + FIX2FLT(end.x), FIX2FLT(end.y), FIX2FLT(end.z), + FIX2FLT(tr_state->ray.dir.x), FIX2FLT(tr_state->ray.dir.y), FIX2FLT(tr_state->ray.dir.z))); + + + + visual->compositor->hit_square_dist = 0; + visual->compositor->hit_node = NULL; + gf_list_reset(visual->compositor->sensors); + + /*not the root scene, use children list*/ + if (visual->compositor->visual != visual) { + while (children) { + gf_node_traverse(children->node, tr_state); + children = children->next; + } + } else { + gf_node_traverse(gf_sg_get_root_node(visual->compositor->scene), tr_state); + } +} + + +void visual_3d_drawable_pick(GF_Node *n, GF_TraverseState *tr_state, GF_Mesh *mesh, GF_Path *path) +{ + SFVec3f local_pt, world_pt, vdiff; + SFVec3f hit_normal; + SFVec2f text_coords; + u32 i, count; + Fixed sqdist; + Bool node_is_over; + GF_Compositor *compositor; + GF_Matrix mx; + GF_Ray r; + u32 cull_bckup = tr_state->cull_flag; + + if (!mesh && !path) return; + + count = gf_list_count(tr_state->vrml_sensors); + compositor = tr_state->visual->compositor; + + node_is_over = 0; + if (mesh) { + if (mesh->mesh_type!=MESH_TRIANGLES) + return; + if (!visual_3d_node_cull(tr_state, &mesh->bounds, 0)) { + tr_state->cull_flag = cull_bckup; + return; + } + } + tr_state->cull_flag = cull_bckup; + r = tr_state->ray; + gf_mx_copy(mx, tr_state->model_matrix); + gf_mx_inverse(&mx); + gf_mx_apply_ray(&mx, &r); + + /*if we already have a hit point don't check anything below...*/ + if (compositor->hit_square_dist && !compositor->grabbed_sensor && !tr_state->layer3d) { + GF_Plane p; + GF_BBox box; + SFVec3f hit = compositor->hit_world_point; + gf_mx_apply_vec(&mx, &hit); + p.normal = r.dir; + p.d = -1 * gf_vec_dot(p.normal, hit); + if (mesh) + box = mesh->bounds; + else + gf_bbox_from_rect(&box, &path->bbox); + + if (gf_bbox_plane_relation(&box, &p) == GF_BBOX_FRONT) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Picking] bounding box of node %s (DEF %s) below current hit point - skipping\n", gf_node_get_class_name(n), gf_node_get_name(n))); + return; + } + } + if (path) { + node_is_over = 0; + if (compositor_get_2d_plane_intersection(&r, &local_pt)) { + if (gf_path_point_over(path, local_pt.x, local_pt.y)) { + hit_normal.x = hit_normal.y = 0; hit_normal.z = FIX_ONE; + text_coords.x = gf_divfix(local_pt.x, path->bbox.width) + FIX_ONE/2; + text_coords.y = gf_divfix(local_pt.y, path->bbox.height) + FIX_ONE/2; + node_is_over = 1; + } + } + } else { + node_is_over = gf_mesh_intersect_ray(mesh, &r, &local_pt, &hit_normal, &text_coords); + } + + if (!node_is_over) return; + + /*check distance from user and keep the closest hitpoint*/ + world_pt = local_pt; + gf_mx_apply_vec(&tr_state->model_matrix, &world_pt); + + for (i=0; inum_clip_planes; i++) { + if (gf_plane_get_distance(&tr_state->clip_planes[i], &world_pt) < 0) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Picking] node %s (def %s) is not in clipper half space\n", gf_node_get_class_name(n), gf_node_get_name(n))); + return; + } + } + + gf_vec_diff(vdiff, world_pt, tr_state->ray.orig); + sqdist = gf_vec_lensq(vdiff); + if (compositor->hit_square_dist && (compositor->hit_square_dist+FIX_EPSILONhit_square_dist))); + return; + } + + compositor->hit_square_dist = sqdist; + gf_list_reset(compositor->sensors); + for (i=0; isensors, gf_list_get(tr_state->vrml_sensors, i)); + } + + gf_mx_copy(compositor->hit_world_to_local, tr_state->model_matrix); + gf_mx_copy(compositor->hit_local_to_world, mx); + compositor->hit_local_point = local_pt; + compositor->hit_world_point = world_pt; + compositor->hit_world_ray = tr_state->ray; + compositor->hit_normal = hit_normal; + compositor->hit_texcoords = text_coords; + + if (compositor_is_composite_texture(tr_state->appear)) { + compositor->hit_appear = tr_state->appear; + } else { + compositor->hit_appear = NULL; + } + compositor->hit_node = n; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Picking] node %s (def %s) is under mouse - hit %g %g %g\n", gf_node_get_class_name(n), gf_node_get_name(n), + FIX2FLT(world_pt.x), FIX2FLT(world_pt.y), FIX2FLT(world_pt.z))); +} + + +void visual_3d_drawable_collide(GF_Node *node, GF_TraverseState *tr_state) +{ + SFVec3f pos, v1, v2, collide_pt, last_pos; + Fixed dist, m_dist; + GF_Matrix mx; + u32 ntag, cull_backup; + Drawable3D *st = (Drawable3D *)gf_node_get_private(node); + if (!st || !st->mesh) return; + + /*no collision with lines & points*/ + if (st->mesh->mesh_type != MESH_TRIANGLES) return; + /*no collision with text (vrml)*/ + ntag = gf_node_get_tag(node); + if ((ntag==TAG_MPEG4_Text) || (ntag==TAG_X3D_Text)) return; + + + /*cull but don't use near plane to detect objects behind us*/ + cull_backup = tr_state->cull_flag; + if (!visual_3d_node_cull(tr_state, &st->mesh->bounds, 1)) { + tr_state->cull_flag = cull_backup; + return; + } + tr_state->cull_flag = cull_backup; + + /*use up & front to get an average size of the collision dist in this space*/ + pos = tr_state->camera->position; + last_pos = tr_state->camera->last_pos; + v1 = camera_get_target_dir(tr_state->camera); + v1 = gf_vec_scale(v1, tr_state->camera->avatar_size.x); + gf_vec_add(v1, v1, pos); + v2 = camera_get_right_dir(tr_state->camera); + v2 = gf_vec_scale(v2, tr_state->camera->avatar_size.x); + gf_vec_add(v2, v2, pos); + + gf_mx_copy(mx, tr_state->model_matrix); + gf_mx_inverse(&mx); + + gf_mx_apply_vec(&mx, &pos); + gf_mx_apply_vec(&mx, &last_pos); + gf_mx_apply_vec(&mx, &v1); + gf_mx_apply_vec(&mx, &v2); + + gf_vec_diff(v1, v1, pos); + gf_vec_diff(v2, v2, pos); + dist = gf_vec_len(v1); + m_dist = gf_vec_len(v2); + if (distmesh, pos, m_dist, &collide_pt)) { + /*get transformed hit*/ + gf_mx_apply_vec(&tr_state->model_matrix, &collide_pt); + gf_vec_diff(v2, tr_state->camera->position, collide_pt); + dist = gf_vec_len(v2); + if (distcamera->collide_dist) { + tr_state->camera->collide_dist = dist; + tr_state->camera->collide_flags |= CF_COLLISION; + tr_state->camera->collide_point = collide_pt; + +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_COMPOSE)) { + gf_vec_diff(v1, pos, collide_pt); + gf_vec_norm(&v1); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] found at %g %g %g (WC) - dist (%g) - local normal %g %g %g\n", + FIX2FLT(tr_state->camera->collide_point.x), FIX2FLT(tr_state->camera->collide_point.y), FIX2FLT(tr_state->camera->collide_point.z), + FIX2FLT(dist), + FIX2FLT(v1.x), FIX2FLT(v1.y), FIX2FLT(v1.z))); + } +#endif + } + else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Existing collision (dist %g) closer than current collsion (dist %g)\n", FIX2FLT(tr_state->camera->collide_dist), FIX2FLT(dist) )); + } + } + + if (tr_state->camera->collide_flags & CF_DO_GRAVITY) { + GF_Ray r; + Bool intersect; + r.orig = tr_state->camera->position; + r.dir = gf_vec_scale(tr_state->camera->up, -FIX_ONE); + gf_mx_apply_ray(&mx, &r); + + intersect = gf_mesh_intersect_ray(st->mesh, &r, &collide_pt, &v1, NULL); + + if (intersect) { + gf_mx_apply_vec(&tr_state->model_matrix, &collide_pt); + gf_vec_diff(v2, tr_state->camera->position, collide_pt); + dist = gf_vec_len(v2); + if (distcamera->ground_dist) { + tr_state->camera->ground_dist = dist; + tr_state->camera->collide_flags |= CF_GRAVITY; + tr_state->camera->ground_point = collide_pt; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Ground found at %g %g %g (WC) - dist %g - local normal %g %g %g\n", + FIX2FLT(tr_state->camera->ground_point.x), FIX2FLT(tr_state->camera->ground_point.y), FIX2FLT(tr_state->camera->ground_point.z), + FIX2FLT(dist), + FIX2FLT(v1.x), FIX2FLT(v1.y), FIX2FLT(v1.z))); + } + else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] Existing ground (dist %g) closer than current (dist %g)\n", FIX2FLT(tr_state->camera->ground_dist), FIX2FLT(dist))); + } + } + } +} + +static GF_TextureHandler *visual_3d_setup_texture_2d(GF_TraverseState *tr_state, DrawAspect2D *asp, Bool is_svg, GF_Mesh *mesh) +{ + if (!asp->fill_texture) return NULL; + + if (asp->fill_color && (GF_COL_A(asp->fill_color) != 0xFF)) { + visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color); + gf_sc_texture_set_blend_mode(asp->fill_texture, TX_MODULATE); + } else { + visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 0); + gf_sc_texture_set_blend_mode(asp->fill_texture, TX_REPLACE); + } + + if (is_svg) { + GF_Rect rc; + gf_rect_from_bbox(&rc, &mesh->bounds); + tr_state->mesh_num_textures = gf_sc_texture_enable_ex(asp->fill_texture, NULL, &rc); + } else { + tr_state->mesh_num_textures = gf_sc_texture_enable(asp->fill_texture, ((M_Appearance *)tr_state->appear)->textureTransform); + } + if (tr_state->mesh_num_textures) return asp->fill_texture; + return NULL; +} + +void visual_3d_set_2d_strike(GF_TraverseState *tr_state, DrawAspect2D *asp) +{ + if (asp->line_texture) { + /*We forgot to specify this in the spec ...*/ + gf_sc_texture_set_blend_mode(asp->line_texture, TX_REPLACE); +#if 0 + if (asp->line_alpha != FIX_ONE) { + visual_3d_set_material_2d(tr_state->visual, asp->line_color, asp->line_alpha); + gf_sc_texture_set_blend_mode(asp->txh, TX_MODULATE); + } else { + visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 0); + gf_sc_texture_set_blend_mode(asp->txh, TX_REPLACE); + } +#endif + tr_state->mesh_num_textures = gf_sc_texture_enable(asp->line_texture, NULL/*asp->tx_trans*/); + if (tr_state->mesh_num_textures) return; + } + /*no texture or not ready, use color*/ + if (asp->line_color) + visual_3d_set_material_2d_argb(tr_state->visual, asp->line_color); +} + + +void visual_3d_draw_2d_with_aspect(Drawable *st, GF_TraverseState *tr_state, DrawAspect2D *asp, Bool is_svg) +{ + StrikeInfo2D *si; + GF_TextureHandler *fill_txh; + + fill_txh = visual_3d_setup_texture_2d(tr_state, asp, is_svg, st->mesh); + + /*fill path*/ + if (fill_txh || (GF_COL_A(asp->fill_color)) ) { + if (!st->mesh) return; + + if (asp->fill_color) + visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color); + + visual_3d_mesh_paint(tr_state, st->mesh); + /*reset texturing in case of line texture*/ + if (tr_state->mesh_num_textures) { + gf_sc_texture_disable(fill_txh); + tr_state->mesh_num_textures = 0; + } + } + + /*strike path*/ + if (!asp->pen_props.width) return; + + si = drawable_get_strikeinfo(tr_state->visual->compositor, st, asp, tr_state->appear, NULL, 0, tr_state); + if (!si) return; + + if (!si->mesh_outline) { + si->is_vectorial = !tr_state->visual->compositor->raster_outlines; + si->mesh_outline = new_mesh(); +#ifndef GPAC_USE_OGL_ES + if (si->is_vectorial) { + TesselatePath(si->mesh_outline, si->outline, asp->line_texture ? 2 : 1); + } else +#endif + mesh_get_outline(si->mesh_outline, st->path); + } + + visual_3d_set_2d_strike(tr_state, asp); + if (asp->line_texture) tr_state->mesh_num_textures = 1; + + if (!si->is_vectorial) { + visual_3d_mesh_strike(tr_state, si->mesh_outline, asp->pen_props.width, asp->line_scale, asp->pen_props.dash); + } else { + visual_3d_mesh_paint(tr_state, si->mesh_outline); + } + if (asp->line_texture) { + gf_sc_texture_disable(asp->line_texture); + tr_state->mesh_num_textures = 0; + } +} + +void visual_3d_draw_2d(Drawable *st, GF_TraverseState *tr_state) +{ + DrawAspect2D asp; + memset(&asp, 0, sizeof(DrawAspect2D)); + drawable_get_aspect_2d_mpeg4(st->node, &asp, tr_state); + visual_3d_draw_2d_with_aspect(st, tr_state, &asp, 0); +} + +void visual_3d_draw_from_context(DrawableContext *ctx, GF_TraverseState *tr_state) +{ + GF_Rect rc; + gf_path_get_bounds(ctx->drawable->path, &rc); + visual_3d_draw_2d_with_aspect(ctx->drawable, tr_state, &ctx->aspect, 1); + + drawable_check_focus_highlight(ctx->drawable->node, tr_state, &rc); +} + + +static GFINLINE Bool visual_3d_setup_material(GF_TraverseState *tr_state, u32 mesh_type, Fixed *diffuse_alpha) +{ + SFColor def; + GF_Node *__mat; + def.red = def.green = def.blue = FIX_ONE; + /*store diffuse alpha*/ + if (diffuse_alpha) *diffuse_alpha = FIX_ONE; + + if (!tr_state->appear) { + /*use material2D to disable lighting*/ + visual_3d_set_material_2d(tr_state->visual, def, FIX_ONE); + return 1; + } + + if (gf_node_get_tag(tr_state->appear)==TAG_X3D_Appearance) { + X_FillProperties *fp = (X_FillProperties *) ((X_Appearance*)tr_state->appear)->fillProperties; + if (fp && !fp->filled) return 0; + } + + __mat = ((M_Appearance *)tr_state->appear)->material; + if (!__mat) { + /*use material2D to disable lighting (cf VRML specs)*/ + visual_3d_set_material_2d(tr_state->visual, def, FIX_ONE); + return 1; + } + + switch (gf_node_get_tag((GF_Node *)__mat)) { + case TAG_MPEG4_Material: + case TAG_X3D_Material: + { + SFColor diff, spec, emi; + Fixed diff_a, spec_a, emi_a; + Fixed vec[4]; + Bool has_alpha; + u32 flag = V3D_STATE_LIGHT /*| V3D_STATE_COLOR*/; + M_Material *mat = (M_Material *)__mat; + + diff = mat->diffuseColor; + diff_a = FIX_ONE - mat->transparency; + + /*if drawing in 2D context or special meshes (lines, points) disable lighting*/ + if (mesh_type || !tr_state->camera->is_3D) { + if (tr_state->camera->is_3D) diff = mat->emissiveColor; + if (!tr_state->color_mat.identity) gf_cmx_apply_fixed(&tr_state->color_mat, &diff_a, &diff.red, &diff.green, &diff.blue); + visual_3d_set_material_2d(tr_state->visual, diff, diff_a); + return 1; + } + + spec = mat->specularColor; + emi = mat->emissiveColor; + spec_a = emi_a = FIX_ONE - mat->transparency; + if (!tr_state->color_mat.identity) { + gf_cmx_apply_fixed(&tr_state->color_mat, &diff_a, &diff.red, &diff.green, &diff.blue); + gf_cmx_apply_fixed(&tr_state->color_mat, &spec_a, &spec.red, &spec.green, &spec.blue); + gf_cmx_apply_fixed(&tr_state->color_mat, &emi_a, &emi.red, &emi.green, &emi.blue); + + if ((diff_a+FIX_EPSILONtransparency>FIX_EPSILON) ? 1 : 0; + /*100% transparent DON'T DRAW*/ + if (mat->transparency+FIX_EPSILON>=FIX_ONE) return 0; + } + + /*using antialiasing with alpha usually gives bad results (non-edge face segments are visible)*/ + visual_3d_enable_antialias(tr_state->visual, !has_alpha); + if (has_alpha) { + flag |= V3D_STATE_BLEND; + tr_state->mesh_is_transparent = 1; + } + visual_3d_set_state(tr_state->visual, flag, 1); + + vec[0] = gf_mulfix(diff.red, mat->ambientIntensity); + vec[1] = gf_mulfix(diff.green, mat->ambientIntensity); + vec[2] = gf_mulfix(diff.blue, mat->ambientIntensity); + vec[3] = diff_a; + visual_3d_set_material(tr_state->visual, V3D_MATERIAL_AMBIENT, vec); + + vec[0] = diff.red; + vec[1] = diff.green; + vec[2] = diff.blue; + vec[3] = diff_a; + visual_3d_set_material(tr_state->visual, V3D_MATERIAL_DIFFUSE, vec); + + vec[0] = spec.red; + vec[1] = spec.green; + vec[2] = spec.blue; + vec[3] = spec_a; + visual_3d_set_material(tr_state->visual, V3D_MATERIAL_SPECULAR, vec); + + + vec[0] = emi.red; + vec[1] = emi.green; + vec[2] = emi.blue; + vec[3] = emi_a; + visual_3d_set_material(tr_state->visual, V3D_MATERIAL_EMISSIVE, vec); + + visual_3d_set_shininess(tr_state->visual, mat->shininess); + if (diffuse_alpha) *diffuse_alpha = diff_a; + } + break; + case TAG_MPEG4_Material2D: + { + SFColor emi; + Fixed emi_a; + M_Material2D *mat = (M_Material2D *)__mat; + + emi = mat->emissiveColor; + emi_a = FIX_ONE - mat->transparency; + if (!tr_state->color_mat.identity) gf_cmx_apply_fixed(&tr_state->color_mat, &emi_a, &emi.red, &emi.green, &emi.blue); + /*100% transparent DON'T DRAW*/ + if (emi_avisual, V3D_STATE_BLEND, 1); + + + /*this is an extra feature: if material2D.filled is FALSE on 3D objects, switch to TX_REPLACE mode + and enable lighting*/ + if (!mat->filled) { + GF_TextureHandler *txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture); + if (txh) { + gf_sc_texture_set_blend_mode(txh, TX_REPLACE); + visual_3d_set_state(tr_state->visual, V3D_STATE_COLOR, 0); + visual_3d_set_state(tr_state->visual, V3D_STATE_LIGHT, 1); + return 1; + } + } + /*regular mat 2D*/ + visual_3d_set_state(tr_state->visual, V3D_STATE_LIGHT | V3D_STATE_COLOR, 0); + visual_3d_set_material_2d(tr_state->visual, emi, emi_a); + } + break; + default: + break; + } + return 1; +} + +Bool visual_3d_setup_texture(GF_TraverseState *tr_state, Fixed diffuse_alpha) +{ + GF_TextureHandler *txh; + tr_state->mesh_num_textures = 0; + if (!tr_state->appear) return 0; + + gf_node_dirty_reset(tr_state->appear); + + txh = gf_sc_texture_get_handler(((M_Appearance *)tr_state->appear)->texture); + if (txh) { + gf_sc_texture_set_blend_mode(txh, gf_sc_texture_is_transparent(txh) ? TX_MODULATE : TX_REPLACE); + tr_state->mesh_num_textures = gf_sc_texture_enable(txh, ((M_Appearance *)tr_state->appear)->textureTransform); + if (tr_state->mesh_num_textures) { + Fixed v[4]; + switch (txh->pixelformat) { + /*override diffuse color with full intensity, but keep material alpha (cf VRML lighting)*/ + case GF_PIXEL_RGB_24: + v[0] = v[1] = v[2] = FIX_ONE; v[3] = diffuse_alpha; + visual_3d_set_material(tr_state->visual, V3D_MATERIAL_DIFFUSE, v); + break; + /*override diffuse color AND material alpha (cf VRML lighting)*/ + case GF_PIXEL_RGBA: + v[0] = v[1] = v[2] = v[3] = FIX_ONE; + visual_3d_set_material(tr_state->visual, V3D_MATERIAL_DIFFUSE, v); + tr_state->mesh_is_transparent = 1; + break; +/* case GF_PIXEL_GREYSCALE: + tr_state->mesh_num_textures = 2; + break; +*/ + } + } + return tr_state->mesh_num_textures; + } + return 0; +} + +void visual_3d_disable_texture(GF_TraverseState *tr_state) +{ + if (tr_state->mesh_num_textures) { + gf_sc_texture_disable(gf_sc_texture_get_handler( ((M_Appearance *)tr_state->appear)->texture) ); + tr_state->mesh_num_textures = 0; + } +} + +Bool visual_3d_setup_appearance(GF_TraverseState *tr_state) +{ + Fixed diff_a; + /*setup material and check if 100% transparent - in which case don't draw*/ + if (!visual_3d_setup_material(tr_state, 0, &diff_a)) return 0; + /*setup texture*/ + visual_3d_setup_texture(tr_state, diff_a); + return 1; +} + + +void visual_3d_draw(GF_TraverseState *tr_state, GF_Mesh *mesh) +{ + if (mesh->mesh_type) { + if (visual_3d_setup_material(tr_state, mesh->mesh_type, NULL)) { + visual_3d_mesh_paint(tr_state, mesh); + } + } else if (visual_3d_setup_appearance(tr_state)) { + visual_3d_mesh_paint(tr_state, mesh); + visual_3d_disable_texture(tr_state); + +#if !defined(GPAC_USE_OGL_ES) && !defined(GPAC_USE_TINYGL) + if (tr_state->appear && gf_node_get_tag(tr_state->appear)==TAG_X3D_Appearance) { + X_Appearance *ap = (X_Appearance *)tr_state->appear; + X_FillProperties *fp = ap->fillProperties ? (X_FillProperties *) ap->fillProperties : NULL; + if (fp && fp->hatched) visual_3d_mesh_hatch(tr_state, mesh, fp->hatchStyle, fp->hatchColor); + } +#endif + } +} + + + +#endif diff --git a/src/compositor/visual_manager_3d.h b/src/compositor/visual_manager_3d.h new file mode 100644 index 0000000..97ee1d5 --- /dev/null +++ b/src/compositor/visual_manager_3d.h @@ -0,0 +1,287 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _VISUAL_MANAGER_3D_ +#define _VISUAL_MANAGER_3D_ + +#include + +#ifndef GPAC_DISABLE_3D + + /* + * Visual 3D functions + */ + +/*draw frame, performing collisions, camera displacement and drawing*/ +Bool visual_3d_draw_frame(GF_VisualManager *visual, GF_Node *root, GF_TraverseState *tr_state, Bool is_root_visual); + +/*traverse the scene and picks the node under the current ray, if any*/ +void visual_3d_pick_node(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children); + +/*checks a bounding box against the visual frustum. Returns true if box is visible, false otherwise. +The cull_flag of the traversing state is updated to the box/frustum relation (in/out/intersect)*/ +Bool visual_3d_node_cull(GF_TraverseState *tr_state, GF_BBox *bbox, Bool skip_near); + +/*modify a viewpoint*/ +void visual_3d_viewpoint_change(GF_TraverseState *tr_state, GF_Node *vp, Bool animate_change, Fixed fieldOfView, SFVec3f position, SFRotation orientation, SFVec3f local_center); + + +/*checks if a 3D mesh or a 2D path is under the current ray. Updates hit info if so.*/ +void visual_3d_drawable_pick(GF_Node *n, GF_TraverseState *tr_state, GF_Mesh *mesh, GF_Path *path) ; +/*performs collision on the given node (2D or 3D object)*/ +void visual_3d_drawable_collide(GF_Node *node, GF_TraverseState *tr_state); + +/*register a geomery node for drawing. If the node is transparent, stacks it for later draw, otherwise draw it directly*/ +void visual_3d_register_context(GF_TraverseState *tr_state, GF_Node *node_to_draw); +/*flushes (draw) all pending transparent nodes*/ +void visual_3d_flush_contexts(GF_VisualManager *visual, GF_TraverseState *tr_state); + + +/*draws a 3D object, setting up material and texture*/ +void visual_3d_draw(GF_TraverseState *tr_state, GF_Mesh *mesh); +/*draws a 2D object, setting up material and texture*/ +void visual_3d_draw_2d(Drawable *st, GF_TraverseState *tr_state); +/*draws a 2D object, setting up material and texture with specified 2D aspect*/ +void visual_3d_draw_2d_with_aspect(Drawable *st, GF_TraverseState *tr_state, DrawAspect2D *asp, Bool is_svg); +/*draws a 2D SVG object - the DrawableContext MUST be set in the traversing state*/ +void visual_3d_draw_from_context(DrawableContext *ctx, GF_TraverseState *tr_state); + + +/*sets 2D strike aspect + - exported for text drawing*/ +void visual_3d_set_2d_strike(GF_TraverseState *tr_state, DrawAspect2D *asp); +/*sets 3D material. Returns false is object is not visible due to appearance + - exported for text drawing*/ +Bool visual_3d_setup_appearance(GF_TraverseState *tr_state); +/*sets 3D texture. Returns true if a texture is found and successfully bound + - exported for text drawing*/ +Bool visual_3d_setup_texture(GF_TraverseState *tr_state, Fixed diffuse_alpha); +/*disables texture + - exported for text drawing*/ +void visual_3d_disable_texture(GF_TraverseState *tr_state); + +/*check for collisions on a list of nodes, or scene root if list is null + - exported for Layer3D - try to harmonize*/ +void visual_3d_check_collisions(GF_TraverseState *tr_state, GF_ChildNodeItem *node_list); + +/*init drawing pass - exported for Layer3D + @layer_type: + 0: not a layer + 1: 3D layer in 3D context, depth clear but no color clear + 2: 3D layer in 2D context (offscreen rendering), depth and color clear with alpha=0 +*/ +void visual_3d_init_draw(GF_TraverseState *tr_state, u32 layer_type); +/*setup projection - exported for Layer3D */ +void visual_3d_setup_projection(GF_TraverseState *tr_state); + + +/*base 3D drawable*/ +typedef struct +{ + /*3D object for drawable if needed - ALLOCATED BY DEFAULT*/ + GF_Mesh *mesh; + +} Drawable3D; + +/*generic Drawable3D constructor*/ +Drawable3D *drawable_3d_new(GF_Node *node); +/*generic Drawable3D destructor*/ +void drawable_3d_del(GF_Node *n); + +void drawable_3d_base_traverse(GF_Node *n, void *rs, Bool is_destroy, void (*build_shape)(GF_Node*,Drawable3D *,GF_TraverseState *) ); + +void drawable3d_check_focus_highlight(GF_Node *node, GF_TraverseState *tr_state, GF_BBox *orig_bounds); + +typedef struct +{ + /*the directional light*/ + GF_Node *dlight; + /*light matrix*/ + GF_Matrix light_matrix; +} DirectionalLightContext; + +typedef struct +{ + /*the one and only geometry node to draw*/ + GF_Node *geometry; + GF_Node *appearance; + /*model matrix at this node*/ + GF_Matrix model_matrix; + /*current color transformation*/ + GF_ColorMatrix color_mat; + /*1-based idx of text element drawn*/ + u32 text_split_idx; + /*needed for bitmap*/ + Bool pixel_metrics; + /*cull flag - needed for AABB tree culling in case object is 100% inside frustum*/ + u32 cull_flag; + + /*directional lights at this node*/ + GF_List *directional_lights; + /*z-depth for sorting*/ + Fixed zmax; + + /*clipper in world coords*/ + GF_Rect clipper; + Bool has_clipper; + + /*clip planes in world coords*/ + GF_Plane clip_planes[MAX_USER_CLIP_PLANES]; + u32 num_clip_planes; +} Drawable3DContext; + +/* + till end of file: all 3D specific calls. +*/ + +/*setup visual (hint & co)*/ +void visual_3d_setup(GF_VisualManager *visual); +/*turns depth buffer on/off*/ +void visual_3d_enable_depth_buffer(GF_VisualManager *visual, Bool on); +/*turns 2D AA on/off*/ +void visual_3d_enable_antialias(GF_VisualManager *visual, Bool bOn); +/*turns headlight on/off*/ +void visual_3d_enable_headlight(GF_VisualManager *visual, Bool bOn, GF_Camera *cam); + +enum +{ + /*lighting flag*/ + V3D_STATE_LIGHT = 1, + /*blending flag*/ + V3D_STATE_BLEND = (1<<1), + /*color (material3D) flag*/ + V3D_STATE_COLOR = (1<<2) +}; + +/*enable/disable one of the above feature*/ +void visual_3d_set_state(GF_VisualManager *visual, u32 flag_mask, Bool setOn); +/*clear visual with given color - alpha should only be specified for composite textures and Layer3D*/ +void visual_3d_clear(GF_VisualManager *visual, SFColor color, Fixed alpha); +/*clear depth*/ +void visual_3d_clear_depth(GF_VisualManager *visual); + +/*turns background state on/off. When on, all quality options are disabled in order to draw as fast as possible*/ +void visual_3d_set_background_state(GF_VisualManager *visual, Bool on); + +/*matrix mode types*/ +enum +{ + V3D_MATRIX_MODELVIEW, + V3D_MATRIX_PROJECTION, + V3D_MATRIX_TEXTURE, +}; +/*set current matrix type*/ +void visual_3d_set_matrix_mode(GF_VisualManager *visual, u32 mat_type); +/*push matrix stack*/ +void visual_3d_matrix_push(GF_VisualManager *visual); +/*reset current matrix (identity)*/ +void visual_3d_matrix_reset(GF_VisualManager *visual); +/*multiply current matrix with given matrix (16 coefs)*/ +void visual_3d_matrix_add(GF_VisualManager *visual, Fixed *mat); +/*loads given matrix (16 coefs) as current one*/ +void visual_3d_matrix_load(GF_VisualManager *visual, Fixed *mat); +/*pop matrix stack*/ +void visual_3d_matrix_pop(GF_VisualManager *visual); + +/*setup viewport (vp: top-left, width, height)*/ +void visual_3d_set_viewport(GF_VisualManager *visual, GF_Rect vp); +/*setup rectangular cliper (clip: top-left, width, height) +NOTE: 2D clippers can only be set from a 2D context, hence will always take the 4 first GL clip planes. +In order to allow multiple Layer2D in Layer2D, THERE IS ALWAYS AT MOST ONE 2D CLIPPER USED AT ANY TIME, +it is the caller responsability to restore previous 2D clipers*/ +void visual_3d_set_clipper_2d(GF_VisualManager *visual, GF_Rect clip); +/*remove 2D clipper*/ +void visual_3d_reset_clipper_2d(GF_VisualManager *visual); +/*set clipping plane*/ +void visual_3d_set_clip_plane(GF_VisualManager *visual, GF_Plane p); +/*reset last clipping plane set*/ +void visual_3d_reset_clip_plane(GF_VisualManager *visual); + +/*draw mesh*/ +void visual_3d_mesh_paint(GF_TraverseState *tr_state, GF_Mesh *mesh); +/*only used for ILS/ILS2D and IFS2D/Text outline*/ +void visual_3d_mesh_strike(GF_TraverseState *tr_state, GF_Mesh *mesh, Fixed width, Fixed line_scale, u32 dash_style); + +/*material types*/ +enum +{ + /*default material*/ + V3D_MATERIAL_NONE, + V3D_MATERIAL_AMBIENT, + V3D_MATERIAL_DIFFUSE, + V3D_MATERIAL_SPECULAR, + V3D_MATERIAL_EMISSIVE, +}; +/*set material*/ +void visual_3d_set_material(GF_VisualManager *visual, u32 material_type, Fixed *rgba); +/*set shininess (between 0 and 1.0)*/ +void visual_3d_set_shininess(GF_VisualManager *visual, Fixed shininess); +/*set 2D material (eq to disable lighting and set material (none))*/ +void visual_3d_set_material_2d(GF_VisualManager *visual, SFColor col, Fixed alpha); + +/*set 2D material (eq to disable lighting and set material (none))*/ +void visual_3d_set_material_2d_argb(GF_VisualManager *visual, u32 col); + +/*disables last light created - for directional lights only*/ +void visual_3d_remove_last_light(GF_VisualManager *visual); +/*disables all lights*/ +void visual_3d_clear_all_lights(GF_VisualManager *visual); +/*insert spot light - returns 0 if too many lights*/ +Bool visual_3d_add_spot_light(GF_VisualManager *visual, Fixed ambientIntensity, SFVec3f attenuation, Fixed beamWidth, + SFColor color, Fixed cutOffAngle, SFVec3f direction, Fixed intensity, SFVec3f location); +/*insert point light - returns 0 if too many lights*/ +Bool visual_3d_add_point_light(GF_VisualManager *visual, Fixed ambientIntensity, SFVec3f attenuation, SFColor color, Fixed intensity, SFVec3f location); +/*insert directional light - returns 0 if too many lights*/ +Bool visual_3d_add_directional_light(GF_VisualManager *visual, Fixed ambientIntensity, SFColor color, Fixed intensity, SFVec3f direction); +/*set fog*/ +void visual_3d_set_fog(GF_VisualManager *visual, const char *type, SFColor color, Fixed density, Fixed visibility); +/*fill given rect with given color (used for text hilighting only) - context shall not be altered*/ +void visual_3d_fill_rect(GF_VisualManager *visual, GF_Rect rc, SFColorRGBA color); + +/*non-oglES functions*/ +#ifndef GPAC_USE_OGL_ES + +/*draws image data: + pos_x, pos_y: top-left pos of image + width, height: size of image + pixelformat: image pixel format + data: image data + scale_x, scale_y: x & y scale +*/ +void visual_3d_draw_image(GF_VisualManager *visual, Fixed pos_x, Fixed pos_y, u32 width, u32 height, u32 pixelformat, char *data, Fixed scale_x, Fixed scale_y); +/*get matrix for the desired mode*/ +void visual_3d_matrix_get(GF_VisualManager *visual, u32 mat_type, Fixed *mat); +/*X3D hatching*/ +void visual_3d_mesh_hatch(GF_TraverseState *tr_state, GF_Mesh *mesh, u32 hatchStyle, SFColor hatchColor); + +#endif + + +void visual_3d_draw_bbox(GF_TraverseState *tr_state, GF_BBox *box); + +#endif /*GPAC_DISABLE_3D*/ + + +#endif /*_VISUAL_MANAGER_3D_*/ + diff --git a/src/compositor/visual_manager_3d_gl.c b/src/compositor/visual_manager_3d_gl.c new file mode 100644 index 0000000..0c50485 --- /dev/null +++ b/src/compositor/visual_manager_3d_gl.c @@ -0,0 +1,1677 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "visual_manager.h" + +#ifndef GPAC_DISABLE_3D + +#include +#include "gl_inc.h" + +#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__) +# if defined(GPAC_USE_TINYGL) +# pragma comment(lib, "TinyGL") + +# elif defined(GPAC_USE_OGL_ES) + +# if 0 +# pragma message("Using OpenGL-ES Common Lite Profile") +# pragma comment(lib, "libGLES_CL") +# define GL_ES_CL_PROFILE +# else +# pragma message("Using OpenGL-ES Common Profile") +# pragma comment(lib, "libGLES_CM") +# endif + +# else +# pragma comment(lib, "opengl32") +# pragma comment(lib, "glu32") +# endif +#endif + +/*!! HORRIBLE HACK, but on my test devices, it seems that glClipPlanex is missing on the device but not in the SDK lib !!*/ +#if defined(GL_MAX_CLIP_PLANES) && defined(__SYMBIAN32__) +#undef GL_MAX_CLIP_PLANES +#endif + +#define CHECK_GL_EXT(name) ((strstr(ext, name) != NULL) ? 1 : 0) + +void gf_sc_load_opengl_extensions(GF_Compositor *compositor) +{ +#ifdef GPAC_USE_TINYGL + /*let TGL handle texturing*/ + compositor->gl_caps.rect_texture = 1; + compositor->gl_caps.npot_texture = 1; +#else + +#if defined (GPAC_USE_OGL_ES) +#define GET_GLFUN(__name) (PFNGLARBMULTITEXTUREPROC) eglGetProcAddress(__name) +#elif defined (WIN32) +#define GET_GLFUN(__name) (PFNGLARBMULTITEXTUREPROC) wglGetProcAddress(__name) +#else +#define GET_GLFUN(__name) (PFNGLARBMULTITEXTUREPROC) glXGetProcAddress(__name) +#endif + + const char *ext; + if (!compositor->visual->type_3d) return; + + ext = (const char *) glGetString(GL_EXTENSIONS); + /*store OGL extension to config for app usage*/ + gf_cfg_set_key(compositor->user->config, "Compositor", "OpenGLExtensions", ext ? ext : "None"); + if (!ext) return; + memset(&compositor->gl_caps, 0, sizeof(GLCaps)); + + if (CHECK_GL_EXT("GL_ARB_multisample") || CHECK_GL_EXT("GLX_ARB_multisample") || CHECK_GL_EXT("WGL_ARB_multisample")) + compositor->gl_caps.multisample = 1; + if (CHECK_GL_EXT("GL_ARB_texture_non_power_of_two")) + compositor->gl_caps.npot_texture = 1; + if (CHECK_GL_EXT("GL_EXT_abgr")) + compositor->gl_caps.abgr_texture = 1; + if (CHECK_GL_EXT("GL_EXT_bgra")) + compositor->gl_caps.bgra_texture = 1; + + if (CHECK_GL_EXT("GL_EXT_texture_rectangle") || CHECK_GL_EXT("GL_NV_texture_rectangle")) { + compositor->gl_caps.rect_texture = 1; + + if (CHECK_GL_EXT("GL_MESA_ycbcr_texture")) compositor->gl_caps.yuv_texture = YCBCR_MESA; + else if (CHECK_GL_EXT("GL_APPLE_ycbcr_422")) compositor->gl_caps.yuv_texture = YCBCR_422_APPLE; + } + + if (CHECK_GL_EXT("GL_ARB_multitexture")) { + compositor->gl_caps.glActiveTextureARB = GET_GLFUN("glActiveTextureARB"); + compositor->gl_caps.glClientActiveTextureARB = GET_GLFUN("glClientActiveTextureARB"); + } + +#endif +} + +static void visual_3d_setup_quality(GF_VisualManager *visual) +{ + if (visual->compositor->high_speed) { + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST); +#ifdef GL_POLYGON_SMOOTH_HINT + glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST); +#endif + } else { + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); +#ifdef GL_POLYGON_SMOOTH_HINT + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); +#endif + } + + if (visual->compositor->antiAlias == GF_ANTIALIAS_FULL) { + glEnable(GL_LINE_SMOOTH); +#ifndef GPAC_USE_OGL_ES + if (visual->compositor->poly_aa) + glEnable(GL_POLYGON_SMOOTH); + else + glDisable(GL_POLYGON_SMOOTH); +#endif + } else { + glDisable(GL_LINE_SMOOTH); +#ifndef GPAC_USE_OGL_ES + glDisable(GL_POLYGON_SMOOTH); +#endif + } +} + +void visual_3d_setup(GF_VisualManager *visual) +{ +#ifndef GPAC_USE_TINYGL + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_LEQUAL); +#endif + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glFrontFace(GL_CCW); + glCullFace(GL_BACK); + +#ifdef GPAC_USE_OGL_ES + glClearDepthx(FIX_ONE); + glLightModelx(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); + glMaterialx(GL_FRONT_AND_BACK, GL_SHININESS, FLT2FIX(0.2f * 128) ); +#else + glClearDepth(1.0f); + glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE); + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, (float) (0.2 * 128)); +#endif + + glShadeModel(GL_SMOOTH); + glGetIntegerv(GL_MAX_LIGHTS, (GLint*)&visual->max_lights); +#ifdef GL_MAX_CLIP_PLANES + glGetIntegerv(GL_MAX_CLIP_PLANES, &visual->max_clips); +#endif + + visual_3d_setup_quality(visual); + + glDisable(GL_POINT_SMOOTH); + glDisable(GL_COLOR_MATERIAL); + glDisable(GL_LIGHTING); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glDisable(GL_CULL_FACE); + glDisable(GL_FOG); + /*Note: we cannot enable/disable normalization on the fly, because we have no clue when the GL implementation + will actually compute the related fragments. Since a typical world always use scaling, we always turn normalization on + to avoid tracking scale*/ + glEnable(GL_NORMALIZE); + + glClear(GL_DEPTH_BUFFER_BIT); +} + +void visual_3d_set_background_state(GF_VisualManager *visual, Bool on) +{ + if (on) { + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); +#ifndef GPAC_USE_OGL_ES + glDisable(GL_POLYGON_SMOOTH); +#endif + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + } else { + visual_3d_setup_quality(visual); + } +} + + + +void visual_3d_enable_antialias(GF_VisualManager *visual, Bool bOn) +{ + if (bOn) { + glEnable(GL_LINE_SMOOTH); +#ifndef GPAC_USE_OGL_ES + if (visual->compositor->poly_aa) + glEnable(GL_POLYGON_SMOOTH); + else + glDisable(GL_POLYGON_SMOOTH); +#endif + } else { + glDisable(GL_LINE_SMOOTH); +#ifndef GPAC_USE_OGL_ES + glDisable(GL_POLYGON_SMOOTH); +#endif + } +} + +void visual_3d_enable_depth_buffer(GF_VisualManager *visual, Bool on) +{ + if (on) glEnable(GL_DEPTH_TEST); + else glDisable(GL_DEPTH_TEST); +} + +void visual_3d_enable_headlight(GF_VisualManager *visual, Bool bOn, GF_Camera *cam) +{ + SFVec3f dir; + SFColor col; + if (!bOn) return; + + col.blue = col.red = col.green = FIX_ONE; + if (cam->is_3D) { + dir = camera_get_target_dir(cam); + } else { + dir.x = dir.y = 0; dir.z = FIX_ONE; + } + visual_3d_add_directional_light(visual, 0, col, FIX_ONE, dir); +} + +void visual_3d_set_viewport(GF_VisualManager *visual, GF_Rect vp) +{ + glViewport(FIX2INT(vp.x), FIX2INT(vp.y), FIX2INT(vp.width), FIX2INT(vp.height)); +} + +void visual_3d_clear_depth(GF_VisualManager *visual) +{ + glClear(GL_DEPTH_BUFFER_BIT); +} + +void VS3D_DrawAABBNode(GF_TraverseState *tr_state, GF_Mesh *mesh, u32 prim_type, GF_Plane *fplanes, u32 *p_indices, AABBNode *n) +{ + u32 i; + + /*if not leaf do cull*/ + if (n->pos) { + u32 p_idx, cull; + SFVec3f vertices[8]; + /*get box vertices*/ + gf_bbox_get_vertices(n->min, n->max, vertices); + cull = CULL_INSIDE; + for (i=0; i<6; i++) { + p_idx = p_indices[i]; + /*check p-vertex: if not in plane, we're out (since p-vertex is the closest point to the plane)*/ + if (gf_plane_get_distance(&fplanes[i], &vertices[p_idx])<0) { cull = CULL_OUTSIDE; break; } + /*check n-vertex: if not in plane, we're intersecting*/ + if (gf_plane_get_distance(&fplanes[i], &vertices[7-p_idx])<0) { cull = CULL_INTERSECTS; break;} + } + + if (cull==CULL_OUTSIDE) return; + + if (cull==CULL_INTERSECTS) { + VS3D_DrawAABBNode(tr_state, mesh, prim_type, fplanes, p_indices, n->pos); + VS3D_DrawAABBNode(tr_state, mesh, prim_type, fplanes, p_indices, n->neg); + return; + } + } + + /*the good thing about the structure of the aabb tree is that the primitive idx is valid for both + leaf and non-leaf nodes, so we can use it as soon as we have a CULL_INSIDE. + However we must push triangles one by one since primitive order may have been swapped when + building the AABB tree*/ + for (i=0; inb_idx; i++) { +#ifdef GPAC_USE_OGL_ES + glDrawElements(prim_type, 3, GL_UNSIGNED_SHORT, &mesh->indices[3*n->indices[i]]); +#else + glDrawElements(prim_type, 3, GL_UNSIGNED_INT, &mesh->indices[3*n->indices[i]]); +#endif + } +} + +void VS3D_DrawMeshIntern(GF_TraverseState *tr_state, GF_Mesh *mesh) +{ + Bool has_col, has_tx, has_norm; + u32 prim_type; + GF_Compositor *compositor = tr_state->visual->compositor; +#if defined(GPAC_FIXED_POINT) && !defined(GPAC_USE_OGL_ES) + Float *color_array = NULL; + Float fix_scale = 1.0f; + fix_scale /= FIX_ONE; +#endif + + has_col = has_tx = has_norm = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[V3D] Drawing mesh 0x%08x\n", mesh)); + + glEnableClientState(GL_VERTEX_ARRAY); +#if defined(GPAC_USE_OGL_ES) + glVertexPointer(3, GL_FIXED, sizeof(GF_Vertex), &mesh->vertices[0].pos); +#elif defined(GPAC_FIXED_POINT) + /*scale modelview matrix*/ + glPushMatrix(); + glScalef(fix_scale, fix_scale, fix_scale); + glVertexPointer(3, GL_INT, sizeof(GF_Vertex), &mesh->vertices[0].pos); +#else + glVertexPointer(3, GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].pos); +#endif + + if (!tr_state->mesh_num_textures && (mesh->flags & MESH_HAS_COLOR)) { + glEnable(GL_COLOR_MATERIAL); +#if !defined (GPAC_USE_OGL_ES) + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); +#endif + glEnableClientState(GL_COLOR_ARRAY); + has_col = 1; + +#if defined (GPAC_USE_OGL_ES) + + if (mesh->flags & MESH_HAS_ALPHA) { + glEnable(GL_BLEND); + tr_state->mesh_is_transparent = 1; + } +#ifdef MESH_USE_SFCOLOR + /*glES only accepts full RGBA colors*/ + glColorPointer(4, GL_FIXED, sizeof(GF_Vertex), &mesh->vertices[0].color); +#else + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(GF_Vertex), &mesh->vertices[0].color); +#endif /*MESH_USE_SFCOLOR*/ + +#elif defined (GPAC_FIXED_POINT) + + +#ifdef MESH_USE_SFCOLOR + /*this is a real pain: we cannot "scale" colors through openGL, and our components are 16.16 (32 bytes) ranging + from [0 to 65536] mapping to [0, 1.0], but openGL assumes for s32 a range from [-2^31 2^31] mapping to [0, 1.0] + we must thus rebuild a dedicated array...*/ + if (mesh->flags & MESH_HAS_ALPHA) { + u32 i; + color_array = malloc(sizeof(Float)*4*mesh->v_count); + for (i=0; iv_count; i++) { + color_array[4*i] = FIX2FLT(mesh->vertices[i].color.red); + color_array[4*i+1] = FIX2FLT(mesh->vertices[i].color.green); + color_array[4*i+2] = FIX2FLT(mesh->vertices[i].color.blue); + color_array[4*i+3] = FIX2FLT(mesh->vertices[i].color.alpha); + } + glEnable(GL_BLEND); + glColorPointer(4, GL_FLOAT, 4*sizeof(Float), color_array); + tr_state->mesh_is_transparent = 1; + } else { + color_array = malloc(sizeof(Float)*3*mesh->v_count); + for (i=0; iv_count; i++) { + color_array[3*i] = FIX2FLT(mesh->vertices[i].color.red); + color_array[3*i+1] = FIX2FLT(mesh->vertices[i].color.green); + color_array[3*i+2] = FIX2FLT(mesh->vertices[i].color.blue); + } + glColorPointer(3, GL_FLOAT, 3*sizeof(Float), color_array); + } +#else + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(GF_Vertex), &mesh->vertices[0].color); +#endif /*MESH_USE_SFCOLOR*/ + +#else + +#ifdef MESH_USE_SFCOLOR + if (mesh->flags & MESH_HAS_ALPHA) { + glEnable(GL_BLEND); + glColorPointer(4, GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].color); + tr_state->mesh_is_transparent = 1; + } else { + glColorPointer(3, GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].color); + } +#else + if (mesh->flags & MESH_HAS_ALPHA) { + glEnable(GL_BLEND); + tr_state->mesh_is_transparent = 1; + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(GF_Vertex), &mesh->vertices[0].color); + } else { + glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(GF_Vertex), &mesh->vertices[0].color); + } +#endif /*MESH_USE_SFCOLOR*/ + +#endif + } + + if (tr_state->mesh_num_textures && !mesh->mesh_type && !(mesh->flags & MESH_NO_TEXTURE)) { + has_tx = 1; +#if defined(GPAC_USE_OGL_ES) + glTexCoordPointer(2, GL_FIXED, sizeof(GF_Vertex), &mesh->vertices[0].texcoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY ); +#elif defined(GPAC_FIXED_POINT) + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glScalef(fix_scale, fix_scale, fix_scale); + glMatrixMode(GL_MODELVIEW); + glTexCoordPointer(2, GL_INT, sizeof(GF_Vertex), &mesh->vertices[0].texcoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY ); +#else + if (tr_state->mesh_num_textures>1) { + u32 i; + for (i=0; imesh_num_textures; i++) { + compositor->gl_caps.glClientActiveTextureARB(GL_TEXTURE0_ARB + i); + glTexCoordPointer(2, GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].texcoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + } else { + glTexCoordPointer(2, GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].texcoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY ); + } +#endif + } + + if (mesh->mesh_type) { +#ifdef GPAC_USE_OGL_ES + glNormal3x(0, 0, FIX_ONE); +#else + glNormal3f(0, 0, 1.0f); +#endif + glDisable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + if (mesh->mesh_type==2) glDisable(GL_LINE_SMOOTH); + else glDisable(GL_POINT_SMOOTH); + +#if !defined(GPAC_USE_TINYGL) && !defined(GL_ES_CL_PROFILE) + glLineWidth(1.0f); +#endif + + } else { + u32 normal_type = GL_FLOAT; + has_norm = 1; + glEnableClientState(GL_NORMAL_ARRAY ); +#ifdef MESH_USE_FIXED_NORMAL + +#if defined(GPAC_USE_OGL_ES) + normal_type = GL_FIXED; +#elif defined(GPAC_FIXED_POINT) + normal_type = GL_INT; +#else + normal_type = GL_FLOAT; +#endif + +#else /*MESH_USE_FIXED_NORMAL*/ + /*normals are stored on signed bytes*/ + normal_type = GL_BYTE; +#endif + glNormalPointer(normal_type, sizeof(GF_Vertex), &mesh->vertices[0].normal); + + if (!mesh->mesh_type) { + if (compositor->backcull + && (!tr_state->mesh_is_transparent || (compositor->backcull ==GF_BACK_CULL_ALPHA) ) + && (mesh->flags & MESH_IS_SOLID)) { + glEnable(GL_CULL_FACE); + glFrontFace((mesh->flags & MESH_IS_CW) ? GL_CW : GL_CCW); + } else { + glDisable(GL_CULL_FACE); + } + } + } + + switch (mesh->mesh_type) { + case MESH_LINESET: prim_type = GL_LINES; break; + case MESH_POINTSET: prim_type = GL_POINTS; break; + default: prim_type = GL_TRIANGLES; break; + } + +#if 1 + /*if inside or no aabb for the mesh draw vertex array*/ + if (compositor->disable_gl_cull || (tr_state->cull_flag==CULL_INSIDE) || !mesh->aabb_root || !mesh->aabb_root->pos) { +#ifdef GPAC_USE_OGL_ES + glDrawElements(prim_type, mesh->i_count, GL_UNSIGNED_SHORT, mesh->indices); +#else + glDrawElements(prim_type, mesh->i_count, GL_UNSIGNED_INT, mesh->indices); +#endif + } else { + /*otherwise cull aabb against frustum - after some testing it appears (as usual) that there must + be a compromise: we're slowing down the compositor here, however the gain is really appreciable for + large meshes, especially terrains/elevation grids*/ + + /*first get transformed frustum in local space*/ + GF_Matrix mx; + u32 i, p_idx[6]; + GF_Plane fplanes[6]; + gf_mx_copy(mx, tr_state->model_matrix); + gf_mx_inverse(&mx); + for (i=0; i<6; i++) { + fplanes[i] = tr_state->camera->planes[i]; + gf_mx_apply_plane(&mx, &fplanes[i]); + p_idx[i] = gf_plane_get_p_vertex_idx(&fplanes[i]); + } + /*then recursively cull & draw AABB tree*/ + VS3D_DrawAABBNode(tr_state, mesh, prim_type, fplanes, p_idx, mesh->aabb_root->pos); + VS3D_DrawAABBNode(tr_state, mesh, prim_type, fplanes, p_idx, mesh->aabb_root->neg); + } + +#endif + + glDisableClientState(GL_VERTEX_ARRAY); + if (has_col) glDisableClientState(GL_COLOR_ARRAY); + glDisable(GL_COLOR_MATERIAL); + + if (has_tx) glDisableClientState(GL_TEXTURE_COORD_ARRAY); + if (has_norm) glDisableClientState(GL_NORMAL_ARRAY); + +#if defined(GPAC_FIXED_POINT) && !defined(GPAC_USE_OGL_ES) + if (color_array) free(color_array); + if (!mesh->mesh_type && !(mesh->flags & MESH_NO_TEXTURE)) { + glMatrixMode(GL_TEXTURE); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + } + glPopMatrix(); +#endif + + if (tr_state->mesh_is_transparent) glDisable(GL_BLEND); + tr_state->mesh_is_transparent = 0; +} + +#ifdef GPAC_USE_OGL_ES +u32 ogles_push_enable(u32 mask) +{ + u32 attrib = 0; +#if !defined(__SYMBIAN32__) && !defined(GL_ES_CL_PROFILE) + if ((mask & GL_LIGHTING) && glIsEnabled(GL_LIGHTING) ) attrib |= GL_LIGHTING; + if ((mask & GL_BLEND) && glIsEnabled(GL_BLEND) ) attrib |= GL_BLEND; + if ((mask & GL_COLOR_MATERIAL) && glIsEnabled(GL_COLOR_MATERIAL) ) attrib |= GL_COLOR_MATERIAL; + if ((mask & GL_TEXTURE_2D) && glIsEnabled(GL_TEXTURE_2D) ) attrib |= GL_TEXTURE_2D; +#endif + return attrib; +} +void ogles_pop_enable(u32 mask) +{ +#if !defined(__SYMBIAN32__) && !defined(GL_ES_CL_PROFILE) + if (mask & GL_LIGHTING) glEnable(GL_LIGHTING); + if (mask & GL_BLEND) glEnable(GL_BLEND); + if (mask & GL_COLOR_MATERIAL) glEnable(GL_COLOR_MATERIAL); + if (mask & GL_TEXTURE_2D) glEnable(GL_TEXTURE_2D); +#endif +} +#endif + +/*note we don't perform any culling for normal drawing...*/ +void VS3D_DrawNormals(GF_TraverseState *tr_state, GF_Mesh *mesh) +{ +#ifndef GPAC_USE_TINYGL + + GF_Vec pt, end; + u32 i, j; + Fixed scale = mesh->bounds.radius / 4; + +#ifdef GPAC_USE_OGL_ES + GF_Vec va[2]; + u16 indices[2]; + u32 attrib = ogles_push_enable(GL_LIGHTING | GL_BLEND | GL_COLOR_MATERIAL | GL_TEXTURE_2D); + glEnableClientState(GL_VERTEX_ARRAY); +#else + glPushAttrib(GL_ENABLE_BIT); +#endif + + glDisable(GL_LIGHTING | GL_BLEND | GL_COLOR_MATERIAL | GL_TEXTURE_2D); +#ifdef GPAC_USE_OGL_ES + glColor4x(0, 0, 0, 1); +#else + glColor3f(1, 1, 1); +#endif + + if (tr_state->visual->compositor->draw_normals==GF_NORMALS_VERTEX) { + IDX_TYPE *idx = mesh->indices; + for (i=0; ii_count; i+=3) { + for (j=0; j<3; j++) { + pt = mesh->vertices[idx[j]].pos; + MESH_GET_NORMAL(end, mesh->vertices[idx[j]]); + end = gf_vec_scale(end, scale); + gf_vec_add(end, pt, end); +#ifdef GPAC_USE_OGL_ES + va[0] = pt; + va[1] = end; + indices[0] = 0; + indices[1] = 1; + glVertexPointer(3, GL_FIXED, 0, va); + glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, indices); +#else + glBegin(GL_LINES); + glVertex3f(FIX2FLT(pt.x), FIX2FLT(pt.y), FIX2FLT(pt.z)); + glVertex3f(FIX2FLT(end.x), FIX2FLT(end.y), FIX2FLT(end.z)); + glEnd(); +#endif + } + idx+=3; + } + } else { + IDX_TYPE *idx = mesh->indices; + for (i=0; ii_count; i+=3) { + gf_vec_add(pt, mesh->vertices[idx[0]].pos, mesh->vertices[idx[1]].pos); + gf_vec_add(pt, pt, mesh->vertices[idx[2]].pos); + pt = gf_vec_scale(pt, FIX_ONE/3); + MESH_GET_NORMAL(end, mesh->vertices[idx[0]]); + end = gf_vec_scale(end, scale); + gf_vec_add(end, pt, end); +#ifdef GPAC_USE_OGL_ES + va[0] = pt; + va[1] = end; + indices[0] = 0; + indices[1] = 1; + glVertexPointer(3, GL_FIXED, 0, va); + glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, indices); +#else + glBegin(GL_LINES); + glVertex3f(FIX2FLT(pt.x), FIX2FLT(pt.y), FIX2FLT(pt.z)); + glVertex3f(FIX2FLT(end.x), FIX2FLT(end.y), FIX2FLT(end.z)); + glEnd(); +#endif + idx += 3; + } + } +#ifdef GPAC_USE_OGL_ES + ogles_pop_enable(attrib); + glDisableClientState(GL_VERTEX_ARRAY); +#else + glPopAttrib(); +#endif + +#endif /*GPAC_USE_TINYGL*/ +} + + +void VS3D_DrawAABBNodeBounds(GF_TraverseState *tr_state, AABBNode *node) +{ + if (node->pos) { + VS3D_DrawAABBNodeBounds(tr_state, node->pos); + VS3D_DrawAABBNodeBounds(tr_state, node->neg); + } else { + SFVec3f c, s; + gf_vec_diff(s, node->max, node->min); + c = gf_vec_scale(s, FIX_ONE/2); + gf_vec_add(c, node->min, c); + + glPushMatrix(); +#ifdef GPAC_USE_OGL_ES + glTranslatex(c.x, c.y, c.z); + glScalex(s.x, s.y, s.z); +#else + glTranslatef(FIX2FLT(c.x), FIX2FLT(c.y), FIX2FLT(c.z)); + glScalef(FIX2FLT(s.x), FIX2FLT(s.y), FIX2FLT(s.z)); +#endif + VS3D_DrawMeshIntern(tr_state, tr_state->visual->compositor->unit_bbox); + glPopMatrix(); + } +} + +void visual_3d_draw_bbox(GF_TraverseState *tr_state, GF_BBox *box) +{ + SFVec3f c, s; +#ifdef GPAC_USE_TINYGL + +#elif defined(GPAC_USE_OGL_ES) + u32 atts = ogles_push_enable(GL_LIGHTING); +#else + glPushAttrib(GL_ENABLE_BIT); +#endif + gf_vec_diff(s, box->max_edge, box->min_edge); + c.x = box->min_edge.x + s.x/2; + c.y = box->min_edge.y + s.y/2; + c.z = box->min_edge.z + s.z/2; + + visual_3d_set_material_2d_argb(tr_state->visual, tr_state->visual->compositor->highlight_stroke); + glPushMatrix(); + +#ifdef GPAC_USE_OGL_ES + glTranslatex(c.x, c.y, c.z); + glScalex(s.x, s.y, s.z); +#else + glTranslatef(FIX2FLT(c.x), FIX2FLT(c.y), FIX2FLT(c.z)); + glScalef(FIX2FLT(s.x), FIX2FLT(s.y), FIX2FLT(s.z)); +// glScalef(1.1f, 1.1f, 1.1f); +#endif + VS3D_DrawMeshIntern(tr_state, tr_state->visual->compositor->unit_bbox); + glPopMatrix(); + +#ifdef GPAC_USE_TINYGL + +#elif defined(GPAC_USE_OGL_ES) + ogles_pop_enable(atts); +#else + glPopAttrib(); +#endif + +} +//#endif + + +void VS3D_DrawMeshBoundingVolume(GF_TraverseState *tr_state, GF_Mesh *mesh) +{ +#ifndef GPAC_USE_TINYGL + if (mesh->aabb_root && (tr_state->visual->compositor->draw_bvol==GF_BOUNDS_AABB)) { +#ifdef GPAC_USE_OGL_ES + u32 atts = ogles_push_enable(GL_LIGHTING); +#else + glPushAttrib(GL_ENABLE_BIT); +#endif + glDisable(GL_LIGHTING); + VS3D_DrawAABBNodeBounds(tr_state, mesh->aabb_root); +#ifdef GPAC_USE_OGL_ES + ogles_pop_enable(atts); +#else + glPopAttrib(); +#endif + } else { + visual_3d_draw_bbox(tr_state, &mesh->bounds); + } + + +#endif /*GPAC_USE_TINYGL*/ +} + +void visual_3d_mesh_paint(GF_TraverseState *tr_state, GF_Mesh *mesh) +{ + Bool mesh_drawn = 0; + if (tr_state->visual->compositor->wiremode != GF_WIREFRAME_ONLY) { + VS3D_DrawMeshIntern(tr_state, mesh); + mesh_drawn = 1; + } + + if (tr_state->visual->compositor->draw_normals) VS3D_DrawNormals(tr_state, mesh); + if (!mesh->mesh_type && (tr_state->visual->compositor->wiremode != GF_WIREFRAME_NONE)) { + glDisable(GL_LIGHTING); +#ifdef GPAC_USE_OGL_ES + if (mesh_drawn) glColor4x(0, 0, 0, FIX_ONE); +#else + if (mesh_drawn) glColor4f(0, 0, 0, 1.0f); +#endif + glEnableClientState(GL_VERTEX_ARRAY); +#ifdef GPAC_USE_OGL_ES + glVertexPointer(3, GL_FIXED, sizeof(GF_Vertex), &mesh->vertices[0].pos); + glDrawElements(GL_LINES, mesh->i_count, GL_UNSIGNED_SHORT, mesh->indices); +#else + glVertexPointer(3, GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].pos); + glDrawElements(GL_LINES, mesh->i_count, GL_UNSIGNED_INT, mesh->indices); +#endif + glDisableClientState(GL_VERTEX_ARRAY); + } + if (tr_state->visual->compositor->draw_bvol) VS3D_DrawMeshBoundingVolume(tr_state, mesh); +} + +#if !defined(GPAC_USE_OGL_ES) && !defined(GPAC_USE_TINYGL) + + +static GLubyte hatch_horiz[] = { + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff +}; + +static GLubyte hatch_vert[] = { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa +}; + +static GLubyte hatch_up[] = { + 0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30, + 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03, + 0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30, + 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03, + 0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30, + 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03, + 0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30, + 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03, + 0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30, + 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03, + 0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30, + 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03, + 0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30, + 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03, + 0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30, + 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03 +}; + +static GLubyte hatch_down[] = { + 0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c, + 0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0, + 0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c, + 0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0, + 0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c, + 0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0, + 0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c, + 0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0, + 0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c, + 0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0, + 0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c, + 0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0, + 0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c, + 0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0, + 0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c, + 0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0 +}; + +static GLubyte hatch_cross[] = { + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c +}; + +void visual_3d_mesh_hatch(GF_TraverseState *tr_state, GF_Mesh *mesh, u32 hatchStyle, SFColor hatchColor) +{ + if (mesh->mesh_type) return; + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].pos); + if (mesh->mesh_type || (mesh->flags & MESH_IS_2D)) { + glDisableClientState(GL_NORMAL_ARRAY); + if (mesh->mesh_type) glDisable(GL_LIGHTING); + glNormal3f(0, 0, 1.0f); + glDisable(GL_CULL_FACE); + } else { + glEnableClientState(GL_NORMAL_ARRAY ); + glNormalPointer(GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].normal); + + if (!mesh->mesh_type) { + /*if mesh is transparent DON'T CULL*/ + if (!tr_state->mesh_is_transparent && (mesh->flags & MESH_IS_SOLID)) { + glEnable(GL_CULL_FACE); + glFrontFace((mesh->flags & MESH_IS_CW) ? GL_CW : GL_CCW); + } else { + glDisable(GL_CULL_FACE); + } + } + } + + glEnable(GL_POLYGON_STIPPLE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + /*can't access ISO International Register of Graphical Items www site :)*/ + switch (hatchStyle) { + case 5: glPolygonStipple(hatch_cross); break; + case 4: glPolygonStipple(hatch_up); break; + case 3: glPolygonStipple(hatch_down); break; + case 2: glPolygonStipple(hatch_vert); break; + case 1: glPolygonStipple(hatch_horiz); break; + default: glDisable(GL_POLYGON_STIPPLE); break; + } + glColor3f(FIX2FLT(hatchColor.red), FIX2FLT(hatchColor.green), FIX2FLT(hatchColor.blue)); + glDrawElements(GL_TRIANGLES, mesh->i_count, GL_UNSIGNED_INT, mesh->indices); + + glDisable(GL_POLYGON_STIPPLE); +} +#endif + +/*only used for ILS/ILS2D or IFS2D outline*/ +void visual_3d_mesh_strike(GF_TraverseState *tr_state, GF_Mesh *mesh, Fixed width, Fixed line_scale, u32 dash_style) +{ +#if !defined(GPAC_USE_OGL_ES) && !defined(GPAC_USE_TINYGL) + u16 style; +#endif + + if (mesh->mesh_type != MESH_LINESET) return; + if (line_scale) width = gf_mulfix(width, line_scale); + width/=2; +#if !defined(GPAC_USE_TINYGL) && !defined(GL_ES_CL_PROFILE) + glLineWidth( FIX2FLT(width)); +#endif + +#if !defined(GPAC_USE_OGL_ES) && !defined(GPAC_USE_TINYGL) + + switch (dash_style) { + case GF_DASH_STYLE_DASH: style = 0x1F1F; break; + case GF_DASH_STYLE_DOT: style = 0x3333; break; + case GF_DASH_STYLE_DASH_DOT: style = 0x6767; break; + case GF_DASH_STYLE_DASH_DASH_DOT: style = 0x33CF; break; + case GF_DASH_STYLE_DASH_DOT_DOT: style = 0x330F; break; + default: + style = 0; + break; + } + if (style) { + u32 factor = FIX2INT(width); + if (!factor) factor = 1; + glEnable(GL_LINE_STIPPLE); + glLineStipple(factor, style); + visual_3d_mesh_paint(tr_state, mesh); + glDisable (GL_LINE_STIPPLE); + } else +#endif + visual_3d_mesh_paint(tr_state, mesh); +} + +void visual_3d_set_material_2d(GF_VisualManager *visual, SFColor col, Fixed alpha) +{ + glDisable(GL_LIGHTING); + if (alpha != FIX_ONE) { + glEnable(GL_BLEND); + visual_3d_enable_antialias(visual, 0); + } else { + glDisable(GL_BLEND); + visual_3d_enable_antialias(visual, visual->compositor->antiAlias ? 1 : 0); + } +#ifdef GPAC_USE_OGL_ES + glColor4x(col.red, col.green, col.blue, alpha); +#else + glColor4f(FIX2FLT(col.red), FIX2FLT(col.green), FIX2FLT(col.blue), FIX2FLT(alpha)); +#endif +} + +void visual_3d_set_material_2d_argb(GF_VisualManager *visual, u32 col) +{ + u32 a; + a = GF_COL_A(col); + + glDisable(GL_LIGHTING); + if (a != 0xFF) { + glEnable(GL_BLEND); + visual_3d_enable_antialias(visual, 0); + } else { + glDisable(GL_BLEND); + visual_3d_enable_antialias(visual, visual->compositor->antiAlias ? 1 : 0); + } +#ifdef GPAC_USE_OGL_ES + glColor4x(GF_COL_R(col)<<8, GF_COL_G(col)<<8, GF_COL_B(col)<<8, a<<8); +#else + glColor4f(GF_COL_R(col)/255.0f, GF_COL_G(col)/255.0f, GF_COL_B(col)/255.0f, a/255.0f); +#endif +} + +void visual_3d_clear(GF_VisualManager *visual, SFColor color, Fixed alpha) +{ +#ifdef GPAC_USE_OGL_ES + glClearColorx(color.red, color.green, color.blue, alpha); +#else + glClearColor(FIX2FLT(color.red), FIX2FLT(color.green), FIX2FLT(color.blue), FIX2FLT(alpha)); +#endif + glClear(GL_COLOR_BUFFER_BIT); +} + +#if !defined(GPAC_USE_OGL_ES) && !defined(GPAC_USE_TINYGL) + +void visual_3d_draw_image(GF_VisualManager *visual, Fixed pos_x, Fixed pos_y, u32 width, u32 height, u32 pixelformat, char *data, Fixed scale_x, Fixed scale_y) +{ + u32 gl_format; + glPixelZoom(FIX2FLT(scale_x), FIX2FLT(scale_y)); + + gl_format = 0; + switch (pixelformat) { + case GF_PIXEL_RGB_24: + gl_format = GL_RGB; + break; + case GF_PIXEL_RGB_32: + case GF_PIXEL_RGBA: + gl_format = GL_RGBA; + break; + case GF_PIXEL_ARGB: + if (!visual->compositor->gl_caps.bgra_texture) return; + gl_format = GL_BGRA_EXT; + break; + default: + return; + } + + /*glRasterPos2f doesn't accept point outside the view volume (it invalidates all draw pixel, draw bitmap) + so we move to the center of the local coord system, draw a NULL bitmap with raster pos displacement*/ + glRasterPos2f(0, 0); + glBitmap(0, 0, 0, 0, FIX2FLT(pos_x), -FIX2FLT(pos_y), NULL); + glDrawPixels(width, height, gl_format, GL_UNSIGNED_BYTE, data); + glBitmap(0, 0, 0, 0, -FIX2FLT(pos_x), FIX2FLT(pos_y), NULL); + +} + + +void visual_3d_matrix_get(GF_VisualManager *visual, u32 mat_type, Fixed *mat) +{ +#ifdef GPAC_FIXED_POINT + u32 i = 0; +#endif + Float _mat[16]; + switch (mat_type) { + case V3D_MATRIX_MODELVIEW: + glGetFloatv(GL_MODELVIEW_MATRIX, _mat); + break; + case V3D_MATRIX_PROJECTION: + glGetFloatv(GL_PROJECTION_MATRIX, _mat); + break; + case V3D_MATRIX_TEXTURE: + glGetFloatv(GL_TEXTURE_MATRIX, _mat); + break; + } +#ifdef GPAC_FIXED_POINT + for (i=0; i<16; i++) mat[i] = FLT2FIX(_mat[i]); +#else + memcpy(mat, _mat, sizeof(Fixed)*16); +#endif +} + +#endif + +void visual_3d_set_matrix_mode(GF_VisualManager *visual, u32 mat_type) +{ + switch (mat_type) { + case V3D_MATRIX_MODELVIEW: + glMatrixMode(GL_MODELVIEW); + break; + case V3D_MATRIX_PROJECTION: + glMatrixMode(GL_PROJECTION); + break; + case V3D_MATRIX_TEXTURE: + glMatrixMode(GL_TEXTURE); + break; + } +} + +void visual_3d_matrix_reset(GF_VisualManager *visual) +{ + glLoadIdentity(); +} +void visual_3d_matrix_push(GF_VisualManager *visual) +{ + glPushMatrix(); +} +void visual_3d_matrix_add(GF_VisualManager *visual, Fixed *mat) +{ +#ifdef GPAC_USE_OGL_ES + glMultMatrixx(mat); +#elif defined(GPAC_FIXED_POINT) + u32 i; + Float _mat[16]; + for (i=0; i<16; i++) _mat[i] = FIX2FLT(mat[i]); + glMultMatrixf(_mat); +#else + glMultMatrixf(mat); +#endif +} + +void visual_3d_matrix_pop(GF_VisualManager *visual) +{ + glPopMatrix(); +} + +void visual_3d_matrix_load(GF_VisualManager *visual, Fixed *mat) +{ +#ifdef GPAC_USE_OGL_ES + glLoadMatrixx(mat); +#elif defined(GPAC_FIXED_POINT) + Float _mat[16]; + u32 i; + for (i=0; i<16; i++) _mat[i] = FIX2FLT(mat[i]); + glLoadMatrixf(_mat); +#else + glLoadMatrixf(mat); +#endif +} + + +void visual_3d_set_clipper_2d(GF_VisualManager *visual, GF_Rect clip) +{ +#ifdef GL_MAX_CLIP_PLANES + +#ifdef GPAC_USE_OGL_ES + + Fixed g[4]; + u32 cp; + visual_3d_reset_clipper_2d(visual); + if (visual->num_clips + 4 > visual->max_clips) return; + cp = visual->num_clips; + g[2] = 0; g[1] = 0; + g[3] = clip.x + clip.width; g[0] = -FIX_ONE; + glClipPlanex(GL_CLIP_PLANE0 + cp, g); glEnable(GL_CLIP_PLANE0 + cp); + g[3] = -clip.x; g[0] = FIX_ONE; + glClipPlanex(GL_CLIP_PLANE0 + cp + 1, g); glEnable(GL_CLIP_PLANE0 + cp + 1); + g[0] = 0; + g[3] = clip.y; g[1] = -FIX_ONE; + glClipPlanex(GL_CLIP_PLANE0 + cp + 2, g); glEnable(GL_CLIP_PLANE0 + cp + 2); + g[3] = clip.height - clip.y; g[1] = FIX_ONE; + glClipPlanex(GL_CLIP_PLANE0 + cp + 3, g); glEnable(GL_CLIP_PLANE0 + cp + 3); + visual->num_clips += 4; +#else + Double g[4]; + u32 cp; + visual_3d_reset_clipper_2d(visual); + if (visual->num_clips + 4 > visual->max_clips) return; + cp = visual->num_clips; + g[2] = 0; + g[1] = 0; + g[3] = FIX2FLT(clip.x) + FIX2FLT(clip.width); g[0] = -1; + glClipPlane(GL_CLIP_PLANE0 + cp, g); glEnable(GL_CLIP_PLANE0 + cp); + g[3] = -FIX2FLT(clip.x); g[0] = 1; + glClipPlane(GL_CLIP_PLANE0 + cp + 1, g); glEnable(GL_CLIP_PLANE0 + cp + 1); + g[0] = 0; + g[3] = FIX2FLT(clip.y); g[1] = -1; + glClipPlane(GL_CLIP_PLANE0 + cp + 2, g); glEnable(GL_CLIP_PLANE0 + cp + 2); + g[3] = FIX2FLT(clip.height-clip.y); g[1] = 1; + glClipPlane(GL_CLIP_PLANE0 + cp + 3, g); glEnable(GL_CLIP_PLANE0 + cp + 3); + visual->num_clips += 4; +#endif + +#endif +} + +void visual_3d_reset_clipper_2d(GF_VisualManager *visual) +{ +#ifdef GL_MAX_CLIP_PLANES + u32 cp; + if (visual->num_clips < 4) return; + cp = visual->num_clips - 4; + glDisable(GL_CLIP_PLANE0 + cp + 3); + glDisable(GL_CLIP_PLANE0 + cp + 2); + glDisable(GL_CLIP_PLANE0 + cp + 1); + glDisable(GL_CLIP_PLANE0 + cp); + visual->num_clips -= 4; +#endif +} + +void visual_3d_set_clip_plane(GF_VisualManager *visual, GF_Plane p) +{ +#ifdef GL_MAX_CLIP_PLANES + +#ifdef GPAC_USE_OGL_ES + Fixed g[4]; + if (visual->num_clips + 1 > visual->max_clips) return; + gf_vec_norm(&p.normal); + g[0] = p.normal.x; + g[1] = p.normal.y; + g[2] = p.normal.z; + g[3] = p.d; + glClipPlanex(GL_CLIP_PLANE0 + visual->num_clips, g); +#else + Double g[4]; + if (visual->num_clips + 1 > visual->max_clips) return; + gf_vec_norm(&p.normal); + g[0] = FIX2FLT(p.normal.x); + g[1] = FIX2FLT(p.normal.y); + g[2] = FIX2FLT(p.normal.z); + g[3] = FIX2FLT(p.d); + glClipPlane(GL_CLIP_PLANE0 + visual->num_clips, g); +#endif + glEnable(GL_CLIP_PLANE0 + visual->num_clips); + visual->num_clips++; +#endif + +} + +void visual_3d_reset_clip_plane(GF_VisualManager *visual) +{ +#ifdef GL_MAX_CLIP_PLANES + if (!visual->num_clips) return; + glDisable(GL_CLIP_PLANE0 + visual->num_clips-1); + visual->num_clips -= 1; +#endif +} + +void visual_3d_set_material(GF_VisualManager *visual, u32 material_type, Fixed *rgba) +{ + GLenum mode; +#if defined(GPAC_USE_OGL_ES) + Fixed *_rgba = rgba; +#elif defined(GPAC_FIXED_POINT) + Float _rgba[4]; + _rgba[0] = FIX2FLT(rgba[0]); _rgba[1] = FIX2FLT(rgba[1]); _rgba[2] = FIX2FLT(rgba[2]); _rgba[3] = FIX2FLT(rgba[3]); +#else + Float *_rgba = rgba; +#endif + + switch (material_type) { + case V3D_MATERIAL_AMBIENT: mode = GL_AMBIENT; break; + case V3D_MATERIAL_DIFFUSE: mode = GL_DIFFUSE; break; + case V3D_MATERIAL_SPECULAR: mode = GL_SPECULAR; break; + case V3D_MATERIAL_EMISSIVE: mode = GL_EMISSION; break; + + case V3D_MATERIAL_NONE: +#ifdef GPAC_USE_OGL_ES + glColor4x(_rgba[0], _rgba[1], _rgba[2], _rgba[3]); +#else + glColor4fv(_rgba); +#endif + /*fall-through*/ + default: + return; + } +#ifdef GPAC_USE_OGL_ES + glMaterialxv(GL_FRONT_AND_BACK, mode, _rgba); +#else + glMaterialfv(GL_FRONT_AND_BACK, mode, _rgba); +#endif +} + +void visual_3d_set_shininess(GF_VisualManager *visual, Fixed shininess) +{ +#ifdef GPAC_USE_OGL_ES + glMaterialx(GL_FRONT_AND_BACK, GL_SHININESS, shininess * 128); +#else + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, FIX2FLT(shininess) * 128); +#endif +} + +void visual_3d_set_state(GF_VisualManager *visual, u32 flag_mask, Bool setOn) +{ + if (setOn) { + if (flag_mask & V3D_STATE_LIGHT) glEnable(GL_LIGHTING); + if (flag_mask & V3D_STATE_BLEND) glEnable(GL_BLEND); + if (flag_mask & V3D_STATE_COLOR) glEnable(GL_COLOR_MATERIAL); + } else { + if (flag_mask & V3D_STATE_LIGHT) + glDisable(GL_LIGHTING); + if (flag_mask & V3D_STATE_BLEND) glDisable(GL_BLEND); +#ifdef GPAC_USE_OGL_ES + if (flag_mask & V3D_STATE_COLOR) glDisable(GL_COLOR_MATERIAL); +#else + if (flag_mask & V3D_STATE_COLOR) glDisable(GL_COLOR_MATERIAL | GL_COLOR_MATERIAL_FACE); +#endif + } +} + +Bool visual_3d_add_spot_light(GF_VisualManager *visual, Fixed _ambientIntensity, SFVec3f attenuation, Fixed _beamWidth, + SFColor color, Fixed _cutOffAngle, SFVec3f direction, Fixed _intensity, SFVec3f location) +{ +#ifdef GPAC_USE_OGL_ES + Fixed vals[4], exp; +#else + Float vals[4], intensity, cutOffAngle, beamWidth, ambientIntensity, exp; +#endif + GLint iLight; + + if (!visual->num_lights) glEnable(GL_LIGHTING); + if (visual->num_lights==visual->max_lights) return 0; + iLight = GL_LIGHT0 + visual->num_lights; + visual->num_lights++; + glEnable(iLight); + +#ifndef GPAC_USE_OGL_ES + ambientIntensity = FIX2FLT(_ambientIntensity); + intensity = FIX2FLT(_intensity); + cutOffAngle = FIX2FLT(_cutOffAngle); + beamWidth = FIX2FLT(_beamWidth); +#endif + + /*in case...*/ + gf_vec_norm(&direction); + +#ifdef GPAC_USE_OGL_ES + vals[0] = direction.x; vals[1] = direction.y; vals[2] = direction.z; vals[3] = FIX_ONE; + glLightxv(iLight, GL_SPOT_DIRECTION, vals); + vals[0] = location.x; vals[1] = location.y; vals[2] = location.z; vals[3] = FIX_ONE; + glLightxv(iLight, GL_POSITION, vals); + glLightx(iLight, GL_CONSTANT_ATTENUATION, attenuation.x ? attenuation.x : FIX_ONE); + glLightx(iLight, GL_LINEAR_ATTENUATION, attenuation.y); + glLightx(iLight, GL_QUADRATIC_ATTENUATION, attenuation.z); + vals[0] = gf_mulfix(color.red, _intensity); vals[1] = gf_mulfix(color.green, _intensity); vals[2] = gf_mulfix(color.blue, _intensity); vals[3] = FIX_ONE; + glLightxv(iLight, GL_DIFFUSE, vals); + glLightxv(iLight, GL_SPECULAR, vals); + vals[0] = gf_mulfix(color.red, _ambientIntensity); vals[1] = gf_mulfix(color.green, _ambientIntensity); vals[2] = gf_mulfix(color.blue, _ambientIntensity); vals[3] = FIX_ONE; + glLightxv(iLight, GL_AMBIENT, vals); + + if (!_beamWidth) exp = FIX_ONE; + else if (_beamWidth>_cutOffAngle) exp = 0; + else { + exp = FIX_ONE - gf_cos(_beamWidth); + if (exp>FIX_ONE) exp = FIX_ONE; + } + glLightx(iLight, GL_SPOT_EXPONENT, exp*128); + glLightx(iLight, GL_SPOT_CUTOFF, gf_divfix(180*_cutOffAngle, GF_PI) ); +#else + vals[0] = FIX2FLT(direction.x); vals[1] = FIX2FLT(direction.y); vals[2] = FIX2FLT(direction.z); vals[3] = 1; + glLightfv(iLight, GL_SPOT_DIRECTION, vals); + vals[0] = FIX2FLT(location.x); vals[1] = FIX2FLT(location.y); vals[2] = FIX2FLT(location.z); vals[3] = 1; + glLightfv(iLight, GL_POSITION, vals); + glLightf(iLight, GL_CONSTANT_ATTENUATION, attenuation.x ? FIX2FLT(attenuation.x) : 1.0f); + glLightf(iLight, GL_LINEAR_ATTENUATION, FIX2FLT(attenuation.y)); + glLightf(iLight, GL_QUADRATIC_ATTENUATION, FIX2FLT(attenuation.z)); + vals[0] = FIX2FLT(color.red)*intensity; vals[1] = FIX2FLT(color.green)*intensity; vals[2] = FIX2FLT(color.blue)*intensity; vals[3] = 1; + glLightfv(iLight, GL_DIFFUSE, vals); + glLightfv(iLight, GL_SPECULAR, vals); + vals[0] = FIX2FLT(color.red)*ambientIntensity; vals[1] = FIX2FLT(color.green)*ambientIntensity; vals[2] = FIX2FLT(color.blue)*ambientIntensity; vals[3] = 1; + glLightfv(iLight, GL_AMBIENT, vals); + + //glLightf(iLight, GL_SPOT_EXPONENT, 0.5f * (beamWidth+0.001f) /*(Float) (0.5 * log(0.5) / log(cos(beamWidth)) ) */); + if (!beamWidth) exp = 1; + else if (beamWidth>cutOffAngle) exp = 0; + else { + exp = 1.0f - (Float) cos(beamWidth); + if (exp>1) exp = 1; + } + glLightf(iLight, GL_SPOT_EXPONENT, exp*128); + glLightf(iLight, GL_SPOT_CUTOFF, 180*cutOffAngle/FIX2FLT(GF_PI)); +#endif + + return 1; +} + +/*insert pointlight - returns 0 if too many lights*/ +Bool visual_3d_add_point_light(GF_VisualManager *visual, Fixed _ambientIntensity, SFVec3f attenuation, SFColor color, Fixed _intensity, SFVec3f location) +{ +#ifdef GPAC_USE_OGL_ES + Fixed vals[4]; +#else + Float vals[4], ambientIntensity, intensity; +#endif + u32 iLight; + + if (!visual->num_lights) glEnable(GL_LIGHTING); + if (visual->num_lights==visual->max_lights) return 0; + iLight = GL_LIGHT0 + visual->num_lights; + visual->num_lights++; + glEnable(iLight); + +#ifdef GPAC_USE_OGL_ES + vals[0] = location.x; vals[1] = location.y; vals[2] = location.z; vals[3] = FIX_ONE; + glLightxv(iLight, GL_POSITION, vals); + glLightx(iLight, GL_CONSTANT_ATTENUATION, attenuation.x ? attenuation.x : FIX_ONE); + glLightx(iLight, GL_LINEAR_ATTENUATION, attenuation.y); + glLightx(iLight, GL_QUADRATIC_ATTENUATION, attenuation.z); + vals[0] = gf_mulfix(color.red, _intensity); vals[1] = gf_mulfix(color.green, _intensity); vals[2] = gf_mulfix(color.blue, _intensity); vals[3] = FIX_ONE; + glLightxv(iLight, GL_DIFFUSE, vals); + glLightxv(iLight, GL_SPECULAR, vals); + vals[0] = gf_mulfix(color.red, _ambientIntensity); vals[1] = gf_mulfix(color.green, _ambientIntensity); vals[2] = gf_mulfix(color.blue, _ambientIntensity); vals[3] = FIX_ONE; + glLightxv(iLight, GL_AMBIENT, vals); + + glLightx(iLight, GL_SPOT_EXPONENT, 0); + glLightx(iLight, GL_SPOT_CUTOFF, INT2FIX(180) ); +#else + ambientIntensity = FIX2FLT(_ambientIntensity); + intensity = FIX2FLT(_intensity); + + vals[0] = FIX2FLT(location.x); vals[1] = FIX2FLT(location.y); vals[2] = FIX2FLT(location.z); vals[3] = 1; + glLightfv(iLight, GL_POSITION, vals); + + glLightf(iLight, GL_CONSTANT_ATTENUATION, attenuation.x ? FIX2FLT(attenuation.x) : 1.0f); + glLightf(iLight, GL_LINEAR_ATTENUATION, FIX2FLT(attenuation.y)); + glLightf(iLight, GL_QUADRATIC_ATTENUATION, FIX2FLT(attenuation.z)); + vals[0] = FIX2FLT(color.red)*intensity; vals[1] = FIX2FLT(color.green)*intensity; vals[2] = FIX2FLT(color.blue)*intensity; vals[3] = 1; + glLightfv(iLight, GL_DIFFUSE, vals); + glLightfv(iLight, GL_SPECULAR, vals); + vals[0] = FIX2FLT(color.red)*ambientIntensity; vals[1] = FIX2FLT(color.green)*ambientIntensity; vals[2] = FIX2FLT(color.blue)*ambientIntensity; vals[3] = 1; + glLightfv(iLight, GL_AMBIENT, vals); + + glLightf(iLight, GL_SPOT_EXPONENT, 0); + glLightf(iLight, GL_SPOT_CUTOFF, 180); +#endif + return 1; +} + +Bool visual_3d_add_directional_light(GF_VisualManager *visual, Fixed _ambientIntensity, SFColor color, Fixed _intensity, SFVec3f direction) +{ +#ifdef GPAC_USE_OGL_ES + Fixed vals[4]; +#else + Float vals[4], ambientIntensity, intensity; +#endif + u32 iLight; + if (!visual->num_lights) glEnable(GL_LIGHTING); + if (visual->num_lights==visual->max_lights) return 0; + iLight = GL_LIGHT0 + visual->num_lights; + visual->num_lights++; + glEnable(iLight); + + /*in case...*/ + gf_vec_norm(&direction); +#ifdef GPAC_USE_OGL_ES + vals[0] = -direction.x; vals[1] = -direction.y; vals[2] = -direction.z; vals[3] = 0; + glLightxv(iLight, GL_POSITION, vals); + vals[0] = gf_mulfix(color.red, _intensity); vals[1] = gf_mulfix(color.green, _intensity); vals[2] = gf_mulfix(color.blue, _intensity); vals[3] = FIX_ONE; + glLightxv(iLight, GL_DIFFUSE, vals); + glLightxv(iLight, GL_SPECULAR, vals); + vals[0] = gf_mulfix(color.red, _ambientIntensity); vals[1] = gf_mulfix(color.green, _ambientIntensity); vals[2] = gf_mulfix(color.blue, _ambientIntensity); vals[3] = FIX_ONE; + glLightxv(iLight, GL_AMBIENT, vals); + + glLightx(iLight, GL_CONSTANT_ATTENUATION, FIX_ONE); + glLightx(iLight, GL_LINEAR_ATTENUATION, 0); + glLightx(iLight, GL_QUADRATIC_ATTENUATION, 0); + glLightx(iLight, GL_SPOT_CUTOFF, INT2FIX(180) ); +#else + ambientIntensity = FIX2FLT(_ambientIntensity); + intensity = FIX2FLT(_intensity); + + vals[0] = -FIX2FLT(direction.x); vals[1] = -FIX2FLT(direction.y); vals[2] = -FIX2FLT(direction.z); vals[3] = 0; + glLightfv(iLight, GL_POSITION, vals); + vals[0] = FIX2FLT(color.red)*intensity; vals[1] = FIX2FLT(color.green)*intensity; vals[2] = FIX2FLT(color.blue)*intensity; vals[3] = 1; + glLightfv(iLight, GL_DIFFUSE, vals); + glLightfv(iLight, GL_SPECULAR, vals); + vals[0] = FIX2FLT(color.red)*ambientIntensity; vals[1] = FIX2FLT(color.green)*ambientIntensity; vals[2] = FIX2FLT(color.blue)*ambientIntensity; vals[3] = 1; + glLightfv(iLight, GL_AMBIENT, vals); + + glLightf(iLight, GL_CONSTANT_ATTENUATION, 1.0f); + glLightf(iLight, GL_LINEAR_ATTENUATION, 0); + glLightf(iLight, GL_QUADRATIC_ATTENUATION, 0); + glLightf(iLight, GL_SPOT_CUTOFF, 180); +#endif + return 1; +} + +void visual_3d_remove_last_light(GF_VisualManager *visual) +{ + if (visual->num_lights) { + glDisable(GL_LIGHT0+visual->num_lights-1); + visual->num_lights--; + } +} + +void visual_3d_clear_all_lights(GF_VisualManager *visual) +{ + u32 i; + for (i=visual->num_lights; i>0; i--) { + glDisable(GL_LIGHT0+i-1); + } + visual->num_lights = 0; + //glDisable(GL_LIGHTING); +} + +void visual_3d_set_fog(GF_VisualManager *visual, const char *type, SFColor color, Fixed density, Fixed visibility) +{ + +#ifndef GPAC_USE_TINYGL + +#ifdef GPAC_USE_OGL_ES + Fixed vals[4]; + glEnable(GL_FOG); + if (!type || !stricmp(type, "LINEAR")) glFogx(GL_FOG_MODE, GL_LINEAR); + else if (!stricmp(type, "EXPONENTIAL")) glFogx(GL_FOG_MODE, GL_EXP); + else if (!stricmp(type, "EXPONENTIAL2")) glFogx(GL_FOG_MODE, GL_EXP2); + glFogx(GL_FOG_DENSITY, density); + glFogx(GL_FOG_START, 0); + glFogx(GL_FOG_END, visibility); + vals[0] = color.red; vals[1] = color.green; vals[2] = color.blue; vals[3] = FIX_ONE; + glFogxv(GL_FOG_COLOR, vals); + glHint(GL_FOG_HINT, visual->compositor->high_speed ? GL_FASTEST : GL_NICEST); +#else + Float vals[4]; + glEnable(GL_FOG); + if (!type || !stricmp(type, "LINEAR")) glFogi(GL_FOG_MODE, GL_LINEAR); + else if (!stricmp(type, "EXPONENTIAL")) glFogi(GL_FOG_MODE, GL_EXP); + else if (!stricmp(type, "EXPONENTIAL2")) glFogi(GL_FOG_MODE, GL_EXP2); + glFogf(GL_FOG_DENSITY, FIX2FLT(density)); + glFogf(GL_FOG_START, 0); + glFogf(GL_FOG_END, FIX2FLT(visibility)); + vals[0] = FIX2FLT(color.red); vals[1] = FIX2FLT(color.green); vals[2] = FIX2FLT(color.blue); vals[3] = 1; + glFogfv(GL_FOG_COLOR, vals); + glHint(GL_FOG_HINT, visual->compositor->high_speed ? GL_FASTEST : GL_NICEST); +#endif + +#endif + +} + +void visual_3d_fill_rect(GF_VisualManager *visual, GF_Rect rc, SFColorRGBA color) +{ + glDisable(GL_BLEND | GL_LIGHTING | GL_TEXTURE_2D); + +#ifdef GPAC_USE_OGL_ES + glNormal3x(0, 0, FIX_ONE); + if (color.alpha!=FIX_ONE) glEnable(GL_BLEND); + glColor4x(color.red, color.green, color.blue, color.alpha); + { + Fixed v[8]; + u16 indices[3]; + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + + v[0] = rc.x; v[1] = rc.y; + v[2] = rc.x+rc.width; v[3] = rc.y-rc.height; + v[4] = rc.x+rc.width; v[5] = rc.y; + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FIXED, 0, v); + glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, indices); + + v[4] = rc.x; v[5] = rc.y-rc.height; + glVertexPointer(2, GL_FIXED, 0, v); + glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, indices); + + glDisableClientState(GL_VERTEX_ARRAY); + } +#else + glNormal3f(0, 0, 1); + if (color.alpha!=FIX_ONE) { + glEnable(GL_BLEND); + glColor4f(FIX2FLT(color.red), FIX2FLT(color.green), FIX2FLT(color.blue), FIX2FLT(color.alpha)); + } else { + glColor3f(FIX2FLT(color.red), FIX2FLT(color.green), FIX2FLT(color.blue)); + } + glBegin(GL_QUADS); + glVertex3f(FIX2FLT(rc.x), FIX2FLT(rc.y), 0); + glVertex3f(FIX2FLT(rc.x), FIX2FLT(rc.y-rc.height), 0); + glVertex3f(FIX2FLT(rc.x+rc.width), FIX2FLT(rc.y-rc.height), 0); + glVertex3f(FIX2FLT(rc.x+rc.width), FIX2FLT(rc.y), 0); + glEnd(); + + glDisable(GL_COLOR_MATERIAL | GL_COLOR_MATERIAL_FACE); +#endif + + glDisable(GL_BLEND); +} + +GF_Err compositor_3d_get_screen_buffer(GF_Compositor *compositor, GF_VideoSurface *fb, u32 depth_dump_mode) +{ + /*FIXME*/ + u32 i, hy; + char *tmp; + + fb->width = compositor->vp_width; + fb->height = compositor->vp_height; + + /*depthmap-only dump*/ + if (depth_dump_mode==1) { + +#ifdef GPAC_USE_OGL_ES + return GF_NOT_SUPPORTED; +#else + + fb->pitch = compositor->vp_width; /* multiply by 4 if float depthbuffer */ + +#ifndef GPAC_USE_TINYGL + fb->video_buffer = (char*)malloc(sizeof(char)* fb->pitch * fb->height); +#else + fb->video_buffer = (char*)malloc(sizeof(char)* 2 * fb->pitch * fb->height); +#endif + + fb->pixel_format = GF_PIXEL_GREYSCALE; +#ifndef GPAC_USE_TINYGL + glPixelTransferf(GL_DEPTH_SCALE, compositor->OGLDepthGain); + glPixelTransferf(GL_DEPTH_BIAS, compositor->OGLDepthOffset); +#endif + /*the following is the inverse z-transform of OpenGL*/ +#if 0 + /* GL_FLOAT for float depthbuffer */ + glReadPixels(compositor->vp_x, compositor->vp_y, fb->width, fb->height, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, fb->video_buffer); +#else + { + float *buff = (float *) malloc(sizeof(float)* fb->width * fb->height); + Fixed n = compositor->traverse_state->camera->z_near; + Fixed f = compositor->traverse_state->camera->z_far; + + glReadPixels(compositor->vp_x, compositor->vp_y, fb->width, fb->height, GL_DEPTH_COMPONENT, GL_FLOAT, buff); + + for (i=0; iheight*fb->width; i++) + fb->video_buffer[i] = (char) (255 * (1.0 - buff[i]) / (1 - buff[i]*(1-(n/f)))); + + free(buff); + + } + +#endif + +#endif /*GPAC_USE_OGL_ES*/ + } + + /* RGBDS or RGBD dump*/ + else if (depth_dump_mode==2 || depth_dump_mode==3){ +#ifdef GPAC_USE_OGL_ES + GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor]: RGB+Depth format not implemented in OpenGL ES\n")); + return GF_NOT_SUPPORTED; +#else + char *depth_data=NULL; + fb->pitch = compositor->vp_width*4; /* 4 bytes for each rgbds pixel */ + +#ifndef GPAC_USE_TINYGL + fb->video_buffer = (char*)malloc(sizeof(char)* fb->pitch * fb->height); +#else + fb->video_buffer = (char*)malloc(sizeof(char)* 2 * fb->pitch * fb->height); +#endif + + + +#ifndef GPAC_USE_TINYGL + + glReadPixels(0, 0, fb->width, fb->height, GL_RGBA, GL_UNSIGNED_BYTE, fb->video_buffer); + + glPixelTransferf(GL_DEPTH_SCALE, compositor->OGLDepthGain); + glPixelTransferf(GL_DEPTH_BIAS, compositor->OGLDepthOffset); + + depth_data = (char*) malloc(sizeof(char)*fb->width*fb->height); + glReadPixels(0, 0, fb->width, fb->height, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, depth_data); + + if (depth_dump_mode==2) { + u32 i; + fb->pixel_format = GF_PIXEL_RGBDS; + + /*this corresponds to the RGBDS ordering*/ + for (i=0; iheight*fb->width; i++) { + u8 ds; + /* erase lowest-weighted depth bit */ + u8 depth = depth_data[i] & 0xfe; + /*get alpha*/ + ds = (fb->video_buffer[i*4 + 3]); + /* if heaviest-weighted alpha bit is set (>128) , turn on shape bit*/ + if (ds & 0x80) depth |= 0x01; + fb->video_buffer[i*4+3] = depth; /*insert depth onto alpha*/ + } + /*this corresponds to RGBD ordering*/ + } else if (depth_dump_mode==3) { + u32 i; + fb->pixel_format = GF_PIXEL_RGBD; + for (i=0; iheight*fb->width; i++) + fb->video_buffer[i*4+3] = depth_data[i]; + } +#else + GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor]: RGB+Depth format not implemented in TinyGL\n")); + return GF_NOT_SUPPORTED; +#endif + +#endif /*GPAC_USE_OGL_ES*/ + + } else { + fb->pitch = 3*compositor->vp_width; + fb->video_buffer = (char*)malloc(sizeof(char) * fb->pitch * fb->height); + fb->pixel_format = GF_PIXEL_RGB_24; + + glReadPixels(compositor->vp_x, compositor->vp_y, fb->width, fb->height, GL_RGB, GL_UNSIGNED_BYTE, fb->video_buffer); + } + + /*flip image (openGL always handle image data bottom to top) */ + tmp = (char*)malloc(sizeof(char)*fb->pitch); + hy = fb->height/2; + for (i=0; ivideo_buffer+ i*fb->pitch, fb->pitch); + memcpy(fb->video_buffer + i*fb->pitch, fb->video_buffer + (fb->height - 1 - i) * fb->pitch, fb->pitch); + memcpy(fb->video_buffer + (fb->height - 1 - i) * fb->pitch, tmp, fb->pitch); + } + free(tmp); + return GF_OK; +} + +GF_Err compositor_3d_release_screen_buffer(GF_Compositor *compositor, GF_VideoSurface *framebuffer) +{ + free(framebuffer->video_buffer); + framebuffer->video_buffer = 0; + return GF_OK; +} + +#endif /*GPAC_DISABLE_3D*/ + + diff --git a/src/compositor/x3d_geometry.c b/src/compositor/x3d_geometry.c new file mode 100644 index 0000000..2b12c40 --- /dev/null +++ b/src/compositor/x3d_geometry.c @@ -0,0 +1,1013 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Compositor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "nodes_stacks.h" +#include "visual_manager.h" +#include "drawable.h" + +static void disk2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) +{ + if (gf_node_dirty_get(node)) { + Fixed a = ((X_Disk2D *) node)->outerRadius * 2; + drawable_reset_path(stack); + gf_path_add_ellipse(stack->path, 0, 0, a, a); + a = ((X_Disk2D *) node)->innerRadius * 2; + if (a) gf_path_add_ellipse(stack->path, 0, 0, a, a); + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); + } +} + +static void TraverseDisk2D(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + drawable_node_del(node); + return; + } + + disk2d_check_changes(node, stack, tr_state); + + switch (tr_state->traversing_mode) { +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + if (!stack->mesh) { + stack->mesh = new_mesh(); + /*FIXME - enable it with OpenGL-ES*/ + mesh_from_path(stack->mesh, stack->path); + } + visual_3d_draw_2d(stack, tr_state); + return; +#endif + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + drawable_finalize_sort(ctx, tr_state, NULL); + return; + } +} + +void compositor_init_disk2d(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, TraverseDisk2D); +} + +static void arc2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) +{ + if (gf_node_dirty_get(node)) { + drawable_reset_path(stack); + if (gf_node_get_tag(node)==TAG_X3D_Arc2D) { + X_Arc2D *a = (X_Arc2D *) node; + gf_path_add_arc(stack->path, a->radius, a->startAngle, a->endAngle, 0); + } else { + X_ArcClose2D *a = (X_ArcClose2D *) node; + gf_path_add_arc(stack->path, a->radius, a->startAngle, a->endAngle, !stricmp(a->closureType.buffer, "PIE") ? 2 : 1); + } + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); + } +} + +static void TraverseArc2D(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + if (is_destroy) { + drawable_node_del(node); + return; + } + + arc2d_check_changes(node, stack, tr_state); + + switch (tr_state->traversing_mode) { +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + if (!stack->mesh) { + stack->mesh = new_mesh(); + if (gf_node_get_tag(node)==TAG_X3D_Arc2D) { + mesh_get_outline(stack->mesh, stack->path); + } else { + mesh_from_path(stack->mesh, stack->path); + } + } + visual_3d_draw_2d(stack, tr_state); + return; +#endif + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); +#ifndef GPAC_DISABLE_3D + gf_bbox_from_rect(&tr_state->bbox, &tr_state->bounds); +#endif + return; + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + drawable_finalize_sort(ctx, tr_state, NULL); + return; + } +} + +void compositor_init_arc2d(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, TraverseArc2D); +} + +static void polyline2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) +{ + if (gf_node_dirty_get(node)) { + u32 i; + X_Polyline2D *a = (X_Polyline2D *) node; + drawable_reset_path(stack); + for (i=0; ilineSegments.count; i++) { + if (i) { + gf_path_add_line_to(stack->path, a->lineSegments.vals[i].x, a->lineSegments.vals[i].y); + } else { + gf_path_add_move_to(stack->path, a->lineSegments.vals[i].x, a->lineSegments.vals[i].y); + } + } + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); + } +} + +static void TraversePolyline2D(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + if (is_destroy) { + drawable_node_del(node); + return; + } + + polyline2d_check_changes(node, stack, tr_state); + + switch (tr_state->traversing_mode) { +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + if (!stack->mesh) { + stack->mesh = new_mesh(); + mesh_get_outline(stack->mesh, stack->path); + } + visual_3d_draw_2d(stack, tr_state); + return; +#endif + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + drawable_finalize_sort(ctx, tr_state, NULL); + return; + } +} + +void compositor_init_polyline2d(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, TraversePolyline2D); +} + +static void triangleset2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) +{ + if (gf_node_dirty_get(node)) { + u32 i, count; + X_TriangleSet2D *p = (X_TriangleSet2D *)node; + drawable_reset_path(stack); + count = p->vertices.count; + while (count%3) count--; + for (i=0; ipath, p->vertices.vals[i].x, p->vertices.vals[i].y); + gf_path_add_line_to(stack->path, p->vertices.vals[i+1].x, p->vertices.vals[i+1].y); + gf_path_add_line_to(stack->path, p->vertices.vals[i+2].x, p->vertices.vals[i+2].y); + gf_path_close(stack->path); + } + gf_node_dirty_clear(node, 0); + drawable_mark_modified(stack, tr_state); + } +} + +static void TraverseTriangleSet2D(GF_Node *node, void *rs, Bool is_destroy) +{ + DrawableContext *ctx; + Drawable *stack = (Drawable *)gf_node_get_private(node); + GF_TraverseState *tr_state = (GF_TraverseState *)rs; + + if (is_destroy) { + drawable_node_del(node); + return; + } + + triangleset2d_check_changes(node, stack, tr_state); + + switch (tr_state->traversing_mode) { +#ifndef GPAC_DISABLE_3D + case TRAVERSE_DRAW_3D: + if (!stack->mesh) { + SFColorRGBA col; + u32 i, count, idx; + GF_Vertex v1, v2, v3; + X_TriangleSet2D *p = (X_TriangleSet2D *)node; + + stack->mesh = new_mesh(); + stack->mesh->mesh_type = MESH_TRIANGLES; + col.red = col.green = col.blue = 0; col.alpha = FIX_ONE; + v1.color = MESH_MAKE_COL(col); + v1.normal.x = v1.normal.y = 0; + v1.normal.z = MESH_NORMAL_UNIT; + v1.pos.z = 0; + v3 = v2 = v1; + count = p->vertices.count; + while (count%3) count--; + for (i=0; imesh->v_count; + v1.pos.x = p->vertices.vals[i].x; + v1.pos.y = p->vertices.vals[i].y; + v2.pos.x = p->vertices.vals[i+1].x; + v2.pos.y = p->vertices.vals[i+1].y; + v3.pos.x = p->vertices.vals[i+2].x; + v3.pos.y = p->vertices.vals[i+2].y; + mesh_set_vertex_vx(stack->mesh, &v1); + mesh_set_vertex_vx(stack->mesh, &v2); + mesh_set_vertex_vx(stack->mesh, &v3); + gf_vec_diff(v2.pos, v2.pos, v1.pos); + gf_vec_diff(v3.pos, v3.pos, v1.pos); + v1.pos = gf_vec_cross(v2.pos, v3.pos); + if (v1.pos.z<0) { + mesh_set_triangle(stack->mesh, idx, idx+2, idx+1); + } else { + mesh_set_triangle(stack->mesh, idx, idx+1, idx+2); + } + } + stack->mesh->flags |= MESH_IS_2D; + mesh_update_bounds(stack->mesh); + } + visual_3d_draw_2d(stack, tr_state); + return; +#endif + case TRAVERSE_GET_BOUNDS: + gf_path_get_bounds(stack->path, &tr_state->bounds); + return; + case TRAVERSE_PICK: + drawable_pick(stack, tr_state); + return; + case TRAVERSE_SORT: +#ifndef GPAC_DISABLE_3D + if (tr_state->visual->type_3d) return; +#endif + ctx = drawable_init_context_mpeg4(stack, tr_state); + if (!ctx) return; + drawable_finalize_sort(ctx, tr_state, NULL); + return; + } +} + +void compositor_init_triangle_set2d(GF_Compositor *compositor, GF_Node *node) +{ + drawable_stack_new(compositor, node); + gf_node_set_callback_function(node, TraverseTriangleSet2D); +} + +#ifndef GPAC_DISABLE_3D + +static void build_polypoint2d(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state) +{ + u32 i; + SFColorRGBA col; + X_Polypoint2D *p = (X_Polypoint2D *)node; + + stack->mesh->mesh_type = MESH_POINTSET; + col.red = col.green = col.blue = 0; col.alpha = FIX_ONE; + for (i=0; ipoint.count; i++) { + mesh_set_point(stack->mesh, p->point.vals[i].x, p->point.vals[i].y, 0, col); + mesh_set_index(stack->mesh, stack->mesh->v_count-1); + } +} + +static void TraversePolypoint2D(GF_Node *node, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(node, rs, is_destroy, build_polypoint2d); +} + +void compositor_init_polypoint2d(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraversePolypoint2D); +} + +static void build_line_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state) +{ + u32 i, j, c_idx; + GenMFField *cols; + GF_Vertex vx; + Bool rgba_col; + SFColorRGBA rgba; + X_LineSet *p = (X_LineSet *)node; + X_Coordinate *c = (X_Coordinate *) p->coord; + + stack->mesh->mesh_type = MESH_LINESET; + + cols = NULL; + rgba_col = 0; + if (p->color) { + if (gf_node_get_tag(p->color)==TAG_X3D_ColorRGBA) { + rgba_col = 1; + cols = (GenMFField *) & ((X_ColorRGBA *) p->color)->color; + } else { + cols = (GenMFField *) & ((M_Color *) p->color)->color; + } + } + c_idx = 0; + memset(&vx, 0, sizeof(GF_Vertex)); + for (i=0; ivertexCount.count; i++) { + if (p->vertexCount.vals[i]<2) continue; + + for (j=0; j<(u32) p->vertexCount.vals[i]; j++) { + vx.pos = c->point.vals[c_idx]; + if (cols && (cols->count>c_idx)) { + if (rgba_col) { + rgba = ((MFColorRGBA *)cols)->vals[c_idx]; + } else { + rgba = gf_sg_sfcolor_to_rgba( ((MFColor *)cols)->vals[c_idx]); + } + vx.color = MESH_MAKE_COL(rgba); + } + mesh_set_vertex_vx(stack->mesh, &vx); + if (j) { + mesh_set_index(stack->mesh, stack->mesh->v_count-2); + mesh_set_index(stack->mesh, stack->mesh->v_count-1); + } + c_idx++; + if (c_idx==c->point.count) break; + } + } + if (cols) stack->mesh->flags |= MESH_HAS_COLOR; + mesh_update_bounds(stack->mesh); +} + +static void TraverseLineSet(GF_Node *node, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(node, rs, is_destroy, build_line_set); +} + + +void compositor_init_lineset(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseLineSet); +} + +static void BuildTriangleSet(GF_Mesh *mesh, GF_Node *_coords, GF_Node *_color, GF_Node *_txcoords, GF_Node *_normal, MFInt32 *indices, Bool normalPerVertex, Bool ccw, Bool solid) +{ + u32 i, count, generate_tx; + GF_Vertex vx; + GenMFField *cols; + MFVec3f *norms; + MFVec2f *txcoords; + Bool rgba_col; + SFColorRGBA rgba; + X_Coordinate *c = (X_Coordinate *) _coords; + + mesh_reset(mesh); + + cols = NULL; + rgba_col = 0; + if (_color) { + if (gf_node_get_tag(_color)==TAG_X3D_ColorRGBA) { + rgba_col = 1; + cols = (GenMFField *) & ((X_ColorRGBA *) _color)->color; + } else { + cols = (GenMFField *) & ((M_Color *) _color)->color; + } + } + norms = NULL; + if (_normal) norms = & ((M_Normal *)_normal)->vector; + txcoords = NULL; + generate_tx = 0; + /*FIXME - this can't work with multitexturing*/ + if (_txcoords) { + switch (gf_node_get_tag(_txcoords)) { + case TAG_X3D_TextureCoordinate: + case TAG_MPEG4_TextureCoordinate: + txcoords = & ((M_TextureCoordinate *)_txcoords)->point; + break; + case TAG_X3D_TextureCoordinateGenerator: + generate_tx = 1; + break; + } + } + + if (indices) { + count = indices->count; + } else { + count = c->point.count; + } + while (count%3) count--; + memset(&vx, 0, sizeof(GF_Vertex)); + for (i=0; icount<=i) return; + idx = indices->vals[i]; + } else { + idx = i; + } + vx.pos = c->point.vals[idx]; + if (cols && (cols->count>idx)) { + if (rgba_col) { + rgba = ((MFColorRGBA *)cols)->vals[idx]; + } else { + rgba = gf_sg_sfcolor_to_rgba( ((MFColor *)cols)->vals[idx]); + } + vx.color = MESH_MAKE_COL(rgba); + } + if (norms && (norms->count>idx)) { + SFVec3f n = norms->vals[idx]; + gf_vec_norm(&n); + MESH_SET_NORMAL(vx, n); + } + if (txcoords) { + if (txcoords->count>idx) vx.texcoords = txcoords->vals[idx]; + } + /*X3D says nothing about default texture mapping here...*/ + else if (!generate_tx) { + switch (i%3) { + case 2: vx.texcoords.x = FIX_ONE; vx.texcoords.y = 0; break; + case 1: vx.texcoords.x = FIX_ONE/2; vx.texcoords.y = FIX_ONE; break; + case 0: vx.texcoords.x = 0; vx.texcoords.y = 0; break; + } + } + mesh_set_vertex_vx(mesh, &vx); + } + for (i=0; iv_count; i+=3) { + mesh_set_triangle(mesh, i, i+1, i+2); + } + if (generate_tx) mesh_generate_tex_coords(mesh, _txcoords); + + if (!ccw) mesh->flags |= MESH_IS_CW; + if (cols) mesh->flags |= MESH_HAS_COLOR; + if (rgba_col) mesh->flags |= MESH_HAS_ALPHA; + if (!_normal) mesh_recompute_normals(mesh); + if (solid) mesh->flags |= MESH_IS_SOLID; + mesh_update_bounds(mesh); + gf_mesh_build_aabbtree(mesh); +} + +static void build_triangle_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state) +{ + X_TriangleSet *ts = (X_TriangleSet *)node; + if (!ts->coord) return; + BuildTriangleSet(stack->mesh, ts->coord, ts->color, ts->texCoord, ts->normal, NULL, ts->normalPerVertex, ts->ccw, ts->solid); +} + +static void TraverseTriangleSet(GF_Node *node, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(node, rs, is_destroy, build_triangle_set); +} + + +void compositor_init_triangle_set(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseTriangleSet); +} + + +static void build_indexed_triangle_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state) +{ + X_IndexedTriangleSet *its = (X_IndexedTriangleSet*)node; + if (!its->coord) return; + BuildTriangleSet(stack->mesh, its->coord, its->color, its->texCoord, its->normal, &its->index, its->normalPerVertex, its->ccw, its->solid); +} + +static void TraverseIndexedTriangleSet(GF_Node *node, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(node, rs, is_destroy, build_indexed_triangle_set); +} + +static void ITS_SetIndex(GF_Node *node) +{ + X_IndexedTriangleSet *its = (X_IndexedTriangleSet*)node; + gf_sg_vrml_field_copy(&its->index, &its->set_index, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&its->set_index, GF_SG_VRML_MFINT32); +} + +void compositor_init_indexed_triangle_set(GF_Compositor *compositor, GF_Node *node) +{ + X_IndexedTriangleSet *its = (X_IndexedTriangleSet*)node; + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseIndexedTriangleSet); + its->on_set_index = ITS_SetIndex; +} + + +static void BuildTriangleStripSet(GF_Mesh *mesh, GF_Node *_coords, GF_Node *_color, GF_Node *_txcoords, GF_Node *_normal, MFInt32 *stripList, MFInt32 *indices, Bool normalPerVertex, Bool ccw, Bool solid) +{ + u32 strip, i, cur_idx, generate_tx; + GF_Vertex vx; + GenMFField *cols; + MFVec3f *norms; + MFVec2f *txcoords; + Bool rgba_col; + SFColorRGBA rgba; + X_Coordinate *c = (X_Coordinate *) _coords; + + mesh_reset(mesh); + + cols = NULL; + rgba_col = 0; + if (_color) { + if (gf_node_get_tag(_color)==TAG_X3D_ColorRGBA) { + rgba_col = 1; + cols = (GenMFField *) & ((X_ColorRGBA *) _color)->color; + } else { + cols = (GenMFField *) & ((M_Color *) _color)->color; + } + } + norms = NULL; + if (_normal) norms = & ((M_Normal *)_normal)->vector; + txcoords = NULL; + generate_tx = 0; + /*FIXME - this can't work with multitexturing*/ + if (_txcoords) { + switch (gf_node_get_tag(_txcoords)) { + case TAG_X3D_TextureCoordinate: + case TAG_MPEG4_TextureCoordinate: + txcoords = & ((M_TextureCoordinate *)_txcoords)->point; + break; + case TAG_X3D_TextureCoordinateGenerator: + generate_tx = 1; + break; + } + } + memset(&vx, 0, sizeof(GF_Vertex)); + cur_idx = 0; + for (strip= 0; stripcount; strip++) { + u32 start_idx = mesh->v_count; + if (stripList->vals[strip] < 3) continue; + + for (i=0; i<(u32) stripList->vals[strip]; i++) { + u32 idx; + if (indices) { + if (indices->count<=cur_idx) return; + if (indices->vals[cur_idx] == -1) { + GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[X3D] bad formatted X3D triangle strip\n")); + return; + } + idx = indices->vals[cur_idx]; + } else { + idx = cur_idx; + } + + vx.pos = c->point.vals[idx]; + + if (cols && (cols->count>idx)) { + if (rgba_col) { + rgba = ((MFColorRGBA *)cols)->vals[idx]; + } else { + rgba = gf_sg_sfcolor_to_rgba( ((MFColor *)cols)->vals[idx]); + } + vx.color = MESH_MAKE_COL(rgba); + } + /*according to X3D spec, if normal field is set, it is ALWAYS as normal per vertex*/ + if (norms && (norms->count>idx)) { + SFVec3f n = norms->vals[idx]; + gf_vec_norm(&n); + MESH_SET_NORMAL(vx, n); + } + if (txcoords) { + if (txcoords->count>idx) vx.texcoords = txcoords->vals[idx]; + } + /*X3D says nothing about default texture mapping here...*/ + else if (!generate_tx) { + switch (idx%3) { + case 2: vx.texcoords.x = FIX_ONE; vx.texcoords.y = 0; break; + case 1: vx.texcoords.x = FIX_ONE/2; vx.texcoords.y = FIX_ONE; break; + case 0: vx.texcoords.x = 0; vx.texcoords.y = 0; break; + } + } + if (i>2) { + /*duplicate last 2 vertices (we really need independent vertices to handle normals per face)*/ + mesh_set_vertex_vx(mesh, &mesh->vertices[mesh->v_count-2]); + mesh_set_vertex_vx(mesh, &mesh->vertices[mesh->v_count-2]); + } + mesh_set_vertex_vx(mesh, &vx); + + cur_idx ++; + if (indices) { + if (cur_idx>=indices->count) break; + } else if (cur_idx==c->point.count) break; + + } + for (i=start_idx; iv_count; i+=3) { + mesh_set_triangle(mesh, i, i+1, i+2); + } + /*get rid of -1*/ + if (indices && (cur_idxcount) && (indices->vals[cur_idx]==-1)) cur_idx++; + } + if (generate_tx) mesh_generate_tex_coords(mesh, _txcoords); + + if (cols) mesh->flags |= MESH_HAS_COLOR; + if (rgba_col) mesh->flags |= MESH_HAS_ALPHA; + if (_normal) { + if (!ccw) mesh->flags |= MESH_IS_CW; + } + /*reorder everything to CCW*/ + else { + u32 cur_face = 0; + mesh_recompute_normals(mesh); + for (i=0; ii_count; i+= 3) { + if ((ccw && (cur_face%2)) || (!ccw && !(cur_face%2))) { + SFVec3f v; + u32 idx; + MESH_GET_NORMAL(v, mesh->vertices[mesh->indices[i]]); + v = gf_vec_scale(v,-1); + MESH_SET_NORMAL(mesh->vertices[mesh->indices[i]], v); + mesh->vertices[mesh->indices[i+1]].normal = mesh->vertices[mesh->indices[i]].normal; + mesh->vertices[mesh->indices[i+2]].normal = mesh->vertices[mesh->indices[i]].normal; + idx = mesh->indices[i+2]; + mesh->indices[i+2] = mesh->indices[i+1]; + mesh->indices[i+1] = idx; + } + cur_face++; + } + if (normalPerVertex) { + cur_face = 0; + for (strip=0; stripcount; strip++) { + SFVec3f n_0, n_1, n_2, n_avg; + u32 nb_face; + if (stripList->vals[strip] < 3) continue; + if (stripList->vals[strip] <= 3) { cur_face ++; continue; } + + /*first face normal*/ + MESH_GET_NORMAL(n_0, mesh->vertices[mesh->indices[3*cur_face]]); + /*second face normal*/ + MESH_GET_NORMAL(n_1, mesh->vertices[mesh->indices[3*(cur_face+1)]]); + + gf_vec_add(n_avg, n_0, n_1); + gf_vec_norm(&n_avg); + /*assign to second point of first face and first of second face*/ + MESH_SET_NORMAL(mesh->vertices[mesh->indices[3*cur_face+1]], n_avg); + MESH_SET_NORMAL(mesh->vertices[mesh->indices[3*(cur_face+1)]], n_avg); + nb_face = stripList->vals[strip] - 2; + cur_face++; + for (i=1; ivertices[mesh->indices[3*cur_face + 1]]); + gf_vec_add(n_avg, n_0, n_1); + gf_vec_add(n_avg, n_avg, n_2); + gf_vec_norm(&n_avg); + /*last of prev face*/ + MESH_SET_NORMAL(mesh->vertices[mesh->indices[3*cur_face - 1]], n_avg); + /*second of current face*/ + mesh->vertices[mesh->indices[3*cur_face + 1]].normal = mesh->vertices[mesh->indices[3*cur_face - 1]].normal; + /*first of next face*/ + mesh->vertices[mesh->indices[3*cur_face + 3]].normal = mesh->vertices[mesh->indices[3*cur_face - 1]].normal; + n_0 = n_1; + n_1 = n_2; + cur_face++; + } + } + } + } + if (solid) mesh->flags |= MESH_IS_SOLID; + mesh_update_bounds(mesh); + gf_mesh_build_aabbtree(mesh); +} + +static void build_triangle_strip_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state) +{ + X_TriangleStripSet *tss = (X_TriangleStripSet *)node; + if (!tss->coord) return; + BuildTriangleStripSet(stack->mesh, tss->coord, tss->color, tss->texCoord, tss->normal, &tss->stripCount, NULL, tss->normalPerVertex, tss->ccw, tss->solid); +} + +static void TraverseTriangleStripSet(GF_Node *node, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(node, rs, is_destroy, build_triangle_strip_set); +} + +void compositor_init_triangle_strip_set(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseTriangleStripSet); +} + +static void build_indexed_triangle_strip_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state) +{ + MFInt32 stripList; + u32 i, nb_strips; + X_IndexedTriangleStripSet *itss = (X_IndexedTriangleStripSet *)node; + + if (!itss->coord) return; + + stripList.count = 0; stripList.vals = NULL; + nb_strips = 0; + for (i=0; iindex.count; i++) { + if (itss->index.vals[i]==-1) { + if (nb_strips>=3) { + u32 *out_nb; + gf_sg_vrml_mf_append(&stripList, GF_SG_VRML_MFINT32, (void **) &out_nb); + *out_nb = nb_strips; + } + nb_strips = 0; + } else { + nb_strips++; + } + } + if (nb_strips>=3) { + u32 *out_nb; + gf_sg_vrml_mf_append(&stripList, GF_SG_VRML_MFINT32, (void **) &out_nb); + *out_nb = nb_strips; + } + BuildTriangleStripSet(stack->mesh, itss->coord, itss->color, itss->texCoord, itss->normal, &stripList, &itss->index, itss->normalPerVertex, itss->ccw, itss->solid); + gf_sg_vrml_mf_reset(&stripList, GF_SG_VRML_MFINT32); +} + +static void TraverseIndexedTriangleStripSet(GF_Node *node, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(node, rs, is_destroy, build_indexed_triangle_strip_set); +} + +static void ITSS_SetIndex(GF_Node *node) +{ + X_IndexedTriangleStripSet *itss = (X_IndexedTriangleStripSet*)node; + gf_sg_vrml_field_copy(&itss->index, &itss->set_index, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&itss->set_index, GF_SG_VRML_MFINT32); +} + +void compositor_init_indexed_triangle_strip_set(GF_Compositor *compositor, GF_Node *node) +{ + X_IndexedTriangleStripSet *itss = (X_IndexedTriangleStripSet*)node; + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseIndexedTriangleStripSet); + itss->on_set_index = ITSS_SetIndex; +} + + +static void BuildTriangleFanSet(GF_Mesh *mesh, GF_Node *_coords, GF_Node *_color, GF_Node *_txcoords, GF_Node *_normal, MFInt32 *fanList, MFInt32 *indices, Bool normalPerVertex, Bool ccw, Bool solid) +{ + u32 fan, i, cur_idx, generate_tx; + GF_Vertex vx; + GenMFField *cols; + MFVec3f *norms; + MFVec2f *txcoords; + Bool rgba_col; + SFColorRGBA rgba; + X_Coordinate *c = (X_Coordinate *) _coords; + mesh_reset(mesh); + + cols = NULL; + rgba_col = 0; + if (_color) { + if (gf_node_get_tag(_color)==TAG_X3D_ColorRGBA) { + rgba_col = 1; + cols = (GenMFField *) & ((X_ColorRGBA *) _color)->color; + } else { + cols = (GenMFField *) & ((M_Color *) _color)->color; + } + } + norms = NULL; + if (_normal) norms = & ((M_Normal *)_normal)->vector; + txcoords = NULL; + generate_tx = 0; + /*FIXME - this can't work with multitexturing*/ + if (_txcoords) { + switch (gf_node_get_tag(_txcoords)) { + case TAG_X3D_TextureCoordinate: + case TAG_MPEG4_TextureCoordinate: + txcoords = & ((M_TextureCoordinate *)_txcoords)->point; + break; + case TAG_X3D_TextureCoordinateGenerator: + generate_tx = 1; + break; + } + } + + memset(&vx, 0, sizeof(GF_Vertex)); + cur_idx = 0; + for (fan= 0; fancount; fan++) { + u32 start_idx = mesh->v_count; + if (fanList->vals[fan] < 3) continue; + + for (i=0; i<(u32) fanList->vals[fan]; i++) { + u32 idx; + if (indices) { + if (indices->count<=cur_idx) return; + if (indices->vals[cur_idx] == -1) { + GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[X3D] bad formatted X3D triangle set\n")); + return; + } + idx = indices->vals[cur_idx]; + } else { + idx = cur_idx; + } + vx.pos = c->point.vals[idx]; + + if (cols && (cols->count>idx)) { + if (rgba_col) { + rgba = ((MFColorRGBA *)cols)->vals[idx]; + } else { + rgba = gf_sg_sfcolor_to_rgba( ((MFColor *)cols)->vals[idx]); + } + vx.color = MESH_MAKE_COL(rgba); + } + /*according to X3D spec, if normal field is set, it is ALWAYS as normal per vertex*/ + if (norms && (norms->count>idx)) { + SFVec3f n = norms->vals[idx]; + gf_vec_norm(&n); + MESH_SET_NORMAL(vx, n); + } + if (txcoords) { + if (txcoords->count>idx) vx.texcoords = txcoords->vals[idx]; + } + /*X3D says nothing about default texture mapping here...*/ + else if (!generate_tx) { + switch (idx%3) { + case 2: vx.texcoords.x = FIX_ONE; vx.texcoords.y = 0; break; + case 1: vx.texcoords.x = FIX_ONE/2; vx.texcoords.y = FIX_ONE; break; + case 0: vx.texcoords.x = 0; vx.texcoords.y = 0; break; + } + } + mesh_set_vertex_vx(mesh, &vx); + + cur_idx ++; + if (indices) { + if (cur_idx>=indices->count) break; + } else if (cur_idx==c->point.count) break; + + if (i>1) { + mesh_set_vertex_vx(mesh, &mesh->vertices[start_idx]); + mesh_set_vertex_vx(mesh, &vx); + } + } + for (i=start_idx; iv_count; i+=3) { + mesh_set_triangle(mesh, i, i+1, i+2); + } + /*get rid of -1*/ + if (indices && (cur_idxcount) && (indices->vals[cur_idx]==-1)) cur_idx++; + } + if (generate_tx) mesh_generate_tex_coords(mesh, _txcoords); + + if (cols) mesh->flags |= MESH_HAS_COLOR; + if (rgba_col) mesh->flags |= MESH_HAS_ALPHA; + if (!ccw) mesh->flags |= MESH_IS_CW; + if (!_normal) { + mesh_recompute_normals(mesh); + if (normalPerVertex) { + u32 cur_face = 0; + for (fan=0; fancount; fan++) { + SFVec3f n_0, n_1, n_avg, n_tot; + u32 nb_face, start_face; + if (fanList->vals[fan] < 3) continue; + if (fanList->vals[fan] == 3) { cur_face++; continue; } + + start_face = cur_face; + + /*first face normal*/ + MESH_GET_NORMAL(n_0, mesh->vertices[mesh->indices[3*cur_face]]); + n_tot = n_0; + cur_face++; + nb_face = fanList->vals[fan] - 2; + for (i=1; ivertices[mesh->indices[3*cur_face + 1]]); + gf_vec_add(n_avg, n_0, n_1); + gf_vec_norm(&n_avg); + MESH_SET_NORMAL(mesh->vertices[mesh->indices[3*cur_face + 1]], n_avg); + gf_vec_add(n_tot, n_tot, n_1); + n_0 = n_1; + cur_face++; + } + /*and assign center normal*/ + gf_vec_norm(&n_tot); + for (i=0; ivertices[mesh->indices[3*(i+start_face)]], n_tot); + } + } + } + } + if (solid) mesh->flags |= MESH_IS_SOLID; + mesh_update_bounds(mesh); + gf_mesh_build_aabbtree(mesh); +} + +static void build_triangle_fan_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state) +{ + X_TriangleFanSet *tfs = (X_TriangleFanSet *)node; + if (!tfs->coord) return; + BuildTriangleFanSet(stack->mesh, tfs->coord, tfs->color, tfs->texCoord, tfs->normal, &tfs->fanCount, NULL, tfs->normalPerVertex, tfs->ccw, tfs->solid); +} + +static void TraverseTriangleFanSet(GF_Node *node, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(node, rs, is_destroy, build_triangle_fan_set); +} + +void compositor_init_triangle_fan_set(GF_Compositor *compositor, GF_Node *node) +{ + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseTriangleFanSet); +} + +static void build_indexed_triangle_fan_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state) +{ + MFInt32 fanList; + u32 i, nb_fans; + X_IndexedTriangleFanSet *itfs = (X_IndexedTriangleFanSet *)node; + gf_node_dirty_clear(node, 0); + if (!itfs->coord) return; + + fanList.count = 0; fanList.vals = NULL; + nb_fans = 0; + for (i=0; iindex.count; i++) { + if (itfs->index.vals[i]==-1) { + if (nb_fans>=3) { + u32 *out_nb; + gf_sg_vrml_mf_append(&fanList, GF_SG_VRML_MFINT32, (void **) &out_nb); + *out_nb = nb_fans; + } + nb_fans = 0; + } else { + nb_fans++; + } + } + if (nb_fans>=3) { + u32 *out_nb; + gf_sg_vrml_mf_append(&fanList, GF_SG_VRML_MFINT32, (void **) &out_nb); + *out_nb = nb_fans; + } + BuildTriangleFanSet(stack->mesh, itfs->coord, itfs->color, itfs->texCoord, itfs->normal, &fanList, &itfs->index, itfs->normalPerVertex, itfs->ccw, itfs->solid); + gf_sg_vrml_mf_reset(&fanList, GF_SG_VRML_MFINT32); +} + +static void TraverseIndexedTriangleFanSet(GF_Node *node, void *rs, Bool is_destroy) +{ + drawable_3d_base_traverse(node, rs, is_destroy, build_indexed_triangle_fan_set); +} + +static void ITFS_SetIndex(GF_Node *node) +{ + X_IndexedTriangleFanSet *itfs = (X_IndexedTriangleFanSet *)node; + gf_sg_vrml_field_copy(&itfs->index, &itfs->set_index, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&itfs->set_index, GF_SG_VRML_MFINT32); +} + +void compositor_init_indexed_triangle_fan_set(GF_Compositor *compositor, GF_Node *node) +{ + X_IndexedTriangleFanSet *itfs = (X_IndexedTriangleFanSet *)node; + drawable_3d_new(node); + gf_node_set_callback_function(node, TraverseIndexedTriangleFanSet); + itfs->on_set_index = ITFS_SetIndex; +} + +#endif /*GPAC_DISABLE_3D*/ diff --git a/src/dir_info b/src/dir_info new file mode 100644 index 0000000..fed8895 --- /dev/null +++ b/src/dir_info @@ -0,0 +1,47 @@ +This is a short overview of the gpac library source repository. + +gpac/src/bifs + BInary Format for Scene coding (decoder and encoder) (BIFS tables are with MPEG4Gen application) + +gpac/src/ietf + Small RTP/RTSP/SDP library, plus media packetizers. + +gpac/src/isomedia + IsoMedia File Format - features file reading/writing/editing, precise interleaving, hint track creation +and supports movie fragments (read/write). Includes 3GPP/3GPP2 features and simple AVC support - JPEG2000 support to be done. + +gpac/src/mcrypt + Port of libmcrypt into gpac for cryptographic tool. Currently only AES-128 CTR mode is used, but the other tools will likely be usefull for IPMPX + +gpac/src/media_tools + media tools for authoring: ISMA & 3GPP tools, media importing and exporting, hinting + +gpac/src/odf + MPEG-4 Object Descriptor Framework: encoding/decoding of all descriptors, OD codec and OCI codec + +gpac/src/compositor + Compositor for 2D & 3D drawing - handles MPEG-4, X3D/VRML and SVG. + +gpac/src/scene_manager + memory representation of the scene, importers (BT/XMT/SWF/QT), dumpers and encoding + +gpac/src/scenegraph + Scene Graph API (MPEG4/VRML/X3D/SVG) + +gpac/src/terminal + Client application engine. The guts of the gpac client performing stream synchronization and +media object setup, control and decoding. All decoders / network services are abstracted through plugins + +gpac/src/utils + All generic objects used throughout the lib (list, bitstream, thread, mutex...) +The OS specific files are in dedicated sub_folders (eg ./Tools/w32 for all windows code, ...) +porting the lib means barely porting these files and updating the makefile + +gpac/include/gpac + all exported files of the lib (high level APIs). Development headers are + +gpac/include/gpac/internal + all development files of the lib (low level access). + +gpac/include/gpac/modules + all module APIs defined in GPAC. diff --git a/src/ietf/rtcp.c b/src/ietf/rtcp.c new file mode 100644 index 0000000..66a178d --- /dev/null +++ b/src/ietf/rtcp.c @@ -0,0 +1,577 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#ifndef _WIN32_WCE +#include +#endif + +GF_EXPORT +u32 gf_rtp_read_rtcp(GF_RTPChannel *ch, char *buffer, u32 buffer_size) +{ + GF_Err e; + u32 res; + + //only if the socket exist (otherwise RTSP interleaved channel) + if (!ch || !ch->rtcp) return 0; + + e = gf_sk_receive(ch->rtcp, buffer, buffer_size, 0, &res); + if (e) return 0; + return res; +} + +GF_EXPORT +GF_Err gf_rtp_decode_rtcp(GF_RTPChannel *ch, char *pck, u32 pck_size) +{ + GF_RTCPHeader rtcp_hdr; + GF_BitStream *bs; + char sdes_buffer[300]; + u32 i, sender_ssrc, cur_ssrc, val, sdes_type, sdes_len, res, first, nb_bytes, nb_pck; + GF_Err e = GF_OK; + + //bad RTCP packet + if (pck_size < 4 ) return GF_NON_COMPLIANT_BITSTREAM; + bs = gf_bs_new(pck, pck_size, GF_BITSTREAM_READ); + + first = 1; + while (pck_size) { + //global header + rtcp_hdr.Version = gf_bs_read_int(bs, 2); + if (rtcp_hdr.Version != 2 ) { + gf_bs_del(bs); + return GF_NOT_SUPPORTED; + } + rtcp_hdr.Padding = gf_bs_read_int(bs, 1); + rtcp_hdr.Count = gf_bs_read_int(bs, 5); + rtcp_hdr.PayloadType = gf_bs_read_u8(bs); + rtcp_hdr.Length = gf_bs_read_u16(bs); + + //check pck size + if (pck_size < (u32) (rtcp_hdr.Length + 1) * 4) { + gf_bs_del(bs); + //we return OK + return GF_CORRUPTED_DATA; + } + //substract this RTCP pck size + pck_size -= (rtcp_hdr.Length + 1) * 4; + //all RTCP are Compounds (>1 pck), the first SHALL be SR or RR without padding + if (first) { + if ( ( (rtcp_hdr.PayloadType!=200) && (rtcp_hdr.PayloadType!=201) ) + || rtcp_hdr.Padding + || !pck_size + ) { + gf_bs_del(bs); + return GF_CORRUPTED_DATA; + } + first = 0; + } + + //specific extensions + switch (rtcp_hdr.PayloadType) { + //Sender report - we assume there's only one sender + case 200: + /*sender ssrc*/ + sender_ssrc = gf_bs_read_u32(bs); + rtcp_hdr.Length -= 1; + /*not for us...*/ + if (ch->SenderSSRC && (ch->SenderSSRC != sender_ssrc)) break; + + if (ch->first_SR) { + ch->first_SR = 0; + gf_rtp_get_next_report_time(ch); + ch->SenderSSRC = sender_ssrc; + } + ch->last_report_time = gf_rtp_get_report_time(); + + ch->last_SR_NTP_sec = gf_bs_read_u32(bs); + ch->last_SR_NTP_frac = gf_bs_read_u32(bs); + ch->last_SR_rtp_time = gf_bs_read_u32(bs); + nb_pck = gf_bs_read_u32(bs); + nb_bytes = gf_bs_read_u32(bs); + + rtcp_hdr.Length -= 5; + +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= (GF_LOG_DEBUG)) && (gf_log_get_tools() & (GF_LOG_RTP))) { +#ifndef _WIN32_WCE + time_t gtime = ch->last_SR_NTP_sec - GF_NTP_SEC_1900_TO_1970; + const char *ascTime = asctime(gmtime(>ime)); +#else + const char *ascTime = "Not Available"; +#endif + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] RTCP-SR\t%d\t%d\t%d\t%d\t%s\n", + ch->SenderSSRC, + ch->last_SR_rtp_time, + ch->total_pck, + ch->total_bytes, + ascTime + )); + } +#endif + + //common encoding for SR and RR + goto process_reports; + + + case 201: + //sender ssrc + sender_ssrc = gf_bs_read_u32(bs); + rtcp_hdr.Length -= 1; + +process_reports: + //process all reports - we actually don't since we do not handle sources + //to add + for (i=0; iSenderSSRC == cur_ssrc) { + e = GF_EOS; + break; + } + } + //extra info - skip it + while (rtcp_hdr.Length) { + gf_bs_read_u32(bs); + rtcp_hdr.Length -= 1; + } + break; +/* + //APP packet + case 204: + + + //sender ssrc + sender_ssrc = gf_bs_read_u32(bs); + //ASCI 4 char type + gf_bs_read_u8(bs); + gf_bs_read_u8(bs); + gf_bs_read_u8(bs); + gf_bs_read_u8(bs); + + rtcp_hdr.Length -= 2; + + //till endd of pck + gf_bs_read_data(bs, sdes_buffer, rtcp_hdr.Length*4); + rtcp_hdr.Length = 0; + break; +*/ + default: + //read all till end + gf_bs_read_data(bs, sdes_buffer, rtcp_hdr.Length*4); + rtcp_hdr.Length = 0; + break; + } + //WE SHALL CONSUME EVERYTHING otherwise the packet is bad + if (rtcp_hdr.Length) { + gf_bs_del(bs); + return GF_CORRUPTED_DATA; + } + } + + gf_bs_del(bs); + return e; +} + +u32 gf_rtp_get_ntp_frac(u32 sec, u32 frac) +{ + return ( ((sec & 0x0000ffff) << 16) | ((frac & 0xffff0000) >> 16)); +} + +static u32 RTCP_FormatReport(GF_RTPChannel *ch, GF_BitStream *bs, u32 NTP_Time) +{ + u32 length, is_sr, sec, frac, expected, val, size; + s32 extended, expect_diff, loss_diff; + Double f; + + is_sr = ch->pck_sent_since_last_sr ? 1 : 0; + + //common header + //version + gf_bs_write_int(bs, 2, 2); + //padding - reports are aligned + gf_bs_write_int(bs, 0, 1); + //count - only one for now in RR, 0 in sender mode + gf_bs_write_int(bs, !is_sr, 5); + //if we have sent stuff send an SR, otherwise an RR. We need to determine whether + //we are active or not + //type + gf_bs_write_u8(bs, is_sr ? 200 : 201); + //length = (num of 32bit words in full pck) - 1 + //we're updating only one ssrc for now in RR and none in SR + length = is_sr ? 6 : (1 + 6 * 1); + gf_bs_write_u16(bs, length); + + //sender SSRC + gf_bs_write_u32(bs, ch->SSRC); + + size = 8; + + gf_net_get_ntp(&sec, &frac); + + //SenderReport part + if (is_sr) { + //sender time + gf_bs_write_u32(bs, sec); + gf_bs_write_u32(bs, frac); + //RTP time at this time + f = 1000 * (sec - ch->last_pck_ntp_sec); + f += ((frac - ch->last_pck_ntp_frac) >> 4) / 0x10000; + f /= 1000; + f *= ch->TimeScale; + val = (u32) f + ch->last_pck_ts; + gf_bs_write_u32(bs, val); + //num pck sent + gf_bs_write_u32(bs, ch->num_pck_sent); + //num payload bytes sent + gf_bs_write_u32(bs, ch->num_payload_bytes); + + + size += 20; + //nota: as we only support single-way channels we are done for SR... + return size; + } + //loop through all our sources (1) and send information... + gf_bs_write_u32(bs, ch->SenderSSRC); + + //Fraction lost and cumulative lost + extended = ( (ch->num_sn_loops << 16) | ch->last_pck_sn); + expected = extended - ch->rtp_first_SN; + expect_diff = expected - ch->tot_num_pck_expected; + loss_diff = expect_diff - ch->last_num_pck_rcv; + + if (!expect_diff || (loss_diff <= 0)) loss_diff = 0; + else loss_diff = (loss_diff<<8) / expect_diff; + + gf_bs_write_u8(bs, loss_diff); + + //update and write cumulative loss + ch->tot_num_pck_rcv += ch->last_num_pck_rcv; + ch->tot_num_pck_expected = expected; + gf_bs_write_u24(bs, (expected - ch->tot_num_pck_rcv)); + + //Extend sequence number + gf_bs_write_u32(bs, extended); + + + //Jitter + //RTP specs annexe A.8 + gf_bs_write_u32(bs, ( ch->Jitter >> 4)); + //LSR + val = ch->last_SR_NTP_sec ? gf_rtp_get_ntp_frac(ch->last_SR_NTP_sec, ch->last_SR_NTP_frac) : 0; + gf_bs_write_u32(bs, val); + + // DLSR + gf_bs_write_u32(bs, (NTP_Time - ch->last_report_time)); + + +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= (GF_LOG_DEBUG)) && (gf_log_get_tools() & (GF_LOG_RTP))) { +#ifndef _WIN32_WCE + time_t gtime = ch->last_SR_NTP_sec - GF_NTP_SEC_1900_TO_1970; + const char *ascTime = asctime(gmtime(>ime)); +#else + const char *ascTime = "Not Available"; +#endif + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] RTCP-RR\t%d\t%d\t%d\t%d\t%d\t%s\n", + ch->SSRC, + ch->Jitter >> 4, + extended, + expect_diff, + loss_diff, + ascTime + )); + } +#endif + + size += 24; + return size; +} + + +static u32 RTCP_FormatSDES(GF_RTPChannel *ch, GF_BitStream *bs) +{ + u32 length, padd; + + //one item (type, len, data) + \0 marker at the end of the item + length = 2+strlen(ch->CName) + 1; +// length = 2+strlen(ch->CName); + padd = length % 4; + if (padd*4 != length) { + //padding octets + padd = 4 - padd; + //header length + length = length/4 + 1; + } else { + padd = 0; + length = length/4; + } + //add SSRC + length += 1; + + //common part as usual + gf_bs_write_int(bs, 2, 2); + //notify padding? according to RFC1889 "In a compound RTCP packet, padding should + //only be required on the last individual packet because the compound packet is + //encrypted as a whole" -> we write it without notifying it (this is a bit messy in + //the spec IMO) + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 1, 5); + //SDES pck type + gf_bs_write_u8(bs, 202); + //length + gf_bs_write_u16(bs, length); + + //SSRC + gf_bs_write_u32(bs, ch->SSRC); + + //CNAME type + gf_bs_write_u8(bs, 1); + //length and cname + gf_bs_write_u8(bs, strlen(ch->CName)); + gf_bs_write_data(bs, ch->CName, strlen(ch->CName)); + + gf_bs_write_u8(bs, 0); + + //32-align field with 0 + gf_bs_write_int(bs, 0, 8*padd); + return (length + 1)*4; +} + + +static u32 RTCP_FormatBYE(GF_RTPChannel *ch, GF_BitStream *bs) +{ + //version + gf_bs_write_int(bs, 2, 2); + //no padding + gf_bs_write_int(bs, 0, 1); + //count - only one for now + gf_bs_write_int(bs, 1, 5); + //type=BYE + gf_bs_write_u8(bs, 203); + //length = (num of 32bit words in full pck) - 1 + gf_bs_write_u16(bs, 1); + + //sender SSRC + gf_bs_write_u32(bs, ch->SSRC); + return 8; +} + +GF_EXPORT +GF_Err gf_rtp_send_bye(GF_RTPChannel *ch, + GF_Err (*RTP_TCPCallback)(void *cbk, char *pck, u32 pck_size), + void *rtsp_cbk) +{ + GF_BitStream *bs; + u32 report_size; + char *report_buf; + GF_Err e = GF_OK; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + /*k were received/sent send the RR/SR - note we don't wait for next Repor and force its emission now*/ + if (ch->last_num_pck_rcv || ch->pck_sent_since_last_sr) { + RTCP_FormatReport(ch, bs, gf_rtp_get_report_time()); + } + + //always send SDES (CNAME shall be sent at each RTCP) + RTCP_FormatSDES(ch, bs); + + //send BYE + RTCP_FormatBYE(ch, bs); + + + report_buf = NULL; + report_size = 0; + gf_bs_get_content(bs, &report_buf, &report_size); + gf_bs_del(bs); + + if (ch->rtcp) { + e = gf_sk_send(ch->rtcp, report_buf, report_size); + } else { + if (RTP_TCPCallback) + e = RTP_TCPCallback(rtsp_cbk, report_buf, report_size); + else + e = GF_BAD_PARAM; + } + free(report_buf); + return e; +} + +GF_EXPORT +GF_Err gf_rtp_send_rtcp_report(GF_RTPChannel *ch, + GF_Err (*RTP_TCPCallback)(void *cbk, char *pck, u32 pck_size), + void *rtsp_cbk) +{ + u32 Time, report_size; + GF_BitStream *bs; + char *report_buf; + GF_Err e = GF_OK; + + if (ch->first_SR) return GF_OK; + Time = gf_rtp_get_report_time(); + if ( Time < ch->next_report_time) return GF_OK; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + //pck were received/sent send the RR/SR + if (ch->last_num_pck_rcv || ch->pck_sent_since_last_sr) { + RTCP_FormatReport(ch, bs, Time); + } + + //always send SDES (CNAME shall be sent at each RTCP) + RTCP_FormatSDES(ch, bs); + + + //get content + report_buf = NULL; + report_size = 0; + gf_bs_get_content(bs, &report_buf, &report_size); + gf_bs_del(bs); + + + if (ch->rtcp) { + e = gf_sk_send(ch->rtcp, report_buf, report_size); + } else { + if (RTP_TCPCallback) + e = RTP_TCPCallback(rtsp_cbk, report_buf, report_size); + else + e = GF_BAD_PARAM; + } + + ch->rtcp_bytes_sent += report_size; + + free(report_buf); + + if (!e) { + //Update the channel record if no error - otherwise next RTCP will triger an RR + ch->last_num_pck_rcv = ch->last_num_pck_expected = ch->last_num_pck_loss = 0; + } + gf_rtp_get_next_report_time(ch); + return e; +} + + + +#define RTCP_SAFE_FREE(p) if (p) free(p); \ + p = NULL; + +GF_EXPORT +GF_Err gf_rtp_set_info_rtcp(GF_RTPChannel *ch, u32 InfoCode, char *info_string) +{ + if (!ch) return GF_BAD_PARAM; + + switch (InfoCode) { + case GF_RTCP_INFO_NAME: + RTCP_SAFE_FREE(ch->s_name); + if (info_string) ch->s_name = strdup(info_string); + break; + case GF_RTCP_INFO_EMAIL: + RTCP_SAFE_FREE(ch->s_email); + if (info_string) ch->s_email = strdup(info_string); + break; + case GF_RTCP_INFO_PHONE: + RTCP_SAFE_FREE(ch->s_phone); + if (info_string) ch->s_phone = strdup(info_string); + break; + case GF_RTCP_INFO_LOCATION: + RTCP_SAFE_FREE(ch->s_location); + if (info_string) ch->s_location = strdup(info_string); + break; + case GF_RTCP_INFO_TOOL: + RTCP_SAFE_FREE(ch->s_tool); + if (info_string) ch->s_tool = strdup(info_string); + break; + case GF_RTCP_INFO_NOTE: + RTCP_SAFE_FREE(ch->s_note); + if (info_string) ch->s_note = strdup(info_string); + break; + case GF_RTCP_INFO_PRIV: + RTCP_SAFE_FREE(ch->s_priv); + if (info_string) ch->s_name = strdup(info_string); + break; + default: + return GF_BAD_PARAM; + } + return GF_OK; +} diff --git a/src/ietf/rtp.c b/src/ietf/rtp.c new file mode 100644 index 0000000..090e9b8 --- /dev/null +++ b/src/ietf/rtp.c @@ -0,0 +1,901 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#define MAX_RTP_SN 0x10000 + + +GF_EXPORT +GF_RTPChannel *gf_rtp_new() +{ + GF_RTPChannel *tmp; + GF_SAFEALLOC(tmp, GF_RTPChannel); + tmp->first_SR = 1; + tmp->SSRC = gf_rand(); + + return tmp; +} + +GF_EXPORT +void gf_rtp_del(GF_RTPChannel *ch) +{ + if (!ch) return; + if (ch->rtp) gf_sk_del(ch->rtp); + if (ch->rtcp) gf_sk_del(ch->rtcp); + if (ch->net_info.source) free(ch->net_info.source); + if (ch->net_info.destination) free(ch->net_info.destination); + if (ch->net_info.Profile) free(ch->net_info.Profile); + if (ch->po) gf_rtp_reorderer_del(ch->po); + if (ch->send_buffer) free(ch->send_buffer); + + if (ch->CName) free(ch->CName); + if (ch->s_name) free(ch->s_name); + if (ch->s_email) free(ch->s_email); + if (ch->s_location) free(ch->s_location); + if (ch->s_phone) free(ch->s_phone); + if (ch->s_tool) free(ch->s_tool); + if (ch->s_note) free(ch->s_note); + if (ch->s_priv) free(ch->s_priv); + free(ch); +} + + + +GF_EXPORT +GF_Err gf_rtp_setup_transport(GF_RTPChannel *ch, GF_RTSPTransport *trans_info, char *remote_address) +{ + if (!ch || !trans_info) return GF_BAD_PARAM; + //assert we have at least ONE source ID + if (!trans_info->source && !remote_address) return GF_BAD_PARAM; + + if (ch->net_info.destination) free(ch->net_info.destination); + if (ch->net_info.Profile) free(ch->net_info.Profile); + if (ch->net_info.source) free(ch->net_info.source); + memcpy(&ch->net_info, trans_info, sizeof(GF_RTSPTransport)); + + if (trans_info->destination) + ch->net_info.destination = strdup(trans_info->destination); + + if (trans_info->Profile) + ch->net_info.Profile = strdup(trans_info->Profile); + + if (!ch->net_info.IsUnicast && trans_info->destination) { + ch->net_info.source = strdup(trans_info->destination); + if (ch->net_info.client_port_first) { + ch->net_info.port_first = ch->net_info.client_port_first; + ch->net_info.port_last = ch->net_info.client_port_last; + } + ch->net_info.source = strdup(trans_info->destination); + } else if (trans_info->source) { + ch->net_info.source = strdup(trans_info->source); + } else { + ch->net_info.source = strdup(remote_address); + } + if (trans_info->SSRC) ch->SenderSSRC = trans_info->SSRC; + + //check we REALLY have unicast or multicast + if (gf_sk_is_multicast_address(ch->net_info.source) && ch->net_info.IsUnicast) return GF_SERVICE_ERROR; + return GF_OK; +} + + +GF_EXPORT +void gf_rtp_reset_buffers(GF_RTPChannel *ch) +{ + if (ch->rtp) gf_sk_reset(ch->rtp); + if (ch->rtcp) gf_sk_reset(ch->rtcp); + if (ch->po) gf_rtp_reorderer_reset(ch->po); + /*also reset ssrc*/ + //ch->SenderSSRC = 0; + ch->first_SR = 1; +} + + +GF_EXPORT +void gf_rtp_enable_nat_keepalive(GF_RTPChannel *ch, u32 nat_timeout) +{ + if (ch) { + ch->nat_keepalive_time_period = nat_timeout; + ch->last_nat_keepalive_time = 0; + } +} + + + +GF_EXPORT +GF_Err gf_rtp_set_info_rtp(GF_RTPChannel *ch, u32 seq_num, u32 rtp_time, u32 ssrc) +{ + if (!ch) return GF_BAD_PARAM; + ch->rtp_time = rtp_time; + ch->last_pck_sn = 0; + ch->rtp_first_SN = seq_num; + ch->num_sn_loops = 0; + //reset RTCP + ch->ntp_init = 0; + ch->first_SR = 1; + if (ssrc) ch->SenderSSRC = ssrc; + ch->total_pck = ch->total_bytes = ch->last_num_pck_rcv = ch->last_num_pck_expected = ch->last_num_pck_loss = ch->tot_num_pck_rcv = ch->tot_num_pck_expected = ch->rtcp_bytes_sent = 0; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_rtp_initialize(GF_RTPChannel *ch, u32 UDPBufferSize, Bool IsSource, u32 PathMTU, u32 ReorederingSize, u32 MaxReorderDelay, char *local_ip) +{ + GF_Err e; + + if (IsSource && !PathMTU) return GF_BAD_PARAM; + + if (ch->rtp) gf_sk_del(ch->rtp); + if (ch->rtcp) gf_sk_del(ch->rtcp); + if (ch->po) gf_rtp_reorderer_del(ch->po); + + ch->CurrentTime = 0; + ch->rtp_time = 0; + + //create sockets for RTP/AVP profile only + if (ch->net_info.Profile && + ( !stricmp(ch->net_info.Profile, GF_RTSP_PROFILE_RTP_AVP) + || !stricmp(ch->net_info.Profile, "RTP/AVP/UDP") + || !stricmp(ch->net_info.Profile, "RTP/SAVP") + ) + ) { + //destination MUST be specified for unicast + if (IsSource && ch->net_info.IsUnicast && !ch->net_info.destination) return GF_BAD_PARAM; + + // + // RTP + // + ch->rtp = gf_sk_new(GF_SOCK_TYPE_UDP); + if (!ch->rtp) return GF_IP_NETWORK_FAILURE; + if (ch->net_info.IsUnicast) { + //if client, bind and connect the socket + if (!IsSource) { + e = gf_sk_bind(ch->rtp, local_ip, ch->net_info.client_port_first, ch->net_info.source, ch->net_info.port_first, GF_SOCK_REUSE_PORT); + if (e) return e; + } + //else bind and set remote destination + else { + if (!ch->net_info.port_first) ch->net_info.port_first = ch->net_info.client_port_first; + e = gf_sk_bind(ch->rtp, local_ip,ch->net_info.port_first, ch->net_info.destination, ch->net_info.client_port_first, GF_SOCK_REUSE_PORT); + if (e) return e; + } + } else { + //Bind to multicast (auto-join the group). + //we do not bind the socket if this is a source-only channel because some servers + //don't like that on local loop ... + e = gf_sk_setup_multicast(ch->rtp, ch->net_info.source, ch->net_info.port_first, ch->net_info.TTL, (IsSource==2), local_ip); + if (e) return e; + + //destination is used for multicast interface addressing - TO DO + + } + if (UDPBufferSize) gf_sk_set_buffer_size(ch->rtp, IsSource, UDPBufferSize); + + if (IsSource) { + if (ch->send_buffer) free(ch->send_buffer); + ch->send_buffer = (char *) malloc(sizeof(char) * PathMTU); + ch->send_buffer_size = PathMTU; + } + + + //create re-ordering queue for UDP only, and receive + if (ReorederingSize && !IsSource) { + if (!MaxReorderDelay) MaxReorderDelay = 200; + ch->po = gf_rtp_reorderer_new(ReorederingSize, MaxReorderDelay); + } + + // + // RTCP + // + ch->rtcp = gf_sk_new(GF_SOCK_TYPE_UDP); + if (!ch->rtcp) return GF_IP_NETWORK_FAILURE; + if (ch->net_info.IsUnicast) { + if (!IsSource) { + e = gf_sk_bind(ch->rtcp, local_ip, ch->net_info.client_port_last, ch->net_info.source, ch->net_info.port_last, GF_SOCK_REUSE_PORT); + if (e) return e; + } else { + e = gf_sk_bind(ch->rtcp, local_ip, ch->net_info.port_last, ch->net_info.destination, ch->net_info.client_port_last, GF_SOCK_REUSE_PORT); + if (e) return e; + } + } else { + if (!ch->net_info.port_last) ch->net_info.port_last = ch->net_info.client_port_last; + //Bind to multicast (auto-join the group) + e = gf_sk_setup_multicast(ch->rtcp, ch->net_info.source, ch->net_info.port_last, ch->net_info.TTL, (IsSource==2), local_ip); + if (e) return e; + //destination is used for multicast interface addressing - TO DO + } + } + + //format CNAME if not done yet + if (!ch->CName) { + //this is the real CName setup + if (!ch->rtp) { + ch->CName = strdup("mpeg4rtp"); + } else { + char name[GF_MAX_IP_NAME_LEN]; + + s32 start; + gf_get_user_name(name, 1024); + if (strlen(name)) strcat(name, "@"); + start = strlen(name); + //get host IP or loopback if error + if (gf_sk_get_local_ip(ch->rtp, name+start) != GF_OK) strcpy(name+start, "127.0.0.1"); + ch->CName = strdup(name); + } + } + + +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= (GF_LOG_DEBUG)) && (gf_log_get_tools() & (GF_LOG_RTP))) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Packet Log Format: SSRC SequenceNumber TimeStamp NTP@recvTime deviance, Jiter, PckLost PckTotal BytesTotal\n")); + } +#endif + + return GF_OK; +} + +/*get the UTC time expressed in RTP timescale*/ +u32 gf_rtp_channel_time(GF_RTPChannel *ch) +{ + u32 sec, frac, res; + gf_net_get_ntp(&sec, &frac); + res = ( (u32) ( (frac>>26)*ch->TimeScale) ) >> 6; + res += ch->TimeScale*(sec - ch->ntp_init); + return (u32) res; +} + +u32 gf_rtp_get_report_time() +{ + u32 sec, frac; + gf_net_get_ntp(&sec, &frac); + /*in units of 1/65536 seconds*/ + return (u32) ( (frac>>16) + 0x10000L*sec ); +} + + +void gf_rtp_get_next_report_time(GF_RTPChannel *ch) +{ + Double d; + + /*offset between .5 and 1.5 sec*/ + d = 0.5 + ((Double) gf_rand()) / ((Double) RAND_MAX); + /*of a minimal 5sec interval expressed in 1/65536 of a sec*/ + d = 5.0 * d * 65536; + /*we should estimate bandwidth sharing too, but as we only support one sender*/ + ch->next_report_time = gf_rtp_get_report_time() + (u32) d; +} + + +GF_EXPORT +u32 gf_rtp_read_rtp(GF_RTPChannel *ch, char *buffer, u32 buffer_size) +{ + GF_Err e; + u32 seq_num, res; + char *pck; + + //only if the socket exist (otherwise RTSP interleaved channel) + if (!ch || !ch->rtp) return 0; + + e = gf_sk_receive(ch->rtp, buffer, buffer_size, 0, &res); + if (!res || e || (res < 12)) res = 0; + + //add the packet to our Queue if any + if (ch->po) { + if (res) { + seq_num = ((buffer[2] << 8) & 0xFF00) | (buffer[3] & 0xFF); + gf_rtp_reorderer_add(ch->po, (void *) buffer, res, seq_num); + } + + //pck queue may need to be flushed + pck = (char *) gf_rtp_reorderer_get(ch->po, &res); + if (pck) { + memcpy(buffer, pck, res); + free(pck); + } + } + /*monitor keep-alive period*/ + if (ch->nat_keepalive_time_period) { + u32 now = gf_sys_clock(); + if (res) { + ch->last_nat_keepalive_time = now; + } else { + if (now - ch->last_nat_keepalive_time >= ch->nat_keepalive_time_period) { + char rtp_nat[12]; + rtp_nat[0] = (u8) 0xC0; + rtp_nat[1] = ch->PayloadType; + rtp_nat[2] = (ch->last_pck_sn>>8)&0xFF; + rtp_nat[3] = (ch->last_pck_sn)&0xFF; + rtp_nat[4] = (ch->last_pck_ts>>24)&0xFF; + rtp_nat[5] = (ch->last_pck_ts>>16)&0xFF; + rtp_nat[6] = (ch->last_pck_ts>>8)&0xFF; + rtp_nat[7] = (ch->last_pck_ts)&0xFF; + rtp_nat[8] = (ch->SenderSSRC>>24)&0xFF; + rtp_nat[9] = (ch->SenderSSRC>>16)&0xFF; + rtp_nat[10] = (ch->SenderSSRC>>8)&0xFF; + rtp_nat[11] = (ch->SenderSSRC)&0xFF; + + e = gf_sk_send(ch->rtp, buffer, 12); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP] Error sending NAT keep-alive packet: %s - disabling NAT\n", gf_error_to_string(e) )); + ch->nat_keepalive_time_period = 0; + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Sending NAT keep-alive packet - response %s\n", gf_error_to_string(e) )); + } + ch->last_nat_keepalive_time = now; + } + } + } + return res; +} + + +GF_EXPORT +GF_Err gf_rtp_decode_rtp(GF_RTPChannel *ch, char *pck, u32 pck_size, GF_RTPHeader *rtp_hdr, u32 *PayloadStart) +{ + GF_Err e; + s32 deviance, delta; + u32 CurrSeq, LastSeq; + u32 ntp, lost, low16; + + if (!rtp_hdr) return GF_BAD_PARAM; + e = GF_OK; + + //we need to uncompress the RTP header + rtp_hdr->Version = (pck[0] & 0xC0 ) >> 6; + if (rtp_hdr->Version != 2) return GF_NOT_SUPPORTED; + + rtp_hdr->Padding = ( pck[0] & 0x20 ) >> 5; + rtp_hdr->Extension = ( pck[0] & 0x10 ) >> 4; + rtp_hdr->CSRCCount = pck[0] & 0x0F; + rtp_hdr->Marker = ( pck[1] & 0x80 ) >> 7; + rtp_hdr->PayloadType = pck[1] & 0x7F; + + /*we don't support multiple CSRC now. Only one source (the server) is allowed*/ + if (rtp_hdr->CSRCCount) return GF_NOT_SUPPORTED; + /*SeqNum*/ + rtp_hdr->SequenceNumber = ((pck[2] << 8) & 0xFF00) | (pck[3] & 0xFF); + /*TS*/ + rtp_hdr->TimeStamp = (u32) ((pck[4]<<24) &0xFF000000) | ((pck[5]<<16) & 0xFF0000) | ((pck[6]<<8) & 0xFF00) | ((pck[7]) & 0xFF); + /*SSRC*/ + rtp_hdr->SSRC = ((pck[8]<<24) &0xFF000000) | ((pck[9]<<16) & 0xFF0000) | ((pck[10]<<8) & 0xFF00) | ((pck[11]) & 0xFF); + /*first we only work with one payload type...*/ + if (rtp_hdr->PayloadType != ch->PayloadType) return GF_NOT_SUPPORTED; + + /*update RTP time if we didn't get the info*/ + if (!ch->rtp_time) { + ch->rtp_time = rtp_hdr->TimeStamp; + ch->rtp_first_SN = rtp_hdr->SequenceNumber; + ch->num_sn_loops = 0; + } + + if (!ch->ntp_init && ch->SenderSSRC && (ch->SenderSSRC != rtp_hdr->SSRC) ) { + GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTP] SSRC mismatch: %d vs %d\n", rtp_hdr->SSRC, ch->SenderSSRC)); + return GF_IP_NETWORK_EMPTY; + } + + + /*RTP specs annexe A.8*/ + if (!ch->ntp_init) { + gf_net_get_ntp(&ch->ntp_init, &lost); + ch->last_pck_sn = (u32) rtp_hdr->SequenceNumber-1; + } + /*this is a loop in SN - add it*/ + if ( (ch->last_pck_sn + 1 > rtp_hdr->SequenceNumber) + && (rtp_hdr->SequenceNumber >= ch->last_pck_sn + MAX_RTP_SN/2)) { + ch->num_sn_loops += 1; + } + + ntp = gf_rtp_channel_time(ch); + deviance = ntp - rtp_hdr->TimeStamp; + delta = deviance - ch->last_deviance; + ch->last_deviance = deviance; + + if (delta < 0) delta = -delta; + ch->Jitter += delta - ( (ch->Jitter + 8) >> 4); + + lost = 0; + LastSeq = ch->last_pck_sn; + CurrSeq = (u32) rtp_hdr->SequenceNumber; + /*next sequential pck*/ + if ( ( (LastSeq + 1) & 0xffff ) == CurrSeq ) { + ch->last_num_pck_rcv += 1; + ch->last_num_pck_expected += 1; + } + /*repeated pck*/ + else if ( (LastSeq & 0xffff ) == CurrSeq ) { + ch->last_num_pck_rcv += 1; + } + /*drop pck*/ + else { + low16 = LastSeq & 0xffff; + if ( CurrSeq > low16 ) + lost = CurrSeq - low16; + else + lost = 0xffff - low16 + CurrSeq + 1; + + ch->last_num_pck_expected += lost; + ch->last_num_pck_rcv += 1; + ch->last_num_pck_loss += lost; + } + ch->last_pck_sn = CurrSeq; + +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= (GF_LOG_DEBUG)) && (gf_log_get_tools() & (GF_LOG_RTP))) { + ch->total_pck++; + ch->total_bytes += pck_size-12; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP]\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", + ch->SenderSSRC, + rtp_hdr->SequenceNumber, + rtp_hdr->TimeStamp, + ntp, + delta, + ch->Jitter >> 4, + lost, + ch->total_pck, + ch->total_bytes + )); + } +#endif + + //we work with no CSRC so payload offset is always 12 + *PayloadStart = 12; + + //store the time + ch->CurrentTime = rtp_hdr->TimeStamp; + return e; +} + + +GF_EXPORT +Double gf_rtp_get_current_time(GF_RTPChannel *ch) +{ + Double ret; + if (!ch) return 0.0; + ret = (Double) ch->CurrentTime; + ret -= (Double) ch->rtp_time; + ret /= ch->TimeScale; + return ret; +} + + + + + + +GF_EXPORT +GF_Err gf_rtp_send_packet(GF_RTPChannel *ch, GF_RTPHeader *rtp_hdr, char *extra_header, u32 extra_header_size, char *pck, u32 pck_size) +{ + GF_Err e; + u32 Start, i; + GF_BitStream *bs; + + if (!ch || !rtp_hdr + || !ch->send_buffer + || !pck + || (rtp_hdr->CSRCCount && !rtp_hdr->CSRC) + || (rtp_hdr->CSRCCount > 15)) return GF_BAD_PARAM; + + if (12 + extra_header_size + pck_size + 4*rtp_hdr->CSRCCount > ch->send_buffer_size) return GF_IO_ERR; + + //we don't support multiple CSRC now. Only one source (the server) is allowed + if (rtp_hdr->CSRCCount) return GF_NOT_SUPPORTED; + + bs = gf_bs_new(ch->send_buffer, ch->send_buffer_size, GF_BITSTREAM_WRITE); + + //write header + gf_bs_write_int(bs, rtp_hdr->Version, 2); + gf_bs_write_int(bs, rtp_hdr->Padding, 1); + gf_bs_write_int(bs, rtp_hdr->Extension, 1); + gf_bs_write_int(bs, rtp_hdr->CSRCCount, 4); + gf_bs_write_int(bs, rtp_hdr->Marker, 1); + gf_bs_write_int(bs, rtp_hdr->PayloadType, 7); + gf_bs_write_u16(bs, rtp_hdr->SequenceNumber); + gf_bs_write_u32(bs, rtp_hdr->TimeStamp); + gf_bs_write_u32(bs, rtp_hdr->SSRC); + + for (i=0; iCSRCCount; i++) { + gf_bs_write_u32(bs, rtp_hdr->CSRC[i]); + } + //nb: RTP header is always aligned + Start = (u32) gf_bs_get_position(bs); + gf_bs_del(bs); + + //extra header + if (extra_header_size) { + memcpy(ch->send_buffer + Start, extra_header, extra_header_size); + Start += extra_header_size; + } + //payload + memcpy(ch->send_buffer + Start, pck, pck_size); + e = gf_sk_send(ch->rtp, ch->send_buffer, Start + pck_size); + if (e) return e; + + //Update RTCP for sender reports + ch->pck_sent_since_last_sr += 1; + if (ch->first_SR) { + //get a new report time + gf_rtp_get_next_report_time(ch); + ch->num_payload_bytes = 0; + ch->num_pck_sent = 0; + ch->first_SR = 0; + } + + ch->num_payload_bytes += pck_size + extra_header_size; + ch->num_pck_sent += 1; + //store timing + ch->last_pck_ts = rtp_hdr->TimeStamp; + gf_net_get_ntp(&ch->last_pck_ntp_sec, &ch->last_pck_ntp_frac); + return GF_OK; +} + +GF_EXPORT +u32 gf_rtp_is_unicast(GF_RTPChannel *ch) +{ + if (!ch) return 0; + return ch->net_info.IsUnicast; +} + +GF_EXPORT +u32 gf_rtp_is_interleaved(GF_RTPChannel *ch) +{ + if (!ch || !ch->net_info.Profile) return 0; + return ch->net_info.IsInterleaved; +} + +GF_EXPORT +u32 gf_rtp_get_clockrate(GF_RTPChannel *ch) +{ + if (!ch || !ch->TimeScale) return 0; + return ch->TimeScale; +} + +GF_EXPORT +u32 gf_rtp_is_active(GF_RTPChannel *ch) +{ + if (!ch) return 0; + if (!ch->rtp_first_SN && !ch->rtp_time) return 0; + return 1; +} + +GF_EXPORT +u8 gf_rtp_get_low_interleave_id(GF_RTPChannel *ch) +{ + if (!ch || !ch->net_info.IsInterleaved) return 0; + return ch->net_info.rtpID; +} + +GF_EXPORT +u8 gf_rtp_get_hight_interleave_id(GF_RTPChannel *ch) +{ + if (!ch || !ch->net_info.IsInterleaved) return 0; + return ch->net_info.rtcpID; +} + + +#define RTP_DEFAULT_FIRSTPORT 7040 + +static u16 NextAvailablePort = 0; + +GF_EXPORT +GF_Err gf_rtp_set_ports(GF_RTPChannel *ch, u16 first_port) +{ + u32 retry; + u16 p; + GF_Socket *sock; + if (!ch) return GF_BAD_PARAM; + + if (!NextAvailablePort) { + NextAvailablePort = first_port ? first_port : RTP_DEFAULT_FIRSTPORT; + } + p = NextAvailablePort; + if (ch->net_info.client_port_first) return GF_OK; + + sock = gf_sk_new(GF_SOCK_TYPE_UDP); + if (!sock) return GF_IO_ERR; + + /*should be way enough (more than 100 rtp streams open on the machine)*/ + retry = 100; + while (1) { + /*try to bind without reuse. If fails this means the port is used on the machine, don't reuse it*/ + GF_Err e = gf_sk_bind(sock, NULL, p, NULL, 0, 0); + if (e==GF_OK) break; + if (e!=GF_IP_CONNECTION_FAILURE) { + gf_sk_del(sock); + return GF_IP_NETWORK_FAILURE; + } + p+=2; + } + gf_sk_del(sock); + ch->net_info.client_port_first = p; + ch->net_info.client_port_last = p + 1; + NextAvailablePort = p + 2; + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_rtp_setup_payload(GF_RTPChannel *ch, GF_RTPMap *map) +{ + if (!ch || !map) return GF_BAD_PARAM; + ch->PayloadType = map->PayloadType; + ch->TimeScale = map->ClockRate; + return GF_OK; +} + +GF_EXPORT +GF_RTSPTransport *gf_rtp_get_transport(GF_RTPChannel *ch) +{ + if (!ch) return NULL; + return &ch->net_info; +} + +GF_EXPORT +u32 gf_rtp_get_local_ssrc(GF_RTPChannel *ch) +{ + if (!ch) return 0; + return ch->SSRC; +} + + +#if 0 + "#RTP log format:\n" + "#RTP SenderSSRC RTP_TimeStamp RTP_SeqNum NTP@Recv Deviance Jitter NbLost NbTotPck NbTotBytes\n" + "#RTCP Sender reports log format:\n" + "#RTCP-SR SenderSSRC RTP_TimeStamp@NTP NbTotPck NbTotBytes NTP\n" + "#RTCP Receiver reports log format:\n" + "#RTCP-RR StreamSSRC Jitter ExtendedSeqNum ExpectDiff LossDiff NTP\n" +#endif + +GF_EXPORT +Float gf_rtp_get_loss(GF_RTPChannel *ch) +{ + if (!ch->tot_num_pck_expected) return 0.0f; + return 100.0f - (100.0f * ch->tot_num_pck_rcv) / ch->tot_num_pck_expected; +} + +GF_EXPORT +u32 gf_rtp_get_tcp_bytes_sent(GF_RTPChannel *ch) +{ + return ch->rtcp_bytes_sent; +} + +GF_EXPORT +void gf_rtp_get_ports(GF_RTPChannel *ch, u16 *rtp_port, u16 *rtcp_port) +{ + *rtp_port = ch->net_info.client_port_first; + *rtcp_port = ch->net_info.client_port_last; +} + + +/* + RTP packet reorderer +*/ + +#define SN_CHECK_OFFSET 0x0A + +GF_RTPReorder *gf_rtp_reorderer_new(u32 MaxCount, u32 MaxDelay) +{ + GF_RTPReorder *tmp; + + if (MaxCount <= 1 || !MaxDelay) return NULL; + + GF_SAFEALLOC(tmp , GF_RTPReorder); + tmp->MaxCount = MaxCount; + tmp->MaxDelay = MaxDelay; + return tmp; +} + +static void DelItem(GF_POItem *it) +{ + if (it) { + if (it->next) DelItem(it->next); + free(it->pck); + free(it); + } +} + + +void gf_rtp_reorderer_del(GF_RTPReorder *po) +{ + if (po->in) DelItem(po->in); + free(po); +} + +void gf_rtp_reorderer_reset(GF_RTPReorder *po) +{ + if (!po) return; + + if (po->in) DelItem(po->in); + po->head_seqnum = 0; + po->Count = 0; + po->IsInit = 0; + po->in = NULL; +} + +GF_Err gf_rtp_reorderer_add(GF_RTPReorder *po, void *pck, u32 pck_size, u32 pck_seqnum) +{ + GF_POItem *it, *cur; + u32 bounds; + + if (!po) return GF_BAD_PARAM; + + it = (GF_POItem *) malloc(sizeof(GF_POItem)); + it->pck_seq_num = pck_seqnum; + it->next = NULL; + it->size = pck_size; + it->pck = malloc(pck_size); + memcpy(it->pck, pck, pck_size); + /*reset timeout*/ + po->LastTime = 0; + + //no input, this packet will be the input + if (!po->in) { + //the seq num was not initialized + if (!po->head_seqnum) { + po->head_seqnum = pck_seqnum; + } else if (!po->IsInit) { + //this is not in our current range for init + if (ABSDIFF(po->head_seqnum, pck_seqnum) > SN_CHECK_OFFSET) goto discard; + po->IsInit = 1; + } + + po->in = it; + po->Count += 1; + return GF_OK; + } + + //this is 16 bitr seq num, as we work with RTP only for now + bounds = 0; + if ( (po->head_seqnum >= 0xf000 ) || (po->head_seqnum <= 0x1000) ) bounds = 0x2000; + + //first check the head of the list + //same seq num, we drop + if (po->in->pck_seq_num == pck_seqnum) goto discard; + + if ( ( (u16) (pck_seqnum + bounds) <= (u16) (po->in->pck_seq_num + bounds) ) + && ( (u16) (pck_seqnum + bounds) >= (u16) (po->head_seqnum + bounds)) ) { + + it->next = po->in; + po->in = it; + po->Count += 1; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[rtp] Packet Reorderer: inserting packet %d at head\n", pck_seqnum)); + return GF_OK; + } + + //no, insert at the right place + cur = po->in; + + while (1) { + //same seq num, we drop + if (cur->pck_seq_num == pck_seqnum) goto discard; + + //end of list + if (!cur->next) { + cur->next = it; + po->Count += 1; +#ifndef GPAC_DISABLE_LOG + if (cur->pck_seq_num +1 != it->pck_seq_num) + GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[rtp] Packet Reorderer: got %d expected %d\n", cur->pck_seq_num+1, it->pck_seq_num)); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[rtp] Packet Reorderer: Appending packet %d\n", pck_seqnum)); +#endif + return GF_OK; + } + + //are we in the bounds ?? + if ( ( (u16) (cur->pck_seq_num + bounds) < (u16) (pck_seqnum + bounds) ) + && ( (u16) (pck_seqnum + bounds) < (u16) (cur->next->pck_seq_num + bounds)) ) { + + //insert + it->next = cur->next; + cur->next = it; + po->Count += 1; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[rtp] Packet Reorderer: Inserting packet %d", pck_seqnum)); + //done + return GF_OK; + } + cur = cur->next; + } + + +discard: + free(it->pck); + free(it); + GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[rtp] Packet Reorderer: Dropping packet %d", pck_seqnum)); + return GF_OK; +} + +//retrieve the first available packet. Note that the behavior will be undefined if the first +//ever received packet if its SeqNum was unknown +//the BUFFER is yours, you must delete it +void *gf_rtp_reorderer_get(GF_RTPReorder *po, u32 *pck_size) +{ + GF_POItem *t; + u32 bounds; + void *ret; + + if (!po || !pck_size) return NULL; + + *pck_size = 0; + + //empty queue + if (!po->in) return NULL; + + //check we have received the first packet + if ( po->head_seqnum && po->MaxCount + && (po->MaxCount > po->Count) + && (po->in->pck_seq_num != po->head_seqnum)) + return NULL; + + //no entry + if (!po->in->next) goto check_timeout; + + bounds = 0; + if ( (po->head_seqnum >= 0xf000 ) || (po->head_seqnum <= 0x1000) ) bounds = 0x2000; + + //release the output if SN in order or maxCount reached + if (( (u16) (po->in->pck_seq_num + bounds + 1) == (u16) (po->in->next->pck_seq_num + bounds)) + || (po->MaxCount && (po->Count >= po->MaxCount)) ) { + +#ifndef GPAC_DISABLE_LOG + if (po->in->pck_seq_num + 1 != po->in->next->pck_seq_num) + GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[rtp] Packet Reorderer: Fetched %d expected %d\n", po->in->pck_seq_num, po->in->next->pck_seq_num)); +#endif + goto send_it; + } + //update timing + else { +check_timeout: + if (!po->LastTime) { + po->LastTime = gf_sys_clock(); + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[rtp] Packet Reorderer: starting timeout at %d\n", po->LastTime)); + return NULL; + } + //if exceeding the delay send the head + if (gf_sys_clock() - po->LastTime >= po->MaxDelay) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[rtp] Packet Reorderer: Forcing output after %d ms wait (max allowed %d)\n", gf_sys_clock() - po->LastTime, po->MaxDelay)); + goto send_it; + } + } + return NULL; + + +send_it: + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[rtp] Packet Reorderer: Fetching %d\n", po->in->pck_seq_num)); + *pck_size = po->in->size; + t = po->in; + po->in = po->in->next; + //no other output. reset the head seqnum + po->head_seqnum = po->in ? po->in->pck_seq_num : 0; + po->Count -= 1; + //release the item + ret = t->pck; + free(t); + return ret; +} diff --git a/src/ietf/rtp_depacketizer.c b/src/ietf/rtp_depacketizer.c new file mode 100644 index 0000000..d18ffc6 --- /dev/null +++ b/src/ietf/rtp_depacketizer.c @@ -0,0 +1,1461 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / RTP input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include + + + +static void gf_rtp_parse_mpeg4(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + u32 aux_size, au_size, first_idx, au_hdr_size, pay_start, num_au; + s32 au_idx; + GF_BitStream *hdr_bs, *aux_bs; + + hdr_bs = gf_bs_new(payload, size, GF_BITSTREAM_READ); + aux_bs = gf_bs_new(payload, size, GF_BITSTREAM_READ); + +// printf("parsing packet %d size %d ts %d M %d\n", hdr->SequenceNumber, size, hdr->TimeStamp, hdr->Marker); + + /*global AU header len*/ + au_hdr_size = 0; + if (rtp->sl_map.auh_first_min_len) { + au_hdr_size = gf_bs_read_u16(hdr_bs); + gf_bs_read_u16(aux_bs); + } + + /*jump to aux section, skip it and get payload start*/ + gf_bs_read_int(aux_bs, au_hdr_size); + gf_bs_align(aux_bs); + if (rtp->sl_map.AuxiliaryDataSizeLength) { + aux_size = gf_bs_read_int(aux_bs, rtp->sl_map.AuxiliaryDataSizeLength); + gf_bs_read_int(aux_bs, aux_size); + gf_bs_align(aux_bs); + } + pay_start = (u32) gf_bs_get_position(aux_bs); + gf_bs_del(aux_bs); + + first_idx = 0; + au_idx = 0; + + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.decodingTimeStamp = hdr->TimeStamp; + + num_au = 0; + + rtp->sl_hdr.accessUnitEndFlag = hdr->Marker; + /*override some defaults for RFC 3016*/ + if (rtp->flags & GF_RTP_NEW_AU) { + rtp->sl_hdr.accessUnitStartFlag = 1; + } else { + rtp->sl_hdr.accessUnitStartFlag = 0; + } + rtp->sl_hdr.randomAccessPointFlag = 0; + + while (1) { + /*get default AU size*/ + au_size = rtp->sl_map.ConstantSize; + /*not signaled, assume max one AU per packet*/ + if (!au_size) au_size = size - pay_start; + + if ((!num_au && rtp->sl_map.auh_first_min_len) || (num_au && rtp->sl_map.auh_min_len)) { + /*ISMACryp*/ + if (rtp->flags & GF_RTP_HAS_ISMACRYP) { + u32 nbbits; + rtp->sl_hdr.isma_encrypted = 1; + if (rtp->flags & GF_RTP_ISMA_SEL_ENC) { + rtp->sl_hdr.isma_encrypted = gf_bs_read_int(hdr_bs, 1); + gf_bs_read_int(hdr_bs, 7); + au_hdr_size -= 8; + } + /*Note: ISMACryp ALWAYS indicates IV (BSO) and KEYIDX, even when sample is not encrypted. + This is quite a waste when using selective encryption....*/ + if (!num_au) { + nbbits = 8*rtp->sl_map.IV_length; + if (nbbits) { + rtp->sl_hdr.isma_BSO = gf_bs_read_int(hdr_bs, nbbits); + au_hdr_size -= nbbits; + } + } + /*NOT SUPPORTED YET*/ + else if (rtp->sl_map.IV_delta_length) { + nbbits = 8*rtp->sl_map.IV_delta_length; + if (nbbits) { + rtp->sl_hdr.isma_BSO += gf_bs_read_int(hdr_bs, nbbits); + au_hdr_size -= nbbits; + } + } + if (rtp->sl_map.KI_length) { + /*NOT SUPPORTED YET*/ + if (!num_au || !(rtp->flags & GF_RTP_ISMA_HAS_KEY_IDX) ) { + nbbits = 8*rtp->sl_map.KI_length; + if (nbbits) { + gf_bs_read_int(hdr_bs, nbbits); + au_hdr_size -= nbbits; + } + } + } + } + + /*AU size*/ + if (rtp->sl_map.SizeLength) { + au_size = gf_bs_read_int(hdr_bs, rtp->sl_map.SizeLength); + if (au_size > size - pay_start) au_size = size - pay_start; + au_hdr_size -= rtp->sl_map.SizeLength; + } + /*AU index*/ + if (! num_au) { + au_idx = first_idx = gf_bs_read_int(hdr_bs, rtp->sl_map.IndexLength); + au_hdr_size -= rtp->sl_map.IndexLength; + } else { + au_idx += 1 + (s32) gf_bs_read_int(hdr_bs, rtp->sl_map.IndexDeltaLength); + au_hdr_size -= rtp->sl_map.IndexDeltaLength; + } + /*CTS flag*/ + if (rtp->sl_map.CTSDeltaLength) { + rtp->sl_hdr.compositionTimeStampFlag = gf_bs_read_int(hdr_bs, 1); + au_hdr_size -= 1; + } else { + /*get CTS from IDX*/ + if (rtp->sl_map.ConstantDuration) { + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp + (au_idx - first_idx) * rtp->sl_map.ConstantDuration; + } else { + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp + (au_idx - first_idx) * rtp->sl_hdr.au_duration; + } + } + + /*CTS in-band*/ + if (rtp->sl_hdr.compositionTimeStampFlag) { + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp + (s32) gf_bs_read_int(hdr_bs, rtp->sl_map.CTSDeltaLength); + au_hdr_size -= rtp->sl_map.CTSDeltaLength; + } + /*DTS flag is always present (needed for reconstruction of TSs in case of packet loss)*/ + if (rtp->sl_map.DTSDeltaLength) { + rtp->sl_hdr.decodingTimeStampFlag = gf_bs_read_int(hdr_bs, 1); + au_hdr_size -= 1; + } else { + /*NO DTS otherwise*/ + rtp->sl_hdr.decodingTimeStampFlag = 0; + } + if (rtp->sl_hdr.decodingTimeStampFlag) { + u32 ts_off = gf_bs_read_int(hdr_bs, rtp->sl_map.DTSDeltaLength); + /*TODO FIXME may not be true in case of TS wrapping*/ + if (hdr->TimeStamp > ts_off) rtp->sl_hdr.decodingTimeStamp = hdr->TimeStamp - ts_off; + au_hdr_size -= rtp->sl_map.DTSDeltaLength; + } + /*RAP flag*/ + if (rtp->sl_map.RandomAccessIndication) { + rtp->sl_hdr.randomAccessPointFlag = gf_bs_read_int(hdr_bs, 1); + au_hdr_size -= 1; + if (rtp->sl_hdr.randomAccessPointFlag) + rtp->sl_hdr.randomAccessPointFlag=1; + } + /*stream state - map directly to seqNum*/ + if (rtp->sl_map.StreamStateIndication) { + rtp->sl_hdr.AU_sequenceNumber = gf_bs_read_int(hdr_bs, rtp->sl_map.StreamStateIndication); + au_hdr_size -= rtp->sl_map.StreamStateIndication; + } else { + rtp->sl_hdr.AU_sequenceNumber = au_idx; + } + } + /*no header present, update CTS/DTS - note we're sure there's no interleaving*/ + else { + if (num_au) { + rtp->sl_hdr.compositionTimeStamp += rtp->sl_map.ConstantDuration; + rtp->sl_hdr.decodingTimeStamp += rtp->sl_map.ConstantDuration; + } + } + /*we cannot map RTP SN to SL SN since an RTP packet may carry several SL ones - only inc by 1 seq nums*/ + rtp->sl_hdr.packetSequenceNumber += 1; + + /*force indication of CTS whenever we have a new AU*/ + + rtp->sl_hdr.compositionTimeStampFlag = (rtp->flags & GF_RTP_NEW_AU) ? 1 : 0; + + /*locate VOP start code*/ + if (rtp->sl_hdr.accessUnitStartFlag && (rtp->flags & GF_RTP_M4V_CHECK_RAP)) { + u32 i; + Bool is_rap = 0; + unsigned char *pay = (unsigned char *) payload + pay_start; + i=0; + while (isl_hdr.randomAccessPointFlag = is_rap ? 1 : 0; + } + + rtp->on_sl_packet(rtp->udta, payload + pay_start, au_size, &rtp->sl_hdr, GF_OK); + + rtp->sl_hdr.compositionTimeStampFlag = 0; + + if (rtp->flags & GF_RTP_HAS_ISMACRYP) rtp->sl_hdr.isma_BSO += au_size; + + if (au_hdr_size < rtp->sl_map.auh_min_len) break; + pay_start += au_size; + if (pay_start >= size) break; + num_au ++; + } + assert(!au_hdr_size); + + if (hdr->Marker) + rtp->flags |= GF_RTP_NEW_AU; + else + rtp->flags &= ~GF_RTP_NEW_AU; + + gf_bs_del(hdr_bs); +} + + +static void gf_rtp_parse_mpeg12_audio(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + u16 offset; + u32 mp3hdr, ts; + GF_BitStream *bs; + + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.decodingTimeStamp = hdr->TimeStamp; + + rtp->sl_hdr.accessUnitStartFlag = rtp->sl_hdr.accessUnitEndFlag ? 1 : 0; + if (rtp->flags & GF_RTP_NEW_AU) rtp->sl_hdr.accessUnitStartFlag = 1; + + /*get frag header*/ + bs = gf_bs_new(payload, size, GF_BITSTREAM_READ); + gf_bs_read_u16(bs); + offset = gf_bs_read_u16(bs); + gf_bs_del(bs); + payload += 4; + size -= 4; + mp3hdr = 0; + while (1) { + + /*frame start if no offset*/ + rtp->sl_hdr.accessUnitStartFlag = offset ? 0 : 1; + + /*new frame, store size*/ + rtp->sl_hdr.compositionTimeStampFlag = 0; + if (rtp->sl_hdr.accessUnitStartFlag) { + mp3hdr = GF_4CC((u8) payload[0], (u8) payload[1], (u8) payload[2], (u8) payload[3]); + rtp->sl_hdr.accessUnitLength = gf_mp3_frame_size(mp3hdr); + rtp->sl_hdr.compositionTimeStampFlag = 1; + } + if (!rtp->sl_hdr.accessUnitLength) break; + /*fragmented frame*/ + if (rtp->sl_hdr.accessUnitLength>size) { + rtp->on_sl_packet(rtp->udta, payload, rtp->sl_hdr.accessUnitLength, &rtp->sl_hdr, GF_OK); + rtp->sl_hdr.accessUnitLength -= size; + rtp->sl_hdr.accessUnitStartFlag = rtp->sl_hdr.accessUnitEndFlag = 0; + return; + } + /*complete frame*/ + rtp->sl_hdr.accessUnitEndFlag = 1; + rtp->on_sl_packet(rtp->udta, payload, rtp->sl_hdr.accessUnitLength, &rtp->sl_hdr, GF_OK); + payload += rtp->sl_hdr.accessUnitLength; + size -= rtp->sl_hdr.accessUnitLength; + rtp->sl_hdr.accessUnitLength = 0; + + /*if fragmented there shall not be other frames in the packet*/ + if (!rtp->sl_hdr.accessUnitStartFlag) return; + if (!size) break; + offset = 0; + /*get ts*/ + ts = gf_mp3_window_size(mp3hdr); + rtp->sl_hdr.compositionTimeStamp += ts; + rtp->sl_hdr.decodingTimeStamp += ts; + } + rtp->flags |= GF_RTP_NEW_AU; +} + +static void gf_rtp_parse_mpeg12_video(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + u8 pic_type; + + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.decodingTimeStamp = hdr->TimeStamp; + + + pic_type = payload[2] & 0x7; + payload += 4; + size -= 4; + + /*missed something*/ + if (rtp->sl_hdr.compositionTimeStamp != hdr->TimeStamp) rtp->flags |= GF_RTP_NEW_AU; + + rtp->sl_hdr.accessUnitStartFlag = (rtp->flags & GF_RTP_NEW_AU) ? 1 : 0; + rtp->sl_hdr.accessUnitEndFlag = hdr->Marker ? 1 : 0; + rtp->sl_hdr.randomAccessPointFlag = (pic_type==1) ? 1 : 0; + + if (rtp->sl_hdr.accessUnitStartFlag) { + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.compositionTimeStampFlag = 1; + } else { + rtp->sl_hdr.compositionTimeStampFlag = 0; + } + rtp->on_sl_packet(rtp->udta, payload, size, &rtp->sl_hdr, GF_OK); + if (hdr->Marker) { + rtp->flags |= GF_RTP_NEW_AU; + } else { + rtp->flags &= ~GF_RTP_NEW_AU; + } +} + +static void gf_rtp_parse_amr(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + unsigned char c, type; + char *data; + /*we support max 30 frames in one RTP packet...*/ + u32 nbFrame, i, frame_size; + /*not supported yet*/ + if (!(rtp->flags & GF_RTP_AMR_ALIGN) ) return; + + /*process toc and locate start of payload data*/ + nbFrame = 0; + while (1) { + c = payload[nbFrame + 1]; + nbFrame++; + if (!(c & 0x80)) break; + } + data = payload + nbFrame + 1; + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + /*then each frame*/ + for (i=0; i> 3); + if (rtp->payt==GF_RTP_PAYT_AMR) { + frame_size = GF_AMR_FRAME_SIZE[type]; + } else { + frame_size = GF_AMR_WB_FRAME_SIZE[type]; + } + + rtp->sl_hdr.compositionTimeStampFlag = 1; + rtp->sl_hdr.accessUnitStartFlag = 1; + rtp->sl_hdr.accessUnitEndFlag = 0; + /*send TOC*/ + rtp->on_sl_packet(rtp->udta, &payload[i+1], 1, &rtp->sl_hdr, GF_OK); + rtp->sl_hdr.packetSequenceNumber ++; + rtp->sl_hdr.compositionTimeStampFlag = 0; + rtp->sl_hdr.accessUnitStartFlag = 0; + rtp->sl_hdr.accessUnitEndFlag = 1; + /*send payload*/ + rtp->on_sl_packet(rtp->udta, data, frame_size, &rtp->sl_hdr, GF_OK); + data += frame_size; + rtp->sl_hdr.compositionTimeStamp += 160; + } +} + + +static void gf_rtp_parse_h263(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + GF_BitStream *bs; + Bool P_bit, V_bit; + u32 plen, plen_bits, offset; + char blank[2]; + + bs = gf_bs_new(payload, size, GF_BITSTREAM_READ); + /*reserved*/ + gf_bs_read_int(bs, 5); + P_bit = gf_bs_read_int(bs, 1); + V_bit = gf_bs_read_int(bs, 1); + plen = gf_bs_read_int(bs, 6); + plen_bits = gf_bs_read_int(bs, 3); + + /*VRC not supported yet*/ + if (V_bit) { + gf_bs_read_u8(bs); + } + /*extra picture header not supported yet*/ + if (plen) { + gf_bs_skip_bytes(bs, plen); + } + offset = (u32) gf_bs_get_position(bs); + gf_bs_del(bs); + + blank[0] = blank[1] = 0; + /*start*/ + if (P_bit) { + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.compositionTimeStampFlag = 1; + rtp->sl_hdr.accessUnitStartFlag = 1; + rtp->sl_hdr.accessUnitEndFlag = 0; + + if (rtp->sl_hdr.accessUnitStartFlag) { + /*the first 16 bytes are NOT sent on the wire*/ + rtp->sl_hdr.randomAccessPointFlag = (payload[offset+2]&0x02) ? 0 : 1; + } + /*send missing start code*/ + rtp->on_sl_packet(rtp->udta, (char *) blank, 2, &rtp->sl_hdr, GF_OK); + /*send payload*/ + rtp->sl_hdr.compositionTimeStampFlag = 0; + rtp->sl_hdr.accessUnitStartFlag = 0; + rtp->sl_hdr.randomAccessPointFlag = 0; + + /*if M bit set, end of frame*/ + rtp->sl_hdr.accessUnitEndFlag = hdr->Marker; + rtp->on_sl_packet(rtp->udta, payload + offset, size - offset, &rtp->sl_hdr, GF_OK); + } else { + /*middle/end of frames - if M bit set, end of frame*/ + rtp->sl_hdr.accessUnitEndFlag = hdr->Marker; + rtp->on_sl_packet(rtp->udta, payload + offset, size - offset, &rtp->sl_hdr, GF_OK); + } +} + +static void gf_rtp_ttxt_flush(GF_RTPDepacketizer *rtp, u32 ts) +{ + GF_BitStream *bs; + char *data; + u32 data_size; + if (!rtp->inter_bs) return; + + rtp->sl_hdr.compositionTimeStamp = ts; + rtp->sl_hdr.compositionTimeStampFlag = 1; + rtp->sl_hdr.accessUnitStartFlag = 1; + rtp->sl_hdr.accessUnitEndFlag = 0; + rtp->sl_hdr.randomAccessPointFlag = 1; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, rtp->sl_hdr.idleFlag, 1); + rtp->sl_hdr.idleFlag = 0; + gf_bs_write_int(bs, 0, 4); + gf_bs_write_int(bs, 1, 3); + gf_bs_write_u16(bs, 8 + (u16) gf_bs_get_position(rtp->inter_bs)); + gf_bs_write_u8(bs, rtp->sidx); + gf_bs_write_u24(bs, rtp->sl_hdr.au_duration); + gf_bs_write_u16(bs, rtp->txt_len); + gf_bs_get_content(bs, &data, &data_size); + gf_bs_del(bs); + + rtp->on_sl_packet(rtp->udta, data, data_size, &rtp->sl_hdr, GF_OK); + free(data); + rtp->sl_hdr.accessUnitStartFlag = 0; + rtp->sl_hdr.accessUnitEndFlag = 1; + gf_bs_get_content(rtp->inter_bs, &data, &data_size); + rtp->on_sl_packet(rtp->udta, data, data_size, &rtp->sl_hdr, GF_OK); + free(data); + + gf_bs_del(rtp->inter_bs); + rtp->inter_bs = NULL; + rtp->nb_txt_frag = rtp->cur_txt_frag = rtp->sidx = rtp->txt_len = rtp->nb_mod_frag = 0; +} + +static void gf_rtp_parse_ttxt(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + Bool is_utf_16; + u32 type, ttu_len, pay_start, duration, ts, sidx, txt_size; + u32 nb_frag, cur_frag; + GF_BitStream *bs; + + ts = hdr->TimeStamp; + + bs = gf_bs_new(payload, size, GF_BITSTREAM_READ); + while (gf_bs_available(bs)) { + pay_start = (u32) gf_bs_get_position(bs); + is_utf_16 = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 4); + type = gf_bs_read_int(bs, 3); + ttu_len = gf_bs_read_u16(bs); + if (ttu_len<2) break; + + if (type==1) { + /*flush any existing packet*/ + gf_rtp_ttxt_flush(rtp, (u32) rtp->sl_hdr.compositionTimeStamp); + + /*bad ttu(1)*/ + if (ttu_len<8) break; + rtp->sl_hdr.compositionTimeStamp = ts; + rtp->sl_hdr.compositionTimeStampFlag = 1; + rtp->sl_hdr.accessUnitStartFlag = 1; + rtp->sl_hdr.accessUnitEndFlag = 1; + rtp->sl_hdr.randomAccessPointFlag = 1; + gf_bs_read_u8(bs); + rtp->sl_hdr.au_duration = gf_bs_read_u24(bs); + rtp->on_sl_packet(rtp->udta, payload + pay_start, ttu_len + 1, &rtp->sl_hdr, GF_OK); + gf_bs_skip_bytes(bs, ttu_len - 6); + ts += rtp->sl_hdr.au_duration; + } + /*text segment*/ + else if (type==2) { + /*TS changed, flush packet*/ + if (rtp->sl_hdr.compositionTimeStamp < ts) { + gf_rtp_ttxt_flush(rtp, (u32) rtp->sl_hdr.compositionTimeStamp); + } + if (ttu_len<9) break; + rtp->sl_hdr.compositionTimeStamp = ts; + rtp->sl_hdr.idleFlag = is_utf_16; + nb_frag = gf_bs_read_int(bs, 4); + cur_frag = gf_bs_read_int(bs, 4); + duration = gf_bs_read_u24(bs); + sidx = gf_bs_read_u8(bs); + gf_bs_read_u16(bs);/*complete text sample size, ignored*/ + txt_size = size - 10; + + /*init - 3GPP/MPEG-4 spliting is IMHO stupid: + - nb frag & cur frags are not needed: rtp reordering insures packet are in order, and + !!!we assume fragments are sent in order!!! + - any other TTU suffices to indicate end of text string (modifiers or != RTP TS) + - replacing these 8bits field with a 16 bit absolute character offset would add error recovery + */ + if (!rtp->nb_txt_frag) { + rtp->nb_txt_frag = nb_frag; + rtp->cur_txt_frag = 0; + rtp->sidx = sidx; + } + /*flush prev if any mismatch*/ + if ((nb_frag != rtp->nb_txt_frag) || (rtp->cur_txt_frag > cur_frag)) { + gf_rtp_ttxt_flush(rtp, (u32) rtp->sl_hdr.compositionTimeStamp); + rtp->nb_txt_frag = nb_frag; + rtp->sidx = sidx; + } + if (!rtp->inter_bs) rtp->inter_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + /*we don't reorder - RTP reordering is done at lower level, if this is out of order too bad*/ + rtp->cur_txt_frag = cur_frag; + gf_bs_write_data(rtp->inter_bs, payload+10, txt_size); + gf_bs_skip_bytes(bs, txt_size); + + rtp->sl_hdr.au_duration = duration; + /*done*/ + if (hdr->Marker) { + rtp->txt_len = (u32) gf_bs_get_position(rtp->inter_bs); + gf_rtp_ttxt_flush(rtp, ts); + } + } else if ((type==3) || (type==4)) { + if (!rtp->inter_bs) rtp->inter_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*first modifier, store effective written text*/ + if (type==3) rtp->txt_len = (u32) gf_bs_get_position(rtp->inter_bs); + if (ttu_len<6) break; + + nb_frag = gf_bs_read_int(bs, 4); + if (!rtp->nb_mod_frag) rtp->nb_mod_frag = nb_frag; + else if (rtp->nb_mod_frag != nb_frag) { + gf_rtp_ttxt_flush(rtp, (u32) rtp->sl_hdr.compositionTimeStamp); + rtp->nb_mod_frag = nb_frag; + } + gf_bs_read_int(bs, 4); /*cur_frag, ignore*/ + rtp->sl_hdr.au_duration = gf_bs_read_u24(bs); + gf_bs_write_data(rtp->inter_bs, payload+7, ttu_len-6); + gf_bs_skip_bytes(bs, ttu_len-6); + + /*done*/ + if (hdr->Marker) gf_rtp_ttxt_flush(rtp, ts); + } + } + gf_bs_del(bs); +} + +static void gf_rtp_h264_flush(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, Bool missed_end) +{ + char *data; + u32 data_size, nal_s; + if (!rtp->inter_bs) return; + + data = NULL; + data_size = 0; + gf_bs_get_content(rtp->inter_bs, &data, &data_size); + gf_bs_del(rtp->inter_bs); + rtp->inter_bs = NULL; + nal_s = data_size-4; + data[0] = nal_s>>24; data[1] = nal_s>>16; data[2] = nal_s>>8; data[3] = nal_s&0xFF; + /*set F-bit since nal is corrupted*/ + if (missed_end) data[4] |= 0x80; + + rtp->sl_hdr.accessUnitEndFlag = (rtp->flags & GF_RTP_UNRELIABLE_M) ? 0 : hdr->Marker; + rtp->sl_hdr.compositionTimeStampFlag = 1; + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.decodingTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.decodingTimeStampFlag = 1; + rtp->on_sl_packet(rtp->udta, data, data_size, &rtp->sl_hdr, GF_OK); + rtp->sl_hdr.accessUnitStartFlag = 0; + rtp->sl_hdr.randomAccessPointFlag = 0; + free(data); +} + +void gf_rtp_parse_h264(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + char nalhdr[4]; + u32 nal_type; + if (rtp->h264_pck_mode==2) return; + + nal_type = payload[0] & 0x1F; + + /*set start*/ + if (rtp->sl_hdr.compositionTimeStamp != hdr->TimeStamp) { + if (rtp->flags & GF_RTP_UNRELIABLE_M) { + rtp->sl_hdr.accessUnitEndFlag = 1; + rtp->on_sl_packet(rtp->udta, NULL, 0, &rtp->sl_hdr, GF_OK); + } + rtp->sl_hdr.accessUnitEndFlag = 0; + rtp->sl_hdr.accessUnitStartFlag = 1; + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.compositionTimeStampFlag = 1; + rtp->sl_hdr.decodingTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.decodingTimeStampFlag = 1; + rtp->sl_hdr.randomAccessPointFlag = 0; + } else if (rtp->sl_hdr.accessUnitEndFlag) { + rtp->flags |= GF_RTP_UNRELIABLE_M; + GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[H264 RTP] error in Marker bit - switching to unreliable mode\n")); + } + + /*single NALU*/ + if (nal_type<23) { + if (nal_type==5) { + rtp->sl_hdr.randomAccessPointFlag = 1; + rtp->flags &= ~GF_RTP_AVC_WAIT_RAP; + } + else if (rtp->flags & GF_RTP_AVC_WAIT_RAP) + return; + + rtp->sl_hdr.accessUnitEndFlag = 0; + /*signal NALU size on 4 bytes*/ + nalhdr[0] = size>>24; nalhdr[1] = size>>16; nalhdr[2] = size>>8; nalhdr[3] = size&0xFF; + rtp->on_sl_packet(rtp->udta, nalhdr, 4, &rtp->sl_hdr, GF_OK); + rtp->sl_hdr.accessUnitStartFlag = 0; + rtp->sl_hdr.compositionTimeStampFlag = 0; + rtp->sl_hdr.accessUnitEndFlag = (rtp->flags & GF_RTP_UNRELIABLE_M) ? 0 : hdr->Marker; + + /*send NAL payload*/ + rtp->on_sl_packet(rtp->udta, payload, size, &rtp->sl_hdr, GF_OK); + } + /*STAP-A NALU*/ + else if (nal_type==24) { + u32 offset = 1; + while (offsetsl_hdr.randomAccessPointFlag = 1; + rtp->flags &= ~GF_RTP_AVC_WAIT_RAP; + } + if (rtp->flags & GF_RTP_AVC_WAIT_RAP) send = 0; + + /*signal NALU size on 4 bytes*/ + nalhdr[0] = nal_size>>24; nalhdr[1] = nal_size>>16; nalhdr[2] = nal_size>>8; nalhdr[3] = nal_size&0xFF; + if (send) { + rtp->on_sl_packet(rtp->udta, nalhdr, 4, &rtp->sl_hdr, GF_OK); + rtp->sl_hdr.accessUnitStartFlag = 0; + rtp->sl_hdr.compositionTimeStampFlag = 0; + } + rtp->sl_hdr.accessUnitEndFlag = (!(rtp->flags & GF_RTP_UNRELIABLE_M) && hdr->Marker && (offset+nal_size==size)) ? 1 : 0; + if (send) rtp->on_sl_packet(rtp->udta, payload+offset, nal_size, &rtp->sl_hdr, GF_OK); + offset += nal_size; + } + } + /*FU-A NALU*/ + else if (nal_type==28) { + Bool is_start = payload[1] & 0x80; + Bool is_end = payload[1] & 0x40; + /*flush*/ + if (is_start) gf_rtp_h264_flush(rtp, hdr, 1); + + if ((payload[1] & 0x1F) == 5) { + rtp->flags &= ~GF_RTP_AVC_WAIT_RAP; + rtp->sl_hdr.randomAccessPointFlag = 1; + } else if (rtp->flags & GF_RTP_AVC_WAIT_RAP) + return; + + /*setup*/ + if (!rtp->inter_bs) { + u8 nal_hdr; + rtp->inter_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*copy F and NRI*/ + nal_hdr = payload[0] & 0xE0; + /*start bit not set, signal corrupted data (we missed start packet)*/ + if (!is_start) nal_hdr |= 0x80; + /*copy NALU type*/ + nal_hdr |= (payload[1] & 0x1F); + /*dummy size field*/ + gf_bs_write_u32(rtp->inter_bs, 0); + gf_bs_write_u8(rtp->inter_bs, nal_hdr); + } + gf_bs_write_data(rtp->inter_bs, payload+2, size-2); + if (is_end || hdr->Marker) gf_rtp_h264_flush(rtp, hdr, 0); + } +} + + +static void gf_rtp_parse_latm(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + u32 remain, latm_hdr_size, latm_size; + + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.compositionTimeStampFlag = 1; + rtp->sl_hdr.accessUnitStartFlag = rtp->sl_hdr.accessUnitEndFlag = 1; + rtp->sl_hdr.randomAccessPointFlag = 1; + + remain = size; + while (remain) { + latm_hdr_size = latm_size = 0; + while (1) { + u8 c = *payload; + latm_hdr_size += 1; + latm_size += c; + payload ++; + if (c < 0xFF) break; + } + + rtp->on_sl_packet(rtp->udta, (char *) payload, latm_size, &rtp->sl_hdr, GF_OK); + payload += latm_size; + remain -= (latm_size+latm_hdr_size); + rtp->sl_hdr.compositionTimeStamp += rtp->sl_hdr.au_duration; + } +} + +static void gf_rtp_parse_3gpp_dims(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + u32 du_size, offset, dsize, hdr_size; + char *data, dhdr[6]; + + u32 frag_state = ((payload[0]>>3) & 0x7); + + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + rtp->sl_hdr.compositionTimeStampFlag = 1; + + if (rtp->flags & GF_RTP_NEW_AU) { + rtp->flags &= ~GF_RTP_NEW_AU; + rtp->sl_hdr.accessUnitStartFlag = 1; + } + rtp->sl_hdr.accessUnitEndFlag = 0; + if (hdr->Marker) rtp->flags |= GF_RTP_NEW_AU; + + rtp->sl_hdr.randomAccessPointFlag = (payload[0] & 0x40); + rtp->sl_hdr.AU_sequenceNumber = (payload[0] & 0x7); + + offset = 1; + while (offset < size) { + switch (frag_state) { + case 0: + { + GF_BitStream *bs; + bs = gf_bs_new(payload+offset, 2, GF_BITSTREAM_READ); + du_size = 2 + gf_bs_read_u16(bs); + gf_bs_del(bs); + if (hdr->Marker && offset+du_size>=size) { + rtp->sl_hdr.accessUnitEndFlag = 1; + } + rtp->on_sl_packet(rtp->udta, payload + offset, du_size, &rtp->sl_hdr, GF_OK); + rtp->sl_hdr.accessUnitStartFlag = 0; + offset += du_size; + } + break; + case 1: + if (rtp->inter_bs) gf_bs_del(rtp->inter_bs); + + rtp->inter_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_data(rtp->inter_bs, payload+offset, size-offset); + return; + case 2: + if (!rtp->inter_bs) return; + gf_bs_write_data(rtp->inter_bs, payload+offset, size-offset); + return; + case 3: + if (!rtp->inter_bs) return; + gf_bs_write_data(rtp->inter_bs, payload+offset, size-offset); + gf_bs_get_content(rtp->inter_bs, &data, &dsize); + gf_bs_del(rtp->inter_bs); + + /*send unit header - if dims size is >0xFFFF, use our internal hack for large units*/ + rtp->inter_bs = gf_bs_new(dhdr, 6, GF_BITSTREAM_WRITE); + if (dsize<=0xFFFF) { + gf_bs_write_u16(rtp->inter_bs, dsize); + hdr_size = 2; + } else { + gf_bs_write_u16(rtp->inter_bs, 0); + gf_bs_write_u32(rtp->inter_bs, dsize); + hdr_size = 6; + } + gf_bs_del(rtp->inter_bs); + rtp->inter_bs = NULL; + + rtp->on_sl_packet(rtp->udta, dhdr, hdr_size, &rtp->sl_hdr, GF_OK); + rtp->sl_hdr.accessUnitStartFlag = 0; + + rtp->sl_hdr.accessUnitEndFlag = hdr->Marker; + rtp->on_sl_packet(rtp->udta, data, dsize, &rtp->sl_hdr, GF_OK); + free(data); + return; + } + } + +} + + +static void gf_rtp_parse_ac3(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + u8 ft, nb_pck; + + rtp->sl_hdr.compositionTimeStampFlag = 1; + rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; + ft = payload[0]; + nb_pck = payload[1]; + payload += 2; + size -= 2; + + if (!ft) { + GF_AC3Header hdr; + rtp->sl_hdr.accessUnitStartFlag = rtp->sl_hdr.accessUnitEndFlag = 1; + while (size) { + u32 offset; + if (!gf_ac3_parser(payload, size, &offset, &hdr, 0)) { + return; + } + if (offset) { + if (offset>size) return; + payload+=offset; + size-=offset; + } + rtp->on_sl_packet(rtp->udta, payload, hdr.framesize, &rtp->sl_hdr, GF_OK); + if (size < hdr.framesize) return; + size -= hdr.framesize; + payload += hdr.framesize; + rtp->sl_hdr.compositionTimeStamp += 1536; + } + rtp->flags |= GF_RTP_NEW_AU; + } else if (ft==3) { + rtp->sl_hdr.accessUnitStartFlag = 0; + rtp->sl_hdr.accessUnitEndFlag = hdr->Marker ? 1 : 0; + rtp->on_sl_packet(rtp->udta, payload, size, &rtp->sl_hdr, GF_OK); + } else { + rtp->sl_hdr.accessUnitStartFlag = 1; + rtp->sl_hdr.accessUnitEndFlag = 0; + rtp->on_sl_packet(rtp->udta, payload, size, &rtp->sl_hdr, GF_OK); + } +} + +static u32 gf_rtp_get_payload_type(GF_RTPMap *map, GF_SDPMedia *media) +{ + u32 i, j; + + if (!stricmp(map->payload_name, "MP4V-ES") ) return GF_RTP_PAYT_MPEG4; + else if (!stricmp(map->payload_name, "mpeg4-generic")) return GF_RTP_PAYT_MPEG4; + else if (!stricmp(map->payload_name, "enc-mpeg4-generic")) return GF_RTP_PAYT_MPEG4; + /*optibase mm400 card hack*/ + else if (!stricmp(map->payload_name, "enc-generic-mp4") ) { + free(map->payload_name); + map->payload_name = strdup("enc-mpeg4-generic"); + return GF_RTP_PAYT_MPEG4; + } + + /*LATM: only without multiplexing (not tested but should be straight AUs)*/ + else if (!stricmp(map->payload_name, "MP4A-LATM")) { + GF_SDP_FMTP *fmtp; + i=0; + while ((fmtp = (GF_SDP_FMTP *) gf_list_enum(media->FMTP, &i))) { + GF_X_Attribute *att; + if (fmtp->PayloadType != map->PayloadType) continue; + //this is our payload. check cpresent is 0 + j=0; + while ((att = (GF_X_Attribute *)gf_list_enum(fmtp->Attributes, &j))) { + if (!stricmp(att->Name, "cpresent") && atoi(att->Value)) return 0; + } + } + return GF_RTP_PAYT_LATM; + } + else if (!stricmp(map->payload_name, "MPA")) return GF_RTP_PAYT_MPEG12_AUDIO; + else if (!stricmp(map->payload_name, "MPV")) return GF_RTP_PAYT_MPEG12_VIDEO; + else if (!stricmp(map->payload_name, "H263-1998") || !stricmp(map->payload_name, "H263-2000")) return GF_RTP_PAYT_H263; + else if (!stricmp(map->payload_name, "AMR")) return GF_RTP_PAYT_AMR; + else if (!stricmp(map->payload_name, "AMR-WB")) return GF_RTP_PAYT_AMR_WB; + else if (!stricmp(map->payload_name, "3gpp-tt")) return GF_RTP_PAYT_3GPP_TEXT; + else if (!stricmp(map->payload_name, "H264")) return GF_RTP_PAYT_H264_AVC; + else if (!stricmp(map->payload_name, "richmedia+xml")) return GF_RTP_PAYT_3GPP_DIMS; + else if (!stricmp(map->payload_name, "ac3")) return GF_RTP_PAYT_AC3; + else return 0; +} + + +static GF_Err payt_set_param(GF_RTPDepacketizer *rtp, char *param_name, char *param_val) +{ + u32 i, val; + char valS[3]; + GF_BitStream *bs; + + if (!rtp || !param_name) return GF_BAD_PARAM; + + /*1 - mpeg4-generic / RFC 3016 payload type items*/ + + /*PL (not needed when IOD is here)*/ + if (!stricmp(param_name, "Profile-level-id")) { + if (rtp->payt == GF_RTP_PAYT_H264_AVC) { + sscanf(param_val, "%x", &rtp->sl_map.PL_ID); + } else { + rtp->sl_map.PL_ID = atoi(param_val); + } + } + /*decoder specific info (not needed when IOD is here)*/ + else if (!stricmp(param_name, "config")) { + u32 len = strlen(param_val); + //decode the buffer - the string buffer is MSB hexadecimal + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + valS[2] = 0; + for (i=0; isl_map.config) free(rtp->sl_map.config); + rtp->sl_map.config = NULL; + gf_bs_get_content(bs, &rtp->sl_map.config, &rtp->sl_map.configSize); + gf_bs_del(bs); + } + /*mpeg4-generic payload type items required*/ + + /*constant size (size of all AUs) */ + else if (!stricmp(param_name, "ConstantSize")) { + rtp->sl_map.ConstantSize = atoi(param_val); + } + /*constant size (size of all AUs) */ + else if (!stricmp(param_name, "ConstantDuration")) { + rtp->sl_map.ConstantDuration = atoi(param_val); + } + /*object type indication (not needed when IOD is here)*/ + else if (!stricmp(param_name, "ObjectType")) { + rtp->sl_map.ObjectTypeIndication = atoi(param_val); + } + else if (!stricmp(param_name, "StreamType")) + rtp->sl_map.StreamType = atoi(param_val); + else if (!stricmp(param_name, "mode")) { + strcpy(rtp->sl_map.mode, param_val); + /*in case no IOD and no streamType/OTI in the file*/ + if (!stricmp(param_val, "AAC-hbr") || !stricmp(param_val, "AAC-lbr") || !stricmp(param_val, "CELP-vbr") || !stricmp(param_val, "CELP-cbr")) { + rtp->sl_map.StreamType = GF_STREAM_AUDIO; + rtp->sl_map.ObjectTypeIndication = 0x40; + } + /*in case no IOD and no streamType/OTI in the file*/ + else if (!stricmp(param_val, "avc-video") ) { + rtp->sl_map.StreamType = GF_STREAM_VISUAL; + rtp->sl_map.ObjectTypeIndication = 0x21; + } + } + + else if (!stricmp(param_name, "DTSDeltaLength")) rtp->sl_map.DTSDeltaLength = atoi(param_val); + else if (!stricmp(param_name, "CTSDeltaLength")) rtp->sl_map.CTSDeltaLength = atoi(param_val); + else if (!stricmp(param_name, "SizeLength")) rtp->sl_map.SizeLength = atoi(param_val); + else if (!stricmp(param_name, "IndexLength")) rtp->sl_map.IndexLength = atoi(param_val); + else if (!stricmp(param_name, "IndexDeltaLength")) rtp->sl_map.IndexDeltaLength = atoi(param_val); + else if (!stricmp(param_name, "RandomAccessIndication")) rtp->sl_map.RandomAccessIndication = atoi(param_val); + else if (!stricmp(param_name, "StreamStateIndication")) rtp->sl_map.StreamStateIndication = atoi(param_val); + else if (!stricmp(param_name, "AuxiliaryDataSizeLength")) rtp->sl_map.AuxiliaryDataSizeLength = atoi(param_val); + + /*H264/AVC config - we only handle mode 0 and 1*/ + else if (!stricmp(param_name, "packetization-mode")) + rtp->h264_pck_mode = 1; + /*AMR config*/ + else if (!stricmp(param_name, "octet-align")) + rtp->flags |= GF_RTP_AMR_ALIGN; + /*ISMACryp config*/ + else if (!stricmp(param_name, "ISMACrypCryptoSuite")) { + if (!stricmp(param_val, "AES_CTR_128")) + rtp->isma_scheme = GF_4CC('i','A','E','C'); + else + rtp->isma_scheme = 0; + } + else if (!stricmp(param_name, "ISMACrypSelectiveEncryption")) { + if (!stricmp(param_val, "1") || !stricmp(param_val, "true")) + rtp->flags |= GF_RTP_ISMA_SEL_ENC; + else + rtp->flags &= ~GF_RTP_ISMA_SEL_ENC; + } + else if (!stricmp(param_name, "ISMACrypIVLength")) + rtp->sl_map.IV_length = atoi(param_val); + else if (!stricmp(param_name, "ISMACrypDeltaIVLength")) + rtp->sl_map.IV_delta_length = atoi(param_val); + else if (!stricmp(param_name, "ISMACrypKeyIndicatorLength")) + rtp->sl_map.KI_length = atoi(param_val); + else if (!stricmp(param_name, "ISMACrypKeyIndicatorPerAU")) { + if (!stricmp(param_val, "1") || !stricmp(param_val, "true")) + rtp->flags |= GF_RTP_ISMA_HAS_KEY_IDX; + else + rtp->flags &= ~GF_RTP_ISMA_HAS_KEY_IDX; + } + else if (!stricmp(param_name, "ISMACrypKey")) + rtp->key = strdup(param_val); + + return GF_OK; +} + +static GF_Err gf_rtp_payt_setup(GF_RTPDepacketizer *rtp, GF_RTPMap *map, GF_SDPMedia *media) +{ + u32 i, j; + GF_SDP_FMTP *fmtp; + + /*reset sl map*/ + memset(&rtp->sl_map, 0, sizeof(GP_RTPSLMap)); + + if (!stricmp(map->payload_name, "enc-mpeg4-generic")) rtp->flags |= GF_RTP_HAS_ISMACRYP; + + /*then process all FMTPs*/ + i=0; + while ((fmtp = (GF_SDP_FMTP*)gf_list_enum(media->FMTP, &i))) { + GF_X_Attribute *att; + //we work with only one PayloadType for now + if (fmtp->PayloadType != map->PayloadType) continue; + j=0; + while ((att = (GF_X_Attribute *)gf_list_enum(fmtp->Attributes, &j))) { + payt_set_param(rtp, att->Name, att->Value); + } + } + + switch (rtp->payt) { + case GF_RTP_PAYT_LATM: + { + u32 AudioMuxVersion, AllStreamsSameTime, numSubFrames, numPrograms, numLayers; + GF_M4ADecSpecInfo cfg; + char *latm_dsi = rtp->sl_map.config; + GF_BitStream *bs = gf_bs_new(latm_dsi, rtp->sl_map.configSize, GF_BITSTREAM_READ); + AudioMuxVersion = gf_bs_read_int(bs, 1); + AllStreamsSameTime = gf_bs_read_int(bs, 1); + numSubFrames = gf_bs_read_int(bs, 6); + numPrograms = gf_bs_read_int(bs, 4); + numLayers = gf_bs_read_int(bs, 3); + + if (AudioMuxVersion || !AllStreamsSameTime || numSubFrames || numPrograms || numLayers) { + gf_bs_del(bs); + return GF_NOT_SUPPORTED; + } + memset(&cfg, 0, sizeof(cfg)); + cfg.base_object_type = gf_bs_read_int(bs, 5); + cfg.base_sr_index = gf_bs_read_int(bs, 4); + if (cfg.base_sr_index == 0x0F) { + cfg.base_sr = gf_bs_read_int(bs, 24); + } else { + cfg.base_sr = GF_M4ASampleRates[cfg.base_sr_index]; + } + cfg.nb_chan = gf_bs_read_int(bs, 4); + if (cfg.base_object_type==5) { + cfg.has_sbr = 1; + cfg.sbr_sr_index = gf_bs_read_int(bs, 4); + if (cfg.sbr_sr_index == 0x0F) { + cfg.sbr_sr = gf_bs_read_int(bs, 24); + } else { + cfg.sbr_sr = GF_M4ASampleRates[cfg.sbr_sr_index]; + } + cfg.sbr_object_type = gf_bs_read_int(bs, 5); + } + gf_bs_del(bs); + free(rtp->sl_map.config); + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*write as regular AAC*/ + gf_bs_write_int(bs, cfg.base_object_type, 5); + gf_bs_write_int(bs, cfg.base_sr_index, 4); + gf_bs_write_int(bs, cfg.nb_chan, 4); + gf_bs_align(bs); + gf_bs_get_content(bs, &rtp->sl_map.config, &rtp->sl_map.configSize); + gf_bs_del(bs); + rtp->sl_map.StreamType = GF_STREAM_AUDIO; + rtp->sl_map.ObjectTypeIndication = 0x40; + + /*assign depacketizer*/ + rtp->depacketize = gf_rtp_parse_latm; + } + break; + case GF_RTP_PAYT_MPEG4: + /*mark if AU header is present*/ + rtp->sl_map.auh_first_min_len = 0; + if (rtp->flags & GF_RTP_HAS_ISMACRYP) { + if (!rtp->isma_scheme) rtp->isma_scheme = GF_4CC('i','A','E','C'); + if (!rtp->sl_map.IV_length) rtp->sl_map.IV_length = 4; + + if (rtp->flags & GF_RTP_ISMA_SEL_ENC) rtp->sl_map.auh_first_min_len += 8; + else rtp->sl_map.auh_first_min_len += 8*(rtp->sl_map.IV_length + rtp->sl_map.KI_length); + } + rtp->sl_map.auh_first_min_len += rtp->sl_map.CTSDeltaLength; + rtp->sl_map.auh_first_min_len += rtp->sl_map.DTSDeltaLength; + rtp->sl_map.auh_first_min_len += rtp->sl_map.SizeLength; + rtp->sl_map.auh_first_min_len += rtp->sl_map.RandomAccessIndication; + rtp->sl_map.auh_first_min_len += rtp->sl_map.StreamStateIndication; + rtp->sl_map.auh_min_len = rtp->sl_map.auh_first_min_len; + rtp->sl_map.auh_first_min_len += rtp->sl_map.IndexLength; + rtp->sl_map.auh_min_len += rtp->sl_map.IndexDeltaLength; + /*RFC3016 flags*/ + if (!stricmp(map->payload_name, "MP4V-ES")) { + rtp->sl_map.StreamType = GF_STREAM_VISUAL; + rtp->sl_map.ObjectTypeIndication = 0x20; + } + else if (!stricmp(map->payload_name, "MP4A-LATM")) { + rtp->sl_map.StreamType = GF_STREAM_AUDIO; + rtp->sl_map.ObjectTypeIndication = 0x40; + } + /*MPEG-4 video, check RAPs if not indicated*/ + if ((rtp->sl_map.StreamType == GF_STREAM_VISUAL) && (rtp->sl_map.ObjectTypeIndication == 0x20) && !rtp->sl_map.RandomAccessIndication) { + rtp->flags |= GF_RTP_M4V_CHECK_RAP; + } + /*assign depacketizer*/ + rtp->depacketize = gf_rtp_parse_mpeg4; + break; + case GF_RTP_PAYT_MPEG12_AUDIO: + rtp->sl_map.StreamType = GF_STREAM_AUDIO; + rtp->sl_map.ObjectTypeIndication = 0x69; + /*assign depacketizer*/ + rtp->depacketize = gf_rtp_parse_mpeg12_audio; + break; + case GF_RTP_PAYT_MPEG12_VIDEO: + /*we signal RAPs*/ + rtp->sl_map.RandomAccessIndication = 1; + rtp->sl_map.StreamType = GF_STREAM_VISUAL; + /*FIXME: how to differentiate MPEG1 from MPEG2 video before any frame is received??*/ + rtp->sl_map.ObjectTypeIndication = 0x6A; + /*assign depacketizer*/ + rtp->depacketize = gf_rtp_parse_mpeg12_video; + break; + case GF_RTP_PAYT_AMR: + case GF_RTP_PAYT_AMR_WB: + { + GF_BitStream *bs; + rtp->sl_map.StreamType = GF_STREAM_AUDIO; + rtp->sl_map.ObjectTypeIndication = GPAC_OTI_MEDIA_GENERIC; + /*create DSI*/ + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + if (rtp->payt == GF_RTP_PAYT_AMR) { + gf_bs_write_u32(bs, GF_4CC('s', 'a', 'm', 'r')); + gf_bs_write_u32(bs, 8000); + gf_bs_write_u16(bs, 1); + gf_bs_write_u16(bs, 160); + } else { + gf_bs_write_u32(bs, GF_4CC('s', 'a', 'w', 'b')); + gf_bs_write_u32(bs, 16000); + gf_bs_write_u16(bs, 1); + gf_bs_write_u16(bs, 320); + } + gf_bs_write_u8(bs, 16); + gf_bs_write_u8(bs, 1); + gf_bs_get_content(bs, &rtp->sl_map.config, &rtp->sl_map.configSize); + gf_bs_del(bs); + /*assign depacketizer*/ + rtp->depacketize = gf_rtp_parse_amr; + } + break; + case GF_RTP_PAYT_H263: + { + u32 x, y, w, h; + GF_X_Attribute *att; + GF_BitStream *bs; + x = y = w = h = 0; + j=0; + while ((att = (GF_X_Attribute *)gf_list_enum(media->Attributes, &j))) { + if (stricmp(att->Name, "cliprect")) continue; + /*only get the display area*/ + sscanf(att->Value, "%d,%d,%d,%d", &y, &x, &h, &w); + } + + rtp->sl_map.StreamType = GF_STREAM_VISUAL; + rtp->sl_map.ObjectTypeIndication = GPAC_OTI_MEDIA_GENERIC; + /*create DSI*/ + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, GF_4CC('h', '2', '6', '3')); + gf_bs_write_u16(bs, w); + gf_bs_write_u16(bs, h); + gf_bs_get_content(bs, &rtp->sl_map.config, &rtp->sl_map.configSize); + gf_bs_del(bs); + /*we signal RAPs*/ + rtp->sl_map.RandomAccessIndication = 1; + /*assign depacketizer*/ + rtp->depacketize = gf_rtp_parse_h263; + } + break; + case GF_RTP_PAYT_3GPP_TEXT: + { + char *tx3g, *a_tx3g; + GF_BitStream *bs; + u32 nb_desc; + GF_SDP_FMTP *fmtp; + GF_TextConfig tcfg; + memset(&tcfg, 0, sizeof(GF_TextConfig)); + tcfg.tag = GF_ODF_TEXT_CFG_TAG; + tcfg.Base3GPPFormat = 0x10; + tcfg.MPEGExtendedFormat = 0x10; + tcfg.profileLevel = 0x10; + tcfg.timescale = map->ClockRate; + tcfg.sampleDescriptionFlags = 1; + tx3g = NULL; + + i=0; + while ((fmtp = (GF_SDP_FMTP*)gf_list_enum(media->FMTP, &i))) { + GF_X_Attribute *att; + if (fmtp->PayloadType != map->PayloadType) continue; + j=0; + while ((att = (GF_X_Attribute *)gf_list_enum(fmtp->Attributes, &j))) { + + if (!stricmp(att->Name, "width")) tcfg.text_width = atoi(att->Value); + else if (!stricmp(att->Name, "height")) tcfg.text_height = atoi(att->Value); + else if (!stricmp(att->Name, "tx")) tcfg.horiz_offset = atoi(att->Value); + else if (!stricmp(att->Name, "ty")) tcfg.vert_offset = atoi(att->Value); + else if (!stricmp(att->Name, "layer")) tcfg.layer = atoi(att->Value); + else if (!stricmp(att->Name, "max-w")) tcfg.video_width = atoi(att->Value); + else if (!stricmp(att->Name, "max-h")) tcfg.video_height = atoi(att->Value); + else if (!stricmp(att->Name, "tx3g")) tx3g = att->Value; + } + } + if (!tx3g) return GF_NON_COMPLIANT_BITSTREAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u8(bs, tcfg.Base3GPPFormat); + gf_bs_write_u8(bs, tcfg.MPEGExtendedFormat); /*MPEGExtendedFormat*/ + gf_bs_write_u8(bs, tcfg.profileLevel); /*profileLevel*/ + gf_bs_write_u24(bs, tcfg.timescale); + gf_bs_write_int(bs, 0, 1); /*no alt formats*/ + gf_bs_write_int(bs, tcfg.sampleDescriptionFlags, 2); + gf_bs_write_int(bs, 1, 1); /*we will write sample desc*/ + gf_bs_write_int(bs, 1, 1); /*video info*/ + gf_bs_write_int(bs, 0, 3); /*reserved, spec doesn't say the values*/ + gf_bs_write_u8(bs, tcfg.layer); + gf_bs_write_u16(bs, tcfg.text_width); + gf_bs_write_u16(bs, tcfg.text_height); + /*get all tx3g (comma separated)*/ + nb_desc = 1; + a_tx3g = tx3g; + while ((a_tx3g = strchr(a_tx3g, ',')) ) { + a_tx3g ++; + nb_desc ++; + } + a_tx3g = tx3g; + gf_bs_write_u8(bs, nb_desc); + while (1) { + char *next_tx3g, szOut[1000]; + u32 len; + strcpy(a_tx3g, tx3g); + next_tx3g = strchr(a_tx3g, ','); + if (next_tx3g) next_tx3g[0] = 0; + len = gf_base64_decode(a_tx3g, strlen(a_tx3g), szOut, 1000); + gf_bs_write_data(bs, szOut, len); + tx3g = strchr(tx3g, ','); + if (!tx3g) break; + tx3g += 1; + while (tx3g[0] == ' ') tx3g += 1; + } + + /*write video cfg*/ + gf_bs_write_u16(bs, tcfg.video_width); + gf_bs_write_u16(bs, tcfg.video_height); + gf_bs_write_u16(bs, tcfg.horiz_offset); + gf_bs_write_u16(bs, tcfg.vert_offset); + gf_bs_get_content(bs, &rtp->sl_map.config, &rtp->sl_map.configSize); + rtp->sl_map.StreamType = GF_STREAM_TEXT; + rtp->sl_map.ObjectTypeIndication = 0x08; + gf_bs_del(bs); + /*assign depacketizer*/ + rtp->depacketize = gf_rtp_parse_ttxt; + } + break; + case GF_RTP_PAYT_H264_AVC: + { + GF_SDP_FMTP *fmtp; + GF_AVCConfig *avcc = gf_odf_avc_cfg_new(); + avcc->AVCProfileIndication = (rtp->sl_map.PL_ID>>16) & 0xFF; + avcc->profile_compatibility = (rtp->sl_map.PL_ID>>8) & 0xFF; + avcc->AVCLevelIndication = rtp->sl_map.PL_ID & 0xFF; + avcc->configurationVersion = 1; + avcc->nal_unit_size = 4; + rtp->sl_map.StreamType = 4; + rtp->sl_map.ObjectTypeIndication = 0x21; + /*we will signal RAPs*/ + rtp->sl_map.RandomAccessIndication = 1; + /*rewrite sps and pps*/ + i=0; + while ((fmtp = (GF_SDP_FMTP*)gf_list_enum(media->FMTP, &i))) { + GF_X_Attribute *att; + if (fmtp->PayloadType != map->PayloadType) continue; + j=0; + while ((att = (GF_X_Attribute *)gf_list_enum(fmtp->Attributes, &j))) { + char *nal_ptr, *sep; + if (stricmp(att->Name, "sprop-parameter-sets")) continue; + + nal_ptr = att->Value; + while (nal_ptr) { + u32 nalt, b64size, ret; + char *b64_d; + + sep = strchr(nal_ptr, ','); + if (sep) sep[0] = 0; + + b64size = strlen(nal_ptr); + b64_d = (char*)malloc(sizeof(char)*b64size); + ret = gf_base64_decode(nal_ptr, b64size, b64_d, b64size); + b64_d[ret] = 0; + + nalt = b64_d[0] & 0x1F; + if (/*SPS*/(nalt==0x07) || /*PPS*/(nalt==0x08)) { + GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)malloc(sizeof(GF_AVCConfigSlot)); + sl->size = ret; + sl->data = (char*)malloc(sizeof(char)*sl->size); + memcpy(sl->data, b64_d, sizeof(char)*sl->size); + if (nalt==0x07) { + gf_list_add(avcc->sequenceParameterSets, sl); + } else { + gf_list_add(avcc->pictureParameterSets, sl); + } + } + free(b64_d); + + if (sep) { + sep[0] = ','; + nal_ptr = sep+1; + } else { + break; + } + } + } + } + gf_odf_avc_cfg_write(avcc, &rtp->sl_map.config, &rtp->sl_map.configSize); + gf_odf_avc_cfg_del(avcc); + } + /*assign depacketizer*/ + rtp->depacketize = gf_rtp_parse_h264; + break; + /*todo - rewrite DIMS config*/ + case GF_RTP_PAYT_3GPP_DIMS: + rtp->sl_map.StreamType = GF_STREAM_SCENE; + rtp->sl_map.ObjectTypeIndication = GPAC_OTI_SCENE_DIMS; + /*we will signal RAPs*/ + rtp->sl_map.RandomAccessIndication = 1; + /*we map DIMS CTR to AU seq num, hence 3 bits*/ + rtp->sl_map.StreamStateIndication = 3; + rtp->sl_map.IndexLength = 3; + /*assign depacketizer*/ + rtp->depacketize = gf_rtp_parse_3gpp_dims; + break; + case GF_RTP_PAYT_AC3: + rtp->sl_map.StreamType = GF_STREAM_AUDIO; + rtp->sl_map.ObjectTypeIndication = 0xA5; + rtp->sl_map.RandomAccessIndication = 1; + /*assign depacketizer*/ + rtp->depacketize = gf_rtp_parse_ac3; + break; + default: + return GF_NOT_SUPPORTED; + } + return GF_OK; +} + +GF_EXPORT +GF_RTPDepacketizer *gf_rtp_depacketizer_new(GF_SDPMedia *media, void (*sl_packet_cbk)(void *udta, char *payload, u32 size, GF_SLHeader *hdr, GF_Err e), void *udta) +{ + GF_Err e; + GF_RTPMap *map; + u32 payt; + GF_RTPDepacketizer *tmp; + + /*check RTP map. For now we only support 1 RTPMap*/ + if (!sl_packet_cbk || !media || media->fmt_list || (gf_list_count(media->RTPMaps) > 1)) return NULL; + + /*check payload type*/ + map = (GF_RTPMap *)gf_list_get(media->RTPMaps, 0); + + payt = gf_rtp_get_payload_type(map, media); + if (!payt) return NULL; + + GF_SAFEALLOC(tmp, GF_RTPDepacketizer); + tmp->payt = payt; + + e = gf_rtp_payt_setup(tmp, map, media); + if (e) { + free(tmp); + return NULL; + } + assert(tmp->depacketize); + tmp->clock_rate = map->ClockRate; + tmp->on_sl_packet = sl_packet_cbk; + tmp->udta = udta; + return tmp; +} + +GF_EXPORT +void gf_rtp_depacketizer_reset(GF_RTPDepacketizer *rtp, Bool full_reset) +{ + if (rtp) { + if (rtp->inter_bs) gf_bs_del(rtp->inter_bs); + rtp->inter_bs = NULL; + rtp->flags |= GF_RTP_NEW_AU; + if (full_reset) { + u32 dur = rtp->sl_hdr.au_duration; + memset(&rtp->sl_hdr, 0, sizeof(GF_SLHeader)); + rtp->sl_hdr.au_duration = dur; + } + } +} + +GF_EXPORT +void gf_rtp_depacketizer_del(GF_RTPDepacketizer *rtp) +{ + if (rtp) { + gf_rtp_depacketizer_reset(rtp, 0); + if (rtp->sl_map.config) free(rtp->sl_map.config); + if (rtp->key) free(rtp->key); + free(rtp); + } +} + +GF_EXPORT +void gf_rtp_depacketizer_process(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) +{ + assert(rtp && rtp->depacketize); + rtp->depacketize(rtp, hdr, payload, size); +} + + +GF_EXPORT +void gf_rtp_depacketizer_get_slconfig(GF_RTPDepacketizer *rtp, GF_SLConfig *slc) +{ + memset(slc, 0, sizeof(GF_SLConfig)); + slc->tag = GF_ODF_SLC_TAG; + + + slc->AULength = rtp->sl_map.ConstantSize; + if (rtp->sl_map.ConstantDuration) { + slc->CUDuration = slc->AUDuration = rtp->sl_map.ConstantDuration; + } else { + slc->CUDuration = slc->AUDuration = rtp->sl_hdr.au_duration; + } + slc->AUSeqNumLength = rtp->sl_map.StreamStateIndication; + + /*RTP SN is on 16 bits*/ + slc->packetSeqNumLength = 0; + /*RTP TS is on 32 bits*/ + slc->timestampLength = 32; + slc->timeScale = slc->timestampResolution = rtp->clock_rate; + slc->useTimestampsFlag = 1; + + /*we override these flags because we emulate the flags through the marker bit */ + slc->useAccessUnitEndFlag = slc->useAccessUnitStartFlag = 1; + slc->useRandomAccessPointFlag = rtp->sl_map.RandomAccessIndication; + /*by default all packets are RAP if not signaled. This is true for audio + shoud NEVER be seen with systems streams and is overriden for video (cf below)*/ + slc->hasRandomAccessUnitsOnlyFlag = rtp->sl_map.RandomAccessIndication ? 0 : 1; + /*checking RAP for video*/ + if (rtp->flags & GF_RTP_M4V_CHECK_RAP) { + slc->useRandomAccessPointFlag = 1; + slc->hasRandomAccessUnitsOnlyFlag = 0; + } + /*should work for simple carsousel without streamState indicated*/ + slc->AUSeqNumLength = rtp->sl_map.IndexLength; + +} + diff --git a/src/ietf/rtp_packetizer.c b/src/ietf/rtp_packetizer.c new file mode 100644 index 0000000..466f5be --- /dev/null +++ b/src/ietf/rtp_packetizer.c @@ -0,0 +1,575 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +void InitSL_RTP(GF_SLConfig *slc); + + + + +GF_EXPORT +GP_RTPPacketizer *gf_rtp_builder_new(u32 rtp_payt, GF_SLConfig *slc, u32 flags, + void *cbk_obj, + void (*OnNewPacket)(void *cbk, GF_RTPHeader *header), + void (*OnPacketDone)(void *cbk, GF_RTPHeader *header), + void (*OnDataReference)(void *cbk, u32 payload_size, u32 offset_from_orig), + void (*OnData)(void *cbk, char *data, u32 data_size, Bool is_head) + ) +{ + GP_RTPPacketizer *tmp; + if (!rtp_payt || !cbk_obj | !OnPacketDone) return NULL; + + GF_SAFEALLOC(tmp, GP_RTPPacketizer); + if (!tmp) return NULL; + + if (slc) { + memcpy(&tmp->sl_config, slc, sizeof(GF_SLConfig)); + } else { + memset(&tmp->sl_config, 0, sizeof(GF_SLConfig)); + tmp->sl_config.useTimestampsFlag = 1; + tmp->sl_config.timestampLength = 32; + } + tmp->OnNewPacket = OnNewPacket; + tmp->OnDataReference = OnDataReference; + tmp->OnData = OnData; + tmp->cbk_obj = cbk_obj; + tmp->OnPacketDone = OnPacketDone; + tmp->rtp_payt = rtp_payt; + tmp->flags = flags; + //default init + tmp->sl_header.AU_sequenceNumber = 1; + tmp->sl_header.packetSequenceNumber = 1; + + //we assume we start on a new AU + tmp->sl_header.accessUnitStartFlag = 1; + return tmp; +} + +GF_EXPORT +void gf_rtp_builder_del(GP_RTPPacketizer *builder) +{ + if (!builder) return; + + if (builder->payload) gf_bs_del(builder->payload); + if (builder->pck_hdr) gf_bs_del(builder->pck_hdr); + free(builder); +} + +GF_EXPORT +GF_Err gf_rtp_builder_process(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration, u8 descIndex) +{ + if (!builder) return GF_BAD_PARAM; + + switch (builder->rtp_payt) { + case GF_RTP_PAYT_MPEG4: + return gp_rtp_builder_do_mpeg4(builder, data, data_size, IsAUEnd, FullAUSize); + case GF_RTP_PAYT_MPEG12_VIDEO: + return gp_rtp_builder_do_mpeg12_video(builder, data, data_size, IsAUEnd, FullAUSize); + case GF_RTP_PAYT_MPEG12_AUDIO: + return gp_rtp_builder_do_mpeg12_audio(builder, data, data_size, IsAUEnd, FullAUSize); + case GF_RTP_PAYT_H263: + return gp_rtp_builder_do_h263(builder, data, data_size, IsAUEnd, FullAUSize); + case GF_RTP_PAYT_AMR: + case GF_RTP_PAYT_AMR_WB: + return gp_rtp_builder_do_amr(builder, data, data_size, IsAUEnd, FullAUSize); + case GF_RTP_PAYT_3GPP_TEXT: + return gp_rtp_builder_do_tx3g(builder, data, data_size, IsAUEnd, FullAUSize, duration, descIndex); + case GF_RTP_PAYT_H264_AVC: + return gp_rtp_builder_do_avc(builder, data, data_size, IsAUEnd, FullAUSize); + case GF_RTP_PAYT_QCELP: + return gp_rtp_builder_do_qcelp(builder, data, data_size, IsAUEnd, FullAUSize); + case GF_RTP_PAYT_EVRC_SMV: + return gp_rtp_builder_do_smv(builder, data, data_size, IsAUEnd, FullAUSize); + case GF_RTP_PAYT_LATM: + return gp_rtp_builder_do_latm(builder, data, data_size, IsAUEnd, FullAUSize, duration); + case GF_RTP_PAYT_3GPP_DIMS: + return gp_rtp_builder_do_dims(builder, data, data_size, IsAUEnd, FullAUSize, duration); + case GF_RTP_PAYT_AC3: + return gp_rtp_builder_do_ac3(builder, data, data_size, IsAUEnd, FullAUSize); + default: + return GF_BAD_PARAM; + } +} + + +//Compute the #params of the slMap +GF_EXPORT +void gf_rtp_builder_init(GP_RTPPacketizer *builder, u8 PayloadType, u32 PathMTU, u32 max_ptime, + u32 StreamType, u32 OTI, u32 PL_ID, + u32 avgSize, u32 maxSize, + u32 avgTS, u32 maxDTS, + u32 IV_length, u32 KI_length, + char *pref_mode) +{ + u32 k, totDelta, ismacrypt_flags; + + memset(&builder->slMap, 0, sizeof(GP_RTPSLMap)); + builder->Path_MTU = PathMTU; + builder->PayloadType = PayloadType; + builder->slMap.StreamType = StreamType; + builder->slMap.ObjectTypeIndication = OTI; + builder->slMap.PL_ID = PL_ID; + builder->max_ptime = max_ptime; + if (pref_mode) strcpy(builder->slMap.mode, pref_mode); + + + //some cst vars + builder->rtp_header.Version = 2; + builder->rtp_header.PayloadType = builder->PayloadType; + + /*our max config is with 1 packet only (SingleSL)*/ + builder->first_sl_in_rtp = 1; + /*no AUX data*/ + builder->slMap.AuxiliaryDataSizeLength= 0; + + + /*just compute max aggregation size*/ + switch (builder->rtp_payt) { + case GF_RTP_PAYT_QCELP: + case GF_RTP_PAYT_EVRC_SMV: + case GF_RTP_PAYT_AMR: + case GF_RTP_PAYT_AMR_WB: + { + u32 nb_pck = 1; + u32 block_size = 160; + /*compute max frames per packet - if no avg size, use max size per codec*/ + if (builder->flags & GP_RTP_PCK_USE_MULTI) { + if (builder->rtp_payt == GF_RTP_PAYT_QCELP) { + if (!avgSize) avgSize = 35; + nb_pck = (PathMTU-1) / avgSize; /*one-byte header*/ + if (nb_pck>10) nb_pck=10; /*cf RFC2658*/ + } else if (builder->rtp_payt == GF_RTP_PAYT_EVRC_SMV) { + if (!avgSize) avgSize = 23; + nb_pck = (PathMTU) / avgSize; + if (nb_pck>32) nb_pck=32; /*cf RFC3558*/ + } else if (builder->rtp_payt == GF_RTP_PAYT_AMR_WB) { + if (!avgSize) avgSize = 61; + nb_pck = (PathMTU-1) / avgSize; + block_size = 320; + } else { + if (!avgSize) avgSize = 32; + nb_pck = (PathMTU-1) / avgSize; + } + if (max_ptime) { + u32 max_pck = max_ptime / block_size; + if (nb_pck > max_pck) nb_pck = max_pck; + } + } + if (nb_pck<=1) { + builder->flags &= ~(GP_RTP_PCK_USE_MULTI|GP_RTP_PCK_USE_INTERLEAVING); + builder->auh_size = 1; + } else { + builder->auh_size = nb_pck; + } + /*remove all MPEG-4 and ISMA flags */ + builder->flags &= 0x07; + } + return; + case GF_RTP_PAYT_LATM: + case GF_RTP_PAYT_MPEG4: + break; + default: + /*remove all MPEG-4 and ISMA flags */ + builder->flags &= 0x07; + /*disable aggregation for visual streams*/ + if (StreamType==GF_STREAM_VISUAL) builder->flags &= ~GP_RTP_PCK_USE_MULTI; + else if (avgSize && (PathMTU <= avgSize) ) builder->flags &= ~GP_RTP_PCK_USE_MULTI; + return; + } + + builder->slMap.IV_length = IV_length; + builder->slMap.KI_length = KI_length; + + ismacrypt_flags = 0; + if (builder->flags & GP_RTP_PCK_SELECTIVE_ENCRYPTION) ismacrypt_flags |= GP_RTP_PCK_SELECTIVE_ENCRYPTION; + if (builder->flags & GP_RTP_PCK_KEY_IDX_PER_AU) ismacrypt_flags |= GP_RTP_PCK_KEY_IDX_PER_AU; + + /*mode setup*/ + if (!strnicmp(builder->slMap.mode, "AAC", 3)) { + builder->flags = GP_RTP_PCK_USE_MULTI | GP_RTP_PCK_SIGNAL_SIZE | GP_RTP_PCK_SIGNAL_AU_IDX | ismacrypt_flags; + if (builder->flags & GP_RTP_PCK_USE_INTERLEAVING) builder->slMap.ConstantDuration = avgTS; + + /*AAC LBR*/ + if (maxSize < 63) { + strcpy(builder->slMap.mode, "AAC-lbr"); + builder->slMap.IndexLength = builder->slMap.IndexDeltaLength = 2; + builder->slMap.SizeLength = 6; + } + /*AAC HBR*/ + else { + strcpy(builder->slMap.mode, "AAC-hbr"); + builder->slMap.IndexLength = builder->slMap.IndexDeltaLength = 3; + builder->slMap.SizeLength = 13; + } + goto check_header; + } + if (!strnicmp(builder->slMap.mode, "CELP", 4)) { + /*CELP-cbr*/ + if (maxSize == avgSize) { + /*reset flags (interleaving forbidden)*/ + builder->flags = GP_RTP_PCK_USE_MULTI | ismacrypt_flags; + strcpy(builder->slMap.mode, "CELP-cbr"); + builder->slMap.ConstantSize = avgSize; + builder->slMap.ConstantDuration = avgTS; + } + /*CELP VBR*/ + else { + strcpy(builder->slMap.mode, "CELP-vbr"); + builder->slMap.IndexLength = builder->slMap.IndexDeltaLength = 2; + builder->slMap.SizeLength = 6; + if (builder->flags & GP_RTP_PCK_USE_INTERLEAVING) builder->slMap.ConstantDuration = avgTS; + builder->flags = GP_RTP_PCK_USE_MULTI | GP_RTP_PCK_SIGNAL_SIZE | GP_RTP_PCK_SIGNAL_AU_IDX | ismacrypt_flags; + } + goto check_header; + } + + /*generic setup by flags*/ + + /*size*/ + if (builder->flags & GP_RTP_PCK_SIGNAL_SIZE) { + if (avgSize==maxSize) { + builder->slMap.SizeLength = 0; + builder->slMap.ConstantSize = maxSize; + } else { + builder->slMap.SizeLength = gf_get_bit_size(maxSize ? maxSize : PathMTU); + builder->slMap.ConstantSize = 0; + } + } else { + builder->slMap.SizeLength = 0; + if (builder->flags & GP_RTP_PCK_USE_MULTI) + builder->slMap.ConstantSize = (avgSize==maxSize) ? maxSize : 0; + else + builder->slMap.ConstantSize = 0; + } + + /*single SL per RTP*/ + if (!(builder->flags & GP_RTP_PCK_USE_MULTI)) { + if ( builder->sl_config.AUSeqNumLength && (builder->flags & GP_RTP_PCK_SIGNAL_AU_IDX)) { + builder->slMap.IndexLength = builder->sl_config.AUSeqNumLength; + } else { + builder->slMap.IndexLength = 0; + } + /*one packet per RTP so no delta*/ + builder->slMap.IndexDeltaLength = 0; + builder->slMap.IV_delta_length = 0; + + /*CTS Delta is always 0 since we have one SL packet per RTP*/ + builder->slMap.CTSDeltaLength = 0; + + /*DTS Delta depends on the video type*/ + if ((builder->flags & GP_RTP_PCK_SIGNAL_TS) && maxDTS ) + builder->slMap.DTSDeltaLength = gf_get_bit_size(maxDTS); + else + builder->slMap.DTSDeltaLength = 0; + + /*RAP*/ + if (builder->sl_config.useRandomAccessPointFlag && (builder->flags & GP_RTP_PCK_SIGNAL_RAP)) { + builder->slMap.RandomAccessIndication = 1; + } else { + builder->slMap.RandomAccessIndication = 0; + } + /*TODO: stream state*/ + goto check_header; + } + + /*this is the avg samples we can store per RTP packet*/ + k = PathMTU / avgSize; + if (k<=1) { + builder->flags &= ~GP_RTP_PCK_USE_MULTI; + /*keep TS signaling for B-frames (eg never default to M4V-ES when B-frames are present)*/ + //builder->flags &= ~GP_RTP_PCK_SIGNAL_TS; + builder->flags &= ~GP_RTP_PCK_SIGNAL_SIZE; + builder->flags &= ~GP_RTP_PCK_SIGNAL_AU_IDX; + builder->flags &= ~GP_RTP_PCK_USE_INTERLEAVING; + builder->flags &= ~GP_RTP_PCK_KEY_IDX_PER_AU; + gf_rtp_builder_init(builder, PayloadType, PathMTU, max_ptime, StreamType, OTI, PL_ID, avgSize, maxSize, avgTS, maxDTS, IV_length, KI_length, pref_mode); + return; + } + + /*multiple SL per RTP - check if we have to send TS*/ + builder->slMap.ConstantDuration = builder->sl_config.CUDuration; + if (!builder->slMap.ConstantDuration) { + builder->flags |= GP_RTP_PCK_SIGNAL_TS; + } + /*if we have a constant duration and are not writting TSs, make sure we write AU IDX when interleaving*/ + else if (! (builder->flags & GP_RTP_PCK_SIGNAL_TS) && (builder->flags & GP_RTP_PCK_USE_INTERLEAVING)) { + builder->flags |= GP_RTP_PCK_SIGNAL_AU_IDX; + } + + if (builder->flags & GP_RTP_PCK_SIGNAL_TS) { + /*compute CTS delta*/ + totDelta = k*avgTS; + builder->slMap.CTSDeltaLength = gf_get_bit_size(k*avgTS); + + /*compute DTS delta. Delta is ALWAYS from the CTS of the same sample*/ + if (maxDTS) + builder->slMap.DTSDeltaLength = gf_get_bit_size(maxDTS); + else + builder->slMap.DTSDeltaLength = 0; + } + + if ((builder->flags & GP_RTP_PCK_SIGNAL_AU_IDX) && builder->sl_config.AUSeqNumLength) { + builder->slMap.IndexLength = builder->sl_config.AUSeqNumLength; + /*and k-1 AUs in Delta*/ + builder->slMap.IndexDeltaLength = (builder->flags & GP_RTP_PCK_USE_INTERLEAVING) ? gf_get_bit_size(k-1) : 0; + } + + /*RAP*/ + if (builder->sl_config.useRandomAccessPointFlag && (builder->flags & GP_RTP_PCK_SIGNAL_RAP)) { + builder->slMap.RandomAccessIndication = 1; + } else { + builder->slMap.RandomAccessIndication = 0; + } + +check_header: + + /*IV delta only if interleaving (otherwise reconstruction from IV is trivial)*/ + if (IV_length && (builder->flags & GP_RTP_PCK_USE_INTERLEAVING)) { + builder->slMap.IV_delta_length = gf_get_bit_size(maxSize); + } + /*ISMACryp video mode*/ + if ((builder->slMap.StreamType==GF_STREAM_VISUAL) && (builder->slMap.ObjectTypeIndication==0x20) + && (builder->flags & GP_RTP_PCK_SIGNAL_RAP) && builder->slMap.IV_length + && !(builder->flags & GP_RTP_PCK_SIGNAL_AU_IDX) && !(builder->flags & GP_RTP_PCK_SIGNAL_SIZE) + /*shall have SignalTS*/ + && (builder->flags & GP_RTP_PCK_SIGNAL_TS) && !(builder->flags & GP_RTP_PCK_USE_MULTI) + ) { + strcpy(builder->slMap.mode, "mpeg4-video"); + } + /*ISMACryp AVC video mode*/ + else if ((builder->slMap.StreamType==GF_STREAM_VISUAL) && (builder->slMap.ObjectTypeIndication==0x21) + && (builder->flags & GP_RTP_PCK_SIGNAL_RAP) && builder->slMap.IV_length + && !(builder->flags & GP_RTP_PCK_SIGNAL_AU_IDX) && !(builder->flags & GP_RTP_PCK_SIGNAL_SIZE) + /*shall have SignalTS*/ + && (builder->flags & GP_RTP_PCK_SIGNAL_TS) && !(builder->flags & GP_RTP_PCK_USE_MULTI) + ) { + strcpy(builder->slMap.mode, "avc-video"); + } + + /*check if we use AU header or not*/ + if (!builder->slMap.SizeLength + && !builder->slMap.IndexLength + && !builder->slMap.IndexDeltaLength + && !builder->slMap.DTSDeltaLength + && !builder->slMap.CTSDeltaLength + && !builder->slMap.RandomAccessIndication + && !builder->slMap.IV_length + && !builder->slMap.KI_length + ) { + builder->has_AU_header= 0; + } else { + builder->has_AU_header = 1; + } +} + +void gp_rtp_builder_set_cryp_info(GP_RTPPacketizer *builder, u64 IV, char *key_indicator, Bool is_encrypted) +{ + if (!key_indicator) { + if (builder->key_indicator) { + /*force flush if no provision for keyIndicator per AU*/ + builder->force_flush = (builder->flags & GP_RTP_PCK_KEY_IDX_PER_AU) ? 0 : 1; + free(builder->key_indicator); + builder->key_indicator = NULL; + } + } else if (!builder->key_indicator + || + memcmp(builder->key_indicator, key_indicator, sizeof(char)*builder->slMap.KI_length) + ) { + /*force flush if no provision for keyIndicator per AU*/ + builder->force_flush = (builder->flags & GP_RTP_PCK_KEY_IDX_PER_AU) ? 0 : 1; + + if (!builder->key_indicator) builder->key_indicator = (char *) malloc(sizeof(char)*builder->slMap.KI_length); + memcpy(builder->key_indicator, key_indicator, sizeof(char)*builder->slMap.KI_length); + } + if (builder->IV != IV) { + builder->IV = IV; + if (builder->slMap.IV_delta_length && (builder->slMap.IV_delta_length < gf_get_bit_size((u32) (IV - builder->first_AU_IV) ))) { + builder->first_AU_IV = IV; + builder->force_flush = 1; + } + } + builder->is_encrypted = is_encrypted; +} + +GF_EXPORT +Bool gf_rtp_builder_get_payload_name(GP_RTPPacketizer *rtpb, char *szPayloadName, char *szMediaName) +{ + u32 flags = rtpb->flags; + + switch (rtpb->rtp_payt) { + case GF_RTP_PAYT_MPEG4: + if ((rtpb->slMap.StreamType==GF_STREAM_VISUAL) && (rtpb->slMap.ObjectTypeIndication==0x20)) { + strcpy(szMediaName, "video"); + /*ISMACryp video*/ + if ( (flags & GP_RTP_PCK_SIGNAL_RAP) && rtpb->slMap.IV_length + && !(flags & GP_RTP_PCK_SIGNAL_AU_IDX) && !(flags & GP_RTP_PCK_SIGNAL_SIZE) + && (flags & GP_RTP_PCK_SIGNAL_TS) && !(flags & GP_RTP_PCK_USE_MULTI) + ) + { + strcpy(szPayloadName, "enc-mpeg4-generic"); + return 1; + } + /*mpeg4-generic*/ + if ( (flags & GP_RTP_PCK_SIGNAL_RAP) || (flags & GP_RTP_PCK_SIGNAL_AU_IDX) || (flags & GP_RTP_PCK_SIGNAL_SIZE) + || (flags & GP_RTP_PCK_SIGNAL_TS) || (flags & GP_RTP_PCK_USE_MULTI) ) { + strcpy(szPayloadName, "mpeg4-generic"); + return 1; + } else { + strcpy(szPayloadName, "MP4V-ES"); + return 1; + } + } + /*for all other types*/ + if (rtpb->slMap.StreamType==GF_STREAM_AUDIO) strcpy(szMediaName, "audio"); + else if (rtpb->slMap.StreamType==GF_STREAM_MPEGJ) strcpy(szMediaName, "application"); + else strcpy(szMediaName, "video"); + strcpy(szPayloadName, rtpb->slMap.IV_length ? "enc-mpeg4-generic" : "mpeg4-generic"); + return 1; + case GF_RTP_PAYT_MPEG12_VIDEO: + strcpy(szMediaName, "video"); + strcpy(szPayloadName, "MPV"); + return 1; + case GF_RTP_PAYT_MPEG12_AUDIO: + strcpy(szMediaName, "audio"); + strcpy(szPayloadName, "MPA"); + return 1; + case GF_RTP_PAYT_H263: + strcpy(szMediaName, "video"); + strcpy(szPayloadName, "H263-1998"); + return 1; + case GF_RTP_PAYT_AMR: + strcpy(szMediaName, "audio"); + strcpy(szPayloadName, "AMR"); + return 1; + case GF_RTP_PAYT_AMR_WB: + strcpy(szMediaName, "audio"); + strcpy(szPayloadName, "AMR-WB"); + return 1; + case GF_RTP_PAYT_3GPP_TEXT: + strcpy(szMediaName, "text"); + strcpy(szPayloadName, "3gpp-tt"); + return 1; + case GF_RTP_PAYT_H264_AVC: + strcpy(szMediaName, "video"); + strcpy(szPayloadName, "H264"); + return 1; + case GF_RTP_PAYT_QCELP: + strcpy(szMediaName, "audio"); + strcpy(szPayloadName, "QCELP"); + return 1; + case GF_RTP_PAYT_EVRC_SMV: + strcpy(szMediaName, "audio"); + strcpy(szPayloadName, (rtpb->slMap.ObjectTypeIndication==0xA0) ? "EVRC" : "SMV"); + /*header-free version*/ + if (rtpb->auh_size<=1) strcat(szPayloadName, "0"); + return 1; + case GF_RTP_PAYT_LATM: + strcpy(szMediaName, "audio"); + strcpy(szPayloadName, "MP4A-LATM"); + return 1; + case GF_RTP_PAYT_3GPP_DIMS: + strcpy(szMediaName, "video"); + strcpy(szPayloadName, "richmedia+xml"); + return 1; + case GF_RTP_PAYT_AC3: + strcpy(szMediaName, "audio"); + strcpy(szPayloadName, "ac3"); + return 1; + default: + strcpy(szMediaName, ""); + strcpy(szPayloadName, ""); + return 0; + } + return 0; +} + + +GF_EXPORT +GF_Err gf_rtp_builder_format_sdp(GP_RTPPacketizer *builder, char *payload_name, char *sdpLine, char *dsi, u32 dsi_size) +{ + char buffer[20000], dsiString[20000]; + u32 i, k; + Bool is_first = 1; + + if ((builder->rtp_payt!=GF_RTP_PAYT_MPEG4) && (builder->rtp_payt!=GF_RTP_PAYT_LATM) ) return GF_BAD_PARAM; + +#define SDP_ADD_INT(_name, _val) { if (!is_first) strcat(sdpLine, "; "); sprintf(buffer, "%s=%d", _name, _val); strcat(sdpLine, buffer); is_first = 0;} +#define SDP_ADD_STR(_name, _val) { if (!is_first) strcat(sdpLine, "; "); sprintf(buffer, "%s=%s", _name, _val); strcat(sdpLine, buffer); is_first = 0;} + + sprintf(sdpLine, "a=fmtp:%d ", builder->PayloadType); + + /*mandatory fields*/ + if (builder->slMap.PL_ID) SDP_ADD_INT("profile-level-id", builder->slMap.PL_ID); + + if (builder->rtp_payt == GF_RTP_PAYT_LATM) SDP_ADD_INT("cpresent", 0); + + if (dsi && dsi_size) { + k = 0; + for (i=0; irtp_payt == GF_RTP_PAYT_LATM) ) return GF_OK; + + SDP_ADD_INT("streamType", builder->slMap.StreamType); + if (strcmp(builder->slMap.mode, "") && strcmp(builder->slMap.mode, "default")) { + SDP_ADD_STR("mode", builder->slMap.mode); + } else { + SDP_ADD_STR("mode", "generic"); + } + + /*optional fields*/ + if (builder->slMap.ObjectTypeIndication) SDP_ADD_INT("objectType", builder->slMap.ObjectTypeIndication); + if (builder->slMap.ConstantSize) SDP_ADD_INT("constantSize", builder->slMap.ConstantSize); + if (builder->slMap.ConstantDuration) SDP_ADD_INT("constantDuration", builder->slMap.ConstantDuration); + if (builder->slMap.maxDisplacement) SDP_ADD_INT("maxDisplacement", builder->slMap.maxDisplacement); + if (builder->slMap.deinterleaveBufferSize) SDP_ADD_INT("de-interleaveBufferSize", builder->slMap.deinterleaveBufferSize); + if (builder->slMap.SizeLength) SDP_ADD_INT("sizeLength", builder->slMap.SizeLength); + if (builder->slMap.IndexLength) SDP_ADD_INT("indexLength", builder->slMap.IndexLength); + if (builder->slMap.IndexDeltaLength) SDP_ADD_INT("indexDeltaLength", builder->slMap.IndexDeltaLength); + if (builder->slMap.CTSDeltaLength) SDP_ADD_INT("CTSDeltaLength", builder->slMap.CTSDeltaLength); + if (builder->slMap.DTSDeltaLength) SDP_ADD_INT("DTSDeltaLength", builder->slMap.DTSDeltaLength); + if (builder->slMap.RandomAccessIndication) SDP_ADD_INT("randomAccessIndication", builder->slMap.RandomAccessIndication); + if (builder->slMap.StreamStateIndication) SDP_ADD_INT("streamStateIndication", builder->slMap.StreamStateIndication); + if (builder->slMap.AuxiliaryDataSizeLength) SDP_ADD_INT("auxiliaryDataSizeLength", builder->slMap.AuxiliaryDataSizeLength); + + /*ISMACryp config*/ + if (builder->slMap.IV_length) { + /*don't write default*/ + /*SDP_ADD_STR("ISMACrypCryptoSuite", "AES_CTR_128");*/ + if (builder->flags & GP_RTP_PCK_SELECTIVE_ENCRYPTION) SDP_ADD_INT("ISMACrypSelectiveEncryption", 1); + SDP_ADD_INT("ISMACrypIVLength", builder->slMap.IV_length); + if (builder->slMap.IV_delta_length) SDP_ADD_INT("ISMACrypDeltaIVLength", builder->slMap.IV_delta_length); + if (builder->slMap.KI_length) SDP_ADD_INT("ISMACrypKeyIndicatorLength", builder->slMap.KI_length); + if (builder->flags & GP_RTP_PCK_KEY_IDX_PER_AU) SDP_ADD_INT("ISMACrypKeyIndicatorPerAU", 1); + } + return GF_OK; +} + diff --git a/src/ietf/rtp_pck_3gpp.c b/src/ietf/rtp_pck_3gpp.c new file mode 100644 index 0000000..554048b --- /dev/null +++ b/src/ietf/rtp_pck_3gpp.c @@ -0,0 +1,770 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +static void rtp_amr_flush(GP_RTPPacketizer *builder) +{ + char *hdr; + u32 hdr_size; + if (!builder->bytesInPacket) return; + gf_bs_get_content(builder->pck_hdr, &hdr, &hdr_size); + gf_bs_del(builder->pck_hdr); + builder->pck_hdr = NULL; + /*overwrite last frame F bit*/ + hdr[builder->last_au_sn] &= 0x7F; + builder->OnData(builder->cbk_obj, hdr, hdr_size, 1); + free(hdr); + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + builder->last_au_sn = 0; +} + +GF_Err gp_rtp_builder_do_amr(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize) +{ + u32 offset, rtp_ts, block_size; + + if (!data) { + rtp_amr_flush(builder); + return GF_OK; + } + + rtp_ts = (u32) builder->sl_header.compositionTimeStamp; + + offset = 0; + while (data_size>offset) { + u8 ft = (data[offset] & 0x78) >> 3; + u8 size; + + if (builder->rtp_payt == GF_RTP_PAYT_AMR_WB) { + size = GF_AMR_WB_FRAME_SIZE[ft]; + block_size = 320; + } else { + size = GF_AMR_FRAME_SIZE[ft]; + block_size = 160; + } + + /*packet full or too long*/ + if (builder->bytesInPacket + 1 + size > builder->Path_MTU) + rtp_amr_flush(builder); + + /*need new*/ + if (!builder->bytesInPacket) { + builder->rtp_header.TimeStamp = rtp_ts; + builder->rtp_header.Marker = 0; /*never set*/ + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + assert(builder->pck_hdr==NULL); + + /*always have header and TOC*/ + builder->pck_hdr = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*CMR + res (all 0, no interleaving)*/ + gf_bs_write_int(builder->pck_hdr, ft, 4); + gf_bs_write_int(builder->pck_hdr, 0, 4); + builder->bytesInPacket = 1; + /*no interleaving*/ + } + + /*F always to 1*/ + gf_bs_write_int(builder->pck_hdr, 1, 1); + gf_bs_write_int(builder->pck_hdr, ft, 4); + /*Q*/ + gf_bs_write_int(builder->pck_hdr, (data[offset] & 0x4) ? 1 : 0, 1); + gf_bs_write_int(builder->pck_hdr, 0, 2); + builder->bytesInPacket ++; + + /*remove frame type byte*/ + offset++; + + /*add frame data without rate_type byte header*/ + if (builder->OnDataReference) { + builder->OnDataReference(builder->cbk_obj, size, offset); + } else { + builder->OnData(builder->cbk_obj, data+offset, size, 0); + } + builder->last_au_sn++; + builder->bytesInPacket += size; + offset += size; + rtp_ts += block_size; + assert(builder->bytesInPacket<=builder->Path_MTU); + /*take care of aggregation, flush if needed*/ + if (builder->last_au_sn==builder->auh_size) rtp_amr_flush(builder); + } + return GF_OK; +} + +static GFINLINE u8 qes_get_rate_size(u32 idx, const u32 *rates, u32 nb_rates) +{ + u32 i; + for (i=0; ibytesInPacket) builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + builder->last_au_sn = 0; + return GF_OK; + } + + rtp_ts = (u32) builder->sl_header.compositionTimeStamp; + + + offset = 0; + while (data_size>offset) { + u8 frame_type = data[offset]; + u8 size = qes_get_rate_size(frame_type, GF_QCELP_RATE_TO_SIZE, GF_QCELP_RATE_TO_SIZE_NB); + /*reserved, not sent)*/ + if (frame_type>=5) { + offset += size; + continue; + } + /*packet full or too long*/ + if (builder->bytesInPacket + size > builder->Path_MTU) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + builder->last_au_sn = 0; + } + + /*need new*/ + if (!builder->bytesInPacket) { + builder->rtp_header.TimeStamp = rtp_ts; + builder->rtp_header.Marker = 0; /*never set*/ + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + hdr = 0;/*no interleaving*/ + builder->OnData(builder->cbk_obj, (char*)&hdr, 1, 0); + builder->bytesInPacket = 1; + } + if (builder->OnDataReference) { + builder->OnDataReference(builder->cbk_obj, size, offset); + } else { + builder->OnData(builder->cbk_obj, data+offset, size, 0); + } + builder->bytesInPacket += size; + offset += size; + rtp_ts += 160; + assert(builder->bytesInPacket<=builder->Path_MTU); + + /*take care of aggregation, flush if needed*/ + builder->last_au_sn++; + if (builder->last_au_sn==builder->auh_size) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + builder->last_au_sn = 0; + } + } + return GF_OK; +} + + +static void rtp_evrc_smv_flush(GP_RTPPacketizer *builder) +{ + if (!builder->bytesInPacket) return; + if (builder->auh_size>1) { + char *hdr; + u32 hdr_size; + /*padding*/ + if (builder->last_au_sn % 2) gf_bs_write_int(builder->pck_hdr, 0, 4); + gf_bs_get_content(builder->pck_hdr, &hdr, &hdr_size); + gf_bs_del(builder->pck_hdr); + builder->pck_hdr = NULL; + /*overwrite count*/ + hdr[0] = 0; + hdr[1] = builder->last_au_sn-1;/*MMM + frameCount-1*/ + builder->OnData(builder->cbk_obj, hdr, hdr_size, 1); + free(hdr); + } + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + builder->last_au_sn = 0; +} + +GF_Err gp_rtp_builder_do_smv(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize) +{ + u32 offset, rtp_ts; + + if (!data) { + rtp_evrc_smv_flush(builder); + return GF_OK; + } + + rtp_ts = (u32) builder->sl_header.compositionTimeStamp; + + offset = 0; + while (data_size>offset) { + u8 frame_type = data[offset]; + u8 size = qes_get_rate_size(frame_type, (u32 *)GF_SMV_EVRC_RATE_TO_SIZE, GF_SMV_EVRC_RATE_TO_SIZE_NB); + + /*reserved, not sent)*/ + if (frame_type>=5) { + offset += size; + continue; + } + /*packet full or too long*/ + if (builder->bytesInPacket + size > builder->Path_MTU) + rtp_evrc_smv_flush(builder); + + /*need new*/ + if (!builder->bytesInPacket) { + builder->rtp_header.TimeStamp = rtp_ts; + builder->rtp_header.Marker = 0; /*never set*/ + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + assert(builder->pck_hdr==NULL); + + if (builder->auh_size>1) { + builder->pck_hdr = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*RRLLLNNN (all 0, no interleaving)*/ + gf_bs_write_u8(builder->pck_hdr, 0); + /*MMM + count-1 : overriden when flushing*/ + gf_bs_write_u8(builder->pck_hdr, 0); + builder->bytesInPacket = 2; + } + } + + /*bundle mode: cat rate byte to TOC, on 4 bits*/ + if (builder->auh_size>1) { + gf_bs_write_int(builder->pck_hdr, data[offset], 4); + if (!(builder->last_au_sn % 2)) builder->bytesInPacket += 1; + } + /*note that EVEN in header-free format the rate_type byte is removed*/ + offset++; + size--; + + /*add frame data without rate_type byte header*/ + if (builder->OnDataReference) { + builder->OnDataReference(builder->cbk_obj, size, offset); + } else { + builder->OnData(builder->cbk_obj, data+offset, size, 0); + } + builder->last_au_sn++; + builder->bytesInPacket += size; + offset += size; + rtp_ts += 160; + assert(builder->bytesInPacket<=builder->Path_MTU); + /*take care of aggregation, flush if needed*/ + if (builder->last_au_sn==builder->auh_size) rtp_evrc_smv_flush(builder); + } + return GF_OK; +} + +GF_Err gp_rtp_builder_do_h263(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize) +{ + GF_BitStream *bs; + char hdr[2]; + Bool Pbit; + u32 offset, size, max_size; + + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + + /*the H263 hinter doesn't perform inter-sample concatenation*/ + if (!data) return GF_OK; + + Pbit = 1; + + /*skip 16 0'ed bits of start code*/ + offset = 2; + data_size -= 2; + max_size = builder->Path_MTU - 2; + + while(data_size > 0) { + if(data_size > max_size){ + size = max_size; + builder->rtp_header.Marker = 0; + }else{ + size = data_size; + builder->rtp_header.Marker = 1; + } + + data_size -= size; + + /*create new RTP Packet */ + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + + bs = gf_bs_new(hdr, 2, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, 0, 5); + gf_bs_write_int(bs, Pbit, 1); + gf_bs_write_int(bs, 0, 10); + gf_bs_del(bs); + + /*add header*/ + builder->OnData(builder->cbk_obj, (char*) hdr, 2, 1); + /*add payload*/ + if (builder->OnDataReference) + builder->OnDataReference(builder->cbk_obj, size, offset); + else + builder->OnData(builder->cbk_obj, data + offset, size, 0); + + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + + offset += size; + Pbit = 0; + } + return GF_OK; +} + +GF_Err gp_rtp_builder_do_tx3g(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration, u8 descIndex) +{ + GF_BitStream *bs; + char *hdr; + u32 samp_size, txt_size, pay_start, hdr_size, txt_done, cur_frag, nb_frag; + Bool is_utf_16 = 0; + + if (!data) { + /*flush packet*/ + if (builder->bytesInPacket) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + return GF_OK; + } + /*cfg packet*/ + txt_size = data[0]; txt_size <<= 8; txt_size |= (unsigned char) data[1]; + /*remove BOM*/ + pay_start = 2; + if (txt_size>2) { + /*seems 3GP only accepts BE UTF-16 (no LE, no UTF32)*/ + if (((u8) data[2]==(u8) 0xFE) && ((u8) data[3]==(u8) 0xFF)) { + is_utf_16 = 1; + pay_start = 4; + txt_size -= 2; + } + } + samp_size = data_size - pay_start; + + /*if TTU does not fit in packet flush packet*/ + if (builder->bytesInPacket && (builder->bytesInPacket + 3 + 6 + samp_size > builder->Path_MTU)) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + + /*first TTU in packet*/ + if (!builder->bytesInPacket) { + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + builder->rtp_header.Marker = 1; + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + } + /*fits entirely*/ + if (builder->bytesInPacket + 3 + 6 + samp_size <= builder->Path_MTU) { + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, is_utf_16, 1); + gf_bs_write_int(bs, 0, 4); + gf_bs_write_int(bs, 1, 3); + gf_bs_write_u16(bs, 8 + samp_size); + gf_bs_write_u8(bs, descIndex); + gf_bs_write_u24(bs, duration); + gf_bs_write_u16(bs, txt_size); + gf_bs_get_content(bs, &hdr, &hdr_size); + gf_bs_del(bs); + builder->OnData(builder->cbk_obj, (char *) hdr, hdr_size, 0); + builder->bytesInPacket += hdr_size; + free(hdr); + + if (txt_size) { + if (builder->OnDataReference) { + builder->OnDataReference(builder->cbk_obj, samp_size, pay_start); + } else { + builder->OnData(builder->cbk_obj, data + pay_start, samp_size, 0); + } + builder->bytesInPacket += samp_size; + } + /*disable aggregation*/ + if (!(builder->flags & GP_RTP_PCK_USE_MULTI)) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + return GF_OK; + } + /*doesn't fit and already data, flush packet*/ + if (builder->bytesInPacket) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + /*split unit*/ + builder->rtp_header.Marker = 0; + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + /*write all type2 units (text only) - FIXME: split at char boundaries, NOT SUPPORTED YET*/ + txt_done = 0; + nb_frag = 1; + /*all fragments needed for Type2 units*/ + while (txt_done + (builder->Path_MTU-10) < txt_size) { + txt_done += (builder->Path_MTU-10); + nb_frag += 1; + } + /*all fragments needed for Type3/4 units*/ + txt_done = txt_size; + while (txt_done + (builder->Path_MTU-7) < samp_size) { + txt_done += (builder->Path_MTU-7); + nb_frag += 1; + } + + + cur_frag = 0; + txt_done = 0; + while (txt_donePath_MTU-10) < txt_size) { + size = builder->Path_MTU-10; + } else { + size = txt_size - txt_done; + } + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, is_utf_16, 1); + gf_bs_write_int(bs, 0, 4); + gf_bs_write_int(bs, 2, 3); + gf_bs_write_u16(bs, 9 + size); + gf_bs_write_int(bs, nb_frag, 4); + gf_bs_write_int(bs, cur_frag, 4); + gf_bs_write_u24(bs, duration); + gf_bs_write_u8(bs, descIndex); + /*SLEN is the full original length minus text len and BOM (put here for buffer allocation purposes)*/ + gf_bs_write_u16(bs, samp_size); + gf_bs_get_content(bs, &hdr, &hdr_size); + gf_bs_del(bs); + builder->OnData(builder->cbk_obj, (char *) hdr, hdr_size, 0); + builder->bytesInPacket += hdr_size; + free(hdr); + + if (builder->OnDataReference) { + builder->OnDataReference(builder->cbk_obj, size, pay_start + txt_done); + } else { + builder->OnData(builder->cbk_obj, data + pay_start + txt_done, size, 0); + } + builder->bytesInPacket += size; + cur_frag++; + + /*flush packet*/ + if (cur_frag == nb_frag) { + txt_done = txt_size; + if (pay_start + txt_done == data_size) { + builder->rtp_header.Marker = 1; + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + } else { + txt_done += size; + builder->rtp_header.Marker = 0; + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + } + + txt_done = txt_size; + + /*write all modifiers - OPT: split at modifiers boundaries*/ + while (txt_donePath_MTU-7) < samp_size) { + size = builder->Path_MTU-10; + } else { + size = samp_size - txt_done; + } + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, is_utf_16, 1); + gf_bs_write_int(bs, 0, 4); + gf_bs_write_int(bs, type, 3); + gf_bs_write_u16(bs, 6 + size); + gf_bs_write_int(bs, nb_frag, 4); + gf_bs_write_int(bs, cur_frag, 4); + gf_bs_write_u24(bs, duration); + + gf_bs_get_content(bs, &hdr, &hdr_size); + gf_bs_del(bs); + builder->OnData(builder->cbk_obj, (char *) hdr, hdr_size, 0); + builder->bytesInPacket += hdr_size; + free(hdr); + + if (builder->OnDataReference) { + builder->OnDataReference(builder->cbk_obj, size, pay_start + txt_done); + } else { + builder->OnData(builder->cbk_obj, data + pay_start + txt_done, size, 0); + } + builder->bytesInPacket += size; + cur_frag++; + if (cur_frag==nb_frag) { + builder->rtp_header.Marker = 1; + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } else { + builder->rtp_header.Marker = 0; + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + txt_done += size; + } + return GF_OK; +} + + +GF_Err gp_rtp_builder_do_dims(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration) +{ + u32 frag_state; + GF_BitStream *bs; + u32 offset; + Bool is_last_du; + + /*the DIMS hinter doesn't perform inter-sample concatenation*/ + if (!data) return GF_OK; + + offset = 0; + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); + while (offset < data_size) { + u32 du_offset = 0; + u32 hdr_offset = 0; + u32 orig_size, du_size; + + orig_size = du_size = 2+gf_bs_read_u16(bs); + /*if dims size is >0xFFFF, use our internal hack for large units*/ + if (du_size==2) { + orig_size = du_size = 2+gf_bs_read_u32(bs); + hdr_offset = 4; + } + gf_bs_skip_bytes(bs, du_size-2); + + /*prepare M-bit*/ + is_last_du = (offset+du_size==data_size) ? 1 : 0; + + frag_state = 0; + while (du_size) { + u32 size_offset = 0; + u32 size = du_size; + + /*does not fit, flush required*/ + if (builder->bytesInPacket && (du_size + 1 + builder->bytesInPacket > builder->Path_MTU)) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + + /*fragmentation required*/ + if (du_size + 1 > builder->Path_MTU) { + size = builder->Path_MTU - 1; + /*first fragment*/ + if (!frag_state) { + /*size field is skipped !!*/ + size_offset = 2 + hdr_offset; + frag_state = 1; + + while (du_size - size_offset <= size) { + size--; + } + } + /*any middle fragment*/ + else frag_state = 2; + + builder->rtp_header.Marker = 0; + } + /*last fragment*/ + else if (frag_state) { + size = du_size; + frag_state = 3; + builder->rtp_header.Marker = is_last_du; + } else { + size = du_size; + builder->rtp_header.Marker = is_last_du; + } + + if (frag_state && builder->bytesInPacket) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + + /*need a new packet*/ + if (!builder->bytesInPacket) { + char dims_rtp_hdr[1]; + + /*the unit is critical, increase counter (coded on 3 bits)*/ + if (! (data[2+hdr_offset] & GF_DIMS_UNIT_P) && (frag_state <= 1) ) { + builder->last_au_sn++; + builder->last_au_sn %= 8; + } + /*set CTR value*/ + dims_rtp_hdr[0] = builder->last_au_sn; + /*if M-bit is set in the dims unit header, replicate it*/ + if (data[2+hdr_offset] & (1<<1) ) dims_rtp_hdr[0] |= (1<<6); + /*add unit fragmentation type*/ + dims_rtp_hdr[0] |= (frag_state<<3); + + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + builder->OnData(builder->cbk_obj, (char *) dims_rtp_hdr, 1, 1); + builder->bytesInPacket = 1; + } + + /*add payload*/ + if (builder->OnDataReference) + builder->OnDataReference(builder->cbk_obj, size, offset+du_offset+size_offset); + else + builder->OnData(builder->cbk_obj, data+offset+du_offset+size_offset, size, 0); + + /*if fragmentation, force packet flush even on last packet since aggregation unit do not + use the same packet format*/ + if (frag_state) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } else { + builder->bytesInPacket += size; + } + du_offset += size+size_offset; + assert(du_size>= size+size_offset); + du_size -= size+size_offset; + } + offset += orig_size; + } + if (builder->bytesInPacket) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + gf_bs_del(bs); + return GF_OK; +} + + + +static void gf_rtp_ac3_flush(GP_RTPPacketizer *builder) +{ + char hdr[2]; + if (!builder->bytesInPacket) return; + + hdr[0] = builder->ac3_ft; + hdr[1] = builder->last_au_sn; + builder->OnData(builder->cbk_obj, hdr, 2, 1); + + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + builder->last_au_sn = 0; + builder->ac3_ft = 0; +} + +GF_Err gp_rtp_builder_do_ac3(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize) +{ + char hdr[2]; + u32 offset, nb_pck; + + /*flush*/ + if (!data) { + gf_rtp_ac3_flush(builder); + return GF_OK; + } + + if ( + /*AU does not fit*/ + (builder->bytesInPacket + data_size > builder->Path_MTU) + || + /*aggregation is not enabled*/ + !(builder->flags & GP_RTP_PCK_USE_MULTI) + || + /*max ptime is exceeded*/ + (builder->max_ptime && ( (u32) builder->sl_header.compositionTimeStamp >= builder->rtp_header.TimeStamp + builder->max_ptime) ) + + ) { + gf_rtp_ac3_flush(builder); + } + + /*fits*/ + if (builder->bytesInPacket + data_size < builder->Path_MTU) { + /*need a new packet*/ + if (!builder->bytesInPacket) { + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + builder->ac3_ft = 0; + builder->rtp_header.Marker = 1; + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + /*2 bytes header*/ + builder->bytesInPacket = 2; + } + + /*add payload*/ + if (builder->OnDataReference) + builder->OnDataReference(builder->cbk_obj, data_size, 0); + else + builder->OnData(builder->cbk_obj, data, data_size, 0); + + builder->bytesInPacket += data_size; + builder->last_au_sn++; + return GF_OK; + } + + /*need fragmentation*/ + assert(!builder->bytesInPacket); + offset = 0; + nb_pck = data_size / (builder->Path_MTU-2); + if (data_size % (builder->Path_MTU-2)) nb_pck++; + builder->last_au_sn = nb_pck; + + while (offset < data_size) { + u32 pck_size = MIN(data_size-offset, builder->Path_MTU-2); + + builder->rtp_header.Marker = 0; + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + builder->rtp_header.SequenceNumber += 1; + + if (!offset) { + builder->ac3_ft = (pck_size > 5*data_size/8) ? 1 : 2; + } else { + builder->ac3_ft = 3; + if (offset + pck_size == data_size) + builder->rtp_header.Marker = 1; + } + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + + hdr[0] = builder->ac3_ft; + hdr[1] = builder->last_au_sn; + builder->OnData(builder->cbk_obj, hdr, 2, 1); + + /*add payload*/ + if (builder->OnDataReference) + builder->OnDataReference(builder->cbk_obj, pck_size, offset); + else + builder->OnData(builder->cbk_obj, data+offset, pck_size, 0); + + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + offset += pck_size; + builder->bytesInPacket = 0; + } + + return GF_OK; +} + diff --git a/src/ietf/rtp_pck_mpeg12.c b/src/ietf/rtp_pck_mpeg12.c new file mode 100644 index 0000000..a6d21ef --- /dev/null +++ b/src/ietf/rtp_pck_mpeg12.c @@ -0,0 +1,233 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +static void mpa12_do_flush(GP_RTPPacketizer *builder, Bool start_new) +{ + char *tmp; + u32 tmp_size; + /*flush*/ + if (builder->pck_hdr) { + gf_bs_get_content(builder->pck_hdr, &tmp, &tmp_size); + builder->OnData(builder->cbk_obj, tmp, tmp_size, 1); + free(tmp); + + if (gf_bs_get_size(builder->payload)) { + gf_bs_get_content(builder->payload, &tmp, &tmp_size); + builder->OnData(builder->cbk_obj, tmp, tmp_size, 0); + free(tmp); + } + + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + gf_bs_del(builder->pck_hdr); + gf_bs_del(builder->payload); + builder->pck_hdr = NULL; + builder->payload = NULL; + builder->bytesInPacket = 0; + } + if (!start_new) return; + + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + builder->pck_hdr = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + builder->payload = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*create new RTP Packet */ + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + builder->first_sl_in_rtp = 1; + builder->bytesInPacket = 0; +} + +GF_Err gp_rtp_builder_do_mpeg12_audio(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize) +{ + u32 pck_size; + u16 offset; + + /*if no data flush, if nothing start if not enough space restart*/ + if (!data || !builder->bytesInPacket || (builder->bytesInPacket + data_size > builder->Path_MTU)) { + mpa12_do_flush(builder, data ? 1 : 0); + if (!data) return GF_OK; + } + + offset = 0; + while (data_size) { + if (data_size + 4 < builder->Path_MTU) { + pck_size = data_size; + } else { + pck_size = builder->Path_MTU - 4; + } + if (builder->first_sl_in_rtp) { + gf_bs_write_u16(builder->pck_hdr, 0); + gf_bs_write_u16(builder->pck_hdr, offset); + builder->first_sl_in_rtp = 0; + builder->bytesInPacket = 2; + } + /*add reference*/ + if (builder->OnDataReference) + builder->OnDataReference(builder->cbk_obj, pck_size, offset); + else + gf_bs_write_data(builder->payload, data + offset, pck_size); + + data_size -= pck_size; + builder->bytesInPacket += pck_size; + /*start new packet if fragmenting*/ + if (data_size) { + offset += (u16) pck_size; + mpa12_do_flush(builder, 1); + } + } + /*if offset or no aggregation*/ + if (offset || !(builder->flags & GP_RTP_PCK_USE_MULTI) ) mpa12_do_flush(builder, 0); + + return GF_OK; +} + +s32 MPEG12_FindNextSliceStart(unsigned char *pbuffer, u32 startoffset, u32 buflen, u32 *slice_offset); +s32 MPEG12_FindNextStartCode(unsigned char *pbuffer, u32 buflen, u32 *optr, u32 *scode); + +#define MPEG12_PICTURE_START_CODE 0x00000100 +#define MPEG12_SEQUENCE_START_CODE 0x000001b3 + +GF_Err gp_rtp_builder_do_mpeg12_video(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize) +{ + u32 startcode, pic_type, max_pck_size, offset, prev_slice, next_slice; + Bool start_with_slice, slices_done, got_slice, first_slice, have_seq; + char mpv_hdr[4]; + char *payload, *buffer; + + /*no flsuh (no aggregation)*/ + if (!data) return GF_OK; + + offset = 0; + have_seq = 0; + + while (1) { + u32 oldoffset; + oldoffset = offset; + if (MPEG12_FindNextStartCode((unsigned char *) data + offset, data_size - offset, &offset, &startcode) < 0) + break; + + offset += oldoffset; + if (startcode == MPEG12_SEQUENCE_START_CODE) have_seq = 1; + offset += 4; + + if (startcode == MPEG12_PICTURE_START_CODE) break; + } + + max_pck_size = builder->Path_MTU - 4; + + payload = data + offset; + pic_type = (payload[1] >> 3) & 0x7; + /*first 6 bits (MBZ and T bit) not used*/ + /*temp ref on 10 bits*/ + mpv_hdr[0] = (payload[0] >> 6) & 0x3; + mpv_hdr[1] = (payload[0] << 2) | ((payload[1] >> 6) & 0x3); + mpv_hdr[2] = pic_type; + mpv_hdr[3] = 0; + + if ((pic_type==2) || (pic_type== 3)) { + mpv_hdr[3] = payload[3] << 5; + if ((payload[4] & 0x80) != 0) mpv_hdr[3] |= 0x10; + if (pic_type == 3) mpv_hdr[3] |= (payload[4] >> 3) & 0xf; + } + + /*start packet*/ + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + builder->rtp_header.Marker = 1; + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + + buffer = data; + prev_slice = 0; + start_with_slice = (MPEG12_FindNextSliceStart((unsigned char *)buffer, offset, data_size, &next_slice) >= 0) ? 1 : 0; + offset = 0; + slices_done = 0; + got_slice = start_with_slice; + first_slice = 1; + + while (data_size > 0) { + Bool last_pck; + u32 len_to_write; + + if (data_size <= max_pck_size) { + len_to_write = data_size; + last_pck = 1; + prev_slice = 0; + } else { + got_slice = (!first_slice && !slices_done && (next_slice <= max_pck_size)) ? 1 : 0; + first_slice = 0; + last_pck = 0; + + while (!slices_done && (next_slice <= max_pck_size)) { + prev_slice = next_slice; + if (MPEG12_FindNextSliceStart((unsigned char *)buffer, next_slice + 4, data_size, &next_slice) >= 0) { + got_slice = 1; + } else { + slices_done = 1; + } + } + if (got_slice) len_to_write = prev_slice; + else len_to_write = MIN(max_pck_size, data_size); + } + + mpv_hdr[2] = pic_type; + + if (have_seq) { + mpv_hdr[2] |= 0x20; + have_seq = 0; + } + if (first_slice) mpv_hdr[2] |= 0x10; + + if (got_slice || last_pck) { + mpv_hdr[2] |= 0x08; + start_with_slice = 1; + } else { + start_with_slice = 0; + } + + builder->OnData(builder->cbk_obj, mpv_hdr, 4, 0); + if (builder->OnDataReference) { + builder->OnDataReference(builder->cbk_obj, len_to_write, offset); + } else { + builder->OnData(builder->cbk_obj, data + offset, len_to_write, 0); + } + + builder->rtp_header.Marker = last_pck ? 1 : 0; + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + + offset += len_to_write; + data_size -= len_to_write; + prev_slice = 0; + next_slice -= len_to_write; + buffer += len_to_write; + + if (!last_pck) { + builder->rtp_header.Marker = 0; + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + } + } + return GF_OK; +} diff --git a/src/ietf/rtp_pck_mpeg4.c b/src/ietf/rtp_pck_mpeg4.c new file mode 100644 index 0000000..a5952de --- /dev/null +++ b/src/ietf/rtp_pck_mpeg4.c @@ -0,0 +1,615 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +//get the size of the RSLH section given the GF_SLHeader and the SLMap +static u32 gf_rtp_build_au_hdr_size(GP_RTPPacketizer *builder, GF_SLHeader *slh) +{ + u32 nbBits = 0; + + /*sel enc*/ + if (builder->flags & GP_RTP_PCK_SELECTIVE_ENCRYPTION) nbBits += 8; + /*Note: ISMACryp ALWAYS indicates IV (BSO) and KEYIDX, even when sample is not encrypted. This is + quite a waste when using selective encryption....*/ + + /*IV*/ + nbBits += builder->first_sl_in_rtp ? 8*builder->slMap.IV_length : 8*builder->slMap.IV_delta_length; + /*keyIndicator*/ + if (builder->first_sl_in_rtp || (builder->flags & GP_RTP_PCK_KEY_IDX_PER_AU)) { + nbBits += 8*builder->slMap.KI_length; + } + + /*no input header specified, compute the MAX size*/ + if (!slh) { + /*size length*/ + if (!builder->slMap.ConstantSize) nbBits += builder->slMap.SizeLength; + /*AU index length*/ + nbBits += builder->first_sl_in_rtp ? builder->slMap.IndexLength : builder->slMap.IndexDeltaLength; + /*CTS flag*/ + if (builder->slMap.CTSDeltaLength) { + nbBits += 1; + /*all non-first packets have the CTS written if asked*/ + if (!builder->first_sl_in_rtp) nbBits += builder->slMap.CTSDeltaLength; + } + if (builder->slMap.DTSDeltaLength) nbBits += 1 + builder->slMap.DTSDeltaLength; + if (builder->flags & GP_RTP_PCK_SELECTIVE_ENCRYPTION) nbBits += 8; + return nbBits; + } + + /*size length*/ + if (!builder->slMap.ConstantSize) nbBits += builder->slMap.SizeLength; + + /*AU index*/ + if (builder->first_sl_in_rtp) { + if (builder->slMap.IndexLength) nbBits += builder->slMap.IndexLength; + } else { + if (builder->slMap.IndexDeltaLength) nbBits += builder->slMap.IndexDeltaLength; + } + + /*CTS Flag*/ + if (builder->slMap.CTSDeltaLength) { + /*CTS not written if first SL*/ + if (builder->first_sl_in_rtp) slh->compositionTimeStampFlag = 0; + /*but CTS flag is always written*/ + nbBits += 1; + } else { + slh->compositionTimeStampFlag = 0; + } + /*CTS*/ + if (slh->compositionTimeStampFlag) nbBits += builder->slMap.CTSDeltaLength; + + /*DTS Flag*/ + if (builder->slMap.DTSDeltaLength) { + nbBits += 1; + } else { + slh->decodingTimeStampFlag = 0; + } + /*DTS*/ + if (slh->decodingTimeStampFlag) nbBits += builder->slMap.DTSDeltaLength; + /*RAP indication*/ + if (builder->slMap.RandomAccessIndication) nbBits ++; + /*streamState indication*/ + nbBits += builder->slMap.StreamStateIndication; + + return nbBits; +} + + +/*write the AU header section - return the nb of BITS written for AU header*/ +u32 gf_rtp_build_au_hdr_write(GP_RTPPacketizer *builder, u32 PayloadSize, u32 RTP_TS) +{ + u32 nbBits = 0; + s32 delta; + + /*selective encryption*/ + if (builder->flags & GP_RTP_PCK_SELECTIVE_ENCRYPTION) { + gf_bs_write_int(builder->pck_hdr, builder->is_encrypted, 1); + gf_bs_write_int(builder->pck_hdr, 0, 7); + nbBits = 8; + } + /*IV*/ + if (builder->first_sl_in_rtp) { + if (builder->slMap.IV_length) { + gf_bs_write_long_int(builder->pck_hdr, builder->IV, 8*builder->slMap.IV_length); + nbBits += 8*builder->slMap.IV_length; + } + } else if (builder->slMap.IV_delta_length) { + /*NOT SUPPORTED!!! this only applies to interleaving*/ + } + /*key*/ + if (builder->slMap.KI_length) { + if (builder->first_sl_in_rtp || (builder->flags & GP_RTP_PCK_KEY_IDX_PER_AU)) { + if (builder->key_indicator) gf_bs_write_data(builder->pck_hdr, builder->key_indicator, builder->slMap.KI_length); + else gf_bs_write_int(builder->pck_hdr, 0, 8*builder->slMap.KI_length); + nbBits += 8*builder->slMap.KI_length; + } + } + + + /*size length*/ + if (builder->slMap.ConstantSize) { + if (PayloadSize != builder->slMap.ConstantSize) return nbBits; + } else if (builder->slMap.SizeLength) { + /*write the AU size - if not enough bytes (real-time cases) set size to 0*/ + if (builder->sl_header.accessUnitLength >= (1<slMap.SizeLength)) { + gf_bs_write_int(builder->pck_hdr, 0, builder->slMap.SizeLength); + } else { + gf_bs_write_int(builder->pck_hdr, builder->sl_header.accessUnitLength, builder->slMap.SizeLength); + } + nbBits += builder->slMap.SizeLength; + } + /*AU index*/ + if (builder->first_sl_in_rtp) { + if (builder->slMap.IndexLength) { + gf_bs_write_int(builder->pck_hdr, builder->sl_header.AU_sequenceNumber, builder->slMap.IndexLength); + nbBits += builder->slMap.IndexLength; + } + } else { + if (builder->slMap.IndexDeltaLength) { + //check interleaving, otherwise force default (which is currently always the case) + delta = builder->sl_header.AU_sequenceNumber - builder->last_au_sn; + delta -= 1; + gf_bs_write_int(builder->pck_hdr, delta, builder->slMap.IndexDeltaLength); + nbBits += builder->slMap.IndexDeltaLength; + } + } + + /*CTS Flag*/ + if (builder->slMap.CTSDeltaLength) { + if (builder->first_sl_in_rtp) { + builder->sl_header.compositionTimeStampFlag = 0; + builder->sl_header.compositionTimeStamp = RTP_TS; + } + gf_bs_write_int(builder->pck_hdr, builder->sl_header.compositionTimeStampFlag, 1); + nbBits += 1; + } + /*CTS*/ + if (builder->sl_header.compositionTimeStampFlag) { + delta = (u32) builder->sl_header.compositionTimeStamp - RTP_TS; + gf_bs_write_int(builder->pck_hdr, delta, builder->slMap.CTSDeltaLength); + nbBits += builder->slMap.CTSDeltaLength; + } + /*DTS Flag*/ + if (builder->slMap.DTSDeltaLength) { + gf_bs_write_int(builder->pck_hdr, builder->sl_header.decodingTimeStampFlag, 1); + nbBits += 1; + } + /*DTS*/ + if (builder->sl_header.decodingTimeStampFlag) { + delta = (u32) (builder->sl_header.compositionTimeStamp - builder->sl_header.decodingTimeStamp); + gf_bs_write_int(builder->pck_hdr, delta, builder->slMap.DTSDeltaLength); + nbBits += builder->slMap.DTSDeltaLength; + } + /*RAP indication*/ + if (builder->slMap.RandomAccessIndication) { + gf_bs_write_int(builder->pck_hdr, builder->sl_header.randomAccessPointFlag, 1); + nbBits ++; + } + return nbBits; +} + + +GF_Err gp_rtp_builder_do_mpeg4(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize) +{ + char *sl_buffer, *payl_buffer; + u32 sl_buffer_size, payl_buffer_size; + u32 auh_size_tmp, rslh_tmp, bytesLeftInPacket, infoSize, pckSize, pos; + u8 flush_pck, no_split; + + flush_pck = 0; + rslh_tmp = 0; + + bytesLeftInPacket = data_size; + /*flush everything*/ + if (!data) { + if (builder->payload) goto flush_packet; + return GF_OK; + } + if (builder->payload && builder->force_flush) goto flush_packet; + + //go till done + while (bytesLeftInPacket) { + no_split = 0; + + if (builder->sl_header.accessUnitStartFlag) { + //init SL + if (builder->sl_header.compositionTimeStamp != builder->sl_header.decodingTimeStamp) { + builder->sl_header.decodingTimeStampFlag = 1; + } + builder->sl_header.compositionTimeStampFlag = 1; + builder->sl_header.accessUnitLength = FullAUSize; + + //init some vars - based on the available size and the TS + //we decide if we go with the same RTP TS serie or not + if (builder->payload) { + //don't store more than what we can (that is 2^slMap->CTSDelta - 1) + if ( (builder->flags & GP_RTP_PCK_SIGNAL_TS) + && (builder->sl_header.compositionTimeStamp - builder->rtp_header.TimeStamp >= (u32) ( 1 << builder->slMap.CTSDeltaLength) ) ) { + goto flush_packet; + } + //don't split AU if # TS , start a new RTP pck + if (builder->sl_header.compositionTimeStamp != builder->rtp_header.TimeStamp) + no_split = 1; + } + } + + /*new RTP Packet*/ + if (!builder->payload) { + /*first SL in RTP*/ + builder->first_sl_in_rtp = 1; + + /*if this is the end of an AU we will set it to 0 as soon as an AU is splited*/ + builder->rtp_header.Marker = 1; + builder->rtp_header.PayloadType = builder->PayloadType; + builder->rtp_header.SequenceNumber += 1; + + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + /*prepare the mapped headers*/ + builder->pck_hdr = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + builder->payload = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + pckSize = infoSize = 0; + builder->bytesInPacket = 0; + + /*in multiSL there is a MSLHSize structure on 2 bytes*/ + builder->auh_size = 0; + if (builder->has_AU_header) { + builder->auh_size = 16; + gf_bs_write_int(builder->pck_hdr, 0, 16); + } + flush_pck = 0; + /*and create packet*/ + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + } + + //make sure we are not interleaving too much - this should never happen actually + if (builder->slMap.IndexDeltaLength + && !builder->first_sl_in_rtp + && (builder->sl_header.AU_sequenceNumber - builder->last_au_sn >= (u32) 1<slMap.IndexDeltaLength)) { + //we cannot write this packet here + goto flush_packet; + } + /*check max ptime*/ + if (builder->max_ptime && ( (u32) builder->sl_header.compositionTimeStamp >= builder->rtp_header.TimeStamp + builder->max_ptime) ) + goto flush_packet; + + auh_size_tmp = gf_rtp_build_au_hdr_size(builder, &builder->sl_header); + + infoSize = auh_size_tmp + builder->auh_size; + infoSize /= 8; + /*align*/ + if ( (builder->auh_size + auh_size_tmp) % 8) infoSize += 1; + + + if (bytesLeftInPacket + infoSize + builder->bytesInPacket <= builder->Path_MTU) { + //End of our data chunk + pckSize = bytesLeftInPacket; + builder->sl_header.accessUnitEndFlag = IsAUEnd; + + builder->auh_size += auh_size_tmp; + + builder->sl_header.paddingFlag = builder->sl_header.paddingBits ? 1 : 0; + } else { + + //AU cannot fit in packet. If no split, start a new packet + if (no_split) goto flush_packet; + + builder->auh_size += auh_size_tmp; + + pckSize = builder->Path_MTU - (infoSize + builder->bytesInPacket); + //that's the end of the rtp packet + flush_pck = 1; + //but not of the AU -> marker is 0 + builder->rtp_header.Marker = 0; + } + + gf_rtp_build_au_hdr_write(builder, pckSize, builder->rtp_header.TimeStamp); + + //notify the user of our data structure + if (builder->OnDataReference) + builder->OnDataReference(builder->cbk_obj, pckSize, data_size - bytesLeftInPacket); + else + gf_bs_write_data(builder->payload, data + (data_size - bytesLeftInPacket), pckSize); + + + bytesLeftInPacket -= pckSize; + builder->bytesInPacket += pckSize; + /*update IV*/ + builder->IV += pckSize; + builder->sl_header.paddingFlag = 0; + builder->sl_header.accessUnitStartFlag = 0; + + //we are splitting a payload, auto increment SL seq num + if (bytesLeftInPacket) { + builder->sl_header.packetSequenceNumber += 1; + } else if (! (builder->flags & GP_RTP_PCK_USE_MULTI) ) { + builder->rtp_header.Marker = 1; + flush_pck = 1; + } + + //first SL in RTP is done + builder->first_sl_in_rtp = 0; + + //store current sl + builder->last_au_sn = builder->sl_header.AU_sequenceNumber; + + if (!flush_pck) continue; + + //done with the packet +flush_packet: + + gf_bs_align(builder->pck_hdr); + + /*no aux data yet*/ + if (builder->slMap.AuxiliaryDataSizeLength) { + //write RSLH after the MSLH + gf_bs_write_int(builder->pck_hdr, 0, builder->slMap.AuxiliaryDataSizeLength); + } + /*rewrite the size header*/ + if (builder->has_AU_header) { + pos = (u32) gf_bs_get_position(builder->pck_hdr); + gf_bs_seek(builder->pck_hdr, 0); + builder->auh_size -= 16; + gf_bs_write_int(builder->pck_hdr, builder->auh_size, 16); + gf_bs_seek(builder->pck_hdr, pos); + } + + sl_buffer = NULL; + gf_bs_get_content(builder->pck_hdr, &sl_buffer, &sl_buffer_size); + //delete our bitstream + gf_bs_del(builder->pck_hdr); + builder->pck_hdr = NULL; + + payl_buffer = NULL; + payl_buffer_size = 0; + if (!builder->OnDataReference) + gf_bs_get_content(builder->payload, &payl_buffer, &payl_buffer_size); + + gf_bs_del(builder->payload); + builder->payload = NULL; + + /*notify header*/ + builder->OnData(builder->cbk_obj, sl_buffer, sl_buffer_size, 1); + /*notify payload*/ + if (payl_buffer) { + builder->OnData(builder->cbk_obj, payl_buffer, payl_buffer_size, 0); + free(payl_buffer); + } + /*flush packet*/ + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + free(sl_buffer); + } + //packet is done, update AU markers + if (IsAUEnd) { + builder->sl_header.accessUnitStartFlag = 1; + builder->sl_header.accessUnitEndFlag = 0; + } + return GF_OK; +} + + +GF_Err gp_rtp_builder_do_avc(GP_RTPPacketizer *builder, char *nalu, u32 nalu_size, u8 IsAUEnd, u32 FullAUSize) +{ + u32 do_flush, bytesLeft, size, nal_type; + char shdr[2]; + char stap_hdr; + + do_flush = 0; + if (!nalu) do_flush = 1; + /*we only do STAP or SINGLE modes*/ + else if (builder->sl_header.accessUnitStartFlag) do_flush = 1; + /*we must NOT fragment a NALU*/ + else if (builder->bytesInPacket + nalu_size >= builder->Path_MTU) do_flush = 2; + /*aggregation is disabled*/ + else if (! (builder->flags & GP_RTP_PCK_USE_MULTI) ) do_flush = 2; + + if (builder->bytesInPacket && do_flush) { + builder->rtp_header.Marker = (do_flush==1) ? 1 : 0; + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + + if (!nalu) return GF_OK; + + /*need a new RTP packet*/ + if (!builder->bytesInPacket) { + builder->rtp_header.PayloadType = builder->PayloadType; + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + builder->avc_non_idr = 1; + } + + /*check NAL type to see if disposable or not*/ + nal_type = nalu[0] & 0x1F; + switch (nal_type) { + case GF_AVC_NALU_NON_IDR_SLICE: + case GF_AVC_NALU_ACCESS_UNIT: + case GF_AVC_NALU_END_OF_SEQ: + case GF_AVC_NALU_END_OF_STREAM: + case GF_AVC_NALU_FILLER_DATA: + break; + default: + builder->avc_non_idr = 0; + break; + } + + /*at this point we're sure the NALU fits in current packet OR must be splitted*/ + + /*pb: we don't know if next NALU from this AU will be small enough to fit in the packet, so we always + go for stap...*/ + if (builder->bytesInPacket+nalu_sizePath_MTU) { + Bool use_stap = 0; + /*if this is the AU end and no NALU in packet, go for single mode*/ + if (IsAUEnd && !builder->bytesInPacket) use_stap = 0; + + if (use_stap) { + /*declare STAP-A NAL*/ + if (!builder->bytesInPacket) { + /*copy over F and NRI from first nal in packet and assign type*/ + stap_hdr = (nalu[0] & 0xE0) | 24; + builder->OnData(builder->cbk_obj, (char *) &stap_hdr, 1, 0); + builder->bytesInPacket = 1; + } + /*add NALU size*/ + shdr[0] = nalu_size>>8; + shdr[1] = nalu_size&0x00ff; + builder->OnData(builder->cbk_obj, (char *)shdr, 2, 0); + builder->bytesInPacket += 2; + } + /*add data*/ + if (builder->OnDataReference) + builder->OnDataReference(builder->cbk_obj, nalu_size, 0); + else + builder->OnData(builder->cbk_obj, nalu, nalu_size, 0); + + builder->bytesInPacket += nalu_size; + + if (IsAUEnd) { + builder->rtp_header.Marker = 1; + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + } + /*fragmentation unit*/ + else { + u32 offset; + assert(nalu_size>=builder->Path_MTU); + assert(!builder->bytesInPacket); + /*FU payload doesn't have the NAL hdr*/ + bytesLeft = nalu_size - 1; + offset = 1; + while (bytesLeft) { + if (2 + bytesLeft > builder->Path_MTU) { + size = builder->Path_MTU - 2; + } else { + size = bytesLeft; + } + + /*copy over F and NRI from nal in packet and assign type*/ + shdr[0] = (nalu[0] & 0xE0) | 28; + /*copy over NAL type from nal and set start bit and end bit*/ + shdr[1] = (nalu[0] & 0x1F); + /*start bit*/ + if (offset==1) shdr[1] |= 0x80; + /*end bit*/ + else if (size == bytesLeft) shdr[1] |= 0x40; + + builder->OnData(builder->cbk_obj, (char *)shdr, 2, 0); + + /*add data*/ + if (builder->OnDataReference) + builder->OnDataReference(builder->cbk_obj, size, offset); + else + builder->OnData(builder->cbk_obj, nalu+offset, size, 0); + + offset += size; + bytesLeft -= size; + + /*flush no matter what (FUs cannot be agreggated)*/ + builder->rtp_header.Marker = (IsAUEnd && !bytesLeft) ? 1 : 0; + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + + if (bytesLeft) { + builder->rtp_header.PayloadType = builder->PayloadType; + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + builder->rtp_header.SequenceNumber += 1; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + } + } + } + + return GF_OK; +} + +void latm_flush(GP_RTPPacketizer *builder) +{ + if (builder->bytesInPacket) { + builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); + builder->bytesInPacket = 0; + } + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; +} + +GF_Err gp_rtp_builder_do_latm(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration) +{ + u32 size, latm_hdr_size, i, data_offset; + Bool fragmented; + unsigned char *latm_hdr; + + if (!data) { + latm_flush(builder); + return GF_OK; + } + + if ((builder->flags & GP_RTP_PCK_USE_MULTI) && builder->max_ptime) { + if ((u32) builder->sl_header.compositionTimeStamp + duration >= builder->rtp_header.TimeStamp + builder->max_ptime) + latm_flush(builder); + } + /*compute max size for frame, flush current if this doesn't fit*/ + latm_hdr_size = (data_size / 255) + 1; + if (latm_hdr_size+data_size > builder->Path_MTU - builder->bytesInPacket) { + latm_flush(builder); + } + + data_offset = 0; + fragmented = 0; + while (data_size > 0) { + + latm_hdr_size = (data_size / 255) + 1; + /*fragmenting*/ + if (latm_hdr_size + data_size > builder->Path_MTU) { + assert(!builder->bytesInPacket); + fragmented = 1; + latm_hdr_size = (builder->Path_MTU / 255) + 1; + size = builder->Path_MTU - latm_hdr_size; + builder->rtp_header.Marker = 0; + } + /*last fragment or full AU*/ + else { + fragmented = 0; + size = data_size; + builder->rtp_header.Marker = 1; + } + data_size -= size; + + /*create new RTP Packet if needed*/ + if (!builder->bytesInPacket) { + builder->rtp_header.SequenceNumber += 1; + builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; + builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); + } + + /* compute AudioMuxUnit header */ + latm_hdr_size = (size / 255) + 1; + latm_hdr = (unsigned char *)malloc( sizeof(char) * latm_hdr_size); + for (i=0; iOnData(builder->cbk_obj, (char*) latm_hdr, latm_hdr_size, 0); + builder->bytesInPacket += latm_hdr_size; + free(latm_hdr); + + /*add payload*/ + if (builder->OnDataReference) { + builder->OnDataReference(builder->cbk_obj, size, data_offset); + } else + builder->OnData(builder->cbk_obj, data, size, 0); + + builder->bytesInPacket += size; + + data_offset += size; + + /*fragmented AU, always flush packet*/ + if (!builder->rtp_header.Marker) latm_flush(builder); + } + /*if the AU has been fragmented or we don't use RTP aggregation, flush*/ + if (! (builder->flags & GP_RTP_PCK_USE_MULTI) ) fragmented = 1; + if (fragmented) latm_flush(builder); + + return GF_OK; +} diff --git a/src/ietf/rtsp_command.c b/src/ietf/rtsp_command.c new file mode 100644 index 0000000..ea8048a --- /dev/null +++ b/src/ietf/rtsp_command.c @@ -0,0 +1,579 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +GF_EXPORT +GF_RTSPCommand *gf_rtsp_command_new() +{ + GF_RTSPCommand *tmp; + GF_SAFEALLOC(tmp, GF_RTSPCommand); + tmp->Xtensions = gf_list_new(); + tmp->Transports = gf_list_new(); + return tmp; +} + + +#define COM_FREE_CLEAN(hdr) if (com->hdr) free(com->hdr); \ + com->hdr = NULL; + +GF_EXPORT +void gf_rtsp_command_reset(GF_RTSPCommand *com) +{ + GF_RTSPTransport *trans; + GF_X_Attribute *att; + if (!com) return; + + //free all headers + COM_FREE_CLEAN(Accept); + COM_FREE_CLEAN(Accept_Encoding); + COM_FREE_CLEAN(Accept_Language); + COM_FREE_CLEAN(Authorization); + COM_FREE_CLEAN(Cache_Control); + COM_FREE_CLEAN(Conference); + COM_FREE_CLEAN(Connection); + COM_FREE_CLEAN(From); + COM_FREE_CLEAN(Proxy_Authorization); + COM_FREE_CLEAN(Proxy_Require); + COM_FREE_CLEAN(Referer); + COM_FREE_CLEAN(Session); + COM_FREE_CLEAN(User_Agent); + COM_FREE_CLEAN(body); + COM_FREE_CLEAN(service_name); + COM_FREE_CLEAN(ControlString); + COM_FREE_CLEAN(method); + + //this is for server only, set to OK by default + com->StatusCode = NC_RTSP_OK; + + + com->user_data = NULL; + + com->Bandwidth = com->Blocksize = com->Content_Length = com->CSeq = 0; + com->Scale = com->Speed = 0.0; + if (com->Range) free(com->Range); + com->Range = NULL; + + while (gf_list_count(com->Transports)) { + trans = (GF_RTSPTransport *) gf_list_get(com->Transports, 0); + gf_list_rem(com->Transports, 0); + gf_rtsp_transport_del(trans); + } + while (gf_list_count(com->Xtensions)) { + att = (GF_X_Attribute*)gf_list_get(com->Xtensions, 0); + gf_list_rem(com->Xtensions, 0); + free(att->Name); + free(att->Value); + free(att); + } +} + +GF_EXPORT +void gf_rtsp_command_del(GF_RTSPCommand *com) +{ + if (!com) return; + gf_rtsp_command_reset(com); + gf_list_del(com->Xtensions); + gf_list_del(com->Transports); + free(com); +} + + +GF_Err RTSP_WriteCommand(GF_RTSPSession *sess, GF_RTSPCommand *com, unsigned char *req_buffer, + unsigned char **out_buffer, u32 *out_size) +{ + u32 i, cur_pos, size, count; + char *buffer, temp[50]; + GF_RTSPTransport *trans; + GF_X_Attribute *att; + + *out_buffer = NULL; + + size = RTSP_WRITE_STEPALLOC; + buffer = (char *) malloc(size); + cur_pos = 0; + + //request + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, req_buffer); + + //then all headers + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept", com->Accept); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Encoding", com->Accept_Encoding); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Language", com->Accept_Language); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Authorization", com->Authorization); + if (com->Bandwidth) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Bandwidth: "); + RTSP_WRITE_INT(buffer, size, cur_pos, com->Bandwidth, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + if (com->Blocksize) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Blocksize: "); + RTSP_WRITE_INT(buffer, size, cur_pos, com->Blocksize, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Cache-Control", com->Cache_Control); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Conference", com->Conference); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Connection", com->Connection); + //if we have a body write the content length + if (com->body) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Content-Length: "); + RTSP_WRITE_INT(buffer, size, cur_pos, strlen(com->body), 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + //write the CSeq - use the SESSION CSeq + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "CSeq: "); + RTSP_WRITE_INT(buffer, size, cur_pos, sess->CSeq, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + + RTSP_WRITE_HEADER(buffer, size, cur_pos, "From", com->From); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Authorization", com->Proxy_Authorization); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Require", com->Proxy_Require); + + //Range, only NPT + if (com->Range && !com->Range->UseSMPTE) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Range: npt="); + RTSP_WRITE_FLOAT(buffer, size, cur_pos, com->Range->start); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-"); + if (com->Range->end > com->Range->start) { + RTSP_WRITE_FLOAT(buffer, size, cur_pos, com->Range->end); + } + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Referer", com->Referer); + if (com->Scale != 0.0) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Scale: "); + RTSP_WRITE_FLOAT(buffer, size, cur_pos, com->Scale); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Session", com->Session); + if (com->Speed != 0.0) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Speed: "); + RTSP_WRITE_FLOAT(buffer, size, cur_pos, com->Speed); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + + //transport info + count = gf_list_count(com->Transports); + if (count) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Transport: "); + for (i=0; iTransports, i); + + //then write the structure + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->Profile); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";unicast" : ";multicast")); + if (trans->destination) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";destination="); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->destination); + } + if (trans->source) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";source="); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->source); + } + if (trans->IsRecord) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";mode=RECORD"); + if (trans->Append) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";append"); + } + if (trans->IsInterleaved) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";interleaved="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtpID, 0); + if (trans->rtcpID != trans->rtpID) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-"); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtcpID, 0); + } + } + if (trans->port_first) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->IsUnicast ? ";server_port=" : ";port="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_first, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-"); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_last, 0); + } + if (/*trans->IsUnicast && */trans->client_port_first) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";client_port="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_first, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-"); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_last, 0); + } + //multicast specific + if (!trans->IsUnicast) { + if (trans->MulticastLayers) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";layers="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->MulticastLayers, 0); + } + if (trans->TTL) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ttl="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->TTL, 0); + } + } + if (trans->SSRC) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ssrc="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->SSRC, 0); + } + } + //done with transport + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + RTSP_WRITE_HEADER(buffer, size, cur_pos, "User-Agent", com->User_Agent); + + //eXtensions + count = gf_list_count(com->Xtensions); + for (i=0; iXtensions, i); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "x-"); + RTSP_WRITE_HEADER(buffer, size, cur_pos, att->Name, att->Value); + } + + //the end of header + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + //then body + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, com->body); + //the end of message ? to check, should not be needed... +// RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + + *out_buffer = (unsigned char *)buffer; + *out_size = strlen(buffer); + return GF_OK; +} + + +//format a DESCRIBE, SETUP, PLAY or PAUSE on a session +//YOUR COMMAND MUST BE FORMATTED ACCORDINGLY +//sCtrl contains a control string if needed, formating the REQUEST as server_url/service_name/sCtrl +GF_EXPORT +GF_Err gf_rtsp_send_command(GF_RTSPSession *sess, GF_RTSPCommand *com) +{ + GF_Err e; + char *sCtrl; + const char *rad; + u32 size; + char buffer[1024], *result, *body; + + if (!com || !com->method) return GF_BAD_PARAM; + + sCtrl = com->ControlString; + + //NB: OPTIONS is not sent this way + if (strcmp(com->method, GF_RTSP_DESCRIBE) + && strcmp(com->method, GF_RTSP_ANNOUNCE) + && strcmp(com->method, GF_RTSP_GET_PARAMETER) + && strcmp(com->method, GF_RTSP_SET_PARAMETER) + && strcmp(com->method, GF_RTSP_SETUP) + && strcmp(com->method, GF_RTSP_PLAY) + && strcmp(com->method, GF_RTSP_PAUSE) + && strcmp(com->method, GF_RTSP_RECORD) + && strcmp(com->method, GF_RTSP_REDIRECTE) + && strcmp(com->method, GF_RTSP_TEARDOWN) + && strcmp(com->method, GF_RTSP_OPTIONS) + + ) return GF_BAD_PARAM; + + //check the state machine + if (strcmp(com->method, GF_RTSP_PLAY) + && strcmp(com->method, GF_RTSP_PAUSE) + && strcmp(com->method, GF_RTSP_RECORD) + && sess->RTSP_State != GF_RTSP_STATE_INIT) + return GF_SERVICE_ERROR; + + //aggregation is ONLY for the same request - unclear in RFC2326 ... + //it is often mentioned "queued requests" at the server, like 3 PLAYS + //and a PAUSE .... + + /* + else if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL + && strcmp(com->method, sess->RTSPLastRequest)) + && strcmp(com->method, GF_RTSP_OPTIONS)) + + return GF_BAD_PARAM; +*/ + + //OPTIONS must have a parameter string + if (!strcmp(com->method, GF_RTSP_OPTIONS) && !sCtrl) return GF_BAD_PARAM; + + + //update sequence number + sess->CSeq += 1; + sess->NbPending += 1; + + if (!strcmp(com->method, GF_RTSP_OPTIONS)) { + sprintf(buffer, "OPTIONS %s %s\r\n", sCtrl, GF_RTSP_VERSION); + } else { + rad = (sess->ConnectionType == GF_SOCK_TYPE_TCP) ? "rtsp" : "rtspu"; + if (sCtrl) { + //if both server and service names are included in the control, just + //use the control + if (strstr(sCtrl, sess->Server) && strstr(sCtrl, sess->Service)) { + sprintf(buffer, "%s %s %s\r\n", com->method, sCtrl, GF_RTSP_VERSION); + } + //if service is specified in ctrl, do not rewrite it + else if (strstr(sCtrl, sess->Service)) { + sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sCtrl, GF_RTSP_VERSION); + } + else if (!strnicmp(sCtrl, "rtsp", 4)) { + sprintf(buffer, "%s %s %s\r\n", com->method, sCtrl, GF_RTSP_VERSION); + } + //otherwise rewrite full URL + else { + sprintf(buffer, "%s %s://%s/%s/%s %s\r\n", com->method, rad, sess->Server, sess->Service, sCtrl, GF_RTSP_VERSION); +// sprintf(buffer, "%s %s://%s:%d/%s/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, sCtrl, GF_RTSP_VERSION); + } + } else { + sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, GF_RTSP_VERSION); + } + } + + //Body on ANNOUNCE, GET_PARAMETER, SET_PARAMETER ONLY + body = NULL; + if (strcmp(com->method, GF_RTSP_ANNOUNCE) + && strcmp(com->method, GF_RTSP_GET_PARAMETER) + && strcmp(com->method, GF_RTSP_SET_PARAMETER) + ) { + //this is an error, but don't say anything + if (com->body) { + body = com->body; + com->body = NULL; + } + } + + result = NULL; + e = RTSP_WriteCommand(sess, com, (unsigned char *)buffer, (unsigned char **) &result, &size); + //restore body if needed + if (body) com->body = body; + if (e) goto exit; + + + GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Sending Command:\n%s\n", result)); + + //send buffer + e = gf_rtsp_send_data(sess, result, size); + if (e) goto exit; + + + //update our state + if (!strcmp(com->method, GF_RTSP_RECORD)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL; + else if (!strcmp(com->method, GF_RTSP_PLAY)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL; + else if (!strcmp(com->method, GF_RTSP_PAUSE)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL; + else sess->RTSP_State = GF_RTSP_STATE_WAITING; + //teardown invalidates the session most of the time, so we force the user to wait for the reply + //as the reply may indicate a connection-closed + strcpy(sess->RTSPLastRequest, com->method); + +exit: + if (result) free(result); + return e; +} + + +void gf_rtsp_set_command_value(GF_RTSPCommand *com, char *Header, char *Value) +{ + char LineBuffer[400]; + s32 LinePos; + GF_RTSPTransport *trans; + GF_X_Attribute *x_Att; + + if (!stricmp(Header, "Accept")) com->Accept = strdup(Value); + else if (!stricmp(Header, "Accept-Encoding")) com->Accept_Encoding = strdup(Value); + else if (!stricmp(Header, "Accept-Language")) com->Accept_Language = strdup(Value); + else if (!stricmp(Header, "Authorization")) com->Authorization = strdup(Value); + else if (!stricmp(Header, "Bandwidth")) sscanf(Value, "%d", &com->Bandwidth); + else if (!stricmp(Header, "Blocksize")) sscanf(Value, "%d", &com->Blocksize); + else if (!stricmp(Header, "Cache-Control")) com->Cache_Control = strdup(Value); + else if (!stricmp(Header, "Conference")) com->Conference = strdup(Value); + else if (!stricmp(Header, "Connection")) com->Connection = strdup(Value); + else if (!stricmp(Header, "Content-Length")) sscanf(Value, "%d", &com->Content_Length); + else if (!stricmp(Header, "CSeq")) sscanf(Value, "%d", &com->CSeq); + else if (!stricmp(Header, "From")) com->From = strdup(Value); + else if (!stricmp(Header, "Proxy_Authorization")) com->Proxy_Authorization = strdup(Value); + else if (!stricmp(Header, "Proxy_Require")) com->Proxy_Require = strdup(Value); + else if (!stricmp(Header, "Range")) com->Range = gf_rtsp_range_parse(Value); + else if (!stricmp(Header, "Referer")) com->Referer = strdup(Value); + else if (!stricmp(Header, "Scale")) sscanf(Value, "%lf", &com->Scale); + else if (!stricmp(Header, "Session")) com->Session = strdup(Value); + else if (!stricmp(Header, "Speed")) sscanf(Value, "%lf", &com->Speed); + else if (!stricmp(Header, "User_Agent")) com->User_Agent = strdup(Value); + //Transports + else if (!stricmp(Header, "Transport")) { + LinePos = 0; + while (1) { + LinePos = gf_token_get(Value, LinePos, "\r\n", LineBuffer, 400); + if (LinePos <= 0) return; + trans = gf_rtsp_transport_parse(Value); + if (trans) gf_list_add(com->Transports, trans); + } + } + //eXtensions attributes + else if (!strnicmp(Header, "x-", 2)) { + x_Att = (GF_X_Attribute*)malloc(sizeof(GF_X_Attribute)); + x_Att->Name = strdup(Header+2); + x_Att->Value = NULL; + if (Value && strlen(Value)) x_Att->Value = strdup(Value); + gf_list_add(com->Xtensions, x_Att); + } + //the rest is ignored +} + +GF_Err RTSP_ParseCommandHeader(GF_RTSPSession *sess, GF_RTSPCommand *com, u32 BodyStart) +{ + char LineBuffer[1024]; + char ValBuf[1024]; + char *buffer; + s32 Pos, ret; + u32 Size; + + Size = sess->CurrentSize - sess->CurrentPos; + buffer = sess->TCPBuffer + sess->CurrentPos; + + //by default the command is wrong ;) + com->StatusCode = NC_RTSP_Bad_Request; + + //parse first line + ret = gf_token_get_line(buffer, 0, Size, LineBuffer, 1024); + if (ret < 0) return GF_REMOTE_SERVICE_ERROR; + + //method + Pos = gf_token_get(LineBuffer, 0, " \t\r\n", ValBuf, 1024); + if (Pos <= 0) return GF_OK; + com->method = strdup((const char *) ValBuf); + + //URL + Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024); + if (Pos <= 0) return GF_OK; + com->service_name = strdup(ValBuf); + + //RTSP version + Pos = gf_token_get(LineBuffer, Pos, "\t\r\n", ValBuf, 1024); + if (Pos <= 0) return GF_OK; + if (strcmp(ValBuf, GF_RTSP_VERSION)) { + com->StatusCode = NC_RTSP_RTSP_Version_Not_Supported; + return GF_OK; + } + + com->StatusCode = NC_RTSP_OK; + + return gf_rtsp_parse_header(buffer + ret, Size - ret, BodyStart, com, NULL); +} + + +GF_EXPORT +GF_Err gf_rtsp_get_command(GF_RTSPSession *sess, GF_RTSPCommand *com) +{ + GF_Err e; + u32 BodyStart, size; + if (!sess || !com) return GF_BAD_PARAM; + + //reset the command + gf_rtsp_command_reset(com); + //if no connection, we have sent a "Connection: Close" + if (!sess->connection) return GF_IP_CONNECTION_CLOSED; + + //lock + gf_mx_p(sess->mx); + + //fill TCP buffer + e = gf_rtsp_fill_buffer(sess); + if (e) goto exit; + //this is upcoming, interleaved data + if (strncmp(sess->TCPBuffer+sess->CurrentPos, "RTSP", 4)) { + e = GF_IP_NETWORK_EMPTY; + goto exit; + } + e = gf_rtsp_read_reply(sess); + if (e) goto exit; + + gf_rtsp_get_body_info(sess, &BodyStart, &size); + e = RTSP_ParseCommandHeader(sess, com, BodyStart); + //before returning an error we MUST reset the TCP buffer + + //copy the body if any + if (!e && com->Content_Length) { + com->body = (char *) malloc(sizeof(char) * (com->Content_Length)); + memcpy(com->body, sess->TCPBuffer+sess->CurrentPos + BodyStart, com->Content_Length); + } + //reset TCP buffer + sess->CurrentPos += BodyStart + com->Content_Length; + + if (!com->CSeq) com->StatusCode = NC_RTSP_Bad_Request; + + if (e || (com->StatusCode != NC_RTSP_OK)) goto exit; + + //NB: there is no "session state" in our lib when acting at the server side, as it depends + //on the server implementation. We cannot block responses / announcement to be sent + //dynamically, nor reset the session ourselves as we don't know the details of the session + //(eg TEARDOWN may keep resources up or not, ...) + + //we also have the same pb for CSeq, as nothing forbids a server to buffer commands (and it + //happens during aggregation of PLAY/PAUSE with overlapping ranges) + + //however store the last CSeq in case for client checking + if (!sess->CSeq) { + sess->CSeq = com->CSeq; + } + //check we're in the right range + else { + if (sess->CSeq >= com->CSeq) + com->StatusCode = NC_RTSP_Header_Field_Not_Valid; + else + sess->CSeq = com->CSeq; + } + + // + //if a connection closed is signal, check this is the good session + // and reset it (the client is no longer connected) + if (sess->last_session_id && com->Session && !strcmp(com->Session, sess->last_session_id) + && com->Connection && !stricmp(com->Connection, "Close")) { + + gf_rtsp_session_reset(sess, 0); + //destroy the socket + if (sess->connection) gf_sk_del(sess->connection); + sess->connection = NULL; + + //destroy the http tunnel if any + if (sess->HasTunnel && sess->http) { + gf_sk_del(sess->http); + sess->http = NULL; + } + } + +exit: + gf_mx_v(sess->mx); + return e; +} + + + + + + + + + + + + + + diff --git a/src/ietf/rtsp_common.c b/src/ietf/rtsp_common.c new file mode 100644 index 0000000..f86166b --- /dev/null +++ b/src/ietf/rtsp_common.c @@ -0,0 +1,367 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + + +GF_Err gf_rtsp_read_reply(GF_RTSPSession *sess) +{ + GF_Err e; + u32 res, body_size; + u32 BodyStart = 0; + + //fetch more data on the socket if needed + while (1) { + //Locate header / body + if (!BodyStart) gf_rtsp_get_body_info(sess, &BodyStart, &body_size); + + //enough data + res = sess->CurrentSize - sess->CurrentPos; + if (!body_size || (res >= body_size + BodyStart)) { + //done + break; + } + //this is the tricky part: if we do NOT have a body start -> we refill + e = gf_rtsp_refill_buffer(sess); + if (e) return e; + } + return GF_OK; +} + +void gf_rtsp_get_body_info(GF_RTSPSession *sess, u32 *body_start, u32 *body_size) +{ + u32 i; + char *buffer; + char *cl_str, val[30]; + + *body_start = *body_size = 0; + + buffer = sess->TCPBuffer + sess->CurrentPos; + *body_start = gf_token_find(buffer, 0, sess->CurrentSize - sess->CurrentPos, "\r\n\r\n"); + + //if found add the 2 "\r\n" and parse it + if (*body_start) { + *body_start += 4; + + //get the content length + cl_str = strstr(buffer, "Content-Length: "); + if (!cl_str) cl_str = strstr(buffer, "Content-length: "); + + if (cl_str) { + cl_str += 16; + i = 0; + while (cl_str[i] != '\r') { + val[i] = cl_str[i]; + i += 1; + } + val[i] = 0; + *body_size = atoi(val); + } else { + *body_size = 0; + } + } +} + + +GF_Err gf_rtsp_refill_buffer(GF_RTSPSession *sess) +{ + GF_Err e; + u32 res; + char *ptr; + + if (!sess) return GF_BAD_PARAM; + if (!sess->connection) return GF_IP_NETWORK_EMPTY; + + res = sess->CurrentSize - sess->CurrentPos; + if (!res) return gf_rtsp_fill_buffer(sess); + +// printf("Forcing reading\n"); + + ptr = (char *)malloc(sizeof(char) * res); + memcpy(ptr, sess->TCPBuffer+sess->CurrentPos, res); + memcpy(sess->TCPBuffer, ptr, res); + free(ptr); + + sess->CurrentPos = 0; + sess->CurrentSize = res; + + //now read from current pos + e = gf_sk_receive(sess->connection, sess->TCPBuffer + sess->CurrentSize, + RTSP_TCP_BUF_SIZE - sess->CurrentSize, + 0, &res); + + if (!e) { + sess->CurrentSize += res; + } + return e; +} + + +GF_Err gf_rtsp_fill_buffer(GF_RTSPSession *sess) +{ + GF_Err e = GF_OK; + + if (!sess->connection) return GF_IP_NETWORK_EMPTY; + + if (sess->CurrentSize == sess->CurrentPos) { + e = gf_sk_receive(sess->connection, sess->TCPBuffer, RTSP_TCP_BUF_SIZE, 0, &sess->CurrentSize); + sess->CurrentPos = 0; + sess->TCPBuffer[sess->CurrentSize] = 0; + if (e) sess->CurrentSize = 0; + } else if (!sess->CurrentSize) e = GF_IP_NETWORK_EMPTY; + return e; +} + + +GF_RTSPTransport *gf_rtsp_transport_parse(char *buffer) +{ + Bool IsFirst; + char buf[100], param_name[100], param_val[100]; + s32 pos, nPos; + GF_RTSPTransport *tmp; + pos = 0; + if (!buffer) return NULL; + //only support for RTP/AVP for now + if (strnicmp(buffer, "RTP/AVP", 7) && strnicmp(buffer, "RTP/SAVP", 8)) return NULL; + + GF_SAFEALLOC(tmp, GF_RTSPTransport); + + IsFirst = 1; + pos = 0; + while (1) { + pos = gf_token_get(buffer, pos, " ;", buf, 100); + if (pos <= 0) break; + if (strstr(buf, "=")) { + nPos = gf_token_get(buf, 0, "=", param_name, 100); + nPos = gf_token_get(buf, nPos, "=", param_val, 100); + } else { + strcpy(param_name, buf); + } + + //very first param is the profile + if (IsFirst) { + tmp->Profile = strdup(param_name); + IsFirst = 0; + continue; + } + + if (!stricmp(param_name, "destination")) { + if (tmp->destination) free(tmp->destination); + tmp->destination = strdup(param_val); + } + else if (!stricmp(param_name, "source")) { + if (tmp->source) free(tmp->source); + tmp->source = strdup(param_val); + } + else if (!stricmp(param_name, "unicast")) tmp->IsUnicast = 1; + else if (!stricmp(param_name, "RECORD")) tmp->IsRecord = 1; + else if (!stricmp(param_name, "append")) tmp->Append = 1; + else if (!stricmp(param_name, "interleaved")) { + u32 rID, rcID; + tmp->IsInterleaved = 1; + if (sscanf(param_val, "%d-%d", &rID, &rcID) == 1) { + sscanf(param_val, "%d", &rID); + tmp->rtcpID = tmp->rtpID = (u8) rID; + } else { + tmp->rtpID = (u8) rID; + tmp->rtcpID = (u8) rcID; + } + } + else if (!stricmp(param_name, "layers")) sscanf(param_val, "%d", &tmp->MulticastLayers); + else if (!stricmp(param_name, "ttl")) sscanf(param_val, "%c ", &tmp->TTL); + else if (!stricmp(param_name, "port")) sscanf(param_val, "%hd-%hd", &tmp->port_first, &tmp->port_last); + else if (!stricmp(param_name, "server_port")) sscanf(param_val, "%hd-%hd", &tmp->port_first, &tmp->port_last); + else if (!stricmp(param_name, "client_port")) sscanf(param_val, "%hd-%hd", &tmp->client_port_first, &tmp->client_port_last); + else if (!stricmp(param_name, "ssrc")) sscanf(param_val, "%X", &tmp->SSRC); + } + return tmp; +} + + + +GF_Err gf_rtsp_parse_header(char *buffer, u32 BufferSize, u32 BodyStart, GF_RTSPCommand *com, GF_RTSPResponse *rsp) +{ + char LineBuffer[1024]; + char HeaderBuf[100], ValBuf[1024], temp[400]; + s32 Pos, LinePos; + u32 HeaderLine; + + //then parse the full header + LinePos = 0; + strcpy(HeaderBuf, ""); + while (1) { + HeaderLine = 0; + LinePos = gf_token_get_line(buffer, LinePos, BufferSize, LineBuffer, 1024); + if (LinePos <= 0) return GF_REMOTE_SERVICE_ERROR; + + //extract field header and value. Warning: some params (transport, ..) may be on several lines + Pos = gf_token_get(LineBuffer, 0, ":\r\n", temp, 400); + + //end of header + if (Pos <= 0) { + HeaderLine = 2; + } + //this is a header + else if (LineBuffer[0] != ' ') { + HeaderLine = 1; + } else { + Pos = gf_token_get(LineBuffer, 0, ", \r\n", temp, 400); + //end of header - process any pending one + if (Pos <= 0) { + HeaderLine = 2; + } else { + //n-line value - append + strcat(ValBuf, "\r\n"); + strcat(ValBuf, temp); + continue; + } + } + //process current value + if (HeaderLine && strlen(HeaderBuf)) { + if (rsp) { + gf_rtsp_set_response_value(rsp, HeaderBuf, ValBuf); + } + else { + gf_rtsp_set_command_value(com, HeaderBuf, ValBuf); + } + } + //done with the header + if ( (HeaderLine == 2) || ((u32) LinePos >= BodyStart) ) return GF_OK; + + //process current line + strcpy(HeaderBuf, temp); + + //skip ':' + Pos += 1; + //a server should normally reply with a space, but check it + if (LineBuffer[Pos] == ' ') Pos += 1; + /*!! empty value !! - DSS may send these for CSeq if something goes wrong*/ + if (!strcmp(LineBuffer+Pos, "\r\n")) { + HeaderBuf[0] = 0; + continue; + } + Pos = gf_token_get(LineBuffer, Pos, "\r\n", ValBuf, 400); + if (Pos <= 0) break; + + } + //if we get here we haven't reached the BodyStart + return GF_REMOTE_SERVICE_ERROR; +} + + +GF_EXPORT +const char *gf_rtsp_nc_to_string(u32 ErrCode) +{ + switch (ErrCode) { + case NC_RTSP_Continue: + return "Continue"; + case NC_RTSP_OK: + return "OK"; + case NC_RTSP_Created: + return "Created"; + case NC_RTSP_Low_on_Storage_Space: + return "Low on Storage Space"; + case NC_RTSP_Multiple_Choice: + return "Multiple Choice"; + case NC_RTSP_Moved_Permanently: + return "Moved Permanently"; + case NC_RTSP_Moved_Temporarily: + return "Moved Temporarily"; + case NC_RTSP_See_Other: + return "See Other"; + case NC_RTSP_Use_Proxy: + return "Use Proxy"; + case NC_RTSP_Bad_Request: + return "Bad Request"; + case NC_RTSP_Unauthorized: + return "Unauthorized"; + case NC_RTSP_Payment_Required: + return "Payment Required"; + case NC_RTSP_Forbidden: + return "Forbidden"; + case NC_RTSP_Not_Found: + return "Not Found"; + case NC_RTSP_Method_Not_Allowed: + return "Method Not Allowed"; + case NC_RTSP_Not_Acceptable: + return "Not Acceptable"; + case NC_RTSP_Proxy_Authentication_Required: + return "Proxy Authentication Required"; + case NC_RTSP_Request_Timeout: + return "Request Timeout"; + case NC_RTSP_Gone: + return "Gone"; + case NC_RTSP_Length_Required: + return "Length Required"; + case NC_RTSP_Precondition_Failed: + return "Precondition Failed"; + case NC_RTSP_Request_Entity_Too_Large: + return "Request Entity Too Large"; + case NC_RTSP_Request_URI_Too_Long: + return "Request URI Too Long"; + case NC_RTSP_Unsupported_Media_Type: + return "Unsupported Media Type"; + case NC_RTSP_Invalid_parameter: + return "Invalid parameter"; + case NC_RTSP_Illegal_Conference_Identifier: + return "Illegal Conference Identifier"; + case NC_RTSP_Not_Enough_Bandwidth: + return "Not Enough Bandwidth"; + case NC_RTSP_Session_Not_Found: + return "Session Not Found"; + case NC_RTSP_Method_Not_Valid_In_This_State: + return "Method Not Valid In This State"; + case NC_RTSP_Header_Field_Not_Valid: + return "Header Field Not Valid"; + case NC_RTSP_Invalid_Range: + return "Invalid Range"; + case NC_RTSP_Parameter_Is_ReadOnly: + return "Parameter Is Read-Only"; + case NC_RTSP_Aggregate_Operation_Not_Allowed: + return "Aggregate Operation Not Allowed"; + case NC_RTSP_Only_Aggregate_Operation_Allowed: + return "Only Aggregate Operation Allowed"; + case NC_RTSP_Unsupported_Transport: + return "Unsupported Transport"; + case NC_RTSP_Destination_Unreachable: + return "Destination Unreachable"; + case NC_RTSP_Internal_Server_Error: + return "Internal Server Error"; + case NC_RTSP_Bad_Gateway: + return "Bad Gateway"; + case NC_RTSP_Service_Unavailable: + return "Service Unavailable"; + case NC_RTSP_Gateway_Timeout: + return "Gateway Timeout"; + case NC_RTSP_RTSP_Version_Not_Supported: + return "RTSP Version Not Supported"; + case NC_RTSP_Option_not_support: + return "Option not support"; + + case NC_RTSP_Not_Implemented: + default: + return "Not Implemented"; + } +} diff --git a/src/ietf/rtsp_response.c b/src/ietf/rtsp_response.c new file mode 100644 index 0000000..5eba471 --- /dev/null +++ b/src/ietf/rtsp_response.c @@ -0,0 +1,708 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + + +GF_EXPORT +GF_RTSPResponse *gf_rtsp_response_new() +{ + GF_RTSPResponse *tmp; + GF_SAFEALLOC(tmp, GF_RTSPResponse); + tmp->Transports = gf_list_new(); + tmp->RTP_Infos = gf_list_new(); + tmp->Xtensions = gf_list_new(); + return tmp; +} + + +#define RSP_FREE_CLEAN(hdr) if (rsp->hdr) free(rsp->hdr); \ + rsp->hdr = NULL; + +GF_EXPORT +void gf_rtsp_response_reset(GF_RTSPResponse *rsp) +{ + GF_RTPInfo *inf; + GF_RTSPTransport *trans; + GF_X_Attribute *att; + if (!rsp) return; + + //free all headers + RSP_FREE_CLEAN(Accept); + RSP_FREE_CLEAN(Accept_Encoding); + RSP_FREE_CLEAN(Accept_Language); + RSP_FREE_CLEAN(Allow); + RSP_FREE_CLEAN(Authorization); + RSP_FREE_CLEAN(Cache_Control); + RSP_FREE_CLEAN(Conference); + RSP_FREE_CLEAN(Connection); + RSP_FREE_CLEAN(Content_Base); + RSP_FREE_CLEAN(Content_Encoding); + RSP_FREE_CLEAN(Content_Language); + RSP_FREE_CLEAN(Content_Location); + RSP_FREE_CLEAN(Content_Type); + RSP_FREE_CLEAN(Date); + RSP_FREE_CLEAN(Expires); + RSP_FREE_CLEAN(From); + RSP_FREE_CLEAN(Host); + RSP_FREE_CLEAN(If_Match); + RSP_FREE_CLEAN(If_Modified_Since); + RSP_FREE_CLEAN(Last_Modified); + RSP_FREE_CLEAN(Location); + RSP_FREE_CLEAN(Proxy_Authenticate); + RSP_FREE_CLEAN(Proxy_Require); + RSP_FREE_CLEAN(Public); + RSP_FREE_CLEAN(Referer); + RSP_FREE_CLEAN(Require); + RSP_FREE_CLEAN(Retry_After); + RSP_FREE_CLEAN(Server); + RSP_FREE_CLEAN(Session); + RSP_FREE_CLEAN(Timestamp); + RSP_FREE_CLEAN(Unsupported); + RSP_FREE_CLEAN(User_Agent); + RSP_FREE_CLEAN(Vary); + RSP_FREE_CLEAN(Via); + RSP_FREE_CLEAN(WWW_Authenticate); + + //this is for us + RSP_FREE_CLEAN(ResponseInfo); + RSP_FREE_CLEAN(body); + + rsp->Bandwidth = rsp->Blocksize = rsp->ResponseCode = rsp->Content_Length = rsp->CSeq = 0; + rsp->Scale = rsp->Speed = 0.0; + if (rsp->Range) free(rsp->Range); + rsp->Range = NULL; + + rsp->SessionTimeOut = 0; + + while (gf_list_count(rsp->Transports)) { + trans = (GF_RTSPTransport*) gf_list_get(rsp->Transports, 0); + gf_list_rem(rsp->Transports, 0); + gf_rtsp_transport_del(trans); + } + + while (gf_list_count(rsp->RTP_Infos)) { + inf = (GF_RTPInfo*) gf_list_get(rsp->RTP_Infos, 0); + gf_list_rem(rsp->RTP_Infos, 0); + if (inf->url) free(inf->url); + free(inf); + } + while (gf_list_count(rsp->Xtensions)) { + att = (GF_X_Attribute*)gf_list_get(rsp->Xtensions, 0); + gf_list_rem(rsp->Xtensions, 0); + free(att->Name); + free(att->Value); + free(att); + } +} + +GF_EXPORT +void gf_rtsp_response_del(GF_RTSPResponse *rsp) +{ + if (!rsp) return; + + gf_rtsp_response_reset(rsp); + gf_list_del(rsp->RTP_Infos); + gf_list_del(rsp->Xtensions); + gf_list_del(rsp->Transports); + free(rsp); +} + + + +GF_EXPORT +GF_RTSPRange *gf_rtsp_range_parse(char *range_buf) +{ + GF_RTSPRange *rg; + + if (!strstr(range_buf, "npt")) return NULL; + + GF_SAFEALLOC(rg, GF_RTSPRange); + if (sscanf(range_buf, "npt=%lf-%lf", &rg->start, &rg->end) != 2) { + rg->end = -1.0; + sscanf(range_buf, "npt=%lf-", &rg->start); + } + return rg; +} + +GF_EXPORT +void gf_rtsp_transport_del(GF_RTSPTransport *transp) +{ + if (!transp) return; + if (transp->destination) free(transp->destination); + if (transp->Profile) free(transp->Profile); + if (transp->source) free(transp->source); + free(transp); +} + +GF_EXPORT +GF_RTSPTransport *gf_rtsp_transport_clone(GF_RTSPTransport *original) +{ + GF_RTSPTransport *tr; + + if (!original) return NULL; + + tr = (GF_RTSPTransport*) malloc(sizeof(GF_RTSPTransport)); + memcpy(tr, original, sizeof(GF_RTSPTransport)); + tr->destination = tr->source = tr->Profile = NULL; + if (original->destination) tr->destination = strdup(original->destination); + if (original->source) tr->source = strdup(original->source); + if (original->Profile) tr->Profile = strdup(original->Profile); + return tr; +} + +GF_EXPORT +GF_RTSPRange *gf_rtsp_range_new() +{ + GF_RTSPRange *tmp; + GF_SAFEALLOC(tmp, GF_RTSPRange); + return tmp; +} + +GF_EXPORT +void gf_rtsp_range_del(GF_RTSPRange *range) +{ + if (!range) return; + free(range); +} + +void gf_rtsp_set_response_value(GF_RTSPResponse *rsp, char *Header, char *Value) +{ + char LineBuffer[400], buf[100], param_name[100], param_val[100]; + s32 LinePos, Pos, nPos, s_val; + GF_RTPInfo *info; + GF_RTSPTransport *trans; + GF_X_Attribute *x_Att; + + if (!stricmp(Header, "Accept")) rsp->Accept = strdup(Value); + else if (!stricmp(Header, "Accept-Encoding")) rsp->Accept_Encoding = strdup(Value); + else if (!stricmp(Header, "Accept-Language")) rsp->Accept_Language = strdup(Value); + else if (!stricmp(Header, "Allow")) rsp->Allow = strdup(Value); + else if (!stricmp(Header, "Authorization")) rsp->Authorization = strdup(Value); + else if (!stricmp(Header, "Bandwidth")) sscanf(Value, "%d", &rsp->Bandwidth); + else if (!stricmp(Header, "Blocksize")) sscanf(Value, "%d", &rsp->Blocksize); + else if (!stricmp(Header, "Cache-Control")) rsp->Cache_Control = strdup(Value); + else if (!stricmp(Header, "Conference")) rsp->Conference = strdup(Value); + else if (!stricmp(Header, "Connection")) rsp->Connection = strdup(Value); + else if (!stricmp(Header, "Content-Base")) rsp->Content_Base = strdup(Value); + else if (!stricmp(Header, "Content-Encoding")) rsp->Content_Encoding = strdup(Value); + else if (!stricmp(Header, "Content-Length")) sscanf(Value, "%d", &rsp->Content_Length); + else if (!stricmp(Header, "Content-Language")) rsp->Content_Language = strdup(Value); + else if (!stricmp(Header, "Content-Location")) rsp->Content_Location = strdup(Value); + else if (!stricmp(Header, "Content-Type")) rsp->Content_Type = strdup(Value); + else if (!stricmp(Header, "CSeq")) sscanf(Value, "%d", &rsp->CSeq); + else if (!stricmp(Header, "Date")) rsp->Date = strdup(Value); + else if (!stricmp(Header, "Expires")) rsp->Expires = strdup(Value); + else if (!stricmp(Header, "From")) rsp->From = strdup(Value); + else if (!stricmp(Header, "Host")) rsp->Host = strdup(Value); + else if (!stricmp(Header, "If-Match")) rsp->If_Match = strdup(Value); + else if (!stricmp(Header, "If-Modified-Since")) rsp->If_Modified_Since = strdup(Value); + else if (!stricmp(Header, "Last-Modified")) rsp->Last_Modified = strdup(Value); + else if (!stricmp(Header, "Location")) rsp->Location = strdup(Value); + else if (!stricmp(Header, "Proxy-Authenticate")) rsp->Proxy_Authenticate = strdup(Value); + else if (!stricmp(Header, "Proxy-Require")) rsp->Proxy_Require = strdup(Value); + else if (!stricmp(Header, "Public")) rsp->Public = strdup(Value); + else if (!stricmp(Header, "Referer")) rsp->Referer = strdup(Value); + else if (!stricmp(Header, "Require")) rsp->Require = strdup(Value); + else if (!stricmp(Header, "Retry-After")) rsp->Retry_After = strdup(Value); + else if (!stricmp(Header, "Scale")) sscanf(Value, "%lf", &rsp->Scale); + else if (!stricmp(Header, "Server")) rsp->Server = strdup(Value); + else if (!stricmp(Header, "Speed")) sscanf(Value, "%lf", &rsp->Speed); + else if (!stricmp(Header, "Timestamp")) rsp->Timestamp = strdup(Value); + else if (!stricmp(Header, "Unsupported")) rsp->Unsupported = strdup(Value); + else if (!stricmp(Header, "User-Agent")) rsp->User_Agent = strdup(Value); + else if (!stricmp(Header, "Vary")) rsp->Vary = strdup(Value); + else if (!stricmp(Header, "Via")) rsp->Vary = strdup(Value); + else if (!stricmp(Header, "WWW_Authenticate")) rsp->Vary = strdup(Value); + else if (!stricmp(Header, "Transport")) { + LinePos = 0; + while (1) { + LinePos = gf_token_get(Value, LinePos, "\r\n", LineBuffer, 400); + if (LinePos <= 0) return; + trans = gf_rtsp_transport_parse(Value); + if (trans) gf_list_add(rsp->Transports, trans); + } + } + //Session + else if (!stricmp(Header, "Session")) { + LinePos = gf_token_get(Value, 0, ";\r\n", LineBuffer, 400); + rsp->Session = strdup(LineBuffer); + //get timeout if any + if (Value[LinePos] == ';') { + LinePos += 1; + LinePos = gf_token_get(Value, LinePos, ";\r\n", LineBuffer, 400); + //default + rsp->SessionTimeOut = 60; + sscanf(LineBuffer, "timeout=%d", &rsp->SessionTimeOut); + } + } + + //Range + else if (!stricmp(Header, "Range")) rsp->Range = gf_rtsp_range_parse(Value); + //RTP-Info + else if (!stricmp(Header, "RTP-Info")) { + LinePos = 0; + while (1) { + LinePos = gf_token_get(Value, LinePos, ",\r\n", LineBuffer, 400); + if (LinePos <= 0) return; + + GF_SAFEALLOC(info, GF_RTPInfo); + Pos = 0; + while (1) { + Pos = gf_token_get(LineBuffer, Pos, " ;", buf, 100); + if (Pos <= 0) break; + if (strstr(buf, "=")) { + nPos = gf_token_get(buf, 0, "=", param_name, 100); + nPos += 1; + nPos = gf_token_get(buf, nPos, "", param_val, 100); + } else { + strcpy(param_name, buf); + } + if (!stricmp(param_name, "url")) info->url = strdup(param_val); + else if (!stricmp(param_name, "seq")) sscanf(param_val, "%d", &info->seq); + else if (!stricmp(param_name, "rtptime")) { + sscanf(param_val, "%i", &s_val); + info->rtp_time = (s_val>0) ? s_val : 0; + } + else if (!stricmp(param_name, "ssrc")) { + sscanf(param_val, "%i", &s_val); + info->ssrc = (s_val>0) ? s_val : 0; + } + } + gf_list_add(rsp->RTP_Infos, info); + } + } + //check for extended attributes + else if (!strnicmp(Header, "x-", 2)) { + x_Att = (GF_X_Attribute*)malloc(sizeof(GF_X_Attribute)); + x_Att->Name = strdup(Header+2); + x_Att->Value = NULL; + if (Value && strlen(Value)) x_Att->Value = strdup(Value); + gf_list_add(rsp->Xtensions, x_Att); + } + //unknown field - skip it +} + + + +//parse all fields in the header +GF_Err RTSP_ParseResponseHeader(GF_RTSPSession *sess, GF_RTSPResponse *rsp, u32 BodyStart) +{ + char LineBuffer[1024]; + char ValBuf[400]; + char *buffer; + s32 Pos, ret; + u32 Size; + + Size = sess->CurrentSize - sess->CurrentPos; + buffer = sess->TCPBuffer + sess->CurrentPos; + + //parse first line + ret = gf_token_get_line(buffer, 0, Size, LineBuffer, 1024); + if (ret < 0) return GF_REMOTE_SERVICE_ERROR; + //RTSP/1.0 + Pos = gf_token_get(LineBuffer, 0, " \t\r\n", ValBuf, 400); + if (Pos <= 0) return GF_REMOTE_SERVICE_ERROR; + if (strcmp(ValBuf, GF_RTSP_VERSION)) return GF_SERVICE_ERROR; + //CODE + Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 400); + if (Pos <= 0) return GF_REMOTE_SERVICE_ERROR; + rsp->ResponseCode = atoi(ValBuf); + //string info + Pos = gf_token_get(LineBuffer, Pos, "\t\r\n", ValBuf, 400); + if (Pos > 0) rsp->ResponseInfo = strdup(ValBuf); + + return gf_rtsp_parse_header(buffer + ret, Size - ret, BodyStart, NULL, rsp); +} + + + +u32 IsRTSPMessage(char *buffer) +{ + if (!buffer) return 0; + if (buffer[0]=='$') return 0; + + if (!strncmp(buffer, "RTSP", 4)) return 1; + if (!strncmp(buffer, "GET_PARAMETER", strlen("GET_PARAMETER"))) return 1; + if (!strncmp(buffer, "ANNOUNCE", strlen("ANNOUNCE"))) return 1; + if (!strncmp(buffer, "SET_PARAMETER", strlen("SET_PARAMETER"))) return 1; + if (!strncmp(buffer, "REDIRECT", strlen("REDIRECT"))) return 1; + if (!strncmp(buffer, "OPTIONS", strlen("OPTIONS"))) return 1; + return 0; +} + + +GF_EXPORT +GF_Err gf_rtsp_get_response(GF_RTSPSession *sess, GF_RTSPResponse *rsp) +{ + GF_Err e; + Bool force_reset = 0; + u32 BodyStart, size; + + if (!sess || !rsp) return GF_BAD_PARAM; + gf_rtsp_response_reset(rsp); + + + //LOCK + gf_mx_p(sess->mx); + + + e = gf_rtsp_check_connection(sess); + if (e) goto exit; + + //push data in our queue + e = gf_rtsp_fill_buffer(sess); + if (e) goto exit; + + //this is interleaved data + if (!IsRTSPMessage(sess->TCPBuffer+sess->CurrentPos) ) { + gf_rtsp_session_read(sess); + e = GF_IP_NETWORK_EMPTY; + goto exit; + } + e = gf_rtsp_read_reply(sess); + if (e) goto exit; + + //get the reply + gf_rtsp_get_body_info(sess, &BodyStart, &size); + e = RTSP_ParseResponseHeader(sess, rsp, BodyStart); + + //copy the body if any + if (!e && rsp->Content_Length) { + rsp->body = (char *)malloc(sizeof(char) * (rsp->Content_Length)); + memcpy(rsp->body, sess->TCPBuffer+sess->CurrentPos + BodyStart, rsp->Content_Length); + } + + GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Got Response:\n%s\n", sess->TCPBuffer+sess->CurrentPos)); + + //reset TCP buffer + sess->CurrentPos += BodyStart + rsp->Content_Length; + + if (e) goto exit; + + //update RTSP aggreagation info + if (sess->NbPending) sess->NbPending -= 1; + + if (sess->RTSP_State == GF_RTSP_STATE_WAITING) sess->RTSP_State = GF_RTSP_STATE_INIT; + //control, and everything is received + else if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL) { + if (!sess->NbPending) sess->RTSP_State = GF_RTSP_STATE_INIT; + } + //this is a late reply to an aggregated control - signal nothing + if (!strcmp(sess->RTSPLastRequest, "RESET") && sess->CSeq > rsp->CSeq) { + e = GF_IP_NETWORK_EMPTY; + goto exit; + } + + //reset last request + if (sess->RTSP_State == GF_RTSP_STATE_INIT) strcpy(sess->RTSPLastRequest, ""); + + //check the CSeq is in the right range. The server should ALWAYS reply in sequence + //to an aggreagated sequence of requests + //if we have reseted the connection (due to an APP error) return empty + if (rsp->CSeq && sess->CSeq > rsp->CSeq + sess->NbPending) { + gf_mx_v(sess->mx); + return gf_rtsp_get_response(sess, rsp); + } + + if (sess->CSeq != rsp->CSeq + sess->NbPending) { + e = GF_REMOTE_SERVICE_ERROR; + goto exit; + } + + /*check session ID*/ + if (rsp->Session && sess->last_session_id && strcmp(sess->last_session_id, rsp->Session)) { + e = GF_REMOTE_SERVICE_ERROR; + goto exit; + } + + //destroy sessionID if needed - real doesn't close the connection when destroying + //session + if (!strcmp(sess->RTSPLastRequest, GF_RTSP_TEARDOWN)) { + sess->last_session_id = NULL; + } + +exit: + if (rsp->Connection && !stricmp(rsp->Connection, "Close")) + force_reset = 1; + else if (e && (e != GF_IP_NETWORK_EMPTY)) + force_reset = 1; + + if (force_reset) { + gf_rtsp_session_reset(sess, 0); + //destroy the socket + if (sess->connection) gf_sk_del(sess->connection); + sess->connection = NULL; + + //destroy the http tunnel if any + if (sess->HasTunnel && sess->http) { + gf_sk_del(sess->http); + sess->http = NULL; + } + } + + gf_mx_v(sess->mx); + return e; +} + + + + +GF_Err RTSP_WriteResponse(GF_RTSPSession *sess, GF_RTSPResponse *rsp, + unsigned char **out_buffer, u32 *out_size) +{ + u32 i, cur_pos, size, count; + char *buffer, temp[50]; + GF_RTSPTransport *trans; + GF_X_Attribute *att; + GF_RTPInfo *info; + + + *out_buffer = NULL; + + size = RTSP_WRITE_STEPALLOC; + buffer = (char *) malloc(size); + cur_pos = 0; + + //RTSP line + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, GF_RTSP_VERSION); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, " "); + RTSP_WRITE_INT(buffer, size, cur_pos, rsp->ResponseCode, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, " "); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, gf_rtsp_nc_to_string(rsp->ResponseCode)); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + + //all headers + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept", rsp->Accept); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Encoding", rsp->Accept_Encoding); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Language", rsp->Accept_Language); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Allow", rsp->Allow); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Authorization", rsp->Authorization); + if (rsp->Bandwidth) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Bandwidth: "); + RTSP_WRITE_INT(buffer, size, cur_pos, rsp->Bandwidth, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + if (rsp->Blocksize) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Blocksize: "); + RTSP_WRITE_INT(buffer, size, cur_pos, rsp->Blocksize, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Cache-Control", rsp->Cache_Control); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Conference", rsp->Conference); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Connection", rsp->Connection); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Content-Base", rsp->Content_Base); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Content-Encoding", rsp->Content_Encoding); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Content-Language", rsp->Content_Language); + //if we have a body write the content length + if (rsp->body) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Content-Length: "); + RTSP_WRITE_INT(buffer, size, cur_pos, strlen(rsp->body), 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Content-Location", rsp->Content_Location); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Content-Type", rsp->Content_Type); + //write the CSeq - use the RESPONSE CSeq + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "CSeq: "); + RTSP_WRITE_INT(buffer, size, cur_pos, rsp->CSeq, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Date", rsp->Date); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Expires", rsp->Expires); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "From", rsp->From); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Host", rsp->Host); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "If-Match", rsp->If_Match); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "If-Modified-Since", rsp->If_Modified_Since); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Last-Modified", rsp->Last_Modified); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Location", rsp->Location); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Authenticate", rsp->Proxy_Authenticate); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Require", rsp->Proxy_Require); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Public", rsp->Public); + + //Range, only NPT + if (rsp->Range && !rsp->Range->UseSMPTE) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Range: npt:"); + RTSP_WRITE_FLOAT(buffer, size, cur_pos, rsp->Range->start); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-"); + if (rsp->Range->end > rsp->Range->start) { + RTSP_WRITE_FLOAT(buffer, size, cur_pos, rsp->Range->end); + } + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Referer", rsp->Referer); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Require", rsp->Require); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Retry-After", rsp->Retry_After); + + //RTP Infos + count = gf_list_count(rsp->RTP_Infos); + if (count) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "RTPInfo: "); + + for (i=0; iRTP_Infos, i); + + if (info->url) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "url="); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, info->url); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";"); + } + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "seq="); + RTSP_WRITE_INT(buffer, size, cur_pos, info->seq, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";rtptime="); + RTSP_WRITE_INT(buffer, size, cur_pos, info->rtp_time, 0); + } + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + + if (rsp->Scale != 0.0) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Scale: "); + RTSP_WRITE_FLOAT(buffer, size, cur_pos, rsp->Scale); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Server", rsp->Server); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Session", rsp->Session); + if (rsp->Speed != 0.0) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Scale: "); + RTSP_WRITE_FLOAT(buffer, size, cur_pos, rsp->Speed); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Timestamp", rsp->Timestamp); + + //transport info + count = gf_list_count(rsp->Transports); + if (count) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Transport: "); + for (i=0; iTransports, i); + + //then write the structure + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->Profile); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";unicast" : ";multicast")); + if (trans->destination) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";destination="); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->destination); + } + if (trans->source) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";source="); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->source); + } + if (trans->IsRecord) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";mode=RECORD"); + if (trans->Append) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";append"); + } + if (trans->IsInterleaved) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";interleaved="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtpID, 0); + if (trans->rtcpID != trans->rtpID) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-"); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtcpID, 0); + } + } + //multicast specific + if (!trans->IsUnicast) { + if (trans->MulticastLayers) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";layers="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->MulticastLayers, 0); + } + if (trans->TTL) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ttl="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->TTL, 0); + } + } + if (trans->port_first) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->IsUnicast ? ";server_port=" : ";port="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_first, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-"); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_last, 0); + } + if (trans->IsUnicast && trans->client_port_first) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";client_port="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_first, 0); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-"); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_last, 0); + } + if (trans->SSRC) { + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ssrc="); + RTSP_WRITE_INT(buffer, size, cur_pos, trans->SSRC, 0); + } + } + //done with transport + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + } + + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Unsupported", rsp->Unsupported); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "User-Agent", rsp->User_Agent); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Vary", rsp->Vary); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "Via", rsp->Via); + RTSP_WRITE_HEADER(buffer, size, cur_pos, "WWW-Authenticate", rsp->WWW_Authenticate); + + //eXtensions + count = gf_list_count(rsp->Xtensions); + for (i=0; iXtensions, i); + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "x-"); + RTSP_WRITE_HEADER(buffer, size, cur_pos, att->Name, att->Value); + } + //end of header + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n"); + //then body + RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, rsp->body); + + *out_buffer = (unsigned char *) buffer; + *out_size = strlen(buffer); + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_rtsp_send_response(GF_RTSPSession *sess, GF_RTSPResponse *rsp) +{ + u32 size; + char *buffer; + GF_Err e; + + if (!sess || !rsp || !rsp->CSeq) return GF_BAD_PARAM; + + //check we're not sending something greater than the current CSeq + if (rsp->CSeq > sess->CSeq) return GF_BAD_PARAM; + + e = RTSP_WriteResponse(sess, rsp, (unsigned char **) &buffer, &size); + if (e) goto exit; + + //send buffer + e = gf_rtsp_send_data(sess, buffer, size); + if (e) return e; +// printf("RTSP Send Response\n\n%s\n\n", buffer); + +exit: + if (buffer) free(buffer); + return e; +} + diff --git a/src/ietf/rtsp_session.c b/src/ietf/rtsp_session.c new file mode 100644 index 0000000..322963e --- /dev/null +++ b/src/ietf/rtsp_session.c @@ -0,0 +1,780 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#ifdef _WIN32_WCE +#define RTSP_TRACE 0 +#else +#define RTSP_TRACE 1 +#endif + + +GF_Err RTSP_UnpackURL(char *sURL, char *Server, u16 *Port, char *Service, Bool *useTCP) +{ + char schema[10], *test, text[1024], *retest; + u32 i, len; + Bool is_ipv6; + if (!sURL) return GF_BAD_PARAM; + + strcpy(Server, ""); + strcpy(Service, ""); + *Port = *useTCP =0; + + if (!strchr(sURL, ':')) return GF_BAD_PARAM; + + //extract the schema + i = 0; + while (i<=strlen(sURL)) { + if (sURL[i] == ':') goto found; + schema[i] = sURL[i]; + i += 1; + } + return GF_BAD_PARAM; + +found: + schema[i] = 0; + if (stricmp(schema, "rtsp") && stricmp(schema, "rtspu")) return GF_URL_ERROR; + //check for user/pass - not allowed +/* + test = strstr(sURL, "@"); + if (test) return GF_NOT_SUPPORTED; +*/ + test = strstr(sURL, "://"); + if (!test) return GF_URL_ERROR; + test += 3; + //check for service + retest = strstr(test, "/"); + if (!retest) return GF_URL_ERROR; + + if (!stricmp(schema, "rtsp")) *useTCP = 1; + + //check for port + retest = strrchr(test, ':'); + /*IPV6 address*/ + if (retest && strchr(retest, ']')) retest = NULL; + + if (retest && strstr(retest, "/")) { + retest += 1; + i=0; + while (iConnectionType = UseTCP ? GF_SOCK_TYPE_TCP : GF_SOCK_TYPE_UDP; + if (Port) sess->Port = Port; + else if (DefaultPort) sess->Port = DefaultPort; + else sess->Port = 554; + + //HTTP tunnel + if (sess->Port == 80) { + sess->ConnectionType = GF_SOCK_TYPE_TCP; + sess->HasTunnel = 1; + } + + sess->Server = strdup(server); + sess->Service = strdup(service); + sess->mx = gf_mx_new("RTSPSession"); + sess->TCPChannels = gf_list_new(); + gf_rtsp_session_reset(sess, 0); + return sess; +} + + +GF_EXPORT +void gf_rtsp_reset_aggregation(GF_RTSPSession *sess) +{ + if (!sess) return; + + gf_mx_p(sess->mx); + if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL) { + strcpy(sess->RTSPLastRequest, "RESET"); + //skip all we haven't received + sess->CSeq += sess->NbPending; + sess->NbPending = 0; + } + sess->RTSP_State = GF_RTSP_STATE_INIT; + gf_mx_v(sess->mx); +} + +void RTSP_AcknowledgeError(GF_RTSPSession *sess) +{ +} + +void RemoveTCPChannels(GF_RTSPSession *sess) +{ + GF_TCPChan *ch; + while (gf_list_count(sess->TCPChannels)) { + ch = (GF_TCPChan*)gf_list_get(sess->TCPChannels, 0); + free(ch); + gf_list_rem(sess->TCPChannels, 0); + } +} + + +GF_EXPORT +void gf_rtsp_session_reset(GF_RTSPSession *sess, Bool ResetConnection) +{ + gf_mx_p(sess->mx); + + sess->last_session_id = NULL; + sess->NeedConnection = 1; + + if (ResetConnection) { + if (sess->connection) gf_sk_del(sess->connection); + sess->connection = NULL; + if (sess->http) { + gf_sk_del(sess->http); + sess->http = NULL; + } + } + + sess->RTSP_State = GF_RTSP_STATE_INIT; +// sess->CSeq = sess->NbPending = 0; + sess->InterID = (u8) -1; + sess->pck_start = sess->payloadSize = 0; + sess->CurrentPos = sess->CurrentSize = 0; + strcpy(sess->RTSPLastRequest, ""); + RemoveTCPChannels(sess); + gf_mx_v(sess->mx); +} + +GF_EXPORT +void gf_rtsp_session_del(GF_RTSPSession *sess) +{ + if (!sess) return; + + gf_rtsp_session_reset(sess, 0); + + if (sess->connection) gf_sk_del(sess->connection); + if (sess->http) gf_sk_del(sess->http); + if (sess->Server) free(sess->Server); + if (sess->Service) free(sess->Service); + gf_list_del(sess->TCPChannels); + if (sess->rtsp_pck_buf) free(sess->rtsp_pck_buf); + if (sess->MobileIP) free(sess->MobileIP); + gf_mx_del(sess->mx); + free(sess); +} + +GF_EXPORT +u32 gf_rtsp_get_session_state(GF_RTSPSession *sess) +{ + u32 state; + if (!sess) return GF_RTSP_STATE_INVALIDATED; + + gf_mx_p(sess->mx); + state = sess->RTSP_State; + gf_mx_v(sess->mx); + return state; +} + +GF_EXPORT +void gf_rtsp_set_mobile_ip(GF_RTSPSession *sess, char *MobileIP) +{ + if (sess->MobileIP) free(sess->MobileIP); + sess->MobileIP = NULL; + if (MobileIP) sess->MobileIP = strdup(MobileIP); +} + + +GF_EXPORT +char *gf_rtsp_get_last_request(GF_RTSPSession *sess) +{ + char *ret; + if (!sess) return NULL; + gf_mx_p(sess->mx); + ret = sess->RTSPLastRequest; + gf_mx_v(sess->mx); + return ret; +} + + +//check whether the url contains server and service name +//no thread protection as this is const throughout the session +GF_EXPORT +u32 gf_rtsp_is_my_session(GF_RTSPSession *sess, char *url) +{ + if (!sess) return 0; + if (!strstr(url, sess->Server)) return 0; + //same url or sub-url + if (strstr(url, sess->Service)) return 1; + return 0; +} + +GF_EXPORT +const char *gf_rtsp_get_last_session_id(GF_RTSPSession *sess) +{ + if (!sess) return NULL; + return sess->last_session_id; +} + +GF_EXPORT +char *gf_rtsp_get_server_name(GF_RTSPSession *sess) +{ + if (!sess) return NULL; + return sess->Server; +} + +GF_EXPORT +char *gf_rtsp_get_service_name(GF_RTSPSession *sess) +{ + if (!sess) return NULL; + return sess->Service; +} + +GF_EXPORT +u16 gf_rtsp_get_session_port(GF_RTSPSession *sess) +{ + return (sess ? sess->Port : 0); +} + +GF_Err gf_rtsp_check_connection(GF_RTSPSession *sess) +{ + GF_Err e; + //active, return + if (!sess->NeedConnection) return GF_OK; + + //socket is destroyed, recreate + if (!sess->connection) { + sess->connection = gf_sk_new(sess->ConnectionType); + if (!sess->connection) return GF_OUT_OF_MEM; + } + //the session is down, reconnect + e = gf_sk_connect(sess->connection, sess->Server, sess->Port, sess->MobileIP); + if (e) return e; + + if (sess->SockBufferSize) gf_sk_set_buffer_size(sess->connection, 0, sess->SockBufferSize); + + if (!sess->http && sess->HasTunnel) { + e = gf_rtsp_http_tunnel_start(sess, "toto is the king of RTSP"); + if (e) return e; + } + sess->NeedConnection = 0; + return GF_OK; +} + + +GF_Err gf_rtsp_send_data(GF_RTSPSession *sess, char *buffer, u32 Size) +{ + GF_Err e; + u32 Size64; + + e = gf_rtsp_check_connection(sess); + if (e) return e; + + //RTSP requests on HTTP are base 64 encoded + if (sess->HasTunnel) { + char buf64[3000]; + Size64 = gf_base64_encode(buffer, Size, buf64, 3000); + buf64[Size64] = 0; + //send on http connection + return gf_sk_send_wait(sess->http, buf64, Size64, 30); + } else { + return gf_sk_send(sess->connection, buffer, Size); + } +} + + + +static GF_TCPChan *GetTCPChannel(GF_RTSPSession *sess, u8 rtpID, u8 rtcpID, Bool RemoveIt) +{ + GF_TCPChan *ptr; + u32 i, count = gf_list_count(sess->TCPChannels); + for (i=0; iTCPChannels, i); + if (ptr->rtpID == rtpID) goto exit;; + if (ptr->rtcpID == rtcpID) goto exit; + } + return NULL; +exit: + if (RemoveIt) gf_list_rem(sess->TCPChannels, i); + return ptr; +} + + +GF_Err gf_rtsp_set_deinterleave(GF_RTSPSession *sess) +{ + GF_TCPChan *ch; + Bool IsRTCP; + u8 InterID; + u16 paySize; + u32 res, Size; + char *buffer; + + if (!sess) return GF_SERVICE_ERROR; + + Size = sess->CurrentSize - sess->CurrentPos; + buffer = sess->TCPBuffer + sess->CurrentPos; + + if (!Size) return GF_IP_NETWORK_EMPTY; + + //we do not work with just a header -> force a refill + if (Size <= 4) return gf_rtsp_refill_buffer(sess); + + //break if we get RTSP response on the wire + if (!strncmp(buffer, "RTSP", 4)) + return GF_IP_NETWORK_EMPTY; + + //new packet + if (!sess->pck_start && (buffer[0] == '$')) { + InterID = buffer[1]; + paySize = ((buffer[2] << 8) & 0xFF00) | (buffer[3] & 0xFF); + /*this may be NULL (data fetched after a teardown) - resync and return*/ + ch = GetTCPChannel(sess, InterID, InterID, 0); + + /*then check wether this is a full packet or a split*/ + if (paySize <= Size-4) { + if (ch) { + IsRTCP = (ch->rtcpID == InterID); + sess->RTSP_SignalData(sess, ch->ch_ptr, buffer+4, paySize, IsRTCP); + } + sess->CurrentPos += paySize+4; + assert(sess->CurrentPos <= sess->CurrentSize); + } else { + /*missed end of pck ?*/ + if (sess->payloadSize) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP over RTSP] Missed end of packet (%d bytes) in stream %d\n", sess->payloadSize - sess->pck_start, sess->InterID)); + ch = GetTCPChannel(sess, sess->InterID, sess->InterID, 0); + if (ch) { + IsRTCP = (ch->rtcpID == sess->InterID); + sess->RTSP_SignalData(sess, ch->ch_ptr, sess->rtsp_pck_buf, sess->payloadSize, IsRTCP); + } + } + sess->InterID = InterID; + sess->payloadSize = paySize; + sess->pck_start = Size-4; + if (sess->rtsp_pck_size < paySize) { + sess->rtsp_pck_buf = (char *)realloc(sess->rtsp_pck_buf, sizeof(char)*paySize); + sess->rtsp_pck_size = paySize; + } + memcpy(sess->rtsp_pck_buf, buffer+4, Size-4); + sess->CurrentPos += Size; + assert(sess->CurrentPos <= sess->CurrentSize); + } + } + /*end of packet*/ + else if (sess->payloadSize - sess->pck_start <= Size) { +// GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP over RTSP] Missed begining of packet (%d bytes) in stream %d\n", Size, sess->InterID)); + + res = sess->payloadSize - sess->pck_start; + memcpy(sess->rtsp_pck_buf + sess->pck_start, buffer, res); + //flush - same as above, don't complain if channel not found + ch = GetTCPChannel(sess, sess->InterID, sess->InterID, 0); + if (ch) { + IsRTCP = (ch->rtcpID == sess->InterID); + sess->RTSP_SignalData(sess, ch->ch_ptr, sess->rtsp_pck_buf, sess->payloadSize, IsRTCP); + } + sess->payloadSize = 0; + sess->pck_start = 0; + sess->InterID = (u8) -1; + sess->CurrentPos += res; + assert(sess->CurrentPos <= sess->CurrentSize); + } + /*middle of packet*/ + else { +// GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP over RTSP] Missed begining of RTP packet in stream %d\n", sess->InterID)); + memcpy(sess->rtsp_pck_buf + sess->pck_start, buffer, Size); + sess->pck_start += Size; + sess->CurrentPos += Size; + assert(sess->CurrentPos <= sess->CurrentSize); + } + return GF_OK; +} + + + + + + + + +/* + Exposed API, thread-safe +*/ + +GF_Err RTSP_ResetInterleaving(GF_RTSPSession *sess, Bool ResetChannels) +{ + if (!sess) return GF_BAD_PARAM; + + gf_mx_p(sess->mx); + sess->payloadSize = 0; + sess->pck_start = 0; + sess->InterID = (u8) -1; + if (ResetChannels) RemoveTCPChannels(sess); + gf_mx_v(sess->mx); + + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_rtsp_session_read(GF_RTSPSession *sess) +{ + GF_Err e; + if (!sess) return GF_BAD_PARAM; + + gf_mx_p(sess->mx); + e = gf_rtsp_fill_buffer(sess); + if (!e) { + //only read if not RTSP + while (1) { + e = gf_rtsp_set_deinterleave(sess); + if (e) break; + } + } + gf_mx_v(sess->mx); + return e; +} + + +GF_EXPORT +u32 gf_rtsp_unregister_interleave(GF_RTSPSession *sess, u8 LowInterID) +{ + GF_TCPChan *ptr; + + gf_mx_p(sess->mx); + ptr = GetTCPChannel(sess, LowInterID, LowInterID, 1); + if (ptr) free(ptr); + gf_mx_v(sess->mx); + return gf_list_count(sess->TCPChannels); +} + +GF_EXPORT +GF_Err gf_rtsp_register_interleave(GF_RTSPSession *sess, void *the_ch, u8 LowInterID, u8 HighInterID) +{ + GF_TCPChan *ptr; + + if (!sess) return GF_BAD_PARAM; + + gf_mx_p(sess->mx); + //do NOT register twice + ptr = GetTCPChannel(sess, LowInterID, HighInterID, 0); + if (!ptr) { + ptr = (GF_TCPChan *)malloc(sizeof(GF_TCPChan)); + ptr->ch_ptr = the_ch; + ptr->rtpID = LowInterID; + ptr->rtcpID = HighInterID; + gf_list_add(sess->TCPChannels, ptr); + } + gf_mx_v(sess->mx); + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_rtsp_set_interleave_callback(GF_RTSPSession *sess, + GF_Err (*SignalData)(GF_RTSPSession *sess, void *chan, char *buffer, u32 bufferSize, Bool IsRTCP) + ) +{ + if (!sess) return GF_BAD_PARAM; + + gf_mx_p(sess->mx); + + //only if existing + if (SignalData) sess->RTSP_SignalData = SignalData; + + //realloc or alloc + if (sess->rtsp_pck_buf && sess->rtsp_pck_size != RTSP_PCK_SIZE) { + sess->rtsp_pck_size = RTSP_PCK_SIZE; + sess->rtsp_pck_buf = (char *)realloc(sess->rtsp_pck_buf, sizeof(char)*sess->rtsp_pck_size); + } else if (!sess->rtsp_pck_buf) { + sess->rtsp_pck_size = RTSP_PCK_SIZE; + sess->rtsp_pck_buf = (char *)realloc(sess->rtsp_pck_buf, sizeof(char)*sess->rtsp_pck_size); + sess->pck_start = 0; + } + gf_mx_v(sess->mx); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_rtsp_set_buffer_size(GF_RTSPSession *sess, u32 BufferSize) +{ + if (!sess) return GF_BAD_PARAM; + sess->SockBufferSize = BufferSize; + return GF_OK; +} + + +static Bool HTTP_RandInit = 1; + +#define HTTP_WAIT_SEC 30 + +#define HTTP_RSP_OK "HTTP/1.0 200 OK" + + +void RTSP_GenerateHTTPCookie(GF_RTSPSession *sess) +{ + u32 i, num, temp; + + if (HTTP_RandInit) { + gf_rand_init(0); + HTTP_RandInit = 0; + } + if (!sess->CookieRadLen) { + strcpy(sess->HTTP_Cookie, "MPEG4M4"); + sess->CookieRadLen = 8; + } + num = gf_rand(); + for (i=0; i < 8; i++) { + temp = (num >> (i * 4)) & 0x0f; + sess->HTTP_Cookie[sess->CookieRadLen + i] = (u8) temp + sess->HTTP_Cookie[0]; + } + sess->HTTP_Cookie[sess->CookieRadLen + i] = 0; +} + + + + +//http tunnelling start. +GF_Err gf_rtsp_http_tunnel_start(GF_RTSPSession *sess, char *UserAgent) +{ + GF_Err e; + u32 size; + s32 pos; + char buffer[GF_RTSP_DEFAULT_BUFFER]; + + RTSP_GenerateHTTPCookie(sess); + + // 1. send "GET /sample.mov HTTP/1.0\r\n ..." + memset(buffer, 0, GF_RTSP_DEFAULT_BUFFER); + pos = 0; + pos += sprintf(buffer + pos, "GET /%s HTTP/1.0\r\n", sess->Service); + pos += sprintf(buffer + pos, "User-Agent: %s\r\n", UserAgent); + pos += sprintf(buffer + pos, "x-sessioncookie: %s\r\n", sess->HTTP_Cookie); + pos += sprintf(buffer + pos, "Accept: application/x-rtsp-tunnelled\r\n" ); + pos += sprintf(buffer + pos, "Pragma: no-cache\r\n" ); + pos += sprintf(buffer + pos, "Cache-Control: no-cache\r\n\r\n" ); + + // send it! + e = gf_sk_send_wait(sess->connection, buffer, strlen(buffer), HTTP_WAIT_SEC); + if (e) return e; + + // 2. wait for "HTTP/1.0 200 OK" + e = gf_sk_receive_wait(sess->connection, buffer, GF_RTSP_DEFAULT_BUFFER, 0, &size, HTTP_WAIT_SEC); + if (e) return e; + + //get HTTP/1.0 200 OK + if (strncmp(buffer, HTTP_RSP_OK, strlen(HTTP_RSP_OK))) return GF_REMOTE_SERVICE_ERROR; + + + // 3. send "POST /sample.mov HTTP/1.0\r\n ..." + sess->http = gf_sk_new(GF_SOCK_TYPE_TCP); + if (!sess->http ) return GF_IP_NETWORK_FAILURE; + + /*mobileIP is enabled, bind first*/ + if (gf_sk_connect(sess->http, sess->Server, sess->Port, sess->MobileIP)) return GF_IP_CONNECTION_FAILURE; + + memset(buffer, 0, GF_RTSP_DEFAULT_BUFFER); + pos = 0; + pos += sprintf(buffer + pos, "POST /%s HTTP/1.0\r\n", sess->Service); + pos += sprintf(buffer + pos, "User-Agent: %s\r\n", UserAgent); + pos += sprintf(buffer + pos, "x-sessioncookie: %s\r\n", sess->HTTP_Cookie); + pos += sprintf(buffer + pos, "Accept: application/x-rtsp-tunnelled\r\n"); + pos += sprintf(buffer + pos, "Pragma: no-cache\r\n"); + pos += sprintf(buffer + pos, "Cache-Control: no-cache\r\n"); + pos += sprintf(buffer + pos, "Content-Length: 32767\r\n"); + pos += sprintf(buffer + pos, "Expires: Sun. 9 Jan 1972 00:00:00 GMT\r\n\r\n"); + + // send it! + e = gf_sk_send_wait(sess->http, buffer, strlen(buffer), HTTP_WAIT_SEC); + + return e; +} + + +/*server-side RTSP sockets*/ + +static u32 SessionID_RandInit = 0; + + +GF_EXPORT +GF_RTSPSession *gf_rtsp_session_new_server(GF_Socket *rtsp_listener) +{ + GF_RTSPSession *sess; + GF_Socket *new_conn; + GF_Err e; + u32 fam; + u16 port; + char name[GF_MAX_IP_NAME_LEN]; + + if (!rtsp_listener) return NULL; + + + e = gf_sk_accept(rtsp_listener, &new_conn); + if (!new_conn || e) return NULL; + + e = gf_sk_get_local_info(new_conn, &port, &fam); + if (e) { + gf_sk_del(new_conn); + return NULL; + } + e = gf_sk_set_block_mode(new_conn, 1); + if (e) { + gf_sk_del(new_conn); + return NULL; + } + e = gf_sk_server_mode(new_conn, 1); + if (e) { + gf_sk_del(new_conn); + return NULL; + } + + //OK create a new session + GF_SAFEALLOC(sess, GF_RTSPSession); + + sess->connection = new_conn; + sess->Port = port; + sess->ConnectionType = fam; + gf_sk_get_host_name(name); + sess->Server = strdup(name); + + sess->TCPChannels = gf_list_new(); + return sess; +} + + +GF_EXPORT +GF_Err gf_rtsp_load_service_name(GF_RTSPSession *sess, char *URL) +{ + char server[1024], service[1024]; + GF_Err e; + u16 Port; + Bool UseTCP; + u32 type; + + if (!sess || !URL) return GF_BAD_PARAM; + e = RTSP_UnpackURL(URL, server, &Port, service, &UseTCP); + if (e) return e; + + type = UseTCP ? GF_SOCK_TYPE_TCP : GF_SOCK_TYPE_UDP; + //check the network type matches, otherwise deny client + if (sess->ConnectionType != type) return GF_URL_ERROR; + if (sess->Port != Port) return GF_URL_ERROR; + + //ok + sess->Server = strdup(server); + sess->Service = strdup(service); + return GF_OK; +} + + +GF_EXPORT +char *gf_rtsp_generate_session_id(GF_RTSPSession *sess) +{ + u32 one, two; + u64 res; + char buffer[30]; + + if (!sess) return NULL; + + if (!SessionID_RandInit) { + SessionID_RandInit = 1; + gf_rand_init(0); + } + one = gf_rand(); + //try to be as random as possible. if we had some global stats that'd be better + two = (u32) sess + sess->CurrentPos + sess->CurrentSize; + res = one; + res <<= 32; + res += two; + sprintf(buffer, LLU, res); + return strdup(buffer); +} + + +GF_EXPORT +GF_Err gf_rtsp_get_session_ip(GF_RTSPSession *sess, char *buffer) +{ + if (!sess || !sess->connection) return GF_BAD_PARAM; + gf_sk_get_local_ip(sess->connection, buffer); + return GF_OK; +} + + +GF_EXPORT +u8 gf_rtsp_get_next_interleave_id(GF_RTSPSession *sess) +{ + u32 i; + u8 id; + GF_TCPChan *ch; + id = 0; + i=0; + while ((ch = (GF_TCPChan *)gf_list_enum(sess->TCPChannels, &i))) { + if (ch->rtpID >= id) id = ch->rtpID + 1; + if (ch->rtcpID >= id) id = ch->rtcpID + 1; + } + return id; +} + + +GF_EXPORT +GF_Err gf_rtsp_get_remote_address(GF_RTSPSession *sess, char *buf) +{ + if (!sess || !sess->connection) return GF_BAD_PARAM; + return gf_sk_get_remote_address(sess->connection, buf); +} diff --git a/src/ietf/sdp.c b/src/ietf/sdp.c new file mode 100644 index 0000000..5135146 --- /dev/null +++ b/src/ietf/sdp.c @@ -0,0 +1,1153 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include + +#define SDP_WRITE_STEPALLOC 2048 + + +GF_EXPORT +GF_SDP_FMTP *gf_sdp_fmtp_new() +{ + GF_SDP_FMTP *tmp = (GF_SDP_FMTP*)malloc(sizeof(GF_SDP_FMTP)); + tmp->PayloadType = 0; + tmp->Attributes = gf_list_new(); + return tmp; +} + +GF_EXPORT +void gf_sdp_fmtp_del(GF_SDP_FMTP *fmtp) +{ + GF_X_Attribute *att; + if (!fmtp) return; + while (gf_list_count(fmtp->Attributes)) { + att = (GF_X_Attribute*)gf_list_get(fmtp->Attributes, 0); + gf_list_rem(fmtp->Attributes, 0); + if (att->Name) free(att->Name); + if (att->Value) free(att->Value); + free(att); + } + gf_list_del(fmtp->Attributes); + free(fmtp); +} + +GF_SDP_FMTP *SDP_GetFMTPForPayload(GF_SDPMedia *media, u32 PayloadType) +{ + GF_SDP_FMTP *tmp; + u32 i; + if (!media) return NULL; + i=0; + while ((tmp = (GF_SDP_FMTP*)gf_list_enum(media->FMTP, &i))) { + if (tmp->PayloadType == PayloadType) return tmp; + } + return NULL; +} + +void SDP_ParseAttribute(GF_SDPInfo *sdp, char *buffer, GF_SDPMedia *media) +{ + s32 pos; + u32 PayT; + char comp[3000]; + GF_RTPMap *map; + GF_SDP_FMTP *fmtp; + GF_X_Attribute *att; + + pos = gf_token_get(buffer, 0, " :\t\r\n", comp, 3000); + + if (!strcmp(comp, "cat")) { + if (media) return; + pos = gf_token_get(buffer, pos, ":\t\r\n", comp, 3000); + sdp->a_cat = strdup(comp); + return; + } + if (!strcmp(comp, "keywds")) { + if (media) return; + pos = gf_token_get(buffer, pos, ":\t\r\n", comp, 3000); + sdp->a_keywds = strdup(comp); + return; + } + if (!strcmp(comp, "tool")) { + if (media) return; + pos = gf_token_get(buffer, pos, ":\r\n", comp, 3000); + sdp->a_tool = strdup(comp); + return; + } + + if (!strcmp(comp, "ptime")) { + if (!media) return; + pos = gf_token_get(buffer, pos, ":\r\n", comp, 3000); + media->PacketTime = atoi(comp); + return; + } + if (!strcmp(comp, "recvonly")) { + if (!media) { + sdp->a_SendReceive = 1; + } else { + media->SendReceive = 1; + } + return; + } + if (!strcmp(comp, "sendonly")) { + if (!media) { + sdp->a_SendReceive = 2; + } else { + media->SendReceive = 2; + } + return; + } + if (!strcmp(comp, "sendrecv")) { + if (!media) { + sdp->a_SendReceive = 3; + } else { + media->SendReceive = 3; + } + return; + } + if (!strcmp(comp, "orient")) { + if (!media || media->Type) return; + pos = gf_token_get(buffer, pos, ":\r\n", comp, 3000); + media->orientation = strdup(comp); + return; + } + if (!strcmp(comp, "type")) { + if (media) return; + pos = gf_token_get(buffer, pos, ":\r\n", comp, 3000); + sdp->a_type = strdup(comp); + return; + } + if (!strcmp(comp, "charset")) { + if (media) return; + pos = gf_token_get(buffer, pos, ":\r\n", comp, 3000); + sdp->a_charset = strdup(comp); + return; + } + if (!strcmp(comp, "sdplang")) { + pos = gf_token_get(buffer, pos, ":\r\n", comp, 3000); + if (media) { + media->sdplang = strdup(comp); + } else { + sdp->a_sdplang = strdup(comp); + } + return; + } + if (!strcmp(comp, "lang")) { + pos = gf_token_get(buffer, pos, ":\r\n", comp, 3000); + if (media) { + media->lang = strdup(comp); + } else { + sdp->a_lang = strdup(comp); + } + return; + } + if (!strcmp(comp, "framerate")) { + //only for video + if (!media || (media->Type != 1)) return; + pos = gf_token_get(buffer, pos, ":\r\n", comp, 3000); + media->FrameRate = atof(comp); + return; + } + if (!strcmp(comp, "quality")) { + if (!media) return; + pos = gf_token_get(buffer, pos, ":\r\n", comp, 3000); + media->Quality = atoi(comp); + return; + } + if (!strcmp(comp, "rtpmap")) { + if (!media) return; + map = (GF_RTPMap*)malloc(sizeof(GF_RTPMap)); + pos = gf_token_get(buffer, pos, ": \r\n", comp, 3000); + map->PayloadType = atoi(comp); + pos = gf_token_get(buffer, pos, " /\r\n", comp, 3000); + map->payload_name = strdup(comp); + pos = gf_token_get(buffer, pos, " /\r\n", comp, 3000); + map->ClockRate = atoi(comp); + pos = gf_token_get(buffer, pos, " /\r\n", comp, 3000); + map->AudioChannels = (pos > 0) ? atoi(comp) : 0; + gf_list_add(media->RTPMaps, map); + return; + } + //FMTP + if (!strcmp(comp, "fmtp")) { + if (!media) return; + pos = gf_token_get(buffer, pos, ": \r\n", comp, 3000); + PayT = atoi(comp); + fmtp = SDP_GetFMTPForPayload(media, PayT); + if (!fmtp) { + fmtp = gf_sdp_fmtp_new(); + fmtp->PayloadType = PayT; + gf_list_add(media->FMTP, fmtp); + } + while (1) { + pos = gf_token_get(buffer, pos, "; =\r\n", comp, 3000); + if (pos <= 0) break; + att = (GF_X_Attribute*)malloc(sizeof(GF_X_Attribute)); + att->Name = strdup(comp); + att->Value = NULL; + pos ++; + pos = gf_token_get(buffer, pos, ";\r\n", comp, 3000); + if (pos > 0) att->Value = strdup(comp); + gf_list_add(fmtp->Attributes, att); + } + return; + } + //the rest cannot be discarded that way as it may be application-specific + //so keep it. + //a= || : + //we add in case ... + pos = gf_token_get(buffer, 0, " :\r\n", comp, 3000); + att = (GF_X_Attribute*)malloc(sizeof(GF_X_Attribute)); + att->Name = strdup(comp); + att->Value = NULL; + pos += 1; + if (buffer[pos] == ' ') pos += 1; + pos = gf_token_get(buffer, pos, "\r\n", comp, 3000); + if (pos > 0) att->Value = strdup(comp); + + if (media) { + gf_list_add(media->Attributes, att); + } else { + gf_list_add(sdp->Attributes, att); + } +} + + + +#define SDPM_DESTROY(p) if (media->p) free(media->p) +GF_EXPORT +void gf_sdp_media_del(GF_SDPMedia *media) +{ + GF_SDPBandwidth *bw; + GF_RTPMap *map; + GF_SDPConnection *conn; + GF_SDP_FMTP *fmtp; + GF_X_Attribute *att; + if (!media) return; + + while (gf_list_count(media->FMTP)) { + fmtp = (GF_SDP_FMTP*)gf_list_get(media->FMTP, 0); + gf_list_rem(media->FMTP, 0); + gf_sdp_fmtp_del(fmtp); + } + gf_list_del(media->FMTP); + + while (gf_list_count(media->Attributes)) { + att = (GF_X_Attribute*)gf_list_get(media->Attributes, 0); + gf_list_rem(media->Attributes, 0); + if (att->Name) free(att->Name); + if (att->Value) free(att->Value); + free(att); + } + gf_list_del(media->Attributes); + + while (gf_list_count(media->RTPMaps)) { + map = (GF_RTPMap*)gf_list_get(media->RTPMaps, 0); + free(map->payload_name); + free(map); + gf_list_rem(media->RTPMaps, 0); + } + gf_list_del(media->RTPMaps); + + while (gf_list_count(media->Connections)) { + conn = (GF_SDPConnection*)gf_list_get(media->Connections, 0); + gf_list_rem(media->Connections, 0); + gf_sdp_conn_del(conn); + } + gf_list_del(media->Connections); + + while (gf_list_count(media->Bandwidths)) { + bw = (GF_SDPBandwidth*)gf_list_get(media->Bandwidths, 0); + gf_list_rem(media->Bandwidths, 0); + if (bw->name) free(bw->name); + free(bw); + } + gf_list_del(media->Bandwidths); + + SDPM_DESTROY(orientation); + SDPM_DESTROY(sdplang); + SDPM_DESTROY(lang); + SDPM_DESTROY(Profile); + SDPM_DESTROY(fmt_list); + SDPM_DESTROY(k_method); + SDPM_DESTROY(k_key); + free(media); +} + + +GF_EXPORT +GF_SDPConnection *gf_sdp_conn_new() +{ + GF_SDPConnection *conn; + GF_SAFEALLOC(conn, GF_SDPConnection); + conn->TTL = -1; + return conn; +} + +GF_EXPORT +void gf_sdp_conn_del(GF_SDPConnection *conn) +{ + if (conn->add_type) free(conn->add_type); + if (conn->host) free(conn->host); + if (conn->net_type) free(conn->net_type); + free(conn); +} + +GF_EXPORT +GF_SDPMedia *gf_sdp_media_new() +{ + GF_SDPMedia *tmp; + GF_SAFEALLOC(tmp, GF_SDPMedia); + tmp->FMTP = gf_list_new(); + tmp->RTPMaps = gf_list_new(); + tmp->Attributes = gf_list_new(); + tmp->Connections = gf_list_new(); + tmp->Bandwidths = gf_list_new(); + tmp->Quality = -1; + return tmp; +} + +GF_EXPORT +GF_SDPInfo *gf_sdp_info_new() +{ + GF_SDPInfo *sdp; + GF_SAFEALLOC(sdp, GF_SDPInfo); + sdp->b_bandwidth = gf_list_new(); + sdp->media_desc = gf_list_new(); + sdp->Attributes = gf_list_new(); + sdp->Timing = gf_list_new(); + return sdp; +} + +#define SDP_DESTROY(p) if (sdp->p) \ + free(sdp->p); \ + sdp->p = NULL; + + +GF_EXPORT +void gf_sdp_info_reset(GF_SDPInfo *sdp) +{ + GF_SDPBandwidth *bw; + GF_SDPMedia *media; + GF_SDPTiming *timing; + GF_X_Attribute *att; + + if (!sdp) return; + + while (gf_list_count(sdp->media_desc)) { + media = (GF_SDPMedia*)gf_list_get(sdp->media_desc, 0); + gf_list_rem(sdp->media_desc, 0); + gf_sdp_media_del(media); + } + while (gf_list_count(sdp->Attributes)) { + att = (GF_X_Attribute*)gf_list_get(sdp->Attributes, 0); + gf_list_rem(sdp->Attributes, 0); + if (att->Name) free(att->Name); + if (att->Value) free(att->Value); + free(att); + } + while (gf_list_count(sdp->b_bandwidth)) { + bw = (GF_SDPBandwidth*)gf_list_get(sdp->b_bandwidth, 0); + gf_list_rem(sdp->b_bandwidth, 0); + if (bw->name) free(bw->name); + free(bw); + } + while (gf_list_count(sdp->Timing)) { + timing = (GF_SDPTiming*)gf_list_get(sdp->Timing, 0); + gf_list_rem(sdp->Timing, 0); + free(timing); + } + + //then delete all info ... + SDP_DESTROY(o_username); + SDP_DESTROY(o_session_id); + SDP_DESTROY(o_version); + SDP_DESTROY(o_address); + SDP_DESTROY(o_net_type); + SDP_DESTROY(o_add_type); + SDP_DESTROY(s_session_name); + SDP_DESTROY(i_description); + SDP_DESTROY(u_uri); + SDP_DESTROY(e_email); + SDP_DESTROY(p_phone); + SDP_DESTROY(k_method); + SDP_DESTROY(k_key); + SDP_DESTROY(a_cat); + SDP_DESTROY(a_keywds); + SDP_DESTROY(a_tool); + SDP_DESTROY(a_type); + SDP_DESTROY(a_charset); + SDP_DESTROY(a_sdplang); + SDP_DESTROY(a_lang); + + if (sdp->c_connection) { + gf_sdp_conn_del(sdp->c_connection); + sdp->c_connection = NULL; + } + sdp->a_SendReceive = 0; +} + +GF_EXPORT +void gf_sdp_info_del(GF_SDPInfo *sdp) +{ + if (!sdp) return; + gf_sdp_info_reset(sdp); + gf_list_del(sdp->media_desc); + gf_list_del(sdp->Attributes); + gf_list_del(sdp->b_bandwidth); + gf_list_del(sdp->Timing); + free(sdp); +} + + +Bool SDP_IsDynamicPayload(GF_SDPMedia *media, char *payt) +{ + u32 i; + GF_RTPMap *map; + char buf[10]; + i=0; + while ((map = (GF_RTPMap*)gf_list_enum(media->RTPMaps, &i))) { + sprintf(buf, "%d", map->PayloadType); + if (!strcmp(payt, buf)) return 1; + } + return 0; +} + +//translate h || m || d in sec. Fractions are not allowed with this writing +s32 SDP_MakeSeconds(char *buf) +{ + s32 sign; + char num[30], *test; + sign = 1; + if (buf[0] == '-') { + sign = -1; + buf += 1; + } + memset(num, 0, 30); + test = strstr(buf, "d"); + if (test) { + strncpy(num, buf, strlen(buf)-strlen(test)); + return (atoi(num)*sign*86400); + } + test = strstr(buf, "h"); + if (test) { + strncpy(num, buf, strlen(buf)-strlen(test)); + return (atoi(num)*sign*3600); + } + test = strstr(buf, "m"); + if (test) { + strncpy(num, buf, strlen(buf)-strlen(test)); + return (atoi(num)*sign*60); + } + return (atoi(buf) * sign); +} + + +GF_EXPORT +GF_Err gf_sdp_info_parse(GF_SDPInfo *sdp, char *sdp_text, u32 text_size) +{ + GF_SDPBandwidth *bw; + GF_SDPConnection *conn; + GF_SDPMedia *media; + GF_SDPTiming *timing; + u32 i; + s32 pos, LinePos; + char LineBuf[3000], comp[3000]; + + media = NULL; + timing = NULL; + + if (!sdp) return GF_BAD_PARAM; + + //Clean SDP info + gf_sdp_info_reset(sdp); + + LinePos = 0; + while (1) { + LinePos = gf_token_get_line(sdp_text, LinePos, text_size, LineBuf, 3000); + if (LinePos <= 0) break; + if (!strcmp(LineBuf, "\r\n") || !strcmp(LineBuf, "\n") || !strcmp(LineBuf, "\r")) continue; + + + switch (LineBuf[0]) { + case 'v': + pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); + sdp->Version = atoi(comp); + break; + case 'o': + pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000); + sdp->o_username = strdup(comp); + pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); + sdp->o_session_id = strdup(comp); + pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); + sdp->o_version = strdup(comp); + + pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); + sdp->o_net_type = strdup(comp); + + pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); + sdp->o_add_type = strdup(comp); + + pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); + sdp->o_address = strdup(comp); + break; + case 's': + pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); + sdp->s_session_name = strdup(comp); + break; + case 'i': + pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); + sdp->i_description = strdup(comp); + break; + case 'u': + pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); + sdp->u_uri = strdup(comp); + break; + case 'e': + pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); + sdp->e_email = strdup(comp); + break; + case 'p': + pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); + sdp->p_phone = strdup(comp); + break; + case 'c': + //if at session level, only 1 is allowed for all SDP + if (sdp->c_connection) break; + + conn = gf_sdp_conn_new(); + + pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000); + conn->net_type = strdup(comp); + + pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); + conn->add_type = strdup(comp); + + pos = gf_token_get(LineBuf, pos, " /\r\n", comp, 3000); + conn->host = strdup(comp); + if (gf_sk_is_multicast_address(conn->host)) { + //a valid SDP will have TTL if address is multicast + pos = gf_token_get(LineBuf, pos, "/\r\n", comp, 3000); + if (pos <= 0) { + gf_sdp_conn_del(conn); + break; + } + conn->TTL = atoi(comp); + //multiple address indication is only valid for media + pos = gf_token_get(LineBuf, pos, "/\r\n", comp, 3000); + if (pos > 0) { + if (!media) { + gf_sdp_conn_del(conn); + break; + } + conn->add_count = atoi(comp); + } + } + if (!media) + sdp->c_connection = conn; + else + gf_list_add(media->Connections, conn); + + break; + case 'b': + pos = gf_token_get(LineBuf, 2, ":\r\n", comp, 3000); + if (strcmp(comp, "CT") && strcmp(comp, "AS") && (comp[0] != 'X')) break; + + bw = (GF_SDPBandwidth*)malloc(sizeof(GF_SDPBandwidth)); + bw->name = strdup(comp); + pos = gf_token_get(LineBuf, pos, ":\r\n", comp, 3000); + bw->value = atoi(comp); + if (media) { + gf_list_add(media->Bandwidths, bw); + } else { + gf_list_add(sdp->b_bandwidth, bw); + } + break; + + case 't': + if (media) break; + //create a new time structure for each entry + GF_SAFEALLOC(timing, GF_SDPTiming); + pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000); + timing->StartTime = atoi(comp); + pos = gf_token_get(LineBuf, pos, "\r\n", comp, 3000); + timing->StopTime = atoi(comp); + gf_list_add(sdp->Timing, timing); + break; + case 'r': + if (media) break; + pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000); + timing->RepeatInterval = SDP_MakeSeconds(comp); + pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); + timing->ActiveDuration = SDP_MakeSeconds(comp); + while (1) { + pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); + if (pos <= 0) break; + timing->OffsetFromStart[timing->NbRepeatOffsets] = SDP_MakeSeconds(comp); + timing->NbRepeatOffsets += 1; + } + break; + case 'z': + if (media) break; + pos = 2; + while (1) { + pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); + if (pos <= 0) break; + timing->AdjustmentTime[timing->NbZoneOffsets] = atoi(comp); + pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); + timing->AdjustmentOffset[timing->NbZoneOffsets] = SDP_MakeSeconds(comp); + timing->NbZoneOffsets += 1; + } + break; + case 'k': + pos = gf_token_get(LineBuf, 2, ":\t\r\n", comp, 3000); + if (media) { + media->k_method = strdup(comp); + } else { + sdp->k_method = strdup(comp); + } + pos = gf_token_get(LineBuf, pos, ":\r\n", comp, 3000); + if (pos > 0) { + if (media) { + media->k_key = strdup(comp); + } else { + sdp->k_key = strdup(comp); + } + } + break; + case 'a': + SDP_ParseAttribute(sdp, LineBuf+2, media); + break; + case 'm': + pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000); + if (strcmp(comp, "audio") + && strcmp(comp, "data") + && strcmp(comp, "control") + && strcmp(comp, "video") + && strcmp(comp, "text") + && strcmp(comp, "application")) { + return GF_SERVICE_ERROR; + } + media = gf_sdp_media_new(); + //media type + if (!strcmp(comp, "video")) media->Type = 1; + else if (!strcmp(comp, "audio")) media->Type = 2; + else if (!strcmp(comp, "text")) media->Type = 3; + else if (!strcmp(comp, "data")) media->Type = 4; + else if (!strcmp(comp, "control")) media->Type = 5; + else media->Type = 0; + //port numbers + gf_token_get(LineBuf, pos, " ", comp, 3000); + if (!strstr(comp, "/")) { + pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000); + media->PortNumber = atoi(comp); + media->NumPorts = 0; + } else { + pos = gf_token_get(LineBuf, pos, " /\r\n", comp, 3000); + media->PortNumber = atoi(comp); + pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000); + media->NumPorts = atoi(comp); + } + //transport Profile + pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000); + media->Profile = strdup(comp); + pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000); + media->fmt_list = strdup(comp); + + gf_list_add(sdp->media_desc, media); + break; + } + } + //finally rewrite the fmt_list for all media, and remove dynamic payloads + //from the list + i=0; + while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) { + pos = 0; + LinePos = 1; + strcpy(LineBuf, ""); + while (1) { + if (!media->fmt_list) break; + pos = gf_token_get(media->fmt_list, pos, " ", comp, 3000); + if (pos <= 0) break; + if (!SDP_IsDynamicPayload(media, comp)) { + if (!LinePos) { + strcat(LineBuf, " "); + } else { + LinePos = 0; + } + strcat(LineBuf, comp); + } + free(media->fmt_list); + media->fmt_list = NULL; + if (strlen(LineBuf)) { + media->fmt_list = strdup(LineBuf); + } + } + } + return GF_OK; +} + + + +GF_Err SDP_CheckConnection(GF_SDPConnection *conn) +{ + if (!conn) return GF_BAD_PARAM; + if (!conn->host || !conn->add_type || !conn->net_type) return GF_REMOTE_SERVICE_ERROR; + if (gf_sk_is_multicast_address(conn->host)) { + if (conn->TTL < 0 || conn->TTL > 255) return GF_REMOTE_SERVICE_ERROR; + } else { + conn->TTL = -1; + conn->add_count = 0; + } + return GF_OK; +} + +//return GF_BAD_PARAM if invalid structure, GF_REMOTE_SERVICE_ERROR if bad formatting +//or GF_OK +GF_EXPORT +GF_Err gf_sdp_info_check(GF_SDPInfo *sdp) +{ + GF_Err e; + u32 i, j, count; + GF_SDPMedia *media; + GF_SDPConnection *conn; + GF_RTPMap *map; + Bool HasGlobalConnection, HasSeveralPorts; + + if (!sdp || !sdp->media_desc || !sdp->Attributes) return GF_BAD_PARAM; + //we force at least one media per SDP + if (!gf_list_count(sdp->media_desc)) return GF_REMOTE_SERVICE_ERROR; + + //normative fields + //o= + if (!sdp->o_add_type || !sdp->o_address || !sdp->o_username || !sdp->o_session_id || !sdp->o_version) + return GF_REMOTE_SERVICE_ERROR; + //s= + if (!sdp->s_session_name) return GF_REMOTE_SERVICE_ERROR; + //t= +// if () return GF_REMOTE_SERVICE_ERROR; + //c= + if (sdp->c_connection) { + e = SDP_CheckConnection(sdp->c_connection); + if (e) return e; + //multiple addresses are only for media desc + if (sdp->c_connection->add_count >= 2) return GF_REMOTE_SERVICE_ERROR; + HasGlobalConnection = 1; + } else { + HasGlobalConnection = 0; + } + + //then check all media + i=0; + while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) { + HasSeveralPorts = 0; + + //m= : force non-null port, profile and fmt_list + if (/*!media->PortNumber || */ !media->Profile) return GF_REMOTE_SERVICE_ERROR; + if (media->NumPorts) HasSeveralPorts = 1; + + //no connections specified - THIS IS AN ERROR IN SDP BUT NOT IN ALL RTSP SESSIONS... +// if (!HasGlobalConnection && !gf_list_count(media->Connections)) return GF_REMOTE_SERVICE_ERROR; + //too many connections specified + if (HasGlobalConnection && gf_list_count(media->Connections)) return GF_REMOTE_SERVICE_ERROR; + + //check all connections, and make sure we don't have multiple addresses + //and multiple ports at the same time + count = gf_list_count(media->Connections); + if (count>1 && HasSeveralPorts) return GF_REMOTE_SERVICE_ERROR; + + for (j=0; jConnections, j); + e = SDP_CheckConnection(conn); + if (e) return e; + if ((conn->add_count >= 2) && HasSeveralPorts) return GF_REMOTE_SERVICE_ERROR; + } + //RTPMaps. 0 is tolerated, but if some are specified check them + j=0; + while ((map = (GF_RTPMap*)gf_list_enum(media->RTPMaps, &j))) { + //RFC2327 is not clear here, but we assume the PayloadType should be a DYN one + //however this depends on the profile (RTP/AVP or others) so don't check it + //ClockRate SHALL NOT be NULL + if (!map->payload_name || !map->ClockRate) return GF_REMOTE_SERVICE_ERROR; + } + } + //Encryption: nothing tells wether the scope of the global key is eclusive or not. + //we accept a global key + keys per media entry, assuming that the media key primes + //on the global key + + return GF_OK; +} + + + + +#define SDP_WRITE_ALLOC_STR(str, space) \ + if (str) { \ + if (strlen(str)+pos + (space ? 1 : 0) >= buf_size) { \ + buf_size += SDP_WRITE_STEPALLOC; \ + buf = (char*)realloc(buf, sizeof(char)*buf_size); \ + } \ + strcpy(buf+pos, str); \ + pos += strlen(str); \ + if (space) { \ + strcat(buf+pos, " "); \ + pos += 1; \ + } \ + } \ + +#define SDP_WRITE_ALLOC_INT(d, spa, sig) \ + if (sig) { \ + sprintf(temp, "%d", d); \ + } else { \ + sprintf(temp, "%u", d); \ + } \ + SDP_WRITE_ALLOC_STR(temp, spa); + +#define SDP_WRITE_ALLOC_FLOAT(d, spa) \ + sprintf(temp, "%.2f", d); \ + SDP_WRITE_ALLOC_STR(temp, spa); + +#define TEST_SDP_WRITE_SINGLE(type, str, sep) \ + if (str) { \ + SDP_WRITE_ALLOC_STR(type, 0); \ + if (sep) SDP_WRITE_ALLOC_STR(":", 0); \ + SDP_WRITE_ALLOC_STR(str, 0); \ + SDP_WRITE_ALLOC_STR("\r\n", 0); \ + } + + +#define SDP_WRITE_CONN(conn) \ + if (conn) { \ + SDP_WRITE_ALLOC_STR("c=", 0); \ + SDP_WRITE_ALLOC_STR(conn->net_type, 1); \ + SDP_WRITE_ALLOC_STR(conn->add_type, 1); \ + SDP_WRITE_ALLOC_STR(conn->host, 0); \ + if (gf_sk_is_multicast_address(conn->host)) { \ + SDP_WRITE_ALLOC_STR("/", 0); \ + SDP_WRITE_ALLOC_INT(conn->TTL, 0, 0); \ + if (conn->add_count >= 2) { \ + SDP_WRITE_ALLOC_STR("/", 0); \ + SDP_WRITE_ALLOC_INT(conn->add_count, 0, 0); \ + } \ + } \ + SDP_WRITE_ALLOC_STR("\r\n", 0); \ + } + +GF_EXPORT +GF_Err gf_sdp_info_write(GF_SDPInfo *sdp, char **out_str_buf) +{ + char *buf; + GF_SDP_FMTP *fmtp; + char temp[50]; + GF_SDPMedia *media; + GF_SDPBandwidth *bw; + u32 buf_size, pos, i, j, k; + GF_RTPMap *map; + GF_SDPConnection *conn; + GF_Err e; + GF_SDPTiming *timing; + GF_X_Attribute *att; + + e = gf_sdp_info_check(sdp); + if (e) return e; + + buf = (char *)malloc(SDP_WRITE_STEPALLOC); + buf_size = SDP_WRITE_STEPALLOC; + pos = 0; + + //v + SDP_WRITE_ALLOC_STR("v=", 0); + SDP_WRITE_ALLOC_INT(sdp->Version, 0, 0); + SDP_WRITE_ALLOC_STR("\r\n", 0); + //o + SDP_WRITE_ALLOC_STR("o=", 0); + SDP_WRITE_ALLOC_STR(sdp->o_username, 1); + SDP_WRITE_ALLOC_STR(sdp->o_session_id, 1); + SDP_WRITE_ALLOC_STR(sdp->o_version, 1); + SDP_WRITE_ALLOC_STR(sdp->o_net_type, 1); + SDP_WRITE_ALLOC_STR(sdp->o_add_type, 1); + SDP_WRITE_ALLOC_STR(sdp->o_address, 0); + SDP_WRITE_ALLOC_STR("\r\n", 0); + //s + TEST_SDP_WRITE_SINGLE("s=", sdp->s_session_name, 0); + //i + TEST_SDP_WRITE_SINGLE("i=", sdp->i_description, 0); + //u + TEST_SDP_WRITE_SINGLE("u=", sdp->u_uri, 0); + //e + TEST_SDP_WRITE_SINGLE("e=", sdp->e_email, 0); + //p + TEST_SDP_WRITE_SINGLE("p=", sdp->p_phone, 0); + //c + SDP_WRITE_CONN(sdp->c_connection); + //b + i=0; + while ((bw = (GF_SDPBandwidth*)gf_list_enum(sdp->b_bandwidth, &i))) { + SDP_WRITE_ALLOC_STR("b=", 0); + SDP_WRITE_ALLOC_STR(bw->name, 0); + SDP_WRITE_ALLOC_STR(":", 0); + SDP_WRITE_ALLOC_INT(bw->value, 0, 0); + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + //t+r+z + i=0; + while ((timing = (GF_SDPTiming*)gf_list_enum(sdp->Timing, &i))) { + if (timing->NbRepeatOffsets > GF_SDP_MAX_TIMEOFFSET) timing->NbRepeatOffsets = GF_SDP_MAX_TIMEOFFSET; + if (timing->NbZoneOffsets > GF_SDP_MAX_TIMEOFFSET) timing->NbZoneOffsets = GF_SDP_MAX_TIMEOFFSET; + //t + SDP_WRITE_ALLOC_STR("t=", 0); + SDP_WRITE_ALLOC_INT(timing->StartTime, 1, 0); + SDP_WRITE_ALLOC_INT(timing->StopTime, 0, 0); + SDP_WRITE_ALLOC_STR("\r\n", 0); + if (timing->NbRepeatOffsets) { + SDP_WRITE_ALLOC_STR("r=", 0); + SDP_WRITE_ALLOC_INT(timing->RepeatInterval, 1, 0); + SDP_WRITE_ALLOC_INT(timing->ActiveDuration, 0, 0); + for (j=0; jNbRepeatOffsets; j++) { + SDP_WRITE_ALLOC_STR(" ", 0); + SDP_WRITE_ALLOC_INT(timing->OffsetFromStart[j], 0, 0); + } + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + if (timing->NbZoneOffsets) { + SDP_WRITE_ALLOC_STR("z=", 0); + for (j=0; jNbZoneOffsets; j++) { + SDP_WRITE_ALLOC_INT(timing->AdjustmentTime[j], 1, 0); + if (j+1 == timing->NbRepeatOffsets) { + SDP_WRITE_ALLOC_INT(timing->AdjustmentOffset[j], 0, 1); + } else { + SDP_WRITE_ALLOC_INT(timing->AdjustmentOffset[j], 1, 1); + } + } + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + } + //k + if (sdp->k_method) { + SDP_WRITE_ALLOC_STR("k=", 0); + SDP_WRITE_ALLOC_STR(sdp->k_method, 0); + if (sdp->k_key) { + SDP_WRITE_ALLOC_STR(":", 0); + SDP_WRITE_ALLOC_STR(sdp->k_key, 0); + } + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + //a=cat + TEST_SDP_WRITE_SINGLE("a=cat", sdp->a_cat, 1); + //a=keywds + TEST_SDP_WRITE_SINGLE("a=keywds", sdp->a_keywds, 1); + //a=tool + TEST_SDP_WRITE_SINGLE("a=tool", sdp->a_tool, 1); + //a=SendRecv + switch (sdp->a_SendReceive) { + case 1: + TEST_SDP_WRITE_SINGLE("a=", "recvonly", 0); + break; + case 2: + TEST_SDP_WRITE_SINGLE("a=", "sendonly", 0); + break; + case 3: + TEST_SDP_WRITE_SINGLE("a=", "sendrecv", 0); + break; + default: + break; + } + //a=type + TEST_SDP_WRITE_SINGLE("a=type", sdp->a_type, 1); + //a=charset + TEST_SDP_WRITE_SINGLE("a=charset", sdp->a_charset, 1); + //a=sdplang + TEST_SDP_WRITE_SINGLE("a=sdplang", sdp->a_sdplang, 1); + //a=lang + TEST_SDP_WRITE_SINGLE("a=lang", sdp->a_lang, 1); + + //the rest + i=0; + while ((att = (GF_X_Attribute*)gf_list_enum(sdp->Attributes, &i))) { + SDP_WRITE_ALLOC_STR("a=", 0); + SDP_WRITE_ALLOC_STR(att->Name, 0); + if (att->Value) { + SDP_WRITE_ALLOC_STR(":", 0); + SDP_WRITE_ALLOC_STR(att->Value, 0); + } + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + + //now write media specific + i=0; + while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) { + //m= + SDP_WRITE_ALLOC_STR("m=", 0); + switch (media->Type) { + case 1: + SDP_WRITE_ALLOC_STR("video", 1); + break; + case 2: + SDP_WRITE_ALLOC_STR("audio", 1); + break; + case 3: + SDP_WRITE_ALLOC_STR("data", 1); + break; + case 4: + SDP_WRITE_ALLOC_STR("control", 1); + break; + default: + SDP_WRITE_ALLOC_STR("application", 1); + break; + } + SDP_WRITE_ALLOC_INT(media->PortNumber, 0, 0); + if (media->NumPorts >= 2) { + SDP_WRITE_ALLOC_STR("/", 0); + SDP_WRITE_ALLOC_INT(media->NumPorts, 1, 0); + } else { + SDP_WRITE_ALLOC_STR(" ", 0); + } + SDP_WRITE_ALLOC_STR(media->Profile, 1); + SDP_WRITE_ALLOC_STR(media->fmt_list, 0); + + j=0; + while ((map = (GF_RTPMap*)gf_list_enum(media->RTPMaps, &j))) { + SDP_WRITE_ALLOC_STR(" ", 0); + SDP_WRITE_ALLOC_INT(map->PayloadType, 0, 0); + } + SDP_WRITE_ALLOC_STR("\r\n", 0); + + //c= + j=0; + while ((conn = (GF_SDPConnection*)gf_list_enum(media->Connections, &j))) { + SDP_WRITE_CONN(conn); + } + + //k= + if (media->k_method) { + SDP_WRITE_ALLOC_STR("k=", 0); + SDP_WRITE_ALLOC_STR(media->k_method, 0); + if (media->k_key) { + SDP_WRITE_ALLOC_STR(":", 0); + SDP_WRITE_ALLOC_STR(media->k_key, 0); + } + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + //b + j=0; + while ((bw = (GF_SDPBandwidth*)gf_list_enum(media->Bandwidths, &j))) { + SDP_WRITE_ALLOC_STR("b=", 0); + SDP_WRITE_ALLOC_STR(bw->name, 0); + SDP_WRITE_ALLOC_STR(":", 0); + SDP_WRITE_ALLOC_INT(bw->value, 0, 0); + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + + //a=rtpmap + j=0; + while ((map = (GF_RTPMap*)gf_list_enum(media->RTPMaps, &j))) { + + SDP_WRITE_ALLOC_STR("a=rtpmap", 0); + SDP_WRITE_ALLOC_STR(":", 0); + SDP_WRITE_ALLOC_INT(map->PayloadType, 1, 0); + SDP_WRITE_ALLOC_STR(map->payload_name, 0); + SDP_WRITE_ALLOC_STR("/", 0); + SDP_WRITE_ALLOC_INT(map->ClockRate, 0, 0); + if (map->AudioChannels > 1) { + SDP_WRITE_ALLOC_STR("/", 0); + SDP_WRITE_ALLOC_INT(map->AudioChannels, 0, 0); + } + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + //a=fmtp + j=0; + while ((fmtp = (GF_SDP_FMTP*)gf_list_enum(media->FMTP, &j))) { + SDP_WRITE_ALLOC_STR("a=fmtp:", 0); + SDP_WRITE_ALLOC_INT(fmtp->PayloadType, 1 , 0); + k=0; + while ((att = (GF_X_Attribute*)gf_list_enum(fmtp->Attributes, &k)) ) { + if (k>1) SDP_WRITE_ALLOC_STR(";", 0); + SDP_WRITE_ALLOC_STR(att->Name, 0); + if (att->Value) { + SDP_WRITE_ALLOC_STR("=", 0); + SDP_WRITE_ALLOC_STR(att->Value, 0); + } + } + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + //a=ptime + if (media->PacketTime) { + SDP_WRITE_ALLOC_STR("a=ptime:", 0); + SDP_WRITE_ALLOC_INT(media->PacketTime, 0, 0); + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + //a=FrameRate + if (media->Type == 1 && media->FrameRate) { + SDP_WRITE_ALLOC_STR("a=framerate:", 0); + SDP_WRITE_ALLOC_FLOAT(media->FrameRate, 0); + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + //a=SendRecv + switch (media->SendReceive) { + case 1: + TEST_SDP_WRITE_SINGLE("a=", "recvonly", 0); + break; + case 2: + TEST_SDP_WRITE_SINGLE("a=", "sendonly", 0); + break; + case 3: + TEST_SDP_WRITE_SINGLE("a=", "sendrecv", 0); + break; + default: + break; + } + //a=orient + TEST_SDP_WRITE_SINGLE("a=orient", media->orientation, 1); + //a=sdplang + TEST_SDP_WRITE_SINGLE("a=sdplang", media->sdplang, 1); + //a=lang + TEST_SDP_WRITE_SINGLE("a=lang", media->lang, 1); + //a=quality + if (media->Quality >= 0) { + SDP_WRITE_ALLOC_STR("a=quality:", 0); + SDP_WRITE_ALLOC_INT(media->Quality, 0, 0); + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + //the rest + j=0; + while ((att = (GF_X_Attribute*)gf_list_enum(media->Attributes, &j))) { + SDP_WRITE_ALLOC_STR("a=", 0); + SDP_WRITE_ALLOC_STR(att->Name, 0); + if (att->Value) { + SDP_WRITE_ALLOC_STR(":", 0); + SDP_WRITE_ALLOC_STR(att->Value, 0); + } + SDP_WRITE_ALLOC_STR("\r\n", 0); + } + } + + //finally realloc + //finall NULL char + pos += 1; + buf = (char *)realloc(buf, pos); + *out_str_buf = buf; + return GF_OK; +} + diff --git a/src/isomedia/avc_ext.c b/src/isomedia/avc_ext.c new file mode 100644 index 0000000..2eeb0f5 --- /dev/null +++ b/src/isomedia/avc_ext.c @@ -0,0 +1,449 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + + +void AVC_RewriteESDescriptor(GF_MPEGVisualSampleEntryBox *avc) +{ + if (avc->emul_esd) gf_odf_desc_del((GF_Descriptor *)avc->emul_esd); + avc->emul_esd = gf_odf_desc_esd_new(2); + avc->emul_esd->decoderConfig->streamType = GF_STREAM_VISUAL; + /*AVC OTI is 0x21, AVC parameter set stream OTI (not supported in gpac) is 0x22*/ + avc->emul_esd->decoderConfig->objectTypeIndication = 0x21; + if (avc->bitrate) { + avc->emul_esd->decoderConfig->bufferSizeDB = avc->bitrate->bufferSizeDB; + avc->emul_esd->decoderConfig->avgBitrate = avc->bitrate->avgBitrate; + avc->emul_esd->decoderConfig->maxBitrate = avc->bitrate->maxBitrate; + } + if (avc->descr) { + u32 i=0; + GF_Descriptor *desc,*clone; + i=0; + while ((desc = (GF_Descriptor *)gf_list_enum(avc->descr->descriptors, &i))) { + clone = NULL; + gf_odf_desc_copy(desc, &clone); + if (gf_odf_desc_add_desc((GF_Descriptor *)avc->emul_esd, clone) != GF_OK) + gf_odf_desc_del(clone); + } + } + if (avc->avc_config && avc->avc_config->config) { + gf_odf_avc_cfg_write(avc->avc_config->config, &avc->emul_esd->decoderConfig->decoderSpecificInfo->data, &avc->emul_esd->decoderConfig->decoderSpecificInfo->dataLength); + } +} + +GF_Err AVC_UpdateESD(GF_MPEGVisualSampleEntryBox *avc, GF_ESD *esd) +{ + if (!avc->bitrate) avc->bitrate = (GF_MPEG4BitRateBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_BTRT); + if (avc->descr) gf_isom_box_del((GF_Box *) avc->descr); + avc->descr = NULL; + avc->bitrate->avgBitrate = esd->decoderConfig->avgBitrate; + avc->bitrate->maxBitrate = esd->decoderConfig->maxBitrate; + avc->bitrate->bufferSizeDB = esd->decoderConfig->bufferSizeDB; + + if (gf_list_count(esd->IPIDataSet) + || gf_list_count(esd->IPMPDescriptorPointers) + || esd->langDesc + || gf_list_count(esd->extensionDescriptors) + || esd->ipiPtr || esd->qos || esd->RegDescriptor) { + + avc->descr = (GF_MPEG4ExtensionDescriptorsBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_M4DS); + if (esd->RegDescriptor) { gf_list_add(avc->descr->descriptors, esd->RegDescriptor); esd->RegDescriptor = NULL; } + if (esd->qos) { gf_list_add(avc->descr->descriptors, esd->qos); esd->qos = NULL; } + if (esd->ipiPtr) { gf_list_add(avc->descr->descriptors, esd->ipiPtr); esd->ipiPtr= NULL; } + + while (gf_list_count(esd->IPIDataSet)) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->IPIDataSet, 0); + gf_list_rem(esd->IPIDataSet, 0); + gf_list_add(avc->descr->descriptors, desc); + } + while (gf_list_count(esd->IPMPDescriptorPointers)) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->IPMPDescriptorPointers, 0); + gf_list_rem(esd->IPMPDescriptorPointers, 0); + gf_list_add(avc->descr->descriptors, desc); + } + if (esd->langDesc) { + gf_list_add(avc->descr->descriptors, esd->langDesc); + esd->langDesc = NULL; + } + while (gf_list_count(esd->extensionDescriptors)) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->extensionDescriptors, 0); + gf_list_rem(esd->extensionDescriptors, 0); + gf_list_add(avc->descr->descriptors, desc); + } + } + + /*update GF_AVCConfig*/ + if (!avc->avc_config) avc->avc_config = (GF_AVCConfigurationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC); + if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { + if (avc->avc_config->config) gf_odf_avc_cfg_del(avc->avc_config->config); + avc->avc_config->config = gf_odf_avc_cfg_read(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); + } + gf_odf_desc_del((GF_Descriptor *)esd); + AVC_RewriteESDescriptor(avc); + return GF_OK; +} + +static GF_AVCConfig *AVC_DuplicateConfig(GF_AVCConfig *cfg) +{ + u32 i, count; + GF_AVCConfigSlot *p1, *p2; + GF_AVCConfig *cfg_new = gf_odf_avc_cfg_new(); + cfg_new->AVCLevelIndication = cfg->AVCLevelIndication; + cfg_new->AVCProfileIndication = cfg->AVCProfileIndication; + cfg_new->configurationVersion = cfg->configurationVersion; + cfg_new->nal_unit_size = cfg->nal_unit_size; + cfg_new->profile_compatibility = cfg->profile_compatibility; + + count = gf_list_count(cfg->sequenceParameterSets); + for (i=0; isequenceParameterSets, i); + p2 = (GF_AVCConfigSlot*)malloc(sizeof(GF_AVCConfigSlot)); + p2->size = p1->size; + p2->data = (char *)malloc(sizeof(char)*p1->size); + memcpy(p2->data, p1->data, sizeof(char)*p1->size); + gf_list_add(cfg_new->sequenceParameterSets, p2); + } + + count = gf_list_count(cfg->pictureParameterSets); + for (i=0; ipictureParameterSets, i); + p2 = (GF_AVCConfigSlot*)malloc(sizeof(GF_AVCConfigSlot)); + p2->size = p1->size; + p2->data = (char*)malloc(sizeof(char)*p1->size); + memcpy(p2->data, p1->data, sizeof(char)*p1->size); + gf_list_add(cfg_new->pictureParameterSets, p2); + } + return cfg_new; +} + +#ifndef GPAC_READ_ONLY +GF_Err gf_isom_avc_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AVCConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_MPEGVisualSampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !cfg) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + //create a new entry + entry = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AVC1); + if (!entry) return GF_OUT_OF_MEM; + entry->avc_config = (GF_AVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC); + entry->avc_config->config = AVC_DuplicateConfig(cfg); + entry->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + AVC_RewriteESDescriptor(entry); + return e; +} + +GF_Err gf_isom_avc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_AVCConfig *cfg) +{ + GF_TrackBox *trak; + GF_Err e; + GF_MPEGVisualSampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !cfg || !DescriptionIndex) return GF_BAD_PARAM; + entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return GF_BAD_PARAM; + if (entry->type != GF_ISOM_BOX_TYPE_AVC1) return GF_BAD_PARAM; + + if (entry->avc_config->config) gf_odf_avc_cfg_del(entry->avc_config->config); + entry->avc_config->config = AVC_DuplicateConfig(cfg); + AVC_RewriteESDescriptor(entry); + return GF_OK; +} + +GF_Err gf_isom_set_ipod_compatible(GF_ISOFile *the_file, u32 trackNumber) +{ + static const u8 ipod_ext[][16] = { { 0x6B, 0x68, 0x40, 0xF2, 0x5F, 0x24, 0x4F, 0xC5, 0xBA, 0x39, 0xA5, 0x1B, 0xCF, 0x03, 0x23, 0xF3} }; + GF_TrackBox *trak; + GF_Err e; + GF_MPEGVisualSampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media) return GF_BAD_PARAM; + entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, 0); + if (!entry) return GF_OK; + if (entry->type != GF_ISOM_BOX_TYPE_AVC1) return GF_OK; + + if (!entry->ipod_ext) entry->ipod_ext = (GF_UnknownUUIDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); + memcpy(entry->ipod_ext->uuid, ipod_ext, sizeof(u8)*16); + entry->ipod_ext->dataSize = 0; + return GF_OK; +} + +#endif + +GF_EXPORT +GF_AVCConfig *gf_isom_avc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_MPEGVisualSampleEntryBox *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !DescriptionIndex) return NULL; + entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return NULL; + //if (entry->type != GF_ISOM_BOX_TYPE_AVC1) return NULL; + if (!entry->avc_config) return NULL; + return AVC_DuplicateConfig(entry->avc_config->config); +} + +void btrt_del(GF_Box *s) +{ + GF_MPEG4BitRateBox *ptr = (GF_MPEG4BitRateBox *)s; + if (ptr) free(ptr); +} +GF_Err btrt_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MPEG4BitRateBox *ptr = (GF_MPEG4BitRateBox *)s; + ptr->bufferSizeDB = gf_bs_read_u32(bs); + ptr->maxBitrate = gf_bs_read_u32(bs); + ptr->avgBitrate = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *btrt_New() +{ + GF_MPEG4BitRateBox *tmp = (GF_MPEG4BitRateBox *) malloc(sizeof(GF_MPEG4BitRateBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MPEG4BitRateBox)); + tmp->type = GF_ISOM_BOX_TYPE_BTRT; + return (GF_Box *)tmp; +} + +#ifndef GPAC_READ_ONLY +GF_Err btrt_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MPEG4BitRateBox *ptr = (GF_MPEG4BitRateBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->bufferSizeDB); + gf_bs_write_u32(bs, ptr->maxBitrate); + gf_bs_write_u32(bs, ptr->avgBitrate); + return GF_OK; +} +GF_Err btrt_Size(GF_Box *s) +{ + GF_Err e; + GF_MPEG4BitRateBox *ptr = (GF_MPEG4BitRateBox *)s; + e = gf_isom_box_get_size(s); + ptr->size += 12; + return e; +} +#endif + + + +void m4ds_del(GF_Box *s) +{ + GF_MPEG4ExtensionDescriptorsBox *ptr = (GF_MPEG4ExtensionDescriptorsBox *)s; + gf_odf_desc_list_del(ptr->descriptors); + gf_list_del(ptr->descriptors); + free(ptr); +} +GF_Err m4ds_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + char *enc_od; + GF_MPEG4ExtensionDescriptorsBox *ptr = (GF_MPEG4ExtensionDescriptorsBox *)s; + u32 od_size = (u32) ptr->size; + if (!od_size) return GF_OK; + enc_od = (char *)malloc(sizeof(char) * od_size); + gf_bs_read_data(bs, enc_od, od_size); + e = gf_odf_desc_list_read((char *)enc_od, od_size, ptr->descriptors); + free(enc_od); + return e; +} +GF_Box *m4ds_New() +{ + GF_MPEG4ExtensionDescriptorsBox *tmp = (GF_MPEG4ExtensionDescriptorsBox *) malloc(sizeof(GF_MPEG4ExtensionDescriptorsBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MPEG4ExtensionDescriptorsBox)); + tmp->type = GF_ISOM_BOX_TYPE_M4DS; + tmp->descriptors = gf_list_new(); + return (GF_Box *)tmp; +} + +#ifndef GPAC_READ_ONLY +GF_Err m4ds_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + char *enc_ods; + u32 enc_od_size; + GF_MPEG4ExtensionDescriptorsBox *ptr = (GF_MPEG4ExtensionDescriptorsBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + enc_ods = NULL; + enc_od_size = 0; + e = gf_odf_desc_list_write(ptr->descriptors, &enc_ods, &enc_od_size); + if (e) return e; + if (enc_od_size) { + gf_bs_write_data(bs, enc_ods, enc_od_size); + free(enc_ods); + } + return GF_OK; +} +GF_Err m4ds_Size(GF_Box *s) +{ + GF_Err e; + u32 descSize; + GF_MPEG4ExtensionDescriptorsBox *ptr = (GF_MPEG4ExtensionDescriptorsBox *)s; + e = gf_isom_box_get_size(s); + if (!e) e = gf_odf_desc_list_size(ptr->descriptors, &descSize); + ptr->size += descSize; + return e; +} +#endif + + + +void avcc_del(GF_Box *s) +{ + GF_AVCConfigurationBox *ptr = (GF_AVCConfigurationBox *)s; + if (ptr->config) gf_odf_avc_cfg_del(ptr->config); + free(ptr); +} +GF_Err avcc_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i, count; + GF_AVCConfigurationBox *ptr = (GF_AVCConfigurationBox *)s; + + if (ptr->config) gf_odf_avc_cfg_del(ptr->config); + ptr->config = gf_odf_avc_cfg_new(); + ptr->config->configurationVersion = gf_bs_read_u8(bs); + ptr->config->AVCProfileIndication = gf_bs_read_u8(bs); + ptr->config->profile_compatibility = gf_bs_read_u8(bs); + ptr->config->AVCLevelIndication = gf_bs_read_u8(bs); + gf_bs_read_int(bs, 6); + ptr->config->nal_unit_size = 1 + gf_bs_read_int(bs, 2); + gf_bs_read_int(bs, 3); + count = gf_bs_read_int(bs, 5); + + for (i=0; isize = gf_bs_read_u16(bs); + sl->data = (char *)malloc(sizeof(char) * sl->size); + gf_bs_read_data(bs, sl->data, sl->size); + gf_list_add(ptr->config->sequenceParameterSets, sl); + } + + count = gf_bs_read_u8(bs); + for (i=0; isize = gf_bs_read_u16(bs); + sl->data = (char *)malloc(sizeof(char) * sl->size); + gf_bs_read_data(bs, sl->data, sl->size); + gf_list_add(ptr->config->pictureParameterSets, sl); + } + return GF_OK; +} +GF_Box *avcc_New() +{ + GF_AVCConfigurationBox *tmp = (GF_AVCConfigurationBox *) malloc(sizeof(GF_MPEG4BitRateBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_AVCConfigurationBox)); + tmp->type = GF_ISOM_BOX_TYPE_AVCC; + return (GF_Box *)tmp; +} + +#ifndef GPAC_READ_ONLY +GF_Err avcc_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 i, count; + GF_Err e; + GF_AVCConfigurationBox *ptr = (GF_AVCConfigurationBox *) s; + if (!s) return GF_BAD_PARAM; + if (!ptr->config) return GF_OK; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_u8(bs, ptr->config->configurationVersion); + gf_bs_write_u8(bs, ptr->config->AVCProfileIndication); + gf_bs_write_u8(bs, ptr->config->profile_compatibility); + gf_bs_write_u8(bs, ptr->config->AVCLevelIndication); + gf_bs_write_int(bs, 0x3F, 6); + gf_bs_write_int(bs, ptr->config->nal_unit_size - 1, 2); + gf_bs_write_int(bs, 0x7, 3); + count = gf_list_count(ptr->config->sequenceParameterSets); + gf_bs_write_int(bs, count, 5); + for (i=0; iconfig->sequenceParameterSets, i); + gf_bs_write_u16(bs, sl->size); + gf_bs_write_data(bs, sl->data, sl->size); + } + + count = gf_list_count(ptr->config->pictureParameterSets); + gf_bs_write_u8(bs, count); + for (i=0; iconfig->pictureParameterSets, i); + gf_bs_write_u16(bs, sl->size); + gf_bs_write_data(bs, sl->data, sl->size); + } + return GF_OK; +} +GF_Err avcc_Size(GF_Box *s) +{ + GF_Err e; + u32 i, count; + GF_AVCConfigurationBox *ptr = (GF_AVCConfigurationBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + if (!ptr->config) { + ptr->size = 0; + return e; + } + ptr->size += 7; + count = gf_list_count(ptr->config->sequenceParameterSets); + for (i=0; isize += 2 + ((GF_AVCConfigSlot *)gf_list_get(ptr->config->sequenceParameterSets, i))->size; + count = gf_list_count(ptr->config->pictureParameterSets); + for (i=0; isize += 2 + ((GF_AVCConfigSlot *)gf_list_get(ptr->config->pictureParameterSets, i))->size; + return GF_OK; +} +#endif + + diff --git a/src/isomedia/box_code_3gpp.c b/src/isomedia/box_code_3gpp.c new file mode 100644 index 0000000..9394f89 --- /dev/null +++ b/src/isomedia/box_code_3gpp.c @@ -0,0 +1,1263 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +void gppa_del(GF_Box *s) +{ + GF_3GPPAudioSampleEntryBox *ptr = (GF_3GPPAudioSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->info) gf_isom_box_del((GF_Box *)ptr->info); + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + free(ptr); +} + + +GF_Err gppa_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPAudioSampleEntryBox *ptr = (GF_3GPPAudioSampleEntryBox *)s; + e = gf_isom_audio_sample_entry_read((GF_AudioSampleEntryBox*)s, bs); + if (e) return e; + e = gf_isom_parse_box((GF_Box **)&ptr->info, bs); + if (e) return e; + ptr->info->cfg.type = ptr->type; + return GF_OK; +} + +GF_Box *gppa_New(u32 type) +{ + GF_3GPPAudioSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_3GPPAudioSampleEntryBox); + if (tmp == NULL) return NULL; + gf_isom_audio_sample_entry_init((GF_AudioSampleEntryBox*)tmp); + tmp->type = type; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err gppa_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPAudioSampleEntryBox *ptr = (GF_3GPPAudioSampleEntryBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_isom_audio_sample_entry_write((GF_AudioSampleEntryBox*)s, bs); + return gf_isom_box_write((GF_Box *)ptr->info, bs); +} + +GF_Err gppa_Size(GF_Box *s) +{ + GF_Err e; + GF_3GPPAudioSampleEntryBox *ptr = (GF_3GPPAudioSampleEntryBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + gf_isom_audio_sample_entry_size((GF_AudioSampleEntryBox*)s); + e = gf_isom_box_size((GF_Box *)ptr->info); + if (e) return e; + ptr->size += ptr->info->size; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +GF_Box *gppv_New(u32 type) +{ + GF_3GPPVisualSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_3GPPVisualSampleEntryBox); + if (tmp == NULL) return NULL; + gf_isom_video_sample_entry_init((GF_VisualSampleEntryBox *)tmp); + tmp->type = type; + return (GF_Box *)tmp; +} +void gppv_del(GF_Box *s) +{ + GF_3GPPVisualSampleEntryBox *ptr = (GF_3GPPVisualSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->info) gf_isom_box_del((GF_Box *)ptr->info); + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + free(ptr); +} + +GF_Err gppv_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPVisualSampleEntryBox *ptr = (GF_3GPPVisualSampleEntryBox *)s; + e = gf_isom_video_sample_entry_read((GF_VisualSampleEntryBox *)ptr, bs); + if (e) return e; + /*FIXME - check for any other boxes...*/ + e = gf_isom_parse_box((GF_Box **)&ptr->info, bs); + return e; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err gppv_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPVisualSampleEntryBox *ptr = (GF_3GPPVisualSampleEntryBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_isom_video_sample_entry_write((GF_VisualSampleEntryBox *)s, bs); + e = gf_isom_box_write((GF_Box *)ptr->info, bs); + if (e) return e; + return GF_OK; +} + +GF_Err gppv_Size(GF_Box *s) +{ + GF_Err e; + GF_3GPPVisualSampleEntryBox *ptr = (GF_3GPPVisualSampleEntryBox*)s; + e = gf_isom_box_get_size(s); + if (e) return e; + gf_isom_video_sample_entry_size((GF_VisualSampleEntryBox *)s); + e = gf_isom_box_size((GF_Box *)ptr->info); + if (e) return e; + ptr->size += ptr->info->size; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +GF_Box *gppc_New(u32 type) +{ + GF_3GPPConfigBox *tmp = (GF_3GPPConfigBox *) malloc(sizeof(GF_3GPPConfigBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_3GPPConfigBox)); + tmp->type = type; + return (GF_Box *)tmp; +} + +void gppc_del(GF_Box *s) +{ + GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s; + if (ptr == NULL) return; + free(ptr); +} + + +GF_Err gppc_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + memset(&ptr->cfg, 0, sizeof(GF_3GPConfig)); + + ptr->cfg.vendor = gf_bs_read_u32(bs); + ptr->cfg.decoder_version = gf_bs_read_u8(bs); + + switch (ptr->type) { + case GF_ISOM_BOX_TYPE_D263: + ptr->cfg.H263_level = gf_bs_read_u8(bs); + ptr->cfg.H263_profile = gf_bs_read_u8(bs); + break; + case GF_ISOM_BOX_TYPE_DAMR: + ptr->cfg.AMR_mode_set = gf_bs_read_u16(bs); + ptr->cfg.AMR_mode_change_period = gf_bs_read_u8(bs); + ptr->cfg.frames_per_sample = gf_bs_read_u8(bs); + break; + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + ptr->cfg.frames_per_sample = gf_bs_read_u8(bs); + break; + } + return GF_OK; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err gppc_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_u32(bs, ptr->cfg.vendor); + gf_bs_write_u8(bs, ptr->cfg.decoder_version); + switch (ptr->cfg.type) { + case GF_ISOM_SUBTYPE_3GP_H263: + gf_bs_write_u8(bs, ptr->cfg.H263_level); + gf_bs_write_u8(bs, ptr->cfg.H263_profile); + break; + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + gf_bs_write_u16(bs, ptr->cfg.AMR_mode_set); + gf_bs_write_u8(bs, ptr->cfg.AMR_mode_change_period); + gf_bs_write_u8(bs, ptr->cfg.frames_per_sample); + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + gf_bs_write_u8(bs, ptr->cfg.frames_per_sample); + break; + } + return GF_OK; +} + +GF_Err gppc_Size(GF_Box *s) +{ + GF_Err e; + GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 5; + switch (ptr->cfg.type) { + case GF_ISOM_SUBTYPE_3GP_H263: + s->size += 2; + break; + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + s->size += 4; + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + s->size += 1; + break; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +GF_Box *ftab_New() +{ + GF_FontTableBox *tmp; + GF_SAFEALLOC(tmp, GF_FontTableBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_FTAB; + return (GF_Box *) tmp; +} +void ftab_del(GF_Box *s) +{ + GF_FontTableBox *ptr = (GF_FontTableBox *)s; + if (ptr->fonts) { + u32 i; + for (i=0; ientry_count; i++) + if (ptr->fonts[i].fontName) free(ptr->fonts[i].fontName); + free(ptr->fonts); + } + free(ptr); +} +GF_Err ftab_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_FontTableBox *ptr = (GF_FontTableBox *)s; + ptr->entry_count = gf_bs_read_u16(bs); + ptr->fonts = (GF_FontRecord *) malloc(sizeof(GF_FontRecord)*ptr->entry_count); + for (i=0; ientry_count; i++) { + u32 len; + ptr->fonts[i].fontID = gf_bs_read_u16(bs); + len = gf_bs_read_u8(bs); + if (len) { + ptr->fonts[i].fontName = (char *)malloc(sizeof(char)*(len+1)); + gf_bs_read_data(bs, ptr->fonts[i].fontName, len); + ptr->fonts[i].fontName[len] = 0; + } + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err ftab_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_FontTableBox *ptr = (GF_FontTableBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->entry_count); + for (i=0; ientry_count; i++) { + gf_bs_write_u16(bs, ptr->fonts[i].fontID); + if (ptr->fonts[i].fontName) { + u32 len = strlen(ptr->fonts[i].fontName); + gf_bs_write_u8(bs, len); + gf_bs_write_data(bs, ptr->fonts[i].fontName, len); + } else { + gf_bs_write_u8(bs, 0); + } + } + return GF_OK; +} +GF_Err ftab_Size(GF_Box *s) +{ + u32 i; + GF_FontTableBox *ptr = (GF_FontTableBox *)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 2; + for (i=0; ientry_count; i++) { + s->size += 3; + if (ptr->fonts[i].fontName) s->size += strlen(ptr->fonts[i].fontName); + } + return GF_OK; +} + +#endif + + + + +GF_Box *tx3g_New() +{ + GF_TextSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_TextSampleEntryBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TX3G; + return (GF_Box *) tmp; +} + +void tx3g_del(GF_Box *s) +{ + GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s; + if (ptr->font_table) gf_isom_box_del((GF_Box *)ptr->font_table); + free(ptr); +} + +static u32 gpp_read_rgba(GF_BitStream *bs) +{ + u8 r, g, b, a; + u32 col; + r = gf_bs_read_u8(bs); + g = gf_bs_read_u8(bs); + b = gf_bs_read_u8(bs); + a = gf_bs_read_u8(bs); + col = a; col<<=8; + col |= r; col<<=8; + col |= g; col<<=8; + col |= b; + return col; +} + +#define GPP_BOX_SIZE 8 +static void gpp_read_box(GF_BitStream *bs, GF_BoxRecord *rec) +{ + rec->top = gf_bs_read_u16(bs); + rec->left = gf_bs_read_u16(bs); + rec->bottom = gf_bs_read_u16(bs); + rec->right = gf_bs_read_u16(bs); +} + +#define GPP_STYLE_SIZE 12 +static void gpp_read_style(GF_BitStream *bs, GF_StyleRecord *rec) +{ + rec->startCharOffset = gf_bs_read_u16(bs); + rec->endCharOffset = gf_bs_read_u16(bs); + rec->fontID = gf_bs_read_u16(bs); + rec->style_flags = gf_bs_read_u8(bs); + rec->font_size = gf_bs_read_u8(bs); + rec->text_color = gpp_read_rgba(bs); +} + +GF_Err tx3g_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_Box *a; + GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s; + + if (ptr->size < 18 + GPP_BOX_SIZE + GPP_STYLE_SIZE) return GF_ISOM_INVALID_FILE; + + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->displayFlags = gf_bs_read_u32(bs); + ptr->horizontal_justification = gf_bs_read_u8(bs); + ptr->vertical_justification = gf_bs_read_u8(bs); + ptr->back_color = gpp_read_rgba(bs); + gpp_read_box(bs, &ptr->default_box); + gpp_read_style(bs, &ptr->default_style); + ptr->size -= 18 + GPP_BOX_SIZE + GPP_STYLE_SIZE; + + while (ptr->size) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size -= a->size; + if (a->type==GF_ISOM_BOX_TYPE_FTAB) { + if (ptr->font_table) gf_isom_box_del((GF_Box *) ptr->font_table); + ptr->font_table = (GF_FontTableBox *)a; + } else { + gf_isom_box_del(a); + } + } + return GF_OK; +} + + +void gpp_write_rgba(GF_BitStream *bs, u32 col) +{ + gf_bs_write_u8(bs, (col>>16) & 0xFF); + gf_bs_write_u8(bs, (col>>8) & 0xFF); + gf_bs_write_u8(bs, (col) & 0xFF); + gf_bs_write_u8(bs, (col>>24) & 0xFF); +} + +void gpp_write_box(GF_BitStream *bs, GF_BoxRecord *rec) +{ + gf_bs_write_u16(bs, rec->top); + gf_bs_write_u16(bs, rec->left); + gf_bs_write_u16(bs, rec->bottom); + gf_bs_write_u16(bs, rec->right); +} + +#define GPP_STYLE_SIZE 12 +void gpp_write_style(GF_BitStream *bs, GF_StyleRecord *rec) +{ + gf_bs_write_u16(bs, rec->startCharOffset); + gf_bs_write_u16(bs, rec->endCharOffset); + gf_bs_write_u16(bs, rec->fontID); + gf_bs_write_u8(bs, rec->style_flags); + gf_bs_write_u8(bs, rec->font_size); + gpp_write_rgba(bs, rec->text_color); +} + +#ifndef GPAC_READ_ONLY + +GF_Err tx3g_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + gf_bs_write_u32(bs, ptr->displayFlags); + gf_bs_write_u8(bs, ptr->horizontal_justification); + gf_bs_write_u8(bs, ptr->vertical_justification); + gpp_write_rgba(bs, ptr->back_color); + gpp_write_box(bs, &ptr->default_box); + gpp_write_style(bs, &ptr->default_style); + return gf_isom_box_write((GF_Box *) ptr->font_table, bs); +} + +GF_Err tx3g_Size(GF_Box *s) +{ + GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + /*base + this + box + style*/ + s->size += 18 + GPP_BOX_SIZE + GPP_STYLE_SIZE; + if (ptr->font_table) { + e = gf_isom_box_size((GF_Box *) ptr->font_table); + if (e) return e; + s->size += ptr->font_table->size; + } + return GF_OK; +} + +#endif + +GF_Box *styl_New() +{ + GF_TextStyleBox *tmp; + GF_SAFEALLOC(tmp, GF_TextStyleBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_STYL; + return (GF_Box *) tmp; +} + +void styl_del(GF_Box *s) +{ + GF_TextStyleBox*ptr = (GF_TextStyleBox*)s; + if (ptr->styles) free(ptr->styles); + free(ptr); +} + +GF_Err styl_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_TextStyleBox*ptr = (GF_TextStyleBox*)s; + ptr->entry_count = gf_bs_read_u16(bs); + if (ptr->entry_count) { + ptr->styles = (GF_StyleRecord*)malloc(sizeof(GF_StyleRecord)*ptr->entry_count); + for (i=0; ientry_count; i++) { + gpp_read_style(bs, &ptr->styles[i]); + } + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err styl_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TextStyleBox*ptr = (GF_TextStyleBox*)s; + e = gf_isom_box_write_header(s, bs); + + gf_bs_write_u16(bs, ptr->entry_count); + for (i=0; ientry_count; i++) gpp_write_style(bs, &ptr->styles[i]); + return GF_OK; +} + +GF_Err styl_Size(GF_Box *s) +{ + GF_TextStyleBox*ptr = (GF_TextStyleBox*)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 2 + ptr->entry_count * GPP_STYLE_SIZE; + return GF_OK; +} + +#endif + +GF_Box *hlit_New() +{ + GF_TextHighlightBox *tmp; + GF_SAFEALLOC(tmp, GF_TextHighlightBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_HLIT; + return (GF_Box *) tmp; +} + +void hlit_del(GF_Box *s) +{ + free(s); +} + +GF_Err hlit_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextHighlightBox *ptr = (GF_TextHighlightBox *)s; + ptr->startcharoffset = gf_bs_read_u16(bs); + ptr->endcharoffset = gf_bs_read_u16(bs); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err hlit_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextHighlightBox *ptr = (GF_TextHighlightBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->startcharoffset); + gf_bs_write_u16(bs, ptr->endcharoffset); + return GF_OK; +} + +GF_Err hlit_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} + +#endif + +GF_Box *hclr_New() +{ + GF_TextHighlightColorBox*tmp; + GF_SAFEALLOC(tmp, GF_TextHighlightColorBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_HCLR; + return (GF_Box *) tmp; +} + +void hclr_del(GF_Box *s) +{ + free(s); +} + +GF_Err hclr_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextHighlightColorBox*ptr = (GF_TextHighlightColorBox*)s; + ptr->hil_color = gpp_read_rgba(bs); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err hclr_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextHighlightColorBox*ptr = (GF_TextHighlightColorBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gpp_write_rgba(bs, ptr->hil_color); + return GF_OK; +} + +GF_Err hclr_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} + +#endif + +GF_Box *krok_New() +{ + GF_TextKaraokeBox*tmp; + GF_SAFEALLOC(tmp, GF_TextKaraokeBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_KROK; + return (GF_Box *) tmp; +} + +void krok_del(GF_Box *s) +{ + GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s; + if (ptr->records) free(ptr->records); + free(ptr); +} + +GF_Err krok_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s; + + ptr->highlight_starttime = gf_bs_read_u32(bs); + ptr->nb_entries = gf_bs_read_u16(bs); + if (ptr->nb_entries) { + u32 i; + ptr->records = (KaraokeRecord*)malloc(sizeof(KaraokeRecord)*ptr->nb_entries); + for (i=0; inb_entries; i++) { + ptr->records[i].highlight_endtime = gf_bs_read_u32(bs); + ptr->records[i].start_charoffset = gf_bs_read_u16(bs); + ptr->records[i].end_charoffset = gf_bs_read_u16(bs); + } + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err krok_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_u32(bs, ptr->highlight_starttime); + gf_bs_write_u16(bs, ptr->nb_entries); + for (i=0; inb_entries; i++) { + gf_bs_write_u32(bs, ptr->records[i].highlight_endtime); + gf_bs_write_u16(bs, ptr->records[i].start_charoffset); + gf_bs_write_u16(bs, ptr->records[i].end_charoffset); + } + return GF_OK; +} + +GF_Err krok_Size(GF_Box *s) +{ + GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 6 * 8*ptr->nb_entries; + return GF_OK; +} + +#endif + +GF_Box *dlay_New() +{ + GF_TextScrollDelayBox*tmp; + GF_SAFEALLOC(tmp, GF_TextScrollDelayBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DLAY; + return (GF_Box *) tmp; +} + +void dlay_del(GF_Box *s) +{ + free(s); +} + +GF_Err dlay_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextScrollDelayBox*ptr = (GF_TextScrollDelayBox*)s; + ptr->scroll_delay = gf_bs_read_u32(bs); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err dlay_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextScrollDelayBox*ptr = (GF_TextScrollDelayBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->scroll_delay); + return GF_OK; +} + +GF_Err dlay_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} + +#endif + +GF_Box *href_New() +{ + GF_TextHyperTextBox*tmp; + GF_SAFEALLOC(tmp, GF_TextHyperTextBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_HREF; + return (GF_Box *) tmp; +} + +void href_del(GF_Box *s) +{ + GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s; + if (ptr->URL) free(ptr->URL); + if (ptr->URL_hint) free(ptr->URL_hint); + free(ptr); +} + +GF_Err href_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 len; + GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s; + ptr->startcharoffset = gf_bs_read_u16(bs); + ptr->endcharoffset = gf_bs_read_u16(bs); + len = gf_bs_read_u8(bs); + if (len) { + ptr->URL = (char *) malloc(sizeof(char) * (len+1)); + gf_bs_read_data(bs, ptr->URL, len); + ptr->URL[len] = 0; + } + len = gf_bs_read_u8(bs); + if (len) { + ptr->URL_hint = (char *) malloc(sizeof(char) * (len+1)); + gf_bs_read_data(bs, ptr->URL_hint, len); + ptr->URL_hint[len]= 0; + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err href_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 len; + GF_Err e; + GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_u16(bs, ptr->startcharoffset); + gf_bs_write_u16(bs, ptr->endcharoffset); + if (ptr->URL) { + len = strlen(ptr->URL); + gf_bs_write_u8(bs, len); + gf_bs_write_data(bs, ptr->URL, len); + } else { + gf_bs_write_u8(bs, 0); + } + if (ptr->URL_hint) { + len = strlen(ptr->URL_hint); + gf_bs_write_u8(bs, len); + gf_bs_write_data(bs, ptr->URL_hint, len); + } else { + gf_bs_write_u8(bs, 0); + } + return GF_OK; +} + +GF_Err href_Size(GF_Box *s) +{ + GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 6; + if (ptr->URL) s->size += strlen(ptr->URL); + if (ptr->URL_hint) s->size += strlen(ptr->URL_hint); + return GF_OK; +} + +#endif + + +GF_Box *tbox_New() +{ + GF_TextBoxBox*tmp; + GF_SAFEALLOC(tmp, GF_TextBoxBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TBOX; + return (GF_Box *) tmp; +} + +void tbox_del(GF_Box *s) +{ + free(s); +} + +GF_Err tbox_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextBoxBox*ptr = (GF_TextBoxBox*)s; + gpp_read_box(bs, &ptr->box); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err tbox_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextBoxBox*ptr = (GF_TextBoxBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gpp_write_box(bs, &ptr->box); + return GF_OK; +} + +GF_Err tbox_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} + +#endif + + +GF_Box *blnk_New() +{ + GF_TextBlinkBox*tmp; + GF_SAFEALLOC(tmp, GF_TextBlinkBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_BLNK; + return (GF_Box *) tmp; +} + +void blnk_del(GF_Box *s) +{ + free(s); +} + +GF_Err blnk_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextBlinkBox*ptr = (GF_TextBlinkBox*)s; + ptr->startcharoffset = gf_bs_read_u16(bs); + ptr->endcharoffset = gf_bs_read_u16(bs); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err blnk_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextBlinkBox*ptr = (GF_TextBlinkBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->startcharoffset); + gf_bs_write_u16(bs, ptr->endcharoffset); + return GF_OK; +} + +GF_Err blnk_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} + +#endif + +GF_Box *twrp_New() +{ + GF_TextWrapBox*tmp; + GF_SAFEALLOC(tmp, GF_TextWrapBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TWRP; + return (GF_Box *) tmp; +} + +void twrp_del(GF_Box *s) +{ + free(s); +} + +GF_Err twrp_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextWrapBox*ptr = (GF_TextWrapBox*)s; + ptr->wrap_flag = gf_bs_read_u8(bs); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err twrp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextWrapBox*ptr = (GF_TextWrapBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u8(bs, ptr->wrap_flag); + return GF_OK; +} +GF_Err twrp_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 1; + return GF_OK; +} + +#endif + +void tsel_del(GF_Box *s) +{ + GF_TrackSelectionBox *ptr; + ptr = (GF_TrackSelectionBox *) s; + if (ptr == NULL) return; + if (ptr->attributeList) free(ptr->attributeList); + free(ptr); +} + +GF_Err tsel_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TrackSelectionBox *ptr = (GF_TrackSelectionBox *) s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->switchGroup = gf_bs_read_u32(bs); + ptr->size -= 4; + if (ptr->size % 4) return GF_ISOM_INVALID_FILE; + ptr->attributeListCount = (u32)ptr->size/4; + ptr->attributeList = malloc(ptr->attributeListCount*sizeof(u32)); + if (ptr->attributeList == NULL) return GF_OUT_OF_MEM; + + for (i=0; i< ptr->attributeListCount; i++) { + ptr->attributeList[i] = gf_bs_read_u32(bs); + } + return GF_OK; +} + +GF_Box *tsel_New() +{ + GF_TrackSelectionBox *tmp; + + tmp = (GF_TrackSelectionBox *) malloc(sizeof(GF_TrackSelectionBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackSelectionBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_TSEL; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err tsel_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TrackSelectionBox *ptr = (GF_TrackSelectionBox *) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs,ptr->switchGroup); + + for (i = 0; i < ptr->attributeListCount; i++ ) { + gf_bs_write_u32(bs, ptr->attributeList[i]); + } + + return GF_OK; +} + +GF_Err tsel_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackSelectionBox *ptr = (GF_TrackSelectionBox *) s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (4*ptr->attributeListCount); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +GF_Box *dimC_New() +{ + GF_DIMSSceneConfigBox *tmp; + + GF_SAFEALLOC(tmp, GF_DIMSSceneConfigBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_DIMC; + return (GF_Box *)tmp; +} +void dimC_del(GF_Box *s) +{ + GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s; + if (p->contentEncoding) free(p->contentEncoding); + if (p->textEncoding) free(p->textEncoding); + free(p); +} + +GF_Err dimC_Read(GF_Box *s, GF_BitStream *bs) +{ + char str[1024]; + u32 i; + GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s; + GF_Err e = gf_isom_full_box_read(s, bs); + if (e) return e; + + p->profile = gf_bs_read_u8(bs); + p->level = gf_bs_read_u8(bs); + p->pathComponents = gf_bs_read_int(bs, 4); + p->fullRequestHost = gf_bs_read_int(bs, 1); + p->streamType = gf_bs_read_int(bs, 1); + p->containsRedundant = gf_bs_read_int(bs, 2); + s->size -= 3; + + i=0; + str[0]=0; + while (1) { + str[i] = gf_bs_read_u8(bs); + if (!str[i]) break; + i++; + } + if (s->size < i) return GF_ISOM_INVALID_FILE; + s->size -= i; + p->textEncoding = strdup(str); + + i=0; + str[0]=0; + while (1) { + str[i] = gf_bs_read_u8(bs); + if (!str[i]) break; + i++; + } + if (s->size < i) return GF_ISOM_INVALID_FILE; + s->size -= i; + p->contentEncoding = strdup(str); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err dimC_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s; + GF_Err e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u8(bs, p->profile); + gf_bs_write_u8(bs, p->level); + gf_bs_write_int(bs, p->pathComponents, 4); + gf_bs_write_int(bs, p->fullRequestHost, 1); + gf_bs_write_int(bs, p->streamType, 1); + gf_bs_write_int(bs, p->containsRedundant, 2); + gf_bs_write_data(bs, p->textEncoding, strlen(p->textEncoding)+1); + gf_bs_write_data(bs, p->contentEncoding, strlen(p->contentEncoding)+1); + return GF_OK; +} +GF_Err dimC_Size(GF_Box *s) +{ + GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s; + GF_Err e = gf_isom_full_box_get_size(s); + if (e) return e; + s->size += 3 + 1 + strlen(p->textEncoding) + 1 + strlen(p->contentEncoding); + return GF_OK; +} +#endif + + + +GF_Box *diST_New() +{ + GF_DIMSScriptTypesBox *tmp; + + GF_SAFEALLOC(tmp, GF_DIMSScriptTypesBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DIST; + return (GF_Box *)tmp; +} +void diST_del(GF_Box *s) +{ + GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s; + if (p->content_script_types) free(p->content_script_types); + free(p); +} + +GF_Err diST_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + char str[1024]; + GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s; + + i=0; + str[0]=0; + while (1) { + str[i] = gf_bs_read_u8(bs); + if (!str[i]) break; + i++; + } + if (s->size < i) return GF_ISOM_INVALID_FILE; + s->size -= i; + p->content_script_types = strdup(str); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err diST_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s; + GF_Err e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (p->content_script_types) + gf_bs_write_data(bs, p->content_script_types, strlen(p->content_script_types)+1); + else + gf_bs_write_u8(bs, 0); + return GF_OK; +} +GF_Err diST_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s; + if (e) return e; + s->size += p->content_script_types ? (strlen(p->content_script_types)+1) : 1; + return GF_OK; +} +#endif + + +GF_Box *dims_New() +{ + GF_DIMSSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_DIMSSampleEntryBox); + tmp->type = GF_ISOM_BOX_TYPE_DIMS; + return (GF_Box*)tmp; +} +void dims_del(GF_Box *s) +{ + GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s; + if (p->config) gf_isom_box_del((GF_Box *)p->config); + if (p->bitrate ) gf_isom_box_del((GF_Box *)p->bitrate); + if (p->protection_info) gf_isom_box_del((GF_Box *)p->protection_info); + if (p->scripts) gf_isom_box_del((GF_Box *)p->scripts); + free(p); +} + +static GF_Err dims_AddBox(GF_Box *s, GF_Box *a) +{ + GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_DIMC: + if (p->config) return GF_ISOM_INVALID_FILE; + p->config = (GF_DIMSSceneConfigBox*)a; + break; + case GF_ISOM_BOX_TYPE_DIST: + if (p->scripts) return GF_ISOM_INVALID_FILE; + p->scripts = (GF_DIMSScriptTypesBox*)a; + break; + case GF_ISOM_BOX_TYPE_BTRT: + if (p->bitrate) return GF_ISOM_INVALID_FILE; + p->bitrate = (GF_MPEG4BitRateBox*)a; + break; + case GF_ISOM_BOX_TYPE_SINF: + if (p->protection_info) return GF_ISOM_INVALID_FILE; + p->protection_info = (GF_ProtectionInfoBox*)a; + break; + default: + gf_isom_box_del(a); + break; + } + return GF_OK; +} +GF_Err dims_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s; + gf_bs_read_data(bs, p->reserved, 6); + p->dataReferenceIndex = gf_bs_read_u16(bs); + p->size -= 8; + return gf_isom_read_box_list(s, bs, dims_AddBox); +} + +#ifndef GPAC_READ_ONLY +GF_Err dims_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s; + GF_Err e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_data(bs, p->reserved, 6); + gf_bs_write_u16(bs, p->dataReferenceIndex); + if (p->config) { + e = gf_isom_box_write((GF_Box *)p->config, bs); + if (e) return e; + } + if (p->scripts) { + e = gf_isom_box_write((GF_Box *)p->scripts, bs); + if (e) return e; + } + if (p->bitrate) { + e = gf_isom_box_write((GF_Box *)p->bitrate, bs); + if (e) return e; + } + if (p->protection_info) { + e = gf_isom_box_write((GF_Box *)p->protection_info, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err dims_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s; + if (e) return e; + s->size += 8; + + if (p->config) { + e = gf_isom_box_size((GF_Box *) p->config); + if (e) return e; + p->size += p->config->size; + } + if (p->protection_info) { + e = gf_isom_box_size((GF_Box *) p->protection_info); + if (e) return e; + p->size += p->protection_info->size; + } + if (p->bitrate) { + e = gf_isom_box_size((GF_Box *) p->bitrate); + if (e) return e; + p->size += p->bitrate->size; + } + if (p->scripts) { + e = gf_isom_box_size((GF_Box *) p->scripts); + if (e) return e; + p->size += p->scripts->size; + } + return GF_OK; +} +#endif diff --git a/src/isomedia/box_code_apple.c b/src/isomedia/box_code_apple.c new file mode 100644 index 0000000..550b621 --- /dev/null +++ b/src/isomedia/box_code_apple.c @@ -0,0 +1,324 @@ +/* +Author: Andrew Voznytsa + +Project: GPAC - Multimedia Framework C SDK +Module: ISO Media File Format sub-project + +Copyright: (c) 2006, Andrew Voznytsa +License: see License.txt in the top level directory. +*/ + +#include + +void ilst_del(GF_Box *s) +{ + GF_ItemListBox *ptr = (GF_ItemListBox *)s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->tags); + free(ptr); +} + +GF_Err ilst_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 sub_type; + GF_Box *a; + GF_ItemListBox *ptr = (GF_ItemListBox *)s; + while (ptr->size) { + /*if no ilst type coded, break*/ + sub_type = gf_bs_peek_bits(bs, 32, 0); + if (sub_type) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size -= a->size; + gf_list_add(ptr->tags, a); + } else { + gf_bs_read_u32(bs); + ptr->size -= 4; + } + } + return GF_OK; +} + +GF_Box *ilst_New() +{ + GF_ItemListBox *tmp = (GF_ItemListBox *) malloc(sizeof(GF_ItemListBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ItemListBox)); + tmp->type = GF_ISOM_BOX_TYPE_ILST; + tmp->tags = gf_list_new(); + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err ilst_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ItemListBox *ptr = (GF_ItemListBox *)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + return gf_isom_box_array_write(s, ptr->tags, bs); +} + + +GF_Err ilst_Size(GF_Box *s) +{ + GF_Err e; + GF_ItemListBox *ptr = (GF_ItemListBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + + return gf_isom_box_array_size(s, ptr->tags); +} + +#endif //GPAC_READ_ONLY + +void ListItem_del(GF_Box *s) +{ + GF_ListItemBox *ptr = (GF_ListItemBox *) s; + if (ptr == NULL) return; + if (ptr->data != NULL) { + if (ptr->data->data) free(ptr->data->data); + free(ptr->data); + } + free(ptr); +} + +GF_Err ListItem_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + u32 sub_type; + GF_Box *a = NULL; + GF_ListItemBox *ptr = (GF_ListItemBox *)s; + + /*iTunes way: there's a data atom containing the data*/ + sub_type = gf_bs_peek_bits(bs, 32, 4); + if (sub_type == GF_ISOM_BOX_TYPE_DATA ) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size -= a->size; + + if (a && ptr->data) gf_isom_box_del((GF_Box *) ptr->data); + ptr->data = (GF_DataBox *)a; + } + /*QT way*/ + else { + ptr->data->type = 0; + ptr->data->dataSize = gf_bs_read_u16(bs); + gf_bs_read_u16(bs); + ptr->data->data = (char *) malloc(sizeof(char)*(ptr->data->dataSize + 1)); + gf_bs_read_data(bs, ptr->data->data, ptr->data->dataSize); + ptr->data->data[ptr->data->dataSize] = 0; + ptr->size -= ptr->data->dataSize; + } + return GF_OK; +} + +GF_Box *ListItem_New(u32 type) +{ + GF_ListItemBox *tmp; + + tmp = (GF_ListItemBox *) malloc(sizeof(GF_ListItemBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ListItemBox)); + + tmp->type = type; + + tmp->data = (GF_DataBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_DATA); + + if (tmp->data == NULL){ + free(tmp); + return NULL; + } + + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err ListItem_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ListItemBox *ptr = (GF_ListItemBox *) s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + /*iTune way*/ + if (ptr->data->type) return gf_isom_box_write((GF_Box* )ptr->data, bs); + /*QT way*/ + gf_bs_write_u16(bs, ptr->data->dataSize); + gf_bs_write_u16(bs, 0); + gf_bs_write_data(bs, ptr->data->data, ptr->data->dataSize); + return GF_OK; +} + +GF_Err ListItem_Size(GF_Box *s) +{ + GF_Err e; + GF_ListItemBox *ptr = (GF_ListItemBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + + /*iTune way*/ + if (ptr->data && ptr->data->type) { + e = gf_isom_box_size((GF_Box *)ptr->data); + if (e) return e; + ptr->size += ptr->data->size; + } + /*QT way*/ + else { + ptr->size += ptr->data->dataSize + 4; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void data_del(GF_Box *s) +{ + GF_DataBox *ptr = (GF_DataBox *) s; + if (ptr == NULL) return; + if (ptr->data) + free(ptr->data); + free(ptr); + +} + +GF_Err data_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + GF_DataBox *ptr = (GF_DataBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->reserved = gf_bs_read_int(bs, 32); + ptr->size -= 4; + if (ptr->size) { + ptr->dataSize = (u32) ptr->size; + ptr->data = (char*)malloc(ptr->dataSize * sizeof(ptr->data[0]) + 1); + if (ptr->data == NULL) return GF_OUT_OF_MEM; + ptr->data[ptr->dataSize] = 0; + gf_bs_read_data(bs, ptr->data, ptr->dataSize); + } + + return GF_OK; +} + +GF_Box *data_New() +{ + GF_DataBox *tmp; + + tmp = (GF_DataBox *) malloc(sizeof(GF_DataBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DataBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_DATA; + + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err data_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DataBox *ptr = (GF_DataBox *) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_int(bs, ptr->reserved, 32); + if(ptr->data != NULL && ptr->dataSize > 0){ + gf_bs_write_data(bs, ptr->data, ptr->dataSize); + } + return GF_OK; +} + +GF_Err data_Size(GF_Box *s) +{ + GF_Err e; + GF_DataBox *ptr = (GF_DataBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + if(ptr->data != NULL && ptr->dataSize > 0){ + ptr->size += ptr->dataSize; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +GF_MetaBox *gf_isom_apple_get_meta_extensions(GF_ISOFile *mov) +{ + u32 i; + GF_MetaBox *meta; + GF_UserDataMap *map; + + if (!mov || !mov->moov) return NULL; + + if (!mov->moov->udta) return NULL; + map = udta_getEntry(mov->moov->udta, GF_ISOM_BOX_TYPE_META, NULL); + if (!map) return NULL; + + for(i = 0; i < gf_list_count(map->boxList); i++){ + meta = (GF_MetaBox*)gf_list_get(map->boxList, i); + + if(meta != NULL && meta->handler != NULL && meta->handler->handlerType == GF_ISOM_HANDLER_TYPE_MDIR) return meta; + } + + return NULL; +} + +#ifndef GPAC_READ_ONLY +GF_MetaBox *gf_isom_apple_create_meta_extensions(GF_ISOFile *mov) +{ + GF_Err e; + u32 i; + GF_MetaBox *meta; + GF_UserDataMap *map; + + if (!mov || !mov->moov) return NULL; + + if (!mov->moov->udta){ + e = moov_AddBox((GF_Box*)mov->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return NULL; + } + + map = udta_getEntry(mov->moov->udta, GF_ISOM_BOX_TYPE_META, NULL); + if (map){ + for(i = 0; i < gf_list_count(map->boxList); i++){ + meta = (GF_MetaBox*)gf_list_get(map->boxList, i); + + if(meta != NULL && meta->handler != NULL && meta->handler->handlerType == GF_ISOM_HANDLER_TYPE_MDIR) return meta; + } + } + + meta = (GF_MetaBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_META); + + if(meta != NULL){ + meta->handler = (GF_HandlerBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_HDLR); + if(meta->handler == NULL){ + gf_isom_box_del((GF_Box *)meta); + return NULL; + } + meta->handler->handlerType = GF_ISOM_HANDLER_TYPE_MDIR; + gf_list_add(meta->other_boxes, gf_isom_box_new(GF_ISOM_BOX_TYPE_ILST)); + udta_AddBox(mov->moov->udta, (GF_Box *)meta); + } + + return meta; +} +#endif + diff --git a/src/isomedia/box_code_base.c b/src/isomedia/box_code_base.c new file mode 100644 index 0000000..236e213 --- /dev/null +++ b/src/isomedia/box_code_base.c @@ -0,0 +1,7419 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +void co64_del(GF_Box *s) +{ + GF_ChunkLargeOffsetBox *ptr; + ptr = (GF_ChunkLargeOffsetBox *) s; + if (ptr == NULL) return; + if (ptr->offsets) free(ptr->offsets); + free(ptr); +} + +GF_Err co64_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + u32 entries; + GF_ChunkLargeOffsetBox *ptr = (GF_ChunkLargeOffsetBox *) s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->nb_entries = gf_bs_read_u32(bs); + ptr->offsets = (u64 *) malloc(ptr->nb_entries * sizeof(u64) ); + if (ptr->offsets == NULL) return GF_OUT_OF_MEM; + ptr->alloc_size = ptr->nb_entries; + for (entries = 0; entries < ptr->nb_entries; entries++) { + ptr->offsets[entries] = gf_bs_read_u64(bs); + } + return GF_OK; +} + +GF_Box *co64_New() +{ + GF_ChunkLargeOffsetBox *tmp; + + tmp = (GF_ChunkLargeOffsetBox *) malloc(sizeof(GF_ChunkLargeOffsetBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ChunkLargeOffsetBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_CO64; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err co64_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_ChunkLargeOffsetBox *ptr = (GF_ChunkLargeOffsetBox *) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i = 0; i < ptr->nb_entries; i++ ) { + gf_bs_write_u64(bs, ptr->offsets[i]); + } + return GF_OK; +} + +GF_Err co64_Size(GF_Box *s) +{ + GF_Err e; + GF_ChunkLargeOffsetBox *ptr = (GF_ChunkLargeOffsetBox *) s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (8 * ptr->nb_entries); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void cprt_del(GF_Box *s) +{ + GF_CopyrightBox *ptr = (GF_CopyrightBox *) s; + if (ptr == NULL) return; + if (ptr->notice) + free(ptr->notice); + free(ptr); +} + + +GF_Box *chpl_New() +{ + GF_ChapterListBox *tmp; + + tmp = (GF_ChapterListBox *) malloc(sizeof(GF_ChapterListBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_CopyrightBox)); + tmp->list = gf_list_new(); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_CHPL; + tmp->version = 1; + return (GF_Box *)tmp; +} + +void chpl_del(GF_Box *s) +{ + GF_ChapterListBox *ptr = (GF_ChapterListBox *) s; + if (ptr == NULL) return; + while (gf_list_count(ptr->list)) { + GF_ChapterEntry *ce = (GF_ChapterEntry *)gf_list_get(ptr->list, 0); + if (ce->name) free(ce->name); + free(ce); + gf_list_rem(ptr->list, 0); + } + gf_list_del(ptr->list); + free(ptr); +} + +/*this is using chpl format according to some NeroRecode samples*/ +GF_Err chpl_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + GF_ChapterEntry *ce; + u32 nb_chaps, len, i, count; + GF_ChapterListBox *ptr = (GF_ChapterListBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + /*reserved or ???*/ + gf_bs_read_u32(bs); + nb_chaps = gf_bs_read_u8(bs); + + count = 0; + while (nb_chaps) { + GF_SAFEALLOC(ce, GF_ChapterEntry); + ce->start_time = gf_bs_read_u64(bs); + len = gf_bs_read_u8(bs); + if (len) { + ce->name = (char *)malloc(sizeof(char)*(len+1)); + gf_bs_read_data(bs, ce->name, len); + ce->name[len] = 0; + } else { + ce->name = strdup(""); + } + + for (i=0;ilist, i); + if (ace->start_time >= ce->start_time) { + gf_list_insert(ptr->list, ce, i); + ce = NULL; + break; + } + } + if (ce) gf_list_add(ptr->list, ce); + count++; + nb_chaps--; + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY + +GF_Err chpl_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 count, i; + GF_ChapterListBox *ptr = (GF_ChapterListBox *) s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + count = gf_list_count(ptr->list); + gf_bs_write_u32(bs, 0); + gf_bs_write_u8(bs, count); + for (i=0; ilist, i); + gf_bs_write_u64(bs, ce->start_time); + if (ce->name) { + len = strlen(ce->name); if (len>255) len = 255; + gf_bs_write_u8(bs, len); + gf_bs_write_data(bs, ce->name, len); + } else { + gf_bs_write_u8(bs, 0); + } + } + return GF_OK; +} + +GF_Err chpl_Size(GF_Box *s) +{ + GF_Err e; + u32 count, i; + GF_ChapterListBox *ptr = (GF_ChapterListBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 5; + + count = gf_list_count(ptr->list); + for (i=0; ilist, i); + ptr->size += 9; /*64bit time stamp + 8bit str len*/ + if (ce->name) ptr->size += strlen(ce->name); + } + return GF_OK; +} + +#endif + + +GF_Err cprt_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + GF_CopyrightBox *ptr = (GF_CopyrightBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + gf_bs_read_int(bs, 1); + //the spec is unclear here, just says "the value 0 is interpreted as undetermined" + ptr->packedLanguageCode[0] = gf_bs_read_int(bs, 5); + ptr->packedLanguageCode[1] = gf_bs_read_int(bs, 5); + ptr->packedLanguageCode[2] = gf_bs_read_int(bs, 5); + ptr->size -= 2; + //but before or after compaction ?? We assume before + if (ptr->packedLanguageCode[0] || ptr->packedLanguageCode[1] || ptr->packedLanguageCode[2]) { + ptr->packedLanguageCode[0] += 0x60; + ptr->packedLanguageCode[1] += 0x60; + ptr->packedLanguageCode[2] += 0x60; + } else { + ptr->packedLanguageCode[0] = 'u'; + ptr->packedLanguageCode[1] = 'n'; + ptr->packedLanguageCode[2] = 'd'; + } + if (ptr->size) { + u32 bytesToRead = (u32) ptr->size; + ptr->notice = (char*)malloc(bytesToRead * sizeof(char)); + if (ptr->notice == NULL) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->notice, bytesToRead); + } + return GF_OK; +} + +GF_Box *cprt_New() +{ + GF_CopyrightBox *tmp; + + tmp = (GF_CopyrightBox *) malloc(sizeof(GF_CopyrightBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_CopyrightBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_CPRT; + tmp->packedLanguageCode[0] = 'u'; + tmp->packedLanguageCode[1] = 'n'; + tmp->packedLanguageCode[2] = 'd'; + + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err cprt_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_CopyrightBox *ptr = (GF_CopyrightBox *) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_int(bs, 0, 1); + if (ptr->packedLanguageCode[0]) { + gf_bs_write_int(bs, ptr->packedLanguageCode[0] - 0x60, 5); + gf_bs_write_int(bs, ptr->packedLanguageCode[1] - 0x60, 5); + gf_bs_write_int(bs, ptr->packedLanguageCode[2] - 0x60, 5); + } else { + gf_bs_write_int(bs, 0, 15); + } + if (ptr->notice) { + gf_bs_write_data(bs, ptr->notice, (unsigned long)strlen(ptr->notice) + 1); + } + return GF_OK; +} + +GF_Err cprt_Size(GF_Box *s) +{ + GF_Err e; + GF_CopyrightBox *ptr = (GF_CopyrightBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 2; + if (ptr->notice) + ptr->size += strlen(ptr->notice) + 1; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void ctts_del(GF_Box *s) +{ + GF_CompositionOffsetBox *ptr = (GF_CompositionOffsetBox *)s; + if (ptr->entries) free(ptr->entries); + free(ptr); +} + + + +GF_Err ctts_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + u32 sampleCount; + GF_CompositionOffsetBox *ptr = (GF_CompositionOffsetBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->nb_entries = gf_bs_read_u32(bs); + ptr->alloc_size = ptr->nb_entries; + ptr->entries = malloc(sizeof(GF_DttsEntry)*ptr->alloc_size); + if (!ptr->entries) return GF_OUT_OF_MEM; + sampleCount = 0; + for (i=0; inb_entries; i++) { + ptr->entries[i].sampleCount = gf_bs_read_u32(bs); + ptr->entries[i].decodingOffset = gf_bs_read_u32(bs); + sampleCount += ptr->entries[i].sampleCount; + } +#ifndef GPAC_READ_ONLY + ptr->w_LastSampleNumber = sampleCount; +#endif + return GF_OK; +} + +GF_Box *ctts_New() +{ + GF_CompositionOffsetBox *tmp; + + tmp = (GF_CompositionOffsetBox *) malloc(sizeof(GF_CompositionOffsetBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_CompositionOffsetBox)); + + gf_isom_full_box_init((GF_Box *) tmp); + tmp->type = GF_ISOM_BOX_TYPE_CTTS; + return (GF_Box *) tmp; +} + + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err ctts_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_CompositionOffsetBox *ptr = (GF_CompositionOffsetBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i=0; inb_entries; i++ ) { + gf_bs_write_u32(bs, ptr->entries[i].sampleCount); + gf_bs_write_u32(bs, ptr->entries[i].decodingOffset); + } + return GF_OK; +} + +GF_Err ctts_Size(GF_Box *s) +{ + GF_Err e; + GF_CompositionOffsetBox *ptr = (GF_CompositionOffsetBox *) s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (8 * ptr->nb_entries); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void url_del(GF_Box *s) +{ + GF_DataEntryURLBox *ptr = (GF_DataEntryURLBox *)s; + if (ptr == NULL) return; + if (ptr->location) free(ptr->location); + free(ptr); + return; +} + + +GF_Err url_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DataEntryURLBox *ptr = (GF_DataEntryURLBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (ptr->size) { + ptr->location = (char*)malloc((u32) ptr->size); + if (! ptr->location) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->location, (u32)ptr->size); + } + return GF_OK; +} + +GF_Box *url_New() +{ + GF_DataEntryURLBox *tmp = (GF_DataEntryURLBox *) malloc(sizeof(GF_DataEntryURLBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DataEntryURLBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_URL; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err url_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DataEntryURLBox *ptr = (GF_DataEntryURLBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + //the flag set indicates we have a string (WE HAVE TO for URLs) + if ( !(ptr->flags & 1)) { + if (ptr->location) { + gf_bs_write_data(bs, ptr->location, (u32)strlen(ptr->location) + 1); + } + } + return GF_OK; +} + +GF_Err url_Size(GF_Box *s) +{ + GF_Err e; + GF_DataEntryURLBox *ptr = (GF_DataEntryURLBox *)s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + if ( !(ptr->flags & 1)) { + if (ptr->location) ptr->size += 1 + strlen(ptr->location); + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void urn_del(GF_Box *s) +{ + GF_DataEntryURNBox *ptr = (GF_DataEntryURNBox *)s; + if (ptr == NULL) return; + if (ptr->location) free(ptr->location); + if (ptr->nameURN) free(ptr->nameURN); + free(ptr); +} + + +GF_Err urn_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, to_read; + char *tmpName; + GF_DataEntryURNBox *ptr = (GF_DataEntryURNBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (! ptr->size ) return GF_OK; + + //here we have to handle that in a clever way + to_read = (u32) ptr->size; + tmpName = (char*)malloc(sizeof(char) * to_read); + if (!tmpName) return GF_OUT_OF_MEM; + //get the data + gf_bs_read_data(bs, tmpName, to_read); + + //then get the break + i = 0; + while ( (tmpName[i] != 0) && (i < to_read) ) { + i++; + } + //check the data is consistent + if (i == to_read) { + free(tmpName); + return GF_ISOM_INVALID_FILE; + } + //no NULL char, URL is not specified + if (i == to_read - 1) { + ptr->nameURN = tmpName; + ptr->location = NULL; + return GF_OK; + } + //OK, this has both URN and URL + ptr->nameURN = (char*)malloc(sizeof(char) * (i+1)); + if (!ptr->nameURN) { + free(tmpName); + return GF_OUT_OF_MEM; + } + ptr->location = (char*)malloc(sizeof(char) * (to_read - i - 1)); + if (!ptr->location) { + free(tmpName); + free(ptr->nameURN); + ptr->nameURN = NULL; + return GF_OUT_OF_MEM; + } + memcpy(ptr->nameURN, tmpName, i + 1); + memcpy(ptr->location, tmpName + i + 1, (to_read - i - 1)); + free(tmpName); + return GF_OK; +} + +GF_Box *urn_New() +{ + GF_DataEntryURNBox *tmp = (GF_DataEntryURNBox *) malloc(sizeof(GF_DataEntryURNBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DataEntryURNBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_URN; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + + +GF_Err urn_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DataEntryURNBox *ptr = (GF_DataEntryURNBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + //the flag set indicates we have a string (WE HAVE TO for URLs) + if ( !(ptr->flags & 1)) { + //to check, the spec says: First name, then location + if (ptr->nameURN) { + gf_bs_write_data(bs, ptr->nameURN, (u32)strlen(ptr->nameURN) + 1); + } + if (ptr->location) { + gf_bs_write_data(bs, ptr->location, (u32)strlen(ptr->location) + 1); + } + } + return GF_OK; +} + +GF_Err urn_Size(GF_Box *s) +{ + GF_Err e; + GF_DataEntryURNBox *ptr = (GF_DataEntryURNBox *)s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + if ( !(ptr->flags & 1)) { + if (ptr->nameURN) ptr->size += 1 + strlen(ptr->nameURN); + if (ptr->location) ptr->size += 1 + strlen(ptr->location); + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void defa_del(GF_Box *s) +{ + GF_UnknownBox *ptr = (GF_UnknownBox *) s; + if (!s) return; + if (ptr->data) free(ptr->data); + free(ptr); +} + + +GF_Err defa_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 bytesToRead; + GF_UnknownBox *ptr = (GF_UnknownBox *)s; + if (ptr->size > 0xFFFFFFFF) return GF_ISOM_INVALID_FILE; + bytesToRead = (u32) (ptr->size); + + if (bytesToRead) { + ptr->data = (char*)malloc(bytesToRead); + if (ptr->data == NULL ) return GF_OUT_OF_MEM; + ptr->dataSize = bytesToRead; + gf_bs_read_data(bs, ptr->data, ptr->dataSize); + } + return GF_OK; +} + +//warning: we don't have any boxType, trick has to be done while creating.. +GF_Box *defa_New() +{ + GF_UnknownBox *tmp = (GF_UnknownBox *) malloc(sizeof(GF_UnknownBox)); + memset(tmp, 0, sizeof(GF_UnknownBox)); + return (GF_Box *) tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err defa_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_UnknownBox *ptr = (GF_UnknownBox *)s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (ptr->data) { + gf_bs_write_data(bs, ptr->data, ptr->dataSize); + } + return GF_OK; +} + +GF_Err defa_Size(GF_Box *s) +{ + GF_Err e; + GF_UnknownBox *ptr = (GF_UnknownBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += ptr->dataSize; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void uuid_del(GF_Box *s) +{ + GF_UnknownUUIDBox *ptr = (GF_UnknownUUIDBox *) s; + if (!s) return; + if (ptr->data) free(ptr->data); + free(ptr); +} + + +GF_Err uuid_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 bytesToRead; + GF_UnknownUUIDBox *ptr = (GF_UnknownUUIDBox *)s; + if (ptr->size > 0xFFFFFFFF) return GF_ISOM_INVALID_FILE; + bytesToRead = (u32) (ptr->size); + + if (bytesToRead) { + ptr->data = (char*)malloc(bytesToRead); + if (ptr->data == NULL ) return GF_OUT_OF_MEM; + ptr->dataSize = bytesToRead; + gf_bs_read_data(bs, ptr->data, ptr->dataSize); + } + return GF_OK; +} + +//warning: we don't have any boxType, trick has to be done while creating.. +GF_Box *uuid_New() +{ + GF_UnknownUUIDBox *tmp = (GF_UnknownUUIDBox *) malloc(sizeof(GF_UnknownUUIDBox)); + memset(tmp, 0, sizeof(GF_UnknownUUIDBox)); + tmp->type = GF_ISOM_BOX_TYPE_UUID; + return (GF_Box *) tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err uuid_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_UnknownUUIDBox *ptr = (GF_UnknownUUIDBox*)s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (ptr->data) { + gf_bs_write_data(bs, ptr->data, ptr->dataSize); + } + return GF_OK; +} + +GF_Err uuid_Size(GF_Box *s) +{ + GF_Err e; + GF_UnknownUUIDBox*ptr = (GF_UnknownUUIDBox*)s; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += ptr->dataSize; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void dinf_del(GF_Box *s) +{ + GF_DataInformationBox *ptr = (GF_DataInformationBox *)s; + if (ptr == NULL) return; + gf_isom_box_del((GF_Box *)ptr->dref); + free(ptr); +} + + + +GF_Err dinf_AddBox(GF_Box *s, GF_Box *a) +{ + GF_DataInformationBox *ptr = (GF_DataInformationBox *)s; + switch(a->type) { + case GF_ISOM_BOX_TYPE_DREF: + if (ptr->dref) return GF_ISOM_INVALID_FILE; + ptr->dref = (GF_DataReferenceBox *)a; + return GF_OK; + } + gf_isom_box_del(a); + return GF_OK; +} + +GF_Err dinf_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, dinf_AddBox); +} + +GF_Box *dinf_New() +{ + GF_DataInformationBox *tmp = (GF_DataInformationBox *) malloc(sizeof(GF_DataInformationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DataInformationBox)); + tmp->type = GF_ISOM_BOX_TYPE_DINF; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err dinf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DataInformationBox *ptr = (GF_DataInformationBox *)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (ptr->dref) { + e = gf_isom_box_write((GF_Box *)ptr->dref, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err dinf_Size(GF_Box *s) +{ + GF_Err e; + GF_DataInformationBox *ptr = (GF_DataInformationBox *)s; + e = gf_isom_box_get_size(s); + if (ptr->dref) { + e = gf_isom_box_size((GF_Box *) ptr->dref); + if (e) return e; + ptr->size += ptr->dref->size; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void dref_del(GF_Box *s) +{ + GF_DataReferenceBox *ptr = (GF_DataReferenceBox *) s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->boxList); + free(ptr); +} + + +GF_Err dref_AddDataEntry(GF_DataReferenceBox *ptr, GF_Box *entry) +{ + return gf_list_add(ptr->boxList, entry); +} + +GF_Err dref_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 count, i; + GF_Box *a; + GF_DataReferenceBox *ptr = (GF_DataReferenceBox *)s; + + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + count = gf_bs_read_u32(bs); + + for ( i = 0; i < count; i++ ) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + e = gf_list_add(ptr->boxList, a); + if (e) return e; + ptr->size -= a->size; + } + return GF_OK; +} + +GF_Box *dref_New() +{ + GF_DataReferenceBox *tmp = (GF_DataReferenceBox *) malloc(sizeof(GF_DataReferenceBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DataReferenceBox)); + + gf_isom_full_box_init((GF_Box *) tmp); + tmp->boxList = gf_list_new(); + if (!tmp->boxList) { + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_DREF; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err dref_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 count; + GF_DataReferenceBox *ptr = (GF_DataReferenceBox *)s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + count = gf_list_count(ptr->boxList); + gf_bs_write_u32(bs, count); + return gf_isom_box_array_write(s, ptr->boxList, bs); +} + +GF_Err dref_Size(GF_Box *s) +{ + GF_Err e; + GF_DataReferenceBox *ptr = (GF_DataReferenceBox *)s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + e = gf_isom_box_array_size(s, ptr->boxList); + if (e) return e; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void edts_del(GF_Box *s) +{ + GF_EditBox *ptr = (GF_EditBox *) s; + gf_isom_box_del((GF_Box *)ptr->editList); + free(ptr); +} + + +GF_Err edts_AddBox(GF_Box *s, GF_Box *a) +{ + GF_EditBox *ptr = (GF_EditBox *)s; + if (a->type == GF_ISOM_BOX_TYPE_ELST) { + if (ptr->editList) return GF_BAD_PARAM; + ptr->editList = (GF_EditListBox *)a; + return GF_OK; + } + gf_isom_box_del(a); + return GF_OK; +} + + +GF_Err edts_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, edts_AddBox); +} + +GF_Box *edts_New() +{ + GF_EditBox *tmp; + GF_SAFEALLOC(tmp, GF_EditBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_EDTS; + return (GF_Box *) tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err edts_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_EditBox *ptr = (GF_EditBox *)s; + + //here we have a trick: if editList is empty, skip the box + if (gf_list_count(ptr->editList->entryList)) { + e = gf_isom_box_write_header(s, bs); + if (e) return e; + e = gf_isom_box_write((GF_Box *) ptr->editList, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err edts_Size(GF_Box *s) +{ + GF_Err e; + GF_EditBox *ptr = (GF_EditBox *)s; + + //here we have a trick: if editList is empty, skip the box + if (! gf_list_count(ptr->editList->entryList)) { + ptr->size = 0; + } else { + e = gf_isom_box_get_size(s); + if (e) return e; + e = gf_isom_box_size((GF_Box *)ptr->editList); + if (e) return e; + ptr->size += ptr->editList->size; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void elst_del(GF_Box *s) +{ + GF_EditListBox *ptr; + GF_EdtsEntry *p; + u32 nb_entries; + u32 i; + + ptr = (GF_EditListBox *)s; + if (ptr == NULL) return; + nb_entries = gf_list_count(ptr->entryList); + for (i = 0; i < nb_entries; i++) { + p = (GF_EdtsEntry*)gf_list_get(ptr->entryList, i); + if (p) free(p); + } + gf_list_del(ptr->entryList); + free(ptr); +} + + + + +GF_Err elst_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 entries; + s32 tr; + u32 nb_entries; + GF_EdtsEntry *p; + GF_EditListBox *ptr = (GF_EditListBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + nb_entries = gf_bs_read_u32(bs); + + for (entries = 0; entries < nb_entries; entries++ ) { + p = (GF_EdtsEntry *) malloc(sizeof(GF_EdtsEntry)); + if (!p) return GF_OUT_OF_MEM; + if (ptr->version == 1) { + p->segmentDuration = gf_bs_read_u64(bs); + p->mediaTime = (s64) gf_bs_read_u64(bs); + } else { + p->segmentDuration = gf_bs_read_u32(bs); + tr = gf_bs_read_u32(bs); + p->mediaTime = (s64) tr; + } + p->mediaRate = gf_bs_read_u16(bs); + gf_bs_read_u16(bs); + gf_list_add(ptr->entryList, p); + } + return GF_OK; +} + +GF_Box *elst_New() +{ + GF_EditListBox *tmp; + + tmp = (GF_EditListBox *) malloc(sizeof(GF_EditListBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_EditListBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->entryList = gf_list_new(); + if (!tmp->entryList) { + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_ELST; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err elst_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + u32 nb_entries; + GF_EdtsEntry *p; + GF_EditListBox *ptr = (GF_EditListBox *)s; + if (!ptr) return GF_BAD_PARAM; + + nb_entries = gf_list_count(ptr->entryList); + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, nb_entries); + for (i = 0; i < nb_entries; i++ ) { + p = (GF_EdtsEntry*)gf_list_get(ptr->entryList, i); + if (ptr->version == 1) { + gf_bs_write_u64(bs, p->segmentDuration); + gf_bs_write_u64(bs, p->mediaTime); + } else { + gf_bs_write_u32(bs, (u32) p->segmentDuration); + gf_bs_write_u32(bs, (s32) p->mediaTime); + } + gf_bs_write_u16(bs, p->mediaRate); + gf_bs_write_u16(bs, 0); + } + return GF_OK; +} + +GF_Err elst_Size(GF_Box *s) +{ + GF_Err e; + u32 durtimebytes; + u32 i, nb_entries; + GF_EditListBox *ptr = (GF_EditListBox *)s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + //entry count + ptr->size += 4; + nb_entries = gf_list_count(ptr->entryList); + ptr->version = 0; + for (i=0; ientryList, i); + if ((p->segmentDuration>0xFFFFFFFF) || (p->mediaTime>0xFFFFFFFF)) { + ptr->version = 1; + break; + } + } + durtimebytes = (ptr->version == 1 ? 16 : 8) + 4; + ptr->size += (nb_entries * durtimebytes); + return GF_OK; +} + + +#endif //GPAC_READ_ONLY + +void esds_del(GF_Box *s) +{ + GF_ESDBox *ptr = (GF_ESDBox *)s; + if (ptr == NULL) return; + if (ptr->desc) gf_odf_desc_del((GF_Descriptor *)ptr->desc); + free(ptr); +} + + +GF_Err esds_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 descSize; + char *enc_desc; + u32 SLIsPredefined(GF_SLConfig *sl); + GF_ESDBox *ptr = (GF_ESDBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + descSize = (u32) (ptr->size); + + if (descSize) { + enc_desc = (char*)malloc(sizeof(char) * descSize); + if (!enc_desc) return GF_OUT_OF_MEM; + //get the payload + gf_bs_read_data(bs, enc_desc, descSize); + //send it to the OD Codec + e = gf_odf_desc_read(enc_desc, descSize, (GF_Descriptor **) &ptr->desc); + //OK, free our desc + free(enc_desc); + //we do not abbort on error, but skip the descritpor + if (e) { + ptr->desc = NULL; + } else { + /*fix broken files*/ + if (!ptr->desc->URLString) { + if (!ptr->desc->slConfig) { + ptr->desc->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + ptr->desc->slConfig->predefined = SLPredef_MP4; + } else if (ptr->desc->slConfig->predefined != SLPredef_MP4) { + ptr->desc->slConfig->predefined = SLPredef_MP4; + gf_odf_slc_set_pref(ptr->desc->slConfig); + } + } + } + } + return GF_OK; +} + +GF_Box *esds_New() +{ + GF_ESDBox *tmp = (GF_ESDBox *) malloc(sizeof(GF_ESDBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ESDBox)); + + gf_isom_full_box_init((GF_Box *) tmp); + tmp->type = GF_ISOM_BOX_TYPE_ESDS; + + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err esds_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + char *enc_desc; + u32 descSize = 0; + GF_ESDBox *ptr = (GF_ESDBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + e = gf_odf_desc_write((GF_Descriptor *)ptr->desc, &enc_desc, &descSize); + if (e) return e; + gf_bs_write_data(bs, enc_desc, descSize); + //free our buffer + free(enc_desc); + return GF_OK; +} + +GF_Err esds_Size(GF_Box *s) +{ + GF_Err e; + u32 descSize = 0; + GF_ESDBox *ptr = (GF_ESDBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + descSize = gf_odf_desc_size((GF_Descriptor *)ptr->desc); + ptr->size += descSize; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void free_del(GF_Box *s) +{ + GF_FreeSpaceBox *ptr = (GF_FreeSpaceBox *)s; + if (ptr->data) free(ptr->data); + free(ptr); +} + + +GF_Err free_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 bytesToRead; + GF_FreeSpaceBox *ptr = (GF_FreeSpaceBox *)s; + + if (ptr->size > 0xFFFFFFFF) return GF_IO_ERR; + + bytesToRead = (u32) (ptr->size); + + if (bytesToRead) { + ptr->data = (char*)malloc(bytesToRead * sizeof(char)); + gf_bs_read_data(bs, ptr->data, bytesToRead); + ptr->dataSize = bytesToRead; + } + return GF_OK; +} + +GF_Box *free_New() +{ + GF_FreeSpaceBox *tmp = (GF_FreeSpaceBox *) malloc(sizeof(GF_FreeSpaceBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_FreeSpaceBox)); + tmp->type = GF_ISOM_BOX_TYPE_FREE; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err free_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_FreeSpaceBox *ptr = (GF_FreeSpaceBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (ptr->dataSize) gf_bs_write_data(bs, ptr->data, ptr->dataSize); + return GF_OK; +} + +GF_Err free_Size(GF_Box *s) +{ + GF_Err e; + GF_FreeSpaceBox *ptr = (GF_FreeSpaceBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += ptr->dataSize; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void ftyp_del(GF_Box *s) +{ + GF_FileTypeBox *ptr = (GF_FileTypeBox *) s; + if (ptr->altBrand) free(ptr->altBrand); + free(ptr); +} + +GF_Box *ftyp_New() +{ + GF_FileTypeBox *tmp; + + tmp = (GF_FileTypeBox *) malloc(sizeof(GF_FileTypeBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_FileTypeBox)); + + tmp->type = GF_ISOM_BOX_TYPE_FTYP; + return (GF_Box *)tmp; +} + +GF_Err ftyp_Read(GF_Box *s,GF_BitStream *bs) +{ + u32 i; + GF_FileTypeBox *ptr = (GF_FileTypeBox *)s; + + ptr->majorBrand = gf_bs_read_u32(bs); + ptr->minorVersion = gf_bs_read_u32(bs); + ptr->size -= 8; + + ptr->altCount = ( (u32) (ptr->size)) / 4; + if (!ptr->altCount) return GF_OK; + if (ptr->altCount * 4 != (u32) (ptr->size)) return GF_ISOM_INVALID_FILE; + + ptr->altBrand = (u32*)malloc(sizeof(u32)*ptr->altCount); + for (i = 0; ialtCount; i++) { + ptr->altBrand[i] = gf_bs_read_u32(bs); + } + return GF_OK; +} + + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err ftyp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_FileTypeBox *ptr = (GF_FileTypeBox *) s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->majorBrand); + gf_bs_write_u32(bs, ptr->minorVersion); + for (i=0; ialtCount; i++) { + gf_bs_write_u32(bs, ptr->altBrand[i]); + } + return GF_OK; +} + +GF_Err ftyp_Size(GF_Box *s) +{ + GF_Err e; + GF_FileTypeBox *ptr = (GF_FileTypeBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 8 + ptr->altCount * 4; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +void gnrm_del(GF_Box *s) +{ + GF_GenericSampleEntryBox *ptr = (GF_GenericSampleEntryBox *)s; + if (ptr->data) free(ptr->data); + free(ptr); +} + +GF_Box *gnrm_New() +{ + GF_GenericSampleEntryBox *tmp = (GF_GenericSampleEntryBox *) malloc(sizeof(GF_GenericSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_GenericSampleEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_GNRM; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err gnrm_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_GenericSampleEntryBox *ptr = (GF_GenericSampleEntryBox *)s; + + //carefull we are not writing the box type but the entry type so switch for write + ptr->type = ptr->EntryType; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + ptr->type = GF_ISOM_BOX_TYPE_GNRM; + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + gf_bs_write_data(bs, ptr->data, ptr->data_size); + return GF_OK; +} + +GF_Err gnrm_Size(GF_Box *s) +{ + GF_Err e; + GF_GenericSampleEntryBox *ptr = (GF_GenericSampleEntryBox *)s; + s->type = ptr->EntryType; + e = gf_isom_box_get_size(s); + s->type = GF_ISOM_BOX_TYPE_GNRM; + if (e) return e; + ptr->size += 8+ptr->data_size; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void gnrv_del(GF_Box *s) +{ + GF_GenericVisualSampleEntryBox *ptr = (GF_GenericVisualSampleEntryBox *)s; + if (ptr->data) free(ptr->data); + free(ptr); +} + +GF_Box *gnrv_New() +{ + GF_GenericVisualSampleEntryBox *tmp = (GF_GenericVisualSampleEntryBox *) malloc(sizeof(GF_GenericVisualSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_GenericVisualSampleEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_GNRV; + gf_isom_video_sample_entry_init((GF_VisualSampleEntryBox*) tmp); + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err gnrv_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_GenericVisualSampleEntryBox *ptr = (GF_GenericVisualSampleEntryBox *)s; + + //carefull we are not writing the box type but the entry type so switch for write + ptr->type = ptr->EntryType; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + ptr->type = GF_ISOM_BOX_TYPE_GNRV; + + gf_isom_video_sample_entry_write((GF_VisualSampleEntryBox *)ptr, bs); + gf_bs_write_data(bs, ptr->data, ptr->data_size); + return GF_OK; +} + +GF_Err gnrv_Size(GF_Box *s) +{ + GF_Err e; + GF_GenericVisualSampleEntryBox *ptr = (GF_GenericVisualSampleEntryBox *)s; + s->type = ptr->EntryType; + e = gf_isom_box_get_size(s); + s->type = GF_ISOM_BOX_TYPE_GNRV; + if (e) return e; + gf_isom_video_sample_entry_size((GF_VisualSampleEntryBox *)s); + ptr->size += ptr->data_size; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +void gnra_del(GF_Box *s) +{ + GF_GenericAudioSampleEntryBox *ptr = (GF_GenericAudioSampleEntryBox *)s; + if (ptr->data) free(ptr->data); + free(ptr); +} + +GF_Box *gnra_New() +{ + GF_GenericAudioSampleEntryBox *tmp = (GF_GenericAudioSampleEntryBox *) malloc(sizeof(GF_GenericAudioSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_GenericAudioSampleEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_GNRA; + gf_isom_audio_sample_entry_init((GF_AudioSampleEntryBox*) tmp); + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err gnra_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_GenericAudioSampleEntryBox *ptr = (GF_GenericAudioSampleEntryBox *)s; + + //carefull we are not writing the box type but the entry type so switch for write + ptr->type = ptr->EntryType; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + ptr->type = GF_ISOM_BOX_TYPE_GNRA; + + gf_isom_audio_sample_entry_write((GF_AudioSampleEntryBox *)ptr, bs); + gf_bs_write_data(bs, ptr->data, ptr->data_size); + return GF_OK; +} + +GF_Err gnra_Size(GF_Box *s) +{ + GF_Err e; + GF_GenericAudioSampleEntryBox *ptr = (GF_GenericAudioSampleEntryBox *)s; + s->type = ptr->EntryType; + e = gf_isom_box_get_size(s); + s->type = GF_ISOM_BOX_TYPE_GNRA; + if (e) return e; + gf_isom_audio_sample_entry_size((GF_AudioSampleEntryBox *)s); + ptr->size += ptr->data_size; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void hdlr_del(GF_Box *s) +{ + GF_HandlerBox *ptr = (GF_HandlerBox *)s; + if (ptr == NULL) return; + if (ptr->nameUTF8) free(ptr->nameUTF8); + free(ptr); +} + + +GF_Err hdlr_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HandlerBox *ptr = (GF_HandlerBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->reserved1 = gf_bs_read_u32(bs); + ptr->handlerType = gf_bs_read_u32(bs); + gf_bs_read_data(bs, (char*)ptr->reserved2, 12); + ptr->size -= 20; + if (ptr->size) { + ptr->nameUTF8 = (char*)malloc((u32) ptr->size); + if (ptr->nameUTF8 == NULL) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->nameUTF8, (u32) ptr->size); + /*safety check in case the string is not null-terminated*/ + if (ptr->nameUTF8[ptr->size-1]) { + char *str = (char*)malloc((u32) ptr->size + 1); + memcpy(str, ptr->nameUTF8, (u32) ptr->size); + str[ptr->size] = 0; + free(ptr->nameUTF8); + ptr->nameUTF8 = str; + } + } + return GF_OK; +} + +GF_Box *hdlr_New() +{ + GF_HandlerBox *tmp = (GF_HandlerBox *) malloc(sizeof(GF_HandlerBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_HandlerBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_HDLR; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err hdlr_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HandlerBox *ptr = (GF_HandlerBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->reserved1); + gf_bs_write_u32(bs, ptr->handlerType); + gf_bs_write_data(bs, (char*)ptr->reserved2, 12); + if (ptr->nameUTF8) gf_bs_write_data(bs, ptr->nameUTF8, strlen(ptr->nameUTF8)); + /*NULL-terminated string is written*/ + gf_bs_write_u8(bs, 0); + return GF_OK; +} + +GF_Err hdlr_Size(GF_Box *s) +{ + GF_Err e; + GF_HandlerBox *ptr = (GF_HandlerBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 20 + 1; + if (ptr->nameUTF8) ptr->size += strlen(ptr->nameUTF8); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void hinf_del(GF_Box *s) +{ + GF_HintInfoBox *hinf = (GF_HintInfoBox *)s; + gf_isom_box_array_del(hinf->boxList); + gf_list_del(hinf->dataRates); + free(hinf); +} + +GF_Box *hinf_New() +{ + GF_HintInfoBox *tmp = (GF_HintInfoBox *)malloc(sizeof(GF_HintInfoBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_HintInfoBox)); + + tmp->boxList = gf_list_new(); + if (!tmp->boxList) { + free(tmp); + return NULL; + } + tmp->dataRates = gf_list_new(); + if (!tmp->dataRates) { + gf_list_del(tmp->boxList); + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_HINF; + return (GF_Box *)tmp; +} + +GF_Err hinf_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MAXRBox *maxR; + GF_HintInfoBox *hinf = (GF_HintInfoBox *)s; + u32 i; + switch (a->type) { + case GF_ISOM_BOX_TYPE_MAXR: + i=0; + while ((maxR = (GF_MAXRBox *)gf_list_enum(hinf->dataRates, &i))) { + if (maxR->granularity == ((GF_MAXRBox *)a)->granularity) return GF_ISOM_INVALID_FILE; + } + gf_list_add(hinf->dataRates, a); + break; + default: + break; + } + return gf_list_add(hinf->boxList, a); +} + + +GF_Err hinf_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, hinf_AddBox); +} + +#ifndef GPAC_READ_ONLY + +GF_Err hinf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HintInfoBox *ptr = (GF_HintInfoBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + return gf_isom_box_array_write(s, ptr->boxList, bs); +} + +GF_Err hinf_Size(GF_Box *s) +{ + GF_Err e; + GF_HintInfoBox *ptr = (GF_HintInfoBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + return gf_isom_box_array_size(s, ptr->boxList); +} +#endif + +void hmhd_del(GF_Box *s) +{ + GF_HintMediaHeaderBox *ptr = (GF_HintMediaHeaderBox *)s; + if (ptr == NULL) return; + free(ptr); +} + + +GF_Err hmhd_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + GF_HintMediaHeaderBox *ptr = (GF_HintMediaHeaderBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->maxPDUSize = gf_bs_read_u16(bs); + ptr->avgPDUSize = gf_bs_read_u16(bs); + ptr->maxBitrate = gf_bs_read_u32(bs); + ptr->avgBitrate = gf_bs_read_u32(bs); + ptr->slidingAverageBitrate = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *hmhd_New() +{ + GF_HintMediaHeaderBox *tmp = (GF_HintMediaHeaderBox *) malloc(sizeof(GF_HintMediaHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_HintMediaHeaderBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_HMHD; + + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err hmhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HintMediaHeaderBox *ptr = (GF_HintMediaHeaderBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->maxPDUSize); + gf_bs_write_u16(bs, ptr->avgPDUSize); + gf_bs_write_u32(bs, ptr->maxBitrate); + gf_bs_write_u32(bs, ptr->avgBitrate); + gf_bs_write_u32(bs, ptr->slidingAverageBitrate); + return GF_OK; +} + +GF_Err hmhd_Size(GF_Box *s) +{ + GF_Err e; + GF_HintMediaHeaderBox *ptr = (GF_HintMediaHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 16; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +GF_Box *hnti_New() +{ + GF_HintTrackInfoBox *tmp = (GF_HintTrackInfoBox *)malloc(sizeof(GF_HintTrackInfoBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_HintTrackInfoBox)); + tmp->boxList = gf_list_new(); + if (!tmp->boxList) { + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_HNTI; + return (GF_Box *)tmp; +} + +void hnti_del(GF_Box *a) +{ + GF_Box *t; + GF_RTPBox *rtp; + GF_HintTrackInfoBox *ptr = (GF_HintTrackInfoBox *)a; + while (gf_list_count(ptr->boxList)) { + t = (GF_Box*)gf_list_get(ptr->boxList, 0); + if (t->type != GF_ISOM_BOX_TYPE_RTP) { + gf_isom_box_del(t); + } else { + rtp = (GF_RTPBox *)t; + if (rtp->sdpText) free(rtp->sdpText); + free(rtp); + } + gf_list_rem(ptr->boxList, 0); + } + gf_list_del(ptr->boxList); + free(ptr); +} + +GF_Err hnti_AddBox(GF_HintTrackInfoBox *hnti, GF_Box *a) +{ + if (!hnti || !a) return GF_BAD_PARAM; + + switch (a->type) { + //this is the value for GF_RTPBox - same as HintSampleEntry for RTP !!! + case GF_ISOM_BOX_TYPE_RTP: + case GF_ISOM_BOX_TYPE_SDP: + if (hnti->SDP) return GF_BAD_PARAM; + hnti->SDP = a; + break; + default: + break; + } + return gf_list_add(hnti->boxList, a); +} + +GF_Err hnti_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 type; + u32 length; + GF_Err e; + GF_Box *a; + GF_RTPBox *rtp; + + GF_HintTrackInfoBox *ptr = (GF_HintTrackInfoBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + //WARNING: because of the HNTI at movie level, we cannot use the generic parsing scheme! + //this because the child SDP box at the movie level has a type of RTP, used for + //the HintSampleEntry ! + while (ptr->size) { + //get the type of the box (4 bytes after our current position in the bitstream) + //before parsing... + type = gf_bs_peek_bits(bs, 32, 4); + if (type != GF_ISOM_BOX_TYPE_RTP) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + e = hnti_AddBox(ptr, a); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size-=a->size; + } else { + u32 sr; + rtp = (GF_RTPBox*)malloc(sizeof(GF_RTPBox)); + if (!rtp) return GF_OUT_OF_MEM; + rtp->size = gf_bs_read_u32(bs); + rtp->type = gf_bs_read_u32(bs); + sr = 8; + //"ITS LENGTH IS CALCULATED BY SUBSTRACTING 8 (or 12) from the box size" - QT specs + //this means that we don't have any NULL char as a delimiter in QT ... + if (rtp->size == 1) return GF_BAD_PARAM; + rtp->subType = gf_bs_read_u32(bs); + sr += 4; + if (rtp->subType != GF_ISOM_BOX_TYPE_SDP) return GF_NOT_SUPPORTED; + if (rtp->size < sr) return GF_ISOM_INVALID_FILE; + length = (u32) (rtp->size - sr); + rtp->sdpText = (char*)malloc(sizeof(char) * (length + 1)); + if (!rtp->sdpText) { + free(rtp); + return GF_OUT_OF_MEM; + } + gf_bs_read_data(bs, rtp->sdpText, length); + rtp->sdpText[length] = 0; + sr += length; + e = hnti_AddBox(ptr, (GF_Box *)rtp); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size -= rtp->size; + } + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err hnti_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, count; + GF_Box *a; + GF_RTPBox *rtp; + + GF_HintTrackInfoBox *ptr = (GF_HintTrackInfoBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + count = gf_list_count(ptr->boxList); + for (i = 0; i < count; i ++) { + a = (GF_Box*)gf_list_get(ptr->boxList, i); + if (a->type != GF_ISOM_BOX_TYPE_RTP) { + e = gf_isom_box_write(a, bs); + if (e) return e; + } else { + //write the GF_RTPBox by hand + rtp = (GF_RTPBox *)a; + e = gf_isom_box_write_header(a, bs); + if (e) return e; + gf_bs_write_u32(bs, rtp->subType); + //don't write the NULL char + gf_bs_write_data(bs, rtp->sdpText, strlen(rtp->sdpText)); + } + } + return GF_OK; +} + + +GF_Err hnti_Size(GF_Box *s) +{ + GF_Err e; + u32 i, count; + GF_Box *a; + GF_RTPBox *rtp; + + GF_HintTrackInfoBox *ptr = (GF_HintTrackInfoBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_box_get_size(s); + if (e) return e; + + count = gf_list_count(ptr->boxList); + for (i = 0; i < count; i ++) { + a = (GF_Box*)gf_list_get(ptr->boxList, i); + if (a->type != GF_ISOM_BOX_TYPE_RTP) { + e = gf_isom_box_size(a); + if (e) return e; + } else { + //get the GF_RTPBox size by hand + rtp = (GF_RTPBox *)a; + e = gf_isom_box_get_size(a); + if (e) return e; + //don't count the NULL char... + rtp->size += 4 + strlen(rtp->sdpText); + } + ptr->size += a->size; + } + return GF_OK; +} +#endif + +/********************************************************** + GF_SDPBox +**********************************************************/ + +void sdp_del(GF_Box *s) +{ + GF_SDPBox *ptr = (GF_SDPBox *)s; + if (ptr->sdpText) free(ptr->sdpText); + free(ptr); + +} +GF_Err sdp_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 length; + GF_SDPBox *ptr = (GF_SDPBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + length = (u32) (ptr->size); + //sdp text has no delimiter !!! + ptr->sdpText = (char*)malloc(sizeof(char) * (length+1)); + if (!ptr->sdpText) return GF_OUT_OF_MEM; + + gf_bs_read_data(bs, ptr->sdpText, length); + ptr->sdpText[length] = 0; + return GF_OK; +} +GF_Box *sdp_New() +{ + GF_SDPBox *tmp = (GF_SDPBox *) malloc(sizeof(GF_SDPBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_SDPBox)); + tmp->type = GF_ISOM_BOX_TYPE_SDP; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err sdp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SDPBox *ptr = (GF_SDPBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + //don't write the NULL char!!! + gf_bs_write_data(bs, ptr->sdpText, strlen(ptr->sdpText)); + return GF_OK; +} +GF_Err sdp_Size(GF_Box *s) +{ + GF_Err e; + GF_SDPBox *ptr = (GF_SDPBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + //don't count the NULL char!!! + ptr->size += strlen(ptr->sdpText); + return GF_OK; +} + +#endif + + +/********************************************************** + TRPY GF_Box +**********************************************************/ + +void trpy_del(GF_Box *s) +{ + free((GF_TRPYBox *)s); +} +GF_Err trpy_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TRPYBox *ptr = (GF_TRPYBox *)s; + ptr->nbBytes = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *trpy_New() +{ + GF_TRPYBox *tmp = (GF_TRPYBox *) malloc(sizeof(GF_TRPYBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TRPY; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY + +GF_Err trpy_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TRPYBox *ptr = (GF_TRPYBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err trpy_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif + +/********************************************************** + TOTL GF_Box +**********************************************************/ + +void totl_del(GF_Box *s) +{ + free((GF_TRPYBox *)s); +} +GF_Err totl_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TOTLBox *ptr = (GF_TOTLBox *)s; + ptr->nbBytes = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *totl_New() +{ + GF_TOTLBox *tmp = (GF_TOTLBox *) malloc(sizeof(GF_TOTLBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TOTL; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} + +#ifndef GPAC_READ_ONLY + +GF_Err totl_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TOTLBox *ptr = (GF_TOTLBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err totl_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif + + +/********************************************************** + NUMP GF_Box +**********************************************************/ + +void nump_del(GF_Box *s) +{ + free((GF_NUMPBox *)s); +} +GF_Err nump_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_NUMPBox *ptr = (GF_NUMPBox *)s; + ptr->nbPackets = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *nump_New() +{ + GF_NUMPBox *tmp = (GF_NUMPBox *) malloc(sizeof(GF_NUMPBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_NUMP; + tmp->nbPackets = 0; + return (GF_Box *)tmp; +} + +#ifndef GPAC_READ_ONLY +GF_Err nump_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_NUMPBox *ptr = (GF_NUMPBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbPackets); + return GF_OK; +} +GF_Err nump_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif + + +/********************************************************** + NPCK GF_Box +**********************************************************/ + +void npck_del(GF_Box *s) +{ + free((GF_NPCKBox *)s); +} +GF_Err npck_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_NPCKBox *ptr = (GF_NPCKBox *)s; + ptr->nbPackets = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *npck_New() +{ + GF_NPCKBox *tmp = (GF_NPCKBox *) malloc(sizeof(GF_NPCKBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_NPCK; + tmp->nbPackets = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err npck_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_NPCKBox *ptr = (GF_NPCKBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nbPackets); + return GF_OK; +} +GF_Err npck_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif + + +/********************************************************** + TPYL GF_Box +**********************************************************/ + +void tpyl_del(GF_Box *s) +{ + free((GF_NTYLBox *)s); +} +GF_Err tpyl_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_NTYLBox *ptr = (GF_NTYLBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + ptr->nbBytes = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *tpyl_New() +{ + GF_NTYLBox *tmp = (GF_NTYLBox *) malloc(sizeof(GF_NTYLBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TPYL; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err tpyl_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_NTYLBox *ptr = (GF_NTYLBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err tpyl_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif + +/********************************************************** + TPAY GF_Box +**********************************************************/ + +void tpay_del(GF_Box *s) +{ + free((GF_TPAYBox *)s); +} +GF_Err tpay_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TPAYBox *ptr = (GF_TPAYBox *)s; + ptr->nbBytes = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *tpay_New() +{ + GF_TPAYBox *tmp = (GF_TPAYBox *) malloc(sizeof(GF_TPAYBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TPAY; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err tpay_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TPAYBox *ptr = (GF_TPAYBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err tpay_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif + + +/********************************************************** + MAXR GF_Box +**********************************************************/ + +void maxr_del(GF_Box *s) +{ + free((GF_MAXRBox *)s); +} +GF_Err maxr_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MAXRBox *ptr = (GF_MAXRBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + ptr->granularity = gf_bs_read_u32(bs); + ptr->maxDataRate = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *maxr_New() +{ + GF_MAXRBox *tmp = (GF_MAXRBox *) malloc(sizeof(GF_MAXRBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_MAXR; + tmp->granularity = tmp->maxDataRate = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err maxr_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MAXRBox *ptr = (GF_MAXRBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->granularity); + gf_bs_write_u32(bs, ptr->maxDataRate); + return GF_OK; +} +GF_Err maxr_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif + + +/********************************************************** + DMED GF_Box +**********************************************************/ + +void dmed_del(GF_Box *s) +{ + free((GF_DMEDBox *)s); +} +GF_Err dmed_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_DMEDBox *ptr = (GF_DMEDBox *)s; + ptr->nbBytes = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *dmed_New() +{ + GF_DMEDBox *tmp = (GF_DMEDBox *) malloc(sizeof(GF_DMEDBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DMED; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err dmed_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DMEDBox *ptr = (GF_DMEDBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err dmed_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif + +/********************************************************** + DIMM GF_Box +**********************************************************/ + +void dimm_del(GF_Box *s) +{ + free((GF_DIMMBox *)s); +} +GF_Err dimm_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_DIMMBox *ptr = (GF_DIMMBox *)s; + ptr->nbBytes = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *dimm_New() +{ + GF_DIMMBox *tmp = (GF_DIMMBox *) malloc(sizeof(GF_DIMMBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DIMM; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err dimm_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DIMMBox *ptr = (GF_DIMMBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err dimm_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif + +/********************************************************** + DREP GF_Box +**********************************************************/ + +void drep_del(GF_Box *s) +{ + free((GF_DREPBox *)s); +} +GF_Err drep_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_DREPBox *ptr = (GF_DREPBox *)s; + ptr->nbBytes = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *drep_New() +{ + GF_DREPBox *tmp = (GF_DREPBox *) malloc(sizeof(GF_DREPBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DREP; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err drep_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DREPBox *ptr = (GF_DREPBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err drep_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif + + + +/********************************************************** + TMIN GF_Box +**********************************************************/ + +void tmin_del(GF_Box *s) +{ + free((GF_TMINBox *)s); +} +GF_Err tmin_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TMINBox *ptr = (GF_TMINBox *)s; + ptr->minTime = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *tmin_New() +{ + GF_TMINBox *tmp = (GF_TMINBox *) malloc(sizeof(GF_TMINBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TMIN; + tmp->minTime = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err tmin_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TMINBox *ptr = (GF_TMINBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->minTime); + return GF_OK; +} +GF_Err tmin_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif + + +/********************************************************** + TMAX GF_Box +**********************************************************/ + +void tmax_del(GF_Box *s) +{ + free((GF_TMAXBox *)s); +} +GF_Err tmax_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TMAXBox *ptr = (GF_TMAXBox *)s; + ptr->maxTime = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *tmax_New() +{ + GF_TMAXBox *tmp = (GF_TMAXBox *) malloc(sizeof(GF_TMAXBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TMAX; + tmp->maxTime = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err tmax_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TMAXBox *ptr = (GF_TMAXBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->maxTime); + return GF_OK; +} +GF_Err tmax_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif + + +/********************************************************** + PMAX GF_Box +**********************************************************/ + +void pmax_del(GF_Box *s) +{ + free((GF_PMAXBox *)s); +} +GF_Err pmax_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_PMAXBox *ptr = (GF_PMAXBox *)s; + ptr->maxSize = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *pmax_New() +{ + GF_PMAXBox *tmp = (GF_PMAXBox *) malloc(sizeof(GF_PMAXBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_PMAX; + tmp->maxSize = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err pmax_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_PMAXBox *ptr = (GF_PMAXBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->maxSize); + return GF_OK; +} +GF_Err pmax_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif + + +/********************************************************** + DMAX GF_Box +**********************************************************/ + +void dmax_del(GF_Box *s) +{ + free((GF_DMAXBox *)s); +} +GF_Err dmax_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_DMAXBox *ptr = (GF_DMAXBox *)s; + ptr->maxDur = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *dmax_New() +{ + GF_DMAXBox *tmp = (GF_DMAXBox *) malloc(sizeof(GF_DMAXBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DMAX; + tmp->maxDur = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err dmax_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DMAXBox *ptr = (GF_DMAXBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->maxDur); + return GF_OK; +} +GF_Err dmax_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif + + +/********************************************************** + PAYT GF_Box +**********************************************************/ + +void payt_del(GF_Box *s) +{ + GF_PAYTBox *payt = (GF_PAYTBox *)s; + if (payt->payloadString) free(payt->payloadString); + free(payt); +} +GF_Err payt_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 length; + GF_PAYTBox *ptr = (GF_PAYTBox *)s; + + ptr->payloadCode = gf_bs_read_u32(bs); + length = gf_bs_read_u8(bs); + ptr->payloadString = (char*)malloc(sizeof(char) * (length+1) ); + if (! ptr->payloadString) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->payloadString, length); + ptr->payloadString[length] = 0; + ptr->size -= 4+length+1; + return GF_OK; +} +GF_Box *payt_New() +{ + GF_PAYTBox *tmp = (GF_PAYTBox *) malloc(sizeof(GF_PAYTBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_PAYT; + tmp->payloadCode = 0; + tmp->payloadString = NULL; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err payt_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 len; + GF_Err e; + GF_PAYTBox *ptr = (GF_PAYTBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->payloadCode); + len = strlen(ptr->payloadString); + gf_bs_write_u8(bs, len); + if (len) gf_bs_write_data(bs, ptr->payloadString, len); + return GF_OK; +} +GF_Err payt_Size(GF_Box *s) +{ + GF_Err e; + GF_PAYTBox *ptr = (GF_PAYTBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + if (ptr->payloadString) ptr->size += strlen(ptr->payloadString) + 1; + return GF_OK; +} +#endif + + +/********************************************************** + PAYT GF_Box +**********************************************************/ + +void name_del(GF_Box *s) +{ + GF_NameBox *name = (GF_NameBox *)s; + if (name->string) free(name->string); + free(name); +} +GF_Err name_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 length; + GF_NameBox *ptr = (GF_NameBox *)s; + + length = (u32) (ptr->size); + ptr->string = (char*)malloc(sizeof(char) * length); + if (! ptr->string) return GF_OUT_OF_MEM; + + gf_bs_read_data(bs, ptr->string, length); + return GF_OK; +} +GF_Box *name_New() +{ + GF_NameBox *tmp = (GF_NameBox *) malloc(sizeof(GF_NameBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_NAME; + tmp->string = NULL; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err name_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_NameBox *ptr = (GF_NameBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (ptr->string) { + gf_bs_write_data(bs, ptr->string, strlen(ptr->string) + 1); + } + return GF_OK; +} +GF_Err name_Size(GF_Box *s) +{ + GF_Err e; + GF_NameBox *ptr = (GF_NameBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + if (ptr->string) ptr->size += strlen(ptr->string) + 1; + return GF_OK; +} +#endif + +void iods_del(GF_Box *s) +{ + GF_ObjectDescriptorBox *ptr = (GF_ObjectDescriptorBox *)s; + if (ptr == NULL) return; + if (ptr->descriptor) gf_odf_desc_del(ptr->descriptor); + free(ptr); +} + + +GF_Err iods_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 descSize; + char *desc; + GF_ObjectDescriptorBox *ptr = (GF_ObjectDescriptorBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + //use the OD codec... + descSize = (u32) (ptr->size); + desc = (char*)malloc(sizeof(char) * descSize); + gf_bs_read_data(bs, desc, descSize); + e = gf_odf_desc_read(desc, descSize, &ptr->descriptor); + //OK, free our desc + free(desc); + return GF_OK; +} + +GF_Box *iods_New() +{ + GF_ObjectDescriptorBox *tmp = (GF_ObjectDescriptorBox *) malloc(sizeof(GF_ObjectDescriptorBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ObjectDescriptorBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_IODS; + return (GF_Box *)tmp; +} + + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err iods_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 descSize; + char *desc; + GF_ObjectDescriptorBox *ptr = (GF_ObjectDescriptorBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + //call our OD codec + e = gf_odf_desc_write(ptr->descriptor, &desc, &descSize); + if (e) return e; + gf_bs_write_data(bs, desc, descSize); + //and free our stuff maybe!! + free(desc); + return GF_OK; +} + +GF_Err iods_Size(GF_Box *s) +{ + GF_Err e; + GF_ObjectDescriptorBox *ptr = (GF_ObjectDescriptorBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += gf_odf_desc_size(ptr->descriptor); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void mdat_del(GF_Box *s) +{ + GF_MediaDataBox *ptr = (GF_MediaDataBox *)s; + if (!s) return; + + if (ptr->data) free(ptr->data); + free(ptr); +} + + +GF_Err mdat_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MediaDataBox *ptr = (GF_MediaDataBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + ptr->dataSize = s->size; + //then skip these bytes + gf_bs_skip_bytes(bs, ptr->dataSize); + return GF_OK; +} + +GF_Box *mdat_New() +{ + GF_MediaDataBox *tmp = (GF_MediaDataBox *) malloc(sizeof(GF_MediaDataBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MediaDataBox)); + tmp->type = GF_ISOM_BOX_TYPE_MDAT; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err mdat_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MediaDataBox *ptr = (GF_MediaDataBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + //make sure we have some data ... + //if not, we handle that independantly (edit files) + if (ptr->data) { + gf_bs_write_data(bs, ptr->data, (u32) ptr->dataSize); + } + return GF_OK; +} + +GF_Err mdat_Size(GF_Box *s) +{ + GF_Err e; + GF_MediaDataBox *ptr = (GF_MediaDataBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + ptr->size += ptr->dataSize; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void mdhd_del(GF_Box *s) +{ + GF_MediaHeaderBox *ptr = (GF_MediaHeaderBox *)s; + if (ptr == NULL) return; + free(ptr); +} + +GF_Err mdhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MediaHeaderBox *ptr = (GF_MediaHeaderBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (ptr->version == 1) { + ptr->creationTime = gf_bs_read_u64(bs); + ptr->modificationTime = gf_bs_read_u64(bs); + ptr->timeScale = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u64(bs); + } else { + ptr->creationTime = gf_bs_read_u32(bs); + ptr->modificationTime = gf_bs_read_u32(bs); + ptr->timeScale = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u32(bs); + } + //our padding bit + gf_bs_read_int(bs, 1); + //the spec is unclear here, just says "the value 0 is interpreted as undetermined" + ptr->packedLanguage[0] = gf_bs_read_int(bs, 5); + ptr->packedLanguage[1] = gf_bs_read_int(bs, 5); + ptr->packedLanguage[2] = gf_bs_read_int(bs, 5); + //but before or after compaction ?? We assume before + if (ptr->packedLanguage[0] || ptr->packedLanguage[1] || ptr->packedLanguage[2]) { + ptr->packedLanguage[0] += 0x60; + ptr->packedLanguage[1] += 0x60; + ptr->packedLanguage[2] += 0x60; + } else { + ptr->packedLanguage[0] = 'u'; + ptr->packedLanguage[1] = 'n'; + ptr->packedLanguage[2] = 'd'; + } + ptr->reserved = gf_bs_read_u16(bs); + return GF_OK; +} + +GF_Box *mdhd_New() +{ + GF_MediaHeaderBox *tmp = (GF_MediaHeaderBox *) malloc(sizeof(GF_MediaHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MediaHeaderBox)); + + gf_isom_full_box_init((GF_Box *) tmp); + tmp->type = GF_ISOM_BOX_TYPE_MDHD; + + tmp->packedLanguage[0] = 'u'; + tmp->packedLanguage[1] = 'n'; + tmp->packedLanguage[2] = 'd'; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err mdhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MediaHeaderBox *ptr = (GF_MediaHeaderBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->version == 1) { + gf_bs_write_u64(bs, ptr->creationTime); + gf_bs_write_u64(bs, ptr->modificationTime); + gf_bs_write_u32(bs, ptr->timeScale); + gf_bs_write_u64(bs, ptr->duration); + } else { + gf_bs_write_u32(bs, (u32) ptr->creationTime); + gf_bs_write_u32(bs, (u32) ptr->modificationTime); + gf_bs_write_u32(bs, ptr->timeScale); + gf_bs_write_u32(bs, (u32) ptr->duration); + } + //SPECS: BIT(1) of padding + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, ptr->packedLanguage[0] - 0x60, 5); + gf_bs_write_int(bs, ptr->packedLanguage[1] - 0x60, 5); + gf_bs_write_int(bs, ptr->packedLanguage[2] - 0x60, 5); + gf_bs_write_u16(bs, ptr->reserved); + return GF_OK; +} + +GF_Err mdhd_Size(GF_Box *s) +{ + GF_Err e; + GF_MediaHeaderBox *ptr = (GF_MediaHeaderBox *)s; + ptr->version = (ptr->duration>0xFFFFFFFF) ? 1 : 0; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + ptr->size += (ptr->version == 1) ? 28 : 16; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void mdia_del(GF_Box *s) +{ + GF_MediaBox *ptr = (GF_MediaBox *)s; + if (ptr == NULL) return; + if (ptr->mediaHeader) gf_isom_box_del((GF_Box *)ptr->mediaHeader); + if (ptr->information) gf_isom_box_del((GF_Box *)ptr->information); + if (ptr->handler) gf_isom_box_del((GF_Box *)ptr->handler); + free(ptr); +} + + +GF_Err mdia_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MediaBox *ptr = (GF_MediaBox *)s; + switch(a->type) { + case GF_ISOM_BOX_TYPE_MDHD: + if (ptr->mediaHeader) return GF_ISOM_INVALID_FILE; + ptr->mediaHeader = (GF_MediaHeaderBox *)a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_HDLR: + if (ptr->handler) return GF_ISOM_INVALID_FILE; + ptr->handler = (GF_HandlerBox *)a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_MINF: + if (ptr->information) return GF_ISOM_INVALID_FILE; + ptr->information = (GF_MediaInformationBox *)a; + return GF_OK; + } + gf_isom_box_del(a); + return GF_OK; +} + + +GF_Err mdia_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, mdia_AddBox); +} + +GF_Box *mdia_New() +{ + GF_MediaBox *tmp = (GF_MediaBox *) malloc(sizeof(GF_MediaBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MediaBox)); + tmp->type = GF_ISOM_BOX_TYPE_MDIA; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err mdia_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MediaBox *ptr = (GF_MediaBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + //Header first + if (ptr->mediaHeader) { + e = gf_isom_box_write((GF_Box *) ptr->mediaHeader, bs); + if (e) return e; + } + //then handler + if (ptr->handler) { + e = gf_isom_box_write((GF_Box *) ptr->handler, bs); + if (e) return e; + } + if (ptr->information) { + e = gf_isom_box_write((GF_Box *) ptr->information, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err mdia_Size(GF_Box *s) +{ + GF_Err e; + GF_MediaBox *ptr = (GF_MediaBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + if (ptr->mediaHeader) { + e = gf_isom_box_size((GF_Box *) ptr->mediaHeader); + if (e) return e; + ptr->size += ptr->mediaHeader->size; + } + if (ptr->handler) { + e = gf_isom_box_size((GF_Box *) ptr->handler); + if (e) return e; + ptr->size += ptr->handler->size; + } + if (ptr->information) { + e = gf_isom_box_size((GF_Box *) ptr->information); + if (e) return e; + ptr->size += ptr->information->size; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +void mfhd_del(GF_Box *s) +{ + GF_MovieFragmentHeaderBox *ptr = (GF_MovieFragmentHeaderBox *)s; + if (ptr == NULL) return; + free(ptr); +} + +GF_Err mfhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieFragmentHeaderBox *ptr = (GF_MovieFragmentHeaderBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->sequence_number = gf_bs_read_u32(bs); + if (!ptr->sequence_number) return GF_ISOM_INVALID_FILE; + return GF_OK; +} + +GF_Box *mfhd_New() +{ + GF_MovieFragmentHeaderBox *tmp = (GF_MovieFragmentHeaderBox *) malloc(sizeof(GF_MovieFragmentHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MovieFragmentHeaderBox)); + tmp->type = GF_ISOM_BOX_TYPE_MFHD; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + + +GF_Err mfhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieFragmentHeaderBox *ptr = (GF_MovieFragmentHeaderBox *) s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->sequence_number); + return GF_OK; +} + +GF_Err mfhd_Size(GF_Box *s) +{ + GF_Err e; + GF_MovieFragmentHeaderBox *ptr = (GF_MovieFragmentHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + return GF_OK; +} + + + +#endif /*GPAC_READ_ONLY*/ + +#endif /*GPAC_ISOM_NO_FRAGMENTS*/ + + +void minf_del(GF_Box *s) +{ + GF_MediaInformationBox *ptr = (GF_MediaInformationBox *)s; + if (ptr == NULL) return; + + //if we have a Handler not self-contained, delete it (the self-contained belongs to the movie) + if (ptr->dataHandler) { + gf_isom_datamap_close(ptr); + } + if (ptr->InfoHeader) gf_isom_box_del((GF_Box *)ptr->InfoHeader); + if (ptr->dataInformation) gf_isom_box_del((GF_Box *)ptr->dataInformation); + if (ptr->sampleTable) gf_isom_box_del((GF_Box *)ptr->sampleTable); + gf_isom_box_array_del(ptr->boxes); + free(ptr); +} + +GF_Err minf_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MediaInformationBox *ptr = (GF_MediaInformationBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_NMHD: + case GF_ISOM_BOX_TYPE_VMHD: + case GF_ISOM_BOX_TYPE_SMHD: + case GF_ISOM_BOX_TYPE_HMHD: + case GF_ISOM_BOX_TYPE_GMHD: + if (ptr->InfoHeader) return GF_ISOM_INVALID_FILE; + ptr->InfoHeader = a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_DINF: + if (ptr->dataInformation ) return GF_ISOM_INVALID_FILE; + ptr->dataInformation = (GF_DataInformationBox *)a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_STBL: + if (ptr->sampleTable ) return GF_ISOM_INVALID_FILE; + ptr->sampleTable = (GF_SampleTableBox *)a; + return GF_OK; + default: + return gf_list_add(ptr->boxes, a); + } + return GF_OK; +} + + +GF_Err minf_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, minf_AddBox); +} + +GF_Box *minf_New() +{ + GF_MediaInformationBox *tmp = (GF_MediaInformationBox *) malloc(sizeof(GF_MediaInformationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MediaInformationBox)); + tmp->type = GF_ISOM_BOX_TYPE_MINF; + tmp->boxes = gf_list_new(); + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err minf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MediaInformationBox *ptr = (GF_MediaInformationBox *)s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + //Header first + if (ptr->InfoHeader) { + e = gf_isom_box_write((GF_Box *) ptr->InfoHeader, bs); + if (e) return e; + } + //then dataInfo + if (ptr->dataInformation) { + e = gf_isom_box_write((GF_Box *) ptr->dataInformation, bs); + if (e) return e; + } + //then sampleTable + if (ptr->sampleTable) { + e = gf_isom_box_write((GF_Box *) ptr->sampleTable, bs); + if (e) return e; + } + return gf_isom_box_array_write(s, ptr->boxes, bs); +} + +GF_Err minf_Size(GF_Box *s) +{ + GF_Err e; + GF_MediaInformationBox *ptr = (GF_MediaInformationBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + if (ptr->InfoHeader) { + e = gf_isom_box_size((GF_Box *) ptr->InfoHeader); + if (e) return e; + ptr->size += ptr->InfoHeader->size; + } + if (ptr->dataInformation) { + e = gf_isom_box_size((GF_Box *) ptr->dataInformation); + if (e) return e; + ptr->size += ptr->dataInformation->size; + } + if (ptr->sampleTable) { + e = gf_isom_box_size((GF_Box *) ptr->sampleTable); + if (e) return e; + ptr->size += ptr->sampleTable->size; + } + return gf_isom_box_array_size(s, ptr->boxes); +} + +#endif //GPAC_READ_ONLY + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +void moof_del(GF_Box *s) +{ + GF_MovieFragmentBox *ptr = (GF_MovieFragmentBox *)s; + if (ptr == NULL) return; + + if (ptr->mfhd) gf_isom_box_del((GF_Box *) ptr->mfhd); + gf_isom_box_array_del(ptr->TrackList); + free(ptr); +} + +GF_Err moof_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MovieFragmentBox *ptr = (GF_MovieFragmentBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_MFHD: + if (ptr->mfhd) return GF_ISOM_INVALID_FILE; + ptr->mfhd = (GF_MovieFragmentHeaderBox *) a; + return GF_OK; + case GF_ISOM_BOX_TYPE_TRAF: + return gf_list_add(ptr->TrackList, a); + default: + return GF_ISOM_INVALID_FILE; + } +} + +GF_Err moof_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, moof_AddBox); +} + +GF_Box *moof_New() +{ + GF_MovieFragmentBox *tmp = (GF_MovieFragmentBox *) malloc(sizeof(GF_MovieFragmentBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MovieFragmentBox)); + tmp->type = GF_ISOM_BOX_TYPE_MOOF; + tmp->TrackList = gf_list_new(); + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err moof_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieFragmentBox *ptr = (GF_MovieFragmentBox *) s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + //Header First + if (ptr->mfhd) { + e = gf_isom_box_write((GF_Box *) ptr->mfhd, bs); + if (e) return e; + } + //then the track list + return gf_isom_box_array_write(s, ptr->TrackList, bs); +} + +GF_Err moof_Size(GF_Box *s) +{ + GF_Err e; + GF_MovieFragmentBox *ptr = (GF_MovieFragmentBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + + if (ptr->mfhd) { + e = gf_isom_box_size((GF_Box *) ptr->mfhd); + if (e) return e; + ptr->size += ptr->mfhd->size; + } + return gf_isom_box_array_size(s, ptr->TrackList); +} + + + +#endif //GPAC_READ_ONLY + + +#endif + +void moov_del(GF_Box *s) +{ + GF_MovieBox *ptr = (GF_MovieBox *)s; + if (ptr == NULL) return; + + if (ptr->mvhd) gf_isom_box_del((GF_Box *)ptr->mvhd); + if (ptr->meta) gf_isom_box_del((GF_Box *)ptr->meta); + if (ptr->iods) gf_isom_box_del((GF_Box *)ptr->iods); + if (ptr->udta) gf_isom_box_del((GF_Box *)ptr->udta); +#ifndef GPAC_ISOM_NO_FRAGMENTS + if (ptr->mvex) gf_isom_box_del((GF_Box *)ptr->mvex); +#endif + + gf_isom_box_array_del(ptr->trackList); + gf_isom_box_array_del(ptr->boxes); + free(ptr); +} + + +GF_Err moov_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MovieBox *ptr = (GF_MovieBox *)s; + switch (a->type ) { + case GF_ISOM_BOX_TYPE_IODS: + if (ptr->iods) return GF_ISOM_INVALID_FILE; + ptr->iods = (GF_ObjectDescriptorBox *)a; + //if no IOD, delete the box + if (!ptr->iods->descriptor) { + ptr->iods = NULL; + gf_isom_box_del(a); + } + return GF_OK; + + case GF_ISOM_BOX_TYPE_MVHD: + if (ptr->mvhd) return GF_ISOM_INVALID_FILE; + ptr->mvhd = (GF_MovieHeaderBox *)a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_UDTA: + if (ptr->udta) return GF_ISOM_INVALID_FILE; + ptr->udta = (GF_UserDataBox *)a; + return GF_OK; + +#ifndef GPAC_ISOM_NO_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: + if (ptr->mvex) return GF_ISOM_INVALID_FILE; + ptr->mvex = (GF_MovieExtendsBox *)a; + ptr->mvex->mov = ptr->mov; + return GF_OK; +#endif + + case GF_ISOM_BOX_TYPE_META: + if (ptr->meta) return GF_ISOM_INVALID_FILE; + ptr->meta = (GF_MetaBox *)a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_TRAK: + //set our pointer to this obj + ((GF_TrackBox *)a)->moov = ptr; + return gf_list_add(ptr->trackList, a); + default: + return gf_list_add(ptr->boxes, a); + } +} + + + +GF_Err moov_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, moov_AddBox); +} + +GF_Box *moov_New() +{ + GF_MovieBox *tmp = (GF_MovieBox *) malloc(sizeof(GF_MovieBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MovieBox)); + tmp->trackList = gf_list_new(); + if (!tmp->trackList) { + free(tmp); + return NULL; + } + tmp->boxes = gf_list_new(); + if (!tmp->boxes) { + gf_list_del(tmp->trackList); + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_MOOV; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + + +GF_Err moov_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieBox *ptr = (GF_MovieBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + if (ptr->mvhd) { + e = gf_isom_box_write((GF_Box *) ptr->mvhd, bs); + if (e) return e; + } + if (ptr->iods) { + e = gf_isom_box_write((GF_Box *) ptr->iods, bs); + if (e) return e; + } + if (ptr->meta) { + e = gf_isom_box_write((GF_Box *) ptr->meta, bs); + if (e) return e; + } +#ifndef GPAC_ISOM_NO_FRAGMENTS + if (ptr->mvex) { + e = gf_isom_box_write((GF_Box *) ptr->mvex, bs); + if (e) return e; + } +#endif + + e = gf_isom_box_array_write(s, ptr->trackList, bs); + if (e) return e; + + if (ptr->udta) { + e = gf_isom_box_write((GF_Box *) ptr->udta, bs); + if (e) return e; + } + return gf_isom_box_array_write(s, ptr->boxes, bs); +} + +GF_Err moov_Size(GF_Box *s) +{ + GF_Err e; + GF_MovieBox *ptr = (GF_MovieBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + if (ptr->mvhd) { + e = gf_isom_box_size((GF_Box *) ptr->mvhd); + if (e) return e; + ptr->size += ptr->mvhd->size; + } + if (ptr->iods) { + e = gf_isom_box_size((GF_Box *) ptr->iods); + if (e) return e; + ptr->size += ptr->iods->size; + } + if (ptr->udta) { + e = gf_isom_box_size((GF_Box *) ptr->udta); + if (e) return e; + ptr->size += ptr->udta->size; + } + if (ptr->meta) { + e = gf_isom_box_size((GF_Box *) ptr->meta); + if (e) return e; + ptr->size += ptr->meta->size; + } +#ifndef GPAC_ISOM_NO_FRAGMENTS + if (ptr->mvex) { + e = gf_isom_box_size((GF_Box *) ptr->mvex); + if (e) return e; + ptr->size += ptr->mvex->size; + } +#endif + + e = gf_isom_box_array_size(s, ptr->trackList); + if (e) return e; + return gf_isom_box_array_size(s, ptr->boxes); +} + +#endif //GPAC_READ_ONLY + +void mp4a_del(GF_Box *s) +{ + GF_MPEGAudioSampleEntryBox *ptr = (GF_MPEGAudioSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->esd) gf_isom_box_del((GF_Box *)ptr->esd); + if (ptr->slc) gf_odf_desc_del((GF_Descriptor *)ptr->slc); + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + free(ptr); +} + +GF_Err mp4a_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MPEGAudioSampleEntryBox *ptr = (GF_MPEGAudioSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_ESDS: + if (ptr->esd) return GF_ISOM_INVALID_FILE; + ptr->esd = (GF_ESDBox *)a; + break; + case GF_ISOM_BOX_TYPE_SINF: + if (ptr->protection_info) return GF_ISOM_INVALID_FILE; + ptr->protection_info = (GF_ProtectionInfoBox*)a; + break; + case GF_4CC('w','a','v','e'): + if (ptr->esd) return GF_ISOM_INVALID_FILE; + /*HACK for QT files: get the esds box from the track*/ + { + GF_UnknownBox *wave = (GF_UnknownBox *)a; + u32 offset = 0; + while ((wave->data[offset+4]!='e') && (wave->data[offset+5]!='s')) { + offset++; + if (offset == wave->dataSize) break; + } + if (offset < wave->dataSize) { + GF_Box *a; + GF_Err e; + GF_BitStream *bs = gf_bs_new(wave->data+offset, wave->dataSize-offset, GF_BITSTREAM_READ); + e = gf_isom_parse_box(&a, bs); + gf_bs_del(bs); + ptr->esd = (GF_ESDBox *)a; + } + gf_isom_box_del(a); + } + break; + default: + gf_isom_box_del(a); + break; + } + return GF_OK; +} +GF_Err mp4a_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MPEGAudioSampleEntryBox *ptr; + char *data; + u32 i, size; + GF_Err e; + u64 pos; + + e = gf_isom_audio_sample_entry_read((GF_AudioSampleEntryBox*)s, bs); + if (e) return e; + pos = gf_bs_get_position(bs); + size = (u32) s->size; + + e = gf_isom_read_box_list(s, bs, mp4a_AddBox); + if (!e) return GF_OK; + + /*hack for some weird files (possibly recorded with live.com tools, needs further investigations)*/ + ptr = (GF_MPEGAudioSampleEntryBox *)s; + gf_bs_seek(bs, pos); + data = (char*)malloc(sizeof(char) * size); + gf_bs_read_data(bs, data, size); + for (i=0; iesd, mybs); + gf_bs_del(mybs); + break; + } + } + free(data); + return e; +} + +GF_Box *mp4a_New() +{ + GF_MPEGAudioSampleEntryBox *tmp = (GF_MPEGAudioSampleEntryBox *)malloc(sizeof(GF_MPEGAudioSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MPEGAudioSampleEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_MP4A; + gf_isom_audio_sample_entry_init((GF_AudioSampleEntryBox*)tmp); + return (GF_Box *)tmp; +} + +GF_Box *enca_New() +{ + GF_Box *tmp = mp4a_New(); + tmp->type = GF_ISOM_BOX_TYPE_ENCA; + return tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err mp4a_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MPEGAudioSampleEntryBox *ptr = (GF_MPEGAudioSampleEntryBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_isom_audio_sample_entry_write((GF_AudioSampleEntryBox*)s, bs); + e = gf_isom_box_write((GF_Box *)ptr->esd, bs); + if (e) return e; + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCA)) { + e = gf_isom_box_write((GF_Box *)ptr->protection_info, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err mp4a_Size(GF_Box *s) +{ + GF_Err e; + GF_MPEGAudioSampleEntryBox *ptr = (GF_MPEGAudioSampleEntryBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + gf_isom_audio_sample_entry_size((GF_AudioSampleEntryBox*)s); + + e = gf_isom_box_size((GF_Box *)ptr->esd); + if (e) return e; + ptr->size += ptr->esd->size; + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCA)) { + e = gf_isom_box_size((GF_Box *)ptr->protection_info); + if (e) return e; + ptr->size += ptr->protection_info->size; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void mp4s_del(GF_Box *s) +{ + GF_MPEGSampleEntryBox *ptr = (GF_MPEGSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->esd) gf_isom_box_del((GF_Box *)ptr->esd); + if (ptr->slc) gf_odf_desc_del((GF_Descriptor *)ptr->slc); + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + free(ptr); +} + +GF_Err mp4s_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MPEGSampleEntryBox *ptr = (GF_MPEGSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_ESDS: + if (ptr->esd) return GF_ISOM_INVALID_FILE; + ptr->esd = (GF_ESDBox *)a; + break; + case GF_ISOM_BOX_TYPE_SINF: + if (ptr->protection_info) return GF_ISOM_INVALID_FILE; + ptr->protection_info = (GF_ProtectionInfoBox*)a; + break; + default: + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err mp4s_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MPEGSampleEntryBox *ptr = (GF_MPEGSampleEntryBox *)s; + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->size -= 8; + return gf_isom_read_box_list(s, bs, mp4s_AddBox); +} + +GF_Box *mp4s_New() +{ + GF_MPEGSampleEntryBox *tmp = (GF_MPEGSampleEntryBox *) malloc(sizeof(GF_MPEGSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MPEGSampleEntryBox)); + + tmp->type = GF_ISOM_BOX_TYPE_MP4S; + return (GF_Box *)tmp; +} + +GF_Box *encs_New() +{ + GF_Box *tmp = mp4s_New(); + tmp->type = GF_ISOM_BOX_TYPE_ENCS; + return tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err mp4s_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MPEGSampleEntryBox *ptr = (GF_MPEGSampleEntryBox *)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + e = gf_isom_box_write((GF_Box *)ptr->esd, bs); + if (e) return e; + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCS)) { + e = gf_isom_box_write((GF_Box *)ptr->protection_info, bs); + } + return e; +} + +GF_Err mp4s_Size(GF_Box *s) +{ + GF_Err e; + GF_MPEGSampleEntryBox *ptr = (GF_MPEGSampleEntryBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 8; + e = gf_isom_box_size((GF_Box *)ptr->esd); + if (e) return e; + ptr->size += ptr->esd->size; + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCS)) { + e = gf_isom_box_size((GF_Box *)ptr->protection_info); + if (e) return e; + ptr->size += ptr->protection_info->size; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void mp4v_del(GF_Box *s) +{ + GF_MPEGVisualSampleEntryBox *ptr = (GF_MPEGVisualSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->esd) gf_isom_box_del((GF_Box *)ptr->esd); + if (ptr->slc) gf_odf_desc_del((GF_Descriptor *)ptr->slc); + if (ptr->avc_config) gf_isom_box_del((GF_Box *) ptr->avc_config); + if (ptr->bitrate) gf_isom_box_del((GF_Box *) ptr->bitrate); + if (ptr->descr) gf_isom_box_del((GF_Box *) ptr->descr); + if (ptr->ipod_ext) gf_isom_box_del((GF_Box *)ptr->ipod_ext); + /*for publishing*/ + if (ptr->emul_esd) gf_odf_desc_del((GF_Descriptor *)ptr->emul_esd); + + if (ptr->pasp) gf_isom_box_del((GF_Box *)ptr->pasp); + + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + free(ptr); +} + +GF_Err mp4v_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MPEGVisualSampleEntryBox *ptr = (GF_MPEGVisualSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_ESDS: + if (ptr->esd) return GF_ISOM_INVALID_FILE; + ptr->esd = (GF_ESDBox *)a; + break; + case GF_ISOM_BOX_TYPE_SINF: + if (ptr->protection_info) return GF_ISOM_INVALID_FILE; + ptr->protection_info = (GF_ProtectionInfoBox*)a; + break; + case GF_ISOM_BOX_TYPE_AVCC: + if (ptr->avc_config) return GF_ISOM_INVALID_FILE; + ptr->avc_config = (GF_AVCConfigurationBox *)a; + break; + case GF_ISOM_BOX_TYPE_BTRT: + if (ptr->bitrate) return GF_ISOM_INVALID_FILE; + ptr->bitrate = (GF_MPEG4BitRateBox *)a; + break; + case GF_ISOM_BOX_TYPE_M4DS: + if (ptr->descr) return GF_ISOM_INVALID_FILE; + ptr->descr = (GF_MPEG4ExtensionDescriptorsBox *)a; + break; + case GF_ISOM_BOX_TYPE_UUID: + if (ptr->ipod_ext) return GF_ISOM_INVALID_FILE; + ptr->ipod_ext = (GF_UnknownUUIDBox *)a; + break; + case GF_ISOM_BOX_TYPE_PASP: + if (ptr->pasp) return GF_ISOM_INVALID_FILE; + ptr->pasp = (GF_PixelAspectRatioBox *)a; + break; + default: + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err mp4v_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MPEGVisualSampleEntryBox *mp4v = (GF_MPEGVisualSampleEntryBox*)s; + GF_Err e; + e = gf_isom_video_sample_entry_read((GF_VisualSampleEntryBox *)s, bs); + if (e) return e; + e = gf_isom_read_box_list(s, bs, mp4v_AddBox); + if (e) return e; + /*this is an AVC sample desc*/ + if (mp4v->avc_config) AVC_RewriteESDescriptor(mp4v); + return GF_OK; +} + +static GF_Box *mp4v_encv_avc1_new(u32 type) +{ + GF_MPEGVisualSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_MPEGVisualSampleEntryBox); + if (tmp == NULL) return NULL; + + gf_isom_video_sample_entry_init((GF_VisualSampleEntryBox *)tmp); + tmp->type = type; + return (GF_Box *)tmp; +} +GF_Box *mp4v_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_MP4V); +} + +GF_Box *avc1_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_AVC1); +} + +GF_Box *encv_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_ENCV); +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err mp4v_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MPEGVisualSampleEntryBox *ptr = (GF_MPEGVisualSampleEntryBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_isom_video_sample_entry_write((GF_VisualSampleEntryBox *)s, bs); + + if (ptr->pasp) { + e = gf_isom_box_write((GF_Box *)ptr->pasp, bs); + if (e) return e; + } + + /*mp4v*/ + if (ptr->esd) { + e = gf_isom_box_write((GF_Box *)ptr->esd, bs); + if (e) return e; + } + /*avc1*/ + else { + if (ptr->avc_config && ptr->avc_config->config) { + e = gf_isom_box_write((GF_Box *) ptr->avc_config, bs); + if (e) return e; + } + if (ptr->ipod_ext) { + e = gf_isom_box_write((GF_Box *) ptr->ipod_ext, bs); + if (e) return e; + } + if (ptr->bitrate) { + e = gf_isom_box_write((GF_Box *) ptr->bitrate, bs); + if (e) return e; + } + if (ptr->descr) { + e = gf_isom_box_write((GF_Box *) ptr->descr, bs); + if (e) return e; + } + } + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCV)) { + e = gf_isom_box_write((GF_Box *)ptr->protection_info, bs); + if (e) return e; + } + return e; +} + +GF_Err mp4v_Size(GF_Box *s) +{ + GF_Err e; + GF_MPEGVisualSampleEntryBox *ptr = (GF_MPEGVisualSampleEntryBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + gf_isom_video_sample_entry_size((GF_VisualSampleEntryBox *)s); + + if (ptr->esd) { + e = gf_isom_box_size((GF_Box *)ptr->esd); + if (e) return e; + ptr->size += ptr->esd->size; + } else if (ptr->avc_config) { + if (ptr->avc_config && ptr->avc_config->config) { + e = gf_isom_box_size((GF_Box *) ptr->avc_config); + if (e) return e; + ptr->size += ptr->avc_config->size; + } + if (ptr->ipod_ext) { + e = gf_isom_box_size((GF_Box *) ptr->ipod_ext); + if (e) return e; + ptr->size += ptr->ipod_ext->size; + } + if (ptr->bitrate) { + e = gf_isom_box_size((GF_Box *) ptr->bitrate); + if (e) return e; + ptr->size += ptr->bitrate->size; + } + if (ptr->descr) { + e = gf_isom_box_size((GF_Box *) ptr->descr); + if (e) return e; + ptr->size += ptr->descr->size; + } + } else { + return GF_ISOM_INVALID_FILE; + } + if (ptr->pasp) { + e = gf_isom_box_size((GF_Box *)ptr->pasp); + if (e) return e; + ptr->size += ptr->pasp->size; + } + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCV)) { + e = gf_isom_box_size((GF_Box *)ptr->protection_info); + if (e) return e; + ptr->size += ptr->protection_info->size; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +void mvex_del(GF_Box *s) +{ + GF_MovieExtendsBox *ptr = (GF_MovieExtendsBox *)s; + if (ptr == NULL) return; + if (ptr->mehd) gf_isom_box_del((GF_Box*)ptr->mehd); + gf_isom_box_array_del(ptr->TrackExList); + free(ptr); +} + + +GF_Err mvex_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MovieExtendsBox *ptr = (GF_MovieExtendsBox *)s; + + switch (a->type) { + case GF_ISOM_BOX_TYPE_TREX: + return gf_list_add(ptr->TrackExList, a); + case GF_ISOM_BOX_TYPE_MEHD: + if (ptr->mehd) break; + ptr->mehd = (GF_MovieExtendsHeaderBox*)a; + return GF_OK; + } + gf_isom_box_del(a); + return GF_OK; +} + + + +GF_Err mvex_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, mvex_AddBox); +} + +GF_Box *mvex_New() +{ + GF_MovieExtendsBox *tmp = (GF_MovieExtendsBox *) malloc(sizeof(GF_MovieExtendsBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MovieExtendsBox)); + tmp->TrackExList = gf_list_new(); + if (!tmp->TrackExList) { + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_MVEX; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + + +GF_Err mvex_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieExtendsBox *ptr = (GF_MovieExtendsBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + return gf_isom_box_array_write(s, ptr->TrackExList, bs); +} + +GF_Err mvex_Size(GF_Box *s) +{ + GF_Err e; + GF_MovieExtendsBox *ptr = (GF_MovieExtendsBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + return gf_isom_box_array_size(s, ptr->TrackExList); +} + + + +#endif //GPAC_READ_ONLY + +GF_Box *mehd_New() +{ + GF_MovieExtendsHeaderBox *tmp; + GF_SAFEALLOC(tmp, GF_MovieExtendsHeaderBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_MEHD; + return (GF_Box *)tmp; +} +void mehd_del(GF_Box *s) +{ + free(s); +} +GF_Err mehd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieExtendsHeaderBox *ptr = (GF_MovieExtendsHeaderBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (ptr->version==1) { + ptr->fragment_duration = gf_bs_read_u64(bs); + } else { + ptr->fragment_duration = (u64) gf_bs_read_u32(bs); + } + return GF_OK; +} +#ifndef GPAC_READ_ONLY +GF_Err mehd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_MovieExtendsHeaderBox *ptr = (GF_MovieExtendsHeaderBox *)s; + GF_Err e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->version == 1) { + gf_bs_write_u64(bs, ptr->fragment_duration); + } else { + gf_bs_write_u32(bs, (u32) ptr->fragment_duration); + } + return GF_OK; +} +GF_Err mehd_Size(GF_Box *s) +{ + GF_Err e = gf_isom_full_box_get_size(s); + GF_MovieExtendsHeaderBox *ptr = (GF_MovieExtendsHeaderBox *)s; + if (e) return e; + ptr->version = (ptr->fragment_duration>0xFFFFFFFF) ? 1 : 0; + s->size += (ptr->version == 1) ? 8 : 4; + return GF_OK; +} +#endif + +#endif + + +void mvhd_del(GF_Box *s) +{ + GF_MovieHeaderBox *ptr = (GF_MovieHeaderBox *)s; + if (ptr == NULL) return; + free(ptr); +} + + +GF_Err mvhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieHeaderBox *ptr = (GF_MovieHeaderBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (ptr->version == 1) { + ptr->creationTime = gf_bs_read_u64(bs); + ptr->modificationTime = gf_bs_read_u64(bs); + ptr->timeScale = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u64(bs); + } else { + ptr->creationTime = gf_bs_read_u32(bs); + ptr->modificationTime = gf_bs_read_u32(bs); + ptr->timeScale = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u32(bs); + } + ptr->preferredRate = gf_bs_read_u32(bs); + ptr->preferredVolume = gf_bs_read_u16(bs); + gf_bs_read_data(bs, ptr->reserved, 10); + ptr->matrixA = gf_bs_read_u32(bs); + ptr->matrixB = gf_bs_read_u32(bs); + ptr->matrixU = gf_bs_read_u32(bs); + ptr->matrixC = gf_bs_read_u32(bs); + ptr->matrixD = gf_bs_read_u32(bs); + ptr->matrixV = gf_bs_read_u32(bs); + ptr->matrixX = gf_bs_read_u32(bs); + ptr->matrixY = gf_bs_read_u32(bs); + ptr->matrixW = gf_bs_read_u32(bs); + ptr->previewTime = gf_bs_read_u32(bs); + ptr->previewDuration = gf_bs_read_u32(bs); + ptr->posterTime = gf_bs_read_u32(bs); + ptr->selectionTime = gf_bs_read_u32(bs); + ptr->selectionDuration = gf_bs_read_u32(bs); + ptr->currentTime = gf_bs_read_u32(bs); + ptr->nextTrackID = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *mvhd_New() +{ + GF_MovieHeaderBox *tmp = (GF_MovieHeaderBox *) malloc(sizeof(GF_MovieHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MovieHeaderBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_MVHD; + tmp->preferredRate = (1<<16); + tmp->preferredVolume = (1<<8); + + tmp->matrixA = (1<<16); + tmp->matrixD = (1<<16); + tmp->matrixW = (1<<30); + + tmp->nextTrackID = 1; + + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err mvhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieHeaderBox *ptr = (GF_MovieHeaderBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->version == 1) { + gf_bs_write_u64(bs, ptr->creationTime); + gf_bs_write_u64(bs, ptr->modificationTime); + gf_bs_write_u32(bs, ptr->timeScale); + gf_bs_write_u64(bs, ptr->duration); + } else { + gf_bs_write_u32(bs, (u32) ptr->creationTime); + gf_bs_write_u32(bs, (u32) ptr->modificationTime); + gf_bs_write_u32(bs, ptr->timeScale); + gf_bs_write_u32(bs, (u32) ptr->duration); + } + gf_bs_write_u32(bs, ptr->preferredRate); + gf_bs_write_u16(bs, ptr->preferredVolume); + gf_bs_write_data(bs, ptr->reserved, 10); + gf_bs_write_u32(bs, ptr->matrixA); + gf_bs_write_u32(bs, ptr->matrixB); + gf_bs_write_u32(bs, ptr->matrixU); + gf_bs_write_u32(bs, ptr->matrixC); + gf_bs_write_u32(bs, ptr->matrixD); + gf_bs_write_u32(bs, ptr->matrixV); + gf_bs_write_u32(bs, ptr->matrixX); + gf_bs_write_u32(bs, ptr->matrixY); + gf_bs_write_u32(bs, ptr->matrixW); + gf_bs_write_u32(bs, ptr->previewTime); + gf_bs_write_u32(bs, ptr->previewDuration); + gf_bs_write_u32(bs, ptr->posterTime); + gf_bs_write_u32(bs, ptr->selectionTime); + gf_bs_write_u32(bs, ptr->selectionDuration); + gf_bs_write_u32(bs, ptr->currentTime); + gf_bs_write_u32(bs, ptr->nextTrackID); + return GF_OK; +} + +GF_Err mvhd_Size(GF_Box *s) +{ + GF_Err e; + GF_MovieHeaderBox *ptr = (GF_MovieHeaderBox *)s; + ptr->version = (ptr->duration>0xFFFFFFFF) ? 1 : 0; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += (ptr->version == 1) ? 28 : 16; + ptr->size += 80; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void nmhd_del(GF_Box *s) +{ + GF_MPEGMediaHeaderBox *ptr = (GF_MPEGMediaHeaderBox *)s; + if (ptr == NULL) return; + free(ptr); +} + + + +GF_Err nmhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e = gf_isom_full_box_read(s, bs); + if (e) return e; + return GF_OK; +} + +GF_Box *nmhd_New() +{ + GF_MPEGMediaHeaderBox *tmp = (GF_MPEGMediaHeaderBox *) malloc(sizeof(GF_MPEGMediaHeaderBox)); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *) tmp); + tmp->type = GF_ISOM_BOX_TYPE_NMHD; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err nmhd_Write(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_full_box_write(s, bs); +} + +GF_Err nmhd_Size(GF_Box *s) +{ + return gf_isom_full_box_get_size(s); +} + +#endif //GPAC_READ_ONLY + + + +void padb_del(GF_Box *s) +{ + GF_PaddingBitsBox *ptr = (GF_PaddingBitsBox *) s; + if (ptr == NULL) return; + if (ptr->padbits) free(ptr->padbits); + free(ptr); +} + + +GF_Err padb_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_PaddingBitsBox *ptr = (GF_PaddingBitsBox *)s; + + e = gf_isom_full_box_read( s, bs); + if (e) return e; + + ptr->SampleCount = gf_bs_read_u32(bs); + + ptr->padbits = (u8 *)malloc(sizeof(u8)*ptr->SampleCount); + for (i=0; iSampleCount; i += 2) { + gf_bs_read_int(bs, 1); + if (i+1 < ptr->SampleCount) { + ptr->padbits[i+1] = gf_bs_read_int(bs, 3); + } else { + gf_bs_read_int(bs, 3); + } + gf_bs_read_int(bs, 1); + ptr->padbits[i] = gf_bs_read_int(bs, 3); + } + return GF_OK; +} + +GF_Box *padb_New() +{ + GF_PaddingBitsBox *tmp; + + tmp = (GF_PaddingBitsBox *) malloc(sizeof(GF_PaddingBitsBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_PaddingBitsBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_FADB; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err padb_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_Err e; + GF_PaddingBitsBox *ptr = (GF_PaddingBitsBox *) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_int(bs, ptr->SampleCount, 32); + + for (i=0 ; iSampleCount; i += 2) { + gf_bs_write_int(bs, 0, 1); + if (i+1 < ptr->SampleCount) { + gf_bs_write_int(bs, ptr->padbits[i+1], 3); + } else { + gf_bs_write_int(bs, 0, 3); + } + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, ptr->padbits[i], 3); + } + return GF_OK; +} + +GF_Err padb_Size(GF_Box *s) +{ + GF_Err e; + GF_PaddingBitsBox *ptr = (GF_PaddingBitsBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + if (ptr->SampleCount) ptr->size += (ptr->SampleCount + 1) / 2; + + + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void rely_del(GF_Box *s) +{ + GF_RelyHintBox *rely = (GF_RelyHintBox *)s; + free(rely); +} + +GF_Err rely_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_RelyHintBox *ptr = (GF_RelyHintBox *)s; + ptr->reserved = gf_bs_read_int(bs, 6); + ptr->prefered = gf_bs_read_int(bs, 1); + ptr->required = gf_bs_read_int(bs, 1); + return GF_OK; +} + +GF_Box *rely_New() +{ + GF_RelyHintBox *tmp = (GF_RelyHintBox *)malloc(sizeof(GF_RelyHintBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_RelyHintBox)); + tmp->type = GF_ISOM_BOX_TYPE_RELY; + + return (GF_Box *)tmp; +} + + +#ifndef GPAC_READ_ONLY +GF_Err rely_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_RelyHintBox *ptr = (GF_RelyHintBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_int(bs, ptr->reserved, 6); + gf_bs_write_int(bs, ptr->prefered, 1); + gf_bs_write_int(bs, ptr->required, 1); + return GF_OK; +} + +GF_Err rely_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 1; + return GF_OK; +} +#endif + + +void rtpo_del(GF_Box *s) +{ + GF_RTPOBox *rtpo = (GF_RTPOBox *)s; + free(rtpo); +} + +GF_Err rtpo_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_RTPOBox *ptr = (GF_RTPOBox *)s; + ptr->timeOffset = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *rtpo_New() +{ + GF_RTPOBox *tmp = (GF_RTPOBox *) malloc(sizeof(GF_RTPOBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_RTPOBox)); + tmp->type = GF_ISOM_BOX_TYPE_RTPO; + return (GF_Box *)tmp; +} +#ifndef GPAC_READ_ONLY +GF_Err rtpo_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_RTPOBox *ptr = (GF_RTPOBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + //here we have no pb, just remembed that some entries will have to + //be 4-bytes aligned ... + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->timeOffset); + return GF_OK; +} + +GF_Err rtpo_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif + +void smhd_del(GF_Box *s) +{ + GF_SoundMediaHeaderBox *ptr = (GF_SoundMediaHeaderBox *)s; + if (ptr == NULL ) return; + free(ptr); +} + + +GF_Err smhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SoundMediaHeaderBox *ptr = (GF_SoundMediaHeaderBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->reserved = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *smhd_New() +{ + GF_SoundMediaHeaderBox *tmp = (GF_SoundMediaHeaderBox *) malloc(sizeof(GF_SoundMediaHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SoundMediaHeaderBox)); + gf_isom_full_box_init((GF_Box *) tmp); + tmp->type = GF_ISOM_BOX_TYPE_SMHD; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err smhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SoundMediaHeaderBox *ptr = (GF_SoundMediaHeaderBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->reserved); + return GF_OK; +} + +GF_Err smhd_Size(GF_Box *s) +{ + GF_Err e; + GF_SoundMediaHeaderBox *ptr = (GF_SoundMediaHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->reserved = 0; + ptr->size += 4; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +void snro_del(GF_Box *s) +{ + GF_SeqOffHintEntryBox *snro = (GF_SeqOffHintEntryBox *)s; + free(snro); +} + +GF_Err snro_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_SeqOffHintEntryBox *ptr = (GF_SeqOffHintEntryBox *)s; + ptr->SeqOffset = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *snro_New() +{ + GF_SeqOffHintEntryBox *tmp = (GF_SeqOffHintEntryBox *) malloc(sizeof(GF_SeqOffHintEntryBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_SeqOffHintEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_SNRO; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_READ_ONLY +GF_Err snro_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SeqOffHintEntryBox *ptr = (GF_SeqOffHintEntryBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->SeqOffset); + return GF_OK; +} + +GF_Err snro_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif + + +#define WRITE_SAMPLE_FRAGMENTS 1 + +void stbl_del(GF_Box *s) +{ + GF_SampleTableBox *ptr = (GF_SampleTableBox *)s; + if (ptr == NULL) return; + + if (ptr->ChunkOffset) gf_isom_box_del(ptr->ChunkOffset); + if (ptr->CompositionOffset) gf_isom_box_del((GF_Box *) ptr->CompositionOffset); + if (ptr->DegradationPriority) gf_isom_box_del((GF_Box *) ptr->DegradationPriority); + if (ptr->SampleDescription) gf_isom_box_del((GF_Box *) ptr->SampleDescription); + if (ptr->SampleSize) gf_isom_box_del((GF_Box *) ptr->SampleSize); + if (ptr->SampleToChunk) gf_isom_box_del((GF_Box *) ptr->SampleToChunk); + if (ptr->ShadowSync) gf_isom_box_del((GF_Box *) ptr->ShadowSync); + if (ptr->SyncSample) gf_isom_box_del((GF_Box *) ptr->SyncSample); + if (ptr->TimeToSample) gf_isom_box_del((GF_Box *) ptr->TimeToSample); + if (ptr->SampleDep) gf_isom_box_del((GF_Box *) ptr->SampleDep); + if (ptr->PaddingBits) gf_isom_box_del((GF_Box *) ptr->PaddingBits); + if (ptr->Fragments) gf_isom_box_del((GF_Box *) ptr->Fragments); + + free(ptr); +} + +GF_Err stbl_AddBox(GF_SampleTableBox *ptr, GF_Box *a) +{ + if (!a) return GF_OK; + switch (a->type) { + case GF_ISOM_BOX_TYPE_STTS: + if (ptr->TimeToSample) return GF_ISOM_INVALID_FILE; + ptr->TimeToSample = (GF_TimeToSampleBox *)a; + break; + case GF_ISOM_BOX_TYPE_CTTS: + if (ptr->CompositionOffset) return GF_ISOM_INVALID_FILE; + ptr->CompositionOffset = (GF_CompositionOffsetBox *)a; + break; + case GF_ISOM_BOX_TYPE_STSS: + if (ptr->SyncSample) return GF_ISOM_INVALID_FILE; + ptr->SyncSample = (GF_SyncSampleBox *)a; + break; + case GF_ISOM_BOX_TYPE_STSD: + if (ptr->SampleDescription) return GF_ISOM_INVALID_FILE; + ptr->SampleDescription =(GF_SampleDescriptionBox *)a; + break; + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + if (ptr->SampleSize) return GF_ISOM_INVALID_FILE; + ptr->SampleSize = (GF_SampleSizeBox *)a; + break; + case GF_ISOM_BOX_TYPE_STSC: + if (ptr->SampleToChunk) return GF_ISOM_INVALID_FILE; + ptr->SampleToChunk = (GF_SampleToChunkBox *)a; + break; + case GF_ISOM_BOX_TYPE_FADB: + if (ptr->PaddingBits) return GF_ISOM_INVALID_FILE; + ptr->PaddingBits = (GF_PaddingBitsBox *) a; + break; + + //WARNING: AS THIS MAY CHANGE DYNAMICALLY DURING EDIT, + case GF_ISOM_BOX_TYPE_CO64: + case GF_ISOM_BOX_TYPE_STCO: + if (ptr->ChunkOffset) { + gf_isom_box_del(ptr->ChunkOffset); + } + ptr->ChunkOffset = a; + return GF_OK; + case GF_ISOM_BOX_TYPE_STSH: + if (ptr->ShadowSync) return GF_ISOM_INVALID_FILE; + ptr->ShadowSync = (GF_ShadowSyncBox *)a; + break; + case GF_ISOM_BOX_TYPE_STDP: + if (ptr->DegradationPriority) return GF_ISOM_INVALID_FILE; + ptr->DegradationPriority = (GF_DegradationPriorityBox *)a; + break; + case GF_ISOM_BOX_TYPE_SDTP: + if (ptr->SampleDep) return GF_ISOM_INVALID_FILE; + ptr->SampleDep= (GF_SampleDependencyTypeBox *)a; + break; + + case GF_ISOM_BOX_TYPE_STSF: + if (ptr->Fragments) return GF_ISOM_INVALID_FILE; + ptr->Fragments = (GF_SampleFragmentBox *)a; + break; + + //what's this box ??? delete it + default: + gf_isom_box_del(a); + } + return GF_OK; +} + + + + +GF_Err stbl_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_Box *a; + //we need to parse DegPrior in a special way + GF_SampleTableBox *ptr = (GF_SampleTableBox *)s; + + while (ptr->size) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + //we need to read the DegPriority in a different way... + if ((a->type == GF_ISOM_BOX_TYPE_STDP) || (a->type == GF_ISOM_BOX_TYPE_SDTP)) { + u64 s = a->size; +/* + if (!ptr->SampleSize) { + gf_isom_box_del(a); + return GF_ISOM_INVALID_FILE; + } +*/ + if (a->type == GF_ISOM_BOX_TYPE_STDP) { + if (ptr->SampleSize) ((GF_DegradationPriorityBox *)a)->nb_entries = ptr->SampleSize->sampleCount; + e = stdp_Read(a, bs); + } else { + if (ptr->SampleSize) ((GF_SampleDependencyTypeBox *)a)->sampleCount = ptr->SampleSize->sampleCount; + e = sdtp_Read(a, bs); + } + if (e) { + gf_isom_box_del(a); + return e; + } + a->size = s; + } + if (ptr->sizesize) { + gf_isom_box_del(a); + return GF_ISOM_INVALID_FILE; + } + ptr->size -= a->size; + e = stbl_AddBox(ptr, a); + if (e) return e; + } + return GF_OK; +} + +GF_Box *stbl_New() +{ + GF_SampleTableBox *tmp = (GF_SampleTableBox *) malloc(sizeof(GF_SampleTableBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleTableBox)); + + tmp->type = GF_ISOM_BOX_TYPE_STBL; + //maxSamplePer chunk is 10 by default + tmp->MaxSamplePerChunk = 10; + tmp->groupID = 1; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err stbl_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SampleTableBox *ptr = (GF_SampleTableBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + if (ptr->SampleDescription) { + e = gf_isom_box_write((GF_Box *) ptr->SampleDescription, bs); + if (e) return e; + } + if (ptr->TimeToSample) { + e = gf_isom_box_write((GF_Box *) ptr->TimeToSample, bs); + if (e) return e; + } + if (ptr->CompositionOffset) { + e = gf_isom_box_write((GF_Box *) ptr->CompositionOffset, bs); + if (e) return e; + } + if (ptr->SyncSample) { + e = gf_isom_box_write((GF_Box *) ptr->SyncSample, bs); + if (e) return e; + } + if (ptr->ShadowSync) { + e = gf_isom_box_write((GF_Box *) ptr->ShadowSync, bs); + if (e) return e; + } + if (ptr->SampleToChunk) { + e = gf_isom_box_write((GF_Box *) ptr->SampleToChunk, bs); + if (e) return e; + } + if (ptr->SampleSize) { + e = gf_isom_box_write((GF_Box *) ptr->SampleSize, bs); + if (e) return e; + } + if (ptr->ChunkOffset) { + e = gf_isom_box_write(ptr->ChunkOffset, bs); + if (e) return e; + } + if (ptr->DegradationPriority) { + e = gf_isom_box_write((GF_Box *) ptr->DegradationPriority, bs); + if (e) return e; + } + if (ptr->SampleDep && ptr->SampleDep->sampleCount) { + e = gf_isom_box_write((GF_Box *) ptr->SampleDep, bs); + if (e) return e; + } + if (ptr->PaddingBits) { + e = gf_isom_box_write((GF_Box *) ptr->PaddingBits, bs); + if (e) return e; + } + +#if WRITE_SAMPLE_FRAGMENTS + //sampleFragments + if (ptr->Fragments) { + e = gf_isom_box_write((GF_Box *) ptr->Fragments, bs); + if (e) return e; + } +#endif + return GF_OK; +} + +GF_Err stbl_Size(GF_Box *s) +{ + GF_Err e; + GF_SampleTableBox *ptr = (GF_SampleTableBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + //Mandatory boxs (but not internally :) + if (ptr->SampleDescription) { + e = gf_isom_box_size((GF_Box *) ptr->SampleDescription); + if (e) return e; + ptr->size += ptr->SampleDescription->size; + } + if (ptr->SampleSize) { + e = gf_isom_box_size((GF_Box *) ptr->SampleSize); + if (e) return e; + ptr->size += ptr->SampleSize->size; + } + if (ptr->SampleToChunk) { + e = gf_isom_box_size((GF_Box *) ptr->SampleToChunk); + if (e) return e; + ptr->size += ptr->SampleToChunk->size; + } + if (ptr->TimeToSample) { + e = gf_isom_box_size((GF_Box *) ptr->TimeToSample); + if (e) return e; + ptr->size += ptr->TimeToSample->size; + } + if (ptr->ChunkOffset) { + e = gf_isom_box_size(ptr->ChunkOffset); + if (e) return e; + ptr->size += ptr->ChunkOffset->size; + } + + //optional boxs + if (ptr->CompositionOffset) { + e = gf_isom_box_size((GF_Box *) ptr->CompositionOffset); + if (e) return e; + ptr->size += ptr->CompositionOffset->size; + } + if (ptr->DegradationPriority) { + e = gf_isom_box_size((GF_Box *) ptr->DegradationPriority); + if (e) return e; + ptr->size += ptr->DegradationPriority->size; + } + if (ptr->ShadowSync) { + e = gf_isom_box_size((GF_Box *) ptr->ShadowSync); + if (e) return e; + ptr->size += ptr->ShadowSync->size; + } + if (ptr->SyncSample) { + e = gf_isom_box_size((GF_Box *) ptr->SyncSample); + if (e) return e; + ptr->size += ptr->SyncSample->size; + } + if (ptr->SampleDep && ptr->SampleDep->sampleCount) { + e = gf_isom_box_size((GF_Box *) ptr->SampleDep); + if (e) return e; + ptr->size += ptr->SampleDep->size; + } + //padb + if (ptr->PaddingBits) { + e = gf_isom_box_size((GF_Box *) ptr->PaddingBits); + if (e) return e; + ptr->size += ptr->PaddingBits->size; + } +#if WRITE_SAMPLE_FRAGMENTS + //sample fragments + if (ptr->Fragments) { + e = gf_isom_box_size((GF_Box *) ptr->Fragments); + if (e) return e; + ptr->size += ptr->Fragments->size; + } +#endif + + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void stco_del(GF_Box *s) +{ + GF_ChunkOffsetBox *ptr = (GF_ChunkOffsetBox *)s; + if (ptr == NULL) return; + if (ptr->offsets) free(ptr->offsets); + free(ptr); +} + + +GF_Err stco_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 entries; + GF_ChunkOffsetBox *ptr = (GF_ChunkOffsetBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->nb_entries = gf_bs_read_u32(bs); + + if (ptr->nb_entries) { + ptr->offsets = (u32 *) malloc(ptr->nb_entries * sizeof(u32) ); + if (ptr->offsets == NULL) return GF_OUT_OF_MEM; + ptr->alloc_size = ptr->nb_entries; + + for (entries = 0; entries < ptr->nb_entries; entries++) { + ptr->offsets[entries] = gf_bs_read_u32(bs); + } + } + return GF_OK; +} + +GF_Box *stco_New() +{ + GF_ChunkOffsetBox *tmp = (GF_ChunkOffsetBox *) malloc(sizeof(GF_ChunkOffsetBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ChunkOffsetBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_STCO; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err stco_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_ChunkOffsetBox *ptr = (GF_ChunkOffsetBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i = 0; i < ptr->nb_entries; i++) { + gf_bs_write_u32(bs, ptr->offsets[i]); + } + return GF_OK; +} + + +GF_Err stco_Size(GF_Box *s) +{ + GF_Err e; + GF_ChunkOffsetBox *ptr = (GF_ChunkOffsetBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (4 * ptr->nb_entries); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +void stdp_del(GF_Box *s) +{ + GF_DegradationPriorityBox *ptr = (GF_DegradationPriorityBox *)s; + if (ptr == NULL ) return; + if (ptr->priorities) free(ptr->priorities); + free(ptr); +} + +//this is called through stbl_read... +GF_Err stdp_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 entry; + GF_DegradationPriorityBox *ptr = (GF_DegradationPriorityBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + /*out-of-order stdp, assume no padding at the end*/ + if (!ptr->nb_entries) ptr->nb_entries = (u32) (ptr->size-8) / 2; + ptr->priorities = (u16 *) malloc(ptr->nb_entries * sizeof(u16)); + if (ptr->priorities == NULL) return GF_OUT_OF_MEM; + for (entry = 0; entry < ptr->nb_entries; entry++) { + //we have a bit for padding + gf_bs_read_int(bs, 1); + ptr->priorities[entry] = gf_bs_read_int(bs, 15); + } + return GF_OK; +} + +GF_Box *stdp_New() +{ + GF_DegradationPriorityBox *tmp = (GF_DegradationPriorityBox *) malloc(sizeof(GF_DegradationPriorityBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DegradationPriorityBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_STDP; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err stdp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_DegradationPriorityBox *ptr = (GF_DegradationPriorityBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + for (i = 0; i < ptr->nb_entries; i++) { + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, ptr->priorities[i], 15); + } + return GF_OK; +} + +GF_Err stdp_Size(GF_Box *s) +{ + GF_Err e; + GF_DegradationPriorityBox *ptr = (GF_DegradationPriorityBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += (2 * ptr->nb_entries); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void stsc_del(GF_Box *s) +{ + GF_SampleToChunkBox *ptr = (GF_SampleToChunkBox *)s; + if (ptr == NULL) return; + if (ptr->entries) free(ptr->entries); + free(ptr); +} + + +GF_Err stsc_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SampleToChunkBox *ptr = (GF_SampleToChunkBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->nb_entries = gf_bs_read_u32(bs); + ptr->alloc_size = ptr->nb_entries; + ptr->entries = malloc(sizeof(GF_StscEntry)*ptr->alloc_size); + if (!ptr->entries) return GF_OUT_OF_MEM; + + for (i = 0; i < ptr->nb_entries; i++) { + ptr->entries[i].firstChunk = gf_bs_read_u32(bs); + ptr->entries[i].samplesPerChunk = gf_bs_read_u32(bs); + ptr->entries[i].sampleDescriptionIndex = gf_bs_read_u32(bs); + ptr->entries[i].isEdited = 0; + ptr->entries[i].nextChunk = 0; + + //update the next chunk in the previous entry + if (i) ptr->entries[i-1].nextChunk = ptr->entries[i].firstChunk; + } + ptr->currentIndex = 0; + ptr->firstSampleInCurrentChunk = 0; + ptr->currentChunk = 0; + ptr->ghostNumber = 0; + return GF_OK; +} + +GF_Box *stsc_New() +{ + GF_SampleToChunkBox *tmp = (GF_SampleToChunkBox *) malloc(sizeof(GF_SampleToChunkBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleToChunkBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_STSC; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err stsc_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SampleToChunkBox *ptr = (GF_SampleToChunkBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i=0; inb_entries; i++) { + gf_bs_write_u32(bs, ptr->entries[i].firstChunk); + gf_bs_write_u32(bs, ptr->entries[i].samplesPerChunk); + gf_bs_write_u32(bs, ptr->entries[i].sampleDescriptionIndex); + } + return GF_OK; +} + +GF_Err stsc_Size(GF_Box *s) +{ + GF_Err e; + GF_SampleToChunkBox *ptr = (GF_SampleToChunkBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (12 * ptr->nb_entries); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void stsd_del(GF_Box *s) +{ + GF_SampleDescriptionBox *ptr = (GF_SampleDescriptionBox *)s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->boxList); + free(ptr); +} + +GF_Err stsd_AddBox(GF_SampleDescriptionBox *ptr, GF_Box *a) +{ + GF_UnknownBox *def; + if (!a) return GF_OK; + + switch (a->type) { + case GF_ISOM_BOX_TYPE_MP4S: + case GF_ISOM_BOX_TYPE_ENCS: + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_GHNT: + case GF_ISOM_BOX_TYPE_RTP_STSD: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_TX3G: + case GF_ISOM_BOX_TYPE_ENCT: + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + case GF_ISOM_BOX_TYPE_DIMS: + case GF_ISOM_BOX_TYPE_AC3: + return gf_list_add(ptr->boxList, a); + /*for 3GP config, we must set the type*/ + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + { + ((GF_3GPPAudioSampleEntryBox *)a)->info->cfg.type = a->type; + return gf_list_add(ptr->boxList, a); + } + case GF_ISOM_SUBTYPE_3GP_H263: + { + ((GF_3GPPVisualSampleEntryBox *)a)->info->cfg.type = a->type; + return gf_list_add(ptr->boxList, a); + } + + //unknown sample description: we need a specific box to handle the data ref index + //rather than a default box ... + default: + def = (GF_UnknownBox *)a; + /*we need at least 8 bytes for unknown sample entries*/ + if (def->dataSize < 8) { + gf_isom_box_del(a); + return GF_OK; + } + return gf_list_add(ptr->boxList, a); + } +} + + +GF_Err stsd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 nb_entries; + u32 i; + GF_Box *a; + GF_SampleDescriptionBox *ptr = (GF_SampleDescriptionBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + nb_entries = gf_bs_read_u32(bs); + for (i = 0; i < nb_entries; i++) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + e = stsd_AddBox(ptr, a); + if (e) return e; + } + return GF_OK; +} + +GF_Box *stsd_New() +{ + GF_SampleDescriptionBox *tmp = (GF_SampleDescriptionBox *) malloc(sizeof(GF_SampleDescriptionBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleDescriptionBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->boxList = gf_list_new(); + if (! tmp->boxList) { + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_STSD; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err stsd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 nb_entries; + GF_SampleDescriptionBox *ptr = (GF_SampleDescriptionBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + nb_entries = gf_list_count(ptr->boxList); + gf_bs_write_u32(bs, nb_entries); + return gf_isom_box_array_write(s, ptr->boxList, bs); +} + +GF_Err stsd_Size(GF_Box *s) +{ + GF_Err e; + GF_SampleDescriptionBox *ptr = (GF_SampleDescriptionBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + return gf_isom_box_array_size(s, ptr->boxList); +} + +#endif //GPAC_READ_ONLY + +void stsf_del(GF_Box *s) +{ + u32 nb_entries; + u32 i; + GF_StsfEntry *pe; + GF_SampleFragmentBox *ptr = (GF_SampleFragmentBox *)s; + if (ptr == NULL) return; + + if (ptr->entryList) { + nb_entries = gf_list_count(ptr->entryList); + for ( i = 0; i < nb_entries; i++ ) { + pe = (GF_StsfEntry*)gf_list_get(ptr->entryList, i); + if (pe->fragmentSizes) free(pe->fragmentSizes); + free(pe); + } + gf_list_del(ptr->entryList); + } + free(ptr); +} + + + +GF_Err stsf_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 entries, i; + u32 nb_entries; + GF_StsfEntry *p; + GF_SampleFragmentBox *ptr = (GF_SampleFragmentBox *)s; + + p = NULL; + if (!ptr) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + nb_entries = gf_bs_read_u32(bs); + + p = NULL; + for ( entries = 0; entries < nb_entries; entries++ ) { + p = (GF_StsfEntry *) malloc(sizeof(GF_StsfEntry)); + if (!p) return GF_OUT_OF_MEM; + p->SampleNumber = gf_bs_read_u32(bs); + p->fragmentCount = gf_bs_read_u32(bs); + p->fragmentSizes = (u16*)malloc(sizeof(GF_StsfEntry) * p->fragmentCount); + for (i=0; ifragmentCount; i++) { + p->fragmentSizes[i] = gf_bs_read_u16(bs); + } + gf_list_add(ptr->entryList, p); + } +#ifndef GPAC_READ_ONLY + ptr->w_currentEntry = p; + ptr->w_currentEntryIndex = nb_entries-1; +#endif + return GF_OK; +} + +GF_Box *stsf_New() +{ + GF_SampleFragmentBox *tmp; + + tmp = (GF_SampleFragmentBox *) malloc(sizeof(GF_SampleFragmentBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleFragmentBox)); + + gf_isom_full_box_init((GF_Box *) tmp); + tmp->entryList = gf_list_new(); + if (! tmp->entryList) { + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_STSF; + return (GF_Box *) tmp; +} + + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err stsf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, j; + u32 nb_entries; + GF_StsfEntry *p; + GF_SampleFragmentBox *ptr = (GF_SampleFragmentBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + nb_entries = gf_list_count(ptr->entryList); + gf_bs_write_u32(bs, nb_entries); + for ( i = 0; i < nb_entries; i++ ) { + p = (GF_StsfEntry*)gf_list_get(ptr->entryList, i); + gf_bs_write_u32(bs, p->SampleNumber); + gf_bs_write_u32(bs, p->fragmentCount); + for (j=0;jfragmentCount;j++) { + gf_bs_write_u16(bs, p->fragmentSizes[j]); + } + } + return GF_OK; +} + +GF_Err stsf_Size(GF_Box *s) +{ + GF_Err e; + GF_StsfEntry *p; + u32 nb_entries, i; + GF_SampleFragmentBox *ptr = (GF_SampleFragmentBox *) s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + nb_entries = gf_list_count(ptr->entryList); + ptr->size += 4; + for (i=0;ientryList, i); + ptr->size += 8 + 2*p->fragmentCount; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +void stsh_del(GF_Box *s) +{ + u32 i = 0; + GF_StshEntry *ent; + GF_ShadowSyncBox *ptr = (GF_ShadowSyncBox *)s; + if (ptr == NULL) return; + while ( (ent = (GF_StshEntry *)gf_list_enum(ptr->entries, &i)) ) { + free(ent); + } + gf_list_del(ptr->entries); + free(ptr); +} + + + +GF_Err stsh_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 count, i; + GF_StshEntry *ent; + GF_ShadowSyncBox *ptr = (GF_ShadowSyncBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + count = gf_bs_read_u32(bs); + + for (i = 0; i < count; i++) { + ent = (GF_StshEntry *) malloc(sizeof(GF_StshEntry)); + if (!ent) return GF_OUT_OF_MEM; + ent->shadowedSampleNumber = gf_bs_read_u32(bs); + ent->syncSampleNumber = gf_bs_read_u32(bs); + e = gf_list_add(ptr->entries, ent); + if (e) return e; + } + return GF_OK; +} + +GF_Box *stsh_New() +{ + GF_ShadowSyncBox *tmp = (GF_ShadowSyncBox *) malloc(sizeof(GF_ShadowSyncBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ShadowSyncBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->entries = gf_list_new(); + if (!tmp->entries) { + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_STSH; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err stsh_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_StshEntry *ent; + GF_ShadowSyncBox *ptr = (GF_ShadowSyncBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, gf_list_count(ptr->entries)); + i=0; + while ((ent = (GF_StshEntry *)gf_list_enum(ptr->entries, &i))) { + gf_bs_write_u32(bs, ent->shadowedSampleNumber); + gf_bs_write_u32(bs, ent->syncSampleNumber); + } + return GF_OK; +} + +GF_Err stsh_Size(GF_Box *s) +{ + GF_Err e; + GF_ShadowSyncBox *ptr = (GF_ShadowSyncBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (8 * gf_list_count(ptr->entries)); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +void stss_del(GF_Box *s) +{ + GF_SyncSampleBox *ptr = (GF_SyncSampleBox *)s; + if (ptr == NULL) return; + if (ptr->sampleNumbers) free(ptr->sampleNumbers); + free(ptr); +} + +GF_Err stss_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SyncSampleBox *ptr = (GF_SyncSampleBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->nb_entries = gf_bs_read_u32(bs); + ptr->alloc_size = ptr->nb_entries; + ptr->sampleNumbers = (u32 *) malloc( ptr->alloc_size * sizeof(u32)); + if (ptr->sampleNumbers == NULL) return GF_OUT_OF_MEM; + + for (i = 0; i < ptr->nb_entries; i++) { + ptr->sampleNumbers[i] = gf_bs_read_u32(bs); + } + return GF_OK; +} + +GF_Box *stss_New() +{ + GF_SyncSampleBox *tmp = (GF_SyncSampleBox *) malloc(sizeof(GF_SyncSampleBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SyncSampleBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_STSS; + return (GF_Box*)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err stss_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SyncSampleBox *ptr = (GF_SyncSampleBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i = 0; i < ptr->nb_entries; i++) { + gf_bs_write_u32(bs, ptr->sampleNumbers[i]); + } + return GF_OK; +} + +GF_Err stss_Size(GF_Box *s) +{ + GF_Err e; + GF_SyncSampleBox *ptr = (GF_SyncSampleBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (4 * ptr->nb_entries); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void stsz_del(GF_Box *s) +{ + GF_SampleSizeBox *ptr = (GF_SampleSizeBox *)s; + if (ptr == NULL) return; + if (ptr->sizes) free(ptr->sizes); + free(ptr); +} + + +GF_Err stsz_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, estSize; + GF_SampleSizeBox *ptr = (GF_SampleSizeBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + //support for CompactSizes + if (s->type == GF_ISOM_BOX_TYPE_STSZ) { + ptr->sampleSize = gf_bs_read_u32(bs); + ptr->sampleCount = gf_bs_read_u32(bs); + ptr->size -= 8; + } else { + //24-reserved + gf_bs_read_int(bs, 24); + i = gf_bs_read_u8(bs); + ptr->sampleCount = gf_bs_read_u32(bs); + ptr->size -= 8; + switch (i) { + case 4: + case 8: + case 16: + ptr->sampleSize = i; + break; + default: + //try to fix the file + //no samples, no parsing pb + if (!ptr->sampleCount) { + ptr->sampleSize = 16; + return GF_OK; + } + estSize = (u32) (ptr->size) / ptr->sampleCount; + if (!estSize && ((ptr->sampleCount+1)/2 == (ptr->size)) ) { + ptr->sampleSize = 4; + break; + } else if (estSize == 1 || estSize == 2) { + ptr->sampleSize = 8 * estSize; + } else { + return GF_ISOM_INVALID_FILE; + } + } + } + if (s->type == GF_ISOM_BOX_TYPE_STSZ) { + if (! ptr->sampleSize && ptr->sampleCount) { + ptr->sizes = (u32 *) malloc(ptr->sampleCount * sizeof(u32)); + if (! ptr->sizes) return GF_OUT_OF_MEM; + for (i = 0; i < ptr->sampleCount; i++) { + ptr->sizes[i] = gf_bs_read_u32(bs); + } + } + } else { + //note we could optimize the mem usage by keeping the table compact + //in memory. But that would complicate both caching and editing + //we therefore keep all sizes as u32 and uncompress the table + ptr->sizes = (u32 *) malloc(ptr->sampleCount * sizeof(u32)); + if (! ptr->sizes) return GF_OUT_OF_MEM; + + for (i = 0; i < ptr->sampleCount; ) { + switch (ptr->sampleSize) { + case 4: + ptr->sizes[i] = gf_bs_read_int(bs, 4); + if (i+1 < ptr->sampleCount) { + ptr->sizes[i+1] = gf_bs_read_int(bs, 4); + } else { + //0 padding in odd sample count + gf_bs_read_int(bs, 4); + } + i += 2; + break; + default: + ptr->sizes[i] = gf_bs_read_int(bs, ptr->sampleSize); + i += 1; + break; + } + } + } + return GF_OK; +} + +GF_Box *stsz_New() +{ + GF_SampleSizeBox *tmp = (GF_SampleSizeBox *) malloc(sizeof(GF_SampleSizeBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleSizeBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + //type is unknown here, can be regular or compact table + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err stsz_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SampleSizeBox *ptr = (GF_SampleSizeBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + //in both versions this is still valid + if (ptr->type == GF_ISOM_BOX_TYPE_STSZ) { + gf_bs_write_u32(bs, ptr->sampleSize); + } else { + gf_bs_write_u24(bs, 0); + gf_bs_write_u8(bs, ptr->sampleSize); + } + gf_bs_write_u32(bs, ptr->sampleCount); + + if (ptr->type == GF_ISOM_BOX_TYPE_STSZ) { + if (! ptr->sampleSize) { + for (i = 0; i < ptr->sampleCount; i++) { + gf_bs_write_u32(bs, ptr->sizes[i]); + } + } + } else { + for (i = 0; i < ptr->sampleCount; ) { + switch (ptr->sampleSize) { + case 4: + gf_bs_write_int(bs, ptr->sizes[i], 4); + if (i+1 < ptr->sampleCount) { + gf_bs_write_int(bs, ptr->sizes[i+1], 4); + } else { + //0 padding in odd sample count + gf_bs_write_int(bs, 0, 4); + } + i += 2; + break; + default: + gf_bs_write_int(bs, ptr->sizes[i], ptr->sampleSize); + i += 1; + break; + } + } + } + return GF_OK; +} + +GF_Err stsz_Size(GF_Box *s) +{ + GF_Err e; + u32 i, fieldSize, size; + GF_SampleSizeBox *ptr = (GF_SampleSizeBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + + ptr->size += 8; + if (!ptr->sampleCount) return GF_OK; + + //regular table + if (ptr->type == GF_ISOM_BOX_TYPE_STSZ) { + if (ptr->sampleSize) return GF_OK; + ptr->size += (4 * ptr->sampleCount); + return GF_OK; + } + + fieldSize = 4; + size = ptr->sizes[0]; + + for (i=0; i < ptr->sampleCount; i++) { + if (ptr->sizes[i] <= 0xF) continue; + //switch to 8-bit table + else if (ptr->sizes[i] <= 0xFF) { + fieldSize = 8; + } + //switch to 16-bit table + else if (ptr->sizes[i] <= 0xFFFF) { + fieldSize = 16; + } + //switch to 32-bit table + else { + fieldSize = 32; + } + + //check the size + if (size != ptr->sizes[i]) size = 0; + } + //if all samples are of the same size, switch to regular (more compact) + if (size) { + ptr->type = GF_ISOM_BOX_TYPE_STSZ; + ptr->sampleSize = size; + free(ptr->sizes); + ptr->sizes = NULL; + } + + if (fieldSize == 32) { + //oops, doesn't fit in a compact table + ptr->type = GF_ISOM_BOX_TYPE_STSZ; + ptr->size += (4 * ptr->sampleCount); + return GF_OK; + } + + //make sure we are a compact table (no need to change the mem representation) + ptr->type = GF_ISOM_BOX_TYPE_STZ2; + ptr->sampleSize = fieldSize; + if (fieldSize == 4) { + //do not forget the 0 padding field for odd count + ptr->size += (ptr->sampleCount + 1) / 2; + } else { + ptr->size += (ptr->sampleCount) * (fieldSize/8); + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void stts_del(GF_Box *s) +{ + GF_TimeToSampleBox *ptr = (GF_TimeToSampleBox *)s; + if (ptr->entries) free(ptr->entries); + free(ptr); +} + + +GF_Err stts_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TimeToSampleBox *ptr = (GF_TimeToSampleBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + +#ifndef GPAC_READ_ONLY + ptr->w_LastDTS = 0; +#endif + ptr->nb_entries = gf_bs_read_u32(bs); + ptr->alloc_size = ptr->nb_entries; + ptr->entries = malloc(sizeof(GF_SttsEntry)*ptr->alloc_size); + if (!ptr->entries) return GF_OUT_OF_MEM; + for (i=0; inb_entries; i++) { + ptr->entries[i].sampleCount = gf_bs_read_u32(bs); + ptr->entries[i].sampleDelta = gf_bs_read_u32(bs); +#ifndef GPAC_READ_ONLY + ptr->w_currentSampleNum += ptr->entries[i].sampleCount; + ptr->w_LastDTS += ptr->entries[i].sampleCount * ptr->entries[i].sampleDelta; +#endif + } + //remove the last sample delta. +#ifndef GPAC_READ_ONLY + if (ptr->nb_entries) ptr->w_LastDTS -= ptr->entries[ptr->nb_entries-1].sampleDelta; +#endif + return GF_OK; +} + +GF_Box *stts_New() +{ + GF_TimeToSampleBox *tmp = (GF_TimeToSampleBox *) malloc(sizeof(GF_TimeToSampleBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TimeToSampleBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_STTS; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err stts_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TimeToSampleBox *ptr = (GF_TimeToSampleBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i=0; inb_entries;i++) { + gf_bs_write_u32(bs, ptr->entries[i].sampleCount); + gf_bs_write_u32(bs, ptr->entries[i].sampleDelta); + } + return GF_OK; +} + +GF_Err stts_Size(GF_Box *s) +{ + GF_Err e; + GF_TimeToSampleBox *ptr = (GF_TimeToSampleBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (8 * ptr->nb_entries); + return GF_OK; +} + + +#endif //GPAC_READ_ONLY + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +void tfhd_del(GF_Box *s) +{ + GF_TrackFragmentHeaderBox *ptr = (GF_TrackFragmentHeaderBox *)s; + if (ptr == NULL) return; + free(ptr); +} + +GF_Err tfhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackFragmentHeaderBox *ptr = (GF_TrackFragmentHeaderBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + ptr->trackID = gf_bs_read_u32(bs); + + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRAF_BASE_OFFSET) { + ptr->base_data_offset = gf_bs_read_u64(bs); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DESC) { + ptr->sample_desc_index = gf_bs_read_u32(bs); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DUR) { + ptr->def_sample_duration = gf_bs_read_u32(bs); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_SIZE) { + ptr->def_sample_size = gf_bs_read_u32(bs); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) { + ptr->def_sample_flags = gf_bs_read_u32(bs); + } + return GF_OK; +} + +GF_Box *tfhd_New() +{ + GF_TrackFragmentHeaderBox *tmp = (GF_TrackFragmentHeaderBox *) malloc(sizeof(GF_TrackFragmentHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackFragmentHeaderBox)); + tmp->type = GF_ISOM_BOX_TYPE_TFHD; + //NO FLAGS SET BY DEFAULT + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + + +GF_Err tfhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackFragmentHeaderBox *ptr = (GF_TrackFragmentHeaderBox *) s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->trackID); + + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRAF_BASE_OFFSET) { + gf_bs_write_u64(bs, ptr->base_data_offset); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DESC) { + gf_bs_write_u32(bs, ptr->sample_desc_index); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DUR) { + gf_bs_write_u32(bs, ptr->def_sample_duration); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_SIZE) { + gf_bs_write_u32(bs, ptr->def_sample_size); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) { + gf_bs_write_u32(bs, ptr->def_sample_flags); + } + return GF_OK; +} + +GF_Err tfhd_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackFragmentHeaderBox *ptr = (GF_TrackFragmentHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRAF_BASE_OFFSET) ptr->size += 8; + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DESC) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DUR) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_SIZE) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) ptr->size += 4; + return GF_OK; +} + + + +#endif //GPAC_READ_ONLY + +#endif + +void tims_del(GF_Box *s) +{ + GF_TSHintEntryBox *tims = (GF_TSHintEntryBox *)s; + free(tims); +} + +GF_Err tims_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TSHintEntryBox *ptr = (GF_TSHintEntryBox *)s; + ptr->timeScale = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *tims_New() +{ + GF_TSHintEntryBox *tmp = (GF_TSHintEntryBox *) malloc(sizeof(GF_TSHintEntryBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_TSHintEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_TIMS; + return (GF_Box *)tmp; +} + +#ifndef GPAC_READ_ONLY + +GF_Err tims_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TSHintEntryBox *ptr = (GF_TSHintEntryBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->timeScale); + return GF_OK; +} + +GF_Err tims_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} + +#endif + + +void tkhd_del(GF_Box *s) +{ + GF_TrackHeaderBox *ptr = (GF_TrackHeaderBox *)s; + if (ptr == NULL) return; + free(ptr); + return; +} + + +GF_Err tkhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackHeaderBox *ptr = (GF_TrackHeaderBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (ptr->version == 1) { + ptr->creationTime = gf_bs_read_u64(bs); + ptr->modificationTime = gf_bs_read_u64(bs); + ptr->trackID = gf_bs_read_u32(bs); + ptr->reserved1 = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u64(bs); + } else { + ptr->creationTime = gf_bs_read_u32(bs); + ptr->modificationTime = gf_bs_read_u32(bs); + ptr->trackID = gf_bs_read_u32(bs); + ptr->reserved1 = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u32(bs); + } + ptr->reserved2[0] = gf_bs_read_u32(bs); + ptr->reserved2[1] = gf_bs_read_u32(bs); + ptr->layer = gf_bs_read_u16(bs); + ptr->alternate_group = gf_bs_read_u16(bs); + ptr->volume = gf_bs_read_u16(bs); + ptr->reserved3 = gf_bs_read_u16(bs); + ptr->matrix[0] = gf_bs_read_u32(bs); + ptr->matrix[1] = gf_bs_read_u32(bs); + ptr->matrix[2] = gf_bs_read_u32(bs); + ptr->matrix[3] = gf_bs_read_u32(bs); + ptr->matrix[4] = gf_bs_read_u32(bs); + ptr->matrix[5] = gf_bs_read_u32(bs); + ptr->matrix[6] = gf_bs_read_u32(bs); + ptr->matrix[7] = gf_bs_read_u32(bs); + ptr->matrix[8] = gf_bs_read_u32(bs); + ptr->width = gf_bs_read_u32(bs); + ptr->height = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *tkhd_New() +{ + GF_TrackHeaderBox *tmp = (GF_TrackHeaderBox *) malloc(sizeof(GF_TrackHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackHeaderBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_TKHD; + tmp->matrix[0] = 0x00010000; + tmp->matrix[4] = 0x00010000; + tmp->matrix[8] = 0x40000000; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err tkhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackHeaderBox *ptr = (GF_TrackHeaderBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->version == 1) { + gf_bs_write_u64(bs, ptr->creationTime); + gf_bs_write_u64(bs, ptr->modificationTime); + gf_bs_write_u32(bs, ptr->trackID); + gf_bs_write_u32(bs, ptr->reserved1); + gf_bs_write_u64(bs, ptr->duration); + } else { + gf_bs_write_u32(bs, (u32) ptr->creationTime); + gf_bs_write_u32(bs, (u32) ptr->modificationTime); + gf_bs_write_u32(bs, ptr->trackID); + gf_bs_write_u32(bs, ptr->reserved1); + gf_bs_write_u32(bs, (u32) ptr->duration); + } + gf_bs_write_u32(bs, ptr->reserved2[0]); + gf_bs_write_u32(bs, ptr->reserved2[1]); + gf_bs_write_u16(bs, ptr->layer); + gf_bs_write_u16(bs, ptr->alternate_group); + gf_bs_write_u16(bs, ptr->volume); + gf_bs_write_u16(bs, ptr->reserved3); + gf_bs_write_u32(bs, ptr->matrix[0]); + gf_bs_write_u32(bs, ptr->matrix[1]); + gf_bs_write_u32(bs, ptr->matrix[2]); + gf_bs_write_u32(bs, ptr->matrix[3]); + gf_bs_write_u32(bs, ptr->matrix[4]); + gf_bs_write_u32(bs, ptr->matrix[5]); + gf_bs_write_u32(bs, ptr->matrix[6]); + gf_bs_write_u32(bs, ptr->matrix[7]); + gf_bs_write_u32(bs, ptr->matrix[8]); + gf_bs_write_u32(bs, ptr->width); + gf_bs_write_u32(bs, ptr->height); + return GF_OK; +} + +GF_Err tkhd_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackHeaderBox *ptr = (GF_TrackHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->version = (ptr->duration>0xFFFFFFFF) ? 1 : 0; + ptr->size += (ptr->version == 1) ? 32 : 20; + ptr->size += 60; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +void traf_del(GF_Box *s) +{ + GF_TrackFragmentBox *ptr = (GF_TrackFragmentBox *)s; + if (ptr == NULL) return; + if (ptr->tfhd) gf_isom_box_del((GF_Box *) ptr->tfhd); + gf_isom_box_array_del(ptr->TrackRuns); + free(ptr); +} + +GF_Err traf_AddBox(GF_Box *s, GF_Box *a) +{ + GF_TrackFragmentBox *ptr = (GF_TrackFragmentBox *)s; + + switch (a->type) { + case GF_ISOM_BOX_TYPE_TFHD: + if (ptr->tfhd) return GF_ISOM_INVALID_FILE; + ptr->tfhd = (GF_TrackFragmentHeaderBox *) a; + return GF_OK; + case GF_ISOM_BOX_TYPE_TRUN: + return gf_list_add(ptr->TrackRuns, a); + + default: + return GF_ISOM_INVALID_FILE; + } +} + + +GF_Err traf_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, traf_AddBox); +} + +GF_Box *traf_New() +{ + GF_TrackFragmentBox *tmp = (GF_TrackFragmentBox *) malloc(sizeof(GF_TrackFragmentBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackFragmentBox)); + tmp->type = GF_ISOM_BOX_TYPE_TRAF; + tmp->TrackRuns = gf_list_new(); + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + + +GF_Err traf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackFragmentBox *ptr = (GF_TrackFragmentBox *) s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + //Header first + if (ptr->tfhd) { + e = gf_isom_box_write((GF_Box *) ptr->tfhd, bs); + if (e) return e; + } + return gf_isom_box_array_write(s, ptr->TrackRuns, bs); +} + +GF_Err traf_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackFragmentBox *ptr = (GF_TrackFragmentBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + if (ptr->tfhd) { + e = gf_isom_box_size((GF_Box *) ptr->tfhd); + if (e) return e; + ptr->size += ptr->tfhd->size; + } + return gf_isom_box_array_size(s, ptr->TrackRuns); +} + + + +#endif //GPAC_READ_ONLY + +#endif + +void trak_del(GF_Box *s) +{ + GF_TrackBox *ptr = (GF_TrackBox *) s; + if (ptr == NULL) return; + + if (ptr->Header) gf_isom_box_del((GF_Box *)ptr->Header); + if (ptr->udta) gf_isom_box_del((GF_Box *)ptr->udta); + if (ptr->Media) gf_isom_box_del((GF_Box *)ptr->Media); + if (ptr->References) gf_isom_box_del((GF_Box *)ptr->References); + if (ptr->editBox) gf_isom_box_del((GF_Box *)ptr->editBox); + if (ptr->meta) gf_isom_box_del((GF_Box *)ptr->meta); + gf_isom_box_array_del(ptr->boxes); + if (ptr->name) free(ptr->name); + free(ptr); +} + +static void gf_isom_check_sample_desc(GF_TrackBox *trak) +{ + GF_BitStream *bs; + GF_GenericVisualSampleEntryBox *genv; + GF_GenericAudioSampleEntryBox *gena; + GF_GenericSampleEntryBox *genm; + GF_UnknownBox *a; + u32 i; + u64 read; + + i=0; + while ((a = (GF_UnknownBox*)gf_list_enum(trak->Media->information->sampleTable->SampleDescription->boxList, &i))) { + switch (a->type) { + case GF_ISOM_BOX_TYPE_MP4S: + case GF_ISOM_BOX_TYPE_ENCS: + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_BOX_TYPE_GHNT: + case GF_ISOM_BOX_TYPE_RTP_STSD: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_TX3G: + case GF_ISOM_BOX_TYPE_ENCT: + case GF_ISOM_BOX_TYPE_DIMS: + case GF_ISOM_BOX_TYPE_AC3: + continue; + default: + break; + } + /*only process visual or audio*/ + switch (trak->Media->handler->handlerType) { + case GF_ISOM_MEDIA_VISUAL: + /*remove entry*/ + gf_list_rem(trak->Media->information->sampleTable->SampleDescription->boxList, i-1); + genv = (GF_GenericVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV); + bs = gf_bs_new(a->data, a->dataSize, GF_BITSTREAM_READ); + genv->size = a->size; + gf_isom_video_sample_entry_read((GF_VisualSampleEntryBox *) genv, bs); + genv->data_size = (u32) gf_bs_available(bs); + if (genv->data_size) { + genv->data = (char*)malloc(sizeof(char) * genv->data_size); + gf_bs_read_data(bs, genv->data, genv->data_size); + } + gf_bs_del(bs); + genv->size = a->size; + genv->EntryType = a->type; + gf_isom_box_del((GF_Box *)a); + gf_list_insert(trak->Media->information->sampleTable->SampleDescription->boxList, genv, i-1); + break; + case GF_ISOM_MEDIA_AUDIO: + /*remove entry*/ + gf_list_rem(trak->Media->information->sampleTable->SampleDescription->boxList, i-1); + gena = (GF_GenericAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA); + gena->size = a->size; + bs = gf_bs_new(a->data, a->dataSize, GF_BITSTREAM_READ); + gf_isom_audio_sample_entry_read((GF_AudioSampleEntryBox *) gena, bs); + gena->data_size = (u32) gf_bs_available(bs); + if (gena->data_size) { + gena->data = (char*)malloc(sizeof(char) * gena->data_size); + gf_bs_read_data(bs, gena->data, gena->data_size); + } + gf_bs_del(bs); + gena->size = a->size; + gena->EntryType = a->type; + gf_isom_box_del((GF_Box *)a); + gf_list_insert(trak->Media->information->sampleTable->SampleDescription->boxList, gena, i-1); + break; + + default: + /*remove entry*/ + gf_list_rem(trak->Media->information->sampleTable->SampleDescription->boxList, i-1); + genm = (GF_GenericSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM); + genm->size = a->size; + bs = gf_bs_new(a->data, a->dataSize, GF_BITSTREAM_READ); + read = 0; + gf_bs_read_data(bs, genm->reserved, 6); + genm->dataReferenceIndex = gf_bs_read_u16(bs); + genm->data_size = (u32) gf_bs_available(bs); + if (genm->data_size) { + genm->data = (char*)malloc(sizeof(char) * genm->data_size); + gf_bs_read_data(bs, genm->data, genm->data_size); + } + gf_bs_del(bs); + genm->size = a->size; + genm->EntryType = a->type; + gf_isom_box_del((GF_Box *)a); + gf_list_insert(trak->Media->information->sampleTable->SampleDescription->boxList, genm, i-1); + break; + } + + } +} + + +GF_Err trak_AddBox(GF_Box *s, GF_Box *a) +{ + GF_TrackBox *ptr = (GF_TrackBox *)s; + if (!a) return GF_OK; + switch(a->type) { + case GF_ISOM_BOX_TYPE_TKHD: + if (ptr->Header) return GF_ISOM_INVALID_FILE; + ptr->Header = (GF_TrackHeaderBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_EDTS: + if (ptr->editBox) return GF_ISOM_INVALID_FILE; + ptr->editBox = (GF_EditBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_UDTA: + if (ptr->udta) return GF_ISOM_INVALID_FILE; + ptr->udta = (GF_UserDataBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_META: + if (ptr->meta) return GF_ISOM_INVALID_FILE; + ptr->meta = (GF_MetaBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_TREF: + if (ptr->References) return GF_ISOM_INVALID_FILE; + ptr->References = (GF_TrackReferenceBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_MDIA: + if (ptr->Media) return GF_ISOM_INVALID_FILE; + ptr->Media = (GF_MediaBox *)a; + ((GF_MediaBox *)a)->mediaTrack = ptr; + return GF_OK; + default: + gf_list_add(ptr->boxes, a); + return GF_OK; + } + return GF_OK; +} + + +GF_Err trak_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackBox *ptr = (GF_TrackBox *)s; + e = gf_isom_read_box_list(s, bs, trak_AddBox); + if (e) return e; + gf_isom_check_sample_desc(ptr); + return GF_OK; +} + +GF_Box *trak_New() +{ + GF_TrackBox *tmp = (GF_TrackBox *) malloc(sizeof(GF_TrackBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackBox)); + tmp->type = GF_ISOM_BOX_TYPE_TRAK; + tmp->boxes = gf_list_new(); + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err trak_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackBox *ptr = (GF_TrackBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + if (ptr->Header) { + e = gf_isom_box_write((GF_Box *) ptr->Header, bs); + if (e) return e; + } + if (ptr->References) { + e = gf_isom_box_write((GF_Box *) ptr->References, bs); + if (e) return e; + } + if (ptr->editBox) { + e = gf_isom_box_write((GF_Box *) ptr->editBox, bs); + if (e) return e; + } + if (ptr->Media) { + e = gf_isom_box_write((GF_Box *) ptr->Media, bs); + if (e) return e; + } + if (ptr->meta) { + e = gf_isom_box_write((GF_Box *) ptr->meta, bs); + if (e) return e; + } + if (ptr->udta) { + e = gf_isom_box_write((GF_Box *) ptr->udta, bs); + if (e) return e; + } + return gf_isom_box_array_write(s, ptr->boxes, bs); +} + +GF_Err trak_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackBox *ptr = (GF_TrackBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + + if (ptr->Header) { + e = gf_isom_box_size((GF_Box *) ptr->Header); + if (e) return e; + ptr->size += ptr->Header->size; + } + if (ptr->udta) { + e = gf_isom_box_size((GF_Box *) ptr->udta); + if (e) return e; + ptr->size += ptr->udta->size; + } + if (ptr->References) { + e = gf_isom_box_size((GF_Box *) ptr->References); + if (e) return e; + ptr->size += ptr->References->size; + } + if (ptr->editBox) { + e = gf_isom_box_size((GF_Box *) ptr->editBox); + if (e) return e; + ptr->size += ptr->editBox->size; + } + if (ptr->Media) { + e = gf_isom_box_size((GF_Box *) ptr->Media); + if (e) return e; + ptr->size += ptr->Media->size; + } + if (ptr->meta) { + e = gf_isom_box_size((GF_Box *) ptr->meta); + if (e) return e; + ptr->size += ptr->meta->size; + } + return gf_isom_box_array_size(s, ptr->boxes); +} + +#endif //GPAC_READ_ONLY + + +void tref_del(GF_Box *s) +{ + GF_TrackReferenceBox *ptr = (GF_TrackReferenceBox *)s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->boxList); + free(ptr); +} + + +GF_Err tref_AddBox(GF_Box *s, GF_Box *a) +{ + GF_TrackReferenceBox *ptr = (GF_TrackReferenceBox *)s; + return gf_list_add(ptr->boxList, a); +} + +GF_Err tref_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list_ex(s, bs, tref_AddBox, s->type); +} + +GF_Box *tref_New() +{ + GF_TrackReferenceBox *tmp = (GF_TrackReferenceBox *) malloc(sizeof(GF_TrackReferenceBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackReferenceBox)); + tmp->boxList = gf_list_new(); + if (!tmp->boxList) { + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_TREF; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err tref_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackReferenceBox *ptr = (GF_TrackReferenceBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + return gf_isom_box_array_write(s, ptr->boxList, bs); +} + +GF_Err tref_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackReferenceBox *ptr = (GF_TrackReferenceBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + return gf_isom_box_array_size(s, ptr->boxList); +} + +#endif //GPAC_READ_ONLY + +void reftype_del(GF_Box *s) +{ + GF_TrackReferenceTypeBox *ptr = (GF_TrackReferenceTypeBox *)s; + if (!ptr) return; + if (ptr->trackIDs) free(ptr->trackIDs); + free(ptr); +} + + +GF_Err reftype_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 bytesToRead; + u32 i; + GF_TrackReferenceTypeBox *ptr = (GF_TrackReferenceTypeBox *)s; + + bytesToRead = (u32) (ptr->size); + if (!bytesToRead) return GF_OK; + + ptr->trackIDCount = (u32) (bytesToRead) / sizeof(u32); + ptr->trackIDs = (u32 *) malloc(ptr->trackIDCount * sizeof(u32)); + if (!ptr->trackIDs) return GF_OUT_OF_MEM; + + for (i = 0; i < ptr->trackIDCount; i++) { + ptr->trackIDs[i] = gf_bs_read_u32(bs); + } + return GF_OK; +} + +GF_Box *reftype_New() +{ + GF_TrackReferenceTypeBox *tmp = (GF_TrackReferenceTypeBox *) malloc(sizeof(GF_TrackReferenceTypeBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackReferenceTypeBox)); + tmp->type = GF_ISOM_BOX_TYPE_REFT; + return (GF_Box *)tmp; +} + + +GF_Err reftype_AddRefTrack(GF_TrackReferenceTypeBox *ref, u32 trackID, u16 *outRefIndex) +{ + u32 i; + if (!ref || !trackID) return GF_BAD_PARAM; + + if (outRefIndex) *outRefIndex = 0; + //don't add a dep if already here !! + for (i = 0; i < ref->trackIDCount; i++) { + if (ref->trackIDs[i] == trackID) { + if (outRefIndex) *outRefIndex = i+1; + return GF_OK; + } + } + + ref->trackIDs = (u32 *) realloc(ref->trackIDs, (ref->trackIDCount + 1) * sizeof(u32) ); + if (!ref->trackIDs) return GF_OUT_OF_MEM; + ref->trackIDs[ref->trackIDCount] = trackID; + ref->trackIDCount++; + if (outRefIndex) *outRefIndex = ref->trackIDCount; + return GF_OK; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err reftype_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TrackReferenceTypeBox *ptr = (GF_TrackReferenceTypeBox *)s; + ptr->type = ptr->reference_type; + e = gf_isom_box_write_header(s, bs); + ptr->type = GF_ISOM_BOX_TYPE_REFT; + if (e) return e; + for (i = 0; i < ptr->trackIDCount; i++) { + gf_bs_write_u32(bs, ptr->trackIDs[i]); + } + return GF_OK; +} + + +GF_Err reftype_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackReferenceTypeBox *ptr = (GF_TrackReferenceTypeBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += (ptr->trackIDCount * sizeof(u32)); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +void trex_del(GF_Box *s) +{ + GF_TrackExtendsBox *ptr = (GF_TrackExtendsBox *)s; + if (ptr == NULL) return; + free(ptr); +} + + +GF_Err trex_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackExtendsBox *ptr = (GF_TrackExtendsBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + ptr->trackID = gf_bs_read_u32(bs); + ptr->def_sample_desc_index = gf_bs_read_u32(bs); + ptr->def_sample_duration = gf_bs_read_u32(bs); + ptr->def_sample_size = gf_bs_read_u32(bs); + ptr->def_sample_flags = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *trex_New() +{ + GF_TrackExtendsBox *tmp = (GF_TrackExtendsBox *) malloc(sizeof(GF_TrackExtendsBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackExtendsBox)); + tmp->type = GF_ISOM_BOX_TYPE_TREX; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + + +GF_Err trex_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackExtendsBox *ptr = (GF_TrackExtendsBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + gf_bs_write_u32(bs, ptr->trackID); + gf_bs_write_u32(bs, ptr->def_sample_desc_index); + gf_bs_write_u32(bs, ptr->def_sample_duration); + gf_bs_write_u32(bs, ptr->def_sample_size); + gf_bs_write_u32(bs, ptr->def_sample_flags); + return GF_OK; +} + +GF_Err trex_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackExtendsBox *ptr = (GF_TrackExtendsBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 20; + return GF_OK; +} + + + +#endif //GPAC_READ_ONLY + +#endif + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +void trun_del(GF_Box *s) +{ + GF_TrunEntry *p; + GF_TrackFragmentRunBox *ptr = (GF_TrackFragmentRunBox *)s; + if (ptr == NULL) return; + + while (gf_list_count(ptr->entries)) { + p = (GF_TrunEntry*)gf_list_get(ptr->entries, 0); + gf_list_rem(ptr->entries, 0); + free(p); + } + gf_list_del(ptr->entries); + if (ptr->cache) gf_bs_del(ptr->cache); + free(ptr); +} + +GF_Err trun_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TrunEntry *p; + GF_TrackFragmentRunBox *ptr = (GF_TrackFragmentRunBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + //check this is a good file + if ((ptr->flags & GF_ISOM_TRUN_FIRST_FLAG) && (ptr->flags & GF_ISOM_TRUN_FLAGS)) + return GF_ISOM_INVALID_FILE; + + ptr->sample_count = gf_bs_read_u32(bs); + + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRUN_DATA_OFFSET) { + ptr->data_offset = gf_bs_read_u32(bs); + ptr->size -= 4; + } + if (ptr->flags & GF_ISOM_TRUN_FIRST_FLAG) { + ptr->first_sample_flags = gf_bs_read_u32(bs); + ptr->size -= 4; + } + + //read each entry (even though nothing may be written) + for (i=0; isample_count; i++) { + u32 trun_size = 0; + p = (GF_TrunEntry *) malloc(sizeof(GF_TrunEntry)); + memset(p, 0, sizeof(GF_TrunEntry)); + + if (ptr->flags & GF_ISOM_TRUN_DURATION) { + p->Duration = gf_bs_read_u32(bs); + trun_size += 4; + } + if (ptr->flags & GF_ISOM_TRUN_SIZE) { + p->size = gf_bs_read_u32(bs); + trun_size += 4; + } + //SHOULDN'T BE USED IF GF_ISOM_TRUN_FIRST_FLAG IS DEFINED + if (ptr->flags & GF_ISOM_TRUN_FLAGS) { + p->flags = gf_bs_read_u32(bs); + trun_size += 4; + } + if (ptr->flags & GF_ISOM_TRUN_CTS_OFFSET) { + p->CTS_Offset = gf_bs_read_u32(bs); + } + gf_list_add(ptr->entries, p); + if (ptr->sizesize-=trun_size; + } + return GF_OK; +} + +GF_Box *trun_New() +{ + GF_TrackFragmentRunBox *tmp = (GF_TrackFragmentRunBox *) malloc(sizeof(GF_TrackFragmentRunBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackFragmentRunBox)); + tmp->type = GF_ISOM_BOX_TYPE_TRUN; + tmp->entries = gf_list_new(); + //NO FLAGS SET BY DEFAULT + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + + +GF_Err trun_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_TrunEntry *p; + GF_Err e; + u32 i, count; + GF_TrackFragmentRunBox *ptr = (GF_TrackFragmentRunBox *) s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + gf_bs_write_u32(bs, ptr->sample_count); + + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRUN_DATA_OFFSET) { + gf_bs_write_u32(bs, ptr->data_offset); + } + if (ptr->flags & GF_ISOM_TRUN_FIRST_FLAG) { + gf_bs_write_u32(bs, ptr->first_sample_flags); + } + + //if nothing to do, this will be skipped automatically + count = gf_list_count(ptr->entries); + for (i=0; ientries, i); + + if (ptr->flags & GF_ISOM_TRUN_DURATION) { + gf_bs_write_u32(bs, p->Duration); + } + if (ptr->flags & GF_ISOM_TRUN_SIZE) { + gf_bs_write_u32(bs, p->size); + } + //SHOULDN'T BE USED IF GF_ISOM_TRUN_FIRST_FLAG IS DEFINED + if (ptr->flags & GF_ISOM_TRUN_FLAGS) { + gf_bs_write_u32(bs, p->flags); + } + if (ptr->flags & GF_ISOM_TRUN_CTS_OFFSET) { + gf_bs_write_u32(bs, p->CTS_Offset); + } + } + return GF_OK; +} + +GF_Err trun_Size(GF_Box *s) +{ + GF_Err e; + u32 i, count; + GF_TrunEntry *p; + GF_TrackFragmentRunBox *ptr = (GF_TrackFragmentRunBox *)s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + + ptr->size += 4; + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRUN_DATA_OFFSET) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRUN_FIRST_FLAG) ptr->size += 4; + + //if nothing to do, this will be skipped automatically + count = gf_list_count(ptr->entries); + for (i=0; ientries, i); + if (ptr->flags & GF_ISOM_TRUN_DURATION) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRUN_SIZE) ptr->size += 4; + //SHOULDN'T BE USED IF GF_ISOM_TRUN_FIRST_FLAG IS DEFINED + if (ptr->flags & GF_ISOM_TRUN_FLAGS) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRUN_CTS_OFFSET) ptr->size += 4; + } + + return GF_OK; +} + + + +#endif //GPAC_READ_ONLY + +#endif + +void tsro_del(GF_Box *s) +{ + GF_TimeOffHintEntryBox *tsro = (GF_TimeOffHintEntryBox *)s; + free(tsro); +} + +GF_Err tsro_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TimeOffHintEntryBox *ptr = (GF_TimeOffHintEntryBox *)s; + ptr->TimeOffset = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *tsro_New() +{ + GF_TimeOffHintEntryBox *tmp = (GF_TimeOffHintEntryBox *) malloc(sizeof(GF_TimeOffHintEntryBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_TimeOffHintEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_TSRO; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_READ_ONLY +GF_Err tsro_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TimeOffHintEntryBox *ptr = (GF_TimeOffHintEntryBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->TimeOffset); + return GF_OK; +} + +GF_Err tsro_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif + + +void udta_del(GF_Box *s) +{ + u32 i; + GF_UserDataMap *map; + GF_UserDataBox *ptr = (GF_UserDataBox *)s; + if (ptr == NULL) return; + i=0; + while ((map = (GF_UserDataMap *)gf_list_enum(ptr->recordList, &i))) { + gf_isom_box_array_del(map->boxList); + free(map); + } + gf_list_del(ptr->recordList); + free(ptr); +} + +GF_UserDataMap *udta_getEntry(GF_UserDataBox *ptr, u32 box_type, bin128 *uuid) +{ + u32 i; + GF_UserDataMap *map; + i=0; + while ((map = (GF_UserDataMap *)gf_list_enum(ptr->recordList, &i))) { + if (map->boxType == box_type) { + if ((box_type != GF_ISOM_BOX_TYPE_UUID) || !uuid) return map; + if (!memcmp(map->uuid, *uuid, 16)) return map; + } + } + return NULL; +} + +GF_Err udta_AddBox(GF_UserDataBox *ptr, GF_Box *a) +{ + GF_Err e; + GF_UserDataMap *map; + if (!ptr) return GF_BAD_PARAM; + if (!a) return GF_OK; + + map = udta_getEntry(ptr, a->type, (a->type==GF_ISOM_BOX_TYPE_UUID) ? & ((GF_UUIDBox *)a)->uuid : NULL); + if (map == NULL) { + map = (GF_UserDataMap *) malloc(sizeof(GF_UserDataMap)); + if (map == NULL) return GF_OUT_OF_MEM; + memset(map, 0, sizeof(GF_UserDataMap)); + + map->boxType = a->type; + if (a->type == GF_ISOM_BOX_TYPE_UUID) + memcpy(map->uuid, ((GF_UUIDBox *)a)->uuid, 16); + map->boxList = gf_list_new(); + if (!map->boxList) { + free(map); + return GF_OUT_OF_MEM; + } + e = gf_list_add(ptr->recordList, map); + if (e) return e; + } + return gf_list_add(map->boxList, a); +} + + +GF_Err udta_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 sub_type; + GF_Box *a; + GF_UserDataBox *ptr = (GF_UserDataBox *)s; + while (ptr->size) { + /*if no udta type coded, break*/ + sub_type = gf_bs_peek_bits(bs, 32, 0); + if (sub_type) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + e = udta_AddBox(ptr, a); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size -= a->size; + } else { + gf_bs_read_u32(bs); + ptr->size -= 4; + } + } + return GF_OK; +} + +GF_Box *udta_New() +{ + GF_UserDataBox *tmp = (GF_UserDataBox *) malloc(sizeof(GF_UserDataBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_UserDataBox)); + tmp->recordList = gf_list_new(); + if (!tmp->recordList) { + free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_UDTA; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err udta_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_UserDataMap *map; + GF_UserDataBox *ptr = (GF_UserDataBox *)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + i=0; + while ((map = (GF_UserDataMap *)gf_list_enum(ptr->recordList, &i))) { + //warning: here we are not passing the actual "parent" of the list + //but the UDTA box. The parent itself is not an box, we don't care about it + e = gf_isom_box_array_write(s, map->boxList, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err udta_Size(GF_Box *s) +{ + GF_Err e; + u32 i; + GF_UserDataMap *map; + GF_UserDataBox *ptr = (GF_UserDataBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + i=0; + while ((map = (GF_UserDataMap *)gf_list_enum(ptr->recordList, &i))) { + //warning: here we are not passing the actual "parent" of the list + //but the UDTA box. The parent itself is not an box, we don't care about it + e = gf_isom_box_array_size(s, map->boxList); + if (e) return e; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void vmhd_del(GF_Box *s) +{ + GF_VideoMediaHeaderBox *ptr = (GF_VideoMediaHeaderBox *)s; + if (ptr == NULL) return; + free(ptr); +} + + +GF_Err vmhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_VideoMediaHeaderBox *ptr = (GF_VideoMediaHeaderBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->reserved = gf_bs_read_u64(bs); + return GF_OK; +} + +GF_Box *vmhd_New() +{ + GF_VideoMediaHeaderBox *tmp = (GF_VideoMediaHeaderBox *) malloc(sizeof(GF_VideoMediaHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_VideoMediaHeaderBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->flags = 1; + tmp->type = GF_ISOM_BOX_TYPE_VMHD; + return (GF_Box *)tmp; +} + + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err vmhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_VideoMediaHeaderBox *ptr = (GF_VideoMediaHeaderBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->reserved); + return GF_OK; +} + +GF_Err vmhd_Size(GF_Box *s) +{ + GF_Err e; + GF_VideoMediaHeaderBox *ptr = (GF_VideoMediaHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 8; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +void void_del(GF_Box *s) +{ + free(s); +} + + +GF_Err void_Read(GF_Box *s, GF_BitStream *bs) +{ + if (s->size) return GF_ISOM_INVALID_FILE; + return GF_OK; +} + +GF_Box *void_New() +{ + GF_Box *tmp = (GF_Box *) malloc(sizeof(GF_Box)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_VOID; + return tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err void_Write(GF_Box *s, GF_BitStream *bs) +{ + gf_bs_write_u32(bs, 0); + return GF_OK; +} + +GF_Err void_Size(GF_Box *s) +{ + s->size = 4; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +GF_Box *pdin_New() +{ + GF_ProgressiveDownloadBox *tmp = (GF_ProgressiveDownloadBox*) malloc(sizeof(GF_ProgressiveDownloadBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ProgressiveDownloadBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->flags = 1; + tmp->type = GF_ISOM_BOX_TYPE_PDIN; + return (GF_Box *)tmp; +} + + +void pdin_del(GF_Box *s) +{ + GF_ProgressiveDownloadBox *ptr = (GF_ProgressiveDownloadBox*)s; + if (ptr == NULL) return; + if (ptr->rates) free(ptr->rates); + if (ptr->times) free(ptr->times); + free(ptr); +} + + +GF_Err pdin_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_Err e; + GF_ProgressiveDownloadBox *ptr = (GF_ProgressiveDownloadBox*)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + ptr->count = (u32) (ptr->size) / 8; + ptr->rates = (u32*)malloc(sizeof(u32)*ptr->count); + ptr->times = (u32*)malloc(sizeof(u32)*ptr->count); + for (i=0; icount; i++) { + ptr->rates[i] = gf_bs_read_u32(bs); + ptr->times[i] = gf_bs_read_u32(bs); + } + return GF_OK; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err pdin_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_ProgressiveDownloadBox *ptr = (GF_ProgressiveDownloadBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + for (i=0; icount; i++) { + gf_bs_write_u32(bs, ptr->rates[i]); + gf_bs_write_u32(bs, ptr->times[i]); + } + return GF_OK; +} + +GF_Err pdin_Size(GF_Box *s) +{ + GF_Err e; + GF_ProgressiveDownloadBox *ptr = (GF_ProgressiveDownloadBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 8*ptr->count; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + + +GF_Box *sdtp_New() +{ + GF_SampleDependencyTypeBox *tmp = (GF_SampleDependencyTypeBox*) malloc(sizeof(GF_SampleDependencyTypeBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleDependencyTypeBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->flags = 1; + tmp->type = GF_ISOM_BOX_TYPE_SDTP; + return (GF_Box *)tmp; +} + + +void sdtp_del(GF_Box *s) +{ + GF_SampleDependencyTypeBox *ptr = (GF_SampleDependencyTypeBox*)s; + if (ptr == NULL) return; + if (ptr->sample_info) free(ptr->sample_info); + free(ptr); +} + + +GF_Err sdtp_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SampleDependencyTypeBox *ptr = (GF_SampleDependencyTypeBox*)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + /*out-of-order sdtp, assume no padding at the end*/ + if (!ptr->sampleCount) ptr->sampleCount = (u32) (ptr->size - 8); + ptr->sample_info = (u8 *) malloc(sizeof(u8)*ptr->sampleCount); + gf_bs_read_data(bs, (char*)ptr->sample_info, ptr->sampleCount); + ptr->size -= ptr->sampleCount; + return GF_OK; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err sdtp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SampleDependencyTypeBox *ptr = (GF_SampleDependencyTypeBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_data(bs, (char*)ptr->sample_info, ptr->sampleCount); + return GF_OK; +} + +GF_Err sdtp_Size(GF_Box *s) +{ + GF_Err e; + GF_SampleDependencyTypeBox *ptr = (GF_SampleDependencyTypeBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += ptr->sampleCount; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +GF_Box *pasp_New() +{ + GF_PixelAspectRatioBox *tmp; + GF_SAFEALLOC(tmp, GF_PixelAspectRatioBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_PASP; + return (GF_Box *)tmp; +} + + +void pasp_del(GF_Box *s) +{ + GF_PixelAspectRatioBox *ptr = (GF_PixelAspectRatioBox*)s; + if (ptr == NULL) return; + free(ptr); +} + + +GF_Err pasp_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_PixelAspectRatioBox *ptr = (GF_PixelAspectRatioBox*)s; + ptr->hSpacing = gf_bs_read_u32(bs); + ptr->vSpacing = gf_bs_read_u32(bs); + ptr->size -= 8; + return GF_OK; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err pasp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_PixelAspectRatioBox *ptr = (GF_PixelAspectRatioBox *)s; + GF_Err e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->hSpacing); + gf_bs_write_u32(bs, ptr->vSpacing); + return GF_OK; +} + +GF_Err pasp_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + + +GF_Box *metx_New(u32 type) +{ + GF_MetaDataSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_MetaDataSampleEntryBox); + if (tmp == NULL) return NULL; + tmp->type = type; + return (GF_Box *)tmp; +} + + +void metx_del(GF_Box *s) +{ + GF_MetaDataSampleEntryBox *ptr = (GF_MetaDataSampleEntryBox*)s; + if (ptr == NULL) return; + if (ptr->content_encoding) free(ptr->content_encoding); + if (ptr->mime_type_or_namespace) free(ptr->mime_type_or_namespace); + if (ptr->xml_schema_loc) free(ptr->xml_schema_loc); + if (ptr->bitrate) gf_isom_box_del((GF_Box *) ptr->bitrate); + free(ptr); +} + + +GF_Err metx_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MPEGVisualSampleEntryBox *ptr = (GF_MPEGVisualSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_SINF: + if (ptr->protection_info) return GF_ISOM_INVALID_FILE; + ptr->protection_info = (GF_ProtectionInfoBox*)a; + break; + case GF_ISOM_BOX_TYPE_BTRT: + if (ptr->bitrate) return GF_ISOM_INVALID_FILE; + ptr->bitrate = (GF_MPEG4BitRateBox *)a; + break; + default: + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err metx_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 size, i; + char *str; + GF_MetaDataSampleEntryBox *ptr = (GF_MetaDataSampleEntryBox*)s; + size = (u32) ptr->size; + str = malloc(sizeof(char)*size); + i=0; + while (icontent_encoding = strdup(str); + + i=0; + while (imime_type_or_namespace = strdup(str); + + if (ptr->type==GF_ISOM_BOX_TYPE_METX) { + i=0; + while (ixml_schema_loc = strdup(str); + } + ptr->size = size; + return gf_isom_read_box_list(s, bs, metx_AddBox); +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err metx_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_MetaDataSampleEntryBox *ptr = (GF_MetaDataSampleEntryBox *)s; + GF_Err e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + + if (ptr->content_encoding) + gf_bs_write_data(bs, ptr->content_encoding, strlen(ptr->content_encoding)); + gf_bs_write_u8(bs, 0); + + if (ptr->mime_type_or_namespace) + gf_bs_write_data(bs, ptr->mime_type_or_namespace, strlen(ptr->mime_type_or_namespace)); + gf_bs_write_u8(bs, 0); + + if (ptr->xml_schema_loc) + gf_bs_write_data(bs, ptr->xml_schema_loc, strlen(ptr->xml_schema_loc)); + gf_bs_write_u8(bs, 0); + + if (ptr->bitrate) { + e = gf_isom_box_write((GF_Box *)ptr->bitrate, bs); + if (e) return e; + } + if (ptr->protection_info) { + e = gf_isom_box_write((GF_Box *)ptr->protection_info, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err metx_Size(GF_Box *s) +{ + GF_MetaDataSampleEntryBox *ptr = (GF_MetaDataSampleEntryBox *)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 8; + + if (ptr->content_encoding) + ptr->size += strlen(ptr->content_encoding); + ptr->size++; + if (ptr->mime_type_or_namespace) + ptr->size += strlen(ptr->mime_type_or_namespace); + ptr->size++; + if (ptr->xml_schema_loc) + ptr->size += strlen(ptr->xml_schema_loc); + ptr->size++; + + if (ptr->bitrate) { + e = gf_isom_box_size((GF_Box *)ptr->bitrate); + if (e) return e; + ptr->size += ptr->bitrate->size; + } + if (ptr->protection_info) { + e = gf_isom_box_size((GF_Box *)ptr->protection_info); + if (e) return e; + ptr->size += ptr->protection_info->size; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +GF_Box *dac3_New() +{ + GF_AC3ConfigBox *tmp = (GF_AC3ConfigBox *) malloc(sizeof(GF_AC3ConfigBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_AC3ConfigBox)); + tmp->type = GF_ISOM_BOX_TYPE_DAC3; + return (GF_Box *)tmp; +} + +void dac3_del(GF_Box *s) +{ + GF_AC3ConfigBox *ptr = (GF_AC3ConfigBox *)s; + free(ptr); +} + + +GF_Err dac3_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_AC3ConfigBox *ptr = (GF_AC3ConfigBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + ptr->cfg.fscod = gf_bs_read_int(bs, 2); + ptr->cfg.bsid = gf_bs_read_int(bs, 5); + ptr->cfg.bsmod = gf_bs_read_int(bs, 3); + ptr->cfg.acmod = gf_bs_read_int(bs, 3); + ptr->cfg.lfon = gf_bs_read_int(bs, 1); + ptr->cfg.brcode = gf_bs_read_int(bs, 5); + gf_bs_read_int(bs, 5); + return GF_OK; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err dac3_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_AC3ConfigBox *ptr = (GF_AC3ConfigBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_int(bs, ptr->cfg.fscod, 2); + gf_bs_write_int(bs, ptr->cfg.bsid, 5); + gf_bs_write_int(bs, ptr->cfg.bsmod, 3); + gf_bs_write_int(bs, ptr->cfg.acmod, 3); + gf_bs_write_int(bs, ptr->cfg.lfon, 1); + gf_bs_write_int(bs, ptr->cfg.brcode, 5); + gf_bs_write_int(bs, 0, 5); + return GF_OK; +} + +GF_Err dac3_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 3; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +void ac3_del(GF_Box *s) +{ + GF_AC3SampleEntryBox *ptr = (GF_AC3SampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->info) gf_isom_box_del((GF_Box *)ptr->info); + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + free(ptr); +} + + +GF_Err ac3_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_AC3SampleEntryBox *ptr = (GF_AC3SampleEntryBox *)s; + e = gf_isom_audio_sample_entry_read((GF_AudioSampleEntryBox*)s, bs); + if (e) return e; + e = gf_isom_parse_box((GF_Box **)&ptr->info, bs); + if (e) return e; + return GF_OK; +} + +GF_Box *ac3_New() +{ + GF_AC3SampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_AC3SampleEntryBox); + if (tmp == NULL) return NULL; + gf_isom_audio_sample_entry_init((GF_AudioSampleEntryBox*)tmp); + tmp->type = GF_ISOM_BOX_TYPE_AC3; + return (GF_Box *)tmp; +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err ac3_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPAudioSampleEntryBox *ptr = (GF_3GPPAudioSampleEntryBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_isom_audio_sample_entry_write((GF_AudioSampleEntryBox*)s, bs); + return gf_isom_box_write((GF_Box *)ptr->info, bs); +} + +GF_Err ac3_Size(GF_Box *s) +{ + GF_Err e; + GF_3GPPAudioSampleEntryBox *ptr = (GF_3GPPAudioSampleEntryBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + gf_isom_audio_sample_entry_size((GF_AudioSampleEntryBox*)s); + e = gf_isom_box_size((GF_Box *)ptr->info); + if (e) return e; + ptr->size += ptr->info->size; + return GF_OK; +} + +#endif //GPAC_READ_ONLY diff --git a/src/isomedia/box_code_isma.c b/src/isomedia/box_code_isma.c new file mode 100644 index 0000000..3f0498e --- /dev/null +++ b/src/isomedia/box_code_isma.c @@ -0,0 +1,885 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/* ProtectionInfo Box */ +GF_Box *sinf_New() +{ + GF_ProtectionInfoBox *tmp = (GF_ProtectionInfoBox *) malloc(sizeof(GF_ProtectionInfoBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ProtectionInfoBox)); + tmp->type = GF_ISOM_BOX_TYPE_SINF; + return (GF_Box *)tmp; +} + +void sinf_del(GF_Box *s) +{ + GF_ProtectionInfoBox *ptr = (GF_ProtectionInfoBox *)s; + if (ptr == NULL) return; + if (ptr->original_format) gf_isom_box_del((GF_Box *)ptr->original_format); + if (ptr->info) gf_isom_box_del((GF_Box *)ptr->info); + if (ptr->scheme_type) gf_isom_box_del((GF_Box *)ptr->scheme_type); + free(ptr); +} + +GF_Err sinf_AddBox(GF_Box *s, GF_Box *a) +{ + GF_ProtectionInfoBox *ptr = (GF_ProtectionInfoBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_FRMA: + if (ptr->original_format) return GF_ISOM_INVALID_FILE; + ptr->original_format = (GF_OriginalFormatBox*)a; + break; + case GF_ISOM_BOX_TYPE_SCHM: + if (ptr->scheme_type) return GF_ISOM_INVALID_FILE; + ptr->scheme_type = (GF_SchemeTypeBox*)a; + break; + case GF_ISOM_BOX_TYPE_SCHI: + if (ptr->info) return GF_ISOM_INVALID_FILE; + ptr->info = (GF_SchemeInformationBox*)a; + break; + default: + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err sinf_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, sinf_AddBox); +} + +#ifndef GPAC_READ_ONLY +GF_Err sinf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ProtectionInfoBox *ptr = (GF_ProtectionInfoBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + //frma + e = gf_isom_box_write((GF_Box *) ptr->original_format, bs); + if (e) return e; + // schm + e = gf_isom_box_write((GF_Box *) ptr->scheme_type, bs); + if (e) return e; + // schi + e = gf_isom_box_write((GF_Box *) ptr->info, bs); + if (e) return e; + return GF_OK; +} + +GF_Err sinf_Size(GF_Box *s) +{ + GF_Err e; + GF_ProtectionInfoBox *ptr = (GF_ProtectionInfoBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_get_size(s); + if (e) return e; + e = gf_isom_box_size((GF_Box *) ptr->original_format); + if (e) return e; + ptr->size += ptr->original_format->size; + e = gf_isom_box_size((GF_Box *) ptr->scheme_type); + if (e) return e; + ptr->size += ptr->scheme_type->size; + e = gf_isom_box_size((GF_Box *) ptr->info); + if (e) return e; + ptr->size += ptr->info->size; + return GF_OK; +} +#endif //GPAC_READ_ONLY + +/* OriginalFormat Box */ +GF_Box *frma_New() +{ + GF_OriginalFormatBox *tmp = (GF_OriginalFormatBox *) malloc(sizeof(GF_OriginalFormatBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_OriginalFormatBox)); + tmp->type = GF_ISOM_BOX_TYPE_FRMA; + return (GF_Box *)tmp; +} + +void frma_del(GF_Box *s) +{ + GF_OriginalFormatBox *ptr = (GF_OriginalFormatBox *)s; + if (ptr == NULL) return; + free(ptr); +} + +GF_Err frma_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_OriginalFormatBox *ptr = (GF_OriginalFormatBox *)s; + ptr->data_format = gf_bs_read_u32(bs); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err frma_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OriginalFormatBox *ptr = (GF_OriginalFormatBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->data_format); + return GF_OK; +} + +GF_Err frma_Size(GF_Box *s) +{ + GF_Err e; + GF_OriginalFormatBox *ptr = (GF_OriginalFormatBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 4; + return GF_OK; +} +#endif //GPAC_READ_ONLY + +/* SchemeType Box */ +GF_Box *schm_New() +{ + GF_SchemeTypeBox *tmp = (GF_SchemeTypeBox *) malloc(sizeof(GF_SchemeTypeBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SchemeTypeBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_SCHM; + return (GF_Box *)tmp; +} + +void schm_del(GF_Box *s) +{ + GF_SchemeTypeBox *ptr = (GF_SchemeTypeBox *)s; + if (ptr == NULL) return; + if (ptr->URI) free(ptr->URI); + free(ptr); +} + +GF_Err schm_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SchemeTypeBox *ptr = (GF_SchemeTypeBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->scheme_type = gf_bs_read_u32(bs); + ptr->scheme_version = gf_bs_read_u32(bs); + ptr->size -= 8; + if (ptr->size && (ptr->flags & 0x000001)) { + u32 len = (u32) (ptr->size); + ptr->URI = (char*)malloc(sizeof(char)*len); + if (!ptr->URI) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->URI, len); + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err schm_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SchemeTypeBox *ptr = (GF_SchemeTypeBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + gf_bs_write_u32(bs, ptr->scheme_type); + gf_bs_write_u32(bs, ptr->scheme_version); + if (ptr->flags & 0x000001) gf_bs_write_data(bs, ptr->URI, strlen(ptr->URI)+1); + return GF_OK; +} + +GF_Err schm_Size(GF_Box *s) +{ + GF_Err e; + GF_SchemeTypeBox *ptr = (GF_SchemeTypeBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 8; + if (ptr->flags & 0x000001) ptr->size += strlen(ptr->URI)+1; + return GF_OK; +} +#endif //GPAC_READ_ONLY + +/* SchemeInformation Box */ +GF_Box *schi_New() +{ + GF_SchemeInformationBox *tmp = (GF_SchemeInformationBox *) malloc(sizeof(GF_SchemeInformationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SchemeInformationBox)); + tmp->type = GF_ISOM_BOX_TYPE_SCHI; + return (GF_Box *)tmp; +} + +void schi_del(GF_Box *s) +{ + GF_SchemeInformationBox *ptr = (GF_SchemeInformationBox *)s; + if (ptr == NULL) return; + if (ptr->ikms) gf_isom_box_del((GF_Box *)ptr->ikms); + if (ptr->isfm) gf_isom_box_del((GF_Box *)ptr->isfm); + free(ptr); +} + +GF_Err schi_AddBox(GF_Box *s, GF_Box *a) +{ + GF_SchemeInformationBox *ptr = (GF_SchemeInformationBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_IKMS: + if (ptr->ikms) return GF_ISOM_INVALID_FILE; + ptr->ikms = (GF_ISMAKMSBox*)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_ISFM: + if (ptr->isfm) return GF_ISOM_INVALID_FILE; + ptr->isfm = (GF_ISMASampleFormatBox*)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_ODKM: + if (ptr->okms) return GF_ISOM_INVALID_FILE; + ptr->okms = (GF_OMADRMKMSBox*)a; + return GF_OK; + default: + gf_isom_box_del(a); + return GF_OK; + } +} + +GF_Err schi_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, schi_AddBox); +} + +#ifndef GPAC_READ_ONLY +GF_Err schi_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SchemeInformationBox *ptr = (GF_SchemeInformationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + if (ptr->ikms) { + e = gf_isom_box_write((GF_Box *) ptr->ikms, bs); + if (e) return e; + } + if (ptr->isfm) { + e = gf_isom_box_write((GF_Box *) ptr->isfm, bs); + if (e) return e; + } + if (ptr->okms) { + e = gf_isom_box_write((GF_Box *) ptr->okms, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err schi_Size(GF_Box *s) +{ + GF_Err e; + GF_SchemeInformationBox *ptr = (GF_SchemeInformationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_get_size(s); + if (e) return e; + + if (ptr->ikms) { + e = gf_isom_box_size((GF_Box *) ptr->ikms); + if (e) return e; + ptr->size += ptr->ikms->size; + } + if (ptr->isfm) { + e = gf_isom_box_size((GF_Box *) ptr->isfm); + if (e) return e; + ptr->size += ptr->isfm->size; + } + if (ptr->okms) { + e = gf_isom_box_size((GF_Box *) ptr->okms); + if (e) return e; + ptr->size += ptr->okms->size; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +/* ISMAKMS Box */ +GF_Box *iKMS_New() +{ + GF_ISMAKMSBox *tmp = (GF_ISMAKMSBox *) malloc(sizeof(GF_ISMAKMSBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ISMAKMSBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_IKMS; + return (GF_Box *)tmp; +} + +void iKMS_del(GF_Box *s) +{ + GF_ISMAKMSBox *ptr = (GF_ISMAKMSBox *)s; + if (ptr == NULL) return; + if (ptr->URI) free(ptr->URI); + free(ptr); +} + +GF_Err iKMS_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 len; + GF_ISMAKMSBox *ptr = (GF_ISMAKMSBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + len = (u32) (ptr->size); + ptr->URI = (char*) malloc(sizeof(char)*len); + if (!ptr->URI) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->URI, len); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err iKMS_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ISMAKMSBox *ptr = (GF_ISMAKMSBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->URI, strlen(ptr->URI)+1); + return GF_OK; +} + +GF_Err iKMS_Size(GF_Box *s) +{ + GF_Err e; + GF_ISMAKMSBox *ptr = (GF_ISMAKMSBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += strlen(ptr->URI)+1; + return GF_OK; +} +#endif //GPAC_READ_ONLY + +/* ISMASampleFormat Box */ +GF_Box *iSFM_New() +{ + GF_ISMASampleFormatBox *tmp = (GF_ISMASampleFormatBox *) malloc(sizeof(GF_ISMASampleFormatBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ISMASampleFormatBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_ISFM; + return (GF_Box *)tmp; +} + +void iSFM_del(GF_Box *s) +{ + GF_ISMASampleFormatBox *ptr = (GF_ISMASampleFormatBox *)s; + if (ptr == NULL) return; + free(ptr); +} + + +GF_Err iSFM_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ISMASampleFormatBox *ptr = (GF_ISMASampleFormatBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->selective_encryption = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + ptr->key_indicator_length = gf_bs_read_u8(bs); + ptr->IV_length = gf_bs_read_u8(bs); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err iSFM_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ISMASampleFormatBox *ptr = (GF_ISMASampleFormatBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_int(bs, ptr->selective_encryption, 1); + gf_bs_write_int(bs, 0, 7); + gf_bs_write_u8(bs, ptr->key_indicator_length); + gf_bs_write_u8(bs, ptr->IV_length); + return GF_OK; +} + +GF_Err iSFM_Size(GF_Box *s) +{ + GF_Err e; + GF_ISMASampleFormatBox *ptr = (GF_ISMASampleFormatBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 3; + return GF_OK; +} +#endif //GPAC_READ_ONLY + + + +/* OMADRMCommonHeader Box */ +GF_Box *ohdr_New() +{ + GF_OMADRMCommonHeaderBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMCommonHeaderBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_OHDR; + tmp->ExtendedHeaders = gf_list_new(); + return (GF_Box *)tmp; +} + +void ohdr_del(GF_Box *s) +{ + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox*)s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->ExtendedHeaders); + if (ptr->ContentID) free(ptr->ContentID); + if (ptr->RightsIssuerURL) free(ptr->RightsIssuerURL); + if (ptr->TextualHeaders) free(ptr->TextualHeaders); + free(ptr); +} + +GF_Err ohdr_AddBox(GF_Box *s, GF_Box *a) +{ + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox*)s; + return gf_list_add(ptr->ExtendedHeaders, a); +} + +GF_Err ohdr_Read(GF_Box *s, GF_BitStream *bs) +{ + u16 cid_len, ri_len; + GF_Err e; + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox*)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->EncryptionMethod = gf_bs_read_u8(bs); + ptr->PaddingScheme = gf_bs_read_u8(bs); + ptr->PlaintextLength = gf_bs_read_u64(bs); + cid_len = gf_bs_read_u16(bs); + ri_len = gf_bs_read_u16(bs); + ptr->TextualHeadersLen = gf_bs_read_u16(bs); + ptr->size -= 1+1+8+2+2+2; + if (ptr->sizeTextualHeadersLen) return GF_ISOM_INVALID_FILE; + + if (cid_len) { + ptr->ContentID = (char *)malloc(sizeof(char)*(cid_len+1)); + gf_bs_read_data(bs, ptr->ContentID, cid_len); + ptr->ContentID[cid_len]=0; + } + + if (ri_len) { + ptr->RightsIssuerURL = (char *)malloc(sizeof(char)*(ri_len+1)); + gf_bs_read_data(bs, ptr->RightsIssuerURL, ri_len); + ptr->RightsIssuerURL[ri_len]=0; + } + + if (ptr->TextualHeadersLen) { + ptr->TextualHeaders = (char *)malloc(sizeof(char)*(ptr->TextualHeadersLen+1)); + gf_bs_read_data(bs, ptr->TextualHeaders, ptr->TextualHeadersLen); + ptr->TextualHeaders[ptr->TextualHeadersLen] = 0; + } + + ptr->size -= cid_len+ri_len+ptr->TextualHeadersLen; + + return gf_isom_read_box_list(s, bs, ohdr_AddBox); +} + +#ifndef GPAC_READ_ONLY +GF_Err ohdr_Write(GF_Box *s, GF_BitStream *bs) +{ + u16 cid_len, ri_len; + GF_Err e; + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u8(bs, ptr->EncryptionMethod); + gf_bs_write_u8(bs, ptr->PaddingScheme); + gf_bs_write_u64(bs, ptr->PlaintextLength); + + cid_len = ptr->ContentID ? strlen(ptr->ContentID) : 0; + gf_bs_write_u16(bs, cid_len); + ri_len = ptr->RightsIssuerURL ? strlen(ptr->RightsIssuerURL) : 0; + gf_bs_write_u16(bs, ri_len); + gf_bs_write_u16(bs, ptr->TextualHeadersLen); + + if (cid_len) gf_bs_write_data(bs, ptr->ContentID, strlen(ptr->ContentID)); + if (ri_len) gf_bs_write_data(bs, ptr->RightsIssuerURL, strlen(ptr->RightsIssuerURL)); + if (ptr->TextualHeadersLen) gf_bs_write_data(bs, ptr->TextualHeaders, ptr->TextualHeadersLen); + ptr->size -= cid_len+ri_len+ptr->TextualHeadersLen; + return gf_isom_box_array_write(s, ptr->ExtendedHeaders, bs); +} + +GF_Err ohdr_Size(GF_Box *s) +{ + GF_Err e; + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 1+1+8+2+2+2; + if (ptr->ContentID) ptr->size += strlen(ptr->ContentID); + if (ptr->RightsIssuerURL) ptr->size += strlen(ptr->RightsIssuerURL); + if (ptr->TextualHeadersLen) ptr->size += ptr->TextualHeadersLen; + return gf_isom_box_array_size(s, ptr->ExtendedHeaders); +} +#endif //GPAC_READ_ONLY + + +/* OMADRMGroupID Box */ +GF_Box *grpi_New() +{ + GF_OMADRMGroupIDBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMGroupIDBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_GRPI; + return (GF_Box *)tmp; +} + +void grpi_del(GF_Box *s) +{ + GF_OMADRMGroupIDBox *ptr = (GF_OMADRMGroupIDBox *)s; + if (ptr == NULL) return; + if (ptr->GroupID) free(ptr->GroupID); + if (ptr->GroupKey) free(ptr->GroupKey); + free(ptr); +} + +GF_Err grpi_Read(GF_Box *s, GF_BitStream *bs) +{ + u16 gid_len; + GF_Err e; + GF_OMADRMGroupIDBox *ptr = (GF_OMADRMGroupIDBox*)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + gid_len = gf_bs_read_u16(bs); + ptr->GKEncryptionMethod = gf_bs_read_u8(bs); + ptr->GKLength = gf_bs_read_u16(bs); + + ptr->size -= 1+2+2; + if (ptr->sizeGKLength) return GF_ISOM_INVALID_FILE; + + ptr->GroupID = malloc(sizeof(char)*(gid_len+1)); + gf_bs_read_data(bs, ptr->GroupID, gid_len); + ptr->GroupID[gid_len]=0; + + ptr->GroupKey = (char *)malloc(sizeof(char)*ptr->GKLength); + gf_bs_read_data(bs, ptr->GroupKey, ptr->GKLength); + ptr->size -= gid_len+ptr->GKLength; + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err grpi_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u16 gid_len; + GF_OMADRMGroupIDBox *ptr = (GF_OMADRMGroupIDBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gid_len = ptr->GroupID ? strlen(ptr->GroupID) : 0; + gf_bs_write_u16(bs, gid_len); + gf_bs_write_u8(bs, ptr->GKEncryptionMethod); + gf_bs_write_u16(bs, ptr->GKLength); + gf_bs_write_data(bs, ptr->GroupID, gid_len); + gf_bs_write_data(bs, ptr->GroupKey, ptr->GKLength); + return GF_OK; +} + +GF_Err grpi_Size(GF_Box *s) +{ + GF_Err e; + GF_OMADRMGroupIDBox *ptr = (GF_OMADRMGroupIDBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 2+2+1 + ptr->GKLength; + if (ptr->GroupID) ptr->size += strlen(ptr->GroupID); + return GF_OK; +} +#endif //GPAC_READ_ONLY + + + + +/* OMADRMMutableInformation Box */ +GF_Box *mdri_New() +{ + GF_OMADRMMutableInformationBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMMutableInformationBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_MDRI; + tmp->boxes = gf_list_new(); + return (GF_Box *)tmp; +} + +void mdri_del(GF_Box *s) +{ + GF_OMADRMMutableInformationBox*ptr = (GF_OMADRMMutableInformationBox*)s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->boxes); + free(ptr); +} + +GF_Err mdri_AddBox(GF_Box *s, GF_Box *a) +{ + GF_OMADRMMutableInformationBox *ptr = (GF_OMADRMMutableInformationBox *)s; + return gf_list_add(ptr->boxes, a); +} + +GF_Err mdri_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, mdri_AddBox); +} + +#ifndef GPAC_READ_ONLY +GF_Err mdri_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_OMADRMMutableInformationBox*ptr = (GF_OMADRMMutableInformationBox*)s; + GF_Err e = gf_isom_box_write_header(s, bs); + if (e) return e; + return gf_isom_box_array_write(s, ptr->boxes, bs); +} + +GF_Err mdri_Size(GF_Box *s) +{ + GF_Err e; + GF_OMADRMMutableInformationBox *ptr = (GF_OMADRMMutableInformationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_get_size(s); + if (e) return e; + + return gf_isom_box_array_size(s, ptr->boxes); +} +#endif //GPAC_READ_ONLY + + +/* OMADRMTransactionTracking Box */ +GF_Box *odtt_New() +{ + GF_OMADRMTransactionTrackingBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMTransactionTrackingBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_ODTT; + return (GF_Box *)tmp; +} + +void odtt_del(GF_Box *s) +{ + GF_OMADRMTransactionTrackingBox *ptr = (GF_OMADRMTransactionTrackingBox*)s; + free(ptr); +} + +GF_Err odtt_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OMADRMTransactionTrackingBox *ptr = (GF_OMADRMTransactionTrackingBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + gf_bs_read_data(bs, ptr->TransactionID, 16); + ptr->size -= 16; + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err odtt_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OMADRMTransactionTrackingBox *ptr = (GF_OMADRMTransactionTrackingBox*)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->TransactionID, 16); + return GF_OK; +} + +GF_Err odtt_Size(GF_Box *s) +{ + GF_Err e; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + s->size += 16; + return GF_OK; +} +#endif //GPAC_READ_ONLY + + + +/* OMADRMRightsObject Box */ +GF_Box *odrb_New() +{ + GF_OMADRMRightsObjectBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMRightsObjectBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_ODRB; + return (GF_Box *)tmp; +} + +void odrb_del(GF_Box *s) +{ + GF_OMADRMRightsObjectBox *ptr = (GF_OMADRMRightsObjectBox*)s; + if (ptr->oma_ro) free(ptr->oma_ro); + free(ptr); +} + +GF_Err odrb_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OMADRMRightsObjectBox *ptr = (GF_OMADRMRightsObjectBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->oma_ro_size = (u32) ptr->size; + ptr->oma_ro = (char*) malloc(sizeof(char)*ptr->oma_ro_size); + gf_bs_read_data(bs, ptr->oma_ro, ptr->oma_ro_size); + ptr->size = 0; + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err odrb_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OMADRMRightsObjectBox *ptr = (GF_OMADRMRightsObjectBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->oma_ro, ptr->oma_ro_size); + return GF_OK; +} + +GF_Err odrb_Size(GF_Box *s) +{ + GF_Err e; + GF_OMADRMRightsObjectBox *ptr = (GF_OMADRMRightsObjectBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + s->size += ptr->oma_ro_size; + return GF_OK; +} +#endif //GPAC_READ_ONLY + + + + +/* OMADRMKMS Box */ +GF_Box *odkm_New() +{ + GF_OMADRMKMSBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMKMSBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_ODKM; + return (GF_Box *)tmp; +} + +void odkm_del(GF_Box *s) +{ + GF_OMADRMKMSBox *ptr = (GF_OMADRMKMSBox *)s; + if (ptr->hdr) gf_isom_box_del((GF_Box*)ptr->hdr); + if (ptr->fmt) gf_isom_box_del((GF_Box*)ptr->fmt); + free(ptr); +} + +GF_Err odkm_Add(GF_Box *s, GF_Box *a) +{ + GF_OMADRMKMSBox *ptr = (GF_OMADRMKMSBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_OHDR: + if (ptr->hdr) gf_isom_box_del((GF_Box*)ptr->hdr); + ptr->hdr = (GF_OMADRMCommonHeaderBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_ODAF: + if (ptr->fmt) gf_isom_box_del((GF_Box*)ptr->fmt); + ptr->fmt = (GF_OMADRMAUFormatBox*)a; + return GF_OK; + default: + gf_isom_box_del(a); + return GF_OK; + } +} + +GF_Err odkm_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + if (s == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + return gf_isom_read_box_list(s, bs, odkm_Add); +} + +#ifndef GPAC_READ_ONLY +GF_Err odkm_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OMADRMKMSBox *ptr = (GF_OMADRMKMSBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->hdr) { + e = gf_isom_box_write((GF_Box*)ptr->hdr, bs); + if (e) return e; + } + if (ptr->fmt) { + e = gf_isom_box_write((GF_Box*)ptr->fmt, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err odkm_Size(GF_Box *s) +{ + GF_Err e; + GF_OMADRMKMSBox *ptr = (GF_OMADRMKMSBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + if (ptr->hdr) { + e = gf_isom_box_size((GF_Box*)ptr->hdr); + if (e) return e; + ptr->size += ptr->hdr->size; + } + if (ptr->fmt) { + e = gf_isom_box_size((GF_Box*)ptr->fmt); + if (e) return e; + ptr->size += ptr->fmt->size; + } + return GF_OK; +} +#endif //GPAC_READ_ONLY + + diff --git a/src/isomedia/box_code_meta.c b/src/isomedia/box_code_meta.c new file mode 100644 index 0000000..3b7f701 --- /dev/null +++ b/src/isomedia/box_code_meta.c @@ -0,0 +1,773 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato / Jean Le Feuvre 2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +GF_Box *meta_New() +{ + GF_MetaBox *tmp = (GF_MetaBox *) malloc(sizeof(GF_MetaBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MetaBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_META; + tmp->other_boxes = gf_list_new(); + return (GF_Box *)tmp; +} + +void meta_del(GF_Box *s) +{ + u32 count, i; + GF_MetaBox *ptr = (GF_MetaBox *)s; + if (ptr == NULL) return; + gf_isom_box_del((GF_Box *)ptr->handler); + if (ptr->primary_resource) gf_isom_box_del((GF_Box *)ptr->primary_resource); + if (ptr->file_locations) gf_isom_box_del((GF_Box *)ptr->file_locations); + if (ptr->item_locations) gf_isom_box_del((GF_Box *)ptr->item_locations); + if (ptr->protections) gf_isom_box_del((GF_Box *)ptr->protections); + if (ptr->item_infos) gf_isom_box_del((GF_Box *)ptr->item_infos); + if (ptr->IPMP_control) gf_isom_box_del((GF_Box *)ptr->IPMP_control); + count = gf_list_count(ptr->other_boxes); + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->other_boxes, i); + gf_isom_box_del(a); + } + gf_list_del(ptr->other_boxes); + free(ptr); +} + +GF_Err meta_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MetaBox *ptr = (GF_MetaBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_HDLR: + if (ptr->handler) return GF_ISOM_INVALID_FILE; + ptr->handler = (GF_HandlerBox*)a; + break; + case GF_ISOM_BOX_TYPE_PITM: + if (ptr->primary_resource) return GF_ISOM_INVALID_FILE; + ptr->primary_resource = (GF_PrimaryItemBox*)a; + break; + case GF_ISOM_BOX_TYPE_DINF: + if (ptr->file_locations) return GF_ISOM_INVALID_FILE; + ptr->file_locations = (GF_DataInformationBox*)a; + break; + case GF_ISOM_BOX_TYPE_ILOC: + if (ptr->item_locations) return GF_ISOM_INVALID_FILE; + ptr->item_locations = (GF_ItemLocationBox*)a; + break; + case GF_ISOM_BOX_TYPE_IPRO: + if (ptr->protections) return GF_ISOM_INVALID_FILE; + ptr->protections = (GF_ItemProtectionBox*)a; + break; + case GF_ISOM_BOX_TYPE_IINF: + if (ptr->item_infos) return GF_ISOM_INVALID_FILE; + ptr->item_infos = (GF_ItemInfoBox*)a; + break; + //case ???: ptr->IPMP_control = (???*)a; break; + case GF_ISOM_BOX_TYPE_XML: + case GF_ISOM_BOX_TYPE_BXML: + case GF_ISOM_BOX_TYPE_ILST: + gf_list_add(ptr->other_boxes, a); break; + default: + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err meta_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 next_size = gf_bs_peek_bits(bs, 32, 4); + GF_Err e; + /*try to hack around QT files which don't use a full box for meta*/ + if (next_sizesize) { + e = gf_isom_full_box_read(s, bs); + if (e) return e; + } + return gf_isom_read_box_list(s, bs, meta_AddBox); +} + +#ifndef GPAC_READ_ONLY +GF_Err meta_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 count, i; + GF_Err e; + GF_MetaBox *ptr = (GF_MetaBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + e = gf_isom_box_write((GF_Box *) ptr->handler, bs); + if (e) return e; + if (ptr->primary_resource) { + e = gf_isom_box_write((GF_Box *) ptr->primary_resource, bs); + if (e) return e; + } + if (ptr->file_locations) { + e = gf_isom_box_write((GF_Box *) ptr->file_locations, bs); + if (e) return e; + } + if (ptr->item_locations) { + e = gf_isom_box_write((GF_Box *) ptr->item_locations, bs); + if (e) return e; + } + if (ptr->protections) { + e = gf_isom_box_write((GF_Box *) ptr->protections, bs); + if (e) return e; + } + if (ptr->item_infos) { + e = gf_isom_box_write((GF_Box *) ptr->item_infos, bs); + if (e) return e; + } + if (ptr->IPMP_control) { + e = gf_isom_box_write((GF_Box *) ptr->IPMP_control, bs); + if (e) return e; + } + if ((count = gf_list_count(ptr->other_boxes))) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->other_boxes, i); + e = gf_isom_box_write(a, bs); + if (e) return e; + } + } + return GF_OK; +} + +GF_Err meta_Size(GF_Box *s) +{ + u32 i, count; + GF_Err e; + GF_MetaBox *ptr = (GF_MetaBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + e = gf_isom_box_size((GF_Box *) ptr->handler); + if (e) return e; + ptr->size += ptr->handler->size; + if (ptr->primary_resource) { + e = gf_isom_box_size((GF_Box *) ptr->primary_resource); + if (e) return e; + ptr->size += ptr->primary_resource->size; + } + if (ptr->file_locations) { + e = gf_isom_box_size((GF_Box *) ptr->file_locations); + if (e) return e; + ptr->size += ptr->file_locations->size; + } + if (ptr->item_locations) { + e = gf_isom_box_size((GF_Box *) ptr->item_locations); + if (e) return e; + ptr->size += ptr->item_locations->size; + } + if (ptr->protections) { + e = gf_isom_box_size((GF_Box *) ptr->protections); + if (e) return e; + ptr->size += ptr->protections->size; + } + if (ptr->item_infos) { + e = gf_isom_box_size((GF_Box *) ptr->item_infos); + if (e) return e; + ptr->size += ptr->item_infos->size; + } + if (ptr->IPMP_control) { + e = gf_isom_box_size((GF_Box *) ptr->IPMP_control); + if (e) return e; + ptr->size += ptr->IPMP_control->size; + } + if ((count = gf_list_count(ptr->other_boxes))) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->other_boxes, i); + e = gf_isom_box_size(a); + if (e) return e; + ptr->size += a->size; + } + } + return GF_OK; +} +#endif + +GF_Box *xml_New() +{ + GF_XMLBox *tmp = (GF_XMLBox *) malloc(sizeof(GF_XMLBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_XMLBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_XML; + return (GF_Box *)tmp; +} + +void xml_del(GF_Box *s) +{ + GF_XMLBox *ptr = (GF_XMLBox *)s; + if (ptr == NULL) return; + if (ptr->xml_length && ptr->xml) free(ptr->xml); + free(ptr); +} + +GF_Err xml_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_XMLBox *ptr = (GF_XMLBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->xml_length = (u32)(ptr->size); + ptr->xml = (char *)malloc(sizeof(char)*ptr->xml_length); + if (!ptr->xml) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->xml, ptr->xml_length); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err xml_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_XMLBox *ptr = (GF_XMLBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->xml_length) gf_bs_write_data(bs, ptr->xml, ptr->xml_length); + return GF_OK; +} + +GF_Err xml_Size(GF_Box *s) +{ + GF_Err e; + GF_XMLBox *ptr = (GF_XMLBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += ptr->xml_length; + return GF_OK; +} +#endif + +GF_Box *bxml_New() +{ + GF_BinaryXMLBox *tmp = (GF_BinaryXMLBox *) malloc(sizeof(GF_BinaryXMLBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_BinaryXMLBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_BXML; + return (GF_Box *)tmp; +} + +void bxml_del(GF_Box *s) +{ + GF_BinaryXMLBox *ptr = (GF_BinaryXMLBox *)s; + if (ptr == NULL) return; + if (ptr->data_length && ptr->data) free(ptr->data); + free(ptr); +} + +GF_Err bxml_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_BinaryXMLBox *ptr = (GF_BinaryXMLBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->data_length = (u32)(ptr->size); + ptr->data = (char*)malloc(sizeof(char)*ptr->data_length); + if (!ptr->data) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->data, ptr->data_length); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err bxml_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_BinaryXMLBox *ptr = (GF_BinaryXMLBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->data_length) gf_bs_write_data(bs, ptr->data, ptr->data_length); + return GF_OK; +} + +GF_Err bxml_Size(GF_Box *s) +{ + GF_Err e; + GF_BinaryXMLBox *ptr = (GF_BinaryXMLBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += ptr->data_length; + return GF_OK; +} +#endif + +GF_Box *iloc_New() +{ + GF_ItemLocationBox *tmp = (GF_ItemLocationBox *) malloc(sizeof(GF_ItemLocationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ItemLocationBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_ILOC; + tmp->location_entries = gf_list_new(); + return (GF_Box *)tmp; +} + +void iloc_del(GF_Box *s) +{ + u32 i, j, item_count, extent_count; + GF_ItemLocationBox *ptr = (GF_ItemLocationBox *)s; + if (ptr == NULL) return; + item_count = gf_list_count(ptr->location_entries); + if (item_count) { + for (i = 0; i < item_count; i++) { + GF_ItemLocationEntry *location = (GF_ItemLocationEntry *)gf_list_get(ptr->location_entries, i); + extent_count = gf_list_count(location->extent_entries); + for (j = 0; j < extent_count; j++) { + GF_ItemExtentEntry *extent = (GF_ItemExtentEntry *)gf_list_get(location->extent_entries, j); + free(extent); + } + gf_list_del(location->extent_entries); + free(location); + } + gf_list_del(ptr->location_entries); + } + free(ptr); +} + +GF_Err iloc_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 item_count, extent_count, i, j; + GF_ItemLocationBox *ptr = (GF_ItemLocationBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->offset_size = gf_bs_read_int(bs, 4); + ptr->length_size = gf_bs_read_int(bs, 4); + ptr->base_offset_size = gf_bs_read_int(bs, 4); + gf_bs_read_int(bs, 4); + item_count = gf_bs_read_u16(bs); + for (i = 0; i < item_count; i++) { + GF_ItemLocationEntry *location_entry = (GF_ItemLocationEntry *)malloc(sizeof(GF_ItemLocationEntry)); + gf_list_add(ptr->location_entries, location_entry); + location_entry->item_ID = gf_bs_read_u16(bs); + location_entry->data_reference_index = gf_bs_read_u16(bs); + location_entry->base_offset = gf_bs_read_int(bs, 8*ptr->base_offset_size); +#ifndef GPAC_READ_ONLY + location_entry->original_base_offset = location_entry->base_offset; +#endif + + extent_count = gf_bs_read_u16(bs); + location_entry->extent_entries = gf_list_new(); + for (j = 0; j < extent_count; j++) { + GF_ItemExtentEntry *extent_entry = (GF_ItemExtentEntry *)malloc(sizeof(GF_ItemExtentEntry)); + gf_list_add(location_entry->extent_entries, extent_entry); + extent_entry->extent_offset = gf_bs_read_int(bs, 8*ptr->offset_size); + extent_entry->extent_length = gf_bs_read_int(bs, 8*ptr->length_size); +#ifndef GPAC_READ_ONLY + extent_entry->original_extent_offset = extent_entry->extent_offset; +#endif + } + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err iloc_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, j, item_count, extent_count; + GF_ItemLocationBox *ptr = (GF_ItemLocationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_int(bs, ptr->offset_size, 4); + gf_bs_write_int(bs, ptr->length_size, 4); + gf_bs_write_int(bs, ptr->base_offset_size, 4); + gf_bs_write_int(bs, 0, 4); + item_count = gf_list_count(ptr->location_entries); + gf_bs_write_u16(bs, item_count); + for (i = 0; i < item_count; i++) { + GF_ItemLocationEntry *location = (GF_ItemLocationEntry *)gf_list_get(ptr->location_entries, i); + gf_bs_write_u16(bs, location->item_ID); + gf_bs_write_u16(bs, location->data_reference_index); + gf_bs_write_long_int(bs, location->base_offset, 8*ptr->base_offset_size); + extent_count = gf_list_count(location->extent_entries); + gf_bs_write_u16(bs, extent_count); + for (j=0; jextent_entries, j); + gf_bs_write_long_int(bs, extent->extent_offset, 8*ptr->offset_size); + gf_bs_write_long_int(bs, extent->extent_length, 8*ptr->length_size); + } + } + return GF_OK; +} + +GF_Err iloc_Size(GF_Box *s) +{ + GF_Err e; + u32 i, item_count, extent_count; + GF_ItemLocationBox *ptr = (GF_ItemLocationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + item_count = gf_list_count(ptr->location_entries); + for (i = 0; i < item_count; i++) { + GF_ItemLocationEntry *location = (GF_ItemLocationEntry *)gf_list_get(ptr->location_entries, i); + extent_count = gf_list_count(location->extent_entries); + ptr->size += 6 + ptr->base_offset_size + extent_count*(ptr->offset_size + ptr->length_size); + } + return GF_OK; +} +#endif + +GF_Box *pitm_New() +{ + GF_PrimaryItemBox *tmp = (GF_PrimaryItemBox *) malloc(sizeof(GF_PrimaryItemBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_PrimaryItemBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_PITM; + return (GF_Box *)tmp; +} + +void pitm_del(GF_Box *s) +{ + GF_PrimaryItemBox *ptr = (GF_PrimaryItemBox *)s; + if (ptr == NULL) return; + free(ptr); +} + +GF_Err pitm_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_PrimaryItemBox *ptr = (GF_PrimaryItemBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->item_ID = gf_bs_read_u16(bs); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err pitm_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_PrimaryItemBox *ptr = (GF_PrimaryItemBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->item_ID); + return GF_OK; +} + +GF_Err pitm_Size(GF_Box *s) +{ + GF_Err e; + GF_ItemLocationBox *ptr = (GF_ItemLocationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 16; + return GF_OK; +} +#endif + +GF_Box *ipro_New() +{ + GF_ItemProtectionBox *tmp = (GF_ItemProtectionBox *) malloc(sizeof(GF_ItemProtectionBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ItemProtectionBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_IPRO; + tmp->protection_information = gf_list_new(); + return (GF_Box *)tmp; +} + +void ipro_del(GF_Box *s) +{ + u32 count, i; + GF_ItemProtectionBox *ptr = (GF_ItemProtectionBox *)s; + if (ptr == NULL) return; + count = gf_list_count(ptr->protection_information); + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->protection_information, i); + gf_isom_box_del(a); + } + gf_list_del(ptr->protection_information); + free(ptr); +} + +GF_Err ipro_AddBox(GF_Box *s, GF_Box *a) +{ + GF_ItemProtectionBox *ptr = (GF_ItemProtectionBox *)s; + if (a->type == GF_ISOM_BOX_TYPE_SINF) + gf_list_add(ptr->protection_information, a); + else + gf_isom_box_del(a); + return GF_OK; +} +GF_Err ipro_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + return gf_isom_read_box_list(s, bs, ipro_AddBox); +} + +#ifndef GPAC_READ_ONLY +GF_Err ipro_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 count, i; + GF_Err e; + GF_ItemProtectionBox *ptr = (GF_ItemProtectionBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + count = gf_list_count(ptr->protection_information); + gf_bs_write_u16(bs, count); + if (count) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->protection_information, i); + e = gf_isom_box_write(a, bs); + if (e) return e; + } + } + return GF_OK; +} + +GF_Err ipro_Size(GF_Box *s) +{ + u32 i, count; + GF_Err e; + GF_ItemProtectionBox *ptr = (GF_ItemProtectionBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 2; + if ((count = gf_list_count(ptr->protection_information))) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->protection_information, i); + e = gf_isom_box_size(a); + if (e) return e; + ptr->size += a->size; + } + } + return GF_OK; +} +#endif + +GF_Box *infe_New() +{ + GF_ItemInfoEntryBox *tmp = (GF_ItemInfoEntryBox *) malloc(sizeof(GF_ItemInfoEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ItemInfoEntryBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_INFE; + return (GF_Box *)tmp; +} + +void infe_del(GF_Box *s) +{ + GF_ItemInfoEntryBox *ptr = (GF_ItemInfoEntryBox *)s; + if (ptr == NULL) return; + if (ptr->item_name) free(ptr->item_name); + if (ptr->full_path) free(ptr->full_path); + if (ptr->content_type) free(ptr->content_type); + if (ptr->content_encoding) free(ptr->content_encoding); + free(ptr); +} + +GF_Err infe_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + char *buf; + u32 buf_len, i, string_len, string_start; + GF_ItemInfoEntryBox *ptr = (GF_ItemInfoEntryBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + ptr->item_ID = gf_bs_read_u16(bs); + ptr->item_protection_index = gf_bs_read_u16(bs); + ptr->size -= 4; + buf_len = (u32) (ptr->size); + buf = (char*)malloc(buf_len); + if (buf_len != gf_bs_read_data(bs, buf, buf_len)) { + free(buf); + return GF_ISOM_INVALID_FILE; + } + string_len = 1; + string_start = 0; + for (i = 0; i < buf_len; i++) { + if (buf[i] == 0) { + if (!ptr->item_name) { + ptr->item_name = (char*)malloc(sizeof(char)*string_len); + memcpy(ptr->item_name, buf+string_start, string_len); + } else if (!ptr->content_type) { + ptr->content_type = (char*)malloc(sizeof(char)*string_len); + memcpy(ptr->content_type, buf+string_start, string_len); + } else { + ptr->content_encoding = (char*)malloc(sizeof(char)*string_len); + memcpy(ptr->content_encoding, buf+string_start, string_len); + } + string_start += string_len; + string_len = 0; + } + string_len++; + } + free(buf); + if (!ptr->item_name || !ptr->content_type) return GF_ISOM_INVALID_FILE; + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err infe_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 len; + GF_ItemInfoEntryBox *ptr = (GF_ItemInfoEntryBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->item_ID); + gf_bs_write_u16(bs, ptr->item_protection_index); + if (ptr->item_name) { + len = strlen(ptr->item_name)+1; + gf_bs_write_data(bs, ptr->item_name, len); + } + if (ptr->content_type) { + len = strlen(ptr->content_type)+1; + gf_bs_write_data(bs, ptr->content_type, len); + } + if (ptr->content_encoding) { + len = strlen(ptr->content_encoding)+1; + gf_bs_write_data(bs, ptr->content_encoding, len); + } + return GF_OK; +} + +GF_Err infe_Size(GF_Box *s) +{ + GF_Err e; + GF_ItemInfoEntryBox *ptr = (GF_ItemInfoEntryBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + if (ptr->item_name) ptr->size += strlen(ptr->item_name)+1; + if (ptr->content_type) ptr->size += strlen(ptr->content_type)+1; + if (ptr->content_encoding) ptr->size += strlen(ptr->content_encoding)+1; + return GF_OK; +} +#endif + +GF_Box *iinf_New() +{ + GF_ItemInfoBox *tmp = (GF_ItemInfoBox *) malloc(sizeof(GF_ItemInfoBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ItemInfoBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_IINF; + tmp->item_infos = gf_list_new(); + return (GF_Box *)tmp; +} + +void iinf_del(GF_Box *s) +{ + u32 count, i; + GF_ItemInfoBox *ptr = (GF_ItemInfoBox *)s; + if (ptr == NULL) return; + count = gf_list_count(ptr->item_infos); + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->item_infos, i); + gf_isom_box_del(a); + } + gf_list_del(ptr->item_infos); + free(ptr); +} + +GF_Err iinf_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_Box *a; + u32 count; + GF_ItemInfoBox *ptr = (GF_ItemInfoBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + count = gf_bs_read_u16(bs); + + while (count) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + + if (a->type == GF_ISOM_BOX_TYPE_INFE) + gf_list_add(ptr->item_infos, a); + else + gf_isom_box_del(a); + count --; + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err iinf_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 count, i; + GF_Err e; + GF_ItemInfoBox *ptr = (GF_ItemInfoBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + count = gf_list_count(ptr->item_infos); + gf_bs_write_u16(bs, count); + if (count) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->item_infos, i); + e = gf_isom_box_write(a, bs); + if (e) return e; + } + } + return GF_OK; +} + +GF_Err iinf_Size(GF_Box *s) +{ + u32 i, count; + GF_Err e; + GF_ItemInfoBox *ptr = (GF_ItemInfoBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 2; + if ((count = gf_list_count(ptr->item_infos))) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->item_infos, i); + e = gf_isom_box_size(a); + if (e) return e; + ptr->size += a->size; + } + } + return GF_OK; +} +#endif + + + diff --git a/src/isomedia/box_dump.c b/src/isomedia/box_dump.c new file mode 100644 index 0000000..6de107e --- /dev/null +++ b/src/isomedia/box_dump.c @@ -0,0 +1,3251 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +static GF_Err apple_tag_dump(GF_Box *a, FILE * trace); + +void NullBoxErr(FILE * trace) +{ + fprintf(trace, "\n"); +} + +void BadTopBoxErr(GF_Box *a, FILE * trace) +{ + fprintf(trace, "\n", gf_4cc_to_str(a->type)); +} + +static void DumpData(FILE *trace, char *data, u32 dataLength) +{ + u32 i; + fprintf(trace, "data:application/octet-string,"); + for (i=0; isize > 0xFFFFFFFF) { + fprintf(trace, "size); + } else { + fprintf(trace, "size); + } + if (a->type == GF_ISOM_BOX_TYPE_UUID) { + u32 i; + fprintf(trace, "UUID=\"{"); + for (i=0; i<16; i++) { + fprintf(trace, "%02X", (unsigned char) ((GF_UUIDBox*)a)->uuid[i]); + if ((i<15) && (i%4)==3) fprintf(trace, "-"); + } + fprintf(trace, "}\"/>\n"); + } else { + fprintf(trace, "Type=\"%s\"/>\n", gf_4cc_to_str(a->type)); + } + return GF_OK; +} + + +GF_Err gf_box_dump(void *ptr, FILE * trace) +{ + GF_Box *a = (GF_Box *) ptr; + + if (!a) { + NullBoxErr(trace); + return GF_OK; + } + + switch (a->type) { + case GF_ISOM_BOX_TYPE_REFT: + return reftype_dump(a, trace); + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + return free_dump(a, trace); + case GF_ISOM_BOX_TYPE_MDAT: return mdat_dump(a, trace); + case GF_ISOM_BOX_TYPE_MOOV: return moov_dump(a, trace); + case GF_ISOM_BOX_TYPE_MVHD: return mvhd_dump(a, trace); + case GF_ISOM_BOX_TYPE_MDHD: return mdhd_dump(a, trace); + case GF_ISOM_BOX_TYPE_VMHD: return vmhd_dump(a, trace); + case GF_ISOM_BOX_TYPE_SMHD: return smhd_dump(a, trace); + case GF_ISOM_BOX_TYPE_HMHD: return hmhd_dump(a, trace); + //the same box is used for all MPEG4 systems streams + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + return nmhd_dump(a, trace); + case GF_ISOM_BOX_TYPE_STBL: return stbl_dump(a, trace); + case GF_ISOM_BOX_TYPE_DINF: return dinf_dump(a, trace); + case GF_ISOM_BOX_TYPE_URL: return url_dump(a, trace); + case GF_ISOM_BOX_TYPE_URN: return urn_dump(a, trace); + case GF_ISOM_BOX_TYPE_CPRT: return cprt_dump(a, trace); + case GF_ISOM_BOX_TYPE_HDLR: return hdlr_dump(a, trace); + case GF_ISOM_BOX_TYPE_IODS: return iods_dump(a, trace); + case GF_ISOM_BOX_TYPE_TRAK: return trak_dump(a, trace); + case GF_ISOM_BOX_TYPE_MP4S: return mp4s_dump(a, trace); + case GF_ISOM_BOX_TYPE_MP4V: return mp4v_dump(a, trace); + case GF_ISOM_BOX_TYPE_MP4A: return mp4a_dump(a, trace); + case GF_ISOM_BOX_TYPE_GNRM: return gnrm_dump(a, trace); + case GF_ISOM_BOX_TYPE_GNRV: return gnrv_dump(a, trace); + case GF_ISOM_BOX_TYPE_GNRA: return gnra_dump(a, trace); + case GF_ISOM_BOX_TYPE_EDTS: return edts_dump(a, trace); + case GF_ISOM_BOX_TYPE_UDTA: return udta_dump(a, trace); + case GF_ISOM_BOX_TYPE_DREF: return dref_dump(a, trace); + case GF_ISOM_BOX_TYPE_STSD: return stsd_dump(a, trace); + case GF_ISOM_BOX_TYPE_STTS: return stts_dump(a, trace); + case GF_ISOM_BOX_TYPE_CTTS: return ctts_dump(a, trace); + case GF_ISOM_BOX_TYPE_STSH: return stsh_dump(a, trace); + case GF_ISOM_BOX_TYPE_ELST: return elst_dump(a, trace); + case GF_ISOM_BOX_TYPE_STSC: return stsc_dump(a, trace); + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + return stsz_dump(a, trace); + case GF_ISOM_BOX_TYPE_STCO: return stco_dump(a, trace); + case GF_ISOM_BOX_TYPE_STSS: return stss_dump(a, trace); + case GF_ISOM_BOX_TYPE_STDP: return stdp_dump(a, trace); + case GF_ISOM_BOX_TYPE_SDTP: return sdtp_dump(a, trace); + case GF_ISOM_BOX_TYPE_CO64: return co64_dump(a, trace); + case GF_ISOM_BOX_TYPE_ESDS: return esds_dump(a, trace); + case GF_ISOM_BOX_TYPE_MINF: return minf_dump(a, trace); + case GF_ISOM_BOX_TYPE_TKHD: return tkhd_dump(a, trace); + case GF_ISOM_BOX_TYPE_TREF: return tref_dump(a, trace); + case GF_ISOM_BOX_TYPE_MDIA: return mdia_dump(a, trace); + case GF_ISOM_BOX_TYPE_CHPL: return chpl_dump(a, trace); + case GF_ISOM_BOX_TYPE_PDIN: return dpin_dump(a, trace); + + case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_dump(a, trace); + case GF_ISOM_BOX_TYPE_RTPO: return rtpo_dump(a, trace); + case GF_ISOM_BOX_TYPE_HNTI: return hnti_dump(a, trace); + case GF_ISOM_BOX_TYPE_SDP: return sdp_dump(a, trace); + case GF_ISOM_BOX_TYPE_HINF: return hinf_dump(a, trace); + case GF_ISOM_BOX_TYPE_RELY: return rely_dump(a, trace); + case GF_ISOM_BOX_TYPE_TIMS: return tims_dump(a, trace); + case GF_ISOM_BOX_TYPE_TSRO: return tsro_dump(a, trace); + case GF_ISOM_BOX_TYPE_SNRO: return snro_dump(a, trace); + case GF_ISOM_BOX_TYPE_TRPY: return trpy_dump(a, trace); + case GF_ISOM_BOX_TYPE_NUMP: return nump_dump(a, trace); + case GF_ISOM_BOX_TYPE_TOTL: return totl_dump(a, trace); + case GF_ISOM_BOX_TYPE_NPCK: return npck_dump(a, trace); + case GF_ISOM_BOX_TYPE_TPYL: return tpyl_dump(a, trace); + case GF_ISOM_BOX_TYPE_TPAY: return tpay_dump(a, trace); + case GF_ISOM_BOX_TYPE_MAXR: return maxr_dump(a, trace); + case GF_ISOM_BOX_TYPE_DMED: return dmed_dump(a, trace); + case GF_ISOM_BOX_TYPE_DIMM: return dimm_dump(a, trace); + case GF_ISOM_BOX_TYPE_DREP: return drep_dump(a, trace); + case GF_ISOM_BOX_TYPE_TMIN: return tmin_dump(a, trace); + case GF_ISOM_BOX_TYPE_TMAX: return tmax_dump(a, trace); + case GF_ISOM_BOX_TYPE_PMAX: return pmax_dump(a, trace); + case GF_ISOM_BOX_TYPE_DMAX: return dmax_dump(a, trace); + case GF_ISOM_BOX_TYPE_PAYT: return payt_dump(a, trace); + case GF_ISOM_BOX_TYPE_NAME: return name_dump(a, trace); + + case GF_ISOM_BOX_TYPE_FTYP: return ftyp_dump(a, trace); + case GF_ISOM_BOX_TYPE_FADB: return padb_dump(a, trace); + +#ifndef GPAC_ISOM_NO_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: return mvex_dump(a, trace); + case GF_ISOM_BOX_TYPE_MEHD: return mehd_dump(a, trace); + case GF_ISOM_BOX_TYPE_TREX: return trex_dump(a, trace); + case GF_ISOM_BOX_TYPE_MOOF: return moof_dump(a, trace); + case GF_ISOM_BOX_TYPE_MFHD: return mfhd_dump(a, trace); + case GF_ISOM_BOX_TYPE_TRAF: return traf_dump(a, trace); + case GF_ISOM_BOX_TYPE_TFHD: return tfhd_dump(a, trace); + case GF_ISOM_BOX_TYPE_TRUN: return trun_dump(a, trace); +#endif + + case GF_ISOM_BOX_TYPE_VOID: return void_dump(a, trace); + case GF_ISOM_BOX_TYPE_STSF: return stsf_dump(a, trace); + + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_SMV: + return gppa_dump(a, trace); + case GF_ISOM_SUBTYPE_3GP_H263: + return gppv_dump(a, trace); + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + return gppc_dump(a, trace); + + case GF_ISOM_BOX_TYPE_AVCC: return avcc_dump(a, trace); + case GF_ISOM_BOX_TYPE_BTRT: return btrt_dump(a, trace); + case GF_ISOM_BOX_TYPE_M4DS: return m4ds_dump(a, trace); + case GF_ISOM_BOX_TYPE_AVC1: return mp4v_dump(a, trace); + case GF_ISOM_BOX_TYPE_PASP: return pasp_dump(a, trace); + + case GF_ISOM_BOX_TYPE_FTAB: return ftab_dump(a, trace); + case GF_ISOM_BOX_TYPE_TX3G: return tx3g_dump(a, trace); + case GF_ISOM_BOX_TYPE_STYL: return styl_dump(a, trace); + case GF_ISOM_BOX_TYPE_HLIT: return hlit_dump(a, trace); + case GF_ISOM_BOX_TYPE_HCLR: return hclr_dump(a, trace); + case GF_ISOM_BOX_TYPE_KROK: return krok_dump(a, trace); + case GF_ISOM_BOX_TYPE_DLAY: return dlay_dump(a, trace); + case GF_ISOM_BOX_TYPE_HREF: return href_dump(a, trace); + case GF_ISOM_BOX_TYPE_TBOX: return tbox_dump(a, trace); + case GF_ISOM_BOX_TYPE_BLNK: return blnk_dump(a, trace); + case GF_ISOM_BOX_TYPE_TWRP: return twrp_dump(a, trace); + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: return iKMS_dump(a, trace); + case GF_ISOM_BOX_TYPE_ISFM: return iSFM_dump(a, trace); + + /*MPEG-21 extensions*/ + case GF_ISOM_BOX_TYPE_META: return meta_dump(a, trace); + case GF_ISOM_BOX_TYPE_XML: return xml_dump(a, trace); + case GF_ISOM_BOX_TYPE_BXML: return bxml_dump(a, trace); + case GF_ISOM_BOX_TYPE_ILOC: return iloc_dump(a, trace); + case GF_ISOM_BOX_TYPE_PITM: return pitm_dump(a, trace); + case GF_ISOM_BOX_TYPE_IPRO: return ipro_dump(a, trace); + case GF_ISOM_BOX_TYPE_INFE: return infe_dump(a, trace); + case GF_ISOM_BOX_TYPE_IINF: return iinf_dump(a, trace); + case GF_ISOM_BOX_TYPE_SINF: return sinf_dump(a, trace); + case GF_ISOM_BOX_TYPE_FRMA: return frma_dump(a, trace); + case GF_ISOM_BOX_TYPE_SCHM: return schm_dump(a, trace); + case GF_ISOM_BOX_TYPE_SCHI: return schi_dump(a, trace); + case GF_ISOM_BOX_TYPE_ENCA: return mp4a_dump(a, trace); + case GF_ISOM_BOX_TYPE_ENCV: return mp4v_dump(a, trace); + case GF_ISOM_BOX_TYPE_ENCS: return mp4s_dump(a, trace); + + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_COVR: + case GF_ISOM_BOX_TYPE_iTunesSpecificInfo: + return apple_tag_dump(a, trace); + /*Apple extensions*/ + case GF_ISOM_BOX_TYPE_ILST: return ilst_dump(a, trace); + + case GF_ISOM_BOX_TYPE_OHDR: return ohdr_dump(a, trace); + case GF_ISOM_BOX_TYPE_GRPI: return grpi_dump(a, trace); + case GF_ISOM_BOX_TYPE_MDRI: return mdri_dump(a, trace); + case GF_ISOM_BOX_TYPE_ODTT: return odtt_dump(a, trace); + case GF_ISOM_BOX_TYPE_ODRB: return odrb_dump(a, trace); + case GF_ISOM_BOX_TYPE_ODKM: return odkm_dump(a, trace); + case GF_ISOM_BOX_TYPE_ODAF: return iSFM_dump(a, trace); + + case GF_ISOM_BOX_TYPE_TSEL: return tsel_dump(a, trace); + case GF_ISOM_BOX_TYPE_METX: return metx_dump(a, trace); + case GF_ISOM_BOX_TYPE_METT: return metx_dump(a, trace); + + case GF_ISOM_BOX_TYPE_DIMS: return dims_dump(a, trace); + case GF_ISOM_BOX_TYPE_DIMC: return dimC_dump(a, trace); + case GF_ISOM_BOX_TYPE_DIST: return diST_dump(a, trace); + + case GF_ISOM_BOX_TYPE_AC3: return ac3_dump(a, trace); + case GF_ISOM_BOX_TYPE_DAC3: return dac3_dump(a, trace); + + default: return defa_dump(a, trace); + } +} + + +GF_Err gf_box_array_dump(GF_List *list, FILE * trace) +{ + u32 i; + GF_Box *a; + if (!list) return GF_OK; + i=0; + while ((a = (GF_Box *)gf_list_enum(list, &i))) { + gf_box_dump(a, trace); + } + return GF_OK; +} + + + +GF_EXPORT +GF_Err gf_isom_dump(GF_ISOFile *mov, FILE * trace) +{ + GF_Err gf_box_dump(void *ptr, FILE * trace); + void BadTopBoxErr(GF_Box *a, FILE * trace); + u32 i; + GF_Box *box; + if (!mov || !trace) return GF_BAD_PARAM; + + fprintf(trace, "\n"); + fprintf(trace, "\n"); + + fprintf(trace, "\n", mov->fileName); + + i=0; + while ((box = (GF_Box *)gf_list_enum(mov->TopBoxes, &i))) { + switch (box->type) { + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_MOOV: + case GF_ISOM_BOX_TYPE_MDAT: + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_META: + case GF_ISOM_BOX_TYPE_SKIP: +#ifndef GPAC_ISOM_NO_FRAGMENTS + case GF_ISOM_BOX_TYPE_MOOF: +#endif + break; + + default: + BadTopBoxErr(box, trace); + break; + } + gf_box_dump(box, trace); + } + fprintf(trace, "\n"); + return GF_OK; +} + + + +GF_Err gf_full_box_dump(GF_Box *a, FILE * trace) +{ + GF_FullBox *p; + p = (GF_FullBox *)a; + fprintf(trace, "\n", p->version, p->flags); + return GF_OK; +} + + +GF_Err reftype_dump(GF_Box *a, FILE * trace) +{ + char *s; + u32 i; + GF_TrackReferenceTypeBox *p; + + p = (GF_TrackReferenceTypeBox *)a; + p->type = p->reference_type; + + switch (a->type) { + case GF_ISOM_BOX_TYPE_HINT: s = "Hint"; break; + case GF_ISOM_BOX_TYPE_DPND: s = "Stream"; break; + case GF_ISOM_BOX_TYPE_MPOD: s = "OD"; break; + case GF_ISOM_BOX_TYPE_SYNC: s = "Sync"; break; + case GF_ISOM_BOX_TYPE_CHAP: s = "Chapter"; break; + default: + s = (char *) gf_4cc_to_str(a->type); + break; + } + fprintf(trace, "<%sTrackReferenceBox Tracks=\"", s); + for (i=0; itrackIDCount; i++) fprintf(trace, " %d", p->trackIDs[i]); + fprintf(trace, "\">\n"); + DumpBox(a, trace); + fprintf(trace, "\n", s); + p->type = GF_ISOM_BOX_TYPE_REFT; + return GF_OK; +} + +GF_Err free_dump(GF_Box *a, FILE * trace) +{ + GF_FreeSpaceBox *p; + + p = (GF_FreeSpaceBox *)a; + fprintf(trace, "\n", p->dataSize); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err mdat_dump(GF_Box *a, FILE * trace) +{ + GF_MediaDataBox *p; + + p = (GF_MediaDataBox *)a; + fprintf(trace, "\n", LLD_CAST p->dataSize); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err moov_dump(GF_Box *a, FILE * trace) +{ + GF_MovieBox *p; + p = (GF_MovieBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + + if (p->iods) gf_box_dump(p->iods, trace); + if (p->meta) gf_box_dump(p->meta, trace); + gf_box_dump(p->mvhd, trace); + +#ifndef GPAC_ISOM_NO_FRAGMENTS + if (p->mvex) gf_box_dump(p->mvex, trace); +#endif + + gf_box_array_dump(p->trackList, trace); + if (p->udta) gf_box_dump(p->udta, trace); + gf_box_array_dump(p->boxes, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err mvhd_dump(GF_Box *a, FILE * trace) +{ + GF_MovieHeaderBox *p; + + p = (GF_MovieHeaderBox *) a; + + fprintf(trace, "creationTime); + fprintf(trace, "ModificationTime=\""LLD"\" ", LLD_CAST p->modificationTime); + fprintf(trace, "TimeScale=\"%d\" ", p->timeScale); + fprintf(trace, "Duration=\""LLD"\" ", LLD_CAST p->duration); + fprintf(trace, "NextTrackID=\"%d\">\n", p->nextTrackID); + + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + fprintf(trace, ""); + return GF_OK; +} + +GF_Err mdhd_dump(GF_Box *a, FILE * trace) +{ + GF_MediaHeaderBox *p; + + p = (GF_MediaHeaderBox *)a; + fprintf(trace, "creationTime); + fprintf(trace, "ModificationTime=\""LLD"\" ", LLD_CAST p->modificationTime); + fprintf(trace, "TimeScale=\"%d\" ", p->timeScale); + fprintf(trace, "Duration=\""LLD"\" ", LLD_CAST p->duration); + fprintf(trace, "LanguageCode=\"%c%c%c\">\n", p->packedLanguage[0], p->packedLanguage[1], p->packedLanguage[2]); + + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err vmhd_dump(GF_Box *a, FILE * trace) +{ + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err smhd_dump(GF_Box *a, FILE * trace) +{ + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err hmhd_dump(GF_Box *a, FILE * trace) +{ + GF_HintMediaHeaderBox *p; + + p = (GF_HintMediaHeaderBox *)a; + + fprintf(trace, "maxPDUSize); + fprintf(trace, "AveragePDUSize=\"%d\" ", p->avgPDUSize); + fprintf(trace, "MaxBitRate=\"%d\" ", p->maxBitrate); + fprintf(trace, "AverageBitRate=\"%d\">\n", p->avgBitrate); + + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err nmhd_dump(GF_Box *a, FILE * trace) +{ + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err stbl_dump(GF_Box *a, FILE * trace) +{ + GF_SampleTableBox *p; + p = (GF_SampleTableBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + + gf_box_dump(p->SampleDescription, trace); + gf_box_dump(p->TimeToSample, trace); + if (p->CompositionOffset) gf_box_dump(p->CompositionOffset, trace); + if (p->SyncSample) gf_box_dump(p->SyncSample, trace); + if (p->ShadowSync) gf_box_dump(p->ShadowSync, trace); + gf_box_dump(p->SampleToChunk, trace); + gf_box_dump(p->SampleSize, trace); + gf_box_dump(p->ChunkOffset, trace); + if (p->DegradationPriority) gf_box_dump(p->DegradationPriority, trace); + if (p->SampleDep) gf_box_dump(p->SampleDep, trace); + if (p->PaddingBits) gf_box_dump(p->PaddingBits, trace); + if (p->Fragments) gf_box_dump(p->Fragments, trace); + + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err dinf_dump(GF_Box *a, FILE * trace) +{ + GF_DataInformationBox *p; + p = (GF_DataInformationBox *)a; + fprintf(trace, ""); + DumpBox(a, trace); + gf_box_dump(p->dref, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err url_dump(GF_Box *a, FILE * trace) +{ + GF_DataEntryURLBox *p; + + p = (GF_DataEntryURLBox *)a; + fprintf(trace, "location) { + fprintf(trace, " URL=\"%s\">\n", p->location); + } else { + fprintf(trace, ">\n"); + if (! (p->flags & 1) ) { + fprintf(trace, "\n"); + } else { + fprintf(trace, "\n"); + } + } + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err urn_dump(GF_Box *a, FILE * trace) +{ + GF_DataEntryURNBox *p; + + p = (GF_DataEntryURNBox *)a; + fprintf(trace, "nameURN) fprintf(trace, " URN=\"%s\"", p->nameURN); + if (p->location) fprintf(trace, " URL=\"%s\"", p->location); + fprintf(trace, ">\n"); + + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err cprt_dump(GF_Box *a, FILE * trace) +{ + GF_CopyrightBox *p; + + p = (GF_CopyrightBox *)a; + fprintf(trace, "\n", p->packedLanguageCode, p->notice); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + + +static char *format_duration(u64 dur, u32 timescale, char *szDur) +{ + u32 h, m, s, ms; + dur = (u32) (( ((Double) (s64) dur)/timescale)*1000); + h = (u32) (dur / 3600000); + dur -= h*3600000; + m = (u32) (dur / 60000); + dur -= m*60000; + s = (u32) (dur/1000); + dur -= s*1000; + ms = (u32) (dur); + sprintf(szDur, "%02d:%02d:%02d.%03d", h, m, s, ms); + return szDur; +} + +GF_Err chpl_dump(GF_Box *a, FILE * trace) +{ + u32 i, count; + char szDur[20]; + GF_ChapterListBox *p = (GF_ChapterListBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + count = gf_list_count(p->list); + for (i=0; ilist, i); + fprintf(trace, "\n", ce->name, format_duration(ce->start_time, 1000*10000, szDur)); + } + + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err dpin_dump(GF_Box *a, FILE * trace) +{ + u32 i; + GF_ProgressiveDownloadBox *p = (GF_ProgressiveDownloadBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + for (i=0; icount; i++) { + fprintf(trace, "\n", p->rates[i], p->times[i]); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err hdlr_dump(GF_Box *a, FILE * trace) +{ + GF_HandlerBox *p = (GF_HandlerBox *)a; + if (p->nameUTF8 && (u32) p->nameUTF8[0] == strlen(p->nameUTF8+1)) { + fprintf(trace, "handlerType), p->nameUTF8+1); + } else { + fprintf(trace, "handlerType), p->nameUTF8); + } + fprintf(trace, "reserved1=\"%d\" reserved2=\"", p->reserved1); + DumpData(trace, p->reserved2, 12); + fprintf(trace, "\""); + + fprintf(trace, ">\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err iods_dump(GF_Box *a, FILE * trace) +{ + GF_ObjectDescriptorBox *p; + + p = (GF_ObjectDescriptorBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + if (p->descriptor) { + gf_odf_dump_desc(p->descriptor, trace, 1, 1); + } else { + fprintf(trace, "\n"); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err trak_dump(GF_Box *a, FILE * trace) +{ + GF_TrackBox *p; + + p = (GF_TrackBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + if (p->Header) { + gf_box_dump(p->Header, trace); + } else { + fprintf(trace, "\n"); + } + if (p->References) gf_box_dump(p->References, trace); + if (p->meta) gf_box_dump(p->meta, trace); + if (p->editBox) gf_box_dump(p->editBox, trace); + if (p->Media) gf_box_dump(p->Media, trace); + if (p->udta) gf_box_dump(p->udta, trace); + gf_box_array_dump(p->boxes, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err mp4s_dump(GF_Box *a, FILE * trace) +{ + GF_MPEGSampleEntryBox *p; + + p = (GF_MPEGSampleEntryBox *)a; + fprintf(trace, "\n", p->dataReferenceIndex); + DumpBox(a, trace); + if (p->esd) { + gf_box_dump(p->esd, trace); + } else { + fprintf(trace, "\n"); + } + if (a->type == GF_ISOM_BOX_TYPE_ENCS) { + gf_box_dump(p->protection_info, trace); + } + fprintf(trace, "\n"); + return GF_OK; +} + + +void base_visual_entry_dump(GF_VisualSampleEntryBox *p, FILE * trace) +{ + fprintf(trace, " DataReferenceIndex=\"%d\" Width=\"%d\" Height=\"%d\"", p->dataReferenceIndex, p->Width, p->Height); + + //dump reserved info + fprintf(trace, " XDPI=\"%d\" YDPI=\"%d\" BitDepth=\"%d\"", p->horiz_res, p->vert_res, p->bit_depth); + if (strlen((const char*)p->compressor_name) ) + fprintf(trace, " CompressorName=\"%s\"\n", p->compressor_name); + +} + +GF_Err mp4v_dump(GF_Box *a, FILE * trace) +{ + GF_MPEGVisualSampleEntryBox *p = (GF_MPEGVisualSampleEntryBox *)a; + const char *name = p->avc_config ? "AVCSampleEntryBox" : "MPEGVisualSampleDescriptionBox"; + + fprintf(trace, "<%s", name); + base_visual_entry_dump((GF_VisualSampleEntryBox *)p, trace); + fprintf(trace, ">\n"); + + if (p->esd) { + gf_box_dump(p->esd, trace); + } else if (p->avc_config) { + if (p->avc_config) gf_box_dump(p->avc_config, trace); + if (p->ipod_ext) gf_box_dump(p->ipod_ext, trace); + if (p->descr) gf_box_dump(p->descr, trace); + if (p->bitrate) gf_box_dump(p->bitrate, trace); + } else { + fprintf(trace, "\n"); + } + if (a->type == GF_ISOM_BOX_TYPE_ENCV) { + gf_box_dump(p->protection_info, trace); + } + if (p->pasp) gf_box_dump(p->pasp, trace); + + DumpBox(a, trace); + + fprintf(trace, "\n", name); + return GF_OK; +} + + +void base_audio_entry_dump(GF_AudioSampleEntryBox *p, FILE * trace) +{ + fprintf(trace, " DataReferenceIndex=\"%d\" SampleRate=\"%d\"", p->dataReferenceIndex, p->samplerate_hi); + fprintf(trace, " Channels=\"%d\" BitsPerSample=\"%d\"", p->channel_count, p->bitspersample); +} + +GF_Err mp4a_dump(GF_Box *a, FILE * trace) +{ + GF_MPEGAudioSampleEntryBox *p; + + p = (GF_MPEGAudioSampleEntryBox *)a; + fprintf(trace, "\n"); + + DumpBox(a, trace); + if (p->esd) { + gf_box_dump(p->esd, trace); + } else { + fprintf(trace, "\n"); + } + if (a->type == GF_ISOM_BOX_TYPE_ENCA) { + gf_box_dump(p->protection_info, trace); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err gnrm_dump(GF_Box *a, FILE * trace) +{ + GF_GenericSampleEntryBox *p = (GF_GenericSampleEntryBox *)a; + fprintf(trace, "\n", p->dataReferenceIndex, p->data_size); + a->type = p->EntryType; + DumpBox(a, trace); + a->type = GF_ISOM_BOX_TYPE_GNRM; + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err gnrv_dump(GF_Box *a, FILE * trace) +{ + GF_GenericVisualSampleEntryBox *p = (GF_GenericVisualSampleEntryBox *)a; + fprintf(trace, "\n", + p->dataReferenceIndex, p->version, p->revision, p->vendor, p->temporal_quality, p->spacial_quality, p->Width, p->Height, p->horiz_res, p->vert_res, p->compressor_name, p->bit_depth); + a->type = p->EntryType; + DumpBox(a, trace); + a->type = GF_ISOM_BOX_TYPE_GNRV; + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err gnra_dump(GF_Box *a, FILE * trace) +{ + GF_GenericAudioSampleEntryBox *p = (GF_GenericAudioSampleEntryBox *)a; + fprintf(trace, "\n", + p->dataReferenceIndex, p->version, p->revision, p->vendor, p->channel_count, p->bitspersample, p->samplerate_hi); + a->type = p->EntryType; + DumpBox(a, trace); + a->type = GF_ISOM_BOX_TYPE_GNRA; + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err edts_dump(GF_Box *a, FILE * trace) +{ + GF_EditBox *p; + + p = (GF_EditBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_box_dump(p->editList, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err udta_dump(GF_Box *a, FILE * trace) +{ + GF_UserDataBox *p; + GF_UserDataMap *map; + u32 i; + + p = (GF_UserDataBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + + i=0; + while ((map = (GF_UserDataMap *)gf_list_enum(p->recordList, &i))) { + fprintf(trace, "\n", gf_4cc_to_str(map->boxType)); + gf_box_array_dump(map->boxList, trace); + fprintf(trace, "\n"); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err dref_dump(GF_Box *a, FILE * trace) +{ + GF_DataReferenceBox *p; + + p = (GF_DataReferenceBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + gf_box_array_dump(p->boxList, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err stsd_dump(GF_Box *a, FILE * trace) +{ + GF_SampleDescriptionBox *p; + p = (GF_SampleDescriptionBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + gf_box_array_dump(p->boxList, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err stts_dump(GF_Box *a, FILE * trace) +{ + GF_TimeToSampleBox *p; + u32 i; + + p = (GF_TimeToSampleBox *)a; + fprintf(trace, "\n", p->nb_entries); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + for (i=0; inb_entries; i++) { + fprintf(trace, "\n", p->entries[i].sampleDelta, p->entries[i].sampleCount); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err ctts_dump(GF_Box *a, FILE * trace) +{ + GF_CompositionOffsetBox *p; + u32 i; + p = (GF_CompositionOffsetBox *)a; + fprintf(trace, "\n", p->nb_entries); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + for (i=0; inb_entries;i++) { + fprintf(trace, "\n", p->entries[i].decodingOffset, p->entries[i].sampleCount); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err stsh_dump(GF_Box *a, FILE * trace) +{ + GF_ShadowSyncBox *p; + u32 i; + GF_StshEntry *t; + + p = (GF_ShadowSyncBox *)a; + fprintf(trace, "\n", gf_list_count(p->entries)); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + i=0; + while ((t = (GF_StshEntry *)gf_list_enum(p->entries, &i))) { + fprintf(trace, "\n", t->shadowedSampleNumber, t->syncSampleNumber); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err elst_dump(GF_Box *a, FILE * trace) +{ + GF_EditListBox *p; + u32 i; + GF_EdtsEntry *t; + + p = (GF_EditListBox *)a; + fprintf(trace, "\n", gf_list_count(p->entryList)); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + i=0; + while ((t = (GF_EdtsEntry *)gf_list_enum(p->entryList, &i))) { + fprintf(trace, "\n", LLD_CAST t->segmentDuration, LLD_CAST t->mediaTime, t->mediaRate); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err stsc_dump(GF_Box *a, FILE * trace) +{ + GF_SampleToChunkBox *p; + u32 i; + + p = (GF_SampleToChunkBox *)a; + fprintf(trace, "\n", p->nb_entries); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + for (i=0; inb_entries; i++) { + fprintf(trace, "\n", p->entries[i].firstChunk, p->entries[i].samplesPerChunk, p->entries[i].sampleDescriptionIndex); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err stsz_dump(GF_Box *a, FILE * trace) +{ + GF_SampleSizeBox *p; + u32 i; + p = (GF_SampleSizeBox *)a; + + fprintf(trace, "<%sBox SampleCount=\"%d\"", (a->type == GF_ISOM_BOX_TYPE_STSZ) ? "SampleSize" : "CompactSampleSize", p->sampleCount); + if (a->type == GF_ISOM_BOX_TYPE_STSZ) { + if (p->sampleSize) { + fprintf(trace, " ConstantSampleSize=\"%d\"", p->sampleSize); + } + } else { + fprintf(trace, " SampleSizeBits=\"%d\"", p->sampleSize); + } + fprintf(trace, ">\n"); + + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + if ((a->type != GF_ISOM_BOX_TYPE_STSZ) || !p->sampleSize) { + if (!p->sizes) { + fprintf(trace, "\n"); + } else { + for (i=0; isampleCount; i++) { + fprintf(trace, "\n", p->sizes[i]); + } + } + } + fprintf(trace, "\n", (a->type == GF_ISOM_BOX_TYPE_STSZ) ? "SampleSize" : "CompactSampleSize"); + return GF_OK; +} + +GF_Err stco_dump(GF_Box *a, FILE * trace) +{ + GF_ChunkOffsetBox *p; + u32 i; + + p = (GF_ChunkOffsetBox *)a; + fprintf(trace, "\n", p->nb_entries); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + if (!p->offsets) { + fprintf(trace, "\n"); + } else { + for (i=0; inb_entries; i++) { + fprintf(trace, "\n", p->offsets[i]); + } + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err stss_dump(GF_Box *a, FILE * trace) +{ + GF_SyncSampleBox *p; + u32 i; + + p = (GF_SyncSampleBox *)a; + fprintf(trace, "\n", p->nb_entries); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + if (!p->sampleNumbers) { + fprintf(trace, "\n"); + } else { + for (i=0; inb_entries; i++) { + fprintf(trace, "\n", p->sampleNumbers[i]); + } + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err stdp_dump(GF_Box *a, FILE * trace) +{ + GF_DegradationPriorityBox *p; + u32 i; + + p = (GF_DegradationPriorityBox *)a; + fprintf(trace, "\n", p->nb_entries); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + if (!p->priorities) { + fprintf(trace, "\n"); + } else { + for (i=0; inb_entries; i++) { + fprintf(trace, "\n", p->priorities[i]); + } + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err sdtp_dump(GF_Box *a, FILE * trace) +{ + GF_SampleDependencyTypeBox *p; + u32 i; + + p = (GF_SampleDependencyTypeBox*)a; + fprintf(trace, "\n", p->sampleCount); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + if (!p->sample_info) { + fprintf(trace, "\n"); + } else { + for (i=0; isampleCount; i++) { + u8 flag = p->sample_info[i]; + fprintf(trace, "> 4) & 3) { + case 0: fprintf(trace, "dependsOnOther=\"unknown\" "); break; + case 1: fprintf(trace, "dependsOnOther=\"yes\" "); break; + case 2: fprintf(trace, "dependsOnOther=\"no\" "); break; + case 3: fprintf(trace, "dependsOnOther=\"!! RESERVED !!\" "); break; + } + switch ( (flag >> 2) & 3) { + case 0: fprintf(trace, "dependedOn=\"unknown\" "); break; + case 1: fprintf(trace, "dependedOn=\"yes\" "); break; + case 2: fprintf(trace, "dependedOn=\"no\" "); break; + case 3: fprintf(trace, "dependedOn=\"!! RESERVED !!\" "); break; + } + switch ( flag & 3) { + case 0: fprintf(trace, "hasRedundancy=\"unknown\" "); break; + case 1: fprintf(trace, "hasRedundancy=\"yes\" "); break; + case 2: fprintf(trace, "hasRedundancy=\"no\" "); break; + case 3: fprintf(trace, "hasRedundancy=\"!! RESERVED !!\" "); break; + } + fprintf(trace, " />\n"); + } + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err co64_dump(GF_Box *a, FILE * trace) +{ + GF_ChunkLargeOffsetBox *p; + u32 i; + + p = (GF_ChunkLargeOffsetBox *)a; + fprintf(trace, "\n", p->nb_entries); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + if (!p->offsets) { + fprintf(trace, "\n"); + } else { + for (i=0; inb_entries; i++) + fprintf(trace, "\n", LLD_CAST p->offsets[i]); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err esds_dump(GF_Box *a, FILE * trace) +{ + GF_ESDBox *p; + + p = (GF_ESDBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + if (p->desc) { + gf_odf_dump_desc(p->desc, trace, 1, 1); + } else { + fprintf(trace, "\n"); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err minf_dump(GF_Box *a, FILE * trace) +{ + GF_MediaInformationBox *p; + + p = (GF_MediaInformationBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + + gf_box_dump(p->InfoHeader, trace); + gf_box_dump(p->dataInformation, trace); + gf_box_dump(p->sampleTable, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err tkhd_dump(GF_Box *a, FILE * trace) +{ + GF_TrackHeaderBox *p; + p = (GF_TrackHeaderBox *)a; + fprintf(trace, "creationTime, LLD_CAST p->modificationTime, p->trackID, LLD_CAST p->duration); + + if (p->alternate_group) fprintf(trace, " AlternateGroupID=\"%d\"", p->alternate_group); + if (p->volume) { + fprintf(trace, " Volume=\"%.2f\"", (Float)p->volume / 256); + } else if (p->width || p->height) { + fprintf(trace, " Width=\"%.2f\" Height=\"%.2f\"", (Float)p->width / 65536, (Float)p->height / 65536); + if (p->layer) fprintf(trace, " Layer=\"%d\"", p->layer); + if (p->alternate_group) fprintf(trace, " AlternateGroup=\"%d\"", p->alternate_group); + } + fprintf(trace, ">\n"); + if (p->width || p->height) { + fprintf(trace, "", + p->matrix[0], p->matrix[1], p->matrix[2], + p->matrix[3], p->matrix[4], p->matrix[5], + p->matrix[6], p->matrix[7], p->matrix[8]); + } + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err tref_dump(GF_Box *a, FILE * trace) +{ + GF_TrackReferenceBox *p; + + p = (GF_TrackReferenceBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_box_array_dump(p->boxList, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err mdia_dump(GF_Box *a, FILE * trace) +{ + GF_MediaBox *p; + + p = (GF_MediaBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_box_dump(p->mediaHeader, trace); + gf_box_dump(p->handler, trace); + gf_box_dump(p->information, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err defa_dump(GF_Box *a, FILE * trace) +{ + fprintf(trace, "\n"); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err void_dump(GF_Box *a, FILE * trace) +{ + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err ftyp_dump(GF_Box *a, FILE * trace) +{ + GF_FileTypeBox *p; + u32 i; + + p = (GF_FileTypeBox *)a; + fprintf(trace, "\n", gf_4cc_to_str(p->majorBrand), p->minorVersion); + DumpBox(a, trace); + + for (i=0; ialtCount; i++) { + fprintf(trace, "\n", gf_4cc_to_str(p->altBrand[i])); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err padb_dump(GF_Box *a, FILE * trace) +{ + GF_PaddingBitsBox *p; + u32 i; + + p = (GF_PaddingBitsBox *)a; + fprintf(trace, "\n", p->SampleCount); + DumpBox(a, trace); + for (i=0; iSampleCount; i+=1) { + fprintf(trace, "\n", p->padbits[i]); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err stsf_dump(GF_Box *a, FILE * trace) +{ + GF_SampleFragmentBox *p; + GF_StsfEntry *ent; + u32 i, j, count; + + + p = (GF_SampleFragmentBox *)a; + count = gf_list_count(p->entryList); + fprintf(trace, "\n", count); + DumpBox(a, trace); + + for (i=0; ientryList, i); + fprintf(trace, "\n", ent->SampleNumber, ent->fragmentCount); + for (j=0;jfragmentCount;j++) fprintf(trace, "\n", ent->fragmentSizes[j]); + fprintf(trace, "\n"); + } + + fprintf(trace, "\n"); + return GF_OK; +} + + +GF_Err gppa_dump(GF_Box *a, FILE * trace) +{ + char *szName; + GF_3GPPAudioSampleEntryBox *p = (GF_3GPPAudioSampleEntryBox *)a; + switch (p->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: szName = "AMRSampleDescription"; break; + case GF_ISOM_SUBTYPE_3GP_AMR_WB: szName = "AMR_WB_SampleDescription"; break; + case GF_ISOM_SUBTYPE_3GP_EVRC: szName = "EVRCSampleDescription"; break; + case GF_ISOM_SUBTYPE_3GP_QCELP: szName = "QCELPSampleDescription"; break; + case GF_ISOM_SUBTYPE_3GP_SMV: szName = "SMVSampleDescription"; break; + default: szName = "3GPAudioSampleDescription"; break; + } + fprintf(trace, "<%sBox", szName); + base_audio_entry_dump((GF_AudioSampleEntryBox *)p, trace); + fprintf(trace, ">\n"); + DumpBox(a, trace); + + if (p->info) { + gf_box_dump(p->info, trace); + } else { + fprintf(trace, "\n"); + } + fprintf(trace, "\n", szName); + return GF_OK; +} + +GF_Err gppv_dump(GF_Box *a, FILE * trace) +{ + char *szName; + GF_3GPPVisualSampleEntryBox *p = (GF_3GPPVisualSampleEntryBox *)a; + + switch (p->type) { + case GF_ISOM_SUBTYPE_3GP_H263: szName = "H263SampleDescription"; break; + default: szName = "3GPVisualSampleDescription"; break; + } + fprintf(trace, "<%sBox", szName); + base_visual_entry_dump((GF_VisualSampleEntryBox *)p, trace); + fprintf(trace, ">\n"); + DumpBox(a, trace); + if (p->info) { + gf_box_dump(p->info, trace); + } else { + fprintf(trace, "\n"); + } + fprintf(trace, "\n", szName); + return GF_OK; +} + +GF_Err gppc_dump(GF_Box *a, FILE * trace) +{ + GF_3GPPConfigBox *p = (GF_3GPPConfigBox *)a; + const char *name = gf_4cc_to_str(p->cfg.vendor); + switch (p->cfg.type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + fprintf(trace, "cfg.decoder_version); + fprintf(trace, " FramesPerSample=\"%d\" SupportedModes=\"%x\" ModeRotating=\"%d\"", p->cfg.frames_per_sample, p->cfg.AMR_mode_set, p->cfg.AMR_mode_change_period); + fprintf(trace, ">\n"); + DumpBox(a, trace); + fprintf(trace, "\n"); + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + fprintf(trace, "\n", name, p->cfg.decoder_version, p->cfg.frames_per_sample); + DumpBox(a, trace); + fprintf(trace, "\n"); + break; + case GF_ISOM_SUBTYPE_3GP_QCELP: + fprintf(trace, "\n", name, p->cfg.decoder_version, p->cfg.frames_per_sample); + DumpBox(a, trace); + fprintf(trace, "\n"); + break; + case GF_ISOM_SUBTYPE_3GP_SMV: + fprintf(trace, "\n", name, p->cfg.decoder_version, p->cfg.frames_per_sample); + DumpBox(a, trace); + fprintf(trace, "\n"); + break; + case GF_ISOM_SUBTYPE_3GP_H263: + fprintf(trace, "cfg.decoder_version); + fprintf(trace, " Profile=\"%d\" Level=\"%d\"", p->cfg.H263_profile, p->cfg.H263_level); + fprintf(trace, ">\n"); + DumpBox(a, trace); + fprintf(trace, "\n"); + break; + default: + break; + } + return GF_OK; +} + + +GF_Err avcc_dump(GF_Box *a, FILE * trace) +{ + u32 i, count; + GF_AVCConfigurationBox *p = (GF_AVCConfigurationBox *) a; + fprintf(trace, "\n"); + + fprintf(trace, "\n", + p->config->configurationVersion, p->config->AVCProfileIndication, p->config->profile_compatibility, p->config->AVCLevelIndication, p->config->nal_unit_size); + + count = gf_list_count(p->config->sequenceParameterSets); + for (i=0; iconfig->sequenceParameterSets, i); + fprintf(trace, "size); + DumpData(trace, c->data, c->size); + fprintf(trace, "\"/>\n"); + } + count = gf_list_count(p->config->pictureParameterSets); + for (i=0; iconfig->pictureParameterSets, i); + fprintf(trace, "size); + DumpData(trace, c->data, c->size); + fprintf(trace, "\"/>\n"); + } + fprintf(trace, "\n"); + + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err m4ds_dump(GF_Box *a, FILE * trace) +{ + u32 i; + GF_Descriptor *desc; + GF_MPEG4ExtensionDescriptorsBox *p = (GF_MPEG4ExtensionDescriptorsBox *) a; + fprintf(trace, "\n"); + + i=0; + while ((desc = (GF_Descriptor *)gf_list_enum(p->descriptors, &i))) { + gf_odf_dump_desc(desc, trace, 1, 1); + } + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err btrt_dump(GF_Box *a, FILE * trace) +{ + GF_MPEG4BitRateBox *p = (GF_MPEG4BitRateBox*)a; + fprintf(trace, "\n", p->bufferSizeDB, p->avgBitrate, p->maxBitrate); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err ftab_dump(GF_Box *a, FILE * trace) +{ + u32 i; + GF_FontTableBox *p = (GF_FontTableBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + for (i=0; ientry_count; i++) { + fprintf(trace, "\n", p->fonts[i].fontID, p->fonts[i].fontName ? p->fonts[i].fontName : "NULL"); + } + fprintf(trace, "\n"); + return GF_OK; +} + +static void gpp_dump_rgba(FILE * trace, char *name, u32 col) +{ + fprintf(trace, "%s=\"%x %x %x %x\"", name, (col>>16)&0xFF, (col>>8)&0xFF, (col)&0xFF, (col>>24)&0xFF); +} +static void gpp_dump_box(FILE * trace, GF_BoxRecord *rec) +{ + fprintf(trace, "\n", rec->top, rec->left, rec->bottom, rec->right); +} +static void gpp_dump_style(FILE * trace, GF_StyleRecord *rec) +{ + fprintf(trace, "startCharOffset, rec->endCharOffset, rec->fontID); + if (!rec->style_flags) { + fprintf(trace, "Normal"); + } else { + if (rec->style_flags & 1) fprintf(trace, "Bold "); + if (rec->style_flags & 2) fprintf(trace, "Italic "); + if (rec->style_flags & 4) fprintf(trace, "Underlined "); + } + fprintf(trace, "\" fontSize=\"%d\" ", rec->font_size); + gpp_dump_rgba(trace, "text-color", rec->text_color); + fprintf(trace, "/>\n"); +} + +GF_Err tx3g_dump(GF_Box *a, FILE * trace) +{ + GF_TextSampleEntryBox *p = (GF_TextSampleEntryBox *)a; + fprintf(trace, "dataReferenceIndex, p->displayFlags, p->horizontal_justification, p->vertical_justification); + + gpp_dump_rgba(trace, "background-color", p->back_color); + fprintf(trace, ">\n"); + DumpBox(a, trace); + + fprintf(trace, "\n"); + gpp_dump_box(trace, &p->default_box); + fprintf(trace, "\n"); + fprintf(trace, "\n"); + gpp_dump_style(trace, &p->default_style); + fprintf(trace, "\n"); + gf_box_dump(p->font_table, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err styl_dump(GF_Box *a, FILE * trace) +{ + u32 i; + GF_TextStyleBox*p = (GF_TextStyleBox*)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + for (i=0; ientry_count; i++) gpp_dump_style(trace, &p->styles[i]); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err hlit_dump(GF_Box *a, FILE * trace) +{ + GF_TextHighlightBox*p = (GF_TextHighlightBox*)a; + fprintf(trace, "\n", p->startcharoffset, p->endcharoffset); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err hclr_dump(GF_Box *a, FILE * trace) +{ + GF_TextHighlightColorBox*p = (GF_TextHighlightColorBox*)a; + fprintf(trace, "hil_color); + fprintf(trace, ">\n"); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err krok_dump(GF_Box *a, FILE * trace) +{ + u32 i; + GF_TextKaraokeBox*p = (GF_TextKaraokeBox*)a; + fprintf(trace, "\n", p->highlight_starttime); + DumpBox(a, trace); + for (i=0; inb_entries; i++) { + fprintf(trace, "\n", p->records[i].highlight_endtime, p->records[i].start_charoffset, p->records[i].end_charoffset); + } + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err dlay_dump(GF_Box *a, FILE * trace) +{ + GF_TextScrollDelayBox*p = (GF_TextScrollDelayBox*)a; + fprintf(trace, "\n", p->scroll_delay); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err href_dump(GF_Box *a, FILE * trace) +{ + GF_TextHyperTextBox*p = (GF_TextHyperTextBox*)a; + fprintf(trace, "\n", p->startcharoffset, p->endcharoffset, p->URL ? p->URL : "NULL", p->URL_hint ? p->URL_hint : "NULL"); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err tbox_dump(GF_Box *a, FILE * trace) +{ + GF_TextBoxBox*p = (GF_TextBoxBox*)a; + fprintf(trace, "\n"); + gpp_dump_box(trace, &p->box); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err blnk_dump(GF_Box *a, FILE * trace) +{ + GF_TextBlinkBox*p = (GF_TextBlinkBox*)a; + fprintf(trace, "\n", p->startcharoffset, p->endcharoffset); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err twrp_dump(GF_Box *a, FILE * trace) +{ + GF_TextWrapBox*p = (GF_TextWrapBox*)a; + fprintf(trace, "\n", p->wrap_flag ? ( (p->wrap_flag>1) ? "Reserved" : "Automatic" ) : "No Wrap"); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + + +GF_Err meta_dump(GF_Box *a, FILE * trace) +{ + GF_MetaBox *p; + p = (GF_MetaBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + if (p->handler) gf_box_dump(p->handler, trace); + if (p->primary_resource) gf_box_dump(p->primary_resource, trace); + if (p->file_locations) gf_box_dump(p->file_locations, trace); + if (p->item_locations) gf_box_dump(p->item_locations, trace); + if (p->protections) gf_box_dump(p->protections, trace); + if (p->item_infos) gf_box_dump(p->item_infos, trace); + if (p->IPMP_control) gf_box_dump(p->IPMP_control, trace); + + gf_box_array_dump(p->other_boxes, trace); + fprintf(trace, "\n"); + return GF_OK; +} + + +GF_Err xml_dump(GF_Box *a, FILE * trace) +{ + GF_XMLBox *p = (GF_XMLBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + fwrite(p->xml, p->xml_length, 1, trace); + fprintf(trace, "\n"); + fprintf(trace, "\n"); + return GF_OK; +} + + +GF_Err bxml_dump(GF_Box *a, FILE * trace) +{ + GF_BinaryXMLBox *p = (GF_BinaryXMLBox *)a; + fprintf(trace, "\n", p->data_length); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + + +GF_Err pitm_dump(GF_Box *a, FILE * trace) +{ + GF_PrimaryItemBox *p = (GF_PrimaryItemBox *)a; + fprintf(trace, "\n", p->item_ID); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err ipro_dump(GF_Box *a, FILE * trace) +{ + GF_ItemProtectionBox *p = (GF_ItemProtectionBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + gf_box_array_dump(p->protection_information, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err infe_dump(GF_Box *a, FILE * trace) +{ + GF_ItemInfoEntryBox *p = (GF_ItemInfoEntryBox *)a; + fprintf(trace, "\n", p->item_ID, p->item_protection_index, p->item_name, p->content_type, p->content_encoding); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err iinf_dump(GF_Box *a, FILE * trace) +{ + GF_ItemInfoBox *p = (GF_ItemInfoBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + gf_box_array_dump(p->item_infos, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err iloc_dump(GF_Box *a, FILE * trace) +{ + u32 i, j, count, count2; + GF_ItemLocationBox *p = (GF_ItemLocationBox*)a; + fprintf(trace, "\n", p->offset_size, p->length_size, p->base_offset_size); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + count = gf_list_count(p->location_entries); + for (i=0;ilocation_entries, i); + count2 = gf_list_count(ie->extent_entries); + fprintf(trace, "\n", ie->item_ID, ie->data_reference_index, LLD_CAST ie->base_offset); + for (j=0; jextent_entries, j); + fprintf(trace, "\n", LLD_CAST iee->extent_offset, LLD_CAST iee->extent_length); + } + } + fprintf(trace, "\n"); + return GF_OK; +} + + +GF_Err hinf_dump(GF_Box *a, FILE * trace) +{ + GF_HintInfoBox *p; + + p = (GF_HintInfoBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_box_array_dump(p->boxList, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err trpy_dump(GF_Box *a, FILE * trace) +{ + GF_TRPYBox *p; + + p = (GF_TRPYBox *)a; + fprintf(trace, "\n", LLD_CAST p->nbBytes); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err totl_dump(GF_Box *a, FILE * trace) +{ + GF_TOTLBox *p; + + p = (GF_TOTLBox *)a; + fprintf(trace, "\n", p->nbBytes); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err nump_dump(GF_Box *a, FILE * trace) +{ + GF_NUMPBox *p; + + p = (GF_NUMPBox *)a; + fprintf(trace, "\n", LLD_CAST p->nbPackets); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err npck_dump(GF_Box *a, FILE * trace) +{ + GF_NPCKBox *p; + + p = (GF_NPCKBox *)a; + fprintf(trace, "\n", p->nbPackets); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err tpyl_dump(GF_Box *a, FILE * trace) +{ + GF_NTYLBox *p; + + p = (GF_NTYLBox *)a; + fprintf(trace, "\n", LLD_CAST p->nbBytes); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err tpay_dump(GF_Box *a, FILE * trace) +{ + GF_TPAYBox *p; + + p = (GF_TPAYBox *)a; + fprintf(trace, "\n", p->nbBytes); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err maxr_dump(GF_Box *a, FILE * trace) +{ + GF_MAXRBox *p; + p = (GF_MAXRBox *)a; + fprintf(trace, "\n", p->maxDataRate, p->granularity); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err dmed_dump(GF_Box *a, FILE * trace) +{ + GF_DMEDBox *p; + + p = (GF_DMEDBox *)a; + fprintf(trace, "\n", LLD_CAST p->nbBytes); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err dimm_dump(GF_Box *a, FILE * trace) +{ + GF_DIMMBox *p; + + p = (GF_DIMMBox *)a; + fprintf(trace, "\n", LLD_CAST p->nbBytes); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err drep_dump(GF_Box *a, FILE * trace) +{ + GF_DREPBox *p; + + p = (GF_DREPBox *)a; + fprintf(trace, "\n", LLD_CAST p->nbBytes); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err tmin_dump(GF_Box *a, FILE * trace) +{ + GF_TMINBox *p; + + p = (GF_TMINBox *)a; + fprintf(trace, "\n", p->minTime); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err tmax_dump(GF_Box *a, FILE * trace) +{ + GF_TMAXBox *p; + + p = (GF_TMAXBox *)a; + fprintf(trace, "\n", p->maxTime); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err pmax_dump(GF_Box *a, FILE * trace) +{ + GF_PMAXBox *p; + + p = (GF_PMAXBox *)a; + fprintf(trace, "\n", p->maxSize); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err dmax_dump(GF_Box *a, FILE * trace) +{ + GF_DMAXBox *p; + + p = (GF_DMAXBox *)a; + fprintf(trace, "\n", p->maxDur); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err payt_dump(GF_Box *a, FILE * trace) +{ + GF_PAYTBox *p; + + p = (GF_PAYTBox *)a; + fprintf(trace, "\n", p->payloadCode, p->payloadString); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err name_dump(GF_Box *a, FILE * trace) +{ + GF_NameBox *p; + + p = (GF_NameBox *)a; + fprintf(trace, "\n", p->string); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err rely_dump(GF_Box *a, FILE * trace) +{ + GF_RelyHintBox *p; + + p = (GF_RelyHintBox *)a; + fprintf(trace, "\n", p->prefered, p->required); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err snro_dump(GF_Box *a, FILE * trace) +{ + GF_SeqOffHintEntryBox *p; + + p = (GF_SeqOffHintEntryBox *)a; + fprintf(trace, "\n", p->SeqOffset); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err tims_dump(GF_Box *a, FILE * trace) +{ + GF_TSHintEntryBox *p; + + p = (GF_TSHintEntryBox *)a; + fprintf(trace, "\n", p->timeScale); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err tsro_dump(GF_Box *a, FILE * trace) +{ + GF_TimeOffHintEntryBox *p; + + p = (GF_TimeOffHintEntryBox *)a; + fprintf(trace, "\n", p->TimeOffset); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + + +GF_Err ghnt_dump(GF_Box *a, FILE * trace) +{ + GF_HintSampleEntryBox *p; + + p = (GF_HintSampleEntryBox *)a; + fprintf(trace, "\n", + gf_4cc_to_str(p->type), p->dataReferenceIndex, p->HintTrackVersion, p->LastCompatibleVersion, p->MaxPacketSize); + + DumpBox(a, trace); + gf_box_array_dump(p->HintDataTable, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err hnti_dump(GF_Box *a, FILE * trace) +{ + GF_HintTrackInfoBox *p; + GF_Box *ptr; + GF_RTPBox *rtp; + u32 i; + + p = (GF_HintTrackInfoBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + + i=0; + while ((ptr = (GF_Box *)gf_list_enum(p->boxList, &i))) { + if (ptr->type !=GF_ISOM_BOX_TYPE_RTP) { + gf_box_dump(ptr, trace); + } else { + rtp = (GF_RTPBox *)ptr; + fprintf(trace, "\n", gf_4cc_to_str(rtp->subType)); + fprintf(trace, "\n", rtp->sdpText); + fprintf(trace, "\n"); + } + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err sdp_dump(GF_Box *a, FILE * trace) +{ + GF_SDPBox *p; + + p = (GF_SDPBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + fprintf(trace, "\n", p->sdpText); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err rtpo_dump(GF_Box *a, FILE * trace) +{ + GF_RTPOBox *p; + + p = (GF_RTPOBox *)a; + fprintf(trace, "\n", p->timeOffset); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +GF_Err mvex_dump(GF_Box *a, FILE * trace) +{ + GF_MovieExtendsBox *p; + + p = (GF_MovieExtendsBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + if (p->mehd) gf_box_dump(p->mehd, trace); + gf_box_array_dump(p->TrackExList, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err mehd_dump(GF_Box *a, FILE * trace) +{ + GF_MovieExtendsHeaderBox *p = (GF_MovieExtendsHeaderBox*)a; + fprintf(trace, "\n", LLD_CAST p->fragment_duration); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err trex_dump(GF_Box *a, FILE * trace) +{ + GF_TrackExtendsBox *p; + + p = (GF_TrackExtendsBox *)a; + fprintf(trace, "trackID); + + fprintf(trace, " SampleDescriptionIndex=\"%d\" SampleDuration=\"%d\" SampleSize=\"%d\"", p->def_sample_desc_index, p->def_sample_duration, p->def_sample_size); + fprintf(trace, " SamplePadding=\"%d\" SampleSync=\"%d\" SampleDegradationPriority=\"%d\"", + GF_ISOM_GET_FRAG_PAD(p->def_sample_flags), GF_ISOM_GET_FRAG_SYNC(p->def_sample_flags), GF_ISOM_GET_FRAG_DEG(p->def_sample_flags)); + + fprintf(trace, ">\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err moof_dump(GF_Box *a, FILE * trace) +{ + GF_MovieFragmentBox *p; + + p = (GF_MovieFragmentBox *)a; + fprintf(trace, "\n", gf_list_count(p->TrackList)); + DumpBox(a, trace); + + if (p->mfhd) gf_box_dump(p->mfhd, trace); + gf_box_array_dump(p->TrackList, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err mfhd_dump(GF_Box *a, FILE * trace) +{ + GF_MovieFragmentHeaderBox *p; + + p = (GF_MovieFragmentHeaderBox *)a; + fprintf(trace, "\n", p->sequence_number); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err traf_dump(GF_Box *a, FILE * trace) +{ + GF_TrackFragmentBox *p; + + p = (GF_TrackFragmentBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + if (p->tfhd) gf_box_dump(p->tfhd, trace); + gf_box_array_dump(p->TrackRuns, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err tfhd_dump(GF_Box *a, FILE * trace) +{ + GF_TrackFragmentHeaderBox *p; + + p = (GF_TrackFragmentHeaderBox *)a; + fprintf(trace, "trackID); + + if (p->flags & GF_ISOM_TRAF_BASE_OFFSET) { + fprintf(trace, " BaseDataOffset=\""LLD"\"", LLD_CAST p->base_data_offset); + } + if (p->flags & GF_ISOM_TRAF_SAMPLE_DESC) + fprintf(trace, "SampleDescriptionIndex=\"%d\"", p->sample_desc_index); + if (p->flags & GF_ISOM_TRAF_SAMPLE_DUR) + fprintf(trace, " SampleDuration=\"%d\"", p->def_sample_duration); + if (p->flags & GF_ISOM_TRAF_SAMPLE_SIZE) + fprintf(trace, " SampleSize=\"%d\"", p->def_sample_size); + if (p->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) { + fprintf(trace, " SamplePadding=\"%d\"", GF_ISOM_GET_FRAG_PAD(p->def_sample_flags)); + fprintf(trace, " SampleSync=\"%d\"", GF_ISOM_GET_FRAG_SYNC(p->def_sample_flags)); + fprintf(trace, " SampleDegradationPriority=\"%d\"", GF_ISOM_GET_FRAG_DEG(p->def_sample_flags)); + } + fprintf(trace, ">\n"); + + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err trun_dump(GF_Box *a, FILE * trace) +{ + u32 i; + GF_TrunEntry *ent; + GF_TrackFragmentRunBox *p; + + p = (GF_TrackFragmentRunBox *)a; + fprintf(trace, "sample_count); + + if (p->flags & GF_ISOM_TRUN_DATA_OFFSET) + fprintf(trace, " DataOffset=\"%d\"", p->data_offset); + if (p->flags & GF_ISOM_TRUN_FIRST_FLAG) { + fprintf(trace, " FirstSamplePadding=\"%d\" FirstSampleSync=\"%d\" FirstSampleDegradationPriority=\"%d\"", GF_ISOM_GET_FRAG_PAD(p->first_sample_flags), GF_ISOM_GET_FRAG_SYNC(p->first_sample_flags), GF_ISOM_GET_FRAG_DEG(p->first_sample_flags)); + } + fprintf(trace, ">\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + if (p->flags & (GF_ISOM_TRUN_DURATION|GF_ISOM_TRUN_SIZE|GF_ISOM_TRUN_CTS_OFFSET|GF_ISOM_TRUN_FLAGS)) { + i=0; + while ((ent = (GF_TrunEntry *)gf_list_enum(p->entries, &i))) { + + fprintf(trace, "flags & GF_ISOM_TRUN_DURATION) + fprintf(trace, " Duration=\"%d\"", ent->Duration); + if (p->flags & GF_ISOM_TRUN_SIZE) + fprintf(trace, " Size=\"%d\"", ent->size); + if (p->flags & GF_ISOM_TRUN_CTS_OFFSET) + fprintf(trace, " CTSOffset=\"%d\"", ent->CTS_Offset); + + if (p->flags & GF_ISOM_TRUN_FLAGS) { + fprintf(trace, " SamplePadding=\"%d\" Sync=\"%d\" DegradationPriority=\"%d\"", + GF_ISOM_GET_FRAG_PAD(ent->flags), GF_ISOM_GET_FRAG_SYNC(ent->flags), GF_ISOM_GET_FRAG_DEG(ent->flags)); + } + fprintf(trace, "/>\n"); + } + } else { + fprintf(trace, "\n"); + } + fprintf(trace, "\n"); + return GF_OK; +} + + +#endif + +GF_Err DTE_Dump(GF_List *dte, FILE * trace) +{ + GF_GenericDTE *p; + GF_ImmediateDTE *i_p; + GF_SampleDTE *s_p; + GF_StreamDescDTE *sd_p; + u32 i, count; + + count = gf_list_count(dte); + for (i=0; isource) { + case 0: + fprintf(trace, "\n"); + break; + case 1: + i_p = (GF_ImmediateDTE *) p; + fprintf(trace, "\n", i_p->dataLength); + break; + case 2: + s_p = (GF_SampleDTE *) p; + fprintf(trace, "\n", + s_p->dataLength, s_p->byteOffset, s_p->sampleNumber, s_p->trackRefIndex); + break; + case 3: + sd_p = (GF_StreamDescDTE *) p; + fprintf(trace, "\n", + sd_p->dataLength, sd_p->byteOffset, sd_p->streamDescIndex, sd_p->trackRefIndex); + break; + default: + fprintf(trace, "\n"); + break; + } + } + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_isom_dump_hint_sample(GF_ISOFile *the_file, u32 trackNumber, u32 SampleNum, FILE * trace) +{ + GF_ISOSample *tmp; + GF_HintSampleEntryBox *entry; + u32 descIndex, count, count2, i; + GF_Err e; + GF_BitStream *bs; + GF_HintSample *s; + GF_TrackBox *trak; + GF_RTPPacket *pck; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM; + + tmp = gf_isom_get_sample(the_file, trackNumber, SampleNum, &descIndex); + if (!tmp) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, descIndex, (GF_SampleEntryBox **) &entry, &count); + if (e) { + gf_isom_sample_del(&tmp); + return e; + } + + //check we can read the sample + switch (entry->type) { + case GF_ISOM_BOX_TYPE_RTP_STSD: + break; + default: + gf_isom_sample_del(&tmp); + return GF_NOT_SUPPORTED; + } + + bs = gf_bs_new(tmp->data, tmp->dataLength, GF_BITSTREAM_READ); + s = gf_isom_hint_sample_new(entry->type); + gf_isom_hint_sample_read(s, bs, tmp->dataLength); + gf_bs_del(bs); + + count = gf_list_count(s->packetTable); + + fprintf(trace, "\n", SampleNum, LLD_CAST tmp->DTS, tmp->IsRAP, count); + + for (i=0; ipacketTable, i); + + fprintf(trace, "P_bit, pck->X_bit, pck->M_bit, pck->payloadType); + + fprintf(trace, " SequenceNumber=\"%d\" RepeatedPacket=\"%d\" DropablePacket=\"%d\" RelativeTransmissionTime=\"%d\" FullPacketSize=\"%d\">\n", + pck->SequenceNumber, pck->R_bit, pck->B_bit, pck->relativeTransTime, gf_isom_hint_rtp_length(pck)); + + + //TLV is made of Boxes + count2 = gf_list_count(pck->TLV); + if (count2) { + fprintf(trace, "\n", count2); + gf_box_array_dump(pck->TLV, trace); + fprintf(trace, "\n"); + } + //DTE is made of NON boxes + count2 = gf_list_count(pck->DataTable); + if (count2) { + fprintf(trace, "\n", count2); + DTE_Dump(pck->DataTable, trace); + fprintf(trace, "\n"); + } + fprintf(trace, "\n"); + } + + fprintf(trace, "\n"); + gf_isom_sample_del(&tmp); + gf_isom_hint_sample_del(s); + return GF_OK; +} + + +static void gpp_dump_box_nobox(FILE * trace, GF_BoxRecord *rec) +{ + fprintf(trace, "\n", rec->top, rec->left, rec->bottom, rec->right); +} + +static void gpp_print_char_offsets(FILE * trace, u32 start, u32 end, u32 *shift_offset, u32 so_count) +{ + u32 i; + if (shift_offset) { + for (i=0; ishift_offset[i]) { + start --; + break; + } + } + for (i=0; ishift_offset[i]) { + end --; + break; + } + } + } + if (start || end) fprintf(trace, "fromChar=\"%d\" toChar=\"%d\" ", start, end); +} + +static void gpp_dump_style_nobox(FILE * trace, GF_StyleRecord *rec, u32 *shift_offset, u32 so_count) +{ + fprintf(trace, ""); + if ((new_styles & GF_TXT_STYLE_ITALIC) && !(styles & GF_TXT_STYLE_ITALIC)) fprintf(dump, ""); + if ((new_styles & GF_TXT_STYLE_UNDERLINED) && !(styles & GF_TXT_STYLE_UNDERLINED)) fprintf(dump, ""); + + if ((styles & GF_TXT_STYLE_BOLD) && !(new_styles & GF_TXT_STYLE_BOLD)) fprintf(dump, ""); + if ((styles & GF_TXT_STYLE_ITALIC) && !(new_styles & GF_TXT_STYLE_ITALIC)) fprintf(dump, ""); + if ((styles & GF_TXT_STYLE_UNDERLINED) && !(new_styles & GF_TXT_STYLE_UNDERLINED)) fprintf(dump, ""); + + styles = new_styles; + } + + /*not sure if styles must be reseted at line breaks in srt...*/ + is_new_line = 0; + if ((utf16Line[j]=='\n') || (utf16Line[j]=='\r') ) { + if ((utf16Line[j]=='\r') && (utf16Line[j+1]=='\n')) j++; + fprintf(dump, "\n"); + is_new_line = 1; + } + + if (!is_new_line) { + u32 sl; + char szChar[30]; + s16 swT[2], *swz; + swT[0] = utf16Line[j]; + swT[1] = 0; + swz= (s16 *)swT; + sl = gf_utf8_wcstombs(szChar, 30, (const unsigned short **) &swz); + szChar[sl]=0; + fprintf(dump, "%s", szChar); + } + char_num++; + } + new_styles = 0; + if (new_styles != styles) { + if ((new_styles & GF_TXT_STYLE_BOLD) && !(styles & GF_TXT_STYLE_BOLD)) fprintf(dump, ""); + if ((new_styles & GF_TXT_STYLE_ITALIC) && !(styles & GF_TXT_STYLE_ITALIC)) fprintf(dump, ""); + if ((new_styles & GF_TXT_STYLE_UNDERLINED) && !(styles & GF_TXT_STYLE_UNDERLINED)) fprintf(dump, ""); + + if ((styles & GF_TXT_STYLE_BOLD) && !(new_styles & GF_TXT_STYLE_BOLD)) fprintf(dump, ""); + if ((styles & GF_TXT_STYLE_ITALIC) && !(new_styles & GF_TXT_STYLE_ITALIC)) fprintf(dump, ""); + if ((styles & GF_TXT_STYLE_UNDERLINED) && !(new_styles & GF_TXT_STYLE_UNDERLINED)) fprintf(dump, ""); + + styles = new_styles; + } + + fprintf(dump, "\n"); + } + } + gf_isom_sample_del(&s); + gf_isom_delete_text_sample(txt); + fprintf(dump, "\n"); + gf_set_progress("SRT Extract", i, count); + } + if (count) gf_set_progress("SRT Extract", i, count); + return GF_OK; +} + +static GF_Err gf_isom_dump_svg_track(GF_ISOFile *the_file, u32 track, FILE *dump) +{ + char nhmlFileName[1024]; + FILE *nhmlFile; + u32 i, count, di, ts, cur_frame; + u64 start, end; + GF_BitStream *bs; + + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, track); + if (!trak || (trak->Media->handler->handlerType != GF_ISOM_MEDIA_TEXT)) return GF_BAD_PARAM; + + strcpy(nhmlFileName, the_file->fileName); + strcat(nhmlFileName, ".nhml"); + nhmlFile = fopen(nhmlFileName, "wt"); + fprintf(nhmlFile, "\n"); + fprintf(nhmlFile, "\n", trak->Media->mediaHeader->timeScale); + fprintf(nhmlFile, "\n"); + + ts = trak->Media->mediaHeader->timeScale; + cur_frame = 0; + start = end = 0; + + fprintf(dump, "\n"); + fprintf(dump, "\n", trak->Header->width >> 16 , trak->Header->height >> 16); + fprintf(dump, "\n", (trak->Header->width >> 16)/2 , (trak->Header->height >> 16)/2); + + count = gf_isom_get_sample_count(the_file, track); + for (i=0; iDTS; + if (s->dataLength==2) { + gf_isom_sample_del(&s); + continue; + } + if (i+1DTS; + gf_isom_sample_del(&next); + } + } + + cur_frame++; + bs = gf_bs_new(s->data, s->dataLength, GF_BITSTREAM_READ); + txt = gf_isom_parse_texte_sample(bs); + gf_bs_del(bs); + + if (!txt->len) continue; + + fprintf(dump, " %s\n", cur_frame, txt->text); + fprintf(dump, " \n", ((s64)start*1.0)/ts, ((s64)end*1.0)/ts); + fprintf(dump, " \n", ((s64)end*1.0)/ts); + fprintf(dump, " \n"); + gf_isom_sample_del(&s); + gf_isom_delete_text_sample(txt); + fprintf(dump, "\n"); + gf_set_progress("SRT Extract", i, count); + + if (i == count - 2) { + fprintf(nhmlFile, "\n", ((s64)start*1.0), cur_frame); + } else { + fprintf(nhmlFile, "\n", ((s64)start*1.0), cur_frame, cur_frame+1); + } + + } + fprintf(dump, "\n"); + fprintf(dump, "\n"); + + fprintf(nhmlFile, "\n"); + fclose(nhmlFile); + + if (count) gf_set_progress("SRT Extract", i, count); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_text_dump(GF_ISOFile *the_file, u32 track, FILE *dump, u32 dump_type) +{ + switch (dump_type) { + case 2: + return gf_isom_dump_svg_track(the_file, track, dump); + case 1: + return gf_isom_dump_srt_track(the_file, track, dump); + default: + return gf_isom_dump_ttxt_track(the_file, track, dump); + } +} + + +/* ISMA 1.0 Encryption and Authentication V 1.0 dump */ +GF_Err sinf_dump(GF_Box *a, FILE * trace) +{ + GF_ProtectionInfoBox *p; + p = (GF_ProtectionInfoBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + + gf_box_dump(p->original_format, trace); + gf_box_dump(p->scheme_type, trace); + gf_box_dump(p->info, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err frma_dump(GF_Box *a, FILE * trace) +{ + GF_OriginalFormatBox *p; + p = (GF_OriginalFormatBox *)a; + fprintf(trace, "\n", gf_4cc_to_str(p->data_format)); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err schm_dump(GF_Box *a, FILE * trace) +{ + GF_SchemeTypeBox *p; + p = (GF_SchemeTypeBox *)a; + fprintf(trace, "scheme_type), p->scheme_version); + if (p->URI) fprintf(trace, "scheme_uri=\"%s\"", p->URI); + fprintf(trace, ">\n"); + + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err schi_dump(GF_Box *a, FILE * trace) +{ + GF_SchemeInformationBox *p; + p = (GF_SchemeInformationBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + if (p->ikms) gf_box_dump(p->ikms, trace); + if (p->isfm) gf_box_dump(p->isfm, trace); + if (p->okms) gf_box_dump(p->okms, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err iKMS_dump(GF_Box *a, FILE * trace) +{ + GF_ISMAKMSBox *p; + p = (GF_ISMAKMSBox *)a; + fprintf(trace, "\n", p->URI); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + fprintf(trace, "\n"); + return GF_OK; + +} + +GF_Err iSFM_dump(GF_Box *a, FILE * trace) +{ + GF_ISMASampleFormatBox *p; + const char *name = (a->type==GF_ISOM_BOX_TYPE_ISFM) ? "ISMASampleFormat" : "OMADRMAUFormatBox"; + p = (GF_ISMASampleFormatBox *)a; + fprintf(trace, "<%s selective_encryption=\"%d\" key_indicator_length=\"%d\" IV_length=\"%d\">\n", name, p->selective_encryption, p->key_indicator_length, p->IV_length); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + + fprintf(trace, "\n", name); + return GF_OK; +} + +static void dump_data(FILE *trace, char *name, char *data, u32 data_size) +{ + u32 i; + fprintf(trace, "%s=\"0x", name); + for (i=0; i\n"); + count = gf_isom_get_sample_description_count(the_file, trackNumber); + for (i=0; iMedia, i+1, (GF_SampleEntryBox **) &entry, NULL); + if (e) return e; + + switch (entry->type) { + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_ENCT: + case GF_ISOM_BOX_TYPE_ENCS: + break; + default: + continue; + } + gf_box_dump(entry, trace); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err gf_isom_dump_ismacryp_sample(GF_ISOFile *the_file, u32 trackNumber, u32 SampleNum, FILE * trace) +{ + GF_ISOSample *samp; + GF_ISMASample *isma_samp; + u32 descIndex; + + samp = gf_isom_get_sample(the_file, trackNumber, SampleNum, &descIndex); + if (!samp) return GF_BAD_PARAM; + + isma_samp = gf_isom_get_ismacryp_sample(the_file, trackNumber, samp, descIndex); + if (!isma_samp) { + gf_isom_sample_del(&samp); + return GF_NOT_SUPPORTED; + } + + fprintf(trace, "dataLength, LLD_CAST (samp->DTS+samp->CTS_Offset) ); + if (samp->CTS_Offset) fprintf(trace, "DecodingTime=\""LLD"\" ", LLD_CAST samp->DTS); + if (gf_isom_has_sync_points(the_file, trackNumber)) fprintf(trace, "RandomAccessPoint=\"%s\" ", samp->IsRAP ? "Yes" : "No"); + fprintf(trace, "IsEncrypted=\"%s\" ", (isma_samp->flags & GF_ISOM_ISMA_IS_ENCRYPTED) ? "Yes" : "No"); + if (isma_samp->flags & GF_ISOM_ISMA_IS_ENCRYPTED) { + fprintf(trace, "IV=\""LLD"\" ", LLD_CAST isma_samp->IV); + if (isma_samp->key_indicator) dump_data(trace, "KeyIndicator", (char*)isma_samp->key_indicator, isma_samp->KI_length); + } + fprintf(trace, "/>\n"); + + gf_isom_sample_del(&samp); + gf_isom_ismacryp_delete_sample(isma_samp); + return GF_OK; +} + +/* end of ISMA 1.0 Encryption and Authentication V 1.0 */ + + +/* Apple extensions */ + +static GF_Err apple_tag_dump(GF_Box *a, FILE * trace) +{ + GF_BitStream *bs; + u32 val; + Bool no_dump = 0; + char *name = "Unknown"; + GF_ListItemBox *itune = (GF_ListItemBox *)a; + switch (itune->type) { + case GF_ISOM_BOX_TYPE_0xA9NAM: name = "Name"; break; + case GF_ISOM_BOX_TYPE_0xA9CMT: name = "Comment"; break; + case GF_ISOM_BOX_TYPE_0xA9DAY: name = "Created"; break; + case GF_ISOM_BOX_TYPE_0xA9ART: name = "Artist"; break; + case GF_ISOM_BOX_TYPE_0xA9TRK: name = "Track"; break; + case GF_ISOM_BOX_TYPE_0xA9ALB: name = "Album"; break; + case GF_ISOM_BOX_TYPE_0xA9COM: name = "Compositor"; break; + case GF_ISOM_BOX_TYPE_0xA9WRT: name = "Writer"; break; + case GF_ISOM_BOX_TYPE_0xA9TOO: name = "Tool"; break; + case GF_ISOM_BOX_TYPE_0xA9CPY: name = "Copyright"; break; + case GF_ISOM_BOX_TYPE_0xA9DES: name = "Description"; break; + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_GNRE: + name = "Genre"; break; + case GF_ISOM_BOX_TYPE_aART: name = "AlbumArtist"; break; + case GF_ISOM_BOX_TYPE_PGAP: name = "Gapeless"; break; + case GF_ISOM_BOX_TYPE_DISK: name = "Disk"; break; + case GF_ISOM_BOX_TYPE_TRKN: name = "TrackNumber"; break; + case GF_ISOM_BOX_TYPE_TMPO: name = "Tempo"; break; + case GF_ISOM_BOX_TYPE_CPIL: name = "Compilation"; break; + case GF_ISOM_BOX_TYPE_COVR: name = "CoverArt"; no_dump = 1; break; + case GF_ISOM_BOX_TYPE_iTunesSpecificInfo: name = "iTunesSpecific"; no_dump = 1; break; + case GF_ISOM_BOX_TYPE_0xA9GRP: name = "Group"; break; + case GF_ISOM_ITUNE_ENCODER: name = "Encoder"; break; + } + fprintf(trace, "<%sBox", name); + if (!no_dump) { + switch (itune->type) { + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + bs = gf_bs_new(itune->data->data, itune->data->dataSize, GF_BITSTREAM_READ); + gf_bs_read_int(bs, 16); + val = gf_bs_read_int(bs, 16); + if (itune->type==GF_ISOM_BOX_TYPE_DISK) { + fprintf(trace, " DiskNumber=\"%d\" NbDisks=\"%d\" ", val, gf_bs_read_int(bs, 16) ); + } else { + fprintf(trace, " TrackNumber=\"%d\" NbTracks=\"%d\" ", val, gf_bs_read_int(bs, 16) ); + } + gf_bs_del(bs); + break; + case GF_ISOM_BOX_TYPE_TMPO: + bs = gf_bs_new(itune->data->data, itune->data->dataSize, GF_BITSTREAM_READ); + fprintf(trace, " BPM=\"%d\" ", gf_bs_read_int(bs, 16) ); + gf_bs_del(bs); + break; + case GF_ISOM_BOX_TYPE_CPIL: + fprintf(trace, " IsCompilation=\"%s\" ", itune->data->data[0] ? "yes" : "no"); + break; + case GF_ISOM_BOX_TYPE_PGAP: + fprintf(trace, " IsGapeless=\"%s\" ", itune->data->data[0] ? "yes" : "no"); + break; + default: + if (strcmp(name, "Unknown")) { + if (itune->data->data[0]) { + fprintf(trace, " value=\"%s\" ", itune->data->data); + } else { + fprintf(trace, " value=\""); + DumpData(trace, itune->data->data, itune->data->dataSize); + fprintf(trace, "\" "); + } + } + break; + } + } + fprintf(trace, ">\n"); + gf_full_box_dump((GF_Box *)itune->data, trace); + DumpBox(a, trace); + fprintf(trace, "\n", name); + return GF_OK; +} + +GF_Err ilst_dump(GF_Box *a, FILE * trace) +{ + u32 i; + GF_Box *tag; + GF_Err e; + GF_ItemListBox *ptr; + ptr = (GF_ItemListBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + + i=0; + while ( (tag = gf_list_enum(ptr->tags, &i))) { + e = apple_tag_dump(tag, trace); + if(e) return e; + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err ListEntry_dump(GF_Box *a, FILE * trace) +{ + GF_ItemListBox *p; + + p = (GF_ItemListBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err data_dump(GF_Box *a, FILE * trace) +{ + GF_ItemListBox *p; + + p = (GF_ItemListBox *)a; + fprintf(trace, "\n"); + DumpBox(a, trace); + gf_full_box_dump(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err ohdr_dump(GF_Box *a, FILE * trace) +{ + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox *)a; + fprintf(trace, "EncryptionMethod, ptr->PaddingScheme, ptr->PlaintextLength); + if (ptr->RightsIssuerURL) fprintf(trace, "RightsIssuerURL=\"%s\" ", ptr->RightsIssuerURL); + if (ptr->ContentID) fprintf(trace, "ContentID=\"%s\" ", ptr->ContentID); + if (ptr->TextualHeaders) { + u32 i, offset; + char *start = ptr->TextualHeaders; + fprintf(trace, "TextualHeaders=\""); + i=offset=0; + while (iTextualHeadersLen) { + if (start[i]==0) { + fprintf(trace, "%s ", start+offset); + offset=i+1; + } + i++; + } + fprintf(trace, "%s\" ", start+offset); + } + + fprintf(trace, ">\n"); + gf_full_box_dump((GF_Box *)a, trace); + gf_box_array_dump(ptr->ExtendedHeaders, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err grpi_dump(GF_Box *a, FILE * trace) +{ + GF_OMADRMGroupIDBox *ptr = (GF_OMADRMGroupIDBox *)a; + fprintf(trace, "GroupID, ptr->GKEncryptionMethod); + DumpData(trace, ptr->GroupKey, ptr->GKLength); + fprintf(trace, ">\n"); + gf_full_box_dump((GF_Box *)a, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err mdri_dump(GF_Box *a, FILE * trace) +{ + GF_OMADRMMutableInformationBox *ptr = (GF_OMADRMMutableInformationBox*)a; + fprintf(trace, "\n"); + gf_box_dump((GF_Box *)a, trace); + gf_box_array_dump(ptr->boxes, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err odtt_dump(GF_Box *a, FILE * trace) +{ + GF_OMADRMTransactionTrackingBox *ptr = (GF_OMADRMTransactionTrackingBox *)a; + fprintf(trace, "TransactionID, 16); + fprintf(trace, "\">\n"); + gf_full_box_dump((GF_Box *)a, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err odrb_dump(GF_Box *a, FILE * trace) +{ + GF_OMADRMRightsObjectBox*ptr = (GF_OMADRMRightsObjectBox*)a; + fprintf(trace, "oma_ro, ptr->oma_ro_size); + fprintf(trace, "\">\n"); + gf_full_box_dump((GF_Box *)a, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err odkm_dump(GF_Box *a, FILE * trace) +{ + GF_OMADRMKMSBox *ptr = (GF_OMADRMKMSBox*)a; + fprintf(trace, "\n"); + gf_full_box_dump((GF_Box *)a, trace); + if (ptr->hdr) gf_box_dump((GF_Box *)ptr->hdr, trace); + if (ptr->fmt) gf_box_dump((GF_Box *)ptr->fmt, trace); + fprintf(trace, "\n"); + return GF_OK; +} + + +GF_Err pasp_dump(GF_Box *a, FILE * trace) +{ + GF_PixelAspectRatioBox *ptr = (GF_PixelAspectRatioBox*)a; + fprintf(trace, "\n", ptr->hSpacing, ptr->vSpacing); + DumpBox((GF_Box *)a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + + +GF_Err tsel_dump(GF_Box *a, FILE * trace) +{ + u32 i; + GF_TrackSelectionBox *ptr = (GF_TrackSelectionBox *)a; + fprintf(trace, "switchGroup); + for (i=0; iattributeListCount;i++) { + if (i) fprintf(trace, ";"); + fprintf(trace, "%s", gf_4cc_to_str(ptr->attributeList[i])); + } + fprintf(trace, "\">\n"); + gf_full_box_dump((GF_Box *)a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err metx_dump(GF_Box *a, FILE * trace) +{ + GF_MetaDataSampleEntryBox *ptr = (GF_MetaDataSampleEntryBox*)a; + const char *name = (ptr->type==GF_ISOM_BOX_TYPE_METX) ? "XMLMetaDataSampleEntryBox" : "TextMetaDataSampleEntryBox"; + + fprintf(trace, "<%s ", name); + if (ptr->type==GF_ISOM_BOX_TYPE_METX) { + fprintf(trace, "namespace=\"%s\" ", ptr->mime_type_or_namespace); + if (ptr->xml_schema_loc) fprintf(trace, "schema_location=\"%s\" ", ptr->xml_schema_loc); + } else { + fprintf(trace, "mime_type=\"%s\" ", ptr->mime_type_or_namespace); + } + if (ptr->content_encoding) fprintf(trace, "content_encoding=\"%s\" ", ptr->content_encoding); + fprintf(trace, ">\n"); + DumpBox(a, trace); + + if (ptr->bitrate) gf_box_dump(ptr->bitrate, trace); + if (ptr->protection_info) gf_box_dump(ptr->protection_info, trace); + + fprintf(trace, "\n", name); + return GF_OK; +} + + +GF_Err dims_dump(GF_Box *a, FILE * trace) +{ + GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox*)a; + + fprintf(trace, "\n", p->dataReferenceIndex); + DumpBox(a, trace); + if (p->config) gf_box_dump(p->config, trace); + if (p->scripts) gf_box_dump(p->scripts, trace); + if (p->bitrate) gf_box_dump(p->bitrate, trace); + if (p->protection_info) gf_box_dump(p->protection_info, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err diST_dump(GF_Box *a, FILE * trace) +{ + GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox*)a; + + fprintf(trace, "\n", p->content_script_types); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} +GF_Err dimC_dump(GF_Box *a, FILE * trace) +{ + GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)a; + + fprintf(trace, "\n", + p->profile, p->level, p->pathComponents, p->fullRequestHost, p->streamType, p->containsRedundant, p->textEncoding, p->contentEncoding); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + + +GF_Err dac3_dump(GF_Box *a, FILE * trace) +{ + GF_AC3ConfigBox *p = (GF_AC3ConfigBox *)a; + + fprintf(trace, "\n", + p->cfg.fscod, p->cfg.bsid, p->cfg.bsmod, p->cfg.acmod, p->cfg.lfon, p->cfg.brcode); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err ac3_dump(GF_Box *a, FILE * trace) +{ + GF_AC3SampleEntryBox *p = (GF_AC3SampleEntryBox *)a; + fprintf(trace, "\n"); + gf_box_dump(p->info, trace); + DumpBox(a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + diff --git a/src/isomedia/box_funcs.c b/src/isomedia/box_funcs.c new file mode 100644 index 0000000..711be7f --- /dev/null +++ b/src/isomedia/box_funcs.c @@ -0,0 +1,1416 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +//Add this funct to handle incomplete files... +//bytesExpected is 0 most of the time. If the file is incomplete, bytesExpected +//is the number of bytes missing to parse the box... +GF_Err gf_isom_parse_root_box(GF_Box **outBox, GF_BitStream *bs, u64 *bytesExpected) +{ + GF_Err ret; + u64 start; + //first make sure we can at least get the box size and type... + //18 = size (int32) + type (int32) + if (gf_bs_available(bs) < 8) { + *bytesExpected = 8; + return GF_ISOM_INCOMPLETE_FILE; + } + start = gf_bs_get_position(bs); + ret = gf_isom_parse_box(outBox, bs); + if (ret == GF_ISOM_INCOMPLETE_FILE) { + *bytesExpected = (*outBox)->size; + gf_bs_seek(bs, start); + gf_isom_box_del(*outBox); + *outBox = NULL; + } + return ret; +} + + +GF_Err gf_isom_parse_box_ex(GF_Box **outBox, GF_BitStream *bs, u32 parent_type) +{ + u32 type, hdr_size; + u64 size, start, end; + char uuid[16]; + GF_Err e; + GF_Box *newBox; + e = GF_OK; + if ((bs == NULL) || (outBox == NULL) ) return GF_BAD_PARAM; + *outBox = NULL; + + start = gf_bs_get_position(bs); + + size = (u64) gf_bs_read_u32(bs); + hdr_size = 4; + /*fix for some boxes found in some old hinted files*/ + if ((size >= 2) && (size <= 4)) { + size = 4; + type = GF_ISOM_BOX_TYPE_VOID; + } else { + /*now here's a bad thing: some files use size 0 for void atoms, some for "till end of file" indictaion..*/ + if (!size) { + type = gf_bs_peek_bits(bs, 32, 0); + if (!isalnum((type>>24)&0xFF) || !isalnum((type>>16)&0xFF) || !isalnum((type>>8)&0xFF) || !isalnum(type&0xFF)) { + size = 4; + type = GF_ISOM_BOX_TYPE_VOID; + } else { + goto proceed_box; + } + } else { +proceed_box: + type = gf_bs_read_u32(bs); + hdr_size += 4; + /*no size means till end of file - EXCEPT FOR some old QuickTime boxes...*/ + if (type == GF_ISOM_BOX_TYPE_TOTL) + size = 12; + if (!size) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning Read Box type %s size 0 reading till the end of file\n", gf_4cc_to_str(type))); + size = gf_bs_available(bs) + 8; + } + } + } + /*handle uuid*/ + memset(uuid, 0, 16); + if (type == GF_ISOM_BOX_TYPE_UUID ) { + gf_bs_read_data(bs, uuid, 16); + hdr_size += 16; + } + + //handle large box + if (size == 1) { + size = gf_bs_read_u64(bs); + hdr_size += 8; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Read Box type %s size "LLD" start "LLD"\n", gf_4cc_to_str(type), LLD_CAST size, LLD_CAST start)); + + if ( size < hdr_size ) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Box size "LLD" less than box header size %d\n", LLD_CAST size, hdr_size)); + return GF_ISOM_INVALID_FILE; + } + + if (parent_type && (parent_type==GF_ISOM_BOX_TYPE_TREF)) { + newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + if (!newBox) return GF_OUT_OF_MEM; + ((GF_TrackReferenceTypeBox*)newBox)->reference_type = type; + } else { + //OK, create the box based on the type + newBox = gf_isom_box_new(type); + if (!newBox) return GF_OUT_OF_MEM; + } + + //OK, init and read this box + if (type==GF_ISOM_BOX_TYPE_UUID) memcpy(((GF_UUIDBox *)newBox)->uuid, uuid, 16); + + if (!newBox->type) newBox->type = type; + + end = gf_bs_available(bs); + if (size - hdr_size > end ) { + newBox->size = size - hdr_size - end; + *outBox = newBox; + return GF_ISOM_INCOMPLETE_FILE; + } + //we need a special reading for these boxes... + if ((newBox->type == GF_ISOM_BOX_TYPE_STDP) || (newBox->type == GF_ISOM_BOX_TYPE_SDTP)) { + newBox->size = size; + *outBox = newBox; + return GF_OK; + } + + newBox->size = size - hdr_size; + e = gf_isom_box_read(newBox, bs); + newBox->size = size; + end = gf_bs_get_position(bs); + + if (e && (e != GF_ISOM_INCOMPLETE_FILE)) { + gf_isom_box_del(newBox); + *outBox = NULL; + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Read Box \"%s\" failed (%s)\n", gf_4cc_to_str(type), gf_error_to_string(e))); + return e; + } + + if (end-start > size) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Box \"%s\" size "LLU" invalid (read "LLU")\n", gf_4cc_to_str(type), LLU_CAST size, LLU_CAST (end-start) )); + /*let's still try to load the file since no error was notified*/ + gf_bs_seek(bs, start+size); + } else if (end-start < size) { + u32 to_skip = (u32) (size-(end-start)); + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Box \"%s\" has %d extra bytes\n", gf_4cc_to_str(type), to_skip)); + gf_bs_skip_bytes(bs, to_skip); + } + *outBox = newBox; + return e; +} + +GF_Err gf_isom_parse_box(GF_Box **outBox, GF_BitStream *bs) +{ + return gf_isom_parse_box_ex(outBox, bs, 0); +} + +GF_Err gf_isom_full_box_read(GF_Box *ptr, GF_BitStream *bs) +{ + GF_FullBox *self = (GF_FullBox *) ptr; + if (ptr->size<4) return GF_ISOM_INVALID_FILE; + self->version = gf_bs_read_u8(bs); + self->flags = gf_bs_read_u24(bs); + ptr->size -= 4; + return GF_OK; +} + +void gf_isom_full_box_init(GF_Box *a) +{ + GF_FullBox *ptr = (GF_FullBox *)a; + if (! ptr) return; + ptr->flags = 0; + ptr->version = 0; +} + + +void gf_isom_box_array_del(GF_List *boxList) +{ + u32 count, i; + GF_Box *a; + if (!boxList) return; + count = gf_list_count(boxList); + for (i = 0; i < count; i++) { + a = (GF_Box *)gf_list_get(boxList, i); + if (a) gf_isom_box_del(a); + } + gf_list_del(boxList); +} + +GF_Err gf_isom_read_box_list_ex(GF_Box *parent, GF_BitStream *bs, GF_Err (*add_box)(GF_Box *par, GF_Box *b), u32 parent_type) +{ + GF_Err e; + GF_Box *a; + while (parent->size) { + e = gf_isom_parse_box_ex(&a, bs, parent_type); + if (e) { + if (a) gf_isom_box_del(a); + return e; + } + if (parent->size < a->size) { + if (a) gf_isom_box_del(a); + return GF_OK; + //return GF_ISOM_INVALID_FILE; + } + parent->size -= a->size; + e = add_box(parent, a); + if (e) { + gf_isom_box_del(a); + return e; + } + } + return GF_OK; +} + +GF_Err gf_isom_read_box_list(GF_Box *parent, GF_BitStream *bs, GF_Err (*add_box)(GF_Box *par, GF_Box *b)) +{ + return gf_isom_read_box_list_ex(parent, bs, add_box, 0); +} + +//from here, for write/edit versions +#ifndef GPAC_READ_ONLY + +GF_Err gf_isom_box_get_size(GF_Box *ptr) +{ + ptr->size = 8; + + if (ptr->type == GF_ISOM_BOX_TYPE_UUID) { + ptr->size += 16; + } + //the large size is handled during write, cause at this stage we don't know the size + return GF_OK; +} + +GF_Err gf_isom_full_box_get_size(GF_Box *ptr) +{ + GF_Err e; + e = gf_isom_box_get_size(ptr); + if (e) return e; + ptr->size += 4; + return GF_OK; +} + + +GF_Err gf_isom_box_write_header(GF_Box *ptr, GF_BitStream *bs) +{ + if (! bs || !ptr) return GF_BAD_PARAM; + if (!ptr->size) return GF_ISOM_INVALID_FILE; + + if (ptr->size > 0xFFFFFFFF) { + gf_bs_write_u32(bs, 1); + } else { + gf_bs_write_u32(bs, (u32) ptr->size); + } + gf_bs_write_u32(bs, ptr->type); + if (ptr->type == GF_ISOM_BOX_TYPE_UUID) + gf_bs_write_data(bs, (char*)((GF_UUIDBox*)ptr)->uuid, 16); + if (ptr->size > 0xFFFFFFFF) + gf_bs_write_u64(bs, ptr->size); + return GF_OK; +} + +GF_Err gf_isom_full_box_write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_FullBox *ptr = (GF_FullBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u8(bs, ptr->version); + gf_bs_write_u24(bs, ptr->flags); + return GF_OK; +} + + +GF_Err gf_isom_box_array_write(GF_Box *parent, GF_List *list, GF_BitStream *bs) +{ + u32 count, i; + GF_Box *a; + GF_Err e; + if (!list) return GF_BAD_PARAM; + count = gf_list_count(list); + for (i = 0; i < count; i++) { + a = (GF_Box *)gf_list_get(list, i); + if (a) { + e = gf_isom_box_write(a, bs); + if (e) return e; + } + } + return GF_OK; +} + +GF_Err gf_isom_box_array_size(GF_Box *parent, GF_List *list) +{ + GF_Err e; + u32 count, i; + GF_Box *a; + if (! list) return GF_BAD_PARAM; + + count = gf_list_count(list); + for (i = 0; i < count; i++) { + a = (GF_Box *)gf_list_get(list, i); + if (a) { + e = gf_isom_box_size(a); + if (e) return e; + parent->size += a->size; + } + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +GF_Box *gf_isom_box_new(u32 boxType) +{ + GF_Box *a; + switch (boxType) { + case GF_ISOM_BOX_TYPE_REFT: + return reftype_New(); + case GF_ISOM_BOX_TYPE_FREE: return free_New(); + case GF_ISOM_BOX_TYPE_SKIP: + a = free_New(); + if (a) a->type = GF_ISOM_BOX_TYPE_SKIP; + return a; + case GF_ISOM_BOX_TYPE_MDAT: return mdat_New(); + case GF_ISOM_BOX_TYPE_MOOV: return moov_New(); + case GF_ISOM_BOX_TYPE_MVHD: return mvhd_New(); + case GF_ISOM_BOX_TYPE_MDHD: return mdhd_New(); + case GF_ISOM_BOX_TYPE_VMHD: return vmhd_New(); + case GF_ISOM_BOX_TYPE_SMHD: return smhd_New(); + case GF_ISOM_BOX_TYPE_HMHD: return hmhd_New(); + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + a = nmhd_New(); + if (a) a->type = boxType; + return a; + case GF_ISOM_BOX_TYPE_STBL: return stbl_New(); + case GF_ISOM_BOX_TYPE_DINF: return dinf_New(); + case GF_ISOM_BOX_TYPE_URL: return url_New(); + case GF_ISOM_BOX_TYPE_URN: return urn_New(); + case GF_ISOM_BOX_TYPE_CPRT: return cprt_New(); + case GF_ISOM_BOX_TYPE_CHPL: return chpl_New(); + case GF_ISOM_BOX_TYPE_HDLR: return hdlr_New(); + case GF_ISOM_BOX_TYPE_IODS: return iods_New(); + case GF_ISOM_BOX_TYPE_TRAK: return trak_New(); + case GF_ISOM_BOX_TYPE_MP4S: return mp4s_New(); + case GF_ISOM_BOX_TYPE_MP4V: return mp4v_New(); + case GF_ISOM_BOX_TYPE_MP4A: return mp4a_New(); + case GF_ISOM_BOX_TYPE_GNRM: return gnrm_New(); + case GF_ISOM_BOX_TYPE_GNRV: return gnrv_New(); + case GF_ISOM_BOX_TYPE_GNRA: return gnra_New(); + case GF_ISOM_BOX_TYPE_EDTS: return edts_New(); + case GF_ISOM_BOX_TYPE_UDTA: return udta_New(); + case GF_ISOM_BOX_TYPE_DREF: return dref_New(); + case GF_ISOM_BOX_TYPE_STSD: return stsd_New(); + case GF_ISOM_BOX_TYPE_STTS: return stts_New(); + case GF_ISOM_BOX_TYPE_CTTS: return ctts_New(); + case GF_ISOM_BOX_TYPE_STSH: return stsh_New(); + case GF_ISOM_BOX_TYPE_ELST: return elst_New(); + case GF_ISOM_BOX_TYPE_STSC: return stsc_New(); + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + a = stsz_New(); + if (a) a->type = boxType; + return a; + case GF_ISOM_BOX_TYPE_STCO: return stco_New(); + case GF_ISOM_BOX_TYPE_STSS: return stss_New(); + case GF_ISOM_BOX_TYPE_STDP: return stdp_New(); + case GF_ISOM_BOX_TYPE_SDTP: return sdtp_New(); + case GF_ISOM_BOX_TYPE_CO64: return co64_New(); + case GF_ISOM_BOX_TYPE_ESDS: return esds_New(); + case GF_ISOM_BOX_TYPE_MINF: return minf_New(); + case GF_ISOM_BOX_TYPE_TKHD: return tkhd_New(); + case GF_ISOM_BOX_TYPE_TREF: return tref_New(); + case GF_ISOM_BOX_TYPE_MDIA: return mdia_New(); + + case GF_ISOM_BOX_TYPE_FTYP: return ftyp_New(); + case GF_ISOM_BOX_TYPE_FADB: return padb_New(); + case GF_ISOM_BOX_TYPE_VOID: return void_New(); + case GF_ISOM_BOX_TYPE_STSF: return stsf_New(); + case GF_ISOM_BOX_TYPE_PDIN: return pdin_New(); + + case GF_ISOM_BOX_TYPE_RTP_STSD: + a = ghnt_New(); + if (a) a->type = boxType; + return a; + case GF_ISOM_BOX_TYPE_RTPO: return rtpo_New(); + case GF_ISOM_BOX_TYPE_HNTI: return hnti_New(); + case GF_ISOM_BOX_TYPE_SDP: return sdp_New(); + case GF_ISOM_BOX_TYPE_HINF: return hinf_New(); + case GF_ISOM_BOX_TYPE_RELY: return rely_New(); + case GF_ISOM_BOX_TYPE_TIMS: return tims_New(); + case GF_ISOM_BOX_TYPE_TSRO: return tsro_New(); + case GF_ISOM_BOX_TYPE_SNRO: return snro_New(); + case GF_ISOM_BOX_TYPE_TRPY: return trpy_New(); + case GF_ISOM_BOX_TYPE_NUMP: return nump_New(); + case GF_ISOM_BOX_TYPE_TOTL: return totl_New(); + case GF_ISOM_BOX_TYPE_NPCK: return npck_New(); + case GF_ISOM_BOX_TYPE_TPYL: return tpyl_New(); + case GF_ISOM_BOX_TYPE_TPAY: return tpay_New(); + case GF_ISOM_BOX_TYPE_MAXR: return maxr_New(); + case GF_ISOM_BOX_TYPE_DMED: return dmed_New(); + case GF_ISOM_BOX_TYPE_DIMM: return dimm_New(); + case GF_ISOM_BOX_TYPE_DREP: return drep_New(); + case GF_ISOM_BOX_TYPE_TMIN: return tmin_New(); + case GF_ISOM_BOX_TYPE_TMAX: return tmax_New(); + case GF_ISOM_BOX_TYPE_PMAX: return pmax_New(); + case GF_ISOM_BOX_TYPE_DMAX: return dmax_New(); + case GF_ISOM_BOX_TYPE_PAYT: return payt_New(); + case GF_ISOM_BOX_TYPE_NAME: return name_New(); + +#ifndef GPAC_ISOM_NO_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: return mvex_New(); + case GF_ISOM_BOX_TYPE_MEHD: return mehd_New(); + case GF_ISOM_BOX_TYPE_TREX: return trex_New(); + case GF_ISOM_BOX_TYPE_MOOF: return moof_New(); + case GF_ISOM_BOX_TYPE_MFHD: return mfhd_New(); + case GF_ISOM_BOX_TYPE_TRAF: return traf_New(); + case GF_ISOM_BOX_TYPE_TFHD: return tfhd_New(); + case GF_ISOM_BOX_TYPE_TRUN: return trun_New(); +#endif + + /*3GPP boxes*/ + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + return gppa_New(boxType); + case GF_ISOM_SUBTYPE_3GP_H263: + return gppv_New(boxType); + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + return gppc_New(boxType); + + /*AVC boxes*/ + case GF_ISOM_BOX_TYPE_AVCC: return avcc_New(); + case GF_ISOM_BOX_TYPE_BTRT: return btrt_New(); + case GF_ISOM_BOX_TYPE_M4DS: return m4ds_New(); + case GF_ISOM_BOX_TYPE_AVC1: return avc1_New(); + + /*3GPP streaming text*/ + case GF_ISOM_BOX_TYPE_FTAB: return ftab_New(); + case GF_ISOM_BOX_TYPE_TX3G: return tx3g_New(); + case GF_ISOM_BOX_TYPE_STYL: return styl_New(); + case GF_ISOM_BOX_TYPE_HLIT: return hlit_New(); + case GF_ISOM_BOX_TYPE_HCLR: return hclr_New(); + case GF_ISOM_BOX_TYPE_KROK: return krok_New(); + case GF_ISOM_BOX_TYPE_DLAY: return dlay_New(); + case GF_ISOM_BOX_TYPE_HREF: return href_New(); + case GF_ISOM_BOX_TYPE_TBOX: return tbox_New(); + case GF_ISOM_BOX_TYPE_BLNK: return blnk_New(); + case GF_ISOM_BOX_TYPE_TWRP: return twrp_New(); + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: return iKMS_New(); + case GF_ISOM_BOX_TYPE_ISFM: return iSFM_New(); + + /* ISO FF extensions for MPEG-21 */ + case GF_ISOM_BOX_TYPE_META: return meta_New(); + case GF_ISOM_BOX_TYPE_XML: return xml_New(); + case GF_ISOM_BOX_TYPE_BXML: return bxml_New(); + case GF_ISOM_BOX_TYPE_ILOC: return iloc_New(); + case GF_ISOM_BOX_TYPE_PITM: return pitm_New(); + case GF_ISOM_BOX_TYPE_IPRO: return ipro_New(); + case GF_ISOM_BOX_TYPE_INFE: return infe_New(); + case GF_ISOM_BOX_TYPE_IINF: return iinf_New(); + case GF_ISOM_BOX_TYPE_SINF: return sinf_New(); + case GF_ISOM_BOX_TYPE_FRMA: return frma_New(); + case GF_ISOM_BOX_TYPE_SCHM: return schm_New(); + case GF_ISOM_BOX_TYPE_SCHI: return schi_New(); + case GF_ISOM_BOX_TYPE_ENCA: return enca_New(); + case GF_ISOM_BOX_TYPE_ENCV: return encv_New(); + case GF_ISOM_BOX_TYPE_ENCS: return encs_New(); + case GF_ISOM_BOX_TYPE_UUID: return uuid_New(); + + /* Apple extensions */ + case GF_ISOM_BOX_TYPE_ILST: return ilst_New(); + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_0xA9ENC: + case GF_ISOM_BOX_TYPE_aART: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_COVR: return ListItem_New(boxType); + + case GF_ISOM_BOX_TYPE_DATA: return data_New(); + + case GF_ISOM_BOX_TYPE_OHDR: return ohdr_New(); + case GF_ISOM_BOX_TYPE_GRPI: return grpi_New(); + case GF_ISOM_BOX_TYPE_MDRI: return mdri_New(); + case GF_ISOM_BOX_TYPE_ODTT: return odtt_New(); + case GF_ISOM_BOX_TYPE_ODRB: return odrb_New(); + case GF_ISOM_BOX_TYPE_ODKM: return odkm_New(); + case GF_ISOM_BOX_TYPE_ODAF: + a = iSFM_New(); + a->type = GF_ISOM_BOX_TYPE_ODAF; + return a; + + case GF_ISOM_BOX_TYPE_PASP: return pasp_New(); + case GF_ISOM_BOX_TYPE_TSEL: return tsel_New(); + + case GF_ISOM_BOX_TYPE_DIMS: return dims_New(); + case GF_ISOM_BOX_TYPE_DIMC: return dimC_New(); + case GF_ISOM_BOX_TYPE_DIST: return diST_New(); + + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + return metx_New(boxType); + + case GF_ISOM_BOX_TYPE_AC3: return ac3_New(); + case GF_ISOM_BOX_TYPE_DAC3: return dac3_New(); + + default: + a = defa_New(); + if (a) a->type = boxType; + return a; + } +} + + +void gf_isom_box_del(GF_Box *a) +{ + if (!a) return; + switch (a->type) { + case GF_ISOM_BOX_TYPE_REFT: + reftype_del(a); + return; + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + free_del(a); + return; + case GF_ISOM_BOX_TYPE_MDAT: mdat_del(a); return; + case GF_ISOM_BOX_TYPE_MOOV: moov_del(a); return; + case GF_ISOM_BOX_TYPE_MVHD: mvhd_del(a); return; + case GF_ISOM_BOX_TYPE_MDHD: mdhd_del(a); return; + case GF_ISOM_BOX_TYPE_VMHD: vmhd_del(a); return; + case GF_ISOM_BOX_TYPE_SMHD: smhd_del(a); return; + case GF_ISOM_BOX_TYPE_HMHD: hmhd_del(a); return; + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + nmhd_del(a); + return; + case GF_ISOM_BOX_TYPE_STBL: stbl_del(a); return; + case GF_ISOM_BOX_TYPE_DINF: dinf_del(a); return; + case GF_ISOM_BOX_TYPE_URL: url_del(a); return; + case GF_ISOM_BOX_TYPE_URN: urn_del(a); return; + case GF_ISOM_BOX_TYPE_CHPL: chpl_del(a); return; + case GF_ISOM_BOX_TYPE_CPRT: cprt_del(a); return; + case GF_ISOM_BOX_TYPE_HDLR: hdlr_del(a); return; + case GF_ISOM_BOX_TYPE_IODS: iods_del(a); return; + case GF_ISOM_BOX_TYPE_TRAK: trak_del(a); return; + case GF_ISOM_BOX_TYPE_MP4S: mp4s_del(a); return; + case GF_4CC('2','6','4','b'): + case GF_ISOM_BOX_TYPE_MP4V: + mp4v_del(a); + return; + case GF_ISOM_BOX_TYPE_MP4A: mp4a_del(a); return; + case GF_ISOM_BOX_TYPE_GNRM: gnrm_del(a); return; + case GF_ISOM_BOX_TYPE_GNRV: gnrv_del(a); return; + case GF_ISOM_BOX_TYPE_GNRA: gnra_del(a); return; + case GF_ISOM_BOX_TYPE_EDTS: edts_del(a); return; + case GF_ISOM_BOX_TYPE_UDTA: udta_del(a); return; + case GF_ISOM_BOX_TYPE_DREF: dref_del(a); return; + case GF_ISOM_BOX_TYPE_STSD: stsd_del(a); return; + case GF_ISOM_BOX_TYPE_STTS: stts_del(a); return; + case GF_ISOM_BOX_TYPE_CTTS: ctts_del(a); return; + case GF_ISOM_BOX_TYPE_STSH: stsh_del(a); return; + case GF_ISOM_BOX_TYPE_ELST: elst_del(a); return; + case GF_ISOM_BOX_TYPE_STSC: stsc_del(a); return; + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + stsz_del(a); + return; + case GF_ISOM_BOX_TYPE_STCO: stco_del(a); return; + case GF_ISOM_BOX_TYPE_STSS: stss_del(a); return; + case GF_ISOM_BOX_TYPE_STDP: stdp_del(a); return; + case GF_ISOM_BOX_TYPE_SDTP: sdtp_del(a); return; + case GF_ISOM_BOX_TYPE_CO64: co64_del(a); return; + case GF_ISOM_BOX_TYPE_ESDS: esds_del(a); return; + case GF_ISOM_BOX_TYPE_MINF: minf_del(a); return; + case GF_ISOM_BOX_TYPE_TKHD: tkhd_del(a); return; + case GF_ISOM_BOX_TYPE_TREF: tref_del(a); return; + case GF_ISOM_BOX_TYPE_MDIA: mdia_del(a); return; + case GF_ISOM_BOX_TYPE_FTYP: ftyp_del(a); return; + case GF_ISOM_BOX_TYPE_FADB: padb_del(a); return; + case GF_ISOM_BOX_TYPE_VOID: void_del(a); return; + case GF_ISOM_BOX_TYPE_STSF: stsf_del(a); return; + case GF_ISOM_BOX_TYPE_PDIN: pdin_del(a); return; + + case GF_ISOM_BOX_TYPE_RTP_STSD: ghnt_del(a); return; + case GF_ISOM_BOX_TYPE_RTPO: rtpo_del(a); return; + case GF_ISOM_BOX_TYPE_HNTI: hnti_del(a); return; + case GF_ISOM_BOX_TYPE_SDP: sdp_del(a); return; + case GF_ISOM_BOX_TYPE_HINF: hinf_del(a); return; + case GF_ISOM_BOX_TYPE_RELY: rely_del(a); return; + case GF_ISOM_BOX_TYPE_TIMS: tims_del(a); return; + case GF_ISOM_BOX_TYPE_TSRO: tsro_del(a); return; + case GF_ISOM_BOX_TYPE_SNRO: snro_del(a); return; + case GF_ISOM_BOX_TYPE_TRPY: trpy_del(a); return; + case GF_ISOM_BOX_TYPE_NUMP: nump_del(a); return; + case GF_ISOM_BOX_TYPE_TOTL: totl_del(a); return; + case GF_ISOM_BOX_TYPE_NPCK: npck_del(a); return; + case GF_ISOM_BOX_TYPE_TPYL: tpyl_del(a); return; + case GF_ISOM_BOX_TYPE_TPAY: tpay_del(a); return; + case GF_ISOM_BOX_TYPE_MAXR: maxr_del(a); return; + case GF_ISOM_BOX_TYPE_DMED: dmed_del(a); return; + case GF_ISOM_BOX_TYPE_DIMM: dimm_del(a); return; + case GF_ISOM_BOX_TYPE_DREP: drep_del(a); return; + case GF_ISOM_BOX_TYPE_TMIN: tmin_del(a); return; + case GF_ISOM_BOX_TYPE_TMAX: tmax_del(a); return; + case GF_ISOM_BOX_TYPE_PMAX: pmax_del(a); return; + case GF_ISOM_BOX_TYPE_DMAX: dmax_del(a); return; + case GF_ISOM_BOX_TYPE_PAYT: payt_del(a); return; + case GF_ISOM_BOX_TYPE_NAME: name_del(a); return; + +#ifndef GPAC_ISOM_NO_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: mvex_del(a); return; + case GF_ISOM_BOX_TYPE_MEHD: mehd_del(a); return; + case GF_ISOM_BOX_TYPE_TREX: trex_del(a); return; + case GF_ISOM_BOX_TYPE_MOOF: moof_del(a); return; + case GF_ISOM_BOX_TYPE_MFHD: mfhd_del(a); return; + case GF_ISOM_BOX_TYPE_TRAF: traf_del(a); return; + case GF_ISOM_BOX_TYPE_TFHD: tfhd_del(a); return; + case GF_ISOM_BOX_TYPE_TRUN: trun_del(a); return; +#endif + + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + gppa_del(a); + return; + case GF_ISOM_SUBTYPE_3GP_H263: gppv_del(a); return; + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + gppc_del(a); return; + + /*AVC boxes*/ + case GF_ISOM_BOX_TYPE_AVCC: avcc_del(a); return; + case GF_ISOM_BOX_TYPE_BTRT: btrt_del(a); return; + case GF_ISOM_BOX_TYPE_M4DS: m4ds_del(a); return; + case GF_ISOM_BOX_TYPE_AVC1: mp4v_del(a); return; + + /*3GPP streaming text*/ + case GF_ISOM_BOX_TYPE_FTAB: ftab_del(a); return; + case GF_ISOM_BOX_TYPE_TX3G: tx3g_del(a); return; + case GF_ISOM_BOX_TYPE_STYL: styl_del(a); return; + case GF_ISOM_BOX_TYPE_HLIT: hlit_del(a); return; + case GF_ISOM_BOX_TYPE_HCLR: hclr_del(a); return; + case GF_ISOM_BOX_TYPE_KROK: krok_del(a); return; + case GF_ISOM_BOX_TYPE_DLAY: dlay_del(a); return; + case GF_ISOM_BOX_TYPE_HREF: href_del(a); return; + case GF_ISOM_BOX_TYPE_TBOX: tbox_del(a); return; + case GF_ISOM_BOX_TYPE_BLNK: blnk_del(a); return; + case GF_ISOM_BOX_TYPE_TWRP: twrp_del(a); return; + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: iKMS_del(a); return; + case GF_ISOM_BOX_TYPE_ISFM: iSFM_del(a); return; + + /* ISO FF extensions for MPEG-21 */ + case GF_ISOM_BOX_TYPE_META: meta_del(a); return; + case GF_ISOM_BOX_TYPE_XML: xml_del(a); return; + case GF_ISOM_BOX_TYPE_BXML: bxml_del(a); return; + case GF_ISOM_BOX_TYPE_ILOC: iloc_del(a); return; + case GF_ISOM_BOX_TYPE_PITM: pitm_del(a); return; + case GF_ISOM_BOX_TYPE_IPRO: ipro_del(a); return; + case GF_ISOM_BOX_TYPE_INFE: infe_del(a); return; + case GF_ISOM_BOX_TYPE_IINF: iinf_del(a); return; + case GF_ISOM_BOX_TYPE_SINF: sinf_del(a); return; + case GF_ISOM_BOX_TYPE_FRMA: frma_del(a); return; + case GF_ISOM_BOX_TYPE_SCHM: schm_del(a); return; + case GF_ISOM_BOX_TYPE_SCHI: schi_del(a); return; + + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_ENCS: + a->type = ((GF_SampleEntryBox *)a)->protection_info->original_format->data_format; + gf_isom_box_del(a); + return; + case GF_ISOM_BOX_TYPE_UUID: + uuid_del(a); + return; + + /* Apple extensions */ + case GF_ISOM_BOX_TYPE_ILST: ilst_del(a); return; + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_0xA9ENC: + case GF_ISOM_BOX_TYPE_aART: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_COVR: ListItem_del(a); return; + + case GF_ISOM_BOX_TYPE_DATA: data_del(a); return; + + case GF_ISOM_BOX_TYPE_OHDR: ohdr_del(a); return; + case GF_ISOM_BOX_TYPE_GRPI: grpi_del(a); return; + case GF_ISOM_BOX_TYPE_MDRI: mdri_del(a); return; + case GF_ISOM_BOX_TYPE_ODTT: odtt_del(a); return; + case GF_ISOM_BOX_TYPE_ODRB: odrb_del(a); return; + case GF_ISOM_BOX_TYPE_ODKM: odkm_del(a); return; + case GF_ISOM_BOX_TYPE_ODAF: iSFM_del(a); return; + + case GF_ISOM_BOX_TYPE_PASP: pasp_del(a); return; + case GF_ISOM_BOX_TYPE_TSEL: tsel_del(a); return; + + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + metx_del(a); + return; + + case GF_ISOM_BOX_TYPE_DIMS: dims_del(a); return; + case GF_ISOM_BOX_TYPE_DIMC: dimC_del(a); return; + case GF_ISOM_BOX_TYPE_DIST: diST_del(a); return; + + case GF_ISOM_BOX_TYPE_AC3: ac3_del(a); return; + case GF_ISOM_BOX_TYPE_DAC3: dac3_del(a); return; + + default: + defa_del(a); + return; + } +} + + + + +GF_Err gf_isom_box_read(GF_Box *a, GF_BitStream *bs) +{ + switch (a->type) { + case GF_ISOM_BOX_TYPE_REFT: + return reftype_Read(a, bs); + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + return free_Read(a, bs); + case GF_ISOM_BOX_TYPE_MDAT: return mdat_Read(a, bs); + case GF_ISOM_BOX_TYPE_MOOV: return moov_Read(a, bs); + case GF_ISOM_BOX_TYPE_MVHD: return mvhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_MDHD: return mdhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_VMHD: return vmhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_SMHD: return smhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_HMHD: return hmhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + return nmhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_STBL: return stbl_Read(a, bs); + case GF_ISOM_BOX_TYPE_DINF: return dinf_Read(a, bs); + case GF_ISOM_BOX_TYPE_URL: return url_Read(a, bs); + case GF_ISOM_BOX_TYPE_URN: return urn_Read(a, bs); + case GF_ISOM_BOX_TYPE_CPRT: return cprt_Read(a, bs); + case GF_ISOM_BOX_TYPE_HDLR: return hdlr_Read(a, bs); + case GF_ISOM_BOX_TYPE_IODS: return iods_Read(a, bs); + case GF_ISOM_BOX_TYPE_TRAK: return trak_Read(a, bs); + case GF_ISOM_BOX_TYPE_MP4S: return mp4s_Read(a, bs); + case GF_ISOM_BOX_TYPE_MP4V: return mp4v_Read(a, bs); + case GF_ISOM_BOX_TYPE_MP4A: return mp4a_Read(a, bs); + case GF_ISOM_BOX_TYPE_EDTS: return edts_Read(a, bs); + case GF_ISOM_BOX_TYPE_UDTA: return udta_Read(a, bs); + case GF_ISOM_BOX_TYPE_DREF: return dref_Read(a, bs); + case GF_ISOM_BOX_TYPE_STSD: return stsd_Read(a, bs); + case GF_ISOM_BOX_TYPE_STTS: return stts_Read(a, bs); + case GF_ISOM_BOX_TYPE_CTTS: return ctts_Read(a, bs); + case GF_ISOM_BOX_TYPE_STSH: return stsh_Read(a, bs); + case GF_ISOM_BOX_TYPE_ELST: return elst_Read(a, bs); + case GF_ISOM_BOX_TYPE_STSC: return stsc_Read(a, bs); + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + return stsz_Read(a, bs); + case GF_ISOM_BOX_TYPE_STCO: return stco_Read(a, bs); + case GF_ISOM_BOX_TYPE_STSS: return stss_Read(a, bs); + case GF_ISOM_BOX_TYPE_STDP: return stdp_Read(a, bs); + case GF_ISOM_BOX_TYPE_SDTP: return sdtp_Read(a, bs); + case GF_ISOM_BOX_TYPE_CO64: return co64_Read(a, bs); + case GF_ISOM_BOX_TYPE_ESDS: return esds_Read(a, bs); + case GF_ISOM_BOX_TYPE_MINF: return minf_Read(a, bs); + case GF_ISOM_BOX_TYPE_TKHD: return tkhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_TREF: return tref_Read(a, bs); + case GF_ISOM_BOX_TYPE_MDIA: return mdia_Read(a, bs); + case GF_ISOM_BOX_TYPE_CHPL: return chpl_Read(a, bs); + case GF_ISOM_BOX_TYPE_FTYP: return ftyp_Read(a, bs); + case GF_ISOM_BOX_TYPE_FADB: return padb_Read(a, bs); + case GF_ISOM_BOX_TYPE_VOID: return void_Read(a, bs); + case GF_ISOM_BOX_TYPE_STSF: return stsf_Read(a, bs); + case GF_ISOM_BOX_TYPE_PDIN: return pdin_Read(a, bs); + + case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_Read(a, bs); + case GF_ISOM_BOX_TYPE_RTPO: return rtpo_Read(a, bs); + case GF_ISOM_BOX_TYPE_HNTI: return hnti_Read(a, bs); + case GF_ISOM_BOX_TYPE_SDP: return sdp_Read(a, bs); + case GF_ISOM_BOX_TYPE_HINF: return hinf_Read(a, bs); + case GF_ISOM_BOX_TYPE_RELY: return rely_Read(a, bs); + case GF_ISOM_BOX_TYPE_TIMS: return tims_Read(a, bs); + case GF_ISOM_BOX_TYPE_TSRO: return tsro_Read(a, bs); + case GF_ISOM_BOX_TYPE_SNRO: return snro_Read(a, bs); + case GF_ISOM_BOX_TYPE_TRPY: return trpy_Read(a, bs); + case GF_ISOM_BOX_TYPE_NUMP: return nump_Read(a, bs); + case GF_ISOM_BOX_TYPE_TOTL: return totl_Read(a, bs); + case GF_ISOM_BOX_TYPE_NPCK: return npck_Read(a, bs); + case GF_ISOM_BOX_TYPE_TPYL: return tpyl_Read(a, bs); + case GF_ISOM_BOX_TYPE_TPAY: return tpay_Read(a, bs); + case GF_ISOM_BOX_TYPE_MAXR: return maxr_Read(a, bs); + case GF_ISOM_BOX_TYPE_DMED: return dmed_Read(a, bs); + case GF_ISOM_BOX_TYPE_DIMM: return dimm_Read(a, bs); + case GF_ISOM_BOX_TYPE_DREP: return drep_Read(a, bs); + case GF_ISOM_BOX_TYPE_TMIN: return tmin_Read(a, bs); + case GF_ISOM_BOX_TYPE_TMAX: return tmax_Read(a, bs); + case GF_ISOM_BOX_TYPE_PMAX: return pmax_Read(a, bs); + case GF_ISOM_BOX_TYPE_DMAX: return dmax_Read(a, bs); + case GF_ISOM_BOX_TYPE_PAYT: return payt_Read(a, bs); + case GF_ISOM_BOX_TYPE_NAME: return name_Read(a, bs); + +#ifndef GPAC_ISOM_NO_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: return mvex_Read(a, bs); + case GF_ISOM_BOX_TYPE_MEHD: return mehd_Read(a, bs); + case GF_ISOM_BOX_TYPE_TREX: return trex_Read(a, bs); + case GF_ISOM_BOX_TYPE_MOOF: return moof_Read(a, bs); + case GF_ISOM_BOX_TYPE_MFHD: return mfhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_TRAF: return traf_Read(a, bs); + case GF_ISOM_BOX_TYPE_TFHD: return tfhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_TRUN: return trun_Read(a, bs); +#endif + + + /*3GPP boxes*/ + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + return gppa_Read(a, bs); + case GF_ISOM_SUBTYPE_3GP_H263: return gppv_Read(a, bs); + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + return gppc_Read(a, bs); + + case GF_ISOM_BOX_TYPE_AVCC: return avcc_Read(a, bs); + case GF_ISOM_BOX_TYPE_BTRT: return btrt_Read(a, bs); + case GF_ISOM_BOX_TYPE_M4DS: return m4ds_Read(a, bs); + case GF_ISOM_BOX_TYPE_AVC1: return mp4v_Read(a, bs); + + /*3GPP streaming text*/ + case GF_ISOM_BOX_TYPE_FTAB: return ftab_Read(a, bs); + case GF_ISOM_BOX_TYPE_TX3G: return tx3g_Read(a, bs); + case GF_ISOM_BOX_TYPE_STYL: return styl_Read(a, bs); + case GF_ISOM_BOX_TYPE_HLIT: return hlit_Read(a, bs); + case GF_ISOM_BOX_TYPE_HCLR: return hclr_Read(a, bs); + case GF_ISOM_BOX_TYPE_KROK: return krok_Read(a, bs); + case GF_ISOM_BOX_TYPE_DLAY: return dlay_Read(a, bs); + case GF_ISOM_BOX_TYPE_HREF: return href_Read(a, bs); + case GF_ISOM_BOX_TYPE_TBOX: return tbox_Read(a, bs); + case GF_ISOM_BOX_TYPE_BLNK: return blnk_Read(a, bs); + case GF_ISOM_BOX_TYPE_TWRP: return twrp_Read(a, bs); + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: return iKMS_Read(a, bs); + case GF_ISOM_BOX_TYPE_ISFM: return iSFM_Read(a, bs); + + /* ISO FF extensions for MPEG-21 */ + case GF_ISOM_BOX_TYPE_META: return meta_Read(a, bs); + case GF_ISOM_BOX_TYPE_XML: return xml_Read(a, bs); + case GF_ISOM_BOX_TYPE_BXML: return bxml_Read(a, bs); + case GF_ISOM_BOX_TYPE_ILOC: return iloc_Read(a, bs); + case GF_ISOM_BOX_TYPE_PITM: return pitm_Read(a, bs); + case GF_ISOM_BOX_TYPE_IPRO: return ipro_Read(a, bs); + case GF_ISOM_BOX_TYPE_INFE: return infe_Read(a, bs); + case GF_ISOM_BOX_TYPE_IINF: return iinf_Read(a, bs); + case GF_ISOM_BOX_TYPE_SINF: return sinf_Read(a, bs); + case GF_ISOM_BOX_TYPE_FRMA: return frma_Read(a, bs); + case GF_ISOM_BOX_TYPE_SCHM: return schm_Read(a, bs); + case GF_ISOM_BOX_TYPE_SCHI: return schi_Read(a, bs); + case GF_ISOM_BOX_TYPE_ENCA: return mp4a_Read(a, bs); + case GF_ISOM_BOX_TYPE_ENCV: return mp4v_Read(a, bs); + case GF_ISOM_BOX_TYPE_ENCS: return mp4s_Read(a, bs); + case GF_ISOM_BOX_TYPE_UUID: return uuid_Read(a, bs); + + /* Apple extensions */ + case GF_ISOM_BOX_TYPE_ILST: return ilst_Read(a, bs); + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_0xA9ENC: + case GF_ISOM_BOX_TYPE_aART: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_COVR: return ListItem_Read(a, bs); + + case GF_ISOM_BOX_TYPE_DATA: return data_Read(a, bs); + + case GF_ISOM_BOX_TYPE_OHDR: return ohdr_Read(a, bs); + case GF_ISOM_BOX_TYPE_GRPI: return grpi_Read(a, bs); + case GF_ISOM_BOX_TYPE_MDRI: return mdri_Read(a, bs); + case GF_ISOM_BOX_TYPE_ODTT: return odtt_Read(a, bs); + case GF_ISOM_BOX_TYPE_ODRB: return odrb_Read(a, bs); + case GF_ISOM_BOX_TYPE_ODKM: return odkm_Read(a, bs); + case GF_ISOM_BOX_TYPE_ODAF: return iSFM_Read(a, bs); + + case GF_ISOM_BOX_TYPE_PASP: return pasp_Read(a, bs); + case GF_ISOM_BOX_TYPE_TSEL: return tsel_Read(a, bs); + + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + return metx_Read(a, bs); + + case GF_ISOM_BOX_TYPE_DIMS: return dims_Read(a, bs); + case GF_ISOM_BOX_TYPE_DIMC: return dimC_Read(a, bs); + case GF_ISOM_BOX_TYPE_DIST: return diST_Read(a, bs); + + case GF_ISOM_BOX_TYPE_AC3: return ac3_Read(a, bs); + case GF_ISOM_BOX_TYPE_DAC3: return dac3_Read(a, bs); + + default: + return defa_Read(a, bs); + } +} + +/*from here, for write/edit versions*/ +#ifndef GPAC_READ_ONLY + +GF_Err gf_isom_box_write(GF_Box *a, GF_BitStream *bs) +{ + switch (a->type) { + case GF_ISOM_BOX_TYPE_REFT: + return reftype_Write(a, bs); + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + return free_Write(a, bs); + case GF_ISOM_BOX_TYPE_MDAT: return mdat_Write(a, bs); + case GF_ISOM_BOX_TYPE_MOOV: return moov_Write(a, bs); + case GF_ISOM_BOX_TYPE_MVHD: return mvhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_MDHD: return mdhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_VMHD: return vmhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_SMHD: return smhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_HMHD: return hmhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + return nmhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_STBL: return stbl_Write(a, bs); + case GF_ISOM_BOX_TYPE_DINF: return dinf_Write(a, bs); + case GF_ISOM_BOX_TYPE_URL: return url_Write(a, bs); + case GF_ISOM_BOX_TYPE_URN: return urn_Write(a, bs); + case GF_ISOM_BOX_TYPE_CHPL: return chpl_Write(a, bs); + case GF_ISOM_BOX_TYPE_CPRT: return cprt_Write(a, bs); + case GF_ISOM_BOX_TYPE_HDLR: return hdlr_Write(a, bs); + case GF_ISOM_BOX_TYPE_IODS: return iods_Write(a, bs); + case GF_ISOM_BOX_TYPE_TRAK: return trak_Write(a, bs); + case GF_ISOM_BOX_TYPE_MP4S: return mp4s_Write(a, bs); + case GF_ISOM_BOX_TYPE_MP4V: return mp4v_Write(a, bs); + case GF_ISOM_BOX_TYPE_MP4A: return mp4a_Write(a, bs); + case GF_ISOM_BOX_TYPE_GNRM: return gnrm_Write(a, bs); + case GF_ISOM_BOX_TYPE_GNRV: return gnrv_Write(a, bs); + case GF_ISOM_BOX_TYPE_GNRA: return gnra_Write(a, bs); + case GF_ISOM_BOX_TYPE_EDTS: return edts_Write(a, bs); + case GF_ISOM_BOX_TYPE_UDTA: return udta_Write(a, bs); + case GF_ISOM_BOX_TYPE_DREF: return dref_Write(a, bs); + case GF_ISOM_BOX_TYPE_STSD: return stsd_Write(a, bs); + case GF_ISOM_BOX_TYPE_STTS: return stts_Write(a, bs); + case GF_ISOM_BOX_TYPE_CTTS: return ctts_Write(a, bs); + case GF_ISOM_BOX_TYPE_STSH: return stsh_Write(a, bs); + case GF_ISOM_BOX_TYPE_ELST: return elst_Write(a, bs); + case GF_ISOM_BOX_TYPE_STSC: return stsc_Write(a, bs); + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + return stsz_Write(a, bs); + case GF_ISOM_BOX_TYPE_STCO: return stco_Write(a, bs); + case GF_ISOM_BOX_TYPE_STSS: return stss_Write(a, bs); + case GF_ISOM_BOX_TYPE_STDP: return stdp_Write(a, bs); + case GF_ISOM_BOX_TYPE_SDTP: return sdtp_Write(a, bs); + case GF_ISOM_BOX_TYPE_CO64: return co64_Write(a, bs); + case GF_ISOM_BOX_TYPE_ESDS: return esds_Write(a, bs); + case GF_ISOM_BOX_TYPE_MINF: return minf_Write(a, bs); + case GF_ISOM_BOX_TYPE_TKHD: return tkhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_TREF: return tref_Write(a, bs); + case GF_ISOM_BOX_TYPE_MDIA: return mdia_Write(a, bs); + case GF_ISOM_BOX_TYPE_FTYP: return ftyp_Write(a, bs); + case GF_ISOM_BOX_TYPE_FADB: return padb_Write(a, bs); + case GF_ISOM_BOX_TYPE_VOID: return void_Write(a, bs); + case GF_ISOM_BOX_TYPE_STSF: return stsf_Write(a, bs); + case GF_ISOM_BOX_TYPE_PDIN: return pdin_Write(a, bs); + + case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_Write(a, bs); + case GF_ISOM_BOX_TYPE_RTPO: return rtpo_Write(a, bs); + case GF_ISOM_BOX_TYPE_HNTI: return hnti_Write(a, bs); + case GF_ISOM_BOX_TYPE_SDP: return sdp_Write(a, bs); + case GF_ISOM_BOX_TYPE_HINF: return hinf_Write(a, bs); + case GF_ISOM_BOX_TYPE_RELY: return rely_Write(a, bs); + case GF_ISOM_BOX_TYPE_TIMS: return tims_Write(a, bs); + case GF_ISOM_BOX_TYPE_TSRO: return tsro_Write(a, bs); + case GF_ISOM_BOX_TYPE_SNRO: return snro_Write(a, bs); + case GF_ISOM_BOX_TYPE_TRPY: return trpy_Write(a, bs); + case GF_ISOM_BOX_TYPE_NUMP: return nump_Write(a, bs); + case GF_ISOM_BOX_TYPE_TOTL: return totl_Write(a, bs); + case GF_ISOM_BOX_TYPE_NPCK: return npck_Write(a, bs); + case GF_ISOM_BOX_TYPE_TPYL: return tpyl_Write(a, bs); + case GF_ISOM_BOX_TYPE_TPAY: return tpay_Write(a, bs); + case GF_ISOM_BOX_TYPE_MAXR: return maxr_Write(a, bs); + case GF_ISOM_BOX_TYPE_DMED: return dmed_Write(a, bs); + case GF_ISOM_BOX_TYPE_DIMM: return dimm_Write(a, bs); + case GF_ISOM_BOX_TYPE_DREP: return drep_Write(a, bs); + case GF_ISOM_BOX_TYPE_TMIN: return tmin_Write(a, bs); + case GF_ISOM_BOX_TYPE_TMAX: return tmax_Write(a, bs); + case GF_ISOM_BOX_TYPE_PMAX: return pmax_Write(a, bs); + case GF_ISOM_BOX_TYPE_DMAX: return dmax_Write(a, bs); + case GF_ISOM_BOX_TYPE_PAYT: return payt_Write(a, bs); + case GF_ISOM_BOX_TYPE_NAME: return name_Write(a, bs); + +#ifndef GPAC_ISOM_NO_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: return mvex_Write(a, bs); + case GF_ISOM_BOX_TYPE_MEHD: return mehd_Write(a, bs); + case GF_ISOM_BOX_TYPE_TREX: return trex_Write(a, bs); + case GF_ISOM_BOX_TYPE_MOOF: return moof_Write(a, bs); + case GF_ISOM_BOX_TYPE_MFHD: return mfhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_TRAF: return traf_Write(a, bs); + case GF_ISOM_BOX_TYPE_TFHD: return tfhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_TRUN: return trun_Write(a, bs); +#endif + + /*3GPP boxes*/ + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + return gppa_Write(a, bs); + case GF_ISOM_SUBTYPE_3GP_H263: + return gppv_Write(a, bs); + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + return gppc_Write(a, bs); + + case GF_ISOM_BOX_TYPE_AVCC: return avcc_Write(a, bs); + case GF_ISOM_BOX_TYPE_BTRT: return btrt_Write(a, bs); + case GF_ISOM_BOX_TYPE_M4DS: return m4ds_Write(a, bs); + case GF_ISOM_BOX_TYPE_AVC1: return mp4v_Write(a, bs); + + /*3GPP streaming text*/ + case GF_ISOM_BOX_TYPE_FTAB: return ftab_Write(a, bs); + case GF_ISOM_BOX_TYPE_TX3G: return tx3g_Write(a, bs); + case GF_ISOM_BOX_TYPE_STYL: return styl_Write(a, bs); + case GF_ISOM_BOX_TYPE_HLIT: return hlit_Write(a, bs); + case GF_ISOM_BOX_TYPE_HCLR: return hclr_Write(a, bs); + case GF_ISOM_BOX_TYPE_KROK: return krok_Write(a, bs); + case GF_ISOM_BOX_TYPE_DLAY: return dlay_Write(a, bs); + case GF_ISOM_BOX_TYPE_HREF: return href_Write(a, bs); + case GF_ISOM_BOX_TYPE_TBOX: return tbox_Write(a, bs); + case GF_ISOM_BOX_TYPE_BLNK: return blnk_Write(a, bs); + case GF_ISOM_BOX_TYPE_TWRP: return twrp_Write(a, bs); + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: return iKMS_Write(a, bs); + case GF_ISOM_BOX_TYPE_ISFM: return iSFM_Write(a, bs); + + /* ISO FF extensions for MPEG-21 */ + case GF_ISOM_BOX_TYPE_META: return meta_Write(a, bs); + case GF_ISOM_BOX_TYPE_XML: return xml_Write(a, bs); + case GF_ISOM_BOX_TYPE_BXML: return bxml_Write(a, bs); + case GF_ISOM_BOX_TYPE_ILOC: return iloc_Write(a, bs); + case GF_ISOM_BOX_TYPE_PITM: return pitm_Write(a, bs); + case GF_ISOM_BOX_TYPE_IPRO: return ipro_Write(a, bs); + case GF_ISOM_BOX_TYPE_INFE: return infe_Write(a, bs); + case GF_ISOM_BOX_TYPE_IINF: return iinf_Write(a, bs); + case GF_ISOM_BOX_TYPE_SINF: return sinf_Write(a, bs); + case GF_ISOM_BOX_TYPE_FRMA: return frma_Write(a, bs); + case GF_ISOM_BOX_TYPE_SCHM: return schm_Write(a, bs); + case GF_ISOM_BOX_TYPE_SCHI: return schi_Write(a, bs); + case GF_ISOM_BOX_TYPE_ENCA: return mp4a_Write(a, bs); + case GF_ISOM_BOX_TYPE_ENCV: return mp4v_Write(a, bs); + case GF_ISOM_BOX_TYPE_ENCS: return mp4s_Write(a, bs); + case GF_ISOM_BOX_TYPE_UUID: return uuid_Write(a, bs); + + /* Apple extensions */ + case GF_ISOM_BOX_TYPE_ILST: return ilst_Write(a, bs); + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_0xA9ENC: + case GF_ISOM_BOX_TYPE_aART: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_COVR: return ListItem_Write(a, bs); + + case GF_ISOM_BOX_TYPE_DATA: return data_Write(a, bs); + + case GF_ISOM_BOX_TYPE_OHDR: return ohdr_Write(a, bs); + case GF_ISOM_BOX_TYPE_GRPI: return grpi_Write(a, bs); + case GF_ISOM_BOX_TYPE_MDRI: return mdri_Write(a, bs); + case GF_ISOM_BOX_TYPE_ODTT: return odtt_Write(a, bs); + case GF_ISOM_BOX_TYPE_ODRB: return odrb_Write(a, bs); + case GF_ISOM_BOX_TYPE_ODKM: return odkm_Write(a, bs); + case GF_ISOM_BOX_TYPE_ODAF: return iSFM_Write(a, bs); + + case GF_ISOM_BOX_TYPE_PASP: return pasp_Write(a, bs); + case GF_ISOM_BOX_TYPE_TSEL: return tsel_Write(a, bs); + + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + return metx_Write(a, bs); + + case GF_ISOM_BOX_TYPE_DIMS: return dims_Write(a, bs); + case GF_ISOM_BOX_TYPE_DIMC: return dimC_Write(a, bs); + case GF_ISOM_BOX_TYPE_DIST: return diST_Write(a, bs); + + case GF_ISOM_BOX_TYPE_AC3: return ac3_Write(a, bs); + case GF_ISOM_BOX_TYPE_DAC3: return dac3_Write(a, bs); + + default: + return defa_Write(a, bs); + } +} + + +GF_Err gf_isom_box_size(GF_Box *a) +{ + switch (a->type) { + case GF_ISOM_BOX_TYPE_REFT: + return reftype_Size(a); + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + return free_Size(a); + case GF_ISOM_BOX_TYPE_MDAT: return mdat_Size(a); + case GF_ISOM_BOX_TYPE_MOOV: return moov_Size(a); + case GF_ISOM_BOX_TYPE_MVHD: return mvhd_Size(a); + case GF_ISOM_BOX_TYPE_MDHD: return mdhd_Size(a); + case GF_ISOM_BOX_TYPE_VMHD: return vmhd_Size(a); + case GF_ISOM_BOX_TYPE_SMHD: return smhd_Size(a); + case GF_ISOM_BOX_TYPE_HMHD: return hmhd_Size(a); + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + return nmhd_Size(a); + case GF_ISOM_BOX_TYPE_STBL: return stbl_Size(a); + case GF_ISOM_BOX_TYPE_DINF: return dinf_Size(a); + case GF_ISOM_BOX_TYPE_URL: return url_Size(a); + case GF_ISOM_BOX_TYPE_URN: return urn_Size(a); + case GF_ISOM_BOX_TYPE_CHPL: return chpl_Size(a); + case GF_ISOM_BOX_TYPE_CPRT: return cprt_Size(a); + case GF_ISOM_BOX_TYPE_HDLR: return hdlr_Size(a); + case GF_ISOM_BOX_TYPE_IODS: return iods_Size(a); + case GF_ISOM_BOX_TYPE_TRAK: return trak_Size(a); + case GF_ISOM_BOX_TYPE_MP4S: return mp4s_Size(a); + case GF_ISOM_BOX_TYPE_MP4V: return mp4v_Size(a); + case GF_ISOM_BOX_TYPE_MP4A: return mp4a_Size(a); + case GF_ISOM_BOX_TYPE_GNRM: return gnrm_Size(a); + case GF_ISOM_BOX_TYPE_GNRV: return gnrv_Size(a); + case GF_ISOM_BOX_TYPE_GNRA: return gnra_Size(a); + case GF_ISOM_BOX_TYPE_EDTS: return edts_Size(a); + case GF_ISOM_BOX_TYPE_UDTA: return udta_Size(a); + case GF_ISOM_BOX_TYPE_DREF: return dref_Size(a); + case GF_ISOM_BOX_TYPE_STSD: return stsd_Size(a); + case GF_ISOM_BOX_TYPE_STTS: return stts_Size(a); + case GF_ISOM_BOX_TYPE_CTTS: return ctts_Size(a); + case GF_ISOM_BOX_TYPE_STSH: return stsh_Size(a); + case GF_ISOM_BOX_TYPE_ELST: return elst_Size(a); + case GF_ISOM_BOX_TYPE_STSC: return stsc_Size(a); + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + return stsz_Size(a); + case GF_ISOM_BOX_TYPE_STCO: return stco_Size(a); + case GF_ISOM_BOX_TYPE_STSS: return stss_Size(a); + case GF_ISOM_BOX_TYPE_SDTP: return sdtp_Size(a); + case GF_ISOM_BOX_TYPE_CO64: return co64_Size(a); + case GF_ISOM_BOX_TYPE_ESDS: return esds_Size(a); + case GF_ISOM_BOX_TYPE_MINF: return minf_Size(a); + case GF_ISOM_BOX_TYPE_TKHD: return tkhd_Size(a); + case GF_ISOM_BOX_TYPE_TREF: return tref_Size(a); + case GF_ISOM_BOX_TYPE_MDIA: return mdia_Size(a); + case GF_ISOM_BOX_TYPE_FTYP: return ftyp_Size(a); + case GF_ISOM_BOX_TYPE_FADB: return padb_Size(a); + case GF_ISOM_BOX_TYPE_VOID: return void_Size(a); + case GF_ISOM_BOX_TYPE_STSF: return stsf_Size(a); + case GF_ISOM_BOX_TYPE_PDIN: return pdin_Size(a); + + case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_Size(a); + case GF_ISOM_BOX_TYPE_RTPO: return rtpo_Size(a); + case GF_ISOM_BOX_TYPE_HNTI: return hnti_Size(a); + case GF_ISOM_BOX_TYPE_SDP: return sdp_Size(a); + case GF_ISOM_BOX_TYPE_HINF: return hinf_Size(a); + case GF_ISOM_BOX_TYPE_RELY: return rely_Size(a); + case GF_ISOM_BOX_TYPE_TIMS: return tims_Size(a); + case GF_ISOM_BOX_TYPE_TSRO: return tsro_Size(a); + case GF_ISOM_BOX_TYPE_SNRO: return snro_Size(a); + case GF_ISOM_BOX_TYPE_TRPY: return trpy_Size(a); + case GF_ISOM_BOX_TYPE_NUMP: return nump_Size(a); + case GF_ISOM_BOX_TYPE_TOTL: return totl_Size(a); + case GF_ISOM_BOX_TYPE_NPCK: return npck_Size(a); + case GF_ISOM_BOX_TYPE_TPYL: return tpyl_Size(a); + case GF_ISOM_BOX_TYPE_TPAY: return tpay_Size(a); + case GF_ISOM_BOX_TYPE_MAXR: return maxr_Size(a); + case GF_ISOM_BOX_TYPE_DMED: return dmed_Size(a); + case GF_ISOM_BOX_TYPE_DIMM: return dimm_Size(a); + case GF_ISOM_BOX_TYPE_DREP: return drep_Size(a); + case GF_ISOM_BOX_TYPE_TMIN: return tmin_Size(a); + case GF_ISOM_BOX_TYPE_TMAX: return tmax_Size(a); + case GF_ISOM_BOX_TYPE_PMAX: return pmax_Size(a); + case GF_ISOM_BOX_TYPE_DMAX: return dmax_Size(a); + case GF_ISOM_BOX_TYPE_PAYT: return payt_Size(a); + case GF_ISOM_BOX_TYPE_NAME: return name_Size(a); + +#ifndef GPAC_ISOM_NO_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: return mvex_Size(a); + case GF_ISOM_BOX_TYPE_MEHD: return mehd_Size(a); + case GF_ISOM_BOX_TYPE_TREX: return trex_Size(a); + case GF_ISOM_BOX_TYPE_MOOF: return moof_Size(a); + case GF_ISOM_BOX_TYPE_MFHD: return mfhd_Size(a); + case GF_ISOM_BOX_TYPE_TRAF: return traf_Size(a); + case GF_ISOM_BOX_TYPE_TFHD: return tfhd_Size(a); + case GF_ISOM_BOX_TYPE_TRUN: return trun_Size(a); +#endif + + /*3GPP boxes*/ + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + return gppa_Size(a); + case GF_ISOM_SUBTYPE_3GP_H263: return gppv_Size(a); + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + return gppc_Size(a); + + case GF_ISOM_BOX_TYPE_AVCC: return avcc_Size(a); + case GF_ISOM_BOX_TYPE_BTRT: return btrt_Size(a); + case GF_ISOM_BOX_TYPE_M4DS: return m4ds_Size(a); + case GF_ISOM_BOX_TYPE_AVC1: return mp4v_Size(a); + + /*3GPP streaming text*/ + case GF_ISOM_BOX_TYPE_FTAB: return ftab_Size(a); + case GF_ISOM_BOX_TYPE_TX3G: return tx3g_Size(a); + case GF_ISOM_BOX_TYPE_STYL: return styl_Size(a); + case GF_ISOM_BOX_TYPE_HLIT: return hlit_Size(a); + case GF_ISOM_BOX_TYPE_HCLR: return hclr_Size(a); + case GF_ISOM_BOX_TYPE_KROK: return krok_Size(a); + case GF_ISOM_BOX_TYPE_DLAY: return dlay_Size(a); + case GF_ISOM_BOX_TYPE_HREF: return href_Size(a); + case GF_ISOM_BOX_TYPE_TBOX: return tbox_Size(a); + case GF_ISOM_BOX_TYPE_BLNK: return blnk_Size(a); + case GF_ISOM_BOX_TYPE_TWRP: return twrp_Size(a); + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: return iKMS_Size(a); + case GF_ISOM_BOX_TYPE_ISFM: return iSFM_Size(a); + + /* ISO FF extensions for MPEG-21 */ + case GF_ISOM_BOX_TYPE_META: return meta_Size(a); + case GF_ISOM_BOX_TYPE_XML: return xml_Size(a); + case GF_ISOM_BOX_TYPE_BXML: return bxml_Size(a); + case GF_ISOM_BOX_TYPE_ILOC: return iloc_Size(a); + case GF_ISOM_BOX_TYPE_PITM: return pitm_Size(a); + case GF_ISOM_BOX_TYPE_IPRO: return ipro_Size(a); + case GF_ISOM_BOX_TYPE_INFE: return infe_Size(a); + case GF_ISOM_BOX_TYPE_IINF: return iinf_Size(a); + case GF_ISOM_BOX_TYPE_SINF: return sinf_Size(a); + case GF_ISOM_BOX_TYPE_FRMA: return frma_Size(a); + case GF_ISOM_BOX_TYPE_SCHM: return schm_Size(a); + case GF_ISOM_BOX_TYPE_SCHI: return schi_Size(a); + case GF_ISOM_BOX_TYPE_ENCA: return mp4a_Size(a); + case GF_ISOM_BOX_TYPE_ENCV: return mp4v_Size(a); + case GF_ISOM_BOX_TYPE_ENCS: return mp4s_Size(a); + case GF_ISOM_BOX_TYPE_UUID: return uuid_Size(a); + + /* Apple extensions */ + case GF_ISOM_BOX_TYPE_ILST: return ilst_Size(a); + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_0xA9ENC: + case GF_ISOM_BOX_TYPE_aART: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_COVR: return ListItem_Size(a); + + case GF_ISOM_BOX_TYPE_DATA: return data_Size(a); + + case GF_ISOM_BOX_TYPE_OHDR: return ohdr_Size(a); + case GF_ISOM_BOX_TYPE_GRPI: return grpi_Size(a); + case GF_ISOM_BOX_TYPE_MDRI: return mdri_Size(a); + case GF_ISOM_BOX_TYPE_ODTT: return odtt_Size(a); + case GF_ISOM_BOX_TYPE_ODRB: return odrb_Size(a); + case GF_ISOM_BOX_TYPE_ODKM: return odkm_Size(a); + case GF_ISOM_BOX_TYPE_ODAF: return iSFM_Size(a); + + case GF_ISOM_BOX_TYPE_PASP: return pasp_Size(a); + case GF_ISOM_BOX_TYPE_TSEL: return tsel_Size(a); + + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + return metx_Size(a); + + case GF_ISOM_BOX_TYPE_DIMS: return dims_Size(a); + case GF_ISOM_BOX_TYPE_DIMC: return dimC_Size(a); + case GF_ISOM_BOX_TYPE_DIST: return diST_Size(a); + + case GF_ISOM_BOX_TYPE_AC3: return ac3_Size(a); + case GF_ISOM_BOX_TYPE_DAC3: return dac3_Size(a); + + default: return defa_Size(a); + } +} + +#endif + diff --git a/src/isomedia/data_map.c b/src/isomedia/data_map.c new file mode 100644 index 0000000..04433f6 --- /dev/null +++ b/src/isomedia/data_map.c @@ -0,0 +1,562 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +void gf_isom_datamap_del(GF_DataMap *ptr) +{ + if (!ptr) return; + + //then delete the structure itself.... + switch (ptr->type) { + //file-based + case GF_ISOM_DATA_FILE: + gf_isom_fdm_del((GF_FileDataMap *)ptr); + break; + case GF_ISOM_DATA_FILE_MAPPING: + gf_isom_fmo_del((GF_FileMappingDataMap *)ptr); + break; + //not implemented + default: + break; + } +} + +//Close a data entry +void gf_isom_datamap_close(GF_MediaInformationBox *minf) +{ + GF_DataEntryBox *ent; + if (!minf || !minf->dataHandler) return; + + ent = (GF_DataEntryBox*)gf_list_get(minf->dataInformation->dref->boxList, minf->dataEntryIndex - 1); + + //if ent NULL, the data entry was not used (should never happen) + if (ent == NULL) return; + + //self contained, do nothing + switch (ent->type) { + case GF_ISOM_BOX_TYPE_URL: + case GF_ISOM_BOX_TYPE_URN: + if (ent->flags == 1) return; + break; + default: + return; + } + + //finally close it + gf_isom_datamap_del(minf->dataHandler); + minf->dataHandler = NULL; +} + +/*cf below, we disable filedatamap since it tricks mem usage on w32*/ +#if 0 +static Bool IsLargeFile(char *path) +{ +#ifndef _WIN32_WCE + FILE *stream; + s64 size; + stream = gf_f64_open(path, "rb"); + if (!stream) return 0; + gf_f64_seek(stream, 0, SEEK_END); + size = gf_f64_tell(stream); + fclose(stream); + if (size == -1L) return 0; + if (size > 0xFFFFFFFF) return 1; +#endif + return 0; +} +#endif + + +//Special constructor, we need some error feedback... + +GF_Err gf_isom_datamap_new(const char *location, const char *parentPath, u8 mode, GF_DataMap **outDataMap) +{ + Bool extern_file; + char *sPath; + *outDataMap = NULL; + + //if nothing specified, this is a MEMORY data map + if (!location) { + //not supported yet + return GF_NOT_SUPPORTED; + } + //we need a temp file ... + if (!strcmp(location, "mp4_tmp_edit")) { +#ifndef GPAC_READ_ONLY + *outDataMap = gf_isom_fdm_new_temp(parentPath); + if (! (*outDataMap)) return GF_IO_ERR; + return GF_OK; +#else + return GF_NOT_SUPPORTED; +#endif + } + + extern_file = !gf_url_is_local(location); + + if (mode == GF_ISOM_DATA_MAP_EDIT) { + //we need a local file for edition!!! + if (extern_file) return GF_ISOM_INVALID_MODE; + //OK, switch back to READ mode + mode = GF_ISOM_DATA_MAP_READ; + } + + //TEMP: however, only support for file right now (we'd have to add some callback functions at some point) + if (extern_file) return GF_NOT_SUPPORTED; + + sPath = gf_url_get_absolute_path(location, parentPath); + if (sPath == NULL) return GF_URL_ERROR; + + if (mode == GF_ISOM_DATA_MAP_READ_ONLY) { + mode = GF_ISOM_DATA_MAP_READ; + /*It seems win32 file mapping is reported in prog mem usage -> large increases of occupancy. Should not be a pb + but unless you want mapping, only regular IO will be used...*/ +#if 0 + if (IsLargeFile(sPath)) { + *outDataMap = gf_isom_fdm_new(sPath, mode); + } else { + *outDataMap = gf_isom_fmo_new(sPath, mode); + } +#else + *outDataMap = gf_isom_fdm_new(sPath, mode); +#endif + } else { + *outDataMap = gf_isom_fdm_new(sPath, mode); + } + + free(sPath); + if (! (*outDataMap)) return GF_URL_ERROR; + return GF_OK; +} + +//Open a data entry of a track +//Edit is used to switch between original and edition file +GF_Err gf_isom_datamap_open(GF_MediaBox *mdia, u32 dataRefIndex, u8 Edit) +{ + GF_DataEntryBox *ent; + GF_MediaInformationBox *minf; + u32 SelfCont; + GF_Err e = GF_OK; + if ((mdia == NULL) || (! mdia->information) || !dataRefIndex) + return GF_ISOM_INVALID_MEDIA; + + minf = mdia->information; + + if (dataRefIndex > gf_list_count(minf->dataInformation->dref->boxList)) + return GF_BAD_PARAM; + + ent = (GF_DataEntryBox*)gf_list_get(minf->dataInformation->dref->boxList, dataRefIndex - 1); + if (ent == NULL) return GF_ISOM_INVALID_MEDIA; + + //if the current dataEntry is the desired one, and not self contained, return + if ((minf->dataEntryIndex == dataRefIndex) && (ent->flags != 1)) { + return GF_OK; + } + + //we need to open a new one + //first close the existing one + if (minf->dataHandler) gf_isom_datamap_close(minf); + + SelfCont = 0; + switch (ent->type) { + case GF_ISOM_BOX_TYPE_URL: + case GF_ISOM_BOX_TYPE_URN: + if (ent->flags == 1) SelfCont = 1; + break; + default: + SelfCont = 1; + break; + } + //if self-contained, assign the input file + if (SelfCont) { + //if no edit, open the input file + if (!Edit) { + if (mdia->mediaTrack->moov->mov->movieFileMap == NULL) return GF_ISOM_INVALID_FILE; + minf->dataHandler = mdia->mediaTrack->moov->mov->movieFileMap; + } else { +#ifndef GPAC_READ_ONLY + if (mdia->mediaTrack->moov->mov->editFileMap == NULL) return GF_ISOM_INVALID_FILE; + minf->dataHandler = mdia->mediaTrack->moov->mov->editFileMap; +#else + //this should never be the case in an read-only MP4 file + return GF_BAD_PARAM; +#endif + } + //else this is a URL (read mode only) + } else { + e = gf_isom_datamap_new(ent->location, mdia->mediaTrack->moov->mov->fileName, GF_ISOM_DATA_MAP_READ, & mdia->information->dataHandler); + if (e) return (e==GF_URL_ERROR) ? GF_ISOM_UNKNOWN_DATA_REF : e; + } + //OK, set the data entry index + minf->dataEntryIndex = dataRefIndex; + return GF_OK; +} + +//return the NB of bytes actually read (used for HTTP, ...) in case file is uncomplete +u32 gf_isom_datamap_get_data(GF_DataMap *map, char *buffer, u32 bufferLength, u64 Offset) +{ + if (!map || !buffer) return 0; + + switch (map->type) { + case GF_ISOM_DATA_FILE: + return gf_isom_fdm_get_data((GF_FileDataMap *)map, buffer, bufferLength, Offset); + + case GF_ISOM_DATA_FILE_MAPPING: + return gf_isom_fmo_get_data((GF_FileMappingDataMap *)map, buffer, bufferLength, Offset); + + default: + return 0; + } +} + + +#ifndef GPAC_READ_ONLY + +u64 FDM_GetTotalOffset(GF_FileDataMap *ptr); +GF_Err FDM_AddData(GF_FileDataMap *ptr, char *data, u32 dataSize); + +u64 gf_isom_datamap_get_offset(GF_DataMap *map) +{ + if (!map) return 0; + + switch (map->type) { + case GF_ISOM_DATA_FILE: + return FDM_GetTotalOffset((GF_FileDataMap *)map); + + default: + return 0; + } +} + + +GF_Err gf_isom_datamap_add_data(GF_DataMap *ptr, char *data, u32 dataSize) +{ + if (!ptr || !data|| !dataSize) return GF_BAD_PARAM; + + switch (ptr->type) { + case GF_ISOM_DATA_FILE: + return FDM_AddData((GF_FileDataMap *)ptr, data, dataSize); + default: + return GF_NOT_SUPPORTED; + } +} + +#endif + + +#ifndef GPAC_READ_ONLY +GF_DataMap *gf_isom_fdm_new_temp(const char *sPath) +{ + GF_FileDataMap *tmp = (GF_FileDataMap *) malloc(sizeof(GF_FileDataMap)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_FileDataMap)); + tmp->type = GF_ISOM_DATA_FILE; + tmp->mode = GF_ISOM_DATA_MAP_WRITE; + + if (!sPath) { + tmp->stream = gf_temp_file_new(); + } else { + char szPath[GF_MAX_PATH]; + if ((sPath[strlen(sPath)-1] != '\\') && (sPath[strlen(sPath)-1] != '/')) { + sprintf(szPath, "%s%c%d_isotmp", sPath, GF_PATH_SEPARATOR, (u32) tmp); + } else { + sprintf(szPath, "%s%d_isotmp", sPath, (u32) tmp); + } + tmp->stream = gf_f64_open(szPath, "w+b"); + tmp->temp_file = strdup(szPath); + } + if (!tmp->stream) { + if (tmp->temp_file) free(tmp->temp_file); + free(tmp); + return NULL; + } + tmp->bs = gf_bs_from_file(tmp->stream, GF_BITSTREAM_READ); + if (!tmp->bs) { + fclose(tmp->stream); + free(tmp); + return NULL; + } + return (GF_DataMap *)tmp; +} + +#endif + +GF_DataMap *gf_isom_fdm_new(const char *sPath, u8 mode) +{ + u8 bs_mode; + + GF_FileDataMap *tmp = (GF_FileDataMap *) malloc(sizeof(GF_FileDataMap)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_FileDataMap)); + tmp->type = GF_ISOM_DATA_FILE; + tmp->mode = mode; + +#ifndef GPAC_READ_ONLY + //open a temp file + if (!strcmp(sPath, "mp4_tmp_edit")) { + //create a temp file (that only occurs in EDIT/WRITE mode) + tmp->stream = gf_temp_file_new(); + bs_mode = GF_BITSTREAM_READ; + } +#endif + + switch (mode) { + case GF_ISOM_DATA_MAP_READ: + if (!tmp->stream) tmp->stream = gf_f64_open(sPath, "rb"); + bs_mode = GF_BITSTREAM_READ; + break; + ///we open the file in READ/WRITE mode, in case + case GF_ISOM_DATA_MAP_WRITE: + if (!tmp->stream) tmp->stream = gf_f64_open(sPath, "w+b"); + if (!tmp->stream) tmp->stream = gf_f64_open(sPath, "wb"); + bs_mode = GF_BITSTREAM_WRITE; + break; + default: + free(tmp); + return NULL; + } + if (!tmp->stream) { + free(tmp); + return NULL; + } + tmp->bs = gf_bs_from_file(tmp->stream, bs_mode); + if (!tmp->bs) { + fclose(tmp->stream); + free(tmp); + return NULL; + } + return (GF_DataMap *)tmp; +} + +void gf_isom_fdm_del(GF_FileDataMap *ptr) +{ + if (!ptr || (ptr->type != GF_ISOM_DATA_FILE)) return; + if (ptr->bs) gf_bs_del(ptr->bs); + if (ptr->stream) fclose(ptr->stream); + +#ifndef GPAC_READ_ONLY + if (ptr->temp_file) { + gf_delete_file(ptr->temp_file); + free(ptr->temp_file); + } +#endif + free(ptr); +} + + + +u32 gf_isom_fdm_get_data(GF_FileDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset) +{ + u32 bytesRead; + + //can we seek till that point ??? + if (fileOffset > gf_bs_get_size(ptr->bs)) return 0; + + //ouch, we are not at the previous location, do a seek + if (ptr->curPos != fileOffset) { + if (gf_bs_seek(ptr->bs, fileOffset) != GF_OK) return 0; + ptr->curPos = fileOffset; + } + //read our data. + bytesRead = gf_bs_read_data(ptr->bs, buffer, bufferLength); + //update our cache + if (bytesRead == bufferLength) { + ptr->curPos += bytesRead; + } else { + //rewind to original (if seek fails, return 0 cause this means: + //1- no support for seek on the platform + //2- corrupted file for the OS + fflush(ptr->stream); + gf_bs_seek(ptr->bs, ptr->curPos); + } + ptr->last_acces_was_read = 1; + return bytesRead; +} + + + +#ifndef GPAC_READ_ONLY + + +u64 FDM_GetTotalOffset(GF_FileDataMap *ptr) +{ + if (!ptr) return 0; + //the pos is not always at the end + //this function is called to set up the chunks + //so we need the next WRITE offset + return gf_bs_get_size(ptr->bs); +} + + + +GF_Err FDM_AddData(GF_FileDataMap *ptr, char *data, u32 dataSize) +{ + u32 ret; + u64 orig; + if (ptr->mode == GF_ISOM_DATA_MAP_READ) return GF_BAD_PARAM; + + orig = gf_bs_get_size(ptr->bs); + + /*last access was read, seek to end of file*/ + if (ptr->last_acces_was_read) { + gf_bs_seek(ptr->bs, orig); + ptr->last_acces_was_read = 0; + } + //OK, write our stuff to the datamap... + //we don't use bs here cause we want to know more about what has been written + ret = gf_bs_write_data(ptr->bs, data, dataSize); + if (ret != dataSize) { + ptr->curPos = orig; + gf_bs_seek(ptr->bs, orig); + return GF_IO_ERR; + } + ptr->curPos = gf_bs_get_position(ptr->bs); + //flush the stream !! + fflush(ptr->stream); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + +#ifdef WIN32 + +#include +#include + +GF_DataMap *gf_isom_fmo_new(const char *sPath, u8 mode) +{ + GF_FileMappingDataMap *tmp; + HANDLE fileH, fileMapH; + DWORD err; +#ifdef _WIN32_WCE + unsigned short sWPath[MAX_PATH]; +#endif + + //only in read only + if (mode != GF_ISOM_DATA_MAP_READ) return NULL; + + tmp = (GF_FileMappingDataMap *) malloc(sizeof(GF_FileMappingDataMap)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_FileMappingDataMap)); + tmp->type = GF_ISOM_DATA_FILE_MAPPING; + tmp->mode = mode; + tmp->name = _strdup(sPath); + + // + // Open the file + // +#ifdef _WIN32_WCE + //convert to WIDE + CE_CharToWide((char *)sPath, sWPath); + + fileH = CreateFileForMapping(sWPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + (FILE_ATTRIBUTE_READONLY | FILE_FLAG_RANDOM_ACCESS), NULL ); +#else + fileH = CreateFile(sPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + (FILE_ATTRIBUTE_READONLY | FILE_FLAG_RANDOM_ACCESS), NULL ); +#endif + + + if (fileH == INVALID_HANDLE_VALUE) { + free(tmp->name); + free(tmp); + return NULL; + } + + tmp->file_size = GetFileSize(fileH, NULL); + if (tmp->file_size == 0xFFFFFFFF) { + CloseHandle(fileH); + free(tmp->name); + free(tmp); + return NULL; + } + + // + // Create the mapping + // + fileMapH = CreateFileMapping(fileH, NULL, PAGE_READONLY, 0, 0, NULL); + if (fileMapH == NULL) { + CloseHandle(fileH); + free(tmp->name); + free(tmp); + err = GetLastError(); + return NULL; + } + + tmp->byte_map = MapViewOfFile(fileMapH, FILE_MAP_READ, 0, 0, 0); + if (tmp->byte_map == NULL) { + CloseHandle(fileMapH); + CloseHandle(fileH); + free(tmp->name); + free(tmp); + return NULL; + } + + CloseHandle(fileH); + CloseHandle(fileMapH); + + //finaly open our bitstream (from buffer) + tmp->bs = gf_bs_new(tmp->byte_map, tmp->file_size, GF_BITSTREAM_READ); + return (GF_DataMap *)tmp; +} + +void gf_isom_fmo_del(GF_FileMappingDataMap *ptr) +{ + if (!ptr || (ptr->type != GF_ISOM_DATA_FILE_MAPPING)) return; + + if (ptr->bs) gf_bs_del(ptr->bs); + if (ptr->byte_map) UnmapViewOfFile(ptr->byte_map); + free(ptr->name); + free(ptr); +} + + +u32 gf_isom_fmo_get_data(GF_FileMappingDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset) +{ + u32 size; + + //can we seek till that point ??? + if (fileOffset > ptr->file_size) return 0; + size = (u32) fileOffset; + + //we do only read operations, so trivial + memcpy(buffer, ptr->byte_map + fileOffset, bufferLength); + return bufferLength; +} + +#else + +GF_DataMap *gf_isom_fmo_new(const char *sPath, u8 mode) { return gf_isom_fdm_new(sPath, mode); } +void gf_isom_fmo_del(GF_FileMappingDataMap *ptr) { gf_isom_fdm_del((GF_FileDataMap *)ptr); } +u32 gf_isom_fmo_get_data(GF_FileMappingDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset) +{ + return gf_isom_fdm_get_data((GF_FileDataMap *)ptr, buffer, bufferLength, fileOffset); +} + +#endif + + + diff --git a/src/isomedia/hint_track.c b/src/isomedia/hint_track.c new file mode 100644 index 0000000..6266b2c --- /dev/null +++ b/src/isomedia/hint_track.c @@ -0,0 +1,989 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +Bool IsHintTrack(GF_TrackBox *trak) +{ + if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_HINT) return 0; + //QT doesn't specify any InfoHeader on HintTracks + if (trak->Media->information->InfoHeader + && trak->Media->information->InfoHeader->type != GF_ISOM_BOX_TYPE_HMHD) + return 0; + + return 1; +} + +u32 GetHintFormat(GF_TrackBox *trak) +{ + GF_HintMediaHeaderBox *hmhd = (GF_HintMediaHeaderBox *)trak->Media->information->InfoHeader; + if (!hmhd->subType) { + GF_Box *a = (GF_Box *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, 0); + if (a) hmhd->subType = a->type; + } + return hmhd->subType; +} + +Bool CheckHintFormat(GF_TrackBox *trak, u32 HintType) +{ + if (!IsHintTrack(trak)) return 0; + if (GetHintFormat(trak) != HintType) return 0; + return 1; +} + +#ifndef GPAC_READ_ONLY + +GF_Err AdjustHintInfo(GF_HintSampleEntryBox *entry, u32 HintSampleNumber) +{ + u32 offset, count, i, size; + GF_HintPacket *pck; + GF_Err e; + + offset = gf_isom_hint_sample_size(entry->hint_sample) - entry->hint_sample->dataLength; + count = gf_list_count(entry->hint_sample->packetTable); + + for (i=0; ihint_sample->packetTable, i); + if (offset && entry->hint_sample->dataLength) { + //adjust any offset in this packet + e = gf_isom_hint_pck_offset(entry->hint_sample->HintType, pck, offset, HintSampleNumber); + if (e) return e; + } + //adjust the max packet size for this sample entry... + size = gf_isom_hint_pck_length(entry->hint_sample->HintType, pck); + if (entry->MaxPacketSize < size) entry->MaxPacketSize = size; + } + return GF_OK; +} + + +GF_Err gf_isom_setup_hint_track(GF_ISOFile *movie, u32 trackNumber, u32 HintType) +{ + GF_Err e; + GF_TrackBox *trak; + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *dpnd; + GF_HintMediaHeaderBox *hmhd; + //UDTA related ... + GF_UserDataBox *udta; + + + //what do we support + switch (HintType) { + case GF_ISOM_HINT_RTP: + break; + default: + return GF_NOT_SUPPORTED; + } + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return gf_isom_last_error(movie); + + //check we have a hint ... + if ( !IsHintTrack(trak)) { + return GF_BAD_PARAM; + } + hmhd = (GF_HintMediaHeaderBox *)trak->Media->information->InfoHeader; + //make sure the subtype was not already defined + if (hmhd->subType) return GF_BAD_PARAM; + //store the HintTrack format for later use... + hmhd->subType = HintType; + + + //hint tracks always have a tref and everything ... + if (!trak->References) { + if (!trak->References) { + tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); + e = trak_AddBox((GF_Box*)trak, (GF_Box *)tref); + if (e) return e; + } + } + tref = trak->References; + + //do we have a hint reference on this trak ??? + e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_HINT, &dpnd); + if (e) return e; + //if yes, return false (existing hint track...) + if (dpnd) return GF_BAD_PARAM; + + //create our dep + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = GF_ISOM_BOX_TYPE_HINT; + e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); + if (e) return e; + + //for RTP, we need to do some UDTA-related stuff... + if (HintType != GF_ISOM_HINT_RTP) return GF_OK; + + if (!trak->udta) { + //create one + udta = (GF_UserDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA); + e = trak_AddBox((GF_Box*)trak, (GF_Box *) udta); + if (e) return e; + } + udta = trak->udta; + + //HNTI + e = udta_AddBox(udta, gf_isom_box_new(GF_ISOM_BOX_TYPE_HNTI)); + if (e) return e; + +/* + //NAME + e = udta_AddBox(udta, gf_isom_box_new(GF_ISOM_BOX_TYPE_NAME)); + if (e) return e; + //HINF + return udta_AddBox(udta, gf_isom_box_new(GF_ISOM_BOX_TYPE_HINF)); +*/ + return GF_OK; +} + +//to use with internally supported protocols +GF_Err gf_isom_new_hint_description(GF_ISOFile *the_file, u32 trackNumber, s32 HintTrackVersion, s32 LastCompatibleVersion, u8 Rely, u32 *HintDescriptionIndex) +{ + GF_Err e; + u32 drefIndex; + GF_TrackBox *trak; + GF_HintSampleEntryBox *hdesc; + GF_RelyHintBox *relyA; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + *HintDescriptionIndex = 0; + if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM; + + //OK, create a new HintSampleDesc + hdesc = (GF_HintSampleEntryBox *) gf_isom_box_new(GetHintFormat(trak)); + + if (HintTrackVersion > 0) hdesc->HintTrackVersion = HintTrackVersion; + if (LastCompatibleVersion > 0) hdesc->LastCompatibleVersion = LastCompatibleVersion; + + //create a data reference - WE ONLY DEAL WITH SELF-CONTAINED HINT TRACKS + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, NULL, NULL, &drefIndex); + if (e) return e; + hdesc->dataReferenceIndex = drefIndex; + + //add the entry to our table... + e = stsd_AddBox(trak->Media->information->sampleTable->SampleDescription, (GF_Box *) hdesc); + if (e) return e; + *HintDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + + //RTP needs a default timeScale... use the media one. + if (CheckHintFormat(trak, GF_ISOM_HINT_RTP)) { + e = gf_isom_rtp_set_timescale(the_file, trackNumber, *HintDescriptionIndex, trak->Media->mediaHeader->timeScale); + if (e) return e; + } + if (!Rely) return GF_OK; + + //we need a rely box (common to all protocols) + relyA = (GF_RelyHintBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_RELY); + if (Rely == 1) { + relyA->prefered = 1; + } else { + relyA->required = 1; + } + return gf_list_add(hdesc->HintDataTable, relyA); +} + + +/******************************************************************* + RTP WRITING API +*******************************************************************/ + +//sets the RTP TimeScale +GF_Err gf_isom_rtp_set_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 TimeScale) +{ + GF_TrackBox *trak; + GF_HintSampleEntryBox *hdesc; + u32 i, count; + GF_TSHintEntryBox *ent; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; + + //OK, create a new HintSampleDesc + hdesc = (GF_HintSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, HintDescriptionIndex - 1); + count = gf_list_count(hdesc->HintDataTable); + + for (i=0; i< count; i++) { + ent = (GF_TSHintEntryBox *)gf_list_get(hdesc->HintDataTable, i); + if (ent->type == GF_ISOM_BOX_TYPE_TIMS) { + ent->timeScale = TimeScale; + return GF_OK; + } + } + //we have to create a new entry... + ent = (GF_TSHintEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TIMS); + ent->timeScale = TimeScale; + return gf_list_add(hdesc->HintDataTable, ent); +} + +//sets the RTP TimeOffset that the server will add to the packets +//if not set, the server adds a random offset +GF_Err gf_isom_rtp_set_time_offset(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 TimeOffset) +{ + GF_TrackBox *trak; + GF_HintSampleEntryBox *hdesc; + u32 i, count; + GF_TimeOffHintEntryBox *ent; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; + + //OK, create a new HintSampleDesc + hdesc = (GF_HintSampleEntryBox *) gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, HintDescriptionIndex - 1); + count = gf_list_count(hdesc->HintDataTable); + + for (i=0; i< count; i++) { + ent = (GF_TimeOffHintEntryBox *)gf_list_get(hdesc->HintDataTable, i); + if (ent->type == GF_ISOM_BOX_TYPE_TSRO) { + ent->TimeOffset = TimeOffset; + return GF_OK; + } + } + //we have to create a new entry... + ent = (GF_TimeOffHintEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TSRO); + ent->TimeOffset = TimeOffset; + return gf_list_add(hdesc->HintDataTable, ent); +} + +//sets the RTP SequenceNumber Offset that the server will add to the packets +//if not set, the server adds a random offset +GF_Err gf_isom_rtp_set_time_sequence_offset(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 SequenceNumberOffset) +{ + GF_TrackBox *trak; + GF_HintSampleEntryBox *hdesc; + u32 i, count; + GF_SeqOffHintEntryBox *ent; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; + + //OK, create a new HintSampleDesc + hdesc = (GF_HintSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, HintDescriptionIndex - 1); + count = gf_list_count(hdesc->HintDataTable); + + for (i=0; i< count; i++) { + ent = (GF_SeqOffHintEntryBox *)gf_list_get(hdesc->HintDataTable, i); + if (ent->type == GF_ISOM_BOX_TYPE_SNRO) { + ent->SeqOffset = SequenceNumberOffset; + return GF_OK; + } + } + //we have to create a new entry... + ent = (GF_SeqOffHintEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SNRO); + ent->SeqOffset = SequenceNumberOffset; + return gf_list_add(hdesc->HintDataTable, ent); +} + +//Starts a new sample for the hint track. A sample is just a collection of packets +//the transmissionTime is indicated in the media timeScale of the hint track +GF_Err gf_isom_begin_hint_sample(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 TransmissionTime) +{ + GF_TrackBox *trak; + u32 descIndex, dataRefIndex; + GF_HintSample *samp; + GF_HintSampleEntryBox *entry; + GF_Err e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM; + + //assert we're increasing the timing... + if (trak->Media->information->sampleTable->TimeToSample->w_LastDTS > TransmissionTime) return GF_BAD_PARAM; + + //store the descIndex for this sample + descIndex = HintDescriptionIndex; + if (!HintDescriptionIndex) { + descIndex = trak->Media->information->sampleTable->currentEntryIndex; + } + e = Media_GetSampleDesc(trak->Media, descIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex); + if (e) return e; + if (!entry || !dataRefIndex) return GF_BAD_PARAM; + //set the current to this one if no packet is used + if (entry->hint_sample) return GF_BAD_PARAM; + trak->Media->information->sampleTable->currentEntryIndex = descIndex; + + //create a new sample based on the protocol type of the hint description entry + samp = gf_isom_hint_sample_new(entry->type); + if (!samp) return GF_NOT_SUPPORTED; + + //OK, let's store the time of this sample + samp->TransmissionTime = TransmissionTime; + //OK, set our sample in the entry... + entry->hint_sample = samp; + return GF_OK; +} + +//stores the hint sample in the file +//set IsRandomAccessPoint if you want to indicate that this is a random access point +//in the stream +GF_Err gf_isom_end_hint_sample(GF_ISOFile *the_file, u32 trackNumber, u8 IsRandomAccessPoint) +{ + GF_TrackBox *trak; + GF_HintSampleEntryBox *entry; + u32 dataRefIndex; + GF_Err e; + GF_BitStream *bs; + GF_ISOSample *samp; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex); + if (e) return e; + if (!entry->hint_sample) return GF_BAD_PARAM; + + //first of all, we need to adjust the offset for data referenced IN THIS hint sample + //and get some PckSize + e = AdjustHintInfo(entry, trak->Media->information->sampleTable->SampleSize->sampleCount + 1); + if (e) return e; + + //ok, let's write the sample + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + e = gf_isom_hint_sample_write(entry->hint_sample, bs); + if (e) { + gf_bs_del(bs); + return e; + } + samp = gf_isom_sample_new(); + samp->CTS_Offset = 0; + samp->IsRAP = IsRandomAccessPoint; + samp->DTS = entry->hint_sample->TransmissionTime; + //get the sample + gf_bs_get_content(bs, &samp->data, &samp->dataLength); + gf_bs_del(bs); + + //finally add the sample + e = gf_isom_add_sample(the_file, trackNumber, trak->Media->information->sampleTable->currentEntryIndex, samp); + gf_isom_sample_del(&samp); + + //and delete the sample in our entry ... + gf_isom_hint_sample_del(entry->hint_sample); + entry->hint_sample = NULL; + return e; +} + + +//adds a blank chunk of data in the sample that is skipped while streaming +GF_Err gf_isom_hint_blank_data(GF_ISOFile *the_file, u32 trackNumber, u8 AtBegin) +{ + GF_TrackBox *trak; + GF_HintSampleEntryBox *entry; + u32 count; + GF_HintPacket *pck; + GF_EmptyDTE *dte; + GF_Err e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &count); + if (e) return e; + if (!entry->hint_sample) return GF_BAD_PARAM; + count = gf_list_count(entry->hint_sample->packetTable); + if (!count) return GF_BAD_PARAM; + pck = (GF_HintPacket *)gf_list_get(entry->hint_sample->packetTable, count - 1); + + dte = (GF_EmptyDTE *) NewDTE(0); + return gf_isom_hint_pck_add_dte(entry->hint_sample->HintType, pck, (GF_GenericDTE *)dte, AtBegin); +} + + +//adds a chunk of data (max 14 bytes) in the packet that is directly copied +//while streaming +GF_Err gf_isom_hint_direct_data(GF_ISOFile *the_file, u32 trackNumber, char *data, u32 dataLength, u8 AtBegin) +{ + GF_TrackBox *trak; + GF_HintSampleEntryBox *entry; + u32 count; + GF_HintPacket *pck; + GF_ImmediateDTE *dte; + GF_Err e; + u32 offset = 0; + + if (!dataLength) return GF_OK; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !IsHintTrack(trak) || (dataLength > 14)) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &count); + if (e) return e; + if (!entry->hint_sample) return GF_BAD_PARAM; + count = gf_list_count(entry->hint_sample->packetTable); + if (!count) return GF_BAD_PARAM; + pck = (GF_HintPacket *)gf_list_get(entry->hint_sample->packetTable, count - 1); + + dte = (GF_ImmediateDTE *) NewDTE(1); + memcpy(dte->data, data + offset, dataLength); + dte->dataLength = dataLength; + return gf_isom_hint_pck_add_dte(entry->hint_sample->HintType, pck, (GF_GenericDTE *)dte, AtBegin); +} + +GF_Err gf_isom_hint_sample_data(GF_ISOFile *the_file, u32 trackNumber, u32 SourceTrackID, u32 SampleNumber, u16 DataLength, u32 offsetInSample, char *extra_data, u8 AtBegin) +{ + GF_TrackBox *trak; + GF_HintSampleEntryBox *entry; + u32 count; + u16 refIndex; + GF_HintPacket *pck; + GF_SampleDTE *dte; + GF_Err e; + GF_TrackReferenceTypeBox *hint; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM; + + + e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &count); + if (e) return e; + if (!entry->hint_sample) return GF_BAD_PARAM; + count = gf_list_count(entry->hint_sample->packetTable); + if (!count) return GF_BAD_PARAM; + pck = (GF_HintPacket *)gf_list_get(entry->hint_sample->packetTable, count - 1); + + dte = (GF_SampleDTE *) NewDTE(2); + + dte->dataLength = DataLength; + dte->sampleNumber = SampleNumber; + dte->byteOffset = offsetInSample; + + //we're getting data from another track + if (SourceTrackID != trak->Header->trackID) { + //get (or set) the track reference index + e = Track_FindRef(trak, GF_ISOM_REF_HINT, &hint); + if (e) return e; + e = reftype_AddRefTrack(hint, SourceTrackID, &refIndex); + if (e) return e; + //WARNING: IN QT, MUST BE 0-based !!! + dte->trackRefIndex = (u8) (refIndex - 1); + } else { + //we're in the hint track + dte->trackRefIndex = (s8) -1; + //basic check... + if (SampleNumber > trak->Media->information->sampleTable->SampleSize->sampleCount + 1) { + DelDTE((GF_GenericDTE *)dte); + return GF_BAD_PARAM; + } + + //are we in the current sample ?? + if (!SampleNumber || (SampleNumber == trak->Media->information->sampleTable->SampleSize->sampleCount + 1)) { + //we adding some stuff in the current sample ... + dte->byteOffset += entry->hint_sample->dataLength; + entry->hint_sample->AdditionalData = (char*)realloc(entry->hint_sample->AdditionalData, sizeof(char) * (entry->hint_sample->dataLength + DataLength)); + if (AtBegin) { + if (entry->hint_sample->dataLength) + memmove(entry->hint_sample->AdditionalData + DataLength, entry->hint_sample->AdditionalData, entry->hint_sample->dataLength); + memcpy(entry->hint_sample->AdditionalData, extra_data, DataLength); + /*offset existing DTE*/ + gf_isom_hint_pck_offset(entry->hint_sample->HintType, pck, DataLength, SampleNumber); + } else { + memcpy(entry->hint_sample->AdditionalData + entry->hint_sample->dataLength, extra_data, DataLength); + } + entry->hint_sample->dataLength += DataLength; + //and set the sample number ... + dte->sampleNumber = trak->Media->information->sampleTable->SampleSize->sampleCount + 1; + } + } + //OK, add the entry + return gf_isom_hint_pck_add_dte(entry->hint_sample->HintType, pck, (GF_GenericDTE *)dte, AtBegin); +} + +GF_Err gf_isom_hint_sample_description_data(GF_ISOFile *the_file, u32 trackNumber, u32 SourceTrackID, u32 StreamDescriptionIndex, u16 DataLength, u32 offsetInDescription, u8 AtBegin) +{ + GF_TrackBox *trak; + GF_HintSampleEntryBox *entry; + u32 count; + u16 refIndex; + GF_HintPacket *pck; + GF_StreamDescDTE *dte; + GF_Err e; + GF_TrackReferenceTypeBox *hint; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !IsHintTrack(trak)) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &count); + if (e) return e; + if (!entry->hint_sample) return GF_BAD_PARAM; + count = gf_list_count(entry->hint_sample->packetTable); + if (!count) return GF_BAD_PARAM; + pck = (GF_HintPacket *)gf_list_get(entry->hint_sample->packetTable, count - 1); + + dte = (GF_StreamDescDTE *) NewDTE(3); + dte->byteOffset = offsetInDescription; + dte->dataLength = DataLength; + dte->streamDescIndex = StreamDescriptionIndex; + if (SourceTrackID == trak->Header->trackID) { + dte->trackRefIndex = (s8) -1; + } else { + //get (or set) the track reference index + e = Track_FindRef(trak, GF_ISOM_REF_HINT, &hint); + if (e) return e; + e = reftype_AddRefTrack(hint, SourceTrackID, &refIndex); + if (e) return e; + //WARNING: IN QT, MUST BE 0-based !!! + dte->trackRefIndex = (u8) (refIndex - 1); + } + return gf_isom_hint_pck_add_dte(entry->hint_sample->HintType, pck, (GF_GenericDTE *)dte, AtBegin); +} + +#endif //GPAC_READ_ONLY + +#ifndef GPAC_READ_ONLY + +GF_Err gf_isom_rtp_packet_set_flags(GF_ISOFile *the_file, u32 trackNumber, + u8 PackingBit, + u8 eXtensionBit, + u8 MarkerBit, + u8 disposable_packet, + u8 IsRepeatedPacket) +{ + GF_TrackBox *trak; + GF_HintSampleEntryBox *entry; + GF_RTPPacket *pck; + u32 dataRefIndex, ind; + GF_Err e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex); + if (e) return e; + if (!entry->hint_sample) return GF_BAD_PARAM; + + ind = gf_list_count(entry->hint_sample->packetTable); + if (!ind) return GF_BAD_PARAM; + pck = (GF_RTPPacket *)gf_list_get(entry->hint_sample->packetTable, ind-1); + + pck->P_bit = PackingBit ? 1 : 0; + pck->X_bit = eXtensionBit ? 1 : 0; + pck->M_bit = MarkerBit ? 1 : 0; + pck->B_bit = disposable_packet ? 1 : 0; + pck->R_bit = IsRepeatedPacket ? 1 : 0; + return GF_OK; +} + +GF_Err gf_isom_rtp_packet_begin(GF_ISOFile *the_file, u32 trackNumber, + s32 relativeTime, + u8 PackingBit, + u8 eXtensionBit, + u8 MarkerBit, + u8 PayloadType, + u8 B_frame, + u8 IsRepeatedPacket, + u16 SequenceNumber) +{ + GF_TrackBox *trak; + GF_HintSampleEntryBox *entry; + GF_RTPPacket *pck; + u32 dataRefIndex; + GF_Err e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex); + if (e) return e; + if (!entry->hint_sample) return GF_BAD_PARAM; + + pck = (GF_RTPPacket *) gf_isom_hint_pck_new(entry->hint_sample->HintType); + + pck->P_bit = PackingBit ? 1 : 0; + pck->X_bit = eXtensionBit ? 1 : 0; + pck->M_bit = MarkerBit ? 1 : 0; + pck->payloadType = PayloadType; + pck->SequenceNumber = SequenceNumber; + pck->B_bit = B_frame ? 1 : 0; + pck->R_bit = IsRepeatedPacket ? 1 : 0; + pck->relativeTransTime = relativeTime; + return gf_list_add(entry->hint_sample->packetTable, pck); +} + + +//set the time offset of this packet. This enables packets to be placed in the hint track +//in decoding order, but have their presentation time-stamp in the transmitted +//packet be in a different order. Typically used for MPEG video with B-frames +GF_Err gf_isom_rtp_packet_set_offset(GF_ISOFile *the_file, u32 trackNumber, s32 timeOffset) +{ + GF_RTPOBox *rtpo; + GF_TrackBox *trak; + GF_HintSampleEntryBox *entry; + GF_RTPPacket *pck; + u32 dataRefIndex, i; + GF_Err e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, trak->Media->information->sampleTable->currentEntryIndex, (GF_SampleEntryBox **) &entry, &dataRefIndex); + if (e) return e; + if (!entry->hint_sample) return GF_BAD_PARAM; + + pck = (GF_RTPPacket *)gf_list_get(entry->hint_sample->packetTable, gf_list_count(entry->hint_sample->packetTable) - 1); + if (!pck) return GF_BAD_PARAM; + + //look in the TLV + i=0; + while ((rtpo = (GF_RTPOBox *)gf_list_enum(pck->TLV, &i))) { + if (rtpo->type == GF_ISOM_BOX_TYPE_RTPO) { + rtpo->timeOffset = timeOffset; + return GF_OK; + } + } + //not found, add it + rtpo = (GF_RTPOBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_RTPO); + rtpo->timeOffset = timeOffset; + + return gf_list_add(pck->TLV, rtpo); +} + + +static void AddSDPLine(GF_List *list, char *sdp_text, Bool is_movie_sdp) +{ + const char *sdp_order; + u32 i, count = gf_list_count(list); + char fc = sdp_text[0]; + + sdp_order = (is_movie_sdp) ? "vosiuepcbzkatr" : "micbka"; + for (i=0; istrlen(s1))) { + gf_list_insert(list, sdp_text, i); + return; + } + } + gf_list_add(list, sdp_text); +} + +static void ReorderSDP(char *sdp_text, Bool is_movie_sdp) +{ + char *cur, b; + GF_List *lines = gf_list_new(); + cur = sdp_text; + while (cur) { + char *st = strstr(cur, "\r\n"); + assert(st); + st += 2; + if (!st[0]) { + AddSDPLine(lines, strdup(cur), is_movie_sdp); + break; + } + b = st[0]; + st[0] = 0; + AddSDPLine(lines, strdup(cur), is_movie_sdp); + st[0] = b; + cur = st; + } + strcpy(sdp_text, ""); + while (gf_list_count(lines)) { + char *cur = (char *)gf_list_get(lines, 0); + gf_list_rem(lines, 0); + strcat(sdp_text, cur); + free(cur); + } + gf_list_del(lines); +} + +//add an SDP line to the SDP container at the track level (media-specific SDP info) +GF_Err gf_isom_sdp_add_track_line(GF_ISOFile *the_file, u32 trackNumber, const char *text) +{ + GF_TrackBox *trak; + GF_UserDataMap *map; + GF_HintTrackInfoBox *hnti; + GF_SDPBox *sdp; + GF_Err e; + char *buf; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + //currently, only RTP hinting supports SDP + if (!CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; + + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HNTI, NULL); + if (!map) return GF_ISOM_INVALID_FILE; + + //we should have only one HNTI in the UDTA + if (gf_list_count(map->boxList) != 1) return GF_ISOM_INVALID_FILE; + + hnti = (GF_HintTrackInfoBox *)gf_list_get(map->boxList, 0); + if (!hnti->SDP) { + e = hnti_AddBox(hnti, gf_isom_box_new(GF_ISOM_BOX_TYPE_SDP)); + if (e) return e; + } + sdp = (GF_SDPBox *) hnti->SDP; + + if (!sdp->sdpText) { + sdp->sdpText = (char *)malloc(sizeof(char) * (strlen(text) + 3)); + strcpy(sdp->sdpText, text); + strcat(sdp->sdpText, "\r\n"); + return GF_OK; + } + buf = (char *)malloc(sizeof(char) * (strlen(sdp->sdpText) + strlen(text) + 3)); + strcpy(buf, sdp->sdpText); + strcat(buf, text); + strcat(buf, "\r\n"); + free(sdp->sdpText); + ReorderSDP(buf, 0); + sdp->sdpText = buf; + return GF_OK; +} + +//remove all SDP info at the track level +GF_Err gf_isom_sdp_clean_track(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + GF_UserDataMap *map; + GF_HintTrackInfoBox *hnti; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + //currently, only RTP hinting supports SDP + if (!CheckHintFormat(trak, GF_ISOM_HINT_RTP)) return GF_BAD_PARAM; + + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HNTI, NULL); + if (!map) return GF_ISOM_INVALID_FILE; + + //we should have only one HNTI in the UDTA + if (gf_list_count(map->boxList) != 1) return GF_ISOM_INVALID_FILE; + + hnti = (GF_HintTrackInfoBox *)gf_list_get(map->boxList, 0); + if (!hnti->SDP) return GF_OK; + //and free the SDP + free(((GF_SDPBox *)hnti->SDP)->sdpText); + ((GF_SDPBox *)hnti->SDP)->sdpText = NULL; + return GF_OK; +} + + +//add an SDP line to the SDP container at the movie level (presentation SDP info) +//NOTE: the \r\n end of line for SDP is automatically inserted +GF_Err gf_isom_sdp_add_line(GF_ISOFile *movie, const char *text) +{ + GF_UserDataMap *map; + GF_RTPBox *rtp; + GF_Err e; + GF_HintTrackInfoBox *hnti; + char *buf; + + if (!movie->moov) return GF_BAD_PARAM; + + //check if we have a udta ... + if (!movie->moov->udta) { + e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + //find a hnti in the udta + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_HNTI, NULL); + if (!map) { + e = udta_AddBox(movie->moov->udta, gf_isom_box_new(GF_ISOM_BOX_TYPE_HNTI)); + if (e) return e; + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_HNTI, NULL); + } + + //there should be one and only one hnti + if (!gf_list_count(map->boxList) ) { + e = udta_AddBox(movie->moov->udta, gf_isom_box_new(GF_ISOM_BOX_TYPE_HNTI)); + if (e) return e; + } + else if (gf_list_count(map->boxList) < 1) return GF_ISOM_INVALID_FILE; + + hnti = (GF_HintTrackInfoBox *)gf_list_get(map->boxList, 0); + + if (!hnti->SDP) { + //we have to create it by hand, as we have a duplication of box type + //(GF_RTPSampleEntryBox and GF_RTPBox have the same type...) + rtp = (GF_RTPBox *) malloc(sizeof(GF_RTPBox)); + rtp->subType = GF_ISOM_BOX_TYPE_SDP; + rtp->type = GF_ISOM_BOX_TYPE_RTP; + rtp->sdpText = NULL; + hnti_AddBox(hnti, (GF_Box *)rtp); + } + rtp = (GF_RTPBox *) hnti->SDP; + + if (!rtp->sdpText) { + rtp->sdpText = (char*)malloc(sizeof(char) * (strlen(text) + 3)); + strcpy(rtp->sdpText, text); + strcat(rtp->sdpText, "\r\n"); + return GF_OK; + } + buf = (char*)malloc(sizeof(char) * (strlen(rtp->sdpText) + strlen(text) + 3)); + strcpy(buf, rtp->sdpText); + strcat(buf, text); + strcat(buf, "\r\n"); + free(rtp->sdpText); + ReorderSDP(buf, 1); + rtp->sdpText = buf; + return GF_OK; +} + + +//remove all SDP info at the movie level +GF_Err gf_isom_sdp_clean(GF_ISOFile *movie) +{ + GF_UserDataMap *map; + GF_HintTrackInfoBox *hnti; + + //check if we have a udta ... + if (!movie->moov || !movie->moov->udta) return GF_OK; + + //find a hnti in the udta + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_HNTI, NULL); + if (!map) return GF_OK; + + //there should be one and only one hnti + if (gf_list_count(map->boxList) != 1) return GF_ISOM_INVALID_FILE; + hnti = (GF_HintTrackInfoBox *)gf_list_get(map->boxList, 0); + + //remove and destroy the entry + gf_list_rem(map->boxList, 0); + gf_isom_box_del((GF_Box *)hnti); + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +GF_EXPORT +GF_Err gf_isom_sdp_get(GF_ISOFile *movie, const char **sdp, u32 *length) +{ + GF_UserDataMap *map; + GF_HintTrackInfoBox *hnti; + GF_RTPBox *rtp; + *length = 0; + *sdp = NULL; + if (!movie || !movie->moov) return GF_BAD_PARAM; + //check if we have a udta ... + if (!movie->moov->udta) return GF_OK; + + //find a hnti in the udta + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_HNTI, NULL); + if (!map) return GF_OK; + + //there should be one and only one hnti + if (gf_list_count(map->boxList) != 1) return GF_ISOM_INVALID_FILE; + hnti = (GF_HintTrackInfoBox *)gf_list_get(map->boxList, 0); + + if (!hnti->SDP) return GF_OK; + rtp = (GF_RTPBox *) hnti->SDP; + + *length = strlen(rtp->sdpText); + *sdp = rtp->sdpText; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_sdp_track_get(GF_ISOFile *the_file, u32 trackNumber, const char **sdp, u32 *length) +{ + GF_TrackBox *trak; + GF_UserDataMap *map; + GF_HintTrackInfoBox *hnti; + GF_SDPBox *sdpa; + + *sdp = NULL; + *length = 0; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->udta) return GF_OK; + + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HNTI, NULL); + if (!map) return GF_ISOM_INVALID_FILE; + + //we should have only one HNTI in the UDTA + if (gf_list_count(map->boxList) != 1) return GF_ISOM_INVALID_FILE; + + hnti = (GF_HintTrackInfoBox *)gf_list_get(map->boxList, 0); + if (!hnti->SDP) return GF_OK; + sdpa = (GF_SDPBox *) hnti->SDP; + + *length = strlen(sdpa->sdpText); + *sdp = sdpa->sdpText; + return GF_OK; +} + + +GF_EXPORT +u32 gf_isom_get_payt_count(GF_ISOFile *the_file, u32 trackNumber) +{ + u32 i, count; + GF_TrackBox *trak; + GF_UserDataMap *map; + GF_HintInfoBox *hinf; + GF_PAYTBox *payt; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + if (!CheckHintFormat(trak, GF_4CC('r', 't', 'p', ' '))) return 0; + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HINF, NULL); + if (!map) return 0; + if (gf_list_count(map->boxList) != 1) return 0; + + hinf = (GF_HintInfoBox *)gf_list_get(map->boxList, 0); + count = 0; + i = 0; + while ((payt = gf_list_enum(hinf->boxList, &i))) { + if (payt->type == GF_ISOM_BOX_TYPE_PAYT) count++; + } + return count; +} + +GF_EXPORT +const char *gf_isom_get_payt_info(GF_ISOFile *the_file, u32 trackNumber, u32 index, u32 *payID) +{ + u32 i, count; + GF_TrackBox *trak; + GF_UserDataMap *map; + GF_HintInfoBox *hinf; + GF_PAYTBox *payt; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !index) return NULL; + + if (!CheckHintFormat(trak, GF_4CC('r', 't', 'p', ' '))) return NULL; + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HINF, NULL); + if (!map) return NULL; + if (gf_list_count(map->boxList) != 1) return NULL; + + hinf = (GF_HintInfoBox *)gf_list_get(map->boxList, 0); + count = 0; + i = 0; + while ((payt = gf_list_enum(hinf->boxList, &i))) { + if (payt->type == GF_ISOM_BOX_TYPE_PAYT) { + count++; + if (count == index) { + if (payID) *payID=payt->payloadCode; + return payt->payloadString; + } + } + } + return NULL; +} + diff --git a/src/isomedia/hinting.c b/src/isomedia/hinting.c new file mode 100644 index 0000000..2b88ea0 --- /dev/null +++ b/src/isomedia/hinting.c @@ -0,0 +1,982 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + + +GF_Box *ghnt_New() +{ + GF_HintSampleEntryBox *tmp = (GF_HintSampleEntryBox *) malloc(sizeof(GF_HintSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_HintSampleEntryBox)); + tmp->HintDataTable = gf_list_new(); + if (!tmp->HintDataTable) { + free(tmp); + return NULL; + } + //this type is used internally for protocols that share the same base entry + //currently only RTP uses this, but a flexMux could use this entry too... + tmp->type = GF_ISOM_BOX_TYPE_GHNT; + tmp->HintTrackVersion = 1; + tmp->LastCompatibleVersion = 1; + return (GF_Box *)tmp; +} + +void ghnt_del(GF_Box *s) +{ + GF_HintSampleEntryBox *ptr; + + ptr = (GF_HintSampleEntryBox *)s; + gf_isom_box_array_del(ptr->HintDataTable); + if (ptr->hint_sample) gf_isom_hint_sample_del(ptr->hint_sample); + free(ptr); +} + +GF_Err ghnt_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Box *a; + GF_Err e; + GF_HintSampleEntryBox *ptr = (GF_HintSampleEntryBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + if (ptr->size < 16) return GF_ISOM_INVALID_FILE; + + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->HintTrackVersion = gf_bs_read_u16(bs); + ptr->LastCompatibleVersion = gf_bs_read_u16(bs); + ptr->MaxPacketSize = gf_bs_read_u32(bs); + ptr->size -= 16; + + while (ptr->size) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + e = gf_list_add(ptr->HintDataTable, a); + if (e) return e; + ptr->size -= a->size; + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY + +GF_Err ghnt_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HintSampleEntryBox *ptr = (GF_HintSampleEntryBox *)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + gf_bs_write_u16(bs, ptr->HintTrackVersion); + gf_bs_write_u16(bs, ptr->LastCompatibleVersion); + gf_bs_write_u32(bs, ptr->MaxPacketSize); + return gf_isom_box_array_write(s, ptr->HintDataTable, bs); +} + +GF_Err ghnt_Size(GF_Box *s) +{ + GF_Err e; + GF_HintSampleEntryBox *ptr = (GF_HintSampleEntryBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 16; + e = gf_isom_box_array_size(s, ptr->HintDataTable); + if (e) return e; + return GF_OK; +} + + +#endif //GPAC_READ_ONLY + + +GF_HintSample *gf_isom_hint_sample_new(u32 ProtocolType) +{ + GF_HintSample *tmp; + u8 type; + + switch (ProtocolType) { + case GF_ISOM_BOX_TYPE_RTP_STSD: + type = GF_ISMO_HINT_RTP; + break; + default: + return NULL; + } + GF_SAFEALLOC(tmp, GF_HintSample); + tmp->packetTable = gf_list_new(); + tmp->HintType = type; + return tmp; +} + +void gf_isom_hint_sample_del(GF_HintSample *ptr) +{ + GF_HintPacket *pck; + + while (gf_list_count(ptr->packetTable)) { + pck = (GF_HintPacket *)gf_list_get(ptr->packetTable, 0); + gf_isom_hint_pck_del(ptr->HintType, pck); + gf_list_rem(ptr->packetTable, 0); + } + gf_list_del(ptr->packetTable); + if (ptr->AdditionalData) free(ptr->AdditionalData); + + if (ptr->sample_cache) { + while (gf_list_count(ptr->sample_cache)) { + GF_HintDataCache *hdc = (GF_HintDataCache *)gf_list_get(ptr->sample_cache, 0); + gf_list_rem(ptr->sample_cache, 0); + if (hdc->samp) gf_isom_sample_del(&hdc->samp); + free(hdc); + } + gf_list_del(ptr->sample_cache); + } + free(ptr); +} + +GF_Err gf_isom_hint_sample_read(GF_HintSample *ptr, GF_BitStream *bs, u32 sampleSize) +{ + u16 entryCount, i; + GF_HintPacket *pck; + GF_Err e; + u64 sizeIn, sizeOut; + + sizeIn = gf_bs_available(bs); + + entryCount = gf_bs_read_u16(bs); + ptr->reserved = gf_bs_read_u16(bs); + + for (i = 0; i < entryCount; i++) { + pck = gf_isom_hint_pck_new(ptr->HintType); + e = gf_isom_hint_pck_read(ptr->HintType, pck, bs); + if (e) return e; + gf_list_add(ptr->packetTable, pck); + } + + sizeOut = gf_bs_available(bs) - sizeIn; + + //do we have some more data after the packets ?? + if ((u32)sizeOut < sampleSize) { + ptr->dataLength = sampleSize - (u32)sizeOut; + ptr->AdditionalData = (char*)malloc(sizeof(char) * ptr->dataLength); + gf_bs_read_data(bs, ptr->AdditionalData, ptr->dataLength); + } + return GF_OK; +} + + +#ifndef GPAC_READ_ONLY + +GF_Err gf_isom_hint_sample_write(GF_HintSample *ptr, GF_BitStream *bs) +{ + u32 count, i; + GF_HintPacket *pck; + GF_Err e; + + count = gf_list_count(ptr->packetTable); + gf_bs_write_u16(bs, count); + gf_bs_write_u16(bs, ptr->reserved); + //write the packet table + for (i=0; ipacketTable, i); + e = gf_isom_hint_pck_write(ptr->HintType, pck, bs); + if (e) return e; + } + //write additional data + if (ptr->AdditionalData) { + gf_bs_write_data(bs, ptr->AdditionalData, ptr->dataLength); + } + return GF_OK; +} + + +u32 gf_isom_hint_sample_size(GF_HintSample *ptr) +{ + u32 size, count, i; + GF_HintPacket *pck; + + size = 4; + count = gf_list_count(ptr->packetTable); + for (i=0; ipacketTable, i); + size += gf_isom_hint_pck_size(ptr->HintType, pck); + } + size += ptr->dataLength; + return size; +} + +#endif + + + +GF_HintPacket *gf_isom_hint_pck_new(u8 HintType) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return (GF_HintPacket *) gf_isom_hint_rtp_new(); + default: + return NULL; + } +} + +void gf_isom_hint_pck_del(u8 HintType, GF_HintPacket *ptr) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + gf_isom_hint_rtp_del((GF_RTPPacket *)ptr); + break; + default: + break; + } +} + +GF_Err gf_isom_hint_pck_read(u8 HintType, GF_HintPacket *ptr, GF_BitStream *bs) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return gf_isom_hint_rtp_read((GF_RTPPacket *)ptr, bs); + default: + return GF_NOT_SUPPORTED; + } +} + +#ifndef GPAC_READ_ONLY + +GF_Err gf_isom_hint_pck_write(u8 HintType, GF_HintPacket *ptr, GF_BitStream *bs) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return gf_isom_hint_rtp_write((GF_RTPPacket *)ptr, bs); + default: + return GF_NOT_SUPPORTED; + } +} + +u32 gf_isom_hint_pck_size(u8 HintType, GF_HintPacket *ptr) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return gf_isom_hint_rtp_size((GF_RTPPacket *)ptr); + default: + return 0; + } +} + +GF_Err gf_isom_hint_pck_offset(u8 HintType, GF_HintPacket *ptr, u32 offset, u32 HintSampleNumber) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return gf_isom_hint_rtp_offset((GF_RTPPacket *)ptr, offset, HintSampleNumber); + default: + return GF_NOT_SUPPORTED; + } +} + +GF_Err gf_isom_hint_pck_add_dte(u8 HintType, GF_HintPacket *ptr, GF_GenericDTE *dte, u8 AtBegin) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + if (AtBegin) + return gf_list_insert( ((GF_RTPPacket *)ptr)->DataTable, dte, 0); + else + return gf_list_add( ((GF_RTPPacket *)ptr)->DataTable, dte); + + default: + return GF_NOT_SUPPORTED; + } +} + +u32 gf_isom_hint_pck_length(u8 HintType, GF_HintPacket *ptr) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return gf_isom_hint_rtp_length((GF_RTPPacket *)ptr); + default: + return 0; + } +} + + +#endif + + + +/******************************************************************** + Creation of DataTable entries in the RTP sample +********************************************************************/ + +GF_GenericDTE *New_EmptyDTE() +{ + GF_EmptyDTE *dte = (GF_EmptyDTE *)malloc(sizeof(GF_EmptyDTE)); + dte->source = 0; + return (GF_GenericDTE *)dte; +} + +GF_GenericDTE *New_ImmediateDTE() +{ + GF_ImmediateDTE *dte = (GF_ImmediateDTE *)malloc(sizeof(GF_ImmediateDTE)); + dte->source = 1; + memset(dte->data, 0, 14); + dte->dataLength = 0; + return (GF_GenericDTE *)dte; +} + +GF_GenericDTE *New_SampleDTE() +{ + GF_SampleDTE *dte = (GF_SampleDTE *)malloc(sizeof(GF_SampleDTE)); + dte->source = 2; + //can be -1 in QT , so init at -2 + dte->trackRefIndex = (s8) -2; + dte->dataLength = 0; + dte->sampleNumber = 0; + dte->samplesPerComp = 1; + dte->byteOffset = 0; + dte->bytesPerComp = 1; + return (GF_GenericDTE *)dte; +} + +GF_GenericDTE *New_StreamDescDTE() +{ + GF_StreamDescDTE *dte = (GF_StreamDescDTE *)malloc(sizeof(GF_StreamDescDTE)); + dte->source = 3; + dte->byteOffset = 0; + dte->dataLength = 0; + dte->reserved = 0; + dte->streamDescIndex = 0; + //can be -1 in QT , so init at -2 + dte->trackRefIndex = (s8) -2; + return (GF_GenericDTE *)dte; +} + +//creation of DTEs +GF_GenericDTE *NewDTE(u8 type) +{ + switch (type) { + case 0: + return New_EmptyDTE(); + case 1: + return New_ImmediateDTE(); + case 2: + return New_SampleDTE(); + case 3: + return New_StreamDescDTE(); + default: + return NULL; + } +} + +/******************************************************************** + Deletion of DataTable entries in the RTP sample +********************************************************************/ +void Del_EmptyDTE(GF_EmptyDTE *dte) +{ free(dte); } + +void Del_ImmediateDTE(GF_ImmediateDTE *dte) +{ free(dte); } + +void Del_SampleDTE(GF_SampleDTE *dte) +{ free(dte); } + +void Del_StreamDescDTE(GF_StreamDescDTE *dte) +{ free(dte); } + +//deletion of DTEs +void DelDTE(GF_GenericDTE *dte) +{ + switch (dte->source) { + case 0: + Del_EmptyDTE((GF_EmptyDTE *)dte); + break; + case 1: + Del_ImmediateDTE((GF_ImmediateDTE *)dte); + break; + case 2: + Del_SampleDTE((GF_SampleDTE *)dte); + break; + case 3: + Del_StreamDescDTE((GF_StreamDescDTE *)dte); + break; + default: + return; + } +} + + + +/******************************************************************** + Reading of DataTable entries in the RTP sample +********************************************************************/ +GF_Err Read_EmptyDTE(GF_EmptyDTE *dte, GF_BitStream *bs) +{ + char empty[15]; + //empty but always 15 bytes !!! + gf_bs_read_data(bs, empty, 15); + return GF_OK; +} + +GF_Err Read_ImmediateDTE(GF_ImmediateDTE *dte, GF_BitStream *bs) +{ + dte->dataLength = gf_bs_read_u8(bs); + if (dte->dataLength > 14) return GF_ISOM_INVALID_FILE; + gf_bs_read_data(bs, dte->data, dte->dataLength); + if (dte->dataLength < 14) gf_bs_skip_bytes(bs, 14 - dte->dataLength); + return GF_OK; +} + +GF_Err Read_SampleDTE(GF_SampleDTE *dte, GF_BitStream *bs) +{ + dte->trackRefIndex = gf_bs_read_u8(bs); + dte->dataLength = gf_bs_read_u16(bs); + dte->sampleNumber = gf_bs_read_u32(bs); + dte->byteOffset = gf_bs_read_u32(bs); + dte->bytesPerComp = gf_bs_read_u16(bs); + dte->samplesPerComp = gf_bs_read_u16(bs); + return GF_OK; +} + +GF_Err Read_StreamDescDTE(GF_StreamDescDTE *dte, GF_BitStream *bs) +{ + dte->trackRefIndex = gf_bs_read_u8(bs); + dte->dataLength = gf_bs_read_u16(bs); + dte->streamDescIndex = gf_bs_read_u32(bs); + dte->byteOffset = gf_bs_read_u32(bs); + dte->reserved = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Err ReadDTE(GF_GenericDTE *dte, GF_BitStream *bs) +{ + switch (dte->source) { + case 0: + //nothing to o, it is an empty entry + return Read_EmptyDTE((GF_EmptyDTE *)dte, bs); + case 1: + return Read_ImmediateDTE((GF_ImmediateDTE *)dte, bs); + case 2: + return Read_SampleDTE((GF_SampleDTE *)dte, bs); + case 3: + return Read_StreamDescDTE((GF_StreamDescDTE *)dte, bs); + default: + return GF_ISOM_INVALID_FILE; + } +} + +/******************************************************************** + Writing of DataTable entries in the RTP sample +********************************************************************/ +GF_Err Write_EmptyDTE(GF_EmptyDTE *dte, GF_BitStream *bs) +{ + gf_bs_write_u8(bs, dte->source); + //empty but always 15 bytes !!! + gf_bs_write_data(bs, "empty hint DTE", 15); + return GF_OK; +} + +GF_Err Write_ImmediateDTE(GF_ImmediateDTE *dte, GF_BitStream *bs) +{ + char data[14]; + gf_bs_write_u8(bs, dte->source); + gf_bs_write_u8(bs, dte->dataLength); + gf_bs_write_data(bs, dte->data, dte->dataLength); + if (dte->dataLength < 14) { + memset(data, 0, 14); + gf_bs_write_data(bs, data, 14 - dte->dataLength); + } + return GF_OK; +} + +GF_Err Write_SampleDTE(GF_SampleDTE *dte, GF_BitStream *bs) +{ + gf_bs_write_u8(bs, dte->source); + gf_bs_write_u8(bs, dte->trackRefIndex); + gf_bs_write_u16(bs, dte->dataLength); + gf_bs_write_u32(bs, dte->sampleNumber); + gf_bs_write_u32(bs, dte->byteOffset); + gf_bs_write_u16(bs, dte->bytesPerComp); + gf_bs_write_u16(bs, dte->samplesPerComp); + return GF_OK; +} + +GF_Err Write_StreamDescDTE(GF_StreamDescDTE *dte, GF_BitStream *bs) +{ + gf_bs_write_u8(bs, dte->source); + + gf_bs_write_u8(bs, dte->trackRefIndex); + gf_bs_write_u16(bs, dte->dataLength); + gf_bs_write_u32(bs, dte->streamDescIndex); + gf_bs_write_u32(bs, dte->byteOffset); + gf_bs_write_u32(bs, dte->reserved); + return GF_OK; +} + +GF_Err WriteDTE(GF_GenericDTE *dte, GF_BitStream *bs) +{ + switch (dte->source) { + case 0: + //nothing to do, it is an empty entry + return Write_EmptyDTE((GF_EmptyDTE *)dte, bs); + case 1: + return Write_ImmediateDTE((GF_ImmediateDTE *)dte, bs); + case 2: + return Write_SampleDTE((GF_SampleDTE *)dte, bs); + case 3: + return Write_StreamDescDTE((GF_StreamDescDTE *)dte, bs); + default: + return GF_ISOM_INVALID_FILE; + } +} + +GF_Err OffsetDTE(GF_GenericDTE *dte, u32 offset, u32 HintSampleNumber) +{ + GF_SampleDTE *sDTE; + //offset shifting is only true for intra sample reference + switch (dte->source) { + case 2: + break; + default: + return GF_OK; + } + + sDTE = (GF_SampleDTE *)dte; + //we only adjust for intra HintTrack reference + if (sDTE->trackRefIndex != (s8) -1) return GF_OK; + //and in the same sample + if (sDTE->sampleNumber != HintSampleNumber) return GF_OK; + sDTE->byteOffset += offset; + return GF_OK; +} + +GF_RTPPacket *gf_isom_hint_rtp_new() +{ + GF_RTPPacket *tmp = (GF_RTPPacket *)malloc(sizeof(GF_RTPPacket)); + tmp->TLV = gf_list_new(); + tmp->DataTable = gf_list_new(); + tmp->B_bit = tmp->M_bit = tmp->P_bit = tmp->payloadType = tmp->payloadType = tmp->R_bit = tmp->X_bit = 0; + tmp->relativeTransTime = 0; + tmp->SequenceNumber = 0; + return tmp; +} + +void gf_isom_hint_rtp_del(GF_RTPPacket *ptr) +{ + GF_GenericDTE *p; + //the DTE + while (gf_list_count(ptr->DataTable)) { + p = (GF_GenericDTE *)gf_list_get(ptr->DataTable, 0); + DelDTE(p); + gf_list_rem(ptr->DataTable, 0); + } + gf_list_del(ptr->DataTable); + //the TLV + gf_isom_box_array_del(ptr->TLV); + free(ptr); +} + +GF_Err gf_isom_hint_rtp_read(GF_RTPPacket *ptr, GF_BitStream *bs) +{ + GF_Err e; + u8 hasTLV, type; + u16 i, count; + u32 TLVsize, tempSize; + GF_GenericDTE *dte; + GF_Box *a; + + ptr->relativeTransTime = gf_bs_read_u32(bs); + //RTP Header + //1- reserved fields + gf_bs_read_int(bs, 2); + ptr->P_bit = gf_bs_read_int(bs, 1); + ptr->X_bit = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 4); + ptr->M_bit = gf_bs_read_int(bs, 1); + ptr->payloadType = gf_bs_read_int(bs, 7); + + ptr->SequenceNumber = gf_bs_read_u16(bs); + gf_bs_read_int(bs, 13); + hasTLV = gf_bs_read_int(bs, 1); + ptr->B_bit = gf_bs_read_int(bs, 1); + ptr->R_bit = gf_bs_read_int(bs, 1); + count = gf_bs_read_u16(bs); + + //read the TLV + if (hasTLV) { + tempSize = 4; //TLVsize includes its field length + TLVsize = gf_bs_read_u32(bs); + while (tempSize < TLVsize) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + gf_list_add(ptr->TLV, a); + tempSize += (u32) a->size; + } + if (tempSize != TLVsize) return GF_ISOM_INVALID_FILE; + } + + //read the DTEs + for (i=0; idataLength) add_it = 1; + break; + case 2: + if ( ((GF_SampleDTE *)dte)->dataLength) add_it = 1; + break; + case 3: + if ( ((GF_StreamDescDTE *)dte)->dataLength) add_it = 1; + break; + } + if (add_it) + gf_list_add(ptr->DataTable, dte); + else + DelDTE(dte); + } + return GF_OK; +} + +GF_Err gf_isom_hint_rtp_offset(GF_RTPPacket *ptr, u32 offset, u32 HintSampleNumber) +{ + u32 count, i; + GF_GenericDTE *dte; + GF_Err e; + + count = gf_list_count(ptr->DataTable); + for (i=0; iDataTable, i); + e = OffsetDTE(dte, offset, HintSampleNumber); + if (e) return e; + } + return GF_OK; +} + +//Gets the REAL size of the packet once rebuild, but without CSRC fields in the +//header +u32 gf_isom_hint_rtp_length(GF_RTPPacket *ptr) +{ + u32 size, count, i; + GF_GenericDTE *dte; + + //64 bit header + size = 8; + //32 bit SSRC + size += 4; + count = gf_list_count(ptr->DataTable); + for (i=0; iDataTable, i); + switch (dte->source) { + case 0: + break; + case 1: + size += ((GF_ImmediateDTE *)dte)->dataLength; + break; + case 2: + size += ((GF_SampleDTE *)dte)->dataLength; + break; + case 3: + size += ((GF_StreamDescDTE *)dte)->dataLength; + break; + } + } + return size; +} + + +#ifndef GPAC_READ_ONLY + +u32 gf_isom_hint_rtp_size(GF_RTPPacket *ptr) +{ + GF_Box none; + u32 size, count; + //the RTP Header size and co + size = 12; + //the extra table size + count = gf_list_count(ptr->TLV); + if (count) { + none.size = 4; //WE INCLUDE THE SIZE FIELD LENGTH + none.type = 0; + //REMEMBER THAT TLV ENTRIES ARE 4-BYTES ALIGNED !!! + gf_isom_box_array_size(&none, ptr->TLV); + size += (u32) none.size; + } + //the DTE (each entry is 16 bytes) + count = gf_list_count(ptr->DataTable); + size += count * 16; + return size; +} + +GF_Err gf_isom_hint_rtp_write(GF_RTPPacket *ptr, GF_BitStream *bs) +{ + GF_Err e; + u32 TLVcount, DTEcount, i; + GF_Box none; + GF_GenericDTE *dte; + + gf_bs_write_u32(bs, ptr->relativeTransTime); + //RTP Header +// gf_bs_write_int(bs, 2, 2); + //version is 2 + gf_bs_write_int(bs, 2, 2); + gf_bs_write_int(bs, ptr->P_bit, 1); + gf_bs_write_int(bs, ptr->X_bit, 1); + gf_bs_write_int(bs, 0, 4); + gf_bs_write_int(bs, ptr->M_bit, 1); + gf_bs_write_int(bs, ptr->payloadType, 7); + + gf_bs_write_u16(bs, ptr->SequenceNumber); + gf_bs_write_int(bs, 0, 13); + TLVcount = gf_list_count(ptr->TLV); + DTEcount = gf_list_count(ptr->DataTable); + gf_bs_write_int(bs, TLVcount ? 1 : 0, 1); + gf_bs_write_int(bs, ptr->B_bit, 1); + gf_bs_write_int(bs, ptr->R_bit, 1); + + gf_bs_write_u16(bs, DTEcount); + + if (TLVcount) { + //first write the size of the table ... + none.size = 4; //WE INCLUDE THE SIZE FIELD LENGTH + none.type = 0; + gf_isom_box_array_size(&none, ptr->TLV); + gf_bs_write_u32(bs, (u32) none.size); + e = gf_isom_box_array_write(&none, ptr->TLV, bs); + if (e) return e; + } + //write the DTE... + for (i = 0; i < DTEcount; i++) { + dte = (GF_GenericDTE *)gf_list_get(ptr->DataTable, i); + e = WriteDTE(dte, bs); + if (e) return e; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + +GF_EXPORT +GF_Err gf_isom_reset_hint_reader(GF_ISOFile *the_file, u32 trackNumber, u32 sample_start, u32 ts_offset, u32 sn_offset, u32 ssrc) +{ + GF_Err e; + GF_TrackBox *trak; + GF_HintSampleEntryBox *entry; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (!sample_start) return GF_BAD_PARAM; + if (sample_start>=trak->Media->information->sampleTable->SampleSize->sampleCount) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, 1, (GF_SampleEntryBox **) &entry, NULL); + if (e) return e; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_RTP_STSD: + break; + default: + return GF_NOT_SUPPORTED; + } + + entry->hint_ref = NULL; + e = Track_FindRef(trak, GF_ISOM_REF_HINT, &entry->hint_ref); + if (e) return e; + + entry->cur_sample = sample_start; + entry->pck_sn = 1 + sn_offset; + entry->ssrc = ssrc; + entry->ts_offset = ts_offset; + if (entry->hint_sample) gf_isom_hint_sample_del(entry->hint_sample); + entry->hint_sample = NULL; + return GF_OK; +} + +static GF_Err gf_isom_load_next_hint_sample(GF_ISOFile *the_file, u32 trackNumber, GF_TrackBox *trak, GF_HintSampleEntryBox *entry) +{ + GF_BitStream *bs; + u32 descIdx; + GF_ISOSample *samp; + + if (!entry->cur_sample) return GF_BAD_PARAM; + if (entry->cur_sample>trak->Media->information->sampleTable->SampleSize->sampleCount) return GF_EOS; + + samp = gf_isom_get_sample(the_file, trackNumber, entry->cur_sample, &descIdx); + if (!samp) return GF_IO_ERR; + entry->cur_sample++; + + if (entry->hint_sample) gf_isom_hint_sample_del(entry->hint_sample); + + bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ); + entry->hint_sample = gf_isom_hint_sample_new(entry->type); + gf_isom_hint_sample_read(entry->hint_sample, bs, samp->dataLength); + gf_bs_del(bs); + entry->hint_sample->TransmissionTime = samp->DTS; + gf_isom_sample_del(&samp); + entry->hint_sample->sample_cache = gf_list_new(); + return GF_OK; +} + +static GF_ISOSample *gf_isom_get_data_sample(GF_HintSample *hsamp, GF_TrackBox *trak, u32 sample_num) +{ + GF_ISOSample *samp; + GF_HintDataCache *hdc; + u32 i, count; + count = gf_list_count(hsamp->sample_cache); + for (i=0; isample_cache, i); + if ((hdc->sample_num==sample_num) && (hdc->trak==trak)) return hdc->samp; + } + + samp = gf_isom_sample_new(); + Media_GetSample(trak->Media, sample_num, &samp, &i, 0, NULL); + if (!samp) return NULL; + GF_SAFEALLOC(hdc, GF_HintDataCache); + hdc->samp = samp; + hdc->sample_num = sample_num; + hdc->trak = trak; + /*we insert all new samples, since they're more likely to be fetched next (except for audio + interleaving and other multiplex)*/ + gf_list_insert(hsamp->sample_cache, hdc, 0); + return samp; +} + +GF_EXPORT +GF_Err gf_isom_next_hint_packet(GF_ISOFile *the_file, u32 trackNumber, char **pck_data, u32 *pck_size, Bool *disposable, Bool *repeated, u32 *trans_ts, u32 *sample_num) +{ + GF_RTPPacket *pck; + GF_Err e; + GF_BitStream *bs; + GF_TrackBox *trak, *ref_trak; + GF_HintSampleEntryBox *entry; + u32 i, count, ts; + s32 cts_off; + + *pck_data = NULL; + *pck_size = 0; + if (trans_ts) *trans_ts = 0; + if (disposable) *disposable = 0; + if (repeated) *repeated = 0; + if (sample_num) *sample_num = 0; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + e = Media_GetSampleDesc(trak->Media, 1, (GF_SampleEntryBox **) &entry, NULL); + if (e) return e; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_RTP_STSD: + break; + default: + return GF_NOT_SUPPORTED; + } + + if (!entry->hint_sample) { + e = gf_isom_load_next_hint_sample(the_file, trackNumber, trak, entry); + if (e) return e; + } + pck = (GF_RTPPacket *)gf_list_get(entry->hint_sample->packetTable, 0); + gf_list_rem(entry->hint_sample->packetTable, 0); + if (!pck) return GF_BAD_PARAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*write RTP header*/ + gf_bs_write_int(bs, 2, 2); /*version*/ + gf_bs_write_int(bs, pck->P_bit, 1); /*P bit*/ + gf_bs_write_int(bs, pck->X_bit, 1); /*X bit*/ + gf_bs_write_int(bs, 0, 4); /*CSRC count*/ + gf_bs_write_int(bs, pck->M_bit, 1); /*M bit*/ + gf_bs_write_int(bs, pck->payloadType, 7); /*payt*/ + gf_bs_write_u16(bs, entry->pck_sn); /*seq num*/ + entry->pck_sn++; + + /*look for CTS offset in TLV*/ + cts_off = 0; + count = gf_list_count(pck->TLV); + for (i=0; iTLV, i); + if (rtpo->type == GF_ISOM_BOX_TYPE_RTPO) { + cts_off = rtpo->timeOffset; + break; + } + } + /*TS - TODO check TS wrapping*/ + ts = (u32) (entry->hint_sample->TransmissionTime + pck->relativeTransTime + entry->ts_offset + cts_off); + gf_bs_write_u32(bs, ts ); + gf_bs_write_u32(bs, entry->ssrc); /*SSRC*/ + + /*then build all data*/ + count = gf_list_count(pck->DataTable); + for (i=0; iDataTable, i); + switch (dte->source) { + /*empty*/ + case 0: + break; + /*immediate data*/ + case 1: + gf_bs_write_data(bs, ((GF_ImmediateDTE *)dte)->data, ((GF_ImmediateDTE *)dte)->dataLength); + break; + /*sample data*/ + case 2: + { + GF_ISOSample *samp; + GF_SampleDTE *sdte = (GF_SampleDTE *)dte; + /*get track if not this one*/ + if (sdte->trackRefIndex != (s8) -1) { + if (!entry->hint_ref || !entry->hint_ref->trackIDs) { + gf_isom_hint_rtp_del(pck); + gf_bs_del(bs); + return GF_ISOM_INVALID_FILE; + } + ref_trak = gf_isom_get_track_from_id(trak->moov, entry->hint_ref->trackIDs[(u32)sdte->trackRefIndex]); + } else { + ref_trak = trak; + } + samp = gf_isom_get_data_sample(entry->hint_sample, ref_trak, sdte->sampleNumber); + if (!samp) { + gf_isom_hint_rtp_del(pck); + gf_bs_del(bs); + return GF_IO_ERR; + } + gf_bs_write_data(bs, samp->data + sdte->byteOffset, sdte->dataLength); + } + break; + /*sample desc data - currently NOT SUPPORTED !!!*/ + case 3: + break; + } + } + if (trans_ts) *trans_ts = ts; + if (disposable) *disposable = pck->B_bit; + if (repeated) *repeated = pck->R_bit; + if (sample_num) *sample_num = entry->cur_sample-1; + + gf_bs_get_content(bs, pck_data, pck_size); + gf_bs_del(bs); + gf_isom_hint_rtp_del(pck); + if (!gf_list_count(entry->hint_sample->packetTable)) { + gf_isom_hint_sample_del(entry->hint_sample); + entry->hint_sample = NULL; + } + return GF_OK; +} diff --git a/src/isomedia/isma_sample.c b/src/isomedia/isma_sample.c new file mode 100644 index 0000000..ef7c0f0 --- /dev/null +++ b/src/isomedia/isma_sample.c @@ -0,0 +1,492 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato / Jean Le Feuvre 2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +GF_ISMASample *gf_isom_ismacryp_new_sample() +{ + GF_ISMASample *tmp = (GF_ISMASample *) malloc(sizeof(GF_ISMASample)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_ISMASample)); + return tmp; +} +GF_EXPORT +void gf_isom_ismacryp_delete_sample(GF_ISMASample *samp) +{ + if (!samp) return; + if (samp->data && samp->dataLength) free(samp->data); + if (samp->key_indicator) free(samp->key_indicator); + free(samp); +} + + +GF_ISMASample *gf_isom_ismacryp_sample_from_data(char *data, u32 dataLength, Bool use_selective_encryption, u8 KI_length, u8 IV_length) +{ + GF_ISMASample *s; + GF_BitStream *bs; + /*empty text sample*/ + if (!data || !dataLength) { + return gf_isom_ismacryp_new_sample(); + } + + s = gf_isom_ismacryp_new_sample(); + + /*empty sample*/ + if (!data || !dataLength) return s; + + bs = gf_bs_new(data, dataLength, GF_BITSTREAM_READ); + + s->dataLength = dataLength; + s->IV_length = IV_length; + s->KI_length = KI_length; + + if (use_selective_encryption) { + s->flags = GF_ISOM_ISMA_USE_SEL_ENC; + if (s->dataLength < 1) goto exit; + if (gf_bs_read_int(bs, 1)) s->flags |= GF_ISOM_ISMA_IS_ENCRYPTED; + gf_bs_read_int(bs, 7); + s->dataLength -= 1; + } else { + s->flags = GF_ISOM_ISMA_IS_ENCRYPTED; + } + if (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) { + if (IV_length != 0) { + if (s->dataLength < IV_length) goto exit; + s->IV = gf_bs_read_long_int(bs, 8*IV_length); + s->dataLength -= IV_length; + } + if (KI_length) { + if (s->dataLength < KI_length) goto exit; + s->key_indicator = (u8 *)malloc(KI_length); + gf_bs_read_data(bs, (char*)s->key_indicator, KI_length); + s->dataLength -= KI_length; + } + } + s->data = (char*)malloc(sizeof(char)*s->dataLength); + gf_bs_read_data(bs, s->data, s->dataLength); + gf_bs_del(bs); + return s; + +exit: + gf_isom_ismacryp_delete_sample(s); + return NULL; +} + +GF_Err gf_isom_ismacryp_sample_to_sample(GF_ISMASample *s, GF_ISOSample *dest) +{ + GF_BitStream *bs; + if (!s || !dest) return GF_BAD_PARAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + if (s->flags & GF_ISOM_ISMA_USE_SEL_ENC) { + gf_bs_write_int(bs, (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + } + if (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) { + if (s->IV_length) gf_bs_write_long_int(bs, (s64) s->IV, 8*s->IV_length); + if (s->KI_length) gf_bs_write_data(bs, (char*)s->key_indicator, s->KI_length); + } + gf_bs_write_data(bs, s->data, s->dataLength); + if (dest->data) free(dest->data); + dest->data = NULL; + dest->dataLength = 0; + gf_bs_get_content(bs, &dest->data, &dest->dataLength); + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_ISMASample *gf_isom_get_ismacryp_sample(GF_ISOFile *the_file, u32 trackNumber, GF_ISOSample *samp, u32 sampleDescriptionIndex) +{ + GF_TrackBox *trak; + GF_ISMASampleFormatBox *fmt; + GF_SampleEntryBox *sea; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return NULL; + + Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL); + /*non-encrypted or non-ISMA*/ + if (!sea || !sea->protection_info + || !sea->protection_info->scheme_type + || !sea->protection_info->info + ) { + return NULL; + } + /*ISMA*/ + if (sea->protection_info->scheme_type->scheme_type == GF_ISOM_ISMACRYP_SCHEME) { + fmt = sea->protection_info->info->isfm; + if (!fmt) return NULL; + return gf_isom_ismacryp_sample_from_data(samp->data, samp->dataLength, sea->protection_info->info->isfm->selective_encryption, sea->protection_info->info->isfm->key_indicator_length, sea->protection_info->info->isfm->IV_length); + } + /*OMA*/ + else if (sea->protection_info->scheme_type->scheme_type == GF_4CC('o','d','k','m') ) { + if (!sea->protection_info->info->okms) return NULL; + fmt = sea->protection_info->info->okms->fmt; + + if (fmt) { + return gf_isom_ismacryp_sample_from_data(samp->data, samp->dataLength, fmt->selective_encryption, fmt->key_indicator_length, fmt->IV_length); + } + /*OMA default: no selective encryption, one key, 128 bit IV*/ + return gf_isom_ismacryp_sample_from_data(samp->data, samp->dataLength, 0, 0, 128); + } + return NULL; +} + + +GF_EXPORT +u32 gf_isom_is_media_encrypted(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *sea; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL); + /*non-encrypted or non-ISMA*/ + if (!sea || !sea->protection_info || !sea->protection_info->scheme_type) return 0; + return sea->protection_info->scheme_type->scheme_type; +} + +GF_EXPORT +Bool gf_isom_is_ismacryp_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *sea; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL); + /*non-encrypted or non-ISMA*/ + if (!sea + || !sea->protection_info + || !sea->protection_info->scheme_type + || (sea->protection_info->scheme_type->scheme_type != GF_ISOM_ISMACRYP_SCHEME) + || !sea->protection_info->info + || !sea->protection_info->info->ikms + || !sea->protection_info->info->isfm + ) + return 0; + + return 1; +} + +GF_EXPORT +Bool gf_isom_is_omadrm_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *sea; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL); + /*non-encrypted or non-ISMA*/ + if (!sea + || !sea->protection_info + || !sea->protection_info->scheme_type + || (sea->protection_info->scheme_type->scheme_type != GF_4CC('o','d','k','m') ) + || !sea->protection_info->info + || !sea->protection_info->info->okms + || !sea->protection_info->info->okms->hdr + ) + return 0; + + return 1; +} + +/*retrieves ISMACryp info for the given track & SDI*/ +GF_EXPORT +GF_Err gf_isom_get_ismacryp_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, u32 *outSchemeType, u32 *outSchemeVersion, const char **outSchemeURI, const char **outKMS_URI, Bool *outSelectiveEncryption, u32 *outIVLength, u32 *outKeyIndicationLength) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *sea; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL); + /*non-encrypted or non-ISMA*/ + if (!sea || !sea->protection_info) return GF_BAD_PARAM; + if (!sea->protection_info->scheme_type || !sea->protection_info->original_format) return GF_NON_COMPLIANT_BITSTREAM; + + if (outOriginalFormat) { + *outOriginalFormat = sea->protection_info->original_format->data_format; + if (IsMP4Description(sea->protection_info->original_format->data_format)) *outOriginalFormat = GF_ISOM_SUBTYPE_MPEG4; + } + if (outSchemeType) *outSchemeType = sea->protection_info->scheme_type->scheme_type; + if (outSchemeVersion) *outSchemeVersion = sea->protection_info->scheme_type->scheme_version; + if (outSchemeURI) *outSchemeURI = sea->protection_info->scheme_type->URI; + + if (sea->protection_info->info && sea->protection_info->info->ikms) { + if (outKMS_URI) *outKMS_URI = sea->protection_info->info->ikms->URI; + } else { + if (outKMS_URI) *outKMS_URI = NULL; + } + if (sea->protection_info->info && sea->protection_info->info->isfm) { + if (outSelectiveEncryption) *outSelectiveEncryption = sea->protection_info->info->isfm->selective_encryption; + if (outIVLength) *outIVLength = sea->protection_info->info->isfm->IV_length; + if (outKeyIndicationLength) *outKeyIndicationLength = sea->protection_info->info->isfm->key_indicator_length; + } else { + if (outSelectiveEncryption) *outSelectiveEncryption = 0; + if (outIVLength) *outIVLength = 0; + if (outKeyIndicationLength) *outKeyIndicationLength = 0; + } + return GF_OK; +} + + +/*retrieves ISMACryp info for the given track & SDI*/ +GF_EXPORT +GF_Err gf_isom_get_omadrm_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, + u32 *outSchemeType, u32 *outSchemeVersion, + const char **outContentID, const char **outRightsIssuerURL, const char **outTextualHeaders, u32 *outTextualHeadersLen, u64 *outPlaintextLength, u32 *outEncryptionType, Bool *outSelectiveEncryption, u32 *outIVLength, u32 *outKeyIndicationLength) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *sea; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL); + /*non-encrypted or non-ISMA*/ + if (!sea || !sea->protection_info) return GF_BAD_PARAM; + if (!sea->protection_info->scheme_type || !sea->protection_info->original_format) return GF_NON_COMPLIANT_BITSTREAM; + if (!sea->protection_info->info || !sea->protection_info->info->okms || !sea->protection_info->info->okms->hdr) return GF_NON_COMPLIANT_BITSTREAM; + + if (outOriginalFormat) { + *outOriginalFormat = sea->protection_info->original_format->data_format; + if (IsMP4Description(sea->protection_info->original_format->data_format)) *outOriginalFormat = GF_ISOM_SUBTYPE_MPEG4; + } + if (outSchemeType) *outSchemeType = sea->protection_info->scheme_type->scheme_type; + if (outSchemeVersion) *outSchemeVersion = sea->protection_info->scheme_type->scheme_version; + if (outContentID) *outContentID = sea->protection_info->info->okms->hdr->ContentID; + if (outRightsIssuerURL) *outRightsIssuerURL = sea->protection_info->info->okms->hdr->RightsIssuerURL; + if (outTextualHeaders) { + *outTextualHeaders = sea->protection_info->info->okms->hdr->TextualHeaders; + if (outTextualHeadersLen) *outTextualHeadersLen = sea->protection_info->info->okms->hdr->TextualHeadersLen; + } + if (outPlaintextLength) *outPlaintextLength = sea->protection_info->info->okms->hdr->PlaintextLength; + if (outEncryptionType) *outEncryptionType = sea->protection_info->info->okms->hdr->EncryptionMethod; + + if (sea->protection_info->info && sea->protection_info->info->okms && sea->protection_info->info->okms->fmt) { + if (outSelectiveEncryption) *outSelectiveEncryption = sea->protection_info->info->okms->fmt->selective_encryption; + if (outIVLength) *outIVLength = sea->protection_info->info->okms->fmt->IV_length; + if (outKeyIndicationLength) *outKeyIndicationLength = sea->protection_info->info->okms->fmt->key_indicator_length; + } else { + if (outSelectiveEncryption) *outSelectiveEncryption = 0; + if (outIVLength) *outIVLength = 0; + if (outKeyIndicationLength) *outKeyIndicationLength = 0; + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY + +GF_Err gf_isom_remove_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + GF_SampleEntryBox *sea; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !StreamDescriptionIndex) return GF_BAD_PARAM; + + Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &sea, NULL); + /*non-encrypted or non-ISMA*/ + if (!sea || !sea->protection_info) return GF_BAD_PARAM; + if (!sea->protection_info->scheme_type || !sea->protection_info->original_format) return GF_NON_COMPLIANT_BITSTREAM; + + sea->type = sea->protection_info->original_format->data_format; + gf_isom_box_del((GF_Box *)sea->protection_info); + sea->protection_info = NULL; + if (sea->type == GF_4CC('2','6','4','b')) sea->type = GF_ISOM_BOX_TYPE_AVC1; + return GF_OK; +} + +GF_Err gf_isom_change_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, char *scheme_uri, char *kms_uri) +{ + GF_TrackBox *trak; + GF_Err e; + GF_SampleEntryBox *sea; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !StreamDescriptionIndex) return GF_BAD_PARAM; + + Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &sea, NULL); + /*non-encrypted or non-ISMA*/ + if (!sea || !sea->protection_info) return GF_BAD_PARAM; + if (!sea->protection_info->scheme_type || !sea->protection_info->original_format) return GF_NON_COMPLIANT_BITSTREAM; + + if (scheme_uri) { + free(sea->protection_info->scheme_type->URI); + sea->protection_info->scheme_type->URI = strdup(scheme_uri); + } + if (kms_uri) { + free(sea->protection_info->info->ikms->URI); + sea->protection_info->info->ikms->URI = strdup(kms_uri); + } + return GF_OK; +} + + +GF_Err gf_isom_set_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, u32 scheme_type, + u32 scheme_version, char *scheme_uri, char *kms_URI, + Bool selective_encryption, u32 KI_length, u32 IV_length) +{ + u32 original_format; + GF_Err e; + GF_SampleEntryBox *sea; + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, desc_index, &sea, NULL); + if (e) return e; + + /* Replacing the Media Type */ + switch (sea->type) { + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + original_format = sea->type; + sea->type = GF_ISOM_BOX_TYPE_ENCA; + break; + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_D263: + original_format = sea->type; + sea->type = GF_ISOM_BOX_TYPE_ENCV; + break; + /*special case for AVC1*/ + case GF_ISOM_BOX_TYPE_AVC1: + original_format = GF_4CC('2','6','4','b'); + sea->type = GF_ISOM_BOX_TYPE_ENCV; + break; + case GF_ISOM_BOX_TYPE_MP4S: + original_format = sea->type; + sea->type = GF_ISOM_BOX_TYPE_ENCS; + break; + default: + return GF_BAD_PARAM; + } + + sea->protection_info = (GF_ProtectionInfoBox *)sinf_New(); + sea->protection_info->scheme_type = (GF_SchemeTypeBox *)schm_New(); + sea->protection_info->scheme_type->scheme_type = scheme_type; + sea->protection_info->scheme_type->scheme_version = scheme_version; + if (scheme_uri) { + sea->protection_info->scheme_type->flags |= 0x000001; + sea->protection_info->scheme_type->URI = strdup(scheme_uri); + } + sea->protection_info->original_format = (GF_OriginalFormatBox *)frma_New(); + sea->protection_info->original_format->data_format = original_format; + sea->protection_info->info = (GF_SchemeInformationBox *)schi_New(); + + sea->protection_info->info->ikms = (GF_ISMAKMSBox *)iKMS_New(); + sea->protection_info->info->ikms->URI = strdup(kms_URI); + + sea->protection_info->info->isfm = (GF_ISMASampleFormatBox *)iSFM_New(); + sea->protection_info->info->isfm->selective_encryption = selective_encryption; + sea->protection_info->info->isfm->key_indicator_length = KI_length; + sea->protection_info->info->isfm->IV_length = IV_length; + return GF_OK; +} + +GF_Err gf_isom_set_oma_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, + char *contentID, char *kms_URI, u32 encryption_type, u64 plainTextLength, char *textual_headers, u32 textual_headers_len, + Bool selective_encryption, u32 KI_length, u32 IV_length) +{ + u32 original_format; + GF_Err e; + GF_SampleEntryBox *sea; + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, desc_index, &sea, NULL); + if (e) return e; + + /* Replacing the Media Type */ + switch (sea->type) { + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + original_format = sea->type; + sea->type = GF_ISOM_BOX_TYPE_ENCA; + break; + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_D263: + original_format = sea->type; + sea->type = GF_ISOM_BOX_TYPE_ENCV; + break; + case GF_ISOM_BOX_TYPE_MP4S: + original_format = sea->type; + sea->type = GF_ISOM_BOX_TYPE_ENCS; + break; + default: + return GF_BAD_PARAM; + } + + sea->protection_info = (GF_ProtectionInfoBox *)sinf_New(); + sea->protection_info->scheme_type = (GF_SchemeTypeBox *)schm_New(); + sea->protection_info->scheme_type->scheme_type = GF_4CC('o','d','k','m'); + sea->protection_info->scheme_type->scheme_version = 0x00000200; + + sea->protection_info->original_format = (GF_OriginalFormatBox *)frma_New(); + sea->protection_info->original_format->data_format = original_format; + sea->protection_info->info = (GF_SchemeInformationBox *)schi_New(); + + sea->protection_info->info->okms = (GF_OMADRMKMSBox *)odkm_New(); + sea->protection_info->info->okms->fmt = (GF_OMADRMAUFormatBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_ODAF); + sea->protection_info->info->okms->fmt->selective_encryption = selective_encryption; + sea->protection_info->info->okms->fmt->key_indicator_length = KI_length; + sea->protection_info->info->okms->fmt->IV_length = IV_length; + + sea->protection_info->info->okms->hdr = (GF_OMADRMCommonHeaderBox*)ohdr_New(); + sea->protection_info->info->okms->hdr->EncryptionMethod = encryption_type; + sea->protection_info->info->okms->hdr->PaddingScheme = (encryption_type==0x01) ? 1 : 0; + sea->protection_info->info->okms->hdr->PlaintextLength = plainTextLength; + if (contentID) sea->protection_info->info->okms->hdr->ContentID = strdup(contentID); + if (kms_URI) sea->protection_info->info->okms->hdr->RightsIssuerURL = strdup(kms_URI); + if (textual_headers) { + sea->protection_info->info->okms->hdr->TextualHeaders = malloc(sizeof(char)*textual_headers_len); + memcpy(sea->protection_info->info->okms->hdr->TextualHeaders, textual_headers, sizeof(char)*textual_headers_len); + sea->protection_info->info->okms->hdr->TextualHeadersLen = textual_headers_len; + } + return GF_OK; +} + +#endif diff --git a/src/isomedia/isom_intern.c b/src/isomedia/isom_intern.c new file mode 100644 index 0000000..23bae52 --- /dev/null +++ b/src/isomedia/isom_intern.c @@ -0,0 +1,704 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +/************************************************************** + Some Local functions for movie creation +**************************************************************/ +GF_Err gf_isom_parse_root_box(GF_Box **outBox, GF_BitStream *bs, u64 *bytesExpected); + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +GF_Err MergeFragment(GF_MovieFragmentBox *moof, GF_ISOFile *mov) +{ + u32 i, j; + u64 MaxDur; + GF_TrackFragmentBox *traf; + GF_TrackBox *trak; + + GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, u64 *moof_offset); + + MaxDur = 0; + + //we shall have a MOOV and its MVEX BEFORE any MOOF + if (!mov->moov || !mov->moov->mvex) return GF_ISOM_INVALID_FILE; + //and all fragments must be continous + if (mov->NextMoofNumber + 1 != moof->mfhd->sequence_number) return GF_ISOM_INVALID_FILE; + + i=0; + while ((traf = (GF_TrackFragmentBox*)gf_list_enum(moof->TrackList, &i))) { + if (!traf->tfhd) { + trak = NULL; + traf->trex = NULL; + } else { + trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID); + j=0; + while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) { + if (traf->trex->trackID == traf->tfhd->trackID) break; + traf->trex = NULL; + } + } + if (!trak || !traf->trex) return GF_ISOM_INVALID_FILE; + + //NB we can modify the movie data-offset info since we are in the middle of + //parsing an box, so next box readin will reset it... + MergeTrack(trak, traf, &mov->current_top_box_start); + + //update trak duration + SetTrackDuration(trak); + if (trak->Header->duration > MaxDur) + MaxDur = trak->Header->duration; + } + + mov->NextMoofNumber += 1; + //update movie duration + if (mov->moov->mvhd->duration < MaxDur) mov->moov->mvhd->duration = MaxDur; + return GF_OK; +} + +#endif + + +GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing) +{ + GF_Box *a; + u64 totSize; + GF_Err e = GF_OK; + + totSize = 0; + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + /*restart from where we stoped last*/ + totSize = mov->current_top_box_start; + gf_bs_seek(mov->movieFileMap->bs, mov->current_top_box_start); +#endif + + + /*while we have some data, parse our boxes*/ + while (gf_bs_available(mov->movieFileMap->bs)) { + *bytesMissing = 0; +#ifndef GPAC_ISOM_NO_FRAGMENTS + mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs); +#endif + + e = gf_isom_parse_root_box(&a, mov->movieFileMap->bs, bytesMissing); + + if (e >= 0) { + e = GF_OK; + } else if (e == GF_ISOM_INCOMPLETE_FILE) { + /*our mdat is uncomplete, only valid for READ ONLY files...*/ + if (mov->openMode != GF_ISOM_OPEN_READ) { + return GF_ISOM_INVALID_FILE; + } + return e; + } else { + return e; + } + + switch (a->type) { + /*MOOV box*/ + case GF_ISOM_BOX_TYPE_MOOV: + if (mov->moov) return GF_ISOM_INVALID_FILE; + mov->moov = (GF_MovieBox *)a; + /*set our pointer to the movie*/ + mov->moov->mov = mov; + e = gf_list_add(mov->TopBoxes, a); + if (e) return e; + totSize += a->size; + break; + + /*META box*/ + case GF_ISOM_BOX_TYPE_META: + if (mov->meta) return GF_ISOM_INVALID_FILE; + mov->meta = (GF_MetaBox *)a; + e = gf_list_add(mov->TopBoxes, a); + if (e) return e; + totSize += a->size; + break; + + /*we only keep the MDAT in READ for dump purposes*/ + case GF_ISOM_BOX_TYPE_MDAT: + totSize += a->size; + if (mov->openMode == GF_ISOM_OPEN_READ) { + if (!mov->mdat) { + mov->mdat = (GF_MediaDataBox *) a; + e = gf_list_add(mov->TopBoxes, mov->mdat); + if (e) return e; + } +#ifndef GPAC_ISOM_NO_FRAGMENTS + else if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) gf_list_add(mov->TopBoxes, a); +#endif + else gf_isom_box_del(a); + } + /*if we don't have any MDAT yet, create one (edit-write mode) + We only work with one mdat, but we're puting it at the place + of the first mdat found when opening a file for editing*/ + else if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ)) { + gf_isom_box_del(a); + mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT); + e = gf_list_add(mov->TopBoxes, mov->mdat); + if (e) return e; + } else { + gf_isom_box_del(a); + } + break; + case GF_ISOM_BOX_TYPE_FTYP: + /*ONE AND ONLY ONE FTYP*/ + if (mov->brand) { + gf_isom_box_del(a); + return GF_ISOM_INVALID_FILE; + } + mov->brand = (GF_FileTypeBox *)a; + totSize += a->size; + e = gf_list_add(mov->TopBoxes, a); + break; + + case GF_ISOM_BOX_TYPE_PDIN: + /*ONE AND ONLY ONE PDIN*/ + if (mov->pdin) { + gf_isom_box_del(a); + return GF_ISOM_INVALID_FILE; + } + mov->pdin = (GF_ProgressiveDownloadBox *) a; + totSize += a->size; + e = gf_list_add(mov->TopBoxes, a); + break; + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + case GF_ISOM_BOX_TYPE_MOOF: + ((GF_MovieFragmentBox *)a)->mov = mov; + + totSize += a->size; + /*read & debug: store at root level*/ + if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) { + gf_list_add(mov->TopBoxes, a); + } else { + /*merge all info*/ + e = MergeFragment((GF_MovieFragmentBox *)a, mov); + gf_isom_box_del(a); + } + break; +#endif + case GF_4CC('j','P',' ',' '): + { + GF_UnknownBox *box = (GF_UnknownBox*)a; + u8 *c = box->data; + if ((box->dataSize==4) + && (GF_4CC(c[0],c[1],c[2],c[3])==(u32)0x0D0A870A)) + mov->is_jp2 = 1; + + gf_isom_box_del(a); + } + break; + + default: + totSize += a->size; + e = gf_list_add(mov->TopBoxes, a); + break; + } + } + + /*we need at least moov or meta*/ + if (!mov->moov && !mov->meta) return GF_ISOM_INVALID_FILE; + /*we MUST have movie header*/ + if (mov->moov && !mov->moov->mvhd) return GF_ISOM_INVALID_FILE; + /*we MUST have meta handler*/ + if (mov->meta && !mov->meta->handler) return GF_ISOM_INVALID_FILE; + +#ifndef GPAC_READ_ONLY + + if (mov->moov) { + /*set the default interleaving time*/ + mov->interleavingTime = mov->moov->mvhd->timeScale; + +#ifndef GPAC_ISOM_NO_FRAGMENTS + /*not in open mode and successfully loaded the entire file, destroy all fragment + FIXME: we may need to keet it when trying http streaming of fragments...*/ + if (!(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) && mov->moov->mvex) { + gf_isom_box_del((GF_Box *)mov->moov->mvex); + mov->moov->mvex = NULL; + } +#endif + + } +#endif + + return GF_OK; +} + + +GF_ISOFile *gf_isom_new_movie() +{ + GF_ISOFile *mov = (GF_ISOFile*)malloc(sizeof(GF_ISOFile)); + if (mov == NULL) { + gf_isom_set_last_error(NULL, GF_OUT_OF_MEM); + return NULL; + } + memset(mov, 0, sizeof(GF_ISOFile)); + + /*init the boxes*/ + mov->TopBoxes = gf_list_new(); + if (!mov->TopBoxes) { + gf_isom_set_last_error(NULL, GF_OUT_OF_MEM); + free(mov); + return NULL; + } + + /*default storage mode is flat*/ + mov->storageMode = GF_ISOM_STORE_FLAT; + return mov; +} + +//Create and parse the movie for READ - EDIT only +GF_ISOFile *gf_isom_open_file(const char *fileName, u32 OpenMode, const char *tmp_dir) +{ + GF_Err e; + u64 bytes; + GF_ISOFile *mov = gf_isom_new_movie(); + if (! mov) return NULL; + + mov->fileName = strdup(fileName); + mov->openMode = OpenMode; + + if ( (OpenMode == GF_ISOM_OPEN_READ) || (OpenMode == GF_ISOM_OPEN_READ_DUMP) ) { + //always in read ... + mov->openMode = GF_ISOM_OPEN_READ; + mov->es_id_default_sync = -1; + //for open, we do it the regular way and let the GF_DataMap assign the appropriate struct + //this can be FILE (the only one supported...) as well as remote + //(HTTP, ...),not suported yet + //the bitstream IS PART OF the GF_DataMap + //as this is read-only, use a FileMapping. this is the only place where + //we use file mapping + e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_READ_ONLY, &mov->movieFileMap); + if (e) { + gf_isom_set_last_error(NULL, e); + gf_isom_delete_movie(mov); + return NULL; + } + +#ifndef GPAC_ISOM_NO_FRAGMENTS + if (OpenMode == GF_ISOM_OPEN_READ_DUMP) mov->FragmentsFlags |= GF_ISOM_FRAG_READ_DEBUG; +#endif + + } else { + +#ifdef GPAC_READ_ONLY + //not allowed for READ_ONLY lib + gf_isom_delete_movie(mov); + gf_isom_set_last_error(NULL, GF_ISOM_INVALID_MODE); + return NULL; + +#else + + //set a default output name for edited file + mov->finalName = (char*)malloc(strlen(fileName) + 5); + if (!mov->finalName) { + gf_isom_set_last_error(NULL, GF_OUT_OF_MEM); + gf_isom_delete_movie(mov); + return NULL; + } + strcpy(mov->finalName, "out_"); + strcat(mov->finalName, fileName); + + //open the original file with edit tag + e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_EDIT, &mov->movieFileMap); + //if the file doesn't exist, we assume it's wanted and create one from scratch + if (e) { + gf_isom_set_last_error(NULL, e); + gf_isom_delete_movie(mov); + return NULL; + } + //and create a temp fileName for the edit + e = gf_isom_datamap_new("mp4_tmp_edit", tmp_dir, GF_ISOM_DATA_MAP_WRITE, & mov->editFileMap); + if (e) { + gf_isom_set_last_error(NULL, e); + gf_isom_delete_movie(mov); + return NULL; + } + mov->es_id_default_sync = -1; + +#endif + } + + //OK, let's parse the movie... + mov->LastError = gf_isom_parse_movie_boxes(mov, &bytes); + if (mov->LastError) { + gf_isom_set_last_error(NULL, mov->LastError); + gf_isom_delete_movie(mov); + return NULL; + } + return mov; +} + + +u64 gf_isom_get_mp4time() +{ + u32 calctime, msec; + u64 ret; + gf_utc_time_since_1970(&calctime, &msec); + calctime += GF_ISOM_MAC_TIME_OFFSET; + ret = calctime; + return ret; +} + +void gf_isom_delete_movie(GF_ISOFile *mov) +{ + //these are our two main files + if (mov->movieFileMap) gf_isom_datamap_del(mov->movieFileMap); + +#ifndef GPAC_READ_ONLY + if (mov->editFileMap) { + gf_isom_datamap_del(mov->editFileMap); + } + if (mov->finalName) free(mov->finalName); +#endif + + gf_isom_box_array_del(mov->TopBoxes); + + if (mov->fileName) free(mov->fileName); + free(mov); +} + +GF_TrackBox *gf_isom_get_track_from_id(GF_MovieBox *moov, u32 trackID) +{ + u32 i, count; + GF_TrackBox *trak; + if (!moov || !trackID) return NULL; + + count = gf_list_count(moov->trackList); + for (i = 0; itrackList, i); + if (trak->Header->trackID == trackID) return trak; + } + return NULL; +} + +GF_TrackBox *gf_isom_get_track_from_file(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + if (!movie) return NULL; + trak = gf_isom_get_track(movie->moov, trackNumber); + if (!trak) movie->LastError = GF_BAD_PARAM; + return trak; +} + + +//WARNING: MOVIETIME IS EXPRESSED IN MEDIA TS +GF_Err GetMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *MediaTime, s64 *SegmentStartTime, s64 *MediaOffset, u8 *useEdit) +{ + GF_Err e; + u32 i; + u64 time, lastSampleTime, m_time; + s64 mtime; + GF_EdtsEntry *ent; + Double scale_ts; + u32 sampleNumber, prevSampleNumber; + u64 firstDTS; + GF_SampleTableBox *stbl = trak->Media->information->sampleTable; + + *useEdit = 1; + *MediaTime = 0; + //no segment yet... + *SegmentStartTime = -1; + *MediaOffset = -1; + if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) { + return GF_ISOM_INVALID_FILE; + } + + //no samples... + if (!stbl->SampleSize->sampleCount) { + lastSampleTime = 0; + } else { + lastSampleTime = trak->Media->mediaHeader->duration; + } + + //No edits, 1 to 1 mapping + if (! trak->editBox || !trak->editBox->editList) { + *MediaTime = movieTime; + //check this is in our media time line + if (*MediaTime > lastSampleTime) *MediaTime = lastSampleTime; + *useEdit = 0; + return GF_OK; + } + //browse the edit list and get the time + scale_ts = trak->moov->mvhd->timeScale; + scale_ts /= trak->Media->mediaHeader->timeScale; + scale_ts *= ((s64)movieTime + 1); + m_time = (u64) (scale_ts); + + time = 0; + ent = NULL; + i=0; + while ((ent = (GF_EdtsEntry *)gf_list_enum(trak->editBox->editList->entryList, &i))) { + if (time + ent->segmentDuration > m_time) { + goto ent_found; + } + time += ent->segmentDuration; + } + //we had nothing in the list (strange file but compliant...) + //return the 1 to 1 mapped vale of the last media sample + if (!ent) { + *MediaTime = movieTime; + //check this is in our media time line + if (*MediaTime > lastSampleTime) *MediaTime = lastSampleTime; + *useEdit = 0; + return GF_OK; + } + //request for a bigger time that what we can give: return the last sample (undefined behavior...) + *MediaTime = lastSampleTime; + return GF_OK; + +ent_found: + //OK, we found our entry, set the SegmentTime + *SegmentStartTime = time; + + //we request an empty list, there's no media here... + if (ent->mediaTime < 0) { + *MediaTime = 0; + return GF_OK; + } + //we request a dwell edit + if (! ent->mediaRate) { + *MediaTime = ent->mediaTime; + //no media offset + *MediaOffset = 0; + *useEdit = 2; + return GF_OK; + } + + /*WARNING: this can be "-1" when doing searchForward mode (to prevent jumping to next entry)*/ + mtime = ent->mediaTime + movieTime - (time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale); + if (mtime<0) mtime = 0; + *MediaTime = (u64) mtime; + +#if 0 + // + //Sanity check: is the requested time valid ? This is to cope with wrong EditLists + //we have the translated time, but we need to make sure we have a sample at this time ... + //we have to find a COMPOSITION time + e = findEntryForTime(stbl, (u32) *MediaTime, 1, &sampleNumber, &prevSampleNumber); + if (e) return e; + + //first case: our time is after the last sample DTS (it's a broken editList somehow) + //set the media time to the last sample + if (!sampleNumber && !prevSampleNumber) { + *MediaTime = lastSampleTime; + return GF_OK; + } + //get the appropriated sample + if (!sampleNumber) sampleNumber = prevSampleNumber; + + stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &DTS); + CTS = 0; + if (stbl->CompositionOffset) stbl_GetSampleCTS(stbl->CompositionOffset, sampleNumber, &CTS); +#endif + + //now get the entry sample (the entry time gives the CTS, and we need the DTS + e = findEntryForTime(stbl, (u32) ent->mediaTime, 1, &sampleNumber, &prevSampleNumber); + if (e) return e; + + //oops, the mediaTime indicates a sample that is not in our media ! + if (!sampleNumber && !prevSampleNumber) { + *MediaTime = lastSampleTime; + return GF_ISOM_INVALID_FILE; + } + if (!sampleNumber) sampleNumber = prevSampleNumber; + + stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &firstDTS); + + //and store the "time offset" of the desired sample in this segment + //this is weird, used to rebuild the timeStamp when reading from the track, not the + //media ... + *MediaOffset = firstDTS; + return GF_OK; +} + +GF_Err GetNextMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *OutMovieTime) +{ + u32 i; + u64 time; + GF_EdtsEntry *ent; + + *OutMovieTime = 0; + if (! trak->editBox || !trak->editBox->editList) return GF_BAD_PARAM; + + time = 0; + ent = NULL; + i=0; + while ((ent = (GF_EdtsEntry *)gf_list_enum(trak->editBox->editList->entryList, &i))) { + if (time * trak->Media->mediaHeader->timeScale >= movieTime * trak->moov->mvhd->timeScale) { + /*skip empty edits*/ + if (ent->mediaTime >= 0) { + *OutMovieTime = time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale; + if (*OutMovieTime>0) *OutMovieTime -= 1; + return GF_OK; + } + } + time += ent->segmentDuration; + } + //request for a bigger time that what we can give: return the last sample (undefined behavior...) + *OutMovieTime = trak->moov->mvhd->duration; + return GF_EOS; +} + +GF_Err GetPrevMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *OutMovieTime) +{ + u32 i; + u64 time; + GF_EdtsEntry *ent; + + *OutMovieTime = 0; + if (! trak->editBox || !trak->editBox->editList) return GF_BAD_PARAM; + + time = 0; + ent = NULL; + i=0; + while ((ent = (GF_EdtsEntry *)gf_list_enum(trak->editBox->editList->entryList, &i))) { + if (ent->mediaTime == -1) { + if ( (time + ent->segmentDuration) * trak->Media->mediaHeader->timeScale >= movieTime * trak->moov->mvhd->timeScale) { + *OutMovieTime = time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale; + return GF_OK; + } + continue; + } + /*get the first entry whose end is greater than or equal to the desired time*/ + time += ent->segmentDuration; + if ( time * trak->Media->mediaHeader->timeScale >= movieTime * trak->moov->mvhd->timeScale) { + *OutMovieTime = time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale; + return GF_OK; + } + } + *OutMovieTime = 0; + return GF_OK; +} + +#ifndef GPAC_READ_ONLY + +void gf_isom_insert_moov(GF_ISOFile *file) +{ + u64 now; + GF_MovieHeaderBox *mvhd; + if (file->moov) return; + + //OK, create our boxes (mvhd, iods, ...) + file->moov = (GF_MovieBox *) moov_New(); + file->moov->mov = file; + //Header SetUp + now = gf_isom_get_mp4time(); + mvhd = (GF_MovieHeaderBox *) mvhd_New(); + mvhd->creationTime = now; + mvhd->modificationTime = now; + mvhd->nextTrackID = 1; + //600 is our default movie TimeScale + mvhd->timeScale = 600; + + file->interleavingTime = mvhd->timeScale; + moov_AddBox((GF_Box*)file->moov, (GF_Box *)mvhd); + gf_list_add(file->TopBoxes, file->moov); +} + +//Create the movie for WRITE only +GF_ISOFile *gf_isom_create_movie(const char *fileName, u32 OpenMode, const char *tmp_dir) +{ + GF_Err e; + + GF_ISOFile *mov = gf_isom_new_movie(); + if (!mov) return NULL; + mov->openMode = OpenMode; + //then set up our movie + + //in WRITE, the input dataMap is ALWAYS NULL + mov->movieFileMap = NULL; + + //but we have the edit one + if (OpenMode == GF_ISOM_OPEN_WRITE) { + //THIS IS NOT A TEMP FILE, WRITE mode is used for "live capture" + //this file will be the final file... + mov->fileName = strdup(fileName); + e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_WRITE, & mov->editFileMap); + if (e) goto err_exit; + + /*brand is set to ISOM by default - it may be touched until sample data is added to track*/ + gf_isom_set_brand_info( (GF_ISOFile *) mov, GF_ISOM_BRAND_ISOM, 1); + } else { + //we are in EDIT mode but we are creating the file -> temp file + mov->finalName = (char*)malloc(strlen(fileName) + 1); + strcpy(mov->finalName, fileName); + e = gf_isom_datamap_new("mp4_tmp_edit", tmp_dir, GF_ISOM_DATA_MAP_WRITE, & mov->editFileMap); + if (e) { + gf_isom_set_last_error(NULL, e); + gf_isom_delete_movie(mov); + return NULL; + } + //brand is set to ISOM by default + gf_isom_set_brand_info( (GF_ISOFile *) mov, GF_ISOM_BRAND_ISOM, 1); + } + + //create an MDAT + mov->mdat = (GF_MediaDataBox *) mdat_New(); + gf_list_add(mov->TopBoxes, mov->mdat); + + //default behaviour is capture mode, no interleaving (eg, no rewrite of mdat) + mov->storageMode = GF_ISOM_STORE_FLAT; + return mov; + +err_exit: + gf_isom_set_last_error(NULL, e); + if (mov) gf_isom_delete_movie(mov); + return NULL; +} + +GF_EdtsEntry *CreateEditEntry(u64 EditDuration, u64 MediaTime, u8 EditMode) +{ + GF_EdtsEntry *ent; + + ent = (GF_EdtsEntry*)malloc(sizeof(GF_EdtsEntry)); + if (!ent) return NULL; + + switch (EditMode) { + case GF_ISOM_EDIT_EMPTY: + ent->mediaRate = 1; + ent->mediaTime = -1; + break; + + case GF_ISOM_EDIT_DWELL: + ent->mediaRate = 0; + ent->mediaTime = MediaTime; + break; + default: + ent->mediaRate = 1; + ent->mediaTime = MediaTime; + break; + } + ent->segmentDuration = EditDuration; + return ent; +} + +#endif //GPAC_READ_ONLY + diff --git a/src/isomedia/isom_read.c b/src/isomedia/isom_read.c new file mode 100644 index 0000000..b1f68db --- /dev/null +++ b/src/isomedia/isom_read.c @@ -0,0 +1,2320 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include + +//the only static var. Used to store any error happening while opening a movie +static GF_Err MP4_API_IO_Err; + +void gf_isom_set_last_error(GF_ISOFile *movie, GF_Err error) +{ + if (!movie) { + MP4_API_IO_Err = error; + } else { + movie->LastError = error; + } +} + + +GF_EXPORT +GF_Err gf_isom_last_error(GF_ISOFile *the_file) +{ + if (!the_file) return MP4_API_IO_Err; + return the_file->LastError; +} + +GF_EXPORT +u8 gf_isom_get_mode(GF_ISOFile *the_file) +{ + if (!the_file) return 0; + return the_file->openMode; +} + + +/************************************************************** + Sample Manip +**************************************************************/ + +//creates a new empty sample +GF_EXPORT +GF_ISOSample *gf_isom_sample_new() +{ + GF_ISOSample *tmp; + GF_SAFEALLOC(tmp, GF_ISOSample); + return tmp; +} + +//delete a sample +GF_EXPORT +void gf_isom_sample_del(GF_ISOSample **samp) +{ + if (! *samp) return; + if ((*samp)->data && (*samp)->dataLength) free((*samp)->data); + free(*samp); + *samp = NULL; +} + +GF_EXPORT +Bool gf_isom_probe_file(const char *fileName) +{ + unsigned char data[4]; + u32 type; + FILE *f = fopen(fileName, "rb"); + if (!f) return 0; + type = 0; + if (fread(data, 1, 4, f) == 4) { + if (fread(data, 1, 4, f) == 4) { + type = GF_4CC(data[0], data[1], data[2], data[3]); + } + } + fclose(f); + switch (type) { + case GF_ISOM_BOX_TYPE_MOOV: + case GF_ISOM_BOX_TYPE_MDAT: + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + case GF_ISOM_BOX_TYPE_UDTA: + case GF_ISOM_BOX_TYPE_META: + case GF_ISOM_BOX_TYPE_VOID: + case GF_4CC('j','P',' ',' '): + case GF_4CC('w','i','d','e'): + return 1; + default: + return 0; + } +} + +/************************************************************** + File Opening in streaming mode + the file map is regular (through FILE handles) +**************************************************************/ +GF_EXPORT +GF_Err gf_isom_open_progressive(const char *fileName, GF_ISOFile **the_file, u64 *BytesMissing) +{ + GF_Err e; + GF_ISOFile *movie; + + *BytesMissing = 0; + *the_file = NULL; + + movie = gf_isom_new_movie(); + if (!movie) return GF_OUT_OF_MEM; + + movie->fileName = strdup(fileName); + movie->openMode = GF_ISOM_OPEN_READ; + //do NOT use FileMapping on incomplete files + e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_READ, &movie->movieFileMap); + if (e) { + gf_isom_delete_movie(movie); + return e; + } + +#ifndef GPAC_READ_ONLY + movie->editFileMap = NULL; + movie->finalName = NULL; +#endif //GPAC_READ_ONLY + + e = gf_isom_parse_movie_boxes(movie, BytesMissing); + if (e == GF_ISOM_INCOMPLETE_FILE) { + //if we have a moov, we're fine + if (movie->moov) { + *the_file = (GF_ISOFile *)movie; + return GF_OK; + } + //if not, delete the movie + gf_isom_delete_movie(movie); + return e; + } else if (e) { + //if not, delete the movie + gf_isom_delete_movie(movie); + return e; + } + //OK, let's return + *the_file = (GF_ISOFile *)movie; + return GF_OK; +} + +/************************************************************** + File Reading +**************************************************************/ + +GF_EXPORT +GF_ISOFile *gf_isom_open(const char *fileName, u32 OpenMode, const char *tmp_dir) +{ + GF_ISOFile *movie; + MP4_API_IO_Err = GF_OK; + + switch (OpenMode & 0xFF) { + case GF_ISOM_OPEN_READ_DUMP: + case GF_ISOM_OPEN_READ: + movie = gf_isom_open_file(fileName, OpenMode, NULL); + break; + +#ifndef GPAC_READ_ONLY + + case GF_ISOM_OPEN_WRITE: + movie = gf_isom_create_movie(fileName, OpenMode, tmp_dir); + break; + case GF_ISOM_OPEN_EDIT: + movie = gf_isom_open_file(fileName, OpenMode, tmp_dir); + break; + case GF_ISOM_WRITE_EDIT: + movie = gf_isom_create_movie(fileName, OpenMode, tmp_dir); + break; + +#endif //GPAC_READ_ONLY + + default: + return NULL; + } + return (GF_ISOFile *) movie; +} + +GF_EXPORT +GF_Err gf_isom_close(GF_ISOFile *movie) +{ + GF_Err e; + if (movie == NULL) return GF_ISOM_INVALID_FILE; + e = GF_OK; + +#ifndef GPAC_READ_ONLY + + //write our movie to the file + if (movie->openMode != GF_ISOM_OPEN_READ) { + gf_isom_get_duration(movie); +#ifndef GPAC_ISOM_NO_FRAGMENTS + //movie fragment mode, just store the fragment + if ( (movie->openMode == GF_ISOM_OPEN_WRITE) && (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) { + e = StoreFragment(movie); + } else +#endif + e = WriteToFile(movie); + } + +#endif //GPAC_READ_ONLY + + //free and return; + gf_isom_delete_movie(movie); + return e; +} + + +GF_EXPORT +Bool gf_isom_has_root_od(GF_ISOFile *movie) +{ + if (!movie || !movie->moov || !movie->moov->iods || !movie->moov->iods->descriptor) return 0; + return 1; +} + +//this funct is used for exchange files, where the iods contains an OD +GF_EXPORT +GF_Descriptor *gf_isom_get_root_od(GF_ISOFile *movie) +{ + GF_Descriptor *desc; + GF_ObjectDescriptor *od; + GF_InitialObjectDescriptor *iod; + GF_IsomObjectDescriptor *isom_od; + GF_IsomInitialObjectDescriptor *isom_iod; + GF_ESD *esd; + GF_ES_ID_Inc *inc; + u32 i; + u8 useIOD; + + if (!movie || !movie->moov) return NULL; + if (!movie->moov->iods) return NULL; + + od = NULL; + iod = NULL; + + switch (movie->moov->iods->descriptor->tag) { + case GF_ODF_ISOM_OD_TAG: + od = (GF_ObjectDescriptor*)malloc(sizeof(GF_ObjectDescriptor)); + memset(od, 0, sizeof(GF_ObjectDescriptor)); + od->ESDescriptors = gf_list_new(); + useIOD = 0; + break; + case GF_ODF_ISOM_IOD_TAG: + iod = (GF_InitialObjectDescriptor*)malloc(sizeof(GF_InitialObjectDescriptor)); + memset(iod, 0, sizeof(GF_InitialObjectDescriptor)); + iod->ESDescriptors = gf_list_new(); + useIOD = 1; + break; + default: + return NULL; + } + + //duplicate our descriptor + movie->LastError = gf_odf_desc_copy((GF_Descriptor *) movie->moov->iods->descriptor, &desc); + if (movie->LastError) return NULL; + + if (!useIOD) { + isom_od = (GF_IsomObjectDescriptor *)desc; + od->objectDescriptorID = isom_od->objectDescriptorID; + od->extensionDescriptors = isom_od->extensionDescriptors; + isom_od->extensionDescriptors = NULL; + od->IPMP_Descriptors = isom_od->IPMP_Descriptors; + isom_od->IPMP_Descriptors = NULL; + od->OCIDescriptors = isom_od->OCIDescriptors; + isom_od->OCIDescriptors = NULL; + od->URLString = isom_od->URLString; + isom_od->URLString = NULL; + od->tag = GF_ODF_OD_TAG; + //then recreate the desc in Inc + i=0; + while ((inc = (GF_ES_ID_Inc*)gf_list_enum(isom_od->ES_ID_IncDescriptors, &i))) { + movie->LastError = GetESDForTime(movie->moov, inc->trackID, 0, &esd); + if (!movie->LastError) movie->LastError = gf_list_add(od->ESDescriptors, esd); + if (movie->LastError) { + gf_odf_desc_del(desc); + gf_odf_desc_del((GF_Descriptor *) od); + return NULL; + } + } + gf_odf_desc_del(desc); + return (GF_Descriptor *)od; + } else { + isom_iod = (GF_IsomInitialObjectDescriptor *)desc; + iod->objectDescriptorID = isom_iod->objectDescriptorID; + iod->extensionDescriptors = isom_iod->extensionDescriptors; + isom_iod->extensionDescriptors = NULL; + iod->IPMP_Descriptors = isom_iod->IPMP_Descriptors; + isom_iod->IPMP_Descriptors = NULL; + iod->OCIDescriptors = isom_iod->OCIDescriptors; + isom_iod->OCIDescriptors = NULL; + iod->URLString = isom_iod->URLString; + isom_iod->URLString = NULL; + iod->tag = GF_ODF_IOD_TAG; + + iod->audio_profileAndLevel = isom_iod->audio_profileAndLevel; + iod->graphics_profileAndLevel = isom_iod->graphics_profileAndLevel; + iod->inlineProfileFlag = isom_iod->inlineProfileFlag; + iod->OD_profileAndLevel = isom_iod->OD_profileAndLevel; + iod->scene_profileAndLevel = isom_iod->scene_profileAndLevel; + iod->visual_profileAndLevel = isom_iod->visual_profileAndLevel; + iod->IPMPToolList = isom_iod->IPMPToolList; + isom_iod->IPMPToolList = NULL; + + //then recreate the desc in Inc + i=0; + while ((inc = (GF_ES_ID_Inc*)gf_list_enum(isom_iod->ES_ID_IncDescriptors, &i))) { + movie->LastError = GetESDForTime(movie->moov, inc->trackID, 0, &esd); + if (!movie->LastError) movie->LastError = gf_list_add(iod->ESDescriptors, esd); + if (movie->LastError) { + gf_odf_desc_del(desc); + gf_odf_desc_del((GF_Descriptor *) &iod); + return NULL; + } + } + gf_odf_desc_del(desc); + return (GF_Descriptor *)iod; + } +} + + +GF_EXPORT +u32 gf_isom_get_track_count(GF_ISOFile *movie) +{ + if (!movie || !movie->moov) return 0; + + if (! movie->moov->trackList) { + movie->LastError = GF_ISOM_INVALID_FILE; + return 0; + } + return gf_list_count(movie->moov->trackList); +} + + +GF_EXPORT +u32 gf_isom_get_track_id(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + if (!movie) return 0; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + return trak->Header->trackID; +} + + +GF_EXPORT +u32 gf_isom_get_track_by_id(GF_ISOFile *the_file, u32 trackID) +{ + GF_TrackBox *trak; + u32 count; + u32 i; + if (the_file == NULL) return 0; + + count = gf_isom_get_track_count(the_file); + if (!count) return 0; + for (i = 0; i < count; i++) { + trak = gf_isom_get_track_from_file(the_file, i+1); + if (!trak) return 0; + if (trak->Header->trackID == trackID) return i+1; + } + return 0; +} + +//return the timescale of the movie, 0 if error +GF_EXPORT +Bool gf_isom_has_movie(GF_ISOFile *file) +{ + if (file && file->moov) return 1; + return 0; +} + +//return the timescale of the movie, 0 if error +GF_EXPORT +u32 gf_isom_get_timescale(GF_ISOFile *movie) +{ + if (!movie || !movie->moov) return 0; + return movie->moov->mvhd->timeScale; +} + +//return the duration of the movie, 0 if error +GF_EXPORT +u64 gf_isom_get_duration(GF_ISOFile *movie) +{ +#ifndef GPAC_READ_ONLY + u32 i; + u64 maxDur; + GF_TrackBox *trak; +#endif + + if (!movie || !movie->moov) return 0; + + //if file was open in Write or Edit mode, recompute the duration + //the duration of a movie is the MaxDuration of all the tracks... + +#ifndef GPAC_READ_ONLY + + if (movie->openMode != GF_ISOM_OPEN_READ) { + maxDur = 0; + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { + if( (movie->LastError = SetTrackDuration(trak)) ) return 0; + if (trak->Header->duration > maxDur) + maxDur = trak->Header->duration; + } + movie->moov->mvhd->duration = maxDur; + } + +#endif //GPAC_READ_ONLY + + return movie->moov->mvhd->duration; +} + +//return the creation info of the movie +GF_EXPORT +GF_Err gf_isom_get_creation_time(GF_ISOFile *movie, u64 *creationTime, u64 *modificationTime) +{ + if (!movie || !movie->moov) return GF_BAD_PARAM; + + if (creationTime) *creationTime = movie->moov->mvhd->creationTime; + if (creationTime) *modificationTime = movie->moov->mvhd->modificationTime; + return GF_OK; +} + + +//check the presence of a track in IOD. 0: NO, 1: YES, 2: ERROR +GF_EXPORT +u8 gf_isom_is_track_in_root_od(GF_ISOFile *movie, u32 trackNumber) +{ + u32 i; + u32 trackID; + GF_Descriptor *desc; + GF_ES_ID_Inc *inc; + GF_List *inc_list; + if (!movie) return 2; + if (!movie->moov || !movie->moov->iods) return 0; + + desc = movie->moov->iods->descriptor; + switch (desc->tag) { + case GF_ODF_ISOM_IOD_TAG: + inc_list = ((GF_IsomInitialObjectDescriptor *)desc)->ES_ID_IncDescriptors; + break; + case GF_ODF_ISOM_OD_TAG: + inc_list = ((GF_IsomObjectDescriptor *)desc)->ES_ID_IncDescriptors; + break; + //files without IOD are possible ! + default: + return 0; + } + trackID = gf_isom_get_track_id(movie, trackNumber); + if (!trackID) return 2; + i=0; + while ((inc = (GF_ES_ID_Inc*)gf_list_enum(inc_list, &i))) { + if (inc->trackID == trackID) return 1; + } + return 0; +} + + + +//gets the enable flag of a track +//0: NO, 1: yes, 2: error +GF_EXPORT +u8 gf_isom_is_track_enabled(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + + if (!trak) return 2; + return (trak->Header->flags & 1) ? 1 : 0; +} + + +//get the track duration +//return 0 if bad param +GF_EXPORT +u64 gf_isom_get_track_duration(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + +#ifndef GPAC_READ_ONLY + /*in all modes except dump recompute duration in case headers are wrong*/ + if (movie->openMode != GF_ISOM_OPEN_READ_DUMP) { + SetTrackDuration(trak); + } +#endif + return trak->Header->duration; +} + +GF_EXPORT +GF_Err gf_isom_get_media_language(GF_ISOFile *the_file, u32 trackNumber, char *three_char_code) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + memcpy(three_char_code, trak->Media->mediaHeader->packedLanguage, sizeof(char)*4); + return GF_OK; +} + + +//Return the number of track references of a track for a given ReferenceType +//return -1 if error +GF_EXPORT +s32 gf_isom_get_reference_count(GF_ISOFile *movie, u32 trackNumber, u32 referenceType) +{ + GF_TrackBox *trak; + GF_TrackReferenceTypeBox *dpnd; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return -1; + if (!trak->References) return 0; + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + movie->LastError = GF_ISOM_INVALID_MODE; + return -1; + } + + dpnd = NULL; + if ( (movie->LastError = Track_FindRef(trak, referenceType, &dpnd)) ) return -1; + if (!dpnd) return 0; + return dpnd->trackIDCount; +} + + +//Return the referenced track number for a track and a given ReferenceType and Index +//return -1 if error, 0 if the reference is a NULL one, or the trackNumber +GF_EXPORT +GF_Err gf_isom_get_reference(GF_ISOFile *movie, u32 trackNumber, u32 referenceType, u32 referenceIndex, u32 *refTrack) +{ + GF_Err e; + GF_TrackBox *trak; + GF_TrackReferenceTypeBox *dpnd; + u32 refTrackNum; + trak = gf_isom_get_track_from_file(movie, trackNumber); + + *refTrack = 0; + if (!trak || !trak->References) return GF_BAD_PARAM; + + dpnd = NULL; + e = Track_FindRef(trak, referenceType, &dpnd); + if (e) return e; + if (!dpnd) return GF_BAD_PARAM; + + if (referenceIndex > dpnd->trackIDCount) return GF_BAD_PARAM; + + //the spec allows a NULL reference + //(ex, to force desync of a track, set a sync ref with ID = 0) + if (dpnd->trackIDs[referenceIndex - 1] == 0) return GF_OK; + + refTrackNum = gf_isom_get_tracknum_from_id(movie->moov, dpnd->trackIDs[referenceIndex-1]); + + //if the track was not found, this means the file is broken !!! + if (! refTrackNum) return GF_ISOM_INVALID_FILE; + *refTrack = refTrackNum; + return GF_OK; +} + + +//Return the media time given the absolute time in the Movie +GF_EXPORT +GF_Err gf_isom_get_media_time(GF_ISOFile *the_file, u32 trackNumber, u32 movieTime, u64 *MediaTime) +{ + GF_TrackBox *trak; + u8 useEdit; + s64 SegmentStartTime, mediaOffset; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !MediaTime) return GF_BAD_PARAM;; + + SegmentStartTime = 0; + return GetMediaTime(trak, movieTime, MediaTime, &SegmentStartTime, &mediaOffset, &useEdit); +} + + +//Get the stream description index (eg, the ESD) for a given time IN MEDIA TIMESCALE +//return 0 if error or if empty +GF_EXPORT +u32 gf_isom_get_sample_description_index(GF_ISOFile *movie, u32 trackNumber, u64 for_time) +{ + u32 streamDescIndex; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + + if ( (movie->LastError = Media_GetSampleDescIndex(trak->Media, for_time, &streamDescIndex)) ) { + return 0; + } + return streamDescIndex; +} + +//Get the number of "streams" stored in the media - a media can have several stream descriptions... +GF_EXPORT +u32 gf_isom_get_sample_description_count(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + return gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); +} + + +//Get the GF_ESD given the StreamDescriptionIndex +//THE DESCRIPTOR IS DUPLICATED, SO HAS TO BE DELETED BY THE APP +GF_EXPORT +GF_ESD *gf_isom_get_esd(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_ESD *outESD; + GF_Err e; + e = GetESD(movie->moov, gf_isom_get_track_id(movie, trackNumber), StreamDescriptionIndex, &outESD); + if (e && (e!= GF_ISOM_INVALID_MEDIA)) { + movie->LastError = e; + return NULL; + } + return outESD; +} + +//Get the decoderConfigDescriptor given the SampleDescriptionIndex +//THE DESCRIPTOR IS DUPLICATED, SO HAS TO BE DELETED BY THE APP +GF_EXPORT +GF_DecoderConfig *gf_isom_get_decoder_config(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_TrackBox *trak; + GF_ESD *esd; + GF_Descriptor *decInfo; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return NULL; + //get the ESD (possibly emulated) + Media_GetESD(trak->Media, StreamDescriptionIndex, &esd, 0); + if (!esd) return NULL; + decInfo = (GF_Descriptor *) esd->decoderConfig; + esd->decoderConfig = NULL; + gf_odf_desc_del((GF_Descriptor *) esd); + return (GF_DecoderConfig *)decInfo; +} + + +//get the media duration (without edit) +//return 0 if bad param +GF_EXPORT +u64 gf_isom_get_media_duration(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + + +#ifndef GPAC_READ_ONLY + + /*except in dump mode always recompute the duration*/ + if (movie->openMode != GF_ISOM_OPEN_READ_DUMP) { + if ( (movie->LastError = Media_SetDuration(trak)) ) return 0; + } + +#endif + + return trak->Media->mediaHeader->duration; +} + +//Get the timeScale of the media. All samples DTS/CTS are expressed in this timeScale +GF_EXPORT +u32 gf_isom_get_media_timescale(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->Media->mediaHeader->timeScale; +} + + +GF_EXPORT +u32 gf_isom_get_copyright_count(GF_ISOFile *mov) +{ + GF_UserDataMap *map; + if (!mov || !mov->moov || !mov->moov->udta) return 0; + map = udta_getEntry(mov->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); + if (!map) return 0; + return gf_list_count(map->boxList); +} + +GF_EXPORT +GF_Err gf_isom_get_copyright(GF_ISOFile *mov, u32 Index, const char **threeCharCode, const char **notice) +{ + GF_UserDataMap *map; + GF_CopyrightBox *cprt; + + if (!mov || !mov->moov || !Index) return GF_BAD_PARAM; + + if (!mov->moov->udta) return GF_OK; + map = udta_getEntry(mov->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); + if (!map) return GF_OK; + + if (Index > gf_list_count(map->boxList)) return GF_BAD_PARAM; + + cprt = (GF_CopyrightBox*)gf_list_get(map->boxList, Index-1); + (*threeCharCode) = cprt->packedLanguageCode; + (*notice) = cprt->notice; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_get_watermark(GF_ISOFile *mov, bin128 UUID, u8** data, u32* length) +{ + GF_UserDataMap *map; + GF_UnknownUUIDBox *wm; + + if (!mov) return GF_BAD_PARAM; + if (!mov->moov || !mov->moov->udta) return GF_NOT_SUPPORTED; + + map = udta_getEntry(mov->moov->udta, GF_ISOM_BOX_TYPE_UUID, (bin128 *) & UUID); + if (!map) return GF_NOT_SUPPORTED; + + wm = (GF_UnknownUUIDBox*)gf_list_get(map->boxList, 0); + if (!wm) return GF_NOT_SUPPORTED; + + *data = (u8 *) malloc(sizeof(char)*wm->dataSize); + memcpy(*data, wm->data, wm->dataSize); + *length = wm->dataSize; + return GF_OK; +} + +GF_EXPORT +u32 gf_isom_get_chapter_count(GF_ISOFile *movie, u32 trackNumber) +{ + GF_UserDataMap *map; + GF_ChapterListBox *lst; + GF_UserDataBox *udta; + + if (!movie || !movie->moov) return 0; + + udta = NULL; + if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return 0; + map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); + if (!map) return 0; + lst = (GF_ChapterListBox *)gf_list_get(map->boxList, 0); + if (!lst) return 0; + return gf_list_count(lst->list); +} + +GF_EXPORT +GF_Err gf_isom_get_chapter(GF_ISOFile *movie, u32 trackNumber, u32 Index, u64 *chapter_time, const char **name) +{ + GF_UserDataMap *map; + GF_ChapterListBox *lst; + GF_ChapterEntry *ce; + GF_UserDataBox *udta; + + if (!movie || !movie->moov) return GF_BAD_PARAM; + + udta = NULL; + if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return GF_BAD_PARAM; + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CHPL, NULL); + if (!map) return GF_BAD_PARAM; + lst = (GF_ChapterListBox *)gf_list_get(map->boxList, 0); + if (!lst) return GF_BAD_PARAM; + + ce = (GF_ChapterEntry *)gf_list_get(lst->list, Index-1); + if (!ce) return GF_BAD_PARAM; + if (chapter_time) { + *chapter_time = ce->start_time; + *chapter_time /= 10000L; + } + if (name) *name = ce->name; + return GF_OK; +} + + +GF_EXPORT +u32 gf_isom_get_media_type(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + return trak->Media->handler->handlerType; +} + +Bool IsMP4Description(u32 entryType) +{ + switch (entryType) { + case GF_ISOM_BOX_TYPE_MP4S: + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_ENCS: + return 1; + default: + return 0; + } +} + +Bool IsMP4EncryptedDescription(u32 entryType) +{ + switch (entryType) { + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_ENCS: + return 1; + default: + return 0; + } +} + +GF_EXPORT +u8 gf_isom_is_track_encrypted(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + GF_Box *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 2; + entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, 0); + if (!entry) return 2; + return IsMP4EncryptedDescription(entry->type); +} + +GF_EXPORT +u32 gf_isom_get_media_subtype(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_Box *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !DescriptionIndex) return 0; + entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return 0; + + //filter MPEG sub-types + if (IsMP4Description(entry->type)) { + if (IsMP4EncryptedDescription(entry->type)) return GF_ISOM_SUBTYPE_MPEG4_CRYP; + else return GF_ISOM_SUBTYPE_MPEG4; + } + if (entry->type == GF_ISOM_BOX_TYPE_GNRV) { + return ((GF_GenericVisualSampleEntryBox *)entry)->EntryType; + } + else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) { + return ((GF_GenericAudioSampleEntryBox *)entry)->EntryType; + } + else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) { + return ((GF_GenericSampleEntryBox *)entry)->EntryType; + } + return entry->type; +} + +GF_EXPORT +u32 gf_isom_get_mpeg4_subtype(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_Box *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !DescriptionIndex) return 0; + entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return 0; + + //filter MPEG sub-types + if (!IsMP4Description(entry->type)) return 0; + return entry->type; +} + +//Get the HandlerDescription name. +GF_EXPORT +GF_Err gf_isom_get_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char **outName) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !outName) return GF_BAD_PARAM; + *outName = trak->Media->handler->nameUTF8; + return GF_OK; +} + +//Check the DataReferences of this track +GF_EXPORT +GF_Err gf_isom_check_data_reference(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_Err e; + u32 drefIndex; + GF_TrackBox *trak; + + if (!StreamDescriptionIndex || !trackNumber) return GF_BAD_PARAM; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex , NULL, &drefIndex); + if (e) return e; + if (!drefIndex) return GF_BAD_PARAM; + return Media_CheckDataEntry(trak->Media, drefIndex); +} + +//get the location of the data. If URL && URN are NULL, the data is in this file +GF_EXPORT +GF_Err gf_isom_get_data_reference(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, const char **outURL, const char **outURN) +{ + GF_TrackBox *trak; + GF_DataEntryURLBox *url; + GF_DataEntryURNBox *urn; + u32 drefIndex; + GF_Err e; + + if (!StreamDescriptionIndex || !trackNumber) return GF_BAD_PARAM; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex , NULL, &drefIndex); + if (e) return e; + if (!drefIndex) return GF_BAD_PARAM; + + url = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->boxList, drefIndex - 1); + if (!url) return GF_ISOM_INVALID_FILE; + + *outURL = *outURN = NULL; + if (url->type == GF_ISOM_BOX_TYPE_URL) { + *outURL = url->location; + *outURN = NULL; + } else if (url->type == GF_ISOM_BOX_TYPE_URN) { + urn = (GF_DataEntryURNBox *) url; + *outURN = urn->nameURN; + *outURL = urn->location; + } else { + *outURN = NULL; + *outURL = NULL; + } + return GF_OK; +} + +//Get the number of samples +//return 0 if error or empty +GF_EXPORT +u32 gf_isom_get_sample_count(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->Media->information->sampleTable->SampleSize->sampleCount; +} + +u32 gf_isom_get_constant_sample_size(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->Media->information->sampleTable->SampleSize->sampleSize; +} + +GF_EXPORT +Bool gf_isom_has_time_offset(GF_ISOFile *the_file, u32 trackNumber) +{ + u32 i; + GF_CompositionOffsetBox *ctts; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media->information->sampleTable->CompositionOffset) return 0; + + //return true at the first offset found + ctts = trak->Media->information->sampleTable->CompositionOffset; + for (i=0; inb_entries; i++) { + if (ctts->entries[i].decodingOffset && ctts->entries[i].sampleCount) return 1; + } + return 0; +} + +GF_EXPORT +Bool gf_isom_has_sync_shadows(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + if (!trak->Media->information->sampleTable->ShadowSync) return 0; + if (gf_list_count(trak->Media->information->sampleTable->ShadowSync->entries) ) return 1; + return 0; +} + +GF_EXPORT +Bool gf_isom_has_sample_dependency(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + if (!trak->Media->information->sampleTable->SampleDep) return 0; + return 1; +} + +//return a sample give its number, and set the SampleDescIndex of this sample +//this index allows to retrieve the stream description if needed (2 media in 1 track) +//return NULL if error +GF_EXPORT +GF_ISOSample *gf_isom_get_sample(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 *sampleDescriptionIndex) +{ + GF_Err e; + u32 descIndex; + GF_TrackBox *trak; + GF_ISOSample *samp; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return NULL; + + if (!sampleNumber) return NULL; + samp = gf_isom_sample_new(); + if (!samp) return NULL; + e = Media_GetSample(trak->Media, sampleNumber, &samp, &descIndex, 0, NULL); + if (e) { + gf_isom_set_last_error(the_file, e); + gf_isom_sample_del(&samp); + return NULL; + } + if (sampleDescriptionIndex) *sampleDescriptionIndex = descIndex; + + return samp; +} + +GF_EXPORT +u32 gf_isom_get_sample_duration(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber) +{ + u64 dur; + u64 dts; + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !sampleNumber) return 0; + + stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, sampleNumber, &dur); + if (sampleNumber == trak->Media->information->sampleTable->SampleSize->sampleCount) { + return (u32) (trak->Media->mediaHeader->duration - dur); + } + + stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, sampleNumber+1, &dts); + return (u32) (dts - dur); +} + +//same as gf_isom_get_sample but doesn't fetch media data +GF_EXPORT +GF_ISOSample *gf_isom_get_sample_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 *sampleDescriptionIndex, u64 *data_offset) +{ + GF_Err e; + GF_TrackBox *trak; + GF_ISOSample *samp; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return NULL; + + if (!sampleNumber) return NULL; + samp = gf_isom_sample_new(); + if (!samp) return NULL; + e = Media_GetSample(trak->Media, sampleNumber, &samp, sampleDescriptionIndex, 1, data_offset); + if (e) { + gf_isom_set_last_error(the_file, e); + gf_isom_sample_del(&samp); + return NULL; + } + return samp; +} + +//same as gf_isom_get_sample but doesn't fetch media data +GF_EXPORT +u64 gf_isom_get_sample_dts(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber) +{ + u64 dts; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + if (!sampleNumber) return 0; + if (stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, sampleNumber, &dts) != GF_OK) return 0; + return dts; +} + + +GF_EXPORT +Bool gf_isom_is_self_contained(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return Media_IsSelfContained(trak->Media, sampleDescriptionIndex); +} + +/*retrieves given sample DTS*/ +u32 gf_isom_get_sample_from_dts(GF_ISOFile *the_file, u32 trackNumber, u64 dts) +{ + GF_Err e; + u32 sampleNumber, prevSampleNumber; + GF_TrackBox *trak; + GF_SampleTableBox *stbl; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + stbl = trak->Media->information->sampleTable; + + e = findEntryForTime(stbl, dts, 1, &sampleNumber, &prevSampleNumber); + if (e) return 0; + return sampleNumber; +} + + +//return a sample given a desired display time IN MEDIA TIME SCALE +//and set the StreamDescIndex of this sample +//this index allows to retrieve the stream description if needed (2 media in 1 track) +//return NULL if error +//WARNING: the sample may not be sync even though the sync was requested (depends on the media) +GF_EXPORT +GF_Err gf_isom_get_sample_for_media_time(GF_ISOFile *the_file, u32 trackNumber, u64 desiredTime, u32 *StreamDescriptionIndex, u8 SearchMode, GF_ISOSample **sample, u32 *SampleNum) +{ + GF_Err e; + u32 sampleNumber, prevSampleNumber, syncNum, shadowSync; + GF_TrackBox *trak; + GF_ISOSample *shadow; + GF_SampleTableBox *stbl; + u8 useShadow, IsSync; + + if (!sample) return GF_BAD_PARAM; + + if (SampleNum) *SampleNum = 0; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stbl = trak->Media->information->sampleTable; + + e = findEntryForTime(stbl, desiredTime, 1, &sampleNumber, &prevSampleNumber); + if (e) return e; + + //if no shadow table, reset to sync only + useShadow = 0; + if (!stbl->ShadowSync && (SearchMode == GF_ISOM_SEARCH_SYNC_SHADOW)) + SearchMode = GF_ISOM_SEARCH_SYNC_BACKWARD; + + //if no syncTable, disable syncSearching, as all samples ARE sync + if (! trak->Media->information->sampleTable->SyncSample) { + if (SearchMode == GF_ISOM_SEARCH_SYNC_FORWARD) SearchMode = GF_ISOM_SEARCH_FORWARD; + if (SearchMode == GF_ISOM_SEARCH_SYNC_BACKWARD) SearchMode = GF_ISOM_SEARCH_BACKWARD; + } + + //not found, return EOF or browse backward + if (!sampleNumber && !prevSampleNumber) { + if (SearchMode == GF_ISOM_SEARCH_SYNC_BACKWARD || SearchMode == GF_ISOM_SEARCH_BACKWARD) { + sampleNumber = trak->Media->information->sampleTable->SampleSize->sampleCount; + } + if (!sampleNumber) return GF_EOS; + } + + //check in case we have the perfect sample + IsSync = 0; + + //according to the direction adjust the sampleNum value + switch (SearchMode) { + case GF_ISOM_SEARCH_SYNC_FORWARD: + IsSync = 1; + case GF_ISOM_SEARCH_FORWARD: + //not the exact one + if (!sampleNumber) { + if (prevSampleNumber != stbl->SampleSize->sampleCount) { + sampleNumber = prevSampleNumber + 1; + } else { + sampleNumber = prevSampleNumber; + } + } + break; + + //if dummy mode, reset to default browsing + case GF_ISOM_SEARCH_SYNC_BACKWARD: + IsSync = 1; + case GF_ISOM_SEARCH_SYNC_SHADOW: + case GF_ISOM_SEARCH_BACKWARD: + default: + //first case, not found.... + if (!sampleNumber && !prevSampleNumber) { + sampleNumber = stbl->SampleSize->sampleCount; + } else if (!sampleNumber) { + sampleNumber = prevSampleNumber; + } + break; + } + + //get the sync sample num + if (IsSync) { + //get the SyncNumber + e = Media_FindSyncSample(trak->Media->information->sampleTable, + sampleNumber, &syncNum, SearchMode); + if (e) return e; + if (syncNum) sampleNumber = syncNum; + syncNum = 0; + } + //if we are in shadow mode, get the previous sync sample + //in case we can't find a good SyncShadow + else if (SearchMode == GF_ISOM_SEARCH_SYNC_SHADOW) { + //get the SyncNumber + e = Media_FindSyncSample(trak->Media->information->sampleTable, + sampleNumber, &syncNum, GF_ISOM_SEARCH_SYNC_BACKWARD); + if (e) return e; + } + + + //OK sampleNumber is exactly the sample we need (except for shadow) + + *sample = gf_isom_sample_new(); + if (*sample == NULL) return GF_OUT_OF_MEM; + + //we are in shadow mode, we need to browse both SyncSample and ShadowSyncSample to get + //the desired sample... + if (SearchMode == GF_ISOM_SEARCH_SYNC_SHADOW) { + //get the shadowing number + stbl_GetSampleShadow(stbl->ShadowSync, &sampleNumber, &shadowSync); + //now sampleNumber is the closest previous shadowed sample. + //1- If we have a closer sync sample, use it. + //2- if the shadowSync is 0, we don't have any shadowing, use syncNum + if ((sampleNumber < syncNum) || (!shadowSync)) { + sampleNumber = syncNum; + } else { + //otherwise, we have a better alternate sample in the shadowSync for this sample + useShadow = 1; + } + } + + e = Media_GetSample(trak->Media, sampleNumber, sample, StreamDescriptionIndex, 0, NULL); + if (e) { + gf_isom_sample_del(sample); + return e; + } + //optionally get the sample number + if (SampleNum) *SampleNum = sampleNumber; + + //in shadow mode, we only get the data of the shadowing sample ! + if (useShadow) { + //we have to use StreamDescriptionIndex in case the sample data is in another desc + //though this is unlikely as non optimized... + shadow = gf_isom_get_sample(the_file, trackNumber, shadowSync, StreamDescriptionIndex); + //if no sample, the shadowSync is broken, return the sample + if (!shadow) return GF_OK; + (*sample)->IsRAP = 1; + free((*sample)->data); + (*sample)->dataLength = shadow->dataLength; + (*sample)->data = shadow->data; + //set data length to 0 to keep the buffer alive... + shadow->dataLength = 0; + gf_isom_sample_del(&shadow); + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_get_sample_for_movie_time(GF_ISOFile *the_file, u32 trackNumber, u64 movieTime, u32 *StreamDescriptionIndex, u8 SearchMode, GF_ISOSample **sample, u32 *sampleNumber) +{ + GF_Err e; + GF_TrackBox *trak; + u64 mediaTime; + s64 segStartTime, mediaOffset; + u32 sampNum; + u8 useEdit; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (*sample || !sample) return GF_BAD_PARAM; + //check 0-duration tracks (BIFS and co). Check we're not searching forward + if (!trak->Header->duration) { + if (movieTime && ( (SearchMode == GF_ISOM_SEARCH_SYNC_FORWARD) || (SearchMode == GF_ISOM_SEARCH_FORWARD)) ) { + *sample = NULL; + if (sampleNumber) *sampleNumber = 0; + *StreamDescriptionIndex = 0; + return GF_EOS; + } + } + else if (movieTime * trak->moov->mvhd->timeScale > trak->Header->duration * trak->Media->mediaHeader->timeScale) { + *sample = NULL; + if (sampleNumber) *sampleNumber = 0; + *StreamDescriptionIndex = 0; + return GF_EOS; + } + + //get the media time for this movie time... + mediaTime = segStartTime = 0; + *StreamDescriptionIndex = 0; + + e = GetMediaTime(trak, movieTime, &mediaTime, &segStartTime, &mediaOffset, &useEdit); + if (e) return e; + + /*here we check if we were playing or not and return no sample in normal search modes*/ + if (useEdit && mediaOffset == -1) { + if ((SearchMode==GF_ISOM_SEARCH_FORWARD) || (SearchMode==GF_ISOM_SEARCH_BACKWARD)) { + /*get next sample time in MOVIE timescale*/ + if (SearchMode==GF_ISOM_SEARCH_FORWARD) + e = GetNextMediaTime(trak, movieTime, &mediaTime); + else + e = GetPrevMediaTime(trak, movieTime, &mediaTime); + if (e) return e; + return gf_isom_get_sample_for_movie_time(the_file, trackNumber, (u32) mediaTime, StreamDescriptionIndex, GF_ISOM_SEARCH_SYNC_FORWARD, sample, sampleNumber); + } + if (sampleNumber) *sampleNumber = 0; + *sample = gf_isom_sample_new(); + (*sample)->DTS = movieTime; + return GF_OK; + } + /*dwell edit in non-sync mode, fetch next/prev sample depending on mode. + Otherwise return the dwell entry*/ + if (useEdit==2) { + if ((SearchMode==GF_ISOM_SEARCH_FORWARD) || (SearchMode==GF_ISOM_SEARCH_BACKWARD)) { + /*get next sample time in MOVIE timescale*/ + if (SearchMode==GF_ISOM_SEARCH_FORWARD) + e = GetNextMediaTime(trak, movieTime, &mediaTime); + else + e = GetPrevMediaTime(trak, movieTime, &mediaTime); + if (e) return e; + return gf_isom_get_sample_for_movie_time(the_file, trackNumber, (u32) mediaTime, StreamDescriptionIndex, GF_ISOM_SEARCH_SYNC_FORWARD, sample, sampleNumber); + } + } + + //OK, we have a sample so fetch it + e = gf_isom_get_sample_for_media_time(the_file, trackNumber, mediaTime, StreamDescriptionIndex, SearchMode, sample, &sampNum); + if (e) return e; + + //OK, now the trick: we have to rebuild the time stamps, according + //to the media time scale (used by SLConfig) - add the edit start time but stay in + //the track TS + if (useEdit) { + u64 _ts = segStartTime; + _ts *= trak->Media->mediaHeader->timeScale; + _ts /= trak->moov->mvhd->timeScale; + (*sample)->DTS += _ts; + /*watchout, the sample fetched may be before the first sample in the edit list (when seeking)*/ + if ( (*sample)->DTS > (u64) mediaOffset) { + (*sample)->DTS -= (u64) mediaOffset; + } else { + (*sample)->DTS = 0; + } + } + if (sampleNumber) *sampleNumber = sampNum; + return GF_OK; +} + + + +GF_EXPORT +u64 gf_isom_get_missing_bytes(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + return trak->Media->BytesMissing; +} + +GF_EXPORT +GF_Err gf_isom_set_sample_padding(GF_ISOFile *the_file, u32 trackNumber, u32 padding_bytes) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + trak->padding_bytes = padding_bytes; + return GF_OK; + +} + +//get the number of edited segment +GF_EXPORT +u32 gf_isom_get_edit_segment_count(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + if (!trak->editBox || !trak->editBox->editList) return 0; + return gf_list_count(trak->editBox->editList->entryList); +} + + +//Get the desired segment information +GF_EXPORT +GF_Err gf_isom_get_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u32 SegmentIndex, u64 *EditTime, u64 *SegmentDuration, u64 *MediaTime, u8 *EditMode) +{ + u32 i; + u64 startTime; + GF_TrackBox *trak; + GF_EditListBox *elst; + GF_EdtsEntry *ent; + + ent = NULL; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (!trak || !trak->editBox || + !trak->editBox->editList || + (SegmentIndex > gf_list_count(trak->editBox->editList->entryList)) || + !SegmentIndex) + return GF_BAD_PARAM; + + elst = trak->editBox->editList; + startTime = 0; + + for (i = 0; i < SegmentIndex; i++) { + ent = (GF_EdtsEntry*)gf_list_get(elst->entryList, i); + if (i < SegmentIndex-1) startTime += ent->segmentDuration; + } + *EditTime = startTime; + *SegmentDuration = ent->segmentDuration; + if (ent->mediaTime < 0) { + *MediaTime = 0; + *EditMode = GF_ISOM_EDIT_EMPTY; + return GF_OK; + } + if (ent->mediaRate == 0) { + *MediaTime = ent->mediaTime; + *EditMode = GF_ISOM_EDIT_DWELL; + return GF_OK; + } + *MediaTime = ent->mediaTime; + *EditMode = GF_ISOM_EDIT_NORMAL; + return GF_OK; +} + +GF_EXPORT +u8 gf_isom_has_sync_points(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + if (trak->Media->information->sampleTable->SyncSample) { + if (!trak->Media->information->sampleTable->SyncSample->nb_entries) return 2; + return 1; + } + return 0; +} + +/*returns number of sync points*/ +GF_EXPORT +u32 gf_isom_get_sync_point_count(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + if (trak->Media->information->sampleTable->SyncSample) { + return trak->Media->information->sampleTable->SyncSample->nb_entries; + } + return 0; +} + + +GF_EXPORT +GF_Err gf_isom_get_brand_info(GF_ISOFile *movie, u32 *brand, u32 *minorVersion, u32 *AlternateBrandsCount) +{ + if (!movie || !brand) return GF_BAD_PARAM; + if (!movie->brand) { + *brand = 0; + if (minorVersion) *minorVersion = 0; + if (AlternateBrandsCount) *AlternateBrandsCount = 0; + return GF_OK; + } + + *brand = movie->brand->majorBrand; + if (minorVersion) *minorVersion = movie->brand->minorVersion; + if (AlternateBrandsCount) *AlternateBrandsCount = movie->brand->altCount; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_get_alternate_brand(GF_ISOFile *movie, u32 BrandIndex, u32 *brand) +{ + if (!movie || !movie->brand || !brand) return GF_BAD_PARAM; + if (BrandIndex > movie->brand->altCount || !BrandIndex) return GF_BAD_PARAM; + *brand = movie->brand->altBrand[BrandIndex-1]; + return GF_OK; +} + +GF_Err gf_isom_get_sample_padding_bits(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u8 *NbBits) +{ + GF_TrackBox *trak; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + + //Padding info + return stbl_GetPaddingBits(trak->Media->information->sampleTable->PaddingBits, + sampleNumber, NbBits); + +} + + +GF_EXPORT +Bool gf_isom_has_padding_bits(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + if (trak->Media->information->sampleTable->PaddingBits) return 1; + return 0; +} + + +GF_EXPORT +u32 gf_isom_get_user_data_count(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID) +{ + GF_UserDataMap *map; + GF_TrackBox *trak; + GF_UserDataBox *udta; + bin128 t; + u32 i, count; + + if (!movie || !movie->moov) return 0; + + if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; + memset(t, 1, 16); + + if (trackNumber) { + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return 0; + + i=0; + while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { + count = gf_list_count(map->boxList); + + if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) return count; + else if (map->boxType == UserDataType) return count; + } + return 0; +} + +GF_EXPORT +GF_Err gf_isom_get_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex, char **userData, u32 *userDataSize) +{ + GF_UserDataMap *map; + GF_UnknownBox *ptr; + u32 i; + bin128 t; + GF_TrackBox *trak; + GF_UserDataBox *udta; + + if (!movie || !movie->moov) return GF_BAD_PARAM; + + if (trackNumber) { + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return GF_BAD_PARAM; + + if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; + memset(t, 1, 16); + + if (!UserDataIndex) return GF_BAD_PARAM; + if (!userData || !userDataSize || *userData) return GF_BAD_PARAM; + + i=0; + while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { + if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found; + else if (map->boxType == UserDataType) goto found; + + } + return GF_BAD_PARAM; + +found: + + if (UserDataIndex > gf_list_count(map->boxList) ) return GF_BAD_PARAM; + ptr = (GF_UnknownBox*)gf_list_get(map->boxList, UserDataIndex-1); + + //ok alloc the data + *userData = (char *)malloc(sizeof(char)*ptr->dataSize); + if (!*userData) return GF_OUT_OF_MEM; + memcpy(*userData, ptr->data, sizeof(char)*ptr->dataSize); + *userDataSize = ptr->dataSize; + return GF_OK; +} + +GF_EXPORT +void gf_isom_delete(GF_ISOFile *movie) +{ + //free and return; + gf_isom_delete_movie(movie); +} + +GF_EXPORT +u32 gf_isom_get_max_chunk_duration(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + u32 i, sample_per_chunk, sample_dur; + GF_SampleToChunkBox *stsc; + GF_TimeToSampleBox *stts; + if (!movie || !trackNumber || !movie->moov) return 0; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + + stsc = trak->Media->information->sampleTable->SampleToChunk; + stts = trak->Media->information->sampleTable->TimeToSample; + + sample_per_chunk = 0; + for (i=0; inb_entries; i++) { + if (stsc->entries[i].samplesPerChunk > sample_per_chunk) sample_per_chunk = stsc->entries[i].samplesPerChunk; + } + sample_dur = 0; + for (i=0; inb_entries; i++) { + if (stts->entries[i].sampleDelta > sample_dur) sample_dur = stts->entries[i].sampleDelta; + } + + //rescale to ms + i = 1000 * sample_dur * sample_per_chunk / trak->Media->mediaHeader->timeScale; + return i; +} + +GF_EXPORT +u32 gf_isom_get_sample_fragment_count(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + //Padding info + return stbl_GetSampleFragmentCount(trak->Media->information->sampleTable->Fragments, sampleNumber); +} + +GF_EXPORT +u16 gf_isom_get_sample_fragment_size(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 FragmentIndex) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !FragmentIndex) return 0; + + //Padding info + return stbl_GetSampleFragmentSize(trak->Media->information->sampleTable->Fragments, sampleNumber, FragmentIndex); +} + + +GF_EXPORT +GF_Err gf_isom_get_fragment_defaults(GF_ISOFile *the_file, u32 trackNumber, + u32 *defaultDuration, u32 *defaultSize, u32 *defaultDescriptionIndex, + u32 *defaultRandomAccess, u8 *defaultPadding, u16 *defaultDegradationPriority) +{ + GF_TrackBox *trak; + GF_StscEntry *sc_ent; + u32 i, j, maxValue, value; + GF_SampleTableBox *stbl; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stbl = trak->Media->information->sampleTable; + //duration + if (defaultDuration) { + maxValue = value = 0; + for (i=0; iTimeToSample->nb_entries; i++) { + if (stbl->TimeToSample->entries[i].sampleCount>maxValue) { + value = stbl->TimeToSample->entries[i].sampleDelta; + maxValue = stbl->TimeToSample->entries[i].sampleCount; + } + } + *defaultDuration = value; + } + //size + if (defaultSize) { + *defaultSize = stbl->SampleSize->sampleSize; + } + //descIndex + if (defaultDescriptionIndex) { + GF_SampleToChunkBox *stsc= stbl->SampleToChunk; + maxValue = value = 0; + for (i=0; inb_entries; i++) { + sc_ent = &stsc->entries[i]; + if ((sc_ent->nextChunk - sc_ent->firstChunk) * sc_ent->samplesPerChunk > maxValue) { + value = sc_ent->sampleDescriptionIndex; + maxValue = (sc_ent->nextChunk - sc_ent->firstChunk) * sc_ent->samplesPerChunk; + } + } + *defaultDescriptionIndex = value ? value : 1; + } + //RAP + if (defaultRandomAccess) { + //no sync table is ALL RAP + *defaultRandomAccess = stbl->SyncSample ? 0 : 1; + if (stbl->SyncSample + && (stbl->SyncSample->nb_entries >= stbl->SampleSize->sampleCount/2)) { + *defaultRandomAccess = 1; + } + } + //defaultPadding + if (defaultPadding) { + *defaultPadding = 0; + if (stbl->PaddingBits) { + maxValue = 0; + for (i=0; iPaddingBits->SampleCount; i++) { + value = 0; + for (j=0; jPaddingBits->SampleCount; j++) { + if (stbl->PaddingBits->padbits[i]==stbl->PaddingBits->padbits[j]) { + value ++; + } + } + if (value>maxValue) { + maxValue = value; + *defaultPadding = stbl->PaddingBits->padbits[i]; + } + } + } + } + //defaultDegradationPriority + if (defaultDegradationPriority) { + *defaultDegradationPriority = 0; + if (stbl->DegradationPriority) { + maxValue = 0; + for (i=0; iDegradationPriority->nb_entries; i++) { + value = 0; + for (j=0; jDegradationPriority->nb_entries; j++) { + if (stbl->DegradationPriority->priorities[i]==stbl->DegradationPriority->priorities[j]) { + value ++; + } + } + if (value>maxValue) { + maxValue = value; + *defaultDegradationPriority = stbl->DegradationPriority->priorities[i]; + } + } + } + } + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_isom_refresh_fragmented(GF_ISOFile *movie, u64 *MissingBytes) +{ +#ifdef GPAC_ISOM_NO_FRAGMENTS + return GF_NOT_SUPPORTED; +#else + if (!movie || !movie->moov || !movie->moov->mvex) return GF_BAD_PARAM; + if (movie->openMode != GF_ISOM_OPEN_READ) return GF_BAD_PARAM; + + //ok parse root boxes + return gf_isom_parse_movie_boxes(movie, MissingBytes); +#endif +} + + +GF_EXPORT +GF_Err gf_isom_text_set_streaming_mode(GF_ISOFile *movie, Bool do_convert) +{ + if (!movie) return GF_BAD_PARAM; + movie->convert_streaming_text = do_convert; + return GF_OK; +} + + +GF_EXPORT +GF_GenericSampleDescription *gf_isom_get_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_GenericVisualSampleEntryBox *entry; + GF_GenericAudioSampleEntryBox *gena; + GF_GenericSampleEntryBox *genm; + GF_TrackBox *trak; + GF_GenericSampleDescription *udesc; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !StreamDescriptionIndex) return NULL; + + entry = (GF_GenericVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex-1); + //no entry or MPEG entry: + if (!entry || IsMP4Description(entry->type) ) return NULL; + //if we handle the description return false + switch (entry->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + case GF_ISOM_SUBTYPE_3GP_H263: + return NULL; + case GF_ISOM_BOX_TYPE_GNRV: + GF_SAFEALLOC(udesc, GF_GenericSampleDescription); + if (entry->EntryType == GF_ISOM_BOX_TYPE_UUID) { + memcpy(udesc->UUID, ((GF_UUIDBox*)entry)->uuid, sizeof(bin128)); + } else { + udesc->codec_tag = entry->EntryType; + } + udesc->version = entry->version; + udesc->revision = entry->revision; + udesc->vendor_code = entry->vendor; + udesc->temporal_quality = entry->temporal_quality; + udesc->spacial_quality = entry->spacial_quality; + udesc->width = entry->Width; + udesc->height = entry->Height; + udesc->h_res = entry->horiz_res; + udesc->v_res = entry->vert_res; + strcpy(udesc->compressor_name, entry->compressor_name); + udesc->depth = entry->bit_depth; + udesc->color_table_index = entry->color_table_index; + if (entry->data_size) { + udesc->extension_buf_size = entry->data_size; + udesc->extension_buf = (char*)malloc(sizeof(char) * entry->data_size); + memcpy(udesc->extension_buf, entry->data, entry->data_size); + } + return udesc; + case GF_ISOM_BOX_TYPE_GNRA: + gena = (GF_GenericAudioSampleEntryBox *)entry; + GF_SAFEALLOC(udesc, GF_GenericSampleDescription); + if (gena->EntryType == GF_ISOM_BOX_TYPE_UUID) { + memcpy(udesc->UUID, ((GF_UUIDBox*)gena)->uuid, sizeof(bin128)); + } else { + udesc->codec_tag = gena->EntryType; + } + udesc->version = gena->version; + udesc->revision = gena->revision; + udesc->vendor_code = gena->vendor; + udesc->samplerate = gena->samplerate_hi; + udesc->bits_per_sample = gena->bitspersample; + udesc->nb_channels = gena->channel_count; + if (gena->data_size) { + udesc->extension_buf_size = gena->data_size; + udesc->extension_buf = (char*)malloc(sizeof(char) * gena->data_size); + memcpy(udesc->extension_buf, gena->data, gena->data_size); + } + return udesc; + case GF_ISOM_BOX_TYPE_GNRM: + genm = (GF_GenericSampleEntryBox *)entry; + GF_SAFEALLOC(udesc, GF_GenericSampleDescription); + if (genm->EntryType == GF_ISOM_BOX_TYPE_UUID) { + memcpy(udesc->UUID, ((GF_UUIDBox*)genm)->uuid, sizeof(bin128)); + } else { + udesc->codec_tag = genm->EntryType; + } + if (genm->data_size) { + udesc->extension_buf_size = genm->data_size; + udesc->extension_buf = (char*)malloc(sizeof(char) * genm->data_size); + memcpy(udesc->extension_buf, genm->data, genm->data_size); + } + return udesc; + } + return NULL; +} + +GF_EXPORT +GF_Err gf_isom_get_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 *Width, u32 *Height) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) return movie->LastError = GF_BAD_PARAM; + + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + + //valid for MPEG visual, JPG and 3GPP H263 + switch (entry->type) { + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_GNRV: + *Width = ((GF_VisualSampleEntryBox*)entry)->Width; + *Height = ((GF_VisualSampleEntryBox*)entry)->Height; + return GF_OK; + default: + if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_SCENE) { + *Width = trak->Header->width>>16; + *Height = trak->Header->height>>16; + return GF_OK; + } + return GF_BAD_PARAM; + } +} + +GF_EXPORT +GF_Err gf_isom_get_audio_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 *SampleRate, u32 *Channels, u8 *bitsPerSample) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) return movie->LastError = GF_BAD_PARAM; + + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + + switch (entry->type) { + case GF_ISOM_BOX_TYPE_ENCA: + if (entry->protection_info && (entry->protection_info->original_format->data_format!= GF_ISOM_BOX_TYPE_MP4A)) return GF_ISOM_INVALID_MEDIA; + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + case GF_ISOM_BOX_TYPE_AC3: + if (SampleRate) (*SampleRate) = ((GF_AudioSampleEntryBox*)entry)->samplerate_hi; + if (Channels) (*Channels) = ((GF_AudioSampleEntryBox*)entry)->channel_count; + if (bitsPerSample) (*bitsPerSample) = (u8) ((GF_AudioSampleEntryBox*)entry)->bitspersample; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + +GF_EXPORT +GF_Err gf_isom_get_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 *hSpacing, u32 *vSpacing) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) return movie->LastError = GF_BAD_PARAM; + + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + + //valid for MPEG visual, JPG and 3GPP H263 + switch (entry->type) { + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_GNRV: + *hSpacing = ((GF_VisualSampleEntryBox*)entry)->pasp ? ((GF_VisualSampleEntryBox*)entry)->pasp->hSpacing : 0; + *vSpacing = ((GF_VisualSampleEntryBox*)entry)->pasp ? ((GF_VisualSampleEntryBox*)entry)->pasp->vSpacing : 0; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + +GF_EXPORT +const char *gf_isom_get_filename(GF_ISOFile *movie) +{ + if (!movie) return NULL; +#ifndef GPAC_READ_ONLY + if (movie->finalName && !movie->fileName) return movie->finalName; +#endif + return movie->fileName; +} + + +GF_EXPORT +u8 gf_isom_get_pl_indication(GF_ISOFile *movie, u8 PL_Code) +{ + GF_IsomInitialObjectDescriptor *iod; + if (!movie || !movie->moov) return 0; + if (!movie->moov->iods || !movie->moov->iods->descriptor) return 0xFF; + if (movie->moov->iods->descriptor->tag != GF_ODF_ISOM_IOD_TAG) return 0xFF; + + iod = (GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor; + switch (PL_Code) { + case GF_ISOM_PL_AUDIO: return iod->audio_profileAndLevel; + case GF_ISOM_PL_VISUAL: return iod->visual_profileAndLevel; + case GF_ISOM_PL_GRAPHICS: return iod->graphics_profileAndLevel; + case GF_ISOM_PL_SCENE: return iod->scene_profileAndLevel; + case GF_ISOM_PL_OD: return iod->OD_profileAndLevel; + case GF_ISOM_PL_INLINE: return iod->inlineProfileFlag; + case GF_ISOM_PL_MPEGJ: + default: + return 0xFF; + } +} + + +GF_EXPORT +GF_Err gf_isom_get_track_layout_info(GF_ISOFile *movie, u32 trackNumber, u32 *width, u32 *height, s32 *translation_x, s32 *translation_y, s16 *layer) +{ + GF_TrackBox *tk = gf_isom_get_track_from_file(movie, trackNumber); + if (!tk) return GF_BAD_PARAM; + if (width) *width = tk->Header->width>>16; + if (height) *height = tk->Header->height>>16; + if (layer) *layer = tk->Header->layer; + if (translation_x) *translation_x = tk->Header->matrix[6] >> 16; + if (translation_y) *translation_y = tk->Header->matrix[7] >> 16; + return GF_OK; +} + + +/*returns total amount of media bytes in track*/ +u64 gf_isom_get_media_data_size(GF_ISOFile *movie, u32 trackNumber) +{ + u32 i, size; + GF_SampleSizeBox *stsz; + GF_TrackBox *tk = gf_isom_get_track_from_file(movie, trackNumber); + if (!tk) return 0; + stsz = tk->Media->information->sampleTable->SampleSize; + if (stsz->sampleSize) return stsz->sampleSize*stsz->sampleCount; + size = 0; + for (i=0; isampleCount;i++) size += stsz->sizes[i]; + return size; +} + + +GF_EXPORT +void gf_isom_set_default_sync_track(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *tk = gf_isom_get_track_from_file(movie, trackNumber); + if (!tk) movie->es_id_default_sync = -1; + else movie->es_id_default_sync = tk->Header->trackID; +} + + +GF_EXPORT +Bool gf_isom_is_single_av(GF_ISOFile *file) +{ + u32 count, i, nb_any, nb_a, nb_v, nb_scene, nb_od, nb_text; + nb_a = nb_v = nb_any = nb_scene = nb_od = nb_text = 0; + + if (!file->moov) return 0; + count = gf_isom_get_track_count(file); + for (i=0; i1) nb_any++; + else nb_scene++; + } else if (mtype==GF_ISOM_MEDIA_OD) { + if (gf_isom_get_sample_count(file, i+1)>1) nb_any++; + else nb_od++; + } + else if (mtype==GF_ISOM_MEDIA_TEXT) nb_text++; + else if (mtype==GF_ISOM_MEDIA_AUDIO) nb_a++; + else if (mtype==GF_ISOM_MEDIA_VISUAL) { + /*discard file with images*/ + if (gf_isom_get_sample_count(file, i+1)==1) nb_any++; + else nb_v++; + } + else nb_any++; + } + if (nb_any) return 0; + if ((nb_scene<=1) && (nb_od<=1) && (nb_a<=1) && (nb_v<=1) && (nb_text<=1) ) return 1; + return 0; +} + +GF_EXPORT +Bool gf_isom_is_JPEG2000(GF_ISOFile *mov) +{ + return (mov && mov->is_jp2) ? 1 : 0; +} + +GF_EXPORT +u32 gf_isom_guess_specification(GF_ISOFile *file) +{ + u32 count, i, nb_any, nb_m4s, nb_a, nb_v, nb_scene, nb_od, nb_mp3, nb_aac, nb_m4v, nb_avc, nb_amr, nb_h263, nb_qcelp, nb_evrc, nb_smv, nb_text; + + nb_m4s = nb_a = nb_v = nb_any = nb_scene = nb_od = nb_mp3 = nb_aac = nb_m4v = nb_avc = nb_amr = nb_h263 = nb_qcelp = nb_evrc = nb_smv = nb_text = 0; + + if (file->is_jp2) { + if (file->moov) return GF_4CC('m','j','p','2'); + return GF_4CC('j','p','2',' '); + } + if (!file->moov) { + if (!file->meta || !file->meta->handler) return 0; + return file->meta->handler->handlerType; + } + + count = gf_isom_get_track_count(file); + for (i=0; i1) nb_m4s++; + } else if (mtype==GF_ISOM_MEDIA_OD) { + nb_od++; + /*forces non-isma*/ + if (gf_isom_get_sample_count(file, i+1)>1) nb_m4s++; + } + else if (mtype==GF_ISOM_MEDIA_TEXT) nb_text++; + else if ((mtype==GF_ISOM_MEDIA_AUDIO) || (mtype==GF_ISOM_MEDIA_VISUAL)) { + switch (mstype) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + nb_amr++; + break; + case GF_ISOM_SUBTYPE_3GP_H263: nb_h263++; break; + case GF_ISOM_SUBTYPE_3GP_EVRC: nb_evrc++; break; + case GF_ISOM_SUBTYPE_3GP_QCELP: nb_qcelp++; break; + case GF_ISOM_SUBTYPE_3GP_SMV: nb_smv++; break; + case GF_ISOM_SUBTYPE_AVC_H264: nb_avc++; break; + case GF_ISOM_SUBTYPE_MPEG4: + case GF_ISOM_SUBTYPE_MPEG4_CRYP: + { + GF_DecoderConfig *dcd = gf_isom_get_decoder_config(file, i+1, 1); + switch (dcd->streamType) { + case 0x04: + if (dcd->objectTypeIndication==0x20) nb_m4v++; + else if (dcd->objectTypeIndication==0x21) nb_avc++; + else nb_v++; + break; + case 0x05: + switch (dcd->objectTypeIndication) { + case 0x66: case 0x67: case 0x68: case 0x40: nb_aac++; break; + case 0x69: case 0x6B: nb_mp3++; break; + case 0xA0: nb_evrc++; break; + case 0xA1: nb_smv++; break; + case 0xE1: nb_qcelp++; break; + default: nb_a++; break; + } + break; + /*SHOULD NEVER HAPPEN - IF SO, BROKEN MPEG4 FILE*/ + default: + nb_any++; + break; + } + gf_odf_desc_del((GF_Descriptor *)dcd); + } + break; + default: + if (mtype==GF_ISOM_MEDIA_VISUAL) nb_v++; + else nb_a++; + break; + } + } else if ((mtype==GF_ISOM_SUBTYPE_MPEG4) || (mtype==GF_ISOM_SUBTYPE_MPEG4_CRYP)) nb_m4s++; + else nb_any++; + } + if (nb_any) return GF_ISOM_BRAND_ISOM; + if (nb_qcelp || nb_evrc || nb_smv) { + /*non std mix of streams*/ + if (nb_m4s || nb_avc || nb_scene || nb_od || nb_mp3 || nb_a || nb_v) return GF_ISOM_BRAND_ISOM; + return GF_ISOM_BRAND_3G2A; + } + /*other a/v/s streams*/ + if (nb_v || nb_a || nb_m4s) return GF_ISOM_BRAND_MP42; + + nb_v = nb_m4v + nb_avc + nb_h263; + nb_a = nb_mp3 + nb_aac + nb_amr; + + /*avc file: whatever has AVC and no systems*/ + if (nb_avc) { + if (!nb_scene && !nb_od) return GF_ISOM_BRAND_AVC1; + return GF_ISOM_BRAND_MP42; + } + /*MP3: ISMA and MPEG4*/ + if (nb_mp3) { + if (!nb_text && (nb_v<=1) && (nb_a<=1) && (nb_scene==1) && (nb_od==1)) + return GF_4CC('I', 'S', 'M', 'A'); + return GF_ISOM_BRAND_MP42; + } + /*MP4*/ + if (nb_scene || nb_od) { + /*issue with AMR and H263 which don't have MPEG mapping: non compliant file*/ + if (nb_amr || nb_h263) return GF_ISOM_BRAND_ISOM; + return GF_ISOM_BRAND_MP42; + } + /*use ISMA (3GP fine too)*/ + if (!nb_amr && !nb_h263 && !nb_text) { + if ((nb_v<=1) && (nb_a<=1)) return GF_4CC('I', 'S', 'M', 'A'); + return GF_ISOM_BRAND_MP42; + } + + if ((nb_v<=1) && (nb_a<=1) && (nb_text<=1)) return nb_text ? GF_ISOM_BRAND_3GP6 : GF_ISOM_BRAND_3GP5; + return GF_ISOM_BRAND_3GG6; +} + +GF_ItemListBox *gf_ismo_locate_box(GF_List *list, u32 boxType, bin128 UUID) +{ + u32 i; + GF_Box *box; + i=0; + while ((box = (GF_Box *)gf_list_enum(list, &i))) { + if (box->type == boxType) { + GF_UUIDBox* box2 = (GF_UUIDBox* )box; + if (boxType != GF_ISOM_BOX_TYPE_UUID) return (GF_ItemListBox *)box; + if (!memcmp(box2->uuid, UUID, 16)) return (GF_ItemListBox *)box; + } + } + return NULL; +} + +/*Apple extensions*/ + + +GF_EXPORT +GF_Err gf_isom_apple_get_tag(GF_ISOFile *mov, u32 tag, const char **data, u32 *data_len) +{ + u32 i; + GF_ListItemBox *info; + GF_ItemListBox *ilst; + GF_MetaBox *meta; + + *data = NULL; + *data_len = 0; + + meta = gf_isom_apple_get_meta_extensions(mov); + if (!meta) return GF_URL_ERROR; + + ilst = gf_ismo_locate_box(meta->other_boxes, GF_ISOM_BOX_TYPE_ILST, NULL); + if (!ilst) return GF_URL_ERROR; + + if (tag==GF_ISOM_ITUNE_PROBE) return GF_OK; + + i=0; + while ( (info=gf_list_enum(ilst->tags, &i))) { + if (info->type==tag) break; + /*special cases*/ + if ((tag==GF_ISOM_ITUNE_GENRE) && (info->type==GF_ISOM_BOX_TYPE_0xA9GEN)) break; + info = NULL; + } + if (!info || !info->data || !info->data->data) return GF_URL_ERROR; + + if ((tag == GF_ISOM_ITUNE_GENRE) && (info->data->flags == 0)) { + if (info->data->dataSize && (info->data->dataSize>2) && (info->data->dataSize < 5)) { + GF_BitStream* bs = gf_bs_new(info->data->data, info->data->dataSize, GF_BITSTREAM_READ); + *data_len = gf_bs_read_int(bs, info->data->dataSize * 8); + gf_bs_del(bs); + return GF_OK; + } + } +// if (info->data->flags != 0x1) return GF_URL_ERROR; + *data = info->data->data; + *data_len = info->data->dataSize; + if ((tag==GF_ISOM_ITUNE_COVER_ART) && (info->data->flags==14)) *data_len |= (1<<31); + return GF_OK; +} + + + + +GF_EXPORT +GF_Err gf_isom_get_track_switch_group_count(GF_ISOFile *movie, u32 trackNumber, u32 *alternateGroupID, u32 *nb_groups) +{ + GF_UserDataMap *map; + GF_TrackBox *trak; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + *alternateGroupID = trak->Header->alternate_group; + *nb_groups = 0; + if (!trak->udta) return GF_OK; + + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); + if (!map) return 0; + *nb_groups = gf_list_count(map->boxList); + return GF_OK; +} + +GF_EXPORT +const u32 *gf_isom_get_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 group_index, u32 *switchGroupID, u32 *criteriaListSize) +{ + GF_TrackBox *trak; + GF_UserDataMap *map; + GF_TrackSelectionBox *tsel; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!group_index || !trak || !trak->udta) return NULL; + + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); + if (!map) return NULL; + tsel = gf_list_get(map->boxList, group_index-1); + *switchGroupID = tsel->switchGroup; + *criteriaListSize = tsel->attributeListCount; + return (const u32 *) tsel->attributeList; +} + + +GF_EXPORT +GF_Err gf_isom_get_timed_meta_data_info(GF_ISOFile *file, u32 track, u32 sampleDescription, Bool *is_xml, const char **mime_or_namespace, const char **content_encoding, const char **schema_loc) +{ + GF_TrackBox *trak; + GF_MetaDataSampleEntryBox *ptr; + trak = gf_isom_get_track_from_file(file, track); + if (!trak || !sampleDescription) return GF_BAD_PARAM; + ptr = (GF_MetaDataSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, sampleDescription-1); + if (!ptr) return GF_BAD_PARAM; + + if (ptr->type==GF_ISOM_BOX_TYPE_METX) { + if (is_xml) *is_xml = 1; + if (schema_loc) *schema_loc = ptr->xml_schema_loc; + } else { + if (schema_loc) *schema_loc = NULL; + } + if (mime_or_namespace) *mime_or_namespace = ptr->mime_type_or_namespace; + if (content_encoding) *content_encoding = ptr->content_encoding; + return GF_OK; +} + +u32 gf_isom_get_next_alternate_group_id(GF_ISOFile *movie) +{ + u32 id = 0; + u32 i=0; + + while (i< gf_isom_get_track_count(movie) ) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, i+1); + if (trak->Header->alternate_group > id) + id = trak->Header->alternate_group; + i++; + } + return id+1; +} + diff --git a/src/isomedia/isom_store.c b/src/isomedia/isom_store.c new file mode 100644 index 0000000..a14d2c4 --- /dev/null +++ b/src/isomedia/isom_store.c @@ -0,0 +1,1285 @@ + +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_READ_ONLY + +#define GPAC_ISOM_CPRT_NOTICE "IsoMedia File Produced with GPAC "GPAC_FULL_VERSION + +static GF_Err gf_isom_insert_copyright(GF_ISOFile *movie) +{ + u32 i; + GF_Box *a; + GF_FreeSpaceBox *_free; + i=0; + while ((a = (GF_Box *)gf_list_enum(movie->TopBoxes, &i))) { + if (a->type == GF_ISOM_BOX_TYPE_FREE) { + _free = (GF_FreeSpaceBox *)a; + if (_free->dataSize) { + if (!strcmp(_free->data, GPAC_ISOM_CPRT_NOTICE)) return GF_OK; + if (strstr(_free->data, "File Produced with GPAC")) { + free(_free->data); + _free->data = strdup(GPAC_ISOM_CPRT_NOTICE); + _free->dataSize = strlen(_free->data); + return GF_OK; + } + } + } + } + a = gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE); + if (!a) return GF_OUT_OF_MEM; + _free = (GF_FreeSpaceBox *)a; + _free->dataSize = strlen(GPAC_ISOM_CPRT_NOTICE) + 1; + _free->data = strdup(GPAC_ISOM_CPRT_NOTICE); + if (!_free->data) return GF_OUT_OF_MEM; + return gf_list_add(movie->TopBoxes, _free); +} + +typedef struct +{ + /*the curent sample of this track*/ + u32 sampleNumber; + /*timeScale of the media (for interleaving)*/ + u32 timeScale; + /*this is for generic, time-based interleaving. Expressed in Media TimeScale*/ + u32 chunkDur; + u64 DTSprev; + u8 isDone; + u64 prev_offset; + GF_MediaBox *mdia; + /*each writer has a sampleToChunck and ChunkOffset tables + these tables are filled during emulation mode and then will replace the table in the GF_SampleTableBox*/ + GF_SampleToChunkBox *stsc; + /*we don't know if it's a large offset or not*/ + GF_Box *stco; +} TrackWriter; + +typedef struct +{ + char *buffer; + u32 size; + GF_ISOFile *movie; + u32 total_samples, nb_done; +} MovieWriter; + +void CleanWriters(GF_List *writers) +{ + TrackWriter *writer; + while (gf_list_count(writers)) { + writer = (TrackWriter*)gf_list_get(writers, 0); + gf_isom_box_del(writer->stco); + gf_isom_box_del((GF_Box *)writer->stsc); + free(writer); + gf_list_rem(writers, 0); + } +} + +void ResetWriters(GF_List *writers) +{ + u32 i; + TrackWriter *writer; + i=0; + while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) { + writer->isDone = 0; + writer->chunkDur = 0; + writer->DTSprev = 0; + writer->sampleNumber = 1; + gf_isom_box_del((GF_Box *)writer->stsc); + writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); + if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) { + free(((GF_ChunkOffsetBox *)writer->stco)->offsets); + ((GF_ChunkOffsetBox *)writer->stco)->offsets = NULL; + ((GF_ChunkOffsetBox *)writer->stco)->nb_entries = 0; + ((GF_ChunkOffsetBox *)writer->stco)->alloc_size = 0; + } else { + free(((GF_ChunkLargeOffsetBox *)writer->stco)->offsets); + ((GF_ChunkLargeOffsetBox *)writer->stco)->offsets = NULL; + ((GF_ChunkLargeOffsetBox *)writer->stco)->nb_entries = 0; + ((GF_ChunkLargeOffsetBox *)writer->stco)->alloc_size = 0; + } + } +} + +GF_Err SetupWriters(MovieWriter *mw, GF_List *writers, u8 interleaving) +{ + u32 i, trackCount; + TrackWriter *writer; + GF_TrackBox *trak; + GF_ISOFile *movie = mw->movie; + + mw->total_samples = mw->nb_done = 0; + if (!movie->moov) return GF_OK; + + trackCount = gf_list_count(movie->moov->trackList); + for (i = 0; i < trackCount; i++) { + trak = gf_isom_get_track(movie->moov, i+1); + + GF_SAFEALLOC(writer, TrackWriter); + if (!writer) goto exit; + writer->sampleNumber = 1; + writer->mdia = trak->Media; + writer->timeScale = trak->Media->mediaHeader->timeScale; + writer->isDone = 0; + writer->DTSprev = 0; + writer->chunkDur = 0; + writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); + if (trak->Media->information->sampleTable->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO); + } else { + writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + } + /*stops from chunk escape*/ + if (interleaving) writer->mdia->information->sampleTable->MaxSamplePerChunk = 0; + /*for progress, assume only one descIndex*/ + if (Media_IsSelfContained(writer->mdia, 1)) mw->total_samples += trak->Media->information->sampleTable->SampleSize->sampleCount; + /*optimization for interleaving: put audio last (this can be overriden by priorities)*/ + if (movie->storageMode != GF_ISOM_STORE_INTERLEAVED) { + gf_list_add(writers, writer); + } else { + if (writer->mdia->information->InfoHeader && writer->mdia->information->InfoHeader->type == GF_ISOM_BOX_TYPE_SMHD) { + gf_list_add(writers, writer); + } else { + gf_list_insert(writers, writer, 0); + } + } + } + return GF_OK; + +exit: + CleanWriters(writers); + return GF_OUT_OF_MEM; +} + + +static void ShiftMetaOffset(GF_MetaBox *meta, u64 offset) +{ + u32 i, count; + if (!meta->item_locations) return; + + count = gf_list_count(meta->item_locations->location_entries); + for (i=0; iitem_locations->location_entries, i); + if (iloc->data_reference_index) continue; + if (!iloc->base_offset) { + GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); + if (entry && !entry->extent_length && !entry->original_extent_offset && (gf_list_count(iloc->extent_entries)==1) ) + continue; + } + + iloc->base_offset += offset; + } +} + +static GF_Err ShiftOffset(GF_ISOFile *file, GF_List *writers, u64 offset) +{ + u32 i, j, k, l, last; + TrackWriter *writer; + GF_StscEntry *ent; + GF_ChunkOffsetBox *stco; + GF_ChunkLargeOffsetBox *co64; + + if (file->meta) ShiftMetaOffset(file->meta, offset); + if (file->moov && file->moov->meta) ShiftMetaOffset(file->moov->meta, offset); + + i=0; + while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) { + if (writer->mdia->mediaTrack->meta) ShiftMetaOffset(writer->mdia->mediaTrack->meta, offset); + + //we have to proceed entry by entry in case a part of the media is not self-contained... + for (j=0; jstsc->nb_entries; j++) { + ent = &writer->stsc->entries[j]; + if (!Media_IsSelfContained(writer->mdia, ent->sampleDescriptionIndex)) continue; + + //OK, get the chunk(s) number(s) and "shift" its (their) offset(s). + if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *) writer->stco; + //be carefull for the last entry, nextChunk is set to 0 in edit mode... + last = ent->nextChunk ? ent->nextChunk : stco->nb_entries + 1; + for (k = ent->firstChunk; k < last; k++) { + + if (stco->offsets[k-1] + offset > 0xFFFFFFFF) { + //too bad, rewrite the table.... + co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + if (!co64) return GF_OUT_OF_MEM; + co64->nb_entries = stco->nb_entries; + co64->offsets = (u64*)malloc(co64->nb_entries * sizeof(u64)); + if (!co64) { + gf_isom_box_del((GF_Box *)co64); + return GF_OUT_OF_MEM; + } + //duplicate the table + for (l = 0; l < co64->nb_entries; l++) { + co64->offsets[l] = (u64) stco->offsets[l]; + if (l + 1 == k) co64->offsets[l] += offset; + } + //and replace our box + gf_isom_box_del(writer->stco); + writer->stco = (GF_Box *)co64; + } else { + stco->offsets[k-1] += (u32) offset; + } + } + } else { + co64 = (GF_ChunkLargeOffsetBox *) writer->stco; + //be carefull for the last entry ... + last = ent->nextChunk ? ent->nextChunk : co64->nb_entries + 1; + for (k = ent->firstChunk; k < last; k++) { + co64->offsets[k-1] += offset; + } + } + } + } + + return GF_OK; + +} + + +//replace the chunk and offset tables... +static GF_Err WriteMoovAndMeta(GF_ISOFile *movie, GF_List *writers, GF_BitStream *bs) +{ + u32 i; + TrackWriter *writer; + GF_Err e; + GF_Box *stco; + GF_SampleToChunkBox *stsc; + + if (movie->meta) { + //write the moov box... + e = gf_isom_box_size((GF_Box *)movie->meta); + if (e) return e; + e = gf_isom_box_write((GF_Box *)movie->meta, bs); + if (e) return e; + } + + if (movie->moov) { + //switch all our tables + i=0; + while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) { + //don't delete them !!! + stsc = writer->mdia->information->sampleTable->SampleToChunk; + stco = writer->mdia->information->sampleTable->ChunkOffset; + writer->mdia->information->sampleTable->SampleToChunk = writer->stsc; + writer->mdia->information->sampleTable->ChunkOffset = writer->stco; + writer->stco = stco; + writer->stsc = stsc; + } + //write the moov box... + e = gf_isom_box_size((GF_Box *)movie->moov); + if (e) return e; + e = gf_isom_box_write((GF_Box *)movie->moov, bs); + //and re-switch our table. We have to do it that way because it is + //needed when the moov is written first + i=0; + while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) { + //don't delete them !!! + stsc = writer->stsc; + stco = writer->stco; + writer->stsc = writer->mdia->information->sampleTable->SampleToChunk; + writer->stco = writer->mdia->information->sampleTable->ChunkOffset; + writer->mdia->information->sampleTable->SampleToChunk = stsc; + writer->mdia->information->sampleTable->ChunkOffset = stco; + } + if (e) return e; + } + return GF_OK; +} + +//compute the size of the moov as it will be written. +u64 GetMoovAndMetaSize(GF_ISOFile *movie, GF_List *writers) +{ + u32 i; + u64 size; + TrackWriter *writer; + + size = 0; + if (movie->moov) { + gf_isom_box_size((GF_Box *)movie->moov); + size = movie->moov->size; + if (size > 0xFFFFFFFF) size += 8; + + i=0; + while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) { + size -= writer->mdia->information->sampleTable->ChunkOffset->size; + size -= writer->mdia->information->sampleTable->SampleToChunk->size; + gf_isom_box_size((GF_Box *)writer->stsc); + gf_isom_box_size(writer->stco); + size += writer->stsc->size; + size += writer->stco->size; + } + } + if (movie->meta) { + u64 msize; + gf_isom_box_size((GF_Box *)movie->meta); + msize = movie->meta->size; + if (msize > 0xFFFFFFFF) msize += 8; + size += msize; + } + return size; +} + +//Write a sample to the file - this is only called for self-contained media +GF_Err WriteSample(MovieWriter *mw, u32 size, u64 offset, u8 isEdited, GF_BitStream *bs) +{ + GF_DataMap *map; + u32 bytes; + + if (size>mw->size) { + mw->buffer = (char*)realloc(mw->buffer, size); + mw->size = size; + } + + if (!mw->buffer) return GF_OUT_OF_MEM; + + if (isEdited) { + map = mw->movie->editFileMap; + } else { + map = mw->movie->movieFileMap; + } + //get the payload... + bytes = gf_isom_datamap_get_data(map, mw->buffer, size, offset); + if (bytes != size) return GF_IO_ERR; + //write it to our stream... + bytes = gf_bs_write_data(bs, mw->buffer, size); + if (bytes != size) return GF_IO_ERR; + + mw->nb_done++; + gf_set_progress("ISO File Writing", mw->nb_done, mw->total_samples); + return GF_OK; +} + + +GF_Err DoWriteMeta(GF_ISOFile *file, GF_MetaBox *meta, GF_BitStream *bs, Bool Emulation, u64 baseOffset, u64 *mdatSize) +{ + GF_ItemExtentEntry *entry; + u64 maxExtendOffset, maxExtendSize; + u32 i, j, count; + + maxExtendOffset = 0; + maxExtendSize = 0; + *mdatSize = 0; + if (!meta->item_locations) return GF_OK; + + count = gf_list_count(meta->item_locations->location_entries); + for (i=0; iitem_locations->location_entries, i); + /*get item info*/ + GF_ItemInfoEntryBox *iinf = NULL; + j=0; + while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) { + if (iinf->item_ID==iloc->item_ID) break; + iinf = NULL; + } + + if (!iloc->base_offset && (gf_list_count(iloc->extent_entries)==1)) { + entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); + if (!entry->extent_length && !entry->original_extent_offset) { + entry->extent_offset = 0; + continue; + } + } + + it_size = 0; + /*for self contained only*/ + if (!iloc->data_reference_index) { + iloc->base_offset = baseOffset; + + /*new resource*/ + if (iinf->full_path) { + FILE *src = gf_f64_open(iinf->full_path, "rb"); + + if (!src) continue; + gf_f64_seek(src, 0, SEEK_END); + it_size = gf_f64_tell(src); + gf_f64_seek(src, 0, SEEK_SET); + if (maxExtendSizeextent_entries)) { + GF_SAFEALLOC(entry, GF_ItemExtentEntry); + gf_list_add(iloc->extent_entries, entry); + } + entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); + entry->extent_offset = 0; + entry->extent_length = it_size; + + /*OK write to mdat*/ + if (!Emulation) { + char cache_data[4096]; + u64 remain = entry->extent_length; + while (remain) { + u32 size_cache = (remain>4096) ? 4096 : (u32) remain; + fread(cache_data, 1, size_cache, src); + gf_bs_write_data(bs, cache_data, size_cache); + remain -= size_cache; + } + } + fclose(src); + } + else if (gf_list_count(iloc->extent_entries)) { + u32 j; + j=0; + while ((entry = (GF_ItemExtentEntry *)gf_list_enum(iloc->extent_entries, &j))) { + if (j && (maxExtendOffsetextent_offset = baseOffset + it_size; + + it_size += entry->extent_length; + if (maxExtendSizeextent_length) maxExtendSize = entry->extent_length; + + /*Reading from the input file*/ + if (!Emulation) { + char cache_data[4096]; + u64 remain = entry->extent_length; + gf_bs_seek(file->movieFileMap->bs, entry->original_extent_offset + iloc->original_base_offset); + while (remain) { + u32 size_cache = (remain>4096) ? 4096 : (u32) remain; + gf_bs_read_data(file->movieFileMap->bs, cache_data, size_cache); + /*Writing to the output file*/ + gf_bs_write_data(bs, cache_data, size_cache); + remain -= size_cache; + } + } + } + } + baseOffset += it_size; + *mdatSize += it_size; + } else { + /*we MUST have at least one extent for the dref data*/ + if (!gf_list_count(iloc->extent_entries)) { + GF_SAFEALLOC(entry, GF_ItemExtentEntry); + gf_list_add(iloc->extent_entries, entry); + } + entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); + entry->extent_offset = 0; + /*0 means full length of referenced file*/ + entry->extent_length = 0; + } + } + /*update offset & size length fields*/ + if (baseOffset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8; + else if (baseOffset) meta->item_locations->base_offset_size = 4; + + if (maxExtendSize>0xFFFFFFFF) meta->item_locations->length_size = 8; + else if (maxExtendSize) meta->item_locations->length_size = 4; + + if (maxExtendOffset>0xFFFFFFFF) meta->item_locations->offset_size = 8; + else if (maxExtendOffset) meta->item_locations->offset_size = 4; + return GF_OK; +} + +//this function writes track by track in the order of tracks inside the moov... +GF_Err DoWrite(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset) +{ + u32 i; + GF_Err e; + TrackWriter *writer; + u64 offset, sampOffset, predOffset; + u32 chunkNumber, descIndex, sampSize; + u8 isEdited, force; + u64 size, mdatSize = 0; + GF_ISOFile *movie = mw->movie; + + /*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/ + if (movie->openMode != GF_ISOM_OPEN_WRITE) { + if (movie->meta) { + e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += size; + } + if (movie->moov && movie->moov->meta) { + e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += size; + } + i=0; + while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) { + if (writer->mdia->mediaTrack->meta) { + e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += size; + } + } + } + + offset = StartOffset; + predOffset = 0; + i=0; + while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) { + while (!writer->isDone) { + //To Check: are empty sample tables allowed ??? + if (writer->sampleNumber > writer->mdia->information->sampleTable->SampleSize->sampleCount) { + writer->isDone = 1; + continue; + } + e = stbl_GetSampleInfos(writer->mdia->information->sampleTable, writer->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited); + if (e) return e; + e = stbl_GetSampleSize(writer->mdia->information->sampleTable->SampleSize, writer->sampleNumber, &sampSize); + if (e) return e; + + //update our chunks. + force = 0; + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + offset = sampOffset; + if (predOffset != offset) + force = 1; + } + //update our global offset... + if (Media_IsSelfContained(writer->mdia, descIndex) ) { + e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, offset, force); + if (e) return e; + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + predOffset = sampOffset + sampSize; + } else { + offset += sampSize; + mdatSize += sampSize; + } + } else { + if (predOffset != offset) force = 1; + predOffset = sampOffset + sampSize; + //we have a DataRef, so use the offset idicated in sampleToChunk and ChunkOffset tables... + e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, sampOffset, force); + if (e) return e; + } + //we write the sample if not emulation + if (!Emulation) { + if (Media_IsSelfContained(writer->mdia, descIndex) ) { + e = WriteSample(mw, sampSize, sampOffset, isEdited, bs); + if (e) return e; + } + } + //ok, the track is done + if (writer->sampleNumber == writer->mdia->information->sampleTable->SampleSize->sampleCount) { + writer->isDone = 1; + } else { + writer->sampleNumber ++; + } + } + } + //set the mdatSize... + movie->mdat->dataSize = mdatSize; + return GF_OK; +} + + +//write the file track by track, with moov box before or after the mdat +GF_Err WriteFlat(MovieWriter *mw, u8 moovFirst, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + u64 offset, finalOffset, totSize, begin, firstSize, finalSize; + GF_Box *a; + GF_List *writers = gf_list_new(); + GF_ISOFile *movie = mw->movie; + + begin = totSize = 0; + + //first setup the writers + e = SetupWriters(mw, writers, 0); + if (e) goto exit; + + if (!moovFirst) { + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + begin = 0; + totSize = gf_isom_datamap_get_offset(movie->editFileMap); + /*start boxes have not been written yet, do it*/ + if (!totSize) { + if (movie->is_jp2) { + gf_bs_write_u32(movie->editFileMap->bs, 12); + gf_bs_write_u32(movie->editFileMap->bs, GF_4CC('j','P',' ',' ')); + gf_bs_write_u32(movie->editFileMap->bs, 0x0D0A870A); + totSize += 12; + begin += 12; + } + if (movie->brand) { + e = gf_isom_box_size((GF_Box *)movie->brand); if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs); if (e) goto exit; + totSize += movie->brand->size; + begin += movie->brand->size; + } + if (movie->pdin) { + e = gf_isom_box_size((GF_Box *)movie->pdin); if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs); if (e) goto exit; + totSize += movie->pdin->size; + begin += movie->pdin->size; + } + } else { + if (movie->is_jp2) begin += 12; + if (movie->brand) begin += movie->brand->size; + if (movie->pdin) begin += movie->pdin->size; + } + totSize -= begin; + } else { + if (movie->is_jp2) { + gf_bs_write_u32(bs, 12); + gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ')); + gf_bs_write_u32(bs, 0x0D0A870A); + } + if (movie->brand) { + e = gf_isom_box_size((GF_Box *)movie->brand); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->brand, bs); + if (e) goto exit; + } + /*then progressive download*/ + if (movie->pdin) { + e = gf_isom_box_size((GF_Box *)movie->pdin); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->pdin, bs); + if (e) goto exit; + } + } + + //if the moov is at the end, write directly + i=0; + while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { + switch (a->type) { + /*written by hand*/ + case GF_ISOM_BOX_TYPE_MOOV: + case GF_ISOM_BOX_TYPE_META: + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_PDIN: + break; + case GF_ISOM_BOX_TYPE_MDAT: + //in case we're capturing + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + //emulate a write to recreate our tables (media data already written) + e = DoWrite(mw, writers, bs, 1, begin); + if (e) goto exit; + continue; + } + //to avoid computing the size each time write always 4 + 4 + 8 bytes before + begin = gf_bs_get_position(bs); + gf_bs_write_u64(bs, 0); + gf_bs_write_u64(bs, 0); + e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs)); + if (e) goto exit; + totSize = gf_bs_get_position(bs) - begin; + break; + default: + e = gf_isom_box_size(a); + if (e) goto exit; + e = gf_isom_box_write(a, bs); + if (e) goto exit; + break; + } + } + + //OK, write the movie box. + e = WriteMoovAndMeta(movie, writers, bs); + if (e) goto exit; + + /*if data has been written, update mdat size*/ + if (totSize) { + offset = gf_bs_get_position(bs); + e = gf_bs_seek(bs, begin); + if (e) goto exit; + if (totSize > 0xFFFFFFFF) { + gf_bs_write_u32(bs, 1); + } else { + gf_bs_write_u32(bs, (u32) totSize); + } + gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT); + if (totSize > 0xFFFFFFFF) gf_bs_write_u64(bs, totSize); + e = gf_bs_seek(bs, offset); + } + movie->mdat->size = totSize; + goto exit; + } + + //nope, we have to write the moov first. The pb is that + //1 - we don't know its size till the mdat is written + //2 - we don't know the ofset at which the mdat will start... + //3 - once the mdat is written, the chunkOffset table can have changed... + + if (movie->is_jp2) { + gf_bs_write_u32(bs, 12); + gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ')); + gf_bs_write_u32(bs, 0x0D0A870A); + } + if (movie->brand) { + e = gf_isom_box_size((GF_Box *)movie->brand); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->brand, bs); + if (e) goto exit; + } + /*then progressive dnload*/ + if (movie->pdin) { + e = gf_isom_box_size((GF_Box *)movie->pdin); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->pdin, bs); + if (e) goto exit; + } + //What we will do is first emulate the write from the begining... + //note: this will set the size of the mdat + e = DoWrite(mw, writers, bs, 1, gf_bs_get_position(bs)); + if (e) goto exit; + + firstSize = GetMoovAndMetaSize(movie, writers); + //offset = (firstSize > 0xFFFFFFFF ? firstSize + 8 : firstSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + offset = firstSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + e = ShiftOffset(movie, writers, offset); + if (e) goto exit; + //get the size and see if it has changed (eg, we moved to 64 bit offsets) + finalSize = GetMoovAndMetaSize(movie, writers); + if (firstSize != finalSize) { + //we need to remove our offsets + ResetWriters(writers); + //finalOffset = (finalSize > 0xFFFFFFFF ? finalSize + 8 : finalSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + finalOffset = finalSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + //OK, now we're sure about the final size. + //we don't need to re-emulate, as the only thing that changed is the offset + //so just shift the offset + e = ShiftOffset(movie, writers, finalOffset - offset); + if (e) goto exit; + } + //now write our stuff + e = WriteMoovAndMeta(movie, writers, bs); + if (e) goto exit; + e = gf_isom_box_size((GF_Box *)movie->mdat); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->mdat, bs); + if (e) goto exit; + + //we don't need the offset as the moov is already written... + ResetWriters(writers); + e = DoWrite(mw, writers, bs, 0, 0); + if (e) goto exit; + //then the rest + i=0; + while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { + switch (a->type) { + case GF_ISOM_BOX_TYPE_MOOV: + case GF_ISOM_BOX_TYPE_META: + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_PDIN: + case GF_ISOM_BOX_TYPE_MDAT: + break; + default: + e = gf_isom_box_size(a); + if (e) goto exit; + e = gf_isom_box_write(a, bs); + if (e) goto exit; + } + } + +exit: + CleanWriters(writers); + gf_list_del(writers); + return e; +} + +GF_Err DoFullInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u32 StartOffset) +{ + + u32 i, tracksDone; + TrackWriter *tmp, *curWriter, *prevWriter; + GF_Err e; + u64 DTS, DTStmp, TStmp; + s64 res; + u32 descIndex, sampSize, chunkNumber; + u16 curGroupID, curTrackPriority; + u8 forceNewChunk, writeGroup, isEdited; + //this is used to emulate the write ... + u64 offset, totSize, sampOffset; + GF_ISOFile *movie = mw->movie; + e = GF_OK; + + + totSize = 0; + curGroupID = 1; + + prevWriter = NULL; + //we emulate a write from this offset... + offset = StartOffset; + writeGroup = 1; + tracksDone = 0; + + + //browse each groups + while (1) { + writeGroup = 1; + + //proceed a group + while (writeGroup) { + //first get the appropriated sample for the min time in this group + curWriter = NULL; + DTStmp = (u64) -1; + TStmp = 0; + curTrackPriority = (u16) -1; + + i=0; + while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) { + + //is it done writing ? + //is it in our group ?? + if (tmp->isDone || tmp->mdia->information->sampleTable->groupID != curGroupID) continue; + + //OK, get the current sample in this track + stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS); + res = TStmp ? DTStmp * tmp->timeScale - DTS * TStmp : 0; + if (res < 0) continue; + if ((!res) && curTrackPriority <= tmp->mdia->information->sampleTable->trackPriority) continue; + curWriter = tmp; + curTrackPriority = tmp->mdia->information->sampleTable->trackPriority; + DTStmp = DTS; + TStmp = tmp->timeScale; + } + //no sample found, we're done with this group + if (!curWriter) { + //we're done with the group + curTrackPriority = 0; + writeGroup = 0; + continue; + } + //To Check: are empty sample tables allowed ??? + if (curWriter->sampleNumber > curWriter->mdia->information->sampleTable->SampleSize->sampleCount) { + curWriter->isDone = 1; + tracksDone ++; + continue; + } + + e = stbl_GetSampleInfos(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited); + if (e) return e; + e = stbl_GetSampleSize(curWriter->mdia->information->sampleTable->SampleSize, curWriter->sampleNumber, &sampSize); + if (e) return e; + + //do we actually write, or do we emulate ? + if (Emulation) { + //are we in the same track ??? If not, force a new chunk when adding this sample + if (curWriter != prevWriter) { + forceNewChunk = 1; + } else { + forceNewChunk = 0; + } + //update our offsets... + if (Media_IsSelfContained(curWriter->mdia, descIndex) ) { + e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk); + if (e) return e; + offset += sampSize; + totSize += sampSize; + } else { + if (curWriter->prev_offset != sampOffset) forceNewChunk = 1; + curWriter->prev_offset = sampOffset + sampSize; + + //we have a DataRef, so use the offset idicated in sampleToChunk + //and ChunkOffset tables... + e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, 0); + if (e) return e; + } + } else { + //this is no game, we're writing .... + if (Media_IsSelfContained(curWriter->mdia, descIndex) ) { + e = WriteSample(mw, sampSize, sampOffset, isEdited, bs); + if (e) return e; + } + } + //ok, the sample is done + if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) { + curWriter->isDone = 1; + //one more track done... + tracksDone ++; + } else { + curWriter->sampleNumber ++; + } + prevWriter = curWriter; + } + //if all our track are done, break + if (tracksDone == gf_list_count(writers)) break; + //go to next group + curGroupID ++; + } + movie->mdat->dataSize = totSize; + return GF_OK; +} + +/*uncomment the following to easily test large file generation. This will prepend 4096*1MByte of 0 before the media data*/ +//#define TEST_LARGE_FILES + +GF_Err DoInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u32 StartOffset, Bool drift_inter) +{ + u32 i, tracksDone; + TrackWriter *tmp, *curWriter; + GF_Err e; + u32 descIndex, sampSize, chunkNumber; + u64 DTS; + u16 curGroupID; + u8 forceNewChunk, writeGroup, isEdited; + //this is used to emulate the write ... + u64 offset, sampOffset, size, mdatSize; + u32 count; + GF_ISOFile *movie = mw->movie; + + mdatSize = 0; + +#ifdef TEST_LARGE_FILES + if (!Emulation) { + char *blank; + u32 count, i; + i = count = 0; + blank = malloc(sizeof(char)*1024*1024); + memset(blank, 0, sizeof(char)*1024*1024); + count = 4096; + memset(blank, 0, sizeof(char)*1024*1024); + while (imeta) { + e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += (u32) size; + } + if (movie->moov && movie->moov->meta) { + e = DoWriteMeta(movie, movie->moov->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += (u32) size; + } + i=0; + while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) { + if (tmp->mdia->mediaTrack->meta) { + e = DoWriteMeta(movie, tmp->mdia->mediaTrack->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += (u32) size; + } + } + + + + if (movie->storageMode == GF_ISOM_STORE_TIGHT) + return DoFullInterleave(mw, writers, bs, Emulation, StartOffset); + + e = GF_OK; + + curGroupID = 1; + //we emulate a write from this offset... + offset = StartOffset; + writeGroup = 1; + tracksDone = 0; + +#ifdef TEST_LARGE_FILES + offset += mdatSize; +#endif + + count = gf_list_count(writers); + //browse each groups + while (1) { + /*the max DTS the chunk of the current writer*/ + u64 chunkLastDTS = 0; + /*the timescale related to the max DTS*/ + u32 chunkLastScale = 0; + + writeGroup = 1; + + //proceed a group + while (writeGroup) { + curWriter = NULL; + for (i=0 ; i < count; i++) { + tmp = (TrackWriter*)gf_list_get(writers, i); + + //is it done writing ? + if (tmp->isDone) continue; + + //is it in our group ?? + if (tmp->mdia->information->sampleTable->groupID != curGroupID) continue; + + //write till this chunk is full on this track... + while (1) { + //To Check: are empty sample tables allowed ??? + if (tmp->sampleNumber > tmp->mdia->information->sampleTable->SampleSize->sampleCount) { + tmp->isDone = 1; + tracksDone ++; + break; + } + + //OK, get the current sample in this track + stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS); + + //can this sample fit in our chunk ? + if ( ( (DTS - tmp->DTSprev) + tmp->chunkDur) * movie->moov->mvhd->timeScale > movie->interleavingTime * tmp->timeScale + /*drfit check: reject sample if outside our check window*/ + || (drift_inter && chunkLastDTS && ( ((u64)tmp->DTSprev*chunkLastScale) > ((u64)chunkLastDTS*tmp->timeScale)) ) + ) { + //in case the sample is longer than InterleaveTime + if (!tmp->chunkDur) { + forceNewChunk = 1; + } else { + //this one is full. go to next one (exit the loop) + tmp->chunkDur = 0; + break; + } + } else { + forceNewChunk = tmp->chunkDur ? 0 : 1; + } + //OK, we can write this track + curWriter = tmp; + + //small check for first 2 samples (DTS = 0 :) + if (tmp->sampleNumber == 2 && !tmp->chunkDur) forceNewChunk = 0; + + tmp->chunkDur += (u32) (DTS - tmp->DTSprev); + tmp->DTSprev = DTS; + + e = stbl_GetSampleInfos(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited); + if (e) return e; + e = stbl_GetSampleSize(curWriter->mdia->information->sampleTable->SampleSize, curWriter->sampleNumber, &sampSize); + if (e) return e; + + //do we actually write, or do we emulate ? + if (Emulation) { + //update our offsets... + if (Media_IsSelfContained(curWriter->mdia, descIndex) ) { + e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk); + if (e) return e; + offset += sampSize; + mdatSize += sampSize; + } else { + if (curWriter->prev_offset != sampOffset) forceNewChunk = 1; + curWriter->prev_offset = sampOffset + sampSize; + + //we have a DataRef, so use the offset idicated in sampleToChunk + //and ChunkOffset tables... + e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, forceNewChunk); + if (e) return e; + } + } else { + //this is no game, we're writing .... + if (Media_IsSelfContained(curWriter->mdia, descIndex) ) { + e = WriteSample(mw, sampSize, sampOffset, isEdited, bs); + if (e) return e; + } + } + //ok, the sample is done + if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) { + curWriter->isDone = 1; + //one more track done... + tracksDone ++; + break; + } else { + curWriter->sampleNumber ++; + } + } + /*record chunk end-time & track timescale for drift-controled interleaving*/ + if (drift_inter && curWriter) { + chunkLastScale = curWriter->timeScale; + chunkLastDTS = curWriter->DTSprev; + /*add one interleave window drift - since the "maxDTS" is the previously written one, we will + have the following cases: + - sample doesn't fit: post-pone and force new chunk + - sample time larger than previous chunk time + interleave: post-pone and force new chunk + - otherwise store and track becomes current reference + + this ensures a proper drift regulation (max DTS diff is less than the interleaving window) + */ + chunkLastDTS += curWriter->timeScale * movie->interleavingTime / movie->moov->mvhd->timeScale; + } + } + //no sample found, we're done with this group + if (!curWriter) { + writeGroup = 0; + continue; + } + } + //if all our track are done, break + if (tracksDone == gf_list_count(writers)) break; + //go to next group + curGroupID ++; + } + if (movie->mdat) movie->mdat->dataSize = mdatSize; + return GF_OK; +} + + +static GF_Err WriteInterleaved(MovieWriter *mw, GF_BitStream *bs, Bool drift_inter) +{ + GF_Err e; + u32 i; + GF_Box *a; + u64 firstSize, finalSize, offset, finalOffset; + GF_List *writers = gf_list_new(); + GF_ISOFile *movie = mw->movie; + + //first setup the writers + e = SetupWriters(mw, writers, 1); + if (e) goto exit; + + + if (movie->is_jp2) { + gf_bs_write_u32(bs, 12); + gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ')); + gf_bs_write_u32(bs, 0x0D0A870A); + } + if (movie->brand) { + e = gf_isom_box_size((GF_Box *)movie->brand); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->brand, bs); + if (e) goto exit; + } + if (movie->pdin) { + e = gf_isom_box_size((GF_Box *)movie->pdin); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->pdin, bs); + if (e) goto exit; + } + + e = DoInterleave(mw, writers, bs, 1, (u32) gf_bs_get_position(bs), drift_inter); + if (e) goto exit; + + firstSize = GetMoovAndMetaSize(movie, writers); + offset = firstSize; + if (movie->mdat && movie->mdat->dataSize) offset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + e = ShiftOffset(movie, writers, offset); + if (e) goto exit; + //get the size and see if it has changed (eg, we moved to 64 bit offsets) + finalSize = GetMoovAndMetaSize(movie, writers); + if (firstSize != finalSize) { + //we need to remove our offsets + ResetWriters(writers); + finalOffset = finalSize; + if (movie->mdat->dataSize) finalOffset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + //OK, now we're sure about the final size -> shift the offsets + //we don't need to re-emulate, as the only thing that changed is the offset + //so just shift the offset + e = ShiftOffset(movie, writers, finalOffset - offset); + if (e) goto exit; + } + //now write our stuff + e = WriteMoovAndMeta(movie, writers, bs); + if (e) goto exit; + + /*we have 8 extra bytes for large size (not computed in gf_isom_box_size) */ + if (movie->mdat && movie->mdat->dataSize) { + if (movie->mdat->dataSize > 0xFFFFFFFF) movie->mdat->dataSize += 8; + e = gf_isom_box_size((GF_Box *)movie->mdat); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->mdat, bs); + if (e) goto exit; + } + + //we don't need the offset as we are writing... + ResetWriters(writers); + e = DoInterleave(mw, writers, bs, 0, 0, drift_inter); + if (e) goto exit; + + //then the rest + i=0; + while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { + switch (a->type) { + case GF_ISOM_BOX_TYPE_MOOV: + case GF_ISOM_BOX_TYPE_META: + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_PDIN: + case GF_ISOM_BOX_TYPE_MDAT: + break; + default: + e = gf_isom_box_size(a); + if (e) goto exit; + e = gf_isom_box_write(a, bs); + if (e) goto exit; + } + } + +exit: + CleanWriters(writers); + gf_list_del(writers); + return e; +} + + +GF_Err WriteToFile(GF_ISOFile *movie) +{ + FILE *stream; + GF_BitStream *bs; + MovieWriter mw; + GF_Err e = GF_OK; + if (!movie) return GF_BAD_PARAM; + + if (movie->openMode == GF_ISOM_OPEN_READ) return GF_BAD_PARAM; + + e = gf_isom_insert_copyright(movie); + if (e) return e; + + memset(&mw, 0, sizeof(mw)); + mw.movie = movie; + + //capture mode: we don't need a new bitstream + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + e = WriteFlat(&mw, 0, movie->editFileMap->bs); + } else { + //OK, we need a new bitstream + stream = gf_f64_open(movie->finalName, "w+b"); + if (!stream) return GF_IO_ERR; + bs = gf_bs_from_file(stream, GF_BITSTREAM_WRITE); + if (!bs) { + fclose(stream); + return GF_OUT_OF_MEM; + } + + switch (movie->storageMode) { + case GF_ISOM_STORE_TIGHT: + case GF_ISOM_STORE_INTERLEAVED: + e = WriteInterleaved(&mw, bs, 0); + break; + case GF_ISOM_STORE_DRIFT_INTERLEAVED: + e = WriteInterleaved(&mw, bs, 1); + break; + case GF_ISOM_STORE_STREAMABLE: + e = WriteFlat(&mw, 1, bs); + break; + default: + e = WriteFlat(&mw, 0, bs); + break; + } + + gf_bs_del(bs); + fclose(stream); + } + if (mw.buffer) free(mw.buffer); + if (mw.nb_done + +#ifndef GPAC_READ_ONLY + +GF_Err CanAccessMovie(GF_ISOFile *movie, u32 Mode) +{ + if (!movie) return GF_BAD_PARAM; + if (movie->openMode < Mode) return GF_ISOM_INVALID_MODE; + +#ifndef GF_ISOM_NO_FRAGMENTS + if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_ISOM_INVALID_MODE; +#endif + return GF_OK; +} + +static GF_Err unpack_track(GF_TrackBox *trak) +{ + GF_Err e = GF_OK; + if (!trak->is_unpacked) { + e = stbl_UnpackOffsets(trak->Media->information->sampleTable); + trak->is_unpacked = 1; + } + return e; +} + + +GF_Err FlushCaptureMode(GF_ISOFile *movie) +{ + GF_Err e; + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK; + /*make sure nothing was added*/ + if (gf_bs_get_position(movie->editFileMap->bs)) return GF_OK; + + /*add all first boxes*/ + if (movie->brand) { + e = gf_isom_box_size((GF_Box *)movie->brand); + if (e) return e; + e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs); + if (e) return e; + } + if (movie->pdin) { + e = gf_isom_box_size((GF_Box *)movie->pdin); + if (e) return e; + e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs); + if (e) return e; + } + + /*we have a trick here: the data will be stored on the fly, so the first + thing in the file is the MDAT. As we don't know if we have a large file (>4 GB) or not + do as if we had one and write 16 bytes: 4 (type) + 4 (size) + 8 (largeSize)...*/ + gf_bs_write_int(movie->editFileMap->bs, 0, 128); + return GF_OK; +} + +static GF_Err CheckNoData(GF_ISOFile *movie) +{ + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK; + if (gf_bs_get_position(movie->editFileMap->bs)) return GF_BAD_PARAM; + return GF_OK; +} + +/************************************************************** + File Writing / Editing +**************************************************************/ +//quick function to add an IOD/OD to the file if not present (iods is optional) +GF_Err AddMovieIOD(GF_MovieBox *moov, u8 isIOD) +{ + GF_Descriptor *od; + GF_ObjectDescriptorBox *iods; + + //do we have an IOD ?? If not, create one. + if (moov->iods) return GF_OK; + + if (isIOD) { + od = gf_odf_desc_new(GF_ODF_ISOM_IOD_TAG); + } else { + od = gf_odf_desc_new(GF_ODF_ISOM_OD_TAG); + } + if (!od) return GF_OUT_OF_MEM; + ((GF_IsomObjectDescriptor *)od)->objectDescriptorID = 1; + + iods = (GF_ObjectDescriptorBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_IODS); + iods->descriptor = od; + return moov_AddBox((GF_Box*)moov, (GF_Box *)iods); +} + +//add a track to the root OD +GF_EXPORT +GF_Err gf_isom_add_track_to_root_od(GF_ISOFile *movie, u32 trackNumber) +{ + GF_Err e; + GF_ES_ID_Inc *inc; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); + + if (gf_isom_is_track_in_root_od(movie, trackNumber) == 1) return GF_OK; + + inc = (GF_ES_ID_Inc *) gf_odf_desc_new(GF_ODF_ESD_INC_TAG); + inc->trackID = gf_isom_get_track_id(movie, trackNumber); + if (!inc->trackID) { + gf_odf_desc_del((GF_Descriptor *)inc); + return movie->LastError; + } + if ( (movie->LastError = gf_isom_add_desc_to_root_od(movie, (GF_Descriptor *)inc) ) ) { + return movie->LastError; + } + gf_odf_desc_del((GF_Descriptor *)inc); + return GF_OK; +} + +//remove the root OD +GF_Err gf_isom_remove_root_od(GF_ISOFile *movie) +{ + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + if (!movie->moov || !movie->moov->iods) return GF_OK; + gf_isom_box_del((GF_Box *)movie->moov->iods); + movie->moov->iods = NULL; + return GF_OK; +} + +//remove a track to the root OD +GF_Err gf_isom_remove_track_from_root_od(GF_ISOFile *movie, u32 trackNumber) +{ + GF_List *esds; + GF_ES_ID_Inc *inc; + u32 i; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + if (!movie->moov) return GF_OK; + + if (!gf_isom_is_track_in_root_od(movie, trackNumber)) return GF_OK; + + if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); + + switch (movie->moov->iods->descriptor->tag) { + case GF_ODF_ISOM_IOD_TAG: + esds = ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors; + break; + case GF_ODF_ISOM_OD_TAG: + esds = ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors; + break; + default: + return GF_ISOM_INVALID_FILE; + } + + //get the desc + i=0; + while ((inc = (GF_ES_ID_Inc*)gf_list_enum(esds, &i))) { + if (inc->trackID == gf_isom_get_track_id(movie, trackNumber)) { + gf_odf_desc_del((GF_Descriptor *)inc); + gf_list_rem(esds, i-1); + break; + } + } + //we don't remove the iod for P&Ls and other potential info + return GF_OK; +} + +//sets the enable flag of a track +GF_EXPORT +GF_Err gf_isom_set_track_enabled(GF_ISOFile *movie, u32 trackNumber, u8 enableTrack) +{ + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (enableTrack) { + trak->Header->flags |= 1; + } else { + trak->Header->flags &= ~1; + } + return GF_OK; +} + +GF_Err gf_isom_set_media_language(GF_ISOFile *movie, u32 trackNumber, char *three_char_code) +{ + GF_Err e; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + memcpy(trak->Media->mediaHeader->packedLanguage, three_char_code, sizeof(char)*3); + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + return GF_OK; +} + +static void gf_isom_set_root_iod(GF_ISOFile *movie) +{ + GF_IsomInitialObjectDescriptor *iod; + GF_IsomObjectDescriptor *od; + + gf_isom_insert_moov(movie); + if (!movie->moov->iods) { + AddMovieIOD(movie->moov, 1); + return; + } + //if OD, switch to IOD + if (movie->moov->iods->descriptor->tag == GF_ODF_ISOM_IOD_TAG) return; + od = (GF_IsomObjectDescriptor *) movie->moov->iods->descriptor; + iod = (GF_IsomInitialObjectDescriptor*)malloc(sizeof(GF_IsomInitialObjectDescriptor)); + memset(iod, 0, sizeof(GF_IsomInitialObjectDescriptor)); + + iod->ES_ID_IncDescriptors = od->ES_ID_IncDescriptors; + od->ES_ID_IncDescriptors = NULL; + //not used in root OD + iod->ES_ID_RefDescriptors = NULL; + iod->extensionDescriptors = od->extensionDescriptors; + od->extensionDescriptors = NULL; + iod->IPMP_Descriptors = od->IPMP_Descriptors; + od->IPMP_Descriptors = NULL; + iod->objectDescriptorID = od->objectDescriptorID; + iod->OCIDescriptors = od->OCIDescriptors; + od->OCIDescriptors = NULL; + iod->tag = GF_ODF_ISOM_IOD_TAG; + iod->URLString = od->URLString; + od->URLString = NULL; + + gf_odf_desc_del((GF_Descriptor *) od); + movie->moov->iods->descriptor = (GF_Descriptor *)iod; +} + +GF_Err gf_isom_add_desc_to_root_od(GF_ISOFile *movie, GF_Descriptor *theDesc) +{ + GF_Err e; + GF_Descriptor *desc, *dupDesc; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); + if (theDesc->tag==GF_ODF_IPMP_TL_TAG) gf_isom_set_root_iod(movie); + + desc = movie->moov->iods->descriptor; + //the type of desc is handled at the OD/IOD level, we'll be notified + //if the desc is not allowed + switch (desc->tag) { + case GF_ODF_ISOM_IOD_TAG: + case GF_ODF_ISOM_OD_TAG: + //duplicate the desc + e = gf_odf_desc_copy(theDesc, &dupDesc); + if (e) return e; + //add it (MUST BE (I)OD level desc) + movie->LastError = gf_odf_desc_add_desc(desc, dupDesc); + if (movie->LastError) gf_odf_desc_del((GF_Descriptor *)dupDesc); + break; + default: + movie->LastError = GF_ISOM_INVALID_FILE; + break; + } + return movie->LastError; +} + + +GF_Err gf_isom_set_timescale(GF_ISOFile *movie, u32 timeScale) +{ + GF_Err e; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + movie->moov->mvhd->timeScale = timeScale; + movie->interleavingTime = timeScale; + return GF_OK; +} + +GF_Err gf_isom_set_pl_indication(GF_ISOFile *movie, u8 PL_Code, u8 ProfileLevel) +{ + GF_IsomInitialObjectDescriptor *iod; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + gf_isom_set_root_iod(movie); + iod = (GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor; + + switch (PL_Code) { + case GF_ISOM_PL_AUDIO: + iod->audio_profileAndLevel = ProfileLevel; + break; + case GF_ISOM_PL_GRAPHICS: + iod->graphics_profileAndLevel = ProfileLevel; + break; + case GF_ISOM_PL_OD: + iod->OD_profileAndLevel = ProfileLevel; + break; + case GF_ISOM_PL_SCENE: + iod->scene_profileAndLevel = ProfileLevel; + break; + case GF_ISOM_PL_VISUAL: + iod->visual_profileAndLevel = ProfileLevel; + break; + case GF_ISOM_PL_INLINE: + iod->inlineProfileFlag = ProfileLevel ? 1 : 0; + break; + } + return GF_OK; +} + + +GF_Err gf_isom_set_root_od_id(GF_ISOFile *movie, u32 OD_ID) +{ + GF_Err e; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + gf_isom_insert_moov(movie); + if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); + + switch (movie->moov->iods->descriptor->tag) { + case GF_ODF_ISOM_OD_TAG: + ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID; + break; + case GF_ODF_ISOM_IOD_TAG: + ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID; + break; + default: + return GF_ISOM_INVALID_FILE; + } + return GF_OK; +} + +GF_Err gf_isom_set_root_od_url(GF_ISOFile *movie, char *url_string) +{ + GF_Err e; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); + switch (movie->moov->iods->descriptor->tag) { + case GF_ODF_ISOM_OD_TAG: + if (((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString) free(((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString); + ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? strdup(url_string) : NULL; + break; + case GF_ODF_ISOM_IOD_TAG: + if (((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString) free(((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString); + ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? strdup(url_string) : NULL; + break; + default: + return GF_ISOM_INVALID_FILE; + } + return GF_OK; +} + + + +//creates a new Track. If trackID = 0, the trackID is chosen by the API +//returns the track number or 0 if error +GF_EXPORT +u32 gf_isom_new_track(GF_ISOFile *movie, u32 trakID, u32 MediaType, u32 TimeScale) +{ + GF_Err e; + u64 now; + u8 isHint; + GF_TrackBox *trak; + GF_TrackHeaderBox *tkhd; + GF_MediaBox *mdia; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) { + gf_isom_set_last_error(movie, e); + return 0; + } + gf_isom_insert_moov(movie); + + + isHint = 0; + //we're creating a hint track... it's the same, but mode HAS TO BE EDIT + if (MediaType == GF_ISOM_MEDIA_HINT) { +// if (movie->openMode != GF_ISOM_OPEN_EDIT) return 0; + isHint = 1; + } + + mdia = NULL; + tkhd = NULL; + trak = NULL; + if (trakID) { + //check if we are in ES_ID boundaries + if (!isHint && (trakID > 0xFFFF)) { + gf_isom_set_last_error(movie, GF_BAD_PARAM); + return 0; + } + //here we should look for available IDs ... + if (!RequestTrack(movie->moov, trakID)) return 0; + } else { + trakID = movie->moov->mvhd->nextTrackID; + if (!trakID) trakID = 1; + /*ESIDs are on 16 bits*/ + if (! isHint && (trakID > 0xFFFF)) trakID = 1; + + while (1) { + if (RequestTrack(movie->moov, trakID)) break; + trakID += 1; + if (trakID == 0xFFFFFFFF) break; + } + if (trakID == 0xFFFFFFFF) { + gf_isom_set_last_error(movie, GF_BAD_PARAM); + return 0; + } + if (! isHint && (trakID > 0xFFFF)) { + gf_isom_set_last_error(movie, GF_BAD_PARAM); + return 0; + } + } + + //OK, now create a track... + trak = (GF_TrackBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAK); + if (!trak) { + gf_isom_set_last_error(movie, GF_OUT_OF_MEM); + return 0; + } + tkhd = (GF_TrackHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TKHD); + if (!tkhd) { + gf_isom_set_last_error(movie, GF_OUT_OF_MEM); + gf_isom_box_del((GF_Box *)trak); + return 0; + } + now = gf_isom_get_mp4time(); + tkhd->creationTime = now; + tkhd->modificationTime = now; + //OK, set up the media trak + e = NewMedia(&mdia, MediaType, TimeScale); + if (e) { + gf_isom_box_del((GF_Box *)mdia); + gf_isom_box_del((GF_Box *)trak); + gf_isom_box_del((GF_Box *)tkhd); + return 0; + } + //OK, add this media to our track + mdia->mediaTrack = trak; + + e = trak_AddBox((GF_Box*)trak, (GF_Box *) tkhd); if (e) goto err_exit; + e = trak_AddBox((GF_Box*)trak, (GF_Box *) mdia); if (e) goto err_exit; + tkhd->trackID = trakID; + + + //some default properties for Audio, Visual or private tracks + switch (MediaType) { + case GF_ISOM_MEDIA_VISUAL: + case GF_ISOM_MEDIA_SCENE: + case GF_ISOM_MEDIA_TEXT: + /*320-240 pix in 16.16*/ + tkhd->width = 0x01400000; + tkhd->height = 0x00F00000; + break; + case GF_ISOM_MEDIA_AUDIO: + tkhd->volume = 0x0100; + break; + } + + mdia->mediaHeader->creationTime = mdia->mediaHeader->modificationTime = now; + trak->Header->creationTime = trak->Header->modificationTime = now; + + //OK, add our trak + e = moov_AddBox((GF_Box*)movie->moov, (GF_Box *)trak); if (e) goto err_exit; + //set the new ID available + if (trakID+1> movie->moov->mvhd->nextTrackID) + movie->moov->mvhd->nextTrackID = trakID+1; + + //and return our track number + return gf_isom_get_track_by_id(movie, trakID); + +err_exit: + if (tkhd) gf_isom_box_del((GF_Box *)tkhd); + if (trak) gf_isom_box_del((GF_Box *)trak); + if (mdia) gf_isom_box_del((GF_Box *)mdia); + return 0; +} + + +//Create a new StreamDescription in the file. The URL and URN are used to describe external media +GF_EXPORT +GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *movie, + u32 trackNumber, + GF_ESD *esd, + char *URLname, + char *URNname, + u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_ESD *new_esd; + GF_TrackReferenceBox *tref; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || + !esd || !esd->decoderConfig || + !esd->slConfig) return GF_BAD_PARAM; + + tref = NULL; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + //duplicate our desc + e = gf_odf_desc_copy((GF_Descriptor *)esd, (GF_Descriptor **)&new_esd); + if (e) return e;; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + e = Track_SetStreamDescriptor(trak, 0, dataRefIndex, new_esd, outDescriptionIndex); + if (e) { + gf_odf_desc_del((GF_Descriptor *)new_esd); + return e; + } + if (new_esd->URLString) { + + } + return e; +} + +//Add samples to a track. Use streamDescriptionIndex to specify the desired stream (if several) +GF_EXPORT +GF_Err gf_isom_add_sample(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample) +{ + GF_Err e; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + u32 dataRefIndex; + u64 data_offset; + u32 descIndex; + GF_DataEntryURLBox *Dentry; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = FlushCaptureMode(movie); + if (e) return e; + + e = unpack_track(trak); + if (e) return e; + + //OK, add the sample + //1- Get the streamDescriptionIndex and dataRefIndex + //not specified, get the latest used... + descIndex = StreamDescriptionIndex; + if (!StreamDescriptionIndex) { + descIndex = trak->Media->information->sampleTable->currentEntryIndex; + } + e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); + if (e) return e; + if (!entry || !dataRefIndex) return GF_BAD_PARAM; + //set the current to this one + trak->Media->information->sampleTable->currentEntryIndex = descIndex; + + + //get this dataRef and return false if not self contained + Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->boxList, dataRefIndex - 1); + if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; + + //Open our data map. We are adding stuff, so use EDIT + e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); + if (e) return e; + + //Get the offset... + data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler); + + /*rewrite OD frame*/ + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + GF_ISOSample *od_sample = NULL; + e = Media_ParseODFrame(trak->Media, sample, &od_sample); + if (!e) e = Media_AddSample(trak->Media, data_offset, od_sample, descIndex, 0); + if (!e) e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, od_sample->data, od_sample->dataLength); + if (od_sample) gf_isom_sample_del(&od_sample); + } else { + e = Media_AddSample(trak->Media, data_offset, sample, descIndex, 0); + if (!e) e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, sample->data, sample->dataLength); + } + if (e) return e; + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + return SetTrackDuration(trak); +} + +GF_Err gf_isom_add_sample_shadow(GF_ISOFile *movie, u32 trackNumber, GF_ISOSample *sample) +{ + GF_Err e; + GF_TrackBox *trak; + GF_ISOSample *prev; + GF_SampleEntryBox *entry; + u32 dataRefIndex; + u64 data_offset; + u32 descIndex; + u32 sampleNum, prevSampleNum; + GF_DataEntryURLBox *Dentry; + Bool offset_times = 0; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !sample) return GF_BAD_PARAM; + + e = FlushCaptureMode(movie); + if (e) return e; + + e = unpack_track(trak); + if (e) return e; + + e = findEntryForTime(trak->Media->information->sampleTable, sample->DTS, 0, &sampleNum, &prevSampleNum); + if (e) return e; + /*we need the EXACT match*/ + if (!sampleNum) return GF_BAD_PARAM; + + prev = gf_isom_get_sample_info(movie, trackNumber, sampleNum, &descIndex, NULL); + if (!prev) return gf_isom_last_error(movie); + /*for conformance*/ + if (sample->DTS==prev->DTS) offset_times = 1; + gf_isom_sample_del(&prev); + + e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); + if (e) return e; + if (!entry || !dataRefIndex) return GF_BAD_PARAM; + trak->Media->information->sampleTable->currentEntryIndex = descIndex; + + //get this dataRef and return false if not self contained + Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->boxList, dataRefIndex - 1); + if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; + + //Open our data map. We are adding stuff, so use EDIT + e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); + if (e) return e; + + data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler); + if (offset_times) sample->DTS += 1; + + /*REWRITE ANY OD STUFF*/ + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + GF_ISOSample *od_sample = NULL; + e = Media_ParseODFrame(trak->Media, sample, &od_sample); + if (!e) e = Media_AddSample(trak->Media, data_offset, od_sample, descIndex, sampleNum); + if (!e) e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, od_sample->data, od_sample->dataLength); + if (od_sample) gf_isom_sample_del(&od_sample); + } else { + e = Media_AddSample(trak->Media, data_offset, sample, descIndex, sampleNum); + if (!e) e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, sample->data, sample->dataLength); + } + if (e) return e; + if (offset_times) sample->DTS -= 1; + + //OK, update duration + e = Media_SetDuration(trak); + if (e) return e; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + return SetTrackDuration(trak); +} + +GF_Err gf_isom_append_sample_data(GF_ISOFile *movie, u32 trackNumber, char *data, u32 data_size) +{ + GF_Err e; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + u32 dataRefIndex; + u32 descIndex; + GF_DataEntryURLBox *Dentry; + + if (!data_size) return GF_OK; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) return GF_BAD_PARAM; + + //OK, add the sample + descIndex = trak->Media->information->sampleTable->currentEntryIndex; + + e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); + if (e) return e; + if (!entry || !dataRefIndex) return GF_BAD_PARAM; + + //get this dataRef and return false if not self contained + Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->boxList, dataRefIndex - 1); + if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; + + //Open our data map. We are adding stuff, so use EDIT + e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); + if (e) return e; + + //add the media data + e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, data, data_size); + if (e) return e; + //update data size + return stbl_SampleSizeAppend(trak->Media->information->sampleTable->SampleSize, data_size); +} + + +//Add sample reference to a track. The SampleOffset is the offset of the data in the referenced file +//you must have created a StreamDescription with URL or URN specifying your referenced file +//the data offset specifies the begining of the chunk +//Use streamDescriptionIndex to specify the desired stream (if several) +GF_Err gf_isom_add_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample, u64 dataOffset) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + u32 dataRefIndex; + u32 descIndex; + GF_DataEntryURLBox *Dentry; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = unpack_track(trak); + if (e) return e; + + //OD is not allowed as a data ref + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + return GF_BAD_PARAM; + } + //OK, add the sample + //1- Get the streamDescriptionIndex and dataRefIndex + //not specified, get the latest used... + descIndex = StreamDescriptionIndex; + if (!StreamDescriptionIndex) { + descIndex = trak->Media->information->sampleTable->currentEntryIndex; + } + e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); + if (e) return e; + if (!entry || !dataRefIndex) return GF_BAD_PARAM; + //set the current to this one + trak->Media->information->sampleTable->currentEntryIndex = descIndex; + + + //get this dataRef and return false if self contained + Dentry =(GF_DataEntryURLBox*) gf_list_get(trak->Media->information->dataInformation->dref->boxList, dataRefIndex - 1); + if (Dentry->flags == 1) return GF_BAD_PARAM; + + //add the meta data + e = Media_AddSample(trak->Media, dataOffset, sample, descIndex, 0); + if (e) return e; + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + //OK, update duration + e = Media_SetDuration(trak); + if (e) return e; + return SetTrackDuration(trak); + +} + +//set the duration of the last media sample. If not set, the duration of the last sample is the +//duration of the previous one if any, or 1000 (default value). +GF_Err gf_isom_set_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u32 duration) +{ + GF_TrackBox *trak; + GF_SttsEntry *ent; + GF_TimeToSampleBox *stts; + u64 mdur; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + mdur = trak->Media->mediaHeader->duration; + stts = trak->Media->information->sampleTable->TimeToSample; + if (!stts->nb_entries) return GF_BAD_PARAM; + //get the last entry + ent = (GF_SttsEntry*) &stts->entries[stts->nb_entries-1]; + + mdur -= ent->sampleDelta; + if (duration) { + mdur += duration; + //we only have one sample + if (ent->sampleCount == 1) { + ent->sampleDelta = duration; + } else { + if (ent->sampleDelta == duration) return GF_OK; + ent->sampleCount -= 1; + + if (stts->nb_entries==stts->alloc_size) { + stts->alloc_size++; + stts->entries = realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size); + if (!stts->entries) return GF_OUT_OF_MEM; + } + stts->entries[stts->nb_entries].sampleCount = 1; + stts->entries[stts->nb_entries].sampleDelta = duration; + stts->nb_entries++; + //and update the write cache + stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount; + } + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + trak->Media->mediaHeader->duration = mdur; + return SetTrackDuration(trak); +} + +//update a sample data in the media. Note that the sample MUST exists +GF_Err gf_isom_update_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, Bool data_only) +{ + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = unpack_track(trak); + if (e) return e; + + //block for hint tracks + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; + + //REWRITE ANY OD STUFF + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + GF_ISOSample *od_sample = NULL; + e = Media_ParseODFrame(trak->Media, sample, &od_sample); + if (!e) e = Media_UpdateSample(trak->Media, sampleNumber, od_sample, data_only); + if (od_sample) gf_isom_sample_del(&od_sample); + } else { + e = Media_UpdateSample(trak->Media, sampleNumber, sample, data_only); + } + if (e) return e; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + return GF_OK; +} + +//update a sample data in the media. Note that the sample MUST exists, +//that sample->data MUST be NULL and sample->dataLength must be NON NULL; +GF_Err gf_isom_update_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset) +{ + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + //block for hint tracks + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; + + if (!sampleNumber || !sample) return GF_BAD_PARAM; + + e = unpack_track(trak); + if (e) return e; + + //OD is not allowed as a data ref + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + return GF_BAD_PARAM; + } + //OK, update it + e = Media_UpdateSampleReference(trak->Media, sampleNumber, sample, data_offset); + if (e) return e; + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + return GF_OK; +} + + +//Remove a given sample +GF_Err gf_isom_remove_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber) +{ + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !sampleNumber || (sampleNumber > trak->Media->information->sampleTable->SampleSize->sampleCount) ) + return GF_BAD_PARAM; + + //block for hint tracks + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; + + e = unpack_track(trak); + if (e) return e; + + //remove DTS + e = stbl_RemoveDTS(trak->Media->information->sampleTable, sampleNumber, trak->Media->mediaHeader->timeScale); + if (e) return e; + //remove CTS if any + if (trak->Media->information->sampleTable->CompositionOffset) { + e = stbl_RemoveCTS(trak->Media->information->sampleTable, sampleNumber); + if (e) return e; + } + //remove size + e = stbl_RemoveSize(trak->Media->information->sampleTable->SampleSize, sampleNumber); + if (e) return e; + //remove sampleToChunk and chunk + e = stbl_RemoveChunk(trak->Media->information->sampleTable, sampleNumber); + if (e) return e; + //remove sync + if (trak->Media->information->sampleTable->SyncSample) { + e = stbl_RemoveRAP(trak->Media->information->sampleTable, sampleNumber); + if (e) return e; + } + //remove sample dep + if (trak->Media->information->sampleTable->SampleDep) { + e = stbl_RemoveRedundant(trak->Media->information->sampleTable, sampleNumber); + if (e) return e; + } + //remove shadow + if (trak->Media->information->sampleTable->ShadowSync) { + e = stbl_RemoveShadow(trak->Media->information->sampleTable->ShadowSync, sampleNumber); + if (e) return e; + } + //remove padding + e = stbl_RemovePaddingBits(trak->Media->information->sampleTable, sampleNumber); + if (e) return e; + + return SetTrackDuration(trak); +} + + +GF_Err gf_isom_set_final_name(GF_ISOFile *movie, char *filename) +{ + GF_Err e; + if (!movie ) return GF_BAD_PARAM; + + //if mode is not OPEN_EDIT file was created under the right name + e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT); + if (e) return e; + + if (filename) { + //we don't allow file overwriting + if ( (movie->openMode == GF_ISOM_OPEN_EDIT) + && movie->fileName && !strcmp(filename, movie->fileName)) + return GF_BAD_PARAM; + if (movie->finalName) free(movie->finalName); + movie->finalName = strdup(filename); + if (!movie->finalName) return GF_OUT_OF_MEM; + } + return GF_OK; +} + +//Add a system descriptor to the ESD of a stream(EDIT or WRITE mode only) +GF_Err gf_isom_add_desc_to_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_Descriptor *theDesc) +{ + GF_IPIPtr *ipiD; + GF_Err e; + u16 tmpRef; + GF_TrackBox *trak; + GF_Descriptor *desc; + GF_ESD *esd; + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *dpnd; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + /*GETS NATIVE DESCRIPTOR ONLY*/ + e = Media_GetESD(trak->Media, StreamDescriptionIndex, &esd, 1); + if (e) return e; + + //duplicate the desc + e = gf_odf_desc_copy(theDesc, &desc); + if (e) return e; + + //and add it to the ESD EXCEPT IPI PTR (we need to translate from ES_ID to TrackID!!! + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + switch (desc->tag) { + case GF_ODF_IPI_PTR_TAG: + goto insertIPI; + + default: + return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc); + } + + +insertIPI: + if (esd->ipiPtr) { + gf_odf_desc_del((GF_Descriptor *) esd->ipiPtr); + esd->ipiPtr = NULL; + } + + ipiD = (GF_IPIPtr *) desc; + //find a tref + if (!trak->References) { + tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); + e = trak_AddBox((GF_Box*)trak, (GF_Box *)tref); + if (e) return e; + } + tref = trak->References; + + e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd); + if (e) return e; + if (!dpnd) { + tmpRef = 0; + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR; + e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); + if (e) return e; + e = reftype_AddRefTrack(dpnd, ipiD->IPI_ES_Id, &tmpRef); + if (e) return e; + //and replace the tag and value... + ipiD->IPI_ES_Id = tmpRef; + ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG; + } else { + //Watch out! ONLY ONE IPI dependancy is allowed per stream + dpnd->trackIDCount = 1; + dpnd->trackIDs[0] = ipiD->IPI_ES_Id; + //and replace the tag and value... + ipiD->IPI_ES_Id = 1; + ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG; + } + //and add the desc to the esd... + return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc); +} + + +//use carefully. Very usefull when you made a lot of changes (IPMP, IPI, OCI, ...) +//THIS WILL REPLACE THE WHOLE DESCRIPTOR ... +GF_Err gf_isom_change_mpeg4_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ESD *newESD) +{ + GF_Err e; + GF_ESD *esd; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; + + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) { + return movie->LastError = GF_BAD_PARAM; + } + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + //duplicate our desc + e = gf_odf_desc_copy((GF_Descriptor *)newESD, (GF_Descriptor **)&esd); + if (e) return e; + return Track_SetStreamDescriptor(trak, StreamDescriptionIndex, entry->dataReferenceIndex, esd, NULL); +} + +GF_Err gf_isom_set_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 Width, u32 Height) +{ + GF_Err e; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) { + return movie->LastError = GF_ISOM_INVALID_FILE; + } + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) { + return movie->LastError = GF_BAD_PARAM; + } + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + //valid for MPEG visual, JPG and 3GPP H263 + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_BOX_TYPE_AVC1: + ((GF_VisualSampleEntryBox*)entry)->Width = Width; + ((GF_VisualSampleEntryBox*)entry)->Height = Height; + trak->Header->width = Width<<16; + trak->Header->height = Height<<16; + return GF_OK; + /*check BIFS*/ + default: + if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_SCENE) { + trak->Header->width = Width<<16; + trak->Header->height = Height<<16; + return GF_OK; + } + return GF_BAD_PARAM; + } +} + +GF_Err gf_isom_set_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 hSpacing, u32 vSpacing) +{ + GF_Err e; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_VisualSampleEntryBox*vent; + GF_SampleDescriptionBox *stsd; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) { + return movie->LastError = GF_BAD_PARAM; + } + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_BOX_TYPE_AVC1: + break; + default: + return GF_BAD_PARAM; + } + + vent = (GF_VisualSampleEntryBox*)entry; + if (!hSpacing || !vSpacing) { + if (vent->pasp) gf_isom_box_del((GF_Box*)vent->pasp); + vent->pasp = NULL; + return GF_OK; + } + if (!vent->pasp) vent->pasp = (GF_PixelAspectRatioBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_PASP); + vent->pasp->hSpacing = hSpacing; + vent->pasp->vSpacing = vSpacing; + return GF_OK; +} + +GF_Err gf_isom_set_audio_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 sampleRate, u32 nbChannels, u8 bitsPerSample) +{ + GF_Err e; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) { + return movie->LastError = GF_ISOM_INVALID_FILE; + } + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) { + return movie->LastError = GF_BAD_PARAM; + } + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + ((GF_AudioSampleEntryBox*)entry)->samplerate_hi = sampleRate; + ((GF_AudioSampleEntryBox*)entry)->samplerate_lo = 0; + ((GF_AudioSampleEntryBox*)entry)->channel_count = nbChannels; + ((GF_AudioSampleEntryBox*)entry)->bitspersample = bitsPerSample; + return GF_OK; + /*check BIFS*/ + default: + return GF_BAD_PARAM; + } +} + +//set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED) +GF_Err gf_isom_set_storage_mode(GF_ISOFile *movie, u8 storageMode) +{ + GF_Err e; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + switch (storageMode) { + case GF_ISOM_STORE_FLAT: + case GF_ISOM_STORE_STREAMABLE: + case GF_ISOM_STORE_INTERLEAVED: + case GF_ISOM_STORE_DRIFT_INTERLEAVED: + case GF_ISOM_STORE_TIGHT: + movie->storageMode = storageMode; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +//update or insert a new edit segment in the track time line. Edits are used to modify +//the media normal timing. EditTime and EditDuration are expressed in Movie TimeScale +//If a segment with EditTime already exists, IT IS ERASED +GF_Err gf_isom_set_edit_segment(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u8 EditMode) +{ + GF_TrackBox *trak; + GF_EditBox *edts; + GF_EditListBox *elst; + GF_EdtsEntry *ent, *newEnt; + u32 i; + GF_Err e; + u64 startTime; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + edts = trak->editBox; + if (! edts) { + edts = (GF_EditBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_EDTS); + if (!edts) return GF_OUT_OF_MEM; + trak_AddBox((GF_Box*)trak, (GF_Box *)edts); + } + elst = edts->editList; + if (!elst) { + elst = (GF_EditListBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_ELST); + if (!elst) return GF_OUT_OF_MEM; + edts_AddBox((GF_Box*)edts, (GF_Box *)elst); + } + + startTime = 0; + ent = NULL; + //get the prev entry to this startTime if any + i=0; + while ((ent = (GF_EdtsEntry *)gf_list_enum(elst->entryList, &i))) { + if ( (startTime <= EditTime) && (startTime + ent->segmentDuration > EditTime) ) + goto found; + startTime += ent->segmentDuration; + } + + //not found, add a new entry and adjust the prev one if any + if (!ent) { + newEnt = CreateEditEntry(EditDuration, MediaTime, EditMode); + if (!newEnt) return GF_OUT_OF_MEM; + gf_list_add(elst->entryList, newEnt); + return SetTrackDuration(trak); + } + + startTime -= ent->segmentDuration; + +found: + + //if same time, we erase the current one... + if (startTime == EditTime) { + ent->segmentDuration = EditDuration; + switch (EditMode) { + case GF_ISOM_EDIT_EMPTY: + ent->mediaRate = 1; + ent->mediaTime = -1; + break; + case GF_ISOM_EDIT_DWELL: + ent->mediaRate = 0; + ent->mediaTime = MediaTime; + break; + default: + ent->mediaRate = 1; + ent->mediaTime = MediaTime; + break; + } + return SetTrackDuration(trak); + } + + //adjust so that the prev ent leads to EntryTime + //Note: we don't change the next one as it is unknown to us in + //a lot of case (the author's changes) + ent->segmentDuration = EditTime - startTime; + newEnt = CreateEditEntry(EditDuration, MediaTime, EditMode); + if (!newEnt) return GF_OUT_OF_MEM; + //is it the last entry ??? + if (i >= gf_list_count(elst->entryList) - 1) { + //add the new entry at the end + gf_list_add(elst->entryList, newEnt); + return SetTrackDuration(trak); + } else { + //insert after the current entry (which is i) + gf_list_insert(elst->entryList, newEnt, i+1); + return SetTrackDuration(trak); + } +} + +//remove the edit segments for the whole track +GF_Err gf_isom_remove_edit_segments(GF_ISOFile *movie, u32 trackNumber) +{ + GF_Err e; + GF_TrackBox *trak; + GF_EdtsEntry *ent; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!trak->editBox || !trak->editBox->editList) return GF_OK; + + while (gf_list_count(trak->editBox->editList->entryList)) { + ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, 0); + free(ent); + e = gf_list_rem(trak->editBox->editList->entryList, 0); + if (e) return e; + } + //then delete the GF_EditBox... + gf_isom_box_del((GF_Box *)trak->editBox); + trak->editBox = NULL; + return SetTrackDuration(trak); +} + + +//remove the edit segments for the whole track +GF_Err gf_isom_remove_edit_segment(GF_ISOFile *movie, u32 trackNumber, u32 seg_index) +{ + GF_Err e; + GF_TrackBox *trak; + GF_EdtsEntry *ent, *next_ent; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !seg_index) return GF_BAD_PARAM; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!trak->editBox || !trak->editBox->editList) return GF_OK; + if (gf_list_count(trak->editBox->editList->entryList)<=1) return gf_isom_remove_edit_segments(movie, trackNumber); + + ent = (GF_EdtsEntry*) gf_list_get(trak->editBox->editList->entryList, seg_index-1); + gf_list_rem(trak->editBox->editList->entryList, seg_index-1); + next_ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, seg_index-1); + if (next_ent) next_ent->segmentDuration += ent->segmentDuration; + free(ent); + return SetTrackDuration(trak); +} + + +GF_Err gf_isom_append_edit_segment(GF_ISOFile *movie, u32 trackNumber, u64 EditDuration, u64 MediaTime, u8 EditMode) +{ + GF_Err e; + GF_TrackBox *trak; + GF_EdtsEntry *ent; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!trak->editBox) { + GF_EditBox *edts = (GF_EditBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_EDTS); + if (!edts) return GF_OUT_OF_MEM; + trak_AddBox((GF_Box*)trak, (GF_Box *)edts); + } + if (!trak->editBox->editList) { + GF_EditListBox *elst = (GF_EditListBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_ELST); + if (!elst) return GF_OUT_OF_MEM; + edts_AddBox((GF_Box*)trak->editBox, (GF_Box *)elst); + } + ent = (GF_EdtsEntry *)malloc(sizeof(GF_EdtsEntry)); + if (!ent) return GF_OUT_OF_MEM; + + ent->segmentDuration = EditDuration; + switch (EditMode) { + case GF_ISOM_EDIT_EMPTY: + ent->mediaRate = 1; + ent->mediaTime = -1; + break; + case GF_ISOM_EDIT_DWELL: + ent->mediaRate = 0; + ent->mediaTime = MediaTime; + break; + default: + ent->mediaRate = 1; + ent->mediaTime = MediaTime; + break; + } + gf_list_add(trak->editBox->editList->entryList, ent); + return SetTrackDuration(trak); +} + +GF_Err gf_isom_modify_edit_segment(GF_ISOFile *movie, u32 trackNumber, u32 seg_index, u64 EditDuration, u64 MediaTime, u8 EditMode) +{ + GF_Err e; + GF_TrackBox *trak; + GF_EdtsEntry *ent; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !seg_index) return GF_BAD_PARAM; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!trak->editBox || !trak->editBox->editList) return GF_OK; + if (gf_list_count(trak->editBox->editList->entryList)editBox->editList->entryList, seg_index-1); + + ent->segmentDuration = EditDuration; + switch (EditMode) { + case GF_ISOM_EDIT_EMPTY: + ent->mediaRate = 1; + ent->mediaTime = -1; + break; + case GF_ISOM_EDIT_DWELL: + ent->mediaRate = 0; + ent->mediaTime = MediaTime; + break; + default: + ent->mediaRate = 1; + ent->mediaTime = MediaTime; + break; + } + return SetTrackDuration(trak); +} + +//removes the desired track +GF_Err gf_isom_remove_track(GF_ISOFile *movie, u32 trackNumber) +{ + GF_Err e; + GF_TrackBox *the_trak, *trak; + GF_TrackReferenceTypeBox *tref; + u32 i, j, k, *newRefs, descIndex; + u8 found; + GF_ISOSample *samp; + the_trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!the_trak) return GF_BAD_PARAM; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (movie->moov->iods && movie->moov->iods->descriptor) { + GF_Descriptor *desc; + GF_ES_ID_Inc *inc; + GF_List *ESDs; + desc = movie->moov->iods->descriptor; + if (desc->tag == GF_ODF_ISOM_IOD_TAG) { + ESDs = ((GF_IsomInitialObjectDescriptor *)desc)->ES_ID_IncDescriptors; + } else if (desc->tag == GF_ODF_ISOM_OD_TAG) { + ESDs = ((GF_IsomObjectDescriptor *)desc)->ES_ID_IncDescriptors; + } else { + return GF_ISOM_INVALID_FILE; + } + + //remove the track ref from the root OD if any + i=0; + while ((inc = (GF_ES_ID_Inc *)gf_list_enum(ESDs, &i))) { + if (inc->trackID == the_trak->Header->trackID) { + gf_odf_desc_del((GF_Descriptor *)inc); + i--; + gf_list_rem(ESDs, i); + } + } + } + + //remove the track from the movie + gf_list_del_item(movie->moov->trackList, the_trak); + + //rewrite any OD tracks + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { + if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_OD) continue; + //this is an OD track... + j = gf_isom_get_sample_count(movie, i); + for (k=0; k < j; k++) { + //getting the sample will remove the references to the deleted track in the output OD frame + samp = gf_isom_get_sample(movie, i, k+1, &descIndex); + if (!samp) break; + //so let's update with the new OD frame ! If the sample is empty, remove it + if (!samp->dataLength) { + e = gf_isom_remove_sample(movie, i, k+1); + if (e) return e; + } else { + e = gf_isom_update_sample(movie, i, k+1, samp, 1); + if (e) return e; + } + //and don't forget to delete the sample + gf_isom_sample_del(&samp); + } + } + + //remove the track ref from any "tref" box in all tracks (except the one to delete ;) + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { + if (trak == the_trak) continue; + if (! trak->References || ! gf_list_count(trak->References->boxList)) continue; + + j=0; + while ((tref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->boxList, &j))) { + found = 0; + for (k=0; ktrackIDCount; k++) { + if (tref->trackIDs[k] == the_trak->Header->trackID) found++; + } + if (!found) continue; + //no more refs, remove this ref_type + if (found == tref->trackIDCount) { + gf_isom_box_del((GF_Box *)tref); + j--; + gf_list_rem(trak->References->boxList, j); + } else { + newRefs = (u32*)malloc(sizeof(u32) * (tref->trackIDCount - found)); + found = 0; + for (k = 0; k < tref->trackIDCount; k++) { + if (tref->trackIDs[k] != the_trak->Header->trackID) { + newRefs[k-found] = tref->trackIDs[k]; + } else { + found++; + } + } + free(tref->trackIDs); + tref->trackIDs = newRefs; + tref->trackIDCount -= found; + } + } + //a little opt: remove the ref box if empty... + if (! gf_list_count(trak->References->boxList)) { + gf_isom_box_del((GF_Box *)trak->References); + trak->References = NULL; + } + } + + //delete the track + gf_isom_box_del((GF_Box *)the_trak); + + /*update next track ID*/ + movie->moov->mvhd->nextTrackID = 0; + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { + if (trak->Header->trackID>movie->moov->mvhd->nextTrackID) + movie->moov->mvhd->nextTrackID = trak->Header->trackID; + } + return GF_OK; +} + + +GF_Err gf_isom_set_copyright(GF_ISOFile *movie, const char *threeCharCode, char *notice) +{ + GF_Err e; + GF_CopyrightBox *ptr; + GF_UserDataMap *map; + u32 count, i; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!notice || !threeCharCode) return GF_BAD_PARAM; + + gf_isom_insert_moov(movie); + + if (!movie->moov->udta) { + e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); + + if (map) { + //try to find one in our language... + count = gf_list_count(map->boxList); + for (i=0; iboxList, i); + if (!strcmp(threeCharCode, (const char *) ptr->packedLanguageCode)) { + free(ptr->notice); + ptr->notice = (char*)malloc(sizeof(char) * (strlen(notice) + 1)); + strcpy(ptr->notice, notice); + return GF_OK; + } + } + } + //nope, create one + ptr = (GF_CopyrightBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CPRT); + + memcpy(ptr->packedLanguageCode, threeCharCode, 4); + ptr->notice = (char*)malloc(sizeof(char) * (strlen(notice)+1)); + strcpy(ptr->notice, notice); + return udta_AddBox(movie->moov->udta, (GF_Box *) ptr); +} + +GF_Err gf_isom_add_chapter(GF_ISOFile *movie, u32 trackNumber, u64 timestamp, char *name) +{ + GF_Err e; + GF_ChapterListBox *ptr; + u32 i, count; + GF_ChapterEntry *ce; + GF_UserDataBox *udta; + GF_UserDataMap *map; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + gf_isom_insert_moov(movie); + + if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->udta) { + e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + udta = trak->udta; + } else { + if (!movie->moov->udta) { + e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + udta = movie->moov->udta; + } + + ptr = NULL; + map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); + if (!map) { + ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL); + e = udta_AddBox(udta, (GF_Box *) ptr); + if (e) return e; + } else { + ptr = (GF_ChapterListBox*)gf_list_get(map->boxList, 0); + } + /*this may happen if original MP4 is not properly formatted*/ + if (!ptr) { + ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL); + gf_list_add(map->boxList, ptr); + } + + GF_SAFEALLOC(ce, GF_ChapterEntry); + ce->start_time = timestamp * 10000L; + ce->name = name ? strdup(name) : NULL; + + /*insert in order*/ + count = gf_list_count(ptr->list); + for (i=0; ilist, i); + if (ace->start_time == ce->start_time) { + if (ace->name) free(ace->name); + ace->name = ce->name; + free(ce); + return GF_OK; + } + if (ace->start_time >= ce->start_time) + return gf_list_insert(ptr->list, ce, i); + } + return gf_list_add(ptr->list, ce); +} + + +GF_Err gf_isom_remove_chapter(GF_ISOFile *movie, u32 trackNumber, u32 index) +{ + GF_Err e; + GF_ChapterListBox *ptr; + GF_ChapterEntry *ce; + GF_UserDataBox *udta; + GF_UserDataMap *map; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->udta) { + e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + udta = trak->udta; + } else { + if (!movie->moov->udta) { + e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + udta = movie->moov->udta; + } + + map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); + if (!map) return GF_OK; + ptr = (GF_ChapterListBox*)gf_list_get(map->boxList, 0); + if (!ptr) return GF_OK; + + if (index) { + ce = (GF_ChapterEntry *)gf_list_get(ptr->list, index-1); + if (!ce) return GF_BAD_PARAM; + if (ce->name) free(ce->name); + free(ce); + gf_list_rem(ptr->list, index-1); + } else { + while (gf_list_count(ptr->list)) { + ce = (GF_ChapterEntry *)gf_list_get(ptr->list, 0); + if (ce->name) free(ce->name); + free(ce); + gf_list_rem(ptr->list, 0); + } + } + if (!gf_list_count(ptr->list)) { + gf_list_del_item(udta->recordList, map); + gf_isom_box_array_del(map->boxList); + free(map); + } + return GF_OK; +} + +GF_Err gf_isom_remove_copyright(GF_ISOFile *movie, u32 index) +{ + GF_Err e; + GF_CopyrightBox *ptr; + GF_UserDataMap *map; + u32 count; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + if (!index) return GF_BAD_PARAM; + if (!movie->moov->udta) return GF_OK; + + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); + if (!map) return GF_OK; + + count = gf_list_count(map->boxList); + if (index>count) return GF_BAD_PARAM; + + ptr = (GF_CopyrightBox*)gf_list_get(map->boxList, index-1); + if (ptr) { + gf_list_rem(map->boxList, index-1); + if (ptr->notice) free(ptr->notice); + free(ptr); + } + /*last copyright, remove*/ + if (!gf_list_count(map->boxList)) { + gf_list_del_item(movie->moov->udta->recordList, map); + gf_list_del(map->boxList); + free(map); + } + return GF_OK; +} + + + +GF_Err gf_isom_set_watermark(GF_ISOFile *movie, bin128 UUID, u8* data, u32 length) +{ + GF_Err e; + GF_UnknownUUIDBox *ptr; + GF_UserDataMap *map; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + gf_isom_insert_moov(movie); + if (!movie->moov->udta) { + e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_UUID, (bin128 *) & UUID); + if (map) { + ptr = (GF_UnknownUUIDBox *)gf_list_get(map->boxList, 0); + if (ptr) { + free(ptr->data); + ptr->data = (char*)malloc(length); + memcpy(ptr->data, data, length); + ptr->dataSize = length; + return GF_OK; + } + } + //nope, create one + ptr = (GF_UnknownUUIDBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); + memcpy(ptr->uuid, UUID, 16); + ptr->data = (char*)malloc(length); + memcpy(ptr->data, data, length); + ptr->dataSize = length; + return udta_AddBox(movie->moov->udta, (GF_Box *) ptr); +} + +//set the interleaving time of media data (INTERLEAVED mode only) +//InterleaveTime is in MovieTimeScale +GF_Err gf_isom_set_interleave_time(GF_ISOFile *movie, u32 InterleaveTime) +{ + GF_Err e; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!InterleaveTime || !movie->moov) return GF_OK; + movie->interleavingTime = InterleaveTime; + return GF_OK; +} + +u32 gf_isom_get_interleave_time(GF_ISOFile *movie) +{ + return movie ? movie->interleavingTime : 0; +} + +//set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED) +u8 gf_isom_get_storage_mode(GF_ISOFile *movie) +{ + return movie ? movie->storageMode : 0; +} + + + + +//use a compact track version for sample size. This is not usually recommended +//except for speech codecs where the track has a lot of small samples +//compaction is done automatically while writing based on the track's sample sizes +GF_Err gf_isom_use_compact_size(GF_ISOFile *movie, u32 trackNumber, u8 CompactionOn) +{ + GF_TrackBox *trak; + u32 i, size; + GF_SampleSizeBox *stsz; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (!trak->Media || !trak->Media->information + || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleSize) + return GF_ISOM_INVALID_FILE; + + stsz = trak->Media->information->sampleTable->SampleSize; + + //switch to regular table + if (!CompactionOn) { + if (stsz->type == GF_ISOM_BOX_TYPE_STSZ) return GF_OK; + stsz->type = GF_ISOM_BOX_TYPE_STSZ; + //invalidate the sampleSize and recompute it + stsz->sampleSize = 0; + if (!stsz->sampleCount) return GF_OK; + //if the table is empty we can only assume the track is empty (no size indication) + if (!stsz->sizes) return GF_OK; + size = stsz->sizes[0]; + //check whether the sizes are all the same or not + for (i=1; isampleCount; i++) { + if (size != stsz->sizes[i]) { + size = 0; + break; + } + } + if (size) { + free(stsz->sizes); + stsz->sizes = NULL; + stsz->sampleSize = size; + } + return GF_OK; + } + + //switch to compact table + if (stsz->type == GF_ISOM_BOX_TYPE_STZ2) return GF_OK; + //fill the table. Although it seems weird , this is needed in case of edition + //after the function is called. NOte however than we force regular table + //at write time if all samples are of same size + if (stsz->sampleSize) { + //this is a weird table indeed ;) + if (stsz->sizes) free(stsz->sizes); + stsz->sizes = (u32*) malloc(sizeof(u32)*stsz->sampleCount); + memset(stsz->sizes, stsz->sampleSize, sizeof(u32)); + } + //set the SampleSize to 0 while the file is open + stsz->sampleSize = 0; + stsz->type = GF_ISOM_BOX_TYPE_STZ2; + return GF_OK; +} + + + +GF_Err gf_isom_set_brand_info(GF_ISOFile *movie, u32 MajorBrand, u32 MinorVersion) +{ + u32 i, *p; + GF_Err e; + + if (!MajorBrand) return GF_BAD_PARAM; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + e = CheckNoData(movie); + if (e) return e; + + + if (!movie->brand) { + movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); + gf_list_add(movie->TopBoxes, movie->brand); + } + + movie->brand->majorBrand = MajorBrand; + movie->brand->minorVersion = MinorVersion; + + if (!movie->brand->altBrand) { + movie->brand->altBrand = (u32*)malloc(sizeof(u32)); + movie->brand->altBrand[0] = MajorBrand; + movie->brand->altCount = 1; + return GF_OK; + } + + //if brand already present don't change anything + for (i=0; ibrand->altCount; i++) { + if (movie->brand->altBrand[i] == MajorBrand) return GF_OK; + } + p = (u32*)malloc(sizeof(u32)*(movie->brand->altCount + 1)); + if (!p) return GF_OUT_OF_MEM; + memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount); + p[movie->brand->altCount] = MajorBrand; + movie->brand->altCount += 1; + free(movie->brand->altBrand); + movie->brand->altBrand = p; + return GF_OK; +} + +GF_Err gf_isom_modify_alternate_brand(GF_ISOFile *movie, u32 Brand, u8 AddIt) +{ + u32 i, k, *p; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!Brand) return GF_BAD_PARAM; + + e = CheckNoData(movie); + if (e) return e; + + if (!movie->brand && AddIt) { + movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); + gf_list_add(movie->TopBoxes, movie->brand); + } + + //do not mofify major one + if (!AddIt && movie->brand->majorBrand == Brand) return GF_OK; + + if (!AddIt && movie->brand->altCount == 1) { + //fixes it in case + movie->brand->altBrand[0] = movie->brand->majorBrand; + return GF_OK; + } + //check for the brand + for (i=0; ibrand->altCount; i++) { + if (movie->brand->altBrand[i] == Brand) goto found; + } + //Not found + if (!AddIt) return GF_OK; + //add it + p = (u32*)malloc(sizeof(u32)*(movie->brand->altCount + 1)); + if (!p) return GF_OUT_OF_MEM; + memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount); + p[movie->brand->altCount] = Brand; + movie->brand->altCount += 1; + free(movie->brand->altBrand); + movie->brand->altBrand = p; + return GF_OK; + +found: + + //found + if (AddIt) return GF_OK; + assert(movie->brand->altCount>1); + + //remove it + p = (u32*)malloc(sizeof(u32)*(movie->brand->altCount - 1)); + if (!p) return GF_OUT_OF_MEM; + k = 0; + for (i=0; ibrand->altCount; i++) { + if (movie->brand->altBrand[i] == Brand) continue; + else { + p[k] = movie->brand->altBrand[i]; + k++; + } + } + movie->brand->altCount -= 1; + free(movie->brand->altBrand); + movie->brand->altBrand = p; + return GF_OK; +} + + +GF_Err gf_isom_reset_alt_brands(GF_ISOFile *movie) +{ + u32 *p; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + e = CheckNoData(movie); + if (e) return e; + + if (!movie->brand) { + movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); + gf_list_add(movie->TopBoxes, movie->brand); + } + p = (u32*)malloc(sizeof(u32)); + if (!p) return GF_OUT_OF_MEM; + p[0] = movie->brand->majorBrand; + movie->brand->altCount = 1; + free(movie->brand->altBrand); + movie->brand->altBrand = p; + return GF_OK; +} + +GF_Err gf_isom_set_sample_padding_bits(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u8 NbBits) +{ + GF_TrackBox *trak; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || NbBits > 7) return GF_BAD_PARAM; + + //set Padding info + return stbl_SetPaddingBits(trak->Media->information->sampleTable, sampleNumber, NbBits); +} + + +GF_Err gf_isom_remove_user_data_item(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex) +{ + GF_UserDataMap *map; + GF_Box *a; + u32 i; + bin128 t; + GF_Err e; + GF_TrackBox *trak; + GF_UserDataBox *udta; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; + memset(t, 1, 16); + + if (trackNumber) { + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return GF_BAD_PARAM; + if (!UserDataIndex) return GF_BAD_PARAM; + + i=0; + while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { + if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found; + else if (map->boxType == UserDataType) goto found; + } + //not found + return GF_OK; + +found: + + if (UserDataIndex > gf_list_count(map->boxList) ) return GF_BAD_PARAM; + //delete the box + a = (GF_Box*)gf_list_get(map->boxList, UserDataIndex-1); + + gf_list_rem(map->boxList, UserDataIndex-1); + gf_isom_box_del(a); + + //remove the map if empty + if (!gf_list_count(map->boxList)) { + gf_list_rem(udta->recordList, i-1); + gf_isom_box_array_del(map->boxList); + free(map); + } + //but we keep the UDTA no matter what + return GF_OK; +} + +GF_Err gf_isom_remove_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID) +{ + GF_UserDataMap *map; + u32 i; + GF_Err e; + bin128 t; + GF_TrackBox *trak; + GF_UserDataBox *udta; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; + memset(t, 1, 16); + + if (trackNumber) { + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return GF_BAD_PARAM; + + i=0; + while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { + if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found; + else if (map->boxType == UserDataType) goto found; + } + //not found + return GF_OK; + +found: + + gf_list_rem(udta->recordList, i-1); + gf_isom_box_array_del(map->boxList); + free(map); + + //but we keep the UDTA no matter what + return GF_OK; +} + +GF_Err gf_isom_add_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, char *data, u32 DataLength) +{ + GF_UnknownBox *a; + GF_Err e; + GF_TrackBox *trak; + GF_UserDataBox *udta; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; + + if (trackNumber) { + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->udta) trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + udta = trak->udta; + } else { + if (!movie->moov->udta) moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + udta = movie->moov->udta; + } + if (!udta) return GF_OUT_OF_MEM; + + //create a default box + if (UserDataType) { + a = (GF_UnknownBox *) gf_isom_box_new(UserDataType); + } else { + a = (GF_UnknownBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); + memcpy( ((GF_UUIDBox*)a)->uuid, UUID, 16); + } + + a->data = (char*)malloc(sizeof(char)*DataLength); + memcpy(a->data, data, DataLength); + a->dataSize = DataLength; + + return udta_AddBox(udta, (GF_Box *) a); +} + + +GF_Err gf_isom_add_sample_fragment(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u16 FragmentSize) +{ + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !sampleNumber || !FragmentSize) return GF_BAD_PARAM; + + //set Padding info + return stbl_AddSampleFragment(trak->Media->information->sampleTable, sampleNumber, FragmentSize); +} + + +GF_Err gf_isom_remove_sample_fragment(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber) +{ + GF_TrackBox *trak; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + return stbl_RemoveSampleFragments(trak->Media->information->sampleTable, sampleNumber); +} + +GF_Err gf_isom_remove_sample_fragments(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (trak->Media->information->sampleTable->Fragments) { + gf_isom_box_del((GF_Box *)trak->Media->information->sampleTable->Fragments); + trak->Media->information->sampleTable->Fragments = NULL; + } + return GF_OK; +} + +GF_Err gf_isom_clone_pl_indications(GF_ISOFile *orig, GF_ISOFile *dest) +{ + GF_IsomInitialObjectDescriptor *iod_d; + if (!orig || !dest) return GF_BAD_PARAM; + if (!orig->moov->iods || !orig->moov->iods->descriptor) return GF_OK; + if (orig->moov->iods->descriptor->tag != GF_ODF_ISOM_IOD_TAG) return GF_OK; + + AddMovieIOD(dest->moov, 1); + gf_odf_desc_del((GF_Descriptor *)dest->moov->iods->descriptor); + gf_odf_desc_copy((GF_Descriptor *)orig->moov->iods->descriptor, (GF_Descriptor **)&dest->moov->iods->descriptor); + iod_d = (GF_IsomInitialObjectDescriptor *) dest->moov->iods->descriptor; + while (gf_list_count(iod_d->ES_ID_IncDescriptors)) { + GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_IncDescriptors, 0); + gf_list_rem(iod_d->ES_ID_IncDescriptors, 0); + gf_odf_desc_del(d); + } + while (gf_list_count(iod_d->ES_ID_RefDescriptors)) { + GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_RefDescriptors, 0); + gf_list_rem(iod_d->ES_ID_RefDescriptors, 0); + gf_odf_desc_del(d); + } + return GF_OK; +} + + + +GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *dest_file, Bool keep_data_ref, u32 *dest_track) +{ + GF_TrackBox *trak, *new_tk; + GF_BitStream *bs; + char *data; + u32 data_size; + Double ts_scale; + GF_Err e; + GF_SampleEntryBox *entry; + GF_SampleTableBox *stbl, *stbl_temp; + + e = CanAccessMovie(dest_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(dest_file); + + /*get orig sample desc and clone it*/ + trak = gf_isom_get_track_from_file(orig_file, orig_track); + if (!trak || !trak->Media) return GF_BAD_PARAM; + + stbl = trak->Media->information->sampleTable; + stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL); + stbl_temp->SampleDescription = stbl->SampleDescription; + trak->Media->information->sampleTable = stbl_temp; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + gf_isom_box_size( (GF_Box *) trak); + gf_isom_box_write((GF_Box *) trak, bs); + gf_bs_get_content(bs, &data, &data_size); + gf_bs_del(bs); + bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); + e = gf_isom_parse_box((GF_Box **) &new_tk, bs); + gf_bs_del(bs); + free(data); + trak->Media->information->sampleTable = stbl; + + stbl_temp->SampleDescription = NULL; + gf_isom_box_del((GF_Box *)stbl_temp); + if (e) return e; + + /*create default boxes*/ + stbl = new_tk->Media->information->sampleTable; + stbl->ChunkOffset = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO); + stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSZ); + stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); + stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STTS); + + /*check trackID validity before adding track*/ + if (gf_isom_get_track_by_id(dest_file, new_tk->Header->trackID)) { + u32 ID = 1; + while (1) { + if (RequestTrack(dest_file->moov, ID)) break; + ID += 1; + if (ID == 0xFFFFFFFF) break; + } + new_tk->Header->trackID = ID; + } + + moov_AddBox((GF_Box*)dest_file->moov, (GF_Box *)new_tk); + + ts_scale = dest_file->moov->mvhd->timeScale; + ts_scale /= orig_file->moov->mvhd->timeScale; + new_tk->Header->duration = (u64) (s64) ((s64) new_tk->Header->duration * ts_scale); + if (new_tk->editBox && new_tk->editBox->editList) { + u32 i, count = gf_list_count(new_tk->editBox->editList->entryList); + for (i=0; ieditBox->editList->entryList, i); + ent->segmentDuration = (u64) (s64) ((s64) ent->segmentDuration * ts_scale); + ent->mediaTime = (s64) (ent->mediaTime * ts_scale); + } + } + + /*reset data ref*/ + if (!keep_data_ref) { + gf_isom_box_array_del(new_tk->Media->information->dataInformation->dref->boxList); + new_tk->Media->information->dataInformation->dref->boxList = gf_list_new(); + /*update data ref*/ + entry = (GF_SampleEntryBox*)gf_list_get(new_tk->Media->information->sampleTable->SampleDescription->boxList, 0); + if (entry) { + u32 dref; + Media_CreateDataRef(new_tk->Media->information->dataInformation->dref, NULL, NULL, &dref); + entry->dataReferenceIndex = dref; + } + } + + *dest_track = gf_list_count(dest_file->moov->trackList); + + if (dest_file->moov->mvhd->nextTrackID<= new_tk->Header->trackID) + dest_file->moov->mvhd->nextTrackID = new_tk->Header->trackID+1; + + return GF_OK; +} + + +GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_BitStream *bs; + char *data; + u32 data_size; + GF_Box *entry; + GF_Err e; + u32 dataRefIndex; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + /*get orig sample desc and clone it*/ + trak = gf_isom_get_track_from_file(orig_file, orig_track); + if (!trak || !trak->Media) return GF_BAD_PARAM; + + entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, orig_desc_index-1); + if (!entry) return GF_BAD_PARAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + gf_isom_box_size(entry); + gf_isom_box_write(entry, bs); + gf_bs_get_content(bs, &data, &data_size); + gf_bs_del(bs); + bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); + e = gf_isom_parse_box(&entry, bs); + gf_bs_del(bs); + free(data); + if (e) return e; + + /*get new track and insert clone*/ + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media) goto exit; + + /*get or create the data ref*/ + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) goto exit; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) goto exit; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + /*overwrite dref*/ + ((GF_SampleEntryBox *)entry)->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + + /*also clone track w/h info*/ + if (gf_isom_get_media_type(the_file, trackNumber) == GF_ISOM_MEDIA_VISUAL) { + gf_isom_set_visual_info(the_file, trackNumber, (*outDescriptionIndex), ((GF_VisualSampleEntryBox*)entry)->Width, ((GF_VisualSampleEntryBox*)entry)->Height); + } + return e; + +exit: + gf_isom_box_del(entry); + return e; +} + + +GF_Err gf_isom_new_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, char *URLname, char *URNname, GF_GenericSampleDescription *udesc, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !udesc) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_VISUAL) { + GF_GenericVisualSampleEntryBox *entry; + //create a new entry + entry = (GF_GenericVisualSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV); + if (!entry) return GF_OUT_OF_MEM; + + if (!udesc->codec_tag) { + entry->EntryType = GF_ISOM_BOX_TYPE_UUID; + memcpy(entry->uuid, udesc->UUID, sizeof(bin128)); + } else { + entry->EntryType = udesc->codec_tag; + } + entry->dataReferenceIndex = dataRefIndex; + entry->vendor = udesc->vendor_code; + entry->version = udesc->version; + entry->revision = udesc->revision; + entry->temporal_quality = udesc->temporal_quality; + entry->spacial_quality = udesc->spacial_quality; + entry->Width = udesc->width; + entry->Height = udesc->height; + strcpy(entry->compressor_name, udesc->compressor_name); + entry->color_table_index = -1; + entry->frames_per_sample = 1; + entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000; + entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000; + entry->bit_depth = udesc->depth ? udesc->depth : 0x18; + if (udesc->extension_buf && udesc->extension_buf_size) { + entry->data = (char*)malloc(sizeof(char) * udesc->extension_buf_size); + if (!entry->data) { + gf_isom_box_del((GF_Box *) entry); + return GF_OUT_OF_MEM; + } + memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size); + entry->data_size = udesc->extension_buf_size; + } + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + } + else if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_AUDIO) { + GF_GenericAudioSampleEntryBox *gena; + //create a new entry + gena = (GF_GenericAudioSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA); + if (!gena) return GF_OUT_OF_MEM; + + if (!udesc->codec_tag) { + gena->EntryType = GF_ISOM_BOX_TYPE_UUID; + memcpy(gena->uuid, udesc->UUID, sizeof(bin128)); + } else { + gena->EntryType = udesc->codec_tag; + } + gena->dataReferenceIndex = dataRefIndex; + gena->vendor = udesc->vendor_code; + gena->version = udesc->version; + gena->revision = udesc->revision; + gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16; + gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2; + gena->samplerate_hi = udesc->samplerate>>16; + gena->samplerate_lo = udesc->samplerate & 0xFF; + + if (udesc->extension_buf && udesc->extension_buf_size) { + gena->data = (char*)malloc(sizeof(char) * udesc->extension_buf_size); + if (!gena->data) { + gf_isom_box_del((GF_Box *) gena); + return GF_OUT_OF_MEM; + } + memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size); + gena->data_size = udesc->extension_buf_size; + } + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, gena); + } + else { + GF_GenericSampleEntryBox *genm; + //create a new entry + genm = (GF_GenericSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM); + if (!genm) return GF_OUT_OF_MEM; + + if (!udesc->codec_tag) { + genm->EntryType = GF_ISOM_BOX_TYPE_UUID; + memcpy(genm->uuid, udesc->UUID, sizeof(bin128)); + } else { + genm->EntryType = udesc->codec_tag; + } + genm->dataReferenceIndex = dataRefIndex; + if (udesc->extension_buf && udesc->extension_buf_size) { + genm->data = (char*)malloc(sizeof(char) * udesc->extension_buf_size); + if (!genm->data) { + gf_isom_box_del((GF_Box *) genm); + return GF_OUT_OF_MEM; + } + memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size); + genm->data_size = udesc->extension_buf_size; + } + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, genm); + } + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + return e; +} + +//use carefully. Very usefull when you made a lot of changes (IPMP, IPI, OCI, ...) +//THIS WILL REPLACE THE WHOLE DESCRIPTOR ... +GF_Err gf_isom_change_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_GenericSampleDescription *udesc) +{ + GF_TrackBox *trak; + GF_Err e; + GF_GenericVisualSampleEntryBox *entry; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !StreamDescriptionIndex) return GF_BAD_PARAM; + + entry = (GF_GenericVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex-1); + if (!entry) return GF_BAD_PARAM; + if (entry->type == GF_ISOM_BOX_TYPE_GNRV) { + entry->vendor = udesc->vendor_code; + entry->version = udesc->version; + entry->revision = udesc->revision; + entry->temporal_quality = udesc->temporal_quality; + entry->spacial_quality = udesc->spacial_quality; + entry->Width = udesc->width; + entry->Height = udesc->height; + strcpy(entry->compressor_name, udesc->compressor_name); + entry->color_table_index = -1; + entry->frames_per_sample = 1; + entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000; + entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000; + entry->bit_depth = udesc->depth ? udesc->depth : 0x18; + if (entry->data) free(entry->data); + entry->data = NULL; + entry->data_size = 0; + if (udesc->extension_buf && udesc->extension_buf_size) { + entry->data = (char*)malloc(sizeof(char) * udesc->extension_buf_size); + if (!entry->data) { + gf_isom_box_del((GF_Box *) entry); + return GF_OUT_OF_MEM; + } + memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size); + entry->data_size = udesc->extension_buf_size; + } + return GF_OK; + } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) { + GF_GenericAudioSampleEntryBox *gena = (GF_GenericAudioSampleEntryBox *)entry; + gena->vendor = udesc->vendor_code; + gena->version = udesc->version; + gena->revision = udesc->revision; + gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16; + gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2; + gena->samplerate_hi = udesc->samplerate>>16; + gena->samplerate_lo = udesc->samplerate & 0xFF; + if (gena->data) free(gena->data); + gena->data = NULL; + gena->data_size = 0; + + if (udesc->extension_buf && udesc->extension_buf_size) { + gena->data = (char*)malloc(sizeof(char) * udesc->extension_buf_size); + if (!gena->data) { + gf_isom_box_del((GF_Box *) gena); + return GF_OUT_OF_MEM; + } + memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size); + gena->data_size = udesc->extension_buf_size; + } + return GF_OK; + } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) { + GF_GenericSampleEntryBox *genm = (GF_GenericSampleEntryBox *)entry; + if (genm->data) free(genm->data); + genm->data = NULL; + genm->data_size = 0; + + if (udesc->extension_buf && udesc->extension_buf_size) { + genm->data = (char*)malloc(sizeof(char) * udesc->extension_buf_size); + if (!genm->data) { + gf_isom_box_del((GF_Box *) genm); + return GF_OUT_OF_MEM; + } + memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size); + genm->data_size = udesc->extension_buf_size; + } + return GF_OK; + } + return GF_BAD_PARAM; +} + +/*removes given stream description*/ +GF_Err gf_isom_remove_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 streamDescIndex) +{ + GF_TrackBox *trak; + GF_Err e; + GF_Box *entry; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !streamDescIndex) return GF_BAD_PARAM; + entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, streamDescIndex-1); + if (!entry) return GF_BAD_PARAM; + gf_list_rem(trak->Media->information->sampleTable->SampleDescription->boxList, streamDescIndex-1); + gf_isom_box_del(entry); + return GF_OK; +} + + +//sets a track reference +GF_Err gf_isom_set_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 ReferencedTrackID) +{ + GF_Err e; + GF_TrackBox *trak; + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *dpnd; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + //no tref, create one + tref = trak->References; + if (!tref) { + tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); + e = trak_AddBox((GF_Box*)trak, (GF_Box *) tref); + if (e) return e; + } + //find a ref of the given type + e = Track_FindRef(trak, referenceType, &dpnd); + if (!dpnd) { + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = referenceType; + e = tref_AddBox((GF_Box*)tref, (GF_Box *)dpnd); + if (e) return e; + } + //add the ref + return reftype_AddRefTrack(dpnd, ReferencedTrackID, NULL); +} + +//removes a track reference +GF_Err gf_isom_remove_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 ReferenceIndex) +{ + GF_Err e; + GF_TrackBox *trak; + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *dpnd, *tmp; + u32 i, k, *newIDs; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !ReferenceIndex) return GF_BAD_PARAM; + + //no tref, nothing to remove + tref = trak->References; + if (!tref) return GF_OK; + //find a ref of the given type otherwise return + e = Track_FindRef(trak, referenceType, &dpnd); + if (e || !dpnd) return GF_OK; + //remove the ref + if (ReferenceIndex > dpnd->trackIDCount) return GF_BAD_PARAM; + //last one + if (dpnd->trackIDCount==1) { + i=0; + while ((tmp = (GF_TrackReferenceTypeBox *)gf_list_enum(tref->boxList, &i))) { + if (tmp==dpnd) { + gf_list_rem(tref->boxList, i-1); + gf_isom_box_del((GF_Box *) dpnd); + return GF_OK; + } + } + } + k = 0; + newIDs = (u32*)malloc(sizeof(u32)*(dpnd->trackIDCount-1)); + for (i=0; itrackIDCount; i++) { + if (i+1 != ReferenceIndex) { + newIDs[k] = dpnd->trackIDs[i]; + k++; + } + } + free(dpnd->trackIDs); + dpnd->trackIDCount -= 1; + dpnd->trackIDs = newIDs; + return GF_OK; +} + + +//changes track ID +GF_Err gf_isom_set_track_id(GF_ISOFile *movie, u32 trackNumber, u32 trackID) +{ + GF_TrackReferenceTypeBox *ref; + GF_TrackBox *trak, *a_trak; + u32 i, j, k; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (trak && (trak->Header->trackID==trackID)) return GF_OK; + a_trak = gf_isom_get_track_from_id(movie->moov, trackID); + if (!movie || !trak || a_trak) return GF_BAD_PARAM; + + if (movie->moov->mvhd->nextTrackID<=trackID) + movie->moov->mvhd->nextTrackID = trackID; + + /*rewrite all dependencies*/ + i=0; + while ((a_trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) { + if (!a_trak->References) continue; + j=0; + while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(a_trak->References->boxList, &j))) { + for (k=0; ktrackIDCount; k++) { + if (ref->trackIDs[k]==trak->Header->trackID) { + ref->trackIDs[k] = trackID; + break; + } + } + } + } + + /*and update IOD if any*/ + if (movie->moov->iods && movie->moov->iods->descriptor) { + GF_ES_ID_Inc *inc; + GF_IsomObjectDescriptor *od = (GF_IsomObjectDescriptor *)movie->moov->iods->descriptor; + u32 i = 0; + while ((inc = (GF_ES_ID_Inc*)gf_list_enum(od->ES_ID_IncDescriptors, &i))) { + if (inc->trackID==trak->Header->trackID) inc->trackID = trackID; + } + } + trak->Header->trackID = trackID; + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_isom_modify_cts_offset(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 offset) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM; + if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM; + /*we're in unpack mode: one entry per sample*/ + trak->Media->information->sampleTable->CompositionOffset->entries[sample_number - 1].decodingOffset = offset; + return GF_OK; +} + +GF_Err gf_isom_remove_cts_info(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->Media->information->sampleTable->CompositionOffset) return GF_OK; + + gf_isom_box_del((GF_Box *)trak->Media->information->sampleTable->CompositionOffset); + trak->Media->information->sampleTable->CompositionOffset = NULL; + return GF_OK; +} + +GF_Err gf_isom_set_cts_packing(GF_ISOFile *the_file, u32 trackNumber, Bool unpack) +{ + GF_Err e; + GF_Err stbl_repackCTS(GF_CompositionOffsetBox *ctts); + GF_Err stbl_unpackCTS(GF_SampleTableBox *stbl); + + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (unpack) { + if (!trak->Media->information->sampleTable->CompositionOffset) trak->Media->information->sampleTable->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS); + e = stbl_unpackCTS(trak->Media->information->sampleTable); + } else { + if (!trak->Media->information->sampleTable->CompositionOffset) return GF_OK; + e = stbl_repackCTS(trak->Media->information->sampleTable->CompositionOffset); + } + if (e) return e; + return SetTrackDuration(trak); +} + +GF_Err gf_isom_set_track_layout_info(GF_ISOFile *the_file, u32 trackNumber, u32 width, u32 height, s32 translation_x, s32 translation_y, s16 layer) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Header) return GF_BAD_PARAM; + trak->Header->width = width; + trak->Header->height = height; + trak->Header->matrix[6] = translation_x; + trak->Header->matrix[7] = translation_y; + trak->Header->layer = layer; + return GF_OK; +} + +GF_Err gf_isom_set_track_name(GF_ISOFile *the_file, u32 trackNumber, char *name) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (trak->name) free (trak->name); + trak->name = NULL; + if (name) trak->name = strdup(name); + return GF_OK; +} +const char *gf_isom_get_track_name(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return NULL; + return trak->name; +} + + +GF_Err gf_isom_store_movie_config(GF_ISOFile *movie, Bool remove_all) +{ + u32 i, count, len; + char *data; + GF_BitStream *bs; + bin128 binID; + if (movie == NULL) return GF_BAD_PARAM; + + gf_isom_remove_user_data(movie, 0, GF_4CC('G','P','A','C'), binID); + count = gf_isom_get_track_count(movie); + for (i=0; istorageMode); + gf_bs_write_u32(bs, movie->interleavingTime); + gf_bs_get_content(bs, &data, &len); + gf_bs_del(bs); + gf_isom_add_user_data(movie, 0, GF_4CC('G','P','A','C'), binID, data, len); + free(data); + /*update tracks: interleaving group/priority and track edit name*/ + for (i=0; iMedia->information->sampleTable->groupID); + gf_bs_write_u32(bs, trak->Media->information->sampleTable->trackPriority); + len = trak->name ? strlen(trak->name) : 0; + gf_bs_write_u32(bs, len); + for (j=0; jname[j]); + gf_bs_get_content(bs, &data, &len); + gf_bs_del(bs); + gf_isom_add_user_data(movie, i+1, GF_4CC('G','P','A','C'), binID, data, len); + free(data); + } + return GF_OK; +} + + +GF_Err gf_isom_load_movie_config(GF_ISOFile *movie) +{ + u32 i, count, len; + char *data; + GF_BitStream *bs; + Bool found_cfg; + bin128 binID; + if (movie == NULL) return GF_BAD_PARAM; + + found_cfg = 0; + /*restore movie*/ + count = gf_isom_get_user_data_count(movie, 0, GF_4CC('G','P','A','C'), binID); + for (i=0; istorageMode = gf_bs_read_u8(bs); + movie->interleavingTime = gf_bs_read_u32(bs); + gf_bs_del(bs); + free(data); + found_cfg = 1; + break; + } + + for (i=0; iMedia->information->sampleTable->groupID = gf_bs_read_u32(bs); + trak->Media->information->sampleTable->trackPriority = gf_bs_read_u32(bs); + len = gf_bs_read_u32(bs); + if (len) { + u32 k; + trak->name = (char*)malloc(sizeof(char)*(len+1)); + for (k=0;kname[k] = gf_bs_read_u8(bs); + trak->name[k] = 0; + } + gf_bs_del(bs); + free(data); + found_cfg = 1; + break; + } + } + return found_cfg ? GF_OK : GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_set_media_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 newTS) +{ + Double scale; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media | !trak->Media->mediaHeader) return GF_BAD_PARAM; + if (trak->Media->mediaHeader->timeScale==newTS) return GF_OK; + + scale = newTS; + scale /= trak->Media->mediaHeader->timeScale; + trak->Media->mediaHeader->timeScale = newTS; + if (trak->editBox) { + GF_EdtsEntry *ent; + u32 i=0; + while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) { + ent->mediaTime = (u32) (scale*ent->mediaTime); + } + } + return SetTrackDuration(trak); +} + + + +Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, GF_ISOFile *f2, u32 tk2) +{ + u32 i, count; + GF_TrackBox *trak1, *trak2; + GF_BitStream *bs; + char *data1, *data2; + u32 data1_size, data2_size; + GF_ESD *esd1, *esd2; + GF_Box *a; + Bool ret, need_memcmp; + + /*get orig sample desc and clone it*/ + trak1 = gf_isom_get_track_from_file(f1, tk1); + if (!trak1 || !trak1->Media) return 0; + trak2 = gf_isom_get_track_from_file(f2, tk2); + if (!trak2 || !trak2->Media) return 0; + + if (trak1->Media->handler->handlerType != trak2->Media->handler->handlerType) return 0; + count = gf_list_count(trak1->Media->information->sampleTable->SampleDescription->boxList); + if (count != gf_list_count(trak2->Media->information->sampleTable->SampleDescription->boxList)) return 0; + + need_memcmp = 1; + for (i=0; iMedia->information->sampleTable->SampleDescription->boxList, i); + GF_Box *ent2 = (GF_Box *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->boxList, i); + if (ent1->type != ent2->type) return 0; + + switch (ent1->type) { + /*for MPEG-4 streams, only compare decSpecInfo (bitrate may not be the same but that's not an issue)*/ + case GF_ISOM_BOX_TYPE_MP4S: + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_ENCS: + Media_GetESD(trak1->Media, i+1, &esd1, 1); + Media_GetESD(trak2->Media, i+1, &esd2, 1); + if (!esd1 || !esd2) continue; + need_memcmp = 0; + if (esd1->decoderConfig->streamType != esd2->decoderConfig->streamType) return 0; + if (esd1->decoderConfig->objectTypeIndication != esd2->decoderConfig->objectTypeIndication) return 0; + if (!esd1->decoderConfig->decoderSpecificInfo && esd2->decoderConfig->decoderSpecificInfo) return 0; + if (esd1->decoderConfig->decoderSpecificInfo && !esd2->decoderConfig->decoderSpecificInfo) return 0; + if (!esd1->decoderConfig->decoderSpecificInfo || !esd2->decoderConfig->decoderSpecificInfo) continue; + if (memcmp(esd1->decoderConfig->decoderSpecificInfo->data, esd2->decoderConfig->decoderSpecificInfo->data, sizeof(char)*esd1->decoderConfig->decoderSpecificInfo->dataLength)!=0) return 0; + break; + case GF_ISOM_BOX_TYPE_AVC1: + { + GF_MPEGVisualSampleEntryBox *avc1 = (GF_MPEGVisualSampleEntryBox *)ent1; + GF_MPEGVisualSampleEntryBox *avc2 = (GF_MPEGVisualSampleEntryBox *)ent2; + data1 = data2 = NULL; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + a = (GF_Box *)avc1->avc_config; + gf_isom_box_size(a); + gf_isom_box_write(a, bs); + gf_bs_get_content(bs, &data1, &data1_size); + gf_bs_del(bs); + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + a = (GF_Box *)avc2->avc_config; + gf_isom_box_size(a); + gf_isom_box_write(a, bs); + gf_bs_get_content(bs, &data2, &data2_size); + gf_bs_del(bs); + + ret = 0; + if (data1_size == data2_size) { + ret = (memcmp(data1, data2, sizeof(char)*data1_size)==0) ? 1 : 0; + } + free(data1); + free(data2); + return ret; + } + break; + } + } + if (!need_memcmp) return 1; + ret = 0; + + data1 = data2 = NULL; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + a = (GF_Box *)trak1->Media->information->sampleTable->SampleDescription; + gf_isom_box_size(a); + gf_isom_box_write(a, bs); + gf_bs_get_content(bs, &data1, &data1_size); + gf_bs_del(bs); + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + a = (GF_Box *)trak2->Media->information->sampleTable->SampleDescription; + gf_isom_box_size(a); + gf_isom_box_write(a, bs); + gf_bs_get_content(bs, &data2, &data2_size); + gf_bs_del(bs); + + if (data1_size == data2_size) { + ret = (memcmp(data1, data2, sizeof(char)*data1_size)==0) ? 1 : 0; + } + free(data1); + free(data2); + return ret; +} + +u64 gf_isom_estimate_size(GF_ISOFile *movie) +{ + GF_Err e; + GF_Box *a; + u32 i, count; + u64 mdat_size; + if (!movie) return 0; + + mdat_size = 0; + count = gf_list_count(movie->moov->trackList); + for (i=0; i 0xFFFFFFFF) mdat_size += 8; + } + + i=0; + while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { + e = gf_isom_box_size(a); + mdat_size += a->size; + } + return mdat_size; +} + + +//set shadowing on/off +GF_Err gf_isom_remove_sync_shadows(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + GF_SampleTableBox *stbl; + + if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stbl = trak->Media->information->sampleTable; + if (stbl->ShadowSync) { + gf_isom_box_del((GF_Box *) stbl->ShadowSync); + stbl->ShadowSync = NULL; + } + return GF_OK; +} + +//fill the sync shadow table +GF_Err gf_isom_set_sync_shadow(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u32 syncSample) +{ + GF_TrackBox *trak; + GF_SampleTableBox *stbl; + u8 isRAP; + GF_Err e; + + if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !sampleNumber || !syncSample) return GF_BAD_PARAM; + + stbl = trak->Media->information->sampleTable; + if (!stbl->ShadowSync) stbl->ShadowSync = (GF_ShadowSyncBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSH); + + //if no sync, skip + if (!stbl->SyncSample) return GF_OK; + //else set the sync shadow. + //if the sample is sync, ignore + e = stbl_GetSampleRAP(stbl->SyncSample, sampleNumber, &isRAP, NULL, NULL); + if (e) return e; + if (isRAP) return GF_OK; + //if the shadowing sample is not sync, error + e = stbl_GetSampleRAP(stbl->SyncSample, syncSample, &isRAP, NULL, NULL); + if (e) return e; + if (!isRAP) return GF_BAD_PARAM; + + return stbl_SetSyncShadow(stbl->ShadowSync, sampleNumber, syncSample); +} + +//set the GroupID of a track (only used for interleaving) +GF_Err gf_isom_set_track_group(GF_ISOFile *movie, u32 trackNumber, u32 GroupID) +{ + GF_TrackBox *trak; + + if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !GroupID) return GF_BAD_PARAM; + + trak->Media->information->sampleTable->groupID = GroupID; + return GF_OK; +} + + +//set the Priority of a track within a Group (only used for tight interleaving) +//Priority ranges from 1 to 9 +GF_Err gf_isom_set_track_priority_in_group(GF_ISOFile *movie, u32 trackNumber, u32 Priority) +{ + GF_TrackBox *trak; + + if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !Priority) return GF_BAD_PARAM; + + trak->Media->information->sampleTable->trackPriority = Priority > 255 ? 255 : Priority; + return GF_OK; +} + +//set the max SamplesPerChunk (for file optimization) +GF_Err gf_isom_set_max_samples_per_chunk(GF_ISOFile *movie, u32 trackNumber, u32 maxSamplesPerChunk) +{ + GF_TrackBox *trak; + + if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !maxSamplesPerChunk) return GF_BAD_PARAM; + + trak->Media->information->sampleTable->MaxSamplePerChunk = maxSamplesPerChunk; + return GF_OK; +} + + +GF_Err gf_isom_set_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig *slConfig) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_Err e; + GF_SLConfig **slc; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL); + if (e) return e; + + //we must be sure we are not using a remote ESD + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4S: + if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = & ((GF_MPEGSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4A: + if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = & ((GF_MPEGAudioSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4V: + if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = & ((GF_MPEGVisualSampleEntryBox *)entry)->slc; + break; + default: + return GF_BAD_PARAM; + } + + if (*slc) { + gf_odf_desc_del((GF_Descriptor *)*slc); + *slc = NULL; + } + if (!slConfig) return GF_OK; + //finally duplicate the SL + return gf_odf_desc_copy((GF_Descriptor *) slConfig, (GF_Descriptor **) slc); +} + + +GF_Err gf_isom_get_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig **slConfig) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_Err e; + GF_SLConfig *slc; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL); + if (e) return e; + + //we must be sure we are not using a remote ESD + slc = NULL; + *slConfig = NULL; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4S: + if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = ((GF_MPEGSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4A: + if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4V: + if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc; + break; + default: + return GF_BAD_PARAM; + } + + if (!slc) return GF_OK; + //finally duplicate the SL + return gf_odf_desc_copy((GF_Descriptor *) slc, (GF_Descriptor **) slConfig); +} + + +u32 gf_isom_get_track_group(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->Media->information->sampleTable->groupID; +} + + +u32 gf_isom_get_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->Media->information->sampleTable->trackPriority; +} + + +GF_Err gf_isom_make_interleave(GF_ISOFile *file, Double TimeInSec) +{ + GF_Err e; + if (gf_isom_get_mode(file) < GF_ISOM_OPEN_EDIT) return GF_BAD_PARAM; + e = gf_isom_set_storage_mode(file, GF_ISOM_STORE_INTERLEAVED); + if (e) return e; + return gf_isom_set_interleave_time(file, (u32) (TimeInSec * gf_isom_get_timescale(file))); +} + + +GF_Err gf_isom_set_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char *nameUTF8) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (trak->Media->handler->nameUTF8) free(trak->Media->handler->nameUTF8); + trak->Media->handler->nameUTF8 = NULL; + + if (!nameUTF8) return GF_OK; + + if (!strnicmp(nameUTF8, "file://", 7)) { + u8 BOM[4]; + FILE *f = fopen(nameUTF8+7, "rb"); + u32 size; + if (!f) return GF_URL_ERROR; + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + fread(BOM, 1, 3, f); + /*skip BOM if any*/ + if ((BOM[0]==0xEF) && (BOM[1]==0xBB) && (BOM[2]==0xBF)) size -= 3; + else if ((BOM[0]==0xEF) || (BOM[0]==0xFF)) { + fclose(f); + return GF_BAD_PARAM; + } + else fseek(f, 0, SEEK_SET); + trak->Media->handler->nameUTF8 = (char*)malloc(sizeof(char)*(size+1)); + fread(trak->Media->handler->nameUTF8, 1, size, f); + trak->Media->handler->nameUTF8[size] = 0; + fclose(f); + } else { + u32 i, j, len; + char szOrig[1024], szLine[1024]; + strcpy(szOrig, nameUTF8); + j=0; + len = strlen(szOrig); + for (i=0; i> 6) & 0x3 ); + j++; + szOrig[i] &= 0xbf; + } + /*UTF8 2 bytes char */ + else if ( (szOrig[i] & 0xe0) == 0xc0) { + szLine[j] = szOrig[i]; i++; j++; + } + /*UTF8 3 bytes char */ + else if ( (szOrig[i] & 0xf0) == 0xe0) { + szLine[j] = szOrig[i]; i++; j++; + szLine[j] = szOrig[i]; i++; j++; + } + /*UTF8 4 bytes char */ + else if ( (szOrig[i] & 0xf8) == 0xf0) { + szLine[j] = szOrig[i]; i++; j++; + szLine[j] = szOrig[i]; i++; j++; + szLine[j] = szOrig[i]; i++; j++; + } + } + szLine[j] = szOrig[i]; + j++; + } + szLine[j] = 0; + trak->Media->handler->nameUTF8 = strdup(szLine); + } + return GF_OK; +} + +GF_Err gf_isom_clone_root_od(GF_ISOFile *input, GF_ISOFile *output) +{ + GF_List *esds; + GF_Err e; + u32 i; + GF_Descriptor *desc; + + e = gf_isom_remove_root_od(output); + if (e) return e; + if (!input->moov || !input->moov->iods || !input->moov->iods->descriptor) return GF_OK; + gf_isom_insert_moov(output); + e = AddMovieIOD(output->moov, 0); + if (e) return e; + if (output->moov->iods->descriptor) gf_odf_desc_del(output->moov->iods->descriptor); + output->moov->iods->descriptor = NULL; + gf_odf_desc_copy(input->moov->iods->descriptor, &output->moov->iods->descriptor); + + switch (output->moov->iods->descriptor->tag) { + case GF_ODF_ISOM_IOD_TAG: + esds = ((GF_IsomInitialObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors; + break; + case GF_ODF_ISOM_OD_TAG: + esds = ((GF_IsomObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors; + break; + default: + return GF_ISOM_INVALID_FILE; + } + + //get the desc + i=0; + while ((desc = (GF_Descriptor*)gf_list_enum(esds, &i))) { + gf_odf_desc_del(desc); + gf_list_rem(esds, i-1); + } + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on) +{ + if (!mov) return GF_BAD_PARAM; + mov->is_jp2 = set_on; + return GF_OK; +} + +GF_Err gf_isom_remove_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID) +{ + u32 i, count; + GF_List *list; + + if (trackNumber==(u32) -1) { + if (!movie) return GF_BAD_PARAM; + list = movie->TopBoxes; + } else if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + list = trak->boxes; + } else { + if (!movie) return GF_BAD_PARAM; + list = movie->moov->boxes; + } + + count = gf_list_count(list); + for (i=0; itype != GF_ISOM_BOX_TYPE_UUID) continue; + if (memcmp(UUID, uuid->uuid, sizeof(bin128))) continue; + gf_list_rem(list, i); + i--; + count--; + gf_isom_box_del((GF_Box*)uuid); + } + return GF_OK; +} + + +GF_Err gf_isom_add_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID, char *data, u32 data_size) +{ + GF_List *list; + GF_UnknownUUIDBox *uuid; + + if (!data_size || !data) return GF_OK; + + if (trackNumber==(u32) -1) { + if (!movie) return GF_BAD_PARAM; + list = movie->TopBoxes; + } else if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + list = trak->boxes; + } else { + if (!movie) return GF_BAD_PARAM; + list = movie->moov->boxes; + } + + GF_SAFEALLOC(uuid, GF_UnknownUUIDBox); + uuid->type = GF_ISOM_BOX_TYPE_UUID; + memcpy(uuid->uuid, UUID, sizeof(bin128)); + uuid->dataSize = data_size; + uuid->data = (char*)malloc(sizeof(char)*data_size); + memcpy(uuid->data, data, sizeof(char)*data_size); + gf_list_add(list, uuid); + return GF_OK; +} + +/*Apple extensions*/ + +GF_Err gf_isom_apple_set_tag(GF_ISOFile *mov, u32 tag, const char *data, u32 data_len) +{ + GF_BitStream *bs; + GF_Err e; + GF_ItemListBox *ilst; + GF_MetaBox *meta; + GF_ListItemBox *info; + u32 btype, i; + + + e = CanAccessMovie(mov, GF_ISOM_OPEN_WRITE); + if (e) return e; + + meta = gf_isom_apple_create_meta_extensions(mov); + if (!meta) return GF_BAD_PARAM; + + ilst = gf_ismo_locate_box(meta->other_boxes, GF_ISOM_BOX_TYPE_ILST, NULL); + if (!ilst) return GF_NOT_SUPPORTED; + + if (tag==GF_ISOM_ITUNE_GENRE) { + btype = data ? GF_ISOM_BOX_TYPE_0xA9GEN : GF_ISOM_BOX_TYPE_GNRE; + } else { + btype = tag; + } + /*remove tag*/ + i = 0; + while ((info = gf_list_enum(ilst->tags, &i))) { + if (info->type==btype) { + gf_list_rem(ilst->tags, i-1); + gf_isom_box_del((GF_Box *) info); + break; + } + } + + if (data != NULL) { + info = (GF_ListItemBox *)gf_isom_box_new(btype); + if (info == NULL) return GF_OUT_OF_MEM; + switch (btype) { + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_GNRE: + info->data->flags = 0x0; + break; + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_CPIL: + info->data->flags = 0x15; + break; + default: + info->data->flags = 0x1; + break; + } + if (tag==GF_ISOM_ITUNE_COVER_ART) { + if (data_len & 0x80000000) { + data_len = (data_len & 0x7FFFFFFF); + info->data->flags = 14; + } else { + info->data->flags = 13; + } + } + info->data->dataSize = data_len; + info->data->data = (char*)malloc(sizeof(char)*data_len); + memcpy(info->data->data , data, sizeof(char)*data_len); + } + else if (data_len && (tag==GF_ISOM_ITUNE_GENRE)) { + info = (GF_ListItemBox *)gf_isom_box_new(btype); + if (info == NULL) return GF_OUT_OF_MEM; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u16(bs, data_len); + gf_bs_get_content(bs, & info->data->data, &info->data->dataSize); + info->data->flags = 0x0; + gf_bs_del(bs); + } else if (data_len && (tag==GF_ISOM_ITUNE_COMPILATION)) { + info = (GF_ListItemBox *)gf_isom_box_new(btype); + if (info == NULL) return GF_OUT_OF_MEM; + info->data->data = (char*)malloc(sizeof(char)); + info->data->data[0] = 1; + info->data->dataSize = 1; + info->data->flags = 21; + } else if (data_len && (tag==GF_ISOM_ITUNE_TEMPO)) { + info = (GF_ListItemBox *)gf_isom_box_new(btype); + if (info == NULL) return GF_OUT_OF_MEM; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u16(bs, data_len); + gf_bs_get_content(bs, &info->data->data, &info->data->dataSize); + info->data->flags = 0x15; + gf_bs_del(bs); + } + return gf_list_add(ilst->tags, info); +} + +GF_EXPORT +GF_Err gf_isom_set_alternate_group_id(GF_ISOFile *movie, u32 trackNumber, u32 groupId) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + trak->Header->alternate_group = groupId; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_set_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 trackRefGroup, Bool is_switch_group, u32 *switchGroupID, u32 *criteriaList, u32 criteriaListCount) +{ + GF_TrackSelectionBox *tsel; + GF_TrackBox *trak; + GF_UserDataMap *map; + GF_Err e; + u32 alternateGroupID = 0; + u32 next_switch_group_id = 0; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !switchGroupID) return GF_BAD_PARAM; + + + if (trackRefGroup) { + GF_TrackBox *trak_ref = gf_isom_get_track_from_file(movie, trackRefGroup); + if (trak_ref != trak) { + if (!trak_ref || !trak_ref->Header->alternate_group) return GF_BAD_PARAM; + alternateGroupID = trak_ref->Header->alternate_group; + } else { + alternateGroupID = trak->Header->alternate_group; + } + } + if (!alternateGroupID) { + /*there is a function for this ....*/ + if (trak->Header->alternate_group) return GF_BAD_PARAM; + alternateGroupID = gf_isom_get_next_alternate_group_id(movie); + } + + if (is_switch_group) { + u32 i=0; + while (i< gf_isom_get_track_count(movie) ) { + //locate first available ID + GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i+1); + + if (a_trak->udta) { + u32 j, count; + map = udta_getEntry(a_trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); + if (map) { + count = gf_list_count(map->boxList); + for (j=0; jboxList, j); + + if (*switchGroupID) { + if (tsel->switchGroup==next_switch_group_id) { + if (a_trak->Header->alternate_group != alternateGroupID) return GF_BAD_PARAM; + } + } else { + if (tsel->switchGroup && (tsel->switchGroup>=next_switch_group_id) ) + next_switch_group_id = tsel->switchGroup; + } + } + } + + } + i++; + } + if (! *switchGroupID) *switchGroupID = next_switch_group_id+1; + } + + + trak->Header->alternate_group = alternateGroupID; + + tsel = NULL; + if (*switchGroupID) { + if (!trak->udta) { + e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); + + /*locate tsel box with no switch group*/ + if (map) { + u32 j, count = gf_list_count(map->boxList); + for (j=0; jboxList, j); + if (tsel->switchGroup == *switchGroupID) break; + tsel = NULL; + } + } + if (!tsel) { + tsel = (GF_TrackSelectionBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TSEL); + e = udta_AddBox(trak->udta, (GF_Box *) tsel); + if (e) return e; + } + + tsel->switchGroup = *switchGroupID; + tsel->attributeListCount = criteriaListCount; + if (tsel->attributeList) free(tsel->attributeList); + tsel->attributeList = malloc(sizeof(u32)*criteriaListCount); + memcpy(tsel->attributeList, criteriaList, sizeof(u32)*criteriaListCount); + } + return GF_OK; +} + +void reset_tsel_box(GF_TrackBox *trak) +{ + GF_UserDataMap *map; + trak->Header->alternate_group = 0; + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); + if (map) { + gf_list_del_item(trak->udta->recordList, map); + gf_isom_box_array_del(map->boxList); + free(map); + } + +} + +GF_EXPORT +GF_Err gf_isom_reset_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, Bool reset_all_group) +{ + GF_TrackBox *trak; + u32 alternateGroupID = 0; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->Header->alternate_group) return GF_OK; + + alternateGroupID = trak->Header->alternate_group; + if (reset_all_group) { + u32 i=0; + while (i< gf_isom_get_track_count(movie) ) { + //locate first available ID + GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i+1); + if (a_trak->Header->alternate_group == alternateGroupID) reset_tsel_box(a_trak); + i++; + } + } else { + reset_tsel_box(trak); + } + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_isom_reset_switch_parameters(GF_ISOFile *movie) +{ + u32 i=0; + while (i< gf_isom_get_track_count(movie) ) { + //locate first available ID + GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i+1); + reset_tsel_box(a_trak); + i++; + } + return GF_OK; +} + +GF_Err gf_isom_timed_meta_data_config_new(GF_ISOFile *movie, u32 trackNumber, Bool is_xml, char *mime_or_namespace, char *content_encoding, char *schema_loc, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_MetaDataSampleEntryBox *metad; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !mime_or_namespace) + return GF_BAD_PARAM; + + if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_META) + return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + metad = (GF_MetaDataSampleEntryBox*) gf_isom_box_new(is_xml ? GF_ISOM_BOX_TYPE_METX : GF_ISOM_BOX_TYPE_METT); + if (!metad) return GF_OUT_OF_MEM; + + metad->dataReferenceIndex = dataRefIndex; + metad->mime_type_or_namespace = strdup(mime_or_namespace); + if (content_encoding) metad->content_encoding = strdup(content_encoding); + if (schema_loc) metad->xml_schema_loc = strdup(schema_loc); + + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, metad); + if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + return e; +} + +#endif //GPAC_READ_ONLY + + diff --git a/src/isomedia/media.c b/src/isomedia/media.c new file mode 100644 index 0000000..89888b0 --- /dev/null +++ b/src/isomedia/media.c @@ -0,0 +1,809 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +GF_Err Media_GetSampleDesc(GF_MediaBox *mdia, u32 SampleDescIndex, GF_SampleEntryBox **out_entry, u32 *dataRefIndex) +{ + GF_SampleDescriptionBox *stsd; + GF_SampleEntryBox *entry = NULL; + + if (!mdia) return GF_ISOM_INVALID_FILE; + + stsd = mdia->information->sampleTable->SampleDescription; + if (!stsd) return GF_ISOM_INVALID_FILE; + if (!SampleDescIndex || (SampleDescIndex > gf_list_count(stsd->boxList)) ) return GF_BAD_PARAM; + + entry = (GF_SampleEntryBox*)gf_list_get(stsd->boxList, SampleDescIndex - 1); + if (!entry) return GF_ISOM_INVALID_FILE; + + if (out_entry) *out_entry = entry; + if (dataRefIndex) *dataRefIndex = entry->dataReferenceIndex; + return GF_OK; +} + +GF_Err Media_GetSampleDescIndex(GF_MediaBox *mdia, u64 DTS, u32 *sampleDescIndex) +{ + GF_Err e; + u32 sampleNumber, prevSampleNumber, num; + u64 offset; + u8 isEdited; + if (sampleDescIndex == NULL) return GF_BAD_PARAM; + + //find the sample for this time + e = findEntryForTime(mdia->information->sampleTable, (u32) DTS, 0, &sampleNumber, &prevSampleNumber); + if (e) return e; + + if (!sampleNumber && !prevSampleNumber) { + //we have to assume the track was created to be used... If we have a sampleDesc, OK + if (gf_list_count(mdia->information->sampleTable->SampleDescription->boxList)) { + (*sampleDescIndex) = 1; + return GF_OK; + } + return GF_BAD_PARAM; + } + return stbl_GetSampleInfos(mdia->information->sampleTable, ( sampleNumber ? sampleNumber : prevSampleNumber), &offset, &num, sampleDescIndex, &isEdited); +} + +static GF_Err gf_isom_get_3gpp_audio_esd(GF_SampleTableBox *stbl, GF_GenericAudioSampleEntryBox *entry, GF_ESD **out_esd) +{ + GF_BitStream *bs; + char szName[80]; + + (*out_esd) = gf_odf_desc_esd_new(2); + (*out_esd)->decoderConfig->streamType = 0x05; + /*official mapping to MPEG-4*/ + switch (entry->type) { + case GF_ISOM_SUBTYPE_3GP_EVRC: + (*out_esd)->decoderConfig->objectTypeIndication = 0xA0; + return GF_OK; + case GF_ISOM_SUBTYPE_3GP_QCELP: + { + u32 block_size, sample_rate, sample_size, i; + GF_SttsEntry *ent; + /*only map CBR*/ + sample_size = stbl->SampleSize->sampleSize; + (*out_esd)->decoderConfig->objectTypeIndication = 0xE1; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_data(bs, "QLCMfmt ", 8); + gf_bs_write_u32_le(bs, 150);/*fmt chunk size*/ + gf_bs_write_u8(bs, 1); + gf_bs_write_u8(bs, 0); + /*QCELP GUID*/ + gf_bs_write_data(bs, "\x41\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E", 16); + gf_bs_write_u16_le(bs, 1); + memset(szName, 0, 80); + strcpy(szName, "QCELP-13K(GPAC-emulated)"); + gf_bs_write_data(bs, szName, 80); + ent = &stbl->TimeToSample->entries[0]; + sample_rate = entry->samplerate_hi; + block_size = ent ? ent->sampleDelta : 160; + gf_bs_write_u16_le(bs, 8*sample_size*sample_rate/block_size); + gf_bs_write_u16_le(bs, sample_size); + gf_bs_write_u16_le(bs, block_size); + gf_bs_write_u16_le(bs, sample_rate); + gf_bs_write_u16_le(bs, entry->bitspersample); + gf_bs_write_u32_le(bs, sample_size ? 0 : 7); + /**/ + for (i=0; i<7; i++) { + static const u32 qcelp_r2s [] = {0, 1, 1, 4, 2, 8, 3, 17, 4, 35, 5, 8, 14, 1}; + if (sample_size) { + gf_bs_write_u16(bs, 0); + } else { + gf_bs_write_u8(bs, qcelp_r2s[2*i+1]); + gf_bs_write_u8(bs, qcelp_r2s[2*i]); + } + } + gf_bs_write_u16(bs, 0); + memset(szName, 0, 80); + gf_bs_write_data(bs, szName, 20);/*reserved*/ + gf_bs_get_content(bs, & (*out_esd)->decoderConfig->decoderSpecificInfo->data, & (*out_esd)->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + } + return GF_OK; + case GF_ISOM_SUBTYPE_3GP_SMV: + (*out_esd)->decoderConfig->objectTypeIndication = 0xA1; + return GF_OK; + default: + break; + } + /*this is a user-reserved used in gpac - we need a std OTI for AMR/AMRWB*/ + (*out_esd)->decoderConfig->objectTypeIndication = 0x80; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, entry->type); + gf_bs_write_u16(bs, entry->samplerate_hi); + gf_bs_write_u16(bs, (entry->type == GF_ISOM_SUBTYPE_3GP_AMR) ? 160 : 320); + gf_bs_write_u8(bs, entry->channel_count); + gf_bs_write_u8(bs, entry->bitspersample); + gf_bs_write_u8(bs, 0); + gf_bs_get_content(bs, & (*out_esd)->decoderConfig->decoderSpecificInfo->data, & (*out_esd)->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + return GF_OK; +} + +GF_Err Media_GetESD(GF_MediaBox *mdia, u32 sampleDescIndex, GF_ESD **out_esd, Bool true_desc_only) +{ + GF_ESD *esd; + GF_MPEGSampleEntryBox *entry = NULL; + GF_ESDBox *ESDa; + GF_SampleDescriptionBox *stsd = mdia->information->sampleTable->SampleDescription; + + *out_esd = NULL; + if (!stsd || !stsd->boxList || !sampleDescIndex || (sampleDescIndex > gf_list_count(stsd->boxList)) ) + return GF_BAD_PARAM; + + esd = NULL; + entry = (GF_MPEGSampleEntryBox*)gf_list_get(stsd->boxList, sampleDescIndex - 1); + if (! entry) return GF_ISOM_INVALID_MEDIA; + + *out_esd = NULL; + ESDa = NULL; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_ENCV: + ESDa = ((GF_MPEGVisualSampleEntryBox*)entry)->esd; + if (ESDa) esd = (GF_ESD *) ESDa->desc; + /*avc1 encrypted*/ + else esd = ((GF_MPEGVisualSampleEntryBox*) entry)->emul_esd; + break; + case GF_ISOM_BOX_TYPE_AVC1: + esd = ((GF_MPEGVisualSampleEntryBox*) entry)->emul_esd; + break; + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_ENCA: + ESDa = ((GF_MPEGAudioSampleEntryBox*)entry)->esd; + if (ESDa) esd = (GF_ESD *) ESDa->desc; + break; + case GF_ISOM_BOX_TYPE_MP4S: + case GF_ISOM_BOX_TYPE_ENCS: + ESDa = entry->esd; + if (ESDa) esd = (GF_ESD *) ESDa->desc; + break; + case GF_ISOM_BOX_TYPE_TX3G: + if (!true_desc_only && mdia->mediaTrack->moov->mov->convert_streaming_text) { + GF_Err e = gf_isom_get_ttxt_esd(mdia, out_esd); + if (e) return e; + break; + } + else return GF_ISOM_INVALID_MEDIA; + + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + if (!true_desc_only) { + GF_Err e = gf_isom_get_3gpp_audio_esd(mdia->information->sampleTable, (GF_GenericAudioSampleEntryBox*)entry, out_esd); + if (e) return e; + break; + } else return GF_ISOM_INVALID_MEDIA; + + case GF_ISOM_SUBTYPE_3GP_H263: + if (true_desc_only) { + return GF_ISOM_INVALID_MEDIA; + } else { + GF_BitStream *bs; + esd = gf_odf_desc_esd_new(2); + *out_esd = esd; + esd->decoderConfig->streamType = 0x04; + esd->decoderConfig->objectTypeIndication = 0x80; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, entry->type); + gf_bs_write_u16(bs, ((GF_MPEGVisualSampleEntryBox*)entry)->Width); + gf_bs_write_u16(bs, ((GF_MPEGVisualSampleEntryBox*)entry)->Height); + gf_bs_get_content(bs, & esd->decoderConfig->decoderSpecificInfo->data, & esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + return GF_OK; + } + + default: return GF_ISOM_INVALID_MEDIA; + } + + if (true_desc_only) { + if (!esd) return GF_ISOM_INVALID_MEDIA; + *out_esd = esd; + return GF_OK; + } else { + if (!esd && !*out_esd) return GF_ISOM_INVALID_MEDIA; + if (*out_esd == NULL) gf_odf_desc_copy((GF_Descriptor *)esd, (GF_Descriptor **)out_esd); + } + return GF_OK; +} + +Bool Media_IsSampleSyncShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber) +{ + u32 i; + GF_StshEntry *ent; + if (!stsh) return 0; + i=0; + while ((ent = (GF_StshEntry*)gf_list_enum(stsh->entries, &i))) { + if ((u32) ent->syncSampleNumber == sampleNumber) return 1; + else if ((u32) ent->syncSampleNumber > sampleNumber) return 0; + } + return 0; +} + +GF_Err Media_GetSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample **samp, u32 *sIDX, Bool no_data, u64 *out_offset) +{ + GF_Err e; + u32 bytesRead; + u32 dataRefIndex, chunkNumber; + u64 offset, new_size; + u8 isEdited; + GF_SampleEntryBox *entry; + + + if (!mdia || !mdia->information->sampleTable) return GF_BAD_PARAM; + + //OK, here we go.... + if (sampleNumber > mdia->information->sampleTable->SampleSize->sampleCount) return GF_BAD_PARAM; + + //get the DTS + e = stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber, &(*samp)->DTS); + if (e) return e; + //the CTS offset + if (mdia->information->sampleTable->CompositionOffset) { + e = stbl_GetSampleCTS(mdia->information->sampleTable->CompositionOffset , sampleNumber, &(*samp)->CTS_Offset); + if (e) return e; + } else { + (*samp)->CTS_Offset = 0; + } + //the size + e = stbl_GetSampleSize(mdia->information->sampleTable->SampleSize, sampleNumber, &(*samp)->dataLength); + if (e) return e; + //the RAP + if (mdia->information->sampleTable->SyncSample) { + e = stbl_GetSampleRAP(mdia->information->sampleTable->SyncSample, sampleNumber, &(*samp)->IsRAP, NULL, NULL); + if (e) return e; + } else { + //if no SyncSample, all samples are sync (cf spec) + (*samp)->IsRAP = 1; + } + /*overwrite sync sample with sample dep if any*/ + if (mdia->information->sampleTable->SampleDep) { + u32 dependsOn, dependedOn, redundant; + e = stbl_GetSampleDepType(mdia->information->sampleTable->SampleDep, sampleNumber, &dependsOn, &dependedOn, &redundant); + if (!e) { + if (dependsOn==1) (*samp)->IsRAP = 0; + else if (dependsOn==2) (*samp)->IsRAP = 1; + /*if not depended upon and redundant, mark as carousel sample*/ + if ((dependedOn==2) && (redundant==1)) (*samp)->IsRAP = 2; + /*TODO FIXME - we must enhance the IsRAP semantics to carry disposable info ... */ + } + } + /*get sync shadow*/ + if (Media_IsSampleSyncShadow(mdia->information->sampleTable->ShadowSync, sampleNumber)) (*samp)->IsRAP = 2; + + //the data info + if (!sIDX && !no_data) return GF_BAD_PARAM; + if (!sIDX && !out_offset) return GF_OK; + + (*sIDX) = 0; + e = stbl_GetSampleInfos(mdia->information->sampleTable, sampleNumber, &offset, &chunkNumber, sIDX, &isEdited); + if (e) return e; + + //then get the DataRef + e = Media_GetSampleDesc(mdia, *sIDX, &entry, &dataRefIndex); + if (e) return e; + + // Open the data handler - check our mode, don't reopen in read only if this is + //the same entry. In other modes we have no choice because the main data map is + //divided into the original and the edition files + if (mdia->mediaTrack->moov->mov->openMode == GF_ISOM_OPEN_READ) { + //same as last call in read mode + if (!mdia->information->dataHandler || (mdia->information->dataEntryIndex != dataRefIndex)) { + e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited); + if (e) return e; + } + } else { + e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited); + if (e) return e; + } + + if (out_offset) *out_offset = offset; + if (no_data) return GF_OK; + + /*and finally get the data, include padding if needed*/ + (*samp)->data = (char *) malloc(sizeof(char) * ( (*samp)->dataLength + mdia->mediaTrack->padding_bytes) ); + if (mdia->mediaTrack->padding_bytes) + memset((*samp)->data + (*samp)->dataLength, 0, sizeof(char) * mdia->mediaTrack->padding_bytes); + + //check if we can get the sample (make sure we have enougth data...) + new_size = gf_bs_get_size(mdia->information->dataHandler->bs); + if (offset + (*samp)->dataLength > new_size) { + //always refresh the size to avoid wrong info on http/ftp + new_size = gf_bs_get_refreshed_size(mdia->information->dataHandler->bs); + if (offset + (*samp)->dataLength > new_size) { + mdia->BytesMissing = offset + (*samp)->dataLength - new_size; + return GF_ISOM_INCOMPLETE_FILE; + } + } + + bytesRead = gf_isom_datamap_get_data(mdia->information->dataHandler, (*samp)->data, (*samp)->dataLength, offset); + //if bytesRead != sampleSize, we have an IO err + if (bytesRead < (*samp)->dataLength) { + return GF_IO_ERR; + } + mdia->BytesMissing = 0; + //finally rewrite the sample if this is an OD Access Unit + if (mdia->handler->handlerType == GF_ISOM_MEDIA_OD) { + e = Media_RewriteODFrame(mdia, *samp); + if (e) return e; + } + else if (mdia->mediaTrack->moov->mov->convert_streaming_text + && (mdia->handler->handlerType == GF_ISOM_MEDIA_TEXT) ) { + u64 dur; + if (sampleNumber == mdia->information->sampleTable->SampleSize->sampleCount) { + dur = mdia->mediaHeader->duration - (*samp)->DTS; + } else { + stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber+1, &dur); + dur -= (*samp)->DTS; + } + e = gf_isom_rewrite_text_sample(*samp, *sIDX, (u32) dur); + if (e) return e; + } + return GF_OK; +} + + + +GF_Err Media_CheckDataEntry(GF_MediaBox *mdia, u32 dataEntryIndex) +{ + + GF_DataEntryURLBox *entry; + GF_DataMap *map; + GF_Err e; + if (!mdia || !dataEntryIndex || dataEntryIndex > gf_list_count(mdia->information->dataInformation->dref->boxList)) return GF_BAD_PARAM; + + entry = (GF_DataEntryURLBox*)gf_list_get(mdia->information->dataInformation->dref->boxList, dataEntryIndex - 1); + if (!entry) return GF_ISOM_INVALID_FILE; + if (entry->flags == 1) return GF_OK; + + //ok, not self contained, let's go for it... + //we don't know what's a URN yet + if (entry->type == GF_ISOM_BOX_TYPE_URN) return GF_NOT_SUPPORTED; + if (mdia->mediaTrack->moov->mov->openMode == GF_ISOM_OPEN_WRITE) { + e = gf_isom_datamap_new(entry->location, NULL, GF_ISOM_DATA_MAP_READ, &map); + } else { + e = gf_isom_datamap_new(entry->location, mdia->mediaTrack->moov->mov->fileName, GF_ISOM_DATA_MAP_READ, &map); + } + if (e) return e; + gf_isom_datamap_del(map); + return GF_OK; +} + + +Bool Media_IsSelfContained(GF_MediaBox *mdia, u32 StreamDescIndex) +{ + u32 drefIndex=0; + GF_FullBox *a; + GF_SampleEntryBox *se = NULL; + + Media_GetSampleDesc(mdia, StreamDescIndex, &se, &drefIndex); + if (!drefIndex) return 0; + a = (GF_FullBox*)gf_list_get(mdia->information->dataInformation->dref->boxList, drefIndex - 1); + if (a->flags & 1) return 1; + /*QT specific*/ + if (a->type == GF_4CC('a', 'l', 'i', 's')) return 1; + return 0; +} + + + +//look for a sync sample from a given point in media time +GF_Err Media_FindSyncSample(GF_SampleTableBox *stbl, u32 searchFromSample, u32 *sampleNumber, u8 mode) +{ + u8 isRAP; + u32 next, prev; + if (!stbl || !stbl->SyncSample) return GF_BAD_PARAM; + + //set to current sample if we don't find a RAP + *sampleNumber = searchFromSample; + + //this is not the exact sample, but the prev move to next sample if enough samples.... + if ( (mode == GF_ISOM_SEARCH_SYNC_FORWARD) && (searchFromSample == stbl->SampleSize->sampleCount) ) { + return GF_OK; + } + if ( (mode == GF_ISOM_SEARCH_SYNC_BACKWARD) && !searchFromSample) { + *sampleNumber = 1; + return GF_OK; + } + //get the entry + stbl_GetSampleRAP(stbl->SyncSample, searchFromSample, &isRAP, &prev, &next); + if (isRAP) { + (*sampleNumber) = searchFromSample; + return GF_OK; + } + //nothing yet, go for next time... + if (mode == GF_ISOM_SEARCH_SYNC_FORWARD) { + if (next) *sampleNumber = next; + } else { + if (prev) *sampleNumber = prev; + } + return GF_OK; +} + +//create a DataReference if not existing (only for WRITE-edit mode) +GF_Err Media_FindDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex) +{ + u32 i; + GF_DataEntryURLBox *entry; + + if (!dref) return GF_BAD_PARAM; + *dataRefIndex = 0; + i=0; + while ((entry = (GF_DataEntryURLBox*)gf_list_enum(dref->boxList, &i))) { + if (entry->type == GF_ISOM_BOX_TYPE_URL) { + //self-contained case + if (entry->flags == 1) { + //if nothing specified, get the dataRef + if (!URLname && !URNname) { + *dataRefIndex = i; + return GF_OK; + } + } else { + //OK, check if we have URL + if (URLname && !strcmp(URLname, entry->location)) { + *dataRefIndex = i; + return GF_OK; + } + } + } else { + //this is a URN one, only check the URN name (URL optional) + if (URNname && !strcmp(URNname, ((GF_DataEntryURNBox *)entry)->nameURN)) { + *dataRefIndex = i; + return GF_OK; + } + } + } + return GF_OK; +} + +//Get the total media duration based on the TimeToSample table +GF_Err Media_SetDuration(GF_TrackBox *trak) +{ + GF_ESD *esd; + u64 DTS; + GF_SttsEntry *ent; + u32 nbSamp = trak->Media->information->sampleTable->SampleSize->sampleCount; + + //we need to check how many samples we have. + // == 1 -> last sample duration == default duration + // > 1 -> last sample duration == prev sample duration + switch (nbSamp) { + case 0: + trak->Media->mediaHeader->duration = 0; + if (Track_IsMPEG4Stream(trak->Media->handler->handlerType)) { + Media_GetESD(trak->Media, 1, &esd, 1); + if (esd && esd->URLString) trak->Media->mediaHeader->duration = (u64) -1; + } + return GF_OK; + +// case 1: +// trak->Media->mediaHeader->duration = trak->Media->mediaHeader->timeScale; +// return GF_OK; + + default: + //we assume a constant frame rate for the media and assume the last sample + //will be hold the same time as the prev one + stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp, &DTS); + ent = &trak->Media->information->sampleTable->TimeToSample->entries[trak->Media->information->sampleTable->TimeToSample->nb_entries-1]; + trak->Media->mediaHeader->duration = DTS; + +#if 1 + trak->Media->mediaHeader->duration += ent->sampleDelta; +#else + if (!ent) { + u64 DTSprev; + stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp-1, &DTSprev); + trak->Media->mediaHeader->duration += (DTS - DTSprev); + } else { +#ifndef GPAC_READ_ONLY + if (trak->moov->mov->editFileMap && trak->Media->information->sampleTable->CompositionOffset) { + u32 count, i; + u64 max_ts; + GF_DttsEntry *cts_ent; + GF_CompositionOffsetBox *ctts = trak->Media->information->sampleTable->CompositionOffset; + if (ctts->w_LastSampleNumber==nbSamp) { + count = gf_list_count(ctts->entryList); + max_ts = trak->Media->mediaHeader->duration; + while (count) { + count -= 1; + cts_ent = gf_list_get(ctts->entryList, count); + if (nbSampsampleCount) break; + + for (i=0; isampleCount; i++) { + stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp-i, &DTS); + if ((s32) cts_ent->decodingOffset < 0) max_ts = DTS; + else max_ts = DTS + cts_ent->decodingOffset; + if (max_ts>=trak->Media->mediaHeader->duration) { + trak->Media->mediaHeader->duration = max_ts; + } else { + break; + } + } + if (max_tsMedia->mediaHeader->duration) { + break; + } + nbSamp-=cts_ent->sampleCount; + } + } + } +#endif + trak->Media->mediaHeader->duration += ent->sampleDelta; + } +#endif + return GF_OK; + } +} + + + + +#ifndef GPAC_READ_ONLY + + +GF_Err Media_CreateDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex) +{ + GF_Err e; + GF_DataEntryURLBox *entry; + + GF_Err dref_AddDataEntry(GF_DataReferenceBox *ptr, GF_Box *entry); + + if (!URLname && !URNname) { + //THIS IS SELF CONTAIN, create a regular entry if needed + entry = (GF_DataEntryURLBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_URL); + entry->location = NULL; + entry->flags = 0; + entry->flags |= 1; + e = dref_AddDataEntry(dref, (GF_Box *)entry); + if (e) return e; + *dataRefIndex = gf_list_count(dref->boxList); + return GF_OK; + } else if (!URNname && URLname) { + //THIS IS URL + entry = (GF_DataEntryURLBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_URL); + entry->flags = 0; + entry->location = (char*)malloc(strlen(URLname)+1); + if (! entry->location) { + gf_isom_box_del((GF_Box *)entry); + return GF_OUT_OF_MEM; + } + strcpy(entry->location, URLname); + e = dref_AddDataEntry(dref, (GF_Box *)entry); + if (e) return e; + *dataRefIndex = gf_list_count(dref->boxList); + return GF_OK; + } else { + //THIS IS URN + entry = (GF_DataEntryURLBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_URN); + ((GF_DataEntryURNBox *)entry)->flags = 0; + ((GF_DataEntryURNBox *)entry)->nameURN = (char*)malloc(strlen(URNname)+1); + if (! ((GF_DataEntryURNBox *)entry)->nameURN) { + gf_isom_box_del((GF_Box *)entry); + return GF_OUT_OF_MEM; + } + strcpy(((GF_DataEntryURNBox *)entry)->nameURN, URNname); + //check for URL + if (URLname) { + ((GF_DataEntryURNBox *)entry)->location = (char*)malloc(strlen(URLname)+1); + if (! ((GF_DataEntryURNBox *)entry)->location) { + gf_isom_box_del((GF_Box *)entry); + return GF_OUT_OF_MEM; + } + strcpy(((GF_DataEntryURNBox *)entry)->location, URLname); + } + e = dref_AddDataEntry(dref, (GF_Box *)entry); + if (e) return e; + *dataRefIndex = gf_list_count(dref->boxList); + return GF_OK; + } + return GF_OK; +} + + +GF_Err Media_AddSample(GF_MediaBox *mdia, u64 data_offset, GF_ISOSample *sample, u32 StreamDescIndex, u32 syncShadowNumber) +{ + GF_Err e; + GF_SampleTableBox *stbl; + u32 sampleNumber, i; + if (!mdia || !sample) return GF_BAD_PARAM; + + stbl = mdia->information->sampleTable; + + //get a valid sampleNumber for this new guy + e = stbl_AddDTS(stbl, sample->DTS, &sampleNumber, mdia->mediaHeader->timeScale); + if (e) return e; + + //add size + e = stbl_AddSize(stbl->SampleSize, sampleNumber, sample->dataLength); + if (e) return e; + + //adds CTS offset + if (sample->CTS_Offset) { + //if we don't have a CTS table, add it... + if (!stbl->CompositionOffset) stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS); + //then add our CTS (the prev samples with no CTS offset will be automatically added... + e = stbl_AddCTS(stbl, sampleNumber, sample->CTS_Offset); + if (e) return e; + } else if (stbl->CompositionOffset) { + e = stbl_AddCTS(stbl, sampleNumber, sample->CTS_Offset); + if (e) return e; + } + + //The first non sync sample we see must create a syncTable + if (sample->IsRAP) { + //insert it only if we have a sync table + if (stbl->SyncSample) { + e = stbl_AddRAP(stbl->SyncSample, sampleNumber); + if (e) return e; + } + } else { + //non-sync sample. Create a SyncSample table if needed + if (!stbl->SyncSample) { + stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS); + //all the prev samples are sync + for (i=0; iSampleSize->sampleCount; i++) { + if (i+1 != sampleNumber) { + e = stbl_AddRAP(stbl->SyncSample, i+1); + if (e) return e; + } + } + } + } + if (sample->IsRAP==2) { + e = stbl_AddRedundant(stbl, sampleNumber); + if (e) return e; + } + + //and update the chunks + e = stbl_AddChunkOffset(mdia, sampleNumber, StreamDescIndex, data_offset); + if (e) return e; + + if (!syncShadowNumber) return GF_OK; + if (!stbl->ShadowSync) stbl->ShadowSync = (GF_ShadowSyncBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSH); + return stbl_AddShadow(mdia->information->sampleTable->ShadowSync, sampleNumber, syncShadowNumber); +} + + +GF_Err UpdateSample(GF_MediaBox *mdia, u32 sampleNumber, u32 size, u32 CTS, u64 offset, u8 isRap) +{ + u32 i; + GF_SampleTableBox *stbl = mdia->information->sampleTable; + + //set size, offset, RAP, CTS ... + stbl_SetSampleSize(stbl->SampleSize, sampleNumber, size); + stbl_SetChunkOffset(mdia, sampleNumber, offset); + + //do we have a CTS? + if (stbl->CompositionOffset) { + stbl_SetSampleCTS(stbl, sampleNumber, CTS); + } else { + //do we need one ?? + if (CTS) { + stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS); + stbl_AddCTS(stbl, sampleNumber, CTS); + } + } + //do we have a sync ??? + if (stbl->SyncSample) { + stbl_SetSampleRAP(stbl->SyncSample, sampleNumber, isRap); + } else { + //do we need one + if (! isRap) { + stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS); + //what a pain: all the sample we had have to be sync ... + for (i=0; iSampleSize->sampleCount; i++) { + if (i+1 != sampleNumber) stbl_AddRAP(stbl->SyncSample, i+1); + } + } + } + if (isRap==2) { + stbl_SetRedundant(stbl, sampleNumber); + } + return GF_OK; +} + +GF_Err Media_UpdateSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample *sample, Bool data_only) +{ + GF_Err e; + u32 drefIndex, chunkNum, descIndex; + u64 newOffset, DTS; + u8 isEdited; + GF_DataEntryURLBox *Dentry; + GF_SampleTableBox *stbl; + + GF_Err stbl_AddBox(GF_SampleTableBox *ptr, GF_Box *a); + + if (!mdia || !sample || !sampleNumber || !mdia->mediaTrack->moov->mov->editFileMap) + return GF_BAD_PARAM; + + stbl = mdia->information->sampleTable; + + if (!data_only) { + //check we have the sampe dts + e = stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &DTS); + if (e) return e; + if (DTS != sample->DTS) return GF_BAD_PARAM; + } + + //get our infos + stbl_GetSampleInfos(stbl, sampleNumber, &newOffset, &chunkNum, &descIndex, &isEdited); + + //then check the data ref + e = Media_GetSampleDesc(mdia, descIndex, NULL, &drefIndex); + if (e) return e; + Dentry = (GF_DataEntryURLBox*)gf_list_get(mdia->information->dataInformation->dref->boxList, drefIndex - 1); + if (!Dentry) return GF_ISOM_INVALID_FILE; + + if (Dentry->flags != 1) return GF_BAD_PARAM; + + //MEDIA DATA EDIT: write this new sample to the edit temp file + newOffset = gf_isom_datamap_get_offset(mdia->mediaTrack->moov->mov->editFileMap); + e = gf_isom_datamap_add_data(mdia->mediaTrack->moov->mov->editFileMap, sample->data, sample->dataLength); + if (e) return e; + + if (data_only) { + stbl_SetSampleSize(stbl->SampleSize, sampleNumber, sample->dataLength); + return stbl_SetChunkOffset(mdia, sampleNumber, newOffset); + } + return UpdateSample(mdia, sampleNumber, sample->dataLength, sample->CTS_Offset, newOffset, sample->IsRAP); +} + +GF_Err Media_UpdateSampleReference(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset) +{ + GF_Err e; + u32 drefIndex, chunkNum, descIndex; + u64 off, DTS; + u8 isEdited; + GF_DataEntryURLBox *Dentry; + GF_SampleTableBox *stbl; + GF_Err stbl_AddBox(GF_SampleTableBox *ptr, GF_Box *a); + + if (!mdia) return GF_BAD_PARAM; + stbl = mdia->information->sampleTable; + + //check we have the sampe dts + e = stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &DTS); + if (e) return e; + if (DTS != sample->DTS) return GF_BAD_PARAM; + + //get our infos + stbl_GetSampleInfos(stbl, sampleNumber, &off, &chunkNum, &descIndex, &isEdited); + + //then check the data ref + e = Media_GetSampleDesc(mdia, descIndex, NULL, &drefIndex); + if (e) return e; + Dentry = (GF_DataEntryURLBox*)gf_list_get(mdia->information->dataInformation->dref->boxList, drefIndex - 1); + if (!Dentry) return GF_ISOM_INVALID_FILE; + + //we only modify self-contained data + if (Dentry->flags == 1) return GF_ISOM_INVALID_MODE; + + //and we don't modify the media data + return UpdateSample(mdia, sampleNumber, sample->dataLength, sample->CTS_Offset, data_offset, sample->IsRAP); +} + + +#endif //GPAC_READ_ONLY diff --git a/src/isomedia/media_odf.c b/src/isomedia/media_odf.c new file mode 100644 index 0000000..1597b54 --- /dev/null +++ b/src/isomedia/media_odf.c @@ -0,0 +1,516 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +// Rewrite the good dependancies when an OD AU is extracted from the file +GF_Err Media_RewriteODFrame(GF_MediaBox *mdia, GF_ISOSample *sample) +{ + GF_Err e; + GF_ODCodec *ODdecode; + GF_ODCodec *ODencode; + GF_ODCom *com; + + //the commands we proceed + GF_ESDUpdate *esdU, *esdU2; + GF_ESDRemove *esdR, *esdR2; + GF_ODUpdate *odU, *odU2; + + //the desc they contain + GF_ObjectDescriptor *od; + GF_IsomObjectDescriptor *isom_od; + GF_ESD *esd; + GF_ES_ID_Ref *ref; + GF_Descriptor *desc; + GF_TrackReferenceTypeBox *mpod; + u32 i, j, skipped; + + if (!mdia || !sample || !sample->data || !sample->dataLength) return GF_BAD_PARAM; + + mpod = NULL; + e = Track_FindRef(mdia->mediaTrack, GF_ISOM_BOX_TYPE_MPOD, &mpod); + if (e) return e; + //no references, nothing to do... + if (!mpod) return GF_OK; + + ODdecode = gf_odf_codec_new(); + if (!ODdecode) return GF_OUT_OF_MEM; + ODencode = gf_odf_codec_new(); + if (!ODencode) { + gf_odf_codec_del(ODdecode); + return GF_OUT_OF_MEM; + } + e = gf_odf_codec_set_au(ODdecode, sample->data, sample->dataLength); + if (e) goto err_exit; + e = gf_odf_codec_decode(ODdecode); + if (e) goto err_exit; + + while (1) { + com = gf_odf_codec_get_com(ODdecode); + if (!com) break; + + //we only need to rewrite commands with ESDs inside: ESDUpdate and ODUpdate + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + odU = (GF_ODUpdate *) com; + odU2 = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + + i=0; + while ((desc = (GF_Descriptor*)gf_list_enum(odU->objectDescriptors, &i))) { + switch (desc->tag) { + case GF_ODF_OD_TAG: + case GF_ODF_ISOM_OD_TAG: + //IOD can be used in OD streams + case GF_ODF_ISOM_IOD_TAG: + break; + default: + return GF_ISOM_INVALID_FILE; + } + e = gf_odf_desc_copy(desc, (GF_Descriptor **)&isom_od); + if (e) goto err_exit; + + //create our OD... + if (desc->tag == GF_ODF_ISOM_IOD_TAG) { + od = (GF_ObjectDescriptor *) malloc(sizeof(GF_InitialObjectDescriptor)); + } else { + od = (GF_ObjectDescriptor *) malloc(sizeof(GF_ObjectDescriptor)); + } + if (!od) { + e = GF_OUT_OF_MEM; + goto err_exit; + } + od->ESDescriptors = gf_list_new(); + //and duplicate... + od->objectDescriptorID = isom_od->objectDescriptorID; + od->tag = GF_ODF_OD_TAG; + od->URLString = isom_od->URLString; + isom_od->URLString = NULL; + od->extensionDescriptors = isom_od->extensionDescriptors; + isom_od->extensionDescriptors = NULL; + od->IPMP_Descriptors = isom_od->IPMP_Descriptors; + isom_od->IPMP_Descriptors = NULL; + od->OCIDescriptors = isom_od->OCIDescriptors; + isom_od->OCIDescriptors = NULL; + + //init as IOD + if (isom_od->tag == GF_ODF_ISOM_IOD_TAG) { + ((GF_InitialObjectDescriptor *)od)->audio_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->audio_profileAndLevel; + ((GF_InitialObjectDescriptor *)od)->inlineProfileFlag = ((GF_IsomInitialObjectDescriptor *)isom_od)->inlineProfileFlag; + ((GF_InitialObjectDescriptor *)od)->graphics_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->graphics_profileAndLevel; + ((GF_InitialObjectDescriptor *)od)->OD_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->OD_profileAndLevel; + ((GF_InitialObjectDescriptor *)od)->scene_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->scene_profileAndLevel; + ((GF_InitialObjectDescriptor *)od)->visual_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->visual_profileAndLevel; + ((GF_InitialObjectDescriptor *)od)->IPMPToolList = ((GF_IsomInitialObjectDescriptor *)isom_od)->IPMPToolList; + ((GF_IsomInitialObjectDescriptor *)isom_od)->IPMPToolList = NULL; + } + + //then rewrite the ESDesc + j=0; + while ((ref = (GF_ES_ID_Ref*)gf_list_enum(isom_od->ES_ID_RefDescriptors, &j))){ + //if the ref index is not valid, skip this desc... + if (!mpod->trackIDs || gf_isom_get_track_from_id(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1]) == NULL) continue; + //OK, get the esd + e = GetESDForTime(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1], sample->DTS, &esd); + if (!e) e = gf_odf_desc_add_desc((GF_Descriptor *) od, (GF_Descriptor *) esd); + if (e) { + gf_odf_desc_del((GF_Descriptor *)od); + gf_odf_com_del((GF_ODCom **)&odU2); + gf_odf_desc_del((GF_Descriptor *)isom_od); + gf_odf_com_del((GF_ODCom **)&odU); + goto err_exit; + } + + } + //delete our desc + gf_odf_desc_del((GF_Descriptor *)isom_od); + gf_list_add(odU2->objectDescriptors, od); + } + //clean a bit + gf_odf_com_del((GF_ODCom **)&odU); + gf_odf_codec_add_com(ODencode, (GF_ODCom *)odU2); + break; + + case GF_ODF_ESD_UPDATE_TAG: + esdU = (GF_ESDUpdate *) com; + esdU2 = (GF_ESDUpdate *) gf_odf_com_new(GF_ODF_ESD_UPDATE_TAG); + esdU2->ODID = esdU->ODID; + i=0; + while ((ref = (GF_ES_ID_Ref*)gf_list_enum(esdU->ESDescriptors, &i))) { + //if the ref index is not valid, skip this desc... + if (gf_isom_get_track_from_id(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1]) == NULL) continue; + //OK, get the esd + e = GetESDForTime(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1], sample->DTS, &esd); + if (e) goto err_exit; + gf_list_add(esdU2->ESDescriptors, esd); + } + gf_odf_com_del((GF_ODCom **)&esdU); + gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdU2); + break; + + //brand new case: the ESRemove follows the same principle according to the spec... + case GF_ODF_ESD_REMOVE_REF_TAG: + //both commands have the same structure, only the tags change + esdR = (GF_ESDRemove *) com; + esdR2 = (GF_ESDRemove *) gf_odf_com_new(GF_ODF_ESD_REMOVE_TAG); + esdR2->ODID = esdR->ODID; + esdR2->NbESDs = esdR->NbESDs; + //alloc our stuff + esdR2->ES_ID = (unsigned short*)malloc(sizeof(u32) * esdR->NbESDs); + if (!esdR2->ES_ID) { + e = GF_OUT_OF_MEM; + goto err_exit; + } + skipped = 0; + //get the ES_ID in the mpod indicated in the ES_ID[] + for (i = 0; i < esdR->NbESDs; i++) { + //if the ref index is not valid, remove this desc... + if (gf_isom_get_track_from_id(mdia->mediaTrack->moov, mpod->trackIDs[esdR->ES_ID[i] - 1]) == NULL) { + skipped ++; + } else { + //the command in the file has the ref index of the trackID in the mpod + esdR2->ES_ID[i - skipped] = mpod->trackIDs[esdR->ES_ID[i] - 1]; + } + } + //realloc... + if (skipped && (skipped != esdR2->NbESDs) ) { + esdR2->NbESDs -= skipped; + esdR2->ES_ID = (unsigned short*)realloc(esdR2->ES_ID, sizeof(u32) * esdR2->NbESDs); + } + gf_odf_com_del((GF_ODCom **)&esdR); + gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdR2); + break; + + default: + e = gf_odf_codec_add_com(ODencode, com); + if (e) goto err_exit; + } + } + //encode our new AU + e = gf_odf_codec_encode(ODencode, 1); + if (e) goto err_exit; + + //and set the buffer in the sample + free(sample->data); + sample->data = NULL; + sample->dataLength = 0; + e = gf_odf_codec_get_au(ODencode, &sample->data, &sample->dataLength); + +err_exit: + gf_odf_codec_del(ODdecode); + gf_odf_codec_del(ODencode); + return e; +} + + +// Update the dependancies when an OD AU is inserted in the file +GF_Err Media_ParseODFrame(GF_MediaBox *mdia, GF_ISOSample *sample, GF_ISOSample **od_samp) +{ + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *mpod; + GF_Err e; + GF_ODCom *com; + GF_ODCodec *ODencode; + GF_ODCodec *ODdecode; + u32 i, j; + //the commands we proceed + GF_ESDUpdate *esdU, *esdU2; + GF_ESDRemove *esdR, *esdR2; + GF_ODUpdate *odU, *odU2; + + //the desc they contain + GF_ObjectDescriptor *od; + GF_IsomObjectDescriptor *isom_od; + GF_ESD *esd; + GF_ES_ID_Ref *ref; + GF_Descriptor *desc; + + *od_samp = NULL; + if (!mdia || !sample || !sample->data || !sample->dataLength) return GF_BAD_PARAM; + + //First find the references, and create them if none + tref = mdia->mediaTrack->References; + if (!tref) { + tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); + e = trak_AddBox((GF_Box*)mdia->mediaTrack, (GF_Box *) tref); + if (e) return e; + } + //then find the OD reference, and create it if none + e = Track_FindRef(mdia->mediaTrack, GF_ISOM_BOX_TYPE_MPOD, &mpod); + if (e) return e; + if (!mpod) { + mpod = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + mpod->reference_type = GF_ISOM_BOX_TYPE_MPOD; + e = tref_AddBox((GF_Box*)tref, (GF_Box *)mpod); + if (e) return e; + } + + //OK, create our codecs + ODencode = gf_odf_codec_new(); + if (!ODencode) return GF_OUT_OF_MEM; + ODdecode = gf_odf_codec_new(); + if (!ODdecode) return GF_OUT_OF_MEM; + + e = gf_odf_codec_set_au(ODdecode, sample->data, sample->dataLength); + if (e) goto err_exit; + e = gf_odf_codec_decode(ODdecode); + if (e) goto err_exit; + + while (1) { + com = gf_odf_codec_get_com(ODdecode); + if (!com) break; + + //check our commands + switch (com->tag) { + //Rewrite OD Update + case GF_ODF_OD_UPDATE_TAG: + //duplicate our command + odU = (GF_ODUpdate *) com; + odU2 = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + + i=0; + while ((desc = (GF_Descriptor*)gf_list_enum(odU->objectDescriptors, &i))) { + //both OD and IODs are accepted + switch (desc->tag) { + case GF_ODF_OD_TAG: + case GF_ODF_IOD_TAG: + break; + default: + e = GF_ODF_INVALID_DESCRIPTOR; + goto err_exit; + } + //get the esd + e = gf_odf_desc_copy(desc, (GF_Descriptor **)&od); + if (e) goto err_exit; + if (desc->tag == GF_ODF_OD_TAG) { + isom_od = (GF_IsomObjectDescriptor *) malloc(sizeof(GF_IsomObjectDescriptor)); + isom_od->tag = GF_ODF_ISOM_OD_TAG; + } else { + isom_od = (GF_IsomObjectDescriptor *) malloc(sizeof(GF_IsomInitialObjectDescriptor)); + isom_od->tag = GF_ODF_ISOM_IOD_TAG; + //copy PL + ((GF_IsomInitialObjectDescriptor *)isom_od)->inlineProfileFlag = ((GF_InitialObjectDescriptor *)od)->inlineProfileFlag; + ((GF_IsomInitialObjectDescriptor *)isom_od)->graphics_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->graphics_profileAndLevel; + ((GF_IsomInitialObjectDescriptor *)isom_od)->audio_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->audio_profileAndLevel; + ((GF_IsomInitialObjectDescriptor *)isom_od)->OD_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->OD_profileAndLevel; + ((GF_IsomInitialObjectDescriptor *)isom_od)->scene_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->scene_profileAndLevel; + ((GF_IsomInitialObjectDescriptor *)isom_od)->visual_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->visual_profileAndLevel; + ((GF_IsomInitialObjectDescriptor *)isom_od)->IPMPToolList = ((GF_InitialObjectDescriptor *)od)->IPMPToolList; + ((GF_InitialObjectDescriptor *)od)->IPMPToolList = NULL; + } + //in OD stream only ref desc are accepted + isom_od->ES_ID_RefDescriptors = gf_list_new(); + isom_od->ES_ID_IncDescriptors = NULL; + + //TO DO: check that a given sampleDescription exists + isom_od->extensionDescriptors = od->extensionDescriptors; + od->extensionDescriptors = NULL; + isom_od->IPMP_Descriptors = od->IPMP_Descriptors; + od->IPMP_Descriptors = NULL; + isom_od->OCIDescriptors = od->OCIDescriptors; + od->OCIDescriptors = NULL; + isom_od->URLString = od->URLString; + od->URLString = NULL; + isom_od->objectDescriptorID = od->objectDescriptorID; + + j=0; + while ((esd = (GF_ESD*)gf_list_enum(od->ESDescriptors, &j))) { + ref = (GF_ES_ID_Ref *) gf_odf_desc_new(GF_ODF_ESD_REF_TAG); + //1 to 1 mapping trackID and ESID. Add this track to MPOD + //if track does not exist, this will be remove while reading the OD stream + e = reftype_AddRefTrack(mpod, esd->ESID, &ref->trackRef); + e = gf_odf_desc_add_desc((GF_Descriptor *)isom_od, (GF_Descriptor *)ref); + if (e) goto err_exit; + } + //delete our desc + gf_odf_desc_del((GF_Descriptor *)od); + //and add the new one to our command + gf_list_add(odU2->objectDescriptors, isom_od); + } + //delete the command + gf_odf_com_del((GF_ODCom **)&odU); + //and add the new one to the codec + gf_odf_codec_add_com(ODencode, (GF_ODCom *)odU2); + break; + + //Rewrite ESD Update + case GF_ODF_ESD_UPDATE_TAG: + esdU = (GF_ESDUpdate *) com; + esdU2 = (GF_ESDUpdate *) gf_odf_com_new(GF_ODF_ESD_UPDATE_TAG); + esdU2->ODID = esdU->ODID; + i=0; + while ((esd = (GF_ESD*)gf_list_enum(esdU->ESDescriptors, &i))) { + ref = (GF_ES_ID_Ref *) gf_odf_desc_new(GF_ODF_ESD_REF_TAG); + //1 to 1 mapping trackID and ESID + e = reftype_AddRefTrack(mpod, esd->ESID, &ref->trackRef); + e = gf_list_add(esdU2->ESDescriptors, ref); + if (e) goto err_exit; + } + gf_odf_com_del((GF_ODCom **)&esdU); + gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdU2); + break; + + //Brand new case: the ESRemove has to be rewritten too according to the specs... + case GF_ODF_ESD_REMOVE_TAG: + esdR = (GF_ESDRemove *) com; + esdR2 = (GF_ESDRemove *) gf_odf_com_new(GF_ODF_ESD_REMOVE_TAG); + //change the tag for the file format + esdR2->tag = GF_ODF_ESD_REMOVE_REF_TAG; + esdR2->ODID = esdR->ODID; + esdR2->NbESDs = esdR->NbESDs; + if (esdR->NbESDs) { + //alloc our stuff + esdR2->ES_ID = (unsigned short*)malloc(sizeof(u32) * esdR->NbESDs); + if (!esdR2->ES_ID) { + e = GF_OUT_OF_MEM; + goto err_exit; + } + for (i = 0; i < esdR->NbESDs; i++) { + //1 to 1 mapping trackID and ESID + e = reftype_AddRefTrack(mpod, esdR->ES_ID[i], &esdR2->ES_ID[i]); + if (e) goto err_exit; + } + } + gf_odf_com_del(&com); + gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdR2); + break; + + //Add the command as is + default: + e = gf_odf_codec_add_com(ODencode, com); + if (e) goto err_exit; + } + } + + //encode our new AU + e = gf_odf_codec_encode(ODencode, 1); + if (e) goto err_exit; + + //and set the buffer in the sample + *od_samp = gf_isom_sample_new(); + (*od_samp)->CTS_Offset = sample->CTS_Offset; + (*od_samp)->DTS = sample->DTS; + (*od_samp)->IsRAP = sample->IsRAP; + e = gf_odf_codec_get_au(ODencode, & (*od_samp)->data, & (*od_samp)->dataLength); + if (e) { + gf_isom_sample_del(od_samp); + *od_samp = NULL; + } + +err_exit: + + gf_odf_codec_del(ODencode); + gf_odf_codec_del(ODdecode); + return e; +} + + + +// Rewrite the good dependancies when an OD AU is extracted from the file +static u32 Media_FindOD_ID(GF_MediaBox *mdia, GF_ISOSample *sample, u32 track_id) +{ + GF_Err e; + GF_ODCodec *ODdecode; + GF_ODCom *com; + u32 the_od_id; + GF_ODUpdate *odU; + GF_ESD *esd; + GF_Descriptor *desc; + GF_TrackReferenceTypeBox *mpod; + u32 i, j; + + if (!mdia || !sample || !sample->data || !sample->dataLength) return 0; + + mpod = NULL; + e = Track_FindRef(mdia->mediaTrack, GF_ISOM_BOX_TYPE_MPOD, &mpod); + if (e) return 0; + //no references, nothing to do... + if (!mpod) return 0; + + the_od_id = 0; + + ODdecode = gf_odf_codec_new(); + if (!ODdecode) return 0; + e = gf_odf_codec_set_au(ODdecode, sample->data, sample->dataLength); + if (e) goto err_exit; + e = gf_odf_codec_decode(ODdecode); + if (e) goto err_exit; + + while (1) { + GF_List *esd_list = NULL; + com = gf_odf_codec_get_com(ODdecode); + if (!com) break; + if (com->tag != GF_ODF_OD_UPDATE_TAG) continue; + odU = (GF_ODUpdate *) com; + + i=0; + while ((desc = (GF_Descriptor*)gf_list_enum(odU->objectDescriptors, &i))) { + switch (desc->tag) { + case GF_ODF_OD_TAG: + case GF_ODF_IOD_TAG: + esd_list = ((GF_ObjectDescriptor *)desc)->ESDescriptors; break; + default: + continue; + } + j=0; + while ((esd = (GF_ESD*)gf_list_enum( esd_list, &j))){ + if (esd->ESID==track_id) { + the_od_id = ((GF_IsomObjectDescriptor*)desc)->objectDescriptorID; + break; + } + } + if (the_od_id) break; + } + gf_odf_com_del((GF_ODCom **)&odU); + if (the_od_id) break; + } + +err_exit: + gf_odf_codec_del(ODdecode); + if (e) return 0; + return the_od_id; +} + + +GF_EXPORT +u32 gf_isom_find_od_for_track(GF_ISOFile *file, u32 track) +{ + u32 i, j, di, the_od_id; + GF_TrackBox *od_tk; + GF_TrackBox *tk = gf_isom_get_track_from_file(file, track); + if (!tk) return 0; + + the_od_id = 0; + i=0; + while ( (od_tk = (GF_TrackBox*)gf_list_enum(file->moov->trackList, &i))) { + if (od_tk->Media->handler->handlerType != GF_ISOM_MEDIA_OD) continue; + + for (j=0; jMedia->information->sampleTable->SampleSize->sampleCount; j++) { + GF_ISOSample *samp = gf_isom_get_sample(file, i, j+1, &di); + the_od_id = Media_FindOD_ID(od_tk->Media, samp, tk->Header->trackID); + gf_isom_sample_del(&samp); + if (the_od_id) return the_od_id; + } + } + return 0; +} diff --git a/src/isomedia/meta.c b/src/isomedia/meta.c new file mode 100644 index 0000000..c51b920 --- /dev/null +++ b/src/isomedia/meta.c @@ -0,0 +1,561 @@ +/* + * GPAC Multimedia Framework + * + * Authors: Cyril Concolato - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include + + +GF_MetaBox *gf_isom_get_meta(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + GF_TrackBox *tk; + if (!file) return NULL; + if (root_meta) return file->meta; + if (!track_num) return file->moov ? file->moov->meta : NULL; + + tk = (GF_TrackBox*)gf_list_get(file->moov->trackList, track_num-1); + return tk ? tk->meta : NULL; +} + +GF_EXPORT +u32 gf_isom_get_meta_type(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return 0; + if (!meta->handler) return 0; + return meta->handler->handlerType; +} + +GF_EXPORT +u32 gf_isom_has_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + u32 i, count; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return 0; + + count = gf_list_count(meta->other_boxes); + for (i=0; iother_boxes, i); + if (a->type == GF_ISOM_BOX_TYPE_XML) return 1; + if (a->type == GF_ISOM_BOX_TYPE_BXML) return 2; + } + return 0; +} + +GF_Err gf_isom_extract_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, char *outName, Bool *is_binary) +{ + u32 i, count; + FILE *didfile; + GF_XMLBox *xml = NULL; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return GF_BAD_PARAM; + + /*Find XMLBox*/ + count = gf_list_count(meta->other_boxes); + for (i = 0; i other_boxes, i); + if ((a->type == GF_ISOM_BOX_TYPE_XML) || (a->type == GF_ISOM_BOX_TYPE_BXML) ) { + xml = (GF_XMLBox *)a; + break; + } + } + if (!xml || !xml->xml || !xml->xml_length) return GF_BAD_PARAM; + + didfile = gf_f64_open(outName, "wt"); + if (!didfile) return GF_IO_ERR; + fwrite(xml->xml, xml->xml_length, 1, didfile); + fclose(didfile); + + if (is_binary) *is_binary = (xml->type==GF_ISOM_BOX_TYPE_BXML) ? 1 : 0; + return GF_OK; +} + + + +GF_EXPORT +u32 gf_isom_get_meta_item_count(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->item_infos || !meta->item_locations) return 0; + return gf_list_count(meta->item_infos->item_infos); +} + +GF_EXPORT +GF_Err gf_isom_get_meta_item_info(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num, + u32 *itemID, u32 *protection_idx, Bool *is_self_reference, + const char **item_name, const char **item_mime_type, const char **item_encoding, + const char **item_url, const char **item_urn) +{ + GF_ItemInfoEntryBox *iinf; + u32 i, count; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; + + iinf = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); + if (!iinf) return GF_BAD_PARAM; + + if (itemID) (*itemID) = iinf->item_ID; + if (protection_idx) (*protection_idx) = iinf->item_protection_index; + if (item_name) (*item_name) = iinf->item_name; + if (item_mime_type) (*item_mime_type) = iinf->content_type; + if (item_encoding) (*item_encoding) = iinf->content_encoding; + if (is_self_reference) *is_self_reference = 0; + + if (item_url) (*item_url) = NULL; + if (item_urn) (*item_urn) = NULL; + + count = gf_list_count(meta->item_locations->location_entries); + for (i=0; iitem_locations->location_entries, i); + if (iloc->item_ID==iinf->item_ID) { + if (iloc->data_reference_index) { + GF_Box *a = (GF_Box *)gf_list_get(meta->file_locations->dref->boxList, iloc->data_reference_index-1); + if (a->type==GF_ISOM_BOX_TYPE_URL) { + if (item_url) (*item_url) = ((GF_DataEntryURLBox*)a)->location; + } else if (a->type==GF_ISOM_BOX_TYPE_URN) { + if (item_url) (*item_url) = ((GF_DataEntryURNBox*)a)->location; + if (item_urn) (*item_urn) = ((GF_DataEntryURNBox*)a)->nameURN; + } + break; + } else if (is_self_reference && !iloc->base_offset) { + GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); + if (!entry->extent_length && !entry->original_extent_offset) + *is_self_reference = 1; + } + } + } + return GF_OK; +} + +GF_EXPORT +u32 gf_isom_get_meta_item_by_id(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_ID) +{ + GF_ItemInfoEntryBox *iinf; + u32 i, count; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->item_infos || !meta->item_locations) return 0; + count = gf_list_count(meta->item_infos->item_infos); + for (i=0; iitem_infos->item_infos, i); + if (iinf->item_ID==item_ID) return i+1; + } + return 0; +} + +GF_Err gf_isom_extract_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, const char *dump_file_name) +{ + char szPath[1024]; + GF_ItemExtentEntry *extent_entry; + FILE *resource = NULL; + u32 i, count; + GF_ItemLocationEntry *location_entry; + u32 item_num; + char *item_name = NULL; + + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; + + item_num = gf_isom_get_meta_item_by_id(file, root_meta, track_num, item_id); + if (item_num) { + GF_ItemInfoEntryBox *item_entry = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); + item_name = item_entry->item_name; + } + + location_entry = NULL; + count = gf_list_count(meta->item_locations->location_entries); + for (i=0; iitem_locations->location_entries, i); + if (location_entry->item_ID == item_id) break; + location_entry = NULL; + } + + if (!location_entry) return GF_BAD_PARAM; + /*FIXME*/ + if (location_entry->data_reference_index) { + char *item_url = NULL, *item_urn = NULL; + GF_Box *a = (GF_Box *)gf_list_get(meta->file_locations->dref->boxList, location_entry->data_reference_index-1); + if (a->type==GF_ISOM_BOX_TYPE_URL) { + item_url = ((GF_DataEntryURLBox*)a)->location; + } else if (a->type==GF_ISOM_BOX_TYPE_URN) { + item_url = ((GF_DataEntryURNBox*)a)->location; + item_urn = ((GF_DataEntryURNBox*)a)->nameURN; + } + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[IsoMedia] Item already outside the ISO file at URL: %s, URN: %s\n", (item_url?item_url:"N/A"), (item_urn?item_urn:"N/A") )); + return GF_OK; + } + + /*don't extract self-reference item*/ + count = gf_list_count(location_entry->extent_entries); + if (!location_entry->base_offset && (count==1)) { + extent_entry = (GF_ItemExtentEntry *)gf_list_get(location_entry->extent_entries, 0); + if (!extent_entry->extent_length && !extent_entry->original_extent_offset) return GF_BAD_PARAM; + } + + if (dump_file_name) { + strcpy(szPath, dump_file_name); + } else { + if (item_name) strcpy(szPath, item_name); + else sprintf(szPath, "item_id%02d", item_id); + } + resource = gf_f64_open(szPath, "wb"); + + for (i=0; iextent_entries, i); + gf_bs_seek(file->movieFileMap->bs, /*location_entry->base_offset +*/ extent_entry->extent_offset); + + remain = extent_entry->extent_length; + while (remain) { + u32 cache_size = (remain>4096) ? 4096 : (u32) remain; + gf_bs_read_data(file->movieFileMap->bs, buf_cache, cache_size); + fwrite(buf_cache, 1, cache_size, resource); + remain -= cache_size; + } + } + fclose(resource); + return GF_OK; +} + +u32 gf_isom_get_meta_primary_item_id(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->primary_resource) return 0; + return meta->primary_resource->item_ID; +} + + +#ifndef GPAC_READ_ONLY + + +GF_Err gf_isom_set_meta_type(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 metaType) +{ + char szName[20]; + GF_MetaBox *meta; + + GF_Err e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) { + if (!metaType) return GF_OK; + meta = (GF_MetaBox *) meta_New(); + if (root_meta) { + file->meta = meta; + gf_list_add(file->TopBoxes, meta); + } else { + gf_isom_insert_moov(file); + if (!track_num) { + file->moov->meta = meta; + } else { + GF_TrackBox *tk = (GF_TrackBox *)gf_list_get(file->moov->trackList, track_num-1); + if (!tk) { + gf_isom_box_del((GF_Box *)meta); + return GF_BAD_PARAM; + } + tk->meta = meta; + } + } + } else if (!metaType) { + if (root_meta) { + gf_list_del_item(file->TopBoxes, meta); + gf_isom_box_del((GF_Box *)file->meta); + file->meta = NULL; + } else if (file->moov) { + if (!track_num) { + gf_isom_box_del((GF_Box *)file->moov->meta); + file->moov->meta = NULL; + } else { + GF_TrackBox *tk = (GF_TrackBox *)gf_list_get(file->moov->trackList, track_num-1); + if (!tk) return GF_BAD_PARAM; + gf_isom_box_del((GF_Box *)tk->meta); + tk->meta = NULL; + } + } + return GF_OK; + } + + if (!meta->handler) + meta->handler = (GF_HandlerBox *)hdlr_New(); + + if (meta->handler->nameUTF8) free(meta->handler->nameUTF8); + meta->handler->handlerType = metaType; + sprintf(szName, "GPAC %s Handler", gf_4cc_to_str(metaType)); + meta->handler->nameUTF8 = strdup(szName); + return GF_OK; +} + +GF_Err gf_isom_remove_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + u32 i; + GF_Box *a; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return GF_BAD_PARAM; + i=0; + while ((a = (GF_Box*)gf_list_enum(meta->other_boxes, &i))) { + switch (a->type) { + case GF_ISOM_BOX_TYPE_XML: + case GF_ISOM_BOX_TYPE_BXML: + gf_list_rem(meta->other_boxes, i-1); + gf_isom_box_del(a); + return GF_OK; + } + } + return GF_OK; +} + +GF_Err gf_isom_set_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, char *XMLFileName, Bool IsBinaryXML) +{ + GF_Err e; + FILE *xmlfile; + GF_XMLBox *xml; + GF_MetaBox *meta; + + e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return GF_BAD_PARAM; + + e = gf_isom_remove_meta_xml(file, root_meta, track_num); + if (e) return e; + + xml = (GF_XMLBox *)xml_New(); + if (!xml) return GF_OUT_OF_MEM; + gf_list_add(meta->other_boxes, xml); + if (IsBinaryXML) xml->type = GF_ISOM_BOX_TYPE_BXML; + + + /*assume 32bit max size = 4Go should be sufficient for a DID!!*/ + xmlfile = fopen(XMLFileName, "rb"); + if (!xmlfile) return GF_URL_ERROR; + fseek(xmlfile, 0, SEEK_END); + xml->xml_length = ftell(xmlfile); + fseek(xmlfile, 0, SEEK_SET); + xml->xml = (char*)malloc(sizeof(unsigned char)*xml->xml_length); + xml->xml_length = fread(xml->xml, 1, sizeof(unsigned char)*xml->xml_length, xmlfile); + if (ferror(xmlfile)) { + free(xml->xml); + xml->xml = NULL; + return GF_BAD_PARAM; + } + fclose(xmlfile); + return GF_OK; +} + + +GF_Err gf_isom_add_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, const char *mime_type, const char *content_encoding, const char *URL, const char *URN) +{ + GF_Err e; + GF_ItemLocationEntry *location_entry; + GF_ItemInfoEntryBox *infe; + GF_MetaBox *meta; + u32 lastItemID = 0; + + if (!self_reference && !item_name && !resource_path) return GF_BAD_PARAM; + e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE); + if (e) return e; + meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return GF_BAD_PARAM; + + e = FlushCaptureMode(file); + if (e) return e; + + /*check file exists */ + if (!URN && !URL && !self_reference) { + FILE *src = fopen(resource_path, "rb"); + if (!src) return GF_URL_ERROR; + fclose(src); + } + + if (meta->item_infos) { + u32 i; + u32 item_count = gf_list_count(meta->item_infos->item_infos); + for (i = 0; i < item_count; i++) { + GF_ItemInfoEntryBox *e= (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, i); + if (e->item_ID > lastItemID) lastItemID = e->item_ID; + } + } + + infe = (GF_ItemInfoEntryBox *)infe_New(); + infe->item_ID = ++lastItemID; + + /*get relative name*/ + if (item_name) { + infe->item_name = strdup(item_name); + } else if (resource_path) { + if (strrchr(resource_path, GF_PATH_SEPARATOR)) { + infe->item_name = strdup(strrchr(resource_path, GF_PATH_SEPARATOR) + 1); + } else { + infe->item_name = strdup(resource_path); + } + } + + if (mime_type) { + infe->content_type = strdup(mime_type); + } else { + infe->content_type = strdup("application/octet-stream"); + } + if (content_encoding) infe->content_encoding = strdup(content_encoding); + + /*Creation of the ItemLocation */ + location_entry = (GF_ItemLocationEntry*)malloc(sizeof(GF_ItemLocationEntry)); + if (!location_entry) { + gf_isom_box_del((GF_Box *)infe); + return GF_OUT_OF_MEM; + } + memset(location_entry, 0, sizeof(GF_ItemLocationEntry)); + location_entry->extent_entries = gf_list_new(); + + /*Creates an mdat if it does not exist*/ + if (!file->mdat) { + file->mdat = (GF_MediaDataBox *)mdat_New(); + gf_list_add(file->TopBoxes, file->mdat); + } + + /*Creation an ItemLocation Box if it does not exist*/ + if (!meta->item_locations) meta->item_locations = (GF_ItemLocationBox *)iloc_New(); + gf_list_add(meta->item_locations->location_entries, location_entry); + location_entry->item_ID = lastItemID; + + if (!meta->item_infos) meta->item_infos = (GF_ItemInfoBox *) iinf_New(); + e = gf_list_add(meta->item_infos->item_infos, infe); + if (e) return e; + + /*0: the current file*/ + location_entry->data_reference_index = 0; + if (self_reference) { + GF_ItemExtentEntry *entry; + GF_SAFEALLOC(entry, GF_ItemExtentEntry); + gf_list_add(location_entry->extent_entries, entry); + if (!infe->item_name) infe->item_name = strdup(""); + return GF_OK; + } + + /*file not copied, just referenced*/ + if (URL || URN) { + u32 dataRefIndex; + if (!meta->file_locations) meta->file_locations = (GF_DataInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DINF); + if (!meta->file_locations->dref) meta->file_locations->dref = (GF_DataReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DREF); + e = Media_FindDataRef(meta->file_locations->dref, (char *) URL, (char *) URN, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(meta->file_locations->dref, (char *) URL, (char *) URN, &dataRefIndex); + if (e) return e; + } + location_entry->data_reference_index = dataRefIndex; + } + + /*capture mode, write to disk*/ + if ((file->openMode == GF_ISOM_OPEN_WRITE) && !location_entry->data_reference_index) { + FILE *src; + GF_ItemExtentEntry *entry; + GF_SAFEALLOC(entry, GF_ItemExtentEntry); + + location_entry->base_offset = gf_bs_get_position(file->editFileMap->bs); + + /*update base offset size*/ + if (location_entry->base_offset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8; + else if (location_entry->base_offset && !meta->item_locations->base_offset_size) meta->item_locations->base_offset_size = 4; + + entry->extent_length = 0; + entry->extent_offset = 0; + gf_list_add(location_entry->extent_entries, entry); + + src = gf_f64_open(resource_path, "rb"); + if (src) { + char cache_data[4096]; + u64 remain; + gf_f64_seek(src, 0, SEEK_END); + entry->extent_length = gf_f64_tell(src); + gf_f64_seek(src, 0, SEEK_SET); + + remain = entry->extent_length; + while (remain) { + u32 size_cache = (remain>4096) ? 4096 : (u32) remain; + fread(cache_data, 1, size_cache, src); + gf_bs_write_data(file->editFileMap->bs, cache_data, size_cache); + remain -= size_cache; + } + fclose(src); + + /*update length size*/ + if (entry->extent_length>0xFFFFFFFF) meta->item_locations->length_size = 8; + else if (entry->extent_length && !meta->item_locations->length_size) meta->item_locations->length_size = 4; + } + } + /*store full path for info*/ + else if (!location_entry->data_reference_index) { + infe->full_path = strdup(resource_path); + } + return GF_OK; +} + + +GF_Err gf_isom_remove_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id) +{ + GF_ItemInfoEntryBox *iinf; + u32 i, count; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + u32 item_num; + if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; + + item_num = gf_isom_get_meta_item_by_id(file, root_meta, track_num, item_id); + if (!item_num) return GF_BAD_PARAM; + iinf = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); + gf_list_rem(meta->item_infos->item_infos, item_num-1); + + count = gf_list_count(meta->item_locations->location_entries); + for (i=0; iitem_locations->location_entries, i); + if (iloc->item_ID==iinf->item_ID) { + /*FIXME: remove data ref...*/ + if (iloc->data_reference_index) { } + + gf_list_rem(meta->item_locations->location_entries, i); + gf_isom_box_del((GF_Box *)iloc); + break; + } + } + gf_isom_box_del((GF_Box *)iinf); + return GF_OK; +} + +GF_Err gf_isom_set_meta_primary_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id) +{ + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; + /*either one or the other*/ + if (gf_isom_has_meta_xml(file, root_meta, track_num)) return GF_BAD_PARAM; + + if (meta->primary_resource) gf_isom_box_del((GF_Box*)meta->primary_resource); + meta->primary_resource = (GF_PrimaryItemBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_PITM); + meta->primary_resource->item_ID = item_id; + return GF_OK; +} + + +#endif //GPAC_READ_ONLY + + diff --git a/src/isomedia/movie_fragments.c b/src/isomedia/movie_fragments.c new file mode 100644 index 0000000..b6099aa --- /dev/null +++ b/src/isomedia/movie_fragments.c @@ -0,0 +1,778 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +GF_TrackExtendsBox *GetTrex(GF_MovieBox *moov, u32 TrackID) +{ + u32 i; + GF_TrackExtendsBox *trex; + i=0; + while ((trex = (GF_TrackExtendsBox *)gf_list_enum(moov->mvex->TrackExList, &i))) { + if (trex->trackID == TrackID) return trex; + } + return NULL; +} + + +#ifndef GPAC_READ_ONLY + +GF_TrackFragmentBox *GetTraf(GF_ISOFile *mov, u32 TrackID) +{ + u32 i; + GF_TrackFragmentBox *traf; + if (!mov->moof) return NULL; + + //reverse browse the TRAFs, as there may be more than one per track ... + for (i=gf_list_count(mov->moof->TrackList); i>0; i--) { + traf = (GF_TrackFragmentBox *)gf_list_get(mov->moof->TrackList, i-1); + if (traf->tfhd->trackID == TrackID) return traf; + } + return NULL; +} + +GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *movie) +{ + GF_Err e; + u32 i; + GF_TrackExtendsBox *trex; + if (!movie || !movie->moov) return GF_BAD_PARAM; + //this is only allowed in write mode + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + + if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_OK; + movie->FragmentsFlags = 0; + + //update durations + gf_isom_get_duration(movie); + + //write movie + e = WriteToFile(movie); + if (e) return e; + + //make sure we do have all we need. If not this is not an error, just consider + //the file closed + if (!movie->moov->mvex || !gf_list_count(movie->moov->mvex->TrackExList)) return GF_OK; + + i=0; + while ((trex = (GF_TrackExtendsBox *)gf_list_enum(movie->moov->mvex->TrackExList, &i))) { + if (!trex->trackID || !gf_isom_get_track_from_id(movie->moov, trex->trackID)) return GF_IO_ERR; + //we could also check all our data refs are local but we'll do that at run time + //in order to allow a mix of both (remote refs in MOOV and local in MVEX) + + //one thing that MUST be done is OD cross-dependancies. The movie fragment spec + //is broken here, since it cannot allow dynamic insertion of new ESD and their + //dependancies + } + + //ok we are fine - note the data map is created at the begining + if (i) movie->FragmentsFlags |= GF_ISOM_FRAG_WRITE_READY; + movie->NextMoofNumber = 1; + return GF_OK; +} + + +GF_Err gf_isom_setup_track_fragment(GF_ISOFile *movie, u32 TrackID, + u32 DefaultSampleDescriptionIndex, + u32 DefaultSampleDuration, + u32 DefaultSampleSize, + u8 DefaultSampleIsSync, + u8 DefaultSamplePadding, + u16 DefaultDegradationPriority) +{ + GF_MovieExtendsBox *mvex; + GF_TrackExtendsBox *trex; + GF_TrackBox *trak; + + if (!movie || !movie->moov) return GF_BAD_PARAM; + //this is only allowed in write mode + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + //and only at setup + if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_BAD_PARAM; + + + trak = gf_isom_get_track_from_id(movie->moov, TrackID); + if (!trak) return GF_BAD_PARAM; + + //create MVEX if needed + if (!movie->moov->mvex) { + mvex = (GF_MovieExtendsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MVEX); + moov_AddBox((GF_Box*)movie->moov, (GF_Box *) mvex); + } else { + mvex = movie->moov->mvex; + } + + trex = GetTrex(movie->moov, TrackID); + if (!trex) { + trex = (GF_TrackExtendsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREX); + trex->trackID = TrackID; + mvex_AddBox((GF_Box*)mvex, (GF_Box *) trex); + } + trex->track = trak; + trex->def_sample_desc_index = DefaultSampleDescriptionIndex; + trex->def_sample_duration = DefaultSampleDuration; + trex->def_sample_size = DefaultSampleSize; + trex->def_sample_flags = GF_ISOM_FORMAT_FRAG_FLAGS(DefaultSamplePadding, DefaultSampleIsSync, DefaultDegradationPriority); + + return GF_OK; +} + + +u32 GetNumUsedValues(GF_TrackFragmentBox *traf, u32 value, u32 index) +{ + u32 i, j, NumValue = 0; + GF_TrackFragmentRunBox *trun; + GF_TrunEntry *ent; + + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + j=0; + while ((ent = (GF_TrunEntry *)gf_list_enum(trun->entries, &j))) { + switch (index) { + case 1: + if (value == ent->Duration) NumValue ++; + break; + case 2: + if (value == ent->size) NumValue ++; + break; + case 3: + if (value == ent->flags) NumValue ++; + break; + } + } + } + return NumValue; +} + + +void ComputeFragmentDefaults(GF_TrackFragmentBox *traf) +{ + u32 i, j, MaxNum, DefValue, ret; + GF_TrackFragmentRunBox *trun; + GF_TrunEntry *ent; + + //Duration default + MaxNum = DefValue = 0; + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + j=0; + while ((ent = (GF_TrunEntry *)gf_list_enum(trun->entries, &j))) { + ret = GetNumUsedValues(traf, ent->Duration, 1); + if (ret>MaxNum) { + //at least 2 duration, specify for all + if (MaxNum) { + DefValue = 0; + goto escape_duration; + } + MaxNum = ret; + DefValue = ent->Duration; + } + } + } +escape_duration: + //store if # + if (DefValue && (DefValue != traf->trex->def_sample_duration)) { + traf->tfhd->def_sample_duration = DefValue; + } + + //Size default + MaxNum = DefValue = 0; + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + j=0; + while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) { + ret = GetNumUsedValues(traf, ent->size, 2); + if (ret>MaxNum || (ret==1)) { + //at least 2 sizes so we must specify all sizes + if (MaxNum) { + DefValue = 0; + goto escape_size; + } + MaxNum = ret; + DefValue = ent->size; + } + } + } + +escape_size: + //store if # + if (DefValue && (DefValue != traf->trex->def_sample_size)) { + traf->tfhd->def_sample_size = DefValue; + } + + //Flags default + MaxNum = DefValue = 0; + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + j=0; + while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) { + ret = GetNumUsedValues(traf, ent->flags, 3); + if (ret>MaxNum) { + MaxNum = ret; + DefValue = ent->flags; + } + } + } + //store if # + if (DefValue && (DefValue != traf->trex->def_sample_flags)) { + traf->tfhd->def_sample_flags = DefValue; + } +} + + +GF_Err gf_isom_set_fragment_option(GF_ISOFile *movie, u32 TrackID, u32 Code, u32 Param) +{ + GF_TrackFragmentBox *traf; + if (!movie || !movie->moov) return GF_BAD_PARAM; + //this is only allowed in write mode + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + + traf = GetTraf(movie, TrackID); + if (!traf) return GF_BAD_PARAM; + switch (Code) { + case GF_ISOM_TRAF_EMPTY: + traf->tfhd->EmptyDuration = Param; + break; + case GF_ISOM_TRAF_RANDOM_ACCESS: + traf->tfhd->IFrameSwitching = Param; + break; + case GF_ISOM_TRAF_DATA_CACHE: + //don't cache only one sample ... + traf->DataCache = Param > 1 ? Param : 0; + break; + } + return GF_OK; +} + + +u32 UpdateRuns(GF_TrackFragmentBox *traf) +{ + u32 sampleCount, i, j, RunSize, UseDefaultSize, RunDur, UseDefaultDur, RunFlags, NeedFlags, UseDefaultFlag, UseCTS, count; + GF_TrackFragmentRunBox *trun; + GF_TrunEntry *ent, *first_ent; + + sampleCount = 0; + + + //traf data offset - we ALWAYS use data offset indication when writting otherwise + //we would need to have one TRUN max in a TRAF for offset reconstruction or store + //all TRUN in memory before writting :( Anyway it is much safer to indicate the + //base offset of each traf rather than using offset aggregation rules specified + //in the std + traf->tfhd->flags = GF_ISOM_TRAF_BASE_OFFSET; + + //empty runs + if (traf->tfhd->EmptyDuration) { + while (gf_list_count(traf->TrackRuns)) { + trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, 0); + gf_list_rem(traf->TrackRuns, 0); + gf_isom_box_del((GF_Box *)trun); + } + traf->tfhd->flags = GF_ISOM_TRAF_DUR_EMPTY; + if (traf->tfhd->EmptyDuration != traf->trex->def_sample_duration) { + traf->tfhd->def_sample_duration = traf->tfhd->EmptyDuration; + traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR; + } + return 0; + } + + + UseDefaultSize = 0; + UseDefaultDur = 0; + UseDefaultFlag = 0; + + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + RunSize = 0; + RunDur = 0; + RunFlags = 0; + UseCTS = 0; + NeedFlags = 0; + + first_ent = NULL; + //process all samples in run + count = gf_list_count(trun->entries); + for (j=0; jentries, j); + if (!j) { + first_ent = ent; + RunSize = ent->size; + RunDur = ent->Duration; + } + //we may have one entry only ... + if (j || (count==1)) { + //flags are only after first entry + if (j==1 || (count==1) ) RunFlags = ent->flags; + + if (ent->size != RunSize) RunSize = 0; + if (ent->Duration != RunDur) RunDur = 0; + if (j && (RunFlags != ent->flags)) NeedFlags = 1; + } + if (ent->CTS_Offset) UseCTS = 1; + } + //empty list + if (!first_ent) { + i--; + gf_list_rem(traf->TrackRuns, i); + continue; + } + trun->sample_count = gf_list_count(trun->entries); + trun->flags = 0; + + //size checking + //constant size, check if this is from current fragment default or global default + if (RunSize && (traf->trex->def_sample_size == RunSize)) { + if (!UseDefaultSize) UseDefaultSize = 2; + else if (UseDefaultSize==1) RunSize = 0; + } else if (RunSize && (traf->tfhd->def_sample_size == RunSize)) { + if (!UseDefaultSize) UseDefaultSize = 1; + else if (UseDefaultSize==2) RunSize = 0; + } + //we could check for single entry runs and set the default size in the tfhd but + //that's no bit saving... + else { + RunSize=0; + } + + if (!RunSize) trun->flags |= GF_ISOM_TRUN_SIZE; + + //duration checking + if (RunDur && (traf->trex->def_sample_duration == RunDur)) { + if (!UseDefaultDur) UseDefaultDur = 2; + else if (UseDefaultDur==1) RunDur = 0; + } else if (RunDur && (traf->tfhd->def_sample_duration == RunDur)) { + if (!UseDefaultDur) UseDefaultDur = 1; + else if (UseDefaultDur==2) RunDur = 0; + } + if (!RunDur) trun->flags |= GF_ISOM_TRUN_DURATION; + + //flag checking + if (!NeedFlags) { + if (RunFlags == traf->trex->def_sample_flags) { + if (!UseDefaultFlag) UseDefaultFlag = 2; + else if (UseDefaultFlag==1) NeedFlags = 1; + } else if (RunFlags == traf->tfhd->def_sample_flags) { + if (!UseDefaultFlag) UseDefaultFlag = 1; + else if(UseDefaultFlag==2) NeedFlags = 1; + } + } + if (NeedFlags) { + //one flags entry per sample only + trun->flags |= GF_ISOM_TRUN_FLAGS; + } else { + //indicated in global setup + if (first_ent->flags == traf->trex->def_sample_flags) { + if (!UseDefaultFlag) UseDefaultFlag = 2; + else if (UseDefaultFlag==1) trun->flags |= GF_ISOM_TRUN_FIRST_FLAG; + } + //indicated in local setup + else if (first_ent->flags == traf->tfhd->def_sample_flags) { + if (!UseDefaultFlag) UseDefaultFlag = 1; + else if (UseDefaultFlag==2) trun->flags |= GF_ISOM_TRUN_FIRST_FLAG; + } + //explicit store + else { + trun->flags |= GF_ISOM_TRUN_FIRST_FLAG; + } + } + + //CTS flag + if (UseCTS) trun->flags |= GF_ISOM_TRUN_CTS_OFFSET; + + //run data offset if the offset indicated is 0 (first sample in this MDAT) don't + //indicate it + if (trun->data_offset) trun->flags |= GF_ISOM_TRUN_DATA_OFFSET; + + sampleCount += trun->sample_count; + } + + //last update TRAF flags + if (UseDefaultSize==1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_SIZE; + if (UseDefaultDur==1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR; + if (UseDefaultFlag==1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_FLAGS; + if (traf->tfhd->sample_desc_index && traf->tfhd->sample_desc_index != traf->trex->def_sample_desc_index) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DESC; + + + return sampleCount; +} + + + +GF_Err StoreFragment(GF_ISOFile *movie) +{ + GF_Err e; + u64 moof_start; + u32 size, i, s_count; + char *buffer; + GF_TrackFragmentBox *traf; + GF_TrackFragmentRunBox *trun; + GF_BitStream *bs; + if (!movie->moof) return GF_OK; + + bs = movie->editFileMap->bs; + + //1- flush all caches + i=0; + while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) { + if (!traf->DataCache) continue; + s_count = gf_list_count(traf->TrackRuns); + if (!s_count) continue; + trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, s_count-1); + if (!trun->cache || !trun->sample_count) continue; + + //update offset + trun->data_offset = (u32) (gf_bs_get_position(movie->editFileMap->bs) - movie->current_top_box_start - 8); + //write cache + gf_bs_get_content(trun->cache, &buffer, &size); + gf_bs_write_data(movie->editFileMap->bs, buffer, size); + gf_bs_del(trun->cache); + free(buffer); + trun->cache = NULL; + } + //2- update MOOF MDAT header + moof_start = gf_bs_get_position(bs); + + //start of MDAT + gf_bs_seek(bs, movie->current_top_box_start); + //we assume we never write large MDATs in fragment mode which should always be true + size = (u32) (moof_start - movie->current_top_box_start); + gf_bs_write_u32(bs, (u32) size); + gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT); + gf_bs_seek(bs, moof_start); + + //3- clean our traf's + i=0; + while ((traf = (GF_TrackFragmentBox*) gf_list_enum(movie->moof->TrackList, &i))) { + //compute default settings for the TRAF + ComputeFragmentDefaults(traf); + //updates all trun and set all flags, INCLUDING TRAF FLAGS (durations, ...) + s_count = UpdateRuns(traf); + //empty fragment destroy it + if (!traf->tfhd->EmptyDuration && !s_count) { + i--; + gf_list_rem(movie->moof->TrackList, i); + gf_isom_box_del((GF_Box *) traf); + continue; + } + } + + //4- Write moof + e = gf_isom_box_size((GF_Box *) movie->moof); + if (e) return e; + e = gf_isom_box_write((GF_Box *) movie->moof, bs); + if (e) return e; + + //5- destroy our moof + gf_isom_box_del((GF_Box *) movie->moof); + movie->moof = NULL; + movie->NextMoofNumber ++; + return GF_OK; +} + + +GF_Err gf_isom_start_fragment(GF_ISOFile *movie) +{ + u32 i, count; + GF_TrackExtendsBox *trex; + GF_TrackFragmentBox *traf; + GF_Err e; + //and only at setup + if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + + count = gf_list_count(movie->moov->mvex->TrackExList); + if (!count) return GF_BAD_PARAM; + + //store fragment + if (movie->moof) { + e = StoreFragment(movie); + if (e) return e; + } + + //format MDAT + movie->current_top_box_start = gf_bs_get_position(movie->editFileMap->bs); + gf_bs_write_u32(movie->editFileMap->bs, 0); + gf_bs_write_u32(movie->editFileMap->bs, GF_ISOM_BOX_TYPE_MDAT); + + //create new fragment + movie->moof = (GF_MovieFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MOOF); + movie->moof->mfhd = (GF_MovieFragmentHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MFHD); + movie->moof->mfhd->sequence_number = movie->NextMoofNumber; + + //we create a TRAF for each setup track, unused ones will be removed at store time + for (i=0; imoov->mvex->TrackExList, i); + traf = (GF_TrackFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAF); + traf->trex = trex; + traf->tfhd = (GF_TrackFragmentHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TFHD); + traf->tfhd->trackID = trex->trackID; + //add 8 bytes (MDAT size+type) to avoid the data_offset in the first trun + traf->tfhd->base_data_offset = movie->current_top_box_start + 8; + gf_list_add(movie->moof->TrackList, traf); + } + return GF_OK; +} + +u32 GetRunSize(GF_TrackFragmentRunBox *trun) +{ + u32 i, size; + GF_TrunEntry *ent; + size = 0; + i=0; + while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &i))) { + size += ent->size; + } + return size; +} + + +GF_Err gf_isom_fragment_add_sample(GF_ISOFile *movie, u32 TrackID, GF_ISOSample *sample, u32 DescIndex, + u32 Duration, + u8 PaddingBits, u16 DegradationPriority) +{ + u32 count, buffer_size; + char *buffer; + u64 pos; + GF_ISOSample *od_sample = NULL; + GF_TrunEntry *ent; + GF_TrackFragmentBox *traf, *traf_2; + GF_TrackFragmentRunBox *trun; + if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) || !sample) return GF_BAD_PARAM; + + traf = GetTraf(movie, TrackID); + if (!traf) return GF_BAD_PARAM; + + if (!traf->tfhd->sample_desc_index) traf->tfhd->sample_desc_index = DescIndex ? DescIndex : traf->trex->def_sample_desc_index; + + pos = gf_bs_get_position(movie->editFileMap->bs); + + + //WARNING: we change stream description, create a new TRAF + if ( DescIndex && (traf->tfhd->sample_desc_index != DescIndex)) { + //if we're caching flush the current run + if (traf->DataCache) { + count = gf_list_count(traf->TrackRuns); + if (count) { + trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1); + trun->data_offset = (u32) (pos - movie->current_top_box_start - 8); + gf_bs_get_content(trun->cache, &buffer, &buffer_size); + gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size); + gf_bs_del(trun->cache); + trun->cache = NULL; + free(buffer); + } + } + traf_2 = (GF_TrackFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAF); + traf_2->trex = traf->trex; + traf_2->tfhd = (GF_TrackFragmentHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TFHD); + traf_2->tfhd->trackID = traf->tfhd->trackID; + //keep the same offset + traf_2->tfhd->base_data_offset = movie->current_top_box_start + 8; + gf_list_add(movie->moof->TrackList, traf_2); + + //duplicate infos + traf_2->tfhd->IFrameSwitching = traf->tfhd->IFrameSwitching; + traf_2->DataCache = traf->DataCache; + traf_2->tfhd->sample_desc_index = DescIndex; + + //switch them ... + traf = traf_2; + } + + + //add TRUN entry + count = gf_list_count(traf->TrackRuns); + if (count) { + trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1); + //check data offset when no caching as trun entries shall ALWAYS be contiguous samples + if (!traf->DataCache && (movie->current_top_box_start + 8 + trun->data_offset + GetRunSize(trun) != pos) ) + count = 0; + + //check I-frame detection + if (traf->tfhd->IFrameSwitching && sample->IsRAP) + count = 0; + + if (traf->DataCache && (traf->DataCache==trun->sample_count) ) + count = 0; + + //if data cache is on and we're changing TRUN, store the cache and update data offset + if (!count && traf->DataCache) { + trun->data_offset = (u32) (pos - movie->current_top_box_start - 8); + gf_bs_get_content(trun->cache, &buffer, &buffer_size); + gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size); + gf_bs_del(trun->cache); + trun->cache = NULL; + free(buffer); + } + } + + //new run + if (!count) { + trun = (GF_TrackFragmentRunBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRUN); + //store data offset (we have the 8 btyes offset of the MDAT) + trun->data_offset = (u32) (pos - movie->current_top_box_start - 8); + gf_list_add(traf->TrackRuns, trun); + + //if we use data caching, create a bitstream + if (traf->DataCache) + trun->cache = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + } + + GF_SAFEALLOC(ent, GF_TrunEntry); + ent->CTS_Offset = sample->CTS_Offset; + ent->Duration = Duration; + ent->size = sample->dataLength; + ent->flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, sample->IsRAP, DegradationPriority); + gf_list_add(trun->entries, ent); + + trun->sample_count += 1; + + //rewrite OD frames + if (traf->trex->track->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + //this may fail if depandancies are not well done ... + Media_ParseODFrame(traf->trex->track->Media, sample, &od_sample); + sample = od_sample; + } + + //finally write the data + if (!traf->DataCache) { + gf_bs_write_data(movie->editFileMap->bs, sample->data, sample->dataLength); + } else if (trun->cache) { + gf_bs_write_data(trun->cache, sample->data, sample->dataLength); + } else { + return GF_BAD_PARAM; + } + if (od_sample) gf_isom_sample_del(&od_sample); + return GF_OK; +} + + + +GF_Err gf_isom_fragment_append_data(GF_ISOFile *movie, u32 TrackID, char *data, u32 data_size, u8 PaddingBits) +{ + u32 count; + u8 rap; + u16 degp; + GF_TrunEntry *ent; + GF_TrackFragmentBox *traf; + GF_TrackFragmentRunBox *trun; + if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; + + traf = GetTraf(movie, TrackID); + if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM; + + //add TRUN entry + count = gf_list_count(traf->TrackRuns); + if (!count) return GF_BAD_PARAM; + + trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1); + count = gf_list_count(trun->entries); + if (!count) return GF_BAD_PARAM; + ent = (GF_TrunEntry *)gf_list_get(trun->entries, count-1); + ent->size += data_size; + + rap = GF_ISOM_GET_FRAG_SYNC(ent->flags); + degp = GF_ISOM_GET_FRAG_DEG(ent->flags); + ent->flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, rap, degp); + + //finally write the data + if (!traf->DataCache) { + gf_bs_write_data(movie->editFileMap->bs, data, data_size); + } else if (trun->cache) { + gf_bs_write_data(trun->cache, data, data_size); + } else { + return GF_BAD_PARAM; + } + return GF_OK; +} + + +#endif //GPAC_READ_ONLY + +GF_EXPORT +u32 gf_isom_is_track_fragmented(GF_ISOFile *movie, u32 TrackID) +{ + if (!movie || !movie->moov || !movie->moov->mvex) return 0; + return (GetTrex(movie->moov, TrackID) != NULL) ? 1 : 0; +} + +GF_EXPORT +u32 gf_isom_is_fragmented(GF_ISOFile *movie) +{ + if (!movie || !movie->moov) return 0; + /*only check moof number (in read mode, mvex can be deleted on the fly)*/ + return movie->NextMoofNumber ? 1 : 0; +} + +#else + +GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *the_file) +{ + return GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_setup_track_fragment(GF_ISOFile *the_file, u32 TrackID, + u32 DefaultSampleDescriptionIndex, + u32 DefaultSampleDuration, + u32 DefaultSampleSize, + u8 DefaultSampleIsSync, + u8 DefaultSamplePadding, + u16 DefaultDegradationPriority) +{ + return GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_set_fragment_option(GF_ISOFile *the_file, u32 TrackID, u32 Code, u32 Param) +{ + return GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_start_fragment(GF_ISOFile *the_file) +{ + return GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_fragment_add_sample(GF_ISOFile *the_file, u32 TrackID, GF_ISOSample *sample, u32 DescIndex, + u32 Duration, + u8 PaddingBits, u16 DegradationPriority) +{ + return GF_NOT_SUPPORTED; +} + + +GF_EXPORT +u32 gf_isom_is_track_fragmented(GF_ISOFile *the_file, u32 TrackID) +{ + return 0; +} + +GF_EXPORT +u32 gf_isom_is_fragmented(GF_ISOFile *the_file) +{ + return 0; +} + +#endif diff --git a/src/isomedia/sample_descs.c b/src/isomedia/sample_descs.c new file mode 100644 index 0000000..f6644cc --- /dev/null +++ b/src/isomedia/sample_descs.c @@ -0,0 +1,481 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +void gf_isom_video_sample_entry_init(GF_VisualSampleEntryBox *ent) +{ + ent->horiz_res = ent->vert_res = 0x00480000; + ent->frames_per_sample = 1; + ent->bit_depth = 0x18; + ent->color_table_index = -1; +} + +GF_Err gf_isom_video_sample_entry_read(GF_VisualSampleEntryBox *ptr, GF_BitStream *bs) +{ + if (ptr->size < 78) return GF_ISOM_INVALID_FILE; + ptr->size -= 78; + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->version = gf_bs_read_u16(bs); + ptr->revision = gf_bs_read_u16(bs); + ptr->vendor = gf_bs_read_u32(bs); + ptr->temporal_quality = gf_bs_read_u32(bs); + ptr->spacial_quality = gf_bs_read_u32(bs); + ptr->Width = gf_bs_read_u16(bs); + ptr->Height = gf_bs_read_u16(bs); + ptr->horiz_res = gf_bs_read_u32(bs); + ptr->vert_res = gf_bs_read_u32(bs); + ptr->entry_data_size = gf_bs_read_u32(bs); + ptr->frames_per_sample = gf_bs_read_u16(bs); + gf_bs_read_data(bs, ptr->compressor_name, 32); + ptr->compressor_name[32] = 0; + ptr->bit_depth = gf_bs_read_u16(bs); + ptr->color_table_index = gf_bs_read_u16(bs); + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +void gf_isom_video_sample_entry_write(GF_VisualSampleEntryBox *ptr, GF_BitStream *bs) +{ + + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + + gf_bs_write_u16(bs, ptr->version); + gf_bs_write_u16(bs, ptr->revision); + gf_bs_write_u32(bs, ptr->vendor); + gf_bs_write_u32(bs, ptr->temporal_quality); + gf_bs_write_u32(bs, ptr->spacial_quality); + gf_bs_write_u16(bs, ptr->Width); + gf_bs_write_u16(bs, ptr->Height); + gf_bs_write_u32(bs, ptr->horiz_res); + gf_bs_write_u32(bs, ptr->vert_res); + gf_bs_write_u32(bs, ptr->entry_data_size); + gf_bs_write_u16(bs, ptr->frames_per_sample); + gf_bs_write_data(bs, ptr->compressor_name, 32); + gf_bs_write_u16(bs, ptr->bit_depth); + gf_bs_write_u16(bs, ptr->color_table_index); +} + +void gf_isom_video_sample_entry_size(GF_VisualSampleEntryBox *ent) +{ + ent->size += 78; +} + + +#endif + + + +void gf_isom_audio_sample_entry_init(GF_AudioSampleEntryBox *ptr) +{ + ptr->channel_count = 2; + ptr->bitspersample = 16; +} + +GF_Err gf_isom_audio_sample_entry_read(GF_AudioSampleEntryBox *ptr, GF_BitStream *bs) +{ + if (ptr->size<28) return GF_ISOM_INVALID_FILE; + + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->version = gf_bs_read_u16(bs); + ptr->revision = gf_bs_read_u16(bs); + ptr->vendor = gf_bs_read_u32(bs); + ptr->channel_count = gf_bs_read_u16(bs); + ptr->bitspersample = gf_bs_read_u16(bs); + ptr->compression_id = gf_bs_read_u16(bs); + ptr->packet_size = gf_bs_read_u16(bs); + ptr->samplerate_hi = gf_bs_read_u16(bs); + ptr->samplerate_lo = gf_bs_read_u16(bs); + + ptr->size -= 28; + if (ptr->version==1) { + if (ptr->size<16) return GF_ISOM_INVALID_FILE; + gf_bs_skip_bytes(bs, 16); + ptr->size-=16; + } else if (ptr->version==2) { + if (ptr->size<36) return GF_ISOM_INVALID_FILE; + gf_bs_skip_bytes(bs, 36); + ptr->size -= 36; + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY + +void gf_isom_audio_sample_entry_write(GF_AudioSampleEntryBox *ptr, GF_BitStream *bs) +{ + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + + gf_bs_write_u16(bs, ptr->version); + gf_bs_write_u16(bs, ptr->revision); + gf_bs_write_u32(bs, ptr->vendor); + gf_bs_write_u16(bs, ptr->channel_count); + gf_bs_write_u16(bs, ptr->bitspersample); + gf_bs_write_u16(bs, ptr->compression_id); + gf_bs_write_u16(bs, ptr->packet_size); + gf_bs_write_u16(bs, ptr->samplerate_hi); + gf_bs_write_u16(bs, ptr->samplerate_lo); +} + +void gf_isom_audio_sample_entry_size(GF_AudioSampleEntryBox *ptr) +{ + ptr->size += 28; +} + + +#endif //GPAC_READ_ONLY + + + +GF_EXPORT +GF_3GPConfig *gf_isom_3gp_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_3GPConfig *config, *res; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !StreamDescriptionIndex) return NULL; + + config = NULL; + entry = (GF_SampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex-1); + if (!entry) return NULL; + switch (entry->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + if (! ((GF_3GPPAudioSampleEntryBox*)entry)->info) return NULL; + config = & ((GF_3GPPAudioSampleEntryBox*)entry)->info->cfg; + break; + case GF_ISOM_SUBTYPE_3GP_H263: + if (! ((GF_3GPPVisualSampleEntryBox*)entry)->info) return NULL; + config = & ((GF_3GPPVisualSampleEntryBox*)entry)->info->cfg; + break; + default: + return NULL; + } + if (!config) return NULL; + + res = (GF_3GPConfig*)malloc(sizeof(GF_3GPConfig)); + memcpy(res, config, sizeof(GF_3GPConfig)); + return res; +} + +#ifndef GPAC_READ_ONLY + +GF_EXPORT +GF_Err gf_isom_3gp_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_3GPConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + u32 cfg_type; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !cfg) return GF_BAD_PARAM; + + switch (cfg->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; + cfg_type = GF_ISOM_BOX_TYPE_DAMR; + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; + cfg_type = GF_ISOM_BOX_TYPE_DEVC; + break; + case GF_ISOM_SUBTYPE_3GP_QCELP: + if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; + cfg_type = GF_ISOM_BOX_TYPE_DQCP; + break; + case GF_ISOM_SUBTYPE_3GP_SMV: + if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; + cfg_type = GF_ISOM_BOX_TYPE_DSMV; + break; + case GF_ISOM_SUBTYPE_3GP_H263: + if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_VISUAL) return GF_BAD_PARAM; + cfg_type = GF_ISOM_BOX_TYPE_D263; + break; + case 0: return GF_BAD_PARAM; + default: + return GF_NOT_SUPPORTED; + } + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + switch (cfg->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + { + GF_3GPPAudioSampleEntryBox *entry = (GF_3GPPAudioSampleEntryBox *) gf_isom_box_new(cfg->type); + if (!entry) return GF_OUT_OF_MEM; + entry->info = (GF_3GPPConfigBox *) gf_isom_box_new(cfg_type); + if (!entry->info) { + gf_isom_box_del((GF_Box *) entry); + return GF_OUT_OF_MEM; + } + memcpy(&entry->info->cfg, cfg, sizeof(GF_3GPConfig)); + entry->samplerate_hi = trak->Media->mediaHeader->timeScale; + entry->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + } + break; + case GF_ISOM_SUBTYPE_3GP_H263: + { + GF_3GPPVisualSampleEntryBox *entry = (GF_3GPPVisualSampleEntryBox *) gf_isom_box_new(cfg->type); + if (!entry) return GF_OUT_OF_MEM; + entry->info = (GF_3GPPConfigBox *) gf_isom_box_new(cfg_type); + if (!entry->info) { + gf_isom_box_del((GF_Box *) entry); + return GF_OUT_OF_MEM; + } + memcpy(&entry->info->cfg, cfg, sizeof(GF_3GPConfig)); + entry->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + } + break; + } + return e; +} + +GF_Err gf_isom_3gp_config_update(GF_ISOFile *the_file, u32 trackNumber, GF_3GPConfig *param, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + GF_3GPConfig *cfg; + GF_3GPPAudioSampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !param || !DescriptionIndex) return GF_BAD_PARAM; + + cfg = NULL; + entry = (GF_3GPPAudioSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return GF_BAD_PARAM; + switch (entry->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + cfg = &entry->info->cfg; + break; + case GF_ISOM_SUBTYPE_3GP_H263: + cfg = & ((GF_3GPPVisualSampleEntryBox *)entry)->info->cfg; + break; + default: + break; + } + if (!cfg || (cfg->type != param->type)) return GF_BAD_PARAM; + memcpy(cfg, param, sizeof(GF_3GPConfig)); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_ac3_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AC3Config *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_AC3SampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !cfg) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + entry = (GF_AC3SampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AC3); + if (!entry) return GF_OUT_OF_MEM; + entry->info = (GF_AC3ConfigBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DAC3); + if (!entry->info) { + gf_isom_box_del((GF_Box *) entry); + return GF_OUT_OF_MEM; + } + memcpy(&entry->info->cfg, cfg, sizeof(GF_AC3Config)); + entry->samplerate_hi = trak->Media->mediaHeader->timeScale; + entry->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + return e; +} +#endif //GPAC_READ_ONLY + + + + +GF_Err gf_isom_get_dims_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_DIMSDescription *desc) +{ + GF_DIMSSampleEntryBox *dims; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !descriptionIndex || !desc) return GF_BAD_PARAM; + + dims = (GF_DIMSSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, descriptionIndex-1); + if (!dims) return GF_BAD_PARAM; + if (dims->type != GF_ISOM_BOX_TYPE_DIMS) return GF_BAD_PARAM; + + memset(desc, 0, sizeof(GF_DIMSDescription)); + if (dims->config) { + desc->profile = dims->config->profile; + desc->level = dims->config->level; + desc->pathComponents = dims->config->pathComponents; + desc->fullRequestHost = dims->config->fullRequestHost; + desc->containsRedundant = dims->config->containsRedundant; + desc->streamType = dims->config->streamType; + desc->textEncoding = dims->config->textEncoding; + desc->contentEncoding = dims->config->contentEncoding; + } + if (dims->scripts) { + desc->content_script_types = dims->scripts->content_script_types; + } + return GF_OK; +} + +#ifndef GPAC_READ_ONLY +GF_Err gf_isom_new_dims_description(GF_ISOFile *movie, u32 trackNumber, GF_DIMSDescription *desc, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_DIMSSampleEntryBox *dims; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media) return GF_BAD_PARAM; + + if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_SCENE) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + dims = (GF_DIMSSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DIMS); + dims->dataReferenceIndex = dataRefIndex; + gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, dims); + if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + + dims->config = (GF_DIMSSceneConfigBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DIMC); + dims->config->profile = desc->profile; + dims->config->level = desc->level; + dims->config->pathComponents = desc->pathComponents; + dims->config->fullRequestHost = desc->fullRequestHost; + dims->config->containsRedundant = desc->containsRedundant; + if (!dims->config->containsRedundant) dims->config->containsRedundant = 1; + dims->config->streamType = desc->streamType; + dims->config->textEncoding = strdup(desc->textEncoding ? desc->textEncoding : ""); + dims->config->contentEncoding = strdup(desc->contentEncoding ? desc->contentEncoding : ""); + + if (desc->content_script_types) { + dims->scripts = (GF_DIMSScriptTypesBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DIST); + dims->scripts->content_script_types = strdup(desc->content_script_types); + } + return e; +} + + +GF_Err gf_isom_update_dims_description(GF_ISOFile *movie, u32 trackNumber, GF_DIMSDescription *desc, char *URLname, char *URNname, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + GF_DIMSSampleEntryBox *dims; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !desc || !DescriptionIndex) return GF_BAD_PARAM; + + dims = (GF_DIMSSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!dims) return GF_BAD_PARAM; + if (dims->type != GF_ISOM_BOX_TYPE_DIMS) return GF_BAD_PARAM; + if (!dims->config) + dims->config = (GF_DIMSSceneConfigBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DIMC); + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + dims->config->profile = desc->profile; + dims->config->level = desc->level; + dims->config->pathComponents = desc->pathComponents; + dims->config->fullRequestHost = desc->fullRequestHost; + dims->config->containsRedundant = desc->containsRedundant; + dims->config->streamType = desc->streamType; + + if (dims->config->textEncoding) free(dims->config->textEncoding); + dims->config->textEncoding = strdup(desc->textEncoding ? desc->textEncoding : ""); + + if (dims->config->contentEncoding) free(dims->config->contentEncoding); + dims->config->contentEncoding = strdup(desc->contentEncoding ? desc->contentEncoding : ""); + + if (desc->content_script_types) { + if (!dims->scripts) + dims->scripts = (GF_DIMSScriptTypesBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DIST); + if (dims->scripts->content_script_types) free(dims->scripts->content_script_types); + dims->scripts->content_script_types = strdup(desc->content_script_types ? desc->content_script_types :""); + } else if (dims->scripts) { + gf_isom_box_del((GF_Box *) dims->scripts); + dims->scripts = NULL; + } + return e; +} +#endif //GPAC_READ_ONLY + diff --git a/src/isomedia/stbl_read.c b/src/isomedia/stbl_read.c new file mode 100644 index 0000000..ad4c478 --- /dev/null +++ b/src/isomedia/stbl_read.c @@ -0,0 +1,513 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +//Get the sample number +GF_Err findEntryForTime(GF_SampleTableBox *stbl, u64 DTS, u8 useCTS, u32 *sampleNumber, u32 *prevSampleNumber) +{ + u32 i, j, curSampNum, CTSOffset, count; + u64 curDTS; + GF_SttsEntry *ent; + (*sampleNumber) = 0; + (*prevSampleNumber) = 0; + + if (!stbl->CompositionOffset) useCTS = 0; + /*FIXME: CTS is ALWAYS disabled for now to make sure samples are fetched in + decoding order. */ + useCTS = 0; + + //our cache + if (stbl->TimeToSample->r_FirstSampleInEntry && + (DTS >= stbl->TimeToSample->r_CurrentDTS) ) { + //if we're using CTS, we don't really know whether we're in the good entry or not + //(eg, the real DTS of the sample could be in a previous entry + i = stbl->TimeToSample->r_currentEntryIndex; + curDTS = stbl->TimeToSample->r_CurrentDTS; + curSampNum = stbl->TimeToSample->r_FirstSampleInEntry; + } else { + i = 0; + curDTS = stbl->TimeToSample->r_CurrentDTS = 0; + curSampNum = stbl->TimeToSample->r_FirstSampleInEntry = 1; + stbl->TimeToSample->r_currentEntryIndex = 0; + } + + //we need to validate our cache if we are using CTS because of B-frames and co... + if (i && useCTS) { + while (1) { + stbl_GetSampleCTS(stbl->CompositionOffset, curSampNum, &CTSOffset); + //we're too far, rewind + if ( i && (curDTS + CTSOffset > DTS) ) { + ent = &stbl->TimeToSample->entries[i]; + curSampNum -= ent->sampleCount; + curDTS -= ent->sampleDelta * ent->sampleCount; + i --; + } else if (!i) { + //begining of the table, no choice + curDTS = stbl->TimeToSample->r_CurrentDTS = 0; + curSampNum = stbl->TimeToSample->r_FirstSampleInEntry = 1; + stbl->TimeToSample->r_currentEntryIndex = 0; + break; + } else { + //OK now we're good + break; + } + } + } + + //look for the DTS from this entry + count = stbl->TimeToSample->nb_entries; + for (; iTimeToSample->entries[i]; + if (useCTS) { + stbl_GetSampleCTS(stbl->CompositionOffset, curSampNum, &CTSOffset); + } else { + CTSOffset = 0; + } + for (j=0; jsampleCount; j++) { + if (curDTS + CTSOffset >= DTS) goto entry_found; + curSampNum += 1; + curDTS += ent->sampleDelta; + } + //we're switching to the next entry, update the cache! + stbl->TimeToSample->r_CurrentDTS += ent->sampleCount * ent->sampleDelta; + stbl->TimeToSample->r_currentEntryIndex += 1; + stbl->TimeToSample->r_FirstSampleInEntry += ent->sampleCount; + } + //return as is + return GF_OK; + +entry_found: + //do we have the exact time ? + if (curDTS + CTSOffset == DTS) { + (*sampleNumber) = curSampNum; + } + //if we match the exact DTS also select this sample + else if (curDTS == DTS) { + (*sampleNumber) = curSampNum; + } else { + //exception for the first sample (we need to "load" the playback) + if (curSampNum != 1) { + (*prevSampleNumber) = curSampNum - 1; + } else { + (*prevSampleNumber) = 1; + } + } + return GF_OK; +} + +//Get the Size of a given sample +GF_Err stbl_GetSampleSize(GF_SampleSizeBox *stsz, u32 SampleNumber, u32 *Size) +{ + if (!stsz || !SampleNumber || SampleNumber > stsz->sampleCount) return GF_BAD_PARAM; + + (*Size) = 0; + + if (stsz->sampleSize && (stsz->type != GF_ISOM_BOX_TYPE_STZ2)) { + (*Size) = stsz->sampleSize; + } else { + (*Size) = stsz->sizes[SampleNumber - 1]; + } + return GF_OK; +} + + + +//Get the CTS offset of a given sample +GF_Err stbl_GetSampleCTS(GF_CompositionOffsetBox *ctts, u32 SampleNumber, u32 *CTSoffset) +{ + u32 i; + + (*CTSoffset) = 0; + //test on SampleNumber is done before + if (!ctts || !SampleNumber) return GF_BAD_PARAM; + + if (ctts->r_FirstSampleInEntry && (ctts->r_FirstSampleInEntry < SampleNumber) ) { + i = ctts->r_currentEntryIndex; + } else { + ctts->r_FirstSampleInEntry = 1; + ctts->r_currentEntryIndex = 0; + i = 0; + } + for (; i< ctts->nb_entries; i++) { + if (SampleNumber < ctts->r_FirstSampleInEntry + ctts->entries[i].sampleCount) break; + //update our cache + ctts->r_currentEntryIndex += 1; + ctts->r_FirstSampleInEntry += ctts->entries[i].sampleCount; + } + //no ent, set everything to 0... + if (i==ctts->nb_entries) return GF_OK; + /*asked for a sample not in table - this means CTTS is 0 (that's due to out internal packing construction of CTTS)*/ + if (SampleNumber >= ctts->r_FirstSampleInEntry + ctts->entries[i].sampleCount) return GF_OK; + (*CTSoffset) = ctts->entries[i].decodingOffset; + return GF_OK; +} + +//Get the DTS of a sample +GF_Err stbl_GetSampleDTS(GF_TimeToSampleBox *stts, u32 SampleNumber, u64 *DTS) +{ + u32 i, j, count; + GF_SttsEntry *ent; + + (*DTS) = 0; + if (!stts || !SampleNumber) return GF_BAD_PARAM; + + ent = NULL; + //use our cache + count = stts->nb_entries; + if (stts->r_FirstSampleInEntry + && (stts->r_FirstSampleInEntry <= SampleNumber) + //this is for read/write access + && (stts->r_currentEntryIndex < count) ) { + + i = stts->r_currentEntryIndex; + } else { + i = stts->r_currentEntryIndex = 0; + stts->r_FirstSampleInEntry = 1; + stts->r_CurrentDTS = 0; + } + + for (; i < count; i++) { + ent = &stts->entries[i]; + + //in our entry + if (ent->sampleCount + stts->r_FirstSampleInEntry >= 1 + SampleNumber) { + j = SampleNumber - stts->r_FirstSampleInEntry; + goto found; + } + + //update our cache + stts->r_CurrentDTS += ent->sampleCount * ent->sampleDelta; + stts->r_currentEntryIndex += 1; + stts->r_FirstSampleInEntry += ent->sampleCount; + } +// if (SampleNumber >= stts->r_FirstSampleInEntry + ent->sampleCount) return GF_BAD_PARAM; + + //no ent, this is really weird. Let's assume the DTS is then what is written in the table + if (!ent || (i == count)) (*DTS) = stts->r_CurrentDTS; + return GF_OK; + +found: + (*DTS) = stts->r_CurrentDTS + j * (u64) ent->sampleDelta; + + if (stts->r_FirstSampleInEntry == 1) + stts->r_FirstSampleInEntry = 1; + + + + return GF_OK; +} + +//Set the RAP flag of a sample +GF_Err stbl_GetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 *IsRAP, u32 *prevRAP, u32 *nextRAP) +{ + u32 i; + + if (prevRAP) *prevRAP = 0; + if (nextRAP) *nextRAP = 0; + + (*IsRAP) = 0; + if (!stss || !SampleNumber) return GF_BAD_PARAM; + + if (stss->r_LastSyncSample && (stss->r_LastSyncSample < SampleNumber) ) { + i = stss->r_LastSampleIndex; + } else { + i = 0; + } + for (; i < stss->nb_entries; i++) { + //get the entry + if (stss->sampleNumbers[i] == SampleNumber) { + //update the cache + stss->r_LastSyncSample = SampleNumber; + stss->r_LastSampleIndex = i; + (*IsRAP) = 1; + } + else if (stss->sampleNumbers[i] > SampleNumber) { + if (nextRAP) *nextRAP = stss->sampleNumbers[i]; + return GF_OK; + } + if (prevRAP) *prevRAP = stss->sampleNumbers[i]; + } + return GF_OK; +} + +//get the number of "ghost chunk" (implicit chunks described by an entry) +void GetGhostNum(GF_StscEntry *ent, u32 EntryIndex, u32 count, GF_SampleTableBox *stbl) +{ + GF_StscEntry *nextEnt; + GF_ChunkOffsetBox *stco; + GF_ChunkLargeOffsetBox *co64; + u32 ghostNum = 1; + + if (!ent->nextChunk) { + if (EntryIndex+1 == count) { + //not specified in the spec, what if the last sample to chunk is no written? + if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset; + ghostNum = (stco->nb_entries > ent->firstChunk) ? (1 + stco->nb_entries - ent->firstChunk) : 1; + } else { + co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset; + ghostNum = (co64->nb_entries > ent->firstChunk) ? (1 + co64->nb_entries - ent->firstChunk) : 1; + } + } else { + //this is an unknown case due to edit mode... + nextEnt = &stbl->SampleToChunk->entries[EntryIndex+1]; + ghostNum = nextEnt->firstChunk - ent->firstChunk; + } + } else { + ghostNum = (ent->nextChunk > ent->firstChunk) ? (ent->nextChunk - ent->firstChunk) : 1; + } + stbl->SampleToChunk->ghostNumber = ghostNum; +} + +//Get the offset, descIndex and chunkNumber of a sample... +GF_Err stbl_GetSampleInfos(GF_SampleTableBox *stbl, u32 sampleNumber, u64 *offset, u32 *chunkNumber, u32 *descIndex, u8 *isEdited) +{ + GF_Err e; + u32 i, j, k, offsetInChunk, size; + GF_ChunkOffsetBox *stco; + GF_ChunkLargeOffsetBox *co64; + GF_StscEntry *ent; + + (*offset) = 0; + (*chunkNumber) = (*descIndex) = 0; + (*isEdited) = 0; + if (!stbl || !sampleNumber) return GF_BAD_PARAM; + + if (stbl->SampleToChunk->nb_entries == stbl->SampleSize->sampleCount) { + ent = &stbl->SampleToChunk->entries[sampleNumber-1]; + if (!ent) return GF_BAD_PARAM; + (*descIndex) = ent->sampleDescriptionIndex; + (*chunkNumber) = sampleNumber; + (*isEdited) = ent->isEdited; + if ( stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset; + (*offset) = (u64) stco->offsets[sampleNumber - 1]; + } else { + co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset; + (*offset) = co64->offsets[sampleNumber - 1]; + } + return GF_OK; + } + + //check our cache + if (stbl->SampleToChunk->firstSampleInCurrentChunk && + (stbl->SampleToChunk->firstSampleInCurrentChunk < sampleNumber)) { + + i = stbl->SampleToChunk->currentIndex; +// ent = gf_list_get(stbl->SampleToChunk->entryList, i); + ent = &stbl->SampleToChunk->entries[stbl->SampleToChunk->currentIndex]; + GetGhostNum(ent, i, stbl->SampleToChunk->nb_entries, stbl); + k = stbl->SampleToChunk->currentChunk; + } else { + i = 0; + stbl->SampleToChunk->currentIndex = 0; + stbl->SampleToChunk->currentChunk = 1; + stbl->SampleToChunk->firstSampleInCurrentChunk = 1; + ent = &stbl->SampleToChunk->entries[0]; + GetGhostNum(ent, 0, stbl->SampleToChunk->nb_entries, stbl); + k = stbl->SampleToChunk->currentChunk; + } + + //first get the chunk + for (; i < stbl->SampleToChunk->nb_entries; i++) { + //browse from the current chunk we're browsing from index 1 + for (; k <= stbl->SampleToChunk->ghostNumber; k++) { + //browse all the samples in this chunk + for (j = 0; j < ent->samplesPerChunk; j++) { + //ok, this is our sample + if (stbl->SampleToChunk->firstSampleInCurrentChunk + j == sampleNumber ) + goto sample_found; + } + //nope, get to next chunk + stbl->SampleToChunk->firstSampleInCurrentChunk += ent->samplesPerChunk; + stbl->SampleToChunk->currentChunk ++; + } + //not in this entry, get the next entry if not the last one + if (i+1 != stbl->SampleToChunk->nb_entries) { + ent = &stbl->SampleToChunk->entries[i+1]; + //update the GhostNumber + GetGhostNum(ent, i+1, stbl->SampleToChunk->nb_entries, stbl); + //update the entry in our cache + stbl->SampleToChunk->currentIndex = i+1; + stbl->SampleToChunk->currentChunk = 1; + k = 1; + } + } + //if we get here, gasp, the sample was not found + return GF_ISOM_INVALID_FILE; + +sample_found: + + (*descIndex) = ent->sampleDescriptionIndex; + (*chunkNumber) = ent->firstChunk + stbl->SampleToChunk->currentChunk - 1; + (*isEdited) = ent->isEdited; + + //ok, get the size of all the previous sample + offsetInChunk = 0; + //warning, firstSampleInChunk is at least 1 - not 0 + for (i = stbl->SampleToChunk->firstSampleInCurrentChunk; i < sampleNumber; i++) { + e = stbl_GetSampleSize(stbl->SampleSize, i, &size); + if (e) return e; + offsetInChunk += size; + } + //OK, that's the size of our offset in the chunk + //now get the chunk + if ( stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset; + if (stco->nb_entries < (*chunkNumber) ) return GF_ISOM_INVALID_FILE; + (*offset) = (u64) stco->offsets[(*chunkNumber) - 1] + (u64) offsetInChunk; + } else { + co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset; + if (co64->nb_entries < (*chunkNumber) ) return GF_ISOM_INVALID_FILE; + (*offset) = co64->offsets[(*chunkNumber) - 1] + (u64) offsetInChunk; + } + return GF_OK; +} + + +GF_Err stbl_GetSampleShadow(GF_ShadowSyncBox *stsh, u32 *sampleNumber, u32 *syncNum) +{ + u32 i, count; + GF_StshEntry *ent; + + if (stsh->r_LastFoundSample && (stsh->r_LastFoundSample <= *sampleNumber)) { + i = stsh->r_LastEntryIndex; + } else { + i = 0; + stsh->r_LastFoundSample = 1; + } + + ent = NULL; + (*syncNum) = 0; + + count = gf_list_count(stsh->entries); + for (; ientries, i); + //we get the exact desired sample ! + if (ent->shadowedSampleNumber == *sampleNumber) { + (*syncNum) = ent->syncSampleNumber; + stsh->r_LastFoundSample = *sampleNumber; + stsh->r_LastEntryIndex = i; + return GF_OK; + } else if (ent->shadowedSampleNumber > *sampleNumber) { + //do we have an entry before ? If not, there is no shadowing available + //for this sample + if (!i) return GF_OK; + //ok, indicate the previous ShadowedSample + ent = (GF_StshEntry*)gf_list_get(stsh->entries, i-1); + (*syncNum) = ent->syncSampleNumber; + //change the sample number + (*sampleNumber) = ent->shadowedSampleNumber; + //reset the cache to the last ShadowedSample + stsh->r_LastEntryIndex = i-1; + stsh->r_LastFoundSample = ent->shadowedSampleNumber; + } + } + stsh->r_LastEntryIndex = i-1; + stsh->r_LastFoundSample = ent ? ent->shadowedSampleNumber : 0; + return GF_OK; +} + + + +GF_Err stbl_GetPaddingBits(GF_PaddingBitsBox *padb, u32 SampleNumber, u8 *PadBits) +{ + if (!PadBits) return GF_BAD_PARAM; + *PadBits = 0; + if (!padb || !padb->padbits) return GF_OK; + //the spec says "should" not shall. return 0 padding + if (padb->SampleCount < SampleNumber) return GF_OK; + *PadBits = padb->padbits[SampleNumber-1]; + return GF_OK; +} + +//Set the RAP flag of a sample +GF_Err stbl_GetSampleDepType(GF_SampleDependencyTypeBox *sdep, u32 SampleNumber, u32 *dependsOn, u32 *dependedOn, u32 *redundant) +{ + u8 flag; + + assert(dependsOn && dependedOn && redundant); + *dependsOn = *dependedOn = *redundant = 0; + + if (SampleNumber > sdep->sampleCount) return GF_BAD_PARAM; + flag = sdep->sample_info[SampleNumber-1]; + *dependsOn = (flag >> 4) & 3; + *dependedOn = (flag >> 2) & 3; + *redundant = (flag) & 3; + return GF_OK; +} + +u32 stbl_GetSampleFragmentCount(GF_SampleFragmentBox *stsf, u32 sampleNumber) +{ + GF_StsfEntry *ent; + u32 i, count; + if (!stsf) return 0; + + //check cache + if (!stsf->r_currentEntry || (stsf->r_currentEntry->SampleNumber < sampleNumber)) { + stsf->r_currentEntry = NULL; + stsf->r_currentEntryIndex = 0; + } + i = stsf->r_currentEntryIndex; + + count = gf_list_count(stsf->entryList); + for (; ientryList, i); + if (ent->SampleNumber == sampleNumber) { + stsf->r_currentEntry = ent; + stsf->r_currentEntryIndex = i; + return ent->fragmentCount; + } + } + //not found + return 0; +} + +u32 stbl_GetSampleFragmentSize(GF_SampleFragmentBox *stsf, u32 sampleNumber, u32 FragmentIndex) +{ + GF_StsfEntry *ent; + u32 i, count; + if (!stsf || !FragmentIndex) return 0; + + //check cache + if (!stsf->r_currentEntry || (stsf->r_currentEntry->SampleNumber < sampleNumber)) { + stsf->r_currentEntry = NULL; + stsf->r_currentEntryIndex = 0; + } + i = stsf->r_currentEntryIndex; + count = gf_list_count(stsf->entryList); + for (; ientryList, i); + if (ent->SampleNumber == sampleNumber) { + stsf->r_currentEntry = ent; + stsf->r_currentEntryIndex = i; + if (FragmentIndex > ent->fragmentCount) return 0; + return ent->fragmentSizes[FragmentIndex - 1]; + } + } + //not found + return 0; +} + diff --git a/src/isomedia/stbl_write.c b/src/isomedia/stbl_write.c new file mode 100644 index 0000000..89acfec --- /dev/null +++ b/src/isomedia/stbl_write.c @@ -0,0 +1,1715 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/*macro used for table realloc - we allocate much more than needed in order to keep the number of +realloc low, which greatly impacts performances for large files*/ +#define ALLOC_INC(a) a = ((a<10) ? 100 : (a*3)/2); + +#ifndef GPAC_READ_ONLY + +//adds a DTS in the table and get the sample number of this new sample +//we could return an error if a sample with the same DTS already exists +//but this is not true for QT or MJ2K, only for MP4... +//we assume the authoring tool tries to create a compliant MP4 file. +GF_Err stbl_AddDTS(GF_SampleTableBox *stbl, u64 DTS, u32 *sampleNumber, u32 LastAUDefDuration) +{ + u32 i, j, sampNum; + u64 *DTSs, curDTS; + Bool inserted; + GF_SttsEntry *ent; + + GF_TimeToSampleBox *stts = stbl->TimeToSample; + + //reset the reading cache when adding a sample + stts->r_FirstSampleInEntry = 0; + + *sampleNumber = 0; + //if we don't have an entry, that's the first one... + if (!stts->nb_entries) { + //assert the first DTS is 0. If not, that will break the whole file + if (DTS) return GF_BAD_PARAM; + stts->alloc_size = 1; + stts->nb_entries = 1; + stts->entries = malloc(sizeof(GF_SttsEntry)); + if (!stts->entries) return GF_OUT_OF_MEM; + stts->entries[0].sampleCount = 1; + stts->entries[0].sampleDelta = LastAUDefDuration; + stts->w_currentSampleNum = (*sampleNumber) = 1; + return GF_OK; + } + + //check the last DTS... + if (DTS > stts->w_LastDTS) { + ent = &stts->entries[stts->nb_entries-1]; + //OK, we're adding at the end + if (DTS == stts->w_LastDTS + ent->sampleDelta) { + ent->sampleCount ++; + stts->w_currentSampleNum ++; + (*sampleNumber) = stts->w_currentSampleNum; + stts->w_LastDTS = DTS; + return GF_OK; + } + //we need to split the entry + if (ent->sampleCount == 1) { + //use this one and adjust... + ent->sampleDelta = (u32) (DTS - stts->w_LastDTS); + ent->sampleCount ++; + stts->w_currentSampleNum ++; + stts->w_LastDTS = DTS; + (*sampleNumber) = stts->w_currentSampleNum; + return GF_OK; + } + //we definitely need to split the entry ;) + ent->sampleCount --; + if (stts->alloc_size==stts->nb_entries) { + ALLOC_INC(stts->alloc_size); + stts->entries = realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size); + if (!stts->entries) return GF_OUT_OF_MEM; + } + ent = &stts->entries[stts->nb_entries]; + stts->nb_entries++; + + ent->sampleCount = 2; + ent->sampleDelta = (u32) (DTS - stts->w_LastDTS); + stts->w_LastDTS = DTS; + stts->w_currentSampleNum ++; + (*sampleNumber) = stts->w_currentSampleNum; + return GF_OK; + } + + + //unpack the DTSs and locate new sample... + DTSs = (u64*)malloc(sizeof(u64) * (stbl->SampleSize->sampleCount+2) ); + if (!DTSs) return GF_OUT_OF_MEM; + curDTS = 0; + sampNum = 0; + ent = NULL; + inserted = 0; + for (i=0; inb_entries; i++) { + ent = & stts->entries[i]; + for (j = 0; jsampleCount; j++) { + if (!inserted && (curDTS > DTS)) { + DTSs[sampNum] = DTS; + sampNum++; + *sampleNumber = sampNum; + inserted = 1; + } + DTSs[sampNum] = curDTS; + curDTS += ent->sampleDelta; + sampNum ++; + } + } + if (!inserted) { + free(DTSs); + return GF_BAD_PARAM; + } + + /*we will at most insert 2 new entries*/ + if (stts->nb_entries+2 >= stts->alloc_size) { + stts->alloc_size += 2; + stts->entries = realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size); + if (!stts->entries) return GF_OUT_OF_MEM; + } + + /*repack the DTSs*/ + j=0; + stts->nb_entries = 1; + stts->entries[0].sampleCount = 1; + stts->entries[0].sampleDelta = (u32) DTSs[1] /* - (DTS[0] wichis 0)*/; + for (i=1; iSampleSize->sampleCount+1; i++) { + if (i == stbl->SampleSize->sampleCount) { + //and by default, our last sample has the same delta as the prev + stts->entries[j].sampleCount++; + } else if (stts->entries[j].sampleDelta == (u32) ( DTSs[i+1] - DTSs[i]) ) { + stts->entries[j].sampleCount ++; + } else { + stts->nb_entries ++; + j++; + stts->entries[j].sampleCount = 1; + stts->entries[j].sampleDelta = (u32) (DTSs[i+1] - DTSs[i]); + } + } + free(DTSs); + + //reset the cache to the end + stts->w_currentSampleNum = stbl->SampleSize->sampleCount + 1; + return GF_OK; +} + +GF_Err AddCompositionOffset(GF_CompositionOffsetBox *ctts, u32 offset) +{ + if (!ctts) return GF_BAD_PARAM; + + if (ctts->nb_entries && (ctts->entries[ctts->nb_entries-1].decodingOffset==offset)) { + ctts->entries[ctts->nb_entries-1].sampleCount++; + } else { + if (ctts->alloc_size==ctts->nb_entries) { + ALLOC_INC(ctts->alloc_size); + ctts->entries = realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + if (!ctts->entries) return GF_OUT_OF_MEM; + } + ctts->entries[ctts->nb_entries].decodingOffset = offset; + ctts->entries[ctts->nb_entries].sampleCount = 1; + ctts->nb_entries++; + } + ctts->w_LastSampleNumber++; + return GF_OK; +} + +//adds a CTS offset for a new sample +GF_Err stbl_AddCTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 CTSoffset) +{ + u32 i, j, sampNum, *CTSs; + + GF_CompositionOffsetBox *ctts = stbl->CompositionOffset; + + /*in unpack mode we're sure to have 1 ctts entry per sample*/ + if (ctts->unpack_mode) { + if (ctts->nb_entries==ctts->alloc_size) { + ALLOC_INC(ctts->alloc_size); + ctts->entries = realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + if (!ctts->entries) return GF_OUT_OF_MEM; + } + ctts->entries[ctts->nb_entries].decodingOffset = CTSoffset; + ctts->entries[ctts->nb_entries].sampleCount = 1; + ctts->nb_entries++; + return GF_OK; + } + //check if we're working in order... + if (ctts->w_LastSampleNumber < sampleNumber) { + //add some 0 till we get to the sample + while (ctts->w_LastSampleNumber + 1 != sampleNumber) { + AddCompositionOffset(ctts, 0); + } + return AddCompositionOffset(ctts, CTSoffset); + } + + //NOPE we are inserting a sample... + CTSs = (u32*)malloc(sizeof(u32) * (stbl->SampleSize->sampleCount+1) ); + if (!CTSs) return GF_OUT_OF_MEM; + sampNum = 0; + for (i=0; inb_entries; i++) { + for (j = 0; jentries[i].sampleCount; j++) { + if (sampNum+1==sampleNumber) { + CTSs[sampNum] = CTSoffset; + sampNum ++; + } + CTSs[sampNum] = ctts->entries[i].decodingOffset; + sampNum ++; + } + } + + /*we will at most add 2 new entries (spliting of an existing one)*/ + if (ctts->nb_entries+2>=ctts->alloc_size) { + ctts->alloc_size += 2; + ctts->entries = realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + } + + ctts->entries[0].sampleCount = 1; + ctts->entries[0].decodingOffset = CTSs[0]; + ctts->nb_entries = 1; + j=0; + for (i=1; iSampleSize->sampleCount + 1; i++) { + if (CTSs[i]==ctts->entries[j].decodingOffset) { + ctts->entries[j].sampleCount++; + } else { + j++; + ctts->nb_entries++; + ctts->entries[j].sampleCount = 1; + ctts->entries[j].decodingOffset = CTSs[i]; + } + } + free(CTSs); + + /*we've inserted a sample, therefore the last sample (n) has now number n+1 + we cannot use SampleCount because we have probably skipped some samples + (we're calling AddCTS only if the sample has a CTSOffset !!!)*/ + ctts->w_LastSampleNumber += 1; + return GF_OK; +} + +GF_Err stbl_repackCTS(GF_CompositionOffsetBox *ctts) +{ + u32 i, j; + + if (!ctts->unpack_mode) return GF_OK; + ctts->unpack_mode = 0; + + j=0; + for (i=1; inb_entries; i++) { + if (ctts->entries[i].decodingOffset==ctts->entries[j].decodingOffset) { + ctts->entries[j].sampleCount++; + } else { + j++; + ctts->entries[j].sampleCount = 1; + ctts->entries[j].decodingOffset = ctts->entries[i].decodingOffset; + } + } + ctts->nb_entries=j+1; + /*note we don't realloc*/ + return GF_OK; +} + +GF_Err stbl_unpackCTS(GF_SampleTableBox *stbl) +{ + GF_DttsEntry *packed; + u32 i, j, remain, count; + GF_CompositionOffsetBox *ctts; + ctts = stbl->CompositionOffset; + if (ctts->unpack_mode) return GF_OK; + ctts->unpack_mode = 1; + + packed = ctts->entries; + count = ctts->nb_entries; + ctts->entries = NULL; + ctts->nb_entries = 0; + ctts->alloc_size = 0; + for (i=0;inb_entries == ctts->alloc_size) { + ALLOC_INC(ctts->alloc_size); + ctts->entries = realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + } + ctts->entries[ctts->nb_entries].decodingOffset = packed[i].decodingOffset; + ctts->entries[ctts->nb_entries].sampleCount = 1; + ctts->nb_entries++; + } + } + free(packed); + + remain = stbl->SampleSize->sampleCount - ctts->nb_entries; + while (remain) { + if (ctts->nb_entries == ctts->alloc_size) { + ALLOC_INC(ctts->alloc_size); + ctts->entries = realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + } + ctts->entries[ctts->nb_entries].decodingOffset = 0; + ctts->entries[ctts->nb_entries].sampleCount = 1; + ctts->nb_entries++; + remain--; + } + return GF_OK; +} + +//add size +GF_Err stbl_AddSize(GF_SampleSizeBox *stsz, u32 sampleNumber, u32 size) +{ + u32 i, k; + u32 *newSizes; + if (!stsz || !size || !sampleNumber) return GF_BAD_PARAM; + + if (sampleNumber > stsz->sampleCount + 1) return GF_BAD_PARAM; + + //all samples have the same size + if (stsz->sizes == NULL) { + //1 first sample added in NON COMPACT MODE + if (! stsz->sampleCount && (stsz->type != GF_ISOM_BOX_TYPE_STZ2) ) { + stsz->sampleCount = 1; + stsz->sampleSize = size; + return GF_OK; + } + //2- sample has the same size + if (stsz->sampleSize == size) { + stsz->sampleCount++; + return GF_OK; + } + //3- no, need to alloc a size table + stsz->sizes = (u32*)malloc(sizeof(u32) * (stsz->sampleCount + 1)); + if (!stsz->sizes) return GF_OUT_OF_MEM; + stsz->alloc_size = stsz->sampleCount + 1; + + k = 0; + for (i = 0 ; i < stsz->sampleCount; i++) { + if (i + 1 == sampleNumber) { + stsz->sizes[i + k] = size; + k = 1; + } + stsz->sizes[i+k] = stsz->sampleSize; + } + //this if we append a new sample + if (stsz->sampleCount + 1 == sampleNumber) { + stsz->sizes[stsz->sampleCount] = size; + } + stsz->sampleSize = 0; + stsz->sampleCount++; + return GF_OK; + } + + + /*append*/ + if (stsz->sampleCount + 1 == sampleNumber) { + if (!stsz->alloc_size) stsz->alloc_size = stsz->sampleCount; + if (stsz->sampleCount == stsz->alloc_size) { + ALLOC_INC(stsz->alloc_size); + stsz->sizes = realloc(stsz->sizes, sizeof(u32)*(stsz->alloc_size) ); + if (!stsz->sizes) return GF_OUT_OF_MEM; + } + stsz->sizes[stsz->sampleCount] = size; + } else { + newSizes = (u32*)malloc(sizeof(u32)*(1 + stsz->sampleCount) ); + if (!newSizes) return GF_OUT_OF_MEM; + k = 0; + for (i = 0; i < stsz->sampleCount; i++) { + if (i + 1 == sampleNumber) { + newSizes[i + k] = size; + k = 1; + } + newSizes[i + k] = stsz->sizes[i]; + } + free(stsz->sizes); + stsz->sizes = newSizes; + stsz->alloc_size = 1 + stsz->sampleCount; + } + stsz->sampleCount++; + return GF_OK; +} + + +GF_Err stbl_AddRAP(GF_SyncSampleBox *stss, u32 sampleNumber) +{ + u32 i, k; + u32 *newNumbers; + + if (!stss || !sampleNumber) return GF_BAD_PARAM; + + if (stss->sampleNumbers == NULL) { + ALLOC_INC(stss->alloc_size); + stss->sampleNumbers = (u32*)malloc(sizeof(u32)*stss->alloc_size); + if (!stss->sampleNumbers) return GF_OUT_OF_MEM; + stss->sampleNumbers[0] = sampleNumber; + stss->nb_entries = 1; + return GF_OK; + } + + if (stss->sampleNumbers[stss->nb_entries-1] < sampleNumber) { + if (stss->nb_entries==stss->alloc_size) { + ALLOC_INC(stss->alloc_size); + stss->sampleNumbers = realloc(stss->sampleNumbers, sizeof(u32) * stss->alloc_size); + if (!stss->sampleNumbers) return GF_OUT_OF_MEM; + } + stss->sampleNumbers[stss->nb_entries] = sampleNumber; + } else { + newNumbers = (u32*)malloc(sizeof(u32) * (stss->nb_entries + 1)); + if (!newNumbers) return GF_OUT_OF_MEM; + //the table is in increasing order of sampleNumber + k = 0; + for (i = 0; i < stss->nb_entries; i++) { + if (stss->sampleNumbers[i] >= sampleNumber) { + newNumbers[i + k] = sampleNumber; + k = 1; + } + newNumbers[i + k] = stss->sampleNumbers[i] + k; + } + free(stss->sampleNumbers); + stss->sampleNumbers = newNumbers; + stss->alloc_size = stss->nb_entries+1; + } + //update our list + stss->nb_entries ++; + return GF_OK; +} + +GF_Err stbl_AddRedundant(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + GF_SampleDependencyTypeBox *sdtp; + + if (stbl->SampleDep == NULL) { + stbl->SampleDep = (GF_SampleDependencyTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SDTP); + if (!stbl->SampleDep) return GF_OUT_OF_MEM; + } + sdtp = stbl->SampleDep; + if (sdtp->sampleCount + 1 < sampleNumber) { + u32 missed = sampleNumber-1 - sdtp->sampleCount; + sdtp->sample_info = (u8*) realloc(sdtp->sample_info, sizeof(u8) * (sdtp->sampleCount+missed) ); + while (missed) { + u8 isRAP; + if (stbl->SyncSample) stbl_GetSampleRAP(stbl->SyncSample, sdtp->sampleCount+1, &isRAP, NULL, NULL); + else isRAP = 1; + sdtp->sample_info[sdtp->sampleCount] = isRAP ? 0x20 : 0; + sdtp->sampleCount++; + missed--; + } + } + + sdtp->sample_info = (u8*) realloc(sdtp->sample_info, sizeof(u8) * (sdtp->sampleCount + 1)); + if (!sdtp->sample_info) return GF_OUT_OF_MEM; + if (sdtp->sampleCount < sampleNumber) { + sdtp->sample_info[sdtp->sampleCount] = 0x29; + } else { + u32 snum = sampleNumber-1; + memmove(sdtp->sample_info+snum+1, sdtp->sample_info+snum, sizeof(u8) * (sdtp->sampleCount - snum) ); + sdtp->sample_info[snum] = 0x29; + } + //update our list + sdtp->sampleCount ++; + return GF_OK; +} + +//this function is always called in INCREASING order of shadow sample numbers +GF_Err stbl_AddShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber, u32 shadowNumber) +{ + GF_StshEntry *ent; + u32 i, count; + count = gf_list_count(stsh->entries); + for (i=0; ientries, i); + if (ent->shadowedSampleNumber == shadowNumber) { + ent->syncSampleNumber = sampleNumber; + return GF_OK; + } else if (ent->shadowedSampleNumber > shadowNumber) break; + } + ent = (GF_StshEntry*)malloc(sizeof(GF_StshEntry)); + if (!ent) return GF_OUT_OF_MEM; + ent->shadowedSampleNumber = shadowNumber; + ent->syncSampleNumber = sampleNumber; + if (i == gf_list_count(stsh->entries)) { + return gf_list_add(stsh->entries, ent); + } else { + return gf_list_insert(stsh->entries, ent, i ? i-1 : 0); + } +} + +//used in edit/write, where sampleNumber == chunkNumber +GF_Err stbl_AddChunkOffset(GF_MediaBox *mdia, u32 sampleNumber, u32 StreamDescIndex, u64 offset) +{ + GF_SampleTableBox *stbl; + GF_ChunkOffsetBox *stco; + GF_SampleToChunkBox *stsc; + GF_ChunkLargeOffsetBox *co64; + GF_StscEntry *ent; + u32 i, k, *newOff; + u64 *newLarge; + + stbl = mdia->information->sampleTable; + stsc = stbl->SampleToChunk; + + if (stsc->nb_entries + 1 < sampleNumber ) return GF_BAD_PARAM; + + //add the offset to the chunk... + //and we change our offset + if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset; + //if the new offset is a large one, we have to rewrite our table entry by entry (32->64 bit conv)... + if (offset > 0xFFFFFFFF) { + co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + co64->nb_entries = stco->nb_entries + 1; + co64->offsets = (u64*)malloc(sizeof(u64) * co64->nb_entries); + if (!co64->offsets) return GF_OUT_OF_MEM; + k = 0; + for (i=0;inb_entries; i++) { + if (i + 1 == sampleNumber) { + co64->offsets[i] = offset; + k = 1; + } + co64->offsets[i+k] = (u64) stco->offsets[i]; + } + if (!k) co64->offsets[co64->nb_entries - 1] = offset; + gf_isom_box_del(stbl->ChunkOffset); + stbl->ChunkOffset = (GF_Box *) co64; + } else { + //no, we can use this one. + if (sampleNumber > stco->nb_entries) { + if (!stco->alloc_size) stco->alloc_size = stco->nb_entries; + if (stco->nb_entries == stco->alloc_size) { + ALLOC_INC(stco->alloc_size); + stco->offsets = (u32*)realloc(stco->offsets, sizeof(u32) * stco->alloc_size); + if (!stco->offsets) return GF_OUT_OF_MEM; + } + stco->offsets[stco->nb_entries] = (u32) offset; + stco->nb_entries += 1; + } else { + //nope. we're inserting + newOff = (u32*)malloc(sizeof(u32) * (stco->nb_entries + 1)); + if (!newOff) return GF_OUT_OF_MEM; + k=0; + for (i=0; inb_entries; i++) { + if (i+1 == sampleNumber) { + newOff[i] = (u32) offset; + k=1; + } + newOff[i+k] = stco->offsets[i]; + } + free(stco->offsets); + stco->offsets = newOff; + stco->nb_entries ++; + stco->alloc_size = stco->nb_entries; + } + } + } else { + //use large offset... + co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset; + if (sampleNumber > co64->nb_entries) { + if (!co64->alloc_size) co64->alloc_size = co64->nb_entries; + if (co64->nb_entries == co64->alloc_size) { + ALLOC_INC(co64->alloc_size); + co64->offsets = (u64*)realloc(co64->offsets, sizeof(u64) * co64->alloc_size); + if (!co64->offsets) return GF_OUT_OF_MEM; + } + co64->offsets[co64->nb_entries] = offset; + co64->nb_entries += 1; + } else { + //nope. we're inserting + newLarge = (u64*)malloc(sizeof(u64) * (co64->nb_entries + 1)); + if (!newLarge) return GF_OUT_OF_MEM; + k=0; + for (i=0; inb_entries; i++) { + if (i+1 == sampleNumber) { + newLarge[i] = offset; + k=1; + } + newLarge[i+k] = co64->offsets[i]; + } + free(co64->offsets); + co64->offsets = newLarge; + co64->nb_entries++; + co64->alloc_size++; + } + } + + if (stsc->nb_entries==stsc->alloc_size) { + ALLOC_INC(stsc->alloc_size); + stsc->entries = realloc(stsc->entries, sizeof(GF_StscEntry)*stsc->alloc_size); + if (!stsc->entries) return GF_OUT_OF_MEM; + } + if (sampleNumber == stsc->nb_entries + 1) { + ent = &stsc->entries[stsc->nb_entries]; + } else { + memmove(&stsc->entries[sampleNumber-1], &stsc->entries[sampleNumber], sizeof(GF_StscEntry)*(stsc->nb_entries+1-sampleNumber)); + ent = &stsc->entries[sampleNumber-1]; + } + ent->isEdited = 0; + ent->isEdited = (Media_IsSelfContained(mdia, StreamDescIndex)) ? 1 : 0; + ent->sampleDescriptionIndex = StreamDescIndex; + ent->samplesPerChunk = 1; + ent->firstChunk = sampleNumber; + ent->nextChunk = sampleNumber + 1; + + //OK, now if we've inserted a chunk, update the sample to chunk info... + if (sampleNumber == stsc->nb_entries + 1) { + ent->nextChunk = stsc->nb_entries + 1; + if (stsc->nb_entries) + stsc->entries[stsc->nb_entries-1].nextChunk = ent->firstChunk; + + stbl->SampleToChunk->currentIndex = stsc->nb_entries; + stbl->SampleToChunk->firstSampleInCurrentChunk = sampleNumber; + //write - edit mode: sample number = chunk number + stbl->SampleToChunk->currentChunk = sampleNumber; + stbl->SampleToChunk->ghostNumber = 1; + } else { + /*offset remaining entries*/ + for (i = sampleNumber; inb_entries+1; i++) { + stsc->entries[i].firstChunk++; + } + } + stsc->nb_entries++; + return GF_OK; +} + + + + +GF_Err stbl_SetChunkOffset(GF_MediaBox *mdia, u32 sampleNumber, u64 offset) +{ + GF_StscEntry *ent; + u32 i; + GF_ChunkLargeOffsetBox *co64; + GF_SampleTableBox *stbl = mdia->information->sampleTable; + + if (!sampleNumber || !stbl) return GF_BAD_PARAM; + + ent = &stbl->SampleToChunk->entries[sampleNumber - 1]; + + //we edit our entry if self contained + if (Media_IsSelfContained(mdia, ent->sampleDescriptionIndex)) + ent->isEdited = 1; + + //and we change our offset + if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + //if the new offset is a large one, we have to rewrite our table... + if (offset > 0xFFFFFFFF) { + co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + co64->nb_entries = ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->nb_entries; + co64->offsets = (u64*)malloc(sizeof(u64)*co64->nb_entries); + if (!co64->offsets) return GF_OUT_OF_MEM; + for (i=0;inb_entries; i++) { + co64->offsets[i] = (u64) ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets[i]; + } + co64->offsets[ent->firstChunk - 1] = offset; + gf_isom_box_del(stbl->ChunkOffset); + stbl->ChunkOffset = (GF_Box *) co64; + return GF_OK; + } + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets[ent->firstChunk - 1] = (u32) offset; + } else { + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets[ent->firstChunk - 1] = offset; + } + return GF_OK; +} + + +GF_Err stbl_SetSampleCTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 offset) +{ + GF_CompositionOffsetBox *ctts = stbl->CompositionOffset; + + assert(ctts->unpack_mode); + + //if we're setting the CTS of a sample we've skipped... + if (ctts->w_LastSampleNumber < sampleNumber) { + //add some 0 till we get to the sample + while (ctts->w_LastSampleNumber + 1 != sampleNumber) { + AddCompositionOffset(ctts, 0); + } + return AddCompositionOffset(ctts, offset); + } + ctts->entries[sampleNumber-1].decodingOffset = offset; + return GF_OK; +} + +GF_Err stbl_SetSampleSize(GF_SampleSizeBox *stsz, u32 SampleNumber, u32 size) +{ + u32 i; + if (!SampleNumber || (stsz->sampleCount < SampleNumber)) return GF_BAD_PARAM; + + if (stsz->sampleSize) { + if (stsz->sampleSize == size) return GF_OK; + if (stsz->sampleCount == 1) { + stsz->sampleSize = size; + return GF_OK; + } + //nope, we have to rewrite a table + stsz->sizes = (u32*)malloc(sizeof(u32)*stsz->sampleCount); + if (!stsz->sizes) return GF_OUT_OF_MEM; + for (i=0; isampleCount; i++) stsz->sizes[i] = stsz->sampleSize; + stsz->sampleSize = 0; + } + stsz->sizes[SampleNumber - 1] = size; + return GF_OK; +} + + +GF_Err stbl_SetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 isRAP) +{ + u32 i, nextSamp; + + nextSamp = 0; + //check if we have already a sync sample + for (i = 0; i < stss->nb_entries; i++) { + + if (stss->sampleNumbers[i] < SampleNumber) continue; + else if (stss->sampleNumbers[i] > SampleNumber) break; + + /*found our sample number*/ + if (isRAP) return GF_OK; + /*remove it...*/ + if (i+1 < stss->nb_entries) + memcpy(stss->sampleNumbers + i, stss->sampleNumbers + i + 1, sizeof(u32) * (stss->nb_entries - i - 1)); + stss->nb_entries--; + return GF_OK; + } + //we need to insert a RAP somewhere if RAP ... + if (!isRAP) return GF_OK; + if (stss->nb_entries==stss->alloc_size) { + ALLOC_INC(stss->alloc_size); + stss->sampleNumbers = realloc(stss->sampleNumbers, sizeof(u32)*stss->alloc_size); + if (!stss->sampleNumbers) return GF_OUT_OF_MEM; + } + + if (i+1 < stss->nb_entries) + memcpy(stss->sampleNumbers + i + 1, stss->sampleNumbers + i, sizeof(u32) * (stss->nb_entries - i - 1)); + stss->sampleNumbers[i] = SampleNumber; + stss->nb_entries ++; + return GF_OK; +} + +GF_Err stbl_SetRedundant(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + if (stbl->SampleDep->sampleCount < sampleNumber) { + return stbl_AddRedundant(stbl, sampleNumber); + } else { + stbl->SampleDep->sample_info[sampleNumber-1] = 0x29; + return GF_OK; + } +} + +GF_Err stbl_SetSyncShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber, u32 syncSample) +{ + u32 i, count; + GF_StshEntry *ent; + + count = gf_list_count(stsh->entries); + for (i=0; ientries, i); + if (ent->shadowedSampleNumber == sampleNumber) { + ent->syncSampleNumber = syncSample; + return GF_OK; + } + if (ent->shadowedSampleNumber > sampleNumber) break; + } + //we need a new one... + ent = (GF_StshEntry*)malloc(sizeof(GF_StshEntry)); + if (!ent) return GF_OUT_OF_MEM; + ent->shadowedSampleNumber = sampleNumber; + ent->syncSampleNumber = syncSample; + //insert or append ? + if (i == gf_list_count(stsh->entries)) { + //don't update the cache ... + return gf_list_add(stsh->entries, ent); + } else { + //update the cache + stsh->r_LastEntryIndex = i; + stsh->r_LastFoundSample = sampleNumber; + return gf_list_insert(stsh->entries, ent, i); + } +} + + +//always called before removing the sample from SampleSize +GF_Err stbl_RemoveDTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 LastAUDefDuration) +{ + u64 *DTSs, curDTS; + u32 i, j, k, sampNum; + GF_SttsEntry *ent; + GF_TimeToSampleBox *stts; + + stts = stbl->TimeToSample; + + //we're removing the only sample: empty the sample table + if (stbl->SampleSize->sampleCount == 1) { + stts->nb_entries = 0; + stts->r_FirstSampleInEntry = stts->r_currentEntryIndex = 0; + stts->r_CurrentDTS = 0; + return GF_OK; + } + //we're removing the last sample + if (sampleNumber == stbl->SampleSize->sampleCount) { + ent = &stts->entries[stts->nb_entries-1]; + ent->sampleCount--; + if (!ent->sampleCount) stts->nb_entries--; + } else { + //unpack the DTSs... + DTSs = (u64*)malloc(sizeof(u64) * (stbl->SampleSize->sampleCount - 1)); + if (!DTSs) return GF_OUT_OF_MEM; + curDTS = 0; + sampNum = 0; + ent = NULL; + k=0; + for (i=0; inb_entries; i++) { + ent = & stts->entries[i]; + for (j=0; jsampleCount; j++) { + if (sampNum == sampleNumber - 1) { + k=1; + } else { + DTSs[sampNum-k] = curDTS; + } + curDTS += ent->sampleDelta; + sampNum ++; + } + } + j=0; + stts->nb_entries = 1; + stts->entries[0].sampleCount = 1; + if (stbl->SampleSize->sampleCount == 2) { + stts->entries[0].sampleDelta = LastAUDefDuration; + } else { + stts->entries[0].sampleDelta = (u32) DTSs[1] /*- DTS[0]==0 */; + } + for (i=0; iSampleSize->sampleCount-1; i++) { + if (i+1 == stbl->SampleSize->sampleCount-1) { + //and by default, our last sample has the same delta as the prev + // stts->entries[j].sampleCount++; + } else if (DTSs[i+1] - DTSs[i] == stts->entries[j].sampleDelta) { + stts->entries[j].sampleCount += 1; + } else { + j++; + stts->nb_entries++; + stts->entries[j].sampleCount = 1; + stts->entries[j].sampleDelta = (u32) (DTSs[i+1] - DTSs[i]); + } + } + stts->w_LastDTS = DTSs[stbl->SampleSize->sampleCount - 2]; + free(DTSs); + + } + //reset write the cache to the end + stts->w_currentSampleNum = stbl->SampleSize->sampleCount - 1; + //reset read the cache to the begining + stts->r_FirstSampleInEntry = stts->r_currentEntryIndex = 0; + stts->r_CurrentDTS = 0; + return GF_OK; +} + + +//always called before removing the sample from SampleSize +GF_Err stbl_RemoveCTS(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + GF_CompositionOffsetBox *ctts = stbl->CompositionOffset; + assert(ctts->unpack_mode); + + //last one... + if (stbl->SampleSize->sampleCount == 1) { + gf_isom_box_del((GF_Box *) ctts); + stbl->CompositionOffset = NULL; + return GF_OK; + } + + //the number of entries is NOT ALWAYS the number of samples ! + //instead, use the cache + //first case, we're removing a sample that was not added yet + if (sampleNumber > ctts->w_LastSampleNumber) return GF_OK; + + ctts->nb_entries--; + memmove(&ctts->entries[sampleNumber-1], &ctts->entries[sampleNumber], sizeof(GF_DttsEntry)*ctts->nb_entries); + + ctts->w_LastSampleNumber -= 1; + return GF_OK; +} + +GF_Err stbl_RemoveSize(GF_SampleSizeBox *stsz, u32 sampleNumber) +{ + //last sample + if (stsz->sampleCount == 1) { + if (stsz->sizes) free(stsz->sizes); + stsz->sizes = NULL; + stsz->sampleCount = 0; + return GF_OK; + } + //one single size + if (stsz->sampleSize) { + stsz->sampleCount -= 1; + return GF_OK; + } + if (sampleNumber < stsz->sampleCount) { + memcpy(stsz->sizes + sampleNumber - 1, stsz->sizes + sampleNumber, sizeof(u32) * (stsz->sampleCount - sampleNumber)); + } + stsz->sampleCount--; + return GF_OK; +} + +//always called after removing the sample from SampleSize +GF_Err stbl_RemoveChunk(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + u32 i, k; + u32 *offsets; + u64 *Loffsets; + GF_SampleToChunkBox *stsc = stbl->SampleToChunk; + + //remove the entry in SampleToChunk (1 <-> 1 in edit mode) + memmove(&stsc->entries[sampleNumber-1], &stsc->entries[sampleNumber], sizeof(GF_StscEntry)*(stsc->nb_entries-sampleNumber)); + stsc->nb_entries--; + + //update the firstchunk info + for (i=sampleNumber-1; i < stsc->nb_entries; i++) { + stsc->entries[i].firstChunk -= 1; + stsc->entries[i].nextChunk -= 1; + } + //update the cache + stbl->SampleToChunk->firstSampleInCurrentChunk = 1; + stbl->SampleToChunk->currentIndex = 0; + stbl->SampleToChunk->currentChunk = 1; + stbl->SampleToChunk->ghostNumber = 1; + + //realloc the chunk offset + if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + if (!stbl->SampleSize->sampleCount) { + free(((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets); + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets = NULL; + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->nb_entries = 0; + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->alloc_size = 0; + return GF_OK; + } + offsets = (u32*)malloc(sizeof(u32) * (stbl->SampleSize->sampleCount)); + if (!offsets) return GF_OUT_OF_MEM; + k=0; + for (i=0; iSampleSize->sampleCount+1; i++) { + if (i+1 == sampleNumber) { + k=1; + } else { + offsets[i-k] = ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets[i]; + } + } + free(((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets); + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets = offsets; + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->alloc_size = stbl->SampleSize->sampleCount; + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->nb_entries -= 1; + } else { + if (!stbl->SampleSize->sampleCount) { + free(((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets); + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets = NULL; + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->nb_entries = 0; + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->alloc_size = 0; + return GF_OK; + } + + Loffsets = (u64*)malloc(sizeof(u64) * (stbl->SampleSize->sampleCount)); + if (!Loffsets) return GF_OUT_OF_MEM; + k=0; + for (i=0; iSampleSize->sampleCount+1; i++) { + if (i+1 == sampleNumber) { + k=1; + } else { + Loffsets[i-k] = ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets[i]; + } + } + free(((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets); + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets = Loffsets; + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->alloc_size = stbl->SampleSize->sampleCount; + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->nb_entries -= 1; + } + return GF_OK; +} + + +GF_Err stbl_RemoveRAP(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + u32 i; + + GF_SyncSampleBox *stss = stbl->SyncSample; + //we remove the only one around... + if (stss->nb_entries == 1) { + if (stss->sampleNumbers[0] != sampleNumber) return GF_OK; + //free our numbers but don't delete (all samples are NON-sync + free(stss->sampleNumbers); + stss->sampleNumbers = NULL; + stss->r_LastSampleIndex = stss->r_LastSyncSample = 0; + stss->alloc_size = stss->nb_entries = 0; + return GF_OK; + } + //the real pain is that we may actually not have to change anything.. + for (i=0; inb_entries; i++) { + if (sampleNumber == stss->sampleNumbers[i]) goto found; + } + //nothing to do + return GF_OK; + +found: + //a small opt: the sample numbers are in order... + i++; + for (;inb_entries; i++) { + stss->sampleNumbers[i-1] = stss->sampleNumbers[i]; + } + stss->nb_entries -= 1; + return GF_OK; +} + +GF_Err stbl_RemoveRedundant(GF_SampleTableBox *stbl, u32 SampleNumber) +{ + u32 i; + + if (!stbl->SampleDep) return GF_OK; + if (stbl->SampleDep->sampleCount < SampleNumber) return GF_BAD_PARAM; + + i = stbl->SampleDep->sampleCount - SampleNumber; + if (i) memmove(&stbl->SampleDep->sample_info[SampleNumber-1], & stbl->SampleDep->sample_info[SampleNumber], sizeof(u8)*i); + stbl->SampleDep->sample_info = (u8*)realloc(stbl->SampleDep->sample_info, sizeof(u8) * (stbl->SampleDep->sampleCount-1)); + stbl->SampleDep->sampleCount-=1; + return GF_OK; +} + +GF_Err stbl_RemoveShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber) +{ + u32 i; + GF_StshEntry *ent; + + //we loop for the whole chain cause the spec doesn't say if we can have several + //shadows for 1 sample... + i=0; + while ((ent = (GF_StshEntry *)gf_list_enum(stsh->entries, &i))) { + if (ent->shadowedSampleNumber == sampleNumber) { + i--; + gf_list_rem(stsh->entries, i); + } + } + //reset the cache + stsh->r_LastEntryIndex = 0; + stsh->r_LastFoundSample = 0; + return GF_OK; +} + + +GF_Err stbl_SetPaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber, u8 bits) +{ + u8 *p; + //make sure the sample is a good one + if (SampleNumber > stbl->SampleSize->sampleCount) return GF_BAD_PARAM; + + //create the table + if (!stbl->PaddingBits) stbl->PaddingBits = (GF_PaddingBitsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FADB); + + //alloc + if (!stbl->PaddingBits->padbits || !stbl->PaddingBits->SampleCount) { + stbl->PaddingBits->SampleCount = stbl->SampleSize->sampleCount; + stbl->PaddingBits->padbits = (u8*)malloc(sizeof(u8)*stbl->PaddingBits->SampleCount); + if (!stbl->PaddingBits->padbits) return GF_OUT_OF_MEM; + memset(stbl->PaddingBits->padbits, 0, sizeof(u8)*stbl->PaddingBits->SampleCount); + } + //realloc (this is needed in case n out of k samples get padding added) + if (stbl->PaddingBits->SampleCount < stbl->SampleSize->sampleCount) { + p = (u8*)malloc(sizeof(u8) * stbl->SampleSize->sampleCount); + if (!p) return GF_OUT_OF_MEM; + //set everything to 0 + memset(p, 0, stbl->SampleSize->sampleCount); + //copy our previous table + memcpy(p, stbl->PaddingBits->padbits, stbl->PaddingBits->SampleCount); + free(stbl->PaddingBits->padbits); + stbl->PaddingBits->padbits = p; + stbl->PaddingBits->SampleCount = stbl->SampleSize->sampleCount; + } + stbl->PaddingBits->padbits[SampleNumber-1] = bits; + return GF_OK; +} + +GF_Err stbl_RemovePaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber) +{ + u8 *p; + u32 i, k; + + if (!stbl->PaddingBits) return GF_OK; + if (stbl->PaddingBits->SampleCount < SampleNumber) return GF_BAD_PARAM; + + //last sample - remove the table + if (stbl->PaddingBits->SampleCount == 1) { + gf_isom_box_del((GF_Box *) stbl->PaddingBits); + stbl->PaddingBits = NULL; + return GF_OK; + } + + //reallocate and check size by the way... + p = (u8 *)malloc(sizeof(u8) * (stbl->PaddingBits->SampleCount - 1)); + if (!p) return GF_OUT_OF_MEM; + + k=0; + for (i=0; iPaddingBits->SampleCount; i++) { + if (i+1 != SampleNumber) { + p[k] = stbl->PaddingBits->padbits[i]; + k++; + } + } + + stbl->PaddingBits->SampleCount -= 1; + free(stbl->PaddingBits->padbits); + stbl->PaddingBits->padbits = p; + return GF_OK; +} + + +GF_Err stbl_AddSampleFragment(GF_SampleTableBox *stbl, u32 sampleNumber, u16 size) +{ + GF_Err e; + u32 i, count; + GF_StsfEntry *ent; + GF_SampleFragmentBox *stsf; + stsf = stbl->Fragments; + + if (!stsf) { + //create table if any + stsf = (GF_SampleFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSF); + if (!stsf) return GF_OUT_OF_MEM; + e = stbl_AddBox(stbl, (GF_Box *) stsf); + if (e) return e; + } + + //check cache + if (!stsf->w_currentEntry || (stsf->w_currentEntry->SampleNumber < sampleNumber)) { + stsf->w_currentEntry = NULL; + stsf->w_currentEntryIndex = 0; + } + i = stsf->w_currentEntryIndex; + + count = gf_list_count(stsf->entryList); + for (; ientryList, i); + if (ent->SampleNumber > sampleNumber) { + ent = (GF_StsfEntry *)malloc(sizeof(GF_StsfEntry)); + if (!ent) return GF_OUT_OF_MEM; + memset(ent, 0, sizeof(GF_StsfEntry)); + ent->SampleNumber = sampleNumber; + gf_list_insert(stsf->entryList, ent, i); + stsf->w_currentEntry = ent; + stsf->w_currentEntryIndex = i; + goto ent_found; + } + else if (ent->SampleNumber == sampleNumber) { + stsf->w_currentEntry = ent; + stsf->w_currentEntryIndex = i; + goto ent_found; + } + } + //if we get here add a new entry + GF_SAFEALLOC(ent, GF_StsfEntry); + ent->SampleNumber = sampleNumber; + gf_list_add(stsf->entryList, ent); + stsf->w_currentEntry = ent; + stsf->w_currentEntryIndex = gf_list_count(stsf->entryList)-1; + +ent_found: + if (!ent->fragmentCount) { + ent->fragmentCount = 1; + ent->fragmentSizes = (u16*)malloc(sizeof(u16)); + if (!ent->fragmentSizes) return GF_OUT_OF_MEM; + ent->fragmentSizes[0] = size; + return GF_OK; + } + ent->fragmentSizes = (u16*)realloc(ent->fragmentSizes, sizeof(u16) * (ent->fragmentCount+1)); + if (!ent->fragmentSizes) return GF_OUT_OF_MEM; + ent->fragmentSizes[ent->fragmentCount] = size; + ent->fragmentCount += 1; + + return GF_OK; +} + +GF_Err stbl_RemoveSampleFragments(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + u32 i; + GF_StsfEntry *ent; + GF_SampleFragmentBox *stsf = stbl->Fragments; + + i=0; + while ((ent = (GF_StsfEntry *)gf_list_enum(stsf->entryList, &i))) { + if (ent->SampleNumber == sampleNumber) { + gf_list_rem(stsf->entryList, i-1); + if (ent->fragmentSizes) free(ent->fragmentSizes); + free(ent); + goto exit; + } + } +exit: + //empty table, remove it + if (!gf_list_count(stsf->entryList)) { + stbl->Fragments = NULL; + gf_isom_box_del((GF_Box *)stsf); + } + return GF_OK; +} + +GF_Err stbl_SampleSizeAppend(GF_SampleSizeBox *stsz, u32 data_size) +{ + u32 i; + if (!stsz || !stsz->sampleCount) return GF_BAD_PARAM; + + //we must realloc our table + if (stsz->sampleSize) { + stsz->sizes = (u32*)malloc(sizeof(u32)*stsz->sampleCount); + if (!stsz->sizes) return GF_OUT_OF_MEM; + for (i=0; isampleCount; i++) stsz->sizes[i] = stsz->sampleSize; + stsz->sampleSize = 0; + } + stsz->sizes[stsz->sampleCount-1] += data_size; + return GF_OK; +} + +#endif //GPAC_READ_ONLY + + + +void stbl_AppendTime(GF_SampleTableBox *stbl, u32 duration) +{ + GF_TimeToSampleBox *stts = stbl->TimeToSample; + + if (stts->nb_entries) { + if (stts->entries[stts->nb_entries-1].sampleDelta == duration) { + stts->entries[stts->nb_entries-1].sampleCount += 1; + return; + } + } + if (stts->nb_entries==stts->alloc_size) { + ALLOC_INC(stts->alloc_size); + stts->entries = realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size); + if (!stts->entries) return; + } + stts->entries[stts->nb_entries].sampleCount = 1; + stts->entries[stts->nb_entries].sampleDelta = duration; + stts->nb_entries++; +} + +void stbl_AppendSize(GF_SampleTableBox *stbl, u32 size) +{ + u32 i; + + if (!stbl->SampleSize->sampleCount) { + stbl->SampleSize->sampleSize = size; + stbl->SampleSize->sampleCount = 1; + return; + } + if (stbl->SampleSize->sampleSize && (stbl->SampleSize->sampleSize==size)) { + stbl->SampleSize->sampleCount += 1; + return; + } + if (!stbl->SampleSize->sizes || (stbl->SampleSize->sampleCount==stbl->SampleSize->alloc_size)) { + Bool init_table = (stbl->SampleSize->sizes==NULL) ? 1 : 0; + ALLOC_INC(stbl->SampleSize->alloc_size); + stbl->SampleSize->sizes = (u32 *)realloc(stbl->SampleSize->sizes, sizeof(u32)*stbl->SampleSize->alloc_size); + if (!stbl->SampleSize->sizes) return; + + if (init_table) { + for (i=0; iSampleSize->sampleCount;i++) + stbl->SampleSize->sizes[i] = stbl->SampleSize->sampleSize; + } + } + stbl->SampleSize->sampleSize = 0; + stbl->SampleSize->sizes[stbl->SampleSize->sampleCount] = size; + stbl->SampleSize->sampleCount += 1; +} + +void stbl_AppendChunk(GF_SampleTableBox *stbl, u64 offset) +{ + GF_ChunkOffsetBox *stco; + GF_ChunkLargeOffsetBox *co64; + u32 *new_offsets, i; + u64 *off_64; + + //we may have to convert the table... + if (stbl->ChunkOffset->type==GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset; + + if (offset>0xFFFFFFFF) { + co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + co64->nb_entries = stco->nb_entries + 1; + co64->offsets = (u64*)malloc(sizeof(u64) * co64->nb_entries); + if (!co64->offsets) return; + for (i=0; inb_entries; i++) co64->offsets[i] = stco->offsets[i]; + co64->offsets[i] = offset; + gf_isom_box_del(stbl->ChunkOffset); + stbl->ChunkOffset = (GF_Box *) co64; + return; + } + //we're fine + new_offsets = (u32*)malloc(sizeof(u32)*(stco->nb_entries+1)); + if (!new_offsets) return; + for (i=0; inb_entries; i++) new_offsets[i] = stco->offsets[i]; + new_offsets[i] = (u32) offset; + if (stco->offsets) free(stco->offsets); + stco->offsets = new_offsets; + stco->nb_entries += 1; + stco->alloc_size = stco->nb_entries; + } + //large offsets + else { + co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset; + off_64 = (u64*)malloc(sizeof(u32)*(co64->nb_entries+1)); + if (!off_64) return; + for (i=0; inb_entries; i++) off_64[i] = co64->offsets[i]; + off_64[i] = offset; + if (co64->offsets) free(co64->offsets); + co64->offsets = off_64; + co64->nb_entries += 1; + co64->alloc_size = co64->nb_entries; + } +} + +void stbl_AppendSampleToChunk(GF_SampleTableBox *stbl, u32 DescIndex, u32 samplesInChunk) +{ + u32 nextChunk; + GF_SampleToChunkBox *stsc= stbl->SampleToChunk; + GF_StscEntry *ent; + + nextChunk = ((GF_ChunkOffsetBox *) stbl->ChunkOffset)->nb_entries; + + if (stsc->nb_entries) { + ent = &stsc->entries[stsc->nb_entries-1]; + //good we can use this one + if ( (ent->sampleDescriptionIndex == DescIndex) && (ent->samplesPerChunk==samplesInChunk)) + return; + + //set the next chunk btw ... + ent->nextChunk = nextChunk; + } + if (stsc->nb_entries==stsc->alloc_size) { + ALLOC_INC(stsc->alloc_size); + stsc->entries = realloc(stsc->entries, sizeof(GF_StscEntry)*stsc->alloc_size); + if (!stsc->entries) return; + } + //ok we need a new entry - this assumes this function is called AFTER AppendChunk + ent = &stsc->entries[stsc->nb_entries]; + ent->firstChunk = nextChunk; + ent->sampleDescriptionIndex = DescIndex; + ent->samplesPerChunk = samplesInChunk; + ent->isEdited = 0; + stsc->nb_entries++; +} + +//called AFTER AddSize +void stbl_AppendRAP(GF_SampleTableBox *stbl, u8 isRap) +{ + u32 i; + + //no sync table + if (!stbl->SyncSample) { + //all samples RAP - no table + if (isRap) return; + + //nope, create one + stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS); + if (stbl->SampleSize->sampleCount > 1) { + stbl->SyncSample->sampleNumbers = (u32*)malloc(sizeof(u32) * (stbl->SampleSize->sampleCount-1)); + if (!stbl->SyncSample->sampleNumbers) return; + for (i=0; iSampleSize->sampleCount-1; i++) + stbl->SyncSample->sampleNumbers[i] = i+1; + + } + stbl->SyncSample->nb_entries = stbl->SampleSize->sampleCount-1; + stbl->SyncSample->alloc_size = stbl->SyncSample->nb_entries; + return; + } + if (!isRap) return; + + if (stbl->SyncSample->alloc_size == stbl->SyncSample->nb_entries) { + ALLOC_INC(stbl->SyncSample->alloc_size); + stbl->SyncSample->sampleNumbers = (u32*) realloc(stbl->SyncSample->sampleNumbers, sizeof(u32) * stbl->SyncSample->alloc_size); + if (!stbl->SyncSample->sampleNumbers) return; + } + stbl->SyncSample->sampleNumbers[stbl->SyncSample->nb_entries] = stbl->SampleSize->sampleCount; + stbl->SyncSample->nb_entries += 1; +} + +void stbl_AppendPadding(GF_SampleTableBox *stbl, u8 padding) +{ + u32 i; + u8 *pad_bits; + if (!stbl->PaddingBits) stbl->PaddingBits = (GF_PaddingBitsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FADB); + + pad_bits = (u8*)malloc(sizeof(u8) * stbl->SampleSize->sampleCount); + if (!pad_bits) return; + memset(pad_bits, 0, sizeof(pad_bits)); +// for (i=0; iSampleSize->sampleCount; i++) pad_bits[i] = 0; + for (i=0; iPaddingBits->SampleCount; i++) pad_bits[i] = stbl->PaddingBits->padbits[i]; + pad_bits[stbl->SampleSize->sampleCount-1] = padding; + if (stbl->PaddingBits->padbits) free(stbl->PaddingBits->padbits); + stbl->PaddingBits->padbits = pad_bits; + stbl->PaddingBits->SampleCount = stbl->SampleSize->sampleCount; +} + +void stbl_AppendCTSOffset(GF_SampleTableBox *stbl, u32 CTSOffset) +{ + GF_CompositionOffsetBox *ctts; + + if (!stbl->CompositionOffset) stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS); + + ctts = stbl->CompositionOffset; + + if (ctts->nb_entries && (ctts->entries[ctts->nb_entries-1].decodingOffset == CTSOffset) ){ + ctts->entries[ctts->nb_entries-1].sampleCount++; + return; + } + if (ctts->nb_entries==ctts->alloc_size) { + ALLOC_INC(ctts->alloc_size); + ctts->entries = realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + } + ctts->entries[ctts->nb_entries].decodingOffset = CTSOffset; + ctts->entries[ctts->nb_entries].sampleCount = 1; + ctts->nb_entries++; +} + +void stbl_AppendDegradation(GF_SampleTableBox *stbl, u16 DegradationPriority) +{ + if (!stbl->DegradationPriority) stbl->DegradationPriority = (GF_DegradationPriorityBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STDP); + + stbl->DegradationPriority->priorities = (u16 *)realloc(stbl->DegradationPriority->priorities, sizeof(u16) * stbl->SampleSize->sampleCount); + stbl->DegradationPriority->priorities[stbl->SampleSize->sampleCount-1] = DegradationPriority; + stbl->DegradationPriority->nb_entries = stbl->SampleSize->sampleCount; +} + +void stbl_AppendDepType(GF_SampleTableBox *stbl, u32 DepType) +{ + if (!stbl->SampleDep) stbl->SampleDep= (GF_SampleDependencyTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SDTP); + stbl->SampleDep->sample_info = (u8*)realloc(stbl->SampleDep->sample_info, sizeof(u8)*stbl->SampleSize->sampleCount ); + stbl->SampleDep->sample_info[stbl->SampleDep->sampleCount] = DepType; + stbl->SampleDep->sampleCount = stbl->SampleSize->sampleCount; + +} + + + +//This functions unpack the offset for easy editing, eg each sample +//is contained in one chunk... +GF_Err stbl_UnpackOffsets(GF_SampleTableBox *stbl) +{ + GF_Err e; + u8 isEdited; + u32 i, chunkNumber, sampleDescIndex; + u64 dataOffset; + GF_StscEntry *ent; + GF_ChunkOffsetBox *stco_tmp; + GF_ChunkLargeOffsetBox *co64_tmp; + GF_SampleToChunkBox *stsc_tmp; + + if (!stbl) return GF_ISOM_INVALID_FILE; + + //we should have none of the mandatory boxes (allowed in the spec) + if (!stbl->ChunkOffset && !stbl->SampleDescription && !stbl->SampleSize && !stbl->SampleToChunk && !stbl->TimeToSample) + return GF_OK; + /*empty track (just created)*/ + if (!stbl->SampleToChunk && !stbl->TimeToSample) return GF_OK; + + //or all the mandatory ones ... + if (!stbl->ChunkOffset || !stbl->SampleDescription || !stbl->SampleSize || !stbl->SampleToChunk || !stbl->TimeToSample) + return GF_ISOM_INVALID_FILE; + + //do we need to unpack? Not if we have only one sample per chunk. + if (stbl->SampleSize->sampleCount == stbl->SampleToChunk->nb_entries) return GF_OK; + + //check the offset type and create a new table... + if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + co64_tmp = NULL; + stco_tmp = (GF_ChunkOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO); + if (!stco_tmp) return GF_OUT_OF_MEM; + stco_tmp->nb_entries = stbl->SampleSize->sampleCount; + stco_tmp->offsets = (u32*)malloc(stco_tmp->nb_entries * sizeof(u32)); + if (!stco_tmp->offsets) { + gf_isom_box_del((GF_Box*)stco_tmp); + return GF_OUT_OF_MEM; + } + stco_tmp->alloc_size = stco_tmp->nb_entries; + } else if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_CO64) { + stco_tmp = NULL; + co64_tmp = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + if (!co64_tmp) return GF_OUT_OF_MEM; + co64_tmp->nb_entries = stbl->SampleSize->sampleCount; + co64_tmp->offsets = (u64*)malloc(co64_tmp->nb_entries * sizeof(u64)); + if (!co64_tmp->offsets) { + gf_isom_box_del((GF_Box*)co64_tmp); + return GF_OUT_OF_MEM; + } + co64_tmp->alloc_size = co64_tmp->nb_entries; + } else { + return GF_ISOM_INVALID_FILE; + } + + //create a new SampleToChunk table + stsc_tmp = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); + + stsc_tmp->nb_entries = stsc_tmp->alloc_size = stbl->SampleSize->sampleCount; + stsc_tmp->entries = malloc(sizeof(GF_StscEntry)*stsc_tmp->nb_entries); + if (!stsc_tmp->entries) return GF_OUT_OF_MEM; + + //OK write our two tables... + ent = NULL; + for (i = 0; i < stbl->SampleSize->sampleCount; i++) { + //get the data info for the sample + e = stbl_GetSampleInfos(stbl, i+1, &dataOffset, &chunkNumber, &sampleDescIndex, &isEdited); + if (e) goto err_exit; + ent = &stsc_tmp->entries[i]; + ent->isEdited = 0; + ent->sampleDescriptionIndex = sampleDescIndex; + //here's the trick: each sample is in ONE chunk + ent->firstChunk = i+1; + ent->nextChunk = i+2; + ent->samplesPerChunk = 1; + if (stco_tmp) { + stco_tmp->offsets[i] = (u32) dataOffset; + } else { + co64_tmp->offsets[i] = dataOffset; + } + } + //close the list + if (ent) ent->nextChunk = 0; + + + //done, remove our previous tables + gf_isom_box_del(stbl->ChunkOffset); + gf_isom_box_del((GF_Box *)stbl->SampleToChunk); + //and set these ones... + if (stco_tmp) { + stbl->ChunkOffset = (GF_Box *)stco_tmp; + } else { + stbl->ChunkOffset = (GF_Box *)co64_tmp; + } + stbl->SampleToChunk = stsc_tmp; + stbl->SampleToChunk->currentIndex = 0; + stbl->SampleToChunk->currentChunk = 0; + stbl->SampleToChunk->firstSampleInCurrentChunk = 0; + return GF_OK; + +err_exit: + if (stco_tmp) gf_isom_box_del((GF_Box *) stco_tmp); + if (co64_tmp) gf_isom_box_del((GF_Box *) co64_tmp); + if (stsc_tmp) gf_isom_box_del((GF_Box *) stsc_tmp); + return e; +} + +#ifndef GPAC_READ_ONLY + + + +static GFINLINE GF_Err stbl_AddOffset(GF_Box **a, u64 offset) +{ + GF_ChunkOffsetBox *stco; + GF_ChunkLargeOffsetBox *co64; + u32 i; + + if ((*a)->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *) *a; + //if dataOffset is bigger than 0xFFFFFFFF, move to LARGE offset + if (offset > 0xFFFFFFFF) { + co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + if (!co64) return GF_OUT_OF_MEM; + co64->nb_entries = stco->nb_entries + 1; + co64->offsets = (u64*)malloc(co64->nb_entries * sizeof(u64)); + if (!co64->offsets) { + gf_isom_box_del((GF_Box *)co64); + return GF_OUT_OF_MEM; + } + for (i = 0; i< co64->nb_entries - 1; i++) { + co64->offsets[i] = (u64) stco->offsets[i]; + } + co64->offsets[i] = offset; + //delete the box... + gf_isom_box_del(*a); + *a = (GF_Box *)co64; + return GF_OK; + } + //OK, stick with regular... + if (stco->nb_entries==stco->alloc_size) { + ALLOC_INC(stco->alloc_size); + stco->offsets = (u32*)realloc(stco->offsets, stco->alloc_size * sizeof(u32)); + if (!stco->offsets) return GF_OUT_OF_MEM; + } + + stco->offsets[stco->nb_entries] = (u32) offset; + stco->nb_entries += 1; + } else { + //this is a large offset + co64 = (GF_ChunkLargeOffsetBox *) *a; + if (co64->nb_entries==co64->alloc_size) { + ALLOC_INC(co64->alloc_size); + co64->offsets = (u64*)realloc(co64->offsets, co64->alloc_size * sizeof(u64)); + if (!co64->offsets) return GF_OUT_OF_MEM; + } + co64->offsets[co64->nb_entries] = offset; + co64->nb_entries += 1; + } + return GF_OK; +} + +//This function packs the offset after easy editing, eg samples +//are re-arranged in chunks according to the chunkOffsets +//NOTE: this has to be called once interleaving or whatever is done and +//the final MDAT is written!!! +GF_Err stbl_SetChunkAndOffset(GF_SampleTableBox *stbl, u32 sampleNumber, u32 StreamDescIndex, GF_SampleToChunkBox *the_stsc, GF_Box **the_stco, u64 data_offset, u8 forceNewChunk) +{ + GF_Err e; + u8 newChunk; + GF_StscEntry *ent, *newEnt, *cur_ent; + + if (!stbl) return GF_ISOM_INVALID_FILE; + + newChunk = 0; + //do we need a new chunk ??? For that, we need + //1 - make sure this sample data is contiguous to the prev one + + //force new chunk is set during writing (flat / interleaved) + //it is set to 1 when data is not contiguous in the media (eg, interleaving) + //when writing flat files, it is never used + if (forceNewChunk) newChunk = 1; + + cur_ent = NULL; + //2 - make sure we have the table inited (i=0) + if (! the_stsc->entries) { + newChunk = 1; + } else { + cur_ent = &the_stsc->entries[the_stsc->nb_entries - 1]; + //3 - make sure we do not exceed the MaxSamplesPerChunk and we have the same descIndex + if (StreamDescIndex != cur_ent->sampleDescriptionIndex) + newChunk = 1; + if (stbl->MaxSamplePerChunk && cur_ent->samplesPerChunk == stbl->MaxSamplePerChunk) + newChunk = 1; + } + + //no need for a new chunk + if (!newChunk) { + cur_ent->samplesPerChunk += 1; + return GF_OK; + } + + //OK, we have to create a new chunk... + //check if we can remove the current sampleToChunk entry (same properties) + if (the_stsc->nb_entries > 1) { + ent = &the_stsc->entries[the_stsc->nb_entries - 2]; + if ( (ent->sampleDescriptionIndex == cur_ent->sampleDescriptionIndex) + && (ent->samplesPerChunk == cur_ent->samplesPerChunk) + ) { + //OK, it's the same SampleToChunk, so delete it + ent->nextChunk = cur_ent->firstChunk; + the_stsc->nb_entries--; + } + } + + //add our offset + e = stbl_AddOffset(the_stco, data_offset); + if (e) return e; + + if (the_stsc->nb_entries==the_stsc->alloc_size) { + ALLOC_INC(the_stsc->alloc_size); + the_stsc->entries = realloc(the_stsc->entries, sizeof(GF_StscEntry)*the_stsc->alloc_size); + if (!the_stsc->entries) return GF_OUT_OF_MEM; + } + //create a new entry (could be the first one, BTW) + newEnt = &the_stsc->entries[the_stsc->nb_entries]; + + //get the first chunk value + if ((*the_stco)->type == GF_ISOM_BOX_TYPE_STCO) { + newEnt->firstChunk = ((GF_ChunkOffsetBox *) (*the_stco) )->nb_entries; + } else { + newEnt->firstChunk = ((GF_ChunkLargeOffsetBox *) (*the_stco) )->nb_entries; + } + newEnt->sampleDescriptionIndex = StreamDescIndex; + newEnt->samplesPerChunk = 1; + newEnt->nextChunk = 0; + //if we already have an entry, adjust its next chunk to point to our new chunk + if (the_stsc->nb_entries) + the_stsc->entries[the_stsc->nb_entries-1].nextChunk = newEnt->firstChunk; + the_stsc->nb_entries++; + return GF_OK; +} + + +GF_Err gf_isom_refresh_size_info(GF_ISOFile *file, u32 trackNumber) +{ + u32 i, size; + GF_TrackBox *trak; + GF_SampleSizeBox *stsz; + trak = gf_isom_get_track_from_file(file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsz = trak->Media->information->sampleTable->SampleSize; + if (stsz->sampleSize || !stsz->sampleCount) return GF_OK; + + size = stsz->sizes[0]; + for (i=1; isampleCount; i++) { + if (stsz->sizes[i] != size) { + size = 0; + break; + } + } + if (size) { + free(stsz->sizes); + stsz->sizes = NULL; + stsz->sampleSize = size; + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + diff --git a/src/isomedia/track.c b/src/isomedia/track.c new file mode 100644 index 0000000..ec80e9e --- /dev/null +++ b/src/isomedia/track.c @@ -0,0 +1,791 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +GF_TrackBox *GetTrackbyID(GF_MovieBox *moov, u32 TrackID) +{ + GF_TrackBox *trak; + u32 i; + if (!moov) return NULL; + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) { + if (trak->Header->trackID == TrackID) return trak; + } + return NULL; +} + +GF_TrackBox *gf_isom_get_track(GF_MovieBox *moov, u32 trackNumber) +{ + GF_TrackBox *trak; + if (!moov || !trackNumber || (trackNumber > gf_list_count(moov->trackList))) return NULL; + trak = (GF_TrackBox*)gf_list_get(moov->trackList, trackNumber - 1); + return trak; + +} + +//get the number of a track given its ID +//return 0 if not found error +u32 gf_isom_get_tracknum_from_id(GF_MovieBox *moov, u32 trackID) +{ + u32 i; + GF_TrackBox *trak; + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) { + if (trak->Header->trackID == trackID) return i; + } + return 0; +} + +//extraction of the ESD from the track +GF_Err GetESD(GF_MovieBox *moov, u32 trackID, u32 StreamDescIndex, GF_ESD **outESD) +{ + GF_Err e; + GF_ESD *esd; + GF_SampleTableBox *stbl; + GF_TrackBox *trak, *OCRTrack; + GF_TrackReferenceTypeBox *dpnd; + GF_SLConfig *slc; + GF_MPEGSampleEntryBox *entry; + + dpnd = NULL; + *outESD = NULL; + + trak = gf_isom_get_track(moov, gf_isom_get_tracknum_from_id(moov, trackID)); + if (!trak) return GF_ISOM_INVALID_FILE; + + e = Media_GetESD(trak->Media, StreamDescIndex, &esd, 0); + if (e) return e; + + e = Media_GetSampleDesc(trak->Media, StreamDescIndex, (GF_SampleEntryBox **) &entry, NULL); + if (e) return e; + //set the ID + esd->ESID = trackID; + + //find stream dependencies + e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_DPND , &dpnd); + if (e) return e; + if (dpnd) { + //ONLY ONE STREAM DEPENDENCY IS ALLOWED + if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; + //fix the spec: where is the index located ?? + esd->dependsOnESID = dpnd->trackIDs[0]; + } else { + esd->dependsOnESID = 0; + } + + //OK, get the OCR (in a REAL MP4File, OCR is 0 in ESD and is specified through track reference + dpnd = NULL; + OCRTrack = NULL; + //find OCR dependencies + e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_SYNC, &dpnd); + if (e) return e; + if (dpnd) { + if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; + esd->OCRESID = dpnd->trackIDs[0]; + OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]); + + while (OCRTrack) { + /*if we have a dependency on a track that doesn't have OCR dep, remove that dependency*/ + e = Track_FindRef(OCRTrack, GF_ISOM_BOX_TYPE_SYNC, &dpnd); + if (e || !dpnd || !dpnd->trackIDCount) { + OCRTrack = NULL; + goto default_sync; + } + /*this is explicit desync*/ + if (dpnd && ((dpnd->trackIDs[0]==0) || (dpnd->trackIDs[0]==OCRTrack->Header->trackID))) break; + /*loop in OCRs, break it*/ + if (esd->ESID == OCRTrack->Header->trackID) { + OCRTrack = NULL; + goto default_sync; + } + /*check next*/ + OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]); + } + if (!OCRTrack) goto default_sync; + } else { +default_sync: + /*all tracks are sync'ed by default*/ + if (trak->moov->mov->es_id_default_sync<0) { + if (esd->OCRESID) + trak->moov->mov->es_id_default_sync = esd->OCRESID; + else + trak->moov->mov->es_id_default_sync = esd->ESID; + } + if (trak->moov->mov->es_id_default_sync) esd->OCRESID = (u16) trak->moov->mov->es_id_default_sync; + /*cf ESD writer*/ + if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; + } + + + + //update the IPI stuff if needed + if (esd->ipiPtr != NULL) { + dpnd = NULL; + e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_IPIR, &dpnd); + if (e) return e; + if (dpnd) { + if (esd->ipiPtr->tag != GF_ODF_ISOM_IPI_PTR_TAG) return GF_ISOM_INVALID_FILE; + //OK, retrieve the ID: the IPI_ES_Id is currently the ref track + esd->ipiPtr->IPI_ES_Id = dpnd->trackIDs[esd->ipiPtr->IPI_ES_Id - 1]; + //and change the tag + esd->ipiPtr->tag = GF_ODF_IPI_PTR_TAG; + } else { + return GF_ISOM_INVALID_FILE; + } + } + + if ((trak->Media->mediaHeader->packedLanguage[0] != 'u') + || (trak->Media->mediaHeader->packedLanguage[1] != 'n') + || (trak->Media->mediaHeader->packedLanguage[2] != 'd') ) { + if (!esd->langDesc) esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG); + + esd->langDesc->langCode = trak->Media->mediaHeader->packedLanguage[0]; esd->langDesc->langCode <<= 8; + esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[1]; esd->langDesc->langCode <<= 8; + esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[2]; + } + + /*normally all files shall be stored with predefined=SLPredef_MP4, but of course some are broken (philips) + so we just check the ESD_URL. If set, use the given cfg, otherwise always rewrite it*/ + if (esd->URLString != NULL) { + *outESD = esd; + return GF_OK; + } + + //if we are in publishing mode and we have an SLConfig specified, use it as is + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4V: + slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4A: + slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4S: + slc = entry->slc; + break; + default: + slc = NULL; + break; + } + if (slc) { + gf_odf_desc_del((GF_Descriptor *)esd->slConfig); + gf_odf_desc_copy((GF_Descriptor *)slc, (GF_Descriptor **)&esd->slConfig); + *outESD = esd; + return GF_OK; + } + //otherwise use the regular mapping + + //this is a desc for a media in the file, let's rewrite some param + esd->slConfig->timestampLength = 32; + esd->slConfig->timestampResolution = trak->Media->mediaHeader->timeScale; + //NO OCR from MP4File streams (eg, constant OC Res one) + esd->slConfig->OCRLength = 0; + if (OCRTrack) { + esd->slConfig->OCRResolution = OCRTrack->Media->mediaHeader->timeScale; + } else { + esd->slConfig->OCRResolution = 0; + } + stbl = trak->Media->information->sampleTable; + // a little optimization here: if all our samples are sync, + //set the RAPOnly to true... for external users... + if (! stbl->SyncSample) { + esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1; + esd->slConfig->useRandomAccessPointFlag = 0; + } else { + esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0; + //signal we are NOT using sync points if no info is present in the table + esd->slConfig->useRandomAccessPointFlag = stbl->SyncSample->nb_entries ? 1 : 0; + } + //do we have DegradationPriority ? + if (stbl->DegradationPriority) { + esd->slConfig->degradationPriorityLength = 15; + } else { + esd->slConfig->degradationPriorityLength = 0; + } + //paddingBits + if (stbl->PaddingBits) { + esd->slConfig->usePaddingFlag = 1; + } + //change to support reflecting OD streams + esd->slConfig->useAccessUnitEndFlag = 1; + esd->slConfig->useAccessUnitStartFlag = 1; + + //signal we do have padding flag (since we only use logical SL packet + //the user can decide whether to use the info or not + esd->slConfig->usePaddingFlag = stbl->PaddingBits ? 1 : 0; + + //same with degradation priority + esd->slConfig->degradationPriorityLength = stbl->DegradationPriority ? 32 : 0; + + //this new SL will be OUT OF THE FILE. Let's set its predefined to 0 + esd->slConfig->predefined = 0; + + *outESD = esd; + return GF_OK; +} + + +//extraction of the ESD from the track for the given time +GF_Err GetESDForTime(GF_MovieBox *moov, u32 trackID, u64 CTS, GF_ESD **outESD) +{ + GF_Err e; + u32 sampleDescIndex; + GF_TrackBox *trak; + + trak = gf_isom_get_track(moov, gf_isom_get_tracknum_from_id(moov, trackID)); + if (!trak) return GF_ISOM_INVALID_FILE; + + e = Media_GetSampleDescIndex(trak->Media, CTS, &sampleDescIndex ); + if (e) return e; + return GetESD(moov, trackID, sampleDescIndex, outESD); +} + + +GF_Err Track_FindRef(GF_TrackBox *trak, u32 ReferenceType, GF_TrackReferenceTypeBox **dpnd) +{ + GF_TrackReferenceBox *ref; + GF_TrackReferenceTypeBox *a; + u32 i; + if (! trak) return GF_BAD_PARAM; + if (! trak->References) { + *dpnd = NULL; + return GF_OK; + } + ref = trak->References; + i=0; + while ((a = (GF_TrackReferenceTypeBox *)gf_list_enum(ref->boxList, &i))) { + if (a->reference_type == ReferenceType) { + *dpnd = a; + return GF_OK; + } + } + *dpnd = NULL; + return GF_OK; +} + +Bool Track_IsMPEG4Stream(u32 HandlerType) +{ + switch (HandlerType) { + case GF_ISOM_MEDIA_VISUAL: + case GF_ISOM_MEDIA_AUDIO: + case GF_ISOM_MEDIA_SUBPIC: + case GF_ISOM_MEDIA_OD: + case GF_ISOM_MEDIA_OCR: + case GF_ISOM_MEDIA_SCENE: + case GF_ISOM_MEDIA_MPEG7: + case GF_ISOM_MEDIA_OCI: + case GF_ISOM_MEDIA_IPMP: + case GF_ISOM_MEDIA_MPEGJ: + case GF_ISOM_MEDIA_ESM: + return 1; + /*Timedtext is NOT an MPEG-4 stream*/ + default: + return 0; + } +} + + +GF_Err SetTrackDuration(GF_TrackBox *trak) +{ + u64 trackDuration; + u32 i; + GF_EdtsEntry *ent; + GF_EditListBox *elst; + GF_Err e; + + //the total duration is the media duration: adjust it in case... + e = Media_SetDuration(trak); + if (e) return e; + + //if we have an edit list, the duration is the sum of all the editList + //entries' duration (always expressed in MovieTimeScale) + if (trak->editBox && trak->editBox->editList) { + trackDuration = 0; + elst = trak->editBox->editList; + i=0; + while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) { + trackDuration += ent->segmentDuration; + } + } else { + //assert the timeScales are non-NULL + if (!trak->moov->mvhd->timeScale && !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE; + trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale; + } + trak->Header->duration = trackDuration; + trak->Header->modificationTime = gf_isom_get_mp4time(); + return GF_OK; +} + + +#ifndef GPAC_ISOM_NO_FRAGMENTS + +GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, u64 *moof_offset) +{ + u32 i, j, chunk_size; + u64 base_offset, data_offset; + u32 def_duration, DescIndex, def_size, def_flags; + u32 duration, size, flags, cts_offset; + u8 pad, sync; + u16 degr; + GF_TrackFragmentRunBox *trun; + GF_TrunEntry *ent; + + void stbl_AppendTime(GF_SampleTableBox *stbl, u32 duration); + void stbl_AppendSize(GF_SampleTableBox *stbl, u32 size); + void stbl_AppendChunk(GF_SampleTableBox *stbl, u64 offset); + void stbl_AppendSampleToChunk(GF_SampleTableBox *stbl, u32 DescIndex, u32 samplesInChunk); + void stbl_AppendCTSOffset(GF_SampleTableBox *stbl, u32 CTSOffset); + void stbl_AppendRAP(GF_SampleTableBox *stbl, u8 isRap); + void stbl_AppendPadding(GF_SampleTableBox *stbl, u8 padding); + void stbl_AppendDegradation(GF_SampleTableBox *stbl, u16 DegradationPriority); + + + //setup all our defaults + DescIndex = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DESC) ? traf->tfhd->sample_desc_index : traf->trex->def_sample_desc_index; + def_duration = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DUR) ? traf->tfhd->def_sample_duration : traf->trex->def_sample_duration; + def_size = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_SIZE) ? traf->tfhd->def_sample_size : traf->trex->def_sample_size; + def_flags = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) ? traf->tfhd->def_sample_flags : traf->trex->def_sample_flags; + + //locate base offset + base_offset = (traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET) ? traf->tfhd->base_data_offset : *moof_offset; + + chunk_size = 0; + + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + //merge the run + for (j=0;jsample_count; j++) { + ent = (GF_TrunEntry*)gf_list_get(trun->entries, j); + size = def_size; + duration = def_duration; + flags = def_flags; + + if (ent) { + if (trun->flags & GF_ISOM_TRUN_DURATION) duration = ent->Duration; + if (trun->flags & GF_ISOM_TRUN_SIZE) size = ent->size; + if (trun->flags & GF_ISOM_TRUN_FLAGS) { + flags = ent->flags; + } else if (!j && (trun->flags & GF_ISOM_TRUN_FIRST_FLAG)) { + flags = trun->first_sample_flags; + } + } + //add size first + stbl_AppendSize(trak->Media->information->sampleTable, size); + //then TS + stbl_AppendTime(trak->Media->information->sampleTable, duration); + //add chunk on first sample + if (!j) { + data_offset = base_offset; + //aggregated offset + if (!(traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET)) data_offset += chunk_size; + + if (trun->flags & GF_ISOM_TRUN_DATA_OFFSET) data_offset += trun->data_offset; + + stbl_AppendChunk(trak->Media->information->sampleTable, data_offset); + //then sampleToChunk + stbl_AppendSampleToChunk(trak->Media->information->sampleTable, + DescIndex, trun->sample_count); + } + chunk_size += size; + + + //CTS + cts_offset = (trun->flags & GF_ISOM_TRUN_CTS_OFFSET) ? ent->CTS_Offset : 0; + stbl_AppendCTSOffset(trak->Media->information->sampleTable, cts_offset); + + //flags + sync = GF_ISOM_GET_FRAG_SYNC(flags); + stbl_AppendRAP(trak->Media->information->sampleTable, sync); + pad = GF_ISOM_GET_FRAG_PAD(flags); + if (pad) stbl_AppendPadding(trak->Media->information->sampleTable, pad); + degr = GF_ISOM_GET_FRAG_DEG(flags); + if (degr) stbl_AppendDegradation(trak->Media->information->sampleTable, degr); + } + } + //end of the fragment, update offset + *moof_offset += chunk_size; + return GF_OK; +} + +#endif + + +#ifndef GPAC_READ_ONLY + +//used to check if a TrackID is available +u8 RequestTrack(GF_MovieBox *moov, u32 TrackID) +{ + u32 i; + GF_TrackBox *trak; + + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) { + if (trak->Header->trackID == TrackID) { + gf_isom_set_last_error(moov->mov, GF_BAD_PARAM); + return 0; + } + } + return 1; +} + +GF_Err Track_RemoveRef(GF_TrackBox *trak, u32 ReferenceType) +{ + GF_TrackReferenceBox *ref; + GF_Box *a; + u32 i; + if (! trak) return GF_BAD_PARAM; + if (! trak->References) return GF_OK; + ref = trak->References; + i=0; + while ((a = (GF_Box *)gf_list_enum(ref->boxList, &i))) { + if (a->type == ReferenceType) { + gf_isom_box_del(a); + gf_list_rem(ref->boxList, i-1); + return GF_OK; + } + } + return GF_OK; +} + +GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale) +{ + GF_MediaHeaderBox *mdhd; + GF_Box *mediaInfo; + GF_HandlerBox *hdlr; + GF_MediaInformationBox *minf; + GF_DataInformationBox *dinf; + GF_SampleTableBox *stbl; + GF_SampleDescriptionBox *stsd; + GF_DataReferenceBox *dref; + char *str; + + GF_Err e; + + if (*mdia || !mdia) return GF_BAD_PARAM; + + e = GF_OK; + minf = NULL; + mdhd = NULL; + hdlr = NULL; + dinf = NULL; + stbl = NULL; + stsd = NULL; + dref = NULL; + mediaInfo = NULL; + + //first create the media + *mdia = (GF_MediaBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDIA); + mdhd = (GF_MediaHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDHD); + + //"handler name" is for debugging purposes. Let's stick our name here ;) + switch (MediaType) { + case GF_ISOM_MEDIA_VISUAL: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD); + str = "GPAC ISO Video Handler"; + break; + case GF_ISOM_MEDIA_AUDIO: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_SMHD); + str = "GPAC ISO Audio Handler"; + break; + case GF_ISOM_MEDIA_HINT: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_HMHD); + str = "GPAC ISO Hint Handler"; + break; + case GF_ISOM_MEDIA_META: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC Timed MetaData Handler"; + break; + case GF_ISOM_MEDIA_OD: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 OD Handler"; + break; + case GF_ISOM_MEDIA_OCR: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 OCR Handler"; + break; + case GF_ISOM_MEDIA_SCENE: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 Scene Description Handler"; + break; + case GF_ISOM_MEDIA_MPEG7: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 MPEG-7 Handler"; + break; + case GF_ISOM_MEDIA_OCI: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 OCI Handler"; + break; + case GF_ISOM_MEDIA_IPMP: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 IPMP Handler"; + break; + case GF_ISOM_MEDIA_MPEGJ: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 MPEG-J Handler"; + break; + case GF_ISOM_MEDIA_TEXT: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC Streaming Text Handler"; + break; + case GF_ISOM_MEDIA_DIMS: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD); + MediaType = GF_ISOM_MEDIA_SCENE; + str = "GPAC DIMS Handler"; + break; + default: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC IsoMedia Handler"; + break; + } + hdlr = (GF_HandlerBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HDLR); + minf = (GF_MediaInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MINF); + + mdhd->timeScale = TimeScale; + hdlr->handlerType = MediaType; + hdlr->nameUTF8 = strdup(str); + + //first set-up the sample table... + stbl = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL); + dinf = (GF_DataInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DINF); + stbl->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSD); + stbl->ChunkOffset = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO); + //by default create a regular table + stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSZ); + stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); + stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STTS); + + //Create a data reference WITHOUT DATA ENTRY (we don't know anything yet about the media Data) + dref = (GF_DataReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DREF); + e = dinf_AddBox((GF_Box*)dinf, (GF_Box *)dref); if (e) goto err_exit; + + e = minf_AddBox((GF_Box*)minf, (GF_Box *) mediaInfo); if (e) goto err_exit; + e = minf_AddBox((GF_Box*)minf, (GF_Box *) stbl); if (e) goto err_exit; + e = minf_AddBox((GF_Box*)minf, (GF_Box *) dinf); if (e) goto err_exit; + + e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *) mdhd); if (e) goto err_exit; + e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *) minf); if (e) goto err_exit; + e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *) hdlr); if (e) goto err_exit; + + return GF_OK; + +err_exit: + if (mdhd) gf_isom_box_del((GF_Box *)mdhd); + if (minf) gf_isom_box_del((GF_Box *)minf); + if (hdlr) { + if (hdlr->nameUTF8) free(hdlr->nameUTF8); + gf_isom_box_del((GF_Box *)hdlr); + } + return e; + +} + +GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, GF_ESD *esd, u32 *outStreamIndex) +{ + GF_Err e; + GF_MPEGSampleEntryBox *entry; + GF_MPEGVisualSampleEntryBox *entry_v; + GF_MPEGAudioSampleEntryBox *entry_a; + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *dpnd; + u16 tmpRef; + + entry = NULL; + tref = NULL; + + if (!trak || !esd || (!outStreamIndex && !DataReferenceIndex) ) return GF_BAD_PARAM; + if (!Track_IsMPEG4Stream(trak->Media->handler->handlerType)) return GF_ISOM_INVALID_MEDIA; + + + esd->ESID = 0; + //set SL to predefined if no url + if (esd->URLString == NULL) { + if (!esd->slConfig) esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG); + esd->slConfig->predefined = SLPredef_MP4; + esd->slConfig->durationFlag = 0; + esd->slConfig->useTimestampsFlag = 1; + } + + //get the REF box if needed + if (esd->dependsOnESID || esd->OCRESID ) { + if (!trak->References) { + tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); + e = trak_AddBox((GF_Box*)trak, (GF_Box *)tref); + if (e) return e; + } + tref = trak->References; + } + + //Update Stream dependancies + e = Track_FindRef(trak, GF_ISOM_REF_DECODE, &dpnd); + if (e) return e; + if (!dpnd && esd->dependsOnESID) { + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = GF_ISOM_BOX_TYPE_DPND; + e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); + if (e) return e; + e = reftype_AddRefTrack(dpnd, esd->dependsOnESID, NULL); + if (e) return e; + } else if (dpnd && !esd->dependsOnESID) { + Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_DPND); + } + esd->dependsOnESID = 0; + + //Update GF_Clock dependancies + e = Track_FindRef(trak, GF_ISOM_REF_OCR, &dpnd); + if (e) return e; + if (!dpnd && esd->OCRESID) { + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = GF_ISOM_BOX_TYPE_SYNC; + e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); + if (e) return e; + e = reftype_AddRefTrack(dpnd, esd->OCRESID, NULL); + if (e) return e; + } else if (dpnd && !esd->OCRESID) { + Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_SYNC); + } else if (dpnd && esd->OCRESID) { + if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; + dpnd->trackIDs[0] = esd->OCRESID; + } + esd->OCRESID = 0; + + //brand new case: we have to change the IPI desc + if (esd->ipiPtr) { + e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd); + if (e) return e; + if (!dpnd) { + tmpRef = 0; + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR; + e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); + if (e) return e; + e = reftype_AddRefTrack(dpnd, esd->ipiPtr->IPI_ES_Id, &tmpRef); + if (e) return e; + //and replace the tag and value... + esd->ipiPtr->IPI_ES_Id = tmpRef; + esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG; + } else { + //Watch out! ONLY ONE IPI dependancy is allowed per stream + if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; + //if an existing one is there, what shall we do ??? + //donno, erase it + dpnd->trackIDs[0] = esd->ipiPtr->IPI_ES_Id; + //and replace the tag and value... + esd->ipiPtr->IPI_ES_Id = 1; + esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG; + } + } + + /*don't store the lang desc in ESD, use the media header language info*/ + if (esd->langDesc) { + trak->Media->mediaHeader->packedLanguage[0] = (esd->langDesc->langCode>>16)&0xFF; + trak->Media->mediaHeader->packedLanguage[1] = (esd->langDesc->langCode>>8)&0xFF; + trak->Media->mediaHeader->packedLanguage[2] = (esd->langDesc->langCode)&0xFF; + gf_odf_desc_del((GF_Descriptor *)esd->langDesc); + esd->langDesc = NULL; + } + + //we have a streamDescritpionIndex, use it + if (StreamDescriptionIndex) { + entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex - 1); + if (!entry) return GF_ISOM_INVALID_FILE; + + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4S: + //OK, delete the previous ESD + gf_odf_desc_del((GF_Descriptor *) entry->esd->desc); + entry->esd->desc = esd; + break; + case GF_ISOM_BOX_TYPE_MP4V: + entry_v = (GF_MPEGVisualSampleEntryBox*) entry; + //OK, delete the previous ESD + gf_odf_desc_del((GF_Descriptor *) entry_v->esd->desc); + entry_v->esd->desc = esd; + break; + case GF_ISOM_BOX_TYPE_MP4A: + entry_a = (GF_MPEGAudioSampleEntryBox*) entry; + //OK, delete the previous ESD + gf_odf_desc_del((GF_Descriptor *) entry_a->esd->desc); + entry_a->esd->desc = esd; + break; + case GF_ISOM_BOX_TYPE_AVC1: + e = AVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry, esd); + if (e) return e; + break; + default: + gf_odf_desc_del((GF_Descriptor *) esd); + break; + } + } else { + //need to check we're not in URL mode where only ONE description is allowed... + StreamDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + if (StreamDescriptionIndex) { + entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex - 1); + if (!entry) return GF_ISOM_INVALID_FILE; + if (entry->esd->desc->URLString) return GF_BAD_PARAM; + } + + //OK, check the handler and create the entry + switch (trak->Media->handler->handlerType) { + case GF_ISOM_MEDIA_VISUAL: + if (esd->decoderConfig->objectTypeIndication==0x21) { + entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AVC1); + if (!entry_v) return GF_OUT_OF_MEM; + e = AVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd); + } else { + entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4V); + if (!entry_v) return GF_OUT_OF_MEM; + entry_v->esd = (GF_ESDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_ESDS); + entry_v->esd->desc = esd; + } + + //type cast possible now + entry = (GF_MPEGSampleEntryBox*) entry_v; + break; + case GF_ISOM_MEDIA_AUDIO: + entry_a = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4A); + entry_a->samplerate_hi = trak->Media->mediaHeader->timeScale; + if (!entry_a) return GF_OUT_OF_MEM; + entry_a->esd = (GF_ESDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_ESDS); + entry_a->esd->desc = esd; + //type cast possible now + entry = (GF_MPEGSampleEntryBox*) entry_a; + break; + default: + entry = (GF_MPEGSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4S); + entry->esd = (GF_ESDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_ESDS); + entry->esd->desc = esd; + break; + } + entry->dataReferenceIndex = DataReferenceIndex; + + //and add the entry to our table... + e = stsd_AddBox(trak->Media->information->sampleTable->SampleDescription, (GF_Box *) entry); + if (e) return e; + if(outStreamIndex) *outStreamIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + } + return GF_OK; +} + +#endif //GPAC_READ_ONLY + diff --git a/src/isomedia/tx3g.c b/src/isomedia/tx3g.c new file mode 100644 index 0000000..3683d15 --- /dev/null +++ b/src/isomedia/tx3g.c @@ -0,0 +1,693 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + + +#ifndef GPAC_READ_ONLY + +GF_Err gf_isom_update_text_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_TextSampleDescriptor *desc) +{ + GF_TrackBox *trak; + GF_Err e; + u32 i; + GF_TextSampleEntryBox *txt; + + if (!descriptionIndex || !desc) return GF_BAD_PARAM; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !desc->font_count) return GF_BAD_PARAM; + + if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_TEXT) return GF_BAD_PARAM; + + txt = (GF_TextSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, descriptionIndex - 1); + if (!txt) return GF_BAD_PARAM; + if (txt->type != GF_ISOM_BOX_TYPE_TX3G) return GF_BAD_PARAM; + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + txt->back_color = desc->back_color; + txt->default_box = desc->default_pos; + txt->default_style = desc->default_style; + txt->displayFlags = desc->displayFlags; + txt->vertical_justification = desc->vert_justif; + txt->horizontal_justification = desc->horiz_justif; + if (txt->font_table) gf_isom_box_del((GF_Box*)txt->font_table); + + txt->font_table = (GF_FontTableBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FTAB); + txt->font_table->entry_count = desc->font_count; + txt->font_table->fonts = (GF_FontRecord *) malloc(sizeof(GF_FontRecord) * desc->font_count); + for (i=0; ifont_count; i++) { + txt->font_table->fonts[i].fontID = desc->fonts[i].fontID; + if (desc->fonts[i].fontName) txt->font_table->fonts[i].fontName = strdup(desc->fonts[i].fontName); + } + return e; +} + +GF_Err gf_isom_new_text_description(GF_ISOFile *movie, u32 trackNumber, GF_TextSampleDescriptor *desc, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex, i; + GF_TextSampleEntryBox *txt; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !desc->font_count) return GF_BAD_PARAM; + + if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_TEXT) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + txt = (GF_TextSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TX3G); + txt->dataReferenceIndex = dataRefIndex; + gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, txt); + if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + + txt->back_color = desc->back_color; + txt->default_box = desc->default_pos; + txt->default_style = desc->default_style; + txt->displayFlags = desc->displayFlags; + txt->vertical_justification = desc->vert_justif; + txt->horizontal_justification = desc->horiz_justif; + txt->font_table = (GF_FontTableBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FTAB); + txt->font_table->entry_count = desc->font_count; + + txt->font_table->fonts = (GF_FontRecord *) malloc(sizeof(GF_FontRecord) * desc->font_count); + for (i=0; ifont_count; i++) { + txt->font_table->fonts[i].fontID = desc->fonts[i].fontID; + if (desc->fonts[i].fontName) txt->font_table->fonts[i].fontName = strdup(desc->fonts[i].fontName); + } + return e; +} + + +/*blindly adds text - note we don't rely on terminaison characters to handle utf8 and utf16 data +in the same way. It is the user responsability to signal UTF16*/ +GF_Err gf_isom_text_add_text(GF_TextSample *samp, char *text_data, u32 text_len) +{ + if (!samp) return GF_BAD_PARAM; + if (!text_len) return GF_OK; + samp->text = (char*)realloc(samp->text, sizeof(char) * (samp->len + text_len) ); + memcpy(samp->text + samp->len, text_data, sizeof(char) * text_len); + samp->len += text_len; + return GF_OK; +} + +GF_Err gf_isom_text_set_utf16_marker(GF_TextSample *samp) +{ + /*we MUST have an empty sample*/ + if (!samp || samp->text) return GF_BAD_PARAM; + samp->text = (char*)malloc(sizeof(char) * 2); + samp->text[0] = (char) 0xFE; + samp->text[1] = (char) 0xFF; + samp->len = 2; + return GF_OK; +} + +GF_Err gf_isom_text_add_style(GF_TextSample *samp, GF_StyleRecord *rec) +{ + if (!samp || !rec) return GF_BAD_PARAM; + + if (!samp->styles) { + samp->styles = (GF_TextStyleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STYL); + if (!samp->styles) return GF_OUT_OF_MEM; + } + samp->styles->styles = (GF_StyleRecord*)realloc(samp->styles->styles, sizeof(GF_StyleRecord)*(samp->styles->entry_count+1)); + if (!samp->styles->styles) return GF_OUT_OF_MEM; + samp->styles->styles[samp->styles->entry_count] = *rec; + samp->styles->entry_count++; + return GF_OK; +} + +GF_Err gf_isom_text_add_highlight(GF_TextSample *samp, u16 start_char, u16 end_char) +{ + GF_TextHighlightBox *a; + if (!samp) return GF_BAD_PARAM; + if (start_char == end_char) return GF_BAD_PARAM; + + a = (GF_TextHighlightBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HLIT); + if (!a) return GF_OUT_OF_MEM; + a->startcharoffset = start_char; + a->endcharoffset = end_char; + return gf_list_add(samp->others, a); +} + +GF_Err gf_isom_text_set_highlight_color(GF_TextSample *samp, u8 r, u8 g, u8 b, u8 a) +{ + if (!samp) return GF_BAD_PARAM; + + if (!samp->highlight_color) { + samp->highlight_color = (GF_TextHighlightColorBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HCLR); + if (!samp->highlight_color) return GF_OUT_OF_MEM; + } + samp->highlight_color->hil_color = a; samp->highlight_color->hil_color <<= 8; + samp->highlight_color->hil_color = r; samp->highlight_color->hil_color <<= 8; + samp->highlight_color->hil_color = g; samp->highlight_color->hil_color <<= 8; + samp->highlight_color->hil_color = b; + return GF_OK; +} + +GF_Err gf_isom_text_set_highlight_color_argb(GF_TextSample *samp, u32 argb) +{ + if (!samp) return GF_BAD_PARAM; + + if (!samp->highlight_color) { + samp->highlight_color = (GF_TextHighlightColorBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HCLR); + if (!samp->highlight_color) return GF_OUT_OF_MEM; + } + samp->highlight_color->hil_color = argb; + return GF_OK; +} + +/*3GPP spec is quite obscur here*/ +GF_Err gf_isom_text_add_karaoke(GF_TextSample *samp, u32 start_time) +{ + if (!samp) return GF_BAD_PARAM; + samp->cur_karaoke = (GF_TextKaraokeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_KROK); + if (!samp->cur_karaoke) return GF_OUT_OF_MEM; + samp->cur_karaoke->highlight_starttime = start_time; + return gf_list_add(samp->others, samp->cur_karaoke); +} + +GF_Err gf_isom_text_set_karaoke_segment(GF_TextSample *samp, u32 end_time, u16 start_char, u16 end_char) +{ + if (!samp || !samp->cur_karaoke) return GF_BAD_PARAM; + samp->cur_karaoke->records = (KaraokeRecord*)realloc(samp->cur_karaoke->records, sizeof(KaraokeRecord)*(samp->cur_karaoke->nb_entries+1)); + if (!samp->cur_karaoke->records) return GF_OUT_OF_MEM; + samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].end_charoffset = end_char; + samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].start_charoffset = start_char; + samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].highlight_endtime = end_time; + samp->cur_karaoke->nb_entries++; + return GF_OK; +} + + +GF_Err gf_isom_text_set_scroll_delay(GF_TextSample *samp, u32 scroll_delay) +{ + if (!samp) return GF_BAD_PARAM; + if (!samp->scroll_delay) { + samp->scroll_delay = (GF_TextScrollDelayBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DLAY); + if (!samp->scroll_delay) return GF_OUT_OF_MEM; + } + samp->scroll_delay->scroll_delay = scroll_delay; + return GF_OK; +} + +GF_Err gf_isom_text_add_hyperlink(GF_TextSample *samp, char *URL, char *altString, u16 start_char, u16 end_char) +{ + GF_TextHyperTextBox*a; + if (!samp) return GF_BAD_PARAM; + a = (GF_TextHyperTextBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_HREF); + if (!a) return GF_OUT_OF_MEM; + a->startcharoffset = start_char; + a->endcharoffset = end_char; + a->URL = URL ? strdup(URL) : NULL; + a->URL_hint = altString ? strdup(altString) : NULL; + return gf_list_add(samp->others, a); +} + +GF_Err gf_isom_text_set_box(GF_TextSample *samp, s16 top, s16 left, s16 bottom, s16 right) +{ + if (!samp) return GF_BAD_PARAM; + if (!samp->box) { + samp->box = (GF_TextBoxBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_TBOX); + if (!samp->box) return GF_OUT_OF_MEM; + } + samp->box->box.top = top; + samp->box->box.left = left; + samp->box->box.bottom = bottom; + samp->box->box.right = right; + return GF_OK; +} + +GF_Err gf_isom_text_add_blink(GF_TextSample *samp, u16 start_char, u16 end_char) +{ + GF_TextBlinkBox *a; + if (!samp) return GF_BAD_PARAM; + a = (GF_TextBlinkBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_BLNK); + if (!a) return GF_OUT_OF_MEM; + a->startcharoffset = start_char; + a->endcharoffset = end_char; + return gf_list_add(samp->others, a); +} + +GF_Err gf_isom_text_set_wrap(GF_TextSample *samp, u8 wrap_flags) +{ + if (!samp) return GF_BAD_PARAM; + if (!samp->wrap) { + samp->wrap = (GF_TextWrapBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_TWRP); + if (!samp->wrap) return GF_OUT_OF_MEM; + } + samp->wrap->wrap_flag = wrap_flags; + return GF_OK; +} + +static GFINLINE GF_Err gpp_write_modifier(GF_BitStream *bs, GF_Box *a) +{ + GF_Err e; + if (!a) return GF_OK; + e = gf_isom_box_size(a); + if (!e) e = gf_isom_box_write(a, bs); + return e; +} + +GF_ISOSample *gf_isom_text_to_sample(GF_TextSample *samp) +{ + GF_Err e; + GF_ISOSample *res; + GF_BitStream *bs; + u32 i; + if (!samp) return NULL; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u16(bs, samp->len); + if (samp->len) gf_bs_write_data(bs, samp->text, samp->len); + + e = gpp_write_modifier(bs, (GF_Box *)samp->styles); + if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->highlight_color); + if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->scroll_delay); + if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->box); + if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->wrap); + + if (!e) { + GF_Box *a; + i=0; + while ((a = (GF_Box*)gf_list_enum(samp->others, &i))) { + e = gpp_write_modifier(bs, a); + if (e) break; + } + } + if (e) { + gf_bs_del(bs); + return NULL; + } + res = gf_isom_sample_new(); + if (!res) { + gf_bs_del(bs); + return NULL; + } + gf_bs_get_content(bs, &res->data, &res->dataLength); + gf_bs_del(bs); + res->IsRAP = 1; + return res; +} + +GF_Err gf_isom_text_has_similar_description(GF_ISOFile *movie, u32 trackNumber, GF_TextSampleDescriptor *desc, u32 *outDescIdx, Bool *same_box, Bool *same_styles) +{ + GF_TrackBox *trak; + GF_Err e; + u32 i, j, count; + GF_TextSampleEntryBox *txt; + + *same_box = *same_styles = 0; + *outDescIdx = 0; + + if (!desc) return GF_BAD_PARAM; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return GF_BAD_PARAM; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !desc->font_count) return GF_BAD_PARAM; + + if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_TEXT) return GF_BAD_PARAM; + + count = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + for (i=0; iMedia->information->sampleTable->SampleDescription->boxList, i); + if (!txt|| (txt->type != GF_ISOM_BOX_TYPE_TX3G)) continue; + if (txt->back_color != desc->back_color) continue; + if (txt->displayFlags != desc->displayFlags) continue; + if (txt->vertical_justification != desc->vert_justif) continue; + if (txt->horizontal_justification != desc->horiz_justif) continue; + if (txt->font_table->entry_count != desc->font_count) continue; + + same_fonts = 1; + for (j=0; jfont_count; j++) { + if (txt->font_table->fonts[j].fontID != desc->fonts[j].fontID) same_fonts = 0; + else if (strcmp(desc->fonts[j].fontName, txt->font_table->fonts[j].fontName)) same_fonts = 0; + } + if (same_fonts) { + *outDescIdx = i+1; + if (!memcmp(&txt->default_box, &desc->default_pos, sizeof(GF_BoxRecord))) *same_box = 1; + if (!memcmp(&txt->default_style, &desc->default_style, sizeof(GF_StyleRecord))) *same_styles = 1; + return GF_OK; + } + } + return GF_OK; +} + +#endif + +GF_TextSample *gf_isom_new_text_sample() +{ + GF_TextSample *res; + GF_SAFEALLOC(res, GF_TextSample); + if (!res) return NULL; + res->others = gf_list_new(); + return res; +} + +GF_Err gf_isom_text_reset_styles(GF_TextSample *samp) +{ + if (!samp) return GF_BAD_PARAM; + if (samp->box) gf_isom_box_del((GF_Box *)samp->box); + samp->box = NULL; + if (samp->highlight_color) gf_isom_box_del((GF_Box *)samp->highlight_color); + samp->highlight_color = NULL; + if (samp->scroll_delay) gf_isom_box_del((GF_Box *)samp->scroll_delay); + samp->scroll_delay = NULL; + if (samp->wrap) gf_isom_box_del((GF_Box *)samp->wrap); + samp->wrap = NULL; + if (samp->styles) gf_isom_box_del((GF_Box *)samp->styles); + samp->styles = NULL; + samp->cur_karaoke = NULL; + while (gf_list_count(samp->others)) { + GF_Box *a = (GF_Box*)gf_list_get(samp->others, 0); + gf_list_rem(samp->others, 0); + gf_isom_box_del(a); + } + return GF_OK; +} + +GF_Err gf_isom_text_reset(GF_TextSample *samp) +{ + if (!samp) return GF_BAD_PARAM; + if (samp->text) free(samp->text); + samp->text = NULL; + samp->len = 0; + return gf_isom_text_reset_styles(samp); +} + +GF_EXPORT +void gf_isom_delete_text_sample(GF_TextSample * tx_samp) +{ + gf_isom_text_reset(tx_samp); + gf_list_del(tx_samp->others); + free(tx_samp); +} + +GF_EXPORT +GF_TextSample *gf_isom_parse_texte_sample(GF_BitStream *bs) +{ + GF_TextSample *s = gf_isom_new_text_sample(); + + /*empty sample*/ + if (!bs || !gf_bs_available(bs)) return s; + + s->len = gf_bs_read_u16(bs); + if (s->len) { + /*2 extra bytes for UTF-16 term char just in case (we don't know if a BOM marker is present or + not since this may be a sample carried over RTP*/ + s->text = (char *) malloc(sizeof(char)*(s->len+2) ); + s->text[s->len] = 0; s->text[s->len+1] = 0; + gf_bs_read_data(bs, s->text, s->len); + } + + while (gf_bs_available(bs)) { + GF_Box *a; + GF_Err e = gf_isom_parse_box(&a, bs); + if (!e) { + switch (a->type) { + case GF_ISOM_BOX_TYPE_STYL: + if (s->styles) { + GF_TextStyleBox *st2 = (GF_TextStyleBox *)a; + if (!s->styles->entry_count) { + gf_isom_box_del((GF_Box*)s->styles); + s->styles = st2; + } else { + s->styles->styles = (GF_StyleRecord*)realloc(s->styles->styles, sizeof(GF_StyleRecord) * (s->styles->entry_count + st2->entry_count)); + memcpy(&s->styles->styles[s->styles->entry_count], st2->styles, sizeof(GF_StyleRecord) * st2->entry_count); + s->styles->entry_count += st2->entry_count; + gf_isom_box_del(a); + } + } else { + s->styles = (GF_TextStyleBox*)a; + } + break; + case GF_ISOM_BOX_TYPE_KROK: + s->cur_karaoke = (GF_TextKaraokeBox*)a; + case GF_ISOM_BOX_TYPE_HLIT: + case GF_ISOM_BOX_TYPE_HREF: + case GF_ISOM_BOX_TYPE_BLNK: + gf_list_add(s->others, a); + break; + case GF_ISOM_BOX_TYPE_HCLR: + if (s->highlight_color) gf_isom_box_del(a); + else s->highlight_color = (GF_TextHighlightColorBox *) a; + break; + case GF_ISOM_BOX_TYPE_DLAY: + if (s->scroll_delay) gf_isom_box_del(a); + else s->scroll_delay= (GF_TextScrollDelayBox*) a; + break; + case GF_ISOM_BOX_TYPE_TBOX: + if (s->box) gf_isom_box_del(a); + else s->box= (GF_TextBoxBox *) a; + break; + case GF_ISOM_BOX_TYPE_TWRP: + if (s->wrap) gf_isom_box_del(a); + else s->wrap= (GF_TextWrapBox*) a; + break; + default: + gf_isom_box_del(a); + break; + } + } + } + return s; +} + +GF_TextSample *gf_isom_parse_texte_sample_from_data(char *data, u32 dataLength) +{ + GF_TextSample *s; + GF_BitStream *bs; + /*empty text sample*/ + if (!data || !dataLength) { + return gf_isom_new_text_sample(); + } + + bs = gf_bs_new(data, dataLength, GF_BITSTREAM_READ); + s = gf_isom_parse_texte_sample(bs); + gf_bs_del(bs); + return s; +} + + +/*out-of-band sample desc (128 and 255 reserved in RFC)*/ +#define SAMPLE_INDEX_OFFSET 129 + + +static void gf_isom_write_tx3g(GF_TextSampleEntryBox *a, GF_BitStream *bs, u32 sidx, u32 sidx_offset) +{ + u32 size, j; + void gpp_write_rgba(GF_BitStream *bs, u32 col); + void gpp_write_box(GF_BitStream *bs, GF_BoxRecord *rec); + void gpp_write_style(GF_BitStream *bs, GF_StyleRecord *rec); + + + if (sidx_offset) gf_bs_write_u8(bs, sidx + sidx_offset); + + /*SINCE WINCE HAS A READONLY VERSION OF MP4 WE MUST DO IT BY HAND*/ + size = 8 + 18 + 8 + 12; + size += 8 + 2; + for (j=0; jfont_table->entry_count; j++) { + size += 3; + if (a->font_table->fonts[j].fontName) size += strlen(a->font_table->fonts[j].fontName); + } + /*write TextSampleEntry box*/ + gf_bs_write_u32(bs, size); + gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_TX3G); + gf_bs_write_data(bs, a->reserved, 6); + gf_bs_write_u16(bs, a->dataReferenceIndex); + gf_bs_write_u32(bs, a->displayFlags); + gf_bs_write_u8(bs, a->horizontal_justification); + gf_bs_write_u8(bs, a->vertical_justification); + gpp_write_rgba(bs, a->back_color); + gpp_write_box(bs, &a->default_box); + gpp_write_style(bs, &a->default_style); + /*write font table box*/ + size -= (8 + 18 + 8 + 12); + gf_bs_write_u32(bs, size); + gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_FTAB); + gf_bs_write_u16(bs, a->font_table->entry_count); + for (j=0; jfont_table->entry_count; j++) { + gf_bs_write_u16(bs, a->font_table->fonts[j].fontID); + if (a->font_table->fonts[j].fontName) { + u32 len = strlen(a->font_table->fonts[j].fontName); + gf_bs_write_u8(bs, len); + gf_bs_write_data(bs, a->font_table->fonts[j].fontName, len); + } else { + gf_bs_write_u8(bs, 0); + } + } +} + +GF_Err gf_isom_get_ttxt_esd(GF_MediaBox *mdia, GF_ESD **out_esd) +{ + GF_BitStream *bs; + u32 count, i; + Bool has_v_info; + GF_List *sampleDesc; + GF_ESD *esd; + GF_TrackBox *tk; + + *out_esd = NULL; + sampleDesc = mdia->information->sampleTable->SampleDescription->boxList; + count = gf_list_count(sampleDesc); + if (!count) return GF_ISOM_INVALID_MEDIA; + + esd = gf_odf_desc_esd_new(2); + esd->decoderConfig->streamType = GF_STREAM_TEXT; + esd->decoderConfig->objectTypeIndication = 0x08; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + + /*Base3GPPFormat*/ + gf_bs_write_u8(bs, 0x10); + /*MPEGExtendedFormat*/ + gf_bs_write_u8(bs, 0x10); + /*profileLevel*/ + gf_bs_write_u8(bs, 0x10); + gf_bs_write_u24(bs, mdia->mediaHeader->timeScale); + gf_bs_write_int(bs, 0, 1); /*no alt formats*/ + gf_bs_write_int(bs, 2, 2); /*only out-of-band-band sample desc*/ + gf_bs_write_int(bs, 1, 1); /*we will write sample desc*/ + + /*write v info if any visual track in this movie*/ + has_v_info = 0; + i=0; + while ((tk = (GF_TrackBox*)gf_list_enum(mdia->mediaTrack->moov->trackList, &i))) { + if (tk->Media->handler && (tk->Media->handler->handlerType == GF_ISOM_MEDIA_VISUAL)) { + has_v_info = 1; + } + } + gf_bs_write_int(bs, has_v_info, 1); + + gf_bs_write_int(bs, 0, 3); /*reserved, spec doesn't say the values*/ + gf_bs_write_u8(bs, mdia->mediaTrack->Header->layer); + gf_bs_write_u16(bs, mdia->mediaTrack->Header->width>>16); + gf_bs_write_u16(bs, mdia->mediaTrack->Header->height>>16); + + /*write desc*/ + gf_bs_write_u8(bs, count); + for (i=0; itype != GF_ISOM_BOX_TYPE_TX3G) continue; + gf_isom_write_tx3g(a, bs, i+1, SAMPLE_INDEX_OFFSET); + } + if (has_v_info) { + u32 trans; + /*which video shall we pick for MPEG-4, and how is the associations indicated in 3GP ???*/ + gf_bs_write_u16(bs, 0); + gf_bs_write_u16(bs, 0); + trans = mdia->mediaTrack->Header->matrix[6]; trans >>= 16; + gf_bs_write_u16(bs, trans); + trans = mdia->mediaTrack->Header->matrix[7]; trans >>= 16; + gf_bs_write_u16(bs, trans); + } + + gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + *out_esd = esd; + return GF_OK; +} + +GF_Err gf_isom_rewrite_text_sample(GF_ISOSample *samp, u32 sampleDescriptionIndex, u32 sample_dur) +{ + GF_BitStream *bs; + u32 pay_start, txt_size; + Bool is_utf_16 = 0; + if (!samp || !samp->data || !samp->dataLength) return GF_OK; + + bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ); + txt_size = gf_bs_read_u16(bs); + gf_bs_del(bs); + + /*remove BOM*/ + pay_start = 2; + if (txt_size>2) { + /*seems 3GP only accepts BE UTF-16 (no LE, no UTF32)*/ + if (((u8) samp->data[2]==(u8) 0xFE) && ((u8)samp->data[3]==(u8) 0xFF)) { + is_utf_16 = 1; + pay_start = 4; + txt_size -= 2; + } + } + + /*rewrite as TTU(1)*/ + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, is_utf_16, 1); + gf_bs_write_int(bs, 0, 4); + gf_bs_write_int(bs, 1, 3); + gf_bs_write_u16(bs, 8 + samp->dataLength - pay_start); + gf_bs_write_u8(bs, sampleDescriptionIndex + SAMPLE_INDEX_OFFSET); + gf_bs_write_u24(bs, sample_dur); + /*write text size*/ + gf_bs_write_u16(bs, txt_size); + if (txt_size) gf_bs_write_data(bs, samp->data + pay_start, samp->dataLength - pay_start); + + free(samp->data); + samp->data = NULL; + gf_bs_get_content(bs, &samp->data, &samp->dataLength); + gf_bs_del(bs); + return GF_OK; +} + + +GF_Err gf_isom_text_get_encoded_tx3g(GF_ISOFile *file, u32 track, u32 sidx, u32 sidx_offset, char **tx3g, u32 *tx3g_size) +{ + GF_BitStream *bs; + GF_TrackBox *trak; + GF_TextSampleEntryBox *a; + + trak = gf_isom_get_track_from_file(file, track); + if (!trak) return GF_BAD_PARAM; + + a = (GF_TextSampleEntryBox *) gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, sidx-1); + if (!a || (a->type != GF_ISOM_BOX_TYPE_TX3G)) return GF_BAD_PARAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_isom_write_tx3g(a, bs, sidx, sidx_offset); + *tx3g = NULL; + *tx3g_size = 0; + gf_bs_get_content(bs, tx3g, tx3g_size); + gf_bs_del(bs); + return GF_OK; +} + diff --git a/src/laser/lsr_dec.c b/src/laser/lsr_dec.c new file mode 100644 index 0000000..76cae28 --- /dev/null +++ b/src/laser/lsr_dec.c @@ -0,0 +1,5235 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / LASeR codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +#ifndef GPAC_DISABLE_SVG + + +#define GF_LSR_READ_INT(_codec, _val, _nbBits, _str) {\ + (_val) = gf_bs_read_int(_codec->bs, _nbBits); \ + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] %s\t\t%d\t\t%d\n", _str, _nbBits, _val)); \ + }\ + + +static void lsr_read_group_content(GF_LASeRCodec *lsr, GF_Node *elt, Bool skip_object_content); +static void lsr_read_group_content_post_init(GF_LASeRCodec *lsr, SVG_Element *elt, Bool skip_init); +static GF_Err lsr_read_command_list(GF_LASeRCodec *lsr, GF_List *comList, SVG_Element *cond, Bool first_imp); +static GF_Err lsr_decode_laser_unit(GF_LASeRCodec *lsr, GF_List *com_list); +static void lsr_read_path_type(GF_LASeRCodec *lsr, GF_Node *n, u32 tag, SVG_PathData *path, const char *name); +static void lsr_read_point_sequence(GF_LASeRCodec *lsr, GF_List *pts, const char *name); +static Bool lsr_setup_smil_anim(GF_LASeRCodec *lsr, SVG_Element *anim, SVG_Element *anim_parent); + +GF_EXPORT +GF_LASeRCodec *gf_laser_decoder_new(GF_SceneGraph *graph) +{ + GF_LASeRCodec *tmp; + GF_SAFEALLOC(tmp, GF_LASeRCodec); + if (!tmp) return NULL; + tmp->streamInfo = gf_list_new(); + tmp->font_table = gf_list_new(); + tmp->defered_hrefs = gf_list_new(); + tmp->defered_listeners = gf_list_new(); + tmp->defered_anims = gf_list_new(); + tmp->unresolved_commands = gf_list_new(); + tmp->sg = graph; + return tmp; +} + +GF_EXPORT +void gf_laser_decoder_del(GF_LASeRCodec *codec) +{ + /*destroy all config*/ + while (gf_list_count(codec->streamInfo)) { + LASeRStreamInfo *p = (LASeRStreamInfo *)gf_list_last(codec->streamInfo); + free(p); + gf_list_rem_last(codec->streamInfo); + } + gf_list_del(codec->streamInfo); + if (codec->col_table) free(codec->col_table); + while (gf_list_count(codec->font_table)) { + char *ft = (char *)gf_list_last(codec->font_table); + free(ft); + gf_list_rem_last(codec->font_table); + } + gf_list_del(codec->font_table); +#if 0 + while (gf_list_count(codec->defered_hrefs)) { + XMLRI *iri = (XMLRI *)gf_list_last(codec->defered_hrefs); + gf_list_rem_last(codec->defered_hrefs); + if (iri->string) free(iri->string); + iri->string = NULL; + } +#endif + gf_list_del(codec->defered_hrefs); + gf_list_del(codec->defered_anims); + gf_list_del(codec->defered_listeners); + gf_list_del(codec->unresolved_commands); + free(codec); +} + +static LASeRStreamInfo *lsr_get_stream(GF_LASeRCodec *codec, u16 ESID) +{ + u32 i=0; + LASeRStreamInfo *ptr; + while ((ptr = (LASeRStreamInfo *)gf_list_enum(codec->streamInfo, &i))) { + if (!ESID || (ptr->ESID==ESID)) return ptr; + } + return NULL; +} + + +GF_EXPORT +GF_Err gf_laser_decoder_configure_stream(GF_LASeRCodec *codec, u16 ESID, char *dsi, u32 dsi_len) +{ + LASeRStreamInfo *info; + GF_BitStream *bs; + if (lsr_get_stream(codec, ESID) != NULL) return GF_BAD_PARAM; + GF_SAFEALLOC(info, LASeRStreamInfo); + info->ESID = ESID; + bs = gf_bs_new(dsi, dsi_len, GF_BITSTREAM_READ); + + info->cfg.profile = gf_bs_read_int(bs, 8); + info->cfg.level = gf_bs_read_int(bs, 8); + /*info->cfg.reserved = */ gf_bs_read_int(bs, 3); + info->cfg.pointsCodec = gf_bs_read_int(bs, 2); + info->cfg.pathComponents = gf_bs_read_int(bs, 4); + info->cfg.fullRequestHost = gf_bs_read_int(bs, 1); + if (gf_bs_read_int(bs, 1)) { + info->cfg.time_resolution = gf_bs_read_int(bs, 16); + } else { + info->cfg.time_resolution = 1000; + } + info->cfg.colorComponentBits = gf_bs_read_int(bs, 4); + info->cfg.colorComponentBits += 1; + info->cfg.resolution = gf_bs_read_int(bs, 4); + if (info->cfg.resolution>7) info->cfg.resolution -= 16; + info->cfg.coord_bits = gf_bs_read_int(bs, 5); + info->cfg.scale_bits_minus_coord_bits = gf_bs_read_int(bs, 4); + info->cfg.newSceneIndicator = gf_bs_read_int(bs, 1); + /*reserved*/ gf_bs_read_int(bs, 3); + info->cfg.extensionIDBits = gf_bs_read_int(bs, 4); + /*we ignore the rest*/ + gf_list_add(codec->streamInfo, info); + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_laser_decoder_remove_stream(GF_LASeRCodec *codec, u16 ESID) +{ + u32 i, count; + count = gf_list_count(codec->streamInfo); + for (i=0;istreamInfo, i); + if (ptr->ESID==ESID) { + free(ptr); + gf_list_rem(codec->streamInfo, i); + return GF_OK; + } + } + return GF_BAD_PARAM; +} + + +void gf_bs_set_eos_callback(GF_BitStream *bs, void (*EndOfStream)(void *par), void *par); + +void lsr_end_of_stream(void *co) +{ + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[LASeR] memory overread - corrupted decoding\n")); + ((GF_LASeRCodec *) co)->last_error = GF_NON_COMPLIANT_BITSTREAM; +} + +GF_EXPORT +GF_Err gf_laser_decode_au(GF_LASeRCodec *codec, u16 ESID, char *data, u32 data_len) +{ + GF_Err e; + if (!codec || !data || !data_len) return GF_BAD_PARAM; + + codec->info = lsr_get_stream(codec, ESID); + if (!codec->info) return GF_BAD_PARAM; + codec->coord_bits = codec->info->cfg.coord_bits; + codec->scale_bits = codec->info->cfg.scale_bits_minus_coord_bits; + codec->time_resolution = codec->info->cfg.time_resolution; + codec->color_scale = (1<info->cfg.colorComponentBits) - 1; + if (codec->info->cfg.resolution >= 0) + codec->res_factor = INT2FIX(1<info->cfg.resolution); + else + codec->res_factor = gf_divfix(FIX_ONE, INT2FIX(1 << (-codec->info->cfg.resolution)) ); + + codec->bs = gf_bs_new(data, data_len, GF_BITSTREAM_READ); + gf_bs_set_eos_callback(codec->bs, lsr_end_of_stream, codec); + codec->memory_dec = 0; + e = lsr_decode_laser_unit(codec, NULL); + gf_bs_del(codec->bs); + codec->bs = NULL; + return e; +} + +GF_Err gf_laser_decode_command_list(GF_LASeRCodec *codec, u16 ESID, char *data, u32 data_len, GF_List *com_list) +{ + GF_Err e; + u32 i; + if (!codec || !data || !data_len) return GF_BAD_PARAM; + + codec->info = lsr_get_stream(codec, ESID); + if (!codec->info) return GF_BAD_PARAM; + codec->coord_bits = codec->info->cfg.coord_bits; + codec->scale_bits = codec->info->cfg.scale_bits_minus_coord_bits; + codec->time_resolution = codec->info->cfg.time_resolution; + codec->color_scale = (1<info->cfg.colorComponentBits) - 1; + if (codec->info->cfg.resolution >= 0) + codec->res_factor = INT2FIX(1<info->cfg.resolution); + else + codec->res_factor = gf_divfix(FIX_ONE, INT2FIX(1 << (-codec->info->cfg.resolution)) ); + + codec->bs = gf_bs_new(data, data_len, GF_BITSTREAM_READ); + gf_bs_set_eos_callback(codec->bs, lsr_end_of_stream, codec); + codec->memory_dec = 1; + e = lsr_decode_laser_unit(codec, com_list); + gf_bs_del(codec->bs); + codec->bs = NULL; + if (e) return e; + + for (i=0; iunresolved_commands); i++) { + GF_Command *com = (GF_Command *)gf_list_get(codec->unresolved_commands, i); + assert(!com->node); + com->node = gf_sg_find_node(codec->sg, com->RouteID); + if (com->node) { + gf_node_register(com->node, NULL); + com->RouteID = 0; + gf_list_rem(codec->unresolved_commands, i); + i--; + } + } + return GF_OK; +} + +GF_EXPORT +void gf_laser_decoder_set_clock(GF_LASeRCodec *codec, Double (*GetSceneTime)(void *st_cbk), void *st_cbk ) +{ + codec->GetSceneTime = GetSceneTime; + codec->cbk = st_cbk; +} + +static u32 lsr_read_vluimsbf5(GF_LASeRCodec *lsr, const char *name) +{ + u32 nb_words = 0; + u32 nb_tot, nb_bits, val; + + while (gf_bs_read_int(lsr->bs, 1)) nb_words++; + nb_words++; + nb_tot = nb_words; + nb_bits = nb_words*4; + nb_tot += nb_bits; + val = gf_bs_read_int(lsr->bs, nb_bits); + if (name) GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] %s\t\t%d\t\t%d\n", name, nb_tot, val)); + return val; +} +static u32 lsr_read_vluimsbf8(GF_LASeRCodec *lsr, const char *name) +{ + u32 nb_words = 0; + u32 nb_tot, nb_bits, val; + + while (gf_bs_read_int(lsr->bs, 1)) nb_words++; + nb_words++; + nb_tot = nb_words; + nb_bits = nb_words*7; + nb_tot += nb_bits; + val = gf_bs_read_int(lsr->bs, nb_bits); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] %s\t\t%d\t\t%d\n", name, nb_tot, val)); + return val; +} + +static void lsr_read_extension(GF_LASeRCodec *lsr, const char *name) +{ + u32 len = lsr_read_vluimsbf5(lsr, name); +#if 0 + *out_data = malloc(sizeof(char)*len); + gf_bs_read_data(lsr->bs, *out_data, len); + *out_len = len; +#else + while (len) { gf_bs_read_int(lsr->bs, 8); len--; } +#endif +} + +static void lsr_read_extend_class(GF_LASeRCodec *lsr, char **out_data, u32 *out_len, const char *name) +{ + u32 len; + GF_LSR_READ_INT(lsr, len, lsr->info->cfg.extensionIDBits, "reserved"); + len = lsr_read_vluimsbf5(lsr, "len"); +// while (len) gf_bs_read_int(lsr->bs, 1); + gf_bs_read_long_int(lsr->bs, len); + if (out_data) *out_data = NULL; + if (out_len) *out_len = 0; +} + +static void lsr_read_private_element_container(GF_LASeRCodec *lsr) +{ + u32 val, len; + GF_LSR_READ_INT(lsr, val, 4, "ch4"); + switch (val) { + /*privateAnyXMLElement*/ + case 0: + len = lsr_read_vluimsbf5(lsr, "len"); + gf_bs_skip_bytes(lsr->bs, len); + break; + /*privateOpaqueElement*/ + case 1: + len = lsr_read_vluimsbf5(lsr, "len"); + gf_bs_skip_bytes(lsr->bs, len); + break; + /*element_any*/ + case 2: + lsr_read_extend_class(lsr, NULL, 0, "reserved"); + break; + /*attr_custom_extension*/ + default: + len = lsr_read_vluimsbf5(lsr, "len"); + gf_bs_skip_bytes(lsr->bs, len); + break; + } +} + +static void lsr_read_private_attribute_container(GF_LASeRCodec *lsr) +{ + u32 val; + do { + u32 skip_len; + GF_LSR_READ_INT(lsr, val, 2, "privateDataType"); + skip_len = lsr_read_vluimsbf5(lsr, "skipLen"); + gf_bs_align(lsr->bs); + /*just skip data*/ +#if 1 + if (skip_len>gf_bs_available(lsr->bs)) { + lsr->last_error = GF_NON_COMPLIANT_BITSTREAM; + return; + } + gf_bs_skip_bytes(lsr->bs, skip_len); +#else + switch (val) { + /*private data of type "anyXML"*/ + case 0: + count = lsr_read_vluimsbf5(lsr, "count"); + for (i=0; ibs); + byte[skipLen - ((nameSpaceIndexBits+7)%8)] data; + break; + default: + /*TODO - spec is wrong here (typo, "len" instead of "skipLen" )*/ + gf_bs_skip_bytes(skipLen); + break; + } +#endif + gf_bs_align(lsr->bs); + GF_LSR_READ_INT(lsr, val, 1, "hasMorePrivateData"); + } while (val); +} + +static void lsr_read_any_attribute(GF_LASeRCodec *lsr, GF_Node *node, Bool skippable) +{ + u32 val = 1; + if (skippable) GF_LSR_READ_INT(lsr, val, 1, "has_attrs"); + if (val) { + do { + GF_LSR_READ_INT(lsr, val, lsr->info->cfg.extensionIDBits, "reserved"); + val = lsr_read_vluimsbf5(lsr, "len");//len in BITS + GF_LSR_READ_INT(lsr, val, val, "reserved_val"); + GF_LSR_READ_INT(lsr, val, 1, "hasNextExtension"); + } while (val); + } +} + +static void lsr_read_object_content(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + u32 val; + GF_LSR_READ_INT(lsr, val, 1, "has_private_attr"); + if (val) lsr_read_private_attribute_container(lsr); +} + +static void lsr_read_codec_IDREF(GF_LASeRCodec *lsr, XMLRI *href, const char *name) +{ + GF_Node *n; + u32 flag; + u32 nID = 1+lsr_read_vluimsbf5(lsr, name); + + GF_LSR_READ_INT(lsr, flag, 1, "reserved"); + if (flag) { + u32 len = lsr_read_vluimsbf5(lsr, "len"); + GF_LSR_READ_INT(lsr, flag, len, "reserved"); + } + + n = gf_sg_find_node(lsr->sg, nID); + if (!n) { + char NodeID[1024]; + sprintf(NodeID, "N%d", nID-1); + href->string = strdup(NodeID); + if (href->type!=0xFF) + gf_list_add(lsr->defered_hrefs, href); + href->type = XMLRI_ELEMENTID; + return; + } + href->target = (SVG_Element *)n; + href->type = XMLRI_ELEMENTID; + gf_node_register_iri(lsr->sg, href); +} + +static u32 lsr_read_codec_IDREF_command(GF_LASeRCodec *lsr, const char *name) +{ + u32 flag; + u32 nID = 1+lsr_read_vluimsbf5(lsr, name); + + GF_LSR_READ_INT(lsr, flag, 1, "reserved"); + if (flag) { + u32 len = lsr_read_vluimsbf5(lsr, "len"); + GF_LSR_READ_INT(lsr, flag, len, "reserved"); + } + return nID; +} + +static Fixed lsr_read_fixed_16_8(GF_LASeRCodec *lsr, const char *name) +{ + u32 val; + GF_LSR_READ_INT(lsr, val, 24, name); + if (val & (1<<23)) { + s32 res = val - (1<<24); +#ifdef GPAC_FIXED_POINT + return res*256; +#else + return INT2FIX(res) / 256; +#endif + } else { +#ifdef GPAC_FIXED_POINT + return val*256; +#else + return INT2FIX(val) / 256; +#endif + } +} + +static void lsr_read_fixed_16_8i(GF_LASeRCodec *lsr, SVG_Number *n, const char *name) +{ + s32 val; + GF_LSR_READ_INT(lsr, val, 1, name); + if (val) { + n->type=SVG_NUMBER_INHERIT; + } else { + n->type=SVG_NUMBER_VALUE; + n->value = lsr_read_fixed_16_8(lsr, name); + } +} + + +static void lsr_get_color(GF_LASeRCodec *lsr, u32 idx, SVG_Color *color) +{ + LSRCol *c; + if (idx>=lsr->nb_cols) return; + + c = &lsr->col_table[idx]; + color->red = INT2FIX(c->r) / lsr->color_scale; + color->green = INT2FIX(c->g) / lsr->color_scale; + color->blue = INT2FIX(c->b) / lsr->color_scale; + color->type = SVG_COLOR_RGBCOLOR; +} + + +static void lsr_read_line_increment_type(GF_LASeRCodec *lsr, SVG_Number *li, const char *name) +{ + u32 val; + GF_LSR_READ_INT(lsr, val, 1, "choice"); + if (val==1) { + GF_LSR_READ_INT(lsr, val, 1, "type"); + if (val==1) li->type=SVG_NUMBER_INHERIT; + else li->type=SVG_NUMBER_AUTO; + } else { + li->value = lsr_read_fixed_16_8(lsr, "line-increment-value"); + } +} + +static void lsr_read_byte_align_string(GF_LASeRCodec *lsr, char **str, const char *name) +{ + u32 len; + gf_bs_align(lsr->bs); + len = lsr_read_vluimsbf8(lsr, "len"); + if (str) { + if (*str) free(*str); + *str = NULL; + if (len) { + if (len > gf_bs_available(lsr->bs) ) { + lsr->last_error = GF_NON_COMPLIANT_BITSTREAM; + return; + } + *str = (char*)malloc(sizeof(char)*(len+1)); + gf_bs_read_data(lsr->bs, *str, len); + (*str) [len] = 0; + } + } else { + while (len) { gf_bs_read_int(lsr->bs, 8); len--; } + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] %s\t\t%d\t\t%s\n", name, 8*len, *str)); +} + +static void lsr_read_text_content(GF_LASeRCodec *lsr, GF_Node *elt) +{ + char *str = NULL; + lsr_read_byte_align_string(lsr, &str, "textContent"); + if (!str) return; + gf_dom_add_text_node(elt, str); +} + +static void lsr_read_byte_align_string_list(GF_LASeRCodec *lsr, GF_List *l, const char *name, Bool is_iri) +{ + XMLRI *iri; + char *text, *sep, *sep2, *cur; + while (gf_list_count(l)) { + char *str = (char *)gf_list_last(l); + gf_list_rem_last(l); + free(str); + } + text = NULL; + lsr_read_byte_align_string(lsr, &text, name); + cur = text; + while (cur) { + sep = strchr(cur, '\''); + if (!sep) { + if (is_iri) { + GF_SAFEALLOC(iri, XMLRI); + iri->string = strdup(cur); + iri->type = XMLRI_STRING; + gf_list_add(l, iri); + } else { + gf_list_add(l, strdup(cur)); + } + break; + } + sep2 = strchr(sep + 1, '\''); + if (!sep2) { + if (is_iri) { + GF_SAFEALLOC(iri, XMLRI); + iri->string = strdup(cur); + iri->type = XMLRI_STRING; + gf_list_add(l, iri); + } else { + gf_list_add(l, strdup(cur)); + } + break; + } + sep2[0] = 0; + if (is_iri) { + GF_SAFEALLOC(iri, XMLRI); + iri->string = strdup(sep+1); + iri->type = XMLRI_STRING; + gf_list_add(l, iri); + } else { + gf_list_add(l, strdup(sep+1)); + } + sep2[0] = '\''; + cur = sep2 + 1; + } + free(text); +} + +static void lsr_read_any_uri(GF_LASeRCodec *lsr, XMLRI *iri, const char *name) +{ + u32 val; + GF_LSR_READ_INT(lsr, val, 1, "hasUri"); + if (val) { + char *s = NULL; + iri->type=XMLRI_STRING; + if (iri->string) { + free(iri->string); + iri->string = NULL; + } + lsr_read_byte_align_string(lsr, &s, "uri"); + GF_LSR_READ_INT(lsr, val, 1, "hasData"); + if (!val) { + iri->string = s; + } else { + u32 len_rad, len; + len = lsr_read_vluimsbf5(lsr, "len"); + len_rad = s ? strlen(s) : 0; + iri->string = (char*)malloc(sizeof(char)*(len_rad+1+len+1)); + iri->string[0] = 0; + if (s) { + strcpy(iri->string, s); + free(s); + } + strcat(iri->string, ","); + gf_bs_read_data(lsr->bs, iri->string + len_rad + 1, len); + iri->string[len_rad + 1 + len] = 0; + } + } + GF_LSR_READ_INT(lsr, val, 1, "hasID"); + if (val) lsr_read_codec_IDREF(lsr, iri, "idref"); + + GF_LSR_READ_INT(lsr, val, 1, "hasStreamID"); + if (val) { + iri->type = XMLRI_STREAMID; + iri->lsr_stream_id = lsr_read_vluimsbf5(lsr, name); + GF_LSR_READ_INT(lsr, val, 1, "reserved"); + if (val) { + u32 len = lsr_read_vluimsbf5(lsr, "len"); + GF_LSR_READ_INT(lsr, val, len, "reserved"); + } + } +} + +static void lsr_read_paint(GF_LASeRCodec *lsr, SVG_Paint *paint, const char *name) +{ + u32 val; + GF_LSR_READ_INT(lsr, val, 1, "hasIndex"); + if (val) { + GF_LSR_READ_INT(lsr, val, lsr->colorIndexBits, name); + lsr_get_color(lsr, val, &paint->color); + paint->type = SVG_PAINT_COLOR; + paint->color.type = 0; + } else { + GF_LSR_READ_INT(lsr, val, 2, "enum"); + switch (val) { + case 0: + GF_LSR_READ_INT(lsr, val, 2, "choice"); + switch (val) { + case 0: paint->type = SVG_PAINT_INHERIT; break; + case 1: paint->type = SVG_PAINT_COLOR; paint->color.type = SVG_COLOR_CURRENTCOLOR; break; + default: paint->type = SVG_PAINT_NONE; break; + } + break; + case 1: + { + XMLRI iri; + memset(&iri, 0, sizeof(XMLRI)); + iri.type = 0xFF; + lsr_read_any_uri(lsr, &iri, name); + gf_node_unregister_iri(lsr->sg, &iri); + paint->type = SVG_PAINT_URI; + if (iri.string) { + paint->type = SVG_PAINT_URI; + paint->iri.type = XMLRI_STRING; + paint->iri.string = iri.string; + } else if (iri.target) { + paint->iri.type = XMLRI_ELEMENTID; + paint->iri.target = iri.target; + } + } + break; + case 2: + { + char *sysPaint=NULL; + lsr_read_byte_align_string(lsr, &sysPaint, "systemsPaint"); + if (sysPaint) { + paint->type = SVG_PAINT_COLOR; + paint->color.type = gf_svg_get_system_paint_server_type(sysPaint); + free(sysPaint); + } + } + break; + case 3: + lsr_read_extension(lsr, name); + break; + } + } +} + +static void lsr_read_string_attribute(GF_LASeRCodec *lsr, GF_Node *elt, u32 tag, char *name) +{ + u32 val; + GF_LSR_READ_INT(lsr, val, 1, name); + if (val) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(elt, tag, 1, 0, &info); + lsr_read_byte_align_string(lsr, info.far_ptr, name); + } +} +static void lsr_read_id(GF_LASeRCodec *lsr, GF_Node *n) +{ + GF_FieldInfo info; + u32 val, id, i, count; + char *name; + GF_LSR_READ_INT(lsr, val, 1, "has_id"); + if (!val) return; + + name = NULL; + id = 1+lsr_read_vluimsbf5(lsr, "ID"); + gf_node_set_id(n, id, name); + + GF_LSR_READ_INT(lsr, val, 1, "reserved"); + /*currently not used*/ + if (val) { + u32 len = lsr_read_vluimsbf5(lsr, "len"); + GF_LSR_READ_INT(lsr, val, len, "reserved"); + } + + /*update all pending HREFs*/ + count = gf_list_count(lsr->defered_hrefs); + for (i=0; idefered_hrefs, i); + char *str_id = href->string; + if (str_id[0] == '#') str_id++; + /*skip 'N'*/ + str_id++; + if (id == (1 + (u32) atoi(str_id))) { + href->target = (SVG_Element*) n; + free(href->string); + href->string = NULL; + gf_list_rem(lsr->defered_hrefs, i); + i--; + count--; + } + } + + /*update unresolved listeners*/ + count = gf_list_count(lsr->defered_listeners); + for (i=0; idefered_listeners, i); + + par = NULL; + if (gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_observer, 0, 0, &info) == GF_OK) { + observer = (XMLRI*)info.far_ptr; + if (observer->type == XMLRI_ELEMENTID) { + if (!observer->target) continue; + else par = observer->target; + } + } + if (gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_target, 0, 0, &info) == GF_OK) { + if (((XMLRI*)info.far_ptr)->type == XMLRI_ELEMENTID) { + if (!((XMLRI*)info.far_ptr)->target) continue; + else if (!par) par = ((XMLRI*)info.far_ptr)->target; + } + } + /*FIXME - double check with XML events*/ + if (!par && !observer) { + if (gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_event, 0, 0, &info) == GF_OK) { + XMLEV_Event *ev = (XMLEV_Event *)info.far_ptr; + /*all non-UI get attched to root*/ + if (ev && (ev->type>GF_EVENT_MOUSEWHEEL)) { + par = (GF_Node*) lsr->current_root; + } + } + } + + assert(par); + gf_node_dom_listener_add(par, listener); + gf_list_rem(lsr->defered_listeners, i); + i--; + count--; + } + + /*update all pending animations*/ + count = gf_list_count(lsr->defered_anims); + for (i=0; idefered_anims, i); + if (lsr_setup_smil_anim(lsr, elt, NULL)) { + gf_list_rem(lsr->defered_anims, i); + i--; + count--; + gf_node_init((GF_Node*)elt); + } + } +} + +static Fixed lsr_translate_coords(GF_LASeRCodec *lsr, u32 val, u32 nb_bits) +{ +#ifdef GPAC_FIXED_POINT + if (val >> (nb_bits-1) ) { + s32 neg = (s32) val - (1<res_factor); + return gf_divfix(INT2FIX(neg), lsr->res_factor); + } else { + if (val > FIX_ONE / 2) + return 2 * gf_divfix(INT2FIX(val/2), lsr->res_factor); + return gf_divfix(INT2FIX(val), lsr->res_factor); + } +#else + if (val >> (nb_bits-1) ) { + s32 neg = (s32) val - (1<res_factor); + } else { + return gf_divfix(INT2FIX(val), lsr->res_factor); + } +#endif +} + +static Fixed lsr_translate_scale(GF_LASeRCodec *lsr, u32 val) +{ + if (val >> (lsr->coord_bits-1) ) { + s32 v = val - (1<coord_bits); + return INT2FIX(v) / 256 ; + } else { + return INT2FIX(val) / 256; + } +} +static void lsr_read_matrix(GF_LASeRCodec *lsr, SVG_Transform *mx) +{ + u32 flag; + gf_mx2d_init(mx->mat); + mx->is_ref = 0; + GF_LSR_READ_INT(lsr, flag, 1, "isNotMatrix"); + if (flag) { + GF_LSR_READ_INT(lsr, flag, 1, "isRef"); + if (flag) { + GF_LSR_READ_INT(lsr, flag, 1, "hasXY"); + if (flag) { + mx->mat.m[2] = lsr_read_fixed_16_8(lsr, "valueX"); + mx->mat.m[5] = lsr_read_fixed_16_8(lsr, "valueY"); + } + } else { + lsr_read_extension(lsr, "ext"); + } + } else { + lsr->coord_bits += lsr->scale_bits; + GF_LSR_READ_INT(lsr, flag, 1, "xx_yy_present"); + if (flag) { + GF_LSR_READ_INT(lsr, flag, lsr->coord_bits, "xx"); + mx->mat.m[0] = lsr_translate_scale(lsr, flag); + GF_LSR_READ_INT(lsr, flag, lsr->coord_bits, "yy"); + mx->mat.m[4] = lsr_translate_scale(lsr, flag); + } else { + mx->mat.m[0] = mx->mat.m[4] = FIX_ONE; + } + GF_LSR_READ_INT(lsr, flag, 1, "xy_yx_present"); + if (flag) { + GF_LSR_READ_INT(lsr, flag, lsr->coord_bits, "xy"); + mx->mat.m[1] = lsr_translate_scale(lsr, flag); + GF_LSR_READ_INT(lsr, flag, lsr->coord_bits, "yx"); + mx->mat.m[3] = lsr_translate_scale(lsr, flag); + } + + GF_LSR_READ_INT(lsr, flag, 1, "xz_yz_present"); + if (flag) { + GF_LSR_READ_INT(lsr, flag, lsr->coord_bits, "xz"); + mx->mat.m[2] = lsr_translate_coords(lsr, flag, lsr->coord_bits); + GF_LSR_READ_INT(lsr, flag, lsr->coord_bits, "yz"); + mx->mat.m[5] = lsr_translate_coords(lsr, flag, lsr->coord_bits); + } + lsr->coord_bits -= lsr->scale_bits; + } +} + +static Fixed lsr_read_fixed_clamp(GF_LASeRCodec *lsr, const char *name) +{ + s32 val; + GF_LSR_READ_INT(lsr, val, 8, name); + return INT2FIX(val) / 255; +} + +static void lsr_read_focus(GF_LASeRCodec *lsr, SVG_Focus *foc, const char *name) +{ + u32 flag; + + if (foc->target.string) { + free(foc->target.string); + foc->target.string = NULL; + } + if (foc->target.target) foc->target.target = NULL; + gf_node_unregister_iri(lsr->sg, &foc->target); + + GF_LSR_READ_INT(lsr, flag, 1, "isEnum"); + if (flag) { + GF_LSR_READ_INT(lsr, foc->type, 1, "enum"); + } else { + foc->type = SVG_FOCUS_IRI; + lsr_read_codec_IDREF(lsr, &foc->target, "id"); + } +} + +static void lsr_restore_base(GF_LASeRCodec *lsr, SVG_Element *elt, SVG_Element *base, Bool reset_fill, Bool reset_stroke) +{ + GF_Err e; + GF_FieldInfo f_base, f_clone; + SVGAttribute *att; + + /*clone all propertie from base*/ + att = base->attributes; + while (att) { + Bool is_fill, is_stroke; + is_fill = is_stroke = 0; + switch (att->tag) { + /*for all properties*/ + case TAG_SVG_ATT_fill: + is_fill = 1; + break; + case TAG_SVG_ATT_stroke: + is_stroke = 1; + break; + case TAG_SVG_ATT_audio_level: + case TAG_SVG_ATT_color: + case TAG_SVG_ATT_color_rendering: + case TAG_SVG_ATT_display: + case TAG_SVG_ATT_display_align: + case TAG_SVG_ATT_fill_opacity: + case TAG_SVG_ATT_fill_rule: + case TAG_SVG_ATT_font_family: + case TAG_SVG_ATT_font_size: + case TAG_SVG_ATT_font_style: + case TAG_SVG_ATT_font_variant: + case TAG_SVG_ATT_font_weight: + case TAG_SVG_ATT_image_rendering: + case TAG_SVG_ATT_line_increment: + case TAG_SVG_ATT_opacity: + case TAG_SVG_ATT_pointer_events: + case TAG_SVG_ATT_shape_rendering: + case TAG_SVG_ATT_solid_color: + case TAG_SVG_ATT_solid_opacity: + case TAG_SVG_ATT_stop_color: + case TAG_SVG_ATT_stop_opacity: + case TAG_SVG_ATT_stroke_dasharray: + case TAG_SVG_ATT_stroke_dashoffset: + case TAG_SVG_ATT_stroke_linecap: + case TAG_SVG_ATT_stroke_linejoin: + case TAG_SVG_ATT_stroke_miterlimit: + case TAG_SVG_ATT_stroke_opacity: + case TAG_SVG_ATT_stroke_width: + case TAG_SVG_ATT_text_align: + case TAG_SVG_ATT_text_anchor: + case TAG_SVG_ATT_text_rendering: + case TAG_SVG_ATT_vector_effect: + case TAG_SVG_ATT_viewport_fill: + case TAG_SVG_ATT_viewport_fill_opacity: + case TAG_SVG_ATT_visibility: + /*and xml:_class*/ + case TAG_SVG_ATT__class: + case TAG_SVG_ATT_externalResourcesRequired: + break; + + /*pathLength for path*/ + case TAG_SVG_ATT_pathLength: + break; + /*rx & ry for rect*/ + case TAG_SVG_ATT_rx: + case TAG_SVG_ATT_ry: + if (base->sgprivate->tag!=TAG_SVG_rect) { + att = att->next; + continue; + } + break; + /*x & y for use*/ + case TAG_SVG_ATT_x: + case TAG_SVG_ATT_y: + if (base->sgprivate->tag!=TAG_SVG_use) { + att = att->next; + continue; + } + break; + /*editable & rotate for text*/ + case TAG_SVG_ATT_editable: + case TAG_SVG_ATT_rotate: + if (base->sgprivate->tag!=TAG_SVG_text) { + att = att->next; + continue; + } + break; + case TAG_SVG_ATT_transform: + break; + default: + att = att->next; + continue; + } + /*clone field*/ + e = gf_node_get_attribute_by_tag((GF_Node*)elt, att->tag, 1, 0, &f_clone); + if (e) goto err_exit; + f_base.fieldIndex = att->tag; + f_base.fieldType = att->data_type; + f_base.far_ptr = att->data; + e = gf_svg_attributes_copy(&f_clone, &f_base, 0); + if (e) goto err_exit; + + if (is_fill && reset_fill) { + SVG_Paint*p = (SVG_Paint*)f_clone.far_ptr; + if (p->iri.string) free(p->iri.string); + memset(p, 0, sizeof(SVG_Paint)); + } + if (is_stroke && reset_stroke) { + SVG_Paint*p = (SVG_Paint*)f_clone.far_ptr; + if (p->iri.string) free(p->iri.string); + memset(p, 0, sizeof(SVG_Paint)); + } + att = att->next; + } + return; + +err_exit: + lsr->last_error = e; +} + + +static u32 lsr_to_dom_key(u32 lsr_k) +{ + switch (lsr_k) { + case 0: return GF_KEY_STAR; + case 1: return GF_KEY_0; + case 2: return GF_KEY_1; + case 3: return GF_KEY_2; + case 4: return GF_KEY_3; + case 5: return GF_KEY_4; + case 6: return GF_KEY_5; + case 7: return GF_KEY_6; + case 8: return GF_KEY_7; + case 9: return GF_KEY_8; + case 10: return GF_KEY_9; + case 12: return GF_KEY_DOWN; + case 14: return GF_KEY_LEFT; + case 16: return GF_KEY_RIGHT; + case 20: return GF_KEY_UP; + /*WHAT IS ANY_KEY (11) ??*/ + case 13: return GF_KEY_ENTER; + case 15: return GF_KEY_ESCAPE; + case 17: return GF_KEY_NUMBER; + case 18: return GF_KEY_CELL_SOFT1; + case 19: return GF_KEY_CELL_SOFT2; + default: + /*use '*' by default ... */ + return 0; + } +} + +static void lsr_read_event_type(GF_LASeRCodec *lsr, XMLEV_Event *evtType) +{ + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "choice"); + if (!flag) { + char *evtName, *sep; + evtName = NULL; + lsr_read_byte_align_string(lsr, &evtName, "evtString"); + evtType->type = evtType->parameter = 0; + if (evtName) { + sep = strchr(evtName, '('); + if (sep) { + char *param; + sep[0] = 0; + evtType->type = gf_dom_event_type_by_name(evtName); + sep[0] = '('; + param = sep+1; + sep = strchr(evtName, ')'); + if (sep) sep[0]=0; + if (evtType->type==GF_EVENT_REPEAT) { + evtType->parameter = atoi(param); + } else { + evtType->parameter = gf_dom_get_key_type(param); + } + } else { + evtType->type = gf_dom_event_type_by_name(evtName); + } + free(evtName); + } + } else { + evtType->parameter = 0; + GF_LSR_READ_INT(lsr, flag, 6, "event"); + switch (flag) { + case LSR_EVT_abort: evtType->type = GF_EVENT_ABORT; break; + case LSR_EVT_accessKey: evtType->type = GF_EVENT_KEYDOWN; break; + case LSR_EVT_activate: evtType->type = GF_EVENT_ACTIVATE; break; + case LSR_EVT_activatedEvent: evtType->type = GF_EVENT_ACTIVATED; break; + case LSR_EVT_beginEvent: evtType->type = GF_EVENT_BEGIN_EVENT; break; + case LSR_EVT_click: evtType->type = GF_EVENT_CLICK; break; + case LSR_EVT_deactivatedEvent: evtType->type = GF_EVENT_DEACTIVATED; break; + case LSR_EVT_endEvent: evtType->type = GF_EVENT_END_EVENT; break; + case LSR_EVT_error: evtType->type = GF_EVENT_ERROR; break; + case LSR_EVT_executionTime: evtType->type = GF_EVENT_EXECUTION_TIME; break; + case LSR_EVT_focusin: evtType->type = GF_EVENT_FOCUSIN; break; + case LSR_EVT_focusout: evtType->type = GF_EVENT_FOCUSOUT; break; + case LSR_EVT_keydown: evtType->type = GF_EVENT_KEYDOWN; break; + case LSR_EVT_keyup: evtType->type = GF_EVENT_KEYUP; break; + case LSR_EVT_load: evtType->type = GF_EVENT_LOAD; break; + case LSR_EVT_longAccessKey: evtType->type = GF_EVENT_LONGKEYPRESS; break; + case LSR_EVT_mousedown: evtType->type = GF_EVENT_MOUSEDOWN; break; + case LSR_EVT_mousemove: evtType->type = GF_EVENT_MOUSEMOVE; break; + case LSR_EVT_mouseout: evtType->type = GF_EVENT_MOUSEOUT; break; + case LSR_EVT_mouseover: evtType->type = GF_EVENT_MOUSEOVER; break; + case LSR_EVT_mouseup: evtType->type = GF_EVENT_MOUSEUP; break; + case LSR_EVT_pause: evtType->type = GF_EVENT_PAUSE; break; + case LSR_EVT_pausedEvent: evtType->type = GF_EVENT_PAUSED_EVENT; break; + case LSR_EVT_play: evtType->type = GF_EVENT_PLAY; break; + case LSR_EVT_repeatEvent: evtType->type = GF_EVENT_REPEAT_EVENT; break; + case LSR_EVT_repeatKey: evtType->type = GF_EVENT_REPEAT_KEY; break; + case LSR_EVT_resize: evtType->type = GF_EVENT_RESIZE; break; + case LSR_EVT_resumedEvent: evtType->type = GF_EVENT_RESUME_EVENT; break; + case LSR_EVT_scroll: evtType->type = GF_EVENT_SCROLL; break; + case LSR_EVT_shortAccessKey: evtType->type = GF_EVENT_SHORT_ACCESSKEY; break; + case LSR_EVT_textinput: evtType->type = GF_EVENT_TEXTINPUT; break; + case LSR_EVT_unload: evtType->type = GF_EVENT_UNLOAD; break; + case LSR_EVT_zoom: evtType->type = GF_EVENT_ZOOM; break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[LASeR] Undefined LASeR event %d\n", flag)); + break; + } + switch (flag) { + case LSR_EVT_accessKey: + case LSR_EVT_longAccessKey: + case LSR_EVT_repeatKey: + case LSR_EVT_shortAccessKey: + evtType->parameter = lsr_read_vluimsbf5(lsr, "keyCode"); + evtType->parameter = lsr_to_dom_key(evtType->parameter); + break; + } + } +} + +static SMIL_Time *lsr_read_smil_time(GF_LASeRCodec *lsr, GF_Node *n) +{ + SMIL_Time *t; + u32 val; + + GF_SAFEALLOC(t, SMIL_Time); + t->type = GF_SMIL_TIME_CLOCK; + + GF_LSR_READ_INT(lsr, val, 1, "hasEvent"); + if (val) { + t->type = GF_SMIL_TIME_EVENT; + GF_LSR_READ_INT(lsr, val, 1, "hasIdentifier"); + if (val) { + XMLRI iri; + iri.type = 0xFF; + iri.string = NULL; + lsr_read_codec_IDREF(lsr, &iri, "idref"); + gf_node_unregister_iri(lsr->sg, &iri); + if (iri.string) { + t->element_id = iri.string; + } else { + t->element = (GF_Node *)iri.target; + } + } + lsr_read_event_type(lsr, &t->event); + if (t->event.type==GF_EVENT_EXECUTION_TIME) { + t->type = GF_SMIL_TIME_CLOCK; + t->clock = gf_node_get_scene_time(n); + } + } + GF_LSR_READ_INT(lsr, val, 1, "hasClock"); + if (val) { + u32 now; + GF_LSR_READ_INT(lsr, val, 1, "sign"); + now = lsr_read_vluimsbf5(lsr, "value"); + t->clock = now; + t->clock /= lsr->time_resolution; + if (val) t->clock *= -1; + } + return t; +} + +static void lsr_read_smil_times(GF_LASeRCodec *lsr, GF_Node *n, u32 tag, SMIL_Times *times, const char *name, Bool skipable) +{ + GF_FieldInfo info; + SMIL_Time *v; + u32 val, i, count; + + if (skipable) { + GF_LSR_READ_INT(lsr, val, 1, name); + if (!val) return; + } + if (!times) { + lsr->last_error = gf_node_get_attribute_by_tag(n, tag, 1, 0, &info); + times = (SMIL_Times*)info.far_ptr; + } + + while (gf_list_count(*times)) { + v = (SMIL_Time *)gf_list_last(*times); + gf_list_rem_last(*times); + if (v->element_id) free(v->element_id); + free(v); + } + + GF_LSR_READ_INT(lsr, val, 1, "choice"); + if (val) { + GF_SAFEALLOC(v, SMIL_Time); + v->type = GF_SMIL_TIME_INDEFINITE; + gf_list_add(*times, v); + return; + } + count = lsr_read_vluimsbf5(lsr, "count"); + for (i=0; ilast_error = gf_node_get_attribute_by_tag(n, tag, 1, 0, &info); + if (lsr->last_error) return; + smil = (SMIL_Duration *)info.far_ptr; + } + smil->type = 0; + + GF_LSR_READ_INT(lsr, val, 1, "choice"); + if (val) { + GF_LSR_READ_INT(lsr, smil->type, 2, "time"); + } else { + Bool sign; + u32 now; + GF_LSR_READ_INT(lsr, sign, 1, "sign"); + now = lsr_read_vluimsbf5(lsr, "value"); + smil->clock_value = now; + smil->clock_value /= lsr->time_resolution; + if (sign) smil->clock_value *= -1; + smil->type = SMIL_DURATION_DEFINED; + } +} +static void lsr_read_duration(GF_LASeRCodec *lsr, GF_Node *n) +{ + lsr_read_duration_ex(lsr, n, TAG_SVG_ATT_dur, NULL, "dur", 1); +} +/*TODO Add decent error checking...*/ +static void lsr_read_rare_full(GF_LASeRCodec *lsr, GF_Node *n) +{ + GF_FieldInfo info; + u32 i, nb_rare, field_rare; + s32 field_tag; + + GF_LSR_READ_INT(lsr, nb_rare, 1, "has_rare"); + if (!nb_rare) return; + GF_LSR_READ_INT(lsr, nb_rare, 6, "nbOfAttributes"); + + for (i=0; iinfo->cfg.extensionIDBits, "extensionID"); + len = lsr_read_vluimsbf5(lsr, "len"); + if (extID==2) { + GF_LSR_READ_INT(lsr, len, 2, "nbOfAttributes"); + for (j=0; jlast_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_syncMaster, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SVG_Boolean *)info.far_ptr, 1, "syncMaster"); + break; + case 1: + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_focusHighlight, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SVG_FocusHighlight *)info.far_ptr, 2, "focusHighlight"); + break; + case 2: + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_initialVisibility, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SVG_InitialVisibility *)info.far_ptr, 2, "initialVisibility"); + break; + case 3: + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_fullscreen, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SVG_Boolean *)info.far_ptr, 1, "fullscreen"); + break; + case 4: + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_requiredFonts, 1, 0, &info); + lsr_read_byte_align_string(lsr, info.far_ptr, "requiredFonts"); + break; + } + } + } else { + gf_bs_read_int(lsr->bs, len); + } + GF_LSR_READ_INT(lsr, extID, 1, "hasNextExtension"); + if (!extID) break; + } + continue; + } + field_tag = gf_lsr_rare_type_to_attribute(field_rare); + if (field_tag==-1) { + return; + } + lsr->last_error = gf_node_get_attribute_by_tag(n, field_tag, 1, 0, &info); + if (!info.far_ptr) lsr->last_error = GF_NOT_SUPPORTED; + if (lsr->last_error) return; + + switch (field_tag) { + case TAG_SVG_ATT__class: + lsr_read_byte_align_string(lsr, info.far_ptr, "class"); + break; + /*properties*/ + case TAG_SVG_ATT_audio_level: + ((SVG_Number*)info.far_ptr)->value = lsr_read_fixed_clamp(lsr, "audio-level"); + break; + case TAG_SVG_ATT_color: + lsr_read_paint(lsr, (SVG_Paint *)info.far_ptr, "color"); + break; + case TAG_SVG_ATT_color_rendering: + GF_LSR_READ_INT(lsr, *(SVG_RenderingHint*)info.far_ptr, 2, "color-rendering"); + break; + case TAG_SVG_ATT_display: + GF_LSR_READ_INT(lsr, *(SVG_Display*)info.far_ptr, 5, "display"); + break; + case TAG_SVG_ATT_display_align: + GF_LSR_READ_INT(lsr, *(SVG_DisplayAlign*)info.far_ptr, 3, "display-align"); break; + case TAG_SVG_ATT_fill_opacity: + ((SVG_Number*)info.far_ptr)->type = SVG_NUMBER_VALUE; + ((SVG_Number*)info.far_ptr)->value = lsr_read_fixed_clamp(lsr, "fill-opacity"); + break; + case TAG_SVG_ATT_fill_rule: + GF_LSR_READ_INT(lsr, *(SVG_FillRule*)info.far_ptr, 2, "fill-rule"); + break; + case TAG_SVG_ATT_image_rendering: + GF_LSR_READ_INT(lsr, *(SVG_RenderingHint*)info.far_ptr, 2, "image-rendering"); + break; + case TAG_SVG_ATT_line_increment: + lsr_read_line_increment_type(lsr, info.far_ptr, "line-increment"); + break; + case TAG_SVG_ATT_pointer_events: + GF_LSR_READ_INT(lsr, *(SVG_PointerEvents*)info.far_ptr, 4, "pointer-events"); + break; + case TAG_SVG_ATT_shape_rendering: + GF_LSR_READ_INT(lsr, *(SVG_RenderingHint*)info.far_ptr, 3, "shape-rendering"); + break; + case TAG_SVG_ATT_solid_color: + lsr_read_paint(lsr, info.far_ptr, "solid-color"); + break; + case TAG_SVG_ATT_solid_opacity: + ((SVG_Number*)info.far_ptr)->type = SVG_NUMBER_VALUE; + ((SVG_Number*)info.far_ptr)->value = lsr_read_fixed_clamp(lsr, "solid-opacity"); + break; + case TAG_SVG_ATT_stop_color: + lsr_read_paint(lsr, info.far_ptr, "stop-color"); + break; + case TAG_SVG_ATT_stop_opacity: + ((SVG_Number*)info.far_ptr)->type = SVG_NUMBER_VALUE; + ((SVG_Number*)info.far_ptr)->value = lsr_read_fixed_clamp(lsr, "stop-opacity"); + break; + case TAG_SVG_ATT_stroke_dasharray: + { + u32 j, flag; + SVG_StrokeDashArray *da = (SVG_StrokeDashArray *)info.far_ptr; + GF_LSR_READ_INT(lsr, flag, 1, "dashArray"); + if (flag) { + da->type=SVG_STROKEDASHARRAY_INHERIT; + } else { + da->type=SVG_STROKEDASHARRAY_ARRAY; + da->array.count = lsr_read_vluimsbf5(lsr, "len"); + da->array.vals = (Fixed*)malloc(sizeof(Fixed)*da->array.count); + for (j=0; jarray.count; j++) { + da->array.vals[j] = lsr_read_fixed_16_8(lsr, "dash"); + } + } + } + break; + case TAG_SVG_ATT_stroke_dashoffset: + lsr_read_fixed_16_8i(lsr, info.far_ptr, "dashOffset"); + break; + + case TAG_SVG_ATT_stroke_linecap: + GF_LSR_READ_INT(lsr, *(SVG_StrokeLineCap*)info.far_ptr, 2, "stroke-linecap"); + break; + case TAG_SVG_ATT_stroke_linejoin: + GF_LSR_READ_INT(lsr, *(SVG_StrokeLineJoin*)info.far_ptr, 2, "stroke-linejoin"); + break; + case TAG_SVG_ATT_stroke_miterlimit: + lsr_read_fixed_16_8i(lsr, info.far_ptr, "miterLimit"); + break; + case TAG_SVG_ATT_stroke_opacity: + ((SVG_Number*)info.far_ptr)->type = SVG_NUMBER_VALUE; + ((SVG_Number*)info.far_ptr)->value = lsr_read_fixed_clamp(lsr, "stroke-opacity"); + break; + case TAG_SVG_ATT_stroke_width: + lsr_read_fixed_16_8i(lsr, info.far_ptr, "strokeWidth"); + break; + case TAG_SVG_ATT_text_anchor: + GF_LSR_READ_INT(lsr, *(SVG_TextAnchor*)info.far_ptr, 2, "text-achor"); + break; + case TAG_SVG_ATT_text_rendering: + GF_LSR_READ_INT(lsr, *(SVG_RenderingHint*)info.far_ptr, 3, "text-rendering"); + break; + case TAG_SVG_ATT_viewport_fill: + lsr_read_paint(lsr, info.far_ptr, "viewport-fill"); + break; + case TAG_SVG_ATT_viewport_fill_opacity: + ((SVG_Number*)info.far_ptr)->type = SVG_NUMBER_VALUE; + ((SVG_Number*)info.far_ptr)->value = lsr_read_fixed_clamp(lsr, "viewport-fill-opacity"); + break; + case TAG_SVG_ATT_vector_effect: + GF_LSR_READ_INT(lsr, *(SVG_VectorEffect*)info.far_ptr, 4, "vector-effect"); + break; + case TAG_SVG_ATT_visibility: + GF_LSR_READ_INT(lsr, *(SVG_Visibility*)info.far_ptr, 2, "visibility"); + break; + case TAG_SVG_ATT_requiredExtensions: + lsr_read_byte_align_string_list(lsr, *(GF_List**)info.far_ptr, "requiredExtensions", 1); + break; + case TAG_SVG_ATT_requiredFormats: + lsr_read_byte_align_string_list(lsr, *(GF_List**)info.far_ptr, "requiredFormats", 0); + break; + case TAG_SVG_ATT_requiredFeatures: + { + u32 j, fcount = lsr_read_vluimsbf5(lsr, "count"); + for (j=0; jstring, "xml:base"); + ((XMLRI*)info.far_ptr)->type = XMLRI_STRING; + break; + case TAG_XML_ATT_lang: + lsr_read_byte_align_string(lsr, info.far_ptr, "xml:lang"); + break; + case TAG_XML_ATT_space: + GF_LSR_READ_INT(lsr, *(XML_Space*)info.far_ptr, 1, "xml:space"); + break; + /*focusable*/ + case TAG_SVG_ATT_nav_next: + lsr_read_focus(lsr, (SVG_Focus*)info.far_ptr, "focusNext"); break; + case TAG_SVG_ATT_nav_up: + lsr_read_focus(lsr, (SVG_Focus*)info.far_ptr, "focusNorth"); break; + case TAG_SVG_ATT_nav_up_left: + lsr_read_focus(lsr, (SVG_Focus*)info.far_ptr, "focusNorthEast"); break; + case TAG_SVG_ATT_nav_up_right: + lsr_read_focus(lsr, (SVG_Focus*)info.far_ptr, "focusNorthWest"); break; + case TAG_SVG_ATT_nav_prev: + lsr_read_focus(lsr, (SVG_Focus*)info.far_ptr, "focusPrev"); break; + case TAG_SVG_ATT_nav_down: + lsr_read_focus(lsr, (SVG_Focus*)info.far_ptr, "focusSouth"); break; + case TAG_SVG_ATT_nav_down_left: + lsr_read_focus(lsr, (SVG_Focus*)info.far_ptr, "focusSouthEast"); break; + case TAG_SVG_ATT_nav_down_right: + lsr_read_focus(lsr, (SVG_Focus*)info.far_ptr, "focusSouthWest"); break; + case TAG_SVG_ATT_nav_left: + lsr_read_focus(lsr, (SVG_Focus*)info.far_ptr, "focusEast"); break; + case TAG_SVG_ATT_focusable: + /*wrong !!*/ + GF_LSR_READ_INT(lsr, *(SVG_Focusable*)info.far_ptr, 2, "focusable"); + break; + case TAG_SVG_ATT_nav_right: + lsr_read_focus(lsr, (SVG_Focus*)info.far_ptr, "focusWest"); + break; + case TAG_SVG_ATT_transform: + lsr_read_matrix(lsr, info.far_ptr); + break; + case TAG_SVG_ATT_text_decoration: + /*FIXME ASAP*/ + assert(0); + break; + + case TAG_SVG_ATT_font_variant: + GF_LSR_READ_INT(lsr, *(SVG_FontVariant*)info.far_ptr, 2, "font-variant"); + break; + case TAG_SVG_ATT_font_family: + { + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "isInherit"); + if (flag) { + ((SVG_FontFamily*)info.far_ptr)->type = SVG_FONTFAMILY_INHERIT; + } else { + char *ft; + ((SVG_FontFamily*)info.far_ptr)->type = SVG_FONTFAMILY_VALUE; + GF_LSR_READ_INT(lsr, flag, lsr->fontIndexBits, "fontIndex"); + ft = (char*)gf_list_get(lsr->font_table, flag); + if (ft) ((SVG_FontFamily*)info.far_ptr)->value = strdup(ft); + } + } + break; + case TAG_SVG_ATT_font_size: + lsr_read_fixed_16_8i(lsr, info.far_ptr, "fontSize"); + break; + case TAG_SVG_ATT_font_style: + GF_LSR_READ_INT(lsr, *(SVG_FontStyle*)info.far_ptr, 3, "fontStyle"); + break; + case TAG_SVG_ATT_font_weight: + GF_LSR_READ_INT(lsr, *(SVG_FontWeight*)info.far_ptr, 4, "fontWeight"); + break; + case TAG_XLINK_ATT_title: + lsr_read_byte_align_string(lsr, info.far_ptr, "xlink:title"); + break; + case TAG_XLINK_ATT_type: + /*TODO FIXME*/ + GF_LSR_READ_INT(lsr, field_rare, 3, "xlink:type"); + break; + case TAG_XLINK_ATT_role: + lsr_read_any_uri(lsr, info.far_ptr, "xlink:role"); + break; + case TAG_XLINK_ATT_arcrole: + lsr_read_any_uri(lsr, info.far_ptr, "xlink:arcrole"); + break; + case TAG_XLINK_ATT_actuate: + /*TODO FIXME*/ + GF_LSR_READ_INT(lsr, field_rare, 2, "xlink:actuate"); + break; + case TAG_XLINK_ATT_show: + /*TODO FIXME*/ + GF_LSR_READ_INT(lsr, field_rare, 3, "xlink:show"); + break; + case TAG_SVG_ATT_end: + lsr_read_smil_times(lsr, NULL, 0, info.far_ptr, "end", 0); + break; + case TAG_SVG_ATT_max: lsr_read_duration_ex(lsr, NULL, 0, info.far_ptr, "min", 0); break; + case TAG_SVG_ATT_min: lsr_read_duration_ex(lsr, NULL, 0, info.far_ptr, "min", 0); break; + } + if (lsr->last_error) break; + } +} + +#define lsr_read_rare(_a, _b) lsr_read_rare_full(_a, _b) + +static void lsr_read_fill(GF_LASeRCodec *lsr, GF_Node *n) +{ + Bool has_fill; + GF_LSR_READ_INT(lsr, has_fill, 1, "fill"); + if (has_fill) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_fill, 1, 0, &info); + lsr_read_paint(lsr, info.far_ptr, "fill"); + } +} + +static void lsr_read_stroke(GF_LASeRCodec *lsr, GF_Node *n) +{ + Bool has_stroke; + GF_LSR_READ_INT(lsr, has_stroke, 1, "has_stroke"); + if (has_stroke) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_stroke, 1, 0, &info); + lsr_read_paint(lsr, info.far_ptr, "stroke"); + } +} +static void lsr_read_href(GF_LASeRCodec *lsr, GF_Node *n) +{ + Bool has_href; + GF_LSR_READ_INT(lsr, has_href, 1, "has_href"); + if (has_href) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_XLINK_ATT_href, 1, 0, &info); + lsr_read_any_uri(lsr, info.far_ptr, "href"); + } +} + +static void lsr_read_accumulate(GF_LASeRCodec *lsr, GF_Node *n) +{ + Bool v; + GF_LSR_READ_INT(lsr, v, 1, "has_accumulate"); + if (v) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_accumulate, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SMIL_Accumulate*)info.far_ptr, 1, "accumulate"); + } +} +static void lsr_read_additive(GF_LASeRCodec *lsr, GF_Node *n) +{ + Bool v; + GF_LSR_READ_INT(lsr, v, 1, "has_additive"); + if (v) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_additive, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SMIL_Additive*)info.far_ptr, 1, "additive"); + } +} +static void lsr_read_calc_mode(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 v; + /*SMIL_CALCMODE_LINEAR is default and 0 in our code*/ + GF_LSR_READ_INT(lsr, v, 1, "has_calcMode"); + if (v) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_calcMode, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SMIL_CalcMode*)info.far_ptr, 2, "calcMode"); + } +} + +static void lsr_read_attribute_name_ex(GF_LASeRCodec *lsr, GF_Node *n, Bool skippable) +{ + u32 val = 1; + if (skippable) { + GF_LSR_READ_INT(lsr, val, 1, "hasAttributeName"); + if (!val) return; + } + + GF_LSR_READ_INT(lsr, val, 1, "choice"); + if (val) { + lsr_read_vluimsbf5(lsr, "item[i]"); + lsr_read_vluimsbf5(lsr, "item[i]"); + return; + } else { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_attributeName, 1, 0, &info); + GF_LSR_READ_INT(lsr, val, 8, "attributeType"); + + /*translate type to attribute tag*/ + ((SMIL_AttributeName*)info.far_ptr)->type = gf_lsr_anim_type_to_attribute(val); + } +} +static void lsr_read_attribute_name(GF_LASeRCodec *lsr, GF_Node *n) +{ + lsr_read_attribute_name_ex(lsr, n, 1); +} + +static void lsr_translate_anim_value(SMIL_AnimateValue *val, u32 coded_type) +{ + switch (val->type) { + case SVG_StrokeDashArray_datatype: + { + SVG_StrokeDashArray *da; + GF_List *l = (GF_List *)val->value; + u32 i; + GF_SAFEALLOC(da, SVG_StrokeDashArray); + da->array.count = gf_list_count(l); + if (!da->array.count) { + da->type = SVG_STROKEDASHARRAY_INHERIT; + } else { + da->type = SVG_STROKEDASHARRAY_ARRAY; + da->array.vals = (Fixed *) malloc(sizeof(Fixed)*da->array.count); + for (i=0; iarray.count; i++) { + Fixed *v = (Fixed *)gf_list_get(l, i); + da->array.vals[i] = *v; + free(v); + } + gf_list_del(l); + val->value = da; + } + } + break; + case SVG_ViewBox_datatype: + { + SVG_ViewBox *vb; + GF_List *l = (GF_List *)val->value; + GF_SAFEALLOC(vb, SVG_ViewBox); + if (gf_list_count(l)==4) { + vb->x = * ((Fixed *)gf_list_get(l, 0)); + vb->y = * ((Fixed *)gf_list_get(l, 1)); + vb->width = * ((Fixed *)gf_list_get(l, 2)); + vb->height = * ((Fixed *)gf_list_get(l, 3)); + } + while (gf_list_count(l)) { + Fixed *v = (Fixed *)gf_list_last(l); + free(v); + gf_list_rem_last(l); + } + gf_list_del(l); + val->value = vb; + } + break; + case SVG_Coordinates_datatype: + { + SVG_Coordinates *coords; + if (coded_type==1) { + GF_List *l = gf_list_new(); + /*allocated value is already an SVG number*/ + gf_list_add(l, val->value); + coords = (SVG_Coordinates*)malloc(sizeof(SVG_Coordinates)); + *coords = l; + val->value = coords; + } else if (coded_type==8) { + GF_List *l = (GF_List *)val->value; + u32 i, count = gf_list_count(l); + for (i=0; itype = SVG_NUMBER_VALUE; + c->value = *v; + free(v); + gf_list_rem(l, i); + gf_list_insert(l, c, i); + } + coords = (SVG_Coordinates*)malloc(sizeof(SVG_Coordinates)); + *coords = (GF_List *) val->value; + val->value = coords; + } + } + break; + case SVG_Motion_datatype: + if (coded_type==9) { + GF_Matrix2D *mat; + SVG_Point *pt = (SVG_Point *)val->value; + GF_SAFEALLOC(mat, GF_Matrix2D); + gf_mx2d_init(*mat); + mat->m[2] = pt->x; + mat->m[5] = pt->y; + free(pt); + val->value = mat; + } + break; + } +} +static void lsr_translate_anim_values(SMIL_AnimateValues *val, u32 coded_type) +{ + u32 i, count; + GF_List *list = val->values; + count = gf_list_count(list); + for (i=0; itype) { + case SVG_StrokeDashArray_datatype: + { + SVG_StrokeDashArray *da; + GF_List *l = (GF_List *)gf_list_get(list, i); + u32 j; + GF_SAFEALLOC(da, SVG_StrokeDashArray); + da->array.count = gf_list_count(l); + if (!da->array.count) { + da->type = SVG_STROKEDASHARRAY_INHERIT; + } else { + da->type = SVG_STROKEDASHARRAY_ARRAY; + da->array.vals = (Fixed *)malloc(sizeof(Fixed)*da->array.count); + for (j=0; jarray.count; j++) { + Fixed *v = (Fixed *)gf_list_get(l, j); + da->array.vals[j] = *v; + free(v); + } + gf_list_del(l); + gf_list_rem(list, i); + gf_list_insert(list, da, i); + } + } + break; + case SVG_ViewBox_datatype: + { + SVG_ViewBox *vb; + GF_List *l = (GF_List *)gf_list_get(list, i); + GF_SAFEALLOC(vb, SVG_ViewBox); + if (gf_list_count(l)==4) { + vb->x = * ((Fixed *)gf_list_get(l, 0)); + vb->y = * ((Fixed *)gf_list_get(l, 1)); + vb->width = * ((Fixed *)gf_list_get(l, 2)); + vb->height = * ((Fixed *)gf_list_get(l, 3)); + } + while (gf_list_count(l)) { + Fixed *v=(Fixed *)gf_list_last(l); + free(v); + gf_list_rem_last(l); + } + gf_list_del(l); + gf_list_rem(list, i); + gf_list_insert(list, vb, i); + } + break; + case SVG_Coordinates_datatype: + { + SVG_Coordinates *coords; + GF_List *l = (GF_List *)gf_list_get(list, i); + u32 j, count2; + count2 = gf_list_count(l); + for (j=0; jtype = SVG_NUMBER_VALUE; + c->value = *v; + gf_list_rem(l, j); + gf_list_insert(l, c, j); + free(v); + } + + coords = (SVG_Coordinates*)malloc(sizeof(SVG_Coordinates)); + *coords = l; + gf_list_rem(list, i); + gf_list_insert(list, coords, i); + } + break; + + case SVG_Motion_datatype: + if (coded_type==9) { + GF_Matrix2D *m = (GF_Matrix2D *)malloc(sizeof(GF_Matrix2D )); + GF_Point2D *pt = (GF_Point2D *)gf_list_get(list, i); + gf_mx2d_init(*m); + m->m[2] = pt->x; + m->m[5] = pt->y; + gf_list_rem(list, i); + gf_list_insert(list, m, i); + free(pt); + } + break; + } + } + +} + +static Bool lsr_init_smil_times(GF_LASeRCodec *lsr, SVG_Element *anim, GF_List *times, SVG_Element *parent) +{ + u32 i, count; + count = gf_list_count(times); + for (i=0; itype==GF_SMIL_TIME_EVENT) { + if (t->element_id) { + if (t->element_id[0]=='N') { + t->element = gf_sg_find_node(lsr->sg, atoi(t->element_id+1) + 1); + } else { + t->element = gf_sg_find_node_by_name(lsr->sg, t->element_id); + } + if (!t->element) return 0; + free(t->element_id); + t->element_id = NULL; + } + else if (!t->element) { + if (t->event.parameter && (t->event.type==GF_EVENT_KEYDOWN) ) { + t->element = lsr->sg->RootNode ? lsr->sg->RootNode : lsr->current_root; + } else { + t->element = (GF_Node*)parent; + } + } + } + } + return 1; +} + +static Bool lsr_setup_smil_anim(GF_LASeRCodec *lsr, SVG_Element *anim, SVG_Element *anim_parent) +{ + GF_FieldInfo info; + u32 coded_type, not_res; + GF_Node *target; + Bool is_animateMotion, is_animateTransform; + XMLRI *xlink; + SMIL_AttributeName *name = NULL; + SMIL_AnimateValue *value; + + /*setup smil events*/ + not_res = 0; + if (gf_node_get_attribute_by_tag((GF_Node *)anim, TAG_SVG_ATT_begin, 0, 0, &info)==GF_OK) { + if (!lsr_init_smil_times(lsr, anim, *(GF_List**)info.far_ptr, anim_parent)) not_res++; + } + + if (gf_node_get_attribute_by_tag((GF_Node *)anim, TAG_SVG_ATT_end, 0, 0, &info)==GF_OK) { + if (!lsr_init_smil_times(lsr, anim, *(GF_List**)info.far_ptr, anim_parent)) not_res++; + } + + + /*get xlink*/ + xlink = NULL; + if (gf_node_get_attribute_by_tag((GF_Node *)anim, TAG_XLINK_ATT_href, 0, 0, &info)==GF_OK) { + xlink = info.far_ptr; + } + + /*setup target node*/ + if (!xlink || !xlink->target) { + /*target not received*/ + if (xlink && (xlink->type == XMLRI_ELEMENTID)) return 0; + + if (!xlink) { + /*target is parent, initialize xlink (needed by anim module)*/ + if (gf_node_get_attribute_by_tag((GF_Node *)anim, TAG_XLINK_ATT_href, 1, 0, &info)==GF_OK) { + xlink = info.far_ptr; + } else { + return 0; + } + } + + xlink->type = XMLRI_ELEMENTID; + xlink->target = anim_parent; + gf_node_register_iri(lsr->sg, xlink); + target = (GF_Node *)anim_parent; + } else { + target = (GF_Node *)xlink->target; + } + if (!target || not_res) return 0; + + is_animateTransform = is_animateMotion = 0; + if (anim->sgprivate->tag==TAG_SVG_animateMotion) is_animateMotion = 1; + else if (anim->sgprivate->tag==TAG_SVG_animateTransform) { + is_animateTransform = 1; + } + if (is_animateMotion) goto translate_vals; + + /*for all except animateMotion, get attributeName*/ + if (gf_node_get_attribute_by_tag((GF_Node *)anim, TAG_SVG_ATT_attributeName, 0, 0, &info)==GF_OK) { + name = info.far_ptr; + } + if (!name) return 0; + + if (!name->field_ptr) { + if (gf_node_get_attribute_by_tag((GF_Node *)target, name->type, 1, 0, &info)!=GF_OK) return 0; + name->field_ptr = info.far_ptr; + name->type = info.fieldType; + name->tag = info.fieldIndex; + } + + + /*browse all anim types and retranslate anim values. This must be done in 2 steps since we may not have received + the target node when parsing the animation node*/ +translate_vals: + + /*and setup anim values*/ + if (gf_node_get_attribute_by_tag((GF_Node *)anim, TAG_SVG_ATT_from, 0, 0, &info)==GF_OK) { + if (is_animateTransform) { + name->type = ((SMIL_AnimateValue*)info.far_ptr)->type; + } else { + value = info.far_ptr; + coded_type = value->type; + value->type = is_animateMotion ? SVG_Motion_datatype : name->type; + lsr_translate_anim_value(value, coded_type); + } + } + if (gf_node_get_attribute_by_tag((GF_Node *)anim, TAG_SVG_ATT_by, 0, 0, &info)==GF_OK) { + if (is_animateTransform) { + name->type = ((SMIL_AnimateValue*)info.far_ptr)->type; + } else { + value = info.far_ptr; + coded_type = value->type; + value->type = is_animateMotion ? SVG_Motion_datatype : name->type; + lsr_translate_anim_value(value, coded_type); + } + } + if (gf_node_get_attribute_by_tag((GF_Node *)anim, TAG_SVG_ATT_to, 0, 0, &info)==GF_OK) { + if (is_animateTransform) { + name->type = ((SMIL_AnimateValue*)info.far_ptr)->type; + } else { + value = info.far_ptr; + coded_type = value->type; + value->type = is_animateMotion ? SVG_Motion_datatype : name->type; + lsr_translate_anim_value(value, coded_type); + } + } + if (gf_node_get_attribute_by_tag((GF_Node *)anim, TAG_SVG_ATT_values, 0, 0, &info)==GF_OK) { + if (is_animateTransform) { + name->type = ((SMIL_AnimateValues*)info.far_ptr)->type; + } else { + SMIL_AnimateValues *values = info.far_ptr; + coded_type = values->type; + values->type = is_animateMotion ? SVG_Motion_datatype : name->type; + lsr_translate_anim_values(values, coded_type); + } + } + + return 1; +} + +static void lsr_read_anim_fill(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 val; + + GF_LSR_READ_INT(lsr, val, 1, "has_smil_fill"); + if (val) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_smil_fill, 1, 0, &info); + /*enumeration freeze{0} remove{1}*/ + GF_LSR_READ_INT(lsr, val, 1, "smil_fill"); + *(SMIL_Fill*)info.far_ptr = val ? SMIL_FILL_REMOVE : SMIL_FILL_FREEZE; + } +} +static void lsr_read_anim_repeatCount(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 val; + GF_LSR_READ_INT(lsr, val, 1, "has_repeatCount"); + if (val) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_repeatCount, 1, 0, &info); + GF_LSR_READ_INT(lsr, val, 1, "repeatCount"); + if (val) ((SMIL_RepeatCount*)info.far_ptr)->type = SMIL_REPEATCOUNT_INDEFINITE; + else { + ((SMIL_RepeatCount*)info.far_ptr)->type = SMIL_REPEATCOUNT_DEFINED; + ((SMIL_RepeatCount*)info.far_ptr)->count = lsr_read_fixed_16_8(lsr, "repeatCount"); + } + } +} +static void lsr_read_repeat_duration(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "has_repeatDur"); + if (flag) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_repeatDur, 1, 0, &info); + GF_LSR_READ_INT(lsr, flag, 1, "choice"); + + if (flag) { + ((SMIL_Duration *)info.far_ptr)->type = SMIL_DURATION_INDEFINITE; + } else { + ((SMIL_Duration *)info.far_ptr)->clock_value = (Double) lsr_read_vluimsbf5(lsr, "value"); + ((SMIL_Duration *)info.far_ptr)->clock_value /= lsr->time_resolution; + ((SMIL_Duration *)info.far_ptr)->type = SMIL_DURATION_DEFINED; + } + } +} +static void lsr_read_anim_restart(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 val; + GF_LSR_READ_INT(lsr, val, 1, "has_restart"); + if (val) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_restart, 1, 0, &info); + /*enumeration always{0} never{1} whenNotActive{2}*/ + GF_LSR_READ_INT(lsr, *(SMIL_Restart*)info.far_ptr, 2, "restart"); + } +} + +static void *lsr_read_an_anim_value(GF_LASeRCodec *lsr, u32 coded_type, const char *name) +{ + u32 flag; + u32 escapeFlag, escape_val = 0; + u8 *enum_val; + u32 *id_val; + char *string; + SVG_Number *num; + XMLRI *iri; + SVG_Point *pt; + SVG_Paint *paint; + + GF_LSR_READ_INT(lsr, escapeFlag, 1, "escapeFlag"); + if (escapeFlag) GF_LSR_READ_INT(lsr, escape_val, 2, "escapeEnum"); + + switch (coded_type) { + case 0: + string = NULL; + lsr_read_byte_align_string(lsr, &string, name); + return string; + case 1: + num = (SVG_Number*)malloc(sizeof(SVG_Number)); + if (escapeFlag) { + num->type = escape_val; + } else { + num->type = SVG_NUMBER_VALUE; + num->value = lsr_read_fixed_16_8(lsr, name); + } + return num; + case 2: + { + SVG_PathData *pd = (SVG_PathData *)gf_svg_create_attribute_value(SVG_PathData_datatype); + lsr_read_path_type(lsr, NULL, 0, pd, name); + return pd; + } + case 3: + { + SVG_Points *pts = (SVG_Points *)gf_svg_create_attribute_value(SVG_Points_datatype); + lsr_read_point_sequence(lsr, *pts, name); + return pts; + } + case 4: + num = (SVG_Number*)malloc(sizeof(SVG_Number)); + if (escapeFlag) { + num->type = escape_val; + } else { + num->type = SVG_NUMBER_VALUE; + num->value = lsr_read_fixed_clamp(lsr, name); + } + return num; + case 5: + GF_SAFEALLOC(paint, SVG_Paint); + if (escapeFlag) { + paint->type = SVG_PAINT_INHERIT; + } else { + lsr_read_paint(lsr, paint, name); + } + return paint; + case 6: + enum_val = (u8*)malloc(sizeof(u8)); + *enum_val = lsr_read_vluimsbf5(lsr, name); + return enum_val; + /*TODO check this is correct*/ + case 7: + { + GF_List *l = gf_list_new(); + u32 i, count; + count = lsr_read_vluimsbf5(lsr, "count"); + for (i=0; icoord_bits, "valX"); + pt->x = lsr_translate_coords(lsr, flag, lsr->coord_bits); + GF_LSR_READ_INT(lsr, flag, lsr->coord_bits, "valY"); + pt->y = lsr_translate_coords(lsr, flag, lsr->coord_bits); + return pt; + case 10: + id_val = (u32*)malloc(sizeof(u32)); + *id_val = lsr_read_vluimsbf5(lsr, name); + return id_val; + case 11: + { + SVG_FontFamily *ft; + u32 idx; + GF_SAFEALLOC(ft, SVG_FontFamily); + if (escapeFlag) { + ft->type = SVG_FONTFAMILY_INHERIT; + } else { + idx = lsr_read_vluimsbf5(lsr, name); + ft->type = SVG_FONTFAMILY_VALUE; + ft->value = (char*)gf_list_get(lsr->font_table, idx); + if (ft->value) ft->value = strdup(ft->value); + } + return ft; + } + case 12: + GF_SAFEALLOC(iri, XMLRI); + lsr_read_any_uri(lsr, iri, name); + return iri; + default: + lsr_read_extension(lsr, name); + break; + } + return NULL; +} + +static void lsr_translate_anim_trans_value(SMIL_AnimateValue *val, u32 transform_type) +{ + SVG_Point_Angle *p; + Fixed *f; + u32 coded_type = val->type; + + switch(transform_type) { + case SVG_TRANSFORM_TRANSLATE: + val->type = SVG_Transform_Translate_datatype; + break; + case SVG_TRANSFORM_SCALE: + val->type = SVG_Transform_Scale_datatype; + break; + case SVG_TRANSFORM_ROTATE: + val->type = SVG_Transform_Rotate_datatype; + break; + case SVG_TRANSFORM_SKEWX: + val->type = SVG_Transform_SkewX_datatype; + break; + case SVG_TRANSFORM_SKEWY: + val->type = SVG_Transform_SkewY_datatype; + break; + case SVG_TRANSFORM_MATRIX: + val->type = SVG_Transform_datatype; + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[LSR Parsing] unknown datatype for animate transform.\n")); + return; + } + if (!val->value) return; + switch (transform_type) { + case SVG_TRANSFORM_ROTATE: + p = (SVG_Point_Angle*)malloc(sizeof(SVG_Point_Angle)); + p->x = p->y = 0; + if (coded_type==8) { + GF_List *l = (GF_List *)val->value; + f = (Fixed*)gf_list_get(l, 0); + if (f) { p->angle = *f; free(f); } + f = (Fixed*)gf_list_get(l, 1); + if (f) { p->x = *f; free(f); } + f = (Fixed*)gf_list_get(l, 2); + if (f) { p->y = *f; free(f); } + gf_list_del(l); + } else { + p->angle = ((SVG_Number *)val->value)->value; + free(val->value); + } + p->angle = gf_muldiv(p->angle, GF_PI, INT2FIX(180) ); + val->value = p; + break; + case SVG_TRANSFORM_SCALE: + if (coded_type==8) { + SVG_Point *pt; + GF_List *l = (GF_List *)val->value; + GF_SAFEALLOC(pt , SVG_Point); + f = (Fixed*)gf_list_get(l, 0); + if (f) { pt->x = *f; free(f); } + f = (Fixed*)gf_list_get(l, 1); + if (f) { pt->y = *f; free(f); } + else pt->y = pt->x; + gf_list_del(l); + val->value = pt; + } + break; + case SVG_TRANSFORM_SKEWX: + case SVG_TRANSFORM_SKEWY: + f = (Fixed*)malloc(sizeof(Fixed)); + *f = ((SVG_Number *)val->value)->value; + free(val->value); + val->value = f; + break; + } +} + +static void lsr_translate_anim_trans_values(SMIL_AnimateValues *val, u32 transform_type) +{ + u32 count, i, coded_type; + SVG_Point_Angle *p; + SVG_Point *pt; + Fixed *f; + GF_List *l; + + coded_type = val->type; + switch(transform_type) { + case SVG_TRANSFORM_TRANSLATE: + val->type = SVG_Transform_Translate_datatype; + break; + case SVG_TRANSFORM_SCALE: + val->type = SVG_Transform_Scale_datatype; + break; + case SVG_TRANSFORM_ROTATE: + val->type = SVG_Transform_Rotate_datatype; + break; + case SVG_TRANSFORM_SKEWX: + val->type = SVG_Transform_SkewX_datatype; + break; + case SVG_TRANSFORM_SKEWY: + val->type = SVG_Transform_SkewY_datatype; + break; + case SVG_TRANSFORM_MATRIX: + val->type = SVG_Transform_datatype; + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] unknown datatype for animate transform.\n")); + return; + } + count = gf_list_count(val->values); + if (!count) return; + + if (transform_type==SVG_TRANSFORM_TRANSLATE) + return; + + for (i=0;ivalues, i); + switch (transform_type) { + case SVG_TRANSFORM_ROTATE: + GF_SAFEALLOC(p, SVG_Point_Angle); + + if (coded_type==8) { + l = (GF_List*)a_val; + f = (Fixed*)gf_list_get(l, 0); + p->angle = *f; + f = (Fixed*)gf_list_get(l, 1); + if (f) p->x = *f; + f = (Fixed*)gf_list_get(l, 2); + if (f) p->y = *f; + while (gf_list_count(l)) { + f = (Fixed*)gf_list_last(l); + gf_list_rem_last(l); + free(f); + } + gf_list_del(l); + } else if (coded_type==1) { + p->angle = ((SVG_Number *)a_val)->value; + free(a_val); + } + p->angle = gf_muldiv(p->angle, GF_PI, INT2FIX(180) ); + gf_list_rem(val->values, i); + gf_list_insert(val->values, p, i); + break; + case SVG_TRANSFORM_SKEWX: + case SVG_TRANSFORM_SKEWY: + f = (Fixed*)malloc(sizeof(Fixed)); + *f = ((SVG_Number *)a_val)->value; + free(a_val); + gf_list_rem(val->values, i); + gf_list_insert(val->values, f, i); + break; + case SVG_TRANSFORM_SCALE: + pt = (SVG_Point*)malloc(sizeof(SVG_Point)); + l = (GF_List*)a_val; + f = (Fixed*)gf_list_get(l, 0); + if (f) pt->x = *f; + f = (Fixed*)gf_list_get(l, 1); + if (f) pt->y = *f; + else pt->y = pt->x; + while (gf_list_count(l)) { + f = (Fixed*)gf_list_last(l); + gf_list_rem_last(l); + free(f); + } + gf_list_del(l); + gf_list_rem(val->values, i); + gf_list_insert(val->values, pt, i); + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[LASeR] unknown transform type %d\n", transform_type)); + break; + } + } +} + +static void lsr_read_anim_value_ex(GF_LASeRCodec *lsr, GF_Node *n, u32 tag, const char *name, u32 *tr_type) +{ + u32 val, coded_type; + GF_LSR_READ_INT(lsr, val, 1, name); + if (val) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, tag, 1, 0, &info); + + GF_LSR_READ_INT(lsr, coded_type, 4, "type"); + ((SMIL_AnimateValue*)info.far_ptr)->value = lsr_read_an_anim_value(lsr, coded_type, name); + ((SMIL_AnimateValue*)info.far_ptr)->type = coded_type; + + if (tr_type) { + lsr_translate_anim_trans_value(info.far_ptr, *tr_type); + } + } +} + +static void lsr_read_anim_values_ex(GF_LASeRCodec *lsr, GF_Node *n, u32 *tr_type) +{ + u32 flag, i, count = 0; + u32 coded_type; + GF_FieldInfo info; + SMIL_AnimateValues *values; + + GF_LSR_READ_INT(lsr, flag, 1, "values"); + if (!flag) return; + + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_values, 1, 0, &info); + values = (SMIL_AnimateValues *)info.far_ptr; + + GF_LSR_READ_INT(lsr, coded_type, 4, "type"); + values->type = coded_type; + count = lsr_read_vluimsbf5(lsr, "count"); + for (i=0; ivalues, att); + } + if (tr_type) { + lsr_translate_anim_trans_values(info.far_ptr, *tr_type); + } +} +#define lsr_read_anim_value(_a, _b, _c, _d) lsr_read_anim_value_ex(_a, _b, _c, _d, NULL) +#define lsr_read_anim_values(_a, _b) lsr_read_anim_values_ex(_a, _b, NULL) + +static Fixed *lsr_read_fraction_12_item(GF_LASeRCodec *lsr) +{ + u32 flag; + Fixed *f; + GF_SAFEALLOC(f, Fixed); + GF_LSR_READ_INT(lsr, flag, 1, "hasShort"); + if (flag) { + GF_LSR_READ_INT(lsr, flag, 1, "isZero"); + if (flag) *f = 0; + else *f = FIX_ONE; + } else { + u32 v; + GF_LSR_READ_INT(lsr, v, 12, "val"); + *f = INT2FIX(v) / 4096/*(1<<12)*/; + } + return f; +} + +static void lsr_read_fraction_12(GF_LASeRCodec *lsr, GF_Node *elt, u32 tag, const char *name) +{ + GF_FieldInfo info; + u32 i, count; + GF_LSR_READ_INT(lsr, count, 1, name); + if (!count) return; + + lsr->last_error = gf_node_get_attribute_by_tag(elt, tag, 1, 0, &info); + + count = lsr_read_vluimsbf5(lsr, "name"); + for (i=0; ilast_error = gf_node_get_attribute_by_tag(n, tag, 1, 0, &info); + coords = (SVG_Coordinates*)info.far_ptr; + } else { + while (gf_list_count(*coords)) { + Fixed *v = (Fixed *)gf_list_last(*coords); + gf_list_rem_last(*coords); + free(v); + } + } + count = lsr_read_vluimsbf5(lsr, "count"); + for (i=0;ix = lsr_translate_coords(lsr, v, nb_bits); + GF_LSR_READ_INT(lsr, v, nb_bits, "y"); + pt->y = lsr_translate_coords(lsr, v, nb_bits); + } + } else { + u32 nb_dx, nb_dy, k; + Fixed x, y; + SVG_Point *pt = (SVG_Point *)malloc(sizeof(SVG_Point)); + gf_list_add(pts, pt); + + GF_LSR_READ_INT(lsr, nb_dx, 5, "bits"); + GF_LSR_READ_INT(lsr, k, nb_dx, "x"); + x = pt->x = lsr_translate_coords(lsr, k, nb_dx); + GF_LSR_READ_INT(lsr, k, nb_dx, "y"); + y = pt->y = lsr_translate_coords(lsr, k, nb_dx); + + GF_LSR_READ_INT(lsr, nb_dx, 5, "bitsx"); + GF_LSR_READ_INT(lsr, nb_dy, 5, "bitsy"); + for (i=1; ix = x + lsr_translate_coords(lsr, k, nb_dx); + x = pt->x; + GF_LSR_READ_INT(lsr, k, nb_dy, "dy"); + pt->y = y + lsr_translate_coords(lsr, k, nb_dy); + y = pt->y; + } + } + } +} +static void lsr_read_path_type(GF_LASeRCodec *lsr, GF_Node *n, u32 tag, SVG_PathData *path, const char *name) +{ +#if USE_GF_PATH + GF_Point2D *pt, *ct1, *ct2, *end; + GF_Point2D orig, ct_orig; + u32 i, count, cur_pt, type; + GF_List *pts = gf_list_new(); + lsr_read_point_sequence(lsr, pts, "seq"); + + if (!path) { + GF_FieldInfo info; + gf_node_get_attribute_by_tag(n, tag, 1, 0, &info); + path = info.far_ptr; + } else { + gf_path_reset(path); + } + /*first MoveTo is skipped in LASeR*/ + pt = gf_list_get(pts, 0); + if (!pt) { + gf_list_del(pts); + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] Empty path found.\n")); + return; + } + ct_orig = orig = *pt; + + gf_path_add_move_to_vec(path, pt); + cur_pt = 1; + count = lsr_read_vluimsbf5(lsr, "nbOfTypes"); + for (i=0; ilast_error = GF_NON_COMPLIANT_BITSTREAM; + +exit: + while (gf_list_count(pts)) { + end = gf_list_get(pts, 0); + gf_list_rem(pts, 0); + free(end); + } + gf_list_del(pts); + +#else + u32 i, count, c; + u8 *type; + lsr_read_point_sequence(lsr, path->points, "seq"); + while (gf_list_count(path->commands)) { + u8 *v = (u8 *)gf_list_last(path->commands); + gf_list_rem_last(path->commands); + free(v); + } + + count = lsr_read_vluimsbf5(lsr, "nbOfTypes"); + for (i=0; icommands, type); + } +#endif +} + +static void lsr_read_rotate_type(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "rotate"); + if (flag) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_rotate, 1, 0, &info); + GF_LSR_READ_INT(lsr, flag, 1, "choice"); + + if (flag) { + GF_LSR_READ_INT(lsr, flag, 1, "rotate"); + ((SVG_Number *)info.far_ptr)->type = flag ? SVG_NUMBER_AUTO_REVERSE : SVG_NUMBER_AUTO; + } else { + ((SVG_Number *)info.far_ptr)->value = lsr_read_fixed_16_8(lsr, "rotate"); + ((SVG_Number *)info.far_ptr)->type = SVG_NUMBER_VALUE; + } + } +} +static void lsr_read_sync_behavior(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "syncBehavior"); + if (flag) { + GF_FieldInfo info; + GF_LSR_READ_INT(lsr, flag, 2, "syncBehavior"); + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_syncBehavior, 1, 0, &info); + *(SMIL_SyncBehavior*)info.far_ptr = flag + 1; + } +} +static void lsr_read_sync_tolerance(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "syncTolerance"); + if (flag) { + GF_FieldInfo info; + GF_LSR_READ_INT(lsr, flag, 1, "syncTolerance"); + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_syncTolerance, 1, 0, &info); + if (flag) ((SMIL_SyncTolerance *)info.far_ptr)->type = SMIL_SYNCTOLERANCE_DEFAULT; + else { + u32 v = lsr_read_vluimsbf5(lsr, "value"); + ((SMIL_SyncTolerance *)info.far_ptr)->value = INT2FIX(v); + ((SMIL_SyncTolerance *)info.far_ptr)->value /= lsr->time_resolution; + } + } +} +static void lsr_read_sync_reference(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "syncReference"); + if (flag) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_syncReference, 1, 0, &info); + lsr_read_any_uri(lsr, info.far_ptr, "syncReference"); + } +} + +static void lsr_read_coordinate(GF_LASeRCodec *lsr, SVG_Number *coord, Bool skipable, const char *name) +{ + u32 flag; + if (skipable) { + GF_LSR_READ_INT(lsr, flag, 1, name); + if (!flag) { + //coord->type = SVG_NUMBER_UNKNOWN; + //coord->value = 0; + return; + } + } + coord->type = SVG_NUMBER_VALUE; + GF_LSR_READ_INT(lsr, flag, lsr->coord_bits, name); + coord->value = lsr_translate_coords(lsr, flag, lsr->coord_bits); +} +static void lsr_read_coordinate_ptr(GF_LASeRCodec *lsr, GF_Node *n, u32 tag, Bool skipable, const char *name) +{ + u32 flag; + GF_FieldInfo info; + if (skipable) { + GF_LSR_READ_INT(lsr, flag, 1, name); + if (!flag) return; + } + lsr->last_error = gf_node_get_attribute_by_tag(n, tag, 1, 0, &info); + + ((SVG_Number*)info.far_ptr)->type = SVG_NUMBER_VALUE; + GF_LSR_READ_INT(lsr, flag, lsr->coord_bits, name); + ((SVG_Number*)info.far_ptr)->value = lsr_translate_coords(lsr, flag, lsr->coord_bits); +} + +static void lsr_read_coord_list(GF_LASeRCodec *lsr, GF_Node *elt, u32 tag, const char *name) +{ + GF_FieldInfo info; + u32 i, count; + GF_LSR_READ_INT(lsr, count, 1, name); + if (!count) return; + count = lsr_read_vluimsbf5(lsr, "nb_coords"); + if (!count) return; + if (count>1000000) { + lsr->last_error = GF_NON_COMPLIANT_BITSTREAM; + return; + } + + lsr->last_error = gf_node_get_attribute_by_tag(elt, tag, 1, 0, &info); + + for (i=0; icoord_bits, name); + f->value = lsr_translate_coords(lsr, res, lsr->coord_bits); + gf_list_add(*(SVG_Coordinates*)info.far_ptr, f); + } +} + +static void lsr_read_transform_behavior(GF_LASeRCodec *lsr, GF_Node *n) +{ + GF_FieldInfo info; + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "transformBehavior"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_transformBehavior, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SVG_TransformBehavior*)info.far_ptr, 4, "transformBehavior"); + } +} + +static void lsr_read_content_type(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "hasType"); + if (flag) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_type, 1, 0, &info); + lsr_read_byte_align_string(lsr, info.far_ptr, "type"); + } +} +static void lsr_read_script_type(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "hasType"); + if (flag) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_XLINK_ATT_type, 1, 0, &info); + GF_LSR_READ_INT(lsr, flag, 1, "choice"); + if (flag) { + GF_LSR_READ_INT(lsr, flag, 1, "script"); + switch (flag) { + case 0: *(SVG_String*)info.far_ptr = strdup("application/ecmascript"); break; + case 1: *(SVG_String*)info.far_ptr = strdup("application/jar-archive"); break; + default: break; + } + } else { + lsr_read_byte_align_string(lsr, info.far_ptr, "type"); + } + } +} +static void lsr_read_value_with_units(GF_LASeRCodec *lsr, SVG_Number *n, const char *name) +{ + s32 val; + GF_LSR_READ_INT(lsr, val, 32, name); +#ifdef GPAC_FIXED_POINT + n->value = val << 8; +#else + n->value = INT2FIX(val) / (1<<8); +#endif + GF_LSR_READ_INT(lsr, val, 3, "units"); + switch (val) { + case 1: n->type = SVG_NUMBER_IN; break; + case 2: n->type = SVG_NUMBER_CM; break; + case 3: n->type = SVG_NUMBER_MM; break; + case 4: n->type = SVG_NUMBER_PT; break; + case 5: n->type = SVG_NUMBER_PC; break; + case 6: n->type = SVG_NUMBER_PERCENTAGE; break; + default: n->type = SVG_NUMBER_VALUE; break; + } +} + + +static void lsr_read_clip_time(GF_LASeRCodec *lsr, GF_Node *elt, u32 tag, const char *name) +{ + GF_FieldInfo info; + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, name); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, tag, 1, 0, &info); + GF_LSR_READ_INT(lsr, flag, 1, "isEnum"); + if (!flag) { + GF_LSR_READ_INT(lsr, flag, 1, "sign"); + flag = lsr_read_vluimsbf5(lsr, "val"); + *((SVG_Clock *)info.far_ptr) = flag; + *((SVG_Clock *)info.far_ptr) /= lsr->time_resolution; + } + } +} + +static void lsr_read_attribute_type(GF_LASeRCodec *lsr, GF_Node *elt) +{ + GF_FieldInfo info; + u32 flag; + GF_LSR_READ_INT(lsr, flag, 1, "hasAttributeType"); + if (!flag) return; + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_attributeType, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SMIL_AttributeType*)info.far_ptr, 2, "attributeType"); +} + +static void lsr_read_preserve_aspect_ratio(GF_LASeRCodec *lsr, GF_Node *n) +{ + GF_FieldInfo info; + u32 flag; + SVG_PreserveAspectRatio *par; + + GF_LSR_READ_INT(lsr, flag, 1, "hasPreserveAspectRatio"); + if (!flag) return; + + lsr->last_error = gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_preserveAspectRatio, 1, 0, &info); + par = (SVG_PreserveAspectRatio *)info.far_ptr; + + GF_LSR_READ_INT(lsr, flag, 1, "choice (meetOrSlice)"); + GF_LSR_READ_INT(lsr, par->defer, 1, "choice (defer)"); + GF_LSR_READ_INT(lsr, flag, 4, "alignXandY"); + switch (flag) { + case 1: par->align = SVG_PRESERVEASPECTRATIO_XMAXYMAX; break; + case 2: par->align = SVG_PRESERVEASPECTRATIO_XMAXYMID; break; + case 3: par->align = SVG_PRESERVEASPECTRATIO_XMAXYMIN; break; + case 4: par->align = SVG_PRESERVEASPECTRATIO_XMIDYMAX; break; + case 5: par->align = SVG_PRESERVEASPECTRATIO_XMIDYMID; break; + case 6: par->align = SVG_PRESERVEASPECTRATIO_XMIDYMIN; break; + case 7: par->align = SVG_PRESERVEASPECTRATIO_XMINYMAX; break; + case 8: par->align = SVG_PRESERVEASPECTRATIO_XMINYMID; break; + case 9: par->align = SVG_PRESERVEASPECTRATIO_XMINYMIN; break; + default: par->align = SVG_PRESERVEASPECTRATIO_NONE; break; + } +} + +static void lsr_read_eRR(GF_LASeRCodec *lsr, GF_Node *elt) +{ + u32 err; + GF_LSR_READ_INT(lsr, err, 1, "externalResourcesRequired"); + if (err) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_externalResourcesRequired, 1, 0, &info); + *(SVG_Boolean*)info.far_ptr = 1; + } +} + +static void lsr_read_lsr_enabled(GF_LASeRCodec *lsr, GF_Node *elt) +{ + u32 err; + GF_LSR_READ_INT(lsr, err, 1, "enabled"); + if (err) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_LSR_ATT_enabled, 1, 0, &info); + *(SVG_Boolean*)info.far_ptr = 1; + } +} + +static GF_Node *lsr_read_a(GF_LASeRCodec *lsr) +{ + Bool flag; + GF_Node *elt = (GF_Node*) gf_node_new(lsr->sg, TAG_SVG_a); + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_eRR(lsr, elt); + GF_LSR_READ_INT(lsr, flag, 1, "hasTarget"); + if (flag) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_target, 1, 0, &info); + lsr_read_byte_align_string(lsr, info.far_ptr, "target"); + } + lsr_read_href(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} + + + +static GF_Node *lsr_read_animate(GF_LASeRCodec *lsr, SVG_Element *parent, Bool is_animateColor) +{ + GF_Node *elt = gf_node_new(lsr->sg, is_animateColor ? TAG_SVG_animateColor : TAG_SVG_animate); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_attribute_name(lsr, elt); + + lsr_read_accumulate(lsr, elt); + lsr_read_additive(lsr, elt); + lsr_read_anim_value(lsr, elt, TAG_SVG_ATT_by, "by"); + lsr_read_calc_mode(lsr, elt); + lsr_read_anim_value(lsr, elt, TAG_SVG_ATT_from, "from"); + lsr_read_fraction_12(lsr, elt, TAG_SVG_ATT_keySplines, "keySplines"); + lsr_read_fraction_12(lsr, elt, TAG_SVG_ATT_keyTimes, "keyTimes"); + lsr_read_anim_values(lsr, elt); + lsr_read_attribute_type(lsr, elt); + lsr_read_smil_times(lsr, elt, TAG_SVG_ATT_begin, NULL, "begin", 1); + lsr_read_duration(lsr, elt); + lsr_read_anim_fill(lsr, elt); + lsr_read_anim_repeatCount(lsr, elt); + lsr_read_repeat_duration(lsr, elt); + lsr_read_anim_restart(lsr, elt); + lsr_read_anim_value(lsr, elt, TAG_SVG_ATT_to, "to"); + lsr_read_href(lsr, elt); + lsr_read_lsr_enabled(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + + if (!lsr_setup_smil_anim(lsr, (SVG_Element*)elt, parent)) { + gf_list_add(lsr->defered_anims, elt); + lsr_read_group_content_post_init(lsr, (SVG_Element*)elt, 1); + } else { + lsr_read_group_content(lsr, elt, 0); + } + return elt; +} + + +static GF_Node *lsr_read_animateMotion(GF_LASeRCodec *lsr, SVG_Element *parent) +{ + Bool flag; + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_animateMotion); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_accumulate(lsr, elt); + lsr_read_additive(lsr, elt); + lsr_read_anim_value(lsr, elt, TAG_SVG_ATT_by, "by"); + lsr_read_calc_mode(lsr, elt); + lsr_read_anim_value(lsr, elt, TAG_SVG_ATT_from, "from"); + lsr_read_fraction_12(lsr, elt, TAG_SVG_ATT_keySplines, "keySplines"); + lsr_read_fraction_12(lsr, elt, TAG_SVG_ATT_keyTimes, "keyTimes"); + lsr_read_anim_values(lsr, elt); + lsr_read_attribute_type(lsr, elt); + lsr_read_smil_times(lsr, elt, TAG_SVG_ATT_begin, NULL, "begin", 1); + lsr_read_duration(lsr, elt); + lsr_read_anim_fill(lsr, elt); + lsr_read_anim_repeatCount(lsr, elt); + lsr_read_repeat_duration(lsr, elt); + lsr_read_anim_restart(lsr, elt); + lsr_read_anim_value(lsr, elt, TAG_SVG_ATT_to, "to"); + lsr_read_float_list(lsr, elt, TAG_SVG_ATT_keyPoints, NULL, "keyPoints"); + GF_LSR_READ_INT(lsr, flag, 1, "hasPath"); + if (flag) lsr_read_path_type(lsr, elt, TAG_SVG_ATT_path, NULL, "path"); + + lsr_read_rotate_type(lsr, elt); + lsr_read_href(lsr, elt); + lsr_read_lsr_enabled(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + + if (!lsr_setup_smil_anim(lsr, (SVG_Element*)elt, parent)) { + gf_list_add(lsr->defered_anims, elt); + lsr_read_group_content_post_init(lsr, (SVG_Element*)elt, 1); + } else { + lsr_read_group_content_post_init(lsr, (SVG_Element*)elt, 0); + } + return elt; +} + + +static GF_Node *lsr_read_animateTransform(GF_LASeRCodec *lsr, SVG_Element *parent) +{ + u32 type; + Bool flag; + GF_FieldInfo info; + GF_Node *elt= gf_node_new(lsr->sg, TAG_SVG_animateTransform); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_attribute_name(lsr, elt); + + /*enumeration rotate{0} scale{1} skewX{2} skewY{3} translate{4}*/ + GF_LSR_READ_INT(lsr, flag, 3, "rotscatra"); + switch (flag) { + case 0: type = SVG_TRANSFORM_ROTATE; break; + case 1: type = SVG_TRANSFORM_SCALE; break; + case 2: type = SVG_TRANSFORM_SKEWX; break; + case 3: type = SVG_TRANSFORM_SKEWY; break; + case 4: type = SVG_TRANSFORM_TRANSLATE; break; + } + if (gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_transform_type, 1, 0, &info)==GF_OK) { + *(SVG_TransformType *)info.far_ptr = type; + } + + lsr_read_accumulate(lsr, elt); + lsr_read_additive(lsr, elt); + lsr_read_anim_value_ex(lsr, elt, TAG_SVG_ATT_by, "by", &type); + lsr_read_calc_mode(lsr, elt); + lsr_read_anim_value_ex(lsr, elt, TAG_SVG_ATT_from, "from", &type); + lsr_read_fraction_12(lsr, elt, TAG_SVG_ATT_keySplines, "keySplines"); + lsr_read_fraction_12(lsr, elt, TAG_SVG_ATT_keyTimes, "keyTimes"); + lsr_read_anim_values_ex(lsr, elt, &type); + lsr_read_attribute_type(lsr, elt); + + lsr_read_smil_times(lsr, elt, TAG_SVG_ATT_begin, NULL, "begin", 1); + lsr_read_duration(lsr, elt); + lsr_read_anim_fill(lsr, elt); + lsr_read_anim_repeatCount(lsr, elt); + lsr_read_repeat_duration(lsr, elt); + lsr_read_anim_restart(lsr, elt); + lsr_read_anim_value_ex(lsr, elt, TAG_SVG_ATT_to, "to", &type); + + lsr_read_href(lsr, elt); + lsr_read_lsr_enabled(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + + if (!lsr_setup_smil_anim(lsr, (SVG_Element*)elt, parent)) { + gf_list_add(lsr->defered_anims, elt); + lsr_read_group_content_post_init(lsr, (SVG_Element*)elt, 1); + } else { + lsr_read_group_content(lsr, elt, 0); + } + return elt; +} + +static GF_Node *lsr_read_audio(GF_LASeRCodec *lsr, SVG_Element *parent) +{ + GF_Node *elt= gf_node_new(lsr->sg, TAG_SVG_audio); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_smil_times(lsr, elt, TAG_SVG_ATT_begin, NULL, "begin", 1); + lsr_read_duration(lsr, elt); + lsr_read_eRR(lsr, elt); + lsr_read_anim_repeatCount(lsr, elt); + lsr_read_repeat_duration(lsr, elt); + lsr_read_anim_restart(lsr, elt); + lsr_read_sync_behavior(lsr, elt); + lsr_read_sync_tolerance(lsr, elt); + lsr_read_content_type(lsr, elt); + lsr_read_href(lsr, elt); + + lsr_read_clip_time(lsr, elt, TAG_SVG_ATT_clipBegin, "clipBegin"); + lsr_read_clip_time(lsr, elt, TAG_SVG_ATT_clipEnd, "clipEnd"); + lsr_read_sync_reference(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} +static GF_Node *lsr_read_circle(GF_LASeRCodec *lsr) +{ + GF_Node *elt= gf_node_new(lsr->sg, TAG_SVG_circle); + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_cx, 1, "cx"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_cy, 1, "cy"); + lsr_read_coordinate_ptr(lsr, elt,TAG_SVG_ATT_r, 0, "r"); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} +static GF_Node *lsr_read_conditional(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_LSR_conditional); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_smil_times(lsr, elt, TAG_SVG_ATT_begin, NULL, "begin", 1); + lsr_read_eRR(lsr, elt); + lsr_read_lsr_enabled(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_command_list(lsr, NULL, (SVG_Element*)elt, 0); + + gf_node_init(elt); + return elt; +} +static GF_Node *lsr_read_cursorManager(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_LSR_cursorManager); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt,TAG_SVG_ATT_x, 1, "x"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y, 1, "y"); + lsr_read_href(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} + +static GF_Node *lsr_read_data(GF_LASeRCodec *lsr, u32 node_tag) +{ + GF_Node *elt = gf_node_new(lsr->sg, node_tag); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} + +static GF_Node *lsr_read_defs(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_defs); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} +static GF_Node *lsr_read_ellipse(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_ellipse); + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_cx, 1, "cx"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_cy, 1, "cy"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_rx, 0, "rx"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_ry, 0, "ry"); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} +static GF_Node *lsr_read_foreignObject(GF_LASeRCodec *lsr) +{ + u32 flag; + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_foreignObject); + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_eRR(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_height, 0, "height"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_width, 0, "width"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_x, 1, "x"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y, 1, "y"); + + lsr_read_any_attribute(lsr, elt, 1); +/* TODO + bit(1) opt_group; + if(opt_group) { + vluimsbf5 occ1; + for(int t=0;tsg, TAG_SVG_g); + if (is_same) { + if (lsr->prev_g) { + lsr_restore_base(lsr, (SVG_Element*) elt, lsr->prev_g, 0, 0); + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] sameg coded in bitstream but no g defined !\n")); + } + lsr_read_id(lsr, elt); + } else { + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_eRR(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr->prev_g = (SVG_Element*)elt; + } + lsr_read_group_content(lsr, elt, is_same); + return elt; +} + +static void lsr_read_opacity(GF_LASeRCodec *lsr, GF_Node *elt) +{ + u32 flag; + GF_FieldInfo info; + GF_LSR_READ_INT(lsr, flag, 1, "opacity"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_opacity, 1, 0, &info); + ((SVG_Number*)info.far_ptr)->type = SVG_NUMBER_VALUE; + ((SVG_Number*)info.far_ptr)->value = lsr_read_fixed_clamp(lsr, "opacity"); + } + +} +static GF_Node *lsr_read_image(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_image); + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_eRR(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_height, 1, "height"); + lsr_read_opacity(lsr, elt); + + lsr_read_preserve_aspect_ratio(lsr, elt); + lsr_read_content_type(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_width, 1, "width"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_x, 1, "x"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y, 1, "y"); + lsr_read_href(lsr, elt); + lsr_read_transform_behavior(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} +static GF_Node *lsr_read_line(GF_LASeRCodec *lsr, Bool is_same) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_line); + + if (is_same) { + if (lsr->prev_line) { + lsr_restore_base(lsr, (SVG_Element*) elt, (SVG_Element *)lsr->prev_line, 0, 0); + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] sameline coded in bitstream but no line defined !\n")); + } + lsr_read_id(lsr, elt); + } else { + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + } + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_x1, 1, "x1"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_x2, 0, "x2"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y1, 1, "y1"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y2, 0, "y2"); + if (!is_same) { + lsr_read_any_attribute(lsr, elt, 1); + lsr->prev_line = (SVG_Element*)elt; + } + lsr_read_group_content(lsr, elt, is_same); + return elt; +} + +static void lsr_read_gradient_units(GF_LASeRCodec *lsr, GF_Node *elt) +{ + u32 flag; + GF_FieldInfo info; + GF_LSR_READ_INT(lsr, flag, 1, "hasGradientUnits"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_gradientUnits, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SVG_GradientUnit*)info.far_ptr, 1, "gradientUnits"); + } +} +static GF_Node *lsr_read_linearGradient(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_linearGradient); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_gradient_units(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_x1, 1, "x1"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_x2, 1, "x2"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y1, 1, "y1"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y2, 1, "y2"); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} +static GF_Node *lsr_read_mpath(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_mpath); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_href(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} +static GF_Node *lsr_read_path(GF_LASeRCodec *lsr, u32 same_type) +{ + u32 flag; + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_path); + + if (same_type) { + if (lsr->prev_path) { + lsr_restore_base(lsr, (SVG_Element*)elt, (SVG_Element *)lsr->prev_path, (same_type==2) ? 1 : 0, 0); + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] samepath coded in bitstream but no path defined !\n")); + } + lsr_read_id(lsr, elt); + if (same_type==2) lsr_read_fill(lsr, elt); + lsr_read_path_type(lsr, elt, TAG_SVG_ATT_d, NULL, "d"); + } else { + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_path_type(lsr, elt, TAG_SVG_ATT_d, NULL, "d"); + GF_LSR_READ_INT(lsr, flag, 1, "hasPathLength"); + if (flag) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_pathLength, 1, 0, &info); + ((SVG_Number*)info.far_ptr)->value = lsr_read_fixed_16_8(lsr, "pathLength"); + } + lsr_read_any_attribute(lsr, elt, 1); + lsr->prev_path = (SVG_Element*)elt; + } + lsr_read_group_content(lsr, elt, same_type); + return elt; +} +static GF_Node *lsr_read_polygon(GF_LASeRCodec *lsr, Bool is_polyline, u32 same_type) +{ + GF_FieldInfo info; + GF_Node *elt = gf_node_new(lsr->sg, is_polyline ? TAG_SVG_polyline : TAG_SVG_polygon); + + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_points, 1, 0, &info); + + if (same_type) { + if (lsr->prev_polygon) { + lsr_restore_base(lsr, (SVG_Element*)elt, (SVG_Element *)lsr->prev_polygon, (same_type==2) ? 0 : 0, (same_type==3) ? 0 : 0); + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] samepolyXXX coded in bitstream but no polyXXX defined !\n")); + } + lsr_read_id(lsr, elt); + if (same_type==2) lsr_read_fill(lsr, elt); + else if (same_type==3) lsr_read_stroke(lsr, elt); + lsr_read_point_sequence(lsr, *(GF_List**)info.far_ptr, "points"); + } else { + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_point_sequence(lsr, *(GF_List**)info.far_ptr, "points"); + lsr_read_any_attribute(lsr, elt, 1); + lsr->prev_polygon = (SVG_Element*)elt; + } + lsr_read_group_content(lsr, elt, same_type); + return elt; +} +static GF_Node *lsr_read_radialGradient(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_radialGradient); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_cx, 1, "cx"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_cy, 1, "cy"); + lsr_read_gradient_units(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_r, 1, "r"); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} +static GF_Node *lsr_read_rect(GF_LASeRCodec *lsr, u32 same_type) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_rect); + + if (same_type) { + if (lsr->prev_rect) { + lsr_restore_base(lsr, (SVG_Element*)elt, (SVG_Element *)lsr->prev_rect, (same_type==2) ? 1 : 0, 0); + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] samerect coded in bitstream but no rect defined !\n")); + } + lsr_read_id(lsr, elt); + if (same_type==2) lsr_read_fill(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_height, 0, "height"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_width, 0, "width"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_x, 1, "x"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y, 1, "y"); + } else { + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_height, 0, "height"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_rx, 1, "rx"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_ry, 1, "ry"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_width, 0, "width"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_x, 1, "x"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y, 1, "y"); + lsr_read_any_attribute(lsr, elt, 1); + lsr->prev_rect = (SVG_Element*)elt; + } + lsr_read_group_content(lsr, elt, same_type); + return elt; +} + +static GF_Node *lsr_read_rectClip(GF_LASeRCodec *lsr) +{ + u32 flag; + GF_Node *elt = gf_node_new(lsr->sg, TAG_LSR_rectClip); + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_eRR(lsr, elt); + GF_LSR_READ_INT(lsr, flag, 1, "has_size"); + if (flag) { + SVG_Number num; + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_size, 1, 0, &info); + lsr_read_coordinate(lsr, & num, 0, "width"); + ((LASeR_Size*)info.far_ptr)->width = num.value; + lsr_read_coordinate(lsr, & num, 0, "height"); + ((LASeR_Size*)info.far_ptr)->height = num.value; + } + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} + +static GF_Node *lsr_read_script(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_script); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_eRR(lsr, elt); + lsr_read_script_type(lsr, elt); + lsr_read_href(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} + +static GF_Node *lsr_read_selector(GF_LASeRCodec *lsr) +{ + u32 flag; + GF_Node *elt = gf_node_new(lsr->sg, TAG_LSR_selector); + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_eRR(lsr, elt); + GF_LSR_READ_INT(lsr, flag, 1, "hasChoice"); + if (flag) { + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_choice, 1, 0, &info); + GF_LSR_READ_INT(lsr, flag, 1, "choice"); + if (flag) { + GF_LSR_READ_INT(lsr, ((LASeR_Choice*)info.far_ptr)->type, 1, "type"); + } else { + GF_LSR_READ_INT(lsr, ((LASeR_Choice*)info.far_ptr)->choice_index, 8, "value"); + ((LASeR_Choice*)info.far_ptr)->type = LASeR_CHOICE_N; + } + } + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} + +static GF_Node *lsr_read_set(GF_LASeRCodec *lsr, SVG_Element *parent) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_set); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_attribute_name(lsr, elt); + lsr_read_attribute_type(lsr, elt); + + lsr_read_smil_times(lsr, elt, TAG_SVG_ATT_begin, NULL, "begin", 1); + lsr_read_duration(lsr, elt); + lsr_read_anim_fill(lsr, elt); + lsr_read_anim_repeatCount(lsr, elt); + lsr_read_repeat_duration(lsr, elt); + lsr_read_anim_restart(lsr, elt); + lsr_read_anim_value(lsr, elt, TAG_SVG_ATT_to, "to"); + lsr_read_href(lsr, elt); + lsr_read_lsr_enabled(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + + if (!lsr_setup_smil_anim(lsr, (SVG_Element*)elt, parent)) { + gf_list_add(lsr->defered_anims, elt); + lsr_read_group_content_post_init(lsr, (SVG_Element*)elt, 1); + } else { + lsr_read_group_content(lsr, elt, 0); + } + return elt; +} + +static GF_Node *lsr_read_simpleLayout(GF_LASeRCodec *lsr) +{ + u32 flag; + GF_Node *elt = gf_node_new(lsr->sg, TAG_LSR_simpleLayout); + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + GF_LSR_READ_INT(lsr, flag, 1, "has_delta"); + if (flag) { + SVG_Number num; + GF_FieldInfo info; + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_delta, 1, 0, &info); + lsr_read_coordinate(lsr, & num, 0, "width"); + ((LASeR_Size*)info.far_ptr)->width = num.value; + lsr_read_coordinate(lsr, & num, 0, "height"); + ((LASeR_Size*)info.far_ptr)->height = num.value; + } + lsr_read_eRR(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} + +static GF_Node *lsr_read_stop(GF_LASeRCodec *lsr) +{ + GF_FieldInfo info; + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_stop); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_offset, 1, 0, &info); + ((SVG_Number*)info.far_ptr)->value = lsr_read_fixed_16_8(lsr, "offset"); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} +static GF_Node *lsr_read_svg(GF_LASeRCodec *lsr, Bool init_node) +{ + GF_FieldInfo info; + SMIL_Duration snap; + u32 flag; + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_svg); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_string_attribute(lsr, elt, TAG_SVG_ATT_baseProfile, "baseProfile"); + lsr_read_string_attribute(lsr, elt, TAG_SVG_ATT_contentScriptType, "contentScriptType"); + lsr_read_eRR(lsr, elt); + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_height, 1, 0, &info); + lsr_read_value_with_units(lsr, info.far_ptr, "height"); + GF_LSR_READ_INT(lsr, flag, 1, "hasPlaybackOrder"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_playbackOrder, 1, 1, &info); + GF_LSR_READ_INT(lsr, flag, 1, "playbackOrder"); + if (flag) *(SVG_PlaybackOrder*)info.far_ptr = SVG_PLAYBACKORDER_FORWARDONLY; + } + + lsr_read_preserve_aspect_ratio(lsr, elt); + + + GF_LSR_READ_INT(lsr, flag, 1, "has_snapshotTime"); + if (flag) { + lsr_read_duration_ex(lsr, NULL, 0, &snap, "snapshotTime", 0); + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_snapshotTime, 1, 1, &info); + if (snap.type==SMIL_DURATION_DEFINED) *((SVG_Clock *)info.far_ptr) = snap.clock_value; + } + + GF_LSR_READ_INT(lsr, flag, 1, "hasSyncBehavior"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_syncBehaviorDefault, 1, 0, &info); + GF_LSR_READ_INT(lsr, flag, 2, "syncBehaviorDefault"); + switch (flag) { + case 0: *((SMIL_SyncBehavior*)info.far_ptr) = SMIL_SYNCBEHAVIOR_CANSLIP; break; + case 1: *((SMIL_SyncBehavior*)info.far_ptr) = SMIL_SYNCBEHAVIOR_INDEPENDENT; break; + case 3: *((SMIL_SyncBehavior*)info.far_ptr) = SMIL_SYNCBEHAVIOR_LOCKED; break; + default: *((SMIL_SyncBehavior*)info.far_ptr) = SMIL_SYNCBEHAVIOR_INHERIT; break; + } + } + GF_LSR_READ_INT(lsr, flag, 1, "hasSyncToleranceDefault"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_syncToleranceDefault, 1, 0, &info); + ((SMIL_SyncTolerance*)info.far_ptr)->type = SMIL_SYNCTOLERANCE_VALUE; + GF_LSR_READ_INT(lsr, flag, 1, "choice"); + ((SMIL_SyncTolerance*)info.far_ptr)->value = lsr_read_vluimsbf5(lsr, "value"); + ((SMIL_SyncTolerance*)info.far_ptr)->value /= lsr->time_resolution; + } + GF_LSR_READ_INT(lsr, flag, 1, "hasTimelineBegin"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_timelineBegin, 1, 0, &info); + GF_LSR_READ_INT(lsr, flag, 1, "timelineBegin"); + if (flag) *(SVG_TimelineBegin*)info.far_ptr = SVG_TIMELINEBEGIN_ONLOAD; + } + lsr_read_string_attribute(lsr, elt, TAG_SVG_ATT_version, "version"); + + GF_LSR_READ_INT(lsr, flag, 1, "hasViewBox"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_viewBox, 1, 0, &info); + ((SVG_ViewBox*)info.far_ptr)->x = lsr_read_fixed_16_8(lsr, "viewbox.x"); + ((SVG_ViewBox*)info.far_ptr)->y = lsr_read_fixed_16_8(lsr, "viewbox.y"); + ((SVG_ViewBox*)info.far_ptr)->width = lsr_read_fixed_16_8(lsr, "viewbox.width"); + ((SVG_ViewBox*)info.far_ptr)->height = lsr_read_fixed_16_8(lsr, "viewbox.height"); + ((SVG_ViewBox*)info.far_ptr)->is_set = 1; + } + + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_width, 1, 0, &info); + lsr_read_value_with_units(lsr, info.far_ptr, "width"); + + GF_LSR_READ_INT(lsr, flag, 1, "hasZoomAndPan"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_zoomAndPan, 1, 0, &info); + GF_LSR_READ_INT(lsr, flag, 1, "zoomAndPan"); + *((SVG_ZoomAndPan*)info.far_ptr) = flag ? SVG_ZOOMANDPAN_MAGNIFY : SVG_ZOOMANDPAN_DISABLE; + } + lsr_read_any_attribute(lsr, elt, 1); + /*store current root for listeners with no focus target*/ + lsr->current_root = elt; + + if (init_node) { + gf_node_register(elt, NULL); + gf_sg_set_root_node(lsr->sg, elt); + } + + lsr_read_group_content(lsr, elt, 0); + return elt; +} + +static GF_Node *lsr_read_switch(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_switch); + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_eRR(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} + + +static GF_Node *lsr_read_text(GF_LASeRCodec *lsr, u32 same_type) +{ + u32 flag; + GF_FieldInfo info; + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_text); + if (same_type) { + if (lsr->prev_text) { + lsr_restore_base(lsr, (SVG_Element *)elt, (SVG_Element *)lsr->prev_text, (same_type==2) ? 1 : 0, 0); + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] sametext coded in bitstream but no text defined !\n")); + } + lsr_read_id(lsr, elt); + if (same_type==2) lsr_read_fill(lsr, elt); + lsr_read_coord_list(lsr, elt, TAG_SVG_ATT_text_x, "x"); + lsr_read_coord_list(lsr, elt, TAG_SVG_ATT_text_y, "y"); + } else { + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + + GF_LSR_READ_INT(lsr, flag, 1, "editable"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_editable, 1, 0, &info); + *(SVG_Boolean*)info.far_ptr = flag; + } + lsr_read_float_list(lsr, elt, TAG_SVG_ATT_text_rotate, NULL, "rotate"); + lsr_read_coord_list(lsr, elt, TAG_SVG_ATT_text_x, "x"); + lsr_read_coord_list(lsr, elt, TAG_SVG_ATT_text_y, "y"); + lsr_read_any_attribute(lsr, elt, 1); + lsr->prev_text = (SVG_Element*)elt; + } + lsr_read_group_content(lsr, elt, same_type); + return elt; +} + +static GF_Node *lsr_read_tspan(GF_LASeRCodec *lsr) +{ + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_tspan); + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} + +static GF_Node *lsr_read_use(GF_LASeRCodec *lsr, Bool is_same) +{ + GF_FieldInfo info; + u32 flag; + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_use); + if (is_same) { + if (lsr->prev_use) { + lsr_restore_base(lsr, (SVG_Element *)elt, lsr->prev_use, 0, 0); + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] sameuse coded in bitstream but no use defined !\n")); + } + lsr_read_id(lsr, elt); + lsr_read_href(lsr, elt); + } else { + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_fill(lsr, elt); + lsr_read_stroke(lsr, elt); + lsr_read_eRR(lsr, elt); + + GF_LSR_READ_INT(lsr, flag, 1, "hasOverflow"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_overflow, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SVG_Overflow*)info.far_ptr, 2, "overflow"); + } + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_x, 1, "x"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y, 1, "y"); + lsr_read_href(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr->prev_use = (SVG_Element*)elt; + } + lsr_read_group_content(lsr, elt, is_same); + return elt; +} + +static GF_Node *lsr_read_video(GF_LASeRCodec *lsr, SVG_Element *parent) +{ + GF_FieldInfo info; + u32 flag; + GF_Node*elt = gf_node_new(lsr->sg, TAG_SVG_video); + lsr_read_id(lsr, elt); + lsr_read_rare_full(lsr, elt); + lsr_read_smil_times(lsr, elt, TAG_SVG_ATT_begin, NULL, "begin", 1); + lsr_read_duration(lsr, elt); + lsr_read_eRR(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_height, 1, "height"); + GF_LSR_READ_INT(lsr, flag, 1, "hasOverlay"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_overlay, 1, 1, &info); + GF_LSR_READ_INT(lsr, flag, 1, "choice"); + if (flag) { + GF_LSR_READ_INT(lsr, *(SVG_Overlay*)info.far_ptr, 1, "choice"); + } else { + char *str = NULL; + lsr_read_byte_align_string(lsr, & str, "overlayExt"); + if (str) free(str); + } + } + lsr_read_preserve_aspect_ratio(lsr, elt); + lsr_read_anim_repeatCount(lsr, elt); + lsr_read_repeat_duration(lsr, elt); + lsr_read_anim_restart(lsr, elt); + lsr_read_sync_behavior(lsr, elt); + lsr_read_sync_tolerance(lsr, elt); + lsr_read_transform_behavior(lsr, elt); + lsr_read_content_type(lsr, elt); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_width, 1, "width"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_x, 1, "x"); + lsr_read_coordinate_ptr(lsr, elt, TAG_SVG_ATT_y, 1, "y"); + lsr_read_href(lsr, elt); + + lsr_read_clip_time(lsr, elt, TAG_SVG_ATT_clipBegin, "clipBegin"); + lsr_read_clip_time(lsr, elt, TAG_SVG_ATT_clipEnd, "clipEnd"); + + GF_LSR_READ_INT(lsr, flag, 1, "hasFullscreen"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_SVG_ATT_fullscreen, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(SVG_Boolean *)info.far_ptr, 1, "fullscreen"); + } + + lsr_read_sync_reference(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + return elt; +} + +static GF_Node *lsr_read_listener(GF_LASeRCodec *lsr, SVG_Element *parent) +{ + u32 flag; + GF_FieldInfo info; + XMLEV_Event *ev = NULL; + XMLRI *observer, *target, *handler; + GF_Node *elt = gf_node_new(lsr->sg, TAG_SVG_listener); + + observer = target = handler = NULL; + + lsr_read_id(lsr, elt); + lsr_read_rare(lsr, elt); + GF_LSR_READ_INT(lsr, flag, 1, "hasDefaultAction"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_XMLEV_ATT_defaultAction, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(XMLEV_DefaultAction*)info.far_ptr, 1, "defaultAction"); + } + GF_LSR_READ_INT(lsr, flag, 1, "hasEvent"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_XMLEV_ATT_event, 1, 0, &info); + lsr_read_event_type(lsr, info.far_ptr); + ev = info.far_ptr; + } + /*create default handler but UNINITIALIZED*/ + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_XMLEV_ATT_handler, 1, 0, &info); + handler = info.far_ptr; + GF_LSR_READ_INT(lsr, flag, 1, "hasHandler"); + if (flag) { + lsr_read_any_uri(lsr, info.far_ptr, "handler"); + } + GF_LSR_READ_INT(lsr, flag, 1, "hasObserver"); + /*TODO double check spec here*/ + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_XMLEV_ATT_observer, 1, 0, &info); + lsr_read_codec_IDREF(lsr, info.far_ptr, "observer"); + observer = info.far_ptr; + } + + GF_LSR_READ_INT(lsr, flag, 1, "hasPhase"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_XMLEV_ATT_phase, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(XMLEV_Phase*)info.far_ptr, 1, "phase"); + } + GF_LSR_READ_INT(lsr, flag, 1, "hasPropagate"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_XMLEV_ATT_propagate, 1, 0, &info); + GF_LSR_READ_INT(lsr, *(XMLEV_Propagate*)info.far_ptr, 1, "propagate"); + } + GF_LSR_READ_INT(lsr, flag, 1, "hasTarget"); + if (flag) { + lsr->last_error = gf_node_get_attribute_by_tag(elt, TAG_XMLEV_ATT_target, 1, 0, &info); + lsr_read_codec_IDREF(lsr, info.far_ptr, "target"); + target = info.far_ptr; + } + + lsr_read_lsr_enabled(lsr, elt); + lsr_read_any_attribute(lsr, elt, 1); + lsr_read_group_content(lsr, elt, 0); + + /*register listener element*/ + { + Bool post_pone = 0; + SVG_Element *par = NULL; + if (observer && observer->type == XMLRI_ELEMENTID) { + if (observer->target) par = observer->target; + } + if (!par && target && (target->type == XMLRI_ELEMENTID)) { + if (!target->target) post_pone = 1; + else par = target->target; + } + if (!handler->target) { + handler->type = XMLRI_ELEMENTID; + handler->target = parent; + } + /*FIXME - double check with XML events*/ + if (!par && !observer) { + /*all non-UI get attched to root*/ + if (ev && (ev->type>GF_EVENT_MOUSEWHEEL)) { + par = (SVG_Element*) lsr->current_root; + } + else if (parent) par = parent; + else par = (SVG_Element*) lsr->current_root; + } + if (!par) post_pone = 1; + + if (post_pone) { + gf_list_add(lsr->defered_listeners, elt); + } else { + if (!par) par = parent; + gf_node_dom_listener_add((GF_Node *)par, elt); + } + } + return elt; +} + +static GF_Node *lsr_read_scene_content_model(GF_LASeRCodec *lsr, SVG_Element *parent) +{ + GF_Node *n; + u32 ntype; + GF_LSR_READ_INT(lsr, ntype, 6, "ch4"); + n = NULL; + switch (ntype) { + case LSR_SCENE_CONTENT_MODEL_a: n = lsr_read_a(lsr); break; + case LSR_SCENE_CONTENT_MODEL_animate: n = lsr_read_animate(lsr, parent, 0); break; + case LSR_SCENE_CONTENT_MODEL_animateColor: n = lsr_read_animate(lsr, parent, 1); break; + case LSR_SCENE_CONTENT_MODEL_animateMotion: n = lsr_read_animateMotion(lsr, parent); break; + case LSR_SCENE_CONTENT_MODEL_animateTransform: n = lsr_read_animateTransform(lsr, parent); break; + case LSR_SCENE_CONTENT_MODEL_audio: n = lsr_read_audio(lsr, parent); break; + case LSR_SCENE_CONTENT_MODEL_circle: n = lsr_read_circle(lsr); break; + case LSR_SCENE_CONTENT_MODEL_conditional: n = lsr_read_conditional(lsr); break; + case LSR_SCENE_CONTENT_MODEL_cursorManager: n = lsr_read_cursorManager(lsr); break; + case LSR_SCENE_CONTENT_MODEL_defs: n = lsr_read_defs(lsr); break; + case LSR_SCENE_CONTENT_MODEL_desc: n = lsr_read_data(lsr, TAG_SVG_desc); break; + case LSR_SCENE_CONTENT_MODEL_ellipse: n = lsr_read_ellipse(lsr); break; + case LSR_SCENE_CONTENT_MODEL_foreignObject: n = lsr_read_foreignObject(lsr); break; + case LSR_SCENE_CONTENT_MODEL_g: n = lsr_read_g(lsr, 0); break; + case LSR_SCENE_CONTENT_MODEL_image: n = lsr_read_image(lsr); break; + case LSR_SCENE_CONTENT_MODEL_line: n = lsr_read_line(lsr, 0); break; + case LSR_SCENE_CONTENT_MODEL_linearGradient: n = lsr_read_linearGradient(lsr); break; + case LSR_SCENE_CONTENT_MODEL_metadata: n = lsr_read_data(lsr, TAG_SVG_metadata); break; + case LSR_SCENE_CONTENT_MODEL_mpath: n = lsr_read_mpath(lsr); break; + case LSR_SCENE_CONTENT_MODEL_path: n = lsr_read_path(lsr, 0); break; + case LSR_SCENE_CONTENT_MODEL_polygon: n = lsr_read_polygon(lsr, 0, 0); break; + case LSR_SCENE_CONTENT_MODEL_polyline: n = lsr_read_polygon(lsr, 1, 0); break; + case LSR_SCENE_CONTENT_MODEL_radialGradient: n = lsr_read_radialGradient(lsr); break; + case LSR_SCENE_CONTENT_MODEL_rect: n = lsr_read_rect(lsr, 0); break; + case LSR_SCENE_CONTENT_MODEL_rectClip: n = lsr_read_rectClip(lsr); break; + case LSR_SCENE_CONTENT_MODEL_sameg: n = lsr_read_g(lsr, 1); break; + case LSR_SCENE_CONTENT_MODEL_sameline: n = lsr_read_line(lsr, 1); break; + case LSR_SCENE_CONTENT_MODEL_samepath: n = lsr_read_path(lsr, 1); break; + case LSR_SCENE_CONTENT_MODEL_samepathfill: n = lsr_read_path(lsr, 2); break; + case LSR_SCENE_CONTENT_MODEL_samepolygon: n = lsr_read_polygon(lsr, 0, 1); break; + case LSR_SCENE_CONTENT_MODEL_samepolygonfill: n = lsr_read_polygon(lsr, 0, 2); break; + case LSR_SCENE_CONTENT_MODEL_samepolygonstroke: n = lsr_read_polygon(lsr, 0, 3); break; + case LSR_SCENE_CONTENT_MODEL_samepolyline: n = lsr_read_polygon(lsr, 1, 1); break; + case LSR_SCENE_CONTENT_MODEL_samepolylinefill: n = lsr_read_polygon(lsr, 1, 2); break; + case LSR_SCENE_CONTENT_MODEL_samepolylinestroke: n = lsr_read_polygon(lsr, 1, 3); break; + case LSR_SCENE_CONTENT_MODEL_samerect: n = lsr_read_rect(lsr, 1); break; + case LSR_SCENE_CONTENT_MODEL_samerectfill: n = lsr_read_rect(lsr, 2); break; + case LSR_SCENE_CONTENT_MODEL_sametext: n = lsr_read_text(lsr, 1); break; + case LSR_SCENE_CONTENT_MODEL_sametextfill: n = lsr_read_text(lsr, 2); break; + case LSR_SCENE_CONTENT_MODEL_sameuse: n = lsr_read_use(lsr, 1); break; + case LSR_SCENE_CONTENT_MODEL_script: n = lsr_read_script(lsr); break; + case LSR_SCENE_CONTENT_MODEL_selector: n = lsr_read_selector(lsr); break; + case LSR_SCENE_CONTENT_MODEL_set: n = lsr_read_set(lsr, parent); break; + case LSR_SCENE_CONTENT_MODEL_simpleLayout: n = lsr_read_simpleLayout(lsr); break; + case LSR_SCENE_CONTENT_MODEL_stop: n = lsr_read_stop(lsr); break; + case LSR_SCENE_CONTENT_MODEL_switch: n = lsr_read_switch(lsr); break; + case LSR_SCENE_CONTENT_MODEL_text: n = lsr_read_text(lsr, 0); break; + case LSR_SCENE_CONTENT_MODEL_title: n = lsr_read_data(lsr, TAG_SVG_title); break; + case LSR_SCENE_CONTENT_MODEL_tspan: n = lsr_read_tspan(lsr); break; + case LSR_SCENE_CONTENT_MODEL_use: n = lsr_read_use(lsr, 0); break; + case LSR_SCENE_CONTENT_MODEL_video: n = lsr_read_video(lsr, parent); break; + case LSR_SCENE_CONTENT_MODEL_listener: n = lsr_read_listener(lsr, parent); break; + case LSR_SCENE_CONTENT_MODEL_element_any: lsr_read_extend_class(lsr, NULL, 0, "node"); break; + case LSR_SCENE_CONTENT_MODEL_privateContainer: lsr_read_private_element_container(lsr); break; + case LSR_SCENE_CONTENT_MODEL_textContent: lsr_read_text_content(lsr, (GF_Node*)parent); break; + default: + break; + } + if (n && n->sgprivate->interact && n->sgprivate->interact->dom_evt) { + GF_DOM_Event evt; + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = GF_EVENT_LOAD; + gf_dom_event_fire(n, &evt); + } + return n; +} + +static GF_Node *lsr_read_update_content_model(GF_LASeRCodec *lsr, SVG_Element *parent) +{ + u32 flag; + GF_Node *n=NULL; + GF_LSR_READ_INT(lsr, flag, 1, "ch4"); + if (flag) { + GF_LSR_READ_INT(lsr, flag, 3, "ch61"); + switch (flag) { + case LSR_UPDATE_CONTENT_MODEL2_conditional: + n = lsr_read_conditional(lsr); break; + case LSR_UPDATE_CONTENT_MODEL2_cursorManager: + n = lsr_read_cursorManager(lsr); break; + case LSR_UPDATE_CONTENT_MODEL2_extend: + lsr_read_extend_class(lsr, NULL, 0, "extend"); + return NULL; + case LSR_UPDATE_CONTENT_MODEL2_private: + lsr_read_private_element_container(lsr); + return NULL; + case LSR_UPDATE_CONTENT_MODEL2_rectClip: + n = lsr_read_rectClip(lsr); break; + case LSR_UPDATE_CONTENT_MODEL2_simpleLayout: + n = lsr_read_simpleLayout(lsr); break; + case LSR_UPDATE_CONTENT_MODEL2_selector: + n = lsr_read_selector(lsr); break; + } + } else { + GF_LSR_READ_INT(lsr, flag, 6, "ch6"); + switch(flag) { + case LSR_UPDATE_CONTENT_MODEL_a: n = lsr_read_a(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_animate: n = lsr_read_animate(lsr, parent, 0); break; + case LSR_UPDATE_CONTENT_MODEL_animateColor: n = lsr_read_animate(lsr, parent, 1); break; + case LSR_UPDATE_CONTENT_MODEL_animateMotion: n = lsr_read_animateMotion(lsr, parent); break; + case LSR_UPDATE_CONTENT_MODEL_animateTransform: n = lsr_read_animateTransform(lsr, parent); break; + case LSR_UPDATE_CONTENT_MODEL_audio: n = lsr_read_audio(lsr, parent); break; + case LSR_UPDATE_CONTENT_MODEL_circle: n = lsr_read_circle(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_defs: n = lsr_read_defs(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_desc: n = lsr_read_data(lsr, TAG_SVG_desc); break; + case LSR_UPDATE_CONTENT_MODEL_ellipse: n = lsr_read_ellipse(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_foreignObject: n = lsr_read_foreignObject(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_g: n = lsr_read_g(lsr, 0); break; + case LSR_UPDATE_CONTENT_MODEL_image: n = lsr_read_image(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_line: n = lsr_read_line(lsr, 0); break; + case LSR_UPDATE_CONTENT_MODEL_linearGradient: n = lsr_read_linearGradient(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_metadata: n = lsr_read_data(lsr, TAG_SVG_metadata); break; + case LSR_UPDATE_CONTENT_MODEL_mpath: n = lsr_read_mpath(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_path: n = lsr_read_path(lsr, 0); break; + case LSR_UPDATE_CONTENT_MODEL_polygon: n = lsr_read_polygon(lsr, 0, 0); break; + case LSR_UPDATE_CONTENT_MODEL_polyline: n = lsr_read_polygon(lsr, 1, 0); break; + case LSR_UPDATE_CONTENT_MODEL_radialGradient: n = lsr_read_radialGradient(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_rect: n = lsr_read_rect(lsr, 0); break; + case LSR_UPDATE_CONTENT_MODEL_script: n = lsr_read_script(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_set: n = lsr_read_set(lsr, parent); break; + case LSR_UPDATE_CONTENT_MODEL_stop: n = lsr_read_stop(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_svg: n = lsr_read_svg(lsr, 0); break; + case LSR_UPDATE_CONTENT_MODEL_switch: n = lsr_read_switch(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_text: n = lsr_read_text(lsr, 0); break; + case LSR_UPDATE_CONTENT_MODEL_title: n = lsr_read_data(lsr, TAG_SVG_title); break; + case LSR_UPDATE_CONTENT_MODEL_tspan: n = lsr_read_tspan(lsr); break; + case LSR_UPDATE_CONTENT_MODEL_use: n = lsr_read_use(lsr, 0); break; + case LSR_UPDATE_CONTENT_MODEL_video: n = lsr_read_video(lsr, parent); break; + case LSR_UPDATE_CONTENT_MODEL_listener: n = lsr_read_listener(lsr, parent); break; + } + } + if (n && n->sgprivate->interact && n->sgprivate->interact->dom_evt) { + GF_DOM_Event evt; + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = GF_EVENT_LOAD; + gf_dom_event_fire(n, &evt); + } + return n; +} + +static void lsr_read_group_content(GF_LASeRCodec *lsr, GF_Node *elt, Bool skip_object_content) +{ + u32 i, count; + if (lsr->last_error) return; + + if (!skip_object_content) lsr_read_object_content(lsr, (SVG_Element*)elt); + + + /*node attributes are all parsed*/ + gf_node_init(elt); + + GF_LSR_READ_INT(lsr, count, 1, "opt_group"); + if (count) { + GF_ChildNodeItem *last = NULL; + count = lsr_read_vluimsbf5(lsr, "occ0"); + for (i=0; ilast_error) break; + n = lsr_read_scene_content_model(lsr, (SVG_Element*)elt); + if (n) { + gf_node_register(n, elt); + gf_node_list_add_child_last(& ((SVG_Element*)elt)->children, n, &last); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] ############## end %s ###########\n", gf_node_get_class_name(n))); + } else { + /*either error or text content*/ + } + } + } +} + +static void lsr_read_group_content_post_init(GF_LASeRCodec *lsr, SVG_Element *elt, Bool skip_init) +{ + u32 i, count; + if (lsr->last_error) return; + lsr_read_object_content(lsr, elt); + + GF_LSR_READ_INT(lsr, count, 1, "opt_group"); + if (count) { + GF_ChildNodeItem *last = NULL; + count = lsr_read_vluimsbf5(lsr, "occ0"); + for (i=0; ilast_error) return; + n = lsr_read_scene_content_model(lsr, elt); + if (n) { + gf_node_register(n, (GF_Node*)elt); + gf_node_list_add_child_last(&elt->children, n, &last); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] ############## end %s ###########\n", gf_node_get_class_name(n))); + } else { + /*either error or text content*/ + } + } + } + if (!skip_init) gf_node_init((GF_Node*)elt); +} + +static void *lsr_read_update_value_indexed(GF_LASeRCodec *lsr, GF_Node*node, u32 fieldType, void *rep_val, u32 idx, Bool is_insert, Bool is_com) +{ + Fixed *f_val; + SVG_Point *pt; + SVG_Number num; + + switch (fieldType) { + case SVG_Points_datatype/*ITYPE_point*/: + pt = (SVG_Point*)malloc(sizeof(SVG_Point)); + lsr_read_coordinate(lsr, &num, 0, "coordX"); + pt->x = num.value; + lsr_read_coordinate(lsr, &num, 0, "coordY"); + pt->y = num.value; + return pt; + case SMIL_KeySplines_datatype/*ITYPE_float*/: + case SVG_StrokeDashArray_datatype: + case SVG_ViewBox_datatype: + f_val = (Fixed*)malloc(sizeof(u8)); + *f_val = lsr_read_fixed_16_8(lsr, "floatValue"); + return f_val; + case SMIL_KeyTimes_datatype/*ITYPE_keyTime*/: + f_val = lsr_read_fraction_12_item(lsr); + break; + case SMIL_KeyPoints_datatype/*ITYPE_0to1 - keyPoints*/: + pt = (SVG_Point*)malloc(sizeof(SVG_Point)); + pt->x = lsr_read_fixed_clamp(lsr, "valueX"); + f_val = (Fixed*)malloc(sizeof(Fixed)); + pt->y = lsr_read_fixed_clamp(lsr, "valueY"); + return pt; + case SMIL_Times_datatype/*ITYPE_smil_time*/: + return lsr_read_smil_time(lsr, node); + default: + lsr_read_extension(lsr, "privateData"); + break; + } + return NULL; +} + +static void lsr_read_update_value(GF_LASeRCodec *lsr, GF_Node *node, u32 att_tag, u32 fieldType, void *val, u32 node_tag) +{ + u32 is_default, has_escape, escape_val = 0; + SVG_Paint *paint; + SVG_Number num, *n; + is_default = has_escape = 0; + + switch (fieldType) { + case SVG_Boolean_datatype: + GF_LSR_READ_INT(lsr, *(SVG_Boolean*)val, 1, "val"); + break; + case SVG_Paint_datatype: + paint = (SVG_Paint *)val; + lsr_read_paint(lsr, (SVG_Paint*)val, "val"); + break; +/* + case SVG_AudioLevel_datatype: + n = val; + GF_LSR_READ_INT(lsr, is_default, 1, "isDefaultValue"); + if (is_default) n->type=SVG_NUMBER_INHERIT; + else { + n->type = SVG_NUMBER_VALUE; + n->value = lsr_read_fixed_clamp(lsr, "val"); + } + break; +*/ + case SVG_Transform_Scale_datatype: + ((SVG_Point *)val)->x = lsr_read_fixed_16_8(lsr, "scale_x"); + ((SVG_Point *)val)->y = lsr_read_fixed_16_8(lsr, "scale_y"); + break; + case LASeR_Size_datatype: + case SVG_Transform_Translate_datatype: + lsr_read_coordinate(lsr, &num, 0, "translation_x"); + ((SVG_Point *)val)->x = num.value; + lsr_read_coordinate(lsr, &num, 0, "translation_y"); + ((SVG_Point *)val)->y = num.value; + break; + case SVG_Transform_Rotate_datatype: + GF_LSR_READ_INT(lsr, is_default, 1, "isDefaultValue"); + if (is_default) ((SVG_Point_Angle*)val)->angle = 0; + else { + GF_LSR_READ_INT(lsr, has_escape, 1, "escapeFlag"); + if (has_escape) { + GF_LSR_READ_INT(lsr, escape_val, 2, "escapeEnum"); + ((SVG_Point_Angle*)val)->angle = 0; + } + else { + ((SVG_Point_Angle*)val)->angle = lsr_read_fixed_16_8(lsr, "rotate"); + } + } + break; + case SVG_Transform_datatype: + lsr_read_matrix(lsr, val); + break; + case SVG_Number_datatype: + case SVG_FontSize_datatype: + case SVG_Length_datatype: + n = (SVG_Number*)val; + switch (att_tag) { + /*fractions*/ + case TAG_SVG_ATT_audio_level: + case TAG_SVG_ATT_fill_opacity: + case TAG_SVG_ATT_offset: + case TAG_SVG_ATT_opacity: + case TAG_SVG_ATT_solid_opacity: + case TAG_SVG_ATT_stop_opacity: + case TAG_SVG_ATT_stroke_opacity: + case TAG_SVG_ATT_viewport_fill_opacity: + GF_LSR_READ_INT(lsr, is_default, 1, "isDefaultValue"); + if (is_default) n->type=SVG_NUMBER_INHERIT; + else { + n->type = SVG_NUMBER_VALUE; + n->value = lsr_read_fixed_clamp(lsr, "val"); + } + break; + case TAG_SVG_ATT_width: + case TAG_SVG_ATT_height: + if (node_tag==TAG_SVG_svg) { + lsr_read_value_with_units(lsr, n, "val"); + } else { + lsr_read_coordinate(lsr, n, 0, "val"); + } + break; + default: + GF_LSR_READ_INT(lsr, is_default, 1, "isDefaultValue"); + if (is_default) n->type=SVG_NUMBER_INHERIT; + else { + GF_LSR_READ_INT(lsr, has_escape, 1, "escapeFlag"); + if (has_escape) { + GF_LSR_READ_INT(lsr, escape_val, 2, "escapeEnum"); + n->type = SVG_NUMBER_AUTO;//only lineIncrement + } else { + n->type = SVG_NUMBER_VALUE; + n->value = lsr_read_fixed_16_8(lsr, "val"); + } + } + break; + } + break; + case SVG_Coordinate_datatype: + n = (SVG_Number*)val; + n->type = SVG_NUMBER_VALUE; + lsr_read_coordinate(lsr, n, 0, "val"); + break; + + case SVG_Rotate_datatype: + n = (SVG_Number*)val; + GF_LSR_READ_INT(lsr, is_default, 1, "isDefaultValue"); + if (is_default) n->type=SVG_NUMBER_INHERIT; + else { + GF_LSR_READ_INT(lsr, has_escape, 1, "escapeFlag"); + if (has_escape) { + GF_LSR_READ_INT(lsr, escape_val, 2, "escapeEnum"); + n->type = escape_val ? SVG_NUMBER_AUTO_REVERSE : SVG_NUMBER_AUTO; + } else { + n->type = SVG_NUMBER_VALUE; + n->value = lsr_read_fixed_16_8(lsr, "rotate"); + } + } + break; + case SVG_Coordinates_datatype: + lsr_read_float_list(lsr, NULL, 0, val, "val"); + break; + case SVG_ViewBox_datatype: + { + u32 count; + SVG_ViewBox *vb = (SVG_ViewBox *)val; + GF_LSR_READ_INT(lsr, count, 1, "isDefault"); + if (count) { + vb->is_set = 0; + } else { + vb->is_set = 1; + GF_LSR_READ_INT(lsr, count, 1, "escapeFlag"); + count = lsr_read_vluimsbf5(lsr, "count"); + if (count) { vb->x = lsr_read_fixed_16_8(lsr, "val"); count--; } + if (count) { vb->y = lsr_read_fixed_16_8(lsr, "val"); count--; } + if (count) { vb->width = lsr_read_fixed_16_8(lsr, "val"); count--; } + if (count) { vb->height = lsr_read_fixed_16_8(lsr, "val"); } + } + } + break; + case XMLRI_datatype: + case SVG_Focus_datatype: + if ((att_tag==TAG_XLINK_ATT_href) || (att_tag==TAG_SVG_ATT_syncReference)) { + lsr_read_any_uri(lsr, (XMLRI*)val, "val"); + } else { + Bool is_default, is_escape; + u32 escape_val, ID; + escape_val = ID = 0; + is_escape = 0; + GF_LSR_READ_INT(lsr, is_default, 1, "isDefault"); + if (!is_default) { + GF_LSR_READ_INT(lsr, is_escape, 1, "isEscape"); + if (is_escape) { + GF_LSR_READ_INT(lsr, escape_val, 2, "escapeEnumVal"); + } else { + ID = lsr_read_vluimsbf5(lsr, "ID"); + } + } + if (att_tag==SVG_Focus_datatype) { + if (is_default) ((SVG_Focus*)val)->type = SVG_FOCUS_AUTO; + else if (is_escape) ((SVG_Focus*)val)->type = escape_val; + else { + ((SVG_Focus*)val)->type = SVG_FOCUS_IRI; + ((SVG_Focus*)val)->target.type = XMLRI_ELEMENTID; + ((SVG_Focus*)val)->target.target = gf_sg_find_node(lsr->sg, ID); + } + } else { + if (is_default) ((XMLRI*)val)->type = XMLRI_STRING; + else { + ((XMLRI*)val)->type = XMLRI_ELEMENTID; + ((XMLRI*)val)->target = gf_sg_find_node(lsr->sg, ID); + } + } + } + break; + + case DOM_String_datatype: + case SVG_ContentType_datatype: + case SVG_LanguageID_datatype: + lsr_read_byte_align_string(lsr, (char**)val, "val"); + break; + case SVG_Motion_datatype: + lsr_read_coordinate(lsr, &num, 0, "pointValueX"); + ((GF_Matrix2D*)val)->m[2] = num.value; + lsr_read_coordinate(lsr, &num, 0, "pointValueY"); + ((GF_Matrix2D*)val)->m[5] = num.value; + break; + case SVG_Points_datatype: + lsr_read_point_sequence(lsr, *(GF_List **)val, "val"); + break; + case SVG_PathData_datatype: + lsr_read_path_type(lsr, NULL, 0, (SVG_PathData*)val, "val"); + break; + case SVG_FontFamily_datatype: + { + u32 idx; + SVG_FontFamily *ff = (SVG_FontFamily *)val; + GF_LSR_READ_INT(lsr, idx, 1, "isDefault"); + ff->type = SVG_FONTFAMILY_INHERIT; + if (!idx) { + char *ft; + GF_LSR_READ_INT(lsr, idx, 1, "escapeFlag"); + idx = lsr_read_vluimsbf5(lsr, "index"); + if (ff->value) free(ff->value); + ff->value = NULL; + ft = (char*)gf_list_get(lsr->font_table, idx); + if (ft) { + ff->value = strdup(ft); + ff->type = SVG_FONTFAMILY_VALUE; + } + } + } + break; + break; + case LASeR_Choice_datatype: + GF_LSR_READ_INT(lsr, is_default, 1, "isDefaultValue"); + if (is_default) ((LASeR_Choice *)val)->type = LASeR_CHOICE_ALL; + else { + GF_LSR_READ_INT(lsr, has_escape, 1, "escapeFlag"); + if (has_escape) { + GF_LSR_READ_INT(lsr, escape_val, 2, "escapeEnum"); + ((LASeR_Choice *)val)->type = escape_val ? LASeR_CHOICE_NONE : LASeR_CHOICE_ALL; + } else { + ((LASeR_Choice *)val)->type = LASeR_CHOICE_N; + ((LASeR_Choice *)val)->choice_index = lsr_read_vluimsbf5(lsr, "value"); + } + } + break; + default: + if ((fieldType>=SVG_FillRule_datatype) && (fieldType<=SVG_LAST_U8_PROPERTY)) { + /*TODO fixme, check inherit values*/ + GF_LSR_READ_INT(lsr, is_default, 1, "isDefaultValue"); + if (is_default) *(u8 *)val = 0; + else *(u8 *)val = lsr_read_vluimsbf5(lsr, "val"); + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] Warning: update value not supported: fieldType %d - attribute tag %d\n", fieldType, att_tag)); + } + } + if (node) { + //gf_node_dirty_set(node, 0, 0); + gf_node_changed(node, NULL); + } +} + +static u32 lsr_get_attribute_name(GF_LASeRCodec *lsr) +{ + u32 val = 1; + GF_LSR_READ_INT(lsr, val, 1, "has_attributeName"); + if (!val) return -1; + + GF_LSR_READ_INT(lsr, val, 1, "choice"); + if (val) { + lsr_read_vluimsbf5(lsr, "item[i]"); + lsr_read_vluimsbf5(lsr, "item[i]"); + return -1; + } else { + GF_LSR_READ_INT(lsr, val, 8, "attributeName"); + return val; + } +} + +static GF_Err lsr_read_add_replace_insert(GF_LASeRCodec *lsr, GF_List *com_list, u32 com_type) +{ + GF_FieldInfo info; + GF_Node *n, *operandNode, *new_node; + GF_Command *com; + GF_CommandField *field; + s32 idx, att_type, op_att_type; + u32 type, idref, op_idref = 0; + + operandNode = NULL; + att_type = op_att_type = -1; + + att_type = lsr_get_attribute_name(lsr); + + idx = -1; + if (com_type) { + GF_LSR_READ_INT(lsr, type, 1, "has_index"); + if (type) idx = lsr_read_vluimsbf5(lsr, "index"); + } + if (com_type!=3) { + GF_LSR_READ_INT(lsr, type, 1, "has_operandAttribute"); + if (type) GF_LSR_READ_INT(lsr, op_att_type, 8, "attributeName"); + GF_LSR_READ_INT(lsr, type, 1, "has_operandElementId"); + if (type) { + op_idref = lsr_read_codec_IDREF_command(lsr, "operandElementId"); + operandNode = gf_sg_find_node(lsr->sg, op_idref); + if (!operandNode) + return GF_NON_COMPLIANT_BITSTREAM; + } + } + idref = lsr_read_codec_IDREF_command(lsr, "ref"); + + n = gf_sg_find_node(lsr->sg, idref); + if (!n) { + if (!com_list) { + return GF_NON_COMPLIANT_BITSTREAM; + } + } + + GF_LSR_READ_INT(lsr, type, 1, "has_value"); + if (type) { + /*node or node-list replacement*/ + if (att_type==-2) { + lsr_read_byte_align_string(lsr, NULL, "anyXML"); + } + else if (att_type<0) { + GF_Node *new_node; + if (!com_type) + return GF_NON_COMPLIANT_BITSTREAM; + GF_LSR_READ_INT(lsr, type, 1, "isInherit"); + if (type) + return GF_NON_COMPLIANT_BITSTREAM; + if (idx==-1) { + GF_LSR_READ_INT(lsr, type, 1, "escapeFlag"); + if (type) + return GF_NON_COMPLIANT_BITSTREAM; + } + + new_node = lsr_read_update_content_model(lsr, (idx==-1) ? NULL : (SVG_Element *)n); + if (com_list) { + com = gf_sg_command_new(lsr->sg, (com_type==3) ? GF_SG_LSR_INSERT : GF_SG_LSR_REPLACE); + gf_list_add(com_list, com); + if (n) { + com->node = n; + gf_node_register(com->node, NULL); + } else { + com->RouteID = idref; + gf_list_add(lsr->unresolved_commands, com); + } + field = gf_sg_command_field_new(com); + field->pos = idx; + field->new_node = new_node; + if (new_node) gf_node_register(new_node, NULL); + } else if (com_type==3) { + gf_node_list_insert_child(& ((SVG_Element *)n)->children, new_node, idx); + if (new_node) gf_node_register(new_node, n); + } else { + /*child replacement*/ + if (idx!=-1) { + GF_Node *old = gf_node_list_get_child( ((SVG_Element *)n)->children, idx); + if (old) + gf_node_replace(old, new_node, 0); + else { + gf_node_list_add_child( & ((SVG_Element *)n)->children, new_node); + if (new_node) gf_node_register(new_node, n); + } + } else { + /*node replacement*/ + gf_node_replace(n, new_node, 0); + } + } + } + /*value replace/add*/ + else if (com_list) { + u32 field_type; + Bool text_content = 0; + com = gf_sg_command_new(lsr->sg, (com_type==0) ? GF_SG_LSR_ADD : (com_type==3) ? GF_SG_LSR_INSERT : GF_SG_LSR_REPLACE); + field = gf_sg_command_field_new(com); + field->pos = idx; + field_type = 0; + switch (att_type) { + /*text*/ + case LSR_UPDATE_TYPE_TEXT_CONTENT: + text_content = 1; + break; + /*matrix.translation, scale or rotate*/ + case LSR_UPDATE_TYPE_SCALE: + field->fieldType = field_type = SVG_Transform_Scale_datatype; + field->fieldIndex = gf_lsr_anim_type_to_attribute(att_type); + break; + case LSR_UPDATE_TYPE_ROTATE: + field->fieldType = field_type = SVG_Transform_Rotate_datatype; + field->fieldIndex = TAG_SVG_ATT_transform; + break; + case LSR_UPDATE_TYPE_TRANSLATION: + field->fieldType = field_type = SVG_Transform_Translate_datatype; + field->fieldIndex = gf_lsr_anim_type_to_attribute(att_type); + break; + case LSR_UPDATE_TYPE_SVG_HEIGHT: + field->fieldIndex = TAG_SVG_ATT_height; + field_type = field->fieldType = SVG_Length_datatype; + break; + case LSR_UPDATE_TYPE_SVG_WIDTH: + field->fieldIndex = TAG_SVG_ATT_width; + field_type = field->fieldType = SVG_Length_datatype; + break; + default: + field->fieldIndex = gf_lsr_anim_type_to_attribute(att_type); + if (field->fieldIndex == (u32)-1) { + lsr->last_error = GF_NON_COMPLIANT_BITSTREAM; + gf_sg_command_del(com); + return lsr->last_error; + } + field_type = field->fieldType = gf_xml_get_attribute_type(field->fieldIndex); + break; + } + gf_list_add(com_list, com); + if (n) { + com->node = n; + gf_node_register(com->node, NULL); + } else { + com->RouteID = idref; + gf_list_add(lsr->unresolved_commands, com); + } + if (idx==-1) { + if (text_content) { + GF_DOMText *text = gf_dom_new_text_node(n->sgprivate->scenegraph); + gf_node_register((GF_Node *)text, NULL); + lsr_read_byte_align_string(lsr, &text->textContent, "val"); + field->new_node = (GF_Node*)text; + } else { + field->field_ptr = gf_svg_create_attribute_value(field_type); + lsr_read_update_value(lsr, NULL, field->fieldIndex, field->fieldType, field->field_ptr, n ? n->sgprivate->tag : 0); + } + } else { + field->field_ptr = lsr_read_update_value_indexed(lsr, (GF_Node*)n, field_type, NULL, idx, com_type==LSR_UPDATE_INSERT, 1); + } + } else { + GF_Point2D matrix_tmp; + SVG_Point_Angle matrix_tmp_rot; + u32 fieldIndex = 0; + u32 field_type = 0; + Bool text_content = 0; + Bool is_lsr_transform = 0; + switch (att_type) { + /*text*/ + case LSR_UPDATE_TYPE_TEXT_CONTENT: + text_content = 1; + break; + /*matrix.translation, scale or rotate*/ + case LSR_UPDATE_TYPE_SCALE: + info.far_ptr = (void *)&matrix_tmp; field_type = SVG_Transform_Scale_datatype; is_lsr_transform = 1; + break; + case LSR_UPDATE_TYPE_ROTATE: + info.far_ptr = (void *)&matrix_tmp_rot; field_type = SVG_Transform_Rotate_datatype; is_lsr_transform = 1; + break; + case LSR_UPDATE_TYPE_TRANSLATION: + info.far_ptr = (void *)&matrix_tmp; field_type = SVG_Transform_Translate_datatype; is_lsr_transform = 1; + break; + default: + fieldIndex = gf_lsr_anim_type_to_attribute(att_type); + gf_node_get_attribute_by_tag(n, fieldIndex, 1, 0, &info); + field_type = info.fieldType; + break; + } + if (is_lsr_transform) { + SVG_Transform *dest; + if (idx==-1) { + lsr_read_update_value(lsr, NULL, fieldIndex, field_type, info.far_ptr, 0); + } else { + assert(0); + } + + + fieldIndex = gf_node_get_attribute_by_tag((GF_Node*)n, TAG_SVG_ATT_transform, 1, 1, &info); + dest = (SVG_Transform *)info.far_ptr; + if (com_type) { + GF_Point2D scale, translate; + SVG_Point_Angle rotate; + if (gf_mx2d_decompose(&dest->mat, &scale, &rotate.angle, &translate)) { + gf_mx2d_init(dest->mat); + if (att_type==LSR_UPDATE_TYPE_SCALE) scale = matrix_tmp; + else if (att_type==LSR_UPDATE_TYPE_TRANSLATION) translate = matrix_tmp; + else if (att_type==LSR_UPDATE_TYPE_ROTATE) rotate = matrix_tmp_rot; + + gf_mx2d_add_scale(&dest->mat, scale.x, scale.y); + gf_mx2d_add_rotation(&dest->mat, 0, 0, rotate.angle); + gf_mx2d_add_translation(&dest->mat, translate.x, translate.y); + } + } + else if (att_type==LSR_UPDATE_TYPE_SCALE) gf_mx2d_add_scale(&dest->mat, matrix_tmp.x, matrix_tmp.y); + else if (att_type==LSR_UPDATE_TYPE_TRANSLATION) gf_mx2d_add_translation(&dest->mat, matrix_tmp.x, matrix_tmp.y); + else if (att_type==LSR_UPDATE_TYPE_ROTATE) gf_mx2d_add_rotation(&dest->mat, 0, 0, matrix_tmp_rot.angle); + + gf_node_changed((GF_Node*)n, &info); + } + else if (com_type) { + if (text_content) { + GF_DOMText *t = NULL; + if (idx>=0) { + if (com_type==LSR_UPDATE_INSERT) { + t = gf_dom_new_text_node(n->sgprivate->scenegraph); + gf_node_register((GF_Node *)t, n); + gf_node_list_insert_child(&((GF_ParentNode *)n)->children, (GF_Node*)t, idx); + } else { + t = (GF_DOMText *) gf_node_list_get_child(((SVG_Element*)n)->children, idx); + if (t->sgprivate->tag!=TAG_DOMText) t = NULL; + } + } else { + /*this is a replace, reset ALL node content*/ + gf_sg_parent_reset(n); + t = gf_dom_add_text_node(n, NULL); + } + lsr_read_byte_align_string(lsr, t ? &t->textContent : NULL, "textContent"); + gf_node_changed(n, NULL); + } else if (idx==-1) { + lsr_read_update_value(lsr, (GF_Node*)n, fieldIndex, field_type, info.far_ptr, n->sgprivate->tag); + } else { + SVG_Point *pt; + Fixed *v1, *v2; + //SMIL_Time *t; + void *prev; + void *tmp = lsr_read_update_value_indexed(lsr, (GF_Node*)n, field_type, info.far_ptr, idx, com_type==LSR_UPDATE_INSERT, 0); + switch (field_type) { + /*generic GF_List containers, no type translation needed*/ + case SMIL_KeyTimes_datatype/*ITYPE_keyTime*/: + case SMIL_KeySplines_datatype/*ITYPE_float*/: + case SVG_Points_datatype/*ITYPE_point*/: + case SMIL_Times_datatype/*ITYPE_smil_time*/: + if (com_type==LSR_UPDATE_INSERT) { + gf_list_insert(*(SVG_Coordinates*)info.far_ptr, tmp, idx); + } else { + prev = gf_list_get(*(SVG_Coordinates*)info.far_ptr, idx); + free(prev); + gf_list_rem(*(SVG_Coordinates*)info.far_ptr, idx); + gf_list_insert(*(SVG_Coordinates*)info.far_ptr, tmp, idx); + } + gf_node_changed((GF_Node*)n, NULL); + break; + /*list of floats - to check when implementing it...*/ + case SMIL_KeyPoints_datatype/*ITYPE_0to1 - keyPoints*/: + pt = (SVG_Point*)tmp; + v1 = (Fixed *) malloc(sizeof(Fixed)); + *v1 = pt->x; + v2 = (Fixed *) malloc(sizeof(Fixed)); + *v2 = pt->y; + free(pt); + + if (com_type==LSR_UPDATE_INSERT) { + gf_list_insert(*(SVG_Coordinates*)info.far_ptr, v1, idx); + gf_list_insert(*(SVG_Coordinates*)info.far_ptr, v2, idx+1); + } else { + prev = gf_list_get(*(SVG_Coordinates*)info.far_ptr, idx); + free(prev); + gf_list_rem(*(SVG_Coordinates*)info.far_ptr, idx); + prev = gf_list_get(*(SVG_Coordinates*)info.far_ptr, idx); + free(prev); + gf_list_rem(*(SVG_Coordinates*)info.far_ptr, idx); + gf_list_insert(*(SVG_Coordinates*)info.far_ptr, v1, idx); + gf_list_insert(*(SVG_Coordinates*)info.far_ptr, v2, idx); + } + gf_node_changed((GF_Node*)n, NULL); + break; + case SVG_ViewBox_datatype: + v1 = (Fixed*)tmp; + switch (idx) { + case 0: ((SVG_ViewBox*)info.far_ptr)->x = *v1; break; + case 1: ((SVG_ViewBox*)info.far_ptr)->y = *v1; break; + case 2: ((SVG_ViewBox*)info.far_ptr)->width = *v1; break; + case 3: ((SVG_ViewBox*)info.far_ptr)->height = *v1; break; + } + free(tmp); + gf_node_changed((GF_Node*)n, NULL); + break; + case SVG_StrokeDashArray_datatype: + v1 = (Fixed*)tmp; + if (com_type==LSR_UPDATE_INSERT) { + SVG_StrokeDashArray*da = (SVG_StrokeDashArray*)info.far_ptr; + /*use MFFloat for insert*/ + if (gf_sg_vrml_mf_insert(&da->array, GF_SG_VRML_MFFLOAT, (void*) &v2, idx)==GF_OK) + *v2 = *v1; + } else { + SVG_StrokeDashArray*da = (SVG_StrokeDashArray*)info.far_ptr; + if (idx<(s32)da->array.count) da->array.vals[idx] = *v1; + } + free(tmp); + gf_node_changed((GF_Node*)n, NULL); + break; + default: + free(tmp); + break; + } + } + } else { + GF_FieldInfo tmp; + tmp = info; + if (idx==-1) { + tmp.far_ptr = gf_svg_create_attribute_value(info.fieldType); + lsr_read_update_value(lsr, n, fieldIndex, field_type, tmp.far_ptr, n->sgprivate->tag); + } else { + tmp.far_ptr = lsr_read_update_value_indexed(lsr, n, field_type, NULL, idx, 0, 1); + } + gf_svg_attributes_add(&info, &tmp, &info, 0); + gf_svg_delete_attribute_value(info.fieldType, tmp.far_ptr, gf_node_get_graph(n)); + } + } + } + /*copy from node*/ + else if (operandNode && (op_att_type>=0)) { + u32 opFieldIndex = gf_lsr_anim_type_to_attribute(op_att_type); + if (com_list) { + com = gf_sg_command_new(lsr->sg, com_type ? GF_SG_LSR_REPLACE : GF_SG_LSR_ADD); + gf_list_add(com_list, com); + com->node = n; + gf_node_register(com->node, NULL); + com->fromNodeID = op_idref; + com->fromFieldIndex = opFieldIndex; + field = gf_sg_command_field_new(com); + field->pos = idx; + field->fieldIndex = gf_lsr_anim_type_to_attribute(att_type); + } else { + u32 fieldIndex; + GF_FieldInfo op_info; + fieldIndex = gf_lsr_anim_type_to_attribute(att_type); + gf_node_get_field(n, fieldIndex, &info); + gf_node_get_field(operandNode, opFieldIndex, &op_info); + if (com_type) { + gf_svg_attributes_copy(&info, &op_info, 0); + } else { + gf_svg_attributes_add(&info, &op_info, &info, 0); + } + } + } + + lsr_read_any_attribute(lsr, NULL, 1); + + /*if not add*/ + if (!com_type) return GF_OK; + + /*list replacement*/ + GF_LSR_READ_INT(lsr, type, 1, "opt_group"); + if (type) { + if (!com_type /*|| (idx!=-1) */) + return GF_NON_COMPLIANT_BITSTREAM; + + if (com_list) { + u32 count; + GF_ChildNodeItem *last = NULL; + + if (com_type==LSR_UPDATE_INSERT) count = 1; + else count = lsr_read_vluimsbf5(lsr, "count"); + + com = gf_sg_command_new(lsr->sg, (com_type==LSR_UPDATE_REPLACE) ? GF_SG_LSR_REPLACE : GF_SG_LSR_INSERT); + gf_list_add(com_list, com); + com->node = n; + gf_node_register(com->node, NULL); + field = gf_sg_command_field_new(com); + field->pos = idx; + + if (!count && (att_type==LSR_UPDATE_TYPE_TEXT_CONTENT)) { + field->fieldIndex = -1; + } else if (count==1) { + field->new_node = lsr_read_update_content_model(lsr, (SVG_Element *) n); + gf_node_register(field->new_node, n); + if (att_type>=0) field->fieldIndex = gf_lsr_anim_type_to_attribute(att_type); + } else { + field->field_ptr = &field->node_list; + while (count) { + GF_Node *new_node = lsr_read_update_content_model(lsr, (SVG_Element *) n); + gf_node_register(new_node, n); + gf_node_list_add_child_last(& field->node_list, new_node, &last); + count--; + } + } + } else { + SVG_Element*elt = (SVG_Element*)n; + GF_ChildNodeItem *last = NULL; + u32 count; + if (com_type==LSR_UPDATE_INSERT) count = 1; + else count = lsr_read_vluimsbf5(lsr, "count"); + + if (com_type==LSR_UPDATE_REPLACE) { + if (idx>=0) { + new_node = gf_node_list_del_child_idx(&elt->children, idx); + if (new_node) gf_node_unregister(new_node, n); + } else { + gf_node_unregister_children(n, elt->children); + elt->children = NULL; + } + } + if ((com_type==LSR_UPDATE_INSERT) || (gf_lsr_anim_type_to_attribute(att_type) == TAG_LSR_ATT_children)) { + while (count) { + new_node = lsr_read_update_content_model(lsr, elt); + if (new_node) { + if (idx>=0) { + gf_node_list_insert_child(&elt->children, new_node, idx); + } else { + gf_node_list_add_child_last(&elt->children, new_node, &last); + } + gf_node_register(new_node, n); + } + count--; + } + gf_node_changed(n, NULL); + } + /*node replacement*/ + else if ((att_type==-1) && (count==1)) { + new_node = lsr_read_update_content_model(lsr, elt); + gf_node_replace((GF_Node*)elt, new_node, 0); + } + } + } + return GF_OK; +} + +static GF_Err lsr_read_delete(GF_LASeRCodec *lsr, GF_List *com_list) +{ + GF_FieldInfo info; + s32 idx, att_type; + u32 type, idref; + + att_type = lsr_get_attribute_name(lsr); + + idx = -2; + GF_LSR_READ_INT(lsr, type, 1, "has_index"); + if (type) idx = (s32) lsr_read_vluimsbf5(lsr, "index"); + idref = lsr_read_codec_IDREF_command(lsr, "ref"); + + lsr_read_any_attribute(lsr, NULL, 1); + if (com_list) { + GF_Command *com; + GF_CommandField *field; + com = gf_sg_command_new(lsr->sg, GF_SG_LSR_DELETE); + gf_list_add(com_list, com); + com->node = gf_sg_find_node(lsr->sg, idref); + if (!com->node) { + com->RouteID = idref; + gf_list_add(lsr->unresolved_commands, com); + } else { + gf_node_register(com->node, NULL); + } + + if ((idx>=0) || (att_type>=0)) { + field = gf_sg_command_field_new(com); + field->pos = idx; + if (att_type>=0) { + field->fieldIndex = gf_lsr_anim_type_to_attribute(att_type); + gf_node_get_field(com->node, field->fieldIndex, &info); + field->fieldType = info.fieldType; + } + } + } else { + s32 fieldIndex = -1; + SVG_Element *elt = (SVG_Element *) gf_sg_find_node(lsr->sg, idref); + if (!elt) + return GF_NON_COMPLIANT_BITSTREAM; + + if (att_type>=0) { + fieldIndex = gf_lsr_anim_type_to_attribute(att_type); + gf_node_get_field((GF_Node*)elt, fieldIndex, &info); + /*TODO implement*/ + } + /*node deletion*/ + else if (idx>=0) { + GF_Node *c = (GF_Node *)gf_node_list_get_child(elt->children, idx); + if (c) { + if (!gf_node_list_del_child( & elt->children, c)) + return GF_IO_ERR; + gf_node_unregister(c, (GF_Node*)elt); + } + } else { + gf_node_replace((GF_Node*)elt, NULL, 0); + } + } + return GF_OK; +} + +static GF_Err lsr_read_send_event(GF_LASeRCodec *lsr, GF_List *com_list) +{ + u32 flag, idref; + s32 detail; + SVG_Number x, y; + XMLEV_Event event; + lsr_read_event_type(lsr, &event); + + detail = 0; + GF_LSR_READ_INT(lsr, flag, 1, "has_intvalue"); + if (flag) { + GF_LSR_READ_INT(lsr, flag, 1, "sign"); + detail = lsr_read_vluimsbf5(lsr, "value"); + if (flag) detail = -detail; + + switch (event.type) { + case GF_EVENT_KEYDOWN: + case GF_EVENT_LONGKEYPRESS: + case GF_EVENT_REPEAT_KEY: + case GF_EVENT_SHORT_ACCESSKEY: + detail = lsr_to_dom_key(detail); + break; + } + } + GF_LSR_READ_INT(lsr, flag, 1, "has_pointvalue"); + if (flag) { + lsr_read_coordinate(lsr, &x, 0, "x"); + lsr_read_coordinate(lsr, &y, 0, "y"); + } + idref = lsr_read_codec_IDREF_command(lsr, "idref"); + + GF_LSR_READ_INT(lsr, flag, 1, "has_pointvalue"); + if (flag) { + lsr_read_byte_align_string(lsr, NULL, "string");; + } + lsr_read_any_attribute(lsr, NULL, 1); + + if (!com_list) { + GF_DOM_Event evt; + GF_Node *target = gf_sg_find_node(lsr->sg, idref); + if (!target) return GF_OK; + + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = event.type; + evt.detail = detail ? detail : event.parameter; + evt.clientX = FIX2INT(x.value); + evt.clientY = FIX2INT(y.value); + gf_dom_event_fire(target, &evt); + + } else { + GF_Command *com = gf_sg_command_new(lsr->sg, GF_SG_LSR_SEND_EVENT); + gf_list_add(com_list, com); + com->node = gf_sg_find_node(lsr->sg, idref); + if (!com->node) { + com->RouteID = idref; + gf_list_add(lsr->unresolved_commands, com); + } else { + gf_node_register(com->node, NULL); + } + com->send_event_integer = detail ? detail : event.parameter; + com->send_event_name = event.type; + com->send_event_x = FIX2INT(x.value); + com->send_event_y = FIX2INT(y.value); + } + return GF_OK; +} + +static GF_Err lsr_read_save(GF_LASeRCodec *lsr, GF_List *com_list) +{ + u32 i, count; + count = lsr_read_vluimsbf5(lsr, "nbIds"); + for (i=0; isgprivate->tag!=TAG_DOMUpdates)) return; + assert(!codec->bs); + + codec->info = lsr_get_stream(codec, 0); + if (!codec->info) return; + codec->coord_bits = codec->info->cfg.coord_bits; + codec->scale_bits = codec->info->cfg.scale_bits_minus_coord_bits; + codec->time_resolution = codec->info->cfg.time_resolution; + codec->color_scale = (1<info->cfg.colorComponentBits) - 1; + if (codec->info->cfg.resolution >= 0) + codec->res_factor = INT2FIX(1<info->cfg.resolution); + else + codec->res_factor = gf_divfix(FIX_ONE, INT2FIX(1 << (-codec->info->cfg.resolution)) ); + + codec->bs = gf_bs_new(up->data, up->data_size, GF_BITSTREAM_READ); + codec->memory_dec = 0; + lsr_read_command_list(codec, NULL, NULL, 0); + gf_bs_del(codec->bs); + codec->bs = NULL; +} + +static GF_Err lsr_read_command_list(GF_LASeRCodec *lsr, GF_List *com_list, SVG_Element *cond, Bool first_imp) +{ + GF_Node *n; + GF_Command *com; + GF_Err e; + u32 i, type, count; + + if (cond) { + u32 s_len; + GF_DOMUpdates *up = gf_dom_add_updates_node((GF_Node*)cond); + gf_node_set_callback_function((GF_Node*)up, lsr_exec_command_list); + gf_node_set_private((GF_Node *) up, lsr); + + s_len = lsr_read_vluimsbf5(lsr, NULL/*"encoding-length" - don't log to avoid corrupting the log order!!*/); + gf_bs_align(lsr->bs); + /*not in memory mode, direct decode*/ + if (!lsr->memory_dec) { + com_list = NULL; + up->data_size = s_len; + up->data = (char*)malloc(sizeof(char)*s_len); + gf_bs_read_data(lsr->bs, up->data, s_len); + goto exit; + } + /*memory mode, decode commands*/ + else { + com_list = up->updates; + } + } + count = lsr_read_vluimsbf5(lsr, "occ0"); + if (first_imp) count++; + + for (i=0; ilast_error = GF_NON_COMPLIANT_BITSTREAM); + gf_node_register(n, NULL); + com = gf_sg_command_new(lsr->sg, (type==5) ? GF_SG_LSR_REFRESH_SCENE : GF_SG_LSR_NEW_SCENE); + com->node = n; + gf_list_add(com_list, com); + } else { + gf_sg_reset(lsr->sg); + gf_sg_set_scene_size_info(lsr->sg, 0, 0, 1); + n = lsr_read_svg(lsr, 1); + if (!n) + return (lsr->last_error = GF_NON_COMPLIANT_BITSTREAM); + } + break; + case LSR_UPDATE_TEXT_CONTENT: + lsr_read_byte_align_string(lsr, NULL, "textContent"); + break; + case LSR_UPDATE_SEND_EVENT: + e = lsr_read_send_event(lsr, com_list); + break; + case LSR_UPDATE_RESTORE: + e = lsr_read_restore(lsr, com_list); + break; + case LSR_UPDATE_SAVE: + e = lsr_read_save(lsr, com_list); + break; + case LSR_UPDATE_EXTEND: + { + u32 extID, len; + GF_Node *n; + GF_LSR_READ_INT(lsr, extID, lsr->info->cfg.extensionIDBits, "extensionID"); + len = lsr_read_vluimsbf5(lsr, "len"); + if (extID==2) { + u32 j, sub_count; + lsr_read_vluimsbf5(lsr, "reserved"); + sub_count = lsr_read_vluimsbf5(lsr, "occ2"); + for (j=0; jsg, idref); + if (com_list) { + com = gf_sg_command_new(lsr->sg, (sub_type==1) ? GF_SG_LSR_ACTIVATE : GF_SG_LSR_DEACTIVATE); + if (n) { + com->node = n; + gf_node_register(n, NULL); + } else { + com->RouteID = idref; + } + gf_list_add(com_list, com); + } else if (sub_type==1) { + if (!n) return GF_NON_COMPLIANT_BITSTREAM; + gf_node_activate(n); + } else { + if (!n) return GF_NON_COMPLIANT_BITSTREAM; + gf_node_deactivate(n); + } + break; + default: + lsr_read_private_element_container(lsr); + break; + } + } + } + } + } + break; + default: + return (lsr->last_error = GF_NON_COMPLIANT_BITSTREAM); + } + /*same-coding scope is command-based (to check in the spec)*/ + if (cond) { + lsr->prev_g = NULL; + lsr->prev_line = NULL; + lsr->prev_path = NULL; + lsr->prev_polygon = NULL; + lsr->prev_rect = NULL; + lsr->prev_text = NULL; + lsr->prev_use = NULL; + } + + if (lsr->last_error) + return lsr->last_error; + } +exit: + /*script is align*/ + if (cond) { + gf_bs_align(lsr->bs); + lsr_read_object_content(lsr, (SVG_Element*)cond); + lsr->prev_g = NULL; + lsr->prev_line = NULL; + lsr->prev_path = NULL; + lsr->prev_polygon = NULL; + lsr->prev_rect = NULL; + lsr->prev_text = NULL; + lsr->prev_use = NULL; + } + return GF_OK; +} + +static GF_Err lsr_decode_laser_unit(GF_LASeRCodec *lsr, GF_List *com_list) +{ + GF_Err e; + Bool reset_encoding_context; + u32 flag, i, count = 0, privateDataIdentifierIndexBits; + + lsr->last_error = GF_OK; + + /* + * 1 - laser unit header + */ + GF_LSR_READ_INT(lsr, reset_encoding_context, 1, "resetEncodingContext"); + GF_LSR_READ_INT(lsr, flag, 1, "opt_group"); + if (flag) lsr_read_extension(lsr, "ext"); + + /*clean all tables*/ + if (reset_encoding_context) { + lsr->nb_cols = 0; + if (lsr->col_table) free(lsr->col_table); + lsr->col_table = NULL; + while (gf_list_count(lsr->font_table)) { + char *ft = (char *)gf_list_last(lsr->font_table); + free(ft); + gf_list_rem_last(lsr->font_table); + } + lsr->privateData_id_index = lsr->privateTag_index = 0; + } + + /* + * 2 - codecInitialisations + */ + + /* + * 2.a - condecInitialization.color + */ + GF_LSR_READ_INT(lsr, flag, 1, "colorInitialisation"); + count = 0; + if (flag) { + count = lsr_read_vluimsbf5(lsr, "count"); + lsr->col_table = (LSRCol*)realloc(lsr->col_table, sizeof(LSRCol)*(lsr->nb_cols+count)); + for (i=0; iinfo->cfg.colorComponentBits, "red"); + GF_LSR_READ_INT(lsr, c.g, lsr->info->cfg.colorComponentBits, "green"); + GF_LSR_READ_INT(lsr, c.b, lsr->info->cfg.colorComponentBits, "blue"); + lsr->col_table[lsr->nb_cols+i] = c; + } + lsr->nb_cols += count; + } + lsr->colorIndexBits = gf_get_bit_size(lsr->nb_cols); + /* + * 2.b - condecInitialization.fonts + */ + GF_LSR_READ_INT(lsr, flag, 1, "fontInitialisation"); + count = 0; + if (flag) { + count = lsr_read_vluimsbf5(lsr, "count"); + for (i=0; ifont_table, ft); + } + } + lsr->fontIndexBits = gf_get_bit_size(count); + /* + * 2.c - condecInitialization.private + */ + GF_LSR_READ_INT(lsr, flag, 1, "privateDataIdentifierInitialisation"); + count = 0; + if (flag) { + count = lsr_read_vluimsbf5(lsr, "nbPrivateDataIdentifiers"); + for (i=0; iprivateData_id_index++; + lsr_read_byte_align_string(lsr, NULL, "privateDataIdentifier"); + } + } + /* + * 2.d - condecInitialization.anyXML + */ + GF_LSR_READ_INT(lsr, flag, 1, "anyXMLInitialisation"); + count = 0; + if (flag) { + privateDataIdentifierIndexBits = gf_get_bit_size(lsr->privateData_id_index); + count = lsr_read_vluimsbf5(lsr, "nbTags"); + for (i=0; iprivateTag_index++; + if (i) { + /* uint(privateDataIdentifierIndexBits) = */ + GF_LSR_READ_INT(lsr, flag, privateDataIdentifierIndexBits, "privateDataIdentifierIndex"); + lsr_read_byte_align_string(lsr, NULL, "tag"); + } + GF_LSR_READ_INT(lsr, flag, 1, "hasAttrs"); + if (flag) { + u32 k, c2 = lsr_read_vluimsbf5(lsr, "nbAttrNames"); + for (k=0; kbs); + + count = lsr_read_vluimsbf5(lsr, "len"); + for (i=0; ibs) - pos; + assert(len>=pos); + GF_LSR_READ_INT(lsr, flag, pos, "remainingData"); + } + + e = lsr_read_command_list(lsr, com_list, NULL, 1); + GF_LSR_READ_INT(lsr, flag, 1, "opt_group"); + if (flag) lsr_read_extension(lsr, "ext"); + return e; +} + +#endif /*GPAC_DISABLE_SVG*/ diff --git a/src/laser/lsr_enc.c b/src/laser/lsr_enc.c new file mode 100644 index 0000000..06fba27 --- /dev/null +++ b/src/laser/lsr_enc.c @@ -0,0 +1,3944 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / LASeR codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include + +#ifndef GPAC_DISABLE_SVG + + +#include + + +#define GF_LSR_WRITE_INT(_codec, _val, _nbBits, _str) {\ + gf_bs_write_int(_codec->bs, _val, _nbBits); \ + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] %s\t\t%d\t\t%d\n", _str, _nbBits, _val)); \ + }\ + +static void lsr_write_group_content(GF_LASeRCodec *lsr, SVG_Element *elt, Bool skip_object_content); +static GF_Err lsr_write_command_list(GF_LASeRCodec *lsr, GF_List *comList, SVG_Element *script, Bool first_implicit); +static GF_Err lsr_write_laser_unit(GF_LASeRCodec *lsr, GF_List *com_list, Bool reset_encoding_context); +static void lsr_write_point_sequence(GF_LASeRCodec *lsr, GF_List **pts, const char *name); +static void lsr_write_path_type(GF_LASeRCodec *lsr, SVG_PathData *path, const char *name); + +GF_LASeRCodec *gf_laser_encoder_new(GF_SceneGraph *graph) +{ + GF_LASeRCodec *tmp; + GF_SAFEALLOC(tmp, GF_LASeRCodec); + if (!tmp) return NULL; + tmp->streamInfo = gf_list_new(); + tmp->font_table = gf_list_new(); + tmp->sg = graph; + return tmp; +} + +void gf_laser_encoder_del(GF_LASeRCodec *codec) +{ + /*destroy all config*/ + while (gf_list_count(codec->streamInfo)) { + LASeRStreamInfo *p = (LASeRStreamInfo *)gf_list_last(codec->streamInfo); + free(p); + gf_list_rem_last(codec->streamInfo); + } + gf_list_del(codec->streamInfo); + if (codec->col_table) free(codec->col_table); + while (gf_list_count(codec->font_table)) { + char *ft = (char *)gf_list_last(codec->font_table); + free(ft); + gf_list_rem_last(codec->font_table); + } + gf_list_del(codec->font_table); + free(codec); +} + + +static LASeRStreamInfo *lsr_get_stream(GF_LASeRCodec *codec, u16 ESID) +{ + LASeRStreamInfo *ptr; + u32 i = 0; + while ((ptr = (LASeRStreamInfo *)gf_list_enum(codec->streamInfo, &i))) { + if(ptr->ESID==ESID) return ptr; + } + return NULL; +} + + +GF_Err gf_laser_encoder_new_stream(GF_LASeRCodec *codec, u16 ESID, GF_LASERConfig *cfg) +{ + LASeRStreamInfo *pInfo; + if (lsr_get_stream(codec, ESID) != NULL) return GF_BAD_PARAM; + GF_SAFEALLOC(pInfo, LASeRStreamInfo); + pInfo->ESID = ESID; + memcpy(&pInfo->cfg, cfg, sizeof(GF_LASERConfig)); + if (!pInfo->cfg.time_resolution) pInfo->cfg.time_resolution = 1000; + if (!pInfo->cfg.colorComponentBits) pInfo->cfg.colorComponentBits = 8; + if (!pInfo->cfg.coord_bits) pInfo->cfg.coord_bits = 12; + if (pInfo->cfg.resolution<-8) pInfo->cfg.resolution = (s8) -8; + else if (pInfo->cfg.resolution>7) pInfo->cfg.resolution=7; + + gf_list_add(codec->streamInfo, pInfo); + return GF_OK; +} + +GF_Err gf_laser_encoder_get_config(GF_LASeRCodec *codec, u16 ESID, char **out_data, u32 *out_data_length) +{ + GF_BitStream *bs; + if (!codec || !out_data || !out_data_length) return GF_BAD_PARAM; + + codec->info = lsr_get_stream(codec, ESID); + if (!codec->info) return GF_BAD_PARAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, codec->info->cfg.profile, 8); + gf_bs_write_int(bs, codec->info->cfg.level, 8); + gf_bs_write_int(bs, 0 /*codec->info->cfg.reserved*/, 3); + gf_bs_write_int(bs, codec->info->cfg.pointsCodec, 2); + gf_bs_write_int(bs, codec->info->cfg.pathComponents, 4); + gf_bs_write_int(bs, codec->info->cfg.fullRequestHost, 1); + if (codec->info->cfg.time_resolution != 1000) { + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, codec->info->cfg.time_resolution , 16); + } else { + gf_bs_write_int(bs, 0, 1); + } + gf_bs_write_int(bs, codec->info->cfg.colorComponentBits - 1, 4); + if (codec->info->cfg.resolution<0) + gf_bs_write_int(bs, 16 + codec->info->cfg.resolution, 4); + else + gf_bs_write_int(bs, codec->info->cfg.resolution, 4); + gf_bs_write_int(bs, codec->info->cfg.coord_bits, 5); + gf_bs_write_int(bs, codec->info->cfg.scale_bits_minus_coord_bits, 4); + gf_bs_write_int(bs, codec->info->cfg.newSceneIndicator ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 3); + gf_bs_write_int(bs, codec->info->cfg.extensionIDBits, 4); + /*no extConfig*/ + gf_bs_write_int(bs, 0, 1); + /*no extensions*/ + gf_bs_write_int(bs, 0, 1); + gf_bs_align(bs); + gf_bs_get_content(bs, out_data, out_data_length); + gf_bs_del(bs); + return GF_OK; +} + + +GF_Err gf_laser_encode_au(GF_LASeRCodec *codec, u16 ESID, GF_List *command_list, Bool reset_context, char **out_data, u32 *out_data_length) +{ + GF_Err e; + if (!codec || !command_list || !out_data || !out_data_length) return GF_BAD_PARAM; + + codec->info = lsr_get_stream(codec, ESID); + if (!codec->info) return GF_BAD_PARAM; + codec->coord_bits = codec->info->cfg.coord_bits; + codec->scale_bits = codec->info->cfg.scale_bits_minus_coord_bits; + codec->time_resolution = codec->info->cfg.time_resolution; + codec->color_scale = (1<info->cfg.colorComponentBits) - 1; + if (codec->info->cfg.resolution>=0) + codec->res_factor = gf_divfix(FIX_ONE, INT2FIX(1<info->cfg.resolution) ); + else + codec->res_factor = INT2FIX(1 << (-codec->info->cfg.resolution)); + + codec->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + e = lsr_write_laser_unit(codec, command_list, reset_context); + if (!e) { + gf_bs_align(codec->bs); + gf_bs_get_content(codec->bs, out_data, out_data_length); + } + gf_bs_del(codec->bs); + codec->bs = NULL; + return e; +} + +GF_Err gf_laser_encoder_get_rap(GF_LASeRCodec *codec, char **out_data, u32 *out_data_length) +{ + GF_Err e; + if (!codec->info) codec->info = (LASeRStreamInfo*)gf_list_get(codec->streamInfo, 0); + codec->coord_bits = codec->info->cfg.coord_bits; + codec->scale_bits = codec->info->cfg.scale_bits_minus_coord_bits; + codec->time_resolution = codec->info->cfg.time_resolution; + codec->color_scale = (1<info->cfg.colorComponentBits) - 1; + if (codec->info->cfg.resolution>=0) + codec->res_factor = gf_divfix(FIX_ONE, INT2FIX(1<info->cfg.resolution) ); + else + codec->res_factor = INT2FIX(1 << (-codec->info->cfg.resolution)); + + codec->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + e = lsr_write_laser_unit(codec, NULL, 0); + if (e == GF_OK) gf_bs_get_content(codec->bs, out_data, out_data_length); + gf_bs_del(codec->bs); + codec->bs = NULL; + return e; +} + +static void lsr_write_vluimsbf5(GF_LASeRCodec *lsr, u32 val, const char *name) +{ + u32 nb_words; + u32 nb_bits = val ? gf_get_bit_size(val) : 1; + nb_words = nb_bits / 4; + if (nb_bits%4) nb_words++; + assert(nb_words * 4 >= nb_bits); + nb_bits = 4*nb_words; + while (nb_words) { + nb_words--; + gf_bs_write_int(lsr->bs, nb_words ? 1 : 0, 1); + } + gf_bs_write_int(lsr->bs, val, nb_bits); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] %s\t\t%d\t\t%d\n", name, nb_bits + (nb_bits/4), val)); +} + +static void lsr_write_vluimsbf5_ex(GF_LASeRCodec *lsr, u32 val, u32 extra_words, const char *name) +{ + u32 nb_words; + u32 nb_bits = val ? gf_get_bit_size(val) : 1; + nb_words = nb_bits / 4; + if (nb_bits%4) nb_words++; + nb_words += extra_words; + + assert(nb_words * 4 >= nb_bits); + nb_bits = 4*nb_words; + while (nb_words) { + nb_words--; + gf_bs_write_int(lsr->bs, nb_words ? 1 : 0, 1); + } + gf_bs_write_int(lsr->bs, val, nb_bits); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] %s\t\t%d\t\t%d\n", name, nb_bits + (nb_bits/4), val)); +} + +static u32 lsr_get_vluimsbf5_size(u32 val, u32 extra_words) +{ + u32 nb_words; + u32 nb_bits = val ? gf_get_bit_size(val) : 1; + nb_words = nb_bits / 4; + if (nb_bits%4) nb_words++; + nb_words += extra_words; + return 4*nb_words + nb_words; +} + +static void lsr_write_vluimsbf8(GF_LASeRCodec *lsr, u32 val, const char *name) +{ + u32 nb_words; + u32 nb_tot, nb_bits = val ? gf_get_bit_size(val) : 1; + nb_words = nb_bits / 7; + if (nb_bits%7) nb_words++; + assert(nb_words * 7 >= nb_bits); + nb_bits = nb_words * 7; + nb_tot = nb_words+nb_bits; + while (nb_words) { + nb_words--; + gf_bs_write_int(lsr->bs, nb_words ? 1 : 0, 1); + } + gf_bs_write_int(lsr->bs, val, nb_bits); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] %s\t\t%d\t\t%d\n", name, nb_tot, val)); +} + +static void lsr_write_extension(GF_LASeRCodec *lsr, char *data, u32 len, const char *name) +{ + if (!len) len = strlen(name); + lsr_write_vluimsbf5(lsr, len, name); + gf_bs_write_data(lsr->bs, data, len); +} + +static void lsr_write_extend_class(GF_LASeRCodec *lsr, char *data, u32 len, const char *name) +{ + u32 i=0; + GF_LSR_WRITE_INT(lsr, 0, lsr->info->cfg.extensionIDBits, "reserved"); + lsr_write_vluimsbf5(lsr, len, "len"); + while (ibs, data[i], 8); + i++; + } +} + +static void lsr_write_codec_IDREF(GF_LASeRCodec *lsr, XMLRI *href, const char *name) +{ + u32 nID = 0; + if (href && href->target) nID = gf_node_get_id((GF_Node *)href->target); + else if (name[0]=='#') { + GF_Node *n = gf_sg_find_node_by_name(lsr->sg, (char *) name + 1); + if (n) nID = gf_node_get_id((GF_Node *)href->target); + } + else nID = 1+href->lsr_stream_id; + + assert(nID); + + lsr_write_vluimsbf5(lsr, nID-1, name); + GF_LSR_WRITE_INT(lsr, 0, 1, "reserved"); +} + +static void lsr_write_codec_IDREF_Node(GF_LASeRCodec *lsr, GF_Node *href, const char *name) +{ + u32 nID = gf_node_get_id(href); + assert(nID); + lsr_write_vluimsbf5(lsr, nID-1, name); + GF_LSR_WRITE_INT(lsr, 0, 1, "reserved"); +} + +static u32 lsr_get_IDREF_nb_bits(GF_LASeRCodec *lsr, GF_Node *href) +{ + u32 nb_bits, nb_words, nID; + + nID = gf_node_get_id(href); + assert(nID); + + nb_bits = nID ? gf_get_bit_size(nID) : 1; + nb_words = nb_bits / 4; + if (nb_bits%4) nb_words++; + assert(nb_words * 4 >= nb_bits); + nb_bits = nb_words * 4; + return nb_words+nb_bits /*IDREF part*/ + 1 /*reserevd bit*/; +} + +static void lsr_write_fixed_16_8(GF_LASeRCodec *lsr, Fixed fix, const char *name) +{ + u32 val; + if (fix<0) { +#ifdef GPAC_FIXED_POINT + val = (1<<24) + fix / 256; +#else + val = (1<<24) + FIX2INT(fix * 256); +#endif + } else { +#ifdef GPAC_FIXED_POINT + val = fix/256; +#else + val = FIX2INT(fix*256); +#endif + } + val &= 0x00FFFFFF; + GF_LSR_WRITE_INT(lsr, val, 24, name); +} +static void lsr_write_fixed_16_8i(GF_LASeRCodec *lsr, SVG_Number *n, const char *name) +{ + if (n->type==SVG_NUMBER_INHERIT) { + GF_LSR_WRITE_INT(lsr, 1, 1, name); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + lsr_write_fixed_16_8(lsr, n->value, name); + } +} + +static s32 lsr_get_font_index(GF_LASeRCodec *lsr, SVG_FontFamily *font) +{ + u32 i, count; + if ((font->type!=SVG_FONTFAMILY_VALUE) || !font->value) return -1; + count = gf_list_count(lsr->font_table); + for (i=0; ifont_table, i); + if (!strcmp(n, font->value)) return (s32) i; + } + return -2; +} + +static s32 lsr_get_col_index(GF_LASeRCodec *lsr, SVG_Color *color) +{ + u16 r, g, b; + u32 i; + if (color->type!=SVG_COLOR_RGBCOLOR) return -1; + r = FIX2INT(color->red*lsr->color_scale); + g = FIX2INT(color->green*lsr->color_scale); + b = FIX2INT(color->blue*lsr->color_scale); + for (i=0; inb_cols; i++) { + LSRCol *c = &lsr->col_table[i]; + if ((c->r == r) && (c->g == g) && (c->b == b)) return (s32) i; + } + return -2; +} + +static void lsr_write_line_increment_type(GF_LASeRCodec *lsr, SVG_Number *li, const char *name) +{ + if (li->type==SVG_NUMBER_INHERIT) { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + GF_LSR_WRITE_INT(lsr, 1, 1, "inherit"); + } else if (li->type==SVG_NUMBER_AUTO) { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + GF_LSR_WRITE_INT(lsr, 0, 1, "auto"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + lsr_write_fixed_16_8(lsr, li->value, "line-increment-value"); + } +} + +static void lsr_write_byte_align_string(GF_LASeRCodec *lsr, char *str, const char *name) +{ + u32 len = str ? strlen(str) : 0; + gf_bs_align(lsr->bs); + lsr_write_vluimsbf8(lsr, len, "len"); + if (len) gf_bs_write_data(lsr->bs, str, len); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] %s\t\t%d\t\t%s\n", name, 8*len, str ? str : "")); +} +static void lsr_write_byte_align_string_list(GF_LASeRCodec *lsr, GF_List *l, const char *name, Bool is_iri) +{ + char text[4096]; + u32 i, count = gf_list_count(l); + text[0] = 0; + for (i=0; istring; + } else { + str = (char*)gf_list_get(l, i); + } + strcat(text, str); + if (i+1type==XMLRI_STRING) { + is_iri = 1; + if (iri->string[0]=='#') { + iri->target = (SVG_Element*)gf_sg_find_node_by_name(lsr->sg, iri->string+1); + if (iri->target) { + is_iri = 0; + iri->type = XMLRI_ELEMENTID; + } + } + } + + GF_LSR_WRITE_INT(lsr, is_iri, 1, "hasUri"); + if (is_iri) { + if (!iri->string || strnicmp(iri->string, "data:", 5)) { + lsr_write_byte_align_string(lsr, iri->string, "uri"); + GF_LSR_WRITE_INT(lsr, 0, 1, "hasData"); + } else { + u32 len; + char *sep = strchr(iri->string, ','); + sep[0] = 0; + lsr_write_byte_align_string(lsr, iri->string, "uri"); + sep[0] = ','; + len = strlen(sep+1); + GF_LSR_WRITE_INT(lsr, 1, 1, "hasData"); + lsr_write_vluimsbf5(lsr, len, "len"); + gf_bs_write_data(lsr->bs, sep+1, len); + } + } + GF_LSR_WRITE_INT(lsr, (iri->type==XMLRI_ELEMENTID) ? 1 : 0, 1, "hasID"); + if (iri->type==XMLRI_ELEMENTID) lsr_write_codec_IDREF(lsr, iri, "idref"); + + GF_LSR_WRITE_INT(lsr, (iri->type==XMLRI_STREAMID) ? 1 : 0, 1, "hasID"); + if (iri->type==XMLRI_STREAMID) + lsr_write_codec_IDREF(lsr, iri, "ref"); +} + +static void lsr_write_paint(GF_LASeRCodec *lsr, SVG_Paint *paint, const char *name) +{ + if ((paint->type==SVG_PAINT_COLOR) && (paint->color.type==SVG_COLOR_RGBCOLOR)) { + s32 idx; + GF_LSR_WRITE_INT(lsr, 1, 1, "hasIndex"); + idx = lsr_get_col_index(lsr, &paint->color); + if (idx<0) { + idx = 0; + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[LASeR] color not in colorTable\n")); + } + GF_LSR_WRITE_INT(lsr, (u32) idx, lsr->colorIndexBits, name); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasIndex"); + + switch (paint->type) { + case SVG_PAINT_INHERIT: + GF_LSR_WRITE_INT(lsr, 0, 2, "enum"); + GF_LSR_WRITE_INT(lsr, 0, 2, "choice"); + break; + case SVG_PAINT_NONE: + GF_LSR_WRITE_INT(lsr, 0, 2, "enum"); + GF_LSR_WRITE_INT(lsr, 2, 2, "choice"); + break; + case SVG_PAINT_COLOR: + if (paint->color.type == SVG_COLOR_CURRENTCOLOR) { + GF_LSR_WRITE_INT(lsr, 0, 2, "enum"); + GF_LSR_WRITE_INT(lsr, 1, 2, "choice"); + } else { + GF_LSR_WRITE_INT(lsr, 2, 2, "enum"); + lsr_write_byte_align_string(lsr, (char*)gf_svg_get_system_paint_server_name(paint->color.type), "systemsPaint"); + } + break; + case SVG_PAINT_URI: + GF_LSR_WRITE_INT(lsr, 1, 2, "enum"); + lsr_write_any_uri(lsr, &paint->iri, "uri"); + break; + + default: + GF_LSR_WRITE_INT(lsr, 3, 2, "enum"); + lsr_write_extension(lsr, "ERROR", 5, "colorExType0"); + break; + } + } +} + +static void lsr_write_private_element_container(GF_LASeRCodec *lsr) +{ + /*NO PRIVATE DATA ON ENCODING YET*/ + assert(0); +} + +static void lsr_write_private_att_class(GF_LASeRCodec *lsr) +{ + /*NO PRIVATE DATA ON ENCODING YET*/ + assert(0); +} + +static void lsr_write_private_attr_container(GF_LASeRCodec *lsr, u32 index, const char *name) +{ + assert(0); +} + +static void lsr_write_any_attribute(GF_LASeRCodec *lsr, SVG_Element *node, Bool skippable) +{ + if (1) { + if (skippable) GF_LSR_WRITE_INT(lsr, 0, 1, "has_attrs"); + } else { + if (skippable) GF_LSR_WRITE_INT(lsr, 1, 1, "has_attrs"); +/* + do () { + GF_LSR_WRITE_INT(lsr, 0, lsr->info->cfg.extensionIDBits, "reserved"); + lsr_write_vluimsbf5(lsr, 0, "len");//len in BITS + GF_LSR_WRITE_INT(lsr, 0, 0, "reserved_val"); + } while () +*/ + } +} + +static void lsr_write_private_attributes(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + if (1) { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_private_attr"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_private_attr"); + lsr_write_private_att_class(lsr); + } +} +static void lsr_write_string_attribute(GF_LASeRCodec *lsr, char *class_attr, char *name) +{ + if (class_attr) { + GF_LSR_WRITE_INT(lsr, 1, 1, name); + lsr_write_byte_align_string(lsr, class_attr, name); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + } +} +static void lsr_write_id(GF_LASeRCodec *lsr, GF_Node *n) +{ + u32 id = gf_node_get_id(n); + if (id) { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_id"); + lsr_write_vluimsbf5(lsr, id-1, "ID"); +#if TODO_LASER_EXTENSIONS + if (0) { + GF_LSR_WRITE_INT(lsr, 1, 1, "reserved"); + lsr_write_vluimsbf5(lsr, reserved_len, "len"); + GF_LSR_WRITE_INT(lsr, 0, reserved_len, "reserved"); + } else +#endif + GF_LSR_WRITE_INT(lsr, 0, 1, "reserved"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_id"); + } +} + +static u32 lsr_translate_coords(GF_LASeRCodec *lsr, Fixed x, u32 nb_bits) +{ + s32 res, max; + + res = FIX2INT( gf_divfix(x, lsr->res_factor) ); + /*don't loose too much*/ + if (!res && x) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] resolution factor %g too small to allow coding of %g - adjusting to smallest integer!\n", lsr->res_factor, FIX2FLT(x) )); + res = (x>0) ? 1 : -1; + } + max = (1<<(nb_bits-1)) - 1; + if (res>=0) { + if (res > max) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] nb_bits %d not large enough to encode positive number %g!\n", nb_bits, FIX2FLT(x) )); + res = max; + } + assert( ! (res & (1<<(nb_bits-1)) )); + return (u32) res; + } + res += 1<<(nb_bits); + if (res<=max){ + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] nb_bits %d not large enough to encode negative number %g!\n", nb_bits, FIX2FLT(x) )); + res = max+1; + } + assert( res & (1<<(nb_bits-1)) ); + return res; +} + +static u32 lsr_translate_scale(GF_LASeRCodec *lsr, Fixed v) +{ + s32 res; + /*always 8 bits for fractional part*/ + if (ABS(v) * 256 < 1) v = 0; + v = v*256; + if (v<0) { + res = FIX2INT(v) + (1<coord_bits); + if (res<0) GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[LASeR] nb_bits %d not large enough to encode negative number %d!\n", lsr->coord_bits, res)); + return res; + } + res = FIX2INT(v); + if (res & (1<<(lsr->coord_bits-1)) ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[LASeR] nb_bits %d not large enough to encode positive number %d!\n", lsr->coord_bits, res)); + } + return res; +} +static void lsr_write_matrix(GF_LASeRCodec *lsr, SVG_Transform *mx) +{ + u32 res; + if (mx->is_ref) { + GF_LSR_WRITE_INT(lsr, 1, 1, "isNotMatrix"); + GF_LSR_WRITE_INT(lsr, 1, 1, "isRef"); + GF_LSR_WRITE_INT(lsr, 1, 1, "hasXY"); + lsr_write_fixed_16_8(lsr, mx->mat.m[2], "valueX"); + lsr_write_fixed_16_8(lsr, mx->mat.m[5], "valueY"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "isNotMatrix"); + lsr->coord_bits += lsr->scale_bits; + + if ((mx->mat.m[0]!=FIX_ONE) || (mx->mat.m[4]!=FIX_ONE)) { + GF_LSR_WRITE_INT(lsr, 1, 1, "xx_yy_present"); + res = lsr_translate_scale(lsr, mx->mat.m[0]); + GF_LSR_WRITE_INT(lsr, res, lsr->coord_bits, "xx"); + res = lsr_translate_scale(lsr, mx->mat.m[4]); + GF_LSR_WRITE_INT(lsr, res, lsr->coord_bits, "yy"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "xx_yy_present"); + } + if (mx->mat.m[1] || mx->mat.m[3]) { + GF_LSR_WRITE_INT(lsr, 1, 1, "xy_yx_present"); + res = lsr_translate_scale(lsr, mx->mat.m[1]); + GF_LSR_WRITE_INT(lsr, res, lsr->coord_bits, "xy"); + res = lsr_translate_scale(lsr, mx->mat.m[3]); + GF_LSR_WRITE_INT(lsr, res, lsr->coord_bits, "yx"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "xy_yx_present"); + } + + if (mx->mat.m[2] || mx->mat.m[5]) { + GF_LSR_WRITE_INT(lsr, 1, 1, "xz_yz_present"); + res = lsr_translate_coords(lsr, mx->mat.m[2], lsr->coord_bits); + GF_LSR_WRITE_INT(lsr, res, lsr->coord_bits, "xz"); + res = lsr_translate_coords(lsr, mx->mat.m[5], lsr->coord_bits); + GF_LSR_WRITE_INT(lsr, res, lsr->coord_bits, "yz"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "xz_yz_present"); + } + lsr->coord_bits -= lsr->scale_bits; + } +} + +static void lsr_write_fixed_clamp(GF_LASeRCodec *lsr, Fixed f, const char *name) +{ +#ifdef GPAC_FIXED_POINT + s32 val = f >> 8; +#else + s32 val = (u32) (255 * f); +#endif + if (val<0) val = 0; + else if (val>255) val = 255; + GF_LSR_WRITE_INT(lsr, (u32) val, 8, name); +} + +u32 dom_to_lsr_key(u32 dom_k) +{ + switch (dom_k) { + case GF_KEY_STAR: return 0; + case GF_KEY_0: return 1; + case GF_KEY_1: return 2; + case GF_KEY_2: return 3; + case GF_KEY_3: return 4; + case GF_KEY_4: return 5; + case GF_KEY_5: return 6; + case GF_KEY_6: return 7; + case GF_KEY_7: return 8; + case GF_KEY_8: return 9; + case GF_KEY_9: return 10; + case GF_KEY_DOWN: return 12; + case GF_KEY_LEFT: return 14; + case GF_KEY_RIGHT: return 16; + case GF_KEY_UP: return 20; + /*WHAT IS ANY_KEY (11) ??*/ + case GF_KEY_ENTER: + case GF_KEY_EXECUTE: + return 13; + case GF_KEY_ESCAPE: + return 15; + case GF_KEY_NUMBER: + return 17; + case GF_KEY_CELL_SOFT1: + return 18; + case GF_KEY_CELL_SOFT2: + return 19; + default: + return 100; + } +} +static void lsr_write_event_type(GF_LASeRCodec *lsr, u32 evtType, u32 evtParam) +{ + Bool force_string = 0; + switch (evtType) { + case GF_EVENT_KEYDOWN: + case GF_EVENT_LONGKEYPRESS: + case GF_EVENT_REPEAT_KEY: + case GF_EVENT_SHORT_ACCESSKEY: + if (dom_to_lsr_key(evtParam)==100) force_string = 2; + break; + case GF_EVENT_BEGIN: + case GF_EVENT_END: + force_string = 1; + break; + case GF_EVENT_REPEAT: + force_string = 1; + break; + } + + if (force_string) { + char szName[1024]; + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + if (evtParam) { + if (force_string==2) { + sprintf(szName, "%s(%s)", gf_dom_event_get_name(evtType), gf_dom_get_key_name(evtParam) ); + } else { + sprintf(szName, "%s(%d)", gf_dom_event_get_name(evtType), evtParam); + } + } else { + sprintf(szName, "%s", gf_dom_event_get_name(evtType)); + } + lsr_write_byte_align_string(lsr, szName, "evtString"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + switch (evtType) { + case GF_EVENT_ABORT: + GF_LSR_WRITE_INT(lsr, LSR_EVT_abort, 6, "event"); break; + case GF_EVENT_ACTIVATE: + GF_LSR_WRITE_INT(lsr, LSR_EVT_activate, 6, "event"); break; + case GF_EVENT_ACTIVATED: + GF_LSR_WRITE_INT(lsr, LSR_EVT_activatedEvent, 6, "event"); break; + case GF_EVENT_BEGIN:/*SPEC IS BROKEN, CANNOT ENCODE elt.begin !! */ + case GF_EVENT_BEGIN_EVENT: + GF_LSR_WRITE_INT(lsr, LSR_EVT_beginEvent, 6, "event"); break; + case GF_EVENT_CLICK: + GF_LSR_WRITE_INT(lsr, LSR_EVT_click, 6, "event"); break; + case GF_EVENT_DEACTIVATED: + GF_LSR_WRITE_INT(lsr, LSR_EVT_deactivatedEvent, 6, "event"); break; + case GF_EVENT_END:/*SPEC IS BROKEN, CANNOT ENCODE elt.end !! */ + case GF_EVENT_END_EVENT: + GF_LSR_WRITE_INT(lsr, LSR_EVT_endEvent, 6, "event"); break; + case GF_EVENT_ERROR: + GF_LSR_WRITE_INT(lsr, LSR_EVT_error, 6, "event"); break; + case GF_EVENT_EXECUTION_TIME: + GF_LSR_WRITE_INT(lsr, LSR_EVT_executionTime, 6, "event"); break; + case GF_EVENT_FOCUSIN: + GF_LSR_WRITE_INT(lsr, LSR_EVT_focusin, 6, "event"); break; + case GF_EVENT_FOCUSOUT: + GF_LSR_WRITE_INT(lsr, LSR_EVT_focusout, 6, "event"); break; + case GF_EVENT_KEYDOWN: + /*encode as accessKey() if param*/ + GF_LSR_WRITE_INT(lsr, evtParam ? LSR_EVT_accessKey : LSR_EVT_keydown, 6, "event"); + break; + case GF_EVENT_KEYUP: + GF_LSR_WRITE_INT(lsr, LSR_EVT_keyup, 6, "event"); break; + case GF_EVENT_LOAD: + GF_LSR_WRITE_INT(lsr, LSR_EVT_load, 6, "event"); break; + case GF_EVENT_LONGKEYPRESS: + GF_LSR_WRITE_INT(lsr, LSR_EVT_longAccessKey, 6, "event"); break; + case GF_EVENT_MOUSEDOWN: + GF_LSR_WRITE_INT(lsr, LSR_EVT_mousedown, 6, "event"); break; + case GF_EVENT_MOUSEMOVE: + GF_LSR_WRITE_INT(lsr, LSR_EVT_mousemove, 6, "event"); break; + case GF_EVENT_MOUSEOUT: + GF_LSR_WRITE_INT(lsr, LSR_EVT_mouseout, 6, "event"); break; + case GF_EVENT_MOUSEOVER: + GF_LSR_WRITE_INT(lsr, LSR_EVT_mouseover, 6, "event"); break; + case GF_EVENT_MOUSEUP: + GF_LSR_WRITE_INT(lsr, LSR_EVT_mouseup, 6, "event"); break; + case GF_EVENT_PAUSE: + GF_LSR_WRITE_INT(lsr, LSR_EVT_pause, 6, "event"); break; + case GF_EVENT_PAUSED_EVENT: + GF_LSR_WRITE_INT(lsr, LSR_EVT_pausedEvent, 6, "event"); break; + case GF_EVENT_PLAY: + GF_LSR_WRITE_INT(lsr, LSR_EVT_play, 6, "event"); break; + case GF_EVENT_REPEAT_EVENT: + GF_LSR_WRITE_INT(lsr, LSR_EVT_repeatEvent, 6, "event"); break; + case GF_EVENT_REPEAT_KEY: + GF_LSR_WRITE_INT(lsr, LSR_EVT_repeatKey, 6, "event"); break; + case GF_EVENT_RESIZE: + GF_LSR_WRITE_INT(lsr, LSR_EVT_resize, 6, "event"); break; + case GF_EVENT_RESUME_EVENT: + GF_LSR_WRITE_INT(lsr, LSR_EVT_resumedEvent, 6, "event"); break; + case GF_EVENT_SCROLL: + GF_LSR_WRITE_INT(lsr, LSR_EVT_scroll, 6, "event"); break; + case GF_EVENT_SHORT_ACCESSKEY: + GF_LSR_WRITE_INT(lsr, LSR_EVT_shortAccessKey, 6, "event"); break; + case GF_EVENT_TEXTINPUT: + GF_LSR_WRITE_INT(lsr, LSR_EVT_textinput, 6, "event"); break; + case GF_EVENT_UNLOAD: + GF_LSR_WRITE_INT(lsr, LSR_EVT_unload, 6, "event"); break; + case GF_EVENT_ZOOM: + GF_LSR_WRITE_INT(lsr, LSR_EVT_zoom, 6, "event"); break; + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] Unsupported LASER event %d\n", evtType) ); + GF_LSR_WRITE_INT(lsr, 0, 6, "event"); break; + return; + } + switch (evtType) { + case GF_EVENT_KEYDOWN: + if (!evtParam) break; + case GF_EVENT_LONGKEYPRESS: + case GF_EVENT_REPEAT_KEY: + case GF_EVENT_SHORT_ACCESSKEY: + lsr_write_vluimsbf5(lsr, dom_to_lsr_key(evtParam), "keyCode"); + break; + } + } +} + +static void lsr_write_smil_time(GF_LASeRCodec *lsr, SMIL_Time *t) +{ + s32 now; + if (t->type==GF_SMIL_TIME_EVENT) { + GF_LSR_WRITE_INT(lsr, 1, 1, "hasEvent"); + if (t->element && gf_node_get_id((GF_Node*)t->element) ) { + XMLRI iri; + GF_LSR_WRITE_INT(lsr, 1, 1, "hasIdentifier"); + iri.string = NULL; + iri.type = XMLRI_ELEMENTID; + iri.target = t->element; + lsr_write_codec_IDREF(lsr, &iri, "idref"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasIdentifier"); + } + lsr_write_event_type(lsr, t->event.type, t->event.parameter); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasEvent"); + } + if (!t->clock) { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasClock"); + return; + } + GF_LSR_WRITE_INT(lsr, 1, 1, "hasClock"); + + now = (s32) (t->clock * lsr->time_resolution); + if (now<0) { + now = -now; + GF_LSR_WRITE_INT(lsr, 1, 1, "sign"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "sign"); + } + lsr_write_vluimsbf5(lsr, now, "value"); +} + + +static void lsr_write_smil_times(GF_LASeRCodec *lsr, GF_List **l, const char *name, Bool skipable) +{ + SMIL_Time *v; + u32 r_count, i, count; + Bool indef = 0; + + count = l ? gf_list_count(*l) : 0; + + r_count = 0; + for (i=0; itype==GF_SMIL_TIME_INDEFINITE) { + indef = 1; + break; + } + else if (v->type!=GF_SMIL_TIME_EVENT_RESOLVED) r_count++; + } + if (skipable && !r_count && !indef) { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + return; + } + if (skipable) GF_LSR_WRITE_INT(lsr, 1, 1, name); + GF_LSR_WRITE_INT(lsr, indef, 1, "choice"); + if (indef) return; + lsr_write_vluimsbf5(lsr, r_count, "count"); + for (i=0; itype) { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + return; + } + GF_LSR_WRITE_INT(lsr, 1, 1, name); + } + + if (v->type==SMIL_DURATION_DEFINED) { + s32 now = (s32) (v->clock_value * lsr->time_resolution); + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + GF_LSR_WRITE_INT(lsr, (now<0) ? 1 : 0, 1, "sign"); + if (now<0) now = -now; + lsr_write_vluimsbf5(lsr, now, "value"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + GF_LSR_WRITE_INT(lsr, v->type, 2, "time"); + } +} +#define lsr_write_duration(a, b, c) lsr_write_duration_ex(a, b, c, 1) + +static void lsr_write_focus(GF_LASeRCodec *lsr, SVG_Focus *foc, const char *name) +{ + if (foc->type==SVG_FOCUS_IRI) { + GF_LSR_WRITE_INT(lsr, 0, 1, "isEnum"); + lsr_write_codec_IDREF(lsr, &foc->target, "id"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "isEnum"); + GF_LSR_WRITE_INT(lsr, foc->type, 1, "enum"); + } +} + +static Bool lsr_elt_has_same_base(GF_LASeRCodec *lsr, SVGAllAttributes *atts, SVG_Element *base, Bool *same_fill, Bool *same_stroke, Bool no_stroke_check) +{ + SVGAllAttributes base_atts; + GF_FieldInfo info, base_info; + + if (same_stroke) *same_stroke = 0; + if (same_fill) *same_fill = 0; + + if (!base) return 0; + + gf_svg_flatten_attributes(base, &base_atts); + if (atts->externalResourcesRequired != base_atts.externalResourcesRequired) return 0; + + info.fieldType = base_info.fieldType = SVG_Paint_datatype; + info.far_ptr = atts->stroke; + base_info.far_ptr = base_atts.stroke; + /*check stroke color*/ + if (!gf_svg_attributes_equal(&info, &base_info)) { + if (!no_stroke_check) return 0; + } else { + if (same_stroke) *same_stroke = 1; + } + if (same_fill) { + info.fieldType = base_info.fieldType = SVG_Paint_datatype; + info.far_ptr = atts->fill; + base_info.far_ptr = base_atts.fill; + /*check stroke color*/ + *same_fill = gf_svg_attributes_equal(&info, &base_info) ? 1 : 0; + } + + switch (gf_node_get_tag((GF_Node*) base)) { + /*check path length*/ + case TAG_SVG_path: + info.fieldType = base_info.fieldType = SVG_Number_datatype; + info.far_ptr = atts->pathLength; + base_info.far_ptr = base_atts.pathLength; + if (!gf_svg_attributes_equal(&info, &base_info)) return 0; + break; + /*check rx and ry for rect*/ + case TAG_SVG_rect: + info.fieldType = base_info.fieldType = SVG_Length_datatype; + info.far_ptr = atts->rx; + base_info.far_ptr = base_atts.rx; + if (!gf_svg_attributes_equal(&info, &base_info)) return 0; + + info.fieldType = base_info.fieldType = SVG_Length_datatype; + info.far_ptr = atts->ry; + base_info.far_ptr = base_atts.ry; + if (!gf_svg_attributes_equal(&info, &base_info)) return 0; + break; + /*check x and y for use*/ + case TAG_SVG_use: + info.fieldType = base_info.fieldType = SVG_Coordinate_datatype; + info.far_ptr = atts->x; + base_info.far_ptr = base_atts.x; + if (!gf_svg_attributes_equal(&info, &base_info)) return 0; + + info.fieldType = base_info.fieldType = SVG_Coordinate_datatype; + info.far_ptr = atts->y; + base_info.far_ptr = base_atts.y; + if (!gf_svg_attributes_equal(&info, &base_info)) return 0; + break; + /*check editable and rotate for text*/ + case TAG_SVG_text: + info.fieldType = base_info.fieldType = SVG_Boolean_datatype; + info.far_ptr = atts->editable; + base_info.far_ptr = base_atts.editable; + if (!gf_svg_attributes_equal(&info, &base_info)) return 0; + + info.fieldType = base_info.fieldType = SVG_Numbers_datatype; + info.far_ptr = atts->text_rotate; + base_info.far_ptr = base_atts.text_rotate; + if (!gf_svg_attributes_equal(&info, &base_info)) return 0; + break; + } + + return gf_lsr_same_rare(atts, &base_atts); +} + +static Bool lsr_float_list_equal(GF_List *l1, GF_List *l2) +{ + u32 i, count = gf_list_count(l1); + if (count != gf_list_count(l2)) return 0; + for (i=0;iattributes; + while (att) { + field_rare = gf_lsr_rare_type_from_attribute(att->tag); + if (field_rare>=0) nb_rare++; + att = att->next; + } + + GF_LSR_WRITE_INT(lsr, nb_rare ? 1 : 0, 1, "has_rare"); + if (!nb_rare) return; + + GF_LSR_WRITE_INT(lsr, nb_rare, 6, "nbOfAttributes"); + + att = ((SVG_Element*)n)->attributes; + while (att) { + field_rare = gf_lsr_rare_type_from_attribute(att->tag); + if (field_rare==-1) { + att = att->next; + continue; + } + /*RARE extension*/ + if (field_rare==49) { + Bool is_string = 0; + u32 size, cur_bits; + u32 len = 2+3; + switch (att->tag) { + case TAG_SVG_ATT_syncMaster: len +=1; break; + case TAG_SVG_ATT_requiredFonts: + len += 8*strlen(*(SVG_String*)att->data); + /*get vluimsbf5 field size with one extra word (4 bits, enough to code string alignment)*/ + size = lsr_get_vluimsbf5_size(len, 1); + cur_bits = gf_bs_get_bit_position(lsr->bs) + lsr->info->cfg.extensionIDBits + size + 5; + /*count string alignment*/ + while (cur_bits%8) { + len++; + cur_bits++; + } + is_string = 1; + break; + default: len +=2; break; + } + GF_LSR_WRITE_INT(lsr, 49, 6, "attributeRARE"); + GF_LSR_WRITE_INT(lsr, 2, lsr->info->cfg.extensionIDBits, "extensionID"); + if (is_string) { + lsr_write_vluimsbf5_ex(lsr, len, 1, "len"); + } else { + lsr_write_vluimsbf5(lsr, len, "len"); + } + GF_LSR_WRITE_INT(lsr, 1, 2, "nbOfAttributes"); + + switch (att->tag) { + case TAG_SVG_ATT_syncMaster: + GF_LSR_WRITE_INT(lsr, 0, 3, "attributeRARE"); + GF_LSR_WRITE_INT(lsr, *(SVG_Boolean *)att->data ? 1 : 0, 1, "syncMaster"); + break; + case TAG_SVG_ATT_focusHighlight: + GF_LSR_WRITE_INT(lsr, 1, 3, "attributeRARE"); + GF_LSR_WRITE_INT(lsr, *(SVG_FocusHighlight*)att->data, 2, "focusHighlight"); + break; + case TAG_SVG_ATT_initialVisibility: + GF_LSR_WRITE_INT(lsr, 2, 3, "attributeRARE"); + GF_LSR_WRITE_INT(lsr, *(SVG_InitialVisibility*)att->data, 2, "initialVisibility"); + break; + case TAG_SVG_ATT_fullscreen: + GF_LSR_WRITE_INT(lsr, 3, 3, "attributeRARE"); + GF_LSR_WRITE_INT(lsr, *(SVG_Boolean *)att->data ? 1 : 0, 2, "fullscreen"); + break; + case TAG_SVG_ATT_requiredFonts: + GF_LSR_WRITE_INT(lsr, 4, 3, "attributeRARE"); + lsr_write_byte_align_string(lsr, *(SVG_String*)att->data, "requiredFonts"); + break; + } + GF_LSR_WRITE_INT(lsr, 0, 1, "hasNextExtension"); + att = att->next; + continue; + } + + GF_LSR_WRITE_INT(lsr, (u32)field_rare, 6, "attributeRARE"); + switch (att->tag) { + case TAG_SVG_ATT__class: lsr_write_byte_align_string(lsr, *(SVG_String *)att->data, "class"); break; + + case TAG_SVG_ATT_audio_level: lsr_write_fixed_clamp(lsr, ((SVG_Number *) att->data)->value, "audio-level"); break; + case TAG_SVG_ATT_color: lsr_write_paint(lsr, (SVG_Paint*) att->data, "color"); break; + case TAG_SVG_ATT_color_rendering: GF_LSR_WRITE_INT(lsr, *(SVG_RenderingHint*)att->data, 2, "color-rendering"); break; + case TAG_SVG_ATT_display: GF_LSR_WRITE_INT(lsr, *(SVG_Display*)att->data, 5, "display"); break; + case TAG_SVG_ATT_display_align: GF_LSR_WRITE_INT(lsr, *(SVG_DisplayAlign*)att->data, 3, "display-align"); break; + case TAG_SVG_ATT_fill_opacity: lsr_write_fixed_clamp(lsr, ((SVG_Number *)att->data)->value, "fill-opacity"); break; + case TAG_SVG_ATT_fill_rule: GF_LSR_WRITE_INT(lsr, *(SVG_FillRule*)att->data, 2, "fill-rule"); break; + case TAG_SVG_ATT_image_rendering: GF_LSR_WRITE_INT(lsr, *(SVG_RenderingHint*)att->data, 2, "image-rendering"); break; + case TAG_SVG_ATT_line_increment: lsr_write_line_increment_type(lsr, (SVG_Number*)att->data, "lineIncrement"); break; + case TAG_SVG_ATT_pointer_events: GF_LSR_WRITE_INT(lsr, *(SVG_PointerEvents*)att->data, 4, "pointer-events"); break; + case TAG_SVG_ATT_shape_rendering: GF_LSR_WRITE_INT(lsr, *(SVG_RenderingHint*)att->data, 3, "shape-rendering"); break; + case TAG_SVG_ATT_solid_color: lsr_write_paint(lsr, (SVG_Paint*)att->data, "solid-color"); break; + case TAG_SVG_ATT_solid_opacity: lsr_write_fixed_clamp(lsr, ((SVG_Number *)att->data)->value, "solid-opacity"); break; + case TAG_SVG_ATT_stop_color: lsr_write_paint(lsr, (SVG_Paint*)att->data, "stop-color"); break; + case TAG_SVG_ATT_stop_opacity: lsr_write_fixed_clamp(lsr, ((SVG_Number *)att->data)->value, "stop-opacity"); break; + case TAG_SVG_ATT_stroke_dasharray: + { + u32 j; + SVG_StrokeDashArray *da = (SVG_StrokeDashArray*)att->data; + if (da->type==SVG_STROKEDASHARRAY_INHERIT) { + GF_LSR_WRITE_INT(lsr, 1, 1, "dashArray"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "dashArray"); + lsr_write_vluimsbf5(lsr, da->array.count, "len"); + for (j=0; jarray.count; j++) { + lsr_write_fixed_16_8(lsr, da->array.vals[j], "dash"); + } + } + } + break; + case TAG_SVG_ATT_stroke_dashoffset: + lsr_write_fixed_16_8i(lsr, (SVG_Number*)att->data, "dashOffset"); break; + + case TAG_SVG_ATT_stroke_linecap: GF_LSR_WRITE_INT(lsr, *(SVG_StrokeLineCap*)att->data, 2, "stroke-linecap"); break; + case TAG_SVG_ATT_stroke_linejoin: GF_LSR_WRITE_INT(lsr, *(SVG_StrokeLineJoin*)att->data, 2, "stroke-linejoin"); break; + case TAG_SVG_ATT_stroke_miterlimit: lsr_write_fixed_16_8i(lsr, (SVG_Number*)att->data, "miterLimit"); break; + case TAG_SVG_ATT_stroke_opacity: lsr_write_fixed_clamp(lsr, ((SVG_Number *)att->data)->value, "stroke-opacity"); break; + case TAG_SVG_ATT_stroke_width: lsr_write_fixed_16_8i(lsr, (SVG_Number*)att->data, "strokeWidth"); break; + case TAG_SVG_ATT_text_anchor: GF_LSR_WRITE_INT(lsr, *(SVG_TextAnchor*)att->data, 2, "text-achor"); break; + case TAG_SVG_ATT_text_rendering: GF_LSR_WRITE_INT(lsr, *(SVG_RenderingHint*)att->data, 3, "text-rendering"); break; + case TAG_SVG_ATT_viewport_fill: lsr_write_paint(lsr, (SVG_Paint*)att->data, "viewport-fill"); break; + case TAG_SVG_ATT_viewport_fill_opacity: lsr_write_fixed_clamp(lsr, ((SVG_Number *)att->data)->value, "viewport-fill-opacity"); break; + case TAG_SVG_ATT_vector_effect: GF_LSR_WRITE_INT(lsr, *(SVG_VectorEffect*)att->data, 4, "vector-effect"); break; + case TAG_SVG_ATT_visibility: GF_LSR_WRITE_INT(lsr, *(SVG_PointerEvents*)att->data, 2, "visibility"); break; + case TAG_SVG_ATT_requiredExtensions: lsr_write_byte_align_string_list(lsr, *(GF_List **)att->data, "requiredExtensions", 1); break; + case TAG_SVG_ATT_requiredFormats: lsr_write_byte_align_string_list(lsr, *(GF_List **)att->data, "requiredFormats", 0); break; + case TAG_SVG_ATT_requiredFeatures: + { + GF_List *l = *(GF_List **)att->data; + u32 j, tot_count, count = gf_list_count(l); + u8 *vals = (u8*)malloc(sizeof(u8)*count); + tot_count = 0; + for (i=0; itype != XMLRI_STRING) continue; + ext = strchr(iri->string, '#'); + if (!ext) continue; + if (!stricmp(ext, "Animation")) { vals[tot_count] = 0; tot_count++; } + else if (!stricmp(ext, "Audio")) { vals[tot_count] = 1; tot_count++; } + else if (!stricmp(ext, "ComposedVideo")) { vals[tot_count] = 2; tot_count++; } + else if (!stricmp(ext, "ConditionalProcessing")) { vals[tot_count] = 3; tot_count++; } + else if (!stricmp(ext, "ConditionalProcessingAttribute")) { vals[tot_count] = 4; tot_count++; } + else if (!stricmp(ext, "CoreAttribute")) { vals[tot_count] = 5; tot_count++; } + else if (!stricmp(ext, "Extensibility")) { vals[tot_count] = 6; tot_count++; } + else if (!stricmp(ext, "ExternalResourcesRequired")) { vals[tot_count] = 7; tot_count++; } + else if (!stricmp(ext, "Font")) { vals[tot_count] = 8; tot_count++; } + else if (!stricmp(ext, "Gradient")) { vals[tot_count] = 9; tot_count++; } + else if (!stricmp(ext, "GraphicsAttribute")) { vals[tot_count] = 10; tot_count++; } + else if (!stricmp(ext, "Handler")) { vals[tot_count] = 11; tot_count++; } + else if (!stricmp(ext, "Hyperlinking")) { vals[tot_count] = 12; tot_count++; } + else if (!stricmp(ext, "Image")) { vals[tot_count] = 13; tot_count++; } + else if (!stricmp(ext, "OpacityAttribute")) { vals[tot_count] = 14; tot_count++; } + else if (!stricmp(ext, "PaintAttribute")) { vals[tot_count] = 15; tot_count++; } + else if (!stricmp(ext, "Prefetch")) { vals[tot_count] = 16; tot_count++; } + else if (!stricmp(ext, "SVG")) { vals[tot_count] = 17; tot_count++; } + else if (!stricmp(ext, "SVG-animation")) { vals[tot_count] = 18; tot_count++; } + else if (!stricmp(ext, "SVG-dynamic")) { vals[tot_count] = 19; tot_count++; } + else if (!stricmp(ext, "SVG-static")) { vals[tot_count] = 20; tot_count++; } + else if (!stricmp(ext, "SVGDOM")) { vals[tot_count] = 21; tot_count++; } + else if (!stricmp(ext, "SVGDOM-animation")) { vals[tot_count] = 22; tot_count++; } + else if (!stricmp(ext, "SVGDOM-dynamic")) { vals[tot_count] = 23; tot_count++; } + else if (!stricmp(ext, "SVGDOM-static")) { vals[tot_count] = 24; tot_count++; } + else if (!stricmp(ext, "Script")) { vals[tot_count] = 25; tot_count++; } + else if (!stricmp(ext, "Shape")) { vals[tot_count] = 26; tot_count++; } + else if (!stricmp(ext, "SolidColor")) { vals[tot_count] = 27; tot_count++; } + else if (!stricmp(ext, "Structure")) { vals[tot_count] = 28; tot_count++; } + else if (!stricmp(ext, "Text")) { vals[tot_count] = 29; tot_count++; } + else if (!stricmp(ext, "TimedAnimation")) { vals[tot_count] = 30; tot_count++; } + else if (!stricmp(ext, "TransformedVideo")) { vals[tot_count] = 31; tot_count++; } + else if (!stricmp(ext, "Video")) { vals[tot_count] = 32; tot_count++; } + else if (!stricmp(ext, "XlinkAttribute")) { vals[tot_count] = 33; tot_count++; } + } + lsr_write_vluimsbf5(lsr, tot_count, "len"); + for (j=0; jdata, "systemLanguage", 0); + break; + case TAG_XML_ATT_base: lsr_write_byte_align_string(lsr, ((XMLRI*)att->data)->string, "xml:base"); break; + case TAG_XML_ATT_lang: lsr_write_byte_align_string(lsr, *(SVG_String *)att->data, "xml:lang"); break; + case TAG_XML_ATT_space: GF_LSR_WRITE_INT(lsr, *(XML_Space *)att->data, 1, "xml:space"); break; + case TAG_SVG_ATT_nav_next: lsr_write_focus(lsr, (SVG_Focus*)att->data, "focusNext"); break; + case TAG_SVG_ATT_nav_up: lsr_write_focus(lsr, (SVG_Focus*)att->data, "focusNorth"); break; + case TAG_SVG_ATT_nav_up_left: lsr_write_focus(lsr, (SVG_Focus*)att->data, "focusNorthEast"); break; + case TAG_SVG_ATT_nav_up_right: lsr_write_focus(lsr, (SVG_Focus*)att->data, "focusNorthWest"); break; + case TAG_SVG_ATT_nav_prev: lsr_write_focus(lsr, (SVG_Focus*)att->data, "focusPrev"); break; + case TAG_SVG_ATT_nav_down: lsr_write_focus(lsr, (SVG_Focus*)att->data, "focusSouth"); break; + case TAG_SVG_ATT_nav_down_left: lsr_write_focus(lsr, (SVG_Focus*)att->data, "focusSouthEast"); break; + case TAG_SVG_ATT_nav_down_right: lsr_write_focus(lsr, (SVG_Focus*)att->data, "focusSouthWest"); break; + case TAG_SVG_ATT_nav_right: lsr_write_focus(lsr, (SVG_Focus*)att->data, "focusWest"); break; + case TAG_SVG_ATT_nav_left: lsr_write_focus(lsr, (SVG_Focus*)att->data, "focusEast"); break; + + case TAG_SVG_ATT_font_variant: GF_LSR_WRITE_INT(lsr, *(SVG_FontVariant *)att->data, 2, "font-variant"); break; + case TAG_SVG_ATT_font_family: + { + s32 idx = lsr_get_font_index(lsr, (SVG_FontFamily*)att->data); + if (idx<0) { + GF_LSR_WRITE_INT(lsr, 1, 1, "isInherit"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "isInherit"); + GF_LSR_WRITE_INT(lsr, idx, lsr->fontIndexBits, "fontIndex"); + } + } + break; + case TAG_SVG_ATT_font_size: lsr_write_fixed_16_8i(lsr, (SVG_Number*)att->data, "fontSize"); break; + case TAG_SVG_ATT_font_style: GF_LSR_WRITE_INT(lsr, *((SVG_FontStyle *)att->data), 3, "fontStyle"); break; + case TAG_SVG_ATT_font_weight: GF_LSR_WRITE_INT(lsr, *((SVG_FontWeight *)att->data), 4, "fontWeight"); break; + + case TAG_XLINK_ATT_title: lsr_write_byte_align_string(lsr, *(SVG_String *)att->data, "xlink:title"); break; + /*TODO FIXME*/ + case TAG_XLINK_ATT_type: GF_LSR_WRITE_INT(lsr, 0, 3, "xlink:type"); break; + case TAG_XLINK_ATT_role: lsr_write_any_uri(lsr, (XMLRI*)att->data, "xlink:role"); break; + case TAG_XLINK_ATT_arcrole: lsr_write_any_uri(lsr, (XMLRI*)att->data, "xlink:arcrole"); break; + /*TODO FIXME*/ + case TAG_XLINK_ATT_actuate: GF_LSR_WRITE_INT(lsr, 0, 2, "xlink:actuate"); break; + case TAG_XLINK_ATT_show: GF_LSR_WRITE_INT(lsr, 0, 3, "xlink:show"); break; + case TAG_SVG_ATT_end: lsr_write_smil_times(lsr, (GF_List **)att->data, "end", 0); break; + case TAG_SVG_ATT_min: lsr_write_duration_ex(lsr, (SMIL_Duration*)att->data, "min", 0); break; + case TAG_SVG_ATT_max: lsr_write_duration_ex(lsr, (SMIL_Duration*)att->data, "max", 0); break; + case TAG_SVG_ATT_transform: lsr_write_matrix(lsr, (SVG_Transform*)att->data); break; + + } + att = att->next; + } +} + +static void lsr_write_fill(GF_LASeRCodec *lsr, SVG_Element *n, SVGAllAttributes *atts) +{ + if (atts->fill) { + GF_LSR_WRITE_INT(lsr, 1, 1, "fill"); + lsr_write_paint(lsr, atts->fill, "fill"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "fill"); + } +} + +static void lsr_write_stroke(GF_LASeRCodec *lsr, SVG_Element *n, SVGAllAttributes *atts) +{ + if (atts->stroke) { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_stroke"); + lsr_write_paint(lsr, atts->stroke, "stroke"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_stroke"); + } +} +static void lsr_write_href(GF_LASeRCodec *lsr, XMLRI *iri) +{ + Bool has_href = iri ? 1 : 0; + if (iri) { + if (iri->type==XMLRI_ELEMENTID) { + if (!iri->target && iri->string) iri->target = (SVG_Element *)gf_sg_find_node_by_name(lsr->sg, iri->string+1); + if (!iri->target || !gf_node_get_id((GF_Node *)iri->target)) has_href = 0; + } + else if (iri->type==XMLRI_STREAMID) { + if (!iri->lsr_stream_id) has_href = 0; + } + else if (!iri->string) has_href = 0; + } + + GF_LSR_WRITE_INT(lsr, has_href, 1, "has_href"); + if (has_href) lsr_write_any_uri(lsr, iri, "href"); +} + +static void lsr_write_accumulate(GF_LASeRCodec *lsr, SMIL_Accumulate *accum_type) +{ + GF_LSR_WRITE_INT(lsr, accum_type ? 1 : 0, 1, "has_accumulate"); + if (accum_type) GF_LSR_WRITE_INT(lsr, *accum_type, 1, "accumulate"); +} +static void lsr_write_additive(GF_LASeRCodec *lsr, SMIL_Additive *add_type) +{ + GF_LSR_WRITE_INT(lsr, add_type ? 1 : 0, 1, "has_additive"); + if (add_type) GF_LSR_WRITE_INT(lsr, *add_type, 1, "additive"); +} +static void lsr_write_calc_mode(GF_LASeRCodec *lsr, u8 *calc_mode) +{ + + /*SMIL_CALCMODE_LINEAR is default and 0 in our code*/ + GF_LSR_WRITE_INT(lsr, (!calc_mode || (*calc_mode==SMIL_CALCMODE_LINEAR)) ? 0 : 1, 1, "has_calcMode"); + if (calc_mode && (*calc_mode!=SMIL_CALCMODE_LINEAR)) { + GF_LSR_WRITE_INT(lsr, *calc_mode, 2, "calcMode"); + } +} + +static void lsr_write_animatable(GF_LASeRCodec *lsr, SMIL_AttributeName *anim_type, XMLRI *iri, const char *name) +{ + s32 a_type = -1; + + if (!anim_type || !iri || !iri->target) { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasAttributeName"); + return; + } + + /*locate field - checkme, this may not work since anim is not setup...*/ + assert(anim_type->name || anim_type->tag); + if (!anim_type->tag) anim_type->tag = gf_xml_get_attribute_tag((GF_Node*)iri->target, anim_type->name, 0); + if (!anim_type->type) anim_type->type = gf_xml_get_attribute_type(anim_type->tag); + a_type = gf_lsr_anim_type_from_attribute(anim_type->tag); + if (a_type<0) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] Unsupported attributeName %s\n", anim_type->name)); + } + GF_LSR_WRITE_INT(lsr, 1, 1, "hasAttributeName"); + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + GF_LSR_WRITE_INT(lsr, (u8) a_type, 8, "attributeType"); +} + +static void lsr_write_anim_fill(GF_LASeRCodec *lsr, u8 *animFreeze) +{ + GF_LSR_WRITE_INT(lsr, animFreeze ? 1 : 0, 1, "has_smil_fill"); + if (animFreeze) GF_LSR_WRITE_INT(lsr, *animFreeze, 1, "smil_fill"); +} +static void lsr_write_anim_repeat(GF_LASeRCodec *lsr, SMIL_RepeatCount *repeat) +{ + GF_LSR_WRITE_INT(lsr, repeat ? 1 : 0, 1, "has_repeatCount"); + if (!repeat) return; + + if (repeat->type==SMIL_REPEATCOUNT_DEFINED) { + GF_LSR_WRITE_INT(lsr, 0, 1, "repeatCount"); + lsr_write_fixed_16_8(lsr, repeat->count, "repeatCount"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "repeatCount"); + /*enumeration indefinite{0}*/ + } +} +static void lsr_write_repeat_duration(GF_LASeRCodec *lsr, SMIL_Duration *smil) +{ + GF_LSR_WRITE_INT(lsr, smil ? 1 : 0, 1, "has_repeatDur"); + if (!smil) return; + + if (smil->type==SMIL_DURATION_DEFINED) { + u32 now = (u32) (smil->clock_value * lsr->time_resolution); + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + lsr_write_vluimsbf5(lsr, now, "value"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + /*enumeration indefinite{0}*/ + } +} +static void lsr_write_anim_restart(GF_LASeRCodec *lsr, u8 *animRestart) +{ + GF_LSR_WRITE_INT(lsr, animRestart ? 1 : 0, 1, "has_restart"); + + /*enumeration always{0} never{1} whenNotActive{2}*/ + if (animRestart) GF_LSR_WRITE_INT(lsr, *animRestart, 2, "restart"); +} + +static u32 svg_type_to_lsr_anim(u32 svg_type, u32 transform_type, GF_List *vals, void *a_val) +{ + switch (svg_type) { + /*all string types*/ + case DOM_String_datatype: + return 0; + /*all length types*/ + case SVG_Number_datatype: + case SVG_FontSize_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + return 1; + case SVG_PathData_datatype: + return 2; + /*list of points*/ + case SMIL_KeyPoints_datatype: + case SVG_Points_datatype: + return 3; + /*all 0 - 1 types*/ +/* + case SVG_Opacity_datatype: + return 4; +*/ + case SVG_Paint_datatype: + return 5; + /*all enums (u8) types*/ + case SVG_FillRule_datatype: + case SVG_StrokeLineJoin_datatype: + case SVG_StrokeLineCap_datatype: + case SVG_FontStyle_datatype: + case SVG_FontWeight_datatype: + case SVG_FontVariant_datatype: + case SVG_TextAnchor_datatype: + case SVG_TransformType_datatype: + case SVG_Display_datatype: + case SVG_Visibility_datatype: + case SVG_Overflow_datatype: + case SVG_ZoomAndPan_datatype: + case SVG_DisplayAlign_datatype: + case SVG_TextAlign_datatype: + case SVG_PointerEvents_datatype: + case SVG_RenderingHint_datatype: + case SVG_VectorEffect_datatype: + case SVG_PlaybackOrder_datatype: + case SVG_TimelineBegin_datatype: + return 6; + /*all list-of-int types*/ //return 7; + /*all list-of-float types*/ + case SVG_StrokeDashArray_datatype: + case SVG_ViewBox_datatype: + case SVG_Coordinates_datatype: + return 8; + /*ID (u32) types*/ //return 10; + case SVG_FontFamily_datatype: + return 11; + case XMLRI_datatype: + return 12; + case SVG_Motion_datatype: + return 9; + + /*ARG LOOKS LIKE THE SPEC IS BROKEN HERE*/ + case SVG_Transform_Translate_datatype: return 9; + case SVG_Transform_Scale_datatype: return 8; + case SVG_Transform_Rotate_datatype: + if (vals) { + u32 i=0; + SVG_Point_Angle *pt; + while ((pt = (SVG_Point_Angle *) gf_list_enum(vals, &i))) { + if (pt->x || pt->y) return 8; + } + } else if (a_val) { + SVG_Point_Angle *pt = (SVG_Point_Angle *) a_val; + if (pt->x || pt->y) return 8; + } + return 1; + case SVG_Transform_SkewX_datatype: return 1; + case SVG_Transform_SkewY_datatype: return 1; + //case SVG_Transform_datatype: return; + /*FALL THROUH*/ + default: + return 255; + } +} +static void lsr_write_coordinate(GF_LASeRCodec *lsr, Fixed val, Bool skipable, const char *name) +{ + if (skipable && !val) { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + } else { + u32 res = lsr_translate_coords(lsr, val, lsr->coord_bits); + if (skipable) GF_LSR_WRITE_INT(lsr, 1, 1, name); + GF_LSR_WRITE_INT(lsr, res, lsr->coord_bits, name); + } +} + +static void lsr_write_coordinate_ptr(GF_LASeRCodec *lsr, SVG_Coordinate *val, Bool skipable, const char *name) +{ + if (skipable && !val) { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + } else { + u32 res = lsr_translate_coords(lsr, val ? val->value : 0, lsr->coord_bits); + if (skipable) GF_LSR_WRITE_INT(lsr, 1, 1, name); + GF_LSR_WRITE_INT(lsr, res, lsr->coord_bits, name); + } +} + +static void lsr_write_an_anim_value(GF_LASeRCodec *lsr, void *val, u32 lsr_type, u32 svg_type, u32 transform_type, const char *name) +{ + if ((lsr_type==1) || (lsr_type==4)) { + switch (svg_type) { + case SVG_Transform_Rotate_datatype: + case SVG_Transform_SkewX_datatype: + case SVG_Transform_SkewY_datatype: + GF_LSR_WRITE_INT(lsr, 0, 1, "escapeFlag"); + break; + default: + { + SVG_Number *n = (SVG_Number *) val; + if (n->type != SVG_NUMBER_VALUE) { + GF_LSR_WRITE_INT(lsr, 1, 1, "escapeFlag"); + GF_LSR_WRITE_INT(lsr, n->type, 2, "escapeEnum"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "escapeFlag"); + } + } + break; + } + } else if (svg_type==SVG_StrokeDashArray_datatype) { + SVG_StrokeDashArray *da = (SVG_StrokeDashArray *)val; + if (da->type==SVG_STROKEDASHARRAY_INHERIT) { + GF_LSR_WRITE_INT(lsr, 1, 1, "escapeFlag"); + GF_LSR_WRITE_INT(lsr, 0, 2, "escapeEnum"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "escapeFlag"); + } + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "escapeFlag"); + } + + switch(lsr_type) { + case 0: lsr_write_byte_align_string(lsr, *(DOM_String *)val, name); break; + case 1: + if (svg_type==SVG_Transform_Rotate_datatype) { + Fixed angle = ((SVG_Point_Angle *) val)->angle; + angle = gf_muldiv(angle, INT2FIX(180), GF_PI); + lsr_write_fixed_16_8(lsr, angle, name); + } else if ((svg_type==SVG_Transform_SkewX_datatype) || (svg_type==SVG_Transform_SkewY_datatype)) { + lsr_write_fixed_16_8(lsr, *(Fixed *)val, name); + } else { + lsr_write_fixed_16_8(lsr, ((SVG_Number *) val)->value, name); + } + break; + case 12: lsr_write_any_uri(lsr, (XMLRI*)val, name); break; + case 2: lsr_write_path_type(lsr, (SVG_PathData*)val, name); break; + case 3: lsr_write_point_sequence(lsr, (GF_List **)val, name); break; + case 4: lsr_write_fixed_clamp(lsr, ((SVG_Number *) val)->value, name); break; + case 5: lsr_write_paint(lsr, (SVG_Paint*)val, name); break; + case 6: lsr_write_vluimsbf5(lsr, (u32) *(u8 *) val, name); break; + case 10: lsr_write_vluimsbf5(lsr, *(u32 *) val, name); break; + case 11: + { + s32 idx = lsr_get_font_index(lsr, (SVG_FontFamily*)val); + if (idx<0) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] corrupted font table while encoding anim value\n")); + idx=0; + } + lsr_write_vluimsbf5(lsr, idx, name); + } + break; + case 7: + { + GF_List *l = *(GF_List **)val; + u32 i, count = gf_list_count(l); + lsr_write_vluimsbf5(lsr, count, "count"); + for (i=0; iarray.count, "count"); + for (i=0; iarray.count; i++) { + lsr_write_fixed_16_8(lsr, da->array.vals[i], "val"); + } + } else if (svg_type==SVG_ViewBox_datatype) { + SVG_ViewBox *vb = (SVG_ViewBox *)val; + lsr_write_vluimsbf5(lsr, 4, "count"); + lsr_write_fixed_16_8(lsr, vb->x, "val"); + lsr_write_fixed_16_8(lsr, vb->y, "val"); + lsr_write_fixed_16_8(lsr, vb->width, "val"); + lsr_write_fixed_16_8(lsr, vb->height, "val"); + } else if (svg_type==SVG_Coordinates_datatype) { + GF_List *l = *(GF_List **)val; + count = gf_list_count(l); + lsr_write_vluimsbf5(lsr, count, "count"); + for (i=0; ivalue, "val"); + } + } else if (svg_type==SVG_Transform_Rotate_datatype) { + Fixed angle; + SVG_Point_Angle *p = (SVG_Point_Angle *)val; + count = (p->x || p->y) ? 3 : 1; + lsr_write_vluimsbf5(lsr, count, "count"); + angle = gf_muldiv(p->angle, INT2FIX(180), GF_PI); + lsr_write_fixed_16_8(lsr, p->angle, "val"); + if (count==3) { + lsr_write_fixed_16_8(lsr, p->x, "val"); + lsr_write_fixed_16_8(lsr, p->y, "val"); + } + } else if (svg_type==SVG_Transform_Scale_datatype) { + SVG_Point *pt = (SVG_Point *)val; + count = (pt->x == pt->y) ? 1 : 2; + lsr_write_vluimsbf5(lsr, count, "count"); + lsr_write_fixed_16_8(lsr, pt->x, "val"); + if (count==2) lsr_write_fixed_16_8(lsr, pt->y, "val"); + } else { + GF_List *l = *(GF_List **)val; + count = gf_list_count(l); + lsr_write_vluimsbf5(lsr, count, "count"); + for (i=0; im[2], 0, "valX"); + lsr_write_coordinate(lsr, ((GF_Matrix2D*)val)->m[5], 0, "valY"); + } else { + lsr_write_coordinate(lsr, ((SVG_Point *)val)->x, 0, "valX"); + lsr_write_coordinate(lsr, ((SVG_Point *)val)->y, 0, "valY"); + } + break; + default: + lsr_write_extension(lsr, NULL, 0, name); + break; + } +} + +static void lsr_write_anim_value(GF_LASeRCodec *lsr, SMIL_AnimateValue *val, const char *name) +{ + if (!val || !val->type) { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + } else { + u32 type = svg_type_to_lsr_anim(val->type, 0, NULL, val->value); + if (type==255) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] unsupported anim type %d - skipping\n", val->type )); + GF_LSR_WRITE_INT(lsr, 0, 1, name); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, name); + GF_LSR_WRITE_INT(lsr, type, 4, "type"); + lsr_write_an_anim_value(lsr, val->value, type, val->type, 0, name); + } + } +} + +static void lsr_write_anim_values(GF_LASeRCodec *lsr, SMIL_AnimateValues *anims, const char *name) +{ + u32 type, i, count = 0; + if (anims && anims->type) count = gf_list_count(anims->values); + + if (!count) { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + return; + } + type = svg_type_to_lsr_anim(anims->type, 0, anims->values, NULL); + if (type==255) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] unsupported anim type %d - skipping\n", anims->type )); + GF_LSR_WRITE_INT(lsr, 0, 1, name); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, name); + GF_LSR_WRITE_INT(lsr, type, 4, "type"); + lsr_write_vluimsbf5(lsr, count, "count"); + for (i=0; ivalues, i); + lsr_write_an_anim_value(lsr, att, type, anims->type, 0, "a_value"); + } + } +} + +static void lsr_write_fraction_12(GF_LASeRCodec *lsr, GF_List **l, const char *name) +{ + u32 i, count; + count = l ? gf_list_count(*l) : 0; + if (!count) { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + return; + } + GF_LSR_WRITE_INT(lsr, 1, 1, name); + lsr_write_vluimsbf5(lsr, count, "name"); + for (i=0; ires_factor); + val = (v<0) ? FIX2INT(-v) : FIX2INT(v); + return 1 + gf_get_bit_size(val); +} + +static void lsr_write_point_sequence(GF_LASeRCodec *lsr, GF_List **pts, const char *name) +{ + u32 i, count = pts ? gf_list_count(*pts) : 0; + lsr_write_vluimsbf5(lsr, count, "nbPoints"); + if (!count) return; + /*TODO golomb coding*/ + GF_LSR_WRITE_INT(lsr, 0, 1, "flag"); + if (1) { + if (count < 3) { + u32 nb_bits = 0; + for (i=0; ix); if (k>nb_bits) nb_bits = k; + k = lsr_get_bit_size(lsr, pt->y); if (k>nb_bits) nb_bits = k; + } + + GF_LSR_WRITE_INT(lsr, nb_bits, 5, "bits"); + for (i=0; ix, nb_bits); + GF_LSR_WRITE_INT(lsr, v, nb_bits, "x"); + v = lsr_translate_coords(lsr, pt->y, nb_bits); + GF_LSR_WRITE_INT(lsr, v, nb_bits, "y"); + } + } else { + Fixed c_x, c_y; + u32 k, nb_dx, nb_dy; + SVG_Point *pt = (SVG_Point *)gf_list_get(*pts, 0); + nb_dx = 0; + k = lsr_get_bit_size(lsr, pt->x); if (k>nb_dx) nb_dx = k; + k = lsr_get_bit_size(lsr, pt->y); if (k>nb_dx) nb_dx = k; + GF_LSR_WRITE_INT(lsr, nb_dx, 5, "bits"); + k = lsr_translate_coords(lsr, pt->x, nb_dx); + GF_LSR_WRITE_INT(lsr, k, nb_dx, "x"); + k = lsr_translate_coords(lsr, pt->y, nb_dx); + GF_LSR_WRITE_INT(lsr, k, nb_dx, "y"); + c_x = pt->x; + c_y = pt->y; + nb_dx = nb_dy = 0; + for (i=1; ix - c_x); if (k>nb_dx) nb_dx = k; + k = lsr_get_bit_size(lsr, pt->y - c_y); if (k>nb_dy) nb_dy = k; + c_x = pt->x; + c_y = pt->y; + } + GF_LSR_WRITE_INT(lsr, nb_dx, 5, "bitsx"); + GF_LSR_WRITE_INT(lsr, nb_dy, 5, "bitsy"); + c_x = pt->x; + c_y = pt->y; + for (i=1; ix - c_x, nb_dx); + GF_LSR_WRITE_INT(lsr, k, nb_dx, "dx"); + k = lsr_translate_coords(lsr, pt->y - c_y, nb_dy); + GF_LSR_WRITE_INT(lsr, k, nb_dy, "dy"); + c_x = pt->x; + c_y = pt->y; + } + } + } +} +static void lsr_write_path_type(GF_LASeRCodec *lsr, SVG_PathData *path, const char *name) +{ +#if USE_GF_PATH + u32 i, *contour, nb_types; + GF_List *pts = gf_list_new(); + + contour = path->contours; + nb_types = 0; + for (i=0; in_points; ) { + switch (path->tags[i]) { + case GF_PATH_CURVE_ON: + gf_list_add(pts, &path->points[i]); + nb_types++; + i++; + break; + case GF_PATH_CLOSE: + nb_types++; + i++; + break; + case GF_PATH_CURVE_CONIC: + gf_list_add(pts, &path->points[i]); + gf_list_add(pts, &path->points[i+1]); + i+=2; + nb_types++; + break; + case GF_PATH_CURVE_CUBIC: + gf_list_add(pts, &path->points[i]); + gf_list_add(pts, &path->points[i+1]); + gf_list_add(pts, &path->points[i+2]); + i+=3; + nb_types++; + break; + } + } + lsr_write_point_sequence(lsr, &pts, "seq"); + gf_list_del(pts); + /*first moveTo is skiped*/ + lsr_write_vluimsbf5(lsr, nb_types-1, "nbOfTypes"); + for (i=0; in_points; ) { + switch (path->tags[i]) { + case GF_PATH_CLOSE: + /*close*/ + GF_LSR_WRITE_INT(lsr, LSR_PATH_COM_Z, 5, name); + i++; + break; + case GF_PATH_CURVE_ON: + if (!i) { + } else if (*contour == i-1) { + /*moveTo*/ + GF_LSR_WRITE_INT(lsr, LSR_PATH_COM_M, 5, name); + } else { + /*lineTo*/ + GF_LSR_WRITE_INT(lsr, LSR_PATH_COM_L, 5, name); + } + i++; + break; + case GF_PATH_CURVE_CONIC: + /*Conic*/ + GF_LSR_WRITE_INT(lsr, LSR_PATH_COM_Q, 5, name); + i+=2; + break; + case GF_PATH_CURVE_CUBIC: + /*cubic*/ + GF_LSR_WRITE_INT(lsr, LSR_PATH_COM_C, 5, name); + i+=3; + break; + } + } +#else + u32 i, count; + lsr_write_point_sequence(lsr, path->points, "seq"); + /*initial move is not coded*/ + count = gf_list_count(path->commands); + lsr_write_vluimsbf5(lsr, count, "nbOfTypes"); + for (i; icommands, i); + GF_LSR_WRITE_INT(lsr, type, 5, name); + } +#endif +} + +static void lsr_write_rotate_type(GF_LASeRCodec *lsr, SVG_Rotate *rotate, const char *name) +{ + GF_LSR_WRITE_INT(lsr, rotate ? 1 : 0, 1, name); + if (!rotate) return; + + if ((rotate->type == SVG_NUMBER_AUTO) || (rotate->type == SVG_NUMBER_AUTO_REVERSE)) { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + GF_LSR_WRITE_INT(lsr, (rotate->type == SVG_NUMBER_AUTO) ? 0 : 1, 1, "rotate"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + lsr_write_fixed_16_8(lsr, rotate->value, "rotate"); + } +} +static void lsr_write_sync_behavior(GF_LASeRCodec *lsr, SMIL_SyncBehavior *sync, const char *name) +{ + GF_LSR_WRITE_INT(lsr, sync ? 1 : 0, 1, name); + if (!sync) return; + assert(*sync!=SMIL_SYNCBEHAVIOR_INHERIT); + GF_LSR_WRITE_INT(lsr, *sync-1, 2, name); +} +static void lsr_write_sync_tolerance(GF_LASeRCodec *lsr, SMIL_SyncTolerance *sync, const char *name) +{ + GF_LSR_WRITE_INT(lsr, sync ? 1 : 0, 1, name); + if (!sync) return; + assert(sync->type!=SMIL_SYNCTOLERANCE_INHERIT); + + if (sync->type==SMIL_SYNCTOLERANCE_DEFAULT) { + GF_LSR_WRITE_INT(lsr, 1, 1, name); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + lsr_write_vluimsbf5(lsr, (u32) (sync->value*lsr->time_resolution), "value"); + } +} +static void lsr_write_coord_list(GF_LASeRCodec *lsr, GF_List **coords, const char *name) +{ + u32 i, count = coords ? gf_list_count(*coords) : 0; + if (!count) { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, name); + lsr_write_vluimsbf5(lsr, count, "nb_coords"); + for (i=0; ivalue, lsr->coord_bits); + GF_LSR_WRITE_INT(lsr, res, lsr->coord_bits, name); + } + } +} + +static void lsr_write_transform_behavior(GF_LASeRCodec *lsr, SVG_TransformBehavior *type) +{ + GF_LSR_WRITE_INT(lsr, type ? 1 : 0, 1, "hasTransformBehavior"); + if (!type) return; + GF_LSR_WRITE_INT(lsr, *type, 4, "transformBehavior"); +} + +static void lsr_write_gradient_units(GF_LASeRCodec *lsr, SVG_GradientUnit *type) +{ + GF_LSR_WRITE_INT(lsr, type ? 1 : 0, 1, "hasGradientUnits"); + if (!type) return; + GF_LSR_WRITE_INT(lsr, *type ? 1 : 0, 1, "gradientUnits"); +} + +static void lsr_write_content_type(GF_LASeRCodec *lsr, SVG_String *type, const char *name) +{ + if (type) { + GF_LSR_WRITE_INT(lsr, 1, 1, "hasType"); + lsr_write_byte_align_string(lsr, *type, "type"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasType"); + } +} +static void lsr_write_script_type(GF_LASeRCodec *lsr, SVG_String *type) +{ + GF_LSR_WRITE_INT(lsr, type ? 1 : 0, 1, "hasType"); + if (!type) return; + if (!strcmp(*type, "application/ecmascript")) { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + GF_LSR_WRITE_INT(lsr, 0, 1, "script"); + } else if (!strcmp(*type, "application/jar-archive")) { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + GF_LSR_WRITE_INT(lsr, 1, 1, "script"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + lsr_write_byte_align_string(lsr, *type, "type"); + } +} + +static void lsr_write_value_with_units(GF_LASeRCodec *lsr, SVG_Number *n, const char *name) +{ +#ifdef GPAC_FIXED_POINT + s32 val = n->value >> 8; +#else + s32 val = (s32) (n->value * (1<<8) ); +#endif + GF_LSR_WRITE_INT(lsr, val, 32, name); + switch (n->type) { + case SVG_NUMBER_IN: GF_LSR_WRITE_INT(lsr, 1, 3, "units"); break; + case SVG_NUMBER_CM: GF_LSR_WRITE_INT(lsr, 2, 3, "units"); break; + case SVG_NUMBER_MM: GF_LSR_WRITE_INT(lsr, 3, 3, "units"); break; + case SVG_NUMBER_PT: GF_LSR_WRITE_INT(lsr, 4, 3, "units"); break; + case SVG_NUMBER_PC: GF_LSR_WRITE_INT(lsr, 5, 3, "units"); break; + case SVG_NUMBER_PERCENTAGE: GF_LSR_WRITE_INT(lsr, 6, 3, "units"); break; + default: GF_LSR_WRITE_INT(lsr, 0, 3, "units"); break; + } +} + + +static void lsr_write_clip_time(GF_LASeRCodec *lsr, SVG_Clock *clock, const char *name) +{ + if (!clock || *clock <= 0) { + GF_LSR_WRITE_INT(lsr, 0, 1, name); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, name); + GF_LSR_WRITE_INT(lsr, 0, 1, "isEnum"); + GF_LSR_WRITE_INT(lsr, 0, 1, "sign"); + lsr_write_vluimsbf5(lsr, (u32) (lsr->time_resolution* *clock), "val"); + } +} + +static void lsr_write_href_anim(GF_LASeRCodec *lsr, XMLRI *href, SVG_Element *parent) +{ + if (!href || (href->target && (href->target==parent))) { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_href"); + } else { + lsr_write_href(lsr, href); + } +} + +static void lsr_write_attribute_type(GF_LASeRCodec *lsr, SVGAllAttributes *atts) +{ + if (!atts->attributeType) { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasAttributeType"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "hasAttributeType"); + GF_LSR_WRITE_INT(lsr, (*atts->attributeType), 2, "attributeType"); + } +} + +static void lsr_write_preserve_aspect_ratio(GF_LASeRCodec *lsr, SVG_PreserveAspectRatio *preserveAspectRatio) +{ + GF_LSR_WRITE_INT(lsr, preserveAspectRatio ? 1 : 0, 1, "hasPreserveAspectRatio"); + if (!preserveAspectRatio) return; + GF_LSR_WRITE_INT(lsr, 0, 1, "choice (meetOrSlice)"); + GF_LSR_WRITE_INT(lsr, preserveAspectRatio->defer ? 1 : 0, 1, "choice (defer)"); + switch (preserveAspectRatio->align) { + case SVG_PRESERVEASPECTRATIO_XMAXYMAX: GF_LSR_WRITE_INT(lsr, 1, 4, "alignXandY"); break; + case SVG_PRESERVEASPECTRATIO_XMAXYMID: GF_LSR_WRITE_INT(lsr, 2, 4, "alignXandY"); break; + case SVG_PRESERVEASPECTRATIO_XMAXYMIN: GF_LSR_WRITE_INT(lsr, 3, 4, "alignXandY"); break; + case SVG_PRESERVEASPECTRATIO_XMIDYMAX: GF_LSR_WRITE_INT(lsr, 4, 4, "alignXandY"); break; + case SVG_PRESERVEASPECTRATIO_XMIDYMID: GF_LSR_WRITE_INT(lsr, 5, 4, "alignXandY"); break; + case SVG_PRESERVEASPECTRATIO_XMIDYMIN: GF_LSR_WRITE_INT(lsr, 6, 4, "alignXandY"); break; + case SVG_PRESERVEASPECTRATIO_XMINYMAX: GF_LSR_WRITE_INT(lsr, 7, 4, "alignXandY"); break; + case SVG_PRESERVEASPECTRATIO_XMINYMID: GF_LSR_WRITE_INT(lsr, 8, 4, "alignXandY"); break; + case SVG_PRESERVEASPECTRATIO_XMINYMIN: GF_LSR_WRITE_INT(lsr, 9, 4, "alignXandY"); break; + default: GF_LSR_WRITE_INT(lsr, 0, 4, "alignXandY"); break; + } +} + + +#define lsr_write_err() GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired") + + +static void lsr_write_a(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + lsr_write_stroke(lsr, elt, &atts); + lsr_write_err(); + GF_LSR_WRITE_INT(lsr, (atts.target!=NULL) ? 1 : 0, 1, "hasTarget"); + if (atts.target) lsr_write_byte_align_string(lsr, *(SVG_String*)atts.target, "target"); + lsr_write_href(lsr, atts.xlink_href); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_animate(GF_LASeRCodec *lsr, SVG_Element *elt, SVG_Element *parent) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_animatable(lsr, atts.attributeName, atts.xlink_href, "attributeName"); + + lsr_write_accumulate(lsr, atts.accumulate); + lsr_write_additive(lsr, atts.additive); + lsr_write_anim_value(lsr, atts.by, "by"); + lsr_write_calc_mode(lsr, atts.calcMode); + lsr_write_anim_value(lsr, atts.from, "from"); + lsr_write_fraction_12(lsr, atts.keySplines, "keySplines"); + lsr_write_fraction_12(lsr, atts.keyTimes, "keyTimes"); + lsr_write_anim_values(lsr, atts.values, "values"); + lsr_write_attribute_type(lsr, &atts); + lsr_write_smil_times(lsr, atts.begin, "begin", 1); + lsr_write_duration(lsr, atts.dur, "dur"); + lsr_write_anim_fill(lsr, atts.smil_fill); + lsr_write_anim_repeat(lsr, atts.repeatCount); + lsr_write_repeat_duration(lsr, atts.repeatDur); + lsr_write_anim_restart(lsr, atts.restart); + lsr_write_anim_value(lsr, atts.to, "to"); + lsr_write_href_anim(lsr, atts.xlink_href, parent); + GF_LSR_WRITE_INT(lsr, (atts.lsr_enabled && *atts.lsr_enabled) ? 1 : 0, 1, "enabled"); + + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + + +static void lsr_write_animateMotion(GF_LASeRCodec *lsr, SVG_Element*elt, SVG_Element *parent) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_accumulate(lsr, atts.accumulate); + lsr_write_additive(lsr, atts.additive); + lsr_write_anim_value(lsr, atts.by, "by"); + lsr_write_calc_mode(lsr, atts.calcMode); + lsr_write_anim_value(lsr, atts.from, "from"); + lsr_write_fraction_12(lsr, atts.keySplines, "keySplines"); + lsr_write_fraction_12(lsr, atts.keyTimes, "keyTimes"); + lsr_write_anim_values(lsr, atts.values, "values"); + lsr_write_attribute_type(lsr, &atts); + lsr_write_smil_times(lsr, atts.begin, "begin", 1); + lsr_write_duration(lsr, atts.dur, "dur"); + lsr_write_anim_fill(lsr, atts.smil_fill); + lsr_write_anim_repeat(lsr, atts.repeatCount); + lsr_write_repeat_duration(lsr, atts.repeatDur); + lsr_write_anim_restart(lsr, atts.restart); + lsr_write_anim_value(lsr, atts.to, "to"); + + lsr_write_float_list(lsr, atts.keyPoints, "keyPoints"); + if (atts.path) { + GF_LSR_WRITE_INT(lsr, 1, 1, "hasPath"); + lsr_write_path_type(lsr, atts.path, "path"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasPath"); + } + lsr_write_rotate_type(lsr, atts.rotate, "rotate"); + + lsr_write_href_anim(lsr, atts.xlink_href, parent); + GF_LSR_WRITE_INT(lsr, (atts.lsr_enabled && *atts.lsr_enabled) ? 1 : 0, 1, "enabled"); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_animateTransform(GF_LASeRCodec *lsr, SVG_Element *elt, SVG_Element *parent) +{ + u32 type; + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_animatable(lsr, atts.attributeName, atts.xlink_href, "attributeName"); + + /*no default value for type or we MUST code it in LASeR ...*/ + type = atts.transform_type ? *atts.transform_type : SVG_TRANSFORM_TRANSLATE; + /*enumeration rotate{0} scale{1} skewX{2} skewY{3} translate{4}*/ + switch (type) { + case SVG_TRANSFORM_ROTATE: GF_LSR_WRITE_INT(lsr, 0, 3, "rotscatra"); break; + case SVG_TRANSFORM_SCALE: GF_LSR_WRITE_INT(lsr, 1, 3, "rotscatra"); break; + case SVG_TRANSFORM_SKEWX: GF_LSR_WRITE_INT(lsr, 2, 3, "rotscatra"); break; + case SVG_TRANSFORM_SKEWY: GF_LSR_WRITE_INT(lsr, 3, 3, "rotscatra"); break; + case SVG_TRANSFORM_TRANSLATE: GF_LSR_WRITE_INT(lsr, 4, 3, "rotscatra"); break; + } + + lsr_write_accumulate(lsr, atts.accumulate); + lsr_write_additive(lsr, atts.additive); + lsr_write_anim_value(lsr, atts.by, "by"); + lsr_write_calc_mode(lsr, atts.calcMode); + lsr_write_anim_value(lsr, atts.from, "from"); + lsr_write_fraction_12(lsr, atts.keySplines, "keySplines"); + lsr_write_fraction_12(lsr, atts.keyTimes, "keyTimes"); + lsr_write_anim_values(lsr, atts.values, "values"); + lsr_write_attribute_type(lsr, &atts); + lsr_write_smil_times(lsr, atts.begin, "begin", 1); + lsr_write_duration(lsr, atts.dur, "dur"); + lsr_write_anim_fill(lsr, atts.smil_fill); + lsr_write_anim_repeat(lsr, atts.repeatCount); + lsr_write_repeat_duration(lsr, atts.repeatDur); + lsr_write_anim_restart(lsr, atts.restart); + lsr_write_anim_value(lsr, atts.to, "to"); + + lsr_write_href_anim(lsr, atts.xlink_href, parent); + GF_LSR_WRITE_INT(lsr, (atts.lsr_enabled && *atts.lsr_enabled) ? 1 : 0, 1, "enabled"); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_audio(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_smil_times(lsr, atts.begin, "begin", 1); + lsr_write_duration(lsr, atts.dur, "dur"); + GF_LSR_WRITE_INT(lsr, atts.externalResourcesRequired ? *atts.externalResourcesRequired : 0, 1, "externalResourcesRequired"); + lsr_write_anim_repeat(lsr, atts.repeatCount); + lsr_write_repeat_duration(lsr, atts.repeatDur); + lsr_write_anim_restart(lsr, atts.restart); + lsr_write_sync_behavior(lsr, atts.syncBehavior, "syncBehavior"); + lsr_write_sync_tolerance(lsr, atts.syncTolerance, "syncBehavior"); + lsr_write_content_type(lsr, atts.xlink_type, "type"); + lsr_write_href(lsr, atts.xlink_href); + + lsr_write_clip_time(lsr, atts.clipBegin, "clipBegin"); + lsr_write_clip_time(lsr, atts.clipEnd, "clipEnd"); + GF_LSR_WRITE_INT(lsr, atts.syncReference ? 1 : 0, 1, "hasSyncReference"); + if (atts.syncReference) lsr_write_any_uri(lsr, atts.syncReference, "syncReference"); + + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_circle(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + lsr_write_coordinate_ptr(lsr, atts.cx, 1, "cx"); + lsr_write_coordinate_ptr(lsr, atts.cy, 1, "cy"); + lsr_write_coordinate_ptr(lsr, atts.r, 0, "r"); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_conditional(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + GF_DOMUpdates *up; + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_smil_times(lsr, atts.begin, "begin", 1); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + GF_LSR_WRITE_INT(lsr, (atts.lsr_enabled && *atts.lsr_enabled) ? 1 : 0, 1, "enabled"); + lsr_write_any_attribute(lsr, elt, 1); + up = elt->children ? (GF_DOMUpdates*)elt->children->node : NULL; + lsr_write_command_list(lsr, up ? up->updates : NULL, elt, 0); + lsr_write_private_attributes(lsr, elt); +} + +static void lsr_write_cursorManager(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_coordinate_ptr(lsr, atts.x, 1, "x"); + lsr_write_coordinate_ptr(lsr, atts.x, 1, "y"); + lsr_write_href(lsr, atts.xlink_href); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_data(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_defs(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_ellipse(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + lsr_write_coordinate_ptr(lsr, atts.cx, 1, "cx"); + lsr_write_coordinate_ptr(lsr, atts.cy, 1, "cy"); + lsr_write_coordinate_ptr(lsr, atts.rx, 0, "rx"); + lsr_write_coordinate_ptr(lsr, atts.ry, 0, "ry"); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_foreignObject(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired&&*atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + lsr_write_coordinate_ptr(lsr, atts.height, 0, "height"); + lsr_write_coordinate_ptr(lsr, atts.width, 0, "width"); + lsr_write_coordinate_ptr(lsr, atts.x, 1, "x"); + lsr_write_coordinate_ptr(lsr, atts.y, 1, "y"); + + lsr_write_any_attribute(lsr, elt, 1); +/* TODO + bit(1) opt_group; + if(opt_group) { + vluimsbf5 occ1; + for(int t=0;tprev_g, &same_fill, NULL, 0) + && same_fill + ) { + /*samegType*/ + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_sameg, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + is_same = 1; + } else { + /*gType*/ + if (!ommit_tag) GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_g, 6, "ch4"); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + lsr_write_any_attribute(lsr, elt, 1); + lsr->prev_g = elt; + } + lsr_write_group_content(lsr, elt, is_same); +} +static void lsr_write_image(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + lsr_write_coordinate_ptr(lsr, atts.height, 1, "height"); + + if (atts.opacity && (atts.opacity->type == SVG_NUMBER_VALUE)) { + GF_LSR_WRITE_INT(lsr, 1, 1, "opacity"); + lsr_write_fixed_clamp(lsr, atts.opacity->value, "opacity"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "opacity"); + } + lsr_write_preserve_aspect_ratio(lsr, atts.preserveAspectRatio); + + lsr_write_content_type(lsr, atts.xlink_type, "type"); + lsr_write_coordinate_ptr(lsr, atts.width, 1, "width"); + lsr_write_coordinate_ptr(lsr, atts.x, 1, "x"); + lsr_write_coordinate_ptr(lsr, atts.y, 1, "y"); + lsr_write_href(lsr, atts.xlink_href); + lsr_write_transform_behavior(lsr, atts.transformBehavior); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_line(GF_LASeRCodec *lsr, SVG_Element *elt, Bool ommit_tag) +{ + Bool same_fill; + Bool is_same = 0; + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + if (!ommit_tag && lsr_elt_has_same_base(lsr, &atts, lsr->prev_line, &same_fill, NULL, 0) + && same_fill + ) { + /*samelineType*/ + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_sameline, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + is_same = 1; + } else { + /*lineType*/ + if (!ommit_tag) GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_line, 6, "ch4"); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + lsr_write_stroke(lsr, elt, &atts); + } + + lsr_write_coordinate_ptr(lsr, atts.x1, 1, "x1"); + lsr_write_coordinate_ptr(lsr, atts.x2, 0, "x2"); + lsr_write_coordinate_ptr(lsr, atts.y1, 1, "y1"); + lsr_write_coordinate_ptr(lsr, atts.y2, 0, "y2"); + if (!is_same) { + lsr_write_any_attribute(lsr, elt, 1); + lsr->prev_line = elt; + } + lsr_write_group_content(lsr, elt, is_same); +} +static void lsr_write_linearGradient(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + lsr_write_stroke(lsr, elt, &atts); + lsr_write_gradient_units(lsr, atts.gradientUnits); + lsr_write_coordinate_ptr(lsr, atts.x1, 1, "x1"); + lsr_write_coordinate_ptr(lsr, atts.x2, 1, "x2"); + lsr_write_coordinate_ptr(lsr, atts.y1, 1, "y1"); + lsr_write_coordinate_ptr(lsr, atts.y2, 1, "y2"); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_mpath(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_href(lsr, atts.xlink_href); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_path(GF_LASeRCodec *lsr, SVG_Element *elt, Bool ommit_tag) +{ + Bool same_fill; + Bool is_same = 0; + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + if (!ommit_tag && lsr_elt_has_same_base(lsr, &atts, lsr->prev_path, &same_fill, NULL, 0) ) { + is_same = 1; + if (!same_fill) { + /*samepathfillType*/ + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_samepathfill, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + } else { + /*samepathType*/ + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_samepath, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + } + lsr_write_path_type(lsr, atts.d, "d"); + } else { + /*pathType*/ + if (!ommit_tag) GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_path, 6, "ch4"); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + lsr_write_stroke(lsr, elt, &atts); + lsr_write_path_type(lsr, atts.d, "d"); + if (atts.pathLength) { + GF_LSR_WRITE_INT(lsr, 1, 1, "hasPathLength"); + lsr_write_fixed_16_8(lsr, atts.pathLength->value, "pathLength"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasPathLength"); + } + lsr_write_any_attribute(lsr, elt, 1); + lsr->prev_path = elt; + } + lsr_write_group_content(lsr, elt, is_same); +} +static void lsr_write_polygon(GF_LASeRCodec *lsr, SVG_Element *elt, Bool is_polyline, Bool ommit_tag) +{ + Bool same_fill, same_stroke; + Bool same_type = 0; + + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + if (!ommit_tag && lsr_elt_has_same_base(lsr, &atts, lsr->prev_polygon, &same_fill, &same_stroke, 1) + ) { + if (same_fill && same_stroke) same_type = 1; + else if (same_fill) same_type = 3; + else if (same_stroke) same_type = 2; + } + + if (same_type) { + /*samepolylineType / samepolygonType */ + u8 type = is_polyline ? LSR_SCENE_CONTENT_MODEL_samepolyline : LSR_SCENE_CONTENT_MODEL_samepolygon; + /*samepolylinefillType / samepolygonfillType */ + if (same_type==2) type = is_polyline ? LSR_SCENE_CONTENT_MODEL_samepolylinefill : LSR_SCENE_CONTENT_MODEL_samepolygonfill; + /*samepolylinestrokeType / samepolygonstrokeType*/ + else if (same_type==3) type = is_polyline ? LSR_SCENE_CONTENT_MODEL_samepolylinestroke : LSR_SCENE_CONTENT_MODEL_samepolygonstroke; + + GF_LSR_WRITE_INT(lsr, type, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + if (same_type==2) lsr_write_fill(lsr, elt, &atts); + else if (same_type==3) lsr_write_stroke(lsr, elt, &atts); + lsr_write_point_sequence(lsr, atts.points, "points"); + } else { + /*polyline/polygon*/ + if (!ommit_tag) GF_LSR_WRITE_INT(lsr, is_polyline ? LSR_SCENE_CONTENT_MODEL_polyline : LSR_SCENE_CONTENT_MODEL_polygon, 6, "ch4"); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + lsr_write_stroke(lsr, elt, &atts); + lsr_write_point_sequence(lsr, atts.points, "points"); + lsr_write_any_attribute(lsr, elt, 1); + lsr->prev_polygon = elt; + } + lsr_write_group_content(lsr, elt, same_type); +} +static void lsr_write_radialGradient(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + lsr_write_stroke(lsr, elt, &atts); + lsr_write_coordinate_ptr(lsr, atts.cx, 1, "cx"); + lsr_write_coordinate_ptr(lsr, atts.cy, 1, "cy"); + lsr_write_gradient_units(lsr, atts.gradientUnits); + lsr_write_coordinate_ptr(lsr, atts.r, 1, "r"); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_rect(GF_LASeRCodec *lsr, SVG_Element *elt, Bool ommit_tag) +{ + Bool same_type = 0; + Bool same_fill; + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + if (!ommit_tag && lsr_elt_has_same_base(lsr, &atts, lsr->prev_rect, &same_fill, NULL, 0) + ) { + if (!same_fill) { + same_type = 2; + /*samerectfillType*/ + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_samerectfill, 6, "ch4"); + } else { + same_type = 1; + /*samerectType*/ + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_samerect, 6, "ch4"); + } + lsr_write_id(lsr, (GF_Node *) elt); + if (same_type==2) lsr_write_fill(lsr, elt, &atts); + lsr_write_coordinate_ptr(lsr, atts.height, 0, "height"); + lsr_write_coordinate_ptr(lsr, atts.width, 0, "width"); + lsr_write_coordinate_ptr(lsr, atts.x, 1, "x"); + lsr_write_coordinate_ptr(lsr, atts.y, 1, "y"); + } else { + /*rectType*/ + if (!ommit_tag) GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_rect, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + lsr_write_stroke(lsr, elt, &atts); + lsr_write_coordinate_ptr(lsr, atts.height, 0, "height"); + lsr_write_coordinate_ptr(lsr, atts.rx, 1, "rx"); + lsr_write_coordinate_ptr(lsr, atts.ry, 1, "ry"); + lsr_write_coordinate_ptr(lsr, atts.width, 0, "width"); + lsr_write_coordinate_ptr(lsr, atts.x, 1, "x"); + lsr_write_coordinate_ptr(lsr, atts.y, 1, "y"); + lsr_write_any_attribute(lsr, elt, 1); + lsr->prev_rect = elt; + } + lsr_write_group_content(lsr, elt, same_type); +} + +static void lsr_write_rectClip(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + if (atts.size) { + GF_LSR_WRITE_INT(lsr, 1, 1, "size"); + lsr_write_coordinate(lsr, atts.size->width, 0, "width"); + lsr_write_coordinate(lsr, atts.size->height, 0, "height"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "size"); + } + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_script(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + lsr_write_script_type(lsr, atts.xlink_type); + lsr_write_href(lsr, atts.xlink_href); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_selector(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + + GF_LSR_WRITE_INT(lsr, atts.choice ? 1 : 0, 1, "hasChoice"); + if (atts.choice) { + if (atts.choice->type==LASeR_CHOICE_N) { + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + GF_LSR_WRITE_INT(lsr, atts.choice->choice_index, 8, "value"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + GF_LSR_WRITE_INT(lsr, atts.choice->type, 1, "type"); + } + } + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_set(GF_LASeRCodec *lsr, SVG_Element *elt, SVG_Element *parent) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_animatable(lsr, atts.attributeName, atts.xlink_href, "attributeName"); + lsr_write_attribute_type(lsr, &atts); + lsr_write_smil_times(lsr, atts.begin, "begin", 1); + lsr_write_duration(lsr, atts.dur, "dur"); + lsr_write_anim_fill(lsr, atts.smil_fill); + lsr_write_anim_repeat(lsr, atts.repeatCount); + lsr_write_repeat_duration(lsr, atts.repeatDur); + lsr_write_anim_restart(lsr, atts.restart); + lsr_write_anim_value(lsr, atts.to, "to"); + lsr_write_href_anim(lsr, atts.xlink_href, parent); + GF_LSR_WRITE_INT(lsr, (atts.lsr_enabled && *atts.lsr_enabled) ? 1 : 0, 1, "enabled"); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_simpleLayout(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + if (atts.delta) { + GF_LSR_WRITE_INT(lsr, 1, 1, "delta"); + lsr_write_coordinate(lsr, atts.delta->width, 0, "width"); + lsr_write_coordinate(lsr, atts.delta->height, 0, "height"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "delta"); + } + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_stop(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + lsr_write_stroke(lsr, elt, &atts); + lsr_write_fixed_16_8(lsr, atts.offset ? atts.offset->value : 0, "offset"); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} +static void lsr_write_svg(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SMIL_Duration snap; + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + lsr_write_stroke(lsr, elt, &atts); + lsr_write_string_attribute(lsr, atts.baseProfile ? *atts.baseProfile : NULL, "baseProfile"); + lsr_write_string_attribute(lsr, atts.contentScriptType ? *atts.contentScriptType : NULL, "contentScriptType"); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + lsr_write_value_with_units(lsr, atts.height, "height"); + + GF_LSR_WRITE_INT(lsr, atts.playbackOrder ? 1 : 0, 1, "hasPlaybackOrder"); + if (atts.playbackOrder) GF_LSR_WRITE_INT(lsr, *atts.playbackOrder, 1, "playbackOrder"); + lsr_write_preserve_aspect_ratio(lsr, atts.preserveAspectRatio); + + snap.clock_value = atts.snapshotTime ? *atts.snapshotTime : 0; + snap.type = snap.clock_value ? SMIL_DURATION_DEFINED : SMIL_DURATION_INDEFINITE; + lsr_write_duration(lsr, atts.snapshotTime ? &snap : NULL, "has_snapshotTime"); + + GF_LSR_WRITE_INT(lsr, atts.syncBehaviorDefault ? 1 : 0, 1, "hasSyncBehavior"); + if (atts.syncBehaviorDefault) { + switch (*atts.syncBehaviorDefault) { + case SMIL_SYNCBEHAVIOR_CANSLIP: GF_LSR_WRITE_INT(lsr, 0, 2, "syncBehavior"); break; + case SMIL_SYNCBEHAVIOR_INDEPENDENT: GF_LSR_WRITE_INT(lsr, 1, 2, "syncBehavior"); break; + case SMIL_SYNCBEHAVIOR_LOCKED: GF_LSR_WRITE_INT(lsr, 3, 2, "syncBehavior"); break; + default: GF_LSR_WRITE_INT(lsr, 2, 2, "syncBehavior"); break; + } + } + GF_LSR_WRITE_INT(lsr, atts.syncToleranceDefault ? 1 : 0, 1, "hasSyncToleranceDefault"); + if (atts.syncToleranceDefault) { + if (atts.syncToleranceDefault->type==SMIL_SYNCTOLERANCE_VALUE) { + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + lsr_write_vluimsbf5(lsr, (u32) (atts.syncToleranceDefault->value*lsr->time_resolution), "value"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + } + } + GF_LSR_WRITE_INT(lsr, atts.timelineBegin ? 1 : 0, 1, "hasTimelineBegin"); + if (atts.timelineBegin) GF_LSR_WRITE_INT(lsr, *atts.timelineBegin, 1, "timelineBegin"); + + lsr_write_string_attribute(lsr, atts.version ? *atts.version : NULL, "version"); + + GF_LSR_WRITE_INT(lsr, atts.viewBox ? 1 : 0, 1, "hasViewBox"); + if (atts.viewBox) { + lsr_write_fixed_16_8(lsr, atts.viewBox->x, "viewbox.x"); + lsr_write_fixed_16_8(lsr, atts.viewBox->y, "viewbox.y"); + lsr_write_fixed_16_8(lsr, atts.viewBox->width, "viewbox.width"); + lsr_write_fixed_16_8(lsr, atts.viewBox->height, "viewbox.height"); + } + lsr_write_value_with_units(lsr, atts.width, "width"); + + GF_LSR_WRITE_INT(lsr, atts.zoomAndPan ? 1 : 0, 1, "hasZoomAndPan"); + if (atts.zoomAndPan) { + GF_LSR_WRITE_INT(lsr, (*atts.zoomAndPan==SVG_ZOOMANDPAN_MAGNIFY) ? 1 : 0, 1, "zoomAndPan"); + } + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_switch(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_text(GF_LASeRCodec *lsr, SVG_Element *elt, Bool ommit_tag) +{ + Bool same_fill; + u32 same_type = 0; + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + if (!ommit_tag && lsr_elt_has_same_base(lsr, &atts, lsr->prev_text, &same_fill, NULL, 0) ) { + if (!same_fill) { + /*sametextfillType*/ + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_sametextfill, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, elt, &atts); + } else { + /*sametextType*/ + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_sametext, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + } + lsr_write_coord_list(lsr, atts.text_x, "x"); + lsr_write_coord_list(lsr, atts.text_y, "y"); + same_type = 1; + } else { + /*textType*/ + if (!ommit_tag) GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_text, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + + GF_LSR_WRITE_INT(lsr, (atts.editable && *atts.editable) ? 1 : 0, 1, "editable"); + lsr_write_float_list(lsr, atts.text_rotate, "rotate"); + lsr_write_coord_list(lsr, atts.text_x, "x"); + lsr_write_coord_list(lsr, atts.text_y, "y"); + lsr_write_any_attribute(lsr, elt, 1); + lsr->prev_text = elt; + } + lsr_write_group_content(lsr, elt, same_type); +} + +static void lsr_write_tspan(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_use(GF_LASeRCodec *lsr, SVG_Element *elt, Bool ommit_tag) +{ + SVGAllAttributes atts; + Bool is_same = 0; + gf_svg_flatten_attributes(elt, &atts); + + if (!ommit_tag && lsr_elt_has_same_base(lsr, &atts, lsr->prev_use, NULL, NULL, 0) + /*TODO check overflow*/ + ) { + is_same = 1; + /*sameuseType*/ + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_sameuse, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_href(lsr, atts.xlink_href); + } else { + /*useType*/ + if (!ommit_tag) GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_use, 6, "ch4"); + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_fill(lsr, (SVG_Element*)elt, &atts); + lsr_write_stroke(lsr, (SVG_Element*)elt, &atts); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + + GF_LSR_WRITE_INT(lsr, atts.overflow ? 1 : 0, 1, "hasOverflow"); + /*one value only??*/ + if (atts.overflow) GF_LSR_WRITE_INT(lsr, 0, 2, "overflow"); + + lsr_write_coordinate_ptr(lsr, atts.x, 1, "x"); + lsr_write_coordinate_ptr(lsr, atts.y, 1, "y"); + lsr_write_href(lsr, atts.xlink_href); + lsr_write_any_attribute(lsr, elt, 1); + lsr->prev_use = elt; + } + lsr_write_group_content(lsr, elt, is_same); +} + +static void lsr_write_video(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + u32 fs_value; + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + fs_value = 0; + if (atts.fullscreen) { + fs_value = *atts.fullscreen + 1; + atts.fullscreen = NULL; + } + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + lsr_write_smil_times(lsr, atts.begin, "begin", 1); + lsr_write_duration(lsr, atts.dur, "dur"); + GF_LSR_WRITE_INT(lsr, (atts.externalResourcesRequired && *atts.externalResourcesRequired) ? 1 : 0, 1, "externalResourcesRequired"); + lsr_write_coordinate_ptr(lsr, atts.height, 1, "height"); + + GF_LSR_WRITE_INT(lsr, atts.overlay ? 1 : 0, 1, "hasOverlay"); + if (atts.overlay) { + GF_LSR_WRITE_INT(lsr, 1, 1, "choice"); + GF_LSR_WRITE_INT(lsr, *atts.overlay, 1, "overlay"); + } + lsr_write_preserve_aspect_ratio(lsr, atts.preserveAspectRatio); + + lsr_write_anim_repeat(lsr, atts.repeatCount); + lsr_write_repeat_duration(lsr, atts.repeatDur); + lsr_write_anim_restart(lsr, atts.restart); + lsr_write_sync_behavior(lsr, atts.syncBehavior, "syncBehavior"); + lsr_write_sync_tolerance(lsr, atts.syncTolerance, "syncBehavior"); + lsr_write_transform_behavior(lsr, atts.transformBehavior); + lsr_write_content_type(lsr, atts.xlink_type, "type"); + lsr_write_coordinate_ptr(lsr, atts.width, 1, "width"); + lsr_write_coordinate_ptr(lsr, atts.x, 1, "x"); + lsr_write_coordinate_ptr(lsr, atts.y, 1, "y"); + lsr_write_href(lsr, atts.xlink_href); + + lsr_write_clip_time(lsr, atts.clipBegin, "clipBegin"); + lsr_write_clip_time(lsr, atts.clipEnd, "clipEnd"); + + GF_LSR_WRITE_INT(lsr, fs_value ? 1 : 0, 1, "hasFullscreen"); + if (atts.fullscreen) GF_LSR_WRITE_INT(lsr, fs_value - 1, 1, "fullscreen"); + + GF_LSR_WRITE_INT(lsr, atts.syncReference ? 1 : 0, 1, "hasSyncReference"); + if (atts.syncReference) lsr_write_any_uri(lsr, atts.syncReference, "syncReference"); + + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_listener(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + SVGAllAttributes atts; + gf_svg_flatten_attributes(elt, &atts); + + lsr_write_id(lsr, (GF_Node *) elt); + lsr_write_rare(lsr, (GF_Node *) elt); + + GF_LSR_WRITE_INT(lsr, atts.defaultAction ? 1 : 0, 1, "hasDefaultAction"); + if (atts.defaultAction) GF_LSR_WRITE_INT(lsr, *atts.defaultAction ? 1 : 0, 1, "defaultAction"); + if (atts.event) { + GF_LSR_WRITE_INT(lsr, 1, 1, "hasEvent"); + lsr_write_event_type(lsr, atts.event->type, atts.event->parameter); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasEvent"); + } + if (atts.handler && (atts.handler->string || (atts.handler->target && gf_node_get_id((GF_Node *)atts.handler->target) ) )) { + GF_LSR_WRITE_INT(lsr, 1, 1, "hasHandler"); + lsr_write_any_uri(lsr, atts.handler, "handler"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasHandler"); + } + if (atts.observer && atts.observer->target && gf_node_get_id((GF_Node *)atts.observer->target) ) { + GF_LSR_WRITE_INT(lsr, 1, 1, "hasObserver"); + lsr_write_codec_IDREF(lsr, atts.observer, "observer"); + } else { + if (atts.observer) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] listener.observer %s not found in scene - skipping it\n", atts.observer->string )); + } + GF_LSR_WRITE_INT(lsr, 0, 1, "hasObserver"); + } + + GF_LSR_WRITE_INT(lsr, atts.phase ? 1 : 0, 1, "hasPhase"); + if (atts.phase) GF_LSR_WRITE_INT(lsr, *atts.phase, 1, "phase"); + + GF_LSR_WRITE_INT(lsr, atts.propagate ? 1 : 0, 1, "hasPropagate"); + if (atts.propagate) GF_LSR_WRITE_INT(lsr, *atts.propagate, 1, "propagate"); + + if (atts.listener_target && atts.listener_target->target && gf_node_get_id((GF_Node *)atts.listener_target->target) ) { + GF_LSR_WRITE_INT(lsr, 1, 1, "hasTarget"); + lsr_write_codec_IDREF(lsr, atts.listener_target, "target"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasTarget"); + } + GF_LSR_WRITE_INT(lsr, (atts.lsr_enabled && *atts.lsr_enabled) ? 1 : 0, 1, "enabled"); + + lsr_write_any_attribute(lsr, elt, 1); + lsr_write_group_content(lsr, elt, 0); +} + +static void lsr_write_scene_content_model(GF_LASeRCodec *lsr, SVG_Element *parent, void *node) +{ + u32 tag = gf_node_get_tag((GF_Node*)node); + + switch(tag) { + case TAG_SVG_a: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_a, 6, "ch4"); + lsr_write_a(lsr, node); + break; + case TAG_SVG_animate: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_animate, 6, "ch4"); + lsr_write_animate(lsr, node, parent); + break; + case TAG_SVG_animateColor: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_animateColor, 6, "ch4"); + lsr_write_animate(lsr, node, parent); + break; + case TAG_SVG_animateMotion: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_animateMotion, 6, "ch4"); + lsr_write_animateMotion(lsr, node, parent); + break; + case TAG_SVG_animateTransform: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_animateTransform, 6, "ch4"); + lsr_write_animateTransform(lsr, node, parent); + break; + case TAG_SVG_audio: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_audio, 6, "ch4"); + lsr_write_audio(lsr, node); + break; + case TAG_SVG_circle: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_circle, 6, "ch4"); + lsr_write_circle(lsr, node); + break; + case TAG_LSR_cursorManager: GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_cursorManager, 6, "ch4"); + lsr_write_cursorManager(lsr, node); + break; + case TAG_SVG_defs: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_defs, 6, "ch4"); + lsr_write_defs(lsr, node); + break; + case TAG_SVG_desc: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_desc, 6, "ch4"); + lsr_write_data(lsr, node); + break; + case TAG_SVG_ellipse: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_ellipse, 6, "ch4"); + lsr_write_ellipse(lsr, node); + break; + case TAG_SVG_foreignObject: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_foreignObject, 6, "ch4"); + lsr_write_foreignObject(lsr, node); + break; + + case TAG_SVG_g: + /*type is written in encoding fct for sameg handling*/ + lsr_write_g(lsr, node, 0); + break; + case TAG_SVG_image: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_image, 6, "ch4"); + lsr_write_image(lsr, node); + break; + case TAG_SVG_line: + /*type is written in encoding fct for sameline handling*/ + lsr_write_line(lsr, node, 0); + break; + case TAG_SVG_linearGradient: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_linearGradient, 6, "ch4"); + lsr_write_linearGradient(lsr, node); + break; + case TAG_SVG_metadata: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_metadata, 6, "ch4"); + lsr_write_data(lsr, node); + break; + case TAG_SVG_mpath: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_mpath, 6, "ch4"); + lsr_write_mpath(lsr, node); + break; + case TAG_SVG_path: + /*type is written in encoding fct for samepath handling*/ + lsr_write_path(lsr, node, 0); + break; + case TAG_SVG_polygon: + /*type is written in encoding fct for samepolygon handling*/ + lsr_write_polygon(lsr, node, 0, 0); + break; + case TAG_SVG_polyline: + /*type is written in encoding fct for samepolyline handling*/ + lsr_write_polygon(lsr, node, 1, 0); + break; + case TAG_SVG_radialGradient: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_radialGradient, 6, "ch4"); + lsr_write_radialGradient(lsr, node); + break; + case TAG_SVG_rect: + /*type is written in encoding fct for samepolyline handling*/ + lsr_write_rect(lsr, node, 0); + break; + case TAG_SVG_script: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_script, 6, "ch4"); + lsr_write_script(lsr, node); + break; + case TAG_SVG_set: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_set, 6, "ch4"); + lsr_write_set(lsr, node, parent); + break; + case TAG_SVG_stop: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_stop, 6, "ch4"); + lsr_write_stop(lsr, node); + break; + case TAG_SVG_switch: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_switch, 6, "ch4"); + lsr_write_switch(lsr, node); + break; + case TAG_SVG_text: + /*type is written in encoding fct for sametext handling*/ + lsr_write_text(lsr, node, 0); + break; + case TAG_SVG_title: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_title, 6, "ch4"); + lsr_write_data(lsr, node); + break; + case TAG_SVG_tspan: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_tspan, 6, "ch4"); + lsr_write_tspan(lsr, node); + break; + case TAG_SVG_use: + /*type is written in encoding fct for sameuse handling*/ + lsr_write_use(lsr, node, 0); + break; + case TAG_SVG_video: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_video, 6, "ch4"); + lsr_write_video(lsr, node); + break; + case TAG_SVG_listener: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_listener, 6, "ch4"); + lsr_write_listener(lsr, node); + break; + case TAG_LSR_conditional: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_conditional, 6, "ch4"); + lsr_write_conditional(lsr, node); + break; + case TAG_LSR_rectClip: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_rectClip, 6, "ch4"); + lsr_write_rectClip(lsr, node); + break; + case TAG_LSR_selector: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_selector, 6, "ch4"); + lsr_write_selector(lsr, node); + break; + case TAG_LSR_simpleLayout: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_simpleLayout, 6, "ch4"); + lsr_write_simpleLayout(lsr, node); + break; +#if 0 + /*case privateElement*/ + case TAG_SVG_extendElement: + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_privateContainer, 6, "ch4"); + lsr_write_private_element_container(lsr); + break; +#endif + + default: +#if 0 + /*case extend*/ + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_element_any, 6, "ch4"); + lsr_write_extend_class(lsr, NULL, 0, node); + break; +#else + /*hack for encoding - needs cleaning*/ + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] node %s not part of LASeR children nodes - skipping\n", gf_node_get_class_name((GF_Node*)node))); + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_textContent, 6, "ch4"); + lsr_write_byte_align_string(lsr, "", "textContent"); + break; +#endif + } +} + +static void lsr_write_update_content_model(GF_LASeRCodec *lsr, SVG_Element *parent, void *node) +{ + u32 tag = gf_node_get_tag((GF_Node*)node); + + if (tag==TAG_LSR_conditional) { + GF_LSR_WRITE_INT(lsr, 1, 1, "ch4"); + GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL2_conditional, 3, "ch61"); + lsr_write_conditional(lsr, node); + } else if (tag==TAG_LSR_cursorManager) { + GF_LSR_WRITE_INT(lsr, 1, 1, "ch4"); + GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL2_cursorManager, 3, "ch61"); + lsr_write_cursorManager(lsr, node); + } else if (tag==TAG_LSR_rectClip) { + GF_LSR_WRITE_INT(lsr, 1, 1, "ch4"); + GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL2_rectClip, 3, "ch61"); + lsr_write_rectClip(lsr, node); + } else if (tag==TAG_LSR_selector) { + GF_LSR_WRITE_INT(lsr, 1, 1, "ch4"); + GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL2_selector, 3, "ch61"); + lsr_write_selector(lsr, node); + } else if (tag==TAG_LSR_simpleLayout) { + GF_LSR_WRITE_INT(lsr, 1, 1, "ch4"); + GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL2_simpleLayout, 3, "ch61"); + lsr_write_simpleLayout(lsr, node); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "ch4"); + switch(tag) { + case TAG_SVG_a: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_a, 6, "ch6"); lsr_write_a(lsr, node); break; + case TAG_SVG_animate: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_animate, 6, "ch6"); lsr_write_animate(lsr, node, parent); break; + case TAG_SVG_animateColor: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_animateColor, 6, "ch6"); lsr_write_animate(lsr, node, parent); break; + case TAG_SVG_animateMotion: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_animateMotion, 6, "ch6"); lsr_write_animateMotion(lsr, node, parent); break; + case TAG_SVG_animateTransform: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_animateTransform, 6, "ch6"); lsr_write_animateTransform(lsr, node, parent); break; + case TAG_SVG_audio: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_audio, 6, "ch6"); lsr_write_audio(lsr, node); break; + case TAG_SVG_circle: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_circle, 6, "ch6"); lsr_write_circle(lsr, node); break; + case TAG_SVG_defs: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_defs, 6, "ch6"); lsr_write_defs(lsr, node); break; + case TAG_SVG_desc: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_desc, 6, "ch6"); lsr_write_data(lsr, node); break; + case TAG_SVG_ellipse: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_ellipse, 6, "ch6"); lsr_write_ellipse(lsr, node); break; + case TAG_SVG_foreignObject: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_foreignObject, 6, "ch6"); lsr_write_foreignObject(lsr, node); break; + case TAG_SVG_g: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_g, 6, "ch6"); lsr_write_g(lsr, node, 1); break; + case TAG_SVG_image: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_image, 6, "ch6"); lsr_write_image(lsr, node); break; + case TAG_SVG_line: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_line, 6, "ch6"); lsr_write_line(lsr, node, 1); break; + case TAG_SVG_linearGradient: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_linearGradient, 6, "ch6"); lsr_write_linearGradient(lsr, node); break; + case TAG_SVG_metadata: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_metadata, 6, "ch6"); lsr_write_data(lsr, node); break; + case TAG_SVG_mpath: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_mpath, 6, "ch6"); lsr_write_mpath(lsr, node); break; + case TAG_SVG_path: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_path, 6, "ch6"); lsr_write_path(lsr, node, 1); break; + case TAG_SVG_polygon: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_polygon, 6, "ch6"); lsr_write_polygon(lsr, node, 0, 1); break; + case TAG_SVG_polyline: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_polyline, 6, "ch6"); lsr_write_polygon(lsr, node, 1, 1); break; + case TAG_SVG_radialGradient: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_radialGradient, 6, "ch6"); lsr_write_radialGradient(lsr, node); break; + case TAG_SVG_rect: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_rect, 6, "ch6"); lsr_write_rect(lsr, node, 1); break; + case TAG_SVG_script: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_script, 6, "ch6"); lsr_write_script(lsr, node); break; + case TAG_SVG_set: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_set, 6, "ch6"); lsr_write_set(lsr, node, parent); break; + case TAG_SVG_stop: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_stop, 6, "ch6"); lsr_write_stop(lsr, node); break; + case TAG_SVG_svg: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_svg, 6, "ch6"); lsr_write_svg(lsr, node); break; + case TAG_SVG_switch: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_switch, 6, "ch6"); lsr_write_switch(lsr, node); break; + case TAG_SVG_text: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_text, 6, "ch6"); lsr_write_text(lsr, node, 1); break; + case TAG_SVG_title: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_title, 6, "ch6"); lsr_write_data(lsr, node); break; + case TAG_SVG_tspan: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_tspan, 6, "ch6"); lsr_write_tspan(lsr, node); break; + case TAG_SVG_use: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_use, 6, "ch6"); lsr_write_use(lsr, node, 1); break; + case TAG_SVG_video: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_video, 6, "ch6"); lsr_write_video(lsr, node); break; + case TAG_SVG_listener: GF_LSR_WRITE_INT(lsr, LSR_UPDATE_CONTENT_MODEL_listener, 6, "ch6"); lsr_write_listener(lsr, node); break; + } + } +} + +static void lsr_write_group_content(GF_LASeRCodec *lsr, SVG_Element *elt, Bool skip_object_content) +{ + GF_ChildNodeItem *l; + u32 count; + if (!skip_object_content) lsr_write_private_attributes(lsr, elt); + + count = gf_node_list_get_count(elt->children); + + l = elt->children; + if (!count) { + GF_LSR_WRITE_INT(lsr, 0, 1, "opt_group"); + return; + } + GF_LSR_WRITE_INT(lsr, 1, 1, "opt_group"); + lsr_write_vluimsbf5(lsr, count, "occ0"); + + while (l) { + if (gf_node_get_tag(l->node)==TAG_DOMText) { + GF_DOMText *txt = (GF_DOMText *)l->node; + GF_LSR_WRITE_INT(lsr, LSR_SCENE_CONTENT_MODEL_textContent, 6, "ch4"); + lsr_write_byte_align_string(lsr, txt->textContent, "textContent"); + } else { + lsr_write_scene_content_model(lsr, elt, l->node); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[LASeR] ############## end %s ###########\n", gf_node_get_class_name((GF_Node*)l->node))); + } + l = l->next; + } +} + +static void lsr_write_update_value(GF_LASeRCodec *lsr, SVG_Element *elt, u32 fieldType, u32 att_tag, u32 transformType, void *val, Bool is_indexed) +{ + SVG_Number *n; + if (is_indexed) { + assert(gf_list_count(*(GF_List **)val)); + switch (fieldType) { + case SVG_Points_datatype:/*points*/ + { + GF_Point2D *pt = gf_list_get(*(GF_List **)val, 0); + lsr_write_coordinate(lsr, pt->x, 0, "x"); + lsr_write_coordinate(lsr, pt->y, 0, "y"); + } + break; + case SMIL_Times_datatype:/*smil_times*/ + { + SMIL_Time *st = gf_list_get(*(GF_List **)val, 0); + lsr_write_smil_time(lsr, st); + } + break; + case SMIL_KeyPoints_datatype:/*0to1*/ + { + Fixed *f = gf_list_get(*(GF_List **)val, 0); + lsr_write_fixed_clamp(lsr, *f, "v"); + } + break; + case SMIL_KeySplines_datatype:/*float*/ + { + Fixed *f = gf_list_get(*(GF_List **)val, 0); + lsr_write_fixed_16_8(lsr, *f, "v"); + } + break; + case SMIL_KeyTimes_datatype:/*keyTime*/ + { + Fixed *v = gf_list_get(*(GF_List **)val, 0); + Fixed f = *v; + if ((f==0) || (f==FIX_ONE)) { + GF_LSR_WRITE_INT(lsr, 1, 1, "hasShort"); + GF_LSR_WRITE_INT(lsr, (f==0) ? 0 : 1, 1, "isZero"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "hasShort"); + lsr_write_fixed_clamp(lsr, f, "timevalue"); + } + } + break; + case SVG_StrokeDashArray_datatype:/*float*/ + case SVG_ViewBox_datatype:/*float*/ + break; + case DOM_StringList_datatype: + if (att_tag==TAG_SVG_ATT_requiredFeatures) { + /*int*/ + } + break; + } + } else { + switch (fieldType) { + case SVG_Boolean_datatype: + GF_LSR_WRITE_INT(lsr, *(SVG_Boolean*)val ? 1 : 0, 1, "val"); + break; + case SVG_Paint_datatype: + lsr_write_paint(lsr, (SVG_Paint *)val, "val"); + break; + case SVG_Transform_datatype: + lsr_write_matrix(lsr, (SVG_Transform*)val); + break; + case SVG_Transform_Scale_datatype: + lsr_write_fixed_16_8(lsr, ((SVG_Point *)val)->x, "scale_x"); + lsr_write_fixed_16_8(lsr, ((SVG_Point *)val)->y, "scale_y"); + break; + case LASeR_Size_datatype: + case SVG_Transform_Translate_datatype: + lsr_write_coordinate(lsr, ((SVG_Point *)val)->x, 0, "translation_x"); + lsr_write_coordinate(lsr, ((SVG_Point *)val)->y, 0, "translation_y"); + break; + case SVG_Transform_Rotate_datatype: + GF_LSR_WRITE_INT(lsr, 0, 1, "isDefaultValue"); + GF_LSR_WRITE_INT(lsr, 0, 1, "escapeFlag"); + lsr_write_fixed_16_8(lsr, ((SVG_Point_Angle*)val)->angle, "rotate"); + break; + case SVG_Number_datatype: + case SVG_FontSize_datatype: + case SVG_Length_datatype: + n = (SVG_Number*)val; + switch (att_tag) { + /*fractions*/ + case TAG_SVG_ATT_audio_level: + case TAG_SVG_ATT_fill_opacity: + case TAG_SVG_ATT_offset: + case TAG_SVG_ATT_opacity: + case TAG_SVG_ATT_solid_opacity: + case TAG_SVG_ATT_stop_opacity: + case TAG_SVG_ATT_stroke_opacity: + case TAG_SVG_ATT_viewport_fill_opacity: + if (n->type==SVG_NUMBER_INHERIT) { + GF_LSR_WRITE_INT(lsr, 1, 1, "isDefaultValue"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "isDefaultValue"); + lsr_write_fixed_clamp(lsr, n->value, "val"); + } + break; + case TAG_SVG_ATT_width: + case TAG_SVG_ATT_height: + if (elt->sgprivate->tag==TAG_SVG_svg) { + lsr_write_value_with_units(lsr, n, "val"); + } else { + lsr_write_coordinate(lsr, n->value, 0, "val"); + } + break; + default: + if (n->type==SVG_NUMBER_INHERIT) { + GF_LSR_WRITE_INT(lsr, 1, 1, "isDefaultValue"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "isDefaultValue"); + GF_LSR_WRITE_INT(lsr, 0, 1, "escapeFlag"); + lsr_write_fixed_16_8(lsr, n->value, "val"); + } + } + break; + case SVG_Rotate_datatype: + n = (SVG_Number*)val; + if (n->type==SVG_NUMBER_INHERIT) { + GF_LSR_WRITE_INT(lsr, 1, 1, "isDefaultValue"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "isDefaultValue"); + if ((n->type == SVG_NUMBER_AUTO) || (n->type == SVG_NUMBER_AUTO_REVERSE)) { + GF_LSR_WRITE_INT(lsr, 1, 1, "escapeFlag"); + GF_LSR_WRITE_INT(lsr, (n->type == SVG_NUMBER_AUTO) ? 0 : 1, 2, "rotate"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "escapeFlag"); + lsr_write_fixed_16_8(lsr, n->value, "rotate"); + } + } + break; + case SVG_Coordinate_datatype: + n = (SVG_Number*)val; + lsr_write_coordinate(lsr, n->value, 0, "val"); + break; + case SVG_Coordinates_datatype: + GF_LSR_WRITE_INT(lsr, 0, 1, "isInherit"); + GF_LSR_WRITE_INT(lsr, 0, 1, "escapeFlag"); + lsr_write_float_list(lsr, (GF_List **)val, "val"); + break; + case XMLRI_datatype: + if ((att_tag==TAG_XLINK_ATT_href) || (att_tag==TAG_SVG_ATT_syncReference)) { + lsr_write_any_uri(lsr, (XMLRI*)val, "val"); + } else if (((XMLRI*)val)->target) { + GF_LSR_WRITE_INT(lsr, 0, 1, "isDefault"); + GF_LSR_WRITE_INT(lsr, 0, 1, "isEscape"); + lsr_write_vluimsbf5(lsr, gf_node_get_id( ((XMLRI*)val)->target) - 1, "ID"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "isDefault"); + } + break; + case SVG_Focus_datatype: + if ( (((SVG_Focus*)val)->type == SVG_FOCUS_AUTO) || !((SVG_Focus*)val)->target.target) { + GF_LSR_WRITE_INT(lsr, 1, 1, "isDefault"); + } else if (((SVG_Focus*)val)->type == SVG_FOCUS_SELF) { + GF_LSR_WRITE_INT(lsr, 0, 1, "isDefault"); + GF_LSR_WRITE_INT(lsr, 1, 1, "isEscape"); + GF_LSR_WRITE_INT(lsr, 1, 2, "escapeEnumVal"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "isDefault"); + GF_LSR_WRITE_INT(lsr, 0, 1, "isEscape"); + lsr_write_vluimsbf5(lsr, gf_node_get_id( ((SVG_Focus*)val)->target.target) - 1, "ID"); + } + break; + + case DOM_String_datatype: + case SVG_ContentType_datatype: + case SVG_LanguageID_datatype: + lsr_write_byte_align_string(lsr, val ? *(DOM_String *)val : (char *)"", "val"); + break; + case SVG_Motion_datatype: + lsr_write_coordinate(lsr, ((GF_Matrix2D *)val)->m[2], 0, "pointValueX"); + lsr_write_coordinate(lsr, ((GF_Matrix2D *)val)->m[5], 0, "pointValueY"); + break; + case SVG_Points_datatype: + lsr_write_point_sequence(lsr, (GF_List **)val, "val"); + break; + case SVG_PathData_datatype: + lsr_write_path_type(lsr, (SVG_PathData*)val, "val"); + break; + case LASeR_Choice_datatype: + { + LASeR_Choice *ch = (LASeR_Choice *)val; + GF_LSR_WRITE_INT(lsr, (ch->type==LASeR_CHOICE_ALL) ? 1 : 0, 1, "isDefaultValue"); + if (ch->type!=LASeR_CHOICE_ALL) { + GF_LSR_WRITE_INT(lsr, (ch->type==LASeR_CHOICE_NONE) ? 1 : 0, 1, "escapeFlag"); + if (ch->type==LASeR_CHOICE_NONE) { + GF_LSR_WRITE_INT(lsr, LASeR_CHOICE_NONE, 2, "escapeEnum"); + } else { + lsr_write_vluimsbf5(lsr, ((LASeR_Choice *)val)->choice_index, "value"); + } + } + } + break; + case SVG_ViewBox_datatype: + { + SVG_ViewBox *b = (SVG_ViewBox *)val; + GF_LSR_WRITE_INT(lsr, 0, 1, "isDefaultValue"); + GF_LSR_WRITE_INT(lsr, 0, 1, "escapeFlag"); + lsr_write_vluimsbf5(lsr, 4, "nb1"); + lsr_write_fixed_16_8(lsr, b->x, "x"); + lsr_write_fixed_16_8(lsr, b->y, "y"); + lsr_write_fixed_16_8(lsr, b->width, "width"); + lsr_write_fixed_16_8(lsr, b->width, "height"); + } + break; + case SVG_FontFamily_datatype: + { + SVG_FontFamily *ff = (SVG_FontFamily *)val; + GF_LSR_WRITE_INT(lsr, (ff->type == SVG_FONTFAMILY_INHERIT) ? 1 : 0, 1, "isDefaultValue"); + if (ff->type != SVG_FONTFAMILY_INHERIT) { + u32 idx = lsr_get_font_index(lsr, ff); + if (idx==-1) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] corrupted font table while encoding update value\n")); + idx=0; + } + GF_LSR_WRITE_INT(lsr, 0, 1, "escapeFlag"); + lsr_write_vluimsbf5(lsr, idx, "nb1"); + } + } + break; + default: + if ((fieldType>=SVG_FillRule_datatype) && (fieldType<=SVG_LAST_U8_PROPERTY)) { + u8 v = *(u8 *)val; + /*TODO fixme, check inherit/default values*/ + if (!v) { + GF_LSR_WRITE_INT(lsr, 1, 1, "isDefaultValue"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "isDefaultValue"); + lsr_write_vluimsbf5(lsr, v, "val"); + } + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[LASeR] update value not implemented for type %d - please fix or report\n", fieldType)); + } + } + } +} +/*FIXME - support for scale/translate/rotation*/ +static GF_Err lsr_write_add_replace_insert(GF_LASeRCodec *lsr, GF_Command *com) +{ + GF_CommandField *field; + u8 type = 0; + Bool is_text_node = 0; + u32 field_type, tr_type = 0; + if (com->tag==GF_SG_LSR_REPLACE) type = LSR_UPDATE_REPLACE; + else if (com->tag==GF_SG_LSR_ADD) type = LSR_UPDATE_ADD; + else if (com->tag==GF_SG_LSR_INSERT) type = LSR_UPDATE_INSERT; + else return GF_BAD_PARAM; + + GF_LSR_WRITE_INT(lsr, type, 4, "ch4"); + field = (GF_CommandField*)gf_list_get(com->command_fields, 0); + field_type = 0; + if (field && ( !field->new_node || (field->fieldIndex==TAG_LSR_ATT_children) ) && !field->node_list) { + s32 attType = 0; + field_type = field->fieldType; + attType = gf_lsr_anim_type_from_attribute(field->fieldIndex); + if (attType== -1) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[LASeR] Attribute %s of element %s is not updatable\n", gf_svg_get_attribute_name(com->node, field->fieldIndex), gf_node_get_class_name(com->node) )); + return lsr->last_error = GF_BAD_PARAM; + } + GF_LSR_WRITE_INT(lsr, 1, 1, "has_attributeName"); + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + GF_LSR_WRITE_INT(lsr, attType, 8, "attributeName"); + } + /*single text */ + else if (field->new_node->sgprivate->tag==TAG_DOMText) { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_attributeName"); + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + GF_LSR_WRITE_INT(lsr, LSR_UPDATE_TYPE_TEXT_CONTENT, 8, "attributeName"); + is_text_node = 1; + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_attributeName"); + } + /*if not add*/ + if (type!=LSR_UPDATE_ADD) { + if (!field || field->pos<0) { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_index"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_index"); + lsr_write_vluimsbf5(lsr, (u32) field->pos, "index"); + } + } + if (type!=LSR_UPDATE_INSERT) { + if (com->fromNodeID) { + GF_Node *opNode; + u8 opAttType; + opNode = gf_sg_find_node(lsr->sg, com->fromNodeID); + opAttType = gf_lsr_anim_type_from_attribute(com->fromFieldIndex); + GF_LSR_WRITE_INT(lsr, 1, 1, "has_operandAttribute"); + GF_LSR_WRITE_INT(lsr, opAttType, 8, "operandAttribute"); + GF_LSR_WRITE_INT(lsr, 1, 1, "has_operandElementId"); + lsr_write_vluimsbf5(lsr, com->fromNodeID-1, "operandElementId"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_operandAttribute"); + GF_LSR_WRITE_INT(lsr, 0, 1, "has_operandElementId"); + } + } + lsr_write_codec_IDREF_Node(lsr, com->node, "ref"); + if (field && !field->new_node && !field->node_list && !com->fromNodeID) { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_value"); + lsr_write_update_value(lsr, (SVG_Element *)com->node, field_type, field->fieldIndex, tr_type, field->field_ptr, (field->pos>=0) ? 1 : 0); + } else if (is_text_node) { + GF_DOMText *t = (GF_DOMText *)field->new_node; + GF_LSR_WRITE_INT(lsr, 1, 1, "has_value"); + lsr_write_update_value(lsr, (SVG_Element *)com->node, DOM_String_datatype, field->fieldIndex, 0, &t->textContent, (field->pos>=0) ? 1 : 0); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_value"); + } + lsr_write_any_attribute(lsr, NULL, 1); + /*if not add*/ + if (type!=LSR_UPDATE_ADD) { + if (field && field->node_list && !com->fromNodeID) { + GF_ChildNodeItem *l = field->node_list; + u32 count = gf_node_list_get_count(l); + GF_LSR_WRITE_INT(lsr, 1, 1, "opt_group"); + + if (type==LSR_UPDATE_REPLACE) lsr_write_vluimsbf5(lsr, count, "count"); + + while (l) { + lsr_write_update_content_model(lsr, (SVG_Element*) com->node, l->node); + l = l->next; + if (type==LSR_UPDATE_INSERT) break; + } + } else if (field && field->new_node && !is_text_node) { + GF_LSR_WRITE_INT(lsr, 1, 1, "opt_group"); + if (type==LSR_UPDATE_REPLACE) lsr_write_vluimsbf5(lsr, 1, "count"); + lsr_write_update_content_model(lsr, (SVG_Element*) com->node, field->new_node); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "opt_group"); + } + } + return GF_OK; +} + +static GF_Err lsr_write_command_list(GF_LASeRCodec *lsr, GF_List *com_list, SVG_Element *cond, Bool first_implicit) +{ + GF_CommandField *field; + u32 i, count; + u32 detail; + GF_BitStream *old_bs; + + count = com_list ? gf_list_count(com_list) : 0; + + old_bs = NULL; + if (cond) { + /*use temp bitstream for encoding conditional*/ + old_bs = lsr->bs; + lsr->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + } + assert(count>=first_implicit); + + lsr_write_vluimsbf5(lsr, count-first_implicit, "occ0"); + + if (!com_list) goto exit; + + count = gf_list_count(com_list); + for (i=0; itag) { + case GF_SG_LSR_NEW_SCENE: + case GF_SG_LSR_REFRESH_SCENE: + GF_LSR_WRITE_INT(lsr, (com->tag==GF_SG_LSR_REFRESH_SCENE) ? LSR_UPDATE_REFRESH_SCENE : LSR_UPDATE_NEW_SCENE, 4, "ch4"); + if (com->tag==GF_SG_LSR_REFRESH_SCENE) lsr_write_vluimsbf5(lsr, /*refresh scene time*/0, "time"); + lsr_write_any_attribute(lsr, NULL, 1); + lsr_write_svg(lsr, (SVG_Element*)com->node); + break; + case GF_SG_LSR_REPLACE: + case GF_SG_LSR_ADD: + case GF_SG_LSR_INSERT: + lsr_write_add_replace_insert(lsr, com); + break; + case GF_SG_LSR_CLEAN: + break; + case GF_SG_LSR_DELETE: + GF_LSR_WRITE_INT(lsr, LSR_UPDATE_DELETE, 4, "ch4"); + field = (GF_CommandField*)gf_list_get(com->command_fields, 0); + if (field && field->fieldType) { + u8 attType; + attType = gf_lsr_anim_type_from_attribute(field->fieldIndex); + GF_LSR_WRITE_INT(lsr, 1, 1, "has_attributeName"); + GF_LSR_WRITE_INT(lsr, 0, 1, "choice"); + GF_LSR_WRITE_INT(lsr, attType, 8, "attributeName"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_attributeName"); + } + if (!field || (field->pos<0) ) { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_index"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_index"); + lsr_write_vluimsbf5(lsr, (u32) field->pos, "index"); + } + lsr_write_codec_IDREF_Node(lsr, com->node, "ref"); + lsr_write_any_attribute(lsr, NULL, 1); + break; + case GF_SG_LSR_RESTORE: + return GF_NOT_SUPPORTED; + case GF_SG_LSR_SAVE: + return GF_NOT_SUPPORTED; + case GF_SG_LSR_SEND_EVENT: + GF_LSR_WRITE_INT(lsr, LSR_UPDATE_SEND_EVENT, 4, "ch4"); + + detail = com->send_event_integer; + switch (com->send_event_name) { + case GF_EVENT_KEYDOWN: + case GF_EVENT_LONGKEYPRESS: + case GF_EVENT_REPEAT_KEY: + case GF_EVENT_SHORT_ACCESSKEY: + detail = dom_to_lsr_key(detail); + if (detail==100) detail = 0; + break; + } + + /*FIXME in the spec, way too obscur*/ + lsr_write_event_type(lsr, com->send_event_name, com->send_event_integer); + + if (detail && com->send_event_integer) { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_intvalue"); + GF_LSR_WRITE_INT(lsr, (com->send_event_integer<0) ? 1 : 0, 1, "sign"); + lsr_write_vluimsbf5(lsr, ABS(com->send_event_integer), "value"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_intvalue"); + } + if (com->send_event_name<=GF_EVENT_MOUSEWHEEL) { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_pointvalue"); + lsr_write_coordinate(lsr, INT2FIX(com->send_event_x), 0, "x"); + lsr_write_coordinate(lsr, INT2FIX(com->send_event_y), 0, "y"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_pointvalue"); + } + lsr_write_codec_IDREF_Node(lsr, com->node, "ref"); + if (com->send_event_string) { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_stringvalue"); + lsr_write_byte_align_string(lsr, com->send_event_string, "stringvalue"); + } else if (!detail && com->send_event_integer) { + GF_LSR_WRITE_INT(lsr, 1, 1, "has_stringvalue"); + lsr_write_byte_align_string(lsr, (char *)gf_dom_get_key_name(com->send_event_integer), "stringvalue"); + } else { + GF_LSR_WRITE_INT(lsr, 0, 1, "has_stringvalue"); + } + GF_LSR_WRITE_INT(lsr, 0, 1, "has_attr_any"); + return GF_OK; + + case GF_SG_LSR_ACTIVATE: + case GF_SG_LSR_DEACTIVATE: + { + u32 update_size = lsr_get_IDREF_nb_bits(lsr, com->node); + + GF_LSR_WRITE_INT(lsr, LSR_UPDATE_EXTEND, 4, "ch4"); + GF_LSR_WRITE_INT(lsr, 2, lsr->info->cfg.extensionIDBits, "extensionID"); + lsr_write_vluimsbf5(lsr, 10 + 5 /*occ2*/ + 2 /*reserved*/ + 5 /*occ3*/ + 2 /*ch5*/ + update_size, "len"); + lsr_write_vluimsbf5(lsr, 87, "reserved"); + lsr_write_vluimsbf5(lsr, 1, "occ2"); + GF_LSR_WRITE_INT(lsr, 1, 2, "reserved"); + lsr_write_vluimsbf5(lsr, 1, "occ3"); + GF_LSR_WRITE_INT(lsr, (com->tag==GF_SG_LSR_ACTIVATE) ? 1 : 2, 2, "ch5"); + lsr_write_codec_IDREF_Node(lsr, com->node, "ref"); + } + break; + default: + return GF_BAD_PARAM; + } + /*same-coding scope is command-based (to check in the spec)*/ + lsr->prev_g = NULL; + lsr->prev_line = NULL; + lsr->prev_path = NULL; + lsr->prev_polygon = NULL; + lsr->prev_rect = NULL; + lsr->prev_text = NULL; + lsr->prev_use = NULL; + if (lsr->last_error) break; + } + +exit: + /*script is aligned*/ + if (cond) { + char *data; + u32 data_size; + gf_bs_get_content(lsr->bs, &data, &data_size); + gf_bs_del(lsr->bs); + lsr->bs = old_bs; + lsr_write_vluimsbf5(lsr, data_size, NULL/*"encoding-length" - don't log to avoid corrupting the log order!!*/); + /*script is aligned*/ + gf_bs_align(lsr->bs); + gf_bs_write_data(lsr->bs, data, data_size); + free(data); + } + return lsr->last_error; +} + +static void lsr_add_color(GF_LASeRCodec *lsr, SVG_Color *color) +{ + lsr->col_table = (LSRCol*)realloc(lsr->col_table, sizeof(LSRCol)*(lsr->nb_cols+1)); + lsr->col_table[lsr->nb_cols].r = FIX2INT(color->red*lsr->color_scale); + lsr->col_table[lsr->nb_cols].g = FIX2INT(color->green*lsr->color_scale); + lsr->col_table[lsr->nb_cols].b = FIX2INT(color->blue*lsr->color_scale); + lsr->nb_cols++; +} + +static void lsr_check_col_index(GF_LASeRCodec *lsr, SVG_Color *color, SVG_Paint *paint) +{ + s32 idx; + if (color) { + idx = lsr_get_col_index(lsr, color); + if (idx==-2) lsr_add_color(lsr, color); + } + else if (paint && (paint->type==SVG_PAINT_COLOR) ) { + idx = lsr_get_col_index(lsr, &paint->color); + if (idx==-2) lsr_add_color(lsr, &paint->color); + } +} + +static void lsr_check_font_index(GF_LASeRCodec *lsr, SVG_FontFamily *font) +{ + u32 count, i; + if (font && (font->type == SVG_FONTFAMILY_VALUE) && font->value) { + Bool found = 0; + count = gf_list_count(lsr->font_table); + for (i=0; ifont_table, i); + if (!strcmp(ff, font->value)) { + found = 1; + break; + } + } + if (!found) gf_list_add(lsr->font_table, strdup(font->value)); + } +} + +static void lsr_check_font_and_color(GF_LASeRCodec *lsr, SVG_Element *elt) +{ + GF_ChildNodeItem *l; + SVGAttribute *att; + u32 i, count, tag; + u32 check_anim_font, check_anim_col; + + tag = gf_node_get_tag((GF_Node*)elt); + if (tag < GF_NODE_FIRST_DOM_NODE_TAG) goto check_children; + + check_anim_font = check_anim_col = 0; + att = elt->attributes; + while (att) { + switch (att->data_type) { + case SVG_Paint_datatype: + lsr_check_col_index(lsr, NULL, att->data); + break; + case SVG_FontFamily_datatype: + lsr_check_font_index(lsr, att->data); + break; + case SMIL_AttributeName_datatype: + { + SMIL_AttributeName *an = (SMIL_AttributeName*)att->data; + if (an->name) { + if (!strcmp(an->name, "fill")) check_anim_col = 1; /*we're sure this is not SMIL fill, it is not animatable*/ + else if (!strcmp(an->name, "stroke")) check_anim_col = 1; + else if (!strcmp(an->name, "color")) check_anim_col = 1; + else if (!strcmp(an->name, "solid-olor")) check_anim_col = 2; + else if (!strcmp(an->name, "stop-color")) check_anim_col = 2; + else if (!strcmp(an->name, "font-family")) check_anim_font = 1; + } + } + break; + } + att = att->next; + } + + if (check_anim_font || check_anim_col) { + att = elt->attributes; + while (att) { + switch (att->data_type) { + case SMIL_AnimateValue_datatype: + if (check_anim_font) lsr_check_font_index(lsr, ((SMIL_AnimateValue*)att->data)->value); + else if (check_anim_col == 1) lsr_check_col_index(lsr, NULL, ((SMIL_AnimateValue*)att->data)->value); + else if (check_anim_col == 2) lsr_check_col_index(lsr, ((SMIL_AnimateValue*)att->data)->value, NULL); + break; + case SMIL_AnimateValues_datatype: + { + SMIL_AnimateValues *vals = (SMIL_AnimateValues*)att->data; + count = gf_list_count(vals->values); + for (i=0; ivalues, i)); + else if (check_anim_col == 1) lsr_check_col_index(lsr, NULL, (SVG_Paint*)gf_list_get(vals->values, i) ); + else if (check_anim_col == 2) lsr_check_col_index(lsr, (SVG_Color*)gf_list_get(vals->values, i), NULL); + } + } + break; + } + att = att->next; + } + } + +check_children: + l = elt->children; + while (l) { + if (l->node->sgprivate->tag==TAG_DOMUpdates) { + GF_DOMUpdates *up = (GF_DOMUpdates *)l->node; + count = gf_list_count(up->updates); + for (i=0; iupdates, i); + c2 = gf_list_count(com->command_fields); + for (j=0; jcommand_fields, j); + if (field->new_node) { + lsr_check_font_and_color(lsr, (SVG_Element*)field->new_node); + } + /*replace/insert*/ + else if (field->field_ptr) { + switch (field->fieldType) { + case SVG_Paint_datatype: + lsr_check_col_index(lsr, NULL, (SVG_Paint*)field->field_ptr); + break; + case SVG_Color_datatype: + lsr_check_col_index(lsr, (SVG_Color*)field->field_ptr, NULL); + break; + case SVG_FontFamily_datatype: + lsr_check_font_index(lsr, (SVG_FontFamily*)field->field_ptr); + break; + } + } + } + } + } else { + lsr_check_font_and_color(lsr, (SVG_Element*)l->node); + } + l = l->next; + } +} + +static GF_Err lsr_write_laser_unit(GF_LASeRCodec *lsr, GF_List *com_list, Bool reset_encoding_context) +{ + u32 i, count, prev_col_count, prev_font_count; + + /* + * 1 - laser unit header + */ + if (!com_list) { + if (gf_sg_get_root_node(lsr->sg) == NULL) return GF_BAD_PARAM; + /*RAP generation, force reset encoding context*/ + GF_LSR_WRITE_INT(lsr, 1, 1, "resetEncodingContext"); + } else { + GF_LSR_WRITE_INT(lsr, reset_encoding_context ? 1 : 0, 1, "resetEncodingContext"); + } + GF_LSR_WRITE_INT(lsr, 0, 1, "opt_group"); + if (0) lsr_write_extension(lsr, NULL, 0, "ext"); + + /*clean all tables*/ + if (reset_encoding_context) { + lsr->nb_cols = 0; + if (lsr->col_table) free(lsr->col_table); + lsr->col_table = NULL; + while (gf_list_count(lsr->font_table)) { + char *ft = (char *)gf_list_last(lsr->font_table); + free(ft); + gf_list_rem_last(lsr->font_table); + } + } + + /* + * 2 - codecInitialisations + */ + + prev_col_count = lsr->nb_cols; + prev_font_count = gf_list_count(lsr->font_table); + /*RAP generation, send all fonts and colors*/ + if (!com_list) { + prev_col_count = prev_font_count = 0; + lsr_check_font_and_color(lsr, (SVG_Element *)gf_sg_get_root_node(lsr->sg)); + } else { + /*process all colors and fonts*/ + count = gf_list_count(com_list); + for (i=0; icommand_fields)) { + GF_CommandField *field = (GF_CommandField *)gf_list_get(com->command_fields, 0); + if (field->fieldType==SVG_Paint_datatype) lsr_check_col_index(lsr, NULL, (SVG_Paint*)field->field_ptr); + else if (field->fieldType==SVG_FontFamily_datatype) lsr_check_font_index(lsr, (SVG_FontFamily*)field->field_ptr); + else if (field->new_node) lsr_check_font_and_color(lsr, (SVG_Element*)field->new_node); + else if (field->node_list) { + GF_ChildNodeItem *l = field->node_list; + while (l) { + lsr_check_font_and_color(lsr, (SVG_Element*)l->node); + l = l->next; + } + } + } else if (com->node && (com->tag!=GF_SG_LSR_DELETE) ) { + lsr_check_font_and_color(lsr, (SVG_Element *)com->node); + } + } + } + /* + * 2.a - condecInitialization.color + */ + if (prev_col_count == lsr->nb_cols) { + GF_LSR_WRITE_INT(lsr, 0, 1, "colorInitialisation"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "colorInitialisation"); + lsr_write_vluimsbf5(lsr, lsr->nb_cols - prev_col_count, "count"); + for (i=prev_col_count; inb_cols; i++) { + GF_LSR_WRITE_INT(lsr, lsr->col_table[i].r, lsr->info->cfg.colorComponentBits, "red"); + GF_LSR_WRITE_INT(lsr, lsr->col_table[i].g, lsr->info->cfg.colorComponentBits, "green"); + GF_LSR_WRITE_INT(lsr, lsr->col_table[i].b, lsr->info->cfg.colorComponentBits, "blue"); + } + } + lsr->colorIndexBits = gf_get_bit_size(lsr->nb_cols); + /* + * 2.b - condecInitialization.fonts + */ + count = gf_list_count(lsr->font_table); + if (prev_font_count == count) { + GF_LSR_WRITE_INT(lsr, 0, 1, "fontInitialisation"); + } else { + GF_LSR_WRITE_INT(lsr, 1, 1, "fontInitialisation"); + lsr_write_vluimsbf5(lsr, count - prev_font_count, "count"); + for (i=prev_font_count; ifont_table, i); + lsr_write_byte_align_string(lsr, ft, "font"); + } + } + lsr->fontIndexBits = gf_get_bit_size(count); + /* + * 2.c - condecInitialization.private + */ + GF_LSR_WRITE_INT(lsr, 0, 1, "privateDataIdentifierInitialisation"); + /* + * 2.d - condecInitialization.anyXML + */ + GF_LSR_WRITE_INT(lsr, 0, 1, "anyXMLInitialisation"); + /* + * 2.e - condecInitialization.extended + */ + /*FIXME - node string IDs are currently not used*/ + lsr_write_vluimsbf5(lsr, 0, "countG"); + /*FIXME - global streams are currently not used*/ + GF_LSR_WRITE_INT(lsr, 0, 1, "hasExtension"); + + /*RAP generation, encode NewScene with root node*/ + if (!com_list) { + lsr_write_vluimsbf5(lsr, 0, "occ0"); + GF_LSR_WRITE_INT(lsr, 4, 4, "ch4"); + lsr_write_any_attribute(lsr, NULL, 1); + lsr_write_svg(lsr, (SVG_Element *)gf_sg_get_root_node(lsr->sg) ); + } else { + GF_Err e = lsr_write_command_list(lsr, com_list, NULL, 1); + if (e) return e; + } + GF_LSR_WRITE_INT(lsr, 0, 1, "opt_group"); + if (0) lsr_write_extension(lsr, NULL, 0, "ext"); + return GF_OK; +} + +#endif /*GPAC_DISABLE_SVG*/ diff --git a/src/laser/lsr_tables.c b/src/laser/lsr_tables.c new file mode 100644 index 0000000..99a59b9 --- /dev/null +++ b/src/laser/lsr_tables.c @@ -0,0 +1,849 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean Le Feuvre + * Copyright (c)2004-200X ENST - All rights reserved + * + * This file is part of GPAC / SVG Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + DO NOT MOFIFY - File generated on GMT Tue May 15 11:18:46 2007 + + BY SVGGen for GPAC Version 0.4.3-DEV +*/ + + +#include + + + +s32 gf_lsr_anim_type_from_attribute(u32 tag) { + switch(tag) { + case TAG_SVG_ATT_target: return 0; + case TAG_SVG_ATT_accumulate: return 1; + case TAG_SVG_ATT_additive: return 2; + case TAG_SVG_ATT_audio_level: return 3; + case TAG_SVG_ATT_bandwidth: return 4; + case TAG_SVG_ATT_begin: return 5; + case TAG_SVG_ATT_calcMode: return 6; + case TAG_LSR_ATT_children: return 7; + case TAG_SVG_ATT_choice: return 8; + case TAG_SVG_ATT_clipBegin: return 9; + case TAG_SVG_ATT_clipEnd: return 10; + case TAG_SVG_ATT_color: return 11; + case TAG_SVG_ATT_color_rendering: return 12; + case TAG_SVG_ATT_cx: return 13; + case TAG_SVG_ATT_cy: return 14; + case TAG_SVG_ATT_d: return 15; + case TAG_SVG_ATT_delta: return 16; + case TAG_SVG_ATT_display: return 17; + case TAG_SVG_ATT_display_align: return 18; + case TAG_SVG_ATT_dur: return 19; + case TAG_SVG_ATT_editable: return 20; + case TAG_LSR_ATT_enabled: return 21; + case TAG_SVG_ATT_end: return 22; + case TAG_XMLEV_ATT_event: return 23; + case TAG_SVG_ATT_externalResourcesRequired: return 24; + case TAG_SVG_ATT_fill: return 25; + case TAG_SVG_ATT_fill_opacity: return 26; + case TAG_SVG_ATT_fill_rule: return 27; + case TAG_SVG_ATT_focusable: return 28; + case TAG_SVG_ATT_font_family: return 29; + case TAG_SVG_ATT_font_size: return 30; + case TAG_SVG_ATT_font_style: return 31; + case TAG_SVG_ATT_font_variant: return 32; + case TAG_SVG_ATT_font_weight: return 33; + case TAG_SVG_ATT_fullscreen: return 34; + case TAG_SVG_ATT_gradientUnits: return 35; + case TAG_XMLEV_ATT_handler: return 36; + case TAG_SVG_ATT_height: return 37; + case TAG_SVG_ATT_image_rendering: return 38; + case TAG_SVG_ATT_keyPoints: return 39; + case TAG_SVG_ATT_keySplines: return 40; + case TAG_SVG_ATT_keyTimes: return 41; + case TAG_SVG_ATT_line_increment: return 42; + case TAG_XMLEV_ATT_target: return 43; + case TAG_SVG_ATT_mediaCharacterEncoding: return 44; + case TAG_SVG_ATT_mediaContentEncodings: return 45; + case TAG_SVG_ATT_mediaSize: return 46; + case TAG_SVG_ATT_mediaTime: return 47; + case TAG_SVG_ATT_nav_down: return 48; + case TAG_SVG_ATT_nav_down_left: return 49; + case TAG_SVG_ATT_nav_down_right: return 50; + case TAG_SVG_ATT_nav_left: return 51; + case TAG_SVG_ATT_nav_next: return 52; + case TAG_SVG_ATT_nav_prev: return 53; + case TAG_SVG_ATT_nav_right: return 54; + case TAG_SVG_ATT_nav_up: return 55; + case TAG_SVG_ATT_nav_up_left: return 56; + case TAG_SVG_ATT_nav_up_right: return 57; + case TAG_XMLEV_ATT_observer: return 58; + case TAG_SVG_ATT_offset: return 59; + case TAG_SVG_ATT_opacity: return 60; + case TAG_SVG_ATT_overflow: return 61; + case TAG_SVG_ATT_overlay: return 62; + case TAG_SVG_ATT_path: return 63; + case TAG_SVG_ATT_pathLength: return 64; + case TAG_SVG_ATT_pointer_events: return 65; + case TAG_SVG_ATT_points: return 66; + case TAG_SVG_ATT_preserveAspectRatio: return 67; + case TAG_SVG_ATT_r: return 68; + case TAG_SVG_ATT_repeatCount: return 69; + case TAG_SVG_ATT_repeatDur: return 70; + case TAG_SVG_ATT_requiredExtensions: return 71; + case TAG_SVG_ATT_requiredFeatures: return 72; + case TAG_SVG_ATT_requiredFormats: return 73; + case TAG_SVG_ATT_restart: return 74; + case TAG_SVG_ATT_rotate: return 75; + case TAG_LSR_ATT_rotation: return 76; + case TAG_SVG_ATT_rx: return 77; + case TAG_SVG_ATT_ry: return 78; + case TAG_LSR_ATT_scale: return 79; + case TAG_SVG_ATT_shape_rendering: return 80; + case TAG_SVG_ATT_size: return 81; + case TAG_SVG_ATT_solid_color: return 82; + case TAG_SVG_ATT_solid_opacity: return 83; + case TAG_SVG_ATT_stop_color: return 84; + case TAG_SVG_ATT_stop_opacity: return 85; + case TAG_SVG_ATT_stroke: return 86; + case TAG_SVG_ATT_stroke_dasharray: return 87; + case TAG_SVG_ATT_stroke_dashoffset: return 88; + case TAG_SVG_ATT_stroke_linecap: return 89; + case TAG_SVG_ATT_stroke_linejoin: return 90; + case TAG_SVG_ATT_stroke_miterlimit: return 91; + case TAG_SVG_ATT_stroke_opacity: return 92; + case TAG_SVG_ATT_stroke_width: return 93; + case TAG_LSR_ATT_svg_height: return 94; + case TAG_LSR_ATT_svg_width: return 95; + case TAG_SVG_ATT_syncBehavior: return 96; + case TAG_SVG_ATT_syncBehaviorDefault: return 97; + case TAG_SVG_ATT_syncReference: return 98; + case TAG_SVG_ATT_syncTolerance: return 99; + case TAG_SVG_ATT_syncToleranceDefault: return 100; + case TAG_SVG_ATT_systemLanguage: return 101; + case TAG_SVG_ATT_text_align: return 102; + case TAG_SVG_ATT_text_anchor: return 103; + case TAG_SVG_ATT_text_decoration: return 104; + case TAG_LSR_ATT_text_display: return 105; + case TAG_SVG_ATT_text_rendering: return 106; + case TAG_LSR_ATT_textContent: return 107; + case TAG_SVG_ATT_transform: return 108; + case TAG_SVG_ATT_transformBehavior: return 109; + case TAG_LSR_ATT_translation: return 110; + case TAG_SVG_ATT_vector_effect: return 111; + case TAG_SVG_ATT_viewBox: return 112; + case TAG_SVG_ATT_viewport_fill: return 113; + case TAG_SVG_ATT_viewport_fill_opacity: return 114; + case TAG_SVG_ATT_visibility: return 115; + case TAG_SVG_ATT_width: return 116; + case TAG_SVG_ATT_x: return 117; + case TAG_SVG_ATT_x1: return 118; + case TAG_SVG_ATT_x2: return 119; + case TAG_XLINK_ATT_actuate: return 120; + case TAG_XLINK_ATT_arcrole: return 121; + case TAG_XLINK_ATT_href: return 122; + case TAG_XLINK_ATT_role: return 123; + case TAG_XLINK_ATT_show: return 124; + case TAG_XLINK_ATT_title: return 125; + case TAG_XLINK_ATT_type: return 126; + case TAG_XML_ATT_base: return 127; + case TAG_XML_ATT_lang: return 128; + case TAG_SVG_ATT_y: return 129; + case TAG_SVG_ATT_y1: return 130; + case TAG_SVG_ATT_y2: return 131; + case TAG_SVG_ATT_zoomAndPan: return 132; + default: return -1; + } +} + + + +s32 gf_lsr_rare_type_from_attribute(u32 tag) { + switch(tag) { + case TAG_SVG_ATT__class: return 0; + case TAG_SVG_ATT_audio_level: return 1; + case TAG_SVG_ATT_color: return 2; + case TAG_SVG_ATT_color_rendering: return 3; + case TAG_SVG_ATT_display: return 4; + case TAG_SVG_ATT_display_align: return 5; + case TAG_SVG_ATT_fill_opacity: return 6; + case TAG_SVG_ATT_fill_rule: return 7; + case TAG_SVG_ATT_image_rendering: return 8; + case TAG_SVG_ATT_line_increment: return 9; + case TAG_SVG_ATT_pointer_events: return 10; + case TAG_SVG_ATT_shape_rendering: return 11; + case TAG_SVG_ATT_solid_color: return 12; + case TAG_SVG_ATT_solid_opacity: return 13; + case TAG_SVG_ATT_stop_color: return 14; + case TAG_SVG_ATT_stop_opacity: return 15; + case TAG_SVG_ATT_stroke_dasharray: return 16; + case TAG_SVG_ATT_stroke_dashoffset: return 17; + case TAG_SVG_ATT_stroke_linecap: return 18; + case TAG_SVG_ATT_stroke_linejoin: return 19; + case TAG_SVG_ATT_stroke_miterlimit: return 20; + case TAG_SVG_ATT_stroke_opacity: return 21; + case TAG_SVG_ATT_stroke_width: return 22; + case TAG_SVG_ATT_text_anchor: return 23; + case TAG_SVG_ATT_text_rendering: return 24; + case TAG_SVG_ATT_viewport_fill: return 25; + case TAG_SVG_ATT_viewport_fill_opacity: return 26; + case TAG_SVG_ATT_vector_effect: return 27; + case TAG_SVG_ATT_visibility: return 28; + case TAG_SVG_ATT_requiredExtensions: return 29; + case TAG_SVG_ATT_requiredFeatures: return 30; + case TAG_SVG_ATT_requiredFormats: return 31; + case TAG_SVG_ATT_systemLanguage: return 32; + case TAG_XML_ATT_base: return 33; + case TAG_XML_ATT_lang: return 34; + case TAG_XML_ATT_space: return 35; + case TAG_SVG_ATT_nav_next: return 36; + case TAG_SVG_ATT_nav_up: return 37; + case TAG_SVG_ATT_nav_up_left: return 38; + case TAG_SVG_ATT_nav_up_right: return 39; + case TAG_SVG_ATT_nav_prev: return 40; + case TAG_SVG_ATT_nav_down: return 41; + case TAG_SVG_ATT_nav_down_left: return 42; + case TAG_SVG_ATT_nav_down_right: return 43; + case TAG_SVG_ATT_nav_left: return 44; + case TAG_SVG_ATT_focusable: return 45; + case TAG_SVG_ATT_nav_right: return 46; + case TAG_SVG_ATT_transform: return 47; + case TAG_SVG_ATT_text_decoration: return 48; + case TAG_SVG_ATT_syncMaster: return 49; + case TAG_SVG_ATT_focusHighlight: return 49; + case TAG_SVG_ATT_initialVisibility: return 49; + case TAG_SVG_ATT_fullscreen: return 49; + case TAG_SVG_ATT_requiredFonts: return 49; + case TAG_SVG_ATT_font_variant: return 50; + case TAG_SVG_ATT_font_family: return 51; + case TAG_SVG_ATT_font_size: return 52; + case TAG_SVG_ATT_font_style: return 53; + case TAG_SVG_ATT_font_weight: return 54; + case TAG_XLINK_ATT_title: return 55; + case TAG_XLINK_ATT_type: return 56; + case TAG_XLINK_ATT_role: return 57; + case TAG_XLINK_ATT_arcrole: return 58; + case TAG_XLINK_ATT_actuate: return 59; + case TAG_XLINK_ATT_show: return 60; + case TAG_SVG_ATT_end: return 61; + case TAG_SVG_ATT_max: return 62; + case TAG_SVG_ATT_min: return 63; + default: return -1; + } +} + + + +s32 gf_lsr_anim_type_to_attribute(u32 tag) { + switch(tag) { + case 0: return TAG_SVG_ATT_target; + case 1: return TAG_SVG_ATT_accumulate; + case 2: return TAG_SVG_ATT_additive; + case 3: return TAG_SVG_ATT_audio_level; + case 4: return TAG_SVG_ATT_bandwidth; + case 5: return TAG_SVG_ATT_begin; + case 6: return TAG_SVG_ATT_calcMode; + case 7: return TAG_LSR_ATT_children; + case 8: return TAG_SVG_ATT_choice; + case 9: return TAG_SVG_ATT_clipBegin; + case 10: return TAG_SVG_ATT_clipEnd; + case 11: return TAG_SVG_ATT_color; + case 12: return TAG_SVG_ATT_color_rendering; + case 13: return TAG_SVG_ATT_cx; + case 14: return TAG_SVG_ATT_cy; + case 15: return TAG_SVG_ATT_d; + case 16: return TAG_SVG_ATT_delta; + case 17: return TAG_SVG_ATT_display; + case 18: return TAG_SVG_ATT_display_align; + case 19: return TAG_SVG_ATT_dur; + case 20: return TAG_SVG_ATT_editable; + case 21: return TAG_LSR_ATT_enabled; + case 22: return TAG_SVG_ATT_end; + case 23: return TAG_XMLEV_ATT_event; + case 24: return TAG_SVG_ATT_externalResourcesRequired; + case 25: return TAG_SVG_ATT_fill; + case 26: return TAG_SVG_ATT_fill_opacity; + case 27: return TAG_SVG_ATT_fill_rule; + case 28: return TAG_SVG_ATT_focusable; + case 29: return TAG_SVG_ATT_font_family; + case 30: return TAG_SVG_ATT_font_size; + case 31: return TAG_SVG_ATT_font_style; + case 32: return TAG_SVG_ATT_font_variant; + case 33: return TAG_SVG_ATT_font_weight; + case 34: return TAG_SVG_ATT_fullscreen; + case 35: return TAG_SVG_ATT_gradientUnits; + case 36: return TAG_XMLEV_ATT_handler; + case 37: return TAG_SVG_ATT_height; + case 38: return TAG_SVG_ATT_image_rendering; + case 39: return TAG_SVG_ATT_keyPoints; + case 40: return TAG_SVG_ATT_keySplines; + case 41: return TAG_SVG_ATT_keyTimes; + case 42: return TAG_SVG_ATT_line_increment; + case 43: return TAG_XMLEV_ATT_target; + case 44: return TAG_SVG_ATT_mediaCharacterEncoding; + case 45: return TAG_SVG_ATT_mediaContentEncodings; + case 46: return TAG_SVG_ATT_mediaSize; + case 47: return TAG_SVG_ATT_mediaTime; + case 48: return TAG_SVG_ATT_nav_down; + case 49: return TAG_SVG_ATT_nav_down_left; + case 50: return TAG_SVG_ATT_nav_down_right; + case 51: return TAG_SVG_ATT_nav_left; + case 52: return TAG_SVG_ATT_nav_next; + case 53: return TAG_SVG_ATT_nav_prev; + case 54: return TAG_SVG_ATT_nav_right; + case 55: return TAG_SVG_ATT_nav_up; + case 56: return TAG_SVG_ATT_nav_up_left; + case 57: return TAG_SVG_ATT_nav_up_right; + case 58: return TAG_XMLEV_ATT_observer; + case 59: return TAG_SVG_ATT_offset; + case 60: return TAG_SVG_ATT_opacity; + case 61: return TAG_SVG_ATT_overflow; + case 62: return TAG_SVG_ATT_overlay; + case 63: return TAG_SVG_ATT_path; + case 64: return TAG_SVG_ATT_pathLength; + case 65: return TAG_SVG_ATT_pointer_events; + case 66: return TAG_SVG_ATT_points; + case 67: return TAG_SVG_ATT_preserveAspectRatio; + case 68: return TAG_SVG_ATT_r; + case 69: return TAG_SVG_ATT_repeatCount; + case 70: return TAG_SVG_ATT_repeatDur; + case 71: return TAG_SVG_ATT_requiredExtensions; + case 72: return TAG_SVG_ATT_requiredFeatures; + case 73: return TAG_SVG_ATT_requiredFormats; + case 74: return TAG_SVG_ATT_restart; + case 75: return TAG_SVG_ATT_rotate; + case 76: return TAG_LSR_ATT_rotation; + case 77: return TAG_SVG_ATT_rx; + case 78: return TAG_SVG_ATT_ry; + case 79: return TAG_LSR_ATT_scale; + case 80: return TAG_SVG_ATT_shape_rendering; + case 81: return TAG_SVG_ATT_size; + case 82: return TAG_SVG_ATT_solid_color; + case 83: return TAG_SVG_ATT_solid_opacity; + case 84: return TAG_SVG_ATT_stop_color; + case 85: return TAG_SVG_ATT_stop_opacity; + case 86: return TAG_SVG_ATT_stroke; + case 87: return TAG_SVG_ATT_stroke_dasharray; + case 88: return TAG_SVG_ATT_stroke_dashoffset; + case 89: return TAG_SVG_ATT_stroke_linecap; + case 90: return TAG_SVG_ATT_stroke_linejoin; + case 91: return TAG_SVG_ATT_stroke_miterlimit; + case 92: return TAG_SVG_ATT_stroke_opacity; + case 93: return TAG_SVG_ATT_stroke_width; + case 94: return TAG_LSR_ATT_svg_height; + case 95: return TAG_LSR_ATT_svg_width; + case 96: return TAG_SVG_ATT_syncBehavior; + case 97: return TAG_SVG_ATT_syncBehaviorDefault; + case 98: return TAG_SVG_ATT_syncReference; + case 99: return TAG_SVG_ATT_syncTolerance; + case 100: return TAG_SVG_ATT_syncToleranceDefault; + case 101: return TAG_SVG_ATT_systemLanguage; + case 102: return TAG_SVG_ATT_text_align; + case 103: return TAG_SVG_ATT_text_anchor; + case 104: return TAG_SVG_ATT_text_decoration; + case 105: return TAG_LSR_ATT_text_display; + case 106: return TAG_SVG_ATT_text_rendering; + case 107: return TAG_LSR_ATT_textContent; + case 108: return TAG_SVG_ATT_transform; + case 109: return TAG_SVG_ATT_transformBehavior; + case 110: return TAG_LSR_ATT_translation; + case 111: return TAG_SVG_ATT_vector_effect; + case 112: return TAG_SVG_ATT_viewBox; + case 113: return TAG_SVG_ATT_viewport_fill; + case 114: return TAG_SVG_ATT_viewport_fill_opacity; + case 115: return TAG_SVG_ATT_visibility; + case 116: return TAG_SVG_ATT_width; + case 117: return TAG_SVG_ATT_x; + case 118: return TAG_SVG_ATT_x1; + case 119: return TAG_SVG_ATT_x2; + case 120: return TAG_XLINK_ATT_actuate; + case 121: return TAG_XLINK_ATT_arcrole; + case 122: return TAG_XLINK_ATT_href; + case 123: return TAG_XLINK_ATT_role; + case 124: return TAG_XLINK_ATT_show; + case 125: return TAG_XLINK_ATT_title; + case 126: return TAG_XLINK_ATT_type; + case 127: return TAG_XML_ATT_base; + case 128: return TAG_XML_ATT_lang; + case 129: return TAG_SVG_ATT_y; + case 130: return TAG_SVG_ATT_y1; + case 131: return TAG_SVG_ATT_y2; + case 132: return TAG_SVG_ATT_zoomAndPan; + default: return -1; + } +} + + + +s32 gf_lsr_rare_type_to_attribute(u32 tag) { + switch(tag) { + case 0: return TAG_SVG_ATT__class; + case 1: return TAG_SVG_ATT_audio_level; + case 2: return TAG_SVG_ATT_color; + case 3: return TAG_SVG_ATT_color_rendering; + case 4: return TAG_SVG_ATT_display; + case 5: return TAG_SVG_ATT_display_align; + case 6: return TAG_SVG_ATT_fill_opacity; + case 7: return TAG_SVG_ATT_fill_rule; + case 8: return TAG_SVG_ATT_image_rendering; + case 9: return TAG_SVG_ATT_line_increment; + case 10: return TAG_SVG_ATT_pointer_events; + case 11: return TAG_SVG_ATT_shape_rendering; + case 12: return TAG_SVG_ATT_solid_color; + case 13: return TAG_SVG_ATT_solid_opacity; + case 14: return TAG_SVG_ATT_stop_color; + case 15: return TAG_SVG_ATT_stop_opacity; + case 16: return TAG_SVG_ATT_stroke_dasharray; + case 17: return TAG_SVG_ATT_stroke_dashoffset; + case 18: return TAG_SVG_ATT_stroke_linecap; + case 19: return TAG_SVG_ATT_stroke_linejoin; + case 20: return TAG_SVG_ATT_stroke_miterlimit; + case 21: return TAG_SVG_ATT_stroke_opacity; + case 22: return TAG_SVG_ATT_stroke_width; + case 23: return TAG_SVG_ATT_text_anchor; + case 24: return TAG_SVG_ATT_text_rendering; + case 25: return TAG_SVG_ATT_viewport_fill; + case 26: return TAG_SVG_ATT_viewport_fill_opacity; + case 27: return TAG_SVG_ATT_vector_effect; + case 28: return TAG_SVG_ATT_visibility; + case 29: return TAG_SVG_ATT_requiredExtensions; + case 30: return TAG_SVG_ATT_requiredFeatures; + case 31: return TAG_SVG_ATT_requiredFormats; + case 32: return TAG_SVG_ATT_systemLanguage; + case 33: return TAG_XML_ATT_base; + case 34: return TAG_XML_ATT_lang; + case 35: return TAG_XML_ATT_space; + case 36: return TAG_SVG_ATT_nav_next; + case 37: return TAG_SVG_ATT_nav_up; + case 38: return TAG_SVG_ATT_nav_up_left; + case 39: return TAG_SVG_ATT_nav_up_right; + case 40: return TAG_SVG_ATT_nav_prev; + case 41: return TAG_SVG_ATT_nav_down; + case 42: return TAG_SVG_ATT_nav_down_left; + case 43: return TAG_SVG_ATT_nav_down_right; + case 44: return TAG_SVG_ATT_nav_left; + case 45: return TAG_SVG_ATT_focusable; + case 46: return TAG_SVG_ATT_nav_right; + case 47: return TAG_SVG_ATT_transform; + case 48: return TAG_SVG_ATT_text_decoration; + case 50: return TAG_SVG_ATT_font_variant; + case 51: return TAG_SVG_ATT_font_family; + case 52: return TAG_SVG_ATT_font_size; + case 53: return TAG_SVG_ATT_font_style; + case 54: return TAG_SVG_ATT_font_weight; + case 55: return TAG_XLINK_ATT_title; + case 56: return TAG_XLINK_ATT_type; + case 57: return TAG_XLINK_ATT_role; + case 58: return TAG_XLINK_ATT_arcrole; + case 59: return TAG_XLINK_ATT_actuate; + case 60: return TAG_XLINK_ATT_show; + case 61: return TAG_SVG_ATT_end; + case 62: return TAG_SVG_ATT_max; + case 63: return TAG_SVG_ATT_min; + default: return -1; + } +} + + + +u32 gf_lsr_same_rare(SVGAllAttributes *elt_atts, SVGAllAttributes *base_atts) +{ + GF_FieldInfo f_elt, f_base; + f_elt.fieldType = f_base.fieldType = DOM_String_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT__class; + f_elt.far_ptr = elt_atts->_class; + f_base.far_ptr = base_atts->_class; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Number_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_audio_level; + f_elt.far_ptr = elt_atts->audio_level; + f_base.far_ptr = base_atts->audio_level; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Paint_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_color; + f_elt.far_ptr = elt_atts->color; + f_base.far_ptr = base_atts->color; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_RenderingHint_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_color_rendering; + f_elt.far_ptr = elt_atts->color_rendering; + f_base.far_ptr = base_atts->color_rendering; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Display_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_display; + f_elt.far_ptr = elt_atts->display; + f_base.far_ptr = base_atts->display; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_DisplayAlign_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_display_align; + f_elt.far_ptr = elt_atts->display_align; + f_base.far_ptr = base_atts->display_align; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Number_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_fill_opacity; + f_elt.far_ptr = elt_atts->fill_opacity; + f_base.far_ptr = base_atts->fill_opacity; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_FillRule_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_fill_rule; + f_elt.far_ptr = elt_atts->fill_rule; + f_base.far_ptr = base_atts->fill_rule; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_RenderingHint_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_image_rendering; + f_elt.far_ptr = elt_atts->image_rendering; + f_base.far_ptr = base_atts->image_rendering; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Number_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_line_increment; + f_elt.far_ptr = elt_atts->line_increment; + f_base.far_ptr = base_atts->line_increment; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_PointerEvents_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_pointer_events; + f_elt.far_ptr = elt_atts->pointer_events; + f_base.far_ptr = base_atts->pointer_events; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_RenderingHint_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_shape_rendering; + f_elt.far_ptr = elt_atts->shape_rendering; + f_base.far_ptr = base_atts->shape_rendering; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Paint_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_solid_color; + f_elt.far_ptr = elt_atts->solid_color; + f_base.far_ptr = base_atts->solid_color; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Number_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_solid_opacity; + f_elt.far_ptr = elt_atts->solid_opacity; + f_base.far_ptr = base_atts->solid_opacity; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Paint_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_stop_color; + f_elt.far_ptr = elt_atts->stop_color; + f_base.far_ptr = base_atts->stop_color; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Number_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_stop_opacity; + f_elt.far_ptr = elt_atts->stop_opacity; + f_base.far_ptr = base_atts->stop_opacity; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_StrokeDashArray_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_stroke_dasharray; + f_elt.far_ptr = elt_atts->stroke_dasharray; + f_base.far_ptr = base_atts->stroke_dasharray; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Length_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_stroke_dashoffset; + f_elt.far_ptr = elt_atts->stroke_dashoffset; + f_base.far_ptr = base_atts->stroke_dashoffset; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_StrokeLineCap_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_stroke_linecap; + f_elt.far_ptr = elt_atts->stroke_linecap; + f_base.far_ptr = base_atts->stroke_linecap; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_StrokeLineJoin_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_stroke_linejoin; + f_elt.far_ptr = elt_atts->stroke_linejoin; + f_base.far_ptr = base_atts->stroke_linejoin; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Number_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_stroke_miterlimit; + f_elt.far_ptr = elt_atts->stroke_miterlimit; + f_base.far_ptr = base_atts->stroke_miterlimit; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Number_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_stroke_opacity; + f_elt.far_ptr = elt_atts->stroke_opacity; + f_base.far_ptr = base_atts->stroke_opacity; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Length_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_stroke_width; + f_elt.far_ptr = elt_atts->stroke_width; + f_base.far_ptr = base_atts->stroke_width; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_TextAnchor_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_text_anchor; + f_elt.far_ptr = elt_atts->text_anchor; + f_base.far_ptr = base_atts->text_anchor; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_RenderingHint_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_text_rendering; + f_elt.far_ptr = elt_atts->text_rendering; + f_base.far_ptr = base_atts->text_rendering; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Paint_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_viewport_fill; + f_elt.far_ptr = elt_atts->viewport_fill; + f_base.far_ptr = base_atts->viewport_fill; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Number_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_viewport_fill_opacity; + f_elt.far_ptr = elt_atts->viewport_fill_opacity; + f_base.far_ptr = base_atts->viewport_fill_opacity; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_VectorEffect_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_vector_effect; + f_elt.far_ptr = elt_atts->vector_effect; + f_base.far_ptr = base_atts->vector_effect; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Visibility_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_visibility; + f_elt.far_ptr = elt_atts->visibility; + f_base.far_ptr = base_atts->visibility; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = XMLRI_List_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_requiredExtensions; + f_elt.far_ptr = elt_atts->requiredExtensions; + f_base.far_ptr = base_atts->requiredExtensions; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = XMLRI_List_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_requiredFeatures; + f_elt.far_ptr = elt_atts->requiredFeatures; + f_base.far_ptr = base_atts->requiredFeatures; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = DOM_StringList_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_requiredFormats; + f_elt.far_ptr = elt_atts->requiredFormats; + f_base.far_ptr = base_atts->requiredFormats; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = DOM_StringList_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_systemLanguage; + f_elt.far_ptr = elt_atts->systemLanguage; + f_base.far_ptr = base_atts->systemLanguage; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = XMLRI_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_XML_ATT_base; + f_elt.far_ptr = elt_atts->xml_base; + f_base.far_ptr = base_atts->xml_base; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_LanguageID_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_XML_ATT_lang; + f_elt.far_ptr = elt_atts->xml_lang; + f_base.far_ptr = base_atts->xml_lang; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = XML_Space_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_XML_ATT_space; + f_elt.far_ptr = elt_atts->xml_space; + f_base.far_ptr = base_atts->xml_space; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focus_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_nav_next; + f_elt.far_ptr = elt_atts->nav_next; + f_base.far_ptr = base_atts->nav_next; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focus_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_nav_up; + f_elt.far_ptr = elt_atts->nav_up; + f_base.far_ptr = base_atts->nav_up; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focus_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_nav_up_left; + f_elt.far_ptr = elt_atts->nav_up_left; + f_base.far_ptr = base_atts->nav_up_left; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focus_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_nav_up_right; + f_elt.far_ptr = elt_atts->nav_up_right; + f_base.far_ptr = base_atts->nav_up_right; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focus_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_nav_prev; + f_elt.far_ptr = elt_atts->nav_prev; + f_base.far_ptr = base_atts->nav_prev; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focus_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_nav_down; + f_elt.far_ptr = elt_atts->nav_down; + f_base.far_ptr = base_atts->nav_down; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focus_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_nav_down_left; + f_elt.far_ptr = elt_atts->nav_down_left; + f_base.far_ptr = base_atts->nav_down_left; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focus_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_nav_down_right; + f_elt.far_ptr = elt_atts->nav_down_right; + f_base.far_ptr = base_atts->nav_down_right; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focus_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_nav_left; + f_elt.far_ptr = elt_atts->nav_left; + f_base.far_ptr = base_atts->nav_left; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focusable_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_focusable; + f_elt.far_ptr = elt_atts->focusable; + f_base.far_ptr = base_atts->focusable; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Focus_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_nav_right; + f_elt.far_ptr = elt_atts->nav_right; + f_base.far_ptr = base_atts->nav_right; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_Transform_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_transform; + f_elt.far_ptr = elt_atts->transform; + f_base.far_ptr = base_atts->transform; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = DOM_String_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_text_decoration; + f_elt.far_ptr = elt_atts->text_decoration; + f_base.far_ptr = base_atts->text_decoration; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_FontVariant_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_font_variant; + f_elt.far_ptr = elt_atts->font_variant; + f_base.far_ptr = base_atts->font_variant; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_FontFamily_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_font_family; + f_elt.far_ptr = elt_atts->font_family; + f_base.far_ptr = base_atts->font_family; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_FontSize_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_font_size; + f_elt.far_ptr = elt_atts->font_size; + f_base.far_ptr = base_atts->font_size; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_FontStyle_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_font_style; + f_elt.far_ptr = elt_atts->font_style; + f_base.far_ptr = base_atts->font_style; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SVG_FontWeight_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_font_weight; + f_elt.far_ptr = elt_atts->font_weight; + f_base.far_ptr = base_atts->font_weight; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = DOM_String_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_XLINK_ATT_title; + f_elt.far_ptr = elt_atts->xlink_title; + f_base.far_ptr = base_atts->xlink_title; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = DOM_String_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_XLINK_ATT_type; + f_elt.far_ptr = elt_atts->xlink_type; + f_base.far_ptr = base_atts->xlink_type; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = XMLRI_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_XLINK_ATT_role; + f_elt.far_ptr = elt_atts->xlink_role; + f_base.far_ptr = base_atts->xlink_role; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = XMLRI_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_XLINK_ATT_arcrole; + f_elt.far_ptr = elt_atts->xlink_arcrole; + f_base.far_ptr = base_atts->xlink_arcrole; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = DOM_String_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_XLINK_ATT_actuate; + f_elt.far_ptr = elt_atts->xlink_actuate; + f_base.far_ptr = base_atts->xlink_actuate; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = DOM_String_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_XLINK_ATT_show; + f_elt.far_ptr = elt_atts->xlink_show; + f_base.far_ptr = base_atts->xlink_show; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SMIL_Times_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_end; + f_elt.far_ptr = elt_atts->end; + f_base.far_ptr = base_atts->end; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SMIL_Duration_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_max; + f_elt.far_ptr = elt_atts->max; + f_base.far_ptr = base_atts->max; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + f_elt.fieldType = f_base.fieldType = SMIL_Duration_datatype; + f_elt.fieldIndex = f_base.fieldIndex = TAG_SVG_ATT_min; + f_elt.far_ptr = elt_atts->min; + f_base.far_ptr = base_atts->min; + if (!gf_svg_attributes_equal(&f_elt, &f_base)) return 0; + + return 1; +} + diff --git a/src/libgpac.def b/src/libgpac.def new file mode 100644 index 0000000..ecd1c32 --- /dev/null +++ b/src/libgpac.def @@ -0,0 +1,1235 @@ +LIBRARY libgpac.dll + +;Note: the APIs and headers are likely to change a lot until 0.5.0 is reached, until all symbols needed +;by different scene decoders (SVG, X3D, etc) are identified + +EXPORTS +;tools.h exports + gf_sys_init + gf_sys_close + gf_sleep + gf_sys_clock + gf_sys_get_rti + gf_sys_get_battery_state + gf_4cc_to_str + gf_error_to_string + gf_rand_init + gf_rand + gf_get_user_name + gf_enum_directory + gf_log_get_tools + gf_log_get_level + gf_log_set_level + gf_log_set_tools + gf_log_set_callback + gf_log + gf_log_lt + gf_set_progress + gf_set_progress_callback + gf_temp_file_new + gf_delete_file + gf_f64_open + gf_f64_seek + gf_f64_tell + gf_prompt_has_input + gf_prompt_get_char + gf_prompt_set_echo_off + gf_crc_32 + + +; gf_mulfix +; gf_divfix +; gf_muldiv +; gf_atan2 +; gf_invfix +; gf_sqrt + gf_malloc + gf_realloc + gf_free + gf_strdup + +;array.h exports + gf_list_new + gf_list_del + gf_list_count + gf_list_add + gf_list_insert + gf_list_rem + gf_list_get + gf_list_enum + gf_list_find + gf_list_del_item + gf_list_reset + gf_list_last + gf_list_rem_last + +;bitstream.h exports + gf_bs_new + gf_bs_from_file + gf_bs_del + gf_bs_read_bit + gf_bs_read_int + gf_bs_read_long_int + gf_bs_read_u8 + gf_bs_read_u16 + gf_bs_read_u24 + gf_bs_read_u32 + gf_bs_read_u64 + gf_bs_read_float + gf_bs_read_double + gf_bs_read_data + gf_bs_write_int + gf_bs_write_long_int + gf_bs_write_u8 + gf_bs_write_u16 + gf_bs_write_u24 + gf_bs_write_u32 + gf_bs_write_u64 + gf_bs_write_float + gf_bs_write_double + gf_bs_write_data + gf_bs_set_eos_callback + gf_bs_align + gf_bs_available + gf_bs_get_content + gf_bs_skip_bytes + gf_bs_seek + gf_bs_peek_bits + gf_bs_get_position + gf_bs_get_size + gf_bs_get_refreshed_size + +;thread.h exports + gf_th_new + gf_th_del + gf_th_run + gf_th_stop + gf_th_status + gf_th_set_priority + gf_th_id + gf_mx_new + gf_mx_del + gf_mx_v + gf_mx_p + gf_mx_try_lock + gf_sema_new + gf_sema_del + gf_sema_notify + gf_sema_wait + gf_sema_wait_for + +;net.h exports + gf_sk_new + gf_sk_del + gf_sk_reset + gf_sk_set_buffer_size + gf_sk_set_block_mode + gf_sk_get_handle + gf_sk_bind + gf_sk_connect + gf_sk_send + gf_sk_receive + gf_sk_listen + gf_sk_accept + gf_sk_server_mode + gf_sk_get_host_name + gf_sk_get_local_ip + gf_sk_get_local_info + gf_sk_get_remote_address + gf_sk_send_to + gf_sk_setup_multicast + gf_sk_is_multicast_address + gf_sk_send_wait + gf_sk_receive_wait + gf_url_is_local + gf_url_get_absolute_path + gf_url_concatenate + gf_utc_time_since_1970 + gf_net_get_ntp + gf_net_has_ipv6 + gf_net_is_ipv6 + gf_net_mobileip_set_callback + + +;base_coding.h exports + gf_base64_encode + gf_base64_decode + gf_base16_encode + gf_base16_decode + +;token.h exports + gf_token_get + gf_token_get_line + gf_token_find + +;config.h exports + gf_cfg_new + gf_cfg_del + gf_cfg_save + gf_cfg_get_key + gf_cfg_set_key + gf_cfg_get_section_count + gf_cfg_get_section_name + gf_cfg_get_key_count + gf_cfg_get_key_name + gf_cfg_insert_key + +;module.h exports + gf_modules_new + gf_modules_del + gf_modules_refresh + gf_modules_get_count + gf_modules_get_file_name + gf_modules_load_interface + gf_modules_load_interface_by_name + gf_modules_close_interface + gf_modules_get_option + gf_modules_set_option + gf_modules_get_config + +;utf.h exports + gf_utf8_wcstombs + gf_utf8_mbstowcs + gf_utf8_wcslen + gf_utf8_reorder_bidi + +;download.h exports + gf_dm_sess_get_cache_name + gf_dm_sess_get_stats + gf_dm_sess_fetch_data + +;xml.h exports + gf_xml_sax_new + gf_xml_sax_del + gf_xml_sax_init + gf_xml_sax_parse + gf_xml_sax_suspend + gf_xml_sax_parse_file + gf_xml_sax_get_line + gf_xml_sax_get_file_size + gf_xml_sax_get_file_pos + gf_xml_sax_peek_node + gf_xml_sax_binary_file + gf_xml_sax_get_error + gf_xml_get_root_type + gf_xml_sax_get_node_start_pos + gf_xml_sax_get_node_end_pos + gf_xml_dom_new + gf_xml_dom_del + gf_xml_dom_parse + gf_xml_dom_get_root + gf_xml_dom_get_error + gf_xml_dom_get_line + +;math.h exports +; gf_mulfix +; gf_muldiv +; gf_divfix +; gf_invfix +; gf_cos +; gf_sin +; gf_tan +; gf_atan2 +; gf_sqrt +; gf_ceil +; gf_floor +; gf_acos +; gf_asin + + gf_angle_diff + gf_v2d_len + gf_v2d_from_polar + gf_rect_union + gf_rect_center + gf_rect_overlaps + gf_rect_equal + gf_rect_pixelize + gf_mx2d_add_matrix + gf_mx2d_pre_multiply + gf_mx2d_add_translation + gf_mx2d_add_rotation + gf_mx2d_add_scale + gf_mx2d_add_scale_at + gf_mx2d_add_skew + gf_mx2d_add_skew_x + gf_mx2d_add_skew_y + gf_mx2d_inverse + gf_mx2d_apply_coords + gf_mx2d_apply_point + gf_mx2d_apply_rect + gf_vec_len + gf_vec_lensq + gf_vec_dot + gf_vec_norm + gf_vec_scale + gf_vec_cross + gf_quat_to_rotation + gf_quat_from_matrix + gf_quat_from_rotation + gf_quat_get_inv + gf_quat_multiply + gf_quat_rotate + gf_quat_from_axis_cos + gf_quat_slerp + gf_bbox_refresh + gf_bbox_from_rect + gf_rect_from_bbox + gf_bbox_grow_point + gf_bbox_union + gf_bbox_equal + gf_bbox_point_inside + gf_bbox_get_vertices + gf_mx_from_mx2d + gf_mx_equal + gf_mx_add_translation + gf_mx_add_scale + gf_mx_add_rotation + gf_mx_add_matrix + gf_mx_add_matrix_2d + gf_mx_inverse + gf_mx_apply_vec + gf_mx_apply_rect + gf_mx_ortho + gf_mx_perspective + gf_mx_lookat + gf_mx_apply_bbox + gf_mx_add_matrix_4x4 + gf_mx_inverse_4x4 + gf_mx_apply_vec_4x4 + gf_mx_decompose + gf_mx_rotate_vector + gf_mx_rotation_matrix_from_vectors + gf_mx2d_from_mx + gf_mx_apply_plane + gf_plane_get_distance + gf_plane_intersect_line + gf_closest_point_to_line + gf_plane_get_p_vertex_idx + gf_bbox_plane_relation + gf_mx_apply_ray + gf_ray + gf_ray_hit_box + gf_ray_hit_sphere + gf_ray_hit_triangle + gf_ray_hit_triangle_backcull + +;color.h exports + gf_stretch_bits + gf_cmx_init + gf_cmx_set + gf_cmx_set_all + gf_cmx_copy + gf_cmx_multiply + gf_cmx_apply + gf_cmx_apply_fixed + + +;path2d.h exports + gf_path_new + gf_path_del + gf_path_reset + gf_path_close + gf_path_add_move_to + gf_path_add_move_to_vec + gf_path_add_line_to + gf_path_add_line_to_vec + gf_path_add_cubic_to + gf_path_add_cubic_to_vec + gf_path_add_quadratic_to + gf_path_add_quadratic_to_vec + gf_path_add_rect_center + gf_path_add_rect + gf_path_add_ellipse + gf_path_add_bezier + gf_path_add_arc_to + gf_path_add_svg_arc_to + gf_path_add_arc + gf_path_get_control_bounds + gf_path_get_bounds + gf_path_flatten + gf_path_get_flatten + gf_path_iterator_new + gf_path_iterator_del + gf_path_iterator_get_length + gf_path_iterator_get_transform + gf_polygone2d_get_convexity + gf_path_get_outline + gf_path_point_over + +;mpeg4_odf.h exports + gf_odf_slc_set_pref + gf_odf_get_bifs_config + gf_odf_get_text_config + gf_odf_get_ui_config + gf_odf_get_laser_config + gf_odf_encode_ui_config + gf_odf_codec_new + gf_odf_codec_del + gf_odf_codec_add_com + gf_odf_codec_encode + gf_odf_codec_get_au + gf_odf_codec_set_au + gf_odf_codec_decode + gf_odf_codec_get_com + gf_odf_com_new + gf_odf_com_del + gf_odf_desc_new + gf_odf_desc_del + gf_odf_desc_read + gf_odf_desc_write + gf_odf_desc_size + gf_odf_desc_copy + gf_odf_desc_add_desc + gf_odf_desc_esd_new + gf_odf_desc_list_read + gf_odf_desc_list_write + gf_odf_desc_list_size + gf_odf_desc_list_del + gf_odf_stream_type_name + gf_odf_stream_type_by_name + gf_odf_qos_new + gf_odf_qos_del + gf_odf_qos_add_qualif + gf_oci_event_new + gf_oci_event_del + gf_oci_event_set_start_time + gf_oci_event_set_duration + gf_oci_event_add_desc + gf_oci_event_get_id + gf_oci_event_get_start_time + gf_oci_event_get_duration + gf_oci_event_get_desc_count + gf_oci_event_get_desc + gf_oci_event_rem_desc + gf_oci_codec_new + gf_oci_codec_del + gf_oci_codec_add_event + gf_oci_codec_encode + gf_oci_codec_decode + gf_oci_codec_get_event + gf_odf_dump_au + gf_odf_dump_com + gf_odf_dump_desc + gf_odf_dump_com_list + gf_oci_dump_event + gf_oci_dump_au + gf_odf_avc_cfg_new + gf_odf_avc_cfg_del + gf_odf_avc_cfg_read + gf_odf_avc_cfg_write + gf_sl_packetize + gf_sl_get_header_size + gf_sl_depacketize +; for MPEG-2 plugin + gf_odf_parse_descriptor + gf_odf_write_descriptor + +;isomedia.h exports + gf_isom_sample_new + gf_isom_sample_del + gf_isom_last_error + gf_isom_probe_file + gf_isom_open + gf_isom_close + gf_isom_delete + gf_isom_get_mode + gf_isom_open_progressive + gf_isom_get_missing_bytes + gf_isom_is_fragmented + gf_isom_is_track_fragmented + gf_isom_refresh_fragmented + gf_isom_get_track_count + gf_isom_get_timescale + gf_isom_get_duration + gf_isom_get_creation_time + gf_isom_get_track_id + gf_isom_get_track_by_id + gf_isom_is_track_enabled + gf_isom_is_track_encrypted + gf_isom_get_track_duration + gf_isom_get_media_type + gf_isom_get_media_subtype + gf_isom_get_mpeg4_subtype + gf_isom_get_media_time + gf_isom_get_sample_description_count + gf_isom_get_sample_description_index + gf_isom_is_self_contained + gf_isom_get_media_duration + gf_isom_get_media_timescale + gf_isom_get_max_chunk_duration + gf_isom_get_handler_name + gf_isom_get_media_language + gf_isom_check_data_reference + gf_isom_get_data_reference + gf_isom_get_sample_count + gf_isom_set_sample_padding + gf_isom_get_sample + gf_isom_get_sample_info + gf_isom_get_sample_for_media_time + gf_isom_get_sample_for_movie_time + gf_isom_get_sample_dts + gf_isom_get_sample_duration + gf_isom_get_edit_segment_count + gf_isom_get_edit_segment + gf_isom_get_copyright_count + gf_isom_get_copyright + gf_isom_get_watermark + gf_isom_has_sync_points + gf_isom_has_time_offset + gf_isom_get_sync_point_count + gf_isom_has_root_od + gf_isom_get_root_od + gf_isom_is_track_in_root_od + gf_isom_get_esd + gf_isom_get_decoder_config + gf_isom_get_reference_count + gf_isom_get_reference + gf_isom_get_filename + gf_isom_get_brand_info + gf_isom_get_alternate_brand + gf_isom_has_padding_bits + gf_isom_get_visual_info + gf_isom_get_pixel_aspect_ratio + gf_isom_get_audio_info + gf_isom_get_chapter_count + gf_isom_get_chapter + gf_isom_get_user_data_count + gf_isom_get_user_data + gf_isom_get_generic_sample_description + gf_isom_get_fragment_defaults + gf_isom_get_sample_fragment_count + gf_isom_get_sample_fragment_size + gf_isom_sdp_get + gf_isom_sdp_track_get + gf_isom_set_default_sync_track + gf_isom_dump + gf_isom_dump_hint_sample + gf_isom_3gp_config_get + gf_isom_text_set_streaming_mode + gf_isom_text_dump + gf_isom_get_track_layout_info + gf_isom_get_pl_indication + gf_isom_is_media_encrypted + gf_isom_get_ismacryp_info + gf_isom_is_ismacryp_media + gf_isom_ismacryp_delete_sample + gf_isom_get_ismacryp_sample + gf_isom_avc_config_get + gf_isom_3gp_config_get + gf_isom_get_meta_item_count + gf_isom_get_meta_item_by_id + gf_isom_get_meta_item_info + gf_isom_get_meta_type + gf_isom_has_meta_xml + gf_isom_has_movie + gf_isom_is_single_av + gf_isom_guess_specification + gf_isom_reset_hint_reader + gf_isom_next_hint_packet + gf_isom_has_sync_shadows + gf_isom_has_sample_dependency + gf_isom_find_od_for_track + gf_isom_apple_get_tag + gf_isom_get_media_data_size + gf_isom_get_omadrm_info + gf_isom_is_omadrm_media + gf_isom_get_dims_description + gf_isom_new_dims_description + gf_isom_update_dims_description + gf_isom_get_track_switch_group_count + gf_isom_get_track_switch_parameter + gf_isom_get_next_alternate_group_id + gf_isom_get_payt_count + gf_isom_get_payt_info + gf_isom_get_meta_primary_item_id + gf_isom_is_JPEG2000 + + +;NON-READ-ONLY exports + gf_isom_set_timescale + gf_isom_new_track + gf_isom_remove_track + gf_isom_set_track_enabled + gf_isom_set_track_id + gf_isom_add_sample + gf_isom_add_sample_shadow + gf_isom_append_sample_data + gf_isom_refresh_size_info + gf_isom_add_sample_reference + gf_isom_set_last_sample_duration + gf_isom_set_track_reference + gf_isom_remove_track_reference + gf_isom_update_sample + gf_isom_update_sample_reference + gf_isom_remove_sample + gf_isom_set_final_name + gf_isom_set_storage_mode + gf_isom_get_storage_mode + gf_isom_set_interleave_time + gf_isom_get_interleave_time + gf_isom_set_copyright + gf_isom_remove_copyright + gf_isom_set_watermark + gf_isom_set_edit_segment + gf_isom_modify_edit_segment + gf_isom_append_edit_segment + gf_isom_remove_edit_segments + gf_isom_remove_edit_segment + gf_isom_set_media_language + gf_isom_set_handler_name + gf_isom_add_user_data + gf_isom_remove_user_data + gf_isom_remove_user_data_item + gf_isom_use_compact_size + gf_isom_set_brand_info + gf_isom_modify_alternate_brand + gf_isom_set_sample_padding_bits + gf_isom_set_visual_info + gf_isom_set_pixel_aspect_ratio + gf_isom_set_track_layout_info + gf_isom_set_audio_info + gf_isom_add_sample_fragment + gf_isom_remove_sample_fragment + gf_isom_set_cts_packing + gf_isom_modify_cts_offset + gf_isom_remove_cts_info + gf_isom_set_track_name + gf_isom_get_track_name + gf_isom_set_pl_indication + gf_isom_set_root_od_id + gf_isom_set_root_od_url + gf_isom_remove_root_od + gf_isom_add_desc_to_root_od + gf_isom_add_track_to_root_od + gf_isom_remove_track_from_root_od + gf_isom_new_mpeg4_description + gf_isom_change_mpeg4_description + gf_isom_add_desc_to_description + gf_isom_new_generic_sample_description + gf_isom_change_generic_sample_description + gf_isom_remove_sample_description + gf_isom_clone_sample_description + gf_isom_clone_track + gf_isom_clone_pl_indications + gf_isom_clone_root_od + gf_isom_is_same_sample_description + gf_isom_add_chapter + gf_isom_remove_chapter + gf_isom_setup_track_fragment + gf_isom_finalize_for_fragment + gf_isom_start_fragment + gf_isom_set_fragment_option + gf_isom_fragment_add_sample + gf_isom_fragment_append_data + gf_isom_remove_sync_shadows + gf_isom_set_sync_shadow + gf_isom_set_track_group + gf_isom_set_track_priority_in_group + gf_isom_set_max_samples_per_chunk + gf_isom_set_extraction_slc + gf_isom_get_extraction_slc + gf_isom_get_track_group + gf_isom_get_track_priority_in_group + gf_isom_store_movie_config + gf_isom_load_movie_config + gf_isom_make_interleave + gf_isom_setup_hint_track + gf_isom_new_hint_description + gf_isom_begin_hint_sample + gf_isom_end_hint_sample + gf_isom_hint_blank_data + gf_isom_hint_direct_data + gf_isom_hint_sample_data + gf_isom_hint_sample_description_data + gf_isom_rtp_packet_begin + gf_isom_rtp_packet_set_flags + gf_isom_rtp_packet_set_offset + gf_isom_rtp_set_timescale + gf_isom_rtp_set_time_offset + gf_isom_rtp_set_time_sequence_offset + gf_isom_sdp_add_track_line + gf_isom_sdp_clean_track + gf_isom_sdp_add_line + gf_isom_sdp_clean + gf_isom_3gp_config_new + gf_isom_3gp_config_update + gf_isom_new_text_description + gf_isom_new_text_sample + gf_isom_delete_text_sample + gf_isom_text_reset + gf_isom_text_add_text + gf_isom_text_add_style + gf_isom_text_add_highlight + gf_isom_text_set_highlight_color + gf_isom_text_add_karaoke + gf_isom_text_set_karaoke_segment + gf_isom_text_set_scroll_delay + gf_isom_text_add_hyperlink + gf_isom_text_set_box + gf_isom_text_add_blink + gf_isom_text_set_wrap + gf_isom_text_to_sample + gf_isom_remove_ismacryp_protection + gf_isom_set_ismacryp_protection + gf_isom_change_ismacryp_protection + gf_isom_dump_ismacryp_protection + gf_isom_dump_ismacryp_sample + gf_isom_avc_config_new + gf_isom_avc_config_update + gf_isom_3gp_config_new + gf_isom_3gp_config_update + gf_isom_set_media_timescale + gf_isom_estimate_size + gf_isom_set_meta_type + gf_isom_add_meta_item + gf_isom_remove_meta_item + gf_isom_set_meta_primary_item + gf_isom_set_meta_xml + gf_isom_remove_meta_xml + gf_isom_extract_meta_xml + gf_isom_extract_meta_item + gf_isom_apple_set_tag + gf_isom_get_next_alternate_group_id + gf_isom_set_ipod_compatible + gf_media_make_psp + gf_isom_reset_switch_parameters + gf_isom_reset_track_switch_parameter + gf_isom_set_track_switch_parameter + gf_isom_set_alternate_group_id +;end GPAC_READ_ONLY + +;isomedia_dev.h exports + gf_isom_parse_texte_sample + +;plugin_network.h exports + gf_term_on_message + gf_term_on_connect + gf_term_on_disconnect + gf_term_on_command + gf_term_on_sl_packet + gf_term_get_service_url + gf_term_add_media + gf_term_register_mime_type + gf_term_check_extension + gf_term_download_new + gf_term_download_del + gf_term_download_update_stats + gf_term_scene_update + gf_term_get_screen_buffer + gf_term_release_screen_buffer + +;ietf.h exports + gf_rtsp_nc_to_string + gf_rtsp_range_parse + gf_rtsp_range_new + gf_rtsp_range_del + gf_rtsp_transport_clone + gf_rtsp_transport_del + gf_rtsp_command_new + gf_rtsp_command_del + gf_rtsp_command_reset + gf_rtsp_response_new + gf_rtsp_response_del + gf_rtsp_response_reset + gf_rtsp_session_new + gf_rtsp_session_del + gf_rtsp_set_buffer_size + gf_rtsp_set_mobile_ip + gf_rtsp_session_reset + gf_rtsp_is_my_session + gf_rtsp_get_last_session_id + gf_rtsp_get_server_name + gf_rtsp_get_service_name + gf_rtsp_get_response + gf_rtsp_get_session_state + gf_rtsp_get_last_request + gf_rtsp_reset_aggregation + gf_rtsp_send_command + gf_rtsp_set_interleave_callback + gf_rtsp_session_read + gf_rtsp_register_interleave + gf_rtsp_unregister_interleave + gf_rtsp_session_new_server + gf_rtsp_get_command + gf_rtsp_load_service_name + gf_rtsp_generate_session_id + gf_rtsp_send_response + gf_rtsp_get_session_ip + gf_rtsp_get_next_interleave_id + gf_rtsp_get_remote_address + gf_rtsp_get_session_port + gf_rtp_new + gf_rtp_del + gf_rtp_setup_transport + gf_rtp_set_ports + gf_rtp_setup_payload + gf_rtp_enable_nat_keepalive + gf_rtp_initialize + gf_rtp_set_info_rtp + gf_rtp_get_current_time + gf_rtp_reset_buffers + gf_rtp_read_rtp + gf_rtp_read_rtcp + gf_rtp_decode_rtp + gf_rtp_decode_rtcp + gf_rtp_send_rtcp_report + gf_rtp_send_bye + gf_rtp_send_packet + gf_rtp_set_info_rtcp + gf_rtp_is_unicast + gf_rtp_is_interleaved + gf_rtp_get_clockrate + gf_rtp_is_active + gf_rtp_get_low_interleave_id + gf_rtp_get_hight_interleave_id + gf_rtp_get_transport + gf_rtp_get_local_ssrc + gf_rtp_get_loss + gf_rtp_get_tcp_bytes_sent + gf_rtp_get_ports + gf_sdp_info_new + gf_sdp_info_del + gf_sdp_info_reset + gf_sdp_info_parse + gf_sdp_info_check + gf_sdp_info_write + gf_sdp_media_new + gf_sdp_media_del + gf_sdp_conn_new + gf_sdp_conn_del + gf_sdp_fmtp_new + gf_sdp_fmtp_del + gf_rtp_builder_new + gf_rtp_builder_del + gf_rtp_builder_init + gf_rtp_builder_process + gf_rtp_builder_format_sdp + gf_rtp_builder_get_payload_name + gf_rtp_depacketizer_new + gf_rtp_depacketizer_del + gf_rtp_depacketizer_reset + gf_rtp_depacketizer_process + gf_rtp_depacketizer_get_slconfig + +;avilib exports + AVI_write_frame + AVI_close + AVI_open_output_file + AVI_set_video + +;media.h exports + gf_hinter_track_new + gf_hinter_track_del + gf_hinter_track_process + gf_hinter_track_finalize + gf_hinter_finalize + gf_hinter_track_get_bandwidth + gf_hinter_track_get_flags + gf_hinter_track_get_payload_name + gf_hinter_can_embbed_data + gf_hinter_format_ttxt_sdp + gf_media_make_isma + gf_media_make_3gpp + gf_media_map_esd + gf_media_import + gf_media_export + gf_media_fragment_file + gf_media_import_chapters + gf_media_change_par + gf_media_get_file_hash + gf_rtp_packetizer_create_and_init_from_file + + +;mcrypt exports + gf_crypt_open + gf_crypt_close + gf_crypt_init + gf_crypt_decrypt + gf_crypt_encrypt + gf_crypt_set_state + +;parsers_av.h exports + gf_m4v_parser_new + gf_m4v_parser_del + gf_m4v_parse_config + gf_m4v_parse_frame + gf_m4v_get_object_start + gf_m4v_is_valid_object_type + gf_m4v_get_profile_name + gf_m4v_get_config + gf_m4v_rewrite_pl + gf_mp3_num_channels + gf_mp3_sampling_rate + gf_mp3_window_size + gf_mp3_object_type_indication + gf_mp3_layer + gf_mp3_version + gf_mp3_version_name + gf_mp3_frame_size + gf_mp3_get_next_header + gf_mp3_get_next_header_mem + gf_m4a_get_config + gf_m4a_write_config + gf_m4a_object_type_name + gf_m4a_get_profile_name + gf_vorbis_parse_header + gf_vorbis_check_frame + gf_avc_get_sps_info + gf_avc_get_profile_name + gf_img_parse + gf_img_jpeg_dec + gf_img_png_dec + gf_img_png_enc + gf_ac3_parser_bs + + +;ismacryp.h exports + gf_ismacryp_crypt_file + gf_ismacryp_decrypt_file + gf_ismacryp_encrypt_track + gf_ismacryp_decrypt_track + gf_ismacryp_gpac_get_info + gf_ismacryp_mpeg4ip_get_info + +;scene_manager.h exports + gf_sm_new + gf_sm_del + gf_sm_stream_new + gf_sm_stream_del + gf_sm_stream_au_new + gf_sm_make_random_access + gf_sm_reset + gf_sm_load_init + gf_sm_load_done + gf_sm_load_string + gf_sm_load_run + gf_sm_import_bifs_subtitle + gf_sm_dump + gf_sm_encode_to_file + gf_sm_dumper_new + gf_sm_dumper_del + gf_sm_dump_command_list + gf_sm_dump_graph + gf_sm_stats_new + gf_sm_stats_del + gf_sm_stats_reset + gf_sm_stats_get + gf_sm_stats_for_graph + gf_sm_stats_for_scene + gf_sm_stats_for_command + +;bifsengine exports + gf_beng_init + gf_beng_init_from_string + gf_beng_init_from_context + gf_beng_get_stream_config + gf_beng_encode_context + gf_beng_encode_from_file + gf_beng_encode_from_string + gf_beng_save_context + gf_beng_aggregate_context + gf_beng_terminate + +;bifs.h exports + gf_bifs_decoder_new + gf_bifs_decoder_del + gf_bifs_decoder_ignore_size_info + gf_bifs_decoder_configure_stream + gf_bifs_decoder_remove_stream + gf_bifs_decode_au + gf_bifs_decode_command_list + gf_bifs_encoder_new + gf_bifs_encoder_del + gf_bifs_encoder_new_stream + gf_bifs_encode_au + gf_bifs_encoder_get_config + gf_bifs_encoder_get_version + gf_bifs_encoder_get_rap + gf_bifs_get_aq_info + gf_bifs_get_node_type + + +;scenegraph.h and scenegraph_vrml.h exports + gf_node_get_tag + gf_node_set_id + gf_node_get_name + gf_node_get_id + gf_node_get_name_and_id + gf_node_get_private + gf_node_set_private + gf_node_set_callback_function + gf_node_register + gf_node_unregister + gf_node_replace + gf_node_unregister_children + gf_node_insert_child + gf_node_remove_child + gf_node_replace_child + gf_node_event_out + gf_node_event_out_str + gf_node_allow_cyclic_traverse + gf_node_traverse + gf_node_traverse_children + gf_node_get_parent_count + gf_node_get_parent + gf_node_get_class_name + gf_node_dirty_set + gf_node_dirty_clear + gf_node_dirty_get + gf_node_dirty_reset + gf_node_get_field + gf_node_get_field_by_name + gf_node_get_graph + gf_node_init + gf_node_get_scene_time + gf_sg_new + gf_sg_new_subscene + gf_sg_set_scene_time_callback + gf_sg_set_node_callback + gf_sg_set_proto_loader + gf_sg_del + gf_sg_set_private + gf_sg_get_private + gf_sg_set_scene_size_info + gf_sg_use_pixel_metrics + gf_sg_get_scene_size_info + gf_sg_reset + gf_sg_get_root_node + gf_sg_set_root_node + gf_sg_find_node + gf_sg_find_node_by_name + gf_node_changed + gf_node_new + gf_node_clone + gf_sg_vrml_get_event_type_name + gf_sg_vrml_get_field_type_by_name + gf_sg_vrml_field_pointer_new + gf_sg_vrml_field_pointer_del + gf_sg_vrml_is_sf_field + gf_sg_vrml_get_sf_type + gf_sg_vrml_mf_insert + gf_sg_vrml_mf_append + gf_sg_vrml_mf_remove + gf_sg_vrml_mf_alloc + gf_sg_vrml_mf_get_item + gf_sg_vrml_mf_reset + gf_sg_vrml_field_copy + gf_sg_vrml_field_equal + gf_sg_route_new + gf_sg_route_del + gf_sg_route_del_by_id + gf_sg_route_find + gf_sg_route_find_by_name + gf_sg_route_set_id + gf_sg_route_get_id + gf_sg_route_set_name + gf_sg_route_get_name + gf_sg_activate_routes + gf_sg_proto_new + gf_sg_proto_del + gf_sg_proto_set_in_graph + gf_sg_proto_get_graph + gf_sg_proto_set_private + gf_sg_proto_get_private + gf_sg_proto_get_extern_url + gf_sg_proto_add_node_code + gf_sg_proto_get_field_count + gf_sg_proto_field_find_by_name + gf_sg_proto_field_find + gf_sg_proto_field_new + gf_sg_proto_field_set_ised + gf_sg_proto_field_set_private + gf_sg_proto_field_get_private + gf_sg_proto_field_get_field + gf_sg_proto_create_instance + gf_sg_proto_load_code + gf_sg_find_proto + gf_sg_delete_all_protos + gf_node_get_proto + gf_sg_proto_get_id + gf_sg_proto_get_class_name + gf_sg_proto_field_is_sftime_offset + gf_sg_proto_instance_set_ised + gf_sg_script_field_new + gf_sg_script_field_get_info + gf_sg_script_load + gf_sg_script_event_in + gf_sg_has_scripting + gf_bifs_proto_field_set_aq_info + gf_node_get_num_fields_in_mode + gf_sg_command_new + gf_sg_command_del + gf_sg_command_apply + gf_sg_command_apply_list + gf_sg_command_field_new + gf_sg_command_clone + gf_sg_get_next_available_node_id + gf_sg_get_max_node_id + gf_sg_get_next_available_route_id + gf_sg_set_max_defined_route_id + gf_sg_get_next_available_proto_id + gf_sg_sfrotation_interpolate + gf_node_get_field_count + gf_node_mpeg4_type_by_class_name + gf_node_x3d_type_by_class_name + gf_x3d_get_node_type + gf_sg_mfurl_del + gf_sg_sfcolor_to_rgba + gf_node_list_add_child + gf_node_list_insert_child + gf_node_list_del_child + gf_node_list_find_child + gf_node_list_get_child + gf_node_list_get_count + gf_node_list_del_child_idx + +;terminal.h exports + gf_term_new + gf_term_del + gf_term_connect + gf_term_disconnect + gf_term_navigate_to + gf_term_play_from_time + gf_term_set_option + gf_term_get_option + gf_term_get_framerate + gf_term_get_time_in_ms + gf_term_get_viewpoint + gf_term_set_viewpoint + gf_term_get_root_object + gf_term_get_object_count + gf_term_get_object + gf_term_object_subscene_type + gf_term_select_object + gf_term_get_object_info + gf_term_get_download_info + gf_term_get_channel_net_info + gf_term_get_world_info + gf_term_dump_scene + gf_term_set_size + gf_term_user_event + gf_term_mouse_input + gf_term_keyboard_input + gf_term_string_input + gf_term_connect_from_time + gf_term_add_object + gf_term_is_supported_url + gf_term_attach_service + gf_term_set_simulation_frame_rate + gf_term_get_simulation_frame_rate + gf_term_step_clocks + gf_term_process_flush + gf_term_process_step + gf_term_get_service_interface + gf_term_get_service_info + gf_term_get_text_selection + gf_term_paste_text + gf_term_get_url + +;compositor.h exports + gf_sc_new + gf_sc_del + gf_sc_set_fps + gf_sc_set_scene + gf_sc_draw_frame + gf_sc_on_node_init + gf_sc_invalidate + gf_sc_get_clock + gf_sc_lock + gf_sc_user_event + gf_sc_map_point + gf_sc_set_option + gf_sc_get_option + gf_sc_get_fps + gf_sc_get_screen_buffer + gf_sc_release_screen_buffer + gf_sc_simulation_tick + gf_sc_reset_graphics + gf_sc_pick_node + gf_sc_get_viewpoint + gf_sc_set_viewpoint + gf_sc_set_size + gf_sc_register_extra_graph + +;render_dev.h exports + gf_sc_register_time_node + gf_sc_unregister_time_node + gf_sc_texture_setup + gf_sc_texture_destroy + gf_sc_texture_get_handler + gf_sc_texture_check_url_change + gf_sc_texture_play + gf_sc_texture_play_from_to + gf_sc_texture_stop + gf_sc_texture_restart + gf_sc_texture_update_frame + gf_sc_texture_release_stream + gf_sc_audio_register + gf_sc_audio_unregister + gf_sc_audio_setup + gf_sc_audio_open + gf_sc_audio_stop + gf_sc_audio_restart + gf_sc_get_compositor + + +;terminal_dev exports + gf_inline_new + gf_inline_del + gf_inline_get_time + gf_inline_attach_to_compositor + gf_inline_find_odm + gf_inline_set_duration + gf_inline_setup_object + gf_inline_default_scene_viewpoint + gf_inline_register_extra_graph + gf_inline_force_scene_size + gf_inline_get_proto_lib + gf_inline_process_anchor + gf_inline_disconnect + gf_odm_new + gf_odm_setup_object + gf_odm_disconnect + gf_odm_setup_es + gf_odm_remove_es + gf_term_message + gf_term_node_callback + gf_clock_time + +;mpegts.h exports + gf_m2ts_demux_new + gf_m2ts_demux_del + gf_m2ts_process_data + gf_m2ts_reset_parsers + gf_m2ts_set_pes_framing + gf_m2ts_get_stream_name + gf_m2ts_crc32_check + gf_m2ts_decode_mjd_date + +;laser.h exports + gf_laser_decoder_new + gf_laser_decoder_del + gf_laser_decoder_set_clock + gf_laser_decoder_configure_stream + gf_laser_decoder_remove_stream + gf_laser_decode_au + gf_laser_decode_command_list + gf_laser_encoder_new + gf_laser_encoder_del + gf_laser_encoder_new_stream + gf_laser_encode_au + gf_laser_encoder_get_config + gf_laser_encoder_get_rap + + +;SVG exports for SVG->BIFS + gf_svg_parse_attribute + gf_svg_parse_style + gf_node_get_attribute_by_tag + gf_svg_flatten_attributes + gf_svg_apply_inheritance + gf_svg_properties_init_pointers + gf_svg_properties_reset_pointers + gf_node_register_iri + gf_node_unregister_iri + diff --git a/src/libgpac_ce.def b/src/libgpac_ce.def new file mode 100644 index 0000000..07c3812 --- /dev/null +++ b/src/libgpac_ce.def @@ -0,0 +1,1207 @@ +LIBRARY libgpac.dll + +;Note: the APIs and headers are likely to change a lot until 0.5.0 is reached, until all symbols needed +;by different scene decoders (SVG, X3D, etc) are identified + +EXPORTS +;setup.h exports, winCE only + CE_Assert + CE_WideToChar + CE_CharToWide + +;tools.h exports + gf_sleep + gf_sys_init + gf_sys_close + gf_sys_clock + gf_sys_get_rti + gf_sys_get_battery_state + gf_4cc_to_str + gf_error_to_string + gf_rand_init + gf_rand + gf_utc_time_since_1970 + gf_get_user_name + gf_enum_directory + gf_log_get_tools + gf_log_get_level + gf_log_set_level + gf_log_set_tools + gf_log_set_callback + gf_log + gf_log_lt + gf_set_progress + gf_set_progress_callback + gf_malloc + gf_realloc + gf_free + gf_strdup + gf_crc_32 + + +;array.h exports + gf_list_new + gf_list_del + gf_list_count + gf_list_add + gf_list_insert + gf_list_rem + gf_list_get + gf_list_enum + gf_list_find + gf_list_del_item + gf_list_reset + gf_list_last + gf_list_rem_last + +;bitstream.h exports + gf_bs_new + gf_bs_from_file + gf_bs_del + gf_bs_read_bit + gf_bs_read_int + gf_bs_read_long_int + gf_bs_read_u8 + gf_bs_read_u16 + gf_bs_read_u24 + gf_bs_read_u32 + gf_bs_read_u64 + gf_bs_read_float + gf_bs_read_double + gf_bs_read_data + gf_bs_write_int + gf_bs_write_long_int + gf_bs_write_u8 + gf_bs_write_u16 + gf_bs_write_u24 + gf_bs_write_u32 + gf_bs_write_u64 + gf_bs_write_float + gf_bs_write_double + gf_bs_write_data + gf_bs_set_eos_callback + gf_bs_align + gf_bs_available + gf_bs_get_content + gf_bs_skip_bytes + gf_bs_seek + gf_bs_peek_bits + gf_bs_get_position + gf_bs_get_size + gf_bs_get_refreshed_size + gf_delete_file + ;gf_temp_file_new + +;thread.h exports + gf_th_new + gf_th_del + gf_th_run + gf_th_stop + gf_th_status + gf_th_set_priority + gf_th_id + gf_mx_new + gf_mx_del + gf_mx_v + gf_mx_p + gf_mx_try_lock + gf_sema_new + gf_sema_del + gf_sema_notify + gf_sema_wait + gf_sema_wait_for + +;net.h exports + gf_sk_new + gf_sk_del + gf_sk_reset + gf_sk_set_buffer_size + gf_sk_set_block_mode + gf_sk_get_handle + gf_sk_bind + gf_sk_connect + gf_sk_send + gf_sk_receive + gf_sk_listen + gf_sk_accept + gf_sk_server_mode + gf_sk_get_host_name + gf_sk_get_local_ip + gf_sk_get_local_info + gf_sk_get_remote_address + gf_sk_send_to + gf_sk_setup_multicast + gf_sk_is_multicast_address + gf_sk_send_wait + gf_sk_receive_wait + gf_net_get_ntp + gf_net_has_ipv6 + gf_net_is_ipv6 + + gf_url_is_local + gf_url_get_absolute_path + gf_url_concatenate + +;base_coding.h exports + gf_base64_encode + gf_base64_decode + gf_base16_encode + gf_base16_decode + +;token.h exports + gf_token_get + gf_token_get_line + gf_token_find + +;config.h exports + gf_cfg_new + gf_cfg_del + gf_cfg_get_key + gf_cfg_set_key + gf_cfg_get_section_count + gf_cfg_get_section_name + gf_cfg_get_key_count + gf_cfg_get_key_name + gf_cfg_insert_key + +;module.h exports + gf_modules_new + gf_modules_del + gf_modules_refresh + gf_modules_get_count + gf_modules_get_file_name + gf_modules_load_interface + gf_modules_load_interface_by_name + gf_modules_close_interface + gf_modules_get_option + gf_modules_set_option + gf_modules_get_config + +;utf.h exports + gf_utf8_wcstombs + gf_utf8_mbstowcs + gf_utf8_wcslen + gf_utf8_is_right_to_left + gf_utf8_reorder_bidi + +;download.h exports + gf_dm_sess_get_cache_name + gf_dm_sess_get_stats + gf_dm_sess_fetch_data + gf_dm_sess_last_error + +;xml.h exports + gf_xml_sax_new + gf_xml_sax_del + gf_xml_sax_init + gf_xml_sax_parse + gf_xml_sax_suspend + gf_xml_sax_parse_file + gf_xml_sax_get_line + gf_xml_sax_get_file_size + gf_xml_sax_get_file_pos + gf_xml_sax_peek_node + gf_xml_sax_binary_file + gf_xml_sax_get_error + gf_xml_get_root_type + gf_xml_sax_get_node_start_pos + gf_xml_sax_get_node_end_pos + gf_xml_dom_new + gf_xml_dom_del + gf_xml_dom_parse + gf_xml_dom_get_root + gf_xml_dom_get_error + gf_xml_dom_get_line + +;math.h exports + gf_invfix + gf_mulfix + gf_muldiv + gf_divfix + gf_cos + gf_sin + gf_tan + gf_atan2 + gf_sqrt + gf_ceil + gf_floor + gf_acos + gf_asin + + gf_angle_diff + gf_v2d_len + gf_v2d_from_polar + gf_rect_union + gf_rect_center + gf_rect_overlaps + gf_rect_equal + gf_rect_pixelize + gf_mx2d_add_matrix + gf_mx2d_pre_multiply + gf_mx2d_add_translation + gf_mx2d_add_rotation + gf_mx2d_add_scale + gf_mx2d_add_scale_at + gf_mx2d_add_skew + gf_mx2d_add_skew_x + gf_mx2d_add_skew_y + gf_mx2d_inverse + gf_mx2d_apply_coords + gf_mx2d_apply_point + gf_mx2d_apply_rect + +;exported for 3D tools + gf_vec_len + gf_vec_lensq + gf_vec_dot + gf_vec_norm + gf_vec_scale + gf_vec_cross + gf_quat_to_rotation + gf_quat_from_matrix + gf_quat_from_rotation + gf_quat_get_inv + gf_quat_multiply + gf_quat_rotate + gf_quat_from_axis_cos + gf_quat_slerp + gf_bbox_refresh + gf_bbox_from_rect + gf_rect_from_bbox + gf_bbox_grow_point + gf_bbox_union + gf_bbox_equal + gf_bbox_point_inside + gf_bbox_get_vertices + gf_mx_from_mx2d + gf_mx_equal + gf_mx_add_translation + gf_mx_add_scale + gf_mx_add_rotation + gf_mx_add_matrix + gf_mx_inverse + gf_mx_apply_vec + gf_mx_apply_rect + gf_mx_ortho + gf_mx_perspective + gf_mx_lookat + gf_mx_apply_bbox + gf_mx_add_matrix_4x4 + gf_mx_inverse_4x4 + gf_mx_apply_vec_4x4 + gf_mx_decompose + gf_mx_rotate_vector + gf_mx_rotation_matrix_from_vectors + gf_mx2d_from_mx + gf_mx_apply_plane + gf_mx_add_matrix_2d + gf_plane_get_distance + gf_plane_intersect_line + gf_closest_point_to_line + gf_plane_get_p_vertex_idx + gf_bbox_plane_relation + gf_mx_apply_ray + gf_ray + gf_ray_hit_box + gf_ray_hit_sphere + gf_ray_hit_triangle + gf_ray_hit_triangle_backcull + +;path2d.h exports + gf_path_new + gf_path_del + gf_path_reset + gf_path_close + gf_path_add_move_to + gf_path_add_move_to_vec + gf_path_add_line_to + gf_path_add_line_to_vec + gf_path_add_cubic_to + gf_path_add_cubic_to_vec + gf_path_add_quadratic_to + gf_path_add_quadratic_to_vec + gf_path_add_rect_center + gf_path_add_rect + gf_path_add_ellipse + gf_path_add_bezier + gf_path_add_arc_to + gf_path_add_svg_arc_to + gf_path_add_arc + gf_path_get_control_bounds + gf_path_get_bounds + gf_path_flatten + gf_path_get_flatten + gf_path_iterator_new + gf_path_iterator_del + gf_path_iterator_get_length + gf_path_iterator_get_transform + gf_polygone2d_get_convexity + gf_path_get_outline + gf_path_point_over + +;yuv.h exports + gf_cmx_init + gf_cmx_set + gf_cmx_set_all + gf_cmx_copy + gf_cmx_multiply + gf_cmx_apply + gf_cmx_apply_fixed + gf_stretch_bits + +;mpeg4_odf.h exports + gf_odf_slc_set_pref + gf_odf_get_bifs_config + gf_odf_get_text_config + gf_odf_get_ui_config + gf_odf_encode_ui_config + gf_odf_codec_new + gf_odf_codec_del + gf_odf_codec_add_com + gf_odf_codec_encode + gf_odf_codec_get_au + gf_odf_codec_set_au + gf_odf_codec_decode + gf_odf_codec_get_com + gf_odf_com_new + gf_odf_com_del + gf_odf_desc_new + gf_odf_desc_del + gf_odf_desc_read + gf_odf_desc_write + gf_odf_desc_size + gf_odf_desc_copy + gf_odf_desc_add_desc + gf_odf_desc_esd_new + gf_odf_desc_list_read + gf_odf_desc_list_write + gf_odf_desc_list_size + gf_odf_desc_list_del + gf_odf_stream_type_name + gf_odf_stream_type_by_name + gf_odf_qos_new + gf_odf_qos_del + gf_odf_qos_add_qualif + gf_oci_event_new + gf_oci_event_del + gf_oci_event_set_start_time + gf_oci_event_set_duration + gf_oci_event_add_desc + gf_oci_event_get_id + gf_oci_event_get_start_time + gf_oci_event_get_duration + gf_oci_event_get_desc_count + gf_oci_event_get_desc + gf_oci_event_rem_desc + gf_oci_codec_new + gf_oci_codec_del + gf_oci_codec_add_event + gf_oci_codec_encode + gf_oci_codec_decode + gf_oci_codec_get_event +; gf_odf_dump_au +; gf_odf_dump_com +; gf_odf_dump_desc +; gf_odf_dump_com_list +; gf_oci_dump_event +; gf_oci_dump_au +; gf_sl_packetize + gf_sl_depacketize + gf_odf_avc_cfg_new + gf_odf_avc_cfg_del + gf_odf_avc_cfg_read + gf_odf_avc_cfg_write + +;isomedia.h exports + gf_isom_sample_new + gf_isom_sample_del + gf_isom_last_error + gf_isom_probe_file + gf_isom_open + gf_isom_close + gf_isom_delete + gf_isom_get_mode + gf_isom_open_progressive + gf_isom_get_missing_bytes + gf_isom_is_fragmented + gf_isom_is_track_fragmented + gf_isom_refresh_fragmented + gf_isom_get_track_count + gf_isom_get_timescale + gf_isom_get_duration + gf_isom_get_creation_time + gf_isom_get_track_id + gf_isom_get_track_by_id + gf_isom_is_track_enabled + gf_isom_is_track_encrypted + gf_isom_get_track_duration + gf_isom_get_media_type + gf_isom_get_media_subtype + gf_isom_get_mpeg4_subtype + gf_isom_get_media_time + gf_isom_get_sample_description_count + gf_isom_get_sample_description_index + gf_isom_is_self_contained + gf_isom_get_media_duration + gf_isom_get_media_timescale + gf_isom_get_max_chunk_duration + gf_isom_get_handler_name + gf_isom_get_media_language + gf_isom_check_data_reference + gf_isom_get_data_reference + gf_isom_get_sample_count + gf_isom_set_sample_padding + gf_isom_get_sample + gf_isom_get_sample_info + gf_isom_get_sample_for_media_time + gf_isom_get_sample_for_movie_time + gf_isom_get_sample_dts + gf_isom_get_edit_segment_count + gf_isom_get_edit_segment + gf_isom_get_copyright_count + gf_isom_get_copyright + gf_isom_get_watermark + gf_isom_has_sync_points + gf_isom_has_time_offset + gf_isom_get_sync_point_count + gf_isom_has_root_od + gf_isom_get_root_od + gf_isom_is_track_in_root_od + gf_isom_get_esd + gf_isom_get_decoder_config + gf_isom_get_reference_count + gf_isom_get_reference + gf_isom_get_filename + gf_isom_get_brand_info + gf_isom_get_alternate_brand + gf_isom_has_padding_bits + gf_isom_get_visual_info + gf_isom_get_audio_info + gf_isom_get_chapter_count + gf_isom_get_chapter + gf_isom_get_user_data_count + gf_isom_get_user_data + gf_isom_get_generic_sample_description + gf_isom_get_fragment_defaults + gf_isom_get_sample_fragment_count + gf_isom_get_sample_fragment_size + gf_isom_sdp_get + gf_isom_sdp_track_get + gf_isom_set_default_sync_track +; gf_isom_dump +; gf_isom_dump_hint_sample + gf_isom_3gp_config_get + gf_isom_text_set_streaming_mode +; gf_isom_text_dump + gf_isom_get_track_layout_info + gf_isom_get_pl_indication + gf_isom_is_media_encrypted + gf_isom_get_ismacryp_info + gf_isom_is_ismacryp_media + gf_isom_ismacryp_delete_sample + gf_isom_get_ismacryp_sample + gf_isom_avc_config_get + gf_isom_3gp_config_get + gf_isom_get_meta_item_count + gf_isom_get_meta_item_by_id + gf_isom_get_meta_item_info + gf_isom_get_meta_type + gf_isom_has_meta_xml + gf_isom_has_movie + gf_isom_is_single_av + gf_isom_guess_specification + gf_isom_delete_text_sample + gf_isom_has_sync_shadows + gf_isom_apple_get_tag + gf_isom_get_omadrm_info + gf_isom_is_omadrm_media + gf_isom_get_pixel_aspect_ratio + +;NON-READ-ONLY exports +; gf_isom_set_timescale +; gf_isom_new_track +; gf_isom_remove_track +; gf_isom_set_track_enabled +; gf_isom_set_track_id +; gf_isom_add_sample +; gf_isom_add_sample_shadow +; gf_isom_append_sample_data +; gf_isom_add_sample_reference +; gf_isom_set_last_sample_duration +; gf_isom_set_track_reference +; gf_isom_remove_track_reference +; gf_isom_update_sample +; gf_isom_update_sample_reference +; gf_isom_remove_sample +; gf_isom_set_final_name +; gf_isom_set_storage_mode +; gf_isom_get_storage_mode +; gf_isom_set_interleave_time +; gf_isom_get_interleave_time +; gf_isom_set_copyright +; gf_isom_remove_copyright +; gf_isom_set_watermark +; gf_isom_set_edit_segment +; gf_isom_modify_edit_segment +; gf_isom_append_edit_segment +; gf_isom_remove_edit_segments +; gf_isom_remove_edit_segment +; gf_isom_set_media_language +; gf_isom_add_user_data +; gf_isom_remove_user_data +; gf_isom_remove_user_data_item +; gf_isom_use_compact_size +; gf_isom_set_brand_info +; gf_isom_modify_alternate_brand +; gf_isom_set_sample_padding_bits +; gf_isom_set_visual_info +; gf_isom_set_track_layout_info +; gf_isom_set_audio_info +; gf_isom_add_sample_fragment +; gf_isom_remove_sample_fragment +; gf_isom_set_cts_packing +; gf_isom_modify_cts_offset +; gf_isom_remove_cts_info +; gf_isom_set_track_name +; gf_isom_get_track_name +; gf_isom_set_pl_indication +; gf_isom_set_root_od_id +; gf_isom_set_root_od_url +; gf_isom_remove_root_od +; gf_isom_add_desc_to_root_od +; gf_isom_add_track_to_root_od +; gf_isom_remove_track_from_root_od +; gf_isom_new_mpeg4_description +; gf_isom_change_mpeg4_description +; gf_isom_add_desc_to_description +; gf_isom_new_generic_sample_description +; gf_isom_change_generic_sample_description +; gf_isom_remove_sample_description +; gf_isom_clone_sample_description +; gf_isom_clone_track +; gf_isom_clone_pl_indications +; gf_isom_is_same_sample_description +; gf_isom_add_chapter +; gf_isom_remove_chapter +; gf_isom_setup_track_fragment +; gf_isom_finalize_for_fragment +; gf_isom_start_fragment +; gf_isom_set_fragment_option +; gf_isom_fragment_add_sample +; gf_isom_fragment_append_data +; gf_isom_remove_sync_shadows +; gf_isom_set_sync_shadow +; gf_isom_set_track_group +; gf_isom_set_track_priority_in_group +; gf_isom_set_max_samples_per_chunk +; gf_isom_set_extraction_slc +; gf_isom_get_extraction_slc +; gf_isom_get_track_group +; gf_isom_get_track_priority_in_group +; gf_isom_store_movie_config +; gf_isom_load_movie_config +; gf_isom_make_interleave +; gf_isom_setup_hint_track +; gf_isom_new_hint_description +; gf_isom_begin_hint_sample +; gf_isom_end_hint_sample +; gf_isom_hint_blank_data +; gf_isom_hint_direct_data +; gf_isom_hint_sample_data +; gf_isom_hint_sample_description_data +; gf_isom_rtp_packet_begin +; gf_isom_rtp_packet_set_flags +; gf_isom_rtp_packet_set_offset +; gf_isom_rtp_set_timescale +; gf_isom_rtp_set_time_offset +; gf_isom_rtp_set_time_sequence_offset +; gf_isom_sdp_add_track_line +; gf_isom_sdp_clean_track +; gf_isom_sdp_add_line +; gf_isom_sdp_clean +; gf_isom_3gp_config_new +; gf_isom_3gp_config_update +; gf_isom_new_text_description +; gf_isom_new_text_sample +; gf_isom_text_reset +; gf_isom_text_add_text +; gf_isom_text_add_style +; gf_isom_text_add_highlight +; gf_isom_text_set_highlight_color +; gf_isom_text_add_karaoke +; gf_isom_text_set_karaoke_segment +; gf_isom_text_set_scroll_delay +; gf_isom_text_add_hyperlink +; gf_isom_text_set_box +; gf_isom_text_add_blink +; gf_isom_text_set_wrap +; gf_isom_text_to_sample +; gf_isom_remove_ismacryp_protection +; gf_isom_set_ismacryp_protection +; gf_isom_change_ismacryp_protection +; gf_isom_dump_ismacryp_protection +; gf_isom_dump_ismacryp_sample +; gf_isom_avc_config_new +; gf_isom_avc_config_update +; gf_isom_3gp_config_new +; gf_isom_3gp_config_update +; gf_isom_set_media_timescale +; gf_isom_estimate_size +; gf_isom_set_meta_type +; gf_isom_add_meta_item +; gf_isom_remove_meta_item +; gf_isom_set_meta_primary_item +; gf_isom_set_meta_xml +; gf_isom_remove_meta_xml +; gf_isom_extract_meta_xml +; gf_isom_extract_meta_item +; gf_isom_apple_set_tag + +;end GPAC_READ_ONLY + +;isomedia_dev.h exports + gf_isom_parse_texte_sample + +;plugin_network.h exports + gf_term_on_message + gf_term_on_connect + gf_term_on_disconnect + gf_term_on_command + gf_term_on_sl_packet + gf_term_get_service_url + gf_term_get_service_interface + gf_term_register_mime_type + gf_term_check_extension + gf_term_download_new + gf_term_download_del + gf_term_download_update_stats + gf_term_add_media + gf_term_scene_update + gf_term_process_step + +;ietf.h exports + gf_rtsp_nc_to_string + gf_rtsp_range_parse + gf_rtsp_range_new + gf_rtsp_range_del + gf_rtsp_transport_clone + gf_rtsp_transport_del + gf_rtsp_command_new + gf_rtsp_command_del + gf_rtsp_command_reset + gf_rtsp_response_new + gf_rtsp_response_del + gf_rtsp_response_reset + gf_rtsp_session_new + gf_rtsp_session_del + gf_rtsp_set_mobile_ip + gf_rtsp_set_buffer_size + gf_rtsp_session_reset + gf_rtsp_is_my_session + gf_rtsp_get_server_name + gf_rtsp_get_service_name + gf_rtsp_get_response + gf_rtsp_get_session_state + gf_rtsp_get_last_request + gf_rtsp_reset_aggregation + gf_rtsp_send_command + gf_rtsp_set_interleave_callback + gf_rtsp_session_read + gf_rtsp_register_interleave + gf_rtsp_unregister_interleave + gf_rtsp_session_new_server + gf_rtsp_get_command + gf_rtsp_load_service_name + gf_rtsp_send_response + gf_rtsp_get_session_ip + gf_rtsp_get_next_interleave_id + gf_rtsp_get_remote_address + gf_rtsp_get_session_port + gf_rtp_new + gf_rtp_del + gf_rtp_setup_transport + gf_rtp_set_ports + gf_rtp_setup_payload + gf_rtp_enable_nat_keepalive + gf_rtp_initialize + gf_rtp_set_info_rtp + gf_rtp_get_current_time + gf_rtp_reset_buffers + gf_rtp_read_rtp + gf_rtp_read_rtcp + gf_rtp_decode_rtp + gf_rtp_decode_rtcp + gf_rtp_send_rtcp_report + gf_rtp_send_bye + gf_rtp_send_packet + gf_rtp_set_info_rtcp + gf_rtp_is_unicast + gf_rtp_is_interleaved + gf_rtp_get_clockrate + gf_rtp_is_active + gf_rtp_get_low_interleave_id + gf_rtp_get_hight_interleave_id + gf_rtp_get_transport + gf_rtp_get_local_ssrc + gf_rtp_get_loss + gf_rtp_get_tcp_bytes_sent + gf_rtp_get_ports + gf_sdp_info_new + gf_sdp_info_del + gf_sdp_info_reset + gf_sdp_info_parse + gf_sdp_info_check + gf_sdp_info_write + gf_sdp_media_new + gf_sdp_media_del + gf_sdp_conn_new + gf_sdp_conn_del + gf_sdp_fmtp_new + gf_sdp_fmtp_del + gf_rtp_builder_new + gf_rtp_builder_del + gf_rtp_builder_init + gf_rtp_builder_process + gf_rtp_builder_format_sdp + gf_rtp_depacketizer_new + gf_rtp_depacketizer_del + gf_rtp_depacketizer_reset + gf_rtp_depacketizer_process + gf_rtp_depacketizer_get_slconfig + +;avilib exports +; AVI_write_frame +; AVI_close +; AVI_open_output_file +; AVI_set_video + +;media.h exports +; gf_hinter_track_new +; gf_hinter_track_del +; gf_hinter_track_process +; gf_hinter_track_finalize +; gf_hinter_finalize +; gf_hinter_track_get_bandwidth +; gf_hinter_track_get_flags +; gf_hinter_track_get_payload_name +; gf_hinter_can_embbed_data +; gf_media_make_isma +; gf_media_make_3gpp + gf_media_map_esd + gf_media_get_file_hash +; gf_media_import +; gf_media_export +; gf_media_fragment_file +; gf_media_import_chapters + +;mcrypt exports + gf_crypt_open + gf_crypt_close + gf_crypt_init + gf_crypt_decrypt + gf_crypt_encrypt + gf_crypt_set_state + +;parsers_av.h exports + gf_m4v_parser_new + gf_m4v_parser_del + gf_m4v_parse_config + gf_m4v_parse_frame + gf_m4v_get_object_start + gf_m4v_is_valid_object_type + gf_m4v_get_profile_name + gf_m4v_get_config + gf_m4v_rewrite_pl + gf_mp3_num_channels + gf_mp3_sampling_rate + gf_mp3_window_size + gf_mp3_object_type_indication + gf_mp3_layer + gf_mp3_version + gf_mp3_version_name + gf_mp3_frame_size + gf_mp3_get_next_header + gf_mp3_get_next_header_mem + gf_m4a_get_config + gf_m4a_object_type_name + gf_m4a_get_profile_name + gf_m4a_write_config + gf_vorbis_parse_header + gf_vorbis_check_frame + gf_img_parse + gf_img_jpeg_dec + gf_img_png_dec + gf_img_png_enc + gf_ac3_parser_bs + +;ismacryp.h exports +; gf_ismacryp_crypt_file +; gf_ismacryp_decrypt_file +; gf_ismacryp_encrypt_track +; gf_ismacryp_decrypt_track + gf_ismacryp_gpac_get_info + gf_ismacryp_mpeg4ip_get_info + +;scene_manager.h exports + gf_sm_new + gf_sm_del + gf_sm_stream_new + gf_sm_stream_del + gf_sm_stream_au_new + gf_sm_make_random_access + gf_sm_load_init + gf_sm_load_done + gf_sm_load_run + gf_sm_load_string + gf_sm_import_bifs_subtitle +; gf_sm_dump +; gf_sm_encode_to_file +; gf_sm_dumper_new +; gf_sm_dumper_del +; gf_sm_dump_command_list +; gf_sm_dump_graph +; gf_sm_stats_new +; gf_sm_stats_del +; gf_sm_stats_reset +; gf_sm_stats_get +; gf_sm_stats_for_graph +; gf_sm_stats_for_scene +; gf_sm_stats_for_command + +;bifsengine exports +; gf_beng_init +; gf_beng_get_stream_config +; gf_beng_encode_context +; gf_beng_encode_from_file +; gf_beng_encode_from_string +; gf_beng_save_context +; gf_beng_aggregate_context +; gf_beng_terminate + +;bifs.h exports + gf_bifs_decoder_new + gf_bifs_decoder_del + gf_bifs_decoder_ignore_size_info + gf_bifs_decoder_configure_stream + gf_bifs_decoder_remove_stream + gf_bifs_decode_au + gf_bifs_decode_command_list +; gf_bifs_encoder_new +; gf_bifs_encoder_del +; gf_bifs_encoder_new_stream +; gf_bifs_encode_au +; gf_bifs_encoder_get_config +; gf_bifs_encoder_get_version +; gf_bifs_encoder_get_rap +; gf_bifs_encoder_set_trace +; gf_bifs_get_aq_info + + +;scenegraph.h and scenegraph_vrml.h exports + gf_node_list_add_child + gf_node_list_insert_child + gf_node_list_del_child + gf_node_list_find_child + gf_node_list_get_child + gf_node_list_get_count + gf_node_get_tag + gf_node_set_id + gf_node_get_name + gf_node_get_id + gf_node_get_private + gf_node_set_private + gf_node_set_callback_function + gf_node_register + gf_node_unregister + gf_node_replace + gf_node_unregister_children + gf_node_insert_child + gf_node_remove_child + gf_node_replace_child + gf_node_event_out + gf_node_event_out_str + gf_node_traverse + gf_node_traverse_children + gf_node_get_parent_count + gf_node_get_parent + gf_node_get_class_name + gf_node_dirty_set + gf_node_dirty_clear + gf_node_dirty_get + gf_node_dirty_reset + gf_node_get_field + gf_node_get_field_by_name + gf_node_get_graph + gf_node_init + gf_node_get_scene_time + gf_node_list_get_child + gf_node_list_get_count + gf_node_list_add_child + gf_node_list_del_child + gf_node_list_insert_child + gf_sg_new + gf_sg_new_subscene + gf_sg_set_scene_time_callback + gf_sg_set_node_callback + gf_sg_set_proto_loader + gf_sg_del + gf_sg_set_private + gf_sg_get_private + gf_sg_set_scene_size_info + gf_sg_use_pixel_metrics + gf_sg_get_scene_size_info + gf_sg_reset + gf_sg_get_root_node + gf_sg_set_root_node + gf_sg_find_node + gf_sg_find_node_by_name + gf_node_changed + gf_node_new + gf_node_clone + gf_node_allow_cyclic_traverse + gf_sg_vrml_get_event_type_name + gf_sg_vrml_get_field_type_by_name + gf_sg_vrml_field_pointer_new + gf_sg_vrml_field_pointer_del + gf_sg_vrml_is_sf_field + gf_sg_vrml_get_sf_type + gf_sg_vrml_mf_insert + gf_sg_vrml_mf_append + gf_sg_vrml_mf_remove + gf_sg_vrml_mf_alloc + gf_sg_vrml_mf_get_item + gf_sg_vrml_mf_reset + gf_sg_vrml_field_copy + gf_sg_vrml_field_equal + gf_sg_route_new + gf_sg_route_del + gf_sg_route_del_by_id + gf_sg_route_find + gf_sg_route_find_by_name + gf_sg_route_set_id + gf_sg_route_get_id + gf_sg_route_set_name + gf_sg_route_get_name + gf_sg_activate_routes + gf_sg_proto_new + gf_sg_proto_del + gf_sg_proto_set_in_graph + gf_sg_proto_get_graph + gf_sg_proto_set_private + gf_sg_proto_get_private + gf_sg_proto_get_extern_url + gf_sg_proto_add_node_code + gf_sg_proto_get_field_count + gf_sg_proto_field_find_by_name + gf_sg_proto_field_find + gf_sg_proto_field_new + gf_sg_proto_field_set_ised + gf_sg_proto_field_set_private + gf_sg_proto_field_get_private + gf_sg_proto_field_get_field + gf_sg_proto_create_instance + gf_sg_proto_load_code + gf_sg_find_proto + gf_sg_delete_all_protos + gf_node_get_proto + gf_sg_proto_get_id + gf_sg_proto_get_class_name + gf_sg_proto_field_is_sftime_offset + gf_sg_proto_instance_set_ised + gf_sg_script_field_new + gf_sg_script_field_get_info + gf_sg_script_load + gf_sg_script_event_in + gf_sg_has_scripting + gf_bifs_proto_field_set_aq_info + gf_node_get_num_fields_in_mode + gf_sg_command_new + gf_sg_command_del + gf_sg_command_apply + gf_sg_command_apply_list + gf_sg_command_field_new + gf_sg_command_clone + gf_sg_get_next_available_node_id + gf_sg_get_max_node_id + gf_sg_get_next_available_route_id + gf_sg_set_max_defined_route_id + gf_sg_get_next_available_proto_id + gf_sg_sfrotation_interpolate + gf_node_get_field_count + gf_node_mpeg4_type_by_class_name + gf_node_x3d_type_by_class_name + gf_sg_mfurl_del + gf_sg_sfcolor_to_rgba + +;terminal.h exports + gf_term_new + gf_term_del + gf_term_connect + gf_term_disconnect + gf_term_navigate_to + gf_term_play_from_time + gf_term_set_option + gf_term_get_option + gf_term_get_framerate + gf_term_get_time_in_ms + gf_term_get_viewpoint + gf_term_set_viewpoint + gf_term_get_root_object + gf_term_get_object_count + gf_term_get_object + gf_term_object_subscene_type + gf_term_select_object + gf_term_get_object_info + gf_term_get_download_info + gf_term_get_channel_net_info + gf_term_get_world_info +; gf_term_dump_scene + gf_term_set_size + gf_term_user_event + gf_term_mouse_input + gf_term_keyboard_input + gf_term_string_input + gf_term_connect_from_time + gf_term_add_object + gf_term_is_supported_url + gf_term_attach_service + gf_term_set_simulation_frame_rate + gf_term_get_simulation_frame_rate + gf_term_get_service_interface + gf_term_get_text_selection + gf_term_paste_text + gf_term_get_service_info + gf_term_get_url + +;renderer.h exports + gf_sc_new + gf_sc_del + gf_sc_set_fps + gf_sc_set_scene + gf_sc_draw_frame + gf_sc_on_node_init + gf_sc_invalidate + gf_sc_get_clock + gf_sc_lock + gf_sc_user_event + gf_sc_map_point + gf_sc_set_option + gf_sc_get_option + gf_sc_get_fps + gf_sc_get_screen_buffer + gf_sc_release_screen_buffer + gf_sc_simulation_tick + gf_sc_reset_graphics + gf_sc_pick_node + gf_sc_get_viewpoint + gf_sc_set_viewpoint + gf_sc_set_size + gf_sc_register_extra_graph + gf_sc_get_compositor + gf_sc_svg_convert_length_to_display + +;render_dev.h exports + gf_sc_register_time_node + gf_sc_unregister_time_node + gf_sc_texture_setup + gf_sc_texture_destroy + gf_sc_texture_get_handler + gf_sc_texture_check_url_change + gf_sc_texture_play + gf_sc_texture_play_from_to + gf_sc_texture_stop + gf_sc_texture_restart + gf_sc_texture_update_frame + gf_sc_texture_release_stream + gf_sc_audio_register + gf_sc_audio_unregister + gf_sc_audio_setup + gf_sc_audio_open + gf_sc_audio_stop + gf_sc_audio_restart + +;terminal_dev exports + gf_inline_new + gf_inline_del + gf_inline_get_time + gf_inline_attach_to_compositor + gf_inline_find_odm + gf_inline_set_duration + gf_inline_setup_object + gf_inline_default_scene_viewpoint + gf_inline_register_extra_graph + gf_inline_force_scene_size + gf_inline_get_proto_lib + gf_inline_process_anchor + gf_inline_disconnect + gf_odm_new + gf_odm_setup_object + gf_odm_disconnect + gf_odm_setup_es + gf_odm_remove_es + gf_term_node_callback + gf_term_message + gf_mo_get_loop + gf_mo_get_duration + gf_mo_set_speed + gf_mo_set_flag + gf_mo_get_flags + gf_mo_has_audio + gf_mo_get_last_frame_time + gf_clock_time + + +;laser.h exports + gf_laser_decoder_new + gf_laser_decoder_del + gf_laser_decoder_set_clock + gf_laser_decoder_configure_stream + gf_laser_decoder_remove_stream + gf_laser_decode_au + gf_laser_decode_command_list +; gf_laser_encoder_new +; gf_laser_encoder_del +; gf_laser_encoder_new_stream +; gf_laser_encode_au +; gf_laser_encoder_get_config +; gf_laser_encoder_get_rap +; gf_laser_set_trace + +;SVG exports + gf_svg_create_attribute_value + gf_svg_delete_attribute_value + gf_svg_attributes_equal + gf_svg_attributes_copy + gf_svg_attributes_add + gf_svg_attributes_interpolate + gf_svg_attributes_muladd + gf_svg_parse_attribute + gf_svg_parse_style + gf_svg_properties_init_pointers + gf_svg_properties_reset_pointers + gf_svg_apply_animations + gf_svg_path_build + + gf_svg_flatten_attributes + gf_svg_apply_inheritance + + gf_smil_timing_init_runtime_info + gf_smil_timing_get_normalized_simple_time + gf_smil_timing_is_active + gf_smil_timing_insert_clock + gf_smil_set_media_duration + gf_smil_notify_timed_elements + + gf_node_get_attribute_by_tag + gf_node_store_embedded_data + gf_node_register_iri + gf_node_unregister_iri + + gf_dom_listener_build + gf_dom_listener_add + gf_dom_listener_del + gf_dom_listener_count + gf_dom_listener_get + gf_dom_event_fire + gf_dom_event_type_by_name + gf_dom_event_get_name diff --git a/src/mcrypt/cbc.c b/src/mcrypt/cbc.c new file mode 100644 index 0000000..abb30cb --- /dev/null +++ b/src/mcrypt/cbc.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) 1998,1999,2000,2001 Nikos Mavroyanopoulos + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +typedef struct cbc_buf { + u32 *previous_ciphertext; + u32 *previous_cipher; + int blocksize; +} CBC_BUFFER; + +/* CBC MODE */ + + +static GF_Err _init_mcrypt( CBC_BUFFER* buf,void *key, int lenofkey, void *IV, int size) +{ +/* For cbc */ + buf->previous_ciphertext = + buf->previous_cipher = NULL; + + buf->blocksize = size; + + buf->previous_ciphertext = malloc( size); + buf->previous_cipher = malloc( size); + + if (buf->previous_ciphertext==NULL || + buf->previous_cipher==NULL) goto freeall; + + if (IV!=NULL) { + memcpy(buf->previous_ciphertext, IV, size); + } else { + memset(buf->previous_ciphertext, 0, size); + } + + return GF_OK; + + freeall: + if (buf->previous_ciphertext) free(buf->previous_ciphertext); + if (buf->previous_cipher) free(buf->previous_cipher); + return GF_OUT_OF_MEM; +} + +static GF_Err _mcrypt_set_state( CBC_BUFFER* buf, void *IV, int size) +{ +/* For cbc */ + + memcpy(buf->previous_ciphertext, IV, size); + memcpy(buf->previous_cipher, IV, size); + + return GF_OK; +} + +static GF_Err _mcrypt_get_state( CBC_BUFFER* buf, void *IV, int *size) +{ + if (*size < buf->blocksize) { + *size = buf->blocksize; + return GF_BAD_PARAM; + } + *size = buf->blocksize; + + memcpy( IV, buf->previous_ciphertext, buf->blocksize); + + return GF_OK; +} + + +static void _end_mcrypt( CBC_BUFFER* buf) { + free(buf->previous_ciphertext); + free(buf->previous_cipher); +} + +static GF_Err _mcrypt( CBC_BUFFER* buf, void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ + u32 *fplain = plaintext; + u32 *plain; + int dblock, dlen, i, j; + void (*_mcrypt_block_encrypt) (void *, void *); + + _mcrypt_block_encrypt = func; + + dblock = blocksize / sizeof(u32); + dlen = len / blocksize; + for (j = 0; j < dlen ; j++) { + + plain = &fplain[j * dblock]; + + for (i = 0; i < dblock; i++) { + plain[i] ^= buf->previous_ciphertext[i]; + } + + _mcrypt_block_encrypt(akey, plain); + + /* Copy the ciphertext to prev_ciphertext */ + memcpy(buf->previous_ciphertext, plain, blocksize); + } + if (j==0 && len!=0) return GF_BAD_PARAM; + return GF_OK; +} + + + +static GF_Err _mdecrypt( CBC_BUFFER* buf, void *ciphertext, int len, int blocksize,void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ + u32 *cipher; + u32 *fcipher = ciphertext; + int i, j, dlen, dblock; + void (*_mcrypt_block_decrypt) (void *, void *); + + _mcrypt_block_decrypt = func2; + + + dblock = blocksize / sizeof(u32); + dlen = len / blocksize; + for (j = 0; j < dlen; j++) { + + cipher = &fcipher[j * dblock]; + memcpy(buf->previous_cipher, cipher, blocksize); + + _mcrypt_block_decrypt(akey, cipher); + for (i = 0; i < dblock; i++) { + cipher[i] ^= buf->previous_ciphertext[i]; + } + + /* Copy the ciphertext to prev_cipher */ + memcpy(buf->previous_ciphertext, buf->previous_cipher, blocksize); + + } + if (j==0 && len!=0) return GF_BAD_PARAM; + return GF_OK; +} + +void gf_crypt_register_cbc(GF_Crypt *td) +{ + td->mode_name = "CBC"; + td->_init_mcrypt = _init_mcrypt; + td->_end_mcrypt = _end_mcrypt; + td->_mcrypt = _mcrypt; + td->_mdecrypt = _mdecrypt; + td->_mcrypt_get_state = _mcrypt_get_state; + td->_mcrypt_set_state = _mcrypt_set_state; + + td->has_IV = 1; + td->is_block_mode = 1; + td->is_block_algo_mode = 1; + td->mode_size = sizeof(CBC_BUFFER); + td->mode_version = 20010801; +} + +#endif diff --git a/src/mcrypt/cfb.c b/src/mcrypt/cfb.c new file mode 100644 index 0000000..0853b72 --- /dev/null +++ b/src/mcrypt/cfb.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 1998,1999,2000,2001,2002 Nikos Mavroyanopoulos + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +typedef struct cfb_buf { + u8* s_register; + u8* enc_s_register; + int blocksize; +} CFB_BUFFER; + +/* CFB MODE */ + +static GF_Err _init_mcrypt( CFB_BUFFER* buf, void *key, int lenofkey, void *IV, int size) +{ + + buf->s_register = buf->enc_s_register = NULL; + + buf->blocksize = size; +/* For cfb */ + buf->s_register=malloc( size); + if (buf->s_register==NULL) goto freeall; + + buf->enc_s_register=malloc( size); + if (buf->enc_s_register==NULL) goto freeall; + + if (IV!=NULL) { + memcpy(buf->s_register, IV, size); + } else { + memset(buf->s_register, 0, size); + } +/* End cfb */ + return GF_OK; + + freeall: + if (buf->s_register) free(buf->s_register); + if (buf->enc_s_register) free(buf->enc_s_register); + return GF_OUT_OF_MEM; +} + + +static GF_Err _mcrypt_set_state( CFB_BUFFER* buf, void *IV, int size) +{ + memcpy(buf->enc_s_register, IV, size); + memcpy(buf->s_register, IV, size); + + return GF_OK; +} + +static GF_Err _mcrypt_get_state( CFB_BUFFER* buf, u8 *IV, int *size) +{ + if (*size < buf->blocksize) { + *size = buf->blocksize; + return GF_BAD_PARAM; + } + *size = buf->blocksize; + + memcpy( IV, buf->s_register, buf->blocksize); + + return GF_OK; +} + + +static void _end_mcrypt( CFB_BUFFER* buf) { + free(buf->s_register); + free(buf->enc_s_register); +} + +static GF_Err _mcrypt( CFB_BUFFER* buf, void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ /* plaintext is 1 u8 (8bit cfb) */ + char *plain = plaintext; + int i, j; + void (*_mcrypt_block_encrypt) (void *, void *); + + _mcrypt_block_encrypt = func; + + for (j = 0; j < len; j++) { + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + plain[j] ^= buf->enc_s_register[0]; + +/* Shift the register */ + for (i = 0; i < (blocksize - 1); i++) + buf->s_register[i] = buf->s_register[i + 1]; + + buf->s_register[blocksize - 1] = plain[j]; + } + + return GF_OK; + +} + + +static GF_Err _mdecrypt( CFB_BUFFER* buf, void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ /* plaintext is 1 u8 (8bit ofb) */ + char *plain = plaintext; + int i, j; + void (*_mcrypt_block_encrypt) (void *, void *); + + _mcrypt_block_encrypt = func; + + for (j = 0; j < len; j++) { + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + +/* Shift the register */ + for (i = 0; i < (blocksize - 1); i++) + buf->s_register[i] = buf->s_register[i + 1]; + + buf->s_register[blocksize - 1] = plain[j]; + + plain[j] ^= buf->enc_s_register[0]; + + } + + return GF_OK; +} + +void gf_crypt_register_cfb(GF_Crypt *td) +{ + td->mode_name = "CFB"; + td->_init_mcrypt = _init_mcrypt; + td->_end_mcrypt = _end_mcrypt; + td->_mcrypt = _mcrypt; + td->_mdecrypt = _mdecrypt; + td->_mcrypt_get_state = _mcrypt_get_state; + td->_mcrypt_set_state = _mcrypt_set_state; + + td->has_IV = 1; + td->is_block_mode = 0; + td->is_block_algo_mode = 1; + td->mode_size = sizeof(CFB_BUFFER); + td->mode_version = 20020310; +} + +#endif + diff --git a/src/mcrypt/ctr.c b/src/mcrypt/ctr.c new file mode 100644 index 0000000..75de12c --- /dev/null +++ b/src/mcrypt/ctr.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2002 Nikos Mavroyanopoulos + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +typedef struct ctr_buf { + u8* enc_counter; + u8* c_counter; + int c_counter_pos; + int blocksize; +} CTR_BUFFER; + +/* CTR MODE */ + +/* This function will add one to the given number (as a u8 string). + * has been reached. + */ +static void increase_counter( u8 *x, int x_size) { +register int i, y=0; + + for (i=x_size-1;i>=0;i--) { + y = 0; + if ( x[i] == 0xff) { + x[i] = 0; + y = 1; + } else x[i]++; + + if (y==0) break; + } + + return; +} + + +/* size holds the size of the IV (counter in this mode). + * This is the block size. + */ +GF_Err _init_mcrypt( void *buf, void *key, int lenofkey, void *IV, int size) +{ + ((CTR_BUFFER* )buf)->c_counter = ((CTR_BUFFER* )buf)->enc_counter = NULL; + +/* For ctr */ + ((CTR_BUFFER* )buf)->c_counter_pos = 0; + ((CTR_BUFFER* )buf)->blocksize = size; + + ((CTR_BUFFER* )buf)->c_counter = (u8 *)malloc(size); + if (((CTR_BUFFER* )buf)->c_counter==NULL) goto freeall; + + ((CTR_BUFFER* )buf)->enc_counter = (u8 *)malloc(size); + if (((CTR_BUFFER* )buf)->enc_counter==NULL) goto freeall; + + if (IV!=NULL) { + memcpy(((CTR_BUFFER* )buf)->enc_counter, IV, size); + memcpy(((CTR_BUFFER* )buf)->c_counter, IV, size); + } + +/* End ctr */ + + return GF_OK; + freeall: + free(((CTR_BUFFER* )buf)->c_counter); + free(((CTR_BUFFER* )buf)->enc_counter); + return GF_OUT_OF_MEM; +} + +GF_Err _mcrypt_set_state(void *_buf, void *IV, int size) +{ + CTR_BUFFER* buf = (CTR_BUFFER* )_buf; + ((CTR_BUFFER* )buf)->c_counter_pos = ((u8*)IV)[0]; + memcpy(((CTR_BUFFER* )buf)->c_counter, &((u8*)IV)[1], size-1); + memcpy(((CTR_BUFFER* )buf)->enc_counter, &((u8*)IV)[1], size-1); + + return GF_OK; +} + +GF_Err _mcrypt_get_state(void *buf, void *IV, int *size) +{ + if (*size < ((CTR_BUFFER* )buf)->blocksize + 1) { + *size = ((CTR_BUFFER* )buf)->blocksize + 1; + return GF_BAD_PARAM; + } + *size = ((CTR_BUFFER* )buf)->blocksize + 1; + + ((u8 *)IV)[0] = ((CTR_BUFFER* )buf)->c_counter_pos; + memcpy( & ((u8 *) IV)[1], ((CTR_BUFFER* )buf)->c_counter, ((CTR_BUFFER* )buf)->blocksize); + + return GF_OK; +} + + +void _end_mcrypt(void *buf) { + free(((CTR_BUFFER* )buf)->c_counter); + free(((CTR_BUFFER* )buf)->enc_counter); +} + +static GFINLINE +void xor_stuff( CTR_BUFFER *buf, void* akey, void (*func)(void*,void*), u8* plain, int blocksize, int xor_size) +{ + void (*_mcrypt_block_encrypt) (void *, void *); + + _mcrypt_block_encrypt = func; + + if (xor_size == blocksize) { + if (((CTR_BUFFER* )buf)->c_counter_pos == 0) { + + memcpy( ((CTR_BUFFER* )buf)->enc_counter, ((CTR_BUFFER* )buf)->c_counter, blocksize); + _mcrypt_block_encrypt(akey, ((CTR_BUFFER* )buf)->enc_counter); + + memxor( plain, ((CTR_BUFFER* )buf)->enc_counter, blocksize); + + increase_counter( ((CTR_BUFFER* )buf)->c_counter, blocksize); + + + } else { + int size = blocksize - ((CTR_BUFFER* )buf)->c_counter_pos; + + memxor( plain, &((CTR_BUFFER* )buf)->enc_counter[((CTR_BUFFER* )buf)->c_counter_pos], + size); + + increase_counter( ((CTR_BUFFER* )buf)->c_counter, blocksize); + + memcpy( ((CTR_BUFFER* )buf)->enc_counter, ((CTR_BUFFER* )buf)->c_counter, blocksize); + _mcrypt_block_encrypt(akey, ((CTR_BUFFER* )buf)->enc_counter); + + memxor( &plain[size], ((CTR_BUFFER* )buf)->enc_counter, + ((CTR_BUFFER* )buf)->c_counter_pos); + + /* ((CTR_BUFFER* )buf)->c_counter_pos remains the same */ + + } + } else { /* xor_size != blocksize */ + if (((CTR_BUFFER* )buf)->c_counter_pos == 0) { + memcpy( ((CTR_BUFFER* )buf)->enc_counter, ((CTR_BUFFER* )buf)->c_counter, blocksize); + _mcrypt_block_encrypt(akey, ((CTR_BUFFER* )buf)->enc_counter); + + memxor( plain, ((CTR_BUFFER* )buf)->enc_counter, xor_size); + ((CTR_BUFFER* )buf)->c_counter_pos = xor_size; + } else { + int size = blocksize - ((CTR_BUFFER* )buf)->c_counter_pos; + int min_size = size < xor_size ? size: xor_size; + + memxor( plain, &((CTR_BUFFER* )buf)->enc_counter[((CTR_BUFFER* )buf)->c_counter_pos], + min_size); + + ((CTR_BUFFER* )buf)->c_counter_pos += min_size; + + if (min_size >= xor_size) + return; + + increase_counter( ((CTR_BUFFER* )buf)->c_counter, blocksize); + + memcpy( ((CTR_BUFFER* )buf)->enc_counter, ((CTR_BUFFER* )buf)->c_counter, blocksize); + _mcrypt_block_encrypt(akey, ((CTR_BUFFER* )buf)->enc_counter); + + memxor( &plain[min_size], ((CTR_BUFFER* )buf)->enc_counter, + xor_size - min_size); + + ((CTR_BUFFER* )buf)->c_counter_pos = xor_size - min_size; + + } + + } + return; +} + +GF_Err _mcrypt(void * buf,void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ /* plaintext can be any size */ + u8 *plain; + int dlen, j=0; + int modlen; + + dlen = len / blocksize; + plain = (u8 *)plaintext; + for (j = 0; j < dlen; j++) { + + xor_stuff((CTR_BUFFER*) buf, akey, func, plain, blocksize, blocksize); + + plain += blocksize; + +/* Put the new register */ + + } + modlen = len % blocksize; + if (modlen > 0) { + /* This is only usefull if encrypting the + * final block. Otherwise you'll not be + * able to decrypt it. + */ + + xor_stuff( (CTR_BUFFER*)buf, akey, func, plain, blocksize, modlen); + + } + + return GF_OK; +} + + +GF_Err _mdecrypt(void * buf,void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ /* plaintext can be any size */ + return _mcrypt( buf, plaintext, len, blocksize, akey, func, func2); +} + +void gf_crypt_register_ctr(GF_Crypt *td) +{ + td->mode_name = "CTR"; + td->_init_mcrypt = _init_mcrypt; + td->_end_mcrypt = _end_mcrypt; + td->_mcrypt = _mcrypt; + td->_mdecrypt = _mdecrypt; + td->_mcrypt_get_state = _mcrypt_get_state; + td->_mcrypt_set_state = _mcrypt_set_state; + + td->has_IV = 1; + td->is_block_mode = 1; + td->is_block_algo_mode = 1; + td->mode_size = sizeof(CTR_BUFFER); + td->mode_version = 20020307; +} diff --git a/src/mcrypt/des.c b/src/mcrypt/des.c new file mode 100644 index 0000000..c53277c --- /dev/null +++ b/src/mcrypt/des.c @@ -0,0 +1,580 @@ + +/* Sofware DES functions + * written 12 Dec 1986 by Phil Karn, KA9Q; large sections adapted from + * the 1977 public-domain program by Jim Gillogly + * Modified for additional speed - 6 December 1988 Phil Karn + * Modified for parameterized key schedules - Jan 1991 Phil Karn + * Callers now allocate a key schedule as follows: + * kn = (char (*)[8])malloc(sizeof(char) * 8 * 16); + * or + * char kn[16][8]; + */ + +/* modified in order to use the libmcrypt API by Nikos Mavroyanopoulos + * All modifications are placed under the license of libmcrypt. + */ + +/* $Id: des.c,v 1.1.1.1 2005/07/13 14:36:35 jeanlf Exp $ */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +typedef struct des_key { + char kn[16][8]; + u32 sp[8][64]; + char iperm[16][16][8]; + char fperm[16][16][8]; +} DES_KEY; + + +static void permute_ip(), permute_fp(), perminit_ip(), spinit(), +perminit_fp(); +static u32 f(); + + +/* Tables defined in the Data Encryption Standard documents */ + +/* initial permutation IP */ +static char ip[] = { + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7 +}; + +/* final permutation IP^-1 */ +static char fp[] = { + 40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25 +}; + +/* expansion operation matrix + * This is for reference only; it is unused in the code + * as the f() function performs it implicitly for speed + */ +#ifdef notdef +static char ei[] = { + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1 +}; +#endif + +/* permuted choice table (key) */ +static char pc1[] = { + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4 +}; + +/* number left rotations of pc1 */ +static char totrot[] = { + 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 +}; + +/* permuted choice key (table) */ +static char pc2[] = { + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 +}; + +/* The (in)famous S-boxes */ +static char si[8][64] = { + /* S1 */ + {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}, + + /* S2 */ + {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}, + + /* S3 */ + {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}, + + /* S4 */ + {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}, + + /* S5 */ + {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}, + + /* S6 */ + {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}, + + /* S7 */ + {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}, + + /* S8 */ + {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}, + +}; + +/* 32-bit permutation function P used on the output of the S-boxes */ +static char p32i[] = { + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25 +}; + +/* End of DES-defined tables */ + +/* Lookup tables initialized once only at startup by desinit() */ + +/* bit 0 is left-most in byte */ +static int bytebit[] = { + 0200, 0100, 040, 020, 010, 04, 02, 01 +}; + +static int nibblebit[] = { + 010, 04, 02, 01 +}; + +/* Allocate space and initialize DES lookup arrays + * mode == 0: standard Data Encryption Algorithm + */ +static int _mcrypt_desinit(DES_KEY * key) +{ + + spinit(key); + perminit_ip(key); + perminit_fp(key); + + return 0; +} + + +/* Set key (initialize key schedule array) */ +static GF_Err _mcrypt_set_key(DES_KEY * dkey, char *user_key, int len) +{ + char pc1m[56]; /* place to modify pc1 into */ + char pcr[56]; /* place to rotate pc1 into */ + register int i, j, l; + int m; + + Bzero(dkey, sizeof(DES_KEY)); + _mcrypt_desinit(dkey); + + /* Clear key schedule */ + + + for (j = 0; j < 56; j++) { /* convert pc1 to bits of key */ + l = pc1[j] - 1; /* integer bit location */ + m = l & 07; /* find bit */ + pc1m[j] = (user_key[l >> 3] & /* find which key byte l is in */ + bytebit[m]) /* and which bit of that byte */ + ? 1 : 0; /* and store 1-bit result */ + + } + for (i = 0; i < 16; i++) { /* key chunk for each iteration */ + for (j = 0; j < 56; j++) /* rotate pc1 the right amount */ + pcr[j] = + pc1m[(l = j + totrot[i]) < + (j < 28 ? 28 : 56) ? l : l - 28]; + /* rotate left and right halves independently */ + for (j = 0; j < 48; j++) { /* select bits individually */ + /* check bit that goes to kn[j] */ + if (pcr[pc2[j] - 1]) { + /* mask it in if it's there */ + l = j % 6; + dkey->kn[i][j / 6] |= bytebit[l] >> 2; + } + } + } + return 0; +} + +/* In-place encryption of 64-bit block */ +static void _mcrypt_encrypt(DES_KEY * key, char *block) +{ + register u32 left, right; + register char *knp; + u32 work[2]; /* Working data storage */ + + permute_ip(block, key, (char *) work); /* Initial Permutation */ +#ifndef WORDS_BIGENDIAN + left = byteswap32(work[0]); + right = byteswap32(work[1]); +#else + left = work[0]; + right = work[1]; +#endif + + /* Do the 16 rounds. + * The rounds are numbered from 0 to 15. On even rounds + * the right half is fed to f() and the result exclusive-ORs + * the left half; on odd rounds the reverse is done. + */ + knp = &key->kn[0][0]; + left ^= f(key, right, knp); + knp += 8; + right ^= f(key, left, knp); + knp += 8; + left ^= f(key, right, knp); + knp += 8; + right ^= f(key, left, knp); + knp += 8; + left ^= f(key, right, knp); + knp += 8; + right ^= f(key, left, knp); + knp += 8; + left ^= f(key, right, knp); + knp += 8; + right ^= f(key, left, knp); + knp += 8; + left ^= f(key, right, knp); + knp += 8; + right ^= f(key, left, knp); + knp += 8; + left ^= f(key, right, knp); + knp += 8; + right ^= f(key, left, knp); + knp += 8; + left ^= f(key, right, knp); + knp += 8; + right ^= f(key, left, knp); + knp += 8; + left ^= f(key, right, knp); + knp += 8; + right ^= f(key, left, knp); + + /* Left/right half swap, plus byte swap if little-endian */ +#ifndef WORDS_BIGENDIAN + work[1] = byteswap32(left); + work[0] = byteswap32(right); +#else + work[0] = right; + work[1] = left; +#endif + permute_fp((char *) work, key, block); /* Inverse initial permutation */ +} + +/* In-place decryption of 64-bit block. This function is the mirror + * image of encryption; exactly the same steps are taken, but in + * reverse order + */ +static void _mcrypt_decrypt(DES_KEY * key, char *block) +{ + register u32 left, right; + register char *knp; + u32 work[2]; /* Working data storage */ + + permute_ip(block, key, (char *) work); /* Initial permutation */ + + /* Left/right half swap, plus byte swap if little-endian */ +#ifndef WORDS_BIGENDIAN + right = byteswap32(work[0]); + left = byteswap32(work[1]); +#else + right = work[0]; + left = work[1]; +#endif + /* Do the 16 rounds in reverse order. + * The rounds are numbered from 15 to 0. On even rounds + * the right half is fed to f() and the result exclusive-ORs + * the left half; on odd rounds the reverse is done. + */ + knp = &key->kn[15][0]; + right ^= f(key, left, knp); + knp -= 8; + left ^= f(key, right, knp); + knp -= 8; + right ^= f(key, left, knp); + knp -= 8; + left ^= f(key, right, knp); + knp -= 8; + right ^= f(key, left, knp); + knp -= 8; + left ^= f(key, right, knp); + knp -= 8; + right ^= f(key, left, knp); + knp -= 8; + left ^= f(key, right, knp); + knp -= 8; + right ^= f(key, left, knp); + knp -= 8; + left ^= f(key, right, knp); + knp -= 8; + right ^= f(key, left, knp); + knp -= 8; + left ^= f(key, right, knp); + knp -= 8; + right ^= f(key, left, knp); + knp -= 8; + left ^= f(key, right, knp); + knp -= 8; + right ^= f(key, left, knp); + knp -= 8; + left ^= f(key, right, knp); + +#ifndef WORDS_BIGENDIAN + work[0] = byteswap32(left); + work[1] = byteswap32(right); +#else + work[0] = left; + work[1] = right; +#endif + permute_fp((char *) work, key, block); /* Inverse initial permutation */ +} + +/* Permute inblock with perm */ +static void permute_ip(char *inblock, DES_KEY * key, char *outblock) +{ + register char *ib, *ob; /* ptr to input or output block */ + register char *p, *q; + register int j; + + /* Clear output block */ + Bzero(outblock, 8); + + ib = inblock; + for (j = 0; j < 16; j += 2, ib++) { /* for each input nibble */ + ob = outblock; + p = key->iperm[j][(*ib >> 4) & 0xf]; + q = key->iperm[j + 1][*ib & 0xf]; + /* and each output byte, OR the masks together */ + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + } +} + +/* Permute inblock with perm */ +static void permute_fp(char *inblock, DES_KEY * key, char *outblock) +{ + register char *ib, *ob; /* ptr to input or output block */ + register char *p, *q; + register int j; + + /* Clear output block */ + Bzero(outblock, 8); + + ib = inblock; + for (j = 0; j < 16; j += 2, ib++) { /* for each input nibble */ + ob = outblock; + p = key->fperm[j][(*ib >> 4) & 0xf]; + q = key->fperm[j + 1][*ib & 0xf]; + /* and each output byte, OR the masks together */ + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + } +} + +/* The nonlinear function f(r,k), the heart of DES */ +static u32 f(DES_KEY * key, register u32 r, register char *subkey) +{ + register u32 *spp; + register u32 rval, rt; + register int er; + +#ifdef TRACE + printf("f(%08lx, %02x %02x %02x %02x %02x %02x %02x %02x) = ", + r, + subkey[0], subkey[1], subkey[2], + subkey[3], subkey[4], subkey[5], subkey[6], subkey[7]); +#endif + /* Run E(R) ^ K through the combined S & P boxes. + * This code takes advantage of a convenient regularity in + * E, namely that each group of 6 bits in E(R) feeding + * a single S-box is a contiguous segment of R. + */ + subkey += 7; + + /* Compute E(R) for each block of 6 bits, and run thru boxes */ + er = ((int) r << 1) | ((r & 0x80000000) ? 1 : 0); + spp = &key->sp[7][0]; + rval = spp[(er ^ *subkey--) & 0x3f]; + spp -= 64; + rt = (u32) r >> 3; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rt |= (r & 1) << 5; + rval |= spp[((int) rt ^ *subkey) & 0x3f]; +#ifdef TRACE + printf(" %08lx\n", rval); +#endif + return rval; +} + +/* initialize a perm array */ +static void perminit_ip(DES_KEY * key) +{ + register int l, j, k; + int i, m; + + /* Clear the permutation array */ + Bzero(key->iperm, 16 * 16 * 8); + + for (i = 0; i < 16; i++) /* each input nibble position */ + for (j = 0; j < 16; j++) /* each possible input nibble */ + for (k = 0; k < 64; k++) { /* each output bit position */ + l = ip[k] - 1; /* where does this bit come from */ + if ((l >> 2) != i) /* does it come from input posn? */ + continue; /* if not, bit k is 0 */ + if (!(j & nibblebit[l & 3])) + continue; /* any such bit in input? */ + m = k & 07; /* which bit is this in the byte */ + key->iperm[i][j][k >> 3] |= bytebit[m]; + } +} + +static void perminit_fp(DES_KEY * key) +{ + register int l, j, k; + int i, m; + + /* Clear the permutation array */ + Bzero(key->fperm, 16 * 16 * 8); + + for (i = 0; i < 16; i++) /* each input nibble position */ + for (j = 0; j < 16; j++) /* each possible input nibble */ + for (k = 0; k < 64; k++) { /* each output bit position */ + l = fp[k] - 1; /* where does this bit come from */ + if ((l >> 2) != i) /* does it come from input posn? */ + continue; /* if not, bit k is 0 */ + if (!(j & nibblebit[l & 3])) + continue; /* any such bit in input? */ + m = k & 07; /* which bit is this in the byte */ + key->fperm[i][j][k >> 3] |= bytebit[m]; + } +} + +/* Initialize the lookup table for the combined S and P boxes */ +static void spinit(DES_KEY * key) +{ + char pbox[32]; + int p, i, s, j, rowcol; + u32 val; + + /* Compute pbox, the inverse of p32i. + * This is easier to work with + */ + for (p = 0; p < 32; p++) { + for (i = 0; i < 32; i++) { + if (p32i[i] - 1 == p) { + pbox[p] = i; + break; + } + } + } + for (s = 0; s < 8; s++) { /* For each S-box */ + for (i = 0; i < 64; i++) { /* For each possible input */ + val = 0; + /* The row number is formed from the first and last + * bits; the column number is from the middle 4 + */ + rowcol = + (i & 32) | ((i & 1) ? 16 : 0) | ((i >> 1) & + 0xf); + for (j = 0; j < 4; j++) { /* For each output bit */ + if (si[s][rowcol] & (8 >> j)) { + val |= + 1L << (31 - pbox[4 * s + j]); + } + } + key->sp[s][i] = val; + +#ifdef DEBUG + printf("sp[%d][%2d] = %08lx\n", s, i, + key->sp[s][i]); +#endif + } + } +} + +void gf_crypt_register_des(GF_Crypt *td) +{ + td->a_encrypt = _mcrypt_encrypt; + td->a_decrypt = _mcrypt_decrypt; + td->a_set_key = _mcrypt_set_key; + td->algo_name = "DES"; + td->algo_version = 20010801; + td->num_key_sizes = 1; + td->key_sizes[0] = 8; + td->key_size = 8; + td->is_block_algo = 1; + td->algo_block_size = 8; + td->algo_size = sizeof(DES_KEY); +} + +#endif diff --git a/src/mcrypt/ecb.c b/src/mcrypt/ecb.c new file mode 100644 index 0000000..6700837 --- /dev/null +++ b/src/mcrypt/ecb.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 1998,1999,2000,2001 Nikos Mavroyanopoulos + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +static GF_Err _init_mcrypt( void* ign, void *key, int lenofkey, void *IV, int size) +{ + return 0; + +} + +static GF_Err _mcrypt_set_state( void* buf, void *IV, int size) { return GF_BAD_PARAM; } +static GF_Err _mcrypt_get_state( void* buf, void *IV, int *size) { return GF_BAD_PARAM; } + +static void _end_mcrypt (void* buf) {} + +static GF_Err _mcrypt( void* ign, void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ + int j; + char *plain = plaintext; + void (*_mcrypt_block_encrypt) (void *, void *); + + _mcrypt_block_encrypt = func; + + for (j = 0; j < len / blocksize; j++) { + _mcrypt_block_encrypt(akey, &plain[j * blocksize]); + } + if (j==0 && len!=0) return GF_BAD_PARAM; /* no blocks were encrypted */ + return GF_OK; +} + + + +static GF_Err _mdecrypt( void* ign, void *ciphertext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ + int j; + char *cipher = ciphertext; + void (*_mcrypt_block_decrypt) (void *, void *); + + _mcrypt_block_decrypt = func2; + + for (j = 0; j < len / blocksize; j++) { + _mcrypt_block_decrypt(akey, &cipher[j * blocksize]); + } + if (j==0 && len!=0) return GF_BAD_PARAM; /* no blocks were encrypted */ + return GF_OK; +} + +void gf_crypt_register_ecb(GF_Crypt *td) +{ + td->mode_name = "ECB"; + td->_init_mcrypt = _init_mcrypt; + td->_end_mcrypt = _end_mcrypt; + td->_mcrypt = _mcrypt; + td->_mdecrypt = _mdecrypt; + td->_mcrypt_get_state = _mcrypt_get_state; + td->_mcrypt_set_state = _mcrypt_set_state; + + td->has_IV = 0; + td->is_block_mode = 1; + td->is_block_algo_mode = 1; + td->mode_size = 0; + td->mode_version = 20010801; +} + +#endif + diff --git a/src/mcrypt/g_crypt.c b/src/mcrypt/g_crypt.c new file mode 100644 index 0000000..64ea5a6 --- /dev/null +++ b/src/mcrypt/g_crypt.c @@ -0,0 +1,332 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / crypto lib sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* The GPAC crypto lib is a simplified version of libmcrypt. */ + +#include + + +static Bool gf_crypt_assign_algo(GF_Crypt *td, const char *algorithm) +{ + if (!stricmp(algorithm, "AES-128") || !stricmp(algorithm, "Rijndael-128")) { gf_crypt_register_rijndael_128(td); return 1; } +#ifndef GPAC_CRYPT_ISMA_ONLY + else if (!stricmp(algorithm, "AES-192") || !stricmp(algorithm, "Rijndael-192")) { gf_crypt_register_rijndael_192(td); return 1; } + else if (!stricmp(algorithm, "AES-256") || !stricmp(algorithm, "Rijndael-256")) { gf_crypt_register_rijndael_256(td); return 1; } + else if (!stricmp(algorithm, "DES")) { gf_crypt_register_des(td); return 1; } + else if (!stricmp(algorithm, "3DES")) { gf_crypt_register_3des(td); return 1; } +#endif + return 0; +} +static Bool gf_crypt_assign_mode(GF_Crypt *td, const char *mode) +{ + if (!stricmp(mode, "CTR")) { gf_crypt_register_ctr(td); return 1; } +#ifndef GPAC_CRYPT_ISMA_ONLY + else if (!stricmp(mode, "CBC")) { gf_crypt_register_cbc(td); return 1; } + else if (!stricmp(mode, "CFB")) { gf_crypt_register_cfb(td); return 1; } + else if (!stricmp(mode, "ECB")) { gf_crypt_register_ecb(td); return 1; } + else if (!stricmp(mode, "nCFB")) { gf_crypt_register_ncfb(td); return 1; } + else if (!stricmp(mode, "nOFB")) { gf_crypt_register_nofb(td); return 1; } + else if (!stricmp(mode, "OFB")) { gf_crypt_register_ofb(td); return 1; } + else if (!stricmp(mode, "STREAM")) { gf_crypt_register_stream(td); return 1; } +#endif + return 0; +} + +static GF_Crypt *gf_crypt_open_intern(const char *algorithm, const char *mode, Bool is_check) +{ + GF_Crypt *td; + if ((!algorithm || !mode) && !is_check) return NULL; + + GF_SAFEALLOC(td, GF_Crypt); + if (td==NULL) return NULL; + + + if (algorithm && !gf_crypt_assign_algo(td, algorithm)) { + free(td); + return NULL; + } + if (mode && !gf_crypt_assign_mode(td, mode)) { + free(td); + return NULL; + } + if (is_check) return td; + + if (td->is_block_algo != td->is_block_algo_mode) { + free(td); + return NULL; + } + if (!td->_mcrypt || !td->_mdecrypt || !td->_mcrypt_set_state + || !td->a_decrypt || !td->a_encrypt || !td->a_set_key) + { + free(td); + return NULL; + } + return td; +} + +static void internal_end_mcrypt(GF_Crypt *td) +{ + if (!td || !td->keyword_given) return; + free(td->keyword_given); + td->keyword_given = NULL; + + if (td->akey) { + free(td->akey); + td->akey = NULL; + } + if (td->abuf) { + td->_end_mcrypt(td->abuf); + free(td->abuf); + td->abuf = NULL; + } +} + + +GF_EXPORT +GF_Crypt *gf_crypt_open(const char *algorithm, const char *mode) +{ + return gf_crypt_open_intern(algorithm, mode, 0); +} + +GF_EXPORT +void gf_crypt_close(GF_Crypt *td) +{ + internal_end_mcrypt(td); + free(td); +} + +GF_Err gf_crypt_set_key(GF_Crypt *td, void *key, u32 keysize, const void *IV) +{ + GF_Err (*__mcrypt_set_key_stream) (void *, const void *, int, const void *, int); + GF_Err (*__mcrypt_set_key_block) (void *, const void *, int); + + if (td->is_block_algo== 0) { + /* stream */ + __mcrypt_set_key_stream = (mcrypt_setkeystream) td->a_set_key; + if (__mcrypt_set_key_stream == NULL) return GF_BAD_PARAM; + return __mcrypt_set_key_stream(td->akey, key, keysize, IV, (IV!=NULL) ? gf_crypt_get_iv_size(td) : 0); + } else { + __mcrypt_set_key_block = (mcrypt_setkeyblock) td->a_set_key; + if (__mcrypt_set_key_block == NULL) return GF_BAD_PARAM; + return __mcrypt_set_key_block(td->akey, key, keysize); + } +} + +GF_EXPORT +GF_Err gf_crypt_set_state(GF_Crypt *td, const void *iv, int size) +{ + if (!td) return GF_BAD_PARAM; + return td->_mcrypt_set_state(td->abuf, (void *) iv, size); +} + +GF_Err gf_crypt_get_state(GF_Crypt *td, void *iv, int *size) +{ + if (!td) return GF_BAD_PARAM; + return td->_mcrypt_get_state(td->abuf, iv, size); +} + +u32 gf_crypt_get_block_size(GF_Crypt *td) { return td ? td->algo_block_size : 0; } + +u32 gf_crypt_get_iv_size(GF_Crypt *td) +{ + if (!td) return 0; + if (td->is_block_algo_mode) return td->algo_block_size; + return td->algo_IV_size; +} + +u32 gf_crypt_get_key_size(GF_Crypt *td) { return td ? td->key_size : 0; } + +u32 gf_crypt_get_supported_key_sizes(GF_Crypt *td, u32 *key_sizes) +{ + u32 i; + if (!td || !td->num_key_sizes) return 0; + for (i=0; inum_key_sizes; i++) key_sizes[i] = td->key_sizes[i]; + return td->num_key_sizes; +} + +Bool gf_crypt_is_block_algorithm(GF_Crypt *td) { return td ? td->is_block_algo : 0; } +const char *gf_crypt_get_algorithm_name(GF_Crypt *td) { return td ? td->algo_name : NULL; } +const char *gf_crypt_get_mode_name(GF_Crypt *td) { return td ? td->mode_name : NULL; } +Bool gf_crypt_is_block_mode(GF_Crypt *td) { return td ? td->is_block_mode : 0; } +Bool gf_crypt_mode_has_iv(GF_Crypt *td) { return td ? td->has_IV : 0; } +Bool gf_crypt_is_block_algorithm_mode(GF_Crypt *td) { return td ? td->is_block_algo_mode : 0; } +u32 gf_crypt_get_algorithm_version(GF_Crypt *td) { return td ? td->algo_version : 0; } +u32 gf_crypt_get_mode_version(GF_Crypt *td) { return td ? td->mode_version : 0; } + + + +GF_EXPORT +GF_Err gf_crypt_init(GF_Crypt *td, void *key, u32 lenofkey, const void *IV) +{ + GF_Err e; + u32 sizes[MAX_KEY_SIZES]; + u32 i, num_of_sizes, ok = 0; + u32 key_size = gf_crypt_get_key_size(td); + + if ((lenofkey > key_size) || (lenofkey==0)) return GF_BAD_PARAM; + num_of_sizes = gf_crypt_get_supported_key_sizes(td, sizes); + if (num_of_sizes) { + for (i=0; i < num_of_sizes; i++) { + if (lenofkey == sizes[i]) { + ok = 1; + break; + } + } + } else if (lenofkey <= gf_crypt_get_key_size(td)) { + ok = 1; + } + + if (ok == 0) { /* not supported key size */ + key_size = gf_crypt_get_key_size(td); + if (sizes != NULL) { + for (i = 0; i < num_of_sizes; i++) { + if (lenofkey <= sizes[i]) { + key_size = sizes[i]; + break; + } + } + } else { /* well every key size is supported! */ + key_size = lenofkey; + } + } else { + key_size = lenofkey; + } + + td->keyword_given = (char*)malloc(sizeof(char)*gf_crypt_get_key_size(td)); + if (td->keyword_given==NULL) return GF_OUT_OF_MEM; + + memmove(td->keyword_given, key, lenofkey); + + td->akey = (char*)malloc(sizeof(char)*td->algo_size); + if (td->akey==NULL) { + free(td->keyword_given); + return GF_OUT_OF_MEM; + } + if (td->mode_size > 0) { + td->abuf = (char*)malloc(sizeof(char)*td->mode_size); + if (td->abuf==NULL) { + free(td->keyword_given); + free(td->akey); + return GF_OUT_OF_MEM; + } + } + e = td->_init_mcrypt(td->abuf, (void *) key, key_size, (void *) IV, gf_crypt_get_block_size(td)); + if (e!=GF_OK) { + free(td->keyword_given); + free(td->akey); + free(td->abuf); + return e; + } + + e = gf_crypt_set_key(td, (void *) td->keyword_given, key_size, IV); + + if (e!=GF_OK) internal_end_mcrypt(td); + return e; +} + +void gf_crypt_deinit(GF_Crypt *td) +{ + internal_end_mcrypt(td); +} + +GF_EXPORT +GF_Err gf_crypt_encrypt(GF_Crypt *td, void *plaintext, int len) +{ + if (!td) return GF_BAD_PARAM; + return td->_mcrypt(td->abuf, plaintext, len, gf_crypt_get_block_size(td), td->akey, (mcryptfunc) td->a_encrypt, (mcryptfunc) td->a_decrypt); +} + +GF_EXPORT +GF_Err gf_crypt_decrypt(GF_Crypt *td, void *ciphertext, int len) +{ + if (!td) return GF_BAD_PARAM; + return td->_mdecrypt(td->abuf, ciphertext, len, gf_crypt_get_block_size(td), td->akey, (mcryptfunc) td->a_encrypt, (mcryptfunc) td->a_decrypt); +} + + +u32 gf_crypt_str_get_algorithm_version(const char *algorithm) +{ + u32 ret; + GF_Crypt *td = gf_crypt_open_intern(algorithm, NULL, 1); + ret = td ? td->algo_version : 0; + gf_crypt_close(td); + return ret; +} +u32 gf_crypt_str_get_mode_version(const char *mode) +{ + u32 ret; + GF_Crypt *td = gf_crypt_open_intern(NULL, mode, 1); + ret = td ? td->mode_version : 0; + gf_crypt_close(td); + return ret; +} +Bool gf_crypt_str_is_block_algorithm(const char *algorithm) +{ + Bool ret; + GF_Crypt *td = gf_crypt_open_intern(algorithm, NULL, 1); + ret = td ? td->is_block_algo : 0; + gf_crypt_close(td); + return ret; +} +Bool gf_crypt_str_is_block_algorithm_mode(const char *algorithm) +{ + Bool ret; + GF_Crypt *td = gf_crypt_open_intern(algorithm, NULL, 1); + ret = td ? td->is_block_algo_mode : 0; + gf_crypt_close(td); + return ret; +} +Bool gf_crypt_str_is_block_mode(const char *mode) +{ + Bool ret; + GF_Crypt *td = gf_crypt_open_intern(NULL, mode, 1); + ret = td ? td->is_block_mode : 0; + gf_crypt_close(td); + return ret; +} +u32 gf_crypt_str_module_get_algo_block_size(const char *algorithm) +{ + u32 ret; + GF_Crypt *td = gf_crypt_open_intern(algorithm, NULL, 1); + ret = td ? td->algo_block_size : 0; + gf_crypt_close(td); + return ret; +} +u32 gf_crypt_str_module_get_algo_key_size(const char *algorithm) +{ + u32 ret; + GF_Crypt *td = gf_crypt_open_intern(algorithm, NULL, 1); + ret = td ? td->key_size : 0; + gf_crypt_close(td); + return ret; +} +u32 gf_crypt_str_get_algo_supported_key_sizes(const char *algorithm, int *keys) +{ + u32 ret; + GF_Crypt *td = gf_crypt_open_intern(algorithm, NULL, 1); + ret = gf_crypt_get_supported_key_sizes(td, (u32 *)keys); + gf_crypt_close(td); + return ret; +} + diff --git a/src/mcrypt/ncfb.c b/src/mcrypt/ncfb.c new file mode 100644 index 0000000..436b80c --- /dev/null +++ b/src/mcrypt/ncfb.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 1998,1999,2000,2001 Nikos Mavroyanopoulos + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +typedef struct ncfb_buf { + u8* enc_s_register; + u8* s_register; + int s_register_pos; + int blocksize; +} nCFB_BUFFER; + +/* nCFB MODE */ + +static GF_Err _init_mcrypt( nCFB_BUFFER* buf, void *key, int lenofkey, void *IV, int size) +{ + buf->enc_s_register = buf->s_register = NULL; + buf->s_register_pos = 0; + + buf->blocksize = size; + +/* For cfb */ + buf->enc_s_register=malloc( size); + if (buf->enc_s_register==NULL) goto freeall; + + buf->s_register=malloc( size); + if (buf->s_register==NULL) goto freeall; + + if (IV!=NULL) { + memcpy(buf->enc_s_register, IV, size); + memcpy(buf->s_register, IV, size); + } else { + memset(buf->enc_s_register, 0, size); + memset(buf->s_register, 0, size); + } + +/* End ncfb */ + + return GF_OK; + freeall: + if (buf->enc_s_register) free(buf->enc_s_register); + if (buf->s_register) free(buf->s_register); + return GF_BAD_PARAM; +} + +static GF_Err _mcrypt_set_state( nCFB_BUFFER* buf, u8 *IV, int size) +{ + buf->s_register_pos = IV[0]; + memcpy(buf->enc_s_register, &IV[1], size-1); + memcpy(buf->s_register, &IV[1], size-1); + + return GF_OK; +} + +static GF_Err _mcrypt_get_state( nCFB_BUFFER* buf, u8 *IV, int *size) +{ + if (*size < buf->blocksize + 1) { + *size = buf->blocksize + 1; + return GF_BAD_PARAM; + } + *size = buf->blocksize + 1; + + IV[0] = buf->s_register_pos; + memcpy( &IV[1], buf->s_register, buf->blocksize); + + return GF_OK; +} + +static void _end_mcrypt( nCFB_BUFFER* buf) { + free(buf->enc_s_register); + free(buf->s_register); +} + +GFINLINE static +void xor_stuff_en( nCFB_BUFFER *buf, void* akey, void (*func)(void*,void*), u8* plain, int blocksize, int xor_size) +{ + void (*_mcrypt_block_encrypt) (void *, void *); + + _mcrypt_block_encrypt = func; + + if (xor_size == blocksize) { + if (buf->s_register_pos == 0) { + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memxor( plain, buf->enc_s_register, blocksize); + + memcpy(buf->s_register, plain, blocksize); + + } else { + int size = blocksize - buf->s_register_pos; + + memxor( plain, &buf->enc_s_register[buf->s_register_pos], + size); + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memxor( &plain[size], buf->enc_s_register, + buf->s_register_pos); + + memcpy( &buf->s_register[size], + plain, buf->s_register_pos); + + /* buf->s_register_pos remains the same */ + } + } else { /* xor_size != blocksize */ + if (buf->s_register_pos == 0) { + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memxor( plain, buf->enc_s_register, xor_size); + + memcpy(buf->s_register, plain, xor_size); + + buf->s_register_pos = xor_size; + } else { + int size = blocksize - buf->s_register_pos; + int min_size = size < xor_size ? size: xor_size; + + memxor( plain, &buf->enc_s_register[buf->s_register_pos], + min_size); + + memcpy( &buf->s_register[buf->s_register_pos], plain, min_size); + + buf->s_register_pos += min_size; + + if (min_size >= xor_size) + return; + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memxor( &plain[min_size], buf->s_register, + xor_size - min_size); + + buf->s_register_pos = xor_size - min_size; + + memcpy(buf->s_register, plain, xor_size - min_size); + } + + } + return; +} + +GFINLINE static +void xor_stuff_de( nCFB_BUFFER *buf, void* akey, void (*func)(void*,void*), u8* cipher, int blocksize, int xor_size) +{ + void (*_mcrypt_block_encrypt) (void *, void *); + + _mcrypt_block_encrypt = func; + + if (xor_size == blocksize) { + if (buf->s_register_pos == 0) { + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memcpy(buf->s_register, cipher, blocksize); + + memxor( cipher, buf->enc_s_register, blocksize); + + + } else { + int size = blocksize - buf->s_register_pos; + + memxor( cipher, &buf->enc_s_register[buf->s_register_pos], + size); + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memcpy( &buf->s_register[size], + cipher, buf->s_register_pos); + + memxor( &cipher[size], buf->enc_s_register, + buf->s_register_pos); + + + /* buf->s_register_pos remains the same */ + } + } else { /* xor_size != blocksize */ + if (buf->s_register_pos == 0) { + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memcpy(buf->s_register, cipher, xor_size); + + memxor( cipher, buf->enc_s_register, xor_size); + + + buf->s_register_pos = xor_size; + } else { + int size = blocksize - buf->s_register_pos; + int min_size = size < xor_size ? size: xor_size; + + memxor( cipher, &buf->enc_s_register[buf->s_register_pos], + min_size); + + memcpy( &buf->s_register[buf->s_register_pos], cipher, min_size); + + buf->s_register_pos += min_size; + + if (min_size >= xor_size) + return; + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memcpy(buf->s_register, cipher, xor_size - min_size); + + memxor( &cipher[min_size], buf->s_register, + xor_size - min_size); + + buf->s_register_pos = xor_size - min_size; + + } + + } + return; +} + + +static GF_Err _mcrypt( nCFB_BUFFER* buf,void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ /* plaintext is n*blocksize bytes (nbit cfb) */ + u8* plain; + int dlen, j=0; + void (*_mcrypt_block_encrypt) (void *, void *); + int modlen; + + _mcrypt_block_encrypt = func; + + dlen = len / blocksize; + plain = plaintext; + for (j = 0; j < dlen; j++) { + xor_stuff_en( buf, akey, func, plain, blocksize, blocksize); + + plain += blocksize; + + } + modlen = len % blocksize; + if (modlen > 0) { + xor_stuff_en( buf, akey, func, plain, blocksize, modlen); + } + + return GF_OK; +} + + +static GF_Err _mdecrypt( nCFB_BUFFER* buf,void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ /* plaintext is n*blocksize bytes (nbit cfb) */ + u8* plain; + int dlen, j=0; + void (*_mcrypt_block_encrypt) (void *, void *); + int modlen; + + _mcrypt_block_encrypt = func; + + dlen = len / blocksize; + plain = plaintext; + for (j = 0; j < dlen; j++) { + xor_stuff_de( buf, akey, func, plain, blocksize, blocksize); + + plain += blocksize; + + } + modlen = len % blocksize; + if (modlen > 0) { + xor_stuff_de( buf, akey, func, plain, blocksize, modlen); + } + + return GF_OK; +} + + +void gf_crypt_register_ncfb(GF_Crypt *td) +{ + td->mode_name = "nCFB"; + td->_init_mcrypt = _init_mcrypt; + td->_end_mcrypt = _end_mcrypt; + td->_mcrypt = _mcrypt; + td->_mdecrypt = _mdecrypt; + td->_mcrypt_get_state = _mcrypt_get_state; + td->_mcrypt_set_state = _mcrypt_set_state; + + td->has_IV = 1; + td->is_block_mode = 0; + td->is_block_algo_mode = 1; + td->mode_size = sizeof(nCFB_BUFFER); + td->mode_version = 20020307; +} + +#endif + diff --git a/src/mcrypt/nofb.c b/src/mcrypt/nofb.c new file mode 100644 index 0000000..2c3dc1a --- /dev/null +++ b/src/mcrypt/nofb.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 1998,1999,2000,2001 Nikos Mavroyanopoulos + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +typedef struct ncfb_buf { + u8* enc_s_register; + u8* s_register; + int s_register_pos; + int blocksize; +} nOFB_BUFFER; + +/* nOFB MODE */ + +static GF_Err _init_mcrypt( nOFB_BUFFER* buf, void *key, int lenofkey, void *IV, int size) +{ + buf->enc_s_register = buf->s_register = NULL; + buf->s_register_pos = 0; + + buf->blocksize = size; +/* For ofb */ + buf->enc_s_register=malloc( size); + if (buf->enc_s_register==NULL) goto freeall; + + buf->s_register=malloc( size); + if (buf->s_register==NULL) goto freeall; + + if (IV!=NULL) { + memcpy(buf->enc_s_register, IV, size); + memcpy(buf->s_register, IV, size); + } else { + memset(buf->enc_s_register, 0, size); + memset(buf->s_register, 0, size); + } + +/* End nofb */ + + return GF_OK; + freeall: + if (buf->enc_s_register) free(buf->enc_s_register); + if (buf->s_register) free(buf->s_register); + return GF_OUT_OF_MEM; +} + +static GF_Err _mcrypt_set_state( nOFB_BUFFER* buf, u8 *IV, int size) +{ + buf->s_register_pos = IV[0]; + memcpy(buf->enc_s_register, &IV[1], size-1); + memcpy(buf->s_register, &IV[1], size-1); + + return GF_OK; +} + +static GF_Err _mcrypt_get_state( nOFB_BUFFER* buf, u8 *IV, int *size) +{ + if (*size < buf->blocksize + 1) { + *size = buf->blocksize + 1; + return GF_BAD_PARAM; + } + *size = buf->blocksize + 1; + + IV[0] = buf->s_register_pos; + memcpy( &IV[1], buf->s_register, buf->blocksize); + + return GF_OK; +} + + +static void _end_mcrypt( nOFB_BUFFER* buf) { + free(buf->s_register); + free(buf->enc_s_register); +} + +GFINLINE static +void xor_stuff( nOFB_BUFFER *buf, void* akey, void (*func)(void*,void*), u8* plain, int blocksize, int xor_size) +{ + void (*_mcrypt_block_encrypt) (void *, void *); + + _mcrypt_block_encrypt = func; + + if (xor_size == blocksize) { + if (buf->s_register_pos == 0) { + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memcpy(buf->s_register, buf->enc_s_register, blocksize); + + memxor( plain, buf->enc_s_register, blocksize); + + } else { + int size = blocksize - buf->s_register_pos; + + memxor( plain, &buf->enc_s_register[buf->s_register_pos], + size); + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memcpy( buf->s_register, + buf->enc_s_register, blocksize); + + memxor( &plain[size], buf->enc_s_register, + buf->s_register_pos); + + /* buf->s_register_pos remains the same */ + } + } else { /* xor_size != blocksize */ + if (buf->s_register_pos == 0) { + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memcpy(buf->s_register, buf->enc_s_register, blocksize); + + memxor( plain, buf->enc_s_register, xor_size); + + buf->s_register_pos = xor_size; + } else { + int size = blocksize - buf->s_register_pos; + int min_size = size < xor_size ? size: xor_size; + + memxor( plain, &buf->enc_s_register[buf->s_register_pos], + min_size); + + buf->s_register_pos += min_size; + + if (min_size >= xor_size) + return; + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + memcpy(buf->s_register, buf->enc_s_register, blocksize); + + memxor( &plain[min_size], buf->s_register, + xor_size - min_size); + + buf->s_register_pos = xor_size - min_size; + + } + + } + return; +} + + +static GF_Err _mcrypt( nOFB_BUFFER* buf,void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ /* plaintext is n*blocksize bytes (nbit cfb) */ + u8* plain; + int dlen, j=0; + void (*_mcrypt_block_encrypt) (void *, void *); + int modlen; + + _mcrypt_block_encrypt = func; + + dlen = len / blocksize; + plain = plaintext; + for (j = 0; j < dlen; j++) { + xor_stuff( buf, akey, func, plain, blocksize, blocksize); + + plain += blocksize; + + } + modlen = len % blocksize; + if (modlen > 0) { + xor_stuff( buf, akey, func, plain, blocksize, modlen); + } + + return GF_OK; +} + +void gf_crypt_register_nofb(GF_Crypt *td) +{ + td->mode_name = "nOFB"; + td->_init_mcrypt = _init_mcrypt; + td->_end_mcrypt = _end_mcrypt; + td->_mcrypt = _mcrypt; + td->_mdecrypt = _mcrypt; + td->_mcrypt_get_state = _mcrypt_get_state; + td->_mcrypt_set_state = _mcrypt_set_state; + + td->has_IV = 1; + td->is_block_mode = 0; + td->is_block_algo_mode = 1; + td->mode_size = sizeof(nOFB_BUFFER); + td->mode_version = 20020307; +} + +#endif + diff --git a/src/mcrypt/ofb.c b/src/mcrypt/ofb.c new file mode 100644 index 0000000..dbaffc2 --- /dev/null +++ b/src/mcrypt/ofb.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 1998,1999,2000,2001,2002 Nikos Mavroyanopoulos + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +typedef struct ofb_buf { + u8* s_register; + u8* enc_s_register; + int blocksize; +} OFB_BUFFER; + + +/* OFB MODE */ + +static GF_Err _init_mcrypt( OFB_BUFFER* buf, void *key, int lenofkey, void *IV, int size) +{ + + buf->s_register = buf->enc_s_register = NULL; + + buf->blocksize = size; + + /* For ofb */ + buf->s_register=malloc( size); + if (buf->s_register==NULL) goto freeall; + + buf->enc_s_register=malloc( size); + if (buf->enc_s_register==NULL) goto freeall; + + if (IV!=NULL) { + memcpy(buf->s_register, IV, size); + } else { + memset(buf->s_register, 0, size); + } +/* End ofb */ + + return GF_OK; + + freeall: + if (buf->s_register) free(buf->s_register); + if (buf->enc_s_register) free(buf->enc_s_register); + return GF_OUT_OF_MEM; +} + +static GF_Err _mcrypt_get_state( OFB_BUFFER* buf, u8 *IV, int *size) +{ + if (*size < buf->blocksize) { + *size = buf->blocksize; + return GF_BAD_PARAM; + } + *size = buf->blocksize; + + memcpy( IV, buf->s_register, buf->blocksize); + + return GF_OK; +} + +static GF_Err _mcrypt_set_state( OFB_BUFFER* buf, void *IV, int size) +{ + memcpy(buf->enc_s_register, IV, size); + memcpy(buf->s_register, IV, size); + + return GF_OK; +} + + +static void _end_mcrypt( OFB_BUFFER* buf) { + free(buf->s_register); + free(buf->enc_s_register); +} + + +static GF_Err _mcrypt( OFB_BUFFER* buf,void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*) ) +{ /* plaintext is 1 u8 (8bit ofb) */ + char *plain = plaintext; + int i, j; + void (*_mcrypt_block_encrypt) (void *, void *); + + _mcrypt_block_encrypt = func; + + for (j = 0; j < len; j++) { + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + + plain[j] ^= buf->enc_s_register[0]; + +/* Shift the register */ + for (i = 0; i < (blocksize - 1); i++) + buf->s_register[i] = buf->s_register[i + 1]; + + buf->s_register[blocksize - 1] = buf->enc_s_register[0]; + } + + return GF_OK; + +} + + +static GF_Err _mdecrypt( OFB_BUFFER* buf, void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*), void (*func2)(void*,void*)) +{ /* plaintext is 1 u8 (8bit ofb) */ + char *plain = plaintext; + int i, j; + void (*_mcrypt_block_encrypt) (void *, void *); + + _mcrypt_block_encrypt = func; + + for (j = 0; j < len; j++) { + + memcpy(buf->enc_s_register, buf->s_register, blocksize); + + _mcrypt_block_encrypt(akey, buf->enc_s_register); + +/* Shift the register */ + for (i = 0; i < (blocksize - 1); i++) + buf->s_register[i] = buf->s_register[i + 1]; + + buf->s_register[blocksize - 1] = buf->enc_s_register[0]; + + plain[j] ^= buf->enc_s_register[0]; + + } + + return GF_OK; +} + +void gf_crypt_register_ofb(GF_Crypt *td) +{ + td->mode_name = "OFB"; + td->_init_mcrypt = _init_mcrypt; + td->_end_mcrypt = _end_mcrypt; + td->_mcrypt = _mcrypt; + td->_mdecrypt = _mdecrypt; + td->_mcrypt_get_state = _mcrypt_get_state; + td->_mcrypt_set_state = _mcrypt_set_state; + + td->has_IV = 1; + td->is_block_mode = 0; + td->is_block_algo_mode = 1; + td->mode_size = sizeof(OFB_BUFFER); + td->mode_version = 20010310; +} + +#endif + diff --git a/src/mcrypt/rijndael-128.c b/src/mcrypt/rijndael-128.c new file mode 100644 index 0000000..547a47c --- /dev/null +++ b/src/mcrypt/rijndael-128.c @@ -0,0 +1,414 @@ +/* Rijndael Cipher + + Written by Mike Scott 21st April 1999 + Copyright (c) 1999 Mike Scott + See rijndael documentation + + Permission for free direct or derivative use is granted subject + to compliance with any conditions that the originators of the + algorithm place on its exploitation. + + Inspiration from Brian Gladman's implementation is acknowledged. + + Written for clarity, rather than speed. + Full implementation. + Endian indifferent. +*/ + +/* modified in order to use the libmcrypt API by Nikos Mavroyanopoulos + * All modifications are placed under the license of libmcrypt. + */ + +/* $Id: rijndael-128.c,v 1.2 2006/11/13 18:07:07 jeanlf Exp $ */ + +#include + +typedef struct rijndael_instance { + int Nk,Nb,Nr; + u8 fi[24],ri[24]; + u32 fkey[120]; + u32 rkey[120]; +} RI; + +/* rotates x one bit to the left */ + +#define ROTL(x) (((x)>>7)|((x)<<1)) + +/* Rotates 32-bit word left by 1, 2 or 3 u8 */ + +#define ROTL8(x) (((x)<<8)|((x)>>24)) +#define ROTL16(x) (((x)<<16)|((x)>>16)) +#define ROTL24(x) (((x)<<24)|((x)>>8)) + +/* Fixed Data */ + +static u8 InCo[4] = { 0xB, 0xD, 0x9, 0xE }; /* Inverse Coefficients */ + +static u8 fbsub[256]; +static u8 rbsub[256]; +static u8 ptab[256], ltab[256]; +static u32 ftable[256]; +static u32 rtable[256]; +static u32 rco[30]; +static int tables_ok = 0; +/* Parameter-dependent data */ + +/* in "rijndael.h" */ + +static u32 pack(u8 * b) +{ /* pack bytes into a 32-bit Word */ + return ((u32) b[3] << 24) | ((u32) b[2] << 16) | ((u32) + b[1] << 8) + | (u32) b[0]; +} + +static void unpack(u32 a, u8 * b) +{ /* unpack bytes from a word */ + b[0] = (u8) a; + b[1] = (u8) (a >> 8); + b[2] = (u8) (a >> 16); + b[3] = (u8) (a >> 24); +} + + +static u8 xtime(u8 a) +{ + u8 b; + if (a & 0x80) + b = 0x1B; + else + b = 0; + a <<= 1; + a ^= b; + return a; +} + +static u8 bmul(u8 x, u8 y) +{ /* x.y= AntiLog(Log(x) + Log(y)) */ + if (x && y) + return ptab[(ltab[x] + ltab[y]) % 255]; + else + return 0; +} + +static u32 SubByte(u32 a) +{ + u8 b[4]; + unpack(a, b); + b[0] = fbsub[b[0]]; + b[1] = fbsub[b[1]]; + b[2] = fbsub[b[2]]; + b[3] = fbsub[b[3]]; + return pack(b); +} + +static u8 product(u32 x, u32 y) +{ /* dot product of two 4-u8 arrays */ + u8 xb[4], yb[4]; + unpack(x, xb); + unpack(y, yb); + return bmul(xb[0], yb[0]) ^ bmul(xb[1], yb[1]) ^ bmul(xb[2], + yb[2]) ^ + bmul(xb[3], yb[3]); +} + +static u32 InvMixCol(u32 x) +{ /* matrix Multiplication */ + u32 y, m; + u8 b[4]; + + m = pack(InCo); + b[3] = product(m, x); + m = ROTL24(m); + b[2] = product(m, x); + m = ROTL24(m); + b[1] = product(m, x); + m = ROTL24(m); + b[0] = product(m, x); + y = pack(b); + return y; +} + +static u8 ByteSub(u8 x) +{ + u8 y = ptab[255 - ltab[x]]; /* multiplicative inverse */ + x = y; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + y ^= 0x63; + return y; +} + +static void _mcrypt_rijndael_gentables(void) +{ /* generate tables */ + int i; + u8 y, b[4]; + + /* use 3 as primitive root to generate power and log tables */ + + ltab[0] = 0; + ptab[0] = 1; + ltab[1] = 0; + ptab[1] = 3; + ltab[3] = 1; + for (i = 2; i < 256; i++) { + ptab[i] = ptab[i - 1] ^ xtime(ptab[i - 1]); + ltab[ptab[i]] = i; + } + + /* affine transformation:- each bit is xored with itself shifted one bit */ + + fbsub[0] = 0x63; + rbsub[0x63] = 0; + for (i = 1; i < 256; i++) { + y = ByteSub((u8) i); + fbsub[i] = y; + rbsub[y] = i; + } + + for (i = 0, y = 1; i < 30; i++) { + rco[i] = y; + y = xtime(y); + } + + /* calculate forward and reverse tables */ + for (i = 0; i < 256; i++) { + y = fbsub[i]; + b[3] = y ^ xtime(y); + b[2] = y; + b[1] = y; + b[0] = xtime(y); + ftable[i] = pack(b); + + y = rbsub[i]; + b[3] = bmul(InCo[0], y); + b[2] = bmul(InCo[1], y); + b[1] = bmul(InCo[2], y); + b[0] = bmul(InCo[3], y); + rtable[i] = pack(b); + } +} + +static int _mcrypt_set_key(RI * rinst, u8 * key, int nk) +{ /* blocksize=32*nb bits. Key=32*nk bits */ + /* currently nb,bk = 4, 6 or 8 */ + /* key comes as 4*rinst->Nk bytes */ + /* Key Scheduler. Create expanded encryption key */ + int nb = 4; /* 128 block size */ + int i, j, k, m, N; + int C1, C2, C3; + u32 CipherKey[8]; + + nk /= 4; + if (nk < 4) + nk = 4; + + if (tables_ok == 0) { + _mcrypt_rijndael_gentables(); + tables_ok = 1; + } + + rinst->Nb = nb; + rinst->Nk = nk; + + /* rinst->Nr is number of rounds */ + if (rinst->Nb >= rinst->Nk) + rinst->Nr = 6 + rinst->Nb; + else + rinst->Nr = 6 + rinst->Nk; + + C1 = 1; + if (rinst->Nb < 8) { + C2 = 2; + C3 = 3; + } else { + C2 = 3; + C3 = 4; + } + + /* pre-calculate forward and reverse increments */ + for (m = j = 0; j < nb; j++, m += 3) { + rinst->fi[m] = (j + C1) % nb; + rinst->fi[m + 1] = (j + C2) % nb; + rinst->fi[m + 2] = (j + C3) % nb; + rinst->ri[m] = (nb + j - C1) % nb; + rinst->ri[m + 1] = (nb + j - C2) % nb; + rinst->ri[m + 2] = (nb + j - C3) % nb; + } + + N = rinst->Nb * (rinst->Nr + 1); + + for (i = j = 0; i < rinst->Nk; i++, j += 4) { + CipherKey[i] = pack(&key[j]); + } + for (i = 0; i < rinst->Nk; i++) + rinst->fkey[i] = CipherKey[i]; + for (j = rinst->Nk, k = 0; j < N; j += rinst->Nk, k++) { + rinst->fkey[j] = + rinst->fkey[j - + rinst->Nk] ^ SubByte(ROTL24(rinst-> + fkey[j - + 1])) ^ + rco[k]; + if (rinst->Nk <= 6) { + for (i = 1; i < rinst->Nk && (i + j) < N; i++) + rinst->fkey[i + j] = + rinst->fkey[i + j - + rinst->Nk] ^ rinst-> + fkey[i + j - 1]; + } else { + for (i = 1; i < 4 && (i + j) < N; i++) + rinst->fkey[i + j] = + rinst->fkey[i + j - + rinst->Nk] ^ rinst-> + fkey[i + j - 1]; + if ((j + 4) < N) + rinst->fkey[j + 4] = + rinst->fkey[j + 4 - + rinst-> + Nk] ^ SubByte(rinst-> + fkey[j + 3]); + for (i = 5; i < rinst->Nk && (i + j) < N; i++) + rinst->fkey[i + j] = + rinst->fkey[i + j - + rinst->Nk] ^ rinst-> + fkey[i + j - 1]; + } + + } + + /* now for the expanded decrypt key in reverse order */ + + for (j = 0; j < rinst->Nb; j++) + rinst->rkey[j + N - rinst->Nb] = rinst->fkey[j]; + for (i = rinst->Nb; i < N - rinst->Nb; i += rinst->Nb) { + k = N - rinst->Nb - i; + for (j = 0; j < rinst->Nb; j++) + rinst->rkey[k + j] = InvMixCol(rinst->fkey[i + j]); + } + for (j = N - rinst->Nb; j < N; j++) + rinst->rkey[j - N + rinst->Nb] = rinst->fkey[j]; + return 0; +} + + +/* There is an obvious time/space trade-off possible here. * + * Instead of just one ftable[], I could have 4, the other * + * 3 pre-rotated to save the ROTL8, ROTL16 and ROTL24 overhead */ + +static void _mcrypt_encrypt(RI * rinst, u8 * buff) +{ + int i, j, k, m; + u32 a[8], b[8], *x, *y, *t; + + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + a[i] = pack(&buff[j]); + a[i] ^= rinst->fkey[i]; + } + k = rinst->Nb; + x = a; + y = b; + +/* State alternates between a and b */ + for (i = 1; i < rinst->Nr; i++) { /* rinst->Nr is number of rounds. May be odd. */ + +/* if rinst->Nb is fixed - unroll this next + loop and hard-code in the values of fi[] */ + + for (m = j = 0; j < rinst->Nb; j++, m += 3) { /* deal with each 32-bit element of the State */ + /* This is the time-critical bit */ + y[j] = rinst->fkey[k++] ^ ftable[(u8) x[j]] ^ + ROTL8(ftable[(u8) (x[rinst->fi[m]] >> 8)]) ^ + ROTL16(ftable + [(u8) (x[rinst->fi[m + 1]] >> 16)]) ^ + ROTL24(ftable[x[rinst->fi[m + 2]] >> 24]); + } + t = x; + x = y; + y = t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m = j = 0; j < rinst->Nb; j++, m += 3) { + y[j] = rinst->fkey[k++] ^ (u32) fbsub[(u8) x[j]] ^ + ROTL8((u32) fbsub[(u8) (x[rinst->fi[m]] >> 8)]) ^ + ROTL16((u32) + fbsub[(u8) (x[rinst->fi[m + 1]] >> 16)]) ^ + ROTL24((u32) fbsub[x[rinst->fi[m + 2]] >> 24]); + } + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + unpack(y[i], &buff[j]); + x[i] = y[i] = 0; /* clean up stack */ + } + return; +} + +static void _mcrypt_decrypt(RI * rinst, u8 * buff) +{ + int i, j, k, m; + u32 a[8], b[8], *x, *y, *t; + + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + a[i] = pack(&buff[j]); + a[i] ^= rinst->rkey[i]; + } + k = rinst->Nb; + x = a; + y = b; + +/* State alternates between a and b */ + for (i = 1; i < rinst->Nr; i++) { /* rinst->Nr is number of rounds. May be odd. */ + +/* if rinst->Nb is fixed - unroll this next + loop and hard-code in the values of ri[] */ + + for (m = j = 0; j < rinst->Nb; j++, m += 3) { /* This is the time-critical bit */ + y[j] = rinst->rkey[k++] ^ rtable[(u8) x[j]] ^ + ROTL8(rtable[(u8) (x[rinst->ri[m]] >> 8)]) ^ + ROTL16(rtable + [(u8) (x[rinst->ri[m + 1]] >> 16)]) ^ + ROTL24(rtable[x[rinst->ri[m + 2]] >> 24]); + } + t = x; + x = y; + y = t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m = j = 0; j < rinst->Nb; j++, m += 3) { + y[j] = rinst->rkey[k++] ^ (u32) rbsub[(u8) x[j]] ^ + ROTL8((u32) rbsub[(u8) (x[rinst->ri[m]] >> 8)]) ^ + ROTL16((u32) + rbsub[(u8) (x[rinst->ri[m + 1]] >> 16)]) ^ + ROTL24((u32) rbsub[x[rinst->ri[m + 2]] >> 24]); + } + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + unpack(y[i], &buff[j]); + x[i] = y[i] = 0; /* clean up stack */ + } + return; +} + +void gf_crypt_register_rijndael_128(GF_Crypt *td) +{ + td->a_encrypt = (void *)_mcrypt_encrypt; + td->a_decrypt = (void *)_mcrypt_decrypt; + td->a_set_key = (void *)_mcrypt_set_key; + td->algo_name = "Rijndael-128"; + td->algo_version = 20010801; + td->num_key_sizes = 3; + td->key_sizes[0] = 16; + td->key_sizes[1] = 24; + td->key_sizes[2] = 32; + td->key_size = 32; + td->is_block_algo = 1; + td->algo_block_size = 16; + td->algo_size = sizeof(RI); +} + diff --git a/src/mcrypt/rijndael-192.c b/src/mcrypt/rijndael-192.c new file mode 100644 index 0000000..50487d2 --- /dev/null +++ b/src/mcrypt/rijndael-192.c @@ -0,0 +1,418 @@ +/* Rijndael Cipher + + Written by Mike Scott 21st April 1999 + Copyright (c) 1999 Mike Scott + See rijndael documentation + + Permission for free direct or derivative use is granted subject + to compliance with any conditions that the originators of the + algorithm place on its exploitation. + + Inspiration from Brian Gladman's implementation is acknowledged. + + Written for clarity, rather than speed. + Full implementation. + Endian indifferent. +*/ + +/* modified in order to use the libmcrypt API by Nikos Mavroyanopoulos + * All modifications are placed under the license of libmcrypt. + */ + + +/* $Id: rijndael-192.c,v 1.1.1.1 2005/07/13 14:36:35 jeanlf Exp $ */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +typedef struct rijndael_instance { + int Nk,Nb,Nr; + u8 fi[24],ri[24]; + u32 fkey[120]; + u32 rkey[120]; +} RI; + + +/* rotates x one bit to the left */ + +#define ROTL(x) (((x)>>7)|((x)<<1)) + +/* Rotates 32-bit word left by 1, 2 or 3 u8 */ + +#define ROTL8(x) (((x)<<8)|((x)>>24)) +#define ROTL16(x) (((x)<<16)|((x)>>16)) +#define ROTL24(x) (((x)<<24)|((x)>>8)) + +/* Fixed Data */ + +static u8 InCo[4] = { 0xB, 0xD, 0x9, 0xE }; /* Inverse Coefficients */ + +static u8 fbsub[256]; +static u8 rbsub[256]; +static u8 ptab[256], ltab[256]; +static u32 ftable[256]; +static u32 rtable[256]; +static u32 rco[30]; +static int tables_ok = 0; + +/* Parameter-dependent data */ + +/* in "rijndael.h" */ + +static u32 pack(u8 * b) +{ /* pack bytes into a 32-bit Word */ + return ((u32) b[3] << 24) | ((u32) b[2] << 16) | ((u32) + b[1] << 8) + | (u32) b[0]; +} + +static void unpack(u32 a, u8 * b) +{ /* unpack bytes from a word */ + b[0] = (u8) a; + b[1] = (u8) (a >> 8); + b[2] = (u8) (a >> 16); + b[3] = (u8) (a >> 24); +} + +static u8 xtime(u8 a) +{ + u8 b; + if (a & 0x80) + b = 0x1B; + else + b = 0; + a <<= 1; + a ^= b; + return a; +} + +static u8 bmul(u8 x, u8 y) +{ /* x.y= AntiLog(Log(x) + Log(y)) */ + if (x && y) + return ptab[(ltab[x] + ltab[y]) % 255]; + else + return 0; +} + +static u32 SubByte(u32 a) +{ + u8 b[4]; + unpack(a, b); + b[0] = fbsub[b[0]]; + b[1] = fbsub[b[1]]; + b[2] = fbsub[b[2]]; + b[3] = fbsub[b[3]]; + return pack(b); +} + +static u8 product(u32 x, u32 y) +{ /* dot product of two 4-u8 arrays */ + u8 xb[4], yb[4]; + unpack(x, xb); + unpack(y, yb); + return bmul(xb[0], yb[0]) ^ bmul(xb[1], yb[1]) ^ bmul(xb[2], + yb[2]) ^ + bmul(xb[3], yb[3]); +} + +static u32 InvMixCol(u32 x) +{ /* matrix Multiplication */ + u32 y, m; + u8 b[4]; + + m = pack(InCo); + b[3] = product(m, x); + m = ROTL24(m); + b[2] = product(m, x); + m = ROTL24(m); + b[1] = product(m, x); + m = ROTL24(m); + b[0] = product(m, x); + y = pack(b); + return y; +} + +static u8 ByteSub(u8 x) +{ + u8 y = ptab[255 - ltab[x]]; /* multiplicative inverse */ + x = y; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + y ^= 0x63; + return y; +} + +static void _mcrypt_rijndael_gentables(void) +{ /* generate tables */ + int i; + u8 y, b[4]; + + /* use 3 as primitive root to generate power and log tables */ + + ltab[0] = 0; + ptab[0] = 1; + ltab[1] = 0; + ptab[1] = 3; + ltab[3] = 1; + for (i = 2; i < 256; i++) { + ptab[i] = ptab[i - 1] ^ xtime(ptab[i - 1]); + ltab[ptab[i]] = i; + } + + /* affine transformation:- each bit is xored with itself shifted one bit */ + + fbsub[0] = 0x63; + rbsub[0x63] = 0; + for (i = 1; i < 256; i++) { + y = ByteSub((u8) i); + fbsub[i] = y; + rbsub[y] = i; + } + + for (i = 0, y = 1; i < 30; i++) { + rco[i] = y; + y = xtime(y); + } + + /* calculate forward and reverse tables */ + for (i = 0; i < 256; i++) { + y = fbsub[i]; + b[3] = y ^ xtime(y); + b[2] = y; + b[1] = y; + b[0] = xtime(y); + ftable[i] = pack(b); + + y = rbsub[i]; + b[3] = bmul(InCo[0], y); + b[2] = bmul(InCo[1], y); + b[1] = bmul(InCo[2], y); + b[0] = bmul(InCo[3], y); + rtable[i] = pack(b); + } +} + +static GF_Err _mcrypt_set_key(RI * rinst, u8 * key, int nk) +{ /* blocksize=32*nb bits. Key=32*nk bits */ + /* currently nb,bk = 4, 6 or 8 */ + /* key comes as 4*rinst->Nk bytes */ + /* Key Scheduler. Create expanded encryption key */ + int nb = 6; /* 192 block size */ + int i, j, k, m, N; + int C1, C2, C3; + u32 CipherKey[8]; + + nk /= 4; + + if (tables_ok == 0) { + _mcrypt_rijndael_gentables(); + tables_ok = 1; + } + + rinst->Nb = nb; + rinst->Nk = nk; + + /* rinst->Nr is number of rounds */ + if (rinst->Nb >= rinst->Nk) + rinst->Nr = 6 + rinst->Nb; + else + rinst->Nr = 6 + rinst->Nk; + + C1 = 1; + if (rinst->Nb < 8) { + C2 = 2; + C3 = 3; + } else { + C2 = 3; + C3 = 4; + } + + /* pre-calculate forward and reverse increments */ + for (m = j = 0; j < nb; j++, m += 3) { + rinst->fi[m] = (j + C1) % nb; + rinst->fi[m + 1] = (j + C2) % nb; + rinst->fi[m + 2] = (j + C3) % nb; + rinst->ri[m] = (nb + j - C1) % nb; + rinst->ri[m + 1] = (nb + j - C2) % nb; + rinst->ri[m + 2] = (nb + j - C3) % nb; + } + + N = rinst->Nb * (rinst->Nr + 1); + + for (i = j = 0; i < rinst->Nk; i++, j += 4) { + CipherKey[i] = pack(&key[j]); + } + for (i = 0; i < rinst->Nk; i++) + rinst->fkey[i] = CipherKey[i]; + for (j = rinst->Nk, k = 0; j < N; j += rinst->Nk, k++) { + rinst->fkey[j] = + rinst->fkey[j - + rinst->Nk] ^ SubByte(ROTL24(rinst-> + fkey[j - + 1])) ^ + rco[k]; + if (rinst->Nk <= 6) { + for (i = 1; i < rinst->Nk && (i + j) < N; i++) + rinst->fkey[i + j] = + rinst->fkey[i + j - + rinst->Nk] ^ rinst-> + fkey[i + j - 1]; + } else { + for (i = 1; i < 4 && (i + j) < N; i++) + rinst->fkey[i + j] = + rinst->fkey[i + j - + rinst->Nk] ^ rinst-> + fkey[i + j - 1]; + if ((j + 4) < N) + rinst->fkey[j + 4] = + rinst->fkey[j + 4 - + rinst-> + Nk] ^ SubByte(rinst-> + fkey[j + 3]); + for (i = 5; i < rinst->Nk && (i + j) < N; i++) + rinst->fkey[i + j] = + rinst->fkey[i + j - + rinst->Nk] ^ rinst-> + fkey[i + j - 1]; + } + + } + + /* now for the expanded decrypt key in reverse order */ + + for (j = 0; j < rinst->Nb; j++) + rinst->rkey[j + N - rinst->Nb] = rinst->fkey[j]; + for (i = rinst->Nb; i < N - rinst->Nb; i += rinst->Nb) { + k = N - rinst->Nb - i; + for (j = 0; j < rinst->Nb; j++) + rinst->rkey[k + j] = InvMixCol(rinst->fkey[i + j]); + } + for (j = N - rinst->Nb; j < N; j++) + rinst->rkey[j - N + rinst->Nb] = rinst->fkey[j]; + return GF_OK; +} + + +/* There is an obvious time/space trade-off possible here. * + * Instead of just one ftable[], I could have 4, the other * + * 3 pre-rotated to save the ROTL8, ROTL16 and ROTL24 overhead */ + +static void _mcrypt_encrypt(RI * rinst, u8 * buff) +{ + int i, j, k, m; + u32 a[8], b[8], *x, *y, *t; + + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + a[i] = pack(&buff[j]); + a[i] ^= rinst->fkey[i]; + } + k = rinst->Nb; + x = a; + y = b; + +/* State alternates between a and b */ + for (i = 1; i < rinst->Nr; i++) { /* rinst->Nr is number of rounds. May be odd. */ + +/* if rinst->Nb is fixed - unroll this next + loop and hard-code in the values of fi[] */ + + for (m = j = 0; j < rinst->Nb; j++, m += 3) { /* deal with each 32-bit element of the State */ + /* This is the time-critical bit */ + y[j] = rinst->fkey[k++] ^ ftable[(u8) x[j]] ^ + ROTL8(ftable[(u8) (x[rinst->fi[m]] >> 8)]) ^ + ROTL16(ftable + [(u8) (x[rinst->fi[m + 1]] >> 16)]) ^ + ROTL24(ftable[x[rinst->fi[m + 2]] >> 24]); + } + t = x; + x = y; + y = t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m = j = 0; j < rinst->Nb; j++, m += 3) { + y[j] = rinst->fkey[k++] ^ (u32) fbsub[(u8) x[j]] ^ + ROTL8((u32) fbsub[(u8) (x[rinst->fi[m]] >> 8)]) ^ + ROTL16((u32) + fbsub[(u8) (x[rinst->fi[m + 1]] >> 16)]) ^ + ROTL24((u32) fbsub[x[rinst->fi[m + 2]] >> 24]); + } + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + unpack(y[i], &buff[j]); + x[i] = y[i] = 0; /* clean up stack */ + } + return; +} + +static void _mcrypt_decrypt(RI * rinst, u8 * buff) +{ + int i, j, k, m; + u32 a[8], b[8], *x, *y, *t; + + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + a[i] = pack(&buff[j]); + a[i] ^= rinst->rkey[i]; + } + k = rinst->Nb; + x = a; + y = b; + +/* State alternates between a and b */ + for (i = 1; i < rinst->Nr; i++) { /* rinst->Nr is number of rounds. May be odd. */ + +/* if rinst->Nb is fixed - unroll this next + loop and hard-code in the values of ri[] */ + + for (m = j = 0; j < rinst->Nb; j++, m += 3) { /* This is the time-critical bit */ + y[j] = rinst->rkey[k++] ^ rtable[(u8) x[j]] ^ + ROTL8(rtable[(u8) (x[rinst->ri[m]] >> 8)]) ^ + ROTL16(rtable + [(u8) (x[rinst->ri[m + 1]] >> 16)]) ^ + ROTL24(rtable[x[rinst->ri[m + 2]] >> 24]); + } + t = x; + x = y; + y = t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m = j = 0; j < rinst->Nb; j++, m += 3) { + y[j] = rinst->rkey[k++] ^ (u32) rbsub[(u8) x[j]] ^ + ROTL8((u32) rbsub[(u8) (x[rinst->ri[m]] >> 8)]) ^ + ROTL16((u32) + rbsub[(u8) (x[rinst->ri[m + 1]] >> 16)]) ^ + ROTL24((u32) rbsub[x[rinst->ri[m + 2]] >> 24]); + } + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + unpack(y[i], &buff[j]); + x[i] = y[i] = 0; /* clean up stack */ + } + return; +} + +void gf_crypt_register_rijndael_192(GF_Crypt *td) +{ + td->a_encrypt = _mcrypt_encrypt; + td->a_decrypt = _mcrypt_decrypt; + td->a_set_key = _mcrypt_set_key; + td->algo_name = "Rijndael-192"; + td->algo_version = 20010801; + td->num_key_sizes = 3; + td->key_sizes[0] = 16; + td->key_sizes[1] = 24; + td->key_sizes[2] = 32; + td->key_size = 32; + td->is_block_algo = 1; + td->algo_block_size = 24; + td->algo_size = sizeof(RI); +} + +#endif + diff --git a/src/mcrypt/rijndael-256.c b/src/mcrypt/rijndael-256.c new file mode 100644 index 0000000..356e833 --- /dev/null +++ b/src/mcrypt/rijndael-256.c @@ -0,0 +1,416 @@ +/* Rijndael Cipher + + Written by Mike Scott 21st April 1999 + Copyright (c) 1999 Mike Scott + See rijndael documentation + + Permission for free direct or derivative use is granted subject + to compliance with any conditions that the originators of the + algorithm place on its exploitation. + + Inspiration from Brian Gladman's implementation is acknowledged. + + Written for clarity, rather than speed. + Full implementation. + Endian indifferent. +*/ + +/* modified in order to use the libmcrypt API by Nikos Mavroyanopoulos + * All modifications are placed under the license of libmcrypt. + */ + +/* $Id: rijndael-256.c,v 1.1.1.1 2005/07/13 14:36:35 jeanlf Exp $ */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +typedef struct rijndael_instance { + int Nk,Nb,Nr; + u8 fi[24],ri[24]; + u32 fkey[120]; + u32 rkey[120]; +} RI; + + +/* rotates x one bit to the left */ + +#define ROTL(x) (((x)>>7)|((x)<<1)) + +/* Rotates 32-bit word left by 1, 2 or 3 u8 */ + +#define ROTL8(x) (((x)<<8)|((x)>>24)) +#define ROTL16(x) (((x)<<16)|((x)>>16)) +#define ROTL24(x) (((x)<<24)|((x)>>8)) + +/* Fixed Data */ + +static u8 InCo[4] = { 0xB, 0xD, 0x9, 0xE }; /* Inverse Coefficients */ + +static u8 fbsub[256]; +static u8 rbsub[256]; +static u8 ptab[256], ltab[256]; +static u32 ftable[256]; +static u32 rtable[256]; +static u32 rco[30]; +static int tables_ok = 0; + +/* Parameter-dependent data */ + +/* in "rijndael.h" */ + +static u32 pack(u8 * b) +{ /* pack bytes into a 32-bit Word */ + return ((u32) b[3] << 24) | ((u32) b[2] << 16) | ((u32) + b[1] << 8) + | (u32) b[0]; +} + +static void unpack(u32 a, u8 * b) +{ /* unpack bytes from a word */ + b[0] = (u8) a; + b[1] = (u8) (a >> 8); + b[2] = (u8) (a >> 16); + b[3] = (u8) (a >> 24); +} + +static u8 xtime(u8 a) +{ + u8 b; + if (a & 0x80) + b = 0x1B; + else + b = 0; + a <<= 1; + a ^= b; + return a; +} + +static u8 bmul(u8 x, u8 y) +{ /* x.y= AntiLog(Log(x) + Log(y)) */ + if (x && y) + return ptab[(ltab[x] + ltab[y]) % 255]; + else + return 0; +} + +static u32 SubByte(u32 a) +{ + u8 b[4]; + unpack(a, b); + b[0] = fbsub[b[0]]; + b[1] = fbsub[b[1]]; + b[2] = fbsub[b[2]]; + b[3] = fbsub[b[3]]; + return pack(b); +} + +static u8 product(u32 x, u32 y) +{ /* dot product of two 4-u8 arrays */ + u8 xb[4], yb[4]; + unpack(x, xb); + unpack(y, yb); + return bmul(xb[0], yb[0]) ^ bmul(xb[1], yb[1]) ^ bmul(xb[2], + yb[2]) ^ + bmul(xb[3], yb[3]); +} + +static u32 InvMixCol(u32 x) +{ /* matrix Multiplication */ + u32 y, m; + u8 b[4]; + + m = pack(InCo); + b[3] = product(m, x); + m = ROTL24(m); + b[2] = product(m, x); + m = ROTL24(m); + b[1] = product(m, x); + m = ROTL24(m); + b[0] = product(m, x); + y = pack(b); + return y; +} + +static u8 ByteSub(u8 x) +{ + u8 y = ptab[255 - ltab[x]]; /* multiplicative inverse */ + x = y; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + y ^= 0x63; + return y; +} + +static void _mcrypt_rijndael_gentables(void) +{ /* generate tables */ + int i; + u8 y, b[4]; + + /* use 3 as primitive root to generate power and log tables */ + + ltab[0] = 0; + ptab[0] = 1; + ltab[1] = 0; + ptab[1] = 3; + ltab[3] = 1; + for (i = 2; i < 256; i++) { + ptab[i] = ptab[i - 1] ^ xtime(ptab[i - 1]); + ltab[ptab[i]] = i; + } + + /* affine transformation:- each bit is xored with itself shifted one bit */ + + fbsub[0] = 0x63; + rbsub[0x63] = 0; + for (i = 1; i < 256; i++) { + y = ByteSub((u8) i); + fbsub[i] = y; + rbsub[y] = i; + } + + for (i = 0, y = 1; i < 30; i++) { + rco[i] = y; + y = xtime(y); + } + + /* calculate forward and reverse tables */ + for (i = 0; i < 256; i++) { + y = fbsub[i]; + b[3] = y ^ xtime(y); + b[2] = y; + b[1] = y; + b[0] = xtime(y); + ftable[i] = pack(b); + + y = rbsub[i]; + b[3] = bmul(InCo[0], y); + b[2] = bmul(InCo[1], y); + b[1] = bmul(InCo[2], y); + b[0] = bmul(InCo[3], y); + rtable[i] = pack(b); + } +} + +static GF_Err _mcrypt_set_key(RI * rinst, u8 * key, int nk) +{ /* blocksize=32*nb bits. Key=32*nk bits */ + /* currently nb,bk = 4, 6 or 8 */ + /* key comes as 4*rinst->Nk bytes */ + /* Key Scheduler. Create expanded encryption key */ + int nb = 8; /* 256 block size */ + int i, j, k, m, N; + int C1, C2, C3; + u32 CipherKey[8]; + + nk /= 4; + + if (tables_ok == 0) { + _mcrypt_rijndael_gentables(); + tables_ok = 1; + } + + rinst->Nb = nb; + rinst->Nk = nk; + + /* rinst->Nr is number of rounds */ + if (rinst->Nb >= rinst->Nk) + rinst->Nr = 6 + rinst->Nb; + else + rinst->Nr = 6 + rinst->Nk; + + C1 = 1; + if (rinst->Nb < 8) { + C2 = 2; + C3 = 3; + } else { + C2 = 3; + C3 = 4; + } + + /* pre-calculate forward and reverse increments */ + for (m = j = 0; j < nb; j++, m += 3) { + rinst->fi[m] = (j + C1) % nb; + rinst->fi[m + 1] = (j + C2) % nb; + rinst->fi[m + 2] = (j + C3) % nb; + rinst->ri[m] = (nb + j - C1) % nb; + rinst->ri[m + 1] = (nb + j - C2) % nb; + rinst->ri[m + 2] = (nb + j - C3) % nb; + } + + N = rinst->Nb * (rinst->Nr + 1); + + for (i = j = 0; i < rinst->Nk; i++, j += 4) { + CipherKey[i] = pack(&key[j]); + } + for (i = 0; i < rinst->Nk; i++) + rinst->fkey[i] = CipherKey[i]; + for (j = rinst->Nk, k = 0; j < N; j += rinst->Nk, k++) { + rinst->fkey[j] = + rinst->fkey[j - + rinst->Nk] ^ SubByte(ROTL24(rinst-> + fkey[j - + 1])) ^ + rco[k]; + if (rinst->Nk <= 6) { + for (i = 1; i < rinst->Nk && (i + j) < N; i++) + rinst->fkey[i + j] = + rinst->fkey[i + j - + rinst->Nk] ^ rinst-> + fkey[i + j - 1]; + } else { + for (i = 1; i < 4 && (i + j) < N; i++) + rinst->fkey[i + j] = + rinst->fkey[i + j - + rinst->Nk] ^ rinst-> + fkey[i + j - 1]; + if ((j + 4) < N) + rinst->fkey[j + 4] = + rinst->fkey[j + 4 - + rinst-> + Nk] ^ SubByte(rinst-> + fkey[j + 3]); + for (i = 5; i < rinst->Nk && (i + j) < N; i++) + rinst->fkey[i + j] = + rinst->fkey[i + j - + rinst->Nk] ^ rinst-> + fkey[i + j - 1]; + } + + } + + /* now for the expanded decrypt key in reverse order */ + + for (j = 0; j < rinst->Nb; j++) + rinst->rkey[j + N - rinst->Nb] = rinst->fkey[j]; + for (i = rinst->Nb; i < N - rinst->Nb; i += rinst->Nb) { + k = N - rinst->Nb - i; + for (j = 0; j < rinst->Nb; j++) + rinst->rkey[k + j] = InvMixCol(rinst->fkey[i + j]); + } + for (j = N - rinst->Nb; j < N; j++) + rinst->rkey[j - N + rinst->Nb] = rinst->fkey[j]; + return GF_OK; +} + + +/* There is an obvious time/space trade-off possible here. * + * Instead of just one ftable[], I could have 4, the other * + * 3 pre-rotated to save the ROTL8, ROTL16 and ROTL24 overhead */ + +static void _mcrypt_encrypt(RI * rinst, u8 * buff) +{ + int i, j, k, m; + u32 a[8], b[8], *x, *y, *t; + + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + a[i] = pack(&buff[j]); + a[i] ^= rinst->fkey[i]; + } + k = rinst->Nb; + x = a; + y = b; + +/* State alternates between a and b */ + for (i = 1; i < rinst->Nr; i++) { /* rinst->Nr is number of rounds. May be odd. */ + +/* if rinst->Nb is fixed - unroll this next + loop and hard-code in the values of fi[] */ + + for (m = j = 0; j < rinst->Nb; j++, m += 3) { /* deal with each 32-bit element of the State */ + /* This is the time-critical bit */ + y[j] = rinst->fkey[k++] ^ ftable[(u8) x[j]] ^ + ROTL8(ftable[(u8) (x[rinst->fi[m]] >> 8)]) ^ + ROTL16(ftable + [(u8) (x[rinst->fi[m + 1]] >> 16)]) ^ + ROTL24(ftable[x[rinst->fi[m + 2]] >> 24]); + } + t = x; + x = y; + y = t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m = j = 0; j < rinst->Nb; j++, m += 3) { + y[j] = rinst->fkey[k++] ^ (u32) fbsub[(u8) x[j]] ^ + ROTL8((u32) fbsub[(u8) (x[rinst->fi[m]] >> 8)]) ^ + ROTL16((u32) + fbsub[(u8) (x[rinst->fi[m + 1]] >> 16)]) ^ + ROTL24((u32) fbsub[x[rinst->fi[m + 2]] >> 24]); + } + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + unpack(y[i], &buff[j]); + x[i] = y[i] = 0; /* clean up stack */ + } + return; +} + +static void _mcrypt_decrypt(RI * rinst, u8 * buff) +{ + int i, j, k, m; + u32 a[8], b[8], *x, *y, *t; + + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + a[i] = pack(&buff[j]); + a[i] ^= rinst->rkey[i]; + } + k = rinst->Nb; + x = a; + y = b; + +/* State alternates between a and b */ + for (i = 1; i < rinst->Nr; i++) { /* rinst->Nr is number of rounds. May be odd. */ + +/* if rinst->Nb is fixed - unroll this next + loop and hard-code in the values of ri[] */ + + for (m = j = 0; j < rinst->Nb; j++, m += 3) { /* This is the time-critical bit */ + y[j] = rinst->rkey[k++] ^ rtable[(u8) x[j]] ^ + ROTL8(rtable[(u8) (x[rinst->ri[m]] >> 8)]) ^ + ROTL16(rtable + [(u8) (x[rinst->ri[m + 1]] >> 16)]) ^ + ROTL24(rtable[x[rinst->ri[m + 2]] >> 24]); + } + t = x; + x = y; + y = t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m = j = 0; j < rinst->Nb; j++, m += 3) { + y[j] = rinst->rkey[k++] ^ (u32) rbsub[(u8) x[j]] ^ + ROTL8((u32) rbsub[(u8) (x[rinst->ri[m]] >> 8)]) ^ + ROTL16((u32) + rbsub[(u8) (x[rinst->ri[m + 1]] >> 16)]) ^ + ROTL24((u32) rbsub[x[rinst->ri[m + 2]] >> 24]); + } + for (i = j = 0; i < rinst->Nb; i++, j += 4) { + unpack(y[i], &buff[j]); + x[i] = y[i] = 0; /* clean up stack */ + } + return; +} + +void gf_crypt_register_rijndael_256(GF_Crypt *td) +{ + td->a_encrypt = _mcrypt_encrypt; + td->a_decrypt = _mcrypt_decrypt; + td->a_set_key = _mcrypt_set_key; + td->algo_name = "Rijndael-256"; + td->algo_version = 20010801; + td->num_key_sizes = 3; + td->key_sizes[0] = 16; + td->key_sizes[1] = 24; + td->key_sizes[2] = 32; + td->key_size = 32; + td->is_block_algo = 1; + td->algo_block_size = 32; + td->algo_size = sizeof(RI); +} + +#endif diff --git a/src/mcrypt/sha1.c b/src/mcrypt/sha1.c new file mode 100644 index 0000000..6e07e9e --- /dev/null +++ b/src/mcrypt/sha1.c @@ -0,0 +1,381 @@ +/* + * FIPS-180-1 compliant SHA-1 implementation + * + * Copyright (C) 2003-2006 Christophe Devine + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +#include + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (u32) (b)[(i) ] << 24 ) \ + | ( (u32) (b)[(i) + 1] << 16 ) \ + | ( (u32) (b)[(i) + 2] << 8 ) \ + | ( (u32) (b)[(i) + 3] ); \ +} +#endif +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (u8) ( (n) >> 24 ); \ + (b)[(i) + 1] = (u8) ( (n) >> 16 ); \ + (b)[(i) + 2] = (u8) ( (n) >> 8 ); \ + (b)[(i) + 3] = (u8) ( (n) ); \ +} +#endif + +/* + * SHA-1 context setup + */ +void gf_sha1_starts(GF_SHA1Context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +static void sha1_process(GF_SHA1Context *ctx, u8 data[64] ) +{ + u32 temp, W[16], A, B, C, D, E; + + GET_UINT32_BE( W[0], data, 0 ); + GET_UINT32_BE( W[1], data, 4 ); + GET_UINT32_BE( W[2], data, 8 ); + GET_UINT32_BE( W[3], data, 12 ); + GET_UINT32_BE( W[4], data, 16 ); + GET_UINT32_BE( W[5], data, 20 ); + GET_UINT32_BE( W[6], data, 24 ); + GET_UINT32_BE( W[7], data, 28 ); + GET_UINT32_BE( W[8], data, 32 ); + GET_UINT32_BE( W[9], data, 36 ); + GET_UINT32_BE( W[10], data, 40 ); + GET_UINT32_BE( W[11], data, 44 ); + GET_UINT32_BE( W[12], data, 48 ); + GET_UINT32_BE( W[13], data, 52 ); + GET_UINT32_BE( W[14], data, 56 ); + GET_UINT32_BE( W[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ +( \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ + ( W[t & 0x0F] = S(temp,1) ) \ +) + +#define P(a,b,c,d,e,x) \ +{ \ + e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P( A, B, C, D, E, W[0] ); + P( E, A, B, C, D, W[1] ); + P( D, E, A, B, C, W[2] ); + P( C, D, E, A, B, W[3] ); + P( B, C, D, E, A, W[4] ); + P( A, B, C, D, E, W[5] ); + P( E, A, B, C, D, W[6] ); + P( D, E, A, B, C, W[7] ); + P( C, D, E, A, B, W[8] ); + P( B, C, D, E, A, W[9] ); + P( A, B, C, D, E, W[10] ); + P( E, A, B, C, D, W[11] ); + P( D, E, A, B, C, W[12] ); + P( C, D, E, A, B, W[13] ); + P( B, C, D, E, A, W[14] ); + P( A, B, C, D, E, W[15] ); + P( E, A, B, C, D, R(16) ); + P( D, E, A, B, C, R(17) ); + P( C, D, E, A, B, R(18) ); + P( B, C, D, E, A, R(19) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P( A, B, C, D, E, R(20) ); + P( E, A, B, C, D, R(21) ); + P( D, E, A, B, C, R(22) ); + P( C, D, E, A, B, R(23) ); + P( B, C, D, E, A, R(24) ); + P( A, B, C, D, E, R(25) ); + P( E, A, B, C, D, R(26) ); + P( D, E, A, B, C, R(27) ); + P( C, D, E, A, B, R(28) ); + P( B, C, D, E, A, R(29) ); + P( A, B, C, D, E, R(30) ); + P( E, A, B, C, D, R(31) ); + P( D, E, A, B, C, R(32) ); + P( C, D, E, A, B, R(33) ); + P( B, C, D, E, A, R(34) ); + P( A, B, C, D, E, R(35) ); + P( E, A, B, C, D, R(36) ); + P( D, E, A, B, C, R(37) ); + P( C, D, E, A, B, R(38) ); + P( B, C, D, E, A, R(39) ); + +#undef K +#undef F + +#define F(x,y,z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P( A, B, C, D, E, R(40) ); + P( E, A, B, C, D, R(41) ); + P( D, E, A, B, C, R(42) ); + P( C, D, E, A, B, R(43) ); + P( B, C, D, E, A, R(44) ); + P( A, B, C, D, E, R(45) ); + P( E, A, B, C, D, R(46) ); + P( D, E, A, B, C, R(47) ); + P( C, D, E, A, B, R(48) ); + P( B, C, D, E, A, R(49) ); + P( A, B, C, D, E, R(50) ); + P( E, A, B, C, D, R(51) ); + P( D, E, A, B, C, R(52) ); + P( C, D, E, A, B, R(53) ); + P( B, C, D, E, A, R(54) ); + P( A, B, C, D, E, R(55) ); + P( E, A, B, C, D, R(56) ); + P( D, E, A, B, C, R(57) ); + P( C, D, E, A, B, R(58) ); + P( B, C, D, E, A, R(59) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P( A, B, C, D, E, R(60) ); + P( E, A, B, C, D, R(61) ); + P( D, E, A, B, C, R(62) ); + P( C, D, E, A, B, R(63) ); + P( B, C, D, E, A, R(64) ); + P( A, B, C, D, E, R(65) ); + P( E, A, B, C, D, R(66) ); + P( D, E, A, B, C, R(67) ); + P( C, D, E, A, B, R(68) ); + P( B, C, D, E, A, R(69) ); + P( A, B, C, D, E, R(70) ); + P( E, A, B, C, D, R(71) ); + P( D, E, A, B, C, R(72) ); + P( C, D, E, A, B, R(73) ); + P( B, C, D, E, A, R(74) ); + P( A, B, C, D, E, R(75) ); + P( E, A, B, C, D, R(76) ); + P( D, E, A, B, C, R(77) ); + P( C, D, E, A, B, R(78) ); + P( B, C, D, E, A, R(79) ); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +/* + * SHA-1 process buffer + */ +void gf_sha1_update(GF_SHA1Context *ctx, u8 *input, u32 ilen ) +{ + s32 fill; + u32 left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (u32) ilen ) + ctx->total[1]++; + + if( left && (s32) ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + sha1_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + sha1_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, ilen ); + } +} + +static const u8 sha1_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * SHA-1 final digest + */ +void gf_sha1_finish(GF_SHA1Context *ctx, u8 output[20] ) +{ + u32 last, padn; + u32 high, low; + u8 msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_BE( high, msglen, 0 ); + PUT_UINT32_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + gf_sha1_update( ctx, (u8 *) sha1_padding, padn ); + gf_sha1_update( ctx, msglen, 8 ); + + PUT_UINT32_BE( ctx->state[0], output, 0 ); + PUT_UINT32_BE( ctx->state[1], output, 4 ); + PUT_UINT32_BE( ctx->state[2], output, 8 ); + PUT_UINT32_BE( ctx->state[3], output, 12 ); + PUT_UINT32_BE( ctx->state[4], output, 16 ); +} + +/* + * Output = SHA-1( file contents ) + */ +s32 gf_sha1_file( char *path, u8 output[20] ) +{ + FILE *f; + size_t n; + GF_SHA1Context ctx; + u8 buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( 1 ); + + gf_sha1_starts( &ctx ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + gf_sha1_update( &ctx, buf, (s32) n ); + + gf_sha1_finish( &ctx, output ); + + fclose( f ); + return( 0 ); +} + +/* + * Output = SHA-1( input buffer ) + */ +void gf_sha1_csum( u8 *input, u32 ilen, u8 output[20] ) +{ + GF_SHA1Context ctx; + + gf_sha1_starts( &ctx ); + gf_sha1_update( &ctx, input, ilen ); + gf_sha1_finish( &ctx, output ); +} + +/* + * Output = HMAC-SHA-1( input buffer, hmac key ) + */ +void gf_sha1_hmac( u8 *key, u32 keylen, + u8 *input, u32 ilen, + u8 output[20] ) +{ + s32 i; + GF_SHA1Context ctx; + u8 k_ipad[64]; + u8 k_opad[64]; + u8 tmpbuf[20]; + + memset( k_ipad, 0x36, 64 ); + memset( k_opad, 0x5C, 64 ); + + for( i = 0; i < (s32) keylen; i++ ) + { + if( i >= 64 ) break; + + k_ipad[i] ^= key[i]; + k_opad[i] ^= key[i]; + } + + gf_sha1_starts( &ctx ); + gf_sha1_update( &ctx, k_ipad, 64 ); + gf_sha1_update( &ctx, input, ilen ); + gf_sha1_finish( &ctx, tmpbuf ); + + gf_sha1_starts( &ctx ); + gf_sha1_update( &ctx, k_opad, 64 ); + gf_sha1_update( &ctx, tmpbuf, 20 ); + gf_sha1_finish( &ctx, output ); + + memset( k_ipad, 0, 64 ); + memset( k_opad, 0, 64 ); + memset( tmpbuf, 0, 20 ); + memset( &ctx, 0, sizeof(GF_SHA1Context ) ); +} + diff --git a/src/mcrypt/stream.c b/src/mcrypt/stream.c new file mode 100644 index 0000000..fd04d10 --- /dev/null +++ b/src/mcrypt/stream.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 1998,1999,2000,2001 Nikos Mavroyanopoulos + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +static GF_Err _init_mcrypt( void* ign, void *key, int lenofkey, void *IV, int size) { return GF_OK; } + +static GF_Err _mcrypt_set_state( void* buf, void *IV, int size) { return GF_BAD_PARAM; } +static GF_Err _mcrypt_get_state( void* buf, void *IV, int *size) { return GF_BAD_PARAM; } + +static void _end_mcrypt(void* ign) {} + +static GF_Err _mcrypt( void* ign, void *plaintext, int len, int blocksize, void* akey, void (*func)(void*,void*, int), void (*func2)(void*,void*, int)) +{ + void (*_mcrypt_stream_encrypt) (void *, void *, int); + + _mcrypt_stream_encrypt = func; + + _mcrypt_stream_encrypt(akey, plaintext, len); + return GF_OK; +} + +static GF_Err _mdecrypt( void* ign, void *ciphertext, int len, int blocksize, void* akey, void (*func)(void*,void*, int), void (*func2)(void*,void*, int)) +{ + void (*_mcrypt_stream_decrypt) (void *, void *, int); + + _mcrypt_stream_decrypt = func2; + + _mcrypt_stream_decrypt(akey, ciphertext, len); + return GF_OK; +} + +void gf_crypt_register_stream(GF_Crypt *td) +{ + td->mode_name = "STREAM"; + td->_init_mcrypt = _init_mcrypt; + td->_end_mcrypt = _end_mcrypt; + td->_mcrypt = _mcrypt; + td->_mdecrypt = _mdecrypt; + td->_mcrypt_get_state = _mcrypt_get_state; + td->_mcrypt_set_state = _mcrypt_set_state; + + td->has_IV = 1; + td->is_block_mode = 0; + td->is_block_algo_mode = 0; + td->mode_size = 0; + td->mode_version = 20010801; +} + +#endif + diff --git a/src/mcrypt/tripledes.c b/src/mcrypt/tripledes.c new file mode 100644 index 0000000..e18668c --- /dev/null +++ b/src/mcrypt/tripledes.c @@ -0,0 +1,760 @@ + +/* Sofware DES functions + * written 12 Dec 1986 by Phil Karn, KA9Q; large sections adapted from + * the 1977 public-domain program by Jim Gillogly + * Modified for additional speed - 6 December 1988 Phil Karn + * Modified for parameterized key schedules - Jan 1991 Phil Karn + * Modified modified such that will operate in EDE 3DES - 1999 Nikos Mavroyanopoulos + * + * Callers now allocate a key schedule as follows: + * kn = (char (*)[8])malloc(sizeof(char) * 8 * 16); + * or + * char kn[16][8]; + */ + +/* modified in order to use the libmcrypt API by Nikos Mavroyanopoulos + * All modifications are placed under the license of libmcrypt. + */ + +/* $Id: tripledes.c,v 1.1.1.1 2005/07/13 14:36:36 jeanlf Exp $ */ + +#include + +#ifndef GPAC_CRYPT_ISMA_ONLY + +typedef struct triple_des_key { + char kn[3][16][8]; + u32 sp[3][8][64]; + + char iperm[16][16][8]; + char fperm[16][16][8]; + +} TRIPLEDES_KEY; + + +static void permute(), perminit(), spinit(); +static u32 f(); + + +/* Tables defined in the Data Encryption Standard documents */ + +/* initial permutation IP */ +static char ip[] = { + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7 +}; + +/* final permutation IP^-1 */ +static char fp[] = { + 40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25 +}; + +/* expansion operation matrix + * This is for reference only; it is unused in the code + * as the f() function performs it implicitly for speed + */ +#ifdef notdef +static char ei[] = { + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1 +}; +#endif + +/* permuted choice table (key) */ +static char pc1[] = { + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4 +}; + +/* number left rotations of pc1 */ +static char totrot[] = { + 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 +}; + +/* permuted choice key (table) */ +static char pc2[] = { + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 +}; + +/* The (in)famous S-boxes */ +static char si[8][64] = { + /* S1 */ + {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}, + + /* S2 */ + {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}, + + /* S3 */ + {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}, + + /* S4 */ + {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}, + + /* S5 */ + {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}, + + /* S6 */ + {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}, + + /* S7 */ + {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}, + + /* S8 */ + {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}, + +}; + +/* 32-bit permutation function P used on the output of the S-boxes */ +static char p32i[] = { + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25 +}; + +/* End of DES-defined tables */ + +/* Lookup tables initialized once only at startup by desinit() */ + +/* bit 0 is left-most in byte */ +static int bytebit[] = { + 0200, 0100, 040, 020, 010, 04, 02, 01 +}; + +static int nibblebit[] = { + 010, 04, 02, 01 +}; + +/* Allocate space and initialize DES lookup arrays + * mode == 0: standard Data Encryption Algorithm + */ +static int _mcrypt_desinit(TRIPLEDES_KEY * key) +{ + + spinit(key, 0); + spinit(key, 1); + spinit(key, 2); + perminit(&key->iperm, ip); + perminit(&key->fperm, fp); + + + return 0; +} + + +/* Set key (initialize key schedule array) */ +static GF_Err _mcrypt_set_key(TRIPLEDES_KEY * dkey, char *user_key, int len) +{ + char pc1m[56]; /* place to modify pc1 into */ + char pcr[56]; /* place to rotate pc1 into */ + register int i, j, l; + int m; + char *user_key1 = &user_key[0]; + char *user_key2 = &user_key[8]; + char *user_key3 = &user_key[16]; + + _mcrypt_desinit(dkey); + + /* Clear key schedule */ + Bzero(dkey->kn[0], 16 * 8); + Bzero(dkey->kn[1], 16 * 8); + Bzero(dkey->kn[2], 16 * 8); + + /* DES 1 */ + for (j = 0; j < 56; j++) { /* convert pc1 to bits of key */ + l = pc1[j] - 1; /* integer bit location */ + m = l & 07; /* find bit */ + pc1m[j] = (user_key1[l >> 3] & /* find which key byte l is in */ + bytebit[m]) /* and which bit of that byte */ + ? 1 : 0; /* and store 1-bit result */ + + } + for (i = 0; i < 16; i++) { /* key chunk for each iteration */ + for (j = 0; j < 56; j++) /* rotate pc1 the right amount */ + pcr[j] = + pc1m[(l = j + totrot[i]) < + (j < 28 ? 28 : 56) ? l : l - 28]; + /* rotate left and right halves independently */ + for (j = 0; j < 48; j++) { /* select bits individually */ + /* check bit that goes to kn[j] */ + if (pcr[pc2[j] - 1]) { + /* mask it in if it's there */ + l = j % 6; + dkey->kn[0][i][j / 6] |= bytebit[l] >> 2; + } + } + } +/* DES 2 */ + for (j = 0; j < 56; j++) { /* convert pc1 to bits of key */ + l = pc1[j] - 1; /* integer bit location */ + m = l & 07; /* find bit */ + pc1m[j] = (user_key2[l >> 3] & /* find which key byte l is in */ + bytebit[m]) /* and which bit of that byte */ + ? 1 : 0; /* and store 1-bit result */ + + } + for (i = 0; i < 16; i++) { /* key chunk for each iteration */ + for (j = 0; j < 56; j++) /* rotate pc1 the right amount */ + pcr[j] = + pc1m[(l = j + totrot[i]) < + (j < 28 ? 28 : 56) ? l : l - 28]; + /* rotate left and right halves independently */ + for (j = 0; j < 48; j++) { /* select bits individually */ + /* check bit that goes to kn[j] */ + if (pcr[pc2[j] - 1]) { + /* mask it in if it's there */ + l = j % 6; + dkey->kn[1][i][j / 6] |= bytebit[l] >> 2; + } + } + } +/* DES 3 */ + for (j = 0; j < 56; j++) { /* convert pc1 to bits of key */ + l = pc1[j] - 1; /* integer bit location */ + m = l & 07; /* find bit */ + pc1m[j] = (user_key3[l >> 3] & /* find which key byte l is in */ + bytebit[m]) /* and which bit of that byte */ + ? 1 : 0; /* and store 1-bit result */ + + } + for (i = 0; i < 16; i++) { /* key chunk for each iteration */ + for (j = 0; j < 56; j++) /* rotate pc1 the right amount */ + pcr[j] = + pc1m[(l = j + totrot[i]) < + (j < 28 ? 28 : 56) ? l : l - 28]; + /* rotate left and right halves independently */ + for (j = 0; j < 48; j++) { /* select bits individually */ + /* check bit that goes to kn[j] */ + if (pcr[pc2[j] - 1]) { + /* mask it in if it's there */ + l = j % 6; + dkey->kn[2][i][j / 6] |= bytebit[l] >> 2; + } + } + } + return GF_OK; +} + + + + +/* In-place encryption of 64-bit block */ + +static void _mcrypt_encrypt(TRIPLEDES_KEY * key, char *block) +{ + register u32 left, right; + register char *knp; + u32 work[2]; /* Working data storage */ + +/* DES 1 */ + permute(block, key->iperm, (char *) work); /* Initial Permutation */ +#ifndef WORDS_BIGENDIAN + left = byteswap32(work[0]); + right = byteswap32(work[1]); +#else + left = work[0]; + right = work[1]; +#endif + + /* Do the 16 rounds. + * The rounds are numbered from 0 to 15. On even rounds + * the right half is fed to f() and the result exclusive-ORs + * the left half; on odd rounds the reverse is done. + */ + knp = &key->kn[0][0][0]; + left ^= f(key, 0, right, knp); + knp += 8; + right ^= f(key, 0, left, knp); + knp += 8; + left ^= f(key, 0, right, knp); + knp += 8; + right ^= f(key, 0, left, knp); + knp += 8; + left ^= f(key, 0, right, knp); + knp += 8; + right ^= f(key, 0, left, knp); + knp += 8; + left ^= f(key, 0, right, knp); + knp += 8; + right ^= f(key, 0, left, knp); + knp += 8; + left ^= f(key, 0, right, knp); + knp += 8; + right ^= f(key, 0, left, knp); + knp += 8; + left ^= f(key, 0, right, knp); + knp += 8; + right ^= f(key, 0, left, knp); + knp += 8; + left ^= f(key, 0, right, knp); + knp += 8; + right ^= f(key, 0, left, knp); + knp += 8; + left ^= f(key, 0, right, knp); + knp += 8; + right ^= f(key, 0, left, knp); + +/* DES 2 */ + + /* Do the 16 rounds in reverse order. + * The rounds are numbered from 15 to 0. On even rounds + * the right half is fed to f() and the result exclusive-ORs + * the left half; on odd rounds the reverse is done. + */ + knp = &key->kn[1][15][0]; + right ^= f(key, 1, left, knp); + knp -= 8; + left ^= f(key, 1, right, knp); + knp -= 8; + right ^= f(key, 1, left, knp); + knp -= 8; + left ^= f(key, 1, right, knp); + knp -= 8; + right ^= f(key, 1, left, knp); + knp -= 8; + left ^= f(key, 1, right, knp); + knp -= 8; + right ^= f(key, 1, left, knp); + knp -= 8; + left ^= f(key, 1, right, knp); + knp -= 8; + right ^= f(key, 1, left, knp); + knp -= 8; + left ^= f(key, 1, right, knp); + knp -= 8; + right ^= f(key, 1, left, knp); + knp -= 8; + left ^= f(key, 1, right, knp); + knp -= 8; + right ^= f(key, 1, left, knp); + knp -= 8; + left ^= f(key, 1, right, knp); + knp -= 8; + right ^= f(key, 1, left, knp); + knp -= 8; + left ^= f(key, 1, right, knp); + + /* Do the 16 rounds. + * The rounds are numbered from 0 to 15. On even rounds + * the right half is fed to f() and the result exclusive-ORs + * the left half; on odd rounds the reverse is done. + */ + knp = &key->kn[2][0][0]; + left ^= f(key, 2, right, knp); + knp += 8; + right ^= f(key, 2, left, knp); + knp += 8; + left ^= f(key, 2, right, knp); + knp += 8; + right ^= f(key, 2, left, knp); + knp += 8; + left ^= f(key, 2, right, knp); + knp += 8; + right ^= f(key, 2, left, knp); + knp += 8; + left ^= f(key, 2, right, knp); + knp += 8; + right ^= f(key, 2, left, knp); + knp += 8; + left ^= f(key, 2, right, knp); + knp += 8; + right ^= f(key, 2, left, knp); + knp += 8; + left ^= f(key, 2, right, knp); + knp += 8; + right ^= f(key, 2, left, knp); + knp += 8; + left ^= f(key, 2, right, knp); + knp += 8; + right ^= f(key, 2, left, knp); + knp += 8; + left ^= f(key, 2, right, knp); + knp += 8; + right ^= f(key, 2, left, knp); + + /* Left/right half swap, plus byte swap if little-endian */ +#ifndef WORDS_BIGENDIAN + work[1] = byteswap32(left); + work[0] = byteswap32(right); +#else + work[0] = right; + work[1] = left; +#endif + permute((char *) work, key->fperm, block); /* Inverse initial permutation */ + +} + +/* In-place decryption of 64-bit block. This function is the mirror + * image of encryption; exactly the same steps are taken, but in + * reverse order + */ +static void _mcrypt_decrypt(TRIPLEDES_KEY * key, char *block) +{ + register u32 left, right; + register char *knp; + u32 work[2]; /* Working data storage */ + + permute(block, key->iperm, (char *) work); /* Initial permutation */ + + /* Left/right half swap, plus byte swap if little-endian */ +#ifndef WORDS_BIGENDIAN + right = byteswap32(work[0]); + left = byteswap32(work[1]); +#else + right = work[0]; + left = work[1]; +#endif + +/* DES 3 */ + + /* Do the 16 rounds in reverse order. + * The rounds are numbered from 15 to 0. On even rounds + * the right half is fed to f() and the result exclusive-ORs + * the left half; on odd rounds the reverse is done. + */ + knp = &key->kn[2][15][0]; + right ^= f(key, 2, left, knp); + knp -= 8; + left ^= f(key, 2, right, knp); + knp -= 8; + right ^= f(key, 2, left, knp); + knp -= 8; + left ^= f(key, 2, right, knp); + knp -= 8; + right ^= f(key, 2, left, knp); + knp -= 8; + left ^= f(key, 2, right, knp); + knp -= 8; + right ^= f(key, 2, left, knp); + knp -= 8; + left ^= f(key, 2, right, knp); + knp -= 8; + right ^= f(key, 2, left, knp); + knp -= 8; + left ^= f(key, 2, right, knp); + knp -= 8; + right ^= f(key, 2, left, knp); + knp -= 8; + left ^= f(key, 2, right, knp); + knp -= 8; + right ^= f(key, 2, left, knp); + knp -= 8; + left ^= f(key, 2, right, knp); + knp -= 8; + right ^= f(key, 2, left, knp); + knp -= 8; + left ^= f(key, 2, right, knp); + + +/* DES 2*/ + /* Do the 16 rounds. + * The rounds are numbered from 0 to 15. On even rounds + * the right half is fed to f() and the result exclusive-ORs + * the left half; on odd rounds the reverse is done. + */ + knp = &key->kn[1][0][0]; + left ^= f(key, 1, right, knp); + knp += 8; + right ^= f(key, 1, left, knp); + knp += 8; + left ^= f(key, 1, right, knp); + knp += 8; + right ^= f(key, 1, left, knp); + knp += 8; + left ^= f(key, 1, right, knp); + knp += 8; + right ^= f(key, 1, left, knp); + knp += 8; + left ^= f(key, 1, right, knp); + knp += 8; + right ^= f(key, 1, left, knp); + knp += 8; + left ^= f(key, 1, right, knp); + knp += 8; + right ^= f(key, 1, left, knp); + knp += 8; + left ^= f(key, 1, right, knp); + knp += 8; + right ^= f(key, 1, left, knp); + knp += 8; + left ^= f(key, 1, right, knp); + knp += 8; + right ^= f(key, 1, left, knp); + knp += 8; + left ^= f(key, 1, right, knp); + knp += 8; + right ^= f(key, 1, left, knp); + +/* DES 1 */ + /* Do the 16 rounds in reverse order. + * The rounds are numbered from 15 to 0. On even rounds + * the right half is fed to f() and the result exclusive-ORs + * the left half; on odd rounds the reverse is done. + */ + knp = &key->kn[0][15][0]; + right ^= f(key, 0, left, knp); + knp -= 8; + left ^= f(key, 0, right, knp); + knp -= 8; + right ^= f(key, 0, left, knp); + knp -= 8; + left ^= f(key, 0, right, knp); + knp -= 8; + right ^= f(key, 0, left, knp); + knp -= 8; + left ^= f(key, 0, right, knp); + knp -= 8; + right ^= f(key, 0, left, knp); + knp -= 8; + left ^= f(key, 0, right, knp); + knp -= 8; + right ^= f(key, 0, left, knp); + knp -= 8; + left ^= f(key, 0, right, knp); + knp -= 8; + right ^= f(key, 0, left, knp); + knp -= 8; + left ^= f(key, 0, right, knp); + knp -= 8; + right ^= f(key, 0, left, knp); + knp -= 8; + left ^= f(key, 0, right, knp); + knp -= 8; + right ^= f(key, 0, left, knp); + knp -= 8; + left ^= f(key, 0, right, knp); + + +#ifndef WORDS_BIGENDIAN + work[0] = byteswap32(left); + work[1] = byteswap32(right); +#else + work[0] = left; + work[1] = right; +#endif + permute((char *) work, key->fperm, block); /* Inverse initial permutation */ +} + +/* Permute inblock with perm */ +static void permute(char *inblock, char perm[16][16][8], char *outblock) +{ + register char *ib, *ob; /* ptr to input or output block */ + register char *p, *q; + register int j; + + if (perm == NULL) { + /* No permutation, just copy */ + memmove(outblock, inblock, 8); + return; + } + /* Clear output block */ + Bzero(outblock, 8); + + ib = inblock; + for (j = 0; j < 16; j += 2, ib++) { /* for each input nibble */ + ob = outblock; + p = perm[j][(*ib >> 4) & 0xf]; + q = perm[j + 1][*ib & 0xf]; + /* and each output byte, OR the masks together */ + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + *ob++ |= *p++ | *q++; + } +} + +/* The nonlinear function f(r,k), the heart of DES */ +static u32 f(TRIPLEDES_KEY * key, int pos, register u32 r, + register char *subkey) +{ + register u32 *spp; + register u32 rval, rt; + register int er; + +#ifdef TRACE + printf("f(%08lx, %02x %02x %02x %02x %02x %02x %02x %02x) = ", + r, + subkey[0], subkey[1], subkey[2], + subkey[3], subkey[4], subkey[5], subkey[6], subkey[7]); +#endif + /* Run E(R) ^ K through the combined S & P boxes. + * This code takes advantage of a convenient regularity in + * E, namely that each group of 6 bits in E(R) feeding + * a single S-box is a contiguous segment of R. + */ + subkey += 7; + + /* Compute E(R) for each block of 6 bits, and run thru boxes */ + er = ((int) r << 1) | ((r & 0x80000000) ? 1 : 0); + spp = &key->sp[pos][7][0]; + rval = spp[(er ^ *subkey--) & 0x3f]; + spp -= 64; + rt = (u32) r >> 3; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rval |= spp[((int) rt ^ *subkey--) & 0x3f]; + spp -= 64; + rt >>= 4; + rt |= (r & 1) << 5; + rval |= spp[((int) rt ^ *subkey) & 0x3f]; +#ifdef TRACE + printf(" %08lx\n", rval); +#endif + return rval; +} + +/* initialize a perm array */ +static void perminit(char perm[16][16][8], char p[64]) +{ + register int l, j, k; + int i, m; + + /* Clear the permutation array */ + Bzero((char *) perm, 16 * 16 * 8); + + for (i = 0; i < 16; i++) /* each input nibble position */ + for (j = 0; j < 16; j++) /* each possible input nibble */ + for (k = 0; k < 64; k++) { /* each output bit position */ + l = p[k] - 1; /* where does this bit come from */ + if ((l >> 2) != i) /* does it come from input posn? */ + continue; /* if not, bit k is 0 */ + if (!(j & nibblebit[l & 3])) + continue; /* any such bit in input? */ + m = k & 07; /* which bit is this in the byte */ + perm[i][j][k >> 3] |= bytebit[m]; + } +} + +/* Initialize the lookup table for the combined S and P boxes */ +static void spinit(TRIPLEDES_KEY * key, int pos) +{ + char pbox[32]; + int p, i, s, j, rowcol; + u32 val; + + /* Compute pbox, the inverse of p32i. + * This is easier to work with + */ + for (p = 0; p < 32; p++) { + for (i = 0; i < 32; i++) { + if (p32i[i] - 1 == p) { + pbox[p] = i; + break; + } + } + } + for (s = 0; s < 8; s++) { /* For each S-box */ + for (i = 0; i < 64; i++) { /* For each possible input */ + val = 0; + /* The row number is formed from the first and last + * bits; the column number is from the middle 4 + */ + rowcol = + (i & 32) | ((i & 1) ? 16 : 0) | ((i >> 1) & + 0xf); + for (j = 0; j < 4; j++) { /* For each output bit */ + if (si[s][rowcol] & (8 >> j)) { + val |= + 1L << (31 - pbox[4 * s + j]); + } + } + key->sp[pos][s][i] = val; + } + } +} + +void gf_crypt_register_3des(GF_Crypt *td) +{ + td->a_encrypt = _mcrypt_encrypt; + td->a_decrypt = _mcrypt_decrypt; + td->a_set_key = _mcrypt_set_key; + td->algo_name = "3DES"; + td->algo_version = 19991129; + td->num_key_sizes = 1; + td->key_sizes[0] = 24; + td->key_size = 24; + td->is_block_algo = 1; + td->algo_block_size = 8; + td->algo_size = sizeof(TRIPLEDES_KEY); +} + +#endif + diff --git a/src/media_tools/av_parsers.c b/src/media_tools/av_parsers.c new file mode 100644 index 0000000..8561c99 --- /dev/null +++ b/src/media_tools/av_parsers.c @@ -0,0 +1,2636 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include + +/* + MPEG-4 video (14496-2) +*/ + +#define M4V_VO_START_CODE 0x00 +#define M4V_VOL_START_CODE 0x20 +#define M4V_VOP_START_CODE 0xB6 +#define M4V_VISOBJ_START_CODE 0xB5 +#define M4V_VOS_START_CODE 0xB0 +#define M4V_GOV_START_CODE 0xB3 +#define M4V_UDTA_START_CODE 0xB2 + + +#define M2V_PIC_START_CODE 0x00 +#define M2V_SEQ_START_CODE 0xB3 +#define M2V_EXT_START_CODE 0xB5 +#define M2V_GOP_START_CODE 0xB8 + +struct __tag_m4v_parser +{ + GF_BitStream *bs; + Bool mpeg12; + u32 current_object_type; + u32 current_object_start; + u32 tc_dec, prev_tc_dec, tc_disp, prev_tc_disp; +}; + +GF_EXPORT +GF_M4VParser *gf_m4v_parser_new(char *data, u32 data_size, Bool mpeg12video) +{ + GF_M4VParser *tmp; + if (!data || !data_size) return NULL; + GF_SAFEALLOC(tmp, GF_M4VParser); + tmp->bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); + tmp->mpeg12 = mpeg12video; + return tmp; +} + +GF_M4VParser *gf_m4v_parser_bs_new(GF_BitStream *bs, Bool mpeg12video) +{ + GF_M4VParser *tmp; + GF_SAFEALLOC(tmp, GF_M4VParser); + tmp->bs = bs; + tmp->mpeg12 = mpeg12video; + return tmp; +} + +GF_EXPORT +void gf_m4v_parser_del(GF_M4VParser *m4v) +{ + gf_bs_del(m4v->bs); + free(m4v); +} + + +#define M4V_CACHE_SIZE 4096 +s32 M4V_LoadObject(GF_M4VParser *m4v) +{ + u32 v, bpos, found; + char m4v_cache[M4V_CACHE_SIZE]; + u64 end, cache_start, load_size; + if (!m4v) return 0; + bpos = 0; + found = 0; + load_size = 0; + end = 0; + cache_start = 0; + v = 0xffffffff; + while (!end) { + /*refill cache*/ + if (bpos == (u32) load_size) { + if (!gf_bs_available(m4v->bs)) break; + load_size = gf_bs_available(m4v->bs); + if (load_size>M4V_CACHE_SIZE) load_size=M4V_CACHE_SIZE; + bpos = 0; + cache_start = gf_bs_get_position(m4v->bs); + gf_bs_read_data(m4v->bs, m4v_cache, (u32) load_size); + } + v = ( (v<<8) & 0xFFFFFF00) | ((u8) m4v_cache[bpos]); + bpos++; + if ((v & 0xFFFFFF00) == 0x00000100) { + end = cache_start+bpos-4; + found = 1; + break; + } + } + if (!found) return -1; + m4v->current_object_start = (u32) end; + gf_bs_seek(m4v->bs, end+3); + m4v->current_object_type = gf_bs_read_u8(m4v->bs); + return (s32) m4v->current_object_type; +} + +GF_EXPORT +const char *gf_m4v_get_profile_name(u8 video_pl) +{ + switch (video_pl) { + case 0x00: return "Reserved (0x00) Profile"; + case 0x01: return "Simple Profile @ Level 1"; + case 0x02: return "Simple Profile @ Level 2"; + case 0x03: return "Simple Profile @ Level 3"; + case 0x08: return "Simple Profile @ Level 0"; + case 0x10: return "Simple Scalable Profile @ Level 0"; + case 0x11: return "Simple Scalable Profile @ Level 1"; + case 0x12: return "Simple Scalable Profile @ Level 2"; + case 0x15: return "AVC/H264 Profile"; + case 0x21: return "Core Profile @ Level 1"; + case 0x22: return "Core Profile @ Level 2"; + case 0x32: return "Main Profile @ Level 2"; + case 0x33: return "Main Profile @ Level 3"; + case 0x34: return "Main Profile @ Level 4"; + case 0x42: return "N-bit Profile @ Level 2"; + case 0x51: return "Scalable Texture Profile @ Level 1"; + case 0x61: return "Simple Face Animation Profile @ Level 1"; + case 0x62: return "Simple Face Animation Profile @ Level 2"; + case 0x63: return "Simple FBA Profile @ Level 1"; + case 0x64: return "Simple FBA Profile @ Level 2"; + case 0x71: return "Basic Animated Texture Profile @ Level 1"; + case 0x72: return "Basic Animated Texture Profile @ Level 2"; + case 0x81: return "Hybrid Profile @ Level 1"; + case 0x82: return "Hybrid Profile @ Level 2"; + case 0x91: return "Advanced Real Time Simple Profile @ Level 1"; + case 0x92: return "Advanced Real Time Simple Profile @ Level 2"; + case 0x93: return "Advanced Real Time Simple Profile @ Level 3"; + case 0x94: return "Advanced Real Time Simple Profile @ Level 4"; + case 0xA1: return "Core Scalable Profile @ Level1"; + case 0xA2: return "Core Scalable Profile @ Level2"; + case 0xA3: return "Core Scalable Profile @ Level3"; + case 0xB1: return "Advanced Coding Efficiency Profile @ Level 1"; + case 0xB2: return "Advanced Coding Efficiency Profile @ Level 2"; + case 0xB3: return "Advanced Coding Efficiency Profile @ Level 3"; + case 0xB4: return "Advanced Coding Efficiency Profile @ Level 4"; + case 0xC1: return "Advanced Core Profile @ Level 1"; + case 0xC2: return "Advanced Core Profile @ Level 2"; + case 0xD1: return "Advanced Scalable Texture @ Level1"; + case 0xD2: return "Advanced Scalable Texture @ Level2"; + case 0xE1: return "Simple Studio Profile @ Level 1"; + case 0xE2: return "Simple Studio Profile @ Level 2"; + case 0xE3: return "Simple Studio Profile @ Level 3"; + case 0xE4: return "Simple Studio Profile @ Level 4"; + case 0xE5: return "Core Studio Profile @ Level 1"; + case 0xE6: return "Core Studio Profile @ Level 2"; + case 0xE7: return "Core Studio Profile @ Level 3"; + case 0xE8: return "Core Studio Profile @ Level 4"; + case 0xF0: return "Advanced Simple Profile @ Level 0"; + case 0xF1: return "Advanced Simple Profile @ Level 1"; + case 0xF2: return "Advanced Simple Profile @ Level 2"; + case 0xF3: return "Advanced Simple Profile @ Level 3"; + case 0xF4: return "Advanced Simple Profile @ Level 4"; + case 0xF5: return "Advanced Simple Profile @ Level 5"; + case 0xF7: return "Advanced Simple Profile @ Level 3b"; + case 0xF8: return "Fine Granularity Scalable Profile @ Level 0"; + case 0xF9: return "Fine Granularity Scalable Profile @ Level 1"; + case 0xFA: return "Fine Granularity Scalable Profile @ Level 2"; + case 0xFB: return "Fine Granularity Scalable Profile @ Level 3"; + case 0xFC: return "Fine Granularity Scalable Profile @ Level 4"; + case 0xFD: return "Fine Granularity Scalable Profile @ Level 5"; + case 0xFE: return "Not part of MPEG-4 Visual profiles"; + case 0xFF: return "No visual capability required"; + default: return "ISO Reserved Profile"; + } +} + +GF_EXPORT +void gf_m4v_rewrite_pl(char **o_data, u32 *o_dataLen, u8 PL) +{ + u32 pos = 0; + unsigned char *data = (unsigned char *)*o_data; + u32 dataLen = *o_dataLen; + + while (pos+4bs, start); + m4v->current_object_start = start; + m4v->current_object_type = 0; + return GF_OK; +} + + +static GF_Err gf_m4v_parse_config_mpeg12(GF_M4VParser *m4v, GF_M4VDecSpecInfo *dsi) +{ + char p[4]; + u32 ext_type; + s32 o_type; + u8 go, par; + + if (!m4v || !dsi) return GF_BAD_PARAM; + + memset(dsi, 0, sizeof(GF_M4VDecSpecInfo)); + dsi->VideoPL = 0; + + go = 1; + while (go) { + o_type = M4V_LoadObject(m4v); + switch (o_type) { + case M2V_SEQ_START_CODE: + dsi->RAP_stream = 1; + gf_bs_read_data(m4v->bs, p, 4); + dsi->width = (p[0] << 4) | ((p[1] >> 4) & 0xf); + dsi->height = ((p[1] & 0xf) << 8) | p[2]; + + dsi->VideoPL = 0x6A; + par = (p[3] >> 4) & 0xf; + switch (par) { + case 2: dsi->par_num = dsi->height/3; dsi->par_den = dsi->width/4; break; + case 3: dsi->par_num = dsi->height/9; dsi->par_den = dsi->width/16; break; + case 4: dsi->par_num = dsi->height/2; dsi->par_den = dsi->width/21; break; + default: dsi->par_den = dsi->par_num = 0; break; + } + switch (p[3] & 0xf) { + case 0: break; + case 1: dsi->fps = 24000.0/1001.0; break; + case 2: dsi->fps = 24.0; break; + case 3: dsi->fps = 25.0; break; + case 4: dsi->fps = 30000.0/1001.0; break; + case 5: dsi->fps = 30.0; break; + case 6: dsi->fps = 50.0; break; + case 7: dsi->fps = ((60.0*1000.0)/1001.0); break; + case 8: dsi->fps = 60.0; break; + case 9: dsi->fps = 1; break; + case 10: dsi->fps = 5; break; + case 11: dsi->fps = 10; break; + case 12: dsi->fps = 12; break; + case 13: dsi->fps = 15; break; + } + break; + case M2V_EXT_START_CODE: + gf_bs_read_data(m4v->bs, p, 4); + ext_type = ((p[0] >> 4) & 0xf); + if (ext_type == 1) { + dsi->VideoPL = 0x65; + dsi->height = ((p[1] & 0x1) << 13) | ((p[2] & 0x80) << 5) | (dsi->height & 0x0fff); + dsi->width = (((p[2] >> 5) & 0x3) << 12) | (dsi->width & 0x0fff); + } + break; + case M2V_PIC_START_CODE: + if (dsi->width) go = 0; + break; + default: + break; + /*EOS*/ + case -1: + go = 0; + m4v->current_object_start = (u32) gf_bs_get_position(m4v->bs); + break; + } + } + M4V_Reset(m4v, 0); + return GF_OK; +} + + +static const struct { u32 w, h; } m4v_sar[6] = { { 0, 0 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, { 40, 33 } }; + +static u8 m4v_get_sar_idx(u32 w, u32 h) +{ + u32 i; + for (i=0; i<6; i++) { + if ((m4v_sar[i].w==w) && (m4v_sar[i].h==h)) return i; + } + return 0xF; +} + +static GF_Err gf_m4v_parse_config_mpeg4(GF_M4VParser *m4v, GF_M4VDecSpecInfo *dsi) +{ + s32 o_type; + u8 go, verid, par; + s32 clock_rate; + + if (!m4v || !dsi) return GF_BAD_PARAM; + + memset(dsi, 0, sizeof(GF_M4VDecSpecInfo)); + + go = 1; + while (go) { + o_type = M4V_LoadObject(m4v); + switch (o_type) { + /*vosh*/ + case M4V_VOS_START_CODE: + dsi->VideoPL = (u8) gf_bs_read_u8(m4v->bs); + break; + + case M4V_VOL_START_CODE: + verid = 0; + dsi->RAP_stream = gf_bs_read_int(m4v->bs, 1); + dsi->objectType = gf_bs_read_int(m4v->bs, 8); + if (gf_bs_read_int(m4v->bs, 1)) { + verid = gf_bs_read_int(m4v->bs, 4); + gf_bs_read_int(m4v->bs, 3); + } + par = gf_bs_read_int(m4v->bs, 4); + if (par == 0xF) { + dsi->par_num = gf_bs_read_int(m4v->bs, 8); + dsi->par_den = gf_bs_read_int(m4v->bs, 8); + } else if (par<6) { + dsi->par_num = m4v_sar[par].w; + dsi->par_den = m4v_sar[par].h; + } + if (gf_bs_read_int(m4v->bs, 1)) { + gf_bs_read_int(m4v->bs, 3); + if (gf_bs_read_int(m4v->bs, 1)) gf_bs_read_int(m4v->bs, 79); + } + dsi->has_shape = gf_bs_read_int(m4v->bs, 2); + if (dsi->has_shape && (verid!=1) ) gf_bs_read_int(m4v->bs, 4); + gf_bs_read_int(m4v->bs, 1); + /*clock rate*/ + dsi->clock_rate = gf_bs_read_int(m4v->bs, 16); + /*marker*/ + gf_bs_read_int(m4v->bs, 1); + + clock_rate = dsi->clock_rate-1; + if (clock_rate >= 65536) clock_rate = 65535; + if (clock_rate > 0) { + for (dsi->NumBitsTimeIncrement = 1; dsi->NumBitsTimeIncrement < 16; dsi->NumBitsTimeIncrement++) { + if (clock_rate == 1) break; + clock_rate = (clock_rate >> 1); + } + } else { + /*fix from vivien for divX*/ + dsi->NumBitsTimeIncrement = 1; + } + /*fixed FPS stream*/ + dsi->time_increment = 0; + if (gf_bs_read_int(m4v->bs, 1)) { + dsi->time_increment = gf_bs_read_int(m4v->bs, dsi->NumBitsTimeIncrement); + } + if (!dsi->has_shape) { + gf_bs_read_int(m4v->bs, 1); + dsi->width = gf_bs_read_int(m4v->bs, 13); + gf_bs_read_int(m4v->bs, 1); + dsi->height = gf_bs_read_int(m4v->bs, 13); + } else { + dsi->width = dsi->height = 0; + } + /*shape will be done later*/ + gf_bs_align(m4v->bs); + break; + + case M4V_VOP_START_CODE: + case M4V_GOV_START_CODE: + go = 0; + break; + /*EOS*/ + case -1: + go = 0; + m4v->current_object_start = (u32) gf_bs_get_position(m4v->bs); + break; + /*don't interest us*/ + case M4V_UDTA_START_CODE: + default: + break; + } + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_m4v_parse_config(GF_M4VParser *m4v, GF_M4VDecSpecInfo *dsi) +{ + if (m4v->mpeg12) { + return gf_m4v_parse_config_mpeg12(m4v, dsi); + } else { + return gf_m4v_parse_config_mpeg4(m4v, dsi); + } +} + +static GF_Err gf_m4v_parse_frame_mpeg12(GF_M4VParser *m4v, GF_M4VDecSpecInfo dsi, u8 *frame_type, u32 *time_inc, u32 *size, u32 *start, Bool *is_coded) +{ + u8 go, hasVOP, firstObj, val; + s32 o_type; + + if (!m4v || !size || !start || !frame_type) return GF_BAD_PARAM; + + *size = 0; + firstObj = 1; + hasVOP = 0; + *is_coded = 0; + m4v->current_object_type = (u32) -1; + *frame_type = 0; + + M4V_Reset(m4v, m4v->current_object_start); + go = 1; + + while (go) { + o_type = M4V_LoadObject(m4v); + switch (o_type) { + case M2V_PIC_START_CODE: + /*done*/ + if (hasVOP) { + go = 0; + break; + } + if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + hasVOP = 1; + *is_coded = 1; + + val = gf_bs_read_u8(m4v->bs); + val = gf_bs_read_u8(m4v->bs); + *frame_type = ( (val >> 3) & 0x7 ) - 1; + break; + case M2V_GOP_START_CODE: + if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + if (hasVOP) go = 0; + break; + + case M2V_SEQ_START_CODE: + if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + if (hasVOP) { + go = 0; + break; + } + + /**/ + break; + + default: + break; + + case -1: + *size = (u32) gf_bs_get_position(m4v->bs) - *start; + return GF_EOS; + } + } + *size = m4v->current_object_start - *start; + return GF_OK; +} + +static GF_Err gf_m4v_parse_frame_mpeg4(GF_M4VParser *m4v, GF_M4VDecSpecInfo dsi, u8 *frame_type, u32 *time_inc, u32 *size, u32 *start, Bool *is_coded) +{ + u8 go, hasVOP, firstObj, secs; + s32 o_type; + u32 vop_inc = 0; + + if (!m4v || !size || !start || !frame_type) return GF_BAD_PARAM; + + *size = 0; + firstObj = 1; + hasVOP = 0; + *is_coded = 0; + m4v->current_object_type = (u32) -1; + *frame_type = 0; + + M4V_Reset(m4v, m4v->current_object_start); + go = 1; + while (go) { + o_type = M4V_LoadObject(m4v); + switch (o_type) { + case M4V_VOP_START_CODE: + /*done*/ + if (hasVOP) { + go = 0; + break; + } + if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + hasVOP = 1; + + /*coding type*/ + *frame_type = gf_bs_read_int(m4v->bs, 2); + /*modulo time base*/ + secs = 0; + while (gf_bs_read_int(m4v->bs, 1) != 0) + secs ++; + /*no support for B frames in parsing*/ + secs += (dsi.enh_layer || *frame_type!=2) ? m4v->tc_dec : m4v->tc_disp; + /*marker*/ + gf_bs_read_int(m4v->bs, 1); + /*vop_time_inc*/ + if (dsi.NumBitsTimeIncrement) + vop_inc = gf_bs_read_int(m4v->bs, dsi.NumBitsTimeIncrement); + + m4v->prev_tc_dec = m4v->tc_dec; + m4v->prev_tc_disp = m4v->tc_disp; + if (dsi.enh_layer || *frame_type!=2) { + m4v->tc_disp = m4v->tc_dec; + m4v->tc_dec = secs; + } + *time_inc = secs * dsi.clock_rate + vop_inc; + /*marker*/ + gf_bs_read_int(m4v->bs, 1); + /*coded*/ + *is_coded = gf_bs_read_int(m4v->bs, 1); + gf_bs_align(m4v->bs); + break; + case M4V_GOV_START_CODE: + if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + if (hasVOP) go = 0; + break; + + case M4V_VOS_START_CODE: + case M4V_VOL_START_CODE: + if (hasVOP) { + go = 0; + } else if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + break; + + case M4V_VO_START_CODE: + default: + break; + + case -1: + *size = (u32) gf_bs_get_position(m4v->bs) - *start; + return GF_EOS; + } + } + *size = m4v->current_object_start - *start; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_m4v_parse_frame(GF_M4VParser *m4v, GF_M4VDecSpecInfo dsi, u8 *frame_type, u32 *time_inc, u32 *size, u32 *start, Bool *is_coded) +{ + if (m4v->mpeg12) { + return gf_m4v_parse_frame_mpeg12(m4v, dsi, frame_type, time_inc, size, start, is_coded); + } else { + return gf_m4v_parse_frame_mpeg4(m4v, dsi, frame_type, time_inc, size, start, is_coded); + } +} + +GF_Err gf_m4v_rewrite_par(char **o_data, u32 *o_dataLen, s32 par_n, s32 par_d) +{ + u32 start, end, size; + GF_BitStream *mod; + GF_M4VParser *m4v; + Bool go = 1; + + m4v = gf_m4v_parser_new(*o_data, *o_dataLen, 0); + mod = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + end = start = 0; + while (go) { + u32 type = M4V_LoadObject(m4v); + + end = (u32) gf_bs_get_position(m4v->bs) - 4; + size = end - start; + /*store previous object*/ + if (size) { + if (size) gf_bs_write_data(mod, *o_data + start, size); + start = end; + } + + switch (type) { + case M4V_VOL_START_CODE: + gf_bs_write_int(mod, 0, 8); + gf_bs_write_int(mod, 0, 8); + gf_bs_write_int(mod, 1, 8); + gf_bs_write_int(mod, M4V_VOL_START_CODE, 8); + gf_bs_write_int(mod, gf_bs_read_int(m4v->bs, 1), 1); + gf_bs_write_int(mod, gf_bs_read_int(m4v->bs, 8), 8); + start = gf_bs_read_int(m4v->bs, 1); + gf_bs_write_int(mod, start, 1); + if (start) { + gf_bs_write_int(mod, gf_bs_read_int(m4v->bs, 7), 7); + } + start = gf_bs_read_int(m4v->bs, 4); + if (start == 0xF) { + gf_bs_read_int(m4v->bs, 8); + gf_bs_read_int(m4v->bs, 8); + } + if ((par_n>=0) && (par_d>=0)) { + u8 par = m4v_get_sar_idx(par_n, par_d); + gf_bs_write_int(mod, par, 4); + if (par==0xF) { + gf_bs_write_int(mod, par_n, 8); + gf_bs_write_int(mod, par_d, 8); + } + } else { + gf_bs_write_int(mod, 0x0, 4); + } + case -1: + go = 0; + break; + default: + break; + } + } + while (gf_bs_bits_available(m4v->bs)) { + u32 b = gf_bs_read_int(m4v->bs, 1); + gf_bs_write_int(mod, b, 1); + } + + gf_m4v_parser_del(m4v); + free(*o_data); + gf_bs_get_content(mod, o_data, o_dataLen); + gf_bs_del(mod); + return GF_OK; +} + +GF_EXPORT +u32 gf_m4v_get_object_start(GF_M4VParser *m4v) +{ + return m4v->current_object_start; +} + +GF_EXPORT +Bool gf_m4v_is_valid_object_type(GF_M4VParser *m4v) +{ + return ((s32) m4v->current_object_type==-1) ? 0 : 1; +} + + +GF_EXPORT +GF_Err gf_m4v_get_config(char *rawdsi, u32 rawdsi_size, GF_M4VDecSpecInfo *dsi) +{ + GF_Err e; + GF_M4VParser *vparse; + if (!rawdsi || !rawdsi_size) return GF_NON_COMPLIANT_BITSTREAM; + vparse = gf_m4v_parser_new(rawdsi, rawdsi_size, 0); + e = gf_m4v_parse_config(vparse, dsi); + gf_m4v_parser_del(vparse); + return e; +} + +/* + AAC parser +*/ + +GF_EXPORT +const char *gf_m4a_object_type_name(u32 objectType) +{ + switch (objectType) { + case 0: return "Reserved"; + case 1: return "AAC Main"; + case 2: return "AAC LC"; + case 3: return "AAC SSR"; + case 4: return "AAC LTP"; + case 5: return "SBR"; + case 6: return "AAC Scalable"; + case 7: return "TwinVQ"; + case 8: return "CELP"; + case 9: return "HVXC"; + case 10: return "Reserved"; + case 11: return "Reserved"; + case 12: return "TTSI"; + case 13: return "Main synthetic"; + case 14: return "Wavetable synthesis"; + case 15: return "General MIDI"; + case 16: return "Algorithmic Synthesis and Audio FX"; + case 17: return "ER AAC LC"; + case 18: return "Reserved"; + case 19: return "ER AAC LTP"; + case 20: return "ER AAC scalable"; + case 21: return "ER TwinVQ"; + case 22: return "ER BSAC"; + case 23: return "ER AAC LD"; + case 24: return "ER CELP"; + case 25: return "ER HVXC"; + case 26: return "ER HILN"; + case 27: return "ER Parametric"; + case 28: return "SSC"; + case 29: return "ParametricStereo"; + case 30: return "(Reserved)"; + case 31: return "(Reserved)"; + case 32: return "Layer-1"; + case 33: return "Layer-2"; + case 34: return "Layer-3"; + case 35: return "DST"; + case 36: return "ALS"; + default: return "Unknown"; + } +} + +GF_EXPORT +const char *gf_m4a_get_profile_name(u8 audio_pl) +{ + switch (audio_pl) { + case 0x00: return "ISO Reserved (0x00)"; + case 0x01: return "Main Audio Profile @ Level 1"; + case 0x02: return "Main Audio Profile @ Level 2"; + case 0x03: return "Main Audio Profile @ Level 3"; + case 0x04: return "Main Audio Profile @ Level 4"; + case 0x05: return "Scalable Audio Profile @ Level 1"; + case 0x06: return "Scalable Audio Profile @ Level 2"; + case 0x07: return "Scalable Audio Profile @ Level 3"; + case 0x08: return "Scalable Audio Profile @ Level 4"; + case 0x09: return "Speech Audio Profile @ Level 1"; + case 0x0A: return "Speech Audio Profile @ Level 2"; + case 0x0B: return "Synthetic Audio Profile @ Level 1"; + case 0x0C: return "Synthetic Audio Profile @ Level 2"; + case 0x0D: return "Synthetic Audio Profile @ Level 3"; + case 0x0E: return "High Quality Audio Profile @ Level 1"; + case 0x0F: return "High Quality Audio Profile @ Level 2"; + case 0x10: return "High Quality Audio Profile @ Level 3"; + case 0x11: return "High Quality Audio Profile @ Level 4"; + case 0x12: return "High Quality Audio Profile @ Level 5"; + case 0x13: return "High Quality Audio Profile @ Level 6"; + case 0x14: return "High Quality Audio Profile @ Level 7"; + case 0x15: return "High Quality Audio Profile @ Level 8"; + case 0x16: return "Low Delay Audio Profile @ Level 1"; + case 0x17: return "Low Delay Audio Profile @ Level 2"; + case 0x18: return "Low Delay Audio Profile @ Level 3"; + case 0x19: return "Low Delay Audio Profile @ Level 4"; + case 0x1A: return "Low Delay Audio Profile @ Level 5"; + case 0x1B: return "Low Delay Audio Profile @ Level 6"; + case 0x1C: return "Low Delay Audio Profile @ Level 7"; + case 0x1D: return "Low Delay Audio Profile @ Level 8"; + case 0x1E: return "Natural Audio Profile @ Level 1"; + case 0x1F: return "Natural Audio Profile @ Level 2"; + case 0x20: return "Natural Audio Profile @ Level 3"; + case 0x21: return "Natural Audio Profile @ Level 4"; + case 0x22: return "Mobile Audio Internetworking Profile @ Level 1"; + case 0x23: return "Mobile Audio Internetworking Profile @ Level 2"; + case 0x24: return "Mobile Audio Internetworking Profile @ Level 3"; + case 0x25: return "Mobile Audio Internetworking Profile @ Level 4"; + case 0x26: return "Mobile Audio Internetworking Profile @ Level 5"; + case 0x27: return "Mobile Audio Internetworking Profile @ Level 6"; + case 0x28: return "AAC Profile @ Level 1"; + case 0x29: return "AAC Profile @ Level 2"; + case 0x2A: return "AAC Profile @ Level 4"; + case 0x2B: return "AAC Profile @ Level 5"; + case 0x2C: return "High Efficiency AAC Profile @ Level 2"; + case 0x2D: return "High Efficiency AAC Profile @ Level 3"; + case 0x2E: return "High Efficiency AAC Profile @ Level 4"; + case 0x2F: return "High Efficiency AAC Profile @ Level 5"; + case 0xFE: return "Not part of MPEG-4 audio profiles"; + case 0xFF: return "No audio capability required"; + default: return "ISO Reserved / User Private"; + } +} + +u32 gf_m4a_get_profile(GF_M4ADecSpecInfo *cfg) +{ + switch (cfg->base_object_type) { + case 2: /*AAC LC*/ + if (cfg->nb_chan<=2) return (cfg->base_sr<=24000) ? 0x28 : 0x29; /*LC@L1 or LC@L2*/ + return (cfg->base_sr<=48000) ? 0x2A : 0x2B; /*LC@L4 or LC@L5*/ + case 5: /*HE-AAC*/ + if (cfg->nb_chan<=2) return (cfg->base_sr<=24000) ? 0x2C : 0x2D; /*HE@L2 or HE@L3*/ + return (cfg->base_sr<=48000) ? 0x2E : 0x2F; /*HE@L4 or HE@L5*/ + /*default to HQ*/ + default: + if (cfg->nb_chan<=2) return (cfg->base_sr<24000) ? 0x0E : 0x0F; /*HQ@L1 or HQ@L2*/ + return 0x10; /*HQ@L3*/ + } +} + + + +GF_EXPORT +GF_Err gf_m4a_parse_config(GF_BitStream *bs, GF_M4ADecSpecInfo *cfg, Bool size_known) +{ + memset(cfg, 0, sizeof(GF_M4ADecSpecInfo)); + cfg->base_object_type = gf_bs_read_int(bs, 5); + /*extended object type*/ + if (cfg->base_object_type==31) { + cfg->base_object_type = 32 + gf_bs_read_int(bs, 6); + } + cfg->base_sr_index = gf_bs_read_int(bs, 4); + if (cfg->base_sr_index == 0x0F) { + cfg->base_sr = gf_bs_read_int(bs, 24); + } else { + cfg->base_sr = GF_M4ASampleRates[cfg->base_sr_index]; + } + cfg->nb_chan = gf_bs_read_int(bs, 4); + /*this is 7+1 channels*/ + if (cfg->nb_chan==7) cfg->nb_chan=8; + + if (cfg->base_object_type==5) { + cfg->has_sbr = 1; + cfg->sbr_sr_index = gf_bs_read_int(bs, 4); + if (cfg->sbr_sr_index == 0x0F) { + cfg->sbr_sr = gf_bs_read_int(bs, 24); + } else { + cfg->sbr_sr = GF_M4ASampleRates[cfg->sbr_sr_index]; + } + cfg->sbr_object_type = gf_bs_read_int(bs, 5); + } + + /*object cfg*/ + switch (cfg->base_object_type) { + case 1: + case 2: + case 3: + case 4: + case 6: + case 7: + case 17: + case 19: + case 20: + case 21: + case 22: + case 23: + { + Bool fl_flag, ext_flag; + u32 delay; + /*frame length flag*/ + fl_flag = gf_bs_read_int(bs, 1); + /*depends on core coder*/ + delay = 0; + if (gf_bs_read_int(bs, 1)) + delay = gf_bs_read_int(bs, 14); + ext_flag = gf_bs_read_int(bs, 1); + if (!cfg->nb_chan) { + } + if ((cfg->base_object_type == 6) || (cfg->base_object_type == 20)) { + gf_bs_read_int(bs, 3); + } + if (ext_flag) { + if (cfg->base_object_type == 22) { + gf_bs_read_int(bs, 5); + gf_bs_read_int(bs, 11); + } + if ((cfg->base_object_type == 17) + || (cfg->base_object_type == 19) + || (cfg->base_object_type == 20) + || (cfg->base_object_type == 23) + ) { + gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 1); + } + ext_flag = gf_bs_read_int(bs, 1); + } + } + break; + } + /*ER cfg*/ + switch (cfg->base_object_type) { + case 17: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + { + u32 epConfig = gf_bs_read_int(bs, 2); + if ((epConfig == 2) || (epConfig == 3) ) { + } + if (epConfig == 3) { + gf_bs_read_int(bs, 1); + } + } + break; + } + + if (size_known && (cfg->base_object_type != 5) && gf_bs_available(bs)>=2) { + u32 sync = gf_bs_peek_bits(bs, 11, 0); + if (sync==0x2b7) { + gf_bs_read_int(bs, 11); + cfg->sbr_object_type = gf_bs_read_int(bs, 5); + cfg->has_sbr = gf_bs_read_int(bs, 1); + if (cfg->has_sbr) { + cfg->sbr_sr_index = gf_bs_read_int(bs, 4); + if (cfg->sbr_sr_index == 0x0F) { + cfg->sbr_sr = gf_bs_read_int(bs, 24); + } else { + cfg->sbr_sr = GF_M4ASampleRates[cfg->sbr_sr_index]; + } + } + } + } + cfg->audioPL = gf_m4a_get_profile(cfg); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_m4a_get_config(char *dsi, u32 dsi_size, GF_M4ADecSpecInfo *cfg) +{ + GF_BitStream *bs; + if (!dsi || !dsi_size || (dsi_size<2) ) return GF_NON_COMPLIANT_BITSTREAM; + bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); + gf_m4a_parse_config(bs, cfg, 1); + gf_bs_del(bs); + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_m4a_write_config(GF_M4ADecSpecInfo *cfg, char **dsi, u32 *dsi_size) +{ + GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + /*extended object type*/ + if (cfg->base_object_type>=32) { + gf_bs_write_int(bs, 31, 5); + gf_bs_write_int(bs, cfg->base_object_type-32, 6); + } else { + gf_bs_write_int(bs, cfg->base_object_type, 5); + } + gf_bs_write_int(bs, cfg->base_sr_index, 4); + if (cfg->base_sr_index == 0x0F) { + gf_bs_write_int(bs, cfg->base_sr, 24); + } + if (cfg->nb_chan == 8) { + gf_bs_write_int(bs, 7, 4); + } else { + gf_bs_write_int(bs, cfg->nb_chan, 4); + } + + if (cfg->base_object_type==5) { + cfg->has_sbr = 1; + gf_bs_write_int(bs, cfg->sbr_sr_index, 4); + if (cfg->sbr_sr_index == 0x0F) { + gf_bs_write_int(bs, cfg->sbr_sr, 24); + } + gf_bs_write_int(bs, cfg->sbr_object_type, 5); + } + + /*object cfg*/ + switch (cfg->base_object_type) { + case 1: + case 2: + case 3: + case 4: + case 6: + case 7: + case 17: + case 19: + case 20: + case 21: + case 22: + case 23: + { + /*frame length flag*/ + gf_bs_write_int(bs, 0, 1); + /*depends on core coder*/ + gf_bs_write_int(bs, 0, 1); + /*ext flag*/ + gf_bs_write_int(bs, 0, 1); + if ((cfg->base_object_type == 6) || (cfg->base_object_type == 20)) { + gf_bs_write_int(bs, 0, 3); + } + } + break; + } + /*ER cfg - not supported*/ + + /*implicit sbr - not used yet*/ + if (0 && (cfg->base_object_type != 5) ) { + gf_bs_write_int(bs, 0x2b7, 11); + cfg->sbr_object_type = gf_bs_read_int(bs, 5); + cfg->has_sbr = gf_bs_read_int(bs, 1); + if (cfg->has_sbr) { + cfg->sbr_sr_index = gf_bs_read_int(bs, 4); + if (cfg->sbr_sr_index == 0x0F) { + cfg->sbr_sr = gf_bs_read_int(bs, 24); + } else { + cfg->sbr_sr = GF_M4ASampleRates[cfg->sbr_sr_index]; + } + } + } + gf_bs_get_content(bs, dsi, dsi_size); + gf_bs_del(bs); + return GF_OK; +} + + +/* + MP3 parser + credits go to CISCO - MPEG4IP +*/ +GF_EXPORT +u8 gf_mp3_version(u32 hdr) +{ + return ((hdr >> 19) & 0x3); +} + +static u8 MP3_GetLayerV(u32 hdr) +{ + return ((hdr >> 17) & 0x3); +} + +GF_EXPORT +u8 gf_mp3_layer(u32 hdr) +{ + return 4 - (((hdr >> 17) & 0x3)); +} + +GF_EXPORT +u8 gf_mp3_num_channels(u32 hdr) +{ + if (((hdr >> 6) & 0x3) == 3) return 1; + return 2; +} + + +static GFINLINE u32 get_MP3SamplingRates(u32 version, u32 idx) +{ + switch (version) { + case 0: + switch (idx) { + case 0: return 11025; + case 1: return 12000; + case 2: return 8000; + default: return 0; + } + break; + case 1: + return 0; + case 2: + switch (idx) { + case 0: return 22050; + case 1: return 24000; + case 2: return 16000; + default: return 0; + } + break; + case 3: + switch (idx) { + case 0: return 44100; + case 1: return 48000; + case 2: return 32000; + default: return 0; + } + break; + } + return 0; +} + +GF_EXPORT +u16 gf_mp3_sampling_rate(u32 hdr) +{ + /* extract the necessary fields from the MP3 header */ + u8 version = gf_mp3_version(hdr); + u8 sampleRateIndex = (hdr >> 10) & 0x3; + return get_MP3SamplingRates(version, sampleRateIndex); +} + +GF_EXPORT +u16 gf_mp3_window_size(u32 hdr) +{ + u8 version = gf_mp3_version(hdr); + u8 layer = MP3_GetLayerV(hdr); + + if (layer == 1) { + if (version == 3) return 1152; + return 576; + } + if (layer == 2) return 1152; + return 384; +} + +GF_EXPORT +u8 gf_mp3_object_type_indication(u32 hdr) +{ + switch (gf_mp3_version(hdr)) { + case 3: + return 0x6B; + break; + case 2: + case 0: + return 0x69; + default: + return 0x00; + } +} + +GF_EXPORT +const char *gf_mp3_version_name(u32 hdr) +{ + u32 v = gf_mp3_version(hdr); + switch (v) { + case 0: return "MPEG-2.5"; + case 1: return "Reserved"; + case 2: return "MPEG-2"; + case 3: return "MPEG-1"; + default: return "Unknown"; + } +} + +static GFINLINE u32 get_MP3BitRates(u32 idx1, u32 idx2) +{ + switch (idx1) { + /* MPEG-1, Layer III */ + case 0: + switch (idx2) { + case 0: return 32; + case 1: return 40; + case 2: return 48; + case 3: return 56; + case 4: return 64; + case 5: return 80; + case 6: return 96; + case 7: return 112; + case 8: return 128; + case 9: return 160; + case 10: return 192; + case 11: return 224; + case 12: return 256; + case 13: return 320; + default: return 0; + } + break; + /* MPEG-1, Layer II */ + case 1: + switch (idx2) { + case 0: return 32; + case 1: return 48; + case 2: return 56; + case 3: return 64; + case 4: return 80; + case 5: return 96; + case 6: return 112; + case 7: return 128; + case 8: return 160; + case 9: return 192; + case 10: return 224; + case 11: return 256; + case 12: return 320; + case 13: return 384; + default: return 0; + } + break; + /* MPEG-1, Layer I */ + case 2: + switch (idx2) { + case 0: return 32; + case 1: return 64; + case 2: return 96; + case 3: return 128; + case 4: return 160; + case 5: return 192; + case 6: return 224; + case 7: return 256; + case 8: return 288; + case 9: return 320; + case 10: return 352; + case 11: return 384; + case 12: return 416; + case 13: return 448; + default: return 0; + } + break; + /* MPEG-2 or 2.5, Layer II or III */ + case 3: + switch (idx2) { + case 0: return 8; + case 1: return 16; + case 2: return 24; + case 3: return 32; + case 4: return 40; + case 5: return 48; + case 6: return 56; + case 7: return 64; + case 8: return 80; + case 9: return 96; + case 10: return 112; + case 11: return 128; + case 12: return 144; + case 13: return 160; + default: return 0; + } + break; + /* MPEG-2 or 2.5, Layer I */ + case 4: + switch (idx2) { + case 0: return 32; + case 1: return 48; + case 2: return 56; + case 3: return 64; + case 4: return 80; + case 5: return 96; + case 6: return 112; + case 7: return 128; + case 8: return 144; + case 9: return 160; + case 10: return 176; + case 11: return 192; + case 12: return 224; + case 13: return 256; + default: return 0; + } + break; + default: + return 0; + } +} + + +GF_EXPORT +u16 gf_mp3_frame_size(u32 hdr) +{ + u32 val; + u8 bitRateIndex1; + u8 version = gf_mp3_version(hdr); + u8 layer = MP3_GetLayerV(hdr); + u8 bitRateIndex2 = (hdr >> 12) & 0xF; + u8 sampleRateIndex = (hdr >> 10) & 0x3; + Bool isPadded = (hdr >> 9) & 0x1; + u32 frameSize = 0; + + if (version == 3) { + bitRateIndex1 = layer - 1; + } else { + if (layer == 3) { + bitRateIndex1 = 4; + } else { + bitRateIndex1 = 3; + } + } + + /* compute frame size */ + val = get_MP3SamplingRates(version, sampleRateIndex); + if (!(version & 1)) val <<= 1; + if (!val) return 0; + frameSize = 144 * 1000 * get_MP3BitRates(bitRateIndex1, bitRateIndex2-1); + frameSize /= val; + + if (isPadded) { + if (layer == 3) { + frameSize += 4; + } else { + frameSize++; + } + } + return (u16) frameSize; +} + + +u16 gf_mp3_bit_rate(u32 hdr) +{ + u8 bitRateIndex1; + u8 version = gf_mp3_version(hdr); + u8 layer = MP3_GetLayerV(hdr); + u8 bitRateIndex2 = (hdr >> 12) & 0xF; + if (version == 3) { + bitRateIndex1 = layer - 1; + } else { + if (layer == 3) { + bitRateIndex1 = 4; + } else { + bitRateIndex1 = 3; + } + } + + /* compute frame size */ + return get_MP3BitRates(bitRateIndex1, bitRateIndex2-1); +} + + +GF_EXPORT +u32 gf_mp3_get_next_header(FILE* in) +{ + u8 b, state = 0; + u32 dropped = 0; + unsigned char bytes[4]; + bytes[0] = bytes[1] = bytes[2] = bytes[3] = 0; + + while (1) { + if (fread(&b, 1, 1, in) == 0) return 0; + + if (state==3) { + bytes[state] = b; + return GF_4CC(bytes[0], bytes[1], bytes[2], bytes[3]); + } + if (state==2) { + if (((b & 0xF0) == 0) || ((b & 0xF0) == 0xF0) || ((b & 0x0C) == 0x0C)) { + if (bytes[1] == 0xFF) state = 1; + else state = 0; + } else { + bytes[state] = b; + state = 3; + } + } + if (state==1) { + if (((b & 0xE0) == 0xE0) && ((b & 0x18) != 0x08) && ((b & 0x06) != 0)) { + bytes[state] = b; + state = 2; + } else { + state = 0; + } + } + + if (state==0) { + if (b == 0xFF) { + bytes[state] = b; + state = 1; + } else { + if ((dropped == 0) && ((b & 0xE0) == 0xE0) && ((b & 0x18) != 0x08) && ((b & 0x06) != 0)) { + bytes[0] = (u8) 0xFF; + bytes[1] = b; + state = 2; + } else { + dropped++; + } + } + } + } + return 0; +} + +GF_EXPORT +u32 gf_mp3_get_next_header_mem(char *buffer, u32 size, u32 *pos) +{ + u32 cur; + u8 b, state = 0; + u32 dropped = 0; + unsigned char bytes[4]; + bytes[0] = bytes[1] = bytes[2] = bytes[3] = 0; + + cur = 0; + *pos = 0; + while (cur>1)); + return (v + 1) >> 1; +} + +u32 AVC_IsStartCode(GF_BitStream *bs) +{ + u8 s1, s2, s3, s4; + Bool is_sc = 0; + u64 pos = gf_bs_get_position(bs); + s1 = gf_bs_read_int(bs, 8); + s2 = gf_bs_read_int(bs, 8); + if (!s1 && !s2) { + s3 = gf_bs_read_int(bs, 8); + if (s3==0x01) is_sc = 3; + else if (!s3) { + s4 = gf_bs_read_int(bs, 8); + if (s4==0x01) is_sc = 4; + } + } + gf_bs_seek(bs, pos+is_sc); + return is_sc; +} + +/*read that amount of data at each IO access rather than fetching byte by byte...*/ +#define AVC_CACHE_SIZE 4096 +u32 AVC_NextStartCode(GF_BitStream *bs) +{ + u32 v, bpos; + char avc_cache[AVC_CACHE_SIZE]; + u64 end, cache_start, load_size; + u64 start = gf_bs_get_position(bs); + if (start<3) return 0; + + load_size = 0; + bpos = 0; + cache_start = 0; + end = 0; + v = 0xffffffff; + while (!end) { + /*refill cache*/ + if (bpos == (u32) load_size) { + if (!gf_bs_available(bs)) break; + load_size = gf_bs_available(bs); + if (load_size>AVC_CACHE_SIZE) load_size=AVC_CACHE_SIZE; + bpos = 0; + cache_start = gf_bs_get_position(bs); + gf_bs_read_data(bs, avc_cache, (u32) load_size); + } + v = ( (v<<8) & 0xFFFFFF00) | ((u32) avc_cache[bpos]); + bpos++; + if (v == 0x00000001) end = cache_start+bpos-4; + else if ( (v & 0x00FFFFFF) == 0x00000001) end = cache_start+bpos-3; + } + gf_bs_seek(bs, start); + if (!end) end = gf_bs_get_size(bs); + return (u32) (end-start); +} + + +Bool AVC_NALUIsSlice(u8 type) +{ + return ((type >= GF_AVC_NALU_NON_IDR_SLICE) && (type <= GF_AVC_NALU_IDR_SLICE)) ? 1 : 0; +} + +Bool AVC_SliceIsIDR(AVCState *avc) +{ + if (avc->sei.recovery_point.valid) + { + avc->sei.recovery_point.valid = 0; + return 1; + } + if (avc->s_info.nal_unit_type != GF_AVC_NALU_IDR_SLICE) return 0; + switch (avc->s_info.slice_type) { + case GF_AVC_TYPE_I: + case GF_AVC_TYPE2_I: + case GF_AVC_TYPE_SI: + case GF_AVC_TYPE2_SI: + return 1; + default: + return 0; + } +} + +static const struct { u32 w, h; } avc_sar[14] = +{ + { 0, 0 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, + { 16, 11 }, { 40, 33 }, { 24, 11 }, { 20, 11 }, + { 32, 11 }, { 80, 33 }, { 18, 11 }, { 15, 11 }, + { 64, 33 }, { 160,99 }, +}; + +s32 AVC_ReadSeqInfo(GF_BitStream *bs, AVCState *avc, u32 *vui_flag_pos) +{ + AVC_SPS *sps; + s32 mb_width, mb_height; + u32 sps_id, profile_idc, level_idc, pcomp, i, chroma_format_idc, cl, cr, ct, cb; + + if (vui_flag_pos) *vui_flag_pos = 0; + + profile_idc = gf_bs_read_int(bs, 8); + pcomp = gf_bs_read_int(bs, 8); + level_idc = gf_bs_read_int(bs, 8); + sps_id = avc_get_ue(bs); + + sps = &avc->sps[sps_id]; + if (!sps->status) sps->status = 1; + + /*High Profile cfg stuff*/ + switch (profile_idc) { + case 100: + case 110: + case 122: + case 144: + chroma_format_idc = avc_get_ue(bs); + if (chroma_format_idc == 3) /*residual_colour_transform_flag = */ gf_bs_read_int(bs, 1); + /*bit_depth_luma_minus8 = */ avc_get_ue(bs); + /*bit_depth_chroma_minus8 = */ avc_get_ue(bs); + /*qpprime_y_zero_transform_bypass_flag = */ gf_bs_read_int(bs, 1); + /*seq_scaling_matrix_present_flag*/ + if (gf_bs_read_int(bs, 1)) { + u32 k; + for (k=0; k<8; k++) { + if (gf_bs_read_int(bs, 1)) { + u32 z, last = 8, next = 8; + u32 sl = k<6 ? 16 : 64; + for (z=0; zprofile_idc = profile_idc; + sps->level_idc = level_idc; + sps->prof_compat = pcomp; + sps->log2_max_frame_num = avc_get_ue(bs) + 4; + sps->poc_type = avc_get_ue(bs); + + if (sps->poc_type == 0) { + sps->log2_max_poc_lsb = avc_get_ue(bs) + 4; + } else if(sps->poc_type == 1) { + sps->delta_pic_order_always_zero_flag = gf_bs_read_int(bs, 1); + sps->offset_for_non_ref_pic = avc_get_se(bs); + sps->offset_for_top_to_bottom_field = avc_get_se(bs); + sps->poc_cycle_length = avc_get_ue(bs); + for(i=0; ipoc_cycle_length; i++) sps->offset_for_ref_frame[i] = avc_get_se(bs); + } + if (sps->poc_type > 2) return -1; + avc_get_ue(bs); /*ref_frame_count*/ + gf_bs_read_int(bs, 1); /*gaps_in_frame_num_allowed_flag*/ + mb_width = avc_get_ue(bs) + 1; + mb_height= avc_get_ue(bs) + 1; + + sps->frame_mbs_only_flag = gf_bs_read_int(bs, 1); + + sps->width = mb_width * 16; + sps->height = (2-sps->frame_mbs_only_flag) * mb_height * 16; + + /*mb_adaptive_frame_field_flag*/ + if (!sps->frame_mbs_only_flag) gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 1); /*direct_8x8_inference_flag*/ + cl = cr = ct = cb = 0; + if (gf_bs_read_int(bs, 1)) /*crop*/ { + cl = avc_get_ue(bs); /*crop_left*/ + cr = avc_get_ue(bs); /*crop_right*/ + ct = avc_get_ue(bs); /*crop_top*/ + cb = avc_get_ue(bs); /*crop_bottom*/ + + sps->width = 16*mb_width - 2*(cl + cr); + sps->height -= (2-sps->frame_mbs_only_flag)*2*(ct + cb); + } + + if (vui_flag_pos) { + *vui_flag_pos = (u32) gf_bs_get_bit_offset(bs); + } + + /*vui_parameters_present_flag*/ + if (gf_bs_read_int(bs, 1)) { + /*aspect_ratio_info_present_flag*/ + if (gf_bs_read_int(bs, 1)) { + s32 aspect_ratio_idc = gf_bs_read_int(bs, 8); + if (aspect_ratio_idc == 255) { + sps->par_num = gf_bs_read_int(bs, 16); /*AR num*/ + sps->par_den = gf_bs_read_int(bs, 16); /*AR den*/ + } else if (aspect_ratio_idc<14) { + sps->par_num = avc_sar[aspect_ratio_idc].w; + sps->par_den = avc_sar[aspect_ratio_idc].h; + } + } + if(gf_bs_read_int(bs, 1)) /* overscan_info_present_flag */ + gf_bs_read_int(bs, 1); /* overscan_appropriate_flag */ + + if (gf_bs_read_int(bs, 1)){ /* video_signal_type_present_flag */ + gf_bs_read_int(bs, 3); /* video_format */ + gf_bs_read_int(bs, 1); /* video_full_range_flag */ + if (gf_bs_read_int(bs, 1)){ /* colour_description_present_flag */ + gf_bs_read_int(bs, 8); /* colour_primaries */ + gf_bs_read_int(bs, 8); /* transfer_characteristics */ + gf_bs_read_int(bs, 8); /* matrix_coefficients */ + } + } + + if (gf_bs_read_int(bs, 1)) { /* chroma_location_info_present_flag */ + avc_get_ue(bs); /* chroma_sample_location_type_top_field */ + avc_get_ue(bs); /* chroma_sample_location_type_bottom_field */ + } + + sps->timing_info_present_flag = gf_bs_read_int(bs, 1); + if (sps->timing_info_present_flag) { + sps->num_units_in_tick = gf_bs_read_int(bs, 32); + sps->time_scale = gf_bs_read_int(bs, 32); + sps->fixed_frame_rate_flag = gf_bs_read_int(bs, 1); + } + } + return sps_id; +} + +s32 AVC_ReadPictParamSet(GF_BitStream *bs, AVCState *avc) +{ + s32 pps_id = avc_get_ue(bs); + AVC_PPS *pps= &avc->pps[pps_id]; + + if (!pps->status) pps->status = 1; + pps->sps_id= avc_get_ue(bs); + /*pps->cabac = */gf_bs_read_int(bs, 1); + /*pps->pic_order_present= */gf_bs_read_int(bs, 1); + pps->slice_group_count= avc_get_ue(bs) + 1; + if (pps->slice_group_count > 1 ) /*pps->mb_slice_group_map_type = */avc_get_ue(bs); + /*pps->ref_count[0]= */avc_get_ue(bs) /*+ 1*/; + /*pps->ref_count[1]= */avc_get_ue(bs) /*+ 1*/; + /* + if ((pps->ref_count[0] > 32) || (pps->ref_count[1] > 32)) return -1; + */ + + /*pps->weighted_pred = */gf_bs_read_int(bs, 1); + /*pps->weighted_bipred_idc = */gf_bs_read_int(bs, 2); + /*pps->init_qp = */avc_get_se(bs) /*+ 26*/; + /*pps->init_qs= */avc_get_se(bs) /*+ 26*/; + /*pps->chroma_qp_index_offset = */avc_get_se(bs); + /*pps->deblocking_filter_parameters_present = */gf_bs_read_int(bs, 1); + /*pps->constrained_intra_pred = */gf_bs_read_int(bs, 1); + pps->redundant_pic_cnt_present = gf_bs_read_int(bs, 1); + return pps_id; +} + +static s32 avc_parse_slice(GF_BitStream *bs, AVCState *avc, AVCSliceInfo *si) +{ + s32 first_mb_in_slice, pps_id; + + /*s->current_picture.reference= h->nal_ref_idc != 0;*/ + first_mb_in_slice = avc_get_ue(bs); + si->slice_type = avc_get_ue(bs); + if (si->slice_type > 9) return -1; + + pps_id = avc_get_ue(bs); + if (pps_id>255) return -1; + si->pps = &avc->pps[pps_id]; + if (!si->pps->slice_group_count) return -2; + si->sps = &avc->sps[si->pps->sps_id]; + if (!si->sps->log2_max_frame_num) return -2; + + si->frame_num = gf_bs_read_int(bs, si->sps->log2_max_frame_num); + + si->field_pic_flag = 0; + si->bottom_field_flag = 0; + if (si->sps->frame_mbs_only_flag) { + /*s->picture_structure= PICT_FRAME;*/ + } else { + si->field_pic_flag = gf_bs_read_int(bs, 1); + if (si->field_pic_flag) + si->bottom_field_flag = gf_bs_read_int(bs, 1); + } + if (si->nal_unit_type==GF_AVC_NALU_IDR_SLICE) + si->idr_pic_id = avc_get_ue(bs); + + if (si->sps->poc_type==0) { + si->poc_lsb = gf_bs_read_int(bs, si->sps->log2_max_poc_lsb); + if (si->pps->pic_order_present && !si->field_pic_flag) { + si->delta_poc_bottom = avc_get_se(bs); + } + } else if ((si->sps->poc_type==1) && !si->sps->delta_pic_order_always_zero_flag) { + si->delta_poc[0] = avc_get_se(bs); + if ((si->pps->pic_order_present==1) && !si->field_pic_flag) + si->delta_poc[1] = avc_get_se(bs); + } + if (si->pps->redundant_pic_cnt_present) { + si->redundant_pic_cnt = avc_get_ue(bs); + } + return 0; +} + +static s32 avc_parse_recovery_point_sei(GF_BitStream *bs, AVCState *avc) +{ + AVCSeiRecoveryPoint *rp = &avc->sei.recovery_point; + + rp->frame_cnt = avc_get_ue(bs); + rp->exact_match_flag = gf_bs_read_int(bs, 1); + rp->broken_link_flag = gf_bs_read_int(bs, 1); + rp->changing_slice_group_idc = gf_bs_read_int(bs, 2); + rp->valid = 1; + + return 0; +} + + +static void avc_compute_poc(AVCSliceInfo *si) +{ + enum { + AVC_PIC_FRAME, + AVC_PIC_FIELD_TOP, + AVC_PIC_FIELD_BOTTOM, + } pic_type; + s32 field_poc[2] = {0,0}; + s32 max_frame_num = 1 << (si->sps->log2_max_frame_num); + + /* picture type */ + if (si->sps->frame_mbs_only_flag || !si->field_pic_flag) pic_type = AVC_PIC_FRAME; + else if (si->bottom_field_flag) pic_type = AVC_PIC_FIELD_BOTTOM; + else pic_type = AVC_PIC_FIELD_TOP; + + /* frame_num_offset */ + if (si->nal_unit_type == GF_AVC_NALU_IDR_SLICE) { + si->poc_lsb_prev = 0; + si->poc_msb_prev = 0; + si->frame_num_offset = 0; + } else { + if (si->frame_num < si->frame_num_prev) + si->frame_num_offset = si->frame_num_offset_prev + max_frame_num; + else + si->frame_num_offset = si->frame_num_offset_prev; + } + + if (si->sps->poc_type==0) { + const u32 max_poc_lsb = 1 << (si->sps->log2_max_poc_lsb); + + if ((si->poc_lsb < si->poc_lsb_prev) && + (si->poc_lsb_prev - si->poc_lsb >= max_poc_lsb / 2) ) + si->poc_msb = si->poc_msb_prev + max_poc_lsb; + else if ((si->poc_lsb > si->poc_lsb_prev) && + (si->poc_lsb - si->poc_lsb_prev > max_poc_lsb / 2)) + si->poc_msb = si->poc_msb_prev - max_poc_lsb; + else + si->poc_msb = si->poc_msb_prev; + + field_poc[0] = field_poc[1] = si->poc_msb + si->poc_lsb; + if (pic_type == AVC_PIC_FRAME) field_poc[1] += si->delta_poc_bottom; + } else if (si->sps->poc_type==1) { + u32 i; + s32 abs_frame_num, expected_delta_per_poc_cycle, expected_poc; + + if (si->sps->poc_cycle_length) + abs_frame_num = si->frame_num_offset + si->frame_num; + else + abs_frame_num = 0; + + if (!si->nal_ref_idc && (abs_frame_num > 0)) abs_frame_num--; + + expected_delta_per_poc_cycle = 0; + for (i=0; i < si->sps->poc_cycle_length; i++) + expected_delta_per_poc_cycle += si->sps->offset_for_ref_frame[i]; + + if (abs_frame_num > 0) { + const u32 poc_cycle_cnt = ( abs_frame_num - 1 ) / si->sps->poc_cycle_length; + const u32 frame_num_in_poc_cycle = ( abs_frame_num - 1 ) % si->sps->poc_cycle_length; + + expected_poc = poc_cycle_cnt * expected_delta_per_poc_cycle; + for (i = 0; i<=frame_num_in_poc_cycle; i++) + expected_poc += si->sps->offset_for_ref_frame[i]; + } else { + expected_poc = 0; + } + + if (!si->nal_ref_idc) expected_poc += si->sps->offset_for_non_ref_pic; + + field_poc[0] = expected_poc + si->delta_poc[0]; + field_poc[1] = field_poc[0] + si->sps->offset_for_top_to_bottom_field; + if (pic_type == AVC_PIC_FRAME) field_poc[1] += si->delta_poc[1]; + } else if (si->sps->poc_type== 2) { + int poc; + if (si->nal_unit_type == GF_AVC_NALU_IDR_SLICE) { + poc = 0; + } else { + const int abs_frame_num = si->frame_num_offset + si->frame_num; + poc = 2 * abs_frame_num; + if (!si->nal_ref_idc) poc -= 1; + } + field_poc[0] = poc; + field_poc[1] = poc; + } + if (pic_type == AVC_PIC_FRAME) + si->poc = MIN(field_poc[0], field_poc[1] ); + else if (pic_type == AVC_PIC_FIELD_TOP) + si->poc = field_poc[0]; + else + si->poc = field_poc[1]; +} + +s32 AVC_ParseNALU(GF_BitStream *bs, u32 nal_hdr, AVCState *avc) +{ + u8 temp; + s32 slice, ret; + AVCSliceInfo n_state; + + slice = 0; + memcpy(&n_state, &avc->s_info, sizeof(AVCSliceInfo)); + temp = n_state.nal_unit_type = nal_hdr & 0x1F; + n_state.nal_ref_idc = (nal_hdr>>5) & 0x3; + + ret = 0; + switch (temp) { + case GF_AVC_NALU_ACCESS_UNIT: + case GF_AVC_NALU_END_OF_SEQ: + case GF_AVC_NALU_END_OF_STREAM: + ret = 1; + break; + case GF_AVC_NALU_NON_IDR_SLICE: + case GF_AVC_NALU_DP_A_SLICE: + case GF_AVC_NALU_DP_B_SLICE: + case GF_AVC_NALU_DP_C_SLICE: + case GF_AVC_NALU_IDR_SLICE: + slice = 1; + /* slice buffer - read the info and compare.*/ + ret = avc_parse_slice(bs, avc, &n_state); + if (ret<0) return ret; + ret = 0; + if ((avc->s_info.nal_unit_type > GF_AVC_NALU_IDR_SLICE) + || (avc->s_info.nal_unit_type < GF_AVC_NALU_NON_IDR_SLICE)) { + break; + } + if (avc->s_info.frame_num != n_state.frame_num) { ret = 1; break; } + + if (avc->s_info.field_pic_flag != n_state.field_pic_flag) { ret = 1; break; } + if ((avc->s_info.nal_ref_idc != n_state.nal_ref_idc) && + (!avc->s_info.nal_ref_idc || !n_state.nal_ref_idc)) { + ret = 1; + break; + } + assert(avc->s_info.sps); + + if (avc->s_info.sps->poc_type == n_state.sps->poc_type) { + if (!avc->s_info.sps->poc_type) { + if (!n_state.bottom_field_flag && (avc->s_info.poc_lsb != n_state.poc_lsb)) { + ret = 1; + break; + } + if (avc->s_info.delta_poc_bottom != n_state.delta_poc_bottom) { + ret = 1; + break; + } + } else if (avc->s_info.sps->poc_type==1) { + if (avc->s_info.delta_poc[0] != n_state.delta_poc[0]) { + ret =1; + break; + } + if (avc->s_info.delta_poc[1] != n_state.delta_poc[1]) { + ret = 1; + break; + } + } + } + if ((avc->s_info.nal_unit_type == GF_AVC_NALU_IDR_SLICE) + && (n_state.nal_unit_type == GF_AVC_NALU_IDR_SLICE)) { + if (avc->s_info.idr_pic_id != n_state.idr_pic_id) { + ret = 1; + break; + } + } + break; + case GF_AVC_NALU_SEQ_PARAM: + case GF_AVC_NALU_PIC_PARAM: + return 0; + default: + if (avc->s_info.nal_unit_type <= GF_AVC_NALU_IDR_SLICE) ret = 1; + else ret = 0; + break; + } + + /* save _prev values */ + if (ret && avc->s_info.sps) { + n_state.frame_num_offset_prev = avc->s_info.frame_num_offset; + if ((avc->s_info.sps->poc_type != 2) || (avc->s_info.nal_ref_idc != 0)) + n_state.frame_num_prev = avc->s_info.frame_num; + if (avc->s_info.nal_ref_idc) { + n_state.poc_lsb_prev = avc->s_info.poc_lsb; + n_state.poc_msb_prev = avc->s_info.poc_msb; + } + } + if (slice) avc_compute_poc(&n_state); + memcpy(&avc->s_info, &n_state, sizeof(AVCSliceInfo)); + return ret; +} + +u32 AVC_ReformatSEI_NALU(char *buffer, u32 nal_size, AVCState *avc) +{ + u32 ptype, psize, hdr, written, start, var, num_zero, size_fix, i; + char *new_buffer; + GF_BitStream *bs; + hdr = buffer[0]; + if ((hdr & 0x1F) != GF_AVC_NALU_SEI) return 0; + + bs = gf_bs_new(buffer, nal_size, GF_BITSTREAM_READ); + gf_bs_read_int(bs, 8); + + new_buffer = (char*)malloc(sizeof(char)*nal_size); + new_buffer[0] = (char) hdr; + written = 1; + + /*parse SEI*/ + while (gf_bs_available(bs)) { + Bool do_copy; + ptype = 0; + while (gf_bs_peek_bits(bs, 8, 0)==0xFF) { + gf_bs_read_int(bs, 8); + ptype += 255; + } + ptype += gf_bs_read_int(bs, 8); + psize = 0; + while (gf_bs_peek_bits(bs, 8, 0)==0xFF) { + gf_bs_read_int(bs, 8); + psize += 255; + } + psize += gf_bs_read_int(bs, 8); + + start = (u32) gf_bs_get_position(bs); + do_copy = 1; + switch (ptype) { + /*remove SEI messages forbidden in MP4*/ + case 3: /*filler data*/ + case 10: /*sub_seq info*/ + case 11: /*sub_seq_layer char*/ + case 12: /*sub_seq char*/ + do_copy = 0; + break; + case 5: /*user unregistered */ + { + u8 prev = buffer[start+2+psize]; + buffer[start+2+psize] = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[avc-h264] SEI user message %s\n", buffer+start+2)); + buffer[start+2+psize] = prev; + } + break; + + case 6: /*recovery point*/ + { + GF_BitStream *rp_bs = gf_bs_new(buffer + start, psize, GF_BITSTREAM_READ); + avc_parse_recovery_point_sei(rp_bs, avc); + gf_bs_del(rp_bs); + } + break; + + case 0: /*buffering period*/ + case 1: /*pic_timing*/ + case 2: /*pan scan rect*/ + case 4: /*user registered ITU t35*/ + + case 7: /*def_rec_pic_marking_repetition*/ + case 8: /*spare_pic*/ + case 9: /*scene info*/ + case 13: /*full frame freeze*/ + case 14: /*full frame freeze release*/ + case 15: /*full frame snapshot*/ + case 16: /*progressive refinement segment start*/ + case 17: /*progressive refinement segment end*/ + case 18: /*motion constrained slice group*/ + default:/*reserved*/ + break; + } + + /*fix payload size due to emulation prevention bytes*/ + size_fix = 0; + num_zero = 0; + if (psize%255 == 0) num_zero = 1; + + for (i = 0; i < psize + size_fix; i++) + { + if (!buffer[start + i]) + num_zero++; + else + { + if ((num_zero == 2) && (buffer[start + i] == 3)) + size_fix++; + num_zero = 0; + } + } + + if (do_copy) { + var = ptype; + while (var>=255) { new_buffer[written] = (char) 0xff; written++; var-=255;} + new_buffer[written] = (char) var; written++; + var = psize; + while (var>=255) { new_buffer[written] = (char) 0xff; written++; var-=255;} + new_buffer[written] = (char) var; written++; + memcpy(new_buffer+written, buffer+start, sizeof(char)* (psize + size_fix)); + written += psize + size_fix; + } + + gf_bs_skip_bytes(bs, (u64) (psize + size_fix)); + gf_bs_align(bs); + if (gf_bs_available(bs)<2) { + if (gf_bs_peek_bits(bs, 8, 0)==0x80) { + new_buffer[written] = (char) 0x80; + written += 1; + break; + } + } + } + gf_bs_del(bs); + assert(written<=nal_size); + if (written) memcpy(buffer, new_buffer, sizeof(char)*written); + free(new_buffer); + /*if only hdr written ignore*/ + return (written>1) ? written : 0; +} + +static u8 avc_get_sar_idx(u32 w, u32 h) +{ + u32 i; + for (i=0; i<14; i++) { + if ((avc_sar[i].w==w) && (avc_sar[i].h==h)) return i; + } + return 0xFF; +} + +GF_Err AVC_ChangePAR(GF_AVCConfig *avcc, s32 ar_n, s32 ar_d) +{ + GF_BitStream *orig, *mod; + AVCState avc; + u32 i, bit_offset, flag; + s32 idx; + GF_AVCConfigSlot *slc; + + memset(&avc, 0, sizeof(AVCState)); + + i=0; + while ((slc = (GF_AVCConfigSlot *)gf_list_enum(avcc->sequenceParameterSets, &i))) { + orig = gf_bs_new(slc->data, slc->size, GF_BITSTREAM_READ); + /*skip NALU type*/ + gf_bs_read_int(orig, 8); + idx = AVC_ReadSeqInfo(orig, &avc, &bit_offset); + if (idx<0) { + gf_bs_del(orig); + continue; + } + mod = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_seek(orig, 0); + + /*copy over till vui flag*/ + while (bit_offset) { + flag = gf_bs_read_int(orig, 1); + gf_bs_write_int(mod, flag, 1); + bit_offset--; + } + /*check VUI*/ + flag = gf_bs_read_int(orig, 1); + gf_bs_write_int(mod, 1, 1); + if (flag) { + /*aspect_ratio_info_present_flag*/ + if (gf_bs_read_int(orig, 1)) { + s32 aspect_ratio_idc = gf_bs_read_int(orig, 8); + if (aspect_ratio_idc == 255) { + gf_bs_read_int(orig, 16); /*AR num*/ + gf_bs_read_int(orig, 16); /*AR den*/ + } + } + } + if ((ar_d<0) || (ar_n<0)) { + /*no AR signaled*/ + gf_bs_write_int(mod, 0, 1); + } else { + u32 sarx; + gf_bs_write_int(mod, 1, 1); + sarx = avc_get_sar_idx((u32) ar_n, (u32) ar_d); + gf_bs_write_int(mod, sarx, 8); + if (sarx==0xFF) { + gf_bs_write_int(mod, ar_n, 16); + gf_bs_write_int(mod, ar_d, 16); + } + } + /*no VUI in input bitstream, set all vui flags to 0*/ + if (!flag) { + gf_bs_write_int(mod, 0, 1); /*overscan_info_present_flag */ + gf_bs_write_int(mod, 0, 1); /*video_signal_type_present_flag */ + gf_bs_write_int(mod, 0, 1); /*chroma_location_info_present_flag */ + gf_bs_write_int(mod, 0, 1); /*timing_info_present_flag*/ + gf_bs_write_int(mod, 0, 1); /*nal_hrd_parameters_present*/ + gf_bs_write_int(mod, 0, 1); /*vcl_hrd_parameters_present*/ + gf_bs_write_int(mod, 0, 1); /*pic_struct_present*/ + gf_bs_write_int(mod, 0, 1); /*bitstream_restriction*/ + } + + /*finally copy over remaining*/ + while (gf_bs_bits_available(orig)) { + flag = gf_bs_read_int(orig, 1); + gf_bs_write_int(mod, flag, 1); + } + gf_bs_del(orig); + free(slc->data); + slc->data = NULL; + gf_bs_get_content(mod, (char **) &slc->data, &flag); + slc->size = flag; + gf_bs_del(mod); + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_avc_get_sps_info(char *sps_data, u32 sps_size, u32 *width, u32 *height, s32 *par_n, s32 *par_d) +{ + GF_BitStream *bs; + AVCState avc; + s32 idx; + memset(&avc, 0, sizeof(AVCState)); + + bs = gf_bs_new(sps_data, sps_size, GF_BITSTREAM_READ); + /*skip NALU type*/ + gf_bs_read_int(bs, 8); + idx = AVC_ReadSeqInfo(bs, &avc, NULL); + gf_bs_del(bs); + if (idx<0) return GF_NON_COMPLIANT_BITSTREAM; + + if (width) *width = avc.sps[idx].width; + if (height) *height = avc.sps[idx].height; + if (par_n) *par_n = avc.sps[idx].par_num ? avc.sps[idx].par_num : (u32) -1; + if (par_d) *par_d = avc.sps[idx].par_den ? avc.sps[idx].par_den : (u32) -1; + return GF_OK; +} + +#endif + +GF_EXPORT +const char *gf_avc_get_profile_name(u8 video_prof) +{ + switch (video_prof) { + case 0x42: return "Baseline"; + case 0x4D: return "Main"; + case 0x58: return "Extended"; + case 0x64: return "High"; + case 0x6E: return "High 10"; + case 0x7A: return "High 4:2:2"; + case 0x90: return "High 4:4:4"; + default: return "Unknown"; + } +} + +static u32 AC3_FindSyncCode(u8 *buf, u32 buflen) +{ + u32 end = buflen - 6; + u32 offset = 0; + while (offset <= end) { + if (buf[offset] == 0x0b && buf[offset + 1] == 0x77) { + return offset; + } + offset++; + } + return buflen; +} + + +static Bool AC3_FindSyncCodeBS(GF_BitStream *bs) +{ + u8 b1; + u32 pos = (u32) gf_bs_get_position(bs); + u32 end = (u32) gf_bs_get_size(bs) - 6; + + pos += 1; + b1 = gf_bs_read_u8(bs); + while (pos <= end) { + u8 b2 = gf_bs_read_u8(bs); + if ((b1 == 0x0b) && (b2==0x77)) { + gf_bs_seek(bs, pos-1); + return 1; + } + pos++; + } + return 0; +} + +static const u32 ac3_sizecod_to_bitrate[] = { + 32000, 40000, 48000, 56000, 64000, 80000, 96000, + 112000, 128000, 160000, 192000, 224000, 256000, + 320000, 384000, 448000, 512000, 576000, 640000 +}; + +static const u32 ac3_sizecod2_to_framesize[] = { + 96, 120, 144, 168, 192, 240, 288, 336, 384, 480, 576, 672, + 768, 960, 1152, 1344, 1536, 1728, 1920 +}; + +static const u32 ac3_sizecod1_to_framesize[] = { + 69, 87, 104, 121, 139, 174, 208, 243, 278, 348, 417, 487, + 557, 696, 835, 975, 1114, 1253, 1393 +}; +static const u32 ac3_sizecod0_to_framesize[] = { + 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, + 512, 640, 768, 896, 1024, 1152, 1280 +}; + +static const u32 ac3_mod_to_chans[] = { + 2, 1, 2, 3, 3, 4, 4, 5 +}; + +Bool gf_ac3_parser(u8 *buf, u32 buflen, u32 *pos, GF_AC3Header *hdr, Bool full_parse) +{ + u32 fscod, frmsizecod, bsid, ac3_mod, freq, framesize; + if (buflen < 6) return 0; + (*pos) = AC3_FindSyncCode(buf, buflen); + if (*pos >= buflen) return 0; + + buf += (*pos); + fscod = (buf[4] >> 6) & 0x3; + frmsizecod = (buf[4] & 0x3f); + bsid = (buf[5] >> 3) & 0x1f; + ac3_mod = (buf[6] >> 5) & 0x7; + if (bsid >= 12) return 0; + + if (full_parse && hdr) memset(hdr, 0, sizeof(GF_AC3Header)); + + if (hdr) { + hdr->bitrate = ac3_sizecod_to_bitrate[frmsizecod / 2]; + if (bsid > 8) hdr->bitrate = hdr->bitrate >> (bsid - 8); + } + switch (fscod) { + case 0: + freq = 48000; + framesize = ac3_sizecod0_to_framesize[frmsizecod / 2] * 2; + break; + case 1: + freq = 44100; + framesize = (ac3_sizecod1_to_framesize[frmsizecod / 2] + (frmsizecod & 0x1)) * 2; + break; + case 2: + freq = 32000; + framesize = ac3_sizecod2_to_framesize[frmsizecod / 2] * 2; + break; + default: + return 0; + } + if (hdr) { + u16 maskbit, b67; + hdr->sample_rate = freq; + hdr->framesize = framesize; + + hdr->channels = ac3_mod_to_chans[ac3_mod]; + maskbit = 0x100; + if ((ac3_mod & 0x1) && (ac3_mod != 1)) maskbit >>= 2; + if (ac3_mod & 0x4) maskbit >>= 2; + if (ac3_mod == 0x2) maskbit += 2; + b67 = (buf[6] << 8) | buf[7]; + if ((b67 & maskbit) != 0) hdr->channels += 1; + } + return 1; +} + + +Bool gf_ac3_parser_bs(GF_BitStream *bs, GF_AC3Header *hdr, Bool full_parse) +{ + u32 fscod, frmsizecod, bsid, ac3_mod, freq, framesize, pos, bsmod; + if (!hdr || (gf_bs_available(bs) < 6)) return 0; + if (!AC3_FindSyncCodeBS(bs)) return 0; + + pos = (u32) gf_bs_get_position(bs); + gf_bs_read_u32(bs); + fscod = gf_bs_read_int(bs, 2); + frmsizecod = gf_bs_read_int(bs, 6); + bsid = gf_bs_read_int(bs, 5); + bsmod = gf_bs_read_int(bs, 3); + ac3_mod = gf_bs_read_int(bs, 3); + + if (bsid >= 12) return 0; + + //memset(hdr, 0, sizeof(GF_AC3Header)); + + hdr->bitrate = ac3_sizecod_to_bitrate[frmsizecod / 2]; + if (bsid > 8) hdr->bitrate = hdr->bitrate >> (bsid - 8); + + switch (fscod) { + case 0: + freq = 48000; + framesize = ac3_sizecod0_to_framesize[frmsizecod / 2] * 2; + break; + case 1: + freq = 44100; + framesize = (ac3_sizecod1_to_framesize[frmsizecod / 2] + (frmsizecod & 0x1)) * 2; + break; + case 2: + freq = 32000; + framesize = ac3_sizecod2_to_framesize[frmsizecod / 2] * 2; + break; + default: + return 0; + } + hdr->sample_rate = freq; + hdr->framesize = framesize; + + if (full_parse) { + hdr->bsid = bsid; + hdr->bsmod = bsmod; + hdr->acmod = ac3_mod; + hdr->lfon = 0; + hdr->fscod = fscod; + hdr->brcode = hdr->bitrate/1000; + } + + hdr->channels = ac3_mod_to_chans[ac3_mod]; + if ((ac3_mod & 0x1) && (ac3_mod != 1)) gf_bs_read_int(bs, 2); + if (ac3_mod & 0x4) gf_bs_read_int(bs, 2); + if (ac3_mod == 0x2) gf_bs_read_int(bs, 2); + /*LFEon*/ + if (gf_bs_read_int(bs, 1)) { + hdr->channels += 1; + hdr->lfon = 1; + } + + + gf_bs_seek(bs, pos); + return 1; +} + + + +/* + Vorbis parser +*/ + +static u32 vorbis_book_maptype1_quantvals(u32 entries, u32 dim) +{ + u32 vals = (u32) floor(pow(entries, 1.0/dim)); + while(1) { + u32 acc=1; + u32 acc1=1; + u32 i; + for (i=0;ientries) return (vals); + else { + if (acc>entries) vals--; + else vals++; + } + } +} + +u32 _ilog_(u32 v) +{ + u32 ret=0; + while(v) { + ret++; + v>>=1; + } + return(ret); +} + +static u32 ilog(u32 v) +{ + u32 ret=0; + if(v) --v; + while(v) { + ret++; + v>>=1; + } + return (ret); +} + +static u32 icount(u32 v) +{ + u32 ret=0; + while(v) { + ret += v&1; + v>>=1; + } + return(ret); +} + + +GF_EXPORT +Bool gf_vorbis_parse_header(GF_VorbisParser *vp, char *data, u32 data_len) +{ + u32 pack_type, i, j, k, times, nb_part, nb_books, nb_modes; + char szNAME[8]; + oggpack_buffer opb; + + oggpack_readinit(&opb, (u8*)data, data_len); + pack_type = oggpack_read(&opb, 8); + i=0; + while (i<6) { szNAME[i] = oggpack_read(&opb, 8); i++;} + szNAME[i] = 0; + if (strcmp(szNAME, "vorbis")) return vp->is_init = 0; + + switch (pack_type) { + case 0x01: + vp->version = oggpack_read(&opb, 32); + if (vp->version!=0) return 0; + vp->channels = oggpack_read(&opb, 8); + vp->sample_rate = oggpack_read(&opb, 32); + vp->max_r = oggpack_read(&opb, 32); + vp->avg_r = oggpack_read(&opb, 32); + vp->low_r = oggpack_read(&opb, 32); + + vp->min_block = 1<max_block = 1<sample_rate < 1) return vp->is_init = 0; + if (vp->channels < 1) return vp->is_init = 0; + if (vp->min_block<8) return vp->is_init = 0; + if (vp->max_block < vp->min_block) return vp->is_init = 0; + if (oggpack_read(&opb, 1) != 1) return vp->is_init = 0; + vp->is_init = 1; + return 1; + case 0x03: + /*trash comments*/ + vp->is_init ++; + return 1; + case 0x05: + /*need at least bitstream header to make sure we're parsing the right thing*/ + if (!vp->is_init) return 0; + break; + default: + vp->is_init = 0; + return 0; + } + /*OK parse codebook*/ + nb_books = oggpack_read(&opb, 8) + 1; + /*skip vorbis static books*/ + for (i=0; ichannels)); + oggpack_read(&opb, ilog(vp->channels)); + } + } + oggpack_read(&opb, 2); + if (sub_maps>1) { + for(j=0; jchannels; j++) oggpack_read(&opb, 4); + } + for (j=0;jmode_flag[i] = oggpack_read(&opb, 1); + oggpack_read(&opb, 16); + oggpack_read(&opb, 16); + oggpack_read(&opb, 8); + } + + vp->modebits = 0; + j = nb_modes; + while(j>1) { + vp->modebits++; + j>>=1; + } + return 1; +} + +GF_EXPORT +u32 gf_vorbis_check_frame(GF_VorbisParser *vp, char *data, u32 data_length) +{ + s32 block_size; + oggpack_buffer opb; + if (!vp->is_init) return 0; + oggpack_readinit(&opb, (unsigned char*)data, data_length); + /*not audio*/ + if (oggpack_read(&opb, 1) !=0) return 0; + block_size = oggpack_read(&opb, vp->modebits); + if (block_size == -1) return 0; + return ((vp->mode_flag[block_size]) ? vp->max_block : vp->min_block) / (2); +} diff --git a/src/media_tools/avilib.c b/src/media_tools/avilib.c new file mode 100644 index 0000000..21c01db --- /dev/null +++ b/src/media_tools/avilib.c @@ -0,0 +1,2973 @@ +/* + * avilib.c + * + * Copyright (C) Thomas Östreich - June 2001 + * multiple audio track support Copyright (C) 2002 Thomas Östreich + * + * Original code: + * Copyright (C) 1999 Rainer Johanni + * + * This file is part of transcode, a linux video stream processing tool + * + * transcode is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * transcode is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#ifndef GPAC_READ_ONLY + + +#define PACKAGE "GPAC/avilib" +#define VERSION GPAC_FULL_VERSION + +#define INFO_LIST + +// add a new riff chunk after XX MB +//#define NEW_RIFF_THRES (1900*1024*1024) +#define NEW_RIFF_THRES (1900*1024*1024) +//#define NEW_RIFF_THRES (10*1024*1024) + +// Maximum number of indices per stream +#define NR_IXNN_CHUNKS 32 + + +#define DEBUG_ODML +#undef DEBUG_ODML + +/* The following variable indicates the kind of error */ + +long AVI_errno = 0; + +#define MAX_INFO_STRLEN 64 +static char id_str[MAX_INFO_STRLEN]; + +#define FRAME_RATE_SCALE 1000000 + +/******************************************************************* + * * + * Utilities for writing an AVI File * + * * + *******************************************************************/ + +static u32 avi_read(FILE *fd, char *buf, u32 len) +{ + u32 n = 0; + u32 r = 0; + + while (r < len) { + n = fread(buf + r, 1, len - r, fd); + if (n == 0) break; + if (n < 0) return r; + r += n; + } + + return r; +} + +static u32 avi_write (FILE *fd, char *buf, u32 len) +{ + u32 n = 0; + u32 r = 0; + + while (r < len) { + n = fwrite (buf + r, 1, len - r, fd); + if (n < 0) + return n; + + r += n; + } + return r; +} + +/* HEADERBYTES: The number of bytes to reserve for the header */ + +#define HEADERBYTES 2048 + +/* AVI_MAX_LEN: The maximum length of an AVI file, we stay a bit below + the 2GB limit (Remember: 2*10^9 is smaller than 2 GB) */ + +#define AVI_MAX_LEN (UINT_MAX-(1<<20)*16-HEADERBYTES) + +#define PAD_EVEN(x) ( ((x)+1) & ~1 ) + + +/* Copy n into dst as a 4 or 2 byte, little endian number. + Should also work on big endian machines */ + +static void long2str(unsigned char *dst, s32 n) +{ + dst[0] = (n )&0xff; + dst[1] = (n>> 8)&0xff; + dst[2] = (n>>16)&0xff; + dst[3] = (n>>24)&0xff; +} + +#ifdef WORDS_BIGENDIAN +static void short2str(unsigned char *dst, s32 n) +{ + dst[0] = (n )&0xff; + dst[1] = (n>> 8)&0xff; +} +#endif + +/* Convert a string of 4 or 2 bytes to a number, + also working on big endian machines */ + +static u64 str2ullong(unsigned char *str) +{ + u64 r = (str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24)); + u64 s = (str[4] | (str[5]<<8) | (str[6]<<16) | (str[7]<<24)); +#ifdef __GNUC__ + return ((s<<32)&0xffffffff00000000ULL)|(r&0xffffffff); +#else + return ((s<<32)&0xffffffff00000000)|(r&0xffffffff); +#endif +} + +static u32 str2ulong(unsigned char *str) +{ + return ( str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24) ); +} +static u32 str2ushort(unsigned char *str) +{ + return ( str[0] | (str[1]<<8) ); +} + +// bit 31 denotes a keyframe +static u32 str2ulong_len (unsigned char *str) +{ + return str2ulong(str) & 0x7fffffff; +} + + +// if bit 31 is 0, its a keyframe +static u32 str2ulong_key (unsigned char *str) +{ + u32 c = str2ulong(str); + c &= 0x80000000; + if (c == 0) return 0x10; + else return 0; +} + +/* Calculate audio sample size from number of bits and number of channels. + This may have to be adjusted for eg. 12 bits and stereo */ + +static int avi_sampsize(avi_t *AVI, int j) +{ + int s; + s = ((AVI->track[j].a_bits+7)/8)*AVI->track[j].a_chans; + // if(s==0) s=1; /* avoid possible zero divisions */ + if(s<4) s=4; /* avoid possible zero divisions */ + return s; +} + +/* Add a chunk (=tag and data) to the AVI file, + returns -1 on write error, 0 on success */ + +static int avi_add_chunk(avi_t *AVI, unsigned char *tag, unsigned char *data, u32 length) +{ + unsigned char c[8]; + char p=0; + + /* Copy tag and length int c, so that we need only 1 write system call + for these two values */ + + memcpy(c,tag,4); + long2str(c+4,length); + + /* Output tag, length and data, restore previous position + if the write fails */ + + if( avi_write(AVI->fdes,(char *)c,8) != 8 || + avi_write(AVI->fdes,(char *)data,length) != length || + avi_write(AVI->fdes,&p,length&1) != (length&1)) // if len is uneven, write a pad byte + { + gf_f64_seek(AVI->fdes,AVI->pos,SEEK_SET); + AVI_errno = AVI_ERR_WRITE; + return -1; + } + + /* Update file position */ + + AVI->pos += 8 + PAD_EVEN(length); + + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] pos=%lu %s\n", AVI->pos, tag)); + + return 0; +} + +#define OUTD(n) long2str((unsigned char*) (ix00+bl),(s32)n); bl+=4 +#define OUTW(n) ix00[bl] = (n)&0xff; ix00[bl+1] = (n>>8)&0xff; bl+=2 +#define OUTC(n) ix00[bl] = (n)&0xff; bl+=1 +#define OUTS(s) memcpy(ix00+bl,s,4); bl+=4 + +// this does the physical writeout of the ix## structure +static int avi_ixnn_entry(avi_t *AVI, avistdindex_chunk *ch, avisuperindex_entry *en) +{ + int bl; + u32 k; + unsigned int max = ch->nEntriesInUse * sizeof (u32) * ch->wLongsPerEntry + 24; // header + char *ix00 = (char *)malloc (max); + char dfcc[5]; + memcpy (dfcc, ch->fcc, 4); + dfcc[4] = 0; + + bl = 0; + + if (en) { + en->qwOffset = AVI->pos; + en->dwSize = max; + //en->dwDuration = ch->nEntriesInUse -1; // NUMBER OF stream ticks == frames for video/samples for audio + } + +#ifdef DEBUG_ODML + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML Write %s: Entries %ld size %d \n", dfcc, ch->nEntriesInUse, max)); +#endif + + //OUTS(ch->fcc); + //OUTD(max); + OUTW(ch->wLongsPerEntry); + OUTC(ch->bIndexSubType); + OUTC(ch->bIndexType); + OUTD(ch->nEntriesInUse); + OUTS(ch->dwChunkId); + OUTD(ch->qwBaseOffset&0xffffffff); + OUTD((ch->qwBaseOffset>>32)&0xffffffff); + OUTD(ch->dwReserved3); + + for (k = 0; k < ch->nEntriesInUse; k++) { + OUTD(ch->aIndex[k].dwOffset); + OUTD(ch->aIndex[k].dwSize); + + } + avi_add_chunk (AVI, (unsigned char*)ch->fcc, (unsigned char*)ix00, max); + + free(ix00); + + return 0; +} +#undef OUTS +#undef OUTW +#undef OUTD +#undef OUTC + +// inits a super index structure including its enclosed stdindex +static int avi_init_super_index(avi_t *AVI, unsigned char *idxtag, avisuperindex_chunk **si) +{ + int k; + + avisuperindex_chunk *sil = NULL; + + if ((sil = (avisuperindex_chunk *) malloc (sizeof (avisuperindex_chunk))) == NULL) { + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + memcpy (sil->fcc, "indx", 4); + sil->dwSize = 0; // size of this chunk + sil->wLongsPerEntry = 4; + sil->bIndexSubType = 0; + sil->bIndexType = AVI_INDEX_OF_INDEXES; + sil->nEntriesInUse = 0; // none are in use + memcpy (sil->dwChunkId, idxtag, 4); + memset (sil->dwReserved, 0, sizeof (sil->dwReserved)); + + // NR_IXNN_CHUNKS == allow 32 indices which means 32 GB files -- arbitrary + sil->aIndex = (avisuperindex_entry *) malloc (sil->wLongsPerEntry * NR_IXNN_CHUNKS * sizeof (void*)); + if (!sil->aIndex) { + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + memset (sil->aIndex, 0, sil->wLongsPerEntry * NR_IXNN_CHUNKS * sizeof (u32)); + + sil->stdindex = (avistdindex_chunk **)malloc (NR_IXNN_CHUNKS * sizeof (avistdindex_chunk *)); + if (!sil->stdindex) { + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + for (k = 0; k < NR_IXNN_CHUNKS; k++) { + sil->stdindex[k] = (avistdindex_chunk *) malloc (sizeof (avistdindex_chunk)); + // gets rewritten later + sil->stdindex[k]->qwBaseOffset = (u64)k * NEW_RIFF_THRES; + sil->stdindex[k]->aIndex = NULL; + } + + *si = sil; + + return 0; +} + +// fills an alloc'ed stdindex structure and mallocs some entries for the actual chunks +static int avi_add_std_index(avi_t *AVI, unsigned char *idxtag, unsigned char *strtag, + avistdindex_chunk *stdil) +{ + + memcpy (stdil->fcc, idxtag, 4); + stdil->dwSize = 4096; + stdil->wLongsPerEntry = 2; //sizeof(avistdindex_entry)/sizeof(u32); + stdil->bIndexSubType = 0; + stdil->bIndexType = AVI_INDEX_OF_CHUNKS; + stdil->nEntriesInUse = 0; + + // cp 00db ChunkId + memcpy(stdil->dwChunkId, strtag, 4); + + //stdil->qwBaseOffset = AVI->video_superindex->aIndex[ cur_std_idx ]->qwOffset; + + stdil->aIndex = (avistdindex_entry *)malloc(stdil->dwSize * sizeof (u32) * stdil->wLongsPerEntry); + + if (!stdil->aIndex) { + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + + + return 0; +} + +static int avi_add_odml_index_entry_core(avi_t *AVI, long flags, u64 pos, unsigned long len, avistdindex_chunk *si) +{ + u32 cur_chunk_idx; + // put new chunk into index + si->nEntriesInUse++; + cur_chunk_idx = si->nEntriesInUse-1; + + // need to fetch more memory + if (cur_chunk_idx >= si->dwSize) { + si->dwSize += 4096; + si->aIndex = (avistdindex_entry *)realloc ( si->aIndex, si->dwSize * sizeof (u32) * si->wLongsPerEntry); + } + + if(len>AVI->max_len) AVI->max_len=len; + + // if bit 31 is set, it is NOT a keyframe + if (flags != 0x10) { + len |= 0x80000000; + } + + si->aIndex [ cur_chunk_idx ].dwSize = len; + si->aIndex [ cur_chunk_idx ].dwOffset = (u32) (pos - si->qwBaseOffset + 8); + + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: POS: 0x%lX\n", si->aIndex [ cur_chunk_idx ].dwOffset)); + + return 0; +} + +static int avi_add_odml_index_entry(avi_t *AVI, unsigned char *tag, long flags, u64 pos, unsigned long len) +{ + char fcc[5]; + + int audio = (strchr ((char*)tag, 'w')?1:0); + int video = !audio; + + unsigned int cur_std_idx; + u32 audtr; + s64 towrite = 0; + + if (video) { + + if (!AVI->video_superindex) { + if (avi_init_super_index(AVI, (unsigned char *)"ix00", &AVI->video_superindex) < 0) return -1; + AVI->video_superindex->nEntriesInUse++; + cur_std_idx = AVI->video_superindex->nEntriesInUse-1; + + if (avi_add_std_index (AVI, (unsigned char *)"ix00", (unsigned char *)"00db", AVI->video_superindex->stdindex[ cur_std_idx ]) < 0) + return -1; + } // init + + } // video + + if (audio) { + + fcc[0] = 'i'; fcc[1] = 'x'; fcc[2] = tag[0]; fcc[3] = tag[1]; fcc[4] = '\0'; + if (!AVI->track[AVI->aptr].audio_superindex) { + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: fcc = %s\n", fcc)); +#endif + if (avi_init_super_index(AVI, (unsigned char *)fcc, &AVI->track[AVI->aptr].audio_superindex) < 0) return -1; + + + AVI->track[AVI->aptr].audio_superindex->nEntriesInUse++; + + sprintf(fcc, "ix%02d", AVI->aptr+1); + if (avi_add_std_index (AVI, (unsigned char *)fcc, tag, AVI->track[AVI->aptr].audio_superindex->stdindex[ + AVI->track[AVI->aptr].audio_superindex->nEntriesInUse - 1 ]) < 0 + ) return -1; + } // init + + } + + towrite = 0; + if (AVI->video_superindex) { + + cur_std_idx = AVI->video_superindex->nEntriesInUse-1; + towrite += AVI->video_superindex->stdindex[cur_std_idx]->nEntriesInUse*8 + + 4+4+2+1+1+4+4+8+4; + if (cur_std_idx == 0) { + towrite += AVI->n_idx*16 + 8; + towrite += HEADERBYTES; + } + } + + for (audtr=0; audtranum; audtr++) { + if (AVI->track[audtr].audio_superindex) { + cur_std_idx = AVI->track[audtr].audio_superindex->nEntriesInUse-1; + towrite += AVI->track[audtr].audio_superindex->stdindex[cur_std_idx]->nEntriesInUse*8 + + 4+4+2+1+1+4+4+8+4; + } + } + towrite += len + (len&1) + 8; + + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: towrite = 0x%llX = %lld\n", towrite, towrite)); + + if (AVI->video_superindex && + (s64)(AVI->pos+towrite) > (s64)((s64)NEW_RIFF_THRES*AVI->video_superindex->nEntriesInUse)) { + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] Adding a new RIFF chunk: %d\n", AVI->video_superindex->nEntriesInUse)); + + // rotate ALL indices + AVI->video_superindex->nEntriesInUse++; + cur_std_idx = AVI->video_superindex->nEntriesInUse-1; + + if (AVI->video_superindex->nEntriesInUse > NR_IXNN_CHUNKS) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Internal error in avilib - redefine NR_IXNN_CHUNKS\n")); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] cur_std_idx=%d NR_IXNN_CHUNKS=%d" + "POS=%lld towrite=%lld\n", + cur_std_idx,NR_IXNN_CHUNKS, AVI->pos, towrite)); + return -1; + } + + if (avi_add_std_index (AVI, (unsigned char *)"ix00", (unsigned char *)"00db", AVI->video_superindex->stdindex[ cur_std_idx ]) < 0) + return -1; + + for (audtr = 0; audtr < AVI->anum; audtr++) { + char aud[5]; + if (!AVI->track[audtr].audio_superindex) { + // not initialized -> no index + continue; + } + AVI->track[audtr].audio_superindex->nEntriesInUse++; + + sprintf(fcc, "ix%02d", audtr+1); + sprintf(aud, "0%01dwb", audtr+1); + if (avi_add_std_index (AVI, (unsigned char *)fcc, (unsigned char *)aud, AVI->track[audtr].audio_superindex->stdindex[ + AVI->track[audtr].audio_superindex->nEntriesInUse - 1 ]) < 0 + ) return -1; + } + + // write the new riff; + if (cur_std_idx > 0) { + + // dump the _previous_ == already finished index + avi_ixnn_entry (AVI, AVI->video_superindex->stdindex[cur_std_idx - 1], + &AVI->video_superindex->aIndex[cur_std_idx - 1]); + AVI->video_superindex->aIndex[cur_std_idx - 1].dwDuration = + AVI->video_superindex->stdindex[cur_std_idx - 1]->nEntriesInUse - 1; + + for (audtr = 0; audtr < AVI->anum; audtr++) { + + if (!AVI->track[audtr].audio_superindex) { + // not initialized -> no index + continue; + } + avi_ixnn_entry (AVI, AVI->track[audtr].audio_superindex->stdindex[cur_std_idx - 1], + &AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1]); + + AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1].dwDuration = + AVI->track[audtr].audio_superindex->stdindex[cur_std_idx - 1]->nEntriesInUse - 1; + if (AVI->track[audtr].a_fmt == 0x1) { + AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1].dwDuration *= + AVI->track[audtr].a_bits*AVI->track[audtr].a_rate*AVI->track[audtr].a_chans/800; + } + } + + // XXX: dump idx1 structure + if (cur_std_idx == 1) { + avi_add_chunk(AVI, (unsigned char *)"idx1", (unsigned char *)AVI->idx, AVI->n_idx*16); + // qwBaseOffset will contain the start of the second riff chunk + } + // Fix the Offsets later at closing time + avi_add_chunk(AVI, (unsigned char *)"RIFF", (unsigned char *)"AVIXLIST\0\0\0\0movi", 16); + + AVI->video_superindex->stdindex[ cur_std_idx ]->qwBaseOffset = AVI->pos -16 -8; +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: RIFF No.%02d at Offset 0x%llX\n", cur_std_idx, AVI->pos -16 -8)); +#endif + + for (audtr = 0; audtr < AVI->anum; audtr++) { + if (AVI->track[audtr].audio_superindex) + AVI->track[audtr].audio_superindex->stdindex[ cur_std_idx ]->qwBaseOffset = + AVI->pos -16 -8; + + } + + // now we can be sure + AVI->is_opendml++; + } + + } + + + if (video) { + avi_add_odml_index_entry_core(AVI, flags, AVI->pos, len, + AVI->video_superindex->stdindex[ AVI->video_superindex->nEntriesInUse-1 ]); + + AVI->total_frames++; + } // video + + if (audio) { + avi_add_odml_index_entry_core(AVI, flags, AVI->pos, len, + AVI->track[AVI->aptr].audio_superindex->stdindex[ + AVI->track[AVI->aptr].audio_superindex->nEntriesInUse-1 ]); + } + + + return 0; +} + +// #undef NR_IXNN_CHUNKS + +static int avi_add_index_entry(avi_t *AVI, unsigned char *tag, long flags, u64 pos, u64 len) +{ + void *ptr; + + if(AVI->n_idx>=AVI->max_idx) { + ptr = realloc((void *)AVI->idx,(AVI->max_idx+4096)*16); + + if(ptr == 0) { + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + AVI->max_idx += 4096; + AVI->idx = (unsigned char((*)[16]) ) ptr; + } + + /* Add index entry */ + + // GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] INDEX %s %ld %lu %lu\n", tag, flags, pos, len)); + + memcpy(AVI->idx[AVI->n_idx],tag,4); + long2str(AVI->idx[AVI->n_idx]+ 4,flags); + long2str(AVI->idx[AVI->n_idx]+ 8, (s32) pos); + long2str(AVI->idx[AVI->n_idx]+12, (s32) len); + + /* Update counter */ + + AVI->n_idx++; + + if(len>AVI->max_len) AVI->max_len=(u32) len; + + return 0; +} + +/* Returns 1 if more audio is in that video junk */ +int AVI_can_read_audio(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_WRITE) { return -1; } + if(!AVI->video_index) { return -1; } + if(!AVI->track[AVI->aptr].audio_index) { return -1; } + + // is it -1? the last ones got left out --tibit + //if (AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) { + if (AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks) { + return 0; + } + + if (AVI->video_pos >= AVI->video_frames) return 1; + + if (AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos < AVI->video_index[AVI->video_pos].pos) return 1; + else return 0; +} +/* + AVI_open_output_file: Open an AVI File and write a bunch + of zero bytes as space for the header. + + returns a pointer to avi_t on success, a zero pointer on error +*/ + +GF_EXPORT +avi_t* AVI_open_output_file(char * filename) +{ + avi_t *AVI; + int i; + + unsigned char AVI_header[HEADERBYTES]; + + /* Allocate the avi_t struct and zero it */ + + AVI = (avi_t *) malloc(sizeof(avi_t)); + if(AVI==0) + { + AVI_errno = AVI_ERR_NO_MEM; + return 0; + } + memset((void *)AVI,0,sizeof(avi_t)); + + AVI->fdes = gf_f64_open(filename, "w+b"); + if (!AVI->fdes ) + { + AVI_errno = AVI_ERR_OPEN; + free(AVI); + return 0; + } + + /* Write out HEADERBYTES bytes, the header will go here + when we are finished with writing */ + + for (i=0;ifdes,(char *)AVI_header,HEADERBYTES); + if (i != HEADERBYTES) + { + fclose(AVI->fdes); + AVI_errno = AVI_ERR_WRITE; + free(AVI); + return 0; + } + + AVI->pos = HEADERBYTES; + AVI->mode = AVI_MODE_WRITE; /* open for writing */ + + //init + AVI->anum = 0; + AVI->aptr = 0; + + return AVI; +} + +GF_EXPORT +void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor) +{ + /* may only be called if file is open for writing */ + + if(AVI->mode==AVI_MODE_READ) return; + + AVI->width = width; + AVI->height = height; + AVI->fps = fps; + + if(strncmp(compressor, "RGB", 3)==0) { + memset(AVI->compressor, 0, 4); + } else { + memcpy(AVI->compressor,compressor,4); + } + + AVI->compressor[4] = 0; + + avi_update_header(AVI); +} + +void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate) +{ + /* may only be called if file is open for writing */ + + if(AVI->mode==AVI_MODE_READ) return; + + //inc audio tracks + AVI->aptr=AVI->anum; + ++AVI->anum; + + if(AVI->anum > AVI_MAX_TRACKS) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] error - only %d audio tracks supported\n", AVI_MAX_TRACKS)); + exit(1); + } + + AVI->track[AVI->aptr].a_chans = channels; + AVI->track[AVI->aptr].a_rate = rate; + AVI->track[AVI->aptr].a_bits = bits; + AVI->track[AVI->aptr].a_fmt = format; + AVI->track[AVI->aptr].mp3rate = mp3rate; + + avi_update_header(AVI); +} + +#define OUT4CC(s) \ + if(nhb<=HEADERBYTES-4) memcpy(AVI_header+nhb,s,4); nhb += 4 + +#define OUTLONG(n) \ + if(nhb<=HEADERBYTES-4) long2str(AVI_header+nhb, (s32)(n)); nhb += 4 + +#define OUTSHRT(n) \ + if(nhb<=HEADERBYTES-2) { \ + AVI_header[nhb ] = (u8) ((n )&0xff); \ + AVI_header[nhb+1] = (u8) ((n>>8)&0xff); \ + } \ + nhb += 2 + +#define OUTCHR(n) \ + if(nhb<=HEADERBYTES-1) { \ + AVI_header[nhb ] = (n )&0xff; \ + } \ + nhb += 1 + +#define OUTMEM(d, s) \ + { \ + u32 s_ = (u32) (s); \ + if(nhb + s_ <= HEADERBYTES) \ + memcpy(AVI_header+nhb, (d), s_); \ + nhb += s_; \ + } + + +//ThOe write preliminary AVI file header: 0 frames, max vid/aud size +int avi_update_header(avi_t *AVI) +{ + int njunk, sampsize, hasIndex, ms_per_frame, frate, flag; + int movi_len, hdrl_start, strl_start; + u32 j; + unsigned char AVI_header[HEADERBYTES]; + u32 nhb; + unsigned long xd_size, xd_size_align2; + + //assume max size + movi_len = AVI_MAX_LEN - HEADERBYTES + 4; + + //assume index will be written + hasIndex=1; + + if(AVI->fps < 0.001) { + frate=0; + ms_per_frame=0; + } else { + frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5); + ms_per_frame=(int) (1000000/AVI->fps + 0.5); + } + + /* Prepare the file header */ + + nhb = 0; + + /* The RIFF header */ + + OUT4CC ("RIFF"); + OUTLONG(movi_len); // assume max size + OUT4CC ("AVI "); + + /* Start the header list */ + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + hdrl_start = nhb; /* Store start position */ + OUT4CC ("hdrl"); + + /* The main AVI header */ + + /* The Flags in AVI File header */ + +#define AVIF_HASINDEX 0x00000010 /* Index at end of file */ +#define AVIF_MUSTUSEINDEX 0x00000020 +#define AVIF_ISINTERLEAVED 0x00000100 +#define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */ +#define AVIF_WASCAPTUREFILE 0x00010000 +#define AVIF_COPYRIGHTED 0x00020000 + + OUT4CC ("avih"); + OUTLONG(56); /* # of bytes to follow */ + OUTLONG(ms_per_frame); /* Microseconds per frame */ + //ThOe ->0 + // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */ + OUTLONG(0); + OUTLONG(0); /* PaddingGranularity (whatever that might be) */ + /* Other sources call it 'reserved' */ + flag = AVIF_ISINTERLEAVED; + if(hasIndex) flag |= AVIF_HASINDEX; + if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX; + OUTLONG(flag); /* Flags */ + OUTLONG(0); // no frames yet + OUTLONG(0); /* InitialFrames */ + + OUTLONG(AVI->anum+1); + + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(AVI->width); /* Width */ + OUTLONG(AVI->height); /* Height */ + /* MS calls the following 'reserved': */ + OUTLONG(0); /* TimeScale: Unit used to measure time */ + OUTLONG(0); /* DataRate: Data rate of playback */ + OUTLONG(0); /* StartTime: Starting time of AVI data */ + OUTLONG(0); /* DataLength: Size of AVI data chunk */ + + + /* Start the video stream list ---------------------------------- */ + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + strl_start = nhb; /* Store start position */ + OUT4CC ("strl"); + + /* The video stream header */ + + OUT4CC ("strh"); + OUTLONG(56); /* # of bytes to follow */ + OUT4CC ("vids"); /* Type */ + OUT4CC (AVI->compressor); /* Handler */ + OUTLONG(0); /* Flags */ + OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ + OUTLONG(0); /* InitialFrames */ + OUTLONG(FRAME_RATE_SCALE); /* Scale */ + OUTLONG(frate); /* Rate: Rate/Scale == samples/second */ + OUTLONG(0); /* Start */ + OUTLONG(0); // no frames yet + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(-1); /* Quality */ + OUTLONG(0); /* SampleSize */ + OUTLONG(0); /* Frame */ + OUTLONG(0); /* Frame */ + // OUTLONG(0); /* Frame */ + //OUTLONG(0); /* Frame */ + + /* The video stream format */ + + xd_size = AVI->extradata_size; + xd_size_align2 = (AVI->extradata_size+1) & ~1; + + OUT4CC ("strf"); + OUTLONG(40 + xd_size_align2);/* # of bytes to follow */ + OUTLONG(40 + xd_size); /* Size */ + OUTLONG(AVI->width); /* Width */ + OUTLONG(AVI->height); /* Height */ + OUTSHRT(1); OUTSHRT(24); /* Planes, Count */ + OUT4CC (AVI->compressor); /* Compression */ + // ThOe (*3) + OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */ + OUTLONG(0); /* XPelsPerMeter */ + OUTLONG(0); /* YPelsPerMeter */ + OUTLONG(0); /* ClrUsed: Number of colors used */ + OUTLONG(0); /* ClrImportant: Number of colors important */ + + // write extradata + if (xd_size > 0 && AVI->extradata) { + OUTMEM(AVI->extradata, xd_size); + if (xd_size != xd_size_align2) { + OUTCHR(0); + } + } + + /* Finish stream list, i.e. put number of bytes in the list to proper pos */ + + long2str(AVI_header+strl_start-4,nhb-strl_start); + + + /* Start the audio stream list ---------------------------------- */ + + for(j=0; janum; ++j) { + + sampsize = avi_sampsize(AVI, j); + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + strl_start = nhb; /* Store start position */ + OUT4CC ("strl"); + + /* The audio stream header */ + + OUT4CC ("strh"); + OUTLONG(56); /* # of bytes to follow */ + OUT4CC ("auds"); + + // ----------- + // ThOe + OUTLONG(0); /* Format (Optionally) */ + // ----------- + + OUTLONG(0); /* Flags */ + OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ + OUTLONG(0); /* InitialFrames */ + + // ThOe /4 + OUTLONG(sampsize/4); /* Scale */ + OUTLONG(1000*AVI->track[j].mp3rate/8); + OUTLONG(0); /* Start */ + OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */ + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(-1); /* Quality */ + + // ThOe /4 + OUTLONG(sampsize/4); /* SampleSize */ + + OUTLONG(0); /* Frame */ + OUTLONG(0); /* Frame */ + // OUTLONG(0); /* Frame */ + //OUTLONG(0); /* Frame */ + + /* The audio stream format */ + + OUT4CC ("strf"); + OUTLONG(16); /* # of bytes to follow */ + OUTSHRT(AVI->track[j].a_fmt); /* Format */ + OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ + OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ + // ThOe + OUTLONG(1000*AVI->track[j].mp3rate/8); + //ThOe (/4) + + OUTSHRT(sampsize/4); /* BlockAlign */ + + + OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ + + /* Finish stream list, i.e. put number of bytes in the list to proper pos */ + + long2str(AVI_header+strl_start-4,nhb-strl_start); + } + + /* Finish header list */ + + long2str(AVI_header+hdrl_start-4,nhb-hdrl_start); + + + /* Calculate the needed amount of junk bytes, output junk */ + + njunk = HEADERBYTES - nhb - 8 - 12; + + /* Safety first: if njunk <= 0, somebody has played with + HEADERBYTES without knowing what (s)he did. + This is a fatal error */ + + if(njunk<=0) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] AVI_close_output_file: # of header bytes too small\n")); + exit(1); + } + + OUT4CC ("JUNK"); + OUTLONG(njunk); + memset(AVI_header+nhb,0,njunk); + + nhb += njunk; + + /* Start the movi list */ + + OUT4CC ("LIST"); + OUTLONG(movi_len); /* Length of list in bytes */ + OUT4CC ("movi"); + + /* Output the header, truncate the file to the number of bytes + actually written, report an error if someting goes wrong */ + + if ( gf_f64_seek(AVI->fdes,0,SEEK_SET)<0 || + avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES || + gf_f64_seek(AVI->fdes,AVI->pos,SEEK_SET)<0) + { + AVI_errno = AVI_ERR_CLOSE; + return -1; + } + + return 0; +} + + +//SLM +#ifndef S_IRUSR +#define S_IRWXU 00700 /* read, write, execute: owner */ +#define S_IRUSR 00400 /* read permission: owner */ +#define S_IWUSR 00200 /* write permission: owner */ +#define S_IXUSR 00100 /* execute permission: owner */ +#define S_IRWXG 00070 /* read, write, execute: group */ +#define S_IRGRP 00040 /* read permission: group */ +#define S_IWGRP 00020 /* write permission: group */ +#define S_IXGRP 00010 /* execute permission: group */ +#define S_IRWXO 00007 /* read, write, execute: other */ +#define S_IROTH 00004 /* read permission: other */ +#define S_IWOTH 00002 /* write permission: other */ +#define S_IXOTH 00001 /* execute permission: other */ +#endif + +/* + Write the header of an AVI file and close it. + returns 0 on success, -1 on write error. +*/ + +static int avi_close_output_file(avi_t *AVI) +{ + int ret, njunk, sampsize, hasIndex, ms_per_frame, frate, idxerror, flag; + u64 movi_len; + int hdrl_start, strl_start; + u32 j; + unsigned char AVI_header[HEADERBYTES]; + long nhb; + unsigned long xd_size, xd_size_align2; + +#ifdef INFO_LIST + long info_len; + long id_len, real_id_len; + long info_start_pos; +// time_t calptr; +#endif + + /* Calculate length of movi list */ + + // dump the rest of the index + if (AVI->is_opendml) { + int cur_std_idx = AVI->video_superindex->nEntriesInUse-1; + u32 audtr; + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML dump the rest indices\n")); +#endif + avi_ixnn_entry (AVI, AVI->video_superindex->stdindex[cur_std_idx], + &AVI->video_superindex->aIndex[cur_std_idx]); + + AVI->video_superindex->aIndex[cur_std_idx].dwDuration = + AVI->video_superindex->stdindex[cur_std_idx]->nEntriesInUse - 1; + + for (audtr = 0; audtr < AVI->anum; audtr++) { + if (!AVI->track[audtr].audio_superindex) { + // not initialized -> no index + continue; + } + avi_ixnn_entry (AVI, AVI->track[audtr].audio_superindex->stdindex[cur_std_idx], + &AVI->track[audtr].audio_superindex->aIndex[cur_std_idx]); + AVI->track[audtr].audio_superindex->aIndex[cur_std_idx].dwDuration = + AVI->track[audtr].audio_superindex->stdindex[cur_std_idx]->nEntriesInUse - 1; + if (AVI->track[audtr].a_fmt == 0x1) { + AVI->track[audtr].audio_superindex->aIndex[cur_std_idx].dwDuration *= + AVI->track[audtr].a_bits*AVI->track[audtr].a_rate*AVI->track[audtr].a_chans/800; + } + } + // The AVI->video_superindex->nEntriesInUse contains the offset + AVI->video_superindex->stdindex[ cur_std_idx+1 ]->qwBaseOffset = AVI->pos; + } + + if (AVI->is_opendml) { + // Correct! + movi_len = AVI->video_superindex->stdindex[ 1 ]->qwBaseOffset - HEADERBYTES+4 - AVI->n_idx*16 - 8; + } else { + movi_len = AVI->pos - HEADERBYTES + 4; + } + + + /* Try to ouput the index entries. This may fail e.g. if no space + is left on device. We will report this as an error, but we still + try to write the header correctly (so that the file still may be + readable in the most cases */ + + idxerror = 0; + hasIndex = 1; + if (!AVI->is_opendml) { + // GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] pos=%lu, index_len=%ld \n", AVI->pos, AVI->n_idx*16)); + ret = avi_add_chunk(AVI, (unsigned char *)"idx1", (unsigned char *)AVI->idx, AVI->n_idx*16); + hasIndex = (ret==0); + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] pos=%lu, index_len=%d\n", AVI->pos, hasIndex)); + + if(ret) { + idxerror = 1; + AVI_errno = AVI_ERR_WRITE_INDEX; + } + } + + /* Calculate Microseconds per frame */ + + if(AVI->fps < 0.001) { + frate=0; + ms_per_frame=0; + } else { + frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5); + ms_per_frame=(int) (1000000/AVI->fps + 0.5); + } + + /* Prepare the file header */ + + nhb = 0; + + /* The RIFF header */ + + OUT4CC ("RIFF"); + if (AVI->is_opendml) { + OUTLONG(AVI->video_superindex->stdindex[ 1 ]->qwBaseOffset - 8); /* # of bytes to follow */ + } else { + OUTLONG(AVI->pos - 8); /* # of bytes to follow */ + } + + OUT4CC ("AVI "); + + /* Start the header list */ + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + hdrl_start = nhb; /* Store start position */ + OUT4CC ("hdrl"); + + /* The main AVI header */ + + /* The Flags in AVI File header */ + +#define AVIF_HASINDEX 0x00000010 /* Index at end of file */ +#define AVIF_MUSTUSEINDEX 0x00000020 +#define AVIF_ISINTERLEAVED 0x00000100 +#define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */ +#define AVIF_WASCAPTUREFILE 0x00010000 +#define AVIF_COPYRIGHTED 0x00020000 + + OUT4CC ("avih"); + OUTLONG(56); /* # of bytes to follow */ + OUTLONG(ms_per_frame); /* Microseconds per frame */ + //ThOe ->0 + // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */ + OUTLONG(0); + OUTLONG(0); /* PaddingGranularity (whatever that might be) */ + /* Other sources call it 'reserved' */ + flag = AVIF_ISINTERLEAVED; + if(hasIndex) flag |= AVIF_HASINDEX; + if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX; + OUTLONG(flag); /* Flags */ + OUTLONG(AVI->video_frames); /* TotalFrames */ + OUTLONG(0); /* InitialFrames */ + + OUTLONG(AVI->anum+1); +// if (AVI->track[0].audio_bytes) +// { OUTLONG(2); } /* Streams */ +// else +// { OUTLONG(1); } /* Streams */ + + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(AVI->width); /* Width */ + OUTLONG(AVI->height); /* Height */ + /* MS calls the following 'reserved': */ + OUTLONG(0); /* TimeScale: Unit used to measure time */ + OUTLONG(0); /* DataRate: Data rate of playback */ + OUTLONG(0); /* StartTime: Starting time of AVI data */ + OUTLONG(0); /* DataLength: Size of AVI data chunk */ + + + /* Start the video stream list ---------------------------------- */ + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + strl_start = nhb; /* Store start position */ + OUT4CC ("strl"); + + /* The video stream header */ + + OUT4CC ("strh"); + OUTLONG(56); /* # of bytes to follow */ + OUT4CC ("vids"); /* Type */ + OUT4CC (AVI->compressor); /* Handler */ + OUTLONG(0); /* Flags */ + OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ + OUTLONG(0); /* InitialFrames */ + OUTLONG(FRAME_RATE_SCALE); /* Scale */ + OUTLONG(frate); /* Rate: Rate/Scale == samples/second */ + OUTLONG(0); /* Start */ + OUTLONG(AVI->video_frames); /* Length */ + OUTLONG(AVI->max_len); /* SuggestedBufferSize */ + OUTLONG(0); /* Quality */ + OUTLONG(0); /* SampleSize */ + OUTLONG(0); /* Frame */ + OUTLONG(0); /* Frame */ + //OUTLONG(0); /* Frame */ + //OUTLONG(0); /* Frame */ + + /* The video stream format */ + + xd_size = AVI->extradata_size; + xd_size_align2 = (AVI->extradata_size+1) & ~1; + + OUT4CC ("strf"); + OUTLONG(40 + xd_size_align2);/* # of bytes to follow */ + OUTLONG(40 + xd_size); /* Size */ + OUTLONG(AVI->width); /* Width */ + OUTLONG(AVI->height); /* Height */ + OUTSHRT(1); OUTSHRT(24); /* Planes, Count */ + OUT4CC (AVI->compressor); /* Compression */ + // ThOe (*3) + OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */ + OUTLONG(0); /* XPelsPerMeter */ + OUTLONG(0); /* YPelsPerMeter */ + OUTLONG(0); /* ClrUsed: Number of colors used */ + OUTLONG(0); /* ClrImportant: Number of colors important */ + + // write extradata if present + if (xd_size > 0 && AVI->extradata) { + OUTMEM(AVI->extradata, xd_size); + if (xd_size != xd_size_align2) { + OUTCHR(0); + } + } + + // dump index of indices for audio + if (AVI->is_opendml) { + u32 k; + + OUT4CC(AVI->video_superindex->fcc); + OUTLONG(2+1+1+4+4+3*4 + AVI->video_superindex->nEntriesInUse * (8+4+4)); + OUTSHRT(AVI->video_superindex->wLongsPerEntry); + OUTCHR(AVI->video_superindex->bIndexSubType); + OUTCHR(AVI->video_superindex->bIndexType); + OUTLONG(AVI->video_superindex->nEntriesInUse); + OUT4CC(AVI->video_superindex->dwChunkId); + OUTLONG(0); + OUTLONG(0); + OUTLONG(0); + + + for (k = 0; k < AVI->video_superindex->nEntriesInUse; k++) { + u32 r = (u32) ((AVI->video_superindex->aIndex[k].qwOffset >> 32) & 0xffffffff); + u32 s = (u32) ((AVI->video_superindex->aIndex[k].qwOffset) & 0xffffffff); + + OUTLONG(s); + OUTLONG(r); + OUTLONG(AVI->video_superindex->aIndex[k].dwSize); + OUTLONG(AVI->video_superindex->aIndex[k].dwDuration); + } + + } + + /* Finish stream list, i.e. put number of bytes in the list to proper pos */ + + long2str(AVI_header+strl_start-4,nhb-strl_start); + + /* Start the audio stream list ---------------------------------- */ + + for(j=0; janum; ++j) { + + //if (AVI->track[j].a_chans && AVI->track[j].audio_bytes) + { + unsigned long nBlockAlign = 0; + unsigned long avgbsec = 0; + unsigned long scalerate = 0; + + sampsize = avi_sampsize(AVI, j); + sampsize = AVI->track[j].a_fmt==0x1?sampsize*4:sampsize; + + nBlockAlign = (AVI->track[j].a_rate<32000)?576:1152; + /* + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] XXX sampsize (%d) block (%ld) rate (%ld) audio_bytes (%ld) mp3rate(%ld,%ld)\n", + sampsize, nBlockAlign, AVI->track[j].a_rate, + (long int)AVI->track[j].audio_bytes, + 1000*AVI->track[j].mp3rate/8, AVI->track[j].mp3rate)); + */ + + if (AVI->track[j].a_fmt==0x1) { + sampsize = (AVI->track[j].a_chans<2)?sampsize/2:sampsize; + avgbsec = AVI->track[j].a_rate*sampsize/4; + scalerate = AVI->track[j].a_rate*sampsize/4; + } else { + avgbsec = 1000*AVI->track[j].mp3rate/8; + scalerate = 1000*AVI->track[j].mp3rate/8; + } + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + strl_start = nhb; /* Store start position */ + OUT4CC ("strl"); + + /* The audio stream header */ + + OUT4CC ("strh"); + OUTLONG(56); /* # of bytes to follow */ + OUT4CC ("auds"); + + // ----------- + // ThOe + OUTLONG(0); /* Format (Optionally) */ + // ----------- + + OUTLONG(0); /* Flags */ + OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ + OUTLONG(0); /* InitialFrames */ + + // VBR + if (AVI->track[j].a_fmt == 0x55 && AVI->track[j].a_vbr) { + OUTLONG(nBlockAlign); /* Scale */ + OUTLONG(AVI->track[j].a_rate); /* Rate */ + OUTLONG(0); /* Start */ + OUTLONG(AVI->track[j].audio_chunks); /* Length */ + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(0); /* Quality */ + OUTLONG(0); /* SampleSize */ + OUTLONG(0); /* Frame */ + OUTLONG(0); /* Frame */ + } else { + OUTLONG(sampsize/4); /* Scale */ + OUTLONG(scalerate); /* Rate */ + OUTLONG(0); /* Start */ + OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */ + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(0xffffffff); /* Quality */ + OUTLONG(sampsize/4); /* SampleSize */ + OUTLONG(0); /* Frame */ + OUTLONG(0); /* Frame */ + } + + /* The audio stream format */ + + OUT4CC ("strf"); + + if (AVI->track[j].a_fmt == 0x55 && AVI->track[j].a_vbr) { + + OUTLONG(30); /* # of bytes to follow */ // mplayer writes 28 + OUTSHRT(AVI->track[j].a_fmt); /* Format */ // 2 + OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ // 2 + OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ // 4 + //ThOe/tibit + OUTLONG(1000*AVI->track[j].mp3rate/8); /* maybe we should write an avg. */ // 4 + OUTSHRT(nBlockAlign); /* BlockAlign */ // 2 + OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ // 2 + + OUTSHRT(12); /* cbSize */ // 2 + OUTSHRT(1); /* wID */ // 2 + OUTLONG(2); /* fdwFlags */ // 4 + OUTSHRT(nBlockAlign); /* nBlockSize */ // 2 + OUTSHRT(1); /* nFramesPerBlock */ // 2 + OUTSHRT(0); /* nCodecDelay */ // 2 + + } else if (AVI->track[j].a_fmt == 0x55 && !AVI->track[j].a_vbr) { + + OUTLONG(30); /* # of bytes to follow */ + OUTSHRT(AVI->track[j].a_fmt); /* Format */ + OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ + OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ + //ThOe/tibit + OUTLONG(1000*AVI->track[j].mp3rate/8); + OUTSHRT(sampsize/4); /* BlockAlign */ + OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ + + OUTSHRT(12); /* cbSize */ + OUTSHRT(1); /* wID */ + OUTLONG(2); /* fdwFlags */ + OUTSHRT(nBlockAlign); /* nBlockSize */ + OUTSHRT(1); /* nFramesPerBlock */ + OUTSHRT(0); /* nCodecDelay */ + + } else { + + OUTLONG(18); /* # of bytes to follow */ + OUTSHRT(AVI->track[j].a_fmt); /* Format */ + OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ + OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ + //ThOe/tibit + OUTLONG(avgbsec); /* Avg bytes/sec */ + OUTSHRT(sampsize/4); /* BlockAlign */ + OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ + OUTSHRT(0); /* cbSize */ + + } + } + if (AVI->is_opendml) { + u32 k; + + if (!AVI->track[j].audio_superindex) { + // not initialized -> no index + continue; + } + + OUT4CC(AVI->track[j].audio_superindex->fcc); /* "indx" */ + OUTLONG(2+1+1+4+4+3*4 + AVI->track[j].audio_superindex->nEntriesInUse * (8+4+4)); + OUTSHRT(AVI->track[j].audio_superindex->wLongsPerEntry); + OUTCHR(AVI->track[j].audio_superindex->bIndexSubType); + OUTCHR(AVI->track[j].audio_superindex->bIndexType); + OUTLONG(AVI->track[j].audio_superindex->nEntriesInUse); + OUT4CC(AVI->track[j].audio_superindex->dwChunkId); + OUTLONG(0); OUTLONG(0); OUTLONG(0); + + for (k = 0; k < AVI->track[j].audio_superindex->nEntriesInUse; k++) { + u32 r = (u32) ((AVI->track[j].audio_superindex->aIndex[k].qwOffset >> 32) & 0xffffffff); + u32 s = (u32) ((AVI->track[j].audio_superindex->aIndex[k].qwOffset) & 0xffffffff); + + /* + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] AUD[%d] NrEntries %d/%ld (%c%c%c%c) |0x%llX|%ld|%ld| \n", j, k, + AVI->track[j].audio_superindex->nEntriesInUse, + AVI->track[j].audio_superindex->dwChunkId[0], AVI->track[j].audio_superindex->dwChunkId[1], + AVI->track[j].audio_superindex->dwChunkId[2], AVI->track[j].audio_superindex->dwChunkId[3], + AVI->track[j].audio_superindex->aIndex[k].qwOffset, + AVI->track[j].audio_superindex->aIndex[k].dwSize, + AVI->track[j].audio_superindex->aIndex[k].dwDuration + )); + */ + + OUTLONG(s); + OUTLONG(r); + OUTLONG(AVI->track[j].audio_superindex->aIndex[k].dwSize); + OUTLONG(AVI->track[j].audio_superindex->aIndex[k].dwDuration); + } + } + /* Finish stream list, i.e. put number of bytes in the list to proper pos */ + long2str(AVI_header+strl_start-4,nhb-strl_start); + } + + if (AVI->is_opendml) { + OUT4CC("LIST"); + OUTLONG(16); + OUT4CC("odml"); + OUT4CC("dmlh"); + OUTLONG(4); + OUTLONG(AVI->total_frames); + } + + /* Finish header list */ + + long2str(AVI_header+hdrl_start-4,nhb-hdrl_start); + + + // add INFO list --- (0.6.0pre4) + +#ifdef INFO_LIST + OUT4CC ("LIST"); + + info_start_pos = nhb; + info_len = MAX_INFO_STRLEN + 12; + OUTLONG(info_len); // rewritten later + OUT4CC ("INFO"); + + OUT4CC ("ISFT"); + //OUTLONG(MAX_INFO_STRLEN); + memset(id_str, 0, MAX_INFO_STRLEN); + + sprintf(id_str, "%s-%s", PACKAGE, VERSION); + real_id_len = id_len = strlen(id_str)+1; + if (id_len&1) id_len++; + + OUTLONG(real_id_len); + + memset(AVI_header+nhb, 0, id_len); + memcpy(AVI_header+nhb, id_str, id_len); + nhb += id_len; + + info_len = 0; + + // write correct len + long2str(AVI_header+info_start_pos, info_len + id_len + 4+4+4); + + nhb += info_len; + +// OUT4CC ("ICMT"); +// OUTLONG(MAX_INFO_STRLEN); + +// calptr=time(NULL); +// sprintf(id_str, "\t%s %s", ctime(&calptr), ""); +// memset(AVI_header+nhb, 0, MAX_INFO_STRLEN); +// memcpy(AVI_header+nhb, id_str, 25); +// nhb += MAX_INFO_STRLEN; +#endif + + // ---------------------------- + + /* Calculate the needed amount of junk bytes, output junk */ + + njunk = HEADERBYTES - nhb - 8 - 12; + + /* Safety first: if njunk <= 0, somebody has played with + HEADERBYTES without knowing what (s)he did. + This is a fatal error */ + + if(njunk<=0) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] AVI_close_output_file: # of header bytes too small\n")); + exit(1); + } + + OUT4CC ("JUNK"); + OUTLONG(njunk); + memset(AVI_header+nhb,0,njunk); + + nhb += njunk; + + /* Start the movi list */ + + OUT4CC ("LIST"); + OUTLONG(movi_len); /* Length of list in bytes */ + OUT4CC ("movi"); + + /* Output the header, truncate the file to the number of bytes + actually written, report an error if someting goes wrong */ + + if ( gf_f64_seek(AVI->fdes,0,SEEK_SET)<0 || + avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES +// || ftruncate(AVI->fdes,AVI->pos)<0 + ) + { + AVI_errno = AVI_ERR_CLOSE; + return -1; + } + + + // Fix up the empty additional RIFF and LIST chunks + if (AVI->is_opendml) { + u32 k; + char f[4]; + u32 len; + + for (k=1; kvideo_superindex->nEntriesInUse; k++) { + // the len of the RIFF Chunk + gf_f64_seek(AVI->fdes, AVI->video_superindex->stdindex[k]->qwBaseOffset+4, SEEK_SET); + len = (u32) (AVI->video_superindex->stdindex[k+1]->qwBaseOffset - AVI->video_superindex->stdindex[k]->qwBaseOffset - 8); + long2str((unsigned char *)f, len); + avi_write(AVI->fdes, f, 4); + + // len of the LIST/movi chunk + gf_f64_seek(AVI->fdes, 8, SEEK_CUR); + len -= 12; + long2str((unsigned char *)f, len); + avi_write(AVI->fdes, f, 4); + } + } + + + if(idxerror) return -1; + + return 0; +} + +/* + AVI_write_data: + Add video or audio data to the file; + + Return values: + 0 No error; + -1 Error, AVI_errno is set appropriatly; + +*/ + +static int avi_write_data(avi_t *AVI, char *data, unsigned long length, int audio, int keyframe) +{ + int n = 0; + + unsigned char astr[5]; + + // transcode core itself checks for the size -- unneeded and + // does harm to xvid 2pass encodes where the first pass can get + // _very_ large -- tibit. + +#if 0 + /* Check for maximum file length */ + + if ( (AVI->pos + 8 + length + 8 + (AVI->n_idx+1)*16) > AVI_MAX_LEN ) { + AVI_errno = AVI_ERR_SIZELIM; + return -1; + } +#endif + + /* Add index entry */ + + //set tag for current audio track + sprintf((char *)astr, "0%1dwb", (int)(AVI->aptr+1)); + + if(audio) { + if (!AVI->is_opendml) n = avi_add_index_entry(AVI,astr,0x10,AVI->pos,length); + n += avi_add_odml_index_entry(AVI,astr,0x10,AVI->pos,length); + } else { + if (!AVI->is_opendml) n = avi_add_index_entry(AVI,(unsigned char *)"00db",((keyframe)?0x10:0x0),AVI->pos,length); + n += avi_add_odml_index_entry(AVI,(unsigned char *)"00db",((keyframe)?0x10:0x0),AVI->pos,length); + } + + if(n) return -1; + + /* Output tag and data */ + + if(audio) + n = avi_add_chunk(AVI,(unsigned char *)astr, (unsigned char *)data, length); + else + n = avi_add_chunk(AVI,(unsigned char *)"00db", (unsigned char *)data, length); + + if (n) return -1; + + return 0; +} + +GF_EXPORT +int AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe) +{ + s64 pos; + + if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + pos = AVI->pos; + + if(avi_write_data(AVI,data,bytes,0,keyframe)) return -1; + + AVI->last_pos = pos; + AVI->last_len = bytes; + AVI->video_frames++; + return 0; +} + +int AVI_dup_frame(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + if(AVI->last_pos==0) return 0; /* No previous real frame */ + if(avi_add_index_entry(AVI,(unsigned char *)"00db",0x10,AVI->last_pos,AVI->last_len)) return -1; + AVI->video_frames++; + AVI->must_use_index = 1; + return 0; +} + +int AVI_write_audio(avi_t *AVI, char *data, long bytes) +{ + if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + if( avi_write_data(AVI,data,bytes,1,0) ) return -1; + AVI->track[AVI->aptr].audio_bytes += bytes; + AVI->track[AVI->aptr].audio_chunks++; + return 0; +} + + +int AVI_append_audio(avi_t *AVI, char *data, long bytes) +{ + + // won't work for >2gb + long i, length, pos; + unsigned char c[4]; + + if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + // update last index entry: + + --AVI->n_idx; + length = str2ulong(AVI->idx[AVI->n_idx]+12); + pos = str2ulong(AVI->idx[AVI->n_idx]+8); + + //update; + long2str(AVI->idx[AVI->n_idx]+12,length+bytes); + + ++AVI->n_idx; + + AVI->track[AVI->aptr].audio_bytes += bytes; + + //update chunk header + gf_f64_seek(AVI->fdes, pos+4, SEEK_SET); + long2str(c, length+bytes); + avi_write(AVI->fdes, (char *)c, 4); + + gf_f64_seek(AVI->fdes, pos+8+length, SEEK_SET); + + i=PAD_EVEN(length + bytes); + + bytes = i - length; + avi_write(AVI->fdes, data, bytes); + AVI->pos = pos + 8 + i; + + return 0; +} + + +u64 AVI_bytes_remain(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_READ) return 0; + + return ( AVI_MAX_LEN - (AVI->pos + 8 + 16*AVI->n_idx)); +} + +u64 AVI_bytes_written(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_READ) return 0; + + return (AVI->pos + 8 + 16*AVI->n_idx); +} + +int AVI_set_audio_track(avi_t *AVI, u32 track) +{ + + if (track + 1 > AVI->anum) return(-1); + + //this info is not written to file anyway + AVI->aptr=track; + return 0; +} + +int AVI_get_audio_track(avi_t *AVI) +{ + return(AVI->aptr); +} + +void AVI_set_audio_vbr(avi_t *AVI, long is_vbr) +{ + AVI->track[AVI->aptr].a_vbr = is_vbr; +} + +long AVI_get_audio_vbr(avi_t *AVI) +{ + return(AVI->track[AVI->aptr].a_vbr); +} + + +/******************************************************************* + * * + * Utilities for reading video and audio from an AVI File * + * * + *******************************************************************/ + +GF_EXPORT +int AVI_close(avi_t *AVI) +{ + int ret; + u32 j; + + /* If the file was open for writing, the header and index still have + to be written */ + + if(AVI->mode == AVI_MODE_WRITE) + ret = avi_close_output_file(AVI); + else + ret = 0; + + /* Even if there happened an error, we first clean up */ + + fclose(AVI->fdes); + if(AVI->idx) free(AVI->idx); + if(AVI->video_index) free(AVI->video_index); + if(AVI->video_superindex) { + if(AVI->video_superindex->aIndex) free(AVI->video_superindex->aIndex); + if (AVI->video_superindex->stdindex) { + for (j=0; j < NR_IXNN_CHUNKS; j++) { + if (AVI->video_superindex->stdindex[j]->aIndex) + free(AVI->video_superindex->stdindex[j]->aIndex); + free(AVI->video_superindex->stdindex[j]); + } + free(AVI->video_superindex->stdindex); + } + free(AVI->video_superindex); + } + + for (j=0; janum; j++) + { + if(AVI->track[j].audio_index) free(AVI->track[j].audio_index); + if(AVI->track[j].audio_superindex) { + if(AVI->track[j].audio_superindex->aIndex) free(AVI->track[j].audio_superindex->aIndex); + free(AVI->track[j].audio_superindex); + } + } + + if (AVI->bitmap_info_header) + free(AVI->bitmap_info_header); + for (j = 0; j < AVI->anum; j++) + if (AVI->wave_format_ex[j]) + free(AVI->wave_format_ex[j]); + + free(AVI); + AVI=NULL; + + return ret; +} + + +#define ERR_EXIT(x) \ +{ \ + AVI_close(AVI); \ + AVI_errno = x; \ + return 0; \ +} + + +avi_t *AVI_open_input_file(char *filename, int getIndex) +{ + avi_t *AVI=NULL; + + /* Create avi_t structure */ + + AVI = (avi_t *) malloc(sizeof(avi_t)); + if(AVI==NULL) + { + AVI_errno = AVI_ERR_NO_MEM; + return 0; + } + memset((void *)AVI,0,sizeof(avi_t)); + + AVI->mode = AVI_MODE_READ; /* open for reading */ + + /* Open the file */ + + AVI->fdes = gf_f64_open(filename,"rb"); + if(!AVI->fdes ) + { + AVI_errno = AVI_ERR_OPEN; + free(AVI); + return 0; + } + + AVI_errno = 0; + avi_parse_input_file(AVI, getIndex); + + if (AVI != NULL && !AVI_errno) { + AVI->aptr=0; //reset + } + + if (AVI_errno) return NULL; + + return AVI; +} + +avi_t *AVI_open_fd(FILE *fd, int getIndex) +{ + avi_t *AVI=NULL; + + /* Create avi_t structure */ + + AVI = (avi_t *) malloc(sizeof(avi_t)); + if(AVI==NULL) + { + AVI_errno = AVI_ERR_NO_MEM; + return 0; + } + memset((void *)AVI,0,sizeof(avi_t)); + + AVI->mode = AVI_MODE_READ; /* open for reading */ + + // file alread open + AVI->fdes = fd; + + AVI_errno = 0; + avi_parse_input_file(AVI, getIndex); + + if (AVI != NULL && !AVI_errno) { + AVI->aptr=0; //reset + } + + if (AVI_errno) + return AVI=NULL; + else + return AVI; +} + +int avi_parse_input_file(avi_t *AVI, int getIndex) +{ + long i, rate, scale, idx_type; + s64 n; + unsigned char *hdrl_data; + long header_offset=0, hdrl_len=0; + long nvi, nai[AVI_MAX_TRACKS], ioff; + u64 tot[AVI_MAX_TRACKS]; + u32 j; + int lasttag = 0; + int vids_strh_seen = 0; + int vids_strf_seen = 0; + int auds_strh_seen = 0; + // int auds_strf_seen = 0; + int num_stream = 0; + char data[256]; + s64 oldpos=-1, newpos=-1; + + long aud_chunks = 0; + /* Read first 12 bytes and check that this is an AVI file */ + + if( avi_read(AVI->fdes,data,12) != 12 ) ERR_EXIT(AVI_ERR_READ) + + if( strnicmp(data ,"RIFF",4) !=0 || + strnicmp(data+8,"AVI ",4) !=0 ) ERR_EXIT(AVI_ERR_NO_AVI) + + /* Go through the AVI file and extract the header list, + the start position of the 'movi' list and an optionally + present idx1 tag */ + + hdrl_data = 0; + + + while(1) + { + if( avi_read(AVI->fdes,data,8) != 8 ) break; /* We assume it's EOF */ + newpos = gf_f64_tell(AVI->fdes); + if(oldpos==newpos) { + /* This is a broken AVI stream... */ + return -1; + } + oldpos=newpos; + + n = str2ulong((unsigned char *)data+4); + n = PAD_EVEN(n); + + if(strnicmp(data,"LIST",4) == 0) + { + if( avi_read(AVI->fdes,data,4) != 4 ) ERR_EXIT(AVI_ERR_READ) + n -= 4; + if(strnicmp(data,"hdrl",4) == 0) + { + hdrl_len = (u32) n; + hdrl_data = (unsigned char *) malloc((u32)n); + if(hdrl_data==0) ERR_EXIT(AVI_ERR_NO_MEM); + + // offset of header + + header_offset = (u32) gf_f64_tell(AVI->fdes); + + if( avi_read(AVI->fdes,(char *)hdrl_data, (u32) n) != n ) ERR_EXIT(AVI_ERR_READ) + } + else if(strnicmp(data,"movi",4) == 0) + { + AVI->movi_start = gf_f64_tell(AVI->fdes); + if (gf_f64_seek(AVI->fdes,n,SEEK_CUR)==(u64)-1) break; + } + else + if (gf_f64_seek(AVI->fdes,n,SEEK_CUR)==(u64)-1) break; + } + else if(strnicmp(data,"idx1",4) == 0) + { + /* n must be a multiple of 16, but the reading does not + break if this is not the case */ + + AVI->n_idx = AVI->max_idx = (u32) (n/16); + AVI->idx = (unsigned char((*)[16]) ) malloc((u32)n); + if(AVI->idx==0) ERR_EXIT(AVI_ERR_NO_MEM) + if(avi_read(AVI->fdes, (char *) AVI->idx, (u32) n) != n ) { + free ( AVI->idx); AVI->idx=NULL; + AVI->n_idx = 0; + } + } + else + gf_f64_seek(AVI->fdes,n,SEEK_CUR); + } + + if(!hdrl_data ) ERR_EXIT(AVI_ERR_NO_HDRL) + if(!AVI->movi_start) ERR_EXIT(AVI_ERR_NO_MOVI) + + /* Interpret the header list */ + + for(i=0;icompressor,hdrl_data+i+4,4); + AVI->compressor[4] = 0; + + // ThOe + AVI->v_codech_off = header_offset + i+4; + + scale = str2ulong(hdrl_data+i+20); + rate = str2ulong(hdrl_data+i+24); + if(scale!=0) AVI->fps = (double)rate/(double)scale; + AVI->video_frames = str2ulong(hdrl_data+i+32); + AVI->video_strn = num_stream; + AVI->max_len = 0; + vids_strh_seen = 1; + lasttag = 1; /* vids */ + memcpy(&AVI->video_stream_header, hdrl_data + i, + sizeof(alAVISTREAMHEADER)); + } + else if (strnicmp ((char *)hdrl_data+i,"auds",4) ==0 && ! auds_strh_seen) + { + + //inc audio tracks + AVI->aptr=AVI->anum; + ++AVI->anum; + + if(AVI->anum > AVI_MAX_TRACKS) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] error - only %d audio tracks supported\n", AVI_MAX_TRACKS)); + return(-1); + } + + AVI->track[AVI->aptr].audio_bytes = str2ulong(hdrl_data+i+32)*avi_sampsize(AVI, 0); + AVI->track[AVI->aptr].audio_strn = num_stream; + + // if samplesize==0 -> vbr + AVI->track[AVI->aptr].a_vbr = !str2ulong(hdrl_data+i+44); + + AVI->track[AVI->aptr].padrate = str2ulong(hdrl_data+i+24); + memcpy(&AVI->stream_headers[AVI->aptr], hdrl_data + i, + sizeof(alAVISTREAMHEADER)); + + // auds_strh_seen = 1; + lasttag = 2; /* auds */ + + // ThOe + AVI->track[AVI->aptr].a_codech_off = header_offset + i; + + } + else if (strnicmp ((char*)hdrl_data+i,"iavs",4) ==0 && ! auds_strh_seen) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] AVILIB: error - DV AVI Type 1 no supported\n")); + return (-1); + } + else + lasttag = 0; + num_stream++; + } + else if(strnicmp((char*)hdrl_data+i,"dmlh",4) == 0) { + AVI->total_frames = str2ulong(hdrl_data+i+8); +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] real number of frames %d\n", AVI->total_frames)); +#endif + i += 8; + } + else if(strnicmp((char *)hdrl_data+i,"strf",4)==0) + { + i += 8; + if(lasttag == 1) + { + alBITMAPINFOHEADER bih; + + memcpy(&bih, hdrl_data + i, sizeof(alBITMAPINFOHEADER)); + AVI->bitmap_info_header = (alBITMAPINFOHEADER *) + malloc(str2ulong((unsigned char *)&bih.bi_size)); + if (AVI->bitmap_info_header != NULL) + memcpy(AVI->bitmap_info_header, hdrl_data + i, + str2ulong((unsigned char *)&bih.bi_size)); + + AVI->width = str2ulong(hdrl_data+i+4); + AVI->height = str2ulong(hdrl_data+i+8); + vids_strf_seen = 1; + //ThOe + AVI->v_codecf_off = header_offset + i+16; + + memcpy(AVI->compressor2, hdrl_data+i+16, 4); + AVI->compressor2[4] = 0; + + } + else if(lasttag == 2) + { + alWAVEFORMATEX *wfe; + char *nwfe; + int wfes; + + if ((u32) (hdrl_len - i) < sizeof(alWAVEFORMATEX)) + wfes = hdrl_len - i; + else + wfes = sizeof(alWAVEFORMATEX); + wfe = (alWAVEFORMATEX *)malloc(sizeof(alWAVEFORMATEX)); + if (wfe != NULL) { + memset(wfe, 0, sizeof(alWAVEFORMATEX)); + memcpy(wfe, hdrl_data + i, wfes); + if (str2ushort((unsigned char *)&wfe->cb_size) != 0) { + nwfe = (char *) + realloc(wfe, sizeof(alWAVEFORMATEX) + + str2ushort((unsigned char *)&wfe->cb_size)); + if (nwfe != 0) { + s64 lpos = gf_f64_tell(AVI->fdes); + gf_f64_seek(AVI->fdes, header_offset + i + sizeof(alWAVEFORMATEX), + SEEK_SET); + wfe = (alWAVEFORMATEX *)nwfe; + nwfe = &nwfe[sizeof(alWAVEFORMATEX)]; + avi_read(AVI->fdes, nwfe, + str2ushort((unsigned char *)&wfe->cb_size)); + gf_f64_seek(AVI->fdes, lpos, SEEK_SET); + } + } + AVI->wave_format_ex[AVI->aptr] = wfe; + } + + AVI->track[AVI->aptr].a_fmt = str2ushort(hdrl_data+i ); + + //ThOe + AVI->track[AVI->aptr].a_codecf_off = header_offset + i; + + AVI->track[AVI->aptr].a_chans = str2ushort(hdrl_data+i+2); + AVI->track[AVI->aptr].a_rate = str2ulong (hdrl_data+i+4); + //ThOe: read mp3bitrate + AVI->track[AVI->aptr].mp3rate = 8*str2ulong(hdrl_data+i+8)/1000; + //:ThOe + AVI->track[AVI->aptr].a_bits = str2ushort(hdrl_data+i+14); + // auds_strf_seen = 1; + } + } + else if(strnicmp((char*)hdrl_data+i,"indx",4) == 0) { + char *a; + + if(lasttag == 1) // V I D E O + { + + a = (char*)hdrl_data+i; + + AVI->video_superindex = (avisuperindex_chunk *) malloc (sizeof (avisuperindex_chunk)); + memcpy (AVI->video_superindex->fcc, a, 4); a += 4; + AVI->video_superindex->dwSize = str2ulong((unsigned char *)a); a += 4; + AVI->video_superindex->wLongsPerEntry = str2ushort((unsigned char *)a); a += 2; + AVI->video_superindex->bIndexSubType = *a; a += 1; + AVI->video_superindex->bIndexType = *a; a += 1; + AVI->video_superindex->nEntriesInUse = str2ulong((unsigned char *)a); a += 4; + memcpy (AVI->video_superindex->dwChunkId, a, 4); a += 4; + + // 3 * reserved + a += 4; a += 4; a += 4; + + if (AVI->video_superindex->bIndexSubType != 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Invalid Header, bIndexSubType != 0\n")); + } + + AVI->video_superindex->aIndex = (avisuperindex_entry*) + malloc (AVI->video_superindex->wLongsPerEntry * AVI->video_superindex->nEntriesInUse * sizeof (u32)); + + // position of ix## chunks + for (j=0; jvideo_superindex->nEntriesInUse; ++j) { + AVI->video_superindex->aIndex[j].qwOffset = str2ullong ((unsigned char*)a); a += 8; + AVI->video_superindex->aIndex[j].dwSize = str2ulong ((unsigned char*)a); a += 4; + AVI->video_superindex->aIndex[j].dwDuration = str2ulong ((unsigned char*)a); a += 4; + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d] 0x%llx 0x%lx %lu\n", j, + (unsigned long long)AVI->video_superindex->aIndex[j].qwOffset, + (unsigned long)AVI->video_superindex->aIndex[j].dwSize, + (unsigned long)AVI->video_superindex->aIndex[j].dwDuration)); +#endif + } + + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] FOURCC \"%c%c%c%c\"\n", AVI->video_superindex->fcc[0], AVI->video_superindex->fcc[1], + AVI->video_superindex->fcc[2], AVI->video_superindex->fcc[3])); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] LEN \"%ld\"\n", (long)AVI->video_superindex->dwSize)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] wLongsPerEntry \"%d\"\n", AVI->video_superindex->wLongsPerEntry)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexSubType \"%d\"\n", AVI->video_superindex->bIndexSubType)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexType \"%d\"\n", AVI->video_superindex->bIndexType)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] nEntriesInUse \"%ld\"\n", (long)AVI->video_superindex->nEntriesInUse)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] dwChunkId \"%c%c%c%c\"\n", AVI->video_superindex->dwChunkId[0], AVI->video_superindex->dwChunkId[1], + AVI->video_superindex->dwChunkId[2], AVI->video_superindex->dwChunkId[3])); +#endif + + AVI->is_opendml = 1; + + } + else if(lasttag == 2) // A U D I O + { + + a = (char*) hdrl_data+i; + + AVI->track[AVI->aptr].audio_superindex = (avisuperindex_chunk *) malloc (sizeof (avisuperindex_chunk)); + memcpy (AVI->track[AVI->aptr].audio_superindex->fcc, a, 4); a += 4; + AVI->track[AVI->aptr].audio_superindex->dwSize = str2ulong((unsigned char*)a); a += 4; + AVI->track[AVI->aptr].audio_superindex->wLongsPerEntry = str2ushort((unsigned char*)a); a += 2; + AVI->track[AVI->aptr].audio_superindex->bIndexSubType = *a; a += 1; + AVI->track[AVI->aptr].audio_superindex->bIndexType = *a; a += 1; + AVI->track[AVI->aptr].audio_superindex->nEntriesInUse = str2ulong((unsigned char*)a); a += 4; + memcpy (AVI->track[AVI->aptr].audio_superindex->dwChunkId, a, 4); a += 4; + + // 3 * reserved + a += 4; a += 4; a += 4; + + if (AVI->track[AVI->aptr].audio_superindex->bIndexSubType != 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Invalid Header, bIndexSubType != 0\n")); + } + + AVI->track[AVI->aptr].audio_superindex->aIndex = (avisuperindex_entry*) + malloc (AVI->track[AVI->aptr].audio_superindex->wLongsPerEntry * + AVI->track[AVI->aptr].audio_superindex->nEntriesInUse * sizeof (u32)); + + // position of ix## chunks + for (j=0; jtrack[AVI->aptr].audio_superindex->nEntriesInUse; ++j) { + AVI->track[AVI->aptr].audio_superindex->aIndex[j].qwOffset = str2ullong ((unsigned char*)a); a += 8; + AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwSize = str2ulong ((unsigned char*)a); a += 4; + AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwDuration = str2ulong ((unsigned char*)a); a += 4; + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d] 0x%llx 0x%lx %lu\n", j, + (unsigned long long)AVI->track[AVI->aptr].audio_superindex->aIndex[j].qwOffset, + (unsigned long)AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwSize, + (unsigned long)AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwDuration)); +#endif + } + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] FOURCC \"%.4s\"\n", AVI->track[AVI->aptr].audio_superindex->fcc)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] LEN \"%ld\"\n", (long)AVI->track[AVI->aptr].audio_superindex->dwSize)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] wLongsPerEntry \"%d\"\n", AVI->track[AVI->aptr].audio_superindex->wLongsPerEntry)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexSubType \"%d\"\n", AVI->track[AVI->aptr].audio_superindex->bIndexSubType)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexType \"%d\"\n", AVI->track[AVI->aptr].audio_superindex->bIndexType)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] nEntriesInUse \"%ld\"\n", (long)AVI->track[AVI->aptr].audio_superindex->nEntriesInUse)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] dwChunkId \"%.4s\"\n", AVI->track[AVI->aptr].audio_superindex->dwChunkId[0])); +#endif + + } + i += 8; + } + else if((strnicmp((char*)hdrl_data+i,"JUNK",4) == 0) || + (strnicmp((char*)hdrl_data+i,"strn",4) == 0) || + (strnicmp((char*)hdrl_data+i,"vprp",4) == 0)){ + i += 8; + // do not reset lasttag + } else + { + i += 8; + lasttag = 0; + } + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] adding %ld bytes\n", (long int)n)); + + i += (u32) n; + } + + free(hdrl_data); + + if(!vids_strh_seen || !vids_strf_seen) ERR_EXIT(AVI_ERR_NO_VIDS) + + AVI->video_tag[0] = AVI->video_strn/10 + '0'; + AVI->video_tag[1] = AVI->video_strn%10 + '0'; + AVI->video_tag[2] = 'd'; + AVI->video_tag[3] = 'b'; + + /* Audio tag is set to "99wb" if no audio present */ + if(!AVI->track[0].a_chans) AVI->track[0].audio_strn = 99; + + { + int i=0; + for(j=0; janum+1; ++j) { + if (j == AVI->video_strn) continue; + AVI->track[i].audio_tag[0] = j/10 + '0'; + AVI->track[i].audio_tag[1] = j%10 + '0'; + AVI->track[i].audio_tag[2] = 'w'; + AVI->track[i].audio_tag[3] = 'b'; + ++i; + } + } + + gf_f64_seek(AVI->fdes,AVI->movi_start,SEEK_SET); + + if(!getIndex) return(0); + + /* if the file has an idx1, check if this is relative + to the start of the file or to the start of the movi list */ + + idx_type = 0; + + if(AVI->idx) + { + s64 pos, len; + + /* Search the first videoframe in the idx1 and look where + it is in the file */ + + for(i=0;in_idx;i++) + if( strnicmp((char *)AVI->idx[i],(char *)AVI->video_tag,3)==0 ) break; + if(i>=AVI->n_idx) ERR_EXIT(AVI_ERR_NO_VIDS) + + pos = str2ulong(AVI->idx[i]+ 8); + len = str2ulong(AVI->idx[i]+12); + + gf_f64_seek(AVI->fdes,pos,SEEK_SET); + if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ) + if( strnicmp(data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len ) + { + idx_type = 1; /* Index from start of file */ + } + else + { + gf_f64_seek(AVI->fdes,pos+AVI->movi_start-4,SEEK_SET); + if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ) + if( strnicmp(data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len ) + { + idx_type = 2; /* Index from start of movi list */ + } + } + /* idx_type remains 0 if neither of the two tests above succeeds */ + } + + + if(idx_type == 0 && !AVI->is_opendml && !AVI->total_frames) + { + /* we must search through the file to get the index */ + + gf_f64_seek(AVI->fdes, AVI->movi_start, SEEK_SET); + + AVI->n_idx = 0; + + while(1) + { + if( avi_read(AVI->fdes,data,8) != 8 ) break; + n = str2ulong((unsigned char *)data+4); + + /* The movi list may contain sub-lists, ignore them */ + + if(strnicmp(data,"LIST",4)==0) + { + gf_f64_seek(AVI->fdes,4,SEEK_CUR); + continue; + } + + /* Check if we got a tag ##db, ##dc or ##wb */ + + if( ( (data[2]=='d' || data[2]=='D') && + (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') ) + || ( (data[2]=='w' || data[2]=='W') && + (data[3]=='b' || data[3]=='B') ) ) + { + u64 __pos = gf_f64_tell(AVI->fdes) - 8; + avi_add_index_entry(AVI,(unsigned char *)data,0,__pos,n); + } + + gf_f64_seek(AVI->fdes,PAD_EVEN(n),SEEK_CUR); + } + idx_type = 1; + } + + // ************************ + // OPENDML + // ************************ + + // read extended index chunks + if (AVI->is_opendml) { + u64 offset = 0; + int hdrl_len = 4+4+2+1+1+4+4+8+4; + char *en, *chunk_start; + int k = 0; + u32 audtr = 0; + u32 nrEntries = 0; + + AVI->video_index = NULL; + + nvi = 0; + for(audtr=0; audtranum; ++audtr) { nai[audtr] = 0; tot[audtr] = 0; } + + // ************************ + // VIDEO + // ************************ + + for (j=0; jvideo_superindex->nEntriesInUse; j++) { + + // read from file + chunk_start = en = (char*) malloc ((u32) (AVI->video_superindex->aIndex[j].dwSize+hdrl_len) ); + + if (gf_f64_seek(AVI->fdes, AVI->video_superindex->aIndex[j].qwOffset, SEEK_SET) == (u64)-1) { + free(chunk_start); + continue; + } + + if (avi_read(AVI->fdes, en, (u32) (AVI->video_superindex->aIndex[j].dwSize+hdrl_len) ) <= 0) { + free(chunk_start); + continue; + } + + nrEntries = str2ulong((unsigned char*)en + 12); +#ifdef DEBUG_ODML + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d:0] Video nrEntries %ld\n", j, nrEntries)); +#endif + offset = str2ullong((unsigned char*)en + 20); + + // skip header + en += hdrl_len; + nvi += nrEntries; + AVI->video_index = (video_index_entry *) realloc (AVI->video_index, nvi * sizeof (video_index_entry)); + if (!AVI->video_index) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] out of mem (size = %ld)\n", nvi * sizeof (video_index_entry))); + exit(1); + } + + while (k < nvi) { + + AVI->video_index[k].pos = offset + str2ulong((unsigned char*)en); en += 4; + AVI->video_index[k].len = str2ulong_len((unsigned char*)en); + AVI->video_index[k].key = str2ulong_key((unsigned char*)en); en += 4; + + // completely empty chunk + if (AVI->video_index[k].pos-offset == 0 && AVI->video_index[k].len == 0) { + k--; + nvi--; + } + +#ifdef DEBUG_ODML + /* + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d] POS 0x%llX len=%d key=%s offset (%llx) (%ld)\n", k, + AVI->video_index[k].pos, + (int)AVI->video_index[k].len, + AVI->video_index[k].key?"yes":"no ", offset, + AVI->video_superindex->aIndex[j].dwSize)); + */ +#endif + + k++; + } + + free(chunk_start); + } + + AVI->video_frames = nvi; + // this should deal with broken 'rec ' odml files. + if (AVI->video_frames == 0) { + AVI->is_opendml=0; + goto multiple_riff; + } + + // ************************ + // AUDIO + // ************************ + + for(audtr=0; audtranum; ++audtr) { + + k = 0; + if (!AVI->track[audtr].audio_superindex) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] (%s) cannot read audio index for track %d\n", __FILE__, audtr)); + continue; + } + for (j=0; jtrack[audtr].audio_superindex->nEntriesInUse; j++) { + + // read from file + chunk_start = en = (char*)malloc ((u32) (AVI->track[audtr].audio_superindex->aIndex[j].dwSize+hdrl_len)); + + if (gf_f64_seek(AVI->fdes, AVI->track[audtr].audio_superindex->aIndex[j].qwOffset, SEEK_SET) == (u64)-1) { + free(chunk_start); + continue; + } + + if (avi_read(AVI->fdes, en, (u32) (AVI->track[audtr].audio_superindex->aIndex[j].dwSize+hdrl_len)) <= 0) { + free(chunk_start); + continue; + } + + nrEntries = str2ulong((unsigned char*)en + 12); + //if (nrEntries > 50) nrEntries = 2; // XXX +#ifdef DEBUG_ODML + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d:%d] Audio nrEntries %ld\n", j, audtr, nrEntries)); +#endif + offset = str2ullong((unsigned char*)en + 20); + + // skip header + en += hdrl_len; + nai[audtr] += nrEntries; + AVI->track[audtr].audio_index = (audio_index_entry *) realloc (AVI->track[audtr].audio_index, nai[audtr] * sizeof (audio_index_entry)); + + while (k < nai[audtr]) { + + AVI->track[audtr].audio_index[k].pos = offset + str2ulong((unsigned char*)en); en += 4; + AVI->track[audtr].audio_index[k].len = str2ulong_len((unsigned char*)en); en += 4; + AVI->track[audtr].audio_index[k].tot = tot[audtr]; + tot[audtr] += AVI->track[audtr].audio_index[k].len; + +#ifdef DEBUG_ODML + /* + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d:%d] POS 0x%llX len=%d offset (%llx) (%ld)\n", k, audtr, + AVI->track[audtr].audio_index[k].pos, + (int)AVI->track[audtr].audio_index[k].len, + offset, AVI->track[audtr].audio_superindex->aIndex[j].dwSize)); + */ +#endif + + ++k; + } + + free(chunk_start); + } + + AVI->track[audtr].audio_chunks = nai[audtr]; + AVI->track[audtr].audio_bytes = tot[audtr]; + } + } // is opendml + + else if (AVI->total_frames && !AVI->is_opendml && idx_type==0) { + + // ********************* + // MULTIPLE RIFF CHUNKS (and no index) + // ********************* + +multiple_riff: + + gf_f64_seek(AVI->fdes, AVI->movi_start, SEEK_SET); + + AVI->n_idx = 0; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] Reconstructing index...")); + + // Number of frames; only one audio track supported + nvi = AVI->video_frames = AVI->total_frames; + nai[0] = AVI->track[0].audio_chunks = AVI->total_frames; + for(j=1; janum; ++j) AVI->track[j].audio_chunks = 0; + + AVI->video_index = (video_index_entry *) malloc(nvi*sizeof(video_index_entry)); + + if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM); + + for(j=0; janum; ++j) { + if(AVI->track[j].audio_chunks) { + AVI->track[j].audio_index = (audio_index_entry *) malloc((nai[j]+1)*sizeof(audio_index_entry)); + memset(AVI->track[j].audio_index, 0, (nai[j]+1)*(sizeof(audio_index_entry))); + if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM); + } + } + + nvi = 0; + for(j=0; janum; ++j) {nai[j] = 0; tot[j] = 0;} + + aud_chunks = AVI->total_frames; + + while(1) + { + if (nvi >= AVI->total_frames) break; + + if( avi_read(AVI->fdes,data,8) != 8 ) break; + n = str2ulong((unsigned char *)data+4); + + + j=0; + + if (aud_chunks - nai[j] -1 <= 0) { + aud_chunks += AVI->total_frames; + AVI->track[j].audio_index = (audio_index_entry *) + realloc( AVI->track[j].audio_index, (aud_chunks+1)*sizeof(audio_index_entry)); + if (!AVI->track[j].audio_index) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Internal error in avilib -- no mem\n")); + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + } + + /* Check if we got a tag ##db, ##dc or ##wb */ + + // VIDEO + if( + (data[0]=='0' || data[1]=='0') && + (data[2]=='d' || data[2]=='D') && + (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') ) { + + AVI->video_index[nvi].key = 0x0; + AVI->video_index[nvi].pos = gf_f64_tell(AVI->fdes); + AVI->video_index[nvi].len = (u32) n; + + /* + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] Frame %ld pos %lld len %lld key %ld\n", + nvi, AVI->video_index[nvi].pos, AVI->video_index[nvi].len, (long)AVI->video_index[nvi].key)); + */ + nvi++; + gf_f64_seek(AVI->fdes,PAD_EVEN(n),SEEK_CUR); + } + + //AUDIO + else if( + (data[0]=='0' || data[1]=='1') && + (data[2]=='w' || data[2]=='W') && + (data[3]=='b' || data[3]=='B') ) { + + + AVI->track[j].audio_index[nai[j]].pos = gf_f64_tell(AVI->fdes); + AVI->track[j].audio_index[nai[j]].len = (u32) n; + AVI->track[j].audio_index[nai[j]].tot = tot[j]; + tot[j] += AVI->track[j].audio_index[nai[j]].len; + nai[j]++; + + gf_f64_seek(AVI->fdes,PAD_EVEN(n),SEEK_CUR); + } + else { + gf_f64_seek(AVI->fdes,-4,SEEK_CUR); + } + + } + if (nvi < AVI->total_frames) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[avilib] Uh? Some frames seems missing (%ld/%d)\n", + nvi, AVI->total_frames)); + } + + + AVI->video_frames = nvi; + AVI->track[0].audio_chunks = nai[0]; + + for(j=0; janum; ++j) AVI->track[j].audio_bytes = tot[j]; + idx_type = 1; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] done. nvi=%ld nai=%ld tot=%ld\n", nvi, nai[0], tot[0])); + + } // total_frames but no indx chunk (xawtv does this) + + else + + { + // ****************** + // NO OPENDML + // ****************** + + /* Now generate the video index and audio index arrays */ + + nvi = 0; + for(j=0; janum; ++j) nai[j] = 0; + + for(i=0;in_idx;i++) { + + if(strnicmp((char *)AVI->idx[i],AVI->video_tag,3) == 0) nvi++; + + for(j=0; janum; ++j) if(strnicmp((char *)AVI->idx[i], AVI->track[j].audio_tag,4) == 0) nai[j]++; + } + + AVI->video_frames = nvi; + for(j=0; janum; ++j) AVI->track[j].audio_chunks = nai[j]; + + + if(AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS); + AVI->video_index = (video_index_entry *) malloc(nvi*sizeof(video_index_entry)); + if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM); + + for(j=0; janum; ++j) { + if(AVI->track[j].audio_chunks) { + AVI->track[j].audio_index = (audio_index_entry *) malloc((nai[j]+1)*sizeof(audio_index_entry)); + memset(AVI->track[j].audio_index, 0, (nai[j]+1)*(sizeof(audio_index_entry))); + if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM); + } + } + + nvi = 0; + for(j=0; janum; ++j) {nai[j] = 0; tot[j] = 0;} + + ioff = idx_type == 1 ? 8 : (u32)AVI->movi_start+4; + + for(i=0;in_idx;i++) { + + //video + if(strnicmp((char *)AVI->idx[i],AVI->video_tag,3) == 0) { + AVI->video_index[nvi].key = str2ulong(AVI->idx[i]+ 4); + AVI->video_index[nvi].pos = str2ulong(AVI->idx[i]+ 8)+ioff; + AVI->video_index[nvi].len = str2ulong(AVI->idx[i]+12); + nvi++; + } + + //audio + for(j=0; janum; ++j) { + + if(strnicmp((char *)AVI->idx[i],AVI->track[j].audio_tag,4) == 0) { + AVI->track[j].audio_index[nai[j]].pos = str2ulong(AVI->idx[i]+ 8)+ioff; + AVI->track[j].audio_index[nai[j]].len = str2ulong(AVI->idx[i]+12); + AVI->track[j].audio_index[nai[j]].tot = tot[j]; + tot[j] += AVI->track[j].audio_index[nai[j]].len; + nai[j]++; + } + } + } + + + for(j=0; janum; ++j) AVI->track[j].audio_bytes = tot[j]; + + } // is no opendml + + /* Reposition the file */ + + gf_f64_seek(AVI->fdes,AVI->movi_start,SEEK_SET); + AVI->video_pos = 0; + + return(0); +} + +long AVI_video_frames(avi_t *AVI) +{ + return AVI->video_frames; +} +int AVI_video_width(avi_t *AVI) +{ + return AVI->width; +} +int AVI_video_height(avi_t *AVI) +{ + return AVI->height; +} +double AVI_frame_rate(avi_t *AVI) +{ + return AVI->fps; +} +char* AVI_video_compressor(avi_t *AVI) +{ + return AVI->compressor2; +} + +long AVI_max_video_chunk(avi_t *AVI) +{ + return AVI->max_len; +} + +int AVI_audio_tracks(avi_t *AVI) +{ + return(AVI->anum); +} + +int AVI_audio_channels(avi_t *AVI) +{ + return AVI->track[AVI->aptr].a_chans; +} + +long AVI_audio_mp3rate(avi_t *AVI) +{ + return AVI->track[AVI->aptr].mp3rate; +} + +long AVI_audio_padrate(avi_t *AVI) +{ + return AVI->track[AVI->aptr].padrate; +} + +int AVI_audio_bits(avi_t *AVI) +{ + return AVI->track[AVI->aptr].a_bits; +} + +int AVI_audio_format(avi_t *AVI) +{ + return AVI->track[AVI->aptr].a_fmt; +} + +long AVI_audio_rate(avi_t *AVI) +{ + return AVI->track[AVI->aptr].a_rate; +} + + +long AVI_frame_size(avi_t *AVI, long frame) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if(frame < 0 || frame >= AVI->video_frames) return 0; + return (u32) (AVI->video_index[frame].len); +} + +long AVI_audio_size(avi_t *AVI, long frame) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if(frame < 0 || frame >= AVI->track[AVI->aptr].audio_chunks) return -1; + return (u32) (AVI->track[AVI->aptr].audio_index[frame].len); +} + +u64 AVI_get_video_position(avi_t *AVI, long frame) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return (u64) -1; } + if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return (u64) -1; } + + if(frame < 0 || frame >= AVI->video_frames) return 0; + return(AVI->video_index[frame].pos); +} + + +int AVI_seek_start(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + gf_f64_seek(AVI->fdes,AVI->movi_start,SEEK_SET); + AVI->video_pos = 0; + return 0; +} + +int AVI_set_video_position(avi_t *AVI, long frame) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if (frame < 0 ) frame = 0; + AVI->video_pos = frame; + return 0; +} + +int AVI_set_audio_bitrate(avi_t *AVI, long bitrate) +{ + if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + AVI->track[AVI->aptr].mp3rate = bitrate; + return 0; +} + + +long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe) +{ + long n; + + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if(AVI->video_pos < 0 || AVI->video_pos >= AVI->video_frames) return -1; + n = (u32) AVI->video_index[AVI->video_pos].len; + + *keyframe = (AVI->video_index[AVI->video_pos].key==0x10) ? 1:0; + + if (vidbuf == NULL) { + AVI->video_pos++; + return n; + } + + gf_f64_seek(AVI->fdes, AVI->video_index[AVI->video_pos].pos, SEEK_SET); + + if (avi_read(AVI->fdes,vidbuf,n) != (u32) n) + { + AVI_errno = AVI_ERR_READ; + return -1; + } + + AVI->video_pos++; + + return n; +} + +long AVI_get_audio_position_index(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + return (AVI->track[AVI->aptr].audio_posc); +} + +int AVI_set_audio_position_index(avi_t *AVI, long indexpos) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + if(indexpos > AVI->track[AVI->aptr].audio_chunks) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + AVI->track[AVI->aptr].audio_posc = indexpos; + AVI->track[AVI->aptr].audio_posb = 0; + + return 0; +} + + +int AVI_set_audio_position(avi_t *AVI, long byte) +{ + long n0, n1, n; + + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if(byte < 0) byte = 0; + + /* Binary search in the audio chunks */ + + n0 = 0; + n1 = AVI->track[AVI->aptr].audio_chunks; + + while(n0track[AVI->aptr].audio_index[n].tot>(u32) byte) + n1 = n; + else + n0 = n; + } + + AVI->track[AVI->aptr].audio_posc = n0; + AVI->track[AVI->aptr].audio_posb = (u32) (byte - AVI->track[AVI->aptr].audio_index[n0].tot); + + return 0; +} + +long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes, int *continuous) +{ + long nr, left, todo; + s64 pos; + + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + nr = 0; /* total number of bytes read */ + + if (bytes==0) { + AVI->track[AVI->aptr].audio_posc++; + AVI->track[AVI->aptr].audio_posb = 0; + } + + *continuous = 1; + while(bytes>0) + { + s64 ret; + left = (long) (AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].len - AVI->track[AVI->aptr].audio_posb); + if(left==0) + { + if(AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) return nr; + AVI->track[AVI->aptr].audio_posc++; + AVI->track[AVI->aptr].audio_posb = 0; + *continuous = 0; + continue; + } + if(bytestrack[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos + AVI->track[AVI->aptr].audio_posb; + gf_f64_seek(AVI->fdes, pos, SEEK_SET); + if ( (ret = avi_read(AVI->fdes,audbuf+nr,todo)) != todo) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] XXX pos = %lld, ret = %lld, todo = %ld\n", pos, ret, todo)); + AVI_errno = AVI_ERR_READ; + return -1; + } + bytes -= todo; + nr += todo; + AVI->track[AVI->aptr].audio_posb += todo; + } + + return nr; +} + + +/* AVI_read_data: Special routine for reading the next audio or video chunk + without having an index of the file. */ + +int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf, + char *audbuf, long max_audbuf, + long *len) +{ + +/* + * Return codes: + * + * 1 = video data read + * 2 = audio data read + * 0 = reached EOF + * -1 = video buffer too small + * -2 = audio buffer too small + */ + + s64 n; + char data[8]; + + if(AVI->mode==AVI_MODE_WRITE) return 0; + + while(1) + { + /* Read tag and length */ + + if( avi_read(AVI->fdes,data,8) != 8 ) return 0; + + /* if we got a list tag, ignore it */ + + if(strnicmp(data,"LIST",4) == 0) + { + gf_f64_seek(AVI->fdes,4,SEEK_CUR); + continue; + } + + n = PAD_EVEN(str2ulong((unsigned char *)data+4)); + + if(strnicmp(data,AVI->video_tag,3) == 0) + { + *len = (u32) n; + AVI->video_pos++; + if(n>max_vidbuf) + { + gf_f64_seek(AVI->fdes,n,SEEK_CUR); + return -1; + } + if(avi_read(AVI->fdes,vidbuf, (u32) n) != n ) return 0; + return 1; + } + else if(strnicmp(data,AVI->track[AVI->aptr].audio_tag,4) == 0) + { + *len = (u32) n; + if(n>max_audbuf) + { + gf_f64_seek(AVI->fdes,n,SEEK_CUR); + return -2; + } + if(avi_read(AVI->fdes,audbuf, (u32) n) != n ) return 0; + return 2; + break; + } + else + if(gf_f64_seek(AVI->fdes,n,SEEK_CUR) == (u64) -1) return 0; + } +} + +u64 AVI_max_size(void) +{ + return((u64) AVI_MAX_LEN); +} + + +#endif + diff --git a/src/media_tools/gpac_ogg.c b/src/media_tools/gpac_ogg.c new file mode 100644 index 0000000..9d860e5 --- /dev/null +++ b/src/media_tools/gpac_ogg.c @@ -0,0 +1,1326 @@ + + +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: code raw [Vorbis] packets into framed OggSquish stream and + decode Ogg streams back into raw packets + + note: The CRC code is directly derived from public domain code by + Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html + for details. + + ********************************************************************/ + + +#include + + +#define BUFFER_INCREMENT 256 + +static u32 mask[]= +{0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f, + 0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff, + 0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff, + 0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff, + 0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff, + 0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff, + 0x3fffffff,0x7fffffff,0xffffffff }; + +static u32 mask8B[]= +{0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; + +void oggpack_writeinit(oggpack_buffer *b){ + memset(b,0,sizeof(*b)); + b->ptr = b->buffer = (unsigned char *)malloc(BUFFER_INCREMENT); + b->buffer[0]='\0'; + b->storage=BUFFER_INCREMENT; +} + +void oggpackB_writeinit(oggpack_buffer *b){ + oggpack_writeinit(b); +} + +void oggpack_writetrunc(oggpack_buffer *b,s32 bits){ + s32 bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask[bits]; +} + +void oggpackB_writetrunc(oggpack_buffer *b,s32 bits){ + s32 bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask8B[bits]; +} + +/* Takes only up to 32 bits. */ +void oggpack_write(oggpack_buffer *b,u32 value,s32 bits){ + if(b->endbyte+4>=b->storage){ + b->buffer = (unsigned char *)realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value&=mask[bits]; + bits+=b->endbit; + + b->ptr[0]|=value<endbit; + + if(bits>=8){ + b->ptr[1]=value>>(8-b->endbit); + if(bits>=16){ + b->ptr[2]=value>>(16-b->endbit); + if(bits>=24){ + b->ptr[3]=value>>(24-b->endbit); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=value>>(32-b->endbit); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +/* Takes only up to 32 bits. */ +void oggpackB_write(oggpack_buffer *b,u32 value,s32 bits){ + if(b->endbyte+4>=b->storage){ + b->buffer = (unsigned char *)realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value=(value&mask[bits])<<(32-bits); + bits+=b->endbit; + + b->ptr[0]|=value>>(24+b->endbit); + + if(bits>=8){ + b->ptr[1]=value>>(16+b->endbit); + if(bits>=16){ + b->ptr[2]=value>>(8+b->endbit); + if(bits>=24){ + b->ptr[3]=value>>(b->endbit); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=value<<(8-b->endbit); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +void oggpack_writealign(oggpack_buffer *b){ + s32 bits=8-b->endbit; + if(bits<8) + oggpack_write(b,0,bits); +} + +void oggpackB_writealign(oggpack_buffer *b){ + s32 bits=8-b->endbit; + if(bits<8) + oggpackB_write(b,0,bits); +} + +static void oggpack_writecopy_helper(oggpack_buffer *b, + void *source, + s32 bits, + void (*w)(oggpack_buffer *, + u32, + s32), + s32 msb){ + unsigned char *ptr=(unsigned char *)source; + + s32 bytes=bits/8; + bits-=bytes*8; + + if(b->endbit){ + s32 i; + /* unaligned copy. Do it the hard way. */ + for(i=0;iendbyte+bytes+1>=b->storage){ + b->storage=b->endbyte+bytes+BUFFER_INCREMENT; + b->buffer = (unsigned char *)realloc(b->buffer,b->storage); + b->ptr=b->buffer+b->endbyte; + } + + memmove(b->ptr,source,bytes); + b->ptr+=bytes; + b->buffer+=bytes; + *b->ptr=0; + + } + if(bits){ + if(msb) + w(b,(u32)(ptr[bytes]>>(8-bits)),bits); + else + w(b,(u32)(ptr[bytes]),bits); + } +} + +void oggpack_writecopy(oggpack_buffer *b,void *source,s32 bits){ + oggpack_writecopy_helper(b,source,bits,oggpack_write,0); +} + +void oggpackB_writecopy(oggpack_buffer *b,void *source,s32 bits){ + oggpack_writecopy_helper(b,source,bits,oggpackB_write,1); +} + +void oggpack_reset(oggpack_buffer *b){ + b->ptr=b->buffer; + b->buffer[0]=0; + b->endbit=b->endbyte=0; +} + +void oggpackB_reset(oggpack_buffer *b){ + oggpack_reset(b); +} + +void oggpack_writeclear(oggpack_buffer *b){ + free(b->buffer); + memset(b,0,sizeof(*b)); +} + +void oggpackB_writeclear(oggpack_buffer *b){ + oggpack_writeclear(b); +} + +void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,s32 bytes){ + memset(b,0,sizeof(*b)); + b->buffer=b->ptr=buf; + b->storage=bytes; +} + +void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,s32 bytes){ + oggpack_readinit(b,buf,bytes); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +s32 oggpack_look(oggpack_buffer *b,s32 bits){ + u32 ret; + u32 m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + return(m&ret); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +s32 oggpackB_look(oggpack_buffer *b,s32 bits){ + u32 ret; + s32 m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + return (ret>>(m>>1))>>((m+1)>>1); +} + +s32 oggpack_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>b->endbit)&1); +} + +s32 oggpackB_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>(7-b->endbit))&1); +} + +void oggpack_adv(oggpack_buffer *b,s32 bits){ + bits+=b->endbit; + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; +} + +void oggpackB_adv(oggpack_buffer *b,s32 bits){ + oggpack_adv(b,bits); +} + +void oggpack_adv1(oggpack_buffer *b){ + if(++(b->endbit)>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } +} + +void oggpackB_adv1(oggpack_buffer *b){ + oggpack_adv1(b); +} + +/* bits <= 32 */ +s32 oggpack_read(oggpack_buffer *b,s32 bits){ + u32 ret; + u32 m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=(u32) (-1); + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit){ + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + } + ret&=m; + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +/* bits <= 32 */ +s32 oggpackB_read(oggpack_buffer *b,s32 bits){ + u32 ret; + s32 m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=(u32) (-1); + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + ret=(ret>>(m>>1))>>((m+1)>>1); + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +s32 oggpack_read1(oggpack_buffer *b){ + u32 ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=(u32) (-1); + goto overflow; + } + + ret=(b->ptr[0]>>b->endbit)&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +s32 oggpackB_read1(oggpack_buffer *b){ + u32 ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=(u32) (-1); + goto overflow; + } + + ret=(b->ptr[0]>>(7-b->endbit))&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +s32 oggpack_bytes(oggpack_buffer *b){ + return(b->endbyte+(b->endbit+7)/8); +} + +s32 oggpack_bits(oggpack_buffer *b){ + return(b->endbyte*8+b->endbit); +} + +s32 oggpackB_bytes(oggpack_buffer *b){ + return oggpack_bytes(b); +} + +s32 oggpackB_bits(oggpack_buffer *b){ + return oggpack_bits(b); +} + +unsigned char *oggpack_get_buffer(oggpack_buffer *b){ + return(b->buffer); +} + +unsigned char *oggpackB_get_buffer(oggpack_buffer *b){ + return oggpack_get_buffer(b); +} + + +#undef BUFFER_INCREMENT + +/* A complete description of Ogg framing exists in docs/framing.html */ + +s32 ogg_page_version(ogg_page *og){ + return((s32)(og->header[4])); +} + +s32 ogg_page_continued(ogg_page *og){ + return((s32)(og->header[5]&0x01)); +} + +s32 ogg_page_bos(ogg_page *og){ + return((s32)(og->header[5]&0x02)); +} + +s32 ogg_page_eos(ogg_page *og){ + return((s32)(og->header[5]&0x04)); +} + +s64 ogg_page_granulepos(ogg_page *og){ + unsigned char *page=og->header; + s64 granulepos=page[13]&(0xff); + granulepos= (granulepos<<8)|(page[12]&0xff); + granulepos= (granulepos<<8)|(page[11]&0xff); + granulepos= (granulepos<<8)|(page[10]&0xff); + granulepos= (granulepos<<8)|(page[9]&0xff); + granulepos= (granulepos<<8)|(page[8]&0xff); + granulepos= (granulepos<<8)|(page[7]&0xff); + granulepos= (granulepos<<8)|(page[6]&0xff); + return(granulepos); +} + +s32 ogg_page_serialno(ogg_page *og){ + return(og->header[14] | + (og->header[15]<<8) | + (og->header[16]<<16) | + (og->header[17]<<24)); +} + +s32 ogg_page_pageno(ogg_page *og){ + return(og->header[18] | + (og->header[19]<<8) | + (og->header[20]<<16) | + (og->header[21]<<24)); +} + + + +/* returns the number of packets that are completed on this page (if + the leading packet is begun on a previous page, but ends on this + page, it's counted */ + +/* NOTE: +If a page consists of a packet begun on a previous page, and a new +packet begun (but not completed) on this page, the return will be: + ogg_page_packets(page) ==1, + ogg_page_continued(page) !=0 + +If a page happens to be a single packet that was begun on a +previous page, and spans to the next page (in the case of a three or +more page packet), the return will be: + ogg_page_packets(page) ==0, + ogg_page_continued(page) !=0 +*/ + +s32 ogg_page_packets(ogg_page *og){ + s32 i,n=og->header[26],count=0; + for(i=0;iheader[27+i]<255)count++; + return(count); +} + + +#if 0 +/* helper to initialize lookup for direct-table CRC (illustrative; we + use the static init below) */ + +static u32 _ogg_crc_entry(u32 index){ + s32 i; + u32 r; + + r = index << 24; + for (i=0; i<8; i++) + if (r & 0x80000000UL) + r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator + polynomial, although we use an + unreflected alg and an init/final + of 0, not 0xffffffff */ + else + r<<=1; + return (r & 0xffffffffUL); +} +#endif + +static u32 crc_lookup[256]={ + 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, + 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, + 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, + 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, + 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, + 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, + 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, + 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, + 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, + 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, + 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, + 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, + 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, + 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, + 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, + 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, + 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, + 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, + 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, + 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, + 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, + 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, + 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, + 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, + 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, + 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, + 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, + 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, + 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, + 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, + 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, + 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, + 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, + 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, + 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, + 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, + 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, + 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, + 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, + 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, + 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, + 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, + 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, + 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, + 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, + 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, + 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, + 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, + 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, + 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, + 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, + 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, + 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, + 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, + 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, + 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, + 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, + 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, + 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, + 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, + 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, + 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, + 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, + 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; + +/* init the encode/decode logical stream state */ + +s32 ogg_stream_init(ogg_stream_state *os,s32 serialno){ + if(os){ + memset(os,0,sizeof(*os)); + os->body_storage=16*1024; + os->body_data = (unsigned char *)malloc(os->body_storage*sizeof(*os->body_data)); + + os->lacing_storage=1024; + os->lacing_vals=(s32 *)malloc(os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=(s64*)malloc(os->lacing_storage*sizeof(*os->granule_vals)); + + os->serialno=serialno; + + return(0); + } + return(-1); +} + +/* _clear does not free os, only the non-flat storage within */ +s32 ogg_stream_clear(ogg_stream_state *os){ + if(os){ + if(os->body_data)free(os->body_data); + if(os->lacing_vals)free(os->lacing_vals); + if(os->granule_vals)free(os->granule_vals); + + memset(os,0,sizeof(*os)); + } + return(0); +} + +s32 ogg_stream_destroy(ogg_stream_state *os){ + if(os){ + ogg_stream_clear(os); + free(os); + } + return(0); +} + +/* Helpers for ogg_stream_encode; this keeps the structure and + what's happening fairly clear */ + +static void _os_body_expand(ogg_stream_state *os,s32 needed){ + if(os->body_storage<=os->body_fill+needed){ + os->body_storage+=(needed+1024); + os->body_data = (unsigned char *)realloc(os->body_data,os->body_storage*sizeof(*os->body_data)); + } +} + +static void _os_lacing_expand(ogg_stream_state *os,s32 needed){ + if(os->lacing_storage<=os->lacing_fill+needed){ + os->lacing_storage+=(needed+32); + os->lacing_vals=(s32*)realloc(os->lacing_vals,os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=(s64*)realloc(os->granule_vals,os->lacing_storage*sizeof(*os->granule_vals)); + } +} + +/* checksum the page */ +/* Direct table CRC; note that this will be faster in the future if we + perform the checksum silmultaneously with other copies */ + +void ogg_page_checksum_set(ogg_page *og){ + if(og){ + u32 crc_reg=0; + s32 i; + + /* safety; needed for API behavior, but not framing code */ + og->header[22]=0; + og->header[23]=0; + og->header[24]=0; + og->header[25]=0; + + for(i=0;iheader_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]]; + for(i=0;ibody_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]]; + + og->header[22]=crc_reg&0xff; + og->header[23]=(crc_reg>>8)&0xff; + og->header[24]=(crc_reg>>16)&0xff; + og->header[25]=(crc_reg>>24)&0xff; + } +} + +/* submit data to the internal buffer of the framing engine */ +s32 ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ + s32 lacing_vals=op->bytes/255+1,i; + + if(os->body_returned){ + /* advance packet data according to the body_returned pointer. We + had to keep it around to return a pointer into the buffer last + call */ + + os->body_fill-=os->body_returned; + if(os->body_fill) + memmove(os->body_data,os->body_data+os->body_returned, + os->body_fill); + os->body_returned=0; + } + + /* make sure we have the buffer storage */ + _os_body_expand(os,op->bytes); + _os_lacing_expand(os,lacing_vals); + + /* Copy in the submitted packet. Yes, the copy is a waste; this is + the liability of overly clean abstraction for the time being. It + will actually be fairly easy to eliminate the extra copy in the + future */ + + memcpy(os->body_data+os->body_fill,op->packet,op->bytes); + os->body_fill+=op->bytes; + + /* Store lacing vals for this packet */ + for(i=0;ilacing_vals[os->lacing_fill+i]=255; + os->granule_vals[os->lacing_fill+i]=os->granulepos; + } + os->lacing_vals[os->lacing_fill+i]=(op->bytes)%255; + os->granulepos=os->granule_vals[os->lacing_fill+i]=op->granulepos; + + /* flag the first segment as the beginning of the packet */ + os->lacing_vals[os->lacing_fill]|= 0x100; + + os->lacing_fill+=lacing_vals; + + /* for the sake of completeness */ + os->packetno++; + + if(op->e_o_s)os->e_o_s=1; + + return(0); +} + +/* This will flush remaining packets into a page (returning nonzero), + even if there is not enough data to trigger a flush normally + (undersized page). If there are no packets or partial packets to + flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will + try to flush a normal sized page like ogg_stream_pageout; a call to + ogg_stream_flush does not guarantee that all packets have flushed. + Only a return value of 0 from ogg_stream_flush indicates all packet + data is flushed into pages. + + since ogg_stream_flush will flush the last page in a stream even if + it's undersized, you almost certainly want to use ogg_stream_pageout + (and *not* ogg_stream_flush) unless you specifically need to flush + an page regardless of size in the middle of a stream. */ + +s32 ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ + s32 i; + s32 vals=0; + s32 maxvals=(os->lacing_fill>255?255:os->lacing_fill); + s32 bytes=0; + s32 acc=0; + s64 granule_pos=os->granule_vals[0]; + + if(maxvals==0)return(0); + + /* construct a page */ + /* decide how many segments to include */ + + /* If this is the initial header case, the first page must only include + the initial header packet */ + if(os->b_o_s==0){ /* 'initial header page' case */ + granule_pos=0; + for(vals=0;valslacing_vals[vals]&0x0ff)<255){ + vals++; + break; + } + } + }else{ + for(vals=0;vals4096)break; + acc+=os->lacing_vals[vals]&0x0ff; + granule_pos=os->granule_vals[vals]; + } + } + + /* construct the header in temp storage */ + memcpy(os->header,"OggS",4); + + /* stream structure version */ + os->header[4]=0x00; + + /* continued packet flag? */ + os->header[5]=0x00; + if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01; + /* first page flag? */ + if(os->b_o_s==0)os->header[5]|=0x02; + /* last page flag? */ + if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04; + os->b_o_s=1; + + /* 64 bits of PCM position */ + for(i=6;i<14;i++){ + os->header[i]=(u8) (granule_pos&0xff); + granule_pos>>=8; + } + + /* 32 bits of stream serial number */ + { + s32 serialno=os->serialno; + for(i=14;i<18;i++){ + os->header[i]=(serialno&0xff); + serialno>>=8; + } + } + + /* 32 bits of page counter (we have both counter and page header + because this val can roll over) */ + if(os->pageno==-1)os->pageno=0; /* because someone called + stream_reset; this would be a + strange thing to do in an + encode stream, but it has + plausible uses */ + { + s32 pageno=os->pageno++; + for(i=18;i<22;i++){ + os->header[i]=(pageno&0xff); + pageno>>=8; + } + } + + /* zero for computation; filled in later */ + os->header[22]=0; + os->header[23]=0; + os->header[24]=0; + os->header[25]=0; + + /* segment table */ + os->header[26]=vals&0xff; + for(i=0;iheader[i+27]=(os->lacing_vals[i]&0xff); + + /* set pointers in the ogg_page struct */ + og->header=os->header; + og->header_len=os->header_fill=vals+27; + og->body=os->body_data+os->body_returned; + og->body_len=bytes; + + /* advance the lacing data and set the body_returned pointer */ + + os->lacing_fill-=vals; + memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+vals,os->lacing_fill*sizeof(*os->granule_vals)); + os->body_returned+=bytes; + + /* calculate the checksum */ + + ogg_page_checksum_set(og); + + /* done */ + return(1); +} + + +/* This constructs pages from buffered packet segments. The pointers +returned are to static buffers; do not free. The returned buffers are +good only until the next call (using the same ogg_stream_state) */ + +s32 ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ + + if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ + os->body_fill-os->body_returned > 4096 ||/* 'page nominal size' case */ + os->lacing_fill>=255 || /* 'segment table full' case */ + (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */ + + return(ogg_stream_flush(os,og)); + } + + /* not enough data to construct a page and not end of stream */ + return(0); +} + +s32 ogg_stream_eos(ogg_stream_state *os){ + return os->e_o_s; +} + +/* DECODING PRIMITIVES: packet streaming layer **********************/ + +/* This has two layers to place more of the multi-serialno and paging + control in the application's hands. First, we expose a data buffer + using ogg_sync_buffer(). The app either copies into the + buffer, or passes it directly to read(), etc. We then call + ogg_sync_wrote() to tell how many bytes we just added. + + Pages are returned (pointers into the buffer in ogg_sync_state) + by ogg_sync_pageout(). The page is then submitted to + ogg_stream_pagein() along with the appropriate + ogg_stream_state* (ie, matching serialno). We then get raw + packets out calling ogg_stream_packetout() with a + ogg_stream_state. See the 'frame-prog.txt' docs for details and + example code. */ + +/* initialize the struct to a known state */ +s32 ogg_sync_init(ogg_sync_state *oy){ + if(oy){ + memset(oy,0,sizeof(*oy)); + } + return(0); +} + +/* clear non-flat storage within */ +s32 ogg_sync_clear(ogg_sync_state *oy){ + if(oy){ + if(oy->data)free(oy->data); + ogg_sync_init(oy); + } + return(0); +} + +s32 ogg_sync_destroy(ogg_sync_state *oy){ + if(oy){ + ogg_sync_clear(oy); + free(oy); + } + return(0); +} + +char *ogg_sync_buffer(ogg_sync_state *oy, s32 size){ + + /* first, clear out any space that has been previously returned */ + if(oy->returned){ + oy->fill-=oy->returned; + if(oy->fill>0) + memmove(oy->data,oy->data+oy->returned,oy->fill); + oy->returned=0; + } + + if(size>oy->storage-oy->fill){ + /* We need to extend the internal buffer */ + s32 newsize=size+oy->fill+4096; /* an extra page to be nice */ + + if(oy->data) + oy->data = (unsigned char *)realloc(oy->data,newsize); + else + oy->data = (unsigned char *)malloc(newsize); + oy->storage=newsize; + } + + /* expose a segment at least as large as requested at the fill mark */ + return((char *)oy->data+oy->fill); +} + +s32 ogg_sync_wrote(ogg_sync_state *oy, s32 bytes){ + if(oy->fill+bytes>oy->storage)return(-1); + oy->fill+=bytes; + return(0); +} + +/* sync the stream. This is meant to be useful for finding page + boundaries. + + return values for this: + -n) skipped n bytes + 0) page not ready; more data (no bytes skipped) + n) page synced at current location; page length n bytes + +*/ + +s32 ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ + unsigned char *page=oy->data+oy->returned; + unsigned char *next; + s32 bytes=oy->fill-oy->returned; + + if(oy->headerbytes==0){ + s32 headerbytes,i; + if(bytes<27)return(0); /* not enough for a header */ + + /* verify capture pattern */ + if(memcmp(page,"OggS",4))goto sync_fail; + + headerbytes=page[26]+27; + if(bytesbodybytes+=page[27+i]; + oy->headerbytes=headerbytes; + } + + if(oy->bodybytes+oy->headerbytes>bytes)return(0); + + /* The whole test page is buffered. Verify the checksum */ + { + /* Grab the checksum bytes, set the header field to zero */ + char chksum[4]; + ogg_page log; + + memcpy(chksum,page+22,4); + memset(page+22,0,4); + + /* set up a temp page struct and recompute the checksum */ + log.header=page; + log.header_len=oy->headerbytes; + log.body=page+oy->headerbytes; + log.body_len=oy->bodybytes; + ogg_page_checksum_set(&log); + + /* Compare */ + if(memcmp(chksum,page+22,4)){ + /* D'oh. Mismatch! Corrupt page (or miscapture and not a page + at all) */ + /* replace the computed checksum with the one actually read in */ + memcpy(page+22,chksum,4); + + /* Bad checksum. Lose sync */ + goto sync_fail; + } + } + + /* yes, have a whole page all ready to go */ + { + unsigned char *page=oy->data+oy->returned; + s32 bytes; + + if(og){ + og->header=page; + og->header_len=oy->headerbytes; + og->body=page+oy->headerbytes; + og->body_len=oy->bodybytes; + } + + oy->unsynced=0; + oy->returned+=(bytes=oy->headerbytes+oy->bodybytes); + oy->headerbytes=0; + oy->bodybytes=0; + return(bytes); + } + + sync_fail: + + oy->headerbytes=0; + oy->bodybytes=0; + + /* search for possible capture */ + next = (unsigned char *)memchr(page+1,'O',bytes-1); + if(!next) + next=oy->data+oy->fill; + + oy->returned=next-oy->data; + return(-(next-page)); +} + +/* sync the stream and get a page. Keep trying until we find a page. + Supress 'sync errors' after reporting the first. + + return values: + -1) recapture (hole in data) + 0) need more data + 1) page returned + + Returns pointers into buffered data; invalidated by next call to + _stream, _clear, _init, or _buffer */ + +s32 ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ + + /* all we need to do is verify a page at the head of the stream + buffer. If it doesn't verify, we look for the next potential + frame */ + + while(1){ + s32 ret=ogg_sync_pageseek(oy,og); + if(ret>0){ + /* have a page */ + return(1); + } + if(ret==0){ + /* need more data */ + return(0); + } + + /* head did not start a synced page... skipped some bytes */ + if(!oy->unsynced){ + oy->unsynced=1; + return(-1); + } + + /* loop. keep looking */ + + } +} + +/* add the incoming page to the stream state; we decompose the page + into packet segments here as well. */ + +s32 ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ + unsigned char *header=og->header; + unsigned char *body=og->body; + s32 bodysize=og->body_len; + s32 segptr=0; + + s32 version=ogg_page_version(og); + s32 continued=ogg_page_continued(og); + s32 bos=ogg_page_bos(og); + s32 eos=ogg_page_eos(og); + s64 granulepos=ogg_page_granulepos(og); + s32 serialno=ogg_page_serialno(og); + s32 pageno=ogg_page_pageno(og); + s32 segments=header[26]; + + /* clean up 'returned data' */ + { + s32 lr=os->lacing_returned; + s32 br=os->body_returned; + + /* body data */ + if(br){ + os->body_fill-=br; + if(os->body_fill) + memmove(os->body_data,os->body_data+br,os->body_fill); + os->body_returned=0; + } + + if(lr){ + /* segment table */ + if(os->lacing_fill-lr){ + memmove(os->lacing_vals,os->lacing_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->granule_vals)); + } + os->lacing_fill-=lr; + os->lacing_packet-=lr; + os->lacing_returned=0; + } + } + + /* check the serial number */ + if(serialno!=os->serialno)return(-1); + if(version>0)return(-1); + + _os_lacing_expand(os,segments+1); + + /* are we in sequence? */ + if(pageno!=os->pageno){ + s32 i; + + /* unroll previous partial packet (if any) */ + for(i=os->lacing_packet;ilacing_fill;i++) + os->body_fill-=os->lacing_vals[i]&0xff; + os->lacing_fill=os->lacing_packet; + + /* make a note of dropped data in segment table */ + if(os->pageno!=-1){ + os->lacing_vals[os->lacing_fill++]=0x400; + os->lacing_packet++; + } + + /* are we a 'continued packet' page? If so, we'll need to skip + some segments */ + if(continued){ + bos=0; + for(;segptrbody_data+os->body_fill,body,bodysize); + os->body_fill+=bodysize; + } + + { + s32 saved=-1; + while(segptrlacing_vals[os->lacing_fill]=val; + os->granule_vals[os->lacing_fill]=-1; + + if(bos){ + os->lacing_vals[os->lacing_fill]|=0x100; + bos=0; + } + + if(val<255)saved=os->lacing_fill; + + os->lacing_fill++; + segptr++; + + if(val<255)os->lacing_packet=os->lacing_fill; + } + + /* set the granulepos on the last granuleval of the last full packet */ + if(saved!=-1){ + os->granule_vals[saved]=granulepos; + } + + } + + if(eos){ + os->e_o_s=1; + if(os->lacing_fill>0) + os->lacing_vals[os->lacing_fill-1]|=0x200; + } + + os->pageno=pageno+1; + + return(0); +} + +/* clear things to an initial state. Good to call, eg, before seeking */ +s32 ogg_sync_reset(ogg_sync_state *oy){ + oy->fill=0; + oy->returned=0; + oy->unsynced=0; + oy->headerbytes=0; + oy->bodybytes=0; + return(0); +} + +s32 ogg_stream_reset(ogg_stream_state *os){ + os->body_fill=0; + os->body_returned=0; + + os->lacing_fill=0; + os->lacing_packet=0; + os->lacing_returned=0; + + os->header_fill=0; + + os->e_o_s=0; + os->b_o_s=0; + os->pageno=-1; + os->packetno=0; + os->granulepos=0; + + return(0); +} + +s32 ogg_stream_reset_serialno(ogg_stream_state *os,s32 serialno){ + ogg_stream_reset(os); + os->serialno=serialno; + return(0); +} + +static s32 _packetout(ogg_stream_state *os,ogg_packet *op,s32 adv){ + + /* The last part of decode. We have the stream broken into packet + segments. Now we need to group them into packets (or return the + out of sync markers) */ + + s32 ptr=os->lacing_returned; + + if(os->lacing_packet<=ptr)return(0); + + if(os->lacing_vals[ptr]&0x400){ + /* we need to tell the codec there's a gap; it might need to + handle previous packet dependencies. */ + os->lacing_returned++; + os->packetno++; + return(-1); + } + + if(!op && !adv)return(1); /* just using peek as an inexpensive way + to ask if there's a whole packet + waiting */ + + /* Gather the whole packet. We'll have no holes or a partial packet */ + { + s32 size=os->lacing_vals[ptr]&0xff; + s32 bytes=size; + s32 eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */ + s32 bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */ + + while(size==255){ + s32 val=os->lacing_vals[++ptr]; + size=val&0xff; + if(val&0x200)eos=0x200; + bytes+=size; + } + + if(op){ + op->e_o_s=eos; + op->b_o_s=bos; + op->packet=os->body_data+os->body_returned; + op->packetno=os->packetno; + op->granulepos=os->granule_vals[ptr]; + op->bytes=bytes; + } + + if(adv){ + os->body_returned+=bytes; + os->lacing_returned=ptr+1; + os->packetno++; + } + } + return(1); +} + +s32 ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,1); +} + +s32 ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,0); +} + +void ogg_packet_clear(ogg_packet *op) { + free(op->packet); + memset(op, 0, sizeof(*op)); +} + diff --git a/src/media_tools/img.c b/src/media_tools/img.c new file mode 100644 index 0000000..eab16da --- /dev/null +++ b/src/media_tools/img.c @@ -0,0 +1,646 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#ifdef GPAC_HAS_PNG + +#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__) +#pragma comment(lib, "libpng") +#endif + +/*include png.h before setjmp.h, otherwise we get compilation errors*/ +#include + +#endif /*GPAC_HAS_PNG*/ + + +#ifdef GPAC_HAS_JPEG + +#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__) +#pragma comment(lib, "libjpeg") +#endif + +#include +#include + +#endif /*GPAC_HAS_JPEG*/ + +GF_EXPORT +void gf_img_parse(GF_BitStream *bs, u8 *OTI, u32 *mtype, u32 *width, u32 *height, char **dsi, u32 *dsi_len) +{ + u8 b1, b2, b3; + u32 size, type; + u64 pos; + pos = gf_bs_get_position(bs); + gf_bs_seek(bs, 0); + + *mtype = *width = *height = 0; + *OTI = 0; + if (dsi) { + *dsi = NULL; + *dsi_len = 0; + } + + b1 = gf_bs_read_u8(bs); + b2 = gf_bs_read_u8(bs); + b3 = gf_bs_read_u8(bs); + /*JPEG*/ + if ((b1==0xFF) && (b2==0xD8) && (b3==0xFF)) { + u32 offset = 0; + u32 Xdens, Ydens, nb_comp; + gf_bs_read_u8(bs); + gf_bs_skip_bytes(bs, 10); /*2 size, 5 JFIF\0, 2 version, 1 units*/ + Xdens = gf_bs_read_int(bs, 16); + Ydens = gf_bs_read_int(bs, 16); + nb_comp = 0; + + /*get frame header FFC0*/ + while (gf_bs_available(bs)) { + u32 type, w, h; + if (gf_bs_read_u8(bs) != 0xFF) continue; + if (!offset) offset = (u32)gf_bs_get_position(bs) - 1; + + type = gf_bs_read_u8(bs); + /*process all Start of Image markers*/ + switch (type) { + case 0xC0: + case 0xC1: + case 0xC2: + case 0xC3: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCD: + case 0xCE: + case 0xCF: + gf_bs_skip_bytes(bs, 3); + h = gf_bs_read_int(bs, 16); + w = gf_bs_read_int(bs, 16); + if ((w > *width) || (h > *height)) { + *width = w; + *height = h; + } + nb_comp = gf_bs_read_int(bs, 8); + break; + } + } + *OTI = 0x6C; + *mtype = GF_4CC('j','p','e','g'); + if (dsi) { + GF_BitStream *bs_dsi = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u16(bs_dsi, offset); + gf_bs_write_u16(bs_dsi, Xdens); + gf_bs_write_u16(bs_dsi, Ydens); + gf_bs_write_u8(bs_dsi, nb_comp); + gf_bs_get_content(bs_dsi, dsi, dsi_len); + gf_bs_del(bs_dsi); + } + } + /*PNG*/ + else if ((b1==0x89) && (b2==0x50) && (b3==0x4E)) { + /*check for PNG sig*/ + if ( (gf_bs_read_u8(bs) != 0x47) || (gf_bs_read_u8(bs) != 0x0D) || (gf_bs_read_u8(bs) != 0x0A) + || (gf_bs_read_u8(bs) != 0x1A) || (gf_bs_read_u8(bs) != 0x0A) ) goto exit; + gf_bs_read_u32(bs); + /*check for PNG IHDR*/ + if ( (gf_bs_read_u8(bs) != 'I') || (gf_bs_read_u8(bs) != 'H') + || (gf_bs_read_u8(bs) != 'D') || (gf_bs_read_u8(bs) != 'R')) goto exit; + + *width = gf_bs_read_u32(bs); + *height = gf_bs_read_u32(bs); + *OTI = 0x6D; + *mtype = GF_4CC('p','n','g',' '); + } + size = gf_bs_read_u8(bs); + type = gf_bs_read_u32(bs); + if ( ((size==12) && (type==GF_4CC('j','P',' ',' ') )) + || (type==GF_4CC('j','p','2','h') ) ) { + + if (type==GF_4CC('j','p','2','h')) { + *OTI = 0x6E; + *mtype = GF_4CC('j','p','2',' '); + goto j2k_restart; + } + + type = gf_bs_read_u32(bs); + if (type!=0x0D0A870A) goto exit; + + *OTI = 0x6E; + *mtype = GF_4CC('j','p','2',' '); + + while (gf_bs_available(bs)) { +j2k_restart: + size = gf_bs_read_u32(bs); + type = gf_bs_read_u32(bs); + switch (type) { + case GF_4CC('j','p','2','h'): + goto j2k_restart; + case GF_4CC('i','h','d','r'): + { + u16 nb_comp; + u8 BPC, C, UnkC, IPR; + *height = gf_bs_read_u32(bs); + *width = gf_bs_read_u32(bs); + nb_comp = gf_bs_read_u16(bs); + BPC = gf_bs_read_u8(bs); + C = gf_bs_read_u8(bs); + UnkC = gf_bs_read_u8(bs); + IPR = gf_bs_read_u8(bs); + + if (dsi) { + GF_BitStream *bs_dsi = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs_dsi, *height); + gf_bs_write_u32(bs_dsi, *width); + gf_bs_write_u16(bs_dsi, nb_comp); + gf_bs_write_u8(bs_dsi, BPC); + gf_bs_write_u8(bs_dsi, C); + gf_bs_write_u8(bs_dsi, UnkC); + gf_bs_write_u8(bs_dsi, IPR); + gf_bs_get_content(bs_dsi, dsi, dsi_len); + gf_bs_del(bs_dsi); + } + goto exit; + } + break; + default: + gf_bs_skip_bytes(bs, size-8); + break; + } + } + } + +exit: + gf_bs_seek(bs, pos); +} + +#ifdef GPAC_HAS_JPEG + +void _nonfatal_error(j_common_ptr cinfo) { } +void _nonfatal_error2(j_common_ptr cinfo, int lev) {} + +/*JPG context while decoding*/ +typedef struct +{ + struct jpeg_error_mgr pub; + jmp_buf jmpbuf; +} JPGErr; + +typedef struct +{ + /*io manager*/ + struct jpeg_source_mgr src; + + s32 skip; + struct jpeg_decompress_struct cinfo; +} JPGCtx; + +void _fatal_error(j_common_ptr cinfo) +{ + JPGErr *err = (JPGErr *) cinfo->err; + longjmp(err->jmpbuf, 1); +} + +void stub(j_decompress_ptr cinfo) {} + +/*a JPEG is always carried in a complete, single MPEG4 AU so no refill*/ +boolean fill_input_buffer(j_decompress_ptr cinfo) { return 0; } + +void skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + JPGCtx *jpx = (JPGCtx *) cinfo->src; + if (num_bytes > (long) jpx->src.bytes_in_buffer) { + jpx->skip = num_bytes - jpx->src.bytes_in_buffer; + jpx->src.next_input_byte += jpx->src.bytes_in_buffer; + jpx->src.bytes_in_buffer = 0; + } else { + jpx->src.bytes_in_buffer -= num_bytes; + jpx->src.next_input_byte += num_bytes; + jpx->skip = 0; + } +} + + +#define JPEG_MAX_SCAN_BLOCK_HEIGHT 16 + +GF_EXPORT +GF_Err gf_img_jpeg_dec(char *jpg, u32 jpg_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size, u32 dst_nb_comp) +{ + s32 i, j, scans, k; + u32 stride; + char *scan_line, *ptr, *tmp; + char *lines[JPEG_MAX_SCAN_BLOCK_HEIGHT]; + JPGErr jper; + JPGCtx jpx; + + jpx.cinfo.err = jpeg_std_error(&(jper.pub)); + jper.pub.error_exit = _fatal_error; + jper.pub.output_message = _nonfatal_error; + jper.pub.emit_message = _nonfatal_error2; + if (setjmp(jper.jmpbuf)) { + jpeg_destroy_decompress(&jpx.cinfo); + return GF_IO_ERR; + } + + /*create decompress struct*/ + jpeg_create_decompress(&jpx.cinfo); + + /*prepare IO*/ + jpx.src.init_source = stub; + jpx.src.fill_input_buffer = fill_input_buffer; + jpx.src.skip_input_data = skip_input_data; + jpx.src.resync_to_restart = jpeg_resync_to_restart; + jpx.src.term_source = stub; + jpx.skip = 0; + jpx.src.next_input_byte = jpg; + jpx.src.bytes_in_buffer = jpg_size; + jpx.cinfo.src = (void *) &jpx.src; + + /*read header*/ + do { + i = jpeg_read_header(&jpx.cinfo, TRUE); + } while (i == JPEG_HEADER_TABLES_ONLY); + /*we're supposed to have the full image in the buffer, wrong stream*/ + if (i == JPEG_SUSPENDED) { + jpeg_destroy_decompress(&jpx.cinfo); + return GF_NON_COMPLIANT_BITSTREAM; + } + + *width = jpx.cinfo.image_width; + *height = jpx.cinfo.image_height; + stride = *width * jpx.cinfo.num_components; + + switch (jpx.cinfo.num_components) { + case 1: + *pixel_format = GF_PIXEL_GREYSCALE; + break; + case 3: + *pixel_format = GF_PIXEL_RGB_24; + break; + default: + jpeg_destroy_decompress(&jpx.cinfo); + return GF_NON_COMPLIANT_BITSTREAM; + } + if (*dst_size < *height * *width * jpx.cinfo.num_components) { + *dst_size = *height * *width * jpx.cinfo.num_components; + jpeg_destroy_decompress(&jpx.cinfo); + return GF_BUFFER_TOO_SMALL; + } + + scan_line = NULL; + /*decode*/ + jpx.cinfo.do_fancy_upsampling = FALSE; + jpx.cinfo.do_block_smoothing = FALSE; + if (!jpeg_start_decompress(&jpx.cinfo)) { + if (scan_line) free(scan_line); + jpeg_destroy_decompress(&jpx.cinfo); + return GF_NON_COMPLIANT_BITSTREAM; + } + if (jpx.cinfo.rec_outbuf_height>JPEG_MAX_SCAN_BLOCK_HEIGHT) { + if (scan_line) free(scan_line); + jpeg_destroy_decompress(&jpx.cinfo); + return GF_IO_ERR; + } + + /*read scanlines (the scan is not one line by one line so alloc a placeholder for block scaning) */ + scan_line = malloc(sizeof(char) * stride * jpx.cinfo.rec_outbuf_height); + for (i = 0; i= dst_nb_comp) break; + tmp[c] = ptr[c]; + } + ptr += jpx.cinfo.num_components; + tmp += dst_nb_comp; + } + } + } + } + + /*done*/ + jpeg_finish_decompress(&jpx.cinfo); + jpeg_destroy_decompress(&jpx.cinfo); + + free(scan_line); + + return GF_OK; +} +#else + +GF_EXPORT +GF_Err gf_img_jpeg_dec(char *jpg, u32 jpg_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size, u32 dst_nb_comp) +{ + return GF_NOT_SUPPORTED; +} + +#endif /*GPAC_HAS_JPEG*/ + + +#ifdef GPAC_HAS_PNG + +typedef struct +{ + char *buffer; + u32 pos; + u32 size; +} GFpng; + +static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + GFpng *ctx = (GFpng*)png_ptr->io_ptr; + + if (ctx->pos + length > ctx->size) { + png_error(png_ptr, "Read Error"); + } else { + memcpy(data, (char*) ctx->buffer + ctx->pos, length); + ctx->pos += length; + } +} +static void user_error_fn(png_structp png_ptr,png_const_charp error_msg) +{ + longjmp(png_ptr->jmpbuf, 1); +} + +GF_EXPORT +GF_Err gf_img_png_dec(char *png, u32 png_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size) +{ + GFpng udta; + png_struct *png_ptr; + png_info *info_ptr; + png_byte **rows; + u32 i, stride, bpp; + + if ((png_size<8) || png_sig_cmp(png, 0, 8) ) return GF_NON_COMPLIANT_BITSTREAM; + + udta.buffer = png; + udta.size = png_size; + udta.pos = 0; + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) &udta, NULL, NULL); + if (!png_ptr) return GF_IO_ERR; + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return GF_IO_ERR; + } + if (setjmp(png_ptr->jmpbuf)) { + png_destroy_info_struct(png_ptr,(png_infopp) & info_ptr); + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return GF_IO_ERR; + } + png_set_read_fn(png_ptr, &udta, (png_rw_ptr) user_read_data); + png_set_error_fn(png_ptr, &udta, (png_error_ptr) user_error_fn, NULL); + + png_read_info(png_ptr, info_ptr); + + /*unpaletize*/ + if (info_ptr->color_type==PNG_COLOR_TYPE_PALETTE) { + png_set_expand(png_ptr); + png_read_update_info(png_ptr, info_ptr); + } + if (info_ptr->num_trans) { + png_set_tRNS_to_alpha(png_ptr); + png_read_update_info(png_ptr, info_ptr); + } + + bpp = info_ptr->pixel_depth / 8; + *width = info_ptr->width; + *height = info_ptr->height; + + switch (info_ptr->pixel_depth) { + case 8: + *pixel_format = GF_PIXEL_GREYSCALE; + break; + case 16: + *pixel_format = GF_PIXEL_ALPHAGREY; + break; + case 24: + *pixel_format = GF_PIXEL_RGB_24; + break; + case 32: + *pixel_format = GF_PIXEL_RGBA; + break; + default: + png_destroy_info_struct(png_ptr,(png_infopp) & info_ptr); + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return GF_NOT_SUPPORTED; + + } + + /*new cfg, reset*/ + if (*dst_size != info_ptr->width * info_ptr->height * bpp) { + *dst_size = info_ptr->width * info_ptr->height * bpp; + png_destroy_info_struct(png_ptr,(png_infopp) & info_ptr); + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return GF_BUFFER_TOO_SMALL; + } + *dst_size = info_ptr->width * info_ptr->height * bpp; + + /*read*/ + stride = png_get_rowbytes(png_ptr, info_ptr); + rows = (png_bytepp) malloc(sizeof(png_bytep) * info_ptr->height); + for (i=0; iheight; i++) { + rows[i] = dst + i*stride; + } + png_read_image(png_ptr, rows); + png_read_end(png_ptr, NULL); + free(rows); + + png_destroy_info_struct(png_ptr,(png_infopp) & info_ptr); + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return GF_OK; +} + + +void my_png_write(png_structp png, png_bytep data, png_size_t size) +{ + GFpng *p = (GFpng *)png->io_ptr; + memcpy(p->buffer+p->pos, data, sizeof(char)*size); + p->pos += size; +} +void my_png_flush(png_structp png) +{ +} + +/* write a png file */ +GF_Err gf_img_png_enc(char *data, u32 width, u32 height, u32 pixel_format, char *dst, u32 *dst_size) +{ + GFpng udta; + png_color_8 sig_bit; + png_uint_32 k; + u32 type, nb_comp; + png_bytep *row_pointers; + png_structp png_ptr; + png_infop info_ptr; + + type = 0; + nb_comp = 0; + switch (pixel_format) { + case GF_PIXEL_GREYSCALE: + nb_comp = 1; + type = PNG_COLOR_TYPE_GRAY; + break; + case GF_PIXEL_ALPHAGREY: + nb_comp = 1; + type = PNG_COLOR_TYPE_GRAY_ALPHA; + break; + case GF_PIXEL_RGB_24: + case GF_PIXEL_BGR_24: + case GF_PIXEL_RGB_32: + case GF_PIXEL_BGR_32: + nb_comp = 3; + type = PNG_COLOR_TYPE_RGB; + break; + case GF_PIXEL_RGBA: + case GF_PIXEL_ARGB: + nb_comp = 4; + type = PNG_COLOR_TYPE_RGB_ALPHA; + break; + default: + return GF_NOT_SUPPORTED; + } + if (*dst_size < width*height*nb_comp) return GF_BUFFER_TOO_SMALL; + + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + // png_voidp user_error_ptr, user_error_fn, user_warning_fn); + + if (png_ptr == NULL) return GF_IO_ERR; + + /* Allocate/initialize the image information data. REQUIRED */ + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + png_destroy_write_struct(&png_ptr, png_infopp_NULL); + return GF_IO_ERR; + } + + /* Set error handling. REQUIRED if you aren't supplying your own + * error handling functions in the png_create_write_struct() call. + */ + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + return GF_NON_COMPLIANT_BITSTREAM; + } + + udta.buffer = dst; + udta.pos = 0; + png_set_write_fn(png_ptr, &udta, my_png_write, my_png_flush); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + /* otherwise, if we are dealing with a color image then */ + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + sig_bit.gray = 8; + sig_bit.alpha = 8; + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + +#if 0 + /* Optionally write comments into the image */ + text_ptr[0].key = "Title"; + text_ptr[0].text = "Mona Lisa"; + text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr[1].key = "Author"; + text_ptr[1].text = "Leonardo DaVinci"; + text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr[2].key = "Description"; + text_ptr[2].text = ""; + text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt; +#ifdef PNG_iTXt_SUPPORTED + text_ptr[0].lang = NULL; + text_ptr[1].lang = NULL; + text_ptr[2].lang = NULL; +#endif + png_set_text(png_ptr, info_ptr, text_ptr, 3); +#endif + + png_write_info(png_ptr, info_ptr); + + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + png_set_shift(png_ptr, &sig_bit); + + /* pack pixels into bytes */ + png_set_packing(png_ptr); + + if (pixel_format==GF_PIXEL_ARGB) + png_set_swap_alpha(png_ptr); + + if ((pixel_format==GF_PIXEL_RGB_32) || (pixel_format==GF_PIXEL_BGR_32)) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); + + if ((pixel_format==GF_PIXEL_BGR_24) || (pixel_format==GF_PIXEL_BGR_32)) + png_set_bgr(png_ptr); + + row_pointers = malloc(sizeof(png_bytep)*height); + for (k = 0; k < height; k++) + row_pointers[k] = data + k*width*nb_comp; + + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, info_ptr); + free(row_pointers); + /* clean up after the write, and free any memory allocated */ + png_destroy_write_struct(&png_ptr, &info_ptr); + + *dst_size = udta.pos; + return GF_OK; +} + +#else +GF_EXPORT +GF_Err gf_img_png_dec(char *png, u32 png_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size) +{ + return GF_NOT_SUPPORTED; +} +GF_EXPORT +GF_Err gf_img_png_enc(char *data, u32 width, u32 height, u32 pixel_format, char *dst, u32 *dst_size) +{ + return GF_NOT_SUPPORTED; +} + +#endif /*GPAC_HAS_PNG*/ + + diff --git a/src/media_tools/ismacryp.c b/src/media_tools/ismacryp.c new file mode 100644 index 0000000..5d667c3 --- /dev/null +++ b/src/media_tools/ismacryp.c @@ -0,0 +1,949 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre - 2005 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include +#include +#include +#include +#include + + +typedef struct +{ + GF_List *tcis; + Bool has_common_key; + Bool in_text_header; +} ISMACrypInfo; + +void isma_ea_node_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + GF_XMLAttribute *att; + GF_TrackCryptInfo *tkc; + u32 i; + ISMACrypInfo *info = (ISMACrypInfo *)sax_cbck; + + if (!strcmp(node_name, "OMATextHeader")) { + info->in_text_header = 1; + return; + } + if (!strcmp(node_name, "ISMACrypTrack") || !strcmp(node_name, "OMATrack")) { + GF_SAFEALLOC(tkc, GF_TrackCryptInfo); + gf_list_add(info->tcis, tkc); + + if (!strcmp(node_name, "OMATrack")) { + tkc->enc_type = 1; + /*default to AES 128 in OMA*/ + tkc->encryption = 2; + } + + for (i=0; iname, "trackID") || !stricmp(att->name, "ID")) { + if (!strcmp(att->value, "*")) info->has_common_key = 1; + else tkc->trackID = atoi(att->value); + } + else if (!stricmp(att->name, "key")) { + char *sKey = att->value; + if (!strnicmp(sKey, "0x", 2)) sKey += 2; + if (strlen(sKey) == 32) { + u32 j; + for (j=0; j<32; j+=2) { + u32 v; + char szV[5]; + sprintf(szV, "%c%c", sKey[j], sKey[j+1]); + sscanf(szV, "%x", &v); + tkc->key[j/2] = v; + } + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[ISMA E&A] Key is not 16-bytes long - skipping\n")); + } + } + else if (!stricmp(att->name, "salt")) { + u32 len, j; + char *sKey = att->value; + if (!strnicmp(sKey, "0x", 2)) sKey += 2; + len = strlen(sKey); + for (j=0; jsalt[j/2] = v; + } + } + else if (!stricmp(att->name, "kms_URI")) strcpy(tkc->KMS_URI, att->value); + else if (!stricmp(att->name, "rightsIssuerURL")) strcpy(tkc->KMS_URI, att->value); + else if (!stricmp(att->name, "scheme_URI")) strcpy(tkc->Scheme_URI, att->value); + else if (!stricmp(att->name, "selectiveType")) { + if (!stricmp(att->value, "Rap")) tkc->sel_enc_type = GF_ISMACRYP_SELENC_RAP; + else if (!stricmp(att->value, "Non-Rap")) tkc->sel_enc_type = GF_ISMACRYP_SELENC_NON_RAP; + else if (!stricmp(att->value, "Rand")) tkc->sel_enc_type = GF_ISMACRYP_SELENC_RAND; + else if (!strnicmp(att->value, "Rand", 4)) { + tkc->sel_enc_type = GF_ISMACRYP_SELENC_RAND_RANGE; + tkc->sel_enc_range = atoi(&att->value[4]); + } + else if (sscanf(att->value, "%d", &tkc->sel_enc_range)==1) { + if (tkc->sel_enc_range==1) tkc->sel_enc_range = 0; + else tkc->sel_enc_type = GF_ISMACRYP_SELENC_RANGE; + } + else if (!strnicmp(att->value, "Preview", 7)) { + tkc->sel_enc_type = GF_ISMACRYP_SELENC_PREVIEW; + } + } + else if (!stricmp(att->name, "Preview")) { + tkc->sel_enc_type = GF_ISMACRYP_SELENC_PREVIEW; + sscanf(att->value, "%d", &tkc->sel_enc_range); + } + else if (!stricmp(att->name, "ipmpType")) { + if (!stricmp(att->value, "None")) tkc->ipmp_type = 0; + else if (!stricmp(att->value, "IPMP")) tkc->sel_enc_type = 1; + else if (!stricmp(att->value, "IPMPX")) tkc->sel_enc_type = 2; + } + else if (!stricmp(att->name, "ipmpDescriptorID")) tkc->ipmp_desc_id = atoi(att->value); + else if (!stricmp(att->name, "encryptionMethod")) { + if (!strcmp(att->value, "AES_128_CBC")) tkc->encryption = 1; + else if (!strcmp(att->value, "None")) tkc->encryption = 0; + else if (!strcmp(att->value, "AES_128_CTR") || !strcmp(att->value, "default")) tkc->encryption = 2; + } + else if (!stricmp(att->name, "contentID")) strcpy(tkc->Scheme_URI, att->value); + else if (!stricmp(att->name, "rightsIssuerURL")) strcpy(tkc->KMS_URI, att->value); + else if (!stricmp(att->name, "transactionID")) { + if (strlen(att->value)<=16) strcpy(tkc->TransactionID, att->value); + } + else if (!stricmp(att->name, "textualHeaders")) { + } + } + } +} + +void isma_ea_node_end(void *sax_cbck, const char *node_name, const char *name_space) +{ + ISMACrypInfo *info = (ISMACrypInfo *)sax_cbck; + if (!strcmp(node_name, "OMATextHeader")) { + info->in_text_header = 0; + return; + } +} + +void isma_ea_text(void *sax_cbck, const char *text, Bool is_cdata) +{ + u32 len; + GF_TrackCryptInfo *tkc; + ISMACrypInfo *info = (ISMACrypInfo *)sax_cbck; + + if (!info->in_text_header) return; + + tkc = (GF_TrackCryptInfo *) gf_list_last(info->tcis); + len = strlen(text); + if (len+tkc->TextualHeadersLen > 5000) return; + + if (tkc->TextualHeadersLen) { + tkc->TextualHeadersLen ++; + tkc->TextualHeaders[tkc->TextualHeadersLen] = 0; + } + + memcpy(tkc->TextualHeaders + tkc->TextualHeadersLen, text, sizeof(char)*len); + tkc->TextualHeadersLen += len; + tkc->TextualHeaders[tkc->TextualHeadersLen] = 0; +} + +static void del_crypt_info(ISMACrypInfo *info) +{ + while (gf_list_count(info->tcis)) { + GF_TrackCryptInfo *tci = (GF_TrackCryptInfo *)gf_list_last(info->tcis); + gf_list_rem_last(info->tcis); + free(tci); + } + gf_list_del(info->tcis); + free(info); +} + +static ISMACrypInfo *load_crypt_file(const char *file) +{ + GF_Err e; + ISMACrypInfo *info; + GF_SAXParser *sax; + GF_SAFEALLOC(info, ISMACrypInfo); + info->tcis = gf_list_new(); + sax = gf_xml_sax_new(isma_ea_node_start, isma_ea_node_end, isma_ea_text, info); + e = gf_xml_sax_parse_file(sax, file, NULL); + gf_xml_sax_del(sax); + if (e<0) { + del_crypt_info(info); + return NULL; + } + return info; +} + + +GF_EXPORT +GF_Err gf_ismacryp_gpac_get_info(u32 stream_id, char *drm_file, char *key, char *salt) +{ + GF_Err e; + u32 i, count; + ISMACrypInfo *info; + GF_TrackCryptInfo *tci; + + e = GF_OK; + info = load_crypt_file(drm_file); + if (!info) return GF_NOT_SUPPORTED; + count = gf_list_count(info->tcis); + for (i=0; itcis, i); + if ((info->has_common_key && !tci->trackID) || (tci->trackID == stream_id) ) { + memcpy(key, tci->key, sizeof(char)*16); + memcpy(salt, tci->salt, sizeof(char)*8); + e = GF_OK; + break; + } + } + del_crypt_info(info); + return e; +} + +GF_EXPORT +Bool gf_ismacryp_mpeg4ip_get_info(char *kms_uri, char *key, char *salt) +{ + char szPath[1024], catKey[24]; + u32 i, x; + Bool got_it; + FILE *kms; + strcpy(szPath, getenv("HOME")); + strcat(szPath , "/.kms_data"); + got_it = 0; + kms = fopen(szPath, "r"); + while (kms && !feof(kms)) { + if (!fgets(szPath, 1024, kms)) break; + szPath[strlen(szPath) - 1] = 0; + if (stricmp(szPath, kms_uri)) continue; + for (i=0; i<24; i++) { + if (!fscanf(kms, "%x", &x)) break; + catKey[i] = x; + } + if (i==24) got_it = 1; + break; + } + if (kms) fclose(kms); + if (got_it) { + /*watchout, MPEG4IP stores SALT|KEY, NOT KEY|SALT*/ + memcpy(key, catKey+8, sizeof(char)*16); + memcpy(salt, catKey, sizeof(char)*8); + return 1; + } + return 0; +} + +#ifndef GPAC_READ_ONLY + +static GFINLINE void resync_IV(GF_Crypt *mc, u64 BSO, char *salt) +{ + char IV[17]; + u64 count; + u32 remain; + GF_BitStream *bs; + count = BSO / 16; + remain = (u32) (BSO % 16); + + /*format IV to begin of counter*/ + bs = gf_bs_new(IV, 17, GF_BITSTREAM_WRITE); + gf_bs_write_u8(bs, 0); /*begin of counter*/ + gf_bs_write_data(bs, salt, 8); + gf_bs_write_u64(bs, (s64) count); + gf_bs_del(bs); + gf_crypt_set_state(mc, IV, 17); + + /*decrypt remain bytes*/ + if (remain) { + char dummy[20]; + gf_crypt_decrypt(mc, dummy, remain); + } +} + +GF_EXPORT +GF_Err gf_ismacryp_decrypt_track(GF_ISOFile *mp4, GF_TrackCryptInfo *tci, void (*progress)(void *cbk, u32 done, u32 total), void *cbk) +{ + GF_Err e; + Bool use_sel_enc; + u32 track, count, i, j, si, is_avc; + GF_ISOSample *samp; + GF_ISMASample *ismasamp; + GF_Crypt *mc; + unsigned char IV[17]; + u32 IV_size; + Bool prev_sample_encrypted; + GF_ESD *esd; + + track = gf_isom_get_track_by_id(mp4, tci->trackID); + e = gf_isom_get_ismacryp_info(mp4, track, 1, &is_avc, NULL, NULL, NULL, NULL, &use_sel_enc, &IV_size, NULL); + is_avc = (is_avc==GF_4CC('2','6','4','b')) ? 1 : 0; + + + mc = gf_crypt_open("AES-128", "CTR"); + if (!mc) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Cannot open AES-128 CTR cryptography\n", tci->trackID)); + return GF_IO_ERR; + } + + memset(IV, 0, sizeof(char)*16); + memcpy(IV, tci->salt, sizeof(char)*8); + e = gf_crypt_init(mc, tci->key, 16, IV); + if (e) { + gf_crypt_close(mc); + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] cannot initialize AES-128 CTR (%s)\n", gf_error_to_string(e))); + return GF_IO_ERR; + } + + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[ISMA E&A] Decrypting track ID %d - KMS: %s%s\n", tci->trackID, tci->KMS_URI, use_sel_enc ? " - Selective Decryption" : "")); + + /*start as initialized*/ + prev_sample_encrypted = 1; + /* decrypt each sample */ + count = gf_isom_get_sample_count(mp4, track); + for (i = 0; i < count; i++) { + samp = gf_isom_get_sample(mp4, track, i+1, &si); + ismasamp = gf_isom_get_ismacryp_sample(mp4, track, samp, si); + + free(samp->data); + samp->data = ismasamp->data; + samp->dataLength = ismasamp->dataLength; + ismasamp->data = NULL; + ismasamp->dataLength = 0; + + /* Decrypt payload */ + if (ismasamp->flags & GF_ISOM_ISMA_IS_ENCRYPTED) { + /*restore IV*/ + if (!prev_sample_encrypted) resync_IV(mc, ismasamp->IV, tci->salt); + gf_crypt_decrypt(mc, samp->data, samp->dataLength); + } + prev_sample_encrypted = (ismasamp->flags & GF_ISOM_ISMA_IS_ENCRYPTED); + gf_isom_ismacryp_delete_sample(ismasamp); + + /*replace AVC start codes (0x00000001) by nalu size*/ + if (is_avc) { + u32 nalu_size; + u32 remain = samp->dataLength; + char *start, *end; + start = samp->data; + end = start + 4; + while (remain>4) { + if (!end[0] && !end[1] && !end[2] && (end[3]==0x01)) { + nalu_size = end - start - 4; + start[0] = (nalu_size>>24)&0xFF; + start[1] = (nalu_size>>16)&0xFF; + start[2] = (nalu_size>>8)&0xFF; + start[3] = (nalu_size)&0xFF; + start = end; + end = start+4; + continue; + } + end++; + remain--; + } + nalu_size = end - start - 4; + start[0] = (nalu_size>>24)&0xFF; + start[1] = (nalu_size>>16)&0xFF; + start[2] = (nalu_size>>8)&0xFF; + start[3] = (nalu_size)&0xFF; + } + + gf_isom_update_sample(mp4, track, i+1, samp, 1); + gf_isom_sample_del(&samp); + gf_set_progress("ISMA Decrypt", i+1, count); + } + + gf_crypt_close(mc); + /*and remove protection info*/ + e = gf_isom_remove_ismacryp_protection(mp4, track, 1); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Error ISMACryp signature from trackID %d: %s\n", tci->trackID, gf_error_to_string(e))); + } + + /*remove all IPMP ptrs*/ + esd = gf_isom_get_esd(mp4, track, 1); + if (esd) { + while (gf_list_count(esd->IPMPDescriptorPointers)) { + GF_Descriptor *d = (GF_Descriptor *)gf_list_get(esd->IPMPDescriptorPointers, 0); + gf_list_rem(esd->IPMPDescriptorPointers, 0); + gf_odf_desc_del(d); + } + gf_isom_change_mpeg4_description(mp4, track, 1, esd); + gf_odf_desc_del((GF_Descriptor *)esd); + } + + /*update OD track if any*/ + track = 0; + for (i=0; idata, samp->dataLength); + gf_odf_codec_decode(cod); + for (j=0; jCommandList); j++) { + GF_IPMPUpdate *com = (GF_IPMPUpdate *)gf_list_get(cod->CommandList, j); + if (com->tag != GF_ODF_IPMP_UPDATE_TAG) continue; + gf_list_rem(cod->CommandList, j); + j--; + gf_odf_com_del((GF_ODCom **)&com); + } + free(samp->data); + samp->data = NULL; + samp->dataLength = 0; + gf_odf_codec_encode(cod, 1); + gf_odf_codec_get_au(cod, &samp->data, &samp->dataLength); + gf_odf_codec_del(cod); + gf_isom_update_sample(mp4, i+1, 1, samp, 1); + gf_isom_sample_del(&samp); + + /*remove IPMPToolList if any*/ + if (mp4->moov->iods && (mp4->moov->iods->descriptor->tag == GF_ODF_ISOM_IOD_TAG) ) { + GF_IsomInitialObjectDescriptor *iod = (GF_IsomInitialObjectDescriptor *)mp4->moov->iods->descriptor; + if (iod->IPMPToolList) gf_odf_desc_del((GF_Descriptor*) iod->IPMPToolList); + iod->IPMPToolList = NULL; + } + break; + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_ismacryp_decrypt_file(GF_ISOFile *mp4, const char *drm_file) +{ + GF_Err e; + u32 i, idx, count, common_idx, nb_tracks, scheme_type, cur_tk; + const char *scheme_URI, *KMS_URI; + ISMACrypInfo *info; + Bool is_oma; + GF_TrackCryptInfo *a_tci, tci; + + is_oma = 0; + count = 0; + info = NULL; + if (drm_file) { + info = load_crypt_file(drm_file); + if (!info) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Cannot open or validate xml file %s\n", drm_file)); + return GF_NOT_SUPPORTED; + } + count = gf_list_count(info->tcis); + } + + common_idx=0; + if (info && info->has_common_key) { + for (common_idx=0; common_idxtcis, common_idx); + if (!a_tci->trackID) break; + } + } + + nb_tracks = gf_isom_get_track_count(mp4); + cur_tk = 1; + e = GF_OK; + for (i=0; itcis, idx); + if (a_tci->trackID == trackID) break; + } + if (idx==count) { + if (!drm_file || info->has_common_key) idx = common_idx; + /*no available KMS info for this track*/ + else continue; + } + if (count) { + a_tci = (GF_TrackCryptInfo *)gf_list_get(info->tcis, idx); + memcpy(&tci, a_tci, sizeof(GF_TrackCryptInfo)); + } else { + memset(&tci, 0, sizeof(GF_TrackCryptInfo)); + tci.trackID = trackID; + } + + if (gf_isom_is_ismacryp_media(mp4, i+1, 1)) { + e = gf_isom_get_ismacryp_info(mp4, i+1, 1, NULL, &scheme_type, NULL, &scheme_URI, &KMS_URI, NULL, NULL, NULL); + } else if (gf_isom_is_omadrm_media(mp4, i+1, 1)) { + if (!drm_file) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Cannot decrypt OMA (P)DCF file without GPAC's DRM file & keys\n")); + continue; + } + KMS_URI = "OMA DRM"; + is_oma = 1; + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[ISMA E&A] TrackID %d encrypted with unknown scheme %s - skipping\n", trackID, gf_4cc_to_str(scheme_type) )); + continue; + } + /*get key and salt from KMS*/ + /*GPAC*/ + if (!strnicmp(KMS_URI, "(key)", 5)) { + char data[100]; + gf_base64_decode((char*)KMS_URI+5, strlen(KMS_URI)-5, data, 100); + memcpy(tci.key, data, sizeof(char)*16); + memcpy(tci.salt, data+16, sizeof(char)*8); + } + /*MPEG4IP*/ + else if (!stricmp(KMS_URI, "AudioKey") || !stricmp(KMS_URI, "VideoKey")) { + if (!gf_ismacryp_mpeg4ip_get_info((char *) KMS_URI, tci.key, tci.salt)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Couldn't load MPEG4IP ISMACryp keys for TrackID %d\n", trackID)); + continue; + } + } else if (!drm_file) { + FILE *test = NULL; + if (!stricmp(scheme_URI, "urn:gpac:isma:encryption_scheme")) test = fopen(KMS_URI, "rt"); + + if (!test) { + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[ISMA E&A] TrackID %d does not contain decryption keys - skipping\n", trackID)); + continue; + } + fclose(test); + if (gf_ismacryp_gpac_get_info(tci.trackID, (char *) KMS_URI, tci.key, tci.salt) != GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Couldn't load TrackID %d keys in GPAC DRM file %s\n", tci.trackID, KMS_URI)); + continue; + } + } + + if (KMS_URI && strlen(tci.KMS_URI) && strcmp(KMS_URI, tci.KMS_URI) ) + GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[ISMA E&A] KMS URI for trackID %d Mismatch\n", trackID)); + + if (drm_file || (KMS_URI && strncmp(KMS_URI, "(key)", 5)) ) { + strcpy(tci.KMS_URI, KMS_URI ? KMS_URI : ""); + } else { + strcpy(tci.KMS_URI, "self-contained"); + } + e = gf_ismacryp_decrypt_track(mp4, &tci, NULL, NULL); + if (e) break; + } + if (is_oma) { + e = gf_isom_set_brand_info(mp4, GF_4CC('i','s','o','2'), 0x00000001); + if (!e) e = gf_isom_modify_alternate_brand(mp4, GF_4CC('o','d','c','f'), 0); + } + if (info) del_crypt_info(info); + return e; +} + +GF_EXPORT +GF_Err gf_ismacryp_encrypt_track(GF_ISOFile *mp4, GF_TrackCryptInfo *tci, void (*progress)(void *cbk, u32 done, u32 total), void *cbk) +{ + char IV[16]; + GF_ISOSample *samp; + GF_ISMASample *isamp; + GF_Crypt *mc; + u32 i, count, di, track, IV_size, rand, avc_size_length; + u64 BSO, range_end; + GF_ESD *esd; + GF_IPMPPtr *ipmpdp; + GF_IPMP_Descriptor *ipmpd; + GF_IPMPUpdate *ipmpdU; + GF_IPMPX_ISMACryp *ismac; + GF_Err e; + Bool prev_sample_encryped, has_crypted_samp; + + avc_size_length = 0; + track = gf_isom_get_track_by_id(mp4, tci->trackID); + if (!track) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Cannot find TrackID %d in input file - skipping\n", tci->trackID)); + return GF_OK; + } + esd = gf_isom_get_esd(mp4, track, 1); + if (esd && (esd->decoderConfig->streamType==GF_STREAM_OD)) { + gf_odf_desc_del((GF_Descriptor *) esd); + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Cannot encrypt OD tracks - skipping")); + return GF_NOT_SUPPORTED; + } + if (esd) { + if (esd->decoderConfig->objectTypeIndication==0x21) avc_size_length = 1; + gf_odf_desc_del((GF_Descriptor*) esd); + } + if (avc_size_length) { + GF_AVCConfig *avccfg = gf_isom_avc_config_get(mp4, track, 1); + avc_size_length = avccfg->nal_unit_size; + gf_odf_avc_cfg_del(avccfg); + if (avc_size_length != 4) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Cannot encrypt AVC/H264 track with %d size_length field - onmy 4 supported\n", avc_size_length)); + return GF_NOT_SUPPORTED; + } + } + + if (!tci->enc_type && !strlen(tci->Scheme_URI)) strcpy(tci->Scheme_URI, "urn:gpac:isma:encryption_scheme"); + + if (!gf_isom_has_sync_points(mp4, track) && + ((tci->sel_enc_type==GF_ISMACRYP_SELENC_RAP) || (tci->sel_enc_type==GF_ISMACRYP_SELENC_NON_RAP)) ) { + GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[ISMA E&A] All samples in trackID %d are random access - disabling selective encryption\n", tci->trackID)); + tci->sel_enc_type = GF_ISMACRYP_SELENC_NONE; + } + else if ((tci->sel_enc_type==GF_ISMACRYP_SELENC_RAND) || (tci->sel_enc_type==GF_ISMACRYP_SELENC_RAND_RANGE)) { + gf_rand_init(1); + } + + BSO = gf_isom_get_media_data_size(mp4, track); + if (tci->enc_type==0) { + if (BSO<0xFFFF) IV_size = 2; + else if (BSO<0xFFFFFFFF) IV_size = 4; + else IV_size = 8; + } else { + /*128 bit IV in OMA*/ + IV_size = 16; + } + + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[ISMA E&A] Encrypting track ID %d - KMS: %s%s\n", tci->trackID, tci->KMS_URI, tci->sel_enc_type ? " - Selective Encryption" : "")); + + /*init crypto*/ + memset(IV, 0, sizeof(char)*16); + memcpy(IV, tci->salt, sizeof(char)*8); + mc = gf_crypt_open("AES-128", "CTR"); + if (!mc) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Cannot open AES-128 CTR\n")); + return GF_IO_ERR; + } + e = gf_crypt_init(mc, tci->key, 16, IV); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Cannot initialize AES-128 CTR (%s)\n", gf_error_to_string(e)) ); + gf_crypt_close(mc); + return GF_IO_ERR; + } + if (!stricmp(tci->KMS_URI, "self")) { + char Data[100], d64[100]; + u32 s64; + memcpy(Data, tci->key, sizeof(char)*16); + memcpy(Data+16, tci->salt, sizeof(char)*8); + s64 = gf_base64_encode(Data, 24, d64, 100); + d64[s64] = 0; + strcpy(tci->KMS_URI, "(key)"); + strcat(tci->KMS_URI, d64); + } + + /*create ISMA protection*/ + if (tci->enc_type==0) { + e = gf_isom_set_ismacryp_protection(mp4, track, 1, GF_ISOM_ISMACRYP_SCHEME, 1, + tci->Scheme_URI, tci->KMS_URI, (tci->sel_enc_type!=0) ? 1 : 0, 0, IV_size); + } else { + if ((tci->sel_enc_type==GF_ISMACRYP_SELENC_PREVIEW) && tci->sel_enc_range) { + char *szPreview = tci->TextualHeaders + tci->TextualHeadersLen; + sprintf(szPreview, "PreviewRange:%d", tci->sel_enc_range); + tci->TextualHeadersLen += strlen(szPreview)+1; + } + e = gf_isom_set_oma_protection(mp4, track, 1, + strlen(tci->Scheme_URI) ? tci->Scheme_URI : NULL, + tci->KMS_URI, + tci->encryption, BSO, + tci->TextualHeadersLen ? tci->TextualHeaders : NULL, + tci->TextualHeadersLen, + (tci->sel_enc_type!=0) ? 1 : 0, 0, IV_size); + } + if (e) return e; + + has_crypted_samp = 0; + BSO = 0; + prev_sample_encryped = 1; + range_end = 0; + if (tci->sel_enc_type==GF_ISMACRYP_SELENC_PREVIEW) { + range_end = gf_isom_get_media_timescale(mp4, track) * tci->sel_enc_range; + } + + if (gf_isom_has_time_offset(mp4, track)) gf_isom_set_cts_packing(mp4, track, 1); + + count = gf_isom_get_sample_count(mp4, track); + for (i = 0; i < count; i++) { + samp = gf_isom_get_sample(mp4, track, i+1, &di); + + isamp = gf_isom_ismacryp_new_sample(); + isamp->IV_length = IV_size; + isamp->KI_length = 0; + + switch (tci->sel_enc_type) { + case GF_ISMACRYP_SELENC_RAP: + if (samp->IsRAP) isamp->flags |= GF_ISOM_ISMA_IS_ENCRYPTED; + break; + case GF_ISMACRYP_SELENC_NON_RAP: + if (!samp->IsRAP) isamp->flags |= GF_ISOM_ISMA_IS_ENCRYPTED; + break; + /*random*/ + case GF_ISMACRYP_SELENC_RAND: + rand = gf_rand(); + if (rand%2) isamp->flags |= GF_ISOM_ISMA_IS_ENCRYPTED; + break; + /*random every sel_freq samples*/ + case GF_ISMACRYP_SELENC_RAND_RANGE: + if (!(i%tci->sel_enc_range)) has_crypted_samp = 0; + if (!has_crypted_samp) { + rand = gf_rand(); + if (!(rand%tci->sel_enc_range)) isamp->flags |= GF_ISOM_ISMA_IS_ENCRYPTED; + + if (!(isamp->flags & GF_ISOM_ISMA_IS_ENCRYPTED) && !( (1+i)%tci->sel_enc_range)) { + isamp->flags |= GF_ISOM_ISMA_IS_ENCRYPTED; + } + has_crypted_samp = (isamp->flags & GF_ISOM_ISMA_IS_ENCRYPTED); + } + break; + /*every sel_freq samples*/ + case GF_ISMACRYP_SELENC_RANGE: + if (!(i%tci->sel_enc_type)) isamp->flags |= GF_ISOM_ISMA_IS_ENCRYPTED; + break; + case GF_ISMACRYP_SELENC_PREVIEW: + if (samp->DTS + samp->CTS_Offset >= range_end) + isamp->flags |= GF_ISOM_ISMA_IS_ENCRYPTED; + break; + case 0: + isamp->flags |= GF_ISOM_ISMA_IS_ENCRYPTED; + break; + default: + break; + } + if (tci->sel_enc_type) isamp->flags |= GF_ISOM_ISMA_USE_SEL_ENC; + + /*isma e&a stores AVC1 in AVC/H264 annex B bitstream fashion, with 0x00000001 start codes*/ + if (avc_size_length) { + u32 done = 0; + u8 *d = samp->data; + while (done < samp->dataLength) { + u32 nal_size = GF_4CC(d[0], d[1], d[2], d[3]); + d[0] = d[1] = d[2] = 0; d[3] = 1; + d += 4 + nal_size; + done += 4 + nal_size; + } + } + + if (isamp->flags & GF_ISOM_ISMA_IS_ENCRYPTED) { + /*resync IV*/ + if (!prev_sample_encryped) resync_IV(mc, BSO, tci->salt); + gf_crypt_encrypt(mc, samp->data, samp->dataLength); + prev_sample_encryped = 1; + } else { + prev_sample_encryped = 0; + } + + + isamp->IV = BSO; + BSO += samp->dataLength; + isamp->data = samp->data; + isamp->dataLength = samp->dataLength; + samp->data = NULL; + samp->dataLength = 0; + + gf_isom_ismacryp_sample_to_sample(isamp, samp); + gf_isom_ismacryp_delete_sample(isamp); + gf_isom_update_sample(mp4, track, i+1, samp, 1); + gf_isom_sample_del(&samp); + gf_set_progress("ISMA Encrypt", i+1, count); + } + gf_isom_set_cts_packing(mp4, track, 0); + gf_crypt_close(mc); + + + /*format as IPMP(X) - note that the ISMACryp spec is broken since it always uses IPMPPointers to a + single desc which would assume the same protection (eg key & salt) for all streams using it...*/ + if (!tci->ipmp_type) return GF_OK; + + ipmpdp = (GF_IPMPPtr*)gf_odf_desc_new(GF_ODF_IPMP_PTR_TAG); + if (!tci->ipmp_desc_id) tci->ipmp_desc_id = track; + if (tci->ipmp_type==2) { + ipmpdp->IPMP_DescriptorID = 0xFF; + ipmpdp->IPMP_DescriptorIDEx = tci->ipmp_desc_id; + } else { + ipmpdp->IPMP_DescriptorID = tci->ipmp_desc_id; + } + gf_isom_add_desc_to_description(mp4, track, 1, (GF_Descriptor *)ipmpdp); + gf_odf_desc_del((GF_Descriptor*)ipmpdp); + + ipmpdU = (GF_IPMPUpdate*)gf_odf_com_new(GF_ODF_IPMP_UPDATE_TAG); + /*format IPMPD*/ + ipmpd = (GF_IPMP_Descriptor*)gf_odf_desc_new(GF_ODF_IPMP_TAG); + if (tci->ipmp_type==2) { + ipmpd->IPMP_DescriptorID = 0xFF; + ipmpd->IPMP_DescriptorIDEx = tci->ipmp_desc_id; + ipmpd->IPMPS_Type = 0xFFFF; + ipmpd->IPMP_ToolID[14] = 0x49; ipmpd->IPMP_ToolID[15] = 0x53; + ipmpd->control_point = 1; + ipmpd->cp_sequence_code = 0x80; + /*format IPMPXData*/ + ismac = (GF_IPMPX_ISMACryp *) gf_ipmpx_data_new(GF_IPMPX_ISMACRYP_TAG); + ismac->cryptoSuite = 1; /*default ISMA AESCTR128*/ + ismac->IV_length = IV_size; + ismac->key_indicator_length = 0; + ismac->use_selective_encryption = (tci->sel_enc_type!=0)? 1 : 0; + gf_list_add(ipmpd->ipmpx_data, ismac); + } else { + ipmpd->IPMP_DescriptorID = tci->ipmp_desc_id; + } + gf_list_add(ipmpdU->IPMPDescList, ipmpd); + + for (i=0; idata, samp->dataLength); + gf_odf_codec_decode(cod); + gf_odf_codec_add_com(cod, (GF_ODCom *) ipmpdU); + free(samp->data); + samp->data = NULL; + samp->dataLength = 0; + gf_odf_codec_encode(cod, 1); + gf_odf_codec_get_au(cod, &samp->data, &samp->dataLength); + ipmpdU = NULL; + gf_odf_codec_del(cod); + gf_isom_update_sample(mp4, i+1, 1, samp, 1); + gf_isom_sample_del(&samp); + + if (tci->ipmp_type==2) { + GF_IPMP_ToolList*ipmptl = (GF_IPMP_ToolList*)gf_odf_desc_new(GF_ODF_IPMP_TL_TAG); + GF_IPMP_Tool *ipmpt = (GF_IPMP_Tool*)gf_odf_desc_new(GF_ODF_IPMP_TOOL_TAG); + gf_list_add(ipmptl->ipmp_tools, ipmpt); + ipmpt->IPMP_ToolID[14] = 0x49; ipmpt->IPMP_ToolID[15] = 0x53; + gf_isom_add_desc_to_root_od(mp4, (GF_Descriptor *)ipmptl); + gf_odf_desc_del((GF_Descriptor *)ipmptl); + } + break; + } + return e; +} + +GF_EXPORT +GF_Err gf_ismacryp_crypt_file(GF_ISOFile *mp4, const char *drm_file) +{ + GF_Err e; + u32 i, count, nb_tracks, common_idx, idx; + ISMACrypInfo *info; + Bool is_oma; + GF_TrackCryptInfo *tci; + + is_oma = 0; + + info = load_crypt_file(drm_file); + if (!info) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA E&A] Cannot open or validate xml file %s\n", drm_file)); + return GF_NOT_SUPPORTED; + } + e = GF_OK; + count = gf_list_count(info->tcis); + + common_idx=0; + if (info && info->has_common_key) { + for (common_idx=0; common_idxtcis, common_idx); + if (!tci->trackID) break; + } + } + nb_tracks = gf_isom_get_track_count(mp4); + for (i=0; itcis, idx); + if (tci->trackID==trackID) break; + } + if (idx==count) { + if (!info->has_common_key) continue; + idx = common_idx; + } + tci = (GF_TrackCryptInfo *)gf_list_get(info->tcis, idx); + + /*default to FILE uri*/ + if (!strlen(tci->KMS_URI)) strcpy(tci->KMS_URI, drm_file); + + e = gf_ismacryp_encrypt_track(mp4, tci, NULL, NULL); + if (e) break; + + if (tci->enc_type==1) is_oma = 1; + } + + if (is_oma) { +#if 0 + /*set as OMA V2*/ + e = gf_isom_set_brand_info(mp4, GF_4CC('o','d','c','f'), 0x00000002); + gf_isom_reset_alt_brands(mp4); +#else + e = gf_isom_modify_alternate_brand(mp4, GF_4CC('o','p','f','2'), 1); +#endif + } + + del_crypt_info(info); + return e; +} + +#endif + +GF_EXPORT +GF_Err gf_media_get_file_hash(const char *file, u8 hash[20]) +{ + u8 block[1024]; + u32 read; + u64 size, tot; + FILE *in; + GF_BitStream *bs = NULL; + GF_SHA1Context ctx; + Bool is_isom = gf_isom_probe_file(file); + + in = fopen(file, "rb"); + gf_f64_seek(in, 0, SEEK_END); + size = gf_f64_tell(in); + gf_f64_seek(in, 0, SEEK_SET); + + gf_sha1_starts(&ctx); + tot = 0; + if (is_isom) bs = gf_bs_from_file(in, GF_BITSTREAM_READ); + + while (tot +#include +#include +#include +#include +#include + +#ifndef GPAC_READ_ONLY + +/*RTP track hinter*/ +struct __tag_isom_hinter +{ + GF_ISOFile *file; + /*IDs are kept for mp4 hint sample building*/ + u32 TrackNum, TrackID, HintTrack, HintID; + /*current Hint sample and associated RTP time*/ + u32 HintSample, RTPTime; + + /*track has composition time offset*/ + Bool has_ctts; + /*remember if first SL packet in RTP packet is RAP*/ + u8 SampleIsRAP; + u32 base_offset_in_sample; + u32 OrigTimeScale; + /*rtp builder*/ + GP_RTPPacketizer *rtp_p; + + u32 bandwidth, nb_chan; + + /*NALU size for H264/AVC*/ + u32 avc_nalu_size; + + /*stats*/ + u32 TotalSample, CurrentSample; +}; + + +/* + offset for group ID for hint tracks in SimpleAV mode when all media data + is copied to the hint track (no use interleaving hint and original in this case) + this offset is applied internally by the track hinter. Thus you shouldn't + specify a GroupID >= OFFSET_HINT_GROUP_ID if you want the lib to perform efficient + interleaving in any cases (referenced or copied media) +*/ +#define OFFSET_HINT_GROUP_ID 0x8000 + +void MP4T_DumpSDP(GF_ISOFile *file, const char *name) +{ + const char *sdp; + u32 size, i; + FILE *f; + + f = fopen(name, "wt"); + //get the movie SDP + gf_isom_sdp_get(file, &sdp, &size); + fwrite(sdp, size, 1, f); + fprintf(f, "\r\n"); + + //then tracks + for (i=0; idataLength; + if (*MaxSize < samp->dataLength) *MaxSize = samp->dataLength; + ts_diff = (u32) (samp->DTS+samp->CTS_Offset - prevTS); + //get the time + tdelta += ts_diff; + + if (i==1) { + *const_duration = ts_diff; + } else if ( (iDTS+samp->CTS_Offset; + bw += 8*samp->dataLength; + + //get the CTS delta + if (samp->CTS_Offset > *maxCTSDelta) *maxCTSDelta = samp->CTS_Offset; + gf_isom_sample_del(&samp); + } + if (count>1) *TimeDelta = (u32) (tdelta/ (count-1) ); + else *TimeDelta = (u32) tdelta; + *avgSize /= count; + bw *= gf_isom_get_media_timescale(file, Track); + bw /= (s64) gf_isom_get_media_duration(file, Track); + bw /= 1000; + (*bandwidth) = (u32) (bw+0.5); + + //delta is NOT an average, we need to know exactly how many bits are + //needed to encode CTS-DTS for ANY samples +} + +void InitSL_RTP(GF_SLConfig *slc) +{ + memset(slc, 0, sizeof(GF_SLConfig)); + slc->tag = GF_ODF_SLC_TAG; + slc->useTimestampsFlag = 1; + slc->timestampLength = 32; +} + +void InitSL_NULL(GF_SLConfig *slc) +{ + memset(slc, 0, sizeof(GF_SLConfig)); + slc->tag = GF_ODF_SLC_TAG; + slc->predefined = 0x01; +} + + + +void MP4T_OnPacketDone(void *cbk, GF_RTPHeader *header) +{ + u8 disposable; + GF_RTPHinter *tkHint = (GF_RTPHinter *)cbk; + if (!tkHint || !tkHint->HintSample) return; + assert(header->TimeStamp == tkHint->RTPTime); + + disposable = 0; + if (tkHint->avc_nalu_size) { + disposable = tkHint->rtp_p->avc_non_idr ? 1 : 0; + } + /*for all other, assume that CTS=DTS means B-frame -> disposable*/ + else if (tkHint->has_ctts && (tkHint->rtp_p->sl_header.compositionTimeStamp==tkHint->rtp_p->sl_header.decodingTimeStamp)) { + disposable = 1; + } + + gf_isom_rtp_packet_set_flags(tkHint->file, tkHint->HintTrack, 0, 0, header->Marker, disposable, 0); +} + + +void MP4T_OnDataRef(void *cbk, u32 payload_size, u32 offset_from_orig) +{ + GF_RTPHinter *tkHint = (GF_RTPHinter *)cbk; + if (!tkHint || !payload_size) return; + + /*add reference*/ + gf_isom_hint_sample_data(tkHint->file, tkHint->HintTrack, tkHint->TrackID, + tkHint->CurrentSample, (u16) payload_size, offset_from_orig + tkHint->base_offset_in_sample, + NULL, 0); +} + +void MP4T_OnData(void *cbk, char *data, u32 data_size, Bool is_header) +{ + u8 at_begin; + GF_RTPHinter *tkHint = (GF_RTPHinter *)cbk; + if (!data_size) return; + + at_begin = is_header ? 1 : 0; + if (data_size <= 14) { + gf_isom_hint_direct_data(tkHint->file, tkHint->HintTrack, data, data_size, at_begin); + } else { + gf_isom_hint_sample_data(tkHint->file, tkHint->HintTrack, tkHint->HintID, 0, (u16) data_size, 0, data, at_begin); + } +} + + +void MP4T_OnNewPacket(void *cbk, GF_RTPHeader *header) +{ + s32 res; + GF_RTPHinter *tkHint = (GF_RTPHinter *)cbk; + if (!tkHint) return; + + res = (s32) (tkHint->rtp_p->sl_header.compositionTimeStamp - tkHint->rtp_p->sl_header.decodingTimeStamp); + assert( !res || tkHint->has_ctts); + /*do we need a new sample*/ + if (!tkHint->HintSample || (tkHint->RTPTime != header->TimeStamp)) { + /*close current sample*/ + if (tkHint->HintSample) gf_isom_end_hint_sample(tkHint->file, tkHint->HintTrack, tkHint->SampleIsRAP); + + /*start new sample: We use DTS as the sampling instant (RTP TS) to make sure + all packets are sent in order*/ + gf_isom_begin_hint_sample(tkHint->file, tkHint->HintTrack, 1, header->TimeStamp-res); + tkHint->HintSample ++; + tkHint->RTPTime = header->TimeStamp; + tkHint->SampleIsRAP = tkHint->rtp_p->sl_config.hasRandomAccessUnitsOnlyFlag ? 1 : tkHint->rtp_p->sl_header.randomAccessPointFlag; + } + /*create an RTP Packet with the appropriated marker flag - note: the flags are temp ones, + they are set when the full packet is signaled (to handle multi AUs per RTP)*/ + gf_isom_rtp_packet_begin(tkHint->file, tkHint->HintTrack, 0, 0, 0, header->Marker, header->PayloadType, 0, 0, header->SequenceNumber); + /*Add the delta TS to make sure RTP TS is indeed the CTS (sampling time)*/ + if (res) gf_isom_rtp_packet_set_offset(tkHint->file, tkHint->HintTrack, res); +} + +GP_RTPPacketizer *gf_rtp_packetizer_create_and_init_from_file(GF_ISOFile *file, + u32 TrackNum, + void *cbk_obj, + void (*OnNewPacket)(void *cbk, GF_RTPHeader *header), + void (*OnPacketDone)(void *cbk, GF_RTPHeader *header), + void (*OnDataReference)(void *cbk, u32 payload_size, u32 offset_from_orig), + void (*OnData)(void *cbk, char *data, u32 data_size, Bool is_head), + u32 Path_MTU, + u32 max_ptime, + u32 default_rtp_rate, + u32 flags, + u8 PayloadID, + Bool copy_media, + u32 InterleaveGroupID, + u8 InterleaveGroupPriority) +{ + GF_SLConfig my_sl; + u32 MinSize, MaxSize, avgTS, streamType, oti, const_dur, nb_ch, maxDTSDelta; + u8 OfficialPayloadID; + u32 TrackMediaSubType, TrackMediaType, hintType, required_rate, force_dts_delta, avc_nalu_size, PL_ID, bandwidth, IV_length, KI_length; + const char *url, *urn; + char *mpeg4mode; + Bool is_crypted, has_mpeg4_mapping; + GF_ESD *esd; + GP_RTPPacketizer *rtp_packetizer = NULL; + + /*by default NO PL signaled*/ + PL_ID = 0; + OfficialPayloadID = 0; + force_dts_delta = 0; + streamType = oti = 0; + mpeg4mode = NULL; + required_rate = 0; + is_crypted = 0; + IV_length = KI_length = 0; + oti = 0; + nb_ch = 0; + avc_nalu_size = 0; + has_mpeg4_mapping = 1; + TrackMediaType = gf_isom_get_media_type(file, TrackNum); + TrackMediaSubType = gf_isom_get_media_subtype(file, TrackNum, 1); + + /*for max compatibility with QT*/ + if (!default_rtp_rate) default_rtp_rate = 90000; + + /*timed-text is a bit special, we support multiple stream descriptions & co*/ + if (TrackMediaType==GF_ISOM_MEDIA_TEXT) { + hintType = GF_RTP_PAYT_3GPP_TEXT; + oti = 0x08; + streamType = GF_STREAM_TEXT; + /*fixme - this works cos there's only one PL for text in mpeg4 at the current time*/ + PL_ID = 0x10; + } else { + if (gf_isom_get_sample_description_count(file, TrackNum) > 1) return NULL; + + TrackMediaSubType = gf_isom_get_media_subtype(file, TrackNum, 1); + switch (TrackMediaSubType) { + case GF_ISOM_SUBTYPE_MPEG4_CRYP: + is_crypted = 1; + case GF_ISOM_SUBTYPE_MPEG4: + esd = gf_isom_get_esd(file, TrackNum, 1); + hintType = GF_RTP_PAYT_MPEG4; + if (esd) { + streamType = esd->decoderConfig->streamType; + oti = esd->decoderConfig->objectTypeIndication; + if (esd->URLString) hintType = 0; + /*AAC*/ + if ((streamType==GF_STREAM_AUDIO) && esd->decoderConfig->decoderSpecificInfo + /*(nb: we use mpeg4 for MPEG-2 AAC)*/ + && ((oti==0x40) || (oti==0x40) || (oti==0x66) || (oti==0x67) || (oti==0x68)) ) { + + u32 sample_rate; + GF_M4ADecSpecInfo a_cfg; + gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg); + nb_ch = a_cfg.nb_chan; + sample_rate = a_cfg.base_sr; + PL_ID = a_cfg.audioPL; + switch (a_cfg.base_object_type) { + case GF_M4A_AAC_MAIN: + case GF_M4A_AAC_LC: + if (flags & GP_RTP_PCK_USE_LATM_AAC) { + hintType = GF_RTP_PAYT_LATM; + break; + } + case GF_M4A_AAC_SBR: + case GF_M4A_AAC_LTP: + case GF_M4A_AAC_SCALABLE: + case GF_M4A_ER_AAC_LC: + case GF_M4A_ER_AAC_LTP: + case GF_M4A_ER_AAC_SCALABLE: + mpeg4mode = "AAC"; + break; + case GF_M4A_CELP: + case GF_M4A_ER_CELP: + mpeg4mode = "CELP"; + break; + } + required_rate = sample_rate; + } + /*MPEG1/2 audio*/ + else if ((streamType==GF_STREAM_AUDIO) && ((oti==0x69) || (oti==0x6B))) { + u32 sample_rate; + if (!is_crypted) { + GF_ISOSample *samp = gf_isom_get_sample(file, TrackNum, 1, NULL); + u32 hdr = GF_4CC((u8)samp->data[0], (u8)samp->data[1], (u8)samp->data[2], (u8)samp->data[3]); + nb_ch = gf_mp3_num_channels(hdr); + sample_rate = gf_mp3_sampling_rate(hdr); + gf_isom_sample_del(&samp); + hintType = GF_RTP_PAYT_MPEG12_AUDIO; + /*use official RTP/AVP payload type*/ + OfficialPayloadID = 14; + required_rate = 90000; + } + /*encrypted MP3 must be sent through MPEG-4 generic to signal all ISMACryp stuff*/ + else { + u8 bps; + gf_isom_get_audio_info(file, TrackNum, 1, &sample_rate, &nb_ch, &bps); + required_rate = sample_rate; + } + } + /*QCELP audio*/ + else if ((streamType==GF_STREAM_AUDIO) && (oti==0xE1)) { + hintType = GF_RTP_PAYT_QCELP; + OfficialPayloadID = 12; + required_rate = 8000; + streamType = GF_STREAM_AUDIO; + nb_ch = 1; + } + /*EVRC/SVM audio*/ + else if ((streamType==GF_STREAM_AUDIO) && ((oti==0xA0) || (oti==0xA1)) ) { + hintType = GF_RTP_PAYT_EVRC_SMV; + required_rate = 8000; + streamType = GF_STREAM_AUDIO; + nb_ch = 1; + } + /*visual streams*/ + else if (streamType==GF_STREAM_VISUAL) { + if (oti==0x20) { + GF_M4VDecSpecInfo dsi; + gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + PL_ID = dsi.VideoPL; + } + /*MPEG1/2 video*/ + if ( ((oti>=0x60) && (oti<=0x65)) || (oti==0x6A)) { + if (!is_crypted) { + hintType = GF_RTP_PAYT_MPEG12_VIDEO; + OfficialPayloadID = 32; + } + } + /*for ISMA*/ + if (is_crypted) { + /*that's another pain with ISMACryp, even if no B-frames the DTS is signaled...*/ + if (oti==0x20) force_dts_delta = 22; + flags |= GP_RTP_PCK_SIGNAL_RAP | GP_RTP_PCK_SIGNAL_TS; + } + + required_rate = default_rtp_rate; + } + /*systems streams*/ + else if (gf_isom_has_sync_shadows(file, TrackNum) || gf_isom_has_sample_dependency(file, TrackNum)) { + flags |= GP_RTP_PCK_AUTO_CAROUSEL; + } + gf_odf_desc_del((GF_Descriptor*)esd); + } + break; + case GF_ISOM_SUBTYPE_3GP_H263: + hintType = GF_RTP_PAYT_H263; + required_rate = 90000; + streamType = GF_STREAM_VISUAL; + OfficialPayloadID = 34; + /*not 100% compliant (short header is missing) but should still work*/ + oti = 0x20; + PL_ID = 0x01; + break; + case GF_ISOM_SUBTYPE_3GP_AMR: + required_rate = 8000; + hintType = GF_RTP_PAYT_AMR; + streamType = GF_STREAM_AUDIO; + has_mpeg4_mapping = 0; + nb_ch = 1; + break; + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + required_rate = 16000; + hintType = GF_RTP_PAYT_AMR_WB; + streamType = GF_STREAM_AUDIO; + has_mpeg4_mapping = 0; + nb_ch = 1; + break; + case GF_ISOM_SUBTYPE_AC3: + hintType = GF_RTP_PAYT_AC3; + streamType = GF_STREAM_AUDIO; + has_mpeg4_mapping = 1; + nb_ch = 1; + break; + case GF_ISOM_SUBTYPE_AVC_H264: + { + GF_AVCConfig *avcc = gf_isom_avc_config_get(file, TrackNum, 1); + required_rate = 90000; /* "90 kHz clock rate MUST be used"*/ + hintType = GF_RTP_PAYT_H264_AVC; + streamType = GF_STREAM_VISUAL; + avc_nalu_size = avcc->nal_unit_size; + oti = 0x21; + PL_ID = 0x0F; + gf_odf_avc_cfg_del(avcc); + } + break; + case GF_ISOM_SUBTYPE_3GP_QCELP: + required_rate = 8000; + hintType = GF_RTP_PAYT_QCELP; + streamType = GF_STREAM_AUDIO; + oti = 0xE1; + OfficialPayloadID = 12; + nb_ch = 1; + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_SMV: + required_rate = 8000; + hintType = GF_RTP_PAYT_EVRC_SMV; + streamType = GF_STREAM_AUDIO; + oti = (TrackMediaSubType==GF_ISOM_SUBTYPE_3GP_EVRC) ? 0xA0 : 0xA1; + nb_ch = 1; + break; + default: + /*ERROR*/ + hintType = 0; + break; + } + } + + /*not hintable*/ + if (!hintType) return NULL; + /*we only support self-contained files for hinting*/ + gf_isom_get_data_reference(file, TrackNum, 1, &url, &urn); + if (url || urn) return NULL; + + /*override hinter type if requested and possible*/ + if (has_mpeg4_mapping && (flags & GP_RTP_PCK_FORCE_MPEG4)) { + hintType = GF_RTP_PAYT_MPEG4; + avc_nalu_size = 0; + } + /*use static payload ID if enabled*/ + else if (OfficialPayloadID && (flags & GP_RTP_PCK_USE_STATIC_ID) ) { + PayloadID = OfficialPayloadID; + } + +#if 0 + tmp->file = file; + tmp->TrackNum = TrackNum; + tmp->avc_nalu_size = avc_nalu_size; + tmp->nb_chan = nb_ch; + + /*spatial scalability check*/ + tmp->has_ctts = gf_isom_has_time_offset(file, TrackNum); +#endif + + /*get sample info*/ + GetAvgSampleInfos(file, TrackNum, &MinSize, &MaxSize, &avgTS, &maxDTSDelta, &const_dur, &bandwidth); + + /*systems carousel: we need at least IDX and RAP signaling*/ + if (flags & GP_RTP_PCK_AUTO_CAROUSEL) { + flags |= GP_RTP_PCK_SIGNAL_RAP | GP_RTP_PCK_SIGNAL_AU_IDX; + } + + /*update flags in MultiSL*/ + if (flags & GP_RTP_PCK_USE_MULTI) { + if (MinSize != MaxSize) flags |= GP_RTP_PCK_SIGNAL_SIZE; + if (!const_dur) flags |= GP_RTP_PCK_SIGNAL_TS; + } +#if 0 + if (tmp->has_ctts) flags |= GP_RTP_PCK_SIGNAL_TS; +#endif + + /*default SL for RTP */ + InitSL_RTP(&my_sl); + + my_sl.timestampResolution = gf_isom_get_media_timescale(file, TrackNum); + /*override clockrate if set*/ + if (required_rate) { + Double sc = required_rate; + sc /= my_sl.timestampResolution; + maxDTSDelta = (u32) (maxDTSDelta*sc); + my_sl.timestampResolution = required_rate; + } + /*switch to RTP TS*/ + max_ptime = (u32) (max_ptime * my_sl.timestampResolution / 1000); + + my_sl.AUSeqNumLength = gf_get_bit_size(gf_isom_get_sample_count(file, TrackNum)); + my_sl.CUDuration = const_dur; + + if (gf_isom_has_sync_points(file, TrackNum)) { + my_sl.useRandomAccessPointFlag = 1; + } else { + my_sl.useRandomAccessPointFlag = 0; + my_sl.hasRandomAccessUnitsOnlyFlag = 1; + } + + if (is_crypted) { + Bool use_sel_enc; + gf_isom_get_ismacryp_info(file, TrackNum, 1, NULL, NULL, NULL, NULL, NULL, &use_sel_enc, &IV_length, &KI_length); + if (use_sel_enc) flags |= GP_RTP_PCK_SELECTIVE_ENCRYPTION; + } + + rtp_packetizer = gf_rtp_builder_new(hintType, &my_sl, flags, cbk_obj, + OnNewPacket, OnPacketDone, + /*if copy, no data ref*/ + copy_media ? NULL : OnDataReference, + OnData); + + gf_rtp_builder_init(rtp_packetizer, PayloadID, Path_MTU, max_ptime, + streamType, oti, PL_ID, MinSize, MaxSize, avgTS, maxDTSDelta, IV_length, KI_length, mpeg4mode); + + return rtp_packetizer; +} + +GF_EXPORT +GF_RTPHinter *gf_hinter_track_new(GF_ISOFile *file, u32 TrackNum, + u32 Path_MTU, u32 max_ptime, u32 default_rtp_rate, u32 flags, u8 PayloadID, + Bool copy_media, u32 InterleaveGroupID, u8 InterleaveGroupPriority, GF_Err *e) +{ + + GF_SLConfig my_sl; + u32 descIndex, MinSize, MaxSize, avgTS, streamType, oti, const_dur, nb_ch, maxDTSDelta; + u8 OfficialPayloadID; + u32 TrackMediaSubType, TrackMediaType, hintType, nbEdts, required_rate, force_dts_delta, avc_nalu_size, PL_ID, bandwidth, IV_length, KI_length; + const char *url, *urn; + char *mpeg4mode; + Bool is_crypted, has_mpeg4_mapping; + GF_RTPHinter *tmp; + GF_ESD *esd; + + *e = GF_BAD_PARAM; + if (!file || !TrackNum || !gf_isom_get_track_id(file, TrackNum)) return NULL; + + if (!gf_isom_get_sample_count(file, TrackNum)) { + *e = GF_OK; + return NULL; + } + *e = GF_NOT_SUPPORTED; + nbEdts = gf_isom_get_edit_segment_count(file, TrackNum); + if (nbEdts>1) { + u64 et, sd, mt; + u8 em; + gf_isom_get_edit_segment(file, TrackNum, 1, &et, &sd, &mt, &em); + if ((nbEdts>2) || (em!=GF_ISOM_EDIT_EMPTY)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[rtp hinter] Cannot hint track whith EditList\n")); + return NULL; + } + } + if (nbEdts) gf_isom_remove_edit_segments(file, TrackNum); + + if (!gf_isom_is_track_enabled(file, TrackNum)) return NULL; + + /*by default NO PL signaled*/ + PL_ID = 0; + OfficialPayloadID = 0; + force_dts_delta = 0; + streamType = oti = 0; + mpeg4mode = NULL; + required_rate = 0; + is_crypted = 0; + IV_length = KI_length = 0; + oti = 0; + nb_ch = 0; + avc_nalu_size = 0; + has_mpeg4_mapping = 1; + TrackMediaType = gf_isom_get_media_type(file, TrackNum); + TrackMediaSubType = gf_isom_get_media_subtype(file, TrackNum, 1); + + /*for max compatibility with QT*/ + if (!default_rtp_rate) default_rtp_rate = 90000; + + /*timed-text is a bit special, we support multiple stream descriptions & co*/ + if (TrackMediaType==GF_ISOM_MEDIA_TEXT) { + hintType = GF_RTP_PAYT_3GPP_TEXT; + oti = 0x08; + streamType = GF_STREAM_TEXT; + /*fixme - this works cos there's only one PL for text in mpeg4 at the current time*/ + PL_ID = 0x10; + } else { + if (gf_isom_get_sample_description_count(file, TrackNum) > 1) return NULL; + + TrackMediaSubType = gf_isom_get_media_subtype(file, TrackNum, 1); + switch (TrackMediaSubType) { + case GF_ISOM_SUBTYPE_MPEG4_CRYP: + is_crypted = 1; + case GF_ISOM_SUBTYPE_MPEG4: + esd = gf_isom_get_esd(file, TrackNum, 1); + hintType = GF_RTP_PAYT_MPEG4; + if (esd) { + streamType = esd->decoderConfig->streamType; + oti = esd->decoderConfig->objectTypeIndication; + if (esd->URLString) hintType = 0; + /*AAC*/ + if ((streamType==GF_STREAM_AUDIO) && esd->decoderConfig->decoderSpecificInfo + /*(nb: we use mpeg4 for MPEG-2 AAC)*/ + && ((oti==0x40) || (oti==0x40) || (oti==0x66) || (oti==0x67) || (oti==0x68)) ) { + + u32 sample_rate; + GF_M4ADecSpecInfo a_cfg; + gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg); + nb_ch = a_cfg.nb_chan; + sample_rate = a_cfg.base_sr; + PL_ID = a_cfg.audioPL; + switch (a_cfg.base_object_type) { + case GF_M4A_AAC_MAIN: + case GF_M4A_AAC_LC: + if (flags & GP_RTP_PCK_USE_LATM_AAC) { + hintType = GF_RTP_PAYT_LATM; + break; + } + case GF_M4A_AAC_SBR: + case GF_M4A_AAC_LTP: + case GF_M4A_AAC_SCALABLE: + case GF_M4A_ER_AAC_LC: + case GF_M4A_ER_AAC_LTP: + case GF_M4A_ER_AAC_SCALABLE: + mpeg4mode = "AAC"; + break; + case GF_M4A_CELP: + case GF_M4A_ER_CELP: + mpeg4mode = "CELP"; + break; + } + required_rate = sample_rate; + } + /*MPEG1/2 audio*/ + else if ((streamType==GF_STREAM_AUDIO) && ((oti==0x69) || (oti==0x6B))) { + u32 sample_rate; + if (!is_crypted) { + GF_ISOSample *samp = gf_isom_get_sample(file, TrackNum, 1, NULL); + u32 hdr = GF_4CC((u8)samp->data[0], (u8)samp->data[1], (u8)samp->data[2], (u8)samp->data[3]); + nb_ch = gf_mp3_num_channels(hdr); + sample_rate = gf_mp3_sampling_rate(hdr); + gf_isom_sample_del(&samp); + hintType = GF_RTP_PAYT_MPEG12_AUDIO; + /*use official RTP/AVP payload type*/ + OfficialPayloadID = 14; + required_rate = 90000; + } + /*encrypted MP3 must be sent through MPEG-4 generic to signal all ISMACryp stuff*/ + else { + u8 bps; + gf_isom_get_audio_info(file, TrackNum, 1, &sample_rate, &nb_ch, &bps); + required_rate = sample_rate; + } + } + /*QCELP audio*/ + else if ((streamType==GF_STREAM_AUDIO) && (oti==0xE1)) { + hintType = GF_RTP_PAYT_QCELP; + OfficialPayloadID = 12; + required_rate = 8000; + streamType = GF_STREAM_AUDIO; + nb_ch = 1; + } + /*EVRC/SVM audio*/ + else if ((streamType==GF_STREAM_AUDIO) && ((oti==0xA0) || (oti==0xA1)) ) { + hintType = GF_RTP_PAYT_EVRC_SMV; + required_rate = 8000; + streamType = GF_STREAM_AUDIO; + nb_ch = 1; + } + /*visual streams*/ + else if (streamType==GF_STREAM_VISUAL) { + if (oti==0x20) { + GF_M4VDecSpecInfo dsi; + gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + PL_ID = dsi.VideoPL; + } + /*MPEG1/2 video*/ + if ( ((oti>=0x60) && (oti<=0x65)) || (oti==0x6A)) { + if (!is_crypted) { + hintType = GF_RTP_PAYT_MPEG12_VIDEO; + OfficialPayloadID = 32; + } + } + /*for ISMA*/ + if (is_crypted) { + /*that's another pain with ISMACryp, even if no B-frames the DTS is signaled...*/ + if (oti==0x20) force_dts_delta = 22; + else if (oti==0x21) { + flags &= ~GP_RTP_PCK_USE_MULTI; + force_dts_delta = 22; + } + flags |= GP_RTP_PCK_SIGNAL_RAP | GP_RTP_PCK_SIGNAL_TS; + } + + required_rate = default_rtp_rate; + } + /*systems streams*/ + else if (gf_isom_has_sync_shadows(file, TrackNum) || gf_isom_has_sample_dependency(file, TrackNum)) { + flags |= GP_RTP_PCK_AUTO_CAROUSEL; + } + gf_odf_desc_del((GF_Descriptor*)esd); + } + break; + case GF_ISOM_SUBTYPE_3GP_H263: + hintType = GF_RTP_PAYT_H263; + required_rate = 90000; + streamType = GF_STREAM_VISUAL; + OfficialPayloadID = 34; + /*not 100% compliant (short header is missing) but should still work*/ + oti = 0x20; + PL_ID = 0x01; + break; + case GF_ISOM_SUBTYPE_3GP_AMR: + required_rate = 8000; + hintType = GF_RTP_PAYT_AMR; + streamType = GF_STREAM_AUDIO; + has_mpeg4_mapping = 0; + nb_ch = 1; + break; + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + required_rate = 16000; + hintType = GF_RTP_PAYT_AMR_WB; + streamType = GF_STREAM_AUDIO; + has_mpeg4_mapping = 0; + nb_ch = 1; + break; + case GF_ISOM_SUBTYPE_AVC_H264: + { + GF_AVCConfig *avcc = gf_isom_avc_config_get(file, TrackNum, 1); + required_rate = 90000; /* "90 kHz clock rate MUST be used"*/ + hintType = GF_RTP_PAYT_H264_AVC; + streamType = GF_STREAM_VISUAL; + avc_nalu_size = avcc->nal_unit_size; + oti = 0x21; + PL_ID = 0x0F; + gf_odf_avc_cfg_del(avcc); + } + break; + case GF_ISOM_SUBTYPE_3GP_QCELP: + required_rate = 8000; + hintType = GF_RTP_PAYT_QCELP; + streamType = GF_STREAM_AUDIO; + oti = 0xE1; + OfficialPayloadID = 12; + nb_ch = 1; + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_SMV: + required_rate = 8000; + hintType = GF_RTP_PAYT_EVRC_SMV; + streamType = GF_STREAM_AUDIO; + oti = (TrackMediaSubType==GF_ISOM_SUBTYPE_3GP_EVRC) ? 0xA0 : 0xA1; + nb_ch = 1; + break; + case GF_ISOM_SUBTYPE_3GP_DIMS: + hintType = GF_RTP_PAYT_3GPP_DIMS; + streamType = GF_STREAM_SCENE; + break; + case GF_ISOM_SUBTYPE_AC3: + hintType = GF_RTP_PAYT_AC3; + streamType = GF_STREAM_AUDIO; + gf_isom_get_audio_info(file, TrackNum, 1, NULL, &nb_ch, NULL); + break; + default: + /*ERROR*/ + hintType = 0; + break; + } + } + + /*not hintable*/ + if (!hintType) return NULL; + /*we only support self-contained files for hinting*/ + gf_isom_get_data_reference(file, TrackNum, 1, &url, &urn); + if (url || urn) return NULL; + + *e = GF_OUT_OF_MEM; + GF_SAFEALLOC(tmp, GF_RTPHinter); + if (!tmp) return NULL; + + /*override hinter type if requested and possible*/ + if (has_mpeg4_mapping && (flags & GP_RTP_PCK_FORCE_MPEG4)) { + hintType = GF_RTP_PAYT_MPEG4; + avc_nalu_size = 0; + } + /*use static payload ID if enabled*/ + else if (OfficialPayloadID && (flags & GP_RTP_PCK_USE_STATIC_ID) ) { + PayloadID = OfficialPayloadID; + } + + tmp->file = file; + tmp->TrackNum = TrackNum; + tmp->avc_nalu_size = avc_nalu_size; + tmp->nb_chan = nb_ch; + + /*spatial scalability check*/ + tmp->has_ctts = gf_isom_has_time_offset(file, TrackNum); + + /*get sample info*/ + GetAvgSampleInfos(file, TrackNum, &MinSize, &MaxSize, &avgTS, &maxDTSDelta, &const_dur, &bandwidth); + + /*systems carousel: we need at least IDX and RAP signaling*/ + if (flags & GP_RTP_PCK_AUTO_CAROUSEL) { + flags |= GP_RTP_PCK_SIGNAL_RAP | GP_RTP_PCK_SIGNAL_AU_IDX; + } + + /*update flags in MultiSL*/ + if (flags & GP_RTP_PCK_USE_MULTI) { + if (MinSize != MaxSize) flags |= GP_RTP_PCK_SIGNAL_SIZE; + if (!const_dur) flags |= GP_RTP_PCK_SIGNAL_TS; + } + if (tmp->has_ctts) flags |= GP_RTP_PCK_SIGNAL_TS; + + /*default SL for RTP */ + InitSL_RTP(&my_sl); + + my_sl.timestampResolution = gf_isom_get_media_timescale(file, TrackNum); + /*override clockrate if set*/ + if (required_rate) { + Double sc = required_rate; + sc /= my_sl.timestampResolution; + maxDTSDelta = (u32) (maxDTSDelta*sc); + my_sl.timestampResolution = required_rate; + } + /*switch to RTP TS*/ + max_ptime = (u32) (max_ptime * my_sl.timestampResolution / 1000); + + my_sl.AUSeqNumLength = gf_get_bit_size(gf_isom_get_sample_count(file, TrackNum)); + my_sl.CUDuration = const_dur; + + if (gf_isom_has_sync_points(file, TrackNum)) { + my_sl.useRandomAccessPointFlag = 1; + } else { + my_sl.useRandomAccessPointFlag = 0; + my_sl.hasRandomAccessUnitsOnlyFlag = 1; + } + + if (is_crypted) { + Bool use_sel_enc; + gf_isom_get_ismacryp_info(file, TrackNum, 1, NULL, NULL, NULL, NULL, NULL, &use_sel_enc, &IV_length, &KI_length); + if (use_sel_enc) flags |= GP_RTP_PCK_SELECTIVE_ENCRYPTION; + } + + // in case a different timescale was provided + tmp->OrigTimeScale = gf_isom_get_media_timescale(file, TrackNum); + tmp->rtp_p = gf_rtp_builder_new(hintType, &my_sl, flags, tmp, + MP4T_OnNewPacket, MP4T_OnPacketDone, + /*if copy, no data ref*/ + copy_media ? NULL : MP4T_OnDataRef, + MP4T_OnData); + + //init the builder + gf_rtp_builder_init(tmp->rtp_p, PayloadID, Path_MTU, max_ptime, + streamType, oti, PL_ID, MinSize, MaxSize, avgTS, maxDTSDelta, IV_length, KI_length, mpeg4mode); + + /*ISMA compliance is a pain...*/ + if (force_dts_delta) tmp->rtp_p->slMap.DTSDeltaLength = force_dts_delta; + + + /* Hint Track Setup */ + tmp->TrackID = gf_isom_get_track_id(file, TrackNum); + tmp->HintID = tmp->TrackID + 65535; + while (gf_isom_get_track_by_id(file, tmp->HintID)) tmp->HintID++; + + tmp->HintTrack = gf_isom_new_track(file, tmp->HintID, GF_ISOM_MEDIA_HINT, my_sl.timestampResolution); + gf_isom_setup_hint_track(file, tmp->HintTrack, GF_ISOM_HINT_RTP); + /*create a hint description*/ + gf_isom_new_hint_description(file, tmp->HintTrack, -1, -1, 0, &descIndex); + gf_isom_rtp_set_timescale(file, tmp->HintTrack, descIndex, my_sl.timestampResolution); + + if (hintType==GF_RTP_PAYT_MPEG4) { + tmp->rtp_p->slMap.ObjectTypeIndication = oti; + /*set this SL for extraction.*/ + gf_isom_set_extraction_slc(file, TrackNum, 1, &my_sl); + } + tmp->bandwidth = bandwidth; + + /*set interleaving*/ + gf_isom_set_track_group(file, TrackNum, InterleaveGroupID); + if (!copy_media) { + /*if we don't copy data set hint track and media track in the same group*/ + gf_isom_set_track_group(file, tmp->HintTrack, InterleaveGroupID); + } else { + gf_isom_set_track_group(file, tmp->HintTrack, InterleaveGroupID + OFFSET_HINT_GROUP_ID); + } + /*use user-secified priority*/ + InterleaveGroupPriority*=2; + gf_isom_set_track_priority_in_group(file, TrackNum, InterleaveGroupPriority+1); + gf_isom_set_track_priority_in_group(file, tmp->HintTrack, InterleaveGroupPriority); + +#if 0 + /*QT FF: not setting these flags = server uses a random offset*/ + gf_isom_rtp_set_time_offset(file, tmp->HintTrack, 1, 0); + /*we don't use seq offset for maintainance pruposes*/ + gf_isom_rtp_set_time_sequence_offset(file, tmp->HintTrack, 1, 0); +#endif + *e = GF_OK; + return tmp; +} + +GF_EXPORT +u32 gf_hinter_track_get_bandwidth(GF_RTPHinter *tkHinter) +{ + return tkHinter->bandwidth; +} + +GF_EXPORT +u32 gf_hinter_track_get_flags(GF_RTPHinter *tkHinter) +{ + return tkHinter->rtp_p->flags; +} +GF_EXPORT +void gf_hinter_track_get_payload_name(GF_RTPHinter *tkHinter, char *payloadName) +{ + char mediaName[30]; + gf_rtp_builder_get_payload_name(tkHinter->rtp_p, payloadName, mediaName); +} + +GF_EXPORT +void gf_hinter_track_del(GF_RTPHinter *tkHinter) +{ + if (!tkHinter) return; + + if (tkHinter->rtp_p) gf_rtp_builder_del(tkHinter->rtp_p); + free(tkHinter); +} + +GF_EXPORT +GF_Err gf_hinter_track_process(GF_RTPHinter *tkHint) +{ + GF_Err e; + u32 i, descIndex, duration; + u64 ts; + u8 PadBits; + Double ft; + GF_ISOSample *samp; + + tkHint->HintSample = tkHint->RTPTime = 0; + + tkHint->TotalSample = gf_isom_get_sample_count(tkHint->file, tkHint->TrackNum); + ft = tkHint->rtp_p->sl_config.timestampResolution; + ft /= tkHint->OrigTimeScale; + + e = GF_OK; + for (i=0; iTotalSample; i++) { + samp = gf_isom_get_sample(tkHint->file, tkHint->TrackNum, i+1, &descIndex); + if (!samp) return GF_IO_ERR; + + //setup SL + tkHint->CurrentSample = i + 1; + + /*keep same AU indicator if sync shadow - TODO FIXME: this assumes shadows are placed interleaved with + the track content which is the case for GPAC scene carousel generation, but may not always be true*/ + if (samp->IsRAP==2) { + tkHint->rtp_p->sl_header.AU_sequenceNumber -= 1; + samp->IsRAP = 1; + } + + ts = (u64) (ft * (s64) (samp->DTS+samp->CTS_Offset)); + tkHint->rtp_p->sl_header.compositionTimeStamp = ts; + + ts = (u64) (ft * (s64)(samp->DTS)); + tkHint->rtp_p->sl_header.decodingTimeStamp = ts; + tkHint->rtp_p->sl_header.randomAccessPointFlag = samp->IsRAP; + + tkHint->base_offset_in_sample = 0; + /*crypted*/ + if (tkHint->rtp_p->slMap.IV_length) { + GF_ISMASample *s = gf_isom_get_ismacryp_sample(tkHint->file, tkHint->TrackNum, samp, descIndex); + /*one byte take for selective_enc flag*/ + if (s->flags & GF_ISOM_ISMA_USE_SEL_ENC) tkHint->base_offset_in_sample += 1; + if (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) tkHint->base_offset_in_sample += s->IV_length + s->KI_length; + free(samp->data); + samp->data = s->data; + samp->dataLength = s->dataLength; + gp_rtp_builder_set_cryp_info(tkHint->rtp_p, s->IV, (char*)s->key_indicator, (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) ? 1 : 0); + s->data = NULL; + s->dataLength = 0; + gf_isom_ismacryp_delete_sample(s); + } + + if (tkHint->rtp_p->sl_config.usePaddingFlag) { + gf_isom_get_sample_padding_bits(tkHint->file, tkHint->TrackNum, i+1, &PadBits); + tkHint->rtp_p->sl_header.paddingBits = PadBits; + } else { + tkHint->rtp_p->sl_header.paddingBits = 0; + } + + duration = gf_isom_get_sample_duration(tkHint->file, tkHint->TrackNum, i+1); + ts = (u32) (ft * (s64) (duration)); + + /*unpack nal units*/ + if (tkHint->avc_nalu_size) { + u32 v, size; + u32 remain = samp->dataLength; + char *ptr = samp->data; + + tkHint->rtp_p->sl_header.accessUnitStartFlag = 1; + tkHint->rtp_p->sl_header.accessUnitEndFlag = 0; + while (remain) { + size = 0; + v = tkHint->avc_nalu_size; + while (v) { + size |= (u8) *ptr; + ptr++; + remain--; + v-=1; + if (v) size<<=8; + } + tkHint->base_offset_in_sample = samp->dataLength-remain; + remain -= size; + tkHint->rtp_p->sl_header.accessUnitEndFlag = remain ? 0 : 1; + e = gf_rtp_builder_process(tkHint->rtp_p, ptr, size, (u8) !remain, samp->dataLength, duration, (u8) (descIndex + SIDX_OFFSET_3GPP) ); + ptr += size; + tkHint->rtp_p->sl_header.accessUnitStartFlag = 0; + } + } else { + e = gf_rtp_builder_process(tkHint->rtp_p, samp->data, samp->dataLength, 1, samp->dataLength, duration, (u8) (descIndex + SIDX_OFFSET_3GPP) ); + } + tkHint->rtp_p->sl_header.packetSequenceNumber += 1; + + //signal some progress + gf_set_progress("Hinting", tkHint->CurrentSample, tkHint->TotalSample); + + tkHint->rtp_p->sl_header.AU_sequenceNumber += 1; + gf_isom_sample_del(&samp); + + if (e) return e; + } + + //flush + gf_rtp_builder_process(tkHint->rtp_p, NULL, 0, 1, 0, 0, 0); + + gf_isom_end_hint_sample(tkHint->file, tkHint->HintTrack, (u8) tkHint->SampleIsRAP); + return GF_OK; +} + +void gf_hinter_format_ttxt_sdp(GP_RTPPacketizer *builder, char *payload_name, char *sdpLine, GF_ISOFile *file, u32 track) +{ + char buffer[2000]; + u32 w, h, i, m_w, m_h; + s32 tx, ty; + s16 l; + sprintf(sdpLine, "a=fmtp:%d sver=60; ", builder->PayloadType); + gf_isom_get_track_layout_info(file, track, &w, &h, &tx, &ty, &l); + sprintf(buffer, "width=%d; height=%d; tx=%d; ty=%d; layer=%d; ", w, h, tx, ty, l); + strcat(sdpLine, buffer); + m_w = w; + m_h = h; + for (i=0; im_w) m_w = w; + if (h>m_h) m_h = h; + break; + default: + break; + } + } + sprintf(buffer, "max-w=%d; max-h=%d", m_w, m_h); + strcat(sdpLine, buffer); + + strcat(sdpLine, "; tx3g="); + for (i=0; ifile, tkHint->TrackNum); + if (gf_isom_get_media_type(tkHint->file, tkHint->TrackNum) == GF_ISOM_MEDIA_VISUAL) + gf_isom_get_visual_info(tkHint->file, tkHint->TrackNum, 1, &Width, &Height); + + gf_rtp_builder_get_payload_name(tkHint->rtp_p, payloadName, mediaName); + + /*TODO- extract out of rtp_p for future live tools*/ + sprintf(sdpLine, "m=%s 0 RTP/%s %d", mediaName, tkHint->rtp_p->slMap.IV_length ? "SAVP" : "AVP", tkHint->rtp_p->PayloadType); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + if (tkHint->bandwidth) { + sprintf(sdpLine, "b=AS:%d", tkHint->bandwidth); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + } + if (tkHint->nb_chan) { + sprintf(sdpLine, "a=rtpmap:%d %s/%d/%d", tkHint->rtp_p->PayloadType, payloadName, tkHint->rtp_p->sl_config.timestampResolution, tkHint->nb_chan); + } else { + sprintf(sdpLine, "a=rtpmap:%d %s/%d", tkHint->rtp_p->PayloadType, payloadName, tkHint->rtp_p->sl_config.timestampResolution); + } + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + /*control for MPEG-4*/ + if (AddSystemInfo) { + sprintf(sdpLine, "a=mpeg4-esid:%d", gf_isom_get_track_id(tkHint->file, tkHint->TrackNum)); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + } + /*control for QTSS/DSS*/ + sprintf(sdpLine, "a=control:trackID=%d", gf_isom_get_track_id(tkHint->file, tkHint->HintTrack)); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + + /*H263 extensions*/ + if (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_H263) { + sprintf(sdpLine, "a=cliprect:0,0,%d,%d", Height, Width); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + } + /*AMR*/ + else if ((tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_AMR) || (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_AMR_WB)) { + sprintf(sdpLine, "a=fmtp:%d octet-align", tkHint->rtp_p->PayloadType); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + } + /*Text*/ + else if (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_3GPP_TEXT) { + gf_hinter_format_ttxt_sdp(tkHint->rtp_p, payloadName, sdpLine, tkHint->file, tkHint->TrackNum); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + } + /*EVRC/SMV in non header-free mode*/ + else if ((tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_EVRC_SMV) && (tkHint->rtp_p->auh_size>1)) { + sprintf(sdpLine, "a=fmtp:%d maxptime=%d", tkHint->rtp_p->PayloadType, tkHint->rtp_p->auh_size*20); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + } + /*H264/AVC*/ + else if (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_H264_AVC) { + GF_AVCConfig *avcc = gf_isom_avc_config_get(tkHint->file, tkHint->TrackNum, 1); + sprintf(sdpLine, "a=fmtp:%d profile-level-id=%02X%02X%02X; packetization-mode=1", tkHint->rtp_p->PayloadType, avcc->AVCProfileIndication, avcc->profile_compatibility, avcc->AVCLevelIndication); + if (gf_list_count(avcc->pictureParameterSets) || gf_list_count(avcc->sequenceParameterSets)) { + u32 i, count, b64s; + char b64[200]; + strcat(sdpLine, "; sprop-parameter-sets="); + count = gf_list_count(avcc->sequenceParameterSets); + for (i=0; isequenceParameterSets, i); + b64s = gf_base64_encode(sl->data, sl->size, b64, 200); + b64[b64s]=0; + strcat(sdpLine, b64); + if (i+1pictureParameterSets); + for (i=0; ipictureParameterSets, i); + b64s = gf_base64_encode(sl->data, sl->size, b64, 200); + b64[b64s]=0; + strcat(sdpLine, b64); + if (i+1file, tkHint->HintTrack, sdpLine); + gf_odf_avc_cfg_del(avcc); + } + /*MPEG-4 decoder config*/ + else if (tkHint->rtp_p->rtp_payt==GF_RTP_PAYT_MPEG4) { + dcd = gf_isom_get_decoder_config(tkHint->file, tkHint->TrackNum, 1); + + if (dcd && dcd->decoderSpecificInfo && dcd->decoderSpecificInfo->data) { + gf_rtp_builder_format_sdp(tkHint->rtp_p, payloadName, sdpLine, dcd->decoderSpecificInfo->data, dcd->decoderSpecificInfo->dataLength); + } else { + gf_rtp_builder_format_sdp(tkHint->rtp_p, payloadName, sdpLine, NULL, 0); + } + if (dcd) gf_odf_desc_del((GF_Descriptor *)dcd); + + if (tkHint->rtp_p->slMap.IV_length) { + const char *kms; + gf_isom_get_ismacryp_info(tkHint->file, tkHint->TrackNum, 1, NULL, NULL, NULL, NULL, &kms, NULL, NULL, NULL); + if (!strnicmp(kms, "(key)", 5) || !strnicmp(kms, "(ipmp)", 6) || !strnicmp(kms, "(uri)", 5)) { + strcat(sdpLine, "; ISMACrypKey="); + } else { + strcat(sdpLine, "; ISMACrypKey=(uri)"); + } + strcat(sdpLine, kms); + } + + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + } + /*MPEG-4 Audio LATM*/ + else if (tkHint->rtp_p->rtp_payt==GF_RTP_PAYT_LATM) { + GF_BitStream *bs; + char *config_bytes; + u32 config_size; + + /* form config string */ + bs = gf_bs_new(NULL, 32, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, 0, 1); /* AudioMuxVersion */ + gf_bs_write_int(bs, 1, 1); /* all streams same time */ + gf_bs_write_int(bs, 0, 6); /* numSubFrames */ + gf_bs_write_int(bs, 0, 4); /* numPrograms */ + gf_bs_write_int(bs, 0, 3); /* numLayer */ + + /* audio-specific config */ + dcd = gf_isom_get_decoder_config(tkHint->file, tkHint->TrackNum, 1); + if (dcd) { + gf_bs_write_data(bs, dcd->decoderSpecificInfo->data, dcd->decoderSpecificInfo->dataLength); + gf_odf_desc_del((GF_Descriptor *)dcd); + } + + /* other data */ + gf_bs_write_int(bs, 0, 3); /* frameLengthType */ + gf_bs_write_int(bs, 0xff, 8); /* latmBufferFullness */ + gf_bs_write_int(bs, 0, 1); /* otherDataPresent */ + gf_bs_write_int(bs, 0, 1); /* crcCheckPresent */ + gf_bs_get_content(bs, &config_bytes, &config_size); + gf_bs_del(bs); + + gf_rtp_builder_format_sdp(tkHint->rtp_p, payloadName, sdpLine, config_bytes, config_size); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + free(config_bytes); + } + /*3GPP DIMS*/ + else if (tkHint->rtp_p->rtp_payt==GF_RTP_PAYT_3GPP_DIMS) { + GF_DIMSDescription dims; + char fmt[200]; + gf_isom_get_visual_info(tkHint->file, tkHint->TrackNum, 1, &Width, &Height); + + gf_isom_get_dims_description(tkHint->file, tkHint->TrackNum, 1, &dims); + sprintf(sdpLine, "a=fmtp:%d Version-profile=%d", tkHint->rtp_p->PayloadType, dims.profile); + if (! dims.fullRequestHost) { + strcat(sdpLine, ";useFullRequestHost=0"); + sprintf(fmt, ";pathComponents=%d", dims.pathComponents); + strcat(sdpLine, fmt); + } + if (!dims.streamType) strcat(sdpLine, ";stream-type=secondary"); + if (dims.containsRedundant == 1) strcat(sdpLine, ";contains-redundant=main"); + else if (dims.containsRedundant == 2) strcat(sdpLine, ";contains-redundant=redundant"); + + if (dims.textEncoding && strlen(dims.textEncoding)) { + strcat(sdpLine, ";text-encoding="); + strcat(sdpLine, dims.textEncoding); + } + if (dims.contentEncoding && strlen(dims.contentEncoding)) { + strcat(sdpLine, ";content-coding="); + strcat(sdpLine, dims.contentEncoding); + } + if (dims.content_script_types && strlen(dims.content_script_types) ) { + strcat(sdpLine, ";content-script-types="); + strcat(sdpLine, dims.contentEncoding); + } + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + } + /*extensions for some mobile phones*/ + if (Width && Height) { + sprintf(sdpLine, "a=framesize:%d %d-%d", tkHint->rtp_p->PayloadType, Width, Height); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); + } + + gf_isom_set_track_enabled(tkHint->file, tkHint->HintTrack, 1); + return GF_OK; +} + +GF_EXPORT +Bool gf_hinter_can_embbed_data(char *data, u32 data_size, u32 streamType) +{ + char data64[5000]; + u32 size64; + + size64 = gf_base64_encode(data, data_size, data64, 5000); + if (!size64) return 0; + switch (streamType) { + case GF_STREAM_OD: + size64 += strlen("data:application/mpeg4-od-au;base64,"); + break; + case GF_STREAM_SCENE: + size64 += strlen("data:application/mpeg4-bifs-au;base64,"); + break; + default: + /*NOT NORMATIVE*/ + size64 += strlen("data:application/mpeg4-es-au;base64,"); + break; + } + if (size64>=255) return 0; + return 1; +} + + +GF_EXPORT +GF_Err gf_hinter_finalize(GF_ISOFile *file, u32 IOD_Profile, u32 bandwidth) +{ + u32 i, sceneT, odT, descIndex, size, size64; + GF_InitialObjectDescriptor *iod; + GF_SLConfig slc; + GF_ESD *esd; + GF_ISOSample *samp; + Bool remove_ocr; + char *buffer; + char buf64[5000], sdpLine[2300]; + + + gf_isom_sdp_clean(file); + + if (bandwidth) { + sprintf(buf64, "b=AS:%d", bandwidth); + gf_isom_sdp_add_line(file, buf64); + } + //xtended attribute for copyright + sprintf(buf64, "a=x-copyright: %s", "MP4/3GP File hinted with GPAC " GPAC_FULL_VERSION " (C)2000-2005 - http://gpac.sourceforge.net"); + gf_isom_sdp_add_line(file, buf64); + + if (IOD_Profile == GF_SDP_IOD_NONE) return GF_OK; + + odT = sceneT = 0; + for (i=0; iESDescriptors)) { + esd = (GF_ESD*)gf_list_get(iod->ESDescriptors, 0); + gf_odf_desc_del((GF_Descriptor *) esd); + gf_list_rem(iod->ESDescriptors, 0); + } + + + /*get OD esd, and embbed stream data if possible*/ + if (odT) { + esd = gf_isom_get_esd(file, odT, 1); + if (gf_isom_get_sample_count(file, odT)==1) { + samp = gf_isom_get_sample(file, odT, 1, &descIndex); + if (gf_hinter_can_embbed_data(samp->data, samp->dataLength, GF_STREAM_OD)) { + InitSL_NULL(&slc); + slc.predefined = 0; + slc.hasRandomAccessUnitsOnlyFlag = 1; + slc.timeScale = slc.timestampResolution = gf_isom_get_media_timescale(file, odT); + slc.OCRResolution = 1000; + slc.startCTS = samp->DTS+samp->CTS_Offset; + slc.startDTS = samp->DTS; + //set the SL for future extraction + gf_isom_set_extraction_slc(file, odT, 1, &slc); + + size64 = gf_base64_encode(samp->data, samp->dataLength, buf64, 2000); + buf64[size64] = 0; + sprintf(sdpLine, "data:application/mpeg4-od-au;base64,%s", buf64); + + esd->decoderConfig->avgBitrate = 0; + esd->decoderConfig->bufferSizeDB = samp->dataLength; + esd->decoderConfig->maxBitrate = 0; + size64 = strlen(sdpLine)+1; + esd->URLString = (char*)malloc(sizeof(char) * size64); + strcpy(esd->URLString, sdpLine); + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[rtp hinter] OD sample too large to be embedded in IOD - ISMA disabled\n")); + is_ok = 0; + } + gf_isom_sample_del(&samp); + } + if (remove_ocr) esd->OCRESID = 0; + else if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; + + //OK, add this to our IOD + gf_list_add(iod->ESDescriptors, esd); + } + + esd = gf_isom_get_esd(file, sceneT, 1); + if (gf_isom_get_sample_count(file, sceneT)==1) { + samp = gf_isom_get_sample(file, sceneT, 1, &descIndex); + if (gf_hinter_can_embbed_data(samp->data, samp->dataLength, GF_STREAM_SCENE)) { + + slc.timeScale = slc.timestampResolution = gf_isom_get_media_timescale(file, sceneT); + slc.OCRResolution = 1000; + slc.startCTS = samp->DTS+samp->CTS_Offset; + slc.startDTS = samp->DTS; + //set the SL for future extraction + gf_isom_set_extraction_slc(file, sceneT, 1, &slc); + //encode in Base64 the sample + size64 = gf_base64_encode(samp->data, samp->dataLength, buf64, 2000); + buf64[size64] = 0; + sprintf(sdpLine, "data:application/mpeg4-bifs-au;base64,%s", buf64); + + esd->decoderConfig->avgBitrate = 0; + esd->decoderConfig->bufferSizeDB = samp->dataLength; + esd->decoderConfig->maxBitrate = 0; + esd->URLString = (char*)malloc(sizeof(char) * (strlen(sdpLine)+1)); + strcpy(esd->URLString, sdpLine); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[rtp hinter] Scene description sample too large to be embedded in IOD - ISMA disabled\n")); + is_ok = 0; + } + gf_isom_sample_del(&samp); + } + if (remove_ocr) esd->OCRESID = 0; + else if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; + + gf_list_add(iod->ESDescriptors, esd); + + if (is_ok) { + u32 has_a, has_v, has_i_a, has_i_v; + has_a = has_v = has_i_a = has_i_v = 0; + for (i=0; idecoderConfig->streamType==GF_STREAM_VISUAL) { + if (esd->decoderConfig->objectTypeIndication==0x20) has_i_v ++; + else has_v++; + } else if (esd->decoderConfig->streamType==GF_STREAM_AUDIO) { + if (esd->decoderConfig->objectTypeIndication==0x40) has_i_a ++; + else has_a++; + } + gf_odf_desc_del((GF_Descriptor *)esd); + } + /*only 1 MPEG-4 visual max and 1 MPEG-4 audio max for ISMA compliancy*/ + if (!has_v && !has_a && (has_i_v<=1) && (has_i_a<=1)) { + sprintf(sdpLine, "a=isma-compliance:1,1.0,1"); + gf_isom_sdp_add_line(file, sdpLine); + } + } + } + + //encode the IOD + buffer = NULL; + size = 0; + gf_odf_desc_write((GF_Descriptor *) iod, &buffer, &size); + gf_odf_desc_del((GF_Descriptor *)iod); + + //encode in Base64 the iod + size64 = gf_base64_encode(buffer, size, buf64, 2000); + buf64[size64] = 0; + free(buffer); + + sprintf(sdpLine, "a=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"", buf64); + gf_isom_sdp_add_line(file, sdpLine); + + return GF_OK; +} + + +#endif //GPAC_READ_ONLY + diff --git a/src/media_tools/isom_tools.c b/src/media_tools/isom_tools.c new file mode 100644 index 0000000..6908c4f --- /dev/null +++ b/src/media_tools/isom_tools.c @@ -0,0 +1,1092 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include + +#ifndef GPAC_READ_ONLY + +static const u32 ISMA_VIDEO_OD_ID = 20; +static const u32 ISMA_AUDIO_OD_ID = 10; + +static const u32 ISMA_VIDEO_ES_ID = 201; +static const u32 ISMA_AUDIO_ES_ID = 101; + +static const char ISMA_BIFS_CONFIG[] = {0x00, 0x00, 0x60 }; + +/*ISMA audio*/ +static const u8 ISMA_BIFS_AUDIO[] = +{ + 0xC0, 0x10, 0x12, 0x81, 0x30, 0x2A, 0x05, 0x7C +}; +/*ISMA video*/ +static const u8 ISMA_GF_BIFS_VIDEO[] = +{ + 0xC0, 0x10, 0x12, 0x60, 0x42, 0x82, 0x28, 0x29, + 0xD0, 0x4F, 0x00 +}; +/*ISMA audio-video*/ +static const u8 ISMA_BIFS_AV[] = +{ + 0xC0, 0x10, 0x12, 0x81, 0x30, 0x2A, 0x05, 0x72, + 0x60, 0x42, 0x82, 0x28, 0x29, 0xD0, 0x4F, 0x00 +}; + +/*image only - uses same visual OD ID as video*/ +static const u8 ISMA_BIFS_IMAGE[] = +{ + 0xC0, 0x11, 0xA4, 0xCD, 0x53, 0x6A, 0x0A, 0x44, + 0x13, 0x00 +}; + +/*ISMA audio-image*/ +static const u8 ISMA_BIFS_AI[] = +{ + 0xC0, 0x11, 0xA5, 0x02, 0x60, 0x54, 0x0A, 0xE4, + 0xCD, 0x53, 0x6A, 0x0A, 0x44, 0x13, 0x00 +}; + + +GF_EXPORT +GF_Err gf_media_make_isma(GF_ISOFile *mp4file, Bool keepESIDs, Bool keepImage, Bool no_ocr) +{ + u32 AudioTrack, VideoTrack, Tracks, i, mType, bifsT, odT, descIndex, VideoType, VID, AID, bifsID, odID; + u32 bifs, w, h; + Bool is_image, image_track; + GF_ESD *a_esd, *v_esd, *_esd; + Bool update_vid_esd; + GF_ObjectDescriptor *od; + GF_ODUpdate *odU; + GF_ODCodec *codec; + GF_ISOSample *samp; + GF_BitStream *bs; + u8 audioPL, visualPL; + + switch (gf_isom_get_mode(mp4file)) { + case GF_ISOM_OPEN_EDIT: + case GF_ISOM_OPEN_WRITE: + case GF_ISOM_WRITE_EDIT: + break; + default: + return GF_BAD_PARAM; + } + + + Tracks = gf_isom_get_track_count(mp4file); + AID = VID = 0; + is_image = 0; + + //search for tracks + for (i=0; idecoderConfig->objectTypeIndication==0x6C) || (esd->decoderConfig->objectTypeIndication==0x6D)) ) + image_track = 1; + + /*remove image tracks if wanted*/ + if (keepImage || !image_track) { + /*only ONE video stream possible with ISMA*/ + if (VID) { + if (esd) gf_odf_desc_del((GF_Descriptor*)esd); + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA convert] More than one video track found, cannot convert file - remove extra track(s)\n")); + return GF_NOT_SUPPORTED; + } + VID = gf_isom_get_track_id(mp4file, i+1); + is_image = image_track; + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[ISMA convert] Visual track ID %d: only one sample found, assuming image and removing track\n", gf_isom_get_track_id(mp4file, i+1) ) ); + gf_isom_remove_track(mp4file, i+1); + i -= 1; + Tracks = gf_isom_get_track_count(mp4file); + } + break; + case GF_ISOM_MEDIA_AUDIO: + if (AID) { + if (esd) gf_odf_desc_del((GF_Descriptor*)esd); + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ISMA convert] More than one audio track found, cannot convert file - remove extra track(s)\n") ); + return GF_NOT_SUPPORTED; + } + AID = gf_isom_get_track_id(mp4file, i+1); + break; + /*clean file*/ + default: + if (mType==GF_ISOM_MEDIA_HINT) { + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[ISMA convert] Removing Hint track ID %d\n", gf_isom_get_track_id(mp4file, i+1) )); + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[ISMA convert] Removing track ID %d\n", gf_isom_get_track_id(mp4file, i+1) )); + } + gf_isom_remove_track(mp4file, i+1); + i -= 1; + Tracks = gf_isom_get_track_count(mp4file); + break; + } + if (esd) gf_odf_desc_del((GF_Descriptor*)esd); + } + //no audio no video + if (!AID && !VID) return GF_OK; + + /*reset all PLs*/ + visualPL = 0xFE; + audioPL = 0xFE; + + od = (GF_ObjectDescriptor *) gf_isom_get_root_od(mp4file); + if (od && (od->tag==GF_ODF_IOD_TAG)) { + audioPL = ((GF_InitialObjectDescriptor*)od)->audio_profileAndLevel; + visualPL = ((GF_InitialObjectDescriptor*)od)->visual_profileAndLevel; + } + if (od) gf_odf_desc_del((GF_Descriptor *)od); + + + //create the OD AU + bifs = 0; + odU = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + + a_esd = v_esd = NULL; + update_vid_esd = 0; + + gf_isom_set_root_od_id(mp4file, 1); + + bifsID = 1; + odID = 2; + if (keepESIDs) { + bifsID = 1; + while ((bifsID==AID) || (bifsID==VID)) bifsID++; + odID = 2; + while ((odID==AID) || (odID==VID) || (odID==bifsID)) odID++; + + } + + VideoTrack = gf_isom_get_track_by_id(mp4file, VID); + AudioTrack = gf_isom_get_track_by_id(mp4file, AID); + + w = h = 0; + if (VideoTrack) { + bifs = 1; + VideoType = 0; + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = ISMA_VIDEO_OD_ID; + + if (!keepESIDs && (VID != ISMA_VIDEO_ES_ID)) { + gf_isom_set_track_id(mp4file, VideoTrack, ISMA_VIDEO_ES_ID); + } + + v_esd = gf_isom_get_esd(mp4file, VideoTrack, 1); + if (v_esd) { + v_esd->OCRESID = no_ocr ? 0 : bifsID; + + gf_odf_desc_add_desc((GF_Descriptor *)od, (GF_Descriptor *)v_esd); + gf_list_add(odU->objectDescriptors, od); + + gf_isom_get_track_layout_info(mp4file, VideoTrack, &w, &h, NULL, NULL, NULL); + if (!w || !h) { + gf_isom_get_visual_info(mp4file, VideoTrack, 1, &w, &h); + if ((v_esd->decoderConfig->objectTypeIndication==0x20) && (v_esd->decoderConfig->streamType==GF_STREAM_VISUAL)) { + GF_M4VDecSpecInfo dsi; + gf_m4v_get_config(v_esd->decoderConfig->decoderSpecificInfo->data, v_esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + if (!is_image && (!w || !h)) { + w = dsi.width; + h = dsi.height; + gf_isom_set_visual_info(mp4file, VideoTrack, 1, w, h); + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[ISMA convert] Adjusting visual track size to %d x %d\n", w, h)); + } + if (dsi.par_num && (dsi.par_den!=dsi.par_num)) { + w *= dsi.par_num; + w /= dsi.par_den; + } + if (dsi.VideoPL) visualPL = dsi.VideoPL; + } + } + } + } + + if (AudioTrack) { + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = ISMA_AUDIO_OD_ID; + + if (!keepESIDs && (AID != ISMA_AUDIO_ES_ID)) { + gf_isom_set_track_id(mp4file, AudioTrack, ISMA_AUDIO_ES_ID); + } + + a_esd = gf_isom_get_esd(mp4file, AudioTrack, 1); + if (a_esd) { + a_esd->OCRESID = no_ocr ? 0 : bifsID; + + if (!keepESIDs) a_esd->ESID = ISMA_AUDIO_ES_ID; + gf_odf_desc_add_desc((GF_Descriptor *)od, (GF_Descriptor *)a_esd); + gf_list_add(odU->objectDescriptors, od); + if (!bifs) { + bifs = 3; + } else { + bifs = 2; + } + + if (a_esd->decoderConfig->objectTypeIndication == 0x40) { + GF_M4ADecSpecInfo cfg; + gf_m4a_get_config(a_esd->decoderConfig->decoderSpecificInfo->data, a_esd->decoderConfig->decoderSpecificInfo->dataLength, &cfg); + audioPL = cfg.audioPL; + } + } + } + + /*update video cfg if needed*/ + if (v_esd) gf_isom_change_mpeg4_description(mp4file, VideoTrack, 1, v_esd); + if (a_esd) gf_isom_change_mpeg4_description(mp4file, AudioTrack, 1, a_esd); + + /*likely 3GP or other files...*/ + if ((!a_esd && AudioTrack) || (!v_esd && VideoTrack)) return GF_OK; + + //get the OD sample + codec = gf_odf_codec_new(); + samp = gf_isom_sample_new(); + gf_odf_codec_add_com(codec, (GF_ODCom *)odU); + gf_odf_codec_encode(codec, 1); + gf_odf_codec_get_au(codec, &samp->data, &samp->dataLength); + gf_odf_codec_del(codec); + samp->CTS_Offset = 0; + samp->DTS = 0; + samp->IsRAP = 1; + + /*create the OD track*/ + odT = gf_isom_new_track(mp4file, odID, GF_ISOM_MEDIA_OD, gf_isom_get_timescale(mp4file)); + if (!odT) return gf_isom_last_error(mp4file); + + _esd = gf_odf_desc_esd_new(SLPredef_MP4); + _esd->decoderConfig->bufferSizeDB = samp->dataLength; + _esd->decoderConfig->objectTypeIndication = 0x01; + _esd->decoderConfig->streamType = GF_STREAM_OD; + _esd->ESID = odID; + _esd->OCRESID = no_ocr ? 0 : bifsID; + gf_isom_new_mpeg4_description(mp4file, odT, _esd, NULL, NULL, &descIndex); + gf_odf_desc_del((GF_Descriptor *)_esd); + gf_isom_add_sample(mp4file, odT, 1, samp); + gf_isom_sample_del(&samp); + + gf_isom_set_track_group(mp4file, odT, 1); + + /*create the BIFS track*/ + bifsT = gf_isom_new_track(mp4file, bifsID, GF_ISOM_MEDIA_SCENE, gf_isom_get_timescale(mp4file)); + if (!bifsT) return gf_isom_last_error(mp4file); + + _esd = gf_odf_desc_esd_new(SLPredef_MP4); + _esd->decoderConfig->bufferSizeDB = sizeof(ISMA_BIFS_CONFIG); + _esd->decoderConfig->objectTypeIndication = 0x02; + _esd->decoderConfig->streamType = GF_STREAM_SCENE; + _esd->ESID = bifsID; + _esd->OCRESID = 0; + + /*rewrite ISMA BIFS cfg*/ + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*empty bifs stuff*/ + gf_bs_write_int(bs, 0, 17); + /*command stream*/ + gf_bs_write_int(bs, 1, 1); + /*in pixel metrics*/ + gf_bs_write_int(bs, 1, 1); + /*with size*/ + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, w, 16); + gf_bs_write_int(bs, h, 16); + gf_bs_align(bs); + gf_bs_get_content(bs, &_esd->decoderConfig->decoderSpecificInfo->data, &_esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_isom_new_mpeg4_description(mp4file, bifsT, _esd, NULL, NULL, &descIndex); + gf_odf_desc_del((GF_Descriptor *)_esd); + gf_bs_del(bs); + gf_isom_set_visual_info(mp4file, bifsT, descIndex, w, h); + + samp = gf_isom_sample_new(); + samp->CTS_Offset = 0; + samp->DTS = 0; + switch (bifs) { + case 1: + if (is_image) { + samp->data = (char *) ISMA_BIFS_IMAGE; + samp->dataLength = 10; + } else { + samp->data = (char *) ISMA_GF_BIFS_VIDEO; + samp->dataLength = 11; + } + break; + case 2: + if (is_image) { + samp->data = (char *) ISMA_BIFS_AI; + samp->dataLength = 15; + } else { + samp->data = (char *) ISMA_BIFS_AV; + samp->dataLength = 16; + } + break; + case 3: + samp->data = (char *) ISMA_BIFS_AUDIO; + samp->dataLength = 8; + break; + } + + samp->IsRAP = 1; + + gf_isom_add_sample(mp4file, bifsT, 1, samp); + samp->data = NULL; + gf_isom_sample_del(&samp); + gf_isom_set_track_group(mp4file, bifsT, 1); + + gf_isom_set_track_enabled(mp4file, bifsT, 1); + gf_isom_set_track_enabled(mp4file, odT, 1); + gf_isom_add_track_to_root_od(mp4file, bifsT); + gf_isom_add_track_to_root_od(mp4file, odT); + + gf_isom_set_pl_indication(mp4file, GF_ISOM_PL_SCENE, 1); + gf_isom_set_pl_indication(mp4file, GF_ISOM_PL_GRAPHICS, 1); + gf_isom_set_pl_indication(mp4file, GF_ISOM_PL_OD, 1); + gf_isom_set_pl_indication(mp4file, GF_ISOM_PL_AUDIO, audioPL); + gf_isom_set_pl_indication(mp4file, GF_ISOM_PL_VISUAL, (u8) (is_image ? 0xFE : visualPL)); + + gf_isom_set_brand_info(mp4file, GF_ISOM_BRAND_MP42, 1); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_ISOM, 1); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP5, 1); + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_media_make_3gpp(GF_ISOFile *mp4file) +{ + u32 Tracks, i, mType, stype, nb_vid, nb_avc, nb_aud, nb_txt, nb_non_mp4, nb_dims; + Bool is_3g2 = 0; + + switch (gf_isom_get_mode(mp4file)) { + case GF_ISOM_OPEN_EDIT: + case GF_ISOM_OPEN_WRITE: + case GF_ISOM_WRITE_EDIT: + break; + default: + return GF_BAD_PARAM; + } + + Tracks = gf_isom_get_track_count(mp4file); + nb_vid = nb_aud = nb_txt = nb_avc = nb_non_mp4 = nb_dims = 0; + + for (i=0; idecoderConfig->objectTypeIndication==0x20) || (esd->decoderConfig->objectTypeIndication==0x21) ) { + nb_vid++; + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Video format not supported by 3GP - removing track ID %d\n", gf_isom_get_track_id(mp4file, i+1) )); + goto remove_track; + } + gf_odf_desc_del((GF_Descriptor *)esd); + } + break; + default: + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Video format not supported by 3GP - removing track ID %d\n", gf_isom_get_track_id(mp4file, i+1) )); + goto remove_track; + } + break; + case GF_ISOM_MEDIA_AUDIO: + if (stype == GF_ISOM_SUBTYPE_MPEG4_CRYP) gf_isom_get_ismacryp_info(mp4file, i+1, 1, &stype, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + switch (stype) { + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + is_3g2 = 1; + nb_aud++; + break; + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + nb_aud++; + nb_non_mp4 ++; + break; + case GF_ISOM_SUBTYPE_MPEG4: + { + GF_ESD *esd = gf_isom_get_esd(mp4file, i+1, 1); + switch (esd->decoderConfig->objectTypeIndication) { + case 0xE1: + case 0xA0: + case 0xA1: + is_3g2 = 1; + case 0x40: + nb_aud++; + break; + default: + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Audio format not supported by 3GP - removing track ID %d\n", gf_isom_get_track_id(mp4file, i+1) )); + goto remove_track; + } + gf_odf_desc_del((GF_Descriptor *)esd); + } + break; + default: + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Audio format not supported by 3GP - removing track ID %d\n", gf_isom_get_track_id(mp4file, i+1) )); + goto remove_track; + } + break; + case GF_ISOM_MEDIA_TEXT: + nb_txt++; + break; + case GF_ISOM_MEDIA_SCENE: + if (stype == GF_ISOM_MEDIA_DIMS) { + nb_dims++; + break; + } + /*clean file*/ + default: + if (mType==GF_ISOM_MEDIA_HINT) { + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Removing Hint track ID %d\n", gf_isom_get_track_id(mp4file, i+1) )); + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Removing system track ID %d\n", gf_isom_get_track_id(mp4file, i+1) )); + } + +remove_track: + gf_isom_remove_track(mp4file, i+1); + i -= 1; + Tracks = gf_isom_get_track_count(mp4file); + break; + } + } + + /*no more IOD*/ + gf_isom_remove_root_od(mp4file); + + if (is_3g2) { + gf_isom_set_brand_info(mp4file, GF_ISOM_BRAND_3G2A, 65536); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP6, 0); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP5, 0); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GG6, 0); + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Setting major brand to 3GPP2\n")); + } else { + /*update FType*/ + if ((nb_vid>1) || (nb_aud>1) || (nb_txt>1)) { + /*3GPP general purpose*/ + gf_isom_set_brand_info(mp4file, GF_ISOM_BRAND_3GG6, 1024); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP6, 0); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP5, 0); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP4, 0); + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Setting major brand to 3GPP Generic file\n")); + } + /*commented for QT compatibility, although this is correct (qt doesn't understand 3GP6 brand)*/ + else if (nb_txt && 0) { + gf_isom_set_brand_info(mp4file, GF_ISOM_BRAND_3GP6, 1024); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP5, 1); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP4, 1); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GG6, 0); + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Setting major brand to 3GPP V6 file\n")); + } else if (nb_avc) { + gf_isom_set_brand_info(mp4file, GF_ISOM_BRAND_3GP6, 0/*1024*/); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_AVC1, 1); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP5, 0); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP4, 0); + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Setting major brand to 3GPP V6 file + AVC compatible\n")); + } else { + gf_isom_set_brand_info(mp4file, nb_avc ? GF_ISOM_BRAND_3GP6 : GF_ISOM_BRAND_3GP5, 0/*1024*/); + gf_isom_modify_alternate_brand(mp4file, nb_avc ? GF_ISOM_BRAND_3GP5 : GF_ISOM_BRAND_3GP6, 0); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GP4, 1); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_3GG6, 0); + GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[3GPP convert] Setting major brand to 3GPP V5 file\n")); + } + } + /*add/remove MP4 brands and add isom*/ + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_MP41, (u8) ((nb_avc||is_3g2||nb_non_mp4) ? 0 : 1)); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_MP42, (u8) (nb_non_mp4 ? 0 : 1)); + gf_isom_modify_alternate_brand(mp4file, GF_ISOM_BRAND_ISOM, 1); + return GF_OK; +} + +GF_Err gf_media_make_psp(GF_ISOFile *mp4) +{ + u32 i, count; + u32 nb_a, nb_v; + /*psp track UUID*/ + bin128 psp_track_uuid = {0x55, 0x53, 0x4D, 0x54, 0x21, 0xD2, 0x4F, 0xCE, 0xBB, 0x88, 0x69, 0x5C, 0xFA, 0xC9, 0xC7, 0x40}; + u8 psp_track_sig [] = {0x00, 0x00, 0x00, 0x1C, 0x4D, 0x54, 0x44, 0x54, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0A, 0x55, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; + /*psp mov UUID*/ + //bin128 psp_uuid = {0x50, 0x52, 0x4F, 0x46, 0x21, 0xD2, 0x4F, 0xCE, 0xBB, 0x88, 0x69, 0x5C, 0xFA, 0xC9, 0xC7, 0x40}; + + nb_a = nb_v = 0; + count = gf_isom_get_track_count(mp4); + for (i=0; iTrackID = gf_isom_get_track_id(output, TrackNum); + tf->SampleCount = gf_isom_get_sample_count(input, i+1); + tf->OriginalTrack = i+1; + tf->TimeScale = gf_isom_get_media_timescale(input, i+1); + tf->MediaType = gf_isom_get_media_type(input, i+1); + tf->DefaultDuration = defaultDuration; + gf_list_add(fragmenters, tf); + nb_samp += count; + } + + if (gf_isom_is_track_in_root_od(input, i+1)) gf_isom_add_track_to_root_od(output, TrackNum); + //copy user data ? + } + + + //flush movie + e = gf_isom_finalize_for_fragment(output); + if (e) goto err_exit; + + nb_done = 0; + + while ( (count = gf_list_count(fragmenters)) ) { + + e = gf_isom_start_fragment(output); + if (e) goto err_exit; + //setup some default + for (i=0; iMediaType == GF_ISOM_MEDIA_VISUAL) { + e = gf_isom_set_fragment_option(output, tf->TrackID, GF_ISOM_TRAF_RANDOM_ACCESS, 1); + if (e) goto err_exit; + } + } + sample = NULL; + + //process track by track + for (i=0; iOriginalTrack, tf->SampleNum + 1, &descIndex); + } + gf_isom_get_sample_padding_bits(input, tf->OriginalTrack, tf->SampleNum+1, &NbBits); + + next = gf_isom_get_sample(input, tf->OriginalTrack, tf->SampleNum + 2, &j); + if (next) { + defaultDuration = (u32) (next->DTS - sample->DTS); + } else { + defaultDuration = tf->DefaultDuration; + } + + e = gf_isom_fragment_add_sample(output, tf->TrackID, sample, descIndex, + defaultDuration, NbBits, 0); + if (e) goto err_exit; + + gf_set_progress("ISO File Fragmenting", nb_done, nb_samp); + nb_done++; + + gf_isom_sample_del(&sample); + sample = next; + tf->FragmentLength += defaultDuration; + tf->SampleNum += 1; + + //end of track fragment or track + if ((tf->SampleNum==tf->SampleCount) || (tf->FragmentLength*1000 > MaxFragmentDuration*tf->TimeScale)) { + gf_isom_sample_del(&next); + sample = next = NULL; + tf->FragmentLength = 0; + break; + } + } + if (tf->SampleNum==tf->SampleCount) { + free(tf); + gf_list_rem(fragmenters, i); + i--; + count --; + } + } + } + +err_exit: + while (gf_list_count(fragmenters)) { + tf = (TrackFragmenter *)gf_list_get(fragmenters, 0); + free(tf); + gf_list_rem(fragmenters, 0); + } + gf_list_del(fragmenters); + if (e) gf_isom_delete(output); + else gf_isom_close(output); + gf_set_progress("ISO File Fragmenting", nb_samp, nb_samp); + return e; +} + +GF_EXPORT +GF_Err gf_media_import_chapters(GF_ISOFile *file, char *chap_file, Double import_fps) +{ + GF_Err e; + u32 state; + u32 cur_chap; + u64 ts; + u32 h, m, s, ms, fr, fps; + char line[1024]; + char szTitle[1024]; + FILE *f = fopen(chap_file, "rt"); + if (!f) return GF_URL_ERROR; + + e = gf_isom_remove_chapter(file, 0, 0); + if (e) goto err_exit; + + cur_chap = 0; + ts = 0; + state = 0; + while (fgets(line, 1024, f) != NULL) { + char *title = NULL; + u32 off = 0; + char *sL; + while (1) { + u32 len = strlen(line); + if (!len) break; + switch (line[len-1]) { + case '\n': case '\t': case '\r': case ' ': + line[len-1] = 0; + continue; + } + break; + } + + while (line[off]==' ') off++; + if (!strlen(line+off)) continue; + sL = line+off; + + szTitle[0] = 0; + /*ZoomPlayer chapters*/ + if (!strnicmp(sL, "AddChapter(", 11)) { + u32 nb_fr; + sscanf(sL, "AddChapter(%d,%s)", &nb_fr, szTitle); + ts = nb_fr; + ts *= 1000; + if (import_fps) ts = (u64) (((s64) ts ) / import_fps); + else ts /= 25; + sL = strchr(sL, ','); strcpy(szTitle, sL+1); sL = strrchr(szTitle, ')'); if (sL) sL[0] = 0; + } else if (!strnicmp(sL, "AddChapterBySecond(", 19)) { + u32 nb_s; + sscanf(sL, "AddChapterBySecond(%d,%s)", &nb_s, szTitle); + ts = nb_s; + ts *= 1000; + sL = strchr(sL, ','); strcpy(szTitle, sL+1); sL = strrchr(szTitle, ')'); if (sL) sL[0] = 0; + } else if (!strnicmp(sL, "AddChapterByTime(", 17)) { + u32 h, m, s; + sscanf(sL, "AddChapterByTime(%d,%d,%d,%s)", &h, &m, &s, szTitle); + ts = 3600*h + 60*m + s; + ts *= 1000; + sL = strchr(sL, ','); + if (sL) sL = strchr(sL+1, ','); + if (sL) sL = strchr(sL+1, ','); + strcpy(szTitle, sL+1); sL = strrchr(szTitle, ')'); if (sL) sL[0] = 0; + } + /*regular or SMPTE time codes*/ + else if ((strlen(sL)>=8) && (sL[2]==':') && (sL[5]==':')) { + title = NULL; + if (strlen(sL)==8) { + sscanf(sL, "%02d:%02d:%02d", &h, &m, &s); + ts = (h*3600 + m*60+s)*1000; + } + else { + char szTS[20], *tok; + strncpy(szTS, sL, 18); + tok = strrchr(szTS, ' '); + if (tok) { + title = strchr(sL, ' ') + 1; + while (title[0]==' ') title++; + if (strlen(title)) strcpy(szTitle, title); + tok[0] = 0; + } + ts = 0; + h = m = s = ms = 0; + + if (sscanf(szTS, "%d:%d:%d;%d/%d", &h, &m, &s, &fr, &fps)==5) { + ts = (h*3600 + m*60+s)*1000 + 1000*fr/fps; + } else if (sscanf(szTS, "%d:%d:%d;%d", &h, &m, &s, &fr)==4) { + ts = (h*3600 + m*60+s); + if (import_fps) + ts = (s64) (((import_fps*((s64)ts) + fr) * 1000 ) / import_fps); + else + ts = ((ts*25 + fr) * 1000 ) / 25; + } else if (sscanf(szTS, "%d:%d:%d.%d", &h, &m, &s, &ms) == 4) { + ts = (h*3600 + m*60+s)*1000+ms; + } else if (sscanf(szTS, "%d:%d:%d.%d", &h, &m, &s, &ms) == 4) { + ts = (h*3600 + m*60+s)*1000+ms; + } else if (sscanf(szTS, "%d:%d:%d:%d", &h, &m, &s, &ms) == 4) { + ts = (h*3600 + m*60+s)*1000+ms; + } else if (sscanf(szTS, "%d:%d:%d", &h, &m, &s) == 3) { + ts = (h*3600 + m*60+s) * 1000; + } + } + } + /*CHAPTERX= and CHAPTERXNAME=*/ + else if (!strnicmp(sL, "CHAPTER", 7)) { + u32 idx; + char szTemp[20], *str; + strncpy(szTemp, sL, 19); + str = strrchr(szTemp, '='); + if (!str) continue; + str[0] = 0; + strlwr(szTemp); + idx = cur_chap; + str = strchr(sL, '='); + str++; + if (strstr(szTemp, "name")) { + sscanf(szTemp, "chapter%dname", &idx); + strcpy(szTitle, str); + if (idx!=cur_chap) { + cur_chap=idx; + state = 0; + } + state++; + } else { + sscanf(szTemp, "chapter%d", &idx); + if (idx!=cur_chap) { + cur_chap=idx; + state = 0; + } + state++; + + ts = 0; + h = m = s = ms = 0; + if (sscanf(str, "%d:%d:%d.%d", &h, &m, &s, &ms) == 4) { + ts = (h*3600 + m*60+s)*1000+ms; + } else if (sscanf(str, "%d:%d:%d:%d", &h, &m, &s, &ms) == 4) { + ts = (h*3600 + m*60+s)*1000+ms; + } else if (sscanf(str, "%d:%d:%d", &h, &m, &s) == 3) { + ts = (h*3600 + m*60+s) * 1000; + } + } + if (state==2) { + e = gf_isom_add_chapter(file, 0, ts, szTitle); + if (e) goto err_exit; + state = 0; + } + continue; + } + else continue; + + if (strlen(szTitle)) { + e = gf_isom_add_chapter(file, 0, ts, szTitle); + } else { + e = gf_isom_add_chapter(file, 0, ts, NULL); + } + if (e) goto err_exit; + } + + +err_exit: + fclose(f); + return e; +} + +#endif //GPAC_READ_ONLY + +GF_EXPORT +GF_ESD *gf_media_map_esd(GF_ISOFile *mp4, u32 track) +{ + u32 type; + GF_GenericSampleDescription *udesc; + GF_BitStream *bs; + GF_ESD *esd; + + u32 subtype = gf_isom_get_media_subtype(mp4, track, 1); + /*all types with an official MPEG-4 mapping*/ + switch (subtype) { + case GF_ISOM_SUBTYPE_MPEG4: + case GF_ISOM_SUBTYPE_MPEG4_CRYP: + case GF_ISOM_SUBTYPE_AVC_H264: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + return gf_isom_get_esd(mp4, track, 1); + } + + if (gf_isom_get_media_type(mp4, track) == GF_ISOM_MEDIA_TEXT) + return gf_isom_get_esd(mp4, track, 1); + + if ((subtype == GF_ISOM_SUBTYPE_3GP_AMR) || (subtype == GF_ISOM_SUBTYPE_3GP_AMR_WB)) { + GF_3GPConfig *gpc = gf_isom_3gp_config_get(mp4, track, 1); + esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = gf_isom_get_media_timescale(mp4, track); + esd->ESID = gf_isom_get_track_id(mp4, track); + esd->OCRESID = esd->ESID; + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + /*use private DSI*/ + esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_GENERIC; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*format ext*/ + gf_bs_write_u32(bs, subtype); + gf_bs_write_u32(bs, (subtype == GF_ISOM_SUBTYPE_3GP_AMR) ? 8000 : 16000); + gf_bs_write_u16(bs, 1); + gf_bs_write_u16(bs, (subtype == GF_ISOM_SUBTYPE_3GP_AMR) ? 160 : 320); + gf_bs_write_u8(bs, 16); + gf_bs_write_u8(bs, gpc ? gpc->frames_per_sample : 0); + if (gpc) free(gpc); + gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + return esd; + } + + if (subtype == GF_ISOM_SUBTYPE_3GP_H263) { + u32 w, h; + esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = gf_isom_get_media_timescale(mp4, track); + esd->ESID = gf_isom_get_track_id(mp4, track); + esd->OCRESID = esd->ESID; + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + /*use private DSI*/ + esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_GENERIC; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*format ext*/ + gf_bs_write_u32(bs, GF_4CC('h', '2', '6', '3')); + gf_isom_get_visual_info(mp4, track, 1, &w, &h); + gf_bs_write_u16(bs, w); + gf_bs_write_u16(bs, h); + gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + return esd; + } + + if (subtype == GF_ISOM_SUBTYPE_AC3) { + esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = gf_isom_get_media_timescale(mp4, track); + esd->ESID = gf_isom_get_track_id(mp4, track); + esd->OCRESID = esd->ESID; + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + esd->decoderConfig->objectTypeIndication = 0xA5; + gf_odf_desc_del((GF_Descriptor*)esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = NULL; + return esd; + } + + if (subtype == GF_ISOM_SUBTYPE_3GP_DIMS) { + GF_DIMSDescription dims; + esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = gf_isom_get_media_timescale(mp4, track); + esd->ESID = gf_isom_get_track_id(mp4, track); + esd->OCRESID = esd->ESID; + esd->decoderConfig->streamType = GF_STREAM_SCENE; + /*use private DSI*/ + esd->decoderConfig->objectTypeIndication = GPAC_OTI_SCENE_DIMS; + gf_isom_get_dims_description(mp4, track, 1, &dims); + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*format ext*/ + gf_bs_write_u8(bs, dims.profile); + gf_bs_write_u8(bs, dims.level); + gf_bs_write_int(bs, dims.pathComponents, 4); + gf_bs_write_int(bs, dims.fullRequestHost, 1); + gf_bs_write_int(bs, dims.streamType, 1); + gf_bs_write_int(bs, dims.containsRedundant, 2); + gf_bs_write_data(bs, (char*)dims.textEncoding, strlen(dims.textEncoding)+1); + gf_bs_write_data(bs, (char*)dims.contentEncoding, strlen(dims.contentEncoding)+1); + gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + return esd; + } + + type = gf_isom_get_media_type(mp4, track); + if ((type != GF_ISOM_MEDIA_AUDIO) && (type != GF_ISOM_MEDIA_VISUAL)) return NULL; + + esd = gf_odf_desc_esd_new(0); + esd->OCRESID = esd->ESID = gf_isom_get_track_id(mp4, track); + esd->slConfig->useTimestampsFlag = 1; + esd->slConfig->timestampResolution = gf_isom_get_media_timescale(mp4, track); + esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_GENERIC; + /*format ext*/ + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, subtype); + udesc = gf_isom_get_generic_sample_description(mp4, track, 1); + if (type==GF_ISOM_MEDIA_AUDIO) { + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + gf_bs_write_u32(bs, udesc->samplerate); + gf_bs_write_u16(bs, udesc->nb_channels); + gf_bs_write_u16(bs, 0); + gf_bs_write_u8(bs, udesc->bits_per_sample); + gf_bs_write_u8(bs, 0); + } else { + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + gf_bs_write_u16(bs, udesc->width); + gf_bs_write_u16(bs, udesc->height); + } + if (udesc && udesc->extension_buf_size) { + gf_bs_write_data(bs, udesc->extension_buf, udesc->extension_buf_size); + free(udesc->extension_buf); + } + if (udesc) free(udesc); + gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + return esd; +} diff --git a/src/media_tools/media_export.c b/src/media_tools/media_export.c new file mode 100644 index 0000000..b34404b --- /dev/null +++ b/src/media_tools/media_export.c @@ -0,0 +1,2000 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include + +#ifndef GPAC_READ_ONLY + +#include +#include +#include +#include + +GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc); + +static GF_Err gf_export_message(GF_MediaExporter *dumper, GF_Err e, char *format, ...) +{ + if (dumper->flags & GF_EXPORT_PROBE_ONLY) return e; + +#ifndef GPAC_DISABLE_LOG + if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_AUTHOR)) { + va_list args; + char szMsg[1024]; + va_start(args, format); + vsprintf(szMsg, format, args); + va_end(args); + GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_AUTHOR, ("%s\n", szMsg) ); + } +#endif + return e; +} + +/*that's very very crude, we only support vorbis & theora in MP4 - this will need cleanup as soon as possible*/ +static GF_Err gf_dump_to_ogg(GF_MediaExporter *dumper, char *szName, u32 track) +{ + FILE *out; + ogg_stream_state os; + ogg_packet op; + ogg_page og; + u32 count, i, di, theora_kgs, nb_i, nb_p; + GF_BitStream *bs; + GF_ISOSample *samp; + GF_ESD *esd = gf_isom_get_esd(dumper->file, track, 1); + + + gf_rand_init(1); + ogg_stream_init(&os, gf_rand()); + + op.granulepos = 0; + op.packetno = 0; + op.b_o_s = 1; + op.e_o_s = 0; + + out = gf_f64_open(szName, "wb"); + if (!out) return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName); + + theora_kgs = 0; + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + while (gf_bs_available(bs)) { + op.bytes = gf_bs_read_u16(bs); + op.packet = (unsigned char*)malloc(sizeof(char) * op.bytes); + gf_bs_read_data(bs, (char*)op.packet, op.bytes); + ogg_stream_packetin(&os, &op); + + if (op.b_o_s) { + ogg_stream_pageout(&os, &og); + fwrite(og.header, 1, og.header_len, out); + fwrite(og.body, 1, og.body_len, out); + op.b_o_s = 0; + + if (esd->decoderConfig->objectTypeIndication==0xDF) { + u32 kff; + GF_BitStream *vbs = gf_bs_new((char*)op.packet, op.bytes, GF_BITSTREAM_READ); + gf_bs_skip_bytes(vbs, 40); + gf_bs_read_int(vbs, 6); /* quality */ + kff = 1 << gf_bs_read_int(vbs, 5); + gf_bs_del(vbs); + + theora_kgs = 0; + kff--; + while (kff) { + theora_kgs ++; + kff >>= 1; + } + } + } + free(op.packet); + op.packetno ++; + } + gf_bs_del(bs); + gf_odf_desc_del((GF_Descriptor *)esd); + + while (ogg_stream_pageout(&os, &og)>0) { + fwrite(og.header, 1, og.header_len, out); + fwrite(og.body, 1, og.body_len, out); + } + + op.granulepos = -1; + + count = gf_isom_get_sample_count(dumper->file, track); + + nb_i = nb_p = 0; + samp = gf_isom_get_sample(dumper->file, track, 1, &di); + for (i=0; ifile, track, i+2, &di); + if (!samp) break; + op.bytes = samp->dataLength; + op.packet = (unsigned char*)samp->data; + op.packetno ++; + + if (theora_kgs) { + if (samp->IsRAP) { + if (i) nb_i+=nb_p+1; + nb_p = 0; + } else { + nb_p++; + } + op.granulepos = nb_i; + op.granulepos <<= theora_kgs; + op.granulepos |= nb_p; + } else { + if (next_samp) op.granulepos = next_samp->DTS; + } + if (!next_samp) op.e_o_s = 1; + + ogg_stream_packetin(&os, &op); + + gf_isom_sample_del(&samp); + samp = next_samp; + next_samp = NULL; + gf_set_progress("OGG Export", i+1, count); + if (dumper->flags & GF_EXPORT_DO_ABORT) break; + + while (ogg_stream_pageout(&os, &og)>0) { + fwrite(og.header, 1, og.header_len, out); + fwrite(og.body, 1, og.body_len, out); + } + } + if (samp) gf_isom_sample_del(&samp); + + while (ogg_stream_flush(&os, &og)>0) { + fwrite(og.header, 1, og.header_len, out); + fwrite(og.body, 1, og.body_len, out); + } + ogg_stream_clear(&os); + fclose(out); + return GF_OK; +} + +GF_Err gf_export_hint(GF_MediaExporter *dumper) +{ + GF_Err e; + char szName[1000], szType[5]; + char *pck; + FILE *out; + u32 track, i, size, m_stype, sn, count; + + track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + m_stype = gf_isom_get_media_subtype(dumper->file, track, 1); + + e = gf_isom_reset_hint_reader(dumper->file, track, dumper->sample_num ? dumper->sample_num : 1, 0, 0, 0); + if (e) return gf_export_message(dumper, e, "Error initializing hint reader"); + + gf_export_message(dumper, GF_OK, "Extracting hint track samples - type %s", szType); + + count = gf_isom_get_sample_count(dumper->file, track); + if (dumper->sample_num) count = 0; + + i = 1; + while (1) { + e = gf_isom_next_hint_packet(dumper->file, track, &pck, &size, NULL, NULL, NULL, &sn); + if (e==GF_EOS) break; + if (dumper->sample_num && (dumper->sample_num != sn)) { + free(pck); + break; + } + if (e) return gf_export_message(dumper, e, "Error fetching hint packet %d", i); + sprintf(szName, "%s_pck_%04d.%s", dumper->out_name, i, gf_4cc_to_str(m_stype)); + out = fopen(szName, "wb"); + fwrite(pck, size, 1, out); + fclose(out); + free(pck); + i++; + if (count) gf_set_progress("Hint Export", sn, count); + } + if (count) gf_set_progress("Hint Export", count, count); + + return GF_OK; +} + +static void write_jp2_file(GF_BitStream *bs, char *data, u32 data_size, char *dsi, u32 dsi_size) +{ + gf_bs_write_u32(bs, 12); + gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ')); + gf_bs_write_u32(bs, 0x0D0A870A); + + gf_bs_write_u32(bs, 20); + gf_bs_write_u32(bs, GF_4CC('f','t','y','p')); + gf_bs_write_u32(bs, GF_4CC('j','p','2',' ')); + gf_bs_write_u32(bs, 0); + gf_bs_write_u32(bs, GF_4CC('j','p','2',' ')); + + gf_bs_write_data(bs, dsi, dsi_size); + gf_bs_write_data(bs, data, data_size); +} + +GF_Err gf_media_export_samples(GF_MediaExporter *dumper) +{ + GF_DecoderConfig *dcfg; + GF_GenericSampleDescription *udesc; + char szName[1000], szEXT[10], szNum[1000], *dsi; + FILE *out; + GF_BitStream *bs; + u32 track, i, di, count, m_type, m_stype, dsi_size, is_mj2k; + + track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + m_type = gf_isom_get_media_type(dumper->file, track); + m_stype = gf_isom_get_media_subtype(dumper->file, track, 1); + dsi_size = 0; + + if (dumper->sample_num) sprintf(szNum, " %d", dumper->sample_num); + else strcpy(szNum, "s"); + + is_mj2k = 0; + dsi = NULL; + udesc = NULL; + dcfg = NULL; + if ((m_stype==GF_ISOM_SUBTYPE_MPEG4) || (m_stype==GF_ISOM_SUBTYPE_MPEG4_CRYP)) + dcfg = gf_isom_get_decoder_config(dumper->file, track, 1); + + strcpy(szName, dumper->out_name ? dumper->out_name : ""); + if (dcfg) { + switch (dcfg->streamType) { + case GF_STREAM_VISUAL: + switch (dcfg->objectTypeIndication) { + case 0x20: + strcpy(szEXT, ".cmp"); + gf_export_message(dumper, GF_OK, "Dumping MPEG-4 Visual sample%s", szNum); + break; + case 0x21: + strcpy(szEXT, ".h264"); + gf_export_message(dumper, GF_OK, "Dumping MPEG-4 AVC-H264 Visual sample%s", szNum); + break; + case 0x6C: + strcpy(szEXT, ".jpg"); + gf_export_message(dumper, GF_OK, "Dumping JPEG image%s", szNum); + break; + case 0x6D: + strcpy(szEXT, ".png"); + gf_export_message(dumper, GF_OK, "Dumping PNG image%s", szNum); + break; + case 0x6E: + strcpy(szEXT, ".jp2"); + gf_export_message(dumper, GF_OK, "Dumping JPEG 2000 image%s", szNum); + break; + case GPAC_OTI_MEDIA_OGG: + strcpy(szEXT, ".theo"); + gf_export_message(dumper, GF_OK, "Dumping Theora video sample%s", szNum); + break; + default: + strcpy(szEXT, ".raw"); + gf_export_message(dumper, GF_OK, "Dumping Unknown video sample%s (OTI %d)", szNum, dcfg->objectTypeIndication); + break; + } + break; + case GF_STREAM_AUDIO: + switch (dcfg->objectTypeIndication) { + case 0x66: + case 0x67: + case 0x68: + case 0x40: + strcpy(szEXT, ".aac"); + gf_export_message(dumper, GF_OK, "Dumping MPEG-%d AAC sample%s", (dcfg->objectTypeIndication==0x40) ? 4 : 2, szNum); + break; + case 0x69: + case 0x6B: + strcpy(szEXT, ".mp3"); + gf_export_message(dumper, GF_OK, "Dumping MPEG-1/2 Audio (MP3) sample%s", szNum); + break; + case GPAC_OTI_MEDIA_OGG: + strcpy(szEXT, ".vorb"); + gf_export_message(dumper, GF_OK, "Dumping Vorbis audio sample%s", szNum); + break; + default: + strcpy(szEXT, ".raw"); + gf_export_message(dumper, GF_OK, "Dumping Unknown audio sample%s (OTI %d)", szNum, dcfg->objectTypeIndication); + break; + } + break; + case GF_STREAM_SCENE: + strcpy(szEXT, ".bifs"); gf_export_message(dumper, GF_OK, "Dumping BIFS sample%s", szNum); + break; + case GF_STREAM_OD: + strcpy(szEXT, ".od"); gf_export_message(dumper, GF_OK, "Dumping OD sample%s", szNum); + break; + case GF_STREAM_MPEGJ: + strcpy(szEXT, ".mpj"); gf_export_message(dumper, GF_OK, "Dumping MPEG-J sample%s", szNum); + break; + case GF_STREAM_OCI: + strcpy(szEXT, ".oci"); gf_export_message(dumper, GF_OK, "Dumping OCI sample%s", szNum); + break; + case GF_STREAM_MPEG7: + strcpy(szEXT, ".mp7"); gf_export_message(dumper, GF_OK, "Dumping MPEG7 sample%s", szNum); + break; + case GF_STREAM_IPMP: + strcpy(szEXT, ".ipmp"); gf_export_message(dumper, GF_OK, "Dumping IPMP sample%s", szNum); + break; + case GF_STREAM_TEXT: + strcpy(szEXT, ".tx3g"); gf_export_message(dumper, GF_OK, "Dumping 3GP Text sample%s", szNum); + break; + default: + gf_odf_desc_del((GF_Descriptor *) dcfg); + return gf_export_message(dumper, GF_NOT_SUPPORTED, "Cannot dump systems track ID %d sample%s - use NHNT", dumper->trackID, szNum); + } + gf_odf_desc_del((GF_Descriptor *) dcfg); + } else if ((m_stype==GF_ISOM_SUBTYPE_3GP_AMR) || (m_stype==GF_ISOM_SUBTYPE_3GP_AMR_WB)) { + strcpy(szEXT, ".amr"); + gf_export_message(dumper, GF_OK, "Extracting AMR Audio sample%s", szNum); + } else if (m_stype==GF_ISOM_SUBTYPE_3GP_H263) { + gf_export_message(dumper, GF_OK, "Extracting H263 Video sample%s", szNum); + strcpy(szEXT, ".263"); + } else if (m_stype==GF_ISOM_SUBTYPE_3GP_DIMS) { + gf_export_message(dumper, GF_OK, "Extracting DIMS sample%s", szNum); + strcpy(szEXT, ".dims"); + } else if (m_stype==GF_ISOM_SUBTYPE_AC3) { + gf_export_message(dumper, GF_OK, "Extracting AC3 sample%s", szNum); + strcpy(szEXT, ".ac3"); + } else if (m_stype==GF_ISOM_SUBTYPE_AVC_H264) { + strcpy(szEXT, ".h264"); + gf_export_message(dumper, GF_OK, "Dumping MPEG-4 AVC-H264 Visual sample%s", szNum); + } else if (m_type==GF_ISOM_MEDIA_FLASH) { + gf_export_message(dumper, GF_OK, "Extracting Macromedia Flash Movie sample%s", szNum); + strcpy(szEXT, ".swf"); + } else if (m_type==GF_ISOM_MEDIA_HINT) { + return gf_export_hint(dumper); + } else if (m_stype==GF_4CC('m','j','p','2')) { + strcpy(szEXT, ".jp2"); + gf_export_message(dumper, GF_OK, "Dumping JPEG 2000 sample%s", szNum); + udesc = gf_isom_get_generic_sample_description(dumper->file, track, 1); + dsi = udesc->extension_buf; + dsi_size = udesc->extension_buf_size; + free(udesc); + is_mj2k = 1; + } else { + strcpy(szEXT, "."); + strcat(szEXT, gf_4cc_to_str(m_stype)); + udesc = gf_isom_get_generic_sample_description(dumper->file, track, 1); + switch (m_type) { + case GF_ISOM_MEDIA_VISUAL: gf_export_message(dumper, GF_OK, "Extracting \'%s\' Video - Compressor %s", szEXT, udesc ? udesc->compressor_name: "Unknown"); break; + case GF_ISOM_MEDIA_AUDIO: gf_export_message(dumper, GF_OK, "Extracting \'%s\' Audio - Compressor %s", szEXT, udesc ? udesc->compressor_name : "Unknown"); break; + default: + gf_export_message(dumper, GF_OK, "Extracting \'%s\' Track (type '%s') - Compressor %s sample%s", szEXT, gf_4cc_to_str(m_type), udesc ? udesc->compressor_name : "Unknown", szNum); + break; + } + if (udesc->extension_buf) free(udesc->extension_buf); + if (udesc) free(udesc); + } + if (dumper->flags & GF_EXPORT_PROBE_ONLY) return GF_OK; + + if (dumper->sample_num) { + GF_ISOSample *samp = gf_isom_get_sample(dumper->file, track, dumper->sample_num, &di); + if (!samp) return GF_BAD_PARAM; + sprintf(szName, "%s_%d%s", dumper->out_name, dumper->sample_num, szEXT); + out = fopen(szName, "wb"); + bs = gf_bs_from_file(out, GF_BITSTREAM_WRITE); + if (is_mj2k) + write_jp2_file(bs, samp->data, samp->dataLength, dsi, dsi_size); + else + gf_bs_write_data(bs, samp->data, samp->dataLength); + gf_isom_sample_del(&samp); + gf_bs_del(bs); + fclose(out); + if (dsi) free(dsi); + return GF_OK; + } + + count = gf_isom_get_sample_count(dumper->file, track); + for (i=0; ifile, track, i+1, &di); + if (!samp) break; + + if (count>=1000) { + sprintf(szName, "%s_%08d%s", dumper->out_name, i+1, szEXT); + } else { + sprintf(szName, "%s_%03d%s", dumper->out_name, i+1, szEXT); + } + out = fopen(szName, "wb"); + bs = gf_bs_from_file(out, GF_BITSTREAM_WRITE); + if (dsi) gf_bs_write_data(bs, dsi, dsi_size); + if (is_mj2k) + write_jp2_file(bs, samp->data, samp->dataLength, dsi, dsi_size); + else + gf_bs_write_data(bs, samp->data, samp->dataLength); + gf_isom_sample_del(&samp); + gf_set_progress("Media Export", i+1, count); + gf_bs_del(bs); + fclose(out); + if (dumper->flags & GF_EXPORT_DO_ABORT) break; + } + if (dsi) free(dsi); + return GF_OK; +} + +static GF_Err gf_dump_to_vobsub(GF_MediaExporter *dumper, char *szName, u32 track, char *dsi, u32 dsiSize) +{ + FILE *fidx, *fsub; + u32 width, height, i, count, di; + GF_ISOSample *samp; + char lang[] = "\0\0\0\0"; + + /* Check decoder specific information (palette) size - should be 64 */ + if (dsiSize != 64) { + return gf_export_message(dumper, GF_CORRUPTED_DATA, "Invalid decoder specific info size - must be 64 but is %d", dsiSize); + } + + /* Create an idx file */ + fidx = gf_f64_open(szName, "w"); + if (!fidx) { + return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName); + } + + /* Create a sub file */ + vobsub_trim_ext(szName); + szName = strcat(szName, ".sub"); + fsub = gf_f64_open(szName, "wb"); + if (!fsub) { + fclose(fidx); + return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName); + } + + /* Retrieve original subpicture resolution */ + gf_isom_get_track_layout_info(dumper->file, track, &width, &height, NULL, NULL, NULL); + + /* Write header */ + fputs("# VobSub index file, v7 (do not modify this line!)\n#\n", fidx); + + /* Write original subpicture resolution */ + fprintf(fidx, "size: %ux%u\n", width, height); + + /* Write palette */ + fputs("palette:", fidx); + for (i = 0; i < 16; i++) { + s32 y, u, v, r, g, b; + + y = (s32)(u8)dsi[(i<<2)+1] - 0x10; + u = (s32)(u8)dsi[(i<<2)+3] - 0x80; + v = (s32)(u8)dsi[(i<<2)+2] - 0x80; + r = (298 * y + 409 * v + 128) >> 8; + g = (298 * y - 100 * u - 208 * v + 128) >> 8; + b = (298 * y + 516 * u + 128) >> 8; + + if (i) fputc(',', fidx); + +#define CLIP(x) (((x) >= 0) ? (((x) < 256) ? (x) : 255) : 0) + fprintf(fidx, " %02x%02x%02x", CLIP(r), CLIP(g), CLIP(b)); +#undef CLIP + } + fputc('\n', fidx); + + /* Write some other useful values */ + fputs("# ON: displays only forced subtitles, OFF: shows everything\n", fidx); + fputs("forced subs: OFF\n\n", fidx); + + /* Write current language index */ + fputs("# Language index in use\nlangidx: 0\n", fidx); + + /* Write language header */ + gf_isom_get_media_language(dumper->file, track, lang); + fprintf(fidx, "id: %s, index: 0\n", vobsub_lang_id(lang)); + + /* Retrieve sample count */ + count = gf_isom_get_sample_count(dumper->file, track); + + /* Process samples (skip first - because it is special) */ + for (i = 2; i <= count; i++) + { + u64 dts; + u32 hh, mm, ss, ms; + + samp = gf_isom_get_sample(dumper->file, track, i, &di); + if (!samp) { + break; + } + + dts = samp->DTS / 90; + ms = (u32)(dts % 1000); + dts = dts / 1000; + ss = (u32)(dts % 60); + dts = dts / 60; + mm = (u32)(dts % 60); + hh = (u32)(dts / 60); +#if defined(WIN32) && !defined(__GNUC__) + fprintf(fidx, "timestamp: %02u:%02u:%02u:%03u, filepos: %09lx\n", hh, mm, ss, ms, gf_f64_tell(fsub)); +#else + fprintf(fidx, "timestamp: %02u:%02u:%02u:%03u, filepos: %09llx\n", hh, mm, ss, ms, gf_f64_tell(fsub)); +#endif + if (vobsub_packetize_subpicture(fsub, samp->DTS, samp->data, samp->dataLength) != GF_OK) { + gf_isom_sample_del(&samp); + fclose(fsub); + fclose(fidx); + return gf_export_message(dumper, GF_IO_ERR, "Unable packetize subpicture into file %s\n", szName); + } + + gf_isom_sample_del(&samp); + gf_set_progress("VobSub Export", i + 1, count); + + if (dumper->flags & GF_EXPORT_DO_ABORT) { + break; + } + } + + /* Delete sample if any */ + if (samp) { + gf_isom_sample_del(&samp); + } + + fclose(fsub); + fclose(fidx); + + return GF_OK; +} + +/*QCP codec GUIDs*/ +static const char *QCP_QCELP_GUID_1 = "\x41\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E"; +static const char *QCP_EVRC_GUID = "\x8D\xD4\x89\xE6\x76\x90\xB5\x46\x91\xEF\x73\x6A\x51\x00\xCE\xB4"; +static const char *QCP_SMV_GUID = "\x75\x2B\x7C\x8D\x97\xA7\x46\xED\x98\x5E\xD5\x3C\x8C\xC7\x5F\x84"; + + +GF_Err gf_media_export_native(GF_MediaExporter *dumper) +{ + GF_DecoderConfig *dcfg; + GF_GenericSampleDescription *udesc; + char szName[1000], szEXT[5], GUID[16]; + FILE *out; + u32 *qcp_rates; + GF_AVCConfig *avccfg; + GF_M4ADecSpecInfo a_cfg; + GF_BitStream *bs; + u32 track, i, di, count, m_type, m_stype, dsi_size, qcp_type; + Bool is_ogg, has_qcp_pad, is_vobsub; + u32 aac_type, is_aac; + char *dsi; + QCPRateTable rtable[8]; + u32 rt_cnt; + + dsi_size = 0; + dsi = NULL; + avccfg = NULL; + track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + m_type = gf_isom_get_media_type(dumper->file, track); + m_stype = gf_isom_get_media_subtype(dumper->file, track, 1); + has_qcp_pad = 0; + + is_aac = aac_type = 0; + qcp_type = 0; + is_ogg = 0; + is_vobsub = 0; + udesc = NULL; + dcfg = NULL; + if ((m_stype==GF_ISOM_SUBTYPE_MPEG4) || (m_stype==GF_ISOM_SUBTYPE_MPEG4_CRYP)) + dcfg = gf_isom_get_decoder_config(dumper->file, track, 1); + + strcpy(szName, dumper->out_name ? dumper->out_name : ""); + if (dcfg) { + switch (dcfg->streamType) { + case GF_STREAM_VISUAL: + switch (dcfg->objectTypeIndication) { + case 0x20: + dsi = dcfg->decoderSpecificInfo->data; + dcfg->decoderSpecificInfo->data = NULL; + dsi_size = dcfg->decoderSpecificInfo->dataLength; + strcat(szName, ".cmp"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-4 Visual stream to cmp"); + break; + case 0x21: + avccfg = gf_isom_avc_config_get(dumper->file, track, 1); + strcat(szName, ".h264"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-4 AVC-H264 stream to h264"); + break; + case 0x6A: + strcat(szName, ".m1v"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-1 Visual stream to m1v"); + break; + case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: + strcat(szName, ".m2v"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-2 Visual stream to m2v"); + break; + case 0x6C: + strcat(szName, ".jpg"); + gf_export_message(dumper, GF_OK, "Extracting JPEG image"); + break; + case 0x6D: + strcat(szName, ".png"); + gf_export_message(dumper, GF_OK, "Extracting PNG image"); + break; + case GPAC_OTI_MEDIA_OGG: + strcat(szName, ".ogg"); + gf_export_message(dumper, GF_OK, "Extracting Ogg video"); + is_ogg = 1; + break; + default: + gf_odf_desc_del((GF_Descriptor *) dcfg); + return gf_export_message(dumper, GF_NOT_SUPPORTED, "Unknown media in track ID %d - use NHNT instead", dumper->trackID); + } + break; + case GF_STREAM_AUDIO: + switch (dcfg->objectTypeIndication) { + case 0x66: + case 0x67: + case 0x68: + dsi = dcfg->decoderSpecificInfo->data; + dcfg->decoderSpecificInfo->data = NULL; + dsi_size = dcfg->decoderSpecificInfo->dataLength; + strcat(szName, ".aac"); + is_aac = 1; + aac_type = dcfg->objectTypeIndication - 0x66; + gf_export_message(dumper, GF_OK, "Extracting MPEG-2 AAC"); + break; + case 0x40: + dsi = dcfg->decoderSpecificInfo->data; + dcfg->decoderSpecificInfo->data = NULL; + dsi_size = dcfg->decoderSpecificInfo->dataLength; + is_aac = 2; + strcat(szName, ".aac"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-4 AAC"); + break; + case 0x69: + case 0x6B: + strcat(szName, ".mp3"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-1/2 Audio (MP3)"); + break; + case GPAC_OTI_MEDIA_OGG: + strcat(szName, ".ogg"); + is_ogg = 1; + gf_export_message(dumper, GF_OK, "Extracting Ogg audio"); + break; + case 0xE1: + strcat(szName, ".qcp"); qcp_type = 1; + memcpy(GUID, QCP_QCELP_GUID_1, sizeof(char)*16); + gf_export_message(dumper, GF_OK, "Extracting QCELP-13K (QCP file)"); + break; + case 0xA0: + memcpy(GUID, QCP_EVRC_GUID, sizeof(char)*16); + qcp_type = 3; + if (dumper->flags & GF_EXPORT_PROBE_ONLY) dumper->flags |= GF_EXPORT_USE_QCP; + break; + case 0xA1: + qcp_type = 2; + memcpy(GUID, QCP_SMV_GUID, sizeof(char)*16); + if (dumper->flags & GF_EXPORT_PROBE_ONLY) dumper->flags |= GF_EXPORT_USE_QCP; + break; + case 0xD1: + if (dcfg->decoderSpecificInfo && (dcfg->decoderSpecificInfo->dataLength==8) + && !strnicmp(dcfg->decoderSpecificInfo->data, "pvmm", 4)) { + qcp_type = 3; + memcpy(GUID, QCP_EVRC_GUID, sizeof(char)*16); + if (dumper->flags & GF_EXPORT_PROBE_ONLY) dumper->flags |= GF_EXPORT_USE_QCP; + break; + } + /*fall through*/ + default: + gf_odf_desc_del((GF_Descriptor *) dcfg); + return gf_export_message(dumper, GF_NOT_SUPPORTED, "Unknown audio in track ID %d - use NHNT", dumper->trackID); + } + break; + case GF_STREAM_ND_SUBPIC: + switch (dcfg->objectTypeIndication) + { + case 0xe0: + is_vobsub = 1; + dsi = dcfg->decoderSpecificInfo->data; + dcfg->decoderSpecificInfo->data = NULL; + dsi_size = dcfg->decoderSpecificInfo->dataLength; + strcat(szName, ".idx"); + gf_export_message(dumper, GF_OK, "Extracting NeroDigital VobSub subpicture stream"); + break; + default: + gf_odf_desc_del((GF_Descriptor *) dcfg); + return gf_export_message(dumper, GF_NOT_SUPPORTED, "Unknown subpicture stream in track ID %d - use NHNT", dumper->trackID); + } + break; + default: + gf_odf_desc_del((GF_Descriptor *) dcfg); + return gf_export_message(dumper, GF_NOT_SUPPORTED, "Cannot extract systems track ID %d in raw format - use NHNT", dumper->trackID); + } + gf_odf_desc_del((GF_Descriptor *) dcfg); + } else { + if (m_stype==GF_ISOM_SUBTYPE_3GP_AMR) { + strcat(szName, ".amr"); + gf_export_message(dumper, GF_OK, "Extracting AMR Audio"); + } else if (m_stype==GF_ISOM_SUBTYPE_3GP_AMR_WB) { + strcat(szName, ".awb"); + gf_export_message(dumper, GF_OK, "Extracting AMR WideBand Audio"); + } else if (m_stype==GF_ISOM_SUBTYPE_3GP_QCELP) { + strcat(szName, ".qcp"); qcp_type = 1; + memcpy(GUID, QCP_QCELP_GUID_1, sizeof(char)*16); + gf_export_message(dumper, GF_OK, "Extracting QCELP-13K (QCP file)"); + } else if (m_stype==GF_ISOM_SUBTYPE_3GP_EVRC) { + qcp_type = 3; + memcpy(GUID, QCP_EVRC_GUID, sizeof(char)*16); + if (dumper->flags & GF_EXPORT_PROBE_ONLY) dumper->flags |= GF_EXPORT_USE_QCP; + } else if (m_stype==GF_ISOM_SUBTYPE_3GP_SMV) { + qcp_type = 2; + memcpy(GUID, QCP_SMV_GUID, sizeof(char)*16); + if (dumper->flags & GF_EXPORT_PROBE_ONLY) dumper->flags |= GF_EXPORT_USE_QCP; + } else if (m_stype==GF_ISOM_SUBTYPE_3GP_H263) { + gf_export_message(dumper, GF_OK, "Extracting H263 Video"); + strcat(szName, ".263"); + } else if (m_stype==GF_ISOM_SUBTYPE_3GP_DIMS) { + return gf_media_export_nhml(dumper, 1); + } else if (m_stype==GF_ISOM_SUBTYPE_AVC_H264) { + avccfg = gf_isom_avc_config_get(dumper->file, track, 1); + strcat(szName, ".h264"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-4 AVC-H264 stream to h264"); + } else if (m_type==GF_ISOM_MEDIA_FLASH) { + gf_export_message(dumper, GF_OK, "Extracting Macromedia Flash Movie"); + strcat(szName, ".swf"); + } else if (m_stype==GF_ISOM_SUBTYPE_AC3) { + gf_export_message(dumper, GF_OK, "Extracting AC3 Audio"); + strcat(szName, ".ac3"); + } else { + strcat(szName, "."); + strcat(szName, gf_4cc_to_str(m_stype)); + udesc = gf_isom_get_generic_sample_description(dumper->file, track, 1); + if (udesc) { + dsi = udesc->extension_buf; udesc->extension_buf = NULL; + dsi_size = udesc->extension_buf_size; + } + switch (m_type) { + case GF_ISOM_MEDIA_VISUAL: gf_export_message(dumper, GF_OK, "Extracting \'%s\' Video - Compressor %s", szEXT, udesc ? udesc->compressor_name : "Unknown"); break; + case GF_ISOM_MEDIA_AUDIO: gf_export_message(dumper, GF_OK, "Extracting \'%s\' Audio - Compressor %s", szEXT, udesc ? udesc->compressor_name : "Unknown"); break; + default: + gf_export_message(dumper, GF_OK, "Extracting \'%s\' Track (type '%s') - Compressor %s", szEXT, gf_4cc_to_str(m_type), udesc ? udesc->compressor_name : "Unknown"); + break; + } + if (udesc) free(udesc); + } + } + if (dumper->flags & GF_EXPORT_PROBE_ONLY) { + if (dsi) free(dsi); + if (avccfg) gf_odf_avc_cfg_del(avccfg); + return GF_OK; + } + + if (is_ogg) return gf_dump_to_ogg(dumper, szName, track); + + if (is_vobsub) return gf_dump_to_vobsub(dumper, szName, track, dsi, dsi_size); + + if (qcp_type>1) { + if (dumper->flags & GF_EXPORT_USE_QCP) { + strcat(szName, ".qcp"); + gf_export_message(dumper, GF_OK, "Extracting %s audio (QCP file)", (qcp_type==2) ? "SMV" : "EVRC"); + } else if (qcp_type==2) { + strcat(szName, ".smv"); + gf_export_message(dumper, GF_OK, "Extracting SMV audio"); + } else { + strcat(szName, ".evc"); + gf_export_message(dumper, GF_OK, "Extracting EVRC audio"); + } + } + + if (dumper->out_name && (dumper->flags & GF_EXPORT_MERGE)) { + out = gf_f64_open(dumper->out_name, "a+b"); + if (out) gf_f64_seek(out, 0, SEEK_END); + } else { + out = fopen(szName, "wb"); + } + if (!out) { + if (dsi) free(dsi); + if (avccfg) gf_odf_avc_cfg_del(avccfg); + return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName); + } + bs = gf_bs_from_file(out, GF_BITSTREAM_WRITE); + + if (is_aac) { + gf_m4a_get_config(dsi, dsi_size, &a_cfg); + if (is_aac==2) aac_type = a_cfg.base_object_type - 1; + free(dsi); + dsi = NULL; + } + if (dsi) { + gf_bs_write_data(bs, dsi, dsi_size); + free(dsi); + } + if (avccfg) { + count = gf_list_count(avccfg->sequenceParameterSets); + for (i=0;isequenceParameterSets, i); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, sl->data, sl->size); + } + count = gf_list_count(avccfg->pictureParameterSets); + for (i=0;ipictureParameterSets, i); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, sl->data, sl->size); + } + } + + qcp_rates = NULL; + count = gf_isom_get_sample_count(dumper->file, track); + rt_cnt = 0; + + if (m_stype==GF_ISOM_SUBTYPE_3GP_AMR) gf_bs_write_data(bs, "#!AMR\n", 6); + else if (m_stype==GF_ISOM_SUBTYPE_3GP_AMR_WB) gf_bs_write_data(bs, "#!AMR-WB\n", 9); + else if ((qcp_type>1) && !(dumper->flags & GF_EXPORT_USE_QCP)) { + if (qcp_type==2) gf_bs_write_data(bs, "#!SMV\n", 6); + else gf_bs_write_data(bs, "#!EVRC\n", 7); + qcp_type = 0; + } + + /*QCP formatting*/ + if (qcp_type) { + GF_ISOSample *samp; + Bool needs_rate_octet; + u32 tot_size, data_size, sample_size, avg_rate, agg_samp; + u32 block_size = 160; + u32 sample_rate = 8000; + GF_3GPConfig *gpc = gf_isom_3gp_config_get(dumper->file, track, 1); + sample_size = gf_isom_get_constant_sample_size(dumper->file, track); + agg_samp = 1; + if (gpc) { + agg_samp = gpc->frames_per_sample; + free(gpc); + } + + if (qcp_type==1) { + qcp_rates = (u32 *)GF_QCELP_RATE_TO_SIZE; + rt_cnt = GF_QCELP_RATE_TO_SIZE_NB; + } else { + qcp_rates = (u32 *)GF_SMV_EVRC_RATE_TO_SIZE; + rt_cnt = GF_SMV_EVRC_RATE_TO_SIZE_NB; + } + + /*if cst size and no aggregation, skip table*/ + if (sample_size && !agg_samp) { + data_size = sample_size*count; + } else { + /*dumps full table...*/ + for (i=0; ifile, track); + } + + /*check sample format - packetvideo doesn't include rate octet...*/ + needs_rate_octet = 1; + samp = gf_isom_get_sample_info(dumper->file, track, 1, NULL, NULL); + for (i=0; idataLength) { + needs_rate_octet = 0; + break; + } + } + gf_isom_sample_del(&samp); + if (needs_rate_octet) data_size += count; + has_qcp_pad = (data_size % 2) ? 1 : 0; + + avg_rate = 8*data_size*sample_rate/count/block_size; + /*QLCM + fmt + vrat + data*/ + tot_size = 4+ 8+150 + 8+8 + 8 + data_size; + /*pad is included in riff size*/ + if (has_qcp_pad) tot_size++; + + gf_bs_write_data(bs, "RIFF", 4); + gf_bs_write_u32_le(bs, tot_size); + gf_bs_write_data(bs, "QLCM", 4); + gf_bs_write_data(bs, "fmt ", 4); + gf_bs_write_u32_le(bs, 150);/*fmt chunk size*/ + gf_bs_write_u8(bs, 1); + gf_bs_write_u8(bs, 0); + gf_bs_write_data(bs, GUID, 16); + gf_bs_write_u16_le(bs, 1); + memset(szName, 0, 80); + strcpy(szName, (qcp_type==1) ? "QCELP-GPACExport" : ((qcp_type==2) ? "SMV-GPACExport" : "EVRC-GPACExport")); + gf_bs_write_data(bs, szName, 80); + gf_bs_write_u16_le(bs, avg_rate); + gf_bs_write_u16_le(bs, sample_size); + gf_bs_write_u16_le(bs, block_size); + gf_bs_write_u16_le(bs, sample_rate); + gf_bs_write_u16_le(bs, 16); + gf_bs_write_u32_le(bs, rt_cnt); + for (i=0; i<8; i++) { + if (ifile, track, i+1, &di); + if (!samp) break; + /*AVC sample to NALU*/ + if (avccfg) { + u32 j, nal_size, remain; + char *ptr = samp->data; + remain = samp->dataLength; + while (remain) { + nal_size = 0; + for (j=0; jnal_unit_size; j++) { + nal_size |= ((u8) *ptr); + if (j+1nal_unit_size) nal_size<<=8; + remain--; + ptr++; + } + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, ptr, nal_size); + ptr += nal_size; + remain -= nal_size; + } + } + /*adts frame header*/ + else if (is_aac) { + gf_bs_write_int(bs, 0xFFF, 12);/*sync*/ + gf_bs_write_int(bs, (is_aac==1) ? 1 : 0, 1);/*mpeg2 aac*/ + gf_bs_write_int(bs, 0, 2); /*layer*/ + gf_bs_write_int(bs, 1, 1); /* protection_absent*/ + gf_bs_write_int(bs, aac_type, 2); + gf_bs_write_int(bs, a_cfg.base_sr_index, 4); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, a_cfg.nb_chan, 3); + gf_bs_write_int(bs, 0, 4); + gf_bs_write_int(bs, 7+samp->dataLength, 13); + gf_bs_write_int(bs, 0x7FF, 11); + gf_bs_write_int(bs, 0, 2); + } + /*fix rate octet for QCP*/ + else if (qcp_type) { + u32 j; + for (j=0; jdataLength) { + gf_bs_write_u8(bs, qcp_rates[2*j]); + break; + } + } + } + if (!avccfg) gf_bs_write_data(bs, samp->data, samp->dataLength); + gf_isom_sample_del(&samp); + gf_set_progress("Media Export", i+1, count); + if (dumper->flags & GF_EXPORT_DO_ABORT) break; + } + if (has_qcp_pad) gf_bs_write_u8(bs, 0); + + if (avccfg) gf_odf_avc_cfg_del(avccfg); + gf_bs_del(bs); + fclose(out); + return GF_OK; +} + +GF_Err gf_media_export_avi_track(GF_MediaExporter *dumper) +{ + GF_Err e; + u32 max_size, tot_size, num_samples, i; + s32 size; + char *comp, *frame; + char szOutFile[1024]; + avi_t *in; + FILE *fout; + + in = AVI_open_input_file(dumper->in_name, 1); + if (!in) return gf_export_message(dumper, GF_URL_ERROR, "Unsupported avi file"); + fout = NULL; + + e = GF_OK; + if (dumper->trackID==1) { + Bool key; + comp = AVI_video_compressor(in); + if (!stricmp(comp, "DIVX") || !stricmp(comp, "DX50") /*DivX*/ + || !stricmp(comp, "XVID") /*XviD*/ + || !stricmp(comp, "3iv2") /*3ivX*/ + || !stricmp(comp, "fvfw") /*ffmpeg*/ + || !stricmp(comp, "NDIG") /*nero*/ + || !stricmp(comp, "MP4V") /*!! not tested*/ + || !stricmp(comp, "M4CC") /*Divio - not tested*/ + || !stricmp(comp, "PVMM") /*PacketVideo - not tested*/ + || !stricmp(comp, "SEDG") /*Samsung - not tested*/ + || !stricmp(comp, "RMP4") /*Sigma - not tested*/ + ) { + sprintf(szOutFile, "%s.cmp", dumper->out_name); + } else if (!stricmp(comp, "VSSH") || strstr(comp, "264")) { + sprintf(szOutFile, "%s.h264", dumper->out_name); + } else { + sprintf(szOutFile, "%s.%s", dumper->out_name, comp); + } + gf_export_message(dumper, GF_OK, "Extracting AVI video (format %s) to %s", comp, szOutFile); + + fout = fopen(szOutFile, "wb"); + + max_size = 0; + frame = NULL; + num_samples = AVI_video_frames(in); + for (i=0; i max_size) { + frame = (char*)realloc(frame, sizeof(char) * size); + max_size = size; + } + AVI_read_frame(in, frame, (int*)&key); + if ((u32) size>4) fwrite(frame, 1, size, fout); + gf_set_progress("AVI Extract", i+1, num_samples); + } + free(frame); + fclose(fout); + fout = NULL; + goto exit; + } + i = 0; + tot_size = max_size = 0; + while ((size = AVI_audio_size(in, i) )>0) { + if (max_size<(u32) size) max_size=size; + tot_size += size; + i++; + } + frame = (char*)malloc(sizeof(char) * max_size); + AVI_seek_start(in); + AVI_set_audio_position(in, 0); + + switch (in->track[in->aptr].a_fmt) { + case WAVE_FORMAT_PCM: comp = "pcm"; break; + case WAVE_FORMAT_ADPCM: comp = "adpcm"; break; + case WAVE_FORMAT_IBM_CVSD: comp = "cvsd"; break; + case WAVE_FORMAT_ALAW: comp = "alaw"; break; + case WAVE_FORMAT_MULAW: comp = "mulaw"; break; + case WAVE_FORMAT_OKI_ADPCM: comp = "oki_adpcm"; break; + case WAVE_FORMAT_DVI_ADPCM: comp = "dvi_adpcm"; break; + case WAVE_FORMAT_DIGISTD: comp = "digistd"; break; + case WAVE_FORMAT_YAMAHA_ADPCM: comp = "yam_adpcm"; break; + case WAVE_FORMAT_DSP_TRUESPEECH: comp = "truespeech"; break; + case WAVE_FORMAT_GSM610: comp = "gsm610"; break; + case IBM_FORMAT_MULAW: comp = "ibm_mulaw"; break; + case IBM_FORMAT_ALAW: comp = "ibm_alaw"; break; + case IBM_FORMAT_ADPCM: comp = "ibm_adpcm"; break; + case 0x55: comp = "mp3"; break; + default: comp = "raw"; break; + } + sprintf(szOutFile, "%s.%s", dumper->out_name, comp); + gf_export_message(dumper, GF_OK, "Extracting AVI %s audio", comp); + fout = fopen(szOutFile, "wb"); + num_samples = 0; + while (1) { + Bool continuous; + size = AVI_read_audio(in, frame, max_size, (int*)&continuous); + if (!size) break; + num_samples += size; + fwrite(frame, 1, size, fout); + gf_set_progress("AVI Extract", num_samples, tot_size); + + } + + +exit: + if (fout) fclose(fout); + AVI_close(in); + return e; +} + +GF_Err gf_media_export_nhnt(GF_MediaExporter *dumper) +{ + GF_ESD *esd; + char szName[1000]; + FILE *out_med, *out_inf, *out_nhnt; + GF_BitStream *bs; + Bool has_b_frames; + u32 track, i, di, count, pos; + + track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + esd = gf_isom_get_esd(dumper->file, track, 1); + if (!esd) return gf_export_message(dumper, GF_NON_COMPLIANT_BITSTREAM, "Invalid MPEG-4 stream in track ID %d", dumper->trackID); + + if (dumper->flags & GF_EXPORT_PROBE_ONLY) { + gf_odf_desc_del((GF_Descriptor *) esd); + return GF_OK; + } + + sprintf(szName, "%s.media", dumper->out_name); + out_med = gf_f64_open(szName, "wb"); + if (!out_med) { + gf_odf_desc_del((GF_Descriptor *) esd); + return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName); + } + + sprintf(szName, "%s.nhnt", dumper->out_name); + out_nhnt = fopen(szName, "wb"); + if (!out_nhnt) { + fclose(out_med); + gf_odf_desc_del((GF_Descriptor *) esd); + return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName); + } + + + bs = gf_bs_from_file(out_nhnt, GF_BITSTREAM_WRITE); + + if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { + sprintf(szName, "%s.info", dumper->out_name); + out_inf = fopen(szName, "wb"); + if (out_inf) fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, 1, out_inf); + fclose(out_inf); + } + + /*write header*/ + /*'NHnt' format*/ + gf_bs_write_data(bs, "NHnt", 4); + /*version 1*/ + gf_bs_write_u8(bs, 0); + /*streamType*/ + gf_bs_write_u8(bs, esd->decoderConfig->streamType); + /*OTI*/ + gf_bs_write_u8(bs, esd->decoderConfig->objectTypeIndication); + /*reserved*/ + gf_bs_write_u16(bs, 0); + /*bufferDB*/ + gf_bs_write_u24(bs, esd->decoderConfig->bufferSizeDB); + /*avg BitRate*/ + gf_bs_write_u32(bs, esd->decoderConfig->avgBitrate); + /*max bitrate*/ + gf_bs_write_u32(bs, esd->decoderConfig->maxBitrate); + /*timescale*/ + gf_bs_write_u32(bs, esd->slConfig->timestampResolution); + + gf_odf_desc_del((GF_Descriptor *) esd); + + has_b_frames = gf_isom_has_time_offset(dumper->file, track); + + pos = 0; + count = gf_isom_get_sample_count(dumper->file, track); + for (i=0; ifile, track, i+1, &di); + if (!samp) break; + fwrite(samp->data, samp->dataLength, 1, out_med); + + /*dump nhnt info*/ + gf_bs_write_u24(bs, samp->dataLength); + gf_bs_write_int(bs, samp->IsRAP, 1); + /*AU start & end flag always true*/ + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, 1, 1); + /*reserved*/ + gf_bs_write_int(bs, 0, 3); + /*type - try to guess it*/ + if (has_b_frames) { + if (samp->IsRAP) gf_bs_write_int(bs, 0, 2); + /*if CTS offset, assime P*/ + else if (samp->CTS_Offset) gf_bs_write_int(bs, 1, 2); + else gf_bs_write_int(bs, 2, 2); + } else { + gf_bs_write_int(bs, samp->IsRAP ? 0 : 1, 2); + } + gf_bs_write_u32(bs, pos); + /*TODO support for large files*/ + gf_bs_write_u32(bs, (u32) (samp->DTS + samp->CTS_Offset) ); + gf_bs_write_u32(bs, (u32) samp->DTS); + + pos += samp->dataLength; + gf_isom_sample_del(&samp); + gf_set_progress("NHNT Export", i+1, count); + if (dumper->flags & GF_EXPORT_DO_ABORT) break; + } + fclose(out_med); + gf_bs_del(bs); + fclose(out_nhnt); + return GF_OK; +} + +static GF_Err MP4T_CopyTrack(GF_MediaExporter *dumper, GF_ISOFile *infile, u32 inTrackNum, GF_ISOFile *outfile, Bool ResetDependancies, Bool AddToIOD) +{ + GF_ESD *esd; + GF_InitialObjectDescriptor *iod; + u32 TrackID, newTk, descIndex, i, ts, rate, pos, di, count, msubtype; + u64 dur; + GF_ISOSample *samp; + + if (!inTrackNum) { + if (gf_isom_get_track_count(infile) != 1) return gf_export_message(dumper, GF_BAD_PARAM, "Please specify trackID to export"); + inTrackNum = 1; + } + //check the ID is available + TrackID = gf_isom_get_track_id(infile, inTrackNum); + newTk = gf_isom_get_track_by_id(outfile, TrackID); + if (newTk) TrackID = 0; + + //get the ESD and remove dependancies + esd = NULL; + msubtype = gf_isom_get_media_subtype(infile, inTrackNum, 1); + + if (msubtype == GF_ISOM_SUBTYPE_MPEG4) { + esd = gf_isom_get_esd(infile, inTrackNum, 1); + if (esd && ResetDependancies) { + esd->dependsOnESID = 0; + esd->OCRESID = 0; + } + } + + newTk = gf_isom_new_track(outfile, TrackID, gf_isom_get_media_type(infile, inTrackNum), gf_isom_get_media_timescale(infile, inTrackNum)); + gf_isom_set_track_enabled(outfile, newTk, 1); + + if (esd) { + gf_isom_new_mpeg4_description(outfile, newTk, esd, NULL, NULL, &descIndex); + if ((esd->decoderConfig->streamType == GF_STREAM_VISUAL) || (esd->decoderConfig->streamType == GF_STREAM_SCENE)) { + u32 w, h; + gf_isom_get_visual_info(infile, inTrackNum, 1, &w, &h); + /*this is because so many files have reserved values of 320x240 from v1 ... */ + if ((esd->decoderConfig->objectTypeIndication == 0x20) ) { + GF_M4VDecSpecInfo dsi; + gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + w = dsi.width; + h = dsi.height; + } + gf_isom_set_visual_info(outfile, newTk, 1, w, h); + } + else if ((esd->decoderConfig->streamType == GF_STREAM_ND_SUBPIC) && (esd->decoderConfig->objectTypeIndication == 0xe0)) { + u32 w, h; + s32 trans_x, trans_y; + s16 layer; + gf_isom_get_track_layout_info(infile, inTrackNum, &w, &h, &trans_x, &trans_y, &layer); + gf_isom_set_track_layout_info(outfile, newTk, w << 16, h << 16, trans_x, trans_y, layer); + } + esd->decoderConfig->avgBitrate = 0; + esd->decoderConfig->maxBitrate = 0; + } else { + gf_isom_clone_sample_description(outfile, newTk, infile, inTrackNum, 1, NULL, NULL, &descIndex); + } + + pos = 0; + rate = 0; + ts = gf_isom_get_media_timescale(infile, inTrackNum); + count = gf_isom_get_sample_count(infile, inTrackNum); + for (i=0; idataLength; + esd->decoderConfig->avgBitrate += samp->dataLength; + if (esd->decoderConfig->bufferSizeDBdataLength) esd->decoderConfig->bufferSizeDB = samp->dataLength; + if (samp->DTS - pos > ts) { + if (esd->decoderConfig->maxBitratedecoderConfig->maxBitrate = rate; + rate = 0; + pos = 0; + } + } + gf_isom_sample_del(&samp); + gf_set_progress("ISO File Export", i, count); + } + gf_set_progress("ISO File Export", count, count); + + if (msubtype == GF_ISOM_SUBTYPE_MPEG4_CRYP) { + esd = gf_isom_get_esd(infile, inTrackNum, 1); + } else if (msubtype == GF_ISOM_SUBTYPE_AVC_H264) { + return gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, 0x0F); + } + /*likely 3gp or any non-MPEG-4 isomedia file*/ + else if (!esd) return gf_isom_remove_root_od(outfile); + + dur = gf_isom_get_media_duration(outfile, newTk); + if (!dur) dur = ts; + esd->decoderConfig->maxBitrate *= 8; + esd->decoderConfig->avgBitrate = (u32) (esd->decoderConfig->avgBitrate * 8 * ts / dur); + gf_isom_change_mpeg4_description(outfile, newTk, 1, esd); + + + iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(infile); + switch (esd->decoderConfig->streamType) { + case GF_STREAM_SCENE: + if (iod && (iod->tag==GF_ODF_IOD_TAG)) { + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_SCENE, iod->scene_profileAndLevel); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_GRAPHICS, iod->graphics_profileAndLevel); + } else if (esd->decoderConfig->objectTypeIndication==0x20) { + gf_export_message(dumper, GF_OK, "Warning: Scene PLs not found in original MP4 - defaulting to No Profile Specified"); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_SCENE, 0xFE); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_GRAPHICS, 0xFE); + } + break; + case GF_STREAM_VISUAL: + if (iod && (iod->tag==GF_ODF_IOD_TAG)) { + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, iod->visual_profileAndLevel); + } else if (esd->decoderConfig->objectTypeIndication==0x20) { + GF_M4VDecSpecInfo dsi; + gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, dsi.VideoPL); + } else { + gf_export_message(dumper, GF_OK, "Warning: Visual PLs not found in original MP4 - defaulting to No Profile Specified"); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, 0xFE); + } + break; + case GF_STREAM_AUDIO: + if (iod && (iod->tag==GF_ODF_IOD_TAG)) { + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_AUDIO, iod->audio_profileAndLevel); + } else if (esd->decoderConfig->objectTypeIndication==0x40) { + GF_M4ADecSpecInfo cfg; + gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &cfg); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_AUDIO, cfg.audioPL); + } else { + gf_export_message(dumper, GF_OK, "Warning: Audio PLs not found in original MP4 - defaulting to No Profile Specified"); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_AUDIO, 0xFE); + } + default: + break; + } + if (iod) gf_odf_desc_del((GF_Descriptor *) iod); + gf_odf_desc_del((GF_Descriptor *)esd); + + if (AddToIOD) gf_isom_add_track_to_root_od(outfile, newTk); + + return GF_OK; +} + +GF_Err gf_media_export_isom(GF_MediaExporter *dumper) +{ + GF_ISOFile *outfile; + GF_Err e; + Bool add_to_iod; + char szName[1000], *ext; + u32 track; + u8 mode; + + track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + if (gf_isom_get_media_type(dumper->file, dumper->trackID)==GF_ISOM_MEDIA_OD) { + return gf_export_message(dumper, GF_BAD_PARAM, "Cannot extract OD track, result is meaningless"); + } + + if (dumper->flags & GF_EXPORT_PROBE_ONLY) { + dumper->flags |= GF_EXPORT_MERGE; + return GF_OK; + } + ext = (char *) gf_isom_get_filename(dumper->file); + if (ext) ext = strrchr(ext, '.'); + sprintf(szName, "%s%s", dumper->out_name, ext ? ext : ".mp4"); + + add_to_iod = 1; + mode = GF_ISOM_WRITE_EDIT; + if (dumper->flags & GF_EXPORT_MERGE) { + FILE *t = fopen(szName, "rb"); + if (t) { + add_to_iod = 0; + mode = GF_ISOM_OPEN_EDIT; + fclose(t); + } + } + outfile = gf_isom_open(szName, mode, NULL); + + if (mode == GF_ISOM_WRITE_EDIT) { + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_AUDIO, 0xFF); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, 0xFF); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_GRAPHICS, 0xFF); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_SCENE, 0xFF); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_OD, 0xFF); + gf_isom_set_pl_indication(outfile, GF_ISOM_PL_MPEGJ, 0xFF); + } + + e = MP4T_CopyTrack(dumper, dumper->file, track, outfile, 1, add_to_iod); + if (!add_to_iod) { + u32 i; + for (i=0; ifile, dumper->trackID); + esd = gf_isom_get_esd(dumper->file, track, 1); + if (!esd) return gf_export_message(dumper, GF_NON_COMPLIANT_BITSTREAM, "Invalid MPEG-4 stream in track ID %d", dumper->trackID); + + if ((esd->decoderConfig->streamType!=GF_STREAM_VISUAL) || + ( (esd->decoderConfig->objectTypeIndication!=0x20) && (esd->decoderConfig->objectTypeIndication!=0x21)) ) { + gf_odf_desc_del((GF_Descriptor*)esd); + return gf_export_message(dumper, GF_NON_COMPLIANT_BITSTREAM, "Track ID %d is not MPEG-4 Visual - cannot extract to AVI", dumper->trackID); + } + if (!esd->decoderConfig->decoderSpecificInfo) { + gf_odf_desc_del((GF_Descriptor*)esd); + return gf_export_message(dumper, GF_NON_COMPLIANT_BITSTREAM, "Missing decoder config for track ID %d", dumper->trackID); + } + if (dumper->flags & GF_EXPORT_PROBE_ONLY) return GF_OK; + + sprintf(szName, "%s.avi", dumper->out_name); + avi_out = AVI_open_output_file(szName); + if (!avi_out) { + gf_odf_desc_del((GF_Descriptor *)esd); + return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName); + } + + /*compute FPS - note we assume constant frame rate without droped frames...*/ + count = gf_isom_get_sample_count(dumper->file, track); + FPS = gf_isom_get_media_timescale(dumper->file, track); + FPS *= (count-1); + samp = gf_isom_get_sample(dumper->file, track, count, &di); + FPS /= (s64) samp->DTS; + gf_isom_sample_del(&samp); + + frame_d = 0; + /*AVC - FIXME dump format is probably wrong...*/ + if (esd->decoderConfig->objectTypeIndication==0x21) { + gf_isom_get_visual_info(dumper->file, track, 1, &w, &h); + v4CC = "h264"; + } + /*MPEG4*/ + else { + /*ignore visual size info, get it from dsi*/ + gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + w = dsi.width; + h = dsi.height; + + v4CC = "XVID"; + + /*compute VfW delay*/ + if (gf_isom_has_time_offset(dumper->file, track)) { + u32 max_CTSO; + u64 DTS; + DTS = max_CTSO = 0; + for (i=0; ifile, track, i+1, NULL, NULL); + if (!samp) break; + if (samp->CTS_Offset>max_CTSO) max_CTSO = samp->CTS_Offset; + DTS = samp->DTS; + gf_isom_sample_del(&samp); + } + DTS /= (count-1); + frame_d = max_CTSO / (u32) DTS; + frame_d -= 1; + /*dummy delay frame for xvid unpacked bitstreams*/ + dumdata[0] = 127; + } + } + + gf_export_message(dumper, GF_OK, "Creating AVI file %d x %d @ %.2f FPS - 4CC \"%s\"", w, h, FPS,v4CC); + if (frame_d) gf_export_message(dumper, GF_OK, "B-Frames detected - using unpacked bitstream with max B-VOP delta %d", frame_d); + + AVI_set_video(avi_out, w, h, FPS, v4CC); + + + for (i=0; ifile, track, i+1, &di); + if (!samp) break; + + /*add DSI before each I-frame in MPEG-4 SP*/ + if (samp->IsRAP && (esd->decoderConfig->objectTypeIndication==0x20)) { + char *data = (char*) malloc(sizeof(char) * (samp->dataLength + esd->decoderConfig->decoderSpecificInfo->dataLength)); + memcpy(data, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); + memcpy(data + esd->decoderConfig->decoderSpecificInfo->dataLength, samp->data, samp->dataLength); + AVI_write_frame(avi_out, data, samp->dataLength + esd->decoderConfig->decoderSpecificInfo->dataLength, 1); + free(data); + } else { + AVI_write_frame(avi_out, samp->data, samp->dataLength, samp->IsRAP); + } + gf_isom_sample_del(&samp); + while (frame_d) { + AVI_write_frame(avi_out, dumdata, 1, 0); + frame_d--; + } + gf_set_progress("AVI Export", i+1, count); + if (dumper->flags & GF_EXPORT_DO_ABORT) break; + } + + gf_odf_desc_del((GF_Descriptor *) esd); + AVI_close(avi_out); + return GF_OK; +} + +GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc) +{ + GF_ESD *esd; + char szName[1000], szMedia[1000]; + FILE *med, *inf, *nhml; + Bool full_dump; + u32 w, h; + Bool uncompress; + u32 track, i, di, count, pos, mstype; + const char *szRootName, *szSampleName; + + track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + if (!track) return gf_export_message(dumper, GF_BAD_PARAM, "Invalid track ID %d", dumper->trackID); + + if (dumper->flags & GF_EXPORT_PROBE_ONLY) { + dumper->flags |= GF_EXPORT_NHML_FULL; + return GF_OK; + } + esd = gf_isom_get_esd(dumper->file, track, 1); + full_dump = (dumper->flags & GF_EXPORT_NHML_FULL) ? 1 : 0; + med = NULL; + if (dims_doc) { + sprintf(szName, "%s.dml", dumper->out_name); + szRootName = "DIMSStream"; + szSampleName = "DIMSUnit"; + } else { + sprintf(szMedia, "%s.media", dumper->out_name); + med = gf_f64_open(szMedia, "wb"); + if (!med) { + if (esd) gf_odf_desc_del((GF_Descriptor *) esd); + return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szMedia); + } + + sprintf(szName, "%s.nhml", dumper->out_name); + szRootName = "NHNTStream"; + szSampleName = "NHNTSample"; + } + nhml = fopen(szName, "wt"); + if (!nhml) { + fclose(med); + if (esd) gf_odf_desc_del((GF_Descriptor *) esd); + return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName); + } + + mstype = gf_isom_get_media_subtype(dumper->file, track, 1); + + /*write header*/ + fprintf(nhml, "\n"); + fprintf(nhml, "<%s version=\"1.0\" timeScale=\"%d\" ", szRootName, gf_isom_get_media_timescale(dumper->file, track) ); + if (esd) { + fprintf(nhml, "streamType=\"%d\" objectTypeIndication=\"%d\" ", esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); + if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { + sprintf(szName, "%s.info", dumper->out_name); + inf = fopen(szName, "wb"); + if (inf) fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, 1, inf); + fclose(inf); + fprintf(nhml, "specificInfoFile=\"%s\" ", szName); + } + gf_odf_desc_del((GF_Descriptor *) esd); + + if (gf_isom_get_media_type(dumper->file, track)==GF_ISOM_MEDIA_VISUAL) { + gf_isom_get_visual_info(dumper->file, track, 1, &w, &h); + fprintf(nhml, "width=\"%d\" height=\"%d\" ", w, h); + } + else if (gf_isom_get_media_type(dumper->file, track)==GF_ISOM_MEDIA_AUDIO) { + u32 sr, nb_ch; + u8 bps; + gf_isom_get_audio_info(dumper->file, track, 1, &sr, &nb_ch, &bps); + fprintf(nhml, "sampleRate=\"%d\" numChannels=\"%d\" ", sr, nb_ch); + } + } else if (!dims_doc) { + GF_GenericSampleDescription *sdesc = gf_isom_get_generic_sample_description(dumper->file, track, 1); + u32 mtype = gf_isom_get_media_type(dumper->file, track); + fprintf(nhml, "mediaType=\"%s\" ", gf_4cc_to_str(mtype)); + fprintf(nhml, "mediaSubType=\"%s\" ", gf_4cc_to_str(mstype )); + if (sdesc) { + if (mtype==GF_ISOM_MEDIA_VISUAL) { + fprintf(nhml, "codecVendor=\"%s\" codecVersion=\"%d\" codecRevision=\"%d\" ", gf_4cc_to_str(sdesc->vendor_code), sdesc->version, sdesc->revision); + fprintf(nhml, "width=\"%d\" height=\"%d\" compressorName=\"%s\" temporalQuality=\"%d\" spatialQuality=\"%d\" horizontalResolution=\"%d\" verticalResolution=\"%d\" bitDepth=\"%d\" ", + sdesc->width, sdesc->height, sdesc->compressor_name, sdesc->temporal_quality, sdesc->spacial_quality, sdesc->h_res, sdesc->v_res, sdesc->depth); + } else if (mtype==GF_ISOM_MEDIA_AUDIO) { + fprintf(nhml, "codecVendor=\"%s\" codecVersion=\"%d\" codecRevision=\"%d\" ", gf_4cc_to_str(sdesc->vendor_code), sdesc->version, sdesc->revision); + fprintf(nhml, "sampleRate=\"%d\" numChannels=\"%d\" bitsPerSample=\"%d\" ", sdesc->samplerate, sdesc->nb_channels, sdesc->bits_per_sample); + } + if (sdesc->extension_buf) { + sprintf(szName, "%s.info", dumper->out_name); + inf = fopen(szName, "wb"); + if (inf) fwrite(sdesc->extension_buf, sdesc->extension_buf_size, 1, inf); + fclose(inf); + fprintf(nhml, "specificInfoFile=\"%s\" ", szName); + free(sdesc->extension_buf); + } + free(sdesc); + } + } + + if (gf_isom_is_track_in_root_od(dumper->file, track)) fprintf(nhml, "inRootOD=\"yes\" "); + fprintf(nhml, "trackID=\"%d\" ", dumper->trackID); + + uncompress = 0; + + if (mstype == GF_ISOM_MEDIA_DIMS) { + GF_DIMSDescription dims; + + fprintf(nhml, "xmlns=\"http://www.3gpp.org/richmedia\" "); + gf_isom_get_visual_info(dumper->file, track, 1, &w, &h); + fprintf(nhml, "width=\"%d\" height=\"%d\" ", w, h); + + gf_isom_get_dims_description(dumper->file, track, 1, &dims); + fprintf(nhml, "profile=\"%d\" level=\"%d\" pathComponents=\"%d\" ", dims.profile, dims.level, dims.pathComponents); + fprintf(nhml, "useFullRequestHost=\"%s\" stream_type=\"%s\" ", dims.fullRequestHost ? "yes" : "no", dims.streamType ? "primary" : "secondary"); + fprintf(nhml, "contains_redundant=\"%s\" ", (dims.containsRedundant==1) ? "main" : (dims.containsRedundant==2) ? "redundant" : "main+redundant"); + if (strlen(dims.textEncoding) ) fprintf(nhml, "text_encoding=\"%s\" ", dims.textEncoding); + if (strlen(dims.contentEncoding) ) { + fprintf(nhml, "content_encoding=\"%s\" ", dims.contentEncoding); + if (!strcmp(dims.contentEncoding, "deflate")) uncompress = 1; + } + if (dims.content_script_types) fprintf(nhml, "content_script_types=\"%s\" ", dims.content_script_types); + } else { + fprintf(nhml, "baseMediaFile=\"%s\" ", szMedia); + } + + + fprintf(nhml, ">\n"); + + + pos = 0; + count = gf_isom_get_sample_count(dumper->file, track); + for (i=0; ifile, track, i+1, &di); + if (!samp) break; + + if (med) + fwrite(samp->data, samp->dataLength, 1, med); + + if (dims_doc) { + GF_BitStream *bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ); + + while (gf_bs_available(bs)) { + u32 pos = (u32) gf_bs_get_position(bs); + u16 size = gf_bs_read_u16(bs); + u8 flags = gf_bs_read_u8(bs); + u8 prev; + + if (pos+size+2>samp->dataLength) + break; + + prev = samp->data[pos+2+size]; + samp->data[pos+2+size] = 0; + + + fprintf(nhml, "DTS); + /*DIMS flags*/ + if (flags & GF_DIMS_UNIT_S) fprintf(nhml, " is-Scene=\"yes\""); + if (flags & GF_DIMS_UNIT_M) fprintf(nhml, " is-RAP=\"yes\""); + if (flags & GF_DIMS_UNIT_I) fprintf(nhml, " is-redundant=\"yes\""); + if (flags & GF_DIMS_UNIT_D) fprintf(nhml, " redundant-exit=\"yes\""); + if (flags & GF_DIMS_UNIT_P) fprintf(nhml, " priority=\"high\""); + if (flags & GF_DIMS_UNIT_C) fprintf(nhml, " compressed=\"yes\""); + fprintf(nhml, ">"); + if (uncompress && (flags & GF_DIMS_UNIT_C)) { + char svg_data[2049]; + int err; + u32 done = 0; + z_stream d_stream; + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + d_stream.next_in = (Bytef*)samp->data+pos+3; + d_stream.avail_in = size-1; + d_stream.next_out = (Bytef*)svg_data; + d_stream.avail_out = 2048; + + err = inflateInit(&d_stream); + if (err == Z_OK) { + while ((s32) d_stream.total_in < size-1) { + err = inflate(&d_stream, Z_NO_FLUSH); + if (err < Z_OK) break; + svg_data[d_stream.total_out - done] = 0; + fprintf(nhml, svg_data); + if (err== Z_STREAM_END) break; + done = d_stream.total_out; + d_stream.avail_out = 2048; + d_stream.next_out = (Bytef*)svg_data; + } + inflateEnd(&d_stream); + } + } else { + fwrite(samp->data+pos+3, size-1, 1, nhml); + } + fprintf(nhml, "\n"); + + samp->data[pos+2+size] = prev; + gf_bs_skip_bytes(bs, size-1); + } + gf_bs_del(bs); + + } else { + fprintf(nhml, "DTS, samp->dataLength); + if (full_dump || samp->CTS_Offset) fprintf(nhml, "CTSOffset=\"%d\" ", samp->CTS_Offset); + if (samp->IsRAP==1) fprintf(nhml, "isRAP=\"yes\" "); + else if (samp->IsRAP==2) fprintf(nhml, "isSyncShadow=\"yes\" "); + else if (full_dump) fprintf(nhml, "isRAP=\"no\" "); + if (full_dump) fprintf(nhml, "mediaOffset=\"%d\" ", pos); + fprintf(nhml, "/>\n"); + } + + pos += samp->dataLength; + gf_isom_sample_del(&samp); + gf_set_progress("NHML Export", i+1, count); + if (dumper->flags & GF_EXPORT_DO_ABORT) break; + } + fprintf(nhml, "\n", szRootName); + if (med) fclose(med); + fclose(nhml); + return GF_OK; +} + +typedef struct +{ + u32 track_num, stream_id, last_sample, nb_samp; +} SAFInfo; + +GF_Err gf_media_export_saf(GF_MediaExporter *dumper) +{ + u32 count, i, s_count, di, tot_samp, samp_done; + char out_file[GF_MAX_PATH]; + GF_SAFMuxer *mux; + char *data; + u32 size; + FILE *saf_f; + SAFInfo safs[1024]; + + if (dumper->flags & GF_EXPORT_PROBE_ONLY) return GF_OK; + + s_count = tot_samp = 0; + + mux = gf_saf_mux_new(); + count = gf_isom_get_track_count(dumper->file); + for (i=0; ifile, i+1); + if (mtype==GF_ISOM_MEDIA_OD) continue; + if (mtype==GF_ISOM_MEDIA_HINT) continue; + + stream_id = 0; + time_scale = gf_isom_get_media_timescale(dumper->file, i+1); + esd = gf_isom_get_esd(dumper->file, i+1, 1); + if (esd) { + stream_id = gf_isom_find_od_for_track(dumper->file, i+1); + if (!stream_id) stream_id = esd->ESID; + + /*translate OD IDs to ESIDs !!*/ + if (esd->decoderConfig->decoderSpecificInfo) { + gf_saf_mux_stream_add(mux, stream_id, time_scale, esd->decoderConfig->bufferSizeDB, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication, NULL, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, esd->URLString); + } else { + gf_saf_mux_stream_add(mux, stream_id, time_scale, esd->decoderConfig->bufferSizeDB, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication, NULL, NULL, 0, esd->URLString); + } + gf_odf_desc_del((GF_Descriptor *)esd); + } else { + char *mime = NULL; + switch (gf_isom_get_media_subtype(dumper->file, i+1, 1)) { + case GF_ISOM_SUBTYPE_3GP_H263: mime = "video/h263"; break; + case GF_ISOM_SUBTYPE_3GP_AMR: mime = "audio/amr"; break; + case GF_ISOM_SUBTYPE_3GP_AMR_WB: mime = "audio/amr-wb"; break; + case GF_ISOM_SUBTYPE_3GP_EVRC: mime = "audio/evrc"; break; + case GF_ISOM_SUBTYPE_3GP_QCELP: mime = "audio/qcelp"; break; + case GF_ISOM_SUBTYPE_3GP_SMV: mime = "audio/smv"; break; + } + if (!mime) continue; + stream_id = gf_isom_get_track_id(dumper->file, i+1); + gf_saf_mux_stream_add(mux, stream_id, time_scale, 0, 0xFF, 0xFF, mime, NULL, 0, NULL); + } + + safs[s_count].track_num = i+1; + safs[s_count].stream_id = stream_id; + safs[s_count].nb_samp = gf_isom_get_sample_count(dumper->file, i+1); + safs[s_count].last_sample = 0; + + tot_samp += safs[s_count].nb_samp; + + s_count++; + } + + if (!s_count) { + gf_export_message(dumper, GF_OK, "No tracks available for SAF muxing"); + gf_saf_mux_del(mux); + return GF_OK; + } + gf_export_message(dumper, GF_OK, "SAF: Multiplexing %d tracks", s_count); + + strcpy(out_file, dumper->out_name); + strcat(out_file, ".saf"); + saf_f = fopen(out_file, "wb"); + + samp_done = 0; + while (samp_donefile, safs[i].track_num, safs[i].last_sample + 1, &di); + gf_saf_mux_add_au(mux, safs[i].stream_id, (u32) (samp->DTS+samp->CTS_Offset), samp->data, samp->dataLength, samp->IsRAP); + /*data is kept by muxer!!*/ + free(samp); + safs[i].last_sample++; + samp_done ++; + } + while (1) { + gf_saf_mux_for_time(mux, (u32) -1, 0, &data, &size); + if (!data) break; + fwrite(data, size, 1, saf_f); + free(data); + } + gf_set_progress("SAF Export", samp_done, tot_samp); + if (dumper->flags & GF_EXPORT_DO_ABORT) break; + } + gf_saf_mux_for_time(mux, (u32) -1, 1, &data, &size); + if (data) { + fwrite(data, size, 1, saf_f); + free(data); + } + fclose(saf_f); + + gf_saf_mux_del(mux); + return GF_OK; +} + +void m2ts_export_check(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) +{ + if (evt_type == GF_M2TS_EVT_PAT_REPEAT) ts->user = NULL; +} +void m2ts_export_dump(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) +{ + if (evt_type == GF_M2TS_EVT_PES_PCK) { + FILE *dst = (FILE*)ts->user; + GF_M2TS_PES_PCK *pck = (GF_M2TS_PES_PCK *)par; + fwrite(pck->data, pck->data_len, 1, dst); + } + else if (evt_type == GF_M2TS_EVT_SL_PCK) { + FILE *dst = (FILE*)ts->user; + GF_M2TS_SL_PCK *pck = (GF_M2TS_SL_PCK *)par; + fwrite(pck->data + 5, pck->data_len - 5, 1, dst); + } +} + +GF_Err gf_media_export_ts_native(GF_MediaExporter *dumper) +{ + char data[188], szFile[GF_MAX_PATH]; + GF_M2TS_PES *stream; + u32 i, size, fsize, fdone; + GF_M2TS_Demuxer *ts; + FILE *src, *dst; + + if (dumper->flags & GF_EXPORT_PROBE_ONLY) return GF_OK; + + src = fopen(dumper->in_name, "rb"); + if (!src) return gf_export_message(dumper, GF_CODEC_NOT_FOUND, "Error opening %s", dumper->in_name); + + fseek(src, 0, SEEK_END); + fsize = ftell(src); + fseek(src, 0, SEEK_SET); + + ts = gf_m2ts_demux_new(); + ts->on_event = m2ts_export_check; + ts->user = dumper; + /*get PAT*/ + while (!feof(src)) { + size = fread(data, 1, 188, src); + if (size<188) break; + gf_m2ts_process_data(ts, data, size); + if (!ts->user) break; + } + if (ts->user) { + fclose(src); + gf_m2ts_demux_del(ts); + return gf_export_message(dumper, GF_URL_ERROR, "Cannot locate program association table"); + } + + stream = NULL; + for (i=0; iess[i]; + if (!pes || (pes->pid==pes->program->pmt_pid)) continue; + if (pes->pid == dumper->trackID) { + stream = pes; + gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_RAW); + break; + } else { + gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP); + } + } + if (!stream) { + fclose(src); + gf_m2ts_demux_del(ts); + return gf_export_message(dumper, GF_URL_ERROR, "Cannot find PID %d in transport stream", dumper->trackID); + } + gf_m2ts_reset_parsers(ts); + + sprintf(szFile, "%s_pid%d", dumper->out_name ? dumper->out_name : "", stream->pid); + switch (stream->stream_type) { + case GF_M2TS_VIDEO_MPEG1: + strcat(szFile, ".m1v"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-1 Visual stream to m1v"); + break; + case GF_M2TS_VIDEO_MPEG2: + strcat(szFile, ".m2v"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-2 Visual stream to m1v"); + break; + case GF_M2TS_AUDIO_MPEG1: + strcat(szFile, ".mp3"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-1 Audio stream to mp3"); + break; + case GF_M2TS_AUDIO_MPEG2: + strcat(szFile, ".mp3"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-2 Audio stream to mp3"); + break; + case GF_M2TS_AUDIO_AAC: + strcat(szFile, ".aac"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-4 Audio stream to aac"); + break; + case GF_M2TS_VIDEO_MPEG4: + strcat(szFile, ".cmp"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-4 Visual stream to cmp"); + break; + case GF_M2TS_VIDEO_H264: + strcat(szFile, ".264"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-4 AVC/H264 Visual stream to h264"); + break; + default: + strcat(szFile, ".raw"); + gf_export_message(dumper, GF_OK, "Extracting Unknown stream to raw"); + break; + } + dst = fopen(szFile, "wb"); + if (!dst) { + fclose(src); + gf_m2ts_demux_del(ts); + return gf_export_message(dumper, GF_IO_ERR, "Cannot open file %s for writing", szFile); + } + gf_m2ts_reset_parsers(ts); + gf_f64_seek(src, 0, SEEK_SET); + fdone = 0; + ts->user = dst; + ts->on_event = m2ts_export_dump; + while (!feof(src)) { + size = fread(data, 1, 188, src); + if (size<188) break; + gf_m2ts_process_data(ts, data, size); + fdone += size; + gf_set_progress("MPEG-2 TS Extract", fdone, fsize); + if (dumper->flags & GF_EXPORT_DO_ABORT) break; + } + gf_set_progress("MPEG-2 TS Extract", fsize, fsize); + fclose(dst); + fclose(src); + gf_m2ts_demux_del(ts); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_media_export(GF_MediaExporter *dumper) +{ + if (!dumper) return GF_BAD_PARAM; + if (!dumper->out_name && !dumper->flags & GF_EXPORT_PROBE_ONLY) return GF_BAD_PARAM; + + if (dumper->flags & GF_EXPORT_NATIVE) { + if (dumper->in_name) { + char *ext = strrchr(dumper->in_name, '.'); + if (ext && (!strnicmp(ext, ".ts", 3) || !strnicmp(ext, ".m2t", 4)) ) { + return gf_media_export_ts_native(dumper); + } + } + return gf_media_export_native(dumper); + } + else if (dumper->flags & GF_EXPORT_RAW_SAMPLES) return gf_media_export_samples(dumper); + else if (dumper->flags & GF_EXPORT_NHNT) return gf_media_export_nhnt(dumper); + else if (dumper->flags & GF_EXPORT_AVI) return gf_media_export_avi(dumper); + else if (dumper->flags & GF_EXPORT_MP4) return gf_media_export_isom(dumper); + else if (dumper->flags & GF_EXPORT_AVI_NATIVE) return gf_media_export_avi_track(dumper); + else if (dumper->flags & GF_EXPORT_NHML) return gf_media_export_nhml(dumper, 0); + else if (dumper->flags & GF_EXPORT_SAF) return gf_media_export_saf(dumper); + else return GF_BAD_PARAM; +} + +#endif + diff --git a/src/media_tools/media_import.c b/src/media_tools/media_import.c new file mode 100644 index 0000000..2d1bc93 --- /dev/null +++ b/src/media_tools/media_import.c @@ -0,0 +1,6181 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include +#include +#include +#include +#include +#include +/*since 0.2.2, we use zlib for xmt/x3d reading to handle gz files*/ +#include + +#ifndef GPAC_READ_ONLY + +#define GF_IMPORT_DEFAULT_FPS 25.0 + + +GF_Err gf_import_message(GF_MediaImporter *import, GF_Err e, char *format, ...) +{ +#ifndef GPAC_DISABLE_LOG + if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_AUTHOR)) { + va_list args; + char szMsg[1024]; + va_start(args, format); + vsprintf(szMsg, format, args); + va_end(args); + GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_INFO), GF_LOG_AUTHOR, ("%s\n", szMsg) ); + } +#endif + return e; +} + +static GF_Err gf_media_update_par(GF_ISOFile *file, u32 track) +{ + u32 tk_w, tk_h, stype; + GF_Err e; + + e = gf_isom_get_visual_info(file, track, 1, &tk_w, &tk_h); + if (e) return e; + + stype = gf_isom_get_media_subtype(file, track, 1); + if (stype==GF_ISOM_SUBTYPE_AVC_H264) { + s32 par_n, par_d; + GF_AVCConfig *avcc = gf_isom_avc_config_get(file, track, 1); + GF_AVCConfigSlot *slc = (GF_AVCConfigSlot *)gf_list_get(avcc->sequenceParameterSets, 0); + par_n = par_d = 1; + if (slc) gf_avc_get_sps_info(slc->data, slc->size, NULL, NULL, &par_n, &par_d); + gf_odf_avc_cfg_del(avcc); + + if ((par_n>1) && (par_d>1)) + tk_w = tk_w * par_n / par_d; + } + else if ((stype==GF_ISOM_SUBTYPE_MPEG4) || (stype==GF_ISOM_SUBTYPE_MPEG4_CRYP) ) { + GF_M4VDecSpecInfo dsi; + GF_ESD *esd = gf_isom_get_esd(file, track, 1); + if (!esd || !esd->decoderConfig || (esd->decoderConfig->streamType!=4) || (esd->decoderConfig->objectTypeIndication!=0x20)) { + if (esd) gf_odf_desc_del((GF_Descriptor *) esd); + return GF_NOT_SUPPORTED; + } + gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + if (esd) gf_odf_desc_del((GF_Descriptor *) esd); + + if ((dsi.par_num>1) && (dsi.par_num>1)) + tk_w = dsi.width * dsi.par_num / dsi.par_den; + } else { + return GF_OK; + } + return gf_isom_set_track_layout_info(file, track, tk_w<<16, tk_h<<16, 0, 0, 0); +} + +static void MP4T_RecomputeBitRate(GF_ISOFile *file, u32 track) +{ + u32 i, count, timescale; + u64 time_wnd, rate, max_rate, avg_rate; + Double br; + GF_ESD *esd; + + esd = gf_isom_get_esd(file, track, 1); + if (!esd) return; + + esd->decoderConfig->avgBitrate = 0; + esd->decoderConfig->maxBitrate = 0; + rate = max_rate = avg_rate = time_wnd = 0; + + timescale = gf_isom_get_media_timescale(file, track); + count = gf_isom_get_sample_count(file, track); + for (i=0; idataLength>esd->decoderConfig->bufferSizeDB) esd->decoderConfig->bufferSizeDB = samp->dataLength; + + if (esd->decoderConfig->bufferSizeDB < samp->dataLength) esd->decoderConfig->bufferSizeDB = samp->dataLength; + avg_rate += samp->dataLength; + rate += samp->dataLength; + if (samp->DTS > time_wnd + timescale) { + if (rate > max_rate) max_rate = rate; + time_wnd = samp->DTS; + rate = 0; + } + + gf_isom_sample_del(&samp); + } + + br = (Double) (s64) gf_isom_get_media_duration(file, track); + br /= timescale; + esd->decoderConfig->avgBitrate = (u32) ((Double) (s64)avg_rate / br); + /*move to bps*/ + esd->decoderConfig->avgBitrate *= 8; + esd->decoderConfig->maxBitrate = (u32) (max_rate*8); + + gf_isom_change_mpeg4_description(file, track, 1, esd); + gf_odf_desc_del((GF_Descriptor *)esd); +} + +static void get_video_timing(Double fps, u32 *timescale, u32 *dts_inc) +{ + u32 fps_1000 = (u32) (fps*1000 + 0.5); + /*handle all drop-frame formats*/ + if (fps_1000==29970) { + *timescale = 30000; + *dts_inc = 1001; + } + else if (fps_1000==23976) { + *timescale = 24000; + *dts_inc = 1001; + } + else if (fps_1000==59940) { + *timescale = 60000; + *dts_inc = 1001; + } else { + *timescale = fps_1000; + *dts_inc = 1000; + } +} + + + +static GF_Err gf_import_still_image(GF_MediaImporter *import) +{ + GF_BitStream *bs; + GF_Err e; + Bool destroy_esd; + u32 size, track, di, w, h, dsi_len, mtype; + GF_ISOSample *samp; + u8 OTI; + char *dsi, *data; + FILE *src; + + + src = fopen(import->in_name, "rb"); + if (!src) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + + fseek(src, 0, SEEK_END); + size = ftell(src); + fseek(src, 0, SEEK_SET); + data = (char*)malloc(sizeof(char)*size); + fread(data, sizeof(char)*size, 1, src); + fclose(src); + + /*get image size*/ + bs = gf_bs_new(data, size, GF_BITSTREAM_READ); + gf_img_parse(bs, &OTI, &mtype, &w, &h, &dsi, &dsi_len); + gf_bs_del(bs); + + if (!OTI) { + free(data); + return gf_import_message(import, GF_NOT_SUPPORTED, "Unrecognized file %s", import->in_name); + } + + if (!w || !h) { + free(data); + if (dsi) free(dsi); + return gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Invalid %s file", (OTI==0x6C) ? "JPEG" : (OTI==0x6D) ? "PNG" : "JPEG2000"); + } + + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[0].media_type = mtype; + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF | GF_IMPORT_NO_DURATION; + import->tk_info[0].video_info.width = w; + import->tk_info[0].video_info.height = h; + import->nb_tracks = 1; + if (dsi) free(dsi); + return GF_OK; + } + + e = GF_OK; + destroy_esd = 0; + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(2); + destroy_esd = 1; + } + /*update stream type/oti*/ + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->decoderConfig->streamType = GF_STREAM_VISUAL; + import->esd->decoderConfig->objectTypeIndication = OTI; + import->esd->decoderConfig->bufferSizeDB = size; + import->esd->decoderConfig->avgBitrate = 8*size; + import->esd->decoderConfig->maxBitrate = 8*size; + import->esd->slConfig->timestampResolution = 1000; + + if (dsi) { + if (!import->esd->decoderConfig->decoderSpecificInfo) import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + if (import->esd->decoderConfig->decoderSpecificInfo->data) free(import->esd->decoderConfig->decoderSpecificInfo->data); + import->esd->decoderConfig->decoderSpecificInfo->data = dsi; + import->esd->decoderConfig->decoderSpecificInfo->dataLength = dsi_len; + } + + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_VISUAL, 1000); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + if (e) goto exit; + gf_isom_set_visual_info(import->dest, track, di, w, h); + samp = gf_isom_sample_new(); + samp->IsRAP = 1; + samp->dataLength = size; + + gf_import_message(import, GF_OK, "%s import - size %d x %d", (OTI==0x6C) ? "JPEG" : (OTI==0x6D) ? "PNG" : "JPEG2000", w, h); + + gf_set_progress("Importing Image", 0, 1); + if (import->flags & GF_IMPORT_USE_DATAREF) { + e = gf_isom_add_sample_reference(import->dest, track, di, samp, (u64) 0); + } else { + samp->data = data; + e = gf_isom_add_sample(import->dest, track, di, samp); + samp->data = NULL; + } + gf_set_progress("Importing Image", 1, 1); + + gf_isom_sample_del(&samp); + +exit: + free(data); + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + return e; +} + + +GF_Err gf_import_mp3(GF_MediaImporter *import) +{ + u8 oti; + Bool destroy_esd; + GF_Err e; + u16 sr; + u32 nb_chan; + FILE *in; + u32 hdr, size, max_size, track, di, tot_size, done, offset, duration; + GF_ISOSample *samp; + + in = gf_f64_open(import->in_name, "rb"); + if (!in) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + + hdr = gf_mp3_get_next_header(in); + if (!hdr) { + fclose(in); + return gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Audio isn't MPEG-1/2 audio"); + } + sr = gf_mp3_sampling_rate(hdr); + oti = gf_mp3_object_type_indication(hdr); + if (!oti) { + fclose(in); + return gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Audio isn't MPEG-1/2 audio"); + } + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + fclose(in); + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF; + import->tk_info[0].audio_info.sample_rate = sr; + import->tk_info[0].audio_info.nb_channels = gf_mp3_num_channels(hdr); + import->nb_tracks = 1; + return GF_OK; + } + + + e = GF_OK; + destroy_esd = 0; + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(2); + destroy_esd = 1; + } + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + /*update stream type/oti*/ + import->esd->decoderConfig->streamType = GF_STREAM_AUDIO; + import->esd->decoderConfig->objectTypeIndication = oti; + import->esd->decoderConfig->bufferSizeDB = 20; + import->esd->slConfig->timestampResolution = sr; + + samp = NULL; + nb_chan = gf_mp3_num_channels(hdr); + gf_import_message(import, GF_OK, "MP3 import - sample rate %d - %s audio - %d channel%s", sr, (oti==0x6B) ? "MPEG-1" : "MPEG-2", nb_chan, (nb_chan>1) ? "s" : ""); + + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_AUDIO, sr); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + if (import->esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) import->esd->decoderConfig->decoderSpecificInfo); + import->esd->decoderConfig->decoderSpecificInfo = NULL; + gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + gf_isom_set_audio_info(import->dest, track, di, sr, nb_chan, 16); + + fseek(in, 0, SEEK_END); + tot_size = ftell(in); + fseek(in, 0, SEEK_SET); + + e = GF_OK; + samp = gf_isom_sample_new(); + samp->IsRAP = 1; + + duration = import->duration*sr; + duration /= 1000; + + max_size = 0; + done = 0; + while (tot_size > done) { + /* get the next MP3 frame header */ + hdr = gf_mp3_get_next_header(in); + /*MP3 stream truncated*/ + if (!hdr) break; + + offset = ftell(in) - 4; + size = gf_mp3_frame_size(hdr); + assert(size); + if (size>max_size) { + samp->data = (char*)realloc(samp->data, sizeof(char) * size); + max_size = size; + } + samp->data[0] = (hdr >> 24) & 0xFF; + samp->data[1] = (hdr >> 16) & 0xFF; + samp->data[2] = (hdr >> 8) & 0xFF; + samp->data[3] = hdr & 0xFF; + samp->dataLength = size; + + /* read the frame data into the buffer */ + if (fread(&samp->data[4], 1, size - 4, in) != size - 4) break; + + if (import->flags & GF_IMPORT_USE_DATAREF) { + e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset); + } else { + e = gf_isom_add_sample(import->dest, track, di, samp); + } + if (e) goto exit; + + gf_set_progress("Importing MP3", done, tot_size); + + samp->DTS += gf_mp3_window_size(hdr); + done += samp->dataLength; + if (duration && (samp->DTS > duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + } + MP4T_RecomputeBitRate(import->dest, track); + gf_set_progress("Importing MP3", tot_size, tot_size); + +exit: + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + if (samp) gf_isom_sample_del(&samp); + fclose(in); + return e; +} + +typedef struct +{ + Bool is_mp2, no_crc; + u32 profile, sr_idx, nb_ch, frame_size; +} ADTSHeader; + +static Bool ADTS_SyncFrame(GF_BitStream *bs, ADTSHeader *hdr) +{ + u32 val, hdr_size, pos; + while (gf_bs_available(bs)) { + val = gf_bs_read_u8(bs); + if (val!=0xFF) continue; + val = gf_bs_read_int(bs, 4); + if (val != 0x0F) { + gf_bs_read_int(bs, 4); + continue; + } + hdr->is_mp2 = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 2); + hdr->no_crc = gf_bs_read_int(bs, 1); + pos = (u32) gf_bs_get_position(bs) - 2; + + hdr->profile = 1 + gf_bs_read_int(bs, 2); + hdr->sr_idx = gf_bs_read_int(bs, 4); + gf_bs_read_int(bs, 1); + hdr->nb_ch = gf_bs_read_int(bs, 3); + gf_bs_read_int(bs, 4); + hdr->frame_size = gf_bs_read_int(bs, 13); + gf_bs_read_int(bs, 11); + gf_bs_read_int(bs, 2); + hdr_size = hdr->no_crc ? 7 : 9; + if (!hdr->no_crc) gf_bs_read_int(bs, 16); + if (hdr->frame_size < hdr_size) { + gf_bs_seek(bs, pos+1); + continue; + } + hdr->frame_size -= hdr_size; + if (gf_bs_available(bs) == hdr->frame_size) return 1; + + gf_bs_skip_bytes(bs, hdr->frame_size); + val = gf_bs_read_u8(bs); + if (val!=0xFF) { + gf_bs_seek(bs, pos+1); + continue; + } + val = gf_bs_read_int(bs, 4); + if (val!=0x0F) { + gf_bs_read_int(bs, 4); + gf_bs_seek(bs, pos+2); + continue; + } + gf_bs_seek(bs, pos+hdr_size); + return 1; + } + return 0; +} + +GF_Err gf_import_aac_adts(GF_MediaImporter *import) +{ + u8 oti; + Bool destroy_esd; + GF_Err e; + Bool sync_frame; + u16 sr, sbr_sr, sbr_sr_idx, dts_inc; + GF_BitStream *bs, *dsi; + ADTSHeader hdr; + GF_M4ADecSpecInfo acfg; + FILE *in; + u64 offset; + u32 max_size, track, di, tot_size, done, duration, prof, i; + GF_ISOSample *samp; + + in = gf_f64_open(import->in_name, "rb"); + if (!in) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + + bs = gf_bs_from_file(in, GF_BITSTREAM_READ); + + sync_frame = ADTS_SyncFrame(bs, &hdr); + if (!sync_frame) { + gf_bs_del(bs); + fclose(in); + return gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Audio isn't MPEG-2/4 AAC with ADTS"); + } + if (import->flags & GF_IMPORT_FORCE_MPEG4) hdr.is_mp2 = 0; + + /*keep MPEG-2 AAC OTI even for HE-SBR (that's correct according to latest MPEG-4 audio spec)*/ + oti = hdr.is_mp2 ? hdr.profile+0x66-1 : 0x40; + sr = GF_M4ASampleRates[hdr.sr_idx]; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF | GF_IMPORT_SBR_IMPLICIT | GF_IMPORT_SBR_EXPLICIT | GF_IMPORT_FORCE_MPEG4; + import->nb_tracks = 1; + import->tk_info[0].audio_info.sample_rate = sr; + import->tk_info[0].audio_info.nb_channels = hdr.nb_ch; + gf_bs_del(bs); + fclose(in); + return GF_OK; + } + + dsi = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + sbr_sr = sr; + sbr_sr_idx = hdr.sr_idx; + for (i=0; i<16; i++) { + if (GF_M4ASampleRates[i] == (u32) 2*sr) { + sbr_sr_idx = i; + sbr_sr = 2*sr; + break; + } + } + /*no provision for explicit indication of MPEG-2 AAC through MPEG-4 PLs, so force implicit*/ + if (hdr.is_mp2) { + if (import->flags & GF_IMPORT_SBR_EXPLICIT) { + import->flags &= ~GF_IMPORT_SBR_EXPLICIT; + import->flags |= GF_IMPORT_SBR_IMPLICIT; + } + } + + dts_inc = 1024; + + memset(&acfg, 0, sizeof(GF_M4ADecSpecInfo)); + acfg.base_object_type = hdr.profile; + acfg.base_sr = sr; + acfg.nb_chan = hdr.nb_ch; + acfg.sbr_object_type = 0; + if (import->flags & GF_IMPORT_SBR_EXPLICIT) { + acfg.has_sbr = 1; + acfg.base_object_type = 5; + acfg.sbr_object_type = hdr.profile; + + /*for better interop, always store using full SR when using explict signaling*/ + dts_inc = 2048; + sr = sbr_sr; + } else if (import->flags & GF_IMPORT_SBR_IMPLICIT) { + acfg.has_sbr = 1; + } + acfg.audioPL = gf_m4a_get_profile(&acfg); + /*explicit SBR signal (non backward-compatible)*/ + if (import->flags & GF_IMPORT_SBR_EXPLICIT) { + gf_bs_write_int(dsi, 5, 5); + gf_bs_write_int(dsi, hdr.sr_idx, 4); + gf_bs_write_int(dsi, hdr.nb_ch, 4); + gf_bs_write_int(dsi, sbr_sr ? sbr_sr_idx : hdr.sr_idx, 4); + gf_bs_write_int(dsi, hdr.profile, 5); + } else { + /*regular AAC*/ + gf_bs_write_int(dsi, hdr.profile, 5); + gf_bs_write_int(dsi, hdr.sr_idx, 4); + gf_bs_write_int(dsi, hdr.nb_ch, 4); + gf_bs_align(dsi); + /*implicit AAC SBR signal*/ + if (import->flags & GF_IMPORT_SBR_IMPLICIT) { + gf_bs_write_int(dsi, 0x2b7, 11); /*sync extension type*/ + gf_bs_write_int(dsi, 5, 5); /*SBR objectType*/ + gf_bs_write_int(dsi, 1, 1); /*SBR present flag*/ + gf_bs_write_int(dsi, sbr_sr_idx, 4); + } + } + /*not MPEG4 tool*/ + if (0 && hdr.is_mp2) acfg.audioPL = 0xFE; + + gf_bs_align(dsi); + prof = hdr.profile; + + e = GF_OK; + destroy_esd = 0; + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(2); + destroy_esd = 1; + } + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->decoderConfig->streamType = GF_STREAM_AUDIO; + import->esd->decoderConfig->objectTypeIndication = oti; + import->esd->decoderConfig->bufferSizeDB = 20; + import->esd->slConfig->timestampResolution = sr; + if (!import->esd->decoderConfig->decoderSpecificInfo) import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + if (import->esd->decoderConfig->decoderSpecificInfo->data) free(import->esd->decoderConfig->decoderSpecificInfo->data); + gf_bs_get_content(dsi, &import->esd->decoderConfig->decoderSpecificInfo->data, &import->esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(dsi); + + samp = NULL; + gf_import_message(import, GF_OK, "AAC import %s- sample rate %d - %s audio - %d channel%s", (import->flags & GF_IMPORT_SBR_IMPLICIT) ? "SBR (implicit) " : ((import->flags & GF_IMPORT_SBR_EXPLICIT) ? "SBR (explicit) " : ""), sr, (oti==0x40) ? "MPEG-4" : "MPEG-2", hdr.nb_ch, (hdr.nb_ch>1) ? "s" : ""); + + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_AUDIO, sr); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + gf_isom_set_audio_info(import->dest, track, di, sr, (hdr.nb_ch>2) ? 2 : hdr.nb_ch, 16); + + e = GF_OK; + /*add first sample*/ + samp = gf_isom_sample_new(); + samp->IsRAP = 1; + max_size = samp->dataLength = hdr.frame_size; + samp->data = (char*)malloc(sizeof(char)*hdr.frame_size); + offset = gf_bs_get_position(bs); + gf_bs_read_data(bs, samp->data, hdr.frame_size); + + if (import->flags & GF_IMPORT_USE_DATAREF) { + e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset); + } else { + e = gf_isom_add_sample(import->dest, track, di, samp); + } + if (e) goto exit; + samp->DTS+=dts_inc; + + duration = import->duration*sr; + duration /= 1000; + + tot_size = (u32) gf_bs_get_size(bs); + done = 0; + while (gf_bs_available(bs) ) { + sync_frame = ADTS_SyncFrame(bs, &hdr); + if (!sync_frame) break; + if (hdr.frame_size>max_size) { + samp->data = (char*)realloc(samp->data, sizeof(char) * hdr.frame_size); + max_size = hdr.frame_size; + } + samp->dataLength = hdr.frame_size; + + offset = gf_bs_get_position(bs); + gf_bs_read_data(bs, samp->data, hdr.frame_size); + if (import->flags & GF_IMPORT_USE_DATAREF) { + e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset); + } else { + e = gf_isom_add_sample(import->dest, track, di, samp); + } + if (e) break; + + gf_set_progress("Importing AAC", done, tot_size); + samp->DTS += dts_inc; + done += samp->dataLength; + if (duration && (samp->DTS > duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + } + MP4T_RecomputeBitRate(import->dest, track); + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_AUDIO, acfg.audioPL); + gf_set_progress("Importing AAC", tot_size, tot_size); + +exit: + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + if (samp) gf_isom_sample_del(&samp); + gf_bs_del(bs); + fclose(in); + return e; +} + +static GF_Err gf_import_cmp(GF_MediaImporter *import, Bool mpeg12) +{ + GF_Err e; + Double FPS; + FILE *mdia; + GF_ISOSample *samp; + u32 nb_samp, i, timescale, max_size, samp_offset, track, di, PL, max_b, nbI, nbP, nbB, nbNotCoded, tot_size, done_size, dts_inc, ref_frame, b_frames; + Bool is_vfr, enable_vfr, erase_pl, has_cts_offset, is_packed, destroy_esd, do_vfr, forced_packed; + GF_M4VDecSpecInfo dsi; + GF_M4VParser *vparse; + GF_BitStream *bs; + u64 pos; + u32 duration; + + destroy_esd = forced_packed = 0; + mdia = gf_f64_open(import->in_name, "rb"); + if (!mdia) return gf_import_message(import, GF_URL_ERROR, "Opening %s failed", import->in_name); + bs = gf_bs_from_file(mdia, GF_BITSTREAM_READ); + + samp = NULL; + vparse = gf_m4v_parser_bs_new(bs, mpeg12); + e = gf_m4v_parse_config(vparse, &dsi); + if (e) { + gf_import_message(import, e, "Cannot load MPEG-4 decoder config"); + goto exit; + } + + tot_size = (u32) gf_bs_get_size(bs); + done_size = 0; + destroy_esd = 0; + FPS = mpeg12 ? dsi.fps : GF_IMPORT_DEFAULT_FPS; + if (import->video_fps) FPS = (Double) import->video_fps; + get_video_timing(FPS, ×cale, &dts_inc); + + duration = (u32) (import->duration*FPS); + + is_packed = 0; + nbNotCoded = nbI = nbP = nbB = max_b = 0; + enable_vfr = is_vfr = erase_pl = 0; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[0].media_type = mpeg12 ? ((dsi.VideoPL==0x6A) ? GF_4CC('M','P','G','1') : GF_4CC('M','P','G','2') ) : GF_4CC('M','P','4','V') ; + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF | GF_IMPORT_OVERRIDE_FPS; + if (!mpeg12) import->tk_info[0].flags |= GF_IMPORT_NO_FRAME_DROP | GF_IMPORT_FORCE_PACKED; + import->tk_info[0].video_info.width = dsi.width; + import->tk_info[0].video_info.height = dsi.height; + import->tk_info[0].video_info.par = (dsi.par_num<<16) | dsi.par_den; + import->nb_tracks = 1; + goto exit; + } + + samp = gf_isom_sample_new(); + max_size = 4096; + samp->data = (char*)malloc(sizeof(char)*max_size); + /*no auto frame-rate detection*/ + if (import->video_fps == 10000.0) import->video_fps = 25.0; + + PL = dsi.VideoPL; + if (!PL) { + PL = 0x01; + erase_pl = 1; + } + samp_offset = 0; + /*MPEG-4 visual*/ + if (!mpeg12) samp_offset = gf_m4v_get_object_start(vparse); + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(0); + destroy_esd = 1; + } + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_VISUAL, timescale); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = gf_isom_get_track_id(import->dest, track); + + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->timestampResolution = timescale; + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (import->esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) import->esd->decoderConfig->decoderSpecificInfo); + import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + import->esd->decoderConfig->streamType = GF_STREAM_VISUAL; + if (mpeg12) { + import->esd->decoderConfig->objectTypeIndication = dsi.VideoPL; + } else { + import->esd->decoderConfig->objectTypeIndication = 0x20; + } + if (samp_offset) { + import->esd->decoderConfig->decoderSpecificInfo->data = (char*)malloc(sizeof(char) * samp_offset); + import->esd->decoderConfig->decoderSpecificInfo->dataLength = samp_offset; + pos = gf_bs_get_position(bs); + gf_bs_seek(bs, 0); + gf_bs_read_data(bs, import->esd->decoderConfig->decoderSpecificInfo->data, samp_offset); + gf_bs_seek(bs, pos); + + /*remove packed flag if any (VOSH user data)*/ + forced_packed = 0; + i=0; + while (1) { + char *frame = import->esd->decoderConfig->decoderSpecificInfo->data; + while ((i+3=samp_offset) break; + if (strncmp(frame+i+4, "DivX", 4)) { + i += 4; + continue; + } + frame = import->esd->decoderConfig->decoderSpecificInfo->data + i + 4; + frame = strchr(frame, 'p'); + if (frame) { + forced_packed = 1; + frame[0] = 'n'; + } + break; + } + } + + if (import->flags & GF_IMPORT_FORCE_PACKED) forced_packed = 1; + + gf_isom_set_cts_packing(import->dest, track, 1); + + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name: NULL, NULL, &di); + if (e) goto exit; + gf_isom_set_visual_info(import->dest, track, di, dsi.width, dsi.height); + if (mpeg12) { + gf_import_message(import, GF_OK, "MPEG-%d Video import - %d x %d @ %02.4f FPS", (dsi.VideoPL==0x6A) ? 1 : 2, dsi.width, dsi.height, FPS); + } else { + gf_import_message(import, GF_OK, "MPEG-4 Video import - %d x %d @ %02.4f FPS\nIndicated Profile: %s", dsi.width, dsi.height, FPS, gf_m4v_get_profile_name((u8) PL)); + } + + gf_media_update_par(import->dest, track); + + has_cts_offset = 0; + nb_samp = b_frames = ref_frame = 0; + do_vfr = !(import->flags & GF_IMPORT_NO_FRAME_DROP); + + while (gf_bs_available(bs)) { + u8 ftype; + u32 tinc, frame_start; + Bool is_coded; + pos = gf_m4v_get_object_start(vparse); + e = gf_m4v_parse_frame(vparse, dsi, &ftype, &tinc, &samp->dataLength, &frame_start, &is_coded); + if (e==GF_EOS) e = GF_OK; + if (e) goto exit; + + if (!is_coded) { + nbNotCoded ++; + /*if prev is B and we're parsing a packed bitstream discard n-vop*/ + if (forced_packed && b_frames) { + is_packed = 1; + continue; + } + /*policy is to import at variable frame rate, skip*/ + if (do_vfr) { + is_vfr = 1; + samp->DTS += dts_inc; + continue; + } + /*policy is to keep non coded frame (constant frame rate), add*/ + } + samp->IsRAP = 0; + + if (ftype==2) { + b_frames++; + nbB++; + /*adjust CTS*/ + if (!has_cts_offset) { + u32 i; + for (i=0; idest, track); i++) { + gf_isom_modify_cts_offset(import->dest, track, i+1, dts_inc); + } + has_cts_offset = 1; + } + } else { + if (ftype==0) { + samp->IsRAP = 1; + nbI++; + } else { + nbP++; + } + /*even if no out-of-order frames we MUST adjust CTS if cts_offset is present is */ + if (ref_frame && has_cts_offset) + gf_isom_modify_cts_offset(import->dest, track, ref_frame, (1+b_frames)*dts_inc); + + ref_frame = nb_samp+1; + if (max_bDTS = 0; + + if (import->flags & GF_IMPORT_USE_DATAREF) { + samp->data = NULL; + e = gf_isom_add_sample_reference(import->dest, track, di, samp, frame_start); + } else { + if (samp->dataLength>max_size) { + max_size = samp->dataLength; + samp->data = (char*)realloc(samp->data, sizeof(char)*max_size); + } + gf_bs_seek(bs, frame_start); + gf_bs_read_data(bs, samp->data, samp->dataLength); + e = gf_isom_add_sample(import->dest, track, di, samp); + } + samp->DTS += dts_inc; + nb_samp++; + gf_set_progress("Importing M4V", done_size/1024, tot_size/1024); + done_size += samp->dataLength; + if (e) break; + if (duration && (samp->DTS > duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + + } + /*final flush*/ + if (ref_frame && has_cts_offset) + gf_isom_modify_cts_offset(import->dest, track, ref_frame, (1+b_frames)*dts_inc); + + gf_set_progress("Importing M4V", nb_samp, nb_samp); + if (has_cts_offset) { + gf_import_message(import, GF_OK, "Has B-Frames (%d max consecutive B-VOPs)", max_b); + gf_isom_set_cts_packing(import->dest, track, 0); + /*this is plain ugly but since some encoders (divx) don't use the video PL correctly + we force the system video_pl to ASP@L5 since we have I, P, B in base layer*/ + if (PL<=3) { + PL = 0xF5; + erase_pl = 1; + gf_import_message(import, GF_OK, "WARNING: indicated profile doesn't include B-VOPs - forcing %s", gf_m4v_get_profile_name((u8) PL)); + } + gf_import_message(import, GF_OK, "Import results: %d VOPs (%d Is - %d Ps - %d Bs)", nb_samp, nbI, nbP, nbB); + } else { + /*no B-frames, remove CTS offsets*/ + gf_isom_remove_cts_info(import->dest, track); + gf_import_message(import, GF_OK, "Import results: %d VOPs (%d Is - %d Ps)", nb_samp, nbI, nbP); + } + if (erase_pl) { + gf_m4v_rewrite_pl(&import->esd->decoderConfig->decoderSpecificInfo->data, &import->esd->decoderConfig->decoderSpecificInfo->dataLength, (u8) PL); + gf_isom_change_mpeg4_description(import->dest, track, 1, import->esd); + } + MP4T_RecomputeBitRate(import->dest, track); + + if (is_vfr) { + if (!nbB) { + if (do_vfr) { + gf_import_message(import, GF_OK, "import using Variable Frame Rate - Removed %d N-VOPs", nbNotCoded); + } else { + if (nbNotCoded) gf_import_message(import, GF_OK, "Stream has %d N-VOPs", nbNotCoded); + } + nbNotCoded = 0; + } + } + + if (nbNotCoded) gf_import_message(import, GF_OK, "Removed %d N-VOPs%s", nbNotCoded,is_packed ? " (Packed Bitstream)" : ""); + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_VISUAL, (u8) PL); + + if (dsi.par_den && dsi.par_num) gf_media_change_par(import->dest, track, dsi.par_num, dsi.par_den); + +exit: + if (samp) gf_isom_sample_del(&samp); + if (destroy_esd) gf_odf_desc_del((GF_Descriptor *) import->esd); + + /*this destroys the bitstream as well*/ + gf_m4v_parser_del(vparse); + fclose(mdia); + return e; +} + +GF_Err gf_import_avi_video(GF_MediaImporter *import) +{ + GF_Err e; + Double FPS; + FILE *test; + GF_ISOSample *samp; + u32 i, num_samples, timescale, size, max_size, samp_offset, track, di, PL, max_b, nb_f, ref_frame, b_frames; + u32 nbI, nbP, nbB, nbDummy, nbNotCoded, dts_inc, cur_samp; + Bool is_vfr, enable_vfr, erase_pl; + GF_M4VDecSpecInfo dsi; + GF_M4VParser *vparse; + s32 key; + u32 duration; + Bool destroy_esd, is_packed, is_init, has_cts_offset; + char *comp, *frame; + avi_t *in; + + if (import->trackID>1) return GF_OK; + + test = fopen(import->in_name, "rb"); + if (!test) return gf_import_message(import, GF_URL_ERROR, "Opening %s failed", import->in_name); + fclose(test); + in = AVI_open_input_file(import->in_name, 1); + if (!in) return gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Unsupported avi file"); + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + char *comp; + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF | GF_IMPORT_NO_FRAME_DROP | GF_IMPORT_OVERRIDE_FPS; + import->tk_info[0].video_info.FPS = AVI_frame_rate(in); + import->tk_info[0].video_info.width = AVI_video_width(in); + import->tk_info[0].video_info.height = AVI_video_height(in); + comp = AVI_video_compressor(in); + import->tk_info[0].media_type = GF_4CC((u8)comp[0], (u8)comp[1], (u8)comp[2], (u8)comp[3]); + + import->nb_tracks = 1; + for (i=0; i<(u32) AVI_audio_tracks(in); i++) { + import->tk_info[i+1].track_num = i+2; + import->tk_info[i+1].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[i+1].flags = GF_IMPORT_USE_DATAREF; + import->tk_info[i+1].audio_info.sample_rate = AVI_audio_rate(in); + import->tk_info[i+1].audio_info.nb_channels = AVI_audio_channels(in); + import->nb_tracks ++; + } + AVI_close(in); + return GF_OK; + } + + destroy_esd = 0; + frame = NULL; + AVI_seek_start(in); + + erase_pl = 0; + comp = AVI_video_compressor(in); + if (!comp) { + e = GF_NOT_SUPPORTED; + goto exit; + } + + /*these are/should be OK*/ + if (!stricmp(comp, "DIVX") || !stricmp(comp, "DX50") /*DivX*/ + || !stricmp(comp, "XVID") /*XviD*/ + || !stricmp(comp, "3iv2") /*3ivX*/ + || !stricmp(comp, "fvfw") /*ffmpeg*/ + || !stricmp(comp, "NDIG") /*nero*/ + || !stricmp(comp, "MP4V") /*!! not tested*/ + || !stricmp(comp, "M4CC") /*Divio - not tested*/ + || !stricmp(comp, "PVMM") /*PacketVideo - not tested*/ + || !stricmp(comp, "SEDG") /*Samsung - not tested*/ + || !stricmp(comp, "RMP4") /*Sigma - not tested*/ + || !stricmp(comp, "MP43") /*not tested*/ + || !stricmp(comp, "FMP4") /*not tested*/ + ) { + e = GF_OK; + } + else if (!stricmp(comp, "DIV3") || !stricmp(comp, "DIV4")) { + gf_import_message(import, GF_NOT_SUPPORTED, "Video format %s not compliant with MPEG-4 Visual - please recompress the file first", comp); + e = GF_NOT_SUPPORTED; + goto exit; + } else if (!stricmp(comp, "H264") || !stricmp(comp, "X264")) { + gf_import_message(import, GF_NOT_SUPPORTED, "H264/AVC Video format not supported in AVI - please extract to raw format first", comp); + e = GF_NOT_SUPPORTED; + goto exit; + } else { + gf_import_message(import, GF_NOT_SUPPORTED, "Video format %s not supported - recompress the file first", comp); + e = GF_NOT_SUPPORTED; + goto exit; + } + /*no auto frame-rate detection*/ + if (import->video_fps == 10000.0) import->video_fps = 25.0; + + FPS = AVI_frame_rate(in); + if (import->video_fps) FPS = (Double) import->video_fps; + get_video_timing(FPS, ×cale, &dts_inc); + duration = (u32) (import->duration*FPS); + + e = GF_OK; + max_size = 0; + samp_offset = 0; + frame = NULL; + num_samples = AVI_video_frames(in); + samp = gf_isom_sample_new(); + PL = 0; + track = 0; + is_vfr = 0; + + is_packed = 0; + nbDummy = nbNotCoded = nbI = nbP = nbB = max_b = 0; + enable_vfr = 0; + has_cts_offset = 0; + cur_samp = b_frames = ref_frame = 0; + + is_init = 0; + + for (i=0; i max_size) { + frame = (char*)realloc(frame, sizeof(char) * size); + max_size = size; + } + AVI_read_frame(in, frame, &key); + + /*get DSI*/ + if (!is_init) { + is_init = 1; + vparse = gf_m4v_parser_new(frame, size, 0); + e = gf_m4v_parse_config(vparse, &dsi); + PL = dsi.VideoPL; + if (!PL) { + PL = 0x01; + erase_pl = 1; + } + samp_offset = gf_m4v_get_object_start(vparse); + gf_m4v_parser_del(vparse); + if (e) { + gf_import_message(import, e, "Cannot import decoder config in first frame"); + goto exit; + } + + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(0); + destroy_esd = 1; + } + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_VISUAL, timescale); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = gf_isom_get_track_id(import->dest, track); + + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->timestampResolution = timescale; + + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (import->esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) import->esd->decoderConfig->decoderSpecificInfo); + import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + import->esd->decoderConfig->streamType = GF_STREAM_VISUAL; + import->esd->decoderConfig->objectTypeIndication = 0x20; + import->esd->decoderConfig->decoderSpecificInfo->data = (char *) malloc(sizeof(char) * samp_offset); + memcpy(import->esd->decoderConfig->decoderSpecificInfo->data, frame, sizeof(char) * samp_offset); + import->esd->decoderConfig->decoderSpecificInfo->dataLength = samp_offset; + + gf_isom_set_cts_packing(import->dest, track, 1); + + /*remove packed flag if any (VOSH user data)*/ + while (1) { + char *divx_mark; + while ((i+3=samp_offset) break; + + if (strncmp(frame+i+4, "DivX", 4)) { + i += 4; + continue; + } + divx_mark = import->esd->decoderConfig->decoderSpecificInfo->data + i + 4; + divx_mark = strchr(divx_mark, 'p'); + if (divx_mark) divx_mark[0] = 'n'; + break; + } + i = 0; + + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name: NULL, NULL, &di); + if (e) goto exit; + gf_isom_set_visual_info(import->dest, track, di, dsi.width, dsi.height); + gf_import_message(import, GF_OK, "AVI %s video import - %d x %d @ %02.4f FPS - %d Frames\nIndicated Profile: %s", comp, dsi.width, dsi.height, FPS, num_samples, gf_m4v_get_profile_name((u8) PL)); + + gf_media_update_par(import->dest, track); + } + + + if (size > samp_offset) { + u8 ftype; + u32 tinc, framesize, frame_start; + u64 file_offset; + Bool is_coded; + + size -= samp_offset; + file_offset = (u64) AVI_get_video_position(in, i); + + vparse = gf_m4v_parser_new(frame + samp_offset, size, 0); + + samp->dataLength = 0; + /*removing padding frames*/ + if (size<4) { + nbDummy ++; + size = 0; + } + + nb_f=0; + while (size) { + GF_Err e = gf_m4v_parse_frame(vparse, dsi, &ftype, &tinc, &framesize, &frame_start, &is_coded); + if (e<0) goto exit; + + if (!is_coded) { + if (!gf_m4v_is_valid_object_type(vparse)) gf_import_message(import, GF_OK, "WARNING: AVI frame %d doesn't look like MPEG-4 Visual", i+1); + nbNotCoded ++; + if (!is_packed) { + is_vfr = 1; + /*policy is to import at constant frame rate from AVI*/ + if (import->flags & GF_IMPORT_NO_FRAME_DROP) goto proceed; + /*policy is to import at variable frame rate from AVI*/ + samp->DTS += dts_inc; + } + /*assume this is packed bitstream n-vop and discard it.*/ + } else { +proceed: + if (e==GF_EOS) size = 0; + else is_packed = 1; + nb_f++; + + samp->IsRAP = 0; + + if (ftype==2) { + b_frames ++; + nbB++; + /*adjust CTS*/ + if (!has_cts_offset) { + u32 i; + for (i=0; idest, track); i++) { + gf_isom_modify_cts_offset(import->dest, track, i+1, dts_inc); + } + has_cts_offset = 1; + } + } else { + if (!ftype) { + samp->IsRAP = 1; + nbI++; + } else { + nbP++; + } + /*even if no out-of-order frames we MUST adjust CTS if cts_offset is present is */ + if (ref_frame && has_cts_offset) + gf_isom_modify_cts_offset(import->dest, track, ref_frame, (1+b_frames)*dts_inc); + + ref_frame = cur_samp+1; + if (max_bdata = frame + samp_offset + frame_start; + samp->dataLength = framesize; + + if (import->flags & GF_IMPORT_USE_DATAREF) { + samp->data = NULL; + e = gf_isom_add_sample_reference(import->dest, track, di, samp, file_offset + samp_offset + frame_start); + } else { + e = gf_isom_add_sample(import->dest, track, di, samp); + } + cur_samp++; + samp->DTS += dts_inc; + if (e) { + gf_import_message(import, GF_OK, "Error importing AVI frame %d", i+1); + goto exit; + } + } + if (!size || (size == framesize + frame_start)) break; + } + gf_m4v_parser_del(vparse); + if (nb_f>2) gf_import_message(import, GF_OK, "Warning: more than 2 frames packed together"); + } + samp_offset = 0; + gf_set_progress("Importing AVI Video", i, num_samples); + if (duration && (samp->DTS > duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) + break; + } + + /*final flush*/ + if (ref_frame && has_cts_offset) + gf_isom_modify_cts_offset(import->dest, track, ref_frame, (1+b_frames)*dts_inc); + + gf_set_progress("Importing AVI Video", num_samples, num_samples); + + + num_samples = gf_isom_get_sample_count(import->dest, track); + if (has_cts_offset) { + gf_import_message(import, GF_OK, "Has B-Frames (%d max consecutive B-VOPs%s)", max_b, is_packed ? " - packed bitstream" : ""); + /*repack CTS tables and adjust offsets for B-frames*/ + gf_isom_set_cts_packing(import->dest, track, 0); + /*this is plain ugly but since some encoders (divx) don't use the video PL correctly + we force the system video_pl to ASP@L5 since we have I, P, B in base layer*/ + if (PL<=3) { + PL = 0xF5; + erase_pl = 1; + gf_import_message(import, GF_OK, "WARNING: indicated profile doesn't include B-VOPs - forcing %s", gf_m4v_get_profile_name((u8) PL)); + } + gf_import_message(import, GF_OK, "Import results: %d VOPs (%d Is - %d Ps - %d Bs)", num_samples, nbI, nbP, nbB); + } else { + /*no B-frames, remove CTS offsets*/ + gf_isom_remove_cts_info(import->dest, track); + gf_import_message(import, GF_OK, "Import results: %d VOPs (%d Is - %d Ps)", num_samples, nbI, nbP); + } + + samp->data = NULL; + gf_isom_sample_del(&samp); + + if (erase_pl) { + gf_m4v_rewrite_pl(&import->esd->decoderConfig->decoderSpecificInfo->data, &import->esd->decoderConfig->decoderSpecificInfo->dataLength, (u8) PL); + gf_isom_change_mpeg4_description(import->dest, track, 1, import->esd); + } + MP4T_RecomputeBitRate(import->dest, track); + + if (is_vfr) { + if (nbB) { + if (is_packed) gf_import_message(import, GF_OK, "Warning: Mix of non-coded frames: packed bitstream and encoder skiped - unpredictable timing"); + } else { + if (import->flags & GF_IMPORT_NO_FRAME_DROP) { + if (nbNotCoded) gf_import_message(import, GF_OK, "Stream has %d N-VOPs", nbNotCoded); + } else { + gf_import_message(import, GF_OK, "import using Variable Frame Rate - Removed %d N-VOPs", nbNotCoded); + } + nbNotCoded = 0; + } + } + + if (nbDummy || nbNotCoded) gf_import_message(import, GF_OK, "Removed Frames: %d VFW delay frames - %d N-VOPs", nbDummy, nbNotCoded); + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_VISUAL, (u8) PL); + +exit: + if (destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + if (frame) free(frame); + AVI_close(in); + return e; +} + + + + +/*credits to CISCO MPEG4/IP for MP3 parsing*/ +GF_Err gf_import_avi_audio(GF_MediaImporter *import) +{ + GF_Err e; + FILE *test; + u32 hdr, di, track, i, tot_size, duration; + s64 offset; + s32 size, max_size, done; + u16 sampleRate; + Double dur; + Bool is_cbr; + u8 oti; + GF_ISOSample *samp; + char *frame; + Bool destroy_esd; + s32 continuous; + unsigned char temp[4]; + avi_t *in; + + if (import->flags & GF_IMPORT_PROBE_ONLY) return GF_OK; + + /*video only, ignore*/ + if (import->trackID==1) return GF_OK; + + test = fopen(import->in_name, "rb"); + if (!test) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + fclose(test); + in = AVI_open_input_file(import->in_name, 1); + if (!in) return gf_import_message(import, GF_NOT_SUPPORTED, "Unsupported avi file"); + + AVI_seek_start(in); + + e = GF_OK; + if (import->trackID) AVI_set_audio_track(in, import->trackID-2); + + if (AVI_read_audio(in, (char *) temp, 4, &continuous) != 4) { + AVI_close(in); + return gf_import_message(import, GF_OK, "No audio track found"); + } + + hdr = GF_4CC(temp[0], temp[1], temp[2], temp[3]); + if ((hdr &0xFFE00000) != 0xFFE00000) { + AVI_close(in); + return gf_import_message(import, GF_NOT_SUPPORTED, "Unsupported AVI audio format"); + } + + sampleRate = gf_mp3_sampling_rate(hdr); + oti = gf_mp3_object_type_indication(hdr); + if (!oti || !sampleRate) { + AVI_close(in); + return gf_import_message(import, GF_NOT_SUPPORTED, "Error: invalid MPEG Audio track"); + } + + frame = NULL; + destroy_esd = 0; + if (!import->esd) { + destroy_esd = 1; + import->esd = gf_odf_desc_esd_new(0); + } + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_AUDIO, sampleRate); + if (!track) goto exit; + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->timestampResolution = sampleRate; + if (import->esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) import->esd->decoderConfig->decoderSpecificInfo); + import->esd->decoderConfig->decoderSpecificInfo = NULL; + import->esd->decoderConfig->streamType = GF_STREAM_AUDIO; + import->esd->decoderConfig->objectTypeIndication = oti; + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + if (e) goto exit; + + gf_import_message(import, GF_OK, "AVI Audio import - sample rate %d - %s audio - %d channel%s", sampleRate, (oti==0x6B) ? "MPEG-1" : "MPEG-2", gf_mp3_num_channels(hdr), (gf_mp3_num_channels(hdr)>1) ? "s" : ""); + + AVI_seek_start(in); + AVI_set_audio_position(in, 0); + + i = 0; + tot_size = max_size = 0; + while ((size = AVI_audio_size(in, i) )>0) { + if (max_sizeduration; + dur *= sampleRate; + dur /= 1000; + duration = (u32) dur; + + samp = gf_isom_sample_new(); + done=max_size=0; + is_cbr = 1; + while (1) { + if (AVI_read_audio(in, frame, 4, (int*)&continuous) != 4) break; + offset = gf_f64_tell(in->fdes) - 4; + hdr = GF_4CC((u8) frame[0], (u8) frame[1], (u8) frame[2], (u8) frame[3]); + + size = gf_mp3_frame_size(hdr); + if (size>max_size) { + frame = (char*)realloc(frame, sizeof(char) * size); + if (max_size) is_cbr = 0; + max_size = size; + } + size = 4 + AVI_read_audio(in, &frame[4], size - 4, &continuous); + + if ((import->flags & GF_IMPORT_USE_DATAREF) && !continuous) { + gf_import_message(import, GF_IO_ERR, "Cannot use media references, splited input audio frame found"); + e = GF_IO_ERR; + goto exit; + } + samp->IsRAP = 1; + samp->data = frame; + samp->dataLength = size; + if (import->flags & GF_IMPORT_USE_DATAREF) { + e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset); + } else { + e = gf_isom_add_sample(import->dest, track, di, samp); + } + if (e) goto exit; + + samp->DTS += gf_mp3_window_size(hdr); + gf_set_progress("Importing AVI Audio", done, tot_size); + + done += size; + if (duration && (samp->DTS > duration) ) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + } + + gf_set_progress("Importing AVI Audio", tot_size, tot_size); + + gf_import_message(import, GF_OK, "Import done - %s bit rate MP3 detected", is_cbr ? "constant" : "variable"); + samp->data = NULL; + gf_isom_sample_del(&samp); + + MP4T_RecomputeBitRate(import->dest, track); + + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_AUDIO, 0xFE); + + +exit: + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + if (frame) free(frame); + AVI_close(in); + return e; +} + +GF_Err gf_import_isomedia(GF_MediaImporter *import) +{ + GF_Err e; + u64 offset, sampDTS; + u32 track, di, trackID, track_in, i, num_samples, mtype, stype, w, h, duration, sr, sbr_sr, ch, mstype; + s32 trans_x, trans_y; + s16 layer; + u8 bps; + char lang[4]; + const char *url, *urn; + Bool sbr, is_clone; + GF_ISOSample *samp; + GF_ESD *origin_esd; + GF_InitialObjectDescriptor *iod; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + for (i=0; iorig); i++) { + import->tk_info[i].track_num = gf_isom_get_track_id(import->orig, i+1); + import->tk_info[i].type = gf_isom_get_media_type(import->orig, i+1); + import->tk_info[i].flags = GF_IMPORT_USE_DATAREF; + if (import->tk_info[i].type == GF_ISOM_MEDIA_VISUAL) { + gf_isom_get_visual_info(import->orig, i+1, 1, &import->tk_info[i].video_info.width, &import->tk_info[i].video_info.height); + } else if (import->tk_info[i].type == GF_ISOM_MEDIA_AUDIO) { + gf_isom_get_audio_info(import->orig, i+1, 1, &import->tk_info[i].audio_info.sample_rate, &import->tk_info[i].audio_info.nb_channels, NULL); + } + lang[3] = 0; + gf_isom_get_media_language(import->orig, i+1, lang); + import->tk_info[i].lang = GF_4CC(' ', lang[0], lang[1], lang[2]); + import->nb_tracks ++; + } + return GF_OK; + } + + trackID = import->trackID; + if (!trackID) { + if (gf_isom_get_track_count(import->orig) != 1) return gf_import_message(import, GF_BAD_PARAM, "Several tracks in MP4 - please indicate track to import"); + trackID = gf_isom_get_track_id(import->orig, 1); + } + track_in = gf_isom_get_track_by_id(import->orig, trackID); + if (!track_in) return gf_import_message(import, GF_URL_ERROR, "Cannot find track ID %d in file", trackID); + + origin_esd = gf_isom_get_esd(import->orig, track_in, 1); + + e = GF_OK; + if (import->esd && origin_esd) { + origin_esd->OCRESID = import->esd->OCRESID; + /*there may be other things to import...*/ + } + sbr = 0; + sbr_sr = 0; + iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(import->orig); + if (iod && (iod->tag != GF_ODF_IOD_TAG)) { + gf_odf_desc_del((GF_Descriptor *) iod); + iod = NULL; + } + mtype = gf_isom_get_media_type(import->orig, track_in); + if (mtype==GF_ISOM_MEDIA_VISUAL) { + u8 PL = iod ? iod->visual_profileAndLevel : 0xFE; + w = h = 0; + gf_isom_get_visual_info(import->orig, track_in, 1, &w, &h); + /*for MPEG-4 visual, always check size (don't trust input file)*/ + if (origin_esd && (origin_esd->decoderConfig->objectTypeIndication==0x20)) { + GF_M4VDecSpecInfo dsi; + gf_m4v_get_config(origin_esd->decoderConfig->decoderSpecificInfo->data, origin_esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + w = dsi.width; + h = dsi.height; + PL = dsi.VideoPL; + } + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_VISUAL, PL); + } + else if (mtype==GF_ISOM_MEDIA_AUDIO) { + u8 PL = iod ? iod->audio_profileAndLevel : 0xFE; + bps = 16; + sr = ch = sbr_sr = 0; + sbr = 0; + gf_isom_get_audio_info(import->orig, track_in, 1, &sr, &ch, &bps); + if (origin_esd && (origin_esd->decoderConfig->objectTypeIndication==0x40)) { + GF_M4ADecSpecInfo dsi; + gf_m4a_get_config(origin_esd->decoderConfig->decoderSpecificInfo->data, origin_esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + sr = dsi.base_sr; + if (dsi.has_sbr) sbr_sr = dsi.sbr_sr; + ch = dsi.nb_chan; + PL = dsi.audioPL; + sbr = dsi.has_sbr ? ((dsi.base_object_type==GF_M4A_AAC_SBR) ? 2 : 1) : 0; + } + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_AUDIO, PL); + } + else if (mtype==GF_ISOM_MEDIA_SUBPIC) { + w = h = 0; + trans_x = trans_y = 0; + layer = 0; + if (origin_esd && origin_esd->decoderConfig->objectTypeIndication == 0xe0) { + gf_isom_get_track_layout_info(import->orig, track_in, &w, &h, &trans_x, &trans_y, &layer); + } + } + + gf_odf_desc_del((GF_Descriptor *) iod); + + /*check if MPEG-4 or not*/ + is_clone = 0; + stype = gf_isom_get_media_subtype(import->orig, track_in, 1); + if ((stype==GF_ISOM_SUBTYPE_MPEG4) || (stype==GF_ISOM_SUBTYPE_MPEG4_CRYP)) { + track = gf_isom_new_track(import->dest, import->esd ? import->esd->ESID : 0, gf_isom_get_media_type(import->orig, track_in), gf_isom_get_media_timescale(import->orig, track_in)); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (import->esd && !import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + /*setup data ref*/ + urn = url = NULL; + if (import->flags & GF_IMPORT_USE_DATAREF) { + url = gf_isom_get_filename(import->orig); + if (!gf_isom_is_self_contained(import->orig, track_in, 1)) { + e = gf_isom_get_data_reference(import->orig, track_in, 1, &url, &urn); + if (e) goto exit; + } + } + e = gf_isom_new_mpeg4_description(import->dest, track, origin_esd, (char *) url, (char *) urn, &di); + if (e) goto exit; + /*copy over language*/ + lang[3] = 0; + gf_isom_get_media_language(import->orig, track_in, lang); + gf_isom_set_media_language(import->dest, track, lang); + + } else { + if (! (import->flags & GF_IMPORT_KEEP_ALL_TRACKS) ) { + mstype = gf_isom_get_media_subtype(import->orig, track_in, 1); + switch (mstype) { + case GF_ISOM_SUBTYPE_MPEG4: + case GF_ISOM_SUBTYPE_MPEG4_CRYP: + case GF_ISOM_SUBTYPE_AVC_H264: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + break; + default: + switch (mtype) { + case GF_ISOM_MEDIA_HINT: + case GF_ISOM_MEDIA_TEXT: + break; + default: + return gf_import_message(import, GF_OK, "IsoMedia import - skipping track ID %d (unknown type \'%s\')", trackID, gf_4cc_to_str(mstype)); + } + } + + } + e = gf_isom_clone_track(import->orig, track_in, import->dest, (import->flags & GF_IMPORT_USE_DATAREF), &track); + is_clone = 1; + di = 1; + if (e) goto exit; + } + if (e) goto exit; + import->final_trackID = gf_isom_get_track_id(import->dest, track); + + switch (mtype) { + case GF_ISOM_MEDIA_VISUAL: + if (!is_clone) { + gf_isom_set_visual_info(import->dest, track, di, w, h); + gf_media_update_par(import->dest, track); + } + gf_import_message(import, GF_OK, "IsoMedia import - track ID %d - Video (size %d x %d)", trackID, w, h); + break; + case GF_ISOM_MEDIA_AUDIO: + { + if (!is_clone) gf_isom_set_audio_info(import->dest, track, di, (sbr==2) ? sbr_sr : sr, (ch>1) ? 2 : 1, bps); + if (sbr) { + gf_import_message(import, GF_OK, "IsoMedia import - track ID %d - HE-AAC (SR %d - SBR-SR %d - %d channels)", trackID, sr, sbr_sr, ch); + } else { + gf_import_message(import, GF_OK, "IsoMedia import - track ID %d - Audio (SR %d - %d channels)", trackID, sr, ch); + } + } + break; + case GF_ISOM_MEDIA_SUBPIC: + if (!is_clone) { + gf_isom_set_track_layout_info(import->dest, track, w << 16, h << 16, trans_x, trans_y, layer); + } + gf_import_message(import, GF_OK, "IsoMedia import - track ID %d - VobSub (size %d x %d)", trackID, w, h); + break; + default: + { + char szT[5]; + mstype = gf_isom_get_mpeg4_subtype(import->orig, track_in, di); + if (!mstype) mstype = gf_isom_get_media_subtype(import->orig, track_in, di); + strcpy(szT, gf_4cc_to_str(mtype)); + gf_import_message(import, GF_OK, "IsoMedia import - track ID %d - media type \"%s:%s\"", + trackID, szT, gf_4cc_to_str(mstype)); + } + break; + } + + duration = (u32) (((Double)import->duration * gf_isom_get_media_timescale(import->orig, track_in)) / 1000); + + num_samples = gf_isom_get_sample_count(import->orig, track_in); + for (i=0; iflags & GF_IMPORT_USE_DATAREF) { + samp = gf_isom_get_sample_info(import->orig, track_in, i+1, &di, &offset); + if (!samp) { + e = gf_isom_last_error(import->orig); + goto exit; + } + e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset); + } else { + samp = gf_isom_get_sample(import->orig, track_in, i+1, &di); + e = gf_isom_add_sample(import->dest, track, di, samp); + } + sampDTS = samp->DTS; + gf_isom_sample_del(&samp); + gf_set_progress("Importing ISO File", i+1, num_samples); + if (duration && (sampDTS > duration) ) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + if (e) goto exit; + } + + if (import->esd) { + if (!import->esd->slConfig) { + import->esd->slConfig = origin_esd ? origin_esd->slConfig : NULL; + if (origin_esd) origin_esd->slConfig = NULL; + } + if (!import->esd->decoderConfig) { + import->esd->decoderConfig = origin_esd ? origin_esd->decoderConfig : NULL; + if (origin_esd) origin_esd->decoderConfig = NULL; + } + } + + MP4T_RecomputeBitRate(import->dest, track); + +exit: + if (origin_esd) gf_odf_desc_del((GF_Descriptor *) origin_esd); + return e; +} + +#include "mpeg2_ps.h" + +GF_Err gf_import_mpeg_ps_video(GF_MediaImporter *import) +{ + GF_Err e; + mpeg2ps_t *ps; + Double FPS; + char *buf; + u8 ftype; + u32 track, di, streamID, mtype, w, h, ar, nb_streams, buf_len, frames, ref_frame, timescale, duration, file_size, dts_inc, last_pos; + Bool destroy_esd; + + if (import->flags & GF_IMPORT_USE_DATAREF) + return gf_import_message(import, GF_NOT_SUPPORTED, "Cannot use data referencing with MPEG-1/2 files"); + + /*no auto frame-rate detection*/ + if (import->video_fps == 10000.0) import->video_fps = 25.0; + + ps = mpeg2ps_init(import->in_name); + if (!ps) return gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Failed to open MPEG file %s", import->in_name); + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + u32 i, nb_v_str; + import->nb_tracks = 0; + nb_v_str = nb_streams = mpeg2ps_get_video_stream_count(ps); + for (i=0; itk_info[import->nb_tracks].track_num = i+1; + import->tk_info[import->nb_tracks].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[import->nb_tracks].flags = GF_IMPORT_OVERRIDE_FPS; + + import->tk_info[import->nb_tracks].video_info.FPS = mpeg2ps_get_video_stream_framerate(ps, i); + import->tk_info[import->nb_tracks].video_info.width = mpeg2ps_get_video_stream_width(ps, i); + import->tk_info[import->nb_tracks].video_info.height = mpeg2ps_get_video_stream_height(ps, i); + import->tk_info[import->nb_tracks].video_info.par = mpeg2ps_get_video_stream_aspect_ratio(ps, i); + + import->tk_info[import->nb_tracks].media_type = GF_4CC('M', 'P', 'G', '1'); + if (mpeg2ps_get_video_stream_type(ps, i) == MPEG_VIDEO_MPEG2) import->tk_info[import->nb_tracks].media_type ++; + + import->nb_tracks++; + } + nb_streams = mpeg2ps_get_audio_stream_count(ps); + for (i=0; itk_info[import->nb_tracks].track_num = nb_v_str + i+1; + import->tk_info[import->nb_tracks].type = GF_ISOM_MEDIA_AUDIO; + switch (mpeg2ps_get_audio_stream_type(ps, i)) { + case MPEG_AUDIO_MPEG: import->tk_info[import->nb_tracks].media_type = GF_4CC('M','P','G','A'); break; + case MPEG_AUDIO_AC3: import->tk_info[import->nb_tracks].media_type = GF_4CC('A','C','3',' '); break; + case MPEG_AUDIO_LPCM: import->tk_info[import->nb_tracks].media_type = GF_4CC('L','P','C','M'); break; + default: import->tk_info[import->nb_tracks].media_type = GF_4CC('U','N','K',' '); break; + } + import->tk_info[import->nb_tracks].audio_info.sample_rate = mpeg2ps_get_audio_stream_sample_freq(ps, i); + import->tk_info[import->nb_tracks].audio_info.nb_channels = mpeg2ps_get_audio_stream_channels(ps, i); + import->nb_tracks ++; + } + mpeg2ps_close(ps); + return GF_OK; + } + + + streamID = 0; + nb_streams = mpeg2ps_get_video_stream_count(ps); + if ((nb_streams>1) && !import->trackID) { + mpeg2ps_close(ps); + return gf_import_message(import, GF_BAD_PARAM, "%d video tracks in MPEG file - please indicate track to import", nb_streams); + } + /*audio*/ + if (import->trackID>nb_streams) { + mpeg2ps_close(ps); + return GF_OK; + } + if (import->trackID) streamID = import->trackID - 1; + + if (streamID>=nb_streams) { + mpeg2ps_close(ps); + return gf_import_message(import, GF_BAD_PARAM, "Desired video track not found in MPEG file (%d visual streams)", nb_streams); + } + w = mpeg2ps_get_video_stream_width(ps, streamID); + h = mpeg2ps_get_video_stream_height(ps, streamID); + ar = mpeg2ps_get_video_stream_aspect_ratio(ps, streamID); + mtype = (mpeg2ps_get_video_stream_type(ps, streamID) == MPEG_VIDEO_MPEG2) ? 0x61 : 0x6A; + FPS = mpeg2ps_get_video_stream_framerate(ps, streamID); + if (import->video_fps) FPS = (Double) import->video_fps; + get_video_timing(FPS, ×cale, &dts_inc); + + duration = import->duration*timescale; + duration /= 1000; + + destroy_esd = 0; + if (!import->esd) { + destroy_esd = 1; + import->esd = gf_odf_desc_esd_new(0); + } + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_VISUAL, timescale); + e = gf_isom_last_error(import->dest); + if (!track) goto exit; + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->timestampResolution = timescale; + if (import->esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) import->esd->decoderConfig->decoderSpecificInfo); + import->esd->decoderConfig->decoderSpecificInfo = NULL; + import->esd->decoderConfig->streamType = GF_STREAM_VISUAL; + import->esd->decoderConfig->objectTypeIndication = mtype; + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, NULL, NULL, &di); + if (e) goto exit; + + gf_import_message(import, GF_OK, "%s Video import - Resolution %d x %d @ %02.4f FPS", (mtype==0x6A) ? "MPEG-1" : "MPEG-2", w, h, FPS); + gf_isom_set_visual_info(import->dest, track, di, w, h); + + gf_isom_set_cts_packing(import->dest, track, 1); + + file_size = (u32) mpeg2ps_get_ps_size(ps); + last_pos = 0; + frames = 1; + ref_frame = 1; + while (mpeg2ps_get_video_frame(ps, streamID, (u8 **) &buf, &buf_len, &ftype, TS_90000, NULL)) { + GF_ISOSample *samp; + if ((buf[buf_len - 4] == 0) && (buf[buf_len - 3] == 0) && (buf[buf_len - 2] == 1)) buf_len -= 4; + samp = gf_isom_sample_new(); + samp->data = buf; + samp->dataLength = buf_len; + samp->DTS = dts_inc*(frames-1); + samp->IsRAP = (ftype==1) ? 1 : 0; + samp->CTS_Offset = 0; + e = gf_isom_add_sample(import->dest, track, di, samp); + samp->data = NULL; + gf_isom_sample_del(&samp); + if (e) goto exit; + + last_pos = (u32) mpeg2ps_get_video_pos(ps, streamID); + gf_set_progress("Importing MPEG-PS Video", last_pos/1024, file_size/1024); + + if (ftype != 3) { + gf_isom_modify_cts_offset(import->dest, track, ref_frame, (frames-ref_frame)*dts_inc); + ref_frame = frames; + } + frames++; + + if (duration && (dts_inc*(frames-1) >= duration) ) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + } + gf_isom_set_cts_packing(import->dest, track, 0); + if (last_pos!=file_size) gf_set_progress("Importing MPEG-PS Video", frames, frames); + + MP4T_RecomputeBitRate(import->dest, track); + if (ar) gf_media_change_par(import->dest, track, ar>>16, ar&0xffff); + +exit: + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + mpeg2ps_close(ps); + return e; +} + +GF_Err gf_import_mpeg_ps_audio(GF_MediaImporter *import) +{ + GF_Err e; + mpeg2ps_t *ps; + char *buf; + u32 track, di, streamID, mtype, sr, nb_ch, nb_streams, buf_len, frames, hdr, duration, file_size, last_pos; + Bool destroy_esd; + GF_ISOSample *samp; + + if (import->flags & GF_IMPORT_PROBE_ONLY) return GF_OK; + + if (import->flags & GF_IMPORT_USE_DATAREF) + return gf_import_message(import, GF_NOT_SUPPORTED, "Cannot use data referencing with MPEG-1/2 files"); + + ps = mpeg2ps_init(import->in_name); + if (!ps) return gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Failed to open MPEG file %s", import->in_name); + + + streamID = 0; + nb_streams = mpeg2ps_get_audio_stream_count(ps); + if ((nb_streams>1) && !import->trackID) { + mpeg2ps_close(ps); + return gf_import_message(import, GF_BAD_PARAM, "%d audio tracks in MPEG file - please indicate track to import", nb_streams); + } + + if (import->trackID) { + u32 nb_v = mpeg2ps_get_video_stream_count(ps); + /*video*/ + if (import->trackID<=nb_v) { + mpeg2ps_close(ps); + return GF_OK; + } + streamID = import->trackID - 1 - nb_v; + } + + if (streamID>=nb_streams) { + mpeg2ps_close(ps); + return gf_import_message(import, GF_BAD_PARAM, "Desired audio track not found in MPEG file (%d audio streams)", nb_streams); + } + + mtype = mpeg2ps_get_audio_stream_type(ps, streamID); + if (mtype != MPEG_AUDIO_MPEG) { + mpeg2ps_close(ps); + return gf_import_message(import, GF_NOT_SUPPORTED, "Audio format not supported in MP4"); + } + + if (mpeg2ps_get_audio_frame(ps, streamID, (u8**) &buf, &buf_len, TS_90000, NULL, NULL) == 0) { + mpeg2ps_close(ps); + return gf_import_message(import, GF_IO_ERR, "Cannot fetch audio frame from MPEG file"); + } + + hdr = GF_4CC((u8)buf[0],(u8)buf[1],(u8)buf[2],(u8)buf[3]); + mtype = gf_mp3_object_type_indication(hdr); + sr = gf_mp3_sampling_rate(hdr); + nb_ch = gf_mp3_num_channels(hdr); + + destroy_esd = 0; + if (!import->esd) { + destroy_esd = 1; + import->esd = gf_odf_desc_esd_new(0); + } + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_AUDIO, sr); + e = gf_isom_last_error(import->dest); + if (!track) goto exit; + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->timestampResolution = sr; + if (import->esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) import->esd->decoderConfig->decoderSpecificInfo); + import->esd->decoderConfig->decoderSpecificInfo = NULL; + import->esd->decoderConfig->streamType = GF_STREAM_AUDIO; + import->esd->decoderConfig->objectTypeIndication = mtype; + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, NULL, NULL, &di); + if (e) goto exit; + + gf_isom_set_audio_info(import->dest, track, di, sr, nb_ch, 16); + gf_import_message(import, GF_OK, "%s Audio import - sample rate %d - %d channel%s", (mtype==0x6B) ? "MPEG-1" : "MPEG-2", sr, nb_ch, (nb_ch>1) ? "s" : ""); + + + duration = (u32) ((Double)import->duration/1000.0 * sr); + + samp = gf_isom_sample_new(); + samp->IsRAP = 1; + samp->DTS = 0; + + file_size = (u32) mpeg2ps_get_ps_size(ps); + last_pos = 0; + frames = 0; + do { + samp->data = buf; + samp->dataLength = buf_len; + e = gf_isom_add_sample(import->dest, track, di, samp); + if (e) goto exit; + samp->DTS += gf_mp3_window_size(hdr); + last_pos = (u32) mpeg2ps_get_audio_pos(ps, streamID); + gf_set_progress("Importing MPEG-PS Audio", last_pos/1024, file_size/1024); + frames++; + if (duration && (samp->DTS>=duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + } while (mpeg2ps_get_audio_frame(ps, streamID, (u8**)&buf, &buf_len, TS_90000, NULL, NULL)); + + samp->data = NULL; + gf_isom_sample_del(&samp); + if (last_pos!=file_size) gf_set_progress("Importing MPEG-PS Audio", frames, frames); + MP4T_RecomputeBitRate(import->dest, track); + +exit: + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + mpeg2ps_close(ps); + return e; +} + +GF_Err gf_import_nhnt(GF_MediaImporter *import) +{ + GF_Err e; + Bool destroy_esd, next_is_start; + u32 track, di, mtype, max_size, duration, count, w, h, sig; + GF_ISOSample *samp; + s64 media_size, media_done; + u64 offset; + GF_BitStream *bs; + FILE *nhnt, *mdia, *info; + char *ext, szName[1000], szMedia[1000], szNhnt[1000]; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->nb_tracks = 1; + import->tk_info[0].track_num = 1; + import->tk_info[0].type = 0; + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF; + return GF_OK; + } + + strcpy(szName, import->in_name); + ext = strrchr(szName, '.'); + if (ext) ext[0] = 0; + + strcpy(szMedia, szName); + strcat(szMedia, ".nhnt"); + nhnt = fopen(szMedia, "rb"); + if (!nhnt) return gf_import_message(import, GF_URL_ERROR, "Cannot find NHNT file %s", szMedia); + + strcpy(szMedia, szName); + strcat(szMedia, ".media"); + mdia = gf_f64_open(szMedia, "rb"); + if (!mdia) { + fclose(nhnt); + return gf_import_message(import, GF_URL_ERROR, "Cannot find MEDIA file %s", szMedia); + } + + + e = GF_OK; + destroy_esd = 0; + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(2); + destroy_esd = 1; + } + /*update stream type/oti*/ + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + + + strcpy(szNhnt, szName); + strcat(szNhnt, ".info"); + info = fopen(szNhnt, "rb"); + if (info) { + if (import->esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) import->esd->decoderConfig->decoderSpecificInfo); + import->esd->decoderConfig->decoderSpecificInfo = NULL; + import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + fseek(info, 0, SEEK_END); + import->esd->decoderConfig->decoderSpecificInfo->dataLength = (u32) ftell(info); + import->esd->decoderConfig->decoderSpecificInfo->data = (char*)malloc(sizeof(char) * import->esd->decoderConfig->decoderSpecificInfo->dataLength); + fseek(info, 0, SEEK_SET); + fread(import->esd->decoderConfig->decoderSpecificInfo->data, import->esd->decoderConfig->decoderSpecificInfo->dataLength, 1, info); + fclose(info); + } + /*keep parsed dsi (if any) if no .info file exists*/ + + bs = gf_bs_from_file(nhnt, GF_BITSTREAM_READ); + sig = GF_4CC(gf_bs_read_u8(bs), gf_bs_read_u8(bs), gf_bs_read_u8(bs), gf_bs_read_u8(bs)); + if (sig == GF_4CC('N','H','n','t')) sig = 0; + else if (sig == GF_4CC('N','H','n','l')) sig = 1; + else { + gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Invalid NHNT signature"); + e = GF_NON_COMPLIANT_BITSTREAM; + goto exit; + } + /*version*/ + gf_bs_read_u8(bs); + import->esd->decoderConfig->streamType = gf_bs_read_u8(bs); + import->esd->decoderConfig->objectTypeIndication = gf_bs_read_u8(bs); + gf_bs_read_u16(bs); + import->esd->decoderConfig->bufferSizeDB = gf_bs_read_u24(bs); + import->esd->decoderConfig->avgBitrate = gf_bs_read_u32(bs); + import->esd->decoderConfig->maxBitrate = gf_bs_read_u32(bs); + import->esd->slConfig->timestampResolution = gf_bs_read_u32(bs); + + w = h = 0; + switch (import->esd->decoderConfig->streamType) { + case GF_STREAM_SCENE: + mtype = GF_ISOM_MEDIA_SCENE; + /*we don't know PLs from NHNT...*/ + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_SCENE, 0xFE); + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_GRAPHICS, 0xFE); + break; + case GF_STREAM_VISUAL: + mtype = GF_ISOM_MEDIA_VISUAL; + if (import->esd->decoderConfig->objectTypeIndication==0x20) { + GF_M4VDecSpecInfo dsi; + if (!import->esd->decoderConfig->decoderSpecificInfo) { + e = GF_NON_COMPLIANT_BITSTREAM; + goto exit; + } + e = gf_m4v_get_config(import->esd->decoderConfig->decoderSpecificInfo->data, import->esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + if (e) goto exit; + w = dsi.width; + h = dsi.height; + } + break; + case GF_STREAM_AUDIO: + mtype = GF_ISOM_MEDIA_AUDIO; + break; + case GF_STREAM_MPEG7: + mtype = GF_ISOM_MEDIA_MPEG7; + break; + case GF_STREAM_IPMP: + mtype = GF_ISOM_MEDIA_IPMP; + break; + case GF_STREAM_OCI: + mtype = GF_ISOM_MEDIA_OCI; + break; + case GF_STREAM_MPEGJ: + mtype = GF_ISOM_MEDIA_MPEGJ; + break; + /*note we cannot import OD from NHNT*/ + case GF_STREAM_OD: + e = GF_NOT_SUPPORTED; + goto exit; + case GF_STREAM_INTERACT: + mtype = GF_ISOM_MEDIA_SCENE; + break; + default: + mtype = GF_ISOM_MEDIA_ESM; + break; + } + track = gf_isom_new_track(import->dest, import->esd->ESID, mtype, import->esd->slConfig->timestampResolution); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? szMedia : NULL, NULL, &di); + if (e) goto exit; + + if (w && h) { + gf_isom_set_visual_info(import->dest, track, di, w, h); + gf_media_update_par(import->dest, track); + } + + gf_import_message(import, GF_OK, "NHNT import - Stream Type %s - ObjectTypeIndication 0x%02x", gf_odf_stream_type_name(import->esd->decoderConfig->streamType), import->esd->decoderConfig->objectTypeIndication); + + duration = (u32) ( ((Double) import->duration)/ 1000 * import->esd->slConfig->timestampResolution); + + samp = gf_isom_sample_new(); + samp->data = (char*)malloc(sizeof(char) * 1024); + max_size = 1024; + count = 0; + gf_f64_seek(mdia, 0, SEEK_END); + media_size = gf_f64_tell(mdia); + gf_f64_seek(mdia, 0, SEEK_SET); + media_done = 0; + next_is_start = 1; + + while (!feof(nhnt)) { + Bool is_start, is_end; + samp->dataLength = gf_bs_read_u24(bs); + samp->IsRAP = gf_bs_read_int(bs, 1); + is_start = gf_bs_read_int(bs, 1); + if (next_is_start) { + is_start = 1; + next_is_start = 0; + } + is_end = gf_bs_read_int(bs, 1); + if (is_end) next_is_start = 1; + /*3 reserved + AU type (2)*/ + gf_bs_read_int(bs, 5); + if (sig) { + u64 CTS; + offset = gf_bs_read_u64(bs); + CTS = gf_bs_read_u64(bs); + samp->DTS = gf_bs_read_u64(bs); + samp->CTS_Offset = (u32) (CTS - samp->DTS); + } else { + offset = gf_bs_read_u32(bs); + samp->CTS_Offset = gf_bs_read_u32(bs); + samp->DTS = gf_bs_read_u32(bs); + samp->CTS_Offset -= (u32) samp->DTS; + } + if (!count && samp->DTS) samp->DTS = 0; + count++; + + if (import->flags & GF_IMPORT_USE_DATAREF) { + if (!is_start) { + e = gf_import_message(import, GF_NOT_SUPPORTED, "Fragmented NHNT file detected - cannot use data referencing"); + goto exit; + } + e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset); + } else { + if (samp->dataLength>max_size) { + samp->data = (char*)realloc(samp->data, sizeof(char) * samp->dataLength); + max_size = samp->dataLength; + } + gf_f64_seek(mdia, offset, SEEK_SET); + fread( samp->data, samp->dataLength, 1, mdia); + if (is_start) { + e = gf_isom_add_sample(import->dest, track, di, samp); + } else { + e = gf_isom_append_sample_data(import->dest, track, samp->data, samp->dataLength); + } + } + media_done += samp->dataLength; + gf_set_progress("Importing NHNT", (u32) (media_done/1024), (u32) (media_size/1024)); + if (e) goto exit; + if (!gf_bs_available(bs)) break; + if (duration && (samp->DTS > duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + } + if (media_done!=media_size) gf_set_progress("Importing NHNT", (u32) (media_size/1024), (u32) (media_size/1024)); + gf_isom_sample_del(&samp); + MP4T_RecomputeBitRate(import->dest, track); + +exit: + gf_bs_del(bs); + fclose(nhnt); + fclose(mdia); + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + return e; +} + + +typedef struct +{ + Bool from_is_start, from_is_end, to_is_start, to_is_end; + u32 from_pos, to_pos; + char *from_id, *to_id; + GF_List *id_stack; + GF_SAXParser *sax; +} XMLBreaker; + + +static void nhml_node_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + XMLBreaker *breaker = (XMLBreaker *)sax_cbck; + char *node_id; + u32 i; + GF_XMLAttribute *att; + node_id = NULL; + for (i=0; iname, "DEF") && stricmp(att->name, "id")) continue; + node_id = strdup(att->value); + break; + } + if (!node_id) { + node_id = strdup("__nhml__none"); + gf_list_add(breaker->id_stack, node_id); + return; + } + gf_list_add(breaker->id_stack, node_id); + + if (breaker->from_is_start && breaker->from_id && !strcmp(breaker->from_id, node_id)) { + breaker->from_pos = gf_xml_sax_get_node_start_pos(breaker->sax); + breaker->from_is_start = 0; + } + if (breaker->to_is_start && breaker->to_id && !strcmp(breaker->to_id, node_id)) { + breaker->to_pos = gf_xml_sax_get_node_start_pos(breaker->sax); + breaker->to_is_start = 0; + } + if (!breaker->to_is_start && !breaker->from_is_start && !breaker->to_is_end && !breaker->from_is_end) { + gf_xml_sax_suspend(breaker->sax, 1); + } + +} + +static void nhml_node_end(void *sax_cbck, const char *node_name, const char *name_space) +{ + XMLBreaker *breaker = (XMLBreaker *)sax_cbck; + char *node_id = (char *)gf_list_last(breaker->id_stack); + gf_list_rem_last(breaker->id_stack); + if (breaker->from_is_end && breaker->from_id && !strcmp(breaker->from_id, node_id)) { + breaker->from_pos = gf_xml_sax_get_node_end_pos(breaker->sax); + breaker->from_is_end = 0; + } + if (breaker->to_is_end && breaker->to_id && !strcmp(breaker->to_id, node_id)) { + breaker->to_pos = gf_xml_sax_get_node_end_pos(breaker->sax); + breaker->to_is_end = 0; + } + free(node_id); + if (!breaker->to_is_start && !breaker->from_is_start && !breaker->to_is_end && !breaker->from_is_end) { + gf_xml_sax_suspend(breaker->sax, 1); + } +} + + +GF_Err gf_import_sample_from_xml(GF_MediaImporter *import, GF_ISOSample *samp, char *xml_file, char *xmlFrom, char *xmlTo, u32 *max_size) +{ + GF_Err e; + XMLBreaker breaker; + char *tmp; + FILE *xml; + + if (!xml_file || !xmlFrom || !xmlTo) return GF_BAD_PARAM; + + memset(&breaker, 0, sizeof(XMLBreaker)); + + xml = fopen(xml_file, "rb"); + if (!xml) { + e = gf_import_message(import, GF_BAD_PARAM, "NHML import failure: file %s not found", xml_file); + goto exit; + } + + memset(&breaker, 0, sizeof(XMLBreaker)); + breaker.id_stack = gf_list_new(); + + if (strstr(xmlFrom, ".start")) breaker.from_is_start = 1; + else breaker.from_is_end = 1; + tmp = strchr(xmlFrom, '.'); + *tmp = 0; + if (stricmp(xmlFrom, "doc")) breaker.from_id = strdup(xmlFrom); + /*doc start pos is 0, no need to look for it*/ + else if (breaker.from_is_start) breaker.from_is_start = 0; + *tmp = '.'; + + if (strstr(xmlTo, ".start")) breaker.to_is_start = 1; + else breaker.to_is_end = 1; + tmp = strchr(xmlTo, '.'); + *tmp = 0; + if (stricmp(xmlTo, "doc")) breaker.to_id = strdup(xmlTo); + /*doc end pos is file size, no need to look for it*/ + else if (breaker.to_is_end) breaker.to_is_end = 0; + *tmp = '.'; + + breaker.sax = gf_xml_sax_new(nhml_node_start, nhml_node_end, NULL, &breaker); + e = gf_xml_sax_parse_file(breaker.sax, xml_file, NULL); + gf_xml_sax_del(breaker.sax); + if (e<0) goto exit; + e = GF_OK; + + if (!breaker.to_id) { + fseek(xml, 0, SEEK_END); + breaker.to_pos = ftell(xml); + fseek(xml, 0, SEEK_SET); + } + if(breaker.to_pos < breaker.from_pos) { + e = gf_import_message(import, GF_BAD_PARAM, "NHML import failure: xmlFrom %s is located after xmlTo %s", xmlFrom, xmlTo); + goto exit; + } + + assert(breaker.to_pos > breaker.from_pos); + + samp->dataLength = breaker.to_pos - breaker.from_pos; + if (*max_size < samp->dataLength) { + *max_size = samp->dataLength; + samp->data = (char*)realloc(samp->data, sizeof(char)*samp->dataLength); + } + fseek(xml, breaker.from_pos, SEEK_SET); + fread(samp->data, 1, samp->dataLength, xml); + +exit: + if (xml) fclose(xml); + while (gf_list_count(breaker.id_stack)) { + char *id = (char *)gf_list_last(breaker.id_stack); + gf_list_rem_last(breaker.id_stack); + free(id); + } + gf_list_del(breaker.id_stack); + if (breaker.from_id) free(breaker.from_id); + if (breaker.to_id) free(breaker.to_id); + return e; +} + +#define ZLIB_COMPRESS_SAFE 4 + +static GF_Err compress_sample_data(GF_ISOSample *samp, u32 *max_size, char **dict, u32 offset) +{ + z_stream stream; + int err; + char *dest = (char *)malloc(sizeof(char)*samp->dataLength*ZLIB_COMPRESS_SAFE); + stream.next_in = (Bytef*)samp->data + offset; + stream.avail_in = (uInt)samp->dataLength - offset; + stream.next_out = ( Bytef*)dest; + stream.avail_out = (uInt)samp->dataLength*ZLIB_COMPRESS_SAFE; + stream.zalloc = (alloc_func)NULL; + stream.zfree = (free_func)NULL; + stream.opaque = (voidpf)NULL; + + err = deflateInit(&stream, 9); + if (err != Z_OK) { + free(dest); + return GF_IO_ERR; + } + if (dict && *dict) { + err = deflateSetDictionary(&stream, (Bytef *)*dict, strlen(*dict)); + if (err != Z_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHML import] Error assigning dictionary\n")); + deflateEnd(&stream); + free(dest); + return GF_IO_ERR; + } + } + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + free(dest); + return GF_IO_ERR; + } + if (samp->dataLength - offsetdataLength - offset)); + } + if (dict) { + if (*dict) free(*dict); + *dict = (char*)malloc(sizeof(char)*samp->dataLength); + memcpy(*dict, samp->data, samp->dataLength); + } + if (*max_size < stream.total_out) { + *max_size = samp->dataLength*ZLIB_COMPRESS_SAFE; + samp->data = realloc(samp->data, *max_size * sizeof(char)); + } + + memcpy(samp->data + offset, dest, sizeof(char)*stream.total_out); + samp->dataLength = offset + stream.total_out; + free(dest); + + deflateEnd(&stream); + return GF_OK; +} + +static void nhml_on_progress(void *cbk, u32 done, u32 tot) +{ + gf_set_progress("NHML Loading", done, tot); +} + +/*FIXME - need LARGE FILE support in NHNT - add a new version*/ +GF_Err gf_import_nhml_dims(GF_MediaImporter *import, Bool dims_doc) +{ + GF_Err e; + GF_DIMSDescription dims; + Bool destroy_esd, inRootOD, do_compress, use_dict, is_dims; + u32 i, track, tkID, di, mtype, max_size, duration, count, streamType, oti, timescale, specInfoSize, dts_inc, par_den, par_num; + GF_ISOSample *samp; + GF_XMLAttribute *att; + s64 media_size, media_done, offset; + FILE *nhml, *mdia, *info; + char *dictionary; + char *ext, szName[1000], szMedia[1000], szMediaTemp[1000], szInfo[1000], szXmlFrom[1000], szXmlTo[1000], *specInfo; + GF_GenericSampleDescription sdesc; + GF_DOMParser *parser; + GF_XMLNode *root, *node; + char *szRootName, *szSampleName, *szImpName; + + szRootName = dims_doc ? "DIMSStream" : "NHNTStream"; + szSampleName = dims_doc ? "DIMSUnit" : "NHNTSample"; + szImpName = dims_doc ? "DIMS" : "NHML"; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->nb_tracks = 1; + import->tk_info[0].track_num = 1; + import->tk_info[0].type = 0; + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF; + return GF_OK; + } + nhml = fopen(import->in_name, "rt"); + if (!nhml) return gf_import_message(import, GF_URL_ERROR, "Cannot find %s file %s", szImpName, szMedia); + + strcpy(szName, import->in_name); + ext = strrchr(szName, '.'); + if (ext) ext[0] = 0; + + strcpy(szMedia, szName); + strcat(szMedia, ".media"); + strcpy(szInfo, szName); + strcat(szInfo, ".info"); + + parser = gf_xml_dom_new(); + e = gf_xml_dom_parse(parser, import->in_name, nhml_on_progress, import); + if (e) { + fclose(nhml); + gf_import_message(import, e, "Error parsing %s file: Line %d - %s", szImpName, gf_xml_dom_get_line(parser), gf_xml_dom_get_error(parser)); + gf_xml_dom_del(parser); + return e; + } + root = gf_xml_dom_get_root(parser); + + mdia = NULL; + destroy_esd = 0; + dts_inc = 0; + inRootOD = 0; + do_compress = 0; + is_dims = 0; + specInfo = NULL; + samp = NULL; + memset(&dims, 0, sizeof(GF_DIMSDescription)); + dims.profile = dims.level = 255; + dims.streamType = 1; + dims.containsRedundant = 1; + + if (stricmp(root->name, szRootName)) { + e = gf_import_message(import, GF_BAD_PARAM, "Error parsing %s file - \"%s\" root expected, got \"%s\"", szImpName, szRootName, root->name); + goto exit; + } + dictionary = NULL; + use_dict = 0; + memset(&sdesc, 0, sizeof(GF_GenericSampleDescription)); + tkID = mtype = streamType = oti = par_den = par_num = 0; + timescale = 1000; + i=0; + while ((att = (GF_XMLAttribute *)gf_list_enum(root->attributes, &i))) { + if (!stricmp(att->name, "streamType")) streamType = atoi(att->value); + else if (!stricmp(att->name, "mediaType") && (strlen(att->value)==4)) { + mtype = GF_4CC(att->value[0], att->value[1], att->value[2], att->value[3]); + } + else if (!stricmp(att->name, "mediaSubType") && (strlen(att->value)==4)) { + sdesc.codec_tag = GF_4CC(att->value[0], att->value[1], att->value[2], att->value[3]); + } + else if (!stricmp(att->name, "objectTypeIndication")) oti = atoi(att->value); + else if (!stricmp(att->name, "timeScale")) timescale = atoi(att->value); + else if (!stricmp(att->name, "width")) sdesc.width = atoi(att->value); + else if (!stricmp(att->name, "height")) sdesc.height = atoi(att->value); + else if (!stricmp(att->name, "parNum")) par_num = atoi(att->value); + else if (!stricmp(att->name, "parDen")) par_den = atoi(att->value); + else if (!stricmp(att->name, "sampleRate")) sdesc.samplerate = atoi(att->value); + else if (!stricmp(att->name, "numChannels")) sdesc.nb_channels = atoi(att->value); + else if (!stricmp(att->name, "baseMediaFile")) strcpy(szMedia, att->value); + else if (!stricmp(att->name, "specificInfoFile")) strcpy(szInfo, att->value); + else if (!stricmp(att->name, "trackID")) tkID = atoi(att->value); + else if (!stricmp(att->name, "inRootOD")) inRootOD = (!stricmp(att->value, "yes") ); + else if (!stricmp(att->name, "DTS_increment")) dts_inc = atoi(att->value); + else if (!stricmp(att->name, "gzipSamples")) do_compress = (!stricmp(att->value, "yes")) ? 1 : 0; + else if (!stricmp(att->name, "gzipDictionary")) { + u32 d_size; + if (stricmp(att->value, "self")) { + FILE *d = fopen(att->value, "rb"); + if (!d) { + gf_import_message(import, GF_IO_ERR, "Cannot open dictionary file %s", att->value); + continue; + } + fseek(d, 0, SEEK_END); + d_size = ftell(d); + dictionary = (char*)malloc(sizeof(char)*(d_size+1)); + fseek(d, 0, SEEK_SET); + fread(dictionary, 1, d_size, d); + dictionary[d_size]=0; + } + use_dict = 1; + } + /*unknow desc related*/ + else if (!stricmp(att->name, "compressorName")) strcpy(sdesc.compressor_name, att->value); + else if (!stricmp(att->name, "codecVersion")) sdesc.version = atoi(att->value); + else if (!stricmp(att->name, "codecRevision")) sdesc.revision = atoi(att->value); + else if (!stricmp(att->name, "codecVendor") && (strlen(att->value)==4)) { + sdesc.vendor_code = GF_4CC(att->value[0], att->value[1], att->value[2], att->value[3]); + } + else if (!stricmp(att->name, "temporalQuality")) sdesc.temporal_quality = atoi(att->value); + else if (!stricmp(att->name, "spatialQuality")) sdesc.spacial_quality = atoi(att->value); + else if (!stricmp(att->name, "horizontalResolution")) sdesc.h_res = atoi(att->value); + else if (!stricmp(att->name, "verticalResolution")) sdesc.v_res = atoi(att->value); + else if (!stricmp(att->name, "bitDepth")) sdesc.depth = atoi(att->value); + else if (!stricmp(att->name, "bitsPerSample")) sdesc.bits_per_sample = atoi(att->value); + + /*DIMS stuff*/ + else if (!stricmp(att->name, "profile")) dims.profile = atoi(att->value); + else if (!stricmp(att->name, "level")) dims.level = atoi(att->value); + else if (!stricmp(att->name, "pathComponents")) dims.pathComponents = atoi(att->value); + else if (!stricmp(att->name, "useFullRequestHost") && !stricmp(att->value, "yes")) dims.fullRequestHost = 1; + else if (!stricmp(att->name, "stream_type") && !stricmp(att->value, "secondary")) dims.streamType = 0; + else if (!stricmp(att->name, "contains_redundant")) { + if (!stricmp(att->value, "main")) dims.containsRedundant = 1; + else if (!stricmp(att->value, "redundant")) dims.containsRedundant = 2; + else if (!stricmp(att->value, "main+redundant")) dims.containsRedundant = 3; + } + else if (!stricmp(att->name, "text_encoding")) dims.textEncoding = att->value; + else if (!stricmp(att->name, "content_encoding")) { + if (!strcmp(att->value, "deflate")) { + dims.contentEncoding = att->value; + do_compress = 1; + } + } + else if (!stricmp(att->name, "content_script_types")) dims.content_script_types = att->value; + + } + if (sdesc.samplerate && !timescale) timescale = sdesc.samplerate; + if (!sdesc.bits_per_sample) sdesc.bits_per_sample = 16; + + if (dims_doc || (sdesc.codec_tag==GF_ISOM_SUBTYPE_3GP_DIMS)) { + mtype = GF_ISOM_MEDIA_DIMS; + sdesc.codec_tag=GF_ISOM_SUBTYPE_3GP_DIMS; + is_dims = 1; + streamType=0; + import->flags &= ~GF_IMPORT_USE_DATAREF; + } + + mdia = gf_f64_open(szMedia, "rb"); + + specInfoSize = 0; + if (!streamType && !mtype && !sdesc.codec_tag) { + e = gf_import_message(import, GF_NOT_SUPPORTED, "Error parsing %s file - StreamType or MediaType not specified", szImpName); + goto exit; + } + + info = fopen(szInfo, "rb"); + if (info) { + fseek(info, 0, SEEK_END); + specInfoSize = (u32) ftell(info); + specInfo = (char*)malloc(sizeof(char) * specInfoSize); + fseek(info, 0, SEEK_SET); + fread(specInfo, specInfoSize, 1, info); + fclose(info); + } + /*compressing samples, remove data ref*/ + if (do_compress) import->flags &= ~GF_IMPORT_USE_DATAREF; + + if (streamType) { + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(2); + destroy_esd = 1; + import->esd->ESID = tkID; + } + /*update stream type/oti*/ + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->decoderConfig->streamType = streamType; + import->esd->decoderConfig->objectTypeIndication = oti; + + if (import->esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) import->esd->decoderConfig->decoderSpecificInfo); + import->esd->decoderConfig->decoderSpecificInfo = NULL; + + import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + import->esd->decoderConfig->decoderSpecificInfo->dataLength = specInfoSize; + import->esd->decoderConfig->decoderSpecificInfo->data = specInfo; + specInfo = NULL; + specInfoSize = 0; + import->esd->slConfig->timestampResolution = timescale; + + + switch (import->esd->decoderConfig->streamType) { + case GF_STREAM_SCENE: mtype = GF_ISOM_MEDIA_SCENE; break; + case GF_STREAM_VISUAL: + mtype = GF_ISOM_MEDIA_VISUAL; + if (import->esd->decoderConfig->objectTypeIndication==0x20) { + GF_M4VDecSpecInfo dsi; + if (!import->esd->decoderConfig->decoderSpecificInfo) { + e = GF_NON_COMPLIANT_BITSTREAM; + goto exit; + } + e = gf_m4v_get_config(import->esd->decoderConfig->decoderSpecificInfo->data, import->esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + sdesc.width = dsi.width; + sdesc.height = dsi.height; + if (e) goto exit; + } + break; + case GF_STREAM_AUDIO: + mtype = GF_ISOM_MEDIA_AUDIO; + if (!sdesc.samplerate) sdesc.samplerate = 44100; + if (!sdesc.nb_channels) sdesc.nb_channels = 2; + break; + case GF_STREAM_MPEG7: mtype = GF_ISOM_MEDIA_MPEG7; break; + case GF_STREAM_IPMP: mtype = GF_ISOM_MEDIA_IPMP; break; + case GF_STREAM_OCI: mtype = GF_ISOM_MEDIA_OCI; break; + case GF_STREAM_MPEGJ: mtype = GF_ISOM_MEDIA_MPEGJ; break; + /*note we cannot import OD from NHNT*/ + case GF_STREAM_OD: e = GF_NOT_SUPPORTED; goto exit; + case GF_STREAM_INTERACT: mtype = GF_ISOM_MEDIA_SCENE; break; + default: mtype = GF_ISOM_MEDIA_ESM; break; + } + + track = gf_isom_new_track(import->dest, import->esd->ESID, mtype, timescale); + if (!track) { e = gf_isom_last_error(import->dest); goto exit; } + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? szMedia : NULL, NULL, &di); + if (e) goto exit; + + gf_import_message(import, GF_OK, "NHML import - Stream Type %s - ObjectTypeIndication 0x%02x", gf_odf_stream_type_name(import->esd->decoderConfig->streamType), import->esd->decoderConfig->objectTypeIndication); + + } else if (is_dims) { + track = gf_isom_new_track(import->dest, tkID, mtype, timescale); + if (!track) { e = gf_isom_last_error(import->dest); goto exit; } + e = gf_isom_new_dims_description(import->dest, track, &dims, NULL, NULL, &di); + if (e) goto exit; + + gf_import_message(import, GF_OK, "3GPP DIMS import"); + } else { + char szT[5]; + sdesc.extension_buf = specInfo; + sdesc.extension_buf_size = specInfoSize; + if (!sdesc.vendor_code) sdesc.vendor_code = GF_4CC('G', 'P', 'A', 'C'); + + track = gf_isom_new_track(import->dest, tkID, mtype, timescale); + if (!track) { e = gf_isom_last_error(import->dest); goto exit; } + + e = gf_isom_new_generic_sample_description(import->dest, track, (import->flags & GF_IMPORT_USE_DATAREF) ? szMedia : NULL, NULL, &sdesc, &di); + if (e) goto exit; + + strcpy(szT, gf_4cc_to_str(mtype)); + gf_import_message(import, GF_OK, "NHML import - MediaType \"%s\" - SubType \"%s\"", szT, gf_4cc_to_str(sdesc.codec_tag)); + } + + gf_isom_set_track_enabled(import->dest, track, 1); + import->final_trackID = gf_isom_get_track_id(import->dest, track); + if (import->esd && !import->esd->ESID) import->esd->ESID = import->final_trackID; + + if (sdesc.width && sdesc.height) { + gf_isom_set_visual_info(import->dest, track, di, sdesc.width, sdesc.height); + if (par_den && par_num) { + gf_media_change_par(import->dest, track, par_num, par_den); + } else { + gf_media_update_par(import->dest, track); + } + } + else if (sdesc.samplerate && sdesc.nb_channels) { + gf_isom_set_audio_info(import->dest, track, di, sdesc.samplerate, sdesc.nb_channels, (u8) sdesc.bits_per_sample); + } + + duration = (u32) ( ((Double) import->duration)/ 1000 * timescale); + + samp = gf_isom_sample_new(); + samp->data = (char*)malloc(sizeof(char) * 1024); + max_size = 1024; + count = 0; + media_size = 0; + if (mdia) { + gf_f64_seek(mdia, 0, SEEK_END); + media_size = gf_f64_tell(mdia); + gf_f64_seek(mdia, 0, SEEK_SET); + } + media_done = 0; + + samp->IsRAP = 1; + i=0; + while ((node = (GF_XMLNode *) gf_list_enum(root->content, &i))) { + u32 j, dims_flags; + Bool append, compress; + if (node->type) continue; + if (stricmp(node->name, szSampleName) ) continue; + + strcpy(szMediaTemp, ""); + strcpy(szXmlFrom, ""); + strcpy(szXmlTo, ""); + + /*by default handle all samples as contigous*/ + offset = 0; + samp->dataLength = 0; + dims_flags = 0; + append = 0; + compress = do_compress; + + j=0; + while ( (att = (GF_XMLAttribute *)gf_list_enum(node->attributes, &j))) { + if (!stricmp(att->name, "DTS") || !stricmp(att->name, "time")) { + u32 h, m, s, ms; + if (sscanf(att->value, "%d:%d:%d.%d", &h, &m, &s, &ms) == 4) { + samp->DTS = (u64) ( (Double) ( ((h*3600 + m*60 + s)*1000 + ms) / 1000.0) * timescale ); + } else { + samp->DTS = atoi(att->value); + } + } + else if (!stricmp(att->name, "CTSOffset")) samp->CTS_Offset = atoi(att->value); + else if (!stricmp(att->name, "isRAP") && !samp->IsRAP) { + samp->IsRAP = (!stricmp(att->value, "yes")) ? 1 : 0; + } + else if (!stricmp(att->name, "isSyncShadow")) samp->IsRAP = !stricmp(att->value, "yes") ? 2 : 0; + else if (!stricmp(att->name, "mediaOffset")) offset = (s64) atof(att->value) ; + else if (!stricmp(att->name, "dataLength")) samp->dataLength = atoi(att->value); + else if (!stricmp(att->name, "mediaFile")) strcpy(szMediaTemp, att->value); + else if (!stricmp(att->name, "xmlFrom")) strcpy(szXmlFrom, att->value); + else if (!stricmp(att->name, "xmlTo")) strcpy(szXmlTo, att->value); + /*DIMS flags*/ + else if (!stricmp(att->name, "is-Scene") && !stricmp(att->value, "yes")) + dims_flags |= GF_DIMS_UNIT_S; + else if (!stricmp(att->name, "is-RAP") && !stricmp(att->value, "yes")) { + dims_flags |= GF_DIMS_UNIT_M; + samp->IsRAP = 1; + } + else if (!stricmp(att->name, "is-redundant") && !stricmp(att->value, "yes")) + dims_flags |= GF_DIMS_UNIT_I; + else if (!stricmp(att->name, "redundant-exit") && !stricmp(att->value, "yes")) + dims_flags |= GF_DIMS_UNIT_D; + else if (!stricmp(att->name, "priority") && !stricmp(att->value, "high")) + dims_flags |= GF_DIMS_UNIT_P; + else if (!stricmp(att->name, "compress") && !stricmp(att->value, "yes")) + dims_flags |= GF_DIMS_UNIT_C; + + } + if (samp->IsRAP==1) + dims_flags |= GF_DIMS_UNIT_M; + if (!count && samp->DTS) samp->DTS = 0; + + if (!(dims_flags & GF_DIMS_UNIT_C)) compress = 0; + count++; + + if (import->flags & GF_IMPORT_USE_DATAREF) { + if (offset) offset = media_done; + e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset); + } else if (strlen(szXmlFrom) && strlen(szXmlTo)) { + char *xml_file; + if (strlen(szMediaTemp)) xml_file = szMediaTemp; + else xml_file = szMedia; + samp->dataLength = max_size; + e = gf_import_sample_from_xml(import, samp, xml_file, szXmlFrom, szXmlTo, &max_size); + } else if (is_dims && !strlen(szMediaTemp)) { + GF_BitStream *bs; + char *content = gf_xml_dom_serialize(node, 1); + + samp->dataLength = 3 + strlen(content); + + if (samp->dataLength>max_size) { + samp->data = (char*)realloc(samp->data, sizeof(char) * samp->dataLength); + max_size = samp->dataLength; + } + + bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_WRITE); + gf_bs_write_u16(bs, samp->dataLength-2); + gf_bs_write_u8(bs, (u8) dims_flags); + gf_bs_write_data(bs, content, (samp->dataLength-3)); + free(content); + gf_bs_del(bs); + /*same DIMS unit*/ + if (gf_isom_get_sample_from_dts(import->dest, track, samp->DTS)) + append = 1; + + } else { + Bool close = 0; + FILE *f = mdia; + if (strlen(szMediaTemp)) { + f = gf_f64_open(szMediaTemp, "rb"); + close = 1; + if (offset) gf_f64_seek(f, offset, SEEK_SET); + } else { + if (!offset) offset = media_done; + } + if (!f) { + e = gf_import_message(import, GF_BAD_PARAM, "%s import failure: file %s not found", szImpName, close ? szMediaTemp : szMedia); + goto exit; + } + + if (!samp->dataLength) { + u64 cur_pos = gf_f64_tell(f); + gf_f64_seek(f, 0, SEEK_END); + samp->dataLength = (u32) gf_f64_tell(f); + gf_f64_seek(f, cur_pos, SEEK_SET); + } + + gf_f64_seek(f, offset, SEEK_SET); + if (is_dims) { + GF_BitStream *bs; + if (samp->dataLength+3>max_size) { + samp->data = (char*)realloc(samp->data, sizeof(char) * (samp->dataLength+3)); + max_size = samp->dataLength+3; + } + bs = gf_bs_new(samp->data, samp->dataLength+3, GF_BITSTREAM_WRITE); + gf_bs_write_u16(bs, samp->dataLength+1); + gf_bs_write_u8(bs, (u8) dims_flags); + fread( samp->data+3, samp->dataLength, 1, f); + gf_bs_del(bs); + samp->dataLength+=3; + + /*same DIMS unit*/ + if (gf_isom_get_sample_from_dts(import->dest, track, samp->DTS)) + append = 1; + } else { + if (samp->dataLength>max_size) { + samp->data = (char*)realloc(samp->data, sizeof(char) * samp->dataLength); + max_size = samp->dataLength; + } + fread( samp->data, samp->dataLength, 1, f); + } + if (close) fclose(f); + } + if (e) goto exit; + + if (is_dims) { + if (strstr(samp->data+3, "svg ")) dims_flags |= GF_DIMS_UNIT_S; + if (dims_flags & GF_DIMS_UNIT_S) dims_flags |= GF_DIMS_UNIT_P; + samp->data[2] = dims_flags; + } + + if (compress) { + e = compress_sample_data(samp, &max_size, use_dict ? &dictionary : NULL, is_dims ? 3 : 0); + if (e) goto exit; + if (is_dims) { + GF_BitStream *bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_WRITE); + gf_bs_write_u16(bs, samp->dataLength-2); + gf_bs_del(bs); + } + } + if (is_dims && (samp->dataLength > 0xFFFF)) { + e = gf_import_message(import, GF_BAD_PARAM, "DIMS import failure: sample data is too long - maximum size allowed: 65532 bytes"); + goto exit; + } + + + if ((samp->IsRAP==2) && !is_dims) { + e = gf_isom_add_sample_shadow(import->dest, track, samp); + } else if (append) { + e = gf_isom_append_sample_data(import->dest, track, samp->data, samp->dataLength); + } else { + e = gf_isom_add_sample(import->dest, track, di, samp); + } + if (e) goto exit; + samp->IsRAP = 0; + samp->CTS_Offset = 0; + samp->DTS += dts_inc; + media_done += samp->dataLength; + gf_set_progress(is_dims ? "Importing DIMS" : "Importing NHML", (u32) media_done, (u32) (media_size ? media_size : media_done+1) ); + if (duration && (samp->DTS > duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + } + if (media_done!=media_size) gf_set_progress(is_dims ? "Importing DIMS" : "Importing NHML", (u32) media_size, (u32) media_size); + MP4T_RecomputeBitRate(import->dest, track); + + if (inRootOD) gf_isom_add_track_to_root_od(import->dest, track); + +exit: + fclose(nhml); + if (samp) { + samp->dataLength = 1; + gf_isom_sample_del(&samp); + } + if (mdia) fclose(mdia); + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + gf_xml_dom_del(parser); + if (specInfo) free(specInfo); + if (dictionary) free(dictionary); + return e; +} + + +GF_Err gf_import_amr_evrc_smv(GF_MediaImporter *import) +{ + GF_Err e; + u32 track, trackID, di, max_size, duration, sample_rate, block_size, i; + GF_ISOSample *samp; + char magic[20], *msg; + Bool delete_esd, update_gpp_cfg; + u32 media_size, media_done, offset, mtype, oti, nb_frames; + GF_3GPConfig gpp_cfg; + FILE *mdia; + msg = NULL; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->nb_tracks = 1; + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF | GF_IMPORT_FORCE_MPEG4 | GF_IMPORT_3GPP_AGGREGATION; + return GF_OK; + } + + mdia = fopen(import->in_name, "rb"); + if (!mdia) return gf_import_message(import, GF_URL_ERROR, "Cannot find file %s", import->in_name); + + update_gpp_cfg = 0; + oti = mtype = 0; + sample_rate = 8000; + block_size = 160; + fread(magic, 1, 20, mdia); + if (!strnicmp(magic, "#!AMR\n", 6)) { + gf_import_message(import, GF_OK, "Importing AMR Audio"); + fseek(mdia, 6, SEEK_SET); + mtype = GF_ISOM_SUBTYPE_3GP_AMR; + update_gpp_cfg = 1; + msg = "Importing AMR"; + } + else if (!strnicmp(magic, "#!EVRC\n", 7)) { + gf_import_message(import, GF_OK, "Importing EVRC Audio"); + fseek(mdia, 7, SEEK_SET); + mtype = GF_ISOM_SUBTYPE_3GP_EVRC; + oti = 0xA0; + msg = "Importing EVRC"; + } + else if (!strnicmp(magic, "#!SMV\n", 6)) { + gf_import_message(import, GF_OK, "Importing SMV Audio"); + fseek(mdia, 6, SEEK_SET); + mtype = GF_ISOM_SUBTYPE_3GP_SMV; + oti = 0xA1; + msg = "Importing SMV"; + } + else if (!strnicmp(magic, "#!AMR_MC1.0\n", 12)) { + fclose(mdia); + return gf_import_message(import, GF_NOT_SUPPORTED, "Multichannel AMR Audio Not Supported"); + } + else if (!strnicmp(magic, "#!AMR-WB\n", 9)) { + gf_import_message(import, GF_OK, "Importing AMR WideBand Audio"); + fseek(mdia, 9, SEEK_SET); + mtype = GF_ISOM_SUBTYPE_3GP_AMR_WB; + sample_rate = 16000; + block_size = 320; + update_gpp_cfg = 1; + msg = "Importing AMR-WB"; + } + else if (!strnicmp(magic, "#!AMR-WB_MC1.0\n", 15)) { + fclose(mdia); + return gf_import_message(import, GF_NOT_SUPPORTED, "Multichannel AMR WideBand Audio Not Supported"); + } + else { + char *ext = strrchr(import->in_name, '.'); + if (ext && !stricmp(ext, ".amr")) { mtype = GF_ISOM_SUBTYPE_3GP_AMR; update_gpp_cfg = 1; ext = "AMR"; msg = "Importing AMR";} + else if (ext && !stricmp(ext, ".evc")) { mtype = GF_ISOM_SUBTYPE_3GP_EVRC; oti = 0xA0; ext = "EVRC"; msg = "Importing EVRC";} + else if (ext && !stricmp(ext, ".smv")) { mtype = GF_ISOM_SUBTYPE_3GP_SMV; oti = 0xA1; ext = "SMV"; msg = "Importing SMV";} + else { + fclose(mdia); + return gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Corrupted AMR/SMV/EVRC file header"); + } + + fseek(mdia, 0, SEEK_SET); + gf_import_message(import, GF_OK, "Importing %s Audio (File header corrupted, missing \"#!%s\\n\")", ext, ext); + } + + delete_esd = 0; + trackID = 0; + e = GF_OK; + if (import->esd) trackID = import->esd->ESID; + + track = gf_isom_new_track(import->dest, trackID, GF_ISOM_MEDIA_AUDIO, sample_rate); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (import->esd && !import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = gf_isom_get_track_id(import->dest, track); + + memset(&gpp_cfg, 0, sizeof(GF_3GPConfig)); + gpp_cfg.type = mtype; + gpp_cfg.frames_per_sample = import->frames_per_sample; + if (!gpp_cfg.frames_per_sample) gpp_cfg.frames_per_sample = 1; + else if (gpp_cfg.frames_per_sample >15) gpp_cfg.frames_per_sample = 15; + + if (import->flags & GF_IMPORT_USE_DATAREF) gpp_cfg.frames_per_sample = 1; + + + if (oti && (import->flags & GF_IMPORT_FORCE_MPEG4)) { + if (!import->esd) { + delete_esd = 1; + import->esd = gf_odf_desc_esd_new(2); + import->esd->ESID = trackID; + } + import->esd->decoderConfig->streamType = GF_STREAM_AUDIO; + import->esd->decoderConfig->objectTypeIndication = oti; + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + if (e) goto exit; + } else { + import->flags &= ~GF_IMPORT_FORCE_MPEG4; + gpp_cfg.vendor = GF_4CC('G', 'P', 'A', 'C'); + e = gf_isom_3gp_config_new(import->dest, track, &gpp_cfg, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + if (e) goto exit; + } + gf_isom_set_audio_info(import->dest, track, di, sample_rate, 1, 16); + duration = import->duration * sample_rate; + duration /= 1000; + + samp = gf_isom_sample_new(); + samp->data = (char*)malloc(sizeof(char) * 200); + samp->IsRAP = 1; + max_size = 200; + offset = ftell(mdia); + fseek(mdia, 0, SEEK_END); + media_size = ftell(mdia) - offset; + fseek(mdia, offset, SEEK_SET); + + media_done = 0; + nb_frames = 0; + + while (!feof(mdia)) { + u8 ft, toc; + + offset = ftell(mdia); + toc = fgetc(mdia); + switch (gpp_cfg.type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + ft = (toc >> 3) & 0x0F; + /*update mode set (same mechanism for both AMR and AMR-WB*/ + gpp_cfg.AMR_mode_set |= (1<dataLength = GF_AMR_WB_FRAME_SIZE[ft]; + } else { + samp->dataLength = GF_AMR_FRAME_SIZE[ft]; + } + samp->data[0] = toc; + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_SMV: + for (i=0; idataLength = GF_SMV_EVRC_RATE_TO_SIZE[2*i+1] - 1; + break; + } + } + if (!samp->dataLength) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Corrupted TOC (%d)", toc); + goto exit; + } + samp->data[0] = toc; + break; + } + + if (samp->dataLength) + fread( samp->data + 1, samp->dataLength, 1, mdia); + + samp->dataLength += 1; + /*if last frame is "no data", abort - this happens in many files with constant mode (ie constant files), where + adding this last frame will result in a non-compact version of the stsz table, hence a bigger file*/ + if ((samp->dataLength==1) && feof(mdia)) + break; + + + if (import->flags & GF_IMPORT_USE_DATAREF) { + e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset); + } else if (!nb_frames) { + e = gf_isom_add_sample(import->dest, track, di, samp); + } else { + e = gf_isom_append_sample_data(import->dest, track, samp->data, samp->dataLength); + } + if (e) goto exit; + nb_frames++; + if (nb_frames==gpp_cfg.frames_per_sample) nb_frames=0; + + samp->DTS += block_size; + media_done += samp->dataLength; + gf_set_progress(msg, (u32) media_done, (u32) media_size); + if (duration && (samp->DTS > duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + } + gf_isom_sample_del(&samp); + gf_isom_refresh_size_info(import->dest, track); + + if (import->flags & GF_IMPORT_FORCE_MPEG4) MP4T_RecomputeBitRate(import->dest, track); + + if (update_gpp_cfg) gf_isom_3gp_config_update(import->dest, track, &gpp_cfg, 1); + +exit: + if (delete_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + fclose(mdia); + return e; +} + +/*QCP codec GUIDs*/ +static const char *QCP_QCELP_GUID_1 = "\x41\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E"; +static const char *QCP_QCELP_GUID_2 = "\x42\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E"; +static const char *QCP_EVRC_GUID = "\x8D\xD4\x89\xE6\x76\x90\xB5\x46\x91\xEF\x73\x6A\x51\x00\xCE\xB4"; +static const char *QCP_SMV_GUID = "\x75\x2B\x7C\x8D\x97\xA7\x46\xED\x98\x5E\xD5\x3C\x8C\xC7\x5F\x84"; + +GF_Err gf_import_qcp(GF_MediaImporter *import) +{ + GF_Err e; + u32 track, trackID, di, i, nb_pck, max_size, duration, riff_size, chunk_size, major, minor, version, avg_bps, pck_size, block_size, bps, samplerate, vrat_rate_flag, size_in_packets, nb_frames; + GF_BitStream *bs; + GF_ISOSample *samp; + char magic[12], GUID[16], name[81], fmt[162]; + u32 media_size, media_done, offset, rtable_cnt; + Bool has_pad; + QCPRateTable rtable[8]; + Bool delete_esd; + GF_3GPConfig gpp_cfg; + FILE *mdia; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->nb_tracks = 1; + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF | GF_IMPORT_FORCE_MPEG4 | GF_IMPORT_3GPP_AGGREGATION; + return GF_OK; + } + + memset(&gpp_cfg, 0, sizeof(GF_3GPConfig)); + delete_esd = 0; + + mdia = gf_f64_open(import->in_name, "rb"); + if (!mdia) return gf_import_message(import, GF_URL_ERROR, "Cannot find file %s", import->in_name); + + bs = gf_bs_from_file(mdia, GF_BITSTREAM_READ); + gf_bs_read_data(bs, magic, 4); + if (strnicmp(magic, "RIFF", 4)) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Broken QCP file: RIFF header not found"); + goto exit; + } + riff_size = gf_bs_read_u32_le(bs); + gf_bs_read_data(bs, fmt, 162); + gf_bs_seek(bs, 8); + gf_bs_read_data(bs, magic, 4); + if (strnicmp(magic, "QLCM", 4)) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Broken QCP file: QLCM header not found"); + goto exit; + } + max_size = (u32) gf_bs_get_size(bs); + if (max_size != riff_size+8) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Broken QCP file: Expecting RIFF-Size %d got %d", max_size-8, riff_size); + goto exit; + } + /*fmt*/ + gf_bs_read_data(bs, magic, 4); + if (strnicmp(magic, "fmt ", 4)) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Broken QCP file: FMT not found"); + goto exit; + } + chunk_size = gf_bs_read_u32_le(bs); + has_pad = (chunk_size%2) ? 1 : 0; + major = gf_bs_read_u8(bs); + minor = gf_bs_read_u8(bs); + chunk_size -= 2; + /*codec info*/ + gf_bs_read_data(bs, GUID, 16); + version = gf_bs_read_u16_le(bs); + chunk_size -= 18; + gf_bs_read_data(bs, name, 80); + name[80]=0; + chunk_size -= 80; + avg_bps = gf_bs_read_u16_le(bs); + pck_size = gf_bs_read_u16_le(bs); + block_size = gf_bs_read_u16_le(bs); + samplerate = gf_bs_read_u16_le(bs); + bps = gf_bs_read_u16_le(bs); + rtable_cnt = gf_bs_read_u32_le(bs); + chunk_size -= 14; + /*skip var rate*/ + for (i=0; i<8; i++) { + rtable[i].pck_size = gf_bs_read_u8(bs); + rtable[i].rate_idx = gf_bs_read_u8(bs); + } + chunk_size -= 16; + gf_bs_skip_bytes(bs, 5*4);/*reserved*/ + chunk_size -= 20; + gf_bs_skip_bytes(bs, chunk_size); + if (has_pad) gf_bs_read_u8(bs); + + if (!strncmp(GUID, QCP_QCELP_GUID_1, 16) || !strncmp(GUID, QCP_QCELP_GUID_2, 16)) { + gpp_cfg.type = GF_ISOM_SUBTYPE_3GP_QCELP; + strcpy(name, "QCELP-13K"); + } else if (!strncmp(GUID, QCP_EVRC_GUID, 16)) { + gpp_cfg.type = GF_ISOM_SUBTYPE_3GP_EVRC; + strcpy(name, "EVRC"); + } else if (!strncmp(GUID, QCP_SMV_GUID, 16)) { + gpp_cfg.type = GF_ISOM_SUBTYPE_3GP_SMV; + strcpy(name, "SMV"); + } else { + gpp_cfg.type = 0; + } + /*vrat*/ + gf_bs_read_data(bs, magic, 4); + if (strnicmp(magic, "vrat", 4)) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Broken QCP file: VRAT not found"); + goto exit; + } + chunk_size = gf_bs_read_u32_le(bs); + has_pad = (chunk_size%2) ? 1 : 0; + vrat_rate_flag = gf_bs_read_u32_le(bs); + size_in_packets = gf_bs_read_u32_le(bs); + chunk_size -= 8; + gf_bs_skip_bytes(bs, chunk_size); + if (has_pad) gf_bs_read_u8(bs); + + if (!gpp_cfg.type) { + e = gf_import_message(import, GF_NOT_SUPPORTED, "Unknown QCP file codec %s", name); + goto exit; + } + + gf_import_message(import, GF_OK, "Importing %s Audio - SampleRate %d", name, samplerate); + + trackID = 0; + e = GF_OK; + if (import->esd) trackID = import->esd->ESID; + + track = gf_isom_new_track(import->dest, trackID, GF_ISOM_MEDIA_AUDIO, samplerate); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (import->esd && !import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = gf_isom_get_track_id(import->dest, track); + + if (import->flags & GF_IMPORT_FORCE_MPEG4) { + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(2); + delete_esd = 1; + } + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig*)gf_odf_desc_new(GF_ODF_DCD_TAG); + import->esd->decoderConfig->streamType = 0x05; + switch (gpp_cfg.type) { + case GF_ISOM_SUBTYPE_3GP_QCELP: + import->esd->decoderConfig->objectTypeIndication = 0xE1; + /*DSI is fmt*/ + if (!import->esd->decoderConfig->decoderSpecificInfo) import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + if (import->esd->decoderConfig->decoderSpecificInfo->data) free(import->esd->decoderConfig->decoderSpecificInfo->data); + import->esd->decoderConfig->decoderSpecificInfo->dataLength = 162; + import->esd->decoderConfig->decoderSpecificInfo->data = (char*)malloc(sizeof(char)*162); + memcpy(import->esd->decoderConfig->decoderSpecificInfo->data, fmt, 162); + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + import->esd->decoderConfig->objectTypeIndication = 0xA0; + break; + case GF_ISOM_SUBTYPE_3GP_SMV: + import->esd->decoderConfig->objectTypeIndication = 0xA1; + break; + } + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + if (e) goto exit; + } else { + if (import->frames_per_sample<=1) import->frames_per_sample=1; + else if (import->frames_per_sample>15) import->frames_per_sample=15; + gpp_cfg.frames_per_sample = import->frames_per_sample; + e = gf_isom_3gp_config_new(import->dest, track, &gpp_cfg, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + if (e) goto exit; + } + gf_isom_set_audio_info(import->dest, track, di, samplerate, 1, (u8) bps); + + duration = import->duration * samplerate; + duration /= 1000; + + samp = gf_isom_sample_new(); + samp->data = (char*)malloc(sizeof(char) * 200); + samp->IsRAP = 1; + max_size = 200; + offset = ftell(mdia); + fseek(mdia, 0, SEEK_END); + media_size = ftell(mdia) - offset; + fseek(mdia, offset, SEEK_SET); + + nb_pck = 0; + media_done = 0; + nb_frames = 0; + while (gf_bs_available(bs)) { + gf_bs_read_data(bs, magic, 4); + chunk_size = gf_bs_read_u32_le(bs); + has_pad = (chunk_size%2) ? 1 : 0; + /*process chunk by chunk*/ + if (!strnicmp(magic, "data", 4)) { + + while (chunk_size) { + u32 idx = 0; + u32 size = 0; + + offset = (u32) gf_bs_get_position(bs); + /*get frame rate idx*/ + if (vrat_rate_flag) { + idx = gf_bs_read_u8(bs); + chunk_size-=1; + for (i=0; idataLength = size+1; + } else { + size = pck_size; + samp->dataLength = size; + } + if (max_sizedataLength) { + samp->data = (char*)realloc(samp->data, sizeof(char)*samp->dataLength); + max_size=samp->dataLength; + } + if (import->flags & GF_IMPORT_USE_DATAREF) { + e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset); + gf_bs_skip_bytes(bs, size); + } else { + if (vrat_rate_flag) { + samp->data[0] = idx; + gf_bs_read_data(bs, &samp->data[1], size); + } else { + gf_bs_read_data(bs, samp->data, size); + } + if (!nb_frames) { + e = gf_isom_add_sample(import->dest, track, di, samp); + } else { + e = gf_isom_append_sample_data(import->dest, track, samp->data, samp->dataLength); + } + nb_frames++; + if (nb_frames==import->frames_per_sample) nb_frames=0; + } + if (e) goto exit; + chunk_size -= size; + samp->DTS += block_size; + if (size_in_packets) { + gf_set_progress("Importing QCP", (u32) nb_pck, (u32) size_in_packets); + } else { + gf_set_progress("Importing QCP", (u32) media_done, (u32) media_size); + } + nb_pck++; + media_done += samp->dataLength; + if (duration && (samp->DTS > duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + } + } else if (!strnicmp(magic, "labl", 4)) { + } else if (!strnicmp(magic, "offs", 4)) { + } else if (!strnicmp(magic, "cnfg", 4)) { + } else if (!strnicmp(magic, "text", 4)) { + } + gf_bs_skip_bytes(bs, chunk_size); + if (has_pad) gf_bs_read_u8(bs); + } + gf_isom_sample_del(&samp); + gf_isom_set_brand_info(import->dest, GF_ISOM_BRAND_3G2A, 65536); + if (import->flags & GF_IMPORT_FORCE_MPEG4) MP4T_RecomputeBitRate(import->dest, track); + gf_set_progress("Importing QCP", size_in_packets, size_in_packets); + +exit: + if (delete_esd && import->esd) { + gf_odf_desc_del((GF_Descriptor *)import->esd); + import->esd = NULL; + } + gf_bs_del(bs); + fclose(mdia); + return e; +} + +/*read that amount of data at each IO access rather than fetching byte by byte...*/ +Bool H263_IsStartCode(GF_BitStream *bs) +{ + u32 c; + c = gf_bs_peek_bits(bs, 22, 0); + if (c==0x20) return 1; + return 0; +} + +#define H263_CACHE_SIZE 4096 +u32 H263_NextStartCode(GF_BitStream *bs) +{ + u32 v, bpos; + unsigned char h263_cache[H263_CACHE_SIZE]; + u64 end, cache_start, load_size; + u64 start = gf_bs_get_position(bs); + + /*skip 16b header*/ + gf_bs_read_u16(bs); + bpos = 0; + load_size = 0; + cache_start = 0; + end = 0; + v = 0xffffffff; + while (!end) { + /*refill cache*/ + if (bpos == (u32) load_size) { + if (!gf_bs_available(bs)) break; + load_size = gf_bs_available(bs); + if (load_size>H263_CACHE_SIZE) load_size=H263_CACHE_SIZE; + bpos = 0; + cache_start = gf_bs_get_position(bs); + gf_bs_read_data(bs, h263_cache, (u32) load_size); + } + v = (v<<8) | h263_cache[bpos]; + bpos++; + if ((v >> (32-22)) == 0x20) end = cache_start+bpos-4; + } + gf_bs_seek(bs, start); + if (!end) end = gf_bs_get_size(bs); + return (u32) (end-start); +} +static void h263_get_pic_size(GF_BitStream *bs, u32 fmt, u32 *w, u32 *h) +{ + switch (fmt) { + case 1: *w = 128; *h = 96; break; + case 2: *w = 176; *h = 144; break; + case 3: *w = 352; *h = 288; break; + case 4: *w = 704; *h = 576; break; + case 5: *w = 1409; *h = 1152 ; break; + default: *w = *h = 0; break; + } +} + +GF_Err gf_import_h263(GF_MediaImporter *import) +{ + GF_Err e; + u32 track, trackID, di, max_size, timescale, duration, w, h, fmt, nb_samp, dts_inc; + u64 offset; + GF_ISOSample *samp; + char *samp_data; + u32 media_size, media_done; + GF_3GPConfig gpp_cfg; + Double FPS; + FILE *mdia; + GF_BitStream *bs; + + mdia = gf_f64_open(import->in_name, "rb"); + if (!mdia) return gf_import_message(import, GF_URL_ERROR, "Cannot find file %s", import->in_name); + + e = GF_OK; + bs = gf_bs_from_file(mdia, GF_BITSTREAM_READ); + if (!H263_IsStartCode(bs)) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Cannot find H263 Picture Start Code"); + goto exit; + } + /*no auto frame-rate detection*/ + if (import->video_fps == 10000.0) import->video_fps = 25.0; + + FPS = (Double) import->video_fps; + /*for H263 we use 15 fps by default!!*/ + if (!FPS) FPS = 15; + get_video_timing(FPS, ×cale, &dts_inc); + + /*parse header*/ + gf_bs_read_int(bs, 22); + gf_bs_read_int(bs, 8); + /*spare+0+split_screen_indicator+document_camera_indicator+freeze_picture_release*/ + gf_bs_read_int(bs, 5); + + fmt = gf_bs_read_int(bs, 3); + h263_get_pic_size(bs, fmt, &w, &h); + if (!w || !h) { + e = gf_import_message(import, GF_NOT_SUPPORTED, "Unsupported H263 frame header"); + goto exit; + } + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->nb_tracks = 1; + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF | GF_IMPORT_OVERRIDE_FPS; + import->tk_info[0].video_info.width = w; + import->tk_info[0].video_info.height = h; + goto exit; + } + + trackID = 0; + e = GF_OK; + if (import->esd) { + trackID = import->esd->ESID; + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG); + } + track = gf_isom_new_track(import->dest, trackID, GF_ISOM_MEDIA_VISUAL, timescale); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (import->esd && !import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = gf_isom_get_track_id(import->dest, track); + + memset(&gpp_cfg, 0, sizeof(GF_3GPConfig)); + gpp_cfg.type = GF_ISOM_SUBTYPE_3GP_H263; + gpp_cfg.vendor = GF_4CC('G','P','A','C'); + /*FIXME - we need more in-depth parsing of the bitstream to detect P3@L10 (streaming wireless)*/ + gpp_cfg.H263_profile = 0; + gpp_cfg.H263_level = 10; + e = gf_isom_3gp_config_new(import->dest, track, &gpp_cfg, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + if (e) goto exit; + gf_isom_set_visual_info(import->dest, track, di, w, h); + gf_import_message(import, GF_OK, "Importing H263 video - %d x %d @ %02.4f", w, h, FPS); + + samp = gf_isom_sample_new(); + + duration = (u32) ( ((Double)import->duration) * timescale / 1000.0); + media_size = (u32) gf_bs_get_size(bs); + nb_samp = media_done = 0; + + max_size = 4096; + samp_data = (char*)malloc(sizeof(char)*max_size); + gf_bs_seek(bs, 0); + offset = 0; + while (gf_bs_available(bs)) { + samp->dataLength = H263_NextStartCode(bs); + if (samp->dataLength>max_size) { + max_size = samp->dataLength; + samp_data = (char*)realloc(samp_data, sizeof(char)*max_size); + } + gf_bs_read_data(bs, samp_data, samp->dataLength); + /*we ignore pict number and import at const FPS*/ + samp->IsRAP = (samp_data[4]&0x02) ? 0 : 1; + samp->data = samp_data; + if (import->flags & GF_IMPORT_USE_DATAREF) { + e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset); + } else { + e = gf_isom_add_sample(import->dest, track, di, samp); + } + if (e) goto exit; + samp->data = NULL; + samp->DTS += dts_inc; + nb_samp ++; + offset += samp->dataLength; + gf_set_progress("Importing H263", media_done, media_size); + media_done += samp->dataLength; + + if ((duration && (samp->DTS > duration) ) || (import->flags & GF_IMPORT_DO_ABORT)) { + break; + } + } + free(samp_data); + gf_isom_sample_del(&samp); + gf_set_progress("Importing H263", nb_samp, nb_samp); + gf_isom_modify_alternate_brand(import->dest, GF_4CC('3','g','g','6'), 1); + gf_isom_modify_alternate_brand(import->dest, GF_4CC('3','g','g','5'), 1); + +exit: + gf_bs_del(bs); + fclose(mdia); + return e; +} +static void avc_rewrite_samples(GF_ISOFile *file, u32 track, u32 prev_size, u32 new_size) +{ + u32 i, count, di, remain, msize; + char *buffer; + + msize = 4096; + buffer = (char*)malloc(sizeof(char)*msize); + count = gf_isom_get_sample_count(file, track); + for (i=0; idata, samp->dataLength, GF_BITSTREAM_READ); + GF_BitStream *newbs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + remain = samp->dataLength; + while (remain) { + u32 size = gf_bs_read_int(oldbs, prev_size); + gf_bs_write_int(newbs, size, new_size); + remain -= prev_size/8; + if (size>msize) { + msize = size; + buffer = (char*)realloc(buffer, sizeof(char)*msize); + } + gf_bs_read_data(oldbs, buffer, size); + gf_bs_write_data(newbs, buffer, size); + remain -= size; + } + gf_bs_del(oldbs); + free(samp->data); + samp->data = NULL; + samp->dataLength = 0; + gf_bs_get_content(newbs, &samp->data, &samp->dataLength); + gf_bs_del(newbs); + gf_isom_update_sample(file, track, i+1, samp, 1); + gf_isom_sample_del(&samp); + } + free(buffer); +} + +GF_Err gf_import_h264(GF_MediaImporter *import) +{ + u64 nal_start, nal_end, total_size; + u32 nal_size, track, trackID, di, cur_samp, nb_i, nb_idr, nb_p, nb_b, nb_sp, nb_si, nb_sei, max_w, max_h, duration, max_delay, max_total_delay; + s32 idx; + u8 nal_type; + GF_Err e; + FILE *mdia; + AVCState avc; + GF_AVCConfigSlot *slc; + GF_AVCConfig *avccfg; + GF_BitStream *bs; + GF_BitStream *sample_data; + Bool flush_sample, sample_is_rap, first_nal, slice_is_ref, has_cts_offset, detect_fps, is_paff; + u32 b_frames, ref_frame, pred_frame, timescale, copy_size, size_length, dts_inc; + s32 last_poc, max_last_poc, max_last_b_poc, poc_diff, prev_last_poc, min_poc, poc_shift; + Double FPS; + char *buffer; + u32 max_size = 4096; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->nb_tracks = 1; + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[0].flags = GF_IMPORT_OVERRIDE_FPS | GF_IMPORT_FORCE_PACKED; + return GF_OK; + } + + mdia = gf_f64_open(import->in_name, "rb"); + if (!mdia) return gf_import_message(import, GF_URL_ERROR, "Cannot find file %s", import->in_name); + + detect_fps = 0; + if (import->video_fps == 10000.0) { + import->video_fps = 25.0; + detect_fps = 1; + } + + FPS = (Double) import->video_fps; + if (!FPS) FPS = GF_IMPORT_DEFAULT_FPS; + get_video_timing(FPS, ×cale, &dts_inc); + +restart_import: + + memset(&avc, 0, sizeof(AVCState)); + avccfg = gf_odf_avc_cfg_new(); + buffer = (char*)malloc(sizeof(char) * max_size); + sample_data = NULL; + + bs = gf_bs_from_file(mdia, GF_BITSTREAM_READ); + if (!AVC_IsStartCode(bs)) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Cannot find H264 start code"); + goto exit; + } + + /*NALU size packing disabled*/ + if (!(import->flags & GF_IMPORT_FORCE_PACKED)) size_length = 32; + /*if import in edit mode, use smallest NAL size and adjust on the fly*/ + else if (gf_isom_get_mode(import->dest)!=GF_ISOM_OPEN_WRITE) size_length = 8; + else size_length = 32; + + trackID = 0; + e = GF_OK; + if (import->esd) trackID = import->esd->ESID; + + track = gf_isom_new_track(import->dest, trackID, GF_ISOM_MEDIA_VISUAL, timescale); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (import->esd && !import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = gf_isom_get_track_id(import->dest, track); + + e = gf_isom_avc_config_new(import->dest, track, avccfg, NULL, NULL, &di); + if (e) goto exit; + + sample_data = NULL; + sample_is_rap = 0; + cur_samp = 0; + is_paff = 0; + total_size = gf_bs_get_size(bs); + nal_start = gf_bs_get_position(bs); + duration = (u32) ( ((Double)import->duration) * timescale / 1000.0); + + nb_i = nb_idr = nb_p = nb_b = nb_sp = nb_si = nb_sei = 0; + max_w = max_h = 0; + first_nal = 1; + b_frames = ref_frame = pred_frame = 0; + last_poc = max_last_poc = max_last_b_poc = prev_last_poc = 0; + max_total_delay = max_delay = 0; + + gf_isom_set_cts_packing(import->dest, track, 1); + has_cts_offset = 0; + poc_diff = 0; + min_poc = 0; + poc_shift = 0; + + while (gf_bs_available(bs)) { + u8 nal_hdr, skip_nal; + nal_size = AVC_NextStartCode(bs); + + if (nal_size>max_size) { + buffer = (char*)realloc(buffer, sizeof(char)*nal_size); + max_size = nal_size; + } + gf_bs_read_data(bs, buffer, nal_size); + gf_bs_seek(bs, nal_start); + + nal_hdr = gf_bs_read_u8(bs); + nal_type = nal_hdr & 0x1F; + + skip_nal = 0; + copy_size = flush_sample = 0; + switch (AVC_ParseNALU(bs, nal_hdr, &avc)) { + case 1: + flush_sample = 1; + break; + case -1: + gf_import_message(import, GF_OK, "Waring: Error parsing NAL unit"); + skip_nal = 1; + break; + case -2: + skip_nal = 1; + break; + default: + break; + } + + if (AVC_NALUIsSlice(nal_type)) { + if (! skip_nal) { + copy_size = nal_size; + switch (avc.s_info.slice_type) { + case GF_AVC_TYPE_P: case GF_AVC_TYPE2_P: nb_p++; break; + case GF_AVC_TYPE_I: case GF_AVC_TYPE2_I: nb_i++; break; + case GF_AVC_TYPE_B: case GF_AVC_TYPE2_B: nb_b++; break; + case GF_AVC_TYPE_SP: case GF_AVC_TYPE2_SP: nb_sp++; break; + case GF_AVC_TYPE_SI: case GF_AVC_TYPE2_SI: nb_si++; break; + } + } + } else { + switch (nal_type) { + case GF_AVC_NALU_SEQ_PARAM: + idx = AVC_ReadSeqInfo(bs, &avc, NULL); + if (idx<0) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing SeqInfo"); + goto exit; + } + if (avc.sps[idx].status==1) { + avc.sps[idx].status = 2; + avccfg->configurationVersion = 1; + avccfg->profile_compatibility = avc.sps[idx].prof_compat; + avccfg->AVCProfileIndication = avc.sps[idx].profile_idc; + avccfg->AVCLevelIndication = avc.sps[idx].level_idc; + slc = (GF_AVCConfigSlot*)malloc(sizeof(GF_AVCConfigSlot)); + slc->size = nal_size; + slc->data = (char*)malloc(sizeof(char)*slc->size); + memcpy(slc->data, buffer, sizeof(char)*slc->size); + gf_list_add(avccfg->sequenceParameterSets, slc); + /*disable frame rate scan, most bitstreams have wrong values there*/ + if (detect_fps && avc.sps[idx].timing_info_present_flag && avc.sps[idx].fixed_frame_rate_flag + /*if detected FPS is greater than 50, assume wrong timing info*/ + && (avc.sps[idx].time_scale <= 50*avc.sps[idx].num_units_in_tick) + ) { + timescale = avc.sps[idx].time_scale; + dts_inc = avc.sps[idx].num_units_in_tick; + FPS = (Double)timescale / dts_inc; + detect_fps = 0; + gf_isom_remove_track(import->dest, track); + if (sample_data) gf_bs_del(sample_data); + gf_odf_avc_cfg_del(avccfg); + avccfg = NULL; + free(buffer); + buffer = NULL; + gf_bs_del(bs); + bs = NULL; + gf_f64_seek(mdia, 0, SEEK_SET); + goto restart_import; + } + if (!idx) { + gf_import_message(import, GF_OK, "AVC-H264 import - frame size %d x %d at %02.3f FPS", avc.sps[idx].width, avc.sps[idx].height, FPS); + } + if ((max_w <= avc.sps[idx].width) && (max_h <= avc.sps[idx].height)) { + max_w = avc.sps[idx].width; + max_h = avc.sps[idx].height; + } + } + break; + case GF_AVC_NALU_PIC_PARAM: + idx = AVC_ReadPictParamSet(bs, &avc); + if (idx<0) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing Picture Param"); + goto exit; + } + if (avc.pps[idx].status==1) { + avc.pps[idx].status = 2; + slc = (GF_AVCConfigSlot*)malloc(sizeof(GF_AVCConfigSlot)); + slc->size = nal_size; + slc->data = (char*)malloc(sizeof(char)*slc->size); + memcpy(slc->data, buffer, sizeof(char)*slc->size); + gf_list_add(avccfg->pictureParameterSets, slc); + } + break; + case GF_AVC_NALU_SEI: + copy_size = AVC_ReformatSEI_NALU(buffer, nal_size, &avc); + if (copy_size) nb_sei++; + break; + /*remove*/ + case GF_AVC_NALU_ACCESS_UNIT: + case GF_AVC_NALU_FILLER_DATA: + case GF_AVC_NALU_END_OF_SEQ: + case GF_AVC_NALU_END_OF_STREAM: + break; + default: + gf_import_message(import, GF_OK, "WARNING: NAL Unit type %d not handled - adding", nal_type); + copy_size = nal_size; + break; + } + } + if (!nal_size) break; + + if (flush_sample && sample_data) { + GF_ISOSample *samp = gf_isom_sample_new(); + samp->DTS = dts_inc*cur_samp; + samp->IsRAP = sample_is_rap; + gf_bs_get_content(sample_data, &samp->data, &samp->dataLength); + gf_bs_del(sample_data); + sample_data = NULL; + /*CTS recomuting is much trickier than with MPEG-4 ASP due to b-slice used as references - we therefore + store the poc as the CTS offset and update the whole table at the end*/ + samp->CTS_Offset = last_poc - poc_shift; + assert(samp->CTS_Offset>=0); + e = gf_isom_add_sample(import->dest, track, di, samp); + if (e) goto exit; + + gf_isom_sample_del(&samp); + cur_samp++; + gf_set_progress("Importing AVC-H264", (u32) (nal_start/1024), (u32) (total_size/1024) ); + first_nal = 1; + + if (min_poc > last_poc) + min_poc = last_poc; + } + + if (copy_size) { + if ((size_length<32) && ( (u32) (1<dest, track, size_length, size_length+diff_size); + + /*rewrite current sample*/ + if (sample_data) { + char *sd; + u32 sd_l; + GF_BitStream *prev_sd; + gf_bs_get_content(sample_data, &sd, &sd_l); + gf_bs_del(sample_data); + sample_data = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + prev_sd = gf_bs_new(sd, sd_l, GF_BITSTREAM_READ); + while (gf_bs_available(prev_sd)) { + char *buf; + u32 s = gf_bs_read_int(prev_sd, size_length); + gf_bs_write_int(sample_data, s, size_length+diff_size); + buf = (char*)malloc(sizeof(char)*s); + gf_bs_read_data(prev_sd, buf, s); + gf_bs_write_data(sample_data, buf, s); + free(buf); + } + gf_bs_del(prev_sd); + free(sd); + } + size_length+=diff_size; + + } + if (!sample_data) sample_data = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(sample_data, copy_size, size_length); + gf_bs_write_data(sample_data, buffer, copy_size); + if (AVC_NALUIsSlice(nal_type) ) { + if (!is_paff && avc.s_info.bottom_field_flag) + is_paff = 1; + + if (first_nal) { + first_nal = 0; + /*we only indicate TRUE IDRs for sync samples (cf AVC file format spec). + SEI recovery should be used to build sampleToGroup & RollRecovery tables*/ + avc.sei.recovery_point.valid = 0; + + sample_is_rap = AVC_SliceIsIDR(&avc); + } + slice_is_ref = (avc.s_info.nal_unit_type==GF_AVC_NALU_IDR_SLICE); + if (slice_is_ref) nb_idr++; + + if (avc.s_info.pocdest, track, j, NULL, NULL); + if (!samp) break; + samp->CTS_Offset += poc_shift; + samp->CTS_Offset -= avc.s_info.poc; + gf_isom_modify_cts_offset(import->dest, track, j, samp->CTS_Offset); + gf_isom_sample_del(&samp); + } + } + poc_shift = avc.s_info.poc; + } + + /*if #pics, compute smallest POC increase*/ + if (avc.s_info.poc != last_poc) { + if (!poc_diff || (poc_diff > abs(avc.s_info.poc-last_poc))) + poc_diff = abs(avc.s_info.poc-last_poc); + last_poc = avc.s_info.poc; + } + /*ref slice, reset poc*/ + if (slice_is_ref) { + ref_frame = cur_samp+1; + max_last_poc = last_poc = max_last_b_poc = 0; + pred_frame = 0; + b_frames = 0; + max_delay = 0; + poc_shift = 0; + } + /*strictly less - this is a new P slice*/ + else if (max_last_poclast_poc) { + /*need to store TS offsets*/ + has_cts_offset = 1; + switch (avc.s_info.slice_type) { + case GF_AVC_TYPE_B: + case GF_AVC_TYPE2_B: + if (!max_last_b_poc) { + b_frames ++; + max_last_b_poc = last_poc; + } + /*we had a B-slice reference*/ + else if (last_poc duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + + /*consume next start code*/ + nal_start = AVC_IsStartCode(bs); + assert(nal_start); + nal_start = gf_bs_get_position(bs); + } + + /*final flush*/ + if (sample_data) { + GF_ISOSample *samp = gf_isom_sample_new(); + samp->DTS = dts_inc*cur_samp; + samp->IsRAP = sample_is_rap; + samp->CTS_Offset = last_poc - poc_shift; + gf_bs_get_content(sample_data, &samp->data, &samp->dataLength); + gf_bs_del(sample_data); + sample_data = NULL; + e = gf_isom_add_sample(import->dest, track, di, samp); + if (e) goto exit; + gf_isom_sample_del(&samp); + gf_set_progress("Importing AVC-H264", (u32) cur_samp, cur_samp+1); + cur_samp++; + } + + /*recompute all CTS offsets*/ + if (has_cts_offset) { + u32 i, last_cts_samp; + u64 last_dts, max_cts; + if (!poc_diff) poc_diff = 1; + /*no b-frame references, no need to cope with negative poc*/ + if (!max_total_delay) { + min_poc=0; + max_total_delay = 1; + } + cur_samp = gf_isom_get_sample_count(import->dest, track); + min_poc *= -1; + last_dts = 0; + max_cts = 0; + last_cts_samp = 0; + + for (i=0; idest, track, i+1, NULL, NULL); + /*poc re-init (RAP and POC to 0, otherwise that's SEI recovery), update base DTS*/ + if (samp->IsRAP /*&& !samp->CTS_Offset*/) last_dts = samp->DTS; + + /*CTS offset is frame POC (refers to last IDR)*/ + cts = (min_poc + (s32) samp->CTS_Offset) * dts_inc/poc_diff + (u32) (last_dts + max_total_delay*dts_inc); + + /*if PAFF, 2 pictures (eg poc) <=> 1 aggregated frame (eg sample), divide by 2*/ + if (is_paff) { + cts /= 2; + /*in some cases the poc is not on the top field - if that is the case, round up*/ + if (cts%dts_inc) { + cts = ((cts/dts_inc)+1)*dts_inc; + } + } + samp->CTS_Offset = (u32) (cts - samp->DTS); + + if (max_cts < samp->DTS + samp->CTS_Offset) { + max_cts = samp->DTS + samp->CTS_Offset; + last_cts_samp = i; + } + /*this should never happen, however some streams seem to do weird POC increases (cf sorenson streams, last 2 frames), + this should hopefully take care of some bugs and ensure proper CTS...*/ + if ((s32)samp->CTS_Offset<0) { + u32 j, k; + samp->CTS_Offset = 0; + gf_isom_modify_cts_offset(import->dest, track, i+1, samp->CTS_Offset); + for (j=last_cts_samp; jdest, track, j+1, NULL, NULL); + for (k=j+1; k<=i; k++) { + GF_ISOSample *bsamp = gf_isom_get_sample_info(import->dest, track, k+1, NULL, NULL); + if (asamp->CTS_Offset+asamp->DTS==bsamp->CTS_Offset+bsamp->DTS) { + max_cts += dts_inc; + bsamp->CTS_Offset = (u32) (max_cts - bsamp->DTS); + gf_isom_modify_cts_offset(import->dest, track, k+1, bsamp->CTS_Offset); + } + gf_isom_sample_del(&bsamp); + } + gf_isom_sample_del(&asamp); + } + max_cts = samp->DTS + samp->CTS_Offset; + } else { + gf_isom_modify_cts_offset(import->dest, track, i+1, samp->CTS_Offset); + } + gf_isom_sample_del(&samp); + } + /*and repack table*/ + gf_isom_set_cts_packing(import->dest, track, 0); + } else { + gf_isom_remove_cts_info(import->dest, track); + } + + gf_set_progress("Importing AVC-H264", (u32) cur_samp, cur_samp); + + gf_isom_set_visual_info(import->dest, track, di, max_w, max_h); + avccfg->nal_unit_size = size_length/8; + gf_isom_avc_config_update(import->dest, track, 1, avccfg); + gf_media_update_par(import->dest, track); + MP4T_RecomputeBitRate(import->dest, track); + + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_VISUAL, 0x15); + gf_isom_modify_alternate_brand(import->dest, GF_ISOM_BRAND_AVC1, 1); + + if (nb_sp || nb_si) { + gf_import_message(import, GF_OK, "Import results: %d samples - Slices: %d I %d P %d B %d SP %d SI - %d SEI - %d IDR", + cur_samp, nb_i, nb_p, nb_b, nb_sp, nb_si, nb_sei, nb_idr); + } else { + gf_import_message(import, GF_OK, "Import results: %d samples - Slices: %d I %d P %d B - %d SEI - %d IDR", + cur_samp, nb_i, nb_p, nb_b, nb_sei, nb_idr); + } + + if (max_total_delay>1) { + gf_import_message(import, GF_OK, "\tStream uses B-slice references - max frame delay %d", max_total_delay); + } + + /*rewrite ESD*/ + if (import->esd) { + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->predefined = 2; + import->esd->slConfig->timestampResolution = timescale; + if (import->esd->decoderConfig) gf_odf_desc_del((GF_Descriptor *)import->esd->decoderConfig); + import->esd->decoderConfig = gf_isom_get_decoder_config(import->dest, track, 1); + gf_isom_change_mpeg4_description(import->dest, track, 1, import->esd); + } + +exit: + if (sample_data) gf_bs_del(sample_data); + gf_odf_avc_cfg_del(avccfg); + free(buffer); + gf_bs_del(bs); + fclose(mdia); + return e; +} +#define OGG_BUFFER_SIZE 4096 + +Bool OGG_ReadPage(FILE *f_in, ogg_sync_state *oy, ogg_page *oggpage) +{ + if (feof(f_in)) return 0; + while (ogg_sync_pageout(oy, oggpage ) != 1 ) { + char *buffer = ogg_sync_buffer(oy, OGG_BUFFER_SIZE); + u32 bytes = fread(buffer, 1, OGG_BUFFER_SIZE, f_in); + ogg_sync_wrote(oy, bytes); + if (feof(f_in)) return 1; + } + return 1; +} + +static u32 get_ogg_serial_no_for_stream(char *fileName, u32 stream_num, Bool is_video) +{ + ogg_sync_state oy; + u32 track, serial_no; + ogg_page oggpage; + ogg_packet oggpacket; + ogg_stream_state os; + FILE *f_in; + + /*means first one*/ + if (!stream_num) return 0; + + f_in = gf_f64_open(fileName, "rb"); + if (!f_in) return 0; + + track = 0; + serial_no = 0; + ogg_sync_init(&oy); + while (1) { + if (!OGG_ReadPage(f_in, &oy, &oggpage)) break; + if (!ogg_page_bos(&oggpage)) break; + track ++; + if (track != stream_num) continue; + + serial_no = ogg_page_serialno(&oggpage); + ogg_stream_init(&os, serial_no); + ogg_stream_pagein(&os, &oggpage); + ogg_stream_packetpeek(&os, &oggpacket); + + if (is_video && (oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "theora", 6)) { + ogg_stream_clear(&os); + break; + } + if (!is_video && (oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "vorbis", 6)) { + ogg_stream_clear(&os); + break; + } + ogg_stream_clear(&os); + serial_no = 0; + } + ogg_sync_clear(&oy); + fclose(f_in); + return serial_no; +} + +GF_Err gf_import_ogg_video(GF_MediaImporter *import) +{ + GF_Err e; + ogg_sync_state oy; + u32 di, track, duration; + u64 tot_size, done; + u32 w, h, fps_num, fps_den, keyframe_freq_force, theora_kgs, flag, dts_inc, timescale; + Double FPS; + Bool destroy_esd, go; + u32 serial_no, sno, num_headers; + ogg_packet oggpacket; + ogg_page oggpage; + ogg_stream_state os; + oggpack_buffer opb; + GF_BitStream *bs; + FILE *f_in; + GF_ISOSample *samp; + + + dts_inc = 0; + /*assume audio or simple AV file*/ + if (import->flags & GF_IMPORT_PROBE_ONLY) { + f_in = gf_f64_open(import->in_name, "rb"); + if (!f_in) return GF_URL_ERROR; + + import->nb_tracks = 0; + go = 1; + ogg_sync_init(&oy); + while (go) { + if (!OGG_ReadPage(f_in, &oy, &oggpage)) break; + + if (!ogg_page_bos(&oggpage)) { + go = 0; + continue; + } + serial_no = ogg_page_serialno(&oggpage); + ogg_stream_init(&os, serial_no); + ogg_stream_pagein(&os, &oggpage); + ogg_stream_packetpeek(&os, &oggpacket); + + import->tk_info[import->nb_tracks].track_num = import->nb_tracks+1; + if ((oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "theora", 6)) { + import->tk_info[import->nb_tracks].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[import->nb_tracks].flags = GF_IMPORT_OVERRIDE_FPS; + + bs = gf_bs_new((char*)oggpacket.packet, oggpacket.bytes, GF_BITSTREAM_READ); + gf_bs_read_int(bs, 80); + import->tk_info[import->nb_tracks].video_info.width = gf_bs_read_u16(bs) << 4; + import->tk_info[import->nb_tracks].video_info.height = gf_bs_read_u16(bs) << 4; + gf_bs_read_int(bs, 64); + fps_num = gf_bs_read_u32(bs); + fps_den = gf_bs_read_u32(bs); + gf_bs_del(bs); + import->tk_info[import->nb_tracks].video_info.FPS = fps_num; + import->tk_info[import->nb_tracks].video_info.FPS /= fps_den; + import->tk_info[import->nb_tracks].media_type = GF_4CC('t','h','e','o'); + } else if ((oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "vorbis", 6)) { + import->tk_info[import->nb_tracks].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[import->nb_tracks].flags = 0; + } + ogg_stream_clear(&os); + import->nb_tracks++; + } + ogg_sync_clear(&oy); + fclose(f_in); + return GF_OK; + } + + if (import->flags & GF_IMPORT_USE_DATAREF) return gf_import_message(import, GF_NOT_SUPPORTED, "Cannot use data referencing with OGG files"); + + sno = get_ogg_serial_no_for_stream(import->in_name, import->trackID, 1); + /*not our stream*/ + if (!sno && import->trackID) return GF_OK; + + f_in = gf_f64_open(import->in_name, "rb"); + if (!f_in) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + + e = GF_OK; + done = 0; + gf_f64_seek(f_in, 0, SEEK_END); + tot_size = gf_f64_tell(f_in); + gf_f64_seek(f_in, 0, SEEK_SET); + + + destroy_esd = 0; + samp = gf_isom_sample_new(); + + /*avoids gcc warnings*/ + duration = 0; + FPS = 0; + num_headers = w = h = track = 0; + + ogg_sync_init(&oy); + + bs = NULL; + serial_no = 0; + go = 1; + while (go) { + if (!OGG_ReadPage(f_in, &oy, &oggpage)) break; + + if (ogg_page_bos(&oggpage)) { + if (serial_no) continue; + serial_no = ogg_page_serialno(&oggpage); + ogg_stream_init(&os, serial_no); + ogg_stream_pagein(&os, &oggpage); + ogg_stream_packetpeek(&os, &oggpacket); + + /*not our stream*/ + if (sno && (sno!=serial_no)) { + ogg_stream_clear(&os); + serial_no = 0; + continue; + } + if ((oggpacket.bytes < 7) || strncmp((char *)&oggpacket.packet[1], "theora", 6)) { + ogg_stream_clear(&os); + serial_no = 0; + continue; + } + /*that's ogg-theora*/ + bs = gf_bs_new((char *)oggpacket.packet, oggpacket.bytes, GF_BITSTREAM_READ); + gf_bs_read_int(bs, 80); + w = gf_bs_read_u16(bs) << 4; + h = gf_bs_read_u16(bs) << 4; + gf_bs_read_int(bs, 64); + fps_num = gf_bs_read_u32(bs); + fps_den = gf_bs_read_u32(bs); + gf_bs_read_int(bs, 80); + gf_bs_read_int(bs, 6); + keyframe_freq_force = 1 << gf_bs_read_int(bs, 5); + theora_kgs = 0; + keyframe_freq_force--; + while (keyframe_freq_force) { + theora_kgs ++; + keyframe_freq_force >>= 1; + } + gf_bs_del(bs); + + FPS = ((Double)fps_num) / fps_den; + + /*note that we don't rewrite theora headers (just like in MPEG-4 video, systems timing overrides stream one)*/ + if (import->video_fps) FPS = import->video_fps; + num_headers = 0; + + gf_import_message(import, GF_OK, "OGG Theora import - %2.4f FPS - Resolution %d x %d", FPS, w, h); + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + continue; + } + /*FIXME - check end of stream for concatenated files?*/ + + /*not our stream*/ + if (ogg_stream_pagein(&os, &oggpage) != 0) continue; + + + + while (ogg_stream_packetout(&os, &oggpacket ) > 0 ) { + if (num_headers<3) { + if(!w || !h) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Couldn't find Theora main header"); + goto exit; + } + /*copy headers*/ + gf_bs_write_u16(bs, oggpacket.bytes); + gf_bs_write_data(bs, (char *)oggpacket.packet, oggpacket.bytes); + num_headers++; + + /*let's go, create the track*/ + if (num_headers==3) { + if (!import->esd) { + destroy_esd = 1; + import->esd = gf_odf_desc_esd_new(0); + } + get_video_timing(FPS, ×cale, &dts_inc); + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_VISUAL, timescale); + if (!track) goto exit; + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->timestampResolution = timescale; + if (!import->esd->decoderConfig->decoderSpecificInfo) import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + gf_bs_get_content(bs, &import->esd->decoderConfig->decoderSpecificInfo->data, &import->esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + bs = NULL; + import->esd->decoderConfig->streamType = GF_STREAM_VISUAL; + import->esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_OGG; + + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, NULL, NULL, &di); + if (e) goto exit; + gf_isom_set_visual_info(import->dest, track, di, w, h); + + { + Double d = import->duration; + d *= import->esd->slConfig->timestampResolution; + d /= 1000; + duration = (u32) d; + } + } + + continue; + } + + /*we don't need adedicated parser for theora, just check it's a theora frame and get its key type + WATCHOUT theora bitsteram is in BE*/ + oggpackB_readinit(&opb, oggpacket.packet, oggpacket.bytes); + flag = oggpackB_read(&opb, 1); + if (flag==0) { + /*add packet*/ + samp->IsRAP = oggpackB_read(&opb, 1) ? 0 : 1; + samp->data = (char *)oggpacket.packet; + samp->dataLength = oggpacket.bytes; + e = gf_isom_add_sample(import->dest, track, di, samp); + if (e) goto exit; + samp->DTS += dts_inc; + } + + gf_set_progress("Importing OGG Video", (u32) (done/1024), (u32) (tot_size/1024)); + done += oggpacket.bytes; + if ((duration && (samp->DTS > duration) ) || (import->flags & GF_IMPORT_DO_ABORT)) { + go = 0; + break; + } + } + } + gf_set_progress("Importing OGG Video", (u32) (tot_size/1024), (u32) (tot_size/1024)); + + if (!serial_no) { + gf_import_message(import, GF_OK, "OGG: No supported video found"); + } else { + MP4T_RecomputeBitRate(import->dest, track); + + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_VISUAL, 0xFE); + } + +exit: + if (bs) gf_bs_del(bs); + samp->data = NULL; + gf_isom_sample_del(&samp); + ogg_sync_clear(&oy); + if (serial_no) ogg_stream_clear(&os); + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + fclose(f_in); + return e; +} + +GF_Err gf_import_ogg_audio(GF_MediaImporter *import) +{ + GF_Err e; + ogg_sync_state oy; + u32 di, track, duration; + u64 done, tot_size; + s32 block_size; + GF_ISOSample *samp; + Bool destroy_esd, go; + u32 serial_no, sno, num_headers; + ogg_packet oggpacket; + ogg_page oggpage; + ogg_stream_state os; + GF_VorbisParser vp; + GF_BitStream *vbs; + FILE *f_in; + + if (import->flags & GF_IMPORT_PROBE_ONLY) return GF_OK; + + if (import->flags & GF_IMPORT_USE_DATAREF) return gf_import_message(import, GF_NOT_SUPPORTED, "Cannot use data referencing with OGG files"); + + sno = get_ogg_serial_no_for_stream(import->in_name, import->trackID, 0); + /*not our stream*/ + if (!sno && import->trackID) return GF_OK; + + f_in = gf_f64_open(import->in_name, "rb"); + if (!f_in) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + + e = GF_OK; + + done = 0; + gf_f64_seek(f_in, 0, SEEK_END); + tot_size = gf_f64_tell(f_in); + gf_f64_seek(f_in, 0, SEEK_SET); + + destroy_esd = 0; + samp = gf_isom_sample_new(); + /*avoids gcc warnings*/ + track = num_headers = duration = 0; + + ogg_sync_init(&oy); + + vbs = NULL; + serial_no = 0; + go = 1; + while (go) { + if (!OGG_ReadPage(f_in, &oy, &oggpage)) break; + + if (ogg_page_bos(&oggpage)) { + if (serial_no) continue; + serial_no = ogg_page_serialno(&oggpage); + ogg_stream_init(&os, serial_no); + ogg_stream_pagein(&os, &oggpage); + ogg_stream_packetpeek(&os, &oggpacket); + /*not our stream*/ + if (sno && (sno!=serial_no)) { + ogg_stream_clear(&os); + serial_no = 0; + continue; + } + if ((oggpacket.bytes < 7) || strncmp((char *)&oggpacket.packet[1], "vorbis", 6)) { + ogg_stream_clear(&os); + serial_no = 0; + continue; + } + num_headers = 0; + memset(&vp, 0, sizeof(GF_VorbisParser)); + vbs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + continue; + } + /*FIXME - check end of stream for concatenated files?*/ + + /*not our stream*/ + if (ogg_stream_pagein(&os, &oggpage) != 0) continue; + + + + while (ogg_stream_packetout(&os, &oggpacket ) > 0 ) { + if (num_headers<3) { + if (!gf_vorbis_parse_header(&vp, (char*)oggpacket.packet, oggpacket.bytes)) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Corrupted OGG Vorbis header"); + goto exit; + } + + /*copy headers*/ + gf_bs_write_u16(vbs, oggpacket.bytes); + gf_bs_write_data(vbs, (char *)oggpacket.packet, oggpacket.bytes); + num_headers++; + + /*let's go, create the track*/ + if (num_headers==3) { + if (!vp.is_init) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Corrupted OGG Vorbis headers found"); + goto exit; + } + + gf_import_message(import, GF_OK, "OGG Vorbis import - sample rate %d - %d channel%s", vp.sample_rate, vp.channels, (vp.channels>1) ? "s" : ""); + + if (!import->esd) { + destroy_esd = 1; + import->esd = gf_odf_desc_esd_new(0); + } + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_AUDIO, vp.sample_rate); + if (!track) goto exit; + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->timestampResolution = vp.sample_rate; + if (!import->esd->decoderConfig->decoderSpecificInfo) import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + gf_bs_get_content(vbs, &import->esd->decoderConfig->decoderSpecificInfo->data, &import->esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(vbs); + vbs = NULL; + import->esd->decoderConfig->streamType = GF_STREAM_AUDIO; + import->esd->decoderConfig->avgBitrate = vp.avg_r; + import->esd->decoderConfig->maxBitrate = (vp.max_r>0) ? vp.max_r : vp.avg_r; + import->esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_OGG; + + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, NULL, NULL, &di); + if (e) goto exit; + gf_isom_set_audio_info(import->dest, track, di, vp.sample_rate, (vp.channels>1) ? 2 : 1, 16); + + { + Double d = import->duration; + d *= vp.sample_rate; + d /= 1000; + duration = (u32) d; + } + } + continue; + } + + block_size = gf_vorbis_check_frame(&vp, (char *)oggpacket.packet, oggpacket.bytes); + if (!block_size) continue; + + /*add packet*/ + samp->IsRAP = 1; + samp->data = (char *)oggpacket.packet; + samp->dataLength = oggpacket.bytes; + e = gf_isom_add_sample(import->dest, track, di, samp); + if (e) goto exit; + samp->DTS += block_size; + + gf_set_progress("Importing OGG Audio", (u32) done, (u32) tot_size); + done += oggpacket.bytes; + if ((duration && (samp->DTS > duration) ) || (import->flags & GF_IMPORT_DO_ABORT)) { + go = 0; + break; + } + } + } + gf_set_progress("Importing OGG Audio", (u32) tot_size, (u32) tot_size); + + if (!serial_no) { + gf_import_message(import, GF_OK, "OGG: No supported audio found"); + } else { + samp->data = NULL; + gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_AUDIO, 0xFE); + gf_set_progress("Importing OGG Audio", (u32) tot_size, (u32) tot_size); + + MP4T_RecomputeBitRate(import->dest, track); + } + +exit: + gf_isom_sample_del(&samp); + if (vbs) gf_bs_del(vbs); + if (serial_no) ogg_stream_clear(&os); + ogg_sync_clear(&oy); + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + fclose(f_in); + return e; +} + +GF_Err gf_import_raw_unit(GF_MediaImporter *import) +{ + GF_Err e; + GF_ISOSample *samp; + u32 mtype, track, di, timescale; + FILE *src; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->flags |= GF_IMPORT_USE_DATAREF; + return GF_OK; + } + + if (!import->esd || !import->esd->decoderConfig) { + return gf_import_message(import, GF_BAD_PARAM, "Raw stream needs ESD and DecoderConfig for import"); + } + + src = fopen(import->in_name, "rb"); + if (!src) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + + switch (import->esd->decoderConfig->streamType) { + case GF_STREAM_SCENE: mtype = GF_ISOM_MEDIA_SCENE; break; + case GF_STREAM_VISUAL: mtype = GF_ISOM_MEDIA_VISUAL; break; + case GF_STREAM_AUDIO: mtype = GF_ISOM_MEDIA_AUDIO; break; + case GF_STREAM_TEXT: mtype = GF_ISOM_MEDIA_TEXT; break; + case GF_STREAM_MPEG7: mtype = GF_ISOM_MEDIA_MPEG7; break; + case GF_STREAM_IPMP: mtype = GF_ISOM_MEDIA_IPMP; break; + case GF_STREAM_OCI: mtype = GF_ISOM_MEDIA_OCI; break; + case GF_STREAM_MPEGJ: mtype = GF_ISOM_MEDIA_MPEGJ; break; + case GF_STREAM_INTERACT: mtype = GF_STREAM_SCENE; break; + /*not sure about this one...*/ + case GF_STREAM_IPMP_TOOL: mtype = GF_ISOM_MEDIA_IPMP; break; + /*not sure about this one...*/ + case GF_STREAM_FONT: mtype = GF_ISOM_MEDIA_MPEGJ; break; + default: mtype = GF_ISOM_MEDIA_ESM; + } + timescale = import->esd->slConfig ? import->esd->slConfig->timestampResolution : 1000; + track = gf_isom_new_track(import->dest, import->esd->ESID, mtype, timescale); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + if (e) goto exit; + + gf_import_message(import, GF_OK, "Raw Access Unit import (StreamType %s)", gf_odf_stream_type_name(import->esd->decoderConfig->streamType)); + + samp = gf_isom_sample_new(); + fseek(src, 0, SEEK_END); + samp->dataLength = ftell(src); + fseek(src, 0, SEEK_SET); + samp->IsRAP = 1; + samp->data = (char *)malloc(sizeof(char)*samp->dataLength); + fread(samp->data, samp->dataLength, 1, src); + e = gf_isom_add_sample(import->dest, track, di, samp); + gf_isom_sample_del(&samp); + MP4T_RecomputeBitRate(import->dest, track); +exit: + fclose(src); + return e; +} + + +GF_Err gf_import_saf(GF_MediaImporter *import) +{ + GF_Err e; + u32 track, tot; + FILE *saf; + GF_BitStream *bs; + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->flags |= GF_IMPORT_USE_DATAREF; + } + + saf = fopen(import->in_name, "rb"); + if (!saf) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + + track = 0; + + bs = gf_bs_from_file(saf, GF_BITSTREAM_READ); + tot = (u32) gf_bs_get_size(bs); + + while (gf_bs_available(bs)) { + Bool is_rap; + u32 cts, au_size, type, stream_id; + is_rap = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 15); + gf_bs_read_int(bs, 2); + cts = gf_bs_read_int(bs, 30); + au_size = gf_bs_read_u16(bs); + if (au_size<2) { + gf_bs_del(bs); + fclose(saf); + return gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Invalid SAF Packet Header"); + } + type = gf_bs_read_int(bs, 4); + stream_id = gf_bs_read_int(bs, 12); + au_size-=2; + if (!stream_id) stream_id = 1000; + + if ((type==1) || (type==2) || (type==7)) { + Bool in_root_od = 0; + u32 mtype, stype; + char *name = "Unknown"; + + u8 oti = gf_bs_read_u8(bs); + u8 st = gf_bs_read_u8(bs); + u32 ts_res = gf_bs_read_u24(bs); + u32 buffersize_db = gf_bs_read_u16(bs); + + if (!ts_res) ts_res = 1000; + + au_size -= 7; + + + mtype = GF_ISOM_MEDIA_ESM; + stype = 0; + if (st==GF_STREAM_SCENE) { + mtype = GF_ISOM_MEDIA_SCENE; + name = (char *) ( (oti==0x09) ? "LASeR Scene" : "BIFS Scene" ); + stype = (oti==0x09) ? GF_4CC('L','A','S','R') : GF_4CC('B','I','F','S'); + in_root_od = 1; + } + else if (st==GF_STREAM_VISUAL) { + mtype = GF_ISOM_MEDIA_VISUAL; + switch (oti) { + case 0x21: name = "AVC/H264 Video"; stype = GF_4CC('H','2','6','4'); break; + case 0x20: name = "MPEG-4 Video"; stype = GF_4CC('M','P','4','V'); break; + case 0x6A: name = "MPEG-1 Video"; stype = GF_4CC('M','P','1','V'); break; + case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: name = "MPEG-2 Video"; stype = GF_4CC('M','P','2','V'); break; + case 0x6C: name = "JPEG Image"; stype = GF_4CC('J','P','E','G'); break; + case 0x6D: name = "PNG Image"; stype = GF_4CC('P','N','G',' '); break; + } + } + else if (st==GF_STREAM_AUDIO) { + mtype = GF_ISOM_MEDIA_AUDIO; + switch (oti) { + case 0x69: name = "MPEG-2 Audio"; stype = GF_4CC('M','P','2','A'); break; + case 0x6B: name = "MPEG-1 Audio"; stype = GF_4CC('M','P','1','A'); break; + case 0x40: name = "MPEG-4 Audio"; stype = GF_4CC('M','P','4','A'); break; + } + } + + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + u32 i, found; + found = 0; + for (i=0; inb_tracks; i++) { + if (import->tk_info[i].track_num==stream_id) { found = 1; break; } + } + if (!found) { + import->tk_info[import->nb_tracks].media_type = stype; + import->tk_info[import->nb_tracks].type = mtype; + import->tk_info[import->nb_tracks].flags = GF_IMPORT_USE_DATAREF; + import->tk_info[import->nb_tracks].track_num = stream_id; + import->nb_tracks++; + } + } else if ((stream_id==import->trackID) && !track) { + Bool delete_esd = 0; + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(0); + delete_esd = 1; + if (import->esd->URLString) free(import->esd->URLString); + import->esd->URLString = NULL; + } + import->esd->decoderConfig->streamType = st; + import->esd->decoderConfig->objectTypeIndication = oti; + import->esd->decoderConfig->bufferSizeDB = buffersize_db; + if ((st==0xFF) && (oti==0xFF)) { + assert(0); + } + if (type==7) { + u32 url_len = gf_bs_read_u16(bs); + import->esd->URLString = (char *)malloc(sizeof(char)*(url_len+1)); + gf_bs_read_data(bs, import->esd->URLString, url_len); + import->esd->URLString[url_len] = 0; + au_size-=2+url_len; + } + if (au_size) { + if (!import->esd->decoderConfig->decoderSpecificInfo) import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + if (import->esd->decoderConfig->decoderSpecificInfo->data ) free(import->esd->decoderConfig->decoderSpecificInfo->data); + import->esd->decoderConfig->decoderSpecificInfo->dataLength = au_size; + import->esd->decoderConfig->decoderSpecificInfo->data = (char *)malloc(sizeof(char)*au_size); + gf_bs_read_data(bs, import->esd->decoderConfig->decoderSpecificInfo->data, au_size); + au_size = 0; + } + if (gf_isom_get_track_by_id(import->dest, stream_id)) stream_id = 0; + track = gf_isom_new_track(import->dest, stream_id, mtype, ts_res); + gf_isom_set_track_enabled(import->dest, track, 1); + stream_id = import->final_trackID = import->esd->ESID = gf_isom_get_track_id(import->dest, track); + gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &mtype); + if (delete_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + if (in_root_od) gf_isom_add_track_to_root_od(import->dest, track); + gf_import_message(import, GF_OK, "Importing SAF stream %d: %s", stream_id, name); + } + } + else if ((type==4) && (stream_id==import->trackID) && track) { + GF_ISOSample *samp = gf_isom_sample_new(); + samp->dataLength = au_size; + samp->DTS = cts; + samp->IsRAP = is_rap; + if (import->flags & GF_IMPORT_USE_DATAREF) { + e = gf_isom_add_sample_reference(import->dest, track, 1, samp, gf_bs_get_position(bs) ); + } else { + samp->data = (char *)malloc(sizeof(char)*samp->dataLength); + gf_bs_read_data(bs, samp->data, samp->dataLength); + au_size = 0; + e = gf_isom_add_sample(import->dest, track, 1, samp); + } + gf_isom_sample_del(&samp); + if (e) { + gf_bs_del(bs); + fclose(saf); + return e; + } + gf_set_progress("Importing SAF", (u32) gf_bs_get_position(bs), tot); + } + gf_bs_skip_bytes(bs, au_size); + } + gf_bs_del(bs); + fclose(saf); + if (import->flags & GF_IMPORT_PROBE_ONLY) return GF_OK; + + gf_set_progress("Importing SAF", tot, tot); + MP4T_RecomputeBitRate(import->dest, track); + return GF_OK; +} + + +typedef struct +{ + GF_MediaImporter *import; + u32 track; + u32 nb_i, nb_p, nb_b; + GF_AVCConfig *avccfg; + AVCState avc; + Bool force_next_au_start; + Bool stream_setup; +} GF_TSImport; + + +/* Determine the ESD corresponding to the current track info based on the PID and sets the additional info + in the track info as described in this esd */ +static void m2ts_set_track_mpeg4_probe_info(GF_ESD *esd, + struct __track_import_info* tk_info) +{ + if (esd && tk_info) { + switch (esd->decoderConfig->streamType) { + case GF_STREAM_SCENE: + tk_info->type = GF_ISOM_MEDIA_SCENE; + break; + case GF_STREAM_VISUAL: + tk_info->type = GF_ISOM_MEDIA_VISUAL; + break; + case GF_STREAM_AUDIO: + tk_info->type = GF_ISOM_MEDIA_AUDIO; + break; + case GF_STREAM_MPEG7: + tk_info->type = GF_ISOM_MEDIA_MPEG7; + break; + case GF_STREAM_IPMP: + tk_info->type = GF_ISOM_MEDIA_IPMP; + break; + case GF_STREAM_OCI: + tk_info->type = GF_ISOM_MEDIA_OCI; + break; + case GF_STREAM_MPEGJ: + tk_info->type = GF_ISOM_MEDIA_MPEGJ; + break; + case GF_STREAM_OD: + tk_info->type = GF_ISOM_MEDIA_OD; + break; + case GF_STREAM_INTERACT: + tk_info->type = GF_ISOM_MEDIA_SCENE; + break; + default: + tk_info->type = GF_ISOM_MEDIA_ESM; + break; + } + } +} + +static void m2ts_set_tracks_mpeg4_probe_info(GF_MediaImporter *import, GF_M2TS_Program *prog, GF_List *ESDescriptors) +{ + u32 i, k, esd_count, stream_count; + s32 tk_idx; + + esd_count = gf_list_count(ESDescriptors); + stream_count = gf_list_count(prog->streams); + for (k = 0; k < esd_count; k++) { + GF_M2TS_ES *es = NULL; + GF_ESD *esd = (GF_ESD *)gf_list_get(ESDescriptors, k); + + for (i = 0; i < stream_count; i++) { + GF_M2TS_ES *es_tmp = (GF_M2TS_ES *)gf_list_get(prog->streams, i); + if (es_tmp->mpeg4_es_id == esd->ESID) { + es = es_tmp; + break; + } + } + if (es == NULL) continue; + + tk_idx = -1; + for (i = 0; i < import->nb_tracks; i++) { + if (import->tk_info[i].track_num == es->pid) { + tk_idx = i; + break; + } + } + + if (tk_idx == -1) continue; + if (import->tk_info[tk_idx].type != 0 && import->tk_info[tk_idx].type != GF_ISOM_MEDIA_ESM) continue; + + m2ts_set_track_mpeg4_probe_info(esd, &import->tk_info[tk_idx]); + } + +} + +static void m2ts_set_track_mpeg4_creation_info(GF_MediaImporter *import, u32 *mtype, u32 *stype, u32 *oti) +{ + if (import->esd) { + *stype = import->esd->decoderConfig->streamType; + *oti = import->esd->decoderConfig->objectTypeIndication; + switch (*stype) { + case GF_STREAM_SCENE: + *mtype = GF_ISOM_MEDIA_SCENE; + break; + case GF_STREAM_OD: + *mtype = GF_ISOM_MEDIA_OD; + break; + case GF_STREAM_INTERACT: + *mtype = GF_ISOM_MEDIA_SCENE; + break; + case GF_STREAM_VISUAL: + *mtype = GF_ISOM_MEDIA_VISUAL; + break; + case GF_STREAM_AUDIO: + *mtype = GF_ISOM_MEDIA_AUDIO; + break; + default: + *mtype = GF_ISOM_MEDIA_ESM; + break; + } + } + if (*mtype == 0) { + *mtype = GF_ISOM_MEDIA_ESM; + *oti = 0; + *stype = 0; + } + +} + +static void m2ts_create_track(GF_TSImport *tsimp, u32 mtype, u32 stype, u32 oti, Bool is_in_iod) +{ + GF_MediaImporter *import= (GF_MediaImporter *)tsimp->import; + if (mtype != GF_ISOM_MEDIA_ESM) { + u32 di; + Bool destroy_esd = 0; + tsimp->track = gf_isom_new_track(import->dest, (import->esd?import->esd->ESID:import->trackID), mtype, 90000); + if (!tsimp->track) { + tsimp->track = gf_isom_new_track(import->dest, 0, mtype, 90000); + if (!tsimp->track) { + //error + } + } + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(2); + destroy_esd = 1; + } + /*update stream type/oti*/ + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->decoderConfig->streamType = stype; + import->esd->decoderConfig->objectTypeIndication = oti; + import->esd->slConfig->timestampResolution = 90000; + + gf_isom_set_track_enabled(import->dest, tsimp->track, 1); + + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, tsimp->track); + gf_isom_new_mpeg4_description(import->dest, tsimp->track, import->esd, NULL, NULL, &di); + if (destroy_esd) { + gf_odf_desc_del((GF_Descriptor *)import->esd); + import->esd = NULL; + } + + if (is_in_iod) gf_isom_add_track_to_root_od(import->dest, tsimp->track); + + import->final_trackID = gf_isom_get_track_id(import->dest, tsimp->track); + } +} + +static GF_ESD *m2ts_get_esd(GF_M2TS_ES *es) +{ + GF_ESD *esd; + u32 k, esd_count; + + esd = NULL; + if (es->program->pmt_iod && es->program->pmt_iod->ESDescriptors) { + esd_count = gf_list_count(es->program->pmt_iod->ESDescriptors); + for (k = 0; k < esd_count; k++) { + GF_ESD *esd_tmp = (GF_ESD *)gf_list_get(es->program->pmt_iod->ESDescriptors, k); + if (esd_tmp->ESID != es->mpeg4_es_id) continue; + esd = esd_tmp; + break; + } + } + + if (!esd && es->program->additional_ods) { + u32 od_count, od_index; + od_count = gf_list_count(es->program->additional_ods); + for (od_index = 0; od_index < od_count; od_index++) { + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *)gf_list_get(es->program->additional_ods, od_index); + esd_count = gf_list_count(od->ESDescriptors); + for (k = 0; k < esd_count; k++) { + GF_ESD *esd_tmp = (GF_ESD *)gf_list_get(od->ESDescriptors, k); + if (esd_tmp->ESID != es->mpeg4_es_id) continue; + esd = esd_tmp; + break; + } + } + } + + return esd; +} + +void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) +{ + GF_Err e; + GF_ISOSample *samp; + Bool is_au_start; + u32 i, count, idx; + GF_M2TS_Program *prog; + GF_TSImport *tsimp = (GF_TSImport *) ts->user; + GF_MediaImporter *import= (GF_MediaImporter *)tsimp->import; + GF_M2TS_ES *es = NULL; + GF_M2TS_PES *pes = NULL; + GF_M2TS_SECTION_ES *ses = NULL; + + switch (evt_type) { + case GF_M2TS_EVT_PAT_FOUND: + break; +// case GF_M2TS_EVT_PAT_REPEAT: +// case GF_M2TS_EVT_SDT_REPEAT: + case GF_M2TS_EVT_PMT_REPEAT: + /*abort upon first PMT repeat if not using 4on2. Otherwise we must parse the entire + bitstream to locate ODs sent in OD updates in order to get their stream types...*/ + if (!ts->has_4on2 && (import->flags & GF_IMPORT_PROBE_ONLY) && !import->trackID) + import->flags |= GF_IMPORT_DO_ABORT; + break; + case GF_M2TS_EVT_SDT_FOUND: + import->nb_progs = gf_list_count(ts->SDTs); + for (i=0; inb_progs; i++) { + GF_M2TS_SDT *sdt = (GF_M2TS_SDT *)gf_list_get(ts->SDTs, i); + strcpy(import->pg_info[i].name, sdt->service); + import->pg_info[i].number = sdt->service_id; + } + if (!ts->has_4on2 && import->flags & GF_IMPORT_PROBE_ONLY) + import->flags |= GF_IMPORT_DO_ABORT; + break; + case GF_M2TS_EVT_PMT_FOUND: + prog = (GF_M2TS_Program*)par; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + /* + we scan all the streams declared in this PMT to fill the tk_info structures + NOTE: in the T-DMB case, we also need to decode ObjectDescriptor Updates see "case GF_M2TS_EVT_SL_PCK" + */ + count = gf_list_count(prog->streams); + for (i=0; istreams, i); + if (es->pid == prog->pmt_pid) continue; + if (es->flags & GF_M2TS_ES_IS_SECTION) { + ses = (GF_M2TS_SECTION_ES *)es; + } else { + pes = (GF_M2TS_PES *)es; + } + idx = import->nb_tracks; + import->tk_info[idx].track_num = es->pid; + import->tk_info[idx].prog_num = prog->number; + + switch (es->stream_type) { + case GF_M2TS_VIDEO_MPEG1: + import->tk_info[idx].media_type = GF_4CC('M','P','G','1'); + import->tk_info[idx].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[idx].lang = pes->lang; + import->nb_tracks++; + break; + case GF_M2TS_VIDEO_MPEG2: + import->tk_info[idx].media_type = GF_4CC('M','P','G','2'); + import->tk_info[idx].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[idx].lang = pes->lang; + import->nb_tracks++; + break; + case GF_M2TS_VIDEO_MPEG4: + import->tk_info[idx].media_type = GF_4CC('M','P','4','V'); + import->tk_info[idx].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[idx].lang = pes->lang; + import->nb_tracks++; + break; + case GF_M2TS_VIDEO_H264: + import->tk_info[idx].media_type = GF_4CC('H','2','6','4'); + import->tk_info[idx].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[idx].lang = pes->lang; + import->nb_tracks++; + break; + case GF_M2TS_AUDIO_MPEG1: + import->tk_info[idx].media_type = GF_4CC('M','P','G','1'); + import->tk_info[idx].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[idx].lang = pes->lang; + import->nb_tracks++; + break; + case GF_M2TS_AUDIO_MPEG2: + import->tk_info[idx].media_type = GF_4CC('M','P','G','2'); + import->tk_info[idx].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[idx].lang = pes->lang; + import->nb_tracks++; + break; + case GF_M2TS_AUDIO_AAC: + case GF_M2TS_AUDIO_LATM_AAC: + import->tk_info[idx].media_type = GF_4CC('M','P','4','A'); + import->tk_info[idx].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[idx].lang = pes->lang; + import->nb_tracks++; + break; + case GF_M2TS_SYSTEMS_MPEG4_PES: + case GF_M2TS_SYSTEMS_MPEG4_SECTIONS: + if (es->stream_type == GF_M2TS_SYSTEMS_MPEG4_PES) { + import->tk_info[idx].media_type = GF_4CC('M','4','S','P'); + } else { + import->tk_info[idx].media_type = GF_4CC('M','4','S','S'); + } + if (prog->pmt_iod) { + GF_ESD *esd = m2ts_get_esd(es); + m2ts_set_track_mpeg4_probe_info(esd, &import->tk_info[idx]); + if (esd && esd->decoderConfig->streamType == GF_STREAM_OD) { + es->flags |= GF_M2TS_ES_IS_MPEG4_OD; + } + } else { + import->tk_info[idx].type = GF_ISOM_MEDIA_ESM; + } + import->nb_tracks++; + break; + } + } + } else { + /* We are not in PROBE mode, we are importing only one stream and don't care about the other streams */ + u32 mtype, stype, oti; + Bool is_in_iod, found; + + /* Since the GF_M2TS_ES_IS_MPEG4_OD flag is stored at the ES level and ES are reset after probe, + we need to set it again as in probe mode */ + found = 0; + count = gf_list_count(prog->streams); + for (i=0; istreams, i); + if (es->pid == prog->pmt_pid) continue; + if (es->pid == import->trackID) found = 1; + if (es->flags & GF_M2TS_ES_IS_SECTION) { + ses = (GF_M2TS_SECTION_ES *)es; + } else { + pes = (GF_M2TS_PES *)es; + } + esd = m2ts_get_esd(es); + if (esd && esd->decoderConfig->streamType == GF_STREAM_OD) { + es->flags |= GF_M2TS_ES_IS_MPEG4_OD; + } + } + /*this PMT is not the one of our stream*/ + if (!found) return; + + es = ts->ess[import->trackID]; /* import->trackID == pid */ + if (!es) break; + + if (es->flags & GF_M2TS_ES_IS_SECTION) { + ses = (GF_M2TS_SECTION_ES *)es; + } else { + pes = (GF_M2TS_PES *)es; + } + + mtype = stype = oti = 0; + is_in_iod = 0; + + switch (es->stream_type) { + case GF_M2TS_VIDEO_MPEG1: + mtype = GF_ISOM_MEDIA_VISUAL; + stype = GF_STREAM_VISUAL; oti = 0x6A; + break; + case GF_M2TS_VIDEO_MPEG2: + mtype = GF_ISOM_MEDIA_VISUAL; + stype = GF_STREAM_VISUAL; oti = 0x65; + break; + case GF_M2TS_VIDEO_MPEG4: + mtype = GF_ISOM_MEDIA_VISUAL; + stype = GF_STREAM_VISUAL; oti = 0x20; + break; + case GF_M2TS_VIDEO_H264: + mtype = GF_ISOM_MEDIA_VISUAL; + stype = GF_STREAM_VISUAL; oti = 0x21; + tsimp->avccfg = gf_odf_avc_cfg_new(); + break; + case GF_M2TS_AUDIO_MPEG1: + mtype = GF_ISOM_MEDIA_AUDIO; + stype = GF_STREAM_AUDIO; oti = 0x6B; + break; + case GF_M2TS_AUDIO_MPEG2: + mtype = GF_ISOM_MEDIA_AUDIO; + stype = GF_STREAM_AUDIO; oti = 0x69; + break; + case GF_M2TS_AUDIO_LATM_AAC: + case GF_M2TS_AUDIO_AAC: + mtype = GF_ISOM_MEDIA_AUDIO; + stype = GF_STREAM_AUDIO; oti = 0x40; + break; + case GF_M2TS_SYSTEMS_MPEG4_PES: + case GF_M2TS_SYSTEMS_MPEG4_SECTIONS: + if (prog->pmt_iod && !import->esd) { + import->esd = m2ts_get_esd(es); + m2ts_set_track_mpeg4_creation_info(import, &mtype, &stype, &oti); + is_in_iod = 1; + } + break; + } + m2ts_create_track(tsimp, mtype, stype, oti, is_in_iod); + } + break; + case GF_M2TS_EVT_AAC_CFG: + if (!(import->flags & GF_IMPORT_PROBE_ONLY) && !tsimp->stream_setup) { + GF_ESD *esd = gf_isom_get_esd(import->dest, tsimp->track, 1); + if (esd) { + if (!esd->decoderConfig->decoderSpecificInfo) esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + if (esd->decoderConfig->decoderSpecificInfo->data) free(esd->decoderConfig->decoderSpecificInfo->data); + esd->decoderConfig->decoderSpecificInfo->data = ((GF_M2TS_PES_PCK*)par)->data; + esd->decoderConfig->decoderSpecificInfo->dataLength = ((GF_M2TS_PES_PCK*)par)->data_len; + gf_isom_change_mpeg4_description(import->dest, tsimp->track, 1, esd); + esd->decoderConfig->decoderSpecificInfo->data = NULL; + gf_odf_desc_del((GF_Descriptor *)esd); + tsimp->stream_setup = 1; + } + } + break; + case GF_M2TS_EVT_PES_PCK: + { + GF_M2TS_PES_PCK *pck = (GF_M2TS_PES_PCK *)par; + is_au_start = (pck->flags & GF_M2TS_PES_PCK_AU_START); + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + for (i=0; inb_tracks; i++) { + if (import->tk_info[i].track_num == pck->stream->pid) { + if (pck->stream->aud_sr) { + import->tk_info[i].audio_info.sample_rate = pck->stream->aud_sr; + import->tk_info[i].audio_info.nb_channels = pck->stream->aud_nb_ch; + } else { + import->tk_info[i].video_info.width = pck->stream->vid_w; + import->tk_info[i].video_info.height = pck->stream->vid_h; + } + break; + } + } + if (!ts->has_4on2 && (import->trackID==pck->stream->pid) && (pck->stream->vid_h || pck->stream->aud_sr) ) + import->flags |= GF_IMPORT_DO_ABORT; + return; + } + + /* Even if we don't import this stream we need to check the first dts of the program */ + if (!(pck->stream->flags & GF_M2TS_ES_FIRST_DTS) && is_au_start) { + pck->stream->flags |= GF_M2TS_ES_FIRST_DTS; + pck->stream->first_dts = (pck->DTS?pck->DTS:pck->PTS); + if (!pck->stream->program->first_dts || + pck->stream->program->first_dts > pck->stream->first_dts) { + pck->stream->program->first_dts = pck->stream->first_dts; + } + } + + if (pck->stream->pid != import->trackID) return; + + if (tsimp->avccfg) { + GF_AVCConfigSlot *slc; + GF_BitStream *bs; + s32 idx; + u32 nal_type = pck->data[4] & 0x1F; + + switch (nal_type) { + case GF_AVC_NALU_SEQ_PARAM: + bs = gf_bs_new(pck->data+5, pck->data_len-5, GF_BITSTREAM_READ); + idx = AVC_ReadSeqInfo(bs, &tsimp->avc, NULL); + gf_bs_del(bs); + if ((idx>=0) && (tsimp->avc.sps[idx].status==1)) { + tsimp->avc.sps[idx].status = 2; + /*always store nalu size on 4 bytes*/ + tsimp->avccfg->nal_unit_size = 4; + tsimp->avccfg->configurationVersion = 1; + tsimp->avccfg->profile_compatibility = tsimp->avc.sps[idx].prof_compat; + tsimp->avccfg->AVCProfileIndication = tsimp->avc.sps[idx].profile_idc; + tsimp->avccfg->AVCLevelIndication = tsimp->avc.sps[idx].level_idc; + slc = (GF_AVCConfigSlot*)malloc(sizeof(GF_AVCConfigSlot)); + slc->size = pck->data_len-4; + slc->data = (char*)malloc(sizeof(char)*slc->size); + memcpy(slc->data, pck->data+4, sizeof(char)*slc->size); + gf_list_add(tsimp->avccfg->sequenceParameterSets, slc); + + if (pck->stream->vid_w < tsimp->avc.sps[idx].width) + pck->stream->vid_w = tsimp->avc.sps[idx].width; + if (pck->stream->vid_h < tsimp->avc.sps[idx].height) + pck->stream->vid_h = tsimp->avc.sps[idx].height; + } + return; + case GF_AVC_NALU_PIC_PARAM: + bs = gf_bs_new(pck->data+5, pck->data_len-5, GF_BITSTREAM_READ); + idx = AVC_ReadPictParamSet(bs, &tsimp->avc); + gf_bs_del(bs); + if ((idx>=0) && (tsimp->avc.pps[idx].status==1)) { + tsimp->avc.pps[idx].status = 2; + slc = (GF_AVCConfigSlot*)malloc(sizeof(GF_AVCConfigSlot)); + slc->size = pck->data_len-4; + slc->data = (char*)malloc(sizeof(char)*slc->size); + memcpy(slc->data, pck->data+4, sizeof(char)*slc->size); + gf_list_add(tsimp->avccfg->pictureParameterSets, slc); + } + return; + /*remove*/ + case GF_AVC_NALU_ACCESS_UNIT: + tsimp->force_next_au_start = 1; + return; + case GF_AVC_NALU_FILLER_DATA: + case GF_AVC_NALU_END_OF_SEQ: + case GF_AVC_NALU_END_OF_STREAM: + return; + case GF_AVC_NALU_SEI: + idx = AVC_ReformatSEI_NALU(pck->data+4, pck->data_len-4, &tsimp->avc); + if (idx>0) pck->data_len = idx+4; + break; + } + + /*rewrite nalu header*/ + bs = gf_bs_new(pck->data, pck->data_len, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, pck->data_len - 4); + gf_bs_del(bs); + + if (tsimp->force_next_au_start) { + is_au_start = 1; + tsimp->force_next_au_start = 0; + } + } + if (!is_au_start) { + e = gf_isom_append_sample_data(import->dest, tsimp->track, (char*)pck->data, pck->data_len); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] Error appending sample data\n")); + } + if (pck->flags & GF_M2TS_PES_PCK_I_FRAME) tsimp->nb_i++; + if (pck->flags & GF_M2TS_PES_PCK_P_FRAME) tsimp->nb_p++; + if (pck->flags & GF_M2TS_PES_PCK_B_FRAME) tsimp->nb_b++; + return; + } + + samp = gf_isom_sample_new(); + samp->DTS = pck->DTS ? pck->DTS : pck->PTS; + samp->CTS_Offset = (u32) (pck->PTS - samp->DTS); + + if (pck->stream->first_dts == samp->DTS) { + + switch (pck->stream->stream_type) { + case GF_M2TS_VIDEO_MPEG1: gf_import_message(import, GF_OK, "MPEG-1 Video import (TS PID %d)", pck->stream->pid); break; + case GF_M2TS_VIDEO_MPEG2: gf_import_message(import, GF_OK, "MPEG-2 Video import (TS PID %d)", pck->stream->pid); break; + case GF_M2TS_VIDEO_MPEG4: gf_import_message(import, GF_OK, "MPEG-4 Video import (TS PID %d)", pck->stream->pid); break; + case GF_M2TS_VIDEO_H264: gf_import_message(import, GF_OK, "MPEG-4 AVC/H264 Video import (TS PID %d)", pck->stream->pid); break; + case GF_M2TS_AUDIO_MPEG1: gf_import_message(import, GF_OK, "MPEG-1 Audio import - SampleRate %d Channels %d Language %s (TS PID %d)", pck->stream->aud_sr, pck->stream->aud_nb_ch, gf_4cc_to_str(pck->stream->lang), pck->stream->pid); break; + case GF_M2TS_AUDIO_MPEG2: gf_import_message(import, GF_OK, "MPEG-2 Audio import - SampleRate %d Channels %d Language %s (TS PID %d)", pck->stream->aud_sr, pck->stream->aud_nb_ch, gf_4cc_to_str(pck->stream->lang), pck->stream->pid); break; + case GF_M2TS_AUDIO_AAC: gf_import_message(import, GF_OK, "MPEG-4 AAC Audio import - SampleRate %d Channels %d Language %s (TS PID %d)", pck->stream->aud_sr, pck->stream->aud_nb_ch, gf_4cc_to_str(pck->stream->lang), pck->stream->pid); break; + } + + gf_isom_set_media_language(import->dest, tsimp->track, (char *) gf_4cc_to_str(pck->stream->lang)+1); + } + if (!tsimp->stream_setup) { + if (pck->stream->aud_sr) { + gf_isom_set_audio_info(import->dest, tsimp->track, 1, pck->stream->aud_sr, pck->stream->aud_nb_ch, 16); + tsimp->stream_setup = 1; + } + else if (pck->stream->vid_w) { + u32 w = pck->stream->vid_w; + if (pck->stream->vid_par) w = w * (pck->stream->vid_par>>16) / (pck->stream->vid_par&0xffff); + gf_isom_set_visual_info(import->dest, tsimp->track, 1, pck->stream->vid_w, pck->stream->vid_h); + gf_isom_set_track_layout_info(import->dest, tsimp->track, w<<16, pck->stream->vid_h<<16, 0, 0, 0); + if (w != pck->stream->vid_w) + e = gf_isom_set_pixel_aspect_ratio(import->dest, tsimp->track, 1, pck->stream->vid_par>>16, pck->stream->vid_par&0xff); + + tsimp->stream_setup = 1; + } + } + + if (samp->DTS >= pck->stream->first_dts) { + samp->DTS -= pck->stream->first_dts; + samp->IsRAP = (pck->flags & GF_M2TS_PES_PCK_RAP) ? 1 : 0; + samp->data = pck->data; + samp->dataLength = pck->data_len; + e = gf_isom_add_sample(import->dest, tsimp->track, 1, samp); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] Error adding sample: %s\n", gf_error_to_string(e))); + import->flags |= GF_IMPORT_DO_ABORT; + import->last_error = e; + } + if (import->duration && (import->duration<=(samp->DTS+samp->CTS_Offset)/90)) + import->flags |= GF_IMPORT_DO_ABORT; + + if (pck->flags & GF_M2TS_PES_PCK_I_FRAME) tsimp->nb_i++; + if (pck->flags & GF_M2TS_PES_PCK_P_FRAME) tsimp->nb_p++; + if (pck->flags & GF_M2TS_PES_PCK_B_FRAME) tsimp->nb_b++; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] negative time sample - skipping\n")); + } + samp->data = NULL; + gf_isom_sample_del(&samp); + } + break; + case GF_M2TS_EVT_SL_PCK: + { + GF_M2TS_SL_PCK *sl_pck = (GF_M2TS_SL_PCK *)par; + + /* if there is no IOD for this program we cannot handle SL packets */ + if (!sl_pck->stream->program->pmt_iod) return; + + if (sl_pck->stream->flags & GF_M2TS_ES_IS_SECTION) { + ses = (GF_M2TS_SECTION_ES *)sl_pck->stream; + } else { + pes = (GF_M2TS_PES *)sl_pck->stream; + } + + if (sl_pck->stream->flags & GF_M2TS_ES_IS_MPEG4_OD) { + /* We need to handle OD streams even if this is not the stream we are importing */ + GF_ESD *esd = m2ts_get_esd(sl_pck->stream); + if (esd) { + GF_SLHeader hdr; + u32 hdr_len; + GF_ODCodec *od_codec = gf_odf_codec_new(); + GF_ODCom *com; + GF_ODUpdate* odU; + u32 com_count, com_index, od_count, od_index; + + gf_sl_depacketize(esd->slConfig, &hdr, sl_pck->data, sl_pck->data_len, &hdr_len); + gf_odf_codec_set_au(od_codec, sl_pck->data+hdr_len, sl_pck->data_len - hdr_len); + gf_odf_codec_decode(od_codec); + com_count = gf_list_count(od_codec->CommandList); + for (com_index = 0; com_index < com_count; com_index++) { + com = (GF_ODCom *)gf_list_get(od_codec->CommandList, com_index); + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + odU = (GF_ODUpdate*)com; + od_count = gf_list_count(odU->objectDescriptors); + for (od_index=0; od_indexobjectDescriptors, od_index); + gf_list_add(sl_pck->stream->program->additional_ods, od); + if (import->flags & GF_IMPORT_PROBE_ONLY) { + /* We need to set the remaining unset track info for the streams declared in this OD */ + m2ts_set_tracks_mpeg4_probe_info(import, sl_pck->stream->program, od->ESDescriptors); + } + } + gf_list_reset(odU->objectDescriptors); + } + } + gf_odf_codec_del(od_codec); + } + + } + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + if (pes) { + for (i=0; inb_tracks; i++) { + if (import->tk_info[i].track_num == sl_pck->stream->pid) { + if (pes->aud_sr) { + import->tk_info[i].audio_info.sample_rate = pes->aud_sr; + import->tk_info[i].audio_info.nb_channels = pes->aud_nb_ch; + } else { + import->tk_info[i].video_info.width = pes->vid_w; + import->tk_info[i].video_info.height = pes->vid_h; + } + break; + } + } +// if (pes->vid_h || pes->aud_sr) import->flags |= GF_IMPORT_DO_ABORT; + } + return; + } + + if (sl_pck->stream->pid != import->trackID) return; + + /* we create a track for the stream to import only if it was not created */ + if (!gf_isom_get_track_by_id(import->dest, (import->esd?import->esd->ESID:import->trackID))) { + u32 mtype, stype, oti; + mtype = stype = oti = 0; + import->esd = m2ts_get_esd(sl_pck->stream); + m2ts_set_track_mpeg4_creation_info(import, &mtype, &stype, &oti); + m2ts_create_track(tsimp, mtype, stype, oti, 0); + } + + if (import->esd) { + GF_SLHeader hdr; + u32 hdr_len; + gf_sl_depacketize(import->esd->slConfig, &hdr, sl_pck->data, sl_pck->data_len, &hdr_len); + + if (!hdr.accessUnitStartFlag) { + e = gf_isom_append_sample_data(import->dest, tsimp->track, sl_pck->data + hdr_len, sl_pck->data_len - hdr_len); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] Error appending sample data\n")); + } + } else { + if (!(sl_pck->stream->flags & GF_M2TS_ES_FIRST_DTS)) { + sl_pck->stream->flags |= GF_M2TS_ES_FIRST_DTS; + sl_pck->stream->first_dts = (hdr.decodingTimeStamp?hdr.decodingTimeStamp:hdr.compositionTimeStamp); + if (!sl_pck->stream->program->first_dts || + sl_pck->stream->program->first_dts > sl_pck->stream->first_dts) { + sl_pck->stream->program->first_dts = sl_pck->stream->first_dts; + } + } + + samp = gf_isom_sample_new(); + samp->DTS = (hdr.decodingTimeStamp?hdr.decodingTimeStamp:hdr.compositionTimeStamp); + samp->CTS_Offset = (u32) (hdr.compositionTimeStamp - samp->DTS); + if (samp->DTS >= sl_pck->stream->first_dts) { + samp->DTS -= sl_pck->stream->first_dts; + samp->IsRAP = import->esd->slConfig->useRandomAccessPointFlag ? hdr.randomAccessPointFlag: 1; + + samp->data = sl_pck->data + hdr_len; + samp->dataLength = sl_pck->data_len - hdr_len; + + e = gf_isom_add_sample(import->dest, tsimp->track, 1, samp); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] Error adding sample\n")); + } + if (import->duration && (import->duration<=(samp->DTS+samp->CTS_Offset)/90)) + import->flags |= GF_IMPORT_DO_ABORT; + + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] negative time sample - skipping\n")); + sl_pck->stream->first_dts = samp->DTS; + if (!sl_pck->stream->program->first_dts || + sl_pck->stream->program->first_dts > sl_pck->stream->first_dts) { + sl_pck->stream->program->first_dts = sl_pck->stream->first_dts; + } + } + samp->data = NULL; + gf_isom_sample_del(&samp); + } + } + } + break; + default: + return; + } +} + +/* Warning: we start importing only after finding the PMT */ +GF_Err gf_import_mpeg_ts(GF_MediaImporter *import) +{ + GF_M2TS_Demuxer *ts; + GF_M2TS_ES *es; + char data[188]; + GF_TSImport tsimp; + u64 fsize, done; + u32 size; + FILE *mts; + char progress[1000]; + + if (import->trackID > GF_M2TS_MAX_STREAMS) + return gf_import_message(import, GF_BAD_PARAM, "Invalid PID %d", import->trackID ); + + mts = gf_f64_open(import->in_name, "rb"); + if (!mts) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + + gf_f64_seek(mts, 0, SEEK_END); + fsize = gf_f64_tell(mts); + gf_f64_seek(mts, 0, SEEK_SET); + done = 0; + + memset(&tsimp, 0, sizeof(GF_TSImport)); + tsimp.import = import; + ts = gf_m2ts_demux_new(); + ts->on_event = on_m2ts_import_data; + ts->user = &tsimp; + + sprintf(progress, "Importing MPEG-2 TS (PID %d)", import->trackID); + while (!feof(mts)) { + size = fread(data, 1, 188, mts); + if (size<188) break; + + gf_m2ts_process_data(ts, data, size); + if (import->flags & GF_IMPORT_DO_ABORT) break; + done += size; + gf_set_progress(progress, (u32) (done/1024), (u32) (fsize/1024)); + } + if (import->last_error) { + GF_Err e = import->last_error; + import->last_error = GF_OK; + if (tsimp.avccfg) gf_odf_avc_cfg_del(tsimp.avccfg); + gf_m2ts_demux_del(ts); + fclose(mts); + return e; + } + import->esd = NULL; + gf_set_progress(progress, (u32) (fsize/1024), (u32) (fsize/1024)); + + if (!(import->flags & GF_IMPORT_PROBE_ONLY)) { + es = (GF_M2TS_ES *)ts->ess[import->trackID]; + if (!es) { + gf_m2ts_demux_del(ts); + fclose(mts); + return gf_import_message(import, GF_BAD_PARAM, "Unknown PID %d", import->trackID); + } + + if (tsimp.avccfg) { + u32 w = ((GF_M2TS_PES*)es)->vid_w; + u32 h = ((GF_M2TS_PES*)es)->vid_h; + gf_isom_avc_config_update(import->dest, tsimp.track, 1, tsimp.avccfg); + gf_isom_set_visual_info(import->dest, tsimp.track, 1, w, h); + gf_isom_set_track_layout_info(import->dest, tsimp.track, w<<16, h<<16, 0, 0, 0); + gf_odf_avc_cfg_del(tsimp.avccfg); + } + + MP4T_RecomputeBitRate(import->dest, tsimp.track); + /* creation of the edit lists */ + if (es->first_dts != es->program->first_dts) { + u32 media_ts, moov_ts, offset; + u64 dur; + media_ts = gf_isom_get_media_timescale(import->dest, tsimp.track); + moov_ts = gf_isom_get_timescale(import->dest); + assert(es->program->first_dts < es->first_dts); + offset = (u32)(es->first_dts - es->program->first_dts) * moov_ts / media_ts; + dur = gf_isom_get_media_duration(import->dest, tsimp.track) * moov_ts / media_ts; + gf_isom_set_edit_segment(import->dest, tsimp.track, 0, offset, 0, GF_ISOM_EDIT_EMPTY); + gf_isom_set_edit_segment(import->dest, tsimp.track, offset, dur, 0, GF_ISOM_EDIT_NORMAL); + //gf_import_message(import, GF_OK, "Timeline offset: %d ms", (u32)((pes->first_dts - pes->program->first_dts)/90)); + } + + if (tsimp.nb_p) { + gf_import_message(import, GF_OK, "Import results: %d VOPs (%d Is - %d Ps - %d Bs)", gf_isom_get_sample_count(import->dest, tsimp.track), tsimp.nb_i, tsimp.nb_p, tsimp.nb_b); + } + + if (es->program->pmt_iod) + gf_isom_set_brand_info(import->dest, GF_ISOM_BRAND_MP42, 1); + } + + gf_m2ts_demux_del(ts); + fclose(mts); + return GF_OK; +} + +GF_Err gf_import_vobsub(GF_MediaImporter *import) +{ + static u8 null_subpic[] = { 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0xFF }; + char filename[GF_MAX_PATH]; + FILE *file = NULL; + int version; + vobsub_file *vobsub = NULL; + u32 c, trackID, track, di; + Bool destroy_esd = 0; + GF_Err err = GF_OK; + GF_ISOSample *samp = NULL; + GF_List *subpic; + u32 total, last_samp_dur = 0; + unsigned char buf[0x800]; + + strcpy(filename, import->in_name); + vobsub_trim_ext(filename); + strcat(filename, ".idx"); + + file = gf_f64_open(filename, "r"); + if (!file) { + err = gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", filename); + goto error; + } + + GF_SAFEALLOC(vobsub, vobsub_file); + if (!vobsub) { + err = gf_import_message(import, GF_OUT_OF_MEM, "Memory allocation failed"); + goto error; + } + + err = vobsub_read_idx(file, vobsub, &version); + fclose(file); + + if (err != GF_OK) { + err = gf_import_message(import, err, "Reading VobSub file %s failed", filename); + goto error; + } else if (version < 6) { + err = gf_import_message(import, err, "Unsupported VobSub version", filename); + goto error; + } + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->nb_tracks = 0; + for (c = 0; c < 32; c++) { + if (vobsub->langs[c].id != 0) { + import->tk_info[import->nb_tracks].track_num = c + 1; + import->tk_info[import->nb_tracks].type = GF_ISOM_MEDIA_SUBPIC; + import->tk_info[import->nb_tracks].flags = 0; + import->nb_tracks++; + } + } + vobsub_free(vobsub); + return GF_OK; + } + + strcpy(filename, import->in_name); + vobsub_trim_ext(filename); + strcat(filename, ".sub"); + + file = gf_f64_open(filename, "rb"); + if (!file) { + err = gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", filename); + goto error; + } + + trackID = import->trackID; + if (!trackID) { + trackID = 0-1U; + if (vobsub->num_langs != 1) { + err = gf_import_message(import, GF_BAD_PARAM, "Several tracks in VobSub - please indicate track to import"); + goto error; + } + for (c = 0; c < 32; c++) { + if (vobsub->langs[c].id != 0) { + trackID = c; + break; + } + } + if (trackID == 0-1U) { + err = gf_import_message(import, GF_URL_ERROR, "Cannot find track ID %d in file", trackID); + goto error; + } + } + trackID--; + + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(2); + destroy_esd = 1; + } + if (!import->esd->decoderConfig) { + import->esd->decoderConfig = (GF_DecoderConfig*)gf_odf_desc_new(GF_ODF_DCD_TAG); + } + if (!import->esd->slConfig) { + import->esd->slConfig = (GF_SLConfig*)gf_odf_desc_new(GF_ODF_SLC_TAG); + } + if (!import->esd->decoderConfig->decoderSpecificInfo) { + import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor*)gf_odf_desc_new(GF_ODF_DSI_TAG); + } + + import->esd->decoderConfig->streamType = GF_STREAM_ND_SUBPIC; + import->esd->decoderConfig->objectTypeIndication = 0xe0; + + import->esd->decoderConfig->decoderSpecificInfo->dataLength = sizeof(vobsub->palette); + import->esd->decoderConfig->decoderSpecificInfo->data = (char*)&vobsub->palette[0][0]; + + gf_import_message(import, GF_OK, "VobSub import - subpicture stream '%s'", vobsub->langs[trackID].name); + + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_SUBPIC, 90000); + if (!track) { + err = gf_isom_last_error(import->dest); + err = gf_import_message(import, err, "Could not create new track"); + goto error; + } + + gf_isom_set_track_enabled(import->dest, track, 1); + + if (!import->esd->ESID) { + import->esd->ESID = gf_isom_get_track_id(import->dest, track); + } + import->final_trackID = import->esd->ESID; + + gf_isom_new_mpeg4_description(import->dest, track, import->esd, NULL, NULL, &di); + gf_isom_set_track_layout_info(import->dest, track, vobsub->width << 16, vobsub->height << 16, 0, 0, 0); + gf_isom_set_media_language(import->dest, track, vobsub->langs[trackID].name); + + samp = gf_isom_sample_new(); + samp->IsRAP = 1; + samp->dataLength = sizeof(null_subpic); + samp->data = (char*)null_subpic; + + err = gf_isom_add_sample(import->dest, track, di, samp); + if (err) goto error; + + subpic = vobsub->langs[trackID].subpos; + total = gf_list_count(subpic); + + for (c = 0; c < total; c++) + { + u32 i, left, size, psize, dsize, hsize, duration; + char *packet; + vobsub_pos *pos = (vobsub_pos*)gf_list_get(subpic, c); + + if (import->duration && pos->start > import->duration) { + break; + } + + gf_f64_seek(file, pos->filepos, SEEK_SET); + if (gf_f64_tell(file) != pos->filepos) { + err = gf_import_message(import, GF_IO_ERR, "Could not seek in file"); + goto error; + } + + if (fread(buf, 1, sizeof(buf), file) != sizeof(buf)) { + err = gf_import_message(import, GF_IO_ERR, "Could not read from file"); + goto error; + } + + if (*(u32*)&buf[0x00] != 0xba010000 || + *(u32*)&buf[0x0e] != 0xbd010000 || + !(buf[0x15] & 0x80) || + (buf[0x17] & 0xf0) != 0x20 || + (buf[buf[0x16] + 0x17] & 0xe0) != 0x20) + { + gf_import_message(import, GF_CORRUPTED_DATA, "Corrupted data found in file %s", filename); + continue; + } + + psize = (buf[buf[0x16] + 0x18] << 8) + buf[buf[0x16] + 0x19]; + dsize = (buf[buf[0x16] + 0x1a] << 8) + buf[buf[0x16] + 0x1b]; + packet = (char *) malloc(sizeof(char)*psize); + if (!packet) { + err = gf_import_message(import, GF_OUT_OF_MEM, "Memory allocation failed"); + goto error; + } + + for (i = 0, left = psize; i < psize; i += size, left -= size) { + hsize = 0x18 + buf[0x16]; + size = MIN(left, 0x800 - hsize); + memcpy(packet + i, buf + hsize, size); + + if (size != left) { + while (fread(buf, 1, sizeof(buf), file)) { + if (buf[buf[0x16] + 0x17] == (trackID | 0x20)) { + break; + } + } + } + } + + if (i != psize || left > 0) { + gf_import_message(import, GF_CORRUPTED_DATA, "Corrupted data found in file %s", filename); + continue; + } + + if (vobsub_get_subpic_duration(packet, psize, dsize, &duration) != GF_OK) { + gf_import_message(import, GF_CORRUPTED_DATA, "Corrupted data found in file %s", filename); + continue; + } + + last_samp_dur = duration; + + samp->data = packet; + samp->dataLength = psize; + samp->DTS = pos->start * 90; + + err = gf_isom_add_sample(import->dest, track, di, samp); + if (err) goto error; + free(packet); + + gf_set_progress("Importing VobSub", c, total); + + if (import->flags & GF_IMPORT_DO_ABORT) { + break; + } + } + + gf_isom_set_last_sample_duration(import->dest, track, last_samp_dur); + + MP4T_RecomputeBitRate(import->dest, track); + gf_set_progress("Importing VobSub", total, total); + + err = GF_OK; + +error: + if (import->esd && destroy_esd) { + import->esd->decoderConfig->decoderSpecificInfo->data = NULL; + gf_odf_desc_del((GF_Descriptor *)import->esd); + import->esd = NULL; + } + if (samp) { + samp->data = NULL; + gf_isom_sample_del(&samp); + } + if (vobsub) { + vobsub_free(vobsub); + } + if (file) { + fclose(file); + } + + return err; +} + + +GF_Err gf_import_ac3(GF_MediaImporter *import) +{ + GF_AC3Header hdr; + Bool destroy_esd; + GF_Err e; + u16 sr; + u32 nb_chan; + FILE *in; + GF_BitStream *bs; + u32 max_size, track, di, tot_size, done, duration; + GF_ISOSample *samp; + + in = gf_f64_open(import->in_name, "rb"); + if (!in) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + bs = gf_bs_from_file(in, GF_BITSTREAM_READ); + + if (!gf_ac3_parser_bs(bs, &hdr, 1)) { + gf_bs_del(bs); + fclose(in); + return gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Audio isn't AC3 audio"); + } + sr = hdr.sample_rate; + + if (import->flags & GF_IMPORT_PROBE_ONLY) { + gf_bs_del(bs); + fclose(in); + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[0].media_type = GF_4CC('A', 'C', '3', ' '); + import->tk_info[0].flags = GF_IMPORT_USE_DATAREF; + import->tk_info[0].audio_info.sample_rate = sr; + import->tk_info[0].audio_info.nb_channels = hdr.channels; + import->nb_tracks = 1; + return GF_OK; + } + + e = GF_OK; + destroy_esd = 0; + if (!import->esd) { + import->esd = gf_odf_desc_esd_new(2); + destroy_esd = 1; + } + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + /*update stream type/oti*/ + import->esd->decoderConfig->streamType = GF_STREAM_AUDIO; + import->esd->decoderConfig->objectTypeIndication = 0xA5; + import->esd->decoderConfig->bufferSizeDB = 20; + import->esd->slConfig->timestampResolution = sr; + + samp = NULL; + nb_chan = hdr.channels; + gf_import_message(import, GF_OK, "AC3 import - sample rate %d - %d channel%s", sr, nb_chan, (nb_chan>1) ? "s" : ""); + + track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_AUDIO, sr); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + import->final_trackID = import->esd->ESID; + if (import->esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) import->esd->decoderConfig->decoderSpecificInfo); + import->esd->decoderConfig->decoderSpecificInfo = NULL; + + if (1) { + GF_AC3Config cfg; + cfg.acmod = hdr.acmod; + cfg.brcode = hdr.brcode; + cfg.bsid = hdr.bsid; + cfg.bsmod = hdr.bsmod; + cfg.fscod = hdr.fscod; + cfg.lfon = hdr.lfon; + gf_isom_ac3_config_new(import->dest, track, &cfg, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + } else { + gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di); + } + gf_isom_set_audio_info(import->dest, track, di, sr, nb_chan, 16); + + gf_bs_seek(bs, 0); + tot_size = (u32) gf_bs_get_size(bs); + + e = GF_OK; + samp = gf_isom_sample_new(); + samp->IsRAP = 1; + + duration = import->duration*sr; + duration /= 1000; + + max_size = 0; + done = 0; + while (gf_ac3_parser_bs(bs, &hdr, 0)) { + samp->dataLength = hdr.framesize; + + + if (import->flags & GF_IMPORT_USE_DATAREF) { + e = gf_isom_add_sample_reference(import->dest, track, di, samp, gf_bs_get_position(bs) ); + gf_bs_skip_bytes(bs, samp->dataLength); + } else { + if (samp->dataLength > max_size) { + samp->data = (char*)realloc(samp->data, sizeof(char) * samp->dataLength); + max_size = samp->dataLength; + } + gf_bs_read_data(bs, samp->data, samp->dataLength); + e = gf_isom_add_sample(import->dest, track, di, samp); + } + if (e) goto exit; + + gf_set_progress("Importing AAC", done, tot_size); + + samp->DTS += 1536; + done += samp->dataLength; + if (duration && (samp->DTS > duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + } + MP4T_RecomputeBitRate(import->dest, track); + gf_set_progress("Importing AC3", tot_size, tot_size); + +exit: + if (import->esd && destroy_esd) { + gf_odf_desc_del((GF_Descriptor *) import->esd); + import->esd = NULL; + } + if (samp) gf_isom_sample_del(&samp); + fclose(in); + return e; +} + + +GF_EXPORT +GF_Err gf_media_import(GF_MediaImporter *importer) +{ + GF_Err gf_import_timed_text(GF_MediaImporter *import); + GF_Err e; + char *ext, *xml_type; + char *fmt = ""; + if (!importer || (!importer->dest && (importer->flags!=GF_IMPORT_PROBE_ONLY)) || (!importer->in_name && !importer->orig) ) return GF_BAD_PARAM; + + if (importer->orig) return gf_import_isomedia(importer); + + ext = strrchr(importer->in_name, '.'); + if (!ext) ext = ""; + + if (importer->streamFormat) fmt = importer->streamFormat; + + + /*always try with MP4 - this allows using .m4v extension for both raw CMP and iPod's files*/ + if (gf_isom_probe_file(importer->in_name)) { + importer->orig = gf_isom_open(importer->in_name, GF_ISOM_OPEN_READ, NULL); + if (importer->orig) { + e = gf_import_isomedia(importer); + gf_isom_delete(importer->orig); + importer->orig = NULL; + return e; + } + } + + /*AVI audio/video*/ + if (!strnicmp(ext, ".avi", 4) || !stricmp(fmt, "AVI") ) { + e = gf_import_avi_video(importer); + if (e) return e; + return gf_import_avi_audio(importer); + } + /*OGG audio/video*/ + if (!strnicmp(ext, ".ogg", 4) || !stricmp(fmt, "OGG")) { + e = gf_import_ogg_video(importer); + if (e) return e; + return gf_import_ogg_audio(importer); + } + /*MPEG PS*/ + if (!strnicmp(ext, ".mpg", 4) || !strnicmp(ext, ".mpeg", 5) + || !strnicmp(ext, ".vob", 4) || !strnicmp(ext, ".vcd", 4) || !strnicmp(ext, ".svcd", 5) + || !stricmp(fmt, "MPEG1") || !stricmp(fmt, "MPEG-PS") || !stricmp(fmt, "MPEG2-PS") + ) { + e = gf_import_mpeg_ps_video(importer); + if (e) return e; + return gf_import_mpeg_ps_audio(importer); + } + /*MPEG-2 TS*/ + if (!strnicmp(ext, ".ts", 3) || !strnicmp(ext, ".m2t", 4) + || !stricmp(fmt, "MPEGTS") || !stricmp(fmt, "MPEG-TS") + || !stricmp(fmt, "MPGTS") || !stricmp(fmt, "MPG-TS") + || !stricmp(fmt, "MPEG2TS") || !stricmp(fmt, "MPEG2-TS") + || !stricmp(fmt, "MPG2TS") || !stricmp(fmt, "MPG2-TS") + ) { + return gf_import_mpeg_ts(importer); + } + + /*MPEG1/2 Audio*/ + if (!strnicmp(ext, ".mp2", 4) || !strnicmp(ext, ".mp3", 4) || !strnicmp(ext, ".m1a", 4) || !strnicmp(ext, ".m2a", 4) || !stricmp(fmt, "MP3") || !stricmp(fmt, "MPEG-AUDIO") ) + return gf_import_mp3(importer); + /*NHNT*/ + if (!strnicmp(ext, ".media", 5) || !strnicmp(ext, ".info", 5) || !strnicmp(ext, ".nhnt", 5) || !stricmp(fmt, "NHNT") ) + return gf_import_nhnt(importer); + /*NHML*/ + if (!strnicmp(ext, ".nhml", 5) || !stricmp(fmt, "NHML") ) + return gf_import_nhml_dims(importer, 0); + /*jpg & png & jp2*/ + if (!strnicmp(ext, ".jpg", 4) || !strnicmp(ext, ".jpeg", 5) || !strnicmp(ext, ".jp2", 4) || !strnicmp(ext, ".png", 4) || !stricmp(fmt, "JPEG") || !stricmp(fmt, "PNG") || !stricmp(fmt, "JP2") ) + return gf_import_still_image(importer); + /*MPEG-2/4 AAC*/ + if (!strnicmp(ext, ".aac", 4) || !stricmp(fmt, "AAC") || !stricmp(fmt, "MPEG4-AUDIO") ) + return gf_import_aac_adts(importer); + /*AMR & 3GPP2 speec codecs*/ + if (!strnicmp(ext, ".amr", 4) || !strnicmp(ext, ".awb", 4) || !strnicmp(ext, ".smv", 4) || !strnicmp(ext, ".evc", 4) + || !stricmp(fmt, "AMR") || !stricmp(fmt, "EVRC") || !stricmp(fmt, "SMV") ) + return gf_import_amr_evrc_smv(importer); + /*QCelp & other in QCP file format*/ + if (!strnicmp(ext, ".qcp", 4) || !stricmp(fmt, "QCELP") ) + return gf_import_qcp(importer); + /*MPEG-4 video*/ + if (!strnicmp(ext, ".cmp", 4) || !strnicmp(ext, ".m4v", 4) || !stricmp(fmt, "CMP") || !stricmp(fmt, "MPEG4-Video") ) + return gf_import_cmp(importer, 0); + /*MPEG-1/2 video*/ + if (!strnicmp(ext, ".m2v", 4) || !strnicmp(ext, ".m1v", 4) || !stricmp(fmt, "MPEG2-Video") || !stricmp(fmt, "MPEG1-Video") ) + return gf_import_cmp(importer, 2); + /*H2632 video*/ + if (!strnicmp(ext, ".263", 4) || !strnicmp(ext, ".h263", 5) || !stricmp(fmt, "H263") ) + return gf_import_h263(importer); + /*H264/AVC video*/ + if (!strnicmp(ext, ".h264", 5) || !strnicmp(ext, ".264", 4) || !strnicmp(ext, ".x264", 5) + || !strnicmp(ext, ".h26L", 5) || !strnicmp(ext, ".26l", 4) + || !stricmp(fmt, "AVC") || !stricmp(fmt, "H264") ) + return gf_import_h264(importer); + /*MPEG-4 SAF multiplex*/ + if (!strnicmp(ext, ".saf", 4) || !strnicmp(ext, ".lsr", 4) || !stricmp(fmt, "SAF") ) + return gf_import_saf(importer); + /*text subtitles*/ + if (!strnicmp(ext, ".srt", 4) || !strnicmp(ext, ".sub", 4) || !strnicmp(ext, ".ttxt", 5) + || !stricmp(fmt, "SRT") || !stricmp(fmt, "SUB") || !stricmp(fmt, "TEXT") ) + return gf_import_timed_text(importer); + /*VobSub*/ + if (!strnicmp(ext, ".idx", 4) || !stricmp(fmt, "VOBSUB")) + return gf_import_vobsub(importer); + /*raw importer*/ + if (!stricmp(fmt, "RAW")) { + return gf_import_raw_unit(importer); + } + /*AC3*/ + if (!strnicmp(ext, ".ac3", 4) || !stricmp(fmt, "AC3") ) + return gf_import_ac3(importer); + /*DIMS*/ + if (!strnicmp(ext, ".dml", 4) || !stricmp(fmt, "DIMS") ) + return gf_import_nhml_dims(importer, 1); + + /*try XML things*/ + xml_type = gf_xml_get_root_type(importer->in_name, &e); + if (xml_type) { + if (!stricmp(xml_type, "TextStream") || !stricmp(xml_type, "text3GTrack") ) { + free(xml_type); + return gf_import_timed_text(importer); + } + else if (!stricmp(xml_type, "NHNTStream")) { + free(xml_type); + return gf_import_nhml_dims(importer, 0); + } + else if (!stricmp(xml_type, "DIMSStream") ) { + free(xml_type); + return gf_import_nhml_dims(importer, 1); + } + free(xml_type); + } + + return gf_import_message(importer, e, "Unknown input file type"); +} + +GF_EXPORT +GF_Err gf_media_change_par(GF_ISOFile *file, u32 track, s32 ar_num, s32 ar_den) +{ + u32 tk_w, tk_h, stype; + GF_Err e; + + e = gf_isom_get_visual_info(file, track, 1, &tk_w, &tk_h); + if (e) return e; + + stype = gf_isom_get_media_subtype(file, track, 1); + if (stype==GF_ISOM_SUBTYPE_AVC_H264) { + GF_AVCConfig *avcc = gf_isom_avc_config_get(file, track, 1); + AVC_ChangePAR(avcc, ar_num, ar_den); + e = gf_isom_avc_config_update(file, track, 1, avcc); + gf_odf_avc_cfg_del(avcc); + if (e) return e; + } + else if (stype==GF_ISOM_SUBTYPE_MPEG4) { + GF_ESD *esd = gf_isom_get_esd(file, track, 1); + if (!esd || !esd->decoderConfig || (esd->decoderConfig->streamType!=4) ) { + if (esd) gf_odf_desc_del((GF_Descriptor *) esd); + return GF_NOT_SUPPORTED; + } + if (esd->decoderConfig->objectTypeIndication==0x20) { + e = gf_m4v_rewrite_par(&esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength, ar_num, ar_den); + if (!e) e = gf_isom_change_mpeg4_description(file, track, 1, esd); + gf_odf_desc_del((GF_Descriptor *) esd); + if (e) return e; + } + } else { + return GF_BAD_PARAM; + } + +/* With the removal of the following code, files produced are compatible with Quicktime 7.3 */ +/* + if ((ar_den>=0) && (ar_num>=0)) { + if (ar_den) tk_w = tk_w * ar_num / ar_den; + else if (ar_num) tk_h = tk_h * ar_den / ar_num; + } +*/ + e = gf_isom_set_pixel_aspect_ratio(file, track, 1, ar_num, ar_den); + if (e) return e; + return gf_isom_set_track_layout_info(file, track, tk_w<<16, tk_h<<16, 0, 0, 0); +} + +#endif diff --git a/src/media_tools/mpeg2_ps.c b/src/media_tools/mpeg2_ps.c new file mode 100644 index 0000000..863fb61 --- /dev/null +++ b/src/media_tools/mpeg2_ps.c @@ -0,0 +1,1808 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is MPEG4IP. + * + * The Initial Developer of the Original Code is Cisco Systems Inc. + * Portions created by Cisco Systems Inc. are + * Copyright (C) Cisco Systems Inc. 2004. All Rights Reserved. + * + * Contributor(s): + * Bill May wmay@cisco.com + */ + +/* + * mpeg2ps.c - parse program stream and vob files + */ +#include "mpeg2_ps.h" + + +static GFINLINE u16 convert16 (u8 *p) +{ +#ifdef GPAC_BIG_ENDIAN + return *(u16 *)p; +#else + u16 val = p[0]; + val <<= 8; + return (val | p[1]); +#endif +} + +static GFINLINE u32 convert32 (u8 *p) +{ +#ifdef GPAC_BIG_ENDIAN + return *(u32 *)p; +#else + u32 val; + val = p[0]; val <<= 8; + val |= p[1]; val <<= 8; + val |= p[2]; val <<= 8; + val |= p[3]; + return val; +#endif +} + +#define FDNULL 0 + +/* + * structure for passing timestamps around + */ +typedef struct mpeg2ps_ts_t +{ + Bool have_pts; + Bool have_dts; + u64 pts; + u64 dts; +} mpeg2ps_ts_t; + +typedef struct mpeg2ps_record_pes_t +{ + struct mpeg2ps_record_pes_t *next_rec; + u64 dts; + u64 location; +} mpeg2ps_record_pes_t; + +s32 MPEG12_FindNextStartCode(unsigned char *pbuffer, u32 buflen, u32 *optr, u32 *scode) +{ + u32 value; + u32 offset; + + if (buflen < 4) return -1; + for (offset = 0; offset < buflen - 3; offset++, pbuffer++) { +#ifdef GPAC_BIG_ENDIAN + value = *(u32 *)pbuffer >> 8; +#else + value = (pbuffer[0] << 16) | (pbuffer[1] << 8) | (pbuffer[2] << 0); +#endif + + if (value == MPEG12_START_CODE_PREFIX) { + *optr = offset; + *scode = (value << 8) | pbuffer[3]; + return 0; + } + } + return -1; +} + +s32 MPEG12_FindNextSliceStart(unsigned char *pbuffer, u32 startoffset, u32 buflen, u32 *slice_offset) +{ + u32 slicestart, code; + while (MPEG12_FindNextStartCode(pbuffer + startoffset, buflen - startoffset, &slicestart, &code) >= 0) { + if ((code >= MPEG12_SLICE_MIN_START) && (code <= MPEG12_SLICE_MAX_START)) { + *slice_offset = slicestart + startoffset; + return 0; + } + startoffset += slicestart + 4; + } + return -1; +} + +#ifndef GPAC_READ_ONLY +/* + * information about reading a stream + */ +typedef struct mpeg2ps_stream_t +{ + mpeg2ps_record_pes_t *record_first, *record_last; + FILE *m_fd; + Bool is_video; + u8 m_stream_id; // program stream id + u8 m_substream_id; // substream, for program stream id == 0xbd + + mpeg2ps_ts_t next_pes_ts, frame_ts; + u32 frames_since_last_ts; + u64 last_ts; + + Bool have_frame_loaded; + /* + * pes_buffer processing. this contains the raw elementary stream data + */ + u8 *pes_buffer; + u32 pes_buffer_size; + u32 pes_buffer_size_max; + u32 pes_buffer_on; + u32 frame_len; + u32 pict_header_offset; // for mpeg video + + // timing information and locations. + s64 first_pes_loc; + u64 start_dts; + Bool first_pes_has_dts; + s64 end_dts_loc; + u64 end_dts; + // audio stuff + u32 freq; + u32 channels; + u32 bitrate; + u32 samples_per_frame; + u32 layer; + // video stuff + u32 h, w, par; + Double frame_rate; + s32 have_mpeg2; + Double bit_rate; + u64 ticks_per_frame; + +} mpeg2ps_stream_t; + +/* + * main interface structure - contains stream pointers and other + * information + */ +struct mpeg2ps_ { + mpeg2ps_stream_t *video_streams[16]; + mpeg2ps_stream_t *audio_streams[32]; + char *filename; + FILE *fd; + u64 first_dts; + u32 audio_cnt, video_cnt; + s64 end_loc; + u64 max_dts; + u64 max_time; // time is in msec. +}; + +/************************************************************************* + * File access routines. Could all be inlined + *************************************************************************/ +static FILE *file_open (const char *name) +{ + return gf_f64_open(name, "rb"); +} + +static Bool file_okay (FILE *fd) +{ + return (fd!=NULL) ? 1 : 0; +} + +static void file_close (FILE *fd) +{ + fclose(fd); +} + +static Bool file_read_bytes(FILE *fd, + u8 *buffer, + u32 len) +{ + u32 readval = fread(buffer, 1, len, fd); + return readval == len; +} + +// note: len could be negative. +static void file_skip_bytes (FILE *fd, s32 len) +{ + gf_f64_seek(fd, len, SEEK_CUR); +} + +#define file_location(__f) gf_f64_tell(__f) +#define file_seek_to(__f, __off) gf_f64_seek(__f, __off, SEEK_SET) + +static s64 file_size(FILE *fd) +{ + s64 ret; + gf_f64_seek(fd, 0, SEEK_END); + ret = gf_f64_tell(fd); + gf_f64_seek(fd, 0, SEEK_SET); + return ret; +} + +static mpeg2ps_record_pes_t *create_record (s64 loc, u64 ts) +{ + mpeg2ps_record_pes_t *ret; + GF_SAFEALLOC(ret, mpeg2ps_record_pes_t); + + ret->next_rec = NULL; + ret->dts = ts; + ret->location = loc; + return ret; +} + +#define MPEG2PS_RECORD_TIME ((u64) (5 * 90000)) +void mpeg2ps_record_pts (mpeg2ps_stream_t *sptr, s64 location, mpeg2ps_ts_t *pTs) +{ + u64 ts; + mpeg2ps_record_pes_t *p, *q; + if (sptr->is_video) { + if (pTs->have_dts == 0) return; + ts = pTs->dts; + } else { + if (pTs->have_pts == 0) return; + ts = pTs->pts; + } + + if (sptr->record_first == NULL) { + sptr->record_first = sptr->record_last = create_record(location, ts); + return; + } + if (ts > sptr->record_last->dts) { + if (ts < MPEG2PS_RECORD_TIME + sptr->record_last->dts) return; + sptr->record_last->next_rec = create_record(location, ts); + sptr->record_last = sptr->record_last->next_rec; + return; + } + if (ts < sptr->record_first->dts) { + if (ts < MPEG2PS_RECORD_TIME + sptr->record_first->dts) return; + p = create_record(location, ts); + p->next_rec = sptr->record_first; + sptr->record_first = p; + return; + } + p = sptr->record_first; + q = p->next_rec; + + while (q != NULL && q->dts < ts) { + p = q; + q = q->next_rec; + } + if (p->dts + MPEG2PS_RECORD_TIME <= ts && + ts + MPEG2PS_RECORD_TIME <= q->dts) { + p->next_rec = create_record(location, ts); + p->next_rec->next_rec = q; + } +} +static Double mpeg12_frame_rate_table[16] = +{ + 0.0, /* Pad */ + 24000.0/1001.0, /* Official frame rates */ + 24.0, + 25.0, + 30000.0/1001.0, + 30.0, + 50.0, + ((60.0*1000.0)/1001.0), + 60.0, + + 1, /* Unofficial economy rates */ + 5, + 10, + 12, + 15, + 0, + 0, +}; + +#define SEQ_ID 1 +int MPEG12_ParseSeqHdr(unsigned char *pbuffer, u32 buflen, s32 *have_mpeg2, u32 *height, u32 *width, + Double *frame_rate, Double *bitrate, u32 *aspect_ratio) +{ + u32 aspect_code; + u32 framerate_code; + u32 bitrate_int; + u32 bitrate_ext; + u32 scode, ix; + s32 found = -1; + *have_mpeg2 = 0; + buflen -= 6; + bitrate_int = 0; + for (ix = 0; ix < buflen; ix++, pbuffer++) { + scode = (pbuffer[0] << 24) | (pbuffer[1] << 16) | (pbuffer[2] << 8) | + pbuffer[3]; + + if (scode == MPEG12_SEQUENCE_START_CODE) { + pbuffer += sizeof(u32); + *width = (pbuffer[0]); + *width <<= 4; + *width |= ((pbuffer[1] >> 4) &0xf); + *height = (pbuffer[1] & 0xf); + *height <<= 8; + *height |= pbuffer[2]; + aspect_code = (pbuffer[3] >> 4) & 0xf; + if (aspect_ratio != NULL) { + u32 par = 0; + switch (aspect_code) { + default: *aspect_ratio = 0; break; + case 2: par = 4; par<<=16; par |= 3; break; + case 3: par = 16; par<<=16; par |= 9; break; + case 4: par = 2; par<<=16; par |= 21; break; + } + *aspect_ratio = par; + } + + + framerate_code = pbuffer[3] & 0xf; + *frame_rate = mpeg12_frame_rate_table[framerate_code]; + // 18 bits + bitrate_int = (pbuffer[4] << 10) | + (pbuffer[5] << 2) | + ((pbuffer[6] >> 6) & 0x3); + *bitrate = bitrate_int; + *bitrate *= 400.0; + ix += sizeof(u32) + 7; + pbuffer += 7; + found = 0; + } else if (found == 0) { + if (scode == MPEG12_EXT_START_CODE) { + pbuffer += sizeof(u32); + ix += sizeof(u32); + switch ((pbuffer[0] >> 4) & 0xf) { + case SEQ_ID: + *have_mpeg2 = 1; + *height = ((pbuffer[1] & 0x1) << 13) | + ((pbuffer[2] & 0x80) << 5) | + (*height & 0x0fff); + *width = (((pbuffer[2] >> 5) & 0x3) << 12) | (*width & 0x0fff); + bitrate_ext = (pbuffer[2] & 0x1f) << 7; + bitrate_ext |= (pbuffer[3] >> 1) & 0x7f; + bitrate_int |= (bitrate_ext << 18); + *bitrate = bitrate_int; + *bitrate *= 400.0; + break; + default: + break; + } + pbuffer++; + ix++; + } else if (scode == MPEG12_PICTURE_START_CODE) { + return found; + } + } + } + return found; +} + + +s32 MPEG12_PictHdrType (unsigned char *pbuffer) +{ + pbuffer += sizeof(u32); + return ((pbuffer[1] >> 3) & 0x7); +} + +u16 MPEG12_PictHdrTempRef(unsigned char *pbuffer) +{ + pbuffer += sizeof(u32); + return ((pbuffer[0] << 2) | ((pbuffer[1] >> 6) & 0x3)); +} + + +static u64 read_pts (u8 *pak) +{ + u64 pts; + u16 temp; + + pts = ((pak[0] >> 1) & 0x7); + pts <<= 15; + temp = convert16(&pak[1]) >> 1; + pts |= temp; + pts <<= 15; + temp = convert16(&pak[3]) >> 1; + pts |= temp; + return pts; +} + + +static mpeg2ps_stream_t *mpeg2ps_stream_create (u8 stream_id, + u8 substream) +{ + mpeg2ps_stream_t *ptr; + GF_SAFEALLOC(ptr, mpeg2ps_stream_t); + ptr->m_stream_id = stream_id; + ptr->m_substream_id = substream; + ptr->is_video = stream_id >= 0xe0; + ptr->pes_buffer = (u8 *)malloc(4*4096); + ptr->pes_buffer_size_max = 4 * 4096; + return ptr; +} + +static void mpeg2ps_stream_destroy (mpeg2ps_stream_t *sptr) +{ + mpeg2ps_record_pes_t *p; + while (sptr->record_first != NULL) { + p = sptr->record_first; + sptr->record_first = p->next_rec; + free(p); + } + if (sptr->m_fd != FDNULL) { + file_close(sptr->m_fd); + sptr->m_fd = FDNULL; + } + if (sptr->pes_buffer) free(sptr->pes_buffer); + free(sptr); +} + + +/* + * adv_past_pack_hdr - read the pack header, advance past it + * we don't do anything with the data + */ +static void adv_past_pack_hdr (FILE *fd, + u8 *pak, + u32 read_from_start) +{ + u8 stuffed; + u8 readbyte; + u8 val; + if (read_from_start < 5) { + file_skip_bytes(fd, 5 - read_from_start); + file_read_bytes(fd, &readbyte, 1); + val = readbyte; + } else { + val = pak[4]; + } + + // we've read 6 bytes + if ((val & 0xc0) != 0x40) { + // mpeg1 + file_skip_bytes(fd, 12 - read_from_start); // skip 6 more bytes + return; + } + file_skip_bytes(fd, 13 - read_from_start); + file_read_bytes(fd, &readbyte, 1); + stuffed = readbyte & 0x7; + file_skip_bytes(fd, stuffed); +} + +/* + * find_pack_start + * look for the pack start code in the file - read 512 bytes at a time, + * searching for that code. + * Note: we may also be okay looking for >= 00 00 01 bb + */ +static Bool find_pack_start (FILE *fd, + u8 *saved, + u32 len) +{ + u8 buffer[512]; + u32 buffer_on = 0, new_offset, scode; + memcpy(buffer, saved, len); + if (file_read_bytes(fd, buffer + len, sizeof(buffer) - len) == 0) { + return 0; + } + while (1) { + if (MPEG12_FindNextStartCode(buffer + buffer_on, + sizeof(buffer) - buffer_on, + &new_offset, + &scode) >= 0) { + buffer_on += new_offset; + if (scode == MPEG2_PS_PACKSTART) { + file_skip_bytes(fd, buffer_on - 512); // go back to header + return 1; + } + buffer_on += 1; + } else { + len = 0; + if (buffer[sizeof(buffer) - 3] == 0 && + buffer[sizeof(buffer) - 2] == 0 && + buffer[sizeof(buffer) - 1] == 1) { + buffer[0] = 0; + buffer[1] = 0; + buffer[2] = 1; + len = 3; + } else if (*(u16 *)(buffer + sizeof(buffer) - 2) == 0) { + buffer[0] = 0; + buffer[1] = 0; + len = 2; + } else if (buffer[sizeof(buffer) - 1] == 0) { + buffer[0] = 0; + len = 1; + } + if (file_read_bytes(fd, buffer + len, sizeof(buffer) - len) == 0) { + return 0; + } + buffer_on = 0; + } + } + return 0; +} + +/* + * copy_bytes_to_pes_buffer - read pes_len bytes into the buffer, + * adjusting it if we need it + */ +static void copy_bytes_to_pes_buffer (mpeg2ps_stream_t *sptr, + u16 pes_len) +{ + u32 to_move; + + if (sptr->pes_buffer_size + pes_len > sptr->pes_buffer_size_max) { + // if no room in the buffer, we'll move it - otherwise, just fill + // note - we might want a better strategy about moving the buffer - + // right now, we might be moving a number of bytes if we have a large + // followed by large frame. + to_move = sptr->pes_buffer_size - sptr->pes_buffer_on; + memmove(sptr->pes_buffer, + sptr->pes_buffer + sptr->pes_buffer_on, + to_move); + sptr->pes_buffer_size = to_move; + sptr->pes_buffer_on = 0; + //printf("moving %d bytes\n", to_move); + if (to_move + pes_len > sptr->pes_buffer_size_max) { + sptr->pes_buffer = (u8 *)realloc(sptr->pes_buffer, + to_move + pes_len + 2048); + sptr->pes_buffer_size_max = to_move + pes_len + 2048; + } + } + file_read_bytes(sptr->m_fd, sptr->pes_buffer + sptr->pes_buffer_size, pes_len); + sptr->pes_buffer_size += pes_len; +#if 0 + printf("copying %u bytes - on %u size %u\n", + pes_len, sptr->pes_buffer_on, sptr->pes_buffer_size); +#endif +} + +/* + * read_to_next_pes_header - read the file, look for the next valid + * pes header. We will skip over PACK headers, but not over any of the + * headers listed in 13818-1, table 2-18 - basically, anything with the + * 00 00 01 and the next byte > 0xbb. + * We return the pes len to read, and the "next byte" + */ +static Bool read_to_next_pes_header (FILE *fd, + u8 *stream_id, + u16 *pes_len) +{ + u32 hdr; + u8 local[6]; + + while (1) { + // read the pes header + if (file_read_bytes(fd, local, 6) == 0) { + return 0; + } + + hdr = convert32(local); + // if we're not a 00 00 01, read until we get the next pack start + // we might want to also read until next PES - look into that. + if (((hdr & MPEG2_PS_START_MASK) != MPEG2_PS_START) || + (hdr < MPEG2_PS_END)) { + if (find_pack_start(fd, local, 6) == 0) { + return 0; + } + continue; + } + if (hdr == MPEG2_PS_PACKSTART) { + // pack start code - we can skip down + adv_past_pack_hdr(fd, local, 6); + continue; + } + if (hdr == MPEG2_PS_END) { + file_skip_bytes(fd, -2); + continue; + } + + // we should have a valid stream and pes_len here... + *stream_id = hdr & 0xff; + *pes_len = convert16(local + 4); +#if 0 + printf("loc: "X64" %x len %u\n", file_location(fd) - 6, + local[3], + *pes_len); +#endif + return 1; + } + return 0; +} + +/* + * read_pes_header_data + * this should read past the pes header for the audio and video streams + * it will store the timestamps if it reads them + */ +static Bool read_pes_header_data (FILE *fd, + u16 orig_pes_len, + u16 *pes_left, + Bool *have_ts, + mpeg2ps_ts_t *ts) +{ + u16 pes_len = orig_pes_len; + u8 local[10]; + u32 hdr_len; + + ts->have_pts = 0; + ts->have_dts = 0; + *have_ts = 0; + if (file_read_bytes(fd, local, 1) == 0) { + return 0; + } + pes_len--; // remove this first byte from length + while (*local == 0xff) { + if (file_read_bytes(fd, local, 1) == 0) { + return 0; + } + pes_len--; + if (pes_len == 0) { + *pes_left = 0; + return 1; + } + } + if ((*local & 0xc0) == 0x40) { + // buffer scale & size + file_skip_bytes(fd, 1); + if (file_read_bytes(fd, local, 1) == 0) { + return 0; + } + pes_len -= 2; + } + + if ((*local & 0xf0) == 0x20) { + // mpeg-1 with pts + if (file_read_bytes(fd, local + 1, 4) == 0) { + return 0; + } + ts->have_pts = 1; + ts->pts = ts->dts = read_pts(local); + //printf("mpeg1 pts "U64"\n", ts->pts); + *have_ts = 1; + pes_len -= 4; + } else if ((*local & 0xf0) == 0x30) { + // have mpeg 1 pts and dts + if (file_read_bytes(fd, local + 1, 9) == 0) { + return 0; + } + ts->have_pts = 1; + ts->have_dts = 1; + *have_ts = 1; + ts->pts = read_pts(local); + ts->dts = read_pts(local + 5); + pes_len -= 9; + } else if ((*local & 0xc0) == 0x80) { + // mpeg2 pes header - we're pointing at the flags field now + if (file_read_bytes(fd, local + 1, 2) == 0) { + return 0; + } + hdr_len = local[2]; + pes_len -= hdr_len + 2; // first byte removed already + if ((local[1] & 0xc0) == 0x80) { + // just pts + ts->have_pts = 1; + file_read_bytes(fd, local, 5); + ts->pts = ts->dts = read_pts(local); + *have_ts = 1; + hdr_len -= 5; + } else if ((local[1] & 0xc0) == 0xc0) { + // pts and dts + ts->have_pts = 1; + ts->have_dts = 1; + *have_ts = 1; + file_read_bytes(fd, local, 10); + ts->pts = read_pts(local); + ts->dts = read_pts(local + 5); + hdr_len -= 10; + } + file_skip_bytes(fd, hdr_len); + } else if (*local != 0xf) { + file_skip_bytes(fd, pes_len); + pes_len = 0; + } + *pes_left = pes_len; + return 1; +} + +static Bool search_for_next_pes_header (mpeg2ps_stream_t *sptr, + u16 *pes_len, + Bool *have_ts, + s64 *found_loc) +{ + u8 stream_id; + u8 local; + s64 loc; + while (1) { + // this will read until we find the next pes. We don't know if the + // stream matches - this will read over pack headers + if (read_to_next_pes_header(sptr->m_fd, &stream_id, pes_len) == 0) { + return 0; + } + + if (stream_id != sptr->m_stream_id) { + file_skip_bytes(sptr->m_fd, *pes_len); + continue; + } + loc = file_location(sptr->m_fd) - 6; + // advance past header, reading pts + if (read_pes_header_data(sptr->m_fd, + *pes_len, + pes_len, + have_ts, + &sptr->next_pes_ts) == 0) { + return 0; + } + + // If we're looking at a private stream, make sure that the sub-stream + // matches. + if (sptr->m_stream_id == 0xbd) { + // ac3 or pcm + file_read_bytes(sptr->m_fd, &local, 1); + *pes_len -= 1; + if (local != sptr->m_substream_id) { + file_skip_bytes(sptr->m_fd, *pes_len); + continue; // skip to the next one + } + *pes_len -= 3; + file_skip_bytes(sptr->m_fd, 3); // 4 bytes - we don't need now... + // we need more here... + } + if (have_ts) { + mpeg2ps_record_pts(sptr, loc, &sptr->next_pes_ts); + } + if (found_loc != NULL) *found_loc = loc; + return 1; + } + return 0; +} + +/* + * mpeg2ps_stream_read_next_pes_buffer - for the given stream, + * go forward in the file until the next PES for the stream is read. Read + * the header (pts, dts), and read the data into the pes_buffer pointer + */ +static Bool mpeg2ps_stream_read_next_pes_buffer (mpeg2ps_stream_t *sptr) +{ + u16 pes_len; + Bool have_ts; + + if (search_for_next_pes_header(sptr, &pes_len, &have_ts, NULL) == 0) { + return 0; + } + + copy_bytes_to_pes_buffer(sptr, pes_len); + + return 1; +} + + +/*************************************************************************** + * Frame reading routine. For each stream, the fd's should be different. + * we will read from the pes stream, and save it in the stream's pes buffer. + * This will give us raw data that we can search through for frame headers, + * and the like. We shouldn't read more than we need - when we need to read, + * we'll put the whole next pes buffer in the buffer + * + * Audio routines are of the format: + * look for header + * determine length + * make sure length is in buffer + * + * Video routines + * look for start header (GOP, SEQ, Picture) + * look for pict header + * look for next start (END, GOP, SEQ, Picture) + * + ***************************************************************************/ +#define IS_MPEG_START(a) ((a) == 0xb3 || (a) == 0x00 || (a) == 0xb8) + +static Bool +mpeg2ps_stream_find_mpeg_video_frame (mpeg2ps_stream_t *sptr) +{ + u32 offset, scode; + Bool have_pict; + Bool started_new_pes = 0; + u32 start; + /* + * First thing - determine if we have enough bytes to read the header. + * if we do, we have the correct timestamp. If not, we read the new + * pes, so we'd want to use the timestamp we read. + */ + sptr->frame_ts = sptr->next_pes_ts; + if (sptr->pes_buffer_size <= sptr->pes_buffer_on + 4) { + if (sptr->pes_buffer_size != sptr->pes_buffer_on) + started_new_pes = 1; + if (mpeg2ps_stream_read_next_pes_buffer(sptr) == 0) { + return 0; + } + } + while (MPEG12_FindNextStartCode(sptr->pes_buffer + sptr->pes_buffer_on, + sptr->pes_buffer_size - sptr->pes_buffer_on, + &offset, + &scode) < 0 || + (!IS_MPEG_START(scode & 0xff))) { + if (sptr->pes_buffer_size > 3) + sptr->pes_buffer_on = sptr->pes_buffer_size - 3; + else { + sptr->pes_buffer_on = sptr->pes_buffer_size; + started_new_pes = 1; + } + if (mpeg2ps_stream_read_next_pes_buffer(sptr) == 0) { + return 0; + } + } + sptr->pes_buffer_on += offset; + if (offset == 0 && started_new_pes) { + // nothing... we've copied the timestamp already. + } else { + // we found the new start, but we pulled in a new pes header before + // starting. So, we want to use the header that we read. + sptr->frame_ts = sptr->next_pes_ts; // set timestamp after searching + // clear timestamp indication + sptr->next_pes_ts.have_pts = sptr->next_pes_ts.have_dts = 0; + } +#if 0 + printf("header %x at %d\n", scode, sptr->pes_buffer_on); +#endif + + if (scode == MPEG12_PICTURE_START_CODE) { + sptr->pict_header_offset = sptr->pes_buffer_on; + have_pict = 1; + } else have_pict = 0; + + start = 4 + sptr->pes_buffer_on; + while (1) { + + if (MPEG12_FindNextStartCode(sptr->pes_buffer + start, + sptr->pes_buffer_size - start, + &offset, + &scode) < 0) { + start = sptr->pes_buffer_size - 3; + start -= sptr->pes_buffer_on; + sptr->pict_header_offset -= sptr->pes_buffer_on; + if (mpeg2ps_stream_read_next_pes_buffer(sptr) == 0) { + return 0; + } + start += sptr->pes_buffer_on; + sptr->pict_header_offset += sptr->pes_buffer_on; + } else { +#if 0 + printf("2header %x at %d\n", scode, start); +#endif + + start += offset; + if (have_pict == 0) { + if (scode == MPEG12_PICTURE_START_CODE) { + have_pict = 1; + sptr->pict_header_offset = start; + } + } else { + if (IS_MPEG_START(scode & 0xff) || + scode == MPEG12_SEQUENCE_END_START_CODE) { + sptr->frame_len = start - sptr->pes_buffer_on; + sptr->have_frame_loaded = 1; + return 1; + } + } + start += 4; + } + } + return 0; +} + +static Bool mpeg2ps_stream_find_ac3_frame (mpeg2ps_stream_t *sptr) +{ + u32 diff; + GF_AC3Header hdr; + Bool started_new_pes = 0; + sptr->frame_ts = sptr->next_pes_ts; // set timestamp after searching + if (sptr->pes_buffer_size <= sptr->pes_buffer_on + 6) { + if (sptr->pes_buffer_size != sptr->pes_buffer_on) + started_new_pes = 1; + if (mpeg2ps_stream_read_next_pes_buffer(sptr) == 0) { + return 0; + } + } + while (gf_ac3_parser(sptr->pes_buffer + sptr->pes_buffer_on, + sptr->pes_buffer_size - sptr->pes_buffer_on, + &diff, + &hdr, 0) <= 0) { + // don't have frame + if (sptr->pes_buffer_size > 6) { + sptr->pes_buffer_on = sptr->pes_buffer_size - 6; + started_new_pes = 1; + } else { + sptr->pes_buffer_on = sptr->pes_buffer_size; + } + if (mpeg2ps_stream_read_next_pes_buffer(sptr) == 0) { + return 0; + } + } + sptr->frame_len = hdr.framesize; + sptr->pes_buffer_on += diff; + if (diff == 0 && started_new_pes) { + // we might have a new PTS - but it's not here + } else { + sptr->frame_ts = sptr->next_pes_ts; + sptr->next_pes_ts.have_dts = sptr->next_pes_ts.have_pts = 0; + } + while (sptr->pes_buffer_size - sptr->pes_buffer_on < sptr->frame_len) { +#if 0 + printf("don't have enough - on %u size %u %u %u\n", sptr->pes_buffer_on, + sptr->pes_buffer_size, + sptr->pes_buffer_size - sptr->pes_buffer_on, + sptr->frame_len); +#endif + if (mpeg2ps_stream_read_next_pes_buffer(sptr) == 0) { + return 0; + } + } + sptr->have_frame_loaded = 1; + return 1; +} + +static Bool mpeg2ps_stream_find_mp3_frame (mpeg2ps_stream_t *sptr) +{ + u32 diff, hdr; + Bool started_new_pes = 0; + + sptr->frame_ts = sptr->next_pes_ts; + if (sptr->pes_buffer_size <= sptr->pes_buffer_on + 4) { + if (sptr->pes_buffer_size != sptr->pes_buffer_on) + started_new_pes = 1; + if (mpeg2ps_stream_read_next_pes_buffer(sptr) == 0) { + return 0; + } + } + while ((hdr=gf_mp3_get_next_header_mem((char*)sptr->pes_buffer + sptr->pes_buffer_on, + sptr->pes_buffer_size - sptr->pes_buffer_on, + &diff) ) == 0) { + // don't have frame + if (sptr->pes_buffer_size > 3) { + if (sptr->pes_buffer_on != sptr->pes_buffer_size) { + sptr->pes_buffer_on = sptr->pes_buffer_size - 3; + } + started_new_pes = 1; // we have left over bytes... + } else { + sptr->pes_buffer_on = sptr->pes_buffer_size; + } + if (mpeg2ps_stream_read_next_pes_buffer(sptr) == 0) { + return 0; + } + } + // have frame. + sptr->frame_len = gf_mp3_frame_size(hdr); + sptr->pes_buffer_on += diff; + if (diff == 0 && started_new_pes) { + + } else { + sptr->frame_ts = sptr->next_pes_ts; + sptr->next_pes_ts.have_dts = sptr->next_pes_ts.have_pts = 0; + } + while (sptr->pes_buffer_size - sptr->pes_buffer_on < sptr->frame_len) { +#if 0 + printf("don't have enough - on %u size %u %u %u\n", sptr->pes_buffer_on, + sptr->pes_buffer_size, + sptr->pes_buffer_size - sptr->pes_buffer_on, + sptr->frame_len); +#endif + if (mpeg2ps_stream_read_next_pes_buffer(sptr) == 0) { + return 0; + } + } + sptr->have_frame_loaded = 1; + return 1; +} + +/* + * mpeg2ps_stream_read_frame. read the correct frame based on stream type. + * advance_pointers is 0 when we want to use the data + */ +static Bool mpeg2ps_stream_read_frame (mpeg2ps_stream_t *sptr, + u8 **buffer, + u32 *buflen, + Bool advance_pointers) +{ + // Bool done = 0; + if (sptr->is_video) { + if (mpeg2ps_stream_find_mpeg_video_frame(sptr)) { + *buffer = sptr->pes_buffer + sptr->pes_buffer_on; + *buflen = sptr->frame_len; + if (advance_pointers) { + sptr->pes_buffer_on += sptr->frame_len; + } + return 1; + } + return 0; + } else if (sptr->m_stream_id == 0xbd) { + // would need to handle LPCM here + if (mpeg2ps_stream_find_ac3_frame(sptr)) { + *buffer = sptr->pes_buffer + sptr->pes_buffer_on; + *buflen = sptr->frame_len; + if (advance_pointers) + sptr->pes_buffer_on += sptr->frame_len; + return 1; + } + return 0; + } else if (mpeg2ps_stream_find_mp3_frame(sptr)) { + *buffer = sptr->pes_buffer + sptr->pes_buffer_on; + *buflen = sptr->frame_len; + if (advance_pointers) + sptr->pes_buffer_on += sptr->frame_len; + return 1; + } + return 0; +} + +/* + * get_info_from_frame - we have a frame, get the info from it. + */ +static void get_info_from_frame (mpeg2ps_stream_t *sptr, + u8 *buffer, + u32 buflen) +{ + if (sptr->is_video) { + if (MPEG12_ParseSeqHdr(buffer, buflen, + &sptr->have_mpeg2, + &sptr->h, + &sptr->w, + &sptr->frame_rate, + &sptr->bit_rate, + &sptr->par) < 0) { + sptr->m_stream_id = 0; + sptr->m_fd = FDNULL; + } + sptr->ticks_per_frame = (u64)(90000.0 / sptr->frame_rate); + return; + } + + if (sptr->m_stream_id >= 0xc0) { + // mpeg audio + u32 hdr = GF_4CC(buffer[0],buffer[1],buffer[2],buffer[3]); + + sptr->channels = gf_mp3_num_channels(hdr); + sptr->freq = gf_mp3_sampling_rate(hdr); + sptr->samples_per_frame = gf_mp3_window_size(hdr); + sptr->bitrate = gf_mp3_bit_rate(hdr) * 1000; // give bps, not kbps + sptr->layer = gf_mp3_layer(hdr); + } else if (sptr->m_stream_id == 0xbd) { + if (sptr->m_substream_id >= 0xa0) { + // PCM - ??? + } else if (sptr->m_substream_id >= 0x80) { + u32 pos; + GF_AC3Header hdr; + gf_ac3_parser(buffer, buflen, &pos, &hdr, 0); + sptr->bitrate = hdr.bitrate; + sptr->freq = hdr.sample_rate; + sptr->channels = hdr.channels; + sptr->samples_per_frame = 256 * 6; + } else { + return; + } + } else { + return; + } +} + +/* + * clear_stream_buffer - called when we seek to clear out any data in + * the buffers + */ +static void clear_stream_buffer (mpeg2ps_stream_t *sptr) +{ + sptr->pes_buffer_on = sptr->pes_buffer_size = 0; + sptr->frame_len = 0; + sptr->have_frame_loaded = 0; + sptr->next_pes_ts.have_dts = sptr->next_pes_ts.have_pts = 0; + sptr->frame_ts.have_dts = sptr->frame_ts.have_pts = 0; +} + +/* + * convert_to_msec - convert ts (at 90000) to msec, based on base_ts and + * frames_since_last_ts. + */ +static u64 convert_ts (mpeg2ps_stream_t *sptr, + mpeg2ps_ts_type_t ts_type, + u64 ts, + u64 base_ts, + u32 frames_since_ts) +{ + u64 ret, calc; + ret = ts - base_ts; + if (sptr->is_video) { + // video + ret += frames_since_ts * sptr->ticks_per_frame; + } else { + // audio + calc = (frames_since_ts * 90000 * sptr->samples_per_frame) / sptr->freq; + ret += calc; + } + if (ts_type == TS_MSEC) + ret /= (u64) (90); // * 1000 / 90000 + + return ret; +} + +/* + * find_stream_from_id - given the stream, get the sptr. + * only used in inital set up, really. APIs use index into + * video_streams and audio_streams arrays. + */ +static mpeg2ps_stream_t *find_stream_from_id (mpeg2ps_t *ps, + u8 stream_id, + u8 substream) +{ + u8 ix; + if (stream_id >= 0xe0) { + for (ix = 0; ix < ps->video_cnt; ix++) { + if (ps->video_streams[ix]->m_stream_id == stream_id) { + return ps->video_streams[ix]; + } + } + } else { + for (ix = 0; ix < ps->audio_cnt; ix++) { + if (ps->audio_streams[ix]->m_stream_id == stream_id && + (stream_id != 0xbd || + substream == ps->audio_streams[ix]->m_substream_id)) { + return ps->audio_streams[ix]; + } + } + } + return NULL; +} + +/* + * add_stream - add a new stream + */ +static Bool add_stream (mpeg2ps_t *ps, + u8 stream_id, + u8 substream, + s64 first_loc, + mpeg2ps_ts_t *ts) +{ + mpeg2ps_stream_t *sptr; + + sptr = find_stream_from_id(ps, stream_id, substream); + if (sptr != NULL) return 0; + + // need to add + + sptr = mpeg2ps_stream_create(stream_id, substream); + sptr->first_pes_loc = first_loc; + if (ts == NULL || + (ts->have_dts == 0 && ts->have_pts == 0)) { + sptr->first_pes_has_dts = 0; + } else { + sptr->start_dts = ts->have_dts ? ts->dts : ts->pts; + sptr->first_pes_has_dts = 1; + } + if (sptr->is_video) { + // can't be more than 16 - e0 to ef... + ps->video_streams[ps->video_cnt] = sptr; + ps->video_cnt++; + } else { + if (ps->audio_cnt >= 32) { + mpeg2ps_stream_destroy(sptr); + return 0; + } + ps->audio_streams[ps->audio_cnt] = sptr; + ps->audio_cnt++; + } + return 1; +} + +static void check_fd_for_stream (mpeg2ps_t *ps, + mpeg2ps_stream_t *sptr) +{ + if (sptr->m_fd != FDNULL) return; + + sptr->m_fd = file_open(ps->filename); +} + +/* + * advance_frame - when we're reading frames, this indicates that we're + * done. We will call this when we read a frame, but not when we + * seek. It allows us to leave the last frame we're seeking in the + * buffer + */ +static void advance_frame (mpeg2ps_stream_t *sptr) +{ + sptr->pes_buffer_on += sptr->frame_len; + sptr->have_frame_loaded = 0; + if (sptr->frame_ts.have_dts || sptr->frame_ts.have_pts) { + if (sptr->frame_ts.have_dts) + sptr->last_ts = sptr->frame_ts.dts; + else + sptr->last_ts = sptr->frame_ts.pts; + sptr->frames_since_last_ts = 0; + } else { + sptr->frames_since_last_ts++; + } +} +/* + * get_info_for_all_streams - loop through found streams - read an + * figure out the info + */ +static void get_info_for_all_streams (mpeg2ps_t *ps) +{ + u8 stream_ix, max_ix, av; + mpeg2ps_stream_t *sptr; + u8 *buffer; + u32 buflen; + + file_seek_to(ps->fd, 0); + + // av will be 0 for video streams, 1 for audio streams + // av is just so I don't have to dup a lot of code that does the + // same thing. + for (av = 0; av < 2; av++) { + if (av == 0) max_ix = ps->video_cnt; + else max_ix = ps->audio_cnt; + for (stream_ix = 0; stream_ix < max_ix; stream_ix++) { + if (av == 0) sptr = ps->video_streams[stream_ix]; + else sptr = ps->audio_streams[stream_ix]; + + // we don't open a seperate file descriptor yet (only when they + // start reading or seeking). Use the one from the ps. + sptr->m_fd = ps->fd; // for now + clear_stream_buffer(sptr); + if (mpeg2ps_stream_read_frame(sptr, + &buffer, + &buflen, + 0) == 0) { + sptr->m_stream_id = 0; + sptr->m_fd = FDNULL; + continue; + } + get_info_from_frame(sptr, buffer, buflen); + // here - if (sptr->first_pes_has_dts == 0) should be processed + if (sptr->first_pes_has_dts == 0) { + u32 frames_from_beg = 0; + Bool have_frame; + do { + advance_frame(sptr); + have_frame = + mpeg2ps_stream_read_frame(sptr, &buffer, &buflen, 0); + frames_from_beg++; + } while (have_frame && + sptr->frame_ts.have_dts == 0 && + sptr->frame_ts.have_pts == 0 && + frames_from_beg < 1000); + if (have_frame == 0 || + (sptr->frame_ts.have_dts == 0 && + sptr->frame_ts.have_pts == 0)) { + } else { + sptr->start_dts = sptr->frame_ts.have_dts ? sptr->frame_ts.dts : + sptr->frame_ts.pts; + if (sptr->is_video) { + sptr->start_dts -= frames_from_beg * sptr->ticks_per_frame; + } else { + u64 conv; + conv = sptr->samples_per_frame * 90000; + conv /= (u64)sptr->freq; + sptr->start_dts -= conv; + } + } + } + clear_stream_buffer(sptr); + sptr->m_fd = FDNULL; + } + } +} + +/* + * mpeg2ps_scan_file - read file, grabbing all the information that + * we can out of it (what streams exist, timing, etc). + */ +static void mpeg2ps_scan_file (mpeg2ps_t *ps) +{ + u8 stream_id, stream_ix, substream, av_ix, max_cnt; + u16 pes_len, pes_left; + mpeg2ps_ts_t ts; + s64 loc, first_video_loc = 0, first_audio_loc = 0; + s64 check, orig_check; + mpeg2ps_stream_t *sptr; + Bool valid_stream; + u8 *buffer; + u32 buflen; + Bool have_ts; + + ps->end_loc = file_size(ps->fd); + orig_check = check = MAX(ps->end_loc / 50, 200 * 1024); + + /* + * This part reads and finds the streams. We check up until we + * find audio and video plus a little, with a max of either 200K or + * the file size / 50 + */ + loc = 0; + while (read_to_next_pes_header(ps->fd, &stream_id, &pes_len) && + loc < check) { + pes_left = pes_len; + if (stream_id >= 0xbd && stream_id < 0xf0) { + loc = file_location(ps->fd) - 6; + if (read_pes_header_data(ps->fd, + pes_len, + &pes_left, + &have_ts, + &ts) == 0) { + return; + } + valid_stream = 0; + substream = 0; + if (stream_id == 0xbd) { + if (file_read_bytes(ps->fd, &substream, 1) == 0) { + return; + } + pes_left--; // remove byte we just read + if ((substream >= 0x80 && substream < 0x90) || + (substream >= 0xa0 && substream < 0xb0)){ + valid_stream = 1; + } + } else if (stream_id >= 0xc0 && + stream_id <= 0xef) { + // audio and video + valid_stream = 1; + } + if (valid_stream) { + if (add_stream(ps, stream_id, substream, loc, &ts)) { + // added + if (stream_id >= 0xe0) { + if (ps->video_cnt == 1) { + first_video_loc = loc; + } + } else if (ps->audio_cnt == 1) { + first_audio_loc = loc; + } + if (ps->audio_cnt > 0 && ps->video_cnt > 0) { + s64 diff; + if (first_audio_loc > first_video_loc) + diff = first_audio_loc - first_video_loc; + else + diff = first_video_loc - first_audio_loc; + diff *= 2; + diff += first_video_loc; + if (diff < check) { + check = diff; + } + } + } + } + } + file_skip_bytes(ps->fd, pes_left); + } + if (ps->video_cnt == 0 && ps->audio_cnt == 0) { + return; + } + /* + * Now, we go to close to the end, and try to find the last + * dts that we can + */ + // printf("to end "X64"\n", end - orig_check); + file_seek_to(ps->fd, ps->end_loc - orig_check); + + while (read_to_next_pes_header(ps->fd, &stream_id, &pes_len)) { + loc = file_location(ps->fd) - 6; + if (stream_id == 0xbd || (stream_id >= 0xc0 && stream_id < 0xf0)) { + if (read_pes_header_data(ps->fd, + pes_len, + &pes_left, + &have_ts, + &ts) == 0) { + return; + } + if (stream_id == 0xbd) { + if (file_read_bytes(ps->fd, &substream, 1) == 0) { + return; + } + pes_left--; // remove byte we just read + if (!((substream >= 0x80 && substream < 0x90) || + (substream >= 0xa0 && substream < 0xb0))) { + file_skip_bytes(ps->fd, pes_left); + continue; + } + } else { + substream = 0; + } + sptr = find_stream_from_id(ps, stream_id, substream); + if (sptr == NULL) { + add_stream(ps, stream_id, substream, 0, NULL); + sptr = find_stream_from_id(ps, stream_id, substream); + } + if (sptr != NULL && have_ts) { + sptr->end_dts = ts.have_dts ? ts.dts : ts.pts; + sptr->end_dts_loc = loc; + } +#if 0 + printf("loc "X64" stream %x %x", loc, stream_id, substream); + if (ts.have_pts) printf(" pts "U64, ts.pts); + if (ts.have_dts) printf(" dts "U64, ts.dts); + printf("\n"); +#endif + file_skip_bytes(ps->fd, pes_left); + } + } + + /* + * Now, get the info for all streams, so we can use it again + * we could do this before the above, I suppose + */ + get_info_for_all_streams(ps); + + ps->first_dts = (u64) -1; + + /* + * we need to find the earliest start pts - we use that to calc + * the rest of the timing, so we're 0 based. + */ + for (av_ix = 0; av_ix < 2; av_ix++) { + if (av_ix == 0) max_cnt = ps->video_cnt; + else max_cnt = ps->audio_cnt; + + for (stream_ix = 0; stream_ix < max_cnt; stream_ix++) { + sptr = av_ix == 0 ? ps->video_streams[stream_ix] : + ps->audio_streams[stream_ix]; + if (sptr != NULL && sptr->start_dts < ps->first_dts) { + ps->first_dts = sptr->start_dts; + } + } + } + + /* + * Now, for each thread, we'll start at the last pts location, and + * read the number of frames. This will give us a max time + */ + for (av_ix = 0; av_ix < 2; av_ix++) { + if (av_ix == 0) max_cnt = ps->video_cnt; + else max_cnt = ps->audio_cnt; + for (stream_ix = 0; stream_ix < max_cnt; stream_ix++) { + u32 frame_cnt_since_last; + sptr = av_ix == 0 ? ps->video_streams[stream_ix] : + ps->audio_streams[stream_ix]; + + // pick up here - find the final time... + if (sptr->end_dts_loc != 0) { + file_seek_to(ps->fd, sptr->end_dts_loc); + sptr->m_fd = ps->fd; + frame_cnt_since_last = 0; + clear_stream_buffer(sptr); + while (mpeg2ps_stream_read_frame(sptr, + &buffer, + &buflen, + 1)) { + frame_cnt_since_last++; + } + sptr->m_fd = FDNULL; + clear_stream_buffer(sptr); + ps->max_time = MAX(ps->max_time, + convert_ts(sptr, + TS_MSEC, + sptr->end_dts, + ps->first_dts, + frame_cnt_since_last)); + } + } + } + + ps->max_dts = (ps->max_time * 90) + ps->first_dts; + file_seek_to(ps->fd, 0); +} + +/************************************************************************* + * API routines + *************************************************************************/ +u64 mpeg2ps_get_max_time_msec (mpeg2ps_t *ps) +{ + return ps->max_time; +} + +u32 mpeg2ps_get_video_stream_count (mpeg2ps_t *ps) +{ + return ps->video_cnt; +} + +#define NUM_ELEMENTS_IN_ARRAY(name) ((sizeof((name))) / (sizeof(*(name)))) + +// routine to check stream number passed. +static Bool invalid_video_streamno (mpeg2ps_t *ps, u32 streamno) +{ + if (streamno >= NUM_ELEMENTS_IN_ARRAY(ps->video_streams)) return 1; + if (ps->video_streams[streamno] == NULL) return 1; + return 0; +} + +const char *mpeg2ps_get_video_stream_name (mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_video_streamno(ps, streamno)) { + return 0; + } + if (ps->video_streams[streamno]->have_mpeg2) { + return "Mpeg-2"; + } + return "Mpeg-1"; +} + +mpeg2ps_video_type_t mpeg2ps_get_video_stream_type (mpeg2ps_t *ps, + u32 streamno) +{ + if (invalid_video_streamno(ps, streamno)) { + return MPEG_VIDEO_UNKNOWN; + } + return ps->video_streams[streamno]->have_mpeg2 ? MPEG_VIDEO_MPEG2 : MPEG_VIDEO_MPEG1; +} + +u32 mpeg2ps_get_video_stream_width (mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_video_streamno(ps, streamno)) { + return 0; + } + return ps->video_streams[streamno]->w; +} + +u32 mpeg2ps_get_video_stream_height (mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_video_streamno(ps, streamno)) { + return 0; + } + return ps->video_streams[streamno]->h; +} + +u32 mpeg2ps_get_video_stream_aspect_ratio (mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_video_streamno(ps, streamno)) { + return 0; + } + return ps->video_streams[streamno]->par; +} + +Double mpeg2ps_get_video_stream_bitrate (mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_video_streamno(ps, streamno)) { + return 0; + } + return ps->video_streams[streamno]->bit_rate; +} + +Double mpeg2ps_get_video_stream_framerate (mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_video_streamno(ps, streamno)) { + return 0; + } + return ps->video_streams[streamno]->frame_rate; +} + +static Bool invalid_audio_streamno (mpeg2ps_t *ps, u32 streamno) +{ + if (streamno >= NUM_ELEMENTS_IN_ARRAY(ps->audio_streams)) return 1; + if (ps->audio_streams[streamno] == NULL) return 1; + return 0; +} + +u32 mpeg2ps_get_audio_stream_count (mpeg2ps_t *ps) +{ + return ps->audio_cnt; +} + +const char *mpeg2ps_get_audio_stream_name (mpeg2ps_t *ps, + u32 streamno) +{ + if (invalid_audio_streamno(ps, streamno)) { + return "none"; + } + if (ps->audio_streams[streamno]->m_stream_id >= 0xc0) { + switch (ps->audio_streams[streamno]->layer) { + case 1: return "MP1"; + case 2: return "MP2"; + case 3: return "MP3"; + } + return "unknown mpeg layer"; + } + if (ps->audio_streams[streamno]->m_substream_id >= 0x80 && + ps->audio_streams[streamno]->m_substream_id < 0x90) + return "AC3"; + + return "LPCM"; +} + +mpeg2ps_audio_type_t mpeg2ps_get_audio_stream_type (mpeg2ps_t *ps, + u32 streamno) +{ + if (invalid_audio_streamno(ps, streamno)) { + return MPEG_AUDIO_UNKNOWN; + } + if (ps->audio_streams[streamno]->m_stream_id >= 0xc0) { + return MPEG_AUDIO_MPEG; + } + if (ps->audio_streams[streamno]->m_substream_id >= 0x80 && + ps->audio_streams[streamno]->m_substream_id < 0x90) + return MPEG_AUDIO_AC3; + + return MPEG_AUDIO_LPCM; +} + +u32 mpeg2ps_get_audio_stream_sample_freq (mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_audio_streamno(ps, streamno)) { + return 0; + } + return ps->audio_streams[streamno]->freq; +} + +u32 mpeg2ps_get_audio_stream_channels (mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_audio_streamno(ps, streamno)) { + return 0; + } + return ps->audio_streams[streamno]->channels; +} + +u32 mpeg2ps_get_audio_stream_bitrate (mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_audio_streamno(ps, streamno)) { + return 0; + } + return ps->audio_streams[streamno]->bitrate; +} + +mpeg2ps_t *mpeg2ps_init (const char *filename) +{ + mpeg2ps_t *ps; + GF_SAFEALLOC(ps, mpeg2ps_t); + + if (ps == NULL) { + return NULL; + } + memset(ps, 0, sizeof(*ps)); + ps->fd = file_open(filename); + if (file_okay(ps->fd) == 0) { + free(ps); + return NULL; + } + + ps->filename = strdup(filename); + mpeg2ps_scan_file(ps); + if (ps->video_cnt == 0 && ps->audio_cnt == 0) { + mpeg2ps_close(ps); + return NULL; + } + return ps; +} + +void mpeg2ps_close (mpeg2ps_t *ps) +{ + u32 ix; + if (ps == NULL) return; + for (ix = 0; ix < ps->video_cnt; ix++) { + mpeg2ps_stream_destroy(ps->video_streams[ix]); + ps->video_streams[ix] = NULL; + } + for (ix = 0; ix < ps->audio_cnt; ix++) { + mpeg2ps_stream_destroy(ps->audio_streams[ix]); + ps->audio_streams[ix] = NULL; + } + + if (ps->filename) free(ps->filename); + if (ps->fd) file_close(ps->fd); + free(ps); +} + +/* + * check_fd_for_stream will make sure we have a fd for the stream we're + * trying to read - we use a different fd for each stream + */ + +/* + * stream_convert_frame_ts_to_msec - given a "read" frame, we'll + * calculate the msec and freq timestamps. This can be called more + * than 1 time, if needed, without changing any variables, such as + * frames_since_last_ts, which gets updated in advance_frame + */ +static u64 stream_convert_frame_ts_to_msec (mpeg2ps_stream_t *sptr, + mpeg2ps_ts_type_t ts_type, + u64 base_dts, + u32 *freq_ts) +{ + u64 calc_ts; + u32 frames_since_last = 0; + u64 freq_conv; + + calc_ts = sptr->last_ts; + if (sptr->frame_ts.have_dts) calc_ts = sptr->frame_ts.dts; + else if (sptr->frame_ts.have_pts) calc_ts = sptr->frame_ts.dts; + else frames_since_last = sptr->frames_since_last_ts + 1; + + if (freq_ts != NULL) { + freq_conv = calc_ts - base_dts; + freq_conv *= sptr->freq; + freq_conv /= 90000; + freq_conv += frames_since_last * sptr->samples_per_frame; + *freq_ts = (u32) (freq_conv & 0xffffffff); + } + return convert_ts(sptr, ts_type, calc_ts, base_dts, frames_since_last); +} + +/* + * mpeg2ps_get_video_frame - gets the next frame + */ +Bool mpeg2ps_get_video_frame(mpeg2ps_t *ps, u32 streamno, + u8 **buffer, + u32 *buflen, + u8 *frame_type, + mpeg2ps_ts_type_t ts_type, + u64 *timestamp) +{ + mpeg2ps_stream_t *sptr; + if (invalid_video_streamno(ps, streamno)) return 0; + + sptr = ps->video_streams[streamno]; + check_fd_for_stream(ps, sptr); + + if (sptr->have_frame_loaded == 0) { + // if we don't have the frame in the buffer (like after a seek), + // read the next frame + if (mpeg2ps_stream_find_mpeg_video_frame(sptr) == 0) { + return 0; + } + } + *buffer = sptr->pes_buffer + sptr->pes_buffer_on; + *buflen = sptr->frame_len; + // determine frame type + if (frame_type != NULL) { + *frame_type = MPEG12_PictHdrType(sptr->pes_buffer + + sptr->pict_header_offset); + } + + // and the timestamp + if (timestamp != NULL) { + *timestamp = stream_convert_frame_ts_to_msec(sptr, ts_type, + ps->first_dts, NULL); + } + + // finally, indicate that we read this frame - get ready for the next one. + advance_frame(sptr); + + return 1; +} + + +// see above comments +Bool mpeg2ps_get_audio_frame(mpeg2ps_t *ps, u32 streamno, + u8 **buffer, + u32 *buflen, + mpeg2ps_ts_type_t ts_type, + u32 *freq_timestamp, + u64 *timestamp) +{ + mpeg2ps_stream_t *sptr; + u64 ts; + if (invalid_audio_streamno(ps, streamno)) return 0; + + sptr = ps->audio_streams[streamno]; + check_fd_for_stream(ps, sptr); + + if (sptr->have_frame_loaded == 0) { + if (mpeg2ps_stream_read_frame(sptr, buffer, buflen, 0) == 0) + return 0; + } + + if (timestamp != NULL || freq_timestamp != NULL) { + ts = stream_convert_frame_ts_to_msec(sptr, + ts_type, + ps->first_dts, + freq_timestamp); + if (timestamp != NULL) { + *timestamp = ts; + } + } + + advance_frame(sptr); + return 1; +} + +s64 mpeg2ps_get_ps_size(mpeg2ps_t *ps) +{ + return file_size(ps->fd); +} +s64 mpeg2ps_get_video_pos(mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_video_streamno(ps, streamno)) return 0; + return gf_f64_tell(ps->video_streams[streamno]->m_fd); +} +s64 mpeg2ps_get_audio_pos(mpeg2ps_t *ps, u32 streamno) +{ + if (invalid_audio_streamno(ps, streamno)) return 0; + return gf_f64_tell(ps->audio_streams[streamno]->m_fd); +} + +#endif + diff --git a/src/media_tools/mpeg2_ps.h b/src/media_tools/mpeg2_ps.h new file mode 100644 index 0000000..a1bd3bd --- /dev/null +++ b/src/media_tools/mpeg2_ps.h @@ -0,0 +1,204 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is MPEG4IP. + * + * The Initial Developer of the Original Code is Cisco Systems Inc. + * Portions created by Cisco Systems Inc. are + * Copyright (C) Cisco Systems Inc. 2004. All Rights Reserved. + * + * Contributor(s): + * Bill May wmay@cisco.com + */ + +/* + * mpeg2_ps.h - API for mpeg2ps library + */ +#ifndef __MPEG2_PS_H__ +#define __MPEG2_PS_H__ 1 + +#include + +#define MPEG2_PS_START 0x00000100 +#define MPEG2_PS_START_MASK 0xffffff00 +#define MPEG2_PS_PACKSTART 0x000001BA +#define MPEG2_PS_SYSSTART 0x000001BB +#define MPEG2_PS_END 0x000001B9 + + +#define MPEG12_START_CODE_PREFIX 0x000001 +#define MPEG12_PICTURE_START_CODE 0x00000100 +#define MPEG12_SLICE_MIN_START 0x00000101 +#define MPEG12_SLICE_MAX_START 0x000001af +#define MPEG12_USER_DATA_START_CODE 0x000001b2 +#define MPEG12_SEQUENCE_START_CODE 0x000001b3 +#define MPEG12_SEQUENCE_ERR_START_CODE 0x000001b4 +#define MPEG12_EXT_START_CODE 0x000001b5 +#define MPEG12_SEQUENCE_END_START_CODE 0x000001b7 +#define MPEG12_GOP_START_CODE 0x000001b8 + +typedef struct mpeg2ps_ mpeg2ps_t; + +typedef enum { + TS_MSEC, + TS_90000, +} mpeg2ps_ts_type_t; + +typedef enum { + MPEG_AUDIO_MPEG = 0, + MPEG_AUDIO_AC3 = 1, + MPEG_AUDIO_LPCM = 2, + MPEG_AUDIO_UNKNOWN = 3 +} mpeg2ps_audio_type_t; + +typedef enum { + MPEG_VIDEO_MPEG1 = 0, + MPEG_VIDEO_MPEG2 = 1, + MPEG_VIDEO_UNKNOWN = 2 +} mpeg2ps_video_type_t; + +#ifdef __cplusplus +extern "C" { +#endif + /* + * Interface routines for library. + */ + /* + * mpeg2ps_init() - use to start. It will scan file for all streams + * returns handle to use with rest of calls + */ + mpeg2ps_t *mpeg2ps_init(const char *filename); + + /* + * mpeg2ps_close - clean up - should be called after mpeg2ps_init + */ + void mpeg2ps_close(mpeg2ps_t *ps); + + /* + * mpeg2ps_get_max_time_msec - returns the max time of the longest stream + */ + u64 mpeg2ps_get_max_time_msec(mpeg2ps_t *ps); + /* + * video stream functions + */ + /* + * mpeg2ps_get_video_stream_count - returns count of video streams in file + */ + u32 mpeg2ps_get_video_stream_count(mpeg2ps_t *ps); + /* + * mpeg2ps_get_video_stream_name - returns display name for stream + */ + const char *mpeg2ps_get_video_stream_name(mpeg2ps_t *ps, + u32 streamno); + /* + * mpeg2ps_get_video_stream_type - returns enum type for stream + */ + mpeg2ps_video_type_t mpeg2ps_get_video_stream_type(mpeg2ps_t *ps, + u32 streamno); + /* + * these functions should be fairly self explanatory + */ + u32 mpeg2ps_get_video_stream_width(mpeg2ps_t *ps, u32 streamno); + u32 mpeg2ps_get_video_stream_height(mpeg2ps_t *ps, u32 streamno); + u32 mpeg2ps_get_video_stream_aspect_ratio(mpeg2ps_t *ps, u32 streamno); + double mpeg2ps_get_video_stream_bitrate(mpeg2ps_t *ps, u32 streamno); + double mpeg2ps_get_video_stream_framerate(mpeg2ps_t *ps, u32 streamno); + + /* + * mpeg2ps_get_video_frame - get the next video frame + * returns false at end of stream + * Inputs: + * ps - handle from above + * streamno - stream to read + * buffer - returns pointer to data. Do not free + * buflen - frame length will be stored + * frame_type - if pointer given, frame type I-1, P-2, B-3 will be returned + * msec_timestamp - if pointer, time in msec since start will be given (dts) + */ + Bool mpeg2ps_get_video_frame(mpeg2ps_t *ps, + u32 streamno, + u8 **buffer, + u32 *buflen, + u8 *frame_type, + mpeg2ps_ts_type_t ts_type, + u64 *timestamp); + Bool mpeg2ps_seek_video_frame(mpeg2ps_t *ps, u32 streamno, + u64 msec_timestamp); + + /* + * audio stream functions + */ + /* + * mpeg2ps_get_audio_stream_count - returns count of video streams in file + */ + u32 mpeg2ps_get_audio_stream_count(mpeg2ps_t *ps); + /* + * mpeg2ps_get_audio_stream_name - returns display name for stream + */ + const char *mpeg2ps_get_audio_stream_name(mpeg2ps_t *ps, u32 streamno); + /* + * mpeg2ps_get_audio_stream_type - returns enum type for stream + */ + mpeg2ps_audio_type_t mpeg2ps_get_audio_stream_type(mpeg2ps_t *ps, u32 streamno); + /* + * these functions should be fairly self explanatory + */ + u32 mpeg2ps_get_audio_stream_sample_freq(mpeg2ps_t *ps, u32 streamno); + u32 mpeg2ps_get_audio_stream_channels(mpeg2ps_t *ps, u32 streamno); + u32 mpeg2ps_get_audio_stream_bitrate(mpeg2ps_t *ps, u32 streamno); + + /* + * mpeg2ps_get_audio_frame - get the next audio frame + * returns false at end of stream + * Inputs: + * ps - handle from above + * streamno - stream to read + * buffer - returns pointer to data. Do not free + * buflen - frame length will be stored + * freq_timestamp - will return conversion of timestamp in units of + * sample_frequency. + * msec_timestamp - if pointer, time in msec since start will be given (dts) + */ + Bool mpeg2ps_get_audio_frame(mpeg2ps_t *ps, + u32 streamno, + u8 **buffer, + u32 *buflen, + mpeg2ps_ts_type_t ts_type, + u32 *freq_timestamp, + u64 *msec_timestamp); + Bool mpeg2ps_seek_audio_frame(mpeg2ps_t *ps, u32 streamno, + u64 msec_timestamp); + + + /* + * returns file size + */ + s64 mpeg2ps_get_ps_size(mpeg2ps_t *ps); + /* + * returns position in file + * ps - handle from above + * streamno - stream to read + */ + s64 mpeg2ps_get_video_pos(mpeg2ps_t *ps, u32 streamno); + s64 mpeg2ps_get_audio_pos(mpeg2ps_t *ps, u32 streamno); + +typedef void (*error_msg_func_t)(int loglevel, + const char *lib, + const char *fmt, + va_list ap); + + void mpeg2ps_set_loglevel(int loglevel); + void mpeg2ps_set_error_func(error_msg_func_t func); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/media_tools/mpegts.c b/src/media_tools/mpegts.c new file mode 100644 index 0000000..2c55da6 --- /dev/null +++ b/src/media_tools/mpegts.c @@ -0,0 +1,1611 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Walid B.H - Jean Le Feuvre + * Copyright (c)2006-200X ENST - All rights reserved + * + * This file is part of GPAC / MPEG2-TS sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include + +#define DEBUG_TS_PACKET 0 + +const char *gf_m2ts_get_stream_name(u32 streamType) +{ + switch (streamType) { + case GF_M2TS_VIDEO_MPEG1: return "MPEG-1 Video"; + case GF_M2TS_VIDEO_MPEG2: return "MPEG-2 Video"; + case GF_M2TS_AUDIO_MPEG1: return "MPEG-1 Audio"; + case GF_M2TS_AUDIO_MPEG2: return "MPEG-2 Audio"; + case GF_M2TS_PRIVATE_SECTION: return "Private Section"; + case GF_M2TS_PRIVATE_DATA: return "Private Data"; + case GF_M2TS_AUDIO_AAC: return "AAC Audio"; + case GF_M2TS_VIDEO_MPEG4: return "MPEG-4 Video"; + case GF_M2TS_VIDEO_H264: return "MPEG-4/H264 Video"; + + case GF_M2TS_AUDIO_AC3: return "Dolby AC3 Audio"; + case GF_M2TS_AUDIO_DTS: return "Dolby DTS Audio"; + case GF_M2TS_SUBTITLE_DVB: return "DVB Subtitle"; + case GF_M2TS_SYSTEMS_MPEG4_PES: return "MPEG-4 SL (PES)"; + case GF_M2TS_SYSTEMS_MPEG4_SECTIONS: return "MPEG-4 SL (Section)"; + default: return "Unknown"; + } +} + +static void gf_m2ts_reframe_default(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +{ + GF_M2TS_PES_PCK pck; + pck.flags = 0; + if (pes->rap) pck.flags |= GF_M2TS_PES_PCK_RAP; + + if (PTS) { + pes->PTS = PTS; + /*backup DTS for start detection*/ + PTS = pes->DTS; + if (DTS) pes->DTS = DTS; + else pes->DTS = PTS; + if (!PTS || (PTS != pes->DTS)) pck.flags = GF_M2TS_PES_PCK_AU_START; + } + pck.DTS = pes->DTS; + pck.PTS = pes->PTS; + pck.data = data; + pck.data_len = data_len; + pck.stream = pes; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); +} + + +static void gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +{ + Bool start_code_found = 0; + u32 nal_type, sc_pos = 0; + GF_M2TS_PES_PCK pck; + + if (PTS) { + pes->PTS = PTS; + if (DTS) pes->DTS = DTS; + else pes->DTS = PTS; + } + + /*dispatch frame*/ + pck.stream = pes; + pck.DTS = pes->DTS; + pck.PTS = pes->PTS; + pck.flags = 0; + + while (sc_poson_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + data += sc_pos; + data_len -= sc_pos; + sc_pos = 0; + } + start_code_found = 1; + } else { + pck.data = data; + pck.data_len = sc_pos; + + nal_type = pck.data[4] & 0x1F; + + /*check for SPS and update stream info*/ + if (!pes->vid_w && (nal_type==GF_AVC_NALU_SEQ_PARAM)) { + AVCState avc; + s32 idx; + GF_BitStream *bs = gf_bs_new(data+5, sc_pos-5, GF_BITSTREAM_READ); + memset(&avc, 0, sizeof(AVCState)); + idx = AVC_ReadSeqInfo(bs, &avc, NULL); + gf_bs_del(bs); + + if (idx>=0) { + pes->vid_w = avc.sps[idx].width; + pes->vid_h = avc.sps[idx].height; + } + } + + /*check AU start type*/ + if (nal_type==GF_AVC_NALU_ACCESS_UNIT) pck.flags = GF_M2TS_PES_PCK_AU_START; + else pck.flags = 0; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + + data += sc_pos; + data_len -= sc_pos; + sc_pos = 0; + } + sc_pos++; + } + if (data_len) { + pck.flags = 0; + if (start_code_found) { + pck.data = data; + pck.data_len = data_len; + nal_type = pck.data[4] & 0x1F; + if (nal_type==GF_AVC_NALU_ACCESS_UNIT) pck.flags = GF_M2TS_PES_PCK_AU_START; + } else { + pck.data = data; + pck.data_len = data_len; + } + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + } +} + +void gf_m2ts_reframe_mpeg_video(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +{ + u32 sc_pos = 0; + u32 to_send = data_len; + GF_M2TS_PES_PCK pck; + + if (PTS) { + pes->PTS = PTS; + if (DTS) pes->DTS = DTS; + else pes->DTS = PTS; + } + /*dispatch frame*/ + pck.stream = pes; + pck.DTS = pes->DTS; + pck.PTS = pes->PTS; + pck.flags = 0; + + while (sc_pos+4on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + pck.flags = 0; + data += sc_pos; + data_len -= sc_pos; + to_send -= sc_pos; + sc_pos = 0; + } + new_au = 1; + /*if prev was GOP/SEQ start, this is not a new AU*/ + if (pes->frame_state) + new_au = 0; + pes->frame_state = data[3]; + if (new_au) { + pck.flags = GF_M2TS_PES_PCK_AU_START; + if (pes->rap) + pck.flags |= GF_M2TS_PES_PCK_RAP; + } + + if (!pes->vid_h && (pes->frame_state==0xb3)) { + u32 den, num; + unsigned char *p = data+4; + pes->vid_w = (p[0] << 4) | ((p[1] >> 4) & 0xf); + pes->vid_h = ((p[1] & 0xf) << 8) | p[2]; + pes->vid_par = (p[3] >> 4) & 0xf; + + switch (pes->vid_par) { + default: pes->vid_par = 0; break; + case 2: num = 4; den = 3; break; + case 3: num = 16; den = 9; break; + case 4: num = 221; den = 100; break; + } + if (den) + pes->vid_par = ((pes->vid_h/den)<<16) | (pes->vid_w/num); break; + } + if (pes->frame_state==0x00) { + switch ((data[5] >> 3) & 0x7) { + case 1: pck.flags |= GF_M2TS_PES_PCK_I_FRAME; break; + case 2: pck.flags |= GF_M2TS_PES_PCK_P_FRAME; break; + case 3: pck.flags |= GF_M2TS_PES_PCK_B_FRAME; break; + } + } + } + sc_pos+=3; + } + sc_pos++; + } + pck.data = data; + pck.data_len = data_len; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); +} + +static u32 latm_get_value(GF_BitStream *bs) +{ + u32 i, tmp, value = 0; + u32 bytesForValue = gf_bs_read_int(bs, 2); + for (i=0; i <= bytesForValue; i++) { + value <<= 8; + tmp = gf_bs_read_int(bs, 8); + value += tmp; + } + return value; +} + +void gf_m2ts_reframe_aac_latm(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +{ + u32 sc_pos = 0; + u32 start = 0; + GF_M2TS_PES_PCK pck; + + if (PTS) { + pes->PTS = PTS; + if (DTS) pes->DTS = DTS; + else pes->DTS = PTS; + } + /*dispatch frame*/ + pck.stream = pes; + pck.DTS = pes->DTS; + pck.PTS = pes->PTS; + pck.flags = 0; + + while (sc_pos+2aud_sr) { + pck.stream = pes; + gf_m4a_write_config(&cfg, &pck.data, &pck.data_len); + ts->on_event(ts, GF_M2TS_EVT_AAC_CFG, &pck); + free(pck.data); + pes->aud_sr = cfg.base_sr; + pes->aud_nb_ch = cfg.nb_chan; + } + } + frameLengthType = gf_bs_read_int(bs, 3); + if (!frameLengthType) { + latmBufferFullness = gf_bs_read_int(bs, 8); + if (!allStreamsSameTimeFraming) { + } + } else { + /*not supported*/ + } + } + + } + /*other data present*/ + if (gf_bs_read_int(bs, 1)) { +// u32 k = 0; + } + } + + + } + + /*we have a cfg, read data - we only handle single stream multiplex in LATM/LOAS*/ + if (pes->aud_sr) { + size = 0; + while (1) { + u32 tmp = gf_bs_read_int(bs, 8); + size += tmp; + if (tmp!=255) break; + } + if (size>pes->buf_len) { + pes->buf_len = size; + pes->buf = realloc(pes->buf, sizeof(char)*pes->buf_len); + } + gf_bs_read_data(bs, pes->buf, size); + + /*dispatch frame*/ + pck.stream = pes; + pck.DTS = pes->PTS; + pck.PTS = pes->PTS; + pck.flags = GF_M2TS_PES_PCK_AU_START | GF_M2TS_PES_PCK_RAP; + pck.data = pes->buf; + pck.data_len = size; + + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + + /*update PTS in case we don't get any update*/ + size = 1024*90000/pes->aud_sr; + pes->PTS += size; + } + gf_bs_del(bs); + + /*parse amux*/ + sc_pos += amux_len+3; + start = sc_pos; + + } +} + +static void gf_m2ts_reframe_mpeg_audio(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +{ + GF_M2TS_PES_PCK pck; + u32 pos, frame_size, remain; + + pck.flags = GF_M2TS_PES_PCK_RAP; + pck.stream = pes; + remain = pes->frame_state; + + pes->frame_state = gf_mp3_get_next_header_mem(data, data_len, &pos); + if (!pes->frame_state) { + if (remain) { + /*dispatch end of prev frame*/ + pck.DTS = pck.PTS = pes->PTS; + pck.data = data; + pck.data_len = (remain>data_len) ? data_len : remain; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + if (remain>data_len) pes->frame_state = remain - data_len; + } + return; + } + assert((pes->frame_state & 0xffe00000) == 0xffe00000); + /*resync*/ + if (pos) { + if (remain) { + /*sync error!!*/ + if (remain>pos) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] sync error - start code @ %d - remaining from last frame %d\n", pos, remain) ); + remain = pos; + } + /*dispatch end of prev frame*/ + pck.DTS = pck.PTS = pes->PTS; + pck.data = data; + pck.data_len = remain; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + } + data += pos; + data_len -= pos; + } + if (!pes->PTS) { + pes->aud_sr = gf_mp3_sampling_rate(pes->frame_state); + pes->aud_nb_ch = gf_mp3_num_channels(pes->frame_state); + } + /*we may get a PTS for aither the previous or the current frame*/ + if (PTS>=pes->PTS) pes->PTS = PTS; + pck.flags = GF_M2TS_PES_PCK_RAP | GF_M2TS_PES_PCK_AU_START; + + frame_size = gf_mp3_frame_size(pes->frame_state); + while (frame_size <= data_len) { + /*dispatch frame*/ + pck.DTS = pck.PTS = pes->PTS; + pck.data = data; + pck.data_len = frame_size; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + + pes->PTS += gf_mp3_window_size(pes->frame_state)*90000/gf_mp3_sampling_rate(pes->frame_state); + /*move frame*/ + data += frame_size; + data_len -= frame_size; + if (!data_len) break; + pes->frame_state = gf_mp3_get_next_header_mem(data, data_len, &pos); + /*resync (ID3 or error)*/ + if (!pes->frame_state) { + data_len = 0; + break; + } + /*resync (ID3 or error)*/ + if (pos) { + data_len -= pos; + data += pos; + } + frame_size = gf_mp3_frame_size(pes->frame_state); + } + if (data_len) { + pck.DTS = pck.PTS = pes->PTS; + pck.data = data; + pck.data_len = data_len; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + /*update PTS in case we don't get any update*/ + pes->PTS += gf_mp3_window_size(pes->frame_state)*90000/gf_mp3_sampling_rate(pes->frame_state); + pes->frame_state = frame_size - data_len; + } else { + pes->frame_state = 0; + } +} + +static u32 gf_m2ts_sync(GF_M2TS_Demuxer *ts, Bool simple_check) +{ + u32 i=0; + /*if first byte is sync assume we're sync*/ + if (simple_check && (ts->buffer[i]==0x47)) return 0; + + while (ibuffer_size) { + if (i+188>ts->buffer_size) return ts->buffer_size; + if ((ts->buffer[i]==0x47) && (ts->buffer[i+188]==0x47)) break; + i++; + } + if (i) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] re-sync skipped %d bytes\n", i) ); + } + return i; +} + +Bool gf_m2ts_crc32_check(char *data, u32 len) +{ + u32 crc = gf_crc_32(data, len); + u32 crc_val = GF_4CC((u8) data[len], (u8) data[len+1], (u8) data[len+2], (u8) data[len+3]); + return (crc==crc_val) ? 1 : 0; +} + + +static GF_M2TS_SectionFilter *gf_m2ts_section_filter_new(gf_m2ts_section_callback process_section_callback, Bool process_individual) +{ + GF_M2TS_SectionFilter *sec; + GF_SAFEALLOC(sec, GF_M2TS_SectionFilter); + sec->cc = -1; + sec->process_section = process_section_callback; + sec->process_individual = process_individual; + return sec; +} + +static void gf_m2ts_reset_sections(GF_List *sections) +{ + u32 count; + GF_M2TS_Section *section; + + count = gf_list_count(sections); + while (count) { + section = gf_list_get(sections, 0); + gf_list_rem(sections, 0); + if (section->data) free(section->data); + free(section); + count--; + } +} + +static void gf_m2ts_section_filter_del(GF_M2TS_SectionFilter *sf) +{ + if (sf->section) free(sf->section); + while (sf->table) { + GF_M2TS_Table *t = sf->table; + sf->table = t->next; + gf_m2ts_reset_sections(t->sections); + gf_list_del(t->sections); + free(t); + } + free(sf); +} + +void gf_m2ts_es_del(GF_M2TS_ES *es) +{ + gf_list_del_item(es->program->streams, es); + if (es->flags & GF_M2TS_ES_IS_SECTION) { + GF_M2TS_SECTION_ES *ses = (GF_M2TS_SECTION_ES *)es; + if (ses->sec) gf_m2ts_section_filter_del(ses->sec); + } else if (es->pid!=es->program->pmt_pid) { + GF_M2TS_PES *pes = (GF_M2TS_PES *)es; + if (pes->data) free(pes->data); + if (pes->buf) free(pes->buf); + } + free(es); +} + +static void gf_m2ts_reset_sdt(GF_M2TS_Demuxer *ts) +{ + while (gf_list_count(ts->SDTs)) { + GF_M2TS_SDT *sdt = (GF_M2TS_SDT *)gf_list_last(ts->SDTs); + gf_list_rem_last(ts->SDTs); + if (sdt->provider) free(sdt->provider); + if (sdt->service) free(sdt->service); + free(sdt); + } +} + +static void gf_m2ts_section_complete(GF_M2TS_Demuxer *ts, GF_M2TS_SectionFilter *sec, GF_M2TS_SECTION_ES *ses) +{ + if (! sec->process_section && !sec->had_error) { + if (ts->on_event) { + GF_M2TS_SL_PCK pck; + pck.data_len = sec->length; + pck.data = (unsigned char*)malloc(sizeof(unsigned char)*pck.data_len); + memcpy(pck.data, sec->section, sizeof(unsigned char)*pck.data_len); + //pck.data[pck.data_len] = 0; + pck.stream = (GF_M2TS_ES *)ses; + ts->on_event(ts, GF_M2TS_EVT_DVB_GENERAL, &pck); + free(pck.data); + } + } else { + Bool has_syntax_indicator; + u8 table_id; + u16 extended_table_id; + u32 status, section_start; + GF_M2TS_Table *t, *prev_t; + unsigned char *data; + Bool section_valid = 0; + + status = 0; + /*parse header*/ + data = sec->section; + + /*look for proper table*/ + table_id = data[0]; + + if ((table_id == GF_M2TS_TABLE_ID_PMT || table_id == GF_M2TS_TABLE_ID_NIT_ACTUAL) && ts->on_event) { + GF_M2TS_SL_PCK pck; + pck.data_len = sec->length; + pck.data = (unsigned char*)malloc(sizeof(unsigned char)*pck.data_len); + memcpy(pck.data, sec->section, sizeof(unsigned char)*pck.data_len); + //pck.data[pck.data_len] = 0; + pck.stream = (GF_M2TS_ES *)ses; + ts->on_event(ts, GF_M2TS_EVT_DVB_GENERAL, &pck); + free(pck.data); + } + + has_syntax_indicator = (data[1] & 0x80) ? 1 : 0; + if (has_syntax_indicator) { + extended_table_id = (data[3]<<8) | data[4]; + } else { + extended_table_id = 0; + } + + prev_t = NULL; + t = sec->table; + while (t) { + if ((t->table_id==table_id) && (t->ex_table_id == extended_table_id)) break; + prev_t = t; + t = t->next; + } + + /*create table*/ + if (!t) { + GF_SAFEALLOC(t, GF_M2TS_Table); + t->table_id = table_id; + t->ex_table_id = extended_table_id; + t->sections = gf_list_new(); + if (prev_t) prev_t->next = t; + else sec->table = t; + } + + if (has_syntax_indicator) { + /*remove crc32*/ + sec->length -= 4; + if (gf_m2ts_crc32_check(data, sec->length)) { + s32 cur_sec_num; + t->version_number = (data[5] >> 1) & 0x1f; + t->current_next_indicator = (data[5] & 0x1) ? 1 : 0; + /*add one to section numbers to detect if we missed or not the first section in the table*/ + cur_sec_num = data[6] + 1; + t->last_section_number = data[7] + 1; + section_start = 8; + section_valid = 1; + /*we missed something*/ + if (!sec->process_individual && t->section_number + 1 != cur_sec_num) { + /* TODO - Check how to handle sections when the first complete section does + not have its sec num 0 */ + section_valid = 0; + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] corrupted table (lost section %d)\n", cur_sec_num ? cur_sec_num-1 : 31) ); + } + t->section_number = cur_sec_num; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] corrupted section (CRC32 failed)\n")); + } + } else if (!sec->had_error) { + section_valid = 1; + section_start = 3; + } + /*process section*/ + if (section_valid) { + GF_M2TS_Section *section; + + GF_SAFEALLOC(section, GF_M2TS_Section); + section->data_size = sec->length - section_start; + section->data = (unsigned char*)malloc(sizeof(unsigned char)*section->data_size); + memcpy(section->data, sec->section + section_start, sizeof(unsigned char)*section->data_size); + gf_list_add(t->sections, section); + + if (t->section_number == 1) status |= GF_M2TS_TABLE_START; + + if (t->is_init) { + if (t->last_version_number == t->version_number) { + status |= GF_M2TS_TABLE_REPEAT; + } else { + status |= GF_M2TS_TABLE_UPDATE; + } + } else { + status |= GF_M2TS_TABLE_FOUND; + } + + t->last_version_number = t->version_number; + + if (t->last_section_number == t->section_number) { + status |= GF_M2TS_TABLE_END; + t->is_init = 1; + /*reset section number*/ + t->section_number = 0; + } + + if (sec->process_individual) { + /*send each section of the table and not the aggregated table*/ + sec->process_section(ts, ses, t->sections, t->table_id, t->ex_table_id, t->version_number, (u8) (t->last_section_number - 1), status); + gf_m2ts_reset_sections(t->sections); + } else { + if (status&GF_M2TS_TABLE_END) { + sec->process_section(ts, ses, t->sections, t->table_id, t->ex_table_id, t->version_number, (u8) (t->last_section_number - 1), status); + gf_m2ts_reset_sections(t->sections); + } + } + + } else { + sec->cc = -1; + t->section_number = 0; + } + } + + /*clean-up (including broken sections)*/ + if (sec->section) free(sec->section); + sec->section = NULL; + sec->length = sec->received = 0; +} + +static Bool gf_m2ts_is_long_section(u8 table_id) +{ + switch (table_id) { + case GF_M2TS_TABLE_ID_MPEG4_BIFS: + case GF_M2TS_TABLE_ID_MPEG4_OD: + case GF_M2TS_TABLE_ID_EIT_ACTUAL_PF: + case GF_M2TS_TABLE_ID_EIT_OTHER_PF: + case GF_M2TS_TABLE_ID_ST: + case GF_M2TS_TABLE_ID_SIT: + return 1; + default: + if (table_id >= GF_M2TS_TABLE_ID_EIT_SCHEDULE_MIN && table_id <= GF_M2TS_TABLE_ID_EIT_SCHEDULE_MAX) + return 1; + else + return 0; + } +} + +static void gf_m2ts_gather_section(GF_M2TS_Demuxer *ts, GF_M2TS_SectionFilter *sec, GF_M2TS_SECTION_ES *ses, GF_M2TS_Header *hdr, unsigned char *data, u32 data_size) +{ + u32 payload_size = data_size; + u8 expect_cc = (sec->cc<0) ? hdr->continuity_counter : (sec->cc + 1) & 0xf; + Bool disc = (expect_cc == hdr->continuity_counter) ? 0 : 1; + sec->cc = expect_cc; + + if (hdr->error || + (hdr->adaptation_field==2)) /* 2 = no payload in TS packet */ + return; + + if (hdr->payload_start) { + u32 ptr_field; + + ptr_field = data[0]; + if (ptr_field+1>data_size) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid section start (@ptr_field=%d, @data_size=%d)\n", ptr_field, data_size) ); + return; + } + + /*end of previous section*/ + if (sec->length && sec->received + ptr_field >= sec->length) { + memcpy(sec->section + sec->received, data, sizeof(char)*ptr_field); + sec->received += ptr_field; + gf_m2ts_section_complete(ts, sec, ses); + } + data += ptr_field+1; + data_size -= ptr_field+1; + payload_size -= ptr_field+1; + +aggregated_section: + + if (sec->section) free(sec->section); + sec->length = sec->received = 0; + sec->section = (char*)malloc(sizeof(char)*data_size); + memcpy(sec->section, data, sizeof(char)*data_size); + sec->received = data_size; + sec->had_error = 0; + } else if (disc || hdr->error) { + if (sec->section) free(sec->section); + sec->section = NULL; + sec->received = sec->length = 0; + return; + } else if (!sec->section) { + return; + } else { + if (sec->received+data_size > sec->length) + data_size = sec->length - sec->received; + + if (sec->length) { + memcpy(sec->section + sec->received, data, sizeof(char)*data_size); + } else { + sec->section = (char*)realloc(sec->section, sizeof(char)*(sec->received+data_size)); + memcpy(sec->section + sec->received, data, sizeof(char)*data_size); + } + sec->received += data_size; + } + if (hdr->error) + sec->had_error = 1; + + /*alloc final buffer*/ + if (!sec->length && (sec->received >= 3)) { + if (gf_m2ts_is_long_section(sec->section[0])) { + sec->length = 3 + ( ((sec->section[1]<<8) | (sec->section[2]&0xff)) & 0xfff ); + } else { + sec->length = 3 + ( ((sec->section[1]<<8) | (sec->section[2]&0xff)) & 0x3ff ); + } + sec->section = (char*)realloc(sec->section, sizeof(char)*sec->length); + + if (sec->received > sec->length) { + data_size -= sec->received - sec->length; + sec->received = sec->length; + } + } + if (sec->received < sec->length) return; + + /*OK done*/ + gf_m2ts_section_complete(ts, sec, ses); + + if (payload_size > data_size) { + data += data_size; + /* detect padding after previous section */ + if (data[0] != 0xFF) { + data_size = payload_size - data_size; + payload_size = data_size; + goto aggregated_section; + } + } +} + +static void gf_m2ts_process_mpeg4section(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *es, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status) +{ + GF_M2TS_SL_PCK sl_pck; + u32 nb_sections, i; + GF_M2TS_Section *section; + + /*skip if already received*/ + if (status & GF_M2TS_TABLE_REPEAT) + return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Sections for PID %d\n", es->pid) ); + /*send all sections (eg SL-packets)*/ + nb_sections = gf_list_count(sections); + for (i=0; idata; + sl_pck.data_len = section->data_size; + sl_pck.stream = (GF_M2TS_ES *)es; + if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_SL_PCK, &sl_pck); + } +} + +GF_EXPORT +void gf_m2ts_decode_mjd_date(u32 date, u32 *year, u32 *month, u32 *day) +{ + u32 yp, mp, k; + yp = (u32)floor((date - 15078.2)/365.25); + mp = (u32)floor((date - 14956.1 - floor(yp * 365.25))/30.6001); + *day = (u32)(date - 14956 - floor(yp * 365.25) - floor(mp * 30.6001)); + if (mp == 14 || mp == 15) k = 1; + else k = 0; + *year = yp + k + 1900; + *month = mp - 1 - k*12; +} + +static void gf_m2ts_process_int(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *ip_not_table, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status) +{ + fprintf(stdout, "Processing IP/MAC Notification table (PID %d) %s\n", ip_not_table->pid, (status&GF_M2TS_TABLE_REPEAT)?"repeated":""); +} + +#if 0 +static void gf_m2ts_process_mpe(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *mpe, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status) +{ + fprintf(stdout, "Processing MPE Datagram (PID %d)\n", mpe->pid); +} +#endif + +static void gf_m2ts_process_nit(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *nit_es, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status) +{ + + +} + +static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status) +{ + u32 info_length, pos, desc_len, evt_type, nb_es; + u32 nb_sections; + u32 data_size; + unsigned char *data; + GF_M2TS_Section *section; + + /*wait for the last section */ + if (!(status&GF_M2TS_TABLE_END)) return; + + nb_es = 0; + + /*skip if already received*/ + if (status&GF_M2TS_TABLE_REPEAT) { + if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PMT_REPEAT, pmt->program); + return; + } + + nb_sections = gf_list_count(sections); + if (nb_sections > 1) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("PMT on multiple sections not supported\n")); + } + + section = (GF_M2TS_Section *)gf_list_get(sections, 0); + data = section->data; + data_size = section->data_size; + + pmt->program->pcr_pid = ((data[0] & 0x1f) << 8) | data[1]; + + info_length = ((data[2]&0xf)<<8) | data[3]; + if (info_length != 0) { + /* ...Read Descriptors ... */ + u8 tag, len; + u32 first_loop_len = 0; + tag = data[4]; + len = data[5]; + while (info_length > first_loop_len) { + if (tag == GF_M2TS_MPEG4_IOD_DESCRIPTOR) { + u8 scope, label; + u32 size; + GF_BitStream *iod_bs; + scope = data[6]; + label = data[7]; + iod_bs = gf_bs_new(data+8, len-2, GF_BITSTREAM_READ); + if (pmt->program->pmt_iod) gf_odf_desc_del((GF_Descriptor *)pmt->program->pmt_iod); + gf_odf_parse_descriptor(iod_bs , (GF_Descriptor **) &pmt->program->pmt_iod, &size); + gf_bs_del(iod_bs ); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Skipping descriptor (0x%x) and others not supported\n", tag)); + } + first_loop_len += 2 + len; + } + } + if (data_size <= 4 + info_length) return; + data += 4 + info_length; + data_size -= 4 + info_length; + pos = 0; + + while (posflags |= GF_M2TS_ES_IS_SECTION; + /* carriage of ISO_IEC_14496 data in sections */ + if (stream_type == GF_M2TS_SYSTEMS_MPEG4_SECTIONS) { + /*MPEG-4 sections need to be fully checked: if one section is lost, this means we lost + one SL packet in the AU so we must wait for the complete section again*/ + ses->sec = gf_m2ts_section_filter_new(gf_m2ts_process_mpeg4section, 0); + /*create OD container*/ + if (!pmt->program->additional_ods) { + pmt->program->additional_ods = gf_list_new(); + ts->has_4on2 = 1; + } + } + break; + + case GF_M2TS_PRIVATE_SECTION: + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Stream type (0x%x) for PID %d not supported\n", stream_type, pid ) ); + break; + } + + if (es) { + es->stream_type = stream_type; + es->program = pmt->program; + es->pid = pid; + } + + pos += 5; + data += 5; + + while (desc_len) { + u8 tag = data[0]; + u32 len = data[1]; + if (es) { + switch (tag) { + case GF_M2TS_ISO_639_LANGUAGE_DESCRIPTOR: + pes->lang = GF_4CC(' ', data[2], data[3], data[4]); + break; + case GF_M2TS_MPEG4_SL_DESCRIPTOR: + es->mpeg4_es_id = ((data[2] & 0x1f) << 8) | data[3]; + es->flags |= GF_M2TS_ES_IS_SL; + break; + case GF_M2TS_DVB_DATA_BROADCAST_ID_DESCRIPTOR: + { + u32 id = data[2]<<8 | data[3]; + if (id == 0xB) { + ses->sec = gf_m2ts_section_filter_new(NULL, 1); + gf_list_add(ts->ip_mac_not_tables, es); + } + } + break; + case GF_M2TS_DVB_SUBTITLING_DESCRIPTOR: + { + pes->sub.language[0] = data[2]; + pes->sub.language[1] = data[3]; + pes->sub.language[2] = data[4]; + pes->sub.type = data[5]; + pes->sub.composition_page_id = (data[6]<<8) | data[7]; + pes->sub.ancillary_page_id = (data[8]<<8) | data[9]; + } + break; + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] skipping descriptor (0x%x) not supported\n", tag)); + break; + } + } + + data += len+2; + pos += len+2; + if (desc_len < len+2) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PMT es descriptor size for PID %d\n", pes->pid ) ); + break; + } + desc_len-=len+2; + } + + if (!es) continue; + + /*watchout for pmt update*/ + if (/*(status==GF_M2TS_TABLE_UPDATE) && */ts->ess[pid]) { + GF_M2TS_ES *o_es = ts->ess[es->pid]; + + if ((o_es->stream_type == es->stream_type) + && ((o_es->flags & GF_M2TS_ES_STATIC_FLAGS_MASK) == (es->flags & GF_M2TS_ES_STATIC_FLAGS_MASK)) + && (o_es->mpeg4_es_id == es->mpeg4_es_id) + && ((o_es->flags & GF_M2TS_ES_IS_SECTION) || ((GF_M2TS_PES *)o_es)->lang == ((GF_M2TS_PES *)es)->lang) + ) { + free(es); + es = NULL; + + } + } + + if (es) { + es->stream_type = stream_type; + es->program = pmt->program; + es->pid = pid; + ts->ess[es->pid] = es; + gf_list_add(pmt->program->streams, es); + + if (!(es->flags & GF_M2TS_ES_IS_SECTION) ) gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_DEFAULT); + + nb_es++; + } + } + if (nb_es) { + evt_type = (status&GF_M2TS_TABLE_FOUND) ? GF_M2TS_EVT_PMT_FOUND : GF_M2TS_EVT_PMT_UPDATE; + if (ts->on_event) ts->on_event(ts, evt_type, pmt->program); + } +} + + +static void gf_m2ts_process_pat(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *ses, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status) +{ + GF_M2TS_Program *prog; + GF_M2TS_SECTION_ES *pmt; + u32 i, nb_progs, evt_type; + u32 nb_sections; + u32 data_size; + unsigned char *data; + GF_M2TS_Section *section; + + /*wait for the last section */ + if (!(status&GF_M2TS_TABLE_END)) return; + + /*skip if already received*/ + if (status&GF_M2TS_TABLE_REPEAT) { + if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PAT_REPEAT, NULL); + return; + } + + nb_sections = gf_list_count(sections); + if (nb_sections > 1) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("PMT on multiple sections not supported\n")); + } + + section = (GF_M2TS_Section *)gf_list_get(sections, 0); + data = section->data; + data_size = section->data_size; + + nb_progs = data_size / 4; + + for (i=0; init) { + ts->nit = gf_m2ts_section_filter_new(gf_m2ts_process_nit, 0); + } + } else { + GF_SAFEALLOC(prog, GF_M2TS_Program); + prog->streams = gf_list_new(); + prog->pmt_pid = pid; + prog->number = number; + gf_list_add(ts->programs, prog); + GF_SAFEALLOC(pmt, GF_M2TS_SECTION_ES); + pmt->flags = GF_M2TS_ES_IS_SECTION; + gf_list_add(prog->streams, pmt); + pmt->pid = prog->pmt_pid; + pmt->program = prog; + ts->ess[pmt->pid] = (GF_M2TS_ES *)pmt; + pmt->sec = gf_m2ts_section_filter_new(gf_m2ts_process_pmt, 0); + } + } + + evt_type = (status&GF_M2TS_TABLE_UPDATE) ? GF_M2TS_EVT_PAT_UPDATE : GF_M2TS_EVT_PAT_FOUND; + if (ts->on_event) ts->on_event(ts, evt_type, NULL); +} + + +static GFINLINE u64 gf_m2ts_get_pts(unsigned char *data) +{ + u64 pts; + u32 val; + pts = (u64)((data[0] >> 1) & 0x07) << 30; + val = (data[1] << 8) | data[2]; + pts |= (u64)(val >> 1) << 15; + val = (data[3] << 8) | data[4]; + pts |= (u64)(val >> 1); + return pts; +} + +static void gf_m2ts_pes_header(GF_M2TS_PES *pes, unsigned char *data, u32 data_size, GF_M2TS_PESHeader *pesh) +{ + u32 has_pts, has_dts, te; + u32 len_check; + memset(pesh, 0, sizeof(GF_M2TS_PESHeader)); + + len_check = 0; + + pesh->id = data[0]; + pesh->pck_len = (data[1]<<8) | data[2]; +/* + 2bits + scrambling_control = gf_bs_read_int(bs,2); + priority = gf_bs_read_int(bs,1); +*/ + pesh->data_alignment = (data[3] & 0x4) ? 1 : 0; +/* + copyright = gf_bs_read_int(bs,1); + original = gf_bs_read_int(bs,1); +*/ + te = data[4]; + has_pts = (data[4]&0x80); + has_dts = has_pts ? (data[4]&0x40) : 0; +/* + ESCR_flag = gf_bs_read_int(bs,1); + ES_rate_flag = gf_bs_read_int(bs,1); + DSM_flag = gf_bs_read_int(bs,1); + additional_copy_flag = gf_bs_read_int(bs,1); + prev_crc_flag = gf_bs_read_int(bs,1); + extension_flag = gf_bs_read_int(bs,1); +*/ + + pesh->hdr_data_len = data[5]; + + data += 6; + if (has_pts) { + pesh->PTS = gf_m2ts_get_pts(data); + data+=5; + len_check += 5; + } + if (has_dts) { + pesh->DTS = gf_m2ts_get_pts(data); + data+=5; + len_check += 5; + } + if (len_check < pesh->hdr_data_len) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d Skipping %d bytes in pes header\n", pes->pid, pesh->hdr_data_len - len_check)); + } else if (len_check > pesh->hdr_data_len) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d Wrong pes_header_data_length field %d bytes - read %d\n", pes->pid, pesh->hdr_data_len, len_check)); + } +} + + +static void gf_m2ts_process_pes(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, GF_M2TS_Header *hdr, unsigned char *data, u32 data_size, GF_M2TS_AdaptationField *paf) +{ + Bool flush_pes = 0; + + if (hdr->error) { + if (pes->data) { + free(pes->data); + pes->data = NULL; + } + pes->data_len = 0; + pes->pes_len = 0; + return; + } + if (!pes->reframe) return; + + if (hdr->payload_start) { + flush_pes = 1; + } else if (pes->pes_len && (pes->data_len + data_size == pes->pes_len + 6)) { + /* 6 = startcode+stream_id+length*/ + /*reassemble pes*/ + if (pes->data) pes->data = (char*)realloc(pes->data, pes->data_len+data_size); + else pes->data = (char*)malloc(data_size); + memcpy(pes->data+pes->data_len, data, data_size); + pes->data_len += data_size; + /*force discard*/ + data_size = 0; + flush_pes = 1; + } + + /*PES first fragment: flush previous packet*/ + if (flush_pes && pes->data) { + GF_M2TS_PESHeader pesh; + if (pes->pid==25) + pes->pid=25; + + /*we need at least a full, valid start code !!*/ + if ((pes->data_len >= 4) && !pes->data[0] && !pes->data[1] && (pes->data[2]==0x1)) { + u32 len; + u32 stream_id = pes->data[3] | 0x100; + if ((stream_id >= 0x1c0 && stream_id <= 0x1df) || + (stream_id >= 0x1e0 && stream_id <= 0x1ef) || + (stream_id == 0x1bd)) { + + /*OK read header*/ + gf_m2ts_pes_header(pes, pes->data+3, pes->data_len-3, &pesh); + + /*3-byte start-code + 6 bytes header + hdr extensions*/ + len = 9 + pesh.hdr_data_len; + pes->reframe(ts, pes, pesh.DTS, pesh.PTS, pes->data+len, pes->data_len-len); + } + /*SL-packetized stream*/ + else if ((u8) pes->data[3]==0xfa) { + GF_M2TS_SL_PCK sl_pck; + if (pes->pid==25) + pes->pid=25; + /*read header*/ + gf_m2ts_pes_header(pes, pes->data+3, pes->data_len-3, &pesh); + + /*3-byte start-code + 6 bytes header + hdr extensions*/ + len = 9 + pesh.hdr_data_len; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] SL Packet in PES for %d - ES ID %d\n", pes->pid, pes->mpeg4_es_id)); + if (pes->data_len > len) { + sl_pck.data = pes->data + len; + sl_pck.data_len = pes->data_len - len; + sl_pck.stream = (GF_M2TS_ES *)pes; + if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_SL_PCK, &sl_pck); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Bad SL Packet size: (%d indicated < %d header)\n", pes->pid, pes->data_len, len)); + } + } + } + if (pes->data) { + free(pes->data); + pes->data = NULL; + pes->data_len = 0; + pes->pes_len = 0; + } + pes->rap = 0; + if (!data_size) return; + } + + /*reassemble*/ + if (pes->data) pes->data = (char*)realloc(pes->data, pes->data_len+data_size); + else pes->data = (char*)malloc(data_size); + memcpy(pes->data+pes->data_len, data, data_size); + pes->data_len += data_size; + + if (paf && paf->random_access_indicator) pes->rap = 1; + if (!pes->pes_len && (pes->data_len>=6)) pes->pes_len = (pes->data[4]<<8) | pes->data[5]; +} + + +static void gf_m2ts_get_adaptation_field(GF_M2TS_Demuxer *ts, GF_M2TS_AdaptationField *paf, unsigned char *data, u32 size) +{ + paf->discontinuity_indicator = (data[0] & 0x80) ? 1 : 0; + paf->random_access_indicator = (data[0] & 0x40) ? 1 : 0; + paf->priority_indicator = (data[0] & 0x20) ? 1 : 0; + paf->PCR_flag = (data[0] & 0x10) ? 1 : 0; + paf->OPCR_flag = (data[0] & 0x8) ? 1 : 0; + paf->splicing_point_flag = (data[0] & 0x4) ? 1 : 0; + paf->transport_private_data_flag = (data[0] & 0x2) ? 1 : 0; + paf->adaptation_field_extension_flag = (data[0] & 0x1) ? 1 : 0; + + if (paf->PCR_flag == 1){ + u32 base = (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4]; + u64 PCR = (u64) base; + paf->PCR_base = (PCR << 1) | (data[5] >> 7); + paf->PCR_ext = ((data[5] & 1) << 8) | data[6]; + } + +#if 0 + if (paf->OPCR_flag == 1){ + u32 base = (data[7] << 24) | (data[8] << 16) | (data[9] << 8) | data[10]; + u64 PCR = (u64) base; + paf->PCR_base = (PCR << 1) | (data[11] >> 7); + paf->PCR_ext = ((data[11] & 1) << 8) | data[12]; + } +#endif +} + +static void gf_m2ts_process_packet(GF_M2TS_Demuxer *ts, unsigned char *data) +{ + GF_M2TS_ES *es; + GF_M2TS_Header hdr; + GF_M2TS_AdaptationField af, *paf; + u32 payload_size, af_size; + u32 pos = 0; + + /* read TS packet header*/ + hdr.sync = data[0]; + hdr.error = (data[1] & 0x80) ? 1 : 0; + hdr.payload_start = (data[1] & 0x40) ? 1 : 0; + hdr.priority = (data[1] & 0x20) ? 1 : 0; + hdr.pid = ( (data[1]&0x1f) << 8) | data[2]; + hdr.scrambling_ctrl = (data[3] >> 6) & 0x3; + hdr.adaptation_field = (data[3] >> 4) & 0x3; + hdr.continuity_counter = data[3] & 0xf; + +#if DEBUG_TS_PACKET + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Packet PID %d\n", hdr.pid)); +#endif + + paf = NULL; + payload_size = 184; + pos = 4; + switch (hdr.adaptation_field) { + /*adaptation+data*/ + case 3: + af_size = data[4]; + if (af_size>183) { + //error + return; + } + paf = ⁡ + memset(paf, 0, sizeof(GF_M2TS_AdaptationField)); + gf_m2ts_get_adaptation_field(ts, paf, data+5, af_size); + pos += 1+af_size; + payload_size = 183 - af_size; + break; + /*adaptation only - still process in cas of PCR*/ + case 2: + af_size = data[4]; + if (af_size>183) { + //error + return; + } + paf = ⁡ + memset(paf, 0, sizeof(GF_M2TS_AdaptationField)); + gf_m2ts_get_adaptation_field(ts, paf, data+5, af_size); + payload_size = 0; + break; + /*reserved*/ + case 0: + return; + default: + break; + } + data += pos; + + /*PAT*/ + if (hdr.pid == GF_M2TS_PID_PAT) { + gf_m2ts_gather_section(ts, ts->pat, NULL, &hdr, data, payload_size); + return; + } else { + es = ts->ess[hdr.pid]; + + /*check for DVB reserved PIDs*/ + if (!es) { + if (hdr.pid == GF_M2TS_PID_SDT_BAT_ST) { + gf_m2ts_gather_section(ts, ts->sdt, NULL, &hdr, data, payload_size); + return; + } else if (hdr.pid == GF_M2TS_PID_NIT_ST) { + /*ignore them, unused at application level*/ + if (!hdr.error) gf_m2ts_gather_section(ts, ts->nit, NULL, &hdr, data, payload_size); + return; + } else if (hdr.pid == GF_M2TS_PID_EIT_ST_CIT) { + /* ignore EIT messages for the moment */ + gf_m2ts_gather_section(ts, ts->eit, NULL, &hdr, data, payload_size); + return; + } else if (hdr.pid == GF_M2TS_PID_TDT_TOT_ST) { + gf_m2ts_gather_section(ts, ts->tdt_tot_st, NULL, &hdr, data, payload_size); + } + } else if (es->flags & GF_M2TS_ES_IS_SECTION) { /* The stream uses sections to carry its payload */ + GF_M2TS_SECTION_ES *ses = (GF_M2TS_SECTION_ES *)es; + if (ses->sec) gf_m2ts_gather_section(ts, ses->sec, ses, &hdr, data, payload_size); + } else { + /* regular stream using PES packets */ + /* let the pes reassembler decide if packets with error shall be discarded*/ + gf_m2ts_process_pes(ts, (GF_M2TS_PES *)es, &hdr, data, payload_size, paf); + } + } + if (paf && paf->PCR_flag) { + GF_M2TS_PES_PCK pck; + memset(&pck, 0, sizeof(GF_M2TS_PES_PCK)); + pck.PTS = paf->PCR_base * 300 + paf->PCR_ext; // ??? + pck.stream = (GF_M2TS_PES *)es; + if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PES_PCR, &pck); + } + return; +} + +GF_Err gf_m2ts_process_data(GF_M2TS_Demuxer *ts, char *data, u32 data_size) +{ + u32 pos; + Bool is_align = 1; + if (ts->buffer) { + if (ts->alloc_size < ts->buffer_size+data_size) { + ts->alloc_size = ts->buffer_size+data_size; + ts->buffer = (char*)realloc(ts->buffer, sizeof(char)*ts->alloc_size); + } + memcpy(ts->buffer + ts->buffer_size, data, sizeof(char)*data_size); + ts->buffer_size += data_size; + is_align = 0; + } else { + ts->buffer = data; + ts->buffer_size = data_size; + } + + /*sync input data*/ + pos = gf_m2ts_sync(ts, is_align); + if (pos==ts->buffer_size) { + if (is_align) { + ts->buffer = (char*)malloc(sizeof(char)*data_size); + memcpy(ts->buffer, data, sizeof(char)*data_size); + ts->alloc_size = ts->buffer_size = data_size; + } + return GF_OK; + } + for (;;) { + /*wait for a complete packet*/ + if (ts->buffer_size - pos < 188) { + ts->buffer_size -= pos; + if (!ts->buffer_size) { + if (!is_align) free(ts->buffer); + ts->buffer = NULL; + return GF_OK; + } + if (is_align) { + data = ts->buffer+pos; + ts->buffer = (char*)malloc(sizeof(char)*ts->buffer_size); + memcpy(ts->buffer, data, sizeof(char)*ts->buffer_size); + ts->alloc_size = ts->buffer_size; + } else { + memmove(ts->buffer, ts->buffer + pos, sizeof(char)*ts->buffer_size); + } + return GF_OK; + } + /*process*/ + gf_m2ts_process_packet(ts, ts->buffer+pos); + pos += 188; + } + return GF_OK; +} + +void gf_m2ts_reset_parsers(GF_M2TS_Demuxer *ts) +{ + u32 i; + for (i=0; iess[i]; + if (!es) continue; + + if (es->flags & GF_M2TS_ES_IS_SECTION) { + GF_M2TS_SECTION_ES *ses = (GF_M2TS_SECTION_ES *)es; + ses->sec->cc = -1; + ses->sec->length = 0; + ses->sec->received = 0; + free(ses->sec->section); + ses->sec->section = NULL; + while (ses->sec->table) { + GF_M2TS_Table *t = ses->sec->table; + ses->sec->table = t->next; + gf_m2ts_reset_sections(t->sections); + gf_list_del(t->sections); + free(t); + } + } else { + GF_M2TS_PES *pes = (GF_M2TS_PES *)es; + if (!pes || (pes->pid==pes->program->pmt_pid)) continue; + pes->frame_state = 0; + if (pes->data) free(pes->data); + pes->data = NULL; + pes->data_len = 0; + pes->PTS = pes->DTS = 0; + } +// free(es); +// ts->ess[i] = NULL; + } +/* + if (ts->pat) { + ts->pat->cc = -1; + ts->pat->length = 0; + ts->pat->received = 0; + free(ts->pat->section); + while (ts->pat->table) { + GF_M2TS_Table *t = ts->pat->table; + ts->pat->table = t->next; + if (t->data) free(t->data); + free(t); + } + } +*/ +} + +static void gf_m2ts_reframe_skip(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +{ + u32 res; + res = 0; +} + +GF_Err gf_m2ts_set_pes_framing(GF_M2TS_PES *pes, u32 mode) +{ + /*ignore request for section PIDs*/ + if (pes->flags & GF_M2TS_ES_IS_SECTION) return GF_OK; + + if (pes->pid==pes->program->pmt_pid) return GF_BAD_PARAM; + + if (mode==GF_M2TS_PES_FRAMING_RAW) { + pes->reframe = gf_m2ts_reframe_default; + return GF_OK; + } else if (mode==GF_M2TS_PES_FRAMING_SKIP) { + pes->reframe = gf_m2ts_reframe_skip; + return GF_OK; + } else { // mode==GF_M2TS_PES_FRAMING_DEFAULT + switch (pes->stream_type) { + case GF_M2TS_VIDEO_MPEG1: + case GF_M2TS_VIDEO_MPEG2: + pes->reframe = gf_m2ts_reframe_mpeg_video; + break; + case GF_M2TS_AUDIO_MPEG1: + case GF_M2TS_AUDIO_MPEG2: + pes->reframe = gf_m2ts_reframe_mpeg_audio; + break; + case GF_M2TS_VIDEO_H264: + pes->reframe = gf_m2ts_reframe_avc_h264; + break; + case GF_M2TS_AUDIO_LATM_AAC: + pes->reframe = gf_m2ts_reframe_aac_latm; + break; + case GF_M2TS_PRIVATE_DATA: + /* TODO: handle DVB subtitle streams */ + default: + pes->reframe = gf_m2ts_reframe_default; + break; + } + return GF_OK; + } +} + +GF_M2TS_Demuxer *gf_m2ts_demux_new() +{ + GF_M2TS_Demuxer *ts; + GF_SAFEALLOC(ts, GF_M2TS_Demuxer); + ts->programs = gf_list_new(); + ts->SDTs = gf_list_new(); + + ts->pat = gf_m2ts_section_filter_new(gf_m2ts_process_pat, 0); + ts->sdt = gf_m2ts_section_filter_new(NULL/*gf_m2ts_process_sdt*/, 1); + ts->nit = gf_m2ts_section_filter_new(gf_m2ts_process_nit, 0); + ts->eit = gf_m2ts_section_filter_new(NULL/*gf_m2ts_process_eit*/, 1); + ts->tdt_tot_st = gf_m2ts_section_filter_new(NULL/*gf_m2ts_process_tdt_tot_st*/, 1); + return ts; +} + +void gf_m2ts_demux_del(GF_M2TS_Demuxer *ts) +{ + u32 i; + if (ts->pat) gf_m2ts_section_filter_del(ts->pat); + if (ts->sdt) gf_m2ts_section_filter_del(ts->sdt); + if (ts->nit) gf_m2ts_section_filter_del(ts->nit); + if (ts->eit) gf_m2ts_section_filter_del(ts->eit); + if (ts->tdt_tot_st) gf_m2ts_section_filter_del(ts->tdt_tot_st); + + for (i=0; i<8192; i++) { + if (ts->ess[i]) gf_m2ts_es_del(ts->ess[i]); + } + if (ts->buffer) free(ts->buffer); + while (gf_list_count(ts->programs)) { + GF_M2TS_Program *p = (GF_M2TS_Program *)gf_list_last(ts->programs); + gf_list_rem_last(ts->programs); + gf_list_del(p->streams); + /*reset OD list*/ + if (p->additional_ods) { + gf_odf_desc_list_del(p->additional_ods); + gf_list_del(p->additional_ods); + } + if (p->pmt_iod) gf_odf_desc_del((GF_Descriptor *)p->pmt_iod); + free(p); + } + gf_list_del(ts->programs); + + gf_m2ts_reset_sdt(ts); + gf_list_del(ts->SDTs); + + free(ts); +} diff --git a/src/media_tools/saf.c b/src/media_tools/saf.c new file mode 100644 index 0000000..a23e01a --- /dev/null +++ b/src/media_tools/saf.c @@ -0,0 +1,333 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / LASeR codec sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +enum +{ + SAF_STREAM_HEADER = 1, + SAF_STREAM_HEADER_PERMANENT = 2, + SAF_END_OF_STREAM = 3, + SAF_ACCESS_UNIT = 4, + SAF_END_OF_SESSION = 5, + SAF_CACHE_UNIT = 6, + SAF_REMOTE_STREAM_HEADER = 7, +}; + + +typedef struct +{ + char *data; + u32 data_size; + Bool is_rap; + u32 ts; +} GF_SAFSample; + +typedef struct +{ + u32 stream_id; + u32 ts_resolution; + u32 buffersize_db; + u8 stream_type, object_type; + char *mime_type; + char *remote_url; + + char *dsi; + u32 dsi_len; + + GF_List *aus; + /*0: not declared yet; 1: declared; (1<<1) : done but end of stream not sent yet*/ + u32 state; + u32 last_au_sn, last_au_ts; +} GF_SAFStream; + +struct __saf_muxer +{ + GF_List *streams; + /*0: nothing to do, 1: should regenerate, (1<<1): end of session has been sent*/ + u32 state; + GF_Mutex *mx; +}; + +GF_SAFMuxer *gf_saf_mux_new() +{ + GF_SAFMuxer *mux; + GF_SAFEALLOC(mux, GF_SAFMuxer); + mux->mx = gf_mx_new("SAF"); + mux->streams = gf_list_new(); + return mux; +} + +static void saf_stream_del(GF_SAFStream *str) +{ + if (str->mime_type) free(str->mime_type); + if (str->remote_url) free(str->remote_url); + if (str->dsi) free(str->dsi); + + while (gf_list_count(str->aus)) { + GF_SAFSample *au = (GF_SAFSample *)gf_list_last(str->aus); + gf_list_rem_last(str->aus); + if (au->data) free(au->data); + free(au); + } + gf_list_del(str->aus); + free(str); +} + +void gf_saf_mux_del(GF_SAFMuxer *mux) +{ + while (gf_list_count(mux->streams)) { + GF_SAFStream *str = (GF_SAFStream *)gf_list_last(mux->streams); + gf_list_rem_last(mux->streams); + saf_stream_del(str); + } + gf_list_del(mux->streams); + gf_mx_del(mux->mx); + free(mux); +} + +static GFINLINE GF_SAFStream *saf_get_stream(GF_SAFMuxer *mux, u32 stream_id) +{ + GF_SAFStream *str; + u32 i=0; + while ( (str = (GF_SAFStream *)gf_list_enum(mux->streams, &i)) ) { + if (str->stream_id==stream_id) return str; + } + return NULL; +} + +GF_Err gf_saf_mux_stream_add(GF_SAFMuxer *mux, u32 stream_id, u32 ts_res, u32 buffersize_db, u8 stream_type, u8 object_type, char *mime_type, char *dsi, u32 dsi_len, char *remote_url) +{ + GF_SAFStream *str = saf_get_stream(mux, stream_id); + if (str) return GF_BAD_PARAM; + + if (mux->state == 2) return GF_BAD_PARAM; + + gf_mx_p(mux->mx); + + GF_SAFEALLOC(str, GF_SAFStream); + str->stream_id = stream_id; + str->ts_resolution = ts_res; + str->buffersize_db = buffersize_db; + str->stream_type = stream_type; + str->object_type = object_type; + if (mime_type) { + str->mime_type = strdup(mime_type); + str->stream_type = str->object_type = 0xFF; + } + str->dsi_len = dsi_len; + if (dsi_len) { + str->dsi = (char *) malloc(sizeof(char)*dsi_len); + memcpy(str->dsi, dsi, sizeof(char)*dsi_len); + } + if (remote_url) str->remote_url = strdup(remote_url); + str->aus = gf_list_new(); + mux->state = 0; + gf_list_add(mux->streams, str); + gf_mx_v(mux->mx); + return GF_OK; +} + +GF_Err gf_saf_mux_stream_rem(GF_SAFMuxer *mux, u32 stream_id) +{ + GF_SAFStream *str = saf_get_stream(mux, stream_id); + if (!str) return GF_BAD_PARAM; + if (mux->state == 2) return GF_BAD_PARAM; + + gf_mx_p(mux->mx); + str->state |= 2; + mux->state = 1; + gf_mx_v(mux->mx); + return GF_OK; +} + +GF_Err gf_saf_mux_add_au(GF_SAFMuxer *mux, u32 stream_id, u32 CTS, char *data, u32 data_len, Bool is_rap) +{ + GF_SAFSample *au; + GF_SAFStream *str = saf_get_stream(mux, stream_id); + if (!str) return GF_BAD_PARAM; + if (mux->state == 2) return GF_BAD_PARAM; + + gf_mx_p(mux->mx); + + GF_SAFEALLOC(au, GF_SAFSample); + au->data = data; + au->data_size = data_len; + au->is_rap = is_rap; + au->ts = CTS; + mux->state = 1; + + gf_list_add(str->aus, au); + gf_mx_v(mux->mx); + return GF_OK; +} + + +GF_Err gf_saf_mux_for_time(GF_SAFMuxer *mux, u32 time_ms, Bool force_end_of_session, char **out_data, u32 *out_size) +{ + u32 i, count, dlen; + char *data; + GF_SAFStream *str; + GF_SAFSample*au; + GF_BitStream *bs, *payload; + + *out_data = NULL; + *out_size = 0; + + gf_mx_p(mux->mx); + if (!force_end_of_session && (mux->state!=1)) { + gf_mx_v(mux->mx); + return GF_OK; + } + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + count = gf_list_count(mux->streams); + + /*1: write all stream headers*/ + for (i=0; istreams, i); + if (str->state & 1) continue; + + au = (GF_SAFSample *)gf_list_get(str->aus, 0); + + /*write stream declaration*/ + payload = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(payload, str->remote_url ? SAF_REMOTE_STREAM_HEADER : SAF_STREAM_HEADER, 4); + gf_bs_write_int(payload, str->stream_id, 12); + + gf_bs_write_u8(payload, str->object_type); + gf_bs_write_u8(payload, str->stream_type); + gf_bs_write_int(payload, str->ts_resolution, 24); + gf_bs_write_u16(payload, str->buffersize_db); + if (str->mime_type) { + u32 len = strlen(str->mime_type); + gf_bs_write_u16(payload, len); + gf_bs_write_data(payload, str->mime_type, len); + } + if (str->remote_url) { + u32 len = strlen(str->remote_url); + gf_bs_write_u16(payload, len); + gf_bs_write_data(payload, str->remote_url, len); + } + if (str->dsi) { + gf_bs_write_data(payload, str->dsi, str->dsi_len); + } + + gf_bs_get_content(payload, &data, &dlen); + gf_bs_del(payload); + + /*write SAF packet header*/ + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, 0, 15); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, au ? au->ts : 0, 30); + gf_bs_write_int(bs, dlen, 16); + gf_bs_write_data(bs, data, dlen); + free(data); + + /*mark as signaled*/ + str->state |= 1; + } + + /*write all pending AUs*/ + while (1) { + GF_SAFStream *src = NULL; + u32 mux_time = time_ms; + + for (i=0; istreams, i); + au = (GF_SAFSample*)gf_list_get(str->aus, 0); + if (au && (au->ts*1000 < mux_time*str->ts_resolution)) { + mux_time = 1000*au->ts/str->ts_resolution; + src = str; + } + } + + if (!src) break; + + au = (GF_SAFSample*)gf_list_get(src->aus, 0); + gf_list_rem(src->aus, 0); + + /*write stream declaration*/ + gf_bs_write_int(bs, au->is_rap ? 1 : 0, 1); + gf_bs_write_int(bs, src->last_au_sn, 15); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, au->ts, 30); + gf_bs_write_u16(bs, 2+au->data_size); + gf_bs_write_int(bs, SAF_ACCESS_UNIT, 4); + gf_bs_write_int(bs, src->stream_id, 12); + gf_bs_write_data(bs, au->data, au->data_size); + + src->last_au_sn ++; + src->last_au_ts = au->ts; + free(au->data); + free(au); + } + + /*3: write all end of stream*/ + for (i=0; istreams, i); + /*mark as signaled*/ + if (!(str->state & 2)) continue; + if (gf_list_count(str->aus)) continue; + + /*write stream declaration*/ + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, str->last_au_sn, 15); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, str->last_au_ts, 30); + gf_bs_write_int(bs, 2, 16); + gf_bs_write_int(bs, SAF_END_OF_STREAM, 4); + gf_bs_write_int(bs, str->stream_id, 12); + + /*remove stream*/ + gf_list_rem(mux->streams, i); + i--; + count--; + saf_stream_del(str); + } + mux->state = 0; + if (force_end_of_session) { + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, 0, 15); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, 0, 30); + gf_bs_write_int(bs, 2, 16); + gf_bs_write_int(bs, SAF_END_OF_SESSION, 4); + gf_bs_write_int(bs, 0, 12); + mux->state = 2; + } + gf_bs_get_content(bs, out_data, out_size); + gf_bs_del(bs); + gf_mx_v(mux->mx); + return GF_OK; +} diff --git a/src/media_tools/text_import.c b/src/media_tools/text_import.c new file mode 100644 index 0000000..e193e9c --- /dev/null +++ b/src/media_tools/text_import.c @@ -0,0 +1,1713 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include +#include +#include +#include +#include + +#ifndef GPAC_READ_ONLY + +enum +{ + GF_TEXT_IMPORT_NONE = 0, + GF_TEXT_IMPORT_SRT, + GF_TEXT_IMPORT_SUB, + GF_TEXT_IMPORT_TTXT, + GF_TEXT_IMPORT_TEXML, +}; + +#define REM_TRAIL_MARKS(__str, __sep) while (1) { \ + u32 _len = strlen(__str); \ + if (!_len) break; \ + _len--; \ + if (strchr(__sep, __str[_len])) __str[_len] = 0; \ + else break; \ + } \ + + +static s32 gf_text_get_utf_type(FILE *in_src) +{ + unsigned char BOM[5]; + fread(BOM, 5, 1, in_src); + + if ((BOM[0]==0xFF) && (BOM[1]==0xFE)) { + /*UTF32 not supported*/ + if (!BOM[2] && !BOM[3]) return -1; + fseek(in_src, 2, SEEK_SET); + return 3; + } + if ((BOM[0]==0xFE) && (BOM[1]==0xFF)) { + /*UTF32 not supported*/ + if (!BOM[2] && !BOM[3]) return -1; + fseek(in_src, 2, SEEK_SET); + return 2; + } else if ((BOM[0]==0xEF) && (BOM[1]==0xBB) && (BOM[2]==0xBF)) { + fseek(in_src, 3, SEEK_SET); + return 1; + } + if (BOM[0]<0x80) { + fseek(in_src, 0, SEEK_SET); + return 0; + } + return -1; +} + +static GF_Err gf_text_guess_format(char *filename, u32 *fmt) +{ + char szLine[2048]; + u32 val; + s32 uni_type; + FILE *test = fopen(filename, "rb"); + if (!test) return GF_URL_ERROR; + uni_type = gf_text_get_utf_type(test); + + if (uni_type>1) { + const u16 *sptr; + char szUTF[1024]; + u32 read = fread(szUTF, 1, 1023, test); + szUTF[read]=0; + sptr = (u16*)szUTF; + read = gf_utf8_wcstombs(szLine, read, &sptr); + } else { + val = fread(szLine, 1, 1024, test); + szLine[val]=0; + } + REM_TRAIL_MARKS(szLine, "\r\n\t ") + + *fmt = GF_TEXT_IMPORT_NONE; + if ((szLine[0]=='{') && strstr(szLine, "}{")) *fmt = GF_TEXT_IMPORT_SUB; + else if (!strnicmp(szLine, ""); + if (ext) ext += 2; + if (!ext[0]) fgets(szLine, 2048, test); + if (strstr(szLine, "x-quicktime-tx3g") || strstr(szLine, "text3GTrack")) *fmt = GF_TEXT_IMPORT_TEXML; + else if (strstr(szLine, "TextStream")) *fmt = GF_TEXT_IMPORT_TTXT; + } + else if (strstr(szLine, " --> ") ) + *fmt = GF_TEXT_IMPORT_SRT; + + fclose(test); + return GF_OK; +} + +#define TTXT_DEFAULT_WIDTH 400 +#define TTXT_DEFAULT_HEIGHT 60 +#define TTXT_DEFAULT_FONT_SIZE 18 + +static void gf_text_get_video_size(GF_ISOFile *dest, u32 *width, u32 *height) +{ + u32 w, h, f_w, f_h, i; + + f_w = f_h = 0; + for (i=0; i f_w) f_w = w; + if (h > f_h) f_h = h; + gf_isom_get_track_layout_info(dest, i+1, &w, &h, NULL, NULL, NULL); + if (w > f_w) f_w = w; + if (h > f_h) f_h = h; + break; + } + } + (*width) = f_w ? f_w : TTXT_DEFAULT_WIDTH; + (*height) = f_h ? f_h : TTXT_DEFAULT_HEIGHT; +} + + +static void gf_text_import_set_language(GF_MediaImporter *import, u32 track) +{ + if (import->esd && import->esd->langDesc) { + char lang[4]; + lang[0] = (import->esd->langDesc->langCode>>16) & 0xFF; + lang[1] = (import->esd->langDesc->langCode>>8) & 0xFF; + lang[2] = (import->esd->langDesc->langCode) & 0xFF; + lang[3] = 0; + gf_isom_set_media_language(import->dest, track, lang); + } +} + + +static char *gf_text_get_utf8_line(char *szLine, u32 lineSize, FILE *txt_in, s32 unicode_type) +{ + u32 i, j, len; + char *sOK; + char szLineConv[1024]; + unsigned short *sptr; + + memset(szLine, 0, sizeof(char)*lineSize); + sOK = fgets(szLine, lineSize, txt_in); + if (!sOK) return NULL; + if (unicode_type<=1) { + j=0; + len = strlen(szLine); + for (i=0; i> 6) & 0x3 ); + j++; + szLine[i] &= 0xbf; + } + /*UTF8 2 bytes char*/ + else if ( (szLine[i] & 0xe0) == 0xc0) { + szLineConv[j] = szLine[i]; i++; j++; + } + /*UTF8 3 bytes char*/ + else if ( (szLine[i] & 0xf0) == 0xe0) { + szLineConv[j] = szLine[i]; i++; j++; + szLineConv[j] = szLine[i]; i++; j++; + } + /*UTF8 4 bytes char*/ + else if ( (szLine[i] & 0xf8) == 0xf0) { + szLineConv[j] = szLine[i]; i++; j++; + szLineConv[j] = szLine[i]; i++; j++; + szLineConv[j] = szLine[i]; i++; j++; + } else { + i+=1; + continue; + } + } + szLineConv[j] = szLine[i]; + j++; + } + szLineConv[j] = 0; + strcpy(szLine, szLineConv); + return sOK; + } + +#ifdef GPAC_BIG_ENDIAN + if (unicode_type==3) { +#else + if (unicode_type==2) { +#endif + i=0; + while (1) { + char c; + if (!szLine[i] && !szLine[i+1]) break; + c = szLine[i+1]; + szLine[i+1] = szLine[i]; + szLine[i] = c; + i+=2; + } + } + sptr = (u16 *)szLine; + i = gf_utf8_wcstombs(szLineConv, 1024, (const unsigned short **) &sptr); + szLineConv[i] = 0; + strcpy(szLine, szLineConv); + /*this is ugly indeed: since input is UTF16-LE, there are many chances the fgets never reads the \0 after a \n*/ + if (unicode_type==3) fgetc(txt_in); + return sOK; +} + +static GF_Err gf_text_import_srt(GF_MediaImporter *import) +{ + FILE *srt_in; + Double scale; + u32 track, timescale, i, count; + GF_TextConfig*cfg; + GF_Err e; + GF_StyleRecord rec; + GF_TextSample * samp; + GF_ISOSample *s; + u32 sh, sm, ss, sms, eh, em, es, ems, txt_line, char_len, char_line, nb_samp, j, duration, file_size, rem_styles; + Bool set_start_char, set_end_char, first_samp; + u64 start, end, prev_end; + u32 state, curLine, line, len, ID, OCR_ES_ID; + s32 unicode_type; + char szLine[2048], szText[2048], *ptr; + unsigned short uniLine[5000], uniText[5000], *sptr; + + srt_in = fopen(import->in_name, "rt"); + fseek(srt_in, 0, SEEK_END); + file_size = ftell(srt_in); + fseek(srt_in, 0, SEEK_SET); + + unicode_type = gf_text_get_utf_type(srt_in); + if (unicode_type<0) { + fclose(srt_in); + return gf_import_message(import, GF_NOT_SUPPORTED, "Unsupported SRT UTF encoding"); + } + + cfg = NULL; + if (import->esd) { + if (!import->esd->slConfig) { + import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->predefined = 2; + import->esd->slConfig->timestampResolution = 1000; + } + timescale = import->esd->slConfig->timestampResolution; + if (!timescale) timescale = 1000; + + /*explicit text config*/ + if (import->esd->decoderConfig && import->esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_TEXT_CFG_TAG) { + cfg = (GF_TextConfig *) import->esd->decoderConfig->decoderSpecificInfo; + import->esd->decoderConfig->decoderSpecificInfo = NULL; + } + ID = import->esd->ESID; + OCR_ES_ID = import->esd->OCRESID; + } else { + timescale = 1000; + OCR_ES_ID = ID = 0; + } + + if (cfg && cfg->timescale) timescale = cfg->timescale; + track = gf_isom_new_track(import->dest, ID, GF_ISOM_MEDIA_TEXT, timescale); + if (!track) { + fclose(srt_in); + return gf_import_message(import, gf_isom_last_error(import->dest), "Error creating text track"); + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (import->esd && !import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + + if (OCR_ES_ID) gf_isom_set_track_reference(import->dest, track, GF_ISOM_REF_OCR, OCR_ES_ID); + + /*setup track*/ + if (cfg) { + char *firstFont = NULL; + /*set track info*/ + gf_isom_set_track_layout_info(import->dest, track, cfg->text_width<<16, cfg->text_height<<16, 0, 0, cfg->layer); + + /*and set sample descriptions*/ + count = gf_list_count(cfg->sample_descriptions); + for (i=0; isample_descriptions, i); + if (!sd->font_count) { + sd->fonts = (GF_FontRecord*)malloc(sizeof(GF_FontRecord)); + sd->font_count = 1; + sd->fonts[0].fontID = 1; + sd->fonts[0].fontName = strdup("Serif"); + } + if (!sd->default_style.fontID) sd->default_style.fontID = sd->fonts[0].fontID; + if (!sd->default_style.font_size) sd->default_style.font_size = 16; + if (!sd->default_style.text_color) sd->default_style.text_color = 0xFF000000; + /*store attribs*/ + if (!i) rec = sd->default_style; + + gf_isom_new_text_description(import->dest, track, sd, NULL, NULL, &state); + if (!firstFont) firstFont = sd->fonts[0].fontName; + } + gf_import_message(import, GF_OK, "Timed Text (SRT) import - text track %d x %d, font %s (size %d)", cfg->text_width, cfg->text_height, firstFont, rec.font_size); + + gf_odf_desc_del((GF_Descriptor *)cfg); + } else { + u32 w, h; + GF_TextSampleDescriptor *sd; + gf_text_get_video_size(import->dest, &w, &h); + + /*have to work with default - use max size (if only one video, this means the text region is the + entire display, and with bottom alignment things should be fine...*/ + gf_isom_set_track_layout_info(import->dest, track, w<<16, h<<16, 0, 0, 0); + sd = (GF_TextSampleDescriptor*)gf_odf_desc_new(GF_ODF_TX3G_TAG); + sd->fonts = (GF_FontRecord*)malloc(sizeof(GF_FontRecord)); + sd->font_count = 1; + sd->fonts[0].fontID = 1; + sd->fonts[0].fontName = strdup(import->fontName ? import->fontName : "Serif"); + sd->back_color = 0x00000000; /*transparent*/ + sd->default_style.fontID = 1; + sd->default_style.font_size = import->fontSize ? import->fontSize : TTXT_DEFAULT_FONT_SIZE; + sd->default_style.text_color = 0xFFFFFFFF; /*white*/ + sd->default_style.style_flags = 0; + sd->horiz_justif = 1; /*center of scene*/ + sd->vert_justif = (s8) -1; /*bottom of scene*/ + + if (import->flags & GF_IMPORT_SKIP_TXT_BOX) { + sd->default_pos.top = sd->default_pos.left = sd->default_pos.right = sd->default_pos.bottom = 0; + } else { + if ((sd->default_pos.bottom==sd->default_pos.top) || (sd->default_pos.right==sd->default_pos.left)) { + sd->default_pos.top = sd->default_pos.left = 0; + sd->default_pos.right = w; + sd->default_pos.bottom = h; + } + } + + /*store attribs*/ + rec = sd->default_style; + gf_isom_new_text_description(import->dest, track, sd, NULL, NULL, &state); + + gf_import_message(import, GF_OK, "Timed Text (SRT) import - text track %d x %d, font %s (size %d)", w, h, sd->fonts[0].fontName, rec.font_size); + gf_odf_desc_del((GF_Descriptor *)sd); + } + gf_text_import_set_language(import, track); + duration = (u32) (((Double) import->duration)*timescale/1000.0); + + e = GF_OK; + state = 0; + end = prev_end = 0; + curLine = 0; + txt_line = 0; + set_start_char = set_end_char = 0; + char_len = 0; + start = 0; + nb_samp = 0; + samp = gf_isom_new_text_sample(); + + scale = timescale; + scale /= 1000; + first_samp = 1; + while (1) { + char *sOK = gf_text_get_utf8_line(szLine, 2048, srt_in, unicode_type); + + if (sOK) REM_TRAIL_MARKS(szLine, "\r\n\t ") + if (!sOK || !strlen(szLine)) { + state = 0; + rec.style_flags = 0; + rec.startCharOffset = rec.endCharOffset = 0; + if (txt_line) { + if (prev_end && (start != prev_end)) { + GF_TextSample * empty_samp = gf_isom_new_text_sample(); + s = gf_isom_text_to_sample(empty_samp); + gf_isom_delete_text_sample(empty_samp); + s->DTS = (u64) (scale*(s64)prev_end); + s->IsRAP = 1; + gf_isom_add_sample(import->dest, track, 1, s); + gf_isom_sample_del(&s); + nb_samp++; + } + + s = gf_isom_text_to_sample(samp); + s->DTS = (u64) (scale*(s64) start); + s->IsRAP = 1; + gf_isom_add_sample(import->dest, track, 1, s); + gf_isom_sample_del(&s); + nb_samp++; + prev_end = end; + txt_line = 0; + char_len = 0; + set_start_char = set_end_char = 0; + rec.startCharOffset = rec.endCharOffset = 0; + gf_isom_text_reset(samp); + + //gf_import_progress(import, nb_samp, nb_samp+1); + gf_set_progress("Importing SRT", ftell(srt_in), file_size); + if (duration && (end >= duration)) break; + } + if (!sOK) break; + continue; + } + + switch (state) { + case 0: + if (sscanf(szLine, "%d", &line) != 1) { + e = gf_import_message(import, GF_CORRUPTED_DATA, "Bad SRT formatting - expecting number got \"%s\"", szLine); + goto exit; + } + if (line != curLine + 1) gf_import_message(import, GF_OK, "WARNING: corrupted SRT frame %d after frame %d", line, curLine); + curLine = line; + state = 1; + break; + case 1: + if (sscanf(szLine, "%d:%d:%d,%d --> %d:%d:%d,%d", &sh, &sm, &ss, &sms, &eh, &em, &es, &ems) != 8) { + e = gf_import_message(import, GF_CORRUPTED_DATA, "Error scanning SRT frame %d timing", curLine); + goto exit; + } + start = (3600*sh + 60*sm + ss)*1000 + sms; + if (start0)) { + s = gf_isom_text_to_sample(samp); + s->DTS = 0; + gf_isom_add_sample(import->dest, track, 1, s); + gf_isom_sample_del(&s); + nb_samp++; + } + rec.style_flags = 0; + state = 2; + break; + + default: + /*reset only when text is present*/ + first_samp = 0; + + /*go to line*/ + if (txt_line) { + gf_isom_text_add_text(samp, "\n", 1); + char_len += 1; + } + + ptr = (char *) szLine; + len = gf_utf8_mbstowcs(uniLine, 5000, (const char **) &ptr); + if (len == (u32) -1) { + e = gf_import_message(import, GF_CORRUPTED_DATA, "Invalid UTF data (line %d)", curLine); + goto exit; + } + char_line = 0; + i=j=0; + rem_styles = 0; + while (i')) { + /*store prev style*/ + if (set_end_char) { + assert(set_start_char); + gf_isom_text_add_style(samp, &rec); + set_end_char = set_start_char = 0; + rec.style_flags &= ~rem_styles; + rem_styles = 0; + } + if (set_start_char && (rec.startCharOffset != j)) { + rec.endCharOffset = char_len + j; + if (rec.style_flags) gf_isom_text_add_style(samp, &rec); + } + switch (uniLine[i+1]) { + case 'b': case 'B': + rec.style_flags |= GF_TXT_STYLE_BOLD; + set_start_char = 1; + rec.startCharOffset = char_len + j; + break; + case 'i': case 'I': + rec.style_flags |= GF_TXT_STYLE_ITALIC; + set_start_char = 1; + rec.startCharOffset = char_len + j; + break; + case 'u': case 'U': + rec.style_flags |= GF_TXT_STYLE_UNDERLINED; + set_start_char = 1; + rec.startCharOffset = char_len + j; + break; + } + i+=3; + continue; + } + + /*end of prev style*/ + if ( (uniLine[i]=='<') && (uniLine[i+1]=='/') && (uniLine[i+3]=='>')) { + switch (uniLine[i+2]) { + case 'b': case 'B': + rem_styles |= GF_TXT_STYLE_BOLD; + set_end_char = 1; + rec.endCharOffset = char_len + j; + break; + case 'i': case 'I': + rem_styles |= GF_TXT_STYLE_ITALIC; + set_end_char = 1; + rec.endCharOffset = char_len + j; + break; + case 'u': case 'U': + rem_styles |= GF_TXT_STYLE_UNDERLINED; + set_end_char = 1; + rec.endCharOffset = char_len + j; + break; + } + i+=4; + continue; + } + /*store style*/ + if (set_end_char) { + gf_isom_text_add_style(samp, &rec); + set_end_char = 0; + set_start_char = 1; + rec.startCharOffset = char_len + j; + rec.style_flags &= ~rem_styles; + rem_styles = 0; + } + + uniText[j] = uniLine[i]; + j++; + i++; + } + /*store last style*/ + if (set_end_char) { + gf_isom_text_add_style(samp, &rec); + set_end_char = 0; + set_start_char = 1; + rec.startCharOffset = char_len + j; + rec.style_flags &= ~rem_styles; + rem_styles = 0; + } + + char_line = j; + uniText[j] = 0; + + sptr = (u16 *) uniText; + len = gf_utf8_wcstombs(szText, 5000, (const u16 **) &sptr); + + gf_isom_text_add_text(samp, szText, len); + char_len += char_line; + txt_line ++; + break; + } + if (duration && (start >= duration)) break; + } + + /*final flush*/ + if (end) { + gf_isom_text_reset(samp); + s = gf_isom_text_to_sample(samp); + s->DTS = (u64) (scale*(s64)end); + s->IsRAP = 1; + gf_isom_add_sample(import->dest, track, 1, s); + gf_isom_sample_del(&s); + nb_samp++; + } + gf_isom_delete_text_sample(samp); + gf_isom_set_last_sample_duration(import->dest, track, 0); + gf_set_progress("Importing SRT", nb_samp, nb_samp); + +exit: + if (e) gf_isom_remove_track(import->dest, track); + fclose(srt_in); + return e; +} + + +static GF_Err gf_text_import_sub(GF_MediaImporter *import) +{ + FILE *sub_in; + u32 track, ID, timescale, i, j, desc_idx, start, end, prev_end, nb_samp, duration, file_size, len, line; + GF_TextConfig*cfg; + GF_Err e; + Double FPS; + GF_TextSample * samp; + Bool first_samp; + s32 unicode_type; + char szLine[2048], szTime[20], szText[2048]; + GF_ISOSample *s; + + sub_in = fopen(import->in_name, "rt"); + fseek(sub_in, 0, SEEK_END); + file_size = ftell(sub_in); + fseek(sub_in, 0, SEEK_SET); + + unicode_type = gf_text_get_utf_type(sub_in); + if (unicode_type<0) { + fclose(sub_in); + return gf_import_message(import, GF_NOT_SUPPORTED, "Unsupported SUB UTF encoding"); + } + + FPS = 25.0; + if (import->video_fps) FPS = import->video_fps; + + cfg = NULL; + if (import->esd) { + if (!import->esd->slConfig) { + import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->predefined = 2; + import->esd->slConfig->timestampResolution = 1000; + } + timescale = import->esd->slConfig->timestampResolution; + if (!timescale) timescale = 1000; + + /*explicit text config*/ + if (import->esd->decoderConfig && import->esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_TEXT_CFG_TAG) { + cfg = (GF_TextConfig *) import->esd->decoderConfig->decoderSpecificInfo; + import->esd->decoderConfig->decoderSpecificInfo = NULL; + } + ID = import->esd->ESID; + } else { + timescale = 1000; + ID = 0; + } + + if (cfg && cfg->timescale) timescale = cfg->timescale; + track = gf_isom_new_track(import->dest, ID, GF_ISOM_MEDIA_TEXT, timescale); + if (!track) { + fclose(sub_in); + return gf_import_message(import, gf_isom_last_error(import->dest), "Error creating text track"); + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (import->esd && !import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + + gf_text_import_set_language(import, track); + + file_size = 0; + /*setup track*/ + if (cfg) { + u32 count; + char *firstFont = NULL; + /*set track info*/ + gf_isom_set_track_layout_info(import->dest, track, cfg->text_width<<16, cfg->text_height<<16, 0, 0, cfg->layer); + + /*and set sample descriptions*/ + count = gf_list_count(cfg->sample_descriptions); + for (i=0; isample_descriptions, i); + if (!sd->font_count) { + sd->fonts = (GF_FontRecord*)malloc(sizeof(GF_FontRecord)); + sd->font_count = 1; + sd->fonts[0].fontID = 1; + sd->fonts[0].fontName = strdup("Serif"); + } + if (!sd->default_style.fontID) sd->default_style.fontID = sd->fonts[0].fontID; + if (!sd->default_style.font_size) sd->default_style.font_size = 16; + if (!sd->default_style.text_color) sd->default_style.text_color = 0xFF000000; + file_size = sd->default_style.font_size; + gf_isom_new_text_description(import->dest, track, sd, NULL, NULL, &desc_idx); + if (!firstFont) firstFont = sd->fonts[0].fontName; + } + gf_import_message(import, GF_OK, "Timed Text (SUB @ %02.2f) import - text track %d x %d, font %s (size %d)", FPS, cfg->text_width, cfg->text_height, firstFont, file_size); + + gf_odf_desc_del((GF_Descriptor *)cfg); + } else { + u32 w, h; + GF_TextSampleDescriptor *sd; + gf_text_get_video_size(import->dest, &w, &h); + + /*have to work with default - use max size (if only one video, this means the text region is the + entire display, and with bottom alignment things should be fine...*/ + gf_isom_set_track_layout_info(import->dest, track, w<<16, h<<16, 0, 0, 0); + sd = (GF_TextSampleDescriptor*)gf_odf_desc_new(GF_ODF_TX3G_TAG); + sd->fonts = (GF_FontRecord*)malloc(sizeof(GF_FontRecord)); + sd->font_count = 1; + sd->fonts[0].fontID = 1; + sd->fonts[0].fontName = strdup("Serif"); + sd->back_color = 0x00000000; /*transparent*/ + sd->default_style.fontID = 1; + sd->default_style.font_size = TTXT_DEFAULT_FONT_SIZE; + sd->default_style.text_color = 0xFFFFFFFF; /*white*/ + sd->default_style.style_flags = 0; + sd->horiz_justif = 1; /*center of scene*/ + sd->vert_justif = (s8) -1; /*bottom of scene*/ + + if (import->flags & GF_IMPORT_SKIP_TXT_BOX) { + sd->default_pos.top = sd->default_pos.left = sd->default_pos.right = sd->default_pos.bottom = 0; + } else { + if ((sd->default_pos.bottom==sd->default_pos.top) || (sd->default_pos.right==sd->default_pos.left)) { + sd->default_pos.top = sd->default_pos.left = 0; + sd->default_pos.right = w; + sd->default_pos.bottom = h; + } + } + + gf_isom_new_text_description(import->dest, track, sd, NULL, NULL, &desc_idx); + gf_import_message(import, GF_OK, "Timed Text (SUB @ %02.2f) import - text track %d x %d, font %s (size %d)", FPS, w, h, sd->fonts[0].fontName, TTXT_DEFAULT_FONT_SIZE); + gf_odf_desc_del((GF_Descriptor *)sd); + } + + duration = (u32) (((Double) import->duration)*timescale/1000.0); + + e = GF_OK; + nb_samp = 0; + samp = gf_isom_new_text_sample(); + + FPS = ((Double) timescale ) / FPS; + start = end = prev_end = 0; + + line = 0; + first_samp = 1; + while (1) { + char *sOK = gf_text_get_utf8_line(szLine, 2048, sub_in, unicode_type); + if (!sOK) break; + + REM_TRAIL_MARKS(szLine, "\r\n\t ") + + line++; + len = strlen(szLine); + if (!len) continue; + + i=0; + if (szLine[i] != '{') { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Bad SUB file (line %d): expecting \"{\" got \"%c\"", line, szLine[i]); + goto exit; + } + while (szLine[i+1] && szLine[i+1]!='}') { szTime[i] = szLine[i+1]; i++; } + szTime[i] = 0; + start = atoi(szTime); + if (startend) { + gf_import_message(import, GF_OK, "WARNING: corrupted SUB frame (line %d) - ends (at %d ms) before start of current frame (%d ms) - skipping", line, end, start); + continue; + } + + gf_isom_text_reset(samp); + + if (start && first_samp) { + s = gf_isom_text_to_sample(samp); + s->DTS = 0; + s->IsRAP = 1; + gf_isom_add_sample(import->dest, track, 1, s); + gf_isom_sample_del(&s); + first_samp = 0; + nb_samp++; + } + + for (i=j; iDTS = (u64) (FPS*(s64)prev_end); + gf_isom_add_sample(import->dest, track, 1, s); + gf_isom_sample_del(&s); + nb_samp++; + gf_isom_delete_text_sample(empty_samp); + } + + s = gf_isom_text_to_sample(samp); + s->DTS = (u64) (FPS*(s64)start); + gf_isom_add_sample(import->dest, track, 1, s); + gf_isom_sample_del(&s); + nb_samp++; + gf_isom_text_reset(samp); + prev_end = end; + gf_set_progress("Importing SUB", ftell(sub_in), file_size); + if (duration && (end >= duration)) break; + } + /*final flush*/ + if (end) { + gf_isom_text_reset(samp); + s = gf_isom_text_to_sample(samp); + s->DTS = (u64)(FPS*(s64)end); + gf_isom_add_sample(import->dest, track, 1, s); + gf_isom_sample_del(&s); + nb_samp++; + } + gf_isom_delete_text_sample(samp); + + gf_isom_set_last_sample_duration(import->dest, track, 0); + gf_set_progress("Importing SUB", nb_samp, nb_samp); + +exit: + if (e) gf_isom_remove_track(import->dest, track); + fclose(sub_in); + return e; +} + + +#define CHECK_STR(__str) \ + if (!__str) { \ + e = gf_import_message(import, GF_BAD_PARAM, "Invalid XML formatting (line %d)", parser.line); \ + goto exit; \ + } \ + + +u32 ttxt_get_color(GF_MediaImporter *import, char *val) +{ + u32 r, g, b, a, res; + r = g = b = a = 0; + if (sscanf(val, "%x %x %x %x", &r, &g, &b, &a) != 4) { + gf_import_message(import, GF_OK, "Warning: color badly formatted"); + } + res = (a&0xFF); res<<=8; + res |= (r&0xFF); res<<=8; + res |= (g&0xFF); res<<=8; + res |= (b&0xFF); + return res; +} + +void ttxt_parse_text_box(GF_MediaImporter *import, GF_XMLNode *n, GF_BoxRecord *box) +{ + u32 i=0; + GF_XMLAttribute *att; + memset(box, 0, sizeof(GF_BoxRecord)); + while ( (att=(GF_XMLAttribute *)gf_list_enum(n->attributes, &i))) { + if (!stricmp(att->name, "top")) box->top = atoi(att->value); + else if (!stricmp(att->name, "bottom")) box->bottom = atoi(att->value); + else if (!stricmp(att->name, "left")) box->left = atoi(att->value); + else if (!stricmp(att->name, "right")) box->right = atoi(att->value); + } +} + +void ttxt_parse_text_style(GF_MediaImporter *import, GF_XMLNode *n, GF_StyleRecord *style) +{ + u32 i=0; + GF_XMLAttribute *att; + memset(style, 0, sizeof(GF_StyleRecord)); + style->fontID = 1; + style->font_size = TTXT_DEFAULT_FONT_SIZE; + style->text_color = 0xFFFFFFFF; + + while ( (att=(GF_XMLAttribute *)gf_list_enum(n->attributes, &i))) { + if (!stricmp(att->name, "fromChar")) style->startCharOffset = atoi(att->value); + else if (!stricmp(att->name, "toChar")) style->endCharOffset = atoi(att->value); + else if (!stricmp(att->name, "fontID")) style->fontID = atoi(att->value); + else if (!stricmp(att->name, "fontSize")) style->font_size = atoi(att->value); + else if (!stricmp(att->name, "color")) style->text_color = ttxt_get_color(import, att->value); + else if (!stricmp(att->name, "styles")) { + if (strstr(att->value, "Bold")) style->style_flags |= GF_TXT_STYLE_BOLD; + if (strstr(att->value, "Italic")) style->style_flags |= GF_TXT_STYLE_ITALIC; + if (strstr(att->value, "Underlined")) style->style_flags |= GF_TXT_STYLE_UNDERLINED; + } + } +} + +char *ttxt_parse_string(GF_MediaImporter *import, char *str, Bool strip_lines) +{ + u32 i=0; + u32 k=0; + u32 len = strlen(str); + u32 state = 0; + + if (!strip_lines) { + for (i=0; iflags==GF_IMPORT_PROBE_ONLY) return GF_OK; + + parser = gf_xml_dom_new(); + e = gf_xml_dom_parse(parser, import->in_name, ttxt_import_progress, import); + if (e) { + gf_import_message(import, e, "Error parsing TTXT file: Line %d - %s", gf_xml_dom_get_line(parser), gf_xml_dom_get_error(parser)); + gf_xml_dom_del(parser); + return e; + } + root = gf_xml_dom_get_root(parser); + + e = GF_OK; + if (strcmp(root->name, "TextStream")) { + e = gf_import_message(import, GF_BAD_PARAM, "Invalid Timed Text file - expecting \"TextStream\" got %s", "TextStream", root->name); + goto exit; + } + + /*setup track in 3GP format directly (no ES desc)*/ + ID = (import->esd) ? import->esd->ESID : 0; + track = gf_isom_new_track(import->dest, ID, GF_ISOM_MEDIA_TEXT, 1000); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + /*some MPEG-4 setup*/ + if (import->esd) { + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->timestampResolution = 1000; + import->esd->decoderConfig->streamType = GF_STREAM_TEXT; + import->esd->decoderConfig->objectTypeIndication = 0x08; + if (import->esd->OCRESID) gf_isom_set_track_reference(import->dest, track, GF_ISOM_REF_OCR, import->esd->OCRESID); + } + gf_text_import_set_language(import, track); + + gf_import_message(import, GF_OK, "Timed Text (GPAC TTXT) Import"); + + last_sample_empty = 0; + last_sample_duration = 0; + nb_descs = 0; + nb_samples = 0; + nb_children = gf_list_count(root->content); + + i=0; + while ( (node = (GF_XMLNode*)gf_list_enum(root->content, &i))) { + if (node->type) { nb_children--; continue; } + + if (!strcmp(node->name, "TextStreamHeader")) { + GF_XMLNode *sdesc; + s32 w, h, tx, ty, layer; + u32 tref_id; + w = TTXT_DEFAULT_WIDTH; + h = TTXT_DEFAULT_HEIGHT; + tx = ty = layer = 0; + nb_children--; + tref_id = 0; + + j=0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(node->attributes, &j))) { + if (!strcmp(att->name, "width")) w = atoi(att->value); + else if (!strcmp(att->name, "height")) h = atoi(att->value); + else if (!strcmp(att->name, "layer")) layer = atoi(att->value); + else if (!strcmp(att->name, "translation_x")) tx = atoi(att->value); + else if (!strcmp(att->name, "translation_y")) ty = atoi(att->value); + else if (!strcmp(att->name, "trefID")) tref_id = atoi(att->value); + } + + if (tref_id) + gf_isom_set_track_reference(import->dest, track, GF_ISOM_BOX_TYPE_CHAP, tref_id); + + gf_isom_set_track_layout_info(import->dest, track, w<<16, h<<16, tx<<16, ty<<16, (s16) layer); + + j=0; + while ( (sdesc=(GF_XMLNode*)gf_list_enum(node->content, &j))) { + if (sdesc->type) continue; + + if (!strcmp(sdesc->name, "TextSampleDescription")) { + GF_TextSampleDescriptor td; + u32 idx; + memset(&td, 0, sizeof(GF_TextSampleDescriptor)); + td.tag = GF_ODF_TEXT_CFG_TAG; + td.vert_justif = (s8) -1; + td.default_style.fontID = 1; + td.default_style.font_size = TTXT_DEFAULT_FONT_SIZE; + + k=0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(sdesc->attributes, &k))) { + if (!strcmp(att->name, "horizontalJustification")) { + if (!stricmp(att->value, "center")) td.horiz_justif = 1; + else if (!stricmp(att->value, "right")) td.horiz_justif = (s8) -1; + else if (!stricmp(att->value, "left")) td.horiz_justif = 0; + } + else if (!strcmp(att->name, "verticalJustification")) { + if (!stricmp(att->value, "center")) td.vert_justif = 1; + else if (!stricmp(att->value, "bottom")) td.vert_justif = (s8) -1; + else if (!stricmp(att->value, "top")) td.vert_justif = 0; + } + else if (!strcmp(att->name, "backColor")) td.back_color = ttxt_get_color(import, att->value); + else if (!strcmp(att->name, "verticalText") && !stricmp(att->value, "yes") ) td.displayFlags |= GF_TXT_VERTICAL; + else if (!strcmp(att->name, "fillTextRegion") && !stricmp(att->value, "yes") ) td.displayFlags |= GF_TXT_FILL_REGION; + else if (!strcmp(att->name, "continuousKaraoke") && !stricmp(att->value, "yes") ) td.displayFlags |= GF_TXT_KARAOKE; + else if (!strcmp(att->name, "scroll")) { + if (!stricmp(att->value, "inout")) td.displayFlags |= GF_TXT_SCROLL_IN | GF_TXT_SCROLL_OUT; + else if (!stricmp(att->value, "in")) td.displayFlags |= GF_TXT_SCROLL_IN; + else if (!stricmp(att->value, "out")) td.displayFlags |= GF_TXT_SCROLL_OUT; + } + else if (!strcmp(att->name, "scrollMode")) { + u32 scroll_mode = GF_TXT_SCROLL_CREDITS; + if (!stricmp(att->value, "Credits")) scroll_mode = GF_TXT_SCROLL_CREDITS; + else if (!stricmp(att->value, "Marquee")) scroll_mode = GF_TXT_SCROLL_MARQUEE; + else if (!stricmp(att->value, "Right")) scroll_mode = GF_TXT_SCROLL_RIGHT; + else if (!stricmp(att->value, "Down")) scroll_mode = GF_TXT_SCROLL_DOWN; + td.displayFlags |= ((scroll_mode<<7) & GF_TXT_SCROLL_DIRECTION); + } + } + + k=0; + while ( (ext=(GF_XMLNode*)gf_list_enum(sdesc->content, &k))) { + if (ext->type) continue; + if (!strcmp(ext->name, "TextBox")) ttxt_parse_text_box(import, ext, &td.default_pos); + else if (!strcmp(ext->name, "Style")) ttxt_parse_text_style(import, ext, &td.default_style); + else if (!strcmp(ext->name, "FontTable")) { + GF_XMLNode *ftable; + u32 z=0; + while ( (ftable=(GF_XMLNode*)gf_list_enum(ext->content, &z))) { + u32 m; + if (ftable->type || strcmp(ftable->name, "FontTableEntry")) continue; + td.font_count += 1; + td.fonts = (GF_FontRecord*)realloc(td.fonts, sizeof(GF_FontRecord)*td.font_count); + m=0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(ftable->attributes, &m))) { + if (!stricmp(att->name, "fontID")) td.fonts[td.font_count-1].fontID = atoi(att->value); + else if (!stricmp(att->name, "fontName")) td.fonts[td.font_count-1].fontName = strdup(att->value); + } + } + } + } + if (import->flags & GF_IMPORT_SKIP_TXT_BOX) { + td.default_pos.top = td.default_pos.left = td.default_pos.right = td.default_pos.bottom = 0; + } else { + if ((td.default_pos.bottom==td.default_pos.top) || (td.default_pos.right==td.default_pos.left)) { + td.default_pos.top = td.default_pos.left = 0; + td.default_pos.right = w; + td.default_pos.bottom = h; + } + } + if (!td.fonts) { + td.font_count = 1; + td.fonts = (GF_FontRecord*)malloc(sizeof(GF_FontRecord)); + td.fonts[0].fontID = 1; + td.fonts[0].fontName = strdup("Serif"); + } + gf_isom_new_text_description(import->dest, track, &td, NULL, NULL, &idx); + for (k=0; kname, "TextSample")) { + GF_ISOSample *s; + GF_TextSample * samp; + u32 ts, descIndex; + Bool has_text = 0; + if (!nb_descs) { + e = gf_import_message(import, GF_BAD_PARAM, "Invalid Timed Text file - text stream header not found or empty"); + goto exit; + } + samp = gf_isom_new_text_sample(); + ts = 0; + descIndex = 1; + last_sample_empty = 0; + + j=0; + while ( (att=(GF_XMLAttribute*)gf_list_enum(node->attributes, &j))) { + if (!strcmp(att->name, "sampleTime")) { + u32 h, m, s, ms; + if (sscanf(att->value, "%d:%d:%d.%d", &h, &m, &s, &ms) == 4) { + ts = (h*3600 + m*60 + s)*1000 + ms; + } else { + ts = (u32) (atof(att->value) * 1000); + } + } + else if (!strcmp(att->name, "sampleDescriptionIndex")) descIndex = atoi(att->value); + else if (!strcmp(att->name, "text")) { + u32 len; + char *str = ttxt_parse_string(import, att->value, 1); + len = strlen(str); + gf_isom_text_add_text(samp, str, len); + last_sample_empty = len ? 0 : 1; + has_text = 1; + } + else if (!strcmp(att->name, "scrollDelay")) gf_isom_text_set_scroll_delay(samp, (u32) (1000*atoi(att->value))); + else if (!strcmp(att->name, "highlightColor")) gf_isom_text_set_highlight_color_argb(samp, ttxt_get_color(import, att->value)); + else if (!strcmp(att->name, "wrap") && !strcmp(att->value, "Automatic")) gf_isom_text_set_wrap(samp, 0x01); + } + + /*get all modifiers*/ + j=0; + while ( (ext=(GF_XMLNode*)gf_list_enum(node->content, &j))) { + if (!has_text && (ext->type==GF_XML_TEXT_TYPE)) { + u32 len; + char *str = ttxt_parse_string(import, ext->name, 0); + len = strlen(str); + gf_isom_text_add_text(samp, str, len); + last_sample_empty = len ? 0 : 1; + has_text = 1; + } + if (ext->type) continue; + + if (!stricmp(ext->name, "Style")) { + GF_StyleRecord r; + ttxt_parse_text_style(import, ext, &r); + gf_isom_text_add_style(samp, &r); + } + else if (!stricmp(ext->name, "TextBox")) { + GF_BoxRecord r; + ttxt_parse_text_box(import, ext, &r); + gf_isom_text_set_box(samp, r.top, r.left, r.bottom, r.right); + } + else if (!stricmp(ext->name, "Highlight")) { + u16 start, end; + start = end = 0; + k=0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(ext->attributes, &k))) { + if (!strcmp(att->name, "fromChar")) start = atoi(att->value); + else if (!strcmp(att->name, "toChar")) end = atoi(att->value); + } + gf_isom_text_add_highlight(samp, start, end); + } + else if (!stricmp(ext->name, "Blinking")) { + u16 start, end; + start = end = 0; + k=0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(ext->attributes, &k))) { + if (!strcmp(att->name, "fromChar")) start = atoi(att->value); + else if (!strcmp(att->name, "toChar")) end = atoi(att->value); + } + gf_isom_text_add_blink(samp, start, end); + } + else if (!stricmp(ext->name, "HyperLink")) { + u16 start, end; + char *url, *url_tt; + start = end = 0; + url = url_tt = NULL; + k=0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(ext->attributes, &k))) { + if (!strcmp(att->name, "fromChar")) start = atoi(att->value); + else if (!strcmp(att->name, "toChar")) end = atoi(att->value); + else if (!strcmp(att->name, "URL")) url = strdup(att->value); + else if (!strcmp(att->name, "URLToolTip")) url_tt = strdup(att->value); + } + gf_isom_text_add_hyperlink(samp, url, url_tt, start, end); + if (url) free(url); + if (url_tt) free(url_tt); + } + else if (!stricmp(ext->name, "Karaoke")) { + u32 startTime; + GF_XMLNode *krok; + startTime = 0; + k=0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(ext->attributes, &k))) { + if (!strcmp(att->name, "startTime")) startTime = (u32) (1000*atof(att->value)); + } + gf_isom_text_add_karaoke(samp, startTime); + k=0; + while ( (krok=(GF_XMLNode*)gf_list_enum(ext->content, &k))) { + u16 start, end; + u32 endTime, m; + if (krok->type) continue; + if (strcmp(krok->name, "KaraokeRange")) continue; + start = end = 0; + endTime = 0; + m=0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(krok->attributes, &m))) { + if (!strcmp(att->name, "fromChar")) start = atoi(att->value); + else if (!strcmp(att->name, "toChar")) end = atoi(att->value); + else if (!strcmp(att->name, "endTime")) endTime = (u32) (1000*atof(att->value)); + } + gf_isom_text_set_karaoke_segment(samp, endTime, start, end); + } + } + } + + /*in MP4 we must start at T=0, so add an empty sample*/ + if (ts && !nb_samples) { + GF_TextSample * firstsamp = gf_isom_new_text_sample(); + s = gf_isom_text_to_sample(firstsamp); + s->DTS = 0; + gf_isom_add_sample(import->dest, track, 1, s); + nb_samples++; + gf_isom_delete_text_sample(firstsamp); + gf_isom_sample_del(&s); + } + + s = gf_isom_text_to_sample(samp); + gf_isom_delete_text_sample(samp); + s->DTS = ts; + if (last_sample_empty) { + last_sample_duration = s->DTS - last_sample_duration; + } else { + last_sample_duration = s->DTS; + } + + e = gf_isom_add_sample(import->dest, track, descIndex, s); + if (e) goto exit; + gf_isom_sample_del(&s); + nb_samples++; + + gf_set_progress("Importing TTXT", nb_samples, nb_children); + if (import->duration && (ts>import->duration)) break; + } + } + if (last_sample_empty) { + gf_isom_remove_sample(import->dest, track, nb_samples); + gf_isom_set_last_sample_duration(import->dest, track, (u32) last_sample_duration); + } + gf_set_progress("Importing TTXT", nb_samples, nb_samples); + +exit: + gf_xml_dom_del(parser); + return e; +} + + +u32 tx3g_get_color(GF_MediaImporter *import, char *value) +{ + u32 r, g, b, a; + u32 res, v; + r = g = b = a = 0; + if (sscanf(value, "%d%%, %d%%, %d%%, %d%%", &r, &g, &b, &a) != 4) { + gf_import_message(import, GF_OK, "Warning: color badly formatted"); + } + v = (u32) (a*255/100); + res = (v&0xFF); res<<=8; + v = (u32) (r*255/100); + res |= (v&0xFF); res<<=8; + v = (u32) (g*255/100); + res |= (v&0xFF); res<<=8; + v = (u32) (b*255/100); + res |= (v&0xFF); + return res; +} + +void tx3g_parse_text_box(GF_MediaImporter *import, GF_XMLNode *n, GF_BoxRecord *box) +{ + u32 i=0; + GF_XMLAttribute *att; + memset(box, 0, sizeof(GF_BoxRecord)); + while ((att=(GF_XMLAttribute *)gf_list_enum(n->attributes, &i))) { + if (!stricmp(att->name, "x")) box->left = atoi(att->value); + else if (!stricmp(att->name, "y")) box->top = atoi(att->value); + else if (!stricmp(att->name, "height")) box->bottom = atoi(att->value); + else if (!stricmp(att->name, "width")) box->right = atoi(att->value); + } +} + +typedef struct +{ + u32 id; + u32 pos; +} Marker; + +#define GET_MARKER_POS(_val, __isend) \ + { \ + u32 i, __m = atoi(att->value); \ + _val = 0; \ + for (i=0; iflags==GF_IMPORT_PROBE_ONLY) return GF_OK; + + parser = gf_xml_dom_new(); + e = gf_xml_dom_parse(parser, import->in_name, texml_import_progress, import); + if (e) { + gf_import_message(import, e, "Error parsing TeXML file: Line %d - %s", gf_xml_dom_get_line(parser), gf_xml_dom_get_error(parser)); + gf_xml_dom_del(parser); + return e; + } + root = gf_xml_dom_get_root(parser); + + if (strcmp(root->name, "text3GTrack")) { + e = gf_import_message(import, GF_BAD_PARAM, "Invalid QT TeXML file - expecting root \"text3GTrack\" got \"%s\"", root->name); + goto exit; + } + w = TTXT_DEFAULT_WIDTH; + h = TTXT_DEFAULT_HEIGHT; + tx = ty = 0; + layer = 0; + timescale = 1000; + i=0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(root->attributes, &i))) { + if (!strcmp(att->name, "trackWidth")) w = atoi(att->value); + else if (!strcmp(att->name, "trackHeight")) h = atoi(att->value); + else if (!strcmp(att->name, "layer")) layer = atoi(att->value); + else if (!strcmp(att->name, "timescale")) timescale = atoi(att->value); + else if (!strcmp(att->name, "transform")) { + Float fx, fy; + sscanf(att->value, "translate(%f,%f)", &fx, &fy); + tx = (u32) fx; ty = (u32) fy; + } + } + + /*setup track in 3GP format directly (no ES desc)*/ + ID = (import->esd) ? import->esd->ESID : 0; + track = gf_isom_new_track(import->dest, ID, GF_ISOM_MEDIA_TEXT, timescale); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + /*some MPEG-4 setup*/ + if (import->esd) { + if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->timestampResolution = timescale; + import->esd->decoderConfig->streamType = GF_STREAM_TEXT; + import->esd->decoderConfig->objectTypeIndication = 0x08; + if (import->esd->OCRESID) gf_isom_set_track_reference(import->dest, track, GF_ISOM_REF_OCR, import->esd->OCRESID); + } + DTS = 0; + gf_isom_set_track_layout_info(import->dest, track, w<<16, h<<16, tx<<16, ty<<16, (s16) layer); + + gf_text_import_set_language(import, track); + e = GF_OK; + + gf_import_message(import, GF_OK, "Timed Text (QT TeXML) Import - Track Size %d x %d", w, h); + + nb_children = gf_list_count(root->content); + nb_descs = 0; + nb_samples = 0; + i=0; + while ( (node=(GF_XMLNode*)gf_list_enum(root->content, &i))) { + GF_XMLNode *desc; + GF_TextSampleDescriptor td; + GF_TextSample * samp = NULL; + GF_ISOSample *s; + u32 duration, descIndex, nb_styles, nb_marks; + Bool isRAP, same_style, same_box; + + if (node->type) continue; + if (strcmp(node->name, "sample")) continue; + + isRAP = 0; + duration = 1000; + j=0; + while ((att=(GF_XMLAttribute *)gf_list_enum(node->attributes, &j))) { + if (!strcmp(att->name, "duration")) duration = atoi(att->value); + else if (!strcmp(att->name, "keyframe")) isRAP = !stricmp(att->value, "true"); + } + nb_styles = 0; + nb_marks = 0; + same_style = same_box = 0; + descIndex = 1; + j=0; + while ((desc=(GF_XMLNode*)gf_list_enum(node->content, &j))) { + if (desc->type) continue; + + if (!strcmp(desc->name, "description")) { + GF_XMLNode *sub; + memset(&td, 0, sizeof(GF_TextSampleDescriptor)); + td.tag = GF_ODF_TEXT_CFG_TAG; + td.vert_justif = (s8) -1; + td.default_style.fontID = 1; + td.default_style.font_size = TTXT_DEFAULT_FONT_SIZE; + + k=0; + while ((att=(GF_XMLAttribute *)gf_list_enum(desc->attributes, &k))) { + if (!strcmp(att->name, "horizontalJustification")) { + if (!stricmp(att->value, "center")) td.horiz_justif = 1; + else if (!stricmp(att->value, "right")) td.horiz_justif = (s8) -1; + else if (!stricmp(att->value, "left")) td.horiz_justif = 0; + } + else if (!strcmp(att->name, "verticalJustification")) { + if (!stricmp(att->value, "center")) td.vert_justif = 1; + else if (!stricmp(att->value, "bottom")) td.vert_justif = (s8) -1; + else if (!stricmp(att->value, "top")) td.vert_justif = 0; + } + else if (!strcmp(att->name, "backgroundColor")) td.back_color = tx3g_get_color(import, att->value); + else if (!strcmp(att->name, "displayFlags")) { + Bool rev_scroll = 0; + if (strstr(att->value, "scroll")) { + u32 scroll_mode = 0; + if (strstr(att->value, "scrollIn")) td.displayFlags |= GF_TXT_SCROLL_IN; + if (strstr(att->value, "scrollOut")) td.displayFlags |= GF_TXT_SCROLL_OUT; + if (strstr(att->value, "reverse")) rev_scroll = 1; + if (strstr(att->value, "horizontal")) scroll_mode = rev_scroll ? GF_TXT_SCROLL_RIGHT : GF_TXT_SCROLL_MARQUEE; + else scroll_mode = (rev_scroll ? GF_TXT_SCROLL_DOWN : GF_TXT_SCROLL_CREDITS); + td.displayFlags |= (scroll_mode<<7) & GF_TXT_SCROLL_DIRECTION; + } + /*TODO FIXME: check in QT doc !!*/ + if (strstr(att->value, "writeTextVertically")) td.displayFlags |= GF_TXT_VERTICAL; + if (!strcmp(att->name, "continuousKaraoke")) td.displayFlags |= GF_TXT_KARAOKE; + } + } + + k=0; + while ((sub=(GF_XMLNode*)gf_list_enum(desc->content, &k))) { + if (sub->type) continue; + if (!strcmp(sub->name, "defaultTextBox")) tx3g_parse_text_box(import, sub, &td.default_pos); + else if (!strcmp(sub->name, "fontTable")) { + GF_XMLNode *ftable; + u32 m=0; + while ((ftable=(GF_XMLNode*)gf_list_enum(sub->content, &m))) { + if (ftable->type) continue; + if (!strcmp(ftable->name, "font")) { + u32 n=0; + td.font_count += 1; + td.fonts = (GF_FontRecord*)realloc(td.fonts, sizeof(GF_FontRecord)*td.font_count); + while ((att=(GF_XMLAttribute *)gf_list_enum(ftable->attributes, &n))) { + if (!stricmp(att->name, "id")) td.fonts[td.font_count-1].fontID = atoi(att->value); + else if (!stricmp(att->name, "name")) td.fonts[td.font_count-1].fontName = strdup(att->value); + } + } + } + } + else if (!strcmp(sub->name, "sharedStyles")) { + u32 idx = 0; + GF_XMLNode *style, *ftable; + u32 m=0; + while ((style=(GF_XMLNode*)gf_list_enum(sub->content, &m))) { + if (style->type) continue; + if (!strcmp(style->name, "style")) break; + } + if (style) { + char *cur; + s32 start=0; + char css_style[1024], css_val[1024]; + memset(&styles[nb_styles], 0, sizeof(GF_StyleRecord)); + m=0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(style->attributes, &m))) { + if (!strcmp(att->name, "id")) styles[nb_styles].startCharOffset = atoi(att->value); + } + m=0; + while ( (ftable=(GF_XMLNode*)gf_list_enum(style->content, &m))) { + if (ftable->type) break; + } + cur = ftable->name; + while (cur) { + start = gf_token_get_strip(cur, 0, "{:", " ", css_style, 1024); + if (start <0) break; + start = gf_token_get_strip(cur, start, ":}", " ", css_val, 1024); + if (start <0) break; + cur = strchr(cur+start, '{'); + + if (!strcmp(css_style, "font-table")) { + u32 z; + styles[nb_styles].fontID = atoi(css_val); + for (z=0; zdest, track, &td, &descIndex, &same_box, &same_style); + if (!descIndex) { + gf_isom_new_text_description(import->dest, track, &td, NULL, NULL, &descIndex); + same_style = same_box = 1; + } + + for (k=0; kname, "sampleData")) { + Bool is_utf16 = 0; + GF_XMLNode *sub; + u16 start, end; + u32 styleID; + u32 nb_chars, txt_len, m; + txt_len = nb_chars = 0; + + samp = gf_isom_new_text_sample(); + + k=0; + while ((att=(GF_XMLAttribute *)gf_list_enum(desc->attributes, &k))) { + if (!strcmp(att->name, "targetEncoding") && !strcmp(att->value, "utf16")) is_utf16 = 1; + else if (!strcmp(att->name, "scrollDelay")) gf_isom_text_set_scroll_delay(samp, atoi(att->value) ); + else if (!strcmp(att->name, "highlightColor")) gf_isom_text_set_highlight_color_argb(samp, tx3g_get_color(import, att->value)); + } + start = end = 0; + k=0; + while ((sub=(GF_XMLNode*)gf_list_enum(desc->content, &k))) { + if (sub->type) continue; + if (!strcmp(sub->name, "text")) { + GF_XMLNode *text; + styleID = 0; + m=0; + while ((att=(GF_XMLAttribute *)gf_list_enum(sub->attributes, &m))) { + if (!strcmp(att->name, "styleID")) styleID = atoi(att->value); + } + txt_len = 0; + + m=0; + while ((text=(GF_XMLNode*)gf_list_enum(sub->content, &m))) { + if (!text->type) { + if (!strcmp(text->name, "marker")) { + u32 z; + memset(&marks[nb_marks], 0, sizeof(Marker)); + marks[nb_marks].pos = nb_chars+txt_len; + + z = 0; + while ( (att=(GF_XMLAttribute *)gf_list_enum(text->attributes, &z))) { + if (!strcmp(att->name, "id")) marks[nb_marks].id = atoi(att->value); + } + nb_marks++; + } + } else if (text->type==GF_XML_TEXT_TYPE) { + txt_len += strlen(text->name); + gf_isom_text_add_text(samp, text->name, strlen(text->name)); + } + } + if (styleID && (!same_style || (td.default_style.startCharOffset != styleID))) { + GF_StyleRecord st = td.default_style; + for (m=0; mname, "highlight")) { + m=0; + while ((att=(GF_XMLAttribute *)gf_list_enum(sub->attributes, &m))) { + if (!strcmp(att->name, "startMarker")) GET_MARKER_POS(start, 0) + else if (!strcmp(att->name, "endMarker")) GET_MARKER_POS(end, 1) + } + gf_isom_text_add_highlight(samp, start, end); + } + else if (!stricmp(sub->name, "blink")) { + m=0; + while ((att=(GF_XMLAttribute *)gf_list_enum(sub->attributes, &m))) { + if (!strcmp(att->name, "startMarker")) GET_MARKER_POS(start, 0) + else if (!strcmp(att->name, "endMarker")) GET_MARKER_POS(end, 1) + } + gf_isom_text_add_blink(samp, start, end); + } + else if (!stricmp(sub->name, "link")) { + char *url, *url_tt; + url = url_tt = NULL; + m=0; + while ((att=(GF_XMLAttribute *)gf_list_enum(sub->attributes, &m))) { + if (!strcmp(att->name, "startMarker")) GET_MARKER_POS(start, 0) + else if (!strcmp(att->name, "endMarker")) GET_MARKER_POS(end, 1) + else if (!strcmp(att->name, "URL") || !strcmp(att->name, "href")) url = strdup(att->value); + else if (!strcmp(att->name, "URLToolTip") || !strcmp(att->name, "altString")) url_tt = strdup(att->value); + } + gf_isom_text_add_hyperlink(samp, url, url_tt, start, end); + if (url) free(url); + if (url_tt) free(url_tt); + } + else if (!stricmp(sub->name, "karaoke")) { + u32 time = 0; + GF_XMLNode *krok; + m=0; + while ((att=(GF_XMLAttribute *)gf_list_enum(sub->attributes, &m))) { + if (!strcmp(att->name, "startTime")) time = atoi(att->value); + } + gf_isom_text_add_karaoke(samp, time); + m=0; + while ((krok=(GF_XMLNode*)gf_list_enum(sub->content, &m))) { + u32 u=0; + if (krok->type) continue; + if (strcmp(krok->name, "run")) continue; + start = end = 0; + while ((att=(GF_XMLAttribute *)gf_list_enum(krok->attributes, &u))) { + if (!strcmp(att->name, "startMarker")) GET_MARKER_POS(start, 0) + else if (!strcmp(att->name, "endMarker")) GET_MARKER_POS(end, 1) + else if (!strcmp(att->name, "duration")) time += atoi(att->value); + } + gf_isom_text_set_karaoke_segment(samp, time, start, end); + } + } + } + } + } + /*OK, let's add the sample*/ + if (samp) { + if (!same_box) gf_isom_text_set_box(samp, td.default_pos.top, td.default_pos.left, td.default_pos.bottom, td.default_pos.right); +// if (!same_style) gf_isom_text_add_style(samp, &td.default_style); + + s = gf_isom_text_to_sample(samp); + gf_isom_delete_text_sample(samp); + s->IsRAP = isRAP; + s->DTS = DTS; + gf_isom_add_sample(import->dest, track, descIndex, s); + gf_isom_sample_del(&s); + nb_samples++; + DTS += duration; + gf_set_progress("Importing TeXML", nb_samples, nb_children); + if (import->duration && (DTS*1000> timescale*import->duration)) break; + } + } + gf_isom_set_last_sample_duration(import->dest, track, 0); + gf_set_progress("Importing TeXML", nb_samples, nb_samples); + +exit: + gf_xml_dom_del(parser); + return e; +} + + +GF_Err gf_import_timed_text(GF_MediaImporter *import) +{ + GF_Err e; + u32 fmt; + e = gf_text_guess_format(import->in_name, &fmt); + if (e) return e; + if (!fmt) return GF_NOT_SUPPORTED; + if (import->flags & GF_IMPORT_PROBE_ONLY) { + if (fmt==GF_TEXT_IMPORT_SUB) import->flags |= GF_IMPORT_OVERRIDE_FPS; + return GF_OK; + } + switch (fmt) { + case GF_TEXT_IMPORT_SRT: return gf_text_import_srt(import); + case GF_TEXT_IMPORT_SUB: return gf_text_import_sub(import); + case GF_TEXT_IMPORT_TTXT: return gf_text_import_ttxt(import); + case GF_TEXT_IMPORT_TEXML: return gf_text_import_texml(import); + default: return GF_BAD_PARAM; + } +} + +#endif + diff --git a/src/media_tools/vobsub.c b/src/media_tools/vobsub.c new file mode 100644 index 0000000..5c99223 --- /dev/null +++ b/src/media_tools/vobsub.c @@ -0,0 +1,651 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) by Falco (Ivan Vecera) 2006 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +typedef struct _tag_lang_type +{ + char id[3]; + char lang[4]; +} lang_type; + +static lang_type lang_table[] = +{ + {"--", "und" }, + {"cc", "und" }, + {"aa", "aar" }, + {"ab", "abk" }, + {"af", "afr" }, + {"am", "amh" }, + {"ar", "ara" }, + {"as", "ast" }, + {"ay", "aym" }, + {"az", "aze" }, + {"ba", "bak" }, + {"be", "bel" }, + {"bg", "bul" }, + {"bh", "bih" }, + {"bi", "bis" }, + {"bn", "ben" }, + {"bo", "tib" }, + {"br", "bre" }, + {"ca", "cat" }, + {"co", "cos" }, + {"cs", "cze" }, + {"cy", "wel" }, + {"da", "dan" }, + {"de", "ger" }, + {"dz", "dzo" }, + {"el", "gre" }, + {"en", "eng" }, + {"eo", "epo" }, + {"es", "spa" }, + {"et", "est" }, + {"eu", "baq" }, + {"fa", "per" }, + {"fi", "fin" }, + {"fj", "fij" }, + {"fo", "fao" }, + {"fr", "fre" }, + {"fy", "fry" }, + {"ga", "gle" }, + {"gl", "glg" }, + {"gn", "grn" }, + {"gu", "guj" }, + {"ha", "hau" }, + {"he", "heb" }, + {"hi", "hin" }, + {"hr", "scr" }, + {"hu", "hun" }, + {"hy", "arm" }, + {"ia", "ina" }, + {"id", "ind" }, + {"ik", "ipk" }, + {"is", "ice" }, + {"it", "ita" }, + {"iu", "iku" }, + {"ja", "jpn" }, + {"jv", "jav" }, + {"ka", "geo" }, + {"kk", "kaz" }, + {"kl", "kal" }, + {"km", "khm" }, + {"kn", "kan" }, + {"ko", "kor" }, + {"ks", "kas" }, + {"ku", "kur" }, + {"ky", "kir" }, + {"la", "lat" }, + {"ln", "lin" }, + {"lo", "lao" }, + {"lt", "lit" }, + {"lv", "lav" }, + {"mg", "mlg" }, + {"mi", "mao" }, + {"mk", "mac" }, + {"ml", "mlt" }, + {"mn", "mon" }, + {"mo", "mol" }, + {"mr", "mar" }, + {"ms", "may" }, + {"my", "bur" }, + {"na", "nau" }, + {"ne", "nep" }, + {"nl", "dut" }, + {"no", "nor" }, + {"oc", "oci" }, + {"om", "orm" }, + {"or", "ori" }, + {"pa", "pan" }, + {"pl", "pol" }, + {"ps", "pus" }, + {"pt", "por" }, + {"qu", "que" }, + {"rm", "roh" }, + {"rn", "run" }, + {"ro", "rum" }, + {"ru", "rus" }, + {"rw", "kin" }, + {"sa", "san" }, + {"sd", "snd" }, + {"sg", "sag" }, + {"sh", "scr" }, + {"si", "sin" }, + {"sk", "slo" }, + {"sl", "slv" }, + {"sm", "smo" }, + {"sn", "sna" }, + {"so", "som" }, + {"sq", "alb" }, + {"sr", "srp" }, + {"ss", "ssw" }, + {"st", "sot" }, + {"su", "sun" }, + {"sv", "swe" }, + {"sw", "swa" }, + {"ta", "tam" }, + {"te", "tel" }, + {"tg", "tgk" }, + {"th", "tha" }, + {"ti", "tir" }, + {"tk", "tuk" }, + {"tl", "tgl" }, + {"tn", "tsn" }, + {"to", "tog" }, + {"tr", "tur" }, + {"ts", "tso" }, + {"tt", "tat" }, + {"tw", "twi" }, + {"ug", "uig" }, + {"uk", "ukr" }, + {"ur", "urd" }, + {"uz", "uzb" }, + {"vi", "vie" }, + {"vo", "vol" }, + {"wo", "wol" }, + {"xh", "xho" }, + {"yi", "yid" }, + {"yo", "yor" }, + {"za", "zha" }, + {"zh", "chi" }, + {"zu", "zul" } +}; + +s32 vobsub_lang_name(u16 id) +{ + u16 lang_id; + s32 i, count; + + count = (sizeof(lang_table) / sizeof(lang_table[0])); + + for (i = 0; i < count; i++) { + lang_id = (lang_table[i].id[0]<<8) | lang_table[i].id[1]; + + if (id == lang_id) { + return i; + } + } + + return 0; /* Undefined - und */ +} + +char *vobsub_lang_id(char *name) +{ + s32 i, count; + + count = (sizeof(lang_table) / sizeof(lang_table[0])); + + for (i = 0; i < count; i++) { + if (!stricmp(lang_table[i].lang, name)) { + return lang_table[i].id; + } + } + + return "--"; /* Undefined */ +} + +static char *strltrim(char *str) +{ + if (str == NULL) { + return NULL; + } + + while (*str) { + if (!isspace(*str)) { + return str; + } + str++; + } + + return str; +} + +static char *strrtrim(char *str) +{ + char *end; + + if (str == NULL) { + return NULL; + } + + end = str + strlen(str); + + while (end-- > str) { + if (!isspace(*end)) { + return str; + } + *end = '\0'; + } + + return str; +} + +static char *strtrim(char *str) +{ + return strltrim(strrtrim(str)); +} + +GF_Err vobsub_read_idx(FILE *file, vobsub_file *vobsub, s32 *version) +{ + char strbuf[256]; + char *str, *pos, *entry; + s32 line, id =-1, delay = 0; + Bool error = 0; + + for (line = 0; !error && fgets(strbuf, sizeof(strbuf), file); line++) + { + str = strtrim(strbuf); + + if (line == 0) + { + char *buf = "VobSub index file, v"; + + pos = strstr(str, buf); + if (pos == NULL || sscanf(pos + strlen(buf), "%d", version) != 1 || *version > VOBSUBIDXVER) + { + error = 1; + continue; + } + } + else if (strlen(str) == 0) + { + continue; + } + else if (str[0] == '#') + { + continue; + } + + pos = strchr(str, ':'); + if (pos == NULL || pos == str) + { + continue; + } + + entry = str; + *pos = '\0'; + + str = strtrim(pos + 1); + if (strlen(str) == 0) + { + continue; + } + + if (stricmp(entry, "size") == 0) + { + s32 w, h; + if (sscanf(str, "%dx%d", &w, &h) != 2) + { + error = 1; + } + vobsub->width = w; + vobsub->height = h; + } + else if (stricmp(entry, "palette") == 0) + { + s32 c; + u8 palette[16][4]; + + if (sscanf(str, "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", + (u32 *) &palette[0], (u32 *) &palette[1], (u32 *) &palette[2], (u32 *) &palette[3], + (u32 *) &palette[4], (u32 *) &palette[5], (u32 *) &palette[6], (u32 *) &palette[7], + (u32 *) &palette[8], (u32 *) &palette[9], (u32 *) &palette[10], (u32 *) &palette[11], + (u32 *) &palette[12],(u32 *) &palette[13],(u32 *) &palette[14], (u32 *) &palette[15]) != 16) + { + error = 1; + continue; + } + + for (c = 0; c < 16; c++) + { + u8 r, g, b; + + r = palette[c][2]; + g = palette[c][1]; + b = palette[c][0]; + vobsub->palette[c][0] = 0; + vobsub->palette[c][1] = (( 66 * r + 129 * g + 25 * b + 128 + 4096) >> 8) & 0xff; + vobsub->palette[c][2] = ((112 * r - 94 * g - 18 * b + 128 + 32768) >> 8) & 0xff; + vobsub->palette[c][3] = ((-38 * r - 74 * g + 112 * b + 128 + 32768) >> 8) & 0xff; + } + } + else if (stricmp(entry, "id") == 0) + { + char *buf = "index:"; + s32 lang_id; + + strlwr(str); + lang_id = ((str[0] & 0xff) << 8) | (str[1] & 0xff); + + pos = strstr(str, buf); + if (pos == NULL) + { + error = 1; + continue; + } + + if (sscanf(pos + strlen(buf), "%d", &id) != 1 || id < 0 || id >= 32) + { + error = 1; + continue; + } + + vobsub->langs[id].id = lang_id; + vobsub->langs[id].name = lang_table[vobsub_lang_name((u16)lang_id)].lang; + + vobsub->langs[id].subpos = gf_list_new(); + if (vobsub->langs[id].subpos == NULL) + { + error = 1; + continue; + } + + delay = 0; + vobsub->num_langs++; + } + else if (id >= 0 && stricmp(entry, "delay") == 0) + { + s32 hh, mm, ss, ms; + char c; + s32 sign = (str[0] == '-') ? -1 : 1; + + pos = str; + while (*pos == '-' || *pos == '+') pos++; + + if (sscanf(pos, "%d%c%d%c%d%c%d", &hh, &c, &mm, &c, &ss, &c, &ms) != 7) + { + error = 1; + continue; + } + + delay += (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * sign; + } + else if (id >= 0 && stricmp(entry, "timestamp") == 0) + { + vobsub_pos *vspos; + s32 sign; + char c; + s32 hh, mm, ss, ms; + char *buf = "filepos:"; + + vspos = (vobsub_pos*)calloc(1, sizeof(vobsub_pos)); + if (vspos == NULL) { + error = 1; + continue; + } + + sign = (str[0] == '-') ? -1 : 1; + while (*str == '-' || *str == '+') str++; + + if (sscanf(str, "%d%c%d%c%d%c%d", &hh, &c, &mm, &c, &ss, &c, &ms) != 7) + { + free(vspos); + error = 1; + continue; + } + + vspos->start = (((hh*60 + mm)*60 + ss)*1000 + ms) * sign + delay; + + pos = strstr(str, buf); + if (pos == NULL) + { + free(vspos); + error = 1; + continue; + } + +#if defined (WIN32) && !defined(__GNUC__) + if (sscanf(pos + strlen(buf), "%I64x", &vspos->filepos) != 1) +#else + if (sscanf(pos + strlen(buf), "%llx", &vspos->filepos) != 1) +#endif + { + free(vspos); + error = 1; + continue; + } + + if (delay < 0 && gf_list_count(vobsub->langs[id].subpos) > 0) + { + vobsub_pos *pos; + + pos = (vobsub_pos*)gf_list_get(vobsub->langs[id].subpos, gf_list_count(vobsub->langs[id].subpos) - 1); + if (vspos->start < pos->start) + { + delay += (s32)(pos->start - vspos->start); + vspos->start = pos->start; + } + } + + if (gf_list_add(vobsub->langs[id].subpos, vspos) != GF_OK) + { + free(vspos); + error = 1; + continue; + } + } + } + + return error ? GF_CORRUPTED_DATA : GF_OK; +} + +void vobsub_free(vobsub_file *vobsub) +{ + s32 c; + + if (vobsub == NULL) + { + return; + } + + for (c = 0; c < 32; c++) + { + if (vobsub->langs[c].subpos) + { + GF_List *list = vobsub->langs[c].subpos; + vobsub_pos *vspos; + u32 pos = 0; + + do + { + vspos = (vobsub_pos*)gf_list_enum(list, &pos); + free(vspos); + } + while (vspos != NULL); + } + } +} + +GF_Err vobsub_get_subpic_duration(char *_data, u32 psize, u32 dsize, u32 *duration) +{ + u32 i, dcsq_stm, nxt_dcsq, start_stm, stop_stm; + unsigned char *data = (unsigned char *)_data; + start_stm = 0; + stop_stm = 0; + nxt_dcsq = dsize; + + do { + i = nxt_dcsq; + dcsq_stm = (data[i+0] << 8) | data[i+1]; + nxt_dcsq = (data[i+2] << 8) | data[i+3]; + i += 4; + + if (nxt_dcsq > psize || nxt_dcsq < dsize) { + return GF_CORRUPTED_DATA; + } + + while (1) { + u8 cmd; + int len; + + cmd = data[i++]; + switch (cmd) + { + case 0x00: len = 0; break; + case 0x01: len = 0; break; + case 0x02: len = 0; break; + case 0x03: len = 2; break; + case 0x04: len = 2; break; + case 0x05: len = 6; break; + case 0x06: len = 4; break; + default: len = 0; break; + } + + if (i + len > psize) { + return GF_CORRUPTED_DATA; + } + + i += len; + + if (cmd == 0x00 || cmd == 0x01) { + /* start normal or forced displaying */ + start_stm = dcsq_stm * 1024; + } else if (cmd == 0x02) { + /* stop displaying */ + stop_stm = dcsq_stm * 1024; + } else if (cmd > 0x06) { + /* unknown command or end of control block */ + break; + } + } + } while (i <= nxt_dcsq && i < psize); + + *duration = stop_stm - start_stm; + + return GF_OK; +} + +GF_Err vobsub_packetize_subpicture(FILE *fsub, u64 pts, char *data, u32 dataSize) +{ + u8 buf[0x800], ptsbuf[5]; + u8 *p; + int put_pts = 1; + + /* Build PTS buffer */ + ptsbuf[0] = (u8)(((pts >> 29) & 0x0e) | 0x21); + ptsbuf[1] = (u8)(((pts >> 22) & 0xff)); + ptsbuf[2] = (u8)(((pts >> 14) & 0xfe) | 0x01); + ptsbuf[3] = (u8)(((pts >> 7) & 0xff)); + ptsbuf[4] = (u8)(((pts << 1) & 0xfe) | 0x01); + + while (dataSize > 0) + { + u32 padLen = 0; + u32 dataLen = sizeof(buf); + u32 packLen; + + /* Zerofill packet */ + memset(buf, 0, sizeof(buf)); + p = buf; + + /* Put pack header */ + *p++ = 0x00; + *p++ = 0x00; + *p++ = 0x01; + *p++ = 0xba; + *p++ = 0x40; + + /* Jump to PES header */ + p += 9; + + /* Put PES header */ + *p++ = 0x00; + *p++ = 0x00; + *p++ = 0x01; + *p++ = 0xbd; + + /* Compute max size of content */ + dataLen -= 14; /* Pack header */ + dataLen -= 4; /* Start code + Stream ID */ + dataLen -= 2; /* PES packet size */ + dataLen -= 3; /* PES header extension */ + dataLen -= put_pts ? 5 : 0; /* PTS */ + dataLen -= 1; /* Substream ID */ + + /* Check if the subpicture data fits in packet */ + if (dataSize <= dataLen) { + padLen = dataLen - dataSize; + dataLen = dataSize; + } + + /* Compute and put packet size (PES header extension + PTS + Substream ID + data + padding) */ + packLen = 3 + (put_pts ? 5 : 0) + 1 + dataLen + ((padLen < 6) ? padLen : 0); + *p++ = (packLen >> 8) & 0xff; + *p++ = packLen & 0xff; + + /* Put PES header extension */ + *p++ = 0x80; + *p++ = put_pts ? 0x80 : 0x00; + *p++ = (put_pts ? 5 : 0) + ((padLen < 6) ? padLen : 0); + + /* Put PTS */ + if (put_pts) { + *p++ = ptsbuf[0]; + *p++ = ptsbuf[1]; + *p++ = ptsbuf[2]; + *p++ = ptsbuf[3]; + *p++ = ptsbuf[4]; + } + + /* Skip padding bytes */ + if (padLen < 6) { + p += padLen; + } + + /* Put Substream ID */ + *p++ = 0x20; + + /* Copy data into packet buffer */ + memcpy(p, data, dataLen); + p += dataLen; + + /* Put padding bytes if padding len >= 6 */ + if (padLen >= 6) { + padLen -= 6; + *p++ = 0x00; + *p++ = 0x00; + *p++ = 0x01; + *p++ = 0xbe; + *p++ = (padLen >> 8) & 0xff; + *p++ = padLen & 0xff; + memset(p, 0, padLen); + } + + /* Write packet into file */ + if (fwrite(buf, sizeof(buf), 1, fsub) != 1) { + return GF_IO_ERR; + } + + /* Move data pointer... */ + data += dataLen; + dataSize -= dataLen; + + /* Next packet (if any) will not contain PTS */ + put_pts = 0; + } + + return GF_OK; +} diff --git a/src/odf/desc_private.c b/src/odf/desc_private.c new file mode 100644 index 0000000..f6b0f57 --- /dev/null +++ b/src/odf/desc_private.c @@ -0,0 +1,680 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + + +// +// CONSTRUCTORS +// +GF_Descriptor *gf_odf_create_descriptor(u8 tag) +{ + GF_Descriptor *desc; + + switch (tag) { + case GF_ODF_IOD_TAG: + return gf_odf_new_iod(); + case GF_ODF_OD_TAG: + return gf_odf_new_od(); + case GF_ODF_ESD_TAG: + return gf_odf_new_esd(); + case GF_ODF_DCD_TAG: + return gf_odf_new_dcd(); + case GF_ODF_SLC_TAG: + //default : we create it without any predefinition... + return gf_odf_new_slc(0); + case GF_ODF_CI_TAG: + return gf_odf_new_ci(); + case GF_ODF_SCI_TAG: + return gf_odf_new_sup_cid(); + case GF_ODF_IPI_PTR_TAG: + return gf_odf_new_ipi_ptr(); + //special case for the file format + case GF_ODF_ISOM_IPI_PTR_TAG: + desc = gf_odf_new_ipi_ptr(); + if (!desc) return desc; + desc->tag = GF_ODF_ISOM_IPI_PTR_TAG; + return desc; + + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_new_ipmp_ptr(); + case GF_ODF_IPMP_TAG: + return gf_odf_new_ipmp(); + case GF_ODF_QOS_TAG: + return gf_odf_new_qos(); + case GF_ODF_REG_TAG: + return gf_odf_new_reg(); + case GF_ODF_CC_TAG: + return gf_odf_new_cc(); + case GF_ODF_KW_TAG: + return gf_odf_new_kw(); + case GF_ODF_RATING_TAG: + return gf_odf_new_rating(); + case GF_ODF_LANG_TAG: + return gf_odf_new_lang(); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_new_short_text(); + case GF_ODF_TEXT_TAG: + return gf_odf_new_exp_text(); + case GF_ODF_CC_NAME_TAG: + return gf_odf_new_cc_name(); + case GF_ODF_CC_DATE_TAG: + return gf_odf_new_cc_date(); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_new_oci_name(); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_new_oci_date(); + case GF_ODF_SMPTE_TAG: + return gf_odf_new_smpte_camera(); + case GF_ODF_EXT_PL_TAG: + return gf_odf_new_pl_ext(); + case GF_ODF_PL_IDX_TAG: + return gf_odf_new_pl_idx(); + + //File Format Specific + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_new_isom_iod(); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_new_isom_od(); + case GF_ODF_ESD_INC_TAG: + return gf_odf_new_esd_inc(); + case GF_ODF_ESD_REF_TAG: + return gf_odf_new_esd_ref(); + + case GF_ODF_SEGMENT_TAG: + return gf_odf_new_segment(); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_new_mediatime(); + + case GF_ODF_IPMP_TL_TAG: + return gf_odf_new_ipmp_tool_list(); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_new_ipmp_tool(); + + case GF_ODF_MUXINFO_TAG: + return gf_odf_new_muxinfo(); + case GF_ODF_BIFS_CFG_TAG: + return gf_odf_new_bifs_cfg(); + case GF_ODF_UI_CFG_TAG: + return gf_odf_new_ui_cfg(); + case GF_ODF_TEXT_CFG_TAG: + return gf_odf_new_text_cfg(); + case GF_ODF_TX3G_TAG: + return gf_odf_new_tx3g(); + case GF_ODF_ELEM_MASK_TAG: + return gf_odf_New_ElemMask(); + case GF_ODF_LASER_CFG_TAG: + return gf_odf_new_laser_cfg(); + + //default. The DecSpecInfo is handled as default + //the appropriate decoder (audio, video, bifs...) has to decode the DecSpecInfo alone ! + case GF_ODF_DSI_TAG: + desc = gf_odf_new_default(); + if (!desc) return desc; + desc->tag = GF_ODF_DSI_TAG; + return desc; + + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_new_auxvid(); + + case 0: + case 0xFF: + return NULL; + default: + //ISO Reserved + if ( (tag >= GF_ODF_ISO_RES_BEGIN_TAG) && + (tag <= GF_ODF_ISO_RES_END_TAG) ) { + return NULL; + } + desc = gf_odf_new_default(); + if (!desc) return desc; + desc->tag = tag; + return desc; + } +} + +// +// DESTRUCTORS +// +GF_Err gf_odf_delete_descriptor(GF_Descriptor *desc) +{ + switch (desc->tag) { + case GF_ODF_IOD_TAG : + return gf_odf_del_iod((GF_InitialObjectDescriptor *)desc); + case GF_ODF_ESD_TAG : + return gf_odf_del_esd((GF_ESD *)desc); + case GF_ODF_DCD_TAG : + return gf_odf_del_dcd((GF_DecoderConfig *)desc); + case GF_ODF_SLC_TAG: + return gf_odf_del_slc((GF_SLConfig *)desc); + case GF_ODF_CC_TAG: + return gf_odf_del_cc((GF_CCDescriptor *)desc); + case GF_ODF_CC_DATE_TAG: + return gf_odf_del_cc_date((GF_CC_Date *)desc); + case GF_ODF_CC_NAME_TAG: + return gf_odf_del_cc_name((GF_CC_Name *)desc); + case GF_ODF_CI_TAG: + return gf_odf_del_ci((GF_CIDesc *)desc); + case GF_ODF_ESD_INC_TAG: + return gf_odf_del_esd_inc((GF_ES_ID_Inc *)desc); + case GF_ODF_ESD_REF_TAG: + return gf_odf_del_esd_ref((GF_ES_ID_Ref *)desc); + case GF_ODF_TEXT_TAG: + return gf_odf_del_exp_text((GF_ExpandedTextual *)desc); + case GF_ODF_EXT_PL_TAG: + return gf_odf_del_pl_ext((GF_PLExt *)desc); + case GF_ODF_IPI_PTR_TAG: + case GF_ODF_ISOM_IPI_PTR_TAG: + return gf_odf_del_ipi_ptr((GF_IPIPtr *)desc); + case GF_ODF_IPMP_TAG: + return gf_odf_del_ipmp((GF_IPMP_Descriptor *)desc); + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_del_ipmp_ptr((GF_IPMPPtr *)desc); + case GF_ODF_KW_TAG: + return gf_odf_del_kw((GF_KeyWord *)desc); + case GF_ODF_LANG_TAG: + return gf_odf_del_lang((GF_Language *)desc); + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_del_isom_iod((GF_IsomInitialObjectDescriptor *)desc); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_del_isom_od((GF_IsomObjectDescriptor *)desc); + case GF_ODF_OD_TAG: + return gf_odf_del_od((GF_ObjectDescriptor *)desc); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_del_oci_date((GF_OCI_Data *)desc); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_del_oci_name((GF_OCICreators *)desc); + case GF_ODF_PL_IDX_TAG: + return gf_odf_del_pl_idx((GF_PL_IDX *)desc); + case GF_ODF_QOS_TAG: + return gf_odf_del_qos((GF_QoS_Descriptor *)desc); + case GF_ODF_RATING_TAG: + return gf_odf_del_rating((GF_Rating *)desc); + case GF_ODF_REG_TAG: + return gf_odf_del_reg((GF_Registration *)desc); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_del_short_text((GF_ShortTextual *)desc); + case GF_ODF_SMPTE_TAG: + return gf_odf_del_smpte_camera((GF_SMPTECamera *)desc); + case GF_ODF_SCI_TAG: + return gf_odf_del_sup_cid((GF_SCIDesc *)desc); + + + case GF_ODF_SEGMENT_TAG: + return gf_odf_del_segment((GF_Segment *) desc); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_del_mediatime((GF_MediaTime *) desc); + + case GF_ODF_IPMP_TL_TAG: + return gf_odf_del_ipmp_tool_list((GF_IPMP_ToolList *)desc); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_del_ipmp_tool((GF_IPMP_Tool *)desc); + + case GF_ODF_MUXINFO_TAG: + return gf_odf_del_muxinfo((GF_MuxInfo *)desc); + case GF_ODF_BIFS_CFG_TAG: + return gf_odf_del_bifs_cfg((GF_BIFSConfig *)desc); + case GF_ODF_UI_CFG_TAG: + return gf_odf_del_ui_cfg((GF_UIConfig *)desc); + case GF_ODF_TEXT_CFG_TAG: + return gf_odf_del_text_cfg((GF_TextConfig *)desc); + case GF_ODF_TX3G_TAG: + return gf_odf_del_tx3g((GF_TextSampleDescriptor*)desc); + case GF_ODF_LASER_CFG_TAG: + return gf_odf_del_laser_cfg((GF_LASERConfig *)desc); + + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_del_auxvid((GF_AuxVideoDescriptor *)desc); + + default: + return gf_odf_del_default((GF_DefaultDescriptor *)desc); + } + return GF_OK; +} + + + + +// +// READERS +// +GF_Err gf_odf_read_descriptor(GF_BitStream *bs, GF_Descriptor *desc, u32 DescSize) +{ + switch (desc->tag) { + case GF_ODF_IOD_TAG : + return gf_odf_read_iod(bs, (GF_InitialObjectDescriptor *)desc, DescSize); + case GF_ODF_ESD_TAG : + return gf_odf_read_esd(bs, (GF_ESD *)desc, DescSize); + case GF_ODF_DCD_TAG : + return gf_odf_read_dcd(bs, (GF_DecoderConfig *)desc, DescSize); + case GF_ODF_SLC_TAG : + return gf_odf_read_slc(bs, (GF_SLConfig *)desc, DescSize); + case GF_ODF_CC_TAG: + return gf_odf_read_cc(bs, (GF_CCDescriptor *)desc, DescSize); + case GF_ODF_CC_DATE_TAG: + return gf_odf_read_cc_date(bs, (GF_CC_Date *)desc, DescSize); + case GF_ODF_CC_NAME_TAG: + return gf_odf_read_cc_name(bs, (GF_CC_Name *)desc, DescSize); + case GF_ODF_CI_TAG: + return gf_odf_read_ci(bs, (GF_CIDesc *)desc, DescSize); + case GF_ODF_TEXT_TAG: + return gf_odf_read_exp_text(bs, (GF_ExpandedTextual *)desc, DescSize); + case GF_ODF_EXT_PL_TAG: + return gf_odf_read_pl_ext(bs, (GF_PLExt *)desc, DescSize); + case GF_ODF_IPI_PTR_TAG: + case GF_ODF_ISOM_IPI_PTR_TAG: + return gf_odf_read_ipi_ptr(bs, (GF_IPIPtr *)desc, DescSize); + case GF_ODF_IPMP_TAG: + return gf_odf_read_ipmp(bs, (GF_IPMP_Descriptor *)desc, DescSize); + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_read_ipmp_ptr(bs, (GF_IPMPPtr *)desc, DescSize); + case GF_ODF_KW_TAG: + return gf_odf_read_kw(bs, (GF_KeyWord *)desc, DescSize); + case GF_ODF_LANG_TAG: + return gf_odf_read_lang(bs, (GF_Language *)desc, DescSize); + case GF_ODF_OD_TAG: + return gf_odf_read_od(bs, (GF_ObjectDescriptor *)desc, DescSize); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_read_oci_date(bs, (GF_OCI_Data *)desc, DescSize); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_read_oci_name(bs, (GF_OCICreators *)desc, DescSize); + case GF_ODF_PL_IDX_TAG: + return gf_odf_read_pl_idx(bs, (GF_PL_IDX *)desc, DescSize); + case GF_ODF_QOS_TAG: + return gf_odf_read_qos(bs, (GF_QoS_Descriptor *)desc, DescSize); + case GF_ODF_RATING_TAG: + return gf_odf_read_rating(bs, (GF_Rating *)desc, DescSize); + case GF_ODF_REG_TAG: + return gf_odf_read_reg(bs, (GF_Registration *)desc, DescSize); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_read_short_text(bs, (GF_ShortTextual *)desc, DescSize); + case GF_ODF_SMPTE_TAG: + return gf_odf_read_smpte_camera(bs, (GF_SMPTECamera *)desc, DescSize); + case GF_ODF_SCI_TAG: + return gf_odf_read_sup_cid(bs, (GF_SCIDesc *)desc, DescSize); + //MP4 File Format + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_read_isom_iod(bs, (GF_IsomInitialObjectDescriptor *)desc, DescSize); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_read_isom_od(bs, (GF_IsomObjectDescriptor *)desc, DescSize); + case GF_ODF_ESD_INC_TAG: + return gf_odf_read_esd_inc(bs, (GF_ES_ID_Inc *)desc, DescSize); + case GF_ODF_ESD_REF_TAG: + return gf_odf_read_esd_ref(bs, (GF_ES_ID_Ref *)desc, DescSize); + + case GF_ODF_SEGMENT_TAG: + return gf_odf_read_segment(bs, (GF_Segment *) desc, DescSize); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_read_mediatime(bs, (GF_MediaTime *) desc, DescSize); + + case GF_ODF_IPMP_TL_TAG: + return gf_odf_read_ipmp_tool_list(bs, (GF_IPMP_ToolList *)desc, DescSize); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_read_ipmp_tool(bs, (GF_IPMP_Tool *)desc, DescSize); + + case GF_ODF_MUXINFO_TAG: + return gf_odf_read_muxinfo(bs, (GF_MuxInfo *) desc, DescSize); + + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_read_auxvid(bs, (GF_AuxVideoDescriptor *)desc, DescSize); + + //default: + case GF_ODF_DSI_TAG: + default: + return gf_odf_read_default(bs, (GF_DefaultDescriptor *)desc, DescSize); + + } + return GF_OK; +} + + + + + +// +// SIZE FUNCTION +// +GF_Err gf_odf_size_descriptor(GF_Descriptor *desc, u32 *outSize) +{ + switch(desc->tag) { + case GF_ODF_IOD_TAG : + return gf_odf_size_iod((GF_InitialObjectDescriptor *)desc, outSize); + case GF_ODF_ESD_TAG : + return gf_odf_size_esd((GF_ESD *)desc, outSize); + case GF_ODF_DCD_TAG : + return gf_odf_size_dcd((GF_DecoderConfig *)desc, outSize); + case GF_ODF_SLC_TAG : + return gf_odf_size_slc((GF_SLConfig *)desc, outSize); + case GF_ODF_CC_TAG: + return gf_odf_size_cc((GF_CCDescriptor *)desc, outSize); + case GF_ODF_CC_DATE_TAG: + return gf_odf_size_cc_date((GF_CC_Date *)desc, outSize); + case GF_ODF_CC_NAME_TAG: + return gf_odf_size_cc_name((GF_CC_Name *)desc, outSize); + case GF_ODF_CI_TAG: + return gf_odf_size_ci((GF_CIDesc *)desc, outSize); + case GF_ODF_TEXT_TAG: + return gf_odf_size_exp_text((GF_ExpandedTextual *)desc, outSize); + case GF_ODF_EXT_PL_TAG: + return gf_odf_size_pl_ext((GF_PLExt *)desc, outSize); + case GF_ODF_IPI_PTR_TAG: + case GF_ODF_ISOM_IPI_PTR_TAG: + return gf_odf_size_ipi_ptr((GF_IPIPtr *)desc, outSize); + case GF_ODF_IPMP_TAG: + return gf_odf_size_ipmp((GF_IPMP_Descriptor *)desc, outSize); + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_size_ipmp_ptr((GF_IPMPPtr *)desc, outSize); + case GF_ODF_KW_TAG: + return gf_odf_size_kw((GF_KeyWord *)desc, outSize); + case GF_ODF_LANG_TAG: + return gf_odf_size_lang((GF_Language *)desc, outSize); + case GF_ODF_OD_TAG: + return gf_odf_size_od((GF_ObjectDescriptor *)desc, outSize); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_size_oci_date((GF_OCI_Data *)desc, outSize); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_size_oci_name((GF_OCICreators *)desc, outSize); + case GF_ODF_PL_IDX_TAG: + return gf_odf_size_pl_idx((GF_PL_IDX *)desc, outSize); + case GF_ODF_QOS_TAG: + return gf_odf_size_qos((GF_QoS_Descriptor *)desc, outSize); + case GF_ODF_RATING_TAG: + return gf_odf_size_rating((GF_Rating *)desc, outSize); + case GF_ODF_REG_TAG: + return gf_odf_size_reg((GF_Registration *)desc, outSize); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_size_short_text((GF_ShortTextual *)desc, outSize); + case GF_ODF_SMPTE_TAG: + return gf_odf_size_smpte_camera((GF_SMPTECamera *)desc, outSize); + case GF_ODF_SCI_TAG: + return gf_odf_size_sup_cid((GF_SCIDesc *)desc, outSize); + //MP4File + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_size_isom_iod((GF_IsomInitialObjectDescriptor *)desc, outSize); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_size_isom_od((GF_IsomObjectDescriptor *)desc, outSize); + case GF_ODF_ESD_INC_TAG: + return gf_odf_size_esd_inc((GF_ES_ID_Inc *)desc, outSize); + case GF_ODF_ESD_REF_TAG: + return gf_odf_size_esd_ref((GF_ES_ID_Ref *)desc, outSize); + + case GF_ODF_SEGMENT_TAG: + return gf_odf_size_segment((GF_Segment *) desc, outSize); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_size_mediatime((GF_MediaTime *) desc, outSize); + + case GF_ODF_IPMP_TL_TAG: + return gf_odf_size_ipmp_tool_list((GF_IPMP_ToolList *)desc, outSize); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_size_ipmp_tool((GF_IPMP_Tool *)desc, outSize); + + case GF_ODF_MUXINFO_TAG: + return gf_odf_size_muxinfo((GF_MuxInfo *) desc, outSize); + + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_size_auxvid((GF_AuxVideoDescriptor *)desc, outSize); + + default: + return gf_odf_size_default((GF_DefaultDescriptor *)desc, outSize); + } + return GF_OK; +} + + +// +// WRITERS +// +GF_Err gf_odf_write_descriptor(GF_BitStream *bs, GF_Descriptor *desc) +{ + switch(desc->tag) { + case GF_ODF_IOD_TAG : + return gf_odf_write_iod(bs, (GF_InitialObjectDescriptor *)desc); + case GF_ODF_ESD_TAG : + return gf_odf_write_esd(bs, (GF_ESD *)desc); + case GF_ODF_DCD_TAG : + return gf_odf_write_dcd(bs, (GF_DecoderConfig *)desc); + case GF_ODF_SLC_TAG : + return gf_odf_write_slc(bs, (GF_SLConfig *)desc); + case GF_ODF_CC_TAG: + return gf_odf_write_cc(bs, (GF_CCDescriptor *)desc); + case GF_ODF_CC_DATE_TAG: + return gf_odf_write_cc_date(bs, (GF_CC_Date *)desc); + case GF_ODF_CC_NAME_TAG: + return gf_odf_write_cc_name(bs, (GF_CC_Name *)desc); + case GF_ODF_CI_TAG: + return gf_odf_write_ci(bs, (GF_CIDesc *)desc); + + case GF_ODF_ESD_INC_TAG: + return gf_odf_write_esd_inc(bs, (GF_ES_ID_Inc *)desc); + case GF_ODF_ESD_REF_TAG: + return gf_odf_write_esd_ref(bs, (GF_ES_ID_Ref *)desc); + case GF_ODF_TEXT_TAG: + return gf_odf_write_exp_text(bs, (GF_ExpandedTextual *)desc); + case GF_ODF_EXT_PL_TAG: + return gf_odf_write_pl_ext(bs, (GF_PLExt *)desc); + case GF_ODF_IPI_PTR_TAG: + case GF_ODF_ISOM_IPI_PTR_TAG: + return gf_odf_write_ipi_ptr(bs, (GF_IPIPtr *)desc); + case GF_ODF_IPMP_TAG: + return gf_odf_write_ipmp(bs, (GF_IPMP_Descriptor *)desc); + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_write_ipmp_ptr(bs, (GF_IPMPPtr *)desc); + case GF_ODF_KW_TAG: + return gf_odf_write_kw(bs, (GF_KeyWord *)desc); + case GF_ODF_LANG_TAG: + return gf_odf_write_lang(bs, (GF_Language *)desc); + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_write_isom_iod(bs, (GF_IsomInitialObjectDescriptor *)desc); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_write_isom_od(bs, (GF_IsomObjectDescriptor *)desc); + case GF_ODF_OD_TAG: + return gf_odf_write_od(bs, (GF_ObjectDescriptor *)desc); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_write_oci_date(bs, (GF_OCI_Data *)desc); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_write_oci_name(bs, (GF_OCICreators *)desc); + case GF_ODF_PL_IDX_TAG: + return gf_odf_write_pl_idx(bs, (GF_PL_IDX *)desc); + case GF_ODF_QOS_TAG: + return gf_odf_write_qos(bs, (GF_QoS_Descriptor *)desc); + case GF_ODF_RATING_TAG: + return gf_odf_write_rating(bs, (GF_Rating *)desc); + case GF_ODF_REG_TAG: + return gf_odf_write_reg(bs, (GF_Registration *)desc); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_write_short_text(bs, (GF_ShortTextual *)desc); + case GF_ODF_SMPTE_TAG: + return gf_odf_write_smpte_camera(bs, (GF_SMPTECamera *)desc); + case GF_ODF_SCI_TAG: + return gf_odf_write_sup_cid(bs, (GF_SCIDesc *)desc); + + case GF_ODF_SEGMENT_TAG: + return gf_odf_write_segment(bs, (GF_Segment *) desc); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_write_mediatime(bs, (GF_MediaTime *) desc); + + case GF_ODF_IPMP_TL_TAG: + return gf_odf_write_ipmp_tool_list(bs, (GF_IPMP_ToolList *)desc); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_write_ipmp_tool(bs, (GF_IPMP_Tool *)desc); + + case GF_ODF_MUXINFO_TAG: + return gf_odf_write_muxinfo(bs, (GF_MuxInfo *) desc); + + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_write_auxvid(bs, (GF_AuxVideoDescriptor *)desc); + + default: + return gf_odf_write_default(bs, (GF_DefaultDescriptor *)desc); + } + return GF_OK; +} + + +// +// CONSTRUCTORS +// +GF_ODCom *gf_odf_create_command(u8 tag) +{ + GF_ODCom *com; + switch (tag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_new_od_update(); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_new_od_remove(); + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_new_esd_update(); + case GF_ODF_ESD_REMOVE_TAG: + return gf_odf_new_esd_remove(); + //special case for ESDRemove in the file format... + case GF_ODF_ESD_REMOVE_REF_TAG: + com = gf_odf_new_esd_remove(); + if (!com) return com; + com->tag = GF_ODF_ESD_REMOVE_REF_TAG; + return com; + + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_new_ipmp_update(); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_new_ipmp_remove(); + + default: + if ( (tag >= GF_ODF_COM_ISO_BEGIN_TAG) && + ( tag <= GF_ODF_COM_ISO_END_TAG) ) { + return NULL; + } + com = gf_odf_new_base_command(); + if (!com) return com; + com->tag = tag; + return com; + } +} + + +// +// DESTRUCTORS +// +GF_Err gf_odf_delete_command(GF_ODCom *com) +{ + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_del_od_update((GF_ODUpdate *)com); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_del_od_remove((GF_ODRemove *)com); + + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_del_esd_update((GF_ESDUpdate *)com); + case GF_ODF_ESD_REMOVE_TAG: + case GF_ODF_ESD_REMOVE_REF_TAG: + return gf_odf_del_esd_remove((GF_ESDRemove *)com); + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_del_ipmp_update((GF_IPMPUpdate *)com); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_del_ipmp_remove((GF_IPMPRemove *)com); + + default: + return gf_odf_del_base_command((GF_BaseODCom *)com); + } +} + + +// +// READERS +// +GF_Err gf_odf_read_command(GF_BitStream *bs, GF_ODCom *com, u32 gf_odf_size_command) +{ + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_read_od_update(bs, (GF_ODUpdate *)com, gf_odf_size_command); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_read_od_remove(bs, (GF_ODRemove *)com, gf_odf_size_command); + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_read_esd_update(bs, (GF_ESDUpdate *)com, gf_odf_size_command); + case GF_ODF_ESD_REMOVE_TAG: + case GF_ODF_ESD_REMOVE_REF_TAG: + return gf_odf_read_esd_remove(bs, (GF_ESDRemove *)com, gf_odf_size_command); + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_read_ipmp_update(bs, (GF_IPMPUpdate *)com, gf_odf_size_command); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_read_ipmp_remove(bs, (GF_IPMPRemove *)com, gf_odf_size_command); + default: + return gf_odf_read_base_command(bs, (GF_BaseODCom *)com, gf_odf_size_command); + } +} + + + +// +// SIZE FUNCTION +// +GF_Err gf_odf_size_command(GF_ODCom *com, u32 *outSize) +{ + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_size_od_update((GF_ODUpdate *)com, outSize); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_size_od_remove((GF_ODRemove *)com, outSize); + + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_size_esd_update((GF_ESDUpdate *)com, outSize); + case GF_ODF_ESD_REMOVE_TAG: + case GF_ODF_ESD_REMOVE_REF_TAG: + return gf_odf_size_esd_remove((GF_ESDRemove *)com, outSize); + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_size_ipmp_update((GF_IPMPUpdate *)com, outSize); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_size_ipmp_remove((GF_IPMPRemove *)com, outSize); + + default: + return gf_odf_size_base_command((GF_BaseODCom *)com, outSize); + } +} + + +// +// WRITERS +// +GF_Err gf_odf_write_command(GF_BitStream *bs, GF_ODCom *com) +{ + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_write_od_update(bs, (GF_ODUpdate *)com); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_write_od_remove(bs, (GF_ODRemove *)com); + + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_write_esd_update(bs, (GF_ESDUpdate *)com); + case GF_ODF_ESD_REMOVE_TAG: + case GF_ODF_ESD_REMOVE_REF_TAG: + return gf_odf_write_esd_remove(bs, (GF_ESDRemove *)com); + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_write_ipmp_update(bs, (GF_IPMPUpdate *)com); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_write_ipmp_remove(bs, (GF_IPMPRemove *)com); + + default: + return gf_odf_write_base_command(bs, (GF_BaseODCom *)com); + } +} + diff --git a/src/odf/descriptors.c b/src/odf/descriptors.c new file mode 100644 index 0000000..6a1a6c5 --- /dev/null +++ b/src/odf/descriptors.c @@ -0,0 +1,708 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +s32 gf_odf_size_field_size(u32 size_desc) +{ + if (size_desc < 0x00000080) { + return 1 + 1; + } else if (size_desc < 0x00004000) { + return 2 + 1; + } else if (size_desc < 0x00200000) { + return 3 + 1; + } else if (size_desc < 0x10000000) { + return 4 + 1; + } else { + return -1; + } + +} + + +GF_EXPORT +GF_Err gf_odf_parse_descriptor(GF_BitStream *bs, GF_Descriptor **desc, u32 *desc_size) +{ + u32 val, size, sizeHeader; + u8 tag; + GF_Err err; + GF_Descriptor *newDesc; + if (!bs) return GF_BAD_PARAM; + + *desc_size = 0; + + //tag + tag = (u8) gf_bs_read_int(bs, 8); + sizeHeader = 1; + + //size + size = 0; + do { + val = gf_bs_read_int(bs, 8); + sizeHeader++; + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80); + *desc_size = size; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[ODF] Reading descriptor (tag %d size %d)\n", tag, size )); + + newDesc = gf_odf_create_descriptor(tag); + if (! newDesc) { + *desc = NULL; + *desc_size = sizeHeader; + if ( (tag >= GF_ODF_ISO_RES_BEGIN_TAG) && + (tag <= GF_ODF_ISO_RES_END_TAG) ) { + return GF_ODF_FORBIDDEN_DESCRIPTOR; + } + else if (!tag || (tag == 0xFF)) { + return GF_ODF_INVALID_DESCRIPTOR; + } + return GF_OUT_OF_MEM; + } + + newDesc->tag = tag; + err = gf_odf_read_descriptor(bs, newDesc, *desc_size); + + /*FFMPEG fix*/ + if ((tag==GF_ODF_SLC_TAG) && (((GF_SLConfig*)newDesc)->predefined==2)) { + if (*desc_size==3) { + *desc_size = 1; + err = GF_OK; + } + } + + //little trick to handle lazy bitstreams that encode + //SizeOfInstance on a fix number of bytes + //This nb of bytes is added in Read methods + *desc_size += sizeHeader - gf_odf_size_field_size(*desc_size); + *desc = newDesc; + if (err) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[ODF] Error reading descriptor (tag %d size %d): %s\n", tag, size, gf_error_to_string(err) )); + gf_odf_delete_descriptor(newDesc); + *desc = NULL; + } + return err; +} + + + +GF_Err gf_odf_delete_descriptor_list(GF_List *descList) +{ + GF_Err e; + GF_Descriptor*tmp; + u32 i; + //no error if NULL chain... + if (! descList) return GF_OK; + i=0; + while ((tmp = (GF_Descriptor*)gf_list_enum(descList, &i))) { + e = gf_odf_delete_descriptor(tmp); + if (e) return e; + } + gf_list_del(descList); + return GF_OK; +} + + + + +GF_Err gf_odf_size_descriptor_list(GF_List *descList, u32 *outSize) +{ + GF_Err e; + GF_Descriptor *tmp; + u32 tmpSize, count, i; + if (! descList) return GF_OK; + + count = gf_list_count(descList); + for ( i = 0; i < count; i++ ) { + tmp = (GF_Descriptor*)gf_list_get(descList, i); + if (tmp) { + e = gf_odf_size_descriptor(tmp, &tmpSize); + if (e) return e; + if (tmpSize) *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + } + return GF_OK; +} + + + +GF_Err gf_odf_write_base_descriptor(GF_BitStream *bs, u8 tag, u32 size) +{ + u32 length; + unsigned char vals[4]; + + if (!tag ) return GF_BAD_PARAM; + + length = size; + vals[3] = (unsigned char) (length & 0x7f); + length >>= 7; + vals[2] = (unsigned char) ((length & 0x7f) | 0x80); + length >>= 7; + vals[1] = (unsigned char) ((length & 0x7f) | 0x80); + length >>= 7; + vals[0] = (unsigned char) ((length & 0x7f) | 0x80); + + gf_bs_write_int(bs, tag, 8); + if (size < 0x00000080) { + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x00004000) { + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x00200000) { + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x10000000) { + gf_bs_write_int(bs, vals[0], 8); + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else { + return GF_ODF_INVALID_DESCRIPTOR; + } + return GF_OK; +} + +u32 gf_ipmpx_array_size(GF_BitStream *bs, u32 *array_size) +{ + u32 val, size, io_size; + + io_size = size = 0; + do { + val = gf_bs_read_int(bs, 8); + io_size ++; + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + *array_size = size; + return io_size; +} + +void gf_ipmpx_write_array(GF_BitStream *bs, char *data, u32 data_len) +{ + u32 length; + unsigned char vals[4]; + + if (!data || !data_len) return; + + length = data_len; + vals[3] = (unsigned char) (length & 0x7f); length >>= 7; + vals[2] = (unsigned char) ((length & 0x7f) | 0x80); length >>= 7; + vals[1] = (unsigned char) ((length & 0x7f) | 0x80); length >>= 7; + vals[0] = (unsigned char) ((length & 0x7f) | 0x80); + + if (data_len < 0x00000080) { + gf_bs_write_int(bs, vals[3], 8); + } else if (data_len < 0x00004000) { + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (data_len < 0x00200000) { + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (data_len < 0x10000000) { + gf_bs_write_int(bs, vals[0], 8); + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else { + return; + } + gf_bs_write_data(bs, data, data_len); +} + + +GF_Err gf_odf_write_descriptor_list(GF_BitStream *bs, GF_List *descList) +{ + GF_Err e; + u32 count, i; + GF_Descriptor *tmp; + + if (! descList) return GF_OK; + count = gf_list_count(descList); + for ( i = 0; i < count; i++ ) { + tmp = (GF_Descriptor*)gf_list_get(descList, i); + if (tmp) { + e = gf_odf_write_descriptor(bs, tmp); + if (e) return e; + } + } + return GF_OK; +} + +GF_Err gf_odf_write_descriptor_list_filter(GF_BitStream *bs, GF_List *descList, u8 only_tag) +{ + GF_Err e; + u32 count, i; + GF_Descriptor *tmp; + + if (! descList) return GF_OK; + count = gf_list_count(descList); + for ( i = 0; i < count; i++ ) { + tmp = (GF_Descriptor*)gf_list_get(descList, i); + if (tmp && (tmp->tag==only_tag) ) { + e = gf_odf_write_descriptor(bs, tmp); + if (e) return e; + } + } + return GF_OK; +} + +GF_EXPORT +const char *gf_odf_stream_type_name(u32 streamType) +{ + switch (streamType) { + case GF_STREAM_OD: return "ObjectDescriptor"; + case GF_STREAM_OCR: return "ClockReference"; + case GF_STREAM_SCENE: return "SceneDescription"; + case GF_STREAM_VISUAL: return "Visual"; + case GF_STREAM_AUDIO: return "Audio"; + case GF_STREAM_MPEG7: return "MPEG7"; + case GF_STREAM_IPMP: return "IPMP"; + case GF_STREAM_OCI: return "OCI"; + case GF_STREAM_MPEGJ: return "MPEGJ"; + case GF_STREAM_INTERACT: return "Interaction"; + case GF_STREAM_TEXT: return "Text"; + case GF_STREAM_ND_SUBPIC: return "NeroDigital Subpicture"; + default: return "Unknown"; + } +} + +GF_EXPORT +u32 gf_odf_stream_type_by_name(const char *streamType) +{ + if (!streamType) return 0; + if (!stricmp(streamType, "ObjectDescriptor")) return GF_STREAM_OD; + if (!stricmp(streamType, "ClockReference")) return GF_STREAM_OCR; + if (!stricmp(streamType, "SceneDescription")) return GF_STREAM_SCENE; + if (!stricmp(streamType, "Visual")) return GF_STREAM_VISUAL; + if (!stricmp(streamType, "Audio")) return GF_STREAM_AUDIO; + if (!stricmp(streamType, "MPEG7")) return GF_STREAM_MPEG7; + if (!stricmp(streamType, "IPMP")) return GF_STREAM_IPMP; + if (!stricmp(streamType, "OCI")) return GF_STREAM_OCI; + if (!stricmp(streamType, "MPEGJ")) return GF_STREAM_MPEGJ; + if (!stricmp(streamType, "Interaction")) return GF_STREAM_INTERACT; + if (!stricmp(streamType, "Text")) return GF_STREAM_TEXT; + return 0; +} + + +/*special authoring functions*/ +GF_EXPORT +GF_BIFSConfig *gf_odf_get_bifs_config(GF_DefaultDescriptor *dsi, u8 oti) +{ + Bool hasSize, cmd_stream; + GF_Err e; + GF_BitStream *bs; + GF_BIFSConfig *cfg; + if (!dsi || !dsi->data || !dsi->dataLength ) { + /* Hack for T-DMB non compliant streams (OnTimeTek ?) */ + cfg = (GF_BIFSConfig *) gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); + cfg->pixelMetrics = 1; + cfg->version = 1; + return cfg; + } + bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); + + cfg = (GF_BIFSConfig *) gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); + e = GF_OK; + if (oti==2) { + /*3D Mesh Coding*/ + gf_bs_read_int(bs, 1); + /*PMF*/ + gf_bs_read_int(bs, 1); + } + cfg->nodeIDbits = gf_bs_read_int(bs, 5); + cfg->routeIDbits = gf_bs_read_int(bs, 5); + if (oti==2) cfg->protoIDbits = gf_bs_read_int(bs, 5); + + cmd_stream = gf_bs_read_int(bs, 1); + if (!cmd_stream) { + cfg->elementaryMasks = gf_list_new(); + while (1) { + GF_ElementaryMask* em = (GF_ElementaryMask* ) gf_odf_New_ElemMask(); + em->node_id = gf_bs_read_int(bs, cfg->nodeIDbits); + gf_list_add(cfg->elementaryMasks, em); + /*this assumes only FDP, BDP and IFS2D (no elem mask)*/ + if (gf_bs_read_int(bs, 1) == 0) break; + } + gf_bs_align(bs); + if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) e = GF_NOT_SUPPORTED; + } else { + cfg->pixelMetrics = gf_bs_read_int(bs, 1); + hasSize = gf_bs_read_int(bs, 1); + if (hasSize) { + cfg->pixelWidth = gf_bs_read_int(bs, 16); + cfg->pixelHeight = gf_bs_read_int(bs, 16); + } + gf_bs_align(bs); + if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) e = GF_ODF_INVALID_DESCRIPTOR; + } + gf_bs_del(bs); + return cfg; +} + +/*special function for authoring - convert DSI to LASERConfig*/ +GF_EXPORT +GF_Err gf_odf_get_laser_config(GF_DefaultDescriptor *dsi, GF_LASERConfig *cfg) +{ + u32 to_skip; + GF_BitStream *bs; + if (!dsi || !dsi->data || !dsi->dataLength || !cfg) return GF_BAD_PARAM; + bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); + memset(cfg, 0, sizeof(GF_LASERConfig)); + cfg->tag = GF_ODF_LASER_CFG_TAG; + cfg->profile = gf_bs_read_int(bs, 8); + cfg->level = gf_bs_read_int(bs, 8); + /*cfg->reserved = */gf_bs_read_int(bs, 3); + cfg->pointsCodec = gf_bs_read_int(bs, 2); + cfg->pathComponents = gf_bs_read_int(bs, 4); + cfg->fullRequestHost = gf_bs_read_int(bs, 1); + if (gf_bs_read_int(bs, 1)) cfg->time_resolution = gf_bs_read_int(bs, 16); + else cfg->time_resolution = 1000; + cfg->colorComponentBits = 1 + gf_bs_read_int(bs, 4); + cfg->resolution = gf_bs_read_int(bs, 4); + if (cfg->resolution>7) cfg->resolution -= 16; + cfg->coord_bits = gf_bs_read_int(bs, 5); + cfg->scale_bits_minus_coord_bits = gf_bs_read_int(bs, 4); + cfg->newSceneIndicator = gf_bs_read_int(bs, 1); + /*reserved2*/ gf_bs_read_int(bs, 3); + cfg->extensionIDBits = gf_bs_read_int(bs, 4); + /*hasExtConfig - we just ignore it*/ + if (gf_bs_read_int(bs, 1)) { + to_skip = gf_bs_read_vluimsbf5(bs); + while (to_skip) { + gf_bs_read_int(bs, 8); + to_skip--; + } + } + /*hasExtension - we just ignore it*/ + if (gf_bs_read_int(bs, 1)) { + to_skip = gf_bs_read_vluimsbf5(bs); + while (to_skip) { + gf_bs_read_int(bs, 8); + to_skip--; + } + } + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_odf_get_ui_config(GF_DefaultDescriptor *dsi, GF_UIConfig *cfg) +{ + u32 len, i; + GF_BitStream *bs; + if (!dsi || !dsi->data || !dsi->dataLength || !cfg) return GF_BAD_PARAM; + memset(cfg, 0, sizeof(GF_UIConfig)); + cfg->tag = GF_ODF_UI_CFG_TAG; + bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); + len = gf_bs_read_int(bs, 8); + cfg->deviceName = (char*)malloc(sizeof(char) * (len+1)); + for (i=0; ideviceName[i] = gf_bs_read_int(bs, 8); + cfg->deviceName[i] = 0; + + if (!stricmp(cfg->deviceName, "StringSensor") && gf_bs_available(bs)) { + cfg->termChar = gf_bs_read_int(bs, 8); + cfg->delChar = gf_bs_read_int(bs, 8); + } + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_odf_encode_ui_config(GF_UIConfig *cfg, GF_DefaultDescriptor **out_dsi) +{ + u32 i, len; + GF_BitStream *bs; + GF_DefaultDescriptor *dsi; + if (!out_dsi || (cfg->tag != GF_ODF_UI_CFG_TAG)) return GF_BAD_PARAM; + + *out_dsi = NULL; + if (!cfg->deviceName) return GF_OK; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + len = strlen(cfg->deviceName); + gf_bs_write_int(bs, len, 8); + for (i=0; ideviceName[i], 8); + if (!stricmp(cfg->deviceName, "StringSensor")) { + /*fixme - this should be UTF-8 chars*/ + if (cfg->delChar || cfg->termChar) { + gf_bs_write_int(bs, cfg->termChar, 8); + gf_bs_write_int(bs, cfg->delChar, 8); + } + } + if (cfg->ui_data) gf_bs_write_data(bs, cfg->ui_data, cfg->ui_data_length); + + dsi = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + gf_bs_get_content(bs, &dsi->data, &dsi->dataLength); + gf_bs_del(bs); + *out_dsi = dsi; + return GF_OK; +} + + +GF_EXPORT +GF_AVCConfig *gf_odf_avc_cfg_new() +{ + GF_AVCConfig *cfg; + GF_SAFEALLOC(cfg, GF_AVCConfig); + if (!cfg) return NULL; + cfg->sequenceParameterSets = gf_list_new(); + cfg->pictureParameterSets = gf_list_new(); + return cfg; +} + +GF_EXPORT +void gf_odf_avc_cfg_del(GF_AVCConfig *cfg) +{ + if (!cfg) return; + while (gf_list_count(cfg->sequenceParameterSets)) { + GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(cfg->sequenceParameterSets, 0); + gf_list_rem(cfg->sequenceParameterSets, 0); + if (sl->data) free(sl->data); + free(sl); + } + gf_list_del(cfg->sequenceParameterSets); + while (gf_list_count(cfg->pictureParameterSets)) { + GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(cfg->pictureParameterSets, 0); + gf_list_rem(cfg->pictureParameterSets, 0); + if (sl->data) free(sl->data); + free(sl); + } + gf_list_del(cfg->pictureParameterSets); + free(cfg); +} + +GF_EXPORT +GF_Err gf_odf_avc_cfg_write(GF_AVCConfig *cfg, char **outData, u32 *outSize) +{ + u32 i, count; + GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, cfg->configurationVersion, 8); + gf_bs_write_int(bs, cfg->AVCProfileIndication , 8); + gf_bs_write_int(bs, cfg->profile_compatibility, 8); + gf_bs_write_int(bs, cfg->AVCLevelIndication, 8); + gf_bs_write_int(bs, 0x3F, 6); + gf_bs_write_int(bs, cfg->nal_unit_size - 1, 2); + gf_bs_write_int(bs, 0x7, 3); + count = gf_list_count(cfg->sequenceParameterSets); + gf_bs_write_int(bs, count, 5); + for (i=0; isequenceParameterSets, i); + gf_bs_write_int(bs, sl->size, 16); + gf_bs_write_data(bs, sl->data, sl->size); + } + count = gf_list_count(cfg->pictureParameterSets); + gf_bs_write_int(bs, count, 8); + for (i=0; ipictureParameterSets, i); + gf_bs_write_int(bs, sl->size, 16); + gf_bs_write_data(bs, sl->data, sl->size); + } + + *outSize = 0; + *outData = NULL; + gf_bs_get_content(bs, outData, outSize); + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_AVCConfig *gf_odf_avc_cfg_read(char *dsi, u32 dsi_size) +{ + u32 i, count; + GF_AVCConfig *avcc = gf_odf_avc_cfg_new(); + GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); + avcc->configurationVersion = gf_bs_read_int(bs, 8); + avcc->AVCProfileIndication = gf_bs_read_int(bs, 8); + avcc->profile_compatibility = gf_bs_read_int(bs, 8); + avcc->AVCLevelIndication = gf_bs_read_int(bs, 8); + gf_bs_read_int(bs, 6); + avcc->nal_unit_size = 1 + gf_bs_read_int(bs, 2); + gf_bs_read_int(bs, 3); + count = gf_bs_read_int(bs, 5); + for (i=0; isize = gf_bs_read_int(bs, 16); + sl->data = (char*)malloc(sizeof(char)*sl->size); + gf_bs_read_data(bs, sl->data, sl->size); + gf_list_add(avcc->sequenceParameterSets, sl); + } + count = gf_bs_read_int(bs, 8); + for (i=0; isize = gf_bs_read_int(bs, 16); + sl->data = (char*)malloc(sizeof(char)*sl->size); + gf_bs_read_data(bs, sl->data, sl->size); + gf_list_add(avcc->pictureParameterSets, sl); + } + gf_bs_del(bs); + return avcc; +} + + +GF_Descriptor *gf_odf_new_tx3g() +{ + GF_TextSampleDescriptor *newDesc = (GF_TextSampleDescriptor*) malloc(sizeof(GF_TextSampleDescriptor)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_TextSampleDescriptor)); + newDesc->tag = GF_ODF_TX3G_TAG; + return (GF_Descriptor *) newDesc; +} +GF_Err gf_odf_del_tx3g(GF_TextSampleDescriptor *sd) +{ + u32 i; + for (i=0; ifont_count; i++) + if (sd->fonts[i].fontName) free(sd->fonts[i].fontName); + free(sd->fonts); + free(sd); + return GF_OK; +} + +/*TextConfig*/ +GF_Descriptor *gf_odf_new_text_cfg() +{ + GF_TextConfig *newDesc = (GF_TextConfig*) malloc(sizeof(GF_TextConfig)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_TextConfig)); + newDesc->tag = GF_ODF_TEXT_CFG_TAG; + newDesc->sample_descriptions = gf_list_new(); + newDesc->Base3GPPFormat = 0x10; + newDesc->MPEGExtendedFormat = 0x10; + newDesc->profileLevel = 0x10; + newDesc->timescale = 1000; + return (GF_Descriptor *) newDesc; +} + +void ResetTextConfig(GF_TextConfig *desc) +{ + GF_List *bck; + while (gf_list_count(desc->sample_descriptions)) { + GF_TextSampleDescriptor *sd = (GF_TextSampleDescriptor *)gf_list_get(desc->sample_descriptions, 0); + gf_list_rem(desc->sample_descriptions, 0); + gf_odf_del_tx3g(sd); + } + bck = desc->sample_descriptions; + memset(desc, 0, sizeof(GF_TextConfig)); + desc->tag = GF_ODF_TEXT_CFG_TAG; + desc->sample_descriptions = bck; +} + +GF_Err gf_odf_del_text_cfg(GF_TextConfig *desc) +{ + ResetTextConfig(desc); + gf_list_del(desc->sample_descriptions); + free(desc); + return GF_OK; +} + +/*we need box parsing*/ +#include +GF_EXPORT +GF_Err gf_odf_get_text_config(GF_DefaultDescriptor *dsi, u8 oti, GF_TextConfig *cfg) +{ + u32 i, j; + Bool has_alt_format, has_sd; + GF_Err e; + GF_BitStream *bs; + if (!dsi || !dsi->data || !dsi->dataLength || !cfg) return GF_BAD_PARAM; + if (oti != 0x08) return GF_NOT_SUPPORTED; + + /*reset*/ + ResetTextConfig(cfg); + bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); + + e = GF_OK; + cfg->Base3GPPFormat = gf_bs_read_int(bs, 8); + cfg->MPEGExtendedFormat = gf_bs_read_int(bs, 8); + cfg->profileLevel = gf_bs_read_int(bs, 8); + cfg->timescale = gf_bs_read_int(bs, 24); + has_alt_format = gf_bs_read_int(bs, 1); + cfg->sampleDescriptionFlags = gf_bs_read_int(bs, 2); + has_sd = gf_bs_read_int(bs, 1); + cfg->has_vid_info = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 3); + cfg->layer = gf_bs_read_int(bs, 8); + cfg->text_width = gf_bs_read_int(bs, 16); + cfg->text_height = gf_bs_read_int(bs, 16); + if (has_alt_format) { + cfg->nb_compatible_formats = gf_bs_read_int(bs, 8); + for (i=0; inb_compatible_formats; i++) cfg->compatible_formats[i] = gf_bs_read_int(bs, 8); + } + if (has_sd) { + u8 sample_index; + GF_TextSampleDescriptor *txdesc; + GF_TextSampleEntryBox *a; + s32 avail; + u32 nb_desc = gf_bs_read_int(bs, 8); + + /*parse TTU[5]s*/ + avail = (s32) gf_bs_available(bs); + for (i=0; isize; + + if (avail<0) { + e = GF_NON_COMPLIANT_BITSTREAM; + goto exit; + } + txdesc = (GF_TextSampleDescriptor *)malloc(sizeof(GF_TextSampleDescriptor)); + txdesc->sample_index = sample_index; + txdesc->displayFlags = a->displayFlags; + txdesc->back_color = a->back_color; + txdesc->default_pos = a->default_box; + txdesc->default_style = a->default_style; + txdesc->vert_justif = a->vertical_justification; + txdesc->horiz_justif = a->horizontal_justification; + txdesc->font_count = a->font_table ? a->font_table->entry_count : 0; + if (txdesc->font_count) { + txdesc->fonts = (GF_FontRecord*)malloc(sizeof(GF_FontRecord)*txdesc->font_count); + for (j=0; jfont_count; j++) { + txdesc->fonts[j].fontID = a->font_table->fonts[j].fontID; + txdesc->fonts[j].fontName = a->font_table->fonts[j].fontName ? strdup(a->font_table->fonts[j].fontName) : NULL; + } + } + gf_list_add(cfg->sample_descriptions, txdesc); + gf_isom_box_del((GF_Box *)a); + } + } + if (cfg->has_vid_info) { + cfg->video_width = gf_bs_read_int(bs, 16); + cfg->video_height = gf_bs_read_int(bs, 16); + cfg->horiz_offset = gf_bs_read_int(bs, 16); + cfg->vert_offset = gf_bs_read_int(bs, 16); + } + +exit: + gf_bs_del(bs); + if (e) ResetTextConfig(cfg); + return e; +} + diff --git a/src/odf/ipmpx_code.c b/src/odf/ipmpx_code.c new file mode 100644 index 0000000..e2bfd8d --- /dev/null +++ b/src/odf/ipmpx_code.c @@ -0,0 +1,2017 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#define GF_IPMPX_DATA_ALLOC(__ptr, __stname, __tag) \ + __ptr = (__stname*)malloc(sizeof(__stname)); \ + if (!__ptr) return NULL; \ + memset(__ptr, 0, sizeof(__stname)); \ + ((GF_IPMPX_Data *)__ptr)->tag = __tag; \ + ((GF_IPMPX_Data *)__ptr)->Version = 0x01; \ + + +#define GF_IPMPX_DELETE_ARRAY(__ar) if (__ar) { if (__ar->data) free(__ar->data); free(__ar); } + +u32 get_field_size(u32 size_desc) +{ + if (size_desc < 0x00000080) return 1; + else if (size_desc < 0x00004000) return 2; + else if (size_desc < 0x00200000) return 3; + else return 4; +} + +void write_var_size(GF_BitStream *bs, u32 size) +{ + unsigned char vals[4]; + u32 length = size; + + vals[3] = (unsigned char) (length & 0x7f); length >>= 7; + vals[2] = (unsigned char) ((length & 0x7f) | 0x80); length >>= 7; + vals[1] = (unsigned char) ((length & 0x7f) | 0x80); length >>= 7; + vals[0] = (unsigned char) ((length & 0x7f) | 0x80); + if (size < 0x00000080) { + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x00004000) { + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x00200000) { + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x10000000) { + gf_bs_write_int(bs, vals[0], 8); + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } +} + +GF_IPMPX_ByteArray *GF_IPMPX_GetByteArray(GF_BitStream *bs) +{ + GF_IPMPX_ByteArray *ba; + u32 val, size; + size = 0; + do { + val = gf_bs_read_int(bs, 8); + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + if (!size) return NULL; + ba = (GF_IPMPX_ByteArray*)malloc(sizeof(GF_IPMPX_ByteArray)); + ba->data = (char*)malloc(sizeof(char)*size); + gf_bs_read_data(bs, ba->data, size); + ba->length = size; + return ba; +} + +u32 GF_IPMPX_GetByteArraySize(GF_IPMPX_ByteArray *ba) +{ + if (!ba) return 1; + return ba->length + get_field_size(ba->length); +} + +void GF_IPMPX_WriteByteArray(GF_BitStream *bs, GF_IPMPX_ByteArray *ba) +{ + if (!ba || !ba->data) { + write_var_size(bs, 0); + } else { + write_var_size(bs, ba->length); + gf_bs_write_data(bs, ba->data, ba->length); + } +} + +void GF_IPMPX_AUTH_Delete(GF_IPMPX_Authentication *auth) +{ + if (!auth) return; + switch (auth->tag) { + case GF_IPMPX_AUTH_AlgorithmDescr_Tag: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)auth; + GF_IPMPX_DELETE_ARRAY(p->specAlgoID); + GF_IPMPX_DELETE_ARRAY(p->OpaqueData); + free(p); + } + break; + case GF_IPMPX_AUTH_KeyDescr_Tag: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)auth; + if (p->keyBody) free(p->keyBody); + free(p); + } + break; + } +} + +u32 GF_IPMPX_AUTH_Size(GF_IPMPX_Authentication *auth) +{ + u32 size; + if (!auth) return 0; + switch (auth->tag) { + case GF_IPMPX_AUTH_AlgorithmDescr_Tag: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)auth; + size = 1 + (p->specAlgoID ? GF_IPMPX_GetByteArraySize(p->specAlgoID) : 2); + size += GF_IPMPX_GetByteArraySize(p->OpaqueData); + return size; + } + break; + case GF_IPMPX_AUTH_KeyDescr_Tag: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)auth; + size = p->keyBodyLength; + return size; + } + break; + default: + return 0; + } +} + +u32 GF_IPMPX_AUTH_FullSize(GF_IPMPX_Authentication *auth) +{ + u32 size = GF_IPMPX_AUTH_Size(auth); + size += get_field_size(size); + size += 1;/*tag*/ + return size; +} + +GF_Err WriteGF_IPMPX_AUTH(GF_BitStream *bs, GF_IPMPX_Authentication *auth) +{ + u32 size; + + if (!auth) return GF_OK; + gf_bs_write_int(bs, auth->tag, 8); + + size = GF_IPMPX_AUTH_Size(auth); + write_var_size(bs, size); + + switch (auth->tag) { + case GF_IPMPX_AUTH_AlgorithmDescr_Tag: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)auth; + gf_bs_write_int(bs, p->specAlgoID ? 0 : 1, 1); + gf_bs_write_int(bs, 0, 7); + if (p->specAlgoID) { + GF_IPMPX_WriteByteArray(bs, p->specAlgoID); + } else { + gf_bs_write_int(bs, p->regAlgoID, 16); + } + GF_IPMPX_WriteByteArray(bs, p->OpaqueData); + } + break; + case GF_IPMPX_AUTH_KeyDescr_Tag: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)auth; + /*tag*/ + gf_bs_write_data(bs, p->keyBody, p->keyBodyLength); + } + break; + default: + break; + } + return GF_OK; +} + +GF_Err GF_IPMPX_AUTH_Parse(GF_BitStream *bs, GF_IPMPX_Authentication **auth) +{ + u32 val, size, tag; + + tag = gf_bs_read_int(bs, 8); + size = 0; + do { + val = gf_bs_read_int(bs, 8); + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + /*don't know if tolerated in IPMPX*/ + if (!size) return GF_OK; + + switch (tag) { + case GF_IPMPX_AUTH_KeyDescr_Tag: + { + GF_IPMPX_AUTH_KeyDescriptor *p; + GF_SAFEALLOC(p, GF_IPMPX_AUTH_KeyDescriptor); + if (!p) return GF_OUT_OF_MEM; + p->tag = tag; + p->keyBodyLength = size; + p->keyBody = (char*)malloc(sizeof(char)*size); + gf_bs_read_data(bs, p->keyBody, size); + *auth = (GF_IPMPX_Authentication *)p; + return GF_OK; + } + break; + case GF_IPMPX_AUTH_AlgorithmDescr_Tag: + { + Bool isReg; + GF_IPMPX_AUTH_AlgorithmDescriptor *p; + GF_SAFEALLOC(p, GF_IPMPX_AUTH_AlgorithmDescriptor); + if (!p) return GF_OUT_OF_MEM; + p->tag = tag; + isReg = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (isReg) { + p->regAlgoID = gf_bs_read_int(bs, 16); + } else { + p->specAlgoID = GF_IPMPX_GetByteArray(bs); + } + p->OpaqueData = GF_IPMPX_GetByteArray(bs); + *auth = (GF_IPMPX_Authentication *)p; + return GF_OK; + } + break; + default: + break; + } + return GF_NON_COMPLIANT_BITSTREAM; +} + + +GF_Err GF_IPMPX_ReadData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size); + +u32 gf_ipmpx_data_full_size(GF_IPMPX_Data *p) +{ + u32 size; + if (!p) return 0; + size = gf_ipmpx_data_size(p); + size += 5; /*Version and dataID*/ + size += get_field_size(size); + size += 1;/*tag*/ + return size; +} + +GF_Err gf_ipmpx_data_parse(GF_BitStream *bs, GF_IPMPX_Data **out_data) +{ + GF_Err e; + u32 val, size; + u8 tag; + GF_IPMPX_Data *p; + + *out_data = NULL; + tag = gf_bs_read_int(bs, 8); + size = 0; + do { + val = gf_bs_read_int(bs, 8); + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + /*don't know if tolerated in IPMPX*/ + if (!size) return GF_OK; + + p = gf_ipmpx_data_new(tag); + if (!p) return GF_NON_COMPLIANT_BITSTREAM; + p->Version = gf_bs_read_int(bs, 8); + p->dataID = gf_bs_read_int(bs, 32); + e = GF_IPMPX_ReadData(bs, p, size); + if (e) { + gf_ipmpx_data_del(p); + return e; + } + *out_data = p; + return GF_OK; +} + +GF_Err GF_IPMPX_WriteBase(GF_BitStream *bs, GF_IPMPX_Data *p) +{ + u32 size; + + if (!p) return GF_BAD_PARAM; + size = gf_ipmpx_data_size(p); + size += 5; /*Version & dataID*/ + gf_bs_write_int(bs, p->tag, 8); + write_var_size(bs, size); + gf_bs_write_int(bs, p->Version, 8); + gf_bs_write_int(bs, p->dataID, 32); + return GF_OK; +} + + +static GF_IPMPX_Data *NewGF_IPMPX_InitAuthentication() +{ + GF_IPMPX_InitAuthentication *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_InitAuthentication, GF_IPMPX_INIT_AUTHENTICATION_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_InitAuthentication(GF_IPMPX_Data *_p) +{ + GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p; + free(p); +} +static GF_Err ReadGF_IPMPX_InitAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p; + p->Context = gf_bs_read_int(bs, 32); + p->AuthType = gf_bs_read_int(bs, 8); + return GF_OK; +} +static u32 SizeGF_IPMPX_InitAuthentication(GF_IPMPX_Data *_p) +{ + return 5; +} +static GF_Err WriteGF_IPMPX_InitAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p; + gf_bs_write_int(bs, p->Context, 32); + gf_bs_write_int(bs, p->AuthType, 8); + return GF_OK; +} + + +static GF_IPMPX_Data *NewGF_IPMPX_MutualAuthentication() +{ + GF_IPMPX_MutualAuthentication *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_MutualAuthentication, GF_IPMPX_MUTUAL_AUTHENTICATION_TAG); + ptr->candidateAlgorithms = gf_list_new(); + ptr->agreedAlgorithms = gf_list_new(); + ptr->certificates = gf_list_new(); + return (GF_IPMPX_Data *) ptr; +} + +static void delete_algo_list(GF_List *algos) +{ + u32 i; + for (i=0;icandidateAlgorithms); + delete_algo_list(p->agreedAlgorithms); + GF_IPMPX_DELETE_ARRAY(p->AuthenticationData); + GF_IPMPX_DELETE_ARRAY(p->opaque); + GF_IPMPX_DELETE_ARRAY(p->authCodes); + gf_ipmpx_data_del((GF_IPMPX_Data *)p->trustData); + GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication*)p->publicKey); + while (gf_list_count(p->certificates)) { + GF_IPMPX_ByteArray *ba = (GF_IPMPX_ByteArray *)gf_list_get(p->certificates, 0); + gf_list_rem(p->certificates, 0); + GF_IPMPX_DELETE_ARRAY(ba); + } + gf_list_del(p->certificates); + free(p); +} +static GF_Err ReadGF_IPMPX_MutualAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_Err e; + u32 i, count; + Bool requestNegotiation, successNegotiation, inclAuthenticationData, inclAuthCodes; + GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p; + + requestNegotiation = gf_bs_read_int(bs, 1); + successNegotiation = gf_bs_read_int(bs, 1); + p->failedNegotiation = gf_bs_read_int(bs, 1); + inclAuthenticationData = gf_bs_read_int(bs, 1); + inclAuthCodes = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 3); + + if (requestNegotiation) { + count = gf_bs_read_int(bs, 8); + for (i=0; icandidateAlgorithms, auth); + } + } + if (successNegotiation) { + count = gf_bs_read_int(bs, 8); + for (i=0; iagreedAlgorithms, auth); + } + } + if (inclAuthenticationData) p->AuthenticationData = GF_IPMPX_GetByteArray(bs); + if (inclAuthCodes) { + /*unspecified in spec, IM1 uses 8 bits*/ + u32 type = gf_bs_read_int(bs, 8); + switch (type) { + case 0x01: + count = gf_bs_read_int(bs, 8); + p->certType = gf_bs_read_int(bs, 32); + for (i=0; icertificates, ipd); + } + break; + case 0x02: + e = GF_IPMPX_AUTH_Parse(bs, (GF_IPMPX_Authentication**) &p->publicKey); + if (e) return e; + break; + case 0xFE: + p->opaque = GF_IPMPX_GetByteArray(bs); + break; + default: + break; + } + e = gf_ipmpx_data_parse(bs, (GF_IPMPX_Data **) &p->trustData); + if (e) return e; + p->authCodes = GF_IPMPX_GetByteArray(bs); + } + return GF_OK; +} +static u32 SizeGF_IPMPX_MutualAuthentication(GF_IPMPX_Data *_p) +{ + u32 size, i, count; + GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p; + size = 1; + count = gf_list_count(p->candidateAlgorithms); + if (count) { + size += 1; + for (i=0; icandidateAlgorithms, i); + size += GF_IPMPX_AUTH_FullSize(ip_auth); + } + } + count = gf_list_count(p->agreedAlgorithms); + if (count) { + size += 1; + for (i=0; iagreedAlgorithms, i); + size += GF_IPMPX_AUTH_FullSize(ip_auth); + } + } + if (p->AuthenticationData) size += GF_IPMPX_GetByteArraySize(p->AuthenticationData); + + count = gf_list_count(p->certificates); + if (count || p->opaque || p->publicKey) { + size += 1; + /*type 1*/ + if (count) { + size += 1+4; + for (i=0; icertificates, i); + size += GF_IPMPX_GetByteArraySize(ba); + } + } + /*type 2*/ + else if (p->publicKey) { + size += GF_IPMPX_AUTH_FullSize((GF_IPMPX_Authentication*)p->publicKey); + } + /*type 0xFE*/ + else if (p->opaque) { + size += GF_IPMPX_GetByteArraySize(p->opaque); + } + size += gf_ipmpx_data_full_size((GF_IPMPX_Data *)p->trustData); + size += GF_IPMPX_GetByteArraySize(p->authCodes); + } + return size; +} +static GF_Err WriteGF_IPMPX_MutualAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i, count; + GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p; + + gf_bs_write_int(bs, gf_list_count(p->candidateAlgorithms) ? 1 : 0, 1); + gf_bs_write_int(bs, gf_list_count(p->agreedAlgorithms) ? 1 : 0, 1); + gf_bs_write_int(bs, p->failedNegotiation ? 1 : 0, 1); + gf_bs_write_int(bs, p->AuthenticationData ? 1 : 0, 1); + if (gf_list_count(p->certificates) || p->opaque || p->publicKey) { + gf_bs_write_int(bs, 1, 1); + } else { + gf_bs_write_int(bs, 0, 1); + } + gf_bs_write_int(bs, 0, 3); + + count = gf_list_count(p->candidateAlgorithms); + if (count) { + gf_bs_write_int(bs, count, 8); + for (i=0; icandidateAlgorithms, i); + WriteGF_IPMPX_AUTH(bs, ip_auth); + } + } + count = gf_list_count(p->agreedAlgorithms); + if (count) { + gf_bs_write_int(bs, count, 8); + for (i=0; iagreedAlgorithms, i); + WriteGF_IPMPX_AUTH(bs, ip_auth); + } + } + if (p->AuthenticationData) GF_IPMPX_WriteByteArray(bs, p->AuthenticationData); + + count = gf_list_count(p->certificates); + if (count || p->opaque || p->publicKey) { + /*type 1*/ + if (count) { + gf_bs_write_int(bs, 0x01, 8); + + gf_bs_write_int(bs, count, 8); + gf_bs_write_int(bs, p->certType, 32); + for (i=0; icertificates, i); + if (ipd) GF_IPMPX_WriteByteArray(bs, ipd); + } + } + /*type 2*/ + else if (p->publicKey) { + gf_bs_write_int(bs, 0x02, 8); + WriteGF_IPMPX_AUTH(bs, (GF_IPMPX_Authentication *) p->publicKey); + } + /*type 0xFE*/ + else if (p->opaque) { + gf_bs_write_int(bs, 0xFE, 8); + GF_IPMPX_WriteByteArray(bs, p->opaque); + } + gf_ipmpx_data_write(bs, (GF_IPMPX_Data *)p->trustData); + GF_IPMPX_WriteByteArray(bs, p->authCodes); + } + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_TrustSecurityMetadata() +{ + GF_IPMPX_TrustSecurityMetadata *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_TrustSecurityMetadata, GF_IPMPX_TRUST_SECURITY_METADATA_TAG); + ptr->TrustedTools = gf_list_new(); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_TrustSecurityMetadata(GF_IPMPX_Data *_p) +{ + GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p; + while (gf_list_count(p->TrustedTools)) { + GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)gf_list_get(p->TrustedTools, 0); + gf_list_rem(p->TrustedTools, 0); + while (gf_list_count(tt->trustSpecifications)) { + GF_IPMPX_TrustSpecification *tts = (GF_IPMPX_TrustSpecification *)gf_list_get(tt->trustSpecifications, 0); + gf_list_rem(tt->trustSpecifications, 0); + GF_IPMPX_DELETE_ARRAY(tts->CCTrustMetadata); + free(tts); + } + gf_list_del(tt->trustSpecifications); + free(tt); + } + gf_list_del(p->TrustedTools); + free(p); +} +static GF_Err ReadGF_IPMPX_TrustSecurityMetadata(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 nbTools, nbSpec; + GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p; + nbTools = gf_bs_read_int(bs, 16); + while (nbTools) { + GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)malloc(sizeof(GF_IPMPX_TrustedTool)); + if (!tt) return GF_OUT_OF_MEM; + memset(tt, 0, sizeof(GF_IPMPX_TrustedTool)); + tt->tag = GF_IPMPX_TRUSTED_TOOL_TAG; + nbTools--; + gf_list_add(p->TrustedTools, tt); + gf_bs_read_data(bs, (char*)tt->toolID, 16); + gf_bs_read_data(bs, tt->AuditDate, 5); + tt->trustSpecifications = gf_list_new(); + nbSpec = gf_bs_read_int(bs, 16); + while (nbSpec) { + Bool has_cc; + GF_IPMPX_TrustSpecification *tts = (GF_IPMPX_TrustSpecification *)malloc(sizeof(GF_IPMPX_TrustSpecification)); + if (!tts) return GF_OUT_OF_MEM; + memset(tts, 0, sizeof(GF_IPMPX_TrustSpecification)); + tts->tag = GF_IPMPX_TRUST_SPECIFICATION_TAG; + nbSpec--; + gf_list_add(tt->trustSpecifications, tts); + has_cc = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (has_cc) { + tts->CCTrustMetadata = GF_IPMPX_GetByteArray(bs); + } else { + gf_bs_read_data(bs, tts->startDate, 5); + tts->attackerProfile = gf_bs_read_int(bs, 2); + gf_bs_read_int(bs, 6); + tts->trustedDuration = gf_bs_read_int(bs, 32); + } + } + } + return GF_OK; +} +static u32 SizeGF_IPMPX_TrustSecurityMetadata(GF_IPMPX_Data *_p) +{ + u32 size, i, j; + GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p; + size = 2; + for (i=0;iTrustedTools); i++) { + GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)gf_list_get(p->TrustedTools, i); + size += 23; + for (j=0; jtrustSpecifications); j++) { + GF_IPMPX_TrustSpecification *tts = (GF_IPMPX_TrustSpecification *)gf_list_get(tt->trustSpecifications, j); + size += 1; + if (tts->CCTrustMetadata) size += GF_IPMPX_GetByteArraySize(tts->CCTrustMetadata); + else size += 10; + } + } + return size; +} +static GF_Err WriteGF_IPMPX_TrustSecurityMetadata(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i, j, c1, c2; + GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p; + + c1 = gf_list_count(p->TrustedTools); + gf_bs_write_int(bs, c1, 16); + for (i=0;iTrustedTools, i); + gf_bs_write_data(bs, (char*)tt->toolID, 16); + gf_bs_write_data(bs, (char*)tt->AuditDate, 5); + c2 = gf_list_count(tt->trustSpecifications); + gf_bs_write_int(bs, c2, 16); + + for (j=0; jtrustSpecifications, j); + gf_bs_write_int(bs, tts->CCTrustMetadata ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + if (tts->CCTrustMetadata) GF_IPMPX_WriteByteArray(bs, tts->CCTrustMetadata); + else { + gf_bs_write_data(bs, tts->startDate, 5); + gf_bs_write_int(bs, tts->attackerProfile, 2); + gf_bs_write_int(bs, 0, 6); + gf_bs_write_int(bs, tts->trustedDuration, 32); + } + } + } + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_SecureContainer() +{ + GF_IPMPX_SecureContainer *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_SecureContainer, GF_IPMPX_SECURE_CONTAINER_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_SecureContainer(GF_IPMPX_Data *_p) +{ + GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p; + GF_IPMPX_DELETE_ARRAY(p->encryptedData); + GF_IPMPX_DELETE_ARRAY(p->MAC); + gf_ipmpx_data_del(p->protectedMsg); + free(p); +} +static GF_Err ReadGF_IPMPX_SecureContainer(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_enc, has_mac; + GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p; + has_enc = gf_bs_read_int(bs, 1); + has_mac = gf_bs_read_int(bs, 1); + p->isMACEncrypted = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 5); + if (has_enc) { + p->encryptedData = GF_IPMPX_GetByteArray(bs); + if (has_mac && !p->isMACEncrypted) p->MAC = GF_IPMPX_GetByteArray(bs); + } else { + GF_Err e = gf_ipmpx_data_parse(bs, &p->protectedMsg); + if (e) return e; + if (has_mac) p->MAC = GF_IPMPX_GetByteArray(bs); + } + return GF_OK; +} +static u32 SizeGF_IPMPX_SecureContainer(GF_IPMPX_Data *_p) +{ + u32 size = 1; + GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p; + if (p->MAC) p->isMACEncrypted = 0; + if (p->encryptedData) { + size += GF_IPMPX_GetByteArraySize(p->encryptedData); + if (p->MAC) size += GF_IPMPX_GetByteArraySize(p->MAC); + } else { + size += gf_ipmpx_data_full_size(p->protectedMsg); + if (p->MAC) size += GF_IPMPX_GetByteArraySize(p->MAC); + } + return size; +} +static GF_Err WriteGF_IPMPX_SecureContainer(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p; + if (p->MAC) p->isMACEncrypted = 0; + gf_bs_write_int(bs, p->encryptedData ? 1 : 0, 1); + gf_bs_write_int(bs, (p->MAC || p->isMACEncrypted) ? 1 : 0, 1); + gf_bs_write_int(bs, p->isMACEncrypted, 1); + gf_bs_write_int(bs, 0, 5); + if (p->encryptedData) { + GF_IPMPX_WriteByteArray(bs, p->encryptedData); + if (p->MAC) GF_IPMPX_WriteByteArray(bs, p->MAC); + } else { + GF_Err e = gf_ipmpx_data_write(bs, p->protectedMsg); + if (e) return e; + if (p->MAC) GF_IPMPX_WriteByteArray(bs, p->MAC); + } + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_GetToolsResponse() +{ + GF_IPMPX_GetToolsResponse *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_GetToolsResponse, GF_IPMPX_GET_TOOLS_RESPONSE_TAG); + ptr->ipmp_tools = gf_list_new(); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_GetToolsResponse(GF_IPMPX_Data *_p) +{ + GF_IPMPX_GetToolsResponse *p = (GF_IPMPX_GetToolsResponse *)_p; + while (gf_list_count(p->ipmp_tools)) { + /*IPMPTools are descriptors*/ + GF_Descriptor *d = (GF_Descriptor *)gf_list_get(p->ipmp_tools, 0); + gf_list_rem(p->ipmp_tools, 0); + gf_odf_desc_del((GF_Descriptor*)d); + } + gf_list_del(p->ipmp_tools); + free(p); +} +static GF_Err ReadGF_IPMPX_GetToolsResponse(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 NbBytes = 0; + GF_IPMPX_GetToolsResponse *p = (GF_IPMPX_GetToolsResponse *)_p; + + while (size>NbBytes) { + u32 desc_size, start_o; + GF_Descriptor *desc; + GF_Err e; + start_o = (u32) gf_bs_get_position(bs); + e = gf_odf_parse_descriptor(bs, &desc, &desc_size); + if (e) return e; + gf_list_add(p->ipmp_tools, desc); + NbBytes += (u32) gf_bs_get_position(bs) - start_o; + } + if (sizeipmp_tools, &size); + return size; +} +static GF_Err WriteGF_IPMPX_GetToolsResponse(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i; + GF_IPMPX_GetToolsResponse *p = (GF_IPMPX_GetToolsResponse *)_p; + + for (i=0; iipmp_tools); i++) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(p->ipmp_tools, i); + gf_odf_write_descriptor(bs, desc); + } + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_ParametricDescription() +{ + GF_IPMPX_ParametricDescription *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ParametricDescription, GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG); + ptr->descriptions = gf_list_new(); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ParametricDescription(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p; + GF_IPMPX_DELETE_ARRAY(p->descriptionComment); + while (gf_list_count(p->descriptions)) { + GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_list_get(p->descriptions, 0); + gf_list_rem(p->descriptions, 0); + GF_IPMPX_DELETE_ARRAY(it->main_class); + GF_IPMPX_DELETE_ARRAY(it->subClass); + GF_IPMPX_DELETE_ARRAY(it->typeData); + GF_IPMPX_DELETE_ARRAY(it->type); + GF_IPMPX_DELETE_ARRAY(it->addedData); + free(it); + } + gf_list_del(p->descriptions); + free(p); +} +static GF_Err ReadGF_IPMPX_ParametricDescription(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 count; + GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p; + p->descriptionComment = GF_IPMPX_GetByteArray(bs); + p->majorVersion = gf_bs_read_int(bs, 8); + p->minorVersion = gf_bs_read_int(bs, 8); + count = gf_bs_read_int(bs, 32); + while (count) { + GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)malloc(sizeof(GF_IPMPX_ParametricDescriptionItem)); + gf_list_add(p->descriptions, it); + count--; + it->main_class = GF_IPMPX_GetByteArray(bs); + it->subClass = GF_IPMPX_GetByteArray(bs); + it->typeData = GF_IPMPX_GetByteArray(bs); + it->type = GF_IPMPX_GetByteArray(bs); + it->addedData = GF_IPMPX_GetByteArray(bs); + } + return GF_OK; +} +static u32 SizeGF_IPMPX_ParametricDescription(GF_IPMPX_Data *_p) +{ + u32 size, i; + GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p; + size = GF_IPMPX_GetByteArraySize(p->descriptionComment); + size += 6; + for (i=0; idescriptions); i++) { + GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_list_get(p->descriptions, i); + size += GF_IPMPX_GetByteArraySize(it->main_class); + size += GF_IPMPX_GetByteArraySize(it->subClass); + size += GF_IPMPX_GetByteArraySize(it->typeData); + size += GF_IPMPX_GetByteArraySize(it->type); + size += GF_IPMPX_GetByteArraySize(it->addedData); + } + return size; +} +static GF_Err WriteGF_IPMPX_ParametricDescription(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i; + GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p; + GF_IPMPX_WriteByteArray(bs, p->descriptionComment); + gf_bs_write_int(bs, p->majorVersion, 8); + gf_bs_write_int(bs, p->minorVersion, 8); + gf_bs_write_int(bs, gf_list_count(p->descriptions), 32); + + for (i=0; idescriptions); i++) { + GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_list_get(p->descriptions, i); + GF_IPMPX_WriteByteArray(bs, it->main_class); + GF_IPMPX_WriteByteArray(bs, it->subClass); + GF_IPMPX_WriteByteArray(bs, it->typeData); + GF_IPMPX_WriteByteArray(bs, it->type); + GF_IPMPX_WriteByteArray(bs, it->addedData); + } + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_ToolParamCapabilitiesQuery() +{ + GF_IPMPX_ToolParamCapabilitiesQuery*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ToolParamCapabilitiesQuery, GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ToolParamCapabilitiesQuery(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery *)_p; + gf_ipmpx_data_del((GF_IPMPX_Data *) p->description); + free(p); +} +static GF_Err ReadGF_IPMPX_ToolParamCapabilitiesQuery(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p; + return gf_ipmpx_data_parse(bs, (GF_IPMPX_Data **) &p->description); +} +static u32 SizeGF_IPMPX_ToolParamCapabilitiesQuery(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p; + return gf_ipmpx_data_full_size((GF_IPMPX_Data *) p->description); +} +static GF_Err WriteGF_IPMPX_ToolParamCapabilitiesQuery(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p; + return gf_ipmpx_data_write(bs, (GF_IPMPX_Data *) p->description); +} +static GF_IPMPX_Data *NewGF_IPMPX_ToolParamCapabilitiesResponse() +{ + GF_IPMPX_ToolParamCapabilitiesResponse*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ToolParamCapabilitiesResponse, GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ToolParamCapabilitiesResponse(GF_IPMPX_Data *_p) +{ + free(_p); +} +static GF_Err ReadGF_IPMPX_ToolParamCapabilitiesResponse(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_ToolParamCapabilitiesResponse *p = (GF_IPMPX_ToolParamCapabilitiesResponse*)_p; + p->capabilitiesSupported = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + return GF_OK; +} +static u32 SizeGF_IPMPX_ToolParamCapabilitiesResponse(GF_IPMPX_Data *_p) +{ + return 1; +} +static GF_Err WriteGF_IPMPX_ToolParamCapabilitiesResponse(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolParamCapabilitiesResponse*p = (GF_IPMPX_ToolParamCapabilitiesResponse*)_p; + gf_bs_write_int(bs, p->capabilitiesSupported, 1); + gf_bs_write_int(bs, 0, 7); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_ConnectTool() +{ + GF_IPMPX_ConnectTool*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ConnectTool, GF_IPMPX_CONNECT_TOOL_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ConnectTool(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ConnectTool *p = (GF_IPMPX_ConnectTool*)_p; + if (p->toolDescriptor) gf_odf_desc_del((GF_Descriptor *)p->toolDescriptor); + free(p); +} +static GF_Err ReadGF_IPMPX_ConnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 dsize; + GF_IPMPX_ConnectTool*p = (GF_IPMPX_ConnectTool*)_p; + return gf_odf_parse_descriptor(bs, (GF_Descriptor **) &p->toolDescriptor, &dsize); +} +static u32 SizeGF_IPMPX_ConnectTool(GF_IPMPX_Data *_p) +{ + u32 size; + GF_IPMPX_ConnectTool*p = (GF_IPMPX_ConnectTool*)_p; + gf_odf_size_descriptor((GF_Descriptor *)p->toolDescriptor, &size); + size += gf_odf_size_field_size(size); + return size; +} +static GF_Err WriteGF_IPMPX_ConnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_ConnectTool*p = (GF_IPMPX_ConnectTool*)_p; + gf_odf_write_descriptor(bs, (GF_Descriptor *)p->toolDescriptor); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_DisconnectTool() +{ + GF_IPMPX_DisconnectTool*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_DisconnectTool, GF_IPMPX_DISCONNECT_TOOL_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_DisconnectTool(GF_IPMPX_Data *_p) +{ + free(_p); +} +static GF_Err ReadGF_IPMPX_DisconnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_DisconnectTool*p = (GF_IPMPX_DisconnectTool*)_p; + p->IPMP_ToolContextID = gf_bs_read_int(bs, 32); + return GF_OK; +} +static u32 SizeGF_IPMPX_DisconnectTool(GF_IPMPX_Data *_p) +{ + return 4; +} +static GF_Err WriteGF_IPMPX_DisconnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_DisconnectTool*p = (GF_IPMPX_DisconnectTool*)_p; + gf_bs_write_int(bs, p->IPMP_ToolContextID, 32); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_GetToolContext() +{ + GF_IPMPX_GetToolContext*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_GetToolContext, GF_IPMPX_GET_TOOL_CONTEXT_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_GetToolContext(GF_IPMPX_Data *_p) +{ + free(_p); +} +static GF_Err ReadGF_IPMPX_GetToolContext(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_idex; + GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p; + p->scope = gf_bs_read_int(bs, 3); + has_idex = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 4); + if (has_idex) p->IPMP_DescriptorIDEx = gf_bs_read_int(bs, 16); + + return GF_OK; +} +static u32 SizeGF_IPMPX_GetToolContext(GF_IPMPX_Data *_p) +{ + GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p; + return p->IPMP_DescriptorIDEx ? 3 : 1; +} +static GF_Err WriteGF_IPMPX_GetToolContext(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p; + gf_bs_write_int(bs, p->scope, 3); + gf_bs_write_int(bs, p->IPMP_DescriptorIDEx ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 4); + if (p->IPMP_DescriptorIDEx) gf_bs_write_int(bs, p->IPMP_DescriptorIDEx, 16); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_GetToolContextResponse() +{ + GF_IPMPX_GetToolContextResponse*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_GetToolContextResponse, GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_GetToolContextResponse(GF_IPMPX_Data *_p) +{ + free(_p); +} +static GF_Err ReadGF_IPMPX_GetToolContextResponse(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_esid; + GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p; + has_esid = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 5); + p->OD_ID = gf_bs_read_int(bs, 10); + if (has_esid) p->ESD_ID = gf_bs_read_int(bs, 16); + p->IPMP_ToolContextID = gf_bs_read_int(bs, 32); + return GF_OK; +} +static u32 SizeGF_IPMPX_GetToolContextResponse(GF_IPMPX_Data *_p) +{ + GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p; + return p->ESD_ID ? 8 : 6; +} +static GF_Err WriteGF_IPMPX_GetToolContextResponse(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p; + gf_bs_write_int(bs, p->ESD_ID ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 5); + gf_bs_write_int(bs, p->OD_ID, 10); + if (p->ESD_ID) gf_bs_write_int(bs, p->ESD_ID, 16); + gf_bs_write_int(bs, p->IPMP_ToolContextID, 32); + return GF_OK; +} + + +static GF_IPMPX_Data *NewGF_IPMPX_AddToolNotificationListener() +{ + GF_IPMPX_AddToolNotificationListener*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_AddToolNotificationListener, GF_IPMPX_ADD_TOOL_LISTENER_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_AddToolNotificationListener(GF_IPMPX_Data *_p) +{ + free(_p); +} +static GF_Err ReadGF_IPMPX_AddToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 i; + GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p; + p->scope = gf_bs_read_int(bs, 3); + gf_bs_read_int(bs, 5); + p->eventTypeCount = gf_bs_read_int(bs, 8); + for (i=0;ieventTypeCount; i++) p->eventType[i] = gf_bs_read_int(bs, 8); + return GF_OK; +} +static u32 SizeGF_IPMPX_AddToolNotificationListener(GF_IPMPX_Data *_p) +{ + GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p; + return p->eventTypeCount + 2; +} +static GF_Err WriteGF_IPMPX_AddToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i; + GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p; + gf_bs_write_int(bs, p->scope, 3); + gf_bs_write_int(bs, 0, 5); + gf_bs_write_int(bs, p->eventTypeCount, 8); + for (i=0;ieventTypeCount; i++) gf_bs_write_int(bs, p->eventType[i], 8); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_RemoveToolNotificationListener() +{ + GF_IPMPX_RemoveToolNotificationListener*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_RemoveToolNotificationListener, GF_IPMPX_REMOVE_TOOL_LISTENER_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_RemoveToolNotificationListener(GF_IPMPX_Data *_p) +{ + free(_p); +} +static GF_Err ReadGF_IPMPX_RemoveToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 i; + GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p; + p->eventTypeCount = gf_bs_read_int(bs, 8); + for (i=0;ieventTypeCount; i++) p->eventType[i] = gf_bs_read_int(bs, 8); + return GF_OK; +} +static u32 SizeGF_IPMPX_RemoveToolNotificationListener(GF_IPMPX_Data *_p) +{ + GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p; + return p->eventTypeCount + 1; +} +static GF_Err WriteGF_IPMPX_RemoveToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i; + GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p; + gf_bs_write_int(bs, p->eventTypeCount, 8); + for (i=0;ieventTypeCount; i++) gf_bs_write_int(bs, p->eventType[i], 8); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_NotifyToolEvent() +{ + GF_IPMPX_NotifyToolEvent*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_NotifyToolEvent, GF_IPMPX_NOTIFY_TOOL_EVENT_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_NotifyToolEvent(GF_IPMPX_Data *_p) +{ + free(_p); +} +static GF_Err ReadGF_IPMPX_NotifyToolEvent(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_id; + GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p; + has_id = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (has_id) { + p->OD_ID = gf_bs_read_int(bs, 10); + gf_bs_read_int(bs, 6); + p->ESD_ID = gf_bs_read_int(bs, 16); + } + p->eventType = gf_bs_read_int(bs, 8); + p->IPMP_ToolContextID = gf_bs_read_int(bs, 32); + return GF_OK; +} +static u32 SizeGF_IPMPX_NotifyToolEvent(GF_IPMPX_Data *_p) +{ + GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p; + return (p->OD_ID || p->ESD_ID) ? 10 : 6; +} +static GF_Err WriteGF_IPMPX_NotifyToolEvent(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p; + gf_bs_write_int(bs, (p->OD_ID || p->ESD_ID) ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + if (p->OD_ID || p->ESD_ID) { + gf_bs_write_int(bs, p->OD_ID, 10); + gf_bs_write_int(bs, 0, 6); + gf_bs_write_int(bs, p->ESD_ID, 16); + } + gf_bs_write_int(bs, p->eventType, 8); + gf_bs_write_int(bs, p->IPMP_ToolContextID, 32); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_CanProcess() +{ + GF_IPMPX_CanProcess*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_CanProcess, GF_IPMPX_CAN_PROCESS_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_CanProcess(GF_IPMPX_Data *_p) +{ + free(_p); +} +static GF_Err ReadGF_IPMPX_CanProcess(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_CanProcess*p = (GF_IPMPX_CanProcess*)_p; + p->canProcess = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + return GF_OK; +} +static u32 SizeGF_IPMPX_CanProcess(GF_IPMPX_Data *_p) +{ + return 1; +} +static GF_Err WriteGF_IPMPX_CanProcess(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_CanProcess*p = (GF_IPMPX_CanProcess*)_p; + gf_bs_write_int(bs, p->canProcess ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_OpaqueData(u8 tag) +{ + GF_IPMPX_OpaqueData*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_OpaqueData, tag); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_OpaqueData(GF_IPMPX_Data *_p) +{ + GF_IPMPX_OpaqueData *p = (GF_IPMPX_OpaqueData*)_p; + GF_IPMPX_DELETE_ARRAY(p->opaqueData); + free(p); +} +static GF_Err ReadGF_IPMPX_OpaqueData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_OpaqueData*p = (GF_IPMPX_OpaqueData*)_p; + p->opaqueData = GF_IPMPX_GetByteArray(bs); + return GF_OK; +} +static u32 SizeGF_IPMPX_OpaqueData(GF_IPMPX_Data *_p) +{ + GF_IPMPX_OpaqueData*p = (GF_IPMPX_OpaqueData*)_p; + return GF_IPMPX_GetByteArraySize(p->opaqueData); +} +static GF_Err WriteGF_IPMPX_OpaqueData(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_OpaqueData*p = (GF_IPMPX_OpaqueData*)_p; + GF_IPMPX_WriteByteArray(bs, p->opaqueData); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_KeyData() +{ + GF_IPMPX_KeyData*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_KeyData, GF_IPMPX_KEY_DATA_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_KeyData(GF_IPMPX_Data *_p) +{ + GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p; + GF_IPMPX_DELETE_ARRAY(p->keyBody); + GF_IPMPX_DELETE_ARRAY(p->OpaqueData); + free(p); +} +static GF_Err ReadGF_IPMPX_KeyData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p; + p->keyBody = GF_IPMPX_GetByteArray(bs); + p->flags = 0; + if (gf_bs_read_int(bs, 1)) p->flags |= 1; + if (gf_bs_read_int(bs, 1)) p->flags |= 1<<1; + if (gf_bs_read_int(bs, 1)) p->flags |= 1<<2; + if (gf_bs_read_int(bs, 1)) p->flags |= 1<<3; + gf_bs_read_int(bs, 4); + if (p->flags & (1)) p->startDTS = gf_bs_read_long_int(bs, 64); + if (p->flags & (1<<1)) p->startPacketID = gf_bs_read_int(bs, 32); + if (p->flags & (1<<2)) p->expireDTS = gf_bs_read_long_int(bs, 64); + if (p->flags & (1<<3)) p->expirePacketID = gf_bs_read_int(bs, 32); + p->OpaqueData = GF_IPMPX_GetByteArray(bs); + return GF_OK; +} +static u32 SizeGF_IPMPX_KeyData(GF_IPMPX_Data *_p) +{ + u32 size = 0; + GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p; + size += GF_IPMPX_GetByteArraySize(p->keyBody); + size += 1; + if (p->flags & (1)) size += 8; + if (p->flags & (1<<1)) size += 4; + if (p->flags & (1<<2)) size += 8; + if (p->flags & (1<<3)) size += 4; + size += GF_IPMPX_GetByteArraySize(p->OpaqueData); + return size; +} +static GF_Err WriteGF_IPMPX_KeyData(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p; + GF_IPMPX_WriteByteArray(bs, p->keyBody); + gf_bs_write_int(bs, (p->flags & (1)) ? 1 : 0, 1); + gf_bs_write_int(bs, (p->flags & (1<<1)) ? 1 : 0, 1); + gf_bs_write_int(bs, (p->flags & (1<<2)) ? 1 : 0, 1); + gf_bs_write_int(bs, (p->flags & (1<<3)) ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 4); + if (p->flags & (1)) gf_bs_write_long_int(bs, p->startDTS, 64); + if (p->flags & (1<<1)) gf_bs_write_int(bs, p->startPacketID, 32); + if (p->flags & (1<<2)) gf_bs_write_long_int(bs, p->expireDTS, 64); + if (p->flags & (1<<3)) gf_bs_write_int(bs, p->expirePacketID, 32); + GF_IPMPX_WriteByteArray(bs, p->OpaqueData); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_SelectiveDecryptionInit() +{ + GF_IPMPX_SelectiveDecryptionInit*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_SelectiveDecryptionInit, GF_IPMPX_SEL_DEC_INIT_TAG); + ptr->SelEncBuffer = gf_list_new(); + ptr->SelEncFields = gf_list_new(); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_SelectiveDecryptionInit(GF_IPMPX_Data *_p) +{ + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + while (gf_list_count(p->SelEncBuffer)) { + GF_IPMPX_SelEncBuffer *sb = (GF_IPMPX_SelEncBuffer *)gf_list_get(p->SelEncBuffer, 0); + gf_list_rem(p->SelEncBuffer, 0); + GF_IPMPX_DELETE_ARRAY(sb->Stream_Cipher_Specific_Init_Info); + free(sb); + } + gf_list_del(p->SelEncBuffer); + while (gf_list_count(p->SelEncFields)) { + GF_IPMPX_SelEncField*sf = (GF_IPMPX_SelEncField*)gf_list_get(p->SelEncFields, 0); + gf_list_rem(p->SelEncFields, 0); + GF_IPMPX_DELETE_ARRAY(sf->shuffleSpecificInfo); + if (sf->mappingTable) free(sf->mappingTable); + free(sf); + } + gf_list_del(p->SelEncFields); + if (p->RLE_Data) free(p->RLE_Data); + free(p); +} +static GF_Err ReadGF_IPMPX_SelectiveDecryptionInit(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 count, i; + Bool is_spec; + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + p->mediaTypeExtension = gf_bs_read_int(bs, 8); + p->mediaTypeIndication = gf_bs_read_int(bs, 8); + p->profileLevelIndication = gf_bs_read_int(bs, 8); + p->compliance = gf_bs_read_int(bs, 8); + count = gf_bs_read_int(bs, 8); + while (count) { + Bool is_block; + GF_IPMPX_SelEncBuffer *sb; + GF_SAFEALLOC(sb, GF_IPMPX_SelEncBuffer); + gf_list_add(p->SelEncBuffer, sb); + count--; + gf_bs_read_data(bs, (char*)sb->cipher_Id, 16); + sb->syncBoundary = gf_bs_read_int(bs, 8); + is_block = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (is_block) { + sb->mode = gf_bs_read_int(bs, 8); + sb->blockSize = gf_bs_read_int(bs, 16); + sb->keySize = gf_bs_read_int(bs, 16); + } else { + sb->Stream_Cipher_Specific_Init_Info = GF_IPMPX_GetByteArray(bs); + } + } + is_spec = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (is_spec) { + Bool is_map; + count = gf_bs_read_int(bs, 8); + while (count) { + GF_IPMPX_SelEncField *sf; + GF_SAFEALLOC(sf, GF_IPMPX_SelEncField); + gf_list_add(p->SelEncFields, sf); + count--; + sf->field_Id = gf_bs_read_int(bs, 8); + sf->field_Scope = gf_bs_read_int(bs, 3); + gf_bs_read_int(bs, 5); + sf->buf = gf_bs_read_int(bs, 8); + is_map = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (is_map) { + Bool sendMapTable = gf_bs_read_int(bs, 1); + Bool isShuffled = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 6); + if (sendMapTable) { + sf->mappingTableSize = gf_bs_read_int(bs, 16); + sf->mappingTable = (u16*)malloc(sizeof(u16) * sf->mappingTableSize); + for (i=0; imappingTableSize; i++) sf->mappingTable[i] = gf_bs_read_int(bs, 16); + } + if (isShuffled) sf->shuffleSpecificInfo = GF_IPMPX_GetByteArray(bs); + } + } + } else { + p->RLE_DataLength = gf_bs_read_int(bs, 16); + p->RLE_Data = (u16*)malloc(sizeof(u16)*p->RLE_DataLength); + for (i=0; iRLE_DataLength; i++) p->RLE_Data[i] = gf_bs_read_int(bs, 16); + } + return GF_OK; +} +static u32 SizeGF_IPMPX_SelectiveDecryptionInit(GF_IPMPX_Data *_p) +{ + u32 size, i; + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + + size = 5; + for (i=0; iSelEncBuffer); i++) { + GF_IPMPX_SelEncBuffer *sb = (GF_IPMPX_SelEncBuffer *)gf_list_get(p->SelEncBuffer, i); + size += 18; + if (sb->Stream_Cipher_Specific_Init_Info) { + size += GF_IPMPX_GetByteArraySize(sb->Stream_Cipher_Specific_Init_Info); + } else { + size += 5; + } + } + size += 1; + if (p->RLE_Data) { + size += 2 + 2*p->RLE_DataLength; + } else { + size += 1; + for (i=0; iSelEncFields); i++) { + GF_IPMPX_SelEncField *sf = (GF_IPMPX_SelEncField *)gf_list_get(p->SelEncFields, i); + size += 4; + if (sf->mappingTable || sf->shuffleSpecificInfo) { + size += 1; + if (sf->mappingTable) size += 2 + 2*sf->mappingTableSize; + if (sf->shuffleSpecificInfo) size += GF_IPMPX_GetByteArraySize(sf->shuffleSpecificInfo); + } + } + } + return size; +} +static GF_Err WriteGF_IPMPX_SelectiveDecryptionInit(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 count, i; + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + + gf_bs_write_int(bs, p->mediaTypeExtension, 8); + gf_bs_write_int(bs, p->mediaTypeIndication, 8); + gf_bs_write_int(bs, p->profileLevelIndication, 8); + gf_bs_write_int(bs, p->compliance, 8); + + count = gf_list_count(p->SelEncBuffer); + gf_bs_write_int(bs, count, 8); + for (i=0; iSelEncBuffer, i); + gf_bs_write_data(bs, (char*)sb->cipher_Id, 16); + gf_bs_write_int(bs, sb->syncBoundary, 8); + gf_bs_write_int(bs, sb->Stream_Cipher_Specific_Init_Info ? 0 : 1, 1); + gf_bs_write_int(bs, 0, 7); + if (sb->Stream_Cipher_Specific_Init_Info) { + GF_IPMPX_WriteByteArray(bs, sb->Stream_Cipher_Specific_Init_Info); + } else { + gf_bs_write_int(bs, sb->mode, 8); + gf_bs_write_int(bs, sb->blockSize, 16); + gf_bs_write_int(bs, sb->keySize, 16); + } + } + gf_bs_write_int(bs, p->RLE_Data ? 0 : 1, 1); + gf_bs_write_int(bs, 0, 7); + if (p->RLE_Data) { + gf_bs_write_int(bs, p->RLE_DataLength, 16); + for (i=0; iRLE_DataLength; i++) gf_bs_write_int(bs, p->RLE_Data[i], 16); + } else { + count = gf_list_count(p->SelEncFields); + gf_bs_write_int(bs, count, 8); + for (i=0; iSelEncFields, i); + gf_bs_write_int(bs, sf->field_Id, 8); + gf_bs_write_int(bs, sf->field_Scope, 3); + gf_bs_write_int(bs, 0, 5); + gf_bs_write_int(bs, sf->buf, 8); + gf_bs_write_int(bs, (sf->mappingTable || sf->shuffleSpecificInfo) ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + if (sf->mappingTable || sf->shuffleSpecificInfo) { + gf_bs_write_int(bs, sf->mappingTable ? 1 : 0, 1); + gf_bs_write_int(bs, sf->shuffleSpecificInfo ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 6); + if (sf->mappingTable) { + gf_bs_write_int(bs, sf->mappingTableSize, 16); + for (i=0; imappingTableSize; i++) gf_bs_write_int(bs, sf->mappingTable[i], 16); + } + if (sf->shuffleSpecificInfo) GF_IPMPX_WriteByteArray(bs, sf->shuffleSpecificInfo); + } + } + } + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_WatermarkingInit(u8 tag) +{ + GF_IPMPX_WatermarkingInit *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_WatermarkingInit, tag); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_WatermarkingInit(GF_IPMPX_Data *_p) +{ + GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p; + if (p->wmPayload) free(p->wmPayload); + if (p->opaqueData) free(p->opaqueData); + free(p); +} +static GF_Err ReadGF_IPMPX_WatermarkingInit(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_opaque_data; + GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p; + + p->inputFormat = gf_bs_read_int(bs, 8); + p->requiredOp = gf_bs_read_int(bs, 4); + has_opaque_data = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 3); + if (p->inputFormat==0x01) { + if (p->tag == GF_IPMPX_AUDIO_WM_INIT_TAG) { + p->nChannels = gf_bs_read_int(bs, 8); + p->bitPerSample = gf_bs_read_int(bs, 8); + p->frequency = gf_bs_read_int(bs, 32); + } else { + p->frame_horizontal_size = gf_bs_read_int(bs, 16); + p->frame_vertical_size = gf_bs_read_int(bs, 16); + p->chroma_format = gf_bs_read_int(bs, 8); + } + } + switch (p->requiredOp) { + case GF_IPMPX_WM_INSERT: + case GF_IPMPX_WM_REMARK: + p->wmPayloadLen = gf_bs_read_int(bs, 16); + p->wmPayload = (char*)malloc(sizeof(u8) * p->wmPayloadLen); + gf_bs_read_data(bs, p->wmPayload, p->wmPayloadLen); + break; + case GF_IPMPX_WM_EXTRACT: + case GF_IPMPX_WM_DETECT_COMPRESSION: + p->wmRecipientId = gf_bs_read_int(bs, 16); + break; + } + if (has_opaque_data) { + p->opaqueDataSize = gf_bs_read_int(bs, 16); + p->opaqueData = (char*)malloc(sizeof(u8) * p->wmPayloadLen); + gf_bs_read_data(bs, p->opaqueData, p->opaqueDataSize); + } + return GF_OK; +} +static u32 SizeGF_IPMPX_WatermarkingInit(GF_IPMPX_Data *_p) +{ + u32 size; + GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p; + size = 2; + if (p->inputFormat==0x01) size += (p->tag == GF_IPMPX_AUDIO_WM_INIT_TAG) ? 6 : 5; + switch (p->requiredOp) { + case GF_IPMPX_WM_INSERT: + case GF_IPMPX_WM_REMARK: + size += 2+p->wmPayloadLen; + break; + case GF_IPMPX_WM_EXTRACT: + case GF_IPMPX_WM_DETECT_COMPRESSION: + size += 2; + break; + } + if (p->opaqueData) size += p->opaqueDataSize + 2; + return size; +} +static GF_Err WriteGF_IPMPX_WatermarkingInit(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_WatermarkingInit*p = (GF_IPMPX_WatermarkingInit*)_p; + + gf_bs_write_int(bs, p->inputFormat, 8); + gf_bs_write_int(bs, p->requiredOp, 4); + gf_bs_write_int(bs, p->opaqueData ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 3); + if (p->inputFormat==0x01) { + if (p->tag == GF_IPMPX_AUDIO_WM_INIT_TAG) { + gf_bs_write_int(bs, p->nChannels, 8); + gf_bs_write_int(bs, p->bitPerSample, 8); + gf_bs_write_int(bs, p->frequency, 32); + } else { + gf_bs_write_int(bs, p->frame_horizontal_size, 16); + gf_bs_write_int(bs, p->frame_vertical_size, 16); + gf_bs_write_int(bs, p->chroma_format, 8); + } + } + switch (p->requiredOp) { + case GF_IPMPX_WM_INSERT: + case GF_IPMPX_WM_REMARK: + gf_bs_write_int(bs, p->wmPayloadLen, 16); + gf_bs_write_data(bs, p->wmPayload, p->wmPayloadLen); + break; + case GF_IPMPX_WM_EXTRACT: + case GF_IPMPX_WM_DETECT_COMPRESSION: + gf_bs_write_int(bs, p->wmRecipientId, 16); + break; + } + if (p->opaqueData) { + gf_bs_write_int(bs, p->opaqueDataSize, 16); + gf_bs_write_data(bs, p->opaqueData, p->opaqueDataSize); + } + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_SendWatermark(u8 tag) +{ + GF_IPMPX_SendWatermark*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_SendWatermark, tag); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_SendWatermark(GF_IPMPX_Data *_p) +{ + GF_IPMPX_SendWatermark*p = (GF_IPMPX_SendWatermark*)_p; + GF_IPMPX_DELETE_ARRAY(p->payload); + GF_IPMPX_DELETE_ARRAY(p->opaqueData); + free(p); +} +static GF_Err ReadGF_IPMPX_SendWatermark(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_op_data; + GF_IPMPX_SendWatermark *p = (GF_IPMPX_SendWatermark*)_p; + p->wm_status = gf_bs_read_int(bs, 2); + p->compression_status = gf_bs_read_int(bs, 2); + has_op_data = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 3); + if (p->wm_status==GF_IPMPX_WM_PAYLOAD) p->payload = GF_IPMPX_GetByteArray(bs); + if (has_op_data) p->opaqueData = GF_IPMPX_GetByteArray(bs); + return GF_OK; +} +static u32 SizeGF_IPMPX_SendWatermark(GF_IPMPX_Data *_p) +{ + u32 size; + GF_IPMPX_SendWatermark *p = (GF_IPMPX_SendWatermark*)_p; + size = 1; + if (p->payload) size += GF_IPMPX_GetByteArraySize(p->payload); + if (p->opaqueData) size += GF_IPMPX_GetByteArraySize(p->opaqueData); + return size; +} +static GF_Err WriteGF_IPMPX_SendWatermark(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_SendWatermark*p = (GF_IPMPX_SendWatermark*)_p; + if (p->payload) p->wm_status = GF_IPMPX_WM_PAYLOAD; + gf_bs_write_int(bs, p->wm_status, 2); + gf_bs_write_int(bs, p->compression_status, 2); + gf_bs_write_int(bs, p->opaqueData ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 3); + if (p->payload) GF_IPMPX_WriteByteArray(bs, p->payload); + if (p->opaqueData) GF_IPMPX_WriteByteArray(bs, p->opaqueData); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_ToolAPI_Config() +{ + GF_IPMPX_ToolAPI_Config*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ToolAPI_Config, GF_IPMPX_TOOL_API_CONFIG_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ToolAPI_Config(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p; + GF_IPMPX_DELETE_ARRAY(p->opaqueData); + free(p); +} +static GF_Err ReadGF_IPMPX_ToolAPI_Config(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_i, has_m; + GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p; + has_i = gf_bs_read_int(bs, 1); + has_m = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 6); + if (has_i) p->Instantiation_API_ID = gf_bs_read_int(bs, 32); + if (has_m) p->Messaging_API_ID = gf_bs_read_int(bs, 32); + p->opaqueData = GF_IPMPX_GetByteArray(bs); + return GF_OK; +} +static u32 SizeGF_IPMPX_ToolAPI_Config(GF_IPMPX_Data *_p) +{ + u32 size; + GF_IPMPX_ToolAPI_Config *p = (GF_IPMPX_ToolAPI_Config*)_p; + size = 1; + if (p->Instantiation_API_ID) size += 4; + if (p->Messaging_API_ID ) size += 4; + size += GF_IPMPX_GetByteArraySize(p->opaqueData); + return size; +} +static GF_Err WriteGF_IPMPX_ToolAPI_Config(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p; + gf_bs_write_int(bs, p->Instantiation_API_ID ? 1 : 0, 1); + gf_bs_write_int(bs, p->Messaging_API_ID ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 6); + if (p->Instantiation_API_ID) gf_bs_write_int(bs, p->Instantiation_API_ID, 32); + if (p->Messaging_API_ID) gf_bs_write_int(bs, p->Messaging_API_ID, 32); + GF_IPMPX_WriteByteArray(bs, p->opaqueData); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_ISMACryp() +{ + GF_IPMPX_ISMACryp *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ISMACryp, GF_IPMPX_ISMACRYP_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ISMACryp(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p; + free(p); +} +static GF_Err ReadGF_IPMPX_ISMACryp(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p; + p->cryptoSuite = gf_bs_read_int(bs, 8); + p->IV_length = gf_bs_read_int(bs, 8); + p->use_selective_encryption = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + p->key_indicator_length = gf_bs_read_int(bs, 8); + return GF_OK; +} +static u32 SizeGF_IPMPX_ISMACryp(GF_IPMPX_Data *_p) +{ + return 4; +} +static GF_Err WriteGF_IPMPX_ISMACryp(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p; + gf_bs_write_int(bs, p->cryptoSuite, 8); + gf_bs_write_int(bs, p->IV_length, 8); + gf_bs_write_int(bs, p->use_selective_encryption ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + gf_bs_write_int(bs, p->key_indicator_length, 8); + return GF_OK; +} + +GF_IPMPX_Data *gf_ipmpx_data_new(u8 tag) +{ + switch (tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: return NewGF_IPMPX_OpaqueData(tag); + case GF_IPMPX_KEY_DATA_TAG: return NewGF_IPMPX_KeyData(); + case GF_IPMPX_SECURE_CONTAINER_TAG: return NewGF_IPMPX_SecureContainer(); + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: return NewGF_IPMPX_AddToolNotificationListener(); + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: return NewGF_IPMPX_RemoveToolNotificationListener(); + case GF_IPMPX_INIT_AUTHENTICATION_TAG: return NewGF_IPMPX_InitAuthentication(); + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: return NewGF_IPMPX_MutualAuthentication(); + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: return NewGF_IPMPX_ParametricDescription(); + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: return NewGF_IPMPX_ToolParamCapabilitiesQuery(); + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: return NewGF_IPMPX_ToolParamCapabilitiesResponse(); + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: return NewGF_IPMPX_GetToolsResponse(); + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: return NewGF_IPMPX_GetToolContext(); + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: return NewGF_IPMPX_GetToolContextResponse(); + case GF_IPMPX_CONNECT_TOOL_TAG: return NewGF_IPMPX_ConnectTool(); + case GF_IPMPX_DISCONNECT_TOOL_TAG: return NewGF_IPMPX_DisconnectTool(); + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: return NewGF_IPMPX_NotifyToolEvent(); + case GF_IPMPX_CAN_PROCESS_TAG: return NewGF_IPMPX_CanProcess(); + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: return NewGF_IPMPX_TrustSecurityMetadata(); + case GF_IPMPX_ISMACRYP_TAG: return NewGF_IPMPX_ISMACryp(); + case GF_IPMPX_GET_TOOLS_TAG: + { + GF_IPMPX_Data *p; + GF_IPMPX_DATA_ALLOC(p, GF_IPMPX_Data, GF_IPMPX_GET_TOOLS_TAG); + return p; + } + case GF_IPMPX_TRUSTED_TOOL_TAG: + { + GF_IPMPX_TrustedTool *p; + GF_IPMPX_DATA_ALLOC(p, GF_IPMPX_TrustedTool, GF_IPMPX_TRUSTED_TOOL_TAG); + p->trustSpecifications = gf_list_new(); + return (GF_IPMPX_Data *)p; + } + case GF_IPMPX_TRUST_SPECIFICATION_TAG: + { + GF_IPMPX_TrustSpecification *p; + GF_IPMPX_DATA_ALLOC(p, GF_IPMPX_TrustSpecification, GF_IPMPX_TRUST_SPECIFICATION_TAG); + return (GF_IPMPX_Data *)p; + } + case GF_IPMPX_TOOL_API_CONFIG_TAG: return NewGF_IPMPX_ToolAPI_Config(); + case GF_IPMPX_SEL_DEC_INIT_TAG: return NewGF_IPMPX_SelectiveDecryptionInit(); + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + return NewGF_IPMPX_WatermarkingInit(tag); + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + return NewGF_IPMPX_SendWatermark(tag); + + case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)malloc(sizeof(GF_IPMPX_AUTH_AlgorithmDescriptor)); + if (!p) return NULL; + memset(p, 0, sizeof(GF_IPMPX_AUTH_AlgorithmDescriptor)); + p->tag = GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG; + return (GF_IPMPX_Data *) p; + } + case GF_IPMPX_KEY_DESCRIPTOR_TAG: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)malloc(sizeof(GF_IPMPX_AUTH_KeyDescriptor)); + if (!p) return NULL; + memset(p, 0, sizeof(GF_IPMPX_AUTH_KeyDescriptor)); + p->tag = GF_IPMPX_KEY_DESCRIPTOR_TAG; + return (GF_IPMPX_Data *) p; + } + + case GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)malloc(sizeof(GF_IPMPX_ParametricDescriptionItem)); + if (!p) return NULL; + memset(p, 0, sizeof(GF_IPMPX_ParametricDescriptionItem)); + p->tag = GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG; + return (GF_IPMPX_Data *) p; + } + case GF_IPMPX_SEL_ENC_BUFFER_TAG: + { + GF_IPMPX_SelEncBuffer*p = (GF_IPMPX_SelEncBuffer*)malloc(sizeof(GF_IPMPX_SelEncBuffer)); + if (!p) return NULL; + memset(p, 0, sizeof(GF_IPMPX_SelEncBuffer)); + p->tag = GF_IPMPX_SEL_ENC_BUFFER_TAG; + return (GF_IPMPX_Data *) p; + } + case GF_IPMPX_SEL_ENC_FIELD_TAG: + { + GF_IPMPX_SelEncField*p = (GF_IPMPX_SelEncField*)malloc(sizeof(GF_IPMPX_SelEncField)); + if (!p) return NULL; + memset(p, 0, sizeof(GF_IPMPX_SelEncField)); + p->tag = GF_IPMPX_SEL_ENC_FIELD_TAG; + return (GF_IPMPX_Data *) p; + } + +/* + case GF_IPMPX_USER_QUERY_TAG: return NewGF_IPMPX_UserQuery(); + case GF_IPMPX_USER_RESPONSE_TAG: return NewGF_IPMPX_UserQueryResponse(); +*/ + default: return NULL; + } + +} + +void gf_ipmpx_data_del(GF_IPMPX_Data *_p) +{ + if (!_p) return; + switch (_p->tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: DelGF_IPMPX_OpaqueData(_p); return; + case GF_IPMPX_KEY_DATA_TAG: DelGF_IPMPX_KeyData(_p); return; + case GF_IPMPX_SECURE_CONTAINER_TAG: DelGF_IPMPX_SecureContainer(_p); return; + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: DelGF_IPMPX_AddToolNotificationListener(_p); return; + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: DelGF_IPMPX_RemoveToolNotificationListener(_p); return; + case GF_IPMPX_INIT_AUTHENTICATION_TAG: DelGF_IPMPX_InitAuthentication(_p); return; + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: DelGF_IPMPX_MutualAuthentication(_p); return; + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: DelGF_IPMPX_ParametricDescription(_p); return; + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: DelGF_IPMPX_ToolParamCapabilitiesQuery(_p); return; + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: DelGF_IPMPX_ToolParamCapabilitiesResponse(_p); return; + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: DelGF_IPMPX_GetToolsResponse(_p); return; + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: DelGF_IPMPX_GetToolContext(_p); return; + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: DelGF_IPMPX_GetToolContextResponse(_p); return; + case GF_IPMPX_CONNECT_TOOL_TAG: DelGF_IPMPX_ConnectTool(_p); return; + case GF_IPMPX_DISCONNECT_TOOL_TAG: DelGF_IPMPX_DisconnectTool(_p); return; + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: DelGF_IPMPX_NotifyToolEvent(_p); return; + case GF_IPMPX_CAN_PROCESS_TAG: DelGF_IPMPX_CanProcess(_p); return; + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: DelGF_IPMPX_TrustSecurityMetadata(_p); return; + case GF_IPMPX_TOOL_API_CONFIG_TAG: DelGF_IPMPX_ToolAPI_Config(_p); return; + case GF_IPMPX_ISMACRYP_TAG: DelGF_IPMPX_ISMACryp(_p); return; + case GF_IPMPX_SEL_DEC_INIT_TAG: DelGF_IPMPX_SelectiveDecryptionInit(_p); return; + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + DelGF_IPMPX_WatermarkingInit(_p); return; + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + DelGF_IPMPX_SendWatermark(_p); return; + +/* + case GF_IPMPX_USER_QUERY_TAG: DelGF_IPMPX_UserQuery(_p); return; + case GF_IPMPX_USER_RESPONSE_TAG: DelGF_IPMPX_UserQueryResponse(_p); return; +*/ + case GF_IPMPX_TRUSTED_TOOL_TAG: + { + GF_IPMPX_TrustedTool *p = (GF_IPMPX_TrustedTool *)_p; + while (gf_list_count(p->trustSpecifications)) { + GF_IPMPX_Data *ts = (GF_IPMPX_Data *)gf_list_get(p->trustSpecifications, 0); + gf_list_rem(p->trustSpecifications, 0); + gf_ipmpx_data_del(ts); + } + gf_list_del(p->trustSpecifications); + free(p); + return; + } + case GF_IPMPX_TRUST_SPECIFICATION_TAG: + { + GF_IPMPX_TrustSpecification *p = (GF_IPMPX_TrustSpecification *)_p; + GF_IPMPX_DELETE_ARRAY(p->CCTrustMetadata); + free(p); + return; + } + case GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG: + { + GF_IPMPX_ParametricDescriptionItem *p = (GF_IPMPX_ParametricDescriptionItem*)_p; + GF_IPMPX_DELETE_ARRAY(p->main_class); + GF_IPMPX_DELETE_ARRAY(p->subClass); + GF_IPMPX_DELETE_ARRAY(p->typeData); + GF_IPMPX_DELETE_ARRAY(p->type); + GF_IPMPX_DELETE_ARRAY(p->addedData); + free(p); + return; + } + case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG: + _p->tag = GF_IPMPX_AUTH_AlgorithmDescr_Tag; + GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication*)_p); + return; + case GF_IPMPX_KEY_DESCRIPTOR_TAG: + _p->tag = GF_IPMPX_AUTH_KeyDescr_Tag; + GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication*)_p); + return; + + case GF_IPMPX_SEL_ENC_BUFFER_TAG: + { + GF_IPMPX_SelEncBuffer*p = (GF_IPMPX_SelEncBuffer*)_p; + GF_IPMPX_DELETE_ARRAY(p->Stream_Cipher_Specific_Init_Info); + free(p); + return; + } + case GF_IPMPX_SEL_ENC_FIELD_TAG: + { + GF_IPMPX_SelEncField*p = (GF_IPMPX_SelEncField*)_p; + GF_IPMPX_DELETE_ARRAY(p->shuffleSpecificInfo); + if (p->mappingTable) free(p->mappingTable); + free(p); + return; + } + + case GF_IPMPX_GET_TOOLS_TAG: + default: + free(_p); + return; + } +} + +GF_Err GF_IPMPX_ReadData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 read) +{ + switch (_p->tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: return ReadGF_IPMPX_OpaqueData(bs, _p, read); + case GF_IPMPX_KEY_DATA_TAG: return ReadGF_IPMPX_KeyData(bs, _p, read); + case GF_IPMPX_SECURE_CONTAINER_TAG: return ReadGF_IPMPX_SecureContainer(bs, _p, read); + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: return ReadGF_IPMPX_AddToolNotificationListener(bs, _p, read); + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: return ReadGF_IPMPX_RemoveToolNotificationListener(bs, _p, read); + case GF_IPMPX_INIT_AUTHENTICATION_TAG: return ReadGF_IPMPX_InitAuthentication(bs, _p, read); + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: return ReadGF_IPMPX_MutualAuthentication(bs, _p, read); + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: return ReadGF_IPMPX_ParametricDescription(bs, _p, read); + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: return ReadGF_IPMPX_ToolParamCapabilitiesQuery(bs, _p, read); + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: return ReadGF_IPMPX_ToolParamCapabilitiesResponse(bs, _p, read); + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: return ReadGF_IPMPX_GetToolsResponse(bs, _p, read); + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: return ReadGF_IPMPX_GetToolContext(bs, _p, read); + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: return ReadGF_IPMPX_GetToolContextResponse(bs, _p, read); + case GF_IPMPX_CONNECT_TOOL_TAG: return ReadGF_IPMPX_ConnectTool(bs, _p, read); + case GF_IPMPX_DISCONNECT_TOOL_TAG: return ReadGF_IPMPX_DisconnectTool(bs, _p, read); + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: return ReadGF_IPMPX_NotifyToolEvent(bs, _p, read); + case GF_IPMPX_CAN_PROCESS_TAG: return ReadGF_IPMPX_CanProcess(bs, _p, read); + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: return ReadGF_IPMPX_TrustSecurityMetadata(bs, _p, read); + case GF_IPMPX_TOOL_API_CONFIG_TAG: return ReadGF_IPMPX_ToolAPI_Config(bs, _p, read); + case GF_IPMPX_ISMACRYP_TAG: return ReadGF_IPMPX_ISMACryp(bs, _p, read); + case GF_IPMPX_SEL_DEC_INIT_TAG: return ReadGF_IPMPX_SelectiveDecryptionInit(bs, _p, read); + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + return ReadGF_IPMPX_WatermarkingInit(bs, _p, read); + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + return ReadGF_IPMPX_SendWatermark(bs, _p, read); + +/* + case GF_IPMPX_USER_QUERY_TAG: return ReadGF_IPMPX_UserQuery(bs, _p, read); + case GF_IPMPX_USER_RESPONSE_TAG: return ReadGF_IPMPX_UserQueryResponse(bs, _p, read); +*/ + case GF_IPMPX_GET_TOOLS_TAG: return GF_OK; + default: return GF_BAD_PARAM; + } +} + +u32 gf_ipmpx_data_size(GF_IPMPX_Data *_p) +{ + switch (_p->tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: return SizeGF_IPMPX_OpaqueData(_p); + case GF_IPMPX_KEY_DATA_TAG: return SizeGF_IPMPX_KeyData(_p); + case GF_IPMPX_SECURE_CONTAINER_TAG: return SizeGF_IPMPX_SecureContainer(_p); + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: return SizeGF_IPMPX_AddToolNotificationListener(_p); + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: return SizeGF_IPMPX_RemoveToolNotificationListener(_p); + case GF_IPMPX_INIT_AUTHENTICATION_TAG: return SizeGF_IPMPX_InitAuthentication(_p); + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: return SizeGF_IPMPX_MutualAuthentication(_p); + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: return SizeGF_IPMPX_ParametricDescription(_p); + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: return SizeGF_IPMPX_ToolParamCapabilitiesQuery(_p); + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: return SizeGF_IPMPX_ToolParamCapabilitiesResponse(_p); + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: return SizeGF_IPMPX_GetToolsResponse(_p); + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: return SizeGF_IPMPX_GetToolContext(_p); + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: return SizeGF_IPMPX_GetToolContextResponse(_p); + case GF_IPMPX_CONNECT_TOOL_TAG: return SizeGF_IPMPX_ConnectTool(_p); + case GF_IPMPX_DISCONNECT_TOOL_TAG: return SizeGF_IPMPX_DisconnectTool(_p); + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: return SizeGF_IPMPX_NotifyToolEvent(_p); + case GF_IPMPX_CAN_PROCESS_TAG: return SizeGF_IPMPX_CanProcess(_p); + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: return SizeGF_IPMPX_TrustSecurityMetadata(_p); + case GF_IPMPX_TOOL_API_CONFIG_TAG: return SizeGF_IPMPX_ToolAPI_Config(_p); + case GF_IPMPX_ISMACRYP_TAG: return SizeGF_IPMPX_ISMACryp(_p); + case GF_IPMPX_SEL_DEC_INIT_TAG: return SizeGF_IPMPX_SelectiveDecryptionInit(_p); + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + return SizeGF_IPMPX_WatermarkingInit(_p); + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + return SizeGF_IPMPX_SendWatermark(_p); + +/* + case GF_IPMPX_USER_QUERY_TAG: return SizeGF_IPMPX_UserQuery(_p); + case GF_IPMPX_USER_RESPONSE_TAG: return SizeGF_IPMPX_UserQueryResponse(_p); +*/ + case GF_IPMPX_GET_TOOLS_TAG: return 0; + default: return GF_BAD_PARAM; + } +} + +GF_Err gf_ipmpx_data_write(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_Err e; + if (!_p) return GF_OK; + e = GF_IPMPX_WriteBase(bs, _p); + if (e) return e; + switch (_p->tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: return WriteGF_IPMPX_OpaqueData(bs, _p); + case GF_IPMPX_KEY_DATA_TAG: return WriteGF_IPMPX_KeyData(bs, _p); + case GF_IPMPX_SECURE_CONTAINER_TAG: return WriteGF_IPMPX_SecureContainer(bs, _p); + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: return WriteGF_IPMPX_AddToolNotificationListener(bs, _p); + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: return WriteGF_IPMPX_RemoveToolNotificationListener(bs, _p); + case GF_IPMPX_INIT_AUTHENTICATION_TAG: return WriteGF_IPMPX_InitAuthentication(bs, _p); + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: return WriteGF_IPMPX_MutualAuthentication(bs, _p); + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: return WriteGF_IPMPX_ParametricDescription(bs, _p); + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: return WriteGF_IPMPX_ToolParamCapabilitiesQuery(bs, _p); + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: return WriteGF_IPMPX_ToolParamCapabilitiesResponse(bs, _p); + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: return WriteGF_IPMPX_GetToolsResponse(bs, _p); + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: return WriteGF_IPMPX_GetToolContext(bs, _p); + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: return WriteGF_IPMPX_GetToolContextResponse(bs, _p); + case GF_IPMPX_CONNECT_TOOL_TAG: return WriteGF_IPMPX_ConnectTool(bs, _p); + case GF_IPMPX_DISCONNECT_TOOL_TAG: return WriteGF_IPMPX_DisconnectTool(bs, _p); + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: return WriteGF_IPMPX_NotifyToolEvent(bs, _p); + case GF_IPMPX_CAN_PROCESS_TAG: return WriteGF_IPMPX_CanProcess(bs, _p); + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: return WriteGF_IPMPX_TrustSecurityMetadata(bs, _p); + case GF_IPMPX_TOOL_API_CONFIG_TAG: return WriteGF_IPMPX_ToolAPI_Config(bs, _p); + case GF_IPMPX_ISMACRYP_TAG: return WriteGF_IPMPX_ISMACryp(bs, _p); + case GF_IPMPX_SEL_DEC_INIT_TAG: return WriteGF_IPMPX_SelectiveDecryptionInit(bs, _p); + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + return WriteGF_IPMPX_WatermarkingInit(bs, _p); + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + return WriteGF_IPMPX_SendWatermark(bs, _p); + +/* + case GF_IPMPX_USER_QUERY_TAG: return WriteGF_IPMPX_UserQuery(bs, _p); + case GF_IPMPX_USER_RESPONSE_TAG: return WriteGF_IPMPX_UserQueryResponse(bs, _p); +*/ + case GF_IPMPX_GET_TOOLS_TAG: return GF_OK; + default: return GF_BAD_PARAM; + } +} + diff --git a/src/odf/ipmpx_dump.c b/src/odf/ipmpx_dump.c new file mode 100644 index 0000000..5ce6df8 --- /dev/null +++ b/src/odf/ipmpx_dump.c @@ -0,0 +1,868 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + + +#define GF_IPMPX_MAX_TREE 100 + +#define GF_IPMPX_FORMAT_INDENT( ind_buf, indent ) \ + { \ + u32 z; \ + assert(GF_IPMPX_MAX_TREE>indent); \ + for (z=0; z\n", ind_buf, name); + } else { + fprintf(trace, "%s%s [\n", ind_buf, name); + } +} +static void EndList(FILE *trace, const char *name, u32 indent, Bool XMTDump) +{ + char ind_buf[GF_IPMPX_MAX_TREE]; + GF_IPMPX_FORMAT_INDENT(ind_buf, indent); + if (XMTDump) { + fprintf(trace, "%s\n", ind_buf, name); + } else { + fprintf(trace, "%s]\n", ind_buf); + } +} + +static void StartElement(FILE *trace, const char *descName, u32 indent, Bool XMTDump) +{ + char ind_buf[GF_IPMPX_MAX_TREE]; + GF_IPMPX_FORMAT_INDENT(ind_buf, indent); + + fprintf(trace, "%s", ind_buf); + if (!XMTDump) { + fprintf(trace, "%s {\n", descName); + } else { + fprintf(trace, "<%s ", descName); + } +} + +static void EndAttributes(FILE *trace, Bool XMTDump, Bool has_children) +{ + if (XMTDump) { + if (has_children) { + fprintf(trace, ">\n"); + } else { + fprintf(trace, "/>\n"); + } + } +} + +static void EndElement(FILE *trace, const char *descName, u32 indent, Bool XMTDump) +{ + char ind_buf[GF_IPMPX_MAX_TREE]; + GF_IPMPX_FORMAT_INDENT(ind_buf, indent); + + fprintf(trace, "%s", ind_buf); + if (!XMTDump) { + fprintf(trace, "}\n"); + } else { + fprintf(trace, "\n", descName); + } +} + +static void StartAttribute(FILE *trace, const char *attName, u32 indent, Bool XMTDump) +{ + char ind_buf[GF_IPMPX_MAX_TREE]; + GF_IPMPX_FORMAT_INDENT(ind_buf, indent); + if (!XMTDump) { + fprintf(trace, "%s%s ", ind_buf, attName); + } else { + fprintf(trace, "%s=\"", attName); + } +} +static void EndAttribute(FILE *trace, u32 indent, Bool XMTDump) +{ + if (!XMTDump) { + fprintf(trace, "\n"); + } else { + fprintf(trace, "\" "); + } +} + +static void DumpInt(FILE *trace, char *attName, u32 val, u32 indent, Bool XMTDump) +{ + if (!val) return; + StartAttribute(trace, attName, indent, XMTDump); + fprintf(trace, "%d", val); + EndAttribute(trace, indent, XMTDump); +} + +static void DumpLargeInt(FILE *trace, char *attName, u64 val, u32 indent, Bool XMTDump) +{ + if (!val) return; + StartAttribute(trace, attName, indent, XMTDump); + fprintf(trace, LLD, val); + EndAttribute(trace, indent, XMTDump); +} + +static void DumpBool(FILE *trace, char *attName, u32 val, u32 indent, Bool XMTDump) +{ + if (!val) return; + + StartAttribute(trace, attName, indent, XMTDump); + fprintf(trace, "%s", val ? "true" : "false"); + EndAttribute(trace, indent, XMTDump); +} + +static void DumpData(FILE *trace, const char *name, char *data, u32 dataLength, u32 indent, Bool XMTDump) +{ + u32 i; + Bool ASCII_Dump; + if (!name && !data) return; + + if (name) StartAttribute(trace, name, indent, XMTDump); + if (!XMTDump) fprintf(trace, "\""); + + ASCII_Dump = 1; + for (i=0; i126)) { + ASCII_Dump = 0; + break; + } + } + if (!ASCII_Dump && XMTDump) fprintf(trace, "data:application/octet-string,"); + for (i=0; idata) { + if (XMTDump) { + StartElement(trace, attName ? attName : (char*)"ByteArray", indent, XMTDump); + indent++; + DumpData(trace, "array", _p->data, _p->length, indent, XMTDump); + indent--; + EndAttributes(trace, 1, 0); + } else { + DumpData(trace, attName ? attName : "ByteArray", _p->data, _p->length, indent, 0); + } + } +} + + +void gf_ipmpx_dump_AUTH(GF_IPMPX_Authentication *ipa, FILE *trace, u32 indent, Bool XMTDump) +{ + switch (ipa->tag) { + case GF_IPMPX_AUTH_KeyDescr_Tag: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)ipa; + StartElement(trace, "IPMP_KeyDescriptor", indent, XMTDump); + DumpData(trace, "keyBody", p->keyBody, p->keyBodyLength, indent+1, XMTDump); + if (XMTDump) EndAttributes(trace, 1, 0); + else EndElement(trace, "", indent, 0); + } + break; + case GF_IPMPX_AUTH_AlgorithmDescr_Tag: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)ipa; + StartElement(trace, "IPMP_AlgorithmDescriptor", indent, XMTDump); + if (p->regAlgoID) { + DumpInt(trace, "regAlgoID", p->regAlgoID, indent+1, XMTDump); + } else { + gf_ipmpx_dump_ByteArray(p->specAlgoID, "specAlgoID", trace, indent+1, XMTDump); + } + EndAttributes(trace, XMTDump, 1); + if (p->OpaqueData) gf_ipmpx_dump_ByteArray(p->OpaqueData, "OpaqueData", trace, indent+1, XMTDump); + + EndElement(trace, "IPMP_AlgorithmDescriptor", indent, XMTDump); + } + break; + } +} + +void gf_ipmpx_dump_BaseData(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ +#if 0 + if (XMTDump) { + StartElement(trace, "IPMP_BaseData", indent, XMTDump); + DumpInt(trace, "dataID", _p->dataID, indent, 1); + DumpInt(trace, "Version", _p->Version, indent, 1); + EndLeafAttribute(trace, indent, XMTDump); + } else { + DumpInt(trace, "dataID", _p->dataID, indent, 0); + DumpInt(trace, "Version", _p->Version, indent, 0); + } +#endif +} + +GF_Err gf_ipmpx_dump_OpaqueData(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_OpaqueData *p = (GF_IPMPX_OpaqueData *)_p; + + StartElement(trace, (p->tag==GF_IPMPX_RIGHTS_DATA_TAG) ? "IPMP_RightsData" : "IPMP_OpaqueData", indent, XMTDump); + indent++; + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + gf_ipmpx_dump_ByteArray(p->opaqueData, (p->tag==GF_IPMPX_RIGHTS_DATA_TAG) ? "rightsInfo" : "opaqueData", trace, indent, XMTDump); + indent--; + EndElement(trace, (p->tag==GF_IPMPX_RIGHTS_DATA_TAG) ? "IPMP_RightsData" : "IPMP_OpaqueData", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_ipmpx_dump_KeyData(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p; + + StartElement(trace, "IPMP_KeyData", indent, XMTDump); + indent++; + + DumpBool(trace, "hasStartDTS", (p->flags & 1) ? 1 : 0, indent, XMTDump); + DumpBool(trace, "hasStartPacketID", (p->flags & 2) ? 1 : 0, indent, XMTDump); + DumpBool(trace, "hasEndDTS", (p->flags & 4) ? 1 : 0, indent, XMTDump); + DumpBool(trace, "hasEndPacketID", (p->flags & 8) ? 1 : 0, indent, XMTDump); + + if (p->flags & 1) DumpLargeInt(trace, "startDTS", p->startDTS, indent, XMTDump); + if (p->flags & 2) DumpInt(trace, "startPacketID", p->startPacketID, indent, XMTDump); + if (p->flags & 4) DumpLargeInt(trace, "expireDTS", p->expireDTS, indent, XMTDump); + if (p->flags & 8) DumpInt(trace, "expirePacketID", p->expirePacketID, indent, XMTDump); + + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + gf_ipmpx_dump_ByteArray(p->keyBody, "keyBody", trace, indent, XMTDump); + gf_ipmpx_dump_ByteArray(p->OpaqueData, "OpaqueData", trace, indent, XMTDump); + + indent--; + EndElement(trace, "IPMP_KeyData", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_SecureContainer(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_SecureContainer*p = (GF_IPMPX_SecureContainer*)_p; + StartElement(trace, "IPMP_SecureContainer", indent, XMTDump); + indent++; + DumpBool(trace, "isMACEncrypted", p->isMACEncrypted, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + if (p->encryptedData) gf_ipmpx_dump_ByteArray(p->encryptedData, "encryptedData", trace, indent, XMTDump); + if (p->protectedMsg) gf_ipmpx_dump_data(p->protectedMsg, trace, indent, XMTDump); + if (p->MAC) gf_ipmpx_dump_ByteArray(p->MAC, "MAC", trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_SecureContainer", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_InitAuthentication(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_InitAuthentication*p = (GF_IPMPX_InitAuthentication*)_p; + StartElement(trace, "IPMP_InitAuthentication", indent, XMTDump); + indent++; + DumpInt(trace, "Context", p->Context, indent, XMTDump); + DumpInt(trace, "AuthType", p->AuthType, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_InitAuthentication", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_ipmpx_dump_TrustSecurityMetadata(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i, j; + GF_IPMPX_TrustSecurityMetadata*p = (GF_IPMPX_TrustSecurityMetadata*)_p; + StartElement(trace, "IPMP_TrustSecurityMetadata", indent, XMTDump); + indent++; + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + + StartList(trace, "trustedTools", indent, XMTDump); + indent++; + + for (i=0; iTrustedTools); i++) { + GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)gf_list_get(p->TrustedTools, i); + StartElement(trace, "IPMP_TrustedTool", indent, XMTDump); + indent++; + DumpBin128(trace, "toolID", (char *)tt->toolID, indent, XMTDump); + DumpDate(trace, "AuditDate", tt->AuditDate, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + StartList(trace, "trustSpecifications", indent, XMTDump); + indent++; + for (j=0; jtrustSpecifications); j++) { + GF_IPMPX_TrustSpecification *ts = (GF_IPMPX_TrustSpecification *)gf_list_get(tt->trustSpecifications, j); + StartElement(trace, "IPMP_TrustSpecification", indent, XMTDump); + indent++; + DumpDate(trace, "startDate", ts->startDate, indent, XMTDump); + DumpInt(trace, "attackerProfile", ts->attackerProfile, indent, XMTDump); + DumpInt(trace, "trustedDuration", ts->trustedDuration, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + if (ts->CCTrustMetadata) gf_ipmpx_dump_ByteArray(ts->CCTrustMetadata, "CCTrustMetadata", trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_TrustSpecification", indent, XMTDump); + } + indent--; + EndList(trace, "trustSpecifications", indent, XMTDump); + indent--; + EndElement(trace, "IPMP_TrustedTool", indent, XMTDump); + } + indent--; + EndList(trace, "trustedTools", indent, XMTDump); + + indent--; + EndElement(trace, "IPMP_TrustSecurityMetadata", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_ipmpx_dump_MutualAuthentication(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i, count; + GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication*)_p; + StartElement(trace, "IPMP_MutualAuthentication", indent, XMTDump); + indent++; + DumpBool(trace, "failedNegotiation", p->failedNegotiation, indent, XMTDump); + if (gf_list_count(p->certificates)) DumpInt(trace, "certType", p->certType, indent, XMTDump); + + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + + StartList(trace, "candidateAlgorithms", indent, XMTDump); + count = gf_list_count(p->candidateAlgorithms); + indent++; + for (i=0; icandidateAlgorithms, i); + gf_ipmpx_dump_AUTH(ip_auth, trace, indent, XMTDump); + } + indent--; + EndList(trace, "candidateAlgorithms", indent, XMTDump); + + StartList(trace, "agreedAlgorithms", indent, XMTDump); + count = gf_list_count(p->agreedAlgorithms); + indent++; + for (i=0; iagreedAlgorithms, i); + gf_ipmpx_dump_AUTH(ip_auth, trace, indent, XMTDump); + } + indent--; + EndList(trace, "agreedAlgorithms", indent, XMTDump); + + if (p->AuthenticationData) gf_ipmpx_dump_ByteArray(p->AuthenticationData, "AuthenticationData", trace, indent, XMTDump); + + count = gf_list_count(p->certificates); + if (count || p->opaque || p->publicKey) { + /*type 1*/ + if (count) { + StartList(trace, "certificates", indent, XMTDump); + for (i=0; icertificates, i); + if (XMTDump) { + gf_ipmpx_dump_ByteArray(ipd, NULL, trace, indent, XMTDump); + } else { + StartAttribute(trace, "", indent, 0); + DumpData(trace, NULL, ipd->data, ipd->length, indent, 0); + if (i+1publicKey) { + gf_ipmpx_dump_AUTH((GF_IPMPX_Authentication *) p->publicKey, trace, indent, XMTDump); + } + /*type 0xFE*/ + else if (p->opaque) { + gf_ipmpx_dump_ByteArray(p->opaque, "opaque", trace, indent, XMTDump); + } + if (!XMTDump) StartAttribute(trace, "trustData", indent, 0); + else { + StartElement(trace, "trustData", indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + } + gf_ipmpx_dump_data((GF_IPMPX_Data *)p->trustData, trace, indent, XMTDump); + if (XMTDump) EndElement(trace, "trustData", indent, XMTDump); + gf_ipmpx_dump_ByteArray(p->authCodes, "authCodes", trace, indent, XMTDump); + } + + indent--; + EndElement(trace, "IPMP_MutualAuthentication", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_ipmpx_dump_GetToolsResponse(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_GetToolsResponse*p = (GF_IPMPX_GetToolsResponse*)_p; + StartElement(trace, "IPMP_GetToolsResponse", indent, XMTDump); + indent++; + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + DumpDescList(p->ipmp_tools, trace, indent, "IPMP_Tools", XMTDump, 0); + indent--; + EndElement(trace, "IPMP_GetToolsResponse", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_ipmpx_dump_ParametricDescription(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i; + GF_IPMPX_ParametricDescription*p = (GF_IPMPX_ParametricDescription*)_p; + StartElement(trace, "IPMP_ParametricDescription", indent, XMTDump); + indent++; + DumpInt(trace, "majorVersion", p->majorVersion, indent, XMTDump); + DumpInt(trace, "minorVersion", p->minorVersion, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + gf_ipmpx_dump_ByteArray(p->descriptionComment, "descriptionComment", trace, indent, XMTDump); + + StartList(trace, "descriptions", indent, XMTDump); + indent++; + for (i=0; idescriptions); i++) { + GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_list_get(p->descriptions, i); + StartElement(trace, "IPMP_ParametricDescriptionItem", indent, XMTDump); + indent++; + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_ByteArray(it->main_class, "class", trace, indent, XMTDump); + gf_ipmpx_dump_ByteArray(it->subClass, "subClass", trace, indent, XMTDump); + gf_ipmpx_dump_ByteArray(it->typeData, "typeData", trace, indent, XMTDump); + gf_ipmpx_dump_ByteArray(it->type, "type", trace, indent, XMTDump); + gf_ipmpx_dump_ByteArray(it->addedData, "addedData", trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_ParametricDescriptionItem", indent, XMTDump); + } + indent--; + EndList(trace, "descriptions", indent, XMTDump); + + indent--; + EndElement(trace, "IPMP_ParametricDescription", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_ToolParamCapabilitiesQuery(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_ToolParamCapabilitiesQuery*p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p; + StartElement(trace, "IPMP_ToolParamCapabilitiesQuery", indent, XMTDump); + indent++; + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + if (!XMTDump) StartAttribute(trace, "description", indent, 0); + else { + StartElement(trace, "description", indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + } + gf_ipmpx_dump_data((GF_IPMPX_Data *) p->description, trace, indent, XMTDump); + if (XMTDump) EndElement(trace, "description", indent, XMTDump); + + indent--; + EndElement(trace, "IPMP_ToolParamCapabilitiesQuery", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_ToolParamCapabilitiesResponse(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_ToolParamCapabilitiesResponse*p = (GF_IPMPX_ToolParamCapabilitiesResponse*)_p; + StartElement(trace, "IPMP_ToolParamCapabilitiesResponse", indent, XMTDump); + indent++; + DumpBool(trace, "capabilitiesSupported", p->capabilitiesSupported, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_ToolParamCapabilitiesResponse", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_ConnectTool(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_ConnectTool*p = (GF_IPMPX_ConnectTool*)_p; + StartElement(trace, "IPMP_ConnectTool", indent, XMTDump); + indent++; + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + if (!XMTDump) StartAttribute(trace, "toolDescriptor", indent, 0); + else { + StartElement(trace, "toolDescriptor", indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + } + gf_odf_dump_desc((GF_Descriptor *)p->toolDescriptor, trace, indent, XMTDump); + if (XMTDump) EndElement(trace, "toolDescriptor", indent, XMTDump); + + indent--; + EndElement(trace, "IPMP_ConnectTool", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_DisconnectTool(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_DisconnectTool*p = (GF_IPMPX_DisconnectTool*)_p; + StartElement(trace, "IPMP_DisconnectTool", indent, XMTDump); + indent++; + DumpInt(trace, "IPMP_ToolContextID", p->IPMP_ToolContextID, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_DisconnectTool", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_GetToolContext(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p; + StartElement(trace, "IPMP_GetToolContext", indent, XMTDump); + indent++; + DumpInt(trace, "scope", p->scope, indent, XMTDump); + DumpInt(trace, "IPMP_DescriptorIDEx", p->IPMP_DescriptorIDEx, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_GetToolContext", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_GetToolContextResponse(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p; + StartElement(trace, "IPMP_GetToolContextResponse", indent, XMTDump); + indent++; + DumpInt(trace, "OD_ID", p->OD_ID, indent, XMTDump); + DumpInt(trace, "ESD_ID", p->ESD_ID, indent, XMTDump); + DumpInt(trace, "IPMP_ToolContextID", p->IPMP_ToolContextID, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_GetToolContextResponse", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_AddToolNotificationListener(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i; + GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p; + StartElement(trace, "IPMP_AddToolNotificationListener", indent, XMTDump); + indent++; + DumpInt(trace, "scope", p->scope, indent, XMTDump); + StartAttribute(trace, "eventType", indent, XMTDump); + if (!XMTDump) fprintf(trace, "\""); + for (i=0; ieventTypeCount; i++) { + if (XMTDump) { + fprintf(trace, "\'%d\'", p->eventType[i]); + if (i+1eventTypeCount) fprintf(trace, " "); + } else { + fprintf(trace, "%d", p->eventType[i]); + if (i+1eventTypeCount) fprintf(trace, ","); + } + } + if (!XMTDump) fprintf(trace, "\""); + EndAttribute(trace, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_AddToolNotificationListener", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_RemoveToolNotificationListener(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i; + GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p; + StartElement(trace, "IPMP_RemoveToolNotificationListener", indent, XMTDump); + indent++; + StartAttribute(trace, "eventType", indent, XMTDump); + if (!XMTDump) fprintf(trace, "\""); + for (i=0; ieventTypeCount; i++) { + if (XMTDump) { + fprintf(trace, "\'%d\'", p->eventType[i]); + if (i+1eventTypeCount) fprintf(trace, " "); + } else { + fprintf(trace, "%d", p->eventType[i]); + if (i+1eventTypeCount) fprintf(trace, ","); + } + } + if (!XMTDump) fprintf(trace, "\""); + EndAttribute(trace, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_RemoveToolNotificationListener", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_NotifyToolEvent(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p; + StartElement(trace, "IPMP_NotifyToolEvent", indent, XMTDump); + indent++; + DumpInt(trace, "OD_ID", p->OD_ID, indent, XMTDump); + DumpInt(trace, "ESD_ID", p->ESD_ID, indent, XMTDump); + DumpInt(trace, "IPMP_ToolContextID", p->IPMP_ToolContextID, indent, XMTDump); + DumpInt(trace, "eventType", p->eventType, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_NotifyToolEvent", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_CanProcess(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_CanProcess*p = (GF_IPMPX_CanProcess*)_p; + StartElement(trace, "IPMP_CanProcess", indent, XMTDump); + indent++; + DumpBool(trace, "canProcess", p->canProcess, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_CanProcess", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_ToolAPI_Config(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p; + StartElement(trace, "IPMP_ToolAPI_Config", indent, XMTDump); + indent++; + DumpInt(trace, "Instantiation_API_ID", p->Instantiation_API_ID, indent, XMTDump); + DumpInt(trace, "Messaging_API_ID", p->Messaging_API_ID, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + gf_ipmpx_dump_ByteArray(p->opaqueData, "opaqueData", trace, indent, XMTDump); + indent--; + EndElement(trace, "IPMP_ToolAPI_Config", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_WatermarkingInit(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_WatermarkingInit*p = (GF_IPMPX_WatermarkingInit*)_p; + StartElement(trace, (char*) (_p->tag==GF_IPMPX_AUDIO_WM_INIT_TAG) ? "IPMP_AudioWatermarkingInit" : "IPMP_VideoWatermarkingInit", indent, XMTDump); + indent++; + DumpInt(trace, "inputFormat", p->inputFormat, indent, XMTDump); + DumpInt(trace, "requiredOp", p->requiredOp, indent, XMTDump); + if (p->inputFormat==0x01) { + if (_p->tag==GF_IPMPX_AUDIO_WM_INIT_TAG) { + DumpInt(trace, "nChannels", p->nChannels, indent, XMTDump); + DumpInt(trace, "bitPerSample", p->bitPerSample, indent, XMTDump); + DumpInt(trace, "frequency", p->frequency, indent, XMTDump); + } else { + DumpInt(trace, "frame_horizontal_size", p->frame_horizontal_size, indent, XMTDump); + DumpInt(trace, "frame_vertical_size", p->frame_vertical_size, indent, XMTDump); + DumpInt(trace, "chroma_format", p->chroma_format, indent, XMTDump); + } + } + switch (p->requiredOp) { + case GF_IPMPX_WM_INSERT: + case GF_IPMPX_WM_REMARK: + DumpData(trace, "wmPayload", p->wmPayload, p->wmPayloadLen, indent, XMTDump); + break; + case GF_IPMPX_WM_EXTRACT: + case GF_IPMPX_WM_DETECT_COMPRESSION: + DumpInt(trace, "wmRecipientId", p->wmRecipientId, indent, XMTDump); + break; + } + if (p->opaqueData) DumpData(trace, "opaqueData", p->opaqueData, p->opaqueDataSize, indent, XMTDump); + + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, (char*) (_p->tag==GF_IPMPX_AUDIO_WM_INIT_TAG) ? "IPMP_AudioWatermarkingInit" : "IPMP_VideoWatermarkingInit", indent, XMTDump); + return GF_OK; +} +GF_Err gf_ipmpx_dump_SendWatermark(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_SendWatermark*p = (GF_IPMPX_SendWatermark*)_p; + StartElement(trace, (char*) (_p->tag==GF_IPMPX_AUDIO_WM_SEND_TAG) ? "IPMP_SendAudioWatermark" : "IPMP_SendVideoWatermark", indent, XMTDump); + indent++; + DumpInt(trace, "wmStatus", p->wm_status, indent, XMTDump); + DumpInt(trace, "compression_status", p->compression_status, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + if (p->wm_status==GF_IPMPX_WM_PAYLOAD) gf_ipmpx_dump_ByteArray(p->payload, "payload", trace, indent, XMTDump); + if (p->opaqueData) gf_ipmpx_dump_ByteArray(p->opaqueData, "opaqueData", trace, indent, XMTDump); + indent--; + EndElement(trace, (char*) (_p->tag==GF_IPMPX_AUDIO_WM_SEND_TAG) ? "IPMP_SendAudioWatermark" : "IPMP_SendVideoWatermark", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_ipmpx_dump_SelectiveDecryptionInit(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i, count; + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + StartElement(trace, "IPMP_SelectiveDecryptionInit", indent, XMTDump); + indent++; + DumpInt(trace, "mediaTypeExtension", p->mediaTypeExtension, indent, XMTDump); + DumpInt(trace, "mediaTypeIndication", p->mediaTypeIndication, indent, XMTDump); + DumpInt(trace, "profileLevelIndication", p->profileLevelIndication, indent, XMTDump); + DumpInt(trace, "compliance", p->compliance, indent, XMTDump); + if (p->RLE_Data) DumpData_16(trace, "RLE_Data", p->RLE_Data, p->RLE_DataLength, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + + count = gf_list_count(p->SelEncBuffer); + if (count) { + StartList(trace, "SelectiveBuffers", indent, XMTDump); + indent++; + for (i=0; iSelEncBuffer, i); + StartElement(trace, "IPMP_SelectiveBuffer", indent, XMTDump); + indent++; + DumpBin128(trace, "cipher_Id", (char*)sb->cipher_Id, indent, XMTDump); + DumpInt(trace, "syncBoundary", sb->syncBoundary, indent, XMTDump); + if (!sb->Stream_Cipher_Specific_Init_Info) { + DumpInt(trace, "mode", sb->mode, indent, XMTDump); + DumpInt(trace, "blockSize", sb->blockSize, indent, XMTDump); + DumpInt(trace, "keySize", sb->keySize, indent, XMTDump); + } + EndAttributes(trace, XMTDump, 1); + if (sb->Stream_Cipher_Specific_Init_Info) + gf_ipmpx_dump_ByteArray(sb->Stream_Cipher_Specific_Init_Info, "StreamCipher", trace, indent, XMTDump); + + indent--; + EndElement(trace, "IPMP_SelectiveBuffer", indent, XMTDump); + } + indent--; + EndList(trace, "SelectiveBuffers", indent, XMTDump); + } + + count = gf_list_count(p->SelEncFields); + if (!p->RLE_Data && count) { + StartList(trace, "SelectiveFields", indent, XMTDump); + indent++; + for (i=0; iSelEncFields, i); + StartElement(trace, "IPMP_SelectiveField", indent, XMTDump); + indent++; + DumpInt(trace, "field_Id", sf->field_Id, indent, XMTDump); + DumpInt(trace, "field_Scope", sf->field_Scope, indent, XMTDump); + DumpInt(trace, "buf", sf->buf, indent, XMTDump); + if (sf->mappingTable) DumpData_16(trace, "mappingTable", sf->mappingTable, sf->mappingTableSize, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + if (sf->shuffleSpecificInfo) + gf_ipmpx_dump_ByteArray(sf->shuffleSpecificInfo, "shuffleSpecificInfo", trace, indent, XMTDump); + + indent--; + EndElement(trace, "IPMP_SelectiveField", indent, XMTDump); + } + indent--; + EndList(trace, "SelectiveFields", indent, XMTDump); + } + + indent--; + EndElement(trace, "IPMP_SelectiveDecryptionInit", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_ipmpx_dump_ISMACryp(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p; + StartElement(trace, "ISMACryp_Data", indent, XMTDump); + indent++; + DumpInt(trace, "crypto_suite", p->cryptoSuite, indent, XMTDump); + DumpInt(trace, "IV_length", p->IV_length, indent, XMTDump); + DumpBool(trace, "selective_encryption", p->use_selective_encryption, indent, XMTDump); + DumpInt(trace, "key_indicator_length", p->key_indicator_length, indent, XMTDump); + EndAttributes(trace, XMTDump, 1); + gf_ipmpx_dump_BaseData(_p, trace, indent, XMTDump); + indent--; + EndElement(trace, "ISMACryp_Data", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_ipmpx_dump_data(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump) +{ + switch (_p->tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: return gf_ipmpx_dump_OpaqueData(_p, trace, indent, XMTDump); + case GF_IPMPX_KEY_DATA_TAG: return gf_ipmpx_dump_KeyData(_p, trace, indent, XMTDump); + case GF_IPMPX_SECURE_CONTAINER_TAG: return gf_ipmpx_dump_SecureContainer(_p, trace, indent, XMTDump); + case GF_IPMPX_INIT_AUTHENTICATION_TAG: return gf_ipmpx_dump_InitAuthentication(_p, trace, indent, XMTDump); + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: return gf_ipmpx_dump_TrustSecurityMetadata(_p, trace, indent, XMTDump); + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: return gf_ipmpx_dump_MutualAuthentication(_p, trace, indent, XMTDump); + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: return gf_ipmpx_dump_GetToolsResponse(_p, trace, indent, XMTDump); + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: return gf_ipmpx_dump_ParametricDescription(_p, trace, indent, XMTDump); + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: return gf_ipmpx_dump_ToolParamCapabilitiesQuery(_p, trace, indent, XMTDump); + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: return gf_ipmpx_dump_ToolParamCapabilitiesResponse(_p, trace, indent, XMTDump); + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: return gf_ipmpx_dump_GetToolContext(_p, trace, indent, XMTDump); + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: return gf_ipmpx_dump_GetToolContextResponse(_p, trace, indent, XMTDump); + case GF_IPMPX_CONNECT_TOOL_TAG: return gf_ipmpx_dump_ConnectTool(_p, trace, indent, XMTDump); + case GF_IPMPX_DISCONNECT_TOOL_TAG: return gf_ipmpx_dump_DisconnectTool(_p, trace, indent, XMTDump); + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: return gf_ipmpx_dump_AddToolNotificationListener(_p, trace, indent, XMTDump); + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: return gf_ipmpx_dump_RemoveToolNotificationListener(_p, trace, indent, XMTDump); + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: return gf_ipmpx_dump_NotifyToolEvent(_p, trace, indent, XMTDump); + case GF_IPMPX_CAN_PROCESS_TAG: return gf_ipmpx_dump_CanProcess(_p, trace, indent, XMTDump); + case GF_IPMPX_ISMACRYP_TAG: return gf_ipmpx_dump_ISMACryp(_p, trace, indent, XMTDump); + case GF_IPMPX_TOOL_API_CONFIG_TAG: return gf_ipmpx_dump_ToolAPI_Config(_p, trace, indent, XMTDump); + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + return gf_ipmpx_dump_WatermarkingInit(_p, trace, indent, XMTDump); + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + return gf_ipmpx_dump_SendWatermark(_p, trace, indent, XMTDump); + case GF_IPMPX_SEL_DEC_INIT_TAG: return gf_ipmpx_dump_SelectiveDecryptionInit(_p, trace, indent, XMTDump); + +/* + case GF_IPMPX_USER_QUERY_TAG: return gf_ipmpx_dump_UserQuery(_p, trace, indent, XMTDump); + case GF_IPMPX_USER_RESPONSE_TAG: return gf_ipmpx_dump_UserQueryResponse(_p, trace, indent, XMTDump); +*/ + case GF_IPMPX_GET_TOOLS_TAG: return GF_BAD_PARAM; + default: return GF_BAD_PARAM; + } +} diff --git a/src/odf/ipmpx_parse.c b/src/odf/ipmpx_parse.c new file mode 100644 index 0000000..0272f98 --- /dev/null +++ b/src/odf/ipmpx_parse.c @@ -0,0 +1,682 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include + +void GF_IPMPX_AUTH_Delete(GF_IPMPX_Authentication *auth); + + +u8 gf_ipmpx_get_tag(char *dataName) +{ + if (!stricmp(dataName, "IPMP_KeyData")) return GF_IPMPX_KEY_DATA_TAG; + else if (!stricmp(dataName, "IPMP_RightsData")) return GF_IPMPX_RIGHTS_DATA_TAG; + else if (!stricmp(dataName, "IPMP_OpaqueData")) return GF_IPMPX_OPAQUE_DATA_TAG; + else if (!stricmp(dataName, "IPMP_SecureContainer")) return GF_IPMPX_SECURE_CONTAINER_TAG; + else if (!stricmp(dataName, "IPMP_InitAuthentication")) return GF_IPMPX_INIT_AUTHENTICATION_TAG; + else if (!stricmp(dataName, "IPMP_TrustSecurityMetadata")) return GF_IPMPX_TRUST_SECURITY_METADATA_TAG; + else if (!stricmp(dataName, "IPMP_TrustedTool")) return GF_IPMPX_TRUSTED_TOOL_TAG; + else if (!stricmp(dataName, "IPMP_TrustSpecification")) return GF_IPMPX_TRUST_SPECIFICATION_TAG; + else if (!stricmp(dataName, "IPMP_MutualAuthentication")) return GF_IPMPX_MUTUAL_AUTHENTICATION_TAG; + else if (!stricmp(dataName, "IPMP_AlgorithmDescriptor")) return GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG; + else if (!stricmp(dataName, "IPMP_KeyDescriptor")) return GF_IPMPX_KEY_DESCRIPTOR_TAG; + else if (!stricmp(dataName, "IPMP_GetToolsResponse")) return GF_IPMPX_GET_TOOLS_RESPONSE_TAG; + else if (!stricmp(dataName, "IPMP_ParametricDescription")) return GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG; + else if (!stricmp(dataName, "IPMP_ParametricDescriptionItem")) return GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG; + else if (!stricmp(dataName, "IPMP_ToolParamCapabilitiesQuery")) return GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG; + else if (!stricmp(dataName, "IPMP_ToolParamCapabilitiesResponse")) return GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG; + else if (!stricmp(dataName, "IPMP_ConnectTool")) return GF_IPMPX_CONNECT_TOOL_TAG; + else if (!stricmp(dataName, "IPMP_DisconnectTool")) return GF_IPMPX_DISCONNECT_TOOL_TAG; + else if (!stricmp(dataName, "IPMP_GetToolContext")) return GF_IPMPX_GET_TOOL_CONTEXT_TAG; + else if (!stricmp(dataName, "IPMP_GetToolContextResponse")) return GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG; + else if (!stricmp(dataName, "IPMP_AddToolNotificationListener")) return GF_IPMPX_ADD_TOOL_LISTENER_TAG; + else if (!stricmp(dataName, "IPMP_RemoveToolNotificationListener")) return GF_IPMPX_REMOVE_TOOL_LISTENER_TAG; + else if (!stricmp(dataName, "IPMP_NotifyToolEvent")) return GF_IPMPX_NOTIFY_TOOL_EVENT_TAG; + else if (!stricmp(dataName, "IPMP_CanProcess")) return GF_IPMPX_CAN_PROCESS_TAG; + else if (!stricmp(dataName, "IPMP_ToolAPI_Config")) return GF_IPMPX_TOOL_API_CONFIG_TAG; + else if (!stricmp(dataName, "IPMP_AudioWatermarkingInit")) return GF_IPMPX_AUDIO_WM_INIT_TAG; + else if (!stricmp(dataName, "IPMP_VideoWatermarkingInit")) return GF_IPMPX_VIDEO_WM_INIT_TAG; + else if (!stricmp(dataName, "IPMP_SendAudioWatermark")) return GF_IPMPX_AUDIO_WM_SEND_TAG; + else if (!stricmp(dataName, "IPMP_SendVideoWatermark")) return GF_IPMPX_VIDEO_WM_SEND_TAG; + else if (!stricmp(dataName, "IPMP_SelectiveDecryptionInit")) return GF_IPMPX_SEL_DEC_INIT_TAG; + else if (!stricmp(dataName, "IPMP_SelectiveBuffer")) return GF_IPMPX_SEL_ENC_BUFFER_TAG; + else if (!stricmp(dataName, "IPMP_SelectiveField")) return GF_IPMPX_SEL_ENC_FIELD_TAG; + else if (!stricmp(dataName, "ISMACryp_Data")) return GF_IPMPX_ISMACRYP_TAG; + return 0; +} + +u32 gf_ipmpx_get_field_type(GF_IPMPX_Data *p, char *fieldName) +{ + switch (p->tag) { + case GF_IPMPX_KEY_DATA_TAG: + if (!stricmp(fieldName, "keyBody")|| !stricmp(fieldName, "opaqueData")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_RIGHTS_DATA_TAG: + if (!stricmp(fieldName, "rightsInfo")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_OPAQUE_DATA_TAG: + if (!stricmp(fieldName, "OpaqueData")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_SECURE_CONTAINER_TAG: + if (!stricmp(fieldName, "encryptedData") || !stricmp(fieldName, "MAC")) return GF_ODF_FT_IPMPX_BA; + else if (!stricmp(fieldName, "protectedMsg")) return GF_ODF_FT_IPMPX; + break; + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: + if (!stricmp(fieldName, "trustedTools")) return GF_ODF_FT_IPMPX_LIST; + break; + case GF_IPMPX_TRUSTED_TOOL_TAG: + if (!stricmp(fieldName, "trustSpecifications")) return GF_ODF_FT_IPMPX_LIST; + break; + case GF_IPMPX_TRUST_SPECIFICATION_TAG: + if (!stricmp(fieldName, "CCTrustMetadata")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: + if (!stricmp(fieldName, "candidateAlgorithms") || !stricmp(fieldName, "agreedAlgorithms")) return GF_ODF_FT_IPMPX_LIST; + else if (!stricmp(fieldName, "certificates")) return GF_ODF_FT_IPMPX_BA_LIST; + else if (!stricmp(fieldName, "publicKey") || !stricmp(fieldName, "trustData")) return GF_ODF_FT_IPMPX; + else if (!stricmp(fieldName, "authCodes") || !stricmp(fieldName, "opaque") || !stricmp(fieldName, "AuthenticationData")) + return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG: + if (!stricmp(fieldName, "specAlgoID") || !stricmp(fieldName, "OpaqueData")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: + if (!stricmp(fieldName, "ipmp_tools")) return GF_ODF_FT_OD_LIST; + break; + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: + if (!stricmp(fieldName, "descriptionComment")) return GF_ODF_FT_IPMPX_BA; + else if (!stricmp(fieldName, "descriptions")) return GF_ODF_FT_IPMPX_LIST; + break; + case GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG: + /*all is IPMPX data*/ + return GF_ODF_FT_IPMPX_BA; + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: + if (!stricmp(fieldName, "description")) return GF_ODF_FT_IPMPX; + break; + case GF_IPMPX_CONNECT_TOOL_TAG: + if (!stricmp(fieldName, "toolDescriptor")) return GF_ODF_FT_OD; + break; + case GF_IPMPX_TOOL_API_CONFIG_TAG: + if (!stricmp(fieldName, "opaqueData")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + if (!stricmp(fieldName, "payload") || !stricmp(fieldName, "opaqueData")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_SEL_DEC_INIT_TAG: + if (!stricmp(fieldName, "SelectiveBuffers") || !stricmp(fieldName, "SelectiveFields")) return GF_ODF_FT_IPMPX_LIST; + break; + case GF_IPMPX_SEL_ENC_BUFFER_TAG: + if (!stricmp(fieldName, "StreamCipher")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_SEL_ENC_FIELD_TAG: + if (!stricmp(fieldName, "shuffleSpecificInfo")) return GF_ODF_FT_IPMPX_BA; + break; + default: + break; + } + return 0; +} + +#define GET_U8(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (u8) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (u8) d; } } +#define GET_U16(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (u16) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (u16) d; } } +#define GET_U32(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (u32) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (u32) d; } } +#define GET_S32(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (s32) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (s32) d; } } +#define GET_BOOL(field) { ret = 1; field = (!stricmp(val, "true") || !stricmp(val, "1")) ? 1 : 0; } + +#define GET_DOUBLE(field) { Float v; ret = 1; sscanf(val, "%f", &v); field = (Double) v;} +#define GET_STRING(field) { ret = 1; field = strdup(val); if (val[0] == '"') strcpy(field, val+1); if (field[strlen(field)-1] == '"') field[strlen(field)-1] = 0; } + +void GF_IPMPX_ParseBinData(char *val, char **out_data, u32 *out_data_size) +{ + u32 i, c, len; + char s[3]; + + if (val[0] != '%') { + len = *out_data_size = strlen(val); + *out_data = (char*)malloc(sizeof(char) * len); + memcpy(*out_data, val, sizeof(char) * len); + return; + } + + len = strlen(val) / 3; + if (*out_data) free(*out_data); + *out_data_size = len; + *out_data = (char*)malloc(sizeof(char) * len); + s[2] = 0; + for (i=0; itag) { + case GF_IPMPX_KEY_DATA_TAG: + { + GF_IPMPX_KeyData *p = (GF_IPMPX_KeyData*)_p; + if (!stricmp(fieldName, "hasStartDTS")) { + if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~1; + else p->flags |= 1; + ret = 1; + } + else if (!stricmp(fieldName, "hasStartPacketID")) { + if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~2; + else p->flags |= 2; + ret = 1; + } + else if (!stricmp(fieldName, "hasExpireDTS")) { + if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~4; + else p->flags |= 4; + ret = 1; + } + else if (!stricmp(fieldName, "hasExpirePacketID")) { + if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~8; + else p->flags |= 8; + ret = 1; + } + else if (!stricmp(fieldName, "startDTS")) GET_U32(p->startDTS) + else if (!stricmp(fieldName, "startPacketID")) GET_U32(p->startPacketID) + else if (!stricmp(fieldName, "expireDTS")) GET_U32(p->expireDTS) + else if (!stricmp(fieldName, "expirePacketID")) GET_U32(p->expirePacketID) + } + break; + case GF_IPMPX_SECURE_CONTAINER_TAG: + { + GF_IPMPX_SecureContainer*p = (GF_IPMPX_SecureContainer*)_p; + if (!stricmp(fieldName, "isMACEncrypted")) GET_BOOL(p->isMACEncrypted) + } + break; + case GF_IPMPX_INIT_AUTHENTICATION_TAG: + { + GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p; + if (!stricmp(fieldName, "Context")) GET_U32(p->Context) + else if (!stricmp(fieldName, "AuthType")) GET_U8(p->AuthType) + } + break; + case GF_IPMPX_TRUSTED_TOOL_TAG: + { + GF_IPMPX_TrustedTool *p = (GF_IPMPX_TrustedTool*)_p; + if (!stricmp(fieldName, "toolID")) { GF_IPMPX_ParseBin128(val, &p->toolID); ret = 1; } + else if (!stricmp(fieldName, "AuditDate")) { GF_IPMPX_ParseDate(val, &p->AuditDate); ret = 1; } + } + break; + case GF_IPMPX_TRUST_SPECIFICATION_TAG: + { + GF_IPMPX_TrustSpecification *p = (GF_IPMPX_TrustSpecification*)_p; + if (!stricmp(fieldName, "startDate")) { GF_IPMPX_ParseDate(val, &p->startDate); ret = 1; } + else if (!stricmp(fieldName, "attackerProfile")) GET_U8(p->attackerProfile) + else if (!stricmp(fieldName, "trustedDuration")) GET_U32(p->trustedDuration) + } + break; + case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)_p; + if (!stricmp(fieldName, "regAlgoID")) GET_U16(p->regAlgoID) + } + break; + case GF_IPMPX_KEY_DESCRIPTOR_TAG: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)_p; + if (!stricmp(fieldName, "keyBody")) { + u32 s; + GF_IPMPX_ParseBinData(val, &p->keyBody, &s); + p->keyBodyLength = s; + ret = 1; + } + } + break; + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: + { + GF_IPMPX_ParametricDescription*p = (GF_IPMPX_ParametricDescription*)_p; + if (!stricmp(fieldName, "majorVersion")) GET_U8(p->majorVersion) + else if (!stricmp(fieldName, "minorVersion")) GET_U8(p->minorVersion) + } + break; + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: + { + GF_IPMPX_ToolParamCapabilitiesResponse*p = (GF_IPMPX_ToolParamCapabilitiesResponse*)_p; + if (!stricmp(fieldName, "capabilitiesSupported")) GET_BOOL(p->capabilitiesSupported) + } + break; + case GF_IPMPX_DISCONNECT_TOOL_TAG: + { + GF_IPMPX_DisconnectTool*p = (GF_IPMPX_DisconnectTool*)_p; + if (!stricmp(fieldName, "IPMP_ToolContextID")) GET_U16(p->IPMP_ToolContextID) + } + break; + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: + { + GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p; + if (!stricmp(fieldName, "scope")) GET_U8(p->scope) + else if (!stricmp(fieldName, "IPMP_DescriptorIDEx")) GET_U16(p->IPMP_DescriptorIDEx) + } + break; + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: + { + GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p; + if (!stricmp(fieldName, "OD_ID")) GET_U16(p->OD_ID) + else if (!stricmp(fieldName, "ESD_ID")) GET_U16(p->ESD_ID) + else if (!stricmp(fieldName, "IPMP_ToolContextID")) GET_U16(p->IPMP_ToolContextID) + } + break; + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: + { + GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p; + if (!stricmp(fieldName, "eventType")) return GF_IPMPX_ParseEventType(val, p->eventType, &p->eventTypeCount); + else if (!stricmp(fieldName, "scope")) GET_U8(p->scope) + } + break; + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: + { + GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p; + if (!stricmp(fieldName, "eventType")) return GF_IPMPX_ParseEventType(val, p->eventType, &p->eventTypeCount); + } + break; + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: + { + GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p; + if (!stricmp(fieldName, "OD_ID")) GET_U16(p->OD_ID) + else if (!stricmp(fieldName, "ESD_ID")) GET_U16(p->ESD_ID) + else if (!stricmp(fieldName, "IPMP_ToolContextID")) GET_U16(p->IPMP_ToolContextID) + else if (!stricmp(fieldName, "eventType")) GET_U8(p->eventType) + } + break; + case GF_IPMPX_CAN_PROCESS_TAG: + { + GF_IPMPX_CanProcess*p = (GF_IPMPX_CanProcess*)_p; + if (!stricmp(fieldName, "canProcess")) GET_BOOL(p->canProcess) + } + break; + case GF_IPMPX_TOOL_API_CONFIG_TAG: + { + GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p; + if (!stricmp(fieldName, "Instantiation_API_ID")) GET_U32(p->Instantiation_API_ID) + else if (!stricmp(fieldName, "Messaging_API_ID")) GET_U32(p->Messaging_API_ID) + } + break; + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + { + GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p; + if (!stricmp(fieldName, "inputFormat")) GET_U8(p->inputFormat) + else if (!stricmp(fieldName, "requiredOp")) GET_U8(p->requiredOp) + else if (!stricmp(fieldName, "nChannels")) GET_U8(p->nChannels) + else if (!stricmp(fieldName, "bitPerSample")) GET_U8(p->bitPerSample) + else if (!stricmp(fieldName, "frequency")) GET_U32(p->frequency) + else if (!stricmp(fieldName, "frame_horizontal_size")) GET_U16(p->frame_horizontal_size) + else if (!stricmp(fieldName, "frame_vertical_size")) GET_U16(p->frame_vertical_size) + else if (!stricmp(fieldName, "chroma_format")) GET_U8(p->chroma_format) + else if (!stricmp(fieldName, "wmPayload")) { ret=1; GF_IPMPX_ParseBinData(val, &p->wmPayload, &p->wmPayloadLen); } + else if (!stricmp(fieldName, "opaqueData")) { ret=1; GF_IPMPX_ParseBinData(val, &p->opaqueData, &p->opaqueDataSize); } + else if (!stricmp(fieldName, "wmRecipientId")) GET_U16(p->wmRecipientId) + } + break; + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + { + GF_IPMPX_SendWatermark *p = (GF_IPMPX_SendWatermark*)_p; + if (!stricmp(fieldName, "wm_status")) GET_U8(p->wm_status) + else if (!stricmp(fieldName, "compression_status")) GET_U8(p->compression_status) + } + break; + case GF_IPMPX_SEL_DEC_INIT_TAG: + { + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + if (!stricmp(fieldName, "mediaTypeExtension")) GET_U8(p->mediaTypeExtension) + else if (!stricmp(fieldName, "mediaTypeExtension")) GET_U8(p->mediaTypeExtension) + else if (!stricmp(fieldName, "mediaTypeIndication")) GET_U8(p->mediaTypeIndication) + else if (!stricmp(fieldName, "profileLevelIndication")) GET_U8(p->profileLevelIndication) + else if (!stricmp(fieldName, "compliance")) GET_U8(p->compliance) + else if (!stricmp(fieldName, "RLE_Data")) { ret=1; gf_ipmpx_data_parse_16(val, &p->RLE_Data, &p->RLE_DataLength); } + } + break; + case GF_IPMPX_SEL_ENC_BUFFER_TAG: + { + GF_IPMPX_SelEncBuffer*p = (GF_IPMPX_SelEncBuffer*)_p; + if (!stricmp(fieldName, "cipher_Id")) { GF_IPMPX_ParseBin128(val, &p->cipher_Id); ret = 1; } + else if (!stricmp(fieldName, "syncBoundary")) GET_U8(p->syncBoundary) + else if (!stricmp(fieldName, "mode")) GET_U8(p->mode) + else if (!stricmp(fieldName, "blockSize")) GET_U16(p->blockSize) + else if (!stricmp(fieldName, "keySize")) GET_U16(p->keySize) + } + break; + case GF_IPMPX_SEL_ENC_FIELD_TAG: + { + GF_IPMPX_SelEncField*p = (GF_IPMPX_SelEncField*)_p; + if (!stricmp(fieldName, "field_Id")) GET_U8(p->field_Id) + else if (!stricmp(fieldName, "field_Scope")) GET_U8(p->field_Scope) + else if (!stricmp(fieldName, "buf")) GET_U8(p->buf) + else if (!stricmp(fieldName, "mappingTable")) { ret=1; gf_ipmpx_data_parse_16(val, &p->mappingTable, &p->mappingTableSize); } + } + break; + case GF_IPMPX_ISMACRYP_TAG: + { + GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p; + if (!stricmp(fieldName, "crypto_suite")) GET_U8(p->cryptoSuite) + else if (!stricmp(fieldName, "IV_length")) GET_U8(p->IV_length) + else if (!stricmp(fieldName, "selective_encryption")) GET_BOOL(p->use_selective_encryption) + else if (!stricmp(fieldName, "key_indicator_length")) GET_U8(p->key_indicator_length) + } + break; + } + return ret ? GF_OK : GF_BAD_PARAM; +} + +GF_Err gf_ipmpx_set_sub_data(GF_IPMPX_Data *_p, char *fieldName, GF_IPMPX_Data *sp) +{ + switch (_p->tag) { + case GF_IPMPX_SECURE_CONTAINER_TAG: + { + GF_IPMPX_SecureContainer*p = (GF_IPMPX_SecureContainer*)_p; + if (p->protectedMsg) gf_ipmpx_data_del(p->protectedMsg); + p->protectedMsg = sp; + return GF_OK; + } + break; + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: + { + GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata*)_p; + if (!sp || (sp->tag!=GF_IPMPX_TRUSTED_TOOL_TAG) ) return GF_BAD_PARAM; + gf_list_add(p->TrustedTools, sp); + return GF_OK; + } + break; + case GF_IPMPX_TRUSTED_TOOL_TAG: + { + GF_IPMPX_TrustedTool *p = (GF_IPMPX_TrustedTool *)_p; + if (!sp || (sp->tag!=GF_IPMPX_TRUST_SPECIFICATION_TAG) ) return GF_BAD_PARAM; + gf_list_add(p->trustSpecifications, sp); + return GF_OK; + } + break; + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: + { + GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p; + if (!sp) return GF_BAD_PARAM; + switch (sp->tag) { + case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG: + sp->tag = GF_IPMPX_AUTH_AlgorithmDescr_Tag; + if (!stricmp(fieldName, "candidateAlgorithms")) return gf_list_add(p->agreedAlgorithms, sp); + else if (!stricmp(fieldName, "agreedAlgorithms")) return gf_list_add(p->agreedAlgorithms, sp); + else return GF_BAD_PARAM; + case GF_IPMPX_KEY_DESCRIPTOR_TAG: + sp->tag = GF_IPMPX_AUTH_KeyDescr_Tag; + if (!stricmp(fieldName, "candidateAlgorithms")) return gf_list_add(p->agreedAlgorithms, sp); + else if (!stricmp(fieldName, "agreedAlgorithms")) return gf_list_add(p->agreedAlgorithms, sp); + else if (!stricmp(fieldName, "publicKey")) { + if (p->publicKey) GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication *) p->publicKey); + p->publicKey = (GF_IPMPX_AUTH_KeyDescriptor *) sp; + return GF_OK; + } + else return GF_BAD_PARAM; + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: + if (p->trustData) gf_ipmpx_data_del((GF_IPMPX_Data *)p->trustData); + p->trustData = (GF_IPMPX_TrustSecurityMetadata*)sp; + return GF_OK; + default: + return GF_BAD_PARAM; + } + } + break; + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: + { + GF_IPMPX_ParametricDescription*p = (GF_IPMPX_ParametricDescription*)_p; + if (!sp || (sp->tag!=GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG) ) return GF_BAD_PARAM; + if (!stricmp(fieldName, "descriptions")) return gf_list_add(p->descriptions, sp); + else return GF_BAD_PARAM; + } + break; + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: + { + GF_IPMPX_ToolParamCapabilitiesQuery*p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p; + if (!sp || (sp->tag!=GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG) ) return GF_BAD_PARAM; + if (stricmp(fieldName, "description")) return GF_BAD_PARAM; + if (p->description) gf_ipmpx_data_del((GF_IPMPX_Data *)p->description); + p->description = (GF_IPMPX_ParametricDescription*)sp; + return GF_OK; + } + break; + case GF_IPMPX_SEL_DEC_INIT_TAG: + { + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + if (!sp) return GF_BAD_PARAM; + if (sp->tag==GF_IPMPX_SEL_ENC_BUFFER_TAG) return gf_list_add(p->SelEncBuffer, sp); + else if (sp->tag==GF_IPMPX_SEL_ENC_FIELD_TAG) return gf_list_add(p->SelEncFields, sp); + else return GF_BAD_PARAM; + } + break; + } + return GF_BAD_PARAM; +} + +GF_Err gf_ipmpx_set_byte_array(GF_IPMPX_Data *p, char *field, char *str) +{ + GF_IPMPX_ByteArray *d; + GF_IPMPX_ByteArray **dest; + d = (GF_IPMPX_ByteArray*)malloc(sizeof(GF_IPMPX_ByteArray)); + d->length = strlen(str); + d->data = (char*)malloc(sizeof(char)*d->length); + memcpy(d->data, str, d->length); + + dest = NULL; + switch (p->tag) { + case GF_IPMPX_KEY_DATA_TAG: + if (!stricmp(field, "keyBody")) dest = & ((GF_IPMPX_KeyData*)p)->keyBody; + else if (!stricmp(field, "opaqueData")) dest = & ((GF_IPMPX_KeyData*)p)->OpaqueData; + break; + case GF_IPMPX_RIGHTS_DATA_TAG: + if (!stricmp(field, "rightsInfo")) dest = & ((GF_IPMPX_RightsData*)p)->rightsInfo; + break; + case GF_IPMPX_OPAQUE_DATA_TAG: + if (!stricmp(field, "opaqueData")) dest = & ((GF_IPMPX_OpaqueData*)p)->opaqueData; + break; + case GF_IPMPX_SECURE_CONTAINER_TAG: + if (!stricmp(field, "encryptedData")) dest = & ((GF_IPMPX_SecureContainer*)p)->encryptedData; + else if (!stricmp(field, "MAC")) dest = & ((GF_IPMPX_SecureContainer*)p)->MAC; + break; + case GF_IPMPX_TRUST_SPECIFICATION_TAG: + if (!stricmp(field, "CCTrustMetadata")) dest = & ((GF_IPMPX_TrustSpecification*)p)->CCTrustMetadata; + break; + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: + if (!stricmp(field, "AuthenticationData")) dest = & ((GF_IPMPX_MutualAuthentication*)p)->AuthenticationData; + else if (!stricmp(field, "opaque")) dest = & ((GF_IPMPX_MutualAuthentication*)p)->opaque; + else if (!stricmp(field, "authCodes")) dest = & ((GF_IPMPX_MutualAuthentication*)p)->authCodes; + else if (!stricmp(field, "certificates")) { + gf_list_add(((GF_IPMPX_MutualAuthentication*)p)->certificates, d); + return GF_OK; + } + break; + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: + if (!stricmp(field, "descriptionComment")) dest = & ((GF_IPMPX_ParametricDescription*)p)->descriptionComment; + break; + case GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG: + if (!stricmp(field, "class")) dest = & ((GF_IPMPX_ParametricDescriptionItem*)p)->main_class; + else if (!stricmp(field, "subClass")) dest = & ((GF_IPMPX_ParametricDescriptionItem*)p)->subClass; + else if (!stricmp(field, "typeData")) dest = & ((GF_IPMPX_ParametricDescriptionItem*)p)->typeData; + else if (!stricmp(field, "type")) dest = & ((GF_IPMPX_ParametricDescriptionItem*)p)->type; + else if (!stricmp(field, "addedData")) dest = & ((GF_IPMPX_ParametricDescriptionItem*)p)->addedData; + break; + case GF_IPMPX_TOOL_API_CONFIG_TAG: + if (!stricmp(field, "opaqueData")) dest = & ((GF_IPMPX_ToolAPI_Config*)p)->opaqueData; + break; + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + if (!stricmp(field, "payload")) dest = & ((GF_IPMPX_SendWatermark *)p)->payload; + else if (!stricmp(field, "opaqueData")) dest = & ((GF_IPMPX_SendWatermark*)p)->opaqueData; + break; + case GF_IPMPX_SEL_ENC_BUFFER_TAG: + if (!stricmp(field, "StreamCipher")) dest = & ((GF_IPMPX_SelEncBuffer*)p)->Stream_Cipher_Specific_Init_Info; + break; + case GF_IPMPX_SEL_ENC_FIELD_TAG: + if (!stricmp(field, "shuffleSpecificInfo")) dest = & ((GF_IPMPX_SelEncField*)p)->shuffleSpecificInfo; + break; + } + if (!dest) { + free(d->data); + free(d); + return GF_BAD_PARAM; + } + if ( (*dest) ) { + if ((*dest)->data) free((*dest)->data); + free((*dest)); + } + (*dest) = d; + return GF_OK; +} diff --git a/src/odf/oci_codec.c b/src/odf/oci_codec.c new file mode 100644 index 0000000..2f0ad7a --- /dev/null +++ b/src/odf/oci_codec.c @@ -0,0 +1,422 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include + + +//from OD +GF_Err gf_odf_write_descriptor_list(GF_BitStream *bs, GF_List *descList); +GF_Err gf_odf_size_descriptor_list(GF_List *descList, u32 *outSize); +GF_Err gf_odf_delete_descriptor(GF_Descriptor *desc); +GF_Err gf_odf_parse_descriptor(GF_BitStream *bs, GF_Descriptor **desc, u32 *desc_size); +s32 gf_odf_size_field_size(u32 size_desc); + + +//max size of an OCI event +#define OCI_MAX_EVENT_SIZE 1<<28 - 1 +#define MAX_OCIEVENT_ID 0x7FFF + +struct __tag_oci_event +{ + u16 EventID; + u8 AbsoluteTimeFlag; + char StartingTime[4]; + char duration[4]; + GF_List *OCIDescriptors; +}; + +struct __tag_oci_codec +{ + //events + GF_List *OCIEvents; + //version, should always be one + u8 Version; + //encoder or decoder + u8 Mode; +}; + +GF_EXPORT +OCIEvent *gf_oci_event_new(u16 EventID) +{ + OCIEvent *tmp; + if (EventID > MAX_OCIEVENT_ID) return NULL; + tmp = (OCIEvent *)malloc(sizeof(OCIEvent)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(OCIEvent)); + tmp->EventID = EventID; + tmp->OCIDescriptors = gf_list_new(); + return tmp; +} + +GF_EXPORT +void gf_oci_event_del(OCIEvent *event) +{ + GF_Descriptor *desc; + if (!event) return; + + while (gf_list_count(event->OCIDescriptors)) { + desc = (GF_Descriptor *)gf_list_get(event->OCIDescriptors, 0); + gf_list_rem(event->OCIDescriptors, 0); + gf_odf_delete_descriptor(desc); + } + gf_list_del(event->OCIDescriptors); + free(event); +} + +GF_EXPORT +GF_Err gf_oci_event_set_start_time(OCIEvent *event, u8 Hours, u8 Minutes, u8 Seconds, u8 HundredSeconds, u8 IsAbsoluteTime) +{ + if (!event || (Hours >= 100) || (Minutes >= 100) || (Seconds >= 100) || (HundredSeconds >= 100) ) + return GF_BAD_PARAM; + + event->AbsoluteTimeFlag = IsAbsoluteTime; + event->StartingTime[0] = Hours; + event->StartingTime[1] = Minutes; + event->StartingTime[2] = Seconds; + event->StartingTime[3] = HundredSeconds; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_oci_event_set_duration(OCIEvent *event, u8 Hours, u8 Minutes, u8 Seconds, u8 HundredSeconds) +{ + if (!event || (Hours >= 100) || (Minutes >= 100) || (Seconds >= 100) || (HundredSeconds >= 100) ) + return GF_BAD_PARAM; + + event->duration[0] = Hours; + event->duration[1] = Minutes; + event->duration[2] = Seconds; + event->duration[3] = HundredSeconds; + return GF_OK; +} + +u8 OCI_IsOCIDesc(GF_Descriptor *oci_desc) +{ + if (oci_desc->tag < GF_ODF_OCI_BEGIN_TAG) return 0; + if (oci_desc->tag > GF_ODF_OCI_END_TAG) return 0; + return 1; +} + +GF_EXPORT +GF_Err gf_oci_event_add_desc(OCIEvent *event, GF_Descriptor *oci_desc) +{ + if (!event || !oci_desc) return GF_BAD_PARAM; + if (!OCI_IsOCIDesc(oci_desc)) return GF_ODF_INVALID_DESCRIPTOR; + + gf_list_add(event->OCIDescriptors, oci_desc); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_oci_event_get_id(OCIEvent *event, u16 *ID) +{ + if (!event || !ID) return GF_BAD_PARAM; + *ID = event->EventID; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_oci_event_get_start_time(OCIEvent *event, u8 *Hours, u8 *Minutes, u8 *Seconds, u8 *HundredSeconds, u8 *IsAbsoluteTime) +{ + if (!event || !Hours || !Minutes || !Seconds || !HundredSeconds || !IsAbsoluteTime) + return GF_BAD_PARAM; + + *IsAbsoluteTime = event->AbsoluteTimeFlag; + *Hours = event->StartingTime[0]; + *Minutes = event->StartingTime[1]; + *Seconds = event->StartingTime[2]; + *HundredSeconds = event->StartingTime[3]; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_oci_event_get_duration(OCIEvent *event, u8 *Hours, u8 *Minutes, u8 *Seconds, u8 *HundredSeconds) +{ + if (!event || !Hours || !Minutes || !Seconds || !HundredSeconds) + return GF_BAD_PARAM; + + *Hours = event->duration[0]; + *Minutes = event->duration[1]; + *Seconds = event->duration[2]; + *HundredSeconds = event->duration[3]; + return GF_OK; +} + +GF_EXPORT +u32 gf_oci_event_get_desc_count(OCIEvent *event) +{ + if (!event) return 0; + return gf_list_count(event->OCIDescriptors); +} + +GF_EXPORT +GF_Descriptor *gf_oci_event_get_desc(OCIEvent *event, u32 DescIndex) +{ + if (!event || DescIndex >= gf_list_count(event->OCIDescriptors) ) return NULL; + return (GF_Descriptor *) gf_list_get(event->OCIDescriptors, DescIndex); +} + +GF_EXPORT +GF_Err gf_oci_event_rem_desc(OCIEvent *event, u32 DescIndex) +{ + if (!event || DescIndex >= gf_list_count(event->OCIDescriptors) ) return GF_BAD_PARAM; + return gf_list_rem(event->OCIDescriptors, DescIndex); +} + + +//construction / destruction +GF_EXPORT +OCICodec *gf_oci_codec_new(u8 IsEncoder, u8 Version) +{ + OCICodec *tmp; + if (Version != 0x01) return NULL; + tmp = (OCICodec *)malloc(sizeof(OCICodec)); + if (!tmp) return NULL; + tmp->Mode = IsEncoder ? 1 : 0; + tmp->Version = 0x01; + tmp->OCIEvents = gf_list_new(); + return tmp; +} + +GF_EXPORT +void gf_oci_codec_del(OCICodec *codec) +{ + OCIEvent *ev; + if (!codec) return; + + while (gf_list_count(codec->OCIEvents)) { + ev = (OCIEvent *)gf_list_get(codec->OCIEvents, 0); + gf_oci_event_del(ev); + gf_list_rem(codec->OCIEvents, 0); + } + gf_list_del(codec->OCIEvents); + free(codec); +} + +GF_EXPORT +GF_Err gf_oci_codec_add_event(OCICodec *codec, OCIEvent *event) +{ + if (!codec || !codec->Mode || !event) return GF_BAD_PARAM; + + return gf_list_add(codec->OCIEvents, event); +} + +GF_Err WriteSevenBitLength(GF_BitStream *bs, u32 size) +{ + u32 length; + unsigned char vals[4]; + + if (!bs || !size) return GF_BAD_PARAM; + + length = size; + vals[3] = (unsigned char) (length & 0x7f); + length >>= 7; + vals[2] = (unsigned char) ((length & 0x7f) | 0x80); + length >>= 7; + vals[1] = (unsigned char) ((length & 0x7f) | 0x80); + length >>= 7; + vals[0] = (unsigned char) ((length & 0x7f) | 0x80); + + if (size < 0x00000080) { + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x00004000) { + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x00200000) { + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x10000000) { + gf_bs_write_int(bs, vals[0], 8); + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else { + return GF_ODF_INVALID_DESCRIPTOR; + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_oci_codec_encode(OCICodec *codec, char **outAU, u32 *au_length) +{ + GF_BitStream *bs; + u32 i, size, desc_size; + GF_Err e; + OCIEvent *ev; + + if (!codec || !codec->Mode || *outAU) return GF_BAD_PARAM; + + bs = NULL; + size = 0; + + //get the size of each event + i=0; + while ((ev = (OCIEvent *)gf_list_enum(codec->OCIEvents, &i))) { + //fixed size header + size += 10; + e = gf_odf_size_descriptor_list(codec->OCIEvents, &desc_size); + if (e) goto err_exit; + size += desc_size; + } + + //encode + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + e = WriteSevenBitLength(bs, size); + if (e) goto err_exit; + + //get one event, write it and delete it + while (gf_list_count(codec->OCIEvents)) { + ev = (OCIEvent *)gf_list_get(codec->OCIEvents, 0); + gf_list_rem(codec->OCIEvents, 0); + + gf_bs_write_int(bs, ev->EventID, 15); + gf_bs_write_int(bs, ev->AbsoluteTimeFlag, 1); + gf_bs_write_data(bs, ev->StartingTime, 4); + gf_bs_write_data(bs, ev->duration, 4); + + e = gf_odf_write_descriptor_list(bs, ev->OCIDescriptors); + gf_oci_event_del(ev); + if (e) goto err_exit; + //OCI Event is aligned + gf_bs_align(bs); + } + gf_bs_get_content(bs, outAU, au_length); + gf_bs_del(bs); + return GF_OK; + + +err_exit: + if (bs) gf_bs_del(bs); + //delete everything + while (gf_list_count(codec->OCIEvents)) { + ev = (OCIEvent *)gf_list_get(codec->OCIEvents, 0); + gf_list_rem(codec->OCIEvents, 0); + gf_oci_event_del(ev); + } + return e; +} + + +GF_EXPORT +GF_Err gf_oci_codec_decode(OCICodec *codec, char *au, u32 au_length) +{ + OCIEvent *ev; + GF_BitStream *bs; + u32 size, hdrS, desc_size, tot_size, tmp_size, val; + GF_Descriptor *tmp; + GF_Err e; + + //must be decoder + if (!codec || codec->Mode || !au) return GF_BAD_PARAM; + + bs = gf_bs_new(au, au_length, GF_BITSTREAM_READ); + ev = 0; + tot_size = 0; + while (tot_size < au_length) { + //create an event + ev = gf_oci_event_new(0); + if (!ev) { + e = GF_OUT_OF_MEM; + goto err_exit; + } + + + //FIX IM1 + gf_bs_read_int(bs, 8); + size = 0; + //get its size + hdrS = 0; + do { + val = gf_bs_read_int(bs, 8); + hdrS += 1; + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + + //parse event vars + ev->EventID = gf_bs_read_int(bs, 15); + ev->AbsoluteTimeFlag = gf_bs_read_int(bs, 1); + gf_bs_read_data(bs, ev->StartingTime, 4); + gf_bs_read_data(bs, ev->duration, 4); + desc_size = 0; + + //parse descriptor list + while (desc_size < size - 10) { + e = gf_odf_parse_descriptor(bs, &tmp, &tmp_size); + //RE-FIX IM1 + if (e || !tmp) goto err_exit; + if (!OCI_IsOCIDesc(tmp)) { + gf_odf_delete_descriptor(tmp); + e = GF_ODF_INVALID_DESCRIPTOR; + goto err_exit; + } + gf_list_add(ev->OCIDescriptors, tmp); + desc_size += tmp_size + gf_odf_size_field_size(tmp_size); + } + + if (desc_size != size - 10) { + e = GF_CORRUPTED_DATA; + goto err_exit; + } + + gf_list_add(codec->OCIEvents, ev); + //FIX IM1 + size += 1; + tot_size += size + hdrS; + ev = NULL; + } + + if (tot_size != au_length) { + e = GF_CORRUPTED_DATA; + goto err_exit; + } + + gf_bs_del(bs); + return GF_OK; + +err_exit: + gf_bs_del(bs); + if (ev) gf_oci_event_del(ev); + //delete everything + while (gf_list_count(codec->OCIEvents)) { + ev = (OCIEvent *)gf_list_get(codec->OCIEvents, 0); + gf_list_rem(codec->OCIEvents, 0); + gf_oci_event_del(ev); + } + return e; +} + + +GF_EXPORT +OCIEvent *gf_oci_codec_get_event(OCICodec *codec) +{ + OCIEvent *ev; + if (!codec ||codec->Mode) return NULL; + ev = (OCIEvent *)gf_list_get(codec->OCIEvents, 0); + gf_list_rem(codec->OCIEvents, 0); + return ev; +} diff --git a/src/odf/odf_code.c b/src/odf/odf_code.c new file mode 100644 index 0000000..1ad1782 --- /dev/null +++ b/src/odf/odf_code.c @@ -0,0 +1,3323 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#define DATE_CODING_BIT_LEN 40 + + +static GFINLINE GF_Err OD_ReadUTF8String(GF_BitStream *bs, char **string, Bool isUTF8, u32 *read) +{ + u32 len; + *read = 1; + len = gf_bs_read_int(bs, 8) + 1; + if (!isUTF8) len *= 2; + (*string) = (char *) malloc(sizeof(char)*len); + if (! (*string) ) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, (*string), len); + *read += len; + return GF_OK; +} + +static GFINLINE u32 OD_SizeUTF8String(char *string, Bool isUTF8) +{ + if (isUTF8) return 1 + strlen(string); + return 1 + 2*gf_utf8_wcslen((const unsigned short *)string); +} + +static GFINLINE void OD_WriteUTF8String(GF_BitStream *bs, char *string, Bool isUTF8) +{ + u32 len; + if (isUTF8) { + len = strlen(string); + gf_bs_write_int(bs, len, 8); + gf_bs_write_data(bs, string, len); + } else { + len = gf_utf8_wcslen((const unsigned short *)string); + gf_bs_write_int(bs, len, 8); + gf_bs_write_data(bs, string, len*2); + } +} + +/*use to parse strings read the length as well - Warning : the alloc is done here !!*/ +GF_Err gf_odf_read_url_string(GF_BitStream *bs, char **string, u32 *readBytes) +{ + u32 length; + *readBytes = 0; + + /*if the string is not NULL, return an error...*/ + if (*string != NULL) return GF_BAD_PARAM; + + /*the len is always on 8 bits*/ + length = gf_bs_read_int(bs, 8); + *readBytes = 1; + /*JLF AMD to MPEG-4 systems :) - This is not conformant at all, just hoping MPEG will accept it soon + since 255bytes URL is a real pain in the neck*/ + if (!length) { + length = gf_bs_read_int(bs, 32); + *readBytes += 4; + } + + /*we want to use strlen to get rid of "stringLength" => we need an extra 0*/ + (*string) = (char *) malloc(length + 1); + if (! string) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, (*string), length); + *readBytes += length; + (*string)[length] = 0; + return GF_OK; +} + +/*writes string*/ +GF_Err gf_odf_write_url_string(GF_BitStream *bs, char *string) +{ + u32 len; + /*we accept NULL strings now*/ + if (!string) { + gf_bs_write_int(bs, 0, 8); + return GF_OK; + } + len = strlen(string); + if (len > 255) { + gf_bs_write_int(bs, 0, 8); + gf_bs_write_int(bs, len, 32); + } else { + gf_bs_write_int(bs, len, 8); + } + gf_bs_write_data(bs, string, len); + return GF_OK; +} + +u32 gf_odf_size_url_string(char *string) +{ + u32 len = strlen(string); + if (len>255) return len+5; + return len+1; +} + +GF_Descriptor *gf_odf_new_esd() +{ + GF_ESD *newDesc = (GF_ESD *) malloc(sizeof(GF_ESD)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_ESD)); + newDesc->IPIDataSet = gf_list_new(); + newDesc->IPMPDescriptorPointers = gf_list_new(); + newDesc->extensionDescriptors = gf_list_new(); + newDesc->tag = GF_ODF_ESD_TAG; + return (GF_Descriptor *) newDesc; +} + + +GF_Err gf_odf_del_esd(GF_ESD *esd) +{ + GF_Err e; + if (!esd) return GF_BAD_PARAM; + if (esd->URLString) free(esd->URLString); + + if (esd->decoderConfig) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->decoderConfig); + if (e) return e; + } + if (esd->slConfig) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->slConfig); + if (e) return e; + } + if (esd->ipiPtr) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->ipiPtr); + if (e) return e; + } + if (esd->qos) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->qos); + if (e) return e; + } + if (esd->RegDescriptor) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->RegDescriptor); + if (e) return e; + } + if (esd->langDesc) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->langDesc); + if (e) return e; + } + + e = gf_odf_delete_descriptor_list(esd->IPIDataSet); + if (e) return e; + e = gf_odf_delete_descriptor_list(esd->IPMPDescriptorPointers); + if (e) return e; + e = gf_odf_delete_descriptor_list(esd->extensionDescriptors); + if (e) return e; + free(esd); + return GF_OK; +} + + +GF_Err AddDescriptorToESD(GF_ESD *esd, GF_Descriptor *desc) +{ + if (!esd || !desc) return GF_BAD_PARAM; + + switch (desc->tag) { + case GF_ODF_DCD_TAG: + if (esd->decoderConfig) return GF_ODF_INVALID_DESCRIPTOR; + esd->decoderConfig = (GF_DecoderConfig *) desc; + break; + + case GF_ODF_SLC_TAG: + if (esd->slConfig) return GF_ODF_INVALID_DESCRIPTOR; + esd->slConfig = (GF_SLConfig *) desc; + break; + + //the GF_ODF_ISOM_IPI_PTR_TAG is only used in the file format and replaces GF_ODF_IPI_PTR_TAG... + case GF_ODF_ISOM_IPI_PTR_TAG: + case GF_ODF_IPI_PTR_TAG: + if (esd->ipiPtr) return GF_ODF_INVALID_DESCRIPTOR; + esd->ipiPtr = (GF_IPIPtr *) desc; + break; + + case GF_ODF_QOS_TAG: + if (esd->qos) return GF_ODF_INVALID_DESCRIPTOR; + esd->qos =(GF_QoS_Descriptor *) desc; + break; + + case GF_ODF_LANG_TAG: + if (esd->langDesc) return GF_ODF_INVALID_DESCRIPTOR; + esd->langDesc = (GF_Language *) desc; + break; + + case GF_ODF_CI_TAG: + case GF_ODF_SCI_TAG: + return gf_list_add(esd->IPIDataSet, desc); + + //we use the same struct for v1 and v2 IPMP DPs + case GF_ODF_IPMP_PTR_TAG: + return gf_list_add(esd->IPMPDescriptorPointers, desc); + + case GF_ODF_REG_TAG: + if (esd->RegDescriptor) return GF_ODF_INVALID_DESCRIPTOR; + esd->RegDescriptor =(GF_Registration *) desc; + break; + + case GF_ODF_MUXINFO_TAG: + gf_list_add(esd->extensionDescriptors, desc); + break; + + default: + if ( (desc->tag >= GF_ODF_EXT_BEGIN_TAG) && + (desc->tag <= GF_ODF_EXT_END_TAG) ) { + return gf_list_add(esd->extensionDescriptors, desc); + } + gf_odf_delete_descriptor(desc); + return GF_OK; + } + + return GF_OK; +} + +GF_Err gf_odf_read_esd(GF_BitStream *bs, GF_ESD *esd, u32 DescSize) +{ + GF_Err e = GF_OK; + u32 ocrflag, urlflag, streamdependflag, tmp_size, nbBytes, read; + + if (! esd) return GF_BAD_PARAM; + + nbBytes = 0; + + esd->ESID = gf_bs_read_int(bs, 16); + streamdependflag = gf_bs_read_int(bs, 1); + urlflag = gf_bs_read_int(bs, 1); + ocrflag = gf_bs_read_int(bs, 1); + esd->streamPriority = gf_bs_read_int(bs, 5); + nbBytes += 3; + + if (streamdependflag) { + esd->dependsOnESID = gf_bs_read_int(bs, 16); + nbBytes += 2; + } + + if (urlflag) { + e = gf_odf_read_url_string(bs, & esd->URLString, &read); + if (e) return e; + nbBytes += read; + } + if (ocrflag) { + esd->OCRESID = gf_bs_read_int(bs, 16); + nbBytes += 2; + } + /*fix broken sync*/ +// if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmp_size); + /*fix for iPod files*/ + if (e==GF_ODF_INVALID_DESCRIPTOR) { + nbBytes += tmp_size; + if (nbBytes>DescSize) return e; + gf_bs_read_int(bs, DescSize-nbBytes); + return GF_OK; + } + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = AddDescriptorToESD(esd, tmp); + if (e) return e; + nbBytes += tmp_size + gf_odf_size_field_size(tmp_size); + + //apple fix + if (!tmp_size) nbBytes = DescSize; + + } + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return e; + +} + +GF_Err gf_odf_size_esd(GF_ESD *esd, u32 *outSize) +{ + GF_Err e; + u32 tmpSize; + if (! esd) return GF_BAD_PARAM; + + *outSize = 0; + *outSize += 3; + + /*this helps keeping proper sync: some people argue that OCR_ES_ID == ES_ID is a circular reference + of streams. Since this is equivalent to no OCR_ES_ID, keep it that way*/ +// if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; + + if (esd->dependsOnESID) *outSize += 2; + if (esd->URLString) *outSize += gf_odf_size_url_string(esd->URLString); + if (esd->OCRESID) *outSize += 2; + + if (esd->decoderConfig) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->decoderConfig, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (esd->slConfig) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->slConfig, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (esd->ipiPtr) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->ipiPtr, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (esd->langDesc) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->langDesc, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + + e = gf_odf_size_descriptor_list(esd->IPIDataSet, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(esd->IPMPDescriptorPointers, outSize); + if (e) return e; + if (esd->qos) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->qos, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (esd->RegDescriptor) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->RegDescriptor, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + return gf_odf_size_descriptor_list(esd->extensionDescriptors, outSize); +} + +GF_Err gf_odf_write_esd(GF_BitStream *bs, GF_ESD *esd) +{ + GF_Err e; + u32 size; + if (! esd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)esd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, esd->tag, size); + if (e) return e; + + gf_bs_write_int(bs, esd->ESID, 16); + gf_bs_write_int(bs, esd->dependsOnESID ? 1 : 0, 1); + gf_bs_write_int(bs, esd->URLString != NULL ? 1 : 0, 1); + gf_bs_write_int(bs, esd->OCRESID ? 1 : 0, 1); + gf_bs_write_int(bs, esd->streamPriority, 5); + + if (esd->dependsOnESID) { + gf_bs_write_int(bs, esd->dependsOnESID, 16); + } + if (esd->URLString) { + e = gf_odf_write_url_string(bs, esd->URLString); + if (e) return e; + } + + + if (esd->OCRESID) { + gf_bs_write_int(bs, esd->OCRESID, 16); + } + if (esd->decoderConfig) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->decoderConfig); + if (e) return e; + } + if (esd->slConfig) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->slConfig); + if (e) return e; + } + if (esd->ipiPtr) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->ipiPtr); + if (e) return e; + } + if (esd->langDesc) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->langDesc); + if (e) return e; + } + + e = gf_odf_write_descriptor_list(bs, esd->IPIDataSet); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, esd->IPMPDescriptorPointers); + if (e) return e; + if (esd->qos) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->qos); + if (e) return e; + } + if (esd->RegDescriptor) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->RegDescriptor); + if (e) return e; + } + return gf_odf_write_descriptor_list(bs, esd->extensionDescriptors); +} + +GF_Descriptor *gf_odf_new_iod() +{ + GF_InitialObjectDescriptor *newDesc = (GF_InitialObjectDescriptor *) malloc(sizeof(GF_InitialObjectDescriptor)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_InitialObjectDescriptor)); + + newDesc->ESDescriptors = gf_list_new(); + newDesc->OCIDescriptors = gf_list_new(); + newDesc->IPMP_Descriptors = gf_list_new(); + + newDesc->extensionDescriptors = gf_list_new(); + newDesc->tag = GF_ODF_IOD_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_iod(GF_InitialObjectDescriptor *iod) +{ + GF_Err e; + if (!iod) return GF_BAD_PARAM; + if (iod->URLString) free(iod->URLString); + e = gf_odf_delete_descriptor_list(iod->ESDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->OCIDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->IPMP_Descriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->extensionDescriptors); + if (e) return e; + if (iod->IPMPToolList) gf_odf_delete_descriptor((GF_Descriptor *) iod->IPMPToolList); + free(iod); + return GF_OK; +} + +GF_Err AddDescriptorToIOD(GF_InitialObjectDescriptor *iod, GF_Descriptor *desc) +{ + if (!iod || !desc) return GF_BAD_PARAM; + + switch (desc->tag) { + case GF_ODF_ESD_TAG: + return gf_list_add(iod->ESDescriptors, desc); + + //we use the same struct for v1 and v2 IPMP DPs + case GF_ODF_IPMP_PTR_TAG: + /*IPMPX*/ + case GF_ODF_IPMP_TAG: + return gf_list_add(iod->IPMP_Descriptors, desc); + + /*IPMPX*/ + case GF_ODF_IPMP_TL_TAG: + if (iod->IPMPToolList) gf_odf_desc_del((GF_Descriptor *)iod->IPMPToolList); + iod->IPMPToolList = (GF_IPMP_ToolList *)desc; + return GF_OK; + + default: + break; + } + if ( (desc->tag >= GF_ODF_OCI_BEGIN_TAG) && (desc->tag <= GF_ODF_OCI_END_TAG) ) return gf_list_add(iod->OCIDescriptors, desc); + if ( (desc->tag >= GF_ODF_EXT_BEGIN_TAG) && (desc->tag <= GF_ODF_EXT_END_TAG) ) return gf_list_add(iod->extensionDescriptors, desc); + return GF_BAD_PARAM; +} + +GF_Err gf_odf_read_iod(GF_BitStream *bs, GF_InitialObjectDescriptor *iod, u32 DescSize) +{ + GF_Err e; + u32 reserved, urlflag, read; + u32 tmp_size, nbBytes = 0; + if (! iod) return GF_BAD_PARAM; + + iod->objectDescriptorID = gf_bs_read_int(bs, 10); + urlflag = gf_bs_read_int(bs, 1); + iod->inlineProfileFlag = gf_bs_read_int(bs, 1); + reserved = gf_bs_read_int(bs, 4); + nbBytes += 2; + + if (urlflag) { + e = gf_odf_read_url_string(bs, & iod->URLString, &read); + if (e) return e; + nbBytes += read; + } else { + iod->OD_profileAndLevel = gf_bs_read_int(bs, 8); + iod->scene_profileAndLevel = gf_bs_read_int(bs, 8); + iod->audio_profileAndLevel = gf_bs_read_int(bs, 8); + iod->visual_profileAndLevel = gf_bs_read_int(bs, 8); + iod->graphics_profileAndLevel = gf_bs_read_int(bs, 8); + nbBytes += 5; + } + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmp_size); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = AddDescriptorToIOD(iod, tmp); + if (e) return e; + nbBytes += tmp_size + gf_odf_size_field_size(tmp_size); + } + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_iod(GF_InitialObjectDescriptor *iod, u32 *outSize) +{ + GF_Err e; + if (! iod) return GF_BAD_PARAM; + + *outSize = 0; + *outSize += 2; + if (iod->URLString) { + *outSize += gf_odf_size_url_string(iod->URLString); + } else { + *outSize += 5; + e = gf_odf_size_descriptor_list(iod->ESDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(iod->OCIDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(iod->IPMP_Descriptors, outSize); + if (e) return e; + + } + e = gf_odf_size_descriptor_list(iod->extensionDescriptors, outSize); + if (e) return e; + if (iod->IPMPToolList) { + u32 tmpSize; + e = gf_odf_size_descriptor((GF_Descriptor *) iod->IPMPToolList, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + return GF_OK; +} + +GF_Err gf_odf_write_iod(GF_BitStream *bs, GF_InitialObjectDescriptor *iod) +{ + GF_Err e; + u32 size; + if (! iod) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)iod, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, iod->tag, size); + if (e) return e; + + gf_bs_write_int(bs, iod->objectDescriptorID, 10); + gf_bs_write_int(bs, iod->URLString != NULL ? 1 : 0, 1); + gf_bs_write_int(bs, iod->inlineProfileFlag, 1); + gf_bs_write_int(bs, 15, 4); //reserved: 0b1111 == 15 + + if (iod->URLString) { + gf_odf_write_url_string(bs, iod->URLString); + } else { + gf_bs_write_int(bs, iod->OD_profileAndLevel, 8); + gf_bs_write_int(bs, iod->scene_profileAndLevel, 8); + gf_bs_write_int(bs, iod->audio_profileAndLevel, 8); + gf_bs_write_int(bs, iod->visual_profileAndLevel, 8); + gf_bs_write_int(bs, iod->graphics_profileAndLevel, 8); + e = gf_odf_write_descriptor_list(bs, iod->ESDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, iod->OCIDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, iod->IPMP_Descriptors, GF_ODF_IPMP_PTR_TAG); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, iod->IPMP_Descriptors, GF_ODF_IPMP_TAG); + if (e) return e; + if (iod->IPMPToolList) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) iod->IPMPToolList); + if (e) return e; + } + } + e = gf_odf_write_descriptor_list(bs, iod->extensionDescriptors); + return GF_OK; +} + + + +GF_Descriptor *gf_odf_new_od() +{ + GF_ObjectDescriptor *newDesc = (GF_ObjectDescriptor *) malloc(sizeof(GF_ObjectDescriptor)); + if (!newDesc) return NULL; + + newDesc->URLString = NULL; + newDesc->ESDescriptors = gf_list_new(); + newDesc->OCIDescriptors = gf_list_new(); + newDesc->IPMP_Descriptors = gf_list_new(); + newDesc->extensionDescriptors = gf_list_new(); + newDesc->objectDescriptorID = 0; + newDesc->tag = GF_ODF_OD_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_od(GF_ObjectDescriptor *od) +{ + GF_Err e; + if (!od) return GF_BAD_PARAM; + if (od->URLString) free(od->URLString); + e = gf_odf_delete_descriptor_list(od->ESDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->OCIDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->IPMP_Descriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->extensionDescriptors); + if (e) return e; + free(od); + return GF_OK; +} + +GF_Err AddDescriptorToOD(GF_ObjectDescriptor *od, GF_Descriptor *desc) +{ + if (!od || !desc) return GF_BAD_PARAM; + + //check if we can handle ContentClassif tags + if ( (desc->tag >= GF_ODF_OCI_BEGIN_TAG) && + (desc->tag <= GF_ODF_OCI_END_TAG) ) { + return gf_list_add(od->OCIDescriptors, desc); + } + + //or extensions + if ( (desc->tag >= GF_ODF_EXT_BEGIN_TAG) && + (desc->tag <= GF_ODF_EXT_END_TAG) ) { + return gf_list_add(od->extensionDescriptors, desc); + } + + //to cope with envivio + switch (desc->tag) { + case GF_ODF_ESD_TAG: + case GF_ODF_ESD_REF_TAG: + return gf_list_add(od->ESDescriptors, desc); + + //we use the same struct for v1 and v2 IPMP DPs + case GF_ODF_IPMP_PTR_TAG: + case GF_ODF_IPMP_TAG: + return gf_list_add(od->IPMP_Descriptors, desc); + + default: + return GF_BAD_PARAM; + } +} + +GF_Err gf_odf_read_od(GF_BitStream *bs, GF_ObjectDescriptor *od, u32 DescSize) +{ + GF_Err e; + u32 reserved, urlflag; + u32 tmpSize, nbBytes = 0; + if (! od) return GF_BAD_PARAM; + + od->objectDescriptorID = gf_bs_read_int(bs, 10); + urlflag = gf_bs_read_int(bs, 1); + reserved = gf_bs_read_int(bs, 5); + nbBytes += 2; + + if (urlflag) { + u32 read; + e = gf_odf_read_url_string(bs, & od->URLString, &read); + if (e) return e; + nbBytes += read; + } + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = AddDescriptorToOD(od, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_od(GF_ObjectDescriptor *od, u32 *outSize) +{ + GF_Err e; + if (! od) return GF_BAD_PARAM; + + *outSize = 2; + if (od->URLString) { + *outSize += gf_odf_size_url_string(od->URLString); + } else { + e = gf_odf_size_descriptor_list(od->ESDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(od->OCIDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(od->IPMP_Descriptors, outSize); + if (e) return e; + } + return gf_odf_size_descriptor_list(od->extensionDescriptors, outSize); +} + +GF_Err gf_odf_write_od(GF_BitStream *bs, GF_ObjectDescriptor *od) +{ + GF_Err e; + u32 size; + if (! od) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)od, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, od->tag, size); + if (e) return e; + + gf_bs_write_int(bs, od->objectDescriptorID, 10); + gf_bs_write_int(bs, od->URLString != NULL ? 1 : 0, 1); + gf_bs_write_int(bs, 31, 5); //reserved: 0b1111.1 == 31 + + if (od->URLString) { + gf_odf_write_url_string(bs, od->URLString); + } else { + e = gf_odf_write_descriptor_list(bs, od->ESDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, od->OCIDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, od->IPMP_Descriptors, GF_ODF_IPMP_PTR_TAG); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, od->IPMP_Descriptors, GF_ODF_IPMP_TAG); + if (e) return e; + } + e = gf_odf_write_descriptor_list(bs, od->extensionDescriptors); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_isom_iod() +{ + GF_IsomInitialObjectDescriptor *newDesc = (GF_IsomInitialObjectDescriptor *) malloc(sizeof(GF_IsomInitialObjectDescriptor)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_IsomInitialObjectDescriptor)); + + newDesc->ES_ID_IncDescriptors = gf_list_new(); + newDesc->ES_ID_RefDescriptors = gf_list_new(); + newDesc->OCIDescriptors = gf_list_new(); + newDesc->IPMP_Descriptors = gf_list_new(); + newDesc->extensionDescriptors = gf_list_new(); + newDesc->tag = GF_ODF_ISOM_IOD_TAG; + + //by default create an IOD with no inline and no capabilities + newDesc->audio_profileAndLevel = 0xFF; + newDesc->graphics_profileAndLevel = 0xFF; + newDesc->scene_profileAndLevel = 0xFF; + newDesc->OD_profileAndLevel = 0xFF; + newDesc->visual_profileAndLevel = 0xFF; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_isom_iod(GF_IsomInitialObjectDescriptor *iod) +{ + GF_Err e; + if (!iod) return GF_BAD_PARAM; + if (iod->URLString) free(iod->URLString); + e = gf_odf_delete_descriptor_list(iod->ES_ID_IncDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->ES_ID_RefDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->OCIDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->IPMP_Descriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->extensionDescriptors); + if (e) return e; + if (iod->IPMPToolList) gf_odf_delete_descriptor((GF_Descriptor *) iod->IPMPToolList); + free(iod); + return GF_OK; +} + +GF_Err AddDescriptorToIsomIOD(GF_IsomInitialObjectDescriptor *iod, GF_Descriptor *desc) +{ + if (!iod || !desc) return GF_BAD_PARAM; + + switch (desc->tag) { + case GF_ODF_ESD_TAG: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + case GF_ODF_ESD_INC_TAG: + //there shouldn't be ref if inc + if (gf_list_count(iod->ES_ID_RefDescriptors)) return GF_ODF_FORBIDDEN_DESCRIPTOR; + return gf_list_add(iod->ES_ID_IncDescriptors, desc); + + case GF_ODF_ESD_REF_TAG: + //there shouldn't be inc if ref + if (gf_list_count(iod->ES_ID_IncDescriptors)) return GF_ODF_FORBIDDEN_DESCRIPTOR; + return gf_list_add(iod->ES_ID_RefDescriptors, desc); + + //we use the same struct for v1 and v2 IPMP DPs + case GF_ODF_IPMP_PTR_TAG: + case GF_ODF_IPMP_TAG: + return gf_list_add(iod->IPMP_Descriptors, desc); + + /*IPMPX*/ + case GF_ODF_IPMP_TL_TAG: + if (iod->IPMPToolList) gf_odf_desc_del((GF_Descriptor *)iod->IPMPToolList); + iod->IPMPToolList = (GF_IPMP_ToolList *)desc; + return GF_OK; + + default: + break; + } + //check if we can handle ContentClassif tags + if ( (desc->tag >= GF_ODF_OCI_BEGIN_TAG) && (desc->tag <= GF_ODF_OCI_END_TAG) ) return gf_list_add(iod->OCIDescriptors, desc); + //or extensions + if ( (desc->tag >= GF_ODF_EXT_BEGIN_TAG) && (desc->tag <= GF_ODF_EXT_END_TAG) ) return gf_list_add(iod->extensionDescriptors, desc); + return GF_BAD_PARAM; +} + +GF_Err gf_odf_read_isom_iod(GF_BitStream *bs, GF_IsomInitialObjectDescriptor *iod, u32 DescSize) +{ + u32 nbBytes = 0, tmpSize; + u32 reserved, urlflag; + GF_Err e; + if (! iod) return GF_BAD_PARAM; + + iod->objectDescriptorID = gf_bs_read_int(bs, 10); + urlflag = gf_bs_read_int(bs, 1); + iod->inlineProfileFlag = gf_bs_read_int(bs, 1); + reserved = gf_bs_read_int(bs, 4); + nbBytes += 2; + + if (urlflag) { + u32 read; + e = gf_odf_read_url_string(bs, & iod->URLString, &read); + if (e) return e; + nbBytes += read; + } else { + iod->OD_profileAndLevel = gf_bs_read_int(bs, 8); + iod->scene_profileAndLevel = gf_bs_read_int(bs, 8); + iod->audio_profileAndLevel = gf_bs_read_int(bs, 8); + iod->visual_profileAndLevel = gf_bs_read_int(bs, 8); + iod->graphics_profileAndLevel = gf_bs_read_int(bs, 8); + nbBytes += 5; + } + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = AddDescriptorToIsomIOD(iod, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_isom_iod(GF_IsomInitialObjectDescriptor *iod, u32 *outSize) +{ + GF_Err e; + if (! iod) return GF_BAD_PARAM; + + *outSize = 2; + if (iod->URLString) { + *outSize += gf_odf_size_url_string(iod->URLString); + } else { + *outSize += 5; + e = gf_odf_size_descriptor_list(iod->ES_ID_IncDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(iod->ES_ID_RefDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(iod->OCIDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(iod->IPMP_Descriptors, outSize); + if (e) return e; + } + if (iod->IPMPToolList) { + u32 tmpSize; + e = gf_odf_size_descriptor((GF_Descriptor *) iod->IPMPToolList, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + return gf_odf_size_descriptor_list(iod->extensionDescriptors, outSize); +} + +GF_Err gf_odf_write_isom_iod(GF_BitStream *bs, GF_IsomInitialObjectDescriptor *iod) +{ + GF_Err e; + u32 size; + if (! iod) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)iod, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, iod->tag, size); + if (e) return e; + + gf_bs_write_int(bs, iod->objectDescriptorID, 10); + gf_bs_write_int(bs, iod->URLString != NULL ? 1 : 0, 1); + gf_bs_write_int(bs, iod->inlineProfileFlag, 1); + gf_bs_write_int(bs, 15, 4); //reserved: 0b1111 == 15 + + if (iod->URLString) { + gf_odf_write_url_string(bs, iod->URLString); + } else { + gf_bs_write_int(bs, iod->OD_profileAndLevel, 8); + gf_bs_write_int(bs, iod->scene_profileAndLevel, 8); + gf_bs_write_int(bs, iod->audio_profileAndLevel, 8); + gf_bs_write_int(bs, iod->visual_profileAndLevel, 8); + gf_bs_write_int(bs, iod->graphics_profileAndLevel, 8); + e = gf_odf_write_descriptor_list(bs, iod->ES_ID_IncDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, iod->ES_ID_RefDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, iod->OCIDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, iod->IPMP_Descriptors, GF_ODF_IPMP_PTR_TAG); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, iod->IPMP_Descriptors, GF_ODF_IPMP_TAG); + if (e) return e; + if (iod->IPMPToolList) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) iod->IPMPToolList); + if (e) return e; + } + } + e = gf_odf_write_descriptor_list(bs, iod->extensionDescriptors); + if (e) return e; + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_isom_od() +{ + GF_IsomObjectDescriptor *newDesc = (GF_IsomObjectDescriptor *) malloc(sizeof(GF_IsomObjectDescriptor)); + if (!newDesc) return NULL; + + newDesc->URLString = NULL; + newDesc->ES_ID_IncDescriptors = gf_list_new(); + newDesc->ES_ID_RefDescriptors = gf_list_new(); + newDesc->OCIDescriptors = gf_list_new(); + newDesc->IPMP_Descriptors = gf_list_new(); + newDesc->extensionDescriptors = gf_list_new(); + newDesc->objectDescriptorID = 0; + newDesc->tag = GF_ODF_ISOM_OD_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_isom_od(GF_IsomObjectDescriptor *od) +{ + GF_Err e; + if (!od) return GF_BAD_PARAM; + if (od->URLString) free(od->URLString); + e = gf_odf_delete_descriptor_list(od->ES_ID_IncDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->ES_ID_RefDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->OCIDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->IPMP_Descriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->extensionDescriptors); + if (e) return e; + free(od); + return GF_OK; +} + +GF_Err AddDescriptorToIsomOD(GF_IsomObjectDescriptor *od, GF_Descriptor *desc) +{ + if (!od || !desc) return GF_BAD_PARAM; + + //check if we can handle ContentClassif tags + if ( (desc->tag >= GF_ODF_OCI_BEGIN_TAG) && + (desc->tag <= GF_ODF_OCI_END_TAG) ) { + return gf_list_add(od->OCIDescriptors, desc); + } + + //or extension ... + if ( (desc->tag >= GF_ODF_EXT_BEGIN_TAG) && + (desc->tag <= GF_ODF_EXT_END_TAG) ) { + return gf_list_add(od->extensionDescriptors, desc); + } + + switch (desc->tag) { + case GF_ODF_ESD_TAG: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + case GF_ODF_ESD_INC_TAG: + //there shouldn't be ref if inc + if (gf_list_count(od->ES_ID_RefDescriptors)) return GF_ODF_FORBIDDEN_DESCRIPTOR; + return gf_list_add(od->ES_ID_IncDescriptors, desc); + + case GF_ODF_ESD_REF_TAG: + //there shouldn't be inc if ref + if (gf_list_count(od->ES_ID_IncDescriptors)) return GF_ODF_FORBIDDEN_DESCRIPTOR; + return gf_list_add(od->ES_ID_RefDescriptors, desc); + + //we use the same struct for v1 and v2 IPMP DPs + case GF_ODF_IPMP_PTR_TAG: + case GF_ODF_IPMP_TAG: + return gf_list_add(od->IPMP_Descriptors, desc); + + default: + return GF_BAD_PARAM; + } +} + +GF_Err gf_odf_read_isom_od(GF_BitStream *bs, GF_IsomObjectDescriptor *od, u32 DescSize) +{ + GF_Err e; + u32 reserved, urlflag; + u32 tmpSize, nbBytes = 0; + if (! od) return GF_BAD_PARAM; + + od->objectDescriptorID = gf_bs_read_int(bs, 10); + urlflag = gf_bs_read_int(bs, 1); + reserved = gf_bs_read_int(bs, 5); + nbBytes += 2; + + if (urlflag) { + u32 read; + e = gf_odf_read_url_string(bs, & od->URLString, &read); + if (e) return e; + nbBytes += read; + } + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = AddDescriptorToIsomOD(od, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_isom_od(GF_IsomObjectDescriptor *od, u32 *outSize) +{ + GF_Err e; + if (! od) return GF_BAD_PARAM; + + *outSize = 2; + if (od->URLString) { + *outSize += gf_odf_size_url_string(od->URLString); + } else { + e = gf_odf_size_descriptor_list(od->ES_ID_IncDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(od->ES_ID_RefDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(od->OCIDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(od->IPMP_Descriptors, outSize); + if (e) return e; + } + return gf_odf_size_descriptor_list(od->extensionDescriptors, outSize); +} + +GF_Err gf_odf_write_isom_od(GF_BitStream *bs, GF_IsomObjectDescriptor *od) +{ + GF_Err e; + u32 size; + if (! od) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)od, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, od->tag, size); + if (e) return e; + + gf_bs_write_int(bs, od->objectDescriptorID, 10); + gf_bs_write_int(bs, od->URLString != NULL ? 1 : 0, 1); + gf_bs_write_int(bs, 31, 5); //reserved: 0b1111.1 == 31 + + if (od->URLString) { + gf_odf_write_url_string(bs, od->URLString); + } else { + e = gf_odf_write_descriptor_list(bs, od->ES_ID_IncDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, od->ES_ID_RefDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, od->OCIDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, od->IPMP_Descriptors, GF_ODF_IPMP_PTR_TAG); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, od->IPMP_Descriptors, GF_ODF_IPMP_TAG); + if (e) return e; + } + e = gf_odf_write_descriptor_list(bs, od->extensionDescriptors); + if (e) return e; + return GF_OK; +} + + + +GF_Descriptor *gf_odf_new_cc() +{ + GF_CCDescriptor *newDesc = (GF_CCDescriptor *) malloc(sizeof(GF_CCDescriptor)); + if (!newDesc) return NULL; + + newDesc->contentClassificationData = NULL; + newDesc->dataLength = 0; + newDesc->classificationEntity = 0; + newDesc->classificationTable = 0; + newDesc->tag = GF_ODF_CC_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_cc(GF_CCDescriptor *ccd) +{ + if (!ccd) return GF_BAD_PARAM; + if (ccd->contentClassificationData) free(ccd->contentClassificationData); + free(ccd); + return GF_OK; +} + +GF_Err gf_odf_read_cc(GF_BitStream *bs, GF_CCDescriptor *ccd, u32 DescSize) +{ + u32 nbBytes = 0; + if (!ccd) return GF_BAD_PARAM; + + ccd->classificationEntity = gf_bs_read_int(bs, 32); + ccd->classificationTable = gf_bs_read_int(bs, 16); + nbBytes += 6; + ccd->dataLength = DescSize - 6; + ccd->contentClassificationData = (char*)malloc(sizeof(char) * ccd->dataLength); + if (!ccd->contentClassificationData) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ccd->contentClassificationData, ccd->dataLength); + nbBytes += ccd->dataLength; + + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_cc(GF_CCDescriptor *ccd, u32 *outSize) +{ + if (!ccd) return GF_BAD_PARAM; + *outSize = 6 + ccd->dataLength; + return GF_OK; +} + +GF_Err gf_odf_write_cc(GF_BitStream *bs, GF_CCDescriptor *ccd) +{ + u32 size; + GF_Err e; + if (!ccd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ccd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ccd->tag, size); + if (e) return e; + gf_bs_write_int(bs, ccd->classificationEntity, 32); + gf_bs_write_int(bs, ccd->classificationTable, 16); + gf_bs_write_data(bs, ccd->contentClassificationData, ccd->dataLength); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_cc_date() +{ + GF_CC_Date *newDesc = (GF_CC_Date *) malloc(sizeof(GF_CC_Date)); + if (!newDesc) return NULL; + memset(newDesc->contentCreationDate, 0, 5); + newDesc->tag = GF_ODF_CC_DATE_TAG; + return (GF_Descriptor *) newDesc; +} + + +GF_Err gf_odf_del_cc_date(GF_CC_Date *cdd) +{ + if (!cdd) return GF_BAD_PARAM; + free(cdd); + return GF_OK; +} + +GF_Err gf_odf_read_cc_date(GF_BitStream *bs, GF_CC_Date *cdd, u32 DescSize) +{ + u32 nbBytes = 0; + if (!cdd) return GF_BAD_PARAM; + + gf_bs_read_data(bs, cdd->contentCreationDate, DATE_CODING_BIT_LEN); + nbBytes += DATE_CODING_BIT_LEN / 8; + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_cc_date(GF_CC_Date *cdd, u32 *outSize) +{ + if (!cdd) return GF_BAD_PARAM; + *outSize = (DATE_CODING_BIT_LEN / 8); + return GF_OK; +} + +GF_Err gf_odf_write_cc_date(GF_BitStream *bs, GF_CC_Date *cdd) +{ + u32 size; + GF_Err e; + if (!cdd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)cdd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, cdd->tag, size); + if (e) return e; + + gf_bs_write_data(bs, cdd->contentCreationDate , DATE_CODING_BIT_LEN); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_cc_name() +{ + GF_CC_Name *newDesc = (GF_CC_Name *) malloc(sizeof(GF_CC_Name)); + if (!newDesc) return NULL; + + newDesc->ContentCreators = gf_list_new(); + if (! newDesc->ContentCreators) { + free(newDesc); + return NULL; + } + newDesc->tag = GF_ODF_CC_NAME_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_cc_name(GF_CC_Name *cnd) +{ + u32 i; + GF_ContentCreatorInfo *tmp; + if (!cnd) return GF_BAD_PARAM; + + i=0; + while ((tmp = (GF_ContentCreatorInfo *)gf_list_enum(cnd->ContentCreators, &i))) { + if (tmp->contentCreatorName) free(tmp->contentCreatorName); + free(tmp); + } + gf_list_del(cnd->ContentCreators); + free(cnd); + return GF_OK; +} + +GF_Err gf_odf_read_cc_name(GF_BitStream *bs, GF_CC_Name *cnd, u32 DescSize) +{ + GF_Err e; + u32 i, aligned, count, len, nbBytes = 0; + if (!cnd) return GF_BAD_PARAM; + + count = gf_bs_read_int(bs, 8); + nbBytes += 1; + for (i = 0; i< count; i++) { + GF_ContentCreatorInfo *tmp = (GF_ContentCreatorInfo*)malloc(sizeof(GF_ContentCreatorInfo)); + if (! tmp) return GF_OUT_OF_MEM; + memset(tmp , 0, sizeof(GF_ContentCreatorInfo)); + tmp->langCode = gf_bs_read_int(bs, 24); + tmp->isUTF8 = gf_bs_read_int(bs, 1); + aligned = gf_bs_read_int(bs, 7); + nbBytes += 4; + + e = OD_ReadUTF8String(bs, & tmp->contentCreatorName, tmp->isUTF8, &len); + if (e) return e; + nbBytes += len; + e = gf_list_add(cnd->ContentCreators, tmp); + } + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_cc_name(GF_CC_Name *cnd, u32 *outSize) +{ + u32 i; + GF_ContentCreatorInfo *tmp; + if (!cnd) return GF_BAD_PARAM; + + *outSize = 1; + i=0; + while ((tmp = (GF_ContentCreatorInfo *)gf_list_enum(cnd->ContentCreators, &i))) { + *outSize += 4 + OD_SizeUTF8String(tmp->contentCreatorName, tmp->isUTF8); + } + return GF_OK; +} + +GF_Err gf_odf_write_cc_name(GF_BitStream *bs, GF_CC_Name *cnd) +{ + GF_Err e; + GF_ContentCreatorInfo *tmp; + u32 i, size; + if (!cnd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)cnd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, cnd->tag, size); + if (e) return e; + gf_bs_write_int(bs, gf_list_count(cnd->ContentCreators), 8); + + i=0; + while ((tmp = (GF_ContentCreatorInfo *)gf_list_enum(cnd->ContentCreators, &i))) { + gf_bs_write_int(bs, tmp->langCode, 24); + gf_bs_write_int(bs, tmp->isUTF8, 1); + gf_bs_write_int(bs, 0, 7); //aligned + OD_WriteUTF8String(bs, tmp->contentCreatorName, tmp->isUTF8); + } + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_ci() +{ + GF_CIDesc *newDesc = (GF_CIDesc *) malloc(sizeof(GF_CIDesc)); + if (!newDesc) return NULL; + + newDesc->compatibility = 0; + newDesc->contentIdentifier = NULL; + newDesc->tag = GF_ODF_CI_TAG; + newDesc->contentIdentifierFlag = 0; + newDesc->contentIdentifierType = 0; + newDesc->contentType = 0; + newDesc->contentTypeFlag = 0; + newDesc->protectedContent = 0; + return (GF_Descriptor *) newDesc; +} + + +GF_Err gf_odf_del_ci(GF_CIDesc *cid) +{ + if (!cid) return GF_BAD_PARAM; + + if (cid->contentIdentifier) free(cid->contentIdentifier); + free(cid); + return GF_OK; +} + + +GF_Err gf_odf_read_ci(GF_BitStream *bs, GF_CIDesc *cid, u32 DescSize) +{ + u32 reserved, nbBytes = 0; + if (! cid) return GF_BAD_PARAM; + + cid->compatibility = gf_bs_read_int(bs, 2); //MUST BE NULL + if (cid->compatibility) return GF_ODF_INVALID_DESCRIPTOR; + + cid->contentTypeFlag = gf_bs_read_int(bs, 1); + cid->contentIdentifierFlag = gf_bs_read_int(bs, 1); + cid->protectedContent = gf_bs_read_int(bs, 1); + reserved = gf_bs_read_int(bs, 3); + nbBytes += 1; + + if (cid->contentTypeFlag) { + cid->contentType = gf_bs_read_int(bs, 8); + nbBytes += 1; + } + if (cid->contentIdentifierFlag) { + cid->contentIdentifierType = gf_bs_read_int(bs, 8); + cid->contentIdentifier = (char*)malloc(DescSize - 2 - cid->contentTypeFlag); + if (! cid->contentIdentifier) return GF_OUT_OF_MEM; + + gf_bs_read_data(bs, cid->contentIdentifier, DescSize - 2 - cid->contentTypeFlag); + nbBytes += DescSize - 1 - cid->contentTypeFlag; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_ci(GF_CIDesc *cid, u32 *outSize) +{ + if (! cid) return GF_BAD_PARAM; + + *outSize = 1; + if (cid->contentTypeFlag) *outSize += 1; + + if (cid->contentIdentifierFlag) + *outSize += strlen((const char*)cid->contentIdentifier) - 1 - cid->contentTypeFlag; + return GF_OK; +} + +GF_Err gf_odf_write_ci(GF_BitStream *bs, GF_CIDesc *cid) +{ + GF_Err e; + u32 size; + if (! cid) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)cid, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, cid->tag, size); + if (e) return e; + + gf_bs_write_int(bs, cid->compatibility, 2); + gf_bs_write_int(bs, cid->contentTypeFlag, 1); + gf_bs_write_int(bs, cid->contentIdentifierFlag, 1); + gf_bs_write_int(bs, cid->protectedContent, 1); + gf_bs_write_int(bs, 7, 3); //reserved 0b111 = 7 + + if (cid->contentTypeFlag) { + gf_bs_write_int(bs, cid->contentType, 8); + } + + if (cid->contentIdentifierFlag) { + gf_bs_write_int(bs, cid->contentIdentifierType, 8); + gf_bs_write_data(bs, cid->contentIdentifier, size - 2 - cid->contentTypeFlag); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_dcd() +{ + GF_DecoderConfig *newDesc = (GF_DecoderConfig *) malloc(sizeof(GF_DecoderConfig)); + if (!newDesc) return NULL; + + newDesc->avgBitrate = 0; + newDesc->bufferSizeDB = 0; + newDesc->maxBitrate = 0; + newDesc->objectTypeIndication = 0; + newDesc->streamType = 0; + newDesc->upstream = 0; + newDesc->decoderSpecificInfo = NULL; + newDesc->profileLevelIndicationIndexDescriptor = gf_list_new(); + newDesc->tag = GF_ODF_DCD_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_dcd(GF_DecoderConfig *dcd) +{ + GF_Err e; + if (!dcd) return GF_BAD_PARAM; + + if (dcd->decoderSpecificInfo) { + e = gf_odf_delete_descriptor((GF_Descriptor *) dcd->decoderSpecificInfo); + if (e) return e; + } + e = gf_odf_delete_descriptor_list(dcd->profileLevelIndicationIndexDescriptor); + if (e) return e; + free(dcd); + return GF_OK; +} + +GF_Err gf_odf_read_dcd(GF_BitStream *bs, GF_DecoderConfig *dcd, u32 DescSize) +{ + GF_Err e; + u32 reserved, tmp_size, nbBytes = 0; + if (! dcd) return GF_BAD_PARAM; + + dcd->objectTypeIndication = gf_bs_read_int(bs, 8); + dcd->streamType = gf_bs_read_int(bs, 6); + dcd->upstream = gf_bs_read_int(bs, 1); + reserved = gf_bs_read_int(bs, 1); + dcd->bufferSizeDB = gf_bs_read_int(bs, 24); + dcd->maxBitrate = gf_bs_read_int(bs, 32); + dcd->avgBitrate = gf_bs_read_int(bs, 32); + nbBytes += 13; + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmp_size); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + switch (tmp->tag) { + case GF_ODF_DSI_TAG: + if (dcd->decoderSpecificInfo) { + gf_odf_delete_descriptor(tmp); + return GF_ODF_INVALID_DESCRIPTOR; + } + dcd->decoderSpecificInfo = (GF_DefaultDescriptor *) tmp; + break; + + case GF_ODF_EXT_PL_TAG: + e = gf_list_add(dcd->profileLevelIndicationIndexDescriptor, tmp); + if (e < GF_OK) { + gf_odf_delete_descriptor(tmp); + return e; + } + break; + + /*iPod fix: delete and aborts, this will create an InvalidDescriptor at the ESD level with a loaded DSI, + laoding will abort with a partially valid ESD which is all the matters*/ + case GF_ODF_SLC_TAG: + gf_odf_delete_descriptor(tmp); + return GF_OK; + + //what the hell is this descriptor ?? Don't know, so delete it ! + default: + gf_odf_delete_descriptor(tmp); + break; + } + nbBytes += tmp_size + gf_odf_size_field_size(tmp_size); + } + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_dcd(GF_DecoderConfig *dcd, u32 *outSize) +{ + GF_Err e; + u32 tmpSize; + if (! dcd) return GF_BAD_PARAM; + + *outSize = 0; + *outSize += 13; + if (dcd->decoderSpecificInfo) { + //warning: we don't know anything about the structure of a generic DecSpecInfo + //we check the tag and size of the descriptor, but we most ofthe time can't parse it + //the decSpecInfo is handle as a defaultDescriptor (opaque data, but same structure....) + e = gf_odf_size_descriptor((GF_Descriptor *) dcd->decoderSpecificInfo , &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + e = gf_odf_size_descriptor_list(dcd->profileLevelIndicationIndexDescriptor, outSize); + if (e) return e; + return GF_OK; + +} + +GF_Err gf_odf_write_dcd(GF_BitStream *bs, GF_DecoderConfig *dcd) +{ + GF_Err e; + u32 size; + if (! dcd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)dcd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, dcd->tag, size); + if (e) return e; + + gf_bs_write_int(bs, dcd->objectTypeIndication, 8); + gf_bs_write_int(bs, dcd->streamType, 6); + gf_bs_write_int(bs, dcd->upstream, 1); + gf_bs_write_int(bs, 1, 1); //reserved field... + gf_bs_write_int(bs, dcd->bufferSizeDB, 24); + gf_bs_write_int(bs, dcd->maxBitrate, 32); + gf_bs_write_int(bs, dcd->avgBitrate, 32); + + if (dcd->decoderSpecificInfo) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) dcd->decoderSpecificInfo); + if (e) return e; + } + e = gf_odf_write_descriptor_list(bs, dcd->profileLevelIndicationIndexDescriptor); + return e; +} + + +GF_Descriptor *gf_odf_new_default() +{ + GF_DefaultDescriptor *newDesc = (GF_DefaultDescriptor *) malloc(sizeof(GF_DefaultDescriptor)); + if (!newDesc) return NULL; + + newDesc->dataLength = 0; + newDesc->data = NULL; + //set it to the Max allowed + newDesc->tag = GF_ODF_USER_END_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_default(GF_DefaultDescriptor *dd) +{ + if (!dd) return GF_BAD_PARAM; + + if (dd->data) free(dd->data); + free(dd); + return GF_OK; +} + +GF_Err gf_odf_read_default(GF_BitStream *bs, GF_DefaultDescriptor *dd, u32 DescSize) +{ + u32 nbBytes = 0; + if (! dd) return GF_BAD_PARAM; + + dd->dataLength = DescSize; + dd->data = NULL; + if (DescSize) { + dd->data = (char*)malloc(dd->dataLength); + if (! dd->data) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, dd->data, dd->dataLength); + nbBytes += dd->dataLength; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_default(GF_DefaultDescriptor *dd, u32 *outSize) +{ + if (! dd) return GF_BAD_PARAM; + *outSize = dd->dataLength; + return GF_OK; +} + +GF_Err gf_odf_write_default(GF_BitStream *bs, GF_DefaultDescriptor *dd) +{ + GF_Err e; + u32 size; + if (! dd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)dd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, dd->tag, size); + if (e) return e; + + if (dd->data) { + gf_bs_write_data(bs, dd->data, dd->dataLength); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_esd_inc() +{ + GF_ES_ID_Inc *newDesc = (GF_ES_ID_Inc *) malloc(sizeof(GF_ES_ID_Inc)); + if (!newDesc) return NULL; + newDesc->tag = GF_ODF_ESD_INC_TAG; + newDesc->trackID = 0; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_esd_inc(GF_ES_ID_Inc *esd_inc) +{ + if (!esd_inc) return GF_BAD_PARAM; + free(esd_inc); + return GF_OK; +} +GF_Err gf_odf_read_esd_inc(GF_BitStream *bs, GF_ES_ID_Inc *esd_inc, u32 DescSize) +{ + u32 nbBytes = 0; + if (! esd_inc) return GF_BAD_PARAM; + + esd_inc->trackID = gf_bs_read_int(bs, 32); + nbBytes += 4; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} +GF_Err gf_odf_size_esd_inc(GF_ES_ID_Inc *esd_inc, u32 *outSize) +{ + if (! esd_inc) return GF_BAD_PARAM; + *outSize = 4; + return GF_OK; +} +GF_Err gf_odf_write_esd_inc(GF_BitStream *bs, GF_ES_ID_Inc *esd_inc) +{ + GF_Err e; + u32 size; + if (! esd_inc) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)esd_inc, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, esd_inc->tag, size); + if (e) return e; + gf_bs_write_int(bs, esd_inc->trackID, 32); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_esd_ref() +{ + GF_ES_ID_Ref *newDesc = (GF_ES_ID_Ref *) malloc(sizeof(GF_ES_ID_Ref)); + if (!newDesc) return NULL; + newDesc->tag = GF_ODF_ESD_REF_TAG; + newDesc->trackRef = 0; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_esd_ref(GF_ES_ID_Ref *esd_ref) +{ + if (!esd_ref) return GF_BAD_PARAM; + free(esd_ref); + return GF_OK; +} +GF_Err gf_odf_read_esd_ref(GF_BitStream *bs, GF_ES_ID_Ref *esd_ref, u32 DescSize) +{ + u32 nbBytes = 0; + if (! esd_ref) return GF_BAD_PARAM; + + esd_ref->trackRef = gf_bs_read_int(bs, 16); + nbBytes += 2; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_esd_ref(GF_ES_ID_Ref *esd_ref, u32 *outSize) +{ + if (! esd_ref) return GF_BAD_PARAM; + *outSize = 2; + return GF_OK; +} +GF_Err gf_odf_write_esd_ref(GF_BitStream *bs, GF_ES_ID_Ref *esd_ref) +{ + GF_Err e; + u32 size; + if (! esd_ref) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)esd_ref, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, esd_ref->tag, size); + if (e) return e; + + gf_bs_write_int(bs, esd_ref->trackRef, 16); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_exp_text() +{ + GF_ExpandedTextual *newDesc = (GF_ExpandedTextual *) malloc(sizeof(GF_ExpandedTextual)); + if (!newDesc) return NULL; + + newDesc->itemDescriptionList = gf_list_new(); + if (! newDesc->itemDescriptionList) { + free(newDesc); + return NULL; + } + newDesc->itemTextList = gf_list_new(); + if (! newDesc->itemTextList) { + free(newDesc->itemDescriptionList); + free(newDesc); + return NULL; + } + newDesc->isUTF8 = 0; + newDesc->langCode = 0; + newDesc->NonItemText = NULL; + newDesc->tag = GF_ODF_TEXT_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_exp_text(GF_ExpandedTextual *etd) +{ + if (!etd) return GF_BAD_PARAM; + + while (gf_list_count(etd->itemDescriptionList)) { + GF_ETD_ItemText *tmp = (GF_ETD_ItemText*)gf_list_get(etd->itemDescriptionList, 0); + if (tmp) { + if (tmp->text) free(tmp->text); + free(tmp); + } + gf_list_rem(etd->itemDescriptionList, 0); + } + gf_list_del(etd->itemDescriptionList); + + while (gf_list_count(etd->itemTextList)) { + GF_ETD_ItemText *tmp = (GF_ETD_ItemText*)gf_list_get(etd->itemTextList, 0); + if (tmp) { + if (tmp->text) free(tmp->text); + free(tmp); + } + gf_list_rem(etd->itemTextList, 0); + } + gf_list_del(etd->itemTextList); + + if (etd->NonItemText) free(etd->NonItemText); + free(etd); + return GF_OK; +} + +GF_Err gf_odf_read_exp_text(GF_BitStream *bs, GF_ExpandedTextual *etd, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0; + u32 i, aligned, len, nonLen, count; + if (!etd) return GF_BAD_PARAM; + + etd->langCode = gf_bs_read_int(bs, 24); + etd->isUTF8 = gf_bs_read_int(bs, 1); + aligned = gf_bs_read_int(bs, 7); + count = gf_bs_read_int(bs, 8); + nbBytes += 5; + + for (i = 0; i< count; i++) { + //description + GF_ETD_ItemText *description, *Text; + description = (GF_ETD_ItemText*)malloc(sizeof(GF_ETD_ItemText)); + if (! description) return GF_OUT_OF_MEM; + description->text = NULL; + e = OD_ReadUTF8String(bs, & description->text, etd->isUTF8, &len); + if (e) return e; + e = gf_list_add(etd->itemDescriptionList, description); + if (e) return e; + nbBytes += len; + + //text + Text = (GF_ETD_ItemText*)malloc(sizeof(GF_ETD_ItemText)); + if (! Text) return GF_OUT_OF_MEM; + Text->text = NULL; + e = OD_ReadUTF8String(bs, & Text->text, etd->isUTF8, &len); + if (e) return e; + e = gf_list_add(etd->itemTextList, Text); + if (e) return e; + nbBytes += len; + } + len = gf_bs_read_int(bs, 8); + nbBytes += 1; + nonLen = 0; + while (len == 255) { + nonLen += len; + len = gf_bs_read_int(bs, 8); + nbBytes += 1; + } + nonLen += len; + if (nonLen) { + //here we have no choice but do the job ourselves + //because the length is not encoded on 8 bits + etd->NonItemText = (char *) malloc(sizeof(char) * (1+nonLen) * (etd->isUTF8 ? 1 : 2)); + if (! etd->NonItemText) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, etd->NonItemText, nonLen * (etd->isUTF8 ? 1 : 2)); + nbBytes += nonLen * (etd->isUTF8 ? 1 : 2); + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +GF_Err gf_odf_size_exp_text(GF_ExpandedTextual *etd, u32 *outSize) +{ + u32 i, len, nonLen, lentmp, count; + GF_ETD_ItemText *tmp; + if (!etd) return GF_BAD_PARAM; + + *outSize = 5; + if (gf_list_count(etd->itemDescriptionList) != gf_list_count(etd->itemTextList)) return GF_ODF_INVALID_DESCRIPTOR; + + count = gf_list_count(etd->itemDescriptionList); + for (i=0; iitemDescriptionList, i); + *outSize += OD_SizeUTF8String(tmp->text, etd->isUTF8); + tmp = (GF_ETD_ItemText*)gf_list_get(etd->itemTextList, i); + *outSize += OD_SizeUTF8String(tmp->text, etd->isUTF8); + } + *outSize += 1; + if (etd->NonItemText) { + if (etd->isUTF8) { + nonLen = strlen((const char*)etd->NonItemText); + } else { + nonLen = gf_utf8_wcslen((const unsigned short*)etd->NonItemText); + } + } else { + nonLen = 0; + } + len = 255; + lentmp = nonLen; + if (lentmp < 255) { + len = lentmp; + } + while (len == 255) { + *outSize += 1; + lentmp -= 255; + if (lentmp < 255) { + len = lentmp; + } + } + *outSize += nonLen * (etd->isUTF8 ? 1 : 2); + return GF_OK; +} + +GF_Err gf_odf_write_exp_text(GF_BitStream *bs, GF_ExpandedTextual *etd) +{ + GF_Err e; + u32 size, i, len, nonLen, lentmp, count; + GF_ETD_ItemText *tmp; + if (!etd) return GF_BAD_PARAM; + + if (gf_list_count(etd->itemDescriptionList) != gf_list_count(etd->itemTextList)) return GF_ODF_INVALID_DESCRIPTOR; + + e = gf_odf_size_descriptor((GF_Descriptor *)etd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, etd->tag, size); + if (e) return e; + + gf_bs_write_int(bs, etd->langCode, 24); + gf_bs_write_int(bs, etd->isUTF8, 1); + gf_bs_write_int(bs, 0, 7); //aligned + gf_bs_write_int(bs, gf_list_count(etd->itemDescriptionList), 8); + + count = gf_list_count(etd->itemDescriptionList); + for (i=0; iitemDescriptionList, i); + OD_WriteUTF8String(bs, tmp->text, etd->isUTF8); + tmp = (GF_ETD_ItemText*)gf_list_get(etd->itemTextList, i); + OD_WriteUTF8String(bs, tmp->text, etd->isUTF8); + } + if (etd->NonItemText) { + nonLen = strlen((const char*)etd->NonItemText) + 1; + if (etd->isUTF8) { + nonLen = strlen((const char*)etd->NonItemText); + } else { + nonLen = gf_utf8_wcslen((const unsigned short*)etd->NonItemText); + } + } else { + nonLen = 0; + } + lentmp = nonLen; + len = lentmp < 255 ? lentmp : 255; + while (len == 255) { + gf_bs_write_int(bs, len, 8); + lentmp -= len; + len = lentmp < 255 ? lentmp : 255; + } + gf_bs_write_int(bs, len, 8); + gf_bs_write_data(bs, etd->NonItemText, nonLen * (etd->isUTF8 ? 1 : 2)); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_pl_ext() +{ + GF_PLExt *newDesc = (GF_PLExt *) malloc(sizeof(GF_PLExt)); + if (!newDesc) return NULL; + newDesc->AudioProfileLevelIndication = 0; + newDesc->GraphicsProfileLevelIndication = 0; + newDesc->MPEGJProfileLevelIndication = 0; + newDesc->ODProfileLevelIndication = 0; + newDesc->profileLevelIndicationIndex = 0; + newDesc->SceneProfileLevelIndication = 0; + newDesc->VisualProfileLevelIndication = 0; + newDesc->tag = GF_ODF_EXT_PL_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_pl_ext(GF_PLExt *pld) +{ + if (!pld) return GF_BAD_PARAM; + + free(pld); + return GF_OK; +} + +GF_Err gf_odf_read_pl_ext(GF_BitStream *bs, GF_PLExt *pld, u32 DescSize) +{ + u32 nbBytes = 0; + if (!pld) return GF_BAD_PARAM; + + pld->profileLevelIndicationIndex = gf_bs_read_int(bs, 8); + pld->ODProfileLevelIndication = gf_bs_read_int(bs, 8); + pld->SceneProfileLevelIndication = gf_bs_read_int(bs, 8); + pld->AudioProfileLevelIndication = gf_bs_read_int(bs, 8); + pld->VisualProfileLevelIndication = gf_bs_read_int(bs, 8); + pld->GraphicsProfileLevelIndication = gf_bs_read_int(bs, 8); + pld->MPEGJProfileLevelIndication = gf_bs_read_int(bs, 8); + + nbBytes += 7; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_pl_ext(GF_PLExt *pld, u32 *outSize) +{ + if (!pld) return GF_BAD_PARAM; + + *outSize = 7; + return GF_OK; +} + +GF_Err gf_odf_write_pl_ext(GF_BitStream *bs, GF_PLExt *pld) +{ + GF_Err e; + u32 size; + if (!pld) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)pld, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, pld->tag, size); + if (e) return e; + + gf_bs_write_int(bs, pld->profileLevelIndicationIndex, 8); + gf_bs_write_int(bs, pld->ODProfileLevelIndication, 8); + gf_bs_write_int(bs, pld->SceneProfileLevelIndication, 8); + gf_bs_write_int(bs, pld->AudioProfileLevelIndication, 8); + gf_bs_write_int(bs, pld->VisualProfileLevelIndication, 8); + gf_bs_write_int(bs, pld->GraphicsProfileLevelIndication, 8); + gf_bs_write_int(bs, pld->MPEGJProfileLevelIndication, 8); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_segment() +{ + GF_Segment *newDesc = (GF_Segment *) malloc(sizeof(GF_Segment)); + if (!newDesc) return NULL; + + memset(newDesc, 0, sizeof(GF_Segment)); + newDesc->tag = GF_ODF_SEGMENT_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_segment(GF_Segment *sd) +{ + if (!sd) return GF_BAD_PARAM; + + if (sd->SegmentName) free(sd->SegmentName); + free(sd); + return GF_OK; +} + +GF_Err gf_odf_read_segment(GF_BitStream *bs, GF_Segment *sd, u32 DescSize) +{ + u32 size, nbBytes = 0; + if (!sd) return GF_BAD_PARAM; + + sd->startTime = gf_bs_read_double(bs); + sd->Duration = gf_bs_read_double(bs); + nbBytes += 16; + size = gf_bs_read_int(bs, 8); + nbBytes += 1; + if (size) { + sd->SegmentName = (char*) malloc(sizeof(char)*(size+1)); + if (!sd->SegmentName) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, sd->SegmentName, size); + sd->SegmentName[size] = 0; + nbBytes += size; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_segment(GF_Segment *sd, u32 *outSize) +{ + if (!sd) return GF_BAD_PARAM; + *outSize = 17; + if (sd->SegmentName) *outSize += strlen(sd->SegmentName); + return GF_OK; +} + +GF_Err gf_odf_write_segment(GF_BitStream *bs, GF_Segment *sd) +{ + GF_Err e; + u32 size; + if (!sd) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)sd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, sd->tag, size); + if (e) return e; + gf_bs_write_double(bs, sd->startTime); + gf_bs_write_double(bs, sd->Duration); + if (sd->SegmentName) { + gf_bs_write_int(bs, strlen(sd->SegmentName), 8); + gf_bs_write_data(bs, sd->SegmentName, strlen(sd->SegmentName)); + } else { + gf_bs_write_int(bs, 0, 8); + } + return GF_OK; +} +GF_Descriptor *gf_odf_new_mediatime() +{ + GF_MediaTime *newDesc = (GF_MediaTime *) malloc(sizeof(GF_MediaTime)); + if (!newDesc) return NULL; + + memset(newDesc, 0, sizeof(GF_MediaTime)); + newDesc->tag = GF_ODF_MEDIATIME_TAG; + return (GF_Descriptor *) newDesc; +} +GF_Err gf_odf_del_mediatime(GF_MediaTime *mt) +{ + if (!mt) return GF_BAD_PARAM; + free(mt); + return GF_OK; +} +GF_Err gf_odf_read_mediatime(GF_BitStream *bs, GF_MediaTime *mt, u32 DescSize) +{ + if (!mt) return GF_BAD_PARAM; + mt->mediaTimeStamp = gf_bs_read_double(bs); + return GF_OK; +} +GF_Err gf_odf_size_mediatime(GF_MediaTime *mt, u32 *outSize) +{ + if (!mt) return GF_BAD_PARAM; + *outSize = 8; + return GF_OK; +} +GF_Err gf_odf_write_mediatime(GF_BitStream *bs, GF_MediaTime *mt) +{ + GF_Err e; + u32 size; + if (!mt) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)mt, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, mt->tag, size); + if (e) return e; + gf_bs_write_double(bs, mt->mediaTimeStamp); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_ipi_ptr() +{ + GF_IPIPtr *newDesc = (GF_IPIPtr *) malloc(sizeof(GF_IPIPtr)); + if (!newDesc) return NULL; + newDesc->IPI_ES_Id = 0; + newDesc->tag = GF_ODF_IPI_PTR_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_ipi_ptr(GF_IPIPtr *ipid) +{ + if (!ipid) return GF_BAD_PARAM; + free(ipid); + return GF_OK; +} + +GF_Err gf_odf_read_ipi_ptr(GF_BitStream *bs, GF_IPIPtr *ipid, u32 DescSize) +{ + u32 nbBytes = 0; + if (! ipid) return GF_BAD_PARAM; + + ipid->IPI_ES_Id = gf_bs_read_int(bs, 16); + nbBytes += 2; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_ipi_ptr(GF_IPIPtr *ipid, u32 *outSize) +{ + if (! ipid) return GF_BAD_PARAM; + *outSize = 2; + return GF_OK; +} + +GF_Err gf_odf_write_ipi_ptr(GF_BitStream *bs, GF_IPIPtr *ipid) +{ + GF_Err e; + u32 size; + if (! ipid) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)ipid, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipid->tag, size); + if (e) return e; + gf_bs_write_int(bs, ipid->IPI_ES_Id, 16); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_ipmp() +{ + GF_IPMP_Descriptor *newDesc; + GF_SAFEALLOC(newDesc, GF_IPMP_Descriptor); + if (!newDesc) return NULL; + + newDesc->ipmpx_data = gf_list_new(); + newDesc->tag = GF_ODF_IPMP_TAG; + return (GF_Descriptor *) newDesc; +} +GF_Err gf_odf_del_ipmp(GF_IPMP_Descriptor *ipmp) +{ + if (!ipmp) return GF_BAD_PARAM; + if (ipmp->opaque_data) free(ipmp->opaque_data); + /*TODO DELETE IPMPX*/ + while (gf_list_count(ipmp->ipmpx_data)) { + GF_IPMPX_Data *p = (GF_IPMPX_Data *)gf_list_get(ipmp->ipmpx_data, 0); + gf_list_rem(ipmp->ipmpx_data, 0); + gf_ipmpx_data_del(p); + } + gf_list_del(ipmp->ipmpx_data); + free(ipmp); + return GF_OK; +} + +GF_Err gf_odf_read_ipmp(GF_BitStream *bs, GF_IPMP_Descriptor *ipmp, u32 DescSize) +{ + u32 size, nbBytes = 0; + if (!ipmp) return GF_BAD_PARAM; + + ipmp->IPMP_DescriptorID = gf_bs_read_int(bs, 8); + ipmp->IPMPS_Type = gf_bs_read_int(bs, 16); + nbBytes += 3; + size = DescSize - 3; + + /*IPMPX escape*/ + if ((ipmp->IPMP_DescriptorID==0xFF) && (ipmp->IPMPS_Type==0xFFFF)) { + ipmp->IPMP_DescriptorIDEx = gf_bs_read_int(bs, 16); + gf_bs_read_data(bs, (char*)ipmp->IPMP_ToolID, 16); + ipmp->control_point = gf_bs_read_int(bs, 8); + nbBytes += 19; + if (ipmp->control_point) { + ipmp->cp_sequence_code = gf_bs_read_int(bs, 8); + nbBytes += 1; + } + while (nbBytesipmpx_data, p); + nbBytes += (u32) gf_bs_get_position(bs) - pos; + } + } + /*URL*/ + else if (! ipmp->IPMPS_Type) { + ipmp->opaque_data = (char*)malloc(size + 1); + if (! ipmp->opaque_data) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ipmp->opaque_data, size); + nbBytes += size; + ipmp->opaque_data[size] = 0; + ipmp->opaque_data_size = size; + + } + /*data*/ + else { + ipmp->opaque_data_size = size; + ipmp->opaque_data = (char*)malloc(size); + if (! ipmp->opaque_data) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ipmp->opaque_data, size); + nbBytes += size; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_ipmp(GF_IPMP_Descriptor *ipmp, u32 *outSize) +{ + u32 i, s; + if (!ipmp) return GF_BAD_PARAM; + + *outSize = 3; + /*IPMPX escape*/ + if ((ipmp->IPMP_DescriptorID==0xFF) && (ipmp->IPMPS_Type==0xFFFF)) { + GF_IPMPX_Data *p; + *outSize += 19; + if (ipmp->control_point) *outSize += 1; + s = 0; + i=0; + while ((p = (GF_IPMPX_Data *)gf_list_enum(ipmp->ipmpx_data, &i))) { + s += gf_ipmpx_data_full_size(p); + } + (*outSize) += s; + } + else if (! ipmp->IPMPS_Type) { + if (!ipmp->opaque_data) return GF_ODF_INVALID_DESCRIPTOR; + *outSize += strlen(ipmp->opaque_data); + } else { + *outSize += ipmp->opaque_data_size; + } + return GF_OK; +} + +GF_Err gf_odf_write_ipmp(GF_BitStream *bs, GF_IPMP_Descriptor *ipmp) +{ + GF_Err e; + u32 size, i; + if (!ipmp) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ipmp, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmp->tag, size); + if (e) return e; + gf_bs_write_int(bs, ipmp->IPMP_DescriptorID, 8); + gf_bs_write_int(bs, ipmp->IPMPS_Type, 16); + + if ((ipmp->IPMP_DescriptorID==0xFF) && (ipmp->IPMPS_Type==0xFFFF)) { + GF_IPMPX_Data *p; + gf_bs_write_int(bs, ipmp->IPMP_DescriptorIDEx, 16); + gf_bs_write_data(bs, (char*)ipmp->IPMP_ToolID, 16); + gf_bs_write_int(bs, ipmp->control_point, 8); + if (ipmp->control_point) gf_bs_write_int(bs, ipmp->cp_sequence_code, 8); + + i=0; + while ((p = (GF_IPMPX_Data *) gf_list_enum(ipmp->ipmpx_data, &i))) { + gf_ipmpx_data_write(bs, p); + } + } + else if (!ipmp->IPMPS_Type) { + if (!ipmp->opaque_data) return GF_ODF_INVALID_DESCRIPTOR; + gf_bs_write_data(bs, ipmp->opaque_data, strlen(ipmp->opaque_data)); + } else { + gf_bs_write_data(bs, ipmp->opaque_data, ipmp->opaque_data_size); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_ipmp_ptr() +{ + GF_IPMPPtr *newDesc; + GF_SAFEALLOC(newDesc, GF_IPMPPtr); + if (!newDesc) return NULL; + newDesc->tag = GF_ODF_IPMP_PTR_TAG; + return (GF_Descriptor *) newDesc; +} +GF_Err gf_odf_del_ipmp_ptr(GF_IPMPPtr *ipmpd) +{ + if (!ipmpd) return GF_BAD_PARAM; + free(ipmpd); + return GF_OK; +} + +GF_Err gf_odf_read_ipmp_ptr(GF_BitStream *bs, GF_IPMPPtr *ipmpd, u32 DescSize) +{ + u32 nbBytes = 0; + if (! ipmpd) return GF_BAD_PARAM; + ipmpd->IPMP_DescriptorID = gf_bs_read_int(bs, 8); + nbBytes += 1; + if (ipmpd->IPMP_DescriptorID == 0xFF) { + ipmpd->IPMP_DescriptorIDEx = gf_bs_read_int(bs, 16); + ipmpd->IPMP_ES_ID = gf_bs_read_int(bs, 16); + nbBytes += 4; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} +GF_Err gf_odf_size_ipmp_ptr(GF_IPMPPtr *ipmpd, u32 *outSize) +{ + if (! ipmpd) return GF_BAD_PARAM; + *outSize = 1; + if (ipmpd->IPMP_DescriptorID == 0xFF) *outSize += 4; + return GF_OK; +} +GF_Err gf_odf_write_ipmp_ptr(GF_BitStream *bs, GF_IPMPPtr *ipmpd) +{ + GF_Err e; + u32 size; + if (! ipmpd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ipmpd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmpd->tag, size); + if (e) return e; + gf_bs_write_int(bs, ipmpd->IPMP_DescriptorID, 8); + if (ipmpd->IPMP_DescriptorID == 0xFF) { + gf_bs_write_int(bs, ipmpd->IPMP_DescriptorIDEx, 16); + gf_bs_write_int(bs, ipmpd->IPMP_ES_ID, 16); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_kw() +{ + GF_KeyWord *newDesc = (GF_KeyWord *) malloc(sizeof(GF_KeyWord)); + if (!newDesc) return NULL; + + newDesc->keyWordsList = gf_list_new(); + if (! newDesc->keyWordsList) { + free(newDesc); + return NULL; + } + newDesc->isUTF8 = 0; + newDesc->languageCode = 0; + newDesc->tag = GF_ODF_KW_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_kw(GF_KeyWord *kwd) +{ + if (!kwd) return GF_BAD_PARAM; + + while (gf_list_count(kwd->keyWordsList)) { + GF_KeyWordItem *tmp = (GF_KeyWordItem*)gf_list_get(kwd->keyWordsList, 0); + if (tmp) { + if (tmp->keyWord) free(tmp->keyWord); + free(tmp); + } + } + gf_list_del(kwd->keyWordsList); + free(kwd); + return GF_OK; +} + +GF_Err gf_odf_read_kw(GF_BitStream *bs, GF_KeyWord *kwd, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0, aligned, i, kwcount, len; + if (!kwd) return GF_BAD_PARAM; + + kwd->languageCode = gf_bs_read_int(bs, 24); + kwd->isUTF8 = gf_bs_read_int(bs, 1); + aligned = gf_bs_read_int(bs, 7); + kwcount = gf_bs_read_int(bs, 8); + nbBytes += 5; + + for (i = 0 ; i < kwcount; i++) { + GF_KeyWordItem *tmp = (GF_KeyWordItem*)malloc(sizeof(GF_KeyWordItem)); + if (! tmp) return GF_OUT_OF_MEM; + e = OD_ReadUTF8String(bs, & tmp->keyWord, kwd->isUTF8, &len); + if (e) return e; + e = gf_list_add(kwd->keyWordsList, tmp); + if (e) return e; + nbBytes += len; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +GF_Err gf_odf_size_kw(GF_KeyWord *kwd, u32 *outSize) +{ + u32 i; + GF_KeyWordItem *tmp; + if (!kwd) return GF_BAD_PARAM; + + *outSize = 5; + i=0; + while ((tmp = (GF_KeyWordItem *)gf_list_enum(kwd->keyWordsList, &i))) { + *outSize += OD_SizeUTF8String(tmp->keyWord, kwd->isUTF8); + } + return GF_OK; +} +GF_Err gf_odf_write_kw(GF_BitStream *bs, GF_KeyWord *kwd) +{ + GF_Err e; + u32 size, i; + GF_KeyWordItem *tmp; + if (!kwd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)kwd, &size); + e = gf_odf_write_base_descriptor(bs, kwd->tag, size); + + gf_bs_write_int(bs, kwd->languageCode, 24); + gf_bs_write_int(bs, kwd->isUTF8, 1); + gf_bs_write_int(bs, 0, 7); //aligned(8) + gf_bs_write_int(bs, gf_list_count(kwd->keyWordsList), 8); + + i=0; + while ((tmp = (GF_KeyWordItem *)gf_list_enum(kwd->keyWordsList, &i))) { + OD_WriteUTF8String(bs, tmp->keyWord, kwd->isUTF8); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_lang() +{ + GF_Language *newDesc = (GF_Language *) malloc(sizeof(GF_Language)); + if (!newDesc) return NULL; + newDesc->langCode = 0; + newDesc->tag = GF_ODF_LANG_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_lang(GF_Language *ld) +{ + if (!ld) return GF_BAD_PARAM; + free(ld); + return GF_OK; +} + +GF_Err gf_odf_read_lang(GF_BitStream *bs, GF_Language *ld, u32 DescSize) +{ + u32 nbBytes = 0; + if (!ld) return GF_BAD_PARAM; + + ld->langCode = gf_bs_read_int(bs, 24); + nbBytes += 3; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_lang(GF_Language *ld, u32 *outSize) +{ + if (!ld) return GF_BAD_PARAM; + *outSize = 3; + return GF_OK; +} + +GF_Err gf_odf_write_lang(GF_BitStream *bs, GF_Language *ld) +{ + GF_Err e; + u32 size; + if (!ld) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ld, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ld->tag, size); + if (e) return e; + gf_bs_write_int(bs, ld->langCode, 24); + return GF_OK; +} + + + +GF_Descriptor *gf_odf_new_auxvid() +{ + GF_AuxVideoDescriptor *newDesc; + GF_SAFEALLOC(newDesc, GF_AuxVideoDescriptor); + if (!newDesc) return NULL; + newDesc->tag = GF_ODF_AUX_VIDEO_DATA; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_auxvid(GF_AuxVideoDescriptor *ld) +{ + if (!ld) return GF_BAD_PARAM; + free(ld); + return GF_OK; +} + +GF_Err gf_odf_read_auxvid(GF_BitStream *bs, GF_AuxVideoDescriptor *ld, u32 DescSize) +{ + u32 nbBytes = 0; + if (!ld) return GF_BAD_PARAM; + + ld->aux_video_type = gf_bs_read_int(bs, 8); + ld->position_offset_h = gf_bs_read_int(bs, 8); + ld->position_offset_v = gf_bs_read_int(bs, 8); + nbBytes += 3; + switch (ld->aux_video_type) { + case 0x10: + ld->kfar = gf_bs_read_int(bs, 8); + ld->knear = gf_bs_read_int(bs, 8); + nbBytes += 2; + break; + case 0x11: + ld->parallax_zero = gf_bs_read_int(bs, 16); + ld->parallax_scale = gf_bs_read_int(bs, 16); + ld->dref = gf_bs_read_int(bs, 16); + ld->wref = gf_bs_read_int(bs, 16); + nbBytes += 8; + break; + } + while (nbBytes < DescSize) { + gf_bs_read_int(bs, 8); + nbBytes ++; + } + return GF_OK; +} + +GF_Err gf_odf_size_auxvid(GF_AuxVideoDescriptor *ld, u32 *outSize) +{ + if (!ld) return GF_BAD_PARAM; + switch (ld->aux_video_type) { + case 0x10: + *outSize = 5; + break; + case 0x11: + *outSize = 11; + break; + default: + *outSize = 3; + break; + } + return GF_OK; +} + +GF_Err gf_odf_write_auxvid(GF_BitStream *bs, GF_AuxVideoDescriptor *ld) +{ + GF_Err e; + u32 size; + if (!ld) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)ld, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ld->tag, size); + if (e) return e; + + gf_bs_write_int(bs, ld->aux_video_type, 8); + gf_bs_write_int(bs, ld->position_offset_h, 8); + gf_bs_write_int(bs, ld->position_offset_v, 8); + switch (ld->aux_video_type) { + case 0x10: + gf_bs_write_int(bs, ld->kfar, 8); + gf_bs_write_int(bs, ld->knear, 8); + break; + case 0x11: + gf_bs_write_int(bs, ld->parallax_zero, 16); + gf_bs_write_int(bs, ld->parallax_scale, 16); + gf_bs_write_int(bs, ld->dref, 16); + gf_bs_write_int(bs, ld->wref, 16); + break; + } + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_oci_date() +{ + GF_OCI_Data *newDesc = (GF_OCI_Data *) malloc(sizeof(GF_OCI_Data)); + if (!newDesc) return NULL; + memset(newDesc->OCICreationDate, 0, 5); + newDesc->tag = GF_ODF_OCI_DATE_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_oci_date(GF_OCI_Data *ocd) +{ + if (!ocd) return GF_BAD_PARAM; + free(ocd); + return GF_OK; +} + +GF_Err gf_odf_read_oci_date(GF_BitStream *bs, GF_OCI_Data *ocd, u32 DescSize) +{ + u32 nbBytes = 0; + if (!ocd) return GF_BAD_PARAM; + + gf_bs_read_data(bs, ocd->OCICreationDate, DATE_CODING_BIT_LEN); + nbBytes += DATE_CODING_BIT_LEN / 8; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_oci_date(GF_OCI_Data *ocd, u32 *outSize) +{ + if (!ocd) return GF_BAD_PARAM; + *outSize = (DATE_CODING_BIT_LEN / 8); + return GF_OK; +} + +GF_Err gf_odf_write_oci_date(GF_BitStream *bs, GF_OCI_Data *ocd) +{ + GF_Err e; + u32 size; + if (!ocd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ocd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ocd->tag, size); + if (e) return e; + gf_bs_write_data(bs, ocd->OCICreationDate , DATE_CODING_BIT_LEN); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_oci_name() +{ + GF_OCICreators *newDesc = (GF_OCICreators *) malloc(sizeof(GF_OCICreators)); + if (!newDesc) return NULL; + + newDesc->OCICreators = gf_list_new(); + if (! newDesc->OCICreators) { + free(newDesc); + return NULL; + } + newDesc->tag = GF_ODF_OCI_NAME_TAG; + return (GF_Descriptor *) newDesc; +} +GF_Err gf_odf_del_oci_name(GF_OCICreators *ocn) +{ + u32 i; + GF_OCICreator_item *tmp; + if (!ocn) return GF_BAD_PARAM; + + i=0; + while ((tmp = (GF_OCICreator_item *)gf_list_enum(ocn->OCICreators, &i))) { + if (tmp->OCICreatorName) free(tmp->OCICreatorName); + free(tmp); + } + gf_list_del(ocn->OCICreators); + free(ocn); + return GF_OK; +} + +GF_Err gf_odf_read_oci_name(GF_BitStream *bs, GF_OCICreators *ocn, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0; + u32 i, aligned, count, len; + if (!ocn) return GF_BAD_PARAM; + + count = gf_bs_read_int(bs, 8); + nbBytes += 1; + for (i = 0; i< count; i++) { + GF_OCICreator_item *tmp = (GF_OCICreator_item*)malloc(sizeof(GF_OCICreator_item)); + if (! tmp) return GF_OUT_OF_MEM; + tmp->langCode = gf_bs_read_int(bs, 24); + tmp->isUTF8 = gf_bs_read_int(bs, 1); + aligned = gf_bs_read_int(bs, 7); + nbBytes += 4; + e = OD_ReadUTF8String(bs, & tmp->OCICreatorName, tmp->isUTF8, &len); + if (e) return e; + nbBytes += len; + e = gf_list_add(ocn->OCICreators, tmp); + if (e) return e; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_oci_name(GF_OCICreators *ocn, u32 *outSize) +{ + u32 i; + GF_OCICreator_item *tmp; + if (!ocn) return GF_BAD_PARAM; + + *outSize = 1; + i=0; + while ((tmp = (GF_OCICreator_item *)gf_list_enum(ocn->OCICreators, &i))) { + *outSize += 4 + OD_SizeUTF8String(tmp->OCICreatorName, tmp->isUTF8); + } + return GF_OK; +} + +GF_Err gf_odf_write_oci_name(GF_BitStream *bs, GF_OCICreators *ocn) +{ + GF_Err e; + u32 size; + u32 i; + GF_OCICreator_item *tmp; + if (!ocn) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ocn, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ocn->tag, size); + if (e) return e; + gf_bs_write_int(bs, gf_list_count(ocn->OCICreators), 8); + + i=0; + while ((tmp = (GF_OCICreator_item *)gf_list_enum(ocn->OCICreators, &i))) { + gf_bs_write_int(bs, tmp->langCode, 24); + gf_bs_write_int(bs, tmp->isUTF8, 1); + gf_bs_write_int(bs, 0, 7); //aligned + gf_bs_write_int(bs, strlen(tmp->OCICreatorName) , 8); + OD_WriteUTF8String(bs, tmp->OCICreatorName, tmp->isUTF8); + } + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_pl_idx() +{ + GF_PL_IDX *newDesc = (GF_PL_IDX *) malloc(sizeof(GF_PL_IDX)); + if (!newDesc) return NULL; + newDesc->profileLevelIndicationIndex = 0; + newDesc->tag = GF_ODF_PL_IDX_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_pl_idx(GF_PL_IDX *plid) +{ + if (!plid) return GF_BAD_PARAM; + free(plid); + return GF_OK; +} + +GF_Err gf_odf_read_pl_idx(GF_BitStream *bs, GF_PL_IDX *plid, u32 DescSize) +{ + u32 nbBytes = 0; + if (!plid) return GF_BAD_PARAM; + + plid->profileLevelIndicationIndex = gf_bs_read_int(bs, 8); + nbBytes += 1; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} +GF_Err gf_odf_size_pl_idx(GF_PL_IDX *plid, u32 *outSize) +{ + if (!plid) return GF_BAD_PARAM; + *outSize = 1; + return GF_OK; +} +GF_Err gf_odf_write_pl_idx(GF_BitStream *bs, GF_PL_IDX *plid) +{ + GF_Err e; + u32 size; + if (!plid) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)plid, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, plid->tag, size); + if (e) return e; + gf_bs_write_int(bs, plid->profileLevelIndicationIndex, 8); + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_rating() +{ + GF_Rating *newDesc = (GF_Rating *) malloc(sizeof(GF_Rating)); + if (!newDesc) return NULL; + + newDesc->infoLength = 0; + newDesc->ratingInfo = NULL; + newDesc->ratingCriteria = 0; + newDesc->ratingEntity = 0; + newDesc->tag = GF_ODF_RATING_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_rating(GF_Rating *rd) +{ + if (!rd) return GF_BAD_PARAM; + + if (rd->ratingInfo) free(rd->ratingInfo); + free(rd); + return GF_OK; +} + +GF_Err gf_odf_read_rating(GF_BitStream *bs, GF_Rating *rd, u32 DescSize) +{ + u32 nbBytes = 0; + if (!rd) return GF_BAD_PARAM; + + rd->ratingEntity = gf_bs_read_int(bs, 32); + rd->ratingCriteria = gf_bs_read_int(bs, 16); + rd->infoLength = DescSize - 6; + nbBytes += 6; + + rd->ratingInfo = (char*)malloc(rd->infoLength); + if (! rd->ratingInfo) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, rd->ratingInfo, rd->infoLength); + nbBytes += rd->infoLength; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_rating(GF_Rating *rd, u32 *outSize) +{ + if (!rd) return GF_BAD_PARAM; + + *outSize = 6 + rd->infoLength; + return GF_OK; +} + +GF_Err gf_odf_write_rating(GF_BitStream *bs, GF_Rating *rd) +{ + GF_Err e; + u32 size; + if (!rd) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)rd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, rd->tag, size); + if (e) return e; + gf_bs_write_int(bs, rd->ratingEntity, 32); + gf_bs_write_int(bs, rd->ratingCriteria, 16); + gf_bs_write_data(bs, rd->ratingInfo, rd->infoLength); + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_reg() +{ + GF_Registration *newDesc = (GF_Registration *) malloc(sizeof(GF_Registration)); + if (!newDesc) return NULL; + newDesc->additionalIdentificationInfo = NULL; + newDesc->dataLength = 0; + newDesc->formatIdentifier = 0; + newDesc->tag = GF_ODF_REG_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_reg(GF_Registration *reg) +{ + if (!reg) return GF_BAD_PARAM; + + if (reg->additionalIdentificationInfo) free(reg->additionalIdentificationInfo); + free(reg); + return GF_OK; +} + +GF_Err gf_odf_read_reg(GF_BitStream *bs, GF_Registration *reg, u32 DescSize) +{ + u32 nbBytes = 0; + if (!reg) return GF_BAD_PARAM; + + reg->formatIdentifier = gf_bs_read_int(bs, 32); + reg->dataLength = DescSize - 4; + reg->additionalIdentificationInfo = (char*)malloc(reg->dataLength); + if (! reg->additionalIdentificationInfo) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, reg->additionalIdentificationInfo, reg->dataLength); + nbBytes += reg->dataLength + 4; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +GF_Err gf_odf_size_reg(GF_Registration *reg, u32 *outSize) +{ + if (!reg) return GF_BAD_PARAM; + + *outSize = 4 + reg->dataLength; + return GF_OK; +} + +GF_Err gf_odf_write_reg(GF_BitStream *bs, GF_Registration *reg) +{ + GF_Err e; + u32 size; + if (!reg) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)reg, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, reg->tag, size); + if (e) return e; + + gf_bs_write_int(bs, reg->formatIdentifier, 32); + gf_bs_write_data(bs, reg->additionalIdentificationInfo, reg->dataLength); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_short_text() +{ + GF_ShortTextual *newDesc = (GF_ShortTextual *) malloc(sizeof(GF_ShortTextual)); + if (!newDesc) return NULL; + + newDesc->eventName = NULL; + newDesc->eventText = NULL; + newDesc->isUTF8 = 0; + newDesc->langCode = 0; + newDesc->tag = GF_ODF_SHORT_TEXT_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_short_text(GF_ShortTextual *std) +{ + if (!std) return GF_BAD_PARAM; + + if (std->eventName) free(std->eventName); + if (std->eventText) free(std->eventText); + free(std); + return GF_OK; +} + +GF_Err gf_odf_read_short_text(GF_BitStream *bs, GF_ShortTextual *std, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0, len; + u8 aligned; + if (!std) return GF_BAD_PARAM; + + std->langCode = gf_bs_read_int(bs, 24); + std->isUTF8 = gf_bs_read_int(bs, 1); + aligned = gf_bs_read_int(bs, 7); + nbBytes += 4; + + e = OD_ReadUTF8String(bs, & std->eventName, std->isUTF8, &len); + if (e) return e; + nbBytes += len; + e = OD_ReadUTF8String(bs, & std->eventText, std->isUTF8, &len); + if (e) return e; + nbBytes += len; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_short_text(GF_ShortTextual *std, u32 *outSize) +{ + if (!std) return GF_BAD_PARAM; + *outSize = 4; + *outSize += OD_SizeUTF8String(std->eventName, std->isUTF8) + OD_SizeUTF8String(std->eventText, std->isUTF8); + return GF_OK; +} + +GF_Err gf_odf_write_short_text(GF_BitStream *bs, GF_ShortTextual *std) +{ + GF_Err e; + u32 size; + if (!std) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)std, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, std->tag, size); + if (e) return e; + gf_bs_write_int(bs, std->langCode, 24); + gf_bs_write_int(bs, std->isUTF8, 1); + gf_bs_write_int(bs, 0, 7); + OD_WriteUTF8String(bs, std->eventName, std->isUTF8); + OD_WriteUTF8String(bs, std->eventText, std->isUTF8); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_smpte_camera() +{ + GF_SMPTECamera *newDesc = (GF_SMPTECamera *) malloc(sizeof(GF_SMPTECamera)); + if (!newDesc) return NULL; + + newDesc->ParamList = gf_list_new(); + if (! newDesc->ParamList) { + free(newDesc); + return NULL; + } + newDesc->cameraID = 0; + newDesc->tag = GF_ODF_SMPTE_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_smpte_camera(GF_SMPTECamera *cpd) +{ + u32 i; + GF_SmpteParam *tmp; + if (!cpd) return GF_BAD_PARAM; + + i=0; + while ((tmp = (GF_SmpteParam *)gf_list_enum(cpd->ParamList, &i))) { + free(tmp); + } + gf_list_del(cpd->ParamList); + free(cpd); + return GF_OK; +} +GF_Err gf_odf_read_smpte_camera(GF_BitStream *bs, GF_SMPTECamera *cpd, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0, i, count; + if (!cpd) return GF_BAD_PARAM; + + cpd->cameraID = gf_bs_read_int(bs, 8); + count = gf_bs_read_int(bs, 8); + nbBytes += 2; + + for (i=0; i< count ; i++) { + GF_SmpteParam *tmp = (GF_SmpteParam*)malloc(sizeof(GF_SmpteParam)); + if (! tmp) return GF_OUT_OF_MEM; + tmp->paramID = gf_bs_read_int(bs, 8); + tmp->param = gf_bs_read_int(bs, 32); + nbBytes += 5; + e = gf_list_add(cpd->ParamList, tmp); + if (e) return e; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_smpte_camera(GF_SMPTECamera *cpd, u32 *outSize) +{ + if (!cpd) return GF_BAD_PARAM; + *outSize = 2 + 5 * gf_list_count(cpd->ParamList); + return GF_OK; +} + +GF_Err gf_odf_write_smpte_camera(GF_BitStream *bs, GF_SMPTECamera *cpd) +{ + GF_Err e; + GF_SmpteParam *tmp; + u32 size, i; + if (!cpd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)cpd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, cpd->tag, size); + if (e) return e; + gf_bs_write_int(bs, cpd->cameraID, 8); + gf_bs_write_int(bs, gf_list_count(cpd->ParamList), 8); + + i=0; + while ((tmp = (GF_SmpteParam *)gf_list_enum(cpd->ParamList, &i))) { + gf_bs_write_int(bs, tmp->paramID, 8); + gf_bs_write_int(bs, tmp->param, 32); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_sup_cid() +{ + GF_SCIDesc *newDesc = (GF_SCIDesc *) malloc(sizeof(GF_SCIDesc)); + if (!newDesc) return NULL; + newDesc->supplContentIdentifierTitle = NULL; + newDesc->supplContentIdentifierValue =NULL; + newDesc->languageCode = 0; + newDesc->tag = GF_ODF_SCI_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_sup_cid(GF_SCIDesc *scid) +{ + if (!scid) return GF_BAD_PARAM; + + if (scid->supplContentIdentifierTitle) free(scid->supplContentIdentifierTitle); + if (scid->supplContentIdentifierValue) free(scid->supplContentIdentifierValue); + free(scid); + return GF_OK; +} + +GF_Err gf_odf_read_sup_cid(GF_BitStream *bs, GF_SCIDesc *scid, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0, len; + if (! scid) return GF_BAD_PARAM; + + scid->languageCode = gf_bs_read_int(bs, 24); + nbBytes += 3; + e = OD_ReadUTF8String(bs, & scid->supplContentIdentifierTitle, 1, &len); + if (e) return e; + nbBytes += len; + e = OD_ReadUTF8String(bs, & scid->supplContentIdentifierValue, 1, &len); + if (e) return e; + nbBytes += len; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +GF_Err gf_odf_size_sup_cid(GF_SCIDesc *scid, u32 *outSize) +{ + if (! scid) return GF_BAD_PARAM; + *outSize = 3 + OD_SizeUTF8String(scid->supplContentIdentifierTitle, 1) + OD_SizeUTF8String(scid->supplContentIdentifierValue, 1); + return GF_OK; +} +GF_Err gf_odf_write_sup_cid(GF_BitStream *bs, GF_SCIDesc *scid) +{ + GF_Err e; + u32 size; + if (! scid) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)scid, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, scid->tag, size); + if (e) return e; + gf_bs_write_int(bs, scid->languageCode, 24); + OD_WriteUTF8String(bs, scid->supplContentIdentifierTitle, 1); + OD_WriteUTF8String(bs, scid->supplContentIdentifierValue, 1); + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_muxinfo() +{ + GF_MuxInfo *newDesc = (GF_MuxInfo *) malloc(sizeof(GF_MuxInfo)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_MuxInfo)); + newDesc->tag = GF_ODF_MUXINFO_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_muxinfo(GF_MuxInfo *mi) +{ + if (!mi) return GF_BAD_PARAM; + if (mi->file_name) free(mi->file_name); + if (mi->streamFormat) free(mi->streamFormat); + if (mi->textNode) free(mi->textNode); + if (mi->fontNode) free(mi->fontNode); + free(mi); + return GF_OK; +} + +GF_Err gf_odf_read_muxinfo(GF_BitStream *bs, GF_MuxInfo *mi, u32 DescSize) +{ + return GF_OK; +} +GF_Err gf_odf_size_muxinfo(GF_MuxInfo *mi, u32 *outSize) +{ + *outSize = 0; + return GF_OK; +} +GF_Err gf_odf_write_muxinfo(GF_BitStream *bs, GF_MuxInfo *mi) +{ + return GF_OK; +} + +GF_Descriptor *gf_odf_New_ElemMask() +{ + GF_ElementaryMask *newDesc = (GF_ElementaryMask*) malloc (sizeof(GF_ElementaryMask)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_ElementaryMask)); + newDesc->tag = GF_ODF_ELEM_MASK_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_ElemMask(GF_ElementaryMask *desc) +{ + if (desc->node_name) free(desc->node_name); + free(desc); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_bifs_cfg() +{ + GF_BIFSConfig *newDesc = (GF_BIFSConfig *) malloc(sizeof(GF_BIFSConfig)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_BIFSConfig)); + newDesc->tag = GF_ODF_BIFS_CFG_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_bifs_cfg(GF_BIFSConfig *desc) +{ + if (desc->elementaryMasks) { + u32 i, count = gf_list_count(desc->elementaryMasks); + for (i=0; ielementaryMasks, i); + if (tmp->node_name) free(tmp->node_name); + free(tmp); + } + gf_list_del(desc->elementaryMasks); + } + free(desc); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_laser_cfg() +{ + GF_LASERConfig *newDesc = (GF_LASERConfig *) malloc(sizeof(GF_LASERConfig)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_LASERConfig)); + newDesc->tag = GF_ODF_LASER_CFG_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_laser_cfg(GF_LASERConfig *desc) +{ + free(desc); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_ui_cfg() +{ + GF_UIConfig *newDesc = (GF_UIConfig *) malloc(sizeof(GF_UIConfig)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_UIConfig)); + newDesc->tag = GF_ODF_UI_CFG_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_ui_cfg(GF_UIConfig *desc) +{ + if (desc->deviceName) free(desc->deviceName); + if (desc->ui_data) free(desc->ui_data); + free(desc); + return GF_OK; +} + + +/*IPMPX stuff*/ +GF_Descriptor *gf_odf_new_ipmp_tool_list() +{ + GF_IPMP_ToolList*newDesc = (GF_IPMP_ToolList*) malloc(sizeof(GF_IPMP_ToolList)); + if (!newDesc) return NULL; + newDesc->ipmp_tools = gf_list_new(); + newDesc->tag = GF_ODF_IPMP_TL_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_ipmp_tool_list(GF_IPMP_ToolList *ipmptl) +{ + if (!ipmptl) return GF_BAD_PARAM; + + while (gf_list_count(ipmptl->ipmp_tools)) { + GF_IPMP_Tool *t = (GF_IPMP_Tool *) gf_list_get(ipmptl->ipmp_tools, 0); + gf_list_rem(ipmptl->ipmp_tools, 0); + if (t->tool_url) free(t->tool_url); + free(t); + } + gf_list_del(ipmptl->ipmp_tools); + free(ipmptl); + return GF_OK; +} + +GF_Err gf_odf_read_ipmp_tool_list(GF_BitStream *bs, GF_IPMP_ToolList *ipmptl, u32 DescSize) +{ + GF_Err e; + u32 tmpSize; + u32 nbBytes = 0; + if (! ipmptl) return GF_BAD_PARAM; + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = gf_list_add(ipmptl->ipmp_tools, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +GF_Err gf_odf_size_ipmp_tool_list(GF_IPMP_ToolList *ipmptl, u32 *outSize) +{ + if (!ipmptl) return GF_BAD_PARAM; + *outSize = 0; + return gf_odf_size_descriptor_list(ipmptl->ipmp_tools, outSize); +} + +GF_Err gf_odf_write_ipmp_tool_list(GF_BitStream *bs, GF_IPMP_ToolList *ipmptl) +{ + GF_Err e; + u32 size; + if (!ipmptl) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)ipmptl, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmptl->tag, size); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, ipmptl->ipmp_tools); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_ipmp_tool() +{ + GF_IPMP_Tool *newDesc = (GF_IPMP_Tool*) malloc(sizeof(GF_IPMP_Tool)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_IPMP_Tool)); + newDesc->tag = GF_ODF_IPMP_TL_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_ipmp_tool(GF_IPMP_Tool *ipmpt) +{ + if (!ipmpt) return GF_BAD_PARAM; + if (ipmpt->tool_url) free(ipmpt->tool_url); + free(ipmpt); + return GF_OK; +} + +GF_Err gf_odf_read_ipmp_tool(GF_BitStream *bs, GF_IPMP_Tool *ipmpt, u32 DescSize) +{ + Bool is_alt, is_param; + u32 nbBytes = 0; + if (! ipmpt) return GF_BAD_PARAM; + gf_bs_read_data(bs, (char*) ipmpt->IPMP_ToolID, 16); + is_alt = gf_bs_read_int(bs, 1); + is_param = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 6); + nbBytes = 17; + + if (is_alt) { + u32 i; + ipmpt->num_alternate = gf_bs_read_int(bs, 8); + nbBytes += 1; + for (i=0; inum_alternate; i++) { + gf_bs_read_data(bs, (char*)ipmpt->specificToolID[i], 16); + nbBytes += 16; + if (nbBytes>DescSize) break; + } + } + if (nbBytes>DescSize) return GF_ODF_INVALID_DESCRIPTOR; + + if (is_param) { } + + if (nbBytestool_url = (char*)malloc(sizeof(char)*(s+1)); + gf_bs_read_data(bs, ipmpt->tool_url, s); + ipmpt->tool_url[s] = 0; + nbBytes += s; + } + } + + if (nbBytes!=DescSize) return GF_NON_COMPLIANT_BITSTREAM; + return GF_OK; +} + + +GF_Err gf_odf_size_ipmp_tool(GF_IPMP_Tool *ipmpt, u32 *outSize) +{ + if (!ipmpt) return GF_BAD_PARAM; + *outSize = 17; + if (ipmpt->num_alternate) *outSize += 1 + 16*ipmpt->num_alternate; + + if (ipmpt->tool_url) { + u32 s = strlen(ipmpt->tool_url); + *outSize += gf_odf_size_field_size(s) - 1 + s; + } + return GF_OK; +} + +GF_Err gf_odf_write_ipmp_tool(GF_BitStream *bs, GF_IPMP_Tool *ipmpt) +{ + GF_Err e; + u32 size; + if (!ipmpt) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)ipmpt, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmpt->tag, size); + if (e) return e; + + gf_bs_write_data(bs, (char*)ipmpt->IPMP_ToolID, 16); + gf_bs_write_int(bs, ipmpt->num_alternate ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 0, 6); + + if (ipmpt->num_alternate) { + u32 i; + gf_bs_write_int(bs, ipmpt->num_alternate, 8); + for (i=0;inum_alternate; i++) gf_bs_write_data(bs, (char*)ipmpt->specificToolID[i], 16); + } + if (ipmpt->tool_url) gf_ipmpx_write_array(bs, ipmpt->tool_url, strlen(ipmpt->tool_url)); + return GF_OK; +} diff --git a/src/odf/odf_codec.c b/src/odf/odf_codec.c new file mode 100644 index 0000000..fc4ff9f --- /dev/null +++ b/src/odf/odf_codec.c @@ -0,0 +1,653 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/************************************************************ + Object GF_Descriptor Codec Functions +************************************************************/ + +GF_EXPORT +GF_ODCodec *gf_odf_codec_new() +{ + GF_ODCodec *codec; + GF_List *comList; + + comList = gf_list_new(); + if (!comList) return NULL; + + codec = (GF_ODCodec *) malloc(sizeof(GF_ODCodec)); + if (!codec) { + gf_list_del(comList); + return NULL; + } + //the bitstream is always NULL. It is created on the fly for access unit processing only + codec->bs = NULL; + codec->CommandList = comList; + return codec; +} + +GF_EXPORT +void gf_odf_codec_del(GF_ODCodec *codec) +{ + if (!codec) return; + + while (gf_list_count(codec->CommandList)) { + GF_ODCom *com = (GF_ODCom *)gf_list_get(codec->CommandList, 0); + gf_odf_delete_command(com); + gf_list_rem(codec->CommandList, 0); + } + gf_list_del(codec->CommandList); + if (codec->bs) gf_bs_del(codec->bs); + free(codec); +} + + +/************************************************************ + Codec Encoder Functions +************************************************************/ + +GF_EXPORT +GF_Err gf_odf_codec_add_com(GF_ODCodec *codec, GF_ODCom *command) +{ + if (!codec || !command) return GF_BAD_PARAM; + return gf_list_add(codec->CommandList, command); +} + +GF_EXPORT +GF_Err gf_odf_codec_encode(GF_ODCodec *codec, Bool delete_content) +{ + GF_ODCom *com; + GF_Err e = GF_OK; + u32 i; + + if (!codec) return GF_BAD_PARAM; + + //check our bitstream: if existing, this means the previous encoded AU was not retrieved + //we DON'T allow that + if (codec->bs) return GF_BAD_PARAM; + codec->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + if (!codec->bs) return GF_OUT_OF_MEM; + + /*encode each command*/ + i = 0; + while ((com = (GF_ODCom *)gf_list_enum(codec->CommandList, &i))) { + e = gf_odf_write_command(codec->bs, com); if (e) goto err_exit; + //don't forget OD Commands are aligned... + gf_bs_align(codec->bs); + } + +//if an error occurs, delete the GF_BitStream and empty the codec +err_exit: + if (e) { + gf_bs_del(codec->bs); + codec->bs = NULL; + } + if (delete_content) { + while (gf_list_count(codec->CommandList)) { + com = (GF_ODCom *)gf_list_get(codec->CommandList, 0); + gf_odf_delete_command(com); + gf_list_rem(codec->CommandList, 0); + } + } + return e; +} + +GF_EXPORT +GF_Err gf_odf_codec_get_au(GF_ODCodec *codec, char **outAU, u32 *au_length) +{ + if (!codec || !codec->bs || !outAU || *outAU) return GF_BAD_PARAM; + gf_bs_get_content(codec->bs, outAU, au_length); + gf_bs_del(codec->bs); + codec->bs = NULL; + return GF_OK; +} + + + +/************************************************************ + Codec Decoder Functions +************************************************************/ + +GF_EXPORT +GF_Err gf_odf_codec_set_au(GF_ODCodec *codec, char *au, u32 au_length) +{ + if (!codec ) return GF_BAD_PARAM; + if (!au || !au_length) return GF_OK; + + //if the command list is not empty, this is an error + if (gf_list_count(codec->CommandList)) return GF_BAD_PARAM; + + //the bitStream should not be here + if (codec->bs) return GF_BAD_PARAM; + + codec->bs = gf_bs_new(au, (u64) au_length, (unsigned char)GF_BITSTREAM_READ); + if (!codec->bs) return GF_OUT_OF_MEM; + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_odf_codec_decode(GF_ODCodec *codec) +{ + GF_Err e = GF_OK; + u32 size = 0, comSize, bufSize; + GF_ODCom *com; + + if (!codec || !codec->bs) return GF_BAD_PARAM; + + bufSize = (u32) gf_bs_available(codec->bs); + while (size < bufSize) { + e = gf_odf_parse_command(codec->bs, &com, &comSize); if (e) goto err_exit; + gf_list_add(codec->CommandList, com); + size += comSize + gf_odf_size_field_size(comSize); + //OD Commands are aligned + gf_bs_align(codec->bs); + } + //then delete our bitstream + gf_bs_del(codec->bs); + codec->bs = NULL; + if (size != bufSize) { + e = GF_ODF_INVALID_COMMAND; + goto err_exit; + } + return e; + +err_exit: + if (codec->bs) { + gf_bs_del(codec->bs); + codec->bs = NULL; + } + while (gf_list_count(codec->CommandList)) { + com = (GF_ODCom*)gf_list_get(codec->CommandList, 0); + gf_odf_delete_command(com); + gf_list_rem(codec->CommandList, 0); + } + return e; +} + +//get the first command in the codec and remove the entry +GF_EXPORT +GF_ODCom *gf_odf_codec_get_com(GF_ODCodec *codec) +{ + GF_ODCom *com; + if (!codec || codec->bs) return NULL; + com = (GF_ODCom*)gf_list_get(codec->CommandList, 0); + if (com) gf_list_rem(codec->CommandList, 0); + return com; +} + + + +/************************************************************ + OD Commands Functions +************************************************************/ + +//some easy way to get an OD GF_ODCom... +GF_EXPORT +GF_ODCom *gf_odf_com_new(u8 tag) +{ + GF_ODCom *newcom; + + newcom = gf_odf_create_command(tag); + newcom->tag = tag; + return (GF_ODCom *)newcom; +} + +// ... and to delete it +GF_EXPORT +GF_Err gf_odf_com_del(GF_ODCom **com) +{ + GF_Err e; + e = gf_odf_delete_command(*com); + *com = NULL; + return e; +} + + + +/************************************************************ + Object Descriptors Functions +************************************************************/ + +//some easy way to get an mpeg4 descriptor ... +GF_EXPORT +GF_Descriptor *gf_odf_desc_new(u8 tag) +{ + GF_Descriptor *newdesc; + newdesc = gf_odf_create_descriptor(tag); + newdesc->tag = tag; + return (GF_Descriptor *)newdesc; +} + +// ... and to delete it +GF_EXPORT +void gf_odf_desc_del(GF_Descriptor *desc) +{ + if (desc) gf_odf_delete_descriptor(desc); +} + +//use this function to decode a standalone descriptor +//the desc MUST be formatted with tag and size field!!! +GF_EXPORT +GF_Err gf_odf_desc_read(char *raw_desc, u32 descSize, GF_Descriptor * *outDesc) +{ + GF_Err e; + u32 size; + GF_BitStream *bs; + + if (!raw_desc || !descSize) return GF_BAD_PARAM; + + bs = gf_bs_new(raw_desc, (u64) descSize, GF_BITSTREAM_READ); + if (!bs) return GF_OUT_OF_MEM; + + size = 0; + e = gf_odf_parse_descriptor(bs, outDesc, &size); + //the size dosn't have the header in it + size += gf_odf_size_field_size(size); +/* + if (size != descSize) { + if (*outDesc) gf_odf_delete_descriptor(*outDesc); + *outDesc = NULL; + e = GF_ODF_INVALID_DESCRIPTOR; + } +*/ + + gf_bs_del(bs); + return e; +} + +//use this function to encode a standalone descriptor +//the desc will be formatted with tag and size field +GF_EXPORT +GF_Err gf_odf_desc_write(GF_Descriptor *desc, char **outEncDesc, u32 *outSize) +{ + GF_Err e; + GF_BitStream *bs; + + if (!desc || !outEncDesc || !outSize) return GF_BAD_PARAM; + + *outEncDesc = NULL; + *outSize = 0; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + if (!bs) return GF_OUT_OF_MEM; + //then encode our desc... + e = gf_odf_write_descriptor(bs, desc); + if (e) { + gf_bs_del(bs); + return e; + } + //then get the content from our bitstream + gf_bs_get_content(bs, outEncDesc, outSize); + gf_bs_del(bs); + return GF_OK; +} + +//use this function to get the size of a standalone descriptor +GF_EXPORT +u32 gf_odf_desc_size(GF_Descriptor *desc) +{ + u32 descSize; + GF_Err e; + + if (!desc) return GF_BAD_PARAM; + //get the descriptor length + e = gf_odf_size_descriptor(desc, &descSize); + if (e) return 0; + //add the header length + descSize += gf_odf_size_field_size(descSize); + return descSize; + +} + +//this is usefull to duplicate on the fly a descriptor (mainly for authoring purposes) +GF_EXPORT +GF_Err gf_odf_desc_copy(GF_Descriptor *inDesc, GF_Descriptor **outDesc) +{ + GF_Err e; + char *desc; + u32 size; + + //warning: here we get some data allocated + e = gf_odf_desc_write(inDesc, &desc, &size); + if (e) return e; + e = gf_odf_desc_read(desc, size, outDesc); + free(desc); + return e; +} + + +/************************************************************ + Object Descriptors Edit Functions +************************************************************/ + +//This functions handles internally what desc can be added to another desc +//and adds it. NO DUPLICATION of the descriptor, so +//once a desc is added to its parent, destroying the parent WILL destroy this desc +GF_EXPORT +GF_Err gf_odf_desc_add_desc(GF_Descriptor *parentDesc, GF_Descriptor *newDesc) +{ + GF_DecoderConfig *dcd; + + //our ADD definition + GF_Err AddDescriptorToOD(GF_ObjectDescriptor *od, GF_Descriptor *desc); + GF_Err AddDescriptorToIOD(GF_InitialObjectDescriptor *iod, GF_Descriptor *desc); + GF_Err AddDescriptorToESD(GF_ESD *esd, GF_Descriptor *desc); + GF_Err AddDescriptorToIsomIOD(GF_IsomInitialObjectDescriptor *iod, GF_Descriptor *desc); + GF_Err AddDescriptorToIsomOD(GF_IsomObjectDescriptor *od, GF_Descriptor *desc); + + if (!parentDesc || !newDesc) return GF_BAD_PARAM; + + switch (parentDesc->tag) { + //these are container descriptors + case GF_ODF_OD_TAG: + return AddDescriptorToOD((GF_ObjectDescriptor *)parentDesc, newDesc); + case GF_ODF_IOD_TAG: + return AddDescriptorToIOD((GF_InitialObjectDescriptor *)parentDesc, newDesc); + case GF_ODF_ESD_TAG: + return AddDescriptorToESD((GF_ESD *)parentDesc, newDesc); + case GF_ODF_DCD_TAG: + dcd = (GF_DecoderConfig *)parentDesc; + if ((newDesc->tag == GF_ODF_DSI_TAG) + || (newDesc->tag == GF_ODF_BIFS_CFG_TAG) + || (newDesc->tag == GF_ODF_UI_CFG_TAG) + || (newDesc->tag == GF_ODF_TEXT_CFG_TAG) + ) { + if (dcd->decoderSpecificInfo) return GF_ODF_FORBIDDEN_DESCRIPTOR; + dcd->decoderSpecificInfo = (GF_DefaultDescriptor *) newDesc; + return GF_OK; + } else if (newDesc->tag == GF_ODF_EXT_PL_TAG) { + return gf_list_add(dcd->profileLevelIndicationIndexDescriptor, newDesc); + } + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + case GF_ODF_TEXT_CFG_TAG: + if (newDesc->tag != GF_ODF_TX3G_TAG) return GF_ODF_FORBIDDEN_DESCRIPTOR; + return gf_list_add(((GF_TextConfig *)parentDesc)->sample_descriptions, newDesc); + + case GF_ODF_QOS_TAG: + //tricky: the QoS doesnot accept a descriptor but a qualifier. + //We have another function for that... + return GF_BAD_PARAM; + + //MP4 File Format tags + case GF_ODF_ISOM_IOD_TAG: + return AddDescriptorToIsomIOD((GF_IsomInitialObjectDescriptor *)parentDesc, newDesc); + case GF_ODF_ISOM_OD_TAG: + return AddDescriptorToIsomOD((GF_IsomObjectDescriptor *)parentDesc, newDesc); + + case GF_ODF_IPMP_TL_TAG: + if (newDesc->tag!=GF_ODF_IPMP_TOOL_TAG) return GF_BAD_PARAM; + return gf_list_add(((GF_IPMP_ToolList *)parentDesc)->ipmp_tools, newDesc); + + case GF_ODF_BIFS_CFG_TAG: + { + GF_BIFSConfig *cfg = (GF_BIFSConfig *)parentDesc; + if (newDesc->tag!=GF_ODF_ELEM_MASK_TAG) return GF_BAD_PARAM; + if (!cfg->elementaryMasks) cfg->elementaryMasks = gf_list_new(); + return gf_list_add(cfg->elementaryMasks, newDesc); + } + default: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + } +} + + + +/************************************************************ + QoSQualifiers Functions +************************************************************/ + +GF_EXPORT +GF_QoS_Default *gf_odf_qos_new(u8 tag) +{ + + GF_QoS_Default *NewQoS(u8 tag); + + GF_QoS_Default *qos; + + qos = NewQoS(tag); + return qos; +} + +GF_EXPORT +GF_Err gf_odf_qos_del(GF_QoS_Default **qos) +{ + if (*qos) gf_odf_delete_qos_qual(*qos); + *qos = NULL; + return GF_OK; +} + + +//same function, but for QoS, as a Qualifier IS NOT a descriptor +GF_EXPORT +GF_Err gf_odf_qos_add_qualif(GF_QoS_Descriptor *desc, GF_QoS_Default *qualif) +{ + u32 i; + GF_QoS_Default *def; + + if (desc->tag != GF_ODF_QOS_TAG) return GF_BAD_PARAM; + if (desc->predefined) return GF_ODF_FORBIDDEN_DESCRIPTOR; + + i=0; + while ((def = (GF_QoS_Default *)gf_list_enum(desc->QoS_Qualifiers, &i))) { + //if same Qualifier, not allowed... + if (def->tag == qualif->tag) return GF_ODF_FORBIDDEN_DESCRIPTOR; + } + return gf_list_add(desc->QoS_Qualifiers, qualif); +} + + + + +/***************************************************************************************** + Since IPMP V2, we introduce a new set of functions to read / write a list of + descriptors that have no containers (a bit like an OD command, but for descriptors) + This is usefull for IPMPv2 DecoderSpecificInfo which contains a set of + IPMP_Declarators. + As it could be used for other purposes we keep it generic + You must create the list yourself, the functions just encode/decode from/to the list +*****************************************************************************************/ + +GF_EXPORT +GF_Err gf_odf_desc_list_read(char *raw_list, u32 raw_size, GF_List *descList) +{ + GF_BitStream *bs; + u32 size, desc_size; + GF_Descriptor *desc; + GF_Err e = GF_OK; + + if (!descList || !raw_list || !raw_size) return GF_BAD_PARAM; + + bs = gf_bs_new(raw_list, raw_size, GF_BITSTREAM_READ); + if (!bs) return GF_OUT_OF_MEM; + + size = 0; + while (size < raw_size) { + e = gf_odf_parse_descriptor(bs, &desc, &desc_size); + if (e) goto exit; + gf_list_add(descList, desc); + size += desc_size + gf_odf_size_field_size(desc_size); + } + +exit: + //then delete our bitstream + gf_bs_del(bs); + if (size != raw_size) e = GF_ODF_INVALID_DESCRIPTOR; + return e; +} + + +GF_EXPORT +GF_Err gf_odf_desc_list_write(GF_List *descList, char **outEncList, u32 *outSize) +{ + GF_BitStream *bs; + GF_Err e; + + if (!descList || !outEncList || *outEncList || !outSize) return GF_BAD_PARAM; + + *outSize = 0; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + if (!bs) return GF_OUT_OF_MEM; + + e = gf_odf_write_descriptor_list(bs, descList); + if (e) { + gf_bs_del(bs); + return e; + } + + gf_bs_get_content(bs, outEncList, outSize); + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_odf_desc_list_size(GF_List *descList, u32 *outSize) +{ + return gf_odf_size_descriptor_list(descList, outSize); +} + +//this functions will destroy the descriptors in a list but not the list +GF_EXPORT +GF_Err gf_odf_desc_list_del(GF_List *descList) +{ + GF_Err e; + GF_Descriptor *tmp; + + if (! descList) return GF_BAD_PARAM; + + while (gf_list_count(descList)) { + tmp = (GF_Descriptor*)gf_list_get(descList, 0); + gf_list_rem(descList, 0); + e = gf_odf_delete_descriptor(tmp); + if (e) return e; + } + return GF_OK; +} + + + +GF_EXPORT +GF_ESD *gf_odf_desc_esd_new(u32 sl_predefined) +{ + GF_ESD *esd; + esd = (GF_ESD *) gf_odf_desc_new(GF_ODF_ESD_TAG); + esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + esd->slConfig = (GF_SLConfig *) gf_odf_new_slc((u8) sl_predefined); + return esd; +} + + +GF_Err gf_odf_codec_apply_com(GF_ODCodec *codec, GF_ODCom *command) +{ + GF_ODCom *com; + GF_ODUpdate *odU, *odU_o; + u32 i, count; + count = gf_list_count(codec->CommandList); + + switch (command->tag) { + case GF_ODF_OD_REMOVE_TAG: + for (i=0; iCommandList, i); + /*process OD updates*/ + if (com->tag==GF_ODF_OD_UPDATE_TAG) { + u32 count, j, k; + GF_ODRemove *odR = (GF_ODRemove *) command; + odU = (GF_ODUpdate *)com; + count = gf_list_count(odU->objectDescriptors); + /*remove all descs*/ + for (k=0; kobjectDescriptors, k); + for (j=0; jNbODs; j++) { + if (od->objectDescriptorID==odR->OD_ID[j]) { + gf_list_rem(odU->objectDescriptors, k); + k--; count--; + gf_odf_desc_del((GF_Descriptor *)od); + break; + } + } + } + if (!gf_list_count(odU->objectDescriptors)) { + gf_list_rem(codec->CommandList, i); + i--; + count--; + } + } + /*process ESD updates*/ + else if (com->tag==GF_ODF_ESD_UPDATE_TAG) { + u32 j; + GF_ODRemove *odR = (GF_ODRemove *) command; + GF_ESDUpdate *esdU = (GF_ESDUpdate*)com; + for (j=0; jNbODs; j++) { + if (esdU->ODID==odR->OD_ID[j]) { + gf_list_rem(codec->CommandList, i); + i--; + count--; + gf_odf_com_del((GF_ODCom**)&esdU); + break; + } + } + } + } + return GF_OK; + case GF_ODF_OD_UPDATE_TAG: + odU_o = NULL; + for (i=0; iCommandList, i); + /*process OD updates*/ + if (odU_o->tag==GF_ODF_OD_UPDATE_TAG) break; + odU_o = NULL; + } + if (!odU_o) { + odU_o = (GF_ODUpdate *)gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + gf_list_add(codec->CommandList, odU_o); + } + odU = (GF_ODUpdate*)command; + count = gf_list_count(odU->objectDescriptors); + for (i=0; iobjectDescriptors, i); + u32 j, count2 = gf_list_count(odU_o->objectDescriptors); + for (j=0; jobjectDescriptors, j); + if (od2->objectDescriptorID==od->objectDescriptorID) { + found = 1; + break; + } + } + if (!found) { + GF_ObjectDescriptor *od_new; + gf_odf_desc_copy((GF_Descriptor*)od, (GF_Descriptor**)&od_new); + gf_list_add(odU_o->objectDescriptors, od_new); + } + + } + return GF_OK; + } + return GF_NOT_SUPPORTED; +} + diff --git a/src/odf/odf_command.c b/src/odf/odf_command.c new file mode 100644 index 0000000..e6d7cb5 --- /dev/null +++ b/src/odf/odf_command.c @@ -0,0 +1,647 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include + +GF_Err gf_odf_parse_command(GF_BitStream *bs, GF_ODCom **com, u32 *com_size) +{ + u32 val, size, sizeHeader; + u8 tag; + GF_Err err; + GF_ODCom *newCom; + if (!bs) return GF_BAD_PARAM; + + *com_size = 0; + + //tag + tag = gf_bs_read_int(bs, 8); + sizeHeader = 1; + + //size + size = 0; + do { + val = gf_bs_read_int(bs, 8); + sizeHeader++; + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + *com_size = size; + + newCom = gf_odf_create_command(tag); + if (! newCom) { + *com = NULL; + return GF_OUT_OF_MEM; + } + + newCom->tag = tag; + + err = gf_odf_read_command(bs, newCom, *com_size); + //little trick to handle lazy bitstreams that encode + //SizeOfInstance on a fix number of bytes + //This nb of bytes is added in Read methods + *com_size += sizeHeader - gf_odf_size_field_size(*com_size); + *com = newCom; + if (err) { + gf_odf_delete_command(newCom); + *com = NULL; + } + return err; +} + + +GF_ODCom *gf_odf_new_base_command() +{ + GF_BaseODCom *newCom = (GF_BaseODCom *) malloc(sizeof(GF_BaseODCom)); + if (!newCom) return NULL; + newCom->dataSize = 0; + newCom->data = NULL; + return (GF_ODCom *)newCom; +} +GF_Err gf_odf_del_base_command(GF_BaseODCom *bcRemove) +{ + if (! bcRemove) return GF_BAD_PARAM; + if (bcRemove->data) free(bcRemove->data); + free(bcRemove); + return GF_OK; +} + +GF_Err gf_odf_read_base_command(GF_BitStream *bs, GF_BaseODCom *bcRem, u32 gf_odf_size_command) +{ + if (! bcRem) return GF_BAD_PARAM; + + bcRem->dataSize = gf_odf_size_command; + bcRem->data = (char *) malloc(sizeof(char) * bcRem->dataSize); + if (! bcRem->data) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, bcRem->data, bcRem->dataSize); + return GF_OK; +} +GF_Err gf_odf_size_base_command(GF_BaseODCom *bcRem, u32 *outSize) +{ + if (!bcRem) return GF_BAD_PARAM; + *outSize = bcRem->dataSize; + return GF_OK; +} +GF_Err gf_odf_write_base_command(GF_BitStream *bs, GF_BaseODCom *bcRem) +{ + u32 size; + GF_Err e; + if (! bcRem) return GF_BAD_PARAM; + + e = gf_odf_size_base_command(bcRem, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, bcRem->tag, size); + if (e) return e; + gf_bs_write_data(bs, bcRem->data, bcRem->dataSize); + return GF_OK; +} + +GF_ODCom *gf_odf_new_od_remove() +{ + GF_ODRemove *newCom = (GF_ODRemove *) malloc(sizeof(GF_ODRemove)); + if (!newCom) return NULL; + newCom->NbODs = 0; + newCom->OD_ID = NULL; + newCom->tag = GF_ODF_OD_REMOVE_TAG; + return (GF_ODCom *)newCom; +} +GF_Err gf_odf_del_od_remove(GF_ODRemove *ODRemove) +{ + if (! ODRemove) return GF_BAD_PARAM; + if (ODRemove->OD_ID) free(ODRemove->OD_ID); + free(ODRemove); + return GF_OK; +} +GF_Err gf_odf_read_od_remove(GF_BitStream *bs, GF_ODRemove *odRem, u32 gf_odf_size_command) +{ + u32 i = 0, nbBits; + if (! odRem) return GF_BAD_PARAM; + + odRem->NbODs = (u32 ) (gf_odf_size_command * 8) / 10; + odRem->OD_ID = (u16 *) malloc(sizeof(u16) * odRem->NbODs); + if (! odRem->OD_ID) return GF_OUT_OF_MEM; + + for (i = 0; i < odRem->NbODs ; i++) { + odRem->OD_ID[i] = gf_bs_read_int(bs, 10); + } + nbBits = odRem->NbODs * 10; + //now we need to align !!! + nbBits += gf_bs_align(bs); + if (nbBits != (gf_odf_size_command * 8)) return GF_ODF_INVALID_COMMAND; + return GF_OK; +} + +GF_Err gf_odf_size_od_remove(GF_ODRemove *odRem, u32 *outSize) +{ + u32 size; + if (!odRem) return GF_BAD_PARAM; + + size = 10 * odRem->NbODs; + *outSize = 0; + *outSize = size/8; + if (*outSize * 8 != size) *outSize += 1; + return GF_OK; +} + +GF_Err gf_odf_write_od_remove(GF_BitStream *bs, GF_ODRemove *odRem) +{ + GF_Err e; + u32 size, i; + if (! odRem) return GF_BAD_PARAM; + + e = gf_odf_size_od_remove(odRem, &size); + e = gf_odf_write_base_descriptor(bs, odRem->tag, size); + + for (i = 0; i < odRem->NbODs; i++) { + gf_bs_write_int(bs, odRem->OD_ID[i], 10); + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} + + + +GF_ODCom *gf_odf_new_od_update() +{ + GF_ODUpdate *newCom = (GF_ODUpdate *) malloc(sizeof(GF_ODUpdate)); + if (!newCom) return NULL; + + newCom->objectDescriptors = gf_list_new(); + if (! newCom->objectDescriptors) { + free(newCom); + return NULL; + } + newCom->tag = GF_ODF_OD_UPDATE_TAG; + return (GF_ODCom *)newCom; +} + +GF_Err gf_odf_del_od_update(GF_ODUpdate *ODUpdate) +{ + GF_Err e; + if (! ODUpdate) return GF_BAD_PARAM; + while (gf_list_count(ODUpdate->objectDescriptors)) { + GF_Descriptor *tmp = (GF_Descriptor*)gf_list_get(ODUpdate->objectDescriptors, 0); + e = gf_odf_delete_descriptor(tmp); + if (e) return e; + e = gf_list_rem(ODUpdate->objectDescriptors, 0); + if (e) return e; + } + gf_list_del(ODUpdate->objectDescriptors); + free(ODUpdate); + return GF_OK; +} + + + +GF_Err AddToODUpdate(GF_ODUpdate *odUp, GF_Descriptor *desc) +{ + if (! odUp) return GF_BAD_PARAM; + if (! desc) return GF_OK; + + switch (desc->tag) { + case GF_ODF_OD_TAG: + case GF_ODF_IOD_TAG: + case GF_ODF_ISOM_IOD_TAG: + case GF_ODF_ISOM_OD_TAG: + return gf_list_add(odUp->objectDescriptors, desc); + + default: + gf_odf_delete_descriptor(desc); + return GF_OK; + } +} + +GF_Err gf_odf_read_od_update(GF_BitStream *bs, GF_ODUpdate *odUp, u32 gf_odf_size_command) +{ + GF_Descriptor *tmp; + GF_Err e = GF_OK; + u32 tmpSize = 0, nbBytes = 0; + if (! odUp) return GF_BAD_PARAM; + + while (nbBytes < gf_odf_size_command) { + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + e = AddToODUpdate(odUp, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + //OD commands are aligned + gf_bs_align(bs); + if (nbBytes != gf_odf_size_command) return GF_ODF_INVALID_COMMAND; + return e; +} +GF_Err gf_odf_size_od_update(GF_ODUpdate *odUp, u32 *outSize) +{ + GF_Descriptor *tmp; + u32 i, tmpSize; + if (!odUp) return GF_BAD_PARAM; + + *outSize = 0; + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(odUp->objectDescriptors, &i))) { + gf_odf_size_descriptor(tmp, &tmpSize); + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + return GF_OK; +} +GF_Err gf_odf_write_od_update(GF_BitStream *bs, GF_ODUpdate *odUp) +{ + GF_Err e; + GF_Descriptor *tmp; + u32 size, i; + if (! odUp) return GF_BAD_PARAM; + + e = gf_odf_size_od_update(odUp, &size); + if (e) return e; + gf_odf_write_base_descriptor(bs, odUp->tag, size); + if (e) return e; + + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(odUp->objectDescriptors, &i))) { + e = gf_odf_write_descriptor(bs, tmp); + if (e) return e; + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} + + +GF_ODCom *gf_odf_new_esd_update() +{ + GF_ESDUpdate *newCom = (GF_ESDUpdate *) malloc(sizeof(GF_ESDUpdate)); + if (!newCom) return NULL; + + newCom->ESDescriptors = gf_list_new(); + if (! newCom->ESDescriptors) { + free(newCom); + return NULL; + } + newCom->tag = GF_ODF_ESD_UPDATE_TAG; + return (GF_ODCom *)newCom; +} + +GF_Err gf_odf_del_esd_update(GF_ESDUpdate *ESDUpdate) +{ + GF_Err e; + if (! ESDUpdate) return GF_BAD_PARAM; + while (gf_list_count(ESDUpdate->ESDescriptors)) { + GF_Descriptor *tmp = (GF_Descriptor*)gf_list_get(ESDUpdate->ESDescriptors, 0); + e = gf_odf_delete_descriptor(tmp); + if (e) return e; + e = gf_list_rem(ESDUpdate->ESDescriptors, 0); + if (e) return e; + } + gf_list_del(ESDUpdate->ESDescriptors); + free(ESDUpdate); + return GF_OK; +} + +GF_Err AddToESDUpdate(GF_ESDUpdate *esdUp, GF_Descriptor *desc) +{ + if (! esdUp) return GF_BAD_PARAM; + if (!desc) return GF_OK; + + switch (desc->tag) { + case GF_ODF_ESD_TAG: + case GF_ODF_ESD_REF_TAG: + return gf_list_add(esdUp->ESDescriptors, desc); + + default: + gf_odf_delete_descriptor(desc); + return GF_OK; + } +} + +GF_Err gf_odf_read_esd_update(GF_BitStream *bs, GF_ESDUpdate *esdUp, u32 gf_odf_size_command) +{ + GF_Descriptor *tmp; + u32 tmpSize = 0, nbBits = 0; + GF_Err e = GF_OK; + if (! esdUp) return GF_BAD_PARAM; + + esdUp->ODID = gf_bs_read_int(bs, 10); + nbBits += 10; + //very tricky, we're at the bit level here... + while (1) { + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + e = AddToESDUpdate(esdUp, tmp); + if (e) return e; + nbBits += ( tmpSize + gf_odf_size_field_size(tmpSize) ) * 8; + //our com is aligned, so nbBits is between (gf_odf_size_command-1)*8 and gf_odf_size_command*8 + if ( ( (nbBits >(gf_odf_size_command-1)*8) && (nbBits <= gf_odf_size_command * 8)) + || (nbBits > gf_odf_size_command*8) ) { //this one is a security break + break; + } + } + if (nbBits > gf_odf_size_command * 8) return GF_ODF_INVALID_COMMAND; + //Align our bitstream + nbBits += gf_bs_align(bs); + if (nbBits != gf_odf_size_command *8) return GF_ODF_INVALID_COMMAND; + return e; +} + + + +GF_Err gf_odf_size_esd_update(GF_ESDUpdate *esdUp, u32 *outSize) +{ + u32 i, BitSize, tmpSize; + GF_Descriptor *tmp; + if (!esdUp) return GF_BAD_PARAM; + + *outSize = 0; + BitSize = 10; + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(esdUp->ESDescriptors, &i))) { + gf_odf_size_descriptor(tmp, &tmpSize); + BitSize += ( tmpSize + gf_odf_size_field_size(tmpSize) ) * 8; + } + while ((s32) BitSize > 0) { + BitSize -= 8; + *outSize += 1; + } + return GF_OK; +} +GF_Err gf_odf_write_esd_update(GF_BitStream *bs, GF_ESDUpdate *esdUp) +{ + GF_Descriptor *tmp; + GF_Err e; + u32 size, i; + if (! esdUp) return GF_BAD_PARAM; + + e = gf_odf_size_esd_update(esdUp, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, esdUp->tag, size); + if (e) return e; + + gf_bs_write_int(bs, esdUp->ODID, 10); + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(esdUp->ESDescriptors, &i))) { + e = gf_odf_write_descriptor(bs, tmp); + if (e) return e; + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} + + +GF_ODCom *gf_odf_new_esd_remove() +{ + GF_ESDRemove *newCom = (GF_ESDRemove *) malloc(sizeof(GF_ESDRemove)); + if (!newCom) return NULL; + newCom->NbESDs = 0; + newCom->ES_ID = NULL; + newCom->tag = GF_ODF_ESD_REMOVE_TAG; + return (GF_ODCom *)newCom; +} + +GF_Err gf_odf_del_esd_remove(GF_ESDRemove *ESDRemove) +{ + if (! ESDRemove) return GF_BAD_PARAM; + if (ESDRemove->ES_ID) free(ESDRemove->ES_ID); + free(ESDRemove); + return GF_OK; +} + + +GF_Err gf_odf_read_esd_remove(GF_BitStream *bs, GF_ESDRemove *esdRem, u32 gf_odf_size_command) +{ + u32 i = 0, aligned, nbBits; + if (! esdRem) return GF_BAD_PARAM; + + esdRem->ODID = gf_bs_read_int(bs, 10); + aligned = gf_bs_read_int(bs, 6); //aligned + + //we have gf_odf_size_command - 2 bytes left, and this is our ES_ID[1...255] + //this works because OD commands are aligned + if (gf_odf_size_command < 2) return GF_ODF_INVALID_DESCRIPTOR; + if (gf_odf_size_command == 2) { + esdRem->NbESDs = 0; + esdRem->ES_ID = NULL; + return GF_OK; + } + esdRem->NbESDs = (gf_odf_size_command - 2) / 2; + esdRem->ES_ID = (u16 *) malloc(sizeof(u16) * esdRem->NbESDs); + if (! esdRem->ES_ID) return GF_OUT_OF_MEM; + for (i = 0; i < esdRem->NbESDs ; i++) { + esdRem->ES_ID[i] = gf_bs_read_int(bs, 16); + } + //OD commands are aligned (but we should already be aligned.... + nbBits = gf_bs_align(bs); + return GF_OK; +} + +GF_Err gf_odf_size_esd_remove(GF_ESDRemove *esdRem, u32 *outSize) +{ + if (!esdRem) return GF_BAD_PARAM; + *outSize = 2 + 2 * esdRem->NbESDs; + return GF_OK; +} +GF_Err gf_odf_write_esd_remove(GF_BitStream *bs, GF_ESDRemove *esdRem) +{ + GF_Err e; + u32 size, i; + if (! esdRem) return GF_BAD_PARAM; + + e = gf_odf_size_esd_remove(esdRem, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, esdRem->tag, size); + if (e) return e; + + gf_bs_write_int(bs, esdRem->ODID, 10); + gf_bs_write_int(bs, 0, 6); //aligned + for (i = 0; i < esdRem->NbESDs ; i++) { + gf_bs_write_int(bs, esdRem->ES_ID[i], 16); + } + //OD commands are aligned (but we are already aligned.... + gf_bs_align(bs); + return GF_OK; +} + + +GF_ODCom *gf_odf_new_ipmp_remove() +{ + GF_IPMPRemove *newCom = (GF_IPMPRemove *) malloc(sizeof(GF_IPMPRemove)); + if (!newCom) return NULL; + newCom->IPMPDescID =NULL; + newCom->NbIPMPDs = 0; + newCom->tag = GF_ODF_IPMP_REMOVE_TAG; + return (GF_ODCom *)newCom; +} + +GF_Err gf_odf_del_ipmp_remove(GF_IPMPRemove *IPMPDRemove) +{ + if (! IPMPDRemove) return GF_BAD_PARAM; + if (IPMPDRemove->IPMPDescID) free(IPMPDRemove->IPMPDescID); + free(IPMPDRemove); + return GF_OK; +} + +GF_Err gf_odf_read_ipmp_remove(GF_BitStream *bs, GF_IPMPRemove *ipmpRem, u32 gf_odf_size_command) +{ + u32 i; + if (! ipmpRem) return GF_BAD_PARAM; + + //we have gf_odf_size_command bytes left, and this is our IPMPD_ID[1...255] + //this works because OD commands are aligned + if (!gf_odf_size_command) return GF_OK; + + ipmpRem->NbIPMPDs = gf_odf_size_command; + ipmpRem->IPMPDescID = (u8 *) malloc(sizeof(u8) * ipmpRem->NbIPMPDs); + if (! ipmpRem->IPMPDescID) return GF_OUT_OF_MEM; + + for (i = 0; i < ipmpRem->NbIPMPDs; i++) { + ipmpRem->IPMPDescID[i] = gf_bs_read_int(bs, 8); + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} + +GF_Err gf_odf_size_ipmp_remove(GF_IPMPRemove *ipmpRem, u32 *outSize) +{ + if (!ipmpRem) return GF_BAD_PARAM; + + *outSize = ipmpRem->NbIPMPDs; + return GF_OK; +} +GF_Err gf_odf_write_ipmp_remove(GF_BitStream *bs, GF_IPMPRemove *ipmpRem) +{ + GF_Err e; + u32 size, i; + if (! ipmpRem) return GF_BAD_PARAM; + + e = gf_odf_size_ipmp_remove(ipmpRem, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmpRem->tag, size); + if (e) return e; + + for (i = 0; i < ipmpRem->NbIPMPDs; i++) { + gf_bs_write_int(bs, ipmpRem->IPMPDescID[i], 8); + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} + +GF_ODCom *gf_odf_new_ipmp_update() +{ + GF_IPMPUpdate *newCom = (GF_IPMPUpdate *) malloc(sizeof(GF_IPMPUpdate)); + if (!newCom) return NULL; + newCom->IPMPDescList = gf_list_new(); + if (! newCom->IPMPDescList) { + free(newCom); + return NULL; + } + newCom->tag = GF_ODF_IPMP_UPDATE_TAG; + return (GF_ODCom *)newCom; +} + +GF_Err gf_odf_del_ipmp_update(GF_IPMPUpdate *IPMPDUpdate) +{ + GF_Err e; + if (! IPMPDUpdate) return GF_BAD_PARAM; + while (gf_list_count(IPMPDUpdate->IPMPDescList)) { + GF_Descriptor *tmp = (GF_Descriptor*)gf_list_get(IPMPDUpdate->IPMPDescList, 0); + e = gf_odf_delete_descriptor(tmp); + e = gf_list_rem(IPMPDUpdate->IPMPDescList, 0); + } + gf_list_del(IPMPDUpdate->IPMPDescList); + free(IPMPDUpdate); + return GF_OK; +} + +GF_Err AddToIPMPDUpdate(GF_IPMPUpdate *ipmpUp, GF_Descriptor *desc) +{ + if (! ipmpUp) return GF_BAD_PARAM; + if (!desc) return GF_OK; + + switch (desc->tag) { + case GF_ODF_IPMP_TAG: + return gf_list_add(ipmpUp->IPMPDescList, desc); + default: + gf_odf_delete_descriptor(desc); + return GF_OK; + } +} + +GF_Err gf_odf_read_ipmp_update(GF_BitStream *bs, GF_IPMPUpdate *ipmpUp, u32 gf_odf_size_command) +{ + GF_Descriptor *tmp; + u32 tmpSize = 0, nbBytes = 0; + GF_Err e = GF_OK; + if (! ipmpUp) return GF_BAD_PARAM; + + while (nbBytes < gf_odf_size_command) { + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + e = AddToIPMPDUpdate(ipmpUp, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + //OD commands are aligned + gf_bs_align(bs); + if (nbBytes != gf_odf_size_command) return GF_ODF_INVALID_COMMAND; + return e; +} + + +GF_Err gf_odf_size_ipmp_update(GF_IPMPUpdate *ipmpUp, u32 *outSize) +{ + GF_Descriptor *tmp; + u32 i, tmpSize; + if (!ipmpUp) return GF_BAD_PARAM; + + *outSize = 0; + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(ipmpUp->IPMPDescList, &i))) { + gf_odf_size_descriptor(tmp, &tmpSize); + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + return GF_OK; +} +GF_Err gf_odf_write_ipmp_update(GF_BitStream *bs, GF_IPMPUpdate *ipmpUp) +{ + GF_Err e; + GF_Descriptor *tmp; + u32 size, i; + if (! ipmpUp) return GF_BAD_PARAM; + + e = gf_odf_size_ipmp_update(ipmpUp, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmpUp->tag, size); + if (e) return e; + + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(ipmpUp->IPMPDescList, &i))) { + e = gf_odf_write_descriptor(bs, tmp); + if (e) return e; + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} diff --git a/src/odf/odf_dump.c b/src/odf/odf_dump.c new file mode 100644 index 0000000..e64cb48 --- /dev/null +++ b/src/odf/odf_dump.c @@ -0,0 +1,1940 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +/*for import flags*/ +#include + +#define OD_MAX_TREE 100 + +#define OD_FORMAT_INDENT( ind_buf, indent ) \ + { \ + u32 z; \ + assert(OD_MAX_TREE>indent); \ + for (z=0; ztag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_dump_od_update((GF_ODUpdate *)com, trace, indent, XMTDump); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_dump_od_remove((GF_ODRemove *)com, trace, indent, XMTDump); + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_dump_esd_update((GF_ESDUpdate *)com, trace, indent, XMTDump); + case GF_ODF_ESD_REMOVE_TAG: + return gf_odf_dump_esd_remove((GF_ESDRemove *)com, trace, indent, XMTDump); + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_dump_ipmp_update((GF_IPMPUpdate *)com, trace, indent, XMTDump); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_dump_ipmp_remove((GF_IPMPRemove *)com, trace, indent, XMTDump); + default: + return gf_odf_dump_base_command((GF_BaseODCom *) com, trace, indent, XMTDump); + } +} + + +GF_EXPORT +GF_Err gf_odf_dump_au(char *data, u32 dataLength, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_ODCom *com; + GF_ODCodec *odread = gf_odf_codec_new(); + gf_odf_codec_set_au(odread, data, dataLength); + gf_odf_codec_decode(odread); + + while (1) { + com = gf_odf_codec_get_com(odread); + if (!com) break; + + gf_odf_dump_com(com, trace, indent, XMTDump); + gf_odf_com_del(&com); + } + gf_odf_codec_del(odread); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_odf_dump_com_list(GF_List *commandList, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_ODCom *com; + u32 i; + i=0; + while ((com = (GF_ODCom *)gf_list_enum(commandList, &i))) { + gf_odf_dump_com(com, trace, indent, XMTDump); + } + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_odf_dump_desc(void *ptr, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_Descriptor *desc = (GF_Descriptor *)ptr; + + switch (desc->tag) { + case GF_ODF_IOD_TAG : + return gf_odf_dump_iod((GF_InitialObjectDescriptor *)desc, trace, indent, XMTDump); + case GF_ODF_ESD_TAG : + return gf_odf_dump_esd((GF_ESD *)desc, trace, indent, XMTDump); + case GF_ODF_DCD_TAG : + return gf_odf_dump_dcd((GF_DecoderConfig *)desc, trace, indent, XMTDump); + case GF_ODF_SLC_TAG: + return gf_odf_dump_slc((GF_SLConfig *)desc, trace, indent, XMTDump); + case GF_ODF_CC_TAG: + return gf_odf_dump_cc((GF_CCDescriptor *)desc, trace, indent, XMTDump); + case GF_ODF_CC_DATE_TAG: + return gf_odf_dump_cc_date((GF_CC_Date *)desc, trace, indent, XMTDump); + case GF_ODF_CC_NAME_TAG: + return gf_odf_dump_cc_name((GF_CC_Name *)desc, trace, indent, XMTDump); + case GF_ODF_CI_TAG: + return gf_odf_dump_ci((GF_CIDesc *)desc, trace, indent, XMTDump); + case GF_ODF_ESD_INC_TAG: + return gf_odf_dump_esd_inc((GF_ES_ID_Inc *)desc, trace, indent, XMTDump); + case GF_ODF_ESD_REF_TAG: + return gf_odf_dump_esd_ref((GF_ES_ID_Ref *)desc, trace, indent, XMTDump); + case GF_ODF_TEXT_TAG: + return gf_odf_dump_exp_text((GF_ExpandedTextual *)desc, trace, indent, XMTDump); + case GF_ODF_EXT_PL_TAG: + return gf_odf_dump_pl_ext((GF_PLExt *)desc, trace, indent, XMTDump); + case GF_ODF_IPI_PTR_TAG: + case GF_ODF_ISOM_IPI_PTR_TAG: + return gf_odf_dump_ipi_ptr((GF_IPIPtr *)desc, trace, indent, XMTDump); + case GF_ODF_IPMP_TAG: + return gf_odf_dump_ipmp((GF_IPMP_Descriptor *)desc, trace, indent, XMTDump); + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_dump_ipmp_ptr((GF_IPMPPtr *)desc, trace, indent, XMTDump); + case GF_ODF_KW_TAG: + return gf_odf_dump_kw((GF_KeyWord *)desc, trace, indent, XMTDump); + case GF_ODF_LANG_TAG: + return gf_odf_dump_lang((GF_Language *)desc, trace, indent, XMTDump); + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_dump_isom_iod((GF_IsomInitialObjectDescriptor *)desc, trace, indent, XMTDump); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_dump_isom_od((GF_IsomObjectDescriptor *)desc, trace, indent, XMTDump); + case GF_ODF_OD_TAG: + return gf_odf_dump_od((GF_ObjectDescriptor *)desc, trace, indent, XMTDump); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_dump_oci_date((GF_OCI_Data *)desc, trace, indent, XMTDump); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_dump_oci_name((GF_OCICreators *)desc, trace, indent, XMTDump); + case GF_ODF_PL_IDX_TAG: + return gf_odf_dump_pl_idx((GF_PL_IDX *)desc, trace, indent, XMTDump); + case GF_ODF_QOS_TAG: + return gf_odf_dump_qos((GF_QoS_Descriptor *)desc, trace, indent, XMTDump); + case GF_ODF_RATING_TAG: + return gf_odf_dump_rating((GF_Rating *)desc, trace, indent, XMTDump); + case GF_ODF_REG_TAG: + return gf_odf_dump_reg((GF_Registration *)desc, trace, indent, XMTDump); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_dump_short_text((GF_ShortTextual *)desc, trace, indent, XMTDump); + case GF_ODF_SMPTE_TAG: + return gf_odf_dump_smpte_camera((GF_SMPTECamera *)desc, trace, indent, XMTDump); + case GF_ODF_SCI_TAG: + return gf_odf_dump_sup_cid((GF_SCIDesc *)desc, trace, indent, XMTDump); + case GF_ODF_SEGMENT_TAG: + return gf_odf_dump_segment((GF_Segment *)desc, trace, indent, XMTDump); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_dump_mediatime((GF_MediaTime *)desc, trace, indent, XMTDump); + case GF_ODF_TEXT_CFG_TAG: + return gf_odf_dump_txtcfg((GF_TextConfig *)desc, trace, indent, XMTDump); + case GF_ODF_MUXINFO_TAG: + return gf_odf_dump_muxinfo((GF_MuxInfo *)desc, trace, indent, XMTDump); + case GF_ODF_BIFS_CFG_TAG: + return gf_odf_dump_bifs_cfg((GF_BIFSConfig *)desc, trace, indent, XMTDump); + case GF_ODF_LASER_CFG_TAG: + return gf_odf_dump_laser_cfg((GF_LASERConfig *)desc, trace, indent, XMTDump); + case GF_ODF_UI_CFG_TAG: + return gf_odf_dump_ui_cfg((GF_UIConfig *)desc, trace, indent, XMTDump); + case GF_ODF_IPMP_TL_TAG: + return gf_odf_dump_ipmp_tool_list((GF_IPMP_ToolList*)desc, trace, indent, XMTDump); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_dump_ipmp_tool((GF_IPMP_Tool*)desc, trace, indent, XMTDump); + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_dump_aux_vid((GF_AuxVideoDescriptor *)desc, trace, indent, XMTDump); + default: + return gf_odf_dump_default((GF_DefaultDescriptor *)desc, trace, indent, XMTDump); + } + return GF_OK; +} + + + +static void StartDescDump(FILE *trace, const char *descName, u32 indent, Bool XMTDump) +{ + char ind_buf[OD_MAX_TREE]; + OD_FORMAT_INDENT(ind_buf, indent); + + if (!XMTDump) { + fprintf(trace, "%s {\n", descName); + } else { + fprintf(trace, "%s<%s ", ind_buf, descName); + } +} + +static void EndDescDump(FILE *trace, const char *descName, u32 indent, Bool XMTDump) +{ + char ind_buf[OD_MAX_TREE]; + OD_FORMAT_INDENT(ind_buf, indent); + + if (!XMTDump) { + fprintf(trace, "%s}\n", ind_buf); + } else { + fprintf(trace, "%s\n", ind_buf, descName); + } +} + +/*special element open for XML only, appends "" - used because XMT-A OD representations use lots of +subdescs not present in BT*/ +static void EndSubElement(FILE *trace, u32 indent, Bool XMTDump) +{ + if (XMTDump) fprintf(trace, "/>\n"); +} + +static void EndAttributes(FILE *trace, u32 indent, Bool XMTDump) +{ + if (XMTDump) fprintf(trace, ">\n"); +} + +static void StartElement(FILE *trace, const char *attName, u32 indent, Bool XMTDump, Bool IsList) +{ + char ind_buf[OD_MAX_TREE]; + OD_FORMAT_INDENT(ind_buf, indent); + if (!XMTDump) { + if (IsList) + fprintf(trace, "%s%s [\n", ind_buf, attName); + else + fprintf(trace, "%s%s ", ind_buf, attName); + } else { + fprintf(trace, "%s<%s>\n", ind_buf, attName); + } +} + +static void EndElement(FILE *trace, const char *attName, u32 indent, Bool XMTDump, Bool IsList) +{ + char ind_buf[OD_MAX_TREE]; + OD_FORMAT_INDENT(ind_buf, indent); + if (!XMTDump) { + if (IsList) fprintf(trace, "%s]\n", ind_buf); + } else { + fprintf(trace, "%s\n", ind_buf, attName); + } +} + +static void StartAttribute(FILE *trace, const char *attName, u32 indent, Bool XMTDump) +{ + char ind_buf[OD_MAX_TREE]; + OD_FORMAT_INDENT(ind_buf, indent); + if (!XMTDump) { + fprintf(trace, "%s%s ", ind_buf, attName); + } else { + fprintf(trace, "%s=\"", attName); + } +} +static void EndAttribute(FILE *trace, u32 indent, Bool XMTDump) +{ + if (!XMTDump) { + fprintf(trace, "\n"); + } else { + fprintf(trace, "\" "); + } +} + +static void DumpInt(FILE *trace, const char *attName, u32 val, u32 indent, Bool XMTDump) +{ + if (!val) return; + StartAttribute(trace, attName, indent, XMTDump); + fprintf(trace, "%d", val); + EndAttribute(trace, indent, XMTDump); +} + +static void DumpIntHex(FILE *trace, const char *attName, u32 val, u32 indent, Bool XMTDump, Bool single_byte) +{ + StartAttribute(trace, attName, indent, XMTDump); + if (single_byte) { + fprintf(trace, "0x%02X", val); + } else { + fprintf(trace, "0x%08X", val); + } + EndAttribute(trace, indent, XMTDump); +} + +static void DumpFloat(FILE *trace, const char *attName, Float val, u32 indent, Bool XMTDump) +{ + StartAttribute(trace, attName, indent, XMTDump); + fprintf(trace, "%g", val); + EndAttribute(trace, indent, XMTDump); +} + +static void DumpDouble(FILE *trace, const char *attName, Double val, u32 indent, Bool XMTDump) +{ + StartAttribute(trace, attName, indent, XMTDump); + fprintf(trace, "%g", val); + EndAttribute(trace, indent, XMTDump); +} + +static void DumpBool(FILE *trace, const char *attName, u32 val, u32 indent, Bool XMTDump) +{ + if (!val) return; + + StartAttribute(trace, attName, indent, XMTDump); + fprintf(trace, "%s", val ? "true" : "false"); + EndAttribute(trace, indent, XMTDump); +} + +static void DumpString(FILE *trace, const char *attName, char *val, u32 indent, Bool XMTDump) +{ + if (!val) return; + StartAttribute(trace, attName, indent, XMTDump); + if (!XMTDump) fprintf(trace, "\""); + fprintf(trace, "%s", val); + if (!XMTDump) fprintf(trace, "\""); + EndAttribute(trace, indent, XMTDump); +} + +static void DumpData(FILE *trace, const char *name, char *data, u32 dataLength, u32 indent, Bool XMTDump) +{ + u32 i; + if (!name ||!data) return; + StartAttribute(trace, name, indent, XMTDump); + if (XMTDump) fprintf(trace, "data:application/octet-string,"); + for (i=0; itag == tag_only) num_desc++; + } + if (!num_desc) return GF_OK; + + StartElement(trace, ListName, indent, XMTDump, 1); + indent++; + OD_FORMAT_INDENT(ind_buf, indent); + for (i=0; itag == tag_only) { + //add offset if not XMT + if (!XMTDump) fprintf(trace, "%s", ind_buf); + gf_odf_dump_desc(desc, trace, indent, XMTDump); + } + } + indent--; + EndElement(trace, ListName, indent, XMTDump, 1); + return GF_OK; +} + +GF_Err gf_odf_dump_iod(GF_InitialObjectDescriptor *iod, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "InitialObjectDescriptor", indent, XMTDump); + indent++; + + StartAttribute(trace, "objectDescriptorID", indent, XMTDump); + if (XMTDump) { + fprintf(trace, "od%d", iod->objectDescriptorID); + EndAttribute(trace, indent, XMTDump); + DumpInt(trace, "binaryID", iod->objectDescriptorID, indent, XMTDump); + } else { + fprintf(trace, "%d", iod->objectDescriptorID); + EndAttribute(trace, indent, XMTDump); + } + + EndAttributes(trace, indent, XMTDump); + + StartSubElement(trace, "Profiles", indent, XMTDump); + + DumpInt(trace, "audioProfileLevelIndication", iod->audio_profileAndLevel, indent, XMTDump); + DumpInt(trace, "visualProfileLevelIndication", iod->visual_profileAndLevel, indent, XMTDump); + DumpInt(trace, "sceneProfileLevelIndication", iod->scene_profileAndLevel, indent, XMTDump); + DumpInt(trace, "graphicsProfileLevelIndication", iod->graphics_profileAndLevel, indent, XMTDump); + DumpInt(trace, "ODProfileLevelIndication", iod->OD_profileAndLevel, indent, XMTDump); + DumpBool(trace, "includeInlineProfileLevelFlag", iod->inlineProfileFlag, indent, XMTDump); + + EndSubElement(trace, indent, XMTDump); + + if (iod->URLString) { + StartSubElement(trace, "URL", indent, XMTDump); + DumpString(trace, "URLstring", iod->URLString, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + + if (XMTDump) { + StartElement(trace, "Descr", indent, XMTDump, 1); + indent++; + } + //ESDescr + DumpDescList(iod->ESDescriptors, trace, indent, "esDescr", XMTDump, 0); + DumpDescList(iod->OCIDescriptors, trace, indent, "ociDescr", XMTDump, 0); + DumpDescListFilter(iod->IPMP_Descriptors, trace, indent, "ipmpDescrPtr", XMTDump, GF_ODF_IPMP_PTR_TAG); + DumpDescListFilter(iod->IPMP_Descriptors, trace, indent, "ipmpDescr", XMTDump, GF_ODF_IPMP_TAG); + + DumpDescList(iod->extensionDescriptors, trace, indent, "extDescr", XMTDump, 0); + + if (iod->IPMPToolList) { + StartElement(trace, "toolListDescr" , indent, XMTDump, 0); + gf_odf_dump_desc(iod->IPMPToolList, trace, indent + (XMTDump ? 1 : 0), XMTDump); + EndElement(trace, "toolListDescr" , indent, XMTDump, 0); + } + + if (XMTDump) { + indent--; + EndElement(trace, "Descr", indent, XMTDump, 1); + } + indent--; + EndDescDump(trace, "InitialObjectDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_esd(GF_ESD *esd, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_MuxInfo *mi; + u32 i; + StartDescDump(trace, "ES_Descriptor", indent, XMTDump); + indent++; + + StartAttribute(trace, "ES_ID", indent, XMTDump); + if (XMTDump) { + fprintf(trace, "es%d", esd->ESID); + EndAttribute(trace, indent, XMTDump); + DumpInt(trace, "binaryID", esd->ESID, indent, XMTDump); + } else { + fprintf(trace, "%d", esd->ESID); + EndAttribute(trace, indent, XMTDump); + } + DumpInt(trace, "streamPriority", esd->streamPriority, indent, XMTDump); + + if (XMTDump) { + if (esd->dependsOnESID) { + StartAttribute(trace, "dependsOn_ES_ID", indent, XMTDump); + fprintf(trace, "es%d", esd->dependsOnESID); + EndAttribute(trace, indent, XMTDump); + } + + if (esd->OCRESID) { + StartAttribute(trace, "OCR_ES_ID", indent, XMTDump); + fprintf(trace, "es%d", esd->OCRESID); + EndAttribute(trace, indent, XMTDump); + } + } else { + if (esd->dependsOnESID) DumpInt(trace, "dependsOn_ES_ID", esd->dependsOnESID, indent, XMTDump); + if (esd->OCRESID) DumpInt(trace, "OCR_ES_ID", esd->OCRESID, indent, XMTDump); + } + + EndAttributes(trace, indent, XMTDump); + + if (esd->URLString) { + StartSubElement(trace, "URL", indent, XMTDump); + DumpString(trace, "URLstring", esd->URLString, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + if (esd->decoderConfig) { + StartElement(trace, "decConfigDescr" , indent, XMTDump, 0); + gf_odf_dump_desc(esd->decoderConfig, trace, indent + (XMTDump ? 1 : 0), XMTDump); + EndElement(trace, "decConfigDescr" , indent, XMTDump, 0); + } + if (esd->slConfig) { + StartElement(trace, "slConfigDescr" , indent, XMTDump, 0); + gf_odf_dump_desc(esd->slConfig, trace, indent + (XMTDump ? 1 : 0), XMTDump); + EndElement(trace, "slConfigDescr" , indent, XMTDump, 0); + } + if (esd->ipiPtr) { + StartElement(trace, "ipiPtr" , indent, XMTDump, 0); + gf_odf_dump_desc(esd->ipiPtr, trace, indent + (XMTDump ? 1 : 0), XMTDump); + EndElement(trace, "ipiPtr" , indent, XMTDump, 0); + } + + DumpDescList(esd->IPIDataSet, trace, indent, "ipIDS", XMTDump, 0); + DumpDescList(esd->IPMPDescriptorPointers, trace, indent, "ipmpDescrPtr", XMTDump, 0); + + if (esd->qos) { + StartElement(trace, "qosDescr" , indent, XMTDump, 0); + gf_odf_dump_desc(esd->qos, trace, indent + (XMTDump ? 1 : 0), XMTDump); + EndElement(trace, "qosDescr" , indent, XMTDump, 0); + } + if (esd->langDesc) { + StartElement(trace, "langDescr" , indent, XMTDump, 0); + gf_odf_dump_desc(esd->langDesc, trace, indent + (XMTDump ? 1 : 0), XMTDump); + EndElement(trace, "langDescr" , indent, XMTDump, 0); + } + + if (esd->RegDescriptor) { + StartElement(trace, "regDescr" , indent, XMTDump, 0); + gf_odf_dump_desc(esd->RegDescriptor, trace, indent + (XMTDump ? 1 : 0), XMTDump); + EndElement(trace, "regDescr" , indent, XMTDump, 0); + } + + mi = NULL; + i=0; + while ((mi = (GF_MuxInfo *)gf_list_enum(esd->extensionDescriptors, &i))) { + if (mi->tag == GF_ODF_MUXINFO_TAG) { + gf_list_rem(esd->extensionDescriptors, i-1); + break; + } + mi = NULL; + } + + DumpDescList(esd->extensionDescriptors, trace, indent, "extDescr", XMTDump, 0); + + if (mi) { + gf_list_insert(esd->extensionDescriptors, mi, i); + if (XMTDump) { + gf_odf_dump_desc(mi, trace, indent, 1); + } else { + StartElement(trace, "muxInfo" , indent, 0, 0); + gf_odf_dump_desc(mi, trace, indent, 0); + EndElement(trace, "muxInfo" , indent, 0, 0); + } + } + + indent--; + EndDescDump(trace, "ES_Descriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_bifs_cfg(GF_BIFSConfig *dsi, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i, count; + StartDescDump(trace, (dsi->version==1) ? "BIFSConfig" : "BIFSv2Config", indent, XMTDump); + indent++; + + if (dsi->version==2) { + DumpBool(trace, "use3DMeshCoding", 0, indent, XMTDump); + DumpBool(trace, "usePredictiveMFField", 0, indent, XMTDump); + } + DumpInt(trace, "nodeIDbits", dsi->nodeIDbits, indent, XMTDump); + DumpInt(trace, "routeIDbits", dsi->routeIDbits, indent, XMTDump); + if (dsi->version==2) DumpInt(trace, "protoIDbits", dsi->protoIDbits, indent, XMTDump); + + count = gf_list_count(dsi->elementaryMasks); + if (count) { + EndAttributes(trace, indent, XMTDump); + + /*TODO check XMT-A syntax for anim mask*/ + StartDescDump(trace, "AnimationMask" , indent, XMTDump); + DumpBool(trace, "randomAccess", dsi->randomAccess, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + for (i=0; ielementaryMasks, i); + StartDescDump(trace, "ElementaryMask" , indent, XMTDump); + if (em->node_id) DumpInt(trace, "atNode", em->node_id, indent, XMTDump); + else if (em->node_name) DumpString(trace, "atNode", em->node_name, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + if (XMTDump) EndDescDump(trace, "ElementaryMask", indent, 1); + else EndDescDump(trace, "ElementaryMask", indent, 0); + } + EndDescDump(trace, "AnimationMask", indent, XMTDump); + indent--; + EndDescDump(trace, (dsi->version==1) ? "BIFSConfig" : "BIFSv2Config", indent, XMTDump); + return GF_OK; + } + if (XMTDump) { + EndAttributes(trace, indent, XMTDump); + indent++; + StartDescDump(trace, "commandStream" , indent, XMTDump); + DumpBool(trace, "pixelMetric", dsi->pixelMetrics, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + } else { + DumpBool(trace, "isCommandStream", 1, indent, XMTDump); + DumpBool(trace, "pixelMetric", dsi->pixelMetrics, indent, XMTDump); + } + if (dsi->pixelWidth && dsi->pixelHeight) { + if (XMTDump) { + indent++; + StartDescDump(trace, "size" , indent, XMTDump); + } + + DumpInt(trace, "pixelWidth", dsi->pixelWidth, indent, XMTDump); + DumpInt(trace, "pixelHeight", dsi->pixelHeight, indent, XMTDump); + if (XMTDump) { + EndSubElement(trace, indent, XMTDump); + indent--; + } + } + + if (XMTDump) { + EndDescDump(trace, "commandStream", indent, XMTDump); + indent--; + } + indent--; + EndDescDump(trace, (dsi->version==1) ? "BIFSConfig" : "BIFSv2Config", indent, XMTDump); + return GF_OK; +} + +GF_Err DumpRawBIFSConfig(GF_DefaultDescriptor *dsi, FILE *trace, u32 indent, Bool XMTDump, u32 oti) +{ + GF_BitStream *bs; + u32 flag; + + bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); + + StartDescDump(trace, (oti==1) ? "BIFSConfig" : "BIFSv2Config", indent, XMTDump); + indent++; + + if (oti==2) { + DumpBool(trace, "use3DMeshCoding", gf_bs_read_int(bs, 1), indent, XMTDump); + DumpBool(trace, "usePredictiveMFField", gf_bs_read_int(bs, 1), indent, XMTDump); + } + DumpInt(trace, "nodeIDbits", gf_bs_read_int(bs, 5), indent, XMTDump); + DumpInt(trace, "routeIDbits", gf_bs_read_int(bs, 5), indent, XMTDump); + + if (oti==2) + DumpInt(trace, "protoIDbits", gf_bs_read_int(bs, 5), indent, XMTDump); + + flag = gf_bs_read_int(bs, 1); + if (!flag) { + gf_bs_del(bs); + return GF_NOT_SUPPORTED; + } + + if (XMTDump) { + EndAttributes(trace, indent, XMTDump); + indent++; + StartDescDump(trace, "commandStream" , indent, XMTDump); + DumpBool(trace, "pixelMetric", gf_bs_read_int(bs, 1), indent, XMTDump); + if (XMTDump) EndAttributes(trace, indent, XMTDump); + } else { + DumpBool(trace, "isCommandStream", 1, indent, XMTDump); + DumpBool(trace, "pixelMetric", gf_bs_read_int(bs, 1), indent, XMTDump); + } + + if (gf_bs_read_int(bs, 1)) { + + if (XMTDump) { + indent++; + StartDescDump(trace, "size" , indent, XMTDump); + } + + DumpInt(trace, "pixelWidth", gf_bs_read_int(bs, 16), indent, XMTDump); + DumpInt(trace, "pixelHeight", gf_bs_read_int(bs, 16), indent, XMTDump); + if (XMTDump) { + EndSubElement(trace, indent, XMTDump); + indent--; + } + + } + + if (XMTDump) { + EndDescDump(trace, "commandStream", indent, XMTDump); + indent--; + } + indent--; + EndDescDump(trace, (oti==1) ? "BIFSConfig" : "BIFSv2Config", indent, XMTDump); + + gf_bs_del(bs); + return GF_OK; +} + +GF_Err gf_odf_dump_laser_cfg(GF_LASERConfig *dsi, FILE *trace, u32 indent, Bool XMTDump) +{ + fprintf(trace, "profile ? "mini" : "full", + dsi->pointsCodec ? "Unknown" : "ExpGolombPointsCodec"); + + if (dsi->colorComponentBits) fprintf(trace, " colorComponentBits=\"%d\"", dsi->colorComponentBits); + if (dsi->newSceneIndicator) fprintf(trace, " newSceneIndicator=\"true\""); + if (dsi->coord_bits) fprintf(trace, " coordBits=\"%d\"", dsi->coord_bits); + if (dsi->fullRequestHost) fprintf(trace, " useFullRequestHost=\"true\""); + if (dsi->pathComponents) fprintf(trace, " pathComponents=\"%d\"", dsi->pathComponents); + if (dsi->time_resolution && (dsi->time_resolution!=1000) ) fprintf(trace, " timeResolution=\"%d\"", dsi->time_resolution); + if (dsi->resolution) fprintf(trace, " resolution=\"%d\"", dsi->resolution); + if (dsi->scale_bits_minus_coord_bits) fprintf(trace, " scaleBits_minus_coordBits=\"%d\"", dsi->scale_bits_minus_coord_bits); + fprintf(trace, "/>\n"); + return GF_OK; +} + + + +GF_Err gf_odf_dump_txtcfg(GF_TextConfig *desc, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i, count; + char ind_buf[OD_MAX_TREE]; + StartDescDump(trace, "TextConfig", indent, XMTDump); + indent++; + DumpIntHex(trace, "3GPPBaseFormat", desc->Base3GPPFormat, indent, XMTDump, 1); + DumpIntHex(trace, "MPEGExtendedFormat", desc->MPEGExtendedFormat, indent, XMTDump, 1); + DumpIntHex(trace, "profileLevel", desc->profileLevel, indent, XMTDump, 1); + DumpInt(trace, "durationClock", desc->timescale, indent, XMTDump); + DumpInt(trace, "layer", desc->layer, indent, XMTDump); + DumpInt(trace, "text_width", desc->text_width, indent, XMTDump); + DumpInt(trace, "text_height", desc->text_height, indent, XMTDump); + if (desc->video_width) DumpInt(trace, "video_width", desc->video_width, indent, XMTDump); + if (desc->video_height) DumpInt(trace, "video_height", desc->video_height, indent, XMTDump); + if (desc->horiz_offset) DumpInt(trace, "horizontal_offset", desc->horiz_offset, indent, XMTDump); + if (desc->vert_offset) DumpInt(trace, "vertical_offset", desc->vert_offset, indent, XMTDump); + + StartElement(trace, "SampleDescriptions", indent, XMTDump, 1); + indent++; + OD_FORMAT_INDENT(ind_buf, indent); + + count = gf_list_count(desc->sample_descriptions); + for (i=0; isample_descriptions, i); + if (!XMTDump) fprintf(trace, "%s", ind_buf); + StartDescDump(trace, "TextSampleDescriptor", indent, XMTDump); + indent++; + DumpIntHex(trace, "displayFlags", sd->displayFlags, indent, XMTDump, 0); + DumpInt(trace, "horiz_justif", sd->horiz_justif, indent, XMTDump); + DumpInt(trace, "vert_justif", sd->vert_justif, indent, XMTDump); + DumpIntHex(trace, "back_color", sd->back_color, indent, XMTDump, 0); + DumpInt(trace, "top", sd->default_pos.top, indent, XMTDump); + DumpInt(trace, "bottom", sd->default_pos.bottom, indent, XMTDump); + DumpInt(trace, "left", sd->default_pos.left, indent, XMTDump); + DumpInt(trace, "right", sd->default_pos.right, indent, XMTDump); + DumpInt(trace, "style_font_ID", sd->default_style.fontID, indent, XMTDump); + DumpInt(trace, "style_font_size", sd->default_style.font_size, indent, XMTDump); + DumpIntHex(trace, "style_text_color", sd->default_style.text_color, indent, XMTDump, 0); + strcpy(szStyles, ""); + if (sd->default_style.style_flags & GF_TXT_STYLE_BOLD) strcat(szStyles, "bold "); + if (sd->default_style.style_flags & GF_TXT_STYLE_ITALIC) strcat(szStyles, "italic "); + if (sd->default_style.style_flags & GF_TXT_STYLE_UNDERLINED) strcat(szStyles, "underlined "); + if (strlen(szStyles)) DumpString(trace, "style_flag", szStyles, indent, XMTDump); + + for (j=0; jfont_count; j++) { + DumpInt(trace, "fontID", sd->fonts[j].fontID, indent, XMTDump); + DumpString(trace, "fontName", sd->fonts[i].fontName, indent, XMTDump); + } + + indent--; + EndDescDump(trace, "TextSampleDescriptor", indent, XMTDump); + } + + indent--; + EndElement(trace, "SampleDescriptions", indent, XMTDump, 1); + + indent--; + EndDescDump(trace, "TextConfig", indent, XMTDump); + return GF_OK; +} + +GF_Err DumpRawTextConfig(GF_DefaultDescriptor *dsi, FILE *trace, u32 indent, Bool XMTDump, u32 oti) +{ + GF_TextConfig *cfg = (GF_TextConfig *) gf_odf_desc_new(GF_ODF_TEXT_CFG_TAG); + GF_Err e = gf_odf_get_text_config(dsi, (u8) oti, cfg); + if (!e) gf_odf_dump_desc(cfg, trace, indent, XMTDump); + gf_odf_desc_del((GF_Descriptor *) cfg); + return e; +} + + + +GF_Err gf_odf_dump_ui_cfg(GF_UIConfig *uid, FILE *trace, u32 indent, Bool XMTDump) +{ + char devName[255]; + u32 i; + + StartDescDump(trace, "UIConfig" , indent, XMTDump); + indent++; + DumpString(trace, "deviceName", uid->deviceName, indent, XMTDump); + + if (!stricmp(devName, "StringSensor") && uid->termChar) { + devName[0] = uid->termChar; + devName[1] = 0; + DumpString(trace, "termChar", devName, indent, XMTDump); + devName[0] = uid->delChar; + DumpString(trace, "delChar", devName, indent, XMTDump); + } + if (uid->ui_data_length) { + if (!stricmp(uid->deviceName, "HTKSensor")) { + u32 nb_word, nbPhone, c, j; + GF_BitStream *bs = gf_bs_new(uid->ui_data, uid->ui_data_length, GF_BITSTREAM_READ); + char szPh[3]; + StartAttribute(trace, "uiData", indent, XMTDump); + if (!XMTDump) fprintf(trace, "\""); + fprintf(trace, "HTK:"); + szPh[2] = 0; + nb_word = gf_bs_read_int(bs, 8); + for (i=0; iui_data, uid->ui_data_length, indent, XMTDump); + } + } + + indent--; + EndAttributes(trace, indent, XMTDump); + EndDescDump(trace, "UIConfig", indent, XMTDump); + return GF_OK; +} + +GF_Err DumpRawUIConfig(GF_DefaultDescriptor *dsi, FILE *trace, u32 indent, Bool XMTDump, u32 oti) +{ + char devName[255]; + u32 i, len; + GF_BitStream *bs; + + bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); + + StartDescDump(trace, "UIConfig" , indent, XMTDump); + indent++; + len = gf_bs_read_int(bs, 8); + for (i=0; idata; + data += (u32) gf_bs_get_position(bs); + DumpData(trace, "uiData", data, len, indent, XMTDump); + } + } + + indent--; + EndAttributes(trace, indent, XMTDump); + EndDescDump(trace, "UIConfig", indent, XMTDump); + gf_bs_del(bs); + return GF_OK; +} + +GF_Err OD_DumpDSI(GF_DefaultDescriptor *dsi, FILE *trace, u32 indent, Bool XMTDump, u32 streamType, u32 oti) +{ + switch (streamType) { + case GF_STREAM_SCENE: + if (oti<=2) return DumpRawBIFSConfig(dsi, trace, indent, XMTDump, oti); + break; + case GF_STREAM_INTERACT: + return DumpRawUIConfig(dsi, trace, indent, XMTDump, oti); + case GF_STREAM_TEXT: + if (oti==0x08) return DumpRawTextConfig(dsi, trace, indent, XMTDump, oti); + break; + default: + break; + } + return gf_odf_dump_desc(dsi, trace, indent, XMTDump); +} + +GF_Err gf_odf_dump_dcd(GF_DecoderConfig *dcd, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "DecoderConfigDescriptor", indent, XMTDump); + indent++; + + DumpInt(trace, "objectTypeIndication", dcd->objectTypeIndication, indent, XMTDump); + DumpInt(trace, "streamType", dcd->streamType, indent, XMTDump); + DumpInt(trace, "upStream", dcd->upstream, indent, XMTDump); + DumpInt(trace, "bufferSizeDB", dcd->bufferSizeDB, indent, XMTDump); + DumpInt(trace, "maxBitrate", dcd->maxBitrate, indent, XMTDump); + DumpInt(trace, "avgBitrate", dcd->avgBitrate, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + + if (dcd->decoderSpecificInfo) { + if (dcd->decoderSpecificInfo->tag==GF_ODF_DSI_TAG) { + if (dcd->decoderSpecificInfo->dataLength) { + StartElement(trace, "decSpecificInfo" , indent, XMTDump, 0); + OD_DumpDSI(dcd->decoderSpecificInfo, trace, indent + (XMTDump ? 1 : 0), XMTDump, dcd->streamType, dcd->objectTypeIndication); + EndElement(trace, "decSpecificInfo" , indent, XMTDump, 0); + } + } else { + StartElement(trace, "decSpecificInfo" , indent, XMTDump, 0); + gf_odf_dump_desc(dcd->decoderSpecificInfo, trace, indent + (XMTDump ? 1 : 0), XMTDump); + EndElement(trace, "decSpecificInfo" , indent, XMTDump, 0); + } + } + DumpDescList(dcd->profileLevelIndicationIndexDescriptor, trace, indent, "profileLevelIndicationIndexDescr", XMTDump, 0); + indent--; + EndDescDump(trace, "DecoderConfigDescriptor", indent, XMTDump); + + return GF_OK; +} + +GF_Err gf_odf_dump_slc(GF_SLConfig *sl, FILE *trace, u32 indent, Bool XMTDump) +{ + + StartDescDump(trace, "SLConfigDescriptor", indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + indent++; + + if (sl->predefined) { + StartSubElement(trace, "predefined" , indent, XMTDump); + DumpInt(trace, XMTDump ? "value" : "predefined", sl->predefined, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + if (XMTDump) StartSubElement(trace, "custom" , indent, XMTDump); + + if (!sl->predefined) { + DumpBool(trace, "useAccessUnitStartFlag", sl->useAccessUnitStartFlag, indent, XMTDump); + DumpBool(trace, "useAccessUnitEndFlag", sl->useAccessUnitEndFlag, indent, XMTDump); + DumpBool(trace, "useRandomAccessPointFlag", sl->useRandomAccessPointFlag, indent, XMTDump); + DumpBool(trace, "usePaddingFlag", sl->usePaddingFlag, indent, XMTDump); + if (!XMTDump) DumpBool(trace, "useTimeStampsFlag", sl->useTimestampsFlag, indent, XMTDump); + DumpBool(trace, "useIdleFlag", sl->useIdleFlag, indent, XMTDump); + if (!XMTDump) DumpBool(trace, "durationFlag", sl->durationFlag, indent, XMTDump); + DumpInt(trace, "timeStampResolution", sl->timestampResolution, indent, XMTDump); + DumpInt(trace, "OCRResolution", sl->OCRResolution, indent, XMTDump); + DumpInt(trace, "timeStampLength", sl->timestampLength, indent, XMTDump); + DumpInt(trace, "OCRLength", sl->OCRLength, indent, XMTDump); + DumpInt(trace, "AU_Length", sl->AULength, indent, XMTDump); + DumpInt(trace, "instantBitrateLength", sl->instantBitrateLength, indent, XMTDump); + DumpInt(trace, "degradationPriorityLength", sl->degradationPriorityLength, indent, XMTDump); + DumpInt(trace, "AU_SeqNumLength", sl->AUSeqNumLength, indent, XMTDump); + DumpInt(trace, "packetSeqNumLength", sl->packetSeqNumLength, indent, XMTDump); + } + EndAttributes(trace, indent, XMTDump); + + indent++; + if (sl->durationFlag) { + StartSubElement(trace, "Duration" , indent, XMTDump); + DumpInt(trace, "timescale", sl->timeScale, indent, XMTDump); + DumpInt(trace, "accessUnitDuration", sl->AUDuration, indent, XMTDump); + DumpInt(trace, "compositionUnitDuration", sl->CUDuration, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + if (! sl->useTimestampsFlag) { + StartSubElement(trace, "noUseTimeStamps" , indent, XMTDump); + DumpInt(trace, "startDecodingTimeStamp", (u32) sl->startDTS, indent, XMTDump); + DumpInt(trace, "startCompositionTimeStamp", (u32) sl->startCTS, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + indent--; + if (XMTDump) EndElement(trace, "custom" , indent, XMTDump, 1); + + indent--; + EndDescDump(trace, "SLConfigDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_cc(GF_CCDescriptor *ccd, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "ContentClassificationDescriptor", indent, XMTDump); + DumpInt(trace, "classificationEntity", ccd->classificationEntity, indent, XMTDump); + DumpInt(trace, "classificationTable", ccd->classificationTable, indent, XMTDump); + DumpData(trace, "ccd->contentClassificationData", ccd->contentClassificationData, ccd->dataLength, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + EndDescDump(trace, "ContentClassificationDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_cc_date(GF_CC_Date *cdd, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "ContentClassificationDescriptor", indent, XMTDump); + DumpString(trace, "creationDate", cdd->contentCreationDate, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + EndDescDump(trace, "ContentClassificationDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_cc_name(GF_CC_Name *cnd, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i; + GF_ContentCreatorInfo *p; + + StartDescDump(trace, "ContentCreatorNameDescriptor", indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + indent++; + i=0; + while ((p = (GF_ContentCreatorInfo *)gf_list_enum(cnd->ContentCreators, &i))) { + StartSubElement(trace, "Creator", indent, XMTDump); + DumpInt(trace, "languageCode", p->langCode, indent, XMTDump); + DumpBool(trace, "isUTF8", p->isUTF8, indent, XMTDump); + DumpString(trace, "Name", p->contentCreatorName, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + indent--; + EndDescDump(trace, "ContentCreatorNameDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_ci(GF_CIDesc *cid, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "ContentIdentificationDescriptor", indent, XMTDump); + DumpBool(trace, "protectedContent", cid->protectedContent, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + indent++; + if (cid->contentTypeFlag) { + StartSubElement(trace, "contentType", indent, XMTDump); + DumpInt(trace, "contentType", cid->contentType, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + if (cid->contentIdentifierFlag) { + StartSubElement(trace, "contentIdentifierType", indent, XMTDump); + DumpInt(trace, "contentIdentifierType", cid->contentIdentifierType, indent, XMTDump); + DumpString(trace, "contentIdentifier", cid->contentIdentifier, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + + indent--; + EndDescDump(trace, "ContentIdentificationDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_default(GF_DefaultDescriptor *dd, FILE *trace, u32 indent, Bool XMTDump) +{ + if (dd->tag == GF_ODF_DSI_TAG) { + StartDescDump(trace, "DecoderSpecificInfo", indent, XMTDump); + indent++; + if (XMTDump) { + DumpString(trace, "type", "auto", indent, XMTDump); + DumpData(trace, "src", dd->data, dd->dataLength, indent, XMTDump); + } else { + DumpData(trace, "info", dd->data, dd->dataLength, indent, XMTDump); + } + indent--; + if (XMTDump) { + EndSubElement(trace, indent, 1); + } else { + EndDescDump(trace, "", indent, 0); + } + } else { + StartDescDump(trace, "DefaultDescriptor", indent, XMTDump); + indent++; + DumpData(trace, "data", dd->data, dd->dataLength, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + } + return GF_OK; +} + +GF_Err gf_odf_dump_esd_inc(GF_ES_ID_Inc *esd_inc, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "ES_ID_Inc", indent, XMTDump); + indent++; + DumpInt(trace, "trackID", esd_inc->trackID, indent, XMTDump); + indent--; + EndAttributes(trace, indent, XMTDump); + EndDescDump(trace, "ES_ID_Inc", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_esd_ref(GF_ES_ID_Ref *esd_ref, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "ES_ID_Ref", indent, XMTDump); + indent++; + DumpInt(trace, "trackRef", esd_ref->trackRef, indent, XMTDump); + indent--; + EndAttributes(trace, indent, XMTDump); + EndDescDump(trace, "ES_ID_Ref", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_exp_text(GF_ExpandedTextual *etd, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_ETD_ItemText *it1, *it2; + u32 i, count; + + StartDescDump(trace, "ExpandedTextualDescriptor", indent, XMTDump); + indent++; + DumpInt(trace, "languageCode", etd->langCode, indent, XMTDump); + DumpBool(trace, "isUTF8", etd->isUTF8, indent, XMTDump); + DumpString(trace, "nonItemText", etd->NonItemText, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + + count = gf_list_count(etd->itemDescriptionList); + for (i=0; iitemDescriptionList, i); + it2 = (GF_ETD_ItemText *)gf_list_get(etd->itemTextList, i); + StartSubElement(trace, "item", indent, XMTDump); + DumpString(trace, "description", it1->text, indent, XMTDump); + DumpString(trace, "text", it2->text, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + indent--; + EndDescDump(trace, "ExpandedTextualDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_pl_ext(GF_PLExt *pld, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "ExtensionProfileLevelDescriptor", indent, XMTDump); + indent++; + DumpInt(trace, "profileLevelIndicationIndex", pld->profileLevelIndicationIndex, indent, XMTDump); + DumpInt(trace, "ODProfileLevelIndication", pld->ODProfileLevelIndication, indent, XMTDump); + DumpInt(trace, "sceneProfileLevelIndication", pld->SceneProfileLevelIndication, indent, XMTDump); + DumpInt(trace, "audioProfileLevelIndication", pld->AudioProfileLevelIndication, indent, XMTDump); + DumpInt(trace, "visualProfileLevelIndication", pld->VisualProfileLevelIndication, indent, XMTDump); + DumpInt(trace, "graphicsProfileLevelIndication", pld->GraphicsProfileLevelIndication, indent, XMTDump); + DumpInt(trace, "MPEGJProfileLevelIndication", pld->MPEGJProfileLevelIndication, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_ipi_ptr(GF_IPIPtr *ipid, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "IPI_DescrPointer", indent, XMTDump); + indent++; + DumpInt(trace, "IPI_ES_Id", ipid->IPI_ES_Id, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_ipmp(GF_IPMP_Descriptor *ipmp, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i, count; + StartDescDump(trace, "IPMP_Descriptor", indent, XMTDump); + indent++; + + DumpIntHex(trace, "IPMP_DescriptorID", ipmp->IPMP_DescriptorID, indent, XMTDump, 1); + DumpIntHex(trace, "IPMPS_Type", ipmp->IPMPS_Type, indent, XMTDump, 0); + + + if ((ipmp->IPMP_DescriptorID==0xFF) && (ipmp->IPMPS_Type==0xFFFF)) { + DumpIntHex(trace, "IPMP_DescriptorIDEx", ipmp->IPMP_DescriptorIDEx, indent, XMTDump, 0); + /*how the heck do we represent toolID??*/ + DumpBin128(trace, "IPMP_ToolID", (char*)ipmp->IPMP_ToolID, indent, XMTDump); + DumpInt(trace, "controlPointCode", ipmp->control_point, indent, XMTDump); + if (ipmp->control_point) DumpInt(trace, "sequenceCode", ipmp->cp_sequence_code, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + /*parse IPMPX data*/ + StartElement(trace, "IPMPX_Data", indent, XMTDump, 1); + indent++; + count = gf_list_count(ipmp->ipmpx_data); + for (i=0; iipmpx_data, i); + gf_ipmpx_dump_data(p, trace, indent, XMTDump); + } + indent--; + EndElement(trace, "IPMPX_Data", indent, XMTDump, 1); + } + else if (!ipmp->IPMPS_Type) { + DumpString(trace, "URLString", ipmp->opaque_data, indent, XMTDump); + } else { + DumpData(trace, "IPMP_data", ipmp->opaque_data, ipmp->opaque_data_size, indent, XMTDump); + } + indent--; + EndDescDump(trace, "IPMP_Descriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_ipmp_ptr(GF_IPMPPtr *ipmpd, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "IPMP_DescriptorPointer", indent, XMTDump); + indent++; + if (ipmpd->IPMP_DescriptorID == 0xFF) { + DumpInt(trace, "IPMP_DescriptorID", 0xFF, indent, XMTDump); + DumpInt(trace, "IPMP_DescriptorIDEx", ipmpd->IPMP_DescriptorIDEx, indent, XMTDump); + DumpInt(trace, "IPMP_ES_ID", ipmpd->IPMP_ES_ID, indent, XMTDump); + } else { + DumpInt(trace, "IPMP_DescriptorID", ipmpd->IPMP_DescriptorID, indent, XMTDump); + } + indent--; + if (XMTDump) + EndSubElement(trace, indent, XMTDump); + else + EndDescDump(trace, "IPMP_DescriptorPointer", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_kw(GF_KeyWord *kwd, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_KeyWordItem *p; + u32 i; + + StartDescDump(trace, "KeyWordDescriptor", indent, XMTDump); + indent++; + DumpInt(trace, "languageCode", kwd->languageCode, indent, XMTDump); + DumpBool(trace, "isUTF8", kwd->isUTF8, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + + i=0; + while ((p = (GF_KeyWordItem *)gf_list_enum(kwd->keyWordsList, &i))) { + StartSubElement(trace, "keyWord", indent, XMTDump); + DumpString(trace, "value", p->keyWord, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + indent--; + EndDescDump(trace, "KeyWordDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_lang(GF_Language *ld, FILE *trace, u32 indent, Bool XMTDump) +{ + char sLan[4]; + StartDescDump(trace, "LanguageDescriptor", indent, XMTDump); + indent++; + sLan[0] = (ld->langCode>>16)&0xFF; + sLan[1] = (ld->langCode>>8)&0xFF; + sLan[2] = (ld->langCode)&0xFF; + sLan[3] = 0; + DumpString(trace, "languageCode", sLan, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + if (!XMTDump) EndDescDump(trace, "LanguageDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_aux_vid(GF_AuxVideoDescriptor *ld, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "AuxiliaryVideoData", indent, XMTDump); + indent++; + + DumpIntHex(trace, "aux_video_type", ld->aux_video_type, indent, XMTDump, 1); + DumpInt(trace, "position_offset_h", ld->position_offset_h, indent, XMTDump); + DumpInt(trace, "position_offset_v", ld->position_offset_v, indent, XMTDump); + DumpInt(trace, "knear", ld->knear, indent, XMTDump); + DumpInt(trace, "kfar", ld->kfar, indent, XMTDump); + DumpInt(trace, "parallax_zero", ld->parallax_zero, indent, XMTDump); + DumpInt(trace, "parallax_scale", ld->parallax_scale, indent, XMTDump); + DumpInt(trace, "dref", ld->dref, indent, XMTDump); + DumpInt(trace, "wref", ld->wref, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + if (!XMTDump) EndDescDump(trace, "LanguageDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_isom_iod(GF_IsomInitialObjectDescriptor *iod, FILE *trace, u32 indent, Bool XMTDump) +{ + + + StartDescDump(trace, "MP4InitialObjectDescriptor", indent, XMTDump); + indent++; + + StartAttribute(trace, "objectDescriptorID", indent, XMTDump); + + + if (XMTDump) { + fprintf(trace, "od%d", iod->objectDescriptorID); + EndAttribute(trace, indent, XMTDump); + DumpInt(trace, "binaryID", iod->objectDescriptorID, indent, XMTDump); + } else { + fprintf(trace, "%d", iod->objectDescriptorID); + EndAttribute(trace, indent, XMTDump); + } + + EndAttributes(trace, indent, XMTDump); + + + StartSubElement(trace, "Profile", indent, 1); + + DumpInt(trace, "audioProfileLevelIndication", iod->audio_profileAndLevel, indent, XMTDump); + DumpInt(trace, "visualProfileLevelIndication", iod->visual_profileAndLevel, indent, XMTDump); + DumpInt(trace, "sceneProfileLevelIndication", iod->scene_profileAndLevel, indent, XMTDump); + DumpInt(trace, "graphicsProfileLevelIndication", iod->graphics_profileAndLevel, indent, XMTDump); + DumpInt(trace, "ODProfileLevelIndication", iod->OD_profileAndLevel, indent, XMTDump); + DumpBool(trace, "includeInlineProfileLevelFlag", iod->inlineProfileFlag, indent, XMTDump); + + EndSubElement(trace, indent, XMTDump); + + if (iod->URLString) { + StartSubElement(trace, "URL", indent, XMTDump); + DumpString(trace, "URLstring", iod->URLString, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + + if (XMTDump) { + StartElement(trace, "Descr", indent, XMTDump, 1); + indent++; + } + //ESDescr + if (gf_list_count(iod->ES_ID_IncDescriptors)) { + DumpDescList(iod->ES_ID_IncDescriptors, trace, indent, "esDescrInc", XMTDump, 0); + } else { + DumpDescList(iod->ES_ID_RefDescriptors, trace, indent, "esDescrRef", XMTDump, 0); + } + DumpDescList(iod->OCIDescriptors, trace, indent, "ociDescr", XMTDump, 0); + DumpDescListFilter(iod->IPMP_Descriptors, trace, indent, "ipmpDescrPtr", XMTDump, GF_ODF_IPMP_PTR_TAG); + DumpDescListFilter(iod->IPMP_Descriptors, trace, indent, "ipmpDescr", XMTDump, GF_ODF_IPMP_TAG); + + DumpDescList(iod->extensionDescriptors, trace, indent, "extDescr", XMTDump, 0); + + if (iod->IPMPToolList) { + StartElement(trace, "toolListDescr" , indent, XMTDump, 0); + gf_odf_dump_desc(iod->IPMPToolList, trace, indent + (XMTDump ? 1 : 0), XMTDump); + EndElement(trace, "toolListDescr" , indent, XMTDump, 0); + } + + if (XMTDump) { + indent--; + EndElement(trace, "Descr", indent, XMTDump, 1); + } + indent--; + EndDescDump(trace, "MP4InitialObjectDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_od(GF_ObjectDescriptor *od, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "ObjectDescriptor", indent, XMTDump); + indent++; + StartAttribute(trace, "objectDescriptorID", indent, XMTDump); + if (XMTDump) { + fprintf(trace, "od%d", od->objectDescriptorID); + EndAttribute(trace, indent, XMTDump); + DumpInt(trace, "binaryID", od->objectDescriptorID, indent, XMTDump); + } else { + fprintf(trace, "%d", od->objectDescriptorID); + EndAttribute(trace, indent, XMTDump); + } + EndAttributes(trace, indent, XMTDump); + if (od->URLString) { + StartSubElement(trace, "URL", indent, XMTDump); + DumpString(trace, "URLstring", od->URLString, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + + if (XMTDump) { + StartElement(trace, "Descr", indent, XMTDump, 1); + indent++; + } + //ESDescr + DumpDescList(od->ESDescriptors, trace, indent, "esDescr", XMTDump, 0); + DumpDescList(od->OCIDescriptors, trace, indent, "ociDescr", XMTDump, 0); + DumpDescListFilter(od->IPMP_Descriptors, trace, indent, "ipmpDescrPtr", XMTDump, GF_ODF_IPMP_PTR_TAG); + DumpDescListFilter(od->IPMP_Descriptors, trace, indent, "ipmpDescr", XMTDump, GF_ODF_IPMP_TAG); + + DumpDescList(od->extensionDescriptors, trace, indent, "extDescr", XMTDump, 0); + + if (XMTDump) { + indent--; + EndElement(trace, "Descr", indent, XMTDump, 1); + } + indent--; + EndDescDump(trace, "ObjectDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_isom_od(GF_IsomObjectDescriptor *od, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "MP4ObjectDescriptor", indent, XMTDump); + indent++; + StartAttribute(trace, "objectDescriptorID", indent, XMTDump); + if (XMTDump) { + fprintf(trace, "od%d", od->objectDescriptorID); + EndAttribute(trace, indent, XMTDump); + DumpInt(trace, "binaryID", od->objectDescriptorID, indent, XMTDump); + } else { + fprintf(trace, "%d", od->objectDescriptorID); + EndAttribute(trace, indent, XMTDump); + } + EndAttributes(trace, indent, XMTDump); + if (od->URLString) { + StartSubElement(trace, "URL", indent, XMTDump); + DumpString(trace, "URLstring", od->URLString, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + + if (XMTDump) { + StartElement(trace, "Descr", indent, XMTDump, 1); + indent++; + } + //ESDescr + if (gf_list_count(od->ES_ID_IncDescriptors)) { + DumpDescList(od->ES_ID_IncDescriptors, trace, indent, "esDescrInc", XMTDump, 0); + } else { + DumpDescList(od->ES_ID_RefDescriptors, trace, indent, "esDescrRef", XMTDump, 0); + } + DumpDescList(od->OCIDescriptors, trace, indent, "ociDescr", XMTDump, 0); + DumpDescListFilter(od->IPMP_Descriptors, trace, indent, "ipmpDescrPtr", XMTDump, GF_ODF_IPMP_PTR_TAG); + DumpDescListFilter(od->IPMP_Descriptors, trace, indent, "ipmpDescr", XMTDump, GF_ODF_IPMP_TAG); + DumpDescList(od->extensionDescriptors, trace, indent, "extDescr", XMTDump, 0); + + if (XMTDump) { + indent--; + EndElement(trace, "Descr", indent, XMTDump, 1); + } + indent--; + EndDescDump(trace, "MP4ObjectDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_oci_date(GF_OCI_Data *ocd, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "OCICreationDateDescriptor", indent, XMTDump); + indent++; + DumpString(trace, "OCICreationDate", ocd->OCICreationDate, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_oci_name(GF_OCICreators *ocn, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_OCICreator_item *p; + u32 i; + + StartDescDump(trace, "OCICreatorNameDescriptor", indent, XMTDump); + indent++; + i=0; + while ((p = (GF_OCICreator_item *)gf_list_enum(ocn->OCICreators, &i))) { + StartSubElement(trace, "Creator", indent, XMTDump); + DumpInt(trace, "languageCode", p->langCode, indent, XMTDump); + DumpBool(trace, "isUTF8", p->isUTF8, indent, XMTDump); + DumpString(trace, "name", p->OCICreatorName, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + indent--; + EndDescDump(trace, "OCICreatorNameDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_pl_idx(GF_PL_IDX *plid, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "ProfileLevelIndicationIndexDescriptor", indent, XMTDump); + indent++; + DumpInt(trace, "profileLevelIndicationIndex", plid->profileLevelIndicationIndex, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_qos(GF_QoS_Descriptor *qos, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_QoS_Default *p; + u32 i; + + StartDescDump(trace, "QoS_Descriptor", indent, XMTDump); + indent++; + + if (qos->predefined) { + StartSubElement(trace, "predefined", indent, XMTDump); + DumpInt(trace, "value", qos->predefined, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } else { + i=0; + while ((p = (GF_QoS_Default *)gf_list_enum(qos->QoS_Qualifiers, &i))) { + switch (p->tag) { + case QoSMaxDelayTag: + StartSubElement(trace, "QoSMaxDelay", indent, XMTDump); + DumpInt(trace, "value", ((GF_QoS_MaxDelay *)p)->MaxDelay, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + break; + case QoSPrefMaxDelayTag: + StartSubElement(trace, "QoSPrefMaxDelay", indent, XMTDump); + DumpInt(trace, "value", ((GF_QoS_PrefMaxDelay *)p)->PrefMaxDelay, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + break; + case QoSLossProbTag: + StartSubElement(trace, "QoSLossProb", indent, XMTDump); + DumpFloat(trace, "value", ((GF_QoS_LossProb *)p)->LossProb, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + break; + case QoSMaxGapLossTag: + StartSubElement(trace, "QoSMaxGapLoss", indent, XMTDump); + DumpInt(trace, "value", ((GF_QoS_MaxGapLoss *)p)->MaxGapLoss, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + break; + case QoSMaxAUSizeTag: + StartSubElement(trace, "QoSMaxAUSize", indent, XMTDump); + DumpInt(trace, "value", ((GF_QoS_MaxAUSize *)p)->MaxAUSize, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + break; + case QoSAvgAUSizeTag: + StartSubElement(trace, "QoSAvgAUSize", indent, XMTDump); + DumpInt(trace, "value", ((GF_QoS_AvgAUSize *)p)->AvgAUSize, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + break; + case QoSMaxAURateTag: + StartSubElement(trace, "QoSMaxAURate", indent, XMTDump); + DumpInt(trace, "value", ((GF_QoS_MaxAURate *)p)->MaxAURate, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + break; + default: + StartSubElement(trace, "QoSCustom", indent, XMTDump); + DumpInt(trace, "tag", p->tag, indent, XMTDump); + DumpData(trace, "customData", ((GF_QoS_Private *)p)->Data, ((GF_QoS_Private *)p)->DataLength, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + break; + } + } + } + indent--; + EndDescDump(trace, "QoS_Descriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_rating(GF_Rating *rd, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "RatingDescriptor", indent, XMTDump); + indent++; + DumpInt(trace, "ratingEntity", rd->ratingEntity, indent, XMTDump); + DumpInt(trace, "ratingCriteria", rd->ratingCriteria, indent, XMTDump); + DumpData(trace, "ratingInfo", rd->ratingInfo, rd->infoLength, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_reg(GF_Registration *reg, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "RegistrationDescriptor", indent, XMTDump); + indent++; + DumpInt(trace, "formatIdentifier", reg->formatIdentifier, indent, XMTDump); + DumpData(trace, "additionalIdentificationInfo", reg->additionalIdentificationInfo, reg->dataLength, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_short_text(GF_ShortTextual *std, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "ShortTextualDescriptor", indent, XMTDump); + indent++; + DumpInt(trace, "languageCode", std->langCode, indent, XMTDump); + DumpBool(trace, "isUTF8", std->isUTF8, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + StartSubElement(trace, "event", indent, XMTDump); + DumpString(trace, "name", std->eventName, indent, XMTDump); + DumpString(trace, "text", std->eventText, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + indent--; + EndDescDump(trace, "ShortTextualDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_smpte_camera(GF_SMPTECamera *cpd, FILE *trace, u32 indent, Bool XMTDump) +{ + GF_SmpteParam *p; + u32 i; + + StartDescDump(trace, "SMPTECameraPositionDescriptor", indent, XMTDump); + indent++; + DumpInt(trace, "cameraID", cpd->cameraID, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + + i=0; + while ((p = (GF_SmpteParam *)gf_list_enum(cpd->ParamList, &i))) { + StartSubElement(trace, "parameter", indent, XMTDump); + DumpInt(trace, "id", p->paramID, indent, XMTDump); + DumpInt(trace, "value", p->param, indent, XMTDump); + EndSubElement(trace, indent, XMTDump); + } + indent--; + EndDescDump(trace, "SMPTECameraPositionDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_sup_cid(GF_SCIDesc *scid, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "SupplementaryContentIdentification", indent, XMTDump); + indent++; + DumpInt(trace, "languageCode", scid->languageCode, indent, XMTDump); + DumpString(trace, "supplContentIdentiferTitle", scid->supplContentIdentifierTitle, indent, XMTDump); + DumpString(trace, "supplContentIdentiferValue", scid->supplContentIdentifierValue, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + return GF_OK; +} + + +GF_Err gf_odf_dump_segment(GF_Segment *sd, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "SegmentDescriptor", indent, XMTDump); + indent++; + DumpDouble(trace, "startTime", sd->startTime, indent, XMTDump); + DumpDouble(trace, "duration", sd->Duration, indent, XMTDump); + DumpString(trace, "name", sd->SegmentName, indent, XMTDump); + indent--; + if (XMTDump) + EndSubElement(trace, indent, XMTDump); + else + EndDescDump(trace, "SegmentDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_mediatime(GF_MediaTime *mt, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "MediaTimeDescriptor", indent, XMTDump); + indent++; + DumpDouble(trace, "mediaTimestamp ", mt->mediaTimeStamp, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + return GF_OK; +} + + +GF_Err gf_odf_dump_muxinfo(GF_MuxInfo *mi, FILE *trace, u32 indent, Bool XMTDump) +{ + if (!XMTDump) { + StartDescDump(trace, "MuxInfo", indent, 0); + indent++; + if (mi->file_name) DumpString(trace, "fileName", mi->file_name, indent, 0); + if (mi->streamFormat) DumpString(trace, "streamFormat", mi->streamFormat, indent, 0); + if (mi->GroupID) DumpInt(trace, "GroupID", mi->GroupID, indent, 0); + if (mi->startTime) DumpInt(trace, "startTime", mi->startTime, indent, 0); + if (mi->duration) DumpInt(trace, "duration", mi->duration, indent, 0); +#if 0 + if (mi->import_flags & GF_IMPORT_USE_DATAREF) DumpBool(trace, "useDataReference", 1, indent, 0); + if (mi->import_flags & GF_IMPORT_NO_FRAME_DROP) DumpBool(trace, "noFrameDrop", 1, indent, 0); + if (mi->import_flags & GF_IMPORT_SBR_IMPLICIT) DumpString(trace, "SBR_Type", "implicit", indent, 0); + else if (mi->import_flags & GF_IMPORT_SBR_EXPLICIT) DumpString(trace, "SBR_Type", "explicit", indent, 0); +#endif + + if (mi->textNode) DumpString(trace, "textNode", mi->textNode, indent, 0); + if (mi->fontNode) DumpString(trace, "fontNode", mi->fontNode, indent, 0); + + indent--; + EndDescDump(trace, "MuxInfo", indent, 0); + return GF_OK; + } + + StartDescDump(trace, "StreamSource", indent, 1); + indent++; + if (mi->file_name) DumpString(trace, "url", mi->file_name, indent, 1); + EndAttributes(trace, indent, 1); + + StartDescDump(trace, "MP4MuxHints", indent, 1); + if (mi->GroupID) DumpInt(trace, "GroupID", mi->GroupID, indent, 1); + if (mi->startTime) DumpInt(trace, "startTime", mi->startTime, indent, 1); + if (mi->duration) DumpInt(trace, "duration", mi->duration, indent, 1); + if (mi->import_flags & GF_IMPORT_USE_DATAREF) DumpBool(trace, "useDataReference", 1, indent, 1); + if (mi->import_flags & GF_IMPORT_NO_FRAME_DROP) DumpBool(trace, "noFrameDrop", 1, indent, 1); + if (mi->import_flags & GF_IMPORT_SBR_IMPLICIT) DumpString(trace, "SBR_Type", "implicit", indent, 1); + else if (mi->import_flags & GF_IMPORT_SBR_EXPLICIT) DumpString(trace, "SBR_Type", "explicit", indent, 1); + + if (mi->textNode) DumpString(trace, "textNode", mi->textNode, indent, 1); + if (mi->fontNode) DumpString(trace, "fontNode", mi->fontNode, indent, 1); + EndSubElement(trace, indent, 1); + + indent--; + EndElement(trace, "StreamSource" , indent, 1, 1); + return GF_OK; +} + +GF_Err gf_odf_dump_ipmp_tool_list(GF_IPMP_ToolList *tl, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "IPMP_ToolListDescriptor", indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + indent++; + DumpDescList(tl->ipmp_tools, trace, indent, "ipmpTool", XMTDump, 0); + indent--; + EndDescDump(trace, "IPMP_ToolListDescriptor", indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_ipmp_tool(GF_IPMP_Tool*t, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "IPMP_Tool", indent, XMTDump); + indent++; + DumpBin128(trace, "IPMP_ToolID", (char*)t->IPMP_ToolID, indent, XMTDump); + if (t->tool_url) DumpString(trace, "ToolURL", t->tool_url, indent, XMTDump); + if (t->toolParamDesc) { + StartElement(trace, "toolParamDesc" , indent, XMTDump, 0); + gf_ipmpx_dump_data((GF_IPMPX_Data *)t->toolParamDesc, trace, indent + (XMTDump ? 1 : 0), XMTDump); + EndElement(trace, "toolParamDesc" , indent, XMTDump, 0); + } + EndAttributes(trace, indent, XMTDump); + indent--; + EndDescDump(trace, "IPMP_Tool", indent, XMTDump); + return GF_OK; +} + + +GF_Err gf_odf_dump_od_update(GF_ODUpdate *com, FILE *trace, u32 indent, Bool XMTDump) +{ + if (XMTDump) { + StartDescDump(trace, "ObjectDescriptorUpdate", indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + indent++; + DumpDescList(com->objectDescriptors, trace, indent+1, "OD", XMTDump, 0); + indent--; + EndDescDump(trace, "ObjectDescriptorUpdate", indent, XMTDump); + } else { + DumpDescList(com->objectDescriptors, trace, indent, "UPDATE OD", XMTDump, 1); + } + return GF_OK; +} + +GF_Err gf_odf_dump_od_remove(GF_ODRemove *com, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i; + + if (XMTDump) { + StartDescDump(trace, "ObjectDescriptorRemove", indent, XMTDump); + indent++; + StartAttribute(trace, "objectDescriptorId", indent, XMTDump); + } else { + char ind_buf[OD_MAX_TREE]; + OD_FORMAT_INDENT(ind_buf, indent); + fprintf(trace, "%sREMOVE OD [", ind_buf); + } + for (i=0; iNbODs; i++) { + if (i) fprintf(trace, " "); + fprintf(trace, "%s%d", XMTDump ? "od" : "", com->OD_ID[i]); + } + if (XMTDump) { + EndAttribute(trace, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + } else { + fprintf(trace, "]\n"); + } + return GF_OK; +} + +GF_Err gf_odf_dump_esd_update(GF_ESDUpdate *com, FILE *trace, u32 indent, Bool XMTDump) +{ + if (XMTDump) { + StartDescDump(trace, "ES_DescriptorUpdate", indent, XMTDump); + StartAttribute(trace, "objectDescriptorId", indent, XMTDump); + fprintf(trace, "od%d", com->ODID); + EndAttribute(trace, indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + } else { + char ind_buf[OD_MAX_TREE]; + OD_FORMAT_INDENT(ind_buf, indent); + fprintf(trace, "%sUPDATE ESD in %d\n", ind_buf, com->ODID); + } + indent++; + DumpDescList(com->ESDescriptors, trace, indent+1, "esDescr", XMTDump, 1); + indent--; + if (XMTDump) { + EndDescDump(trace, "ES_DescriptorUpdate", indent, XMTDump); + } else { + fprintf(trace, "\n"); + } + return GF_OK; +} + +GF_Err gf_odf_dump_esd_remove(GF_ESDRemove *com, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i; + + if (XMTDump) { + StartDescDump(trace, "ES_DescriptorRemove", indent, XMTDump); + StartAttribute(trace, "objectDescriptorId", indent, XMTDump); + fprintf(trace, "od%d", com->ODID); + EndAttribute(trace, indent, XMTDump); + StartAttribute(trace, "ES_ID", indent, XMTDump); + } else { + char ind_buf[OD_MAX_TREE]; + OD_FORMAT_INDENT(ind_buf, indent); + fprintf(trace, "%sREMOVE ESD FROM %d [", ind_buf, com->ODID); + } + for (i=0; iNbESDs; i++) { + if (i) fprintf(trace, " "); + if (XMTDump) fprintf(trace, "es"); + fprintf(trace, "%d", com->ES_ID[i]); + } + if (XMTDump) { + EndAttribute(trace, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + } else { + fprintf(trace, "]\n"); + } + return GF_OK; +} + +GF_Err gf_odf_dump_ipmp_update(GF_IPMPUpdate *com, FILE *trace, u32 indent, Bool XMTDump) +{ + if (XMTDump) { + StartDescDump(trace, "IPMP_DescriptorUpdate", indent, XMTDump); + EndAttributes(trace, indent, XMTDump); + indent++; + DumpDescList(com->IPMPDescList, trace, indent+1, "ipmpDesc", XMTDump, 0); + indent--; + EndDescDump(trace, "IPMP_DescriptorUpdate", indent, XMTDump); + } else { + DumpDescList(com->IPMPDescList, trace, indent, "UPDATE IPMPD", XMTDump, 1); + } + return GF_OK; +} + +GF_Err gf_odf_dump_ipmp_remove(GF_IPMPRemove *com, FILE *trace, u32 indent, Bool XMTDump) +{ + u32 i; + + StartDescDump(trace, "IPMP_DescriptorRemove", indent, XMTDump); + indent++; + + StartAttribute(trace, "IPMP_DescriptorID", indent, XMTDump); + for (i=0; iNbIPMPDs; i++) { + if (i) fprintf(trace, " "); + fprintf(trace, "%d", com->IPMPDescID[i]); + } + EndAttribute(trace, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + return GF_OK; +} + +GF_Err gf_odf_dump_base_command(GF_BaseODCom *com, FILE *trace, u32 indent, Bool XMTDump) +{ + StartDescDump(trace, "BaseODCommand", indent, XMTDump); + indent++; + + DumpData(trace, "custom", com->data, com->dataSize, indent, XMTDump); + indent--; + EndSubElement(trace, indent, XMTDump); + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_oci_dump_event(OCIEvent *ev, FILE *trace, u32 indent, Bool XMTDump) +{ + u8 H, M, S, hS, rien; + u16 evID; + u32 i; + GF_Descriptor *desc; + + StartDescDump(trace, "OCI_Event", indent, XMTDump); + indent++; + gf_oci_event_get_id(ev, &evID); + DumpInt(trace, "eventID", evID, indent, XMTDump); + + gf_oci_event_get_start_time(ev, &H, &M, &S, &hS, &rien); + DumpBool(trace, "absoluteTimeFlag", rien, indent, XMTDump); + StartAttribute(trace, "startingTime", indent, XMTDump); + fprintf(trace, "%d:%d:%d:%d", H, M, S, hS); + EndAttribute(trace, indent, XMTDump); + + gf_oci_event_get_duration(ev, &H, &M, &S, &hS); + StartAttribute(trace, "duration", indent, XMTDump); + fprintf(trace, "%d:%d:%d:%d", H, M, S, hS); + EndAttribute(trace, indent, XMTDump); + + StartElement(trace, "OCIDescr", indent, XMTDump, 1); + for (i=0; i +#include +#include +/*for import flags*/ +#include + +/* to complete...*/ + +u32 gf_odf_get_field_type(GF_Descriptor *desc, char *fieldName) +{ + switch (desc->tag) { + case GF_ODF_IOD_TAG: + case GF_ODF_OD_TAG: + if (!stricmp(fieldName, "esDescr")) return GF_ODF_FT_OD_LIST; + else if (!stricmp(fieldName, "ociDescr")) return GF_ODF_FT_OD_LIST; + else if (!stricmp(fieldName, "ipmpDescrPtr")) return GF_ODF_FT_OD_LIST; + else if (!stricmp(fieldName, "ipmpDescr")) return GF_ODF_FT_OD_LIST; + else if (!stricmp(fieldName, "extDescr")) return GF_ODF_FT_OD_LIST; + else if (!stricmp(fieldName, "toolListDescr")) return GF_ODF_FT_OD; + return 0; + case GF_ODF_DCD_TAG: + if (!stricmp(fieldName, "decSpecificInfo")) return GF_ODF_FT_OD; + if (!stricmp(fieldName, "profileLevelIndicationIndexDescr")) return GF_ODF_FT_OD_LIST; + return 0; + case GF_ODF_ESD_TAG: + if (!stricmp(fieldName, "decConfigDescr")) return GF_ODF_FT_OD; + if (!stricmp(fieldName, "muxInfo")) return GF_ODF_FT_OD; + if (!stricmp(fieldName, "StreamSource")) return GF_ODF_FT_OD; + if (!stricmp(fieldName, "slConfigDescr")) return GF_ODF_FT_OD; + if (!stricmp(fieldName, "ipiPtr")) return GF_ODF_FT_OD; + if (!stricmp(fieldName, "qosDescr")) return GF_ODF_FT_OD; + if (!stricmp(fieldName, "regDescr")) return GF_ODF_FT_OD; + if (!stricmp(fieldName, "langDescr")) return GF_ODF_FT_OD; + if (!stricmp(fieldName, "ipIDS")) return GF_ODF_FT_OD_LIST; + if (!stricmp(fieldName, "ipmpDescrPtr")) return GF_ODF_FT_OD_LIST; + if (!stricmp(fieldName, "extDescr")) return GF_ODF_FT_OD_LIST; + return 0; + case GF_ODF_TEXT_CFG_TAG: + if (!stricmp(fieldName, "SampleDescriptions")) return GF_ODF_FT_OD_LIST; + return 0; + case GF_ODF_IPMP_TAG: + if (!stricmp(fieldName, "IPMPX_Data")) return GF_ODF_FT_IPMPX_LIST; + return 0; + case GF_ODF_IPMP_TL_TAG: + if (!stricmp(fieldName, "ipmpTool")) return GF_ODF_FT_OD_LIST; + return 0; + case GF_ODF_IPMP_TOOL_TAG: + if (!stricmp(fieldName, "toolParamDesc")) return GF_ODF_FT_IPMPX; + return 0; + case GF_ODF_BIFS_CFG_TAG: + if (!stricmp(fieldName, "elementaryMask")) return GF_ODF_FT_OD_LIST; + return 0; + } + return 0; +} + +u32 gf_odf_get_tag_by_name(char *descName) +{ + if (!stricmp(descName, "ObjectDescriptor")) return GF_ODF_OD_TAG; + if (!stricmp(descName, "InitialObjectDescriptor")) return GF_ODF_IOD_TAG; + if (!stricmp(descName, "ES_Descriptor")) return GF_ODF_ESD_TAG; + if (!stricmp(descName, "DecoderConfigDescriptor")) return GF_ODF_DCD_TAG; + if (!stricmp(descName, "DecoderSpecificInfo")) return GF_ODF_DSI_TAG; + if (!stricmp(descName, "DecoderSpecificInfoString")) return GF_ODF_DSI_TAG; + if (!stricmp(descName, "SLConfigDescriptor")) return GF_ODF_SLC_TAG; + if (!stricmp(descName, "ContentIdentification")) return GF_ODF_CI_TAG; + if (!stricmp(descName, "SuppContentIdentification")) return GF_ODF_SCI_TAG; + if (!stricmp(descName, "IPIPtr")) return GF_ODF_IPI_PTR_TAG; + if (!stricmp(descName, "IPMP_DescriptorPointer")) return GF_ODF_IPMP_PTR_TAG; + if (!stricmp(descName, "IPMP_Descriptor")) return GF_ODF_IPMP_TAG; + if (!stricmp(descName, "IPMP_ToolListDescriptor")) return GF_ODF_IPMP_TL_TAG; + if (!stricmp(descName, "IPMP_Tool")) return GF_ODF_IPMP_TOOL_TAG; + if (!stricmp(descName, "QoS")) return GF_ODF_QOS_TAG; + if (!stricmp(descName, "RegistrationDescriptor")) return GF_ODF_REG_TAG; + if (!stricmp(descName, "ExtensionPL")) return GF_ODF_EXT_PL_TAG; + if (!stricmp(descName, "PL_IndicationIndex")) return GF_ODF_PL_IDX_TAG; + if (!stricmp(descName, "ContentClassification")) return GF_ODF_CC_TAG; + if (!stricmp(descName, "KeyWordDescriptor")) return GF_ODF_KW_TAG; + if (!stricmp(descName, "RatingDescriptor")) return GF_ODF_RATING_TAG; + if (!stricmp(descName, "LanguageDescriptor")) return GF_ODF_LANG_TAG; + if (!stricmp(descName, "ShortTextualDescriptor")) return GF_ODF_SHORT_TEXT_TAG; + if (!stricmp(descName, "ExpandedTextualDescriptor")) return GF_ODF_TEXT_TAG; + if (!stricmp(descName, "ContentCreatorName")) return GF_ODF_CC_NAME_TAG; + if (!stricmp(descName, "ContentCreationDate")) return GF_ODF_CC_DATE_TAG; + if (!stricmp(descName, "OCI_CreatorName")) return GF_ODF_OCI_NAME_TAG; + if (!stricmp(descName, "OCI_CreationDate")) return GF_ODF_OCI_DATE_TAG; + if (!stricmp(descName, "SmpteCameraPosition")) return GF_ODF_SMPTE_TAG; + if (!stricmp(descName, "SegmentDescriptor")) return GF_ODF_SEGMENT_TAG; + if (!stricmp(descName, "MediaTimeDescriptor")) return GF_ODF_MEDIATIME_TAG; + if (!stricmp(descName, "MuxInfo")) return GF_ODF_MUXINFO_TAG; + if (!stricmp(descName, "StreamSource")) return GF_ODF_MUXINFO_TAG; + if (!stricmp(descName, "BIFSConfig") || !stricmp(descName, "BIFSv2Config")) return GF_ODF_BIFS_CFG_TAG; + if (!stricmp(descName, "ElementaryMask")) return GF_ODF_ELEM_MASK_TAG; + if (!stricmp(descName, "TextConfig")) return GF_ODF_TEXT_CFG_TAG; + if (!stricmp(descName, "TextSampleDescriptor")) return GF_ODF_TX3G_TAG; + if (!stricmp(descName, "UIConfig")) return GF_ODF_UI_CFG_TAG; + if (!stricmp(descName, "ES_ID_Ref")) return GF_ODF_ESD_REF_TAG; + if (!stricmp(descName, "ES_ID_Inc")) return GF_ODF_ESD_INC_TAG; + if (!stricmp(descName, "AuxiliaryVideoData")) return GF_ODF_AUX_VIDEO_DATA; + if (!stricmp(descName, "DefaultDescriptor")) return GF_ODF_DSI_TAG; + return 0; +} + +#define GET_U8(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (u8) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (u8) d; } } +#define GET_U16(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (u16) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (u16) d; } } +#define GET_U32(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (u32) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (u32) d; } } +#define GET_S32(field) { if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (s32) d; } else { ret += sscanf(val, "%d", &d); if (ret) field = (s32) d; } } +#define GET_BOOL(field) { ret = 1; field = (!stricmp(val, "true") || !stricmp(val, "1")) ? 1 : 0; } + +#define GET_DOUBLE(field) { Float v; ret = 1; sscanf(val, "%f", &v); field = (Double) v;} +#define GET_STRING(field) { ret = 1; field = strdup(val); if (val[0] == '"') strcpy(field, val+1); if (field[strlen(field)-1] == '"') field[strlen(field)-1] = 0; } + +void OD_ParseBinData(char *val, char **out_data, u32 *out_data_size) +{ + u32 i, c; + char s[3]; + u32 len = strlen(val) / 3; + if (*out_data) free(*out_data); + *out_data_size = len; + *out_data = (char*)malloc(sizeof(char) * len); + s[2] = 0; + for (i=0; itag) { + case GF_ODF_IOD_TAG: + { + GF_InitialObjectDescriptor *iod = (GF_InitialObjectDescriptor *)desc; + if (!stricmp(fieldName, "objectDescriptorID") || !stricmp(fieldName, "binaryID")) ret += sscanf(val, "%hd", &iod->objectDescriptorID); + else if (!stricmp(fieldName, "URLString")) { + iod->URLString = strdup(val); + ret = 1; + } + else if (!stricmp(fieldName, "includeInlineProfileLevelFlag")) { + GET_BOOL(iod->inlineProfileFlag) + if (!ret) { iod->inlineProfileFlag = 0; ret = 1; } + } + else if (!stricmp(fieldName, "ODProfileLevelIndication")) { + GET_U8(iod->OD_profileAndLevel) + if (!ret) { iod->OD_profileAndLevel = 0xFE; ret = 1; } + } + else if (!stricmp(fieldName, "sceneProfileLevelIndication")) { + GET_U8(iod->scene_profileAndLevel) + if (!ret) { iod->scene_profileAndLevel = 0xFE; ret = 1; } + } + else if (!stricmp(fieldName, "audioProfileLevelIndication")) { + GET_U8(iod->audio_profileAndLevel) + if (!ret) { iod->audio_profileAndLevel = 0xFE; ret = 1; } + } + else if (!stricmp(fieldName, "visualProfileLevelIndication")) { + GET_U8(iod->visual_profileAndLevel) + if (!ret) { iod->visual_profileAndLevel = 0xFE; ret = 1; } + } + else if (!stricmp(fieldName, "graphicsProfileLevelIndication")) { + GET_U8(iod->graphics_profileAndLevel) + if (!ret) { iod->graphics_profileAndLevel = 0xFE; ret = 1; } + } + } + break; + case GF_ODF_OD_TAG: + { + GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) desc; + if (!stricmp(fieldName, "objectDescriptorID") || !stricmp(fieldName, "binaryID")) ret += sscanf(val, "%hd", &od->objectDescriptorID); + else if (!stricmp(fieldName, "URLString")) { + od->URLString = strdup(val); + ret = 1; + } + } + break; + case GF_ODF_DCD_TAG: + { + GF_DecoderConfig *dcd = (GF_DecoderConfig *)desc; + if (!stricmp(fieldName, "objectTypeIndication")) { + GET_U8(dcd->objectTypeIndication) + /*XMT may use string*/ + if (!ret) { + if (!stricmp(val, "MPEG4Systems1")) { dcd->objectTypeIndication = 0x01; ret = 1; } + else if (!stricmp(val, "MPEG4Systems2")) { dcd->objectTypeIndication = 0x02; ret = 1; } + else if (!stricmp(val, "MPEG4Visual")) { dcd->objectTypeIndication = 0x20; ret = 1; } + else if (!stricmp(val, "MPEG4Audio")) { dcd->objectTypeIndication = 0x40; ret = 1; } + else if (!stricmp(val, "MPEG2VisualSimple")) { dcd->objectTypeIndication = 0x60; ret = 1; } + else if (!stricmp(val, "MPEG2VisualMain")) { dcd->objectTypeIndication = 0x61; ret = 1; } + else if (!stricmp(val, "MPEG2VisualSNR")) { dcd->objectTypeIndication = 0x62; ret = 1; } + else if (!stricmp(val, "MPEG2VisualSpatial")) { dcd->objectTypeIndication = 0x63; ret = 1; } + else if (!stricmp(val, "MPEG2VisualHigh")) { dcd->objectTypeIndication = 0x64; ret = 1; } + else if (!stricmp(val, "MPEG2Visual422")) { dcd->objectTypeIndication = 0x65; ret = 1; } + else if (!stricmp(val, "MPEG2AudioMain")) { dcd->objectTypeIndication = 0x66; ret = 1; } + else if (!stricmp(val, "MPEG2AudioLowComplexity")) { dcd->objectTypeIndication = 0x67; ret = 1; } + else if (!stricmp(val, "MPEG2AudioScaleableSamplingRate")) { dcd->objectTypeIndication = 0x68; ret = 1; } + else if (!stricmp(val, "MPEG2AudioPart3")) { dcd->objectTypeIndication = 0x69; ret = 1; } + else if (!stricmp(val, "MPEG1Visual")) { dcd->objectTypeIndication = 0x6A; ret = 1; } + else if (!stricmp(val, "MPEG1Audio")) { dcd->objectTypeIndication = 0x6B; ret = 1; } + else if (!stricmp(val, "JPEG")) { dcd->objectTypeIndication = 0x6C; ret = 1; } + else if (!stricmp(val, "PNG")) { dcd->objectTypeIndication = 0x6D; ret = 1; } + } + } + else if (!stricmp(fieldName, "streamType")) { + GET_U8(dcd->streamType) + /*XMT may use string*/ + if (!ret) { + if (!stricmp(val, "ObjectDescriptor")) { dcd->streamType = GF_STREAM_OD; ret = 1; } + else if (!stricmp(val, "ClockReference")) { dcd->streamType = GF_STREAM_OCR; ret = 1; } + else if (!stricmp(val, "SceneDescription")) { dcd->streamType = GF_STREAM_SCENE; ret = 1; } + else if (!stricmp(val, "Visual")) { dcd->streamType = GF_STREAM_VISUAL; ret = 1; } + else if (!stricmp(val, "Audio")) { dcd->streamType = GF_STREAM_AUDIO; ret = 1; } + else if (!stricmp(val, "MPEG7")) { dcd->streamType = GF_STREAM_MPEG7; ret = 1; } + else if (!stricmp(val, "IPMP")) { dcd->streamType = GF_STREAM_IPMP; ret = 1; } + else if (!stricmp(val, "OCI")) { dcd->streamType = GF_STREAM_OCI; ret = 1; } + else if (!stricmp(val, "MPEGJ")) { dcd->streamType = GF_STREAM_MPEGJ; ret = 1; } + } + } + else if (!stricmp(fieldName, "upStream")) GET_BOOL(dcd->upstream) + else if (!stricmp(fieldName, "bufferSizeDB")) ret += sscanf(val, "%d", &dcd->bufferSizeDB); + else if (!stricmp(fieldName, "maxBitRate")) ret += sscanf(val, "%d", &dcd->maxBitrate); + else if (!stricmp(fieldName, "avgBitRate")) ret += sscanf(val, "%d", &dcd->avgBitrate); + } + break; + case GF_ODF_ESD_TAG: + { + GF_ESD *esd = (GF_ESD *)desc; + if (!stricmp(fieldName, "ES_ID") || !stricmp(fieldName, "binaryID")) { + ret += sscanf(val, "%hd", &esd->ESID); + } + else if (!stricmp(fieldName, "streamPriority")) GET_U8(esd->streamPriority) + else if (!stricmp(fieldName, "dependsOn_ES_ID") || !stricmp(fieldName, "dependsOnESID")) ret += sscanf(val, "%hd", &esd->dependsOnESID); + else if (!stricmp(fieldName, "OCR_ES_ID")) ret += sscanf(val, "%hd", &esd->OCRESID); + else if (!stricmp(fieldName, "URLstring")) { + esd->URLString = strdup(val); + ret = 1; + } + /*ignore*/ + else if (!stricmp(fieldName, "streamDependenceFlag") + || !stricmp(fieldName, "URL_Flag") + || !stricmp(fieldName, "OCRstreamFlag") + ) + ret = 1; + } + break; + case GF_ODF_SLC_TAG: + { + u32 ts; + GF_SLConfig *slc = (GF_SLConfig*)desc; + if (!stricmp(fieldName, "predefined")) GET_U8(slc->predefined) + else if (!stricmp(fieldName, "useAccessUnitStartFlag")) GET_BOOL(slc->useAccessUnitStartFlag) + else if (!stricmp(fieldName, "useAccessUnitEndFlag")) GET_BOOL(slc->useAccessUnitEndFlag) + else if (!stricmp(fieldName, "useRandomAccessPointFlag")) GET_BOOL(slc->useRandomAccessPointFlag) + else if (!stricmp(fieldName, "hasRandomAccessUnitsOnlyFlag") || !stricmp(fieldName, "useRandomAccessUnitsOnlyFlag")) GET_BOOL(slc->hasRandomAccessUnitsOnlyFlag) + else if (!stricmp(fieldName, "usePaddingFlag")) GET_BOOL(slc->usePaddingFlag) + else if (!stricmp(fieldName, "useTimeStampsFlag")) GET_BOOL(slc->useTimestampsFlag) + else if (!stricmp(fieldName, "useIdleFlag")) GET_BOOL(slc->useIdleFlag) + else if (!stricmp(fieldName, "timeStampResolution")) ret += sscanf(val, "%d", &slc->timestampResolution); + else if (!stricmp(fieldName, "OCRResolution")) ret += sscanf(val, "%d", &slc->OCRResolution); + else if (!stricmp(fieldName, "timeStampLength")) GET_U8(slc->timestampLength) + else if (!stricmp(fieldName, "OCRLength")) GET_U8(slc->OCRLength) + else if (!stricmp(fieldName, "AU_Length")) GET_U8(slc->AULength) + else if (!stricmp(fieldName, "instantBitrateLength")) GET_U8(slc->instantBitrateLength) + else if (!stricmp(fieldName, "degradationPriorityLength")) GET_U8(slc->degradationPriorityLength) + else if (!stricmp(fieldName, "AU_seqNumLength")) GET_U8(slc->AUSeqNumLength) + else if (!stricmp(fieldName, "packetSeqNumLength")) GET_U8(slc->packetSeqNumLength) + else if (!stricmp(fieldName, "timeScale")) ret += sscanf(val, "%d", &slc->timeScale); + else if (!stricmp(fieldName, "accessUnitDuration")) ret += sscanf(val, "%hd", &slc->AUDuration); + else if (!stricmp(fieldName, "compositionUnitDuration")) ret += sscanf(val, "%hd", &slc->CUDuration); + else if (!stricmp(fieldName, "startDecodingTimeStamp")) { + ret += sscanf(val, "%d", &ts); + slc->startDTS = ts; + } + else if (!stricmp(fieldName, "startCompositionTimeStamp")) { + ret += sscanf(val, "%d", &ts); + slc->startCTS = ts; + } + else if (!stricmp(fieldName, "durationFlag")) ret = 1; + } + break; + case GF_ODF_ELEM_MASK_TAG: + { + GF_ElementaryMask* em = (GF_ElementaryMask*)desc; + if (!stricmp(fieldName, "atNode")) { + GET_U32(em->node_id); + if (!ret || !em->node_id) em->node_name = strdup(val); + ret = 1; + } + else if (!stricmp(fieldName, "numDynFields")) ret = 1; + } + break; + case GF_ODF_BIFS_CFG_TAG: + { + s32 notused; + GF_BIFSConfig *bcd = (GF_BIFSConfig*)desc; + if (!stricmp(val, "auto")) return GF_OK; + if (!stricmp(fieldName, "nodeIDbits")) ret += sscanf(val, "%hd", &bcd->nodeIDbits); + else if (!stricmp(fieldName, "nodeIDbits")) ret += sscanf(val, "%hd", &bcd->nodeIDbits); + else if (!stricmp(fieldName, "routeIDbits")) ret += sscanf(val, "%hd", &bcd->routeIDbits); + else if (!stricmp(fieldName, "protoIDbits")) ret += sscanf(val, "%hd", &bcd->protoIDbits); + else if (!stricmp(fieldName, "isCommandStream")) { /*GET_BOOL(bcd->isCommandStream)*/ ret = 1; } + else if (!stricmp(fieldName, "pixelMetric") || !stricmp(fieldName, "pixelMetrics")) GET_BOOL(bcd->pixelMetrics) + else if (!stricmp(fieldName, "pixelWidth")) ret += sscanf(val, "%hd", &bcd->pixelWidth); + else if (!stricmp(fieldName, "pixelHeight")) ret += sscanf(val, "%hd", &bcd->pixelHeight); + else if (!stricmp(fieldName, "use3DMeshCoding")) GET_BOOL(notused) + else if (!stricmp(fieldName, "usePredictiveMFField")) GET_BOOL(notused) + else if (!stricmp(fieldName, "randomAccess")) GET_BOOL(bcd->randomAccess) + else if (!stricmp(fieldName, "useNames")) GET_BOOL(bcd->useNames) + } + break; + case GF_ODF_MUXINFO_TAG: + { + GF_MuxInfo *mi = (GF_MuxInfo *)desc; + if (!stricmp(fieldName, "fileName") || !stricmp(fieldName, "url")) GET_STRING(mi->file_name) + else if (!stricmp(fieldName, "streamFormat")) GET_STRING(mi->streamFormat) + else if (!stricmp(fieldName, "GroupID")) ret += sscanf(val, "%d", &mi->GroupID); + else if (!stricmp(fieldName, "startTime")) ret += sscanf(val, "%d", &mi->startTime); + else if (!stricmp(fieldName, "duration")) ret += sscanf(val, "%d", &mi->duration); + else if (!stricmp(fieldName, "compactSize")) + { ret = 1; if (!stricmp(val, "true") || !stricmp(val, "1")) mi->import_flags |= GF_IMPORT_USE_COMPACT_SIZE; } + else if (!stricmp(fieldName, "useDataReference")) + { ret = 1; if (!stricmp(val, "true") || !stricmp(val, "1")) mi->import_flags |= GF_IMPORT_USE_DATAREF; } + else if (!stricmp(fieldName, "noFrameDrop")) + { ret = 1; if (!stricmp(val, "true") || !stricmp(val, "1")) mi->import_flags |= GF_IMPORT_NO_FRAME_DROP; } + else if (!stricmp(fieldName, "SBR_Type")) { + ret = 1; + if (!stricmp(val, "implicit") || !stricmp(val, "1")) mi->import_flags |= GF_IMPORT_SBR_IMPLICIT; + else if (!stricmp(val, "explicit") || !stricmp(val, "2")) mi->import_flags |= GF_IMPORT_SBR_EXPLICIT; + } + + else if (!stricmp(fieldName, "textNode")) GET_STRING(mi->textNode) + else if (!stricmp(fieldName, "fontNode")) GET_STRING(mi->fontNode) + else if (!stricmp(fieldName, "frameRate")) { ret = 1; mi->frame_rate = atof(val); } + } + break; + case GF_ODF_DSI_TAG: + { + GF_DefaultDescriptor *dsi = (GF_DefaultDescriptor*)desc; + if (!stricmp(fieldName, "info")) { + /*only parse true hexa strings*/ + if (val[0] == '%') { + OD_ParseBinData(val, &dsi->data, &dsi->dataLength); + ret = 1; + } else if (!strnicmp(val, "file:", 5)) { + OD_ParseFileData(val+5, &dsi->data, &dsi->dataLength); + ret = 1; + } else if (!strlen(val)) ret = 1; + } + if (!stricmp(fieldName, "src")) { + u32 len = strlen("data:application/octet-string,"); + if (strnicmp(val, "data:application/octet-string,", len)) break; + val += len; + /*only parse true hexa strings*/ + if (val[0] == '%') { + OD_ParseBinData(val, &dsi->data, &dsi->dataLength); + ret = 1; + } else if (!strnicmp(val, "file:", 5)) { + OD_ParseFileData(val+5, &dsi->data, &dsi->dataLength); + ret = 1; + } + } + } + break; + case GF_ODF_SEGMENT_TAG: + { + GF_Segment *sd = (GF_Segment*)desc; + if (!stricmp(fieldName, "start") || !stricmp(fieldName, "startTime")) GET_DOUBLE(sd->startTime) + else if (!stricmp(fieldName, "duration")) GET_DOUBLE(sd->Duration) + else if (!stricmp(fieldName, "name") || !stricmp(fieldName, "segmentName")) GET_STRING(sd->SegmentName) + } + break; + case GF_ODF_UI_CFG_TAG: + { + GF_UIConfig *uic = (GF_UIConfig*)desc; + if (!stricmp(fieldName, "deviceName")) GET_STRING(uic->deviceName) + else if (!stricmp(fieldName, "termChar")) GET_U8(uic->termChar) + else if (!stricmp(fieldName, "delChar")) GET_U8(uic->delChar) + else if (!stricmp(fieldName, "uiData")) { + /*only parse true hexa strings*/ + if (val[0] == '%') { + OD_ParseBinData(val, &uic->ui_data, &uic->ui_data_length); + ret = 1; + } else if (!strnicmp(val, "file:", 5)) { + OD_ParseFileData(val+5, &uic->ui_data, &uic->ui_data_length); + ret = 1; + } else { + ret = OD_ParseUIConfig(val, &uic->ui_data, &uic->ui_data_length); + } + } + } + break; + case GF_ODF_ESD_INC_TAG: + { + GF_ES_ID_Inc *inc = (GF_ES_ID_Inc *)desc; + if (!stricmp(fieldName, "trackID")) ret += sscanf(val, "%d", &inc->trackID); + } + break; + case GF_ODF_ESD_REF_TAG: + { + GF_ES_ID_Ref *inc = (GF_ES_ID_Ref *)desc; + if (!stricmp(fieldName, "trackID")) ret += sscanf(val, "%hd", &inc->trackRef); + } + break; + case GF_ODF_TEXT_CFG_TAG: + { + GF_TextConfig *txt = (GF_TextConfig*)desc; + if (!stricmp(fieldName, "3GPPBaseFormat")) GET_U8(txt->Base3GPPFormat) + else if (!stricmp(fieldName, "MPEGExtendedFormat")) GET_U8(txt->MPEGExtendedFormat) + else if (!stricmp(fieldName, "profileLevel")) GET_U8(txt->profileLevel) + else if (!stricmp(fieldName, "durationClock") || !stricmp(fieldName, "timescale") ) GET_U32(txt->timescale) + else if (!stricmp(fieldName, "layer")) GET_U32(txt->layer) + else if (!stricmp(fieldName, "text_width")) GET_U32(txt->text_width) + else if (!stricmp(fieldName, "text_height")) GET_U32(txt->text_height) + else if (!stricmp(fieldName, "video_width")) { + GET_U32(txt->video_width) + txt->has_vid_info = 1; + } + else if (!stricmp(fieldName, "video_height")) { + GET_U32(txt->video_height) + txt->has_vid_info = 1; + } + else if (!stricmp(fieldName, "horizontal_offset")) { + GET_S32(txt->horiz_offset) + txt->has_vid_info = 1; + } + else if (!stricmp(fieldName, "vertical_offset")) { + GET_S32(txt->vert_offset) + txt->has_vid_info = 1; + } + } + break; + case GF_ODF_TX3G_TAG: + { + GF_TextSampleDescriptor *sd = (GF_TextSampleDescriptor*)desc; + if (!stricmp(fieldName, "displayFlags")) GET_U32(sd->displayFlags) + else if (!stricmp(fieldName, "horiz_justif")) GET_S32(sd->horiz_justif) + else if (!stricmp(fieldName, "vert_justif")) GET_S32(sd->vert_justif) + else if (!stricmp(fieldName, "back_color")) GET_S32(sd->back_color) + else if (!stricmp(fieldName, "top")) GET_S32(sd->default_pos.top) + else if (!stricmp(fieldName, "bottom")) GET_S32(sd->default_pos.bottom) + else if (!stricmp(fieldName, "left")) GET_S32(sd->default_pos.left) + else if (!stricmp(fieldName, "right")) GET_S32(sd->default_pos.right) + else if (!stricmp(fieldName, "style_font_ID")) GET_S32(sd->default_style.fontID) + else if (!stricmp(fieldName, "style_font_size")) GET_S32(sd->default_style.font_size) + else if (!stricmp(fieldName, "style_text_color")) GET_U32(sd->default_style.text_color) + else if (!stricmp(fieldName, "style_flags")) { + char szStyles[1024]; + strcpy(szStyles, val); + strlwr(szStyles); + if (strstr(szStyles, "bold")) sd->default_style.style_flags |= GF_TXT_STYLE_BOLD; + if (strstr(szStyles, "italic")) sd->default_style.style_flags |= GF_TXT_STYLE_ITALIC; + if (strstr(szStyles, "underlined")) sd->default_style.style_flags |= GF_TXT_STYLE_UNDERLINED; + ret = 1; + } + else if (!stricmp(fieldName, "fontID") || !stricmp(fieldName, "fontName")) { + /*check if we need a new entry*/ + if (!sd->font_count) { + sd->fonts = (GF_FontRecord*)malloc(sizeof(GF_FontRecord)); + sd->font_count = 1; + sd->fonts[0].fontID = 0; + sd->fonts[0].fontName = NULL; + } else { + Bool realloc_fonts = 0; + if (!stricmp(fieldName, "fontID") && sd->fonts[sd->font_count-1].fontID) realloc_fonts = 1; + else if (!stricmp(fieldName, "fontName") && sd->fonts[sd->font_count-1].fontName) realloc_fonts = 1; + if (realloc_fonts) { + sd->font_count += 1; + sd->fonts = (GF_FontRecord*)realloc(sd->fonts, sizeof(GF_FontRecord)*sd->font_count); + sd->fonts[sd->font_count-1].fontID = 0; + sd->fonts[sd->font_count-1].fontName = NULL; + } + } + if (!stricmp(fieldName, "fontID")) GET_U32(sd->fonts[sd->font_count-1].fontID) + if (!stricmp(fieldName, "fontName")) GET_STRING(sd->fonts[sd->font_count-1].fontName) + ret = 1; + } + } + break; + case GF_ODF_IPMP_TAG: + { + GF_IPMP_Descriptor *ipmp = (GF_IPMP_Descriptor*)desc; + if (!stricmp(fieldName, "IPMP_DescriptorID")) GET_U8(ipmp->IPMP_DescriptorID) + else if (!stricmp(fieldName, "IPMPS_Type")) GET_U16(ipmp->IPMPS_Type) + else if (!stricmp(fieldName, "IPMP_DescriptorIDEx")) GET_U16(ipmp->IPMP_DescriptorIDEx) + else if (!stricmp(fieldName, "IPMP_ToolID")) { ret = 1; OD_ParseBin128(val, &ipmp->IPMP_ToolID); } + else if (!stricmp(fieldName, "controlPointCode")) GET_U8(ipmp->control_point) + else if (!stricmp(fieldName, "sequenceCode")) GET_U8(ipmp->cp_sequence_code) + } + break; + case GF_ODF_IPMP_PTR_TAG: + { + GF_IPMPPtr *ipmpd = (GF_IPMPPtr*)desc; + if (!stricmp(fieldName, "IPMP_DescriptorID")) GET_U8(ipmpd->IPMP_DescriptorID) + else if (!stricmp(fieldName, "IPMP_DescriptorIDEx")) ret += sscanf(val, "%hd", &ipmpd->IPMP_DescriptorIDEx); + else if (!stricmp(fieldName, "IPMP_ES_ID")) ret += sscanf(val, "%hd", &ipmpd->IPMP_ES_ID); + } + break; + case GF_ODF_LANG_TAG: + { + GF_Language *ld = (GF_Language *)desc; + if (!stricmp(fieldName, "languageCode")) { + u32 li, l = strlen(val); + ld->langCode = 0; + for (li = 0; li < l; li++) { + /* Warning: sensitive to big endian, little endian */ + ld->langCode |= (val[li] << (l-li-1)*8); + } + ret++; + } + } + break; + case GF_ODF_AUX_VIDEO_DATA: + { + GF_AuxVideoDescriptor *avd = (GF_AuxVideoDescriptor *)desc; + if (!stricmp(fieldName, "aux_video_type")) GET_U8(avd->aux_video_type) + if (!stricmp(fieldName, "position_offset_h")) GET_U8(avd->aux_video_type) + if (!stricmp(fieldName, "position_offset_v")) GET_U8(avd->aux_video_type) + if (!stricmp(fieldName, "knear")) GET_U8(avd->aux_video_type) + if (!stricmp(fieldName, "kfar")) GET_U8(avd->aux_video_type) + if (!stricmp(fieldName, "parallax_zero")) GET_U16(avd->aux_video_type) + if (!stricmp(fieldName, "parallax_scale")) GET_U16(avd->aux_video_type) + if (!stricmp(fieldName, "dref")) GET_U16(avd->aux_video_type) + if (!stricmp(fieldName, "wref")) GET_U16(avd->aux_video_type) + } + break; + case GF_ODF_IPMP_TOOL_TAG: + { + GF_IPMP_Tool *it = (GF_IPMP_Tool*)desc; + if (!stricmp(fieldName, "IPMP_ToolID")) { ret = 1; OD_ParseBin128(val, &it->IPMP_ToolID); } + else if (!stricmp(fieldName, "ToolURL")) GET_STRING(it->tool_url) + } + break; + } + + return ret ? GF_OK : GF_BAD_PARAM; +} + + +Bool OD_ParseUIConfig(char *val, char **out_data, u32 *out_data_size) +{ + GF_BitStream *bs; + if (!strnicmp(val, "HTK:", 4)) { + char szItem[100]; + s32 pos, bs_start, bs_cur; + Bool has_word; + u32 nb_phonems, nbWords = 0; + bs_start = 0; + nb_phonems = 0; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*we'll write the nb of words later on*/ + gf_bs_write_int(bs, 0, 8); + has_word = 0; + /*parse all words*/ + val += 4; + while (1) { + pos = gf_token_get(val, 0, " ;", szItem, 100); + if (pos>0) val += pos; + if (!has_word) { + has_word = 1; + nbWords++; + nb_phonems = 0; + bs_start = (u32) gf_bs_get_position(bs); + /*nb phonems*/ + gf_bs_write_int(bs, 0, 8); + gf_bs_write_data(bs, szItem, strlen(szItem)); + gf_bs_write_int(bs, 0, 8); + continue; + } + if (pos>0) { + + nb_phonems ++; + /*would be nicer with a phone book & use indexes*/ + if (!stricmp(szItem, "vcl")) { + gf_bs_write_data(bs, "vc", 2); + } else { + gf_bs_write_data(bs, szItem, 2); + } + + while (val[0] && (val[0]==' ')) val += 1; + } + + if ((pos<0) || !val[0] || val[0]==';') { + if (has_word) { + has_word = 0; + bs_cur = (u32) gf_bs_get_position(bs); + gf_bs_seek(bs, bs_start); + gf_bs_write_int(bs, nb_phonems, 8); + gf_bs_seek(bs, bs_cur); + } + if ((pos<0) || !val[0]) break; + val += 1; + while (val[0] && (val[0]==' ')) val += 1; + } + } + if (nbWords) { + bs_cur = (u32) gf_bs_get_position(bs); + gf_bs_seek(bs, 0); + gf_bs_write_int(bs, nbWords, 8); + gf_bs_seek(bs, bs_cur); + gf_bs_get_content(bs, out_data, out_data_size); + } + gf_bs_del(bs); + return 1; + } + return 0; +} + diff --git a/src/odf/qos.c b/src/odf/qos.c new file mode 100644 index 0000000..5e3cc93 --- /dev/null +++ b/src/odf/qos.c @@ -0,0 +1,390 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +void gf_odf_delete_qos_qual(GF_QoS_Default *qos) +{ + switch (qos->tag) { + case QoSMaxDelayTag : + case QoSPrefMaxDelayTag: + case QoSLossProbTag: + case QoSMaxGapLossTag: + case QoSMaxAUSizeTag: + case QoSAvgAUSizeTag: + case QoSMaxAURateTag: + free(qos); + return; + + default: + if ( ((GF_QoS_Private *)qos)->DataLength) + free(((GF_QoS_Private *)qos)->Data); + free( (GF_QoS_Private *) qos); + return; + } +} + + +GF_Err gf_odf_size_qos_qual(GF_QoS_Default *qos) +{ + if (! qos) return GF_BAD_PARAM; + qos->size = 0; + + switch (qos->tag) { + case QoSMaxDelayTag: + case QoSPrefMaxDelayTag: + case QoSLossProbTag: + case QoSMaxGapLossTag: + case QoSMaxAUSizeTag: + case QoSAvgAUSizeTag: + case QoSMaxAURateTag: + qos->size += 4; + return GF_OK; + + case 0x00: + case 0xFF: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + default : + qos->size += ((GF_QoS_Private *)qos)->DataLength; + } + return GF_OK; +} + +GF_Err gf_odf_write_qos_qual(GF_BitStream *bs, GF_QoS_Default *qos) +{ + GF_Err e; + if (!bs || !qos) return GF_BAD_PARAM; + + e = gf_odf_size_qos_qual(qos); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, qos->tag, qos->size); + if (e) return e; + + switch (qos->tag) { + case QoSMaxDelayTag: + gf_bs_write_int(bs, ((GF_QoS_MaxDelay *)qos)->MaxDelay, 32); + break; + + case QoSPrefMaxDelayTag: + gf_bs_write_int(bs, ((GF_QoS_PrefMaxDelay *)qos)->PrefMaxDelay, 32); + break; + + case QoSLossProbTag: + //FLOAT (double on 4 bytes) + gf_bs_write_float(bs, ((GF_QoS_LossProb *)qos)->LossProb); + break; + + case QoSMaxGapLossTag: + gf_bs_write_int(bs, ((GF_QoS_MaxGapLoss *)qos)->MaxGapLoss, 32); + break; + + case QoSMaxAUSizeTag: + gf_bs_write_int(bs, ((GF_QoS_MaxAUSize *)qos)->MaxAUSize, 32); + break; + + case QoSAvgAUSizeTag: + gf_bs_write_int(bs, ((GF_QoS_AvgAUSize *)qos)->AvgAUSize, 32); + break; + + case QoSMaxAURateTag: + gf_bs_write_int(bs, ((GF_QoS_MaxAURate *)qos)->MaxAURate, 32); + break; + + case 0x00: + case 0xFF: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + default: + //we defined the private qos... + gf_bs_write_data(bs, ((GF_QoS_Private *)qos)->Data, ((GF_QoS_Private *)qos)->DataLength); + break; + } + return GF_OK; +} + + + +GF_Err gf_odf_parse_qos(GF_BitStream *bs, GF_QoS_Default **qos_qual, u32 *qual_size) +{ + u32 tag, qos_size, val, bytesParsed, sizeHeader; + GF_QoS_Default *newQoS; + + //tag + tag = gf_bs_read_int(bs, 8); + bytesParsed = 1; + //size of instance + qos_size = 0; + sizeHeader = 0; + do { + val = gf_bs_read_int(bs, 8); + sizeHeader++; + qos_size <<= 7; + qos_size |= val & 0x7F; + } while ( val & 0x80 ); + bytesParsed += sizeHeader; + + //Payload + switch (tag) { + case QoSMaxDelayTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_MaxDelay)); + ((GF_QoS_MaxDelay *)newQoS)->MaxDelay = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case QoSPrefMaxDelayTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_PrefMaxDelay)); + ((GF_QoS_PrefMaxDelay *)newQoS)->PrefMaxDelay = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case QoSLossProbTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_LossProb)); + ((GF_QoS_LossProb *)newQoS)->LossProb = gf_bs_read_float(bs); + bytesParsed += 4; + break; + + case QoSMaxGapLossTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_MaxGapLoss)); + ((GF_QoS_MaxGapLoss *)newQoS)->MaxGapLoss = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case QoSMaxAUSizeTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_MaxAUSize)); + ((GF_QoS_MaxAUSize *)newQoS)->MaxAUSize = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case QoSAvgAUSizeTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_AvgAUSize)); + ((GF_QoS_AvgAUSize *)newQoS)->AvgAUSize = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case QoSMaxAURateTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_MaxAURate)); + ((GF_QoS_MaxAURate *)newQoS)->MaxAURate = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case 0x00: + case 0xFF: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + default: + //we defined the private qos... + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_Private)); + ((GF_QoS_Private *)newQoS)->DataLength = qos_size; + gf_bs_read_data(bs, ((GF_QoS_Private *)newQoS)->Data, ((GF_QoS_Private *)newQoS)->DataLength); + bytesParsed += ((GF_QoS_Private *)newQoS)->DataLength; + break; + } + newQoS->size = qos_size; + newQoS->tag = tag; + if (bytesParsed != 1 + qos_size + sizeHeader) { + gf_odf_delete_qos_qual(newQoS); + return GF_ODF_INVALID_DESCRIPTOR; + } + *qos_qual = newQoS; + *qual_size = bytesParsed; + return GF_OK; +} + + +GF_QoS_Default *NewQoS(u8 tag) +{ + GF_QoS_Default *newQoS; + + switch (tag) { + case QoSMaxDelayTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_MaxDelay)); + ((GF_QoS_MaxDelay *)newQoS)->MaxDelay = 0; + ((GF_QoS_MaxDelay *)newQoS)->size = 4; + break; + + case QoSPrefMaxDelayTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_PrefMaxDelay)); + ((GF_QoS_PrefMaxDelay *)newQoS)->PrefMaxDelay = 0; + ((GF_QoS_PrefMaxDelay *)newQoS)->size = 4; + break; + + case QoSLossProbTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_LossProb)); + ((GF_QoS_LossProb *)newQoS)->LossProb = 0; + ((GF_QoS_LossProb *)newQoS)->size = 4; + break; + + case QoSMaxGapLossTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_MaxGapLoss)); + ((GF_QoS_MaxGapLoss *)newQoS)->MaxGapLoss = 0; + ((GF_QoS_MaxGapLoss *)newQoS)->size = 4; + break; + + case QoSMaxAUSizeTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_MaxAUSize)); + ((GF_QoS_MaxAUSize *)newQoS)->MaxAUSize = 0; + ((GF_QoS_MaxAUSize *)newQoS)->size = 0; + break; + + case QoSAvgAUSizeTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_AvgAUSize)); + ((GF_QoS_AvgAUSize *)newQoS)->AvgAUSize = 0; + ((GF_QoS_AvgAUSize *)newQoS)->size = 4; + break; + + case QoSMaxAURateTag: + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_MaxAURate)); + ((GF_QoS_MaxAURate *)newQoS)->MaxAURate = 0; + ((GF_QoS_MaxAURate *)newQoS)->size = 4; + break; + + case 0x00: + case 0xFF: + return NULL; + + default: + //we defined the private qos... + newQoS = (GF_QoS_Default *) malloc(sizeof(GF_QoS_Private)); + ((GF_QoS_Private *)newQoS)->DataLength = 0; + ((GF_QoS_Private *)newQoS)->Data = NULL; + break; + } + newQoS->tag = tag; + return newQoS; +} + +// +// Constructor +// +GF_Descriptor *gf_odf_new_qos() +{ + GF_QoS_Descriptor *newDesc = (GF_QoS_Descriptor *) malloc(sizeof(GF_QoS_Descriptor)); + if (!newDesc) return NULL; + newDesc->QoS_Qualifiers = gf_list_new(); + newDesc->predefined = 0; + newDesc->tag = GF_ODF_QOS_TAG; + return (GF_Descriptor *) newDesc; +} + +// +// Desctructor +// +GF_Err gf_odf_del_qos(GF_QoS_Descriptor *qos) +{ + if (!qos) return GF_BAD_PARAM; + + while (gf_list_count(qos->QoS_Qualifiers)) { + GF_QoS_Default *tmp = (GF_QoS_Default*)gf_list_get(qos->QoS_Qualifiers, 0); + gf_odf_delete_qos_qual(tmp); + gf_list_rem(qos->QoS_Qualifiers, 0); + } + gf_list_del(qos->QoS_Qualifiers); + return GF_OK; +} + + +// +// Reader +// +GF_Err gf_odf_read_qos(GF_BitStream *bs, GF_QoS_Descriptor *qos, u32 DescSize) +{ + GF_Err e; + GF_QoS_Default *tmp; + u32 tmp_size, nbBytes = 0; + if (!qos) return GF_BAD_PARAM; + + qos->predefined = gf_bs_read_int(bs, 8); + nbBytes += 1; + + if (qos->predefined) { + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; + } + + while (nbBytes < DescSize) { + tmp = NULL; + e = gf_odf_parse_qos(bs, &tmp, &tmp_size); + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = gf_list_add(qos->QoS_Qualifiers, tmp); + if (e) return e; + nbBytes += tmp_size; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + + + +// +// Size +// +GF_Err gf_odf_size_qos(GF_QoS_Descriptor *qos, u32 *outSize) +{ + GF_Err e; + u32 i; + GF_QoS_Default *tmp; + + if (!qos) return GF_BAD_PARAM; + + *outSize = 1; + + i=0; + while ((tmp = (GF_QoS_Default *)gf_list_enum(qos->QoS_Qualifiers, &i))) { + e = gf_odf_size_qos_qual(tmp); + if (e) return e; + *outSize += tmp->size + gf_odf_size_field_size(tmp->size); + } + return GF_OK; +} + +// +// Writer +// +GF_Err gf_odf_write_qos(GF_BitStream *bs, GF_QoS_Descriptor *qos) +{ + GF_Err e; + u32 size, i; + GF_QoS_Default *tmp; + if (!qos) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)qos, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, qos->tag, size); + if (e) return e; + + gf_bs_write_int(bs, qos->predefined, 8); + + if (! qos->predefined) { + i=0; + while ((tmp = (GF_QoS_Default *)gf_list_enum(qos->QoS_Qualifiers, &i))) { + e = gf_odf_write_qos_qual(bs, tmp); + if (e) return e; + } + } + return GF_OK; +} + + diff --git a/src/odf/slc.c b/src/odf/slc.c new file mode 100644 index 0000000..c031107 --- /dev/null +++ b/src/odf/slc.c @@ -0,0 +1,447 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + + +// +// Constructor +// +GF_Descriptor *gf_odf_new_slc(u8 predef) +{ + GF_SLConfig *newDesc = (GF_SLConfig *) malloc(sizeof(GF_SLConfig)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_SLConfig)); + newDesc->tag = GF_ODF_SLC_TAG; + newDesc->predefined = predef; + if (predef) gf_odf_slc_set_pref(newDesc); + newDesc->useTimestampsFlag = 1; + + return (GF_Descriptor *)newDesc; +} + +// +// Destructor +// +GF_Err gf_odf_del_slc(GF_SLConfig *sl) +{ + if (!sl) return GF_BAD_PARAM; + free(sl); + return GF_OK; +} + +// +// Set the SL to the ISO predefined value +// +GF_EXPORT +GF_Err gf_odf_slc_set_pref(GF_SLConfig *sl) +{ + if (! sl) return GF_BAD_PARAM; + + switch (sl->predefined) { + + case SLPredef_MP4: + sl->useAccessUnitStartFlag = 0; + sl->useAccessUnitEndFlag = 0; + //each packet is an AU, and we need RAP signaling + sl->useRandomAccessPointFlag = 1; + sl->hasRandomAccessUnitsOnlyFlag = 0; + sl->usePaddingFlag = 0; + //in MP4 file, we use TimeStamps + sl->useTimestampsFlag = 1; + sl->useIdleFlag = 0; + sl->durationFlag = 0; + sl->timestampLength = 0; + sl->OCRLength = 0; + sl->AULength = 0; + sl->instantBitrateLength = 0; + sl->degradationPriorityLength = 0; + sl->AUSeqNumLength = 0; + sl->packetSeqNumLength = 0; + break; + + case SLPredef_Null: + sl->useAccessUnitStartFlag = 0; + sl->useAccessUnitEndFlag = 0; + sl->useRandomAccessPointFlag = 0; + sl->hasRandomAccessUnitsOnlyFlag = 0; + sl->usePaddingFlag = 0; + sl->useTimestampsFlag = 0; + sl->useIdleFlag = 0; + sl->AULength = 0; + sl->degradationPriorityLength = 0; + sl->AUSeqNumLength = 0; + sl->packetSeqNumLength = 0; + + //for MPEG4 IP + sl->timestampResolution = 1000; + sl->timestampLength = 32; + break; + case SLPredef_SkipSL: + sl->predefined = SLPredef_SkipSL; + break; + /*handle all unknown predefined as predef-null*/ + default: + sl->useAccessUnitStartFlag = 0; + sl->useAccessUnitEndFlag = 0; + sl->useRandomAccessPointFlag = 0; + sl->hasRandomAccessUnitsOnlyFlag = 0; + sl->usePaddingFlag = 0; + sl->useTimestampsFlag = 1; + sl->useIdleFlag = 0; + sl->AULength = 0; + sl->degradationPriorityLength = 0; + sl->AUSeqNumLength = 0; + sl->packetSeqNumLength = 0; + + sl->timestampResolution = 1000; + sl->timestampLength = 32; + break; + } + + return GF_OK; +} + +u32 SLIsPredefined(GF_SLConfig *sl) +{ + if (!sl) return 0; + if (sl->predefined) return sl->predefined; + + if (!sl->useAccessUnitStartFlag + && !sl->useAccessUnitEndFlag + && !sl->usePaddingFlag + && sl->useTimestampsFlag + && !sl->useIdleFlag + && !sl->durationFlag + && !sl->timestampLength + && !sl->OCRLength + && !sl->AULength + && !sl->instantBitrateLength + && !sl->degradationPriorityLength + && !sl->AUSeqNumLength + && !sl->packetSeqNumLength) + return SLPredef_MP4; + + return 0; +} + +//this function gets the real amount of bytes needed to store the timeStamp +static u32 GetTSbytesLen(GF_SLConfig *sl) +{ + u32 TSlen, TSbytes; + if (! sl) return 0; + + TSlen = sl->timestampLength * 2; + TSbytes = TSlen / 8; + TSlen = TSlen % 8; + if (TSlen) TSbytes += 1; + return TSbytes; +} + +// +// Reader +// +GF_Err gf_odf_read_slc(GF_BitStream *bs, GF_SLConfig *sl, u32 DescSize) +{ + GF_Err e; + u32 reserved, nbBytes = 0; + + if (!sl) return GF_BAD_PARAM; + + //APPLE fix + if (!DescSize) { + sl->predefined = SLPredef_MP4; + return gf_odf_slc_set_pref(sl); + } + + sl->predefined = gf_bs_read_int(bs, 8); + nbBytes += 1; + + /*MPEG4 IP fix*/ + if (!sl->predefined && nbBytes==DescSize) { + sl->predefined = SLPredef_Null; + gf_odf_slc_set_pref(sl); + return GF_OK; + } + + if (sl->predefined) { + //predefined configuration + e = gf_odf_slc_set_pref(sl); + if (e) return e; + } else { + sl->useAccessUnitStartFlag = gf_bs_read_int(bs, 1); + sl->useAccessUnitEndFlag = gf_bs_read_int(bs, 1); + sl->useRandomAccessPointFlag = gf_bs_read_int(bs, 1); + sl->hasRandomAccessUnitsOnlyFlag = gf_bs_read_int(bs, 1); + sl->usePaddingFlag = gf_bs_read_int(bs, 1); + sl->useTimestampsFlag = gf_bs_read_int(bs, 1); + sl->useIdleFlag = gf_bs_read_int(bs, 1); + sl->durationFlag = gf_bs_read_int(bs, 1); + sl->timestampResolution = gf_bs_read_int(bs, 32); + sl->OCRResolution = gf_bs_read_int(bs, 32); + + sl->timestampLength = gf_bs_read_int(bs, 8); + if (sl->timestampLength > 64) return GF_ODF_INVALID_DESCRIPTOR; + + sl->OCRLength = gf_bs_read_int(bs, 8); + if (sl->OCRLength > 64) return GF_ODF_INVALID_DESCRIPTOR; + + sl->AULength = gf_bs_read_int(bs, 8); + if (sl->AULength > 32) return GF_ODF_INVALID_DESCRIPTOR; + + sl->instantBitrateLength = gf_bs_read_int(bs, 8); + sl->degradationPriorityLength = gf_bs_read_int(bs, 4); + sl->AUSeqNumLength = gf_bs_read_int(bs, 5); + if (sl->AUSeqNumLength > 16) return GF_ODF_INVALID_DESCRIPTOR; + sl->packetSeqNumLength = gf_bs_read_int(bs, 5); + if (sl->packetSeqNumLength > 16) return GF_ODF_INVALID_DESCRIPTOR; + + reserved = gf_bs_read_int(bs, 2); + nbBytes += 15; + } + + if (sl->durationFlag) { + sl->timeScale = gf_bs_read_int(bs, 32); + sl->AUDuration = gf_bs_read_int(bs, 16); + sl->CUDuration = gf_bs_read_int(bs, 16); + nbBytes += 8; + } + if (! sl->useTimestampsFlag) { + sl->startDTS = gf_bs_read_long_int(bs, sl->timestampLength); + sl->startCTS = gf_bs_read_long_int(bs, sl->timestampLength); + nbBytes += GetTSbytesLen(sl); + } + + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +// +// Size +// +GF_Err gf_odf_size_slc(GF_SLConfig *sl, u32 *outSize) +{ + if (! sl) return GF_BAD_PARAM; + + *outSize = 1; + if (! sl->predefined) *outSize += 15; + if (sl->durationFlag) *outSize += 8; + if (! sl->useTimestampsFlag) *outSize += GetTSbytesLen(sl); + return GF_OK; +} + +// +// Writer +// +GF_Err gf_odf_write_slc(GF_BitStream *bs, GF_SLConfig *sl) +{ + GF_Err e; + u32 size; + if (! sl) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)sl, &size); + e = gf_odf_write_base_descriptor(bs, sl->tag, size); + + gf_bs_write_int(bs, sl->predefined, 8); + if (! sl->predefined) { + gf_bs_write_int(bs, sl->useAccessUnitStartFlag, 1); + gf_bs_write_int(bs, sl->useAccessUnitEndFlag, 1); + gf_bs_write_int(bs, sl->useRandomAccessPointFlag, 1); + gf_bs_write_int(bs, sl->hasRandomAccessUnitsOnlyFlag, 1); + gf_bs_write_int(bs, sl->usePaddingFlag, 1); + gf_bs_write_int(bs, sl->useTimestampsFlag, 1); + gf_bs_write_int(bs, sl->useIdleFlag, 1); + gf_bs_write_int(bs, sl->durationFlag, 1); + gf_bs_write_int(bs, sl->timestampResolution, 32); + gf_bs_write_int(bs, sl->OCRResolution, 32); + gf_bs_write_int(bs, sl->timestampLength, 8); + gf_bs_write_int(bs, sl->OCRLength, 8); + gf_bs_write_int(bs, sl->AULength, 8); + gf_bs_write_int(bs, sl->instantBitrateLength, 8); + gf_bs_write_int(bs, sl->degradationPriorityLength, 4); + gf_bs_write_int(bs, sl->AUSeqNumLength, 5); + gf_bs_write_int(bs, sl->packetSeqNumLength, 5); + gf_bs_write_int(bs, 3, 2); //reserved: 0b11 == 3 + } + if (sl->durationFlag) { + gf_bs_write_int(bs, sl->timeScale, 32); + gf_bs_write_int(bs, sl->AUDuration, 16); + gf_bs_write_int(bs, sl->CUDuration, 16); + } + if (! sl->useTimestampsFlag) { + gf_bs_write_long_int(bs, sl->startDTS, sl->timestampLength); + gf_bs_write_long_int(bs, sl->startCTS, sl->timestampLength); + } + + return GF_OK; +} + + +/*allocates and writes the SL-PDU (Header + PDU) given the SLConfig and the GF_SLHeader +for this PDU. AUs must be split in PDUs by another process if needed (packetizer).*/ +GF_EXPORT +void gf_sl_packetize(GF_SLConfig* slConfig, + GF_SLHeader *Header, + char *PDU, + u32 size, + char **outPacket, + u32 *OutSize) +{ + GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + *OutSize = 0; + if (!bs) return; + + if (slConfig->useAccessUnitStartFlag) gf_bs_write_int(bs, Header->accessUnitStartFlag, 1); + if (slConfig->useAccessUnitEndFlag) gf_bs_write_int(bs, Header->accessUnitEndFlag, 1); + if (slConfig->OCRLength > 0) gf_bs_write_int(bs, Header->OCRflag, 1); + if (slConfig->useIdleFlag) gf_bs_write_int(bs, Header->idleFlag, 1); + if (slConfig->usePaddingFlag) { + gf_bs_write_int(bs, Header->paddingFlag, 1); + if (Header->paddingFlag) gf_bs_write_int(bs, Header->paddingBits, 3); + } + if (! Header->idleFlag && (! Header->paddingFlag || Header->paddingBits != 0)) { + if (slConfig->packetSeqNumLength > 0) gf_bs_write_int(bs, Header->packetSequenceNumber, slConfig->packetSeqNumLength); + if (slConfig->degradationPriorityLength > 0) { + gf_bs_write_int(bs, Header->degradationPriorityFlag, 1); + if (Header->degradationPriorityFlag) gf_bs_write_int(bs, Header->degradationPriority, slConfig->degradationPriorityLength); + } + if (Header->OCRflag) gf_bs_write_long_int(bs, Header->objectClockReference, slConfig->OCRLength); + if (Header->accessUnitStartFlag) { + if (slConfig->useRandomAccessPointFlag) gf_bs_write_int(bs, Header->randomAccessPointFlag, 1); + if (slConfig->AUSeqNumLength > 0) gf_bs_write_int(bs, Header->AU_sequenceNumber, slConfig->AUSeqNumLength); + if (slConfig->useTimestampsFlag) { + gf_bs_write_int(bs, Header->decodingTimeStampFlag, 1); + gf_bs_write_int(bs, Header->compositionTimeStampFlag, 1); + } + if (slConfig->instantBitrateLength > 0) gf_bs_write_int(bs, Header->instantBitrateFlag, 1); + if (Header->decodingTimeStampFlag) gf_bs_write_long_int(bs, Header->decodingTimeStamp, slConfig->timestampLength); + if (Header->compositionTimeStampFlag) gf_bs_write_long_int(bs, Header->compositionTimeStamp, slConfig->timestampLength); + if (slConfig->AULength > 0) gf_bs_write_int(bs, Header->accessUnitLength, slConfig->AULength); + if (Header->instantBitrateFlag) gf_bs_write_int(bs, Header->instantBitrate, slConfig->instantBitrateLength); + } + } + /*done with the Header, Alin*/ + gf_bs_align(bs); + + /*write the PDU - already byte aligned with stuffing (paddingBits in SL Header)*/ + if (PDU && size) + gf_bs_write_data(bs, PDU, size); + + gf_bs_align(bs); + gf_bs_get_content(bs, outPacket, OutSize); + gf_bs_del(bs); +} + +/*allocates and writes the SL-PDU (Header + PDU) given the SLConfig and the GF_SLHeader +for this PDU. AUs must be split in PDUs by another process if needed (packetizer).*/ +GF_EXPORT +u32 gf_sl_get_header_size(GF_SLConfig* slConfig, GF_SLHeader *Header) +{ + u32 nb_bits = 0; + + if (slConfig->useAccessUnitStartFlag) nb_bits++; + if (slConfig->useAccessUnitEndFlag) nb_bits++; + if (slConfig->OCRLength > 0) nb_bits++; + if (slConfig->useIdleFlag) nb_bits++; + if (slConfig->usePaddingFlag) { + nb_bits++; + if (Header->paddingFlag) nb_bits+=3; + } + if (!Header->idleFlag && (! Header->paddingFlag || Header->paddingBits != 0)) { + if (slConfig->packetSeqNumLength > 0) nb_bits += slConfig->packetSeqNumLength; + if (slConfig->degradationPriorityLength > 0) { + nb_bits++; + if (Header->degradationPriorityFlag) nb_bits += slConfig->degradationPriorityLength; + } + if (Header->OCRflag) nb_bits += slConfig->OCRLength; + if (Header->accessUnitStartFlag) { + if (slConfig->useRandomAccessPointFlag) nb_bits++; + if (slConfig->AUSeqNumLength > 0) nb_bits += slConfig->AUSeqNumLength; + if (slConfig->useTimestampsFlag) { + nb_bits++; + nb_bits++; + } + if (slConfig->instantBitrateLength > 0) nb_bits++; + if (Header->decodingTimeStampFlag) nb_bits += slConfig->timestampLength; + if (Header->compositionTimeStampFlag) nb_bits += slConfig->timestampLength; + if (slConfig->AULength > 0) nb_bits += slConfig->AULength; + if (Header->instantBitrateFlag) nb_bits += slConfig->instantBitrateLength; + } + } + while (nb_bits%8) nb_bits++; + return nb_bits/8; +} + + +GF_EXPORT +void gf_sl_depacketize (GF_SLConfig *slConfig, GF_SLHeader *Header, char *PDU, u32 PDULength, u32 *HeaderLen) +{ + GF_BitStream *bs; + *HeaderLen = 0; + if (!Header) return; + //reset the input header + memset(Header, 0, sizeof(GF_SLHeader)); + + bs = gf_bs_new(PDU, PDULength, GF_BITSTREAM_READ); + if (!bs) return; + + if (slConfig->useAccessUnitStartFlag) Header->accessUnitStartFlag = gf_bs_read_int(bs, 1); + if (slConfig->useAccessUnitEndFlag) Header->accessUnitEndFlag = gf_bs_read_int(bs, 1); + if ( !slConfig->useAccessUnitStartFlag && !slConfig->useAccessUnitEndFlag) { + Header->accessUnitStartFlag = 1; + Header->accessUnitEndFlag = 1; + } + if (slConfig->OCRLength > 0) Header->OCRflag = gf_bs_read_int(bs, 1); + if (slConfig->useIdleFlag) Header->idleFlag = gf_bs_read_int(bs, 1); + if (slConfig->usePaddingFlag) { + Header->paddingFlag = gf_bs_read_int(bs, 1); + if (Header->paddingFlag) Header->paddingBits = gf_bs_read_int(bs, 3); + } + if (!Header->paddingFlag || (Header->paddingBits != 0)) { + + if (slConfig->packetSeqNumLength > 0) Header->packetSequenceNumber = gf_bs_read_int(bs, slConfig->packetSeqNumLength); + if (slConfig->degradationPriorityLength > 0) { + Header->degradationPriorityFlag = gf_bs_read_int(bs, 1); + if (Header->degradationPriorityFlag) Header->degradationPriority = gf_bs_read_int(bs, slConfig->degradationPriorityLength); + } + if (Header->OCRflag) Header->objectClockReference = gf_bs_read_int(bs, slConfig->OCRLength); + if (Header->accessUnitStartFlag) { + if (slConfig->useRandomAccessPointFlag) Header->randomAccessPointFlag = gf_bs_read_int(bs, 1); + if (slConfig->AUSeqNumLength > 0) Header->AU_sequenceNumber = gf_bs_read_int(bs, slConfig->AUSeqNumLength); + if (slConfig->useTimestampsFlag) { + Header->decodingTimeStampFlag = gf_bs_read_int(bs, 1); + Header->compositionTimeStampFlag = gf_bs_read_int(bs, 1); + } + if (slConfig->instantBitrateLength > 0) Header->instantBitrateFlag = gf_bs_read_int(bs, 1); + if (Header->decodingTimeStampFlag) Header->decodingTimeStamp = gf_bs_read_long_int(bs, slConfig->timestampLength); + if (Header->compositionTimeStampFlag) Header->compositionTimeStamp = gf_bs_read_long_int(bs, slConfig->timestampLength); + if (slConfig->AULength > 0) Header->accessUnitLength = gf_bs_read_int(bs, slConfig->AULength); + if (Header->instantBitrateFlag) Header->instantBitrate = gf_bs_read_int(bs, slConfig->instantBitrateLength); + } + } + gf_bs_align(bs); + *HeaderLen = (u32) gf_bs_get_position(bs); + gf_bs_del(bs); +} diff --git a/src/scene_manager/encode_cbk.c b/src/scene_manager/encode_cbk.c new file mode 100644 index 0000000..e2af3eb --- /dev/null +++ b/src/scene_manager/encode_cbk.c @@ -0,0 +1,503 @@ +/* + * GPAC Multimedia Framework + * + * Authors: Cyril Concolato - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_READ_ONLY + +#include +#include +#include + +struct __tag_bifs_engine +{ + GF_SceneGraph *sg; + GF_SceneManager *ctx; + GF_SceneLoader load; + void *calling_object; + GF_StreamContext *sc; + Bool owns_context; + GF_BifsEncoder *bifsenc; + u32 stream_ts_res; + /* TODO: maybe the currentAUCount should be a GF_List of u32 + to capture the number of AU per input BIFS stream */ + u32 currentAUCount; + + char encoded_bifs_config[20]; + u32 encoded_bifs_config_size; +}; + +static GF_Err gf_sm_setup_bifsenc(GF_BifsEngine *codec, GF_InitialObjectDescriptor *iod) +{ + GF_Err e; + char *data; + u32 data_len; + u32 j, nbb; + GF_ESD *esd; + Bool is_in_iod, delete_desc, encode_names, delete_bcfg; + GF_BIFSConfig *bcfg; + + e = GF_OK; + codec->bifsenc = gf_bifs_encoder_new(codec->ctx->scene_graph); + + delete_desc = 0; + esd = NULL; + is_in_iod = 1; + if (iod) { + is_in_iod = 0; + j=0; + while ((esd = (GF_ESD*)gf_list_enum(iod->ESDescriptors, &j))) { + if (esd->decoderConfig && esd->decoderConfig->streamType == GF_STREAM_SCENE) { + if (!codec->sc->ESID) codec->sc->ESID = esd->ESID; + if (codec->sc->ESID == esd->ESID) { + is_in_iod = 1; + break; + } + } + /*special BIFS direct import from NHNT*/ + else if (gf_list_count(iod->ESDescriptors)==1) { + codec->sc->ESID = esd->ESID; + is_in_iod = 1; + break; + } + esd = NULL; + } + } + + if (!esd) { + delete_desc = 1; + esd = gf_odf_desc_esd_new(2); + gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = NULL; + esd->ESID = codec->sc->ESID; + esd->decoderConfig->streamType = GF_STREAM_SCENE; + } + + delete_bcfg = 0; + + /*should NOT happen (means inputctx is not properly setup)*/ + if (!esd->decoderConfig->decoderSpecificInfo) { + bcfg = (GF_BIFSConfig*)gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); + bcfg->pixelMetrics = codec->ctx->is_pixel_metrics; + bcfg->pixelWidth = codec->ctx->scene_width; + bcfg->pixelHeight = codec->ctx->scene_height; + delete_bcfg = 1; + } + /*regular retrieve from ctx*/ + else if (esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_BIFS_CFG_TAG) { + bcfg = (GF_BIFSConfig *)esd->decoderConfig->decoderSpecificInfo; + } + /*should not happen either (unless loading from MP4 in which case BIFSc is not decoded)*/ + else { + bcfg = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication); + delete_bcfg = 1; + } + /*NO CHANGE TO BIFSC otherwise the generated update will not match the input context, UNLESS NO NbBits + were specified*/ + nbb = gf_get_bit_size(codec->ctx->max_node_id); + if (!bcfg->nodeIDbits) bcfg->nodeIDbits = nbb; + else if (bcfg->nodeIDbitsnodeIDbits, codec->ctx->max_node_id)); + } + nbb = gf_get_bit_size(codec->ctx->max_route_id); + if (!bcfg->routeIDbits) bcfg->routeIDbits = nbb; + else if (bcfg->routeIDbitsrouteIDbits, codec->ctx->max_route_id)); + } + nbb = gf_get_bit_size(codec->ctx->max_proto_id); + if (!bcfg->protoIDbits) bcfg->protoIDbits=nbb; + else if (bcfg->protoIDbitsprotoIDbits, codec->ctx->max_proto_id)); + } + + /*this is the real pb, not stored in cfg or file level, set at EACH replaceScene*/ + encode_names = 0; + + /* The BIFS Config that is passed here should be the BIFSConfig from the IOD */ + gf_bifs_encoder_new_stream(codec->bifsenc, codec->sc->ESID, bcfg, encode_names, 0); + if (delete_bcfg) gf_odf_desc_del((GF_Descriptor *)bcfg); + + if (!esd->slConfig) esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + if (codec->sc->timeScale) esd->slConfig->timestampResolution = codec->sc->timeScale; + if (!esd->slConfig->timestampResolution) esd->slConfig->timestampResolution = 1000; + + esd->ESID = codec->sc->ESID; + gf_bifs_encoder_get_config(codec->bifsenc, codec->sc->ESID, &data, &data_len); + + if (esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + esd->decoderConfig->decoderSpecificInfo->data = data; + esd->decoderConfig->decoderSpecificInfo->dataLength = data_len; + + codec->stream_ts_res = esd->slConfig->timestampResolution; + memcpy(codec->encoded_bifs_config, data, data_len); + codec->encoded_bifs_config_size = data_len; + + esd->decoderConfig->objectTypeIndication = gf_bifs_encoder_get_version(codec->bifsenc, codec->sc->ESID); + return GF_OK; +} + +static GF_Err gf_sm_live_setup(GF_BifsEngine *codec) +{ + GF_Err e; + GF_InitialObjectDescriptor *iod; + u32 i, count; + + e = GF_OK; + + iod = (GF_InitialObjectDescriptor *) codec->ctx->root_od; + /*if no iod check we only have one bifs*/ + if (!iod) { + count = 0; + i=0; + while ((codec->sc = (GF_StreamContext*)gf_list_enum(codec->ctx->streams, &i))) { + if (codec->sc->streamType == GF_STREAM_OD) count++; + codec->sc = NULL; + } + if (!iod && count>1) return GF_NOT_SUPPORTED; + } + + codec->sc = NULL; + count = gf_list_count(codec->ctx->streams); + i=0; + while ((codec->sc = (GF_StreamContext*)gf_list_enum(codec->ctx->streams, &i))) { + if (codec->sc->streamType == GF_STREAM_SCENE) break; + } + if (!codec->sc) return GF_NOT_SUPPORTED; + if (!codec->sc->ESID) codec->sc->ESID = 1; + + if (codec->sc->objectType <= GPAC_OTI_SCENE_BIFS_V2) { + return gf_sm_setup_bifsenc(codec, iod); + } else if (codec->sc->objectType == GPAC_OTI_SCENE_DIMS) { + /* TODO */ + return GF_NOT_SUPPORTED; + } + return e; +} + +static GF_Err gf_sm_live_encode_scene_au(GF_BifsEngine *codec, u32 currentAUCount, + GF_Err (*AUCallback)(void *, char *, u32 , u64) + ) +{ + GF_Err e; + u32 j, size, count; + char *data; + GF_AUContext *au; + + if (!AUCallback) return GF_BAD_PARAM; + + e = GF_OK; + count = gf_list_count(codec->sc->AUs); + for (j=currentAUCount; jsc->AUs, j); + /*in case using XMT*/ + if (au->timing_sec) au->timing = (u64) (au->timing_sec * codec->stream_ts_res); + switch(codec->sc->objectType) { + case GPAC_OTI_SCENE_BIFS: + case GPAC_OTI_SCENE_BIFS_V2: + e = gf_bifs_encode_au(codec->bifsenc, codec->sc->ESID, au->commands, &data, &size); + break; + case GPAC_OTI_SCENE_DIMS: + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("Cannot encode AU for Scene OTI %x\n", codec->sc->objectType)); + break; + } + AUCallback(codec->calling_object, data, size, au->timing); + free(data); + data = NULL; + if (e) break; + } + return e; +} + +GF_EXPORT +GF_Err gf_beng_aggregate_context(GF_BifsEngine *codec) +{ + GF_Err e; + + /*make random access for storage*/ + e = gf_sm_make_random_access(codec->ctx); + if (e) return GF_BAD_PARAM; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_beng_save_context(GF_BifsEngine *codec, char * ctxFileName) +{ + u32 d_mode, do_enc; + char szF[GF_MAX_PATH], *ext; + GF_Err e; + + /*check if we dump to BT, XMT or encode to MP4*/ + strcpy(szF, ctxFileName); + ext = strrchr(szF, '.'); + d_mode = GF_SM_DUMP_BT; + do_enc = 0; + if (ext) { + if (!stricmp(ext, ".xmt") || !stricmp(ext, ".xmta")) d_mode = GF_SM_DUMP_XMTA; + else if (!stricmp(ext, ".mp4")) do_enc = 1; + ext[0] = 0; + } + + if (do_enc) { + GF_ISOFile *mp4; + strcat(szF, ".mp4"); + mp4 = gf_isom_open(szF, GF_ISOM_OPEN_WRITE, NULL); + e = gf_sm_encode_to_file(codec->ctx, mp4, NULL); + if (e) gf_isom_delete(mp4); + else gf_isom_close(mp4); + } else { + e = gf_sm_dump(codec->ctx, szF, d_mode); + } + return e; +} + +GF_EXPORT +GF_Err gf_beng_encode_from_string(GF_BifsEngine *codec, char *auString, GF_Err (*AUCallback)(void *, char *, u32 , u64 )) +{ + GF_StreamContext *sc; + u32 i, count; + GF_Err e; + + memset(&(codec->load), 0, sizeof(GF_SceneLoader)); + codec->load.ctx = codec->ctx; + + /* Assumes there is only one BIFS stream in the context + TODO: check how to do it when several BIFS streams are encoded at the same time */ + sc = NULL; + count = gf_list_count(codec->ctx->streams); + i = 0; + while ((sc = (GF_StreamContext*)gf_list_enum(codec->ctx->streams, &i))) { + if (sc->streamType == GF_STREAM_SCENE) break; + sc = NULL; + } + if (!sc) return GF_BAD_PARAM; + codec->currentAUCount = gf_list_count(sc->AUs); + + codec->load.flags = GF_SM_LOAD_MPEG4_STRICT | GF_SM_LOAD_CONTEXT_READY; + codec->load.type = GF_SM_LOAD_BT; + + e = gf_sm_load_string(&codec->load, auString, 0); + if (e) goto exit; + + e = gf_sm_live_encode_scene_au(codec, codec->currentAUCount, AUCallback); +exit: + return e; +} + +GF_EXPORT +GF_Err gf_beng_encode_from_file(GF_BifsEngine *codec, char *auFile, GF_Err (*AUCallback)(void *, char *, u32 , u64 )) +{ + GF_Err e; + GF_StreamContext *sc; + u32 i, count; + + memset(&(codec->load), 0, sizeof(GF_SceneLoader)); + codec->load.fileName = auFile; + codec->load.ctx = codec->ctx; + + /* Assumes there is only one BIFS stream in the context + TODO: check how to do it when several BIFS streams are encoded at the same time */ + sc = NULL; + count = gf_list_count(codec->ctx->streams); + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(codec->ctx->streams, &i))) { + if (sc->streamType == GF_STREAM_SCENE) break; + sc = NULL; + } + if (!sc) return GF_BAD_PARAM; + codec->currentAUCount = gf_list_count(sc->AUs); + + codec->load.flags = GF_SM_LOAD_MPEG4_STRICT | GF_SM_LOAD_CONTEXT_READY; + e = gf_sm_load_init(&codec->load); + if (!e) e = gf_sm_load_run(&codec->load); + gf_sm_load_done(&codec->load); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[BENG] cannot load AU File %s (error %s)\n", auFile, gf_error_to_string(e))); + goto exit; + } + + e = gf_sm_live_encode_scene_au(codec, codec->currentAUCount, AUCallback); + if (e) goto exit; +exit: + return e; +} + +GF_EXPORT +GF_Err gf_beng_encode_context(GF_BifsEngine *codec, GF_Err (*AUCallback)(void *, char *, u32 , u64 )) +{ + return gf_sm_live_encode_scene_au(codec, 0, AUCallback); +} + +GF_EXPORT +void gf_beng_terminate(GF_BifsEngine *codec) +{ + if (codec->bifsenc) gf_bifs_encoder_del(codec->bifsenc); + + if (codec->owns_context) { + if (codec->ctx) gf_sm_del(codec->ctx); + if (codec->sg) gf_sg_del(codec->sg); + } + free(codec); +} + +GF_EXPORT +void gf_beng_get_stream_config(GF_BifsEngine *codec, char **config, u32 *config_len) +{ + *config = codec->encoded_bifs_config; + *config_len = codec->encoded_bifs_config_size; +} + + +GF_EXPORT +GF_BifsEngine *gf_beng_init(void *calling_object, char * inputContext) +{ + GF_BifsEngine *codec; + GF_Err e = GF_OK; + + if (!inputContext) return NULL; + + GF_SAFEALLOC(codec, GF_BifsEngine) + if (!codec) return NULL; + + codec->calling_object = calling_object; + + /*Step 1: create context and load input*/ + codec->sg = gf_sg_new(); + codec->ctx = gf_sm_new(codec->sg); + codec->owns_context = 1; + memset(&(codec->load), 0, sizeof(GF_SceneLoader)); + codec->load.ctx = codec->ctx; + /*since we're encoding in BIFS we must get MPEG-4 nodes only*/ + codec->load.flags = GF_SM_LOAD_MPEG4_STRICT; + + codec->load.fileName = inputContext; + e = gf_sm_load_init(&(codec->load)); + if (!e) e = gf_sm_load_run(&(codec->load)); + gf_sm_load_done(&(codec->load)); + + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[BENG] Cannot load context from %s (error %s)\n", inputContext, gf_error_to_string(e))); + goto exit; + } + e = gf_sm_live_setup(codec); + if (e!=GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[BENG] cannot init scene encoder for context (error %s)\n", gf_error_to_string(e))); + goto exit; + } + return codec; + +exit: + gf_beng_terminate(codec); + return NULL; +} + +GF_EXPORT +GF_BifsEngine *gf_beng_init_from_context(void *calling_object, GF_SceneManager *ctx) +{ + GF_BifsEngine *codec; + GF_Err e = GF_OK; + + if (!ctx) return NULL; + + GF_SAFEALLOC(codec, GF_BifsEngine) + if (!codec) return NULL; + + codec->calling_object = calling_object; + + /*Step 1: create context and load input*/ + codec->sg = ctx->scene_graph; + codec->ctx = ctx; + codec->owns_context = 0; + + e = gf_sm_live_setup(codec); + if (e!=GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[BENG] cannot init scene encoder for context (error %s)\n", gf_error_to_string(e))); + goto exit; + } + return codec; + +exit: + gf_beng_terminate(codec); + return NULL; +} + +GF_EXPORT +GF_BifsEngine *gf_beng_init_from_string(void *calling_object, char * inputContext, u32 width, u32 height, Bool usePixelMetrics) +{ + GF_BifsEngine *codec; + GF_Err e = GF_OK; + + if (!inputContext) return NULL; + + GF_SAFEALLOC(codec, GF_BifsEngine) + if (!codec) return NULL; + + codec->calling_object = calling_object; + + /*Step 1: create context and load input*/ + codec->sg = gf_sg_new(); + codec->ctx = gf_sm_new(codec->sg); + codec->owns_context = 1; + memset(&(codec->load), 0, sizeof(GF_SceneLoader)); + codec->load.ctx = codec->ctx; + /*since we're encoding in BIFS we must get MPEG-4 nodes only*/ + codec->load.flags = GF_SM_LOAD_MPEG4_STRICT; + + if (inputContext[0] == '<') { + if (strstr(inputContext, "load.type = GF_SM_LOAD_SVG_DA; + else if (strstr(inputContext, "load.type = GF_SM_LOAD_XSR; + else if (strstr(inputContext, "XMT-A") || strstr(inputContext, "X3D")) codec->load.type = GF_SM_LOAD_XMTA; + } else { + codec->load.type = GF_SM_LOAD_BT; + } + e = gf_sm_load_string(&codec->load, inputContext, 0); + + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[BENG] cannot load context from %s (error %s)\n", inputContext, gf_error_to_string(e))); + goto exit; + } + if (!codec->ctx->root_od) { + codec->ctx->is_pixel_metrics = usePixelMetrics; + codec->ctx->scene_width = width; + codec->ctx->scene_height = height; + } + + e = gf_sm_live_setup(codec); + if (e!=GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[BENG] cannot init scene encoder for context (error %s)\n", gf_error_to_string(e))); + goto exit; + } + return codec; + +exit: + gf_beng_terminate(codec); + return NULL; +} + + +#endif + diff --git a/src/scene_manager/encode_isom.c b/src/scene_manager/encode_isom.c new file mode 100644 index 0000000..2becd5a --- /dev/null +++ b/src/scene_manager/encode_isom.c @@ -0,0 +1,1219 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#ifndef GPAC_DISABLE_SVG +#include +#include +#endif +#include + + +#ifndef GPAC_READ_ONLY + +static GF_MuxInfo *gf_sm_get_mux_info(GF_ESD *src) +{ + u32 i; + GF_MuxInfo *mux; + i=0; + while ((mux = (GF_MuxInfo *)gf_list_enum(src->extensionDescriptors, &i))) { + if (mux->tag == GF_ODF_MUXINFO_TAG) return mux; + } + return NULL; +} + +static void gf_sm_remove_mux_info(GF_ESD *src) +{ + u32 i; + GF_MuxInfo *mux; + i=0; + while ((mux = (GF_MuxInfo *)gf_list_enum(src->extensionDescriptors, &i))) { + if (mux->tag == GF_ODF_MUXINFO_TAG) { + gf_odf_desc_del((GF_Descriptor *)mux); + gf_list_rem(src->extensionDescriptors, i-1); + return; + } + } +} + +static void gf_sm_finalize_mux(GF_ISOFile *mp4, GF_ESD *src, u32 offset_ts) +{ + u32 track, mts, ts; + GF_MuxInfo *mux = gf_sm_get_mux_info(src); + if (!mux && !offset_ts) return; + track = gf_isom_get_track_by_id(mp4, src->ESID); + if (!track) return; + + mts = gf_isom_get_media_timescale(mp4, track); + ts = gf_isom_get_timescale(mp4); + /*set track time offset*/ + if (mux) offset_ts += mux->startTime * mts / 1000; + if (offset_ts) { + u32 off = offset_ts * ts / mts; + u64 dur = gf_isom_get_media_duration(mp4, track); + dur = dur * ts / mts; + gf_isom_set_edit_segment(mp4, track, 0, off, 0, GF_ISOM_EDIT_EMPTY); + gf_isom_set_edit_segment(mp4, track, off, dur, 0, GF_ISOM_EDIT_NORMAL); + } + /*set track interleaving ID*/ + if (mux) { + if (mux->GroupID) gf_isom_set_track_group(mp4, track, mux->GroupID); + if (mux->import_flags & GF_IMPORT_USE_COMPACT_SIZE) + gf_isom_use_compact_size(mp4, track, 1); + } +} + +static GF_Err gf_sm_import_ui_stream(GF_ISOFile *mp4, GF_ESD *src) +{ + GF_UIConfig *cfg; + u32 len, i; + GF_Err e; + if (!src->slConfig) src->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + src->slConfig->predefined = 2; + src->slConfig->timestampResolution = 1000; + if (!src->decoderConfig || !src->decoderConfig->decoderSpecificInfo) return GF_ODF_INVALID_DESCRIPTOR; + if (src->decoderConfig->decoderSpecificInfo->tag == GF_ODF_UI_CFG_TAG) { + cfg = (GF_UIConfig *) src->decoderConfig->decoderSpecificInfo; + e = gf_odf_encode_ui_config(cfg, &src->decoderConfig->decoderSpecificInfo); + gf_odf_desc_del((GF_Descriptor *) cfg); + if (e) return e; + } else if (src->decoderConfig->decoderSpecificInfo->tag != GF_ODF_DSI_TAG) { + return GF_ODF_INVALID_DESCRIPTOR; + } + /*what's the media type for input sensor ??*/ + len = gf_isom_new_track(mp4, src->ESID, GF_ISOM_MEDIA_SCENE, 1000); + if (!len) return gf_isom_last_error(mp4); + gf_isom_set_track_enabled(mp4, len, 1); + if (!src->ESID) src->ESID = gf_isom_get_track_id(mp4, len); + return gf_isom_new_mpeg4_description(mp4, len, src, NULL, NULL, &i); +} + +static GF_Err gf_sm_import_stream(GF_SceneManager *ctx, GF_ISOFile *mp4, GF_ESD *src, char *mediaSource) +{ + u32 track, di; + GF_Err e; + Bool isAudio, isVideo; + char szName[1024]; + char *ext; + GF_MediaImporter import; + GF_MuxInfo *mux = NULL; + + /*no import if URL string*/ + if (src->URLString) { + u32 mtype, track; + if (!src->slConfig) src->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + if (!src->decoderConfig) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISO File Encode] ESD with URL string needs a decoder config with remote stream type to be encoded\n")); + return GF_BAD_PARAM; + } + /*however we still need a track to store the ESD ...*/ + switch (src->decoderConfig->streamType) { + case GF_STREAM_VISUAL: + mtype = GF_ISOM_MEDIA_VISUAL; + break; + case GF_STREAM_AUDIO: + mtype = GF_ISOM_MEDIA_AUDIO; + break; + case GF_STREAM_MPEG7: + mtype = GF_ISOM_MEDIA_MPEG7; + break; + case GF_STREAM_IPMP: + mtype = GF_ISOM_MEDIA_IPMP; + break; + case GF_STREAM_OCI: + mtype = GF_ISOM_MEDIA_OCI; + break; + case GF_STREAM_MPEGJ: + mtype = GF_ISOM_MEDIA_MPEGJ; + break; + case GF_STREAM_INTERACT: + case GF_STREAM_SCENE: + mtype = GF_ISOM_MEDIA_SCENE; + break; + case GF_STREAM_TEXT: + mtype = GF_ISOM_MEDIA_TEXT; + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISO File Encode] Unsupported media type %d for ESD with URL string\n", src->decoderConfig->streamType)); + return GF_BAD_PARAM; + } + track = gf_isom_new_track(mp4, src->ESID, mtype, 1000); + if (!src->ESID) src->ESID = gf_isom_get_track_id(mp4, track); + return gf_isom_new_mpeg4_description(mp4, track, src, NULL, NULL, &di); + } + + /*look for muxInfo*/ + mux = gf_sm_get_mux_info(src); + + /*special streams*/ + if (src->decoderConfig) { + /*InputSensor*/ + if (src->decoderConfig->decoderSpecificInfo && (src->decoderConfig->decoderSpecificInfo->tag == GF_ODF_UI_CFG_TAG)) + src->decoderConfig->streamType = GF_STREAM_INTERACT; + if (src->decoderConfig->streamType == GF_STREAM_INTERACT) return gf_sm_import_ui_stream(mp4, src); + } + + + /*OCR streams*/ + if (src->decoderConfig && src->decoderConfig->streamType == GF_STREAM_OCR) { + track = gf_isom_new_track(mp4, src->ESID, GF_ISOM_MEDIA_OCR, 1000); + if (!track) return gf_isom_last_error(mp4); + gf_isom_set_track_enabled(mp4, track, 1); + if (!src->ESID) src->ESID = gf_isom_get_track_id(mp4, track); + if (!src->slConfig) src->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + src->slConfig->predefined = 2; + e = gf_isom_new_mpeg4_description(mp4, track, src, NULL, NULL, &di); + if (e) return e; + if (mux && mux->duration) + e = gf_isom_set_edit_segment(mp4, track, 0, mux->duration * gf_isom_get_timescale(mp4) / 1000, 0, GF_ISOM_EDIT_NORMAL); + return e; + } + + if (!mux) { + /*if existing don't import (systems tracks)*/ + track = gf_isom_get_track_by_id(mp4, src->ESID); + if (track) return GF_OK; + if (mediaSource) { + memset(&import, 0, sizeof(GF_MediaImporter)); + import.dest = mp4; + import.trackID = src->ESID; + import.orig = gf_isom_open(mediaSource, GF_ISOM_OPEN_READ, NULL); + if (import.orig) { + e = gf_media_import(&import); + gf_isom_delete(import.orig); + return e; + } + } + return GF_OK; + } + + if (!mux->file_name) return GF_OK; + + memset(&import, 0, sizeof(GF_MediaImporter)); + strcpy(szName, mux->file_name); + ext = strrchr(szName, '.'); + + /*get track types for AVI*/ + if (ext && !strnicmp(ext, ".avi", 4)) { + isAudio = isVideo = 0; + if (ext && !stricmp(ext, ".avi#video")) isVideo = 1; + else if (ext && !stricmp(ext, ".avi#audio")) isAudio = 1; + else if (src->decoderConfig) { + if (src->decoderConfig->streamType == GF_STREAM_VISUAL) isVideo = 1; + else if (src->decoderConfig->streamType == GF_STREAM_AUDIO) isAudio = 1; + } + if (!isAudio && !isVideo) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISO File Encode] missing track specifier for AVI import (file#audio, file#video)\n")); + return GF_NOT_SUPPORTED; + } + if (isVideo) import.trackID = 1; + else import.trackID = 2; + ext = strchr(ext, '#'); + if (ext) ext[0] = 0; + } + /*get track ID for MP4/others*/ + if (ext) { + ext = strchr(ext, '#'); + if (ext) { + import.trackID = atoi(ext+1); + ext[0] = 0; + } + } + + import.streamFormat = mux->streamFormat; + import.dest = mp4; + import.esd = src; + import.duration = mux->duration; + import.flags = mux->import_flags; + import.video_fps = mux->frame_rate; + import.in_name = szName; + e = gf_media_import(&import); + if (e) return e; + + /*if desired delete input*/ + if (mux->delete_file) gf_delete_file(mux->file_name); + return e; +} + +static GF_Err gf_sm_import_stream_special(GF_SceneManager *ctx, GF_ESD *esd) +{ + GF_Err e; + GF_MuxInfo *mux = gf_sm_get_mux_info(esd); + if (!mux || !mux->file_name) return GF_OK; + + if (esd->decoderConfig && esd->decoderConfig->decoderSpecificInfo + && (esd->decoderConfig->decoderSpecificInfo->tag==GF_ODF_TEXT_CFG_TAG)) return GF_OK; + + e = GF_OK; + /*SRT/SUB BIFS import if text node unspecified*/ + if (mux->textNode) { + e = gf_sm_import_bifs_subtitle(ctx, esd, mux); + gf_sm_remove_mux_info(esd); + } + return e; +} + +static GF_Err gf_sm_import_specials(GF_SceneManager *ctx) +{ + GF_Err e; + u32 i, j, n, m, k; + GF_ESD *esd; + GF_AUContext *au; + GF_StreamContext *sc; + + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { + if (sc->streamType != GF_STREAM_OD) continue; + esd = NULL; + j=0; + while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) { + GF_ODCom *com; + k=0; + while ((com = (GF_ODCom *) gf_list_enum(au->commands, &k))) { + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + { + GF_ObjectDescriptor *od; + GF_ODUpdate *odU = (GF_ODUpdate *)com; + n=0; + while ((od = (GF_ObjectDescriptor *) gf_list_enum(odU->objectDescriptors, &n))) { + GF_ESD *imp_esd; + m=0; + while ((imp_esd = (GF_ESD*)gf_list_enum(od->ESDescriptors, &m))) { + e = gf_sm_import_stream_special(ctx, imp_esd); + if (e != GF_OK) return e; + } + } + } + break; + case GF_ODF_ESD_UPDATE_TAG: + { + GF_ESD *imp_esd; + GF_ESDUpdate *esdU = (GF_ESDUpdate *)com; + m=0; + while ((imp_esd = (GF_ESD*)gf_list_enum(esdU->ESDescriptors, &m))) { + e = gf_sm_import_stream_special(ctx, imp_esd); + if (e != GF_OK) return e; + } + } + break; + } + } + } + } + return GF_OK; +} + +/*locate stream in all OD updates/ESD updates (needed for systems tracks)*/ +static GF_ESD *gf_sm_locate_esd(GF_SceneManager *ctx, u16 ES_ID) +{ + u32 i, j, n, m, k; + GF_ESD *esd; + GF_AUContext *au; + GF_StreamContext *sc; + if (!ES_ID) return NULL; + + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { + if (sc->streamType != GF_STREAM_OD) continue; + esd = NULL; + j=0; + while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) { + GF_ODCom *com; + k=0; + while ((com = (GF_ODCom *) gf_list_enum(au->commands, &k))) { + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + { + GF_ObjectDescriptor *od; + GF_ODUpdate *odU = (GF_ODUpdate *)com; + n=0; + while ((od = (GF_ObjectDescriptor *) gf_list_enum(odU->objectDescriptors, &n))) { + GF_ESD *imp_esd; + m=0; + while ((imp_esd = (GF_ESD*)gf_list_enum(od->ESDescriptors, &m))) { + if (imp_esd->ESID == ES_ID) return imp_esd; + } + } + } + break; + case GF_ODF_ESD_UPDATE_TAG: + { + GF_ESD *imp_esd; + GF_ESDUpdate *esdU = (GF_ESDUpdate *)com; + m=0; + while ((imp_esd = (GF_ESD*)gf_list_enum(esdU->ESDescriptors, &m))) { + if (imp_esd->ESID == ES_ID) return imp_esd; + } + } + break; + } + } + } + } + return NULL; +} + +static GF_Err gf_sm_encode_scene(GF_SceneManager *ctx, GF_ISOFile *mp4, GF_SMEncodeOptions *opts, u32 scene_type) +{ + char *data; + Bool is_in_iod, delete_desc, first_scene_id; + u32 i, j, di, rate, init_offset, data_len, count, track, rap_delay, flags, rap_mode; + u64 last_rap, dur, time_slice, avg_rate, prev_dts; + GF_Err e; + GF_InitialObjectDescriptor *iod; + GF_AUContext *au; + GF_ISOSample *samp; + GF_StreamContext *sc; + GF_ESD *esd; + GF_BifsEncoder *bifs_enc; +#ifndef GPAC_DISABLE_SVG + GF_LASeRCodec *lsr_enc; +#endif + + rap_mode = 0; + if (opts && opts->rap_freq) { + if (opts->flags & GF_SM_ENCODE_RAP_INBAND) rap_mode = 3; + else if (opts->flags & GF_SM_ENCODE_RAP_SHADOW) rap_mode = 2; + else rap_mode = 1; + } + + e = GF_OK; + iod = (GF_InitialObjectDescriptor *) ctx->root_od; + /*if no iod check we only have one bifs*/ + if (!iod) { + count = 0; + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { + if (sc->streamType == GF_STREAM_OD) count++; + } + if (!iod && count>1) return GF_NOT_SUPPORTED; + } + + count = gf_list_count(ctx->streams); + + sc = NULL; + + flags = opts ? opts->flags : 0; + delete_desc = 0; + first_scene_id = 0; + esd = NULL; + + + /*configure streams*/ + j=0; + for (i=0; istreams, i); + esd = NULL; + if (sc->streamType != GF_STREAM_SCENE) continue; + /*NOT BIFS*/ + if (!scene_type && (sc->objectType > 2) ) continue; + /*NOT LASeR*/ + if (scene_type && (sc->objectType != 0x09) ) continue; + j++; + } + if (!j) { + GF_Node *n = gf_sg_get_root_node(ctx->scene_graph); + if (!n) return GF_OK; +#ifndef GPAC_DISABLE_SVG + if ((scene_type==1) && (gf_node_get_tag(n)!=TAG_SVG_svg) ) return GF_OK; +#endif + if ((scene_type==0) && (gf_node_get_tag(n)>GF_NODE_RANGE_LAST_X3D) ) return GF_OK; + } + + bifs_enc = NULL; +#ifndef GPAC_DISABLE_SVG + lsr_enc = NULL; +#endif + + if (!scene_type) { + bifs_enc = gf_bifs_encoder_new(ctx->scene_graph); + /*no streams defined, encode a RAP*/ + if (!j) { + delete_desc = 0; + esd = NULL; + is_in_iod = 1; + goto force_scene_rap; + } + } + + if (scene_type==1) { +#ifndef GPAC_DISABLE_SVG + lsr_enc = gf_laser_encoder_new(ctx->scene_graph); + /*no streams defined, encode a RAP*/ + if (!j) { + delete_desc = 0; + esd = NULL; + is_in_iod = 1; + goto force_scene_rap; + } +#else + return GF_NOT_SUPPORTED; +#endif + } + + /*configure streams*/ + for (i=0; istreams, i); + esd = NULL; + if (sc->streamType != GF_STREAM_SCENE) continue; + /*NOT BIFS*/ + if (!scene_type && (sc->objectType > 2) ) continue; + /*NOT LASeR*/ + if (scene_type && (sc->objectType != 0x09) ) continue; + + delete_desc = 0; + esd = NULL; + is_in_iod = 1; + if (iod) { + is_in_iod = 0; + j=0; + while ((esd = (GF_ESD*)gf_list_enum(iod->ESDescriptors, &j))) { + if (esd->decoderConfig && esd->decoderConfig->streamType == GF_STREAM_SCENE) { + if (!sc->ESID) sc->ESID = esd->ESID; + if (sc->ESID == esd->ESID) { + is_in_iod = 1; + break; + } + } + /*special BIFS direct import from NHNT*/ + else if (gf_list_count(iod->ESDescriptors)==1) { + sc->ESID = esd->ESID; + is_in_iod = 1; + break; + } + esd = NULL; + } + } + if (!esd && sc->ESID) esd = gf_sm_locate_esd(ctx, sc->ESID); + + au = NULL; + /*special BIFS direct import from NHNT*/ + au = (GF_AUContext*)gf_list_get(sc->AUs, 0); + if (gf_list_count(sc->AUs) == 1) { + if (gf_list_count(au->commands) == 1) { + GF_Command *com = (GF_Command *)gf_list_get(au->commands, 0); + /*no root node, no protos (empty replace) - that's BIFS NHNT import*/ + if ((com->tag == GF_SG_SCENE_REPLACE) && !com->node && !gf_list_count(com->new_proto_list)) + au = NULL; + } + } + /*sanity check: remove first command if it is REPLACE SCENE BY NULL*/ + if (au && !au->timing && !au->timing_sec && (gf_list_count(au->commands) > 1)) { + GF_Command *com = (GF_Command *)gf_list_get(au->commands, 0); + if (com->tag==GF_SG_SCENE_REPLACE) { + if (!com->node && !gf_list_count(com->new_proto_list) ) { + gf_list_rem(au->commands, 0); + gf_sg_command_del(com); + } + } + } + if (!au && !esd->URLString) { + /*if not in IOD, the stream will be imported when encoding the OD stream*/ + if (!is_in_iod) continue; + e = gf_sm_import_stream(ctx, mp4, esd, NULL); + if (e) goto exit; + gf_sm_finalize_mux(mp4, esd, 0); + gf_isom_add_track_to_root_od(mp4, gf_isom_get_track_by_id(mp4, esd->ESID)); + continue; + } + +force_scene_rap: + if (!esd) { + delete_desc = 1; + esd = gf_odf_desc_esd_new(2); + gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = NULL; + esd->ESID = sc ? sc->ESID : 1; + esd->decoderConfig->streamType = GF_STREAM_SCENE; + } + + if (!esd->slConfig) esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + if (sc && sc->timeScale) esd->slConfig->timestampResolution = sc->timeScale; + if (!esd->slConfig->timestampResolution) esd->slConfig->timestampResolution = 1000; + + /*force scene dependencies (we cannot encode in 2 different scene contexts)*/ + if (!esd->dependsOnESID) { + if (!first_scene_id) { + esd->dependsOnESID = 0; + first_scene_id = esd->ESID; + } else { + esd->dependsOnESID = first_scene_id; + } + } + if (!esd->decoderConfig) esd->decoderConfig = (GF_DecoderConfig*)gf_odf_desc_new(GF_ODF_DCD_TAG); + esd->decoderConfig->streamType = GF_STREAM_SCENE; + + /*create track*/ + track = gf_isom_new_track(mp4, sc ? sc->ESID : 1, GF_ISOM_MEDIA_SCENE, esd->slConfig->timestampResolution); + if (!track) { + e = gf_isom_last_error(mp4); + goto exit; + } + gf_isom_set_track_enabled(mp4, track, 1); + if (sc) { + if (!sc->ESID) sc->ESID = gf_isom_get_track_id(mp4, track); + esd->ESID = sc->ESID; + } + + /*BIFS setup*/ + if (!scene_type) { + GF_BIFSConfig *bcfg; + Bool delete_bcfg = 0; + + if (!esd->decoderConfig->decoderSpecificInfo) { + bcfg = (GF_BIFSConfig*)gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); + delete_bcfg = 1; + } else if (esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_BIFS_CFG_TAG) { + bcfg = (GF_BIFSConfig *)esd->decoderConfig->decoderSpecificInfo; + } else { + bcfg = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication); + delete_bcfg = 1; + } + /*update NodeIDbits and co*/ + /*nodeID bits shall include NULL node*/ + if (!bcfg->nodeIDbits || (bcfg->nodeIDbitsmax_node_id)) ) + bcfg->nodeIDbits = gf_get_bit_size(ctx->max_node_id); + + if (!bcfg->routeIDbits || (bcfg->routeIDbits != gf_get_bit_size(ctx->max_route_id)) ) + bcfg->routeIDbits = gf_get_bit_size(ctx->max_route_id); + + if (!bcfg->protoIDbits || (bcfg->protoIDbits != gf_get_bit_size(ctx->max_proto_id)) ) + bcfg->protoIDbits = gf_get_bit_size(ctx->max_proto_id); + + if (!bcfg->elementaryMasks) { + bcfg->pixelMetrics = ctx->is_pixel_metrics; + bcfg->pixelWidth = ctx->scene_width; + bcfg->pixelHeight = ctx->scene_height; + } + if (bcfg->useNames) flags |= GF_SM_ENCODE_USE_NAMES; + + /*this is for safety, otherwise some players may not understand NULL node*/ + if (!bcfg->nodeIDbits) bcfg->nodeIDbits = 1; + gf_bifs_encoder_new_stream(bifs_enc, esd->ESID, bcfg, (flags & GF_SM_ENCODE_USE_NAMES) ? 1 : 0, 0); + if (delete_bcfg) gf_odf_desc_del((GF_Descriptor *)bcfg); + /*create final BIFS config*/ + if (esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + gf_bifs_encoder_get_config(bifs_enc, esd->ESID, &data, &data_len); + esd->decoderConfig->decoderSpecificInfo->data = data; + esd->decoderConfig->decoderSpecificInfo->dataLength = data_len; + esd->decoderConfig->objectTypeIndication = gf_bifs_encoder_get_version(bifs_enc, esd->ESID); + } + /*LASeR setup*/ +#ifndef GPAC_DISABLE_SVG + if (scene_type==1) { + GF_LASERConfig lsrcfg; + + if (!esd->decoderConfig->decoderSpecificInfo) { + memset(&lsrcfg, 0, sizeof(GF_LASERConfig)); + lsrcfg.tag = GF_ODF_LASER_CFG_TAG; + } else if (esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_LASER_CFG_TAG) { + memcpy(&lsrcfg, (GF_LASERConfig *)esd->decoderConfig->decoderSpecificInfo, sizeof(GF_LASERConfig)); + } else { + gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &lsrcfg); + } + /*create final BIFS config*/ + if (esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + + /*this is for safety, otherwise some players may not understand NULL node*/ + if (flags & GF_SM_ENCODE_USE_NAMES) lsrcfg.force_string_ids = 1; + /*override of default*/ + if (opts) { + if (opts->resolution) lsrcfg.resolution = opts->resolution; + if (opts->coord_bits) lsrcfg.coord_bits = opts->coord_bits; + /*by default use 2 extra bits for scale*/ + lsrcfg.scale_bits_minus_coord_bits = opts->scale_bits ? opts->scale_bits : 2; + } + + gf_laser_encoder_new_stream(lsr_enc, esd->ESID , &lsrcfg); + /*get final config*/ + gf_laser_encoder_get_config(lsr_enc, esd->ESID, &data, &data_len); + + esd->decoderConfig->decoderSpecificInfo->data = data; + esd->decoderConfig->decoderSpecificInfo->dataLength = data_len; + esd->decoderConfig->objectTypeIndication = 0x09; + } +#endif + /*create stream description*/ + gf_isom_new_mpeg4_description(mp4, track, esd, NULL, NULL, &di); + if (is_in_iod) { + gf_isom_add_track_to_root_od(mp4, track); + if (ctx->scene_width && ctx->scene_height) + gf_isom_set_visual_info(mp4, track, di, ctx->scene_width, ctx->scene_height); + } + if (esd->URLString) continue; + + if (!sc) { + samp = gf_isom_sample_new(); + samp->IsRAP = 1; + + if (bifs_enc) + e = gf_bifs_encoder_get_rap(bifs_enc, &samp->data, &samp->dataLength); +#ifndef GPAC_DISABLE_SVG + else if (lsr_enc) + e = gf_laser_encoder_get_rap(lsr_enc, &samp->data, &samp->dataLength); +#endif + if (!e && samp->dataLength) e = gf_isom_add_sample(mp4, track, di, samp); + gf_isom_sample_del(&samp); + goto exit; + } + + dur = 0; + avg_rate = 0; + esd->decoderConfig->bufferSizeDB = 0; + esd->decoderConfig->maxBitrate = 0; + rate = 0; + time_slice = 0; + last_rap = 0; + rap_delay = 0; + if (opts) rap_delay = opts->rap_freq * esd->slConfig->timestampResolution / 1000; + + prev_dts = 0; + init_offset = 0; + j=0; + while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) { + samp = gf_isom_sample_new(); + /*time in sec conversion*/ + if (au->timing_sec) au->timing = (u64) (au->timing_sec * esd->slConfig->timestampResolution + 0.0005); + + if (j==1) init_offset = (u32) au->timing; + + samp->DTS = au->timing - init_offset; + if ((j>1) && (samp->DTS == prev_dts)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[OD-SL] Same sample time %d for Access Unit %d and %d\n", au->timing, j, j-1)); + e = GF_BAD_PARAM; + goto exit; + } + samp->IsRAP = au->is_rap; + if (samp->IsRAP) last_rap = au->timing; + + /*inband RAP insertion*/ + if (rap_mode==3) { + if (samp->DTS - last_rap < rap_delay) { + /*first encode command*/ + if (bifs_enc) + e = gf_bifs_encode_au(bifs_enc, sc->ESID, au->commands, &samp->data, &samp->dataLength); +#ifndef GPAC_DISABLE_SVG + else if (lsr_enc) + e = gf_laser_encode_au(lsr_enc, sc->ESID, au->commands, 0, &samp->data, &samp->dataLength); +#endif + + /*and apply commands*/ + e = gf_sg_command_apply_list(ctx->scene_graph, au->commands, 0); + } else { + /*first apply commands*/ + e = gf_sg_command_apply_list(ctx->scene_graph, au->commands, 0); + /*then get RAP*/ + if (bifs_enc) + e = gf_bifs_encoder_get_rap(bifs_enc, &samp->data, &samp->dataLength); +#ifndef GPAC_DISABLE_SVG + else if (lsr_enc) + e = gf_laser_encoder_get_rap(lsr_enc, &samp->data, &samp->dataLength); +#endif + + samp->IsRAP = 1; + last_rap = samp->DTS; + } + } else { + if (bifs_enc) + e = gf_bifs_encode_au(bifs_enc, sc->ESID, au->commands, &samp->data, &samp->dataLength); +#ifndef GPAC_DISABLE_SVG + else if (lsr_enc) + e = gf_laser_encode_au(lsr_enc, sc->ESID, au->commands, 0, &samp->data, &samp->dataLength); +#endif + + } + + + /*carousel generation*/ + if (!e && (rap_mode == 1)) { + if (samp->DTS - last_rap > rap_delay) { + GF_ISOSample *car_samp = gf_isom_sample_new(); + u64 r_dts = samp->DTS; + + /*then get RAP*/ + if (bifs_enc) + e = gf_bifs_encoder_get_rap(bifs_enc, &car_samp->data, &car_samp->dataLength); +#ifndef GPAC_DISABLE_SVG + else if (lsr_enc) + e = gf_laser_encoder_get_rap(lsr_enc, &car_samp->data, &car_samp->dataLength); +#endif + car_samp->IsRAP = 2; + while (1) { + car_samp->DTS = last_rap+rap_delay; + if (car_samp->DTS==prev_dts) car_samp->DTS++; + e = gf_isom_add_sample(mp4, track, di, car_samp); + if (e) break; + last_rap+=rap_delay; + if (last_rap + rap_delay >= r_dts) break; + } + gf_isom_sample_del(&car_samp); + } + if (!e && samp->dataLength) e = gf_isom_add_sample(mp4, track, di, samp); + /*accumulate commmands*/ + e = gf_sg_command_apply_list(ctx->scene_graph, au->commands, 0); + } else { + /*if no commands don't add the AU*/ + if (!e && samp->dataLength) e = gf_isom_add_sample(mp4, track, di, samp); + } + + dur = au->timing; + avg_rate += samp->dataLength; + rate += samp->dataLength; + if (esd->decoderConfig->bufferSizeDBdataLength) esd->decoderConfig->bufferSizeDB = samp->dataLength; + if (samp->DTS - time_slice > esd->slConfig->timestampResolution) { + if (esd->decoderConfig->maxBitrate < rate) esd->decoderConfig->maxBitrate = rate; + rate = 0; + time_slice = samp->DTS; + } + + + prev_dts = samp->DTS; + gf_isom_sample_del(&samp); + if (e) goto exit; + } + + if (dur) { + esd->decoderConfig->avgBitrate = (u32) (avg_rate * esd->slConfig->timestampResolution * 8 / dur); + esd->decoderConfig->maxBitrate *= 8; + } else { + esd->decoderConfig->avgBitrate = 0; + esd->decoderConfig->maxBitrate = 0; + } + gf_isom_change_mpeg4_description(mp4, track, 1, esd); + + /*sync shadow generation*/ + if (rap_mode==2) { + GF_AUContext *au; + u32 au_count = gf_list_count(sc->AUs); + last_rap = 0; + for (j=0; jAUs, j); + e = gf_sg_command_apply_list(ctx->scene_graph, au->commands, 0); + if (!j) continue; + /*force a RAP shadow on last sample*/ + if ((au->timing - last_rap < rap_delay) && (j+1DTS = au->timing - init_offset; + samp->IsRAP = 1; + /*RAP generation*/ + if (bifs_enc) + e = gf_bifs_encoder_get_rap(bifs_enc, &samp->data, &samp->dataLength); + + if (!e) e = gf_isom_add_sample_shadow(mp4, track, samp); + gf_isom_sample_del(&samp); + if (e) goto exit; + } + } + + /*if offset add edit list*/ + gf_sm_finalize_mux(mp4, esd, (u32) init_offset); + gf_isom_set_last_sample_duration(mp4, track, 0); + + if (delete_desc) { + gf_odf_desc_del((GF_Descriptor *) esd); + esd = NULL; + } + } + + /*to do - proper PL setup according to node used...*/ + gf_isom_set_pl_indication(mp4, GF_ISOM_PL_SCENE, 1); + gf_isom_set_pl_indication(mp4, GF_ISOM_PL_GRAPHICS, 1); + +exit: + if (bifs_enc) gf_bifs_encoder_del(bifs_enc); +#ifndef GPAC_DISABLE_SVG + if (lsr_enc) gf_laser_encoder_del(lsr_enc); +#endif + if (esd && delete_desc) gf_odf_desc_del((GF_Descriptor *) esd); + return e; +} + + +static GF_Err gf_sm_encode_od(GF_SceneManager *ctx, GF_ISOFile *mp4, char *mediaSource, GF_SMEncodeOptions *opts) +{ + u32 i, j, n, m, rap_delay; + GF_ESD *esd; + GF_StreamContext *sc; + GF_AUContext *au; + u32 count, track, rate, di; + u64 dur, time_slice, init_offset, avg_rate, last_rap, last_not_shadow, prev_dts; + Bool is_in_iod, delete_desc, rap_inband, rap_shadow; + GF_ISOSample *samp; + GF_Err e; + GF_ODCodec *codec, *rap_codec; + GF_InitialObjectDescriptor *iod; + + gf_isom_set_pl_indication(mp4, GF_ISOM_PL_OD, 0xFE); + + iod = (GF_InitialObjectDescriptor *) ctx->root_od; + count = 0; + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { + if (sc->streamType == GF_STREAM_OD) count++; + } + /*no OD stream, nothing to do*/ + if (!count) return GF_OK; + if (!iod && count>1) return GF_NOT_SUPPORTED; + + + rap_inband = rap_shadow = 0; + rap_delay = 0; + if (opts && opts->rap_freq) { + if (opts->flags & GF_SM_ENCODE_RAP_INBAND) { + rap_inband = 1; + } else { + rap_shadow = 1; + } + } + + esd = NULL; + codec = rap_codec = NULL; + delete_desc = 0; + + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))){ + if (sc->streamType != GF_STREAM_OD) continue; + + delete_desc = 0; + esd = NULL; + is_in_iod = 1; + if (iod) { + is_in_iod = 0; + j=0; + while ((esd = (GF_ESD*)gf_list_enum(iod->ESDescriptors, &j))) { + if (esd->decoderConfig->streamType != GF_STREAM_OD){ + esd = NULL; + continue; + } + if (!sc->ESID) sc->ESID = esd->ESID; + if (sc->ESID == esd->ESID) { + is_in_iod = 1; + break; + } + } + } + if (!esd) esd = gf_sm_locate_esd(ctx, sc->ESID); + if (!esd) { + delete_desc = 1; + esd = gf_odf_desc_esd_new(2); + esd->ESID = sc->ESID; + esd->decoderConfig->objectTypeIndication = 1; + esd->decoderConfig->streamType = GF_STREAM_OD; + } + + /*create OD track*/ + if (!esd->slConfig) esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + if (sc->timeScale) esd->slConfig->timestampResolution = sc->timeScale; + if (!esd->slConfig->timestampResolution) esd->slConfig->timestampResolution = 1000; + track = gf_isom_new_track(mp4, sc->ESID, GF_ISOM_MEDIA_OD, esd->slConfig->timestampResolution); + if (!sc->ESID) sc->ESID = gf_isom_get_track_id(mp4, track); + if (!esd->decoderConfig->objectTypeIndication) esd->decoderConfig->objectTypeIndication = 1; + gf_isom_set_track_enabled(mp4, track, 1); + /*no DSI required*/ + /*create stream description*/ + gf_isom_new_mpeg4_description(mp4, track, esd, NULL, NULL, &di); + /*add to root OD*/ + if (is_in_iod) gf_isom_add_track_to_root_od(mp4, track); + + codec = gf_odf_codec_new(); + + if (rap_inband || rap_shadow) { + rap_codec = gf_odf_codec_new(); + rap_delay = opts->rap_freq * esd->slConfig->timestampResolution / 1000; + } + + + dur = avg_rate = 0; + esd->decoderConfig->bufferSizeDB = 0; + esd->decoderConfig->maxBitrate = 0; + rate = 0; + time_slice = 0; + init_offset = 0; + last_rap = 0; + rap_delay = 0; + last_not_shadow = 0; + prev_dts = 0; + if (opts) rap_delay = opts->rap_freq * esd->slConfig->timestampResolution / 1000; + + /*encode all samples and perform import - FIXME this is destructive...*/ + j=0; + while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) { + + while (gf_list_count(au->commands) ) { + GF_ODCom *com = (GF_ODCom *) gf_list_get(au->commands, 0); + gf_list_rem(au->commands, 0); + /*only updates commandes need to be parsed for import*/ + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + { + GF_ObjectDescriptor *od; + GF_ODUpdate *odU = (GF_ODUpdate *)com; + n=0; + while ((od = (GF_ObjectDescriptor *) gf_list_enum(odU->objectDescriptors, &n))) { + GF_ESD *imp_esd; + m=0; + while ((imp_esd = (GF_ESD*)gf_list_enum(od->ESDescriptors, &m))) { + switch (imp_esd->tag) { + case GF_ODF_ESD_TAG: + e = gf_sm_import_stream(ctx, mp4, imp_esd, mediaSource); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISO File Encode] cannot import stream %d (error %s)\n", imp_esd->ESID, gf_error_to_string(e))); + gf_odf_com_del(&com); + goto err_exit; + } + gf_sm_finalize_mux(mp4, imp_esd, 0); + break; + case GF_ODF_ESD_REF_TAG: + case GF_ODF_ESD_INC_TAG: + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[ISO File Encode] Invalid descriptor in OD%d.ESDescr\n", od->objectDescriptorID)); + e = GF_BAD_PARAM; + goto err_exit; + break; + } + } + } + } + break; + case GF_ODF_ESD_UPDATE_TAG: + { + GF_ESD *imp_esd; + GF_ESDUpdate *esdU = (GF_ESDUpdate *)com; + m=0; + while ((imp_esd = (GF_ESD*)gf_list_enum(esdU->ESDescriptors, &m))) { + switch (imp_esd->tag) { + case GF_ODF_ESD_TAG: + e = gf_sm_import_stream(ctx, mp4, imp_esd, mediaSource); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISO File Encode] cannot import stream %d (error %s)\n", imp_esd->ESID, gf_error_to_string(e))); + gf_odf_com_del(&com); + goto err_exit; + } + gf_sm_finalize_mux(mp4, imp_esd, 0); + break; + case GF_ODF_ESD_REF_TAG: + case GF_ODF_ESD_INC_TAG: + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[ISO File Encode] Invalid descriptor in ESDUpdate (OD %d)\n", esdU->ODID)); + e = GF_BAD_PARAM; + goto err_exit; + break; + } + } + } + break; + } + + /*add to codec*/ + gf_odf_codec_add_com(codec, com); + + if (rap_codec) { + e = gf_odf_codec_apply_com(rap_codec, com); + if (e) goto err_exit; + } + } + + e = gf_odf_codec_encode(codec, 1); + if (e) goto err_exit; + + /*time in sec conversion*/ + if (au->timing_sec) au->timing = (u64) (au->timing_sec * esd->slConfig->timestampResolution + 0.0005); + + if (j==1) init_offset = au->timing; + + samp = gf_isom_sample_new(); + samp->DTS = au->timing - init_offset; + samp->IsRAP = au->is_rap; + + if ((j>1) && (samp->DTS == prev_dts)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[OD-SL] Same sample time %d for Access Unit %d and %d\n", au->timing, j, j-1)); + e = GF_BAD_PARAM; + goto err_exit; + } + + + + last_not_shadow = samp->DTS; + + if (rap_inband && (samp->DTS - last_rap >= rap_delay)) { + last_rap = samp->DTS; + e = gf_odf_codec_encode(rap_codec, 0); + if (e) goto err_exit; + e = gf_odf_codec_get_au(rap_codec, &samp->data, &samp->dataLength); + samp->IsRAP = 1; + } else { + e = gf_odf_codec_get_au(codec, &samp->data, &samp->dataLength); + } + + + if (!e) e = gf_isom_add_sample(mp4, track, di, samp); + + dur = au->timing - init_offset; + avg_rate += samp->dataLength; + rate += samp->dataLength; + if (esd->decoderConfig->bufferSizeDBdataLength) esd->decoderConfig->bufferSizeDB = samp->dataLength; + if (samp->DTS - time_slice > esd->slConfig->timestampResolution) { + if (esd->decoderConfig->maxBitrate < rate) esd->decoderConfig->maxBitrate = rate; + rate = 0; + time_slice = samp->DTS; + } + + + if (rap_shadow && (samp->DTS - last_rap >= rap_delay)) { + last_rap = samp->DTS; + e = gf_odf_codec_encode(rap_codec, 0); + if (e) goto err_exit; + if (samp->data) free(samp->data); + samp->data = NULL; + samp->dataLength = 0; + e = gf_odf_codec_get_au(rap_codec, &samp->data, &samp->dataLength); + if (e) goto err_exit; + samp->IsRAP = 1; + e = gf_isom_add_sample_shadow(mp4, track, samp); + if (e) goto err_exit; + + last_not_shadow = 0; + } + + prev_dts = samp->DTS; + + gf_isom_sample_del(&samp); + if (e) goto err_exit; + } + + if (dur) { + esd->decoderConfig->avgBitrate = (u32) (avg_rate * esd->slConfig->timestampResolution * 8 / dur); + esd->decoderConfig->maxBitrate *= 8; + } else { + esd->decoderConfig->avgBitrate = 0; + esd->decoderConfig->maxBitrate = 0; + } + gf_isom_change_mpeg4_description(mp4, track, 1, esd); + + gf_sm_finalize_mux(mp4, esd, (u32) init_offset); + if (delete_desc) { + gf_odf_desc_del((GF_Descriptor *) esd); + esd = NULL; + } + esd = NULL; + gf_isom_set_last_sample_duration(mp4, track, 0); + + if (rap_codec) { + if (last_not_shadow) { + samp = gf_isom_sample_new(); + samp->DTS = last_not_shadow; + samp->IsRAP = 1; + e = gf_odf_codec_encode(rap_codec, 0); + if (!e) e = gf_odf_codec_get_au(rap_codec, &samp->data, &samp->dataLength); + if (!e) e = gf_isom_add_sample_shadow(mp4, track, samp); + if (e) goto err_exit; + gf_isom_sample_del(&samp); + } + + gf_odf_codec_del(rap_codec); + rap_codec = NULL; + } + } + e = gf_isom_set_pl_indication(mp4, GF_ISOM_PL_OD, 1); + +err_exit: + if (codec) gf_odf_codec_del(codec); + if (rap_codec) gf_odf_codec_del(rap_codec); + if (esd && delete_desc) gf_odf_desc_del((GF_Descriptor *) esd); + return e; +} + +GF_EXPORT +GF_Err gf_sm_encode_to_file(GF_SceneManager *ctx, GF_ISOFile *mp4, GF_SMEncodeOptions *opts) +{ + u32 i, count; + GF_Descriptor *desc; + GF_Err e; + if (!ctx->scene_graph) return GF_BAD_PARAM; + if (ctx->root_od && (ctx->root_od->tag != GF_ODF_IOD_TAG) && (ctx->root_od->tag != GF_ODF_OD_TAG)) return GF_BAD_PARAM; + + /*import specials, that is input remapping to BIFS*/ + e = gf_sm_import_specials(ctx); + if (e) return e; + + + /*encode BIFS*/ + e = gf_sm_encode_scene(ctx, mp4, opts, 0); + if (e) return e; + /*encode LASeR*/ + e = gf_sm_encode_scene(ctx, mp4, opts, 1); + if (e) return e; + /*then encode OD to setup all streams*/ + e = gf_sm_encode_od(ctx, mp4, opts ? opts->mediaSource : NULL, opts); + if (e) return e; + + /*store iod*/ + if (ctx->root_od) { + gf_isom_set_root_od_id(mp4, ctx->root_od->objectDescriptorID); + if (ctx->root_od->URLString) gf_isom_set_root_od_url(mp4, ctx->root_od->URLString); + count = gf_list_count(ctx->root_od->extensionDescriptors); + for (i=0; iroot_od->extensionDescriptors, i); + gf_isom_add_desc_to_root_od(mp4, desc); + } + count = gf_list_count(ctx->root_od->IPMP_Descriptors); + for (i=0; iroot_od->IPMP_Descriptors, i); + gf_isom_add_desc_to_root_od(mp4, desc); + } + count = gf_list_count(ctx->root_od->OCIDescriptors); + for (i=0; iroot_od->OCIDescriptors, i); + gf_isom_add_desc_to_root_od(mp4, desc); + } + if (ctx->root_od->tag==GF_ODF_IOD_TAG) { + GF_InitialObjectDescriptor *iod = (GF_InitialObjectDescriptor*)ctx->root_od; + if (iod->IPMPToolList) gf_isom_add_desc_to_root_od(mp4, (GF_Descriptor *) iod->IPMPToolList); + } + /*we assume all ESs described in bt/xmt input are used*/ + } + + /*set PLs*/ + if (ctx->root_od && ctx->root_od->tag==GF_ODF_IOD_TAG) { + GF_InitialObjectDescriptor *iod = (GF_InitialObjectDescriptor *)ctx->root_od; + gf_isom_set_pl_indication(mp4, GF_ISOM_PL_SCENE, iod->scene_profileAndLevel); + gf_isom_set_pl_indication(mp4, GF_ISOM_PL_GRAPHICS, iod->graphics_profileAndLevel); + } else { + gf_isom_set_pl_indication(mp4, GF_ISOM_PL_SCENE, 0xFE); + gf_isom_set_pl_indication(mp4, GF_ISOM_PL_GRAPHICS, 0xFE); + } + + return GF_OK; +} + +#endif diff --git a/src/scene_manager/loader_bt.c b/src/scene_manager/loader_bt.c new file mode 100644 index 0000000..f069b3e --- /dev/null +++ b/src/scene_manager/loader_bt.c @@ -0,0 +1,3447 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +#include +/*for key codes...*/ +#include + +/*since 0.2.2, we use zlib for bt reading to handle wrl.gz files*/ +#include + + +void gf_sm_load_done_bt(GF_SceneLoader *load); + +#define BT_LINE_SIZE 4000 + +typedef struct +{ + char *name; + char *value; +} BTDefSymbol; + +typedef struct +{ + GF_SceneLoader *load; + gzFile gz_in; + u32 file_size, file_pos; + + /*create from string only*/ + GF_List *top_nodes; + + GF_Err last_error; + u32 line; + + Bool done; + u32 is_wrl; + /*0: no unicode, 1: UTF-16BE, 2: UTF-16LE*/ + u32 unicode_type; + + GF_List *def_symbols; + + /*routes are not created in the graph when parsing, so we need to track insert and delete/replace*/ + GF_List *unresolved_routes, *inserted_routes, *peeked_nodes; + GF_List *undef_nodes, *def_nodes; + + char *line_buffer; + char cur_buffer[500]; + s32 line_size, line_pos, line_start_pos; + + u32 block_comment; + + /*set when parsing proto*/ + GF_Proto *parsing_proto; + Bool is_extern_proto_field; + + /*current stream ID, AU time and RAP flag*/ + u32 stream_id; + u32 au_time; + Bool au_is_rap; + + /*current BIFS stream & AU*/ + GF_StreamContext *bifs_es; + GF_AUContext *bifs_au; + u32 base_bifs_id; + GF_Command *cur_com; + + /*current OD stream & AU*/ + GF_StreamContext *od_es; + GF_AUContext *od_au; + u32 base_od_id; + + GF_List *scripts; +} GF_BTParser; + +GF_Err gf_bt_parse_bifs_command(GF_BTParser *parser, char *name, GF_List *cmdList); +GF_Route *gf_bt_parse_route(GF_BTParser *parser, Bool skip_def, Bool is_insert, GF_Command *com); +void gf_bt_resolve_routes(GF_BTParser *parser, Bool clean); + +static GF_Err gf_bt_report(GF_BTParser *parser, GF_Err e, char *format, ...) +{ +#ifndef GPAC_DISABLE_LOG + if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + char szMsg[2048]; + va_list args; + va_start(args, format); + vsprintf(szMsg, format, args); + va_end(args); + GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_PARSER, ("[BT/WRL Parsing] %s (line %d)\n", szMsg, parser->line)); + } +#endif + if (e) parser->last_error = e; + return e; +} + + +GF_Node *gf_bt_new_node(GF_BTParser *parser, u32 tag) +{ + GF_Node *n = gf_node_new(parser->load->scene_graph, tag); + return n; +} + +void gf_bt_check_line(GF_BTParser *parser) +{ + while (1) { + switch (parser->line_buffer[parser->line_pos]) { + case ' ': + case '\t': + case '\n': + case '\r': + parser->line_pos++; + continue; + default: + break; + } + break; + } + + if (parser->line_buffer[parser->line_pos]=='#') { + parser->line_size = parser->line_pos; + } + else if ((parser->line_buffer[parser->line_pos]=='/') && (parser->line_buffer[parser->line_pos+1]=='/') ) parser->line_size = parser->line_pos; + + if (parser->line_size == parser->line_pos) { + /*string based input - done*/ + if (!parser->gz_in) { + parser->done = 1; + return; + } + +next_line: + parser->line_start_pos = gztell(parser->gz_in); + parser->line_buffer[0] = 0; + if (parser->unicode_type) { + u8 c1, c2; + unsigned short wchar; + unsigned short l[BT_LINE_SIZE]; + unsigned short *dst = l; + Bool is_ret = 0; + u32 last_space_pos, last_space_pos_stream; + u32 go = BT_LINE_SIZE - 1; + last_space_pos = last_space_pos_stream = 0; + while (go && !gzeof(parser->gz_in) ) { + c1 = gzgetc(parser->gz_in); + c2 = gzgetc(parser->gz_in); + /*Little-endian order*/ + if (parser->unicode_type==2) { + if (c2) { wchar = c2; wchar <<=8; wchar |= c1; } + else wchar = c1; + } else { + wchar = c1; + if (c2) { wchar <<= 8; wchar |= c2;} + } + *dst = wchar; + if (wchar=='\r') is_ret = 1; + else if (wchar=='\n') { + dst++; + break; + } + else if (is_ret && wchar!='\n') { + u32 fpos = gztell(parser->gz_in); + gzseek(parser->gz_in, fpos-2, SEEK_SET); + is_ret = 1; + break; + } + if (wchar==' ') { + last_space_pos_stream = gztell(parser->gz_in); + last_space_pos = dst - l; + } + dst++; + go--; + + } + *dst = 0; + /*long line, rewind stream to last space*/ + if (!go) { + u32 rew_pos = gztell(parser->gz_in) - 2*(dst - &l[last_space_pos]); + gzseek(parser->gz_in, rew_pos, SEEK_SET); + l[last_space_pos+1] = 0; + } + /*check eof*/ + if (l[0]==0xFFFF) { + parser->done = 1; + return; + } + /*convert to mbc string*/ + dst = l; + gf_utf8_wcstombs(parser->line_buffer, BT_LINE_SIZE, (const unsigned short **) &dst); + + if (!strlen(parser->line_buffer) && gzeof(parser->gz_in)) { + parser->done = 1; + return; + } + } else { + if ((gzgets(parser->gz_in, parser->line_buffer, BT_LINE_SIZE) == NULL) + || (!strlen(parser->line_buffer) && gzeof(parser->gz_in))) { + parser->done = 1; + return; + } + /*watchout for long lines*/ + if (1 + strlen(parser->line_buffer) == BT_LINE_SIZE) { + u32 rew, pos, go; + rew = 0; + go = 1; + while (go) { + switch (parser->line_buffer[strlen(parser->line_buffer)-1]) { + case ' ': + case ',': + case '[': + case ']': + go = 0; + break; + default: + parser->line_buffer[strlen(parser->line_buffer)-1] = 0; + rew++; + break; + } + } + pos = gztell(parser->gz_in); + gzseek(parser->gz_in, pos-rew, SEEK_SET); + } + } + + + while (1) { + char c; + u32 len = strlen(parser->line_buffer); + if (!len) break; + c = parser->line_buffer[len-1]; + if (!strchr("\n\r\t", c)) break; + parser->line_buffer[len-1] = 0; + } + + + parser->line_size = strlen(parser->line_buffer); + parser->line_pos = 0; + parser->line++; + + { + u32 pos = gztell(parser->gz_in); + if (pos>=parser->file_pos) { + parser->file_pos = pos; + if (parser->line>1) gf_set_progress("BT Parsing", pos, parser->file_size); + } + } + + while ((parser->line_buffer[parser->line_pos]==' ') || (parser->line_buffer[parser->line_pos]=='\t')) + parser->line_pos++; + if ( (parser->line_buffer[parser->line_pos]=='#') + || ( (parser->line_buffer[parser->line_pos]=='/') && (parser->line_buffer[parser->line_pos+1]=='/')) ) { + + if (parser->line==1) { + if (strstr(parser->line_buffer, "VRML")) { + if (strstr(parser->line_buffer, "VRML V2.0")) parser->is_wrl = 1; + /*although not std, many files use this*/ + else if (strstr(parser->line_buffer, "VRML2.0")) parser->is_wrl = 1; + else { + gf_bt_report(parser, GF_NOT_SUPPORTED, "%s: VRML Version Not Supported", parser->line_buffer); + return; + } + } + else if (strstr(parser->line_buffer, "X3D")) { + if (strstr(parser->line_buffer, "X3D V3.0")) parser->is_wrl = 2; + else { + gf_bt_report(parser, GF_NOT_SUPPORTED, "%s: X3D Version Not Supported", parser->line_buffer); + return; + } + } + } + if (!strnicmp(parser->line_buffer+parser->line_pos, "#define ", 8)) { + char *buf, *sep; + parser->line_pos+=8; + buf = parser->line_buffer+parser->line_pos; + sep = strchr(buf, ' '); + if (sep && (sep[1]!='\n') ) { + BTDefSymbol *def; + GF_SAFEALLOC(def, BTDefSymbol); + sep[0] = 0; + def->name = strdup(buf); + sep[0] = ' '; + buf = sep+1; + while (strchr(" \t", buf[0])) buf++; + def->value = strdup(buf); + gf_list_add(parser->def_symbols, def); + } + } + else if (!strnicmp(parser->line_buffer+parser->line_pos, "#if 0", 5)) { + parser->block_comment++; + } + else if (!strnicmp(parser->line_buffer+parser->line_pos, "#endif", 6)) { + if (parser->block_comment) + parser->block_comment--; + } + goto next_line; + } + + if (parser->block_comment) + goto next_line; + + /*brute-force replacement of defined symbols (!!FIXME - no mem checking done !!)*/ + if (parser->line_pos < parser->line_size) { + u32 i, count; + count = gf_list_count(parser->def_symbols); + while (1) { + Bool found = 0; + for (i=0; idef_symbols, i); + char *start = strstr(parser->line_buffer, def->name); + if (!start) continue; + symb_len = strlen(def->name); + if (!strchr(" \n\r\t,[]{}\'\"", start[symb_len])) continue; + val_len = strlen(def->value); + copy_len = strlen(start + symb_len) + 1; + memmove(start + val_len, start + symb_len, sizeof(char)*copy_len); + memcpy(start, def->value, sizeof(char)*val_len); + parser->line_size = strlen(parser->line_buffer); + found = 1; + } + if (!found) break; + } + } + } + if (!parser->line_size) { + if (!gzeof(parser->gz_in)) gf_bt_check_line(parser); + else parser->done = 1; + } + else if (!parser->done && (parser->line_size == parser->line_pos)) gf_bt_check_line(parser); +} + +void gf_bt_force_line(GF_BTParser *parser) +{ + parser->line_pos = parser->line_size; +} + +Bool gf_bt_check_code(GF_BTParser *parser, char code) +{ + gf_bt_check_line(parser); + if (parser->line_buffer[parser->line_pos]==code) { + parser->line_pos++; + return 1; + } + return 0; +} + +char *gf_bt_get_next(GF_BTParser *parser, Bool point_break) +{ + u32 has_quote; + Bool go = 1; + s32 i; + gf_bt_check_line(parser); + i=0; + has_quote = 0; + while (go) { + if (parser->line_buffer[parser->line_pos + i] == '\"') { + if (!has_quote) has_quote = 1; + else has_quote = 0; + parser->line_pos += 1; + + if (parser->line_pos+i==parser->line_size) break; + continue; + } + if (!has_quote) { + switch (parser->line_buffer[parser->line_pos + i]) { + case 0: + case ' ': + case '\t': + case '\r': + case '\n': + case '{': + case '}': + case ']': + case '[': + case ',': + go = 0; + break; + case '.': + if (point_break) go = 0; + break; + } + if (!go) break; + } + parser->cur_buffer[i] = parser->line_buffer[parser->line_pos + i]; + i++; + if (parser->line_pos+i==parser->line_size) break; + } + parser->cur_buffer[i] = 0; + parser->line_pos += i; + return parser->cur_buffer; +} + +char *gf_bt_get_string(GF_BTParser *parser) +{ + char *res; + s32 i, size; + +#define BT_STR_CHECK_ALLOC \ + if (i==size) { \ + res = (char*)realloc(res, sizeof(char) * (size+500)); \ + size += 500; \ + } \ + + res = (char*)malloc(sizeof(char) * 500); + size = 500; + while (parser->line_buffer[parser->line_pos]==' ') parser->line_pos++; + + if (parser->line_pos==parser->line_size) { + if (gzeof(parser->gz_in)) return NULL; + gf_bt_check_line(parser); + } + + i=0; + while (1) { + if (parser->line_buffer[parser->line_pos] == '\"') + if (parser->line_buffer[parser->line_pos-1] != '\\') break; + + BT_STR_CHECK_ALLOC + + if ((parser->line_buffer[parser->line_pos]=='/') && (parser->line_buffer[parser->line_pos+1]=='/') && (parser->line_buffer[parser->line_pos-1]!=':') ) { + /*this looks like a comment*/ + if (!strstr(&parser->line_buffer[parser->line_pos], "\"")) { + gf_bt_check_line(parser); + continue; + } + } + if ((parser->line_buffer[parser->line_pos] != '\\') || (parser->line_buffer[parser->line_pos+1] != '"')) { + /*handle UTF-8 - WARNING: if parser is in unicode string is already utf8 multibyte chars*/ + if (!parser->unicode_type && parser->line_buffer[parser->line_pos] & 0x80) { + char c = parser->line_buffer[parser->line_pos]; + /*non UTF8 (likely some win-CP)*/ + if ( (parser->line_buffer[parser->line_pos+1] & 0xc0) != 0x80) { + res[i] = 0xc0 | ( (parser->line_buffer[parser->line_pos] >> 6) & 0x3 ); + i++; + BT_STR_CHECK_ALLOC + parser->line_buffer[parser->line_pos] &= 0xbf; + } + /*UTF8 2 bytes char*/ + else if ( (c & 0xe0) == 0xc0) { + res[i] = parser->line_buffer[parser->line_pos]; parser->line_pos++; i++; + BT_STR_CHECK_ALLOC + } + /*UTF8 3 bytes char*/ + else if ( (c & 0xf0) == 0xe0) { + res[i] = parser->line_buffer[parser->line_pos]; parser->line_pos++; i++; + BT_STR_CHECK_ALLOC + res[i] = parser->line_buffer[parser->line_pos]; parser->line_pos++; i++; + BT_STR_CHECK_ALLOC + } + /*UTF8 4 bytes char*/ + else if ( (c & 0xf8) == 0xf0) { + res[i] = parser->line_buffer[parser->line_pos]; parser->line_pos++; i++; + BT_STR_CHECK_ALLOC + res[i] = parser->line_buffer[parser->line_pos]; parser->line_pos++; i++; + BT_STR_CHECK_ALLOC + res[i] = parser->line_buffer[parser->line_pos]; parser->line_pos++; i++; + BT_STR_CHECK_ALLOC + } + } + + res[i] = parser->line_buffer[parser->line_pos]; + i++; + } + parser->line_pos++; + if (parser->line_pos==parser->line_size) { + gf_bt_check_line(parser); + } + + } + +#undef BT_STR_CHECK_ALLOC + + res[i] = 0; + parser->line_pos += 1; + return res; +} + +Bool gf_bt_check_externproto_field(GF_BTParser *parser, char *str) +{ + if (!parser->is_extern_proto_field) return 0; + if (!strcmp(str, "") || !strcmp(str, "field") || !strcmp(str, "eventIn") || !strcmp(str, "eventOut") || !strcmp(str, "exposedField")) { + parser->last_error = GF_EOS; + return 1; + } + return 0; +} + +static Bool check_keyword(GF_BTParser *parser, char *str, s32 *val) +{ + s32 res; + char *sep; + sep = strchr(str, '$'); + if (!sep) return 0; + sep++; + if (!strcmp(sep, "F1")) res = GF_KEY_F1; + else if (!strcmp(sep, "F2")) res = GF_KEY_F2; + else if (!strcmp(sep, "F3")) res = GF_KEY_F3; + else if (!strcmp(sep, "F4")) res = GF_KEY_F4; + else if (!strcmp(sep, "F5")) res = GF_KEY_F5; + else if (!strcmp(sep, "F6")) res = GF_KEY_F6; + else if (!strcmp(sep, "F7")) res = GF_KEY_F7; + else if (!strcmp(sep, "F8")) res = GF_KEY_F8; + else if (!strcmp(sep, "F9")) res = GF_KEY_F9; + else if (!strcmp(sep, "F10")) res = GF_KEY_F10; + else if (!strcmp(sep, "F11")) res = GF_KEY_F11; + else if (!strcmp(sep, "F12")) res = GF_KEY_F12; + else if (!strcmp(sep, "HOME")) res = GF_KEY_HOME; + else if (!strcmp(sep, "END")) res = GF_KEY_END; + else if (!strcmp(sep, "PREV")) res = GF_KEY_PAGEUP; + else if (!strcmp(sep, "NEXT")) res = GF_KEY_PAGEDOWN; + else if (!strcmp(sep, "UP")) res = GF_KEY_UP; + else if (!strcmp(sep, "DOWN")) res = GF_KEY_DOWN; + else if (!strcmp(sep, "LEFT")) res = GF_KEY_LEFT; + else if (!strcmp(sep, "RIGHT")) res = GF_KEY_RIGHT; + else if (!strcmp(sep, "RETURN")) res = GF_KEY_ENTER; + else if (!strcmp(sep, "BACK")) res = GF_KEY_BACKSPACE; + else if (!strcmp(sep, "TAB")) res = GF_KEY_TAB; + else if (strlen(sep)==1) { + char c; + sscanf(sep, "%c", &c); + res = c; + } else { + gf_bt_report(parser, GF_OK, "unrecognized keyword %s - skipping", str); + res = 0; + } + if (strchr(str, '-')) *val = -res; + else *val = res; + return 1; +} + +GF_Err gf_bt_parse_float(GF_BTParser *parser, const char *name, Fixed *val) +{ + s32 var; + Float f; + char *str = gf_bt_get_next(parser, 0); + if (!str) return parser->last_error = GF_IO_ERR; + if (gf_bt_check_externproto_field(parser, str)) return GF_OK; + + if (check_keyword(parser, str, &var)) { + *val = INT2FIX(var); + return GF_OK; + } + if (sscanf(str, "%g", &f) != 1) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Number expected", name); + } + *val = FLT2FIX(f); + return GF_OK; +} +GF_Err gf_bt_parse_double(GF_BTParser *parser, const char *name, SFDouble *val) +{ + char *str = gf_bt_get_next(parser, 0); + if (!str) return parser->last_error = GF_IO_ERR; + if (gf_bt_check_externproto_field(parser, str)) return GF_OK; + if (sscanf(str, "%lf", val) != 1) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Number expected", name); + } + return GF_OK; +} +GF_Err gf_bt_parse_int(GF_BTParser *parser, const char *name, SFInt32 *val) +{ + char *str = gf_bt_get_next(parser, 0); + if (!str) return parser->last_error = GF_IO_ERR; + if (gf_bt_check_externproto_field(parser, str)) return GF_OK; + + if (check_keyword(parser, str, val)) return GF_OK; + /*URL ODID*/ + if (!strnicmp(str, "od:", 3)) str += 3; + if (sscanf(str, "%d", val) != 1) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Number expected", name); + } + return GF_OK; +} +GF_Err gf_bt_parse_bool(GF_BTParser *parser, const char *name, SFBool *val) +{ + char *str = gf_bt_get_next(parser, 0); + if (!str) return parser->last_error = GF_IO_ERR; + if (gf_bt_check_externproto_field(parser, str)) return GF_OK; + + if (!stricmp(str, "true") || !strcmp(str, "1") ) { + *val = 1; + } + else if (!stricmp(str, "false") || !strcmp(str, "0") ) { + *val = 0; + } else { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Boolean expected", name); + } + return GF_OK; +} + +GF_Err gf_bt_parse_color(GF_BTParser *parser, const char *name, SFColor *col) +{ + Float f; + char *str = gf_bt_get_next(parser, 0); + if (!str) return parser->last_error = GF_IO_ERR; + if (gf_bt_check_externproto_field(parser, str)) return GF_OK; + + /*HTML code*/ + if (str[0]=='$') { + u32 val; + sscanf(str+1, "%x", &val); + col->red = INT2FIX((val>>16) & 0xFF) / 255; + col->green = INT2FIX((val>>8) & 0xFF) / 255; + col->blue = INT2FIX(val & 0xFF) / 255; + return parser->last_error; + } + if (sscanf(str, "%f", &f) != 1) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Number expected", name); + } + col->red = FLT2FIX(f); + /*many VRML files use ',' separator*/ + gf_bt_check_code(parser, ','); + gf_bt_parse_float(parser, name, & col->green); + gf_bt_check_code(parser, ','); + gf_bt_parse_float(parser, name, & col->blue); + return parser->last_error; +} + +GF_Err gf_bt_parse_colorRGBA(GF_BTParser *parser, const char *name, SFColorRGBA *col) +{ + Float f; + char *str = gf_bt_get_next(parser, 0); + if (!str) return parser->last_error = GF_IO_ERR; + if (gf_bt_check_externproto_field(parser, str)) return GF_OK; + + /*HTML code*/ + if (str[0]=='$') { + u32 val; + sscanf(str, "%x", &val); + col->red = INT2FIX((val>>24) & 0xFF) / 255; + col->green = INT2FIX((val>>16) & 0xFF) / 255; + col->blue = INT2FIX((val>>8) & 0xFF) / 255; + col->alpha = INT2FIX(val & 0xFF) / 255; + return parser->last_error; + } + if (sscanf(str, "%f", &f) != 1) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Number expected", name); + } + col->red = FLT2FIX(f); + gf_bt_check_code(parser, ','); + gf_bt_parse_float(parser, name, & col->green); + gf_bt_check_code(parser, ','); + gf_bt_parse_float(parser, name, & col->blue); + gf_bt_check_code(parser, ','); + gf_bt_parse_float(parser, name, & col->alpha); + return parser->last_error; +} + +static void gf_bt_offset_time(GF_BTParser *parser, Double *time) +{ + if (!parser->is_wrl) { + Double res; + res = parser->au_time; + res /= parser->bifs_es->timeScale; + *time += res; + } +} + +static void gf_bt_check_time_offset(GF_BTParser *parser, GF_Node *n, GF_FieldInfo *info) +{ + if (!n || !(parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK)) return; + if (gf_node_get_tag(n) != TAG_ProtoNode) { + if (!stricmp(info->name, "startTime") || !stricmp(info->name, "stopTime")) + gf_bt_offset_time(parser, (Double *)info->far_ptr); + } else if (gf_sg_proto_field_is_sftime_offset(n, info)) { + gf_bt_offset_time(parser, (Double *)info->far_ptr); + } +} +static void gf_bt_update_timenode(GF_BTParser *parser, GF_Node *node) +{ + if (!node || !(parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK)) return; + + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_AnimationStream: + gf_bt_offset_time(parser, & ((M_AnimationStream*)node)->startTime); + gf_bt_offset_time(parser, & ((M_AnimationStream*)node)->stopTime); + break; + case TAG_MPEG4_AudioBuffer: + gf_bt_offset_time(parser, & ((M_AudioBuffer*)node)->startTime); + gf_bt_offset_time(parser, & ((M_AudioBuffer*)node)->stopTime); + break; + case TAG_MPEG4_AudioClip: + gf_bt_offset_time(parser, & ((M_AudioClip*)node)->startTime); + gf_bt_offset_time(parser, & ((M_AudioClip*)node)->stopTime); + break; + case TAG_MPEG4_AudioSource: + gf_bt_offset_time(parser, & ((M_AudioSource*)node)->startTime); + gf_bt_offset_time(parser, & ((M_AudioSource*)node)->stopTime); + break; + case TAG_MPEG4_MovieTexture: + gf_bt_offset_time(parser, & ((M_MovieTexture*)node)->startTime); + gf_bt_offset_time(parser, & ((M_MovieTexture*)node)->stopTime); + break; + case TAG_MPEG4_TimeSensor: + gf_bt_offset_time(parser, & ((M_TimeSensor*)node)->startTime); + gf_bt_offset_time(parser, & ((M_TimeSensor*)node)->stopTime); + break; + case TAG_ProtoNode: + { + u32 i, nbFields; + GF_FieldInfo inf; + nbFields = gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_ALL); + for (i=0; ifieldType) { + case GF_SG_VRML_SFINT32: + gf_bt_parse_int(parser, info->name, (SFInt32 *)info->far_ptr); + if (parser->last_error) return; + break; + case GF_SG_VRML_SFBOOL: + gf_bt_parse_bool(parser, info->name, (SFBool *)info->far_ptr); + if (parser->last_error) return; + break; + case GF_SG_VRML_SFFLOAT: + gf_bt_parse_float(parser, info->name, (SFFloat *)info->far_ptr); + if (parser->last_error) return; + break; + case GF_SG_VRML_SFDOUBLE: + gf_bt_parse_double(parser, info->name, (SFDouble *)info->far_ptr); + if (parser->last_error) return; + break; + case GF_SG_VRML_SFTIME: + gf_bt_parse_double(parser, info->name, (SFDouble *)info->far_ptr); + if (parser->last_error) return; + gf_bt_check_time_offset(parser, n, info); + break; + case GF_SG_VRML_SFCOLOR: + gf_bt_parse_color(parser, info->name, (SFColor *)info->far_ptr); + break; + case GF_SG_VRML_SFCOLORRGBA: + gf_bt_parse_colorRGBA(parser, info->name, (SFColorRGBA *)info->far_ptr); + break; + case GF_SG_VRML_SFVEC2F: + gf_bt_parse_float(parser, info->name, & ((SFVec2f *)info->far_ptr)->x); + if (parser->last_error) return; + /*many VRML files use ',' separator*/ + gf_bt_check_code(parser, ','); + gf_bt_parse_float(parser, info->name, & ((SFVec2f *)info->far_ptr)->y); + if (parser->last_error) return; + break; + case GF_SG_VRML_SFVEC2D: + gf_bt_parse_double(parser, info->name, & ((SFVec2d *)info->far_ptr)->x); + if (parser->last_error) return; + /*many VRML files use ',' separator*/ + gf_bt_check_code(parser, ','); + gf_bt_parse_double(parser, info->name, & ((SFVec2d *)info->far_ptr)->y); + if (parser->last_error) return; + break; + case GF_SG_VRML_SFVEC3F: + gf_bt_parse_float(parser, info->name, & ((SFVec3f *)info->far_ptr)->x); + if (parser->last_error) return; + /*many VRML files use ',' separator*/ + gf_bt_check_code(parser, ','); + gf_bt_parse_float(parser, info->name, & ((SFVec3f *)info->far_ptr)->y); + if (parser->last_error) return; + /*many VRML files use ',' separator*/ + gf_bt_check_code(parser, ','); + gf_bt_parse_float(parser, info->name, & ((SFVec3f *)info->far_ptr)->z); + if (parser->last_error) return; + break; + case GF_SG_VRML_SFVEC3D: + gf_bt_parse_double(parser, info->name, & ((SFVec3d *)info->far_ptr)->x); + if (parser->last_error) return; + /*many VRML files use ',' separator*/ + gf_bt_check_code(parser, ','); + gf_bt_parse_double(parser, info->name, & ((SFVec3d *)info->far_ptr)->y); + if (parser->last_error) return; + /*many VRML files use ',' separator*/ + gf_bt_check_code(parser, ','); + gf_bt_parse_double(parser, info->name, & ((SFVec3d *)info->far_ptr)->z); + if (parser->last_error) return; + break; + case GF_SG_VRML_SFROTATION: + gf_bt_parse_float(parser, info->name, & ((SFRotation *)info->far_ptr)->x); + if (parser->last_error) return; + gf_bt_parse_float(parser, info->name, & ((SFRotation *)info->far_ptr)->y); + if (parser->last_error) return; + gf_bt_parse_float(parser, info->name, & ((SFRotation *)info->far_ptr)->z); + if (parser->last_error) return; + gf_bt_parse_float(parser, info->name, & ((SFRotation *)info->far_ptr)->q); + if (parser->last_error) return; + break; + case GF_SG_VRML_SFSTRING: + if (gf_bt_check_code(parser, '\"') || gf_bt_check_code(parser, '\'')) { + char *str = gf_bt_get_string(parser); + if (!str) + goto err; + if (((SFString *)info->far_ptr)->buffer) free(((SFString *)info->far_ptr)->buffer); + ((SFString *)info->far_ptr)->buffer = NULL; + if (strlen(str)) + ((SFString *)info->far_ptr)->buffer = str; + else + free(str); + } else { + goto err; + } + break; + case GF_SG_VRML_SFURL: + if (gf_bt_check_code(parser, '\"') || gf_bt_check_code(parser, '\'')) { + SFURL *url = (SFURL *)info->far_ptr; + char *str = gf_bt_get_string(parser); + if (!str) goto err; + if (url->url) free(url->url); + url->url = NULL; + url->OD_ID = 0; + if (strchr(str, '#')) { + url->url = str; + } else { + u32 id = 0; + char *odstr = str; + if (!strnicmp(str, "od:", 3)) odstr += 3; + /*be carefull, an url like "11-regression-test.mp4" will return 1 on sscanf :)*/ + if (sscanf(odstr, "%d", &id) == 1) { + char szURL[20]; + sprintf(szURL, "%d", id); + if (strcmp(szURL, odstr)) id=0; + } + if (id) { + url->OD_ID = id; + free(str); + } else { + url->url = str; + } + } + } else { + s32 val; + gf_bt_parse_int(parser, info->name, & val ); + if (parser->last_error) return; + ((SFURL *)info->far_ptr)->OD_ID = val; + } + break; + case GF_SG_VRML_SFCOMMANDBUFFER: + { + SFCommandBuffer *cb = (SFCommandBuffer *)info->far_ptr; + if (gf_bt_check_code(parser, '{')) { + GF_Command *prev_com = parser->cur_com; + while (!parser->last_error) { + if (gf_bt_check_code(parser, '}')) break; + parser->last_error = gf_bt_parse_bifs_command(parser, NULL, cb->commandList); + } + parser->cur_com = prev_com; + } + } + break; + case GF_SG_VRML_SFIMAGE: + { + u32 i, size, v; + char *str; + SFImage *img = (SFImage *)info->far_ptr; + gf_bt_parse_int(parser, "width", (SFInt32 *)&img->width); + if (parser->last_error) return; + gf_bt_parse_int(parser, "height", (SFInt32 *)&img->height); + if (parser->last_error) return; + gf_bt_parse_int(parser, "nbComp", (SFInt32 *)&v); + if (parser->last_error) return; + img->numComponents = v; + size = img->width * img->height * img->numComponents; + if (img->pixels) free(img->pixels); + img->pixels = (unsigned char*)malloc(sizeof(char) * size); + for (i=0; inumComponents) { + case 1: + img->pixels[i] = (char) v; + break; + case 2: + img->pixels[i] = (char) (v>>8)&0xFF; + img->pixels[i+1] = (char) (v)&0xFF; + i++; + break; + case 3: + img->pixels[i] = (char) (v>>16)&0xFF; + img->pixels[i+1] = (char) (v>>8)&0xFF; + img->pixels[i+2] = (char) (v)&0xFF; + i+=2; + break; + case 4: + img->pixels[i] = (char) (v>>24)&0xFF; + img->pixels[i+1] = (char) (v>>16)&0xFF; + img->pixels[i+2] = (char) (v>>8)&0xFF; + img->pixels[i+3] = (char) (v)&0xFF; + i+=3; + break; + } + } + } + break; + case GF_SG_VRML_SFSCRIPT: + { + SFScript *sc = (SFScript *) info->far_ptr; + if (!gf_bt_check_code(parser, '\"')) { + gf_bt_report(parser, GF_BAD_PARAM, "\" expected in Script"); + } + sc->script_text = (unsigned char*)gf_bt_get_string(parser); + } + break; + default: + parser->last_error = GF_NOT_SUPPORTED; + break; + + } + gf_bt_check_code(parser, ','); + return; +err: + gf_bt_report(parser, GF_BAD_PARAM, "%s: Invalid field syntax", info->name); +} + +void gf_bt_mffield(GF_BTParser *parser, GF_FieldInfo *info, GF_Node *n) +{ + GF_FieldInfo sfInfo; + Bool force_single = 0; + + if (!gf_bt_check_code(parser, '[')) { + if (parser->is_extern_proto_field) return; + force_single = 1; + } + + sfInfo.fieldType = gf_sg_vrml_get_sf_type(info->fieldType); + sfInfo.name = info->name; + gf_sg_vrml_mf_reset(info->far_ptr, info->fieldType); + + while (!gf_bt_check_code(parser, ']')) { + gf_sg_vrml_mf_append(info->far_ptr, info->fieldType, &sfInfo.far_ptr); + gf_bt_sffield(parser, &sfInfo, n); + if (parser->last_error) return; + + gf_bt_check_code(parser, ','); + if (force_single) break; + } +} + +Bool gf_bt_check_ndt(GF_BTParser *parser, GF_FieldInfo *info, GF_Node *node, GF_Node *parent) +{ + if (!node) return 1; + if (parent->sgprivate->tag == TAG_MPEG4_Script) return 1; + if (parent->sgprivate->tag == TAG_X3D_Script) return 1; + if (node->sgprivate->tag == TAG_UndefinedNode) return 1; + + /*this handles undefined nodes*/ + if (gf_node_in_table(node, info->NDTtype)) return 1; + /*not found*/ + gf_bt_report(parser, GF_BAD_PARAM, "node %s not valid in field %s\n", gf_node_get_class_name(node), info->name); + gf_node_unregister(node, parent); + return 0; +} + +u32 gf_bt_get_next_node_id(GF_BTParser *parser) +{ + u32 ID; + GF_SceneGraph *sc = parser->load->scene_graph; + if (parser->parsing_proto) sc = gf_sg_proto_get_graph(parser->parsing_proto); + ID = gf_sg_get_next_available_node_id(sc); + if (parser->load->ctx && (ID>parser->load->ctx->max_node_id)) + parser->load->ctx->max_node_id = ID; + return ID; +} +u32 gf_bt_get_next_route_id(GF_BTParser *parser) +{ + u32 ID; + GF_SceneGraph *sg = parser->load->scene_graph; + if (parser->parsing_proto) sg = gf_sg_proto_get_graph(parser->parsing_proto); + + ID = gf_sg_get_next_available_route_id(sg); + if (parser->load->ctx && (ID>parser->load->ctx->max_route_id)) + parser->load->ctx->max_route_id = ID; + return ID; +} +u32 gf_bt_get_next_proto_id(GF_BTParser *parser) +{ + u32 ID; + GF_SceneGraph *sc = parser->load->scene_graph; + if (parser->parsing_proto) sc = gf_sg_proto_get_graph(parser->parsing_proto); + ID = gf_sg_get_next_available_proto_id(sc); + if (parser->load->ctx && (ID>parser->load->ctx->max_node_id)) + parser->load->ctx->max_proto_id = ID; + return ID; +} + +u32 gf_bt_get_def_id(GF_BTParser *parser, char *defName) +{ + GF_Node *n; + u32 ID; + if (sscanf(defName, "N%d", &ID) == 1) { + ID ++; + n = gf_sg_find_node(parser->load->scene_graph, ID); + /*if an existing node use*/ + if (n) { + u32 id; + u32 nID = gf_bt_get_next_node_id(parser); + const char *name = gf_node_get_name_and_id(n, &id); + gf_bt_report(parser, GF_OK, "changing node \"%s\" Binary ID from %d to %d", name, id -1, nID-1); + gf_node_set_id(n, nID, name); + } + if (parser->load->ctx && (parser->load->ctx->max_node_idload->ctx->max_node_id=ID; + } else { + ID = gf_bt_get_next_node_id(parser); + } + return ID; +} + +Bool gf_bt_set_field_is(GF_BTParser *parser, GF_FieldInfo *info, GF_Node *n) +{ + GF_Err e; + u32 i; + GF_ProtoFieldInterface *pfield; + GF_FieldInfo pinfo; + char *str; + gf_bt_check_line(parser); + i=0; + while ((parser->line_buffer[parser->line_pos + i] == ' ') || (parser->line_buffer[parser->line_pos + i] == '\t')) i++; + if (strnicmp(&parser->line_buffer[parser->line_pos + i] , "IS", 2)) return 0; + + str = gf_bt_get_next(parser, 0); + str = gf_bt_get_next(parser, 0); + + /*that's an ISed field*/ + pfield = gf_sg_proto_field_find_by_name(parser->parsing_proto, str); + if (!pfield) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown proto field", str); + return 1; + } + gf_sg_proto_field_get_field(pfield, &pinfo); + e = gf_sg_proto_field_set_ised(parser->parsing_proto, pinfo.fieldIndex, n, info->fieldIndex); + if (e) gf_bt_report(parser, GF_BAD_PARAM, "IS: Invalid field type for field %s", info->name); + return 1; +} + +void gf_bt_check_unresolved_nodes(GF_BTParser *parser) +{ + u32 i, count; + count = gf_list_count(parser->undef_nodes); + if (!count) return; + for (i=0; iundef_nodes, i); + gf_bt_report(parser, GF_BAD_PARAM, "Cannot find node %s\n", gf_node_get_name(n) ); + } + parser->last_error = GF_BAD_PARAM; +} + +Bool gf_bt_has_been_def(GF_BTParser *parser, char *node_name) +{ + u32 i, count; + count = gf_list_count(parser->def_nodes); + for (i=0; idef_nodes, i); + if (!strcmp(gf_node_get_name(n), node_name)) return 1; + } + return 0; +} + +u32 gf_bt_get_node_tag(GF_BTParser *parser, char *node_name) +{ + u32 tag; + /*if VRML and allowing non MPEG4 nodes, use X3D*/ + if (parser->is_wrl && !(parser->load->flags & GF_SM_LOAD_MPEG4_STRICT)) { + tag = gf_node_x3d_type_by_class_name(node_name); + if (!tag) tag = gf_node_mpeg4_type_by_class_name(node_name); + if (tag) return tag; + if (!strcmp(node_name, "Rectangle")) return TAG_X3D_Rectangle2D; + if (!strcmp(node_name, "Circle")) return TAG_X3D_Circle2D; + } else { + tag = gf_node_mpeg4_type_by_class_name(node_name); + if (!tag) { + if (!strcmp(node_name, "Rectangle2D")) return TAG_MPEG4_Rectangle; + if (!strcmp(node_name, "Circle2D")) return TAG_MPEG4_Circle; + if (!(parser->load->flags & GF_SM_LOAD_MPEG4_STRICT)) return gf_node_x3d_type_by_class_name(node_name); + } + } + return tag; +} + +GF_Node *gf_bt_sf_node(GF_BTParser *parser, char *node_name, GF_Node *parent, char *szDEFName) +{ + u32 tag, ID; + Bool is_script, replace_prev, register_def; + GF_Proto *proto; + GF_Node *node, *newnode, *undef_node; + GF_FieldInfo info; + Bool init_node; + char *name; + char * str; + + init_node = 0; + + if (node_name) { + str = node_name; + } else { + str = gf_bt_get_next(parser, 0); + } + name = NULL; + if (!strcmp(str, "NULL")) return NULL; + + ID = 0; + register_def = 0; + replace_prev = 0; + undef_node = NULL; + if (!strcmp(str, "DEF")) { + register_def = 1; + str = gf_bt_get_next(parser, 0); + name = strdup(str); + str = gf_bt_get_next(parser, 0); + } else if (szDEFName) { + name = strdup(szDEFName); + register_def = 1; + } + if (name) { + undef_node = gf_sg_find_node_by_name(parser->load->scene_graph, name); + if (undef_node) { + gf_list_del_item(parser->peeked_nodes, undef_node); + ID = gf_node_get_id(undef_node); + /*if we see twice a DEF N1 then force creation of a new node*/ + if (gf_bt_has_been_def(parser, name)) { + undef_node = NULL; + ID = gf_bt_get_def_id(parser, name); + gf_bt_report(parser, GF_OK, "Node %s has been DEFed several times, IDs may get corrupted", name); + } + } else { + ID = gf_bt_get_def_id(parser, name); + } + } + else if (!strcmp(str, "USE")) { + str = gf_bt_get_next(parser, 0); + node = gf_sg_find_node_by_name(parser->load->scene_graph, str); + if (!node) { + /*create a temp node (undefined)*/ + node = gf_bt_new_node(parser, TAG_UndefinedNode); + ID = gf_bt_get_def_id(parser, str); + gf_node_set_id(node, ID, str); + gf_list_add(parser->undef_nodes, node); + } + gf_node_register(node, parent); + return node; + } + proto = NULL; + tag = gf_bt_get_node_tag(parser, str); + if (!tag) { + GF_SceneGraph *sg = parser->load->scene_graph; + while (1) { + proto = gf_sg_find_proto(sg, 0, str); + if (proto) break; + sg = sg->parent_scene; + if (!sg) break; + } + if (!proto) { + /*locate proto*/ + gf_bt_report(parser, GF_BAD_PARAM, "%s: not a valid/supported node", str); + return NULL; + } + tag = TAG_ProtoNode; + } + if (undef_node && (undef_node->sgprivate->tag == tag)) { + node = undef_node; + } else { + if (undef_node) replace_prev = 1; + if (proto) { + node = gf_sg_proto_create_instance(parser->load->scene_graph, proto); + } else { + node = gf_bt_new_node(parser, tag); + } + if (!parser->parsing_proto) init_node = 1; + } + is_script = 0; + if ((tag==TAG_MPEG4_Script) || (tag==TAG_X3D_Script)) + is_script = 1; + + if (!node) { + parser->last_error = GF_SG_UNKNOWN_NODE; + return NULL; + } + if (register_def) gf_list_add(parser->def_nodes, node); + + gf_node_register(node, parent); + + /*VRML: "The transformation hierarchy shall be a directed acyclic graph; results are undefined if a node + in the transformation hierarchy is its own ancestor" + that's good, because the scene graph can't handle cyclic graphs (destroy will never be called). + However we still have to register the node before parsing it, to update node registry and get correct IDs*/ + if (name) { + if (!undef_node || replace_prev) { + gf_node_set_id(node, ID, name); + } + free(name); + name = NULL; + } + if (!parser->parsing_proto) gf_bt_update_timenode(parser, node); + + if (gf_bt_check_code(parser, '{')) { + + while (1) { + if (gf_bt_check_code(parser, '}')) + break; + + str = gf_bt_get_next(parser, 0); + if (!str) { + gf_bt_report(parser, GF_BAD_PARAM, "Invalid node syntax"); + goto err; + } + /*VRML/X3D specific */ + if (parser->is_wrl) { + /*we ignore bboxCenter and bboxSize*/ + if (!strcmp(str, "bboxCenter") || !strcmp(str, "bboxSize")) { + Fixed f; + gf_bt_parse_float(parser, "x", &f); + gf_bt_parse_float(parser, "y", &f); + gf_bt_parse_float(parser, "z", &f); + continue; + } + /*some VRML files declare routes almost anywhere*/ + if (!strcmp(str, "ROUTE")) { + gf_bt_parse_route(parser, 1, 0, NULL); + continue; + } + } + + parser->last_error = gf_node_get_field_by_name(node, str, &info); + + /*check common VRML fields removed in MPEG4*/ + if (parser->last_error) { + if (!parser->is_wrl) { + /*we ignore 'solid' for MPEG4 box/cone/etc*/ + if (!strcmp(str, "solid")) { + Bool b; + gf_bt_parse_bool(parser, "solid", &b); + parser->last_error = GF_OK; + continue; + } + /*we ignore 'description' for MPEG4 sensors*/ + else if (!strcmp(str, "description")) { + char *str = gf_bt_get_string(parser); + free(str); + parser->last_error = GF_OK; + continue; + } + /*remaps X3D to old VRML/MPEG4*/ + else if ((tag==TAG_MPEG4_LOD) && !strcmp(str, "children")) { + str = "level"; + parser->last_error = gf_node_get_field_by_name(node, str, &info); + } + else if ((tag==TAG_MPEG4_Switch) && !strcmp(str, "children")) { + str = "choice"; + parser->last_error = gf_node_get_field_by_name(node, str, &info); + } + else if (!strcmp(str, "enabled")) { + Bool b; + gf_bt_parse_bool(parser, "collide", &b); + parser->last_error = GF_OK; + continue; + } + } else { + /*remaps old VRML/MPEG4 to X3D if possible*/ + if ((tag==TAG_X3D_LOD) && !strcmp(str, "level")) { + str = "children"; + parser->last_error = gf_node_get_field_by_name(node, str, &info); + } + else if ((tag==TAG_X3D_Switch) && !strcmp(str, "choice")) { + str = "children"; + parser->last_error = gf_node_get_field_by_name(node, str, &info); + } + else if (!strcmp(str, "collide")) { + Bool b; + gf_bt_parse_bool(parser, "enabled", &b); + parser->last_error = GF_OK; + continue; + } + } + } + + if (is_script && parser->last_error) { + u32 eType, fType; + GF_ScriptField *sf; + eType = 0; + if (!strcmp(str, "eventIn") || !strcmp(str, "inputOnly")) eType = GF_SG_SCRIPT_TYPE_EVENT_IN; + else if (!strcmp(str, "eventOut") || !strcmp(str, "outputOnly")) eType = GF_SG_SCRIPT_TYPE_EVENT_OUT; + else if (!strcmp(str, "field") || !strcmp(str, "initializeOnly")) eType = GF_SG_SCRIPT_TYPE_FIELD; + else { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown script event type", str); + goto err; + } + str = gf_bt_get_next(parser, 0); + fType = gf_sg_field_type_by_name(str); + if (fType==GF_SG_VRML_UNKNOWN) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown script field type", str); + goto err; + } + parser->last_error = GF_OK; + str = gf_bt_get_next(parser, 0); + sf = gf_sg_script_field_new(node, eType, fType, str); + parser->last_error = gf_node_get_field_by_name(node, str, &info); + + if (parser->parsing_proto && gf_bt_set_field_is(parser, &info, node)) continue; + if ((eType == GF_SG_SCRIPT_TYPE_EVENT_IN) || (eType == GF_SG_SCRIPT_TYPE_EVENT_OUT)) continue; + } + + if (parser->last_error) { + gf_bt_report(parser, GF_OK, "%s: Unknown field", str); + goto err; + } + + if (proto) gf_sg_proto_mark_field_loaded(node, &info); + if (parser->parsing_proto && gf_bt_set_field_is(parser, &info, node)) continue; + + switch (info.fieldType) { + case GF_SG_VRML_SFNODE: + /*if redefining node reset it - this happens with CreateVrmlFromString*/ + if (* ((GF_Node **)info.far_ptr) ) { + gf_node_unregister(* ((GF_Node **)info.far_ptr), node); + * ((GF_Node **)info.far_ptr) = NULL; + } + + newnode = gf_bt_sf_node(parser, NULL, node, NULL); + if (!newnode && parser->last_error) goto err; + if (newnode) { + if (!gf_bt_check_ndt(parser, &info, newnode, node)) goto err; + + * ((GF_Node **)info.far_ptr) = newnode; + } + break; + case GF_SG_VRML_MFNODE: + { + GF_ChildNodeItem *last = NULL; + Bool single_child = 0; + if (!gf_bt_check_code(parser, '[')) { + if (parser->is_wrl) single_child = 1; + else break; + } + + /*if redefining node reset it - this happens with CreateVrmlFromString*/ + if (undef_node==node) { + gf_node_unregister_children(node, *(GF_ChildNodeItem **)info.far_ptr); + *(GF_ChildNodeItem **)info.far_ptr = NULL; + } + + while (single_child || !gf_bt_check_code(parser, ']')) { + /*VRML seems to allow that*/ + gf_bt_check_code(parser, ','); + newnode = gf_bt_sf_node(parser, NULL, node, NULL); + if (!newnode && parser->last_error) goto err; + if (newnode) { + if (!gf_bt_check_ndt(parser, &info, newnode, node)) goto err; + gf_node_list_add_child_last( (GF_ChildNodeItem **)info.far_ptr, newnode, &last); + } + if (single_child) break; + } + } + break; + default: + if (gf_sg_vrml_is_sf_field(info.fieldType)) { + gf_bt_sffield(parser, &info, node); + } else { + gf_bt_mffield(parser, &info, node); + } + if (parser->last_error) goto err; + break; + } + /*VRML seems to allow that*/ + gf_bt_check_code(parser, ','); + } + } + /*VRML seems to allow that*/ + gf_bt_check_code(parser, ','); + + /*we must init the node once ID is set in case we're creating rendering stacks*/ + if (init_node && (gf_node_get_tag(node)!=TAG_ProtoNode) ) gf_node_init(node); + + /*remove temp node*/ + if (replace_prev) { + gf_node_replace(undef_node, node, 0); + gf_list_del_item(parser->undef_nodes, undef_node); + } + + if (!parser->parsing_proto && is_script && (parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK) ) { + if (parser->cur_com) { + if (!parser->cur_com->scripts_to_load) parser->cur_com->scripts_to_load = gf_list_new(); + gf_list_add(parser->cur_com->scripts_to_load, node); + } else { + /*postpone script init since it may use routes/nodes not yet defined ...*/ + gf_list_add(parser->scripts, node); + } + } + return node; + +err: + gf_node_unregister(node, parent); + if (name) free(name); + return NULL; +} +/* + locate node, if not defined yet parse ahead in current AU + optimization: we actually peek ALL DEF NODES till end of AU +*/ +GF_Node *gf_bt_peek_node(GF_BTParser *parser, char *defID) +{ + GF_Node *n, *the_node; + u32 tag, ID; + Bool prev_is_insert = 0; + char *str, *ret; + char nName[1000]; + u32 pos, line, line_pos, i, count; + + n = gf_sg_find_node_by_name(parser->load->scene_graph, defID); + if (n) return n; + + count = gf_list_count(parser->peeked_nodes); + for (i=0; ipeeked_nodes, i); + if (!strcmp(gf_node_get_name(n), defID)) return n; + } + + the_node = NULL; + pos = parser->line_start_pos; + line_pos = parser->line_pos; + line = parser->line; + strcpy(nName, defID); + + n = NULL; + while (!parser->done && !the_node) { + str = gf_bt_get_next(parser, 0); + gf_bt_check_code(parser, '['); + gf_bt_check_code(parser, ']'); + gf_bt_check_code(parser, '{'); + gf_bt_check_code(parser, '}'); + gf_bt_check_code(parser, ','); + gf_bt_check_code(parser, '.'); + + if ( (!prev_is_insert && !strcmp(str, "AT")) || !strcmp(str, "PROTO") ) { + /*only check in current command (but be aware of conditionals..)*/ + if (!the_node && gf_list_find(parser->bifs_au->commands, parser->cur_com)) { + gf_bt_report(parser, GF_BAD_PARAM, "Cannot find node %s\n", nName); + break; + } + continue; + } + if (!strcmp(str, "INSERT")) prev_is_insert = 1; + else prev_is_insert = 0; + + if (strcmp(str, "DEF")) continue; + str = gf_bt_get_next(parser, 0); + ret = strdup(str); + str = gf_bt_get_next(parser, 0); + if (!strcmp(str, "ROUTE")) { + free(ret); + continue; + } + + tag = gf_bt_get_node_tag(parser, str); + if (!tag) { + GF_Proto *p; + GF_SceneGraph *sg = parser->load->scene_graph; + while (1) { + p = gf_sg_find_proto(sg, 0, str); + if (p) break; + sg = sg->parent_scene; + if (!sg) break; + } + if (!p) { + /*locate proto*/ + gf_bt_report(parser, GF_BAD_PARAM, "%s: not a valid/supported node", str); + free(ret); + return NULL; + } + n = gf_sg_proto_create_instance(parser->load->scene_graph, p); + } else { + n = gf_bt_new_node(parser, tag); + } + ID = gf_bt_get_def_id(parser, ret); + if (n) { + gf_node_set_id(n, ID, ret); + gf_list_add(parser->peeked_nodes, n); + if (!parser->parsing_proto) gf_node_init(n); + if (!strcmp(ret, nName)) the_node = n; + } + free(ret); + + /*NO REGISTER on peek (both scene graph or DEF list) because peek is only used to get node type + and fields, never to insert in the graph*/ + + /*go on till end of AU*/ + } + /*restore context*/ + parser->done = 0; + gzrewind(parser->gz_in); + gzseek(parser->gz_in, pos, SEEK_SET); + parser->line_pos = parser->line_size; + gf_bt_check_line(parser); + parser->line = line; + parser->line_pos = line_pos; + + return the_node; +} + +u32 gf_bt_get_route(GF_BTParser *parser, char *name) +{ + u32 i; + GF_Command *com; + GF_Route *r = gf_sg_route_find_by_name(parser->load->scene_graph, name); + if (r) return r->ID; + i=0; + while ((com = (GF_Command *)gf_list_enum(parser->inserted_routes, &i))) { + if (com->def_name && !strcmp(com->def_name, name)) return com->RouteID; + } + return 0; +} + +Bool gf_bt_route_id_used(GF_BTParser *parser, u32 ID) +{ + u32 i; + GF_Command *com; + GF_Route *r = gf_sg_route_find(parser->load->scene_graph, ID); + if (r) return 1; + i=0; + while ((com = (GF_Command *)gf_list_enum(parser->inserted_routes, &i))) { + if (com->RouteID == ID) return 1; + } + return 0; +} + +static u32 get_evt_type(char *eventName) +{ + if (!strcmp(eventName, "eventIn") || !strcmp(eventName, "inputOnly")) return GF_SG_EVENT_IN; + else if (!strcmp(eventName, "eventOut") || !strcmp(eventName, "outputOnly")) return GF_SG_EVENT_OUT; + else if (!strcmp(eventName, "field") || !strcmp(eventName, "initializeOnly")) return GF_SG_EVENT_FIELD; + else if (!strcmp(eventName, "exposedField") || !strcmp(eventName, "inputOutput")) return GF_SG_EVENT_EXPOSED_FIELD; + else return GF_SG_EVENT_UNKNOWN; +} + +GF_Err gf_bt_parse_proto(GF_BTParser *parser, char *proto_code, GF_List *proto_list) +{ + GF_FieldInfo info; + u32 fType, eType, QPType, pID; + Bool externProto; + GF_Proto *proto, *prevproto; + GF_ProtoFieldInterface *pfield; + GF_SceneGraph *sg; + char *str, *name; + char szDefName[1024]; + Bool isDEF; + + if (proto_code) + str = proto_code; + else + str = gf_bt_get_next(parser, 0); + + externProto = !strcmp(str, "EXTERNPROTO") ? 1 : 0; + str = gf_bt_get_next(parser, 0); + name = strdup(str); + if (!gf_bt_check_code(parser, '[')) { + return gf_bt_report(parser, GF_BAD_PARAM, "[ expected in proto declare"); + } + pID = gf_bt_get_next_proto_id(parser); + /*if redefinition remove it - WRL only, may be used by loadVRMLFormString*/ + if (!proto_list && parser->is_wrl) { + proto = gf_sg_find_proto(parser->load->scene_graph, pID, name); + if (proto) gf_sg_proto_del(proto); + } + proto = gf_sg_proto_new(parser->load->scene_graph, pID, name, proto_list ? 1 : 0); + if (proto_list) gf_list_add(proto_list, proto); + if (parser->load->ctx && (parser->load->ctx->max_proto_idload->ctx->max_proto_id = pID; + + /*hack for VRML, where externProto default field values are not mandatory*/ + parser->is_extern_proto_field = externProto; + + free(name); + /*get all fields*/ + while (!parser->last_error && !gf_bt_check_code(parser, ']')) { + str = gf_bt_get_next(parser, 0); + +next_field: + if (gf_bt_check_code(parser, ']')) break; + + eType = get_evt_type(str); + if (eType==GF_SG_EVENT_UNKNOWN) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown event type", str); + goto err; + } + str = gf_bt_get_next(parser, 0); + fType = gf_sg_field_type_by_name(str); + if (fType==GF_SG_VRML_UNKNOWN) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown field type", str); + goto err; + } + str = gf_bt_get_next(parser, 0); + pfield = gf_sg_proto_field_new(proto, fType, eType, str); + if ((eType==GF_SG_EVENT_IN) || (eType==GF_SG_EVENT_OUT)) continue; + + gf_sg_proto_field_get_field(pfield, &info); + if (fType==GF_SG_VRML_SFNODE) { + str = gf_bt_get_next(parser, 0); + if (strcmp(str, "NULL")) { + if ( (!strlen(str) || (get_evt_type(str)!=GF_SG_EVENT_UNKNOWN)) && parser->is_extern_proto_field) goto next_field; + pfield->def_sfnode_value = gf_bt_sf_node(parser, str, NULL, NULL); + } + } else if (fType==GF_SG_VRML_MFNODE) { + GF_ChildNodeItem *last = NULL; + if (gf_bt_check_code(parser, '[')) { + while (1) { + GF_Node *pf_node; + if (gf_bt_check_code(parser, ']')) break; + pf_node = gf_bt_sf_node(parser, NULL, NULL, NULL); + if (pf_node) gf_node_list_add_child_last( &pfield->def_mfnode_value, pf_node, &last); + } + } + } else if (gf_sg_vrml_is_sf_field(fType)) { + gf_bt_sffield(parser, &info, NULL); + /*value not specified for externproto*/ + if (parser->last_error==GF_EOS) { + parser->last_error=GF_OK; + goto next_field; + } + } else { + gf_bt_mffield(parser, &info, NULL); + } + /*check QP info*/ + if (!gf_bt_check_code(parser, '{')) continue; + if (gf_bt_check_code(parser, '}')) continue; + str = gf_bt_get_next(parser, 0); + if (!strcmp(str, "QP")) { + u32 nbBits, hasMin; + Fixed ftMin, ftMax; + gf_bt_parse_int(parser, "QPType", (SFInt32*)&QPType); + + nbBits = 0; + str = gf_bt_get_next(parser, 0); + if (!strcmp(str, "nbBits")) { + gf_bt_parse_int(parser, "nbBits", (SFInt32*)&nbBits); + str = gf_bt_get_next(parser, 0); + } + hasMin = 0; + eType = 0; + if (!strcmp(str, "b")) { + hasMin = 1; + if (!gf_bt_check_code(parser, '{')) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Invalid proto coding parameter declare", str); + goto err; + } + gf_bt_parse_float(parser, "min", &ftMin); + gf_bt_parse_float(parser, "max", &ftMax); + if (!gf_bt_check_code(parser, '}')) { + gf_bt_report(parser, GF_BAD_PARAM, "Invalid proto coding parameter declare"); + goto err; + } + if (gf_sg_vrml_get_sf_type(fType) == GF_SG_VRML_SFINT32) { + eType = GF_SG_VRML_SFINT32; + } else { + eType = GF_SG_VRML_SFFLOAT; + } + } + gf_bifs_proto_field_set_aq_info(pfield, QPType, hasMin, eType, &ftMin, &ftMax, nbBits); + if (!gf_bt_check_code(parser, '}')) { + gf_bt_report(parser, GF_BAD_PARAM, "Invalid proto coding parameter declare"); + goto err; + } + } + } + parser->is_extern_proto_field = 0; + + if (externProto) { + SFURL *url; + u32 nb_urls; + Bool has_urls = 0; + if (gf_bt_check_code(parser, '[')) has_urls = 1; + + gf_sg_vrml_mf_reset(&proto->ExternProto, GF_SG_VRML_MFURL); + nb_urls = 0; + do { + str = gf_bt_get_next(parser, 0); + gf_sg_vrml_mf_append(&proto->ExternProto, GF_SG_VRML_MFURL, (void **) &url); + if (!strnicmp(str, "od:", 3)) { + sscanf(str, "od:%d", &url->OD_ID); + } else { + if (!sscanf(str, "%d", &url->OD_ID)) { + url->url = strdup(str); + } else { + char szURL[20]; + sprintf(szURL, "%d", url->OD_ID); + if (strcmp(szURL, str)) { + url->OD_ID = 0; + url->url = strdup(str); + } + } + } + if (has_urls) { + gf_bt_check_code(parser, ','); + if (gf_bt_check_code(parser, ']')) has_urls = 0; + } + } while (has_urls); + return GF_OK; + } + + /*parse proto code */ + if (!gf_bt_check_code(parser, '{')) { + gf_bt_report(parser, GF_OK, "empty proto body"); + return GF_OK; + } + + prevproto = parser->parsing_proto; + sg = parser->load->scene_graph; + parser->parsing_proto = proto; + parser->load->scene_graph = gf_sg_proto_get_graph(proto); + + isDEF = 0; + while (!gf_bt_check_code(parser, '}')) { + str = gf_bt_get_next(parser, 0); + if (!strcmp(str, "PROTO") || !strcmp(str, "EXTERNPROTO")) { + gf_bt_parse_proto(parser, str, NULL); + } else if (!strcmp(str, "DEF")) { + isDEF = 1; + str = gf_bt_get_next(parser, 0); + strcpy(szDefName, str); + } else if (!strcmp(str, "ROUTE")) { + GF_Route *r = gf_bt_parse_route(parser, 1, 0, NULL); + if (isDEF) { + u32 rID = gf_bt_get_route(parser, szDefName); + if (!rID) rID = gf_bt_get_next_route_id(parser); + parser->last_error = gf_sg_route_set_id(r, rID); + gf_sg_route_set_name(r, szDefName); + isDEF = 0; + } + } else { + GF_Node *n = gf_bt_sf_node(parser, str, NULL, isDEF ? szDefName : NULL); + isDEF = 0; + if (!n) goto err; + if (0 && isDEF) { + u32 ID = gf_bt_get_def_id(parser, szDefName); + isDEF = 0; + gf_node_set_id(n, ID, szDefName); + } + gf_sg_proto_add_node_code(proto, n); + } + } + gf_bt_resolve_routes(parser, 1); + gf_bt_check_unresolved_nodes(parser); + parser->load->scene_graph = sg; + parser->parsing_proto = prevproto; + return parser->last_error; + +err: + if (proto_list) gf_list_del_item(proto_list, proto); + gf_sg_proto_del(proto); + return parser->last_error; +} + + +GF_Route *gf_bt_parse_route(GF_BTParser *parser, Bool skip_def, Bool is_insert, GF_Command *com) +{ + GF_Route *r; + char *str, nstr[1000], rName[1000]; + u32 rID; + GF_Node *orig, *dest; + GF_FieldInfo orig_field, dest_field; + GF_Err e; + + rID = 0; + strcpy(nstr, gf_bt_get_next(parser, 1)); + if (!skip_def && !strcmp(nstr, "DEF")) { + str = gf_bt_get_next(parser, 0); + strcpy(rName, str); + rID = gf_bt_get_route(parser, rName); + if (!rID && (str[0]=='R') ) { + rID = atoi(&str[1]); + if (rID) { + rID++; + if (gf_bt_route_id_used(parser, rID)) rID = 0; + } + } + if (!rID) rID = gf_bt_get_next_route_id(parser); + strcpy(nstr, gf_bt_get_next(parser, 1)); + } + orig = gf_bt_peek_node(parser, nstr); + if (!orig) { + gf_bt_report(parser, GF_BAD_PARAM, "cannot find node %s", nstr); + return NULL; + } + if (!gf_bt_check_code(parser, '.')) { + gf_bt_report(parser, GF_BAD_PARAM, ". expected in route decl"); + return NULL; + } + str = gf_bt_get_next(parser, 0); + e = gf_node_get_field_by_name(orig, str, &orig_field); + /*VRML loosy syntax*/ + if ((e != GF_OK) && parser->is_wrl && !strnicmp(str, "set_", 4)) + e = gf_node_get_field_by_name(orig, &str[4], &orig_field); + + if ((e != GF_OK) && parser->is_wrl && strstr(str, "_changed")) { + char *s = strstr(str, "_changed"); + s[0] = 0; + e = gf_node_get_field_by_name(orig, str, &orig_field); + } + + if (e != GF_OK) { + gf_bt_report(parser, GF_BAD_PARAM, "%s not a field of node %s (%s)", str, gf_node_get_name(orig), gf_node_get_class_name(orig)); + return NULL; + } + str = gf_bt_get_next(parser, 0); + if (strcmp(str, "TO")) { + gf_bt_report(parser, GF_BAD_PARAM, "TO expected in route declaration - got \"%s\"", str); + return NULL; + } + + strcpy(nstr, gf_bt_get_next(parser, 1)); + dest = gf_bt_peek_node(parser, nstr); + if (!dest) { + gf_bt_report(parser, GF_BAD_PARAM, "cannot find node %s", nstr); + return NULL; + } + if (!gf_bt_check_code(parser, '.')) { + gf_bt_report(parser, GF_BAD_PARAM, ". expected in route decl"); + return NULL; + } + str = gf_bt_get_next(parser, 0); + e = gf_node_get_field_by_name(dest, str, &dest_field); + /*VRML loosy syntax*/ + if ((e != GF_OK) && parser->is_wrl && !strnicmp(str, "set_", 4)) + e = gf_node_get_field_by_name(dest, &str[4], &dest_field); + + if ((e != GF_OK) && parser->is_wrl && strstr(str, "_changed")) { + char *s = strstr(str, "_changed"); + s[0] = 0; + e = gf_node_get_field_by_name(dest, str, &dest_field); + } + + if (e != GF_OK) { + gf_bt_report(parser, GF_BAD_PARAM, "%s not a field of node %s (%s)", str, gf_node_get_name(dest), gf_node_get_class_name(dest)); + return NULL; + } + if (com) { + com->fromNodeID = gf_node_get_id(orig); + com->fromFieldIndex = orig_field.fieldIndex; + com->toNodeID = gf_node_get_id(dest); + com->toFieldIndex = dest_field.fieldIndex; + if (rID) { + com->RouteID = rID; + com->def_name = strdup(rName); + /*whenever inserting routes, keep track of max defined ID*/ + if (is_insert) { + gf_sg_set_max_defined_route_id(parser->load->scene_graph, rID); + if (parser->load->ctx && (rID>parser->load->ctx->max_route_id)) + parser->load->ctx->max_route_id = rID; + } + } + return NULL; + } + r = gf_sg_route_new(parser->load->scene_graph, orig, orig_field.fieldIndex, dest, dest_field.fieldIndex); + if (r && rID) { + gf_sg_route_set_id(r, rID); + gf_sg_route_set_name(r, rName); + } + return r; +} + +void gf_bt_resolve_routes(GF_BTParser *parser, Bool clean) +{ + GF_Command *com; + /*resolve all commands*/ + while(gf_list_count(parser->unresolved_routes) ) { + com = (GF_Command *)gf_list_get(parser->unresolved_routes, 0); + gf_list_rem(parser->unresolved_routes, 0); + switch (com->tag) { + case GF_SG_ROUTE_DELETE: + case GF_SG_ROUTE_REPLACE: + com->RouteID = gf_bt_get_route(parser, com->unres_name); + if (!com->RouteID) gf_bt_report(parser, GF_BAD_PARAM, "Cannot resolve Route %s", com->unres_name); + free(com->unres_name); + com->unres_name = NULL; + com->unresolved = 0; + break; + } + } + + if (!clean) return; + while (gf_list_count(parser->inserted_routes)) gf_list_rem(parser->inserted_routes, 0); +} + + +static void bd_set_com_node(GF_Command *com, GF_Node *node) +{ + com->node = node; + gf_node_register(com->node, NULL); +} + +GF_Err gf_bt_parse_bifs_command(GF_BTParser *parser, char *name, GF_List *cmdList) +{ + s32 pos; + GF_Route *r; + GF_Node *n, *newnode, *opnode; + GF_Command *com; + GF_CommandField *inf; + GF_FieldInfo info; + char *str, field[1000], *sep; + if (!name) { + str = gf_bt_get_next(parser, 0); + } else { + str = name; + } + com = NULL; + pos = -2; + /*REPLACE commands*/ + if (!strcmp(str, "REPLACE")) { + Bool is_op = 0; + str = gf_bt_get_next(parser, 1); + if (!strcmp(str, "ROUTE")) { + str = gf_bt_get_next(parser, 0); + r = gf_sg_route_find_by_name(parser->load->scene_graph, str); + if (!r) strcpy(field, str); + str = gf_bt_get_next(parser, 0); + if (strcmp(str, "BY")) { + return gf_bt_report(parser, GF_BAD_PARAM, "BY expected got %s", str); + } + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_ROUTE_REPLACE); + if (r) { + com->RouteID = r->ID; + } else { + com->unres_name = strdup(field); + com->unresolved = 1; + gf_list_add(parser->unresolved_routes, com); + } + gf_bt_parse_route(parser, 1, 0, com); + gf_list_add(cmdList, com); + return parser->last_error; + } + /*scene replace*/ + if (!strcmp(str, "SCENE")) { + str = gf_bt_get_next(parser, 0); + if (strcmp(str, "BY")) { + return gf_bt_report(parser, GF_BAD_PARAM, "BY expected got %s", str); + } + gf_bt_resolve_routes(parser, 1); + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_SCENE_REPLACE); + while (gf_list_count(parser->def_nodes)) gf_list_rem(parser->def_nodes, 0); + + while (1) { + str = gf_bt_get_next(parser, 0); + if (!strcmp(str, "PROTO") || !strcmp(str, "EXTERNPROTO")) { + gf_bt_parse_proto(parser, str, com->new_proto_list); + if (parser->last_error) goto err; + } else { + break; + } + } + n = gf_bt_sf_node(parser, str, NULL, NULL); + com->node = n; + + if (parser->last_error) goto err; + gf_list_add(cmdList, com); + parser->cur_com = com; + return GF_OK; + } + if (!strcmp(str, "LAST")) pos = -1; + else if (!strcmp(str, "BEGIN")) pos = 0; + + gf_bt_check_code(parser, '.'); + strcpy(field, str); + n = gf_bt_peek_node(parser, str); + if (!n) return gf_bt_report(parser, GF_BAD_PARAM, "%s: unknown node", field); + + str = gf_bt_get_next(parser, 0); + strcpy(field, str); + if (gf_bt_check_code(parser, '[')) { + if ( (parser->last_error = gf_bt_parse_int(parser, "index", &pos)) ) return parser->last_error; + if (!gf_bt_check_code(parser, ']')) + return gf_bt_report(parser, GF_BAD_PARAM, "] expected"); + } + /*node replace*/ + if (!strcmp(field, "BY")) { + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_NODE_REPLACE); + bd_set_com_node(com, n); + inf = gf_sg_command_field_new(com); + inf->new_node = gf_bt_sf_node(parser, NULL, NULL, NULL); + inf->fieldType = GF_SG_VRML_SFNODE; + inf->field_ptr = &inf->new_node; + gf_list_add(cmdList, com); + parser->cur_com = com; + return parser->last_error; + } + str = gf_bt_get_next(parser, 0); + if (!strcmp(str, "FROM")) + is_op = 1; + else if (strcmp(str, "BY")) return gf_bt_report(parser, GF_BAD_PARAM, "BY expected got %s", str); + + parser->last_error = gf_node_get_field_by_name(n, field, &info); + if (parser->last_error) + return gf_bt_report(parser, parser->last_error, "%s: Unknown node field", field); + + if (is_op) { + u32 op_type; + s32 op_idx = -1; + GF_FieldInfo src; + str = gf_bt_get_next(parser, 0); + sep = strchr(str, '.'); + if (!sep) return gf_bt_report(parser, GF_BAD_PARAM, "\'.\' expected"); + sep[0] = 0; + opnode = gf_bt_peek_node(parser, str); + if (!opnode) return gf_bt_report(parser, GF_BAD_PARAM, "%s: unknown node", str); + sep[0] = '.'; + sep++; + + if (gf_bt_check_code(parser, '[')) { + if ( (parser->last_error = gf_bt_parse_int(parser, "index", &op_idx)) ) return parser->last_error; + if (!gf_bt_check_code(parser, ']')) + return gf_bt_report(parser, GF_BAD_PARAM, "] expected"); + } + parser->last_error = gf_node_get_field_by_name(opnode, sep, &src); + if (parser->last_error) + return gf_bt_report(parser, parser->last_error, "%s: Unknown node field", sep); + + op_type = src.fieldType; + if (op_idx>=0) op_type = gf_sg_vrml_get_sf_type(src.fieldType); + + if (pos>=0) { + if (op_type != gf_sg_vrml_get_sf_type(info.fieldType) ) + return gf_bt_report(parser, parser->last_error, "Field type mismatch between %s and %s", info.name, src.name); + } else { + if (src.fieldType != info.fieldType) + return gf_bt_report(parser, parser->last_error, "Field type mismatch between %s and %s", info.name, src.name); + } + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_FIELD_REPLACE_OP); + bd_set_com_node(com, n); + com->fromNodeID = gf_node_get_id(opnode); + com->fromFieldIndex = src.fieldIndex; + com->send_event_x = op_idx; + + inf = gf_sg_command_field_new(com); + inf->fieldIndex = info.fieldIndex; + inf->fieldType = info.fieldType; + inf->pos = pos; + gf_list_add(cmdList, com); + parser->cur_com = com; + return parser->last_error; + } + + /*field replace*/ + if (pos==-2) { + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_FIELD_REPLACE); + bd_set_com_node(com, n); + + inf = gf_sg_command_field_new(com); + inf->fieldIndex = info.fieldIndex; + inf->fieldType = info.fieldType; + + switch (info.fieldType) { + case GF_SG_VRML_SFNODE: + inf->new_node = gf_bt_sf_node(parser, NULL, NULL, NULL); + inf->field_ptr = &inf->new_node; + if (!gf_bt_check_ndt(parser, &info, inf->new_node, n)) goto err; + break; + case GF_SG_VRML_MFNODE: + { + GF_ChildNodeItem *last = NULL; + if (!gf_bt_check_code(parser, '[')) break; + inf->field_ptr = &inf->node_list; + while (!gf_bt_check_code(parser, ']')) { + newnode = gf_bt_sf_node(parser, NULL, NULL, NULL); + if (!newnode) goto err; + if (parser->last_error!=GF_OK) goto err; + if (!gf_bt_check_ndt(parser, &info, newnode, n)) goto err; + gf_node_list_add_child_last(& inf->node_list, newnode, &last); + } + } + break; + default: + inf->field_ptr = gf_sg_vrml_field_pointer_new(info.fieldType); + info.far_ptr = inf->field_ptr; + if (gf_sg_vrml_is_sf_field(info.fieldType)) { + gf_bt_sffield(parser, &info, n); + } else { + gf_bt_mffield(parser, &info, n); + } + if (parser->last_error) goto err; + break; + } + + gf_list_add(cmdList, com); + parser->cur_com = com; + return parser->last_error; + } + /*indexed field replace*/ + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_INDEXED_REPLACE); + bd_set_com_node(com, n); + inf = gf_sg_command_field_new(com); + inf->pos = pos; + inf->fieldIndex = info.fieldIndex; + if (gf_sg_vrml_is_sf_field(info.fieldType)) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: MF type field expected", info.name); + goto err; + } + inf->fieldType = gf_sg_vrml_get_sf_type(info.fieldType); + switch (info.fieldType) { + case GF_SG_VRML_MFNODE: + inf->new_node = gf_bt_sf_node(parser, NULL, NULL, NULL); + inf->field_ptr = &inf->new_node; + break; + default: + info.fieldType = inf->fieldType; + info.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); + gf_bt_sffield(parser, &info, n); + break; + } + if (parser->last_error) goto err; + gf_list_add(cmdList, com); + parser->cur_com = com; + return parser->last_error; + } + /*INSERT commands*/ + if (!strcmp(str, "INSERT") || !strcmp(str, "APPEND")) { + Bool is_append = !strcmp(str, "APPEND") ? 1 : 0; + str = gf_bt_get_next(parser, 0); + if (!strcmp(str, "ROUTE")) { + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_ROUTE_INSERT); + gf_bt_parse_route(parser, 0, 1, com); + if (parser->last_error) goto err; + gf_list_add(cmdList, com); + gf_list_add(parser->inserted_routes, com); + parser->cur_com = com; + return GF_OK; + } + if (strcmp(str, "AT") && strcmp(str, "TO")) { + return gf_bt_report(parser, GF_BAD_PARAM, (char*) (is_append ? "TO expected got %s" : "AT expected got %s"), str); + } + str = gf_bt_get_next(parser, 1); + strcpy(field, str); + n = gf_bt_peek_node(parser, str); + if (!n) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown node", field); + } + if (!gf_bt_check_code(parser, '.')) { + return gf_bt_report(parser, GF_BAD_PARAM, ". expected"); + } + str = gf_bt_get_next(parser, 1); + strcpy(field, str); + if (!is_append) { + if (!gf_bt_check_code(parser, '[')) { + return gf_bt_report(parser, GF_BAD_PARAM, "[ expected"); + } + gf_bt_parse_int(parser, "index", &pos); + if (!gf_bt_check_code(parser, ']')) { + return gf_bt_report(parser, GF_BAD_PARAM, "] expected"); + } + } else { + if (gf_bt_check_code(parser, '[')) { + return gf_bt_report(parser, GF_BAD_PARAM, "[ unexpected in Append command"); + } + pos = -1; + } + if (!strcmp(field, "children")) { + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_NODE_INSERT); + bd_set_com_node(com, n); + inf = gf_sg_command_field_new(com); + inf->pos = pos; + inf->new_node = gf_bt_sf_node(parser, NULL, NULL, NULL); + inf->fieldType = GF_SG_VRML_SFNODE; + inf->field_ptr = &inf->new_node; + if (parser->last_error) goto err; + parser->cur_com = com; + return gf_list_add(cmdList, com); + } + gf_node_get_field_by_name(n, field, &info); + if (gf_sg_vrml_is_sf_field(info.fieldType)) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: MF type field expected", info.name); + goto err; + } + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_INDEXED_INSERT); + bd_set_com_node(com, n); + inf = gf_sg_command_field_new(com); + inf->pos = pos; + inf->fieldIndex = info.fieldIndex; + inf->fieldType = gf_sg_vrml_get_sf_type(info.fieldType); + switch (info.fieldType) { + case GF_SG_VRML_MFNODE: + inf->new_node = gf_bt_sf_node(parser, NULL, NULL, NULL); + inf->field_ptr = &inf->new_node; + break; + default: + info.fieldType = inf->fieldType; + inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); + info.far_ptr = inf->field_ptr; + gf_bt_sffield(parser, &info, n); + break; + } + if (parser->last_error) goto err; + gf_list_add(cmdList, com); + parser->cur_com = com; + return parser->last_error; + } + /*DELETE commands*/ + if (!strcmp(str, "DELETE")) { + str = gf_bt_get_next(parser, 1); + if (!strcmp(str, "ROUTE")) { + str = gf_bt_get_next(parser, 0); + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_ROUTE_DELETE); + com->RouteID = gf_bt_get_route(parser, str); + if (!com->RouteID) { + com->unres_name = strdup(str); + com->unresolved = 1; + gf_list_add(parser->unresolved_routes, com); + } + /*for bt<->xmt conversions*/ + com->def_name = strdup(str); + return gf_list_add(cmdList, com); + } + strcpy(field, str); + n = gf_bt_peek_node(parser, str); + if (!n) { + return gf_bt_report(parser, GF_BAD_PARAM, "DELETE %s: Unknown Node", field); + } + if (!gf_bt_check_code(parser, '.')) { + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_NODE_DELETE); + bd_set_com_node(com, n); + return gf_list_add(cmdList, com); + } + str = gf_bt_get_next(parser, 0); + if (gf_node_get_field_by_name(n, str, &info) != GF_OK) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s not a field of node %s", str, gf_node_get_class_name(n) ); + } + if (gf_bt_check_code(parser, '[')) { + gf_bt_parse_int(parser, "index", &pos); + if (!gf_bt_check_code(parser, ']')) + return gf_bt_report(parser, GF_BAD_PARAM, "[ expected"); + } + if (gf_sg_vrml_is_sf_field(info.fieldType)) { + if (info.fieldType == GF_SG_VRML_SFNODE) { + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_FIELD_REPLACE); + bd_set_com_node(com, n); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = info.fieldIndex; + inf->fieldType = info.fieldType; + inf->new_node = NULL; + inf->field_ptr = &inf->new_node; + return gf_list_add(cmdList, com); + } + return gf_bt_report(parser, GF_BAD_PARAM, "%s is an SFField - cannot indexed delete", info.name); + } + + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_INDEXED_DELETE); + bd_set_com_node(com, n); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = info.fieldIndex; + inf->fieldType = info.fieldType; + inf->pos = pos; + return gf_list_add(cmdList, com); + } + /*Extended BIFS commands*/ + + /*GlobalQP commands*/ + if (!strcmp(str, "GLOBALQP")) { + newnode = gf_bt_sf_node(parser, NULL, NULL, NULL); + if (newnode && (newnode->sgprivate->tag != TAG_MPEG4_QuantizationParameter)) { + gf_bt_report(parser, GF_BAD_PARAM, "Only QuantizationParameter node allowed in GLOBALQP"); + gf_node_unregister(newnode, NULL); + return parser->last_error; + } + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_GLOBAL_QUANTIZER); + com->node = NULL; + inf = gf_sg_command_field_new(com); + inf->new_node = newnode; + inf->field_ptr = &inf->new_node; + inf->fieldType = GF_SG_VRML_SFNODE; + return gf_list_add(cmdList, com); + } + + /*MultipleReplace commands*/ + if (!strcmp(str, "MULTIPLEREPLACE")) { + str = gf_bt_get_next(parser, 0); + strcpy(field, str); + n = gf_bt_peek_node(parser, str); + if (!n) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown node", field); + } + if (!gf_bt_check_code(parser, '{')) { + return gf_bt_report(parser, GF_BAD_PARAM, "{ expected"); + } + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_MULTIPLE_REPLACE); + bd_set_com_node(com, n); + + while (!gf_bt_check_code(parser, '}')) { + str = gf_bt_get_next(parser, 0); + parser->last_error = gf_node_get_field_by_name(n, str, &info); + if (parser->last_error) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown node field", str); + goto err; + } + inf = gf_sg_command_field_new(com); + inf->fieldIndex = info.fieldIndex; + inf->fieldType = info.fieldType; + inf->pos = -1; + + switch (info.fieldType) { + case GF_SG_VRML_SFNODE: + inf->new_node = gf_bt_sf_node(parser, NULL, NULL, NULL); + if (parser->last_error) goto err; + if (!gf_bt_check_ndt(parser, &info, inf->new_node, n)) goto err; + inf->field_ptr = &inf->new_node; + break; + case GF_SG_VRML_MFNODE: + { + GF_ChildNodeItem *last = NULL; + if (!gf_bt_check_code(parser, '[')) { + gf_bt_report(parser, GF_BAD_PARAM, "[ expected"); + goto err; + } + info.far_ptr = inf->field_ptr = &inf->node_list; + while (!gf_bt_check_code(parser, ']')) { + newnode = gf_bt_sf_node(parser, NULL, NULL, NULL); + if (parser->last_error!=GF_OK) goto err; + if (!gf_bt_check_ndt(parser, &info, newnode, n)) goto err; + gf_node_list_add_child_last( & inf->node_list, newnode, &last); + } + } + break; + default: + info.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); + if (gf_sg_vrml_is_sf_field(info.fieldType)) { + gf_bt_sffield(parser, &info, n); + } else { + gf_bt_mffield(parser, &info, n); + } + if (parser->last_error) goto err; + break; + } + } + parser->cur_com = com; + return gf_list_add(cmdList, com); + } + + /*MultipleIndexReplace commands*/ + if (!strcmp(str, "MULTIPLEINDREPLACE")) { + str = gf_bt_get_next(parser, 1); + strcpy(field, str); + n = gf_bt_peek_node(parser, str); + if (!n) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown node", field); + } + if (!gf_bt_check_code(parser, '.')) { + return gf_bt_report(parser, GF_BAD_PARAM, ". expected"); + } + str = gf_bt_get_next(parser, 0); + parser->last_error = gf_node_get_field_by_name(n, str, &info); + if (parser->last_error) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown field", info.name); + } + if (gf_sg_vrml_is_sf_field(info.fieldType)) { + return gf_bt_report(parser, GF_BAD_PARAM, "Only MF field allowed"); + } + if (!gf_bt_check_code(parser, '[')) { + return gf_bt_report(parser, GF_BAD_PARAM, "[ expected"); + } + + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_MULTIPLE_INDEXED_REPLACE); + bd_set_com_node(com, n); + info.fieldType = gf_sg_vrml_get_sf_type(info.fieldType); + + while (!gf_bt_check_code(parser, ']')) { + u32 pos; + if (gf_bt_parse_int(parser, "position", (SFInt32 *)&pos)) goto err; + str = gf_bt_get_next(parser, 0); + if (strcmp(str, "BY")) { + gf_bt_report(parser, GF_BAD_PARAM, "BY expected"); + goto err; + } + inf = gf_sg_command_field_new(com); + inf->fieldIndex = info.fieldIndex; + inf->fieldType = info.fieldType; + inf->pos = pos; + if (inf->fieldType==GF_SG_VRML_SFNODE) { + info.far_ptr = inf->field_ptr = &inf->new_node; + inf->new_node = gf_bt_sf_node(parser, NULL, NULL, NULL); + if (parser->last_error) goto err; + if (!gf_bt_check_ndt(parser, &info, inf->new_node, n)) goto err; + inf->field_ptr = &inf->new_node; + } else { + info.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); + gf_bt_sffield(parser, &info, n); + if (parser->last_error) goto err; + } + } + parser->cur_com = com; + return gf_list_add(cmdList, com); + } + + if (!strcmp(str, "XDELETE")) { + str = gf_bt_get_next(parser, 1); + strcpy(field, str); + n = gf_bt_peek_node(parser, str); + if (!n) { + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown Node", field); + } + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_NODE_DELETE_EX); + bd_set_com_node(com, n); + return gf_list_add(cmdList, com); + } + + if (!strcmp(str, "INSERTPROTO")) { + if (!gf_bt_check_code(parser, '[')) { + return gf_bt_report(parser, GF_BAD_PARAM, "[ expected"); + } + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_PROTO_INSERT); + while (!gf_bt_check_code(parser, ']')) { + parser->last_error = gf_bt_parse_proto(parser, NULL, com->new_proto_list); + if (parser->last_error) goto err; + } + gf_list_add(cmdList, com); + return GF_OK; + } + if (!strcmp(str, "DELETEPROTO")) { + if (!gf_bt_check_code(parser, '[')) { + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_PROTO_DELETE_ALL); + str = gf_bt_get_next(parser, 0); + if (strcmp(str, "ALL")) { + gf_bt_report(parser, GF_BAD_PARAM, "ALL expected"); + goto err; + } + return gf_list_add(cmdList, com); + } + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_PROTO_DELETE); + while (!gf_bt_check_code(parser, ']')) { + GF_Proto *proto; + str = gf_bt_get_next(parser, 0); + proto = gf_sg_find_proto(parser->load->scene_graph, 0, str); + if (!proto) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown proto", str); + goto err; + } + com->del_proto_list = (u32*)realloc(com->del_proto_list, sizeof(u32)*(com->del_proto_list_size+1)); + com->del_proto_list[com->del_proto_list_size] = proto->ID; + com->del_proto_list_size++; + } + gf_list_add(cmdList, com); + return GF_OK; + } + return gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown command syntax, str"); + +err: + if (com) gf_sg_command_del(com); + return parser->last_error; +} + +GF_Descriptor *gf_bt_parse_descriptor(GF_BTParser *parser, char *name); + +GF_IPMPX_Data *gf_bt_parse_ipmpx(GF_BTParser *parser, char *name) +{ + char *str, field[500]; + GF_IPMPX_Data *desc, *subdesc; + GF_Descriptor *oddesc; + GF_Err e; + u32 type; + u8 tag; + if (name) { + str = name; + } else { + str = gf_bt_get_next(parser, 0); + } + tag = gf_ipmpx_get_tag(str); + if (!tag) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown IPMPX Data", str); + return NULL; + } + desc = gf_ipmpx_data_new(tag); + + if (!desc) return NULL; + if (!gf_bt_check_code(parser, '{')) return desc; + + while (1) { + /*done*/ + if (gf_bt_check_code(parser, '}')) break; + str = gf_bt_get_next(parser, 0); + strcpy(field, str); + type = gf_ipmpx_get_field_type(desc, str); + switch (type) { + /*single descriptor*/ + case GF_ODF_FT_OD: + assert(desc->tag==GF_IPMPX_CONNECT_TOOL_TAG); + str = gf_bt_get_next(parser, 0); + oddesc = gf_bt_parse_descriptor(parser, str); + if (!oddesc) { + gf_bt_report(parser, GF_BAD_PARAM, "Unknown desc %s in field %s", str, field); + gf_ipmpx_data_del(desc); + return NULL; + } + assert(oddesc->tag==GF_ODF_IPMP_TAG); + ((GF_IPMPX_ConnectTool *)desc)->toolDescriptor = (GF_IPMP_Descriptor *)oddesc; + break; + /*descriptor list*/ + case GF_ODF_FT_OD_LIST: + assert(desc->tag==GF_IPMPX_GET_TOOLS_RESPONSE_TAG); + if (gf_bt_check_code(parser, '[')) { + while (!gf_bt_check_code(parser, ']')) { + GF_Descriptor *ipmp_t = gf_bt_parse_descriptor(parser, NULL); + if (!ipmp_t) { + gf_ipmpx_data_del(desc); + parser->last_error = GF_BAD_PARAM; + return NULL; + } + assert(ipmp_t->tag==GF_ODF_IPMP_TOOL_TAG); + gf_list_add( ((GF_IPMPX_GetToolsResponse *)desc)->ipmp_tools, ipmp_t); + } + } + break; + + /*IPMPX ByteArray list*/ + case GF_ODF_FT_IPMPX_BA_LIST: + if (gf_bt_check_code(parser, '[')) { + while (!gf_bt_check_code(parser, ']')) { + str = gf_bt_get_next(parser, 0); + if (!str) continue; + if (gf_ipmpx_set_byte_array(desc, field, str) != GF_OK) { + gf_bt_report(parser, GF_OK, "Invalid ipmpx %s in field %s - skipping", str, field); + } + gf_bt_check_code(parser, ','); + } + } + break; + /*IPMPX ByteArray: check if declared as sub-data or not*/ + case GF_ODF_FT_IPMPX_BA: + str = NULL; + if (gf_bt_check_code(parser, '{')) { + str = gf_bt_get_next(parser, 0); + if (stricmp(str, "array")) { + gf_bt_report(parser, GF_BAD_PARAM, "IPMP ByteArray syntax is %s { array \"...\" } or %s \"....\"\n", field, field); + gf_ipmpx_data_del(desc); + return NULL; + } + str = gf_bt_get_next(parser, 0); + gf_bt_check_code(parser, '}'); + } else { + str = gf_bt_get_next(parser, 0); + } + e = gf_ipmpx_set_byte_array(desc, field, str); + if (e) { + gf_bt_report(parser, e, "Error assigning IPMP ByteArray %s\n", field); + gf_ipmpx_data_del(desc); + return NULL; + } + break; + /*IPMPX Data list*/ + case GF_ODF_FT_IPMPX_LIST: + if (gf_bt_check_code(parser, '[')) { + while (!gf_bt_check_code(parser, ']')) { + subdesc = gf_bt_parse_ipmpx(parser, NULL); + if (!subdesc) { + gf_ipmpx_data_del(desc); + parser->last_error = GF_BAD_PARAM; + return NULL; + } + if (gf_ipmpx_set_sub_data(desc, field, subdesc) != GF_OK) { + gf_bt_report(parser, GF_BAD_PARAM, "Invalid ipmpx %s in field %s - skipping", str, field); + gf_ipmpx_data_del(subdesc); + } + } + } + break; + /*regular IPMPX Data*/ + case GF_ODF_FT_IPMPX: + str = gf_bt_get_next(parser, 0); + subdesc = gf_bt_parse_ipmpx(parser, str); + if (!subdesc) { + gf_bt_report(parser, GF_BAD_PARAM, "Unknown ipmpx %s in field %s", str, field); + gf_ipmpx_data_del(desc); + return NULL; + } + if (gf_ipmpx_set_sub_data(desc, field, subdesc) != GF_OK) { + gf_bt_report(parser, GF_BAD_PARAM, "Invalid ipmpx in field %s - skipping", field); + gf_ipmpx_data_del(subdesc); + } + break; + default: + str = gf_bt_get_next(parser, 0); + parser->last_error = gf_ipmpx_set_field(desc, field, str); + + if (parser->last_error) { + gf_bt_report(parser, GF_BAD_PARAM, "Invalid value %s in field %s", str, field); + gf_ipmpx_data_del(desc); + return NULL; + } + break; + } + } + return desc; +} + +static void gf_bt_add_desc(GF_BTParser *parser, GF_Descriptor *par, GF_Descriptor *child, char *fieldName) +{ + GF_Err e = gf_odf_desc_add_desc(par, child); + if (e) { + gf_bt_report(parser, GF_OK, "Invalid child descriptor in field %s - skipping", fieldName); + gf_odf_desc_del(child); + } +} + +GF_Descriptor *gf_bt_parse_descriptor(GF_BTParser *parser, char *name) +{ + char *str, field[500]; + GF_Descriptor *desc, *subdesc; + u32 type; + u8 tag; + if (name) { + str = name; + } else { + str = gf_bt_get_next(parser, 0); + } + tag = gf_odf_get_tag_by_name(str); + if (!tag) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown descriptor", str); + return NULL; + } + desc = gf_odf_desc_new(tag); + + if (!desc) return NULL; + if (!gf_bt_check_code(parser, '{')) return desc; + + while (1) { + Bool is_anim_mask = 0; + /*done*/ + if (gf_bt_check_code(parser, '}')) break; + str = gf_bt_get_next(parser, 0); + strcpy(field, str); + + if ((tag==GF_ODF_BIFS_CFG_TAG) && !strcmp(field, "animationMask")) { + gf_bt_get_next(parser, 0); + if (gf_bt_check_code(parser, '{')) is_anim_mask = 1; + str = gf_bt_get_next(parser, 0); + strcpy(field, str); + } + + type = gf_odf_get_field_type(desc, str); + switch (type) { + /*IPMPX list*/ + case GF_ODF_FT_IPMPX_LIST: + if(desc->tag!=GF_ODF_IPMP_TAG) { + gf_bt_report(parser, GF_BAD_PARAM, "IPMPX_Data list only allowed in GF_IPMP_Descriptor"); + gf_odf_desc_del(desc); + return NULL; + } + if (gf_bt_check_code(parser, '[')) { + while (!gf_bt_check_code(parser, ']')) { + GF_IPMPX_Data *ipmpx = gf_bt_parse_ipmpx(parser, NULL); + if (!ipmpx) { + gf_odf_desc_del(desc); + parser->last_error = GF_BAD_PARAM; + return NULL; + } + gf_list_add( ((GF_IPMP_Descriptor *)desc)->ipmpx_data, ipmpx); + } + } + break; + /*IPMPX*/ + case GF_ODF_FT_IPMPX: + if(desc->tag!=GF_ODF_IPMP_TOOL_TAG) { + gf_bt_report(parser, GF_BAD_PARAM, "IPMPX_Data only allowed in GF_IPMP_Tool"); + gf_odf_desc_del(desc); + return NULL; + } + if (gf_bt_check_code(parser, '[')) { + while (!gf_bt_check_code(parser, ']')) { + GF_IPMPX_Data *ipmpx = gf_bt_parse_ipmpx(parser, NULL); + if (!ipmpx) { + gf_odf_desc_del(desc); + parser->last_error = GF_BAD_PARAM; + return NULL; + } + if (ipmpx->tag==GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG) { + GF_IPMP_Tool *it = (GF_IPMP_Tool *)desc; + if (it->toolParamDesc) gf_ipmpx_data_del((GF_IPMPX_Data *)it->toolParamDesc); + it->toolParamDesc = (GF_IPMPX_ParametricDescription*)ipmpx; + } else { + gf_bt_report(parser, GF_OK, "Only ToolParametricDescription allowed in GF_IPMP_Tool - skipping"); + gf_ipmpx_data_del(ipmpx); + } + } + } + break; + + /*descriptor list*/ + case GF_ODF_FT_OD_LIST: + if (gf_bt_check_code(parser, '[')) { + while (!gf_bt_check_code(parser, ']')) { + subdesc = gf_bt_parse_descriptor(parser, NULL); + if (!subdesc) { + gf_odf_desc_del(desc); + parser->last_error = GF_BAD_PARAM; + return NULL; + } + gf_bt_add_desc(parser, desc, subdesc, field); + } + } + if (is_anim_mask) + gf_bt_check_code(parser, '}'); + break; + /*single descriptor*/ + case GF_ODF_FT_OD: + str = gf_bt_get_next(parser, 0); + subdesc = gf_bt_parse_descriptor(parser, str); + if (!subdesc) { + gf_bt_report(parser, GF_BAD_PARAM, "Unknown desc %s in field %s", str, field); + gf_odf_desc_del(desc); + return NULL; + } + gf_bt_add_desc(parser, desc, subdesc, field); + break; + /*regular field*/ + default: + str = gf_bt_get_next(parser, 0); + parser->last_error = gf_odf_set_field(desc, field, str); + + if (parser->last_error) { + gf_bt_report(parser, GF_BAD_PARAM, "Invalid value %s in field %s", str, field); + gf_odf_desc_del(desc); + return NULL; + } + break; + } + } + if (desc->tag == GF_ODF_BIFS_CFG_TAG) { + GF_BIFSConfig *bcfg = (GF_BIFSConfig *)desc; + if (!parser->load->ctx->scene_width) { + parser->load->ctx->scene_width = bcfg->pixelWidth; + parser->load->ctx->scene_height = bcfg->pixelHeight; + parser->load->ctx->is_pixel_metrics = bcfg->pixelMetrics; + } + + /*for bt->xmt*/ + if (!bcfg->version) bcfg->version = 1; + } + else if (desc->tag==GF_ODF_ESD_TAG) { + GF_ESD *esd =(GF_ESD*)desc; + if (esd->decoderConfig) { + /*watchout for default BIFS stream*/ + if (parser->bifs_es && !parser->base_bifs_id && (esd->decoderConfig->streamType==GF_STREAM_SCENE)) { + parser->bifs_es->ESID = parser->base_bifs_id = esd->ESID; + parser->bifs_es->timeScale = esd->slConfig ? esd->slConfig->timestampResolution : 1000; + } else { + GF_StreamContext *sc = gf_sm_stream_new(parser->load->ctx, esd->ESID, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); + /*set default timescale for systems tracks (ignored for other)*/ + if (sc) sc->timeScale = esd->slConfig ? esd->slConfig->timestampResolution : 1000; + /*assign base OD*/ + if (!parser->base_od_id && (esd->decoderConfig->streamType==GF_STREAM_OD)) parser->base_od_id = esd->ESID; + } + } + } + return desc; +} + +void gf_bt_parse_od_command(GF_BTParser *parser, char *name) +{ + u32 val; + char *str; + GF_Descriptor *desc; + + if (!strcmp(name, "UPDATE")) { + str = gf_bt_get_next(parser, 0); + /*OD update*/ + if (!strcmp(str, "OD")) { + GF_ODUpdate *odU; + if (!gf_bt_check_code(parser, '[')) { + gf_bt_report(parser, GF_BAD_PARAM, "[ expected"); + return; + } + odU = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + gf_list_add(parser->od_au->commands, odU); + while (!parser->done) { + str = gf_bt_get_next(parser, 0); + if (gf_bt_check_code(parser, ']')) break; + if (strcmp(str, "ObjectDescriptor") && strcmp(str, "InitialObjectDescriptor")) { + gf_bt_report(parser, GF_BAD_PARAM, "Object Descriptor expected got %s", str); + break; + } + desc = gf_bt_parse_descriptor(parser, str); + if (!desc) break; + gf_list_add(odU->objectDescriptors, desc); + } + return; + } + /*ESD update*/ + if (!strcmp(str, "ESD")) { + GF_ESDUpdate *esdU; + str = gf_bt_get_next(parser, 0); + if (strcmp(str, "IN")) { + gf_bt_report(parser, GF_BAD_PARAM, "IN expected got %s", str); + return; + } + esdU = (GF_ESDUpdate *) gf_odf_com_new(GF_ODF_ESD_UPDATE_TAG); + parser->last_error = gf_bt_parse_int(parser, "OD_ID", (SFInt32*)&val); + if (parser->last_error) return; + esdU->ODID = val; + gf_list_add(parser->od_au->commands, esdU); + + if (!gf_bt_check_code(parser, '[')) { + str = gf_bt_get_next(parser, 0); + if (strcmp(str, "esDescr")) { + gf_bt_report(parser, GF_BAD_PARAM, "esDescr expected got %s", str); + return; + } + if (!gf_bt_check_code(parser, '[')) { + gf_bt_report(parser, GF_BAD_PARAM, "[ expected"); + return; + } + } + + while (!parser->done) { + str = gf_bt_get_next(parser, 0); + if (gf_bt_check_code(parser, ']')) break; + if (strcmp(str, "ESDescriptor")) { + gf_bt_report(parser, GF_BAD_PARAM, "ESDescriptor expected got %s", str); + break; + } + desc = gf_bt_parse_descriptor(parser, str); + if (!desc) break; + gf_list_add(esdU->ESDescriptors, desc); + } + return; + } + /*IPMP descriptor update*/ + if (!strcmp(str, "IPMPD") || !strcmp(str, "IPMPDX")) { + GF_IPMPUpdate *ipU; + if (!gf_bt_check_code(parser, '[')) { + gf_bt_report(parser, GF_BAD_PARAM, "[ expected"); + return; + } + ipU = (GF_IPMPUpdate *) gf_odf_com_new(GF_ODF_IPMP_UPDATE_TAG); + gf_list_add(parser->od_au->commands, ipU); + while (!parser->done) { + str = gf_bt_get_next(parser, 0); + if (gf_bt_check_code(parser, ']')) break; + if (strcmp(str, "IPMP_Descriptor")) { + gf_bt_report(parser, GF_BAD_PARAM, "IPMP_Descriptor expected got %s", str); + break; + } + desc = gf_bt_parse_descriptor(parser, str); + if (!desc) break; + gf_list_add(ipU->IPMPDescList, desc); + } + return; + } + gf_bt_report(parser, GF_BAD_PARAM, "unknown OD command", str); + return; + } + if (!strcmp(name, "REMOVE")) { + str = gf_bt_get_next(parser, 0); + /*OD remove*/ + if (!strcmp(str, "OD")) { + GF_ODRemove *odR; + if (!gf_bt_check_code(parser, '[')) { + gf_bt_report(parser, GF_BAD_PARAM, "[ expected"); + return; + } + odR = (GF_ODRemove *) gf_odf_com_new(GF_ODF_OD_REMOVE_TAG); + gf_list_add(parser->od_au->commands, odR); + while (!parser->done) { + u32 id; + if (gf_bt_check_code(parser, ']')) break; + gf_bt_parse_int(parser, "ODID", (SFInt32*)&id); + if (parser->last_error) return; + odR->OD_ID = (u16*)realloc(odR->OD_ID, sizeof(u16) * (odR->NbODs+1)); + odR->OD_ID[odR->NbODs] = id; + odR->NbODs++; + } + return; + } + /*ESD remove*/ + if (!strcmp(str, "ESD")) { + u32 odid; + GF_ESDRemove *esdR; + str = gf_bt_get_next(parser, 0); + if (strcmp(str, "FROM")) { + gf_bt_report(parser, GF_BAD_PARAM, "FROM expected got %s", str); + return; + } + gf_bt_parse_int(parser, "ODID", (SFInt32*)&odid); + if (parser->last_error) return; + + if (!gf_bt_check_code(parser, '[')) { + gf_bt_report(parser, GF_BAD_PARAM, "[ expected"); + return; + } + esdR = (GF_ESDRemove *) gf_odf_com_new(GF_ODF_ESD_REMOVE_TAG); + esdR->ODID = odid; + gf_list_add(parser->od_au->commands, esdR); + while (!parser->done) { + u32 id; + if (gf_bt_check_code(parser, ']')) break; + gf_bt_parse_int(parser, "ES_ID", (SFInt32*)&id); + if (parser->last_error) return; + esdR->ES_ID = (u16*)realloc(esdR->ES_ID, sizeof(u16) * (esdR->NbESDs+1)); + esdR->ES_ID[esdR->NbESDs] = id; + esdR->NbESDs++; + } + return; + } + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown OD command", str); + return; + } +} + + + +GF_Err gf_bt_loader_run_intern(GF_BTParser *parser, GF_Command *init_com, Bool initial_run) +{ + char *str; + GF_Node *node, *vrml_root_node; + Bool in_com, force_new_com; + GF_Route *r; + Bool has_id; + char szDEFName[1000]; + + vrml_root_node = NULL; + has_id = 0; + in_com = init_com ? 0 : 1; + parser->cur_com = init_com; + + force_new_com = (parser->load->flags & GF_SM_LOAD_CONTEXT_READY) ? 1 : 0; + + + /*create a default root node for all VRML nodes*/ + if ((parser->is_wrl && !parser->top_nodes) && !vrml_root_node) { + if (initial_run ) { + vrml_root_node = gf_node_new(parser->load->scene_graph, (parser->load->flags & GF_SM_LOAD_MPEG4_STRICT) ? TAG_MPEG4_Group : TAG_X3D_Group); + gf_node_register(vrml_root_node, NULL); + gf_node_init(vrml_root_node); + gf_sg_set_root_node(parser->load->scene_graph, vrml_root_node); + } else { + vrml_root_node = gf_sg_get_root_node(parser->load->scene_graph); + } + } + + /*parse all top-level items*/ + while (!parser->last_error) { + str = gf_bt_get_next(parser, 0); + if (parser->done) break; + + /*X3D specific things (ignored for now)*/ + if (!strcmp(str, "PROFILE")) gf_bt_force_line(parser); + else if (!strcmp(str, "COMPONENT")) gf_bt_force_line(parser); + else if (!strcmp(str, "META")) gf_bt_force_line(parser); + else if (!strcmp(str, "IMPORT") || !strcmp(str, "EXPORT")) { + gf_bt_report(parser, GF_NOT_SUPPORTED, "X3D IMPORT/EXPORT not implemented"); + break; + } + + /*IOD*/ + else if (!strcmp(str, "InitialObjectDescriptor") || !strcmp(str, "ObjectDescriptor")) { + parser->load->ctx->root_od = (GF_ObjectDescriptor *) gf_bt_parse_descriptor(parser, str); + } + /*explicit command*/ + else if (!strcmp(str, "AT") || !strcmp(str, "RAP")) { + parser->au_is_rap = 0; + if (!strcmp(str, "RAP")) { + parser->au_is_rap = 1; + str = gf_bt_get_next(parser, 0); + if (strcmp(str, "AT")) { + gf_bt_report(parser, GF_BAD_PARAM, "AT expected got %s", str); + parser->last_error = GF_BAD_PARAM; + break; + } + } + force_new_com = 0; + str = gf_bt_get_next(parser, 0); + if (str[0] == 'D') { + parser->au_time += atoi(&str[1]); + } else { + if (sscanf(str, "%d", &parser->au_time) != 1) { + gf_bt_report(parser, GF_BAD_PARAM, "Number expected got %s", str); + break; + } + } + if (parser->last_error) break; + /*reset all contexts*/ + if (parser->od_au && (parser->od_au->timing != parser->au_time)) parser->od_au = NULL; + if (parser->bifs_au && (parser->bifs_au->timing != parser->au_time)) { + gf_bt_check_unresolved_nodes(parser); + parser->bifs_au = NULL; + } + + parser->stream_id = 0; + /*fix for mp4tool bt which doesn't support RAP signaling: assume the first AU + is always RAP*/ + if (!parser->au_time) parser->au_is_rap = 1; + + in_com = 1; + + if (!gf_bt_check_code(parser, '{')) { + str = gf_bt_get_next(parser, 0); + if (!strcmp(str, "IN")) { + gf_bt_parse_int(parser, "IN", (SFInt32*)&parser->stream_id); + if (parser->last_error) break; + } + if (!gf_bt_check_code(parser, '{')) { + gf_bt_report(parser, GF_BAD_PARAM, "{ expected"); + } + } + /*done loading init frame*/ + if (init_com && parser->au_time) break; + } + else if (!strcmp(str, "PROTO") || !strcmp(str, "EXTERNPROTO")) { + gf_bt_parse_proto(parser, str, init_com ? init_com->new_proto_list : NULL); + } + /*compatibility for old bt (mp4tool) in ProtoLibs*/ + else if (!strcmp(str, "NULL")) { + } + else if (!strcmp(str, "DEF")) { + str = gf_bt_get_next(parser, 0); + strcpy(szDEFName, str); + has_id = 1; + } + else if (!strcmp(str, "ROUTE")) { + GF_Command *com = NULL; + if (!parser->top_nodes && parser->bifs_au && !parser->is_wrl) { + /*if doing a scene replace, we need route insert stuff*/ + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_ROUTE_INSERT); + gf_list_add(parser->bifs_au->commands, com); + gf_list_add(parser->inserted_routes, com); + } + + r = gf_bt_parse_route(parser, 1, 0, com); + if (has_id) { + u32 rID = gf_bt_get_route(parser, szDEFName); + if (!rID) rID = gf_bt_get_next_route_id(parser); + if (com) { + com->RouteID = rID; + com->def_name = strdup(szDEFName); + gf_sg_set_max_defined_route_id(parser->load->scene_graph, rID); + } else if (r) { + gf_sg_route_set_id(r, rID); + gf_sg_route_set_name(r, szDEFName); + } + has_id = 0; + } + } + /*OD commands*/ + else if (!strcmp(str, "UPDATE") || !strcmp(str, "REMOVE")) { + Bool is_base_stream = parser->stream_id ? 0 : 1; + if (!parser->stream_id || parser->stream_id==parser->bifs_es->ESID) parser->stream_id = parser->base_od_id; + + if (parser->od_es && (parser->od_es->ESID != parser->stream_id)) { + GF_StreamContext *prev = parser->od_es; + parser->od_es = gf_sm_stream_new(parser->load->ctx, (u16) parser->stream_id, GF_STREAM_OD, 0); + /*force new AU if stream changed*/ + if (parser->od_es != prev) parser->bifs_au = NULL; + } + if (!parser->od_es) parser->od_es = gf_sm_stream_new(parser->load->ctx, (u16) parser->stream_id, GF_STREAM_OD, 0); + if (!parser->od_au) parser->od_au = gf_sm_stream_au_new(parser->od_es, parser->au_time, 0, parser->au_is_rap); + gf_bt_parse_od_command(parser, str); + if (is_base_stream) parser->stream_id= 0; + } + /*BIFS commands*/ + else if (!strcmp(str, "REPLACE") || !strcmp(str, "INSERT") || !strcmp(str, "APPEND") || !strcmp(str, "DELETE") + /*BIFS extended commands*/ + || !strcmp(str, "GLOBALQP") || !strcmp(str, "MULTIPLEREPLACE") || !strcmp(str, "MULTIPLEINDREPLACE") || !strcmp(str, "XDELETE") || !strcmp(str, "DELETEPROTO") || !strcmp(str, "INSERTPROTO") ) { + Bool is_base_stream = parser->stream_id ? 0 : 1; + + if (!parser->stream_id) parser->stream_id = parser->base_bifs_id; + if (!parser->stream_id || (parser->od_es && (parser->stream_id==parser->od_es->ESID)) ) parser->stream_id = parser->base_bifs_id; + + if (parser->bifs_es->ESID != parser->stream_id) { + GF_StreamContext *prev = parser->bifs_es; + parser->bifs_es = gf_sm_stream_new(parser->load->ctx, (u16) parser->stream_id, GF_STREAM_SCENE, 0); + /*force new AU if stream changed*/ + if (parser->bifs_es != prev) { + gf_bt_check_unresolved_nodes(parser); + parser->bifs_au = NULL; + } + } + if (force_new_com) { + force_new_com = 0; + parser->bifs_au = gf_list_last(parser->bifs_es->AUs); + parser->au_time = (u32) (parser->bifs_au ? parser->bifs_au->timing : 0) + 1; + parser->bifs_au = NULL; + } + + if (!parser->bifs_au) parser->bifs_au = gf_sm_stream_au_new(parser->bifs_es, parser->au_time, 0, parser->au_is_rap); + gf_bt_parse_bifs_command(parser, str, parser->bifs_au->commands); + if (is_base_stream) parser->stream_id= 0; + } + /*implicit BIFS command on SFTopNodes only*/ + else if (!strcmp(str, "OrderedGroup") + || !strcmp(str, "Group") + || !strcmp(str, "Layer2D") + || !strcmp(str, "Layer3D") + /* VRML parsing: all nodes are allowed*/ + || parser->is_wrl + ) + { + + node = gf_bt_sf_node(parser, str, vrml_root_node, has_id ? szDEFName : NULL); + has_id = 0; + if (!node) break; + if (parser->top_nodes) { + gf_list_add(parser->top_nodes, node); + } else if (!vrml_root_node) { + if (init_com) init_com->node = node; + else if (parser->load->flags & GF_SM_LOAD_CONTEXT_READY) { + GF_Command *com = gf_sg_command_new(parser->load->scene_graph, GF_SG_SCENE_REPLACE); + assert(!parser->bifs_au); + assert(parser->bifs_es); + parser->bifs_au = gf_sm_stream_au_new(parser->bifs_es, 0, 0, 1); + gf_list_add(parser->bifs_au->commands, com); + com->node = node; + } + } else { + gf_node_insert_child(vrml_root_node, node, -1); + } + + /* + if (!gf_sg_get_root_node(parser->load->scene_graph)) { + gf_node_register(node, NULL); + gf_sg_set_root_node(parser->load->scene_graph, node); + } + */ + } + + /*if in command, check command end*/ + else { + /*check command end*/ + if (/*in_com && */gf_bt_check_code(parser, '}')) in_com = 0; + else if (strlen(str)) { + gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown top-level element", str); + } + parser->au_is_rap = 0; + } + } + gf_bt_resolve_routes(parser, 0); + gf_bt_check_unresolved_nodes(parser); + + /*load scripts*/ + while (gf_list_count(parser->scripts)) { + GF_Node *n = (GF_Node *)gf_list_get(parser->scripts, 0); + gf_list_rem(parser->scripts, 0); + gf_sg_script_load(n); + } + return parser->last_error; +} + +GF_Err gf_sm_load_init_bt(GF_SceneLoader *load) +{ + u32 size; + gzFile gzInput; + GF_Err e; + GF_BTParser *parser; + GF_Command *com; + FILE *test; + unsigned char BOM[5]; + + if (!load->ctx || !load->fileName) return GF_BAD_PARAM; + + test = fopen(load->fileName, "rb"); + if (!test) return GF_URL_ERROR; + fseek(test, 0, SEEK_END); + size = ftell(test); + fclose(test); + + gzInput = gzopen(load->fileName, "rb"); + if (!gzInput) return GF_IO_ERR; + + GF_SAFEALLOC(parser, GF_BTParser); + parser->load = load; + parser->line_buffer = (char *) malloc(sizeof(char)*BT_LINE_SIZE); + memset(parser->line_buffer, 0, sizeof(char)*BT_LINE_SIZE); + parser->file_size = size; + + gzgets(gzInput, (char*) BOM, 5); + gzseek(gzInput, 0, SEEK_SET); + + /*0: no unicode, 1: UTF-16BE, 2: UTF-16LE*/ + if ((BOM[0]==0xFF) && (BOM[1]==0xFE)) { + if (!BOM[2] && !BOM[3]) { + gf_bt_report(parser, GF_NOT_SUPPORTED, "UTF-32 Text Files not supported"); + gzclose(gzInput); + free(parser); + return GF_NOT_SUPPORTED; + } else { + parser->unicode_type = 2; + gzseek(gzInput, 2, SEEK_CUR); + } + } else if ((BOM[0]==0xFE) && (BOM[1]==0xFF)) { + if (!BOM[2] && !BOM[3]) { + gf_bt_report(parser, GF_NOT_SUPPORTED, "UTF-32 Text Files not supported"); + gzclose(gzInput); + free(parser); + return GF_NOT_SUPPORTED; + } else { + parser->unicode_type = 1; + gzseek(gzInput, 2, SEEK_CUR); + } + } else if ((BOM[0]==0xEF) && (BOM[1]==0xBB) && (BOM[2]==0xBF)) { + /*we handle UTF8 as asci*/ + parser->unicode_type = 0; + gzseek(gzInput, 3, SEEK_CUR); + } + parser->gz_in = gzInput; + load->loader_priv = parser; + parser->def_symbols = gf_list_new(); + + parser->unresolved_routes = gf_list_new(); + parser->inserted_routes = gf_list_new(); + parser->undef_nodes = gf_list_new(); + parser->def_nodes = gf_list_new(); + parser->peeked_nodes = gf_list_new(); + parser->scripts = gf_list_new(); + + /*chunk parsing*/ + if (load->flags & GF_SM_LOAD_CONTEXT_READY) { + u32 i; + GF_StreamContext *sc; + if (!load->ctx) { + gf_sm_load_done_bt(load); + return GF_BAD_PARAM; + } + + /*restore context - note that base layer are ALWAYS declared BEFORE enhancement layers with gpac parsers*/ + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(load->ctx->streams, &i))) { + switch (sc->streamType) { + case GF_STREAM_SCENE: if (!parser->bifs_es) parser->bifs_es = sc; break; + case GF_STREAM_OD: if (!parser->od_es) parser->od_es = sc; break; + default: break; + } + } + /*need at least one scene stream*/ + if (!parser->bifs_es) { + gf_sm_load_done_bt(load); + return GF_BAD_PARAM; + } + parser->base_bifs_id = parser->bifs_es->ESID; + if (parser->od_es) parser->base_od_id = parser->od_es->ESID; + + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("BT: MPEG-4 (BT) Scene Chunk Parsing")); + + /*that's it, nothing else to do*/ + return GF_OK; + } + + parser->load = NULL; + gf_bt_check_line(parser); + parser->load = load; + + /*create at least one empty BIFS stream*/ + if (!parser->is_wrl) { + parser->bifs_es = gf_sm_stream_new(load->ctx, 0, GF_STREAM_SCENE, GPAC_OTI_SCENE_BIFS); + parser->bifs_au = gf_sm_stream_au_new(parser->bifs_es, 0, 0, 1); + parser->load->ctx->is_pixel_metrics = 1; + } + + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ( ((parser->is_wrl==2) ? "BT: X3D (WRL) Scene Parsing\n" : (parser->is_wrl ? "BT: VRML Scene Parsing\n" : "BT: MPEG-4 Scene Parsing\n")) )); + + /*default scene replace - we create it no matter what since it is used to store BIFS config + when parsing IOD.*/ + com = NULL; + if (!parser->is_wrl) { + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_SCENE_REPLACE); + gf_list_add(parser->bifs_au->commands, com); + } + e = gf_bt_loader_run_intern(parser, com, 1); + if (e) gf_sm_load_done_bt(load); + return e; +} + +void gf_sm_load_done_bt(GF_SceneLoader *load) +{ + GF_BTParser *parser = (GF_BTParser *)load->loader_priv; + if (!parser) return; + gf_list_del(parser->unresolved_routes); + gf_list_del(parser->inserted_routes); + gf_list_del(parser->undef_nodes); + gf_list_del(parser->def_nodes); + gf_list_del(parser->peeked_nodes); + while (gf_list_count(parser->def_symbols)) { + BTDefSymbol *d = (BTDefSymbol *)gf_list_get(parser->def_symbols, 0); + gf_list_rem(parser->def_symbols, 0); + free(d->name); + free(d->value); + free(d); + } + gf_list_del(parser->def_symbols); + gf_list_del(parser->scripts); + + gzclose(parser->gz_in); + free(parser->line_buffer); + free(parser); + load->loader_priv = NULL; + return; +} + +GF_Err gf_sm_load_run_bt(GF_SceneLoader *load) +{ + GF_BTParser *parser = (GF_BTParser *)load->loader_priv; + if (!parser) return GF_BAD_PARAM; + return gf_bt_loader_run_intern(parser, NULL, 0); +} + + +GF_List *gf_sm_load_bt_from_string(GF_SceneGraph *in_scene, char *node_str) +{ + GF_SceneLoader ctx; + GF_BTParser parser; + memset(&ctx, 0, sizeof(GF_SceneLoader)); + ctx.scene_graph = in_scene; + memset(&parser, 0, sizeof(GF_BTParser)); + parser.line_buffer = node_str; + parser.line_size = strlen(node_str); + parser.load = &ctx; + parser.top_nodes = gf_list_new(); + parser.undef_nodes = gf_list_new(); + parser.def_nodes = gf_list_new(); + parser.peeked_nodes = gf_list_new(); + parser.is_wrl = 1; + gf_bt_loader_run_intern(&parser, NULL, 1); + gf_list_del(parser.undef_nodes); + gf_list_del(parser.def_nodes); + gf_list_del(parser.peeked_nodes); + while (gf_list_count(parser.def_symbols)) { + BTDefSymbol *d = (BTDefSymbol *)gf_list_get(parser.def_symbols, 0); + gf_list_rem(parser.def_symbols, 0); + free(d->name); + free(d->value); + free(d); + } + gf_list_del(parser.def_symbols); + gf_list_del(parser.scripts); + + return parser.top_nodes; +} + + + +GF_Err gf_sm_load_done_bt_string(GF_SceneLoader *load) +{ + GF_BTParser *parser = (GF_BTParser *)load->loader_priv; + if (!parser) return GF_OK; + gf_list_del(parser->unresolved_routes); + gf_list_del(parser->inserted_routes); + gf_list_del(parser->undef_nodes); + gf_list_del(parser->def_nodes); + gf_list_del(parser->scripts); + free(parser); + load->loader_priv = NULL; + return GF_OK; +} + +GF_Err gf_sm_load_init_bt_string(GF_SceneLoader *load, char *str) +{ + GF_Err e; + GF_BTParser *parser; + GF_Command *com; + + if (!load || (!load->ctx && !load->scene_graph) || !str) return GF_BAD_PARAM; + if (!load->scene_graph) load->scene_graph = load->ctx->scene_graph; + + parser = (GF_BTParser *)malloc(sizeof(GF_BTParser)); + if (parser) memset(parser, 0, sizeof(GF_BTParser)); + else + return GF_OUT_OF_MEM; + + parser->is_wrl = 0; + parser->load = load; + /*we suppose an ascii string*/ + parser->unicode_type = 0; + + parser->line_buffer = str; + parser->line_size = (s32)strlen(str); + parser->gz_in = NULL; + + load->loader_priv = parser; + + parser->unresolved_routes = gf_list_new(); + parser->inserted_routes = gf_list_new(); + parser->undef_nodes = gf_list_new(); + parser->def_nodes = gf_list_new(); + parser->scripts = gf_list_new(); + + /*chunk parsing*/ + if (load->flags & GF_SM_LOAD_CONTEXT_READY) { + u32 i; + GF_StreamContext *sc; + if (!load->ctx) { + gf_sm_load_done_bt(load); + return GF_BAD_PARAM; + } + + /*restore context - note that base layer are ALWAYS declared BEFORE enhancement layers with gpac parsers*/ + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(load->ctx->streams, &i))){ + switch (sc->streamType) { + case GF_STREAM_SCENE: + case GF_STREAM_PRIVATE_SCENE: + if (!parser->bifs_es) parser->bifs_es = sc; break; + case GF_STREAM_OD: if (!parser->od_es) parser->od_es = sc; break; + default: break; + } + } + /*scene creation - pick up a size*/ + if (!parser->bifs_es) { + parser->bifs_es = gf_sm_stream_new(load->ctx, 0, GF_STREAM_SCENE, 0); + + parser->load->ctx->scene_width = 0; + parser->load->ctx->scene_height = 0; + parser->load->ctx->is_pixel_metrics = 1; + + } + else parser->base_bifs_id = parser->bifs_es->ESID; + if (parser->od_es) parser->base_od_id = parser->od_es->ESID; + /*that's it, nothing else to do*/ + return GF_OK; + } + + /*create at least one empty BIFS stream*/ + parser->bifs_es = gf_sm_stream_new(load->ctx, 0, GF_STREAM_SCENE, 0); + parser->bifs_au = gf_sm_stream_au_new(parser->bifs_es, 0, 0, 1); + + /*default scene replace - we create it no matter what since it is used to store BIFS config + when parsing IOD.*/ + com = gf_sg_command_new(parser->load->scene_graph, GF_SG_SCENE_REPLACE); + gf_list_add(parser->bifs_au->commands, com); + e = gf_bt_loader_run_intern(parser, com, 1); + if (e) gf_sm_load_done_bt_string(load); + return e; +} diff --git a/src/scene_manager/loader_isom.c b/src/scene_manager/loader_isom.c new file mode 100644 index 0000000..a6cc6b5 --- /dev/null +++ b/src/scene_manager/loader_isom.c @@ -0,0 +1,368 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#ifndef GPAC_DISABLE_SVG +#include +#endif + + + +#ifndef GPAC_READ_ONLY + +static void UpdateODCommand(GF_ISOFile *mp4, GF_ODCom *com) +{ + u32 i, j; + const char *szName; + char szPath[2048]; + + szName = gf_isom_get_filename(mp4); + if (com->tag == GF_ODF_OD_UPDATE_TAG) { + GF_ObjectDescriptor *od; + GF_ODUpdate *odU = (GF_ODUpdate *)com; + i=0; + while ((od = (GF_ObjectDescriptor *)gf_list_enum(odU->objectDescriptors, &i))) { + GF_ESD *esd; + j=0; + while ((esd = (GF_ESD *)gf_list_enum(od->ESDescriptors, &j))) { + if (esd->URLString) continue; + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: + case GF_STREAM_SCENE: + break; + /*dump the OCR track duration in case the OCR is used by media controls & co*/ + case GF_STREAM_OCR: + { + u32 track; + Double dur; + GF_MuxInfo *mi = (GF_MuxInfo *) gf_odf_desc_new(GF_ODF_MUXINFO_TAG); + gf_list_add(esd->extensionDescriptors, mi); + track = gf_isom_get_track_by_id(mp4, esd->ESID); + dur = (Double) (s64) gf_isom_get_track_duration(mp4, track); + dur /= gf_isom_get_timescale(mp4); + mi->duration = (u32) (dur * 1000); + } + break; + default: + { + GF_MuxInfo *mi = (GF_MuxInfo *) gf_odf_desc_new(GF_ODF_MUXINFO_TAG); + gf_list_add(esd->extensionDescriptors, mi); + sprintf(szPath, "%s#%d", szName, esd->ESID); + mi->file_name = strdup(szPath); + mi->streamFormat = strdup("MP4"); + } + break; + } + } + } + return; + } + if (com->tag == GF_ODF_ESD_UPDATE_TAG) { + GF_ESD *esd; + GF_ESDUpdate *esdU = (GF_ESDUpdate *)com; + i=0; + while ((esd = (GF_ESD *)gf_list_enum(esdU->ESDescriptors, &i))) { + if (esd->URLString) continue; + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: + case GF_STREAM_SCENE: + break; + /*dump the OCR track duration in case the OCR is used by media controls & co*/ + case GF_STREAM_OCR: + { + u32 track; + Double dur; + GF_MuxInfo *mi = (GF_MuxInfo *) gf_odf_desc_new(GF_ODF_MUXINFO_TAG); + gf_list_add(esd->extensionDescriptors, mi); + track = gf_isom_get_track_by_id(mp4, esd->ESID); + dur = (Double) (s64) gf_isom_get_track_duration(mp4, track); + dur /= gf_isom_get_timescale(mp4); + mi->duration = (u32) (dur * 1000); + } + break; + default: + { + GF_MuxInfo *mi = (GF_MuxInfo *) gf_odf_desc_new(GF_ODF_MUXINFO_TAG); + gf_list_add(esd->extensionDescriptors, mi); + sprintf(szPath, "%s#%d", szName, esd->ESID); + mi->file_name = strdup(szPath); + mi->streamFormat = strdup("MP4"); + } + break; + } + } + return; + } +} + +static void mp4_report(GF_SceneLoader *load, GF_Err e, char *format, ...) +{ +#ifndef GPAC_DISABLE_LOG + if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + char szMsg[1024]; + va_list args; + va_start(args, format); + vsprintf(szMsg, format, args); + va_end(args); + GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_PARSER, ("[MP4 Loading] %s\n", szMsg) ); + } +#endif +} + +GF_Err gf_sm_load_run_isom(GF_SceneLoader *load) +{ + GF_Err e; + FILE *logs; + u32 i, j, di, nbBifs, nbLaser, nb_samp, samp_done, init_offset; + GF_StreamContext *sc; + GF_ESD *esd; + GF_ODCodec *od_dec; + GF_BifsDecoder *bifs_dec; +#ifndef GPAC_DISABLE_SVG + GF_LASeRCodec *lsr_dec; +#endif + + if (!load || !load->isom) return GF_BAD_PARAM; + + nbBifs = nbLaser = 0; + e = GF_OK; + bifs_dec = gf_bifs_decoder_new(load->scene_graph, 1); + od_dec = gf_odf_codec_new(); + logs = NULL; +#ifndef GPAC_DISABLE_SVG + lsr_dec = gf_laser_decoder_new(load->scene_graph); +#endif + esd = NULL; + /*load each stream*/ + nb_samp = 0; + for (i=0; iisom); i++) { + u32 type = gf_isom_get_media_type(load->isom, i+1); + switch (type) { + case GF_ISOM_MEDIA_SCENE: + case GF_ISOM_MEDIA_OD: + nb_samp += gf_isom_get_sample_count(load->isom, i+1); + break; + default: + break; + } + } + samp_done = 1; + gf_isom_text_set_streaming_mode(load->isom, 1); + + for (i=0; iisom); i++) { + u32 type = gf_isom_get_media_type(load->isom, i+1); + switch (type) { + case GF_ISOM_MEDIA_SCENE: + break; + case GF_ISOM_MEDIA_OD: + break; + default: + continue; + } + esd = gf_isom_get_esd(load->isom, i+1, 1); + if (!esd) continue; + + sc = gf_sm_stream_new(load->ctx, esd->ESID, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); + sc->streamType = esd->decoderConfig->streamType; + sc->ESID = esd->ESID; + sc->objectType = esd->decoderConfig->objectTypeIndication; + sc->timeScale = gf_isom_get_media_timescale(load->isom, i+1); + + /*we still need to reconfig the BIFS*/ + if (esd->decoderConfig->streamType==GF_STREAM_SCENE) { + /*BIFS*/ + if (esd->decoderConfig->objectTypeIndication<=2) { + if (!esd->dependsOnESID && nbBifs && !i) + mp4_report(load, GF_OK, "several scene namespaces used or improper scene dependencies in file - import may be incorrect"); + if (!esd->decoderConfig->decoderSpecificInfo) { + /* Hack for T-DMB non compliant streams */ + e = gf_bifs_decoder_configure_stream(bifs_dec, esd->ESID, NULL, 0, esd->decoderConfig->objectTypeIndication); + } else { + e = gf_bifs_decoder_configure_stream(bifs_dec, esd->ESID, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, esd->decoderConfig->objectTypeIndication); + } + if (e) goto exit; + nbBifs++; + } +#ifndef GPAC_DISABLE_SVG + /*LASER*/ + else if (esd->decoderConfig->objectTypeIndication==0x09) { + if (!esd->dependsOnESID && nbBifs && !i) + mp4_report(load, GF_OK, "several scene namespaces used or improper scene dependencies in file - import may be incorrect"); + e = gf_laser_decoder_configure_stream(lsr_dec, esd->ESID, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); + if (e) goto exit; + nbLaser++; + } +#endif + } + + init_offset = 0; + /*dump all AUs*/ + for (j=0; jisom, i+1); j++) { + GF_AUContext *au; + GF_ISOSample *samp = gf_isom_get_sample(load->isom, i+1, j+1, &di); + if (!samp) { + mp4_report(load, gf_isom_last_error(load->isom), "Unable to fetch sample %d from track ID %d - aborting track import", j+1, gf_isom_get_track_id(load->isom, i+1)); + break; + } + /*check if track has initial offset*/ + if (!j && gf_isom_get_edit_segment_count(load->isom, i+1)) { + u64 EditTime, dur, mtime; + u8 mode; + gf_isom_get_edit_segment(load->isom, i+1, 1, &EditTime, &dur, &mtime, &mode); + if (mode==GF_ISOM_EDIT_EMPTY) { + init_offset = (u32) (dur * sc->timeScale / gf_isom_get_timescale(load->isom) ); + } + } + samp->DTS += init_offset; + + au = gf_sm_stream_au_new(sc, samp->DTS, ((Double)(s64) samp->DTS) / sc->timeScale, samp->IsRAP); + + if (esd->decoderConfig->streamType==GF_STREAM_SCENE) { + if (esd->decoderConfig->objectTypeIndication<=2) + e = gf_bifs_decode_command_list(bifs_dec, esd->ESID, samp->data, samp->dataLength, au->commands); +#ifndef GPAC_DISABLE_SVG + else if (esd->decoderConfig->objectTypeIndication==0x09) + e = gf_laser_decode_command_list(lsr_dec, esd->ESID, samp->data, samp->dataLength, au->commands); +#endif + } else { + e = gf_odf_codec_set_au(od_dec, samp->data, samp->dataLength); + if (!e) e = gf_odf_codec_decode(od_dec); + if (!e) { + while (1) { + GF_ODCom *odc = gf_odf_codec_get_com(od_dec); + if (!odc) break; + /*update ESDs if any*/ + UpdateODCommand(load->isom, odc); + gf_list_add(au->commands, odc); + } + } + } + gf_isom_sample_del(&samp); + if (e) { + mp4_report(load, gf_isom_last_error(load->isom), "decoding sample %d from track ID %d failed", j+1, gf_isom_get_track_id(load->isom, i+1)); + goto exit; + } + + samp_done++; + gf_set_progress("MP4 Loading", samp_done, nb_samp); + } + gf_odf_desc_del((GF_Descriptor *) esd); + esd = NULL; + } + gf_isom_text_set_streaming_mode(load->isom, 0); + +exit: + gf_bifs_decoder_del(bifs_dec); + gf_odf_codec_del(od_dec); +#ifndef GPAC_DISABLE_SVG + gf_laser_decoder_del(lsr_dec); +#endif + if (esd) gf_odf_desc_del((GF_Descriptor *) esd); + if (logs) fclose(logs); + return e; +} + +GF_Err gf_sm_load_init_isom(GF_SceneLoader *load) +{ + u32 i, track; + GF_BIFSConfig *bc; + GF_ESD *esd; + GF_Err e; + char *scene_msg = "MPEG-4 BIFS Scene Parsing"; + if (!load->isom) return GF_BAD_PARAM; + + /*load IOD*/ + load->ctx->root_od = (GF_ObjectDescriptor *) gf_isom_get_root_od(load->isom); + if (!load->ctx->root_od) { + e = gf_isom_last_error(load->isom); + if (e) return e; + } else if ((load->ctx->root_od->tag != GF_ODF_OD_TAG) && (load->ctx->root_od->tag != GF_ODF_IOD_TAG)) { + gf_odf_desc_del((GF_Descriptor *) load->ctx->root_od); + load->ctx->root_od = NULL; + } + + esd = NULL; + + /*get root scene stream*/ + for (i=0; iisom); i++) { + u32 type = gf_isom_get_media_type(load->isom, i+1); + if (type != GF_ISOM_MEDIA_SCENE) continue; + if (! gf_isom_is_track_in_root_od(load->isom, i+1) ) continue; + esd = gf_isom_get_esd(load->isom, i+1, 1); + + if (esd && esd->URLString) { + gf_odf_desc_del((GF_Descriptor *)esd); + esd = NULL; + continue; + } + + /*make sure we load the root BIFS stream first*/ + if (esd && esd->dependsOnESID && (esd->dependsOnESID!=esd->ESID) ) { + u32 track = gf_isom_get_track_by_id(load->isom, esd->dependsOnESID); + if (gf_isom_get_media_type(load->isom, track) != GF_ISOM_MEDIA_OD) { + gf_odf_desc_del((GF_Descriptor *)esd); + esd = NULL; + continue; + } + } + if (esd->decoderConfig->objectTypeIndication==0x09) scene_msg = "MPEG-4 LASeR Scene Parsing"; + break; + } + if (!esd) return GF_OK; + + e = GF_OK; + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("%s\n", scene_msg)); + + track = i+1; + + /*BIFS: update size & pixel metrics info*/ + if (esd->decoderConfig->objectTypeIndication<=2) { + bc = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication); + if (!bc->elementaryMasks && bc->pixelWidth && bc->pixelHeight) { + load->ctx->scene_width = bc->pixelWidth; + load->ctx->scene_height = bc->pixelHeight; + load->ctx->is_pixel_metrics = bc->pixelMetrics; + } + gf_odf_desc_del((GF_Descriptor *) bc); + /*note we don't load the first BIFS AU to avoid storing the BIFS decoder, needed to properly handle quantization*/ + } + /*LASeR*/ + else if (esd->decoderConfig->objectTypeIndication==0x09) { + load->ctx->is_pixel_metrics = 1; + } + gf_odf_desc_del((GF_Descriptor *) esd); + esd = NULL; + + return GF_OK; +} + +void gf_sm_load_done_isom(GF_SceneLoader *load) +{ + /*nothing to do the file is not ours*/ + +} + +#endif diff --git a/src/scene_manager/loader_qt.c b/src/scene_manager/loader_qt.c new file mode 100644 index 0000000..0698f79 --- /dev/null +++ b/src/scene_manager/loader_qt.c @@ -0,0 +1,190 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +#ifndef GPAC_READ_ONLY + +#include + + +static GF_Err gf_qt_report(GF_SceneLoader *load, GF_Err e, char *format, ...) +{ +#ifndef GPAC_DISABLE_LOG + if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + char szMsg[1024]; + va_list args; + va_start(args, format); + vsprintf(szMsg, format, args); + va_end(args); + GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_PARSER, ("[QT Parsing] %s\n", szMsg) ); + } +#endif + return e; +} + + +/*import cubic QTVR to mp4*/ +GF_Err gf_sm_load_init_qt(GF_SceneLoader *load) +{ + u32 i, di, w, h, tk, pano_t, nb_samp; + Bool has_qtvr; + GF_ISOSample *samp; + GF_ISOFile *src; + GF_StreamContext *st; + GF_AUContext *au; + GF_Command *com; + M_Background *back; + M_NavigationInfo *ni; + M_Group *gr; + GF_ODUpdate *odU; + GF_SceneGraph *sg; + GF_ObjectDescriptor *od; + GF_ESD *esd; + + if (!load->ctx) return GF_NOT_SUPPORTED; + + src = gf_isom_open(load->fileName, GF_ISOM_OPEN_READ, NULL); + if (!src) return gf_qt_report(load, GF_URL_ERROR, "Opening file %s failed", load->fileName); + + w = h = tk = 0; + pano_t = 0; + nb_samp = 0; + + has_qtvr = 0; + for (i=0; iwidth>w) || (udesc->height>h)) { + w = udesc->width; + h = udesc->height; + tk = i+1; + nb_samp = gf_isom_get_sample_count(src, i+1); + } + if (udesc->extension_buf) free(udesc->extension_buf); + free(udesc); + } + break; + case GF_4CC('q','t','v','r'): + has_qtvr = 1; + break; + } + } + if (!has_qtvr) { + gf_isom_delete(src); + return gf_qt_report(load, GF_NOT_SUPPORTED, "QTVR not found - no conversion available for this QuickTime movie"); + } + if (!tk) { + gf_isom_delete(src); + return gf_qt_report(load, GF_NON_COMPLIANT_BITSTREAM, "No associated visual track with QTVR movie"); + } + if (nb_samp!=6) { + gf_isom_delete(src); + return gf_qt_report(load, GF_NOT_SUPPORTED, "Movie %s doesn't look a Cubic QTVR - sorry...", load->fileName); + } + + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("QT: Importing Cubic QTVR Movie")); + + /*create scene*/ + sg = load->ctx->scene_graph; + gr = (M_Group *) gf_node_new(sg, TAG_MPEG4_Group); + gf_node_register((GF_Node *)gr, NULL); + st = gf_sm_stream_new(load->ctx, 1, GF_STREAM_SCENE, 1); + au = gf_sm_stream_au_new(st, 0, 0, 1); + com = gf_sg_command_new(load->ctx->scene_graph, GF_SG_SCENE_REPLACE); + gf_list_add(au->commands, com); + com->node = (GF_Node *)gr; + + back = (M_Background *) gf_node_new(sg, TAG_MPEG4_Background); + gf_node_list_add_child( &gr->children, (GF_Node*)back); + gf_node_register((GF_Node *)back, (GF_Node *)gr); + + gf_sg_vrml_mf_alloc(&back->leftUrl, GF_SG_VRML_MFURL, 1); + back->leftUrl.vals[0].OD_ID = 2; + gf_sg_vrml_mf_alloc(&back->frontUrl, GF_SG_VRML_MFURL, 1); + back->frontUrl.vals[0].OD_ID = 3; + gf_sg_vrml_mf_alloc(&back->rightUrl, GF_SG_VRML_MFURL, 1); + back->rightUrl.vals[0].OD_ID = 4; + gf_sg_vrml_mf_alloc(&back->backUrl, GF_SG_VRML_MFURL, 1); + back->backUrl.vals[0].OD_ID = 5; + gf_sg_vrml_mf_alloc(&back->topUrl, GF_SG_VRML_MFURL, 1); + back->topUrl.vals[0].OD_ID = 6; + gf_sg_vrml_mf_alloc(&back->bottomUrl, GF_SG_VRML_MFURL, 1); + back->bottomUrl.vals[0].OD_ID = 7; + + ni = (M_NavigationInfo *) gf_node_new(sg, TAG_MPEG4_NavigationInfo); + gf_node_list_add_child(&gr->children, (GF_Node*)ni); + gf_node_register((GF_Node *)ni, (GF_Node *)gr); + gf_sg_vrml_mf_reset(&ni->type, GF_SG_VRML_MFSTRING); + gf_sg_vrml_mf_alloc(&ni->type, GF_SG_VRML_MFSTRING, 1); + ni->type.vals[0] = strdup("QTVR"); + + /*create ODs*/ + st = gf_sm_stream_new(load->ctx, 2, GF_STREAM_OD, 1); + au = gf_sm_stream_au_new(st, 0, 0, 1); + odU = (GF_ODUpdate*) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + gf_list_add(au->commands, odU); + for (i=0; i<6; i++) { + GF_MuxInfo *mi; + FILE *img; + char szName[1024]; + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 2+i; + esd = gf_odf_desc_esd_new(2); + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + esd->decoderConfig->objectTypeIndication = 0x6C; + esd->ESID = 3+i; + /*extract image and remember it*/ + mi = (GF_MuxInfo *) gf_odf_desc_new(GF_ODF_MUXINFO_TAG); + gf_list_add(esd->extensionDescriptors, mi); + mi->delete_file = 1; + sprintf(szName, "%s_img%d.jpg", load->fileName, esd->ESID); + mi->file_name = strdup(szName); + + gf_list_add(od->ESDescriptors, esd); + gf_list_add(odU->objectDescriptors, od); + + samp = gf_isom_get_sample(src, tk, i+1, &di); + img = fopen(mi->file_name, "wb"); + fwrite(samp->data, samp->dataLength, 1, img); + fclose(img); + gf_isom_sample_del(&samp); + } + gf_isom_delete(src); + return GF_OK; +} + +void gf_sm_load_done_qt(GF_SceneLoader *load) +{ +} +GF_Err gf_sm_load_run_qt(GF_SceneLoader *load) +{ + return GF_OK; +} + +#endif diff --git a/src/scene_manager/loader_svg.c b/src/scene_manager/loader_svg.c new file mode 100644 index 0000000..c09c41f --- /dev/null +++ b/src/scene_manager/loader_svg.c @@ -0,0 +1,1840 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#ifndef GPAC_DISABLE_SVG + +typedef struct _st_entry +{ + struct _st_entry *next; + /*as refered to by xlink-href*/ + char *stream_name; + /*stream id*/ + u32 id; + const char *nhml_info; +} SVG_SAFExternalStream; + +typedef struct +{ + GF_SceneLoader *load; + GF_Err last_error; + GF_SAXParser *sax_parser; + Bool has_root; + + /* stack of SVG nodes*/ + GF_List *node_stack; + + GF_List *defered_hrefs; + GF_List *defered_animations; + GF_List *defered_listeners; + /*non-linear parsing*/ + GF_List *peeked_nodes; + + /*LASeR parsing*/ + u32 command_depth; + GF_StreamContext *laser_es; + GF_AUContext *laser_au; + GF_Command *command; + /*SAF AU maps to OD AU and is used for each new media declaration*/ + GF_AUContext *saf_au; + GF_StreamContext *saf_es; + + SVG_SAFExternalStream *streams; + + /*the namespace of the parent element. This is a shortcut to avoid querying the namespace list stored + in the scene graph to find out the namespace of the current element. + For example in , it avoids looking for the namespace when parsing + */ + u32 current_ns; + + /*if parser is used to parse a fragment, the root of the fragment is stored here*/ + GF_Node *fragment_root; +} GF_SVG_Parser; + +typedef struct { + /* Stage of the resolving: + 0: resolving attributes which depends on the target: from, to, by, values, type + 1: resolving begin times + 2: resolving end times */ + u32 resolve_stage; + /* Animation element being defered */ + SVG_Element *animation_elt; + /* anim parent*/ + SVG_Element *anim_parent; + /* target animated element*/ + SVG_Element *target; + /* id of the target element when unresolved*/ + char *target_id; + + /* attributes which cannot be parsed until the type of the target attribute is known */ + char *type; /* only for animateTransform */ + char *to; + char *from; + char *by; + char *values; +} SVG_DeferedAnimation; + +typedef struct +{ + /*top of parsed sub-tree*/ + SVG_Element *node; + /*the current element is animation that cannot be parsed completely + upon reception of start tag of element but for which we may be able + to parse at the end tag of the element (animateMotion)*/ + SVG_DeferedAnimation *anim; + /*depth of unknown elements being skipped*/ + u32 unknown_depth; + /*last child added, used to speed-up parsing*/ + GF_ChildNodeItem *last_child; + + /*the namespace of the parent element (used for restoring the context after the current node has been parsed)*/ + u32 current_ns; + Bool has_ns; +} SVG_NodeStack; + +static GF_Err svg_report(GF_SVG_Parser *parser, GF_Err e, char *format, ...) +{ +#ifndef GPAC_DISABLE_LOG + if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + char szMsg[2048]; + va_list args; + va_start(args, format); + vsprintf(szMsg, format, args); + va_end(args); + GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_PARSER, ("[SVG Parsing] line %d - %s\n", gf_xml_sax_get_line(parser->sax_parser), szMsg)); + } +#endif + if (e) parser->last_error = e; + return e; +} + +static void svg_progress(void *cbk, u32 done, u32 total) +{ + GF_SVG_Parser *parser = (GF_SVG_Parser *)cbk; + if (parser->load && parser->load->is && parser->load->is->scene_codec && parser->load->is->scene_codec->odm) { + gf_term_service_media_event(parser->load->is->scene_codec->odm, GF_EVENT_MEDIA_DATA_PROGRESS); + if (done == total) + gf_term_service_media_event(parser->load->is->scene_codec->odm, GF_EVENT_MEDIA_END_OF_DATA); + } + gf_set_progress("SVG (Dynamic Attribute List) Parsing", done, total); +} + +static SVG_SAFExternalStream *svg_saf_get_stream(GF_SVG_Parser *parser, u32 id, const char *name) +{ + SVG_SAFExternalStream *st; + st = parser->streams; + while (st) { + if (id == st->id) return st; + if (name && !strcmp(name, st->stream_name) ) return st; + st = st->next; + } + return NULL; +} + +static SVG_SAFExternalStream *svg_saf_get_next_available_stream(GF_SVG_Parser *parser) +{ + /*1 reserved for base laser stream*/ + u32 id = 1; + SVG_SAFExternalStream *st = parser->streams; + SVG_SAFExternalStream *prev = NULL; + + while (st) { + prev = st; + id = st->id; + st = st->next; + } + GF_SAFEALLOC(st, SVG_SAFExternalStream); + if (prev) prev->next = st; + else parser->streams = st; + st->id = id+1; + return st; +} + +static void svg_lsr_set_v2(GF_SVG_Parser *parser) +{ + u32 i; + if (parser->load->ctx && parser->load->ctx->root_od) { + for (i=0; iload->ctx->root_od->ESDescriptors); i++) { + GF_ESD *esd = gf_list_get(parser->load->ctx->root_od->ESDescriptors, i); + if (esd->decoderConfig->streamType==GF_STREAM_SCENE) { + GF_LASERConfig *cfg = (GF_LASERConfig *)esd->decoderConfig->decoderSpecificInfo; + if (cfg && (cfg->tag==GF_ODF_LASER_CFG_TAG)) { + if (!cfg->extensionIDBits) cfg->extensionIDBits = 2; + } + } + } + } +} + + +static void svg_process_media_href(GF_SVG_Parser *parser, XMLRI *iri) +{ + SVG_SAFExternalStream *st = svg_saf_get_stream(parser, 0, iri->string+1); + if (st) { + free(iri->string); + iri->string = NULL; + iri->lsr_stream_id = st->id; + iri->type = XMLRI_STREAMID; + } +} + +static void xsr_exec_command_list(GF_Node *node, void *par, Bool is_destroy) +{ + GF_DOMUpdates *up = (GF_DOMUpdates *)node; + if (is_destroy || !up || (up->sgprivate->tag!=TAG_DOMUpdates)) return; + gf_sg_command_apply_list(up->sgprivate->scenegraph, up->updates, 0); +} + +static GF_Node *svg_find_node(GF_SVG_Parser *parser, char *ID) +{ + u32 i, count, tag; + char *node_class; + GF_Node *n = gf_sg_find_node_by_name(parser->load->scene_graph, ID); + if (n) return n; + + count = gf_list_count(parser->peeked_nodes); + for (i=0; ipeeked_nodes, i); + if (!strcmp(gf_node_get_name(n), ID)) return n; + } + node_class = gf_xml_sax_peek_node(parser->sax_parser, "id", ID, NULL, NULL, NULL, NULL); + if (!node_class) return NULL; + + tag = gf_xml_get_element_tag(node_class, parser->current_ns); + n = gf_node_new(parser->load->scene_graph, tag); + + free(node_class); + + if (n) { + gf_svg_parse_element_id(n, ID, 0); + gf_list_add(parser->peeked_nodes, n); + } + return n; +} + +static void svg_post_process_href(GF_SVG_Parser *parser, XMLRI *iri) +{ + GF_Err e; + /*keep data when encoding*/ + if ( !(parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK)) return; + + /*unresolved, queue it...*/ + if ((iri->type==XMLRI_ELEMENTID) && !iri->target && iri->string) { + gf_list_add(parser->defered_hrefs, iri); + } + if (iri->type != XMLRI_STRING) return; + e = gf_node_store_embedded_data(iri, parser->load->localPath, parser->load->fileName); + if (e) svg_report(parser, e, "Error storing embedded IRI data"); +} + +static void svg_delete_defered_anim(SVG_DeferedAnimation *anim, GF_List *defered_animations) +{ + if (defered_animations) gf_list_del_item(defered_animations, anim); + + if (anim->target_id) free(anim->target_id); + if (anim->to) free(anim->to); + if (anim->from) free(anim->from); + if (anim->by) free(anim->by); + if (anim->values) free(anim->values); + if (anim->type) free(anim->type); + free(anim); +} + +static Bool svg_parse_animation(GF_SVG_Parser *parser, GF_SceneGraph *sg, SVG_DeferedAnimation *anim, const char *nodeID, u32 force_type) +{ + GF_FieldInfo info; + u32 tag; + u8 anim_value_type = 0; + + if (anim->resolve_stage==0) { + /* Stage 0: parsing the animation attribute values + for that we need to resolve the target first */ + if (!anim->target) + anim->target = (SVG_Element *) gf_sg_find_node_by_name(sg, anim->target_id + 1); + + if (anim->target) { + XMLRI *iri; + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_XLINK_ATT_href, 1, 0, &info); + iri = (XMLRI *)info.far_ptr; + iri->type = XMLRI_ELEMENTID; + iri->target = anim->target; + gf_node_register_iri(sg, iri); + } + + tag = gf_node_get_tag((GF_Node *)anim->animation_elt); + /* get the attribute name attribute if specified */ + if (anim->type && (tag== TAG_SVG_animateTransform) ) { + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_transform_type, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->type, 0); + switch(*(SVG_TransformType *) info.far_ptr) { + case SVG_TRANSFORM_TRANSLATE: + anim_value_type = SVG_Transform_Translate_datatype; + break; + case SVG_TRANSFORM_SCALE: + anim_value_type = SVG_Transform_Scale_datatype; + break; + case SVG_TRANSFORM_ROTATE: + anim_value_type = SVG_Transform_Rotate_datatype; + break; + case SVG_TRANSFORM_SKEWX: + anim_value_type = SVG_Transform_SkewX_datatype; + break; + case SVG_TRANSFORM_SKEWY: + anim_value_type = SVG_Transform_SkewY_datatype; + break; + case SVG_TRANSFORM_MATRIX: + anim_value_type = SVG_Transform_datatype; + break; + default: + svg_report(parser, GF_OK, "unknown datatype for animate transform"); + return 0; + } + } + else if (gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_attributeName, 0, 0, &info) == GF_OK) { + SMIL_AttributeName *attname = (SMIL_AttributeName *)info.far_ptr; + + /*parse the attribute name even if the target is not found, because a namespace could be specified and + only valid for the current node*/ + if (!attname->type) { + char *sep; + char *name = attname->name; + sep = strchr(name, ':'); + if (sep) { + sep[0] = 0; + attname->type = gf_sg_get_namespace_code(anim->animation_elt->sgprivate->scenegraph, name); + sep[0] = ':'; + name = strdup(sep+1); + free(attname->name); + attname->name = name; + } else { + attname->type = parser->current_ns; + } + } + + /* the target is still not known stay in stage 0 */ + if (!anim->target) return 0; + + gf_node_get_attribute_by_name((GF_Node *)anim->target, attname->name, attname->type, 1, 1, &info); + /*set the tag value to avoid parsing the name in the anim node_init phase*/ + attname->tag = info.fieldIndex; + attname->type = 0; + anim_value_type = info.fieldType; + } else { + if (tag == TAG_SVG_animateMotion) { + anim_value_type = SVG_Motion_datatype; + } else if (tag == TAG_SVG_discard) { + /* there is no value to parse in discard, we can jump to the next stage */ + anim->resolve_stage = 1; + return svg_parse_animation(parser, sg, anim, nodeID, 0); + } else { + svg_report(parser, GF_OK, "Missing attributeName attribute on %s", gf_node_get_name((GF_Node *)anim->animation_elt)); + return 0; + } + } + + /* the target is still not known stay in stage 0 */ + if (!anim->target) return 0; + + if (anim->to) { + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_to, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->to, anim_value_type); + if (anim_value_type==XMLRI_datatype) + svg_post_process_href(parser, (XMLRI*)((SMIL_AnimateValue *)info.far_ptr)->value); + } + if (anim->from) { + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_from, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->from, anim_value_type); + if (anim_value_type==XMLRI_datatype) + svg_post_process_href(parser, (XMLRI*)((SMIL_AnimateValue *)info.far_ptr)->value); + } + if (anim->by) { + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_by, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->by, anim_value_type); + if (anim_value_type==XMLRI_datatype) + svg_post_process_href(parser, (XMLRI*)((SMIL_AnimateValue *)info.far_ptr)->value); + } + if (anim->values) { + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_values, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->values, anim_value_type); + if (anim_value_type==XMLRI_datatype) { + u32 i, count; + SMIL_AnimateValues *anim_values; + anim_values = (SMIL_AnimateValues *)info.far_ptr; + count = gf_list_count(anim_values->values); + for (i=0; ivalues, i); + svg_post_process_href(parser, iri); + } + } + } + anim->resolve_stage = 1; + } + + if (anim->resolve_stage == 1) { + /* Stage 1: parsing the begin values + we go into the next stage only if at least one begin value is resolved */ + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_begin, 1, 0, &info); + if (gf_svg_resolve_smil_times((GF_Node *)anim->animation_elt, anim->target, *(GF_List **)info.far_ptr, 0, nodeID)) { + anim->resolve_stage = 2; + } else if (force_type!=2) { + return 0; + } + } + + gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_end, 1, 0, &info); + if (!gf_svg_resolve_smil_times((GF_Node *)anim->animation_elt, anim->target, *(GF_List **)info.far_ptr, 1, nodeID)) { + if (force_type!=2) return 0; + } + + /*animateMotion needs its children to be parsed before it can be initialized !! */ + if (force_type || gf_node_get_tag((GF_Node *)anim->animation_elt) != TAG_SVG_animateMotion) { + gf_node_init((GF_Node *)anim->animation_elt); + return 1; + } else { + return 0; + } + +} + +static void svg_resolved_refs(GF_SVG_Parser *parser, GF_SceneGraph *sg, const char *nodeID) +{ + u32 count, i; + + /*check unresolved HREF*/ + count = gf_list_count(parser->defered_hrefs); + for (i=0; idefered_hrefs, i); + if (nodeID && strcmp(iri->string + 1, nodeID)) continue; + targ = gf_sg_find_node_by_name(sg, iri->string + 1); + if (targ) { + iri->type = XMLRI_ELEMENTID; + iri->target = targ; + gf_node_register_iri(sg, iri); + free(iri->string); + iri->string = NULL; + gf_list_rem(parser->defered_hrefs, i); + i--; + count--; + } + } + + /*check unresolved listeners */ + count = gf_list_count(parser->defered_listeners); + for (i=0; idefered_listeners, i); + + par = NULL; + if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_observer, 0, 0, &info) == GF_OK) { + XMLRI *observer = info.far_ptr; + if (observer->type == XMLRI_ELEMENTID) { + if (!observer->target && observer->string && !strcmp(observer->string, nodeID) ) { + observer->target = gf_sg_find_node_by_name(sg, (char*) nodeID); + } + } + if (observer->type == XMLRI_ELEMENTID) par = observer->target; + if (!par) continue; + } + if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_target, 0, 0, &info) == GF_OK) { + XMLRI *target = info.far_ptr; + if (target->type == XMLRI_ELEMENTID) { + if (!target->target) continue; + else { + if (!par) par = (GF_Node*)target->target; + } + } + } + assert(par); + gf_node_dom_listener_add((GF_Node *)par, (GF_Node *) listener); + gf_list_rem(parser->defered_listeners, i); + i--; + count--; + } + + /*check unresolved anims*/ + count = gf_list_count(parser->defered_animations); + for (i=0; idefered_animations, i); + /*resolve it - we don't check the name since it may be used in SMIL times, which we don't track at anim level*/ + if (svg_parse_animation(parser, sg, anim, nodeID, 1)) { + svg_delete_defered_anim(anim, parser->defered_animations); + i--; + count--; + } + } +} + +static void svg_init_root_element(GF_SVG_Parser *parser, SVG_Element *root_svg) +{ + GF_FieldInfo width_info, height_info; + u32 svg_w, svg_h; + svg_w = svg_h = 0; + if (!gf_node_get_attribute_by_tag((GF_Node *)root_svg, TAG_SVG_ATT_width, 0, 0, &width_info) + && !gf_node_get_attribute_by_tag((GF_Node *)root_svg, TAG_SVG_ATT_height, 0, 0, &height_info)) { + SVG_Length * w = width_info.far_ptr; + SVG_Length * h = height_info.far_ptr; + if (w->type == SVG_NUMBER_VALUE) svg_w = FIX2INT(w->value); + if (h->type == SVG_NUMBER_VALUE) svg_h = FIX2INT(h->value); + gf_sg_set_scene_size_info(parser->load->scene_graph, svg_w, svg_h, 1); + if (parser->load->ctx) { + parser->load->ctx->scene_width = svg_w; + parser->load->ctx->scene_height = svg_h; + } + } + if (parser->load->type == GF_SM_LOAD_XSR) { + assert(parser->command); + assert(parser->command->tag == GF_SG_LSR_NEW_SCENE); + parser->command->node = (GF_Node *)root_svg; + } + gf_sg_set_root_node(parser->load->scene_graph, (GF_Node *)root_svg); + parser->has_root = 1; +} + +//#define SKIP_ALL +//#define SKIP_ATTS +//#define SKIP_INIT + +//#define SKIP_UNKNOWN_NODES + + +static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes, SVG_NodeStack *parent, Bool *has_ns) +{ + GF_FieldInfo info; + u32 tag, i, count, ns, xmlns; + Bool needs_init, has_id; + SVG_Element *elt = NULL; + const char *node_name = NULL; + const char *ev_event, *ev_observer; + SVG_DeferedAnimation *anim = NULL; + char *ID = NULL; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[SVG Parsing] Parsing node %s\n", name)); + + *has_ns = 0; + /*parse all att for namespace*/ + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strncmp(att->name, "xmlns", 5)) { + char *qname = strchr(att->name, ':'); + if (qname) + qname++; + + gf_sg_add_namespace(parser->load->scene_graph, att->value, qname); + if (!qname) + parser->current_ns = gf_sg_get_namespace_code_from_name(parser->load->scene_graph, att->value); + + *has_ns = 1; + } + else if (!stricmp(att->name, "id") || !stricmp(att->name, "xml:id")) { + if (!ID) ID = att->value; + } + } + + xmlns = parser->current_ns; + if (name_space) { + xmlns = gf_sg_get_namespace_code(parser->load->scene_graph, (char *) name_space); + if (!xmlns) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] line %d - XMLNS prefix %s not defined - skipping\n", gf_xml_sax_get_line(parser->sax_parser), name_space)); + return NULL; + } + } + + /* Translates the node type (called name) from a String into a unique numeric identifier in GPAC */ + tag = xmlns ? gf_xml_get_element_tag(name, xmlns) : TAG_UndefinedNode; + if (tag == TAG_UndefinedNode) { +#ifdef SKIP_UNKNOWN_NODES + GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[SVG Parsing] line %d - Unknown element %s - skipping\n", gf_xml_sax_get_line(parser->sax_parser), name)); + return NULL; +#else + tag = TAG_DOMFullNode; +#endif + } + + has_id = 0; + count = gf_list_count(parser->peeked_nodes); + if (count && ID) { + for (i=0; ipeeked_nodes, i); + const char *n_id = gf_node_get_name(n); + if (n_id && !strcmp(n_id, ID)) { + gf_list_rem(parser->peeked_nodes, i); + has_id = 1; + elt = (SVG_Element*)n; + break; + } + } + } + + if (!has_id) { + /* Creates a node in the current scene graph */ + elt = (SVG_Element*)gf_node_new(parser->load->scene_graph, tag); + if (!elt) { + parser->last_error = GF_SG_UNKNOWN_NODE; + return NULL; + } + if (tag == TAG_DOMFullNode) { + GF_DOMFullNode *d = (GF_DOMFullNode *)elt; + d->name = strdup(name); + d->ns = xmlns; + } + } + + gf_node_register((GF_Node *)elt, (parent ? (GF_Node *)parent->node : NULL)); + if (parent && elt) gf_node_list_add_child_last( & parent->node->children, (GF_Node*)elt, & parent->last_child); + + + needs_init = 1; + + if (gf_svg_is_animation_tag(tag)) { + GF_SAFEALLOC(anim, SVG_DeferedAnimation); + /*default anim target is parent node*/ + anim->animation_elt = elt; + if (!parent) { + if (parser->command) { + anim->target = anim->anim_parent = (SVG_Element*) parser->command->node; + } + } else { + anim->target = anim->anim_parent = parent->node; + } + } else if (gf_svg_is_timing_tag(tag)) { + /* warning: we use the SVG_DeferedAnimation structure for some timing nodes which are not + animations, but we put the parse stage at 1 (timing) see svg_parse_animation. */ + GF_SAFEALLOC(anim, SVG_DeferedAnimation); + /*default anim target is parent node*/ + anim->animation_elt = elt; + if (!parent) { + if (parser->command) { + anim->target = anim->anim_parent = (SVG_Element*) parser->command->node; + } + } else { + anim->target = anim->anim_parent = parent->node; + } + anim->resolve_stage = 1; + } else if ((tag == TAG_SVG_script) || (tag==TAG_SVG_handler)) { + needs_init = 0; + } + + ev_event = ev_observer = NULL; + +#ifdef SKIP_ATTS + nb_attributes = 0; +#endif + + /*parse all att*/ + for (i=0; ivalue || !strlen(att->value)) continue; + + ns = xmlns; + att_name = strchr(att->name, ':'); + if (att_name) { + if (!strncmp(att->name, "xmlns", 5)) { + ns = gf_sg_get_namespace_code(parser->load->scene_graph, att_name+1); + att_name = att->name; + } else { + att_name[0] = 0; + ns = gf_sg_get_namespace_code(parser->load->scene_graph, att->name); + att_name[0] = ':'; + att_name++; + } + } else { + att_name = att->name; + } + + if (!stricmp(att_name, "style")) { + gf_svg_parse_style((GF_Node *)elt, att->value); + continue; + } + if (anim) { + if (!stricmp(att_name, "to")) { + anim->to = strdup(att->value); + continue; + } + if (!stricmp(att_name, "from")) { + anim->from = strdup(att->value); + continue; + } + if (!stricmp(att_name, "by")) { + anim->by = strdup(att->value); + continue; + } + if (!stricmp(att_name, "values")) { + anim->values = strdup(att->value); + continue; + } + if ((tag == TAG_SVG_animateTransform) && !stricmp(att_name, "type")) { + anim->type = strdup(att->value); + continue; + } + } + + if ((ns == GF_XMLNS_XLINK) && !stricmp(att_name, "href") ) { + if (gf_svg_is_animation_tag(tag)) { + assert(anim); + anim->target_id = strdup(att->value); + /*may be NULL*/ + anim->target = (SVG_Element *) gf_sg_find_node_by_name(parser->load->scene_graph, anim->target_id + 1); + continue; + } else { + GF_FieldInfo info; + XMLRI *iri = NULL; + if (gf_node_get_attribute_by_tag((GF_Node *)elt, TAG_XLINK_ATT_href, 1, 0, &info)==GF_OK) { + gf_svg_parse_attribute((GF_Node *)elt, &info, att->value, 0); + iri = info.far_ptr; + if ((tag==TAG_SVG_image) || (tag==TAG_SVG_video) || (tag==TAG_SVG_audio)) { + svg_process_media_href(parser, iri); + } + svg_post_process_href(parser, iri); + continue; + } + } + } + if ((tag == TAG_SVG_handler) && (ns == GF_XMLNS_XMLEV)) { + if (!stricmp(att_name, "event") ) { + ev_event = att->value; + continue; + } + if (!stricmp(att_name, "observer") ) { + ev_observer = att->value; + continue; + } + } + + /*laser specific stuff*/ + if (ns == GF_XMLNS_LASER) { + if (!stricmp(att_name, "scale") ) { + if (gf_node_get_attribute_by_tag((GF_Node *)elt, TAG_SVG_ATT_transform, 1, 1, &info)==GF_OK) { + SVG_Point pt; + SVG_Transform *mat = info.far_ptr; + svg_parse_point(&pt, att->value); + gf_mx2d_add_scale(&mat->mat, pt.x, pt.y); + continue; + } + } + if (!stricmp(att_name, "translation") ) { + if (gf_node_get_attribute_by_tag((GF_Node *)elt, TAG_SVG_ATT_transform, 1, 1, &info)==GF_OK) { + SVG_Point pt; + SVG_Transform *mat = info.far_ptr; + svg_parse_point(&pt, att->value); + gf_mx2d_add_translation(&mat->mat, pt.x, pt.y); + continue; + } + } + } + + if (!strncmp(att_name, "on", 2)) { + u32 evtType = gf_dom_event_type_by_name(att_name + 2); + if (evtType != GF_EVENT_UNKNOWN) { + SVG_handlerElement *handler = gf_dom_listener_build((GF_Node *) elt, evtType, 0); + gf_dom_add_text_node((GF_Node *)handler, strdup(att->value) ); + continue; + } + svg_report(parser, GF_OK, "Skipping unknown event handler %s on node %s", att->name, name); + } + + if (gf_node_get_attribute_by_name((GF_Node *)elt, att_name, ns, 1, 0, &info)==GF_OK) { + GF_Err e = gf_svg_parse_attribute((GF_Node *)elt, &info, att->value, 0); + if (e) { + svg_report(parser, e, "Error parsing attribute %s on node %s", att->name, name); + continue; + } + if (info.fieldType == SVG_ID_datatype) { + /*"when both 'id' and 'xml:id' are specified on the same element but with different values, + the SVGElement::id field must return either of the values but should give precedence to + the 'xml:id' attribute."*/ + if (!node_name || (info.fieldIndex == TAG_XML_ATT_id)) { + node_name = *(SVG_ID *)info.far_ptr; + if (isdigit(node_name[0])) { + svg_report(parser, GF_BAD_PARAM, "Invalid value %s for node %s %s", node_name, name, att->name); + node_name = NULL; + } + } + } else { + switch (info.fieldIndex) { + case TAG_SVG_ATT_syncMaster: + case TAG_SVG_ATT_focusHighlight: + case TAG_SVG_ATT_initialVisibility: + case TAG_SVG_ATT_fullscreen: + case TAG_SVG_ATT_requiredFonts: + /*switch to v2*/ + svg_lsr_set_v2(parser); + break; + } + } + continue; + } + svg_report(parser, GF_OK, "Skipping attribute %s on node %s", att->name, name); + } + + /*set root BEFORE processing events in order to have it setup for script init*/ + if ((tag == TAG_SVG_svg) && !parser->has_root) + svg_init_root_element(parser, elt); + + if (ev_event) { + /* When the handler element specifies the event attribute, an implicit listener is defined */ + GF_Node *node = (GF_Node *)elt; + SVG_Element *listener; + u32 type; + GF_FieldInfo info; + listener = (SVG_Element *) gf_node_new(node->sgprivate->scenegraph, TAG_SVG_listener); + /*do not register the listener, this will be done in gf_node_dom_listener_add. We don't want to + insert the implicit listener in the DOM*/ + + /* this listener listens to the given type of event */ + type = gf_dom_event_type_by_name(ev_event); + gf_node_get_attribute_by_tag(node, TAG_XMLEV_ATT_event, 1, 0, &info); + ((XMLEV_Event *)info.far_ptr)->type = type; + gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_event, 1, 0, &info); + ((XMLEV_Event *)info.far_ptr)->type = type; + gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_handler, 1, 0, &info); + ((XMLRI *)info.far_ptr)->target = node; + + if (ev_observer) { + gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_observer, 1, 0, &info); + gf_svg_parse_attribute((GF_Node *)elt, &info, (char*)ev_observer, 0); + } else { + /* this listener listens with the parent of the handler as the event target */ + gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_target, 1, 0, &info); + ((XMLRI *)info.far_ptr)->target = parent->node; + } + if ( ((XMLRI *)info.far_ptr)->target) + gf_node_dom_listener_add(((XMLRI *)info.far_ptr)->target, (GF_Node *) listener); + else + gf_list_add(parser->defered_listeners, listener); + } + + /* if the new element has an id, we try to resolve defered references */ + if (node_name) { + if (!has_id) gf_svg_parse_element_id((GF_Node *)elt, node_name, parser->command_depth ? 1 : 0); + svg_resolved_refs(parser, parser->load->scene_graph, node_name); + } + + /* if the new element is an animation, now that all specified attributes have been found, + we can start parsing them */ + if (anim) { + /*FIXME - we need to parse from/to/values but not initialize the stack*/ +// if (parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK) { + needs_init = 0; + if (svg_parse_animation(parser, parser->load->scene_graph, anim, NULL, 0)) { + svg_delete_defered_anim(anim, NULL); + } else { + gf_list_add(parser->defered_animations, anim); + } +// } else { +// svg_delete_defered_anim(anim, NULL); +// } + } + +#ifndef SKIP_INIT + if (needs_init) gf_node_init((GF_Node *)elt); +#endif + + if (parent && elt) { + /*mark parent element as dirty (new child added) and invalidate parent graph for progressive rendering*/ + gf_node_dirty_set((GF_Node *)parent->node, GF_SG_CHILD_DIRTY, 1); + /*request scene redraw*/ + if (parser->load->scene_graph->NodeCallback) + parser->load->scene_graph->NodeCallback(parser->load->scene_graph->userpriv, GF_SG_CALLBACK_MODIFIED, NULL, NULL); + } + + /*register listener element*/ + if ((parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK) && elt && (tag==TAG_SVG_listener)) { + GF_FieldInfo info; + Bool post_pone = 0; + SVG_Element *par = NULL; + SVG_Element *listener = (SVG_Element *)elt; + + if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_observer, 0, 0, &info) == GF_OK) { + XMLRI *observer = info.far_ptr; + if (observer->type == XMLRI_ELEMENTID) { + if (!observer->target) post_pone = 1; + else par = observer->target; + } + } + + if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_target, 0, 0, &info) == GF_OK) { + XMLRI *target = info.far_ptr; + if (!par && (target->type == XMLRI_ELEMENTID)) { + if (!target->target) post_pone = 1; + else par = target->target; + } + } + /*check handler, create it if not specified*/ + if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_handler, 1, 0, &info) == GF_OK) { + XMLRI *handler = info.far_ptr; + if (!handler->target) { + if (!handler->string) handler->target = parent->node; + } + } + /*if event is a key event, register it with root*/ + if (!par && gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_event, 0, 0, &info) == GF_OK) { + XMLEV_Event *ev = info.far_ptr; + if ((ev->type>=GF_EVENT_KEYUP) && (ev->type<=GF_EVENT_TEXTINPUT)) par = (SVG_Element*) listener->sgprivate->scenegraph->RootNode; + } + + if (post_pone) { + gf_list_add(parser->defered_listeners, listener); + } else { + if (!par) par = parent->node; + gf_node_dom_listener_add((GF_Node *)par, (GF_Node *) listener); + } + } + return elt; +} + + +static GF_Err lsr_parse_command(GF_SVG_Parser *parser, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + GF_FieldInfo info; + GF_Node *opNode; + Bool is_replace = 0; + char *atNode = NULL; + char *atAtt = NULL; + char *atOperandNode = NULL; + char *atOperandAtt = NULL; + char *atValue = NULL; + char *atPoint = NULL; + char *atInteger = NULL; + char *atEvent = NULL; + char *atString = NULL; + GF_CommandField *field; + s32 index = -1; + u32 i; + switch (parser->command->tag) { + case GF_SG_LSR_NEW_SCENE: + return GF_OK; + case GF_SG_LSR_DELETE: + for (i=0; iname, "ref")) atNode = att->value; + else if (!strcmp(att->name, "attributeName")) atAtt = att->value; + else if (!strcmp(att->name, "index")) index = atoi(att->value); + } + if (!atNode) return svg_report(parser, GF_BAD_PARAM, "Missing node ref for command"); + /*should be a XML IDREF, not an XML IRI*/ + if (atNode[0]=='#') atNode++; + + parser->command->node = svg_find_node(parser, atNode); + if (!parser->command->node) return svg_report(parser, GF_BAD_PARAM, "Cannot find node node ref %s for command", atNode); + if (atAtt) { + if (!strcmp(atAtt, "children")) { + field = gf_sg_command_field_new(parser->command); + field->pos = index; + field->fieldIndex = 0; + field->fieldType = 0; + } else { + if (gf_node_get_attribute_by_name(parser->command->node, atAtt, parser->current_ns, 0, 0, &info)==GF_OK) { + field = gf_sg_command_field_new(parser->command); + field->pos = index; + field->fieldIndex = info.fieldIndex; + field->fieldType = info.fieldType; + } + } + } else if (index>=0) { + field = gf_sg_command_field_new(parser->command); + field->pos = index; + } + gf_node_register(parser->command->node, NULL); + return GF_OK; + case GF_SG_LSR_REPLACE: + is_replace = 1; + case GF_SG_LSR_ADD: + case GF_SG_LSR_INSERT: + for (i=0; iname, "ref")) atNode = att->value; + else if (!strcmp(att->name, "operandElementId")) atOperandNode = att->value; + else if (!strcmp(att->name, "operandAttributeName")) atOperandAtt = att->value; + else if (!strcmp(att->name, "value")) atValue = att->value; + else if (!strcmp(att->name, "attributeName")) atAtt = att->value; + /*replace only*/ + else if (!strcmp(att->name, "index")) index = atoi(att->value); + } + if (!atNode) return svg_report(parser, GF_BAD_PARAM, "Missing node ref for command"); + parser->command->node = svg_find_node(parser, atNode); + if (!parser->command->node) return svg_report(parser, GF_BAD_PARAM, "Cannot find node node ref %s for command", atNode); + /*child or node replacement*/ + if ( (is_replace || (parser->command->tag==GF_SG_LSR_INSERT)) && (!atAtt || !strcmp(atAtt, "children")) ) { + field = gf_sg_command_field_new(parser->command); + field->pos = index; + if (atAtt) field->fieldIndex = TAG_LSR_ATT_children; + gf_node_register(parser->command->node, NULL); + return GF_OK; + } + if (!atAtt) return svg_report(parser, GF_BAD_PARAM, "Missing attribute name for command"); + if (!strcmp(atAtt, "textContent")) { + field = gf_sg_command_field_new(parser->command); + field->pos = -1; + field->fieldIndex = (u32) -1; + field->fieldType = DOM_String_datatype; + gf_node_register(parser->command->node, NULL); + if (atValue) { + field->field_ptr = gf_svg_create_attribute_value(field->fieldType); + *(SVG_String *)field->field_ptr = strdup(atValue); + } + return GF_OK; + } + if (!strcmp(atAtt, "scale") || !strcmp(atAtt, "translation") || !strcmp(atAtt, "rotation")) { + if (!strcmp(atAtt, "scale")) { + info.fieldType = SVG_Transform_Scale_datatype; + info.fieldIndex = TAG_LSR_ATT_scale; + } + else if (!strcmp(atAtt, "translation")) { + info.fieldType = SVG_Transform_Translate_datatype; + info.fieldIndex = TAG_LSR_ATT_translation; + } + else if (!strcmp(atAtt, "rotation")) { + info.fieldType = SVG_Transform_Rotate_datatype; + info.fieldIndex = TAG_LSR_ATT_rotation; + } + } else { + /*FIXME - handle namespace properly here !!*/ + info.fieldIndex = gf_xml_get_attribute_tag(parser->command->node, atAtt, parser->current_ns); + info.fieldType = gf_xml_get_attribute_type(info.fieldIndex); + + if (gf_lsr_anim_type_from_attribute(info.fieldIndex)<0) { + return svg_report(parser, GF_BAD_PARAM, "Attribute %s of element %s is not updatable\n", atAtt, gf_node_get_class_name(parser->command->node)); + } + } + + opNode = NULL; + if (atOperandNode) { + opNode = gf_sg_find_node_by_name(parser->load->scene_graph, atOperandNode); + if (!opNode) return svg_report(parser, GF_BAD_PARAM, "Cannot find operand element %s for command", atOperandNode); + } + if (!atValue && (!atOperandNode || !atOperandAtt) ) return svg_report(parser, GF_BAD_PARAM, "Missing attribute value for command"); + field = gf_sg_command_field_new(parser->command); + field->pos = index; + field->fieldIndex = info.fieldIndex; + field->fieldType = info.fieldType; + if (atValue) { + GF_FieldInfo nf; + nf.fieldType = info.fieldType; + field->field_ptr = nf.far_ptr = gf_svg_create_attribute_value(info.fieldType); + if (field->field_ptr) gf_svg_parse_attribute(parser->command->node, &nf, atValue, (u8) info.fieldType); + } else if (opNode) { + parser->command->fromNodeID = gf_node_get_id(opNode); + /*FIXME - handle namespace properly here !!*/ + parser->command->fromFieldIndex = gf_xml_get_attribute_tag(opNode, atOperandAtt, parser->current_ns); + } + gf_node_register(parser->command->node, NULL); + return GF_OK; + case GF_SG_LSR_ACTIVATE: + case GF_SG_LSR_DEACTIVATE: + for (i=0; iname, "ref")) atNode = att->value; + } + if (!atNode) return svg_report(parser, GF_BAD_PARAM, "Missing node ref for command"); + /*should be a XML IDREF, not an XML IRI*/ + if (atNode[0]=='#') atNode++; + parser->command->node = svg_find_node(parser, atNode); + if (!parser->command->node) return svg_report(parser, GF_BAD_PARAM, "Cannot find node node ref %s for command", atNode); + gf_node_register(parser->command->node, NULL); + + /*switch to V2*/ + svg_lsr_set_v2(parser); + + return GF_OK; + case GF_SG_LSR_SEND_EVENT: + for (i=0; iname, "ref")) atNode = att->value; + else if (!strcmp(att->name, "event")) atEvent = att->value; + else if (!strcmp(att->name, "pointvalue")) atPoint = att->value; + else if (!strcmp(att->name, "intvalue")) atInteger = att->value; + else if (!strcmp(att->name, "stringvalue")) atString = att->value; + } + + if (!atEvent) return svg_report(parser, GF_BAD_PARAM, "Missing event name for command"); + if (!atNode) return svg_report(parser, GF_BAD_PARAM, "Missing node ref for command"); + /*should be a XML IDREF, not an XML IRI*/ + if (atNode[0]=='#') atNode++; + parser->command->node = svg_find_node(parser, atNode); + if (!parser->command->node) return svg_report(parser, GF_BAD_PARAM, "Cannot find node node ref %s for command", atNode); + gf_node_register(parser->command->node, NULL); + + parser->command->send_event_name = gf_dom_event_type_by_name(atEvent); + + parser->command->send_event_integer = 0xEFFFFFFF; + if (atString) { + u32 key = gf_dom_get_key_type(atString); + if (key == GF_KEY_UNIDENTIFIED) { + parser->command->send_event_string = strdup(atString); + } else { + parser->command->send_event_integer = key; + } + } + if (atInteger) parser->command->send_event_integer = atoi(atInteger); + if (atPoint) { + SVG_Point pt; + svg_parse_point(&pt, atPoint); + parser->command->send_event_x = FIX2INT(pt.x); + parser->command->send_event_y = FIX2INT(pt.y); + } + return GF_OK; + default: + return GF_NOT_SUPPORTED; + } +} + + +static u32 lsr_get_command_by_name(const char *name) +{ + if (!strcmp(name, "NewScene")) return GF_SG_LSR_NEW_SCENE; + else if (!strcmp(name, "RefreshScene")) return GF_SG_LSR_REFRESH_SCENE; + else if (!strcmp(name, "Add")) return GF_SG_LSR_ADD; + else if (!strcmp(name, "Replace")) return GF_SG_LSR_REPLACE; + else if (!strcmp(name, "Delete")) return GF_SG_LSR_DELETE; + else if (!strcmp(name, "Insert")) return GF_SG_LSR_INSERT; + else if (!strcmp(name, "Restore")) return GF_SG_LSR_RESTORE; + else if (!strcmp(name, "Save")) return GF_SG_LSR_SAVE; + else if (!strcmp(name, "Clean")) return GF_SG_LSR_CLEAN; + else if (!strcmp(name, "SendEvent")) return GF_SG_LSR_SEND_EVENT; + else if (!strcmp(name, "Activate")) return GF_SG_LSR_ACTIVATE; + else if (!strcmp(name, "Deactivate")) return GF_SG_LSR_DEACTIVATE; + return GF_SG_UNDEFINED; +} + +static GF_ESD *lsr_parse_header(GF_SVG_Parser *parser, const char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + GF_ESD *esd; + u32 i; + if (!strcmp(name, "LASeRHeader")) { + GF_LASERConfig *lsrc = (GF_LASERConfig *) gf_odf_desc_new(GF_ODF_LASER_CFG_TAG); + for (i=0; iname, "profile")) lsrc->profile = !strcmp(att->value, "full") ? 1 : 0; + else if (!strcmp(att->name, "level")) lsrc->level = atoi(att->value); + else if (!strcmp(att->name, "resolution")) lsrc->resolution = atoi(att->value); + else if (!strcmp(att->name, "timeResolution")) lsrc->time_resolution = atoi(att->value); + else if (!strcmp(att->name, "coordBits")) lsrc->coord_bits = atoi(att->value); + else if (!strcmp(att->name, "scaleBits_minus_coordBits")) lsrc->scale_bits_minus_coord_bits = atoi(att->value); + else if (!strcmp(att->name, "colorComponentBits")) lsrc->colorComponentBits = atoi(att->value); + else if (!strcmp(att->name, "newSceneIndicator")) lsrc->newSceneIndicator = (!strcmp(att->value, "yes") || !strcmp(att->value, "true")) ? 1 : 0; + else if (!strcmp(att->name, "useFullRequestHost")) lsrc->fullRequestHost = (!strcmp(att->value, "yes") || !strcmp(att->value, "true")) ? 1 : 0; + else if (!strcmp(att->name, "pathComponents")) lsrc->fullRequestHost = atoi(att->value); + else if (!strcmp(att->name, "extensionIDBits")) lsrc->extensionIDBits = atoi(att->value); + /*others are ignored in GPAC atm*/ + } + esd = gf_odf_desc_esd_new(2); + gf_odf_desc_del((GF_Descriptor *)esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) lsrc; + esd->decoderConfig->streamType = GF_STREAM_SCENE; + esd->decoderConfig->objectTypeIndication = 0x09; + esd->slConfig->timestampResolution = lsrc->time_resolution ? lsrc->time_resolution : 1000; + return esd; + } + return NULL; +} + +static void svg_node_start(void *sax_cbck, const char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + GF_SVG_Parser *parser = (GF_SVG_Parser *)sax_cbck; + SVG_NodeStack *stack, *parent; + SVG_Element *elt; + SVG_Element *cond = NULL; + u32 xmlns; + Bool has_ns; + +#ifdef SKIP_ALL + return; +#endif + + parent = (SVG_NodeStack *)gf_list_last(parser->node_stack); + + /*switch to conditional*/ + if (parent && parent->node->sgprivate->tag==TAG_LSR_conditional) { + cond = parent->node; + parent = NULL; + } + + /*saf setup*/ + if ((!parent && (parser->load->type!=GF_SM_LOAD_SVG_DA)) || cond) { + u32 com_type; + /*nothing to do, the context is already created*/ + if (!strcmp(name, "SAFSession")) return; + /*nothing to do, wait for the laser (or other) header before creating stream)*/ + if (!strcmp(name, "sceneHeader")) return; + /*nothing to do, wait for the laser (or other) header before creating stream)*/ + if (!strcmp(name, "LASeRHeader")) { + GF_ESD *esd = lsr_parse_header(parser, name, name_space, attributes, nb_attributes); + if (!esd) svg_report(parser, GF_BAD_PARAM, "Invalid LASER Header"); + /*TODO find a better way of assigning an ID to the laser stream...*/ + esd->ESID = 1; + parser->laser_es = gf_sm_stream_new(parser->load->ctx, esd->ESID, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); + if (!parser->load->ctx->root_od) parser->load->ctx->root_od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_IOD_TAG); + gf_list_add(parser->load->ctx->root_od->ESDescriptors, esd); + parser->laser_es->timeScale = esd->slConfig->timestampResolution; + return; + } + if (!strcmp(name, "sceneUnit") ) { + u32 time, rap, i; + time = rap = 0; + if (!gf_list_count(parser->laser_es->AUs)) rap = 1; + for (i=0; iname, "time")) time = atoi(att->value); + else if (!strcmp(att->name, "rap")) rap = !strcmp(att->value, "yes") ? 1 : 0; + } + /*create new laser unit*/ + parser->laser_au = gf_sm_stream_au_new(parser->laser_es, time, 0, rap); + return; + } + + if (!strcmp(name, "StreamHeader") || !strcmp(name, "RemoteStreamHeader") + /*SAF & SAFML are just a pain ...*/ + || !strcmp(name, "mediaHeader") || !strcmp(name, "imageHeader") + ) { + char *url = NULL; + char *src = NULL; + const char *ID = NULL; + u32 time, OTI, ST, i, ts_res; + GF_ODUpdate *odU; + GF_ObjectDescriptor *od; + Bool rap; + SVG_SAFExternalStream*st; + /*create a SAF stream*/ + if (!parser->saf_es) { + GF_ESD *esd = (GF_ESD*)gf_odf_desc_esd_new(2); + esd->ESID = 0xFFFE; + esd->decoderConfig->streamType = GF_STREAM_OD; + esd->decoderConfig->objectTypeIndication = 1; + parser->saf_es = gf_sm_stream_new(parser->load->ctx, esd->ESID, GF_STREAM_OD, 1); + if (!parser->load->ctx->root_od) parser->load->ctx->root_od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_IOD_TAG); + parser->saf_es->timeScale = 1000; + gf_list_add(parser->load->ctx->root_od->ESDescriptors, esd); + } + time = 0; + rap = 0; + ts_res = 1000; + OTI = ST = 0; + for (i=0; iname, "time")) time = atoi(att->value); + else if (!strcmp(att->name, "rap")) rap = !strcmp(att->value, "yes") ? 1 : 0; + else if (!strcmp(att->name, "url")) url = strdup(att->value); + else if (!strcmp(att->name, "streamID")) ID = att->value; + else if (!strcmp(att->name, "objectTypeIndication")) OTI = atoi(att->value); + else if (!strcmp(att->name, "streamType")) ST = atoi(att->value); + else if (!strcmp(att->name, "timeStampResolution")) ts_res = atoi(att->value); + else if (!strcmp(att->name, "source")) src = att->value; + + } + if (!strcmp(name, "imageHeader")) ST = 4; + + /*create new SAF stream*/ + st = svg_saf_get_next_available_stream(parser); + st->stream_name = strdup(ID); + + /*create new SAF unit*/ + parser->saf_au = gf_sm_stream_au_new(parser->saf_es, time, 0, 0); + odU = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + gf_list_add(parser->saf_au->commands, odU); + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + gf_list_add(odU->objectDescriptors, od); + od->objectDescriptorID = st->id; + + if (url) { + od->URLString = url; + } else { + char szName[1024]; + GF_MuxInfo *mux; + FILE *nhml; + GF_ESD *esd = (GF_ESD*)gf_odf_desc_esd_new(2); + gf_list_add(od->ESDescriptors, esd); + esd->decoderConfig->objectTypeIndication = OTI; + esd->decoderConfig->streamType = ST; + esd->ESID = st->id; + + mux = (GF_MuxInfo *)gf_odf_desc_new(GF_ODF_MUXINFO_TAG); + gf_list_add(esd->extensionDescriptors, mux); + + /*global source for stream, don't use nhml dumping*/ + if (src) { + mux->file_name = strdup(src); + st->nhml_info = NULL; + } else { + if (parser->load->localPath) { + strcpy(szName, parser->load->localPath); + strcat(szName, ID); + } else { + strcpy(szName, ID); + } + strcat(szName, "_temp.nhml"); + mux->file_name = strdup(szName); + st->nhml_info = mux->file_name; + nhml = fopen(st->nhml_info, "wt"); + fprintf(nhml, "\n"); + fprintf(nhml, "\n", ts_res, ST, OTI, st->id); + fclose(nhml); + mux->delete_file = 1; + } + } + return; + } + if (!strcmp(name, "mediaUnit") || !strcmp(name, "imageUnit") ) { + FILE *nhml; + char *id = NULL; + char *src = NULL; + SVG_SAFExternalStream*st; + u32 i, rap, time, offset, length; + rap = time = offset = length = 0; + for (i=0; iname, "time")) time = atoi(att->value); + else if (!strcmp(att->name, "source")) src = att->value; + else if (!strcmp(att->name, "ref")) id = att->value; + else if (!strcmp(att->name, "rap")) rap = !strcmp(att->value, "yes") ? 1 : 0; + else if (!strcmp(att->name, "startOffset")) offset = atoi(att->value); + else if (!strcmp(att->name, "length")) length = atoi(att->value); + } + st = svg_saf_get_stream(parser, 0, id); + if (!st || !st->nhml_info) { + return; + } + nhml = fopen(st->nhml_info, "a+t"); + fprintf(nhml, "\n"); + fclose(nhml); + return; + } + if (!strcmp(name, "endOfStream") ) { + FILE *nhml; + char *id = NULL; + u32 i; + SVG_SAFExternalStream*st; + for (i=0; iname, "ref")) id = att->value; + } + st = svg_saf_get_stream(parser, 0, id); + if (!st || !st->nhml_info) { + return; + } + nhml = fopen(st->nhml_info, "a+t"); + fprintf(nhml, "\n"); + fclose(nhml); + return; + } + + if (!strcmp(name, "endOfSAFSession") ) { + return; + } + if ((parser->load->type==GF_SM_LOAD_XSR) && !parser->laser_au && !cond) { + svg_report(parser, GF_BAD_PARAM, "LASeR Scene unit not defined for command %s", name); + return; + } + /*command parsing*/ + com_type = lsr_get_command_by_name(name); + if (com_type != GF_SG_UNDEFINED) { + SVG_NodeStack *top; + GF_Err e; + parser->command = gf_sg_command_new(parser->load->scene_graph, com_type); + if (cond) { + GF_DOMUpdates *up = cond->children ? (GF_DOMUpdates *)cond->children->node : NULL; + if (!up) { + up = gf_dom_add_updates_node((GF_Node*)cond); + + if (parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK) { + gf_node_set_callback_function((GF_Node*)up, xsr_exec_command_list); + } + } + gf_list_add(up->updates, parser->command); + } else if (parser->laser_au) { + gf_list_add(parser->laser_au->commands, parser->command); + } + /*this is likely a conditional start - update unknown depth level*/ + top = (SVG_NodeStack*)gf_list_last(parser->node_stack); + if (top) { + top->unknown_depth ++; + parser->command_depth++; + } + + e = lsr_parse_command(parser, attributes, nb_attributes); + if (e!= GF_OK) parser->command->node = NULL; + + return; + } + } + + if (!parent && !parser->command && (parser->load->flags & GF_SM_LOAD_CONTEXT_STREAMING)) { + gf_sg_reset(parser->load->scene_graph); + parser->has_root = 0; + } + + /*something not supported happened (bad command name, bad root, ...) */ + if ((parser->has_root==1) && !parent && !parser->command) + return; + + xmlns = parser->current_ns; + has_ns = 0; + + elt = svg_parse_element(parser, name, name_space, attributes, nb_attributes, parent, &has_ns); + if (!elt) { + if (parent) parent->unknown_depth++; + else if (cond) parser->command_depth++; + return; + } + GF_SAFEALLOC(stack, SVG_NodeStack); + stack->node = elt; + stack->current_ns = xmlns; + stack->has_ns = has_ns; + gf_list_add(parser->node_stack, stack); + + if ( (gf_node_get_tag((GF_Node *)elt) == TAG_SVG_svg) && + (!parser->has_root || (parser->command && !parser->command->node) ) + ) { + + if (!parser->has_root) svg_init_root_element(parser, elt); + if (parser->command) parser->command->node = (GF_Node*)elt; + } else if (!parent && parser->has_root && parser->command) { + GF_CommandField *field = (GF_CommandField *)gf_list_get(parser->command->command_fields, 0); + if (field) { + /*either not assigned or textContent*/ + if (field->new_node && (field->new_node->sgprivate->tag==TAG_DOMText)) { + svg_report(parser, GF_OK, "Warning: LASeR cannot replace children with a mix of text nodes and elements - ignoring text\n"); + gf_node_unregister(field->new_node, NULL); + field->new_node = NULL; + } + if (field->new_node) { + field->field_ptr = &field->node_list; + gf_node_list_add_child(& field->node_list, field->new_node); + field->new_node = NULL; + gf_node_list_add_child( & field->node_list, (GF_Node*) elt); + } else if (field->node_list) { + gf_node_list_add_child(& field->node_list, (GF_Node*) elt); + } else if (!field->new_node) { + field->new_node = (GF_Node*)elt; + field->field_ptr = &field->new_node; + } + } else { + assert(parser->command->tag==GF_SG_LSR_NEW_SCENE); + assert(gf_node_get_tag((GF_Node *)elt) == TAG_SVG_svg); + if(!parser->command->node) + parser->command->node = (GF_Node *)elt; + } + } else if (!parser->has_root ) { + gf_list_del_item(parser->node_stack, stack); + free(stack); + gf_node_unregister((GF_Node *)elt, NULL); + } else if ((parser->has_root==2) && !parser->fragment_root) { + parser->fragment_root = (GF_Node *)elt; + } +} + +static void svg_node_end(void *sax_cbck, const char *name, const char *name_space) +{ + u32 ns; + GF_SVG_Parser *parser = (GF_SVG_Parser *)sax_cbck; + SVG_NodeStack *top = (SVG_NodeStack *)gf_list_last(parser->node_stack); + +#ifdef SKIP_ALL + return; +#endif + + if (!top) { + if (parser->laser_au && !strcmp(name, "sceneUnit")) { + parser->laser_au = NULL; + return; + } + if (parser->command) { + u32 com_type = lsr_get_command_by_name(name); + if (com_type == parser->command->tag) { + if (parser->load->type==GF_SM_LOAD_DIMS) { + gf_sg_command_apply(parser->load->scene_graph, parser->command, 0); + gf_sg_command_del(parser->command); + } + parser->command = NULL; + return; + } + } + /*error*/ + return; + } + + ns = parser->current_ns; + if (name_space) + ns = gf_sg_get_namespace_code(parser->load->scene_graph, (char *) name_space); + + /*only remove created nodes ... */ +#ifdef SKIP_UNKNOWN_NODES + if (gf_xml_get_element_tag(name, ns) != TAG_UndefinedNode) +#endif + { + const char *the_name; + Bool mismatch = 0; + SVG_Element *node = top->node; + /*check node name...*/ + the_name = gf_node_get_class_name((GF_Node *)node); + if (name_space && strstr(the_name, name_space) && strstr(the_name, name) ) {} + else if (!strcmp(the_name, name) ) {} + else mismatch = 1; + + if (mismatch) { + if (top->unknown_depth) { + top->unknown_depth--; + return; + } else { + svg_report(parser, GF_BAD_PARAM, "SVG depth mismatch"); + return; + } + } + parser->current_ns = top->current_ns; + if (top->has_ns) gf_xml_pop_namespaces(top->node); + free(top); + gf_list_rem_last(parser->node_stack); + + if (parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK) { + switch (node->sgprivate->tag) { + case TAG_SVG_animateMotion: + /* + try to init animateMotion once all children have been parsed + to make sure we get the mpath child if any, however, we are still not sure + if the target is known. We can force initialisation + because mpath children (if any have been parsed) + */ + { + u32 i, count; + SVG_DeferedAnimation *anim = NULL; + count = gf_list_count(parser->defered_animations); + for (i = 0; i < count; i++) { + anim = gf_list_get(parser->defered_animations, i); + if (anim->animation_elt == node) break; + else anim = NULL; + } + if (anim) { + if (svg_parse_animation(parser, gf_node_get_graph((GF_Node *)node), anim, NULL, 1)) { + svg_delete_defered_anim(anim, parser->defered_animations); + } + } + } + break; + case TAG_SVG_script: + case TAG_SVG_handler: + /*init script once text script is loaded*/ + gf_node_init((GF_Node *)node); + break; + } + /*if we have associated event listeners, trigger the onLoad, only in playback mode */ + if ((parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK) && + (node->sgprivate->interact && node->sgprivate->interact->dom_evt)) { + GF_DOM_Event evt; + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = GF_EVENT_LOAD; + gf_dom_event_fire((GF_Node*)node, &evt); + } + + } + } +#ifdef SKIP_UNKNOWN_NODES + else if (top) { + if (top->unknown_depth) { + top->unknown_depth--; + if (!top->unknown_depth) { + /*this is not 100% correct, we should track which unsupported node changed the namespace*/ + parser->current_ns = top->current_ns; + if (parser->command_depth) parser->command_depth --; + } + } else if (parser->command_depth) { + parser->command_depth--; + } else { + svg_report(parser, GF_BAD_PARAM, "SVG depth mismatch"); + } + } +#endif +} + +static void svg_text_content(void *sax_cbck, const char *text_content, Bool is_cdata) +{ + GF_SVG_Parser *parser = (GF_SVG_Parser *)sax_cbck; + SVG_NodeStack *top = (SVG_NodeStack *)gf_list_last(parser->node_stack); + SVG_Element *elt = (top ? top->node : NULL); + GF_DOMText *text; + u32 skip_text; + u32 tag; + +#ifdef SKIP_ALL + return; +#endif + + if (top && top->unknown_depth && !parser->command_depth) return; + if (!elt && !parser->command) return; + + tag = (elt ? gf_node_get_tag((GF_Node *)elt) : 0); + + /*if top is a conditional, create a new text node*/ + if (!elt || tag == TAG_LSR_conditional) { + GF_CommandField *field; + if (!parser->command) return; + + field = (GF_CommandField *)gf_list_get(parser->command->command_fields, 0); + if (parser->command->tag == GF_SG_LSR_NEW_SCENE || parser->command->tag == GF_SG_LSR_ADD) return; + + if (!field || field->field_ptr) return; + + if (field->new_node) { + svg_report(parser, GF_OK, "Warning: LASeR cannot replace children with a mix of text nodes and elements - ignoring text\n"); + return; + } + + /*create a text node but don't register it with any node*/ + text = gf_dom_new_text_node(parser->load->scene_graph); + gf_node_register((GF_Node *)text, NULL); + text->textContent = strdup(text_content); + + if (field->new_node) { + field->field_ptr = &field->node_list; + gf_node_list_add_child(& field->node_list, field->new_node); + field->new_node = NULL; + gf_node_list_add_child( & field->node_list, (GF_Node*) text); + } else if (field->node_list) { + gf_node_list_add_child(& field->node_list, (GF_Node*) text); + } else if (!field->new_node) { + field->new_node = (GF_Node*)text; + field->field_ptr = &field->new_node; + } + return; + } + skip_text = 1; + switch (tag) { + case TAG_DOMFullNode: + case TAG_SVG_a: + case TAG_SVG_title: + case TAG_SVG_desc: + case TAG_SVG_metadata: + case TAG_SVG_text: + case TAG_SVG_tspan: + case TAG_SVG_textArea: + skip_text = 0; + break; + /*for script and handlers only add text if not empty*/ + case TAG_SVG_handler: + case TAG_SVG_script: + { + u32 i, len = strlen(text_content); + for (i=0; itype = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR; + gf_node_changed((GF_Node *)text, NULL); + } +} + +static GF_SVG_Parser *svg_new_parser(GF_SceneLoader *load) +{ + GF_SVG_Parser *parser; + switch (load->type) { + case GF_SM_LOAD_XSR: + if (!load->ctx) return NULL; + break; + case GF_SM_LOAD_SVG_DA: + case GF_SM_LOAD_DIMS: + break; + default: + return NULL; + } + + GF_SAFEALLOC(parser, GF_SVG_Parser); + parser->node_stack = gf_list_new(); + parser->defered_hrefs = gf_list_new(); + parser->defered_animations = gf_list_new(); + parser->defered_listeners = gf_list_new(); + parser->peeked_nodes = gf_list_new(); + + parser->sax_parser = gf_xml_sax_new(svg_node_start, svg_node_end, svg_text_content, parser); + parser->load = load; + load->loader_priv = parser; + if (load->ctx) load->ctx->is_pixel_metrics = 1; + + /*to cope with old files not signaling XMLNS, add the SVG NS by default*/ + gf_sg_add_namespace(parser->load->scene_graph, "http://www.w3.org/2000/svg", NULL); + parser->current_ns = GF_XMLNS_SVG; + return parser; +} + +static void svg_flush_animations(GF_SVG_Parser *parser) +{ + while (gf_list_count(parser->defered_animations)) { + SVG_DeferedAnimation *anim = (SVG_DeferedAnimation *)gf_list_get(parser->defered_animations, 0); + svg_parse_animation(parser, parser->load->scene_graph, anim, NULL, 2); + svg_delete_defered_anim(anim, parser->defered_animations); + } +} + +static void gf_sm_svg_flush_state(GF_SVG_Parser *parser) +{ + while (gf_list_count(parser->node_stack)) { + SVG_NodeStack *st = (SVG_NodeStack *)gf_list_last(parser->node_stack); + gf_list_rem_last(parser->node_stack); + free(st); + } + /*FIXME - if there still are som defered listeners, we should pass them to the scene graph + and wait for the parent to be defined*/ + while (gf_list_count(parser->defered_listeners)) { + GF_Node *l = gf_list_last(parser->defered_listeners); + gf_list_rem_last(parser->defered_listeners); + /*listeners not resolved are not inserted in the tree - destroy them*/ + gf_node_register(l, NULL); + gf_node_unregister(l, NULL); + } +} + +GF_Err gf_sm_load_init_svg(GF_SceneLoader *load) +{ + GF_Err e; + GF_SVG_Parser *parser; + u32 in_time; + + if (!load->fileName) return GF_BAD_PARAM; + parser = svg_new_parser(load); + if (!parser) return GF_BAD_PARAM; + + + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("[Parser] %s Scene Parsing: %s\n", ((load->type==GF_SM_LOAD_SVG_DA) ? "SVG" : ((load->type==GF_SM_LOAD_XSR) ? "LASeR" : "DIMS")), load->fileName)); + + in_time = gf_sys_clock(); + e = gf_xml_sax_parse_file(parser->sax_parser, (const char *)load->fileName, svg_progress); + if (e<0) return svg_report(parser, e, "Unable to parse file %s: %s", load->fileName, gf_xml_sax_get_error(parser->sax_parser) ); + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("[Parser] Scene parsed and Scene Graph built in %d ms\n", gf_sys_clock() - in_time)); + + svg_flush_animations(parser); + gf_sm_svg_flush_state(parser); + + return parser->last_error; +} + + +GF_Err gf_sm_load_run_svg(GF_SceneLoader *load) +{ + return GF_EOS; +} + +static GF_Err gf_sm_load_init_svg_string_ex(GF_SceneLoader *load, char *str_data, Bool is_fragment) +{ + GF_Err e; + GF_SVG_Parser *parser = (GF_SVG_Parser *)load->loader_priv; + + if (!parser) { + char BOM[6]; + BOM[0] = str_data[0]; + BOM[1] = str_data[1]; + BOM[2] = str_data[2]; + BOM[3] = str_data[3]; + BOM[4] = BOM[5] = 0; + parser = svg_new_parser(load); + if (is_fragment) parser->has_root = 2; + e = gf_xml_sax_init(parser->sax_parser, (unsigned char*)BOM); + if (e) { + svg_report(parser, e, "Error initializing SAX parser: %s", gf_xml_sax_get_error(parser->sax_parser) ); + return e; + } + str_data += 4; + } + return gf_xml_sax_parse(parser->sax_parser, str_data); +} + +GF_Err gf_sm_load_init_svg_string(GF_SceneLoader *load, char *str_data) +{ + return gf_sm_load_init_svg_string_ex(load, str_data, 0); +} + +GF_Err gf_sm_load_done_svg(GF_SceneLoader *load) +{ + SVG_SAFExternalStream *st; + GF_SVG_Parser *parser = (GF_SVG_Parser *)load->loader_priv; + if (!parser) return GF_OK; + + gf_sm_svg_flush_state(parser); + + gf_list_del(parser->node_stack); + gf_list_del(parser->defered_hrefs); + gf_list_del(parser->defered_listeners); + gf_list_del(parser->peeked_nodes); + /* reset animations */ + gf_list_del(parser->defered_animations); + if (parser->sax_parser) { + gf_xml_sax_del(parser->sax_parser); + } + st = parser->streams; + while (st) { + SVG_SAFExternalStream *next = st->next; + free(st->stream_name); + free(st); + st = next; + } + free(parser); + load->loader_priv = NULL; + return GF_OK; +} + +GF_Node *gf_sm_load_svg_from_string(GF_SceneGraph *in_scene, char *node_str) +{ + GF_SVG_Parser *parser; + GF_Node *node; + GF_SceneLoader ctx; + memset(&ctx, 0, sizeof(GF_SceneLoader)); + ctx.scene_graph = in_scene; + ctx.type = GF_SM_LOAD_SVG_DA; + gf_sm_load_init_svg_string_ex(&ctx, node_str, 1); + + parser = (GF_SVG_Parser *)ctx.loader_priv; + node = parser->fragment_root; + /*don't register*/ + if (node) node->sgprivate->num_instances--; + gf_sm_load_done_svg(&ctx); + return node; +} + +#endif + diff --git a/src/scene_manager/loader_xmt.c b/src/scene_manager/loader_xmt.c new file mode 100644 index 0000000..e1170be --- /dev/null +++ b/src/scene_manager/loader_xmt.c @@ -0,0 +1,2861 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/*for QP types*/ +#include "../bifs/quant.h" + +typedef struct +{ + GF_Node *node; + GF_FieldInfo container_field; + GF_ChildNodeItem *last; +} XMTNodeStack; + + +/**/ +enum +{ + /*document is not yet initialized*/ + XMT_STATE_INIT = 0, + /*document head is being parsed*/ + XMT_STATE_HEAD = 1, + /*document body being parsed*/ + XMT_STATE_BODY = 2, + /*commands are being parsed*/ + XMT_STATE_COMMANDS = 3, + /*elements are being parsed*/ + XMT_STATE_ELEMENTS = 4, + /*end of body parsing*/ + XMT_STATE_BODY_END = 5, + /*end of parsing*/ + XMT_STATE_END = 6, +}; + + +typedef struct +{ + /*1: XMT-A, 2: X3D, 3: XMT-O (not supported yet) */ + u32 doc_type; + /*0: not init, 1: header, 2: body*/ + u32 state; + u32 current_node_tag; + + GF_SceneLoader *load; + GF_Err last_error; + GF_SAXParser *sax_parser; + XMTNodeStack *x3d_root; + + /* stack of nodes for SAX parsing*/ + GF_List *nodes; + /* stack of descriptors for SAX parsing*/ + GF_List *descriptors; + + GF_List *peeked_nodes; + GF_List *def_nodes; + GF_List *inserted_routes, *unresolved_routes; + + /* OD and ESD links*/ + GF_List *od_links, *esd_links; + /*set when parsing proto*/ + GF_Proto *parsing_proto; + GF_ProtoFieldInterface *proto_field; + + GF_StreamContext *scene_es; + GF_AUContext *scene_au; + u32 base_scene_id; + /*current scene command*/ + GF_Command *command; + SFCommandBuffer *command_buffer; + + GF_StreamContext *od_es; + GF_AUContext *od_au; + u32 base_od_id; + /*current od command*/ + GF_ODCom *od_command; + + + /*current stream ID, AU time and RAP flag*/ + u32 stream_id; + Double au_time; + Bool au_is_rap; + + GF_List *script_to_load; +} GF_XMTParser; + + +typedef struct +{ + char *desc_name; + u32 ID; + /*store nodes refering to this URL*/ + GF_List *mf_urls; + GF_ObjectDescriptor *od; +} XMT_ODLink; + +typedef struct +{ + char *desc_name; + u32 ESID; + GF_ESD *esd; + char *OCR_Name; + char *Depends_Name; +} XMT_ESDLink; + + +static GF_Err xmt_report(GF_XMTParser *parser, GF_Err e, char *format, ...) +{ +#ifndef GPAC_DISABLE_LOG + if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + char szMsg[2048]; + va_list args; + va_start(args, format); + vsprintf(szMsg, format, args); + va_end(args); + GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_PARSER, ("[XMT Parsing] %s (line %d)\n", szMsg, gf_xml_sax_get_line(parser->sax_parser)) ); + } +#endif + if (e) parser->last_error = e; + return e; +} +static void xmt_progress(void *cbk, u32 done, u32 total) +{ + gf_set_progress("XMT Parsing", done, total); +} +static Bool xmt_esid_available(GF_XMTParser *parser, u16 ESID) +{ + u32 i; + XMT_ESDLink *esdl; + i=0; + while ((esdl = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &i))) { + if (esdl->esd->ESID == ESID) return 0; + } + return 1; +} + +static void xmt_new_od_link(GF_XMTParser *parser, GF_ObjectDescriptor *od, char *name, u32 ID) +{ + u32 i, j, count; + XMT_ODLink *odl; + + if (!ID) { + if (!strnicmp(name, "od", 2)) ID = atoi(name + 2); + else if (!strnicmp(name, "iod", 3)) ID = atoi(name+ 3); + /*be carefull, an url like "11-regression-test.mp4" will return 1 on sscanf :)*/ + else if (sscanf(name, "%d", &ID) == 1) { + char szURL[20]; + sprintf(szURL, "%d", ID); + if (strcmp(szURL, name)) { + ID = 0; + } else { + name = NULL; + } + } + } + + count = gf_list_count(parser->od_links); + for (i=0; iod_links, i); + if ( (ID && (odl->ID == ID)) + || (odl->od == od) + || (odl->desc_name && name && !strcmp(odl->desc_name, name)) + ) { + if (!odl->od) odl->od = od; + if (!odl->desc_name && name) odl->desc_name = strdup(name); + if (!od->objectDescriptorID) { + od->objectDescriptorID = ID; + } else if (ID && (od->objectDescriptorID != ID)) { + xmt_report(parser, GF_BAD_PARAM, "Conflicting OD IDs %d vs %d\n", ID, od->objectDescriptorID); + } + + for (j=i+1; jod_links, j); + if (l2->od == od) { + odl->ID = od->objectDescriptorID = odl->od->objectDescriptorID; + gf_list_rem(parser->od_links, j); + if (l2->desc_name) free(l2->desc_name); + gf_list_del(l2->mf_urls); + free(l2); + break; + } + } + return; + } + } + GF_SAFEALLOC(odl, XMT_ODLink); + odl->mf_urls = gf_list_new(); + odl->od = od; + if (ID) od->objectDescriptorID = ID; + if (name) odl->desc_name = strdup(name); + gf_list_add(parser->od_links, odl); +} + +static void xmt_new_od_link_from_node(GF_XMTParser *parser, char *name, MFURL *url) +{ + u32 i, ID; + XMT_ODLink *odl; + + ID = 0; + if (!strnicmp(name, "od", 2)) ID = atoi(name + 2); + else if (!strnicmp(name, "iod", 3)) ID = atoi(name + 3); + /*be carefull, an url like "11-regression-test.mp4" will return 1 on sscanf :)*/ + else if (sscanf(name, "%d", &ID) == 1) { + char szURL[20]; + sprintf(szURL, "%d", ID); + if (strcmp(szURL, name)) { + ID = 0; + } else { + name = NULL; + } + } + else ID = 0; + + i=0; + while ((odl = (XMT_ODLink*)gf_list_enum(parser->od_links, &i))) { + if ( (name && odl->desc_name && !strcmp(odl->desc_name, name)) + || (ID && odl->od && odl->od->objectDescriptorID==ID) + || (ID && (odl->ID==ID)) + ) { + if (url && (gf_list_find(odl->mf_urls, url)<0) ) gf_list_add(odl->mf_urls, url); + return; + } + } + GF_SAFEALLOC(odl, XMT_ODLink); + odl->mf_urls = gf_list_new(); + if (url) gf_list_add(odl->mf_urls, url); + if (ID) odl->ID = ID; + else odl->desc_name = strdup(name); + gf_list_add(parser->od_links, odl); +} +static void xmt_new_esd_link(GF_XMTParser *parser, GF_ESD *esd, char *desc_name, u32 binID) +{ + u32 i, j; + XMT_ESDLink *esdl; + + i=0; + while ((esdl = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &i))) { + if (esdl->esd && (esd!=esdl->esd)) continue; + if (!esdl->esd) { + if (!esdl->ESID || !desc_name || strcmp(esdl->desc_name, desc_name)) continue; + esdl->esd = esd; + } + if (binID) { + /*remove temp links*/ + if (esdl->ESID == (u16) ( ( ((u32) esdl) >> 16) | ( ((u32)esdl) & 0x0000FFFF) ) ) { + GF_StreamContext *sc; + j=0; + while ((sc = (GF_StreamContext *)gf_list_enum(parser->load->ctx->streams, &j))) { + if (sc->ESID!=esdl->ESID) continue; + /*reassign*/ + sc->ESID = binID; + break; + } + } + esdl->ESID = esdl->esd->ESID = binID; + } + if (desc_name && !esdl->desc_name) { + esdl->desc_name = strdup(desc_name); + if (!esdl->ESID && !strnicmp(desc_name, "es", 2)) esdl->ESID = atoi(&desc_name[2]); + } + return; + } + GF_SAFEALLOC(esdl, XMT_ESDLink); + esdl->esd = esd; + esd->ESID = esdl->ESID = binID; + if (desc_name) { + if (!esdl->ESID && !strnicmp(desc_name, "es", 2)) esdl->ESID = atoi(&desc_name[2]); + esdl->desc_name = strdup(desc_name); + } + if (!esd->ESID) { + esd->ESID = 1; + while (!xmt_esid_available(parser, esd->ESID)) esd->ESID++; + esdl->ESID = esd->ESID; + } + + gf_list_add(parser->esd_links, esdl); +} +static Bool xmt_set_depend_id(GF_XMTParser *parser, GF_ESD *desc, char *es_name, Bool is_ocr_dep) +{ + u32 i; + XMT_ESDLink *esdl; + if (!desc || !es_name) return 0; + + i=0; + while ((esdl = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &i))) { + if (esdl->esd == desc) { + if (is_ocr_dep) + esdl->OCR_Name = strdup(es_name); + else + esdl->Depends_Name = strdup(es_name); + return 1; + } + } + return 0; +} + +static u32 xmt_get_od_id(GF_XMTParser *parser, char *od_name) +{ + u32 i, ID; + XMT_ODLink *l; + if (sscanf(od_name, "%d", &ID)==1) return ID; + + i=0; + while ((l = (XMT_ODLink*)gf_list_enum(parser->od_links, &i))) { + if (!l->od) continue; + if (l->desc_name && !strcmp(l->desc_name, od_name)) return l->od->objectDescriptorID; + } + return 0; +} + +static u32 xmt_get_esd_id(GF_XMTParser *parser, char *esd_name) +{ + u32 i, ID; + XMT_ESDLink *l; + if (sscanf(esd_name, "%d", &ID)==1) return ID; + + i=0; + while ((l = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &i))) { + if (!l->esd) continue; + if (l->desc_name && !strcmp(l->desc_name, esd_name)) return l->esd->ESID; + } + return 0; +} +static u32 xmt_locate_stream(GF_XMTParser *parser, char *stream_name) +{ + XMT_ESDLink *esdl; + u32 i; + char szN[200]; + + i=0; + while ((esdl = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &i))) { + if (esdl->desc_name && !strcmp(esdl->desc_name, stream_name)) return esdl->ESID; + if (esdl->ESID) { + sprintf(szN, "es%d", esdl->ESID); + if (!strcmp(szN, stream_name)) return esdl->ESID; + sprintf(szN, "%d", esdl->ESID); + if (!strcmp(szN, stream_name)) return esdl->ESID; + } + } + /*create a temp one*/ + esdl = (XMT_ESDLink *)malloc(sizeof(XMT_ESDLink)); + memset(esdl, 0, sizeof(XMT_ESDLink)); + esdl->ESID = (u16) ( ((u32) esdl) >> 16) | ( ((u32)esdl) & 0x0000FFFF); + if (!strnicmp(stream_name, "es", 2)) esdl->ESID = atoi(&stream_name[2]); + esdl->desc_name = strdup(stream_name); + gf_list_add(parser->esd_links, esdl); + return esdl->ESID; +} +static Bool xmt_odid_available(GF_XMTParser *parser, u16 ODID) +{ + u32 i; + XMT_ODLink *l; + i=0; + while ((l = (XMT_ODLink*)gf_list_enum(parser->od_links, &i))) { + if (l->ID == ODID) return 0; + if (l->od && l->od->objectDescriptorID == ODID) return 0; + } + return 1; +} + +static void xmt_resolve_od_links(GF_XMTParser *parser) +{ + u32 i, j; + XMT_ESDLink *esdl, *esdl2; + XMT_ODLink *l; + char szURL[5000]; + + /*fix ESD IDs*/ + i=0; + while ((esdl = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &i))) { + if (!esdl->esd) { + xmt_report(parser, GF_BAD_PARAM, "Stream %s ID %d has no associated ES descriptor\n", esdl->desc_name ? esdl->desc_name : "", esdl->ESID); + i--; + gf_list_rem(parser->esd_links, i); + if (esdl->desc_name) free(esdl->desc_name); + free(esdl); + continue; + } + if (esdl->ESID) esdl->esd->ESID = esdl->ESID; + else if (!esdl->esd->ESID) { + u16 ESID = 1; + while (!xmt_esid_available(parser, ESID)) ESID++; + esdl->esd->ESID = ESID; + } + } + + /*set OCR es ids*/ + i=0; + while ((esdl = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &i))) { + Bool use_old_fmt; + u16 ocr_id; + char szTest[50]; + + esdl->esd->OCRESID = 0; + if (!esdl->OCR_Name) continue; + + use_old_fmt = 0; + ocr_id = atoi(esdl->OCR_Name); + sprintf(szTest, "%d", ocr_id); + if (!stricmp(szTest, esdl->OCR_Name)) use_old_fmt = 1; + + j=0; + while ((esdl2 = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &j))) { + if (esdl2->desc_name && !strcmp(esdl2->desc_name, esdl->OCR_Name)) { + esdl->esd->OCRESID = esdl2->esd->ESID; + break; + } + if (use_old_fmt && (esdl2->esd->ESID==ocr_id)) { + esdl->esd->OCRESID = ocr_id; + break; + } + } + if (!esdl->esd->OCRESID) { + xmt_report(parser, GF_OK, "WARNING: Could not find clock reference %s for ES %s - forcing self-synchronization", esdl->OCR_Name, esdl->desc_name); + } + free(esdl->OCR_Name); + esdl->OCR_Name = NULL; + } + + /*set dependsOn es ids*/ + i=0; + while ((esdl = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &i))) { + Bool use_old_fmt; + u16 dep_id; + char szTest[50]; + + esdl->esd->dependsOnESID = 0; + if (!esdl->Depends_Name) continue; + + use_old_fmt = 0; + dep_id = atoi(esdl->Depends_Name); + sprintf(szTest, "%d", dep_id); + if (!stricmp(szTest, esdl->Depends_Name)) use_old_fmt = 1; + + j=0; + while ((esdl2 = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &j))) { + if (esdl2->desc_name && !strcmp(esdl2->desc_name, esdl->Depends_Name)) { + esdl->esd->dependsOnESID = esdl2->esd->ESID; + break; + } + if (use_old_fmt && (esdl2->esd->ESID==dep_id)) { + esdl->esd->dependsOnESID = dep_id; + break; + } + } + if (!esdl->esd->dependsOnESID) { + xmt_report(parser, GF_OK, "WARNING: Could not find stream dependance %s for ES %s - forcing self-synchronization", esdl->Depends_Name, esdl->desc_name); + } + free(esdl->Depends_Name); + esdl->Depends_Name = NULL; + } + + while (gf_list_count(parser->esd_links)) { + esdl = (XMT_ESDLink *)gf_list_get(parser->esd_links, 0); + gf_list_rem(parser->esd_links, 0); + if (esdl->desc_name) free(esdl->desc_name); + free(esdl); + } + + i=0; + while ((l = (XMT_ODLink*)gf_list_enum(parser->od_links, &i))) { + if (l->od && !l->od->objectDescriptorID) { + u16 ODID = 1; + while (!xmt_odid_available(parser, ODID)) ODID++; + l->od->objectDescriptorID = ODID; + } + if (l->od) { + if (!l->ID) l->ID = l->od->objectDescriptorID; + assert(l->ID == l->od->objectDescriptorID); + } + } + + /*unroll dep in case some URLs reference ODs by their binary IDs not their string ones*/ + i=0; + while ((l = (XMT_ODLink*)gf_list_enum(parser->od_links, &i))) { + XMT_ODLink *l2; + /*not OD URL*/ + if (!l->ID) continue; + j=i+1; + while ((l2 = (XMT_ODLink*)gf_list_enum(parser->od_links, &j))) { + /*not OD URL*/ + if (!l2->ID) continue; + if (l->ID == l2->ID) { + while (gf_list_count(l2->mf_urls)) { + MFURL *url = (MFURL *)gf_list_get(l2->mf_urls, 0); + gf_list_rem(l2->mf_urls, 0); + gf_list_add(l->mf_urls, url); + } + j--; + gf_list_rem(parser->od_links, j); + if (l2->desc_name) free(l2->desc_name); + gf_list_del(l2->mf_urls); + free(l2); + } + } + } + + while (gf_list_count(parser->od_links) ) { + l = (XMT_ODLink*)gf_list_get(parser->od_links, 0); + if (!l->od) { + /*if no ID found this is not an OD URL*/ + if (l->ID) { + if (l->desc_name) { + xmt_report(parser, GF_OK, "WARNING: OD \"%s\" (ID %d) not assigned", l->desc_name, l->ID); + } else{ + xmt_report(parser, GF_OK, "WARNING: OD ID %d not assigned", l->ID); + } + } + } else { + MFURL *the_url; + j=0; + while ((the_url = (MFURL *)gf_list_enum(l->mf_urls, &j))) { + u32 k; + char *seg = NULL; + for (k=0; kcount; k++) { + SFURL *url = &the_url->vals[k]; + if (url->url) seg = strstr(url->url, "#"); + if (seg) { + sprintf(szURL, "od:%d#%s", l->od->objectDescriptorID, seg+1); + free(url->url); + url->url = strdup(szURL); + } else { + if (url->url) free(url->url); + url->url = NULL; + url->OD_ID = l->od->objectDescriptorID; + } + } + } + } + + if (l->desc_name) free(l->desc_name); + gf_list_del(l->mf_urls); + free(l); + gf_list_rem(parser->od_links, 0); + } +} + + +static u32 xmt_get_next_node_id(GF_XMTParser *parser) +{ + u32 ID; + GF_SceneGraph *sc = parser->load->scene_graph; + if (parser->parsing_proto) sc = gf_sg_proto_get_graph(parser->parsing_proto); + ID = gf_sg_get_next_available_node_id(sc); + if (parser->load->ctx && (ID>parser->load->ctx->max_node_id)) + parser->load->ctx->max_node_id = ID; + return ID; +} +static u32 xmt_get_node_id(GF_XMTParser *parser, char *name) +{ + GF_Node *n; + u32 ID; + if (sscanf(name, "N%d", &ID) == 1) { + ID ++; + n = gf_sg_find_node(parser->load->scene_graph, ID); + if (n) { + u32 nID = xmt_get_next_node_id(parser); + xmt_report(parser, GF_OK, "WARNING: changing node \"%s\" ID from %d to %d", gf_node_get_name(n), gf_node_get_id(n)-1, nID-1); + gf_node_set_id(n, nID, gf_node_get_name(n)); + } + if (parser->load->ctx && (parser->load->ctx->max_node_idload->ctx->max_node_id=ID; + } else { + ID = xmt_get_next_node_id(parser); + } + return ID; +} + +static u32 xmt_get_node_tag(GF_XMTParser *parser, const char *node_name) +{ + u32 tag; + /*if VRML and allowing non MPEG4 nodes, use X3D*/ + if ((parser->doc_type==2) && !(parser->load->flags & GF_SM_LOAD_MPEG4_STRICT)) { + tag = gf_node_x3d_type_by_class_name(node_name); + if (!tag) tag = gf_node_mpeg4_type_by_class_name(node_name); + } else { + tag = gf_node_mpeg4_type_by_class_name(node_name); + /*if allowing non MPEG4 nodes, try X3D*/ + if (!tag && !(parser->load->flags & GF_SM_LOAD_MPEG4_STRICT)) tag = gf_node_x3d_type_by_class_name(node_name); + } + return tag; +} + +static GF_Node *xmt_find_node(GF_XMTParser *parser, char *ID) +{ + u32 i, count, tag; + Bool is_proto; + char *node_class; + GF_Node *n = gf_sg_find_node_by_name(parser->load->scene_graph, ID); + if (n) return n; + + count = gf_list_count(parser->peeked_nodes); + for (i=0; ipeeked_nodes, i); + if (!strcmp(gf_node_get_name(n), ID)) return n; + } + node_class = gf_xml_sax_peek_node(parser->sax_parser, "DEF", ID, "ProtoInstance", "name", "load->scene_graph; + while (1) { + p = gf_sg_find_proto(sg, 0, node_class); + if (p) break; + sg = sg->parent_scene; + if (!sg) break; + } + if (!p) { + xmt_report(parser, GF_BAD_PARAM, "%s: not a valid/supported proto", node_class); + free(node_class); + return NULL; + } + n = gf_sg_proto_create_instance(parser->load->scene_graph, p); + } else { + tag = xmt_get_node_tag(parser, node_class); + n = gf_node_new(parser->load->scene_graph, tag); + } + free(node_class); + if (n) { + u32 nID = xmt_get_node_id(parser, ID); + gf_node_set_id(n, nID, ID); + if (!parser->parsing_proto) gf_node_init(n); + gf_list_add(parser->peeked_nodes, n); + } + return n; +} + +#define XMT_GET_ONE_VAL \ + char value[100]; \ + u32 i; \ + char *str = a_value; \ + if (!str) { \ + xmt_report(parser, GF_BAD_PARAM, "%s: Number expected", name); \ + return 0; \ + } \ + while (str[0] == ' ') str += 1; \ + i = 0; \ + while ((str[i] != ' ') && str[i]) { \ + value[i] = str[i]; \ + i++; \ + } \ + value[i] = 0; \ + while ((str[i] == ' ') && str[i]) i++; + +static u32 xmt_parse_int(GF_XMTParser *parser, const char *name, SFInt32 *val, char *a_value) +{ + XMT_GET_ONE_VAL + *val = atoi(value); + return i; +} +static u32 xmt_parse_float(GF_XMTParser *parser, const char *name, SFFloat *val, char *a_value) +{ + XMT_GET_ONE_VAL + *val = FLT2FIX(atof(value)); + return i; +} +static u32 xmt_parse_time(GF_XMTParser *parser, const char *name, SFTime *val, char *a_value) +{ + XMT_GET_ONE_VAL + *val = atof(value); + return i; +} +static u32 xmt_parse_bool(GF_XMTParser *parser, const char *name, SFBool *val, char *a_value) +{ + XMT_GET_ONE_VAL + if (!stricmp(value, "1") || !stricmp(value, "true")) + *val = 1; + else + *val = 0; + return i; +} + +static u32 xmt_parse_string(GF_XMTParser *parser, const char *name, SFString *val, Bool is_mf, char *a_value) +{ + char *value; + char sep[10]; + u32 len; + u32 i=0; + u32 k=0; + char *str = a_value; + if (!str) return 0; + + /*SF string, no inspection*/ + if (!is_mf) { + len = strlen(str); + if (val->buffer) free(val->buffer); + val->buffer = NULL; + if (len) val->buffer = strdup(str); + return len+1; + } + + /*now this is the REAL pain: + X3D allows '"String1" "String2"' and therefore '"String "test""' + XMT allows '"String1" "String2"' and therefore '"String \"test\""' + thus translating the string from xml to UTF may screw up the separators !! We need to identify them + */ + + i = 0; + while ((str[i]==' ') || (str[i]=='\t')) i++; + if (!strncmp(&str[i], """, 6)) strcpy(sep, """); + else if (!strncmp(&str[i], "'", 6)) strcpy(sep, "'"); + else if (str[i]=='\'') strcpy(sep, "\'"); + else if (str[i]=='\"') strcpy(sep, "\""); + /*handle as a single field (old GPAC XMT & any unknown cases...*/ + else { + len = strlen(str); + if (val->buffer) free(val->buffer); + val->buffer = NULL; + if (len) val->buffer = strdup(str); + return len; + } + k = 0; + i += strlen(sep); + + value = strdup(str); + + if (strncmp(&str[i], sep, strlen(sep))) { + + while (str[i]) { + if ((str[i] == '\\') && !strncmp(&str[i+1], sep, strlen(sep))) { + i++; + continue; + } + value[k] = str[i]; + i++; + k++; + if (!strncmp(&str[i], sep, strlen(sep)) && (str[i-1] != '\\')) break; + } + } + value[k] = 0; + len = strlen(sep) + i; + + if (val->buffer) free(val->buffer); + val->buffer = NULL; + if (strlen(value)) val->buffer = strdup(value); + free(value); + return len; +} + +static u32 xmt_parse_url(GF_XMTParser *parser, const char *name, MFURL *val, GF_Node *owner, Bool is_mf, char *a_value) +{ + SFString sfstr; + u32 res, idx; + char value[5000], *tmp; + + /*parse as a string*/ + sfstr.buffer = NULL; + res = xmt_parse_string(parser, name, &sfstr, is_mf, a_value); + if (parser->last_error) return res; + + assert(val->count); + idx = val->count - 1; + if (val->vals[idx].url) free(val->vals[idx].url); + val->vals[idx].url = sfstr.buffer; + val->vals[idx].OD_ID = 0; + /*empty*/ + if (!val->vals[idx].url) return res; + + /*remove segments & viewpoints info to create OD link*/ + strcpy(value, val->vals[idx].url); + tmp = strstr(value, "#"); + if (tmp) tmp[0] = 0; + + /*according to XMT-A spec, both 'od:' and 'od://' are tolerated in XMT-A*/ + if (!strnicmp(value, "od://", 5)) + xmt_new_od_link_from_node(parser, value+5, val); + else if (!strnicmp(value, "od:", 3)) + xmt_new_od_link_from_node(parser, value+3, val); + else + xmt_new_od_link_from_node(parser, value, val); + return res; +} + + +static u32 xmt_parse_script(GF_XMTParser *parser, const char *name, SFScript *val, Bool is_mf, char *a_value) +{ + SFString sfstr; + u32 res; + + /*parse as a string*/ + sfstr.buffer = NULL; + res = xmt_parse_string(parser, name, &sfstr, is_mf, a_value); + if (parser->last_error) return res; + + if (val->script_text) free(val->script_text); + val->script_text = (unsigned char*)sfstr.buffer; + return res; +} + +static void xmt_offset_time(GF_XMTParser *parser, Double *time) +{ + *time += parser->au_time; +} +static void xmt_check_time_offset(GF_XMTParser *parser, GF_Node *n, GF_FieldInfo *info) +{ + if (!(parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK)) return; + if (gf_node_get_tag(n) != TAG_ProtoNode) { + if (!stricmp(info->name, "startTime") || !stricmp(info->name, "stopTime")) + xmt_offset_time(parser, (Double *)info->far_ptr); + } else if (gf_sg_proto_field_is_sftime_offset(n, info)) { + xmt_offset_time(parser, (Double *)info->far_ptr); + } +} + +static u32 xmt_parse_sf_field(GF_XMTParser *parser, GF_FieldInfo *info, GF_Node *n, char *a_value) +{ + u32 res = 0; + switch (info->fieldType) { + case GF_SG_VRML_SFINT32: + res = xmt_parse_int(parser, info->name, (SFInt32 *)info->far_ptr, a_value); + break; + case GF_SG_VRML_SFBOOL: + res = xmt_parse_bool(parser, info->name, (SFBool *)info->far_ptr, a_value); + break; + case GF_SG_VRML_SFFLOAT: + res = xmt_parse_float(parser, info->name, (SFFloat *)info->far_ptr, a_value); + break; + case GF_SG_VRML_SFTIME: + res = xmt_parse_time(parser, info->name, (SFTime *)info->far_ptr, a_value); + xmt_check_time_offset(parser, n, info); + break; + case GF_SG_VRML_SFCOLOR: + res = xmt_parse_float(parser, info->name, & ((SFColor *)info->far_ptr)->red, a_value); + res += xmt_parse_float(parser, info->name, & ((SFColor *)info->far_ptr)->green, a_value + res); + res += xmt_parse_float(parser, info->name, & ((SFColor *)info->far_ptr)->blue, a_value + res); + break; + case GF_SG_VRML_SFVEC2F: + res = xmt_parse_float(parser, info->name, & ((SFVec2f *)info->far_ptr)->x, a_value); + res += xmt_parse_float(parser, info->name, & ((SFVec2f *)info->far_ptr)->y, a_value + res); + break; + case GF_SG_VRML_SFVEC3F: + res = xmt_parse_float(parser, info->name, & ((SFVec3f *)info->far_ptr)->x, a_value); + res += xmt_parse_float(parser, info->name, & ((SFVec3f *)info->far_ptr)->y, a_value + res); + res += xmt_parse_float(parser, info->name, & ((SFVec3f *)info->far_ptr)->z, a_value + res); + break; + case GF_SG_VRML_SFROTATION: + res = xmt_parse_float(parser, info->name, & ((SFRotation *)info->far_ptr)->x, a_value); + res += xmt_parse_float(parser, info->name, & ((SFRotation *)info->far_ptr)->y, a_value + res); + res += xmt_parse_float(parser, info->name, & ((SFRotation *)info->far_ptr)->z, a_value + res); + res += xmt_parse_float(parser, info->name, & ((SFRotation *)info->far_ptr)->q, a_value + res); + break; + case GF_SG_VRML_SFSTRING: + res = xmt_parse_string(parser, info->name, (SFString*)info->far_ptr, 0, a_value); + break; + case GF_SG_VRML_SFSCRIPT: + res = xmt_parse_script(parser, info->name, (SFScript *)info->far_ptr, 0, a_value); + break; + case GF_SG_VRML_SFCOMMANDBUFFER: + { + SFCommandBuffer *cb = (SFCommandBuffer *)info->far_ptr; + if (parser->command_buffer) { + cb->buffer = (unsigned char*)parser->command_buffer; + } else { + cb->buffer = (unsigned char*)parser->command; + } + parser->command_buffer = cb; + } + break; + + case GF_SG_VRML_SFIMAGE: + { + u32 k, size, v; + SFImage *img = (SFImage *)info->far_ptr; + res = xmt_parse_int(parser, "width", (SFInt32*)&img->width, a_value); + if (parser->last_error) return res; + res += xmt_parse_int(parser, "height", (SFInt32*)&img->height, a_value + res); + if (parser->last_error) return res; + res += xmt_parse_int(parser, "nbComp", (SFInt32*)&v, a_value + res); + if (parser->last_error) return res; + img->numComponents = v; + size = img->width * img->height * img->numComponents; + if (img->pixels) free(img->pixels); + img->pixels = (unsigned char*)malloc(sizeof(char) * size); + a_value += res; + res = 0; + for (k=0; knumComponents) { + case 1: + img->pixels[k] = (char) v; + break; + case 2: + img->pixels[k] = (char) (v>>8)&0xFF; + img->pixels[k+1] = (char) (v)&0xFF; + k++; + break; + case 3: + img->pixels[k] = (char) (v>>16)&0xFF; + img->pixels[k+1] = (char) (v>>8)&0xFF; + img->pixels[k+2] = (char) (v)&0xFF; + k+=2; + break; + case 4: + img->pixels[k] = (char) (v>>24)&0xFF; + img->pixels[k+1] = (char) (v>>16)&0xFF; + img->pixels[k+2] = (char) (v>>8)&0xFF; + img->pixels[k+3] = (char) (v)&0xFF; + k+=3; + break; + } + res += i; + a_value += i; + } + } + break; + + default: + parser->last_error = GF_NOT_SUPPORTED; + break; + } + return res; +} + +static void xmt_parse_mf_field(GF_XMTParser *parser, GF_FieldInfo *info, GF_Node *n, char *value) +{ + u32 res; + GF_FieldInfo sfInfo; + sfInfo.fieldType = gf_sg_vrml_get_sf_type(info->fieldType); + sfInfo.name = info->name; + gf_sg_vrml_mf_reset(info->far_ptr, info->fieldType); + + if (!value || !strlen(value)) return; + + while (value[0] && !parser->last_error) { + + while (value[0] && value[0] == ' ') value++; + if (!value[0]) break; + + gf_sg_vrml_mf_append(info->far_ptr, info->fieldType, &sfInfo.far_ptr); + + /*special case for MF type based on string (MFString, MFURL and MFScript), we need to take care + of all possible forms of XML multi string encoding*/ + if (sfInfo.fieldType == GF_SG_VRML_SFSTRING) { + res = xmt_parse_string(parser, info->name, (SFString*)sfInfo.far_ptr, 1, value); + } else if (sfInfo.fieldType == GF_SG_VRML_SFURL) { + res = xmt_parse_url(parser, info->name, (MFURL *)info->far_ptr, n, 1, value); + } else if (sfInfo.fieldType == GF_SG_VRML_SFSCRIPT) { + res = xmt_parse_script(parser, info->name, (SFScript*)sfInfo.far_ptr, 1, value); + } else { + res = xmt_parse_sf_field(parser, &sfInfo, n, value); + } + if (res) { + value += res; + } else { + break; + } + } +} + +static Bool xmt_has_been_def(GF_XMTParser *parser, char *node_name) +{ + u32 i, count; + count = gf_list_count(parser->def_nodes); + for (i=0; idef_nodes, i); + if (!strcmp(gf_node_get_name(n), node_name)) return 1; + } + return 0; +} + +static u32 xmt_get_route(GF_XMTParser *parser, char *name, Bool del_com) +{ + u32 i; + GF_Command *com; + GF_Route *r = gf_sg_route_find_by_name(parser->load->scene_graph, name); + if (r) return r->ID; + i=0; + while ((com = (GF_Command *)gf_list_enum(parser->inserted_routes, &i))) { + if (com->def_name && !strcmp(com->def_name, name)) { + if (del_com) gf_list_rem(parser->inserted_routes, i); + return com->RouteID; + } + } + return 0; +} + +static Bool xmt_route_id_used(GF_XMTParser *parser, u32 ID) +{ + u32 i; + GF_Command *com; + GF_Route *r = gf_sg_route_find(parser->load->scene_graph, ID); + if (r) return 1; + i=0; + while ((com = (GF_Command *)gf_list_enum(parser->inserted_routes, &i))) { + if (com->RouteID == ID) return 1; + } + return 0; +} +static u32 xmt_get_next_route_id(GF_XMTParser *parser) +{ + u32 ID; + GF_SceneGraph *sc = parser->load->scene_graph; + if (parser->parsing_proto) sc = gf_sg_proto_get_graph(parser->parsing_proto); + + ID = gf_sg_get_next_available_route_id(sc); + if (parser->load->ctx && (ID>parser->load->ctx->max_route_id)) + parser->load->ctx->max_route_id = ID; + return ID; +} + +static void xmt_resolve_routes(GF_XMTParser *parser) +{ + GF_Command *com; + /*resolve all commands*/ + while (1) { + com = (GF_Command *)gf_list_last(parser->unresolved_routes); + if (!com) break; + gf_list_rem_last(parser->unresolved_routes); + switch (com->tag) { + case GF_SG_ROUTE_DELETE: + case GF_SG_ROUTE_REPLACE: + com->RouteID = xmt_get_route(parser, com->unres_name, 0); + if (!com->RouteID) { + xmt_report(parser, GF_BAD_PARAM, "Cannot resolve GF_Route DEF %s", com->unres_name); + } + free(com->unres_name); + com->unres_name = NULL; + com->unresolved = 0; + break; + } + } + while (gf_list_count(parser->inserted_routes)) gf_list_rem(parser->inserted_routes, 0); +} +static void xmt_parse_route(GF_XMTParser *parser, const GF_XMLAttribute *attributes, u32 nb_attributes, Bool is_insert, GF_Command *com) +{ + GF_Route *r; + char *toN, *toNF, *fromN, *fromNF, *ID; + GF_Node *orig, *dest; + GF_Err e; + u32 rID, i; + GF_FieldInfo orig_field, dest_field; + + toN = toNF = fromN = fromNF = ID = NULL; + + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "fromNode")) fromN = att->value; + else if (!strcmp(att->name, "fromField")) fromNF = att->value; + else if (!strcmp(att->name, "toNode")) toN = att->value; + else if (!strcmp(att->name, "toField")) toNF = att->value; + else if (!strcmp(att->name, "DEF")) ID = att->value; + } + + orig = xmt_find_node(parser, fromN); + if (!orig) { + xmt_report(parser, GF_BAD_PARAM, "ROUTE: Cannot find origin node %s", fromN); + return; + } + e = gf_node_get_field_by_name(orig, fromNF, &orig_field); + if ((e != GF_OK) && strstr(fromNF, "_changed")) { + char *sz = strstr(fromNF, "_changed"); + sz[0] = 0; + e = gf_node_get_field_by_name(orig, fromNF, &orig_field); + } + if (e!=GF_OK) { + xmt_report(parser, GF_BAD_PARAM, "%s is not an attribute of node %s", fromNF, fromN); + return; + } + dest = xmt_find_node(parser, toN); + if (!dest) { + xmt_report(parser, GF_BAD_PARAM, "ROUTE: Cannot find destination node %s", toN); + return; + } + e = gf_node_get_field_by_name(dest, toNF, &dest_field); + if ((e != GF_OK) && !strnicmp(toNF, "set_", 4)) e = gf_node_get_field_by_name(dest, &toNF[4], &dest_field); + if (e != GF_OK) { + xmt_report(parser, GF_BAD_PARAM, "%s is not an attribute of node %s", toNF, toN); + return; + } + rID = 0; + if (ID && strlen(ID)) { + rID = xmt_get_route(parser, ID, 0); + if (!rID && (ID[0]=='R') ) { + rID = atoi(&ID[1]); + if (rID) { + rID++; + if (xmt_route_id_used(parser, rID)) rID = 0; + } + } + if (!rID) rID = xmt_get_next_route_id(parser); + } + if (com) { + /*for insert command*/ + if (rID) { + com->RouteID = rID; + com->def_name = strdup(ID); + /*whenever not inserting in graph, keep track of max defined ID*/ + gf_sg_set_max_defined_route_id(parser->load->scene_graph, rID); + if (rID>parser->load->ctx->max_route_id) parser->load->ctx->max_route_id = rID; + + } + com->fromNodeID = gf_node_get_id(orig); + com->fromFieldIndex = orig_field.fieldIndex; + com->toNodeID = gf_node_get_id(dest); + com->toFieldIndex = dest_field.fieldIndex; + return; + } + r = gf_sg_route_new(parser->load->scene_graph, orig, orig_field.fieldIndex, dest, dest_field.fieldIndex); + if (rID) { + gf_sg_route_set_id(r, rID); + gf_sg_route_set_name(r, ID); + } +} + + +static void xmt_update_timenode(GF_XMTParser *parser, GF_Node *node) +{ + if (!(parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK)) return; + + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_AnimationStream: + xmt_offset_time(parser, & ((M_AnimationStream*)node)->startTime); + xmt_offset_time(parser, & ((M_AnimationStream*)node)->stopTime); + break; + case TAG_MPEG4_AudioBuffer: + xmt_offset_time(parser, & ((M_AudioBuffer*)node)->startTime); + xmt_offset_time(parser, & ((M_AudioBuffer*)node)->stopTime); + break; + case TAG_MPEG4_AudioClip: + xmt_offset_time(parser, & ((M_AudioClip*)node)->startTime); + xmt_offset_time(parser, & ((M_AudioClip*)node)->stopTime); + break; + case TAG_MPEG4_AudioSource: + xmt_offset_time(parser, & ((M_AudioSource*)node)->startTime); + xmt_offset_time(parser, & ((M_AudioSource*)node)->stopTime); + break; + case TAG_MPEG4_MovieTexture: + xmt_offset_time(parser, & ((M_MovieTexture*)node)->startTime); + xmt_offset_time(parser, & ((M_MovieTexture*)node)->stopTime); + break; + case TAG_MPEG4_TimeSensor: + xmt_offset_time(parser, & ((M_TimeSensor*)node)->startTime); + xmt_offset_time(parser, & ((M_TimeSensor*)node)->stopTime); + break; + case TAG_ProtoNode: + { + u32 i, nbFields; + GF_FieldInfo inf; + nbFields = gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_ALL); + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "name")) fieldName = att->value; + else if (!strcmp(att->name, "type")) fieldType = xmt_get_ft_by_name(att->value); + else if (!strcmp(att->name, "vrml97Hint") || !strcmp(att->name, "accessType")) eventType = xmt_get_script_et_by_name(att->value); + else if (strstr(att->name, "value") || strstr(att->name, "Value")) val = att->value; + } + scfield = gf_sg_script_field_new(node, eventType, fieldType, fieldName); + + if (!scfield) { + xmt_report(parser, GF_BAD_PARAM, "Cannot create script field - please check syntax"); + return; + } + if (val) { + gf_node_get_field_by_name(node, fieldName, &field); + if (gf_sg_vrml_is_sf_field(fieldType)) { + xmt_parse_sf_field(parser, &field, node, val); + } else { + xmt_parse_mf_field(parser, &field, node, val); + } + } +} + +static u32 xmt_get_next_proto_id(GF_XMTParser *parser) +{ + u32 ID; + GF_SceneGraph *sc = parser->load->scene_graph; + if (parser->parsing_proto) sc = gf_sg_proto_get_graph(parser->parsing_proto); + ID = gf_sg_get_next_available_proto_id(sc); + if (parser->load->ctx && (ID>parser->load->ctx->max_node_id)) + parser->load->ctx->max_proto_id = ID; + return ID; +} + +static void xmt_parse_proto(GF_XMTParser *parser, const GF_XMLAttribute *attributes, u32 nb_attributes, GF_List *proto_list) +{ + GF_FieldInfo info; + GF_Proto *proto; + char *szName, *extURL; + u32 ID, i; + + ID = 0; + szName = extURL = NULL; + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "name")) szName = att->value; + else if (!strcmp(att->name, "protoID")) ID = atoi(att->value); + else if (!strcmp(att->name, "locations")) extURL = att->value; + else if (!strcmp(att->name, "url")) extURL = att->value; + } + + ID = xmt_get_next_proto_id(parser); + proto = gf_sg_proto_new(parser->load->scene_graph, ID, szName, proto_list ? 1 : 0); + if (proto_list) gf_list_add(proto_list, proto); + if (parser->load->ctx && (parser->load->ctx->max_proto_idload->ctx->max_proto_id=ID; + + /*store previous proto*/ + proto->userpriv = parser->parsing_proto; + parser->parsing_proto = proto; + parser->load->scene_graph = gf_sg_proto_get_graph(proto); + + if (extURL) { + info.fieldType = GF_SG_VRML_MFURL; + info.far_ptr = &proto->ExternProto; + info.name = "ExternURL"; + xmt_parse_mf_field(parser, &info, NULL, extURL); + } +} +static u32 xmt_get_protofield_qp_type(const char *QP_Type) +{ + if (!strcmp(QP_Type, "position3D")) return QC_3DPOS; + else if (!strcmp(QP_Type, "position2D")) return QC_2DPOS; + else if (!strcmp(QP_Type, "drawingOrder")) return QC_ORDER; + else if (!strcmp(QP_Type, "color")) return QC_COLOR; + else if (!strcmp(QP_Type, "textureCoordinate")) return QC_TEXTURE_COORD; + else if (!strcmp(QP_Type, "angle")) return QC_ANGLE; + else if (!strcmp(QP_Type, "scale")) return QC_SCALE; + else if (!strcmp(QP_Type, "keys")) return QC_INTERPOL_KEYS; + else if (!strcmp(QP_Type, "normals")) return QC_NORMALS; + else if (!strcmp(QP_Type, "rotations")) return QC_ROTATION; + else if (!strcmp(QP_Type, "size3D")) return QC_SIZE_3D; + else if (!strcmp(QP_Type, "size2D")) return QC_SIZE_2D; + else if (!strcmp(QP_Type, "linear")) return QC_LINEAR_SCALAR; + else if (!strcmp(QP_Type, "coordIndex")) return QC_COORD_INDEX; + else return 0; +} + +static GF_Err x3d_get_default_container(GF_Node *par, GF_Node *n, GF_FieldInfo *info) +{ + u32 i, count; + count = gf_node_get_field_count(par); + /*get the first field/exposedField accepting this child*/ + for (i=0; ifieldType!=GF_SG_VRML_SFNODE) && (info->fieldType!=GF_SG_VRML_MFNODE)) continue; + if ((info->eventType==GF_SG_EVENT_OUT) || (info->eventType==GF_SG_EVENT_IN)) continue; + if (gf_node_in_table(n, info->NDTtype)) return GF_OK; + } + return GF_BAD_PARAM; +} + + +static GF_Node *xmt_parse_element(GF_XMTParser *parser, char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes, XMTNodeStack *parent) +{ + GF_Err e; + GF_FieldInfo info; + u32 tag, i, count, ID; + Bool register_def = 0; + Bool is_script = 0; + GF_Node *node; + GF_FieldInfo container; + char *def_name; + GF_Proto *proto = NULL; + + node = NULL; + if (!strcmp(name, "NULL")) return NULL; + if (!strcmp(name, "ROUTE")) { + if (!parser->parsing_proto && (parser->doc_type==1) ) { + GF_Command *sgcom = gf_sg_command_new(parser->load->scene_graph, GF_SG_ROUTE_INSERT); + gf_list_add(parser->scene_au->commands, sgcom); + xmt_parse_route(parser, attributes, nb_attributes, 0, sgcom); + if (sgcom->RouteID) gf_list_add(parser->inserted_routes, sgcom); + } else { + xmt_parse_route(parser, attributes, nb_attributes, 0, NULL); + } + return NULL; + } + if (parent && parent->node && ((parent->node->sgprivate->tag==TAG_MPEG4_Script) || (parent->node->sgprivate->tag==TAG_X3D_Script)) ) { + is_script = 1; + if (!strcmp(name, "field")) { + xmt_parse_script_field(parser, parent->node, attributes, nb_attributes); + return NULL; + } + else if (!strcmp(name, "node") || !strcmp(name, "nodes") ) return NULL; + } + + /*proto declaration*/ + if (!strcmp(name, "ProtoDeclare") || !strcmp(name, "ExternProtoDeclare")) { + if (!parser->parsing_proto && parser->command && !parser->command->new_proto_list) parser->command->new_proto_list = gf_list_new(); + xmt_parse_proto(parser, attributes, nb_attributes, (!parser->parsing_proto && parser->command) ? parser->command->new_proto_list : NULL); + return NULL; + } + /*proto parsing*/ + if (parser->parsing_proto) { + if (!strcmp(name, "IS")) + return NULL; + + if (!strcmp(name, "field")) { + char *fieldName = NULL; + char *value = NULL; + u32 fType, eType; + fType = eType = 0; + + for (i=0; ivalue || !strlen(att->value)) continue; + + if (!strcmp(att->name, "name")) fieldName = att->value; + else if (!strcmp(att->name, "type")) fType = xmt_get_ft_by_name(att->value); + else if (!strcmp(att->name, "vrml97Hint") || !strcmp(att->name, "accessType") ) eType = xmt_get_et_by_name(att->value); + else if (strstr(att->name, "value") || strstr(att->name, "Value")) value = att->value; + } + parser->proto_field = gf_sg_proto_field_new(parser->parsing_proto, fType, eType, fieldName); + if (value && strlen(value)) { + gf_sg_proto_field_get_field(parser->proto_field, &info); + if (gf_sg_vrml_is_sf_field(fType)) { + xmt_parse_sf_field(parser, &info, NULL, value); + } else { + xmt_parse_mf_field(parser, &info, NULL, value); + } + } else if (gf_sg_vrml_get_sf_type(fType) != GF_SG_VRML_SFNODE) { + /*value not specified for exter proto*/ + } + /*SF/MFNode proto field: push node stack with container info but no parent*/ + else { + XMTNodeStack *pf_stack; + GF_SAFEALLOC(pf_stack, XMTNodeStack); + gf_sg_proto_field_get_field(parser->proto_field, &pf_stack->container_field); + gf_list_add(parser->nodes, pf_stack); + } + return NULL; + } + + /*X3D style*/ + if (!strcmp(name, "ProtoInterface") || !strcmp(name, "ProtoBody")) + return NULL; + /*XMT1 decl for SFNode proto fields*/ + if (parser->proto_field && (!strcmp(name, "node") || !strcmp(name, "nodes")) ) + return NULL; + /*anim & QP info */ + if (parser->proto_field && !strcmp(name, "InterfaceCodingParameters")) { + u32 qp_type, nbBits, hasMinMax, qp_sftype; + Fixed ftMin, ftMax; + ftMin = ftMax = 0; + qp_type = hasMinMax = nbBits = 0; + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "quantCategory")) qp_type = xmt_get_protofield_qp_type(att->value); + else if (!strcmp(att->name, "nbBits")) nbBits = atoi(att->value); + else if (!strncmp(att->name, "position3DM", 11) || !strncmp(att->name, "position2DM", 11) + || !strncmp(att->name, "drawOrderM", 10) || !strncmp(att->name, "colorM", 6) + || !strncmp(att->name, "textureCoordinateM", 18) || !strncmp(att->name, "angleM", 6) + || !strncmp(att->name, "scaleM", 6) || !strncmp(att->name, "keyM", 4) || !strncmp(att->name, "sizeM", 5) + ) { + hasMinMax = 1; + if (strstr(att->name, "Min")) xmt_parse_float(parser, att->name, &ftMin, att->value); + else xmt_parse_float(parser, att->name, &ftMax, att->value); + } + } + if (gf_sg_vrml_get_sf_type(parser->proto_field->FieldType) == GF_SG_VRML_SFINT32) { + qp_sftype = GF_SG_VRML_SFINT32; + } else { + qp_sftype = GF_SG_VRML_SFFLOAT; + } + gf_bifs_proto_field_set_aq_info(parser->proto_field, qp_type, hasMinMax, qp_sftype, &ftMin, &ftMax, nbBits); + return NULL; + } + /*connect */ + if (!strcmp(name, "connect")) { + GF_ProtoFieldInterface *pf; + Bool is_script = 0; + GF_FieldInfo pfield, nfield; + char *atField, *atProtoField; + XMTNodeStack *last = (XMTNodeStack*)gf_list_last(parser->nodes); + if (!last) { + xmt_report(parser, GF_OK, "connect: no parent node specified - skipping"); + return NULL; + } + atField = atProtoField = NULL; + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "nodeField")) atField = att->value; + else if (!strcmp(att->name, "protoField")) atProtoField = att->value; + } + if (!atField) { + xmt_report(parser, GF_OK, "connect: Missing node field - skipping"); + return NULL; + } + if (!atProtoField) { + xmt_report(parser, GF_OK, "connect: Missing proto field - skipping"); + return NULL; + } + if ( (e = gf_node_get_field_by_name(last->node, atField, &nfield)) != GF_OK) { + u32 l_tag = gf_node_get_tag(last->node); + if ((l_tag!=TAG_MPEG4_Script) && (l_tag!=TAG_X3D_Script)) { + xmt_report(parser, e, "connect: %s not an field of node %s", atField, gf_node_get_class_name(last->node) ); + return NULL; + } + is_script = 1; + } + pf = gf_sg_proto_field_find_by_name(parser->parsing_proto, atProtoField); + if (!pf) { + xmt_report(parser, GF_BAD_PARAM, "connect: Proto field %s is not defined", atProtoField); + return NULL; + } + gf_sg_proto_field_get_field(pf, &pfield); + if (is_script) { + gf_sg_script_field_new(last->node, pfield.eventType, pfield.fieldType, atField); + gf_node_get_field_by_name(last->node, atField, &nfield); + } + e = gf_sg_proto_field_set_ised(parser->parsing_proto, pfield.fieldIndex, last->node, nfield.fieldIndex); + if (e) xmt_report(parser, GF_BAD_PARAM, "connect: %s", gf_error_to_string(e)); + return NULL; + } + } + /*proto instance field*/ + if (!strcmp(name, "fieldValue")) { + char *field, *value; + if (!parent || (parent->node->sgprivate->tag != TAG_ProtoNode)) { + xmt_report(parser, GF_OK, "Warning: fieldValue not a valid node"); + return NULL; + } + field = value = NULL; + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "name")) field = att->value; + else if (!strstr(att->name, "Value") || !strstr(att->name, "value")) value = att->value; + } + if (!field) { + xmt_report(parser, GF_OK, "Warning: unspecified proto field name - skipping"); + return NULL; + } + e = gf_node_get_field_by_name(parent->node, field, &info); + if (e) { + xmt_report(parser, GF_OK, "Warning: Unknown proto field %s - skipping", field); + return NULL; + } + if (value) { + if (gf_sg_vrml_is_sf_field(info.fieldType)) { + xmt_parse_sf_field(parser, &info, parent->node, value); + } else { + xmt_parse_mf_field(parser, &info, parent->node, value); + } + gf_sg_proto_mark_field_loaded(parent->node, &info); + } else if (gf_sg_vrml_get_sf_type(info.fieldType) == GF_SG_VRML_SFNODE) { + parent->container_field = info; + parent->last = NULL; + } + return NULL; + } + if (parent && parent->node && (parent->node->sgprivate->tag == TAG_ProtoNode) && (!strcmp(name, "node") || !strcmp(name, "nodes")) ) { + return NULL; + } + + ID = 0; + def_name = NULL; + tag = 0; + + if (!strcmp(name, "ProtoInstance")) { + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "name")) { + GF_SceneGraph *sg = parser->load->scene_graph; + while (1) { + proto = gf_sg_find_proto(sg, 0, att->value); + if (proto) break; + sg = sg->parent_scene; + if (!sg) break; + } + if (!proto) { + xmt_report(parser, GF_BAD_PARAM, "%s: not a valid/supported proto", att->value); + return NULL; + } + node = gf_sg_proto_create_instance(parser->load->scene_graph, proto); + att->value = NULL; + } + else if (!strcmp(att->name, "USE")) { + node = xmt_find_node(parser, att->value); + e = GF_OK; + if (!node) + e = xmt_report(parser, GF_BAD_PARAM, "Warning: Cannot find node %s referenced in USE - skipping", att->value); + + if (e) return NULL; + ID = 0; + register_def = 0; + tag = 0; + count = 0; + } + } + } else { + tag = xmt_get_node_tag(parser, name); + + if (!tag) { + /*XMT-A weird syntax*/ + if (parent) { + if (gf_node_get_field_by_name(parent->node, name, &parent->container_field)==GF_OK) { + parent->last = NULL; + if (parent->container_field.fieldType==GF_SG_VRML_SFCOMMANDBUFFER) { + parser->command_buffer = (SFCommandBuffer*)parent->container_field.far_ptr; + /*store command*/ + parser->command_buffer->buffer = (unsigned char *)parser->command; + parser->state = XMT_STATE_COMMANDS; + } + return NULL; + } + parent->container_field.far_ptr = NULL; + } + else if (parser->command && (parser->command->tag == GF_SG_MULTIPLE_REPLACE)) { + if (gf_node_get_field_by_name(parser->command->node, name, &container)==GF_OK) { + GF_CommandField *field = gf_sg_command_field_new(parser->command); + field->fieldIndex = container.fieldIndex; + field->fieldType = container.fieldType; + return NULL; + } + } + xmt_report(parser, GF_OK, "Warning: %s is not a valid node - skipping", name); + return NULL; + } + node = gf_node_new(parser->load->scene_graph, tag); + if (!node) { + xmt_report(parser, GF_SG_UNKNOWN_NODE, "Warning: %s is not a supported node - skipping", name); + return NULL; + } + } + + parser->current_node_tag = tag; + + if (parent) container = parent->container_field; + else { + container.far_ptr = NULL; + container.fieldIndex = 0; + container.fieldType = 0; + } + + for (i=0; ivalue || !strlen(att->value)) continue; + + if (!strcmp(att->name, "DEF")) { + GF_Node *undef_node = gf_sg_find_node_by_name(parser->load->scene_graph, att->value); + register_def = 1; + if (undef_node) { + gf_list_del_item(parser->peeked_nodes, undef_node); + ID = gf_node_get_id(undef_node); + /*if we see twice a DEF N1 then force creation of a new node*/ + if (xmt_has_been_def(parser, att->value)) { + ID = xmt_get_node_id(parser, att->value); + xmt_report(parser, GF_OK, "Warning: Node %s has been defined several times - IDs may get corrupted", att->value); + } else { + gf_node_register(node, NULL); + gf_node_unregister(node, NULL); + node = undef_node; + ID = 0; + } + } else { + ID = xmt_get_node_id(parser, att->value); + + } + def_name = att->value; + } + /*USE node*/ + else if (!strcmp(att->name, "USE")) { + GF_Err e; + GF_Node *def_node; + + def_node = xmt_find_node(parser, att->value); + + e = GF_OK; + if (!def_node) + e = xmt_report(parser, GF_BAD_PARAM, "Warning: Cannot find node %s referenced in USE - skipping", att->value); + else if (tag != gf_node_get_tag(def_node)) { + xmt_report(parser, GF_OK, "Warning: Node type %s doesn't match type %s of node %s", gf_node_get_class_name(node), gf_node_get_class_name(def_node), att->value); + } + + /*DESTROY NODE*/ + gf_node_register(node, NULL); + gf_node_unregister(node, NULL); + + if (e) return NULL; + + node = def_node; + ID = 0; + register_def = 0; + tag = 0; + break; + } + /*X3D stuff*/ + else if (!strcmp(att->name, "containerField")) { + if (parent) { + if (gf_node_get_field_by_name(parent->node, att->value, &container) != GF_OK) { + xmt_report(parser, GF_BAD_PARAM, "Warning: Container field %s not member of node %s", att->value, name); + container.far_ptr = NULL; + } + } + } + /*ignored ones*/ + else if (!strcmp(att->name, "bboxCenter") || !strcmp(att->name, "bboxSize")) { + } + /*all other fields*/ + else { + e = gf_node_get_field_by_name(node, att->name, &info); + if (e) xmt_report(parser, GF_OK, "Warning: Unknown field \"%s\" for node %s - skipping", att->name, name); + else if (gf_sg_vrml_is_sf_field(info.fieldType)) { + xmt_parse_sf_field(parser, &info, node, att->value); + } else { + xmt_parse_mf_field(parser, &info, node, att->value); + } + } + } + + if (!parser->parsing_proto) xmt_update_timenode(parser, node); + + if (register_def) gf_list_add(parser->def_nodes, node); + if (ID) gf_node_set_id(node, ID, def_name); + + if (is_script) { + u32 last_field = gf_node_get_field_count(parent->node); + gf_node_get_field(parent->node, last_field-1, &container); + } + + if (parent) { + if (!container.far_ptr) { + if (parser->doc_type==2) { + x3d_get_default_container(parent->node, node, &container); + parent->last = NULL; + } + if (!container.far_ptr) { + gf_node_get_field_by_name(parent->node, "children", &container); + parent->last = NULL; + } + + } + if (container.fieldType == GF_SG_VRML_SFNODE) { + if (* ((GF_Node **)container.far_ptr) ) gf_node_unregister(* ((GF_Node **)container.far_ptr) , parent->node); + * ((GF_Node **)container.far_ptr) = node; + gf_node_register(node, parent->node); + parent->container_field.far_ptr = NULL; + parent->last = NULL; + } else if (container.fieldType == GF_SG_VRML_MFNODE) { + gf_node_list_add_child_last( (GF_ChildNodeItem **)container.far_ptr, node, &parent->last); + gf_node_register(node, parent->node); + } + assert(parent->node); + gf_node_changed(parent->node, NULL); + } + + if (!parser->parsing_proto && (tag || proto) ) + gf_node_init(node); + + return node; +} + + +GF_Descriptor *xmt_parse_descriptor(GF_XMTParser *parser, char *name, const GF_XMLAttribute *attributes, u32 nb_attributes, GF_Descriptor *parent) +{ + GF_Err e; + u32 i; + Bool fake_desc = 0; + GF_Descriptor *desc; + char *xmt_desc_name = NULL, *ocr_ref = NULL, *dep_ref = NULL; + u32 binaryID = 0; + u8 tag = gf_odf_get_tag_by_name(name); + + if (!tag) { + if (!parent) return NULL; + switch (parent->tag) { + case GF_ODF_IOD_TAG: + case GF_ODF_OD_TAG: + if (!strcmp(name, "Profiles")) fake_desc = 1; + else if (!strcmp(name, "Descr")) fake_desc = 1; + else if (!strcmp(name, "esDescr")) fake_desc = 1; + else if (!strcmp(name, "URL")) fake_desc = 1; + else return NULL; + break; + case GF_ODF_ESD_TAG: + if (!strcmp(name, "decConfigDescr")) fake_desc = 1; + else if (!strcmp(name, "slConfigDescr")) fake_desc = 1; + else return NULL; + break; + case GF_ODF_DCD_TAG: + if (!strcmp(name, "decSpecificInfo")) fake_desc = 1; + else return NULL; + break; + case GF_ODF_SLC_TAG: + if (!strcmp(name, "custom")) fake_desc = 1; + else return NULL; + break; + case GF_ODF_MUXINFO_TAG: + if (!strcmp(name, "MP4MuxHints")) fake_desc = 1; + else return NULL; + break; + case GF_ODF_BIFS_CFG_TAG: + if (!strcmp(name, "commandStream")) fake_desc = 1; + else if (!strcmp(name, "size")) fake_desc = 2; + else return NULL; + break; + default: + return NULL; + } + } + if (fake_desc) { + tag = parent->tag; + desc = parent; + } else { + desc = gf_odf_desc_new(tag); + if (!desc) return NULL; + } + + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "binaryID")) binaryID = atoi(att->value); + else if (!stricmp(att->name, "objectDescriptorID")) xmt_desc_name = att->value; + else if (!strcmp(att->name, "ES_ID")) xmt_desc_name = att->value; + else if (!strcmp(att->name, "OCR_ES_ID")) ocr_ref = att->value; + else if (!strcmp(att->name, "dependsOn_ES_ID")) dep_ref = att->value; + else { + e = gf_odf_set_field(desc, att->name, att->value); + if (e) xmt_report(parser, e, "Warning: %s not a valid attribute for descriptor %s", att->name, name); + } + } + if (binaryID || xmt_desc_name) { + if ((tag == GF_ODF_IOD_TAG) || (tag == GF_ODF_OD_TAG)) + xmt_new_od_link(parser, (GF_ObjectDescriptor *)desc, xmt_desc_name, binaryID); + else if (tag == GF_ODF_ESD_TAG) { + xmt_new_esd_link(parser, (GF_ESD *) desc, xmt_desc_name, binaryID); + + /*set references once the esd link has been established*/ + if (ocr_ref) xmt_set_depend_id(parser, (GF_ESD *) desc, ocr_ref, 1); + if (dep_ref) xmt_set_depend_id(parser, (GF_ESD *) desc, dep_ref, 0); + } + } + + if (fake_desc) { + if (fake_desc==2) { + GF_BIFSConfig *bcfg = (GF_BIFSConfig *)desc; + parser->load->ctx->scene_width = bcfg->pixelWidth; + parser->load->ctx->scene_height = bcfg->pixelHeight; + parser->load->ctx->is_pixel_metrics = bcfg->pixelMetrics; + } + return NULL; + } + if (parent) { + e = gf_odf_desc_add_desc(parent, desc); + if (e) { + xmt_report(parser, GF_OK, "Invalid child descriptor"); + gf_odf_desc_del(desc); + return NULL; + } + /*finally check for scene manager streams (scene description, OD, ...)*/ + if (parent->tag == GF_ODF_ESD_TAG) { + GF_ESD *esd = (GF_ESD *)parent; + if (esd->decoderConfig) { + switch (esd->decoderConfig->streamType) { + case GF_STREAM_SCENE: + case GF_STREAM_OD: + /*watchout for default BIFS stream*/ + if (parser->scene_es && !parser->base_scene_id && (esd->decoderConfig->streamType==GF_STREAM_SCENE)) { + parser->scene_es->ESID = parser->base_scene_id = esd->ESID; + parser->scene_es->timeScale = (esd->slConfig && esd->slConfig->timestampResolution) ? esd->slConfig->timestampResolution : 1000; + } else { + GF_StreamContext *sc = gf_sm_stream_new(parser->load->ctx, esd->ESID, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); + /*set default timescale for systems tracks (ignored for other)*/ + if (sc) sc->timeScale = (esd->slConfig && esd->slConfig->timestampResolution) ? esd->slConfig->timestampResolution : 1000; + if (!parser->base_scene_id && (esd->decoderConfig->streamType==GF_STREAM_SCENE)) parser->base_scene_id = esd->ESID; + else if (!parser->base_od_id && (esd->decoderConfig->streamType==GF_STREAM_OD)) parser->base_od_id = esd->ESID; + } + break; + } + } + } + } + return desc; +} + +static void xmt_parse_command(GF_XMTParser *parser, const char *name, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + GF_Err e; + GF_FieldInfo info; + GF_CommandField *field; + u32 i; + if (!strcmp(name, "Scene")) { + parser->state = XMT_STATE_ELEMENTS; + return; + } + + if (!strcmp(name, "par")) { + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "begin")) parser->au_time = atof(att->value); + else if (!strcmp(att->name, "isRAP")) parser->au_is_rap = !strcmp(att->value, "yes") ? 1 : 0; + else if (!strcmp(att->name, "atES_ID")) { + parser->stream_id = xmt_locate_stream(parser, att->value); + if (!parser->stream_id) xmt_report(parser, GF_OK, "Warning: Cannot locate command's target stream %s", att->value); + } + } + return; + } + /*ROUTE insert/replace*/ + if (!strcmp(name, "ROUTE")) { + if (!parser->command || ((parser->command->tag!=GF_SG_ROUTE_REPLACE) && (parser->command->tag!=GF_SG_ROUTE_INSERT))) { + xmt_report(parser, GF_BAD_PARAM, "ROUTE declared outside command scope"); + return; + } + if (parser->command->tag==GF_SG_ROUTE_INSERT) { + xmt_parse_route(parser, attributes, nb_attributes, 1, parser->command); + gf_list_add(parser->inserted_routes, parser->command); + } else { + xmt_parse_route(parser, attributes, nb_attributes, 0, parser->command); + if (!parser->command->RouteID) { + parser->command->unresolved = 1; + if (gf_list_find(parser->unresolved_routes, parser->command)<0) + gf_list_add(parser->unresolved_routes, parser->command); + } + } + return; + } + /*multiple replace*/ + if (!strcmp(name, "repField")) { + char *fieldName = NULL; + char *fieldValue = NULL; + assert(parser->command); + if (!parser->command->node) return; + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "atField")) fieldName = att->value; + else if (!strcmp(att->name, "value")) fieldValue = att->value; + } + if (!fieldName) { + parser->state = XMT_STATE_ELEMENTS; + return; + } + e = gf_node_get_field_by_name(parser->command->node, fieldName, &info); + if (e) { + xmt_report(parser, GF_BAD_PARAM, "Warning: Field %s not a member of node %s ", fieldName, gf_node_get_class_name(parser->command->node) ); + return; + } + if (gf_sg_vrml_get_sf_type(info.fieldType) == GF_SG_VRML_SFNODE) { + parser->state = XMT_STATE_ELEMENTS; + return; + } + if (!fieldValue) return; + + field = gf_sg_command_field_new(parser->command); + field->fieldIndex = info.fieldIndex; + field->fieldType = info.fieldType; + if (fieldValue) { + field->field_ptr = gf_sg_vrml_field_pointer_new(info.fieldType); + info.far_ptr = field->field_ptr; + if (gf_sg_vrml_is_sf_field(info.fieldType)) { + xmt_parse_sf_field(parser, &info, parser->command->node, fieldValue); + } else { + xmt_parse_mf_field(parser, &info, parser->command->node, fieldValue); + } + } else { + parser->state = XMT_STATE_ELEMENTS; + } + return; + } + /*multiple index replace*/ + if (!strcmp(name, "repValue")) { + s32 position = -1; + char *fieldValue = NULL; + assert(parser->command); + if (!parser->command->node) return; + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "position")) { + if (!strcmp(att->value, "BEGIN")) position = 0; + else if (!strcmp(att->value, "END")) position = -1; + else position = atoi(att->value); + } + else if (!strcmp(att->name, "value")) fieldValue = att->value; + } + gf_node_get_field(parser->command->node, parser->command->fromFieldIndex, &info); + if (info.fieldType == GF_SG_VRML_MFNODE) { + field = gf_sg_command_field_new(parser->command); + field->fieldIndex = info.fieldIndex; + field->fieldType = GF_SG_VRML_SFNODE; + field->pos = position; + parser->state = XMT_STATE_ELEMENTS; + } else if (fieldValue) { + field = gf_sg_command_field_new(parser->command); + field->fieldIndex = info.fieldIndex; + field->fieldType = info.fieldType = gf_sg_vrml_get_sf_type(info.fieldType); + field->field_ptr = gf_sg_vrml_field_pointer_new(info.fieldType); + field->pos = position; + info.far_ptr = field->field_ptr; + xmt_parse_sf_field(parser, &info, parser->command->node, fieldValue); + } + return; + } + + + /*BIFS command*/ + if (!strcmp(name, "Replace") || !strcmp(name, "Insert") || !strcmp(name, "Delete")) { + GF_Node *atNode; + u8 tag = GF_SG_UNDEFINED; + u32 stream_id; + Double au_time = parser->au_time; + Bool au_is_rap = parser->au_is_rap; + char *nodeName = NULL; + char *fieldName = NULL; + char *fieldValue = NULL; + char *routeName = NULL; + char *extended = NULL; + s32 position = -2; + + if (!parser->stream_id) parser->stream_id = parser->base_scene_id; + stream_id = parser->stream_id; + + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "begin")) au_time = atoi(att->value); + else if (!strcmp(att->name, "isRAP")) au_is_rap = !strcmp(att->value, "yes") ? 1 : 0; + else if (!strcmp(att->name, "atES_ID")) { + stream_id = xmt_locate_stream(parser, att->value); + if (!stream_id) { + xmt_report(parser, GF_OK, "Warning: Cannot locate command's target stream %s", att->value); + stream_id = parser->stream_id; + } + } + else if (!strcmp(att->name, "atNode")) nodeName = att->value; + else if (!strcmp(att->name, "atField")) fieldName = att->value; + else if (!strcmp(att->name, "value")) fieldValue = att->value; + else if (!strcmp(att->name, "atRoute")) routeName = att->value; + else if (!strcmp(att->name, "extended")) extended = att->value; + else if (!strcmp(att->name, "position")) { + if (!strcmp(att->value, "BEGIN")) position = 0; + else if (!strcmp(att->value, "END")) position = -1; + else position = atoi(att->value); + } + } + + atNode = NULL; + if (nodeName) { + if (fieldName) { + if (position>-2) { + if (!strcmp(name, "Replace")) tag = GF_SG_INDEXED_REPLACE; + else if (!strcmp(name, "Insert")) tag = GF_SG_INDEXED_INSERT; + else if (!strcmp(name, "Delete")) tag = GF_SG_INDEXED_DELETE; + } else { + if (!strcmp(name, "Replace")) tag = GF_SG_FIELD_REPLACE; + } + } else { + if (!strcmp(name, "Replace")) { + tag = GF_SG_NODE_REPLACE; + parser->state = XMT_STATE_ELEMENTS; + } + else if (!strcmp(name, "Insert")) { + tag = GF_SG_NODE_INSERT; + parser->state = XMT_STATE_ELEMENTS; + } + else if (!strcmp(name, "Delete")) tag = GF_SG_NODE_DELETE; + } + + atNode = xmt_find_node(parser, nodeName); + if (!atNode) { + xmt_report(parser, GF_BAD_PARAM, "Warning: Cannot locate node %s for command %s", nodeName, name); + return; + } + if (fieldName) { + e = gf_node_get_field_by_name(atNode, fieldName, &info); + if (e) { + xmt_report(parser, GF_BAD_PARAM, "Warning: Field %s not a member of node %s ", fieldName, nodeName); + return; + } + } + } + else if (routeName) { + if (!strcmp(name, "Replace")) tag = GF_SG_ROUTE_REPLACE; + else if (!strcmp(name, "Delete")) tag = GF_SG_ROUTE_DELETE; + } + else if (!strcmp(name, "Replace")) { + tag = GF_SG_SCENE_REPLACE; + au_is_rap = 1; + gf_list_reset(parser->def_nodes); + } + else if (!strcmp(name, "Insert")) + tag = GF_SG_ROUTE_INSERT; + + if (extended) { + if (!strcmp(extended, "globalQuant")) { + tag = GF_SG_GLOBAL_QUANTIZER; + parser->state = XMT_STATE_ELEMENTS; + } + else if (!strcmp(extended, "fields")) { + tag = GF_SG_MULTIPLE_REPLACE; + parser->state = XMT_STATE_COMMANDS; + } + else if (!strcmp(extended, "indices")) { + tag = GF_SG_MULTIPLE_INDEXED_REPLACE; + parser->state = XMT_STATE_COMMANDS; + } + else if (!strcmp(extended, "deleteOrder")) tag = GF_SG_NODE_DELETE_EX; + else if (!strcmp(extended, "allProtos")) tag = GF_SG_PROTO_DELETE_ALL; + else if (!strcmp(extended, "proto") || !strcmp(extended, "protos")) { + if (!strcmp(name, "Insert")) { + parser->state = XMT_STATE_ELEMENTS; + tag = GF_SG_PROTO_INSERT; + } + else if (!strcmp(name, "Delete")) tag = GF_SG_PROTO_DELETE; + } + else { + xmt_report(parser, GF_BAD_PARAM, "Warning: Unknown extended command %s", extended); + return; + } + + } + + if (tag == GF_SG_UNDEFINED) { + xmt_report(parser, GF_BAD_PARAM, "Warning: Unknown scene command %s", name); + return; + } + + parser->command = gf_sg_command_new(parser->load->scene_graph, tag); + if (parser->command_buffer) { + gf_list_add(parser->command_buffer->commandList, parser->command); + parser->command_buffer->bufferSize++; + } else { + GF_StreamContext *stream = gf_sm_stream_find(parser->load->ctx, (u16) stream_id); + if (!stream || (stream->streamType!=GF_STREAM_SCENE)) stream_id = parser->base_scene_id; + + parser->scene_es = gf_sm_stream_new(parser->load->ctx, (u16) stream_id, GF_STREAM_SCENE, 0); + parser->scene_au = gf_sm_stream_au_new(parser->scene_es, 0, au_time, au_is_rap); + gf_list_add(parser->scene_au->commands, parser->command); + } + + if (atNode) { + parser->command->node = atNode; + gf_node_register(atNode, NULL); + if (tag == GF_SG_MULTIPLE_INDEXED_REPLACE) { + parser->command->fromFieldIndex = info.fieldIndex; + return; + } + if (fieldName) { + field = gf_sg_command_field_new(parser->command); + field->fieldIndex = info.fieldIndex; + if (gf_sg_vrml_get_sf_type(info.fieldType) != GF_SG_VRML_SFNODE) { + if (position==-2) { + field->fieldType = info.fieldType; + field->field_ptr = gf_sg_vrml_field_pointer_new(info.fieldType); + info.far_ptr = field->field_ptr; + if (gf_sg_vrml_is_sf_field(info.fieldType)) { + xmt_parse_sf_field(parser, &info, atNode, fieldValue); + } else { + xmt_parse_mf_field(parser, &info, atNode, fieldValue); + } + } else { + field->fieldType = info.fieldType = gf_sg_vrml_get_sf_type(info.fieldType); + field->pos = position; + if (tag != GF_SG_INDEXED_DELETE) { + field->field_ptr = gf_sg_vrml_field_pointer_new(info.fieldType); + info.far_ptr = field->field_ptr; + xmt_parse_sf_field(parser, &info, atNode, fieldValue); + } + } + } else { + field->pos = position; + if ((position==-2) && (info.fieldType==GF_SG_VRML_MFNODE)) { + field->fieldType = GF_SG_VRML_MFNODE; + } else { + field->fieldType = GF_SG_VRML_SFNODE; + } + parser->state = XMT_STATE_ELEMENTS; + } + } else if (tag==GF_SG_NODE_INSERT) { + field = gf_sg_command_field_new(parser->command); + field->fieldType = GF_SG_VRML_SFNODE; + field->pos = position; + parser->state = XMT_STATE_ELEMENTS; + } + } + else if (routeName) { + u32 rID = xmt_get_route(parser, routeName, 0); + if (!rID) { + parser->command->unres_name = strdup(routeName); + parser->command->unresolved = 1; + gf_list_add(parser->unresolved_routes, parser->command); + } else { + parser->command->RouteID = rID; + /*for bt<->xmt conversions*/ + parser->command->def_name = strdup(routeName); + } + } + else if (tag == GF_SG_PROTO_DELETE) { + char *sep; + while (fieldValue) { + GF_Proto *p; + sep = strchr(fieldValue, ' '); + if (sep) sep[0] = 0; + p = gf_sg_find_proto(parser->load->scene_graph, 0, fieldValue); + if (!p) p = gf_sg_find_proto(parser->load->scene_graph, atoi(fieldValue), NULL); + + if (!p) xmt_report(parser, GF_OK, "Warning: Cannot locate proto %s - skipping", fieldValue); + else { + parser->command->del_proto_list = (u32*)realloc(parser->command->del_proto_list, sizeof(u32) * (parser->command->del_proto_list_size+1)); + parser->command->del_proto_list[parser->command->del_proto_list_size] = p->ID; + parser->command->del_proto_list_size++; + } + if (!sep) break; + sep[0] = ' '; + fieldValue = sep+1; + } + } + + return; + } + + /*OD commands*/ + if (!strcmp(name, "ObjectDescriptorUpdate") || !strcmp(name, "ObjectDescriptorRemove") + || !strcmp(name, "ES_DescriptorUpdate") || !strcmp(name, "ES_DescriptorRemove") + || !strcmp(name, "IPMP_DescriptorUpdate") || !strcmp(name, "IPMP_DescriptorRemove") ) { + u32 stream_id; + u8 tag = 0; + GF_StreamContext *stream; + char *od_ids = NULL; + char *es_ids = NULL; + Double au_time = parser->au_time; + Bool au_is_rap = parser->au_is_rap; + + if (!parser->stream_id) parser->stream_id = parser->base_od_id; + stream_id = parser->stream_id; + + for (i=0; ivalue || !strlen(att->value)) continue; + if (!strcmp(att->name, "begin")) au_time = atoi(att->value); + else if (!strcmp(att->name, "isRAP")) au_is_rap = !strcmp(att->value, "yes") ? 1 : 0; + else if (!stricmp(att->name, "objectDescriptorId")) od_ids = att->value; + else if (!strcmp(att->name, "ES_ID")) es_ids = att->value; + } + + if (!strcmp(name, "ObjectDescriptorUpdate")) tag = GF_ODF_OD_UPDATE_TAG; + else if (!strcmp(name, "ES_DescriptorUpdate")) tag = GF_ODF_ESD_UPDATE_TAG; + else if (!strcmp(name, "IPMP_DescriptorUpdate")) tag = GF_ODF_IPMP_UPDATE_TAG; + else if (!strcmp(name, "ObjectDescriptorRemove")) { + if (!od_ids) return; + tag = GF_ODF_OD_REMOVE_TAG; + } + else if (!strcmp(name, "ES_DescriptorRemove")) { + if (!od_ids || !es_ids) return; + tag = GF_ODF_ESD_REMOVE_TAG; + } + else if (!strcmp(name, "IPMP_DescriptorRemove")) tag = GF_ODF_IPMP_REMOVE_TAG; + + stream = gf_sm_stream_find(parser->load->ctx, (u16) stream_id); + if (!stream || (stream->streamType!=GF_STREAM_OD)) stream_id = parser->base_od_id; + parser->od_es = gf_sm_stream_new(parser->load->ctx, (u16) stream_id, GF_STREAM_OD, 0); + parser->od_au = gf_sm_stream_au_new(parser->od_es, 0, au_time, au_is_rap); + parser->od_command = gf_odf_com_new(tag); + gf_list_add(parser->od_au->commands, parser->od_command); + + if (tag == GF_ODF_ESD_REMOVE_TAG) { + char *sep; + GF_ESDRemove *esdR = (GF_ESDRemove *) parser->od_command ; + esdR->ODID = xmt_get_od_id(parser, od_ids); + while (es_ids) { + u32 es_id; + sep = strchr(es_ids, ' '); + if (sep) sep[0] = 0; + es_id = xmt_get_esd_id(parser, es_ids); + if (!es_id) xmt_report(parser, GF_OK, "Warning: Cannot find ES Descriptor %s - skipping", es_ids); + else { + esdR->ES_ID = (u16*)realloc(esdR->ES_ID, sizeof(u16) * (esdR->NbESDs+1)); + esdR->ES_ID[esdR->NbESDs] = es_id; + esdR->NbESDs++; + } + if (!sep) break; + sep[0] = ' '; + es_ids = sep+1; + } + } + else if (tag == GF_ODF_OD_REMOVE_TAG) { + char *sep; + GF_ODRemove *odR = (GF_ODRemove *) parser->od_command ; + while (od_ids) { + u32 od_id; + sep = strchr(od_ids, ' '); + if (sep) sep[0] = 0; + od_id = xmt_get_od_id(parser, od_ids); + if (!od_id) xmt_report(parser, GF_OK, "Warning: Cannot find Object Descriptor %s - skipping", od_ids); + else { + odR->OD_ID = (u16*)realloc(odR->OD_ID, sizeof(u16) * (odR->NbODs+1)); + odR->OD_ID[odR->NbODs] = od_id; + odR->NbODs++; + } + if (!sep) break; + sep[0] = ' '; + es_ids = sep+1; + } + } + } +} + +static void xmt_node_start(void *sax_cbck, const char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + GF_Node *elt; + XMTNodeStack *top, *new_top; + GF_XMTParser *parser = (GF_XMTParser *)sax_cbck; + + if (parser->last_error) { + gf_xml_sax_suspend(parser->sax_parser, 1); + return; + } + + /*init doc type*/ + if (!parser->doc_type) { + if (!strcmp(name, "XMT-A")) parser->doc_type = 1; + else if (!strcmp(name, "X3D")) { + parser->doc_type = 2; + parser->script_to_load = gf_list_new(); + } + else if (!strcmp(name, "XMT-O")) parser->doc_type = 3; + return; + } + + /*init doc state*/ + if (parser->state == XMT_STATE_INIT) { + /*XMT-A header*/ + if ((parser->doc_type == 1) && !strcmp(name, "Header")) parser->state = XMT_STATE_HEAD; + /*X3D header*/ + else if ((parser->doc_type == 2) && !strcmp(name, "head")) parser->state = XMT_STATE_HEAD; + /*XMT-O header*/ + else if ((parser->doc_type == 3) && !strcmp(name, "head")) parser->state = XMT_STATE_HEAD; + return; + } + + /*XMT-A header: parse OD/IOD*/ + if ((parser->doc_type == 1) && (parser->state == XMT_STATE_HEAD)) { + GF_Descriptor *desc, *par; + par = (GF_Descriptor *)gf_list_last(parser->descriptors); + desc = xmt_parse_descriptor(parser, (char *) name, attributes, nb_attributes, par); + if (desc) gf_list_add(parser->descriptors, desc); + return; + } + + /*scene content*/ + if (parser->state==XMT_STATE_BODY) { + /*XMT-A body*/ + if ((parser->doc_type == 1) && !strcmp(name, "Body")) parser->state = XMT_STATE_COMMANDS; + /*X3D scene*/ + else if ((parser->doc_type == 2) && !strcmp(name, "Scene")) { + parser->state = XMT_STATE_ELEMENTS; + if (parser->load->ctx) { + parser->load->ctx->is_pixel_metrics = 0; + parser->load->ctx->scene_width = parser->load->ctx->scene_height = 0; + } + gf_sg_set_scene_size_info(parser->load->scene_graph, 0, 0, 0); + } + /*XMT-O body*/ + else if ((parser->doc_type == 3) && !strcmp(name, "body")) parser->state = XMT_STATE_COMMANDS; + return; + } + + /*XMT-A command*/ + if ((parser->doc_type == 1) && (parser->state == XMT_STATE_COMMANDS)) { + /*OD command*/ + if (parser->od_command) { + GF_Descriptor *desc, *par; + par = (GF_Descriptor *)gf_list_last(parser->descriptors); + desc = xmt_parse_descriptor(parser, (char *) name, attributes, nb_attributes, par); + if (desc) gf_list_add(parser->descriptors, desc); + } else { + xmt_parse_command(parser, name, attributes, nb_attributes); + } + return; + } + + + /*node*/ + if (parser->state != XMT_STATE_ELEMENTS) return; + + top = (XMTNodeStack*)gf_list_last(parser->nodes); + if (!top) top = parser->x3d_root; + + elt = xmt_parse_element(parser, (char *) name, name_space, attributes, nb_attributes, top); + if (!elt) return; + GF_SAFEALLOC(new_top, XMTNodeStack); + new_top->node = elt; + gf_list_add(parser->nodes, new_top); + + /*assign root node here to enable progressive loading*/ + if (!top && (parser->doc_type == 1) && !parser->parsing_proto && parser->command && (parser->command->tag==GF_SG_SCENE_REPLACE) && !parser->command->node) { + parser->command->node = elt; + gf_node_register(elt, NULL); + } +} + +static void xmt_node_end(void *sax_cbck, const char *name, const char *name_space) +{ + u32 tag; + GF_XMTParser *parser = (GF_XMTParser *)sax_cbck; + XMTNodeStack *top; + GF_Descriptor *desc; + GF_Node *node = NULL; + if (!parser->doc_type || !parser->state) return; + + top = (XMTNodeStack *)gf_list_last(parser->nodes); + + if (!top) { + /*check descr*/ + desc = (GF_Descriptor*)gf_list_last(parser->descriptors); + if (desc && (desc->tag == gf_odf_get_tag_by_name((char *)name)) ) { + + /*assign timescales once the ESD has been parsed*/ + if (desc->tag == GF_ODF_ESD_TAG) { + GF_ESD *esd = (GF_ESD*)desc; + GF_StreamContext *sc = gf_sm_stream_new(parser->load->ctx, esd->ESID, esd->decoderConfig ? esd->decoderConfig->streamType : 0, esd->decoderConfig ? esd->decoderConfig->objectTypeIndication : 0); + if (sc && esd->slConfig && esd->slConfig->timestampResolution) + sc->timeScale = esd->slConfig->timestampResolution; + } + + gf_list_rem_last(parser->descriptors); + if (gf_list_count(parser->descriptors)) return; + + if ((parser->doc_type==1) && (parser->state==XMT_STATE_HEAD) && parser->load->ctx && !parser->load->ctx->root_od) { + parser->load->ctx->root_od = (GF_ObjectDescriptor *)desc; + } + else if (!parser->od_command) { + xmt_report(parser, GF_OK, "Warning: descriptor %s defined outside scene scope - skipping", name); + gf_odf_desc_del(desc); + } else { + switch (parser->od_command->tag) { + case GF_ODF_ESD_UPDATE_TAG: + gf_list_add( ((GF_ESDUpdate *)parser->od_command)->ESDescriptors, desc); + break; + /*same struct for OD update and IPMP update*/ + case GF_ODF_OD_UPDATE_TAG: + case GF_ODF_IPMP_UPDATE_TAG: + gf_list_add( ((GF_ODUpdate *)parser->od_command)->objectDescriptors, desc); + break; + } + } + + return; + } + if (parser->state == XMT_STATE_HEAD) { + if ((parser->doc_type == 1) && !strcmp(name, "Header")) parser->state = XMT_STATE_BODY; + /*X3D header*/ + else if ((parser->doc_type == 2) && !strcmp(name, "head")) { + parser->state = XMT_STATE_BODY; + /*create a group at root level*/ + tag = xmt_get_node_tag(parser, "Group"); + node = gf_node_new(parser->load->scene_graph, tag); + gf_node_register(node, NULL); + gf_sg_set_root_node(parser->load->scene_graph, node); + gf_node_init(node); + + /*create a default top for X3D*/ + GF_SAFEALLOC(parser->x3d_root, XMTNodeStack); + parser->x3d_root->node = node; + } + /*XMT-O header*/ + else if ((parser->doc_type == 3) && !strcmp(name, "head")) parser->state = XMT_STATE_BODY; + } + else if (parser->state == XMT_STATE_ELEMENTS) { + assert((parser->doc_type != 1) || parser->command); + if (!strcmp(name, "Replace") || !strcmp(name, "Insert") || !strcmp(name, "Delete")) { + parser->command = NULL; + parser->state = XMT_STATE_COMMANDS; + } + /*end proto*/ + else if (!strcmp(name, "ProtoDeclare") || !strcmp(name, "ExternProtoDeclare")) { + GF_Proto *cur = parser->parsing_proto; + xmt_resolve_routes(parser); + parser->parsing_proto = (GF_Proto*)cur->userpriv; + parser->load->scene_graph = cur->parent_graph; + cur->userpriv = NULL; + } + else if (parser->proto_field && !strcmp(name, "field")) parser->proto_field = NULL; + /*end X3D body*/ + else if ((parser->doc_type == 2) && !strcmp(name, "Scene")) parser->state = XMT_STATE_BODY_END; + } + else if (parser->state == XMT_STATE_COMMANDS) { + /*end XMT-A body*/ + if ((parser->doc_type == 1) && !strcmp(name, "Body")) parser->state = XMT_STATE_BODY_END; + /*end X3D body*/ + else if ((parser->doc_type == 2) && !strcmp(name, "Scene")) parser->state = XMT_STATE_BODY_END; + /*end XMT-O body*/ + else if ((parser->doc_type == 3) && !strcmp(name, "body")) parser->state = XMT_STATE_BODY_END; + + /*end scene command*/ + else if (!strcmp(name, "Replace") || !strcmp(name, "Insert") || !strcmp(name, "Delete") ) { + /*restore parent command if in CommandBuffer*/ + if (parser->command && parser->command_buffer && parser->command_buffer->buffer) { + parser->command = (GF_Command*) parser->command_buffer->buffer; + parser->command_buffer->buffer = NULL; + parser->command_buffer = NULL; + } else { + parser->command = NULL; + } + } + /*end OD command*/ + else if (!strcmp(name, "ObjectDescriptorUpdate") || !strcmp(name, "ObjectDescriptorRemove") + || !strcmp(name, "ES_DescriptorUpdate") || !strcmp(name, "ES_DescriptorRemove") + || !strcmp(name, "IPMP_DescriptorUpdate") || !strcmp(name, "IPMP_DescriptorRemove") ) { + parser->od_command = NULL; + } + + } + else if (parser->state == XMT_STATE_BODY_END) { + /*end XMT-A*/ + if ((parser->doc_type == 1) && !strcmp(name, "XMT-A")) parser->state = XMT_STATE_END; + /*end X3D*/ + else if ((parser->doc_type == 2) && !strcmp(name, "X3D")) { + while (1) { + GF_Node *n = (GF_Node *)gf_list_last(parser->script_to_load); + if (!n) break; + gf_list_rem_last(parser->script_to_load); + gf_sg_script_load(n); + } + gf_list_del(parser->script_to_load); + parser->script_to_load = NULL; + parser->state = XMT_STATE_END; + } + /*end XMT-O*/ + else if ((parser->doc_type == 3) && !strcmp(name, "XMT-O")) parser->state = XMT_STATE_END; + } + return; + } + /*only remove created nodes ... */ + tag = xmt_get_node_tag(parser, name); + if (!tag) { + if (top->container_field.name) { + if (!strcmp(name, top->container_field.name)) { + if (top->container_field.fieldType==GF_SG_VRML_SFCOMMANDBUFFER) { + parser->state = XMT_STATE_ELEMENTS; + parser->command = (GF_Command *) (void *) parser->command_buffer->buffer; + parser->command_buffer->buffer = NULL; + parser->command_buffer = NULL; + } + top->container_field.far_ptr = NULL; + top->container_field.name = NULL; + top->last = NULL; + } + /*end of command inside an command (conditional.buffer replace)*/ + else if (!strcmp(name, "Replace") || !strcmp(name, "Insert") || !strcmp(name, "Delete") ) { + if (parser->command_buffer) { + if (parser->command_buffer->bufferSize) { + parser->command_buffer->bufferSize--; + } else { + SFCommandBuffer *prev = (SFCommandBuffer *) parser->command_buffer->buffer; + parser->command_buffer->buffer = NULL; + parser->command_buffer = prev; + } + /*stay in command parsing mode (state 3) until we find */ + parser->state = XMT_STATE_COMMANDS; + } + } + /*end of protofield node(s) content*/ + else if (!strcmp(name, "node") || !strcmp(name, "nodes")) { + top->container_field.far_ptr = NULL; + top->container_field.name = NULL; + top->last = NULL; + } + } + /*SF/MFNode proto field, just pop node stack*/ + else if (!top->node && !strcmp(name, "field")) { + gf_list_rem_last(parser->nodes); + free(top); + } else if (top->node && top->node->sgprivate->tag == TAG_ProtoNode) { + if (!strcmp(name, "node") || !strcmp(name, "nodes")) { + top->container_field.far_ptr = NULL; + top->container_field.name = NULL; + top->last = NULL; + } else if (!strcmp(name, "ProtoInstance")) { + gf_list_rem_last(parser->nodes); + node = top->node; + free(top); + goto attach_node; + } + } + } else if (top->node->sgprivate->tag==tag) { + node = top->node; + gf_list_rem_last(parser->nodes); + free(top); + +attach_node: + top = (XMTNodeStack*)gf_list_last(parser->nodes); + /*add node to command*/ + if (!top || (top->container_field.fieldType==GF_SG_VRML_SFCOMMANDBUFFER)) { + if (parser->doc_type == 1) { + GF_CommandField *inf; + Bool single_node = 0; + assert(parser->command); + switch (parser->command->tag) { + case GF_SG_SCENE_REPLACE: + if (parser->parsing_proto) { + gf_sg_proto_add_node_code(parser->parsing_proto, node); + gf_node_register(node, NULL); + } else if (!parser->command->node) { + parser->command->node = node; + gf_node_register(node, NULL); + } else if (parser->command->node != node) { + xmt_report(parser, GF_OK, "Warning: top-node already assigned - discarding node %s", name); + gf_node_register(node, NULL); + gf_node_unregister(node, NULL); + } + break; + case GF_SG_GLOBAL_QUANTIZER: + case GF_SG_NODE_INSERT: + case GF_SG_INDEXED_INSERT: + case GF_SG_INDEXED_REPLACE: + single_node = 1; + case GF_SG_NODE_REPLACE: + case GF_SG_FIELD_REPLACE: + case GF_SG_MULTIPLE_REPLACE: + inf = (GF_CommandField*)gf_list_last(parser->command->command_fields); + if (!inf) { + inf = gf_sg_command_field_new(parser->command); + inf->fieldType = GF_SG_VRML_SFNODE; + } + if ((inf->fieldType==GF_SG_VRML_MFNODE) && !inf->node_list) { + inf->field_ptr = &inf->node_list; + if (inf->new_node) { + gf_node_list_add_child(& inf->node_list, inf->new_node); + inf->new_node = NULL; + } + } + + if (inf->new_node) { + if (single_node) { + gf_node_unregister(inf->new_node, NULL); + } else { + inf->field_ptr = &inf->node_list; + gf_node_list_add_child(& inf->node_list, inf->new_node); + inf->fieldType = GF_SG_VRML_MFNODE; + } + inf->new_node = NULL; + } + gf_node_register(node, NULL); + if (inf->node_list) { + gf_node_list_add_child(& inf->node_list, node); + } else { + inf->new_node = node; + inf->field_ptr = &inf->new_node; + } + break; + case GF_SG_PROTO_INSERT: + if (parser->parsing_proto) { + gf_sg_proto_add_node_code(parser->parsing_proto, node); + gf_node_register(node, NULL); + break; + } + default: + xmt_report(parser, GF_OK, "Warning: node %s defined outside scene scope - skipping", name); + gf_node_register(node, NULL); + gf_node_unregister(node, NULL); + break; + + } + } + /*X3D*/ + else if (parser->doc_type == 2) { + if (parser->parsing_proto) { + gf_sg_proto_add_node_code(parser->parsing_proto, node); + gf_node_register(node, NULL); + } else { + M_Group *gr = (M_Group *)gf_sg_get_root_node(parser->load->scene_graph); + if (!gr) { + xmt_report(parser, GF_OK, "Warning: node %s defined outside scene scope - skipping", name); + gf_node_register(node, NULL); + gf_node_unregister(node, NULL); + } else { + gf_node_list_add_child(& gr->children, node); + gf_node_register(node, NULL); + } + } + } + /*special case: replace scene has already been applied (progressive loading)*/ + else if ((parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK) && (parser->load->scene_graph->RootNode!=node) ) { + gf_node_register(node, NULL); + } else { + xmt_report(parser, GF_OK, "Warning: node %s defined outside scene scope - skipping", name); + gf_node_register(node, NULL); + gf_node_unregister(node, NULL); + } + } + if (parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK) { + /*load scripts*/ + if (!parser->parsing_proto) { + if ((tag==TAG_MPEG4_Script) || (tag==TAG_X3D_Script) ) { + /*it may happen that the script uses itself as a field (not sure this is compliant since this + implies a cyclic structure, but happens in some X3D conformance seq)*/ + if (!top || (top->node != node)) { + if (parser->command) { + if (!parser->command->scripts_to_load) parser->command->scripts_to_load = gf_list_new(); + gf_list_add(parser->command->scripts_to_load, node); + } + /*do not load script until all routes are established!!*/ + else if (parser->doc_type==2) { + gf_list_add(parser->script_to_load, node); + } else { + gf_sg_script_load(node); + } + } + } + } + } + } else if (parser->current_node_tag==tag) { + gf_list_rem_last(parser->nodes); + free(top); + } else { + xmt_report(parser, GF_OK, "Warning: closing element %s doesn't match created node %s", name, gf_node_get_class_name(top->node) ); + } +} + +static void xmt_text_content(void *sax_cbck, const char *text_content, Bool is_cdata) +{ + const char *buf; + u32 len; + GF_XMTParser *parser = (GF_XMTParser *)sax_cbck; + GF_Node *node; + XMTNodeStack *top = (XMTNodeStack *)gf_list_last(parser->nodes); + if (!top || !top->node) return; + + node = top->node; + + buf = text_content; + len = strlen(buf); + + if (!len) return; + + switch (gf_node_get_tag((GF_Node *)node)) { + case TAG_MPEG4_Script: + case TAG_X3D_Script: + if (is_cdata) { + SFScript *sc_f; + M_Script *sc = (M_Script *) node; + gf_sg_vrml_mf_reset(& sc->url, GF_SG_VRML_MFSCRIPT); + gf_sg_vrml_mf_append(& sc->url, GF_SG_VRML_MFSCRIPT, (void **) &sc_f); + sc->url.vals[0].script_text = (unsigned char*)strdup(text_content); + } + break; + default: + break; + } +} + + +static GF_XMTParser *xmt_new_parser(GF_SceneLoader *load) +{ + GF_XMTParser *parser; + if ((load->type==GF_SM_LOAD_XSR) && !load->ctx) return NULL; + GF_SAFEALLOC(parser, GF_XMTParser); + parser->nodes = gf_list_new(); + parser->descriptors = gf_list_new(); + parser->od_links = gf_list_new(); + parser->esd_links = gf_list_new(); + parser->def_nodes = gf_list_new(); + parser->peeked_nodes = gf_list_new(); + parser->inserted_routes = gf_list_new(); + parser->unresolved_routes = gf_list_new(); + + parser->sax_parser = gf_xml_sax_new(xmt_node_start, xmt_node_end, xmt_text_content, parser); + parser->load = load; + load->loader_priv = parser; + if (load->ctx) load->ctx->is_pixel_metrics = 1; + + return parser; +} + +GF_Err gf_sm_load_init_xmt(GF_SceneLoader *load) +{ + GF_Err e; + GF_XMTParser *parser; + + if (!load->fileName) return GF_BAD_PARAM; + parser = xmt_new_parser(load); + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("XMT: MPEG-4 (XMT) Scene Parsing\n")); + e = gf_xml_sax_parse_file(parser->sax_parser, (const char *)load->fileName, xmt_progress); + if (e<0) xmt_report(parser, e, "Invalid XML document\n", gf_xml_sax_get_error(parser->sax_parser)); + return parser->last_error; +} + +GF_Err gf_sm_load_init_xmt_string(GF_SceneLoader *load, char *str_data) +{ + GF_Err e; + GF_XMTParser *parser = (GF_XMTParser *)load->loader_priv; + + if (!parser) { + char BOM[5]; + if (strlen(str_data)<4) return GF_BAD_PARAM; + BOM[0] = str_data[0]; + BOM[1] = str_data[1]; + BOM[2] = str_data[2]; + BOM[3] = str_data[3]; + BOM[4] = 0; + parser = xmt_new_parser(load); + e = gf_xml_sax_init(parser->sax_parser, (unsigned char*)BOM); + if (e) { + xmt_report(parser, e, "Error initializing SAX parser"); + return e; + } + str_data += 4; + + if (load->flags & GF_SM_LOAD_CONTEXT_READY) { + parser->doc_type = (load->type==GF_SM_LOAD_X3D) ? 2 : 1; + parser->state = XMT_STATE_COMMANDS; + } + } + e = gf_xml_sax_parse(parser->sax_parser, str_data); + if (e<0) return xmt_report(parser, e, "Invalid XML document: %s", gf_xml_sax_get_error(parser->sax_parser)); + return GF_OK; +} + + +GF_Err gf_sm_load_run_xmt(GF_SceneLoader *load) +{ + return GF_OK; +} + +GF_Err gf_sm_load_done_xmt(GF_SceneLoader *load) +{ + GF_XMTParser *parser = (GF_XMTParser *)load->loader_priv; + if (!parser) return GF_OK; + while (1) { + XMTNodeStack *st = (XMTNodeStack *)gf_list_last(parser->nodes); + if (!st) break; + gf_list_rem_last(parser->nodes); + gf_node_register(st->node, NULL); + gf_node_unregister(st->node, NULL); + free(st); + } + if (parser->x3d_root) free(parser->x3d_root); + gf_list_del(parser->nodes); + gf_list_del(parser->descriptors); + gf_list_del(parser->def_nodes); + gf_list_del(parser->peeked_nodes); + xmt_resolve_routes(parser); + gf_list_del(parser->inserted_routes); + gf_list_del(parser->unresolved_routes); + xmt_resolve_od_links(parser); + gf_list_del(parser->od_links); + gf_list_del(parser->esd_links); + gf_xml_sax_del(parser->sax_parser); + if (parser->script_to_load) gf_list_del(parser->script_to_load); + free(parser); + load->loader_priv = NULL; + return GF_OK; +} + + diff --git a/src/scene_manager/scene_dump.c b/src/scene_manager/scene_dump.c new file mode 100644 index 0000000..ebca7a2 --- /dev/null +++ b/src/scene_manager/scene_dump.c @@ -0,0 +1,3187 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +//#ifndef GPAC_READ_ONLY + +#ifndef __SYMBIAN32__ +#include +#endif + + +/*for QP types*/ +#include "../bifs/quant.h" + +struct _scenedump +{ + /*the scene we're dumping - set at each SceneReplace or mannually*/ + GF_SceneGraph *sg; + /*the proto we're dumping*/ + GF_Proto *current_proto; + FILE *trace; + u32 indent; + + u32 dump_mode; + u16 CurrentESID; + u8 ind_char; + Bool XMLDump, X3DDump, LSRDump; + + GF_List *dump_nodes; + + /*nodes created through conditionals while parsing but not applied*/ + GF_List *mem_def_nodes; + + Bool skip_scene_replace; + /*for route insert/replace in conditionals in current scene replace*/ + GF_List *current_com_list; + GF_List *inserted_routes; + + Bool in_text; +}; + + +GF_Err DumpRoute(GF_SceneDumper *sdump, GF_Route *r, u32 dump_type); +void DumpNode(GF_SceneDumper *sdump, GF_Node *node, Bool in_list, char *fieldContainer); + +#ifndef GPAC_DISABLE_SVG +void SD_DumpSVG_Element(GF_SceneDumper *sdump, GF_Node *n, GF_Node *parent, Bool is_root); +#endif + +GF_Err gf_sm_dump_command_list(GF_SceneDumper *sdump, GF_List *comList, u32 indent, Bool skip_first_replace); + +GF_EXPORT +GF_SceneDumper *gf_sm_dumper_new(GF_SceneGraph *graph, char *_rad_name, char indent_char, u32 dump_mode) +{ + char rad_name[GF_MAX_PATH]; + GF_SceneDumper *tmp; + if (!graph) return NULL; + GF_SAFEALLOC(tmp, GF_SceneDumper); + + strcpy(rad_name, _rad_name ? _rad_name : ""); + /*store original*/ + tmp->dump_mode = dump_mode; + +#ifndef GPAC_DISABLE_SVG + if ((graph->RootNode && (graph->RootNode->sgprivate->tag>=GF_NODE_RANGE_LAST_VRML) ) + || (dump_mode==GF_SM_DUMP_LASER) || (dump_mode==GF_SM_DUMP_SVG)) { + tmp->XMLDump = 1; + if (dump_mode==GF_SM_DUMP_LASER) tmp->LSRDump = 1; + if (_rad_name) { + strcat(rad_name, tmp->LSRDump ? ".xsr" : ".svg"); + + tmp->trace = fopen(rad_name, "wt"); + if (!tmp->trace) { + free(tmp); + return NULL; + } + } else { + tmp->trace = stdout; + } + } else +#endif + { + + if (dump_mode==GF_SM_DUMP_AUTO_TXT) { + if (!graph->RootNode || (graph->RootNode->sgprivate->tag<=GF_NODE_RANGE_LAST_MPEG4) ) { + dump_mode = GF_SM_DUMP_BT; + } else if (graph->RootNode->sgprivate->tag<=GF_NODE_RANGE_LAST_X3D) { + dump_mode = GF_SM_DUMP_X3D_VRML; + } + } + else if (dump_mode==GF_SM_DUMP_AUTO_XML) { + if (!graph->RootNode || (graph->RootNode->sgprivate->tag<=GF_NODE_RANGE_LAST_MPEG4) ) { + dump_mode = GF_SM_DUMP_XMTA; + } else { + dump_mode = GF_SM_DUMP_X3D_XML; + } + } + + if (_rad_name) { + switch (dump_mode) { + case GF_SM_DUMP_X3D_XML: strcat(rad_name, ".x3d"); tmp->XMLDump = 1; tmp->X3DDump = 1; break; + case GF_SM_DUMP_XMTA: strcat(rad_name, ".xmt"); tmp->XMLDump = 1; break; + case GF_SM_DUMP_X3D_VRML: strcat(rad_name, ".x3dv"); tmp->X3DDump = 1; break; + case GF_SM_DUMP_VRML: strcat(rad_name, ".wrl"); break; + default: strcat(rad_name, ".bt"); break; + } + tmp->trace = fopen(rad_name, "wt"); + if (!tmp->trace) { + free(tmp); + return NULL; + } + } else { + tmp->trace = stdout; + switch (dump_mode) { + case GF_SM_DUMP_X3D_XML: tmp->XMLDump = 1; tmp->X3DDump = 1; break; + case GF_SM_DUMP_XMTA: tmp->XMLDump = 1; break; + case GF_SM_DUMP_X3D_VRML: tmp->X3DDump = 1; break; + default: break; + } + } + } + tmp->ind_char = indent_char; + tmp->dump_nodes = gf_list_new(); + tmp->mem_def_nodes = gf_list_new(); + tmp->inserted_routes = gf_list_new(); + tmp->sg = graph; + return tmp; +} + +GF_EXPORT +void gf_sm_dumper_del(GF_SceneDumper *sdump) +{ + gf_list_del(sdump->dump_nodes); + while (gf_list_count(sdump->mem_def_nodes)) { + GF_Node *tmp = (GF_Node *)gf_list_get(sdump->mem_def_nodes, 0); + gf_list_rem(sdump->mem_def_nodes, 0); + gf_node_unregister(tmp, NULL); + } + gf_list_del(sdump->mem_def_nodes); + gf_list_del(sdump->inserted_routes); + if (sdump->trace != stdout) fclose(sdump->trace); + free(sdump); +} + + +void SD_SetupDump(GF_SceneDumper *sdump, GF_Descriptor *root_od) +{ + if (sdump->XMLDump) { + fprintf(sdump->trace, "\n"); + if (sdump->dump_mode==GF_SM_DUMP_XML) return; + fprintf(sdump->trace, "\n", + (sdump->dump_mode==GF_SM_DUMP_SVG) ? "SVG" : + (sdump->dump_mode==GF_SM_DUMP_LASER) ? "LASeR" : + sdump->X3DDump ? "X3D" : "XMT-A" + ); + } + if (sdump->dump_mode==GF_SM_DUMP_SVG) return; + if (sdump->LSRDump) { + fprintf(sdump->trace, "\n"); +#ifndef GPAC_READ_ONLY + if (root_od) { + GF_ObjectDescriptor *iod = (GF_ObjectDescriptor *)root_od; + u32 i, count; + fprintf(sdump->trace, "\n"); + count = gf_list_count(iod->ESDescriptors); + for (i=0; iESDescriptors, i); + if (esd->decoderConfig->streamType != GF_STREAM_SCENE) continue; + if (esd->decoderConfig->objectTypeIndication != 0x09) continue; + if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->data) continue; + gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &lsrcfg); + gf_odf_dump_desc(&lsrcfg, sdump->trace, 1, 1); + } + fprintf(sdump->trace, "\n"); + } +#endif + return; + } + + if (!sdump->X3DDump) { + /*setup XMT*/ + if (sdump->XMLDump) { + fprintf(sdump->trace, "\n"); + fprintf(sdump->trace, "
    \n"); +#ifndef GPAC_READ_ONLY + if (root_od) gf_odf_dump_desc(root_od, sdump->trace, 1, 1); +#endif + fprintf(sdump->trace, "
    \n"); + fprintf(sdump->trace, "
    \n"); + if (!root_od) { + fprintf(sdump->trace, " \n"); + } + } else { + if (sdump->dump_mode==GF_SM_DUMP_VRML) { + fprintf(sdump->trace, "#VRML V2.0\n"); + } else { + /*dump root OD*/ +#ifndef GPAC_READ_ONLY + if (root_od) gf_odf_dump_desc(root_od, sdump->trace, 0, 0); +#endif + } + fprintf(sdump->trace, "\n"); + } + } else { + if (sdump->XMLDump) { + fprintf(sdump->trace, "\n"); + fprintf(sdump->trace, "\n"); + fprintf(sdump->trace, "\n"); + fprintf(sdump->trace, "\n", GPAC_FULL_VERSION); + fprintf(sdump->trace, "\n"); + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "#X3D V3.0\n\n"); + } + } +} + +void SD_FinalizeDump(GF_SceneDumper *sdump, GF_Descriptor *root_od) +{ + if (sdump->dump_mode==GF_SM_DUMP_SVG) return; + + if (sdump->LSRDump) { + fprintf(sdump->trace, "\n\n"); + return; + } + if (!sdump->XMLDump) return; + + if (!sdump->X3DDump) { + if (!root_od) { + fprintf(sdump->trace, " \n"); + } + fprintf(sdump->trace, " \n"); + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "\n"); + fprintf(sdump->trace, "\n"); + } +} + +Bool SD_IsDEFNode(GF_SceneDumper *sdump, GF_Node *node) +{ + s32 i = gf_list_find(sdump->dump_nodes, node); + if (i>=0) return 0; + gf_list_add(sdump->dump_nodes, node); + return 1; +} + +GF_Node *SD_FindNode(GF_SceneDumper *sdump, u32 ID) +{ + GF_Node *ret = gf_sg_find_node(sdump->sg, ID); + if (ret) return ret; + return NULL; +} + +#define DUMP_IND(sdump) \ + if (sdump->trace) { \ + u32 z; \ + for (z=0; zindent; z++) fprintf(sdump->trace, "%c", sdump->ind_char); \ + } + + +void StartElement(GF_SceneDumper *sdump, const char *name) +{ + if (!sdump->trace) return; + DUMP_IND(sdump); + if (!sdump->XMLDump) { + fprintf(sdump->trace, "%s {\n", name); + } else if (sdump->XMLDump) { + fprintf(sdump->trace, "<%s", name); + } +} +void EndElementHeader(GF_SceneDumper *sdump, Bool has_sub_el) +{ + if (!sdump->trace) return; + if (sdump->XMLDump) { + if (has_sub_el) { + fprintf(sdump->trace, ">\n"); + } else { + fprintf(sdump->trace, "/>\n"); + } + } +} +void EndElement(GF_SceneDumper *sdump, const char *name, Bool had_sub_el) +{ + if (!sdump->trace) return; + if (!sdump->XMLDump) { + DUMP_IND(sdump); + fprintf(sdump->trace, "}\n"); + } else { + if (had_sub_el) { + DUMP_IND(sdump); + fprintf(sdump->trace, "\n", name); + } + } +} + +void StartAttribute(GF_SceneDumper *sdump, const char *name) +{ + if (!sdump->trace) return; + if (!sdump->XMLDump) { + DUMP_IND(sdump); + fprintf(sdump->trace, "%s ", name); + } else { + fprintf(sdump->trace, " %s=\"", name); + } +} + +void EndAttribute(GF_SceneDumper *sdump) +{ + if (!sdump->trace) return; + if (!sdump->XMLDump) { + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "\""); + } +} + + +void StartList(GF_SceneDumper *sdump, const char *name) +{ + if (!sdump->trace) return; + DUMP_IND(sdump); + if (!sdump->XMLDump) { + if (name) + fprintf(sdump->trace, "%s [\n", name); + else + fprintf(sdump->trace, "[\n"); + } else { + fprintf(sdump->trace, "<%s>\n", name); + } +} + +void EndList(GF_SceneDumper *sdump, const char *name) +{ + if (!sdump->trace) return; + DUMP_IND(sdump); + if (!sdump->XMLDump) { + fprintf(sdump->trace, "]\n"); + } else { + fprintf(sdump->trace, "\n", name); + } +} + +void DumpNodeID(GF_SceneDumper *sdump, GF_Node *node) +{ + u32 id; + const char *node_name; + if (!sdump->trace) return; + /*FIXME - optimize id/name fetch*/ + node_name = gf_node_get_name_and_id(node, &id); + if (node_name) + fprintf(sdump->trace, "%s", node_name); + else + fprintf(sdump->trace, "N%d", id - 1); +} + +Bool DumpFindRouteName(GF_SceneDumper *sdump, u32 ID, const char **outName) +{ + GF_Route *r; + u32 i; + GF_Command *com; + r = gf_sg_route_find(sdump->sg, ID); + if (r) { (*outName) = r->name; return 1; } + + i=0; + while ((com = (GF_Command *)gf_list_enum(sdump->inserted_routes, &i))) { + if ((com->tag == GF_SG_ROUTE_INSERT)) { + if (com->RouteID==ID) { + (*outName) = com->def_name; + return 1; + } + } + } + if (!sdump->current_com_list) return 0; + i=1; + while ((com = (GF_Command *)gf_list_enum(sdump->current_com_list, &i))) { + if ((com->tag == GF_SG_ROUTE_INSERT) || (com->tag == GF_SG_ROUTE_REPLACE)) { + if (com->RouteID==ID) { + (*outName) = com->def_name; + return 1; + } + } else return 0; + } + return 0; +} + +void DumpRouteID(GF_SceneDumper *sdump, u32 routeID, char *rName) +{ + if (!sdump->trace) return; + if (!rName) DumpFindRouteName(sdump, routeID, (const char **) &rName); + + if (rName) + fprintf(sdump->trace, "%s", rName); + else + fprintf(sdump->trace, "R%d", routeID - 1); +} + +void DumpBool(GF_SceneDumper *sdump, char *name, u32 value) +{ + if (!sdump->trace) return; + StartAttribute(sdump, name); + fprintf(sdump->trace, "%s", value ? "true" : "false"); + EndAttribute(sdump); +} + +void DumpUTFString(GF_SceneDumper *sdump, Bool escape_xml, char *str) +{ + u32 len, i; + u16 *uniLine; + if (!str) return; + len = strlen(str); + if (!len) return; + uniLine = (u16*)malloc(sizeof(u16) * len); + len = gf_utf8_mbstowcs(uniLine, len, (const char **) &str); + if (len != (size_t) (-1)) { + for (i=0; itrace, "\\"); + switch (uniLine[i]) { + case '\'': + if (escape_xml) fprintf(sdump->trace, "'"); + else fprintf(sdump->trace, "'"); + break; + case '\"': + if (escape_xml) fprintf(sdump->trace, """); + else fprintf(sdump->trace, "\""); + break; + case '&': + fprintf(sdump->trace, "&"); + break; + case '>': + fprintf(sdump->trace, ">"); + break; + case '<': + fprintf(sdump->trace, "<"); + break; + case '\r': fprintf(sdump->trace, ""); break; + case '\n': fprintf(sdump->trace, ""); break; + default: + if (uniLine[i]<128) { + fprintf(sdump->trace, "%c", (u8) uniLine[i]); + } else { + fprintf(sdump->trace, "&#%d;", uniLine[i]); + } + break; + } + } + } + free(uniLine); +} + + +void DumpSFField(GF_SceneDumper *sdump, u32 type, void *ptr, Bool is_mf) +{ + switch (type) { + case GF_SG_VRML_SFBOOL: + fprintf(sdump->trace, "%s", * ((SFBool *)ptr) ? "TRUE" : "FALSE"); + break; + case GF_SG_VRML_SFINT32: + fprintf(sdump->trace, "%d", * ((SFInt32 *)ptr) ); + break; + case GF_SG_VRML_SFFLOAT: + fprintf(sdump->trace, "%g", FIX2FLT( * ((SFFloat *)ptr) ) ); + break; + case GF_SG_VRML_SFDOUBLE: + fprintf(sdump->trace, "%g", * ((SFDouble *)ptr) ); + break; + case GF_SG_VRML_SFTIME: + fprintf(sdump->trace, "%g", * ((SFTime *)ptr) ); + break; + case GF_SG_VRML_SFCOLOR: + fprintf(sdump->trace, "%g %g %g", FIX2FLT( ((SFColor *)ptr)->red ), FIX2FLT( ((SFColor *)ptr)->green ), FIX2FLT( ((SFColor *)ptr)->blue )); + break; + case GF_SG_VRML_SFCOLORRGBA: + fprintf(sdump->trace, "%g %g %g %g", FIX2FLT( ((SFColorRGBA *)ptr)->red ), FIX2FLT( ((SFColorRGBA *)ptr)->green ), FIX2FLT( ((SFColorRGBA *)ptr)->blue ), FIX2FLT( ((SFColorRGBA *)ptr)->alpha )); + break; + case GF_SG_VRML_SFVEC2F: + fprintf(sdump->trace, "%g %g", FIX2FLT( ((SFVec2f *)ptr)->x ), FIX2FLT( ((SFVec2f *)ptr)->y )); + break; + case GF_SG_VRML_SFVEC2D: + fprintf(sdump->trace, "%g %g", ((SFVec2d *)ptr)->x, ((SFVec2d *)ptr)->y); + break; + case GF_SG_VRML_SFVEC3F: + fprintf(sdump->trace, "%g %g %g", FIX2FLT( ((SFVec3f *)ptr)->x ), FIX2FLT( ((SFVec3f *)ptr)->y ), FIX2FLT( ((SFVec3f *)ptr)->z )); + break; + case GF_SG_VRML_SFVEC3D: + fprintf(sdump->trace, "%g %g %g", ((SFVec3d *)ptr)->x, ((SFVec3d *)ptr)->y, ((SFVec3d *)ptr)->z); + break; + case GF_SG_VRML_SFROTATION: + fprintf(sdump->trace, "%g %g %g %g", FIX2FLT( ((SFRotation *)ptr)->x ), FIX2FLT( ((SFRotation *)ptr)->y ), FIX2FLT( ((SFRotation *)ptr)->z ), FIX2FLT( ((SFRotation *)ptr)->q ) ); + break; + + case GF_SG_VRML_SFSCRIPT: + { + u32 len, i; + char *str; + u16 *uniLine; + str = (char*)((SFScript *)ptr)->script_text; + len = strlen(str); + uniLine = (u16*)malloc(sizeof(short) * len); + len = gf_utf8_mbstowcs(uniLine, len, (const char **) &str); + if (len != (size_t) -1) { + if (!sdump->XMLDump) fputc('\"', sdump->trace); + + for (i=0; iXMLDump) { +#ifndef __SYMBIAN32__ + fputwc(uniLine[i], sdump->trace); +#else + fputc(uniLine[i], sdump->trace); +#endif + } else { + switch (uniLine[i]) { + case '&': fprintf(sdump->trace, "&"); break; + case '<': fprintf(sdump->trace, "<"); break; + case '>': fprintf(sdump->trace, ">"); break; + case '\'': + case '"': + fprintf(sdump->trace, "'"); + break; + case 0: + break; + /*FIXME: how the heck can we preserve newlines and spaces of JavaScript in + an XML attribute in any viewer ? */ + default: + if (uniLine[i]<128) { + fprintf(sdump->trace, "%c", (u8) uniLine[i]); + } else { + fprintf(sdump->trace, "&#%d;", uniLine[i]); + } + break; + } + } + } + if (!sdump->XMLDump) fprintf(sdump->trace, "\"\n"); + } + free(uniLine); + DUMP_IND(sdump); + } + break; + + case GF_SG_VRML_SFSTRING: + { + char *str; + if (sdump->XMLDump) { + if (is_mf) fprintf(sdump->trace, sdump->X3DDump ? "\"" : """); + } else { + fprintf(sdump->trace, "\""); + } + /*dump in unicode*/ + str = ((SFString *)ptr)->buffer; + if (str && str[0]) { + if (sdump->XMLDump) { + DumpUTFString(sdump, 1, str); + } else if (!strchr(str, '\"')) { + fprintf(sdump->trace, "%s", str); + } else { + u32 i, len = strlen(str); + for (i=0; itrace); + fputc(str[i], sdump->trace); + } + } + } + + if (sdump->XMLDump) { + if (is_mf) fprintf(sdump->trace, sdump->X3DDump ? "\"" : """); + } else { + fprintf(sdump->trace, "\""); + } + } + break; + + case GF_SG_VRML_SFURL: + if (((SFURL *)ptr)->url) { +#if 0 + u32 len; + char *str; + short uniLine[5000]; + str = ((SFURL *)ptr)->url; + len = gf_utf8_mbstowcs(uniLine, 5000, (const char **) &str); + if (len != (size_t) -1) { + fprintf(sdump->trace, sdump->XMLDump ? (sdump->X3DDump ? "'" : """) : "\""); + fwprintf(sdump->trace, (unsigned short *) uniLine); + fprintf(sdump->trace, sdump->XMLDump ? (sdump->X3DDump ? "'" : """) : "\""); + } +#else + fprintf(sdump->trace, sdump->XMLDump ? (sdump->X3DDump ? "'" : """) : "\""); + fprintf(sdump->trace, ((SFURL *)ptr)->url); + fprintf(sdump->trace, sdump->XMLDump ? (sdump->X3DDump ? "'" : """) : "\""); +#endif + } else { + if (sdump->XMLDump) { + fprintf(sdump->trace, ""od://od%d"", ((SFURL *)ptr)->OD_ID); + } else { + fprintf(sdump->trace, "od:%d", ((SFURL *)ptr)->OD_ID); + } + } + break; + case GF_SG_VRML_SFIMAGE: + { + u32 i, count; + SFImage *img = (SFImage *)ptr; + fprintf(sdump->trace, "%d %d %d", img->width, img->height, img->numComponents); + count = img->width * img->height * img->numComponents; + for (i=0; inumComponents) { + case 1: + fprintf(sdump->trace, " 0x%02X", img->pixels[i]); + i++; + break; + case 2: + fprintf(sdump->trace, " 0x%02X%02X", img->pixels[i], img->pixels[i+1]); + i+=2; + break; + case 3: + fprintf(sdump->trace, " 0x%02X%02X%02X", img->pixels[i], img->pixels[i+1], img->pixels[i+2]); + i+=3; + break; + case 4: + fprintf(sdump->trace, " 0x%02X%02X%02X%02X", img->pixels[i], img->pixels[i+1], img->pixels[i+2], img->pixels[i+3]); + i+=4; + break; + } + } + } + break; + } +} + + +void DumpFieldValue(GF_SceneDumper *sdump, GF_FieldInfo field) +{ + GenMFField *mffield; + u32 i, sf_type; + GF_ChildNodeItem *list; + void *slot_ptr; + + switch (field.fieldType) { + case GF_SG_VRML_SFNODE: + assert ( *(GF_Node **)field.far_ptr); + DumpNode(sdump, *(GF_Node **)field.far_ptr, 0, NULL); + return; + case GF_SG_VRML_MFNODE: + list = * ((GF_ChildNodeItem **) field.far_ptr); + assert( list ); + sdump->indent++; + while (list) { + DumpNode(sdump, list->node, 1, NULL); + list = list->next; + } + sdump->indent--; + return; + case GF_SG_VRML_SFCOMMANDBUFFER: + { + } + return; + } + if (gf_sg_vrml_is_sf_field(field.fieldType)) { + if (sdump->XMLDump) StartAttribute(sdump, "value"); + DumpSFField(sdump, field.fieldType, field.far_ptr, 0); + if (sdump->XMLDump) EndAttribute(sdump); + } else { + mffield = (GenMFField *) field.far_ptr; + sf_type = gf_sg_vrml_get_sf_type(field.fieldType); + if (!sdump->XMLDump) { + fprintf(sdump->trace, "["); + } else if (sf_type==GF_SG_VRML_SFSTRING) { + fprintf(sdump->trace, " value=\'"); + } else { + StartAttribute(sdump, "value"); + } + for (i=0; icount; i++) { + if (i) fprintf(sdump->trace, " "); + gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &slot_ptr, i); + /*this is to cope with single MFString which shall appear as SF in XMT*/ + DumpSFField(sdump, sf_type, slot_ptr, 1); + } + if (!sdump->XMLDump) { + fprintf(sdump->trace, "]"); + } else if (sf_type==GF_SG_VRML_SFSTRING) { + fprintf(sdump->trace, "\'"); + } else { + EndAttribute(sdump); + } + } +} + +Bool SD_NeedsFieldContainer(GF_Node *node, GF_FieldInfo *fi) +{ + u32 i, count, nb_ndt; + GF_FieldInfo info; + if (!strcmp(fi->name, "children")) return 0; + nb_ndt = 0; + count = gf_node_get_field_count(node); + for (i=0; iNDTtype) nb_ndt++; + } + return (nb_ndt>1) ? 1 : 0; +} + +void DumpField(GF_SceneDumper *sdump, GF_Node *node, GF_FieldInfo field) +{ + GenMFField *mffield; + u32 i, sf_type; + Bool needs_field_container; + GF_ChildNodeItem *list; + void *slot_ptr; + + switch (field.fieldType) { + case GF_SG_VRML_SFNODE: + assert ( *(GF_Node **)field.far_ptr); + + if (sdump->XMLDump) { + if (!sdump->X3DDump) { + StartElement(sdump, (char *) field.name); + EndElementHeader(sdump, 1); + sdump->indent++; + } + } else { + StartAttribute(sdump, field.name); + } + DumpNode(sdump, *(GF_Node **)field.far_ptr, 0, NULL); + + if (sdump->XMLDump) { + if (!sdump->X3DDump) { + sdump->indent--; + EndElement(sdump, (char *) field.name, 1); + } + } else { + EndAttribute(sdump); + } + return; + case GF_SG_VRML_MFNODE: + needs_field_container = 0; + if (sdump->XMLDump && sdump->X3DDump) needs_field_container = SD_NeedsFieldContainer(node, &field); + if (!sdump->X3DDump) { + if (gf_node_get_tag(node)==TAG_X3D_Switch) field.name = "choice"; + } + list = * ((GF_ChildNodeItem **) field.far_ptr); + assert(list); + if (!sdump->XMLDump || !sdump->X3DDump) StartList(sdump, field.name); + sdump->indent++; + while (list) { + DumpNode(sdump, list->node, 1, needs_field_container ? (char *) field.name : NULL); + list = list->next; + } + sdump->indent--; + if (!sdump->XMLDump || !sdump->X3DDump) EndList(sdump, field.name); + return; + case GF_SG_VRML_SFCOMMANDBUFFER: + { + SFCommandBuffer *cb = (SFCommandBuffer *)field.far_ptr; + StartElement(sdump, (char *) field.name); + EndElementHeader(sdump, 1); + sdump->indent++; + if (!gf_list_count(cb->commandList)) { + /*the arch does not allow for that (we would need a codec and so on, or decompress the command list + in all cases...)*/ + if (sdump->trace && cb->bufferSize) { + if (sdump->XMLDump) fprintf(sdump->trace, "\n"); + else fprintf(sdump->trace, "#SFCommandBuffer cannot be dumped while playing - use MP4Box instead\n"); + } + } else { + gf_sm_dump_command_list(sdump, cb->commandList, sdump->indent, 0); + } + sdump->indent--; + EndElement(sdump, (char *) field.name, 1); + } + return; + } + if (gf_sg_vrml_is_sf_field(field.fieldType)) { + StartAttribute(sdump, field.name); + DumpSFField(sdump, field.fieldType, field.far_ptr, 0); + EndAttribute(sdump); + } else { + mffield = (GenMFField *) field.far_ptr; + sf_type = gf_sg_vrml_get_sf_type(field.fieldType); + + if (sdump->XMLDump && sdump->X3DDump) { + switch (sf_type) { + case GF_SG_VRML_SFSTRING: + case GF_SG_VRML_SFSCRIPT: + case GF_SG_VRML_SFURL: + fprintf(sdump->trace, " %s=\'", (char *) field.name); + break; + default: + StartAttribute(sdump, field.name); + break; + } + } else { + StartAttribute(sdump, field.name); + } + + if (!sdump->XMLDump) fprintf(sdump->trace, "["); + for (i=0; icount; i++) { + if (i) fprintf(sdump->trace, " "); + gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &slot_ptr, i); + DumpSFField(sdump, sf_type, slot_ptr, 1); + } + if (!sdump->XMLDump) fprintf(sdump->trace, "]"); + + if (sdump->XMLDump && sdump->X3DDump) { + switch (sf_type) { + case GF_SG_VRML_SFSTRING: + case GF_SG_VRML_SFSCRIPT: + case GF_SG_VRML_SFURL: + fprintf(sdump->trace, "\'"); + break; + default: + EndAttribute(sdump); + break; + } + } else { + EndAttribute(sdump); + } + } +} + +const char * GetXMTFieldTypeName(u32 fieldType) +{ + switch (fieldType) { + case GF_SG_VRML_SFBOOL: return "Boolean"; + case GF_SG_VRML_SFINT32: return "Integer"; + case GF_SG_VRML_SFCOLOR: return "Color"; + case GF_SG_VRML_SFVEC2F: return "Vector2"; + case GF_SG_VRML_SFIMAGE: return "Image"; + case GF_SG_VRML_SFTIME: return "Time"; + case GF_SG_VRML_SFFLOAT: return "Float"; + case GF_SG_VRML_SFVEC3F: return "Vector3"; + case GF_SG_VRML_SFROTATION: return "Rotation"; + case GF_SG_VRML_SFSTRING: return "String"; + case GF_SG_VRML_SFNODE: return "Node"; + case GF_SG_VRML_MFBOOL: return "Booleans"; + case GF_SG_VRML_MFINT32: return "Integers"; + case GF_SG_VRML_MFCOLOR: return "Colors"; + case GF_SG_VRML_MFVEC2F: return "Vector2s"; + case GF_SG_VRML_MFIMAGE: return "Images"; + case GF_SG_VRML_MFTIME: return "Times"; + case GF_SG_VRML_MFFLOAT: return "Floats"; + case GF_SG_VRML_MFVEC3F: return "Vector3s"; + case GF_SG_VRML_MFROTATION: return "Rotations"; + case GF_SG_VRML_MFSTRING: return "Strings"; + case GF_SG_VRML_MFNODE: return "Nodes"; + default: return "unknown"; + } +} +const char * GetXMTFieldTypeValueName(u32 fieldType) +{ + switch (fieldType) { + case GF_SG_VRML_SFBOOL: return "booleanValue"; + case GF_SG_VRML_SFINT32: return "integerValue"; + case GF_SG_VRML_SFCOLOR: return "colorValue"; + case GF_SG_VRML_SFVEC2F: return "vector2Value"; + case GF_SG_VRML_SFIMAGE: return "imageValue"; + case GF_SG_VRML_SFTIME: return "timeValue"; + case GF_SG_VRML_SFFLOAT: return "floatValue"; + case GF_SG_VRML_SFVEC3F: return "vector3Value"; + case GF_SG_VRML_SFROTATION: return "rotationValue"; + case GF_SG_VRML_SFSTRING: return "stringValue"; + case GF_SG_VRML_MFBOOL: return "booleanArrayValue"; + case GF_SG_VRML_MFINT32: return "integerArrayValue"; + case GF_SG_VRML_MFCOLOR: return "colorArrayValue"; + case GF_SG_VRML_MFVEC2F: return "vector2ArrayValue"; + case GF_SG_VRML_MFIMAGE: return "imageArrayValue"; + case GF_SG_VRML_MFTIME: return "timeArrayValue"; + case GF_SG_VRML_MFFLOAT: return "floatArrayValue"; + case GF_SG_VRML_MFVEC3F: return "vector3ArrayValue"; + case GF_SG_VRML_MFROTATION: return "rotationArrayValue"; + case GF_SG_VRML_MFSTRING: return "stringArrayValue"; + default: return "unknown"; + } +} + +const char *SD_GetQuantCatName(u32 QP_Type) +{ + switch (QP_Type) { + case QC_3DPOS: return "position3D"; + case QC_2DPOS: return "position2D"; + case QC_ORDER: return "drawingOrder"; + case QC_COLOR: return "color"; + case QC_TEXTURE_COORD: return "textureCoordinate"; + case QC_ANGLE: return "angle"; + case QC_SCALE: return "scale"; + case QC_INTERPOL_KEYS: return "keys"; + case QC_NORMALS: return "normals"; + case QC_ROTATION: return "rotations"; + case QC_SIZE_3D: return "size3D"; + case QC_SIZE_2D: return "size2D"; + case QC_LINEAR_SCALAR: return "linear"; + case QC_COORD_INDEX: return "coordIndex"; + default: return "unknown"; + } +} + +/*field dumping for proto declaration and Script*/ +void DumpDynField(GF_SceneDumper *sdump, GF_Node *node, GF_FieldInfo field, Bool has_sublist) +{ + GenMFField *mffield; + u32 i, sf_type; + void *slot_ptr; + + if (gf_sg_vrml_is_sf_field(field.fieldType)) { + DUMP_IND(sdump); + if (sdump->XMLDump) { + if (sdump->X3DDump) { + fprintf(sdump->trace, "trace, "X3DDump) { + fprintf(sdump->trace, ">\n"); + sdump->indent++; + fprintf(sdump->trace, ""); + DumpNode(sdump, field.far_ptr ? *(GF_Node **)field.far_ptr : NULL, 0, NULL); + fprintf(sdump->trace, ""); + sdump->indent--; + if (!has_sublist) + fprintf(sdump->trace, "\n"); + } else { + if (field.far_ptr) { + fprintf(sdump->trace, ">\n"); + DumpNode(sdump, *(GF_Node **)field.far_ptr, 0, NULL); + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "/>\n"); + } + } + DUMP_IND(sdump); + } else { + if (sdump->X3DDump) { + fprintf(sdump->trace, " value=\""); + } else { + fprintf(sdump->trace, " %s=\"", GetXMTFieldTypeValueName(field.fieldType)); + } + DumpSFField(sdump, field.fieldType, field.far_ptr, 0); + if (has_sublist) + fprintf(sdump->trace, "\">\n"); + else + fprintf(sdump->trace, "\"/>\n"); + } + } else { + fprintf(sdump->trace, "/>\n"); + } + } else { + fprintf(sdump->trace, "%s %s %s", gf_sg_vrml_get_event_type_name(field.eventType, sdump->X3DDump), gf_sg_vrml_get_field_type_by_name(field.fieldType), field.name); + if ((field.eventType==GF_SG_EVENT_FIELD) || (field.eventType==GF_SG_EVENT_EXPOSED_FIELD)) { + fprintf(sdump->trace, " "); + if (field.fieldType == GF_SG_VRML_SFNODE) { + DumpNode(sdump, field.far_ptr ? *(GF_Node **)field.far_ptr : NULL, 0, NULL); + } else { + DumpFieldValue(sdump, field); + } + } + fprintf(sdump->trace, "\n"); + } + } else { + mffield = (GenMFField *) field.far_ptr; + sf_type = gf_sg_vrml_get_sf_type(field.fieldType); + + DUMP_IND(sdump); + if (!sdump->XMLDump) { + fprintf(sdump->trace, "%s %s %s", gf_sg_vrml_get_event_type_name(field.eventType, sdump->X3DDump), gf_sg_vrml_get_field_type_by_name(field.fieldType), field.name); + if ((field.eventType==GF_SG_EVENT_FIELD) || (field.eventType==GF_SG_EVENT_EXPOSED_FIELD)) { + fprintf(sdump->trace, " ["); + + if (sf_type == GF_SG_VRML_SFNODE) { + GF_ChildNodeItem *l = *(GF_ChildNodeItem **)field.far_ptr; + fprintf(sdump->trace, "\n"); + sdump->indent++; + while (l) { + DumpNode(sdump, l->node, 1, NULL); + l = l->next; + } + sdump->indent--; + DUMP_IND(sdump); + } else { + for (i=0; icount; i++) { + if (i) fprintf(sdump->trace, " "); + if (field.fieldType != GF_SG_VRML_MFNODE) { + gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &slot_ptr, i); + DumpSFField(sdump, sf_type, slot_ptr, (mffield->count>1) ? 1 : 0); + } + } + } + fprintf(sdump->trace, "]"); + } + fprintf(sdump->trace, "\n"); + } else { + if (sdump->X3DDump) { + fprintf(sdump->trace, "trace, "trace, ">\n"); + sdump->indent++; + if (!sdump->X3DDump) fprintf(sdump->trace, ""); + while (list) { + DumpNode(sdump, list->node, 1, NULL); + list = list->next; + } + if (!sdump->X3DDump) fprintf(sdump->trace, ""); + sdump->indent++; + DUMP_IND(sdump); + if (!has_sublist) + fprintf(sdump->trace, "\n"); + } else { + if (sdump->X3DDump) { + fprintf(sdump->trace, " value=\""); + } else { + fprintf(sdump->trace, " %s=\"", GetXMTFieldTypeValueName(field.fieldType)); + } + for (i=0; icount; i++) { + if (i) fprintf(sdump->trace, " "); + if (field.fieldType != GF_SG_VRML_MFNODE) { + gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &slot_ptr, i); + DumpSFField(sdump, sf_type, slot_ptr, (mffield->count>1) ? 1 : 0); + } + } + if (has_sublist) + fprintf(sdump->trace, "\">\n"); + else + fprintf(sdump->trace, "\"/>\n"); + } + } else { + fprintf(sdump->trace, "/>\n"); + } + } + } +} + + +/*field dumping for proto instance*/ +void DumpProtoField(GF_SceneDumper *sdump, GF_Node *node, GF_FieldInfo field) +{ + GenMFField *mffield; + u32 i, sf_type; + void *slot_ptr; + + DUMP_IND(sdump); + fprintf(sdump->trace, "trace, ">\n"); + sdump->indent++; + if (!sdump->X3DDump) fprintf(sdump->trace, ""); + DumpNode(sdump, field.far_ptr ? *(GF_Node **)field.far_ptr : NULL, 0, NULL); + if (!sdump->X3DDump) fprintf(sdump->trace, ""); + sdump->indent--; + DUMP_IND(sdump); + fprintf(sdump->trace, "\n"); + } else { + if (sdump->X3DDump) { + fprintf(sdump->trace, " value=\""); + } else { + fprintf(sdump->trace, " %s=\"", GetXMTFieldTypeValueName(field.fieldType)); + } + DumpSFField(sdump, field.fieldType, field.far_ptr, 0); + fprintf(sdump->trace, "\"/>\n"); + } + } else { + mffield = (GenMFField *) field.far_ptr; + sf_type = gf_sg_vrml_get_sf_type(field.fieldType); + + if ((field.eventType==GF_SG_EVENT_FIELD) || (field.eventType==GF_SG_EVENT_EXPOSED_FIELD)) { + if (sf_type == GF_SG_VRML_SFNODE) { + GF_ChildNodeItem *list = *(GF_ChildNodeItem **)field.far_ptr; + fprintf(sdump->trace, ">\n"); + sdump->indent++; + if (!sdump->X3DDump) fprintf(sdump->trace, ""); + while (list) { + DumpNode(sdump, list->node, 1, NULL); + list = list->next; + } + if (!sdump->X3DDump) fprintf(sdump->trace, ""); + sdump->indent--; + DUMP_IND(sdump); + fprintf(sdump->trace, "\n"); + } else { + if (sdump->X3DDump) { + fprintf(sdump->trace, " value=\""); + } else { + fprintf(sdump->trace, " %s=\"", GetXMTFieldTypeValueName(field.fieldType)); + } + for (i=0; icount; i++) { + if (i) fprintf(sdump->trace, " "); + if (field.fieldType != GF_SG_VRML_MFNODE) { + gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &slot_ptr, i); + DumpSFField(sdump, sf_type, slot_ptr, (mffield->count>1) ? 1 : 0); + } + } + fprintf(sdump->trace, "\"/>\n"); + } + } + } +} + +GF_Route *SD_GetISedField(GF_SceneDumper *sdump, GF_Node *node, GF_FieldInfo *field) +{ + u32 i; + GF_Route *r; + i=0; + while ((r = (GF_Route*)gf_list_enum(sdump->current_proto->sub_graph->Routes, &i))) { + if (!r->IS_route) continue; + if ((r->ToNode==node) && (r->ToField.fieldIndex==field->fieldIndex)) return r; + } + if (!node || !node->sgprivate->interact || !node->sgprivate->interact->routes) return NULL; + i=0; + while ((r = (GF_Route*)gf_list_enum(node->sgprivate->interact->routes, &i))) { + if (!r->IS_route) continue; + if (r->FromField.fieldIndex == field->fieldIndex) return r; + } + return NULL; +} + +void DumpISField(GF_SceneDumper *sdump, GF_Node *node, GF_FieldInfo field, Bool isScript, Bool skip_is) +{ + GF_FieldInfo pfield; + + GF_Route *r = SD_GetISedField(sdump, node, &field); + if (r->FromNode) { + pfield.fieldIndex = r->ToField.fieldIndex; + gf_sg_proto_get_field(sdump->current_proto, NULL, &pfield); + } else { + pfield.fieldIndex = r->FromField.fieldIndex; + gf_sg_proto_get_field(sdump->current_proto, NULL, &pfield); + } + + if (!sdump->XMLDump) { + DUMP_IND(sdump); + if (isScript) fprintf(sdump->trace, "%s %s ", gf_sg_vrml_get_event_type_name(field.eventType, sdump->X3DDump), gf_sg_vrml_get_field_type_by_name(field.fieldType)); + fprintf(sdump->trace, "%s IS %s\n", field.name, pfield.name); + } else { + if (!skip_is) { + StartElement(sdump, "IS"); + EndElementHeader(sdump, 1); + sdump->indent++; + } + DUMP_IND(sdump); + fprintf(sdump->trace, "\n", field.name, pfield.name); + if (!skip_is) { + sdump->indent--; + EndElement(sdump, "IS", 1); + } + } +} +const char *SD_GetNodeName(GF_SceneDumper *sdump, GF_Node *node) +{ + /*convert whatever possible*/ + if (sdump->X3DDump) { + if (node->sgprivate->tag == TAG_MPEG4_Circle) return "Circle2D"; + else if (node->sgprivate->tag == TAG_MPEG4_Rectangle) return "Rectangle2D"; + } else if (!sdump->X3DDump) { + if (node->sgprivate->tag == TAG_X3D_Circle2D) return "Circle"; + else if (node->sgprivate->tag == TAG_X3D_Rectangle2D) return "Rectangle"; + } + return gf_node_get_class_name(node); +} + +Bool SD_CanDumpNode(GF_SceneDumper *sdump, GF_Node *node) +{ + const char *name; + u32 tag; + + if (node->sgprivate->tag==TAG_ProtoNode) return 1; + + if (sdump->X3DDump || (sdump->dump_mode==GF_SM_DUMP_VRML)) { + if (node->sgprivate->tag>=GF_NODE_RANGE_FIRST_X3D) return 1; + if (node->sgprivate->tag==TAG_MPEG4_Rectangle) return 1; + if (node->sgprivate->tag==TAG_MPEG4_Circle) return 1; + name = gf_node_get_class_name(node); + tag = gf_node_x3d_type_by_class_name(name); + return tag ? 1 : 0; + } else { + if (node->sgprivate->tag<=GF_NODE_RANGE_LAST_MPEG4) return 1; + if (node->sgprivate->tag==TAG_X3D_Rectangle2D) return 1; + if (node->sgprivate->tag==TAG_X3D_Circle2D) return 1; + name = gf_node_get_class_name(node); + tag = gf_node_mpeg4_type_by_class_name(name); + return tag ? 1 : 0; + } +} + +void DumpNode(GF_SceneDumper *sdump, GF_Node *node, Bool in_list, char *fieldContainer) +{ + u32 i, count, to_dump, sub_el, ID; + u32 *def_fields; + Bool isDEF, isScript, isProto, hasISed; + char *name; + GF_Node *base; + GF_FieldInfo field, base_field; + + if (!node) { + fprintf(sdump->trace, "NULL"); + return; + } + + /*this dumper works only for VRML like graphs*/ + if (node->sgprivate->tag>GF_NODE_RANGE_LAST_X3D) return; + + if (!SD_CanDumpNode(sdump, node)) { + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[Scene Dump] node %s not part of %s standard - removing\n", gf_node_get_class_name(node), sdump->X3DDump ? "X3D" : (sdump->dump_mode==GF_SM_DUMP_VRML) ? "VRML" : "MPEG4")); + if (!in_list) fprintf(sdump->trace, "NULL"); + return; + } + + name = (char *) SD_GetNodeName(sdump, node); + isProto = (gf_node_get_tag(node) == TAG_ProtoNode) ? 1 : 0; + ID = gf_node_get_id(node); + isDEF = 0; + if (ID) { + isDEF = SD_IsDEFNode(sdump, node); + if (!isDEF) { + if (!sdump->XMLDump) { + if (in_list) DUMP_IND(sdump); + fprintf(sdump->trace, "USE "); + DumpNodeID(sdump, node); + if (in_list) fprintf(sdump->trace, "\n"); + } else { + if (isProto) { + StartElement(sdump, "ProtoInstance"); + StartAttribute(sdump, "name"); + fprintf(sdump->trace, "%s", name); + EndAttribute(sdump); + } else { + StartElement(sdump, name); + } + StartAttribute(sdump, "USE"); + DumpNodeID(sdump, node); + EndAttribute(sdump); + EndElementHeader(sdump, 0); + } + return; + } + } + + /*get all fields*/ + count = gf_node_get_field_count(node); + def_fields = (u32*)malloc(sizeof(u32) * count); + + base = NULL; + switch (gf_node_get_tag(node)) { + case TAG_X3D_Script: + case TAG_MPEG4_Script: + isScript = 1; + break; + default: + isScript = 0; + break; + } + + + if (!isScript) { + if (isProto) { + base = gf_sg_proto_create_instance(node->sgprivate->scenegraph, ((GF_ProtoInstance *)node)->proto_interface); + } else { + base = gf_node_new(node->sgprivate->scenegraph, node->sgprivate->tag); + } + } + + if (base) gf_node_register(base, NULL); + + hasISed = 0; + to_dump = sub_el = 0; + for (i=0;i2) ? 2 : 1; + } else { + def_fields[i] = 0; + } + + gf_node_get_field(node, i, &field); + + if (sdump->current_proto) { + if (SD_GetISedField(sdump, node, &field) != NULL) { + def_fields[i] = 3; + if ((field.fieldType == GF_SG_VRML_SFNODE) || (field.fieldType == GF_SG_VRML_MFNODE)) + def_fields[i] = sdump->XMLDump ? 4 : 3; + /*in XMT the ISed is not an attribute*/ + if (sdump->XMLDump) sub_el++; + to_dump++; + hasISed = 1; + continue; + } + } + + if (!isScript && ((field.eventType == GF_SG_EVENT_IN) || (field.eventType == GF_SG_EVENT_OUT)) ) { + continue; + } + /*proto instance in XMT lists all fields as elements*/ + if (sdump->XMLDump && isProto) { + def_fields[i] = 2; + to_dump++; + sub_el++; + continue; + } + switch (field.fieldType) { + case GF_SG_VRML_SFNODE: + if (* (GF_Node **) field.far_ptr) { + def_fields[i] = 2; + to_dump++; + sub_el++; + } + break; + case GF_SG_VRML_MFNODE: + if (* (GF_ChildNodeItem**) field.far_ptr) { + def_fields[i] = 2; + to_dump++; + sub_el++; + } + break; + case GF_SG_VRML_SFCOMMANDBUFFER: + { + SFCommandBuffer *p = (SFCommandBuffer *)field.far_ptr; + if (p->bufferSize || gf_list_count(p->commandList)) { + def_fields[i] = 2; + to_dump++; + sub_el++; + } + } + break; + default: + if (isScript) { + to_dump++; + } else { + gf_node_get_field(base, i, &base_field); + if (!gf_sg_vrml_field_equal(base_field.far_ptr, field.far_ptr, field.fieldType)) { + def_fields[i] = 1; + to_dump++; + } + } + break; + } + } + if (base) gf_node_unregister(base, NULL); + + if (!to_dump) { + if (in_list) DUMP_IND(sdump); + if (!sdump->XMLDump) { + if (isDEF) { + fprintf(sdump->trace, "DEF "); + DumpNodeID(sdump, node); + fprintf(sdump->trace, " "); + } + fprintf(sdump->trace, "%s {}\n", name); + } else { + if (isDEF) { + if (isProto) { + fprintf(sdump->trace, "trace, "<%s DEF=\"", name); + } + DumpNodeID(sdump, node); + fprintf(sdump->trace, "\"/>\n"); + } else { + if (isProto) { + fprintf(sdump->trace, "\n", name); + } else { + fprintf(sdump->trace, "<%s/>\n", name); + } + } + } + free(def_fields); + return; + } + + if (!sdump->XMLDump) { + if (in_list) DUMP_IND(sdump); + if (isDEF) { + fprintf(sdump->trace, "DEF "); + DumpNodeID(sdump, node); + fprintf(sdump->trace, " "); + } + fprintf(sdump->trace, "%s {\n", name); + } else { + if (isProto) { + StartElement(sdump, "ProtoInstance"); + StartAttribute(sdump, "name"); + fprintf(sdump->trace, "%s", name); + EndAttribute(sdump); + } else { + StartElement(sdump, name); + } + if (isDEF) { + StartAttribute(sdump, "DEF"); + DumpNodeID(sdump, node); + EndAttribute(sdump); + } + } + + sdump->indent ++; + for (i=0;iXMLDump) { + DumpDynField(sdump, node, field, 0); + } else if (!i && sdump->XMLDump) { + DumpField(sdump, node, field); + } + break; + /*IS field*/ + case 3: + if (sdump->XMLDump) break; + gf_node_get_field(node, i, &field); + DumpISField(sdump, node, field, isScript, 0); + def_fields[i] = 0; + break; + default: + break; + } + } + if (fieldContainer) fprintf(sdump->trace, " fieldContainer=\"%s\"", fieldContainer); + + if (isScript) sub_el = 1; + EndElementHeader(sdump, sub_el ? 1 : 0); + + if (sub_el) { + /*dump all normal IS elements for XMT*/ + if (hasISed && sdump->XMLDump) { + StartElement(sdump, "IS"); + EndElementHeader(sdump, 1); + sdump->indent++; + } + for (i=0;iXMLDump) { + sdump->indent--; + EndElement(sdump, "IS", 1); + } + /*dump all sub elements and complex IS*/ + for (i=0;iXMLDump) { + DumpProtoField(sdump, node, field); + } else { + DumpField(sdump, node, field); + } + } else { + /*X3D script metadata, NOT DYN*/ + if ((i==3) && (node->sgprivate->tag==TAG_X3D_Script) ) { + if (*((GF_Node **)field.far_ptr)) DumpField(sdump, node, field); + } else { + DumpDynField(sdump, node, field, 0); + } + } + break; + case 4: + gf_node_get_field(node, i, &field); + DumpISField(sdump, node, field, isScript, 0); + break; + } + } + } + + /*finally dump script - XMT dumping is broken!!*/ + if (isScript && !sdump->XMLDump) { + gf_node_get_field(node, 0, &field); + DumpField(sdump, node, field); + } + + sdump->indent --; + if (!sdump->XMLDump && !in_list) { + DUMP_IND(sdump); + fprintf(sdump->trace, "}"); + } else { + EndElement(sdump, isProto ? "ProtoInstance" : name, sub_el); + } + free(def_fields); +} + +GF_Err DumpMultipleIndexedReplace(GF_SceneDumper *sdump, GF_Command *com) +{ + u32 i; + GF_FieldInfo field; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *) gf_list_get(com->command_fields, 0); + gf_node_get_field(com->node, inf->fieldIndex, &field); + field.fieldType = inf->fieldType; + + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "node); + fprintf(sdump->trace, "\" atField=\"%s\">\n", field.name); + } else { + fprintf(sdump->trace, "MULTIPLEINDREPLACE "); + DumpNodeID(sdump, com->node); + fprintf(sdump->trace, ".%s [\n", field.name); + } + sdump->indent++; + i=0; + while ((inf = (GF_CommandField *) gf_list_enum(com->command_fields, &i))) { + field.far_ptr = inf->field_ptr; + + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "pos); + } else { + fprintf(sdump->trace, "%d BY ", inf->pos); + } + DumpFieldValue(sdump, field); + if (sdump->XMLDump) { + fprintf(sdump->trace, "/>"); + } else { + fprintf(sdump->trace, "\n"); + } + } + sdump->indent--; + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "]\n"); + } + return GF_OK; +} + +GF_Err DumpMultipleReplace(GF_SceneDumper *sdump, GF_Command *com) +{ + u32 i; + GF_FieldInfo info; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "node); + fprintf(sdump->trace, "\">\n"); + + sdump->indent++; + i=0; + while ((inf = (GF_CommandField *) gf_list_enum(com->command_fields, &i))) { + gf_node_get_field(com->node, inf->fieldIndex, &info); + info.far_ptr = inf->field_ptr; + + DUMP_IND(sdump); + if (gf_sg_vrml_get_sf_type(info.fieldType) != GF_SG_VRML_SFNODE) { + fprintf(sdump->trace, "trace, "/>\n"); + } else { + fprintf(sdump->trace, ""); + DumpField(sdump, com->node, info); + fprintf(sdump->trace, "\n"); + } + } + sdump->indent--; + + DUMP_IND(sdump); + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "MULTIPLEREPLACE "); + DumpNodeID(sdump, com->node); + fprintf(sdump->trace, " {\n"); + sdump->indent++; + i=0; + while ((inf = (GF_CommandField *) gf_list_enum(com->command_fields, &i))) { + gf_node_get_field(com->node, inf->fieldIndex, &info); + info.far_ptr = inf->field_ptr; + DumpField(sdump, com->node, info); + } + sdump->indent--; + DUMP_IND(sdump); + fprintf(sdump->trace, "}\n"); + } + return GF_OK; +} + +GF_Err DumpGlobalQP(GF_SceneDumper *sdump, GF_Command *com) +{ + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *) gf_list_get(com->command_fields, 0); + + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "GLOBALQP "); + } + DumpNode(sdump, inf->new_node, 0, NULL); + if (sdump->XMLDump) fprintf(sdump->trace, "\n"); + else fprintf(sdump->trace, "\n"); + return GF_OK; +} + +GF_Err DumpNodeInsert(GF_SceneDumper *sdump, GF_Command *com) +{ + GF_CommandField *inf; + char posname[20]; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *) gf_list_get(com->command_fields, 0); + + switch (inf->pos) { + case 0: + strcpy(posname, "BEGIN"); + break; + case -1: + strcpy(posname, "END"); + break; + default: + sprintf(posname, "%d", inf->pos); + break; + } + + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "node); + fprintf(sdump->trace, "\" position=\"%s\">", posname); + } else { + if (inf->pos==-1) { fprintf(sdump->trace, "APPEND TO "); } + else fprintf(sdump->trace, "INSERT AT "); + DumpNodeID(sdump, com->node); + fprintf(sdump->trace, ".children"); + if (inf->pos!=-1) fprintf(sdump->trace, "[%d]", inf->pos); + fprintf(sdump->trace, " "); + } + + DumpNode(sdump, inf->new_node, 0, NULL); + if (sdump->XMLDump) fprintf(sdump->trace, ""); + fprintf(sdump->trace, "\n"); + return GF_OK; +} + +GF_Err DumpRouteInsert(GF_SceneDumper *sdump, GF_Command *com, Bool is_scene_replace) +{ + GF_Route r; + + memset(&r, 0, sizeof(GF_Route)); + r.ID = com->RouteID; + r.name = com->def_name; + r.FromNode = SD_FindNode(sdump, com->fromNodeID); + r.FromField.fieldIndex = com->fromFieldIndex; + r.ToNode = SD_FindNode(sdump, com->toNodeID); + r.ToField.fieldIndex = com->toFieldIndex; + + gf_list_add(sdump->inserted_routes, com); + + if (is_scene_replace) { + DumpRoute(sdump, &r, 0); + } else { + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "INSERT "); + } + DumpRoute(sdump, &r, 2); + if (sdump->XMLDump) fprintf(sdump->trace, ""); + } + return GF_OK; +} + +GF_Err DumpIndexInsert(GF_SceneDumper *sdump, GF_Command *com) +{ + GF_Err e; + GF_FieldInfo field, sffield; + GF_CommandField *inf; + char posname[20]; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *) gf_list_get(com->command_fields, 0); + + switch (inf->pos) { + case 0: + strcpy(posname, "BEGIN"); + break; + case -1: + strcpy(posname, "END"); + break; + default: + sprintf(posname, "%d", inf->pos); + break; + } + + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "node); + fprintf(sdump->trace, "\" atField=\"%s\" position=\"%s\"", field.name, posname); + } else { + if (inf->pos==-1) { fprintf(sdump->trace, "APPEND TO "); } + else fprintf(sdump->trace, "INSERT AT "); + DumpNodeID(sdump, com->node); + fprintf(sdump->trace, ".%s", field.name); + if (inf->pos!=-1) fprintf(sdump->trace, "[%d]", inf->pos); + fprintf(sdump->trace, " "); + } + + memcpy(&sffield, &field, sizeof(GF_FieldInfo)); + sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + + if (field.fieldType==GF_SG_VRML_MFNODE) { + if (sdump->XMLDump) fprintf(sdump->trace, ">\n"); + DumpNode(sdump, inf->new_node, 0, NULL); + if (sdump->XMLDump) fprintf(sdump->trace, ""); + fprintf(sdump->trace, "\n"); + } else { + sffield.far_ptr = inf->field_ptr; + DumpFieldValue(sdump, sffield); + if (sdump->XMLDump) fprintf(sdump->trace, "/>"); + fprintf(sdump->trace, "\n"); + } + return e; +} + +GF_Err DumpIndexDelete(GF_SceneDumper *sdump, GF_Command *com) +{ + char posname[20]; + GF_FieldInfo field; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *) gf_list_get(com->command_fields, 0); + + switch (inf->pos) { + case -1: + strcpy(posname, sdump->XMLDump ? "END" : "LAST"); + break; + case 0: + strcpy(posname, "BEGIN"); + break; + default: + sprintf(posname, "%d", inf->pos); + break; + } + + gf_node_get_field(com->node, inf->fieldIndex, &field); + + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "node); + fprintf(sdump->trace, "\" atField=\"%s\" position=\"%s\"/>", field.name, posname); + } else { + fprintf(sdump->trace, "DELETE "); + if (inf->pos==-1) fprintf(sdump->trace, "%s ", posname); + DumpNodeID(sdump, com->node); + fprintf(sdump->trace, ".%s", field.name); + if (inf->pos!=-1) fprintf(sdump->trace, "[%d]", inf->pos); + fprintf(sdump->trace, "\n"); + } + return GF_OK; +} + + +GF_Err DumpNodeDelete(GF_SceneDumper *sdump, GF_Command *com) +{ + DUMP_IND(sdump); + if (sdump->XMLDump) { + if (com->tag==GF_SG_NODE_DELETE_EX) { + fprintf(sdump->trace, "trace, "node); + fprintf(sdump->trace, "\"/>\n"); + } else { + if (com->tag==GF_SG_NODE_DELETE_EX) fprintf(sdump->trace, "X"); + fprintf(sdump->trace, "DELETE "); + DumpNodeID(sdump, com->node); + fprintf(sdump->trace, "\n"); + } + return GF_OK; +} + +GF_Err DumpRouteDelete(GF_SceneDumper *sdump, GF_Command *com) +{ + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "RouteID, com->def_name); + fprintf(sdump->trace, "\"/>\n"); + } else { + fprintf(sdump->trace, "DELETE ROUTE "); + DumpRouteID(sdump, com->RouteID, com->def_name); + fprintf(sdump->trace, "\n"); + } + return GF_OK; +} + + + +GF_Err DumpNodeReplace(GF_SceneDumper *sdump, GF_Command *com) +{ + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *) gf_list_get(com->command_fields, 0); + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "node); + fprintf(sdump->trace, "\">"); + DumpNode(sdump, inf->new_node, 0, NULL); + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "REPLACE "); + DumpNodeID(sdump, com->node); + fprintf(sdump->trace, " BY "); + DumpNode(sdump, inf->new_node, 0, NULL); + fprintf(sdump->trace, "\n"); + } + return GF_OK; +} + +GF_Err DumpFieldReplace(GF_SceneDumper *sdump, GF_Command *com) +{ + GF_Err e; + GF_FieldInfo field; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *) gf_list_get(com->command_fields, 0); + + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "node); + fprintf(sdump->trace, "\" atField=\"%s\" ", field.name); + } else { + fprintf(sdump->trace, "REPLACE "); + DumpNodeID(sdump, com->node); + fprintf(sdump->trace, ".%s BY ", field.name); + } + + switch (field.fieldType) { + case GF_SG_VRML_SFNODE: + if (sdump->XMLDump) fprintf(sdump->trace, ">"); + DumpNode(sdump, inf->new_node, 0, NULL); + if (sdump->XMLDump) fprintf(sdump->trace, ""); + else fprintf(sdump->trace, "\n"); + break; + case GF_SG_VRML_MFNODE: + { + GF_ChildNodeItem *tmp; + if (sdump->XMLDump) { + fprintf(sdump->trace, ">"); + } else { + fprintf(sdump->trace, " [\n"); + } + sdump->indent++; + tmp = inf->node_list; + while (tmp) { + DumpNode(sdump, tmp->node, 1, NULL); + tmp = tmp->next; + } + sdump->indent--; + if (sdump->XMLDump) { + fprintf(sdump->trace, ""); + } else { + EndList(sdump, NULL); + } + } + break; + case GF_SG_VRML_SFCOMMANDBUFFER: + if (sdump->XMLDump) { + SFCommandBuffer *cb = (SFCommandBuffer*)inf->field_ptr; + fprintf(sdump->trace, ">\n"); + gf_sm_dump_command_list(sdump, cb->commandList, sdump->indent+1, 0); + DUMP_IND(sdump); + fprintf(sdump->trace, "\n"); + } else { + SFCommandBuffer *cb = (SFCommandBuffer*)inf->field_ptr; + fprintf(sdump->trace, " {\n"); + gf_sm_dump_command_list(sdump, cb->commandList, sdump->indent+1, 0); + DUMP_IND(sdump); + fprintf(sdump->trace, "}\n"); + } + break; + default: + field.far_ptr = inf->field_ptr; + DumpFieldValue(sdump, field); + if (sdump->XMLDump) fprintf(sdump->trace, "/>"); + fprintf(sdump->trace, "\n"); + } + return e; +} + + +GF_Err DumpIndexReplace(GF_SceneDumper *sdump, GF_Command *com) +{ + char posname[20]; + GF_Err e; + GF_FieldInfo field; + GF_CommandField *inf; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField *) gf_list_get(com->command_fields, 0); + + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + + switch (inf->pos) { + case 0: + strcpy(posname, "BEGIN"); + break; + case -1: + strcpy(posname, sdump->XMLDump ? "END" : "LAST"); + break; + default: + sprintf(posname, "%d", inf->pos); + break; + } + + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "node); + fprintf(sdump->trace, "\" atField=\"%s\" position=\"%s\"", field.name, posname); + } else { + fprintf(sdump->trace, "REPLACE "); + if (inf->pos==-1) fprintf(sdump->trace, "%s ", posname); + DumpNodeID(sdump, com->node); + fprintf(sdump->trace, ".%s", field.name); + if (inf->pos!=-1) fprintf(sdump->trace, "[%d]", inf->pos); + fprintf(sdump->trace, " BY "); + } + + if (field.fieldType == GF_SG_VRML_MFNODE) { + if (sdump->XMLDump) fprintf(sdump->trace, ">\n"); + DumpNode(sdump, inf->new_node, 0, NULL); + fprintf(sdump->trace, (sdump->XMLDump) ? "\n" : "\n"); + } else { + field.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + field.far_ptr = inf->field_ptr; + DumpFieldValue(sdump, field); + fprintf(sdump->trace, sdump->XMLDump ? "/>\n" : "\n"); + } + return GF_OK; +} + + +GF_Err DumpRouteReplace(GF_SceneDumper *sdump, GF_Command *com) +{ + const char *name; + GF_Route r2; + + if (!DumpFindRouteName(sdump, com->RouteID, &name)) return GF_BAD_PARAM; + + memset(&r2, 0, sizeof(GF_Route)); + r2.FromNode = SD_FindNode(sdump, com->fromNodeID); + r2.FromField.fieldIndex = com->fromFieldIndex; + r2.ToNode = SD_FindNode(sdump, com->toNodeID); + r2.ToField.fieldIndex = com->toFieldIndex; + + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "RouteID, (char *) name); + fprintf(sdump->trace, "\">\n"); + } else { + fprintf(sdump->trace, "REPLACE ROUTE "); + DumpRouteID(sdump, com->RouteID, (char *) name); + fprintf(sdump->trace, " BY "); + } + DumpRoute(sdump, &r2, 1); + if (sdump->XMLDump ) fprintf(sdump->trace, ""); + return GF_OK; +} + +GF_Err DumpRoute(GF_SceneDumper *sdump, GF_Route *r, u32 dump_type) +{ + char toNode[512], fromNode[512]; + const char *node_name; + u32 id; + if (!r->is_setup) { + gf_node_get_field(r->FromNode, r->FromField.fieldIndex, &r->FromField); + gf_node_get_field(r->ToNode, r->ToField.fieldIndex, &r->ToField); + r->is_setup = 1; + } + if (!r->FromNode || !r->ToNode) return GF_BAD_PARAM; + + if (sdump->XMLDump || !dump_type) DUMP_IND(sdump); + + node_name = gf_node_get_name_and_id(r->FromNode, &id); + if (node_name) { + strcpy(fromNode, node_name); + strcpy(toNode, gf_node_get_name(r->ToNode)); + } else { + sprintf(fromNode, "N%d", id-1); + sprintf(toNode, "N%d", gf_node_get_id(r->ToNode) - 1); + } + if (sdump->XMLDump) { + fprintf(sdump->trace, "ID) { + StartAttribute(sdump, "DEF"); + DumpRouteID(sdump, r->ID, r->name); + EndAttribute(sdump); + } + fprintf(sdump->trace, " fromNode=\"%s\" fromField=\"%s\" toNode=\"%s\" toField=\"%s\"/>\n", fromNode, r->FromField.name, toNode, r->ToField.name); + } else { + if (dump_type==2) fprintf(sdump->trace, "ROUTE "); + if (r->ID) { + fprintf(sdump->trace, "DEF "); + DumpRouteID(sdump, r->ID, r->name); + fprintf(sdump->trace, " "); + } + if (dump_type==1) { + fprintf(sdump->trace, "%s.%s TO %s.%s\n", fromNode, r->FromField.name, toNode, r->ToField.name); + } else { + if (dump_type!=2) fprintf(sdump->trace, "ROUTE "); + fprintf(sdump->trace, "%s.%s TO %s.%s\n", fromNode, r->FromField.name, toNode, r->ToField.name); + } + } + return GF_OK; +} + + +GF_Err DumpProtos(GF_SceneDumper *sdump, GF_List *protoList) +{ + u32 i, j, count; + GF_FieldInfo field; + GF_Err e; + GF_SceneGraph *prev_sg; + GF_Proto *proto, *prev_proto; + + prev_proto = sdump->current_proto; + + i=0; + while ((proto = (GF_Proto*)gf_list_enum(protoList, &i))) { + sdump->current_proto = proto; + + DUMP_IND(sdump); + if (!sdump->XMLDump) { + fprintf(sdump->trace, proto->ExternProto.count ? "EXTERNPROTO " : "PROTO "); + fprintf(sdump->trace, "%s [\n", proto->Name); + } else { + fprintf(sdump->trace, "Name, proto->ID); + if (proto->ExternProto.count) { + fprintf(sdump->trace, " locations=\""); + DumpSFField(sdump, GF_SG_VRML_SFURL, &proto->ExternProto.vals[0], 0); + fprintf(sdump->trace, "\""); + } + fprintf(sdump->trace, ">\n"); + } + + if (sdump->XMLDump && sdump->X3DDump) fprintf(sdump->trace, ""); + + sdump->indent++; + count = gf_list_count(proto->proto_fields); + for (j=0; jproto_fields, j); + field.fieldIndex = pf->ALL_index; + field.eventType = pf->EventType; + field.far_ptr = pf->def_value; + field.fieldType = pf->FieldType; + field.name = pf->FieldName; + field.NDTtype = NDT_SFWorldNode; + field.on_event_in = NULL; + + DumpDynField(sdump, NULL, field, pf->QP_Type ? 1 : 0); + + if (!pf->QP_Type) continue; + + /*dump interface coding - BT/TXT extensions, not supported by any other tool*/ + sdump->indent++; + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "QP_Type)); + } else { + fprintf(sdump->trace, "{QP %d", pf->QP_Type); + } + if (pf->QP_Type==QC_LINEAR_SCALAR) fprintf(sdump->trace, sdump->XMLDump ? " nbBits=\"%d\"" : " nbBits %d", pf->NumBits); + if (pf->hasMinMax) { + switch (pf->QP_Type) { + case QC_LINEAR_SCALAR: + case QC_COORD_INDEX: + if (sdump->XMLDump) { + fprintf(sdump->trace, " intMin=\"%d\" intMax=\"%d\"", *((SFInt32 *)pf->qp_min_value), *((SFInt32 *)pf->qp_max_value)); + } else { + fprintf(sdump->trace, " b {%d %d}", *((SFInt32 *)pf->qp_min_value), *((SFInt32 *)pf->qp_max_value)); + } + break; + default: + if (sdump->XMLDump) { + fprintf(sdump->trace, " floatMin=\"%g\" floatMax=\"%g\"", FIX2FLT( *((SFFloat *)pf->qp_min_value) ), FIX2FLT( *((SFFloat *)pf->qp_max_value) )); + } else { + fprintf(sdump->trace, " b {%g %g}", FIX2FLT( *((SFFloat *)pf->qp_min_value) ), FIX2FLT( *((SFFloat *)pf->qp_max_value) ) ); + } + break; + } + } + fprintf(sdump->trace, sdump->XMLDump ? "/>\n" : "}\n"); + sdump->indent--; + if (sdump->XMLDump) { + DUMP_IND(sdump); + fprintf(sdump->trace, "\n"); + } + + } + + sdump->indent--; + DUMP_IND(sdump); + if (!sdump->XMLDump) { + fprintf(sdump->trace, "]"); + } else if (sdump->X3DDump) fprintf(sdump->trace, "\n"); + + if (proto->ExternProto.count) { + if (!sdump->XMLDump) { + fprintf(sdump->trace, " \""); + DumpSFField(sdump, GF_SG_VRML_SFURL, &proto->ExternProto.vals[0], 0); + fprintf(sdump->trace, "\"\n\n"); + } else { + fprintf(sdump->trace, "\n"); + } + continue; + } + if (!sdump->XMLDump) fprintf(sdump->trace, " {\n"); + + sdump->indent++; + + if (sdump->XMLDump && sdump->X3DDump) fprintf(sdump->trace, "\n"); + + e = DumpProtos(sdump, proto->sub_graph->protos); + if (e) return e; + + /*set namespace to the proto one*/ + prev_sg = sdump->sg; + sdump->sg = gf_sg_proto_get_graph(proto); + + count = gf_list_count(proto->node_code); + for (j=0; jnode_code, j); + DumpNode(sdump, n, 1, NULL); + } + count = gf_list_count(proto->sub_graph->Routes); + for (j=0; jsub_graph->Routes, j); + if (r->IS_route) continue; + DumpRoute(sdump, r, 0); + } + + if (sdump->XMLDump && sdump->X3DDump) fprintf(sdump->trace, "\n"); + + /*restore namespace*/ + sdump->sg = prev_sg; + + sdump->indent--; + DUMP_IND(sdump); + if (!sdump->XMLDump) { + fprintf(sdump->trace, "}\n"); + } else { + fprintf(sdump->trace, "\n"); + } + } + sdump->current_proto = prev_proto; + return GF_OK; +} + +GF_Err DumpSceneReplace(GF_SceneDumper *sdump, GF_Command *com) +{ + if (sdump->XMLDump) { + if (!sdump->X3DDump) { + StartElement(sdump, "Replace"); + EndElementHeader(sdump, 1); + sdump->indent++; + } + StartElement(sdump, "Scene"); + if (!sdump->X3DDump && com->use_names) DumpBool(sdump, "USENAMES", com->use_names); + EndElementHeader(sdump, 1); + sdump->indent++; + } else { + if (!sdump->skip_scene_replace) { + DUMP_IND(sdump); + fprintf(sdump->trace, "REPLACE SCENE BY "); + } + } + DumpProtos(sdump, com->new_proto_list); + DumpNode(sdump, com->node, 0, NULL); + if (!sdump->XMLDump) fprintf(sdump->trace, "\n\n"); + + return GF_OK; +} + +GF_Err DumpProtoInsert(GF_SceneDumper *sdump, GF_Command *com) +{ + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "INSERTPROTO [\n"); + } + sdump->indent++; + DumpProtos(sdump, com->new_proto_list); + sdump->indent--; + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "]\n"); + } + return GF_OK; +} + + +#ifndef GPAC_DISABLE_SVG +static char *lsr_format_node_id(GF_Node *n, u32 NodeID, char *str) +{ + if (!n) sprintf(str, "N%d", NodeID-1); + else { + const char *name = gf_node_get_name_and_id(n, &NodeID); + if (name) sprintf(str, "%s", name); + else sprintf(str, "N%d", NodeID - 1); + } + return str; +} + +static char szLSRName[1024]; + +static char *sd_get_lsr_namespace(GF_SceneGraph *sg) +{ + char *lsrns = (char *) gf_sg_get_namespace_qname(sg, GF_XMLNS_LASER); + if (lsrns) { + sprintf(szLSRName, "%s:", lsrns); + return szLSRName; + } + return ""; +} + +GF_Err DumpLSRNewScene(GF_SceneDumper *sdump, GF_Command *com) +{ + char *lsrns = sd_get_lsr_namespace(com->in_scene); + fprintf(sdump->trace, "<%sNewScene>\n", lsrns); + SD_DumpSVG_Element(sdump, com->node, NULL, 0); + fprintf(sdump->trace, "\n", lsrns); + return GF_OK; +} + +GF_Err DumpLSRAddReplaceInsert(GF_SceneDumper *sdump, GF_Command *com) +{ + char szAtt[80000]; + Bool is_text = 0; + GF_CommandField *f; + char *lsrns = sd_get_lsr_namespace(com->in_scene); + + const char *com_name = (com->tag==GF_SG_LSR_REPLACE) ? "Replace" : ( (com->tag==GF_SG_LSR_ADD) ? "Add" : "Insert" ); + + DUMP_IND(sdump); + + fprintf(sdump->trace, "<%s%s ref=\"%s\" ", lsrns, com_name, lsr_format_node_id(com->node, com->RouteID, szAtt)); + f = (GF_CommandField *) gf_list_get(com->command_fields, 0); + if (f && (f->pos>=0) ) fprintf(sdump->trace, "index=\"%d\" ", f->pos); + if (f) { + GF_FieldInfo info; + if (!f->new_node && !f->node_list) { + char *att_name = NULL; + if (f->fieldType==SVG_Transform_Scale_datatype) att_name = "scale"; + else if (f->fieldType==SVG_Transform_Rotate_datatype) att_name = "rotation"; + else if (f->fieldType==SVG_Transform_Translate_datatype) att_name = "translation"; + else if (f->fieldIndex==(u32) -1) att_name = "textContent"; + else att_name = (char*) gf_svg_get_attribute_name(com->node, f->fieldIndex); + + fprintf(sdump->trace, "attributeName=\"%s\" ", att_name); + if (f->field_ptr) { + info.far_ptr = f->field_ptr; + info.fieldIndex = f->fieldIndex; + info.fieldType = f->fieldType; + info.name = att_name; + + if ((s32) f->pos >= 0) { + gf_svg_dump_attribute_indexed(com->node, &info, szAtt); + } else { + gf_svg_dump_attribute(com->node, &info, szAtt); + } + fprintf(sdump->trace, "value=\"%s\" ", szAtt); + } + + if (com->fromNodeID) { + GF_FieldInfo op_info; + GF_Node *op = gf_sg_find_node(sdump->sg, com->fromNodeID); + fprintf(sdump->trace, "operandElementId=\"%s\" ", lsr_format_node_id(op, com->RouteID, szAtt)); + gf_node_get_field(op, com->fromFieldIndex, &op_info); + fprintf(sdump->trace, "operandAttributeName=\"%s\" ", op_info.name); + } + } + } + if (!f->new_node && !f->node_list) { + fprintf(sdump->trace, "/>\n"); + return GF_OK; + } + if (f->new_node && f->new_node->sgprivate->tag==TAG_DOMText) is_text = 1; + /*if fieldIndex (eg attributeName) is set, this is children replacement*/ + if (f->fieldIndex>0) + fprintf(sdump->trace, "attributeName=\"children\" "); + + fprintf(sdump->trace, ">"); + if (!is_text) { + fprintf(sdump->trace, "\n"); + sdump->indent++; + } + if (f->new_node) { + SD_DumpSVG_Element(sdump, f->new_node, com->node, 0); + } else if (f->node_list) { + GF_ChildNodeItem *list = f->node_list; + while (list) { + SD_DumpSVG_Element(sdump, list->node, com->node, 0); + list = list->next; + } + } + sdump->indent--; + if (!is_text) DUMP_IND(sdump); + fprintf(sdump->trace, "\n", lsrns, com_name); + return GF_OK; +} +GF_Err DumpLSRClean(GF_SceneDumper *sdump, GF_Command *com) +{ + return GF_OK; +} +GF_Err DumpLSRDelete(GF_SceneDumper *sdump, GF_Command *com) +{ + char szID[1024]; + GF_CommandField *f; + char *lsrns = sd_get_lsr_namespace(com->in_scene); + DUMP_IND(sdump); + fprintf(sdump->trace, "<%sDelete ref=\"%s\" ", lsrns, lsr_format_node_id(com->node, com->RouteID, szID)); + f = (GF_CommandField *) gf_list_get(com->command_fields, 0); + if (f && (f->pos>=0) ) fprintf(sdump->trace, "index=\"%d\" ", f->pos); + fprintf(sdump->trace, "/>\n"); + return GF_OK; +} +GF_Err DumpLSRInsert(GF_SceneDumper *sdump, GF_Command *com) +{ + return GF_OK; +} +GF_Err DumpLSRRestore(GF_SceneDumper *sdump, GF_Command *com) +{ + return GF_OK; +} +GF_Err DumpLSRSave(GF_SceneDumper *sdump, GF_Command *com) +{ + return GF_OK; +} +GF_Err DumpLSRSendEvent(GF_SceneDumper *sdump, GF_Command *com) +{ + char szID[1024]; + char *lsrns = sd_get_lsr_namespace(com->in_scene); + DUMP_IND(sdump); + fprintf(sdump->trace, "<%sSendEvent ref=\"%s\" event=\"%s\"", lsrns, + lsr_format_node_id(com->node, com->RouteID, szID), + gf_dom_event_get_name(com->send_event_name) + ); + if (com->send_event_name <= GF_EVENT_MOUSEWHEEL) + fprintf(sdump->trace, " pointvalue=\"%g %g\"", FIX2FLT(com->send_event_x), FIX2FLT(com->send_event_y) ); + + switch (com->send_event_name) { + case GF_EVENT_KEYDOWN: + case GF_EVENT_LONGKEYPRESS: + case GF_EVENT_REPEAT_KEY: + case GF_EVENT_SHORT_ACCESSKEY: + if (com->send_event_integer) { + fprintf(sdump->trace, " stringvalue=\"%s\"", gf_dom_get_key_name(com->send_event_integer) ); + break; + } + default: + if (com->send_event_integer) + fprintf(sdump->trace, " intvalue=\"%d\"", com->send_event_integer); + if (com->send_event_string) + fprintf(sdump->trace, " stringvalue=\"%s\"", com->send_event_string); + break; + } + + fprintf(sdump->trace, "/>\n"); + return GF_OK; +} +GF_Err DumpLSRActivate(GF_SceneDumper *sdump, GF_Command *com) +{ + char szID[1024]; + char *lsrns = sd_get_lsr_namespace(com->in_scene); + DUMP_IND(sdump); + if (com->tag==GF_SG_LSR_ACTIVATE) { + fprintf(sdump->trace, "<%sActivate ref=\"%s\" />\n", lsrns, lsr_format_node_id(com->node, com->RouteID, szID)); + } else { + fprintf(sdump->trace, "<%sDeactivate ref=\"%s\" />\n", lsrns, lsr_format_node_id(com->node, com->RouteID, szID)); + } + return GF_OK; +} + +#endif + +GF_EXPORT +GF_Err gf_sm_dump_command_list(GF_SceneDumper *sdump, GF_List *comList, u32 indent, Bool skip_first_replace) +{ + GF_Err e; + u32 i, count; + u32 prev_ind, remain; + Bool prev_skip, has_scene_replace; + + if (!sdump || !sdump->trace|| !comList || !sdump->sg) return GF_BAD_PARAM; + + prev_skip = sdump->skip_scene_replace; + sdump->skip_scene_replace = skip_first_replace; + prev_ind = sdump->indent; + sdump->indent = indent; + has_scene_replace = 0; + + + remain = 0; + e = GF_OK; + count = gf_list_count(comList); + for (i=0; iX3DDump || (sdump->dump_mode==GF_SM_DUMP_VRML))) { + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[Scene Dump] MPEG-4 Commands found, not supported in %s - skipping\n", sdump->X3DDump ? "X3D" : "VRML")); + break; + } + if (has_scene_replace && (com->tag != GF_SG_ROUTE_INSERT)) { + has_scene_replace = 0; + if (sdump->XMLDump) { + sdump->indent--; + EndElement(sdump, "Scene", 1); + sdump->indent--; + EndElement(sdump, "Replace", 1); + } + } + switch (com->tag) { + /*insert commands*/ + case GF_SG_NODE_INSERT: e = DumpNodeInsert(sdump, com); break; + case GF_SG_INDEXED_INSERT: e = DumpIndexInsert(sdump, com); break; + case GF_SG_ROUTE_INSERT: + e = DumpRouteInsert(sdump, com, has_scene_replace); + if (remain) remain--; + break; + /*delete commands*/ + case GF_SG_NODE_DELETE: e = DumpNodeDelete(sdump, com); break; + case GF_SG_INDEXED_DELETE: e = DumpIndexDelete(sdump, com); break; + case GF_SG_ROUTE_DELETE: e = DumpRouteDelete(sdump, com); break; + /*replace commands*/ + case GF_SG_NODE_REPLACE: e = DumpNodeReplace(sdump, com); break; + case GF_SG_FIELD_REPLACE: e = DumpFieldReplace(sdump, com); break; + case GF_SG_INDEXED_REPLACE: e = DumpIndexReplace(sdump, com); break; + case GF_SG_ROUTE_REPLACE: e = DumpRouteReplace(sdump, com); break; + case GF_SG_SCENE_REPLACE: + /*we don't support replace scene in conditional*/ + assert(!sdump->current_com_list); + sdump->current_com_list = comList; + e = DumpSceneReplace(sdump, com); + sdump->current_com_list = NULL; + has_scene_replace = 1; + remain = count - i - 1; + break; + /*extended commands*/ + case GF_SG_PROTO_INSERT: e = DumpProtoInsert(sdump, com); break; + case GF_SG_PROTO_DELETE_ALL: + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "\n"); + } else { + fprintf(sdump->trace, "DELETEPROTO ALL\n"); + } + e = GF_OK; + break; + case GF_SG_PROTO_DELETE: + { + u32 i; + DUMP_IND(sdump); + if (sdump->XMLDump) { + fprintf(sdump->trace, "trace, "DELETEPROTO ["); + } + for (i=0; idel_proto_list_size; i++) { + if (i) fprintf(sdump->trace, " "); + fprintf(sdump->trace, "%d", com->del_proto_list[i]); + } + if (sdump->XMLDump) { + fprintf(sdump->trace, "\"/>\n"); + } else { + fprintf(sdump->trace, "]\n"); + } + e = GF_OK; + } + break; + case GF_SG_GLOBAL_QUANTIZER: e = DumpGlobalQP(sdump, com); break; + case GF_SG_MULTIPLE_REPLACE: e = DumpMultipleReplace(sdump, com); break; + case GF_SG_MULTIPLE_INDEXED_REPLACE: e = DumpMultipleIndexedReplace(sdump, com); break; + case GF_SG_NODE_DELETE_EX: e = DumpNodeDelete(sdump, com); break; + +#ifndef GPAC_DISABLE_SVG + /*laser commands*/ + case GF_SG_LSR_NEW_SCENE: e = DumpLSRNewScene(sdump, com); break; + case GF_SG_LSR_ADD: e = DumpLSRAddReplaceInsert(sdump, com); break; + case GF_SG_LSR_CLEAN: e = DumpLSRClean(sdump, com); break; + case GF_SG_LSR_REPLACE: e = DumpLSRAddReplaceInsert(sdump, com); break; + case GF_SG_LSR_DELETE: e = DumpLSRDelete(sdump, com); break; + case GF_SG_LSR_INSERT: e = DumpLSRAddReplaceInsert(sdump, com); break; + case GF_SG_LSR_RESTORE: e = DumpLSRRestore(sdump, com); break; + case GF_SG_LSR_SAVE: e = DumpLSRSave(sdump, com); break; + case GF_SG_LSR_SEND_EVENT: e = DumpLSRSendEvent(sdump, com); break; + case GF_SG_LSR_ACTIVATE: + case GF_SG_LSR_DEACTIVATE: + e = DumpLSRActivate(sdump, com); + break; +#endif + } + if (e) break; + + + if (!has_scene_replace && sdump->skip_scene_replace) { + sdump->skip_scene_replace = 0; + if (!sdump->XMLDump && (i+1trace, "\nAT 0 {\n"); + sdump->indent++; + } + } + } + + if (remain && !sdump->XMLDump) { + DUMP_IND(sdump); + fprintf(sdump->trace, "}\n"); + } + if (has_scene_replace && sdump->XMLDump) { + sdump->indent--; + EndElement(sdump, "Scene", 1); + if (!sdump->X3DDump) { + sdump->indent--; + EndElement(sdump, "Replace", 1); + } + } + + sdump->indent = prev_ind; + sdump->skip_scene_replace = prev_skip; + return e; +} + +#ifndef GPAC_DISABLE_SVG +void SD_DumpSVG_Element(GF_SceneDumper *sdump, GF_Node *n, GF_Node *parent, Bool is_root) +{ + GF_ChildNodeItem *list; + char attName[100], attValue[81920]; + u32 i, count, nID; + Bool needs_cr; + SVG_Element *svg = (SVG_Element *)n; + GF_FieldInfo info; + SVGAttribute *att; + u32 tag, ns; + if (!n) return; + + nID = gf_node_get_id(n); + tag = n->sgprivate->tag; + /*remove undef listener/handlers*/ + if (!nID) { + switch (tag) { + case TAG_SVG_listener: + if (0 && gf_node_get_attribute_by_tag(n, TAG_XMLEV_ATT_handler, 0, 0, &info)==GF_OK) { + if (((XMLRI*)info.far_ptr)->target && !gf_node_get_id(((XMLRI*)info.far_ptr)->target) ) + return; + } + break; + case TAG_SVG_handler: + /*this handler was not declared in the graph*/ + if (!n->sgprivate->parents || (n->sgprivate->parents->node != parent)) + return; + break; + case TAG_DOMText: + { + GF_DOMText *txt = (GF_DOMText *)n; + if (txt->textContent) { + if ((txt->type==GF_DOM_TEXT_CDATA) || + (parent->sgprivate->tag == TAG_SVG_script) || + (parent->sgprivate->tag == TAG_SVG_handler)) { + fprintf(sdump->trace, "trace, "%s", txt->textContent); + fprintf(sdump->trace, "]]>"); + } else if (txt->type==GF_DOM_TEXT_REGULAR) { + DumpUTFString(sdump, 0, txt->textContent); + } + } + } + return; + } + } + + if (!sdump->in_text) { + DUMP_IND(sdump); + } + + /*register all namespaces specified on this element */ + gf_xml_push_namespaces((GF_DOMNode *)n); + + fprintf(sdump->trace, "<%s", gf_node_get_class_name(n)); + ns = gf_xml_get_element_namespace(n); + + if (nID) fprintf(sdump->trace, " id=\"%s\"", lsr_format_node_id(n, 0, attValue)); + + att = svg->attributes; + while (att) { + if (att->data_type==SVG_ID_datatype) { + att = att->next; + continue; + } + + info.fieldIndex = att->tag; + info.fieldType = att->data_type; + if (att->tag==TAG_DOM_ATT_any) { + u32 att_ns = ((GF_DOMFullAttribute*)att)->xmlns; + info.name = ((GF_DOMFullAttribute*)att)->name; + if ((att_ns != ns) && strncmp(info.name, "xmlns", 5)) { + sprintf(attName, "%s:%s", gf_sg_get_namespace_qname(gf_node_get_graph(n), att_ns), ((GF_DOMFullAttribute*)att)->name); + info.name = attName; + } + } else { + info.name = gf_svg_get_attribute_name(n, att->tag); + } + + if (att->data_type==XMLRI_datatype) { + XMLRI *xlink = (XMLRI *)att->data; + if (xlink->type==XMLRI_ELEMENTID) { + if (!xlink->target || !gf_node_get_id((GF_Node*)xlink->target) ) { + att = att->next; + continue; + } + if (parent && (parent == (GF_Node *) xlink->target)) { + att = att->next; + continue; + } + } + else if (xlink->type==XMLRI_STREAMID) { + fprintf(sdump->trace, " %s=\"#stream%d\"", info.name, xlink->lsr_stream_id); + att = att->next; + continue; + } else { + fprintf(sdump->trace, " %s=\"%s\"", info.name, xlink->string); + att = att->next; + continue; + } + } + info.far_ptr = att->data; + gf_svg_dump_attribute((GF_Node*)svg, &info, attValue); + if (/*strcmp(info.name, "xmlns") &&*/ (info.fieldType = strlen(attValue))) + fprintf(sdump->trace, " %s=\"%s\"", info.name, attValue); + fflush(sdump->trace); + att = att->next; + } + + /*re-translate dynamically created listeners/handlers */ + if (n->sgprivate->interact && n->sgprivate->interact->dom_evt) { + count = gf_list_count(n->sgprivate->interact->dom_evt->evt_list); + for (i=0; isgprivate->interact->dom_evt->evt_list, i); + /*this listener has been created for internal use*/ + if (listener->sgprivate->parents) continue; + if (gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_handler, 0, 0, &info)==GF_OK) { + GF_DOMText *txt; + hdl = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target; + if (!hdl) continue; + /*this handler was declared in the graph*/ + if (hdl->sgprivate->parents + && (hdl->sgprivate->parents->next || (hdl->sgprivate->parents->node != listener)) + ) + continue; + + txt = hdl->children ? (GF_DOMText*)hdl->children->node : NULL; + if (!txt || (txt->sgprivate->tag!=TAG_DOMText) || !txt->textContent) continue; + if (gf_node_get_attribute_by_tag((GF_Node*)hdl, TAG_XMLEV_ATT_event, 0, 0, &info)==GF_OK) { + fprintf(sdump->trace, " on%s=\"%s\"", gf_dom_event_get_name( ((XMLEV_Event*)info.far_ptr)->type), txt->textContent); + } + } + } + } + if (svg->children) fprintf(sdump->trace, ">"); + else { + fprintf(sdump->trace, "/>"); + return; + } + + if (n->sgprivate->tag==TAG_LSR_conditional) { + GF_DOMUpdates *up = svg->children ? (GF_DOMUpdates *)svg->children->node : NULL; + sdump->indent++; + if (up && (up->sgprivate->tag==TAG_DOMUpdates)) { + if (gf_list_count(up->updates)) { + fprintf(sdump->trace, "\n"); + gf_sm_dump_command_list(sdump, up->updates, sdump->indent, 0); + } else if (up->data) { + fprintf(sdump->trace, "\n"); + } + } + sdump->indent--; + DUMP_IND(sdump); + fprintf(sdump->trace, "\n", gf_node_get_class_name(n)); + return; + } + + if (tag==TAG_SVG_text || tag==TAG_SVG_textArea) sdump->in_text = 1; + needs_cr = 1; + sdump->indent++; + list = svg->children; + while (list) { + if (!sdump->in_text) fprintf(sdump->trace, "\n"); + SD_DumpSVG_Element(sdump, list->node, n, 0); + list = list->next; + } + if (!sdump->in_text) fprintf(sdump->trace, "\n"); + sdump->indent--; + if (!sdump->in_text) DUMP_IND(sdump); + fprintf(sdump->trace, "", gf_node_get_class_name(n)); + if (tag==TAG_SVG_text || tag==TAG_SVG_textArea) sdump->in_text = 0; + /*removes all namespaces specified on this element */ + gf_xml_pop_namespaces((GF_DOMNode *)n); +} +#endif + +static void gf_sm_dump_saf_hdr(GF_SceneDumper *dumper, char *unit_name, u64 au_time, Bool is_rap) +{ + fprintf(dumper->trace, "trace, " time=\""LLD"\"", LLD_CAST au_time); + if (is_rap) fprintf(dumper->trace, " rap=\"true\""); + fprintf(dumper->trace, ">\n"); +} + +void dump_od_to_saf(GF_SceneDumper *dumper, GF_AUContext *au, u32 indent) +{ + u32 i, count; + + count = gf_list_count(au->commands); + for (i=0; icommands, i); + if (com->tag != GF_ODF_OD_UPDATE_TAG) continue; + + c2 = gf_list_count(com->objectDescriptors); + for (j=0; jobjectDescriptors, j); + GF_ESD *esd = (GF_ESD *) gf_list_get(od->ESDescriptors, 0); + GF_MuxInfo *mux; + if (!esd) { + if (od->URLString) { + fprintf(dumper->trace, "owner->ESID, od->URLString); + if (au->timing) fprintf(dumper->trace, " time=\""LLD"\"", LLD_CAST au->timing); + fprintf(dumper->trace, "/>\n"); + } + continue; + } + mux = (GF_MuxInfo *)gf_list_get(esd->extensionDescriptors, 0); + if (!mux || (mux->tag!=GF_ODF_MUXINFO_TAG)) mux = NULL; + + + fprintf(dumper->trace, "ESID); + fprintf(dumper->trace, " streamType=\"%d\" objectTypeIndication=\"%d\" timeStampResolution=\"%d\"", esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication, au->owner->timeScale); + if (au->timing) fprintf(dumper->trace, " time=\""LLD"\"", LLD_CAST au->timing); + if (mux && mux->file_name) fprintf(dumper->trace, " source=\"%s\"", mux->file_name); + fprintf(dumper->trace, "/>\n"); + } + + + } + fprintf(dumper->trace, "\n"); +} + + +GF_Err SD_SetSceneGraph(GF_SceneDumper *sdump, GF_SceneGraph *sg) +{ + if (sdump) sdump->sg = sg; + return GF_OK; +} + +GF_Err SD_DumpDOMElement(GF_SceneDumper *sdump, GF_DOMFullNode *node) +{ + const char *ns; + u32 child_type = 0; + GF_DOMFullAttribute *att; + GF_ChildNodeItem *child; + GF_DOMText *txt; + ns = gf_sg_get_namespace_qname(node->sgprivate->scenegraph, node->ns); + + DUMP_IND(sdump); + if (ns) fprintf(sdump->trace, "<%s:%s", ns, node->name); + else fprintf(sdump->trace, "<%s", node->name); + att = (GF_DOMFullAttribute *)node->attributes; + while (att) { + fprintf(sdump->trace, " %s=\"%s\"", att->name, (char *) att->data); + att = (GF_DOMFullAttribute *)att->next; + } + if (!node->children) { + fprintf(sdump->trace, "/>\n"); + return GF_OK; + } + fprintf(sdump->trace, ">"); + sdump->indent++; + child = node->children; + while (child) { + switch(child->node->sgprivate->tag) { + case TAG_DOMFullNode: + if (!child_type) fprintf(sdump->trace, "\n"); + child_type = 1; + SD_DumpDOMElement(sdump, (GF_DOMFullNode*)child->node); + break; + case TAG_DOMText: + child_type = 2; + txt = (GF_DOMText *)child->node; + if (txt->type==GF_DOM_TEXT_REGULAR) { + DumpUTFString(sdump, 0, txt->textContent); + } else if (txt->type==GF_DOM_TEXT_CDATA) { + fprintf(sdump->trace, "trace, "%s", txt->textContent); + fprintf(sdump->trace, "]]>"); + } + break; + } + child = child->next; + } + + sdump->indent--; + if (child_type!=2) { + DUMP_IND(sdump); + } + + if (ns) fprintf(sdump->trace, "\n", ns, node->name); + else fprintf(sdump->trace, "\n", node->name); + + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_sm_dump_graph(GF_SceneDumper *sdump, Bool skip_proto, Bool skip_routes) +{ + u32 tag; + GF_Err e = GF_OK; + if (!sdump->trace || !sdump->sg || !sdump->sg->RootNode) return GF_BAD_PARAM; + + + tag = sdump->sg->RootNode->sgprivate->tag; + + if (tag<=GF_NODE_RANGE_LAST_X3D) { + SD_SetupDump(sdump, NULL); + + if (sdump->XMLDump) { + StartElement(sdump, "Scene"); + EndElementHeader(sdump, 1); + sdump->indent++; + } + if (!skip_proto) { + e = DumpProtos(sdump, sdump->sg->protos); + if (e) return e; + } + + if (sdump->X3DDump) { + GF_ChildNodeItem *list = ((GF_ParentNode *)sdump->sg->RootNode)->children; + while (list) { + DumpNode(sdump, list->node, 0, NULL); + list = list->next; + } + } else { + DumpNode(sdump, sdump->sg->RootNode, 0, NULL); + } + if (!sdump->XMLDump) fprintf(sdump->trace, "\n\n"); + if (!skip_routes) { + GF_Route *r; + u32 i=0; + while ((r = (GF_Route*)gf_list_enum(sdump->sg->Routes, &i))) { + if (r->IS_route || (r->graph!=sdump->sg)) continue; + e = DumpRoute(sdump, r, 0); + if (e) return e; + } + } + if (sdump->XMLDump) { + sdump->indent--; + EndElement(sdump, "Scene", 1); + } + + SD_FinalizeDump(sdump, NULL); + return GF_OK; + } +#ifndef GPAC_DISABLE_SVG + else if ((tag>=GF_NODE_RANGE_FIRST_SVG) && (tag<=GF_NODE_RANGE_LAST_SVG)) { + sdump->dump_mode = GF_SM_DUMP_SVG; + SD_SetupDump(sdump, NULL); + SD_DumpSVG_Element(sdump, sdump->sg->RootNode, NULL, 1); + return GF_OK; + } + else if (tag==TAG_DOMFullNode) { + sdump->dump_mode = GF_SM_DUMP_XML; + SD_SetupDump(sdump, NULL); + SD_DumpDOMElement(sdump, (GF_DOMFullNode*)sdump->sg->RootNode); + } +#endif + + return GF_OK; +} + + + + +static void ReorderAUContext(GF_List *sample_list, GF_AUContext *au, Bool lsr_dump) +{ + u64 autime, time; + u32 i; + Bool has_base; + GF_AUContext *ptr; + + /* + this happens when converting from bt to xmt + NOTE: Comment is wrong? this happens when just loading BT + */ + if (!au->timing_sec) { + au->timing_sec = (Double) (s64) au->timing; + /* Hack to avoid timescale=0 which happens when loading a BT with no SLConfig*/ + if (!au->owner->timeScale) au->owner->timeScale = 1000; + au->timing_sec /= au->owner->timeScale; + } + /*this happens when converting from xmt to bt*/ + if (!au->timing) { + assert(au->owner->timeScale); + au->timing = (u64) (au->timing_sec * au->owner->timeScale); + } + + autime = au->timing + au->owner->dump_time_offset; + has_base = 0; + i=0; + while ((ptr = (GF_AUContext*)gf_list_enum(sample_list, &i))) { + time = ptr->timing + ptr->owner->dump_time_offset; + if ( + /*time ordered*/ + (time > autime) + /*set bifs first for first AU*/ + || (!has_base && (time == autime) && (ptr->owner->streamType < au->owner->streamType) ) + /*set OD first for laser*/ + || (lsr_dump && (au->owner->streamType==GF_STREAM_OD)) + ) { + gf_list_insert(sample_list, au, i-1); + return; + } + + has_base = 0; + if ( (ptr->owner->streamType == au->owner->streamType) && (time == autime) ) has_base = 1; + } + gf_list_add(sample_list, au); +} + + +GF_EXPORT +GF_Err gf_sm_dump(GF_SceneManager *ctx, char *rad_name, u32 dump_mode) +{ + GF_Err e; + GF_List *sample_list; + Bool first_par; + u32 i, j, indent, num_scene, num_od, first_bifs, num_tracks; + Double time; + GF_SceneDumper *dumper; + GF_StreamContext *sc; + GF_AUContext *au; + Bool no_root_found = 1; + + sample_list = gf_list_new(); + + num_scene = num_od = 0; + num_tracks = 0; + indent = 0; + dumper = gf_sm_dumper_new(ctx->scene_graph, rad_name, ' ', dump_mode); + e = GF_OK; + /*configure all systems streams we're dumping*/ + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { + + switch (sc->streamType) { + case GF_STREAM_SCENE: + num_scene ++; + num_tracks ++; + break; + case GF_STREAM_OD: + num_od ++; + num_tracks ++; + break; + default: + continue; + } + + j=0; + while ((au = (GF_AUContext*)gf_list_enum(sc->AUs, &j))) { + ReorderAUContext(sample_list, au, dumper->LSRDump); + if (dumper->dump_mode==GF_SM_DUMP_SVG) break; + } + if (dumper->dump_mode==GF_SM_DUMP_SVG) break; + } + num_scene = (num_scene>1) ? 1 : 0; + num_od = (num_od>1) ? 1 : 0; + + SD_SetupDump(dumper, (GF_Descriptor *) ctx->root_od); + +#ifndef GPAC_DISABLE_SVG + if (dumper->dump_mode==GF_SM_DUMP_SVG) { + GF_AUContext *au = (GF_AUContext*)gf_list_get(sample_list, 0); + GF_Command *com = NULL; + if (au) com = (GF_Command*)gf_list_get(au->commands, 0); + if (!au) { + SD_DumpSVG_Element(dumper, dumper->sg->RootNode, NULL, 1); + } else if (!com || (com->tag!=GF_SG_LSR_NEW_SCENE) || !com->node) { + e = GF_NOT_SUPPORTED; + } else { + SD_DumpSVG_Element(dumper, com->node, NULL, 1); + } + SD_FinalizeDump(dumper, (GF_Descriptor *) ctx->root_od); + gf_sm_dumper_del(dumper); + gf_list_del(sample_list); + return e; + } +#endif + + time = dumper->LSRDump ? -1 : 0; + first_par = 0; + first_bifs = 1; + + while (gf_list_count(sample_list)) { + GF_AUContext *au = (GF_AUContext*)gf_list_get(sample_list, 0); + gf_list_rem(sample_list, 0); + + if (!dumper->XMLDump) { + + if (!first_bifs || (au->owner->streamType != GF_STREAM_SCENE) ) { + if (au->is_rap) fprintf(dumper->trace, "RAP "); + fprintf(dumper->trace, "AT "LLD" ", LLD_CAST au->timing); + if ( (au->owner->streamType==GF_STREAM_OD && num_od) || (au->owner->streamType==GF_STREAM_SCENE && num_scene)) { + fprintf(dumper->trace, "IN %d ", au->owner->ESID); + } + fprintf(dumper->trace, "{\n"); + indent++; + } + + switch (au->owner->streamType) { + case GF_STREAM_OD: + if (dumper->LSRDump) { + dump_od_to_saf(dumper, au, indent); + } else { +#ifndef GPAC_READ_ONLY + e = gf_odf_dump_com_list(au->commands, dumper->trace, indent+1, 0); +#endif + } + break; + case GF_STREAM_SCENE: + e = gf_sm_dump_command_list(dumper, au->commands, indent, first_bifs); + break; + } + if (first_bifs) { + first_bifs = 0; + fprintf(dumper->trace, "\n"); + + } else { + indent--; + fprintf(dumper->trace, "}\n\n"); + } + } + else { + if (dumper->LSRDump) { + if (time != au->timing_sec) { + time = au->timing_sec; + } + } else if (!time && !num_scene && first_bifs) { + } else if (num_scene || num_od) { + if (!first_par) { + first_par = 1; + indent += 1; + } else { + fprintf(dumper->trace, " \n"); + } + fprintf(dumper->trace, " \n", au->timing_sec, au->owner->ESID); + } else if (au->timing_sec>time) { + if (!first_par) { + first_par = 1; + indent += 1; + } else { + fprintf(dumper->trace, " \n"); + } + fprintf(dumper->trace, "\n", au->timing_sec); + } + switch (au->owner->streamType) { + case GF_STREAM_OD: + if (dumper->LSRDump) { + dump_od_to_saf(dumper, au, indent+1); + } else { +#ifndef GPAC_READ_ONLY + e = gf_odf_dump_com_list(au->commands, dumper->trace, indent+1, 1); +#endif + } + break; + case GF_STREAM_SCENE: + if (gf_list_count(au->commands)) { + if (dumper->LSRDump) + gf_sm_dump_saf_hdr(dumper, "sceneUnit", au->timing, au->is_rap); + + e = gf_sm_dump_command_list(dumper, au->commands, indent+1, first_bifs); + first_bifs = 0; + no_root_found = 0; + + if (dumper->LSRDump) + fprintf(dumper->trace, "\n"); + } + break; + } + time = au->timing_sec; + } + if (dumper->X3DDump || (dumper->dump_mode==GF_SM_DUMP_VRML)) break; + } + if (no_root_found && ctx->scene_graph->RootNode) { + GF_Route *r; + DumpProtos(dumper, ctx->scene_graph->protos); + DumpNode(dumper, ctx->scene_graph->RootNode, 0, NULL); + i=0; + fprintf(dumper->trace, "\n"); + while ((r = (GF_Route*)gf_list_enum(dumper->sg->Routes, &i))) { + if (r->IS_route || (r->graph!=dumper->sg)) continue; + e = DumpRoute(dumper, r, 0); + if (e) return e; + } + } + + /*close command*/ + if (!dumper->X3DDump && first_par) fprintf(dumper->trace, " \n"); + + if (gf_list_count(sample_list) && (dumper->X3DDump || (dumper->dump_mode==GF_SM_DUMP_VRML)) ) { + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[Scene Dump] MPEG-4 Commands found, not supported in %s - skipping\n", dumper->X3DDump ? "X3D" : "VRML")); + } + + SD_FinalizeDump(dumper, (GF_Descriptor *) ctx->root_od); + gf_sm_dumper_del(dumper); + gf_list_del(sample_list); + return e; +} + +//#endif + diff --git a/src/scene_manager/scene_manager.c b/src/scene_manager/scene_manager.c new file mode 100644 index 0000000..96dbb47 --- /dev/null +++ b/src/scene_manager/scene_manager.c @@ -0,0 +1,525 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +Bool gf_node_in_table_by_tag(u32 tag, u32 NDTType) +{ + if (!tag) return 0; + if (tag==TAG_ProtoNode) return 1; + else if (tag<=GF_NODE_RANGE_LAST_MPEG4) { + u32 i; + + for (i=0;isgprivate->tag : 0; + if (tag==TAG_ProtoNode) { + tag = gf_sg_proto_get_root_tag(((GF_ProtoInstance *)node)->proto_interface); + if (tag==TAG_UndefinedNode) return 1; + } + return gf_node_in_table_by_tag(tag, NDTType); +} + + +GF_EXPORT +GF_SceneManager *gf_sm_new(GF_SceneGraph *graph) +{ + GF_SceneManager *tmp; + + if (!graph) return NULL; + GF_SAFEALLOC(tmp, GF_SceneManager); + tmp->streams = gf_list_new(); + tmp->scene_graph = graph; + return tmp; +} + +GF_EXPORT +GF_StreamContext *gf_sm_stream_new(GF_SceneManager *ctx, u16 ES_ID, u8 streamType, u8 objectType) +{ + u32 i; + GF_StreamContext *tmp; + + i=0; + while ((tmp = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { + /*we MUST use the same ST*/ + if (tmp->streamType!=streamType) continue; + /*if no ESID/OTI specified this is a base layer (default stream created by parsers) + if ESID/OTI specified this is a stream already setup + */ + if ( tmp->ESID==ES_ID ){ + //tmp->objectType = objectType; + return tmp; + } + } + + GF_SAFEALLOC(tmp, GF_StreamContext); + tmp->AUs = gf_list_new(); + tmp->ESID = ES_ID; + tmp->streamType = streamType; + tmp->objectType = objectType; + tmp->timeScale = 1000; + gf_list_add(ctx->streams, tmp); + return tmp; +} + +GF_StreamContext *gf_sm_stream_find(GF_SceneManager *ctx, u16 ES_ID) +{ + u32 i, count; + if (!ES_ID) return NULL; + count = gf_list_count(ctx->streams); + for (i=0; istreams, i); + if (tmp->ESID==ES_ID) return tmp; + } + return NULL; +} + +static void gf_sm_reset_stream(GF_StreamContext *sc) +{ + while (gf_list_count(sc->AUs)) { + GF_AUContext *au = (GF_AUContext *)gf_list_last(sc->AUs); + gf_list_rem_last(sc->AUs); + + while (gf_list_count(au->commands)) { + void *comptr = gf_list_last(au->commands); + gf_list_rem_last(au->commands); + switch (sc->streamType) { + case GF_STREAM_OD: + gf_odf_com_del((GF_ODCom**) & comptr); + break; + case GF_STREAM_SCENE: + gf_sg_command_del((GF_Command *)comptr); + break; + } + } + gf_list_del(au->commands); + free(au); + } +} + +static void gf_sm_delete_stream(GF_StreamContext *sc) +{ + gf_sm_reset_stream(sc); + gf_list_del(sc->AUs); + free(sc); +} + +GF_EXPORT +void gf_sm_stream_del(GF_SceneManager *ctx, GF_StreamContext *sc) +{ + if (gf_list_del_item(ctx->streams, sc)>=0) { + gf_sm_delete_stream(sc); + } +} + +GF_EXPORT +void gf_sm_del(GF_SceneManager *ctx) +{ + u32 count; + while ( (count = gf_list_count(ctx->streams)) ) { + GF_StreamContext *sc = (GF_StreamContext *)gf_list_get(ctx->streams, count-1); + gf_list_rem(ctx->streams, count-1); + gf_sm_delete_stream(sc); + } + gf_list_del(ctx->streams); + if (ctx->root_od) gf_odf_desc_del((GF_Descriptor *) ctx->root_od); + free(ctx); +} + +GF_EXPORT +void gf_sm_reset(GF_SceneManager *ctx) +{ + GF_StreamContext *sc; + u32 i=0; + while ( (sc = gf_list_enum(ctx->streams, &i)) ) { + gf_sm_reset_stream(sc); + } + if (ctx->root_od) gf_odf_desc_del((GF_Descriptor *) ctx->root_od); + ctx->root_od = NULL; +} + +GF_EXPORT +GF_AUContext *gf_sm_stream_au_new(GF_StreamContext *stream, u64 timing, Double time_sec, Bool isRap) +{ + u32 i; + GF_AUContext *tmp; + + /*look for existing AU*/ + i=0; + while ((tmp = (GF_AUContext *)gf_list_enum(stream->AUs, &i))) { + if (timing && (tmp->timing==timing)) return tmp; + else if (time_sec && (tmp->timing_sec == time_sec)) return tmp; + else if (!time_sec && !timing && !tmp->timing && !tmp->timing_sec) return tmp; + /*insert AU*/ + else if ((time_sec && time_sectiming_sec) || (timing && timingtiming)) { + tmp = (GF_AUContext *)malloc(sizeof(GF_AUContext)); + tmp->commands = gf_list_new(); + tmp->is_rap = isRap; + tmp->timing = timing; + tmp->timing_sec = time_sec; + tmp->owner = stream; + gf_list_insert(stream->AUs, tmp, i); + return tmp; + } + } + tmp = (GF_AUContext *)malloc(sizeof(GF_AUContext)); + tmp->commands = gf_list_new(); + tmp->is_rap = isRap; + tmp->timing = timing; + tmp->timing_sec = time_sec; + tmp->owner = stream; + gf_list_add(stream->AUs, tmp); + return tmp; +} + +GF_EXPORT +GF_Err gf_sm_make_random_access(GF_SceneManager *ctx) +{ + GF_Err e; + u32 i, j, stream_count, au_count, com_count; + GF_AUContext *au; + GF_Command *com; + + e = GF_OK; + stream_count = gf_list_count(ctx->streams); + for (i=0; istreams, i); + /*FIXME - do this as well for ODs*/ + if (sc->streamType == GF_STREAM_SCENE) { + /*apply all commands - this will also apply the SceneReplace*/ + j=0; + while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) { + e = gf_sg_command_apply_list(ctx->scene_graph, au->commands, 0); + if (e) return e; + } + + /* Delete all the commands in the stream */ + while ( (au_count = gf_list_count(sc->AUs)) ) { + au = (GF_AUContext *)gf_list_get(sc->AUs, au_count-1); + gf_list_rem(sc->AUs, au_count-1); + while ( (com_count = gf_list_count(au->commands)) ) { + com = (GF_Command*)gf_list_get(au->commands, com_count - 1); + gf_list_rem(au->commands, com_count - 1); + gf_sg_command_del(com); + } + gf_list_del(au->commands); + free(au); + } + /*and recreate scene replace*/ + au = gf_sm_stream_au_new(sc, 0, 0, 1); + com = gf_sg_command_new(ctx->scene_graph, GF_SG_SCENE_REPLACE); + com->node = ctx->scene_graph->RootNode; + ctx->scene_graph->RootNode = NULL; + gf_list_del(com->new_proto_list); + com->new_proto_list = ctx->scene_graph->protos; + ctx->scene_graph->protos = NULL; + /*indicate the command is the aggregated scene graph, so that PROTOs and ROUTEs + are taken from the scenegraph when encoding*/ + com->aggregated = 1; + gf_list_add(au->commands, com); + } + } + return e; +} + + +GF_Err gf_sm_load_init_bt(GF_SceneLoader *load); +void gf_sm_load_done_bt(GF_SceneLoader *load); +GF_Err gf_sm_load_run_bt(GF_SceneLoader *load); +GF_Err gf_sm_load_init_bt_string(GF_SceneLoader *load, char *str); +GF_Err gf_sm_load_done_bt_string(GF_SceneLoader *load); + +GF_Err gf_sm_load_init_xmt(GF_SceneLoader *load); +void gf_sm_load_done_xmt(GF_SceneLoader *load); +GF_Err gf_sm_load_run_xmt(GF_SceneLoader *load); +GF_Err gf_sm_load_init_xmt_string(GF_SceneLoader *load, char *str); + +GF_Err gf_sm_load_init_isom(GF_SceneLoader *load); +void gf_sm_load_done_isom(GF_SceneLoader *load); +GF_Err gf_sm_load_run_isom(GF_SceneLoader *load); + +#ifndef GPAC_DISABLE_SVG + +GF_Err gf_sm_load_init_svg(GF_SceneLoader *load); +GF_Err gf_sm_load_done_svg(GF_SceneLoader *load); +GF_Err gf_sm_load_run_svg(GF_SceneLoader *load); +GF_Err gf_sm_load_init_svg_string(GF_SceneLoader *load, char *str); + +GF_Err gf_sm_load_init_xbl(GF_SceneLoader *load); +GF_Err gf_sm_load_done_xbl(GF_SceneLoader *load); +GF_Err gf_sm_load_run_xbl(GF_SceneLoader *load); +#endif + +#ifndef GPAC_READ_ONLY + +GF_Err gf_sm_load_init_swf(GF_SceneLoader *load); +void gf_sm_load_done_swf(GF_SceneLoader *load); +GF_Err gf_sm_load_run_swf(GF_SceneLoader *load); + +GF_Err gf_sm_load_init_qt(GF_SceneLoader *load); +void gf_sm_load_done_qt(GF_SceneLoader *load); +GF_Err gf_sm_load_run_qt(GF_SceneLoader *load); +#endif + + +static GF_Err gf_sm_load_init_from_string(GF_SceneLoader *load, char *str) +{ + + /*we need at least a scene graph*/ + if (!load || (!load->ctx && !load->scene_graph)) return GF_BAD_PARAM; + + if (!load->type) return GF_NOT_SUPPORTED; + + if (!load->scene_graph) load->scene_graph = load->ctx->scene_graph; + + switch (load->type) { + case GF_SM_LOAD_BT: + case GF_SM_LOAD_VRML: + case GF_SM_LOAD_X3DV: + return gf_sm_load_init_bt_string(load, str); + case GF_SM_LOAD_XMTA: + case GF_SM_LOAD_X3D: + return gf_sm_load_init_xmt_string(load, str); +#ifndef GPAC_DISABLE_SVG + case GF_SM_LOAD_SVG_DA: + case GF_SM_LOAD_XSR: + case GF_SM_LOAD_DIMS: + return gf_sm_load_init_svg_string(load, str); +#endif + case GF_SM_LOAD_SWF: + return GF_NOT_SUPPORTED; +#ifndef GPAC_READ_ONLY + case GF_SM_LOAD_MP4: + return GF_NOT_SUPPORTED; +#endif + } + return GF_NOT_SUPPORTED; +} + +static void gf_sm_load_done_string(GF_SceneLoader *load, Bool do_clean) +{ + switch (load->type) { + case GF_SM_LOAD_BT: + case GF_SM_LOAD_VRML: + case GF_SM_LOAD_X3DV: + gf_sm_load_done_bt_string(load); + break; + case GF_SM_LOAD_XMTA: + case GF_SM_LOAD_X3D: + /*we do not reset it here to enable SAX parsing*/ + if (do_clean) gf_sm_load_done_xmt(load); + break; +#ifndef GPAC_DISABLE_SVG + /*we do not reset it here to enable SAX parsing*/ + case GF_SM_LOAD_SVG_DA: + case GF_SM_LOAD_XSR: + case GF_SM_LOAD_DIMS: + break; +#endif + default: + break; + } +} + +GF_EXPORT +GF_Err gf_sm_load_string(GF_SceneLoader *load, char *str, Bool do_clean) +{ + GF_Err e = gf_sm_load_init_from_string(load, str); + if (e) return e; + e = gf_sm_load_run(load); + gf_sm_load_done_string(load, do_clean); + return (e<0) ? e : GF_OK; +} + + +/*initializes the context loader*/ +GF_EXPORT +GF_Err gf_sm_load_init(GF_SceneLoader *load) +{ + GF_Err e = GF_NOT_SUPPORTED; + char *ext, szExt[50]; + /*we need at least a scene graph*/ + if (!load || (!load->ctx && !load->scene_graph) || (!load->fileName && !load->isom)) return GF_BAD_PARAM; + + if (!load->type) { + if (load->isom) { + load->type = GF_SM_LOAD_MP4; + } else { + ext = strrchr(load->fileName, '.'); + if (!ext) return GF_NOT_SUPPORTED; + if (!stricmp(ext, ".gz")) { + char *anext; + ext[0] = 0; + anext = strrchr(load->fileName, '.'); + ext[0] = '.'; + ext = anext; + } + strcpy(szExt, &ext[1]); + strlwr(szExt); + if (strstr(szExt, "bt")) load->type = GF_SM_LOAD_BT; + else if (strstr(szExt, "wrl")) load->type = GF_SM_LOAD_VRML; + else if (strstr(szExt, "x3dv")) load->type = GF_SM_LOAD_X3DV; + else if (strstr(szExt, "xmt") || strstr(szExt, "xmta")) load->type = GF_SM_LOAD_XMTA; + else if (strstr(szExt, "x3d")) load->type = GF_SM_LOAD_X3D; + else if (strstr(szExt, "swf")) load->type = GF_SM_LOAD_SWF; + else if (strstr(szExt, "mov")) load->type = GF_SM_LOAD_QT; + else if (strstr(szExt, "svg")) load->type = GF_SM_LOAD_SVG_DA; + else if (strstr(szExt, "xsr")) load->type = GF_SM_LOAD_XSR; + else if (strstr(szExt, "xbl")) load->type = GF_SM_LOAD_XBL; + else if (strstr(szExt, "xml")) { + char *rtype = gf_xml_get_root_type(load->fileName, &e); + if (rtype) { + if (!strcmp(rtype, "SAFSession")) load->type = GF_SM_LOAD_XSR; + else if (!strcmp(rtype, "XMT-A")) load->type = GF_SM_LOAD_XMTA; + else if (!strcmp(rtype, "X3D")) load->type = GF_SM_LOAD_X3D; + else if (!strcmp(rtype, "bindings")) load->type = GF_SM_LOAD_XBL; + + free(rtype); + } + } + } + } + if (!load->type) return e; + + if (!load->scene_graph) load->scene_graph = load->ctx->scene_graph; + + switch (load->type) { + case GF_SM_LOAD_BT: + case GF_SM_LOAD_VRML: + case GF_SM_LOAD_X3DV: + return gf_sm_load_init_bt(load); + case GF_SM_LOAD_XMTA: + case GF_SM_LOAD_X3D: + return gf_sm_load_init_xmt(load); +#ifndef GPAC_DISABLE_SVG + case GF_SM_LOAD_SVG_DA: + case GF_SM_LOAD_XSR: + case GF_SM_LOAD_DIMS: + return gf_sm_load_init_svg(load); + + case GF_SM_LOAD_XBL: + return gf_sm_load_init_xbl(load); +#endif +#ifndef GPAC_READ_ONLY + case GF_SM_LOAD_SWF: + return gf_sm_load_init_swf(load); + case GF_SM_LOAD_QT: + return gf_sm_load_init_qt(load); + case GF_SM_LOAD_MP4: + return gf_sm_load_init_isom(load); +#endif + } + return GF_NOT_SUPPORTED; +} + +GF_EXPORT +void gf_sm_load_done(GF_SceneLoader *load) +{ + switch (load->type) { + case GF_SM_LOAD_BT: + case GF_SM_LOAD_VRML: + case GF_SM_LOAD_X3DV: + gf_sm_load_done_bt(load); + break; + case GF_SM_LOAD_XMTA: + case GF_SM_LOAD_X3D: + gf_sm_load_done_xmt(load); + break; +#ifndef GPAC_DISABLE_SVG + case GF_SM_LOAD_SVG_DA: + case GF_SM_LOAD_XSR: + case GF_SM_LOAD_DIMS: + gf_sm_load_done_svg(load); + break; + + case GF_SM_LOAD_XBL: + gf_sm_load_done_xbl(load); + break; +#endif + +#ifndef GPAC_READ_ONLY + case GF_SM_LOAD_SWF: + gf_sm_load_done_swf(load); + break; + case GF_SM_LOAD_QT: + gf_sm_load_done_qt(load); + break; + case GF_SM_LOAD_MP4: + gf_sm_load_done_isom(load); + break; +#endif + } +} + +GF_EXPORT +GF_Err gf_sm_load_run(GF_SceneLoader *load) +{ + switch (load->type) { + case GF_SM_LOAD_BT: + case GF_SM_LOAD_VRML: + case GF_SM_LOAD_X3DV: + return gf_sm_load_run_bt(load); + case GF_SM_LOAD_XMTA: + case GF_SM_LOAD_X3D: + return gf_sm_load_run_xmt(load); +#ifndef GPAC_DISABLE_SVG + case GF_SM_LOAD_SVG_DA: + case GF_SM_LOAD_XSR: + case GF_SM_LOAD_DIMS: + return gf_sm_load_run_svg(load); +#endif +#ifndef GPAC_DISABLE_SVG + case GF_SM_LOAD_XBL: + return gf_sm_load_run_xbl(load); +#endif +#ifndef GPAC_READ_ONLY + case GF_SM_LOAD_SWF: + return gf_sm_load_run_swf(load); + case GF_SM_LOAD_QT: + return gf_sm_load_run_qt(load); + case GF_SM_LOAD_MP4: + return gf_sm_load_run_isom(load); +#endif + default: + return GF_BAD_PARAM; + } +} + diff --git a/src/scene_manager/scene_stats.c b/src/scene_manager/scene_stats.c new file mode 100644 index 0000000..64b115a --- /dev/null +++ b/src/scene_manager/scene_stats.c @@ -0,0 +1,612 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +#ifndef GPAC_READ_ONLY + +struct _statman +{ + GF_SceneStatistics *stats; + GF_List *def_nodes; +}; + +static GF_SceneStatistics *NewSceneStats() +{ + GF_SceneStatistics *tmp; + GF_SAFEALLOC(tmp, GF_SceneStatistics); + tmp->node_stats = gf_list_new(); + tmp->proto_stats = gf_list_new(); + + tmp->max_2d.x = FIX_MIN; + tmp->max_2d.y = FIX_MIN; + tmp->max_3d.x = FIX_MIN; + tmp->max_3d.y = FIX_MIN; + tmp->max_3d.z = FIX_MIN; + tmp->min_2d.x = FIX_MAX; + tmp->min_2d.y = FIX_MAX; + tmp->min_3d.x = FIX_MAX; + tmp->min_3d.y = FIX_MAX; + tmp->min_3d.z = FIX_MAX; + return tmp; +} + +static void ResetStatisitics(GF_SceneStatistics *stat) +{ + while (gf_list_count(stat->node_stats)) { + GF_NodeStats *ptr = (GF_NodeStats *)gf_list_get(stat->node_stats, 0); + gf_list_rem(stat->node_stats, 0); + free(ptr); + } + while (gf_list_count(stat->proto_stats)) { + GF_NodeStats *ptr = (GF_NodeStats *)gf_list_get(stat->proto_stats, 0); + gf_list_rem(stat->proto_stats, 0); + free(ptr); + } + stat->max_2d.x = FIX_MIN; + stat->max_2d.y = FIX_MIN; + stat->max_3d.x = FIX_MIN; + stat->max_3d.y = FIX_MIN; + stat->max_3d.z = FIX_MIN; + stat->min_2d.x = FIX_MAX; + stat->min_2d.y = FIX_MAX; + stat->min_3d.x = FIX_MAX; + stat->min_3d.y = FIX_MAX; + stat->min_3d.z = FIX_MAX; + stat->count_2d = stat->rem_2d = stat->count_3d = stat->rem_3d = stat->count_float = 0; + stat->rem_float = stat->count_color = stat->rem_color = stat->count_2f = stat->count_3f = 0; +} + +static void DeleteStatisitics(GF_SceneStatistics *stat) +{ + ResetStatisitics(stat); + gf_list_del(stat->node_stats); + gf_list_del(stat->proto_stats); + free(stat); +} + +static void StatNode(GF_SceneStatistics *stat, GF_Node *n, Bool isUsed, Bool isDelete, GF_Node *prev) +{ + u32 i; + GF_NodeStats *ptr = NULL; + if (!stat) return; + + if (n->sgprivate->tag == TAG_ProtoNode) { + GF_ProtoInstance *pr = (GF_ProtoInstance *)n; + i=0; + while ((ptr = (GF_NodeStats *)gf_list_enum(stat->proto_stats, &i))) { + if (pr->proto_interface->ID == ptr->tag) break; + ptr = NULL; + } + if (!ptr) { + GF_SAFEALLOC(ptr, GF_NodeStats); + ptr->tag = pr->proto_interface->ID; + ptr->name = gf_sg_proto_get_class_name(pr->proto_interface); + gf_list_add(stat->proto_stats, ptr); + } + } else { + i=0; + while ((ptr = (GF_NodeStats *)gf_list_enum(stat->node_stats, &i))) { + if (n->sgprivate->tag == ptr->tag) break; + ptr = NULL; + } + if (!ptr) { + GF_SAFEALLOC(ptr, GF_NodeStats); + ptr->tag = n->sgprivate->tag; + ptr->name = gf_node_get_class_name(n); + gf_list_add(stat->node_stats, ptr); + } + } + if (isDelete) ptr->nb_del += n->sgprivate->num_instances; + else if (isUsed) ptr->nb_used += 1; + /*this is because the node passes twice in the stat, once on DumpNode and once in replaceALL*/ + else ptr->nb_created += prev ? (prev->sgprivate->num_instances - 1) : 1; +} + +static void StatFixed(GF_SceneStatistics *stat, Fixed v, Bool scale) +{ + u32 int_res, frac_res; + u32 fixv = FIX2INT((v>0?v:-v) * (1<<16)); + s32 intv = (fixv & 0xFFFF0000)>>16; + u32 fracv = fixv & 0x0000FFFF; + + int_res = 0; + while ( (intv >> int_res) ) int_res++; + int_res++; /* signedness */ + + if (fracv) { + frac_res = 1; + while ((fracv << frac_res) & 0x0000FFFF) + frac_res++; + } else { + frac_res = 0; + } + + if (scale) { + if (int_res > stat->scale_int_res_2d) stat->scale_int_res_2d = int_res; + if (frac_res > stat->scale_frac_res_2d) stat->scale_frac_res_2d = frac_res; + } else { + if (int_res > stat->int_res_2d) stat->int_res_2d = int_res; + if (frac_res > stat->frac_res_2d) stat->frac_res_2d = frac_res; + } + if (stat->max_fixed < v) stat->max_fixed = v; + if (stat->min_fixed > v) stat->min_fixed = v; +} + +static void StatSVGPoint(GF_SceneStatistics *stat, SFVec2f *val) +{ + if (!stat) return; + if (stat->max_2d.x < val->x) stat->max_2d.x = val->x; + if (stat->max_2d.y < val->y) stat->max_2d.y = val->y; + if (stat->min_2d.x > val->x) stat->min_2d.x = val->x; + if (stat->min_2d.y > val->y) stat->min_2d.y = val->y; + StatFixed(stat, val->x, 0); + StatFixed(stat, val->y, 0); +} + +static void StatSFVec2f(GF_SceneStatistics *stat, SFVec2f *val) +{ + if (!stat) return; + if (stat->max_2d.x < val->x) stat->max_2d.x = val->x; + if (stat->max_2d.y < val->y) stat->max_2d.y = val->y; + if (stat->min_2d.x > val->x) stat->min_2d.x = val->x; + if (stat->min_2d.y > val->y) stat->min_2d.y = val->y; +} + +static void StatSFVec3f(GF_SceneStatistics *stat, SFVec3f *val) +{ + if (!stat) return; + if (stat->max_3d.x < val->x) stat->max_3d.x = val->x; + if (stat->max_3d.y < val->y) stat->max_3d.y = val->y; + if (stat->max_3d.z < val->z) stat->max_3d.z = val->y; + if (stat->min_3d.x > val->x) stat->min_3d.x = val->x; + if (stat->min_3d.y > val->y) stat->min_3d.y = val->y; + if (stat->min_3d.z > val->z) stat->min_3d.z = val->z; +} + +static void StatField(GF_SceneStatistics *stat, GF_FieldInfo *field) +{ + u32 i; + + switch (field->fieldType) { + case GF_SG_VRML_SFFLOAT: + stat->count_float++; + if (stat->max_fixed < *(SFFloat*)field->far_ptr) + stat->max_fixed = *(SFFloat*)field->far_ptr; + if (stat->min_fixed > *(SFFloat*)field->far_ptr) + stat->min_fixed = *(SFFloat*)field->far_ptr; + break; + case GF_SG_VRML_SFCOLOR: + stat->count_color++; + break; + case GF_SG_VRML_SFVEC2F: + stat->count_2f++; + StatSFVec2f(stat, field->far_ptr); + break; + case GF_SG_VRML_SFVEC3F: + stat->count_3f++; + StatSFVec3f(stat, field->far_ptr); + break; + + case GF_SG_VRML_MFFLOAT: + stat->count_float+= ((MFFloat *)field->far_ptr)->count; + break; + case GF_SG_VRML_MFCOLOR: + stat->count_color+= ((MFColor *)field->far_ptr)->count; + break; + case GF_SG_VRML_MFVEC2F: + { + MFVec2f *mf2d = (MFVec2f *)field->far_ptr; + for (i=0; icount; i++) { + StatSFVec2f(stat, &mf2d->vals[i]); + stat->count_2d ++; + } + } + break; + case GF_SG_VRML_MFVEC3F: + { + MFVec3f *mf3d = (MFVec3f *)field->far_ptr; + for (i=0; icount; i++) { + StatSFVec3f(stat, &mf3d->vals[i]); + stat->count_3d ++; + } + } + break; + } +} + + +static void StatSingleField(GF_SceneStatistics *stat, GF_FieldInfo *field) +{ + switch (field->fieldType) { + case GF_SG_VRML_SFVEC2F: + StatSFVec2f(stat, (SFVec2f *)field->far_ptr); + break; + case GF_SG_VRML_MFVEC3F: + StatSFVec3f(stat, (SFVec3f *)field->far_ptr); + break; + } +} + +static void StatSVGAttribute(GF_SceneStatistics *stat, GF_FieldInfo *field) +{ + u32 i = 0; + + stat->nb_svg_attributes++; + + switch (field->fieldType) { + case SVG_PathData_datatype: + { +#if USE_GF_PATH + SVG_PathData *d = (SVG_PathData *)field->far_ptr; + for (i=0; in_points; i++) { + StatSVGPoint(stat, &(d->points[i])); + stat->count_2d ++; + } +#else + SVG_PathData *d = (SVG_PathData *)field->far_ptr; + for (i=0; ipoints); i++) { + SVG_Point *p = (SVG_Point *)gf_list_get(d->points, i); + StatSVGPoint(stat, (SFVec2f *)p); + stat->count_2d ++; + } +#endif + } + break; + case SVG_ViewBox_datatype: + { + SVG_ViewBox *vB = (SVG_ViewBox *)field->far_ptr; + StatFixed(stat, vB->x, 0); + StatFixed(stat, vB->y, 0); + StatFixed(stat, vB->width, 0); + StatFixed(stat, vB->height, 0); + } + break; + case SVG_Points_datatype: + case SVG_Coordinates_datatype: + { + GF_List *points = *((GF_List **)field->far_ptr); + for (i=0; icount_2d ++; + } + } + break; + case SVG_Transform_datatype: + { + GF_Matrix2D *mx = &((SVG_Transform *)field->far_ptr)->mat; + if (!gf_mx2d_is_identity(*mx) && !(!mx->m[0] && !mx->m[1] && !mx->m[3] && !mx->m[4])) { + StatFixed(stat, mx->m[0], 1); + StatFixed(stat, mx->m[1], 1); + StatFixed(stat, mx->m[3], 1); + StatFixed(stat, mx->m[4], 1); + StatFixed(stat, mx->m[2], 0); + StatFixed(stat, mx->m[5], 0); + } + } + break; + case SVG_Motion_datatype: + { + GF_Matrix2D *mx = (GF_Matrix2D *)field->far_ptr; + if (!gf_mx2d_is_identity(*mx) && !(!mx->m[0] && !mx->m[1] && !mx->m[3] && !mx->m[4])) { + StatFixed(stat, mx->m[0], 1); + StatFixed(stat, mx->m[1], 1); + StatFixed(stat, mx->m[3], 1); + StatFixed(stat, mx->m[4], 1); + StatFixed(stat, mx->m[2], 0); + StatFixed(stat, mx->m[5], 0); + } + } + break; + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + StatFixed(stat, ((SVG_Number *)field->far_ptr)->value, 0); + break; + } +} + +static void StatRemField(GF_SceneStatistics *stat, u32 fieldType, GF_FieldInfo *field) +{ + u32 count = 1; + if (field) count = ((GenMFField*)field->far_ptr)->count; + switch (fieldType) { + case GF_SG_VRML_MFFLOAT: + stat->rem_float += count; + break; + case GF_SG_VRML_SFCOLOR: + stat->rem_color += count; + break; + case GF_SG_VRML_MFVEC2F: + stat->rem_2d += count; + break; + case GF_SG_VRML_MFVEC3F: + stat->rem_3d += count; + break; + } +} + + +Bool StatIsUSE(GF_StatManager *st, GF_Node *n) +{ + u32 i; + GF_Node *ptr; + if (!n || !gf_node_get_id(n) ) return 0; + i=0; + while ((ptr = (GF_Node*)gf_list_enum(st->def_nodes, &i))) { + if (ptr == n) return 1; + } + gf_list_add(st->def_nodes, n); + return 0; +} + +static GF_Err StatNodeGraph(GF_StatManager *st, GF_Node *n) +{ + GF_Node *child, *clone; + GF_ChildNodeItem *list; + u32 i, count; + GF_FieldInfo field, clone_field; + + if (!n) return GF_OK; + StatNode(st->stats, n, StatIsUSE(st, n), 0, NULL); + + if (n->sgprivate->tag != TAG_ProtoNode) { + clone = gf_node_new(n->sgprivate->scenegraph, n->sgprivate->tag); + } else { + clone = gf_sg_proto_create_node(n->sgprivate->scenegraph, ((GF_ProtoInstance *)n)->proto_interface, NULL); + } + gf_node_register(clone, NULL); + +#ifndef GPAC_DISABLE_SVG + if ((n->sgprivate->tag>= GF_NODE_RANGE_FIRST_SVG) && (n->sgprivate->tag<= GF_NODE_RANGE_LAST_SVG)) { + GF_ChildNodeItem *list = ((SVG_Element *)n)->children; + GF_DOMAttribute *atts = ((GF_DOMNode*)n)->attributes; + while (atts) { + field.far_ptr = atts->data; + field.fieldType = atts->data_type; + field.fieldIndex = atts->tag; + field.name = NULL; + StatSVGAttribute(st->stats, &field); + + atts = atts->next; + } + while (list) { + StatNodeGraph(st, list->node); + list = list->next; + } + } else +#endif + if (n->sgprivate->tag == TAG_DOMText) { + } else if (n->sgprivate->tag == TAG_DOMFullNode) { + } else { + count = gf_node_get_field_count(n); + + for (i=0; inode); + list = list->next; + } + break; + default: + gf_node_get_field(clone, i, &clone_field); + if (!gf_sg_vrml_field_equal(clone_field.far_ptr, field.far_ptr, field.fieldType)) { + StatField(st->stats, &field); + } + break; + } + } + } + gf_node_unregister(clone, NULL); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_sm_stats_for_command(GF_StatManager *stat, GF_Command *com) +{ + GF_FieldInfo field; + GF_Err e; + GF_ChildNodeItem *list; + GF_CommandField *inf = NULL; + if (gf_list_count(com->command_fields)) + inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); + + if (!com || !stat) return GF_BAD_PARAM; + switch (com->tag) { + case GF_SG_SCENE_REPLACE: + if (com->node) StatNodeGraph(stat, com->node); + break; + case GF_SG_NODE_REPLACE: + if (inf && inf->new_node) StatNodeGraph(stat, inf->new_node); + break; + case GF_SG_FIELD_REPLACE: + if (!inf) return GF_OK; + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + + switch (field.fieldType) { + case GF_SG_VRML_SFNODE: + if (inf->new_node) StatNodeGraph(stat, inf->new_node); + break; + case GF_SG_VRML_MFNODE: + list = * ((GF_ChildNodeItem**) inf->field_ptr); + while (list) { + StatNodeGraph(stat, list->node); + list = list->next; + } + break; + default: + field.far_ptr = inf->field_ptr; + StatField(stat->stats, &field); + break; + } + break; + case GF_SG_INDEXED_REPLACE: + if (!inf) return GF_OK; + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + + if (field.fieldType == GF_SG_VRML_MFNODE) { + StatNodeGraph(stat, inf->new_node); + } else { + field.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + field.far_ptr = inf->field_ptr; + StatSingleField(stat->stats, &field); + } + break; + case GF_SG_NODE_DELETE: + if (com->node) StatNode(stat->stats, com->node, 0, 1, NULL); + break; + case GF_SG_INDEXED_DELETE: + if (!inf) return GF_OK; + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + + /*then we need special handling in case of a node*/ + if (gf_sg_vrml_get_sf_type(field.fieldType) == GF_SG_VRML_SFNODE) { + GF_Node *n = gf_node_list_get_child( * (GF_ChildNodeItem **) field.far_ptr, inf->pos); + if (n) StatNode(stat->stats, n, 0, 1, NULL); + } else { + StatRemField(stat->stats, inf->fieldType, NULL); + } + break; + case GF_SG_NODE_INSERT: + if (inf && inf->new_node) StatNodeGraph(stat, inf->new_node); + break; + case GF_SG_INDEXED_INSERT: + if (!inf) return GF_OK; + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + + /*rescale the MFField and parse the SFField*/ + if (field.fieldType != GF_SG_VRML_MFNODE) { + field.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); + field.far_ptr = inf->field_ptr; + StatSingleField(stat->stats, &field); + } else { + if (inf->new_node) StatNodeGraph(stat, inf->new_node); + } + break; + case GF_SG_ROUTE_REPLACE: + case GF_SG_ROUTE_DELETE: + case GF_SG_ROUTE_INSERT: + return GF_OK; + default: + return GF_BAD_PARAM; + } + return GF_OK; +} + +static GF_Err gf_sm_stat_au(GF_List *commandList, GF_StatManager *st) +{ + u32 i, count; + count = gf_list_count(commandList); + for (i=0; istreams)) { + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(sm->streams, &i))) { + GF_AUContext *au; + if (sc->streamType != GF_STREAM_SCENE) continue; + + if (!stat->stats->base_layer) + stat->stats->base_layer = sc; + + j=0; + while ((au = (GF_AUContext*)gf_list_enum(sc->AUs, &j))) { + e = gf_sm_stat_au(au->commands, stat); + if (e) return e; + } + } + } else { /* No scene stream: e.g. SVG */ + if (sm->scene_graph) gf_sm_stats_for_graph(stat, sm->scene_graph); + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_sm_stats_for_graph(GF_StatManager *stat, GF_SceneGraph *sg) +{ + if (!stat || !sg) return GF_BAD_PARAM; + return StatNodeGraph(stat, sg->RootNode); +} + +/*creates new stat handler*/ +GF_EXPORT +GF_StatManager *gf_sm_stats_new() +{ + GF_StatManager *sm = (GF_StatManager *)malloc(sizeof(GF_StatManager)); + sm->def_nodes = gf_list_new(); + sm->stats = NewSceneStats(); + return sm; + +} +/*deletes stat object returned by one of the above functions*/ +GF_EXPORT +void gf_sm_stats_del(GF_StatManager *stat) +{ + gf_list_del(stat->def_nodes); + DeleteStatisitics(stat->stats); + free(stat); +} + +GF_EXPORT +GF_SceneStatistics *gf_sm_stats_get(GF_StatManager *stat) +{ + return stat->stats; +} + +GF_EXPORT +void gf_sm_stats_reset(GF_StatManager *stat) +{ + if (!stat) return; + ResetStatisitics(stat->stats); +} + +#endif + diff --git a/src/scene_manager/swf_bifs.c b/src/scene_manager/swf_bifs.c new file mode 100644 index 0000000..a0d870c --- /dev/null +++ b/src/scene_manager/swf_bifs.c @@ -0,0 +1,2252 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include +#include +#include +#include +#include + +#ifndef GPAC_READ_ONLY + +#define SWF_TEXT_SCALE (1/1024.0f) + +typedef struct +{ + u32 btn_id; + u32 sprite_up_id; +} S2BBtnRec; + +static GF_Err s2b_insert_symbol(SWFReader *read, GF_Node *n) +{ + GF_Command *com; + GF_CommandField *f; + + if (read->flags & GF_SM_SWF_STATIC_DICT) { + M_Switch *par = (M_Switch *)gf_sg_find_node_by_name(read->load->scene_graph, "DICTIONARY"); + gf_node_list_add_child(&par->choice, n); + gf_node_register((GF_Node *)n, (GF_Node *)par); + } else { + com = gf_sg_command_new(read->load->scene_graph, GF_SG_INDEXED_INSERT); + com->node = gf_sg_find_node_by_name(read->load->scene_graph, "DICTIONARY"); + gf_node_register(com->node, NULL); + f = gf_sg_command_field_new(com); + f->field_ptr = &f->new_node; + f->fieldType = GF_SG_VRML_SFNODE; + f->fieldIndex = 0; /*choice index*/ + f->pos = -1; + f->new_node = n; + gf_node_register(f->new_node, NULL); + if (read->bifs_dict_au) + gf_list_add(read->bifs_dict_au->commands, com); + else + gf_list_add(read->bifs_au->commands, com); + } + return GF_OK; +} + +static GF_Node *s2b_get_node(SWFReader *read, u32 ID) +{ + GF_Node *n; + char szDEF[1024]; + sprintf(szDEF, "Shape%d", ID); + n = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + if (n) return n; + sprintf(szDEF, "Text%d", ID); + n = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + if (n) return n; + sprintf(szDEF, "Button%d", ID); + n = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + if (n) return n; + return NULL; +} + +static GF_Node *s2b_new_node(SWFReader *read, u32 tag) +{ + GF_Node *n = gf_node_new(read->load->scene_graph, tag); + if (n) gf_node_init(n); + return n; +} + + +static GF_Node *s2b_get_matrix(SWFReader *read, GF_Matrix2D *mat) +{ + M_TransformMatrix2D *tm = (M_TransformMatrix2D *)s2b_new_node(read, TAG_MPEG4_TransformMatrix2D); + tm->mxx = mat->m[0]; + tm->mxy = mat->m[1]; + tm->tx = mat->m[2]; + tm->myx = mat->m[3]; + tm->myy = mat->m[4]; + tm->ty = mat->m[5]; + return (GF_Node *) tm; +} + +static GF_Node *s2b_get_color_matrix(SWFReader *read, GF_ColorMatrix *cmat) +{ + M_ColorTransform *ct = (M_ColorTransform*)s2b_new_node(read, TAG_MPEG4_ColorTransform); + ct->mrr = cmat->m[0]; + ct->mrg = cmat->m[1]; + ct->mrb = cmat->m[2]; + ct->mra = cmat->m[3]; + ct->tr = cmat->m[4]; + ct->mgr = cmat->m[5]; + ct->mgg = cmat->m[6]; + ct->mgb = cmat->m[7]; + ct->mga = cmat->m[8]; + ct->tg = cmat->m[9]; + ct->mbr = cmat->m[10]; + ct->mbg = cmat->m[11]; + ct->mbb = cmat->m[12]; + ct->mba = cmat->m[13]; + ct->tb = cmat->m[14]; + ct->mar = cmat->m[15]; + ct->mag = cmat->m[16]; + ct->mab = cmat->m[17]; + ct->maa = cmat->m[18]; + ct->ta = cmat->m[19]; + return (GF_Node *) ct; +} + + +static SFColor s2b_get_color(u32 ARGB) +{ + SFColor val; + val.red = INT2FIX((ARGB>>16)&0xFF) / 255; + val.green = INT2FIX((ARGB>>8)&0xFF) / 255; + val.blue = INT2FIX((ARGB)&0xFF) / 255; + return val; +} + +static Fixed s2b_get_alpha(u32 ARGB) +{ + return INT2FIX((ARGB>>24)&0xFF) / 255; +} + +static void s2b_insert_appearance(SWFReader *read, GF_Node *app) +{ + M_Shape *s = (M_Shape *) s2b_new_node(read, TAG_MPEG4_Shape); + s->appearance = app; + gf_node_register(app, (GF_Node *) s); + + s2b_insert_symbol(read, (GF_Node *)s); +} + +static Bool s2b_same_color(SFColor c1, SFColor c2) +{ + if (c1.red != c2.red) return 0; + if (c1.green != c2.green) return 0; + if (c1.blue != c2.blue) return 0; + return 1; +} + +static GF_Node *s2b_get_appearance(SWFReader *read, GF_Node *parent, u32 fill_col, Fixed line_width, u32 l_col) +{ + char szDEF[1024]; + u32 ID, i; + SFColor fc, lc; + Fixed fill_transp, line_transp; + M_Appearance *app; + M_Material2D *mat; + + fc = s2b_get_color(fill_col); + fill_transp = FIX_ONE - s2b_get_alpha(fill_col); + if (fill_transp<0) fill_transp=0; + lc = s2b_get_color(l_col); + line_transp = FIX_ONE - s2b_get_alpha(l_col); + if (line_transp<0) line_transp=0; + + i=0; + while ((app = (M_Appearance*)gf_list_enum(read->apps, &i))) { + mat = (M_Material2D *)app->material; + if (!line_width) { + if (mat->lineProps || !mat->filled) continue; + } else { + if (!mat->lineProps) continue; + if (!s2b_same_color(((M_LineProperties *)mat->lineProps)->lineColor, lc)) continue; + if (((M_LineProperties *)mat->lineProps)->width != line_width) continue; + } + if (!mat->filled && fill_col) continue; + if (mat->filled) { + if (!fill_col) continue; + if (mat->transparency != fill_transp) continue; + if (!s2b_same_color(mat->emissiveColor, fc)) continue; + } + /*OK same appearance let's go*/ + gf_node_register((GF_Node *)app, parent); + return (GF_Node *)app; + } + + app = (M_Appearance *) s2b_new_node(read, TAG_MPEG4_Appearance); + app->material = s2b_new_node(read, TAG_MPEG4_Material2D); + gf_node_register(app->material, (GF_Node *)app); + ((M_Material2D *)app->material)->filled = 0; + + if (fill_col) { + ((M_Material2D *)app->material)->filled = 1; + ((M_Material2D *)app->material)->emissiveColor = fc; + ((M_Material2D *)app->material)->transparency = fill_transp; + } + if (line_width && l_col) { + if (read->flags & GF_SM_SWF_SCALABLE_LINE) { + M_XLineProperties *lp = (M_XLineProperties *) s2b_new_node(read, TAG_MPEG4_XLineProperties); + ((M_Material2D *)app->material)->lineProps = (GF_Node *) lp; + lp->width = line_width; + lp->lineColor = lc; + lp->isScalable = 1; + lp->transparency = line_transp; + gf_node_register((GF_Node *)lp, app->material); + } else { + M_LineProperties *lp = (M_LineProperties *) s2b_new_node(read, TAG_MPEG4_LineProperties); + ((M_Material2D *)app->material)->lineProps = (GF_Node *) lp; + lp->width = line_width; + lp->lineColor = lc; + gf_node_register((GF_Node *)lp, app->material); + } + } + + gf_node_register((GF_Node *)app, parent); + + if (read->load->swf_import_flags & GF_SM_SWF_REUSE_APPEARANCE) { + sprintf(szDEF, "FILLAPP_%d", gf_list_count(read->apps)); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + + gf_node_set_id((GF_Node *)app, ID, szDEF); + s2b_insert_appearance(read, (GF_Node *)app); + gf_list_add(read->apps, app); + } + return (GF_Node *) app; +} + + +static GF_Rect s2b_get_center_bounds(SWFShape *shape, SWFShapeRec *srec) +{ + GF_Rect rc; + u32 i; + Fixed xm, ym, xM, yM; + xM = yM = FIX_MIN; + xm = ym = FIX_MAX; + + for (i=0; ipath->nbPts; i++) { + if (srec->path->pts[i].x<=xm) xm = srec->path->pts[i].x; + if (srec->path->pts[i].x>=xM) xM = srec->path->pts[i].x; + if (srec->path->pts[i].y<=ym) ym = srec->path->pts[i].y; + if (srec->path->pts[i].y>=yM) yM = srec->path->pts[i].y; + } + rc.width = xM-xm; + rc.height = yM-ym; + rc.x = xm; + rc.y = yM; + return rc; +} + +static GF_Node *s2b_get_gradient(SWFReader *read, GF_Node *parent, SWFShape *shape, SWFShapeRec *srec) +{ + Bool is_radial, has_alpha; + GF_Rect rc; + GF_Matrix2D mx; + u32 i; + MFFloat *keys; + MFColor *values; + GF_FieldInfo info; + M_Appearance *app = (M_Appearance *) s2b_new_node(read, TAG_MPEG4_Appearance); + gf_node_register((GF_Node *)app, parent); + app->material = s2b_new_node(read, TAG_MPEG4_Material2D); + gf_node_register(app->material, (GF_Node *)app); + ((M_Material2D *)app->material)->filled = 1; + + is_radial = (srec->type==0x12) ? 1 : 0; + app->texture = s2b_new_node(read, is_radial ? TAG_MPEG4_RadialGradient : TAG_MPEG4_LinearGradient); + gf_node_register((GF_Node *) app->texture, (GF_Node *) app); + + /*set keys*/ + gf_node_get_field_by_name(app->texture, "key", &info); + gf_sg_vrml_mf_alloc(info.far_ptr, info.fieldType, srec->nbGrad); + keys = (MFFloat *)info.far_ptr; + for (i=0; inbGrad; i++) { + keys->vals[i] = INT2FIX(srec->grad_ratio[i])/255; + } + + /*set colors*/ + gf_node_get_field_by_name(app->texture, "keyValue", &info); + gf_sg_vrml_mf_alloc(info.far_ptr, info.fieldType, srec->nbGrad); + values = (MFColor *)info.far_ptr; + has_alpha = 0; + for (i=0; inbGrad; i++) { + values->vals[i] = s2b_get_color(srec->grad_col[i]); + if (s2b_get_alpha(srec->grad_col[i]) != FIX_ONE) has_alpha = 1; + } + /*set opacity*/ + if (has_alpha) { + gf_node_get_field_by_name(app->texture, "opacity", &info); + gf_sg_vrml_mf_alloc(info.far_ptr, info.fieldType, srec->nbGrad); + keys = (MFFloat *)info.far_ptr; + for (i=0; inbGrad; i++) { + keys->vals[i] = s2b_get_alpha(srec->grad_col[i]); + } + /*and remove material !!*/ + ((M_Material2D *)app->material)->filled = 0; + ((M_Material2D *)app->material)->lineProps = s2b_new_node(read, TAG_MPEG4_LineProperties);; + ((M_LineProperties *)((M_Material2D *)app->material)->lineProps)->width = 0; + gf_node_register(((M_Material2D *)app->material)->lineProps, app->material); + } + + rc = s2b_get_center_bounds(shape, srec); + + gf_mx2d_init(mx); + mx.m[0] = gf_invfix(rc.width); + mx.m[2] = - gf_divfix(rc.x, rc.width); + mx.m[4] = gf_invfix(rc.height); + mx.m[5] = FIX_ONE - gf_divfix(rc.y, rc.height); + + gf_mx2d_pre_multiply(&mx, &srec->mat); + + /*define gradient in SWF pixel coordinates*/ + if (is_radial ) { + gf_node_get_field_by_name(app->texture, "center", &info); + ((SFVec2f*)info.far_ptr)->x = 0; + ((SFVec2f*)info.far_ptr)->y = 0; + + gf_node_get_field_by_name(app->texture, "radius", &info); + *((SFFloat*)info.far_ptr) = FLT2FIX(819.20); + } else { + gf_node_get_field_by_name(app->texture, "startPoint", &info); + ((SFVec2f*)info.far_ptr)->x = FLT2FIX(-819.20); + + gf_node_get_field_by_name(app->texture, "endPoint", &info); + ((SFVec2f*)info.far_ptr)->x = FLT2FIX(819.20); + } + + /*matrix from local coordinates to texture coordiantes (Y-flip for BIFS texture coordinates)*/ + gf_mx2d_init(mx); + mx.m[0] = gf_invfix(rc.width); + mx.m[2] = - gf_divfix(rc.x, rc.width); + mx.m[4] = gf_invfix(rc.height); + mx.m[5] = FIX_ONE - gf_divfix(rc.y, rc.height); + /*pre-multiply SWF->local coords matrix*/ + gf_mx2d_pre_multiply(&mx, &srec->mat); + + gf_node_get_field_by_name(app->texture, "transform", &info); + *((GF_Node **)info.far_ptr) = s2b_get_matrix(read, &mx); + gf_node_register(*((GF_Node **)info.far_ptr), app->texture); + return (GF_Node *) app; +} + +static GF_Node *s2b_get_bitmap(SWFReader *read, GF_Node *parent, SWFShape *shape, SWFShapeRec *srec) +{ + GF_Matrix2D mx; + GF_Node *bmp; + GF_FieldInfo info; + M_Appearance *app; + char szDEF[100]; + + sprintf(szDEF, "Bitmap%d", srec->img_id); + bmp = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + if (!bmp) return NULL; + app = (M_Appearance *) s2b_new_node(read, TAG_MPEG4_Appearance); + gf_node_register((GF_Node *)app, parent); + app->material = s2b_new_node(read, TAG_MPEG4_Material2D); + gf_node_register(app->material, (GF_Node *)app); + ((M_Material2D *)app->material)->filled = 1; + + app->texture = bmp; + gf_node_register(bmp, (GF_Node *)app); + + gf_mx2d_init(mx); + gf_mx2d_add_scale(&mx, FIX_ONE, -FIX_ONE); +// gf_mx2d_add_scale(&mx, SWF_TWIP_SCALE, -SWF_TWIP_SCALE); +// gf_mx2d_pre_multiply(&mx, &srec->mat); + + gf_node_get_field_by_name((GF_Node*)app, "textureTransform", &info); + *((GF_Node **)info.far_ptr) = s2b_get_matrix(read, &mx); + gf_node_register(*((GF_Node **)info.far_ptr), (GF_Node*)app); + return (GF_Node *) app; +} + +static void s2b_set_appearance(SWFReader *read, SWFShape *shape, M_Shape *n, SWFShapeRec *srec, Bool is_fill) +{ + /*get regular appearance reuse*/ + if (is_fill) { + switch (srec->type) { + /*solid/alpha fill*/ + case 0x00: + n->appearance = s2b_get_appearance(read, (GF_Node *) n, srec->solid_col, 0, 0); + break; + case 0x10: + case 0x12: + if (read->flags & GF_SM_SWF_NO_GRADIENT) { + u32 col = srec->grad_col[srec->nbGrad/2]; + col |= 0xFF000000; + n->appearance = s2b_get_appearance(read, (GF_Node *) n, col, 0, 0); + } else { + n->appearance = s2b_get_gradient(read, (GF_Node *) n, shape, srec); + } + break; + case 0x40: + case 0x41: + case 0x42: + case 0x43: + n->appearance = s2b_get_bitmap(read, (GF_Node *) n, shape, srec); + break; + default: + swf_report(read, GF_NOT_SUPPORTED, "fill_style %x not supported", srec->type); + break; + } + } else { + n->appearance = s2b_get_appearance(read, (GF_Node *) n, 0, srec->width, srec->solid_col); + } +} + +/*translate a flash sub shape with only one path (eg one looking style) to a BIFS Shape node*/ +static GF_Node *s2b_shape_to_curve2d(SWFReader *read, SWFShape *shape, SWFShapeRec *srec, Bool is_fill, M_Coordinate2D *c) +{ + u32 pt_idx, i; + Bool use_xcurve; + void *fptr; + SFVec2f ct1, ct2, ct, pt; + MFInt32 *types, *idx; + M_Coordinate2D *points; + GF_Node *ic2d = NULL; + M_Shape *n = (M_Shape *) s2b_new_node(read, TAG_MPEG4_Shape); + + s2b_set_appearance(read, shape, n, srec, is_fill); + + use_xcurve = (read->flags & GF_SM_SWF_QUAD_CURVE) ? 1 : 0; + if (c) { + GF_FieldInfo info; + ic2d = gf_sg_proto_create_instance(read->load->scene_graph, gf_sg_find_proto(read->load->scene_graph, 0, "IndexedCurve2D")); + points = c; + + gf_node_get_field_by_name(ic2d, "type", &info); + types = (MFInt32 *)info.far_ptr; + + gf_node_get_field_by_name(ic2d, "coordIndex", &info); + idx = (MFInt32 *)info.far_ptr; + + gf_node_get_field_by_name(ic2d, "coord", &info); + *(GF_Node **)info.far_ptr = (GF_Node *)c; + gf_node_register((GF_Node *)c, ic2d); + + n->geometry = ic2d; + gf_node_register(ic2d, (GF_Node *)n); + } else { + M_Curve2D *curve; + if (use_xcurve) { + curve = (M_Curve2D *) s2b_new_node(read, TAG_MPEG4_XCurve2D); + } else { + curve = (M_Curve2D *) s2b_new_node(read, TAG_MPEG4_Curve2D); + } + points = (M_Coordinate2D *) s2b_new_node(read, TAG_MPEG4_Coordinate2D); + curve->point = (GF_Node *) points; + + gf_node_register((GF_Node *) points, (GF_Node *) curve); + curve->fineness = FIX_ONE; + types = &curve->type; + idx = NULL; + n->geometry = (GF_Node *) curve; + gf_node_register((GF_Node *) curve, (GF_Node *)n); + } + + + assert(srec->path->nbType); + + pt_idx = 0; + for (i=0; ipath->nbType; i++) { + switch (srec->path->types[i]) { + /*moveTo*/ + case 0: + /*first moveTo implicit in BIFS*/ + if (i) { + gf_sg_vrml_mf_append(types, GF_SG_VRML_MFINT32, &fptr); + *((SFInt32 *)fptr) = 0; + } + if (c) { + gf_sg_vrml_mf_append(idx, GF_SG_VRML_MFINT32, &fptr); + *((SFInt32 *)fptr) = srec->path->idx[pt_idx]; + } else { + gf_sg_vrml_mf_append(&points->point, GF_SG_VRML_MFVEC2F, &fptr); + ((SFVec2f *)fptr)->x = srec->path->pts[pt_idx].x; + ((SFVec2f *)fptr)->y = srec->path->pts[pt_idx].y; + } + pt_idx++; + break; + /*lineTo*/ + case 1: + gf_sg_vrml_mf_append(types, GF_SG_VRML_MFINT32, &fptr); + *((SFInt32 *)fptr) = 1; + if (c) { + gf_sg_vrml_mf_append(idx, GF_SG_VRML_MFINT32, &fptr); + *((SFInt32 *)fptr) = srec->path->idx[pt_idx]; + } else { + gf_sg_vrml_mf_append(&points->point, GF_SG_VRML_MFVEC2F, &fptr); + ((SFVec2f *)fptr)->x = srec->path->pts[pt_idx].x; + ((SFVec2f *)fptr)->y = srec->path->pts[pt_idx].y; + } + pt_idx++; + break; + /*curveTo*/ + case 2: + /*XCurve2D has quad arcs*/ + if (c || use_xcurve) { + gf_sg_vrml_mf_append(types, GF_SG_VRML_MFINT32, &fptr); + *((SFInt32 *)fptr) = 7; + + if (c) { + gf_sg_vrml_mf_append(idx, GF_SG_VRML_MFINT32, &fptr); + *((SFInt32 *)fptr) = srec->path->idx[pt_idx]; + gf_sg_vrml_mf_append(idx, GF_SG_VRML_MFINT32, &fptr); + *((SFInt32 *)fptr) = srec->path->idx[pt_idx+1]; + } else { + gf_sg_vrml_mf_append(&points->point, GF_SG_VRML_MFVEC2F, &fptr); + ((SFVec2f *)fptr)->x = srec->path->pts[pt_idx].x; + ((SFVec2f *)fptr)->y = srec->path->pts[pt_idx].y; + gf_sg_vrml_mf_append(&points->point, GF_SG_VRML_MFVEC2F, &fptr); + ((SFVec2f *)fptr)->x = srec->path->pts[pt_idx+1].x; + ((SFVec2f *)fptr)->y = srec->path->pts[pt_idx+1].y; + } + + pt_idx+=2; + } else { + gf_sg_vrml_mf_append(types, GF_SG_VRML_MFINT32, &fptr); + *((SFInt32 *)fptr) = 2; + /*recompute cubic from quad*/ + ct.x = srec->path->pts[pt_idx].x; + ct.y = srec->path->pts[pt_idx].y; + pt.x = srec->path->pts[pt_idx-1].x; + pt.y = srec->path->pts[pt_idx-1].y; + ct1.x = pt.x + 2*(ct.x - pt.x)/3; + ct1.y = pt.y + 2*(ct.y - pt.y)/3; + ct.x = srec->path->pts[pt_idx+1].x; + ct.y = srec->path->pts[pt_idx+1].y; + ct2.x = ct1.x + (ct.x - pt.x) / 3; + ct2.y = ct1.y + (ct.y - pt.y) / 3; + + gf_sg_vrml_mf_append(&points->point, GF_SG_VRML_MFVEC2F, &fptr); + ((SFVec2f *)fptr)->x = ct1.x; + ((SFVec2f *)fptr)->y = ct1.y; + gf_sg_vrml_mf_append(&points->point, GF_SG_VRML_MFVEC2F, &fptr); + ((SFVec2f *)fptr)->x = ct2.x; + ((SFVec2f *)fptr)->y = ct2.y; + gf_sg_vrml_mf_append(&points->point, GF_SG_VRML_MFVEC2F, &fptr); + ((SFVec2f *)fptr)->x = ct.x; + ((SFVec2f *)fptr)->y = ct.y; + pt_idx+=2; + } + break; + } + } + + if (ic2d) gf_node_init(ic2d); + + return (GF_Node *) n; +} + +static void s2b_merge_curve2d(M_Curve2D *s, M_Curve2D *tomerge) +{ + u32 i, pt_idx, j; + SFVec2f pt; + void *ptr; + M_Coordinate2D *dest, *orig; + dest = (M_Coordinate2D *) s->point; + orig = (M_Coordinate2D *) tomerge->point; + + if (!tomerge->type.count) return; + if (!orig->point.count) return; + pt = orig->point.vals[0]; + + if (s->type.vals[s->type.count - 1] == 0) { + dest->point.vals[dest->point.count - 1] = pt; + } else { + gf_sg_vrml_mf_append(&s->type, GF_SG_VRML_MFINT32, &ptr); + *((SFInt32 *)ptr) = 0; + gf_sg_vrml_mf_append(&dest->point, GF_SG_VRML_MFVEC2F, &ptr); + *((SFVec2f *)ptr) = pt; + } + + i = 0; + if (tomerge->type.vals[0] == 0) i=1; + pt_idx = 1; + + for (; itype.count; i++) { + switch (tomerge->type.vals[i]) { + case 0: + if (s->type.vals[s->type.count - 1] == 0) { + dest->point.vals[dest->point.count - 1] = pt; + } else { + gf_sg_vrml_mf_append(&s->type, GF_SG_VRML_MFINT32, &ptr); + *((SFInt32 *)ptr) = 0; + gf_sg_vrml_mf_append(&dest->point, GF_SG_VRML_MFVEC2F, &ptr); + *((SFVec2f *)ptr) = orig->point.vals[pt_idx]; + } + pt_idx++; + break; + case 1: + gf_sg_vrml_mf_append(&s->type, GF_SG_VRML_MFINT32, &ptr); + *((SFInt32 *)ptr) = 1; + gf_sg_vrml_mf_append(&dest->point, GF_SG_VRML_MFVEC2F, &ptr); + *((SFVec2f *)ptr) = orig->point.vals[pt_idx]; + pt_idx++; + break; + case 2: + gf_sg_vrml_mf_append(&s->type, GF_SG_VRML_MFINT32, &ptr); + *((SFInt32 *)ptr) = 2; + for (j=0; j<3; j++) { + gf_sg_vrml_mf_append(&dest->point, GF_SG_VRML_MFVEC2F, &ptr); + *((SFVec2f *)ptr) = orig->point.vals[pt_idx]; + pt_idx++; + } + break; + case 7: + gf_sg_vrml_mf_append(&s->type, GF_SG_VRML_MFINT32, &ptr); + *((SFInt32 *)ptr) = 7; + for (j=0; j<2; j++) { + gf_sg_vrml_mf_append(&dest->point, GF_SG_VRML_MFVEC2F, &ptr); + *((SFVec2f *)ptr) = orig->point.vals[pt_idx]; + pt_idx++; + } + break; + } + } +} + +static void s2b_insert_shape(M_OrderedGroup *og, M_Shape *n, Bool is_proto) +{ + M_Shape *prev; + GF_ChildNodeItem *l = og->children; + if (!is_proto) { + while (l) { + prev = (M_Shape*)l->node; + if (prev->appearance == n->appearance) { + s2b_merge_curve2d( (M_Curve2D *)prev->geometry, (M_Curve2D *)n->geometry); + gf_node_register((GF_Node *)n, NULL); + gf_node_unregister((GF_Node *)n, NULL); + return; + } + l = l->next; + } + } + gf_node_insert_child((GF_Node *)og, (GF_Node *)n, -1); + gf_node_register((GF_Node *) n, (GF_Node *) og); +} + +static void s2b_insert_rec_in_coord(M_Coordinate2D *c, SWFShapeRec *srec) +{ + u32 i, j; + srec->path->idx = malloc(sizeof(u32)*srec->path->nbPts); + + for (i=0; ipath->nbPts; i++) { + for (j=0; jpoint.count; j++) { + if ( (c->point.vals[j].x == srec->path->pts[i].x) && (c->point.vals[j].y == srec->path->pts[i].y)) { + break; + } + } + if (j==c->point.count) { + c->point.count++; + c->point.vals = realloc(c->point.vals, sizeof(SFVec2f)*c->point.count); + c->point.vals[j] = srec->path->pts[i]; + } + srec->path->idx[i] = j; + } +} + +/*translates flash to BIFS shapes*/ +static GF_Err swf_bifs_define_shape(SWFReader *read, SWFShape *shape, SWFFont *parent_font, Bool last_sub_shape) +{ + GF_Node *n; + GF_Node *og; + M_Coordinate2D *c; + char szDEF[1024]; + u32 ID; + u32 i; + SWFShapeRec *srec; + + og = read->cur_shape; + /*we need a grouping node*/ + if (!read->cur_shape) { + + /*empty shape - for fonts, not a mistake, that's likelly space char*/ + if (!shape) { + if (!parent_font) + return GF_OK; + n = s2b_new_node(read, TAG_MPEG4_Shape); + } + /*direct match, no top group*/ + else if (last_sub_shape && (gf_list_count(shape->fill_left) + gf_list_count(shape->lines)<=1)) { + Bool is_fill = 1; + srec = (SWFShapeRec*)gf_list_get(shape->fill_left, 0); + if (!srec) { + srec = (SWFShapeRec*)gf_list_get(shape->lines, 0); + is_fill = 0; + } + if (!srec) { + n = s2b_new_node(read, TAG_MPEG4_Shape); + } else { + n = s2b_shape_to_curve2d(read, shape, srec, is_fill, NULL); + } + } else { + og = s2b_new_node(read, TAG_MPEG4_OrderedGroup); + n = og; + } + /*register*/ + + if (n) { + if (parent_font) { + gf_list_add(parent_font->glyphs, n); + gf_node_register(n, NULL); + } else { + sprintf(szDEF, "Shape%d", shape->ID); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id(n, ID, szDEF); + + s2b_insert_symbol(read, n); + } + } + if (!og) return GF_OK; + } + + c = NULL; + if (read->flags & GF_SM_SWF_USE_IC2D) { + c = (M_Coordinate2D *)gf_node_new(read->load->scene_graph, TAG_MPEG4_Coordinate2D); + sprintf(szDEF, "ShapePts%d", shape->ID); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id((GF_Node*)c, ID, szDEF); + } + + i=0; + while ((srec = (SWFShapeRec*)gf_list_enum(shape->fill_left, &i))) { + if (c) + s2b_insert_rec_in_coord(c, srec); + + n = s2b_shape_to_curve2d(read, shape, srec, 1, c); + if (n) s2b_insert_shape((M_OrderedGroup*)og, (M_Shape *)n, c ? 1 : 0); + } + i=0; + while ((srec = (SWFShapeRec*)gf_list_enum(shape->lines, &i))) { + if (c) + s2b_insert_rec_in_coord(c, srec); + + n = s2b_shape_to_curve2d(read, shape, srec, 0, c); + if (n) s2b_insert_shape((M_OrderedGroup*)og, (M_Shape *)n, c ? 1 : 0); + } + + if (last_sub_shape) read->cur_shape = NULL; + else read->cur_shape = og; + return GF_OK; +} + + +static GF_Node *s2b_get_glyph(SWFReader *read, u32 fontID, u32 gl_index, GF_Node *par) +{ + char szDEF[1024]; + u32 ID; + GF_Node *n, *glyph; + SWFFont *ft; + + sprintf(szDEF, "FT%d_GL%d", fontID, gl_index); + n = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + if (n) { + gf_node_register(n, par); + return n; + } + + /*first use of glyph in file*/ + ft = swf_find_font(read, fontID); + if (!ft) { + swf_report(read, GF_BAD_PARAM, "Cannot find font %d - skipping glyph", fontID); + return NULL; + } + if (ft->nbGlyphs <= gl_index) { + swf_report(read, GF_BAD_PARAM, "Glyph #%d not found in font %d - skipping", gl_index, fontID); + return NULL; + } + n = (GF_Node*)gf_list_get(ft->glyphs, gl_index); + if (gf_node_get_tag(n) != TAG_MPEG4_Shape) { + swf_report(read, GF_BAD_PARAM, "Glyph #%d in font %d not a shape (translated in %s) - skipping", gl_index, fontID, gf_node_get_class_name(n)); + return NULL; + } + glyph = ((M_Shape *)n)->geometry; + /*space*/ + if (!glyph) return NULL; + + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id(glyph, ID, szDEF); + gf_node_register(glyph, par); + + /*also insert glyph*/ + s2b_insert_symbol(read, n); + + return glyph; +} + +static GF_Err swf_bifs_define_text(SWFReader *read, SWFText *text) +{ + u32 i, j; + Bool use_text; + Fixed dx; + SWFGlyphRec *gr; + SWFFont *ft; + M_Transform2D *par, *gl_par; + M_Shape *gl; + M_TransformMatrix2D *tr; + + use_text = (read->flags & GF_SM_SWF_NO_FONT) ? 1 : 0; + tr = (M_TransformMatrix2D *) s2b_new_node(read, TAG_MPEG4_TransformMatrix2D); + tr->mxx = text->mat.m[0]; + tr->mxy = text->mat.m[1]; + tr->tx = text->mat.m[2]; + tr->myx = text->mat.m[3]; + tr->myy = text->mat.m[4]; + tr->ty = text->mat.m[5]; + + + i=0; + while ((gr = (SWFGlyphRec*)gf_list_enum(text->text, &i))) { + par = (M_Transform2D *) s2b_new_node(read, TAG_MPEG4_Transform2D); + par->translation.x = gr->orig_x; + par->translation.y = gr->orig_y; + + ft = NULL; + if (use_text) { + ft = swf_find_font(read, gr->fontID); + if (!ft->glyph_codes) { + use_text = 0; + swf_report(read, GF_BAD_PARAM, "Font glyphs are not defined, cannot reference extern font - Forcing glyph embedding"); + } + } + + if (!use_text) { + par->scale.y = par->scale.x = FLT2FIX(gr->fontSize * SWF_TEXT_SCALE); + } else { + /*don't forget we're flipped at top level...*/ + par->scale.y = -FIX_ONE; + } + gf_node_insert_child((GF_Node *)tr, (GF_Node *) par, -1); + gf_node_register((GF_Node *) par, (GF_Node *)tr); + + if (use_text) { + u16 *str_w, *widestr; + char *str; + void *ptr; + M_Text *t = (M_Text *) s2b_new_node(read, TAG_MPEG4_Text); + M_FontStyle *f = (M_FontStyle *) s2b_new_node(read, TAG_MPEG4_FontStyle); + t->fontStyle = (GF_Node *) f; + gf_node_register(t->fontStyle, (GF_Node *) t); + + /*restore back the font height in pixels (it's currently in SWF glyph design units)*/ + f->size = FLT2FIX(gr->fontSize * SWF_TWIP_SCALE); + + if (ft->fontName) { + gf_sg_vrml_mf_reset(&f->family, GF_SG_VRML_MFSTRING); + gf_sg_vrml_mf_append(&f->family, GF_SG_VRML_MFSTRING, &ptr); + ((SFString*)ptr)->buffer = strdup(ft->fontName); + } + gf_sg_vrml_mf_reset(&f->justify, GF_SG_VRML_MFSTRING); + gf_sg_vrml_mf_append(&f->justify, GF_SG_VRML_MFSTRING, &ptr); + ((SFString*)ptr)->buffer = strdup("BEGIN"); + + if (f->style.buffer) free(f->style.buffer); + if (ft->is_italic && ft->is_bold) f->style.buffer = strdup("BOLDITALIC"); + else if (ft->is_bold) f->style.buffer = strdup("BOLD"); + else if (ft->is_italic) f->style.buffer = strdup("ITALIC"); + else f->style.buffer = strdup("PLAIN"); + + /*convert to UTF-8*/ + str_w = (u16*)malloc(sizeof(u16) * (gr->nbGlyphs+1)); + for (j=0; jnbGlyphs; j++) str_w[j] = ft->glyph_codes[gr->indexes[j]]; + str_w[j] = 0; + str = (char*)malloc(sizeof(char) * (gr->nbGlyphs+2)); + widestr = str_w; + j = gf_utf8_wcstombs(str, sizeof(u8) * (gr->nbGlyphs+1), (const unsigned short **) &widestr); + if (j != (u32) -1) { + str[j] = 0; + gf_sg_vrml_mf_reset(&t->string, GF_SG_VRML_MFSTRING); + gf_sg_vrml_mf_append(&t->string, GF_SG_VRML_MFSTRING, &ptr); + ((SFString*)ptr)->buffer = (char*)malloc(sizeof(char) * (j+1)); + memcpy(((SFString*)ptr)->buffer, str, sizeof(char) * (j+1)); + } + + free(str); + free(str_w); + + gl = (M_Shape *) s2b_new_node(read, TAG_MPEG4_Shape); + gl->appearance = s2b_get_appearance(read, (GF_Node *) gl, gr->col, 0, 0); + gl->geometry = (GF_Node *) t; + gf_node_register(gl->geometry, (GF_Node *) gl); + gf_node_insert_child((GF_Node *) par, (GF_Node *)gl, -1); + gf_node_register((GF_Node *) gl, (GF_Node *) par); + } else { + + /*convert glyphs*/ + dx = 0; + for (j=0; jnbGlyphs; j++) { + gl = (M_Shape *) s2b_new_node(read, TAG_MPEG4_Shape); + gl->geometry = s2b_get_glyph(read, gr->fontID, gr->indexes[j], (GF_Node *) gl); + + if (!gl->geometry) { + gf_node_register((GF_Node *) gl, NULL); + gf_node_unregister((GF_Node *) gl, NULL); + dx += gr->dx[j]; + continue; + } + assert((gf_node_get_tag(gl->geometry)==TAG_MPEG4_Curve2D) || (gf_node_get_tag(gl->geometry)==TAG_MPEG4_XCurve2D)); + + gl_par = (M_Transform2D *) s2b_new_node(read, TAG_MPEG4_Transform2D); + gl->appearance = s2b_get_appearance(read, (GF_Node *) gl, gr->col, 0, 0); + + gl_par->translation.x = gf_divfix(dx, FLT2FIX(gr->fontSize * SWF_TEXT_SCALE) ); + dx += gr->dx[j]; + + gf_node_insert_child((GF_Node *) gl_par, (GF_Node *)gl, -1); + gf_node_register((GF_Node *) gl, (GF_Node *) gl_par); + gf_node_insert_child((GF_Node *) par, (GF_Node *)gl_par, -1); + gf_node_register((GF_Node *) gl_par, (GF_Node *) par); + } + } + } + + if (tr) { + char szDEF[1024]; + u32 ID; + sprintf(szDEF, "Text%d", text->ID); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id((GF_Node *)tr, ID, szDEF); + s2b_insert_symbol(read, (GF_Node *)tr); + } + return GF_OK; +} + + +typedef struct +{ + char *final; + u32 len; +} SWFFlatText; + +static void swf_nstart(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ +} +static void swf_nend(void *sax_cbck, const char *node_name, const char *name_space) +{ +} +static void swf_ntext(void *sax_cbck, const char *content, Bool is_cdata) +{ + u32 len; + SWFFlatText *t; + if (!content || is_cdata) return; + t = (SWFFlatText *)sax_cbck; + len = strlen(content); + if (!len) return; + t->final = realloc(t->final, sizeof(char)*(t->len+len+1)); + t->final [t->len] = 0; + strcat(t->final, content); + t->len = strlen(t->final)+1; +} + + +static GF_Err swf_bifs_define_edit_text(SWFReader *read, SWFEditText *text) +{ + char styles[1024]; + char *ptr; + Bool use_layout; + M_Layout *layout = NULL; + M_Shape *txt; + M_Text *t; + M_FontStyle *f; + M_Transform2D *tr; + + tr = (M_Transform2D *) s2b_new_node(read, TAG_MPEG4_Transform2D); + tr->scale.y = -FIX_ONE; + + use_layout = 0; + if (text->align==3) use_layout = 1; + else if (text->multiline) use_layout = 1; + + if (use_layout) { + layout = (M_Layout *) s2b_new_node(read, TAG_MPEG4_Layout); + tr->translation.x = read->width/2; + tr->translation.y = read->height/2; + } + + t = (M_Text *) s2b_new_node(read, TAG_MPEG4_Text); + f = (M_FontStyle *) s2b_new_node(read, TAG_MPEG4_FontStyle); + t->fontStyle = (GF_Node *) f; + gf_node_register(t->fontStyle, (GF_Node *) t); + + /*restore back the font height in pixels (it's currently in SWF glyph design units)*/ + f->size = text->font_height; + f->spacing = text->font_height + text->leading; + + gf_sg_vrml_mf_reset(&f->justify, GF_SG_VRML_MFSTRING); + gf_sg_vrml_mf_append(&f->justify, GF_SG_VRML_MFSTRING, (void**)&ptr); + switch (text->align) { + case 0: + ((SFString*)ptr)->buffer = strdup("BEGIN"); + break; + case 1: + ((SFString*)ptr)->buffer = strdup("END"); + break; + case 3: + ((SFString*)ptr)->buffer = strdup("JUSTIFY"); + break; + default: + ((SFString*)ptr)->buffer = strdup("MIDDLE"); + break; + } + + strcpy(styles, ""); + if (!text->read_only) strcat(styles, "EDITABLE"); + if (text->password) strcat(styles, "PASSWORD"); + + if (f->style.buffer) free(f->style.buffer); + f->style.buffer = strdup(styles); + + if (text->init_value) { + gf_sg_vrml_mf_reset(&t->string, GF_SG_VRML_MFSTRING); + gf_sg_vrml_mf_append(&t->string, GF_SG_VRML_MFSTRING, (void**)&ptr); + + if (text->html) { + GF_SAXParser *xml; + SWFFlatText flat; + flat.final = 0; + flat.len = 0; + xml = gf_xml_sax_new(swf_nstart, swf_nend, swf_ntext, &flat); + gf_xml_sax_init(xml, NULL); + gf_xml_sax_parse(xml, text->init_value); + gf_xml_sax_del(xml); + + if (flat.final) { + ((SFString*)ptr)->buffer = strdup(flat.final); + free(flat.final); + } + } else { + ((SFString*)ptr)->buffer = strdup(text->init_value); + } + } + + + txt = (M_Shape *) s2b_new_node(read, TAG_MPEG4_Shape); + txt->appearance = s2b_get_appearance(read, (GF_Node *) txt, text->color, 0, 0); + txt->geometry = (GF_Node *) t; + gf_node_register(txt->geometry, (GF_Node *) txt); + + if (layout) { + gf_sg_vrml_mf_reset(&layout->justify, GF_SG_VRML_MFSTRING); + gf_sg_vrml_mf_append(&layout->justify, GF_SG_VRML_MFSTRING, NULL); + switch (text->align) { + case 0: + layout->justify.vals[0] = strdup("BEGIN"); + break; + case 1: + layout->justify.vals[0] = strdup("END"); + break; + case 3: + layout->justify.vals[0] = strdup("JUSTIFY"); + break; + default: + layout->justify.vals[0] = strdup("MIDDLE"); + break; + } + if (text->multiline || text->word_wrap) layout->wrap = 1; + + gf_node_insert_child((GF_Node *) layout, (GF_Node *)txt, -1); + gf_node_register((GF_Node *) txt, (GF_Node *) layout); + + gf_node_insert_child((GF_Node *) tr, (GF_Node *)layout, -1); + gf_node_register((GF_Node *) layout, (GF_Node *) tr); + } else { + gf_node_insert_child((GF_Node *) tr, (GF_Node *)txt, -1); + gf_node_register((GF_Node *) txt, (GF_Node *) tr); + } + if (tr) { + char szDEF[1024]; + u32 ID; + sprintf(szDEF, "Text%d", text->ID); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id((GF_Node*)tr, ID, szDEF); + s2b_insert_symbol(read, (GF_Node*)tr); + } + return GF_OK; +} + + +/*called upon end of sprite or clip*/ +static void swf_bifs_end_of_clip(SWFReader *read) +{ + char szDEF[1024]; + u32 i; + GF_AUContext *au; + GF_Command *com; + GF_CommandField *f; + GF_Node *empty; + + return; + + empty = gf_sg_find_node_by_name(read->load->scene_graph, "Shape0"); + + au = gf_list_get(read->bifs_es->AUs, 0); + for (i=0; imax_depth; i++) { + /*and write command*/ + com = gf_sg_command_new(read->load->scene_graph, GF_SG_INDEXED_REPLACE); + sprintf(szDEF, "CLIP%d_DL", read->current_sprite_id); + com->node = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + + gf_node_register(com->node, NULL); + f = gf_sg_command_field_new(com); + f->field_ptr = &f->new_node; + f->fieldType = GF_SG_VRML_SFNODE; + f->pos = i; + f->fieldIndex = 2; /*children index*/ + f->new_node = empty; + gf_node_register(f->new_node, com->node); + + gf_list_insert(au->commands, com, i); + } +} + +static Bool swf_bifs_allocate_depth(SWFReader *read, u32 depth) +{ + char szDEF[100]; + GF_Node *disp, *empty; + if (read->max_depth > depth) return 1; + + sprintf(szDEF, "CLIP%d_DL", read->current_sprite_id); + disp = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + + empty = gf_sg_find_node_by_name(read->load->scene_graph, "Shape0"); + while (read->max_depth<=depth) { + gf_node_insert_child(disp, empty, -1); + gf_node_register(empty, disp); + read->max_depth++; + } + return 0; +} + +static GF_Err swf_init_od(SWFReader *read, Bool root_only) +{ + GF_ESD *esd; + + if (!read->load->ctx->root_od) { + GF_BIFSConfig *bc; + read->load->ctx->root_od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_IOD_TAG); + /*add BIFS stream*/ + esd = (GF_ESD *) gf_odf_desc_esd_new(0); + if (!esd) return GF_OUT_OF_MEM; + esd->decoderConfig->streamType = GF_STREAM_SCENE; + esd->decoderConfig->objectTypeIndication = 1; + esd->slConfig->timestampResolution = read->bifs_es->timeScale; + esd->ESID = 1; + gf_list_add(read->load->ctx->root_od->ESDescriptors, esd); + read->load->ctx->root_od->objectDescriptorID = 1; + gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + bc = (GF_BIFSConfig *) gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); + bc->pixelMetrics = 1; + bc->pixelWidth = (u16) read->width; + bc->pixelHeight = (u16) read->height; + esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) bc; + } + if (!read->load->ctx->root_od) return GF_OUT_OF_MEM; + if (root_only) return GF_OK; + + if (read->od_es) return GF_OK; + read->od_es = gf_sm_stream_new(read->load->ctx, 2, 1, 1); + if (!read->od_es) return GF_OUT_OF_MEM; + + esd = (GF_ESD *) gf_odf_desc_esd_new(0); + if (!esd) return GF_OUT_OF_MEM; + esd->decoderConfig->streamType = GF_STREAM_OD; + esd->decoderConfig->objectTypeIndication = 1; + esd->slConfig->timestampResolution = read->od_es->timeScale = read->bifs_es->timeScale; + esd->ESID = 2; + esd->OCRESID = 1; + gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = NULL; + return gf_list_add(read->load->ctx->root_od->ESDescriptors, esd); +} + +static GF_Err swf_insert_od(SWFReader *read, u32 at_time, GF_ObjectDescriptor *od) +{ + u32 i; + GF_ODUpdate *com; + read->od_au = gf_sm_stream_au_new(read->od_es, at_time, 0, 1); + if (!read->od_au) return GF_OUT_OF_MEM; + + i=0; + while ((com = (GF_ODUpdate *)gf_list_enum(read->od_au->commands, &i))) { + if (com->tag == GF_ODF_OD_UPDATE_TAG) { + gf_list_add(com->objectDescriptors, od); + return GF_OK; + } + } + com = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + gf_list_add(com->objectDescriptors, od); + return gf_list_add(read->od_au->commands, com); +} + +static u16 swf_get_od_id(SWFReader *read) +{ + return ++read->prev_od_id; +} + +static u16 swf_get_es_id(SWFReader *read) +{ + return ++read->prev_es_id; +} + + +static GF_Err swf_bifs_define_sprite(SWFReader *read, u32 nb_frames) +{ + GF_Err e; + GF_ObjectDescriptor *od; + GF_ESD *esd; + u32 ID; + GF_Node *n, *par; + GF_FieldInfo info; + char szDEF[100]; + GF_StreamContext *prev_sc; + GF_AUContext *prev_au; + + /*init OD*/ + e = swf_init_od(read, 0); + if (e) return e; + + /*create animationStream object*/ + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + if (!od) return GF_OUT_OF_MEM; + + od->objectDescriptorID = swf_get_od_id(read); + esd = (GF_ESD *) gf_odf_desc_esd_new(0); + if (!esd) return GF_OUT_OF_MEM; + esd->ESID = swf_get_es_id(read); + /*sprite runs on its own timeline*/ + esd->OCRESID = esd->ESID; + /*always depends on main scene*/ + esd->dependsOnESID = 1; + esd->decoderConfig->streamType = GF_STREAM_SCENE; + esd->decoderConfig->objectTypeIndication = 1; + esd->slConfig->timestampResolution = read->bifs_es->timeScale; + gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = NULL; + gf_list_add(od->ESDescriptors, esd); + + /*by default insert OD at begining*/ + e = swf_insert_od(read, 0, od); + if (e) { + gf_odf_desc_del((GF_Descriptor *) od); + return e; + } + + /*create AS for sprite - all AS are created in initial scene replace*/ + n = s2b_new_node(read, TAG_MPEG4_AnimationStream); + gf_node_insert_child(read->root, n, 0); + gf_node_register(n, read->root); + /*assign URL*/ + gf_node_get_field_by_name(n, "url", &info); + gf_sg_vrml_mf_alloc(info.far_ptr, info.fieldType, 1); + ((MFURL*)info.far_ptr)->vals[0].OD_ID = od->objectDescriptorID; + ((M_AnimationStream *)n)->startTime = 0; + + n = s2b_new_node(read, TAG_MPEG4_MediaControl); + sprintf(szDEF, "CLIP%d_CTRL", read->current_sprite_id); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id(n, ID, szDEF); + + gf_node_insert_child(read->root, n, 0); + gf_node_register(n, read->root); + /*assign URL*/ + gf_node_get_field_by_name(n, "url", &info); + gf_sg_vrml_mf_alloc(info.far_ptr, info.fieldType, 1); + ((MFURL*)info.far_ptr)->vals[0].OD_ID = od->objectDescriptorID; + /*inactive by default (until inserted)*/ + ((M_MediaControl *)n)->mediaSpeed = 0; + ((M_MediaControl *)n)->loop = 1; + + /*create sprite grouping node*/ + n = s2b_new_node(read, TAG_MPEG4_Group); + sprintf(szDEF, "CLIP%d_DL", read->current_sprite_id); + + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id(n, ID, szDEF); + par = gf_sg_find_node_by_name(read->load->scene_graph, "DICTIONARY"); + assert(par); + gf_node_list_add_child(&((M_Switch *)par)->choice, n); + gf_node_register(n, par); + par = gf_sg_find_node_by_name(read->load->scene_graph, "Shape0"); + gf_node_insert_child(n, par, -1); + gf_node_register(par, n); + + /*store BIFS context*/ + prev_sc = read->bifs_es; + prev_au = read->bifs_au; + /*create new BIFS stream*/ + read->bifs_es = gf_sm_stream_new(read->load->ctx, esd->ESID, GF_STREAM_SCENE, 1); + read->bifs_es->timeScale = prev_sc->timeScale; + read->bifs_es->dump_time_offset = prev_sc->dump_time_offset + prev_au->timing; + + /*create first AU*/ + read->bifs_au = gf_sm_stream_au_new(read->bifs_es, 0, 0, 1); + + e = swf_parse_sprite(read); + if (e) return e; + + swf_bifs_end_of_clip(read); + + /*restore BIFS context*/ + read->bifs_es = prev_sc; + read->bifs_au = prev_au; + + return GF_OK; +} + +static GF_Err swf_bifs_setup_sound(SWFReader *read, SWFSound *snd, Bool soundstream_first_block) +{ + GF_Err e; + GF_ObjectDescriptor *od; + GF_ESD *esd; + GF_MuxInfo *mux; + GF_Node *n, *par; + GF_FieldInfo info; + u32 ID; + char szDEF[100]; + + /*soundstream header, only declare the associated MediaControl node for later actions*/ + if (!snd->ID && !soundstream_first_block) { + n = s2b_new_node(read, TAG_MPEG4_MediaControl); + sprintf(szDEF, "CLIP%d_SND", read->current_sprite_id); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id(n, ID, szDEF); + + gf_node_insert_child(read->root, n, 0); + gf_node_register(n, read->root); + return GF_OK; + } + + e = swf_init_od(read, 0); + if (e) return e; + + /*create audio object*/ + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + if (!od) return GF_OUT_OF_MEM; + od->objectDescriptorID = swf_get_od_id(read); + esd = (GF_ESD *) gf_odf_desc_new(GF_ODF_ESD_TAG); + if (!esd) return GF_OUT_OF_MEM; + esd->ESID = swf_get_es_id(read); + if (snd->ID) { + /*sound runs on its own timeline*/ + esd->OCRESID = esd->ESID; + } else { + /*soundstream runs on movie/sprite timeline*/ + esd->OCRESID = read->bifs_es->ESID; + esd->OCRESID = esd->ESID; + } + gf_list_add(od->ESDescriptors, esd); + + /*setup mux info*/ + mux = (GF_MuxInfo*)gf_odf_desc_new(GF_ODF_MUXINFO_TAG); + mux->file_name = strdup(snd->szFileName); +// mux->startTime = snd->frame_delay_ms; + mux->startTime = 0; + /*MP3 in, destroy file once done*/ + if (snd->format==2) mux->delete_file = 1; + gf_list_add(esd->extensionDescriptors, mux); + + + /*by default insert OD at begining*/ + e = swf_insert_od(read, 0, od); + if (e) { + gf_odf_desc_del((GF_Descriptor *) od); + return e; + } + /*create sound & audio clip*/ + n = s2b_new_node(read, TAG_MPEG4_Sound2D); + gf_node_insert_child(read->root, n, 0); + gf_node_register(n, read->root); + par = n; + n = s2b_new_node(read, TAG_MPEG4_AudioClip); + ((M_Sound2D *)par)->source = n; + gf_node_register(n, par); + /*assign URL*/ + gf_node_get_field_by_name(n, "url", &info); + gf_sg_vrml_mf_alloc(info.far_ptr, info.fieldType, 1); + ((MFURL *)info.far_ptr)->vals[0].OD_ID = od->objectDescriptorID; + + ((M_AudioClip*)n)->startTime = -1.0; + + /*regular sound: set an ID to do play/stop*/ + if (snd->ID) { + sprintf(szDEF, "Sound%d", snd->ID); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id(n, ID, szDEF); + } + /*soundStream - add a MediaControl*/ + else { + /*if sprite always have the media active but controled by its mediaControl*/ + if (read->current_sprite_id) { + ((M_AudioClip*)n)->startTime = 0; + } + /*otherwise start the media at the first soundstream block*/ + else { + ((M_AudioClip*)n)->startTime = snd->frame_delay_ms/1000.0; + ((M_AudioClip*)n)->startTime = 0; + } + + sprintf(szDEF, "CLIP%d_SND", read->current_sprite_id); + n = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + + /*assign URL*/ + gf_node_get_field_by_name(n, "url", &info); + gf_sg_vrml_mf_alloc(info.far_ptr, info.fieldType, 1); + ((MFURL*)info.far_ptr)->vals[0].OD_ID = od->objectDescriptorID; + ((M_MediaControl *)n)->loop = 0; + + /*inactive by default (until inserted)*/ + if (read->current_sprite_id) { + ((M_MediaControl *)n)->mediaSpeed = 0; + } else { + ((M_MediaControl *)n)->mediaSpeed = FIX_ONE; + } + } + return GF_OK; +} + +static GF_Err swf_bifs_setup_image(SWFReader *read, u32 ID, char *fileName) +{ + + GF_Err e; + GF_ObjectDescriptor *od; + GF_ESD *esd; + GF_MuxInfo *mux; + GF_Node *n, *par; + GF_FieldInfo info; + char szDEF[100]; + + e = swf_init_od(read, 0); + if (e) return e; + + /*create visual object*/ + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + if (!od) return GF_OUT_OF_MEM; + od->objectDescriptorID = swf_get_od_id(read); + esd = (GF_ESD *) gf_odf_desc_new(GF_ODF_ESD_TAG); + if (!esd) return GF_OUT_OF_MEM; + esd->ESID = swf_get_es_id(read); + esd->OCRESID = esd->ESID; + gf_list_add(od->ESDescriptors, esd); + + /*setup mux info*/ + mux = (GF_MuxInfo*)gf_odf_desc_new(GF_ODF_MUXINFO_TAG); + + mux->file_name = strdup(fileName); + /*destroy file once done*/ + //mux->delete_file = 1; + gf_list_add(esd->extensionDescriptors, mux); + + + /*by default insert OD at begining*/ + e = swf_insert_od(read, 0, od); + if (e) { + gf_odf_desc_del((GF_Descriptor *) od); + return e; + } + /*create appearance clip*/ + par = s2b_new_node(read, TAG_MPEG4_Shape); + s2b_insert_symbol(read, par); + n = s2b_new_node(read, TAG_MPEG4_Appearance); + ((M_Shape *)par)->appearance = n; + gf_node_register(n, par); + + par = n; + n = s2b_new_node(read, TAG_MPEG4_ImageTexture); + ((M_Appearance *)par)->texture = n; + gf_node_register(n, par); + + sprintf(szDEF, "Bitmap%d", ID); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id(n, ID, szDEF); + + /*assign URL*/ + gf_node_get_field_by_name(n, "url", &info); + gf_sg_vrml_mf_alloc(info.far_ptr, info.fieldType, 1); + ((MFURL *)info.far_ptr)->vals[0].OD_ID = od->objectDescriptorID; + + return GF_OK; +} + + +static GF_Node *s2b_wrap_node(SWFReader *read, GF_Node *node, GF_Matrix2D *mat, GF_ColorMatrix *cmat) +{ + GF_Node *par; + if (mat && gf_mx2d_is_identity(*mat)) mat = NULL; + if (cmat && cmat->identity) cmat = NULL; + + /*then add cmat/mat and node*/ + par = NULL; + if (!mat && !cmat) { + par = node; + } else { + if (mat) par = s2b_get_matrix(read, mat); + if (cmat) { + GF_Node *cm = s2b_get_color_matrix(read, cmat); + if (!par) { + par = cm; + gf_node_insert_child(par, node, -1); + gf_node_register(node, par); + } else { + gf_node_insert_child(par, cm, -1); + gf_node_register(cm, par); + gf_node_insert_child(cm, node, -1); + gf_node_register(node, cm); + } + } else { + gf_node_insert_child(par, node, -1); + gf_node_register(node, par); + } + } + return par; +} + + + +static void s2b_set_field(SWFReader *read, GF_List *dst, GF_Node *n, char *fieldName, s32 pos, u32 type, void *val, Bool insert) +{ + u32 i, count; + GF_FieldInfo info; + GF_Command *com = NULL; + GF_CommandField *f; + + gf_node_get_field_by_name(n, fieldName, &info); + + count = gf_list_count(dst); + for (i=0; inode!=n) continue; + f = gf_list_get(com->command_fields, 0); + if (f->fieldIndex != info.fieldIndex) continue; + if (f->pos != pos) continue; + + if (insert) return; + + if (type==GF_SG_VRML_SFSTRING) { + if (((SFString*)f->field_ptr)->buffer) + free(((SFString*)f->field_ptr)->buffer); + ((SFString*)f->field_ptr)->buffer = strdup( (char *) val); + } else { + gf_sg_vrml_field_copy(f->field_ptr, val, type); + } + gf_list_rem(dst, i); + gf_list_add(dst, com); + return; + } + + com = gf_sg_command_new(read->load->scene_graph, (pos<0) ? GF_SG_FIELD_REPLACE : GF_SG_INDEXED_REPLACE); + com->node = n; + gf_node_register(com->node, NULL); + f = gf_sg_command_field_new(com); + f->field_ptr = gf_sg_vrml_field_pointer_new(type); + if (type==GF_SG_VRML_SFSTRING) { + ((SFString*)f->field_ptr)->buffer = strdup( (char *) val); + } else { + gf_sg_vrml_field_copy(f->field_ptr, val, type); + } + f->fieldType = type; + f->pos = pos; + f->fieldIndex = info.fieldIndex; + + if (insert) + gf_list_insert(dst, com, 0); + else + gf_list_add(dst, com); +} + +static GF_Err swf_bifs_set_backcol(SWFReader *read, u32 xrgb) +{ + SFColor rgb; + GF_Node *bck = gf_sg_find_node_by_name(read->load->scene_graph, "BACKGROUND"); + + rgb.red = INT2FIX((xrgb>>16) & 0xFF) / 255; + rgb.green = INT2FIX((xrgb>>8) & 0xFF) / 255; + rgb.blue = INT2FIX((xrgb) & 0xFF) / 255; + s2b_set_field(read, read->bifs_au->commands, bck, "backColor", -1, GF_SG_VRML_SFCOLOR, &rgb, 0); + return GF_OK; +} + +static GF_Err swf_bifs_start_sound(SWFReader *read, SWFSound *snd, Bool stop) +{ + GF_Node *sound2D; + SFTime t = 0; + char szDEF[100]; + + sprintf(szDEF, "Sound%d", snd->ID); + sound2D = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + /*check flags*/ + if (sound2D) + s2b_set_field(read, read->bifs_au->commands, sound2D, stop ? "stopTime" : "startTime", -1, GF_SG_VRML_SFTIME, &t, 0); + + return GF_OK; +} + +static void s2b_control_sprite(SWFReader *read, GF_List *dst, u32 ID, Bool stop, Bool set_time, SFTime mediaStartTime, Bool rev_order) +{ + u32 i; + GF_Node *obj; + char szDEF[100]; + SFFloat t; + sprintf(szDEF, "CLIP%d_CTRL", ID); + obj = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + if (!obj) return; + + /*filter the current control sequence: if the sprite is marked as started, skip the control. This happens + when a sprite is used in the different states of a button*/ + for (i=0; inode==obj) { + GF_CommandField *f = gf_list_get(com->command_fields, 0); + if ((f->fieldIndex == 3) && (*((SFFloat*)f->field_ptr) != 0)) + return; + } + } + + + if (set_time) + s2b_set_field(read, dst, obj, "mediaStartTime", -1, GF_SG_VRML_SFTIME, &mediaStartTime, rev_order); + t = stop ? 0 : FIX_ONE; + s2b_set_field(read, dst, obj, "mediaSpeed", -1, GF_SG_VRML_SFFLOAT, &t, rev_order); + + sprintf(szDEF, "CLIP%d_SND", ID); + obj = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + if (!obj) return; + if (set_time) { + mediaStartTime -= read->sound_stream->frame_delay_ms/1000.0; + if (mediaStartTime<0) mediaStartTime = 0; + s2b_set_field(read, dst, obj, "mediaStartTime", -1, GF_SG_VRML_SFTIME, &mediaStartTime, rev_order); + } + t = stop ? 0 : FIX_ONE; + s2b_set_field(read, dst, obj, "mediaSpeed", -1, GF_SG_VRML_SFFLOAT, &t, rev_order); +} + +static GF_Err swf_bifs_place_obj(SWFReader *read, u32 depth, u32 ID, u32 prev_id, u32 type, GF_Matrix2D *mat, GF_ColorMatrix *cmat, GF_Matrix2D *prev_mat, GF_ColorMatrix *prev_cmat) +{ + GF_Command *com; + GF_CommandField *f; + GF_Node *obj, *par; + char szDEF[100]; + Bool is_sprite; + + obj = s2b_get_node(read, ID); + is_sprite = 0; + if (!obj) { + sprintf(szDEF, "CLIP%d_DL", ID); + obj = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + if (obj) is_sprite = 1; + } + if (!obj) return GF_BAD_PARAM; + + /*then add cmat/mat and node*/ + par = s2b_wrap_node(read, obj, mat, cmat); + + /*and write command*/ + com = gf_sg_command_new(read->load->scene_graph, GF_SG_INDEXED_REPLACE); + sprintf(szDEF, "CLIP%d_DL", read->current_sprite_id); + com->node = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + gf_node_register(com->node, NULL); + f = gf_sg_command_field_new(com); + f->field_ptr = &f->new_node; + f->fieldType = GF_SG_VRML_SFNODE; + f->pos = depth; + f->fieldIndex = 2; /*children index*/ + f->new_node = par; + gf_node_register(f->new_node, com->node); + gf_list_add(read->bifs_au->commands, com); + + if (ID==prev_id) return GF_OK; + + strcpy(szDEF, gf_node_get_name(obj)); + /*when inserting a button, trigger a pause*/ + if (!strnicmp(szDEF, "Button", 6)) { + u32 i, count; + s2b_control_sprite(read, read->bifs_au->commands, read->current_sprite_id, 1, 0, 0, 1); + + count = gf_list_count(read->buttons); + for (i=0; ibuttons, i); + if (btnrec->btn_id==ID) { + s2b_control_sprite(read, read->bifs_au->commands, btnrec->sprite_up_id, 0, 0, 0, 1); + } + } + } + /*starts anim*/ + else if (is_sprite) { + s2b_control_sprite(read, read->bifs_au->commands, ID, 0, 1, 0, 0); + if (prev_id) { + s2b_control_sprite(read, read->bifs_au->commands, prev_id, 1, 0, 0, 0); + } + } + return GF_OK; +} + +static GF_Err swf_bifs_remove_obj(SWFReader *read, u32 depth, u32 ID) +{ + char szDEF[100]; + GF_Command *com; + GF_CommandField *f; + + com = gf_sg_command_new(read->load->scene_graph, GF_SG_INDEXED_REPLACE); + sprintf(szDEF, "CLIP%d_DL", read->current_sprite_id); + com->node = gf_sg_find_node_by_name(read->load->scene_graph, szDEF); + gf_node_register(com->node, NULL); + f = gf_sg_command_field_new(com); + f->field_ptr = &f->new_node; + f->fieldType = GF_SG_VRML_SFNODE; + f->pos = depth; + f->fieldIndex = 2; /*children index*/ + f->new_node = gf_sg_find_node_by_name(read->load->scene_graph, "Shape0"); + gf_node_register(f->new_node, com->node); + gf_list_add(read->bifs_au->commands, com); + /*check if this is a sprite*/ + if (ID) + s2b_control_sprite(read, read->bifs_au->commands, ID, 1, 0, 0, 0); + return GF_OK; +} + +static GF_Err swf_bifs_show_frame(SWFReader *read) +{ + u32 ts; + Bool is_rap; + + /*hack to allow for empty BIFS AU to be encoded in order to keep the frame-rate (this reduces MP4 table size...)*/ + if (0 && !gf_list_count(read->bifs_au->commands)) { + GF_Command *com; + GF_CommandField *f; + com = gf_sg_command_new(read->load->scene_graph, GF_SG_FIELD_REPLACE); + com->node = gf_sg_find_node_by_name(read->load->scene_graph, "DICTIONARY"); + gf_node_register(com->node, NULL); + f = gf_sg_command_field_new(com); + f->field_ptr = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFINT32); + f->fieldType = GF_SG_VRML_SFINT32; + f->fieldIndex = 1; /*whichCoice index*/ + /*replace by same value*/ + *((SFInt32 *)f->field_ptr) = -1; + gf_list_add(read->bifs_au->commands, com); + } + + /*create a new AU for next frame*/ + ts = (read->current_frame + 1) * 100; + +#if 1 + /*all frames in sprites are RAP (define is not allowed in sprites) + if we use a ctrl stream, all AUs are RAP (defines are placed in a static dictionary) + */ + is_rap = (read->current_sprite_id || (read->flags & GF_SM_SWF_SPLIT_TIMELINE)) ? 1 : 0; +#else + /*"The contents of the second frame are the cumulative effect of performing all the control tag operations from + the beginning of the file to the second ShowFrame tag, and so on" + using RAP=0 forces reprocessing of previous frames when seeking/jumping*/ + is_rap = 0; +#endif + /*if we use ctrl stream, same thing*/ + read->bifs_au = gf_sm_stream_au_new(read->bifs_es, ts, 0, is_rap); + + /*not in a sprite def, using a control stream and no wait_frame: create a new AU for new symbols + */ + if (!read->current_sprite_id) { + if (read->bifs_dict_au && !read->wait_frame) { + read->bifs_dict_au = gf_sm_stream_au_new(read->bifs_dict_es, ts, 0, 0); + } + /*if wait_frame is specified, aggregate all dictionary commands until frame is reached*/ + if (read->wait_frame && read->wait_frame<=read->current_frame) + read->wait_frame = 0; + } + + return GF_OK; +} + + +static GF_Node *s2b_button_add_child(SWFReader *read, GF_Node *button, u32 tag, char *def_name, s32 pos) +{ + GF_Node *n = s2b_new_node(read, tag); + + if (def_name) { + u32 ID; + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id((GF_Node *)n, ID, def_name); + } + + gf_node_insert_child((GF_Node *)button, (GF_Node *)n, pos); + gf_node_register((GF_Node *) n, (GF_Node *) button); + return n; +} + +static void s2b_button_add_route(SWFReader *read, GF_Node *fromNode, u32 fromField, GF_Node *toNode, u32 toField) +{ + GF_Command *com = gf_sg_command_new(read->load->scene_graph, GF_SG_ROUTE_INSERT); + com->fromNodeID = gf_node_get_id(fromNode); + com->fromFieldIndex = fromField; + com->toNodeID = gf_node_get_id(toNode); + com->toFieldIndex = toField; + + if (read->bifs_dict_au) + gf_list_add(read->bifs_dict_au->commands, com); + else + gf_list_add(read->bifs_au->commands, com); +} + + +static GF_Err swf_bifs_define_button(SWFReader *read, SWF_Button *btn) +{ + char szName[1024]; + M_Switch *button; + SWF_ButtonRecord *br; + GF_Node *btn_root, *n, *btn_ts; + u32 i, ID, pos; + + if (!btn) { + read->btn = NULL; + read->btn_over = read->btn_not_over = read->btn_active = read->btn_not_active = NULL; + return GF_OK; + } + + read->btn = btn; + + btn_root = s2b_new_node(read, TAG_MPEG4_Transform2D); + sprintf(szName, "Button%d", btn->ID); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id((GF_Node *)btn_root, ID, szName); + + n = s2b_button_add_child(read, btn_root, TAG_MPEG4_ColorTransform, NULL, -1); + ((M_ColorTransform*)n)->maa = ((M_ColorTransform*)n)->mab = ((M_ColorTransform*)n)->mar = ((M_ColorTransform*)n)->mag = ((M_ColorTransform*)n)->ta = 0; + + /*locate hit buttons and add them to the color transform*/ + for (i=0; icount; i++) { + GF_Node *character; + br = &btn->buttons[i]; + if (!br->hitTest) continue; + character = s2b_get_node(read, br->character_id); + if (!character) { + sprintf(szName, "CLIP%d_DL", br->character_id); + character = gf_sg_find_node_by_name(read->load->scene_graph, szName); + } + if (character) { + gf_node_list_add_child(&((GF_ParentNode*)n)->children, character); + gf_node_register(character, (GF_Node *)n); + } + } + /*add touch sensor to the color transform*/ + sprintf(szName, "BTN%d_TS", read->btn->ID); + btn_ts = s2b_button_add_child(read, n, TAG_MPEG4_TouchSensor, szName, -1); + + s2b_insert_symbol(read, (GF_Node *)btn_root); + + /*isActive handler*/ + sprintf(szName, "BTN%d_CA", read->btn->ID); + n = s2b_button_add_child(read, btn_root, TAG_MPEG4_Conditional, szName, -1); + read->btn_active = ((M_Conditional*)n)->buffer.commandList; + s2b_button_add_route(read, btn_ts, 4, n, 0); + + /*!isActive handler*/ + sprintf(szName, "BTN%d_CNA", read->btn->ID); + n = s2b_button_add_child(read, btn_root, TAG_MPEG4_Conditional, szName, -1); + read->btn_not_active = ((M_Conditional*)n)->buffer.commandList; + s2b_button_add_route(read, btn_ts, 4, n, 1); + + /*isOver handler*/ + sprintf(szName, "BTN%d_CO", read->btn->ID); + n = s2b_button_add_child(read, btn_root, TAG_MPEG4_Conditional, szName, -1); + read->btn_over = ((M_Conditional*)n)->buffer.commandList; + s2b_button_add_route(read, btn_ts, 5, n, 0); + + /*!isOver handler*/ + sprintf(szName, "BTN%d_CNO", read->btn->ID); + n = s2b_button_add_child(read, btn_root, TAG_MPEG4_Conditional, szName, -1); + read->btn_not_over = ((M_Conditional*)n)->buffer.commandList; + s2b_button_add_route(read, btn_ts, 5, n, 1); + + /*by default show first character*/ + pos = 0; + for (i=0; icount; i++) { + GF_Node *sprite_ctrl = NULL; + GF_Node *character; + br = &btn->buttons[i]; + if (!br->up && !br->down && !br->over) continue; + + character = s2b_get_node(read, br->character_id); + + if (!character) { + sprintf(szName, "CLIP%d_DL", br->character_id); + character = gf_sg_find_node_by_name(read->load->scene_graph, szName); + if (character) { + sprintf(szName, "CLIP%d_CTRL", br->character_id); + sprite_ctrl = gf_sg_find_node_by_name(read->load->scene_graph, szName); + } + } + if (character) { + SFInt32 choice = 0; + GF_Node *n = s2b_wrap_node(read, character, &br->mx, &br->cmx); + + sprintf(szName, "BTN%d_R%d", btn->ID, i+1); + button = (M_Switch *) s2b_button_add_child(read, btn_root, TAG_MPEG4_Switch, szName, pos); + pos++; + + gf_node_list_add_child(&button->choice, n); + gf_node_register(n, (GF_Node *)button); + /*initial state*/ + if (br->up) { + button->whichChoice = 0; + /*register this button for sprite start upon place_obj*/ + if (sprite_ctrl) { + S2BBtnRec *btnrec; + if (!read->buttons) read->buttons = gf_list_new(); + btnrec = malloc(sizeof(S2BBtnRec)); + btnrec->btn_id = btn->ID; + btnrec->sprite_up_id = br->character_id; + gf_list_add(read->buttons, btnrec); + } + + } else { + button->whichChoice = -1; + } + + choice = br->up ? 0 : -1; + s2b_set_field(read, read->btn_not_over, (GF_Node *)button, "whichChoice", -1, GF_SG_VRML_SFINT32, &choice, 0); + /*start or stop sprite if button is up or not*/ + if (sprite_ctrl) { + s2b_control_sprite(read, read->btn_not_over, br->character_id, choice, 1, 0, 0); + } + + choice = br->down ? 0 : -1; + s2b_set_field(read, read->btn_active, (GF_Node *)button, "whichChoice", -1, GF_SG_VRML_SFINT32, &choice, 0); + if (sprite_ctrl && !br->over) { + s2b_control_sprite(read, read->btn_active, br->character_id, choice, 1, 0, 0); + } + + choice = br->over ? 0 : -1; + s2b_set_field(read, read->btn_not_active, (GF_Node *)button, "whichChoice", -1, GF_SG_VRML_SFINT32, &choice, 0); + s2b_set_field(read, read->btn_over, (GF_Node *)button, "whichChoice", -1, GF_SG_VRML_SFINT32, &choice, 0); + if (sprite_ctrl) { + s2b_control_sprite(read, read->btn_over, br->character_id, choice, 1, 0, 0); + if (!br->down) + s2b_control_sprite(read, read->btn_not_active, br->character_id, choice, 1, 0, 0); + } + } + } + + return GF_OK; +} + +static void swf_bifs_finalize(SWFReader *read) +{ + u32 i, count; + + swf_bifs_end_of_clip(read); + + while (gf_list_count(read->buttons)) { + S2BBtnRec *btnrec = gf_list_get(read->buttons, 0); + gf_list_rem(read->buttons, 0); + free(btnrec); + } + + count = gf_list_count(read->fonts); + for (i=0;ifonts, i); + while (gf_list_count(ft->glyphs)) { + GF_Node *gl = (GF_Node *)gf_list_get(ft->glyphs, 0); + gf_list_rem(ft->glyphs, 0); + gf_node_unregister(gl, NULL); + } + } +} + +Bool swf_bifs_action(SWFReader *read, SWFAction *act) +{ + GF_List *dst; + MFURL url; + SFURL sfurl; + MFString str; + SFString sfstr; + Bool bval; + GF_Node *n; + Double time; + + dst = read->bifs_au->commands; + if (read->btn) { + if (act->button_mask & GF_SWF_COND_OVERUP_TO_OVERDOWN) dst = read->btn_active; + else if (act->button_mask & GF_SWF_COND_IDLE_TO_OVERUP) dst = read->btn_over; + else if (act->button_mask & GF_SWF_COND_OVERUP_TO_IDLE) dst = read->btn_not_over; + else dst = read->btn_not_active; + } + + switch (act->type) { + case GF_SWF_AS3_WAIT_FOR_FRAME: + /*while correct, this is not optimal, we set the wait-frame upon GOTO frame*/ +// read->wait_frame = act->frame_number; + break; + case GF_SWF_AS3_GOTO_FRAME: + if (act->frame_number>read->current_frame) + read->wait_frame = act->frame_number; + + time = act->frame_number ? act->frame_number +1: 0; + time /= read->frame_rate; + s2b_control_sprite(read, dst, read->current_sprite_id, 0, 1, time, 0); + break; + case GF_SWF_AS3_GET_URL: + n = gf_sg_find_node_by_name(read->load->scene_graph, "MOVIE_URL"); + sfurl.OD_ID = 0; sfurl.url = act->url; + url.count = 1; url.vals = &sfurl; + s2b_set_field(read, dst, n, "url", -1, GF_SG_VRML_MFURL, &url, 0); + + sfstr.buffer = act->target; + str.count = 1; str.vals = &act->target; + s2b_set_field(read, dst, n, "parameter", -1, GF_SG_VRML_MFSTRING, &url, 0); + + bval = 1; + s2b_set_field(read, dst, n, "activate", -1, GF_SG_VRML_SFBOOL, &bval, 0); + break; + case GF_SWF_AS3_PLAY: + s2b_control_sprite(read, dst, read->current_sprite_id, 0, 1, -1, 0); + break; + case GF_SWF_AS3_STOP: + s2b_control_sprite(read, dst, read->current_sprite_id, 1, 0, 0, 0); + break; + default: + return 0; + } + + return 1; +} + +GF_Err swf_to_bifs_init(SWFReader *read) +{ + GF_Err e; + char szMsg[1000]; + GF_ObjectDescriptor *od; + GF_ESD *esd; + u32 ID; + GF_FieldInfo info; + GF_StreamContext *prev_sc; + GF_Command *com; + GF_Node *n, *n2; + + /*init callbacks*/ + read->show_frame = swf_bifs_show_frame; + read->allocate_depth = swf_bifs_allocate_depth; + read->place_obj = swf_bifs_place_obj; + read->remove_obj = swf_bifs_remove_obj; + read->define_shape = swf_bifs_define_shape; + read->define_sprite = swf_bifs_define_sprite; + read->set_backcol = swf_bifs_set_backcol; + read->define_button = swf_bifs_define_button; + read->define_text = swf_bifs_define_text; + read->define_edit_text = swf_bifs_define_edit_text; + read->setup_sound = swf_bifs_setup_sound; + read->start_sound = swf_bifs_start_sound; + read->setup_image = swf_bifs_setup_image; + read->action = swf_bifs_action; + read->finalize = swf_bifs_finalize; + + /*create BIFS stream*/ + read->bifs_es = gf_sm_stream_new(read->load->ctx, 1, GF_STREAM_SCENE, 0x01); + read->bifs_es->timeScale = read->frame_rate*100; + + read->bifs_au = gf_sm_stream_au_new(read->bifs_es, 0, 0.0, 1); + /*create scene replace command*/ + com = gf_sg_command_new(read->load->scene_graph, GF_SG_SCENE_REPLACE); + read->load->ctx->scene_width = FIX2INT(read->width); + read->load->ctx->scene_height = FIX2INT(read->height); + read->load->ctx->is_pixel_metrics = 1; + + gf_list_add(read->bifs_au->commands, com); + read->load->scene_graph = read->load->scene_graph; + + /*create base tree*/ + com->node = read->root = s2b_new_node(read, TAG_MPEG4_OrderedGroup); + gf_node_register(read->root, NULL); + + /*hehehe*/ + n = s2b_new_node(read, TAG_MPEG4_WorldInfo); + gf_node_insert_child(read->root, n, -1); + gf_node_register(n, read->root); + ((M_WorldInfo *)n)->title.buffer = strdup("GPAC SWF CONVERTION DISCLAIMER"); + gf_sg_vrml_mf_alloc( & ((M_WorldInfo *)n)->info, GF_SG_VRML_MFSTRING, 3); + + sprintf(szMsg, "%s file converted to MPEG-4 Systems", read->load->fileName); + ((M_WorldInfo *)n)->info.vals[0] = strdup(szMsg); + ((M_WorldInfo *)n)->info.vals[1] = strdup("Conversion done using GPAC version " GPAC_FULL_VERSION " - (C) 2000-2005 GPAC"); + ((M_WorldInfo *)n)->info.vals[2] = strdup("Macromedia SWF to MPEG-4 Conversion mapping released under GPL license"); + + /*background*/ + n = s2b_new_node(read, TAG_MPEG4_Background2D); + ((M_Background2D *)n)->backColor.red = FIX_ONE; + ((M_Background2D *)n)->backColor.green = FIX_ONE; + ((M_Background2D *)n)->backColor.blue = FIX_ONE; + gf_node_set_id(n, 1, "BACKGROUND"); + gf_node_insert_child(read->root, n, -1); + gf_node_register(n, read->root); + + /*movie anchor*/ + n = s2b_new_node(read, TAG_MPEG4_Anchor); + gf_node_set_id(n, 2, "MOVIE_URL"); + gf_node_insert_child(read->root, n, -1); + gf_node_register(n, read->root); + + /*dictionary*/ + n = s2b_new_node(read, TAG_MPEG4_Switch); + gf_node_set_id(n, 3, "DICTIONARY"); + gf_node_insert_child(read->root, n, -1); + gf_node_register(n, read->root); + /*empty shape to fill depth levels & sprites roots*/ + n2 = s2b_new_node(read, TAG_MPEG4_Shape); + gf_node_set_id(n2, 4, "Shape0"); + gf_node_list_add_child( &((M_Switch *)n)->choice, n2); + gf_node_register(n2, n); + + /*display list*/ + n = s2b_new_node(read, TAG_MPEG4_Transform2D); + gf_node_set_id(n, 5, "CLIP0_DL"); + gf_node_insert_child(read->root, n, -1); + gf_node_register(n, read->root); + /*update w/h transform*/ + ((M_Transform2D *)n)->scale.y = -FIX_ONE; + ((M_Transform2D *)n)->translation.x = -read->width/2; + ((M_Transform2D *)n)->translation.y = read->height/2; + + read->load->ctx->max_node_id = 5; + + + swf_init_od(read, 1); + + /*always reserve ES_ID=2 for OD stream, 3 for main ctrl stream if any*/ + read->prev_es_id = 3; + /*always reserve OD_ID=1 for main ctrl stream if any - use same IDs are ESs*/ + read->prev_od_id = 3; + + + /*setup IndexedCurve2D proto*/ + if (read->flags & GF_SM_SWF_USE_IC2D) { + GF_ProtoFieldInterface *pfield; + GF_FieldInfo info; + SFURL *url; + Fixed ftMin, ftMax; + GF_Proto *proto = gf_sg_proto_new(read->load->scene_graph, 1, "IndexedCurve2D", 0); + if (read->load->ctx) read->load->ctx->max_proto_id = 1; + gf_sg_vrml_mf_reset(&proto->ExternProto, GF_SG_VRML_MFURL); + gf_sg_vrml_mf_append(&proto->ExternProto, GF_SG_VRML_MFURL, (void **) &url); + url->url = strdup("urn:inet:gpac:builtin:IndexedCurve2D"); + + gf_sg_proto_field_new(proto, GF_SG_VRML_SFNODE, GF_SG_EVENT_EXPOSED_FIELD, "coord"); + + pfield = gf_sg_proto_field_new(proto, GF_SG_VRML_SFFLOAT, GF_SG_EVENT_EXPOSED_FIELD, "fineness"); + gf_sg_proto_field_get_field(pfield, &info); + *((SFFloat*)info.far_ptr) = FIX_ONE/2; + + pfield = gf_sg_proto_field_new(proto, GF_SG_VRML_MFINT32, GF_SG_EVENT_EXPOSED_FIELD, "type"); + ftMin = 0; + ftMax = 15*FIX_ONE; + gf_bifs_proto_field_set_aq_info(pfield, 13, 1, GF_SG_VRML_SFINT32, &ftMin, &ftMax, 4); + + pfield = gf_sg_proto_field_new(proto, GF_SG_VRML_MFINT32, GF_SG_EVENT_EXPOSED_FIELD, "coordIndex"); + ftMin = 0; + ftMax = FIX_MAX; + gf_bifs_proto_field_set_aq_info(pfield, 14, 1, GF_SG_VRML_SFINT32, &ftMin, &ftMax, 0); + } + + /*no control stream*/ + if (!(read->flags & GF_SM_SWF_SPLIT_TIMELINE)) return GF_OK; + + /*init OD*/ + e = swf_init_od(read, 0); + if (e) return e; + + /*we want a dynamic dictionary*/ + if (!(read->flags & GF_SM_SWF_STATIC_DICT)) { + read->bifs_dict_es = read->bifs_es; + read->bifs_dict_au = read->bifs_au; + } + + /*create animationStream object*/ + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + if (!od) return GF_OUT_OF_MEM; + od->objectDescriptorID = 1; + esd = (GF_ESD *) gf_odf_desc_esd_new(0); + if (!esd) return GF_OUT_OF_MEM; + esd->ESID = esd->OCRESID = 3; + esd->dependsOnESID = 1; + esd->decoderConfig->streamType = GF_STREAM_SCENE; + esd->decoderConfig->objectTypeIndication = 1; + esd->slConfig->timestampResolution = read->bifs_es->timeScale; + gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); + esd->decoderConfig->decoderSpecificInfo = NULL; + gf_list_add(od->ESDescriptors, esd); + e = swf_insert_od(read, 0, od); + if (e) { + gf_odf_desc_del((GF_Descriptor *) od); + return e; + } + + /*setup a new BIFS context*/ + prev_sc = read->bifs_es; + read->bifs_es = gf_sm_stream_new(read->load->ctx, esd->ESID, GF_STREAM_SCENE, 1); + read->bifs_es->timeScale = prev_sc->timeScale; + /*create first AU*/ + read->bifs_au = gf_sm_stream_au_new(read->bifs_es, 0, 0, 1); + + /*setup the animationStream node*/ + n = s2b_new_node(read, TAG_MPEG4_AnimationStream); + gf_node_insert_child(read->root, n, 0); + gf_node_register(n, read->root); + /*assign URL*/ + gf_node_get_field_by_name(n, "url", &info); + gf_sg_vrml_mf_alloc(info.far_ptr, info.fieldType, 1); + ((MFURL*)info.far_ptr)->vals[0].OD_ID = 1; + /*run from start*/ + ((M_AnimationStream *)n)->startTime = 0; + ((M_AnimationStream *)n)->loop = 0; + + /*setup the MediaControl node*/ + n = s2b_new_node(read, TAG_MPEG4_MediaControl); + read->load->ctx->max_node_id++; + ID = read->load->ctx->max_node_id; + gf_node_set_id(n, ID, "CLIP0_CTRL"); + gf_node_insert_child(read->root, n, 0); + gf_node_register(n, read->root); + /*assign URL*/ + gf_node_get_field_by_name(n, "url", &info); + gf_sg_vrml_mf_alloc(info.far_ptr, info.fieldType, 1); + ((MFURL*)info.far_ptr)->vals[0].OD_ID = 1; + /*run from start*/ + ((M_MediaControl *)n)->loop = 0; + + return GF_OK; +} + +#endif + diff --git a/src/scene_manager/swf_parse.c b/src/scene_manager/swf_parse.c new file mode 100644 index 0000000..77d9983 --- /dev/null +++ b/src/scene_manager/swf_parse.c @@ -0,0 +1,2498 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include + +#include + +#ifndef GPAC_READ_ONLY + + +/*display list item (one per layer only)*/ +typedef struct +{ + GF_Matrix2D mat; + GF_ColorMatrix cmat; + u32 depth; + u32 char_id; +} DispShape; + +enum +{ + SWF_END = 0, + SWF_SHOWFRAME = 1, + SWF_DEFINESHAPE = 2, + SWF_FREECHARACTER = 3, + SWF_PLACEOBJECT = 4, + SWF_REMOVEOBJECT = 5, + SWF_DEFINEBITS = 6, + SWF_DEFINEBITSJPEG = 6, + SWF_DEFINEBUTTON = 7, + SWF_JPEGTABLES = 8, + SWF_SETBACKGROUNDCOLOR = 9, + SWF_DEFINEFONT = 10, + SWF_DEFINETEXT = 11, + SWF_DOACTION = 12, + SWF_DEFINEFONTINFO = 13, + SWF_DEFINESOUND = 14, + SWF_STARTSOUND = 15, + SWF_DEFINEBUTTONSOUND = 17, + SWF_SOUNDSTREAMHEAD = 18, + SWF_SOUNDSTREAMBLOCK = 19, + SWF_DEFINEBITSLOSSLESS = 20, + SWF_DEFINEBITSJPEG2 = 21, + SWF_DEFINESHAPE2 = 22, + SWF_DEFINEBUTTONCXFORM = 23, + SWF_PROTECT = 24, + SWF_PLACEOBJECT2 = 26, + SWF_REMOVEOBJECT2 = 28, + SWF_DEFINESHAPE3 = 32, + SWF_DEFINETEXT2 = 33, + SWF_DEFINEBUTTON2 = 34, + SWF_DEFINEBITSJPEG3 = 35, + SWF_DEFINEBITSLOSSLESS2 = 36, + SWF_DEFINEEDITTEXT = 37, + SWF_DEFINEMOVIE = 38, + SWF_DEFINESPRITE = 39, + SWF_NAMECHARACTER = 40, + SWF_SERIALNUMBER = 41, + SWF_GENERATORTEXT = 42, + SWF_FRAMELABEL = 43, + SWF_SOUNDSTREAMHEAD2 = 45, + SWF_DEFINEMORPHSHAPE = 46, + SWF_DEFINEFONT2 = 48, + SWF_TEMPLATECOMMAND = 49, + SWF_GENERATOR3 = 51, + SWF_EXTERNALFONT = 52, + SWF_EXPORTASSETS = 56, + SWF_IMPORTASSETS = 57, + SWF_ENABLEDEBUGGER = 58, + SWF_MX0 = 59, + SWF_MX1 = 60, + SWF_MX2 = 61, + SWF_MX3 = 62, + SWF_MX4 = 63, + SWF_REFLEX = 777 +}; + + +static void swf_init_decompress(SWFReader *read) +{ + u32 size, dst_size; + char *src, *dst; + + size = (u32) gf_bs_get_size(read->bs)-8; + dst_size = read->length; + src = malloc(sizeof(char)*size); + dst = malloc(sizeof(char)*dst_size); + memset(dst, 0, sizeof(char)*8); + gf_bs_read_data(read->bs, src, size); + dst_size -= 8; + uncompress(dst+8, (uLongf *)&dst_size, src, size); + dst_size += 8; + free(src); + read->mem = dst; + gf_bs_del(read->bs); + read->bs = gf_bs_new(read->mem, dst_size, GF_BITSTREAM_READ); + gf_bs_skip_bytes(read->bs, 8); +} + + +static GF_Err swf_seek_file_to(SWFReader *read, u32 size) +{ + return gf_bs_seek(read->bs, size); +} + +static u32 swf_get_file_pos(SWFReader *read) +{ + return (u32) gf_bs_get_position(read->bs); +} + +static u32 swf_read_data(SWFReader *read, char *data, u32 data_size) +{ + return gf_bs_read_data(read->bs, data, data_size); +} + +static u32 swf_read_int(SWFReader *read, u32 nbBits) +{ + return gf_bs_read_int(read->bs, nbBits); +} + +static s32 swf_read_sint(SWFReader *read, u32 nbBits) +{ + s32 r = 0; + u32 i; + if (!nbBits)return 0; + r = -1 * (s32) swf_read_int(read, 1); + for (i=1; ibs); +} + +static void swf_skip_data(SWFReader *read, u32 size) +{ + while (size && !read->ioerr) { + swf_read_int(read, 8); + size --; + } +} + +static void swf_get_rec(SWFReader *read, SWFRec *rc) +{ + u32 nbbits; + swf_align(read); + nbbits = swf_read_int(read, 5); + rc->x = FLT2FIX( swf_read_sint(read, nbbits) * SWF_TWIP_SCALE ); + rc->w = FLT2FIX( swf_read_sint(read, nbbits) * SWF_TWIP_SCALE ); + rc->w -= rc->x; + rc->y = FLT2FIX( swf_read_sint(read, nbbits) * SWF_TWIP_SCALE ); + rc->h = FLT2FIX( swf_read_sint(read, nbbits) * SWF_TWIP_SCALE ); + rc->h -= rc->y; +} + +static u32 swf_get_32(SWFReader *read) +{ + u32 val, res; + val = swf_read_int(read, 32); + res = (val&0xFF); res <<=8; + res |= ((val>>8)&0xFF); res<<=8; + res |= ((val>>16)&0xFF); res<<=8; + res|= ((val>>24)&0xFF); + return res; +} + +static u16 swf_get_16(SWFReader *read) +{ + u16 val, res; + val = swf_read_int(read, 16); + res = (val&0xFF); res <<=8; + res |= ((val>>8)&0xFF); + return res; +} + +static s16 swf_get_s16(SWFReader *read) +{ + s16 val; + u8 v1; + v1 = swf_read_int(read, 8); + val = swf_read_sint(read, 8); + val = (val<<8)&0xFF00; + val |= (v1&0xFF); + return val; +} + +static u32 swf_get_color(SWFReader *read) +{ + u32 res; + res = 0xFF00; + res |= swf_read_int(read, 8); res<<=8; + res |= swf_read_int(read, 8); res<<=8; + res |= swf_read_int(read, 8); + return res; +} + +static u32 swf_get_argb(SWFReader *read) +{ + u32 res, al; + res = swf_read_int(read, 8); res<<=8; + res |= swf_read_int(read, 8); res<<=8; + res |= swf_read_int(read, 8); + al = swf_read_int(read, 8); + return ((al<<24) | res); +} + +static u32 swf_get_matrix(SWFReader *read, GF_Matrix2D *mat) +{ + u32 bits_read; + u32 flag, nb_bits; + + memset(mat, 0, sizeof(GF_Matrix2D)); + mat->m[0] = mat->m[4] = FIX_ONE; + + bits_read = swf_align(read); + + flag = swf_read_int(read, 1); + bits_read += 1; + if (flag) { + nb_bits = swf_read_int(read, 5); +#ifdef GPAC_FIXED_POINT + mat->m[0] = swf_read_sint(read, nb_bits); + mat->m[4] = swf_read_sint(read, nb_bits); +#else + mat->m[0] = (Float) swf_read_sint(read, nb_bits); + mat->m[0] /= 0x10000; + mat->m[4] = (Float) swf_read_sint(read, nb_bits); + mat->m[4] /= 0x10000; +#endif + bits_read += 5 + 2*nb_bits; + } + flag = swf_read_int(read, 1); + bits_read += 1; + if (flag) { + nb_bits = swf_read_int(read, 5); + /*WATCHOUT FOR ORDER*/ +#ifdef GPAC_FIXED_POINT + mat->m[3] = swf_read_sint(read, nb_bits); + mat->m[1] = swf_read_sint(read, nb_bits); +#else + mat->m[3] = (Float) swf_read_sint(read, nb_bits); + mat->m[3] /= 0x10000; + mat->m[1] = (Float) swf_read_sint(read, nb_bits); + mat->m[1] /= 0x10000; +#endif + bits_read += 5 + 2*nb_bits; + } + nb_bits = swf_read_int(read, 5); + bits_read += 5 + 2*nb_bits; + if (nb_bits) { + mat->m[2] = FLT2FIX( swf_read_sint(read, nb_bits) * SWF_TWIP_SCALE ); + mat->m[5] = FLT2FIX( swf_read_sint(read, nb_bits) * SWF_TWIP_SCALE ); + } + return bits_read; +} + +#define SWF_COLOR_SCALE (1/256.0f) + +static void swf_get_colormatrix(SWFReader *read, GF_ColorMatrix *cmat) +{ + Bool has_add, has_mul; + u32 nbbits; + memset(cmat, 0, sizeof(GF_ColorMatrix)); + cmat->m[0] = cmat->m[6] = cmat->m[12] = cmat->m[18] = FIX_ONE; + + has_add = swf_read_int(read, 1); + has_mul = swf_read_int(read, 1); + nbbits = swf_read_int(read, 4); + if (has_mul) { + cmat->m[0] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); + cmat->m[6] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); + cmat->m[12] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); + cmat->m[18] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); + } + if (has_add) { + cmat->m[4] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); + cmat->m[9] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); + cmat->m[14] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); + cmat->m[19] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); + } + cmat->identity = 0; + if ((cmat->m[0] == cmat->m[6]) + && (cmat->m[0] == cmat->m[12]) + && (cmat->m[0] == cmat->m[18]) + && (cmat->m[0] == FIX_ONE) + && (cmat->m[4] == cmat->m[9]) + && (cmat->m[4] == cmat->m[14]) + && (cmat->m[4] == cmat->m[19]) + && (cmat->m[4] == 0)) + cmat->identity = 1; +} + +static char *swf_get_string(SWFReader *read) +{ + char szName[1024]; + char *name; + u32 i = 0; + + if (read->size>1024) { + name = malloc(sizeof(char)*read->size); + } else { + name = szName; + } + while (1) { + name[i] = swf_read_int(read, 8); + if (!name[i]) break; + i++; + } + if (read->size>1024) { + return realloc(name, sizeof(char)*(strlen(name)+1)); + } else { + return strdup(szName); + } +} + +static SWFShapeRec *swf_new_shape_rec() +{ + SWFShapeRec *style; + GF_SAFEALLOC(style, SWFShapeRec); + GF_SAFEALLOC(style->path, SWFPath); + return style; +} + +static SWFShapeRec *swf_clone_shape_rec(SWFShapeRec *old_sr) +{ + SWFShapeRec *new_sr = (SWFShapeRec *)malloc(sizeof(SWFShapeRec)); + memcpy(new_sr, old_sr, sizeof(SWFShapeRec)); + new_sr->path = (SWFPath*)malloc(sizeof(SWFPath)); + memset(new_sr->path, 0, sizeof(SWFPath)); + + if (old_sr->nbGrad) { + new_sr->grad_col = (u32*)malloc(sizeof(u32) * old_sr->nbGrad); + memcpy(new_sr->grad_col, old_sr->grad_col, sizeof(u32) * old_sr->nbGrad); + new_sr->grad_ratio = (u8*)malloc(sizeof(u8) * old_sr->nbGrad); + memcpy(new_sr->grad_ratio, old_sr->grad_ratio, sizeof(u8) * old_sr->nbGrad); + } + return new_sr; +} + +/*parse/append fill and line styles*/ +static void swf_parse_styles(SWFReader *read, u32 revision, SWFShape *shape, u32 *bits_fill, u32 *bits_line) +{ + u32 i, j, count; + SWFShapeRec *style; + + swf_align(read); + + /*get fill styles*/ + count = swf_read_int(read, 8); + if (revision && (count== 0xFF)) count = swf_get_16(read); + if (count) { + for (i=0; isolid_col = 0xFF00FF00; + style->type = swf_read_int(read, 8); + + /*gradient fill*/ + if (style->type & 0x10) { + swf_get_matrix(read, &style->mat); + swf_align(read); + style->nbGrad = swf_read_int(read, 8); + if (style->nbGrad) { + style->grad_col = (u32 *) malloc(sizeof(u32) * style->nbGrad); + style->grad_ratio = (u8 *) malloc(sizeof(u8) * style->nbGrad); + for (j=0; jnbGrad; j++) { + style->grad_ratio[j] = swf_read_int(read, 8); + if (revision==2) style->grad_col[j] = swf_get_argb(read); + else style->grad_col[j] = swf_get_color(read); + } + style->solid_col = style->grad_col[0]; + + /*make sure we have keys between 0 and 1.0 for BIFS (0 and 255 in swf)*/ + if (style->grad_ratio[0] != 0) { + u32 i; + u32 *grad_col; + u8 *grad_ratio; + grad_ratio = (u8 *) malloc(sizeof(u8) * (style->nbGrad+1)); + grad_col = (u32 *) malloc(sizeof(u32) * (style->nbGrad+1)); + grad_col[0] = style->grad_col[0]; + grad_ratio[0] = 0; + for (i=0; inbGrad; i++) { + grad_col[i+1] = style->grad_col[i]; + grad_ratio[i+1] = style->grad_ratio[i]; + } + free(style->grad_col); + style->grad_col = grad_col; + free(style->grad_ratio); + style->grad_ratio = grad_ratio; + style->nbGrad++; + } + if (style->grad_ratio[style->nbGrad-1] != 255) { + u32 *grad_col = (u32*)malloc(sizeof(u32) * (style->nbGrad+1)); + u8 *grad_ratio = (u8*)malloc(sizeof(u8) * (style->nbGrad+1)); + memcpy(grad_col, style->grad_col, sizeof(u32) * style->nbGrad); + memcpy(grad_ratio, style->grad_ratio, sizeof(u8) * style->nbGrad); + grad_col[style->nbGrad] = style->grad_col[style->nbGrad-1]; + grad_ratio[style->nbGrad] = 255; + free(style->grad_col); + style->grad_col = grad_col; + free(style->grad_ratio); + style->grad_ratio = grad_ratio; + style->nbGrad++; + } + + } else { + style->solid_col = 0xFF; + } + } + /*bitmap fill*/ + else if (style->type & 0x40) { + style->img_id = swf_get_16(read); + if (style->img_id == 65535) { + style->img_id = 0; + style->type = 0; + style->solid_col = 0xFF00FFFF; + } + swf_get_matrix(read, &style->mat); + } + /*solid fill*/ + else { + if (revision==2) style->solid_col = swf_get_argb(read); + else style->solid_col = swf_get_color(read); + } + gf_list_add(shape->fill_right, style); + style = swf_clone_shape_rec(style); + gf_list_add(shape->fill_left, style); + } + } + + swf_align(read); + /*get line styles*/ + count = swf_read_int(read, 8); + if (revision && (count==0xFF)) count = swf_get_16(read); + if (count) { + for (i=0; ilines, style); + style->width = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); + if (revision==2) style->solid_col = swf_get_argb(read); + else style->solid_col = swf_get_color(read); + } + } + + swf_align(read); + *bits_fill = swf_read_int(read, 4); + *bits_line = swf_read_int(read, 4); +} + +static void swf_path_realloc_pts(SWFPath *path, u32 nbPts) +{ + path->pts = (SFVec2f*)realloc(path->pts, sizeof(SFVec2f) * (path->nbPts + nbPts)); +} + +static void swf_path_add_com(SWFShapeRec *sr, SFVec2f pt, SFVec2f ctr, u32 type) +{ + /*not an error*/ + if (!sr) return; + + sr->path->types = (u32*)realloc(sr->path->types, sizeof(u32) * (sr->path->nbType+1)); + + sr->path->types[sr->path->nbType] = type; + switch (type) { + case 2: + swf_path_realloc_pts(sr->path, 2); + sr->path->pts[sr->path->nbPts] = ctr; + sr->path->pts[sr->path->nbPts+1] = pt; + sr->path->nbPts+=2; + break; + case 1: + default: + swf_path_realloc_pts(sr->path, 1); + sr->path->pts[sr->path->nbPts] = pt; + sr->path->nbPts++; + break; + } + sr->path->nbType++; +} + +static void swf_referse_path(SWFPath *path) +{ + u32 i, j, pti, ptj; + u32 *types; + SFVec2f *pts; + + if (path->nbType<=1) return; + + types = (u32 *) malloc(sizeof(u32) * path->nbType); + pts = (SFVec2f *) malloc(sizeof(SFVec2f) * path->nbPts); + + + /*need first moveTo*/ + types[0] = 0; + pts[0] = path->pts[path->nbPts - 1]; + pti = path->nbPts - 2; + ptj = 1; + j=1; + + for (i=0; inbType-1; i++) { + types[j] = path->types[path->nbType - i - 1]; + switch (types[j]) { + case 2: + assert(ptj<=path->nbPts-2); + pts[ptj] = path->pts[pti]; + pts[ptj+1] = path->pts[pti-1]; + pti-=2; + ptj+=2; + break; + case 1: + assert(ptj<=path->nbPts-1); + pts[ptj] = path->pts[pti]; + pti--; + ptj++; + break; + case 0: + assert(ptj<=path->nbPts-1); + pts[ptj] = path->pts[pti]; + pti--; + ptj++; + break; + } + j++; + } + free(path->pts); + path->pts = pts; + free(path->types); + path->types = types; +} + +static void swf_free_shape_rec(SWFShapeRec *ptr) +{ + if (ptr->grad_col) free(ptr->grad_col); + if (ptr->grad_ratio) free(ptr->grad_ratio); + if (ptr->path) { + if (ptr->path->pts) free(ptr->path->pts); + if (ptr->path->types) free(ptr->path->types); + if (ptr->path->idx) free(ptr->path->idx); + free(ptr->path); + } + free(ptr); +} + +static void swf_reset_rec_list(GF_List *recs) +{ + while (gf_list_count(recs)) { + SWFShapeRec *tmp = (SWFShapeRec *)gf_list_get(recs, 0); + gf_list_rem(recs, 0); + swf_free_shape_rec(tmp); + } +} + +static void swf_append_path(SWFPath *a, SWFPath *b) +{ + if (b->nbType<=1) return; + + a->pts = (SFVec2f*)realloc(a->pts, sizeof(SFVec2f) * (a->nbPts + b->nbPts)); + memcpy(&a->pts[a->nbPts], b->pts, sizeof(SFVec2f)*b->nbPts); + a->nbPts += b->nbPts; + + a->types = (u32*)realloc(a->types, sizeof(u32)*(a->nbType+ b->nbType)); + memcpy(&a->types[a->nbType], b->types, sizeof(u32)*b->nbType); + a->nbType += b->nbType; +} + +static void swf_path_add_type(SWFPath *path, u32 val) +{ + path->types = (u32*)realloc(path->types, sizeof(u32) * (path->nbType + 1)); + path->types[path->nbType] = val; + path->nbType++; +} + +static void swf_resort_path(SWFPath *a, SWFReader *read) +{ + u32 idx, i, j; + GF_List *paths; + SWFPath *sorted, *p, *np; + + if (!a->nbType) return; + + paths = gf_list_new(); + GF_SAFEALLOC(sorted, SWFPath); + swf_path_realloc_pts(sorted, 1); + sorted->pts[sorted->nbPts] = a->pts[0]; + sorted->nbPts++; + swf_path_add_type(sorted, 0); + gf_list_add(paths, sorted); + + /*1- split all paths*/ + idx = 1; + for (i=1; inbType; i++) { + switch (a->types[i]) { + case 2: + swf_path_realloc_pts(sorted, 2); + sorted->pts[sorted->nbPts] = a->pts[idx]; + sorted->pts[sorted->nbPts+1] = a->pts[idx+1]; + sorted->nbPts+=2; + swf_path_add_type(sorted, 2); + idx += 2; + break; + case 1: + swf_path_realloc_pts(sorted, 1); + sorted->pts[sorted->nbPts] = a->pts[idx]; + sorted->nbPts+=1; + swf_path_add_type(sorted, 1); + idx += 1; + break; + case 0: + GF_SAFEALLOC(sorted , SWFPath); + swf_path_realloc_pts(sorted, 1); + sorted->pts[sorted->nbPts] = a->pts[idx]; + sorted->nbPts++; + swf_path_add_type(sorted, 0); + gf_list_add(paths, sorted); + idx += 1; + break; + } + } + +restart: + for (i=0; ipts[np->nbPts-1].x == p->pts[0].x) && (np->pts[np->nbPts-1].y == p->pts[0].y)) { + u32 k; + idx = 1; + for (k=1; knbType; k++) { + switch (p->types[k]) { + case 2: + swf_path_realloc_pts(np, 2); + np->pts[np->nbPts] = p->pts[idx]; + np->pts[np->nbPts+1] = p->pts[idx+1]; + np->nbPts+=2; + swf_path_add_type(np, 2); + idx += 2; + break; + case 1: + swf_path_realloc_pts(np, 1); + np->pts[np->nbPts] = p->pts[idx]; + np->nbPts+=1; + swf_path_add_type(np, 1); + idx += 1; + break; + default: + assert(0); + break; + } + } + free(p->pts); + free(p->types); + free(p); + gf_list_rem(paths, i); + goto restart; + } + /*check if any next subpath starts at the same place we're ending*/ + else if ((p->pts[p->nbPts-1].x == np->pts[0].x) && (p->pts[p->nbPts-1].y == np->pts[0].y)) { + u32 k; + idx = 1; + for (k=1; knbType; k++) { + switch (np->types[k]) { + case 2: + swf_path_realloc_pts(p, 2); + p->pts[p->nbPts] = np->pts[idx]; + p->pts[p->nbPts+1] = np->pts[idx+1]; + p->nbPts+=2; + swf_path_add_type(p, 2); + idx += 2; + break; + case 1: + swf_path_realloc_pts(p, 1); + p->pts[p->nbPts] = np->pts[idx]; + p->nbPts+=1; + swf_path_add_type(p, 1); + idx += 1; + break; + default: + assert(0); + break; + } + } + free(np->pts); + free(np->types); + free(np); + gf_list_rem(paths, j); + j--; + } + } + } + + /*reassemble path*/ + free(a->pts); + free(a->types); + memset(a, 0, sizeof(SWFPath)); + + while (gf_list_count(paths)) { + sorted = (SWFPath*)gf_list_get(paths, 0); + if (read->flat_limit==0) { + swf_append_path(a, sorted); + } else { + Bool prev_is_line_to = 0; + idx = 0; + for (i=0; inbType; i++) { + switch (sorted->types[i]) { + case 2: + swf_path_realloc_pts(a, 2); + a->pts[a->nbPts] = sorted->pts[idx]; + a->pts[a->nbPts+1] = sorted->pts[idx+1]; + a->nbPts+=2; + swf_path_add_type(a, 2); + idx += 2; + prev_is_line_to = 0; + break; + case 1: + if (prev_is_line_to) { + Fixed angle; + Bool flatten = 0; + SFVec2f v1, v2; + v1.x = a->pts[a->nbPts-1].x - a->pts[a->nbPts-2].x; + v1.y = a->pts[a->nbPts-1].y - a->pts[a->nbPts-2].y; + v2.x = a->pts[a->nbPts-1].x - sorted->pts[idx].x; + v2.y = a->pts[a->nbPts-1].y - sorted->pts[idx].y; + + angle = gf_mulfix(v1.x,v2.x) + gf_mulfix(v1.y,v2.y); + /*get magnitudes*/ + v1.x = gf_v2d_len(&v1); + v2.x = gf_v2d_len(&v2); + if (!v1.x || !v2.x) flatten = 1; + else { + Fixed h_pi = GF_PI / 2; + angle = gf_divfix(angle, gf_mulfix(v1.x, v2.x)); + if (angle + FIX_EPSILON >= FIX_ONE) angle = 0; + else if (angle - FIX_EPSILON <= -FIX_ONE) angle = GF_PI; + else angle = gf_acos(angle); + + if (angle<0) angle += h_pi; + angle = ABSDIFF(angle, h_pi); + if (angle < read->flat_limit) flatten = 1; + } + if (flatten) { + a->pts[a->nbPts-1] = sorted->pts[idx]; + idx++; + read->flatten_points++; + break; + } + } + swf_path_realloc_pts(a, 1); + a->pts[a->nbPts] = sorted->pts[idx]; + a->nbPts+=1; + swf_path_add_type(a, 1); + idx += 1; + prev_is_line_to = 1; + break; + case 0: + swf_path_realloc_pts(a, 1); + a->pts[a->nbPts] = sorted->pts[idx]; + a->nbPts+=1; + swf_path_add_type(a, 0); + idx += 1; + prev_is_line_to = 0; + break; + } + } + } + free(sorted->pts); + free(sorted->types); + free(sorted); + gf_list_rem(paths, 0); + } + gf_list_del(paths); +} + +/* + Notes on SWF->BIFS conversion - some ideas taken from libswfdec + A single fillStyle has 2 associated path, one used for left fill, one for right fill + This is then a 4 step process: + 1- blindly parse swf shape, and add point/lines to the proper left/right path + 2- for each fillStyles, revert the right path so that it becomes a left path + 3- concatenate left and right paths + 4- resort all subelements of the final path, making sure moveTo introduced by the SWF coding (due to style changes) + are removed. + Ex: if path is + A->C, B->A, C->B = moveTo(A), lineTo(C), moveTo(B), lineTo (A), moveTo(C), lineTo(B) + we restort and remove unneeded moves to get + A->C->B = moveTo(A), lineTo(C), lineTo(B), lineTo(A) +*/ +static GF_Err swf_flush_shape(SWFReader *read, SWFShape *shape, SWFFont *font, Bool last_shape) +{ + GF_Err e; + SWFShapeRec *sf0, *sf1, *sl; + u32 i, count; + count = gf_list_count(shape->fill_left); + for (i=0; ifill_left, i); + sf1 = (SWFShapeRec*)gf_list_get(shape->fill_right, i); + /*reverse right path*/ + swf_referse_path(sf1->path); + /*concatenate with left path*/ + swf_append_path(sf0->path, sf1->path); + /*resort all path curves*/ + swf_resort_path(sf0->path, read); + } + /*remove dummy fill_left*/ + for (i=0; ifill_left); i++) { + sf0 = (SWFShapeRec*)gf_list_get(shape->fill_left, i); + if (sf0->path->nbType<=1) { + gf_list_rem(shape->fill_left, i); + swf_free_shape_rec(sf0); + i--; + } + } + /*remove dummy lines*/ + for (i=0; ilines); i++) { + sl = (SWFShapeRec*)gf_list_get(shape->lines, i); + if (sl->path->nbType<1) { + gf_list_rem(shape->lines, i); + swf_free_shape_rec(sl); + i--; + } else { + swf_resort_path(sl->path, read); + } + } + + /*now translate a flash shape record into BIFS*/ + e = read->define_shape(read, shape, font, last_shape); + + /*delete shape*/ + swf_reset_rec_list(shape->fill_left); + swf_reset_rec_list(shape->fill_right); + swf_reset_rec_list(shape->lines); + return e; +} + +static GF_Err swf_parse_shape_def(SWFReader *read, SWFFont *font, u32 revision) +{ + u32 nbBits, comType; + s32 x, y; + SFVec2f orig, ctrl, end; + Bool flag; + u32 fill0, fill1, strike; + u32 bits_fill, bits_line; + SWFShape shape; + Bool is_empty; + SWFShapeRec *sf0, *sf1, *sl; + + memset(&shape, 0, sizeof(SWFShape)); + shape.fill_left = gf_list_new(); + shape.fill_right = gf_list_new(); + shape.lines = gf_list_new(); + ctrl.x = ctrl.y = 0; + swf_align(read); + + /*regular shape - get initial styles*/ + if (!font) { + shape.ID = swf_get_16(read); + swf_get_rec(read, &shape.rc); + swf_parse_styles(read, revision, &shape, &bits_fill, &bits_line); + } + /*glyph*/ + else { + bits_fill = swf_read_int(read, 4); + bits_line = swf_read_int(read, 4); + + /*fonts are usually defined without styles*/ + if ((read->tag == SWF_DEFINEFONT) || (read->tag==SWF_DEFINEFONT2)) { + sf0 = swf_new_shape_rec(); + gf_list_add(shape.fill_right, sf0); + sf0 = swf_new_shape_rec(); + gf_list_add(shape.fill_left, sf0); + sf0->solid_col = 0xFF000000; + sf0->type = 0; + } + } + + is_empty = 1; + comType = 0; + /*parse all points*/ + fill0 = fill1 = strike = 0; + sf0 = sf1 = sl = NULL; + x = y = 0; + while (1) { + flag = swf_read_int(read, 1); + if (!flag) { + Bool new_style = swf_read_int(read, 1); + Bool set_strike = swf_read_int(read, 1); + Bool set_fill1 = swf_read_int(read, 1); + Bool set_fill0 = swf_read_int(read, 1); + Bool move_to = swf_read_int(read, 1); + /*end of shape*/ + if (!new_style && !set_strike && !set_fill0 && !set_fill1 && !move_to) break; + + is_empty = 0; + + if (move_to) { + nbBits = swf_read_int(read, 5); + x = swf_read_sint(read, nbBits); + y = swf_read_sint(read, nbBits); + } + if (set_fill0) fill0 = swf_read_int(read, bits_fill); + if (set_fill1) fill1 = swf_read_int(read, bits_fill); + if (set_strike) strike = swf_read_int(read, bits_line); + /*looks like newStyle does not append styles but define a new set - old styles can no + longer be referenced*/ + if (new_style) { + /*flush current shape record into BIFS*/ + swf_flush_shape(read, &shape, font, 0); + swf_parse_styles(read, revision, &shape, &bits_fill, &bits_line); + } + + if (read->flags & GF_SM_SWF_NO_LINE) strike = 0; + + /*moveto*/ + comType = 0; + orig.x = FLT2FIX( x * SWF_TWIP_SCALE ); + orig.y = FLT2FIX( y * SWF_TWIP_SCALE ); + end = orig; + + sf0 = fill0 ? (SWFShapeRec*)gf_list_get(shape.fill_left, fill0 - 1) : NULL; + sf1 = fill1 ? (SWFShapeRec*)gf_list_get(shape.fill_right, fill1 - 1) : NULL; + sl = strike ? (SWFShapeRec*)gf_list_get(shape.lines, strike - 1) : NULL; + + if (move_to) { + swf_path_add_com(sf0, end, ctrl, 0); + swf_path_add_com(sf1, end, ctrl, 0); + swf_path_add_com(sl, end, ctrl, 0); + } else { + if (set_fill0) swf_path_add_com(sf0, end, ctrl, 0); + if (set_fill1) swf_path_add_com(sf1, end, ctrl, 0); + if (set_strike) swf_path_add_com(sl, end, ctrl, 0); + } + + } else { + flag = swf_read_int(read, 1); + /*quadratic curve*/ + if (!flag) { + nbBits = 2 + swf_read_int(read, 4); + x += swf_read_sint(read, nbBits); + y += swf_read_sint(read, nbBits); + ctrl.x = FLT2FIX( x * SWF_TWIP_SCALE ); + ctrl.y = FLT2FIX( y * SWF_TWIP_SCALE ); + x += swf_read_sint(read, nbBits); + y += swf_read_sint(read, nbBits); + end.x = FLT2FIX( x * SWF_TWIP_SCALE ); + end.y = FLT2FIX( y * SWF_TWIP_SCALE ); + /*curveTo*/ + comType = 2; + } + /*straight line*/ + else { + nbBits = 2 + swf_read_int(read, 4); + flag = swf_read_int(read, 1); + if (flag) { + x += swf_read_sint(read, nbBits); + y += swf_read_sint(read, nbBits); + } else { + flag = swf_read_int(read, 1); + if (flag) { + y += swf_read_sint(read, nbBits); + } else { + x += swf_read_sint(read, nbBits); + } + } + /*lineTo*/ + comType = 1; + end.x = FLT2FIX( x * SWF_TWIP_SCALE ); + end.y = FLT2FIX( y * SWF_TWIP_SCALE ); + } + swf_path_add_com(sf0, end, ctrl, comType); + swf_path_add_com(sf1, end, ctrl, comType); + swf_path_add_com(sl, end, ctrl, comType); + } + } + + if (is_empty) { + swf_reset_rec_list(shape.fill_left); + swf_reset_rec_list(shape.fill_right); + swf_reset_rec_list(shape.lines); + } + + swf_align(read); + + /*now translate a flash shape record*/ + swf_flush_shape(read, &shape, font, 1); + + /*delete shape*/ + swf_reset_rec_list(shape.fill_left); + swf_reset_rec_list(shape.fill_right); + swf_reset_rec_list(shape.lines); + gf_list_del(shape.fill_left); + gf_list_del(shape.fill_right); + gf_list_del(shape.lines); + + return GF_OK; +} + +SWFFont *swf_find_font(SWFReader *read, u32 ID) +{ + u32 i, count; + count = gf_list_count(read->fonts); + for (i=0; ifonts, i); + if (ft->fontID==ID) return ft; + } + return NULL; +} + +static DispShape *swf_get_depth_entry(SWFReader *read, u32 Depth, Bool create) +{ + u32 i; + DispShape *tmp; + i=0; + while ((tmp = (DispShape *)gf_list_enum(read->display_list, &i))) { + if (tmp->depth == Depth) return tmp; + } + if (!create) return NULL; + GF_SAFEALLOC(tmp , DispShape); + tmp->depth = Depth; + tmp->char_id = 0; + gf_list_add(read->display_list, tmp); + + memset(&tmp->mat, 0, sizeof(GF_Matrix2D)); + tmp->mat.m[0] = tmp->mat.m[4] = FIX_ONE; + + memset(&tmp->cmat, 0, sizeof(GF_ColorMatrix)); + tmp->cmat.m[0] = tmp->cmat.m[6] = tmp->cmat.m[12] = tmp->cmat.m[18] = FIX_ONE; + tmp->cmat.identity = 1; + return tmp; +} + + +static GF_Err swf_func_skip(SWFReader *read) +{ + swf_skip_data(read, read->size); + return read->ioerr; +} + +static GF_Err swf_set_backcol(SWFReader *read) +{ + u32 col = swf_get_color(read); + return read->set_backcol(read, col); +} + +static GF_Err swf_actions(SWFReader *read, u32 mask, u32 key) +{ + u32 skip_actions = 0; + u8 action_code = swf_read_int(read, 8); + u16 length = 0; + read->has_interact = 1; + + +#define DO_ACT(_code) { act.type = _code; read->action(read, &act); break; } + + while (action_code) { + if (action_code > 0x80) length = swf_get_16(read); + else length = 0; + + if (read->no_as || skip_actions) { + swf_skip_data(read, length); + if (skip_actions) skip_actions--; + } else { + SWFAction act; + memset(&act, 0, sizeof(SWFAction)); + act.button_mask = mask; + act.button_key = key; + + switch (action_code) { + /* SWF 3 Action Model */ + case 0x81: /* goto frame */ + assert (length == 2); + act.type = GF_SWF_AS3_GOTO_FRAME; + act.frame_number = swf_get_16(read); + read->action(read, &act); + break; + case 0x83: /* get URL */ + act.type = GF_SWF_AS3_GET_URL; + act.url = swf_get_string(read); + act.target = swf_get_string(read); + read->action(read, &act); + free(act.url); + free(act.target); + break; + /* next frame */ + case 0x04: DO_ACT(GF_SWF_AS3_NEXT_FRAME) + /* previous frame */ + case 0x05: DO_ACT(GF_SWF_AS3_PREV_FRAME) + /* play */ + case 0x06: DO_ACT(GF_SWF_AS3_PLAY) + /* stop */ + case 0x07: DO_ACT(GF_SWF_AS3_STOP) + /* toggle quality */ + case 0x08: DO_ACT(GF_SWF_AS3_TOGGLE_QUALITY) + /* stop sounds*/ + case 0x09: DO_ACT(GF_SWF_AS3_STOP_SOUNDS) + /* wait for frame */ + case 0x8A: + assert (length == 3); + act.type = GF_SWF_AS3_WAIT_FOR_FRAME; + act.frame_number = swf_get_16(read); + skip_actions = swf_read_int(read, 8); + if (read->action(read, &act)) skip_actions = 0; + break; + /* set target */ + case 0x8B: + act.type = GF_SWF_AS3_SET_TARGET; + act.target = swf_get_string(read); + read->action(read, &act); + free(act.target); + break; + /* goto label */ + case 0x8C: + act.type = GF_SWF_AS3_GOTO_LABEL; + act.target = swf_get_string(read); + read->action(read, &act); + free(act.target); + break; + default: +// swf_report(read, GF_OK, "Skipping unsupported action %x", action_code); + if (length) swf_skip_data(read, length); + break; + } + } + action_code = swf_read_int(read, 8); + } +#undef DO_ACT + + return GF_OK; +} + +static GF_Err swf_def_button(SWFReader *read, u32 revision) +{ + SWF_Button button; + Bool has_actions; + + memset(&button, 0, sizeof(SWF_Button)); + has_actions = 0; + button.count = 0; + button.ID = swf_get_16(read); + if (revision==1) { + gf_bs_read_int(read->bs, 7); + gf_bs_read_int(read->bs, 1); + has_actions = swf_get_16(read); + } + while (1) { + SWF_ButtonRecord *rec = &button.buttons[button.count]; + gf_bs_read_int(read->bs, 4); + rec->hitTest = gf_bs_read_int(read->bs, 1); + rec->down = gf_bs_read_int(read->bs, 1); + rec->over = gf_bs_read_int(read->bs, 1); + rec->up = gf_bs_read_int(read->bs, 1); + if (!rec->hitTest && !rec->up && !rec->over && !rec->down) break; + rec->character_id = swf_get_16(read); + rec->depth = swf_get_16(read); + swf_get_matrix(read, &rec->mx); + if (revision==1) { + swf_align(read); + swf_get_colormatrix(read, &rec->cmx); + } + else gf_cmx_init(&rec->cmx); + gf_bs_align(read->bs); + button.count++; + } + read->define_button(read, &button); + if (revision==0) { + swf_actions(read, GF_SWF_COND_OVERUP_TO_OVERDOWN, 0); + } else { + while (has_actions) { + u32 i, mask, key; + has_actions = swf_get_16(read); + mask = 0; + for (i=0; i<8; i++) { + if (swf_read_int(read, 1)) + mask |= 1<define_button(read, NULL); + return GF_OK; +} + +static Bool swf_mat_is_identity(GF_Matrix2D *mat) +{ + if (mat->m[0] != FIX_ONE) return 0; + if (mat->m[4] != FIX_ONE) return 0; + if (mat->m[1]) return 0; + if (mat->m[2]) return 0; + if (mat->m[3]) return 0; + if (mat->m[5]) return 0; + return 1; +} + +static GF_Err swf_place_obj(SWFReader *read, u32 revision) +{ + GF_Err e; + u32 shape_id; + u32 ID, bitsize, ratio; + u32 clip_depth; + GF_Matrix2D mat; + GF_ColorMatrix cmat; + DispShape *ds; + char *name; + u32 depth, type; + Bool had_depth; + /*SWF flags*/ + Bool has_clip_actions, has_clip, has_name, has_ratio, has_cmat, has_mat, has_id, has_move; + + name = NULL; + clip_depth = 0; + ID = 0; + depth = 0; + has_clip_actions = has_clip = has_name = has_ratio = has_cmat = has_mat = has_id = has_move = 0; + + gf_cmx_init(&cmat); + gf_mx2d_init(mat); + /*place*/ + type = SWF_PLACE; + + /*SWF 1.0*/ + if (revision==0) { + ID = swf_get_16(read); + has_id = 1; + depth = swf_get_16(read); + bitsize = 32; + bitsize += swf_get_matrix(read, &mat); + has_mat = 1; + bitsize += swf_align(read); + /*size exceeds matrix, parse col mat*/ + if (bitsize < read->size*8) { + swf_get_colormatrix(read, &cmat); + has_cmat = 1; + swf_align(read); + } + } + /*SWF 3.0*/ + else if (revision==1) { + /*reserved*/ + has_clip_actions = swf_read_int(read, 1); + has_clip = swf_read_int(read, 1); + has_name = swf_read_int(read, 1); + has_ratio = swf_read_int(read, 1); + has_cmat = swf_read_int(read, 1); + has_mat = swf_read_int(read, 1); + has_id = swf_read_int(read, 1); + has_move = swf_read_int(read, 1); + + depth = swf_get_16(read); + if (has_id) ID = swf_get_16(read); + if (has_mat) { + swf_get_matrix(read, &mat); + swf_align(read); + } + if (has_cmat) { + swf_align(read); + swf_get_colormatrix(read, &cmat); + swf_align(read); + } + if (has_ratio) ratio = swf_get_16(read); + if (has_clip) clip_depth = swf_get_16(read); + + if (has_name) { + name = swf_get_string(read); + free(name); + } + if (has_clip_actions) { + swf_get_16(read); + swf_get_16(read); + } + /*replace*/ + if (has_id && has_move) type = SWF_REPLACE; + /*move*/ + else if (!has_id && has_move) type = SWF_MOVE; + /*place*/ + else type = SWF_PLACE; + } + + if (clip_depth) { + swf_report(read, GF_NOT_SUPPORTED, "Clipping not supported - ignoring"); + return GF_OK; + } + + /*1: check depth of display list*/ + had_depth = read->allocate_depth(read, depth); + /*check validity*/ + if ((type==SWF_MOVE) && !had_depth) swf_report(read, GF_BAD_PARAM, "Accessing empty depth level %d", depth); + + ds = NULL; + shape_id = 0; + /*usual case: (re)place depth level*/ + switch (type) { + case SWF_MOVE: + ds = swf_get_depth_entry(read, depth, 0); + shape_id = ds ? ds->char_id : 0; + break; + case SWF_REPLACE: + case SWF_PLACE: + default: + shape_id = ID; + break; + } + + if (!shape_id) { + swf_report(read, GF_BAD_PARAM, "%s unfound object (ID %d)", (type==SWF_MOVE) ? "Moving" : ((type==SWF_PLACE) ? "Placing" : "Replacing"), ID); + return GF_OK; + } + /*restore prev matrix if needed*/ + if (type==SWF_REPLACE) { + if (!ds) ds = swf_get_depth_entry(read, depth, 0); + if (ds) { + if (!has_mat) { + memcpy(&mat, &ds->mat, sizeof(GF_Matrix2D)); + has_mat = 1; + } + if (!has_cmat) { + memcpy(&cmat, &ds->cmat, sizeof(GF_ColorMatrix)); + has_cmat = 1; + } + } + } + + /*check for identity matrices*/ + if (has_cmat && cmat.identity) has_cmat = 0; + if (has_mat && swf_mat_is_identity(&mat)) has_mat = 0; + + /*store in display list*/ + ds = swf_get_depth_entry(read, depth, 1); + e = read->place_obj(read, depth, shape_id, ds->char_id, type, + has_mat ? &mat : NULL, + has_cmat ? &cmat : NULL, + swf_mat_is_identity(&ds->mat) ? NULL : &ds->mat, + ds->cmat.identity ? NULL : &ds->cmat); + + /*remember matrices*/ + memcpy(&ds->mat, &mat, sizeof(GF_Matrix2D)); + memcpy(&ds->cmat, &cmat, sizeof(GF_ColorMatrix)); + ds->char_id = shape_id; + + if (e) swf_report(read, e, "Error %s object ID %d", (type==SWF_MOVE) ? "Moving" : ((type==SWF_PLACE) ? "Placing" : "Replacing"), shape_id); + return GF_OK; +} + +static GF_Err swf_remove_obj(SWFReader *read, u32 revision) +{ + GF_Err e; + DispShape *ds; + u32 depth; + if (revision==0) swf_get_16(read); + depth = swf_get_16(read); + ds = swf_get_depth_entry(read, depth, 0); + /*this happens if a placeObject has failed*/ + if (!ds) return GF_OK; + e = read->remove_obj(read, depth, ds->char_id); + ds->char_id = 0; + return e; +} + +static GF_Err swf_show_frame(SWFReader *read) +{ + GF_Err e; + e = read->show_frame(read); + read->current_frame ++; + return e; +} + +static GF_Err swf_def_font(SWFReader *read, u32 revision) +{ + u32 i, count; + GF_Err e; + SWFFont *ft; + u32 *offset_table = NULL; + u32 start; + + GF_SAFEALLOC(ft, SWFFont); + ft->glyphs = gf_list_new(); + ft->fontID = swf_get_16(read); + e = GF_OK; + + + if (revision==0) { + u32 count; + + start = swf_get_file_pos(read); + + count = swf_get_16(read); + ft->nbGlyphs = count / 2; + offset_table = (u32*)malloc(sizeof(u32) * ft->nbGlyphs); + offset_table[0] = 0; + for (i=1; inbGlyphs; i++) offset_table[i] = swf_get_16(read); + + for (i=0; inbGlyphs; i++) { + swf_align(read); + e = swf_seek_file_to(read, start + offset_table[i]); + if (e) break; + swf_parse_shape_def(read, ft, 0); + } + free(offset_table); + if (e) return e; + } else if (revision==1) { + SWFRec rc; + Bool wide_offset, wide_codes; + u32 code_offset, checkpos; + ft->has_layout = swf_read_int(read, 1); + ft->has_shiftJIS = swf_read_int(read, 1); + ft->is_unicode = swf_read_int(read, 1); + ft->is_ansi = swf_read_int(read, 1); + wide_offset = swf_read_int(read, 1); + wide_codes = swf_read_int(read, 1); + ft->is_italic = swf_read_int(read, 1); + ft->is_bold = swf_read_int(read, 1); + swf_read_int(read, 8); + count = swf_read_int(read, 8); + ft->fontName = (char*)malloc(sizeof(u8)*count+1); + ft->fontName[count] = 0; + for (i=0; ifontName[i] = swf_read_int(read, 8); + + ft->nbGlyphs = swf_get_16(read); + start = swf_get_file_pos(read); + + if (ft->nbGlyphs) { + offset_table = (u32*)malloc(sizeof(u32) * ft->nbGlyphs); + for (i=0; inbGlyphs; i++) { + if (wide_offset) offset_table[i] = swf_get_32(read); + else offset_table[i] = swf_get_16(read); + } + } + + if (wide_offset) { + code_offset = swf_get_32(read); + } else { + code_offset = swf_get_16(read); + } + + if (ft->nbGlyphs) { + for (i=0; inbGlyphs; i++) { + swf_align(read); + e = swf_seek_file_to(read, start + offset_table[i]); + if (e) break; + + swf_parse_shape_def(read, ft, 0); + } + free(offset_table); + if (e) return e; + + checkpos = swf_get_file_pos(read); + if (checkpos != start + code_offset) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SWF Parsing] bad code offset in font\n")); + return GF_NON_COMPLIANT_BITSTREAM; + } + + ft->glyph_codes = (u16*)malloc(sizeof(u16) * ft->nbGlyphs); + for (i=0; inbGlyphs; i++) { + if (wide_codes) ft->glyph_codes[i] = swf_get_16(read); + else ft->glyph_codes[i] = swf_read_int(read, 8); + } + } + if (ft->has_layout) { + ft->ascent = swf_get_s16(read); + ft->descent = swf_get_s16(read); + ft->leading = swf_get_s16(read); + if (ft->nbGlyphs) { + ft->glyph_adv = (s16*)malloc(sizeof(s16) * ft->nbGlyphs); + for (i=0; inbGlyphs; i++) ft->glyph_adv[i] = swf_get_s16(read); + for (i=0; inbGlyphs; i++) swf_get_rec(read, &rc); + } + /*kerning info*/ + count = swf_get_16(read); + for (i=0; ifonts, ft); + return GF_OK; +} + +static GF_Err swf_def_font_info(SWFReader *read) +{ + SWFFont *ft; + Bool wide_chars; + u32 i, count; + + i = swf_get_16(read); + ft = swf_find_font(read, i); + if (!ft) { + swf_report(read, GF_BAD_PARAM, "Cannot locate font ID %d", i); + return GF_BAD_PARAM; + } + /*overwrite font info*/ + if (ft->fontName) free(ft->fontName); + count = swf_read_int(read, 8); + ft->fontName = (char*)malloc(sizeof(char) * (count+1)); + ft->fontName[count] = 0; + for (i=0; ifontName[i] = swf_read_int(read, 8); + swf_read_int(read, 2); + ft->is_unicode = swf_read_int(read, 1); + ft->has_shiftJIS = swf_read_int(read, 1); + ft->is_ansi = swf_read_int(read, 1); + ft->is_italic = swf_read_int(read, 1); + ft->is_bold = swf_read_int(read, 1); + /*TODO - this should be remapped to a font data stream, we currently only assume the glyph code + table is the same as the original font file...*/ + wide_chars = swf_read_int(read, 1); + if (ft->glyph_codes) free(ft->glyph_codes); + ft->glyph_codes = (u16*)malloc(sizeof(u16) * ft->nbGlyphs); + + for (i=0; inbGlyphs; i++) { + if (wide_chars) ft->glyph_codes[i] = swf_get_16(read); + else ft->glyph_codes[i] = swf_read_int(read, 8); + } + return GF_OK; +} + +static GF_Err swf_def_text(SWFReader *read, u32 revision) +{ + SWFRec rc; + SWFText txt; + Bool flag; + u32 nbits_adv, nbits_glyph, i, col, fontID, count, font_height; + Fixed offX, offY; + GF_Err e; + + txt.ID = swf_get_16(read); + swf_get_rec(read, &rc); + swf_get_matrix(read, &txt.mat); + txt.text = gf_list_new(); + + swf_align(read); + nbits_glyph = swf_read_int(read, 8); + nbits_adv = swf_read_int(read, 8); + fontID = 0; + offX = offY = 0; + font_height = 0; + col = 0xFF000000; + e = GF_OK; + + while (1) { + flag = swf_read_int(read, 1); + /*regular glyph record*/ + if (!flag) { + SWFGlyphRec *gr; + count = swf_read_int(read, 7); + if (!count) break; + + if (!fontID) { + e = GF_BAD_PARAM; + swf_report(read, GF_BAD_PARAM, "Defining text %d without assigning font", fontID); + break; + } + + GF_SAFEALLOC(gr, SWFGlyphRec); + gf_list_add(txt.text, gr); + gr->fontID = fontID; + gr->fontSize = font_height; + gr->col = col; + gr->orig_x = offX; + gr->orig_y = offY; + gr->nbGlyphs = count; + gr->indexes = (u32*)malloc(sizeof(u32) * gr->nbGlyphs); + gr->dx = (Fixed*)malloc(sizeof(Fixed) * gr->nbGlyphs); + for (i=0; inbGlyphs; i++) { + gr->indexes[i] = swf_read_int(read, nbits_glyph); + gr->dx[i] = FLT2FIX( swf_read_int(read, nbits_adv) * SWF_TWIP_SCALE ); + } + swf_align(read); + } + /*text state change*/ + else { + Bool has_font, has_col, has_y_off, has_x_off; + /*reserved*/ + swf_read_int(read, 3); + has_font = swf_read_int(read, 1); + has_col = swf_read_int(read, 1); + has_y_off = swf_read_int(read, 1); + has_x_off = swf_read_int(read, 1); + + /*end of rec*/ + if (!has_font && !has_col && !has_y_off && !has_x_off) break; + if (has_font) fontID = swf_get_16(read); + if (has_col) { + if (revision==0) col = swf_get_color(read); + else col = swf_get_argb(read); + } + /*openSWF spec seems to have wrong order here*/ + if (has_x_off) offX = FLT2FIX( swf_get_s16(read) * SWF_TWIP_SCALE ); + if (has_y_off) offY = FLT2FIX( swf_get_s16(read) * SWF_TWIP_SCALE ); + if (has_font) font_height = swf_get_16(read); + } + } + + if (e) goto exit; + + if (! (read->flags & GF_SM_SWF_NO_TEXT) ) { + e = read->define_text(read, &txt); + } + +exit: + while (gf_list_count(txt.text)) { + SWFGlyphRec *gr = (SWFGlyphRec *)gf_list_get(txt.text, 0); + gf_list_rem(txt.text, 0); + if (gr->indexes) free(gr->indexes); + if (gr->dx) free(gr->dx); + free(gr); + } + gf_list_del(txt.text); + + return e; +} + + +static GF_Err swf_def_edit_text(SWFReader *read) +{ + GF_Err e; + SWFEditText txt; + char *var_name; + Bool has_text, has_text_color, has_max_length, has_font; + + memset(&txt, 0, sizeof(SWFEditText)); + txt.color = 0xFF000000; + + txt.ID = swf_get_16(read); + swf_get_rec(read, &txt.bounds); + swf_align(read); + + has_text = swf_read_int(read, 1); + txt.word_wrap = swf_read_int(read, 1); + txt.multiline = swf_read_int(read, 1); + txt.password = swf_read_int(read, 1); + txt.read_only = swf_read_int(read, 1); + has_text_color = swf_read_int(read, 1); + has_max_length = swf_read_int(read, 1); + has_font = swf_read_int(read, 1); + /*reserved*/swf_read_int(read, 1); + txt.auto_size = swf_read_int(read, 1); + txt.has_layout = swf_read_int(read, 1); + txt.no_select = swf_read_int(read, 1); + txt.border = swf_read_int(read, 1); + /*reserved*/swf_read_int(read, 1); + txt.html = swf_read_int(read, 1); + txt.outlines = swf_read_int(read, 1); + + if (has_font) { + txt.fontID = swf_get_16(read); + txt.font_height = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); + } + if (has_text_color) txt.color = swf_get_argb(read); + if (has_max_length) txt.max_length = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); + + if (txt.has_layout) { + txt.align = swf_read_int(read, 8); + txt.left = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); + txt.right = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); + txt.indent = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); + txt.leading = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); + } + var_name = swf_get_string(read); + if (has_text) txt.init_value = swf_get_string(read); + + e = GF_OK; + if (! (read->flags & GF_SM_SWF_NO_TEXT) ) { + e = read->define_edit_text(read, &txt); + } + free(var_name); + if (txt.init_value) free(txt.init_value); + + return e; +} + +static void swf_delete_sound_stream(SWFReader *read) +{ + if (!read->sound_stream) return; + if (read->sound_stream->output) fclose(read->sound_stream->output); + if (read->sound_stream->szFileName) free(read->sound_stream->szFileName); + free(read->sound_stream); + read->sound_stream = NULL; +} + +static GF_Err swf_def_sprite(SWFReader *read) +{ + GF_Err e; + GF_List *prev_dlist; + u32 frame_count; + Bool prev_sprite; + u32 prev_frame, prev_depth; + SWFSound *snd; + + prev_sprite = read->current_sprite_id; + read->current_sprite_id = swf_get_16(read); + frame_count = swf_get_16(read); + + /*store frame state*/ + prev_frame = read->current_frame; + read->current_frame = 0; + /*store soundStream state*/ + snd = read->sound_stream; + read->sound_stream = NULL; + /*store depth state*/ + prev_depth = read->max_depth; + read->max_depth = 0; + + prev_dlist = read->display_list; + read->display_list = gf_list_new(); + + e = read->define_sprite(read, frame_count); + if (e) return e; + + /*close sprite soundStream*/ + swf_delete_sound_stream(read); + /*restore sound stream*/ + read->sound_stream = snd; + read->max_depth = prev_depth; + + while (gf_list_count(read->display_list)) { + DispShape *s = (DispShape *)gf_list_get(read->display_list, 0); + gf_list_rem(read->display_list, 0); + free(s); + } + gf_list_del(read->display_list); + read->display_list = prev_dlist; + + read->current_frame = prev_frame; + read->current_sprite_id = prev_sprite; + + read->tag = SWF_DEFINESPRITE; + return GF_OK; +} + +static GF_Err swf_def_sound(SWFReader *read) +{ + SWFSound *snd; + GF_SAFEALLOC(snd , SWFSound); + snd->ID = swf_get_16(read); + snd->format = swf_read_int(read, 4); + snd->sound_rate = swf_read_int(read, 2); + snd->bits_per_sample = swf_read_int(read, 1) ? 16 : 8; + snd->stereo = swf_read_int(read, 1); + snd->sample_count = swf_get_32(read); + + switch (snd->format) { + /*raw PCM*/ + case 0: + swf_report(read, GF_NOT_SUPPORTED, "Raw PCM Audio not supported"); + free(snd); + break; + /*ADPCM*/ + case 1: + swf_report(read, GF_NOT_SUPPORTED, "AD-PCM Audio not supported"); + free(snd); + break; + /*MP3*/ + case 2: + { + char szName[1024]; + u32 alloc_size, tot_size; + char *frame; + + sprintf(szName, "swf_sound_%d.mp3", snd->ID); + if (read->localPath) { + snd->szFileName = (char*)malloc(sizeof(char)*GF_MAX_PATH); + strcpy(snd->szFileName, read->localPath); + strcat(snd->szFileName, szName); + } else { + snd->szFileName = strdup(szName); + } + snd->output = fopen(snd->szFileName, "wb"); + + alloc_size = 4096; + frame = (char*)malloc(sizeof(char)*4096); + snd->frame_delay_ms = swf_get_16(read); + snd->frame_delay_ms = read->current_frame*1000; + snd->frame_delay_ms /= read->frame_rate; + tot_size = 9; + /*parse all frames*/ + while (tot_sizesize) { + u32 toread = read->size - tot_size; + if (toread>alloc_size) toread = alloc_size; + swf_read_data(read, frame, toread); + fwrite(frame, sizeof(char)*toread, 1, snd->output); + tot_size += toread; + } + + free(frame); + return gf_list_add(read->sounds, snd); + } + case 3: + swf_report(read, GF_NOT_SUPPORTED, "Unrecognized sound format"); + free(snd); + break; + } + return GF_OK; +} + + +typedef struct +{ + u32 sync_flags; + u32 in_point, out_point; + u32 nb_loops; +} SoundInfo; + +static SoundInfo swf_skip_soundinfo(SWFReader *read) +{ + SoundInfo si; + u32 sync_flags = swf_read_int(read, 4); + Bool has_env = swf_read_int(read, 1); + Bool has_loops = swf_read_int(read, 1); + Bool has_out_pt = swf_read_int(read, 1); + Bool has_in_pt = swf_read_int(read, 1); + + memset(&si, 0, sizeof(SoundInfo)); + si.sync_flags = sync_flags; + if (has_in_pt) si.in_point = swf_get_32(read); + if (has_out_pt) si.out_point = swf_get_32(read); + if (has_loops) si.nb_loops = swf_get_16(read); + /*we ignore the envelope*/ + if (has_env) { + u32 i; + u32 nb_ctrl = swf_read_int(read, 8); + for (i=0; isounds, &i))) { + if (snd->ID==ID) return snd; + } + return NULL; +} + +static GF_Err swf_start_sound(SWFReader *read) +{ + SWFSound *snd; + u32 ID = swf_get_16(read); + SoundInfo si; + si = swf_skip_soundinfo(read); + + snd = sndswf_get_sound(read, ID); + if (!snd) { + swf_report(read, GF_BAD_PARAM, "Cannot find sound with ID %d", ID); + return GF_OK; + } + if (!snd->is_setup) { + GF_Err e = read->setup_sound(read, snd, 0); + if (e) return e; + snd->is_setup = 1; + } + return read->start_sound(read, snd, (si.sync_flags & 0x2) ? 1 : 0); +} + +static GF_Err swf_soundstream_hdr(SWFReader *read) +{ + char szName[1024]; + u8 rec_mix; + u32 samplesperframe; + SWFSound *snd; + + if (read->sound_stream) { + swf_report(read, GF_BAD_PARAM, "More than one sound stream for current timeline!!"); + return swf_func_skip(read); + } + + GF_SAFEALLOC(snd, SWFSound); + + rec_mix = swf_read_int(read, 8); + /*0: uncompressed, 1: ADPCM, 2: MP3*/ + snd->format = swf_read_int(read, 4); + /*0: 5.5k, 1: 11k, 2: 2: 22k, 3: 44k*/ + snd->sound_rate = swf_read_int(read, 2); + /*0: 8 bit, 1: 16 bit*/ + snd->bits_per_sample = swf_read_int(read, 1) ? 16 : 8; + /*0: mono, 8 1: stereo*/ + snd->stereo = swf_read_int(read, 1); + /*samplesperframe hint*/ + samplesperframe = swf_read_int(read, 16); + + switch (snd->format) { + /*raw PCM*/ + case 0: + swf_report(read, GF_NOT_SUPPORTED, "Raw PCM Audio not supported"); + free(snd); + break; + /*ADPCM*/ + case 1: + swf_report(read, GF_NOT_SUPPORTED, "AD-PCM Audio not supported"); + free(snd); + break; + /*MP3*/ + case 2: + read->sound_stream = snd; + if (read->localPath) { + sprintf(szName, "%s/swf_soundstream_%d.mp3", read->localPath, read->current_sprite_id); + } else { + sprintf(szName, "swf_soundstream_%d.mp3", read->current_sprite_id); + } + read->sound_stream->szFileName = strdup(szName); + read->setup_sound(read, read->sound_stream, 0); + break; + case 3: + swf_report(read, GF_NOT_SUPPORTED, "Unrecognized sound format"); + free(snd); + break; + } + return GF_OK; +} + +static GF_Err swf_soundstream_block(SWFReader *read) +{ + unsigned char bytes[4]; + u32 hdr, alloc_size, size, tot_size, samplesPerFrame, delay; + char *frame; + + /*note we're doing only MP3*/ + if (!read->sound_stream) return swf_func_skip(read); + + samplesPerFrame = swf_get_16(read); + delay = swf_get_16(read); + + if (!read->sound_stream->is_setup) { + + /*error at setup*/ + if (!read->sound_stream->output) { + read->sound_stream->output = fopen(read->sound_stream->szFileName, "wb"); + if (!read->sound_stream->output) + return swf_func_skip(read); + } + /*store TS of first AU*/ + read->sound_stream->frame_delay_ms = read->current_frame*1000; + read->sound_stream->frame_delay_ms /= read->frame_rate; + read->setup_sound(read, read->sound_stream, 1); + read->sound_stream->is_setup = 1; + } + + if (!samplesPerFrame) return GF_OK; + + alloc_size = 1; + frame = (char*)malloc(sizeof(char)); + tot_size = 4; + /*parse all frames*/ + while (1) { + bytes[0] = swf_read_int(read, 8); + bytes[1] = swf_read_int(read, 8); + bytes[2] = swf_read_int(read, 8); + bytes[3] = swf_read_int(read, 8); + hdr = GF_4CC(bytes[0], bytes[1], bytes[2], bytes[3]); + size = gf_mp3_frame_size(hdr); + if (alloc_size= read->size) size = read->size - tot_size; + + swf_read_data(read, frame, size-4); + fwrite(bytes, sizeof(char)*4, 1, read->sound_stream->output); + fwrite(frame, sizeof(char)*(size-4), 1, read->sound_stream->output); + if (tot_size + size >= read->size) break; + tot_size += size; + } + free(frame); + return GF_OK; +} + +static GF_Err swf_def_hdr_jpeg(SWFReader *read) +{ + if (read->jpeg_hdr) { + swf_report(read, GF_NON_COMPLIANT_BITSTREAM, "JPEG Table already defined in file"); + return GF_NON_COMPLIANT_BITSTREAM; + } + read->jpeg_hdr_size = read->size; + if (read->size) { + read->jpeg_hdr = malloc(sizeof(char)*read->size); + swf_read_data(read, read->jpeg_hdr, read->size); + } + return GF_OK; +} + + +static GF_Err swf_def_bits_jpeg(SWFReader *read, u32 version) +{ + GF_Err e; + u32 ID; + FILE *file = NULL; + char szName[1024]; + u8 *buf; + u32 skip = 0; + u32 AlphaPlaneSize = 0; + u32 size = read->size; + + ID = swf_get_16(read); + size -= 2; + if (version==3) { + u32 offset = swf_get_32(read); + size -= 4; + AlphaPlaneSize = size - offset; + size = offset; + } + + /*dump file*/ + if (read->load->localPath) { + sprintf(szName, "%s/swf_jpeg_%d.jpg", read->load->localPath, ID); + } else { + sprintf(szName, "swf_jpeg_%d.jpg", ID); + } + + if (version!=3) + file = fopen(szName, "wb"); + + if (version==1 && read->jpeg_hdr_size) { + /*remove JPEG EOI*/ + fwrite(read->jpeg_hdr, 1, read->jpeg_hdr_size-2, file); + /*remove JPEG SOI*/ + swf_get_16(read); + size-=2; + } + buf = malloc(sizeof(u8)*size); + swf_read_data(read, buf, size); + if (version==1) { + fwrite(buf, 1, size, file); + } else { + u32 i; + for (i=0; iload->localPath) { + sprintf(szName, "%s/swf_png_%d.png", read->load->localPath, ID); + } else { + sprintf(szName, "swf_png_%d.png", ID); + } + + osize = w*h*4; + buf = realloc(buf, sizeof(char)*osize); + gf_img_png_enc(raw, w, h, GF_PIXEL_RGBA, buf, &osize); + + file = fopen(szName, "wb"); + fwrite(buf, 1, osize, file); + fclose(file); + + free(raw); + } + free(buf); + + + return read->setup_image(read, ID, szName); +} + + +static const char *swf_get_tag_name(u32 tag) +{ + switch (tag) { + case SWF_END: return "End"; + case SWF_SHOWFRAME: return "ShowFrame"; + case SWF_DEFINESHAPE: return "DefineShape"; + case SWF_FREECHARACTER: return "FreeCharacter"; + case SWF_PLACEOBJECT: return "PlaceObject"; + case SWF_REMOVEOBJECT: return "RemoveObject"; + case SWF_DEFINEBITSJPEG: return "DefineBitsJPEG"; + case SWF_DEFINEBUTTON: return "DefineButton"; + case SWF_JPEGTABLES: return "JPEGTables"; + case SWF_SETBACKGROUNDCOLOR: return "SetBackgroundColor"; + case SWF_DEFINEFONT: return "DefineFont"; + case SWF_DEFINETEXT: return "DefineText"; + case SWF_DOACTION: return "DoAction"; + case SWF_DEFINEFONTINFO: return "DefineFontInfo"; + case SWF_DEFINESOUND: return "DefineSound"; + case SWF_STARTSOUND: return "StartSound"; + case SWF_DEFINEBUTTONSOUND: return "DefineButtonSound"; + case SWF_SOUNDSTREAMHEAD: return "SoundStreamHead"; + case SWF_SOUNDSTREAMBLOCK: return "SoundStreamBlock"; + case SWF_DEFINEBITSLOSSLESS: return "DefineBitsLossless"; + case SWF_DEFINEBITSJPEG2: return "DefineBitsJPEG2"; + case SWF_DEFINESHAPE2: return "DefineShape2"; + case SWF_DEFINEBUTTONCXFORM: return "DefineButtonCXForm"; + case SWF_PROTECT: return "Protect"; + case SWF_PLACEOBJECT2: return "PlaceObject2"; + case SWF_REMOVEOBJECT2: return "RemoveObject2"; + case SWF_DEFINESHAPE3: return "DefineShape3"; + case SWF_DEFINETEXT2: return "DefineText2"; + case SWF_DEFINEBUTTON2: return "DefineButton2"; + case SWF_DEFINEBITSJPEG3: return "DefineBitsJPEG3"; + case SWF_DEFINEBITSLOSSLESS2: return "DefineBitsLossless2"; + case SWF_DEFINEEDITTEXT: return "DefineEditText"; + case SWF_DEFINEMOVIE: return "DefineMovie"; + case SWF_DEFINESPRITE: return "DefineSprite"; + case SWF_NAMECHARACTER: return "NameCharacter"; + case SWF_SERIALNUMBER: return "SerialNumber"; + case SWF_GENERATORTEXT: return "GeneratorText"; + case SWF_FRAMELABEL: return "FrameLabel"; + case SWF_SOUNDSTREAMHEAD2: return "SoundStreamHead2"; + case SWF_DEFINEMORPHSHAPE: return "DefineMorphShape"; + case SWF_DEFINEFONT2: return "DefineFont2"; + case SWF_TEMPLATECOMMAND: return "TemplateCommand"; + case SWF_GENERATOR3: return "Generator3"; + case SWF_EXTERNALFONT: return "ExternalFont"; + case SWF_EXPORTASSETS: return "ExportAssets"; + case SWF_IMPORTASSETS: return "ImportAssets"; + case SWF_ENABLEDEBUGGER: return "EnableDebugger"; + case SWF_MX0: return "MX0"; + case SWF_MX1: return "MX1"; + case SWF_MX2: return "MX2"; + case SWF_MX3: return "MX3"; + case SWF_MX4: return "MX4"; + default: return "UnknownTag"; + } +} + +static GF_Err swf_unknown_tag(SWFReader *read) +{ + swf_report(read, GF_NOT_SUPPORTED, "Tag %s (0x%2x) not implemented - skipping", swf_get_tag_name(read->tag), read->tag); + return swf_func_skip(read); +} + +static GF_Err swf_process_tag(SWFReader *read) +{ + switch (read->tag) { + case SWF_END: return GF_OK; + case SWF_PROTECT: return GF_OK; + case SWF_SETBACKGROUNDCOLOR: return swf_set_backcol(read); + case SWF_DEFINESHAPE: return swf_parse_shape_def(read, NULL, 0); + case SWF_DEFINESHAPE2: return swf_parse_shape_def(read, NULL, 1); + case SWF_DEFINESHAPE3: return swf_parse_shape_def(read, NULL, 2); + case SWF_PLACEOBJECT: return swf_place_obj(read, 0); + case SWF_PLACEOBJECT2: return swf_place_obj(read, 1); + case SWF_REMOVEOBJECT: return swf_remove_obj(read, 0); + case SWF_REMOVEOBJECT2: return swf_remove_obj(read, 1); + case SWF_SHOWFRAME: return swf_show_frame(read); + case SWF_DEFINEFONT: return swf_def_font(read, 0); + case SWF_DEFINEFONT2: return swf_def_font(read, 1); + case SWF_DEFINEFONTINFO: return swf_def_font_info(read); + case SWF_DEFINETEXT: return swf_def_text(read, 0); + case SWF_DEFINETEXT2: return swf_def_text(read, 1); + case SWF_DEFINEEDITTEXT: return swf_def_edit_text(read); + case SWF_DEFINESPRITE: return swf_def_sprite(read); + /*no revision needed*/ + case SWF_SOUNDSTREAMHEAD: + case SWF_SOUNDSTREAMHEAD2: + return swf_soundstream_hdr(read); + case SWF_DEFINESOUND: return swf_def_sound(read); + case SWF_STARTSOUND: return swf_start_sound(read); + case SWF_SOUNDSTREAMBLOCK: return swf_soundstream_block(read); + + case SWF_DEFINEBUTTON: return swf_def_button(read, 0); + case SWF_DEFINEBUTTON2: return swf_def_button(read, 1); +// case SWF_DEFINEBUTTONSOUND: + case SWF_DOACTION: + return swf_actions(read, 0, 0); + case SWF_FRAMELABEL: + { + char *framelabel = swf_get_string(read); + free(framelabel); + return GF_OK; + } + + case SWF_JPEGTABLES: return swf_def_hdr_jpeg(read); + case SWF_DEFINEBITSJPEG: return swf_def_bits_jpeg(read, 1); + case SWF_DEFINEBITSJPEG2: return swf_def_bits_jpeg(read, 2); + case SWF_DEFINEBITSJPEG3: return swf_def_bits_jpeg(read, 3); + + default: return swf_unknown_tag(read); + } +} + +static GF_Err swf_parse_tag(SWFReader *read) +{ + GF_Err e; + s32 diff; + u16 hdr; + u32 pos; + + + hdr = swf_get_16(read); + read->tag = hdr>>6; + read->size = hdr & 0x3f; + if (read->size == 0x3f) { + swf_align(read); + read->size = swf_get_32(read); + } + pos = swf_get_file_pos(read); + diff = pos + read->size; + gf_set_progress("SWF Parsing", pos, read->length); + + e = swf_process_tag(read); + swf_align(read); + + diff -= swf_get_file_pos(read); + if (diff<0) { + swf_report(read, GF_IO_ERR, "tag %s over-read of %d bytes (size %d)", swf_get_tag_name(read->tag), -1*diff, read->size); + return GF_IO_ERR; + } else { + swf_read_int(read, diff*8); + } + + + if (!e && !read->tag) return GF_EOS; + if (read->ioerr) { + swf_report(read, GF_IO_ERR, "bitstream IO err (tag size %d)", read->size); + return read->ioerr; + } + return e; +} + + + +GF_Err swf_parse_sprite(SWFReader *read) +{ + /*parse*/ + while (1) { + GF_Err e = swf_parse_tag(read); + if (e<0) { + swf_report(read, e, "Error parsing tag %s", swf_get_tag_name(read->tag)); + return e; + } + /*done with sprite*/ + if (read->tag==SWF_END) break; + } + return GF_OK; +} + + +void swf_report(SWFReader *read, GF_Err e, char *format, ...) +{ +#ifndef GPAC_DISABLE_LOG + if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + char szMsg[2048]; + va_list args; + va_start(args, format); + vsprintf(szMsg, format, args); + va_end(args); + GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_PARSER, ("[SWF Parsing] %s (frame %d)\n", szMsg, read->current_frame+1) ); + } +#endif +} + + +static void swf_io_error(void *par) +{ + SWFReader *read = (SWFReader *)par; + read->ioerr = GF_IO_ERR; +} + +GF_Err gf_sm_load_run_swf(GF_SceneLoader *load) +{ + GF_Err e; + SWFReader *read = (SWFReader *)load->loader_priv; + if (!read) return GF_BAD_PARAM; + + /*parse all tags*/ + e = GF_OK; + while (1) { + e = swf_parse_tag(read); + if (e) break; + } + gf_set_progress("SWF Parsing", read->length, read->length); + + if (e==GF_EOS) e = GF_OK; + if (!e) { + if (read->flat_limit != 0) + swf_report(read, GF_OK, "%d points removed while parsing shapes (Flattening limit %.4f)", read->flatten_points, read->flat_limit); + + if (read->no_as && read->has_interact) swf_report(read, GF_OK, "ActionScripts and interactions have been removed"); + } else + swf_report(read, e, "Error parsing tag %s", swf_get_tag_name(read->tag)); + + + return e; +} + +void gf_sm_load_done_swf(GF_SceneLoader *load) +{ + SWFReader *read = (SWFReader *) load->loader_priv; + if (!read) return; + + gf_bs_del(read->bs); + if (read->mem) free(read->mem); + + read->finalize(read); + + while (gf_list_count(read->display_list)) { + DispShape *s = (DispShape *)gf_list_get(read->display_list, 0); + gf_list_rem(read->display_list, 0); + free(s); + } + gf_list_del(read->display_list); + while (gf_list_count(read->fonts)) { + SWFFont *ft = (SWFFont *)gf_list_get(read->fonts, 0); + gf_list_rem(read->fonts, 0); + if (ft->glyph_adv) free(ft->glyph_adv); + if (ft->glyph_codes) free(ft->glyph_codes); + if (ft->fontName) free(ft->fontName); + gf_list_del(ft->glyphs); + free(ft); + } + gf_list_del(read->fonts); + gf_list_del(read->apps); + + while (gf_list_count(read->sounds)) { + SWFSound *snd = (SWFSound *)gf_list_get(read->sounds, 0); + gf_list_rem(read->sounds, 0); + if (snd->output) fclose(snd->output); + if (snd->szFileName) free(snd->szFileName); + free(snd); + } + gf_list_del(read->sounds); + swf_delete_sound_stream(read); + + if (read->jpeg_hdr) free(read->jpeg_hdr); + if (read->localPath) free(read->localPath); + fclose(read->input); + free(read); + load->loader_priv = NULL; +} + +GF_Err gf_sm_load_init_swf(GF_SceneLoader *load) +{ + SWFReader *read; + SWFRec rc; + GF_Err e; + FILE *input; + u8 sig[3]; + u8 version; + + if (!load->ctx || !load->scene_graph || !load->fileName) return GF_BAD_PARAM; + input = fopen(load->fileName, "rb"); + if (!input) return GF_URL_ERROR; + + GF_SAFEALLOC(read, SWFReader); + read->load = load; + + e = GF_OK; + + read->input = input; + read->bs = gf_bs_from_file(input, GF_BITSTREAM_READ); + gf_bs_set_eos_callback(read->bs, swf_io_error, &read); + read->display_list = gf_list_new(); + read->fonts = gf_list_new(); + read->apps = gf_list_new(); + read->sounds = gf_list_new(); + + read->flags = load->swf_import_flags; + read->flat_limit = FLT2FIX(load->swf_flatten_limit); + if (load->localPath) read->localPath = strdup(load->localPath); + else { + char *c; + read->localPath = strdup(load->fileName); + c = strrchr(read->localPath, GF_PATH_SEPARATOR); + if (c) c[1] = 0; + else { + free(read->localPath); + read->localPath = NULL; + } + } + + load->loader_priv = read; + + /*get signature*/ + sig[0] = gf_bs_read_u8(read->bs); + sig[1] = gf_bs_read_u8(read->bs); + sig[2] = gf_bs_read_u8(read->bs); + /*"FWS" or "CWS"*/ + if ( ((sig[0] != 'F') && (sig[0] != 'C')) || (sig[1] != 'W') || (sig[2] != 'S') ) { + e = GF_URL_ERROR; + goto exit; + } + version = gf_bs_read_u8(read->bs); + read->length = swf_get_32(read); + + /*if compressed decompress the whole file*/ + if (sig[0] == 'C') + swf_init_decompress(read); + + swf_get_rec(read, &rc); + read->width = rc.w; + read->height = rc.h; + load->ctx->scene_width = FIX2INT(read->width); + load->ctx->scene_height = FIX2INT(read->height); + load->ctx->is_pixel_metrics = 1; + + swf_align(read); + read->frame_rate = swf_get_16(read)>>8; + read->frame_count = swf_get_16(read); + + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("SWF Import - Scene Size %dx%d - %d frames @ %d FPS", load->ctx->scene_width, load->ctx->scene_height, read->frame_count, read->frame_rate)); + + if (!(load->swf_import_flags & GF_SM_SWF_SPLIT_TIMELINE) ) { + swf_report(read, GF_OK, "ActionScript disabled"); + read->no_as = 1; + } + + e = swf_to_bifs_init(read); + + /*parse all tags*/ + while (e == GF_OK) { + e = swf_parse_tag(read); + if (read->current_frame==1) break; + } + if (e==GF_EOS) e = GF_OK; + +exit: + if (e) gf_sm_load_done_swf(load); + return e; +} + +#endif + diff --git a/src/scene_manager/text_to_bifs.c b/src/scene_manager/text_to_bifs.c new file mode 100644 index 0000000..50efcf6 --- /dev/null +++ b/src/scene_manager/text_to_bifs.c @@ -0,0 +1,521 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Management sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include +#include +#include +#include + +enum +{ + GF_TEXT_IMPORT_NONE = 0, + GF_TEXT_IMPORT_SRT, + GF_TEXT_IMPORT_SUB, + GF_TEXT_IMPORT_TTXT, + GF_TEXT_IMPORT_TEXML, +}; + +#define REM_TRAIL_MARKS(__str, __sep) while (1) { \ + u32 _len = strlen(__str); \ + if (!_len) break; \ + _len--; \ + if (strchr(__sep, __str[_len])) __str[_len] = 0; \ + else break; \ + } \ + + +static GF_Err gf_text_guess_format(char *filename, u32 *fmt) +{ + char szLine[2048], szTest[10]; + u32 val; + FILE *test = fopen(filename, "rt"); + if (!test) return GF_URL_ERROR; + + while (fgets(szLine, 2048, test) != NULL) { + REM_TRAIL_MARKS(szLine, "\r\n\t ") + + if (strlen(szLine)) break; + } + *fmt = GF_TEXT_IMPORT_NONE; + if ((szLine[0]=='{') && strstr(szLine, "}{")) *fmt = GF_TEXT_IMPORT_SUB; + else if (sscanf(szLine, "%d", &val)==1) { + sprintf(szTest, "%d", val); + if (!strcmp(szTest, szLine)) *fmt = GF_TEXT_IMPORT_SRT; + } + else if (!strnicmp(szLine, ""); + if (ext) ext += 2; + if (!ext[0]) fgets(szLine, 2048, test); + if (strstr(szLine, "x-quicktime-tx3g")) *fmt = GF_TEXT_IMPORT_TEXML; + } + fclose(test); + return GF_OK; +} + +static GF_Err gf_text_import_srt_bifs(GF_SceneManager *ctx, GF_ESD *src, GF_MuxInfo *mux) +{ + GF_Err e; + GF_Node *text, *font; + GF_StreamContext *srt; + FILE *srt_in; + GF_FieldInfo string, style; + u32 sh, sm, ss, sms, eh, em, es, ems, start, end; + GF_AUContext *au; + GF_Command *com; + SFString *sfstr; + GF_CommandField *inf; + Bool italic, underlined, bold; + u32 state, curLine, line, i, len; + char szLine[2048], szText[2048], *ptr; + GF_StreamContext *sc = NULL; + + if (!ctx->scene_graph) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] base scene not assigned\n")); + return GF_BAD_PARAM; + } + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { + if (sc->streamType==GF_STREAM_SCENE) break; + sc = NULL; + } + + if (!sc) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] cannot locate base scene\n")); + return GF_BAD_PARAM; + } + if (!mux->textNode) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] Target text node unspecified\n")); + return GF_BAD_PARAM; + } + text = gf_sg_find_node_by_name(ctx->scene_graph, mux->textNode); + if (!text) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] cannot find target text node %s\n", mux->textNode)); + return GF_BAD_PARAM; + } + if (gf_node_get_field_by_name(text, "string", &string) != GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] Target text node %s doesn't look like text\n", mux->textNode)); + return GF_BAD_PARAM; + } + + font = NULL; + if (mux->fontNode) { + font = gf_sg_find_node_by_name(ctx->scene_graph, mux->fontNode); + if (!font) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] cannot find target font node %s\n", mux->fontNode)); + return GF_BAD_PARAM; + } + if (gf_node_get_field_by_name(font, "style", &style) != GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] Target font node %s doesn't look like font\n", mux->fontNode)); + return GF_BAD_PARAM; + } + } + + srt_in = fopen(mux->file_name, "rt"); + if (!srt_in) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] cannot open input file %s\n", mux->file_name)); + return GF_URL_ERROR; + } + + srt = gf_sm_stream_new(ctx, src->ESID, GF_STREAM_SCENE, 1); + if (!srt) return GF_OUT_OF_MEM; + + if (!src->slConfig) src->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + src->slConfig->timestampResolution = 1000; + if (!src->decoderConfig) src->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + src->decoderConfig->streamType = GF_STREAM_SCENE; + src->decoderConfig->objectTypeIndication = 1; + + e = GF_OK; + state = end = 0; + curLine = 0; + au = NULL; + com = NULL; + italic = underlined = bold = 0; + inf = NULL; + + while (1) { + char *sOK = fgets(szLine, 2048, srt_in); + + if (sOK) REM_TRAIL_MARKS(szLine, "\r\n\t ") + + if (!sOK || !strlen(szLine)) { + state = 0; + if (au) { + /*if italic or underscore do it*/ + if (font && (italic || underlined || bold)) { + com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); + com->node = font; + gf_node_register(font, NULL); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = style.fieldIndex; + inf->fieldType = style.fieldType; + inf->field_ptr = gf_sg_vrml_field_pointer_new(style.fieldType); + sfstr = (SFString *)inf->field_ptr; + if (bold && italic && underlined) sfstr->buffer = strdup("BOLDITALIC UNDERLINED"); + else if (italic && underlined) sfstr->buffer = strdup("ITALIC UNDERLINED"); + else if (bold && underlined) sfstr->buffer = strdup("BOLD UNDERLINED"); + else if (underlined) sfstr->buffer = strdup("UNDERLINED"); + else if (bold && italic) sfstr->buffer = strdup("BOLDITALIC"); + else if (bold) sfstr->buffer = strdup("BOLD"); + else sfstr->buffer = strdup("ITALIC"); + gf_list_add(au->commands, com); + } + + au = gf_sm_stream_au_new(srt, end, 0, 1); + com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); + com->node = text; + gf_node_register(text, NULL); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = string.fieldIndex; + inf->fieldType = string.fieldType; + inf->field_ptr = gf_sg_vrml_field_pointer_new(string.fieldType); + gf_list_add(au->commands, com); + /*reset font styles so that all AUs are true random access*/ + if (font) { + com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); + com->node = font; + gf_node_register(font, NULL); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = style.fieldIndex; + inf->fieldType = style.fieldType; + inf->field_ptr = gf_sg_vrml_field_pointer_new(style.fieldType); + gf_list_add(au->commands, com); + } + au = NULL; + } + inf = NULL; + if (!sOK) break; + continue; + } + + switch (state) { + case 0: + if (sscanf(szLine, "%d", &line) != 1) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] bad frame format (src: %s)\n", szLine)); + e = GF_CORRUPTED_DATA; + goto exit; + } + if (line != curLine + 1) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] bad frame: previous %d - current %d (src: %s)\n", curLine, line, szLine)); + e = GF_CORRUPTED_DATA; + goto exit; + } + curLine = line; + state = 1; + break; + case 1: + if (sscanf(szLine, "%d:%d:%d,%d --> %d:%d:%d,%d", &sh, &sm, &ss, &sms, &eh, &em, &es, &ems) != 8) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[srt->bifs] bad frame %d (src: %s)\n", curLine, szLine)); + e = GF_CORRUPTED_DATA; + goto exit; + } + start = (3600*sh + 60*sm + ss)*1000 + sms; + if (startbifs] corrupted frame starts before end of previous one (SRT Frame %d) - adjusting time stamps\n", curLine)); + start = end; + } + end = (3600*eh + 60*em + es)*1000 + ems; + /*make stream start at 0 by inserting a fake AU*/ + if ((curLine==1) && start>0) { + au = gf_sm_stream_au_new(srt, 0, 0, 1); + com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); + com->node = text; + gf_node_register(text, NULL); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = string.fieldIndex; + inf->fieldType = string.fieldType; + inf->field_ptr = gf_sg_vrml_field_pointer_new(string.fieldType); + gf_list_add(au->commands, com); + } + + au = gf_sm_stream_au_new(srt, start, 0, 1); + com = NULL; + state = 2; + italic = underlined = bold = 0; + break; + + default: + ptr = szLine; + /*FIXME - other styles posssibles ??*/ + while (1) { + if (!strnicmp(ptr, "", 3)) { + italic = 1; + ptr += 3; + } + else if (!strnicmp(ptr, "", 3)) { + underlined = 1; + ptr += 3; + } + else if (!strnicmp(ptr, "", 3)) { + bold = 1; + ptr += 3; + } + else + break; + } + /*if style remove end markers*/ + while ((strlen(ptr)>4) && (ptr[strlen(ptr) - 4] == '<') && (ptr[strlen(ptr) - 1] == '>')) { + ptr[strlen(ptr) - 4] = 0; + } + + if (!com) { + com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); + com->node = text; + gf_node_register(text, NULL); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = string.fieldIndex; + inf->fieldType = string.fieldType; + inf->field_ptr = gf_sg_vrml_field_pointer_new(string.fieldType); + gf_list_add(au->commands, com); + } + assert(inf); + gf_sg_vrml_mf_append(inf->field_ptr, GF_SG_VRML_MFSTRING, (void **) &sfstr); + len = 0; + for (i=0; i> 6) & 0x3 ); + len++; + ptr[i] &= 0xbf; + } + /*we only handle UTF8 chars on 2 bytes (eg first byte is 0b110xxxxx)*/ + else if ((ptr[i] & 0xe0) == 0xc0) { + szText[len] = ptr[i]; + len++; + i++; + } + } + szText[len] = ptr[i]; + len++; + } + szText[len] = 0; + sfstr->buffer = strdup(szText); + break; + } + } + +exit: + if (e) gf_sm_stream_del(ctx, srt); + fclose(srt_in); + return e; +} + +static GF_Err gf_text_import_sub_bifs(GF_SceneManager *ctx, GF_ESD *src, GF_MuxInfo *mux) +{ + GF_Err e; + GF_Node *text, *font; + GF_StreamContext *srt; + FILE *sub_in; + GF_FieldInfo string, style; + u32 start, end, line, i, j, k, len; + GF_AUContext *au; + GF_Command *com; + SFString *sfstr; + GF_CommandField *inf; + Bool first_samp; + Double fps; + char szLine[2048], szTime[30], szText[2048]; + GF_StreamContext *sc = NULL; + + if (!ctx->scene_graph) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] base scene not assigned\n")); + return GF_BAD_PARAM; + } + i=0; + while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { + if (sc->streamType==GF_STREAM_SCENE) break; + sc = NULL; + } + + if (!sc) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] cannot locate base scene\n")); + return GF_BAD_PARAM; + } + if (!mux->textNode) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] target text node unspecified\n")); + return GF_BAD_PARAM; + } + text = gf_sg_find_node_by_name(ctx->scene_graph, mux->textNode); + if (!text) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] cannot find target text node %s\n", mux->textNode)); + return GF_BAD_PARAM; + } + if (gf_node_get_field_by_name(text, "string", &string) != GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] target text node %s doesn't look like text\n", mux->textNode)); + return GF_BAD_PARAM; + } + + font = NULL; + if (mux->fontNode) { + font = gf_sg_find_node_by_name(ctx->scene_graph, mux->fontNode); + if (!font) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] cannot find target font node %s\n", mux->fontNode)); + return GF_BAD_PARAM; + } + if (gf_node_get_field_by_name(font, "style", &style) != GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] target font node %s doesn't look like font\n", mux->fontNode)); + return GF_BAD_PARAM; + } + } + + sub_in = fopen(mux->file_name, "rt"); + if (!sub_in) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] cannot open input file %s\n", mux->file_name)); + return GF_URL_ERROR; + } + + srt = gf_sm_stream_new(ctx, src->ESID, GF_STREAM_SCENE, 1); + if (!srt) return GF_OUT_OF_MEM; + + if (!src->slConfig) src->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + src->slConfig->timestampResolution = 1000; + if (!src->decoderConfig) src->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + src->decoderConfig->streamType = GF_STREAM_SCENE; + src->decoderConfig->objectTypeIndication = 1; + + e = GF_OK; + start = end = 0; + au = NULL; + com = NULL; + inf = NULL; + + fps = 25.0; + if (mux->frame_rate) fps = mux->frame_rate; + + line = 0; + first_samp = 1; + while (1) { + char *sOK = fgets(szLine, 2048, sub_in); + if (!sOK) break; + REM_TRAIL_MARKS(szLine, "\r\n\t ") + + line++; + len = strlen(szLine); + if (!len) continue; + + i=0; + if (szLine[i] != '{') { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] Bad frame (line %d): expecting \"{\" got \"%c\"\n", line, szLine[i])); + e = GF_NON_COMPLIANT_BITSTREAM; + break; + } + while (szLine[i+1] && szLine[i+1]!='}') { szTime[i] = szLine[i+1]; i++; } + szTime[i] = 0; + start = atoi(szTime); + if (startbifs] corrupted SUB frame (line %d) - starts (at %d ms) before end of previous one (%d ms) - adjusting time stamps\n", line, start, end)); + start = end; + } + j=i+2; + i=0; + if (szLine[i+j] != '{') { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[sub->bifs] Bad frame - expecting \"{\" got \"%c\"\n", szLine[i])); + e = GF_NON_COMPLIANT_BITSTREAM; + break; + } + while (szLine[i+1+j] && szLine[i+1+j]!='}') { szTime[i] = szLine[i+1+j]; i++; } + szTime[i] = 0; + end = atoi(szTime); + j+=i+2; + + if (start>end) { + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[sub->bifs] corrupted frame (line %d) - ends (at %d ms) before start of current frame (%d ms) - skipping\n", line, end, start)); + continue; + } + + if (start && first_samp) { + au = gf_sm_stream_au_new(srt, 0, 0, 1); + com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); + com->node = text; + gf_node_register(text, NULL); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = string.fieldIndex; + inf->fieldType = string.fieldType; + inf->field_ptr = gf_sg_vrml_field_pointer_new(string.fieldType); + gf_list_add(au->commands, com); + } + + k=0; + for (i=j; i> 6) & 0x3 ); + k++; + szLine[i] &= 0xbf; + } + /*we only handle UTF8 chars on 2 bytes (eg first byte is 0b110xxxxx)*/ + else if ( (szLine[i] & 0xe0) == 0xc0) { + szText[k] = szLine[i]; + i++; + k++; + } + } + szText[k] = szLine[i]; + } + k++; + } + szText[i-j] = 0; + + com = gf_sg_command_new(ctx->scene_graph, GF_SG_FIELD_REPLACE); + com->node = text; + gf_node_register(text, NULL); + inf = gf_sg_command_field_new(com); + inf->fieldIndex = string.fieldIndex; + inf->fieldType = string.fieldType; + inf->field_ptr = gf_sg_vrml_field_pointer_new(string.fieldType); + gf_list_add(au->commands, com); + + gf_sg_vrml_mf_append(inf->field_ptr, GF_SG_VRML_MFSTRING, (void **) &sfstr); + sfstr->buffer = strdup(szText); + } + + if (e) gf_sm_stream_del(ctx, srt); + fclose(sub_in); + return e; +} + +GF_EXPORT +GF_Err gf_sm_import_bifs_subtitle(GF_SceneManager *ctx, GF_ESD *src, GF_MuxInfo *mux) +{ + GF_Err e; + u32 fmt; + e = gf_text_guess_format(mux->file_name, &fmt); + if (e) return e; + if (!fmt || (fmt>=3)) return GF_NOT_SUPPORTED; + + if (fmt==1) return gf_text_import_srt_bifs(ctx, src, mux); + else return gf_text_import_sub_bifs(ctx, src, mux); +} + diff --git a/src/scenegraph/base_scenegraph.c b/src/scenegraph/base_scenegraph.c new file mode 100644 index 0000000..7c2de6f --- /dev/null +++ b/src/scenegraph/base_scenegraph.c @@ -0,0 +1,1974 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +/*svg proto*/ +#include +/*MPEG4 tags (for internal nodes)*/ +#include +/*X3D tags (for internal nodes)*/ +#include +#include +#include + +static void ReplaceDEFNode(GF_Node *FromNode, GF_Node *node, GF_Node *newNode, Bool updateOrderedGroup); + +#ifndef GPAC_DISABLE_SVG +static void ReplaceIRINode(GF_Node *FromNode, GF_Node *oldNode, GF_Node *newNode); +#endif + +static void node_modif_stub(GF_SceneGraph *sg, GF_Node *node, GF_FieldInfo *info) +{ +} + +GF_SceneGraph *gf_sg_new() +{ + GF_SceneGraph *tmp; + GF_SAFEALLOC(tmp, GF_SceneGraph); + if (!tmp) return NULL; + + tmp->protos = gf_list_new(); + tmp->unregistered_protos = gf_list_new(); + + tmp->Routes = gf_list_new(); + tmp->routes_to_activate = gf_list_new(); + tmp->routes_to_destroy = gf_list_new(); + tmp->exported_nodes = gf_list_new(); + +#ifndef GPAC_DISABLE_SVG + tmp->dom_evt.evt_list = gf_list_new(); + tmp->dom_evt.ptr = tmp; + tmp->dom_evt.ptr_type = GF_DOM_EVENT_DOCUMENT; + tmp->xlink_hrefs = gf_list_new(); + tmp->smil_timed_elements = gf_list_new(); + tmp->modified_smil_timed_elements = gf_list_new(); + tmp->listeners_to_add = gf_list_new(); +#endif + +#ifdef GPAC_HAS_SPIDERMONKEY + tmp->scripts = gf_list_new(); + tmp->objects = gf_list_new(); +#endif + tmp->on_node_modified = node_modif_stub; + return tmp; +} + +GF_EXPORT +GF_SceneGraph *gf_sg_new_subscene(GF_SceneGraph *scene) +{ + GF_SceneGraph *tmp; + if (!scene) return NULL; + tmp = gf_sg_new(); + if (!tmp) return NULL; + tmp->parent_scene = scene; + tmp->script_action = scene->script_action; + tmp->script_action_cbck = scene->script_action_cbck; + tmp->script_load = scene->script_load; + + /*by default use the same callbacks*/ + tmp->userpriv = scene->userpriv; + tmp->GetSceneTime = scene->GetSceneTime; + tmp->GetExternProtoLib = scene->GetExternProtoLib; + tmp->NodeCallback = scene->NodeCallback; + return tmp; +} + + +GF_EXPORT +void gf_sg_set_node_callback(GF_SceneGraph *sg, void (*NodeCallback)(void *user_priv, u32 type, GF_Node *node, void *ctxdata) ) +{ + sg->NodeCallback = NodeCallback; +} + +GF_EXPORT +void gf_sg_set_scene_time_callback(GF_SceneGraph *sg, Double (*GetSceneTime)(void *user_priv)) +{ + sg->GetSceneTime = GetSceneTime; +} + + +GF_EXPORT +Double gf_node_get_scene_time(GF_Node *node) +{ + if (!node || !node->sgprivate->scenegraph->GetSceneTime) return 0.0; + return node->sgprivate->scenegraph->GetSceneTime(node->sgprivate->scenegraph->userpriv); +} + + +GF_EXPORT +void gf_sg_del(GF_SceneGraph *sg) +{ + if (!sg) return; + + if (sg->global_qp) { + gf_node_unregister(sg->global_qp, NULL); + sg->global_qp = NULL; + } + gf_sg_reset(sg); + +#ifndef GPAC_DISABLE_SVG + gf_list_del(sg->dom_evt.evt_list); + gf_list_del(sg->xlink_hrefs); + gf_list_del(sg->smil_timed_elements); + gf_list_del(sg->modified_smil_timed_elements); + gf_list_del(sg->listeners_to_add); +#endif +#ifdef GPAC_HAS_SPIDERMONKEY + gf_list_del(sg->scripts); + gf_list_del(sg->objects); +#endif + gf_list_del(sg->Routes); + gf_list_del(sg->protos); + gf_list_del(sg->unregistered_protos); + gf_list_del(sg->routes_to_activate); + gf_list_del(sg->routes_to_destroy); + gf_list_del(sg->exported_nodes); + free(sg); +} + +/*recursive traverse of the whole graph to check for scope mixes (nodes from an inline graph +inserted in a parent graph through bind or routes). We must do this otherwise we're certain to get random +crashes or mem leaks.*/ +void SG_GraphRemoved(GF_Node *node, GF_SceneGraph *sg) +{ + u32 i, count; + GF_FieldInfo info; + u32 tag; + + + tag = node->sgprivate->tag; + if (tag == TAG_ProtoNode) return; + + /*not possible in DOM ?*/ + if (tag>GF_NODE_RANGE_LAST_VRML) return; + + count = gf_node_get_field_count(node); + for (i=0; isgprivate->scenegraph==sg) { + /*if root of graph, skip*/ + if (sg->RootNode!=n) { + gf_node_unregister(n, node); + /*don't forget to remove node...*/ + *(GF_Node **) info.far_ptr = NULL; + } + } else { + SG_GraphRemoved(n, sg); + } + } + } + else if (info.fieldType==GF_SG_VRML_MFNODE) { + GF_ChildNodeItem *cur, *prev, *list = *(GF_ChildNodeItem **) info.far_ptr; + prev = NULL; + while (list) { + if (list->node->sgprivate->scenegraph==sg) { + gf_node_unregister(list->node, node); + + if (prev) prev->next = list->next; + else *(GF_ChildNodeItem **) info.far_ptr = list->next; + cur = list; + free(cur); + } else { + SG_GraphRemoved(list->node, sg); + } + list = list->next; + } + } + /*for uncompressed files (bt/xmt/laserML), also reset command lists*/ + else if (info.fieldType==GF_SG_VRML_SFCOMMANDBUFFER) { + u32 j, count2; + SFCommandBuffer *cb = (SFCommandBuffer*)info.far_ptr; + count2 = gf_list_count(cb->commandList); + for (j=0; jcommandList, j); + while ((f=gf_list_enum(com->command_fields, &k))) { + switch (f->fieldType) { + case GF_SG_VRML_SFNODE: + if (f->new_node) { + if (f->new_node->sgprivate->scenegraph==sg) { + /*if root of graph, skip*/ + if (sg->RootNode!=f->new_node) { + gf_node_unregister(f->new_node, node); + /*don't forget to remove node...*/ + f->new_node = NULL; + } + } else { + SG_GraphRemoved(f->new_node, sg); + } + } + break; + case GF_SG_VRML_MFNODE: + if (f->node_list) { + GF_ChildNodeItem *cur, *prev, *list = f->node_list; + prev = NULL; + while (list) { + if (list->node->sgprivate->scenegraph==sg) { + gf_node_unregister(list->node, node); + + if (prev) prev->next = list->next; + else f->node_list = list->next; + cur = list; + free(cur); + } else { + SG_GraphRemoved(list->node, sg); + } + list = list->next; + } + } + break; + } + } + } + } + } +} + +GFINLINE GF_Node *SG_SearchForNode(GF_SceneGraph *sg, GF_Node *node) +{ + NodeIDedItem *reg_node = sg->id_node; + while (reg_node) { + if (reg_node->node == node) return reg_node->node; + reg_node = reg_node->next; + } + return NULL; +} + +static GFINLINE u32 get_num_id_nodes(GF_SceneGraph *sg) +{ + u32 count = 0; + NodeIDedItem *reg_node = sg->id_node; + while (reg_node) { + count++; + reg_node = reg_node->next; + } + return count; +} + +GF_EXPORT +void gf_sg_reset(GF_SceneGraph *sg) +{ + u32 type, count; + NodeIDedItem *reg_node; + if (!sg) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[SceneGraph] Reseting scene graph\n")); +#if 0 + /*inlined graph, remove any of this graph nodes from the parent graph*/ + if (!sg->pOwningProto && sg->parent_scene) { + GF_SceneGraph *par = sg->parent_scene; + while (par->parent_scene) par = par->parent_scene; + if (par->RootNode) SG_GraphRemoved(par->RootNode, sg); + } +#endif + +#ifdef GPAC_HAS_SPIDERMONKEY + /*scripts are the first source of cylic references in the graph. In order to clean properly + force a remove of all script nodes, this will release all references to nodes in JS*/ + while (gf_list_count(sg->scripts)) { + GF_Node *n = gf_list_get(sg->scripts, 0); + gf_list_rem(sg->scripts, 0); + /*prevent destroy*/ + gf_node_register(n, NULL); + /*remove from all parents*/ + gf_node_replace(n, NULL, 0); + /*FORCE destroy in case the script refers to itself*/ + n->sgprivate->num_instances=1; + gf_node_unregister(n, NULL); + } +#endif + +#ifndef GPAC_DISABLE_SVG + + /*remove listeners attached to the doc*/ + while (gf_list_count(sg->dom_evt.evt_list)) { + GF_Node *n = gf_list_get(sg->dom_evt.evt_list, 0); + gf_dom_listener_del(n, &sg->dom_evt); + } + + /*flush any pending add_listener*/ + gf_dom_listener_process_add(sg); +#endif + + while (gf_list_count(sg->routes_to_activate)) { + gf_list_rem(sg->routes_to_activate, 0); + } + + /*destroy all routes*/ + while (gf_list_count(sg->Routes)) { + GF_Route *r = (GF_Route*)gf_list_get(sg->Routes, 0); + /*this will unregister the route from the graph, so don't delete the chain entry*/ + gf_sg_route_del(r); + + } + + /*reset the main tree*/ + if (sg->RootNode) gf_node_unregister(sg->RootNode, NULL); + sg->RootNode = NULL; + + /*THEN reset all exported symbols (we must do it after the reset in case of scripts)*/ + while (gf_list_count(sg->exported_nodes)) { + GF_Node *n = gf_list_get(sg->exported_nodes, 0); + gf_list_rem(sg->exported_nodes, 0); + gf_node_replace(n, NULL, 0); + } + + /*WATCHOUT: we may have cyclic dependencies due to + 1- a node referencing itself (forbidden in VRML) + 2- nodes refered to in commands of conditionals children of this node (MPEG-4 is mute about that) + we recursively preocess from last declared DEF node to first one + */ +restart: + reg_node = sg->id_node; + while (reg_node) { + Bool ignore = 0; + GF_Node *node = reg_node->node; + if (!node || (node==sg->global_qp) ) { + reg_node = reg_node->next; + continue; + } + + /*first replace all instances in parents by NULL WITHOUT UNREGISTERING (to avoid destroying the node). + This will take care of nodes referencing themselves*/ + { + GF_ParentList *nlist = node->sgprivate->parents; + type = (node->sgprivate->tag>GF_NODE_RANGE_LAST_VRML) ? 1 : 0; + + while (nlist) { + GF_ParentList *next = nlist->next; +#if 0 + /*parent is a DEF'ed node, try to clean-up properly?*/ + if ((nlist->node!=node) && SG_SearchForNode(sg, nlist->node) != NULL) { + ignore = 1; + break; + } +#endif + +#ifndef GPAC_DISABLE_SVG + if (type) { + ReplaceIRINode(nlist->node, node, NULL); + } else +#endif + ReplaceDEFNode(nlist->node, reg_node->node, NULL, 0); + + free(nlist); + nlist = next; + } + if (ignore) { + node->sgprivate->parents = nlist; + continue; + } + + node->sgprivate->parents = NULL; + } + //sg->node_registry[i-1] = NULL; + count = get_num_id_nodes(sg); + node->sgprivate->num_instances = 1; + gf_node_unregister(node, NULL); + if (count != get_num_id_nodes(sg)) goto restart; + reg_node = reg_node->next; + } + assert((sg->id_node==NULL) || sg->global_qp); + + /*destroy all proto*/ + while (gf_list_count(sg->protos)) { + GF_Proto *p = (GF_Proto *)gf_list_get(sg->protos, 0); + /*this will unregister the proto from the graph, so don't delete the chain entry*/ + gf_sg_proto_del(p); + } + /*destroy all unregistered proto*/ + while (gf_list_count(sg->unregistered_protos)) { + GF_Proto *p = (GF_Proto *)gf_list_get(sg->unregistered_protos, 0); + /*this will unregister the proto from the graph, so don't delete the chain entry*/ + gf_sg_proto_del(p); + } +#ifndef GPAC_DISABLE_SVG +// assert(gf_list_count(sg->xlink_hrefs) == 0); +#endif + + /*last destroy all routes*/ + gf_sg_destroy_routes(sg); + sg->simulation_tick = 0; + + while (gf_list_count(sg->ns)) { + GF_XMLNS *ns = gf_list_get(sg->ns, 0); + gf_list_rem(sg->ns, 0); + if (ns->name) free(ns->name); + if (ns->qname) free(ns->qname); + free(ns); + } + gf_list_del(sg->ns); + sg->ns = 0; + +#ifdef GF_SELF_REPLACE_ENABLE + sg->graph_has_been_reset = 1; +#endif +} + + +GFINLINE GF_Node *SG_SearchForDuplicateNodeID(GF_SceneGraph *sg, u32 nodeID, GF_Node *toExclude) +{ + NodeIDedItem *reg_node = sg->id_node; + while (reg_node) { + if ((reg_node->node != toExclude) && (reg_node->NodeID == nodeID)) return reg_node->node; + reg_node = reg_node->next; + } + return NULL; +} + +void *gf_node_get_name_address(GF_Node*node) +{ + NodeIDedItem *reg_node; + if (!(node->sgprivate->flags & GF_NODE_IS_DEF)) return NULL; + reg_node = node->sgprivate->scenegraph->id_node; + while (reg_node) { + if (reg_node->node == node) return ®_node->NodeName; + reg_node = reg_node->next; + } + return NULL; +} + +void gf_sg_set_private(GF_SceneGraph *sg, void *ptr) +{ + if (sg) sg->userpriv = ptr; +} + +void *gf_sg_get_private(GF_SceneGraph *sg) +{ + return sg ? sg->userpriv : NULL; +} + + +GF_EXPORT +void gf_sg_set_scene_size_info(GF_SceneGraph *sg, u32 width, u32 height, Bool usePixelMetrics) +{ + if (!sg) return; + if (width && height) { + sg->width = width; + sg->height = height; + } else { + sg->width = sg->height = 0; + } + sg->usePixelMetrics = usePixelMetrics; +} + +GF_EXPORT +Bool gf_sg_use_pixel_metrics(GF_SceneGraph *sg) +{ + if (sg) { + while (sg->pOwningProto) sg = sg->parent_scene; + return sg->usePixelMetrics; + } + return 0; +} + +GF_EXPORT +Bool gf_sg_get_scene_size_info(GF_SceneGraph *sg, u32 *width, u32 *height) +{ + if (!sg) return 0; + *width = sg->width; + *height = sg->height; + return (sg->width && sg->height) ? 1 : 0; +} + + +GF_EXPORT +GF_Node *gf_sg_get_root_node(GF_SceneGraph *sg) +{ + return sg ? sg->RootNode : NULL; +} + +GF_EXPORT +void gf_sg_set_root_node(GF_SceneGraph *sg, GF_Node *node) +{ + if (sg) sg->RootNode = node; +} + +void remove_node_id(GF_SceneGraph *sg, GF_Node *node) +{ + NodeIDedItem *reg_node = sg->id_node; + if (reg_node && (reg_node->node==node)) { + sg->id_node = reg_node->next; + if (sg->id_node_last==reg_node) + sg->id_node_last = reg_node->next; + if (reg_node->NodeName) free(reg_node->NodeName); + free(reg_node); + } else { + NodeIDedItem *to_del; + while (reg_node->next) { + if (reg_node->next->node!=node) { + reg_node = reg_node->next; + continue; + } + to_del = reg_node->next; + reg_node->next = to_del->next; + if (sg->id_node_last==to_del) { + sg->id_node_last = reg_node->next ? reg_node->next : reg_node; + } + if (to_del->NodeName) free(to_del->NodeName); + free(to_del); + break; + } + } +} + +GF_EXPORT +GF_Err gf_node_unregister(GF_Node *pNode, GF_Node *parentNode) +{ + u32 j; + GF_SceneGraph *pSG; + GF_Route *r; + + if (!pNode) return GF_OK; + pSG = pNode->sgprivate->scenegraph; + /*if this is a proto its is registered in its parent graph, not the current*/ + if (pSG && (pNode == (GF_Node*)pSG->pOwningProto)) pSG = pSG->parent_scene; + + if (parentNode) { + GF_ParentList *nlist = pNode->sgprivate->parents; + if (nlist) { + GF_ParentList *prev = NULL; + while (nlist) { + if (nlist->node != parentNode) { + prev = nlist; + nlist = nlist->next; + continue; + } + if (prev) prev->next = nlist->next; + else pNode->sgprivate->parents = nlist->next; + free(nlist); + break; + } + } + if (parentNode->sgprivate->scenegraph != pSG) { + gf_list_del_item(pSG->exported_nodes, pNode); + } + } + + /*unregister the instance*/ + assert(pNode->sgprivate->num_instances); + pNode->sgprivate->num_instances -= 1; + + /*this is just an instance removed*/ + if (pNode->sgprivate->num_instances) { + return GF_OK; + } + + + assert(pNode->sgprivate->parents==NULL); + + if (pSG) { + /*if def, remove from sg def table*/ + if (pNode->sgprivate->flags & GF_NODE_IS_DEF) { + remove_node_id(pSG, pNode); + } + + /*check all routes from or to this node and destroy them - cf spec*/ + j=0; + while ((r = (GF_Route *)gf_list_enum(pSG->Routes, &j))) { + if ( (r->ToNode == pNode) || (r->FromNode == pNode)) { + gf_sg_route_del(r); + j--; + } + } + } + /*delete the node*/ + if (pNode->sgprivate->scenegraph && (pNode->sgprivate->scenegraph->RootNode==pNode)) { + pSG = pNode->sgprivate->scenegraph; + gf_node_del(pNode); + pSG->RootNode = NULL; + } else { + gf_node_del(pNode); + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_node_register(GF_Node *node, GF_Node *parentNode) +{ + GF_SceneGraph *pSG; + if (!node) return GF_OK; + + pSG = node->sgprivate->scenegraph; + /*if this is a proto register to the parent graph, not the current*/ + if (pSG && (node == (GF_Node*)pSG->pOwningProto)) pSG = pSG->parent_scene; + + node->sgprivate->num_instances ++; + /*parent may be NULL (top node and proto)*/ + if (parentNode) { + if (!node->sgprivate->parents) { + node->sgprivate->parents = (GF_ParentList*)malloc(sizeof(GF_ParentList)); + node->sgprivate->parents->next = NULL; + node->sgprivate->parents->node = parentNode; + } else { + GF_ParentList *item, *nlist = node->sgprivate->parents; + while (nlist->next) nlist = nlist->next; + item = (GF_ParentList*)malloc(sizeof(GF_ParentList)); + item->next = NULL; + item->node = parentNode; + nlist->next = item; + } + if (parentNode->sgprivate->scenegraph != pSG) { + gf_list_add(pSG->exported_nodes, node); + } + } + return GF_OK; +} + +/*replace or remove node instance in the given node (eg in all GF_Node or MFNode fields) +this doesn't propagate in the scene graph. If updateOrderedGroup and new_node is NULL, the order field of OG +is updated*/ +static void ReplaceDEFNode(GF_Node *FromNode, GF_Node *node, GF_Node *newNode, Bool updateOrderedGroup) +{ + u32 i, j; + GF_Node *p; + GF_ChildNodeItem *list; + + GF_FieldInfo field; + + /*browse all fields*/ + for (i=0; inode) || (list->node != node)) { + list = list->next; + j++; + continue; + } + if (newNode) { + list->node = newNode; + } else { + gf_node_list_del_child( (GF_ChildNodeItem **) field.far_ptr, list->node); + if (updateOrderedGroup && (FromNode->sgprivate->tag==TAG_MPEG4_OrderedGroup)) { + GF_FieldInfo info; + M_OrderedGroup *og = (M_OrderedGroup *)FromNode; + info.fieldIndex = 3; + info.fieldType = GF_SG_VRML_MFFLOAT; + info.on_event_in = NULL; + info.far_ptr = &og->order; + gf_sg_vrml_mf_remove(&og->order, GF_SG_VRML_SFINT32, j); + gf_node_changed_internal(FromNode, &info, 1); + } + } + goto exit; + } + break; + /*not a node, continue*/ + default: + continue; + } + } + /*since we don't filter parent nodes this is called once per USE, not per container, so return if found*/ +exit: + gf_node_changed(FromNode, &field); +} + +#ifndef GPAC_DISABLE_SVG + +static void Replace_IRI(GF_SceneGraph *sg, GF_Node *old_node, GF_Node *newNode) +{ + u32 i, count; + count = gf_list_count(sg->xlink_hrefs); + for (i=0; ixlink_hrefs, i); + if (iri->target == old_node) { + iri->target = newNode; + if (!newNode) { + gf_list_rem(sg->xlink_hrefs, i); + i--; + count--; + } + } + } +} + +/*replace or remove node instance in the given node (eg in all IRI)*/ +static void ReplaceIRINode(GF_Node *FromNode, GF_Node *old_node, GF_Node *newNode) +{ + GF_ChildNodeItem *prev = NULL; + GF_ChildNodeItem *child = ((SVG_Element *)FromNode)->children; + while (child) { + if (child->node != old_node) { + prev = child; + child = child->next; + continue; + } + if (newNode) { + child->node = newNode; + } else { + if (prev) prev->next = child->next; + else ((SVG_Element *)FromNode)->children = child->next; + free(child); + } + break; + } +} +#endif + +/*get all parents of the node and replace, the instance of the node and finally destroy the node*/ +GF_Err gf_node_replace(GF_Node *node, GF_Node *new_node, Bool updateOrderedGroup) +{ + u32 type; + Bool replace_root, replace_proto; + GF_Node *par; + GF_SceneGraph *pSG = node->sgprivate->scenegraph; + + /*if this is a proto its is registered in its parent graph, not the current*/ + if (node == (GF_Node*)pSG->pOwningProto) pSG = pSG->parent_scene; +// if (!SG_SearchForNodeIndex(pSG, node, &i)) return GF_BAD_PARAM; +// assert(node == pSG->node_registry[i]); + + type = (node->sgprivate->tag>GF_NODE_RANGE_LAST_VRML) ? 1 : 0; +#ifndef GPAC_DISABLE_SVG + if (type) { + Replace_IRI(pSG, node, new_node); + } +#endif + + /*first check if this is the root node*/ + replace_root = (node->sgprivate->scenegraph->RootNode == node) ? 1 : 0; + replace_proto = 0; + if (node->sgprivate->scenegraph->pOwningProto + && (gf_list_find(node->sgprivate->scenegraph->pOwningProto->node_code, node)>=0)) { + replace_proto = 1; + } + + while (node->sgprivate->parents) { + Bool do_break = node->sgprivate->parents->next ? 0 : 1; + par = node->sgprivate->parents->node; + +#ifndef GPAC_DISABLE_SVG + if (type) + ReplaceIRINode(par, node, new_node); + else +#endif + ReplaceDEFNode(par, node, new_node, updateOrderedGroup); + + if (new_node) gf_node_register(new_node, par); + gf_node_unregister(node, par); + gf_node_changed(par, NULL); + if (do_break) break; + } + + if (replace_root) { + pSG = node->sgprivate->scenegraph; + gf_node_unregister(node, NULL); + pSG->RootNode = new_node; + } + if (replace_proto) { + pSG = node->sgprivate->scenegraph; + gf_list_del_item(pSG->pOwningProto->node_code, node); + if (pSG->pOwningProto->RenderingNode==node) pSG->pOwningProto->RenderingNode = NULL; + gf_node_unregister(node, NULL); + } + + return GF_OK; +} + +static GFINLINE void insert_node_def(GF_SceneGraph *sg, GF_Node *def, u32 ID, const char *name) +{ + NodeIDedItem *reg_node, *cur; + + reg_node = (NodeIDedItem *) malloc(sizeof(NodeIDedItem)); + reg_node->node = def; + reg_node->NodeID = ID; + reg_node->NodeName = name ? strdup(name) : NULL; + + if (!sg->id_node) { + sg->id_node = reg_node; + sg->id_node_last = sg->id_node; + reg_node->next = NULL; + } else if (sg->id_node_last->NodeID < ID) { + sg->id_node_last->next = reg_node; + sg->id_node_last = reg_node; + reg_node->next = NULL; + } else if (sg->id_node->NodeID>ID) { + reg_node->next = sg->id_node; + sg->id_node = reg_node; + } else { + cur = sg->id_node; + while (cur->next) { + if (cur->next->NodeID>ID) { + reg_node->next = cur->next; + cur->next = reg_node; + return; + } + cur = cur->next; + } + cur->next = reg_node; + sg->id_node_last = reg_node; + reg_node->next = NULL; + } +} + + + +GF_EXPORT +GF_Err gf_node_set_id(GF_Node *p, u32 ID, const char *name) +{ + GF_SceneGraph *pSG; + if (!ID || !p || !p->sgprivate->scenegraph) return GF_BAD_PARAM; + + pSG = p->sgprivate->scenegraph; + /*if this is a proto register to the parent graph, not the current*/ + if (p == (GF_Node*)pSG->pOwningProto) pSG = pSG->parent_scene; + + /*new DEF ID*/ + if (!(p->sgprivate->flags & GF_NODE_IS_DEF) ) { + p->sgprivate->flags |= GF_NODE_IS_DEF; + insert_node_def(pSG, p, ID, name); + } + /*reassigning ID, remove node def*/ + else { + char *_name = strdup(name); + remove_node_id(pSG, p); + insert_node_def(pSG, p, ID, _name); + free(_name); + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_node_remove_id(GF_Node *p) +{ + GF_SceneGraph *pSG; + if (!p) return GF_BAD_PARAM; + + pSG = p->sgprivate->scenegraph; + /*if this is a proto register to the parent graph, not the current*/ + if (p == (GF_Node*)pSG->pOwningProto) pSG = pSG->parent_scene; + + /*new DEF ID*/ + if (p->sgprivate->flags & GF_NODE_IS_DEF) { + remove_node_id(pSG, p); + p->sgprivate->flags &= ~GF_NODE_IS_DEF; + return GF_OK; + } + return GF_BAD_PARAM; +} + +/*calls RenderNode on this node*/ +GF_EXPORT +void gf_node_traverse(GF_Node *node, void *renderStack) +{ + if (!node || !node->sgprivate) return; + + if (node->sgprivate->flags & GF_NODE_IS_DEACTIVATED) return; + + if (node->sgprivate->UserCallback) { +#ifdef GF_CYCLIC_TRAVERSE_ON + if (node->sgprivate->flags & GF_NODE_IN_TRAVERSE) return; + node->sgprivate->flags |= GF_NODE_IN_TRAVERSE; + assert(node->sgprivate->flags); +#endif + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[SceneGraph] Traversing node %s (ID %s)\n", gf_node_get_class_name(node) , gf_node_get_name(node) )); + node->sgprivate->UserCallback(node, renderStack, 0); +#ifdef GF_CYCLIC_TRAVERSE_ON + node->sgprivate->flags &= ~GF_NODE_IN_TRAVERSE; +#endif + return; + } + if (node->sgprivate->tag != TAG_ProtoNode) return; + + /*proto only traverses its first child*/ + if (((GF_ProtoInstance *) node)->RenderingNode) { + node = ((GF_ProtoInstance *) node)->RenderingNode; + /*if rendering node is a proto and not a hardcoded proto, traverse it*/ + if (!node->sgprivate->UserCallback && (node->sgprivate->tag == TAG_ProtoNode)) { + gf_node_traverse(node, renderStack); + return; + } + } + /*if no rendering function is assigned this is a real proto (otherwise this is an hardcoded one)*/ + else if (!node->sgprivate->UserCallback) { + /*if no rendering node, check if the proto is fully instanciated (externProto)*/ + GF_ProtoInstance *proto_inst = (GF_ProtoInstance *) node; + gf_node_dirty_clear(node, 0); + /*proto has been deleted or dummy proto (without node code)*/ + if (!proto_inst->proto_interface || proto_inst->is_loaded) return; + /*try to load the code*/ + gf_sg_proto_instanciate(proto_inst); + + /*if user callback is set, this is an hardcoded proto. If not, locate the first traversable node*/ + if (!node->sgprivate->UserCallback) { + if (!proto_inst->RenderingNode) { + gf_node_dirty_set(node, 0, 1); + return; + } + /*signal we have been loaded*/ + node->sgprivate->scenegraph->NodeCallback(node->sgprivate->scenegraph->userpriv, GF_SG_CALLBACK_MODIFIED, node, NULL); + } + } + if (node->sgprivate->UserCallback) { +#ifdef GF_CYCLIC_TRAVERSE_ON + if (node->sgprivate->flags & GF_NODE_IN_TRAVERSE) return; + node->sgprivate->flags |= GF_NODE_IN_TRAVERSE; +#endif + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[SceneGraph] Traversing node %s\n", gf_node_get_class_name(node) )); + node->sgprivate->UserCallback(node, renderStack, 0); +#ifdef GF_CYCLIC_TRAVERSE_ON + node->sgprivate->flags &= ~GF_NODE_IN_TRAVERSE; +#endif + } +} + +GF_EXPORT +void gf_node_allow_cyclic_traverse(GF_Node *node) +{ +#ifdef GF_CYCLIC_TRAVERSE_ON + if (node) node->sgprivate->flags &= ~GF_NODE_IN_TRAVERSE; +#endif +} + +/*blindly calls RenderNode on all nodes in the "children" list*/ +GF_EXPORT +void gf_node_traverse_children(GF_Node *node, void *renderStack) +{ + GF_ChildNodeItem *child; + + assert(node); + child = ((GF_ParentNode *)node)->children; + while (child) { + gf_node_traverse(child->node, renderStack); + child = child->next; + } +} + + +GF_EXPORT +GF_SceneGraph *gf_node_get_graph(GF_Node *node) +{ + return (node ? node->sgprivate->scenegraph : NULL); +} + +GF_EXPORT +GF_Node *gf_sg_find_node(GF_SceneGraph *sg, u32 nodeID) +{ + NodeIDedItem *reg_node = sg->id_node; + while (reg_node) { + if (reg_node->NodeID == nodeID) return reg_node->node; + reg_node = reg_node->next; + } + return NULL; +} + +GF_EXPORT +GF_Node *gf_sg_find_node_by_name(GF_SceneGraph *sg, char *name) +{ + if (name) { + NodeIDedItem *reg_node = sg->id_node; + while (reg_node) { + if (reg_node->NodeName && !strcmp(reg_node->NodeName, name)) return reg_node->node; + reg_node = reg_node->next; + } + } + return NULL; +} + + +GF_EXPORT +u32 gf_sg_get_next_available_node_id(GF_SceneGraph *sg) +{ + u32 ID; + NodeIDedItem *reg_node; + if (!sg->id_node) return 1; + reg_node = sg->id_node; + ID = reg_node->NodeID; + /*nodes are sorted*/ + while (reg_node->next) { + if (ID+1next->NodeID) return ID+1; + ID = reg_node->next->NodeID; + reg_node = reg_node->next; + } + return ID+1; +} + +u32 gf_sg_get_max_node_id(GF_SceneGraph *sg) +{ + NodeIDedItem *reg_node; + if (!sg->id_node) return 0; + if (sg->id_node_last) return sg->id_node_last->NodeID; + reg_node = sg->id_node; + while (reg_node->next) reg_node = reg_node->next; + return reg_node->NodeID; +} + +void gf_node_setup(GF_Node *p, u32 tag) +{ + GF_SAFEALLOC(p->sgprivate, NodePriv); + p->sgprivate->tag = tag; + p->sgprivate->flags = GF_SG_NODE_DIRTY; +} + +GF_Node *gf_sg_new_base_node() +{ + GF_Node *newnode = (GF_Node *)malloc(sizeof(GF_Node)); + gf_node_setup(newnode, TAG_UndefinedNode); + return newnode; +} +GF_EXPORT +u32 gf_node_get_tag(GF_Node*p) +{ + assert(p); + return p->sgprivate->tag; +} +GF_EXPORT +u32 gf_node_get_id(GF_Node*p) +{ + NodeIDedItem *reg_node; + GF_SceneGraph *sg; + assert(p); + if (!(p->sgprivate->flags & GF_NODE_IS_DEF)) return 0; + sg = p->sgprivate->scenegraph; + /*if this is a proto, look in parent graph*/ + if (p == (GF_Node*)sg->pOwningProto) sg = sg->parent_scene; + + reg_node = sg->id_node; + while (reg_node) { + if (reg_node->node==p) return reg_node->NodeID; + reg_node = reg_node->next; + } + return 0; +} + +GF_EXPORT +const char *gf_node_get_name(GF_Node*p) +{ + GF_SceneGraph *sg; + NodeIDedItem *reg_node; + if (!p || !(p->sgprivate->flags & GF_NODE_IS_DEF)) return NULL; + + sg = p->sgprivate->scenegraph; + /*if this is a proto, look in parent graph*/ + if (p == (GF_Node*)sg->pOwningProto) sg = sg->parent_scene; + + reg_node = sg->id_node; + while (reg_node) { + if (reg_node->node==p) return reg_node->NodeName; + reg_node = reg_node->next; + } + return NULL; +} + +GF_EXPORT +const char *gf_node_get_name_and_id(GF_Node*p, u32 *id) +{ + GF_SceneGraph *sg; + NodeIDedItem *reg_node; + assert(p); + if (!(p->sgprivate->flags & GF_NODE_IS_DEF)) { + *id = 0; + return NULL; + } + + sg = p->sgprivate->scenegraph; + /*if this is a proto, look in parent graph*/ + if (p == (GF_Node*)sg->pOwningProto) sg = sg->parent_scene; + + reg_node = sg->id_node; + while (reg_node) { + if (reg_node->node==p) { + *id = reg_node->NodeID; + return reg_node->NodeName; + } + reg_node = reg_node->next; + } + *id = 0; + return NULL; +} + +GF_EXPORT +void *gf_node_get_private(GF_Node*p) +{ + assert(p); + return p->sgprivate->UserPrivate; +} +GF_EXPORT +void gf_node_set_private(GF_Node*p, void *pr) +{ + assert(p); + p->sgprivate->UserPrivate = pr; +} +GF_EXPORT +GF_Err gf_node_set_callback_function(GF_Node *p, void (*TraverseNode)(GF_Node *node, void *render_stack, Bool is_destroy) ) +{ + assert(p); + p->sgprivate->UserCallback = TraverseNode; + return GF_OK; +} + +void gf_sg_parent_setup(GF_Node *node) +{ + ((GF_ParentNode *)node)->children = NULL; + node->sgprivate->flags |= GF_SG_CHILD_DIRTY; +} + +GF_EXPORT +void gf_node_unregister_children(GF_Node *container, GF_ChildNodeItem *child) +{ + GF_ChildNodeItem *cur; + while (child) { + gf_node_unregister(child->node, container); + cur = child; + child = child->next; + free(cur); + } +} + +GF_EXPORT +GF_Err gf_node_list_insert_child(GF_ChildNodeItem **list, GF_Node *n, u32 pos) +{ + GF_ChildNodeItem *child, *cur, *prev; + u32 cur_pos = 0; + + assert(pos != (u32) -1); + + child = *list; + + cur = (GF_ChildNodeItem*) malloc(sizeof(GF_ChildNodeItem)); + if (!cur) return GF_OUT_OF_MEM; + cur->node = n; + cur->next = NULL; + prev = NULL; + while (child) { + if (pos==cur_pos) break; + /*append*/ + if (!child->next) { + child->next = cur; + return GF_OK; + } + prev = child; + child = child->next; + cur_pos++; + } + cur->next = child; + if (prev) prev->next = cur; + else *list = cur; + return GF_OK; +} + +GF_EXPORT +GF_Node *gf_node_list_get_child(GF_ChildNodeItem *list, s32 pos) +{ + s32 cur_pos = 0; + while (list) { + if (pos==cur_pos) return list->node; + if ((pos<0) && !list->next) return list->node; + list = list->next; + cur_pos++; + } + return NULL; +} + +GF_EXPORT +s32 gf_node_list_find_child(GF_ChildNodeItem *list, GF_Node *n) +{ + s32 res = 0; + while (list) { + if (list->node==n) return res; + list = list->next; + res++; + } + return -1; +} + +GF_EXPORT +GF_Err gf_node_list_add_child(GF_ChildNodeItem **list, GF_Node *n) +{ + GF_ChildNodeItem *child, *cur; + + child = *list; + + cur = (GF_ChildNodeItem*) malloc(sizeof(GF_ChildNodeItem)); + if (!cur) return GF_OUT_OF_MEM; + cur->node = n; + cur->next = NULL; + if (child) { + while (child->next) child = child->next; + child->next = cur; + } else { + *list = cur; + } + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_node_list_add_child_last(GF_ChildNodeItem **list, GF_Node *n, GF_ChildNodeItem **last_child) +{ + GF_ChildNodeItem *child, *cur; + + child = *list; + + cur = (GF_ChildNodeItem*) malloc(sizeof(GF_ChildNodeItem)); + if (!cur) return GF_OUT_OF_MEM; + cur->node = n; + cur->next = NULL; + if (child) { + if (last_child && (*last_child) ) { + while ((*last_child)->next) (*last_child) = (*last_child)->next; + (*last_child)->next = cur; + (*last_child) = (*last_child)->next; + } else { + while (child->next) child = child->next; + child->next = cur; + if (last_child) *last_child = child->next; + } + } else { + *list = cur; + if (last_child) + *last_child = *list; + } + return GF_OK; +} + +GF_EXPORT +Bool gf_node_list_del_child(GF_ChildNodeItem **list, GF_Node *n) +{ + GF_ChildNodeItem *child, *cur; + + child = *list; + if (!child) return 0; + if (child->node==n) { + *list = child->next; + free(child); + return 1; + } + + while (child->next) { + if (child->next->node!=n) { + child = child->next; + continue; + } + cur = child->next; + child->next = cur->next; + free(cur); + return 1; + } + return 0; +} + +GF_EXPORT +GF_Node *gf_node_list_del_child_idx(GF_ChildNodeItem **list, u32 pos) +{ + u32 cur_pos = 0; + GF_Node *ret = NULL; + GF_ChildNodeItem *child, *cur; + + child = *list; + if (!child) return 0; + if (!pos) { + *list = child->next; + ret = child->node; + free(child); + return ret; + } + + while (child->next) { + if (cur_pos+1 != pos) { + child = child->next; + cur_pos++; + continue; + } + cur = child->next; + child->next = cur->next; + ret = cur->node; + free(cur); + return ret; + } + return NULL; +} + +GF_EXPORT +u32 gf_node_list_get_count(GF_ChildNodeItem *list) +{ + u32 count = 0; + while (list) { + count++; + list = list->next; + } + return count; +} + +void gf_sg_parent_reset(GF_Node *node) +{ + gf_node_unregister_children(node, ((GF_ParentNode *)node)->children); + ((GF_ParentNode *)node)->children = NULL; +} + + +void gf_node_free(GF_Node *node) +{ + if (!node) return; + + if (node->sgprivate->UserCallback) node->sgprivate->UserCallback(node, NULL, 1); + + if (node->sgprivate->interact) { + if (node->sgprivate->interact->routes) { + gf_list_del(node->sgprivate->interact->routes); + } +#ifndef GPAC_DISABLE_SVG + if (node->sgprivate->interact->dom_evt) { + while (gf_list_count(node->sgprivate->interact->dom_evt->evt_list)) { + GF_Node *n = gf_list_get(node->sgprivate->interact->dom_evt->evt_list, 0); + gf_dom_listener_del(n, node->sgprivate->interact->dom_evt); + } + gf_list_del(node->sgprivate->interact->dom_evt->evt_list); + free(node->sgprivate->interact->dom_evt); + } + if (node->sgprivate->interact->animations) { + gf_list_del(node->sgprivate->interact->animations); + } +#endif + free(node->sgprivate->interact); + } + assert(! node->sgprivate->parents); + free(node->sgprivate); + free(node); +} + +GF_EXPORT +u32 gf_node_get_parent_count(GF_Node *node) +{ + u32 count = 0; + GF_ParentList *nlist = node->sgprivate->parents; + while (nlist) { count++; nlist = nlist->next; } + return count; +} + +GF_EXPORT +GF_Node *gf_node_get_parent(GF_Node *node, u32 idx) +{ + + GF_ParentList *nlist = node->sgprivate->parents; + /*break cyclic graphs*/ + if (node->sgprivate->scenegraph->RootNode==node) return NULL; + if (node->sgprivate->scenegraph->pOwningProto && node->sgprivate->scenegraph->pOwningProto->RenderingNode==node) + return NULL; + if (!nlist) return NULL; + while (idx) { nlist = nlist->next; idx--;} + return nlist ? nlist->node : NULL; +} + +static GFINLINE void dirty_children(GF_Node *node, u32 val) +{ + u32 i, count; + GF_FieldInfo info; + if (!node) return; + + node->sgprivate->flags |= val; + if (node->sgprivate->tag>=GF_NODE_RANGE_LAST_VRML) { + GF_ChildNodeItem *child = ((GF_ParentNode*)node)->children; + while (child) { + dirty_children(child->node, val); + child = child->next; + } + } else { + count = gf_node_get_field_count(node); + for (i=0; inode, val); + list = list->next; + } + } + } + } +} +static void dirty_parents(GF_Node *node) +{ + Bool check_root = 1; + GF_ParentList *nlist = node->sgprivate->parents; + while (nlist) { + GF_Node *p = nlist->node; + if (! (p->sgprivate->flags & GF_SG_CHILD_DIRTY)) { + p->sgprivate->flags |= GF_SG_CHILD_DIRTY; + dirty_parents(p); + } + check_root = 0; + nlist = nlist->next; + } + /*propagate to parent scene graph */ + if (check_root) { + /*if root node of the scenegraph*/ + if (node->sgprivate->scenegraph->NodeCallback && (node==node->sgprivate->scenegraph->RootNode) ) { + node->sgprivate->scenegraph->NodeCallback(node->sgprivate->scenegraph->userpriv, GF_SG_CALLBACK_GRAPH_DIRTY, NULL, NULL); + } + /*or if parent graph is a protoinstance but the node is not this proto*/ + else if (node->sgprivate->scenegraph->pOwningProto) { + GF_Node *the_node = (GF_Node *) node->sgprivate->scenegraph->pOwningProto; + if (the_node != node) dirty_parents(the_node); + } + } +} + +GF_EXPORT +void gf_node_dirty_set(GF_Node *node, u32 flags, Bool and_dirty_parents) +{ + if (!node) return; + + if (flags) node->sgprivate->flags |= (flags & (~GF_NODE_INTERNAL_FLAGS) ); + else node->sgprivate->flags |= GF_SG_NODE_DIRTY; + + if (and_dirty_parents) dirty_parents(node); +} + +GF_EXPORT +void gf_node_dirty_parents(GF_Node *node) +{ + dirty_parents(node); +} + +GF_EXPORT +void gf_node_dirty_clear(GF_Node *node, u32 flag_to_remove) +{ + if (!node) return; + if (flag_to_remove) node->sgprivate->flags &= ~ (flag_to_remove & ~GF_NODE_INTERNAL_FLAGS); + else node->sgprivate->flags &= GF_NODE_INTERNAL_FLAGS; +} + +GF_EXPORT +u32 gf_node_dirty_get(GF_Node *node) +{ + if (node) return (node->sgprivate->flags & ~GF_NODE_INTERNAL_FLAGS); + return 0; +} + + +GF_EXPORT +void gf_node_dirty_reset(GF_Node *node) +{ + if (!node) return; + if (node->sgprivate->flags & ~GF_NODE_INTERNAL_FLAGS) { + node->sgprivate->flags &= GF_NODE_INTERNAL_FLAGS; + dirty_children(node, 0); + } +} + + + +GF_EXPORT +void gf_node_init(GF_Node *node) +{ + GF_SceneGraph *pSG = node->sgprivate->scenegraph; + assert(pSG); + /*no user-defined init, consider the scenegraph is only used for parsing/encoding/decoding*/ + if (!pSG->NodeCallback) return; + + /*internal nodes*/ + if (gf_sg_vrml_node_init(node)) return; +#ifndef GPAC_DISABLE_SVG + else if (gf_svg_node_init(node)) return; +#endif + /*user defined init*/ + else pSG->NodeCallback(pSG->userpriv, GF_SG_CALLBACK_INIT, node, NULL); +} + + +void gf_node_changed_internal(GF_Node *node, GF_FieldInfo *field, Bool notify_scripts) +{ + GF_SceneGraph *sg; + if (!node) return; + + sg = node->sgprivate->scenegraph; + assert(sg); + + /*signal changes in node to JS for MFFields*/ + if (field && notify_scripts && (node->sgprivate->flags & GF_NODE_HAS_BINDING) && !gf_sg_vrml_is_sf_field(field->fieldType) ) + sg->on_node_modified(sg, node, field); + + /*internal nodes*/ + if (gf_sg_vrml_node_changed(node, field)) return; +#ifndef GPAC_DISABLE_SVG + else if (gf_svg_node_changed(node, field)) return; +#endif + + /*force child dirty tag*/ + if (field && ( (field->fieldType==GF_SG_VRML_SFNODE) || (field->fieldType==GF_SG_VRML_MFNODE)) ) + node->sgprivate->flags |= GF_SG_CHILD_DIRTY; + + if (sg->NodeCallback) sg->NodeCallback(sg->userpriv, GF_SG_CALLBACK_MODIFIED, node, field); +} + +GF_EXPORT +void gf_node_changed(GF_Node *node, GF_FieldInfo *field) +{ + gf_node_changed_internal(node, field, 1); + +#ifndef GPAC_DISABLE_SVG + /* we should avoid dispatching a DOMSubtreeModified event on insertion of time values in begin/end fields + because this retriggers begin/end events and reinsertion */ + if (field == NULL && + node->sgprivate->tag >= GF_NODE_RANGE_FIRST_SVG && node->sgprivate->tag <= GF_NODE_RANGE_LAST_SVG) { + GF_DOM_Event evt; + evt.type = GF_EVENT_TREE_MODIFIED; + evt.bubbles = 0; + evt.relatedNode = node; + gf_dom_event_fire(node, &evt); + } +#endif +} + +void gf_node_del(GF_Node *node) +{ + if (node->sgprivate->tag==TAG_UndefinedNode) gf_node_free(node); + else if (node->sgprivate->tag==TAG_DOMText) { + GF_DOMText *t = (GF_DOMText *)node; + if (t->textContent) free(t->textContent); + gf_sg_parent_reset(node); + gf_node_free(node); + } + else if (node->sgprivate->tag==TAG_DOMUpdates) { + u32 i, count; + GF_DOMUpdates *up = (GF_DOMUpdates *)node; + if (up->data) free(up->data); + count = gf_list_count(up->updates); + for (i=0; iupdates, i); + gf_sg_command_del(com); + } + gf_list_del(up->updates); + gf_sg_parent_reset(node); + gf_node_free(node); + } + else if (node->sgprivate->tag == TAG_DOMFullNode) { + GF_DOMFullNode *n = (GF_DOMFullNode *)node; +#ifndef GPAC_DISABLE_SVG + gf_node_delete_attributes(node); +#endif + if (n->name) free(n->name); + gf_sg_parent_reset(node); + gf_node_free(node); + } + else if (node->sgprivate->tag == TAG_ProtoNode) gf_sg_proto_del_instance((GF_ProtoInstance *)node); + else if (node->sgprivate->tag<=GF_NODE_RANGE_LAST_MPEG4) gf_sg_mpeg4_node_del(node); + else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) gf_sg_x3d_node_del(node); +#ifndef GPAC_DISABLE_SVG + else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_SVG) gf_svg_node_del(node); +#endif + else gf_node_free(node); +} + +GF_EXPORT +u32 gf_node_get_field_count(GF_Node *node) +{ + assert(node); + if (node->sgprivate->tag <= TAG_UndefinedNode) return 0; + /*for both MPEG4 & X3D*/ + else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) return gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_ALL); +#ifndef GPAC_DISABLE_SVG + else if (node->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) return gf_node_get_attribute_count(node); +#endif + return 0; +} + +GF_EXPORT +const char *gf_node_get_class_name(GF_Node *node) +{ + assert(node && node->sgprivate->tag); + if (node->sgprivate->tag==TAG_UndefinedNode) return "UndefinedNode"; + else if (node->sgprivate->tag==TAG_ProtoNode) return ((GF_ProtoInstance*)node)->proto_name; + else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_MPEG4) return gf_sg_mpeg4_node_get_class_name(node->sgprivate->tag); + else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) return gf_sg_x3d_node_get_class_name(node->sgprivate->tag); + else if (node->sgprivate->tag==TAG_DOMText) return ""; + else if (node->sgprivate->tag==TAG_DOMFullNode) { + char *xmlns; + GF_DOMFullNode*full = (GF_DOMFullNode*)node; + u32 ns = gf_sg_get_namespace_code(node->sgprivate->scenegraph, NULL); + if (ns == full->ns) return full->name; + xmlns = (char *) gf_sg_get_namespace_qname(node->sgprivate->scenegraph, full->ns); + if (!xmlns) return full->name; + sprintf(node->sgprivate->scenegraph->szNameBuffer, "%s:%s", xmlns, full->name); + return node->sgprivate->scenegraph->szNameBuffer; + } +#ifndef GPAC_DISABLE_SVG + else return gf_xml_get_element_name(node); +#endif + return "UnsupportedNode"; +} + +GF_EXPORT +u32 gf_sg_node_get_tag_by_class_name(const char *name, u32 ns) +{ + u32 tag; + + /* TODO: handle name spaces */ + tag = gf_node_mpeg4_type_by_class_name(name); + if (tag) return tag; + + tag = gf_node_x3d_type_by_class_name(name); + if (tag) return tag; + +#ifndef GPAC_DISABLE_SVG + tag = gf_xml_get_element_tag(name, ns); + if (tag != TAG_UndefinedNode) return tag; +#endif + + return TAG_UndefinedNode; +} + +GF_EXPORT +GF_Node *gf_node_new(GF_SceneGraph *inScene, u32 tag) +{ + GF_Node *node; +// if (!inScene) return NULL; + /*cannot create proto this way*/ + if (tag==TAG_ProtoNode) return NULL; + else if (tag==TAG_UndefinedNode) node = gf_sg_new_base_node(); + else if (tag <= GF_NODE_RANGE_LAST_MPEG4) node = gf_sg_mpeg4_node_new(tag); + else if (tag <= GF_NODE_RANGE_LAST_X3D) node = gf_sg_x3d_node_new(tag); + else if (tag == TAG_DOMText) { + GF_DOMText *n; + GF_SAFEALLOC(n, GF_DOMText); + node = (GF_Node*)n; + gf_node_setup(node, TAG_DOMText); + } + else if (tag == TAG_DOMFullNode) { + GF_DOMFullNode*n; + GF_SAFEALLOC(n, GF_DOMFullNode); + node = (GF_Node*)n; + gf_node_setup(node, TAG_DOMFullNode); + } +#ifndef GPAC_DISABLE_SVG + else if (tag <= GF_NODE_RANGE_LAST_SVG) node = (GF_Node *) gf_svg_create_node(tag); + else if (tag <= GF_NODE_RANGE_LAST_XBL) node = (GF_Node *) gf_xbl_create_node(tag); +#endif + else node = NULL; + + if (node) node->sgprivate->scenegraph = inScene; + /*script is inited as soon as created since fields are dynamically added*/ + if ((tag==TAG_MPEG4_Script) || (tag==TAG_X3D_Script) ) gf_sg_script_init(node); + return node; +} + + +GF_EXPORT +GF_Err gf_node_get_field(GF_Node *node, u32 FieldIndex, GF_FieldInfo *info) +{ + assert(node); + assert(info); + memset(info, 0, sizeof(GF_FieldInfo)); + info->fieldIndex = FieldIndex; + + if (node->sgprivate->tag==TAG_UndefinedNode) return GF_BAD_PARAM; + else if (node->sgprivate->tag == TAG_ProtoNode) return gf_sg_proto_get_field(NULL, node, info); + else if ((node->sgprivate->tag == TAG_MPEG4_Script) || (node->sgprivate->tag == TAG_X3D_Script) ) + return gf_sg_script_get_field(node, info); + else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_MPEG4) return gf_sg_mpeg4_node_get_field(node, info); + else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) return gf_sg_x3d_node_get_field(node, info); + +#ifndef GPAC_DISABLE_SVG + else if (node->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) return gf_node_get_attribute_info(node, info); +#endif + return GF_NOT_SUPPORTED; +} + +u32 gf_node_get_num_instances(GF_Node *node) +{ + return node->sgprivate->num_instances; +} + +static GF_Err gf_node_get_field_by_name_enum(GF_Node *node, char *name, GF_FieldInfo *field) +{ + u32 i, count; + assert(node); + count = gf_node_get_field_count(node); + memset(field, 0, sizeof(GF_FieldInfo)); + for (i=0; iname, name)) return GF_OK; + } + return GF_BAD_PARAM; +} + +GF_Err gf_node_get_field_by_name(GF_Node *node, char *name, GF_FieldInfo *field) +{ + s32 res = -1; + + if (node->sgprivate->tag==TAG_UndefinedNode) return GF_BAD_PARAM; + else if (node->sgprivate->tag == TAG_ProtoNode) { + res = gf_sg_proto_get_field_index_by_name(NULL, node, name); + } + else if ((node->sgprivate->tag == TAG_MPEG4_Script) || (node->sgprivate->tag == TAG_X3D_Script) ) { + return gf_node_get_field_by_name_enum(node, name, field); + } + else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_MPEG4) res = gf_sg_mpeg4_node_get_field_index_by_name(node, name); + else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) res = gf_sg_x3d_node_get_field_index_by_name(node, name); +#ifndef GPAC_DISABLE_SVG + else if (node->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) return gf_node_get_attribute_by_name(node, name, 0, 1, 0, field); +#endif + if (res==-1) return GF_BAD_PARAM; + return gf_node_get_field(node, (u32) res, field); +} + +static char log_node_name[10]; +const char *gf_node_get_log_name(GF_Node *anim) +{ + const char *name = gf_node_get_name(anim); + if (name) return name; + else { + sprintf(log_node_name, "0x%x", (u32) anim); + return log_node_name; + } +} + + + +static GF_Err gf_node_deactivate_ex(GF_Node *node) +{ +#ifdef GPAC_DISABLE_SVG + return GF_NOT_SUPPORTED; +#else + GF_ChildNodeItem *item; + if (node->sgprivate->tagsgprivate->flags & GF_NODE_IS_DEACTIVATED)) { + + node->sgprivate->flags |= GF_NODE_IS_DEACTIVATED; + + /*deactivate anmiations*/ + if (gf_svg_is_timing_tag(node->sgprivate->tag)) { + SVGTimedAnimBaseElement *timed = (SVGTimedAnimBaseElement*)node; + if (gf_list_del_item(node->sgprivate->scenegraph->smil_timed_elements, timed->timingp->runtime)>=0) { + if (timed->timingp->runtime->evaluate) { + timed->timingp->runtime->evaluate(timed->timingp->runtime, 0, SMIL_TIMING_EVAL_DEACTIVATE); + } + } + } + /*TODO unregister all listeners*/ + + } + /*and deactivate children*/ + item = ((GF_ParentNode*)node)->children; + while (item) { + gf_node_deactivate_ex(item->node); + item = item->next; + } + return GF_OK; +#endif +} + +GF_Err gf_node_deactivate(GF_Node *node) +{ + GF_Err e = gf_node_deactivate_ex(node); + gf_node_changed(node, NULL); + return e; +} + +static u32 gf_node_activate_ex(GF_Node *node) +{ +#ifdef GPAC_DISABLE_SVG + return 0; +#else + u32 ret = 0; + GF_ChildNodeItem *item; + if (node->sgprivate->tagsgprivate->flags & GF_NODE_IS_DEACTIVATED) { + + node->sgprivate->flags &= ~GF_NODE_IS_DEACTIVATED; + ret ++; + + /*deactivate anmiations*/ + if (gf_svg_is_timing_tag(node->sgprivate->tag)) { + SVGTimedAnimBaseElement *timed = (SVGTimedAnimBaseElement*)node; + gf_list_add(node->sgprivate->scenegraph->smil_timed_elements, timed->timingp->runtime); + node->sgprivate->flags &= ~GF_NODE_IS_DEACTIVATED; + if (timed->timingp->runtime->evaluate) { + timed->timingp->runtime->evaluate(timed->timingp->runtime, 0, SMIL_TIMING_EVAL_ACTIVATE); + } + } + /*TODO register all listeners*/ + + } + /*and deactivate children*/ + item = ((GF_ParentNode*)node)->children; + while (item) { + ret += gf_node_activate_ex(item->node); + item = item->next; + } + return ret; +#endif +} + +GF_Err gf_node_activate(GF_Node *node) +{ + if (!node) return GF_BAD_PARAM; + if (gf_node_activate_ex(node)) + gf_node_changed(node, NULL); + return GF_OK; +} + +GF_EXPORT +GF_Node *gf_node_clone(GF_SceneGraph *inScene, GF_Node *orig, GF_Node *cloned_parent, char *id, Bool deep) +{ + if (!orig) return NULL; + if (orig->sgprivate->tag < GF_NODE_RANGE_LAST_VRML) { + /*deep clone is always true for VRML*/ + return gf_vrml_node_clone(inScene, orig, cloned_parent, id); + } else if (orig->sgprivate->tag == TAG_DOMUpdates) { + /*TODO*/ + return NULL; + } else { +#ifndef GPAC_DISABLE_SVG + return gf_xml_node_clone(inScene, orig, cloned_parent, id, deep); +#endif + } + return NULL; +} + + +GF_Err gf_sg_add_namespace(GF_SceneGraph *sg, char *name, char *qname) +{ + u32 id; + GF_XMLNS *ns; + if (!name) return GF_BAD_PARAM; + + id = GF_XMLNS_NONE; + if (!strcmp(name, "http://www.w3.org/XML/1998/namespace")) id = GF_XMLNS_XML; + else if (!strcmp(name, "http://www.w3.org/2001/xml-events")) id = GF_XMLNS_XMLEV; + else if (!strcmp(name, "http://www.w3.org/1999/xlink")) id = GF_XMLNS_XLINK; + else if (!strcmp(name, "http://www.w3.org/2000/svg")) id = GF_XMLNS_SVG; + else if (!strcmp(name, "urn:mpeg:mpeg4:laser:2005")) id = GF_XMLNS_LASER; + else if (!strcmp(name, "http://www.w3.org/ns/xbl")) id = GF_XMLNS_XBL; + + if (!sg->ns) sg->ns = gf_list_new(); + + GF_SAFEALLOC(ns, GF_XMLNS); + ns->xmlns_id = id ? id : gf_crc_32(name, strlen(name)); + ns->name = strdup(name); + + ns->qname = qname ? strdup(qname) : NULL; + return gf_list_insert(sg->ns, ns, 0); +} + +GF_Err gf_sg_remove_namespace(GF_SceneGraph *sg, char *ns_name, char *q_name) +{ + u32 i, count; + count = sg->ns ? gf_list_count(sg->ns) : 0; + for (i=0; ins, i); + if (!q_name && !ns->qname) + ok = 1; + else if (q_name && ns->qname && !strcmp(ns->qname, q_name) ) + ok = 1; + + if (ok && ns->name && !strcmp(ns->name, ns_name)) { + gf_list_rem(sg->ns, i); + free(ns->name); + if (ns->qname) free(ns->qname); + free(ns); + return GF_OK; + } + } + return GF_OK; +} + +u32 gf_sg_get_namespace_code(GF_SceneGraph *sg, char *qname) +{ + GF_XMLNS *ns; + u32 i, count; + count = sg->ns ? gf_list_count(sg->ns) : 0; + for (i=0; ins, i); + if (!ns->qname && !qname) + return ns->xmlns_id; + + if (ns->qname && qname && !strcmp(ns->qname, qname)) + return ns->xmlns_id; + } + if (qname) { + if (!strcmp(qname, "xml")) return GF_XMLNS_XML; + /*we could also add the basic namespaces in case this has been forgotten ?*/ + } + return GF_XMLNS_NONE; +} + +u32 gf_sg_get_namespace_code_from_name(GF_SceneGraph *sg, char *name) +{ + GF_XMLNS *ns; + u32 i, count; + count = sg->ns ? gf_list_count(sg->ns) : 0; + for (i=0; ins, i); + if (ns->name && name && !strcmp(ns->name, name)) + return ns->xmlns_id; + if (!ns->name && !name) + return ns->xmlns_id; + } + return GF_XMLNS_NONE; +} + +const char *gf_sg_get_namespace_qname(GF_SceneGraph *sg, u32 xmlns_id) +{ + GF_XMLNS *ns; + u32 i, count; + count = sg->ns ? gf_list_count(sg->ns) : 0; + for (i=0; ins, i); + if (ns->xmlns_id == xmlns_id) + return ns->qname; + } + if (xmlns_id==GF_XMLNS_XML) return "xml"; + return NULL; +} + + +const char *gf_sg_get_namespace(GF_SceneGraph *sg, u32 xmlns_id) +{ + GF_XMLNS *ns; + u32 i, count; + count = sg->ns ? gf_list_count(sg->ns) : 0; + for (i=0; ins, i); + if (ns->xmlns_id == xmlns_id) + return ns->name; + } + return NULL; +} + + diff --git a/src/scenegraph/commands.c b/src/scenegraph/commands.c new file mode 100644 index 0000000..6c03231 --- /dev/null +++ b/src/scenegraph/commands.c @@ -0,0 +1,869 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +/*MPEG4 tags (for internal nodes)*/ +#include + +#include + +GF_Command *gf_sg_command_new(GF_SceneGraph *graph, u32 tag) +{ + GF_Command *ptr; + GF_SAFEALLOC(ptr, GF_Command); + if (!ptr) return NULL; + ptr->tag = tag; + ptr->in_scene = graph; + ptr->command_fields = gf_list_new(); + if (tag < GF_SG_LAST_BIFS_COMMAND) ptr->new_proto_list = gf_list_new(); + return ptr; +} +static void SG_CheckNodeUnregister(GF_Command *com) +{ + NodeIDedItem *reg_node; + switch (com->tag) { + case GF_SG_SCENE_REPLACE: + case GF_SG_LSR_NEW_SCENE: + case GF_SG_LSR_REFRESH_SCENE: + gf_node_unregister(com->node, NULL); + break; + default: + /*check if node is in registry - do NOT check the node's internals, because it may have been destroyed + when cyclic references are found (only happens with conditional replacing self/other conditional buffer) + note that we're sure the node has an ID since this is not a replace scene*/ + reg_node = com->in_scene->id_node; + while (reg_node) { + if (reg_node->node == com->node) { + gf_node_unregister(com->node, NULL); + return; + } + reg_node = reg_node->next; + } + break; + } +} + +GF_EXPORT +void gf_sg_command_del(GF_Command *com) +{ + u32 i; + GF_Proto *proto; + if (!com) return; + + if (com->tag < GF_SG_LAST_BIFS_COMMAND) { + while (gf_list_count(com->command_fields)) { + GF_CommandField *inf = (GF_CommandField *)gf_list_get(com->command_fields, 0); + gf_list_rem(com->command_fields, 0); + + switch (inf->fieldType) { + case GF_SG_VRML_SFNODE: + if (inf->new_node) gf_node_unregister(inf->new_node, com->node); + break; + case GF_SG_VRML_MFNODE: + if (inf->field_ptr) { + GF_ChildNodeItem *cur, *child; + child = inf->node_list; + while (child) { + cur = child; + gf_node_unregister(child->node, com->node); + child = child->next; + free(cur); + } + } + break; + default: + if (inf->field_ptr) gf_sg_vrml_field_pointer_del(inf->field_ptr, inf->fieldType); + break; + } + free(inf); + } + } else { +#ifndef GPAC_DISABLE_SVG + while (gf_list_count(com->command_fields)) { + GF_CommandField *inf = (GF_CommandField *)gf_list_get(com->command_fields, 0); + gf_list_rem(com->command_fields, 0); + + if (inf->new_node) gf_node_unregister(inf->new_node, com->node); + else if (inf->node_list) { + gf_node_unregister_children(com->node, inf->node_list); + } else if (inf->field_ptr) { + gf_svg_delete_attribute_value(inf->fieldType, inf->field_ptr, com->in_scene); + } + free(inf); + } +#endif + } + gf_list_del(com->command_fields); + + i=0; + while ((proto = (GF_Proto*)gf_list_enum(com->new_proto_list, &i))) { + gf_sg_proto_del(proto); + } + gf_list_del(com->new_proto_list); + + if (com->node) { + if (!com->in_scene) gf_node_unregister(com->node, NULL); + else SG_CheckNodeUnregister(com); + } + + if (com->del_proto_list) free(com->del_proto_list); + if (com->def_name) free(com->def_name); + if (com->scripts_to_load) gf_list_del(com->scripts_to_load); + free(com); +} + +static void SG_CheckFieldChange(GF_Node *node, GF_FieldInfo *field) +{ + /*and propagate eventIn if any*/ + if (field->on_event_in) { + field->on_event_in(node); + } else if ((field->eventType==GF_SG_EVENT_IN) && (gf_node_get_tag(node) == TAG_MPEG4_Script)) { + gf_sg_script_event_in(node, field); + } else { + /*Notify eventOut in all cases to handle protos*/ + gf_node_event_out(node, field->fieldIndex); + } + /*signal node modif*/ + gf_node_changed(node, field); +} + +static void gf_node_unregister_children_deactivate(GF_Node *container, GF_ChildNodeItem *child) +{ + GF_ChildNodeItem *cur; + while (child) { + gf_node_unregister(child->node, container); + gf_node_deactivate(child->node); + cur = child; + child = child->next; + free(cur); + } +} + + +GF_EXPORT +GF_Err gf_sg_command_apply(GF_SceneGraph *graph, GF_Command *com, Double time_offset) +{ + GF_Err e; + GF_CommandField *inf; + GF_FieldInfo field; + GF_Node *def, *node; + void *slot_ptr; + + if (!com || !graph) return GF_BAD_PARAM; + + e = GF_OK; + switch (com->tag) { + case GF_SG_SCENE_REPLACE: + /*unregister root*/ + gf_node_unregister(graph->RootNode, NULL); + /*remove all protos and routes*/ + while (gf_list_count(graph->routes_to_activate)) + gf_list_rem(graph->routes_to_activate, 0); + + if (!com->aggregated) { + /*destroy all routes*/ + while (gf_list_count(graph->Routes)) { + GF_Route *r = (GF_Route *)gf_list_get(graph->Routes, 0); + /*this will unregister the route from the graph, so don't delete the chain entry*/ + gf_sg_route_del(r); + } + /*destroy all proto*/ + while (gf_list_count(graph->protos)) { + GF_Proto *p = (GF_Proto*)gf_list_get(graph->protos, 0); + /*this will unregister the proto from the graph, so don't delete the chain entry*/ + gf_sg_proto_del(p); + } + } + /*DO NOT TOUCH node registry*/ + /*DO NOT TOUCH UNREGISTERED PROTOS*/ + + /*move all protos in graph*/ + while (gf_list_count(com->new_proto_list)) { + GF_Proto *p = (GF_Proto*)gf_list_get(com->new_proto_list, 0); + gf_list_rem(com->new_proto_list, 0); + gf_list_del_item(graph->unregistered_protos, p); + gf_list_add(graph->protos, p); + } + /*assign new root (no need to register/unregister)*/ + graph->RootNode = com->node; + com->node = NULL; + break; + + case GF_SG_NODE_REPLACE: + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); + e = gf_node_replace(com->node, inf->new_node, 0); + if (inf->new_node) gf_node_register(inf->new_node, NULL); + break; + + case GF_SG_FIELD_REPLACE_OP: + { + void *src_ptr; + u32 src_type; + u32 j=0; + GF_FieldInfo src; + def = gf_sg_find_node(com->in_scene, com->fromNodeID); + if (!def) return GF_SG_UNKNOWN_NODE; + e = gf_node_get_field(def, com->fromFieldIndex, &src); + if (e) return e; + + src_type = src.fieldType; + src_ptr = src.far_ptr; + if (com->send_event_x>=0) { + src_type = gf_sg_vrml_get_sf_type(src.fieldType); + e = gf_sg_vrml_mf_get_item(src.far_ptr, src.fieldType, &src_ptr, com->send_event_x); + if (e) return e; + } + + while ((inf = (GF_CommandField*)gf_list_enum(com->command_fields, &j))) { + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + if (inf->pos>=0) { + void *slot_ptr; + e = gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, & slot_ptr, inf->pos); + if (e) return e; + gf_sg_vrml_field_copy(slot_ptr, src_ptr, src_type); + } else { + gf_sg_vrml_field_copy(field.far_ptr, src_ptr, src_type); + } + } + + SG_CheckFieldChange(com->node, &field); + } + break; + + case GF_SG_MULTIPLE_REPLACE: + case GF_SG_FIELD_REPLACE: + { + u32 j; + GF_ChildNodeItem *list, *cur, *prev; + j=0; + while ((inf = (GF_CommandField*)gf_list_enum(com->command_fields, &j))) { + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + + switch (field.fieldType) { + case GF_SG_VRML_SFNODE: + { + node = *((GF_Node **) field.far_ptr); + e = gf_node_unregister(node, com->node); + *((GF_Node **) field.far_ptr) = inf->new_node; + if (!e) gf_node_register(inf->new_node, com->node); + break; + } + case GF_SG_VRML_MFNODE: + gf_node_unregister_children(com->node, * ((GF_ChildNodeItem **) field.far_ptr)); + * ((GF_ChildNodeItem **) field.far_ptr) = NULL; + + list = * ((GF_ChildNodeItem **) inf->field_ptr); + prev=NULL; + while (list) { + cur = malloc(sizeof(GF_ChildNodeItem)); + cur->next = NULL; + cur->node = list->node; + if (prev) { + prev->next = cur; + } else { + * ((GF_ChildNodeItem **) field.far_ptr) = cur; + } + gf_node_register(list->node, com->node); + prev = cur; + list = list->next; + } + break; + case GF_SG_VRML_SFCOMMANDBUFFER: + { + u32 i, count; + GF_SceneGraph *sg; + SFCommandBuffer *cb_dst = (SFCommandBuffer *)field.far_ptr; + SFCommandBuffer *cb_src = (SFCommandBuffer *)inf->field_ptr; + + /*reset dest*/ + while (gf_list_count(cb_dst->commandList)) { + GF_Command *sub_com = (GF_Command *)gf_list_get(cb_dst->commandList, 0); + gf_sg_command_del(sub_com); + gf_list_rem(cb_dst->commandList, 0); + } + if (cb_dst->buffer) { + free(cb_dst->buffer); + cb_dst->buffer = NULL; + } + + /*clone command list*/ + sg = gf_node_get_graph(com->node); + count = gf_list_count(cb_src->commandList); + for (i=0; icommandList, i); + GF_Command *new_com = gf_sg_command_clone(sub_com, sg, 0); + gf_list_add(cb_dst->commandList, new_com); + } + } + break; + + default: + /*this is a regular field, reset it and clone - we cannot switch pointers since the + original fields are NOT pointers*/ + if (!gf_sg_vrml_is_sf_field(field.fieldType)) { + e = gf_sg_vrml_mf_reset(field.far_ptr, field.fieldType); + } + if (e) return e; + gf_sg_vrml_field_copy(field.far_ptr, inf->field_ptr, field.fieldType); + + if ((field.fieldType==GF_SG_VRML_SFTIME) && !strstr(field.name, "media")) + *(SFTime *)field.far_ptr = *(SFTime *)field.far_ptr + time_offset; + break; + } + SG_CheckFieldChange(com->node, &field); + } + break; + } + + case GF_SG_MULTIPLE_INDEXED_REPLACE: + case GF_SG_INDEXED_REPLACE: + { + u32 sftype, i=0; + while ((inf = (GF_CommandField*)gf_list_enum(com->command_fields, &i))) { + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + + /*if MFNode remove the child and set new node*/ + if (field.fieldType == GF_SG_VRML_MFNODE) { + /*we must remove the node before in case the new node uses the same ID (not forbidden) and this + command removes the last instance of the node with the same ID*/ + gf_node_replace_child(com->node, (GF_ChildNodeItem**) field.far_ptr, inf->pos, inf->new_node); + if (inf->new_node) gf_node_register(inf->new_node, NULL); + } + /*erase the field item*/ + else { + if ((inf->pos < 0) || ((u32) inf->pos >= ((GenMFField *) field.far_ptr)->count) ) { + inf->pos = ((GenMFField *)field.far_ptr)->count - 1; + /*may happen with text and default value*/ + if (inf->pos < 0) { + inf->pos = 0; + gf_sg_vrml_mf_alloc(field.far_ptr, field.fieldType, 1); + } + } + e = gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, & slot_ptr, inf->pos); + if (e) return e; + sftype = gf_sg_vrml_get_sf_type(field.fieldType); + gf_sg_vrml_field_copy(slot_ptr, inf->field_ptr, sftype); + /*note we don't add time offset, since there's no MFTime*/ + } + SG_CheckFieldChange(com->node, &field); + } + break; + } + case GF_SG_ROUTE_REPLACE: + { + GF_Route *r; + char *name; + r = gf_sg_route_find(graph, com->RouteID); + def = gf_sg_find_node(graph, com->fromNodeID); + node = gf_sg_find_node(graph, com->toNodeID); + if (!node || !def) return GF_SG_UNKNOWN_NODE; + name = NULL; + if (r) { + name = r->name; + r->name = NULL; + gf_sg_route_del(r); + } + r = gf_sg_route_new(graph, def, com->fromFieldIndex, node, com->toFieldIndex); + gf_sg_route_set_id(r, com->RouteID); + if (name) { + gf_sg_route_set_name(r, name); + free(name); + } + break; + } + case GF_SG_NODE_DELETE_EX: + case GF_SG_NODE_DELETE: + { + if (com->node) gf_node_replace(com->node, NULL, (com->tag==GF_SG_NODE_DELETE_EX) ? 1 : 0); + break; + } + case GF_SG_ROUTE_DELETE: + { + return gf_sg_route_del_by_id(graph, com->RouteID); + } + case GF_SG_INDEXED_DELETE: + { + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); + + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; + + /*then we need special handling in case of a node*/ + if (gf_sg_vrml_get_sf_type(field.fieldType) == GF_SG_VRML_SFNODE) { + e = gf_node_replace_child(com->node, (GF_ChildNodeItem **) field.far_ptr, inf->pos, NULL); + } else { + if ((inf->pos < 0) || ((u32) inf->pos >= ((GenMFField *) field.far_ptr)->count) ) { + inf->pos = ((GenMFField *)field.far_ptr)->count - 1; + } + /*this is a regular MFField, just remove the item (realloc)*/ + e = gf_sg_vrml_mf_remove(field.far_ptr, field.fieldType, inf->pos); + } + /*deletion -> node has changed*/ + if (!e) SG_CheckFieldChange(com->node, &field); + break; + } + case GF_SG_NODE_INSERT: + { + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); + + e = gf_node_insert_child(com->node, inf->new_node, inf->pos); + if (!e) gf_node_register(inf->new_node, com->node); + if (!e) gf_node_event_out(com->node, inf->fieldIndex); + if (!e) gf_node_changed(com->node, NULL); + break; + } + case GF_SG_ROUTE_INSERT: + { + GF_Route *r; + def = gf_sg_find_node(graph, com->fromNodeID); + node = gf_sg_find_node(graph, com->toNodeID); + if (!node || !def) return GF_SG_UNKNOWN_NODE; + r = gf_sg_route_new(graph, def, com->fromFieldIndex, node, com->toFieldIndex); + if (com->RouteID) gf_sg_route_set_id(r, com->RouteID); + if (com->def_name) { + gf_sg_route_set_name(r, com->def_name); + free(com->def_name); + com->def_name = NULL; + } + break; + } + case GF_SG_INDEXED_INSERT: + { + u32 sftype; + if (!gf_list_count(com->command_fields)) return GF_OK; + inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); + e = gf_node_get_field(com->node, inf->fieldIndex, &field); + if (e) return e; + + /*rescale the MFField and parse the SFField*/ + if (field.fieldType != GF_SG_VRML_MFNODE) { + if (inf->pos == -1) { + e = gf_sg_vrml_mf_append(field.far_ptr, field.fieldType, & slot_ptr); + } else { + e = gf_sg_vrml_mf_insert(field.far_ptr, field.fieldType, & slot_ptr, inf->pos); + } + if (e) return e; + sftype = gf_sg_vrml_get_sf_type(field.fieldType); + gf_sg_vrml_field_copy(slot_ptr, inf->field_ptr, sftype); + } else { + if (inf->new_node) { + if (inf->pos == -1) { + gf_node_list_add_child( (GF_ChildNodeItem **) field.far_ptr, inf->new_node); + } else { + gf_node_list_insert_child((GF_ChildNodeItem **) field.far_ptr, inf->new_node, inf->pos); + } + gf_node_register(inf->new_node, com->node); + } + } + if (!e) SG_CheckFieldChange(com->node, &field); + break; + } + case GF_SG_PROTO_INSERT: + /*destroy all proto*/ + while (gf_list_count(com->new_proto_list)) { + GF_Proto *p = (GF_Proto*)gf_list_get(com->new_proto_list, 0); + gf_list_rem(com->new_proto_list, 0); + gf_list_del_item(graph->unregistered_protos, p); + gf_list_add(graph->protos, p); + } + return GF_OK; + case GF_SG_PROTO_DELETE: + { + u32 i; + for (i=0; idel_proto_list_size; i++) { + /*note this will check for unregistered protos, but since IDs are unique we are sure we will + not destroy an unregistered one*/ + GF_Proto *proto = gf_sg_find_proto(graph, com->del_proto_list[i], NULL); + if (proto) gf_sg_proto_del(proto); + } + } + return GF_OK; + case GF_SG_PROTO_DELETE_ALL: + /*destroy all proto*/ + while (gf_list_count(graph->protos)) { + GF_Proto *p = (GF_Proto*)gf_list_get(graph->protos, 0); + gf_list_rem(graph->protos, 0); + /*this will unregister the proto from the graph, so don't delete the chain entry*/ + gf_sg_proto_del(p); + } + /*DO NOT TOUCH UNREGISTERED PROTOS*/ + return GF_OK; + /*only used by BIFS*/ + case GF_SG_GLOBAL_QUANTIZER: + return GF_OK; + +#ifndef GPAC_DISABLE_SVG + /*laser commands*/ + case GF_SG_LSR_NEW_SCENE: + /*DO NOT TOUCH node registry*/ + + /*assign new root (no need to register/unregister)*/ + graph->RootNode = com->node; + com->node = NULL; + break; + case GF_SG_LSR_DELETE: + if (!com->node) return GF_NON_COMPLIANT_BITSTREAM; + if (!gf_list_count(com->command_fields)) { + gf_node_replace(com->node, NULL, 0); + gf_node_deactivate(com->node); + return GF_OK; + } + inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); + node = gf_node_list_get_child(((SVG_Element *)com->node)->children, inf->pos); + if (node) { + e = gf_node_replace_child(com->node, &((SVG_Element *)com->node)->children, inf->pos, NULL); + gf_node_deactivate(node); + } + break; + case GF_SG_LSR_INSERT: + inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); + if (!com->node || !inf) return GF_NON_COMPLIANT_BITSTREAM; + if (inf->new_node) { + if (inf->pos<0) + gf_node_list_add_child(& ((SVG_Element *)com->node)->children, inf->new_node); + else + gf_node_list_insert_child(& ((SVG_Element *)com->node)->children, inf->new_node, inf->pos); + + gf_node_register(inf->new_node, com->node); + gf_node_activate(inf->new_node); + gf_node_changed(com->node, NULL); + } else { + /*NOT SUPPORTED*/ + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[LASeR] VALUE INSERTION NOT SUPPORTED\n")); + } + break; + case GF_SG_LSR_ADD: + case GF_SG_LSR_REPLACE: + inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); + if (!com->node || !inf) return GF_NON_COMPLIANT_BITSTREAM; + if (inf->new_node) { + if (inf->pos<0) { + /*if fieldIndex (eg attributeName) is set, this is children replacement*/ + if (inf->fieldIndex>0) { + gf_node_unregister_children_deactivate(com->node, ((SVG_Element *)com->node)->children); + ((SVG_Element *)com->node)->children = NULL; + gf_node_list_add_child(& ((SVG_Element *)com->node)->children, inf->new_node); + gf_node_register(inf->new_node, com->node); + gf_node_activate(inf->new_node); + } else { + e = gf_node_replace(com->node, inf->new_node, 0); + gf_node_activate(inf->new_node); + } + } else { + node = gf_node_list_get_child( ((SVG_Element *)com->node)->children, inf->pos); + gf_node_replace_child(com->node, & ((SVG_Element *)com->node)->children, inf->pos, inf->new_node); + gf_node_register(inf->new_node, com->node); + if (node) gf_node_deactivate(node); + gf_node_activate(inf->new_node); + } + /*signal node modif*/ + gf_node_changed(com->node, NULL); + return e; + } else if (inf->node_list) { + GF_ChildNodeItem *child, *cur, *prev; + gf_node_unregister_children_deactivate(com->node, ((SVG_Element *)com->node)->children); + ((SVG_Element *)com->node)->children = NULL; + + prev = NULL; + child = inf->node_list; + while (child) { + cur = (GF_ChildNodeItem*)malloc(sizeof(GF_ChildNodeItem)); + cur->next = NULL; + cur->node = child->node; + gf_node_register(child->node, com->node); + gf_node_activate(child->node); + if (prev) prev->next = cur; + else ((SVG_Element *)com->node)->children = cur; + prev = cur; + child = child->next; + } + /*signal node modif*/ + gf_node_changed(com->node, NULL); + return GF_OK; + } + /*attribute modif*/ + else if (inf->field_ptr) { + GF_FieldInfo a, b; + if (inf->fieldIndex==(u32) -2) { + GF_Point2D scale, translate; + Fixed rotate; + GF_Matrix2D *dest; + gf_node_get_field_by_name(com->node, "transform", &a); + dest = (GF_Matrix2D*)a.far_ptr; + + if (com->tag==GF_SG_LSR_REPLACE) { + if (gf_mx2d_decompose(dest, &scale, &rotate, &translate)) { + gf_mx2d_init(*dest); + if (inf->fieldType==SVG_TRANSFORM_SCALE) scale = *(GF_Point2D *)inf->field_ptr; + else if (inf->fieldType==SVG_TRANSFORM_TRANSLATE) translate = *(GF_Point2D *)inf->field_ptr; + else if (inf->fieldType==SVG_TRANSFORM_ROTATE) rotate = ((SVG_Point_Angle*)inf->field_ptr)->angle; + + gf_mx2d_add_scale(dest, scale.x, scale.y); + gf_mx2d_add_rotation(dest, 0, 0, rotate); + gf_mx2d_add_translation(dest, translate.x, translate.y); + } + } else { + GF_Point2D *pt = (GF_Point2D *)inf->field_ptr; + if (inf->fieldType==SVG_TRANSFORM_SCALE) gf_mx2d_add_scale(dest, pt->x, pt->y); + else if (inf->fieldType==SVG_TRANSFORM_TRANSLATE) gf_mx2d_add_translation(dest, pt->x, pt->y); + else if (inf->fieldType == SVG_TRANSFORM_ROTATE) gf_mx2d_add_rotation(dest, 0, 0, ((SVG_Point_Angle*)inf->field_ptr)->angle); + } + } else { + if ((inf->fieldIndex==(u32) -1) && (inf->fieldType==DOM_String_datatype)) { + char *str = *(SVG_String*)inf->field_ptr; + + if (com->tag == GF_SG_LSR_REPLACE) { + GF_DOMText *t = ((SVG_Element*)com->node)->children ? (GF_DOMText*) ((SVG_Element*)com->node)->children->node :NULL; + if (t && (t->sgprivate->tag==TAG_DOMText)) { + if (t->textContent) free(t->textContent); + t->textContent = NULL; + if (str) t->textContent = strdup(str); + } + } else { + if (str) gf_dom_add_text_node(com->node, strdup(str)); + } + } + else if ((inf->fieldIndex==TAG_LSR_ATT_scale) + || (inf->fieldIndex==TAG_LSR_ATT_translation) + || (inf->fieldIndex==TAG_LSR_ATT_rotation) + ) { + SVG_Transform *mx; + gf_node_get_attribute_by_tag(com->node, TAG_SVG_ATT_transform, 1, 0, &a); + mx = a.far_ptr; + if (com->tag == GF_SG_LSR_REPLACE) { + GF_Point2D scale, translate; + SVG_Point_Angle rotate; + if (gf_mx2d_decompose(&mx->mat, &scale, &rotate.angle, &translate)) { + gf_mx2d_init(mx->mat); + if (inf->fieldIndex==TAG_LSR_ATT_scale) scale = *(GF_Point2D *)inf->field_ptr; + else if (inf->fieldIndex==TAG_LSR_ATT_translation) translate = *(GF_Point2D *)inf->field_ptr; + else if (inf->fieldIndex==TAG_LSR_ATT_rotation) rotate = *(SVG_Point_Angle*)inf->field_ptr; + + gf_mx2d_add_scale(&mx->mat, scale.x, scale.y); + gf_mx2d_add_rotation(&mx->mat, 0, 0, rotate.angle); + gf_mx2d_add_translation(&mx->mat, translate.x, translate.y); + } + } else { + if (inf->fieldIndex==TAG_LSR_ATT_scale) gf_mx2d_add_scale(&mx->mat, ((GF_Point2D*)inf->field_ptr)->x, ((GF_Point2D*)inf->field_ptr)->y); + if (inf->fieldIndex==TAG_LSR_ATT_translation) gf_mx2d_add_translation(&mx->mat, ((GF_Point2D*)inf->field_ptr)->x, ((GF_Point2D*)inf->field_ptr)->y); + if (inf->fieldIndex==TAG_LSR_ATT_rotation) gf_mx2d_add_rotation(&mx->mat, 0, 0, ((SVG_Point_Angle*)inf->field_ptr)->angle); + } + } + else if (gf_node_get_attribute_by_tag(com->node, inf->fieldIndex, 1, 0, &a) == GF_OK) { + b = a; + b.far_ptr = inf->field_ptr; + if (com->tag == GF_SG_LSR_REPLACE) { + gf_svg_attributes_copy(&a, &b, 0); + } else { + gf_svg_attributes_add(&a, &b, &a, 0); + } + } + b = a; + b.far_ptr = inf->field_ptr; + } + /*signal node modif*/ + gf_node_changed(com->node, &a); + } else if (com->fromNodeID) { + GF_FieldInfo a, b; + GF_Node *fromNode = gf_sg_find_node(graph, com->fromNodeID); + if (!fromNode) return GF_NON_COMPLIANT_BITSTREAM; + if (gf_node_get_field(fromNode, com->fromFieldIndex, &b) != GF_OK) return GF_NON_COMPLIANT_BITSTREAM; + + if ((inf->fieldIndex==(u32) -1) && (inf->fieldType==DOM_String_datatype)) { + char *str = *(SVG_String*)inf->field_ptr; + + if (com->tag == GF_SG_LSR_REPLACE) { + GF_DOMText *t = ((SVG_Element*)com->node)->children ? (GF_DOMText*) ((SVG_Element*)com->node)->children->node :NULL; + if (t && (t->sgprivate->tag==TAG_DOMText)) { + if (t->textContent) free(t->textContent); + t->textContent = NULL; + if (str) t->textContent = strdup(str); + } + } else { + if (str) gf_dom_add_text_node(com->node, strdup(str)); + } + } else { + gf_node_get_field(com->node, inf->fieldIndex, &a); + if (com->tag == GF_SG_LSR_REPLACE) { + e = gf_svg_attributes_copy(&a, &b, 0); + } else { + e = gf_svg_attributes_add(&a, &b, &a, 0); + } + } + gf_node_changed(com->node, &a); + return e; + } else { + return GF_NON_COMPLIANT_BITSTREAM; + } + break; + case GF_SG_LSR_ACTIVATE: + gf_node_activate(com->node); + break; + case GF_SG_LSR_DEACTIVATE: + gf_node_deactivate(com->node); + gf_node_changed(com->node, NULL); + break; + case GF_SG_LSR_SEND_EVENT: + { + GF_DOM_Event evt; + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = com->send_event_name; + evt.detail = com->send_event_integer; + evt.clientX = com->send_event_x; + evt.clientY = com->send_event_y; + gf_dom_event_fire(com->node, &evt); + } + break; +#endif + + default: + return GF_NOT_SUPPORTED; + } + if (e) return e; + + if (com->scripts_to_load) { + while (gf_list_count(com->scripts_to_load)) { + GF_Node *script = (GF_Node *)gf_list_get(com->scripts_to_load, 0); + gf_list_rem(com->scripts_to_load, 0); + gf_sg_script_load(script); + } + gf_list_del(com->scripts_to_load); + com->scripts_to_load = NULL; + } + return GF_OK; +} + +GF_CommandField *gf_sg_command_field_new(GF_Command *com) +{ + GF_CommandField *ptr; + GF_SAFEALLOC(ptr, GF_CommandField); + gf_list_add(com->command_fields, ptr); + return ptr; +} + + +GF_EXPORT +GF_Err gf_sg_command_apply_list(GF_SceneGraph *graph, GF_List *comList, Double time_offset) +{ + GF_Err e; + GF_Command *com; + u32 i=0; + while ((com = (GF_Command *)gf_list_enum(comList, &i))) { + e = gf_sg_command_apply(graph, com, time_offset); + if (e) return e; + } + return GF_OK; +} + +GF_Command *gf_sg_command_clone(GF_Command *com, GF_SceneGraph *inGraph, Bool force_clone) +{ + u32 i, count; + GF_Command *dest; + + if (com->tag==GF_SG_SCENE_REPLACE) return NULL; + /*FIXME - to do*/ + if (gf_list_count(com->new_proto_list)) return NULL; + dest = gf_sg_command_new(inGraph, com->tag); + + if (com->in_scene!=inGraph) force_clone = 1; + + /*node the command applies to - may be NULL*/ + if (force_clone) { + dest->node = gf_node_clone(inGraph, com->node, NULL, "", 0); + } else { + dest->node = com->node; + gf_node_register(dest->node, NULL); + } + /*route insert, replace and delete*/ + dest->RouteID = com->RouteID; + if (com->def_name) dest->def_name = strdup(com->def_name); + dest->fromNodeID = com->fromNodeID; + dest->fromFieldIndex = com->fromFieldIndex; + dest->toNodeID = com->toNodeID; + dest->toFieldIndex = com->toFieldIndex; + dest->send_event_integer = com->send_event_integer; + dest->send_event_x = com->send_event_x; + dest->send_event_y = com->send_event_y; + if (com->send_event_string) + dest->send_event_string = strdup(com->send_event_string); + + dest->del_proto_list_size = com->del_proto_list_size; + if (com->del_proto_list_size) { + dest->del_proto_list = (u32*)malloc(sizeof(u32) * com->del_proto_list_size); + memcpy(dest->del_proto_list, com->del_proto_list, sizeof(u32) * com->del_proto_list_size); + } + count = gf_list_count(com->command_fields); + for (i=0; icommand_fields, i); + GF_CommandField *fd = (GF_CommandField *)gf_sg_command_field_new(dest); + + fd->fieldIndex = fo->fieldIndex; + fd->fieldType = fo->fieldType; + fd->pos = fo->pos; + if (fo->field_ptr) { + fd->field_ptr = gf_sg_vrml_field_pointer_new(fd->fieldType); + gf_sg_vrml_field_copy(fd->field_ptr, fo->field_ptr, fo->fieldType); + } + + if (fo->new_node) { + if (force_clone) { + fd->new_node = gf_node_clone(inGraph, fo->new_node, dest->node, "", 0); + } else { + fd->new_node = fo->new_node; + gf_node_register(fd->new_node, NULL); + } + fd->field_ptr = &fd->new_node; + } + if (fo->node_list) { + GF_ChildNodeItem *child, *cur, *prev; + prev = NULL; + child = fo->node_list; + while (child) { + cur = (GF_ChildNodeItem*) malloc(sizeof(GF_ChildNodeItem)); + if (force_clone) { + cur->node = gf_node_clone(inGraph, child->node, dest->node, "", 0); + } else { + cur->node = child->node; + gf_node_register(cur->node, NULL); + } + cur->next = NULL; + if (prev) prev->next = cur; + else fd->node_list = cur; + prev = cur; + child = child->next; + } + fd->field_ptr = &fd->node_list; + } + } + return dest; +} + diff --git a/src/scenegraph/dom_events.c b/src/scenegraph/dom_events.c new file mode 100644 index 0000000..d24bcc7 --- /dev/null +++ b/src/scenegraph/dom_events.c @@ -0,0 +1,731 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004 + * All rights reserved + * + * This file is part of GPAC / DOM 3 Events sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include +#include + +#ifndef GPAC_DISABLE_SVG + +#include +#include +#include + +static void gf_smil_handle_event(GF_Node *anim, GF_FieldInfo *info, GF_DOM_Event *evt, Bool is_end); + + +static void gf_dom_refresh_event_filter(GF_SceneGraph *sg) +{ + GF_SceneGraph *par; + u32 prev_flags = sg->dom_evt_filter; + + sg->dom_evt_filter = 0; + if (sg->nb_evts_mouse) sg->dom_evt_filter |= GF_DOM_EVENT_MOUSE; + if (sg->nb_evts_focus) sg->dom_evt_filter |= GF_DOM_EVENT_FOCUS; + if (sg->nb_evts_key) sg->dom_evt_filter |= GF_DOM_EVENT_KEY; + if (sg->nb_evts_ui) sg->dom_evt_filter |= GF_DOM_EVENT_UI; + if (sg->nb_evts_mutation) sg->dom_evt_filter |= GF_DOM_EVENT_MUTATION; + if (sg->nb_evts_text) sg->dom_evt_filter |= GF_DOM_EVENT_TEXT; + if (sg->nb_evts_smil) sg->dom_evt_filter |= GF_DOM_EVENT_SMIL; + if (sg->nb_evts_laser) sg->dom_evt_filter |= GF_DOM_EVENT_LASER; + if (sg->nb_evts_svg) sg->dom_evt_filter |= GF_DOM_EVENT_SVG; + if (sg->nb_evts_mae) sg->dom_evt_filter |= GF_DOM_EVENT_MEDIA_ACCESS; + + /*for each graph until top, update event filter*/ + par = sg->parent_scene; + while (par) { + par->dom_evt_filter &= ~prev_flags; + par->dom_evt_filter |= sg->dom_evt_filter; + par = par->parent_scene; + } +} + +void gf_sg_unregister_event_type(GF_SceneGraph *sg, u32 type) +{ + if (sg->nb_evts_mouse && (type & GF_DOM_EVENT_MOUSE)) sg->nb_evts_mouse--; + if (sg->nb_evts_focus && (type & GF_DOM_EVENT_FOCUS)) sg->nb_evts_focus--; + if (sg->nb_evts_key && (type & GF_DOM_EVENT_KEY)) sg->nb_evts_key--; + if (sg->nb_evts_ui && (type & GF_DOM_EVENT_UI)) sg->nb_evts_ui--; + if (sg->nb_evts_mutation && (type & GF_DOM_EVENT_MUTATION)) sg->nb_evts_mutation--; + if (sg->nb_evts_text && (type & GF_DOM_EVENT_TEXT)) sg->nb_evts_text--; + if (sg->nb_evts_smil && (type & GF_DOM_EVENT_SMIL)) sg->nb_evts_smil--; + if (sg->nb_evts_laser && (type & GF_DOM_EVENT_LASER)) sg->nb_evts_laser--; + if (sg->nb_evts_text && (type & GF_DOM_EVENT_TEXT)) sg->nb_evts_text--; + if (sg->nb_evts_svg && (type & GF_DOM_EVENT_SVG)) sg->nb_evts_svg--; + if (sg->nb_evts_mae && (type & GF_DOM_EVENT_MEDIA_ACCESS)) sg->nb_evts_mae--; + + gf_dom_refresh_event_filter(sg); +} + + +void gf_sg_register_event_type(GF_SceneGraph *sg, u32 type) +{ + if (type & GF_DOM_EVENT_MOUSE) sg->nb_evts_mouse++; + if (type & GF_DOM_EVENT_FOCUS) sg->nb_evts_focus++; + if (type & GF_DOM_EVENT_KEY) sg->nb_evts_key++; + if (type & GF_DOM_EVENT_UI) sg->nb_evts_ui++; + if (type & GF_DOM_EVENT_MUTATION) sg->nb_evts_mutation++; + if (type & GF_DOM_EVENT_TEXT) sg->nb_evts_text++; + if (type & GF_DOM_EVENT_SMIL) sg->nb_evts_smil++; + if (type & GF_DOM_EVENT_LASER) sg->nb_evts_laser++; + if (type & GF_DOM_EVENT_SVG) sg->nb_evts_svg++; + if (type & GF_DOM_EVENT_MEDIA_ACCESS) sg->nb_evts_mae++; + + gf_dom_refresh_event_filter(sg); +} + +u32 gf_sg_get_dom_event_filter(GF_SceneGraph *sg) +{ + return sg->dom_evt_filter; +} +u32 gf_node_get_dom_event_filter(GF_Node *node) +{ + return node->sgprivate->scenegraph->dom_evt_filter; +} + +GF_Err gf_dom_listener_add(GF_Node *listener, GF_DOMEventTarget *evt_target) +{ + GF_FieldInfo info; + if (!evt_target || !listener) return GF_BAD_PARAM; + if (listener->sgprivate->tag!=TAG_SVG_listener) + return GF_BAD_PARAM; + + + /*only one observer per listener*/ + if (listener->sgprivate->UserPrivate!=NULL) return GF_NOT_SUPPORTED; + listener->sgprivate->UserPrivate = evt_target; + + /*register with NULL parent*/ + gf_node_register((GF_Node *)listener, NULL); + + if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_event, 0, 0, &info) == GF_OK) { + u32 type = ((XMLEV_Event *)info.far_ptr)->type; + gf_sg_register_event_type(listener->sgprivate->scenegraph, gf_dom_event_get_category(type)); + } + + return gf_list_add(evt_target->evt_list, listener); +} + +GF_Err gf_node_dom_listener_add(GF_Node *node, GF_Node *listener) +{ + if (!node || !listener) return GF_BAD_PARAM; + if (listener->sgprivate->tag!=TAG_SVG_listener) + return GF_BAD_PARAM; + + if (!node->sgprivate->interact) + GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); + + if (!node->sgprivate->interact->dom_evt) { + GF_SAFEALLOC(node->sgprivate->interact->dom_evt, GF_DOMEventTarget); + node->sgprivate->interact->dom_evt->ptr = node; + node->sgprivate->interact->dom_evt->ptr_type = GF_DOM_EVENT_NODE; + node->sgprivate->interact->dom_evt->evt_list = gf_list_new(); + } + return gf_dom_listener_add(listener, node->sgprivate->interact->dom_evt); +} + +GF_Err gf_dom_listener_del(GF_Node *listener, GF_DOMEventTarget *target) +{ + GF_FieldInfo info; + if (!listener || !target) return GF_BAD_PARAM; + + if (gf_list_del_item(target->evt_list, listener)<0) return GF_BAD_PARAM; + + if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_event, 0, 0, &info) == GF_OK) { + u32 type = ((XMLEV_Event *)info.far_ptr)->type; + gf_sg_unregister_event_type(listener->sgprivate->scenegraph, gf_dom_event_get_category(type)); + } + listener->sgprivate->UserPrivate = NULL; + gf_node_unregister((GF_Node *)listener, NULL); + return GF_OK; +} + +GF_EXPORT +u32 gf_dom_listener_count(GF_Node *node) +{ + if (!node || !node->sgprivate->interact || !node->sgprivate->interact->dom_evt) return 0; + return gf_list_count(node->sgprivate->interact->dom_evt->evt_list); +} + +GF_EXPORT +GF_Node *gf_dom_listener_get(GF_Node *node, u32 i) +{ + if (!node || !node->sgprivate->interact || !node->sgprivate->interact->dom_evt) return 0; + return (GF_Node *)gf_list_get(node->sgprivate->interact->dom_evt->evt_list, i); +} + +typedef struct +{ + GF_Node *obs; + GF_Node *listener; +} DOMAddListener; + +void gf_dom_listener_post_add(GF_Node *obs, GF_Node *listener) +{ + DOMAddListener *l; + l = (DOMAddListener*)malloc(sizeof(DOMAddListener)); + l->listener = listener; + l->obs = obs; + gf_list_add(obs->sgprivate->scenegraph->listeners_to_add, l); +} + +void gf_dom_listener_process_add(GF_SceneGraph *sg) +{ + u32 i, count; + count = gf_list_count(sg->listeners_to_add); + for (i=0; ilisteners_to_add, i); + gf_node_dom_listener_add(l->obs, l->listener); + free(l); + } + gf_list_reset(sg->listeners_to_add); +} + +void gf_sg_handle_dom_event(GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer) +{ +#ifdef GPAC_HAS_SPIDERMONKEY + if (hdl->sgprivate->scenegraph->svg_js) + if (hdl->sgprivate->scenegraph->svg_js->handler_execute(hdl, event, observer)) return; +#endif + /*no clue what this is*/ + GF_LOG(GF_LOG_WARNING, GF_LOG_INTERACT, ("[DOM Events ] Unknown event handler\n")); +} + +static GF_Node *dom_evt_get_handler(GF_Node *n) +{ + XMLRI *iri; + GF_FieldInfo info; + + if (!n || (n->sgprivate->tag!=TAG_SVG_handler)) return n; + + if (!n || (gf_node_get_attribute_by_tag(n, TAG_XLINK_ATT_href, 0, 0, &info) != GF_OK)) { + return n; + } + iri = (XMLRI *)info.far_ptr; + if (!iri->target && iri->string) { + iri->target = gf_sg_find_node_by_name(n->sgprivate->scenegraph, iri->string+1); + } + return dom_evt_get_handler(iri->target); +} + +static void dom_event_process(GF_Node *listen, GF_DOM_Event *event, GF_Node *observer) +{ + GF_Node *hdl_node; + + switch (listen->sgprivate->tag) { + case TAG_SVG_listener: + { + GF_FieldInfo info; + if (gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_handler, 0, 0, &info) == GF_OK) { + XMLRI *iri = (XMLRI *)info.far_ptr; + if (!iri->target && iri->string) { + iri->target = gf_sg_find_node_by_name(listen->sgprivate->scenegraph, iri->string+1); + } + hdl_node = dom_evt_get_handler(iri->target); + } else { + return; + } + } + break; + default: + return; + } + if (!hdl_node) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[DOM Events ] Time %f - Processing event type: %s\n", gf_node_get_scene_time((GF_Node *)listen), gf_dom_event_get_name(event->type))); + + switch (hdl_node->sgprivate->tag) { + case TAG_SVG_handler: + { + //GF_FieldInfo info; + SVG_handlerElement *handler = (SVG_handlerElement *) hdl_node; + if (!handler->handle_event) return; + + /*spec is not clear regarding event filtering*/ +#if 0 + if (gf_node_get_attribute_by_tag(hdl_node, TAG_XMLEV_ATT_event, 0, 0, &info) == GF_OK) { + XMLEV_Event *ev_event = (XMLEV_Event *)info.far_ptr; + if (ev_event->type != event->type) return; + handler->handle_event(hdl_node, event, observer); + } else { + handler->handle_event(hdl_node, event, observer); + } +#else + handler->handle_event(hdl_node, event, observer); +#endif + } + break; + case TAG_LSR_conditional: + if ( ((SVG_Element*)hdl_node)->children) + gf_node_traverse(((SVG_Element*)hdl_node)->children->node, NULL); + break; + case TAG_SVG_a: + { + GF_DOM_Event act; + memset(&act, 0, sizeof(GF_DOM_Event)); + act.type = GF_EVENT_ACTIVATE; + gf_dom_event_fire((GF_Node *)hdl_node, &act); + } + break; + default: + return; + } +} + +static Bool sg_fire_dom_event(GF_DOMEventTarget *et, GF_DOM_Event *event, GF_SceneGraph *sg, GF_Node *n) +{ + if (et) { + GF_Node *observer = (et->ptr_type==GF_DOM_EVENT_NODE) ? et->ptr : NULL; + u32 i, count, post_count; + count = gf_list_count(et->evt_list); + for (i=0; ievt_list, i); + + switch (listen->sgprivate->tag) { + case TAG_SVG_listener: + { + SVGAllAttributes atts; + gf_svg_flatten_attributes((SVG_Element*)listen, &atts); + listened_event = atts.event; + if (!listened_event) continue; + + if (atts.propagate && (*atts.propagate==XMLEVENT_PROPAGATE_STOP)) + event->event_phase |= GF_DOM_EVENT_PHASE_CANCEL; + if (atts.defaultAction && (*atts.defaultAction==XMLEVENT_DEFAULTACTION_CANCEL)) + event->event_phase |= GF_DOM_EVENT_PHASE_PREVENT; + } + break; + default: + continue; + } + if (listened_event->type <= GF_EVENT_MOUSEMOVE) event->has_ui_events=1; + if (listened_event->type != event->type) continue; + if (listened_event->parameter && (listened_event->parameter != event->detail)) continue; + event->currentTarget = et; + event->consumed ++; + + /*load event cannot bubble and can only be called once (on load :) ), remove it + to release some resources*/ + if (event->type==GF_EVENT_LOAD) { + dom_event_process(listen, event, observer); + /*delete listener*/ + gf_dom_listener_del(listen, et); + } else if (n) { + assert(n->sgprivate->num_instances); + /*protect node*/ + n->sgprivate->num_instances++; + /*exec event*/ + dom_event_process(listen, event, observer); + /*the event has destroyed ourselves, abort propagation + THIS IS NOT DOM compliant, the event should propagate on the original target+ancestors path*/ + if (n->sgprivate->num_instances==1) { + /*unprotect node event*/ + gf_node_unregister(n, NULL); + return 0; + } + n->sgprivate->num_instances--; + } else { + dom_event_process(listen, event, observer); + } + /*canceled*/ + if (event->event_phase & GF_DOM_EVENT_PHASE_CANCEL_ALL) { + gf_dom_listener_process_add(sg); + return 0; + } + + /*if listeners have been removed, update count*/ + post_count = gf_list_count(et->evt_list); + if (post_count < count) { + s32 pos = gf_list_find(et->evt_list, listen); + if (pos>=0) i = pos; + /*FIXME this is not going to work in all cases...*/ + else i--; + count = post_count; + } + } + /*propagation stoped*/ + if (event->event_phase & (GF_DOM_EVENT_PHASE_CANCEL|GF_DOM_EVENT_PHASE_CANCEL_ALL) ) { + gf_dom_listener_process_add(sg); + return 0; + } + } + gf_dom_listener_process_add(sg); + /*if the current target is a node, we can bubble*/ + return n ? 1 : 0; +} + +static void gf_sg_dom_event_bubble(GF_Node *node, GF_DOM_Event *event, GF_List *use_stack, u32 cur_par_idx) +{ + GF_Node *parent; + + if (!node) return; + + /*get the node's parent*/ + parent = gf_node_get_parent(node, 0); + + if (!parent) { + /*top of the graph, use Document*/ + if (node->sgprivate->scenegraph->RootNode==node) + sg_fire_dom_event(&node->sgprivate->scenegraph->dom_evt, event, node->sgprivate->scenegraph, NULL); + return; + } + if (cur_par_idx) { + GF_Node *used_node = gf_list_get(use_stack, cur_par_idx-1); + /*if the node is a used one, switch to the subtree*/ + if (used_node==node) { + parent = gf_list_get(use_stack, cur_par_idx); + if (cur_par_idx>1) cur_par_idx-=2; + else cur_par_idx = 0; + node->sgprivate->scenegraph->use_stack = use_stack; + /*if no events attached,bubble by default*/ + if (parent->sgprivate->interact && !sg_fire_dom_event(parent->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, parent)) return; + gf_sg_dom_event_bubble(parent, event, use_stack, cur_par_idx); + node->sgprivate->scenegraph->use_stack = use_stack; + return; + } + } + /*if no events attached,bubble by default*/ + if (parent->sgprivate->interact && !sg_fire_dom_event(parent->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, parent)) return; + gf_sg_dom_event_bubble(parent, event, use_stack, cur_par_idx); +} + + +void gf_sg_dom_stack_parents(GF_Node *node, GF_List *stack) +{ + if (!node) return; + if (node->sgprivate->interact && node->sgprivate->interact->dom_evt) gf_list_insert(stack, node, 0); + gf_sg_dom_stack_parents(gf_node_get_parent(node, 0), stack); +} + +GF_EXPORT +Bool gf_dom_event_fire_ex(GF_Node *node, GF_DOM_Event *event, GF_List *use_stack) +{ + GF_DOMEventTarget cur_target; + u32 cur_par_idx; + if (!node || !event) return 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[DOM Events ] Time %f - Firing event %s.%s\n", gf_node_get_scene_time(node), gf_node_get_log_name(node), gf_dom_event_get_name(event->type))); + + /*flush any pending add_listener + see "determine the current target's candidate event listeners" in http://www.w3.org/TR/DOM-Level-3-Events/events.html*/ + gf_dom_listener_process_add(node->sgprivate->scenegraph); + + event->consumed = 0; + event->target = node; + event->target_type = GF_DOM_EVENT_NODE; + if (node->sgprivate->interact && node->sgprivate->interact->dom_evt) { + event->currentTarget = node->sgprivate->interact->dom_evt; + } else { + cur_target.ptr_type = GF_DOM_EVENT_NODE; + cur_target.ptr = node; + cur_target.evt_list = NULL; + event->currentTarget = &cur_target; + } + + /*capture phase - not 100% sure, the actual capture phase should be determined by the std using the DOM events + SVGT doesn't use this phase, so we don't add it for now.*/ + if (0) { + Bool aborted = 0; + u32 i, count; + GF_List *parents; + event->event_phase = GF_DOM_EVENT_PHASE_CAPTURE; + parents = gf_list_new(); + /*get all parents to top*/ + gf_sg_dom_stack_parents(gf_node_get_parent(node, 0), parents); + count = gf_list_count(parents); + for (i=0; isgprivate->interact) + sg_fire_dom_event(n->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, n); + + /*event has been canceled*/ + if (event->event_phase & (GF_DOM_EVENT_PHASE_CANCEL|GF_DOM_EVENT_PHASE_CANCEL_ALL) ) { + aborted = 1; + break; + } + } + gf_list_del(parents); + if (aborted) return 1; + } + /*target phase*/ + event->event_phase = GF_DOM_EVENT_PHASE_AT_TARGET; + cur_par_idx = 0; + if (use_stack) { + cur_par_idx = gf_list_count(use_stack); + if (cur_par_idx) cur_par_idx--; + } + if ( (!node->sgprivate->interact || sg_fire_dom_event(node->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, node)) + && event->bubbles) { + /*bubbling phase*/ + event->event_phase = GF_DOM_EVENT_PHASE_BUBBLE; + gf_sg_dom_event_bubble(node, event, use_stack, cur_par_idx); + } + + return event->consumed ? 1 : 0; +} + +GF_EXPORT +Bool gf_dom_event_fire(GF_Node *node, GF_DOM_Event *event) +{ + return gf_dom_event_fire_ex(node, event, NULL); +} + +GF_DOMHandler *gf_dom_listener_build_ex(GF_Node *node, u32 event_type, u32 event_parameter, GF_Node *handler, GF_Node **out_listener) +{ + u32 tag; + SVG_Element *listener; + GF_FieldInfo info; + GF_ChildNodeItem *last = NULL; + + tag = gf_node_get_tag(node); + listener = (SVG_Element *) gf_node_new(node->sgprivate->scenegraph, TAG_SVG_listener); + /*don't register the listener, this will be done when adding to the node events list*/ + + if (handler) { + if (gf_node_get_attribute_by_tag(handler, TAG_XMLEV_ATT_event, 0, 0, &info)==GF_OK) { + event_type = ((XMLEV_Event *)info.far_ptr)->type; + event_parameter = ((XMLEV_Event *)info.far_ptr)->parameter; + } + } else { + handler = gf_node_new(node->sgprivate->scenegraph, TAG_SVG_handler); + gf_node_get_attribute_by_tag(handler, TAG_XMLEV_ATT_event, 1, 0, &info); + ((XMLEV_Event *)info.far_ptr)->type = event_type; + ((XMLEV_Event *)info.far_ptr)->parameter = event_parameter; + + /*register the handler as a child of the listener*/ + gf_node_register((GF_Node *)handler, (GF_Node *) listener); + gf_node_list_add_child_last(& ((GF_ParentNode *)listener)->children, (GF_Node*)handler, &last); + } + + gf_node_get_attribute_by_tag((GF_Node*)listener, TAG_XMLEV_ATT_event, 1, 0, &info); + ((XMLEV_Event *)info.far_ptr)->type = event_type; + ((XMLEV_Event *)info.far_ptr)->parameter = event_parameter; + + gf_node_get_attribute_by_tag((GF_Node*)listener, TAG_XMLEV_ATT_handler, 1, 0, &info); + ((XMLRI *)info.far_ptr)->target = handler; + + gf_node_get_attribute_by_tag((GF_Node*)listener, TAG_XMLEV_ATT_target, 1, 0, &info); + ((XMLRI *)info.far_ptr)->target = node; + + gf_node_dom_listener_add((GF_Node *) node, (GF_Node *) listener); + + if (out_listener) *out_listener = (GF_Node *) listener; + + /*set default handler*/ + ((SVG_handlerElement *) handler)->handle_event = gf_sg_handle_dom_event; + return (SVG_handlerElement *) handler; +} + +GF_EXPORT +GF_DOMHandler *gf_dom_listener_build(GF_Node *node, u32 event_type, u32 event_parameter) +{ + return gf_dom_listener_build_ex(node, event_type, event_parameter, NULL, NULL); +} + +static void gf_smil_handle_event(GF_Node *timed_elt, GF_FieldInfo *info, GF_DOM_Event *evt, Bool is_end) +{ + SMIL_Time *resolved, *proto; + Double scene_time = gf_node_get_scene_time(evt->target); + GF_List *times = *(GF_List **)info->far_ptr; + u32 found = 0; + u32 i, j, count = gf_list_count(times); + + /*remove all previously instantiated times that are in the past + TODO FIXME: this is not 100% correct, a begin val in the past can be interpreted!!*/ + for (i=0; itype == GF_SMIL_TIME_EVENT_RESOLVED) && (proto->clocktype != GF_SMIL_TIME_EVENT) continue; + if (proto->event.type != evt->type) continue; + if ((evt->type == GF_EVENT_KEYDOWN) || (evt->type == GF_EVENT_REPEAT_EVENT)) { + if (proto->event.parameter!=evt->detail) continue; + } + /*only handle event if coming from the watched element*/ + if (proto->element) { + if ((evt->currentTarget->ptr_type!=GF_DOM_EVENT_NODE) || (proto->element!= (GF_Node*)evt->currentTarget->ptr)) + continue; + } + + /*solve*/ + GF_SAFEALLOC(resolved, SMIL_Time); + resolved->type = GF_SMIL_TIME_EVENT_RESOLVED; + + if (proto->is_absolute_event) { + resolved->clock = evt->smil_event_time + proto->clock; + } else { + resolved->clock = scene_time + proto->clock; + } + + /*insert in sorted order*/ + for (j=0; jtype) ) { + if (proto->clock > resolved->clock) break; + } else { + break; + } + } + gf_list_insert(times, resolved, j); + if (j!=count) i++; + count++; + found++; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Inserting new time in %s: %f\n", gf_node_get_scene_time(timed_elt), gf_node_get_log_name(timed_elt), (is_end?"end":"begin"), resolved->clock)); + } + /* calling indirectly gf_smil_timing_modified */ + if (found) gf_node_changed(timed_elt, info); +} + +static void gf_smil_handle_event_begin(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer) +{ + GF_FieldInfo info; + GF_Node *timed_elt = (GF_Node *)gf_node_get_private(hdl); + memset(&info, 0, sizeof(GF_FieldInfo)); + info.name = "begin"; + info.far_ptr = ((SVGTimedAnimBaseElement *)timed_elt)->timingp->begin; + info.fieldType = SMIL_Times_datatype; + gf_smil_handle_event(timed_elt, &info, evt, 0); +} + +static void gf_smil_handle_event_end(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer) +{ + GF_FieldInfo info; + GF_Node *timed_elt = (GF_Node *)gf_node_get_private(hdl); + memset(&info, 0, sizeof(GF_FieldInfo)); + info.name = "end"; + info.far_ptr = ((SVGTimedAnimBaseElement *)timed_elt)->timingp->end; + info.fieldType = SMIL_Times_datatype; + gf_smil_handle_event((GF_Node *)timed_elt, &info, evt, 1); +} + +static void gf_smil_setup_event_list(GF_Node *node, GF_List *l, Bool is_begin) +{ + void *hdl; + u32 i, count; + count = gf_list_count(l); + for (i=0; itype != GF_SMIL_TIME_EVENT) continue; + /*not resolved yet*/ + if (!t->element && t->element_id) continue; + if (t->event.type==GF_EVENT_BEGIN) { + t->event.type=GF_EVENT_BEGIN_EVENT; + t->is_absolute_event = 1; + } else if (t->event.type==GF_EVENT_END) { + t->event.type=GF_EVENT_END_EVENT; + t->is_absolute_event = 1; + } else if (t->event.type==GF_EVENT_REPEAT) { + t->event.type=GF_EVENT_REPEAT_EVENT; + t->is_absolute_event = 1; + } + + /*create a new listener*/ + hdl = gf_dom_listener_build_ex(t->element, t->event.type, t->event.parameter, NULL, &t->listener); + + /*register the listener so that we can handle cyclic references: + - If the anim node gets destroyed, the listener is removed through the SMIL_Time reference + - If the target gets destroyed, the listener is removed through the regular way + */ + if (t->listener) + gf_node_register(t->listener, NULL); + + if (hdl) { + ((SVG_handlerElement *)hdl)->handle_event = is_begin ? gf_smil_handle_event_begin : gf_smil_handle_event_end; + } + else { + continue; + } + gf_node_set_private((GF_Node *)hdl, node); + /*we keep the t->element pointer in order to discard the source of identical events (begin of # elements, ...)*/ + } +} + +void gf_smil_setup_events(GF_Node *node) +{ + GF_FieldInfo info; + if (gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_begin, 0, 0, &info) == GF_OK) + gf_smil_setup_event_list(node, * (GF_List **)info.far_ptr, 1); + if (gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_end, 0, 0, &info) == GF_OK) + gf_smil_setup_event_list(node, * (GF_List **)info.far_ptr, 0); +} + + +GF_DOMText *gf_dom_add_text_node(GF_Node *parent, char *text_data) +{ + GF_DOMText *text; + GF_SAFEALLOC(text, GF_DOMText); + + gf_node_setup((GF_Node *)text, TAG_DOMText); + text->sgprivate->scenegraph = parent->sgprivate->scenegraph; + text->textContent = text_data; + gf_node_register((GF_Node *)text, parent); + gf_node_list_add_child_last(&((GF_ParentNode *)parent)->children, (GF_Node*)text, NULL); + return text; +} +GF_DOMText *gf_dom_new_text_node(GF_SceneGraph *sg) +{ + GF_DOMText *text; + GF_SAFEALLOC(text, GF_DOMText); + gf_node_setup((GF_Node *)text, TAG_DOMText); + text->sgprivate->scenegraph = sg; + return text; +} + +GF_DOMUpdates *gf_dom_add_updates_node(GF_Node *parent) +{ + GF_DOMUpdates *text; + GF_SAFEALLOC(text, GF_DOMUpdates); + + gf_node_setup((GF_Node *)text, TAG_DOMUpdates); + text->sgprivate->scenegraph = parent->sgprivate->scenegraph; + text->updates = gf_list_new(); + gf_node_register((GF_Node *)text, parent); + gf_node_list_add_child_last(&((GF_ParentNode *)parent)->children, (GF_Node*)text, NULL); + return text; +} + + +GF_DOMUpdates *gf_dom_add_update_node(GF_Node *parent) +{ + GF_DOMUpdates *update; + GF_SAFEALLOC(update, GF_DOMUpdates); + + gf_node_setup((GF_Node *)update, TAG_DOMUpdates); + update->sgprivate->scenegraph = parent->sgprivate->scenegraph; + update->updates = gf_list_new(); + gf_node_register((GF_Node *)update, parent); + gf_node_list_add_child_last(&((GF_ParentNode *)parent)->children, (GF_Node*)update, NULL); + return update; +} + + +#endif //GPAC_DISABLE_SVG + diff --git a/src/scenegraph/dom_smjs.c b/src/scenegraph/dom_smjs.c new file mode 100644 index 0000000..b3795b8 --- /dev/null +++ b/src/scenegraph/dom_smjs.c @@ -0,0 +1,3655 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2007-200X ENST + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/*base SVG type*/ +#include +/*dom events*/ +#include +/*dom text event*/ +#include + +#include +#include +#include + +#ifndef GPAC_DISABLE_SVG + +#ifdef GPAC_HAS_SPIDERMONKEY + +#include + +JSBool js_has_instance(JSContext *c, JSObject *obj, jsval val, JSBool *vp) +{ + *vp = JS_FALSE; + if (JSVAL_IS_OBJECT(val)) { + JSObject *p; + JSClass *js_class = JS_GET_CLASS(c, obj); + p = JSVAL_TO_OBJECT(val); + if (JS_InstanceOf(c, p, js_class, NULL) ) *vp = JS_TRUE; + } + return JS_TRUE; +} + +static GFINLINE Bool ScriptAction(GF_SceneGraph *scene, u32 type, GF_Node *node, GF_JSAPIParam *param) +{ + if (scene->script_action) + return scene->script_action(scene->script_action_cbck, type, node, param); + return 0; +} + +static GFINLINE GF_SceneGraph *xml_get_scenegraph(JSContext *c) +{ + GF_SceneGraph *scene; + JSObject *global = JS_GetGlobalObject(c); + assert(global); + scene = JS_GetPrivate(c, global); + assert(scene); + return scene; +} + +#define JSVAL_CHECK_STRING(_v) (JSVAL_IS_STRING(_v) || JSVAL_IS_NULL(_v)) +#define JSVAL_GET_STRING(_v) (JSVAL_IS_NULL(_v) ? NULL : JS_GetStringBytes(JSVAL_TO_STRING(_v)) ) + +char *js_get_utf8(jsval val) +{ + jschar *utf16; + char *txt; + u32 len; + if (!JSVAL_CHECK_STRING(val) || JSVAL_IS_NULL(val)) return NULL; + + utf16 = JS_GetStringChars(JSVAL_TO_STRING(val) ); + len = gf_utf8_wcslen(utf16)*2 + 1; + txt = malloc(sizeof(char)*len); + len = gf_utf8_wcstombs(txt, len, &utf16); + if ((s32)len<0) { + free(txt); + return NULL; + } + txt[len]=0; + return txt; +} + +/************************************************************ + * + * DOM3 core implementation + * + * This is NOT a full dom implementation :) + * + *************************************************************/ +typedef struct +{ + u32 nb_inst; + JSClass domDocumentClass; + JSClass domNodeClass; + JSClass domElementClass; + JSClass domTextClass; + JSClass domNodeListClass; + JSClass domEventClass; + + JSClass xmlHTTPRequestClass; + JSClass DCCIClass; + + JSObject *dom_node_proto; + JSObject *dom_document_proto; + JSObject *dom_element_proto; + JSObject *dom_event_proto; + + void *(*get_element_class)(GF_Node *n); + void *(*get_document_class)(GF_SceneGraph *n); +} GF_DOMRuntime; + +static GF_DOMRuntime *dom_rt = NULL; + + +static JSBool xml_dom3_not_implemented(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return JS_TRUE; +} + + +#define JS_DOM3_uDOM_FIRST_PROP 18 + +static void dom_node_changed(GF_Node *n, Bool child_modif, GF_FieldInfo *info) +{ + if (info) { + if (child_modif) { + gf_node_dirty_set(n, GF_SG_CHILD_DIRTY, 0); + } +#ifndef GPAC_DISABLE_SVG + else { + u32 flag = gf_svg_get_modification_flags((SVG_Element *)n, info); + gf_node_dirty_set(n, flag, 0); + } +#endif + } + gf_node_changed(n, info); +} + +static void define_dom_exception(JSContext *c, JSObject *global) +{ +#define DEF_EXC(_val) \ + JS_DefineProperty(c, obj, #_val, INT_TO_JSVAL(GF_DOM_EXC_##_val), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + + JSObject *obj = JS_DefineObject(c, global, "DOMException", NULL, 0, 0); + + DEF_EXC(INDEX_SIZE_ERR); + DEF_EXC(DOMSTRING_SIZE_ERR); + DEF_EXC(HIERARCHY_REQUEST_ERR); + DEF_EXC(WRONG_DOCUMENT_ERR); + DEF_EXC(INVALID_CHARACTER_ERR); + DEF_EXC(NO_DATA_ALLOWED_ERR); + DEF_EXC(NO_MODIFICATION_ALLOWED_ERR); + DEF_EXC(NOT_FOUND_ERR); + DEF_EXC(NOT_SUPPORTED_ERR); + DEF_EXC(INUSE_ATTRIBUTE_ERR); + DEF_EXC(INVALID_STATE_ERR); + DEF_EXC(SYNTAX_ERR); + DEF_EXC(INVALID_MODIFICATION_ERR); + DEF_EXC(NAMESPACE_ERR); + DEF_EXC(INVALID_ACCESS_ERR); + DEF_EXC(VALIDATION_ERR); + DEF_EXC(TYPE_MISMATCH_ERR); +#undef DEF_EXC + + JS_AliasProperty(c, global, "DOMException", "e"); + obj = JS_DefineObject(c, global, "EventException", NULL, 0, 0); + JS_DefineProperty(c, obj, "UNSPECIFIED_EVENT_TYPE_ERR", INT_TO_JSVAL(0), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "DISPATCH_REQUEST_ERR", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); +} + + +JSBool dom_throw_exception(JSContext *c, u32 code) +{ + JSObject *obj = JS_NewObject(c, NULL, 0, 0); + JS_DefineProperty(c, obj, "code", INT_TO_JSVAL(code), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_SetPendingException(c, OBJECT_TO_JSVAL(obj)); + return JS_FALSE; +} + + +GF_Node *dom_get_node(JSContext *c, JSObject *obj) +{ + GF_Node *n = obj ? JS_GetPrivate(c, obj) : NULL; + if (n && n->sgprivate) return n; + return NULL; +} + +GF_Node *dom_get_element(JSContext *c, JSObject *obj) +{ + GF_Node *n = JS_GetPrivate(c, obj); + if (!n || !n->sgprivate) return NULL; + if (n->sgprivate->tag==TAG_DOMText) return NULL; + return n; +} + +GF_SceneGraph *dom_get_doc(JSContext *c, JSObject *obj) +{ + GF_SceneGraph *sg = JS_GetPrivate(c, obj); + if (sg && !sg->__reserved_null) return sg; + return NULL; +} + +static jsval dom_document_construct(JSContext *c, GF_SceneGraph *sg) +{ + JSClass *jsclass; + JSObject *new_obj; + if (sg->document) return OBJECT_TO_JSVAL(sg->document); + + if (sg->reference_count) + sg->reference_count++; + gf_node_register(sg->RootNode, NULL); + + jsclass = &dom_rt->domDocumentClass; + if (dom_rt->get_document_class) + jsclass = (JSClass *) dom_rt->get_document_class(sg); + + new_obj = JS_NewObject(c, jsclass, 0, 0); + JS_SetPrivate(c, new_obj, sg); + sg->document = new_obj; + return OBJECT_TO_JSVAL(new_obj); +} +static jsval dom_base_node_construct(JSContext *c, JSClass *_class, GF_Node *n) +{ + GF_SceneGraph *sg; + u32 i, count; + JSObject *new_obj; + if (!n || !n->sgprivate->scenegraph) return JSVAL_VOID; + if (n->sgprivate->tagsgprivate->scenegraph; + count = gf_list_count(sg->objects); + for (i=0; iobjects, i); + GF_Node *a_node = JS_GetPrivate(c, obj); + if (n==a_node) return OBJECT_TO_JSVAL(obj); + } + + if (n->sgprivate->scenegraph->reference_count) + n->sgprivate->scenegraph->reference_count ++; + + gf_node_register(n, NULL); + new_obj = JS_NewObject(c, _class, 0, 0); + JS_SetPrivate(c, new_obj, n); + gf_list_add(sg->objects, new_obj); + return OBJECT_TO_JSVAL(new_obj); +} +static jsval dom_node_construct(JSContext *c, GF_Node *n) +{ + JSClass *__class = &dom_rt->domElementClass; + if (!n) return JSVAL_NULL; + if (n->sgprivate->scenegraph->dcci_doc) + __class = &dom_rt->DCCIClass; + else if (dom_rt->get_element_class) + __class = (JSClass *) dom_rt->get_element_class(n); + + /*in our implementation ONLY ELEMENTS are created, never attributes. We therefore always + create Elements when asked to create a node !!*/ + return dom_base_node_construct(c, __class, n); +} +jsval dom_element_construct(JSContext *c, GF_Node *n) +{ + JSClass *__class = &dom_rt->domElementClass; + if (!n) return JSVAL_NULL; + if (n->sgprivate->scenegraph->dcci_doc) + __class = &dom_rt->DCCIClass; + else if (dom_rt->get_element_class) + __class = (JSClass *) dom_rt->get_element_class(n); + + return dom_base_node_construct(c, __class, n); +} + +static jsval dom_text_construct(JSContext *c, GF_Node *n) +{ + return dom_base_node_construct(c, &dom_rt->domTextClass, n); +} + +static void dom_unregister_node(GF_Node *n) +{ + GF_SceneGraph *sg = n->sgprivate->scenegraph; + if (!sg) return; + /*!! node is being deleted !! */ + if (!n->sgprivate->num_instances) return; + + gf_node_unregister(n, NULL); + if (sg->reference_count) { + sg->reference_count--; + if (!sg->reference_count) + gf_sg_del(sg); + } +} + +jsval dom_node_get_sibling(JSContext *c, GF_Node *n, Bool is_prev, Bool elt_only) +{ + GF_Node *val; + GF_ChildNodeItem *child; + s32 idx, cur; + GF_ParentNode *par; + if (!n) return JSVAL_NULL; + par = (GF_ParentNode *)gf_node_get_parent(n, 0); + if (!par) return JSVAL_NULL; + + idx = gf_node_list_find_child(par->children, n); + if (idx<0) return JSVAL_NULL; + + if (!elt_only) { + if (is_prev) { + idx--; + if (idx<0) return JSVAL_NULL; + } + else idx++; + return dom_node_construct(c, gf_node_list_get_child(par->children, idx) ); + } + cur = 0; + val = NULL; + child = par->children; + while (child) { + if ((idx!=cur) && (child->node->sgprivate->tag!=TAG_DOMText)) { + val = child->node; + } + if (is_prev) { + if (idx<=cur) + break; + } else { + if (idx>=cur) val = NULL; + if (val && idxnext; + cur++; + } + return dom_node_construct(c, val); +} + +char *dom_node_flatten_text(GF_Node *n) +{ + u32 len = 0; + char *res = NULL; + GF_ChildNodeItem *list; + + if ((n->sgprivate->tag==TAG_DOMText) && ((GF_DOMText*)n)->textContent) { + if ( ((GF_DOMText*)n)->type == GF_DOM_TEXT_REGULAR) { + res = strdup(((GF_DOMText*)n)->textContent); + len = strlen(res); + } + } + + list = ((GF_ParentNode *)n)->children; + while (list) { + char *t = dom_node_flatten_text(list->node); + if (t) { + u32 sub_len = strlen(t); + res = realloc(res, sizeof(char)*(len+sub_len+1)); + if (!len) res[0] = 0; + len += sub_len; + strcat(res, t); + free(t); + } + list = list->next; + } + return res; +} + + +/*dom3 NodeList/NamedNodeMap*/ +typedef struct +{ + /*set if the object is a childList from an existing node*/ + GF_ParentNode *owner; + /*child list*/ + GF_ChildNodeItem *child; +} DOMNodeList; + + +static jsval dom_nodelist_construct(JSContext *c, GF_ParentNode *n) +{ + DOMNodeList *nl; + JSObject *new_obj; + if (!n) return JSVAL_VOID; + GF_SAFEALLOC(nl, DOMNodeList); + nl->owner = n; + if (n->sgprivate->scenegraph->reference_count) + n->sgprivate->scenegraph->reference_count++; + + gf_node_register((GF_Node*)n, NULL); + new_obj = JS_NewObject(c, &dom_rt->domNodeListClass, 0, 0); + JS_SetPrivate(c, new_obj, nl); + return OBJECT_TO_JSVAL(new_obj); +} + +static void dom_nodelist_finalize(JSContext *c, JSObject *obj) +{ + DOMNodeList *nl; + if (!JS_InstanceOf(c, obj, &dom_rt->domNodeListClass, NULL) ) + return; + + nl = (DOMNodeList *) JS_GetPrivate(c, obj); + if (!nl) return; + + if (nl->owner) { + dom_unregister_node((GF_Node*)nl->owner); + } else { + /*unregister all nodes for created lists*/ + while (nl->child) { + GF_ChildNodeItem *child = nl->child; + nl->child = child->next; + dom_unregister_node(child->node); + free(child); + } + } + free(nl); +} + +static JSBool dom_nodelist_item(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_Node *n; + u32 idx, count; + DOMNodeList *nl; + if (!JS_InstanceOf(c, obj, &dom_rt->domNodeListClass, NULL) ) + return JS_TRUE; + if ((argc!=1) || !JSVAL_IS_INT(argv[0])) + return JS_TRUE; + + nl = (DOMNodeList *)JS_GetPrivate(c, obj); + count = gf_node_list_get_count(nl->owner ? nl->owner->children : nl->child); + idx = JSVAL_TO_INT(argv[0]); + if ((idx<0) || (idx>=count)) { + *rval = JSVAL_VOID; + return JS_TRUE; + } + n = gf_node_list_get_child(nl->owner ? nl->owner->children : nl->child, idx); + *rval = dom_node_construct(c, n); + return JS_TRUE; +} + + + +static JSBool dom_nodelist_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + DOMNodeList *nl; + if (!JS_InstanceOf(c, obj, &dom_rt->domNodeListClass, NULL) + ) + return JS_TRUE; + + if (!JSVAL_IS_INT(id)) return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case 0: + nl = (DOMNodeList *) JS_GetPrivate(c, obj); + *vp = INT_TO_JSVAL( gf_node_list_get_count(nl->owner ? nl->owner->children : nl->child) ); + return JS_TRUE; + } + return JS_TRUE; +} +static JSBool dom_nodelist_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + if (!JSVAL_IS_INT(id)) return JS_TRUE; + /*no write prop*/ + return JS_TRUE; +} + + +/*dom event listener*/ + +#define JS_DOM3_EVEN_TARGET_INTERFACE \ + {"addEventListenerNS", dom_event_add_listener, 4}, \ + {"removeEventListenerNS", dom_event_remove_listener, 4}, \ + {"addEventListener", dom_event_add_listener, 3}, \ + {"removeEventListener", dom_event_remove_listener, 3}, \ + {"dispatchEvent", xml_dom3_not_implemented, 1}, + +/*eventListeners routines used by document, element and connection interfaces*/ +JSBool dom_event_add_listener_ex(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, GF_Node *vrml_node) +{ + GF_DOMEventTarget *target; + GF_FieldInfo info; + GF_Node *listener; + SVG_handlerElement *handler; + char *type, *callback; + u32 of = 0; + u32 evtType; + char *inNS = NULL; + GF_SceneGraph *sg = NULL; + JSFunction*fun = NULL; + GF_Node *n = NULL; + jsval funval = 0; + JSObject *evt_handler; + + target = NULL; + /*document interface*/ + sg = dom_get_doc(c, obj); + if (sg) { +#ifndef GPAC_DISABLE_SVG + target = &sg->dom_evt; +#else + return JS_TRUE; +#endif + } else { + if (vrml_node) { + n = vrml_node; + } else { + n = dom_get_element(c, obj); + } + if (n) sg = n->sgprivate->scenegraph; + } + + /*FIXME - SVG uDOM connection not supported yet*/ + + if (!sg) return JS_TRUE; + + /*NS version (4 args in DOM2, 5 in DOM3 for evt_group param)*/ + if (argc>=4) { + if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + inNS = js_get_utf8(argv[0]); + of = 1; + } + evt_handler = obj; + + if (!JSVAL_CHECK_STRING(argv[of])) goto err_exit; + type = JSVAL_GET_STRING(argv[of]); + callback = NULL; + + if (JSVAL_CHECK_STRING(argv[of+1])) { + callback = JSVAL_GET_STRING(argv[of+1]); + if (!callback) goto err_exit; + } else if (JSVAL_IS_OBJECT(argv[of+1])) { + if (JS_ObjectIsFunction(c, JSVAL_TO_OBJECT(argv[of+1]))) { + //fun = JS_ValueToFunction(c, argv[of+1]); + funval = argv[of+1]; + } else { + JSBool found; + jsval evt_fun; + evt_handler = JSVAL_TO_OBJECT(argv[of+1]); + found = JS_GetProperty(c, evt_handler, "handleEvent", &evt_fun); + if (!found || !JSVAL_IS_OBJECT(evt_fun) ) goto err_exit; + if (!JS_ObjectIsFunction(c, JSVAL_TO_OBJECT(evt_fun)) ) goto err_exit; + funval = evt_fun; + } + } + + evtType = gf_dom_event_type_by_name(type); + if (evtType==GF_EVENT_UNKNOWN) goto err_exit; + + listener = gf_node_new(sg, TAG_SVG_listener); + /*we don't register the listener with the parent node , it will be registered + on gf_dom_listener__add*/ + + /*!!! create the handler in the scene owning the script context !!! */ + { + GF_SceneGraph *sg = xml_get_scenegraph(c); + handler = (SVG_handlerElement *) gf_node_new(sg, TAG_SVG_handler); + /*we register the handler with the listener node to avoid modifying the DOM*/ + gf_node_register((GF_Node *)handler, listener); + gf_node_list_add_child(& ((GF_ParentNode *)listener)->children, (GF_Node*)handler); + + if (!callback) { + handler->js_fun = fun; + handler->js_fun_val = funval; + handler->evt_listen_obj = evt_handler; + } + } + + /*create attributes if needed*/ + gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_event, 1, 0, &info); + ((XMLEV_Event*)info.far_ptr)->type = evtType; + gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_handler, 1, 0, &info); + ((XMLRI*)info.far_ptr)->target = (GF_Node*)handler; + if (n) { + gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_target, 1, 0, &info); + ((XMLRI*)info.far_ptr)->target = n; + } + + gf_node_get_attribute_by_tag((GF_Node*)handler, TAG_XMLEV_ATT_event, 1, 0, &info); + ((XMLEV_Event*)info.far_ptr)->type = evtType; + + if (callback) gf_dom_add_text_node((GF_Node *)handler, strdup(callback)); + +#ifndef GPAC_DISABLE_SVG + if (handler->sgprivate->scenegraph->svg_js) + handler->handle_event = gf_sg_handle_dom_event; +#endif + + if (!handler->handle_event) { + handler->handle_event = gf_sg_handle_dom_event_for_vrml; + handler->js_context = c; + } + + /*don't add listener directly, post it and wait for event processing*/ + if (n) { + gf_dom_listener_post_add((GF_Node *) n, listener); + } else { + gf_dom_listener_add(listener, target); + } + +err_exit: + if (inNS) free(inNS); + return JS_TRUE; +} +JSBool dom_event_add_listener(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return dom_event_add_listener_ex(c, obj, argc, argv, rval, NULL); +} + +JSBool dom_event_remove_listener_ex(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, GF_Node *vrml_node) +{ +#ifndef GPAC_DISABLE_SVG + char *type, *callback; + u32 of = 0; + u32 evtType, i, count; + char *inNS = NULL; + GF_Node *node = NULL; + jsval funval = 0; + GF_SceneGraph *sg = NULL; + GF_DOMEventTarget *target = NULL; + + /*get the scenegraph first*/ + sg = dom_get_doc(c, obj); + if (!sg) { + if (vrml_node) { + node = vrml_node; + } else { + node = dom_get_element(c, obj); + } + if (node) sg = node->sgprivate->scenegraph; + } + + if (!sg) return JS_TRUE; + /*flush all pending add_listener*/ + gf_dom_listener_process_add(sg); + if (node) { + if (node->sgprivate->interact) target = node->sgprivate->interact->dom_evt; + } + /*FIXME - SVG uDOM connection not supported yet*/ + else { + target = &sg->dom_evt; + } + if (!target) return JS_TRUE; + + + if (argc==4) { + if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + inNS = js_get_utf8(argv[0]); + of = 1; + } + + if (!JSVAL_CHECK_STRING(argv[of])) goto err_exit; + type = JSVAL_GET_STRING(argv[of]); + callback = NULL; + if (JSVAL_CHECK_STRING(argv[of+1])) { + callback = JSVAL_GET_STRING(argv[of+1]); + } else if (JSVAL_IS_OBJECT(argv[of+1])) { + if (JS_ObjectIsFunction(c, JSVAL_TO_OBJECT(argv[of+1]) )) { + funval = argv[of+1]; + } + } + if (!callback && !funval) goto err_exit; + + evtType = gf_dom_event_type_by_name(type); + if (evtType==GF_EVENT_UNKNOWN) goto err_exit; + + count = gf_list_count(target->evt_list); + for (i=0; ievt_list, i); + + gf_node_get_attribute_by_tag(el, TAG_XMLEV_ATT_event, 0, 0, &info); + if (!info.far_ptr) continue; + if (((XMLEV_Event*)info.far_ptr)->type != evtType) continue; + + gf_node_get_attribute_by_tag(el, TAG_XMLEV_ATT_handler, 0, 0, &info); + if (!info.far_ptr) continue; + hdl = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target; + if (!hdl) continue; + if (funval) { + if (funval != hdl->js_fun_val) continue; + } else if (hdl->children) { + txt = (GF_DOMText *) hdl->children->node; + if (txt->sgprivate->tag != TAG_DOMText) continue; + if (!txt->textContent || strcmp(txt->textContent, callback)) continue; + } else { + continue; + } + + /*this will destroy the listener and its child handler*/ + gf_dom_listener_del(el, target); + break; + } +#endif + +err_exit: + if (inNS) free(inNS); + return JS_TRUE; +} + +JSBool dom_event_remove_listener(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return dom_event_remove_listener_ex(c, obj, argc, argv, rval, NULL); +} + +/*dom3 node*/ +static void dom_node_finalize(JSContext *c, JSObject *obj) +{ + GF_Node *n = (GF_Node *) JS_GetPrivate(c, obj); + /*the JS proto of the svgClass or a destroyed object*/ + if (!n) return; + if (!n->sgprivate) return; + gf_list_del_item(n->sgprivate->scenegraph->objects, obj); + dom_unregister_node(n); +} + +static Bool check_dom_parents(JSContext *c, GF_Node *n, GF_Node *parent) +{ + GF_ParentList *par = n->sgprivate->parents; + if (n->sgprivate->scenegraph != parent->sgprivate->scenegraph) { + dom_throw_exception(c, GF_DOM_EXC_WRONG_DOCUMENT_ERR); + return 0; + } + while (par) { + if (par->node==parent) { + dom_throw_exception(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR); + return 0; + } + if (!check_dom_parents(c, par->node, parent)) + return 0; + par = par->next; + } + return 1; +} + +static void dom_node_inserted(GF_Node *par, GF_Node *n, s32 pos) +{ + GF_ParentNode *old_par; + Bool do_init = 0; + + /*if node is already in graph, remove it from its parent*/ + old_par = (GF_ParentNode*)gf_node_get_parent(n, 0); + if (old_par) { + /*prevent destruction when removing node*/ + n->sgprivate->num_instances++; + gf_node_list_del_child(&old_par->children, n); + gf_node_unregister(n, (GF_Node*)old_par); + n->sgprivate->num_instances--; + } else { + do_init = (n->sgprivate->UserCallback || n->sgprivate->UserPrivate) ? 0 : 1; + } + + if (pos<0) gf_node_list_add_child( & ((GF_ParentNode *)par)->children, n); + else gf_node_list_insert_child( & ((GF_ParentNode *)par)->children, n, (u32) pos); + gf_node_register(n, par); + + if (do_init) { + /*node is a handler, create listener*/ + if (par && (n->sgprivate->tag==TAG_SVG_handler)) { + gf_dom_listener_build_ex(par, 0, 0, n, NULL); + } + gf_node_init(n); + +#ifndef GPAC_DISABLE_SVG + if (n->sgprivate->interact && n->sgprivate->interact->dom_evt) { + GF_DOM_Event evt; + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = GF_EVENT_LOAD; + gf_dom_event_fire(n, &evt); + } +#endif + } + /*node is being re-inserted, activate it in case*/ + if (!old_par) gf_node_activate(n); + + dom_node_changed(par, 1, NULL); +} + +static JSBool xml_node_insert_before(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + s32 idx; + u32 tag; + GF_Node *n, *target, *new_node; + GF_ParentNode *par; + if (!argc || !JSVAL_IS_OBJECT(argv[0])) + return JS_TRUE; + + n = dom_get_node(c, obj); + if (!n) { + return dom_throw_exception(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR); + } + + new_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[0])); + if (!new_node) return JS_TRUE; + + target = NULL; + if ((argc==2) && JSVAL_IS_OBJECT(argv[1]) && !JSVAL_IS_NULL(argv[1])) { + target = dom_get_node(c, JSVAL_TO_OBJECT(argv[1])); + if (!target) return JS_TRUE; + } + tag = gf_node_get_tag(n); + if (tag==TAG_DOMText) return JS_TRUE; + + if (!check_dom_parents(c, n, new_node)) + return JS_FALSE; + + par = (GF_ParentNode*)n; + idx = -1; + if (target) { + idx = gf_node_list_find_child(par->children, target); + if (idx<0) { + return dom_throw_exception(c, GF_DOM_EXC_NOT_FOUND_ERR); + } + } + dom_node_inserted(n, new_node, idx); + *rval = argv[0]; + return JS_TRUE; +} + +static JSBool xml_node_append_child(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 tag; + GF_Node *n, *new_node; + if (!argc || !JSVAL_IS_OBJECT(argv[0])) return JS_TRUE; + n = dom_get_node(c, obj); + if (!n) { + return dom_throw_exception(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR); + } + + new_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[0])); + if (!new_node) return JS_TRUE; + tag = gf_node_get_tag(n); + if (tag==TAG_DOMText) return JS_TRUE; + + if (!check_dom_parents(c, n, new_node)) + return JS_FALSE; + + dom_node_inserted(n, new_node, -1); + + *rval = argv[0]; + return JS_TRUE; +} + +static JSBool xml_node_replace_child(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + s32 idx; + u32 tag; + GF_Node *n, *new_node, *old_node; + GF_ParentNode *par; + if ((argc!=2) || !JSVAL_IS_OBJECT(argv[0]) || !JSVAL_IS_OBJECT(argv[1])) return JS_TRUE; + n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + + new_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[0])); + if (!new_node) return JS_TRUE; + old_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[1])); + if (!old_node) return JS_TRUE; + tag = gf_node_get_tag(n); + if (tag==TAG_DOMText) return JS_TRUE; + par = (GF_ParentNode*)n; + idx = gf_node_list_find_child(par->children, old_node); + if (idx<0) return JS_TRUE; + + gf_node_list_del_child(&par->children, old_node); + gf_node_unregister(old_node, n); + + dom_node_inserted(n, new_node, -1); + + *rval = argv[0]; + return JS_TRUE; +} + +static JSBool xml_node_remove_child(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 tag; + GF_Node *n, *old_node; + GF_ParentNode *par; + if (!argc || !JSVAL_IS_OBJECT(argv[0])) return JS_TRUE; + n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + + old_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[0])); + if (!old_node) return JS_TRUE; + tag = gf_node_get_tag(n); + if (tag==TAG_DOMText) return JS_TRUE; + par = (GF_ParentNode*)n; + + /*if node is present in parent, unregister*/ + if (gf_node_list_del_child(&par->children, old_node)) { + gf_node_unregister(old_node, n); + } else { + return dom_throw_exception(c, GF_DOM_EXC_NOT_FOUND_ERR); + } + *rval = argv[0]; + dom_node_changed(n, 1, NULL); + return JS_TRUE; +} + + +static JSBool xml_clone_node(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + Bool deep; + GF_Node *n, *clone; + n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + deep = argc ? JSVAL_TO_BOOLEAN(argv[0]) : 0; + + clone = gf_node_clone(n->sgprivate->scenegraph, n, NULL, "", deep); + *rval = dom_node_construct(c, clone); + return JS_TRUE; +} + + +static JSBool xml_node_has_children(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_Node *n; + n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + *rval = BOOLEAN_TO_JSVAL( ((GF_ParentNode*)n)->children ? JS_TRUE : JS_FALSE); + return JS_TRUE; +} + +static JSBool xml_node_has_attributes(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 tag; + GF_Node *n; + n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + tag = gf_node_get_tag(n); + if (tag>=GF_NODE_FIRST_DOM_NODE_TAG) { + *rval = BOOLEAN_TO_JSVAL( ((GF_DOMNode*)n)->attributes ? JS_TRUE : JS_FALSE); + } else { + /*not supported for other node types*/ + *rval = BOOLEAN_TO_JSVAL( JS_FALSE); + } + return JS_TRUE; +} + +static JSBool xml_node_is_same_node(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_Node *n, *a_node; + if (!argc || !JSVAL_IS_OBJECT(argv[0])) return JS_TRUE; + n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + a_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[0])); + if (!a_node) return JS_TRUE; + *rval = BOOLEAN_TO_JSVAL( (a_node==n) ? JS_TRUE : JS_FALSE); + return JS_TRUE; +} + +static const char *node_get_local_name(GF_Node *node) +{ + const char *res; + GF_List *l; + if (!node) return NULL; + l = node->sgprivate->scenegraph->ns; + node->sgprivate->scenegraph->ns = NULL; + res = gf_node_get_class_name(node); + node->sgprivate->scenegraph->ns = l; + return res; +} + +static const char *node_lookup_namespace_by_tag(GF_Node *node, u32 tag) +{ + /*browse attributes*/ + GF_DOMAttribute *att; + if (!node) return NULL; + att = ((SVG_Element*)node)->attributes; + while (att) { + if (att->tag==TAG_DOM_ATT_any) { + GF_DOMFullAttribute *datt = (GF_DOMFullAttribute*)att; + if (datt->name && !strncmp(datt->name, "xmlns", 5)) { + char *xmlns = *(DOM_String *) datt->data; + u32 crc = gf_crc_32(xmlns, strlen(xmlns)); + if (tag==crc) return xmlns; + } + } + att = att->next; + } + /*browse for parent*/ + return node_lookup_namespace_by_tag(gf_node_get_parent(node, 0), tag); +} + +static u32 get_namespace_code_by_prefix(GF_Node *node, char *prefix) +{ + /*browse attributes*/ + GF_DOMAttribute *att; + if (!node) return 0; + att = ((SVG_Element*)node)->attributes; + while (att) { + if (att->tag==TAG_DOM_ATT_any) { + GF_DOMFullAttribute *datt = (GF_DOMFullAttribute*)att; + if (!prefix) { + if (!strcmp(datt->name, "xmlns")) return datt->xmlns; + } else if (!strncmp(datt->name, "xmlns:", 6)) { + if (!strcmp(datt->name+6, prefix)) return datt->xmlns; + } + } + att = att->next; + } + /*browse for parent*/ + return get_namespace_code_by_prefix(gf_node_get_parent(node, 0), prefix); +} + +static JSBool dom_node_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + u32 tag; + GF_Node *n; + GF_SceneGraph *sg = NULL; + GF_ParentNode *par; + + n = dom_get_node(c, obj); + if (!n) { + sg = dom_get_doc(c, obj); + if (!sg) return JS_TRUE; + } + + /*not supported*/ + if (!JSVAL_IS_INT(id)) return JS_TRUE; + + tag = n ? gf_node_get_tag(n) : 0; + par = (GF_ParentNode*)n; + + switch (JSVAL_TO_INT(id)) { + /*"nodeName"*/ + case 0: + if (sg) { + *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "#document") ); + } + else if (tag==TAG_DOMText) { + GF_DOMText *txt = (GF_DOMText *)n; + if (txt->type==GF_DOM_TEXT_CDATA) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "#cdata-section") ); + else *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "#text") ); + } + else { + *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, gf_node_get_class_name(n) ) ); + } + return JS_TRUE; + /*"nodeValue"*/ + case 1: + *vp = JSVAL_VOID; + if (tag==TAG_DOMText) { + GF_DOMText *txt = (GF_DOMText *)n; + *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, txt->textContent) ); + } + return JS_TRUE; + /*"nodeType"*/ + case 2: + if (sg) *vp = INT_TO_JSVAL(9); + else if (tag==TAG_DOMText) { + GF_DOMText *txt = (GF_DOMText *)n; + if (txt->type==GF_DOM_TEXT_CDATA) *vp = INT_TO_JSVAL(4); + else *vp = INT_TO_JSVAL(3); + } + else *vp = INT_TO_JSVAL(1); + return JS_TRUE; + /*"parentNode"*/ + case 3: + if (sg) { + *vp = JSVAL_NULL; + } + /*if root node of the tree, the parentNode is the document*/ + else if (n->sgprivate->scenegraph->RootNode==n) { + *vp = dom_document_construct(c, n->sgprivate->scenegraph); + } else { + *vp = dom_node_construct(c, gf_node_get_parent(n, 0) ); + } + return JS_TRUE; + /*"childNodes"*/ + case 4: + /*NOT SUPPORTED YET*/ + if (sg) *vp = JSVAL_VOID; + else if (tag==TAG_DOMText) *vp = JSVAL_NULL; + else *vp = dom_nodelist_construct(c, par); + return JS_TRUE; + /*"firstChild"*/ + case 5: + if (sg) *vp = dom_node_construct(c, sg->RootNode); + else if (tag==TAG_DOMText) *vp = JSVAL_NULL; + else if (!par->children) { + *vp = JSVAL_NULL; + } + else *vp = dom_node_construct(c, par->children->node); + return JS_TRUE; + /*"lastChild"*/ + case 6: + if (sg) *vp = dom_node_construct(c, sg->RootNode); + else if ((tag==TAG_DOMText) || !par->children) *vp = JSVAL_VOID; + else *vp = dom_node_construct(c, gf_node_list_get_child(par->children, -1) ); + return JS_TRUE; + /*"previousSibling"*/ + case 7: + /*works for doc as well since n is NULL*/ + *vp = dom_node_get_sibling(c, n, 1, 0); + return JS_TRUE; + /*"nextSibling"*/ + case 8: + *vp = dom_node_get_sibling(c, n, 0, 0); + return JS_TRUE; + /*"attributes"*/ + case 9: + /*NOT SUPPORTED YET*/ + *vp = JSVAL_VOID; + return JS_TRUE; + /*"ownerDocument"*/ + case 10: + if (sg) *vp = JSVAL_NULL; + else *vp = dom_document_construct(c, n->sgprivate->scenegraph); + return JS_TRUE; + /*"namespaceURI"*/ + case 11: + *vp = JSVAL_NULL; + if (!sg) { + tag = gf_xml_get_element_namespace(n); + if (tag) { + const char *xmlns = gf_sg_get_namespace(n->sgprivate->scenegraph, tag); + if (!xmlns) xmlns = node_lookup_namespace_by_tag(n, tag); + *vp = xmlns ? STRING_TO_JSVAL( JS_NewStringCopyZ(c, xmlns) ) : JSVAL_VOID; + } + } + return JS_TRUE; + /*"prefix"*/ + case 12: + if (sg) tag = gf_sg_get_namespace_code(sg, NULL); + else tag = gf_xml_get_element_namespace(n); + *vp = JSVAL_NULL; + if (tag) { + char *xmlns = (char *)gf_sg_get_namespace_qname(sg ? sg : n->sgprivate->scenegraph, tag); + if (xmlns) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, xmlns) ); + } + return JS_TRUE; + /*"localName"*/ + case 13: + *vp = JSVAL_NULL; + if (!sg && (tag!=TAG_DOMText)) { + *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, node_get_local_name(n) ) ); + } + return JS_TRUE; + /*"baseURI"*/ + case 14: + /*NOT SUPPORTED YET*/ + *vp = JSVAL_NULL; + return JS_TRUE; + /*"textContent"*/ + case 15: + *vp = JSVAL_VOID; + if (!sg) { + char *res = dom_node_flatten_text(n); + *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, res) ); + free(res); + } + return JS_TRUE; + } + /*not supported*/ + return JS_TRUE; +} + +void dom_node_set_textContent(GF_Node *n, char *text) +{ + GF_ParentNode *par = (GF_ParentNode *)n; + gf_node_unregister_children(n, par->children); + par->children = NULL; + gf_dom_add_text_node(n, strdup( text) ); + dom_node_changed(n, 1, NULL); +} + +static JSBool dom_node_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + u32 tag; + GF_Node *n; + GF_ParentNode *par; + + n = dom_get_node(c, obj); + /*note an element - we don't support property setting on document yet*/ + if (!n) return JS_TRUE; + + if (!JSVAL_IS_INT(id)) return JS_TRUE; + + tag = n ? gf_node_get_tag(n) : 0; + par = (GF_ParentNode*)n; + + switch (JSVAL_TO_INT(id)) { + /*"nodeValue"*/ + case 1: + if ((tag==TAG_DOMText) && JSVAL_CHECK_STRING(*vp)) { + GF_DOMText *txt = (GF_DOMText *)n; + if (txt->textContent) free(txt->textContent); + txt->textContent = js_get_utf8(*vp); + dom_node_changed(n, 1, NULL); + } + /*we only support element and sg in the Node interface, no set*/ + return JS_TRUE; + /*"prefix"*/ + case 12: + /*NOT SUPPORTED YET*/ + return JS_TRUE; + /*"textContent"*/ + case 15: + { + char *txt; + txt = js_get_utf8(*vp); + dom_node_set_textContent(n, txt); + if (txt) free(txt); + } + return JS_TRUE; + } + /*not supported*/ + return JS_TRUE; +} + + +/*dom3 document*/ + +/*don't attempt to do anything with the scenegraph, it may be destroyed +fortunately a sg cannot be created like that...*/ +void dom_document_finalize(JSContext *c, JSObject *obj) +{ + GF_SceneGraph *sg = dom_get_doc(c, obj); + + sg = (GF_SceneGraph*) JS_GetPrivate(c, obj); + /*the JS proto of the svgClass or a destroyed object*/ + if (!sg) return; + + sg->document = NULL; + gf_node_unregister(sg->RootNode, NULL); + if (sg->reference_count) { + sg->reference_count--; + if (!sg->reference_count) + gf_sg_del(sg); + } +} + +static JSBool dom_document_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + u32 prop_id; + GF_SceneGraph *sg = dom_get_doc(c, obj); + if (!sg) return JS_TRUE; + + if (!JSVAL_IS_INT(id)) return JS_TRUE; + prop_id = JSVAL_TO_INT(id); + + switch (prop_id) { + case 2:/*implementation*/ + /*FIXME, this is wrong, we should have our own implementation + but at the current time we rely on the global object to provide it*/ + *vp = OBJECT_TO_JSVAL( JS_GetGlobalObject(c) ); + return JS_TRUE; + case 3: /*"documentElement"*/ + *vp = dom_element_construct(c, sg->RootNode); + return JS_TRUE; + + case 11:/*global*/ + *vp = OBJECT_TO_JSVAL( JS_GetGlobalObject(c) ); + return JS_TRUE; + + + /*NOT SUPPORTED YET*/ + + case 1: /*"doctype"*/ + case 4: /*"inputEncoding"*/ + case 5: /*"xmlEncoding"*/ + case 6: /*"xmlStandalone"*/ + case 7: /*"xmlVersion"*/ + case 8: /*"strictErrorChecking"*/ + case 9: /*"documentURI"*/ + case 10: /*"domConfig"*/ + *vp = JSVAL_VOID; + return JS_TRUE; + } + return JS_TRUE; +} + +static JSBool dom_document_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + u32 prop_id; + GF_SceneGraph *sg = dom_get_doc(c, obj); + if (!sg) return JS_TRUE; + + if (!JSVAL_IS_INT(id)) return JS_TRUE; + prop_id = JSVAL_TO_INT(id); + + switch (prop_id) { + case 6:/*"xmlStandalone"*/ + break; + case 7:/*"xmlVersion"*/ + break; + case 8:/*"strictErrorChecking"*/ + break; + case 9:/*"documentURI"*/ + break; + case 10:/*"domConfig"*/ + break; + + /*the rest is read-only*/ + } + return JS_TRUE; +} + +static JSBool xml_document_create_element(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 tag, ns; + GF_Node *n; + char *name, *xmlns; + GF_SceneGraph *sg = dom_get_doc(c, obj); + + if (!sg || !argc || !JSVAL_CHECK_STRING(argv[0]) ) + return JS_TRUE; + + name = NULL; + /*NS version*/ + ns = 0; + xmlns = NULL; + if (argc==2) { + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + xmlns = js_get_utf8(argv[0]); + if (xmlns) ns = gf_sg_get_namespace_code_from_name(sg, xmlns); + name = JSVAL_GET_STRING(argv[1]); + } else { + name = JSVAL_GET_STRING(argv[0]); + } + + if (name) { + /*browse all our supported DOM implementations*/ + tag = gf_xml_get_element_tag(name, ns); + if (!tag) tag = TAG_DOMFullNode; + n = gf_node_new(sg, tag); + if (n && (tag == TAG_DOMFullNode)) { + GF_DOMFullNode *elt = (GF_DOMFullNode *)n; + elt->name = strdup(name); + if (xmlns) { + gf_sg_add_namespace(sg, xmlns, NULL); + elt->ns = gf_sg_get_namespace_code_from_name(sg, xmlns); + } + } + *rval = dom_element_construct(c, n); + } + if (xmlns) free(xmlns); + return JS_TRUE; +} + +static JSBool xml_document_create_text(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_Node *n; + GF_SceneGraph *sg = dom_get_doc(c, obj); + if (!sg) return JS_TRUE; + + n = gf_node_new(sg, TAG_DOMText); + if (argc) { + char *str = js_get_utf8(argv[0]); + if (!str) str = strdup(""); + ((GF_DOMText*)n)->textContent = str; + } + *rval = dom_text_construct(c, n); + return JS_TRUE; +} + +static void xml_doc_gather_nodes(GF_ParentNode *node, char *name, DOMNodeList *nl) +{ + Bool bookmark = 1; + GF_ChildNodeItem *child; + const char *node_name; + if (!node) return; + if (name) { + node_name = gf_node_get_class_name((GF_Node*)node); + if (strcmp(node_name, name)) bookmark = 0; + } + if (bookmark) { + gf_node_register((GF_Node*)node, NULL); + if (node->sgprivate->scenegraph->reference_count) + node->sgprivate->scenegraph->reference_count++; + gf_node_list_add_child(&nl->child, (GF_Node*)node); + } + if (node->sgprivate->tagchildren; + while (child) { + xml_doc_gather_nodes((GF_ParentNode*)child->node, name, nl); + child = child->next; + } +} + +static JSBool xml_document_elements_by_tag(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + DOMNodeList *nl; + JSObject *new_obj; + char *name; + GF_SceneGraph *sg = dom_get_doc(c, obj); + if (!sg) return JS_TRUE; + + if (!argc || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + name = JSVAL_GET_STRING(argv[0]); + /*NS version - TODO*/ + if (argc==2) { + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + name = JSVAL_GET_STRING(argv[1]); + } + + GF_SAFEALLOC(nl, DOMNodeList); + if (name && !strcmp(name, "*")) name = NULL; + xml_doc_gather_nodes((GF_ParentNode*)sg->RootNode, name, nl); + new_obj = JS_NewObject(c, &dom_rt->domNodeListClass, 0, 0); + JS_SetPrivate(c, new_obj, nl); + *rval = OBJECT_TO_JSVAL(new_obj); + return JS_TRUE; +} + +static JSBool xml_document_element_by_id(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + NodeIDedItem *reg_node; + GF_Node *n; + char *id; + GF_SceneGraph *sg = dom_get_doc(c, obj); + if (!sg) return JS_FALSE; + if (!argc || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + id = JSVAL_GET_STRING(argv[0]); + + /*we don't use the regular gf_sg_find_node_by_name because we may have nodes defined with the + same ID and we need to locate the first one which is inserted in the tree*/ + n = NULL; + reg_node = sg->id_node; + while (reg_node) { + if (reg_node->NodeName && !strcmp(reg_node->NodeName, id)) { + n = reg_node->node; + /*element is not inserted - fixme, we should check all parents*/ + if (n && (n->sgprivate->scenegraph->RootNode!=n) && !n->sgprivate->parents) n = NULL; + else break; + } + reg_node = reg_node->next; + } + *rval = dom_element_construct(c, n); + return JS_TRUE; +} + +/*dom3 element*/ +void dom_element_finalize(JSContext *c, JSObject *obj) +{ + dom_node_finalize(c, obj); +} + +static JSBool dom_element_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + u32 prop_id; + GF_Node *n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + + if (!JSVAL_IS_INT(id)) return JS_TRUE; + prop_id = JSVAL_TO_INT(id); + switch (prop_id) { + case 1: /*"tagName"*/ + *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, gf_node_get_class_name(n) ) ); + return JS_TRUE; + case 2: /*"schemaTypeInfo"*/ + /*NOT SUPPORTED YET*/ + *vp = JSVAL_VOID; + return JS_TRUE; + } + return JS_TRUE; +} + +static JSBool xml_element_get_attribute(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *name, *ns; + GF_Node *n = dom_get_node(c, obj); + if (!n) + return JS_TRUE; + + if (!argc || !JSVAL_CHECK_STRING(argv[0])) + return JS_TRUE; + name = JSVAL_GET_STRING(argv[0]); + ns = NULL; + /*NS version*/ + if (argc==2) { + if (!JSVAL_CHECK_STRING(argv[1])) + return JS_TRUE; + ns = js_get_utf8(argv[0]); + name = JSVAL_GET_STRING(argv[1]); + } + if (!name) goto exit; + + /*ugly ugly hack ...*/ + if (!strcmp(name, "id") || !strcmp(name, "xml:id") ) { + char *sID = (char *) gf_node_get_name(n); + *rval = STRING_TO_JSVAL( JS_NewStringCopyZ(c, sID ? sID : "") ); + goto exit; + } + + if (n->sgprivate->tag==TAG_DOMFullNode) { + GF_DOMFullNode *node = (GF_DOMFullNode*)n; + GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes; + while (att) { + if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) { + *rval = STRING_TO_JSVAL( JS_NewStringCopyZ(c, (char*)att->data ) ); + goto exit; + } + att = (GF_DOMFullAttribute *) att->next; + } + } + else if (n->sgprivate->tag==TAG_DOMText) { + + } else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) { + GF_FieldInfo info; + u32 ns_code = 0; + if (ns) { + ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns); + if (!ns_code) ns_code = gf_crc_32(ns, strlen(ns)); + } + else { + ns_code = gf_xml_get_element_namespace(n); + } + + if (gf_node_get_attribute_by_name(n, name, ns_code, 0, 0, &info)==GF_OK) { + char szAtt[4096]; + gf_svg_dump_attribute(n, &info, szAtt); + *rval = STRING_TO_JSVAL( JS_NewStringCopyZ(c, szAtt) ); + goto exit; + } + } + *rval = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "") ); +exit: + if (ns) free(ns); + return JS_TRUE; +} + + +static JSBool xml_element_has_attribute(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *name, *ns; + GF_Node *n = dom_get_node(c, obj); + if (!n) + return JS_TRUE; + + if (!argc || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + name = JSVAL_GET_STRING(argv[0]); + ns = NULL; + /*NS version*/ + if (argc==2) { + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + ns = js_get_utf8(argv[0]); + name = JSVAL_GET_STRING(argv[1]); + } + if (!name) goto exit; + if (n->sgprivate->tag==TAG_DOMFullNode) { + GF_DOMFullNode *node = (GF_DOMFullNode*)n; + GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes; + while (att) { + if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) { + *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + goto exit; + } + att = (GF_DOMFullAttribute *) att->next; + } + } + else if (n->sgprivate->tag==TAG_DOMText) { + } + else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) { + GF_FieldInfo info; + u32 ns_code = 0; + if (ns) ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns); + else ns_code = gf_sg_get_namespace_code(n->sgprivate->scenegraph, NULL); + + if (gf_node_get_attribute_by_name(n, name, ns_code, 0, 0, &info)==GF_OK) { + *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + goto exit; + } + } + *rval = BOOLEAN_TO_JSVAL(JS_FALSE); +exit: + if (ns) free(ns); + return JS_TRUE; +} + + + +static JSBool xml_element_remove_attribute(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 tag; + GF_DOMFullNode *node; + GF_DOMFullAttribute *prev, *att; + char *name, *ns; + GF_Node *n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + + if (!argc || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + name = JSVAL_GET_STRING(argv[0]); + ns = NULL; + /*NS version*/ + if (argc==2) { + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + ns = js_get_utf8(argv[0]); + name = JSVAL_GET_STRING(argv[1]); + } + if (!name) goto exit; + + tag = TAG_DOM_ATT_any; + node = (GF_DOMFullNode*)n; + prev = NULL; + att = (GF_DOMFullAttribute*)node->attributes; + + if (n->sgprivate->tag==TAG_DOMFullNode) tag = TAG_DOM_ATT_any; + else if (n->sgprivate->tag==TAG_DOMText) { + goto exit; + } else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) { + u32 ns_code = 0; + if (ns) ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns); + else ns_code = gf_sg_get_namespace_code(n->sgprivate->scenegraph, NULL); + + tag = gf_xml_get_attribute_tag(n, name, ns_code); + } + + while (att) { + if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) { + if (prev) prev->next = att->next; + else node->attributes = att->next; + if (att->data) free(att->data); + free(att->name); + free(att); + dom_node_changed(n, 0, NULL); + goto exit; + } else if (tag==att->tag) { + if (prev) prev->next = att->next; + else node->attributes = att->next; + gf_svg_delete_attribute_value(att->data_type, att->data, n->sgprivate->scenegraph); + free(att); + dom_node_changed(n, 0, NULL); + goto exit; + } + prev = att; + att = (GF_DOMFullAttribute *) att->next; + } +exit: + if (ns) free(ns); + return JS_TRUE; +} + + +static JSBool xml_element_set_attribute(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 evtType, idx; + char *name, *val, *ns; + char szVal[100]; + GF_Node *n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + if ((argc < 2)) return JS_TRUE; + + if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + name = JSVAL_GET_STRING(argv[0]); + + idx = 1; + ns = NULL; + /*NS version*/ + if (argc==3) { + char *sep; + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + ns = js_get_utf8(argv[0]); + gf_sg_add_namespace(n->sgprivate->scenegraph, ns, NULL); + name = JSVAL_GET_STRING(argv[1]); + idx = 2; + + sep = strchr(name, ':'); + if (sep) name = sep+1; + + } + + val = NULL; + if (JSVAL_CHECK_STRING(argv[idx])) { + val = JSVAL_GET_STRING(argv[idx]); + } else if (JSVAL_IS_INT(argv[idx])) { + sprintf(szVal, "%d", JSVAL_TO_INT(argv[idx])); + val = szVal; + } else if (JSVAL_IS_NUMBER(argv[idx])) { + jsdouble d; + JS_ValueToNumber(c, argv[idx], &d); + sprintf(szVal, "%g", d); + val = szVal; + } else if (JSVAL_IS_BOOLEAN(argv[idx])) { + sprintf(szVal, "%s", JSVAL_TO_BOOLEAN(argv[idx]) ? "true" : "false"); + val = szVal; + } else { + goto exit; + } + if (!name || !val) goto exit; + + + if ((name[0]=='o') && (name[1]=='n')) { + evtType = gf_dom_event_type_by_name(name + 2); + if (evtType != GF_EVENT_UNKNOWN) { + /*check if we're modifying an existing listener*/ + SVG_handlerElement *handler; + u32 i, count = gf_dom_listener_count(n); + for (i=0;itype != evtType)) continue; + gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_handler, 0, 0, &info); + assert(info.far_ptr); + handler = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target; + text = (GF_DOMText*)handler->children->node; + if (text->sgprivate->tag==TAG_DOMText) { + if (text->textContent) free(text->textContent); + text->textContent = strdup(val); + } + goto exit; + } + /*nope, create a listener*/ + handler = gf_dom_listener_build(n, evtType, 0); + gf_dom_add_text_node((GF_Node*)handler, strdup(val) ); + goto exit; + } + } + + if (n->sgprivate->tag==TAG_DOMFullNode) { + GF_DOMFullAttribute *prev = NULL; + GF_DOMFullNode *node = (GF_DOMFullNode*)n; + GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes; + while (att) { + if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) { + if (att->data) free(att->data); + att->data = strdup(val); + dom_node_changed(n, 0, NULL); + goto exit; + } + prev = att; + att = (GF_DOMFullAttribute *) att->next; + } + /*create new att*/ + GF_SAFEALLOC(att, GF_DOMFullAttribute); + att->name = strdup(name); + att->data = strdup(val); + if (prev) prev->next = (GF_DOMAttribute*) att; + else node->attributes = (GF_DOMAttribute*) att; + goto exit; + } + + if (n->sgprivate->tag==TAG_DOMText) { + goto exit; + } + + if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) { + GF_FieldInfo info; + u32 ns_code = 0; + if (ns) { + ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns); + } else { + ns_code = gf_xml_get_element_namespace(n); + } + if (gf_node_get_attribute_by_name(n, name, ns_code, 1, 1, &info)==GF_OK) { + gf_svg_parse_attribute(n, &info, val, 0); + + if (info.fieldType==SVG_ID_datatype) { + gf_svg_parse_element_id(n, *(SVG_String*)info.far_ptr, 0); + } + dom_node_changed(n, 0, &info); + goto exit; + } + } +exit: + if (ns) free(ns); + return JS_TRUE; +} + + +static JSBool xml_element_elements_by_tag(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + DOMNodeList *nl; + JSObject *new_obj; + char *name; + GF_Node *n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + + if (!argc || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + + name = JSVAL_GET_STRING(argv[0]); + /*NS version*/ + if (argc==2) { + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + name = JSVAL_GET_STRING(argv[1]); + } + GF_SAFEALLOC(nl, DOMNodeList); + if (name && !strcmp(name, "*")) name = NULL; + xml_doc_gather_nodes((GF_ParentNode*)n, name, nl); + new_obj = JS_NewObject(c, &dom_rt->domNodeListClass, 0, 0); + JS_SetPrivate(c, new_obj, nl); + *rval = OBJECT_TO_JSVAL(new_obj); + return JS_TRUE; +} + +static JSBool xml_element_set_id(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + const char *node_name; + u32 node_id; + char *name; + Bool is_id; + GF_Node *n = dom_get_node(c, obj); + if (!n) return JS_TRUE; + + if ((argc<2) || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + name = JSVAL_GET_STRING(argv[0]); + + /*NS version*/ + if (argc==2) { + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + name = JSVAL_GET_STRING(argv[1]); + is_id = (JSVAL_TO_BOOLEAN(argv[2])==JS_TRUE) ? 1 : 0; + } else { + is_id = (JSVAL_TO_BOOLEAN(argv[1])==JS_TRUE) ? 1 : 0; + } + node_name = gf_node_get_name_and_id(n, &node_id); + if (node_id && is_id) { + /*we only support ONE ID per node*/ + return JS_TRUE; + } + if (is_id) { + if (!name) return JS_TRUE; + gf_node_set_id(n, gf_sg_get_max_node_id(n->sgprivate->scenegraph) + 1, strdup(name) ); + } else if (node_id) { + gf_node_remove_id(n); + } + return JS_TRUE; +} + + +/*dom3 character/text/comment*/ + +static JSBool dom_text_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + u32 prop_id; + GF_DOMText *txt = (GF_DOMText*)dom_get_node(c, obj); + if (!txt || (txt->sgprivate->tag != TAG_DOMText)) return JS_TRUE; + if (!JSVAL_IS_INT(id)) return JS_TRUE; + prop_id = JSVAL_TO_INT(id); + + switch (prop_id) { + case 1: /*"data"*/ + if (txt->textContent) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, txt->textContent ) ); + else *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "") ); + return JS_TRUE; + case 2:/*"length"*/ + *vp = INT_TO_JSVAL(txt->textContent ? strlen(txt->textContent) : 0); + return JS_TRUE; + case 3:/*"isElementContentWhitespace"*/ + *vp = BOOLEAN_TO_JSVAL(JS_FALSE); + return JS_TRUE; + case 4:/*"wholeText"*/ + /*FIXME - this is wrong*/ + *vp = INT_TO_JSVAL(txt->textContent ? strlen(txt->textContent) : 0); + return JS_TRUE; + } + return JS_TRUE; +} +static JSBool dom_text_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + u32 prop_id; + GF_DOMText *txt = (GF_DOMText*)dom_get_node(c, obj); + if (!txt || (txt->sgprivate->tag != TAG_DOMText)) return JS_TRUE; + if (!JSVAL_IS_INT(id)) return JS_TRUE; + prop_id = JSVAL_TO_INT(id); + + switch (prop_id) { + case 1: /*"data"*/ + if (txt->textContent) free(txt->textContent); + txt->textContent = NULL; + if (JSVAL_CHECK_STRING(*vp)) { + char *str = js_get_utf8(*vp); + txt->textContent = str ? str : strdup("" ); + } + dom_node_changed((GF_Node*)txt, 0, NULL); + return JS_TRUE; + /*the rest is read-only*/ + } + return JS_TRUE; +} + +static JSBool event_stop_propagation(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_DOM_Event *evt = JS_GetPrivate(c, obj); + if (!evt) return JS_TRUE; + evt->event_phase |= GF_DOM_EVENT_PHASE_CANCEL; + return JS_TRUE; +} +static JSBool event_stop_immediate_propagation(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_DOM_Event *evt = JS_GetPrivate(c, obj); + if (!evt) return JS_TRUE; + evt->event_phase |= GF_DOM_EVENT_PHASE_CANCEL_ALL; + return JS_TRUE; +} +static JSBool event_prevent_default(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_DOM_Event *evt = JS_GetPrivate(c, obj); + if (!evt) return JS_TRUE; + evt->event_phase |= GF_DOM_EVENT_PHASE_PREVENT; + return JS_TRUE; +} + +static JSBool event_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + JSString *s; + GF_DOM_Event *evt = JS_GetPrivate(c, obj); + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + case 0: /*type*/ + s = JS_NewStringCopyZ(c, gf_dom_event_get_name(evt->type) ); + *vp = STRING_TO_JSVAL( s ); + break; + case 1: /*target*/ + if (evt->is_vrml) return JS_TRUE; + switch (evt->target_type) { + case GF_DOM_EVENT_NODE: + *vp = dom_element_construct(c, (GF_Node*) evt->target); + break; + case GF_DOM_EVENT_DOCUMENT: + *vp = dom_document_construct(c, (GF_SceneGraph *) evt->target); + break; + } + return JS_TRUE; + case 2: /*currentTarget*/ + if (evt->is_vrml) return JS_TRUE; + switch (evt->currentTarget->ptr_type) { + case GF_DOM_EVENT_NODE: + *vp = dom_element_construct(c, (GF_Node*) evt->currentTarget->ptr); + break; + case GF_DOM_EVENT_DOCUMENT: + *vp = dom_document_construct(c, (GF_SceneGraph *) evt->currentTarget->ptr); + break; + } + return JS_TRUE; + /*eventPhase */ + case 3: + *vp = INT_TO_JSVAL( (evt->event_phase & 0x3) ); return JS_TRUE; + case 4: /*bubbles*/ + *vp = BOOLEAN_TO_JSVAL(evt->bubbles ? JS_TRUE : JS_FALSE); return JS_TRUE; + case 5: /*cancelable*/ + *vp = BOOLEAN_TO_JSVAL(evt->cancelable ? JS_TRUE : JS_FALSE); return JS_TRUE; + case 6: /*namespaceURI*/ + *vp = JSVAL_NULL; + return JS_TRUE; + /*timeStamp */ + case 7: + *vp = JSVAL_VOID; + return JS_TRUE; + /*defaultPrevented */ + case 8: + *vp = BOOLEAN_TO_JSVAL((evt->event_phase & GF_DOM_EVENT_PHASE_PREVENT) ? JS_TRUE : JS_FALSE); return JS_TRUE; + + + case 20:/*detail*/ + *vp = INT_TO_JSVAL(evt->detail); return JS_TRUE; + + case 25: /*data*/ + { + u32 len; + s16 txt[2]; + const u16 *srcp; + char szData[5];; + txt[0] = evt->detail; + txt[1] = 0; + srcp = txt; + len = gf_utf8_wcstombs(szData, 5, &srcp); + szData[len] = 0; + s = JS_NewStringCopyZ(c, szData); + *vp = STRING_TO_JSVAL( s ); + } + return JS_TRUE; + + case 30:/*screenX*/ + *vp = INT_TO_JSVAL(evt->screenX); return JS_TRUE; + case 31: /*screenY*/ + *vp = INT_TO_JSVAL(evt->screenY); return JS_TRUE; + case 32: /*clientX*/ + *vp = INT_TO_JSVAL(evt->clientX); return JS_TRUE; + case 33: /*clientY*/ + *vp = INT_TO_JSVAL(evt->clientY); return JS_TRUE; + case 34:/*button*/ + *vp = INT_TO_JSVAL(evt->button); return JS_TRUE; + case 35:/*relatedTarget*/ + if (evt->is_vrml) return JS_TRUE; + *vp = dom_element_construct(c, evt->relatedTarget); + return JS_TRUE; + case 36:/*wheelDelta*/ + *vp = INT_TO_JSVAL(FIX2INT(evt->new_scale) ); return JS_TRUE; + + + /*DOM3 event keyIndentifier*/ + case 40: + s = JS_NewStringCopyZ(c, gf_dom_get_key_name(evt->detail) ); + *vp = STRING_TO_JSVAL( s ); + return JS_TRUE; + /*Mozilla keyChar, charCode: wrap up to same value*/ + case 41: + case 42: + *vp = INT_TO_JSVAL(evt->detail); return JS_TRUE; + + /*MAE*/ + case 54:/*bufferLevelValid*/ + if (!evt->mae) return JS_TRUE; + *vp = BOOLEAN_TO_JSVAL( evt->mae->bufferValid ? JS_TRUE : JS_FALSE); + return JS_TRUE; + case 55:/*bufferLevel*/ + if (!evt->mae) return JS_TRUE; + *vp = INT_TO_JSVAL( evt->mae->level); + return JS_TRUE; + case 56:/*bufferRemainingTime*/ + if (!evt->mae) return JS_TRUE; + *vp = DOUBLE_TO_JSVAL( JS_NewDouble(c, evt->mae->remaining_time) ); + return JS_TRUE; + case 57:/*status*/ + if (!evt->mae) return JS_TRUE; + *vp = INT_TO_JSVAL( evt->mae->status); + return JS_TRUE; + + /*VRML ones*/ + case 60:/*width*/ + *vp = DOUBLE_TO_JSVAL( JS_NewDouble(c, evt->screen_rect.width) ); + return JS_TRUE; + case 61:/*height*/ + *vp = DOUBLE_TO_JSVAL( JS_NewDouble(c, evt->screen_rect.height) ); + return JS_TRUE; + case 62:/*h_translation*/ + *vp = DOUBLE_TO_JSVAL( JS_NewDouble(c, evt->new_translate.x) ); + return JS_TRUE; + case 63:/*v_translation*/ + *vp = DOUBLE_TO_JSVAL( JS_NewDouble(c, evt->new_translate.y) ); + return JS_TRUE; + + + default: return JS_TRUE; + } + } + return JS_TRUE; +} + +/************************************************************ + * + * xmlHttpRequest implementation + * + *************************************************************/ + +typedef struct +{ + JSContext *c; + JSObject *_this; + JSFunction *onreadystatechange; + + u32 readyState; + Bool async; + /*header/header-val, terminated by NULL*/ + char **headers; + u32 cur_header; + char **recv_headers; + + char *method, *url; + GF_DownloadSession *sess; + char *data; + u32 size; + GF_Err ret_code; + u32 html_status; + char *statusText; + + GF_SAXParser *sax; + GF_List *node_stack; + /*dom graph*/ + GF_SceneGraph *document; +} XMLHTTPContext; + +static void xml_http_append_send_header(XMLHTTPContext *ctx, char *hdr, char *val) +{ + u32 nb_hdr = 0; + if (!hdr) return; + + if (ctx->headers) { + nb_hdr = 0; + while (ctx->headers && ctx->headers[nb_hdr]) { + if (stricmp(ctx->headers[nb_hdr], hdr)) { + nb_hdr+=2; + continue; + } + /*ignore these ones*/ + if (!stricmp(hdr, "Accept-Charset") + || !stricmp(hdr, "Accept-Encoding") + || !stricmp(hdr, "Content-Length") + || !stricmp(hdr, "Expect") + || !stricmp(hdr, "Date") + || !stricmp(hdr, "Host") + || !stricmp(hdr, "Keep-Alive") + || !stricmp(hdr, "Referer") + || !stricmp(hdr, "TE") + || !stricmp(hdr, "Trailer") + || !stricmp(hdr, "Transfer-Encoding") + || !stricmp(hdr, "Upgrade") + ) { + return; + } + + /*replace content for these ones*/ + if (!stricmp(hdr, "Authorization") + || !stricmp(hdr, "Content-Base") + || !stricmp(hdr, "Content-Location") + || !stricmp(hdr, "Content-MD5") + || !stricmp(hdr, "Content-Range") + || !stricmp(hdr, "Content-Type") + || !stricmp(hdr, "Content-Version") + || !stricmp(hdr, "Delta-Base") + || !stricmp(hdr, "Depth") + || !stricmp(hdr, "Destination") + || !stricmp(hdr, "ETag") + || !stricmp(hdr, "From") + || !stricmp(hdr, "If-Modified-Since") + || !stricmp(hdr, "If-Range") + || !stricmp(hdr, "If-Unmodified-Since") + || !stricmp(hdr, "Max-Forwards") + || !stricmp(hdr, "MIME-Version") + || !stricmp(hdr, "Overwrite") + || !stricmp(hdr, "Proxy-Authorization") + || !stricmp(hdr, "SOAPAction") + || !stricmp(hdr, "Timeout") ) { + free(ctx->headers[nb_hdr+1]); + ctx->headers[nb_hdr+1] = strdup(val); + return; + } + /*append value*/ + else { + char *new_val = malloc(sizeof(char) * (strlen(ctx->headers[nb_hdr+1])+strlen(val)+3)); + sprintf(new_val, "%s, %s", ctx->headers[nb_hdr+1], val); + free(ctx->headers[nb_hdr+1]); + ctx->headers[nb_hdr+1] = new_val; + return; + } + nb_hdr+=2; + } + } + nb_hdr = 0; + if (ctx->headers) { + while (ctx->headers[nb_hdr]) nb_hdr+=2; + } + ctx->headers = realloc(ctx->headers, sizeof(char*)*(nb_hdr+3)); + ctx->headers[nb_hdr] = strdup(hdr); + ctx->headers[nb_hdr+1] = strdup(val ? val : ""); + ctx->headers[nb_hdr+2] = NULL; +} + +static void xml_http_reset_recv_hdr(XMLHTTPContext *ctx) +{ + u32 nb_hdr = 0; + if (ctx->recv_headers) { + while (ctx->recv_headers[nb_hdr]) { + free(ctx->recv_headers[nb_hdr]); + free(ctx->recv_headers[nb_hdr+1]); + nb_hdr+=2; + } + free(ctx->recv_headers); + ctx->recv_headers = NULL; + } +} +static void xml_http_append_recv_header(XMLHTTPContext *ctx, char *hdr, char *val) +{ + u32 nb_hdr = 0; + if (ctx->recv_headers) { + while (ctx->recv_headers[nb_hdr]) nb_hdr+=2; + } + ctx->recv_headers = realloc(ctx->recv_headers, sizeof(char*)*(nb_hdr+3)); + ctx->recv_headers[nb_hdr] = strdup(hdr); + ctx->recv_headers[nb_hdr+1] = strdup(val ? val : ""); + ctx->recv_headers[nb_hdr+2] = NULL; +} + + +static void xml_http_reset(XMLHTTPContext *ctx) +{ + u32 nb_hdr = 0; + if (ctx->method) { free(ctx->method); ctx->method = NULL; } + if (ctx->url) { free(ctx->url); ctx->url = NULL; } + + xml_http_reset_recv_hdr(ctx); + if (ctx->headers) { + while (ctx->headers[nb_hdr]) { + free(ctx->headers[nb_hdr]); + free(ctx->headers[nb_hdr+1]); + nb_hdr+=2; + } + free(ctx->headers); + ctx->headers = NULL; + } + if (ctx->sess) { + gf_dm_sess_del(ctx->sess); + ctx->sess = NULL; + } + if (ctx->data) { + free(ctx->data); + ctx->data = NULL; + } + if (ctx->statusText) { + free(ctx->statusText); + ctx->statusText = NULL; + } + if (ctx->url) { + free(ctx->url); + ctx->url = NULL; + } + if (ctx->sax) { + gf_xml_sax_del(ctx->sax); + ctx->sax = NULL; + } + if (ctx->node_stack) { + gf_list_del(ctx->node_stack); + ctx->node_stack = NULL; + } + if (ctx->document) { + gf_node_unregister(ctx->document->RootNode, NULL); + /*we're sure the graph is a "nomade" one since we initially put the refcount to 1 ourselves*/ + ctx->document->reference_count--; + if (!ctx->document->reference_count) { + gf_sg_del(ctx->document); + } + } + ctx->document = NULL; + ctx->size = 0; + ctx->async = 0; + ctx->readyState = 0; + ctx->cur_header = 0; + ctx->ret_code = 0; + ctx->html_status = 0; +} + +static void xml_http_finalize(JSContext *c, JSObject *obj) +{ + XMLHTTPContext *ctx; + if (!JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return; + ctx = (XMLHTTPContext *)JS_GetPrivate(c, obj); + if (ctx) { + xml_http_reset(ctx); + free(ctx); + } +} +static JSBool xml_http_constructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + XMLHTTPContext *p; + if (!JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE; + GF_SAFEALLOC(p, XMLHTTPContext); + p->c = c; + p->_this = obj; + JS_SetPrivate(c, obj, p); + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static void xml_http_state_change(XMLHTTPContext *ctx) +{ + jsval rval; + //GF_SceneGraph *scene = (GF_SceneGraph *) xml_get_scenegraph(ctx->c); + if (ctx->onreadystatechange) + JS_CallFunction(ctx->c, ctx->_this, ctx->onreadystatechange, 0, NULL, &rval); + + /*todo - fire event*/ +} + + +static JSBool xml_http_open(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *val; + GF_JSAPIParam par; + XMLHTTPContext *ctx; + GF_SceneGraph *scene; + + if (!JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE; + ctx = (XMLHTTPContext *)JS_GetPrivate(c, obj); + if (!ctx) return JS_TRUE; + + /*reset*/ + if (ctx->readyState) xml_http_reset(ctx); + + if (argc<2) return JS_TRUE; + /*method is a string*/ + if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + /*url is a string*/ + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + + xml_http_reset(ctx); + val = JSVAL_GET_STRING(argv[0]); + if (strcmp(val, "GET") && strcmp(val, "POST") && strcmp(val, "HEAD") + && strcmp(val, "PUT") && strcmp(val, "DELETE") && strcmp(val, "OPTIONS") ) + return JS_TRUE; + + ctx->method = strdup(val); + + val = JSVAL_GET_STRING(argv[1]); + /*concatenate URL*/ + scene = xml_get_scenegraph(c); + while (scene->pOwningProto && scene->parent_scene) scene = scene->parent_scene; + ScriptAction(scene, GF_JSAPI_OP_GET_SCENE_URI, scene->RootNode, &par); + + if (par.uri.url) ctx->url = gf_url_concatenate(par.uri.url, val); + if (!ctx->url) ctx->url = strdup(val); + + /*async defaults to true*/ + ctx->async = 1; + if (argc>2) { + ctx->async = (JSVAL_TO_BOOLEAN(argv[2])==JS_TRUE) ? 1 : 0; + if (argc>3) { + if (!JSVAL_CHECK_STRING(argv[3])) return JS_TRUE; + val = JSVAL_GET_STRING(argv[3]); + /*TODO*/ + if (argc>4) { + if (!JSVAL_CHECK_STRING(argv[3])) return JS_TRUE; + val = JSVAL_GET_STRING(argv[3]); + /*TODO*/ + } + } + } + /*OPEN success*/ + ctx->readyState = 1; + xml_http_state_change(ctx); + return JS_TRUE; +} + +static JSBool xml_http_set_header(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *hdr, *val; + XMLHTTPContext *ctx; + if (!JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE; + ctx = (XMLHTTPContext *)JS_GetPrivate(c, obj); + if (!ctx) return JS_TRUE; + + if (ctx->readyState!=1) return JS_TRUE; + if (argc!=2) return JS_TRUE; + if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + + hdr = JSVAL_GET_STRING(argv[0]); + val = JSVAL_GET_STRING(argv[1]); + xml_http_append_send_header(ctx, hdr, val); + return JS_TRUE; +} +static void xml_http_sax_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + u32 i; + GF_DOMFullAttribute *prev = NULL; + GF_DOMFullNode *par; + XMLHTTPContext *ctx = (XMLHTTPContext *)sax_cbck; + GF_DOMFullNode *node = (GF_DOMFullNode *) gf_node_new(ctx->document, TAG_DOMFullNode); + + node->name = strdup(node_name); + for (i=0; idocument) + 1; + gf_node_set_id((GF_Node *)node, id, attributes[i].value); + } else { + GF_DOMFullAttribute *att; + GF_SAFEALLOC(att, GF_DOMFullAttribute); + att->tag = TAG_DOM_ATT_any; + att->name = strdup(attributes[i].name); + att->data = strdup(attributes[i].value); + if (prev) prev->next = (GF_DOMAttribute*)att; + else node->attributes = (GF_DOMAttribute*)att; + prev = att; + } + } + par = gf_list_last(ctx->node_stack); + gf_node_register((GF_Node*)node, (GF_Node*)par); + if (par) { + gf_node_list_add_child(&par->children, (GF_Node*)node); + } else { + assert(!ctx->document->RootNode); + ctx->document->RootNode = (GF_Node*)node; + } + gf_list_add(ctx->node_stack, node); +} + +static void xml_http_sax_end(void *sax_cbck, const char *node_name, const char *name_space) +{ + XMLHTTPContext *ctx = (XMLHTTPContext *)sax_cbck; + GF_DOMFullNode *par = gf_list_last(ctx->node_stack); + if (par) { + /*depth mismatch*/ + if (strcmp(par->name, node_name)) return; + gf_list_rem_last(ctx->node_stack); + } +} +static void xml_http_sax_text(void *sax_cbck, const char *content, Bool is_cdata) +{ + XMLHTTPContext *ctx = (XMLHTTPContext *)sax_cbck; + GF_DOMFullNode *par = gf_list_last(ctx->node_stack); + if (par) { + u32 i, len; + GF_DOMText *txt; + /*basic check, remove all empty text nodes*/ + len = strlen(content); + for (i=0; itype = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR; + } +} + +static void xml_http_on_data(void *usr_cbk, GF_NETIO_Parameter *parameter) +{ + XMLHTTPContext *ctx = (XMLHTTPContext *)usr_cbk; + + /*if session not set, we've had an abort*/ + if (!ctx->sess) return; + + switch (parameter->msg_type) { + case GF_NETIO_SETUP: + /*nothing to do*/ + return; + case GF_NETIO_CONNECTED: + /*nothing to do*/ + return; + case GF_NETIO_WAIT_FOR_REPLY: + /*reset send() state (data, current header) and prepare recv headers*/ + if (ctx->data) free(ctx->data); + ctx->data = NULL; + ctx->size = 0; + ctx->cur_header = 0; + ctx->html_status = 0; + if (ctx->statusText) { + free(ctx->statusText); + ctx->statusText = NULL; + } + xml_http_reset_recv_hdr(ctx); + ctx->readyState = 2; + xml_http_state_change(ctx); + return; + /*this is signaled sent AFTER headers*/ + case GF_NETIO_PARSE_REPLY: + ctx->html_status = parameter->reply; + if (parameter->value) { + ctx->statusText = strdup(parameter->value); + } + ctx->readyState = 3; + xml_http_state_change(ctx); + return; + + case GF_NETIO_GET_METHOD: + parameter->name = ctx->method; + return; + case GF_NETIO_GET_HEADER: + if (ctx->headers && ctx->headers[2*ctx->cur_header]) { + parameter->name = ctx->headers[2*ctx->cur_header]; + parameter->value = ctx->headers[2*ctx->cur_header+1]; + ctx->cur_header++; + } + return; + case GF_NETIO_GET_CONTENT: + if (ctx->data) { + parameter->data = ctx->data; + parameter->size = strlen(ctx->data); + } + return; + case GF_NETIO_PARSE_HEADER: + xml_http_append_recv_header(ctx, parameter->name, parameter->value); + /*prepare DOM*/ + if (strcmp(parameter->name, "Content-Type")) return; + if (!strncmp(parameter->value, "application/xml", 15) + || !strncmp(parameter->value, "text/xml", 8) + || strstr(parameter->value, "+xml") + || !strncmp(parameter->value, "text/plain", 10) + ) { + assert(!ctx->sax); + ctx->sax = gf_xml_sax_new(xml_http_sax_start, xml_http_sax_end, xml_http_sax_text, ctx); + ctx->node_stack = gf_list_new(); + ctx->document = gf_sg_new(); + /*mark this doc as "nomade", and let it leave until all references to it are destroyed*/ + ctx->document->reference_count = 1; + } + return; + case GF_NETIO_DATA_EXCHANGE: + if (parameter->data && parameter->size) { + if (ctx->sax) { + GF_Err e; + if (!ctx->size) e = gf_xml_sax_init(ctx->sax, parameter->data); + else e = gf_xml_sax_parse(ctx->sax, parameter->data); + if (e) { + gf_xml_sax_del(ctx->sax); + ctx->sax = NULL; + } + } + ctx->data = realloc(ctx->data, sizeof(char)*(ctx->size+parameter->size+1)); + memcpy(ctx->data + ctx->size, parameter->data, sizeof(char)*parameter->size); + ctx->size += parameter->size; + ctx->data[ctx->size] = 0; + } + return; + case GF_NETIO_DATA_TRANSFERED: + break; + case GF_NETIO_DISCONNECTED: + return; + case GF_NETIO_STATE_ERROR: + ctx->ret_code = parameter->error; + break; + } + + /*if we get here, destroy downloader - FIXME we'll need a mutex here for sync case...*/ + if (ctx->sess) { + gf_dm_sess_del(ctx->sess); + ctx->sess = NULL; + } + + /*error, complete reset*/ + if (parameter->error) { + xml_http_reset(ctx); + } + /*but stay in loaded mode*/ + ctx->readyState = 4; + xml_http_state_change(ctx); +} + +static JSBool xml_http_send(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_JSAPIParam par; + GF_SceneGraph *scene; + char *data = NULL; + XMLHTTPContext *ctx; + GF_Err e; + + if (!JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE; + ctx = (XMLHTTPContext *)JS_GetPrivate(c, obj); + if (!ctx) return JS_TRUE; + + if (ctx->readyState!=1) return JS_TRUE; + if (ctx->sess) return JS_TRUE; + + if (argc) { + if (JSVAL_IS_NULL(argv[0])) { + } else if (JSVAL_IS_OBJECT(argv[0])) { +// if (!JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &documentClass, NULL) ) return JS_TRUE; + + /*NOT SUPPORTED YET, we must serialize the sg*/ + return JS_TRUE; + } else { + if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + data = JSVAL_GET_STRING(argv[0]); + } + } + + scene = xml_get_scenegraph(c); + par.dnld_man = NULL; + ScriptAction(scene, GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, NULL, &par); + if (!par.dnld_man) return JS_TRUE; + + /*reset previous text*/ + if (ctx->data) free(ctx->data); + + ctx->data = data ? strdup(data) : NULL; + ctx->sess = gf_dm_sess_new(par.dnld_man, ctx->url, GF_NETIO_SESSION_NOT_CACHED, xml_http_on_data, ctx, &e); + if (!ctx->sess) return JS_TRUE; + + /*just wait for destruction*/ + if (!ctx->async) { + while (ctx->sess) { + gf_sleep(20); + } + } + return JS_TRUE; +} + +static JSBool xml_http_abort(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_DownloadSession *sess; + XMLHTTPContext *ctx; + if (!JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE; + ctx = (XMLHTTPContext *)JS_GetPrivate(c, obj); + if (!ctx) return JS_TRUE; + + sess = ctx->sess; + ctx->sess = NULL; + if (sess) gf_dm_sess_del(sess); + + xml_http_reset(ctx); + return JS_TRUE; +} + +static JSBool xml_http_get_all_headers(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 nb_hdr; + char szVal[4096]; + XMLHTTPContext *ctx; + if (!JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE; + ctx = (XMLHTTPContext *)JS_GetPrivate(c, obj); + if (!ctx) return JS_TRUE; + + /*must be received or loaded*/ + if (ctx->readyState<3) return JS_TRUE; + szVal[0] = 0; + nb_hdr = 0; + if (ctx->recv_headers) { + while (ctx->recv_headers[nb_hdr]) { + if (szVal[0]) strcat(szVal, "\r\n"); + strcat(szVal, ctx->recv_headers[nb_hdr]); + strcat(szVal, ": "); + strcat(szVal, ctx->recv_headers[nb_hdr+1]); + nb_hdr+=2; + } + } + if (!szVal[0]) { + *rval = JSVAL_VOID; + } else { + JSString *s; + s = JS_NewStringCopyZ(c, szVal); + *rval = STRING_TO_JSVAL( s ); + } + + return JS_TRUE; +} + +static JSBool xml_http_get_header(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 nb_hdr; + char *hdr; + char szVal[2048]; + XMLHTTPContext *ctx; + if (!argc || !JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE; + ctx = (XMLHTTPContext *)JS_GetPrivate(c, obj); + if (!ctx) return JS_TRUE; + + if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + hdr = JSVAL_GET_STRING(argv[0]); + /*must be received or loaded*/ + if (ctx->readyState<3) return JS_TRUE; + szVal[0] = 0; + nb_hdr = 0; + if (ctx->recv_headers) { + while (ctx->recv_headers[nb_hdr]) { + if (!strcmp(ctx->recv_headers[nb_hdr], hdr)) { + if (szVal[0]) strcat(szVal, ", "); + strcat(szVal, ctx->recv_headers[nb_hdr+1]); + } + nb_hdr+=2; + } + } + if (!szVal[0]) { + *rval = JSVAL_VOID; + } else { + JSString *s; + s = JS_NewStringCopyZ(c, szVal); + *rval = STRING_TO_JSVAL( s ); + } + + return JS_TRUE; +} + +static JSBool xml_http_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + JSString *s; + XMLHTTPContext *ctx; + if (!JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE; + ctx = (XMLHTTPContext *)JS_GetPrivate(c, obj); + if (!ctx) return JS_TRUE; + + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + /*onreadystatechange*/ + case 0: + if (ctx->onreadystatechange) { + *vp = OBJECT_TO_JSVAL(ctx->onreadystatechange); + } else { + *vp = JSVAL_VOID; + } + return JS_TRUE; + /*readyState*/ + case 1: + *vp = INT_TO_JSVAL(ctx->readyState); return JS_TRUE; + /*responseText*/ + case 2: + if (ctx->readyState<3) return JS_TRUE; + if (ctx->data) { + s = JS_NewStringCopyZ(c, ctx->data); + *vp = STRING_TO_JSVAL( s ); + } else { + *vp = JSVAL_VOID; + } + return JS_TRUE; + /*responseXML*/ + case 3: + if (ctx->readyState<3) return JS_TRUE; + *vp = dom_document_construct(c, ctx->document); + return JS_TRUE; + /*status*/ + case 4: + *vp = INT_TO_JSVAL(ctx->html_status); return JS_TRUE; + /*statusText*/ + case 5: + if (ctx->statusText) { + s = JS_NewStringCopyZ(c, ctx->statusText); + *vp = STRING_TO_JSVAL( s ); + } else { + *vp = JSVAL_VOID; + } + return JS_TRUE; + default: + return JS_TRUE; + } + } + return JS_PropertyStub(c, obj, id, vp); + return JS_TRUE; +} + +static JSBool xml_http_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + XMLHTTPContext *ctx; + if (!JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE; + ctx = (XMLHTTPContext *)JS_GetPrivate(c, obj); + if (!ctx) return JS_TRUE; + + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + /*onreadystatechange*/ + case 0: + break; + /*all other properties are read-only*/ + default: + return JS_TRUE; + } + if (JSVAL_IS_VOID(*vp)) { + ctx->onreadystatechange = NULL; + return JS_TRUE; + } + else if (JSVAL_CHECK_STRING(*vp)) { + jsval fval; + char *callback = JSVAL_GET_STRING(*vp); + if (! JS_LookupProperty(c, JS_GetGlobalObject(c), callback, &fval)) return JS_TRUE; + ctx->onreadystatechange = JS_ValueToFunction(c, fval); + if (ctx->onreadystatechange) return JS_TRUE; + } else if (JSVAL_IS_OBJECT(*vp)) { + ctx->onreadystatechange = JS_ValueToFunction(c, *vp); + if (ctx->onreadystatechange) return JS_TRUE; + } + return JS_TRUE; + } + return JS_TRUE; +} + + +static JSBool dcci_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_DOMFullAttribute *att; + GF_ChildNodeItem *child; + GF_DOMFullNode *n; + char *value; + JSString *s; + if (!JS_InstanceOf(c, obj, &dom_rt->DCCIClass, NULL) ) return JS_TRUE; + n = (GF_DOMFullNode*) dom_get_node(c, obj); + if (!n) return JS_TRUE; + + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + /*value*/ + case 0: + child = n->children; + while (child) { + if (child->node && (child->node->sgprivate->tag==TAG_DOMText)) { + GF_DOMText *txt = (GF_DOMText *)child->node; + if (txt->type==GF_DOM_TEXT_REGULAR) { + s = JS_NewStringCopyZ(c, txt->textContent); + *vp = STRING_TO_JSVAL( s ); + return JS_TRUE; + } + } + child = child->next; + } + *vp = JSVAL_NULL; + return JS_TRUE; + /*valueType*/ + case 1: + value = "DOMString"; + att = (GF_DOMFullAttribute*) n->attributes; + while (att) { + if (att->name && !strcmp(att->name, "valueType") && att->data) { + value = (char *)att->data; + break; + } + att = (GF_DOMFullAttribute*) att->next; + } + s = JS_NewStringCopyZ(c, value); + *vp = STRING_TO_JSVAL( s ); + return JS_TRUE; + /*propertyType*/ + case 2: + value = "DOMString"; + att = (GF_DOMFullAttribute*) n->attributes; + while (att) { + if (att->name && !strcmp(att->name, "propertyType") && att->data) { + value = (char *)att->data; + break; + } + att = (GF_DOMFullAttribute*) att->next; + } + s = JS_NewStringCopyZ(c, value); + *vp = STRING_TO_JSVAL( s ); + return JS_TRUE; + /*readOnly*/ + case 3: + att = (GF_DOMFullAttribute*) n->attributes; + while (att) { + if (att->name && !strcmp(att->name, "readOnly") && att->data && !strcmp(att->data, "true")) { + *vp = BOOLEAN_TO_JSVAL(JS_TRUE); + return JS_TRUE; + } + att = (GF_DOMFullAttribute*) att->next; + } + *vp = BOOLEAN_TO_JSVAL(JS_FALSE); + return JS_TRUE; + /*DCCIMetadataInterfaceType*/ + case 4: + *vp = JSVAL_NULL; + return JS_TRUE; + /*DCCIMetadataInterface*/ + case 5: + *vp = JSVAL_NULL; + return JS_TRUE; + /*version*/ + case 6: + s = JS_NewStringCopyZ(c, "1.0"); + *vp = STRING_TO_JSVAL( s ); + return JS_TRUE; + default: + return JS_TRUE; + } + } + return JS_PropertyStub(c, obj, id, vp); +} + +static JSBool dcci_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_ChildNodeItem *child; + GF_DOMFullNode *n; + GF_DOM_Event evt; + char *str; + jsval readonly; + if (!JS_InstanceOf(c, obj, &dom_rt->DCCIClass, NULL) ) return JS_TRUE; + n = (GF_DOMFullNode*) dom_get_node(c, obj); + if (!n) return JS_TRUE; + + str = JSVAL_GET_STRING( *vp); + if (!str) return JS_TRUE; + + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + /*value*/ + case 0: + dcci_getProperty(c, obj, INT_TO_JSVAL(3), &readonly); + if (JSVAL_TO_BOOLEAN(readonly) == JS_TRUE) return JS_TRUE; + child = n->children; + while (child) { + if (child->node && (child->node->sgprivate->tag==TAG_DOMText)) { + GF_DOMText *txt = (GF_DOMText *)child->node; + if (txt->type==GF_DOM_TEXT_REGULAR) { + if (txt->textContent) free(txt->textContent); + txt->textContent = strdup(str); + break; + + } + } + child = child->next; + } + if (!child) + gf_dom_add_text_node((GF_Node*)n, strdup(str) ); + + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = GF_EVENT_DCCI_PROP_CHANGE; + evt.bubbles = 1; + evt.relatedNode = (GF_Node*)n; + gf_dom_event_fire((GF_Node*)n, &evt); + n->sgprivate->scenegraph->modified = 1; + return JS_TRUE; + /*propertyType*/ + case 2: + return JS_TRUE; + + /*all other properties are read-only*/ + default: + return JS_TRUE; + } + return JS_TRUE; + } + return JS_TRUE; +} + +Bool dcci_prop_lookup(GF_DOMFullNode *n, char *ns, char *name, Bool deep, Bool first) +{ + Bool ok = 1; + GF_ChildNodeItem *child = n->children; + /*ns mismatch*/ + if (strcmp(ns, "*") && n->ns && strcmp(ns, gf_sg_get_namespace(n->sgprivate->scenegraph, n->ns) )) + ok = 0; + /*name mismatch*/ + if (strcmp(name, "*") && n->name && strcmp(name, n->name)) + ok = 0; + + /*"Some DCCIProperty nodes may not have a value"*/ + if (ok) return 1; + + /*not found*/ + if (!first && !deep) return 0; + + while (child) { + if (child->node && (child->node->sgprivate->tag==TAG_DOMFullNode)) { + ok = dcci_prop_lookup((GF_DOMFullNode *)child->node, ns, name, deep, 0); + if (ok) return 1; + } + child = child->next; + } + return 0; +} + +static JSBool dcci_has_property(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_DOMFullNode *n; + Bool deep; + char *ns, *name; + if (!JS_InstanceOf(c, obj, &dom_rt->DCCIClass, NULL) ) return JS_TRUE; + n = (GF_DOMFullNode*) dom_get_node(c, obj); + if (!n) return JS_TRUE; + if (argc!=3) return JS_TRUE; + if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + ns = JSVAL_GET_STRING(argv[0]); + name = JSVAL_GET_STRING(argv[1]); + + deep = JSVAL_TO_BOOLEAN(argv[2]) ? 1 : 0; + deep = dcci_prop_lookup(n, ns, name, deep, 1); + *rval = BOOLEAN_TO_JSVAL(deep ? JS_TRUE : JS_FALSE); + + return JS_TRUE; +} + +void dcci_prop_collect(DOMNodeList *nl, GF_DOMFullNode *n, char *ns, char *name, Bool deep, Bool first) +{ + Bool ok = 1; + GF_ChildNodeItem *child = n->children; + /*ns mismatch*/ + if (strcmp(ns, "*") && n->ns && strcmp(ns, gf_sg_get_namespace(n->sgprivate->scenegraph, n->ns) )) + ok = 0; + /*name mismatch*/ + if (strcmp(name, "*") && n->name && strcmp(name, n->name)) + ok = 0; + + /*"Some DCCIProperty nodes may not have a value"*/ + if (ok) { + gf_node_register((GF_Node*)n, NULL); + if (n->sgprivate->scenegraph->reference_count) + n->sgprivate->scenegraph->reference_count++; + gf_node_list_add_child(&nl->child, (GF_Node*)n); + } + + /*not found*/ + if (!first && !deep) return; + + while (child) { + if (child->node && (child->node->sgprivate->tag==TAG_DOMFullNode)) { + dcci_prop_collect(nl, (GF_DOMFullNode *)child->node, ns, name, 1, 0); + } + child = child->next; + } +} + +static JSBool dcci_search_property(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *new_obj; + DOMNodeList *nl; + GF_DOMFullNode *n; + Bool deep; + char *ns, *name; + if (!JS_InstanceOf(c, obj, &dom_rt->DCCIClass, NULL) ) return JS_TRUE; + n = (GF_DOMFullNode*) dom_get_node(c, obj); + if (!n) return JS_TRUE; + if (argc!=4) return JS_TRUE; + if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + ns = JSVAL_GET_STRING(argv[0]); + name = JSVAL_GET_STRING(argv[1]); + /*todo - DCCI prop filter*/ + deep = JSVAL_TO_BOOLEAN(argv[3]) ? 1 : 0; + + GF_SAFEALLOC(nl, DOMNodeList); + dcci_prop_collect(nl, n, ns, name, deep, 1); + new_obj = JS_NewObject(c, &dom_rt->domNodeListClass, 0, 0); + JS_SetPrivate(c, new_obj, nl); + *rval = OBJECT_TO_JSVAL(new_obj); + return JS_TRUE; +} + +void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global) +{ + if (!dom_rt) { + GF_SAFEALLOC(dom_rt, GF_DOMRuntime); + JS_SETUP_CLASS(dom_rt->domNodeClass, "Node", JSCLASS_HAS_PRIVATE, dom_node_getProperty, dom_node_setProperty, dom_node_finalize); + JS_SETUP_CLASS(dom_rt->domDocumentClass, "Document", JSCLASS_HAS_PRIVATE, dom_document_getProperty, dom_document_setProperty, dom_document_finalize); + /*Element uses the same destructor as node*/ + JS_SETUP_CLASS(dom_rt->domElementClass, "Element", JSCLASS_HAS_PRIVATE, dom_element_getProperty, JS_PropertyStub, dom_node_finalize); + /*Text uses the same destructor as node*/ + JS_SETUP_CLASS(dom_rt->domTextClass, "Text", JSCLASS_HAS_PRIVATE, dom_text_getProperty, dom_text_setProperty, dom_node_finalize); + /*Event class*/ + JS_SETUP_CLASS(dom_rt->domEventClass , "Event", JSCLASS_HAS_PRIVATE, event_getProperty, JS_PropertyStub, JS_FinalizeStub); + + + JS_SETUP_CLASS(dom_rt->domNodeListClass, "NodeList", JSCLASS_HAS_PRIVATE, dom_nodelist_getProperty, dom_nodelist_setProperty, dom_nodelist_finalize); + JS_SETUP_CLASS(dom_rt->xmlHTTPRequestClass, "XMLHttpRequest", JSCLASS_HAS_PRIVATE, xml_http_getProperty, xml_http_setProperty, xml_http_finalize); + + JS_SETUP_CLASS(dom_rt->DCCIClass, "DCCI", JSCLASS_HAS_PRIVATE, dcci_getProperty, dcci_setProperty, dom_node_finalize); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] dom run-time allocated\n")); + } + dom_rt->nb_inst++; + + define_dom_exception(c, global); + + { + JSFunctionSpec nodeFuncs[] = { + {"insertBefore", xml_node_insert_before, 2}, + {"replaceChild", xml_node_replace_child, 2}, + {"removeChild", xml_node_remove_child, 1}, + {"appendChild", xml_node_append_child, 1}, + {"hasChildNodes", xml_node_has_children, 0}, + {"cloneNode", xml_clone_node, 1}, + {"normalize", xml_dom3_not_implemented, 0}, + {"isSupported", xml_dom3_not_implemented, 2}, + {"hasAttributes", xml_node_has_attributes, 0}, + {"compareDocumentPosition", xml_dom3_not_implemented, 1}, + {"isSameNode", xml_node_is_same_node, 1}, + {"lookupPrefix", xml_dom3_not_implemented, 1}, + {"isDefaultNamespace", xml_dom3_not_implemented, 1}, + {"lookupNamespaceURI", xml_dom3_not_implemented, 1}, + /*we don't support full node compare*/ + {"isEqualNode", xml_node_is_same_node, 1}, + {"getFeature", xml_dom3_not_implemented, 2}, + {"setUserData", xml_dom3_not_implemented, 3}, + {"getUserData", xml_dom3_not_implemented, 1}, + {0} + }; + JSPropertySpec nodeProps[] = { + {"nodeName", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"nodeValue", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"nodeType", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"parentNode", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"childNodes", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"firstChild", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"lastChild", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"previousSibling", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"nextSibling", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"attributes", 9, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"ownerDocument", 10, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"namespaceURI", 11, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"prefix", 12, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"localName", 13, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"baseURI", 14, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"textContent", 15, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {0} + }; + dom_rt->dom_node_proto = JS_InitClass(c, global, 0, &dom_rt->domNodeClass, 0, 0, nodeProps, nodeFuncs, 0, 0); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] node class initialized\n")); + + JS_DefineProperty(c, dom_rt->dom_node_proto, "ELEMENT_NODE", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, dom_rt->dom_node_proto, "TEXT_NODE", INT_TO_JSVAL(3), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, dom_rt->dom_node_proto, "CDATA_SECTION_NODE", INT_TO_JSVAL(4), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, dom_rt->dom_node_proto, "DOCUMENT_NODE", INT_TO_JSVAL(9), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + + } + { + JSFunctionSpec documentFuncs[] = { + {"createElement", xml_document_create_element, 1}, + {"createDocumentFragment", xml_dom3_not_implemented, 0}, + {"createTextNode", xml_document_create_text, 1}, + {"createComment", xml_dom3_not_implemented, 1}, + {"createCDATASection", xml_dom3_not_implemented, 1}, + {"createProcessingInstruction", xml_dom3_not_implemented, 2}, + {"createAttribute", xml_dom3_not_implemented, 1}, + {"createEntityReference", xml_dom3_not_implemented, 1}, + {"getElementsByTagName", xml_document_elements_by_tag, 1}, + {"importNode", xml_dom3_not_implemented, 2}, + {"createElementNS", xml_document_create_element, 2}, + {"createAttributeNS", xml_dom3_not_implemented, 2}, + {"getElementsByTagNameNS", xml_document_elements_by_tag, 2}, + {"getElementById", xml_document_element_by_id, 1}, + {"adoptNode", xml_dom3_not_implemented, 1}, + {"normalizeDocument", xml_dom3_not_implemented, 0}, + {"renameNode", xml_dom3_not_implemented, 3}, + /*eventTarget interface*/ + JS_DOM3_EVEN_TARGET_INTERFACE + /*DocumentEvent interface*/ + {"createEvent", xml_dom3_not_implemented, 1}, + {0} + }; + + JSPropertySpec documentProps[] = { + {"doctype", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"implementation", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"documentElement", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"inputEncoding", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"xmlEncoding", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"xmlStandalone", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"xmlVersion", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"strictErrorChecking", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"documentURI", 9, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"domConfig", 10, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {0} + }; + + dom_rt->dom_document_proto = JS_InitClass(c, global, dom_rt->dom_node_proto, &dom_rt->domDocumentClass, 0, 0, documentProps, documentFuncs, 0, 0); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] document class initialized\n")); + } + + { + JSFunctionSpec elementFuncs[] = { + {"getAttribute", xml_element_get_attribute, 1}, + {"setAttribute", xml_element_set_attribute, 2}, + {"removeAttribute", xml_element_remove_attribute, 1}, + {"getAttributeNS", xml_element_get_attribute, 2}, + {"setAttributeNS", xml_element_set_attribute, 3}, + {"removeAttributeNS", xml_element_remove_attribute, 2}, + {"hasAttribute", xml_element_has_attribute, 1}, + {"hasAttributeNS", xml_element_has_attribute, 2}, + {"getElementsByTagName", xml_element_elements_by_tag, 1}, + {"getElementsByTagNameNS", xml_element_elements_by_tag, 2}, + {"setIdAttribute", xml_element_set_id, 2}, + {"setIdAttributeNS", xml_element_set_id, 3}, + {"getAttributeNode", xml_dom3_not_implemented, 1}, + {"setAttributeNode", xml_dom3_not_implemented, 1}, + {"removeAttributeNode", xml_dom3_not_implemented, 1}, + {"getAttributeNodeNS", xml_dom3_not_implemented, 2}, + {"setAttributeNodeNS", xml_dom3_not_implemented, 1}, + {"setIdAttributeNode", xml_dom3_not_implemented, 2}, + /*eventTarget interface*/ + JS_DOM3_EVEN_TARGET_INTERFACE + {0} + }; + + JSPropertySpec elementProps[] = { + {"tagName", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"schemaTypeInfo", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {0} + }; + dom_rt->dom_element_proto = JS_InitClass(c, global, dom_rt->dom_node_proto, &dom_rt->domElementClass, 0, 0, elementProps, elementFuncs, 0, 0); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] element class initialized\n")); + } + + { + JSFunctionSpec textFuncs[] = { +#if 0 + {"substringData", xml_dom3_not_implemented, 2}, + {"appendData", xml_dom3_not_implemented, 1}, + {"insertData", xml_dom3_not_implemented, 2}, + {"deleteData", xml_dom3_not_implemented, 2}, + {"replaceData", xml_dom3_not_implemented, 3}, + /*text*/ + {"splitText", xml_dom3_not_implemented, 1}, + {"replaceWholeText", xml_dom3_not_implemented, 1}, +#endif + {0} + }; + JSPropertySpec textProps[] = { + {"data", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"length", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + /*text*/ + {"isElementContentWhitespace", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"wholeText", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {0} + }; + JS_InitClass(c, global, dom_rt->dom_node_proto, &dom_rt->domTextClass, 0, 0, textProps, textFuncs, 0, 0); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] text class initialized\n")); + } + + /*event API*/ + { + JSFunctionSpec eventFuncs[] = { + {"stopPropagation", event_stop_propagation, 0}, + {"stopImmediatePropagation", event_stop_immediate_propagation, 0}, + {"preventDefault", event_prevent_default, 0}, +#if 0 + {"initEvent", event_prevent_default, 3}, + {"initEventNS", event_prevent_default, 4}, +#endif + {0}, + }; + JSPropertySpec eventProps[] = { + {"type", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"target", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"currentTarget", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"eventPhase", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"bubbles", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"cancelable", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"timeStamp", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"namespaceURI", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"defaultPrevented", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + + /*UIEvent*/ + {"detail", 20, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + /*text, connectionEvent*/ + {"data", 25, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + /*MouseEvent*/ + {"screenX", 30, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"screenY", 31, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"clientX", 32, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"clientY", 33, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"button", 34, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"relatedTarget", 35, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + /*wheelEvent*/ + {"wheelDelta", 36, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + + /*keyboard*/ + {"keyIdentifier", 40, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"keyChar", 41, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"charCode", 42, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + + /*progress*/ + {"lengthComputable",50, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"typeArg", 51, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"loaded", 52, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"total", 53, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"bufferLevelValid", 54, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"bufferLevel", 55, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"bufferRemainingTime", 56, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"status", 57, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + + /*used by vrml*/ + {"width", 60, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"height", 61, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"translation_x", 62, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"translation_y", 63, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + + + {0}, + }; + dom_rt->dom_event_proto = JS_InitClass(c, global, 0, &dom_rt->domEventClass, 0, 0, eventProps, eventFuncs, 0, 0); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] Event class initialized\n")); + + JS_DefineProperty(c, dom_rt->dom_event_proto, "CAPTURING_PHASE", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, dom_rt->dom_event_proto, "AT_TARGET", INT_TO_JSVAL(2), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, dom_rt->dom_event_proto, "BUBBLING_PHASE", INT_TO_JSVAL(3), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + + JS_DefineProperty(c, dom_rt->dom_event_proto, "DOM_KEY_LOCATION_STANDARD ", INT_TO_JSVAL(0), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, dom_rt->dom_event_proto, "DOM_KEY_LOCATION_LEFT", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, dom_rt->dom_event_proto, "DOM_KEY_LOCATION_RIGHT", INT_TO_JSVAL(2), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, dom_rt->dom_event_proto, "DOM_KEY_LOCATION_NUMPAD", INT_TO_JSVAL(3), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + } + + + { + JSFunctionSpec nodeListFuncs[] = { + {"item", dom_nodelist_item, 1}, + {0} + }; + JSPropertySpec nodeListProps[] = { + {"length", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {0} + }; + JS_InitClass(c, global, 0, &dom_rt->domNodeListClass, 0, 0, nodeListProps, nodeListFuncs, 0, 0); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] nodeList class initialized\n")); + } + + { + JSPropertySpec xmlHTTPRequestClassProps[] = { + {"onreadystatechange", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"readyState", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"responseText", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"responseXML", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"status", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"statusText", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {0} + }; + JSFunctionSpec xmlHTTPRequestClassFuncs[] = { + {"open", xml_http_open, 2}, + {"setRequestHeader", xml_http_set_header, 2}, + {"send", xml_http_send, 0}, + {"abort", xml_http_abort, 0}, + {"getAllResponseHeaders", xml_http_get_all_headers, 0}, + {"getResponseHeader", xml_http_get_header, 1}, + /*todo - addEventListener and removeEventListener*/ + {0} + }; + JS_InitClass(c, global, 0, &dom_rt->xmlHTTPRequestClass, xml_http_constructor, 0, xmlHTTPRequestClassProps, xmlHTTPRequestClassFuncs, 0, 0); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] XMLHttpRequest class initialized\n")); + } + + + { + GF_SceneGraph *dcci; + jsval dcci_root; + + dcci = NULL; + if (scene->script_action) { + GF_JSAPIParam par; + par.node = NULL; + scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_GET_DCCI, NULL, &par); + dcci = par.scene; + } + if (dcci && dcci->RootNode) { + + JSPropertySpec DCCIClassProps[] = { + {"value", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"valueType", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"propertyType", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"readOnly", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"DCCIMetadataInterfaceType", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"DCCIMetadataInterface", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"version", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {0} + }; + JSFunctionSpec DCCIClassFuncs[] = { + {"hasProperty", dcci_has_property, 3}, + {"searchProperty", dcci_search_property, 4}, + {0} + }; + + JS_InitClass(c, global, dom_rt->dom_element_proto, &dom_rt->DCCIClass, 0, 0, DCCIClassProps, DCCIClassFuncs, 0, 0); + dcci_root = dom_base_node_construct(c, &dom_rt->DCCIClass, dcci->RootNode); + JS_DefineProperty(c, global, "DCCIRoot", dcci_root, 0, 0, JSPROP_READONLY | JSPROP_PERMANENT ); + + dcci->dcci_doc = 1; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] DCCI class initialized\n")); + } + } +} + +void dom_js_unload() +{ + if (!dom_rt) return; + dom_rt->nb_inst--; + if (!dom_rt->nb_inst) { + free(dom_rt); + dom_rt = NULL; + } +} + +static void dom_js_define_document_ex(JSContext *c, JSObject *global, GF_SceneGraph *doc, const char *name) +{ + JSClass *__class; + JSObject *obj; + if (!doc || !doc->RootNode) return; + + if (doc->reference_count) + doc->reference_count++; + + __class = &dom_rt->domDocumentClass; + if (dom_rt->get_document_class) + __class = dom_rt->get_document_class(doc); + + obj = JS_DefineObject(c, global, name, __class, 0, 0 ); + gf_node_register(doc->RootNode, NULL); + JS_SetPrivate(c, obj, doc); + doc->document = obj; +} + +void dom_js_define_document(JSContext *c, JSObject *global, GF_SceneGraph *doc) +{ + dom_js_define_document_ex(c, global, doc, "document"); +} + +JSObject *dom_js_define_event(JSContext *c, JSObject *global) +{ + JSObject *obj = JS_DefineObject(c, global, "evt", &dom_rt->domEventClass, 0, 0 ); + JS_AliasProperty(c, global, "evt", "event"); + return obj; +} + +JSObject *gf_dom_new_event(JSContext *c) +{ + return JS_NewObject(c, &dom_rt->domEventClass, 0, 0); +} +JSObject *dom_js_get_node_proto(JSContext *c) { return dom_rt->dom_node_proto; } +JSObject *dom_js_get_element_proto(JSContext *c) { return dom_rt->dom_element_proto; } +JSObject *dom_js_get_document_proto(JSContext *c) { return dom_rt->dom_document_proto; } +JSObject *dom_js_get_event_proto(JSContext *c) { return dom_rt->dom_event_proto; } + +void dom_set_class_selector(JSContext *c, void *(*get_element_class)(GF_Node *n), void *(*get_document_class)(GF_SceneGraph *n) ) +{ + if (dom_rt) { + dom_rt->get_document_class = get_document_class; + dom_rt->get_element_class = get_element_class; + } +} + +#endif /*GPAC_HAS_SPIDERMONKEY*/ + + + +GF_Err gf_sg_new_from_xml_doc(const char *src, GF_SceneGraph **scene) +{ +#ifdef GPAC_HAS_SPIDERMONKEY + GF_Err e; + XMLHTTPContext *ctx; + GF_SceneGraph *sg; + + GF_SAFEALLOC(ctx, XMLHTTPContext); + ctx->sax = gf_xml_sax_new(xml_http_sax_start, xml_http_sax_end, xml_http_sax_text, ctx); + ctx->node_stack = gf_list_new(); + sg = gf_sg_new(); + ctx->document = sg; + e = gf_xml_sax_parse_file(ctx->sax, src, NULL); + gf_xml_sax_del(ctx->sax); + gf_list_del(ctx->node_stack); + free(ctx); + + *scene = NULL; + if (e<0) { + gf_sg_del(sg); + return e; + } + *scene = sg; + return GF_OK; +#else + return GF_NOT_SUPPORTED; +#endif +} + +#ifdef GPAC_HAS_SPIDERMONKEY + +typedef struct +{ + GF_SAXParser *sax; + GF_List *node_stack; + /*dom graph*/ + GF_SceneGraph *document; +} XMLReloadContext; + +static void xml_reload_node_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + GF_DOM_Event evt; + Bool is_root = 0; + Bool modified = 0; + Bool atts_modified = 0; + Bool new_node = 0; + u32 i; + GF_DOMFullAttribute *prev = NULL; + GF_DOMFullNode *par = NULL; + XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck; + GF_DOMFullNode *node; + + node = gf_list_last(ctx->node_stack); + if (!node) { + is_root = 1; + node = (GF_DOMFullNode *) ctx->document->RootNode; + } + + if (is_root) { + Bool same_node = 1; + if (strcmp(node->name, node_name)) same_node = 0; + if (node->ns && name_space && strcmp(gf_sg_get_namespace(node->sgprivate->scenegraph, node->ns), name_space)) same_node = 0; + + if (!same_node) { + if (node->name) free(node->name); + node->name = strdup(node_name); + node->ns = 0; + if (name_space) { + gf_sg_add_namespace(node->sgprivate->scenegraph, (char *) name_space, NULL); + node->ns = gf_sg_get_namespace_code_from_name(node->sgprivate->scenegraph, (char*)name_space); + } + modified = 1; + } + } else { + GF_ChildNodeItem *child = node->children; + node = NULL; + /*locate the node in the children*/ + while (child) { + Bool same_node = 1; + node = (GF_DOMFullNode*)child->node; + if (strcmp(node->name, node_name)) same_node = 0; + if (node->ns && name_space && strcmp(gf_sg_get_namespace(node->sgprivate->scenegraph, node->ns), name_space)) same_node = 0; + if (same_node) { + break; + } + node = NULL; + child = child->next; + } + if (!node) { + modified = 1; + + /*create the new node*/ + node = (GF_DOMFullNode *) gf_node_new(ctx->document, TAG_DOMFullNode); + node->name = strdup(node_name); + if (name_space) { + gf_sg_add_namespace(node->sgprivate->scenegraph, (char *)name_space, NULL); + node->ns = gf_sg_get_namespace_code(node->sgprivate->scenegraph, (char *)name_space); + } + + par = gf_list_last(ctx->node_stack); + gf_node_register((GF_Node*)node, (GF_Node*)par); + gf_node_list_add_child(&par->children, (GF_Node*)node); + new_node = 1; + } + } + gf_list_add(ctx->node_stack, node); + + if (!modified) { + u32 count = 0; + GF_DOMFullAttribute *att = (GF_DOMFullAttribute *)node->attributes; + while (att) { + Bool found = 0; + for (i=0; iname, attributes[i].name)) { + found = 1; + if (strcmp(att->data, attributes[i].value)) { + atts_modified = 1; + free(att->data); + att->data = strdup(attributes[i].value); + } + } + } + if (!found) { + modified = 1; + break; + } + count++; + att = (GF_DOMFullAttribute *)att->next; + } + if (count != nb_attributes) + modified = 1; + } + + + if (modified) { + GF_DOMFullAttribute *tmp, *att; + att = (GF_DOMFullAttribute *)node->attributes; + while (att) { + if (att->name) free(att->name); + if (att->data) free(att->data); + tmp = att; + att = (GF_DOMFullAttribute *)att->next; + free(tmp); + } + + /*parse all atts*/ + for (i=0; idocument) + 1; + gf_node_set_id((GF_Node *)node, id, attributes[i].value); + } else { + GF_DOMFullAttribute *att; + GF_SAFEALLOC(att, GF_DOMFullAttribute); + att->tag = TAG_DOM_ATT_any; + att->name = strdup(attributes[i].name); + att->data = strdup(attributes[i].value); + if (prev) prev->next = (GF_DOMAttribute*)att; + else node->attributes = (GF_DOMAttribute*)att; + prev = att; + } + } + atts_modified = 1; + } + + if (atts_modified || new_node) { + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = new_node ? GF_EVENT_NODE_INSERTED : GF_EVENT_ATTR_MODIFIED; + evt.bubbles = 1; + evt.relatedNode = (GF_Node*) (GF_EVENT_NODE_INSERTED ? par : node); + gf_dom_event_fire((GF_Node*)node, &evt); + } +} + +static void xml_reload_node_end(void *sax_cbck, const char *node_name, const char *name_space) +{ + XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck; + GF_DOMFullNode *par = gf_list_last(ctx->node_stack); + if (par) { + /*depth mismatch*/ + if (strcmp(par->name, node_name)) return; + gf_list_rem_last(ctx->node_stack); + } +} +static void xml_reload_text_content(void *sax_cbck, const char *content, Bool is_cdata) +{ + GF_DOM_Event evt; + u32 i, len; + GF_ChildNodeItem *child; + GF_DOMText *txt = NULL; + XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck; + GF_DOMFullNode *par = gf_list_last(ctx->node_stack); + if (!par) return; + + /*basic check, remove all empty text nodes*/ + len = strlen(content); + for (i=0; ichildren; + while (child) { + if (child->node->sgprivate->tag == TAG_DOMText) { + txt = (GF_DOMText *)child->node; + if (!strcmp(txt->textContent, content) && ((txt->type==GF_DOM_TEXT_REGULAR) || is_cdata)) + return; + if (txt->textContent) free(txt->textContent); + txt->textContent = strdup(content); + txt->type = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR; + break; + } + child = child->next; + } + if (!txt) { + txt = gf_dom_add_text_node((GF_Node *)par, strdup(content) ); + txt->type = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR; + + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = GF_EVENT_NODE_INSERTED; + evt.bubbles = 1; + evt.relatedNode = (GF_Node*) par; + gf_dom_event_fire((GF_Node*)txt, &evt); + } + + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = GF_EVENT_CHAR_DATA_MODIFIED; + evt.bubbles = 1; + evt.relatedNode = (GF_Node*)par; + gf_dom_event_fire((GF_Node*)par, &evt); + + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.type = GF_EVENT_DCCI_PROP_CHANGE; + evt.bubbles = 1; + evt.relatedNode = (GF_Node*)par; + gf_dom_event_fire((GF_Node*)par, &evt); +} +#endif + +GF_Err gf_sg_reload_xml_doc(const char *src, GF_SceneGraph *scene) +{ +#ifndef GPAC_HAS_SPIDERMONKEY + return GF_NOT_SUPPORTED; +#else + GF_Err e; + XMLReloadContext ctx; + + if (!src || !scene) return GF_BAD_PARAM; + memset(&ctx, 0, sizeof(XMLReloadContext)); + ctx.document = scene; + ctx.node_stack = gf_list_new(); + + ctx.sax = gf_xml_sax_new(xml_reload_node_start, xml_reload_node_end, xml_reload_text_content, &ctx); + e = gf_xml_sax_parse_file(ctx.sax, src, NULL); + gf_list_del(ctx.node_stack); + gf_xml_sax_del(ctx.sax); + if (e<0) return e; + return GF_OK; +#endif +} + +#endif /*GPAC_DISABLE_SVG*/ diff --git a/src/scenegraph/mpeg4_animators.c b/src/scenegraph/mpeg4_animators.c new file mode 100644 index 0000000..141cc4c --- /dev/null +++ b/src/scenegraph/mpeg4_animators.c @@ -0,0 +1,792 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +/*MPEG4 tags (for internal nodes)*/ +#include + +static Fixed Interpolate(Fixed keyValue1, Fixed keyValue2, Fixed fraction) +{ + return gf_mulfix(keyValue2 - keyValue1, fraction) + keyValue1; +} + +static Fixed GetInterpolateFraction(Fixed key1, Fixed key2, Fixed fraction) +{ + Fixed keyDiff = key2 - key1; + assert((fraction >= key1) && (fraction <= key2)); + if (ABS(keyDiff) < FIX_EPSILON) return 0; + return gf_divfix(fraction - key1, keyDiff); +} + +enum +{ + ANIM_LINE, + ANIM_QUADRATIC, + ANIM_CUBIC, + ANIM_NURBS, + /*NOT SUPPORTED*/ + ANIM_SPLINE +}; + +enum +{ + ANIM_DEFAULT, + ANIM_DISCRETE, + ANIM_LINEAR, + /*NOT SUPPORTED ON SPLINES*/ + ANIM_PACED, + ANIM_VELOCITY +}; + + +/* Bisection algorithm to find u=a*t^3+b*t^2+c*t+d */ +Fixed do_bisection(Fixed t, SFVec2f a, SFVec2f b, SFVec2f c, SFVec2f d) +{ + Fixed left, right, usearch, tsearch, limit; + left = 0; + right = FIX_ONE; + limit = FIX_ONE/1000; + + do { + usearch = (left+right)/2; + tsearch = gf_mulfix(usearch, c.x + gf_mulfix(usearch, b.x + gf_mulfix(usearch, a.x))) + d.x; + if (t < tsearch + limit) right = usearch; + else left = usearch; + } while ((t > tsearch + limit) || (tn) free(nurbs->n); + if (nurbs->left) free(nurbs->left); + if (nurbs->right) free(nurbs->right); + nurbs->n = nurbs->left = nurbs->right = NULL; +} + +static void anurbs_init(anim_nurbs *nurbs, u32 type, u32 nCtrl, u32 nKnots, Fixed *knots, u32 nWeight, Fixed *weights) +{ + memset(nurbs, 0, sizeof(anim_nurbs)); + nurbs->type = type; + switch (type) { + case ANIM_CUBIC: + nurbs->npoints = 4; + nurbs->nknots = 8; + nurbs->knots = cubic_knots; + break; + case ANIM_QUADRATIC: + nurbs->npoints = 3; + nurbs->nknots = 6; + nurbs->knots = quadratic_knots; + break; + default: + nurbs->npoints = nCtrl; + nurbs->knots = knots; + nurbs->nknots = nKnots; + nurbs->weights = weights; + nurbs->nweights = nWeight; + break; + } + nurbs->p = nurbs->nknots - nurbs->npoints - 1; + if ((nurbs->p<=0) || (nurbs->p >= nurbs->nknots -1) + || ((nurbs->nweights>0) && (nurbs->npoints != nurbs->nweights)) ) { + nurbs->valid = 0; + } else { + nurbs->valid = 1; + } +} + +static void anurbs_basis(anim_nurbs *nurbs, s32 span, Fixed t) +{ + u32 i, j; + Fixed saved, temp; + if (!nurbs->n) { + nurbs->n = (Fixed*)malloc(sizeof(Fixed) * (nurbs->p+1)); + nurbs->left = (Fixed*)malloc(sizeof(Fixed) * (nurbs->p+1)); + nurbs->right = (Fixed*)malloc(sizeof(Fixed) * (nurbs->p+1)); + } + nurbs->n[0] = FIX_ONE; + + for(i=1; i<=nurbs->p; i++) { + nurbs->left[i] = t - nurbs->knots[span+1-i]; + nurbs->right[i] = nurbs->knots[span+i]-t; + saved = 0; + + for(j=0; jn[j], nurbs->right[j+1] + nurbs->left[i-j]); + nurbs->n[j] = saved + gf_mulfix(nurbs->right[j+1], temp); + saved = gf_mulfix(nurbs->left[i-j], temp); + } + nurbs->n[i]=saved; + } +} + +static s32 anurbs_find_span(anim_nurbs *nurbs, Fixed u) +{ +#if 0 + s32 span; + if (u == nurbs->knots[nurbs->npoints]) return nurbs->npoints - 1; + for (span = (s32) nurbs->p; span < (s32) nurbs->nknots - (s32) nurbs->p; span++) { + if (uknots[span]) break; + } + span--; + return span; +#else + s32 low, high, mid; + if (u == nurbs->knots[nurbs->npoints]) return nurbs->npoints - 1; + low = nurbs->p; + high = nurbs->npoints; + mid = (low + high)/2; + + while (u < nurbs->knots[mid] || u >= nurbs->knots[mid+1]) { + if (u < nurbs->knots[mid]) high = mid; + else low = mid; + mid = (low + high)/2; + } + return (mid); + +#endif +} + +static SFVec3f anurbs_get_vec3f(anim_nurbs *nurbs, s32 span, SFVec3f *pts) +{ + SFVec3f res, tmp; + Fixed w, wi; + u32 i; + tmp.x = tmp.y = tmp.z = 0; + res = tmp; + w=0; + for(i=0; i<=nurbs->p;i++) { + tmp = pts[span - nurbs->p + i]; + if (nurbs->nweights>0) { + wi = nurbs->weights[span - nurbs->p + i]; + tmp = gf_vec_scale(tmp, wi); + w += gf_mulfix(nurbs->n[i], wi); + } + res.x += gf_mulfix(nurbs->n[i], tmp.x); + res.y += gf_mulfix(nurbs->n[i], tmp.y); + res.z += gf_mulfix(nurbs->n[i], tmp.z); + } + if (nurbs->nweights>0) { + if (w) { + w = gf_invfix(w); + res = gf_vec_scale(res, w); + } + } + return res; +} + +static SFVec2f anurbs_get_vec2f(anim_nurbs *nurbs, s32 span, SFVec2f *pts) +{ + SFVec2f res, tmp; + Fixed w, wi; + u32 i; + tmp.x = tmp.y = 0; + res = tmp; + w=0; + for(i=0; i<=nurbs->p;i++) { + tmp = pts[span - nurbs->p + i]; + if (nurbs->nweights>0) { + wi = nurbs->weights[span - nurbs->p + i]; + tmp.x = gf_mulfix(tmp.x, wi); + tmp.y = gf_mulfix(tmp.y, wi); + w += gf_mulfix(nurbs->n[i], wi); + } + res.x += gf_mulfix(nurbs->n[i], tmp.x); + res.y += gf_mulfix(nurbs->n[i], tmp.y); + } + if (nurbs->nweights>0) { + if (w) { + w = gf_invfix(w); + res.x = gf_mulfix(res.x, w); + res.y = gf_mulfix(res.y, w); + } + } + return res; +} + +static Fixed anurbs_get_float(anim_nurbs *nurbs, s32 span, Fixed *vals) +{ + Fixed res, tmp; + Fixed w, wi; + u32 i; + res = tmp = 0; + w=0; + for(i=0; i<=nurbs->p;i++) { + tmp = vals[span - nurbs->p + i]; + if (nurbs->nweights>0) { + wi = nurbs->weights[span - nurbs->p + i]; + tmp = gf_mulfix(tmp, wi); + w += gf_mulfix(nurbs->n[i], wi); + } + res += gf_mulfix(nurbs->n[i], tmp); + } + if (nurbs->nweights>0) res = gf_divfix(res, w); + return res; +} + +typedef struct +{ + Bool is_dirty; + u32 anim_type; + /*for paced anim*/ + Fixed length; + /*for spline anim*/ + SFVec2f a, b, c, d; + /*nurbs path*/ + anim_nurbs anurbs; +} AnimatorStack; + +static void Anim_Destroy(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node); + anurbs_reset(&stack->anurbs); + free(stack); + } +} + +static void Animator_Update(AnimatorStack *stack, u32 keyValueType, u32 nCtrl, MFVec2f *keySpline, u32 nWeight, Fixed *weights) +{ + if (stack->anim_type==ANIM_SPLINE) { + stack->a.x = (keySpline->vals[0].x - keySpline->vals[1].x)*3 + FIX_ONE; + stack->a.y = (keySpline->vals[0].y - keySpline->vals[1].y)*3 + FIX_ONE; + stack->b.x = (keySpline->vals[1].x - 2*keySpline->vals[0].x)*3; + stack->b.y = (keySpline->vals[1].y - 2*keySpline->vals[0].y)*3; + stack->c.x = keySpline->vals[0].x*3; + stack->c.y = keySpline->vals[0].y*3; + stack->d.x = stack->d.y = 0; + } + anurbs_reset(&stack->anurbs); + switch (keyValueType) { + case ANIM_CUBIC: + anurbs_init(&stack->anurbs, ANIM_CUBIC, 0, 0, NULL, 0, NULL); + break; + case ANIM_QUADRATIC: + anurbs_init(&stack->anurbs, ANIM_QUADRATIC, 0, 0, NULL, 0, NULL); + break; + case ANIM_NURBS: + anurbs_init(&stack->anurbs, ANIM_NURBS, nCtrl, keySpline->count, (Fixed *) &keySpline->vals[0].x, nWeight, weights); + break; + } +} + + +static Bool anim_check_frac(Fixed frac, SFVec2f *fromTo) +{ + if (frac<0) return 0; + if (frac>FIX_ONE) return 0; + if (fromTo->x > fromTo->y) return 0; + /*not active*/ + if (fracx) return 0; + if (frac>fromTo->y) return 0; + return 1; +} + +static void PA_Update(M_PositionAnimator *pa, AnimatorStack *stack) +{ + u32 i; + GF_Vec d; + stack->is_dirty = 0; + stack->anim_type = pa->keyType; + /*if empty key and default anim switch to linear*/ + if (!pa->key.count && !stack->anim_type) stack->anim_type = ANIM_LINEAR; + + if (stack->anim_type == ANIM_PACED) { + stack->length = 0; + for (i=0; ikeyValue.count-1; i++) { + d.x = pa->keyValue.vals[i+1].x - pa->keyValue.vals[i].x; + d.y = pa->keyValue.vals[i+1].y - pa->keyValue.vals[i].y; + d.z = pa->keyValue.vals[i+1].z - pa->keyValue.vals[i].z; + stack->length += gf_vec_len(d); + } + } + Animator_Update(stack, pa->keyValueType, pa->keyValue.count, &pa->keySpline, pa->weight.count, pa->weight.vals); +} +static void PA_SetFraction(GF_Node *node) +{ + Fixed frac; + u32 nbKeys, nbVals, i; + GF_Vec d; + Fixed len, dlen, dist; + M_PositionAnimator *pa = (M_PositionAnimator *)node; + AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node); + + frac = pa->set_fraction; + if (!anim_check_frac(frac, &pa->fromTo)) return; + + if (stack->is_dirty) PA_Update(pa, stack); + + nbKeys = pa->key.count; + nbVals = pa->keyValue.count; + + i=0; + switch (pa->keyValueType) { + /*linear interpolate*/ + case ANIM_LINE: + /*compute frac and segment start index*/ + switch (stack->anim_type) { + case ANIM_DEFAULT: + if (nbKeys != nbVals) return; + if (frackey.vals[0]) { i=0; frac = 0; } + else if (frac>pa->key.vals[nbKeys-1]) { i=nbVals-2; frac = FIX_ONE; } + else { + for (i=0; i=pa->key.vals[i]) && (frackey.vals[i+1])) break; + } + frac = GetInterpolateFraction(pa->key.vals[i], pa->key.vals[i+1], frac); + } + break; + case ANIM_DISCRETE: + i = FIX2INT(gf_floor(frac*nbVals)); + frac = 0; + break; + case ANIM_LINEAR: + i = FIX2INT(gf_floor(frac*(nbVals-1))); + frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1); + break; + case ANIM_PACED: + /*at cst speed, this is the length done*/ + dist = gf_mulfix(frac, stack->length); + /*then figure out in which seg we are*/ + len = 0; + dlen = 0; + for (i=0; ikeyValue.vals[i+1].x - pa->keyValue.vals[i].x; + d.y = pa->keyValue.vals[i+1].y - pa->keyValue.vals[i].y; + d.z = pa->keyValue.vals[i+1].z - pa->keyValue.vals[i].z; + dlen = gf_vec_len(d); + if (len+dlen>dist) break; + len += dlen; + } + /*that's our fraction inside the seg*/ + frac = gf_divfix(dist-len, dlen); + break; + case ANIM_SPLINE: + frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d); + i = FIX2INT(gf_floor(frac*(nbVals-1))); + frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1); + break; + default: + return; + } + /*interpolate*/ + pa->value_changed.x = Interpolate(pa->keyValue.vals[i].x, pa->keyValue.vals[i+1].x, frac); + pa->value_changed.y = Interpolate(pa->keyValue.vals[i].y, pa->keyValue.vals[i+1].y, frac); + pa->value_changed.z = Interpolate(pa->keyValue.vals[i].z, pa->keyValue.vals[i+1].z, frac); + break; + /*bezier interpolate*/ + case ANIM_QUADRATIC: + case ANIM_CUBIC: + case ANIM_NURBS: + if (!stack->anurbs.valid) return; + /*compute frac*/ + switch (stack->anim_type) { + case ANIM_DISCRETE: + i = FIX2INT(gf_floor(frac*nbVals)); + frac = INT2FIX(i) / nbVals; + break; + case ANIM_LINEAR: + i = FIX2INT(gf_floor(frac*(nbVals-1))); + frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1); + break; + case ANIM_VELOCITY: + frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d); + break; + /*nothing to do for this one here*/ + case ANIM_DEFAULT: + /*not supported - use frac as is*/ + case ANIM_PACED: + default: + break; + } + /*evaluate*/ + i = anurbs_find_span(&stack->anurbs, frac); + anurbs_basis(&stack->anurbs, i, frac); + pa->value_changed = anurbs_get_vec3f(&stack->anurbs, i, pa->keyValue.vals); + break; + /*not supported*/ + case ANIM_SPLINE: + default: + return; + } + + pa->value_changed.x += pa->offset.x; + pa->value_changed.y += pa->offset.y; + pa->value_changed.z += pa->offset.z; + gf_node_event_out_str(node, "value_changed"); +} + +void PA_Modified(GF_Node *node, GF_FieldInfo *field) +{ + AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node); + M_PositionAnimator *pa = (M_PositionAnimator *)node; + + if ( /*all fields impacting cached path len / nurbs*/ + (field->far_ptr == &pa->keyValue) + || (field->far_ptr == &pa->keyValueType) + || (field->far_ptr == &pa->key) + || (field->far_ptr == &pa->keyType) + || (field->far_ptr == &pa->keySpline) + || (field->far_ptr == &pa->weight) + || (field->far_ptr == &pa->key) + + ) stack->is_dirty = 1; +} +void PA_Init(GF_Node *n) +{ + M_PositionAnimator *sa = (M_PositionAnimator*)n; + AnimatorStack *stack; + GF_SAFEALLOC(stack, AnimatorStack); + stack->is_dirty = 1; + gf_node_set_private(n, stack); + gf_node_set_callback_function(n, Anim_Destroy); + sa->on_set_fraction = PA_SetFraction; +} + +static void PA2D_Update(M_PositionAnimator2D *pa, AnimatorStack *stack) +{ + u32 i; + Fixed dx, dy; + stack->is_dirty = 0; + stack->anim_type = pa->keyType; + /*if empty key and default anim switch to linear*/ + if (!pa->key.count && !stack->anim_type) stack->anim_type = ANIM_LINEAR; + + if (stack->anim_type == ANIM_PACED) { + stack->length = 0; + for (i=0; ikeyValue.count-1; i++) { + dx = pa->keyValue.vals[i+1].x - pa->keyValue.vals[i].x; + dy = pa->keyValue.vals[i+1].y - pa->keyValue.vals[i].y; + stack->length += gf_sqrt(gf_mulfix(dx, dx) + gf_mulfix(dy, dy)); + } + } + Animator_Update(stack, pa->keyValueType, pa->keyValue.count, &pa->keySpline, pa->weight.count, pa->weight.vals); +} +static void PA2D_SetFraction(GF_Node *node) +{ + Fixed frac; + u32 nbKeys, nbVals, i; + Fixed dx, dy; + Fixed len, dlen, dist; + M_PositionAnimator2D *pa = (M_PositionAnimator2D *)node; + AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node); + + frac = pa->set_fraction; + if (!anim_check_frac(frac, &pa->fromTo)) return; + + if (stack->is_dirty) PA2D_Update(pa, stack); + + nbKeys = pa->key.count; + nbVals = pa->keyValue.count; + + i=0; + switch (pa->keyValueType) { + /*linear interpolate*/ + case ANIM_LINE: + /*compute frac and segment start index*/ + switch (stack->anim_type) { + case ANIM_DEFAULT: + if (nbKeys != nbVals) return; + if (frac<=pa->key.vals[0]) { i=0; frac = 0; } + else if (frac>=pa->key.vals[nbKeys-1]) { i=nbVals-2; frac=FIX_ONE; } + else { + for (i=0; i=pa->key.vals[i]) && (frackey.vals[i+1])) break; + } + frac = GetInterpolateFraction(pa->key.vals[i], pa->key.vals[i+1], frac); + } + break; + case ANIM_DISCRETE: + i = FIX2INT(gf_floor(frac*nbVals)); + frac = 0; + break; + case ANIM_LINEAR: + i = FIX2INT(gf_floor(frac*(nbVals-1))); + frac = (frac - INT2FIX(i)/ (nbVals-1) ) * (nbVals-1); + break; + case ANIM_PACED: + /*at cst speed, this is the length done*/ + dist = gf_mulfix(frac, stack->length); + /*then figure out in which seg we are*/ + len = 0; + dlen = 0; + for (i=0; ikeyValue.vals[i+1].x - pa->keyValue.vals[i].x; + dy = pa->keyValue.vals[i+1].y - pa->keyValue.vals[i].y; + dlen = gf_sqrt(gf_mulfix(dx,dx) + gf_mulfix(dy,dy)); + if (len+dlen>dist) break; + len += dlen; + } + /*that's our fraction inside the seg*/ + frac = gf_divfix(dist-len, dlen); + break; + case ANIM_SPLINE: + frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d); + i = FIX2INT(gf_floor(frac * (nbVals-1))); + frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1); + break; + default: + return; + } + /*interpolate*/ + pa->value_changed.x = Interpolate(pa->keyValue.vals[i].x, pa->keyValue.vals[i+1].x, frac); + pa->value_changed.y = Interpolate(pa->keyValue.vals[i].y, pa->keyValue.vals[i+1].y, frac); + break; + /*bezier interpolate*/ + case ANIM_QUADRATIC: + case ANIM_CUBIC: + case ANIM_NURBS: + if (!stack->anurbs.valid) return; + /*compute frac*/ + switch (stack->anim_type) { + case ANIM_DISCRETE: + i = FIX2INT(gf_floor(frac*nbVals)); + frac = INT2FIX(i) / nbVals; + break; + case ANIM_LINEAR: + i = FIX2INT(gf_floor(frac*(nbVals-1))); + frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1); + break; + case ANIM_VELOCITY: + frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d); + break; + /*nothing to do for this one here*/ + case ANIM_DEFAULT: + /*not supported - use frac as is*/ + case ANIM_PACED: + default: + break; + } + /*evaluate*/ + i = anurbs_find_span(&stack->anurbs, frac); + anurbs_basis(&stack->anurbs, i, frac); + pa->value_changed = anurbs_get_vec2f(&stack->anurbs, i, pa->keyValue.vals); + break; + /*not supported*/ + case ANIM_SPLINE: + default: + return; + } + + pa->value_changed.x += pa->offset.x; + pa->value_changed.y += pa->offset.y; + gf_node_event_out_str(node, "value_changed"); +} + +void PA2D_Modified(GF_Node *node, GF_FieldInfo *field) +{ + AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node); + M_PositionAnimator2D *pa = (M_PositionAnimator2D *)node; + + if ( /*all fields impacting cached path len / nurbs*/ + (field->far_ptr == &pa->keyValue) + || (field->far_ptr == &pa->keyValueType) + || (field->far_ptr == &pa->key) + || (field->far_ptr == &pa->keyType) + || (field->far_ptr == &pa->keySpline) + || (field->far_ptr == &pa->weight) + || (field->far_ptr == &pa->key) + + ) stack->is_dirty = 1; +} +void PA2D_Init(GF_Node *n) +{ + M_PositionAnimator2D *sa = (M_PositionAnimator2D *)n; + AnimatorStack *stack; + GF_SAFEALLOC(stack, AnimatorStack); + stack->is_dirty = 1; + gf_node_set_private(n, stack); + gf_node_set_callback_function(n, Anim_Destroy); + sa->on_set_fraction = PA2D_SetFraction; +} + +static void SA_Update(M_ScalarAnimator *sa, AnimatorStack *stack) +{ + u32 i; + Fixed len; + stack->is_dirty = 0; + stack->anim_type = sa->keyType; + /*if empty key and default anim switch to linear*/ + if (!sa->key.count && !stack->anim_type) stack->anim_type = ANIM_LINEAR; + + if (stack->anim_type == ANIM_PACED) { + stack->length = 0; + for (i=0; ikeyValue.count-1; i++) { + len = sa->keyValue.vals[i+1] - sa->keyValue.vals[i]; + stack->length += ABS(len); + } + } + Animator_Update(stack, sa->keyValueType, sa->keyValue.count, &sa->keySpline, sa->weight.count, sa->weight.vals); +} + +void SA_SetFraction(GF_Node *node) +{ + Fixed frac; + u32 nbKeys, nbVals, i; + Fixed len, dlen, dist; + M_ScalarAnimator *sa = (M_ScalarAnimator *)node; + AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node); + + frac = sa->set_fraction; + if (!anim_check_frac(frac, &sa->fromTo)) return; + + if (stack->is_dirty) SA_Update(sa, stack); + + nbKeys = sa->key.count; + nbVals = sa->keyValue.count; + + i = 0; + switch (sa->keyValueType) { + /*linear interpolate*/ + case ANIM_LINE: + /*compute frac & segment start index*/ + switch (stack->anim_type) { + case ANIM_DEFAULT: + if (nbKeys != nbVals) return; + if (frackey.vals[0]) { i=0; frac = 0; } + else if (frac>sa->key.vals[nbKeys-1]) { i=nbVals-2; frac=FIX_ONE; } + else { + for (i=0; i=sa->key.vals[i]) && (frackey.vals[i+1])) break; + } + frac = GetInterpolateFraction(sa->key.vals[i], sa->key.vals[i+1], frac); + } + break; + case ANIM_DISCRETE: + i = FIX2INT(gf_floor(frac*nbVals)); + frac = 0; + break; + case ANIM_LINEAR: + i = FIX2INT(gf_floor(frac*(nbVals-1))); + frac = (frac - INT2FIX(i)/ (nbVals-1) ) * (nbVals-1); + break; + case ANIM_PACED: + /*at cst speed, this is the length done*/ + dist = gf_mulfix(frac, stack->length); + /*then figure out in which seg we are*/ + len = 0; + dlen = 0; + for (i=0; ikeyValue.vals[i+1] - sa->keyValue.vals[i]; + if (dlen<0) dlen *= -1; + if (len+dlen>dist) break; + len += dlen; + } + /*that's our fraction inside the seg*/ + frac = gf_divfix(dist-len, dlen); + break; + case ANIM_SPLINE: + frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d); + i = FIX2INT(gf_floor(frac*(nbVals-1))); + frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1); + break; + } + /*interpolate*/ + sa->value_changed = Interpolate(sa->keyValue.vals[i], sa->keyValue.vals[i+1], frac); + break; + /*bezier interpolate*/ + case ANIM_QUADRATIC: + case ANIM_CUBIC: + case ANIM_NURBS: + if (!stack->anurbs.valid) return; + /*compute frac*/ + switch (stack->anim_type) { + case ANIM_DISCRETE: + i = FIX2INT(gf_floor(frac*nbVals)); + frac = INT2FIX(i) / nbVals; + break; + case ANIM_LINEAR: + i = FIX2INT(gf_floor(frac*(nbVals-1))); + frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1); + break; + case ANIM_VELOCITY: + frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d); + break; + /*nothing to do for this one here*/ + case ANIM_DEFAULT: + /*not supported - use frac as is*/ + case ANIM_PACED: + default: + break; + } + /*evaluate nurbs*/ + i = anurbs_find_span(&stack->anurbs, frac); + anurbs_basis(&stack->anurbs, i, frac); + sa->value_changed = anurbs_get_float(&stack->anurbs, i, sa->keyValue.vals); + break; + /*not supported*/ + case ANIM_SPLINE: + default: + return; + } + + sa->value_changed += sa->offset; + gf_node_event_out_str(node, "value_changed"); +} + +void SA_Modified(GF_Node *node, GF_FieldInfo *field) +{ + AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node); + M_ScalarAnimator *sa = (M_ScalarAnimator *)node; + + if ( /*all fields impacting cached path len / nurbs*/ + (field->far_ptr == &sa->keyValue) + || (field->far_ptr == &sa->keyValueType) + || (field->far_ptr == &sa->key) + || (field->far_ptr == &sa->keyType) + || (field->far_ptr == &sa->keySpline) + || (field->far_ptr == &sa->weight) + || (field->far_ptr == &sa->key) + + ) stack->is_dirty = 1; +} + +void SA_Init(GF_Node *n) +{ + M_ScalarAnimator *sa = (M_ScalarAnimator *)n; + AnimatorStack *stack; + GF_SAFEALLOC(stack, AnimatorStack); + stack->is_dirty = 1; + gf_node_set_private(n, stack); + gf_node_set_callback_function(n, Anim_Destroy); + sa->on_set_fraction = SA_SetFraction; +} + + diff --git a/src/scenegraph/mpeg4_nodes.c b/src/scenegraph/mpeg4_nodes.c new file mode 100644 index 0000000..5156221 --- /dev/null +++ b/src/scenegraph/mpeg4_nodes.c @@ -0,0 +1,20795 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + DO NOT MOFIFY - File generated on GMT Thu Aug 07 11:43:26 2008 + + BY MPEG4Gen for GPAC Version 0.4.5-DEV +*/ + +#include + + +#include + +/* + Anchor Node deletion +*/ + +static void Anchor_Del(GF_Node *node) +{ + M_Anchor *p = (M_Anchor *) node; + gf_sg_sfstring_del(p->description); + gf_sg_mfstring_del(p->parameter); + gf_sg_mfurl_del(p->url); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Anchor_Def2All[] = { 2, 3, 4, 5}; +static const u16 Anchor_In2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 Anchor_Out2All[] = { 2, 3, 4, 5}; + +static u32 Anchor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 7; + case GF_SG_FIELD_CODING_DEF: return 4; + case GF_SG_FIELD_CODING_OUT: return 4; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 7; + } +} + +static GF_Err Anchor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Anchor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Anchor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Anchor_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Anchor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Anchor *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Anchor *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Anchor *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Anchor *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Anchor *)node)->children; + return GF_OK; + case 3: + info->name = "description"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_Anchor *) node)->description; + return GF_OK; + case 4: + info->name = "parameter"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_Anchor *) node)->parameter; + return GF_OK; + case 5: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_Anchor *) node)->url; + return GF_OK; + case 6: + info->name = "activate"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Anchor *)node)->on_activate; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Anchor *) node)->activate; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Anchor_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("description", name)) return 3; + if (!strcmp("parameter", name)) return 4; + if (!strcmp("url", name)) return 5; + if (!strcmp("activate", name)) return 6; + return -1; + } +static Bool Anchor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *Anchor_Create() +{ + M_Anchor *p; + GF_SAFEALLOC(p, M_Anchor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Anchor); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + AnimationStream Node deletion +*/ + +static void AnimationStream_Del(GF_Node *node) +{ + M_AnimationStream *p = (M_AnimationStream *) node; + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 AnimationStream_Def2All[] = { 0, 1, 2, 3, 4}; +static const u16 AnimationStream_In2All[] = { 0, 1, 2, 3, 4}; +static const u16 AnimationStream_Out2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 AnimationStream_Dyn2All[] = { 1}; + +static u32 AnimationStream_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 7; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 7; + } +} + +static GF_Err AnimationStream_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = AnimationStream_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = AnimationStream_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = AnimationStream_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = AnimationStream_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err AnimationStream_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "loop"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_AnimationStream *) node)->loop; + return GF_OK; + case 1: + info->name = "speed"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_AnimationStream *) node)->speed; + return GF_OK; + case 2: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AnimationStream *) node)->startTime; + return GF_OK; + case 3: + info->name = "stopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AnimationStream *) node)->stopTime; + return GF_OK; + case 4: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_AnimationStream *) node)->url; + return GF_OK; + case 5: + info->name = "duration_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AnimationStream *) node)->duration_changed; + return GF_OK; + case 6: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_AnimationStream *) node)->isActive; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AnimationStream_get_field_index_by_name(char *name) +{ + if (!strcmp("loop", name)) return 0; + if (!strcmp("speed", name)) return 1; + if (!strcmp("startTime", name)) return 2; + if (!strcmp("stopTime", name)) return 3; + if (!strcmp("url", name)) return 4; + if (!strcmp("duration_changed", name)) return 5; + if (!strcmp("isActive", name)) return 6; + return -1; + } +static Bool AnimationStream_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *AnimationStream_Create() +{ + M_AnimationStream *p; + GF_SAFEALLOC(p, M_AnimationStream); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_AnimationStream); + + /*default field values*/ + p->speed = FLT2FIX(1.0); + p->startTime = 0; + p->stopTime = 0; + return (GF_Node *)p; +} + + +/* + Appearance Node deletion +*/ + +static void Appearance_Del(GF_Node *node) +{ + M_Appearance *p = (M_Appearance *) node; + gf_node_unregister((GF_Node *) p->material, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->texture, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->textureTransform, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Appearance_Def2All[] = { 0, 1, 2}; +static const u16 Appearance_In2All[] = { 0, 1, 2}; +static const u16 Appearance_Out2All[] = { 0, 1, 2}; + +static u32 Appearance_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 3; + } +} + +static GF_Err Appearance_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Appearance_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Appearance_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Appearance_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Appearance_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "material"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMaterialNode; + info->far_ptr = & ((M_Appearance *)node)->material; + return GF_OK; + case 1: + info->name = "texture"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((M_Appearance *)node)->texture; + return GF_OK; + case 2: + info->name = "textureTransform"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureTransformNode; + info->far_ptr = & ((M_Appearance *)node)->textureTransform; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Appearance_get_field_index_by_name(char *name) +{ + if (!strcmp("material", name)) return 0; + if (!strcmp("texture", name)) return 1; + if (!strcmp("textureTransform", name)) return 2; + return -1; + } +static Bool Appearance_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *Appearance_Create() +{ + M_Appearance *p; + GF_SAFEALLOC(p, M_Appearance); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Appearance); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + AudioBuffer Node deletion +*/ + +static void AudioBuffer_Del(GF_Node *node) +{ + M_AudioBuffer *p = (M_AudioBuffer *) node; + gf_node_unregister_children((GF_Node *) p, p->children); + gf_sg_mfint32_del(p->phaseGroup); + gf_node_free((GF_Node *) p); +} + +static const u16 AudioBuffer_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 AudioBuffer_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 AudioBuffer_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 AudioBuffer_Dyn2All[] = { 1}; + +static u32 AudioBuffer_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 8; + case GF_SG_FIELD_CODING_DEF: return 8; + case GF_SG_FIELD_CODING_OUT: return 10; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 10; + } +} + +static GF_Err AudioBuffer_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = AudioBuffer_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = AudioBuffer_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = AudioBuffer_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = AudioBuffer_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err AudioBuffer_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "loop"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_AudioBuffer *) node)->loop; + return GF_OK; + case 1: + info->name = "pitch"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_AudioBuffer *) node)->pitch; + return GF_OK; + case 2: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AudioBuffer *) node)->startTime; + return GF_OK; + case 3: + info->name = "stopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AudioBuffer *) node)->stopTime; + return GF_OK; + case 4: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioBuffer *)node)->children; + return GF_OK; + case 5: + info->name = "numChan"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_AudioBuffer *) node)->numChan; + return GF_OK; + case 6: + info->name = "phaseGroup"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_AudioBuffer *) node)->phaseGroup; + return GF_OK; + case 7: + info->name = "length"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_AudioBuffer *) node)->length; + return GF_OK; + case 8: + info->name = "duration_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AudioBuffer *) node)->duration_changed; + return GF_OK; + case 9: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_AudioBuffer *) node)->isActive; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AudioBuffer_get_field_index_by_name(char *name) +{ + if (!strcmp("loop", name)) return 0; + if (!strcmp("pitch", name)) return 1; + if (!strcmp("startTime", name)) return 2; + if (!strcmp("stopTime", name)) return 3; + if (!strcmp("children", name)) return 4; + if (!strcmp("numChan", name)) return 5; + if (!strcmp("phaseGroup", name)) return 6; + if (!strcmp("length", name)) return 7; + if (!strcmp("duration_changed", name)) return 8; + if (!strcmp("isActive", name)) return 9; + return -1; + } +static Bool AudioBuffer_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 7; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 5: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + case 7: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *AudioBuffer_Create() +{ + M_AudioBuffer *p; + GF_SAFEALLOC(p, M_AudioBuffer); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_AudioBuffer); + + /*default field values*/ + p->pitch = FLT2FIX(1); + p->startTime = 0; + p->stopTime = 0; + p->numChan = 1; + p->phaseGroup.vals = (SFInt32*)malloc(sizeof(SFInt32)*1); + p->phaseGroup.count = 1; + p->phaseGroup.vals[0] = 1; + p->length = FLT2FIX(0.0); + return (GF_Node *)p; +} + + +/* + AudioClip Node deletion +*/ + +static void AudioClip_Del(GF_Node *node) +{ + M_AudioClip *p = (M_AudioClip *) node; + gf_sg_sfstring_del(p->description); + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 AudioClip_Def2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 AudioClip_In2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 AudioClip_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 AudioClip_Dyn2All[] = { 2}; + +static u32 AudioClip_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 6; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 8; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 8; + } +} + +static GF_Err AudioClip_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = AudioClip_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = AudioClip_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = AudioClip_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = AudioClip_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err AudioClip_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "description"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_AudioClip *) node)->description; + return GF_OK; + case 1: + info->name = "loop"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_AudioClip *) node)->loop; + return GF_OK; + case 2: + info->name = "pitch"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_AudioClip *) node)->pitch; + return GF_OK; + case 3: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AudioClip *) node)->startTime; + return GF_OK; + case 4: + info->name = "stopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AudioClip *) node)->stopTime; + return GF_OK; + case 5: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_AudioClip *) node)->url; + return GF_OK; + case 6: + info->name = "duration_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AudioClip *) node)->duration_changed; + return GF_OK; + case 7: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_AudioClip *) node)->isActive; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AudioClip_get_field_index_by_name(char *name) +{ + if (!strcmp("description", name)) return 0; + if (!strcmp("loop", name)) return 1; + if (!strcmp("pitch", name)) return 2; + if (!strcmp("startTime", name)) return 3; + if (!strcmp("stopTime", name)) return 4; + if (!strcmp("url", name)) return 5; + if (!strcmp("duration_changed", name)) return 6; + if (!strcmp("isActive", name)) return 7; + return -1; + } +static Bool AudioClip_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 2: + *AType = 7; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *AudioClip_Create() +{ + M_AudioClip *p; + GF_SAFEALLOC(p, M_AudioClip); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_AudioClip); + + /*default field values*/ + p->pitch = FLT2FIX(1.0); + p->startTime = 0; + p->stopTime = 0; + return (GF_Node *)p; +} + + +/* + AudioDelay Node deletion +*/ + +static void AudioDelay_Del(GF_Node *node) +{ + M_AudioDelay *p = (M_AudioDelay *) node; + gf_sg_mfint32_del(p->phaseGroup); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 AudioDelay_Def2All[] = { 2, 3, 4, 5}; +static const u16 AudioDelay_In2All[] = { 0, 1, 2, 3}; +static const u16 AudioDelay_Out2All[] = { 2, 3}; + +static u32 AudioDelay_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 4; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 6; + } +} + +static GF_Err AudioDelay_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = AudioDelay_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = AudioDelay_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = AudioDelay_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err AudioDelay_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_AudioDelay *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioDelay *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_AudioDelay *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioDelay *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioDelay *)node)->children; + return GF_OK; + case 3: + info->name = "delay"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AudioDelay *) node)->delay; + return GF_OK; + case 4: + info->name = "numChan"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_AudioDelay *) node)->numChan; + return GF_OK; + case 5: + info->name = "phaseGroup"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_AudioDelay *) node)->phaseGroup; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AudioDelay_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("delay", name)) return 3; + if (!strcmp("numChan", name)) return 4; + if (!strcmp("phaseGroup", name)) return 5; + return -1; + } +static Bool AudioDelay_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 4: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + case 5: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + default: + return 0; + } +} + + + +GF_Node *AudioDelay_Create() +{ + M_AudioDelay *p; + GF_SAFEALLOC(p, M_AudioDelay); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_AudioDelay); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->delay = 0; + p->numChan = 1; + return (GF_Node *)p; +} + + +/* + AudioFX Node deletion +*/ + +static void AudioFX_Del(GF_Node *node) +{ + M_AudioFX *p = (M_AudioFX *) node; + gf_sg_sfstring_del(p->orch); + gf_sg_sfstring_del(p->score); + gf_sg_mffloat_del(p->params); + gf_sg_mfint32_del(p->phaseGroup); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 AudioFX_Def2All[] = { 2, 3, 4, 5, 6, 7}; +static const u16 AudioFX_In2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 AudioFX_Out2All[] = { 2, 3, 4, 5}; +static const u16 AudioFX_Dyn2All[] = { 5}; + +static u32 AudioFX_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 6; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 4; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 8; + } +} + +static GF_Err AudioFX_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = AudioFX_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = AudioFX_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = AudioFX_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = AudioFX_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err AudioFX_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_AudioFX *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioFX *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_AudioFX *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioFX *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioFX *)node)->children; + return GF_OK; + case 3: + info->name = "orch"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_AudioFX *) node)->orch; + return GF_OK; + case 4: + info->name = "score"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_AudioFX *) node)->score; + return GF_OK; + case 5: + info->name = "params"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_AudioFX *) node)->params; + return GF_OK; + case 6: + info->name = "numChan"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_AudioFX *) node)->numChan; + return GF_OK; + case 7: + info->name = "phaseGroup"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_AudioFX *) node)->phaseGroup; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AudioFX_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("orch", name)) return 3; + if (!strcmp("score", name)) return 4; + if (!strcmp("params", name)) return 5; + if (!strcmp("numChan", name)) return 6; + if (!strcmp("phaseGroup", name)) return 7; + return -1; + } +static Bool AudioFX_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 5: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + case 7: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + default: + return 0; + } +} + + + +GF_Node *AudioFX_Create() +{ + M_AudioFX *p; + GF_SAFEALLOC(p, M_AudioFX); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_AudioFX); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->numChan = 1; + return (GF_Node *)p; +} + + +/* + AudioMix Node deletion +*/ + +static void AudioMix_Del(GF_Node *node) +{ + M_AudioMix *p = (M_AudioMix *) node; + gf_sg_mffloat_del(p->matrix); + gf_sg_mfint32_del(p->phaseGroup); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 AudioMix_Def2All[] = { 2, 3, 4, 5, 6}; +static const u16 AudioMix_In2All[] = { 0, 1, 2, 3, 4}; +static const u16 AudioMix_Out2All[] = { 2, 3, 4}; +static const u16 AudioMix_Dyn2All[] = { 4}; + +static u32 AudioMix_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 7; + } +} + +static GF_Err AudioMix_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = AudioMix_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = AudioMix_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = AudioMix_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = AudioMix_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err AudioMix_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_AudioMix *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioMix *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_AudioMix *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioMix *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioMix *)node)->children; + return GF_OK; + case 3: + info->name = "numInputs"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_AudioMix *) node)->numInputs; + return GF_OK; + case 4: + info->name = "matrix"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_AudioMix *) node)->matrix; + return GF_OK; + case 5: + info->name = "numChan"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_AudioMix *) node)->numChan; + return GF_OK; + case 6: + info->name = "phaseGroup"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_AudioMix *) node)->phaseGroup; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AudioMix_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("numInputs", name)) return 3; + if (!strcmp("matrix", name)) return 4; + if (!strcmp("numChan", name)) return 5; + if (!strcmp("phaseGroup", name)) return 6; + return -1; + } +static Bool AudioMix_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(1); + *b_max = FLT2FIX(255); + return 1; + case 4: + *AType = 7; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 5: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + case 6: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + default: + return 0; + } +} + + + +GF_Node *AudioMix_Create() +{ + M_AudioMix *p; + GF_SAFEALLOC(p, M_AudioMix); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_AudioMix); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->numInputs = 1; + p->numChan = 1; + return (GF_Node *)p; +} + + +/* + AudioSource Node deletion +*/ + +static void AudioSource_Del(GF_Node *node) +{ + M_AudioSource *p = (M_AudioSource *) node; + gf_sg_mfurl_del(p->url); + gf_sg_mfint32_del(p->phaseGroup); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 AudioSource_Def2All[] = { 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 AudioSource_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 AudioSource_Out2All[] = { 2, 3, 4, 5, 6, 7}; +static const u16 AudioSource_Dyn2All[] = { 4, 5}; + +static u32 AudioSource_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 8; + case GF_SG_FIELD_CODING_DEF: return 8; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 10; + } +} + +static GF_Err AudioSource_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = AudioSource_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = AudioSource_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = AudioSource_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = AudioSource_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err AudioSource_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_AudioSource *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioSource *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_AudioSource *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioSource *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioSource *)node)->children; + return GF_OK; + case 3: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_AudioSource *) node)->url; + return GF_OK; + case 4: + info->name = "pitch"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_AudioSource *) node)->pitch; + return GF_OK; + case 5: + info->name = "speed"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_AudioSource *) node)->speed; + return GF_OK; + case 6: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AudioSource *) node)->startTime; + return GF_OK; + case 7: + info->name = "stopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AudioSource *) node)->stopTime; + return GF_OK; + case 8: + info->name = "numChan"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_AudioSource *) node)->numChan; + return GF_OK; + case 9: + info->name = "phaseGroup"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_AudioSource *) node)->phaseGroup; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AudioSource_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("url", name)) return 3; + if (!strcmp("pitch", name)) return 4; + if (!strcmp("speed", name)) return 5; + if (!strcmp("startTime", name)) return 6; + if (!strcmp("stopTime", name)) return 7; + if (!strcmp("numChan", name)) return 8; + if (!strcmp("phaseGroup", name)) return 9; + return -1; + } +static Bool AudioSource_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 4: + *AType = 7; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 5: + *AType = 7; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 8: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + case 9: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + default: + return 0; + } +} + + + +GF_Node *AudioSource_Create() +{ + M_AudioSource *p; + GF_SAFEALLOC(p, M_AudioSource); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_AudioSource); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->pitch = FLT2FIX(1); + p->speed = FLT2FIX(1); + p->startTime = 0; + p->stopTime = 0; + p->numChan = 1; + return (GF_Node *)p; +} + + +/* + AudioSwitch Node deletion +*/ + +static void AudioSwitch_Del(GF_Node *node) +{ + M_AudioSwitch *p = (M_AudioSwitch *) node; + gf_sg_mfint32_del(p->whichChoice); + gf_sg_mfint32_del(p->phaseGroup); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 AudioSwitch_Def2All[] = { 2, 3, 4, 5}; +static const u16 AudioSwitch_In2All[] = { 0, 1, 2, 3}; +static const u16 AudioSwitch_Out2All[] = { 2, 3}; + +static u32 AudioSwitch_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 4; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 6; + } +} + +static GF_Err AudioSwitch_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = AudioSwitch_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = AudioSwitch_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = AudioSwitch_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err AudioSwitch_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_AudioSwitch *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioSwitch *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_AudioSwitch *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioSwitch *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_AudioSwitch *)node)->children; + return GF_OK; + case 3: + info->name = "whichChoice"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_AudioSwitch *) node)->whichChoice; + return GF_OK; + case 4: + info->name = "numChan"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_AudioSwitch *) node)->numChan; + return GF_OK; + case 5: + info->name = "phaseGroup"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_AudioSwitch *) node)->phaseGroup; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AudioSwitch_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("whichChoice", name)) return 3; + if (!strcmp("numChan", name)) return 4; + if (!strcmp("phaseGroup", name)) return 5; + return -1; + } +static Bool AudioSwitch_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 0; + *QType = 13; + *QT13_bits = 1; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 4: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + case 5: + *AType = 0; + *QType = 13; + *QT13_bits = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(255); + return 1; + default: + return 0; + } +} + + + +GF_Node *AudioSwitch_Create() +{ + M_AudioSwitch *p; + GF_SAFEALLOC(p, M_AudioSwitch); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_AudioSwitch); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->numChan = 1; + return (GF_Node *)p; +} + + +/* + Background Node deletion +*/ + +static void Background_Del(GF_Node *node) +{ + M_Background *p = (M_Background *) node; + gf_sg_mffloat_del(p->groundAngle); + gf_sg_mfcolor_del(p->groundColor); + gf_sg_mfurl_del(p->backUrl); + gf_sg_mfurl_del(p->bottomUrl); + gf_sg_mfurl_del(p->frontUrl); + gf_sg_mfurl_del(p->leftUrl); + gf_sg_mfurl_del(p->rightUrl); + gf_sg_mfurl_del(p->topUrl); + gf_sg_mffloat_del(p->skyAngle); + gf_sg_mfcolor_del(p->skyColor); + gf_node_free((GF_Node *) p); +} + +static const u16 Background_Def2All[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; +static const u16 Background_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; +static const u16 Background_Out2All[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; +static const u16 Background_Dyn2All[] = { 1, 2, 9, 10}; + +static u32 Background_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 11; + case GF_SG_FIELD_CODING_DEF: return 10; + case GF_SG_FIELD_CODING_OUT: return 11; + case GF_SG_FIELD_CODING_DYN: return 4; + default: + return 12; + } +} + +static GF_Err Background_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Background_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Background_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Background_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Background_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Background_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Background *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Background *) node)->set_bind; + return GF_OK; + case 1: + info->name = "groundAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_Background *) node)->groundAngle; + return GF_OK; + case 2: + info->name = "groundColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((M_Background *) node)->groundColor; + return GF_OK; + case 3: + info->name = "backUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_Background *) node)->backUrl; + return GF_OK; + case 4: + info->name = "bottomUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_Background *) node)->bottomUrl; + return GF_OK; + case 5: + info->name = "frontUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_Background *) node)->frontUrl; + return GF_OK; + case 6: + info->name = "leftUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_Background *) node)->leftUrl; + return GF_OK; + case 7: + info->name = "rightUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_Background *) node)->rightUrl; + return GF_OK; + case 8: + info->name = "topUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_Background *) node)->topUrl; + return GF_OK; + case 9: + info->name = "skyAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_Background *) node)->skyAngle; + return GF_OK; + case 10: + info->name = "skyColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((M_Background *) node)->skyColor; + return GF_OK; + case 11: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Background *) node)->isBound; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Background_get_field_index_by_name(char *name) +{ + if (!strcmp("set_bind", name)) return 0; + if (!strcmp("groundAngle", name)) return 1; + if (!strcmp("groundColor", name)) return 2; + if (!strcmp("backUrl", name)) return 3; + if (!strcmp("bottomUrl", name)) return 4; + if (!strcmp("frontUrl", name)) return 5; + if (!strcmp("leftUrl", name)) return 6; + if (!strcmp("rightUrl", name)) return 7; + if (!strcmp("topUrl", name)) return 8; + if (!strcmp("skyAngle", name)) return 9; + if (!strcmp("skyColor", name)) return 10; + if (!strcmp("isBound", name)) return 11; + return -1; + } +static Bool Background_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 8; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1.5707963); + return 1; + case 2: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 9: + *AType = 8; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(3.14159265); + return 1; + case 10: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *Background_Create() +{ + M_Background *p; + GF_SAFEALLOC(p, M_Background); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Background); + + /*default field values*/ + p->skyColor.vals = (SFColor*)malloc(sizeof(SFColor)*1); + p->skyColor.count = 1; + p->skyColor.vals[0].red = FLT2FIX(0); + p->skyColor.vals[0].green = FLT2FIX(0); + p->skyColor.vals[0].blue = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Background2D Node deletion +*/ + +static void Background2D_Del(GF_Node *node) +{ + M_Background2D *p = (M_Background2D *) node; + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 Background2D_Def2All[] = { 1, 2}; +static const u16 Background2D_In2All[] = { 0, 1, 2}; +static const u16 Background2D_Out2All[] = { 1, 2, 3}; +static const u16 Background2D_Dyn2All[] = { 1}; + +static u32 Background2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 4; + } +} + +static GF_Err Background2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Background2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Background2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Background2D_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Background2D_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Background2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Background2D *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Background2D *) node)->set_bind; + return GF_OK; + case 1: + info->name = "backColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_Background2D *) node)->backColor; + return GF_OK; + case 2: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_Background2D *) node)->url; + return GF_OK; + case 3: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Background2D *) node)->isBound; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Background2D_get_field_index_by_name(char *name) +{ + if (!strcmp("set_bind", name)) return 0; + if (!strcmp("backColor", name)) return 1; + if (!strcmp("url", name)) return 2; + if (!strcmp("isBound", name)) return 3; + return -1; + } +static Bool Background2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *Background2D_Create() +{ + M_Background2D *p; + GF_SAFEALLOC(p, M_Background2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Background2D); + + /*default field values*/ + p->backColor.red = FLT2FIX(0); + p->backColor.green = FLT2FIX(0); + p->backColor.blue = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Billboard Node deletion +*/ + +static void Billboard_Del(GF_Node *node) +{ + M_Billboard *p = (M_Billboard *) node; + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Billboard_Def2All[] = { 2, 3}; +static const u16 Billboard_In2All[] = { 0, 1, 2, 3}; +static const u16 Billboard_Out2All[] = { 2, 3}; +static const u16 Billboard_Dyn2All[] = { 3}; + +static u32 Billboard_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 4; + } +} + +static GF_Err Billboard_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Billboard_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Billboard_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Billboard_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Billboard_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Billboard_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Billboard *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Billboard *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Billboard *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Billboard *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Billboard *)node)->children; + return GF_OK; + case 3: + info->name = "axisOfRotation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_Billboard *) node)->axisOfRotation; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Billboard_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("axisOfRotation", name)) return 3; + return -1; + } +static Bool Billboard_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 9; + *QType = 9; + return 1; + default: + return 0; + } +} + + + +GF_Node *Billboard_Create() +{ + M_Billboard *p; + GF_SAFEALLOC(p, M_Billboard); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Billboard); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->axisOfRotation.x = FLT2FIX(0); + p->axisOfRotation.y = FLT2FIX(1); + p->axisOfRotation.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Bitmap Node deletion +*/ + +static void Bitmap_Del(GF_Node *node) +{ + M_Bitmap *p = (M_Bitmap *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 Bitmap_Def2All[] = { 0}; +static const u16 Bitmap_In2All[] = { 0}; +static const u16 Bitmap_Out2All[] = { 0}; +static const u16 Bitmap_Dyn2All[] = { 0}; + +static u32 Bitmap_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 1; + } +} + +static GF_Err Bitmap_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Bitmap_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Bitmap_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Bitmap_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Bitmap_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Bitmap_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "scale"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Bitmap *) node)->scale; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Bitmap_get_field_index_by_name(char *name) +{ + if (!strcmp("scale", name)) return 0; + return -1; + } +static Bool Bitmap_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 12; + *QType = 12; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Bitmap_Create() +{ + M_Bitmap *p; + GF_SAFEALLOC(p, M_Bitmap); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Bitmap); + + /*default field values*/ + p->scale.x = FLT2FIX(-1); + p->scale.y = FLT2FIX(-1); + return (GF_Node *)p; +} + + +/* + Box Node deletion +*/ + +static void Box_Del(GF_Node *node) +{ + M_Box *p = (M_Box *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 Box_Def2All[] = { 0}; + +static u32 Box_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 0; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 0; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 1; + } +} + +static GF_Err Box_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_DEF: + *allField = Box_Def2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Box_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "size"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_Box *) node)->size; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Box_get_field_index_by_name(char *name) +{ + if (!strcmp("size", name)) return 0; + return -1; + } +static Bool Box_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Box_Create() +{ + M_Box *p; + GF_SAFEALLOC(p, M_Box); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Box); + + /*default field values*/ + p->size.x = FLT2FIX(2); + p->size.y = FLT2FIX(2); + p->size.z = FLT2FIX(2); + return (GF_Node *)p; +} + + +/* + Circle Node deletion +*/ + +static void Circle_Del(GF_Node *node) +{ + M_Circle *p = (M_Circle *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 Circle_Def2All[] = { 0}; +static const u16 Circle_In2All[] = { 0}; +static const u16 Circle_Out2All[] = { 0}; +static const u16 Circle_Dyn2All[] = { 0}; + +static u32 Circle_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 1; + } +} + +static GF_Err Circle_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Circle_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Circle_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Circle_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Circle_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Circle_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "radius"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Circle *) node)->radius; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Circle_get_field_index_by_name(char *name) +{ + if (!strcmp("radius", name)) return 0; + return -1; + } +static Bool Circle_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 7; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Circle_Create() +{ + M_Circle *p; + GF_SAFEALLOC(p, M_Circle); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Circle); + + /*default field values*/ + p->radius = FLT2FIX(1); + return (GF_Node *)p; +} + + +/* + Collision Node deletion +*/ + +static void Collision_Del(GF_Node *node) +{ + M_Collision *p = (M_Collision *) node; + gf_node_unregister((GF_Node *) p->proxy, (GF_Node *) p); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Collision_Def2All[] = { 2, 3, 4}; +static const u16 Collision_In2All[] = { 0, 1, 2, 3}; +static const u16 Collision_Out2All[] = { 2, 3, 5}; + +static u32 Collision_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 6; + } +} + +static GF_Err Collision_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Collision_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Collision_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Collision_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Collision_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Collision *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Collision *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Collision *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Collision *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Collision *)node)->children; + return GF_OK; + case 3: + info->name = "collide"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Collision *) node)->collide; + return GF_OK; + case 4: + info->name = "proxy"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Collision *)node)->proxy; + return GF_OK; + case 5: + info->name = "collideTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_Collision *) node)->collideTime; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Collision_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("collide", name)) return 3; + if (!strcmp("proxy", name)) return 4; + if (!strcmp("collideTime", name)) return 5; + return -1; + } +static Bool Collision_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *Collision_Create() +{ + M_Collision *p; + GF_SAFEALLOC(p, M_Collision); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Collision); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->collide = 1; + return (GF_Node *)p; +} + + +/* + Color Node deletion +*/ + +static void Color_Del(GF_Node *node) +{ + M_Color *p = (M_Color *) node; + gf_sg_mfcolor_del(p->color); + gf_node_free((GF_Node *) p); +} + +static const u16 Color_Def2All[] = { 0}; +static const u16 Color_In2All[] = { 0}; +static const u16 Color_Out2All[] = { 0}; +static const u16 Color_Dyn2All[] = { 0}; + +static u32 Color_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 1; + } +} + +static GF_Err Color_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Color_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Color_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Color_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Color_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Color_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((M_Color *) node)->color; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Color_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + return -1; + } +static Bool Color_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *Color_Create() +{ + M_Color *p; + GF_SAFEALLOC(p, M_Color); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Color); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + ColorInterpolator Node deletion +*/ + +static void ColorInterpolator_Del(GF_Node *node) +{ + M_ColorInterpolator *p = (M_ColorInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfcolor_del(p->keyValue); + gf_node_free((GF_Node *) p); +} + +static const u16 ColorInterpolator_Def2All[] = { 1, 2}; +static const u16 ColorInterpolator_In2All[] = { 0, 1, 2}; +static const u16 ColorInterpolator_Out2All[] = { 1, 2, 3}; + +static u32 ColorInterpolator_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err ColorInterpolator_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ColorInterpolator_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ColorInterpolator_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ColorInterpolator_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ColorInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_ColorInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_ColorInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((M_ColorInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_ColorInterpolator *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ColorInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + return -1; + } +static Bool ColorInterpolator_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *ColorInterpolator_Create() +{ + M_ColorInterpolator *p; + GF_SAFEALLOC(p, M_ColorInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ColorInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + CompositeTexture2D Node deletion +*/ + +static void CompositeTexture2D_Del(GF_Node *node) +{ + M_CompositeTexture2D *p = (M_CompositeTexture2D *) node; + gf_node_unregister((GF_Node *) p->background, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->viewport, (GF_Node *) p); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 CompositeTexture2D_Def2All[] = { 2, 3, 4, 5, 6, 7}; +static const u16 CompositeTexture2D_In2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 CompositeTexture2D_Out2All[] = { 2, 3, 4, 5, 6}; + +static u32 CompositeTexture2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 7; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 5; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 8; + } +} + +static GF_Err CompositeTexture2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = CompositeTexture2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = CompositeTexture2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = CompositeTexture2D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err CompositeTexture2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_CompositeTexture2D *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_CompositeTexture2D *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_CompositeTexture2D *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_CompositeTexture2D *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_CompositeTexture2D *)node)->children; + return GF_OK; + case 3: + info->name = "pixelWidth"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_CompositeTexture2D *) node)->pixelWidth; + return GF_OK; + case 4: + info->name = "pixelHeight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_CompositeTexture2D *) node)->pixelHeight; + return GF_OK; + case 5: + info->name = "background"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFBackground2DNode; + info->far_ptr = & ((M_CompositeTexture2D *)node)->background; + return GF_OK; + case 6: + info->name = "viewport"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFViewportNode; + info->far_ptr = & ((M_CompositeTexture2D *)node)->viewport; + return GF_OK; + case 7: + info->name = "repeatSandT"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_CompositeTexture2D *) node)->repeatSandT; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 CompositeTexture2D_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("pixelWidth", name)) return 3; + if (!strcmp("pixelHeight", name)) return 4; + if (!strcmp("background", name)) return 5; + if (!strcmp("viewport", name)) return 6; + if (!strcmp("repeatSandT", name)) return 7; + return -1; + } +static Bool CompositeTexture2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 0; + *QType = 13; + *QT13_bits = 16; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + return 1; + case 4: + *AType = 0; + *QType = 13; + *QT13_bits = 16; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + return 1; + case 7: + *AType = 0; + *QType = 13; + *QT13_bits = 2; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(3); + return 1; + default: + return 0; + } +} + + + +GF_Node *CompositeTexture2D_Create() +{ + M_CompositeTexture2D *p; + GF_SAFEALLOC(p, M_CompositeTexture2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_CompositeTexture2D); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->pixelWidth = -1; + p->pixelHeight = -1; + p->repeatSandT = 3; + return (GF_Node *)p; +} + + +/* + CompositeTexture3D Node deletion +*/ + +static void CompositeTexture3D_Del(GF_Node *node) +{ + M_CompositeTexture3D *p = (M_CompositeTexture3D *) node; + gf_node_unregister((GF_Node *) p->background, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->fog, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->navigationInfo, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->viewpoint, (GF_Node *) p); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 CompositeTexture3D_Def2All[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10}; +static const u16 CompositeTexture3D_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; +static const u16 CompositeTexture3D_Out2All[] = { 2, 3, 4, 5, 6, 7, 8}; + +static u32 CompositeTexture3D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 9; + case GF_SG_FIELD_CODING_DEF: return 9; + case GF_SG_FIELD_CODING_OUT: return 7; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 11; + } +} + +static GF_Err CompositeTexture3D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = CompositeTexture3D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = CompositeTexture3D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = CompositeTexture3D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err CompositeTexture3D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_CompositeTexture3D *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_CompositeTexture3D *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_CompositeTexture3D *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_CompositeTexture3D *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_CompositeTexture3D *)node)->children; + return GF_OK; + case 3: + info->name = "pixelWidth"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_CompositeTexture3D *) node)->pixelWidth; + return GF_OK; + case 4: + info->name = "pixelHeight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_CompositeTexture3D *) node)->pixelHeight; + return GF_OK; + case 5: + info->name = "background"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFBackground3DNode; + info->far_ptr = & ((M_CompositeTexture3D *)node)->background; + return GF_OK; + case 6: + info->name = "fog"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFFogNode; + info->far_ptr = & ((M_CompositeTexture3D *)node)->fog; + return GF_OK; + case 7: + info->name = "navigationInfo"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNavigationInfoNode; + info->far_ptr = & ((M_CompositeTexture3D *)node)->navigationInfo; + return GF_OK; + case 8: + info->name = "viewpoint"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFViewpointNode; + info->far_ptr = & ((M_CompositeTexture3D *)node)->viewpoint; + return GF_OK; + case 9: + info->name = "repeatS"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_CompositeTexture3D *) node)->repeatS; + return GF_OK; + case 10: + info->name = "repeatT"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_CompositeTexture3D *) node)->repeatT; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 CompositeTexture3D_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("pixelWidth", name)) return 3; + if (!strcmp("pixelHeight", name)) return 4; + if (!strcmp("background", name)) return 5; + if (!strcmp("fog", name)) return 6; + if (!strcmp("navigationInfo", name)) return 7; + if (!strcmp("viewpoint", name)) return 8; + if (!strcmp("repeatS", name)) return 9; + if (!strcmp("repeatT", name)) return 10; + return -1; + } +static Bool CompositeTexture3D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 0; + *QType = 13; + *QT13_bits = 16; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + return 1; + case 4: + *AType = 0; + *QType = 13; + *QT13_bits = 16; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + return 1; + default: + return 0; + } +} + + + +GF_Node *CompositeTexture3D_Create() +{ + M_CompositeTexture3D *p; + GF_SAFEALLOC(p, M_CompositeTexture3D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_CompositeTexture3D); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->pixelWidth = -1; + p->pixelHeight = -1; + p->repeatS = 1; + p->repeatT = 1; + return (GF_Node *)p; +} + + +/* + Conditional Node deletion +*/ + +static void Conditional_Del(GF_Node *node) +{ + M_Conditional *p = (M_Conditional *) node; + gf_sg_sfcommand_del(p->buffer); + gf_node_free((GF_Node *) p); +} + +static const u16 Conditional_Def2All[] = { 2}; +static const u16 Conditional_In2All[] = { 0, 1, 2}; +static const u16 Conditional_Out2All[] = { 2, 3}; + +static u32 Conditional_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err Conditional_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Conditional_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Conditional_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Conditional_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Conditional_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "activate"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Conditional *)node)->on_activate; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Conditional *) node)->activate; + return GF_OK; + case 1: + info->name = "reverseActivate"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Conditional *)node)->on_reverseActivate; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Conditional *) node)->reverseActivate; + return GF_OK; + case 2: + info->name = "buffer"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOMMANDBUFFER; + info->far_ptr = & ((M_Conditional *) node)->buffer; + return GF_OK; + case 3: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Conditional *) node)->isActive; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Conditional_get_field_index_by_name(char *name) +{ + if (!strcmp("activate", name)) return 0; + if (!strcmp("reverseActivate", name)) return 1; + if (!strcmp("buffer", name)) return 2; + if (!strcmp("isActive", name)) return 3; + return -1; + } +static Bool Conditional_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *Conditional_Create() +{ + M_Conditional *p; + GF_SAFEALLOC(p, M_Conditional); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Conditional); + p->buffer.commandList = gf_list_new(); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Cone Node deletion +*/ + +static void Cone_Del(GF_Node *node) +{ + M_Cone *p = (M_Cone *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 Cone_Def2All[] = { 0, 1, 2, 3}; + +static u32 Cone_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 0; + case GF_SG_FIELD_CODING_DEF: return 4; + case GF_SG_FIELD_CODING_OUT: return 0; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err Cone_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_DEF: + *allField = Cone_Def2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Cone_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "bottomRadius"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Cone *) node)->bottomRadius; + return GF_OK; + case 1: + info->name = "height"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Cone *) node)->height; + return GF_OK; + case 2: + info->name = "side"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Cone *) node)->side; + return GF_OK; + case 3: + info->name = "bottom"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Cone *) node)->bottom; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Cone_get_field_index_by_name(char *name) +{ + if (!strcmp("bottomRadius", name)) return 0; + if (!strcmp("height", name)) return 1; + if (!strcmp("side", name)) return 2; + if (!strcmp("bottom", name)) return 3; + return -1; + } +static Bool Cone_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Cone_Create() +{ + M_Cone *p; + GF_SAFEALLOC(p, M_Cone); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Cone); + + /*default field values*/ + p->bottomRadius = FLT2FIX(1); + p->height = FLT2FIX(2); + p->side = 1; + p->bottom = 1; + return (GF_Node *)p; +} + + +/* + Coordinate Node deletion +*/ + +static void Coordinate_Del(GF_Node *node) +{ + M_Coordinate *p = (M_Coordinate *) node; + gf_sg_mfvec3f_del(p->point); + gf_node_free((GF_Node *) p); +} + +static const u16 Coordinate_Def2All[] = { 0}; +static const u16 Coordinate_In2All[] = { 0}; +static const u16 Coordinate_Out2All[] = { 0}; +static const u16 Coordinate_Dyn2All[] = { 0}; + +static u32 Coordinate_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 1; + } +} + +static GF_Err Coordinate_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Coordinate_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Coordinate_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Coordinate_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Coordinate_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Coordinate_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_Coordinate *) node)->point; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Coordinate_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + return -1; + } +static Bool Coordinate_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Coordinate_Create() +{ + M_Coordinate *p; + GF_SAFEALLOC(p, M_Coordinate); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Coordinate); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Coordinate2D Node deletion +*/ + +static void Coordinate2D_Del(GF_Node *node) +{ + M_Coordinate2D *p = (M_Coordinate2D *) node; + gf_sg_mfvec2f_del(p->point); + gf_node_free((GF_Node *) p); +} + +static const u16 Coordinate2D_Def2All[] = { 0}; +static const u16 Coordinate2D_In2All[] = { 0}; +static const u16 Coordinate2D_Out2All[] = { 0}; +static const u16 Coordinate2D_Dyn2All[] = { 0}; + +static u32 Coordinate2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 1; + } +} + +static GF_Err Coordinate2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Coordinate2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Coordinate2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Coordinate2D_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Coordinate2D_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Coordinate2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_Coordinate2D *) node)->point; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Coordinate2D_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + return -1; + } +static Bool Coordinate2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 2; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Coordinate2D_Create() +{ + M_Coordinate2D *p; + GF_SAFEALLOC(p, M_Coordinate2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Coordinate2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + CoordinateInterpolator Node deletion +*/ + +static void CoordinateInterpolator_Del(GF_Node *node) +{ + M_CoordinateInterpolator *p = (M_CoordinateInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec3f_del(p->keyValue); + gf_sg_mfvec3f_del(p->value_changed); + gf_node_free((GF_Node *) p); +} + +static const u16 CoordinateInterpolator_Def2All[] = { 1, 2}; +static const u16 CoordinateInterpolator_In2All[] = { 0, 1, 2}; +static const u16 CoordinateInterpolator_Out2All[] = { 1, 2, 3}; + +static u32 CoordinateInterpolator_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err CoordinateInterpolator_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = CoordinateInterpolator_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = CoordinateInterpolator_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = CoordinateInterpolator_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err CoordinateInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_CoordinateInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_CoordinateInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_CoordinateInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_CoordinateInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_CoordinateInterpolator *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 CoordinateInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + return -1; + } +static Bool CoordinateInterpolator_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *CoordinateInterpolator_Create() +{ + M_CoordinateInterpolator *p; + GF_SAFEALLOC(p, M_CoordinateInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_CoordinateInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + CoordinateInterpolator2D Node deletion +*/ + +static void CoordinateInterpolator2D_Del(GF_Node *node) +{ + M_CoordinateInterpolator2D *p = (M_CoordinateInterpolator2D *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec2f_del(p->keyValue); + gf_sg_mfvec2f_del(p->value_changed); + gf_node_free((GF_Node *) p); +} + +static const u16 CoordinateInterpolator2D_Def2All[] = { 1, 2}; +static const u16 CoordinateInterpolator2D_In2All[] = { 0, 1, 2}; +static const u16 CoordinateInterpolator2D_Out2All[] = { 1, 2, 3}; + +static u32 CoordinateInterpolator2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err CoordinateInterpolator2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = CoordinateInterpolator2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = CoordinateInterpolator2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = CoordinateInterpolator2D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err CoordinateInterpolator2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_CoordinateInterpolator2D *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_CoordinateInterpolator2D *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_CoordinateInterpolator2D *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_CoordinateInterpolator2D *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_CoordinateInterpolator2D *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 CoordinateInterpolator2D_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + return -1; + } +static Bool CoordinateInterpolator2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *CoordinateInterpolator2D_Create() +{ + M_CoordinateInterpolator2D *p; + GF_SAFEALLOC(p, M_CoordinateInterpolator2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_CoordinateInterpolator2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Curve2D Node deletion +*/ + +static void Curve2D_Del(GF_Node *node) +{ + M_Curve2D *p = (M_Curve2D *) node; + gf_node_unregister((GF_Node *) p->point, (GF_Node *) p); + gf_sg_mfint32_del(p->type); + gf_node_free((GF_Node *) p); +} + +static const u16 Curve2D_Def2All[] = { 0, 1, 2}; +static const u16 Curve2D_In2All[] = { 0, 1, 2}; +static const u16 Curve2D_Out2All[] = { 0, 1, 2}; +static const u16 Curve2D_Dyn2All[] = { 1}; + +static u32 Curve2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 3; + } +} + +static GF_Err Curve2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Curve2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Curve2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Curve2D_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Curve2D_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Curve2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinate2DNode; + info->far_ptr = & ((M_Curve2D *)node)->point; + return GF_OK; + case 1: + info->name = "fineness"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Curve2D *) node)->fineness; + return GF_OK; + case 2: + info->name = "type"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_Curve2D *) node)->type; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Curve2D_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + if (!strcmp("fineness", name)) return 1; + if (!strcmp("type", name)) return 2; + return -1; + } +static Bool Curve2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 7; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 13; + *QT13_bits = 2; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(3); + return 1; + default: + return 0; + } +} + + + +GF_Node *Curve2D_Create() +{ + M_Curve2D *p; + GF_SAFEALLOC(p, M_Curve2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Curve2D); + + /*default field values*/ + p->fineness = FLT2FIX(0.5); + return (GF_Node *)p; +} + + +/* + Cylinder Node deletion +*/ + +static void Cylinder_Del(GF_Node *node) +{ + M_Cylinder *p = (M_Cylinder *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 Cylinder_Def2All[] = { 0, 1, 2, 3, 4}; + +static u32 Cylinder_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 0; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 0; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 5; + } +} + +static GF_Err Cylinder_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_DEF: + *allField = Cylinder_Def2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Cylinder_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "bottom"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Cylinder *) node)->bottom; + return GF_OK; + case 1: + info->name = "height"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Cylinder *) node)->height; + return GF_OK; + case 2: + info->name = "radius"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Cylinder *) node)->radius; + return GF_OK; + case 3: + info->name = "side"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Cylinder *) node)->side; + return GF_OK; + case 4: + info->name = "top"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Cylinder *) node)->top; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Cylinder_get_field_index_by_name(char *name) +{ + if (!strcmp("bottom", name)) return 0; + if (!strcmp("height", name)) return 1; + if (!strcmp("radius", name)) return 2; + if (!strcmp("side", name)) return 3; + if (!strcmp("top", name)) return 4; + return -1; + } +static Bool Cylinder_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Cylinder_Create() +{ + M_Cylinder *p; + GF_SAFEALLOC(p, M_Cylinder); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Cylinder); + + /*default field values*/ + p->bottom = 1; + p->height = FLT2FIX(2); + p->radius = FLT2FIX(1); + p->side = 1; + p->top = 1; + return (GF_Node *)p; +} + + +/* + CylinderSensor Node deletion +*/ + +static void CylinderSensor_Del(GF_Node *node) +{ + M_CylinderSensor *p = (M_CylinderSensor *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 CylinderSensor_Def2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 CylinderSensor_In2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 CylinderSensor_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; + +static u32 CylinderSensor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 6; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 9; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 9; + } +} + +static GF_Err CylinderSensor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = CylinderSensor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = CylinderSensor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = CylinderSensor_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err CylinderSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "autoOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_CylinderSensor *) node)->autoOffset; + return GF_OK; + case 1: + info->name = "diskAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_CylinderSensor *) node)->diskAngle; + return GF_OK; + case 2: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_CylinderSensor *) node)->enabled; + return GF_OK; + case 3: + info->name = "maxAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_CylinderSensor *) node)->maxAngle; + return GF_OK; + case 4: + info->name = "minAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_CylinderSensor *) node)->minAngle; + return GF_OK; + case 5: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_CylinderSensor *) node)->offset; + return GF_OK; + case 6: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_CylinderSensor *) node)->isActive; + return GF_OK; + case 7: + info->name = "rotation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_CylinderSensor *) node)->rotation_changed; + return GF_OK; + case 8: + info->name = "trackPoint_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_CylinderSensor *) node)->trackPoint_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 CylinderSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("autoOffset", name)) return 0; + if (!strcmp("diskAngle", name)) return 1; + if (!strcmp("enabled", name)) return 2; + if (!strcmp("maxAngle", name)) return 3; + if (!strcmp("minAngle", name)) return 4; + if (!strcmp("offset", name)) return 5; + if (!strcmp("isActive", name)) return 6; + if (!strcmp("rotation_changed", name)) return 7; + if (!strcmp("trackPoint_changed", name)) return 8; + return -1; + } +static Bool CylinderSensor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1.5707963); + return 1; + case 3: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(-6.2831853); + *b_max = FLT2FIX(6.2831853); + return 1; + case 4: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(-6.2831853); + *b_max = FLT2FIX(6.2831853); + return 1; + case 5: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + default: + return 0; + } +} + + + +GF_Node *CylinderSensor_Create() +{ + M_CylinderSensor *p; + GF_SAFEALLOC(p, M_CylinderSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_CylinderSensor); + + /*default field values*/ + p->autoOffset = 1; + p->diskAngle = FLT2FIX(0.262); + p->enabled = 1; + p->maxAngle = FLT2FIX(-1); + p->minAngle = FLT2FIX(0); + p->offset = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + DirectionalLight Node deletion +*/ + +static void DirectionalLight_Del(GF_Node *node) +{ + M_DirectionalLight *p = (M_DirectionalLight *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 DirectionalLight_Def2All[] = { 0, 1, 2, 3, 4}; +static const u16 DirectionalLight_In2All[] = { 0, 1, 2, 3, 4}; +static const u16 DirectionalLight_Out2All[] = { 0, 1, 2, 3, 4}; +static const u16 DirectionalLight_Dyn2All[] = { 0, 1, 2, 3}; + +static u32 DirectionalLight_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 5; + case GF_SG_FIELD_CODING_DYN: return 4; + default: + return 5; + } +} + +static GF_Err DirectionalLight_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = DirectionalLight_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = DirectionalLight_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = DirectionalLight_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = DirectionalLight_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err DirectionalLight_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "ambientIntensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_DirectionalLight *) node)->ambientIntensity; + return GF_OK; + case 1: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_DirectionalLight *) node)->color; + return GF_OK; + case 2: + info->name = "direction"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_DirectionalLight *) node)->direction; + return GF_OK; + case 3: + info->name = "intensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_DirectionalLight *) node)->intensity; + return GF_OK; + case 4: + info->name = "on"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_DirectionalLight *) node)->on; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 DirectionalLight_get_field_index_by_name(char *name) +{ + if (!strcmp("ambientIntensity", name)) return 0; + if (!strcmp("color", name)) return 1; + if (!strcmp("direction", name)) return 2; + if (!strcmp("intensity", name)) return 3; + if (!strcmp("on", name)) return 4; + return -1; + } +static Bool DirectionalLight_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 1: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 9; + *QType = 9; + return 1; + case 3: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *DirectionalLight_Create() +{ + M_DirectionalLight *p; + GF_SAFEALLOC(p, M_DirectionalLight); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_DirectionalLight); + + /*default field values*/ + p->ambientIntensity = FLT2FIX(0); + p->color.red = FLT2FIX(1); + p->color.green = FLT2FIX(1); + p->color.blue = FLT2FIX(1); + p->direction.x = FLT2FIX(0); + p->direction.y = FLT2FIX(0); + p->direction.z = FLT2FIX(-1); + p->intensity = FLT2FIX(1); + p->on = 1; + return (GF_Node *)p; +} + + +/* + DiscSensor Node deletion +*/ + +static void DiscSensor_Del(GF_Node *node) +{ + M_DiscSensor *p = (M_DiscSensor *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 DiscSensor_Def2All[] = { 0, 1, 2, 3, 4}; +static const u16 DiscSensor_In2All[] = { 0, 1, 2, 3, 4}; +static const u16 DiscSensor_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; + +static u32 DiscSensor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 8; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 8; + } +} + +static GF_Err DiscSensor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = DiscSensor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = DiscSensor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = DiscSensor_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err DiscSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "autoOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_DiscSensor *) node)->autoOffset; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_DiscSensor *) node)->enabled; + return GF_OK; + case 2: + info->name = "maxAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_DiscSensor *) node)->maxAngle; + return GF_OK; + case 3: + info->name = "minAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_DiscSensor *) node)->minAngle; + return GF_OK; + case 4: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_DiscSensor *) node)->offset; + return GF_OK; + case 5: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_DiscSensor *) node)->isActive; + return GF_OK; + case 6: + info->name = "rotation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_DiscSensor *) node)->rotation_changed; + return GF_OK; + case 7: + info->name = "trackPoint_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_DiscSensor *) node)->trackPoint_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 DiscSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("autoOffset", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("maxAngle", name)) return 2; + if (!strcmp("minAngle", name)) return 3; + if (!strcmp("offset", name)) return 4; + if (!strcmp("isActive", name)) return 5; + if (!strcmp("rotation_changed", name)) return 6; + if (!strcmp("trackPoint_changed", name)) return 7; + return -1; + } +static Bool DiscSensor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 2: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(-6.2831853); + *b_max = FLT2FIX(6.2831853); + return 1; + case 3: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(-6.2831853); + *b_max = FLT2FIX(6.2831853); + return 1; + case 4: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + default: + return 0; + } +} + + + +GF_Node *DiscSensor_Create() +{ + M_DiscSensor *p; + GF_SAFEALLOC(p, M_DiscSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_DiscSensor); + + /*default field values*/ + p->autoOffset = 1; + p->enabled = 1; + p->maxAngle = FLT2FIX(-1); + p->minAngle = FLT2FIX(0); + p->offset = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + ElevationGrid Node deletion +*/ + +static void ElevationGrid_Del(GF_Node *node) +{ + M_ElevationGrid *p = (M_ElevationGrid *) node; + gf_sg_mffloat_del(p->set_height); + gf_node_unregister((GF_Node *) p->color, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->normal, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->texCoord, (GF_Node *) p); + gf_sg_mffloat_del(p->height); + gf_node_free((GF_Node *) p); +} + +static const u16 ElevationGrid_Def2All[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; +static const u16 ElevationGrid_In2All[] = { 0, 1, 2, 3}; +static const u16 ElevationGrid_Out2All[] = { 1, 2, 3}; + +static u32 ElevationGrid_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 13; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 14; + } +} + +static GF_Err ElevationGrid_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ElevationGrid_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ElevationGrid_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ElevationGrid_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ElevationGrid_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_height"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_ElevationGrid *)node)->on_set_height; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_ElevationGrid *) node)->set_height; + return GF_OK; + case 1: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((M_ElevationGrid *)node)->color; + return GF_OK; + case 2: + info->name = "normal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNormalNode; + info->far_ptr = & ((M_ElevationGrid *)node)->normal; + return GF_OK; + case 3: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((M_ElevationGrid *)node)->texCoord; + return GF_OK; + case 4: + info->name = "height"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_ElevationGrid *) node)->height; + return GF_OK; + case 5: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ElevationGrid *) node)->ccw; + return GF_OK; + case 6: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ElevationGrid *) node)->colorPerVertex; + return GF_OK; + case 7: + info->name = "creaseAngle"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ElevationGrid *) node)->creaseAngle; + return GF_OK; + case 8: + info->name = "normalPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ElevationGrid *) node)->normalPerVertex; + return GF_OK; + case 9: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ElevationGrid *) node)->solid; + return GF_OK; + case 10: + info->name = "xDimension"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_ElevationGrid *) node)->xDimension; + return GF_OK; + case 11: + info->name = "xSpacing"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ElevationGrid *) node)->xSpacing; + return GF_OK; + case 12: + info->name = "zDimension"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_ElevationGrid *) node)->zDimension; + return GF_OK; + case 13: + info->name = "zSpacing"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ElevationGrid *) node)->zSpacing; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ElevationGrid_get_field_index_by_name(char *name) +{ + if (!strcmp("set_height", name)) return 0; + if (!strcmp("color", name)) return 1; + if (!strcmp("normal", name)) return 2; + if (!strcmp("texCoord", name)) return 3; + if (!strcmp("height", name)) return 4; + if (!strcmp("ccw", name)) return 5; + if (!strcmp("colorPerVertex", name)) return 6; + if (!strcmp("creaseAngle", name)) return 7; + if (!strcmp("normalPerVertex", name)) return 8; + if (!strcmp("solid", name)) return 9; + if (!strcmp("xDimension", name)) return 10; + if (!strcmp("xSpacing", name)) return 11; + if (!strcmp("zDimension", name)) return 12; + if (!strcmp("zSpacing", name)) return 13; + return -1; + } +static Bool ElevationGrid_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 4: + *AType = 7; + *QType = 11; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 7: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + case 10: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 11: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 12: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 13: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *ElevationGrid_Create() +{ + M_ElevationGrid *p; + GF_SAFEALLOC(p, M_ElevationGrid); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ElevationGrid); + + /*default field values*/ + p->ccw = 1; + p->colorPerVertex = 1; + p->creaseAngle = FLT2FIX(0.0); + p->normalPerVertex = 1; + p->solid = 1; + p->xDimension = 0; + p->xSpacing = FLT2FIX(1.0); + p->zDimension = 0; + p->zSpacing = FLT2FIX(1.0); + return (GF_Node *)p; +} + + +/* + Extrusion Node deletion +*/ + +static void Extrusion_Del(GF_Node *node) +{ + M_Extrusion *p = (M_Extrusion *) node; + gf_sg_mfvec2f_del(p->set_crossSection); + gf_sg_mfrotation_del(p->set_orientation); + gf_sg_mfvec2f_del(p->set_scale); + gf_sg_mfvec3f_del(p->set_spine); + gf_sg_mfvec2f_del(p->crossSection); + gf_sg_mfrotation_del(p->orientation); + gf_sg_mfvec2f_del(p->scale); + gf_sg_mfvec3f_del(p->spine); + gf_node_free((GF_Node *) p); +} + +static const u16 Extrusion_Def2All[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; +static const u16 Extrusion_In2All[] = { 0, 1, 2, 3}; + +static u32 Extrusion_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 10; + case GF_SG_FIELD_CODING_OUT: return 0; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 14; + } +} + +static GF_Err Extrusion_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Extrusion_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Extrusion_Def2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Extrusion_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_crossSection"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Extrusion *)node)->on_set_crossSection; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_Extrusion *) node)->set_crossSection; + return GF_OK; + case 1: + info->name = "set_orientation"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Extrusion *)node)->on_set_orientation; + info->fieldType = GF_SG_VRML_MFROTATION; + info->far_ptr = & ((M_Extrusion *) node)->set_orientation; + return GF_OK; + case 2: + info->name = "set_scale"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Extrusion *)node)->on_set_scale; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_Extrusion *) node)->set_scale; + return GF_OK; + case 3: + info->name = "set_spine"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Extrusion *)node)->on_set_spine; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_Extrusion *) node)->set_spine; + return GF_OK; + case 4: + info->name = "beginCap"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Extrusion *) node)->beginCap; + return GF_OK; + case 5: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Extrusion *) node)->ccw; + return GF_OK; + case 6: + info->name = "convex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Extrusion *) node)->convex; + return GF_OK; + case 7: + info->name = "creaseAngle"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Extrusion *) node)->creaseAngle; + return GF_OK; + case 8: + info->name = "crossSection"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_Extrusion *) node)->crossSection; + return GF_OK; + case 9: + info->name = "endCap"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Extrusion *) node)->endCap; + return GF_OK; + case 10: + info->name = "orientation"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFROTATION; + info->far_ptr = & ((M_Extrusion *) node)->orientation; + return GF_OK; + case 11: + info->name = "scale"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_Extrusion *) node)->scale; + return GF_OK; + case 12: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Extrusion *) node)->solid; + return GF_OK; + case 13: + info->name = "spine"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_Extrusion *) node)->spine; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Extrusion_get_field_index_by_name(char *name) +{ + if (!strcmp("set_crossSection", name)) return 0; + if (!strcmp("set_orientation", name)) return 1; + if (!strcmp("set_scale", name)) return 2; + if (!strcmp("set_spine", name)) return 3; + if (!strcmp("beginCap", name)) return 4; + if (!strcmp("ccw", name)) return 5; + if (!strcmp("convex", name)) return 6; + if (!strcmp("creaseAngle", name)) return 7; + if (!strcmp("crossSection", name)) return 8; + if (!strcmp("endCap", name)) return 9; + if (!strcmp("orientation", name)) return 10; + if (!strcmp("scale", name)) return 11; + if (!strcmp("solid", name)) return 12; + if (!strcmp("spine", name)) return 13; + return -1; + } +static Bool Extrusion_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 7: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + case 8: + *AType = 0; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 10: + *AType = 0; + *QType = 10; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 11: + *AType = 0; + *QType = 7; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 13: + *AType = 0; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Extrusion_Create() +{ + M_Extrusion *p; + GF_SAFEALLOC(p, M_Extrusion); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Extrusion); + + /*default field values*/ + p->beginCap = 1; + p->ccw = 1; + p->convex = 1; + p->creaseAngle = FLT2FIX(0.0); + p->crossSection.vals = (SFVec2f*)malloc(sizeof(SFVec2f)*5); + p->crossSection.count = 5; + p->crossSection.vals[0].x = FLT2FIX(1); + p->crossSection.vals[0].y = FLT2FIX(1); + p->crossSection.vals[1].x = FLT2FIX(1); + p->crossSection.vals[1].y = FLT2FIX(-1); + p->crossSection.vals[2].x = FLT2FIX(-1); + p->crossSection.vals[2].y = FLT2FIX(-1); + p->crossSection.vals[3].x = FLT2FIX(-1); + p->crossSection.vals[3].y = FLT2FIX(1); + p->crossSection.vals[4].x = FLT2FIX(1); + p->crossSection.vals[4].y = FLT2FIX(1); + p->endCap = 1; + p->orientation.vals = (GF_Vec4*)malloc(sizeof(GF_Vec4)*1); + p->orientation.count = 1; + p->orientation.vals[0].x = FLT2FIX(0); + p->orientation.vals[0].y = FLT2FIX(0); + p->orientation.vals[0].z = FLT2FIX(1); + p->orientation.vals[0].q = FLT2FIX(0); + p->scale.vals = (SFVec2f*)malloc(sizeof(SFVec2f)*1); + p->scale.count = 1; + p->scale.vals[0].x = FLT2FIX(1); + p->scale.vals[0].y = FLT2FIX(1); + p->solid = 1; + p->spine.vals = (SFVec3f *)malloc(sizeof(SFVec3f)*2); + p->spine.count = 2; + p->spine.vals[0].x = FLT2FIX(0); + p->spine.vals[0].y = FLT2FIX(0); + p->spine.vals[0].z = FLT2FIX(0); + p->spine.vals[1].x = FLT2FIX(0); + p->spine.vals[1].y = FLT2FIX(1); + p->spine.vals[1].z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Fog Node deletion +*/ + +static void Fog_Del(GF_Node *node) +{ + M_Fog *p = (M_Fog *) node; + gf_sg_sfstring_del(p->fogType); + gf_node_free((GF_Node *) p); +} + +static const u16 Fog_Def2All[] = { 0, 1, 2}; +static const u16 Fog_In2All[] = { 0, 1, 2, 3}; +static const u16 Fog_Out2All[] = { 0, 1, 2, 4}; +static const u16 Fog_Dyn2All[] = { 0, 2}; + +static u32 Fog_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 4; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 5; + } +} + +static GF_Err Fog_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Fog_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Fog_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Fog_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Fog_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Fog_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_Fog *) node)->color; + return GF_OK; + case 1: + info->name = "fogType"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_Fog *) node)->fogType; + return GF_OK; + case 2: + info->name = "visibilityRange"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Fog *) node)->visibilityRange; + return GF_OK; + case 3: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Fog *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Fog *) node)->set_bind; + return GF_OK; + case 4: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Fog *) node)->isBound; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Fog_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("fogType", name)) return 1; + if (!strcmp("visibilityRange", name)) return 2; + if (!strcmp("set_bind", name)) return 3; + if (!strcmp("isBound", name)) return 4; + return -1; + } +static Bool Fog_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 7; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Fog_Create() +{ + M_Fog *p; + GF_SAFEALLOC(p, M_Fog); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Fog); + + /*default field values*/ + p->color.red = FLT2FIX(1); + p->color.green = FLT2FIX(1); + p->color.blue = FLT2FIX(1); + p->fogType.buffer = (char*)malloc(sizeof(char) * 7); + strcpy(p->fogType.buffer, "LINEAR"); + p->visibilityRange = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + FontStyle Node deletion +*/ + +static void FontStyle_Del(GF_Node *node) +{ + M_FontStyle *p = (M_FontStyle *) node; + gf_sg_mfstring_del(p->family); + gf_sg_mfstring_del(p->justify); + gf_sg_sfstring_del(p->language); + gf_sg_sfstring_del(p->style); + gf_node_free((GF_Node *) p); +} + +static const u16 FontStyle_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; +static const u16 FontStyle_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; +static const u16 FontStyle_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; + +static u32 FontStyle_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 9; + case GF_SG_FIELD_CODING_DEF: return 9; + case GF_SG_FIELD_CODING_OUT: return 9; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 9; + } +} + +static GF_Err FontStyle_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = FontStyle_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = FontStyle_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = FontStyle_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err FontStyle_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "family"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_FontStyle *) node)->family; + return GF_OK; + case 1: + info->name = "horizontal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_FontStyle *) node)->horizontal; + return GF_OK; + case 2: + info->name = "justify"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_FontStyle *) node)->justify; + return GF_OK; + case 3: + info->name = "language"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_FontStyle *) node)->language; + return GF_OK; + case 4: + info->name = "leftToRight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_FontStyle *) node)->leftToRight; + return GF_OK; + case 5: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_FontStyle *) node)->size; + return GF_OK; + case 6: + info->name = "spacing"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_FontStyle *) node)->spacing; + return GF_OK; + case 7: + info->name = "style"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_FontStyle *) node)->style; + return GF_OK; + case 8: + info->name = "topToBottom"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_FontStyle *) node)->topToBottom; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 FontStyle_get_field_index_by_name(char *name) +{ + if (!strcmp("family", name)) return 0; + if (!strcmp("horizontal", name)) return 1; + if (!strcmp("justify", name)) return 2; + if (!strcmp("language", name)) return 3; + if (!strcmp("leftToRight", name)) return 4; + if (!strcmp("size", name)) return 5; + if (!strcmp("spacing", name)) return 6; + if (!strcmp("style", name)) return 7; + if (!strcmp("topToBottom", name)) return 8; + return -1; + } +static Bool FontStyle_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 5: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *FontStyle_Create() +{ + M_FontStyle *p; + GF_SAFEALLOC(p, M_FontStyle); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_FontStyle); + + /*default field values*/ + p->family.vals = (char**)malloc(sizeof(SFString)*1); + p->family.count = 1; + p->family.vals[0] = (char*)malloc(sizeof(char) * 6); + strcpy(p->family.vals[0], "SERIF"); + p->horizontal = 1; + p->justify.vals = (char**)malloc(sizeof(SFString)*1); + p->justify.count = 1; + p->justify.vals[0] = (char*)malloc(sizeof(char) * 6); + strcpy(p->justify.vals[0], "BEGIN"); + p->leftToRight = 1; + p->size = FLT2FIX(1.0); + p->spacing = FLT2FIX(1.0); + p->style.buffer = (char*)malloc(sizeof(char) * 6); + strcpy(p->style.buffer, "PLAIN"); + p->topToBottom = 1; + return (GF_Node *)p; +} + + +/* + Form Node deletion +*/ + +static void Form_Del(GF_Node *node) +{ + M_Form *p = (M_Form *) node; + gf_sg_mfint32_del(p->groups); + gf_sg_mfstring_del(p->constraints); + gf_sg_mfint32_del(p->groupsIndex); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Form_Def2All[] = { 2, 3, 4, 5, 6}; +static const u16 Form_In2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 Form_Out2All[] = { 2, 3, 4, 5, 6}; +static const u16 Form_Dyn2All[] = { 3}; + +static u32 Form_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 7; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 5; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 7; + } +} + +static GF_Err Form_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Form_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Form_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Form_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Form_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Form_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Form *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Form *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Form *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Form *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Form *)node)->children; + return GF_OK; + case 3: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Form *) node)->size; + return GF_OK; + case 4: + info->name = "groups"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_Form *) node)->groups; + return GF_OK; + case 5: + info->name = "constraints"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_Form *) node)->constraints; + return GF_OK; + case 6: + info->name = "groupsIndex"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_Form *) node)->groupsIndex; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Form_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("size", name)) return 3; + if (!strcmp("groups", name)) return 4; + if (!strcmp("constraints", name)) return 5; + if (!strcmp("groupsIndex", name)) return 6; + return -1; + } +static Bool Form_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 12; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 0; + *QType = 13; + *QT13_bits = 10; + *b_min = FLT2FIX(-1); + *b_max = FLT2FIX(1022); + return 1; + case 6: + *AType = 0; + *QType = 13; + *QT13_bits = 10; + *b_min = FLT2FIX(-1); + *b_max = FLT2FIX(1022); + return 1; + default: + return 0; + } +} + + + +GF_Node *Form_Create() +{ + M_Form *p; + GF_SAFEALLOC(p, M_Form); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Form); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->size.x = FLT2FIX(-1); + p->size.y = FLT2FIX(-1); + return (GF_Node *)p; +} + + +/* + Group Node deletion +*/ + +static void Group_Del(GF_Node *node) +{ + M_Group *p = (M_Group *) node; + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Group_Def2All[] = { 2}; +static const u16 Group_In2All[] = { 0, 1, 2}; +static const u16 Group_Out2All[] = { 2}; + +static u32 Group_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 3; + } +} + +static GF_Err Group_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Group_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Group_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Group_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Group_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Group *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Group *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Group *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Group *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Group *)node)->children; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Group_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + return -1; + } +static Bool Group_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *Group_Create() +{ + M_Group *p; + GF_SAFEALLOC(p, M_Group); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Group); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + ImageTexture Node deletion +*/ + +static void ImageTexture_Del(GF_Node *node) +{ + M_ImageTexture *p = (M_ImageTexture *) node; + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 ImageTexture_Def2All[] = { 0, 1, 2}; +static const u16 ImageTexture_In2All[] = { 0}; +static const u16 ImageTexture_Out2All[] = { 0}; + +static u32 ImageTexture_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 3; + } +} + +static GF_Err ImageTexture_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ImageTexture_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ImageTexture_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ImageTexture_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ImageTexture_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_ImageTexture *) node)->url; + return GF_OK; + case 1: + info->name = "repeatS"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ImageTexture *) node)->repeatS; + return GF_OK; + case 2: + info->name = "repeatT"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ImageTexture *) node)->repeatT; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ImageTexture_get_field_index_by_name(char *name) +{ + if (!strcmp("url", name)) return 0; + if (!strcmp("repeatS", name)) return 1; + if (!strcmp("repeatT", name)) return 2; + return -1; + } +static Bool ImageTexture_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *ImageTexture_Create() +{ + M_ImageTexture *p; + GF_SAFEALLOC(p, M_ImageTexture); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ImageTexture); + + /*default field values*/ + p->repeatS = 1; + p->repeatT = 1; + return (GF_Node *)p; +} + + +/* + IndexedFaceSet Node deletion +*/ + +static void IndexedFaceSet_Del(GF_Node *node) +{ + M_IndexedFaceSet *p = (M_IndexedFaceSet *) node; + gf_sg_mfint32_del(p->set_colorIndex); + gf_sg_mfint32_del(p->set_coordIndex); + gf_sg_mfint32_del(p->set_normalIndex); + gf_sg_mfint32_del(p->set_texCoordIndex); + gf_node_unregister((GF_Node *) p->color, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->coord, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->normal, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->texCoord, (GF_Node *) p); + gf_sg_mfint32_del(p->colorIndex); + gf_sg_mfint32_del(p->coordIndex); + gf_sg_mfint32_del(p->normalIndex); + gf_sg_mfint32_del(p->texCoordIndex); + gf_node_free((GF_Node *) p); +} + +static const u16 IndexedFaceSet_Def2All[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; +static const u16 IndexedFaceSet_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 IndexedFaceSet_Out2All[] = { 4, 5, 6, 7}; + +static u32 IndexedFaceSet_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 8; + case GF_SG_FIELD_CODING_DEF: return 14; + case GF_SG_FIELD_CODING_OUT: return 4; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 18; + } +} + +static GF_Err IndexedFaceSet_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = IndexedFaceSet_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = IndexedFaceSet_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = IndexedFaceSet_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err IndexedFaceSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_colorIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedFaceSet *)node)->on_set_colorIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet *) node)->set_colorIndex; + return GF_OK; + case 1: + info->name = "set_coordIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedFaceSet *)node)->on_set_coordIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet *) node)->set_coordIndex; + return GF_OK; + case 2: + info->name = "set_normalIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedFaceSet *)node)->on_set_normalIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet *) node)->set_normalIndex; + return GF_OK; + case 3: + info->name = "set_texCoordIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedFaceSet *)node)->on_set_texCoordIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet *) node)->set_texCoordIndex; + return GF_OK; + case 4: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((M_IndexedFaceSet *)node)->color; + return GF_OK; + case 5: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((M_IndexedFaceSet *)node)->coord; + return GF_OK; + case 6: + info->name = "normal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNormalNode; + info->far_ptr = & ((M_IndexedFaceSet *)node)->normal; + return GF_OK; + case 7: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((M_IndexedFaceSet *)node)->texCoord; + return GF_OK; + case 8: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_IndexedFaceSet *) node)->ccw; + return GF_OK; + case 9: + info->name = "colorIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet *) node)->colorIndex; + return GF_OK; + case 10: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_IndexedFaceSet *) node)->colorPerVertex; + return GF_OK; + case 11: + info->name = "convex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_IndexedFaceSet *) node)->convex; + return GF_OK; + case 12: + info->name = "coordIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet *) node)->coordIndex; + return GF_OK; + case 13: + info->name = "creaseAngle"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_IndexedFaceSet *) node)->creaseAngle; + return GF_OK; + case 14: + info->name = "normalIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet *) node)->normalIndex; + return GF_OK; + case 15: + info->name = "normalPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_IndexedFaceSet *) node)->normalPerVertex; + return GF_OK; + case 16: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_IndexedFaceSet *) node)->solid; + return GF_OK; + case 17: + info->name = "texCoordIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet *) node)->texCoordIndex; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IndexedFaceSet_get_field_index_by_name(char *name) +{ + if (!strcmp("set_colorIndex", name)) return 0; + if (!strcmp("set_coordIndex", name)) return 1; + if (!strcmp("set_normalIndex", name)) return 2; + if (!strcmp("set_texCoordIndex", name)) return 3; + if (!strcmp("color", name)) return 4; + if (!strcmp("coord", name)) return 5; + if (!strcmp("normal", name)) return 6; + if (!strcmp("texCoord", name)) return 7; + if (!strcmp("ccw", name)) return 8; + if (!strcmp("colorIndex", name)) return 9; + if (!strcmp("colorPerVertex", name)) return 10; + if (!strcmp("convex", name)) return 11; + if (!strcmp("coordIndex", name)) return 12; + if (!strcmp("creaseAngle", name)) return 13; + if (!strcmp("normalIndex", name)) return 14; + if (!strcmp("normalPerVertex", name)) return 15; + if (!strcmp("solid", name)) return 16; + if (!strcmp("texCoordIndex", name)) return 17; + return -1; + } +static Bool IndexedFaceSet_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 9: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + case 12: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + case 13: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + case 14: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + case 17: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *IndexedFaceSet_Create() +{ + M_IndexedFaceSet *p; + GF_SAFEALLOC(p, M_IndexedFaceSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_IndexedFaceSet); + + /*default field values*/ + p->ccw = 1; + p->colorPerVertex = 1; + p->convex = 1; + p->creaseAngle = FLT2FIX(0.0); + p->normalPerVertex = 1; + p->solid = 1; + return (GF_Node *)p; +} + + +/* + IndexedFaceSet2D Node deletion +*/ + +static void IndexedFaceSet2D_Del(GF_Node *node) +{ + M_IndexedFaceSet2D *p = (M_IndexedFaceSet2D *) node; + gf_sg_mfint32_del(p->set_colorIndex); + gf_sg_mfint32_del(p->set_coordIndex); + gf_sg_mfint32_del(p->set_texCoordIndex); + gf_node_unregister((GF_Node *) p->color, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->coord, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->texCoord, (GF_Node *) p); + gf_sg_mfint32_del(p->colorIndex); + gf_sg_mfint32_del(p->coordIndex); + gf_sg_mfint32_del(p->texCoordIndex); + gf_node_free((GF_Node *) p); +} + +static const u16 IndexedFaceSet2D_Def2All[] = { 3, 4, 5, 6, 7, 8, 9, 10}; +static const u16 IndexedFaceSet2D_In2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 IndexedFaceSet2D_Out2All[] = { 3, 4, 5}; + +static u32 IndexedFaceSet2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 6; + case GF_SG_FIELD_CODING_DEF: return 8; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 11; + } +} + +static GF_Err IndexedFaceSet2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = IndexedFaceSet2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = IndexedFaceSet2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = IndexedFaceSet2D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err IndexedFaceSet2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_colorIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedFaceSet2D *)node)->on_set_colorIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet2D *) node)->set_colorIndex; + return GF_OK; + case 1: + info->name = "set_coordIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedFaceSet2D *)node)->on_set_coordIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet2D *) node)->set_coordIndex; + return GF_OK; + case 2: + info->name = "set_texCoordIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedFaceSet2D *)node)->on_set_texCoordIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet2D *) node)->set_texCoordIndex; + return GF_OK; + case 3: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((M_IndexedFaceSet2D *)node)->color; + return GF_OK; + case 4: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinate2DNode; + info->far_ptr = & ((M_IndexedFaceSet2D *)node)->coord; + return GF_OK; + case 5: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((M_IndexedFaceSet2D *)node)->texCoord; + return GF_OK; + case 6: + info->name = "colorIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet2D *) node)->colorIndex; + return GF_OK; + case 7: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_IndexedFaceSet2D *) node)->colorPerVertex; + return GF_OK; + case 8: + info->name = "convex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_IndexedFaceSet2D *) node)->convex; + return GF_OK; + case 9: + info->name = "coordIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet2D *) node)->coordIndex; + return GF_OK; + case 10: + info->name = "texCoordIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedFaceSet2D *) node)->texCoordIndex; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IndexedFaceSet2D_get_field_index_by_name(char *name) +{ + if (!strcmp("set_colorIndex", name)) return 0; + if (!strcmp("set_coordIndex", name)) return 1; + if (!strcmp("set_texCoordIndex", name)) return 2; + if (!strcmp("color", name)) return 3; + if (!strcmp("coord", name)) return 4; + if (!strcmp("texCoord", name)) return 5; + if (!strcmp("colorIndex", name)) return 6; + if (!strcmp("colorPerVertex", name)) return 7; + if (!strcmp("convex", name)) return 8; + if (!strcmp("coordIndex", name)) return 9; + if (!strcmp("texCoordIndex", name)) return 10; + return -1; + } +static Bool IndexedFaceSet2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 6: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + case 9: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + case 10: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *IndexedFaceSet2D_Create() +{ + M_IndexedFaceSet2D *p; + GF_SAFEALLOC(p, M_IndexedFaceSet2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_IndexedFaceSet2D); + + /*default field values*/ + p->colorPerVertex = 1; + p->convex = 1; + return (GF_Node *)p; +} + + +/* + IndexedLineSet Node deletion +*/ + +static void IndexedLineSet_Del(GF_Node *node) +{ + M_IndexedLineSet *p = (M_IndexedLineSet *) node; + gf_sg_mfint32_del(p->set_colorIndex); + gf_sg_mfint32_del(p->set_coordIndex); + gf_node_unregister((GF_Node *) p->color, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->coord, (GF_Node *) p); + gf_sg_mfint32_del(p->colorIndex); + gf_sg_mfint32_del(p->coordIndex); + gf_node_free((GF_Node *) p); +} + +static const u16 IndexedLineSet_Def2All[] = { 2, 3, 4, 5, 6}; +static const u16 IndexedLineSet_In2All[] = { 0, 1, 2, 3}; +static const u16 IndexedLineSet_Out2All[] = { 2, 3}; + +static u32 IndexedLineSet_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 7; + } +} + +static GF_Err IndexedLineSet_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = IndexedLineSet_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = IndexedLineSet_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = IndexedLineSet_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err IndexedLineSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_colorIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedLineSet *)node)->on_set_colorIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedLineSet *) node)->set_colorIndex; + return GF_OK; + case 1: + info->name = "set_coordIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedLineSet *)node)->on_set_coordIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedLineSet *) node)->set_coordIndex; + return GF_OK; + case 2: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((M_IndexedLineSet *)node)->color; + return GF_OK; + case 3: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((M_IndexedLineSet *)node)->coord; + return GF_OK; + case 4: + info->name = "colorIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedLineSet *) node)->colorIndex; + return GF_OK; + case 5: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_IndexedLineSet *) node)->colorPerVertex; + return GF_OK; + case 6: + info->name = "coordIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedLineSet *) node)->coordIndex; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IndexedLineSet_get_field_index_by_name(char *name) +{ + if (!strcmp("set_colorIndex", name)) return 0; + if (!strcmp("set_coordIndex", name)) return 1; + if (!strcmp("color", name)) return 2; + if (!strcmp("coord", name)) return 3; + if (!strcmp("colorIndex", name)) return 4; + if (!strcmp("colorPerVertex", name)) return 5; + if (!strcmp("coordIndex", name)) return 6; + return -1; + } +static Bool IndexedLineSet_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 4: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *IndexedLineSet_Create() +{ + M_IndexedLineSet *p; + GF_SAFEALLOC(p, M_IndexedLineSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_IndexedLineSet); + + /*default field values*/ + p->colorPerVertex = 1; + return (GF_Node *)p; +} + + +/* + IndexedLineSet2D Node deletion +*/ + +static void IndexedLineSet2D_Del(GF_Node *node) +{ + M_IndexedLineSet2D *p = (M_IndexedLineSet2D *) node; + gf_sg_mfint32_del(p->set_colorIndex); + gf_sg_mfint32_del(p->set_coordIndex); + gf_node_unregister((GF_Node *) p->color, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->coord, (GF_Node *) p); + gf_sg_mfint32_del(p->colorIndex); + gf_sg_mfint32_del(p->coordIndex); + gf_node_free((GF_Node *) p); +} + +static const u16 IndexedLineSet2D_Def2All[] = { 2, 3, 4, 5, 6}; +static const u16 IndexedLineSet2D_In2All[] = { 0, 1, 2, 3}; +static const u16 IndexedLineSet2D_Out2All[] = { 2, 3}; + +static u32 IndexedLineSet2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 7; + } +} + +static GF_Err IndexedLineSet2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = IndexedLineSet2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = IndexedLineSet2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = IndexedLineSet2D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err IndexedLineSet2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_colorIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedLineSet2D *)node)->on_set_colorIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedLineSet2D *) node)->set_colorIndex; + return GF_OK; + case 1: + info->name = "set_coordIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_IndexedLineSet2D *)node)->on_set_coordIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedLineSet2D *) node)->set_coordIndex; + return GF_OK; + case 2: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((M_IndexedLineSet2D *)node)->color; + return GF_OK; + case 3: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinate2DNode; + info->far_ptr = & ((M_IndexedLineSet2D *)node)->coord; + return GF_OK; + case 4: + info->name = "colorIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedLineSet2D *) node)->colorIndex; + return GF_OK; + case 5: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_IndexedLineSet2D *) node)->colorPerVertex; + return GF_OK; + case 6: + info->name = "coordIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_IndexedLineSet2D *) node)->coordIndex; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IndexedLineSet2D_get_field_index_by_name(char *name) +{ + if (!strcmp("set_colorIndex", name)) return 0; + if (!strcmp("set_coordIndex", name)) return 1; + if (!strcmp("color", name)) return 2; + if (!strcmp("coord", name)) return 3; + if (!strcmp("colorIndex", name)) return 4; + if (!strcmp("colorPerVertex", name)) return 5; + if (!strcmp("coordIndex", name)) return 6; + return -1; + } +static Bool IndexedLineSet2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 4: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 0; + *QType = 14; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *IndexedLineSet2D_Create() +{ + M_IndexedLineSet2D *p; + GF_SAFEALLOC(p, M_IndexedLineSet2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_IndexedLineSet2D); + + /*default field values*/ + p->colorPerVertex = 1; + return (GF_Node *)p; +} + + +/* + Inline Node deletion +*/ + +static void Inline_Del(GF_Node *node) +{ + M_Inline *p = (M_Inline *) node; + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 Inline_Def2All[] = { 0}; +static const u16 Inline_In2All[] = { 0}; +static const u16 Inline_Out2All[] = { 0}; + +static u32 Inline_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 1; + } +} + +static GF_Err Inline_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Inline_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Inline_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Inline_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Inline_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_Inline *) node)->url; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Inline_get_field_index_by_name(char *name) +{ + if (!strcmp("url", name)) return 0; + return -1; + } +static Bool Inline_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *Inline_Create() +{ + M_Inline *p; + GF_SAFEALLOC(p, M_Inline); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Inline); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + LOD Node deletion +*/ + +static void LOD_Del(GF_Node *node) +{ + M_LOD *p = (M_LOD *) node; + gf_node_unregister_children((GF_Node *) p, p->level); + gf_sg_mffloat_del(p->range); + gf_node_free((GF_Node *) p); +} + +static const u16 LOD_Def2All[] = { 0, 1, 2}; +static const u16 LOD_In2All[] = { 0}; +static const u16 LOD_Out2All[] = { 0}; + +static u32 LOD_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 3; + } +} + +static GF_Err LOD_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = LOD_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = LOD_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = LOD_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err LOD_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "level"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_LOD *)node)->level; + return GF_OK; + case 1: + info->name = "center"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_LOD *) node)->center; + return GF_OK; + case 2: + info->name = "range"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_LOD *) node)->range; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 LOD_get_field_index_by_name(char *name) +{ + if (!strcmp("level", name)) return 0; + if (!strcmp("center", name)) return 1; + if (!strcmp("range", name)) return 2; + return -1; + } +static Bool LOD_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *LOD_Create() +{ + M_LOD *p; + GF_SAFEALLOC(p, M_LOD); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_LOD); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->center.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Layer2D Node deletion +*/ + +static void Layer2D_Del(GF_Node *node) +{ + M_Layer2D *p = (M_Layer2D *) node; + gf_node_unregister((GF_Node *) p->background, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->viewport, (GF_Node *) p); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Layer2D_Def2All[] = { 2, 3, 4, 5}; +static const u16 Layer2D_In2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 Layer2D_Out2All[] = { 2, 3, 4, 5}; +static const u16 Layer2D_Dyn2All[] = { 3}; + +static u32 Layer2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 6; + case GF_SG_FIELD_CODING_DEF: return 4; + case GF_SG_FIELD_CODING_OUT: return 4; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 6; + } +} + +static GF_Err Layer2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Layer2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Layer2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Layer2D_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Layer2D_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Layer2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Layer2D *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Layer2D *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Layer2D *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Layer2D *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Layer2D *)node)->children; + return GF_OK; + case 3: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Layer2D *) node)->size; + return GF_OK; + case 4: + info->name = "background"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFBackground2DNode; + info->far_ptr = & ((M_Layer2D *)node)->background; + return GF_OK; + case 5: + info->name = "viewport"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFViewportNode; + info->far_ptr = & ((M_Layer2D *)node)->viewport; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Layer2D_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("size", name)) return 3; + if (!strcmp("background", name)) return 4; + if (!strcmp("viewport", name)) return 5; + return -1; + } +static Bool Layer2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 12; + *QType = 12; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Layer2D_Create() +{ + M_Layer2D *p; + GF_SAFEALLOC(p, M_Layer2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Layer2D); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->size.x = FLT2FIX(-1); + p->size.y = FLT2FIX(-1); + return (GF_Node *)p; +} + + +/* + Layer3D Node deletion +*/ + +static void Layer3D_Del(GF_Node *node) +{ + M_Layer3D *p = (M_Layer3D *) node; + gf_node_unregister((GF_Node *) p->background, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->fog, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->navigationInfo, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->viewpoint, (GF_Node *) p); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Layer3D_Def2All[] = { 2, 3, 4, 5, 6, 7}; +static const u16 Layer3D_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 Layer3D_Out2All[] = { 2, 3, 4, 5, 6, 7}; +static const u16 Layer3D_Dyn2All[] = { 3}; + +static u32 Layer3D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 8; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 8; + } +} + +static GF_Err Layer3D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Layer3D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Layer3D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Layer3D_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Layer3D_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Layer3D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Layer3D *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Layer3D *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Layer3D *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Layer3D *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Layer3D *)node)->children; + return GF_OK; + case 3: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Layer3D *) node)->size; + return GF_OK; + case 4: + info->name = "background"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFBackground3DNode; + info->far_ptr = & ((M_Layer3D *)node)->background; + return GF_OK; + case 5: + info->name = "fog"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFFogNode; + info->far_ptr = & ((M_Layer3D *)node)->fog; + return GF_OK; + case 6: + info->name = "navigationInfo"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNavigationInfoNode; + info->far_ptr = & ((M_Layer3D *)node)->navigationInfo; + return GF_OK; + case 7: + info->name = "viewpoint"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFViewpointNode; + info->far_ptr = & ((M_Layer3D *)node)->viewpoint; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Layer3D_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("size", name)) return 3; + if (!strcmp("background", name)) return 4; + if (!strcmp("fog", name)) return 5; + if (!strcmp("navigationInfo", name)) return 6; + if (!strcmp("viewpoint", name)) return 7; + return -1; + } +static Bool Layer3D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 12; + *QType = 12; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Layer3D_Create() +{ + M_Layer3D *p; + GF_SAFEALLOC(p, M_Layer3D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Layer3D); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->size.x = FLT2FIX(-1); + p->size.y = FLT2FIX(-1); + return (GF_Node *)p; +} + + +/* + Layout Node deletion +*/ + +static void Layout_Del(GF_Node *node) +{ + M_Layout *p = (M_Layout *) node; + gf_sg_mfstring_del(p->justify); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Layout_Def2All[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; +static const u16 Layout_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; +static const u16 Layout_Out2All[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; +static const u16 Layout_Dyn2All[] = { 4, 9, 13}; + +static u32 Layout_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 15; + case GF_SG_FIELD_CODING_DEF: return 13; + case GF_SG_FIELD_CODING_OUT: return 13; + case GF_SG_FIELD_CODING_DYN: return 3; + default: + return 15; + } +} + +static GF_Err Layout_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Layout_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Layout_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Layout_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Layout_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Layout_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Layout *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Layout *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Layout *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Layout *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Layout *)node)->children; + return GF_OK; + case 3: + info->name = "wrap"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Layout *) node)->wrap; + return GF_OK; + case 4: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Layout *) node)->size; + return GF_OK; + case 5: + info->name = "horizontal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Layout *) node)->horizontal; + return GF_OK; + case 6: + info->name = "justify"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_Layout *) node)->justify; + return GF_OK; + case 7: + info->name = "leftToRight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Layout *) node)->leftToRight; + return GF_OK; + case 8: + info->name = "topToBottom"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Layout *) node)->topToBottom; + return GF_OK; + case 9: + info->name = "spacing"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Layout *) node)->spacing; + return GF_OK; + case 10: + info->name = "smoothScroll"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Layout *) node)->smoothScroll; + return GF_OK; + case 11: + info->name = "loop"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Layout *) node)->loop; + return GF_OK; + case 12: + info->name = "scrollVertical"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Layout *) node)->scrollVertical; + return GF_OK; + case 13: + info->name = "scrollRate"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Layout *) node)->scrollRate; + return GF_OK; + case 14: + info->name = "scrollMode"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_Layout *) node)->scrollMode; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Layout_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("wrap", name)) return 3; + if (!strcmp("size", name)) return 4; + if (!strcmp("horizontal", name)) return 5; + if (!strcmp("justify", name)) return 6; + if (!strcmp("leftToRight", name)) return 7; + if (!strcmp("topToBottom", name)) return 8; + if (!strcmp("spacing", name)) return 9; + if (!strcmp("smoothScroll", name)) return 10; + if (!strcmp("loop", name)) return 11; + if (!strcmp("scrollVertical", name)) return 12; + if (!strcmp("scrollRate", name)) return 13; + if (!strcmp("scrollMode", name)) return 14; + return -1; + } +static Bool Layout_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 4: + *AType = 12; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 9: + *AType = 7; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 13: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 14: + *AType = 0; + *QType = 13; + *QT13_bits = 2; + *b_min = FLT2FIX(-1); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *Layout_Create() +{ + M_Layout *p; + GF_SAFEALLOC(p, M_Layout); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Layout); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->size.x = FLT2FIX(-1); + p->size.y = FLT2FIX(-1); + p->horizontal = 1; + p->justify.vals = (char**)malloc(sizeof(SFString)*1); + p->justify.count = 1; + p->justify.vals[0] = (char*)malloc(sizeof(char) * 6); + strcpy(p->justify.vals[0], "BEGIN"); + p->leftToRight = 1; + p->topToBottom = 1; + p->spacing = FLT2FIX(1); + p->scrollVertical = 1; + p->scrollRate = FLT2FIX(0); + p->scrollMode = 0; + return (GF_Node *)p; +} + + +/* + LineProperties Node deletion +*/ + +static void LineProperties_Del(GF_Node *node) +{ + M_LineProperties *p = (M_LineProperties *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 LineProperties_Def2All[] = { 0, 1, 2}; +static const u16 LineProperties_In2All[] = { 0, 1, 2}; +static const u16 LineProperties_Out2All[] = { 0, 1, 2}; +static const u16 LineProperties_Dyn2All[] = { 0, 2}; + +static u32 LineProperties_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 3; + } +} + +static GF_Err LineProperties_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = LineProperties_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = LineProperties_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = LineProperties_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = LineProperties_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err LineProperties_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "lineColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_LineProperties *) node)->lineColor; + return GF_OK; + case 1: + info->name = "lineStyle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_LineProperties *) node)->lineStyle; + return GF_OK; + case 2: + info->name = "width"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_LineProperties *) node)->width; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 LineProperties_get_field_index_by_name(char *name) +{ + if (!strcmp("lineColor", name)) return 0; + if (!strcmp("lineStyle", name)) return 1; + if (!strcmp("width", name)) return 2; + return -1; + } +static Bool LineProperties_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 1: + *AType = 0; + *QType = 13; + *QT13_bits = 3; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(5); + return 1; + case 2: + *AType = 7; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *LineProperties_Create() +{ + M_LineProperties *p; + GF_SAFEALLOC(p, M_LineProperties); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_LineProperties); + + /*default field values*/ + p->lineColor.red = FLT2FIX(0); + p->lineColor.green = FLT2FIX(0); + p->lineColor.blue = FLT2FIX(0); + p->lineStyle = 0; + p->width = FLT2FIX(1); + return (GF_Node *)p; +} + + +/* + ListeningPoint Node deletion +*/ + +static void ListeningPoint_Del(GF_Node *node) +{ + M_ListeningPoint *p = (M_ListeningPoint *) node; + gf_sg_sfstring_del(p->description); + gf_node_free((GF_Node *) p); +} + +static const u16 ListeningPoint_Def2All[] = { 1, 2, 3, 4}; +static const u16 ListeningPoint_In2All[] = { 0, 1, 2, 3}; +static const u16 ListeningPoint_Out2All[] = { 1, 2, 3, 5, 6}; +static const u16 ListeningPoint_Dyn2All[] = { 2, 3}; + +static u32 ListeningPoint_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 4; + case GF_SG_FIELD_CODING_OUT: return 5; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 7; + } +} + +static GF_Err ListeningPoint_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ListeningPoint_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ListeningPoint_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ListeningPoint_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = ListeningPoint_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ListeningPoint_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_ListeningPoint *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ListeningPoint *) node)->set_bind; + return GF_OK; + case 1: + info->name = "jump"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ListeningPoint *) node)->jump; + return GF_OK; + case 2: + info->name = "orientation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_ListeningPoint *) node)->orientation; + return GF_OK; + case 3: + info->name = "position"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_ListeningPoint *) node)->position; + return GF_OK; + case 4: + info->name = "description"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_ListeningPoint *) node)->description; + return GF_OK; + case 5: + info->name = "bindTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_ListeningPoint *) node)->bindTime; + return GF_OK; + case 6: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ListeningPoint *) node)->isBound; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ListeningPoint_get_field_index_by_name(char *name) +{ + if (!strcmp("set_bind", name)) return 0; + if (!strcmp("jump", name)) return 1; + if (!strcmp("orientation", name)) return 2; + if (!strcmp("position", name)) return 3; + if (!strcmp("description", name)) return 4; + if (!strcmp("bindTime", name)) return 5; + if (!strcmp("isBound", name)) return 6; + return -1; + } +static Bool ListeningPoint_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 2: + *AType = 10; + *QType = 10; + return 1; + case 3: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *ListeningPoint_Create() +{ + M_ListeningPoint *p; + GF_SAFEALLOC(p, M_ListeningPoint); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ListeningPoint); + + /*default field values*/ + p->jump = 1; + p->orientation.x = FLT2FIX(0); + p->orientation.y = FLT2FIX(0); + p->orientation.z = FLT2FIX(1); + p->orientation.q = FLT2FIX(0); + p->position.x = FLT2FIX(0); + p->position.y = FLT2FIX(0); + p->position.z = FLT2FIX(10); + return (GF_Node *)p; +} + + +/* + Material Node deletion +*/ + +static void Material_Del(GF_Node *node) +{ + M_Material *p = (M_Material *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 Material_Def2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 Material_In2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 Material_Out2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 Material_Dyn2All[] = { 0, 1, 2, 3, 4, 5}; + +static u32 Material_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 6; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 6; + default: + return 6; + } +} + +static GF_Err Material_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Material_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Material_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Material_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Material_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Material_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "ambientIntensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Material *) node)->ambientIntensity; + return GF_OK; + case 1: + info->name = "diffuseColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_Material *) node)->diffuseColor; + return GF_OK; + case 2: + info->name = "emissiveColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_Material *) node)->emissiveColor; + return GF_OK; + case 3: + info->name = "shininess"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Material *) node)->shininess; + return GF_OK; + case 4: + info->name = "specularColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_Material *) node)->specularColor; + return GF_OK; + case 5: + info->name = "transparency"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Material *) node)->transparency; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Material_get_field_index_by_name(char *name) +{ + if (!strcmp("ambientIntensity", name)) return 0; + if (!strcmp("diffuseColor", name)) return 1; + if (!strcmp("emissiveColor", name)) return 2; + if (!strcmp("shininess", name)) return 3; + if (!strcmp("specularColor", name)) return 4; + if (!strcmp("transparency", name)) return 5; + return -1; + } +static Bool Material_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 1: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 3: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 4: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 5: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *Material_Create() +{ + M_Material *p; + GF_SAFEALLOC(p, M_Material); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Material); + + /*default field values*/ + p->ambientIntensity = FLT2FIX(0.2); + p->diffuseColor.red = FLT2FIX(0.8); + p->diffuseColor.green = FLT2FIX(0.8); + p->diffuseColor.blue = FLT2FIX(0.8); + p->emissiveColor.red = FLT2FIX(0); + p->emissiveColor.green = FLT2FIX(0); + p->emissiveColor.blue = FLT2FIX(0); + p->shininess = FLT2FIX(0.2); + p->specularColor.red = FLT2FIX(0); + p->specularColor.green = FLT2FIX(0); + p->specularColor.blue = FLT2FIX(0); + p->transparency = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Material2D Node deletion +*/ + +static void Material2D_Del(GF_Node *node) +{ + M_Material2D *p = (M_Material2D *) node; + gf_node_unregister((GF_Node *) p->lineProps, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Material2D_Def2All[] = { 0, 1, 2, 3}; +static const u16 Material2D_In2All[] = { 0, 1, 2, 3}; +static const u16 Material2D_Out2All[] = { 0, 1, 2, 3}; +static const u16 Material2D_Dyn2All[] = { 0, 3}; + +static u32 Material2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 4; + case GF_SG_FIELD_CODING_OUT: return 4; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 4; + } +} + +static GF_Err Material2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Material2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Material2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Material2D_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Material2D_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Material2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "emissiveColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_Material2D *) node)->emissiveColor; + return GF_OK; + case 1: + info->name = "filled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Material2D *) node)->filled; + return GF_OK; + case 2: + info->name = "lineProps"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFLinePropertiesNode; + info->far_ptr = & ((M_Material2D *)node)->lineProps; + return GF_OK; + case 3: + info->name = "transparency"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Material2D *) node)->transparency; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Material2D_get_field_index_by_name(char *name) +{ + if (!strcmp("emissiveColor", name)) return 0; + if (!strcmp("filled", name)) return 1; + if (!strcmp("lineProps", name)) return 2; + if (!strcmp("transparency", name)) return 3; + return -1; + } +static Bool Material2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 3: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *Material2D_Create() +{ + M_Material2D *p; + GF_SAFEALLOC(p, M_Material2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Material2D); + + /*default field values*/ + p->emissiveColor.red = FLT2FIX(0.8); + p->emissiveColor.green = FLT2FIX(0.8); + p->emissiveColor.blue = FLT2FIX(0.8); + p->transparency = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + MovieTexture Node deletion +*/ + +static void MovieTexture_Del(GF_Node *node) +{ + M_MovieTexture *p = (M_MovieTexture *) node; + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 MovieTexture_Def2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 MovieTexture_In2All[] = { 0, 1, 2, 3, 4}; +static const u16 MovieTexture_Out2All[] = { 0, 1, 2, 3, 4, 7, 8}; +static const u16 MovieTexture_Dyn2All[] = { 1}; + +static u32 MovieTexture_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 7; + case GF_SG_FIELD_CODING_OUT: return 7; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 9; + } +} + +static GF_Err MovieTexture_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = MovieTexture_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = MovieTexture_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = MovieTexture_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = MovieTexture_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err MovieTexture_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "loop"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MovieTexture *) node)->loop; + return GF_OK; + case 1: + info->name = "speed"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_MovieTexture *) node)->speed; + return GF_OK; + case 2: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_MovieTexture *) node)->startTime; + return GF_OK; + case 3: + info->name = "stopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_MovieTexture *) node)->stopTime; + return GF_OK; + case 4: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_MovieTexture *) node)->url; + return GF_OK; + case 5: + info->name = "repeatS"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MovieTexture *) node)->repeatS; + return GF_OK; + case 6: + info->name = "repeatT"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MovieTexture *) node)->repeatT; + return GF_OK; + case 7: + info->name = "duration_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_MovieTexture *) node)->duration_changed; + return GF_OK; + case 8: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MovieTexture *) node)->isActive; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MovieTexture_get_field_index_by_name(char *name) +{ + if (!strcmp("loop", name)) return 0; + if (!strcmp("speed", name)) return 1; + if (!strcmp("startTime", name)) return 2; + if (!strcmp("stopTime", name)) return 3; + if (!strcmp("url", name)) return 4; + if (!strcmp("repeatS", name)) return 5; + if (!strcmp("repeatT", name)) return 6; + if (!strcmp("duration_changed", name)) return 7; + if (!strcmp("isActive", name)) return 8; + return -1; + } +static Bool MovieTexture_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *MovieTexture_Create() +{ + M_MovieTexture *p; + GF_SAFEALLOC(p, M_MovieTexture); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_MovieTexture); + + /*default field values*/ + p->speed = FLT2FIX(1.0); + p->startTime = 0; + p->stopTime = 0; + p->repeatS = 1; + p->repeatT = 1; + return (GF_Node *)p; +} + + +/* + NavigationInfo Node deletion +*/ + +static void NavigationInfo_Del(GF_Node *node) +{ + M_NavigationInfo *p = (M_NavigationInfo *) node; + gf_sg_mffloat_del(p->avatarSize); + gf_sg_mfstring_del(p->type); + gf_node_free((GF_Node *) p); +} + +static const u16 NavigationInfo_Def2All[] = { 1, 2, 3, 4, 5}; +static const u16 NavigationInfo_In2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 NavigationInfo_Out2All[] = { 1, 2, 3, 4, 5, 6}; +static const u16 NavigationInfo_Dyn2All[] = { 5}; + +static u32 NavigationInfo_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 6; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 7; + } +} + +static GF_Err NavigationInfo_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = NavigationInfo_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = NavigationInfo_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = NavigationInfo_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = NavigationInfo_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err NavigationInfo_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_NavigationInfo *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_NavigationInfo *) node)->set_bind; + return GF_OK; + case 1: + info->name = "avatarSize"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_NavigationInfo *) node)->avatarSize; + return GF_OK; + case 2: + info->name = "headlight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_NavigationInfo *) node)->headlight; + return GF_OK; + case 3: + info->name = "speed"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_NavigationInfo *) node)->speed; + return GF_OK; + case 4: + info->name = "type"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_NavigationInfo *) node)->type; + return GF_OK; + case 5: + info->name = "visibilityLimit"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_NavigationInfo *) node)->visibilityLimit; + return GF_OK; + case 6: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_NavigationInfo *) node)->isBound; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 NavigationInfo_get_field_index_by_name(char *name) +{ + if (!strcmp("set_bind", name)) return 0; + if (!strcmp("avatarSize", name)) return 1; + if (!strcmp("headlight", name)) return 2; + if (!strcmp("speed", name)) return 3; + if (!strcmp("type", name)) return 4; + if (!strcmp("visibilityLimit", name)) return 5; + if (!strcmp("isBound", name)) return 6; + return -1; + } +static Bool NavigationInfo_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 5: + *AType = 7; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *NavigationInfo_Create() +{ + M_NavigationInfo *p; + GF_SAFEALLOC(p, M_NavigationInfo); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_NavigationInfo); + + /*default field values*/ + p->avatarSize.vals = (SFFloat*)malloc(sizeof(SFFloat)*3); + p->avatarSize.count = 3; + p->avatarSize.vals[0] = FLT2FIX(0.25); + p->avatarSize.vals[1] = FLT2FIX(1.6); + p->avatarSize.vals[2] = FLT2FIX(0.75); + p->headlight = 1; + p->speed = FLT2FIX(1.0); + p->type.vals = (char**)malloc(sizeof(SFString)*2); + p->type.count = 2; + p->type.vals[0] = (char*)malloc(sizeof(char) * 5); + strcpy(p->type.vals[0], "WALK"); + p->type.vals[1] = (char*)malloc(sizeof(char) * 4); + strcpy(p->type.vals[1], "ANY"); + p->visibilityLimit = FLT2FIX(0.0); + return (GF_Node *)p; +} + + +/* + Normal Node deletion +*/ + +static void Normal_Del(GF_Node *node) +{ + M_Normal *p = (M_Normal *) node; + gf_sg_mfvec3f_del(p->vector); + gf_node_free((GF_Node *) p); +} + +static const u16 Normal_Def2All[] = { 0}; +static const u16 Normal_In2All[] = { 0}; +static const u16 Normal_Out2All[] = { 0}; +static const u16 Normal_Dyn2All[] = { 0}; + +static u32 Normal_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 1; + } +} + +static GF_Err Normal_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Normal_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Normal_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Normal_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Normal_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Normal_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "vector"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_Normal *) node)->vector; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Normal_get_field_index_by_name(char *name) +{ + if (!strcmp("vector", name)) return 0; + return -1; + } +static Bool Normal_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 9; + *QType = 9; + return 1; + default: + return 0; + } +} + + + +GF_Node *Normal_Create() +{ + M_Normal *p; + GF_SAFEALLOC(p, M_Normal); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Normal); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + NormalInterpolator Node deletion +*/ + +static void NormalInterpolator_Del(GF_Node *node) +{ + M_NormalInterpolator *p = (M_NormalInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec3f_del(p->keyValue); + gf_sg_mfvec3f_del(p->value_changed); + gf_node_free((GF_Node *) p); +} + +static const u16 NormalInterpolator_Def2All[] = { 1, 2}; +static const u16 NormalInterpolator_In2All[] = { 0, 1, 2}; +static const u16 NormalInterpolator_Out2All[] = { 1, 2, 3}; + +static u32 NormalInterpolator_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err NormalInterpolator_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = NormalInterpolator_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = NormalInterpolator_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = NormalInterpolator_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err NormalInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_NormalInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_NormalInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_NormalInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_NormalInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_NormalInterpolator *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 NormalInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + return -1; + } +static Bool NormalInterpolator_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 9; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *NormalInterpolator_Create() +{ + M_NormalInterpolator *p; + GF_SAFEALLOC(p, M_NormalInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_NormalInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + OrderedGroup Node deletion +*/ + +static void OrderedGroup_Del(GF_Node *node) +{ + M_OrderedGroup *p = (M_OrderedGroup *) node; + gf_sg_mffloat_del(p->order); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 OrderedGroup_Def2All[] = { 2, 3}; +static const u16 OrderedGroup_In2All[] = { 0, 1, 2, 3}; +static const u16 OrderedGroup_Out2All[] = { 2, 3}; + +static u32 OrderedGroup_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err OrderedGroup_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = OrderedGroup_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = OrderedGroup_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = OrderedGroup_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err OrderedGroup_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_OrderedGroup *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_OrderedGroup *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_OrderedGroup *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_OrderedGroup *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_OrderedGroup *)node)->children; + return GF_OK; + case 3: + info->name = "order"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_OrderedGroup *) node)->order; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 OrderedGroup_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("order", name)) return 3; + return -1; + } +static Bool OrderedGroup_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 0; + *QType = 3; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *OrderedGroup_Create() +{ + M_OrderedGroup *p; + GF_SAFEALLOC(p, M_OrderedGroup); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_OrderedGroup); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + OrientationInterpolator Node deletion +*/ + +static void OrientationInterpolator_Del(GF_Node *node) +{ + M_OrientationInterpolator *p = (M_OrientationInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfrotation_del(p->keyValue); + gf_node_free((GF_Node *) p); +} + +static const u16 OrientationInterpolator_Def2All[] = { 1, 2}; +static const u16 OrientationInterpolator_In2All[] = { 0, 1, 2}; +static const u16 OrientationInterpolator_Out2All[] = { 1, 2, 3}; + +static u32 OrientationInterpolator_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err OrientationInterpolator_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = OrientationInterpolator_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = OrientationInterpolator_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = OrientationInterpolator_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err OrientationInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_OrientationInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_OrientationInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_OrientationInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFROTATION; + info->far_ptr = & ((M_OrientationInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_OrientationInterpolator *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 OrientationInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + return -1; + } +static Bool OrientationInterpolator_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 10; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *OrientationInterpolator_Create() +{ + M_OrientationInterpolator *p; + GF_SAFEALLOC(p, M_OrientationInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_OrientationInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + PixelTexture Node deletion +*/ + +static void PixelTexture_Del(GF_Node *node) +{ + M_PixelTexture *p = (M_PixelTexture *) node; + gf_sg_sfimage_del(p->image); + gf_node_free((GF_Node *) p); +} + +static const u16 PixelTexture_Def2All[] = { 0, 1, 2}; +static const u16 PixelTexture_In2All[] = { 0}; +static const u16 PixelTexture_Out2All[] = { 0}; + +static u32 PixelTexture_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 3; + } +} + +static GF_Err PixelTexture_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PixelTexture_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PixelTexture_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PixelTexture_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PixelTexture_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "image"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFIMAGE; + info->far_ptr = & ((M_PixelTexture *) node)->image; + return GF_OK; + case 1: + info->name = "repeatS"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PixelTexture *) node)->repeatS; + return GF_OK; + case 2: + info->name = "repeatT"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PixelTexture *) node)->repeatT; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PixelTexture_get_field_index_by_name(char *name) +{ + if (!strcmp("image", name)) return 0; + if (!strcmp("repeatS", name)) return 1; + if (!strcmp("repeatT", name)) return 2; + return -1; + } +static Bool PixelTexture_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 0; + *QType = 0; + return 1; + default: + return 0; + } +} + + + +GF_Node *PixelTexture_Create() +{ + M_PixelTexture *p; + GF_SAFEALLOC(p, M_PixelTexture); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PixelTexture); + + /*default field values*/ + p->repeatS = 1; + p->repeatT = 1; + return (GF_Node *)p; +} + + +/* + PlaneSensor Node deletion +*/ + +static void PlaneSensor_Del(GF_Node *node) +{ + M_PlaneSensor *p = (M_PlaneSensor *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 PlaneSensor_Def2All[] = { 0, 1, 2, 3, 4}; +static const u16 PlaneSensor_In2All[] = { 0, 1, 2, 3, 4}; +static const u16 PlaneSensor_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; + +static u32 PlaneSensor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 8; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 8; + } +} + +static GF_Err PlaneSensor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PlaneSensor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PlaneSensor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PlaneSensor_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PlaneSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "autoOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PlaneSensor *) node)->autoOffset; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PlaneSensor *) node)->enabled; + return GF_OK; + case 2: + info->name = "maxPosition"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PlaneSensor *) node)->maxPosition; + return GF_OK; + case 3: + info->name = "minPosition"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PlaneSensor *) node)->minPosition; + return GF_OK; + case 4: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_PlaneSensor *) node)->offset; + return GF_OK; + case 5: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PlaneSensor *) node)->isActive; + return GF_OK; + case 6: + info->name = "trackPoint_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_PlaneSensor *) node)->trackPoint_changed; + return GF_OK; + case 7: + info->name = "translation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_PlaneSensor *) node)->translation_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PlaneSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("autoOffset", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("maxPosition", name)) return 2; + if (!strcmp("minPosition", name)) return 3; + if (!strcmp("offset", name)) return 4; + if (!strcmp("isActive", name)) return 5; + if (!strcmp("trackPoint_changed", name)) return 6; + if (!strcmp("translation_changed", name)) return 7; + return -1; + } +static Bool PlaneSensor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 2: + *AType = 0; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 0; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 0; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *PlaneSensor_Create() +{ + M_PlaneSensor *p; + GF_SAFEALLOC(p, M_PlaneSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PlaneSensor); + + /*default field values*/ + p->autoOffset = 1; + p->enabled = 1; + p->maxPosition.x = FLT2FIX(-1); + p->maxPosition.y = FLT2FIX(-1); + p->minPosition.x = FLT2FIX(0); + p->minPosition.y = FLT2FIX(0); + p->offset.x = FLT2FIX(0); + p->offset.y = FLT2FIX(0); + p->offset.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + PlaneSensor2D Node deletion +*/ + +static void PlaneSensor2D_Del(GF_Node *node) +{ + M_PlaneSensor2D *p = (M_PlaneSensor2D *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 PlaneSensor2D_Def2All[] = { 0, 1, 2, 3, 4}; +static const u16 PlaneSensor2D_In2All[] = { 0, 1, 2, 3, 4}; +static const u16 PlaneSensor2D_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; + +static u32 PlaneSensor2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 8; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 8; + } +} + +static GF_Err PlaneSensor2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PlaneSensor2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PlaneSensor2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PlaneSensor2D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PlaneSensor2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "autoOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PlaneSensor2D *) node)->autoOffset; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PlaneSensor2D *) node)->enabled; + return GF_OK; + case 2: + info->name = "maxPosition"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PlaneSensor2D *) node)->maxPosition; + return GF_OK; + case 3: + info->name = "minPosition"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PlaneSensor2D *) node)->minPosition; + return GF_OK; + case 4: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PlaneSensor2D *) node)->offset; + return GF_OK; + case 5: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PlaneSensor2D *) node)->isActive; + return GF_OK; + case 6: + info->name = "trackPoint_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PlaneSensor2D *) node)->trackPoint_changed; + return GF_OK; + case 7: + info->name = "translation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PlaneSensor2D *) node)->translation_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PlaneSensor2D_get_field_index_by_name(char *name) +{ + if (!strcmp("autoOffset", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("maxPosition", name)) return 2; + if (!strcmp("minPosition", name)) return 3; + if (!strcmp("offset", name)) return 4; + if (!strcmp("isActive", name)) return 5; + if (!strcmp("trackPoint_changed", name)) return 6; + if (!strcmp("translation_changed", name)) return 7; + return -1; + } +static Bool PlaneSensor2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 2: + *AType = 0; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 0; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 0; + *QType = 12; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *PlaneSensor2D_Create() +{ + M_PlaneSensor2D *p; + GF_SAFEALLOC(p, M_PlaneSensor2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PlaneSensor2D); + + /*default field values*/ + p->autoOffset = 1; + p->enabled = 1; + p->maxPosition.x = FLT2FIX(0); + p->maxPosition.y = FLT2FIX(0); + p->minPosition.x = FLT2FIX(0); + p->minPosition.y = FLT2FIX(0); + p->offset.x = FLT2FIX(0); + p->offset.y = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + PointLight Node deletion +*/ + +static void PointLight_Del(GF_Node *node) +{ + M_PointLight *p = (M_PointLight *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 PointLight_Def2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 PointLight_In2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 PointLight_Out2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 PointLight_Dyn2All[] = { 0, 1, 2, 3, 4, 6}; + +static u32 PointLight_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 7; + case GF_SG_FIELD_CODING_DEF: return 7; + case GF_SG_FIELD_CODING_OUT: return 7; + case GF_SG_FIELD_CODING_DYN: return 6; + default: + return 7; + } +} + +static GF_Err PointLight_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PointLight_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PointLight_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PointLight_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = PointLight_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PointLight_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "ambientIntensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PointLight *) node)->ambientIntensity; + return GF_OK; + case 1: + info->name = "attenuation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_PointLight *) node)->attenuation; + return GF_OK; + case 2: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_PointLight *) node)->color; + return GF_OK; + case 3: + info->name = "intensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PointLight *) node)->intensity; + return GF_OK; + case 4: + info->name = "location"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_PointLight *) node)->location; + return GF_OK; + case 5: + info->name = "on"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PointLight *) node)->on; + return GF_OK; + case 6: + info->name = "radius"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PointLight *) node)->radius; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PointLight_get_field_index_by_name(char *name) +{ + if (!strcmp("ambientIntensity", name)) return 0; + if (!strcmp("attenuation", name)) return 1; + if (!strcmp("color", name)) return 2; + if (!strcmp("intensity", name)) return 3; + if (!strcmp("location", name)) return 4; + if (!strcmp("on", name)) return 5; + if (!strcmp("radius", name)) return 6; + return -1; + } +static Bool PointLight_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 1: + *AType = 1; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 3: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 4: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 7; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *PointLight_Create() +{ + M_PointLight *p; + GF_SAFEALLOC(p, M_PointLight); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PointLight); + + /*default field values*/ + p->ambientIntensity = FLT2FIX(0); + p->attenuation.x = FLT2FIX(1); + p->attenuation.y = FLT2FIX(0); + p->attenuation.z = FLT2FIX(0); + p->color.red = FLT2FIX(1); + p->color.green = FLT2FIX(1); + p->color.blue = FLT2FIX(1); + p->intensity = FLT2FIX(1); + p->location.x = FLT2FIX(0); + p->location.y = FLT2FIX(0); + p->location.z = FLT2FIX(0); + p->on = 1; + p->radius = FLT2FIX(100); + return (GF_Node *)p; +} + + +/* + PointSet Node deletion +*/ + +static void PointSet_Del(GF_Node *node) +{ + M_PointSet *p = (M_PointSet *) node; + gf_node_unregister((GF_Node *) p->color, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->coord, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 PointSet_Def2All[] = { 0, 1}; +static const u16 PointSet_In2All[] = { 0, 1}; +static const u16 PointSet_Out2All[] = { 0, 1}; + +static u32 PointSet_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 2; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 2; + } +} + +static GF_Err PointSet_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PointSet_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PointSet_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PointSet_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PointSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((M_PointSet *)node)->color; + return GF_OK; + case 1: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((M_PointSet *)node)->coord; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PointSet_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("coord", name)) return 1; + return -1; + } +static Bool PointSet_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *PointSet_Create() +{ + M_PointSet *p; + GF_SAFEALLOC(p, M_PointSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PointSet); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + PointSet2D Node deletion +*/ + +static void PointSet2D_Del(GF_Node *node) +{ + M_PointSet2D *p = (M_PointSet2D *) node; + gf_node_unregister((GF_Node *) p->color, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->coord, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 PointSet2D_Def2All[] = { 0, 1}; +static const u16 PointSet2D_In2All[] = { 0, 1}; +static const u16 PointSet2D_Out2All[] = { 0, 1}; + +static u32 PointSet2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 2; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 2; + } +} + +static GF_Err PointSet2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PointSet2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PointSet2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PointSet2D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PointSet2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((M_PointSet2D *)node)->color; + return GF_OK; + case 1: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinate2DNode; + info->far_ptr = & ((M_PointSet2D *)node)->coord; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PointSet2D_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("coord", name)) return 1; + return -1; + } +static Bool PointSet2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *PointSet2D_Create() +{ + M_PointSet2D *p; + GF_SAFEALLOC(p, M_PointSet2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PointSet2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + PositionInterpolator Node deletion +*/ + +static void PositionInterpolator_Del(GF_Node *node) +{ + M_PositionInterpolator *p = (M_PositionInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec3f_del(p->keyValue); + gf_node_free((GF_Node *) p); +} + +static const u16 PositionInterpolator_Def2All[] = { 1, 2}; +static const u16 PositionInterpolator_In2All[] = { 0, 1, 2}; +static const u16 PositionInterpolator_Out2All[] = { 1, 2, 3}; + +static u32 PositionInterpolator_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err PositionInterpolator_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PositionInterpolator_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PositionInterpolator_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PositionInterpolator_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PositionInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_PositionInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PositionInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_PositionInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_PositionInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_PositionInterpolator *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PositionInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + return -1; + } +static Bool PositionInterpolator_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *PositionInterpolator_Create() +{ + M_PositionInterpolator *p; + GF_SAFEALLOC(p, M_PositionInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PositionInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + PositionInterpolator2D Node deletion +*/ + +static void PositionInterpolator2D_Del(GF_Node *node) +{ + M_PositionInterpolator2D *p = (M_PositionInterpolator2D *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec2f_del(p->keyValue); + gf_node_free((GF_Node *) p); +} + +static const u16 PositionInterpolator2D_Def2All[] = { 1, 2}; +static const u16 PositionInterpolator2D_In2All[] = { 0, 1, 2}; +static const u16 PositionInterpolator2D_Out2All[] = { 1, 2, 3}; + +static u32 PositionInterpolator2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err PositionInterpolator2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PositionInterpolator2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PositionInterpolator2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PositionInterpolator2D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PositionInterpolator2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_PositionInterpolator2D *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PositionInterpolator2D *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_PositionInterpolator2D *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_PositionInterpolator2D *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PositionInterpolator2D *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PositionInterpolator2D_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + return -1; + } +static Bool PositionInterpolator2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *PositionInterpolator2D_Create() +{ + M_PositionInterpolator2D *p; + GF_SAFEALLOC(p, M_PositionInterpolator2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PositionInterpolator2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + ProximitySensor2D Node deletion +*/ + +static void ProximitySensor2D_Del(GF_Node *node) +{ + M_ProximitySensor2D *p = (M_ProximitySensor2D *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 ProximitySensor2D_Def2All[] = { 0, 1, 2}; +static const u16 ProximitySensor2D_In2All[] = { 0, 1, 2}; +static const u16 ProximitySensor2D_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; + +static u32 ProximitySensor2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 8; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 8; + } +} + +static GF_Err ProximitySensor2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ProximitySensor2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ProximitySensor2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ProximitySensor2D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ProximitySensor2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_ProximitySensor2D *) node)->center; + return GF_OK; + case 1: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_ProximitySensor2D *) node)->size; + return GF_OK; + case 2: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ProximitySensor2D *) node)->enabled; + return GF_OK; + case 3: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ProximitySensor2D *) node)->isActive; + return GF_OK; + case 4: + info->name = "position_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_ProximitySensor2D *) node)->position_changed; + return GF_OK; + case 5: + info->name = "orientation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ProximitySensor2D *) node)->orientation_changed; + return GF_OK; + case 6: + info->name = "enterTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_ProximitySensor2D *) node)->enterTime; + return GF_OK; + case 7: + info->name = "exitTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_ProximitySensor2D *) node)->exitTime; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ProximitySensor2D_get_field_index_by_name(char *name) +{ + if (!strcmp("center", name)) return 0; + if (!strcmp("size", name)) return 1; + if (!strcmp("enabled", name)) return 2; + if (!strcmp("isActive", name)) return 3; + if (!strcmp("position_changed", name)) return 4; + if (!strcmp("orientation_changed", name)) return 5; + if (!strcmp("enterTime", name)) return 6; + if (!strcmp("exitTime", name)) return 7; + return -1; + } +static Bool ProximitySensor2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 0; + *QType = 2; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 0; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *ProximitySensor2D_Create() +{ + M_ProximitySensor2D *p; + GF_SAFEALLOC(p, M_ProximitySensor2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ProximitySensor2D); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->size.x = FLT2FIX(0); + p->size.y = FLT2FIX(0); + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + ProximitySensor Node deletion +*/ + +static void ProximitySensor_Del(GF_Node *node) +{ + M_ProximitySensor *p = (M_ProximitySensor *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 ProximitySensor_Def2All[] = { 0, 1, 2}; +static const u16 ProximitySensor_In2All[] = { 0, 1, 2}; +static const u16 ProximitySensor_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; + +static u32 ProximitySensor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 8; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 8; + } +} + +static GF_Err ProximitySensor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ProximitySensor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ProximitySensor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ProximitySensor_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ProximitySensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_ProximitySensor *) node)->center; + return GF_OK; + case 1: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_ProximitySensor *) node)->size; + return GF_OK; + case 2: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ProximitySensor *) node)->enabled; + return GF_OK; + case 3: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ProximitySensor *) node)->isActive; + return GF_OK; + case 4: + info->name = "position_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_ProximitySensor *) node)->position_changed; + return GF_OK; + case 5: + info->name = "orientation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_ProximitySensor *) node)->orientation_changed; + return GF_OK; + case 6: + info->name = "enterTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_ProximitySensor *) node)->enterTime; + return GF_OK; + case 7: + info->name = "exitTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_ProximitySensor *) node)->exitTime; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ProximitySensor_get_field_index_by_name(char *name) +{ + if (!strcmp("center", name)) return 0; + if (!strcmp("size", name)) return 1; + if (!strcmp("enabled", name)) return 2; + if (!strcmp("isActive", name)) return 3; + if (!strcmp("position_changed", name)) return 4; + if (!strcmp("orientation_changed", name)) return 5; + if (!strcmp("enterTime", name)) return 6; + if (!strcmp("exitTime", name)) return 7; + return -1; + } +static Bool ProximitySensor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 0; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *ProximitySensor_Create() +{ + M_ProximitySensor *p; + GF_SAFEALLOC(p, M_ProximitySensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ProximitySensor); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->center.z = FLT2FIX(0); + p->size.x = FLT2FIX(0); + p->size.y = FLT2FIX(0); + p->size.z = FLT2FIX(0); + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + QuantizationParameter Node deletion +*/ + +static void QuantizationParameter_Del(GF_Node *node) +{ + M_QuantizationParameter *p = (M_QuantizationParameter *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 QuantizationParameter_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + +static u32 QuantizationParameter_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 0; + case GF_SG_FIELD_CODING_DEF: return 40; + case GF_SG_FIELD_CODING_OUT: return 0; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 40; + } +} + +static GF_Err QuantizationParameter_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_DEF: + *allField = QuantizationParameter_Def2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err QuantizationParameter_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "isLocal"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->isLocal; + return GF_OK; + case 1: + info->name = "position3DQuant"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->position3DQuant; + return GF_OK; + case 2: + info->name = "position3DMin"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_QuantizationParameter *) node)->position3DMin; + return GF_OK; + case 3: + info->name = "position3DMax"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_QuantizationParameter *) node)->position3DMax; + return GF_OK; + case 4: + info->name = "position3DNbBits"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_QuantizationParameter *) node)->position3DNbBits; + return GF_OK; + case 5: + info->name = "position2DQuant"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->position2DQuant; + return GF_OK; + case 6: + info->name = "position2DMin"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_QuantizationParameter *) node)->position2DMin; + return GF_OK; + case 7: + info->name = "position2DMax"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_QuantizationParameter *) node)->position2DMax; + return GF_OK; + case 8: + info->name = "position2DNbBits"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_QuantizationParameter *) node)->position2DNbBits; + return GF_OK; + case 9: + info->name = "drawOrderQuant"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->drawOrderQuant; + return GF_OK; + case 10: + info->name = "drawOrderMin"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->drawOrderMin; + return GF_OK; + case 11: + info->name = "drawOrderMax"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->drawOrderMax; + return GF_OK; + case 12: + info->name = "drawOrderNbBits"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_QuantizationParameter *) node)->drawOrderNbBits; + return GF_OK; + case 13: + info->name = "colorQuant"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->colorQuant; + return GF_OK; + case 14: + info->name = "colorMin"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->colorMin; + return GF_OK; + case 15: + info->name = "colorMax"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->colorMax; + return GF_OK; + case 16: + info->name = "colorNbBits"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_QuantizationParameter *) node)->colorNbBits; + return GF_OK; + case 17: + info->name = "textureCoordinateQuant"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->textureCoordinateQuant; + return GF_OK; + case 18: + info->name = "textureCoordinateMin"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->textureCoordinateMin; + return GF_OK; + case 19: + info->name = "textureCoordinateMax"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->textureCoordinateMax; + return GF_OK; + case 20: + info->name = "textureCoordinateNbBits"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_QuantizationParameter *) node)->textureCoordinateNbBits; + return GF_OK; + case 21: + info->name = "angleQuant"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->angleQuant; + return GF_OK; + case 22: + info->name = "angleMin"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->angleMin; + return GF_OK; + case 23: + info->name = "angleMax"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->angleMax; + return GF_OK; + case 24: + info->name = "angleNbBits"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_QuantizationParameter *) node)->angleNbBits; + return GF_OK; + case 25: + info->name = "scaleQuant"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->scaleQuant; + return GF_OK; + case 26: + info->name = "scaleMin"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->scaleMin; + return GF_OK; + case 27: + info->name = "scaleMax"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->scaleMax; + return GF_OK; + case 28: + info->name = "scaleNbBits"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_QuantizationParameter *) node)->scaleNbBits; + return GF_OK; + case 29: + info->name = "keyQuant"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->keyQuant; + return GF_OK; + case 30: + info->name = "keyMin"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->keyMin; + return GF_OK; + case 31: + info->name = "keyMax"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->keyMax; + return GF_OK; + case 32: + info->name = "keyNbBits"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_QuantizationParameter *) node)->keyNbBits; + return GF_OK; + case 33: + info->name = "normalQuant"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->normalQuant; + return GF_OK; + case 34: + info->name = "normalNbBits"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_QuantizationParameter *) node)->normalNbBits; + return GF_OK; + case 35: + info->name = "sizeQuant"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->sizeQuant; + return GF_OK; + case 36: + info->name = "sizeMin"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->sizeMin; + return GF_OK; + case 37: + info->name = "sizeMax"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_QuantizationParameter *) node)->sizeMax; + return GF_OK; + case 38: + info->name = "sizeNbBits"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_QuantizationParameter *) node)->sizeNbBits; + return GF_OK; + case 39: + info->name = "useEfficientCoding"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_QuantizationParameter *) node)->useEfficientCoding; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 QuantizationParameter_get_field_index_by_name(char *name) +{ + if (!strcmp("isLocal", name)) return 0; + if (!strcmp("position3DQuant", name)) return 1; + if (!strcmp("position3DMin", name)) return 2; + if (!strcmp("position3DMax", name)) return 3; + if (!strcmp("position3DNbBits", name)) return 4; + if (!strcmp("position2DQuant", name)) return 5; + if (!strcmp("position2DMin", name)) return 6; + if (!strcmp("position2DMax", name)) return 7; + if (!strcmp("position2DNbBits", name)) return 8; + if (!strcmp("drawOrderQuant", name)) return 9; + if (!strcmp("drawOrderMin", name)) return 10; + if (!strcmp("drawOrderMax", name)) return 11; + if (!strcmp("drawOrderNbBits", name)) return 12; + if (!strcmp("colorQuant", name)) return 13; + if (!strcmp("colorMin", name)) return 14; + if (!strcmp("colorMax", name)) return 15; + if (!strcmp("colorNbBits", name)) return 16; + if (!strcmp("textureCoordinateQuant", name)) return 17; + if (!strcmp("textureCoordinateMin", name)) return 18; + if (!strcmp("textureCoordinateMax", name)) return 19; + if (!strcmp("textureCoordinateNbBits", name)) return 20; + if (!strcmp("angleQuant", name)) return 21; + if (!strcmp("angleMin", name)) return 22; + if (!strcmp("angleMax", name)) return 23; + if (!strcmp("angleNbBits", name)) return 24; + if (!strcmp("scaleQuant", name)) return 25; + if (!strcmp("scaleMin", name)) return 26; + if (!strcmp("scaleMax", name)) return 27; + if (!strcmp("scaleNbBits", name)) return 28; + if (!strcmp("keyQuant", name)) return 29; + if (!strcmp("keyMin", name)) return 30; + if (!strcmp("keyMax", name)) return 31; + if (!strcmp("keyNbBits", name)) return 32; + if (!strcmp("normalQuant", name)) return 33; + if (!strcmp("normalNbBits", name)) return 34; + if (!strcmp("sizeQuant", name)) return 35; + if (!strcmp("sizeMin", name)) return 36; + if (!strcmp("sizeMax", name)) return 37; + if (!strcmp("sizeNbBits", name)) return 38; + if (!strcmp("useEfficientCoding", name)) return 39; + return -1; + } +static Bool QuantizationParameter_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 2: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 0; + *QType = 13; + *QT13_bits = 5; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(31); + return 1; + case 6: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 7: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 8: + *AType = 0; + *QType = 13; + *QT13_bits = 5; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(31); + return 1; + case 10: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 11: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 12: + *AType = 0; + *QType = 13; + *QT13_bits = 5; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(31); + return 1; + case 14: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 15: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 16: + *AType = 0; + *QType = 13; + *QT13_bits = 5; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(31); + return 1; + case 18: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 19: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 20: + *AType = 0; + *QType = 13; + *QT13_bits = 5; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(31); + return 1; + case 22: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + case 23: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + case 24: + *AType = 0; + *QType = 13; + *QT13_bits = 5; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(31); + return 1; + case 26: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 27: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 28: + *AType = 0; + *QType = 13; + *QT13_bits = 5; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(31); + return 1; + case 30: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 31: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 32: + *AType = 0; + *QType = 13; + *QT13_bits = 5; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(31); + return 1; + case 34: + *AType = 0; + *QType = 13; + *QT13_bits = 5; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(31); + return 1; + case 36: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 37: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 38: + *AType = 0; + *QType = 13; + *QT13_bits = 5; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(31); + return 1; + default: + return 0; + } +} + + + +GF_Node *QuantizationParameter_Create() +{ + M_QuantizationParameter *p; + GF_SAFEALLOC(p, M_QuantizationParameter); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_QuantizationParameter); + + /*default field values*/ + p->position3DMin.x = FIX_MIN; + p->position3DMin.y = FIX_MIN; + p->position3DMin.z = FIX_MIN; + p->position3DMax.x = FIX_MAX; + p->position3DMax.y = FIX_MAX; + p->position3DMax.z = FIX_MAX; + p->position3DNbBits = 16; + p->position2DMin.x = FIX_MIN; + p->position2DMin.y = FIX_MIN; + p->position2DMax.x = FIX_MAX; + p->position2DMax.y = FIX_MAX; + p->position2DNbBits = 16; + p->drawOrderMin = FIX_MIN; + p->drawOrderMax = FIX_MAX; + p->drawOrderNbBits = 8; + p->colorQuant = 1; + p->colorMin = FLT2FIX(0.0); + p->colorMax = FLT2FIX(1.0); + p->colorNbBits = 8; + p->textureCoordinateQuant = 1; + p->textureCoordinateMin = FLT2FIX(0); + p->textureCoordinateMax = FLT2FIX(1); + p->textureCoordinateNbBits = 16; + p->angleQuant = 1; + p->angleMin = FLT2FIX(0.0); + p->angleMax = FLT2FIX(6.2831853); + p->angleNbBits = 16; + p->scaleMin = FLT2FIX(0.0); + p->scaleMax = FIX_MAX; + p->scaleNbBits = 8; + p->keyQuant = 1; + p->keyMin = FLT2FIX(0.0); + p->keyMax = FLT2FIX(1.0); + p->keyNbBits = 8; + p->normalQuant = 1; + p->normalNbBits = 8; + p->sizeMin = FLT2FIX(0); + p->sizeMax = FIX_MAX; + p->sizeNbBits = 8; + return (GF_Node *)p; +} + + +/* + Rectangle Node deletion +*/ + +static void Rectangle_Del(GF_Node *node) +{ + M_Rectangle *p = (M_Rectangle *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 Rectangle_Def2All[] = { 0}; +static const u16 Rectangle_In2All[] = { 0}; +static const u16 Rectangle_Out2All[] = { 0}; +static const u16 Rectangle_Dyn2All[] = { 0}; + +static u32 Rectangle_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 1; + } +} + +static GF_Err Rectangle_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Rectangle_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Rectangle_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Rectangle_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Rectangle_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Rectangle_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Rectangle *) node)->size; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Rectangle_get_field_index_by_name(char *name) +{ + if (!strcmp("size", name)) return 0; + return -1; + } +static Bool Rectangle_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 2; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Rectangle_Create() +{ + M_Rectangle *p; + GF_SAFEALLOC(p, M_Rectangle); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Rectangle); + + /*default field values*/ + p->size.x = FLT2FIX(2); + p->size.y = FLT2FIX(2); + return (GF_Node *)p; +} + + +/* + ScalarInterpolator Node deletion +*/ + +static void ScalarInterpolator_Del(GF_Node *node) +{ + M_ScalarInterpolator *p = (M_ScalarInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mffloat_del(p->keyValue); + gf_node_free((GF_Node *) p); +} + +static const u16 ScalarInterpolator_Def2All[] = { 1, 2}; +static const u16 ScalarInterpolator_In2All[] = { 0, 1, 2}; +static const u16 ScalarInterpolator_Out2All[] = { 1, 2, 3}; + +static u32 ScalarInterpolator_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err ScalarInterpolator_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ScalarInterpolator_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ScalarInterpolator_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ScalarInterpolator_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ScalarInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_ScalarInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ScalarInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_ScalarInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_ScalarInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ScalarInterpolator *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ScalarInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + return -1; + } +static Bool ScalarInterpolator_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *ScalarInterpolator_Create() +{ + M_ScalarInterpolator *p; + GF_SAFEALLOC(p, M_ScalarInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ScalarInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Script Node deletion +*/ + +static void Script_Del(GF_Node *node) +{ + M_Script *p = (M_Script *) node; + gf_sg_mfscript_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 Script_Def2All[] = { 0, 1, 2}; +static const u16 Script_In2All[] = { 0}; +static const u16 Script_Out2All[] = { 0}; + +static u32 Script_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 3; + } +} + +static GF_Err Script_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Script_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Script_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Script_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Script_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSCRIPT; + info->far_ptr = & ((M_Script *) node)->url; + return GF_OK; + case 1: + info->name = "directOutput"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Script *) node)->directOutput; + return GF_OK; + case 2: + info->name = "mustEvaluate"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Script *) node)->mustEvaluate; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Script_get_field_index_by_name(char *name) +{ + if (!strcmp("url", name)) return 0; + if (!strcmp("directOutput", name)) return 1; + if (!strcmp("mustEvaluate", name)) return 2; + return -1; + } +static Bool Script_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *Script_Create() +{ + M_Script *p; + GF_SAFEALLOC(p, M_Script); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Script); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Shape Node deletion +*/ + +static void Shape_Del(GF_Node *node) +{ + M_Shape *p = (M_Shape *) node; + gf_node_unregister((GF_Node *) p->appearance, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->geometry, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Shape_Def2All[] = { 0, 1}; +static const u16 Shape_In2All[] = { 0, 1}; +static const u16 Shape_Out2All[] = { 0, 1}; + +static u32 Shape_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 2; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 2; + } +} + +static GF_Err Shape_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Shape_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Shape_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Shape_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Shape_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "appearance"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFAppearanceNode; + info->far_ptr = & ((M_Shape *)node)->appearance; + return GF_OK; + case 1: + info->name = "geometry"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFGeometryNode; + info->far_ptr = & ((M_Shape *)node)->geometry; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Shape_get_field_index_by_name(char *name) +{ + if (!strcmp("appearance", name)) return 0; + if (!strcmp("geometry", name)) return 1; + return -1; + } +static Bool Shape_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *Shape_Create() +{ + M_Shape *p; + GF_SAFEALLOC(p, M_Shape); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Shape); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Sound Node deletion +*/ + +static void Sound_Del(GF_Node *node) +{ + M_Sound *p = (M_Sound *) node; + gf_node_unregister((GF_Node *) p->source, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Sound_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 Sound_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; +static const u16 Sound_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; +static const u16 Sound_Dyn2All[] = { 1, 2, 3, 4, 5, 6}; + +static u32 Sound_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 9; + case GF_SG_FIELD_CODING_DEF: return 10; + case GF_SG_FIELD_CODING_OUT: return 9; + case GF_SG_FIELD_CODING_DYN: return 6; + default: + return 10; + } +} + +static GF_Err Sound_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Sound_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Sound_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Sound_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Sound_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Sound_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "direction"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_Sound *) node)->direction; + return GF_OK; + case 1: + info->name = "intensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Sound *) node)->intensity; + return GF_OK; + case 2: + info->name = "location"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_Sound *) node)->location; + return GF_OK; + case 3: + info->name = "maxBack"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Sound *) node)->maxBack; + return GF_OK; + case 4: + info->name = "maxFront"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Sound *) node)->maxFront; + return GF_OK; + case 5: + info->name = "minBack"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Sound *) node)->minBack; + return GF_OK; + case 6: + info->name = "minFront"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Sound *) node)->minFront; + return GF_OK; + case 7: + info->name = "priority"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Sound *) node)->priority; + return GF_OK; + case 8: + info->name = "source"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_Sound *)node)->source; + return GF_OK; + case 9: + info->name = "spatialize"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Sound *) node)->spatialize; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Sound_get_field_index_by_name(char *name) +{ + if (!strcmp("direction", name)) return 0; + if (!strcmp("intensity", name)) return 1; + if (!strcmp("location", name)) return 2; + if (!strcmp("maxBack", name)) return 3; + if (!strcmp("maxFront", name)) return 4; + if (!strcmp("minBack", name)) return 5; + if (!strcmp("minFront", name)) return 6; + if (!strcmp("priority", name)) return 7; + if (!strcmp("source", name)) return 8; + if (!strcmp("spatialize", name)) return 9; + return -1; + } +static Bool Sound_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 0; + *QType = 9; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 7; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 7; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 7; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 5: + *AType = 7; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 7; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 7: + *AType = 0; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *Sound_Create() +{ + M_Sound *p; + GF_SAFEALLOC(p, M_Sound); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Sound); + + /*default field values*/ + p->direction.x = FLT2FIX(0); + p->direction.y = FLT2FIX(0); + p->direction.z = FLT2FIX(1); + p->intensity = FLT2FIX(1); + p->location.x = FLT2FIX(0); + p->location.y = FLT2FIX(0); + p->location.z = FLT2FIX(0); + p->maxBack = FLT2FIX(10); + p->maxFront = FLT2FIX(10); + p->minBack = FLT2FIX(1); + p->minFront = FLT2FIX(1); + p->priority = FLT2FIX(0); + p->spatialize = 1; + return (GF_Node *)p; +} + + +/* + Sound2D Node deletion +*/ + +static void Sound2D_Del(GF_Node *node) +{ + M_Sound2D *p = (M_Sound2D *) node; + gf_node_unregister((GF_Node *) p->source, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Sound2D_Def2All[] = { 0, 1, 2, 3}; +static const u16 Sound2D_In2All[] = { 0, 1, 2}; +static const u16 Sound2D_Out2All[] = { 0, 1, 2}; +static const u16 Sound2D_Dyn2All[] = { 0, 1}; + +static u32 Sound2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 4; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 4; + } +} + +static GF_Err Sound2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Sound2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Sound2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Sound2D_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Sound2D_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Sound2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "intensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Sound2D *) node)->intensity; + return GF_OK; + case 1: + info->name = "location"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Sound2D *) node)->location; + return GF_OK; + case 2: + info->name = "source"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_Sound2D *)node)->source; + return GF_OK; + case 3: + info->name = "spatialize"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Sound2D *) node)->spatialize; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Sound2D_get_field_index_by_name(char *name) +{ + if (!strcmp("intensity", name)) return 0; + if (!strcmp("location", name)) return 1; + if (!strcmp("source", name)) return 2; + if (!strcmp("spatialize", name)) return 3; + return -1; + } +static Bool Sound2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 7; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 1: + *AType = 2; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Sound2D_Create() +{ + M_Sound2D *p; + GF_SAFEALLOC(p, M_Sound2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Sound2D); + + /*default field values*/ + p->intensity = FLT2FIX(1); + p->location.x = FLT2FIX(0); + p->location.y = FLT2FIX(0); + p->spatialize = 1; + return (GF_Node *)p; +} + + +/* + Sphere Node deletion +*/ + +static void Sphere_Del(GF_Node *node) +{ + M_Sphere *p = (M_Sphere *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 Sphere_Def2All[] = { 0}; + +static u32 Sphere_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 0; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 0; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 1; + } +} + +static GF_Err Sphere_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_DEF: + *allField = Sphere_Def2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Sphere_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "radius"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Sphere *) node)->radius; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Sphere_get_field_index_by_name(char *name) +{ + if (!strcmp("radius", name)) return 0; + return -1; + } +static Bool Sphere_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Sphere_Create() +{ + M_Sphere *p; + GF_SAFEALLOC(p, M_Sphere); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Sphere); + + /*default field values*/ + p->radius = FLT2FIX(1); + return (GF_Node *)p; +} + + +/* + SphereSensor Node deletion +*/ + +static void SphereSensor_Del(GF_Node *node) +{ + M_SphereSensor *p = (M_SphereSensor *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 SphereSensor_Def2All[] = { 0, 1, 2}; +static const u16 SphereSensor_In2All[] = { 0, 1, 2}; +static const u16 SphereSensor_Out2All[] = { 0, 1, 2, 3, 4, 5}; + +static u32 SphereSensor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 6; + } +} + +static GF_Err SphereSensor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = SphereSensor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = SphereSensor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = SphereSensor_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err SphereSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "autoOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_SphereSensor *) node)->autoOffset; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_SphereSensor *) node)->enabled; + return GF_OK; + case 2: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_SphereSensor *) node)->offset; + return GF_OK; + case 3: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_SphereSensor *) node)->isActive; + return GF_OK; + case 4: + info->name = "rotation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_SphereSensor *) node)->rotation_changed; + return GF_OK; + case 5: + info->name = "trackPoint_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_SphereSensor *) node)->trackPoint_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 SphereSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("autoOffset", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("offset", name)) return 2; + if (!strcmp("isActive", name)) return 3; + if (!strcmp("rotation_changed", name)) return 4; + if (!strcmp("trackPoint_changed", name)) return 5; + return -1; + } +static Bool SphereSensor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 2: + *AType = 0; + *QType = 10; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *SphereSensor_Create() +{ + M_SphereSensor *p; + GF_SAFEALLOC(p, M_SphereSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_SphereSensor); + + /*default field values*/ + p->autoOffset = 1; + p->enabled = 1; + p->offset.x = FLT2FIX(0); + p->offset.y = FLT2FIX(1); + p->offset.z = FLT2FIX(0); + p->offset.q = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + SpotLight Node deletion +*/ + +static void SpotLight_Del(GF_Node *node) +{ + M_SpotLight *p = (M_SpotLight *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 SpotLight_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 SpotLight_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 SpotLight_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 SpotLight_Dyn2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 9}; + +static u32 SpotLight_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 10; + case GF_SG_FIELD_CODING_DEF: return 10; + case GF_SG_FIELD_CODING_OUT: return 10; + case GF_SG_FIELD_CODING_DYN: return 9; + default: + return 10; + } +} + +static GF_Err SpotLight_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = SpotLight_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = SpotLight_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = SpotLight_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = SpotLight_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err SpotLight_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "ambientIntensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_SpotLight *) node)->ambientIntensity; + return GF_OK; + case 1: + info->name = "attenuation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_SpotLight *) node)->attenuation; + return GF_OK; + case 2: + info->name = "beamWidth"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_SpotLight *) node)->beamWidth; + return GF_OK; + case 3: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_SpotLight *) node)->color; + return GF_OK; + case 4: + info->name = "cutOffAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_SpotLight *) node)->cutOffAngle; + return GF_OK; + case 5: + info->name = "direction"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_SpotLight *) node)->direction; + return GF_OK; + case 6: + info->name = "intensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_SpotLight *) node)->intensity; + return GF_OK; + case 7: + info->name = "location"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_SpotLight *) node)->location; + return GF_OK; + case 8: + info->name = "on"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_SpotLight *) node)->on; + return GF_OK; + case 9: + info->name = "radius"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_SpotLight *) node)->radius; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 SpotLight_get_field_index_by_name(char *name) +{ + if (!strcmp("ambientIntensity", name)) return 0; + if (!strcmp("attenuation", name)) return 1; + if (!strcmp("beamWidth", name)) return 2; + if (!strcmp("color", name)) return 3; + if (!strcmp("cutOffAngle", name)) return 4; + if (!strcmp("direction", name)) return 5; + if (!strcmp("intensity", name)) return 6; + if (!strcmp("location", name)) return 7; + if (!strcmp("on", name)) return 8; + if (!strcmp("radius", name)) return 9; + return -1; + } +static Bool SpotLight_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 1: + *AType = 1; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 8; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1.5707963); + return 1; + case 3: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 4: + *AType = 8; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1.5707963); + return 1; + case 5: + *AType = 9; + *QType = 9; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 7: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 9: + *AType = 7; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *SpotLight_Create() +{ + M_SpotLight *p; + GF_SAFEALLOC(p, M_SpotLight); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_SpotLight); + + /*default field values*/ + p->ambientIntensity = FLT2FIX(0); + p->attenuation.x = FLT2FIX(1); + p->attenuation.y = FLT2FIX(0); + p->attenuation.z = FLT2FIX(0); + p->beamWidth = FLT2FIX(1.570796); + p->color.red = FLT2FIX(1); + p->color.green = FLT2FIX(1); + p->color.blue = FLT2FIX(1); + p->cutOffAngle = FLT2FIX(0.785398); + p->direction.x = FLT2FIX(0); + p->direction.y = FLT2FIX(0); + p->direction.z = FLT2FIX(-1); + p->intensity = FLT2FIX(1); + p->location.x = FLT2FIX(0); + p->location.y = FLT2FIX(0); + p->location.z = FLT2FIX(0); + p->on = 1; + p->radius = FLT2FIX(100); + return (GF_Node *)p; +} + + +/* + Switch Node deletion +*/ + +static void Switch_Del(GF_Node *node) +{ + M_Switch *p = (M_Switch *) node; + gf_node_unregister_children((GF_Node *) p, p->choice); + gf_node_free((GF_Node *) p); +} + +static const u16 Switch_Def2All[] = { 0, 1}; +static const u16 Switch_In2All[] = { 0, 1}; +static const u16 Switch_Out2All[] = { 0, 1}; + +static u32 Switch_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 2; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 2; + } +} + +static GF_Err Switch_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Switch_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Switch_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Switch_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Switch_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "choice"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Switch *)node)->choice; + return GF_OK; + case 1: + info->name = "whichChoice"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_Switch *) node)->whichChoice; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Switch_get_field_index_by_name(char *name) +{ + if (!strcmp("choice", name)) return 0; + if (!strcmp("whichChoice", name)) return 1; + return -1; + } +static Bool Switch_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 13; + *QT13_bits = 10; + *b_min = FLT2FIX(-1); + *b_max = FLT2FIX( 1022); + return 1; + default: + return 0; + } +} + + + +GF_Node *Switch_Create() +{ + M_Switch *p; + GF_SAFEALLOC(p, M_Switch); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Switch); + + /*default field values*/ + p->whichChoice = -1; + return (GF_Node *)p; +} + + +/* + TermCap Node deletion +*/ + +static void TermCap_Del(GF_Node *node) +{ + M_TermCap *p = (M_TermCap *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 TermCap_Def2All[] = { 1}; +static const u16 TermCap_In2All[] = { 0, 1}; +static const u16 TermCap_Out2All[] = { 1, 2}; + +static u32 TermCap_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 2; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 3; + } +} + +static GF_Err TermCap_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = TermCap_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = TermCap_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = TermCap_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err TermCap_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "evaluate"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_TermCap *)node)->on_evaluate; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TermCap *) node)->evaluate; + return GF_OK; + case 1: + info->name = "capability"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_TermCap *) node)->capability; + return GF_OK; + case 2: + info->name = "value"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_TermCap *) node)->value; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TermCap_get_field_index_by_name(char *name) +{ + if (!strcmp("evaluate", name)) return 0; + if (!strcmp("capability", name)) return 1; + if (!strcmp("value", name)) return 2; + return -1; + } +static Bool TermCap_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 13; + *QT13_bits = 7; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(127); + return 1; + case 2: + *AType = 0; + *QType = 13; + *QT13_bits = 3; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(7); + return 1; + default: + return 0; + } +} + + + +GF_Node *TermCap_Create() +{ + M_TermCap *p; + GF_SAFEALLOC(p, M_TermCap); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_TermCap); + + /*default field values*/ + p->capability = 0; + return (GF_Node *)p; +} + + +/* + Text Node deletion +*/ + +static void Text_Del(GF_Node *node) +{ + M_Text *p = (M_Text *) node; + gf_sg_mfstring_del(p->string); + gf_sg_mffloat_del(p->length); + gf_node_unregister((GF_Node *) p->fontStyle, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Text_Def2All[] = { 0, 1, 2, 3}; +static const u16 Text_In2All[] = { 0, 1, 2, 3}; +static const u16 Text_Out2All[] = { 0, 1, 2, 3}; +static const u16 Text_Dyn2All[] = { 1, 3}; + +static u32 Text_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 4; + case GF_SG_FIELD_CODING_OUT: return 4; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 4; + } +} + +static GF_Err Text_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Text_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Text_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Text_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Text_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Text_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "string"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_Text *) node)->string; + return GF_OK; + case 1: + info->name = "length"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_Text *) node)->length; + return GF_OK; + case 2: + info->name = "fontStyle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFFontStyleNode; + info->far_ptr = & ((M_Text *)node)->fontStyle; + return GF_OK; + case 3: + info->name = "maxExtent"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Text *) node)->maxExtent; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Text_get_field_index_by_name(char *name) +{ + if (!strcmp("string", name)) return 0; + if (!strcmp("length", name)) return 1; + if (!strcmp("fontStyle", name)) return 2; + if (!strcmp("maxExtent", name)) return 3; + return -1; + } +static Bool Text_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 7; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 7; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Text_Create() +{ + M_Text *p; + GF_SAFEALLOC(p, M_Text); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Text); + + /*default field values*/ + p->maxExtent = FLT2FIX(0.0); + return (GF_Node *)p; +} + + +/* + TextureCoordinate Node deletion +*/ + +static void TextureCoordinate_Del(GF_Node *node) +{ + M_TextureCoordinate *p = (M_TextureCoordinate *) node; + gf_sg_mfvec2f_del(p->point); + gf_node_free((GF_Node *) p); +} + +static const u16 TextureCoordinate_Def2All[] = { 0}; +static const u16 TextureCoordinate_In2All[] = { 0}; +static const u16 TextureCoordinate_Out2All[] = { 0}; +static const u16 TextureCoordinate_Dyn2All[] = { 0}; + +static u32 TextureCoordinate_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 1; + } +} + +static GF_Err TextureCoordinate_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = TextureCoordinate_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = TextureCoordinate_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = TextureCoordinate_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = TextureCoordinate_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err TextureCoordinate_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_TextureCoordinate *) node)->point; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TextureCoordinate_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + return -1; + } +static Bool TextureCoordinate_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 2; + *QType = 5; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *TextureCoordinate_Create() +{ + M_TextureCoordinate *p; + GF_SAFEALLOC(p, M_TextureCoordinate); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_TextureCoordinate); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + TextureTransform Node deletion +*/ + +static void TextureTransform_Del(GF_Node *node) +{ + M_TextureTransform *p = (M_TextureTransform *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 TextureTransform_Def2All[] = { 0, 1, 2, 3}; +static const u16 TextureTransform_In2All[] = { 0, 1, 2, 3}; +static const u16 TextureTransform_Out2All[] = { 0, 1, 2, 3}; +static const u16 TextureTransform_Dyn2All[] = { 0, 1, 2, 3}; + +static u32 TextureTransform_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 4; + case GF_SG_FIELD_CODING_OUT: return 4; + case GF_SG_FIELD_CODING_DYN: return 4; + default: + return 4; + } +} + +static GF_Err TextureTransform_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = TextureTransform_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = TextureTransform_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = TextureTransform_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = TextureTransform_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err TextureTransform_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_TextureTransform *) node)->center; + return GF_OK; + case 1: + info->name = "rotation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_TextureTransform *) node)->rotation; + return GF_OK; + case 2: + info->name = "scale"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_TextureTransform *) node)->scale; + return GF_OK; + case 3: + info->name = "translation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_TextureTransform *) node)->translation; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TextureTransform_get_field_index_by_name(char *name) +{ + if (!strcmp("center", name)) return 0; + if (!strcmp("rotation", name)) return 1; + if (!strcmp("scale", name)) return 2; + if (!strcmp("translation", name)) return 3; + return -1; + } +static Bool TextureTransform_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 2; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 6; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + case 2: + *AType = 12; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 2; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *TextureTransform_Create() +{ + M_TextureTransform *p; + GF_SAFEALLOC(p, M_TextureTransform); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_TextureTransform); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->rotation = FLT2FIX(0); + p->scale.x = FLT2FIX(1); + p->scale.y = FLT2FIX(1); + p->translation.x = FLT2FIX(0); + p->translation.y = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + TimeSensor Node deletion +*/ + +static void TimeSensor_Del(GF_Node *node) +{ + M_TimeSensor *p = (M_TimeSensor *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 TimeSensor_Def2All[] = { 0, 1, 2, 3, 4}; +static const u16 TimeSensor_In2All[] = { 0, 1, 2, 3, 4}; +static const u16 TimeSensor_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; + +static u32 TimeSensor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 9; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 9; + } +} + +static GF_Err TimeSensor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = TimeSensor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = TimeSensor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = TimeSensor_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err TimeSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "cycleInterval"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TimeSensor *) node)->cycleInterval; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TimeSensor *) node)->enabled; + return GF_OK; + case 2: + info->name = "loop"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TimeSensor *) node)->loop; + return GF_OK; + case 3: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TimeSensor *) node)->startTime; + return GF_OK; + case 4: + info->name = "stopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TimeSensor *) node)->stopTime; + return GF_OK; + case 5: + info->name = "cycleTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TimeSensor *) node)->cycleTime; + return GF_OK; + case 6: + info->name = "fraction_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_TimeSensor *) node)->fraction_changed; + return GF_OK; + case 7: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TimeSensor *) node)->isActive; + return GF_OK; + case 8: + info->name = "time"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TimeSensor *) node)->time; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TimeSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("cycleInterval", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("loop", name)) return 2; + if (!strcmp("startTime", name)) return 3; + if (!strcmp("stopTime", name)) return 4; + if (!strcmp("cycleTime", name)) return 5; + if (!strcmp("fraction_changed", name)) return 6; + if (!strcmp("isActive", name)) return 7; + if (!strcmp("time", name)) return 8; + return -1; + } +static Bool TimeSensor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *TimeSensor_Create() +{ + M_TimeSensor *p; + GF_SAFEALLOC(p, M_TimeSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_TimeSensor); + + /*default field values*/ + p->cycleInterval = 1; + p->enabled = 1; + p->startTime = 0; + p->stopTime = 0; + return (GF_Node *)p; +} + + +/* + TouchSensor Node deletion +*/ + +static void TouchSensor_Del(GF_Node *node) +{ + M_TouchSensor *p = (M_TouchSensor *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 TouchSensor_Def2All[] = { 0}; +static const u16 TouchSensor_In2All[] = { 0}; +static const u16 TouchSensor_Out2All[] = { 0, 1, 2, 3, 4, 5, 6}; + +static u32 TouchSensor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 7; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 7; + } +} + +static GF_Err TouchSensor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = TouchSensor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = TouchSensor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = TouchSensor_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err TouchSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TouchSensor *) node)->enabled; + return GF_OK; + case 1: + info->name = "hitNormal_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_TouchSensor *) node)->hitNormal_changed; + return GF_OK; + case 2: + info->name = "hitPoint_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_TouchSensor *) node)->hitPoint_changed; + return GF_OK; + case 3: + info->name = "hitTexCoord_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_TouchSensor *) node)->hitTexCoord_changed; + return GF_OK; + case 4: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TouchSensor *) node)->isActive; + return GF_OK; + case 5: + info->name = "isOver"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TouchSensor *) node)->isOver; + return GF_OK; + case 6: + info->name = "touchTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TouchSensor *) node)->touchTime; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TouchSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("enabled", name)) return 0; + if (!strcmp("hitNormal_changed", name)) return 1; + if (!strcmp("hitPoint_changed", name)) return 2; + if (!strcmp("hitTexCoord_changed", name)) return 3; + if (!strcmp("isActive", name)) return 4; + if (!strcmp("isOver", name)) return 5; + if (!strcmp("touchTime", name)) return 6; + return -1; + } +static Bool TouchSensor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *TouchSensor_Create() +{ + M_TouchSensor *p; + GF_SAFEALLOC(p, M_TouchSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_TouchSensor); + + /*default field values*/ + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + Transform Node deletion +*/ + +static void Transform_Del(GF_Node *node) +{ + M_Transform *p = (M_Transform *) node; + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Transform_Def2All[] = { 2, 3, 4, 5, 6, 7}; +static const u16 Transform_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 Transform_Out2All[] = { 2, 3, 4, 5, 6, 7}; +static const u16 Transform_Dyn2All[] = { 2, 4, 5, 6, 7}; + +static u32 Transform_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 8; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 5; + default: + return 8; + } +} + +static GF_Err Transform_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Transform_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Transform_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Transform_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Transform_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Transform_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Transform *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Transform *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Transform *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Transform *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_Transform *) node)->center; + return GF_OK; + case 3: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_Transform *)node)->children; + return GF_OK; + case 4: + info->name = "rotation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_Transform *) node)->rotation; + return GF_OK; + case 5: + info->name = "scale"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_Transform *) node)->scale; + return GF_OK; + case 6: + info->name = "scaleOrientation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_Transform *) node)->scaleOrientation; + return GF_OK; + case 7: + info->name = "translation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_Transform *) node)->translation; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Transform_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("center", name)) return 2; + if (!strcmp("children", name)) return 3; + if (!strcmp("rotation", name)) return 4; + if (!strcmp("scale", name)) return 5; + if (!strcmp("scaleOrientation", name)) return 6; + if (!strcmp("translation", name)) return 7; + return -1; + } +static Bool Transform_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 2: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 10; + *QType = 10; + return 1; + case 5: + *AType = 11; + *QType = 7; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 10; + *QType = 10; + return 1; + case 7: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Transform_Create() +{ + M_Transform *p; + GF_SAFEALLOC(p, M_Transform); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Transform); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->center.z = FLT2FIX(0); + p->rotation.x = FLT2FIX(0); + p->rotation.y = FLT2FIX(0); + p->rotation.z = FLT2FIX(1); + p->rotation.q = FLT2FIX(0); + p->scale.x = FLT2FIX(1); + p->scale.y = FLT2FIX(1); + p->scale.z = FLT2FIX(1); + p->scaleOrientation.x = FLT2FIX(0); + p->scaleOrientation.y = FLT2FIX(0); + p->scaleOrientation.z = FLT2FIX(1); + p->scaleOrientation.q = FLT2FIX(0); + p->translation.x = FLT2FIX(0); + p->translation.y = FLT2FIX(0); + p->translation.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Transform2D Node deletion +*/ + +static void Transform2D_Del(GF_Node *node) +{ + M_Transform2D *p = (M_Transform2D *) node; + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Transform2D_Def2All[] = { 2, 3, 4, 5, 6, 7}; +static const u16 Transform2D_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 Transform2D_Out2All[] = { 2, 3, 4, 5, 6, 7}; +static const u16 Transform2D_Dyn2All[] = { 3, 4, 5, 6, 7}; + +static u32 Transform2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 8; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 5; + default: + return 8; + } +} + +static GF_Err Transform2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Transform2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Transform2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Transform2D_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Transform2D_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Transform2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Transform2D *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Transform2D *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Transform2D *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Transform2D *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Transform2D *)node)->children; + return GF_OK; + case 3: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Transform2D *) node)->center; + return GF_OK; + case 4: + info->name = "rotationAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Transform2D *) node)->rotationAngle; + return GF_OK; + case 5: + info->name = "scale"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Transform2D *) node)->scale; + return GF_OK; + case 6: + info->name = "scaleOrientation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Transform2D *) node)->scaleOrientation; + return GF_OK; + case 7: + info->name = "translation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Transform2D *) node)->translation; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Transform2D_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("center", name)) return 3; + if (!strcmp("rotationAngle", name)) return 4; + if (!strcmp("scale", name)) return 5; + if (!strcmp("scaleOrientation", name)) return 6; + if (!strcmp("translation", name)) return 7; + return -1; + } +static Bool Transform2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 2; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 6; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + case 5: + *AType = 12; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 6; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + case 7: + *AType = 2; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Transform2D_Create() +{ + M_Transform2D *p; + GF_SAFEALLOC(p, M_Transform2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Transform2D); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->rotationAngle = FLT2FIX(0); + p->scale.x = FLT2FIX(1); + p->scale.y = FLT2FIX(1); + p->scaleOrientation = FLT2FIX(0); + p->translation.x = FLT2FIX(0); + p->translation.y = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Valuator Node deletion +*/ + +static void Valuator_Del(GF_Node *node) +{ + M_Valuator *p = (M_Valuator *) node; + gf_sg_mfcolor_del(p->inMFColor); + gf_sg_mffloat_del(p->inMFFloat); + gf_sg_mfint32_del(p->inMFInt32); + gf_sg_mfrotation_del(p->inMFRotation); + gf_sg_sfstring_del(p->inSFString); + gf_sg_mfstring_del(p->inMFString); + gf_sg_mfvec2f_del(p->inMFVec2f); + gf_sg_mfvec3f_del(p->inMFVec3f); + gf_sg_mfcolor_del(p->outMFColor); + gf_sg_mffloat_del(p->outMFFloat); + gf_sg_mfint32_del(p->outMFInt32); + gf_sg_mfrotation_del(p->outMFRotation); + gf_sg_sfstring_del(p->outSFString); + gf_sg_mfstring_del(p->outMFString); + gf_sg_mfvec2f_del(p->outMFVec2f); + gf_sg_mfvec3f_del(p->outMFVec3f); + gf_node_free((GF_Node *) p); +} + +static const u16 Valuator_Def2All[] = { 32, 33, 34, 35, 36, 37, 38, 39, 40}; +static const u16 Valuator_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 32, 33, 34, 35, 36, 37, 38, 39, 40}; +static const u16 Valuator_Out2All[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40}; + +static u32 Valuator_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 25; + case GF_SG_FIELD_CODING_DEF: return 9; + case GF_SG_FIELD_CODING_OUT: return 25; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 41; + } +} + +static GF_Err Valuator_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Valuator_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Valuator_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Valuator_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Valuator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "inSFBool"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inSFBool; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Valuator *) node)->inSFBool; + return GF_OK; + case 1: + info->name = "inSFColor"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inSFColor; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_Valuator *) node)->inSFColor; + return GF_OK; + case 2: + info->name = "inMFColor"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inMFColor; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((M_Valuator *) node)->inMFColor; + return GF_OK; + case 3: + info->name = "inSFFloat"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inSFFloat; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->inSFFloat; + return GF_OK; + case 4: + info->name = "inMFFloat"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inMFFloat; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->inMFFloat; + return GF_OK; + case 5: + info->name = "inSFInt32"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inSFInt32; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_Valuator *) node)->inSFInt32; + return GF_OK; + case 6: + info->name = "inMFInt32"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inMFInt32; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_Valuator *) node)->inMFInt32; + return GF_OK; + case 7: + info->name = "inSFRotation"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inSFRotation; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_Valuator *) node)->inSFRotation; + return GF_OK; + case 8: + info->name = "inMFRotation"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inMFRotation; + info->fieldType = GF_SG_VRML_MFROTATION; + info->far_ptr = & ((M_Valuator *) node)->inMFRotation; + return GF_OK; + case 9: + info->name = "inSFString"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inSFString; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_Valuator *) node)->inSFString; + return GF_OK; + case 10: + info->name = "inMFString"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inMFString; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_Valuator *) node)->inMFString; + return GF_OK; + case 11: + info->name = "inSFTime"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inSFTime; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_Valuator *) node)->inSFTime; + return GF_OK; + case 12: + info->name = "inSFVec2f"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inSFVec2f; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Valuator *) node)->inSFVec2f; + return GF_OK; + case 13: + info->name = "inMFVec2f"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inMFVec2f; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_Valuator *) node)->inMFVec2f; + return GF_OK; + case 14: + info->name = "inSFVec3f"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inSFVec3f; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_Valuator *) node)->inSFVec3f; + return GF_OK; + case 15: + info->name = "inMFVec3f"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Valuator *)node)->on_inMFVec3f; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_Valuator *) node)->inMFVec3f; + return GF_OK; + case 16: + info->name = "outSFBool"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Valuator *) node)->outSFBool; + return GF_OK; + case 17: + info->name = "outSFColor"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_Valuator *) node)->outSFColor; + return GF_OK; + case 18: + info->name = "outMFColor"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((M_Valuator *) node)->outMFColor; + return GF_OK; + case 19: + info->name = "outSFFloat"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->outSFFloat; + return GF_OK; + case 20: + info->name = "outMFFloat"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->outMFFloat; + return GF_OK; + case 21: + info->name = "outSFInt32"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_Valuator *) node)->outSFInt32; + return GF_OK; + case 22: + info->name = "outMFInt32"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_Valuator *) node)->outMFInt32; + return GF_OK; + case 23: + info->name = "outSFRotation"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_Valuator *) node)->outSFRotation; + return GF_OK; + case 24: + info->name = "outMFRotation"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFROTATION; + info->far_ptr = & ((M_Valuator *) node)->outMFRotation; + return GF_OK; + case 25: + info->name = "outSFString"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_Valuator *) node)->outSFString; + return GF_OK; + case 26: + info->name = "outMFString"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_Valuator *) node)->outMFString; + return GF_OK; + case 27: + info->name = "outSFTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_Valuator *) node)->outSFTime; + return GF_OK; + case 28: + info->name = "outSFVec2f"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Valuator *) node)->outSFVec2f; + return GF_OK; + case 29: + info->name = "outMFVec2f"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_Valuator *) node)->outMFVec2f; + return GF_OK; + case 30: + info->name = "outSFVec3f"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_Valuator *) node)->outSFVec3f; + return GF_OK; + case 31: + info->name = "outMFVec3f"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_Valuator *) node)->outMFVec3f; + return GF_OK; + case 32: + info->name = "Factor1"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->Factor1; + return GF_OK; + case 33: + info->name = "Factor2"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->Factor2; + return GF_OK; + case 34: + info->name = "Factor3"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->Factor3; + return GF_OK; + case 35: + info->name = "Factor4"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->Factor4; + return GF_OK; + case 36: + info->name = "Offset1"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->Offset1; + return GF_OK; + case 37: + info->name = "Offset2"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->Offset2; + return GF_OK; + case 38: + info->name = "Offset3"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->Offset3; + return GF_OK; + case 39: + info->name = "Offset4"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Valuator *) node)->Offset4; + return GF_OK; + case 40: + info->name = "Sum"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Valuator *) node)->Sum; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Valuator_get_field_index_by_name(char *name) +{ + if (!strcmp("inSFBool", name)) return 0; + if (!strcmp("inSFColor", name)) return 1; + if (!strcmp("inMFColor", name)) return 2; + if (!strcmp("inSFFloat", name)) return 3; + if (!strcmp("inMFFloat", name)) return 4; + if (!strcmp("inSFInt32", name)) return 5; + if (!strcmp("inMFInt32", name)) return 6; + if (!strcmp("inSFRotation", name)) return 7; + if (!strcmp("inMFRotation", name)) return 8; + if (!strcmp("inSFString", name)) return 9; + if (!strcmp("inMFString", name)) return 10; + if (!strcmp("inSFTime", name)) return 11; + if (!strcmp("inSFVec2f", name)) return 12; + if (!strcmp("inMFVec2f", name)) return 13; + if (!strcmp("inSFVec3f", name)) return 14; + if (!strcmp("inMFVec3f", name)) return 15; + if (!strcmp("outSFBool", name)) return 16; + if (!strcmp("outSFColor", name)) return 17; + if (!strcmp("outMFColor", name)) return 18; + if (!strcmp("outSFFloat", name)) return 19; + if (!strcmp("outMFFloat", name)) return 20; + if (!strcmp("outSFInt32", name)) return 21; + if (!strcmp("outMFInt32", name)) return 22; + if (!strcmp("outSFRotation", name)) return 23; + if (!strcmp("outMFRotation", name)) return 24; + if (!strcmp("outSFString", name)) return 25; + if (!strcmp("outMFString", name)) return 26; + if (!strcmp("outSFTime", name)) return 27; + if (!strcmp("outSFVec2f", name)) return 28; + if (!strcmp("outMFVec2f", name)) return 29; + if (!strcmp("outSFVec3f", name)) return 30; + if (!strcmp("outMFVec3f", name)) return 31; + if (!strcmp("Factor1", name)) return 32; + if (!strcmp("Factor2", name)) return 33; + if (!strcmp("Factor3", name)) return 34; + if (!strcmp("Factor4", name)) return 35; + if (!strcmp("Offset1", name)) return 36; + if (!strcmp("Offset2", name)) return 37; + if (!strcmp("Offset3", name)) return 38; + if (!strcmp("Offset4", name)) return 39; + if (!strcmp("Sum", name)) return 40; + return -1; + } +static Bool Valuator_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 32: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 33: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 34: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 35: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 36: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 37: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 38: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 39: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Valuator_Create() +{ + M_Valuator *p; + GF_SAFEALLOC(p, M_Valuator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Valuator); + + /*default field values*/ + p->Factor1 = FLT2FIX(1.0); + p->Factor2 = FLT2FIX(1.0); + p->Factor3 = FLT2FIX(1.0); + p->Factor4 = FLT2FIX(1.0); + p->Offset1 = FLT2FIX(0.0); + p->Offset2 = FLT2FIX(0.0); + p->Offset3 = FLT2FIX(0.0); + p->Offset4 = FLT2FIX(0.0); + return (GF_Node *)p; +} + + +/* + Viewpoint Node deletion +*/ + +static void Viewpoint_Del(GF_Node *node) +{ + M_Viewpoint *p = (M_Viewpoint *) node; + gf_sg_sfstring_del(p->description); + gf_node_free((GF_Node *) p); +} + +static const u16 Viewpoint_Def2All[] = { 1, 2, 3, 4, 5}; +static const u16 Viewpoint_In2All[] = { 0, 1, 2, 3, 4}; +static const u16 Viewpoint_Out2All[] = { 1, 2, 3, 4, 6, 7}; +static const u16 Viewpoint_Dyn2All[] = { 1, 3, 4}; + +static u32 Viewpoint_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 3; + default: + return 8; + } +} + +static GF_Err Viewpoint_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Viewpoint_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Viewpoint_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Viewpoint_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Viewpoint_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Viewpoint_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Viewpoint *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Viewpoint *) node)->set_bind; + return GF_OK; + case 1: + info->name = "fieldOfView"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Viewpoint *) node)->fieldOfView; + return GF_OK; + case 2: + info->name = "jump"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Viewpoint *) node)->jump; + return GF_OK; + case 3: + info->name = "orientation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_Viewpoint *) node)->orientation; + return GF_OK; + case 4: + info->name = "position"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_Viewpoint *) node)->position; + return GF_OK; + case 5: + info->name = "description"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_Viewpoint *) node)->description; + return GF_OK; + case 6: + info->name = "bindTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_Viewpoint *) node)->bindTime; + return GF_OK; + case 7: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Viewpoint *) node)->isBound; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Viewpoint_get_field_index_by_name(char *name) +{ + if (!strcmp("set_bind", name)) return 0; + if (!strcmp("fieldOfView", name)) return 1; + if (!strcmp("jump", name)) return 2; + if (!strcmp("orientation", name)) return 3; + if (!strcmp("position", name)) return 4; + if (!strcmp("description", name)) return 5; + if (!strcmp("bindTime", name)) return 6; + if (!strcmp("isBound", name)) return 7; + return -1; + } +static Bool Viewpoint_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 8; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(3.1415927); + return 1; + case 3: + *AType = 10; + *QType = 10; + return 1; + case 4: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Viewpoint_Create() +{ + M_Viewpoint *p; + GF_SAFEALLOC(p, M_Viewpoint); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Viewpoint); + + /*default field values*/ + p->fieldOfView = FLT2FIX(0.785398); + p->jump = 1; + p->orientation.x = FLT2FIX(0); + p->orientation.y = FLT2FIX(0); + p->orientation.z = FLT2FIX(1); + p->orientation.q = FLT2FIX(0); + p->position.x = FLT2FIX(0); + p->position.y = FLT2FIX(0); + p->position.z = FLT2FIX(10); + return (GF_Node *)p; +} + + +/* + VisibilitySensor Node deletion +*/ + +static void VisibilitySensor_Del(GF_Node *node) +{ + M_VisibilitySensor *p = (M_VisibilitySensor *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 VisibilitySensor_Def2All[] = { 0, 1, 2}; +static const u16 VisibilitySensor_In2All[] = { 0, 1, 2}; +static const u16 VisibilitySensor_Out2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 VisibilitySensor_Dyn2All[] = { 0, 2}; + +static u32 VisibilitySensor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 6; + } +} + +static GF_Err VisibilitySensor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = VisibilitySensor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = VisibilitySensor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = VisibilitySensor_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = VisibilitySensor_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err VisibilitySensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_VisibilitySensor *) node)->center; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_VisibilitySensor *) node)->enabled; + return GF_OK; + case 2: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_VisibilitySensor *) node)->size; + return GF_OK; + case 3: + info->name = "enterTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_VisibilitySensor *) node)->enterTime; + return GF_OK; + case 4: + info->name = "exitTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_VisibilitySensor *) node)->exitTime; + return GF_OK; + case 5: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_VisibilitySensor *) node)->isActive; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 VisibilitySensor_get_field_index_by_name(char *name) +{ + if (!strcmp("center", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("size", name)) return 2; + if (!strcmp("enterTime", name)) return 3; + if (!strcmp("exitTime", name)) return 4; + if (!strcmp("isActive", name)) return 5; + return -1; + } +static Bool VisibilitySensor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 11; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *VisibilitySensor_Create() +{ + M_VisibilitySensor *p; + GF_SAFEALLOC(p, M_VisibilitySensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_VisibilitySensor); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->center.z = FLT2FIX(0); + p->enabled = 1; + p->size.x = FLT2FIX(0); + p->size.y = FLT2FIX(0); + p->size.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + WorldInfo Node deletion +*/ + +static void WorldInfo_Del(GF_Node *node) +{ + M_WorldInfo *p = (M_WorldInfo *) node; + gf_sg_mfstring_del(p->info); + gf_sg_sfstring_del(p->title); + gf_node_free((GF_Node *) p); +} + +static const u16 WorldInfo_Def2All[] = { 0, 1}; + +static u32 WorldInfo_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 0; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 0; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 2; + } +} + +static GF_Err WorldInfo_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_DEF: + *allField = WorldInfo_Def2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err WorldInfo_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "info"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_WorldInfo *) node)->info; + return GF_OK; + case 1: + info->name = "title"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_WorldInfo *) node)->title; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 WorldInfo_get_field_index_by_name(char *name) +{ + if (!strcmp("info", name)) return 0; + if (!strcmp("title", name)) return 1; + return -1; + } +static Bool WorldInfo_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *WorldInfo_Create() +{ + M_WorldInfo *p; + GF_SAFEALLOC(p, M_WorldInfo); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_WorldInfo); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + AcousticMaterial Node deletion +*/ + +static void AcousticMaterial_Del(GF_Node *node) +{ + M_AcousticMaterial *p = (M_AcousticMaterial *) node; + gf_sg_mffloat_del(p->reffunc); + gf_sg_mffloat_del(p->transfunc); + gf_sg_mffloat_del(p->refFrequency); + gf_sg_mffloat_del(p->transFrequency); + gf_node_free((GF_Node *) p); +} + +static const u16 AcousticMaterial_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 AcousticMaterial_In2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 AcousticMaterial_Out2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 AcousticMaterial_Dyn2All[] = { 0, 1, 2, 3, 4, 5}; + +static u32 AcousticMaterial_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 6; + case GF_SG_FIELD_CODING_DEF: return 10; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 6; + default: + return 10; + } +} + +static GF_Err AcousticMaterial_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = AcousticMaterial_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = AcousticMaterial_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = AcousticMaterial_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = AcousticMaterial_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err AcousticMaterial_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "ambientIntensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_AcousticMaterial *) node)->ambientIntensity; + return GF_OK; + case 1: + info->name = "diffuseColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_AcousticMaterial *) node)->diffuseColor; + return GF_OK; + case 2: + info->name = "emissiveColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_AcousticMaterial *) node)->emissiveColor; + return GF_OK; + case 3: + info->name = "shininess"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_AcousticMaterial *) node)->shininess; + return GF_OK; + case 4: + info->name = "specularColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_AcousticMaterial *) node)->specularColor; + return GF_OK; + case 5: + info->name = "transparency"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_AcousticMaterial *) node)->transparency; + return GF_OK; + case 6: + info->name = "reffunc"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_AcousticMaterial *) node)->reffunc; + return GF_OK; + case 7: + info->name = "transfunc"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_AcousticMaterial *) node)->transfunc; + return GF_OK; + case 8: + info->name = "refFrequency"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_AcousticMaterial *) node)->refFrequency; + return GF_OK; + case 9: + info->name = "transFrequency"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_AcousticMaterial *) node)->transFrequency; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AcousticMaterial_get_field_index_by_name(char *name) +{ + if (!strcmp("ambientIntensity", name)) return 0; + if (!strcmp("diffuseColor", name)) return 1; + if (!strcmp("emissiveColor", name)) return 2; + if (!strcmp("shininess", name)) return 3; + if (!strcmp("specularColor", name)) return 4; + if (!strcmp("transparency", name)) return 5; + if (!strcmp("reffunc", name)) return 6; + if (!strcmp("transfunc", name)) return 7; + if (!strcmp("refFrequency", name)) return 8; + if (!strcmp("transFrequency", name)) return 9; + return -1; + } +static Bool AcousticMaterial_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 1: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 3: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 4: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 5: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 6: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 7: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 8: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 9: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *AcousticMaterial_Create() +{ + M_AcousticMaterial *p; + GF_SAFEALLOC(p, M_AcousticMaterial); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_AcousticMaterial); + + /*default field values*/ + p->ambientIntensity = FLT2FIX(0.2); + p->diffuseColor.red = FLT2FIX(0.8); + p->diffuseColor.green = FLT2FIX(0.8); + p->diffuseColor.blue = FLT2FIX(0.8); + p->emissiveColor.red = FLT2FIX(0); + p->emissiveColor.green = FLT2FIX(0); + p->emissiveColor.blue = FLT2FIX(0); + p->shininess = FLT2FIX(0.2); + p->specularColor.red = FLT2FIX(0); + p->specularColor.green = FLT2FIX(0); + p->specularColor.blue = FLT2FIX(0); + p->transparency = FLT2FIX(0); + p->reffunc.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->reffunc.count = 1; + p->reffunc.vals[0] = FLT2FIX(0); + p->transfunc.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->transfunc.count = 1; + p->transfunc.vals[0] = FLT2FIX(1); + p->refFrequency.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->refFrequency.count = 1; + p->refFrequency.vals[0] = FLT2FIX(0); + p->transFrequency.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->transFrequency.count = 1; + p->transFrequency.vals[0] = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + AcousticScene Node deletion +*/ + +static void AcousticScene_Del(GF_Node *node) +{ + M_AcousticScene *p = (M_AcousticScene *) node; + gf_sg_mftime_del(p->reverbTime); + gf_sg_mffloat_del(p->reverbFreq); + gf_node_free((GF_Node *) p); +} + +static const u16 AcousticScene_Def2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 AcousticScene_In2All[] = { 4, 5}; +static const u16 AcousticScene_Out2All[] = { 4, 5}; +static const u16 AcousticScene_Dyn2All[] = { 4}; + +static u32 AcousticScene_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 2; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 6; + } +} + +static GF_Err AcousticScene_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = AcousticScene_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = AcousticScene_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = AcousticScene_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = AcousticScene_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err AcousticScene_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "center"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_AcousticScene *) node)->center; + return GF_OK; + case 1: + info->name = "Size"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_AcousticScene *) node)->Size; + return GF_OK; + case 2: + info->name = "reverbTime"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFTIME; + info->far_ptr = & ((M_AcousticScene *) node)->reverbTime; + return GF_OK; + case 3: + info->name = "reverbFreq"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_AcousticScene *) node)->reverbFreq; + return GF_OK; + case 4: + info->name = "reverbLevel"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_AcousticScene *) node)->reverbLevel; + return GF_OK; + case 5: + info->name = "reverbDelay"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_AcousticScene *) node)->reverbDelay; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AcousticScene_get_field_index_by_name(char *name) +{ + if (!strcmp("center", name)) return 0; + if (!strcmp("Size", name)) return 1; + if (!strcmp("reverbTime", name)) return 2; + if (!strcmp("reverbFreq", name)) return 3; + if (!strcmp("reverbLevel", name)) return 4; + if (!strcmp("reverbDelay", name)) return 5; + return -1; + } +static Bool AcousticScene_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 0; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 0; + *QType = 11; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0 ); + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0 ); + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 7; + *QType = 0; + *b_min = FLT2FIX(0 ); + *b_max = FIX_MAX; + return 1; + case 5: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0 ); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *AcousticScene_Create() +{ + M_AcousticScene *p; + GF_SAFEALLOC(p, M_AcousticScene); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_AcousticScene); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->center.z = FLT2FIX(0); + p->Size.x = FLT2FIX(-1); + p->Size.y = FLT2FIX(-1); + p->Size.z = FLT2FIX(-1); + p->reverbTime.vals = (SFTime*)malloc(sizeof(SFTime)*1); + p->reverbTime.count = 1; + p->reverbTime.vals[0] = 0; + p->reverbFreq.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->reverbFreq.count = 1; + p->reverbFreq.vals[0] = FLT2FIX(1000); + p->reverbLevel = FLT2FIX(0.4); + p->reverbDelay = 0.5; + return (GF_Node *)p; +} + + +/* + ApplicationWindow Node deletion +*/ + +static void ApplicationWindow_Del(GF_Node *node) +{ + M_ApplicationWindow *p = (M_ApplicationWindow *) node; + gf_sg_sfstring_del(p->description); + gf_sg_mfstring_del(p->parameter); + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 ApplicationWindow_Def2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 ApplicationWindow_In2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 ApplicationWindow_Out2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 ApplicationWindow_Dyn2All[] = { 6}; + +static u32 ApplicationWindow_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 7; + case GF_SG_FIELD_CODING_DEF: return 7; + case GF_SG_FIELD_CODING_OUT: return 7; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 7; + } +} + +static GF_Err ApplicationWindow_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ApplicationWindow_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ApplicationWindow_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ApplicationWindow_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = ApplicationWindow_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ApplicationWindow_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ApplicationWindow *) node)->isActive; + return GF_OK; + case 1: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_ApplicationWindow *) node)->startTime; + return GF_OK; + case 2: + info->name = "stopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_ApplicationWindow *) node)->stopTime; + return GF_OK; + case 3: + info->name = "description"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_ApplicationWindow *) node)->description; + return GF_OK; + case 4: + info->name = "parameter"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_ApplicationWindow *) node)->parameter; + return GF_OK; + case 5: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_ApplicationWindow *) node)->url; + return GF_OK; + case 6: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_ApplicationWindow *) node)->size; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ApplicationWindow_get_field_index_by_name(char *name) +{ + if (!strcmp("isActive", name)) return 0; + if (!strcmp("startTime", name)) return 1; + if (!strcmp("stopTime", name)) return 2; + if (!strcmp("description", name)) return 3; + if (!strcmp("parameter", name)) return 4; + if (!strcmp("url", name)) return 5; + if (!strcmp("size", name)) return 6; + return -1; + } +static Bool ApplicationWindow_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 12; + *QType = 12; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *ApplicationWindow_Create() +{ + M_ApplicationWindow *p; + GF_SAFEALLOC(p, M_ApplicationWindow); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ApplicationWindow); + + /*default field values*/ + p->startTime = 0; + p->stopTime = 0; + p->size.x = FLT2FIX(0); + p->size.y = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + DirectiveSound Node deletion +*/ + +static void DirectiveSound_Del(GF_Node *node) +{ + M_DirectiveSound *p = (M_DirectiveSound *) node; + gf_node_unregister((GF_Node *) p->source, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->perceptualParameters, (GF_Node *) p); + gf_sg_mffloat_del(p->directivity); + gf_sg_mffloat_del(p->angles); + gf_sg_mffloat_del(p->frequency); + gf_node_free((GF_Node *) p); +} + +static const u16 DirectiveSound_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; +static const u16 DirectiveSound_In2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 DirectiveSound_Out2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 DirectiveSound_Dyn2All[] = { 0, 1, 2}; + +static u32 DirectiveSound_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 7; + case GF_SG_FIELD_CODING_DEF: return 13; + case GF_SG_FIELD_CODING_OUT: return 7; + case GF_SG_FIELD_CODING_DYN: return 3; + default: + return 13; + } +} + +static GF_Err DirectiveSound_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = DirectiveSound_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = DirectiveSound_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = DirectiveSound_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = DirectiveSound_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err DirectiveSound_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "direction"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_DirectiveSound *) node)->direction; + return GF_OK; + case 1: + info->name = "intensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_DirectiveSound *) node)->intensity; + return GF_OK; + case 2: + info->name = "location"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_DirectiveSound *) node)->location; + return GF_OK; + case 3: + info->name = "source"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((M_DirectiveSound *)node)->source; + return GF_OK; + case 4: + info->name = "perceptualParameters"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFPerceptualParameterNode; + info->far_ptr = & ((M_DirectiveSound *)node)->perceptualParameters; + return GF_OK; + case 5: + info->name = "roomEffect"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_DirectiveSound *) node)->roomEffect; + return GF_OK; + case 6: + info->name = "spatialize"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_DirectiveSound *) node)->spatialize; + return GF_OK; + case 7: + info->name = "directivity"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_DirectiveSound *) node)->directivity; + return GF_OK; + case 8: + info->name = "angles"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_DirectiveSound *) node)->angles; + return GF_OK; + case 9: + info->name = "frequency"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_DirectiveSound *) node)->frequency; + return GF_OK; + case 10: + info->name = "speedOfSound"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_DirectiveSound *) node)->speedOfSound; + return GF_OK; + case 11: + info->name = "distance"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_DirectiveSound *) node)->distance; + return GF_OK; + case 12: + info->name = "useAirabs"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_DirectiveSound *) node)->useAirabs; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 DirectiveSound_get_field_index_by_name(char *name) +{ + if (!strcmp("direction", name)) return 0; + if (!strcmp("intensity", name)) return 1; + if (!strcmp("location", name)) return 2; + if (!strcmp("source", name)) return 3; + if (!strcmp("perceptualParameters", name)) return 4; + if (!strcmp("roomEffect", name)) return 5; + if (!strcmp("spatialize", name)) return 6; + if (!strcmp("directivity", name)) return 7; + if (!strcmp("angles", name)) return 8; + if (!strcmp("frequency", name)) return 9; + if (!strcmp("speedOfSound", name)) return 10; + if (!strcmp("distance", name)) return 11; + if (!strcmp("useAirabs", name)) return 12; + return -1; + } +static Bool DirectiveSound_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 9; + *QType = 9; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 7; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 7: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 8: + *AType = 0; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(3.14159265); + return 1; + case 9: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 10: + *AType = 0; + *QType = 1; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 11: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *DirectiveSound_Create() +{ + M_DirectiveSound *p; + GF_SAFEALLOC(p, M_DirectiveSound); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_DirectiveSound); + + /*default field values*/ + p->direction.x = FLT2FIX(0); + p->direction.y = FLT2FIX(0); + p->direction.z = FLT2FIX(-1); + p->intensity = FLT2FIX(1); + p->location.x = FLT2FIX(0); + p->location.y = FLT2FIX(0); + p->location.z = FLT2FIX(0); + p->spatialize = 1; + p->directivity.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->directivity.count = 1; + p->directivity.vals[0] = FLT2FIX(1); + p->angles.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->angles.count = 1; + p->angles.vals[0] = FLT2FIX(1); + p->speedOfSound = FLT2FIX(340); + p->distance = FLT2FIX(100); + return (GF_Node *)p; +} + + +/* + Hierarchical3DMesh Node deletion +*/ + +static void Hierarchical3DMesh_Del(GF_Node *node) +{ + M_Hierarchical3DMesh *p = (M_Hierarchical3DMesh *) node; + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 Hierarchical3DMesh_Def2All[] = { 1, 2}; +static const u16 Hierarchical3DMesh_In2All[] = { 0, 1}; +static const u16 Hierarchical3DMesh_Out2All[] = { 1, 3}; + +static u32 Hierarchical3DMesh_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 2; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 2; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err Hierarchical3DMesh_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Hierarchical3DMesh_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Hierarchical3DMesh_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Hierarchical3DMesh_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Hierarchical3DMesh_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "triangleBudget"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Hierarchical3DMesh *)node)->on_triangleBudget; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_Hierarchical3DMesh *) node)->triangleBudget; + return GF_OK; + case 1: + info->name = "level"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Hierarchical3DMesh *) node)->level; + return GF_OK; + case 2: + info->name = "url"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_Hierarchical3DMesh *) node)->url; + return GF_OK; + case 3: + info->name = "doneLoading"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Hierarchical3DMesh *) node)->doneLoading; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Hierarchical3DMesh_get_field_index_by_name(char *name) +{ + if (!strcmp("triangleBudget", name)) return 0; + if (!strcmp("level", name)) return 1; + if (!strcmp("url", name)) return 2; + if (!strcmp("doneLoading", name)) return 3; + return -1; + } +static Bool Hierarchical3DMesh_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 0; + *QType = 0; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Hierarchical3DMesh_Create() +{ + M_Hierarchical3DMesh *p; + GF_SAFEALLOC(p, M_Hierarchical3DMesh); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Hierarchical3DMesh); + + /*default field values*/ + p->level = FLT2FIX(1); + return (GF_Node *)p; +} + + +/* + MaterialKey Node deletion +*/ + +static void MaterialKey_Del(GF_Node *node) +{ + M_MaterialKey *p = (M_MaterialKey *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 MaterialKey_Def2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 MaterialKey_In2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 MaterialKey_Out2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 MaterialKey_Dyn2All[] = { 2, 3, 4, 5}; + +static u32 MaterialKey_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 6; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 4; + default: + return 6; + } +} + +static GF_Err MaterialKey_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = MaterialKey_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = MaterialKey_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = MaterialKey_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = MaterialKey_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err MaterialKey_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "isKeyed"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MaterialKey *) node)->isKeyed; + return GF_OK; + case 1: + info->name = "isRGB"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MaterialKey *) node)->isRGB; + return GF_OK; + case 2: + info->name = "keyColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_MaterialKey *) node)->keyColor; + return GF_OK; + case 3: + info->name = "lowThreshold"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_MaterialKey *) node)->lowThreshold; + return GF_OK; + case 4: + info->name = "highThreshold"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_MaterialKey *) node)->highThreshold; + return GF_OK; + case 5: + info->name = "transparency"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_MaterialKey *) node)->transparency; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MaterialKey_get_field_index_by_name(char *name) +{ + if (!strcmp("isKeyed", name)) return 0; + if (!strcmp("isRGB", name)) return 1; + if (!strcmp("keyColor", name)) return 2; + if (!strcmp("lowThreshold", name)) return 3; + if (!strcmp("highThreshold", name)) return 4; + if (!strcmp("transparency", name)) return 5; + return -1; + } +static Bool MaterialKey_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 2: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 3: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 4: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 5: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *MaterialKey_Create() +{ + M_MaterialKey *p; + GF_SAFEALLOC(p, M_MaterialKey); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_MaterialKey); + + /*default field values*/ + p->isKeyed = 1; + p->isRGB = 1; + p->keyColor.red = FLT2FIX(0); + p->keyColor.green = FLT2FIX(0); + p->keyColor.blue = FLT2FIX(0); + p->lowThreshold = FLT2FIX(0); + p->highThreshold = FLT2FIX(0); + p->transparency = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + PerceptualParameters Node deletion +*/ + +static void PerceptualParameters_Del(GF_Node *node) +{ + M_PerceptualParameters *p = (M_PerceptualParameters *) node; + gf_sg_mffloat_del(p->omniDirectivity); + gf_sg_mffloat_del(p->directFilterGains); + gf_sg_mffloat_del(p->inputFilterGains); + gf_node_free((GF_Node *) p); +} + +static const u16 PerceptualParameters_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; +static const u16 PerceptualParameters_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; +static const u16 PerceptualParameters_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; +static const u16 PerceptualParameters_Dyn2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + +static u32 PerceptualParameters_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 19; + case GF_SG_FIELD_CODING_DEF: return 19; + case GF_SG_FIELD_CODING_OUT: return 19; + case GF_SG_FIELD_CODING_DYN: return 15; + default: + return 19; + } +} + +static GF_Err PerceptualParameters_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PerceptualParameters_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PerceptualParameters_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PerceptualParameters_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = PerceptualParameters_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PerceptualParameters_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "sourcePresence"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->sourcePresence; + return GF_OK; + case 1: + info->name = "sourceWarmth"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->sourceWarmth; + return GF_OK; + case 2: + info->name = "sourceBrilliance"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->sourceBrilliance; + return GF_OK; + case 3: + info->name = "roomPresence"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->roomPresence; + return GF_OK; + case 4: + info->name = "runningReverberance"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->runningReverberance; + return GF_OK; + case 5: + info->name = "envelopment"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->envelopment; + return GF_OK; + case 6: + info->name = "lateReverberance"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->lateReverberance; + return GF_OK; + case 7: + info->name = "heavyness"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->heavyness; + return GF_OK; + case 8: + info->name = "liveness"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->liveness; + return GF_OK; + case 9: + info->name = "omniDirectivity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->omniDirectivity; + return GF_OK; + case 10: + info->name = "directFilterGains"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->directFilterGains; + return GF_OK; + case 11: + info->name = "inputFilterGains"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->inputFilterGains; + return GF_OK; + case 12: + info->name = "refDistance"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->refDistance; + return GF_OK; + case 13: + info->name = "freqLow"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->freqLow; + return GF_OK; + case 14: + info->name = "freqHigh"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PerceptualParameters *) node)->freqHigh; + return GF_OK; + case 15: + info->name = "timeLimit1"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_PerceptualParameters *) node)->timeLimit1; + return GF_OK; + case 16: + info->name = "timeLimit2"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_PerceptualParameters *) node)->timeLimit2; + return GF_OK; + case 17: + info->name = "timeLimit3"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_PerceptualParameters *) node)->timeLimit3; + return GF_OK; + case 18: + info->name = "modalDensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_PerceptualParameters *) node)->modalDensity; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PerceptualParameters_get_field_index_by_name(char *name) +{ + if (!strcmp("sourcePresence", name)) return 0; + if (!strcmp("sourceWarmth", name)) return 1; + if (!strcmp("sourceBrilliance", name)) return 2; + if (!strcmp("roomPresence", name)) return 3; + if (!strcmp("runningReverberance", name)) return 4; + if (!strcmp("envelopment", name)) return 5; + if (!strcmp("lateReverberance", name)) return 6; + if (!strcmp("heavyness", name)) return 7; + if (!strcmp("liveness", name)) return 8; + if (!strcmp("omniDirectivity", name)) return 9; + if (!strcmp("directFilterGains", name)) return 10; + if (!strcmp("inputFilterGains", name)) return 11; + if (!strcmp("refDistance", name)) return 12; + if (!strcmp("freqLow", name)) return 13; + if (!strcmp("freqHigh", name)) return 14; + if (!strcmp("timeLimit1", name)) return 15; + if (!strcmp("timeLimit2", name)) return 16; + if (!strcmp("timeLimit3", name)) return 17; + if (!strcmp("modalDensity", name)) return 18; + return -1; + } +static Bool PerceptualParameters_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 5: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 7: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 8: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 9: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 10: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 11: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 12: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 13: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 14: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 15: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 16: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 17: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 18: + *AType = 0; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *PerceptualParameters_Create() +{ + M_PerceptualParameters *p; + GF_SAFEALLOC(p, M_PerceptualParameters); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PerceptualParameters); + + /*default field values*/ + p->sourcePresence = FLT2FIX(1.0); + p->sourceWarmth = FLT2FIX(1.0); + p->sourceBrilliance = FLT2FIX(1.0); + p->roomPresence = FLT2FIX(1.0); + p->runningReverberance = FLT2FIX(1.0); + p->envelopment = FLT2FIX(0.0); + p->lateReverberance = FLT2FIX(1.0); + p->heavyness = FLT2FIX(1.0); + p->liveness = FLT2FIX(1.0); + p->omniDirectivity.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->omniDirectivity.count = 1; + p->omniDirectivity.vals[0] = FLT2FIX(1.0); + p->directFilterGains.vals = (SFFloat*)malloc(sizeof(SFFloat)*3); + p->directFilterGains.count = 3; + p->directFilterGains.vals[0] = FLT2FIX(1.0); + p->directFilterGains.vals[1] = FLT2FIX(1.0); + p->directFilterGains.vals[2] = FLT2FIX(1.0); + p->inputFilterGains.vals = (SFFloat*)malloc(sizeof(SFFloat)*3); + p->inputFilterGains.count = 3; + p->inputFilterGains.vals[0] = FLT2FIX(1.0); + p->inputFilterGains.vals[1] = FLT2FIX(1.0); + p->inputFilterGains.vals[2] = FLT2FIX(1.0); + p->refDistance = FLT2FIX(1.0); + p->freqLow = FLT2FIX(250.0); + p->freqHigh = FLT2FIX(4000.0); + p->timeLimit1 = 0.02; + p->timeLimit2 = 0.04; + p->timeLimit3 = 0.1; + p->modalDensity = 0.8; + return (GF_Node *)p; +} + + +/* + TemporalTransform Node deletion +*/ + +static void TemporalTransform_Del(GF_Node *node) +{ + M_TemporalTransform *p = (M_TemporalTransform *) node; + gf_sg_mfurl_del(p->url); + gf_sg_mfint32_del(p->stretchMode); + gf_sg_mfint32_del(p->shrinkMode); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 TemporalTransform_Def2All[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; +static const u16 TemporalTransform_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; +static const u16 TemporalTransform_Out2All[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; +static const u16 TemporalTransform_Dyn2All[] = { 7, 8}; + +static u32 TemporalTransform_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 12; + case GF_SG_FIELD_CODING_DEF: return 10; + case GF_SG_FIELD_CODING_OUT: return 11; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 13; + } +} + +static GF_Err TemporalTransform_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = TemporalTransform_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = TemporalTransform_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = TemporalTransform_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = TemporalTransform_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err TemporalTransform_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_TemporalTransform *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_TemporalTransform *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_TemporalTransform *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_TemporalTransform *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_TemporalTransform *)node)->children; + return GF_OK; + case 3: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_TemporalTransform *) node)->url; + return GF_OK; + case 4: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TemporalTransform *) node)->startTime; + return GF_OK; + case 5: + info->name = "optimalDuration"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TemporalTransform *) node)->optimalDuration; + return GF_OK; + case 6: + info->name = "active"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TemporalTransform *) node)->active; + return GF_OK; + case 7: + info->name = "speed"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_TemporalTransform *) node)->speed; + return GF_OK; + case 8: + info->name = "scalability"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_TemporalTransform *) node)->scalability; + return GF_OK; + case 9: + info->name = "stretchMode"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_TemporalTransform *) node)->stretchMode; + return GF_OK; + case 10: + info->name = "shrinkMode"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_TemporalTransform *) node)->shrinkMode; + return GF_OK; + case 11: + info->name = "maxDelay"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TemporalTransform *) node)->maxDelay; + return GF_OK; + case 12: + info->name = "actualDuration"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_TemporalTransform *) node)->actualDuration; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TemporalTransform_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("url", name)) return 3; + if (!strcmp("startTime", name)) return 4; + if (!strcmp("optimalDuration", name)) return 5; + if (!strcmp("active", name)) return 6; + if (!strcmp("speed", name)) return 7; + if (!strcmp("scalability", name)) return 8; + if (!strcmp("stretchMode", name)) return 9; + if (!strcmp("shrinkMode", name)) return 10; + if (!strcmp("maxDelay", name)) return 11; + if (!strcmp("actualDuration", name)) return 12; + return -1; + } +static Bool TemporalTransform_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 7: + *AType = 7; + *QType = 0; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 8: + *AType = 12; + *QType = 12; + *b_min = FLT2FIX(-1); + *b_max = FIX_MAX; + return 1; + case 9: + *AType = 0; + *QType = 13; + *QT13_bits = 2; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(2); + return 1; + case 10: + *AType = 0; + *QType = 13; + *QT13_bits = 1; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + default: + return 0; + } +} + + + +GF_Node *TemporalTransform_Create() +{ + M_TemporalTransform *p; + GF_SAFEALLOC(p, M_TemporalTransform); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_TemporalTransform); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->startTime = -1.0; + p->optimalDuration = -1.0; + p->speed = FLT2FIX(1.0); + p->scalability.x = FLT2FIX(1.0); + p->scalability.y = FLT2FIX(1.0); + p->stretchMode.vals = (SFInt32*)malloc(sizeof(SFInt32)*1); + p->stretchMode.count = 1; + p->stretchMode.vals[0] = 0; + p->shrinkMode.vals = (SFInt32*)malloc(sizeof(SFInt32)*1); + p->shrinkMode.count = 1; + p->shrinkMode.vals[0] = 0; + p->maxDelay = 0; + return (GF_Node *)p; +} + + +/* + TemporalGroup Node deletion +*/ + +static void TemporalGroup_Del(GF_Node *node) +{ + M_TemporalGroup *p = (M_TemporalGroup *) node; + gf_sg_mffloat_del(p->priority); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 TemporalGroup_Def2All[] = { 2, 3, 4, 5, 6}; +static const u16 TemporalGroup_In2All[] = { 0, 1, 2, 6}; +static const u16 TemporalGroup_Out2All[] = { 2, 6, 7, 8}; + +static u32 TemporalGroup_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 4; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 9; + } +} + +static GF_Err TemporalGroup_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = TemporalGroup_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = TemporalGroup_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = TemporalGroup_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err TemporalGroup_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_TemporalGroup *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFTemporalNode; + info->far_ptr = & ((M_TemporalGroup *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_TemporalGroup *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFTemporalNode; + info->far_ptr = & ((M_TemporalGroup *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFTemporalNode; + info->far_ptr = & ((M_TemporalGroup *)node)->children; + return GF_OK; + case 3: + info->name = "costart"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TemporalGroup *) node)->costart; + return GF_OK; + case 4: + info->name = "coend"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TemporalGroup *) node)->coend; + return GF_OK; + case 5: + info->name = "meet"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TemporalGroup *) node)->meet; + return GF_OK; + case 6: + info->name = "priority"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_TemporalGroup *) node)->priority; + return GF_OK; + case 7: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_TemporalGroup *) node)->isActive; + return GF_OK; + case 8: + info->name = "activeChild"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_TemporalGroup *) node)->activeChild; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TemporalGroup_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("costart", name)) return 3; + if (!strcmp("coend", name)) return 4; + if (!strcmp("meet", name)) return 5; + if (!strcmp("priority", name)) return 6; + if (!strcmp("isActive", name)) return 7; + if (!strcmp("activeChild", name)) return 8; + return -1; + } +static Bool TemporalGroup_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 6: + *AType = 0; + *QType = 3; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *TemporalGroup_Create() +{ + M_TemporalGroup *p; + GF_SAFEALLOC(p, M_TemporalGroup); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_TemporalGroup); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->costart = 1; + return (GF_Node *)p; +} + + +/* + ServerCommand Node deletion +*/ + +static void ServerCommand_Del(GF_Node *node) +{ + M_ServerCommand *p = (M_ServerCommand *) node; + gf_sg_mfurl_del(p->url); + gf_sg_sfstring_del(p->command); + gf_node_free((GF_Node *) p); +} + +static const u16 ServerCommand_Def2All[] = { 1, 2, 3}; +static const u16 ServerCommand_In2All[] = { 0, 1, 2, 3}; +static const u16 ServerCommand_Out2All[] = { 1, 2, 3}; + +static u32 ServerCommand_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 4; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err ServerCommand_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ServerCommand_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ServerCommand_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ServerCommand_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ServerCommand_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "trigger"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_ServerCommand *)node)->on_trigger; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ServerCommand *) node)->trigger; + return GF_OK; + case 1: + info->name = "enable"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_ServerCommand *) node)->enable; + return GF_OK; + case 2: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_ServerCommand *) node)->url; + return GF_OK; + case 3: + info->name = "command"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_ServerCommand *) node)->command; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ServerCommand_get_field_index_by_name(char *name) +{ + if (!strcmp("trigger", name)) return 0; + if (!strcmp("enable", name)) return 1; + if (!strcmp("url", name)) return 2; + if (!strcmp("command", name)) return 3; + return -1; + } +static Bool ServerCommand_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *ServerCommand_Create() +{ + M_ServerCommand *p; + GF_SAFEALLOC(p, M_ServerCommand); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ServerCommand); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + InputSensor Node deletion +*/ + +static void InputSensor_Del(GF_Node *node) +{ + M_InputSensor *p = (M_InputSensor *) node; + gf_sg_sfcommand_del(p->buffer); + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 InputSensor_Def2All[] = { 0, 1, 2}; +static const u16 InputSensor_In2All[] = { 0, 1, 2}; +static const u16 InputSensor_Out2All[] = { 0, 1, 2, 3}; + +static u32 InputSensor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 4; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err InputSensor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = InputSensor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = InputSensor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = InputSensor_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err InputSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_InputSensor *) node)->enabled; + return GF_OK; + case 1: + info->name = "buffer"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOMMANDBUFFER; + info->far_ptr = & ((M_InputSensor *) node)->buffer; + return GF_OK; + case 2: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_InputSensor *) node)->url; + return GF_OK; + case 3: + info->name = "eventTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_InputSensor *) node)->eventTime; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 InputSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("enabled", name)) return 0; + if (!strcmp("buffer", name)) return 1; + if (!strcmp("url", name)) return 2; + if (!strcmp("eventTime", name)) return 3; + return -1; + } +static Bool InputSensor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *InputSensor_Create() +{ + M_InputSensor *p; + GF_SAFEALLOC(p, M_InputSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_InputSensor); + p->buffer.commandList = gf_list_new(); + + /*default field values*/ + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + MatteTexture Node deletion +*/ + +static void MatteTexture_Del(GF_Node *node) +{ + M_MatteTexture *p = (M_MatteTexture *) node; + gf_node_unregister((GF_Node *) p->surfaceA, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->surfaceB, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->alphaSurface, (GF_Node *) p); + gf_sg_sfstring_del(p->operation); + gf_sg_mffloat_del(p->parameter); + gf_node_free((GF_Node *) p); +} + +static const u16 MatteTexture_Def2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 MatteTexture_In2All[] = { 3, 5, 6}; +static const u16 MatteTexture_Out2All[] = { 3, 5, 6}; + +static u32 MatteTexture_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 7; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 7; + } +} + +static GF_Err MatteTexture_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = MatteTexture_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = MatteTexture_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = MatteTexture_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err MatteTexture_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "surfaceA"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((M_MatteTexture *)node)->surfaceA; + return GF_OK; + case 1: + info->name = "surfaceB"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((M_MatteTexture *)node)->surfaceB; + return GF_OK; + case 2: + info->name = "alphaSurface"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((M_MatteTexture *)node)->alphaSurface; + return GF_OK; + case 3: + info->name = "operation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_MatteTexture *) node)->operation; + return GF_OK; + case 4: + info->name = "overwrite"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MatteTexture *) node)->overwrite; + return GF_OK; + case 5: + info->name = "fraction"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_MatteTexture *) node)->fraction; + return GF_OK; + case 6: + info->name = "parameter"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_MatteTexture *) node)->parameter; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MatteTexture_get_field_index_by_name(char *name) +{ + if (!strcmp("surfaceA", name)) return 0; + if (!strcmp("surfaceB", name)) return 1; + if (!strcmp("alphaSurface", name)) return 2; + if (!strcmp("operation", name)) return 3; + if (!strcmp("overwrite", name)) return 4; + if (!strcmp("fraction", name)) return 5; + if (!strcmp("parameter", name)) return 6; + return -1; + } +static Bool MatteTexture_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *MatteTexture_Create() +{ + M_MatteTexture *p; + GF_SAFEALLOC(p, M_MatteTexture); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_MatteTexture); + + /*default field values*/ + p->fraction = FLT2FIX(0); + p->parameter.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->parameter.count = 1; + p->parameter.vals[0] = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + MediaBuffer Node deletion +*/ + +static void MediaBuffer_Del(GF_Node *node) +{ + M_MediaBuffer *p = (M_MediaBuffer *) node; + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 MediaBuffer_Def2All[] = { 0, 1, 2, 3, 5}; +static const u16 MediaBuffer_In2All[] = { 0, 1, 2, 3, 5}; +static const u16 MediaBuffer_Out2All[] = { 0, 1, 2, 3, 4, 5}; + +static u32 MediaBuffer_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 6; + } +} + +static GF_Err MediaBuffer_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = MediaBuffer_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = MediaBuffer_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = MediaBuffer_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err MediaBuffer_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "bufferSize"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_MediaBuffer *) node)->bufferSize; + return GF_OK; + case 1: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_MediaBuffer *) node)->url; + return GF_OK; + case 2: + info->name = "mediaStartTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_MediaBuffer *) node)->mediaStartTime; + return GF_OK; + case 3: + info->name = "mediaStopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_MediaBuffer *) node)->mediaStopTime; + return GF_OK; + case 4: + info->name = "isBuffered"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MediaBuffer *) node)->isBuffered; + return GF_OK; + case 5: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MediaBuffer *) node)->enabled; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MediaBuffer_get_field_index_by_name(char *name) +{ + if (!strcmp("bufferSize", name)) return 0; + if (!strcmp("url", name)) return 1; + if (!strcmp("mediaStartTime", name)) return 2; + if (!strcmp("mediaStopTime", name)) return 3; + if (!strcmp("isBuffered", name)) return 4; + if (!strcmp("enabled", name)) return 5; + return -1; + } +static Bool MediaBuffer_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *MediaBuffer_Create() +{ + M_MediaBuffer *p; + GF_SAFEALLOC(p, M_MediaBuffer); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_MediaBuffer); + + /*default field values*/ + p->bufferSize = FLT2FIX(0.0); + p->mediaStartTime = -1; + p->mediaStopTime = FIX_MAX; + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + MediaControl Node deletion +*/ + +static void MediaControl_Del(GF_Node *node) +{ + M_MediaControl *p = (M_MediaControl *) node; + gf_sg_mfurl_del(p->url); + gf_node_free((GF_Node *) p); +} + +static const u16 MediaControl_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 MediaControl_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 MediaControl_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; + +static u32 MediaControl_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 8; + case GF_SG_FIELD_CODING_DEF: return 8; + case GF_SG_FIELD_CODING_OUT: return 9; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 9; + } +} + +static GF_Err MediaControl_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = MediaControl_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = MediaControl_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = MediaControl_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err MediaControl_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_MediaControl *) node)->url; + return GF_OK; + case 1: + info->name = "mediaStartTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_MediaControl *) node)->mediaStartTime; + return GF_OK; + case 2: + info->name = "mediaStopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_MediaControl *) node)->mediaStopTime; + return GF_OK; + case 3: + info->name = "mediaSpeed"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_MediaControl *) node)->mediaSpeed; + return GF_OK; + case 4: + info->name = "loop"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MediaControl *) node)->loop; + return GF_OK; + case 5: + info->name = "preRoll"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MediaControl *) node)->preRoll; + return GF_OK; + case 6: + info->name = "mute"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MediaControl *) node)->mute; + return GF_OK; + case 7: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MediaControl *) node)->enabled; + return GF_OK; + case 8: + info->name = "isPreRolled"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MediaControl *) node)->isPreRolled; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MediaControl_get_field_index_by_name(char *name) +{ + if (!strcmp("url", name)) return 0; + if (!strcmp("mediaStartTime", name)) return 1; + if (!strcmp("mediaStopTime", name)) return 2; + if (!strcmp("mediaSpeed", name)) return 3; + if (!strcmp("loop", name)) return 4; + if (!strcmp("preRoll", name)) return 5; + if (!strcmp("mute", name)) return 6; + if (!strcmp("enabled", name)) return 7; + if (!strcmp("isPreRolled", name)) return 8; + return -1; + } +static Bool MediaControl_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *MediaControl_Create() +{ + M_MediaControl *p; + GF_SAFEALLOC(p, M_MediaControl); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_MediaControl); + + /*default field values*/ + p->mediaStartTime = -1; + p->mediaStopTime = FIX_MAX; + p->mediaSpeed = FLT2FIX(1.0); + p->preRoll = 1; + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + MediaSensor Node deletion +*/ + +static void MediaSensor_Del(GF_Node *node) +{ + M_MediaSensor *p = (M_MediaSensor *) node; + gf_sg_mfurl_del(p->url); + gf_sg_mfstring_del(p->info); + gf_node_free((GF_Node *) p); +} + +static const u16 MediaSensor_Def2All[] = { 0}; +static const u16 MediaSensor_In2All[] = { 0}; +static const u16 MediaSensor_Out2All[] = { 0, 1, 2, 3, 4, 5}; + +static u32 MediaSensor_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 6; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 6; + } +} + +static GF_Err MediaSensor_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = MediaSensor_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = MediaSensor_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = MediaSensor_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err MediaSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((M_MediaSensor *) node)->url; + return GF_OK; + case 1: + info->name = "mediaCurrentTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_MediaSensor *) node)->mediaCurrentTime; + return GF_OK; + case 2: + info->name = "streamObjectStartTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_MediaSensor *) node)->streamObjectStartTime; + return GF_OK; + case 3: + info->name = "mediaDuration"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_MediaSensor *) node)->mediaDuration; + return GF_OK; + case 4: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_MediaSensor *) node)->isActive; + return GF_OK; + case 5: + info->name = "info"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_MediaSensor *) node)->info; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MediaSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("url", name)) return 0; + if (!strcmp("mediaCurrentTime", name)) return 1; + if (!strcmp("streamObjectStartTime", name)) return 2; + if (!strcmp("mediaDuration", name)) return 3; + if (!strcmp("isActive", name)) return 4; + if (!strcmp("info", name)) return 5; + return -1; + } +static Bool MediaSensor_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *MediaSensor_Create() +{ + M_MediaSensor *p; + GF_SAFEALLOC(p, M_MediaSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_MediaSensor); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + CoordinateInterpolator4D Node deletion +*/ + +static void CoordinateInterpolator4D_Del(GF_Node *node) +{ + M_CoordinateInterpolator4D *p = (M_CoordinateInterpolator4D *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec4f_del(p->keyValue); + gf_sg_mfvec4f_del(p->value_changed); + gf_node_free((GF_Node *) p); +} + +static const u16 CoordinateInterpolator4D_Def2All[] = { 1, 2}; +static const u16 CoordinateInterpolator4D_In2All[] = { 0, 1, 2}; +static const u16 CoordinateInterpolator4D_Out2All[] = { 1, 2, 3}; + +static u32 CoordinateInterpolator4D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err CoordinateInterpolator4D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = CoordinateInterpolator4D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = CoordinateInterpolator4D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = CoordinateInterpolator4D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err CoordinateInterpolator4D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_CoordinateInterpolator4D *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_CoordinateInterpolator4D *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_CoordinateInterpolator4D *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC4F; + info->far_ptr = & ((M_CoordinateInterpolator4D *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFVEC4F; + info->far_ptr = & ((M_CoordinateInterpolator4D *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 CoordinateInterpolator4D_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + return -1; + } +static Bool CoordinateInterpolator4D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 15; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *CoordinateInterpolator4D_Create() +{ + M_CoordinateInterpolator4D *p; + GF_SAFEALLOC(p, M_CoordinateInterpolator4D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_CoordinateInterpolator4D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + NonLinearDeformer Node deletion +*/ + +static void NonLinearDeformer_Del(GF_Node *node) +{ + M_NonLinearDeformer *p = (M_NonLinearDeformer *) node; + gf_sg_mffloat_del(p->extend); + gf_node_unregister((GF_Node *) p->geometry, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 NonLinearDeformer_Def2All[] = { 0, 1, 2, 3, 4}; +static const u16 NonLinearDeformer_In2All[] = { 0, 1, 2, 3, 4}; +static const u16 NonLinearDeformer_Out2All[] = { 0, 1, 2, 3, 4}; + +static u32 NonLinearDeformer_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 5; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 5; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 5; + } +} + +static GF_Err NonLinearDeformer_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = NonLinearDeformer_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = NonLinearDeformer_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = NonLinearDeformer_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err NonLinearDeformer_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "axis"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_NonLinearDeformer *) node)->axis; + return GF_OK; + case 1: + info->name = "extend"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_NonLinearDeformer *) node)->extend; + return GF_OK; + case 2: + info->name = "geometry"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFGeometryNode; + info->far_ptr = & ((M_NonLinearDeformer *)node)->geometry; + return GF_OK; + case 3: + info->name = "param"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_NonLinearDeformer *) node)->param; + return GF_OK; + case 4: + info->name = "type"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_NonLinearDeformer *) node)->type; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 NonLinearDeformer_get_field_index_by_name(char *name) +{ + if (!strcmp("axis", name)) return 0; + if (!strcmp("extend", name)) return 1; + if (!strcmp("geometry", name)) return 2; + if (!strcmp("param", name)) return 3; + if (!strcmp("type", name)) return 4; + return -1; + } +static Bool NonLinearDeformer_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *NonLinearDeformer_Create() +{ + M_NonLinearDeformer *p; + GF_SAFEALLOC(p, M_NonLinearDeformer); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_NonLinearDeformer); + + /*default field values*/ + p->axis.x = FLT2FIX(0); + p->axis.y = FLT2FIX(0); + p->axis.z = FLT2FIX(1); + p->param = FLT2FIX(0); + p->type = 0; + return (GF_Node *)p; +} + + +/* + PositionAnimator Node deletion +*/ + +static void PositionAnimator_Del(GF_Node *node) +{ + M_PositionAnimator *p = (M_PositionAnimator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfrotation_del(p->keyOrientation); + gf_sg_mfvec2f_del(p->keySpline); + gf_sg_mfvec3f_del(p->keyValue); + gf_sg_mffloat_del(p->weight); + gf_node_free((GF_Node *) p); +} + +static const u16 PositionAnimator_Def2All[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 PositionAnimator_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 PositionAnimator_Out2All[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + +static u32 PositionAnimator_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 10; + case GF_SG_FIELD_CODING_DEF: return 9; + case GF_SG_FIELD_CODING_OUT: return 12; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 13; + } +} + +static GF_Err PositionAnimator_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PositionAnimator_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PositionAnimator_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PositionAnimator_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PositionAnimator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_PositionAnimator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PositionAnimator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "fromTo"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PositionAnimator *) node)->fromTo; + return GF_OK; + case 2: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_PositionAnimator *) node)->key; + return GF_OK; + case 3: + info->name = "keyOrientation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFROTATION; + info->far_ptr = & ((M_PositionAnimator *) node)->keyOrientation; + return GF_OK; + case 4: + info->name = "keyType"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_PositionAnimator *) node)->keyType; + return GF_OK; + case 5: + info->name = "keySpline"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_PositionAnimator *) node)->keySpline; + return GF_OK; + case 6: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((M_PositionAnimator *) node)->keyValue; + return GF_OK; + case 7: + info->name = "keyValueType"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_PositionAnimator *) node)->keyValueType; + return GF_OK; + case 8: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_PositionAnimator *) node)->offset; + return GF_OK; + case 9: + info->name = "weight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_PositionAnimator *) node)->weight; + return GF_OK; + case 10: + info->name = "endValue"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_PositionAnimator *) node)->endValue; + return GF_OK; + case 11: + info->name = "rotation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((M_PositionAnimator *) node)->rotation_changed; + return GF_OK; + case 12: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((M_PositionAnimator *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PositionAnimator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("fromTo", name)) return 1; + if (!strcmp("key", name)) return 2; + if (!strcmp("keyOrientation", name)) return 3; + if (!strcmp("keyType", name)) return 4; + if (!strcmp("keySpline", name)) return 5; + if (!strcmp("keyValue", name)) return 6; + if (!strcmp("keyValueType", name)) return 7; + if (!strcmp("offset", name)) return 8; + if (!strcmp("weight", name)) return 9; + if (!strcmp("endValue", name)) return 10; + if (!strcmp("rotation_changed", name)) return 11; + if (!strcmp("value_changed", name)) return 12; + return -1; + } +static Bool PositionAnimator_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + return 1; + case 2: + *AType = 0; + *QType = 8; + return 1; + case 5: + *AType = 0; + *QType = 8; + return 1; + case 6: + *AType = 0; + *QType = 4; + return 1; + case 8: + *AType = 0; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *PositionAnimator_Create() +{ + M_PositionAnimator *p; + GF_SAFEALLOC(p, M_PositionAnimator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PositionAnimator); + + /*default field values*/ + p->fromTo.x = FLT2FIX(0); + p->fromTo.y = FLT2FIX(1); + p->keyType = 0; + p->keySpline.vals = (SFVec2f*)malloc(sizeof(SFVec2f)*2); + p->keySpline.count = 2; + p->keySpline.vals[0].x = FLT2FIX(0); + p->keySpline.vals[0].y = FLT2FIX(0); + p->keySpline.vals[1].x = FLT2FIX(1); + p->keySpline.vals[1].y = FLT2FIX(1); + p->keyValueType = 0; + p->offset.x = FLT2FIX(0); + p->offset.y = FLT2FIX(0); + p->offset.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + PositionAnimator2D Node deletion +*/ + +static void PositionAnimator2D_Del(GF_Node *node) +{ + M_PositionAnimator2D *p = (M_PositionAnimator2D *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec2f_del(p->keySpline); + gf_sg_mfvec2f_del(p->keyValue); + gf_sg_mffloat_del(p->weight); + gf_node_free((GF_Node *) p); +} + +static const u16 PositionAnimator2D_Def2All[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 PositionAnimator2D_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 PositionAnimator2D_Out2All[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + +static u32 PositionAnimator2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 10; + case GF_SG_FIELD_CODING_DEF: return 9; + case GF_SG_FIELD_CODING_OUT: return 12; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 13; + } +} + +static GF_Err PositionAnimator2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PositionAnimator2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PositionAnimator2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PositionAnimator2D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PositionAnimator2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_PositionAnimator2D *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PositionAnimator2D *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "fromTo"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PositionAnimator2D *) node)->fromTo; + return GF_OK; + case 2: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_PositionAnimator2D *) node)->key; + return GF_OK; + case 3: + info->name = "keyOrientation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_PositionAnimator2D *) node)->keyOrientation; + return GF_OK; + case 4: + info->name = "keyType"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_PositionAnimator2D *) node)->keyType; + return GF_OK; + case 5: + info->name = "keySpline"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_PositionAnimator2D *) node)->keySpline; + return GF_OK; + case 6: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_PositionAnimator2D *) node)->keyValue; + return GF_OK; + case 7: + info->name = "keyValueType"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_PositionAnimator2D *) node)->keyValueType; + return GF_OK; + case 8: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PositionAnimator2D *) node)->offset; + return GF_OK; + case 9: + info->name = "weight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_PositionAnimator2D *) node)->weight; + return GF_OK; + case 10: + info->name = "endValue"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PositionAnimator2D *) node)->endValue; + return GF_OK; + case 11: + info->name = "rotation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PositionAnimator2D *) node)->rotation_changed; + return GF_OK; + case 12: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_PositionAnimator2D *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PositionAnimator2D_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("fromTo", name)) return 1; + if (!strcmp("key", name)) return 2; + if (!strcmp("keyOrientation", name)) return 3; + if (!strcmp("keyType", name)) return 4; + if (!strcmp("keySpline", name)) return 5; + if (!strcmp("keyValue", name)) return 6; + if (!strcmp("keyValueType", name)) return 7; + if (!strcmp("offset", name)) return 8; + if (!strcmp("weight", name)) return 9; + if (!strcmp("endValue", name)) return 10; + if (!strcmp("rotation_changed", name)) return 11; + if (!strcmp("value_changed", name)) return 12; + return -1; + } +static Bool PositionAnimator2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + return 1; + case 2: + *AType = 0; + *QType = 8; + return 1; + case 5: + *AType = 0; + *QType = 8; + return 1; + case 6: + *AType = 0; + *QType = 4; + return 1; + case 8: + *AType = 0; + *QType = 2; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *PositionAnimator2D_Create() +{ + M_PositionAnimator2D *p; + GF_SAFEALLOC(p, M_PositionAnimator2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PositionAnimator2D); + + /*default field values*/ + p->fromTo.x = FLT2FIX(0); + p->fromTo.y = FLT2FIX(1); + p->keyOrientation = 0; + p->keyType = 0; + p->keySpline.vals = (SFVec2f*)malloc(sizeof(SFVec2f)*2); + p->keySpline.count = 2; + p->keySpline.vals[0].x = FLT2FIX(0); + p->keySpline.vals[0].y = FLT2FIX(0); + p->keySpline.vals[1].x = FLT2FIX(1); + p->keySpline.vals[1].y = FLT2FIX(1); + p->keyValueType = 0; + p->offset.x = FLT2FIX(0); + p->offset.y = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + PositionInterpolator4D Node deletion +*/ + +static void PositionInterpolator4D_Del(GF_Node *node) +{ + M_PositionInterpolator4D *p = (M_PositionInterpolator4D *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec4f_del(p->keyValue); + gf_node_free((GF_Node *) p); +} + +static const u16 PositionInterpolator4D_Def2All[] = { 1, 2}; +static const u16 PositionInterpolator4D_In2All[] = { 0, 1, 2}; +static const u16 PositionInterpolator4D_Out2All[] = { 1, 2, 3}; + +static u32 PositionInterpolator4D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 2; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 4; + } +} + +static GF_Err PositionInterpolator4D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PositionInterpolator4D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PositionInterpolator4D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PositionInterpolator4D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PositionInterpolator4D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_PositionInterpolator4D *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PositionInterpolator4D *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_PositionInterpolator4D *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC4F; + info->far_ptr = & ((M_PositionInterpolator4D *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC4F; + info->far_ptr = & ((M_PositionInterpolator4D *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PositionInterpolator4D_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + return -1; + } +static Bool PositionInterpolator4D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 15; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *PositionInterpolator4D_Create() +{ + M_PositionInterpolator4D *p; + GF_SAFEALLOC(p, M_PositionInterpolator4D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PositionInterpolator4D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + ScalarAnimator Node deletion +*/ + +static void ScalarAnimator_Del(GF_Node *node) +{ + M_ScalarAnimator *p = (M_ScalarAnimator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec2f_del(p->keySpline); + gf_sg_mffloat_del(p->keyValue); + gf_sg_mffloat_del(p->weight); + gf_node_free((GF_Node *) p); +} + +static const u16 ScalarAnimator_Def2All[] = { 1, 2, 3, 4, 5, 6, 7, 8}; +static const u16 ScalarAnimator_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; +static const u16 ScalarAnimator_Out2All[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + +static u32 ScalarAnimator_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 9; + case GF_SG_FIELD_CODING_DEF: return 8; + case GF_SG_FIELD_CODING_OUT: return 10; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 11; + } +} + +static GF_Err ScalarAnimator_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ScalarAnimator_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ScalarAnimator_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ScalarAnimator_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ScalarAnimator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_ScalarAnimator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ScalarAnimator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "fromTo"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_ScalarAnimator *) node)->fromTo; + return GF_OK; + case 2: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_ScalarAnimator *) node)->key; + return GF_OK; + case 3: + info->name = "keyType"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_ScalarAnimator *) node)->keyType; + return GF_OK; + case 4: + info->name = "keySpline"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((M_ScalarAnimator *) node)->keySpline; + return GF_OK; + case 5: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_ScalarAnimator *) node)->keyValue; + return GF_OK; + case 6: + info->name = "keyValueType"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_ScalarAnimator *) node)->keyValueType; + return GF_OK; + case 7: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ScalarAnimator *) node)->offset; + return GF_OK; + case 8: + info->name = "weight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_ScalarAnimator *) node)->weight; + return GF_OK; + case 9: + info->name = "endValue"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ScalarAnimator *) node)->endValue; + return GF_OK; + case 10: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ScalarAnimator *) node)->value_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ScalarAnimator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("fromTo", name)) return 1; + if (!strcmp("key", name)) return 2; + if (!strcmp("keyType", name)) return 3; + if (!strcmp("keySpline", name)) return 4; + if (!strcmp("keyValue", name)) return 5; + if (!strcmp("keyValueType", name)) return 6; + if (!strcmp("offset", name)) return 7; + if (!strcmp("weight", name)) return 8; + if (!strcmp("endValue", name)) return 9; + if (!strcmp("value_changed", name)) return 10; + return -1; + } +static Bool ScalarAnimator_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 0; + *QType = 8; + return 1; + case 2: + *AType = 0; + *QType = 8; + return 1; + case 4: + *AType = 0; + *QType = 8; + return 1; + case 5: + *AType = 0; + *QType = 0; + return 1; + default: + return 0; + } +} + + + +GF_Node *ScalarAnimator_Create() +{ + M_ScalarAnimator *p; + GF_SAFEALLOC(p, M_ScalarAnimator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ScalarAnimator); + + /*default field values*/ + p->fromTo.x = FLT2FIX(0); + p->fromTo.y = FLT2FIX(1); + p->keyType = 0; + p->keySpline.vals = (SFVec2f*)malloc(sizeof(SFVec2f)*2); + p->keySpline.count = 2; + p->keySpline.vals[0].x = FLT2FIX(0); + p->keySpline.vals[0].y = FLT2FIX(0); + p->keySpline.vals[1].x = FLT2FIX(1); + p->keySpline.vals[1].y = FLT2FIX(1); + p->keyValueType = 0; + p->offset = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Clipper2D Node deletion +*/ + +static void Clipper2D_Del(GF_Node *node) +{ + M_Clipper2D *p = (M_Clipper2D *) node; + gf_node_unregister((GF_Node *) p->geometry, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->transform, (GF_Node *) p); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 Clipper2D_Def2All[] = { 2, 3, 4, 5, 6}; +static const u16 Clipper2D_In2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 Clipper2D_Out2All[] = { 2, 3, 4, 5, 6}; + +static u32 Clipper2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 7; + case GF_SG_FIELD_CODING_DEF: return 5; + case GF_SG_FIELD_CODING_OUT: return 5; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 7; + } +} + +static GF_Err Clipper2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Clipper2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Clipper2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Clipper2D_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Clipper2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Clipper2D *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Clipper2D *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Clipper2D *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Clipper2D *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Clipper2D *)node)->children; + return GF_OK; + case 3: + info->name = "geometry"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFGeometryNode; + info->far_ptr = & ((M_Clipper2D *)node)->geometry; + return GF_OK; + case 4: + info->name = "inside"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Clipper2D *) node)->inside; + return GF_OK; + case 5: + info->name = "transform"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_Clipper2D *)node)->transform; + return GF_OK; + case 6: + info->name = "XOR"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Clipper2D *) node)->XOR; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Clipper2D_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("geometry", name)) return 3; + if (!strcmp("inside", name)) return 4; + if (!strcmp("transform", name)) return 5; + if (!strcmp("XOR", name)) return 6; + return -1; + } +static Bool Clipper2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + default: + return 0; + } +} + + + +GF_Node *Clipper2D_Create() +{ + M_Clipper2D *p; + GF_SAFEALLOC(p, M_Clipper2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Clipper2D); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->inside = 1; + return (GF_Node *)p; +} + + +/* + ColorTransform Node deletion +*/ + +static void ColorTransform_Del(GF_Node *node) +{ + M_ColorTransform *p = (M_ColorTransform *) node; + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 ColorTransform_Def2All[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}; +static const u16 ColorTransform_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}; +static const u16 ColorTransform_Out2All[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}; +static const u16 ColorTransform_Dyn2All[] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}; + +static u32 ColorTransform_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 23; + case GF_SG_FIELD_CODING_DEF: return 21; + case GF_SG_FIELD_CODING_OUT: return 21; + case GF_SG_FIELD_CODING_DYN: return 20; + default: + return 23; + } +} + +static GF_Err ColorTransform_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = ColorTransform_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = ColorTransform_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = ColorTransform_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = ColorTransform_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err ColorTransform_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_ColorTransform *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_ColorTransform *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_ColorTransform *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_ColorTransform *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_ColorTransform *)node)->children; + return GF_OK; + case 3: + info->name = "mrr"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mrr; + return GF_OK; + case 4: + info->name = "mrg"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mrg; + return GF_OK; + case 5: + info->name = "mrb"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mrb; + return GF_OK; + case 6: + info->name = "mra"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mra; + return GF_OK; + case 7: + info->name = "tr"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->tr; + return GF_OK; + case 8: + info->name = "mgr"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mgr; + return GF_OK; + case 9: + info->name = "mgg"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mgg; + return GF_OK; + case 10: + info->name = "mgb"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mgb; + return GF_OK; + case 11: + info->name = "mga"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mga; + return GF_OK; + case 12: + info->name = "tg"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->tg; + return GF_OK; + case 13: + info->name = "mbr"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mbr; + return GF_OK; + case 14: + info->name = "mbg"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mbg; + return GF_OK; + case 15: + info->name = "mbb"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mbb; + return GF_OK; + case 16: + info->name = "mba"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mba; + return GF_OK; + case 17: + info->name = "tb"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->tb; + return GF_OK; + case 18: + info->name = "mar"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mar; + return GF_OK; + case 19: + info->name = "mag"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mag; + return GF_OK; + case 20: + info->name = "mab"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->mab; + return GF_OK; + case 21: + info->name = "maa"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->maa; + return GF_OK; + case 22: + info->name = "ta"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_ColorTransform *) node)->ta; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ColorTransform_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("mrr", name)) return 3; + if (!strcmp("mrg", name)) return 4; + if (!strcmp("mrb", name)) return 5; + if (!strcmp("mra", name)) return 6; + if (!strcmp("tr", name)) return 7; + if (!strcmp("mgr", name)) return 8; + if (!strcmp("mgg", name)) return 9; + if (!strcmp("mgb", name)) return 10; + if (!strcmp("mga", name)) return 11; + if (!strcmp("tg", name)) return 12; + if (!strcmp("mbr", name)) return 13; + if (!strcmp("mbg", name)) return 14; + if (!strcmp("mbb", name)) return 15; + if (!strcmp("mba", name)) return 16; + if (!strcmp("tb", name)) return 17; + if (!strcmp("mar", name)) return 18; + if (!strcmp("mag", name)) return 19; + if (!strcmp("mab", name)) return 20; + if (!strcmp("maa", name)) return 21; + if (!strcmp("ta", name)) return 22; + return -1; + } +static Bool ColorTransform_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 5: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 7: + *AType = 7; + *QType = 4; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 8: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 9: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 10: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 11: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 12: + *AType = 7; + *QType = 4; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 13: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 14: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 15: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 16: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 17: + *AType = 7; + *QType = 4; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 18: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 19: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 20: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 21: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 22: + *AType = 7; + *QType = 4; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *ColorTransform_Create() +{ + M_ColorTransform *p; + GF_SAFEALLOC(p, M_ColorTransform); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_ColorTransform); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->mrr = FLT2FIX(1); + p->mrg = FLT2FIX(0); + p->mrb = FLT2FIX(0); + p->mra = FLT2FIX(0); + p->tr = FLT2FIX(0); + p->mgr = FLT2FIX(0); + p->mgg = FLT2FIX(1); + p->mgb = FLT2FIX(0); + p->mga = FLT2FIX(0); + p->tg = FLT2FIX(0); + p->mbr = FLT2FIX(0); + p->mbg = FLT2FIX(0); + p->mbb = FLT2FIX(1); + p->mba = FLT2FIX(0); + p->tb = FLT2FIX(0); + p->mar = FLT2FIX(0); + p->mag = FLT2FIX(0); + p->mab = FLT2FIX(0); + p->maa = FLT2FIX(1); + p->ta = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Ellipse Node deletion +*/ + +static void Ellipse_Del(GF_Node *node) +{ + M_Ellipse *p = (M_Ellipse *) node; + gf_node_free((GF_Node *) p); +} + +static const u16 Ellipse_Def2All[] = { 0}; +static const u16 Ellipse_In2All[] = { 0}; +static const u16 Ellipse_Out2All[] = { 0}; +static const u16 Ellipse_Dyn2All[] = { 0}; + +static u32 Ellipse_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 1; + case GF_SG_FIELD_CODING_DEF: return 1; + case GF_SG_FIELD_CODING_OUT: return 1; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 1; + } +} + +static GF_Err Ellipse_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Ellipse_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Ellipse_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Ellipse_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Ellipse_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Ellipse_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "radius"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Ellipse *) node)->radius; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Ellipse_get_field_index_by_name(char *name) +{ + if (!strcmp("radius", name)) return 0; + return -1; + } +static Bool Ellipse_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 2; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *Ellipse_Create() +{ + M_Ellipse *p; + GF_SAFEALLOC(p, M_Ellipse); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Ellipse); + + /*default field values*/ + p->radius.x = FLT2FIX(1); + p->radius.y = FLT2FIX(1); + return (GF_Node *)p; +} + + +/* + LinearGradient Node deletion +*/ + +static void LinearGradient_Del(GF_Node *node) +{ + M_LinearGradient *p = (M_LinearGradient *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfcolor_del(p->keyValue); + gf_sg_mffloat_del(p->opacity); + gf_node_unregister((GF_Node *) p->transform, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 LinearGradient_Def2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 LinearGradient_In2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 LinearGradient_Out2All[] = { 0, 1, 2, 3, 4, 5, 6}; +static const u16 LinearGradient_Dyn2All[] = { 0, 5}; + +static u32 LinearGradient_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 7; + case GF_SG_FIELD_CODING_DEF: return 7; + case GF_SG_FIELD_CODING_OUT: return 7; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 7; + } +} + +static GF_Err LinearGradient_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = LinearGradient_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = LinearGradient_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = LinearGradient_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = LinearGradient_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err LinearGradient_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "endPoint"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_LinearGradient *) node)->endPoint; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_LinearGradient *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((M_LinearGradient *) node)->keyValue; + return GF_OK; + case 3: + info->name = "opacity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_LinearGradient *) node)->opacity; + return GF_OK; + case 4: + info->name = "spreadMethod"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_LinearGradient *) node)->spreadMethod; + return GF_OK; + case 5: + info->name = "startPoint"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_LinearGradient *) node)->startPoint; + return GF_OK; + case 6: + info->name = "transform"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_LinearGradient *)node)->transform; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 LinearGradient_get_field_index_by_name(char *name) +{ + if (!strcmp("endPoint", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("opacity", name)) return 3; + if (!strcmp("spreadMethod", name)) return 4; + if (!strcmp("startPoint", name)) return 5; + if (!strcmp("transform", name)) return 6; + return -1; + } +static Bool LinearGradient_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 2; + *QType = 5; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 3: + *AType = 0; + *QType = 7; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 4: + *AType = 0; + *QType = 13; + *QT13_bits = 2; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(2); + return 1; + case 5: + *AType = 2; + *QType = 5; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *LinearGradient_Create() +{ + M_LinearGradient *p; + GF_SAFEALLOC(p, M_LinearGradient); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_LinearGradient); + + /*default field values*/ + p->endPoint.x = FLT2FIX(1); + p->endPoint.y = FLT2FIX(0); + p->opacity.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->opacity.count = 1; + p->opacity.vals[0] = FLT2FIX(1); + p->spreadMethod = 0; + p->startPoint.x = FLT2FIX(0); + p->startPoint.y = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + PathLayout Node deletion +*/ + +static void PathLayout_Del(GF_Node *node) +{ + M_PathLayout *p = (M_PathLayout *) node; + gf_node_unregister((GF_Node *) p->geometry, (GF_Node *) p); + gf_sg_mfint32_del(p->alignment); + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 PathLayout_Def2All[] = { 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 PathLayout_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 PathLayout_Out2All[] = { 2, 3, 4, 5, 6, 7, 8, 9}; +static const u16 PathLayout_Dyn2All[] = { 5, 6}; + +static u32 PathLayout_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 10; + case GF_SG_FIELD_CODING_DEF: return 8; + case GF_SG_FIELD_CODING_OUT: return 8; + case GF_SG_FIELD_CODING_DYN: return 2; + default: + return 10; + } +} + +static GF_Err PathLayout_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = PathLayout_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = PathLayout_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = PathLayout_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = PathLayout_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err PathLayout_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_PathLayout *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_PathLayout *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_PathLayout *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_PathLayout *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_PathLayout *)node)->children; + return GF_OK; + case 3: + info->name = "geometry"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFGeometryNode; + info->far_ptr = & ((M_PathLayout *)node)->geometry; + return GF_OK; + case 4: + info->name = "alignment"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_PathLayout *) node)->alignment; + return GF_OK; + case 5: + info->name = "pathOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PathLayout *) node)->pathOffset; + return GF_OK; + case 6: + info->name = "spacing"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_PathLayout *) node)->spacing; + return GF_OK; + case 7: + info->name = "reverseLayout"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PathLayout *) node)->reverseLayout; + return GF_OK; + case 8: + info->name = "wrapMode"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_PathLayout *) node)->wrapMode; + return GF_OK; + case 9: + info->name = "splitText"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_PathLayout *) node)->splitText; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PathLayout_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("geometry", name)) return 3; + if (!strcmp("alignment", name)) return 4; + if (!strcmp("pathOffset", name)) return 5; + if (!strcmp("spacing", name)) return 6; + if (!strcmp("reverseLayout", name)) return 7; + if (!strcmp("wrapMode", name)) return 8; + if (!strcmp("splitText", name)) return 9; + return -1; + } +static Bool PathLayout_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 4: + *AType = 0; + *QType = 13; + *QT13_bits = 2; + *b_min = FLT2FIX(-1); + *b_max = FLT2FIX(1); + return 1; + case 5: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 8: + *AType = 0; + *QType = 13; + *QT13_bits = 2; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(2); + return 1; + default: + return 0; + } +} + + + +GF_Node *PathLayout_Create() +{ + M_PathLayout *p; + GF_SAFEALLOC(p, M_PathLayout); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_PathLayout); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->alignment.vals = (SFInt32*)malloc(sizeof(SFInt32)*1); + p->alignment.count = 1; + p->alignment.vals[0] = 0; + p->pathOffset = FLT2FIX(0); + p->spacing = FLT2FIX(1.0); + p->wrapMode = 0; + p->splitText = 1; + return (GF_Node *)p; +} + + +/* + RadialGradient Node deletion +*/ + +static void RadialGradient_Del(GF_Node *node) +{ + M_RadialGradient *p = (M_RadialGradient *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfcolor_del(p->keyValue); + gf_sg_mffloat_del(p->opacity); + gf_node_unregister((GF_Node *) p->transform, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 RadialGradient_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 RadialGradient_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 RadialGradient_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7}; +static const u16 RadialGradient_Dyn2All[] = { 0, 1, 5}; + +static u32 RadialGradient_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 8; + case GF_SG_FIELD_CODING_DEF: return 8; + case GF_SG_FIELD_CODING_OUT: return 8; + case GF_SG_FIELD_CODING_DYN: return 3; + default: + return 8; + } +} + +static GF_Err RadialGradient_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = RadialGradient_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = RadialGradient_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = RadialGradient_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = RadialGradient_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err RadialGradient_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_RadialGradient *) node)->center; + return GF_OK; + case 1: + info->name = "focalPoint"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_RadialGradient *) node)->focalPoint; + return GF_OK; + case 2: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_RadialGradient *) node)->key; + return GF_OK; + case 3: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((M_RadialGradient *) node)->keyValue; + return GF_OK; + case 4: + info->name = "opacity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_RadialGradient *) node)->opacity; + return GF_OK; + case 5: + info->name = "radius"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_RadialGradient *) node)->radius; + return GF_OK; + case 6: + info->name = "spreadMethod"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_RadialGradient *) node)->spreadMethod; + return GF_OK; + case 7: + info->name = "transform"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((M_RadialGradient *)node)->transform; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 RadialGradient_get_field_index_by_name(char *name) +{ + if (!strcmp("center", name)) return 0; + if (!strcmp("focalPoint", name)) return 1; + if (!strcmp("key", name)) return 2; + if (!strcmp("keyValue", name)) return 3; + if (!strcmp("opacity", name)) return 4; + if (!strcmp("radius", name)) return 5; + if (!strcmp("spreadMethod", name)) return 6; + if (!strcmp("transform", name)) return 7; + return -1; + } +static Bool RadialGradient_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 2; + *QType = 5; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 1: + *AType = 2; + *QType = 5; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 0; + *QType = 8; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 3: + *AType = 0; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 4: + *AType = 0; + *QType = 7; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 5: + *AType = 7; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 0; + *QType = 13; + *QT13_bits = 2; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(2); + return 1; + default: + return 0; + } +} + + + +GF_Node *RadialGradient_Create() +{ + M_RadialGradient *p; + GF_SAFEALLOC(p, M_RadialGradient); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_RadialGradient); + + /*default field values*/ + p->center.x = FLT2FIX(0.5); + p->center.y = FLT2FIX(0.5); + p->focalPoint.x = FLT2FIX(0); + p->focalPoint.y = FLT2FIX(0); + p->opacity.vals = (SFFloat*)malloc(sizeof(SFFloat)*1); + p->opacity.count = 1; + p->opacity.vals[0] = FLT2FIX(1); + p->radius = FLT2FIX(0.5); + p->spreadMethod = 0; + return (GF_Node *)p; +} + + +/* + TransformMatrix2D Node deletion +*/ + +static void TransformMatrix2D_Del(GF_Node *node) +{ + M_TransformMatrix2D *p = (M_TransformMatrix2D *) node; + gf_sg_vrml_parent_destroy((GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 TransformMatrix2D_Def2All[] = { 2, 3, 4, 5, 6, 7, 8}; +static const u16 TransformMatrix2D_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; +static const u16 TransformMatrix2D_Out2All[] = { 2, 3, 4, 5, 6, 7, 8}; +static const u16 TransformMatrix2D_Dyn2All[] = { 3, 4, 5, 6, 7, 8}; + +static u32 TransformMatrix2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 9; + case GF_SG_FIELD_CODING_DEF: return 7; + case GF_SG_FIELD_CODING_OUT: return 7; + case GF_SG_FIELD_CODING_DYN: return 6; + default: + return 9; + } +} + +static GF_Err TransformMatrix2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = TransformMatrix2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = TransformMatrix2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = TransformMatrix2D_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = TransformMatrix2D_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err TransformMatrix2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_TransformMatrix2D *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_TransformMatrix2D *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_TransformMatrix2D *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_TransformMatrix2D *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF2DNode; + info->far_ptr = & ((M_TransformMatrix2D *)node)->children; + return GF_OK; + case 3: + info->name = "mxx"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_TransformMatrix2D *) node)->mxx; + return GF_OK; + case 4: + info->name = "mxy"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_TransformMatrix2D *) node)->mxy; + return GF_OK; + case 5: + info->name = "tx"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_TransformMatrix2D *) node)->tx; + return GF_OK; + case 6: + info->name = "myx"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_TransformMatrix2D *) node)->myx; + return GF_OK; + case 7: + info->name = "myy"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_TransformMatrix2D *) node)->myy; + return GF_OK; + case 8: + info->name = "ty"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_TransformMatrix2D *) node)->ty; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TransformMatrix2D_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("mxx", name)) return 3; + if (!strcmp("mxy", name)) return 4; + if (!strcmp("tx", name)) return 5; + if (!strcmp("myx", name)) return 6; + if (!strcmp("myy", name)) return 7; + if (!strcmp("ty", name)) return 8; + return -1; + } +static Bool TransformMatrix2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 3: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 4: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 5: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 6: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 7: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 8: + *AType = 7; + *QType = 7; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *TransformMatrix2D_Create() +{ + M_TransformMatrix2D *p; + GF_SAFEALLOC(p, M_TransformMatrix2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_TransformMatrix2D); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->mxx = FLT2FIX(1); + p->mxy = FLT2FIX(0); + p->tx = FLT2FIX(0); + p->myx = FLT2FIX(0); + p->myy = FLT2FIX(1); + p->ty = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Viewport Node deletion +*/ + +static void Viewport_Del(GF_Node *node) +{ + M_Viewport *p = (M_Viewport *) node; + gf_sg_mfint32_del(p->alignment); + gf_sg_sfstring_del(p->description); + gf_node_free((GF_Node *) p); +} + +static const u16 Viewport_Def2All[] = { 1, 2, 3, 4, 5, 6}; +static const u16 Viewport_In2All[] = { 0, 1, 2, 3, 4, 5}; +static const u16 Viewport_Out2All[] = { 1, 2, 3, 4, 5, 7, 8}; +static const u16 Viewport_Dyn2All[] = { 1, 2, 3}; + +static u32 Viewport_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 6; + case GF_SG_FIELD_CODING_DEF: return 6; + case GF_SG_FIELD_CODING_OUT: return 7; + case GF_SG_FIELD_CODING_DYN: return 3; + default: + return 9; + } +} + +static GF_Err Viewport_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = Viewport_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = Viewport_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = Viewport_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = Viewport_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err Viewport_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((M_Viewport *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Viewport *) node)->set_bind; + return GF_OK; + case 1: + info->name = "position"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Viewport *) node)->position; + return GF_OK; + case 2: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((M_Viewport *) node)->size; + return GF_OK; + case 3: + info->name = "orientation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_Viewport *) node)->orientation; + return GF_OK; + case 4: + info->name = "alignment"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_Viewport *) node)->alignment; + return GF_OK; + case 5: + info->name = "fit"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_Viewport *) node)->fit; + return GF_OK; + case 6: + info->name = "description"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_Viewport *) node)->description; + return GF_OK; + case 7: + info->name = "bindTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((M_Viewport *) node)->bindTime; + return GF_OK; + case 8: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_Viewport *) node)->isBound; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Viewport_get_field_index_by_name(char *name) +{ + if (!strcmp("set_bind", name)) return 0; + if (!strcmp("position", name)) return 1; + if (!strcmp("size", name)) return 2; + if (!strcmp("orientation", name)) return 3; + if (!strcmp("alignment", name)) return 4; + if (!strcmp("fit", name)) return 5; + if (!strcmp("description", name)) return 6; + if (!strcmp("bindTime", name)) return 7; + if (!strcmp("isBound", name)) return 8; + return -1; + } +static Bool Viewport_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 1; + *QType = 1; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 2: + *AType = 12; + *QType = 12; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + return 1; + case 3: + *AType = 6; + *QType = 6; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(6.2831853); + return 1; + case 4: + *AType = 0; + *QType = 13; + *QT13_bits = 3; + *b_min = FLT2FIX(-1); + *b_max = FLT2FIX(1); + return 1; + case 5: + *AType = 0; + *QType = 13; + *QT13_bits = 3; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(2); + return 1; + default: + return 0; + } +} + + + +GF_Node *Viewport_Create() +{ + M_Viewport *p; + GF_SAFEALLOC(p, M_Viewport); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_Viewport); + + /*default field values*/ + p->position.x = FLT2FIX(0); + p->position.y = FLT2FIX(0); + p->size.x = FLT2FIX(-1); + p->size.y = FLT2FIX(-1); + p->orientation = FLT2FIX(0); + p->alignment.vals = (SFInt32*)malloc(sizeof(SFInt32)*1); + p->alignment.count = 1; + p->alignment.vals[0] = 0; + p->fit = 0; + return (GF_Node *)p; +} + + +/* + XCurve2D Node deletion +*/ + +static void XCurve2D_Del(GF_Node *node) +{ + M_XCurve2D *p = (M_XCurve2D *) node; + gf_node_unregister((GF_Node *) p->point, (GF_Node *) p); + gf_sg_mfint32_del(p->type); + gf_node_free((GF_Node *) p); +} + +static const u16 XCurve2D_Def2All[] = { 0, 1, 2}; +static const u16 XCurve2D_In2All[] = { 0, 1, 2}; +static const u16 XCurve2D_Out2All[] = { 0, 1, 2}; +static const u16 XCurve2D_Dyn2All[] = { 1}; + +static u32 XCurve2D_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 3; + case GF_SG_FIELD_CODING_DEF: return 3; + case GF_SG_FIELD_CODING_OUT: return 3; + case GF_SG_FIELD_CODING_DYN: return 1; + default: + return 3; + } +} + +static GF_Err XCurve2D_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = XCurve2D_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = XCurve2D_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = XCurve2D_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = XCurve2D_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err XCurve2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinate2DNode; + info->far_ptr = & ((M_XCurve2D *)node)->point; + return GF_OK; + case 1: + info->name = "fineness"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_XCurve2D *) node)->fineness; + return GF_OK; + case 2: + info->name = "type"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_XCurve2D *) node)->type; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 XCurve2D_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + if (!strcmp("fineness", name)) return 1; + if (!strcmp("type", name)) return 2; + return -1; + } +static Bool XCurve2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 1: + *AType = 7; + *QType = 0; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 2: + *AType = 0; + *QType = 13; + *QT13_bits = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(15); + return 1; + default: + return 0; + } +} + + + +GF_Node *XCurve2D_Create() +{ + M_XCurve2D *p; + GF_SAFEALLOC(p, M_XCurve2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_XCurve2D); + + /*default field values*/ + p->fineness = FLT2FIX(0.5); + return (GF_Node *)p; +} + + +/* + XFontStyle Node deletion +*/ + +static void XFontStyle_Del(GF_Node *node) +{ + M_XFontStyle *p = (M_XFontStyle *) node; + gf_sg_mfstring_del(p->fontName); + gf_sg_mfstring_del(p->justify); + gf_sg_sfstring_del(p->language); + gf_sg_sfstring_del(p->stretch); + gf_sg_sfstring_del(p->style); + gf_sg_mfstring_del(p->featureName); + gf_sg_mfint32_del(p->featureStartOffset); + gf_sg_mfint32_del(p->featureLength); + gf_sg_mfint32_del(p->featureValue); + gf_node_free((GF_Node *) p); +} + +static const u16 XFontStyle_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; +static const u16 XFontStyle_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; +static const u16 XFontStyle_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + +static u32 XFontStyle_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 17; + case GF_SG_FIELD_CODING_DEF: return 17; + case GF_SG_FIELD_CODING_OUT: return 17; + case GF_SG_FIELD_CODING_DYN: return 0; + default: + return 17; + } +} + +static GF_Err XFontStyle_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = XFontStyle_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = XFontStyle_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = XFontStyle_Out2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err XFontStyle_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "fontName"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_XFontStyle *) node)->fontName; + return GF_OK; + case 1: + info->name = "horizontal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_XFontStyle *) node)->horizontal; + return GF_OK; + case 2: + info->name = "justify"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_XFontStyle *) node)->justify; + return GF_OK; + case 3: + info->name = "language"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_XFontStyle *) node)->language; + return GF_OK; + case 4: + info->name = "leftToRight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_XFontStyle *) node)->leftToRight; + return GF_OK; + case 5: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_XFontStyle *) node)->size; + return GF_OK; + case 6: + info->name = "stretch"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_XFontStyle *) node)->stretch; + return GF_OK; + case 7: + info->name = "letterSpacing"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_XFontStyle *) node)->letterSpacing; + return GF_OK; + case 8: + info->name = "wordSpacing"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_XFontStyle *) node)->wordSpacing; + return GF_OK; + case 9: + info->name = "weight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_XFontStyle *) node)->weight; + return GF_OK; + case 10: + info->name = "fontKerning"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_XFontStyle *) node)->fontKerning; + return GF_OK; + case 11: + info->name = "style"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((M_XFontStyle *) node)->style; + return GF_OK; + case 12: + info->name = "topToBottom"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_XFontStyle *) node)->topToBottom; + return GF_OK; + case 13: + info->name = "featureName"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((M_XFontStyle *) node)->featureName; + return GF_OK; + case 14: + info->name = "featureStartOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_XFontStyle *) node)->featureStartOffset; + return GF_OK; + case 15: + info->name = "featureLength"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_XFontStyle *) node)->featureLength; + return GF_OK; + case 16: + info->name = "featureValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((M_XFontStyle *) node)->featureValue; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 XFontStyle_get_field_index_by_name(char *name) +{ + if (!strcmp("fontName", name)) return 0; + if (!strcmp("horizontal", name)) return 1; + if (!strcmp("justify", name)) return 2; + if (!strcmp("language", name)) return 3; + if (!strcmp("leftToRight", name)) return 4; + if (!strcmp("size", name)) return 5; + if (!strcmp("stretch", name)) return 6; + if (!strcmp("letterSpacing", name)) return 7; + if (!strcmp("wordSpacing", name)) return 8; + if (!strcmp("weight", name)) return 9; + if (!strcmp("fontKerning", name)) return 10; + if (!strcmp("style", name)) return 11; + if (!strcmp("topToBottom", name)) return 12; + if (!strcmp("featureName", name)) return 13; + if (!strcmp("featureStartOffset", name)) return 14; + if (!strcmp("featureLength", name)) return 15; + if (!strcmp("featureValue", name)) return 16; + return -1; + } +static Bool XFontStyle_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 5: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 7: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 8: + *AType = 0; + *QType = 11; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *XFontStyle_Create() +{ + M_XFontStyle *p; + GF_SAFEALLOC(p, M_XFontStyle); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_XFontStyle); + + /*default field values*/ + p->fontName.vals = (char**)malloc(sizeof(SFString)*1); + p->fontName.count = 1; + p->fontName.vals[0] = (char*)malloc(sizeof(char) * 6); + strcpy(p->fontName.vals[0], "SERIF"); + p->horizontal = 1; + p->justify.vals = (char**)malloc(sizeof(SFString)*1); + p->justify.count = 1; + p->justify.vals[0] = (char*)malloc(sizeof(char) * 6); + strcpy(p->justify.vals[0], "BEGIN"); + p->leftToRight = 1; + p->size = FLT2FIX(1.0); + p->stretch.buffer = (char*)malloc(sizeof(char) * 7); + strcpy(p->stretch.buffer, "NORMAL"); + p->letterSpacing = FLT2FIX(0.0); + p->wordSpacing = FLT2FIX(0.0); + p->weight = 400; + p->fontKerning = 1; + p->style.buffer = (char*)malloc(sizeof(char) * 6); + strcpy(p->style.buffer, "PLAIN"); + p->topToBottom = 1; + return (GF_Node *)p; +} + + +/* + XLineProperties Node deletion +*/ + +static void XLineProperties_Del(GF_Node *node) +{ + M_XLineProperties *p = (M_XLineProperties *) node; + gf_sg_mffloat_del(p->dashes); + gf_node_unregister((GF_Node *) p->texture, (GF_Node *) p); + gf_node_unregister((GF_Node *) p->textureTransform, (GF_Node *) p); + gf_node_free((GF_Node *) p); +} + +static const u16 XLineProperties_Def2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; +static const u16 XLineProperties_In2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; +static const u16 XLineProperties_Out2All[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; +static const u16 XLineProperties_Dyn2All[] = { 0, 7, 8, 9, 10}; + +static u32 XLineProperties_get_field_count(GF_Node *node, u8 IndexMode) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: return 13; + case GF_SG_FIELD_CODING_DEF: return 13; + case GF_SG_FIELD_CODING_OUT: return 13; + case GF_SG_FIELD_CODING_DYN: return 5; + default: + return 13; + } +} + +static GF_Err XLineProperties_get_field_index(GF_Node *n, u32 inField, u8 IndexMode, u32 *allField) +{ + switch(IndexMode) { + case GF_SG_FIELD_CODING_IN: + *allField = XLineProperties_In2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DEF: + *allField = XLineProperties_Def2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_OUT: + *allField = XLineProperties_Out2All[inField]; + return GF_OK; + case GF_SG_FIELD_CODING_DYN: + *allField = XLineProperties_Dyn2All[inField]; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} +static GF_Err XLineProperties_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "lineColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((M_XLineProperties *) node)->lineColor; + return GF_OK; + case 1: + info->name = "lineStyle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_XLineProperties *) node)->lineStyle; + return GF_OK; + case 2: + info->name = "isCenterAligned"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_XLineProperties *) node)->isCenterAligned; + return GF_OK; + case 3: + info->name = "isScalable"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((M_XLineProperties *) node)->isScalable; + return GF_OK; + case 4: + info->name = "lineCap"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_XLineProperties *) node)->lineCap; + return GF_OK; + case 5: + info->name = "lineJoin"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((M_XLineProperties *) node)->lineJoin; + return GF_OK; + case 6: + info->name = "miterLimit"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_XLineProperties *) node)->miterLimit; + return GF_OK; + case 7: + info->name = "transparency"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_XLineProperties *) node)->transparency; + return GF_OK; + case 8: + info->name = "width"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_XLineProperties *) node)->width; + return GF_OK; + case 9: + info->name = "dashOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((M_XLineProperties *) node)->dashOffset; + return GF_OK; + case 10: + info->name = "dashes"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((M_XLineProperties *) node)->dashes; + return GF_OK; + case 11: + info->name = "texture"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((M_XLineProperties *)node)->texture; + return GF_OK; + case 12: + info->name = "textureTransform"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureTransformNode; + info->far_ptr = & ((M_XLineProperties *)node)->textureTransform; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 XLineProperties_get_field_index_by_name(char *name) +{ + if (!strcmp("lineColor", name)) return 0; + if (!strcmp("lineStyle", name)) return 1; + if (!strcmp("isCenterAligned", name)) return 2; + if (!strcmp("isScalable", name)) return 3; + if (!strcmp("lineCap", name)) return 4; + if (!strcmp("lineJoin", name)) return 5; + if (!strcmp("miterLimit", name)) return 6; + if (!strcmp("transparency", name)) return 7; + if (!strcmp("width", name)) return 8; + if (!strcmp("dashOffset", name)) return 9; + if (!strcmp("dashes", name)) return 10; + if (!strcmp("texture", name)) return 11; + if (!strcmp("textureTransform", name)) return 12; + return -1; + } +static Bool XLineProperties_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (FieldIndex) { + case 0: + *AType = 4; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 1: + *AType = 0; + *QType = 13; + *QT13_bits = 3; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(5); + return 1; + case 4: + *AType = 0; + *QType = 13; + *QT13_bits = 3; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(2); + return 1; + case 5: + *AType = 0; + *QType = 13; + *QT13_bits = 3; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(2); + return 1; + case 6: + *AType = 0; + *QType = 12; + *b_min = FLT2FIX(1); + *b_max = FIX_MAX; + return 1; + case 7: + *AType = 8; + *QType = 4; + *b_min = FLT2FIX(0); + *b_max = FLT2FIX(1); + return 1; + case 8: + *AType = 7; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 9: + *AType = 7; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + case 10: + *AType = 7; + *QType = 12; + *b_min = FLT2FIX(0); + *b_max = FIX_MAX; + return 1; + default: + return 0; + } +} + + + +GF_Node *XLineProperties_Create() +{ + M_XLineProperties *p; + GF_SAFEALLOC(p, M_XLineProperties); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_MPEG4_XLineProperties); + + /*default field values*/ + p->lineColor.red = FLT2FIX(0); + p->lineColor.green = FLT2FIX(0); + p->lineColor.blue = FLT2FIX(0); + p->lineStyle = 0; + p->isCenterAligned = 1; + p->isScalable = 1; + p->lineCap = 0; + p->lineJoin = 0; + p->miterLimit = FLT2FIX(4); + p->transparency = FLT2FIX(0); + p->width = FLT2FIX(1); + p->dashOffset = FLT2FIX(0); + return (GF_Node *)p; +} + + + + +GF_Node *gf_sg_mpeg4_node_new(u32 NodeTag) +{ + switch (NodeTag) { + case TAG_MPEG4_Anchor: + return Anchor_Create(); + case TAG_MPEG4_AnimationStream: + return AnimationStream_Create(); + case TAG_MPEG4_Appearance: + return Appearance_Create(); + case TAG_MPEG4_AudioBuffer: + return AudioBuffer_Create(); + case TAG_MPEG4_AudioClip: + return AudioClip_Create(); + case TAG_MPEG4_AudioDelay: + return AudioDelay_Create(); + case TAG_MPEG4_AudioFX: + return AudioFX_Create(); + case TAG_MPEG4_AudioMix: + return AudioMix_Create(); + case TAG_MPEG4_AudioSource: + return AudioSource_Create(); + case TAG_MPEG4_AudioSwitch: + return AudioSwitch_Create(); + case TAG_MPEG4_Background: + return Background_Create(); + case TAG_MPEG4_Background2D: + return Background2D_Create(); + case TAG_MPEG4_Billboard: + return Billboard_Create(); + case TAG_MPEG4_Bitmap: + return Bitmap_Create(); + case TAG_MPEG4_Box: + return Box_Create(); + case TAG_MPEG4_Circle: + return Circle_Create(); + case TAG_MPEG4_Collision: + return Collision_Create(); + case TAG_MPEG4_Color: + return Color_Create(); + case TAG_MPEG4_ColorInterpolator: + return ColorInterpolator_Create(); + case TAG_MPEG4_CompositeTexture2D: + return CompositeTexture2D_Create(); + case TAG_MPEG4_CompositeTexture3D: + return CompositeTexture3D_Create(); + case TAG_MPEG4_Conditional: + return Conditional_Create(); + case TAG_MPEG4_Cone: + return Cone_Create(); + case TAG_MPEG4_Coordinate: + return Coordinate_Create(); + case TAG_MPEG4_Coordinate2D: + return Coordinate2D_Create(); + case TAG_MPEG4_CoordinateInterpolator: + return CoordinateInterpolator_Create(); + case TAG_MPEG4_CoordinateInterpolator2D: + return CoordinateInterpolator2D_Create(); + case TAG_MPEG4_Curve2D: + return Curve2D_Create(); + case TAG_MPEG4_Cylinder: + return Cylinder_Create(); + case TAG_MPEG4_CylinderSensor: + return CylinderSensor_Create(); + case TAG_MPEG4_DirectionalLight: + return DirectionalLight_Create(); + case TAG_MPEG4_DiscSensor: + return DiscSensor_Create(); + case TAG_MPEG4_ElevationGrid: + return ElevationGrid_Create(); + case TAG_MPEG4_Extrusion: + return Extrusion_Create(); + case TAG_MPEG4_Fog: + return Fog_Create(); + case TAG_MPEG4_FontStyle: + return FontStyle_Create(); + case TAG_MPEG4_Form: + return Form_Create(); + case TAG_MPEG4_Group: + return Group_Create(); + case TAG_MPEG4_ImageTexture: + return ImageTexture_Create(); + case TAG_MPEG4_IndexedFaceSet: + return IndexedFaceSet_Create(); + case TAG_MPEG4_IndexedFaceSet2D: + return IndexedFaceSet2D_Create(); + case TAG_MPEG4_IndexedLineSet: + return IndexedLineSet_Create(); + case TAG_MPEG4_IndexedLineSet2D: + return IndexedLineSet2D_Create(); + case TAG_MPEG4_Inline: + return Inline_Create(); + case TAG_MPEG4_LOD: + return LOD_Create(); + case TAG_MPEG4_Layer2D: + return Layer2D_Create(); + case TAG_MPEG4_Layer3D: + return Layer3D_Create(); + case TAG_MPEG4_Layout: + return Layout_Create(); + case TAG_MPEG4_LineProperties: + return LineProperties_Create(); + case TAG_MPEG4_ListeningPoint: + return ListeningPoint_Create(); + case TAG_MPEG4_Material: + return Material_Create(); + case TAG_MPEG4_Material2D: + return Material2D_Create(); + case TAG_MPEG4_MovieTexture: + return MovieTexture_Create(); + case TAG_MPEG4_NavigationInfo: + return NavigationInfo_Create(); + case TAG_MPEG4_Normal: + return Normal_Create(); + case TAG_MPEG4_NormalInterpolator: + return NormalInterpolator_Create(); + case TAG_MPEG4_OrderedGroup: + return OrderedGroup_Create(); + case TAG_MPEG4_OrientationInterpolator: + return OrientationInterpolator_Create(); + case TAG_MPEG4_PixelTexture: + return PixelTexture_Create(); + case TAG_MPEG4_PlaneSensor: + return PlaneSensor_Create(); + case TAG_MPEG4_PlaneSensor2D: + return PlaneSensor2D_Create(); + case TAG_MPEG4_PointLight: + return PointLight_Create(); + case TAG_MPEG4_PointSet: + return PointSet_Create(); + case TAG_MPEG4_PointSet2D: + return PointSet2D_Create(); + case TAG_MPEG4_PositionInterpolator: + return PositionInterpolator_Create(); + case TAG_MPEG4_PositionInterpolator2D: + return PositionInterpolator2D_Create(); + case TAG_MPEG4_ProximitySensor2D: + return ProximitySensor2D_Create(); + case TAG_MPEG4_ProximitySensor: + return ProximitySensor_Create(); + case TAG_MPEG4_QuantizationParameter: + return QuantizationParameter_Create(); + case TAG_MPEG4_Rectangle: + return Rectangle_Create(); + case TAG_MPEG4_ScalarInterpolator: + return ScalarInterpolator_Create(); + case TAG_MPEG4_Script: + return Script_Create(); + case TAG_MPEG4_Shape: + return Shape_Create(); + case TAG_MPEG4_Sound: + return Sound_Create(); + case TAG_MPEG4_Sound2D: + return Sound2D_Create(); + case TAG_MPEG4_Sphere: + return Sphere_Create(); + case TAG_MPEG4_SphereSensor: + return SphereSensor_Create(); + case TAG_MPEG4_SpotLight: + return SpotLight_Create(); + case TAG_MPEG4_Switch: + return Switch_Create(); + case TAG_MPEG4_TermCap: + return TermCap_Create(); + case TAG_MPEG4_Text: + return Text_Create(); + case TAG_MPEG4_TextureCoordinate: + return TextureCoordinate_Create(); + case TAG_MPEG4_TextureTransform: + return TextureTransform_Create(); + case TAG_MPEG4_TimeSensor: + return TimeSensor_Create(); + case TAG_MPEG4_TouchSensor: + return TouchSensor_Create(); + case TAG_MPEG4_Transform: + return Transform_Create(); + case TAG_MPEG4_Transform2D: + return Transform2D_Create(); + case TAG_MPEG4_Valuator: + return Valuator_Create(); + case TAG_MPEG4_Viewpoint: + return Viewpoint_Create(); + case TAG_MPEG4_VisibilitySensor: + return VisibilitySensor_Create(); + case TAG_MPEG4_WorldInfo: + return WorldInfo_Create(); + case TAG_MPEG4_AcousticMaterial: + return AcousticMaterial_Create(); + case TAG_MPEG4_AcousticScene: + return AcousticScene_Create(); + case TAG_MPEG4_ApplicationWindow: + return ApplicationWindow_Create(); + case TAG_MPEG4_DirectiveSound: + return DirectiveSound_Create(); + case TAG_MPEG4_Hierarchical3DMesh: + return Hierarchical3DMesh_Create(); + case TAG_MPEG4_MaterialKey: + return MaterialKey_Create(); + case TAG_MPEG4_PerceptualParameters: + return PerceptualParameters_Create(); + case TAG_MPEG4_TemporalTransform: + return TemporalTransform_Create(); + case TAG_MPEG4_TemporalGroup: + return TemporalGroup_Create(); + case TAG_MPEG4_ServerCommand: + return ServerCommand_Create(); + case TAG_MPEG4_InputSensor: + return InputSensor_Create(); + case TAG_MPEG4_MatteTexture: + return MatteTexture_Create(); + case TAG_MPEG4_MediaBuffer: + return MediaBuffer_Create(); + case TAG_MPEG4_MediaControl: + return MediaControl_Create(); + case TAG_MPEG4_MediaSensor: + return MediaSensor_Create(); + case TAG_MPEG4_CoordinateInterpolator4D: + return CoordinateInterpolator4D_Create(); + case TAG_MPEG4_NonLinearDeformer: + return NonLinearDeformer_Create(); + case TAG_MPEG4_PositionAnimator: + return PositionAnimator_Create(); + case TAG_MPEG4_PositionAnimator2D: + return PositionAnimator2D_Create(); + case TAG_MPEG4_PositionInterpolator4D: + return PositionInterpolator4D_Create(); + case TAG_MPEG4_ScalarAnimator: + return ScalarAnimator_Create(); + case TAG_MPEG4_Clipper2D: + return Clipper2D_Create(); + case TAG_MPEG4_ColorTransform: + return ColorTransform_Create(); + case TAG_MPEG4_Ellipse: + return Ellipse_Create(); + case TAG_MPEG4_LinearGradient: + return LinearGradient_Create(); + case TAG_MPEG4_PathLayout: + return PathLayout_Create(); + case TAG_MPEG4_RadialGradient: + return RadialGradient_Create(); + case TAG_MPEG4_TransformMatrix2D: + return TransformMatrix2D_Create(); + case TAG_MPEG4_Viewport: + return Viewport_Create(); + case TAG_MPEG4_XCurve2D: + return XCurve2D_Create(); + case TAG_MPEG4_XFontStyle: + return XFontStyle_Create(); + case TAG_MPEG4_XLineProperties: + return XLineProperties_Create(); + default: + return NULL; + } +} + +const char *gf_sg_mpeg4_node_get_class_name(u32 NodeTag) +{ + switch (NodeTag) { + case TAG_MPEG4_Anchor: + return "Anchor"; + case TAG_MPEG4_AnimationStream: + return "AnimationStream"; + case TAG_MPEG4_Appearance: + return "Appearance"; + case TAG_MPEG4_AudioBuffer: + return "AudioBuffer"; + case TAG_MPEG4_AudioClip: + return "AudioClip"; + case TAG_MPEG4_AudioDelay: + return "AudioDelay"; + case TAG_MPEG4_AudioFX: + return "AudioFX"; + case TAG_MPEG4_AudioMix: + return "AudioMix"; + case TAG_MPEG4_AudioSource: + return "AudioSource"; + case TAG_MPEG4_AudioSwitch: + return "AudioSwitch"; + case TAG_MPEG4_Background: + return "Background"; + case TAG_MPEG4_Background2D: + return "Background2D"; + case TAG_MPEG4_Billboard: + return "Billboard"; + case TAG_MPEG4_Bitmap: + return "Bitmap"; + case TAG_MPEG4_Box: + return "Box"; + case TAG_MPEG4_Circle: + return "Circle"; + case TAG_MPEG4_Collision: + return "Collision"; + case TAG_MPEG4_Color: + return "Color"; + case TAG_MPEG4_ColorInterpolator: + return "ColorInterpolator"; + case TAG_MPEG4_CompositeTexture2D: + return "CompositeTexture2D"; + case TAG_MPEG4_CompositeTexture3D: + return "CompositeTexture3D"; + case TAG_MPEG4_Conditional: + return "Conditional"; + case TAG_MPEG4_Cone: + return "Cone"; + case TAG_MPEG4_Coordinate: + return "Coordinate"; + case TAG_MPEG4_Coordinate2D: + return "Coordinate2D"; + case TAG_MPEG4_CoordinateInterpolator: + return "CoordinateInterpolator"; + case TAG_MPEG4_CoordinateInterpolator2D: + return "CoordinateInterpolator2D"; + case TAG_MPEG4_Curve2D: + return "Curve2D"; + case TAG_MPEG4_Cylinder: + return "Cylinder"; + case TAG_MPEG4_CylinderSensor: + return "CylinderSensor"; + case TAG_MPEG4_DirectionalLight: + return "DirectionalLight"; + case TAG_MPEG4_DiscSensor: + return "DiscSensor"; + case TAG_MPEG4_ElevationGrid: + return "ElevationGrid"; + case TAG_MPEG4_Extrusion: + return "Extrusion"; + case TAG_MPEG4_Fog: + return "Fog"; + case TAG_MPEG4_FontStyle: + return "FontStyle"; + case TAG_MPEG4_Form: + return "Form"; + case TAG_MPEG4_Group: + return "Group"; + case TAG_MPEG4_ImageTexture: + return "ImageTexture"; + case TAG_MPEG4_IndexedFaceSet: + return "IndexedFaceSet"; + case TAG_MPEG4_IndexedFaceSet2D: + return "IndexedFaceSet2D"; + case TAG_MPEG4_IndexedLineSet: + return "IndexedLineSet"; + case TAG_MPEG4_IndexedLineSet2D: + return "IndexedLineSet2D"; + case TAG_MPEG4_Inline: + return "Inline"; + case TAG_MPEG4_LOD: + return "LOD"; + case TAG_MPEG4_Layer2D: + return "Layer2D"; + case TAG_MPEG4_Layer3D: + return "Layer3D"; + case TAG_MPEG4_Layout: + return "Layout"; + case TAG_MPEG4_LineProperties: + return "LineProperties"; + case TAG_MPEG4_ListeningPoint: + return "ListeningPoint"; + case TAG_MPEG4_Material: + return "Material"; + case TAG_MPEG4_Material2D: + return "Material2D"; + case TAG_MPEG4_MovieTexture: + return "MovieTexture"; + case TAG_MPEG4_NavigationInfo: + return "NavigationInfo"; + case TAG_MPEG4_Normal: + return "Normal"; + case TAG_MPEG4_NormalInterpolator: + return "NormalInterpolator"; + case TAG_MPEG4_OrderedGroup: + return "OrderedGroup"; + case TAG_MPEG4_OrientationInterpolator: + return "OrientationInterpolator"; + case TAG_MPEG4_PixelTexture: + return "PixelTexture"; + case TAG_MPEG4_PlaneSensor: + return "PlaneSensor"; + case TAG_MPEG4_PlaneSensor2D: + return "PlaneSensor2D"; + case TAG_MPEG4_PointLight: + return "PointLight"; + case TAG_MPEG4_PointSet: + return "PointSet"; + case TAG_MPEG4_PointSet2D: + return "PointSet2D"; + case TAG_MPEG4_PositionInterpolator: + return "PositionInterpolator"; + case TAG_MPEG4_PositionInterpolator2D: + return "PositionInterpolator2D"; + case TAG_MPEG4_ProximitySensor2D: + return "ProximitySensor2D"; + case TAG_MPEG4_ProximitySensor: + return "ProximitySensor"; + case TAG_MPEG4_QuantizationParameter: + return "QuantizationParameter"; + case TAG_MPEG4_Rectangle: + return "Rectangle"; + case TAG_MPEG4_ScalarInterpolator: + return "ScalarInterpolator"; + case TAG_MPEG4_Script: + return "Script"; + case TAG_MPEG4_Shape: + return "Shape"; + case TAG_MPEG4_Sound: + return "Sound"; + case TAG_MPEG4_Sound2D: + return "Sound2D"; + case TAG_MPEG4_Sphere: + return "Sphere"; + case TAG_MPEG4_SphereSensor: + return "SphereSensor"; + case TAG_MPEG4_SpotLight: + return "SpotLight"; + case TAG_MPEG4_Switch: + return "Switch"; + case TAG_MPEG4_TermCap: + return "TermCap"; + case TAG_MPEG4_Text: + return "Text"; + case TAG_MPEG4_TextureCoordinate: + return "TextureCoordinate"; + case TAG_MPEG4_TextureTransform: + return "TextureTransform"; + case TAG_MPEG4_TimeSensor: + return "TimeSensor"; + case TAG_MPEG4_TouchSensor: + return "TouchSensor"; + case TAG_MPEG4_Transform: + return "Transform"; + case TAG_MPEG4_Transform2D: + return "Transform2D"; + case TAG_MPEG4_Valuator: + return "Valuator"; + case TAG_MPEG4_Viewpoint: + return "Viewpoint"; + case TAG_MPEG4_VisibilitySensor: + return "VisibilitySensor"; + case TAG_MPEG4_WorldInfo: + return "WorldInfo"; + case TAG_MPEG4_AcousticMaterial: + return "AcousticMaterial"; + case TAG_MPEG4_AcousticScene: + return "AcousticScene"; + case TAG_MPEG4_ApplicationWindow: + return "ApplicationWindow"; + case TAG_MPEG4_DirectiveSound: + return "DirectiveSound"; + case TAG_MPEG4_Hierarchical3DMesh: + return "Hierarchical3DMesh"; + case TAG_MPEG4_MaterialKey: + return "MaterialKey"; + case TAG_MPEG4_PerceptualParameters: + return "PerceptualParameters"; + case TAG_MPEG4_TemporalTransform: + return "TemporalTransform"; + case TAG_MPEG4_TemporalGroup: + return "TemporalGroup"; + case TAG_MPEG4_ServerCommand: + return "ServerCommand"; + case TAG_MPEG4_InputSensor: + return "InputSensor"; + case TAG_MPEG4_MatteTexture: + return "MatteTexture"; + case TAG_MPEG4_MediaBuffer: + return "MediaBuffer"; + case TAG_MPEG4_MediaControl: + return "MediaControl"; + case TAG_MPEG4_MediaSensor: + return "MediaSensor"; + case TAG_MPEG4_CoordinateInterpolator4D: + return "CoordinateInterpolator4D"; + case TAG_MPEG4_NonLinearDeformer: + return "NonLinearDeformer"; + case TAG_MPEG4_PositionAnimator: + return "PositionAnimator"; + case TAG_MPEG4_PositionAnimator2D: + return "PositionAnimator2D"; + case TAG_MPEG4_PositionInterpolator4D: + return "PositionInterpolator4D"; + case TAG_MPEG4_ScalarAnimator: + return "ScalarAnimator"; + case TAG_MPEG4_Clipper2D: + return "Clipper2D"; + case TAG_MPEG4_ColorTransform: + return "ColorTransform"; + case TAG_MPEG4_Ellipse: + return "Ellipse"; + case TAG_MPEG4_LinearGradient: + return "LinearGradient"; + case TAG_MPEG4_PathLayout: + return "PathLayout"; + case TAG_MPEG4_RadialGradient: + return "RadialGradient"; + case TAG_MPEG4_TransformMatrix2D: + return "TransformMatrix2D"; + case TAG_MPEG4_Viewport: + return "Viewport"; + case TAG_MPEG4_XCurve2D: + return "XCurve2D"; + case TAG_MPEG4_XFontStyle: + return "XFontStyle"; + case TAG_MPEG4_XLineProperties: + return "XLineProperties"; + default: + return "Unknown Node"; + } +} + +void gf_sg_mpeg4_node_del(GF_Node *node) +{ + switch (node->sgprivate->tag) { + case TAG_MPEG4_Anchor: + Anchor_Del(node); return; + case TAG_MPEG4_AnimationStream: + AnimationStream_Del(node); return; + case TAG_MPEG4_Appearance: + Appearance_Del(node); return; + case TAG_MPEG4_AudioBuffer: + AudioBuffer_Del(node); return; + case TAG_MPEG4_AudioClip: + AudioClip_Del(node); return; + case TAG_MPEG4_AudioDelay: + AudioDelay_Del(node); return; + case TAG_MPEG4_AudioFX: + AudioFX_Del(node); return; + case TAG_MPEG4_AudioMix: + AudioMix_Del(node); return; + case TAG_MPEG4_AudioSource: + AudioSource_Del(node); return; + case TAG_MPEG4_AudioSwitch: + AudioSwitch_Del(node); return; + case TAG_MPEG4_Background: + Background_Del(node); return; + case TAG_MPEG4_Background2D: + Background2D_Del(node); return; + case TAG_MPEG4_Billboard: + Billboard_Del(node); return; + case TAG_MPEG4_Bitmap: + Bitmap_Del(node); return; + case TAG_MPEG4_Box: + Box_Del(node); return; + case TAG_MPEG4_Circle: + Circle_Del(node); return; + case TAG_MPEG4_Collision: + Collision_Del(node); return; + case TAG_MPEG4_Color: + Color_Del(node); return; + case TAG_MPEG4_ColorInterpolator: + ColorInterpolator_Del(node); return; + case TAG_MPEG4_CompositeTexture2D: + CompositeTexture2D_Del(node); return; + case TAG_MPEG4_CompositeTexture3D: + CompositeTexture3D_Del(node); return; + case TAG_MPEG4_Conditional: + Conditional_Del(node); return; + case TAG_MPEG4_Cone: + Cone_Del(node); return; + case TAG_MPEG4_Coordinate: + Coordinate_Del(node); return; + case TAG_MPEG4_Coordinate2D: + Coordinate2D_Del(node); return; + case TAG_MPEG4_CoordinateInterpolator: + CoordinateInterpolator_Del(node); return; + case TAG_MPEG4_CoordinateInterpolator2D: + CoordinateInterpolator2D_Del(node); return; + case TAG_MPEG4_Curve2D: + Curve2D_Del(node); return; + case TAG_MPEG4_Cylinder: + Cylinder_Del(node); return; + case TAG_MPEG4_CylinderSensor: + CylinderSensor_Del(node); return; + case TAG_MPEG4_DirectionalLight: + DirectionalLight_Del(node); return; + case TAG_MPEG4_DiscSensor: + DiscSensor_Del(node); return; + case TAG_MPEG4_ElevationGrid: + ElevationGrid_Del(node); return; + case TAG_MPEG4_Extrusion: + Extrusion_Del(node); return; + case TAG_MPEG4_Fog: + Fog_Del(node); return; + case TAG_MPEG4_FontStyle: + FontStyle_Del(node); return; + case TAG_MPEG4_Form: + Form_Del(node); return; + case TAG_MPEG4_Group: + Group_Del(node); return; + case TAG_MPEG4_ImageTexture: + ImageTexture_Del(node); return; + case TAG_MPEG4_IndexedFaceSet: + IndexedFaceSet_Del(node); return; + case TAG_MPEG4_IndexedFaceSet2D: + IndexedFaceSet2D_Del(node); return; + case TAG_MPEG4_IndexedLineSet: + IndexedLineSet_Del(node); return; + case TAG_MPEG4_IndexedLineSet2D: + IndexedLineSet2D_Del(node); return; + case TAG_MPEG4_Inline: + Inline_Del(node); return; + case TAG_MPEG4_LOD: + LOD_Del(node); return; + case TAG_MPEG4_Layer2D: + Layer2D_Del(node); return; + case TAG_MPEG4_Layer3D: + Layer3D_Del(node); return; + case TAG_MPEG4_Layout: + Layout_Del(node); return; + case TAG_MPEG4_LineProperties: + LineProperties_Del(node); return; + case TAG_MPEG4_ListeningPoint: + ListeningPoint_Del(node); return; + case TAG_MPEG4_Material: + Material_Del(node); return; + case TAG_MPEG4_Material2D: + Material2D_Del(node); return; + case TAG_MPEG4_MovieTexture: + MovieTexture_Del(node); return; + case TAG_MPEG4_NavigationInfo: + NavigationInfo_Del(node); return; + case TAG_MPEG4_Normal: + Normal_Del(node); return; + case TAG_MPEG4_NormalInterpolator: + NormalInterpolator_Del(node); return; + case TAG_MPEG4_OrderedGroup: + OrderedGroup_Del(node); return; + case TAG_MPEG4_OrientationInterpolator: + OrientationInterpolator_Del(node); return; + case TAG_MPEG4_PixelTexture: + PixelTexture_Del(node); return; + case TAG_MPEG4_PlaneSensor: + PlaneSensor_Del(node); return; + case TAG_MPEG4_PlaneSensor2D: + PlaneSensor2D_Del(node); return; + case TAG_MPEG4_PointLight: + PointLight_Del(node); return; + case TAG_MPEG4_PointSet: + PointSet_Del(node); return; + case TAG_MPEG4_PointSet2D: + PointSet2D_Del(node); return; + case TAG_MPEG4_PositionInterpolator: + PositionInterpolator_Del(node); return; + case TAG_MPEG4_PositionInterpolator2D: + PositionInterpolator2D_Del(node); return; + case TAG_MPEG4_ProximitySensor2D: + ProximitySensor2D_Del(node); return; + case TAG_MPEG4_ProximitySensor: + ProximitySensor_Del(node); return; + case TAG_MPEG4_QuantizationParameter: + QuantizationParameter_Del(node); return; + case TAG_MPEG4_Rectangle: + Rectangle_Del(node); return; + case TAG_MPEG4_ScalarInterpolator: + ScalarInterpolator_Del(node); return; + case TAG_MPEG4_Script: + Script_Del(node); return; + case TAG_MPEG4_Shape: + Shape_Del(node); return; + case TAG_MPEG4_Sound: + Sound_Del(node); return; + case TAG_MPEG4_Sound2D: + Sound2D_Del(node); return; + case TAG_MPEG4_Sphere: + Sphere_Del(node); return; + case TAG_MPEG4_SphereSensor: + SphereSensor_Del(node); return; + case TAG_MPEG4_SpotLight: + SpotLight_Del(node); return; + case TAG_MPEG4_Switch: + Switch_Del(node); return; + case TAG_MPEG4_TermCap: + TermCap_Del(node); return; + case TAG_MPEG4_Text: + Text_Del(node); return; + case TAG_MPEG4_TextureCoordinate: + TextureCoordinate_Del(node); return; + case TAG_MPEG4_TextureTransform: + TextureTransform_Del(node); return; + case TAG_MPEG4_TimeSensor: + TimeSensor_Del(node); return; + case TAG_MPEG4_TouchSensor: + TouchSensor_Del(node); return; + case TAG_MPEG4_Transform: + Transform_Del(node); return; + case TAG_MPEG4_Transform2D: + Transform2D_Del(node); return; + case TAG_MPEG4_Valuator: + Valuator_Del(node); return; + case TAG_MPEG4_Viewpoint: + Viewpoint_Del(node); return; + case TAG_MPEG4_VisibilitySensor: + VisibilitySensor_Del(node); return; + case TAG_MPEG4_WorldInfo: + WorldInfo_Del(node); return; + case TAG_MPEG4_AcousticMaterial: + AcousticMaterial_Del(node); return; + case TAG_MPEG4_AcousticScene: + AcousticScene_Del(node); return; + case TAG_MPEG4_ApplicationWindow: + ApplicationWindow_Del(node); return; + case TAG_MPEG4_DirectiveSound: + DirectiveSound_Del(node); return; + case TAG_MPEG4_Hierarchical3DMesh: + Hierarchical3DMesh_Del(node); return; + case TAG_MPEG4_MaterialKey: + MaterialKey_Del(node); return; + case TAG_MPEG4_PerceptualParameters: + PerceptualParameters_Del(node); return; + case TAG_MPEG4_TemporalTransform: + TemporalTransform_Del(node); return; + case TAG_MPEG4_TemporalGroup: + TemporalGroup_Del(node); return; + case TAG_MPEG4_ServerCommand: + ServerCommand_Del(node); return; + case TAG_MPEG4_InputSensor: + InputSensor_Del(node); return; + case TAG_MPEG4_MatteTexture: + MatteTexture_Del(node); return; + case TAG_MPEG4_MediaBuffer: + MediaBuffer_Del(node); return; + case TAG_MPEG4_MediaControl: + MediaControl_Del(node); return; + case TAG_MPEG4_MediaSensor: + MediaSensor_Del(node); return; + case TAG_MPEG4_CoordinateInterpolator4D: + CoordinateInterpolator4D_Del(node); return; + case TAG_MPEG4_NonLinearDeformer: + NonLinearDeformer_Del(node); return; + case TAG_MPEG4_PositionAnimator: + PositionAnimator_Del(node); return; + case TAG_MPEG4_PositionAnimator2D: + PositionAnimator2D_Del(node); return; + case TAG_MPEG4_PositionInterpolator4D: + PositionInterpolator4D_Del(node); return; + case TAG_MPEG4_ScalarAnimator: + ScalarAnimator_Del(node); return; + case TAG_MPEG4_Clipper2D: + Clipper2D_Del(node); return; + case TAG_MPEG4_ColorTransform: + ColorTransform_Del(node); return; + case TAG_MPEG4_Ellipse: + Ellipse_Del(node); return; + case TAG_MPEG4_LinearGradient: + LinearGradient_Del(node); return; + case TAG_MPEG4_PathLayout: + PathLayout_Del(node); return; + case TAG_MPEG4_RadialGradient: + RadialGradient_Del(node); return; + case TAG_MPEG4_TransformMatrix2D: + TransformMatrix2D_Del(node); return; + case TAG_MPEG4_Viewport: + Viewport_Del(node); return; + case TAG_MPEG4_XCurve2D: + XCurve2D_Del(node); return; + case TAG_MPEG4_XFontStyle: + XFontStyle_Del(node); return; + case TAG_MPEG4_XLineProperties: + XLineProperties_Del(node); return; + default: + return; + } +} + +u32 gf_sg_mpeg4_node_get_field_count(GF_Node *node, u8 code_mode) +{ + switch (node->sgprivate->tag) { + case TAG_MPEG4_Anchor:return Anchor_get_field_count(node, code_mode); + case TAG_MPEG4_AnimationStream:return AnimationStream_get_field_count(node, code_mode); + case TAG_MPEG4_Appearance:return Appearance_get_field_count(node, code_mode); + case TAG_MPEG4_AudioBuffer:return AudioBuffer_get_field_count(node, code_mode); + case TAG_MPEG4_AudioClip:return AudioClip_get_field_count(node, code_mode); + case TAG_MPEG4_AudioDelay:return AudioDelay_get_field_count(node, code_mode); + case TAG_MPEG4_AudioFX:return AudioFX_get_field_count(node, code_mode); + case TAG_MPEG4_AudioMix:return AudioMix_get_field_count(node, code_mode); + case TAG_MPEG4_AudioSource:return AudioSource_get_field_count(node, code_mode); + case TAG_MPEG4_AudioSwitch:return AudioSwitch_get_field_count(node, code_mode); + case TAG_MPEG4_Background:return Background_get_field_count(node, code_mode); + case TAG_MPEG4_Background2D:return Background2D_get_field_count(node, code_mode); + case TAG_MPEG4_Billboard:return Billboard_get_field_count(node, code_mode); + case TAG_MPEG4_Bitmap:return Bitmap_get_field_count(node, code_mode); + case TAG_MPEG4_Box:return Box_get_field_count(node, code_mode); + case TAG_MPEG4_Circle:return Circle_get_field_count(node, code_mode); + case TAG_MPEG4_Collision:return Collision_get_field_count(node, code_mode); + case TAG_MPEG4_Color:return Color_get_field_count(node, code_mode); + case TAG_MPEG4_ColorInterpolator:return ColorInterpolator_get_field_count(node, code_mode); + case TAG_MPEG4_CompositeTexture2D:return CompositeTexture2D_get_field_count(node, code_mode); + case TAG_MPEG4_CompositeTexture3D:return CompositeTexture3D_get_field_count(node, code_mode); + case TAG_MPEG4_Conditional:return Conditional_get_field_count(node, code_mode); + case TAG_MPEG4_Cone:return Cone_get_field_count(node, code_mode); + case TAG_MPEG4_Coordinate:return Coordinate_get_field_count(node, code_mode); + case TAG_MPEG4_Coordinate2D:return Coordinate2D_get_field_count(node, code_mode); + case TAG_MPEG4_CoordinateInterpolator:return CoordinateInterpolator_get_field_count(node, code_mode); + case TAG_MPEG4_CoordinateInterpolator2D:return CoordinateInterpolator2D_get_field_count(node, code_mode); + case TAG_MPEG4_Curve2D:return Curve2D_get_field_count(node, code_mode); + case TAG_MPEG4_Cylinder:return Cylinder_get_field_count(node, code_mode); + case TAG_MPEG4_CylinderSensor:return CylinderSensor_get_field_count(node, code_mode); + case TAG_MPEG4_DirectionalLight:return DirectionalLight_get_field_count(node, code_mode); + case TAG_MPEG4_DiscSensor:return DiscSensor_get_field_count(node, code_mode); + case TAG_MPEG4_ElevationGrid:return ElevationGrid_get_field_count(node, code_mode); + case TAG_MPEG4_Extrusion:return Extrusion_get_field_count(node, code_mode); + case TAG_MPEG4_Fog:return Fog_get_field_count(node, code_mode); + case TAG_MPEG4_FontStyle:return FontStyle_get_field_count(node, code_mode); + case TAG_MPEG4_Form:return Form_get_field_count(node, code_mode); + case TAG_MPEG4_Group:return Group_get_field_count(node, code_mode); + case TAG_MPEG4_ImageTexture:return ImageTexture_get_field_count(node, code_mode); + case TAG_MPEG4_IndexedFaceSet:return IndexedFaceSet_get_field_count(node, code_mode); + case TAG_MPEG4_IndexedFaceSet2D:return IndexedFaceSet2D_get_field_count(node, code_mode); + case TAG_MPEG4_IndexedLineSet:return IndexedLineSet_get_field_count(node, code_mode); + case TAG_MPEG4_IndexedLineSet2D:return IndexedLineSet2D_get_field_count(node, code_mode); + case TAG_MPEG4_Inline:return Inline_get_field_count(node, code_mode); + case TAG_MPEG4_LOD:return LOD_get_field_count(node, code_mode); + case TAG_MPEG4_Layer2D:return Layer2D_get_field_count(node, code_mode); + case TAG_MPEG4_Layer3D:return Layer3D_get_field_count(node, code_mode); + case TAG_MPEG4_Layout:return Layout_get_field_count(node, code_mode); + case TAG_MPEG4_LineProperties:return LineProperties_get_field_count(node, code_mode); + case TAG_MPEG4_ListeningPoint:return ListeningPoint_get_field_count(node, code_mode); + case TAG_MPEG4_Material:return Material_get_field_count(node, code_mode); + case TAG_MPEG4_Material2D:return Material2D_get_field_count(node, code_mode); + case TAG_MPEG4_MovieTexture:return MovieTexture_get_field_count(node, code_mode); + case TAG_MPEG4_NavigationInfo:return NavigationInfo_get_field_count(node, code_mode); + case TAG_MPEG4_Normal:return Normal_get_field_count(node, code_mode); + case TAG_MPEG4_NormalInterpolator:return NormalInterpolator_get_field_count(node, code_mode); + case TAG_MPEG4_OrderedGroup:return OrderedGroup_get_field_count(node, code_mode); + case TAG_MPEG4_OrientationInterpolator:return OrientationInterpolator_get_field_count(node, code_mode); + case TAG_MPEG4_PixelTexture:return PixelTexture_get_field_count(node, code_mode); + case TAG_MPEG4_PlaneSensor:return PlaneSensor_get_field_count(node, code_mode); + case TAG_MPEG4_PlaneSensor2D:return PlaneSensor2D_get_field_count(node, code_mode); + case TAG_MPEG4_PointLight:return PointLight_get_field_count(node, code_mode); + case TAG_MPEG4_PointSet:return PointSet_get_field_count(node, code_mode); + case TAG_MPEG4_PointSet2D:return PointSet2D_get_field_count(node, code_mode); + case TAG_MPEG4_PositionInterpolator:return PositionInterpolator_get_field_count(node, code_mode); + case TAG_MPEG4_PositionInterpolator2D:return PositionInterpolator2D_get_field_count(node, code_mode); + case TAG_MPEG4_ProximitySensor2D:return ProximitySensor2D_get_field_count(node, code_mode); + case TAG_MPEG4_ProximitySensor:return ProximitySensor_get_field_count(node, code_mode); + case TAG_MPEG4_QuantizationParameter:return QuantizationParameter_get_field_count(node, code_mode); + case TAG_MPEG4_Rectangle:return Rectangle_get_field_count(node, code_mode); + case TAG_MPEG4_ScalarInterpolator:return ScalarInterpolator_get_field_count(node, code_mode); + case TAG_MPEG4_Script:return Script_get_field_count(node, code_mode); + case TAG_MPEG4_Shape:return Shape_get_field_count(node, code_mode); + case TAG_MPEG4_Sound:return Sound_get_field_count(node, code_mode); + case TAG_MPEG4_Sound2D:return Sound2D_get_field_count(node, code_mode); + case TAG_MPEG4_Sphere:return Sphere_get_field_count(node, code_mode); + case TAG_MPEG4_SphereSensor:return SphereSensor_get_field_count(node, code_mode); + case TAG_MPEG4_SpotLight:return SpotLight_get_field_count(node, code_mode); + case TAG_MPEG4_Switch:return Switch_get_field_count(node, code_mode); + case TAG_MPEG4_TermCap:return TermCap_get_field_count(node, code_mode); + case TAG_MPEG4_Text:return Text_get_field_count(node, code_mode); + case TAG_MPEG4_TextureCoordinate:return TextureCoordinate_get_field_count(node, code_mode); + case TAG_MPEG4_TextureTransform:return TextureTransform_get_field_count(node, code_mode); + case TAG_MPEG4_TimeSensor:return TimeSensor_get_field_count(node, code_mode); + case TAG_MPEG4_TouchSensor:return TouchSensor_get_field_count(node, code_mode); + case TAG_MPEG4_Transform:return Transform_get_field_count(node, code_mode); + case TAG_MPEG4_Transform2D:return Transform2D_get_field_count(node, code_mode); + case TAG_MPEG4_Valuator:return Valuator_get_field_count(node, code_mode); + case TAG_MPEG4_Viewpoint:return Viewpoint_get_field_count(node, code_mode); + case TAG_MPEG4_VisibilitySensor:return VisibilitySensor_get_field_count(node, code_mode); + case TAG_MPEG4_WorldInfo:return WorldInfo_get_field_count(node, code_mode); + case TAG_MPEG4_AcousticMaterial:return AcousticMaterial_get_field_count(node, code_mode); + case TAG_MPEG4_AcousticScene:return AcousticScene_get_field_count(node, code_mode); + case TAG_MPEG4_ApplicationWindow:return ApplicationWindow_get_field_count(node, code_mode); + case TAG_MPEG4_DirectiveSound:return DirectiveSound_get_field_count(node, code_mode); + case TAG_MPEG4_Hierarchical3DMesh:return Hierarchical3DMesh_get_field_count(node, code_mode); + case TAG_MPEG4_MaterialKey:return MaterialKey_get_field_count(node, code_mode); + case TAG_MPEG4_PerceptualParameters:return PerceptualParameters_get_field_count(node, code_mode); + case TAG_MPEG4_TemporalTransform:return TemporalTransform_get_field_count(node, code_mode); + case TAG_MPEG4_TemporalGroup:return TemporalGroup_get_field_count(node, code_mode); + case TAG_MPEG4_ServerCommand:return ServerCommand_get_field_count(node, code_mode); + case TAG_MPEG4_InputSensor:return InputSensor_get_field_count(node, code_mode); + case TAG_MPEG4_MatteTexture:return MatteTexture_get_field_count(node, code_mode); + case TAG_MPEG4_MediaBuffer:return MediaBuffer_get_field_count(node, code_mode); + case TAG_MPEG4_MediaControl:return MediaControl_get_field_count(node, code_mode); + case TAG_MPEG4_MediaSensor:return MediaSensor_get_field_count(node, code_mode); + case TAG_MPEG4_CoordinateInterpolator4D:return CoordinateInterpolator4D_get_field_count(node, code_mode); + case TAG_MPEG4_NonLinearDeformer:return NonLinearDeformer_get_field_count(node, code_mode); + case TAG_MPEG4_PositionAnimator:return PositionAnimator_get_field_count(node, code_mode); + case TAG_MPEG4_PositionAnimator2D:return PositionAnimator2D_get_field_count(node, code_mode); + case TAG_MPEG4_PositionInterpolator4D:return PositionInterpolator4D_get_field_count(node, code_mode); + case TAG_MPEG4_ScalarAnimator:return ScalarAnimator_get_field_count(node, code_mode); + case TAG_MPEG4_Clipper2D:return Clipper2D_get_field_count(node, code_mode); + case TAG_MPEG4_ColorTransform:return ColorTransform_get_field_count(node, code_mode); + case TAG_MPEG4_Ellipse:return Ellipse_get_field_count(node, code_mode); + case TAG_MPEG4_LinearGradient:return LinearGradient_get_field_count(node, code_mode); + case TAG_MPEG4_PathLayout:return PathLayout_get_field_count(node, code_mode); + case TAG_MPEG4_RadialGradient:return RadialGradient_get_field_count(node, code_mode); + case TAG_MPEG4_TransformMatrix2D:return TransformMatrix2D_get_field_count(node, code_mode); + case TAG_MPEG4_Viewport:return Viewport_get_field_count(node, code_mode); + case TAG_MPEG4_XCurve2D:return XCurve2D_get_field_count(node, code_mode); + case TAG_MPEG4_XFontStyle:return XFontStyle_get_field_count(node, code_mode); + case TAG_MPEG4_XLineProperties:return XLineProperties_get_field_count(node, code_mode); + default: + return 0; + } +} + +GF_Err gf_sg_mpeg4_node_get_field(GF_Node *node, GF_FieldInfo *field) +{ + switch (node->sgprivate->tag) { + case TAG_MPEG4_Anchor: return Anchor_get_field(node, field); + case TAG_MPEG4_AnimationStream: return AnimationStream_get_field(node, field); + case TAG_MPEG4_Appearance: return Appearance_get_field(node, field); + case TAG_MPEG4_AudioBuffer: return AudioBuffer_get_field(node, field); + case TAG_MPEG4_AudioClip: return AudioClip_get_field(node, field); + case TAG_MPEG4_AudioDelay: return AudioDelay_get_field(node, field); + case TAG_MPEG4_AudioFX: return AudioFX_get_field(node, field); + case TAG_MPEG4_AudioMix: return AudioMix_get_field(node, field); + case TAG_MPEG4_AudioSource: return AudioSource_get_field(node, field); + case TAG_MPEG4_AudioSwitch: return AudioSwitch_get_field(node, field); + case TAG_MPEG4_Background: return Background_get_field(node, field); + case TAG_MPEG4_Background2D: return Background2D_get_field(node, field); + case TAG_MPEG4_Billboard: return Billboard_get_field(node, field); + case TAG_MPEG4_Bitmap: return Bitmap_get_field(node, field); + case TAG_MPEG4_Box: return Box_get_field(node, field); + case TAG_MPEG4_Circle: return Circle_get_field(node, field); + case TAG_MPEG4_Collision: return Collision_get_field(node, field); + case TAG_MPEG4_Color: return Color_get_field(node, field); + case TAG_MPEG4_ColorInterpolator: return ColorInterpolator_get_field(node, field); + case TAG_MPEG4_CompositeTexture2D: return CompositeTexture2D_get_field(node, field); + case TAG_MPEG4_CompositeTexture3D: return CompositeTexture3D_get_field(node, field); + case TAG_MPEG4_Conditional: return Conditional_get_field(node, field); + case TAG_MPEG4_Cone: return Cone_get_field(node, field); + case TAG_MPEG4_Coordinate: return Coordinate_get_field(node, field); + case TAG_MPEG4_Coordinate2D: return Coordinate2D_get_field(node, field); + case TAG_MPEG4_CoordinateInterpolator: return CoordinateInterpolator_get_field(node, field); + case TAG_MPEG4_CoordinateInterpolator2D: return CoordinateInterpolator2D_get_field(node, field); + case TAG_MPEG4_Curve2D: return Curve2D_get_field(node, field); + case TAG_MPEG4_Cylinder: return Cylinder_get_field(node, field); + case TAG_MPEG4_CylinderSensor: return CylinderSensor_get_field(node, field); + case TAG_MPEG4_DirectionalLight: return DirectionalLight_get_field(node, field); + case TAG_MPEG4_DiscSensor: return DiscSensor_get_field(node, field); + case TAG_MPEG4_ElevationGrid: return ElevationGrid_get_field(node, field); + case TAG_MPEG4_Extrusion: return Extrusion_get_field(node, field); + case TAG_MPEG4_Fog: return Fog_get_field(node, field); + case TAG_MPEG4_FontStyle: return FontStyle_get_field(node, field); + case TAG_MPEG4_Form: return Form_get_field(node, field); + case TAG_MPEG4_Group: return Group_get_field(node, field); + case TAG_MPEG4_ImageTexture: return ImageTexture_get_field(node, field); + case TAG_MPEG4_IndexedFaceSet: return IndexedFaceSet_get_field(node, field); + case TAG_MPEG4_IndexedFaceSet2D: return IndexedFaceSet2D_get_field(node, field); + case TAG_MPEG4_IndexedLineSet: return IndexedLineSet_get_field(node, field); + case TAG_MPEG4_IndexedLineSet2D: return IndexedLineSet2D_get_field(node, field); + case TAG_MPEG4_Inline: return Inline_get_field(node, field); + case TAG_MPEG4_LOD: return LOD_get_field(node, field); + case TAG_MPEG4_Layer2D: return Layer2D_get_field(node, field); + case TAG_MPEG4_Layer3D: return Layer3D_get_field(node, field); + case TAG_MPEG4_Layout: return Layout_get_field(node, field); + case TAG_MPEG4_LineProperties: return LineProperties_get_field(node, field); + case TAG_MPEG4_ListeningPoint: return ListeningPoint_get_field(node, field); + case TAG_MPEG4_Material: return Material_get_field(node, field); + case TAG_MPEG4_Material2D: return Material2D_get_field(node, field); + case TAG_MPEG4_MovieTexture: return MovieTexture_get_field(node, field); + case TAG_MPEG4_NavigationInfo: return NavigationInfo_get_field(node, field); + case TAG_MPEG4_Normal: return Normal_get_field(node, field); + case TAG_MPEG4_NormalInterpolator: return NormalInterpolator_get_field(node, field); + case TAG_MPEG4_OrderedGroup: return OrderedGroup_get_field(node, field); + case TAG_MPEG4_OrientationInterpolator: return OrientationInterpolator_get_field(node, field); + case TAG_MPEG4_PixelTexture: return PixelTexture_get_field(node, field); + case TAG_MPEG4_PlaneSensor: return PlaneSensor_get_field(node, field); + case TAG_MPEG4_PlaneSensor2D: return PlaneSensor2D_get_field(node, field); + case TAG_MPEG4_PointLight: return PointLight_get_field(node, field); + case TAG_MPEG4_PointSet: return PointSet_get_field(node, field); + case TAG_MPEG4_PointSet2D: return PointSet2D_get_field(node, field); + case TAG_MPEG4_PositionInterpolator: return PositionInterpolator_get_field(node, field); + case TAG_MPEG4_PositionInterpolator2D: return PositionInterpolator2D_get_field(node, field); + case TAG_MPEG4_ProximitySensor2D: return ProximitySensor2D_get_field(node, field); + case TAG_MPEG4_ProximitySensor: return ProximitySensor_get_field(node, field); + case TAG_MPEG4_QuantizationParameter: return QuantizationParameter_get_field(node, field); + case TAG_MPEG4_Rectangle: return Rectangle_get_field(node, field); + case TAG_MPEG4_ScalarInterpolator: return ScalarInterpolator_get_field(node, field); + case TAG_MPEG4_Script: return Script_get_field(node, field); + case TAG_MPEG4_Shape: return Shape_get_field(node, field); + case TAG_MPEG4_Sound: return Sound_get_field(node, field); + case TAG_MPEG4_Sound2D: return Sound2D_get_field(node, field); + case TAG_MPEG4_Sphere: return Sphere_get_field(node, field); + case TAG_MPEG4_SphereSensor: return SphereSensor_get_field(node, field); + case TAG_MPEG4_SpotLight: return SpotLight_get_field(node, field); + case TAG_MPEG4_Switch: return Switch_get_field(node, field); + case TAG_MPEG4_TermCap: return TermCap_get_field(node, field); + case TAG_MPEG4_Text: return Text_get_field(node, field); + case TAG_MPEG4_TextureCoordinate: return TextureCoordinate_get_field(node, field); + case TAG_MPEG4_TextureTransform: return TextureTransform_get_field(node, field); + case TAG_MPEG4_TimeSensor: return TimeSensor_get_field(node, field); + case TAG_MPEG4_TouchSensor: return TouchSensor_get_field(node, field); + case TAG_MPEG4_Transform: return Transform_get_field(node, field); + case TAG_MPEG4_Transform2D: return Transform2D_get_field(node, field); + case TAG_MPEG4_Valuator: return Valuator_get_field(node, field); + case TAG_MPEG4_Viewpoint: return Viewpoint_get_field(node, field); + case TAG_MPEG4_VisibilitySensor: return VisibilitySensor_get_field(node, field); + case TAG_MPEG4_WorldInfo: return WorldInfo_get_field(node, field); + case TAG_MPEG4_AcousticMaterial: return AcousticMaterial_get_field(node, field); + case TAG_MPEG4_AcousticScene: return AcousticScene_get_field(node, field); + case TAG_MPEG4_ApplicationWindow: return ApplicationWindow_get_field(node, field); + case TAG_MPEG4_DirectiveSound: return DirectiveSound_get_field(node, field); + case TAG_MPEG4_Hierarchical3DMesh: return Hierarchical3DMesh_get_field(node, field); + case TAG_MPEG4_MaterialKey: return MaterialKey_get_field(node, field); + case TAG_MPEG4_PerceptualParameters: return PerceptualParameters_get_field(node, field); + case TAG_MPEG4_TemporalTransform: return TemporalTransform_get_field(node, field); + case TAG_MPEG4_TemporalGroup: return TemporalGroup_get_field(node, field); + case TAG_MPEG4_ServerCommand: return ServerCommand_get_field(node, field); + case TAG_MPEG4_InputSensor: return InputSensor_get_field(node, field); + case TAG_MPEG4_MatteTexture: return MatteTexture_get_field(node, field); + case TAG_MPEG4_MediaBuffer: return MediaBuffer_get_field(node, field); + case TAG_MPEG4_MediaControl: return MediaControl_get_field(node, field); + case TAG_MPEG4_MediaSensor: return MediaSensor_get_field(node, field); + case TAG_MPEG4_CoordinateInterpolator4D: return CoordinateInterpolator4D_get_field(node, field); + case TAG_MPEG4_NonLinearDeformer: return NonLinearDeformer_get_field(node, field); + case TAG_MPEG4_PositionAnimator: return PositionAnimator_get_field(node, field); + case TAG_MPEG4_PositionAnimator2D: return PositionAnimator2D_get_field(node, field); + case TAG_MPEG4_PositionInterpolator4D: return PositionInterpolator4D_get_field(node, field); + case TAG_MPEG4_ScalarAnimator: return ScalarAnimator_get_field(node, field); + case TAG_MPEG4_Clipper2D: return Clipper2D_get_field(node, field); + case TAG_MPEG4_ColorTransform: return ColorTransform_get_field(node, field); + case TAG_MPEG4_Ellipse: return Ellipse_get_field(node, field); + case TAG_MPEG4_LinearGradient: return LinearGradient_get_field(node, field); + case TAG_MPEG4_PathLayout: return PathLayout_get_field(node, field); + case TAG_MPEG4_RadialGradient: return RadialGradient_get_field(node, field); + case TAG_MPEG4_TransformMatrix2D: return TransformMatrix2D_get_field(node, field); + case TAG_MPEG4_Viewport: return Viewport_get_field(node, field); + case TAG_MPEG4_XCurve2D: return XCurve2D_get_field(node, field); + case TAG_MPEG4_XFontStyle: return XFontStyle_get_field(node, field); + case TAG_MPEG4_XLineProperties: return XLineProperties_get_field(node, field); + default: + return GF_BAD_PARAM; + } +} + +GF_Err gf_sg_mpeg4_node_get_field_index(GF_Node *node, u32 inField, u8 code_mode, u32 *fieldIndex) +{ + switch (node->sgprivate->tag) { + case TAG_MPEG4_Anchor: return Anchor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_AnimationStream: return AnimationStream_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Appearance: return Appearance_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_AudioBuffer: return AudioBuffer_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_AudioClip: return AudioClip_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_AudioDelay: return AudioDelay_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_AudioFX: return AudioFX_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_AudioMix: return AudioMix_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_AudioSource: return AudioSource_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_AudioSwitch: return AudioSwitch_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Background: return Background_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Background2D: return Background2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Billboard: return Billboard_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Bitmap: return Bitmap_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Box: return Box_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Circle: return Circle_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Collision: return Collision_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Color: return Color_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ColorInterpolator: return ColorInterpolator_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_CompositeTexture2D: return CompositeTexture2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_CompositeTexture3D: return CompositeTexture3D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Conditional: return Conditional_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Cone: return Cone_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Coordinate: return Coordinate_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Coordinate2D: return Coordinate2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_CoordinateInterpolator: return CoordinateInterpolator_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_CoordinateInterpolator2D: return CoordinateInterpolator2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Curve2D: return Curve2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Cylinder: return Cylinder_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_CylinderSensor: return CylinderSensor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_DirectionalLight: return DirectionalLight_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_DiscSensor: return DiscSensor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ElevationGrid: return ElevationGrid_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Extrusion: return Extrusion_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Fog: return Fog_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_FontStyle: return FontStyle_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Form: return Form_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Group: return Group_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ImageTexture: return ImageTexture_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_IndexedFaceSet: return IndexedFaceSet_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_IndexedFaceSet2D: return IndexedFaceSet2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_IndexedLineSet: return IndexedLineSet_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_IndexedLineSet2D: return IndexedLineSet2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Inline: return Inline_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_LOD: return LOD_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Layer2D: return Layer2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Layer3D: return Layer3D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Layout: return Layout_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_LineProperties: return LineProperties_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ListeningPoint: return ListeningPoint_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Material: return Material_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Material2D: return Material2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_MovieTexture: return MovieTexture_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_NavigationInfo: return NavigationInfo_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Normal: return Normal_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_NormalInterpolator: return NormalInterpolator_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_OrderedGroup: return OrderedGroup_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_OrientationInterpolator: return OrientationInterpolator_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PixelTexture: return PixelTexture_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PlaneSensor: return PlaneSensor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PlaneSensor2D: return PlaneSensor2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PointLight: return PointLight_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PointSet: return PointSet_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PointSet2D: return PointSet2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PositionInterpolator: return PositionInterpolator_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PositionInterpolator2D: return PositionInterpolator2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ProximitySensor2D: return ProximitySensor2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ProximitySensor: return ProximitySensor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_QuantizationParameter: return QuantizationParameter_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Rectangle: return Rectangle_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ScalarInterpolator: return ScalarInterpolator_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Script: return Script_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Shape: return Shape_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Sound: return Sound_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Sound2D: return Sound2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Sphere: return Sphere_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_SphereSensor: return SphereSensor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_SpotLight: return SpotLight_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Switch: return Switch_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_TermCap: return TermCap_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Text: return Text_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_TextureCoordinate: return TextureCoordinate_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_TextureTransform: return TextureTransform_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_TimeSensor: return TimeSensor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_TouchSensor: return TouchSensor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Transform: return Transform_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Transform2D: return Transform2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Valuator: return Valuator_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Viewpoint: return Viewpoint_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_VisibilitySensor: return VisibilitySensor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_WorldInfo: return WorldInfo_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_AcousticMaterial: return AcousticMaterial_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_AcousticScene: return AcousticScene_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ApplicationWindow: return ApplicationWindow_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_DirectiveSound: return DirectiveSound_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Hierarchical3DMesh: return Hierarchical3DMesh_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_MaterialKey: return MaterialKey_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PerceptualParameters: return PerceptualParameters_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_TemporalTransform: return TemporalTransform_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_TemporalGroup: return TemporalGroup_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ServerCommand: return ServerCommand_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_InputSensor: return InputSensor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_MatteTexture: return MatteTexture_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_MediaBuffer: return MediaBuffer_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_MediaControl: return MediaControl_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_MediaSensor: return MediaSensor_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_CoordinateInterpolator4D: return CoordinateInterpolator4D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_NonLinearDeformer: return NonLinearDeformer_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PositionAnimator: return PositionAnimator_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PositionAnimator2D: return PositionAnimator2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PositionInterpolator4D: return PositionInterpolator4D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ScalarAnimator: return ScalarAnimator_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Clipper2D: return Clipper2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_ColorTransform: return ColorTransform_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Ellipse: return Ellipse_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_LinearGradient: return LinearGradient_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_PathLayout: return PathLayout_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_RadialGradient: return RadialGradient_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_TransformMatrix2D: return TransformMatrix2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_Viewport: return Viewport_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_XCurve2D: return XCurve2D_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_XFontStyle: return XFontStyle_get_field_index(node, inField, code_mode, fieldIndex); + case TAG_MPEG4_XLineProperties: return XLineProperties_get_field_index(node, inField, code_mode, fieldIndex); + default: + return GF_BAD_PARAM; + } +} + +Bool gf_sg_mpeg4_node_get_aq_info(GF_Node *node, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (node->sgprivate->tag) { + case TAG_MPEG4_Anchor: return Anchor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_AnimationStream: return AnimationStream_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Appearance: return Appearance_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_AudioBuffer: return AudioBuffer_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_AudioClip: return AudioClip_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_AudioDelay: return AudioDelay_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_AudioFX: return AudioFX_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_AudioMix: return AudioMix_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_AudioSource: return AudioSource_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_AudioSwitch: return AudioSwitch_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Background: return Background_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Background2D: return Background2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Billboard: return Billboard_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Bitmap: return Bitmap_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Box: return Box_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Circle: return Circle_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Collision: return Collision_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Color: return Color_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ColorInterpolator: return ColorInterpolator_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_CompositeTexture2D: return CompositeTexture2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_CompositeTexture3D: return CompositeTexture3D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Conditional: return Conditional_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Cone: return Cone_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Coordinate: return Coordinate_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Coordinate2D: return Coordinate2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_CoordinateInterpolator: return CoordinateInterpolator_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_CoordinateInterpolator2D: return CoordinateInterpolator2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Curve2D: return Curve2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Cylinder: return Cylinder_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_CylinderSensor: return CylinderSensor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_DirectionalLight: return DirectionalLight_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_DiscSensor: return DiscSensor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ElevationGrid: return ElevationGrid_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Extrusion: return Extrusion_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Fog: return Fog_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_FontStyle: return FontStyle_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Form: return Form_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Group: return Group_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ImageTexture: return ImageTexture_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_IndexedFaceSet: return IndexedFaceSet_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_IndexedFaceSet2D: return IndexedFaceSet2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_IndexedLineSet: return IndexedLineSet_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_IndexedLineSet2D: return IndexedLineSet2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Inline: return Inline_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_LOD: return LOD_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Layer2D: return Layer2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Layer3D: return Layer3D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Layout: return Layout_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_LineProperties: return LineProperties_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ListeningPoint: return ListeningPoint_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Material: return Material_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Material2D: return Material2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_MovieTexture: return MovieTexture_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_NavigationInfo: return NavigationInfo_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Normal: return Normal_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_NormalInterpolator: return NormalInterpolator_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_OrderedGroup: return OrderedGroup_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_OrientationInterpolator: return OrientationInterpolator_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PixelTexture: return PixelTexture_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PlaneSensor: return PlaneSensor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PlaneSensor2D: return PlaneSensor2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PointLight: return PointLight_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PointSet: return PointSet_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PointSet2D: return PointSet2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PositionInterpolator: return PositionInterpolator_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PositionInterpolator2D: return PositionInterpolator2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ProximitySensor2D: return ProximitySensor2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ProximitySensor: return ProximitySensor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_QuantizationParameter: return QuantizationParameter_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Rectangle: return Rectangle_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ScalarInterpolator: return ScalarInterpolator_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Script: return Script_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Shape: return Shape_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Sound: return Sound_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Sound2D: return Sound2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Sphere: return Sphere_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_SphereSensor: return SphereSensor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_SpotLight: return SpotLight_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Switch: return Switch_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_TermCap: return TermCap_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Text: return Text_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_TextureCoordinate: return TextureCoordinate_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_TextureTransform: return TextureTransform_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_TimeSensor: return TimeSensor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_TouchSensor: return TouchSensor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Transform: return Transform_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Transform2D: return Transform2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Valuator: return Valuator_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Viewpoint: return Viewpoint_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_VisibilitySensor: return VisibilitySensor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_WorldInfo: return WorldInfo_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_AcousticMaterial: return AcousticMaterial_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_AcousticScene: return AcousticScene_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ApplicationWindow: return ApplicationWindow_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_DirectiveSound: return DirectiveSound_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Hierarchical3DMesh: return Hierarchical3DMesh_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_MaterialKey: return MaterialKey_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PerceptualParameters: return PerceptualParameters_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_TemporalTransform: return TemporalTransform_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_TemporalGroup: return TemporalGroup_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ServerCommand: return ServerCommand_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_InputSensor: return InputSensor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_MatteTexture: return MatteTexture_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_MediaBuffer: return MediaBuffer_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_MediaControl: return MediaControl_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_MediaSensor: return MediaSensor_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_CoordinateInterpolator4D: return CoordinateInterpolator4D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_NonLinearDeformer: return NonLinearDeformer_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PositionAnimator: return PositionAnimator_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PositionAnimator2D: return PositionAnimator2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PositionInterpolator4D: return PositionInterpolator4D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ScalarAnimator: return ScalarAnimator_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Clipper2D: return Clipper2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_ColorTransform: return ColorTransform_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Ellipse: return Ellipse_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_LinearGradient: return LinearGradient_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_PathLayout: return PathLayout_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_RadialGradient: return RadialGradient_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_TransformMatrix2D: return TransformMatrix2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_Viewport: return Viewport_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_XCurve2D: return XCurve2D_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_XFontStyle: return XFontStyle_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + case TAG_MPEG4_XLineProperties: return XLineProperties_get_aq_info(node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + default: + return 0; + } +} + +u32 gf_sg_mpeg4_node_get_child_ndt(GF_Node *node) +{ + switch (node->sgprivate->tag) { + case TAG_MPEG4_Anchor: return NDT_SF3DNode; + case TAG_MPEG4_AudioBuffer: return NDT_SFAudioNode; + case TAG_MPEG4_AudioDelay: return NDT_SFAudioNode; + case TAG_MPEG4_AudioFX: return NDT_SFAudioNode; + case TAG_MPEG4_AudioMix: return NDT_SFAudioNode; + case TAG_MPEG4_AudioSource: return NDT_SFAudioNode; + case TAG_MPEG4_AudioSwitch: return NDT_SFAudioNode; + case TAG_MPEG4_Billboard: return NDT_SF3DNode; + case TAG_MPEG4_Collision: return NDT_SF3DNode; + case TAG_MPEG4_CompositeTexture2D: return NDT_SF2DNode; + case TAG_MPEG4_CompositeTexture3D: return NDT_SF3DNode; + case TAG_MPEG4_Form: return NDT_SF2DNode; + case TAG_MPEG4_Group: return NDT_SF3DNode; + case TAG_MPEG4_LOD: return NDT_SF3DNode; + case TAG_MPEG4_Layer2D: return NDT_SF2DNode; + case TAG_MPEG4_Layer3D: return NDT_SF3DNode; + case TAG_MPEG4_Layout: return NDT_SF2DNode; + case TAG_MPEG4_OrderedGroup: return NDT_SF3DNode; + case TAG_MPEG4_Switch: return NDT_SF3DNode; + case TAG_MPEG4_Transform: return NDT_SF3DNode; + case TAG_MPEG4_Transform2D: return NDT_SF2DNode; + case TAG_MPEG4_TemporalTransform: return NDT_SF3DNode; + case TAG_MPEG4_TemporalGroup: return NDT_SFTemporalNode; + case TAG_MPEG4_Clipper2D: return NDT_SF2DNode; + case TAG_MPEG4_ColorTransform: return NDT_SF3DNode; + case TAG_MPEG4_PathLayout: return NDT_SF2DNode; + case TAG_MPEG4_TransformMatrix2D: return NDT_SF2DNode; + default: + return 0; + } +} + + +u32 gf_node_mpeg4_type_by_class_name(const char *node_name) +{ + if(!node_name) return 0; + if (!strcmp(node_name, "Anchor")) return TAG_MPEG4_Anchor; + if (!strcmp(node_name, "AnimationStream")) return TAG_MPEG4_AnimationStream; + if (!strcmp(node_name, "Appearance")) return TAG_MPEG4_Appearance; + if (!strcmp(node_name, "AudioBuffer")) return TAG_MPEG4_AudioBuffer; + if (!strcmp(node_name, "AudioClip")) return TAG_MPEG4_AudioClip; + if (!strcmp(node_name, "AudioDelay")) return TAG_MPEG4_AudioDelay; + if (!strcmp(node_name, "AudioFX")) return TAG_MPEG4_AudioFX; + if (!strcmp(node_name, "AudioMix")) return TAG_MPEG4_AudioMix; + if (!strcmp(node_name, "AudioSource")) return TAG_MPEG4_AudioSource; + if (!strcmp(node_name, "AudioSwitch")) return TAG_MPEG4_AudioSwitch; + if (!strcmp(node_name, "Background")) return TAG_MPEG4_Background; + if (!strcmp(node_name, "Background2D")) return TAG_MPEG4_Background2D; + if (!strcmp(node_name, "Billboard")) return TAG_MPEG4_Billboard; + if (!strcmp(node_name, "Bitmap")) return TAG_MPEG4_Bitmap; + if (!strcmp(node_name, "Box")) return TAG_MPEG4_Box; + if (!strcmp(node_name, "Circle")) return TAG_MPEG4_Circle; + if (!strcmp(node_name, "Collision")) return TAG_MPEG4_Collision; + if (!strcmp(node_name, "Color")) return TAG_MPEG4_Color; + if (!strcmp(node_name, "ColorInterpolator")) return TAG_MPEG4_ColorInterpolator; + if (!strcmp(node_name, "CompositeTexture2D")) return TAG_MPEG4_CompositeTexture2D; + if (!strcmp(node_name, "CompositeTexture3D")) return TAG_MPEG4_CompositeTexture3D; + if (!strcmp(node_name, "Conditional")) return TAG_MPEG4_Conditional; + if (!strcmp(node_name, "Cone")) return TAG_MPEG4_Cone; + if (!strcmp(node_name, "Coordinate")) return TAG_MPEG4_Coordinate; + if (!strcmp(node_name, "Coordinate2D")) return TAG_MPEG4_Coordinate2D; + if (!strcmp(node_name, "CoordinateInterpolator")) return TAG_MPEG4_CoordinateInterpolator; + if (!strcmp(node_name, "CoordinateInterpolator2D")) return TAG_MPEG4_CoordinateInterpolator2D; + if (!strcmp(node_name, "Curve2D")) return TAG_MPEG4_Curve2D; + if (!strcmp(node_name, "Cylinder")) return TAG_MPEG4_Cylinder; + if (!strcmp(node_name, "CylinderSensor")) return TAG_MPEG4_CylinderSensor; + if (!strcmp(node_name, "DirectionalLight")) return TAG_MPEG4_DirectionalLight; + if (!strcmp(node_name, "DiscSensor")) return TAG_MPEG4_DiscSensor; + if (!strcmp(node_name, "ElevationGrid")) return TAG_MPEG4_ElevationGrid; + if (!strcmp(node_name, "Extrusion")) return TAG_MPEG4_Extrusion; + if (!strcmp(node_name, "Fog")) return TAG_MPEG4_Fog; + if (!strcmp(node_name, "FontStyle")) return TAG_MPEG4_FontStyle; + if (!strcmp(node_name, "Form")) return TAG_MPEG4_Form; + if (!strcmp(node_name, "Group")) return TAG_MPEG4_Group; + if (!strcmp(node_name, "ImageTexture")) return TAG_MPEG4_ImageTexture; + if (!strcmp(node_name, "IndexedFaceSet")) return TAG_MPEG4_IndexedFaceSet; + if (!strcmp(node_name, "IndexedFaceSet2D")) return TAG_MPEG4_IndexedFaceSet2D; + if (!strcmp(node_name, "IndexedLineSet")) return TAG_MPEG4_IndexedLineSet; + if (!strcmp(node_name, "IndexedLineSet2D")) return TAG_MPEG4_IndexedLineSet2D; + if (!strcmp(node_name, "Inline")) return TAG_MPEG4_Inline; + if (!strcmp(node_name, "LOD")) return TAG_MPEG4_LOD; + if (!strcmp(node_name, "Layer2D")) return TAG_MPEG4_Layer2D; + if (!strcmp(node_name, "Layer3D")) return TAG_MPEG4_Layer3D; + if (!strcmp(node_name, "Layout")) return TAG_MPEG4_Layout; + if (!strcmp(node_name, "LineProperties")) return TAG_MPEG4_LineProperties; + if (!strcmp(node_name, "ListeningPoint")) return TAG_MPEG4_ListeningPoint; + if (!strcmp(node_name, "Material")) return TAG_MPEG4_Material; + if (!strcmp(node_name, "Material2D")) return TAG_MPEG4_Material2D; + if (!strcmp(node_name, "MovieTexture")) return TAG_MPEG4_MovieTexture; + if (!strcmp(node_name, "NavigationInfo")) return TAG_MPEG4_NavigationInfo; + if (!strcmp(node_name, "Normal")) return TAG_MPEG4_Normal; + if (!strcmp(node_name, "NormalInterpolator")) return TAG_MPEG4_NormalInterpolator; + if (!strcmp(node_name, "OrderedGroup")) return TAG_MPEG4_OrderedGroup; + if (!strcmp(node_name, "OrientationInterpolator")) return TAG_MPEG4_OrientationInterpolator; + if (!strcmp(node_name, "PixelTexture")) return TAG_MPEG4_PixelTexture; + if (!strcmp(node_name, "PlaneSensor")) return TAG_MPEG4_PlaneSensor; + if (!strcmp(node_name, "PlaneSensor2D")) return TAG_MPEG4_PlaneSensor2D; + if (!strcmp(node_name, "PointLight")) return TAG_MPEG4_PointLight; + if (!strcmp(node_name, "PointSet")) return TAG_MPEG4_PointSet; + if (!strcmp(node_name, "PointSet2D")) return TAG_MPEG4_PointSet2D; + if (!strcmp(node_name, "PositionInterpolator")) return TAG_MPEG4_PositionInterpolator; + if (!strcmp(node_name, "PositionInterpolator2D")) return TAG_MPEG4_PositionInterpolator2D; + if (!strcmp(node_name, "ProximitySensor2D")) return TAG_MPEG4_ProximitySensor2D; + if (!strcmp(node_name, "ProximitySensor")) return TAG_MPEG4_ProximitySensor; + if (!strcmp(node_name, "QuantizationParameter")) return TAG_MPEG4_QuantizationParameter; + if (!strcmp(node_name, "Rectangle")) return TAG_MPEG4_Rectangle; + if (!strcmp(node_name, "ScalarInterpolator")) return TAG_MPEG4_ScalarInterpolator; + if (!strcmp(node_name, "Script")) return TAG_MPEG4_Script; + if (!strcmp(node_name, "Shape")) return TAG_MPEG4_Shape; + if (!strcmp(node_name, "Sound")) return TAG_MPEG4_Sound; + if (!strcmp(node_name, "Sound2D")) return TAG_MPEG4_Sound2D; + if (!strcmp(node_name, "Sphere")) return TAG_MPEG4_Sphere; + if (!strcmp(node_name, "SphereSensor")) return TAG_MPEG4_SphereSensor; + if (!strcmp(node_name, "SpotLight")) return TAG_MPEG4_SpotLight; + if (!strcmp(node_name, "Switch")) return TAG_MPEG4_Switch; + if (!strcmp(node_name, "TermCap")) return TAG_MPEG4_TermCap; + if (!strcmp(node_name, "Text")) return TAG_MPEG4_Text; + if (!strcmp(node_name, "TextureCoordinate")) return TAG_MPEG4_TextureCoordinate; + if (!strcmp(node_name, "TextureTransform")) return TAG_MPEG4_TextureTransform; + if (!strcmp(node_name, "TimeSensor")) return TAG_MPEG4_TimeSensor; + if (!strcmp(node_name, "TouchSensor")) return TAG_MPEG4_TouchSensor; + if (!strcmp(node_name, "Transform")) return TAG_MPEG4_Transform; + if (!strcmp(node_name, "Transform2D")) return TAG_MPEG4_Transform2D; + if (!strcmp(node_name, "Valuator")) return TAG_MPEG4_Valuator; + if (!strcmp(node_name, "Viewpoint")) return TAG_MPEG4_Viewpoint; + if (!strcmp(node_name, "VisibilitySensor")) return TAG_MPEG4_VisibilitySensor; + if (!strcmp(node_name, "WorldInfo")) return TAG_MPEG4_WorldInfo; + if (!strcmp(node_name, "AcousticMaterial")) return TAG_MPEG4_AcousticMaterial; + if (!strcmp(node_name, "AcousticScene")) return TAG_MPEG4_AcousticScene; + if (!strcmp(node_name, "ApplicationWindow")) return TAG_MPEG4_ApplicationWindow; + if (!strcmp(node_name, "DirectiveSound")) return TAG_MPEG4_DirectiveSound; + if (!strcmp(node_name, "Hierarchical3DMesh")) return TAG_MPEG4_Hierarchical3DMesh; + if (!strcmp(node_name, "MaterialKey")) return TAG_MPEG4_MaterialKey; + if (!strcmp(node_name, "PerceptualParameters")) return TAG_MPEG4_PerceptualParameters; + if (!strcmp(node_name, "TemporalTransform")) return TAG_MPEG4_TemporalTransform; + if (!strcmp(node_name, "TemporalGroup")) return TAG_MPEG4_TemporalGroup; + if (!strcmp(node_name, "ServerCommand")) return TAG_MPEG4_ServerCommand; + if (!strcmp(node_name, "InputSensor")) return TAG_MPEG4_InputSensor; + if (!strcmp(node_name, "MatteTexture")) return TAG_MPEG4_MatteTexture; + if (!strcmp(node_name, "MediaBuffer")) return TAG_MPEG4_MediaBuffer; + if (!strcmp(node_name, "MediaControl")) return TAG_MPEG4_MediaControl; + if (!strcmp(node_name, "MediaSensor")) return TAG_MPEG4_MediaSensor; + if (!strcmp(node_name, "CoordinateInterpolator4D")) return TAG_MPEG4_CoordinateInterpolator4D; + if (!strcmp(node_name, "NonLinearDeformer")) return TAG_MPEG4_NonLinearDeformer; + if (!strcmp(node_name, "PositionAnimator")) return TAG_MPEG4_PositionAnimator; + if (!strcmp(node_name, "PositionAnimator2D")) return TAG_MPEG4_PositionAnimator2D; + if (!strcmp(node_name, "PositionInterpolator4D")) return TAG_MPEG4_PositionInterpolator4D; + if (!strcmp(node_name, "ScalarAnimator")) return TAG_MPEG4_ScalarAnimator; + if (!strcmp(node_name, "Clipper2D")) return TAG_MPEG4_Clipper2D; + if (!strcmp(node_name, "ColorTransform")) return TAG_MPEG4_ColorTransform; + if (!strcmp(node_name, "Ellipse")) return TAG_MPEG4_Ellipse; + if (!strcmp(node_name, "LinearGradient")) return TAG_MPEG4_LinearGradient; + if (!strcmp(node_name, "PathLayout")) return TAG_MPEG4_PathLayout; + if (!strcmp(node_name, "RadialGradient")) return TAG_MPEG4_RadialGradient; + if (!strcmp(node_name, "TransformMatrix2D")) return TAG_MPEG4_TransformMatrix2D; + if (!strcmp(node_name, "Viewport")) return TAG_MPEG4_Viewport; + if (!strcmp(node_name, "XCurve2D")) return TAG_MPEG4_XCurve2D; + if (!strcmp(node_name, "XFontStyle")) return TAG_MPEG4_XFontStyle; + if (!strcmp(node_name, "XLineProperties")) return TAG_MPEG4_XLineProperties; + return 0; +} + +s32 gf_sg_mpeg4_node_get_field_index_by_name(GF_Node *node, char *name) +{ + switch (node->sgprivate->tag) { + case TAG_MPEG4_Anchor: return Anchor_get_field_index_by_name(name); + case TAG_MPEG4_AnimationStream: return AnimationStream_get_field_index_by_name(name); + case TAG_MPEG4_Appearance: return Appearance_get_field_index_by_name(name); + case TAG_MPEG4_AudioBuffer: return AudioBuffer_get_field_index_by_name(name); + case TAG_MPEG4_AudioClip: return AudioClip_get_field_index_by_name(name); + case TAG_MPEG4_AudioDelay: return AudioDelay_get_field_index_by_name(name); + case TAG_MPEG4_AudioFX: return AudioFX_get_field_index_by_name(name); + case TAG_MPEG4_AudioMix: return AudioMix_get_field_index_by_name(name); + case TAG_MPEG4_AudioSource: return AudioSource_get_field_index_by_name(name); + case TAG_MPEG4_AudioSwitch: return AudioSwitch_get_field_index_by_name(name); + case TAG_MPEG4_Background: return Background_get_field_index_by_name(name); + case TAG_MPEG4_Background2D: return Background2D_get_field_index_by_name(name); + case TAG_MPEG4_Billboard: return Billboard_get_field_index_by_name(name); + case TAG_MPEG4_Bitmap: return Bitmap_get_field_index_by_name(name); + case TAG_MPEG4_Box: return Box_get_field_index_by_name(name); + case TAG_MPEG4_Circle: return Circle_get_field_index_by_name(name); + case TAG_MPEG4_Collision: return Collision_get_field_index_by_name(name); + case TAG_MPEG4_Color: return Color_get_field_index_by_name(name); + case TAG_MPEG4_ColorInterpolator: return ColorInterpolator_get_field_index_by_name(name); + case TAG_MPEG4_CompositeTexture2D: return CompositeTexture2D_get_field_index_by_name(name); + case TAG_MPEG4_CompositeTexture3D: return CompositeTexture3D_get_field_index_by_name(name); + case TAG_MPEG4_Conditional: return Conditional_get_field_index_by_name(name); + case TAG_MPEG4_Cone: return Cone_get_field_index_by_name(name); + case TAG_MPEG4_Coordinate: return Coordinate_get_field_index_by_name(name); + case TAG_MPEG4_Coordinate2D: return Coordinate2D_get_field_index_by_name(name); + case TAG_MPEG4_CoordinateInterpolator: return CoordinateInterpolator_get_field_index_by_name(name); + case TAG_MPEG4_CoordinateInterpolator2D: return CoordinateInterpolator2D_get_field_index_by_name(name); + case TAG_MPEG4_Curve2D: return Curve2D_get_field_index_by_name(name); + case TAG_MPEG4_Cylinder: return Cylinder_get_field_index_by_name(name); + case TAG_MPEG4_CylinderSensor: return CylinderSensor_get_field_index_by_name(name); + case TAG_MPEG4_DirectionalLight: return DirectionalLight_get_field_index_by_name(name); + case TAG_MPEG4_DiscSensor: return DiscSensor_get_field_index_by_name(name); + case TAG_MPEG4_ElevationGrid: return ElevationGrid_get_field_index_by_name(name); + case TAG_MPEG4_Extrusion: return Extrusion_get_field_index_by_name(name); + case TAG_MPEG4_Fog: return Fog_get_field_index_by_name(name); + case TAG_MPEG4_FontStyle: return FontStyle_get_field_index_by_name(name); + case TAG_MPEG4_Form: return Form_get_field_index_by_name(name); + case TAG_MPEG4_Group: return Group_get_field_index_by_name(name); + case TAG_MPEG4_ImageTexture: return ImageTexture_get_field_index_by_name(name); + case TAG_MPEG4_IndexedFaceSet: return IndexedFaceSet_get_field_index_by_name(name); + case TAG_MPEG4_IndexedFaceSet2D: return IndexedFaceSet2D_get_field_index_by_name(name); + case TAG_MPEG4_IndexedLineSet: return IndexedLineSet_get_field_index_by_name(name); + case TAG_MPEG4_IndexedLineSet2D: return IndexedLineSet2D_get_field_index_by_name(name); + case TAG_MPEG4_Inline: return Inline_get_field_index_by_name(name); + case TAG_MPEG4_LOD: return LOD_get_field_index_by_name(name); + case TAG_MPEG4_Layer2D: return Layer2D_get_field_index_by_name(name); + case TAG_MPEG4_Layer3D: return Layer3D_get_field_index_by_name(name); + case TAG_MPEG4_Layout: return Layout_get_field_index_by_name(name); + case TAG_MPEG4_LineProperties: return LineProperties_get_field_index_by_name(name); + case TAG_MPEG4_ListeningPoint: return ListeningPoint_get_field_index_by_name(name); + case TAG_MPEG4_Material: return Material_get_field_index_by_name(name); + case TAG_MPEG4_Material2D: return Material2D_get_field_index_by_name(name); + case TAG_MPEG4_MovieTexture: return MovieTexture_get_field_index_by_name(name); + case TAG_MPEG4_NavigationInfo: return NavigationInfo_get_field_index_by_name(name); + case TAG_MPEG4_Normal: return Normal_get_field_index_by_name(name); + case TAG_MPEG4_NormalInterpolator: return NormalInterpolator_get_field_index_by_name(name); + case TAG_MPEG4_OrderedGroup: return OrderedGroup_get_field_index_by_name(name); + case TAG_MPEG4_OrientationInterpolator: return OrientationInterpolator_get_field_index_by_name(name); + case TAG_MPEG4_PixelTexture: return PixelTexture_get_field_index_by_name(name); + case TAG_MPEG4_PlaneSensor: return PlaneSensor_get_field_index_by_name(name); + case TAG_MPEG4_PlaneSensor2D: return PlaneSensor2D_get_field_index_by_name(name); + case TAG_MPEG4_PointLight: return PointLight_get_field_index_by_name(name); + case TAG_MPEG4_PointSet: return PointSet_get_field_index_by_name(name); + case TAG_MPEG4_PointSet2D: return PointSet2D_get_field_index_by_name(name); + case TAG_MPEG4_PositionInterpolator: return PositionInterpolator_get_field_index_by_name(name); + case TAG_MPEG4_PositionInterpolator2D: return PositionInterpolator2D_get_field_index_by_name(name); + case TAG_MPEG4_ProximitySensor2D: return ProximitySensor2D_get_field_index_by_name(name); + case TAG_MPEG4_ProximitySensor: return ProximitySensor_get_field_index_by_name(name); + case TAG_MPEG4_QuantizationParameter: return QuantizationParameter_get_field_index_by_name(name); + case TAG_MPEG4_Rectangle: return Rectangle_get_field_index_by_name(name); + case TAG_MPEG4_ScalarInterpolator: return ScalarInterpolator_get_field_index_by_name(name); + case TAG_MPEG4_Script: return Script_get_field_index_by_name(name); + case TAG_MPEG4_Shape: return Shape_get_field_index_by_name(name); + case TAG_MPEG4_Sound: return Sound_get_field_index_by_name(name); + case TAG_MPEG4_Sound2D: return Sound2D_get_field_index_by_name(name); + case TAG_MPEG4_Sphere: return Sphere_get_field_index_by_name(name); + case TAG_MPEG4_SphereSensor: return SphereSensor_get_field_index_by_name(name); + case TAG_MPEG4_SpotLight: return SpotLight_get_field_index_by_name(name); + case TAG_MPEG4_Switch: return Switch_get_field_index_by_name(name); + case TAG_MPEG4_TermCap: return TermCap_get_field_index_by_name(name); + case TAG_MPEG4_Text: return Text_get_field_index_by_name(name); + case TAG_MPEG4_TextureCoordinate: return TextureCoordinate_get_field_index_by_name(name); + case TAG_MPEG4_TextureTransform: return TextureTransform_get_field_index_by_name(name); + case TAG_MPEG4_TimeSensor: return TimeSensor_get_field_index_by_name(name); + case TAG_MPEG4_TouchSensor: return TouchSensor_get_field_index_by_name(name); + case TAG_MPEG4_Transform: return Transform_get_field_index_by_name(name); + case TAG_MPEG4_Transform2D: return Transform2D_get_field_index_by_name(name); + case TAG_MPEG4_Valuator: return Valuator_get_field_index_by_name(name); + case TAG_MPEG4_Viewpoint: return Viewpoint_get_field_index_by_name(name); + case TAG_MPEG4_VisibilitySensor: return VisibilitySensor_get_field_index_by_name(name); + case TAG_MPEG4_WorldInfo: return WorldInfo_get_field_index_by_name(name); + case TAG_MPEG4_AcousticMaterial: return AcousticMaterial_get_field_index_by_name(name); + case TAG_MPEG4_AcousticScene: return AcousticScene_get_field_index_by_name(name); + case TAG_MPEG4_ApplicationWindow: return ApplicationWindow_get_field_index_by_name(name); + case TAG_MPEG4_DirectiveSound: return DirectiveSound_get_field_index_by_name(name); + case TAG_MPEG4_Hierarchical3DMesh: return Hierarchical3DMesh_get_field_index_by_name(name); + case TAG_MPEG4_MaterialKey: return MaterialKey_get_field_index_by_name(name); + case TAG_MPEG4_PerceptualParameters: return PerceptualParameters_get_field_index_by_name(name); + case TAG_MPEG4_TemporalTransform: return TemporalTransform_get_field_index_by_name(name); + case TAG_MPEG4_TemporalGroup: return TemporalGroup_get_field_index_by_name(name); + case TAG_MPEG4_ServerCommand: return ServerCommand_get_field_index_by_name(name); + case TAG_MPEG4_InputSensor: return InputSensor_get_field_index_by_name(name); + case TAG_MPEG4_MatteTexture: return MatteTexture_get_field_index_by_name(name); + case TAG_MPEG4_MediaBuffer: return MediaBuffer_get_field_index_by_name(name); + case TAG_MPEG4_MediaControl: return MediaControl_get_field_index_by_name(name); + case TAG_MPEG4_MediaSensor: return MediaSensor_get_field_index_by_name(name); + case TAG_MPEG4_CoordinateInterpolator4D: return CoordinateInterpolator4D_get_field_index_by_name(name); + case TAG_MPEG4_NonLinearDeformer: return NonLinearDeformer_get_field_index_by_name(name); + case TAG_MPEG4_PositionAnimator: return PositionAnimator_get_field_index_by_name(name); + case TAG_MPEG4_PositionAnimator2D: return PositionAnimator2D_get_field_index_by_name(name); + case TAG_MPEG4_PositionInterpolator4D: return PositionInterpolator4D_get_field_index_by_name(name); + case TAG_MPEG4_ScalarAnimator: return ScalarAnimator_get_field_index_by_name(name); + case TAG_MPEG4_Clipper2D: return Clipper2D_get_field_index_by_name(name); + case TAG_MPEG4_ColorTransform: return ColorTransform_get_field_index_by_name(name); + case TAG_MPEG4_Ellipse: return Ellipse_get_field_index_by_name(name); + case TAG_MPEG4_LinearGradient: return LinearGradient_get_field_index_by_name(name); + case TAG_MPEG4_PathLayout: return PathLayout_get_field_index_by_name(name); + case TAG_MPEG4_RadialGradient: return RadialGradient_get_field_index_by_name(name); + case TAG_MPEG4_TransformMatrix2D: return TransformMatrix2D_get_field_index_by_name(name); + case TAG_MPEG4_Viewport: return Viewport_get_field_index_by_name(name); + case TAG_MPEG4_XCurve2D: return XCurve2D_get_field_index_by_name(name); + case TAG_MPEG4_XFontStyle: return XFontStyle_get_field_index_by_name(name); + case TAG_MPEG4_XLineProperties: return XLineProperties_get_field_index_by_name(name); + default: + return -1; + } +} + diff --git a/src/scenegraph/mpeg4_valuator.c b/src/scenegraph/mpeg4_valuator.c new file mode 100644 index 0000000..c53b71b --- /dev/null +++ b/src/scenegraph/mpeg4_valuator.c @@ -0,0 +1,432 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +/*MPEG4 tags (for internal nodes)*/ +#include + +static void format_sftime_string(Fixed _val, char *str) +{ + u32 h, m, s; + Bool neg = 0; + Float val = FIX2FLT(_val); + if (val<0) { + val = -val; + neg = 1; + } + h = (u32) (val/3600); + m = (u32) (val/60) - h*60; + s = (u32) (val) - h*3600 - m*60; + sprintf(str, "%s%02d:%02d:%02d", neg ? "-" : "", h, m, s); +} + +static void SetValuatorOutput(M_Valuator *p, SFVec4f *inSFField, GenMFField *inMFField, u32 inType) +{ + char str[500]; + u32 i; + GF_Route *r; + SFVec4f output, sf_out; + u32 count, num_out; + Bool do_sum; + + if (!gf_node_get_id((GF_Node*)p) && !p->sgprivate->scenegraph->pOwningProto) return; + if (!p->sgprivate->interact) return; + + do_sum = p->Sum; + num_out = 1; + + if (!inMFField) { + count = 1; + output.x = gf_mulfix(p->Factor1, inSFField->x) + p->Offset1; + output.y = gf_mulfix(p->Factor2, inSFField->y) + p->Offset2; + output.z = gf_mulfix(p->Factor3, inSFField->z) + p->Offset3; + output.q = gf_mulfix(p->Factor4, inSFField->q) + p->Offset4; + + if (do_sum) { + output.x = output.x + output.y + output.z + output.q; + output.y = output.z = output.q = output.x; + do_sum = 0; + } + + switch (inType) { + case GF_SG_VRML_SFVEC2F: + num_out = 2; + break; + case GF_SG_VRML_SFVEC3F: + case GF_SG_VRML_SFCOLOR: + num_out = 3; + break; + case GF_SG_VRML_SFVEC4F: + case GF_SG_VRML_SFROTATION: + num_out = 4; + break; + } + } else { + count = inMFField->count; + } + /*reallocate all MF fields*/ +/* gf_sg_vrml_mf_reset(&p->outMFColor, GF_SG_VRML_MFCOLOR); + gf_sg_vrml_mf_reset(&p->outMFFloat, GF_SG_VRML_MFFLOAT); + gf_sg_vrml_mf_reset(&p->outMFInt32, GF_SG_VRML_MFINT32); + gf_sg_vrml_mf_reset(&p->outMFRotation, GF_SG_VRML_MFROTATION); + gf_sg_vrml_mf_reset(&p->outMFString, GF_SG_VRML_MFSTRING); + gf_sg_vrml_mf_reset(&p->outMFVec2f, GF_SG_VRML_MFVEC2F); + gf_sg_vrml_mf_reset(&p->outMFVec3f, GF_SG_VRML_MFVEC3F); +*/ + gf_sg_vrml_mf_alloc(&p->outMFColor, GF_SG_VRML_MFCOLOR, count); + gf_sg_vrml_mf_alloc(&p->outMFFloat, GF_SG_VRML_MFFLOAT, count); + gf_sg_vrml_mf_alloc(&p->outMFInt32, GF_SG_VRML_MFINT32, count); + gf_sg_vrml_mf_alloc(&p->outMFRotation, GF_SG_VRML_MFROTATION, count); + gf_sg_vrml_mf_alloc(&p->outMFString, GF_SG_VRML_MFSTRING, count); gf_sg_vrml_mf_alloc(&p->outMFVec2f, GF_SG_VRML_MFVEC2F, count); + gf_sg_vrml_mf_alloc(&p->outMFVec3f, GF_SG_VRML_MFVEC3F, count); + + /*set all MF outputs*/ + for (i=0; ivals[i]; + Fixed vi = INT2FIX(sfi); + output.x = gf_mulfix(p->Factor1, vi) + p->Offset1; + output.y = gf_mulfix(p->Factor2, vi) + p->Offset2; + output.z = gf_mulfix(p->Factor3, vi) + p->Offset3; + output.q = gf_mulfix(p->Factor4, vi) + p->Offset4; + } + break; + case GF_SG_VRML_MFFLOAT: + { + Fixed sff = ((MFFloat *)inMFField)->vals[i]; + output.x = gf_mulfix(p->Factor1, sff) + p->Offset1; + output.y = gf_mulfix(p->Factor2, sff) + p->Offset2; + output.z = gf_mulfix(p->Factor3, sff) + p->Offset3; + output.q = gf_mulfix(p->Factor4, sff) + p->Offset4; + } + break; + case GF_SG_VRML_MFCOLOR: + { + SFColor sfc = ((MFColor *)inMFField)->vals[i]; + output.x = gf_mulfix(p->Factor1, sfc.red) + p->Offset1; + output.y = gf_mulfix(p->Factor2, sfc.green) + p->Offset2; + output.z = gf_mulfix(p->Factor3, sfc.blue) + p->Offset3; + output.q = p->Offset4; + num_out = 3; + } + break; + case GF_SG_VRML_MFVEC2F: + { + SFVec2f sfv = ((MFVec2f *)inMFField)->vals[i]; + output.x = gf_mulfix(p->Factor1, sfv.x) + p->Offset1; + output.y = gf_mulfix(p->Factor2, sfv.y) + p->Offset2; + output.z = p->Offset3; + output.q = p->Offset4; + num_out = 2; + } + break; + case GF_SG_VRML_MFVEC3F: + { + SFVec3f sfv = ((MFVec3f *)inMFField)->vals[i]; + output.x = gf_mulfix(p->Factor1, sfv.x) + p->Offset1; + output.y = gf_mulfix(p->Factor2, sfv.y) + p->Offset2; + output.z = gf_mulfix(p->Factor3, sfv.z) + p->Offset3; + output.q = p->Offset4; + num_out = 3; + } + break; + case GF_SG_VRML_MFVEC4F: + case GF_SG_VRML_MFROTATION: + { + SFVec4f sfv = ((MFVec4f *)inMFField)->vals[i]; + output.x = gf_mulfix(p->Factor1, sfv.x) + p->Offset1; + output.y = gf_mulfix(p->Factor2, sfv.y) + p->Offset2; + output.z = gf_mulfix(p->Factor3, sfv.z) + p->Offset3; + output.q = gf_mulfix(p->Factor4, sfv.q) + p->Offset4; + num_out = 4; + } + break; + case GF_SG_VRML_MFSTRING: + /*cf below*/ + output.x = output.y = output.z = output.q = 0; + if (((MFString *)inMFField)->vals[i]) { + if (!stricmp(((MFString *)inMFField)->vals[i], "true")) { + output.x = output.y = output.z = output.q = FIX_ONE; + } else if (!strstr(((MFString *)inMFField)->vals[i], ".")) { + output.x = INT2FIX( atoi(((MFString *)inMFField)->vals[i]) ); + output.y = output.z = output.q = output.x; + } else { + output.x = FLT2FIX( atof(((MFString *)inMFField)->vals[i]) ); + output.y = output.z = output.q = output.x; + } + } + + output.x = gf_mulfix(p->Factor1, output.x) + p->Offset1; + output.y = gf_mulfix(p->Factor2, output.y) + p->Offset2; + output.z = gf_mulfix(p->Factor3, output.z) + p->Offset3; + output.q = gf_mulfix(p->Factor4, output.q) + p->Offset4; + break; + + } + if (do_sum) { + output.x = output.x + output.y + output.z + output.q; + output.y = output.z = output.q = output.x; + } + } + + p->outMFFloat.vals[i] = output.x; + p->outMFInt32.vals[i] = FIX2INT(output.x); + p->outMFColor.vals[i].red = output.x; + p->outMFColor.vals[i].green = output.y; + p->outMFColor.vals[i].blue = output.z; + p->outMFVec2f.vals[i].x = output.x; + p->outMFVec2f.vals[i].y = output.y; + p->outMFVec3f.vals[i].x = output.x; + p->outMFVec3f.vals[i].y = output.y; + p->outMFVec3f.vals[i].z = output.z; + p->outMFRotation.vals[i].x = output.x; + p->outMFRotation.vals[i].y = output.y; + p->outMFRotation.vals[i].z = output.z; + p->outMFRotation.vals[i].q = output.q; + + if (num_out==1) { + if (inType==GF_SG_VRML_SFTIME) { + format_sftime_string(output.x, str); + } else { + sprintf(str, "%g", FIX2FLT(output.x)); + } + } else if (num_out==2) { + sprintf(str, "%g %g", FIX2FLT(output.x), FIX2FLT(output.y)); + } else if (num_out==3) { + sprintf(str, "%g %g %g", FIX2FLT(output.x), FIX2FLT(output.y), FIX2FLT(output.z)); + } else if (num_out==4) { + sprintf(str, "%g %g %g %g", FIX2FLT(output.x), FIX2FLT(output.y), FIX2FLT(output.z), FIX2FLT(output.q)); + } + + if (p->outMFString.vals[i]) free(p->outMFString.vals[i]); + p->outMFString.vals[i] = strdup(str); + + if (!i) sf_out = output; + + } + + p->outSFBool = (Bool) (sf_out.x ? 1 : 0); + p->outSFFloat = sf_out.x; + p->outSFInt32 = FIX2INT(sf_out.x); + p->outSFTime = (SFTime) FIX2FLT(sf_out.x); + p->outSFRotation.x = sf_out.x; + p->outSFRotation.y = sf_out.y; + p->outSFRotation.z = sf_out.z; + p->outSFRotation.q = sf_out.q; + p->outSFColor.red = sf_out.x; + p->outSFColor.green = sf_out.y; + p->outSFColor.blue = sf_out.z; + p->outSFVec2f.x = sf_out.x; + p->outSFVec2f.y = sf_out.y; + p->outSFVec3f.x = sf_out.x; + p->outSFVec3f.y = sf_out.y; + p->outSFVec3f.z = sf_out.z; + + if (num_out==1) { + if (inType==GF_SG_VRML_SFTIME) { + format_sftime_string(output.x, str); + } else { + sprintf(str, "%.6f", FIX2FLT(sf_out.x)); + } + } else if (num_out==2) { + sprintf(str, "%.4f %.4f", FIX2FLT(sf_out.x), FIX2FLT(sf_out.y)); + } else if (num_out==3) { + sprintf(str, "%.3f %.3f %.3f", FIX2FLT(sf_out.x), FIX2FLT(sf_out.y), FIX2FLT(sf_out.z)); + } else if (num_out==4) { + sprintf(str, "%.2f %.2f %.2f %.2f", FIX2FLT(sf_out.x), FIX2FLT(sf_out.y), FIX2FLT(sf_out.z), FIX2FLT(sf_out.q)); + } + if (p->outSFString.buffer ) free(p->outSFString.buffer); + p->outSFString.buffer = strdup(str); + + /*valuator is a special case, all routes are triggered*/ + i=0; + while ((r = (GF_Route*)gf_list_enum(p->sgprivate->interact->routes, &i))) { + if (r->FromNode != (GF_Node *)p) continue; + + if (r->IS_route) { + gf_sg_route_activate(r); + } else { + gf_sg_route_queue(p->sgprivate->scenegraph, r); + } + } +} + + +/* +valuator spec (9.4.2.116.2) +"In the special case of a scalar input type (e.g. SFBool, SFInt32) that is cast to a vectorial output type (e.g. +SFVec2f), for all components i of output.i, input.i shall take the value of the scalar input type, after appropriate type +conversion" +*/ + +static void Valuator_SetInSFBool(GF_Node *n) +{ + SFVec4f val; + M_Valuator *_this = (M_Valuator *) n; + val.x = val.y = val.z = val.q = _this->inSFBool ? FIX_ONE : 0; + SetValuatorOutput(_this, &val, NULL, GF_SG_VRML_SFBOOL); +} +static void Valuator_SetInSFFloat(GF_Node *n) +{ + SFVec4f val; + M_Valuator *_this = (M_Valuator *) n; + val.x = val.y = val.z = val.q = _this->inSFFloat; + SetValuatorOutput(_this, &val, NULL, GF_SG_VRML_SFFLOAT); +} +static void Valuator_SetInSFInt32(GF_Node *n) +{ + SFVec4f val; + M_Valuator *_this = (M_Valuator *) n; + val.x = val.y = val.z = val.q = INT2FIX(_this->inSFInt32); + SetValuatorOutput(_this, &val, NULL, GF_SG_VRML_SFINT32); +} +static void Valuator_SetInSFTime(GF_Node *n) +{ + SFVec4f val; + M_Valuator *_this = (M_Valuator *) n; + val.x = val.y = val.z = val.q = FLT2FIX(_this->inSFTime); + SetValuatorOutput(_this, &val, NULL, GF_SG_VRML_SFTIME); +} +static void Valuator_SetInSFColor(GF_Node *n) +{ + SFVec4f val; + M_Valuator *_this = (M_Valuator *) n; + val.x = _this->inSFColor.red; + val.y = _this->inSFColor.green; + val.z = _this->inSFColor.blue; + val.q = 0; + SetValuatorOutput(_this, &val, NULL, GF_SG_VRML_SFCOLOR); +} +static void Valuator_SetInSFVec2f(GF_Node *n) +{ + SFVec4f val; + M_Valuator *_this = (M_Valuator *) n; + val.x = _this->inSFVec2f.x; + val.y = _this->inSFVec2f.y; + val.z = val.q = 0; + SetValuatorOutput(_this, &val, NULL, GF_SG_VRML_SFVEC2F); +} +static void Valuator_SetInSFVec3f(GF_Node *n) +{ + SFVec4f val; + M_Valuator *_this = (M_Valuator *) n; + val.x = _this->inSFVec3f.x; + val.y = _this->inSFVec3f.y; + val.z = _this->inSFVec3f.z; + val.q = 0; + SetValuatorOutput(_this, &val, NULL, GF_SG_VRML_SFVEC3F); +} +static void Valuator_SetInSFRotation(GF_Node *n) +{ + SFVec4f val; + M_Valuator *_this = (M_Valuator *) n; + val = _this->inSFRotation; + SetValuatorOutput(_this, &val, NULL, GF_SG_VRML_SFROTATION); +} + +/* +valuator spec (9.4.2.116.2) +Convert if the content of the string represents an int, float or +double value. ‘Boolean’ string values 'true' and 'false' are +converted to 1.0 and 0.0 respectively. Any other string is converted to 0.0 +*/ +static void Valuator_SetInSFString(GF_Node *n) +{ + SFVec4f val; + M_Valuator *_this = (M_Valuator *) n; + val.x = val.y = val.z = val.q = 0; + if (! _this->inSFString.buffer) return; + if (!stricmp(_this->inSFString.buffer, "true")) { + val.x = val.y = val.z = val.q = FIX_ONE; + } else if (!strstr(_this->inSFString.buffer, ".")) { + val.x = INT2FIX( atoi(_this->inSFString.buffer) ); + val.y = val.z = val.q = val.x; + } else { + val.x = FLT2FIX( atof(_this->inSFString.buffer) ); + val.y = val.z = val.q = val.x; + } + SetValuatorOutput(_this, &val, NULL, GF_SG_VRML_SFSTRING); +} + +static void Valuator_SetInMFColor(GF_Node *n) +{ + M_Valuator *_this = (M_Valuator *) n; + SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFColor, GF_SG_VRML_MFCOLOR); +} + +static void Valuator_SetInMFFloat(GF_Node *n) +{ + M_Valuator *_this = (M_Valuator *) n; + SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFFloat, GF_SG_VRML_MFFLOAT); +} +static void Valuator_SetInMFInt32(GF_Node *n) +{ + M_Valuator *_this = (M_Valuator *) n; + SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFInt32, GF_SG_VRML_MFINT32); +} +static void Valuator_SetInMFVec2f(GF_Node *n) +{ + M_Valuator *_this = (M_Valuator *) n; + SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFVec2f, GF_SG_VRML_MFVEC2F); +} +static void Valuator_SetInMFVec3f(GF_Node *n) +{ + M_Valuator *_this = (M_Valuator *) n; + SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFVec3f, GF_SG_VRML_MFVEC3F); +} +static void Valuator_SetInMFRotation(GF_Node *n) +{ + M_Valuator *_this = (M_Valuator *) n; + SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFRotation, GF_SG_VRML_MFROTATION); +} +static void Valuator_SetInMFString(GF_Node *n) +{ + M_Valuator *_this = (M_Valuator *) n; + SetValuatorOutput(_this, NULL, (GenMFField *) &_this->inMFString, GF_SG_VRML_MFSTRING); +} + +Bool InitValuator(M_Valuator *node) +{ + node->on_inSFTime = Valuator_SetInSFTime; + node->on_inSFBool = Valuator_SetInSFBool; + node->on_inSFColor = Valuator_SetInSFColor; + node->on_inSFInt32 = Valuator_SetInSFInt32; + node->on_inSFFloat = Valuator_SetInSFFloat; + node->on_inSFVec2f = Valuator_SetInSFVec2f; + node->on_inSFVec3f = Valuator_SetInSFVec3f; + node->on_inSFRotation = Valuator_SetInSFRotation; + node->on_inSFString = Valuator_SetInSFString; + node->on_inMFColor = Valuator_SetInMFColor; + node->on_inMFInt32 = Valuator_SetInMFInt32; + node->on_inMFFloat = Valuator_SetInMFFloat; + node->on_inMFVec2f = Valuator_SetInMFVec2f; + node->on_inMFVec3f = Valuator_SetInMFVec3f; + node->on_inMFRotation = Valuator_SetInMFRotation; + node->on_inMFString = Valuator_SetInMFString; + return 1; +} diff --git a/src/scenegraph/smil_anim.c b/src/scenegraph/smil_anim.c new file mode 100644 index 0000000..3f65d8b --- /dev/null +++ b/src/scenegraph/smil_anim.c @@ -0,0 +1,1462 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean Le Feuvre + * Copyright (c)2004-200X ENST - All rights reserved + * + * This file is part of GPAC / SVG Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#ifndef GPAC_DISABLE_LOG +u32 time_spent_in_anim = 0; +#endif + +#ifndef GPAC_DISABLE_SVG + + +/************************************************************************************** + * Each GF_Node holds the (SVG/SMIL) animation elements which target itself in a list * + * The following are the generic functions to manipulate this list: * + * - add a new animation to the list, * + * - get an animation from the list, * + * - remove an animation from the list, * + * - count the animations in the list, * + * - delete the list * + **************************************************************************************/ +GF_Err gf_node_animation_add(GF_Node *node, void *animation) +{ + if (!node || !animation) return GF_BAD_PARAM; + if (!node->sgprivate->interact) GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); + if (!node->sgprivate->interact->animations) node->sgprivate->interact->animations = gf_list_new(); + return gf_list_add(node->sgprivate->interact->animations, animation); +} + +GF_Err gf_node_animation_del(GF_Node *node) +{ + if (!node || !node->sgprivate->interact || !node->sgprivate->interact->animations) return GF_BAD_PARAM; + gf_list_del(node->sgprivate->interact->animations); + node->sgprivate->interact->animations = NULL; + return GF_OK; +} + +u32 gf_node_animation_count(GF_Node *node) +{ + if (!node || !node->sgprivate->interact|| !node->sgprivate->interact->animations) return 0; + return gf_list_count(node->sgprivate->interact->animations); +} + +void *gf_node_animation_get(GF_Node *node, u32 i) +{ + if (!node || !node->sgprivate->interact || !node->sgprivate->interact->animations) return 0; + return gf_list_get(node->sgprivate->interact->animations, i); +} + +GF_Err gf_node_animation_rem(GF_Node *node, u32 i) +{ + if (!node || !node->sgprivate->interact || !node->sgprivate->interact->animations) return GF_OK; + return gf_list_rem(node->sgprivate->interact->animations, i); +} +/************************************************************************************** + * End of Generic GF_Node animations list * + **************************************************************************************/ + + +/************************************************************************************** + * Helping functions for animation * + **************************************************************************************/ +/* Sets the pointer to the attribute value with the pointer + to the value which passed (if unspecified) */ +void gf_svg_attributes_resolve_unspecified(GF_FieldInfo *in, GF_FieldInfo *p, GF_FieldInfo *t) +{ + if (in->fieldType == 0) { + if (p->fieldType == SVG_Transform_datatype) { + /* if the input value is not specified, and the presentation value is of type Transform, + then we should use the default identity transform instead of the presentation value */ + *in = *t; + } else { + *in = *p; + } + } +} + +/* Replaces the pointer to the attribute value with the pointer + to the value which is inherited (if inherited) */ +void gf_svg_attributes_resolve_inherit(GF_FieldInfo *in, GF_FieldInfo *prop) +{ + if (gf_svg_is_inherit(in)) *in = *prop; +} + +/* Replaces the pointer to the attribute value with the pointer + to the value of the color attribute (if the current value is set to currentColor) */ +void gf_svg_attributes_resolve_currentColor(GF_FieldInfo *in, GF_FieldInfo *current_color) +{ + if ((in->fieldType == SVG_Paint_datatype) && gf_svg_is_current_color(in)) { + *in = *current_color; + } +} + +/************************************************************************************** + * The main function doing evaluation of the animation is: gf_smil_anim_evaluate * + * Depending on the timing status of the animation it calls: * + * - gf_smil_anim_animate * + * - gf_smil_anim_animate_with_fraction * + * - gf_smil_anim_freeze * + * - gf_smil_anim_remove * + * * + * The gf_smil_anim_animate consists in * + * - interpolating using gf_smil_anim_compute_interpolation_value * + * - accumulating using gf_smil_anim_apply_accumulate * + * - applying additive behavior * + * * + * Depending on the animation attributes, one of the following functions is called * + * by the function gf_smil_anim_compute_interpolation_value * + * - gf_smil_anim_set * + * - gf_smil_anim_animate_using_values * + * - gf_smil_anim_animate_from_to * + * - gf_smil_anim_animate_from_by * + * - gf_smil_anim_animate_using_path * + * * + * In most animation methods, the important step in the animation is to resolve * + * the inherit and currentColor values to perform further interpolation, i.e. calls: * + * gf_svg_attributes_resolve_currentColor(&info, &rai->owner->current_color_value); * + * gf_svg_attributes_resolve_inherit(&info, &rai->owner->parent_presentation_value); * + * * + **************************************************************************************/ +static void gf_smil_anim_set(SMIL_Anim_RTI *rai) +{ + GF_FieldInfo to_info; + SMILAnimationAttributesPointers *animp = rai->animp; + + if (!animp->to || !animp->to->type) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, + ("[SMIL Animation] Animation %s - set element without to attribute\n", + gf_node_get_log_name((GF_Node *)rai->anim_elt))); + return; + } + + if (rai->change_detection_mode) { + /* if the set has been applied, unless next animations are additive we don't need + to apply it again */ + if (rai->previous_coef > 0) rai->interpolated_value_changed = 0; + else rai->interpolated_value_changed = 1; + return; + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying set animation\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), + gf_node_get_log_name((GF_Node *)rai->anim_elt))); + + to_info.fieldType = animp->to->type; + to_info.far_ptr = animp->to->value; + /* we do not need to resolve currentColor values or inherit values here, + because no further interpolation is required for the animation and + because inheritance is applied after animations in the compositor. */ + + gf_svg_attributes_copy(&rai->interpolated_value, &to_info, 0); + rai->previous_coef = FIX_ONE; + } +} + +static void gf_smil_anim_use_keypoints_keytimes(SMIL_Anim_RTI *rai, Fixed normalized_simple_time, + Fixed *interpolation_coefficient, u32 *keyValueIndex) +{ + SMILAnimationAttributesPointers *animp = rai->animp; + u32 keyTimeIndex = 0; + Fixed interval_duration; + + *interpolation_coefficient = normalized_simple_time; + + /* Computing new interpolation coefficient */ + if (rai->key_times_count) { + Fixed keyTimeBefore = 0, keyTimeAfter=0; + for (keyTimeIndex = rai->previous_keytime_index; keyTimeIndex< rai->key_times_count; keyTimeIndex++) { + Fixed *tm1, *t = (Fixed *)gf_list_get(*animp->keyTimes, keyTimeIndex); + if (normalized_simple_time < *t) { + rai->previous_keytime_index = keyTimeIndex; + tm1 = (Fixed *) gf_list_get(*animp->keyTimes, keyTimeIndex-1); + if (tm1) keyTimeBefore = *tm1; + else keyTimeBefore = 0; + keyTimeAfter = *t; + break; + } + } + keyTimeIndex--; + interval_duration = keyTimeAfter - keyTimeBefore; + if (keyValueIndex) *keyValueIndex = keyTimeIndex; + if (interval_duration) + *interpolation_coefficient = gf_divfix(normalized_simple_time - keyTimeBefore, interval_duration); + else + *interpolation_coefficient = FIX_ONE; + if (!rai->change_detection_mode) + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - Using Key Times: index %d, interval duration %.2f, coeff: %.2f\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), + gf_node_get_log_name((GF_Node *)rai->anim_elt), + keyTimeIndex, + interval_duration, + interpolation_coefficient)); + } + + if (rai->anim_elt->sgprivate->tag == TAG_SVG_animateMotion && rai->key_points_count) { + Fixed *p1, *p2; + p1 = (Fixed *)gf_list_get(*animp->keyPoints, keyTimeIndex); + if (animp->calcMode && *animp->calcMode == SMIL_CALCMODE_DISCRETE) { + *interpolation_coefficient = *p1; + } else { + p2 = (Fixed *)gf_list_get(*animp->keyPoints, keyTimeIndex+1); + *interpolation_coefficient = gf_mulfix(FIX_ONE - *interpolation_coefficient, *p1) + + gf_mulfix(*interpolation_coefficient, (p2 ? *p2 : *p1)); + } + if (keyValueIndex) *keyValueIndex = 0; + if (!rai->change_detection_mode) + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - Using Key Points: key Point Index %d, coeff: %.2f\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyTimeIndex, *interpolation_coefficient)); + } +} + +static void gf_smil_anim_animate_using_values(SMIL_Anim_RTI *rai, Fixed normalized_simple_time) +{ + SMILAnimationAttributesPointers *animp = rai->animp; + GF_List *values = NULL; + GF_FieldInfo value_info, value_info_next; + u32 keyValueIndex; + Fixed interpolation_coefficient; + u32 real_calcMode; + + values = animp->values->values; + + memset(&value_info, 0, sizeof(GF_FieldInfo)); + value_info.fieldType = animp->values->type; + value_info_next = value_info; + + real_calcMode = (gf_svg_attribute_is_interpolatable(animp->values->type)? + (animp->calcMode ? *animp->calcMode : SMIL_CALCMODE_LINEAR): + SMIL_CALCMODE_DISCRETE + ); + + if (rai->values_count == 1) { + if (rai->change_detection_mode) { + /* Since we have only 1 value, the previous key index should always be 0, + unless the animation has not started or is reset (-1) */ + if (rai->previous_key_index == 0) rai->interpolated_value_changed = 0; + else rai->interpolated_value_changed = 1; + return; + } else { + value_info.far_ptr = gf_list_get(values, 0); + /* no further interpolation needed + therefore no need to resolve inherit and currentColor */ + gf_svg_attributes_copy(&rai->interpolated_value, &value_info, 0); + rai->previous_key_index = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - Using values[0] as interpolation value\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); + return; + } + } + + /* Computing new key value index and interpolation coefficient */ + if (!rai->key_times_count) { + if (real_calcMode == SMIL_CALCMODE_DISCRETE) { + if (normalized_simple_time == FIX_ONE) { + keyValueIndex = rai->values_count-1; + interpolation_coefficient = FIX_ONE; + } else { + Fixed tmp = normalized_simple_time*rai->values_count; + Fixed tmp_floor = gf_floor(tmp); + if ((tmp - tmp_floor) == 0 && tmp) { + keyValueIndex = FIX2INT(tmp_floor) - 1; + } else { + keyValueIndex = FIX2INT(tmp_floor); + } + interpolation_coefficient = tmp - INT2FIX(keyValueIndex); + } + } else { + Fixed tmp = normalized_simple_time*(rai->values_count-1); + if (normalized_simple_time == FIX_ONE) { + keyValueIndex = rai->values_count-2; + } else { + keyValueIndex = FIX2INT(gf_floor(tmp)); + } + interpolation_coefficient = tmp - INT2FIX(keyValueIndex); + } + //GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Animation] Time %f - Animation %s - No KeyTimes: key index %d, coeff: %.2f\n", gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex, FIX2FLT(interpolation_coefficient))); + } else { + gf_smil_anim_use_keypoints_keytimes(rai, normalized_simple_time, &interpolation_coefficient, &keyValueIndex); + } + + if (rai->change_detection_mode) { + if (real_calcMode == SMIL_CALCMODE_DISCRETE && rai->previous_key_index == (s32)keyValueIndex && rai->previous_coef != -FIX_ONE) { + rai->interpolated_value_changed = 0; + } else if (rai->previous_key_index == (s32)keyValueIndex && rai->previous_coef == interpolation_coefficient) + rai->interpolated_value_changed = 0; + else + rai->interpolated_value_changed = 1; + } else { + rai->previous_key_index = keyValueIndex; + rai->previous_coef = interpolation_coefficient; + + switch (real_calcMode) { + case SMIL_CALCMODE_DISCRETE: + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying discrete animation using values (key value index: %d)\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex)); + value_info.far_ptr = gf_list_get(values, keyValueIndex); + /* no further interpolation needed + therefore no need to resolve inherit and currentColor */ + gf_svg_attributes_copy(&rai->interpolated_value, &value_info, 0); + break; + case SMIL_CALCMODE_PACED: + /* TODO: at the moment assume it is linear */ + case SMIL_CALCMODE_SPLINE: + /* TODO: at the moment assume it is linear */ + case SMIL_CALCMODE_LINEAR: + if (keyValueIndex == rai->values_count - 1) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying linear animation using values (setting last key value: %d)\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex)); + value_info.far_ptr = gf_list_get(values, rai->values_count - 1); + /* no further interpolation needed + therefore no need to resolve inherit and currentColor */ + gf_svg_attributes_copy(&rai->interpolated_value, &value_info, 0); + } else { + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying linear animation using values (key value indices: %d, %d / coeff: %f)\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), keyValueIndex, keyValueIndex+1, interpolation_coefficient)); + value_info.far_ptr = gf_list_get(values, keyValueIndex); + if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(animp->values->type)) { + gf_svg_attributes_resolve_currentColor(&value_info, &rai->owner->current_color_value); + gf_svg_attributes_resolve_inherit(&value_info, &rai->owner->parent_presentation_value); + } + + value_info_next.far_ptr = gf_list_get(values, keyValueIndex+1); + if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(animp->values->type)) { + gf_svg_attributes_resolve_currentColor(&value_info_next, &rai->owner->current_color_value); + gf_svg_attributes_resolve_inherit(&value_info_next, &rai->owner->parent_presentation_value); + } + + gf_svg_attributes_interpolate(&value_info, + &value_info_next, + &rai->interpolated_value, + interpolation_coefficient, 1); + } + break; + } + } +} + +static void gf_smil_anim_animate_from_to(SMIL_Anim_RTI *rai, Fixed normalized_simple_time) +{ + GF_FieldInfo from_info, to_info; + SMILAnimationAttributesPointers *animp = rai->animp; + Fixed interpolation_coefficient; + s32 useFrom = (normalized_simple_time<=FIX_ONE/2); + u32 real_calcMode; + + real_calcMode = (animp->to && gf_svg_attribute_is_interpolatable(animp->to->type)? + (animp->calcMode ? *animp->calcMode : SMIL_CALCMODE_LINEAR): + SMIL_CALCMODE_DISCRETE + ); + + if (rai->change_detection_mode) { + if (rai->previous_coef == normalized_simple_time) + rai->interpolated_value_changed = 0; + else { + if (real_calcMode == SMIL_CALCMODE_DISCRETE && + useFrom == rai->previous_key_index) { + rai->interpolated_value_changed = 0; + } else { + rai->interpolated_value_changed = 1; + } + } + } else { + + if (animp->from) { + from_info.fieldType = animp->from->type; + from_info.far_ptr = animp->from->value; + } else { + from_info.fieldType = 0; + from_info.far_ptr = NULL; + } + + if (rai->is_first_anim) + gf_svg_attributes_resolve_unspecified(&from_info, + &rai->owner->specified_value, + &rai->default_transform_value); + else + gf_svg_attributes_resolve_unspecified(&from_info, + &rai->owner->presentation_value, + &rai->default_transform_value); + + if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(from_info.fieldType)) { + gf_svg_attributes_resolve_currentColor(&from_info, &rai->owner->current_color_value); + gf_svg_attributes_resolve_inherit(&from_info, &rai->owner->parent_presentation_value); + } + if (animp->to) { + to_info.fieldType = animp->to->type; + to_info.far_ptr = animp->to->value; + } else { + to_info.fieldType = 0; + to_info.far_ptr = NULL; + } + + if (rai->is_first_anim) + gf_svg_attributes_resolve_unspecified(&to_info, + &rai->owner->specified_value, + &rai->default_transform_value); + else + gf_svg_attributes_resolve_unspecified(&to_info, + &rai->owner->presentation_value, + &rai->default_transform_value); + + if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(to_info.fieldType)) { + gf_svg_attributes_resolve_currentColor(&to_info, &rai->owner->current_color_value); + gf_svg_attributes_resolve_inherit(&to_info, &rai->owner->parent_presentation_value); + } + + gf_smil_anim_use_keypoints_keytimes(rai, normalized_simple_time, &interpolation_coefficient, NULL); + + rai->previous_coef = interpolation_coefficient; + + switch (real_calcMode) { + case SMIL_CALCMODE_DISCRETE: + { + /* before half of the duration stay at 'from' and then switch to 'to' */ + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying from-to animation (using %s value)\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), (useFrom?"from":"to"))); + gf_svg_attributes_copy(&rai->interpolated_value, (useFrom?&from_info:&to_info), 0); + rai->previous_key_index = useFrom; + } + break; + case SMIL_CALCMODE_SPLINE: + case SMIL_CALCMODE_PACED: + case SMIL_CALCMODE_LINEAR: + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying from-to animation (linear interpolation, using coefficient %f)\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), interpolation_coefficient)); + gf_svg_attributes_interpolate(&from_info, &to_info, &rai->interpolated_value, interpolation_coefficient, 1); + break; + } + } +} + +static void gf_smil_anim_animate_from_by(SMIL_Anim_RTI *rai, Fixed normalized_simple_time) +{ + Fixed from_coef; + GF_FieldInfo from_info, by_info; + SMILAnimationAttributesPointers *animp = rai->animp; + s32 useFrom = (normalized_simple_time<=FIX_ONE/2); + + if (rai->change_detection_mode) { + if (rai->previous_coef == normalized_simple_time) + rai->interpolated_value_changed = 0; + else { + if (animp->calcMode && + *animp->calcMode == SMIL_CALCMODE_DISCRETE && + useFrom == rai->previous_key_index) { + rai->interpolated_value_changed = 0; + } else { + rai->interpolated_value_changed = 1; + } + } + } else { + rai->previous_coef = normalized_simple_time; + + if (animp->from) { + from_info.fieldType = animp->from->type; + from_info.far_ptr = animp->from->value; + from_coef = FIX_ONE; + } else { + from_info.fieldType = 0; + from_info.far_ptr = NULL; + /* this is a by animation only, then, it is always additive, + we don't need the from value*/ + from_coef = 0; + } + + if (rai->is_first_anim) + gf_svg_attributes_resolve_unspecified(&from_info, + &rai->owner->specified_value, + &rai->default_transform_value); + else + gf_svg_attributes_resolve_unspecified(&from_info, + &rai->owner->presentation_value, + &rai->default_transform_value); + + if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(from_info.fieldType)) { + gf_svg_attributes_resolve_currentColor(&from_info, &rai->owner->current_color_value); + gf_svg_attributes_resolve_inherit(&from_info, &rai->owner->parent_presentation_value); + } + + if (animp->by) { + by_info.fieldType = animp->by->type; + by_info.far_ptr = animp->by->value; + } else { + by_info.fieldType = 0; + by_info.far_ptr = NULL; + } + + if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(from_info.fieldType)) { + gf_svg_attributes_resolve_currentColor(&by_info, &rai->owner->current_color_value); + gf_svg_attributes_resolve_inherit(&by_info, &rai->owner->parent_presentation_value); + } + + switch ((animp->calcMode ? *animp->calcMode : SMIL_CALCMODE_LINEAR)) { + case SMIL_CALCMODE_DISCRETE: + { + /* before half of the duration stay at 'from' and then switch to 'to' */ + if (useFrom) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying from-by animation (setting from)", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); + gf_svg_attributes_muladd(from_coef, &from_info, 0, &by_info, &rai->interpolated_value, 0); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying from-by animation (setting from+by)", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); + gf_svg_attributes_muladd(from_coef, &from_info, FIX_ONE, &by_info, &rai->interpolated_value, 0); + } + rai->previous_key_index = useFrom; + } + break; + case SMIL_CALCMODE_SPLINE: + case SMIL_CALCMODE_PACED: + case SMIL_CALCMODE_LINEAR: + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying from-by animation (linear interpolation between from and from+by, coef: %f)\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), normalized_simple_time)); + gf_svg_attributes_muladd(from_coef, &from_info, normalized_simple_time, &by_info, &rai->interpolated_value, 0); + break; + } + } +} + +static void gf_svg_compute_path_anim(SMIL_Anim_RTI *rai, GF_Matrix2D *m, Fixed normalized_simple_time) +{ + Fixed offset; + offset = gf_mulfix(normalized_simple_time, rai->length); + gf_mx2d_init(*m); + + gf_path_iterator_get_transform(rai->path_iterator, offset, 1, m, 1, 0); + //GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("offset: %f, position: (%f, %f)", offset, ((GF_Matrix2D *)rai->interpolated_value.far_ptr)->m[2], ((GF_Matrix2D *)rai->interpolated_value.far_ptr)->m[5])); + switch (rai->rotate) { + case SVG_NUMBER_AUTO: + break; + case SVG_NUMBER_AUTO_REVERSE: + gf_mx2d_add_rotation(m, m->m[2], m->m[5], GF_PI); + break; + default: + m->m[0] = FIX_ONE; + m->m[1] = 0; + m->m[3] = 0; + m->m[4] = FIX_ONE; + } +} + +static void gf_smil_anim_animate_using_path(SMIL_Anim_RTI *rai, Fixed normalized_simple_time) +{ + Fixed interpolation_coefficient; + + gf_smil_anim_use_keypoints_keytimes(rai, normalized_simple_time, &interpolation_coefficient, NULL); + + if (rai->change_detection_mode) { + if (rai->previous_coef == interpolation_coefficient) + rai->interpolated_value_changed = 0; + else { + rai->interpolated_value_changed = 1; + } + } else { + rai->previous_coef = interpolation_coefficient; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying path animation (coef: %f)\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), normalized_simple_time)); + + gf_svg_compute_path_anim(rai, (GF_Matrix2D*)rai->interpolated_value.far_ptr, interpolation_coefficient); + } +} + +static void gf_smil_anim_compute_interpolation_value(SMIL_Anim_RTI *rai, Fixed normalized_simple_time) +{ + SMILAnimationAttributesPointers *animp = rai->animp; + + if (rai->path) { + gf_smil_anim_animate_using_path(rai, normalized_simple_time); + } else if (rai->anim_elt->sgprivate->tag == TAG_SVG_set) { + gf_smil_anim_set(rai); + } else if (rai->values_count) { + /* Ignore 'from'/'to'/'by'*/ + gf_smil_anim_animate_using_values(rai, normalized_simple_time); + } else if ((animp->by && animp->by->type) && (!animp->to || animp->to->type == 0)) { + /* 'to' is not specified but 'by' is, so this is a 'by' animation or a 'from'-'by' animation */ + gf_smil_anim_animate_from_by(rai, normalized_simple_time); + } else { + /* Ignore 'by' if specified */ + gf_smil_anim_animate_from_to(rai, normalized_simple_time); + } + +#ifndef GPAC_DISABLE_LOG + if (0 && (gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_SMIL)) { + char str[1000]; + gf_log_lt(GF_LOG_DEBUG, GF_LOG_SMIL); + gf_svg_dump_attribute(rai->anim_elt, &rai->interpolated_value, str); + assert(strlen(str) < 1000); + gf_log("[SMIL Animation] Time %f - Animation %s - Interpolation value changed for attribute %s, new value: %s \n", + gf_node_get_scene_time(rai->anim_elt), gf_node_get_log_name(rai->anim_elt), + gf_svg_get_attribute_name(rai->anim_elt, rai->owner->presentation_value.fieldIndex), str); + } +#endif +} + +void gf_smil_anim_set_anim_runtime_in_timing(GF_Node *n) +{ + u32 i, j; + SVGTimedAnimBaseElement *timed_elt = NULL; + SMIL_Timing_RTI *rti = NULL; + GF_Node *target = NULL; + + if (!n) return; + timed_elt = (SVGTimedAnimBaseElement *)n; + + if (!gf_svg_is_animation_tag(n->sgprivate->tag)) return; + + target = timed_elt->xlinkp->href->target; + if (!target) return; + + if (timed_elt->timingp) rti = timed_elt->timingp->runtime; + if (!rti) return; + + rti->rai = NULL; + + for (i = 0; i < gf_node_animation_count(target); i++) { + SMIL_Anim_RTI *rai_tmp; + SMIL_AttributeAnimations *aa = (SMIL_AttributeAnimations *)gf_node_animation_get(target, i); + j=0; + while ((rai_tmp = (SMIL_Anim_RTI *)gf_list_enum(aa->anims, &j))) { + if (rai_tmp->timingp->runtime == rti) { + rti->rai = rai_tmp; + return; + } + } + } +} + +static void gf_smil_anim_get_last_specified_value(SMIL_Anim_RTI *rai) +{ + SMILAnimationAttributesPointers *animp = rai->animp; + + if (!animp) return; + + if (rai->path) { + if (!rai->last_specified_value.far_ptr) rai->last_specified_value.far_ptr = malloc(sizeof(GF_Matrix2D)); + gf_svg_compute_path_anim(rai, rai->last_specified_value.far_ptr, FIX_ONE); + return; + } else if (rai->anim_elt->sgprivate->tag == TAG_SVG_set) { + if (animp->to) { + rai->last_specified_value.fieldType = animp->to->type; + rai->last_specified_value.far_ptr = animp->to->value; + } else { + /* TODO ??? */ + GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, + ("[SMIL Animation] Animation %s - set element without to attribute\n", + gf_node_get_log_name((GF_Node *)rai->anim_elt))); + } + return; + } + + if (rai->values_count) { + /* Ignore from/to/by*/ + rai->last_specified_value.fieldType = animp->values->type; + rai->last_specified_value.far_ptr = gf_list_last(animp->values->values); + } else if ((animp->by && animp->by->type) && (!animp->to || animp->to->type == 0)) { + rai->last_specified_value.fieldType = animp->by->type; + rai->last_specified_value.far_ptr = animp->by->value; + } else if (animp->to) { + rai->last_specified_value.fieldType = animp->to->type; + rai->last_specified_value.far_ptr = animp->to->value; + } + if (gf_svg_is_inherit(&rai->last_specified_value)) { + rai->last_specified_value.fieldType = rai->owner->presentation_value.fieldType; + rai->last_specified_value.far_ptr = rai->owner->presentation_value.far_ptr; + } + if (rai->owner->is_property && gf_svg_attribute_is_interpolatable(rai->last_specified_value.fieldType)) { + gf_svg_attributes_resolve_currentColor(&rai->last_specified_value, &rai->owner->current_color_value); + gf_svg_attributes_resolve_inherit(&rai->last_specified_value, &rai->owner->parent_presentation_value); + } +} + +/* if the animation behavior is accumulative and this is not the first iteration, + then we modify the interpolation value as follows: + interpolation value += last specified value * number of iterations completed */ +static void gf_smil_anim_apply_accumulate(SMIL_Anim_RTI *rai) +{ + u32 nb_iterations; + + SMILAnimationAttributesPointers *animp = rai->animp; + SMILTimingAttributesPointers *timingp = rai->timingp; + + nb_iterations = (timingp->runtime->current_interval ? timingp->runtime->current_interval->nb_iterations : 1); + + if (rai->change_detection_mode) { + if ((animp->accumulate && *animp->accumulate == SMIL_ACCUMULATE_SUM) + && nb_iterations > 0 + && rai->previous_iteration != (s32) nb_iterations) { + /* if we actually do accumulation and the number of iteration is different, + then we force the result as changed regardless of the result of the interpolation + (TODO: check if this need to be improved)*/ + rai->interpolated_value_changed = 1; + } else { + /* if we don't accumulate we leave the value of interpolated_value_changed unchanged */ + } + } else { + if (nb_iterations > 0 && rai->previous_iteration != (s32) nb_iterations) { + rai->previous_iteration = nb_iterations; + } + + if ((animp->accumulate && *animp->accumulate == SMIL_ACCUMULATE_SUM) && nb_iterations > 0) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying accumulation (iteration #%d)\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt), nb_iterations)); + + gf_svg_attributes_muladd(FIX_ONE, &rai->interpolated_value, + INT2FIX(nb_iterations), &rai->last_specified_value, + &rai->interpolated_value, 1); + + if ((animp->from) && animp->by && (rai->last_specified_value.far_ptr == animp->by->value)) { + /* this is a from-by animation, the last specified value is not the 'by' value but actually 'from'+'by', + we need to add nb_iterations times from to the interpolated_value + see (animate-elem-210-t.svg (upper two circles in the mid column, after 9s/14s */ + GF_FieldInfo from_info; + from_info.fieldType = rai->animp->from->type; + from_info.far_ptr = rai->animp->from->value; + gf_svg_attributes_muladd(FIX_ONE, &rai->interpolated_value, + INT2FIX(nb_iterations), &from_info, + &rai->interpolated_value, 1); + } + } + } +} + +static void gf_smil_apply_additive(SMIL_Anim_RTI *rai) +{ + SMILAnimationAttributesPointers *animp = rai->animp; + if (rai->change_detection_mode) return; + else { + /* Apply additive behavior if required + PV = (additive == sum ? PV + animp->IV : animp->IV); */ + if (animp->additive && *animp->additive == SMIL_ADDITIVE_SUM) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying additive behavior\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); + + gf_svg_attributes_add((rai->is_first_anim ? &rai->owner->specified_value : &rai->owner->presentation_value), + &rai->interpolated_value, + &rai->owner->presentation_value, + 1); + +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_SMIL)) { + char str[1000]; + gf_log_lt(GF_LOG_DEBUG, GF_LOG_SMIL); + gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value, str); + assert(strlen(str) < 1000); + gf_log("[SMIL Animation] Time %f - Animation %s - Presentation value changed for attribute %s, new value: %s\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node*)rai->anim_elt), + gf_svg_get_attribute_name((GF_Node*)rai->anim_elt, rai->owner->presentation_value.fieldIndex), str); + } +#endif + + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying non-additive behavior\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); + + /* FIXME: if we switch pointers to avoid copying values, + we need to modify the address in the DOM node as well, + we also need to take care about change detections. Not easy!! + + void *tmp = rai->owner->presentation_value.far_ptr; + rai->owner->presentation_value.far_ptr = rai->interpolated_value.far_ptr; + rai->interpolated_value.far_ptr = tmp; + */ + + gf_svg_attributes_copy(&rai->owner->presentation_value, &rai->interpolated_value, 1); +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_SMIL)) { + char str[1000]; + gf_log_lt(GF_LOG_DEBUG, GF_LOG_SMIL); + gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value, str); + assert(strlen(str) < 1000); + gf_log("[SMIL Animation] Time %f - Animation %s - Presentation value changed for attribute %s, new value: %s\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node*)rai->anim_elt), + gf_svg_get_attribute_name((GF_Node*)rai->anim_elt, rai->owner->presentation_value.fieldIndex), str); + } +#endif + } + } +} + +static void gf_smil_anim_animate(SMIL_Timing_RTI *rti, Fixed normalized_simple_time) +{ + SMIL_Anim_RTI *rai = rti->rai; + SMILAnimationAttributesPointers *animp = rai->animp; + + if (!rai || !animp) return; + + gf_smil_anim_compute_interpolation_value(rai, normalized_simple_time); + gf_smil_anim_apply_accumulate(rai); + gf_smil_apply_additive(rai); +} + +static void gf_smil_anim_animate_with_fraction(SMIL_Timing_RTI *rti, Fixed normalized_simple_time) +{ + gf_smil_anim_animate(rti, rti->fraction); + rti->evaluate_status = SMIL_TIMING_EVAL_NONE; +} + +void gf_smil_anim_reset_variables(SMIL_Anim_RTI *rai) +{ + if (!rai) return; + /* we reset all the animation parameters to force computation of next interpolation value + when the animation restarts */ + rai->interpolated_value_changed = 0; + rai->previous_key_index = -1; + rai->previous_coef = -FIX_ONE; + rai->previous_iteration = -1; + rai->previous_keytime_index = 0; + rai->anim_done = 0; +} + +/* copy/paste of the animate function + TODO: check if computations of interpolation value can be avoided. +*/ +static void gf_smil_anim_freeze(SMIL_Timing_RTI *rti, Fixed normalized_simple_time) +{ + SMIL_Anim_RTI *rai = rti->rai; + SMILAnimationAttributesPointers *animp = rai->animp; + + if (!rai || !animp) return; + + if (rai->change_detection_mode) { + if (rai->anim_done == 0) + rai->interpolated_value_changed = 1; + else + rai->interpolated_value_changed = 0; + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying freeze behavior\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); + + gf_smil_anim_compute_interpolation_value(rai, normalized_simple_time); + gf_smil_anim_apply_accumulate(rai); + gf_smil_apply_additive(rai); + rai->anim_done = 1; + } +} + +static void gf_smil_anim_remove(SMIL_Timing_RTI *rti, Fixed normalized_simple_time) +{ + SMIL_Anim_RTI *rai = rti->rai; + if (!rai) return; + + if (rai->change_detection_mode) { + if (rai->anim_done == 0) + rai->interpolated_value_changed = 1; + else + rai->interpolated_value_changed = 0; + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, + ("[SMIL Animation] Time %f - Animation %s - applying remove behavior\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node *)rai->anim_elt))); + + /* TODO: see if we can avoid this copy by switching pointers */ + gf_svg_attributes_copy(&rai->owner->presentation_value, &rai->owner->specified_value, 0); + /* TODO: check if we need to apply additive behavior even in fill='remove' + maybe (see animate-elem-211-t.svg) */ + + rai->anim_done = 1; + +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_SMIL)) { + char str[1000]; + gf_log_lt(GF_LOG_DEBUG, GF_LOG_SMIL); + gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value, str); + assert(strlen(str) < 1000); + gf_log("[SMIL Animation] Time %f - Animation %s - Presentation value changed for attribute %s, new value: %s\n", + gf_node_get_scene_time((GF_Node*)rai->anim_elt), gf_node_get_log_name((GF_Node*)rai->anim_elt), + gf_svg_get_attribute_name((GF_Node*)rai->anim_elt, rai->owner->presentation_value.fieldIndex), str); + } +#endif + + } +} + +static void gf_smil_anim_evaluate(SMIL_Timing_RTI *rti, Fixed normalized_simple_time, u32 state) +{ + SMIL_Anim_RTI *rai = rti->rai; + switch (state) { + case SMIL_TIMING_EVAL_REPEAT: + /* we are starting a new cycle of animation, therefore we need to reset the previous state variables + like previous_keytime_index ... */ + gf_smil_anim_reset_variables(rai); + case SMIL_TIMING_EVAL_UPDATE: + gf_smil_anim_animate(rti, normalized_simple_time); + break; + case SMIL_TIMING_EVAL_FREEZE: + gf_smil_anim_freeze(rti, normalized_simple_time); + break; + case SMIL_TIMING_EVAL_REMOVE: + gf_smil_anim_remove(rti, normalized_simple_time); + break; + case SMIL_TIMING_EVAL_FRACTION: + gf_smil_anim_animate_with_fraction(rti, normalized_simple_time); + break; +/* + discard should be done before in smil_notify_time + case SMIL_TIMING_EVAL_DISCARD: + break; +*/ + } +} +/************************************************************************************** + **************************************************************************************/ + +GF_EXPORT +void gf_svg_apply_animations(GF_Node *node, SVGPropertiesPointers *render_svg_props) +{ + u32 count_all, i; +#ifndef GPAC_DISABLE_LOG + u32 time=0; + u32 active_anim; + + if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_RTI)) { + time = gf_sys_clock(); + } +#endif + + /* Perform all the animations on this node */ + count_all = gf_node_animation_count(node); + for (i = 0; i < count_all; i++) { + GF_FieldInfo info; + s32 j; + u32 count; + SMIL_AttributeAnimations *aa; + + + aa = (SMIL_AttributeAnimations *)gf_node_animation_get(node, i); + count = gf_list_count(aa->anims); + if (!count) continue; + + aa->presentation_value_changed = 0; + + if (aa->is_property) { + /* Storing the pointer to the parent presentation value, + i.e. the presentation value produced at the parent level in the tree */ + aa->parent_presentation_value = aa->presentation_value; + aa->parent_presentation_value.far_ptr = + gf_svg_get_property_pointer((SVG_Element *)node, aa->orig_dom_ptr, render_svg_props); + + /* Storing also the pointer to the presentation value of the color property + (special handling of the keyword 'currentColor' if used in animation values) */ + gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_color, 1, 1, &info); + aa->current_color_value.far_ptr = info.far_ptr; + } + + /* We start with the last animation (TODO in the execution order), then scan in the reverse order + up to the first animation which is not additive, to determine if the presentation value will change + We evaluate each animation, but only in the 'change_detection_mode' */ + for (j = count-1; j >= 0; j--) { + SMIL_Anim_RTI *rai = (SMIL_Anim_RTI *)gf_list_get(aa->anims, j); + SMIL_Timing_RTI *rti = rai->timingp->runtime; + + rai->interpolated_value_changed = 0; + + /* The evaluate_status has been updated when notifying the new scene time to this animation, + i.e. before the scene tree traversal */ + if (rti->evaluate_status) { + rai->change_detection_mode = 1; + rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status); + aa->presentation_value_changed += rai->interpolated_value_changed; + if (!rai->animp->additive || *rai->animp->additive == SMIL_ADDITIVE_REPLACE) { + /* we don't need to check previous animations since this one will overwrite it */ + j--; + break; + } + } + } + + active_anim = 0; + if (aa->presentation_value_changed) { + /* If the result of all the combined animations will produce a different result compared to the previous frame, + we start in the forward order from the j were the previous step stopped (i.e. the first anim in replace mode) + and evaluate each animation, in the computation mode (change_detection_mode = 0)*/ + for (j++; j<(s32)count; j++) { + SMIL_Anim_RTI *rai = (SMIL_Anim_RTI *)gf_list_get(aa->anims, j); + SMIL_Timing_RTI *rti = rai->timingp->runtime; + + if (j == 0) rai->is_first_anim = 1; + else rai->is_first_anim = 0; + + if (rti->evaluate_status) { + rai->change_detection_mode = 0; + rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status); + active_anim++; + } + } + } + +/* DEBUG: uncomment this line to remove animation effect, and keep animation computation */ +// gf_svg_attributes_copy(&aa->presentation_value, &aa->specified_value, 0); + +#ifndef GPAC_DISABLE_LOG + if (aa->presentation_value_changed) { + if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_SMIL)) { + char str[1000]; + gf_log_lt(GF_LOG_DEBUG, GF_LOG_SMIL); + gf_svg_dump_attribute(node, &aa->presentation_value, str); + assert(strlen(str) < 1000); + gf_log("[SMIL Animation] Time %f - Element %s - Presentation value changed for attribute %s, new value: %s - dirty flags %x\n", + gf_node_get_scene_time(node), gf_node_get_log_name(node), + gf_svg_get_attribute_name(node, aa->presentation_value.fieldIndex), str, aa->dirty_flags); + } + } +#endif + + /* we only set dirty flags when a real flag is set to avoid unnecessary computation + for example, it is not necessary to set it when the anim is an animateTransform + since there is no associated flag */ + if (aa->dirty_flags) { + if (aa->presentation_value_changed) { + gf_node_dirty_set(node, aa->dirty_flags, aa->dirty_parents); + } else { + /* WARNING - This does not work for use elements because apply_animations may be called several times */ + if (active_anim) gf_node_dirty_clear(node, aa->dirty_flags); + } + } + } + +#ifndef GPAC_DISABLE_LOG + if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_RTI)) { + time_spent_in_anim += gf_sys_clock() - time; + } +#endif +} + + + +void gf_smil_anim_init_runtime_info(GF_Node *e) +{ + u32 i; + GF_FieldInfo target_attribute; + SMIL_AttributeAnimations *aa = NULL; + SMIL_Anim_RTI *rai; + XLinkAttributesPointers *xlinkp = NULL; + SMILAnimationAttributesPointers *animp = NULL; + SMILTimingAttributesPointers *timingp = NULL; + GF_Node *target = NULL; + + if (!e) return; + + /* Filling animation structures to be independent of the SVG Element structure */ + animp = ((SVGTimedAnimBaseElement *)e)->animp; + timingp = ((SVGTimedAnimBaseElement *)e)->timingp; + xlinkp = ((SVGTimedAnimBaseElement *)e)->xlinkp; + + target = xlinkp->href->target; + + memset(&target_attribute, 0, sizeof(GF_FieldInfo)); + if (animp->attributeName && (animp->attributeName->name || animp->attributeName->tag)) { + /* Filling the target_attribute structure with info on the animated attribute (type, pointer to data, ...) + NOTE: in the mode Dynamic Allocation of Attributes, this means that the animated + attribute is created with a default value, if it was not specified on the target element */ + if (animp->attributeName->tag) { + gf_node_get_attribute_by_tag(target, animp->attributeName->tag, 1, 1, &target_attribute); + } else { + gf_node_get_field_by_name(target, animp->attributeName->name, &target_attribute); + } + } else { + /* All animation elements should have a target attribute except for animateMotion + cf http://www.w3.org/mid/u403c21ajf1sjqtk58g0g38eaep9f9g2ss@hive.bjoern.hoehrmann.de + "For animateMotion, the attributeName is implied and cannot be specified; + animateTransform requires specification of the attribute name and any attribute that is + a transform-like attribute can be a target, e.g. gradientTransform."*/ + + switch (e->sgprivate->tag) { + case TAG_SVG_animateMotion: + /* Explicit creation of the pseudo 'motionTransform' attribute since it cannot be specified */ + gf_node_get_attribute_by_tag(target, TAG_SVG_ATT_motionTransform, 1, 0, &target_attribute); + gf_mx2d_init(*(GF_Matrix2D *)target_attribute.far_ptr); + break; + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_SMIL, + ("[SMIL Animation] Missing attributeName attribute on element %s\n", + gf_node_get_log_name((GF_Node*)e) )); + return; + } + } + + if (animp->attributeType && *animp->attributeType == SMIL_ATTRIBUTETYPE_CSS) { + /* see example animate-elem-219-t.svg from the SVG test suite, upper row */ + if (!gf_svg_is_property(target, &target_attribute)) { + GF_LOG(GF_LOG_WARNING, GF_LOG_SMIL, + ("[SMIL Animation] Using CSS attributeType for an animation on an attribute which is not a property %s\n", + gf_node_get_log_name((GF_Node*)e) )); + return; + } + } + + /* Creation and setup of the runtime structure for animation */ + GF_SAFEALLOC(rai, SMIL_Anim_RTI) + + rai->anim_elt = e; + rai->animp = animp; + rai->timingp = timingp; + rai->xlinkp = xlinkp; + + gf_mx2d_init(rai->identity); + rai->default_transform_value.far_ptr = &rai->identity; + rai->default_transform_value.fieldType = SVG_Transform_datatype; + + /* the interpolated value has the same type as the target attribute, + but we need to create a new pointer to hold its value */ + rai->interpolated_value = target_attribute; + rai->interpolated_value.far_ptr = gf_svg_create_attribute_value(target_attribute.fieldType); + + /* there has not been any interpolation yet, so the previous key index and interpolation coefficient + shall not be set*/ + gf_smil_anim_reset_variables(rai); + + rai->values_count = (animp->values ? gf_list_count(animp->values->values) : 0); + rai->key_times_count = (animp->keyTimes ? gf_list_count(*animp->keyTimes) : 0); + rai->key_points_count = (animp->keyPoints ? gf_list_count(*animp->keyPoints) : 0); + rai->key_splines_count = (animp->keySplines ? gf_list_count(*animp->keySplines) : 0); + + + if (!rai->values_count && /* 'values' attribute not specified */ + (!animp->to || animp->to->type == 0) && /* 'to' attribute not specified */ + (!animp->from || animp->from->type == 0) && /* 'from' attribute not specified */ + (animp->by && animp->by->type != 0)) { /* 'by' attribute specified */ + /* if this is a 'by' animation without from the animation is defined to be additive + see http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#AnimationNS-FromToBy + we override the additive attribute */ + if (!animp->additive) { + /* this case can only happen with dynamic allocation of attributes */ + GF_FieldInfo info; + gf_node_get_attribute_by_tag(e, TAG_SVG_ATT_additive, 1, 0, &info); + animp->additive = info.far_ptr; + } + if (*animp->additive == SMIL_ADDITIVE_REPLACE) { + GF_LOG(GF_LOG_WARNING, GF_LOG_SMIL, ("[SMIL Animation] Warning: by-animations cannot use additive=\"replace\"\n")); + } + *animp->additive = SMIL_ADDITIVE_SUM; + } + + /*TODO + http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-ToAnimation + To animation defines its own kind of additive semantics, so the additive attribute is ignored. + */ + + /*TODO + http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-ToAnimation + Because to animation is defined in terms of absolute values of the target attribute, + cumulative animation is not defined: + */ + + /* TODO + http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-setElement + The set element is non-additive. The additive and accumulate attributes are not allowed, + and will be ignored if specified. + */ + + /* For animateMotion, we need to retrieve the value of the rotate attribute, retrieve the path either + from the 'path' attribute or from the 'mpath' element, and then initialize the path iterator*/ + if (e->sgprivate->tag == TAG_SVG_animateMotion) { + GF_Path *the_path = NULL; + GF_ChildNodeItem *child = NULL; + + GF_FieldInfo info; + if (gf_node_get_attribute_by_tag(e, TAG_SVG_ATT_rotate, 0, 0, &info) == GF_OK) { + rai->rotate = ((SVG_Rotate *)info.far_ptr)->type; + } else { + rai->rotate = SVG_NUMBER_VALUE; + } + if (gf_node_get_attribute_by_tag(e, TAG_SVG_ATT_path, 0, 0, &info) == GF_OK) { + the_path = ((SVG_PathData *)info.far_ptr); + } + child = ((SVG_Element *)e)->children; + + if ((!animp->to || animp->to->type == 0) && + (!animp->by || animp->by->type == 0) && + (!animp->values || animp->values->type == 0)) { +#if USE_GF_PATH + if (!gf_path_is_empty(the_path)) { + rai->path = the_path; + rai->path_iterator = gf_path_iterator_new(rai->path); + rai->length = gf_path_iterator_get_length(rai->path_iterator); + } +#else + rai->path = gf_path_new(); + if (gf_list_count(the_path->points)) { + gf_svg_path_build(rai->path, the_path->commands, the_path->points); + rai->path_iterator = gf_path_iterator_new(rai->path); + rai->length = gf_path_iterator_get_length(rai->path_iterator); + } +#endif + else { + while (child) { + GF_Node *used_path = NULL; + u32 child_tag = gf_node_get_tag(child->node); + if (child_tag == TAG_SVG_mpath) { + GF_FieldInfo info; + if (gf_node_get_attribute_by_tag(child->node, TAG_XLINK_ATT_href, 0, 0, &info) == GF_OK) { + XMLRI *iri = (XMLRI *)info.far_ptr; + if (iri->target) used_path = iri->target; + else if (iri->string) used_path = + (GF_Node *)gf_sg_find_node_by_name(gf_node_get_graph(child->node), iri->string); + if (used_path && gf_node_get_tag(used_path) == TAG_SVG_path) { + gf_node_get_attribute_by_tag(used_path, TAG_SVG_ATT_d, 1, 0, &info); +#if USE_GF_PATH + rai->path = (SVG_PathData *)info.far_ptr; +#else + gf_svg_path_build(rai->path, + ((SVG_PathData *)info.far_ptr)->commands, + ((SVG_PathData *)info.far_ptr)->points); +#endif + rai->path_iterator = gf_path_iterator_new(rai->path); + rai->length = gf_path_iterator_get_length(rai->path_iterator); + } + } + break; + } + child = child->next; + } + } + } + } + + /* for all animations, check if there is already one animation on this attribute, + if yes, get the list and append the new animation runtime info + if no, create a list and add the new animation runtime info. */ + for (i = 0; i < gf_node_animation_count(target); i++) { + aa = (SMIL_AttributeAnimations *)gf_node_animation_get(target, i); + if (aa->presentation_value.fieldIndex == target_attribute.fieldIndex) { + gf_list_add(aa->anims, rai); + break; + } + aa = NULL; + } + if (!aa) { + GF_SAFEALLOC(aa, SMIL_AttributeAnimations) + + /* We determine if the animated attribute is a property since this changes quite a lot the animation model */ + aa->is_property = gf_svg_is_property(target, &target_attribute); + aa->current_color_value.fieldType = SVG_Paint_datatype; + + /* We copy (one copy for all animations on the same attribute) the DOM specified + value before any animation starts (because the animation will override it), + we also save the initial memory address of the specified value (orig_dom_ptr) + for inheritance hack */ + aa->specified_value = target_attribute; + aa->orig_dom_ptr = aa->specified_value.far_ptr; + aa->specified_value.far_ptr = gf_svg_create_attribute_value(target_attribute.fieldType); + gf_svg_attributes_copy(&aa->specified_value, &target_attribute, 0); + + /* Now, the initial memory address of the specified value holds the presentation value, + and the presentation value is initialized */ + aa->presentation_value = target_attribute; + + aa->anims = gf_list_new(); + gf_list_add(aa->anims, rai); + gf_node_animation_add(target, aa); + + /* determine what the rendering will need to do when the animation runs */ + aa->dirty_flags = gf_svg_get_modification_flags((SVG_Element *)target, &target_attribute); + + /* If the animation will result in a change of geometry or of the display property, + this animation will require traversing the tree, we need to inform the parents of the target node */ + aa->dirty_parents = 0; + if (aa->dirty_flags & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_SVG_DISPLAY_DIRTY)) aa->dirty_parents = 1; + } + + rai->owner = aa; + gf_smil_anim_get_last_specified_value(rai); + + /* for animation (unlike other timed elements like video), the evaluation (i.e. interpolation) cannot be done + during timing evaluation, because due to inheritance, interpolation can only be computed + during scene tree traversal, therefore we need to postpone evaluation of the timed element */ + timingp->runtime->postpone = 1; + + timingp->runtime->evaluate = gf_smil_anim_evaluate; +} + +void gf_smil_anim_delete_runtime_info(SMIL_Anim_RTI *rai) +{ + gf_svg_delete_attribute_value(rai->interpolated_value.fieldType, + rai->interpolated_value.far_ptr, + rai->anim_elt->sgprivate->scenegraph); + if (rai->path) { + gf_svg_delete_attribute_value(rai->last_specified_value.fieldType, + rai->last_specified_value.far_ptr, + rai->anim_elt->sgprivate->scenegraph); + } +#if USE_GF_PATH +#else + if (rai->path) gf_path_del(rai->path); +#endif + if (rai->path_iterator) gf_path_iterator_del(rai->path_iterator); + free(rai); +} + +void gf_smil_anim_remove_from_target(GF_Node *anim, GF_Node *target) +{ + u32 i, j; + if (!target) return; + for (i = 0; i < gf_node_animation_count((GF_Node *)target); i ++) { + SMIL_Anim_RTI *rai; + SMIL_AttributeAnimations *aa = (SMIL_AttributeAnimations *)gf_node_animation_get((GF_Node *)target, i); + j=0; + while ((rai = (SMIL_Anim_RTI *)gf_list_enum(aa->anims, &j))) { + if ((GF_Node *)rai->anim_elt == anim) { + gf_list_rem(aa->anims, j-1); + gf_smil_anim_delete_runtime_info(rai); + break; + } + } + if (gf_list_count(aa->anims) == 0) { + gf_list_del(aa->anims); + gf_svg_delete_attribute_value(aa->specified_value.fieldType, + aa->specified_value.far_ptr, + target->sgprivate->scenegraph); + aa->presentation_value.far_ptr = aa->orig_dom_ptr; + gf_node_animation_rem((GF_Node *)target, i); + free(aa); + } + } +} + +void gf_smil_anim_delete_animations(GF_Node *e) +{ + u32 i, j; + + for (i = 0; i < gf_node_animation_count(e); i ++) { + SMIL_Anim_RTI *rai; + SMIL_AttributeAnimations *aa = (SMIL_AttributeAnimations *)gf_node_animation_get(e, i); + gf_svg_delete_attribute_value(aa->specified_value.fieldType, + aa->specified_value.far_ptr, + e->sgprivate->scenegraph); + j=0; + while ((rai = (SMIL_Anim_RTI *)gf_list_enum(aa->anims, &j))) { + rai->xlinkp->href->target = NULL; + gf_smil_anim_delete_runtime_info(rai); + } + gf_list_del(aa->anims); + free(aa); + } + gf_node_animation_del(e); +} + +void gf_smil_anim_init_discard(GF_Node *node) +{ + SVGAllAttributes all_atts; + XLinkAttributesPointers *xlinkp = NULL; + SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)node; + gf_smil_timing_init_runtime_info(node); + + gf_svg_flatten_attributes((SVG_Element *)e, &all_atts); + GF_SAFEALLOC(e->xlinkp, XLinkAttributesPointers); + xlinkp = e->xlinkp; + xlinkp->href = all_atts.xlink_href; + xlinkp->type = all_atts.xlink_type; + + e->timingp->runtime->evaluate_status = SMIL_TIMING_EVAL_DISCARD; +} + +void gf_smil_anim_init_node(GF_Node *node) +{ + XLinkAttributesPointers *xlinkp = NULL; + SMILAnimationAttributesPointers *animp = NULL; + SVGAllAttributes all_atts; + SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)node; + + gf_svg_flatten_attributes((SVG_Element *)e, &all_atts); + e->xlinkp = malloc(sizeof(XLinkAttributesPointers)); + xlinkp = e->xlinkp; + xlinkp->href = all_atts.xlink_href; + xlinkp->type = all_atts.xlink_type; + + e->animp = malloc(sizeof(SMILAnimationAttributesPointers)); + animp = e->animp; + animp->accumulate = all_atts.accumulate; + animp->additive = all_atts.additive; + animp->attributeName = all_atts.attributeName; + animp->attributeType = all_atts.attributeType; + animp->by = all_atts.by; + animp->calcMode = all_atts.calcMode; + animp->from = all_atts.from; + animp->keySplines = all_atts.keySplines; + animp->keyTimes = all_atts.keyTimes; + animp->lsr_enabled = all_atts.lsr_enabled; + animp->to = all_atts.to; + animp->type = all_atts.transform_type; + animp->values = all_atts.values; + if (node->sgprivate->tag == TAG_SVG_animateMotion) { + e->animp->keyPoints = all_atts.keyPoints; + e->animp->origin = all_atts.origin; + e->animp->path = all_atts.path; + e->animp->rotate = all_atts.rotate; + } else { + e->animp->keyPoints = NULL; + e->animp->origin = NULL; + e->animp->path = NULL; + e->animp->rotate = NULL; + } + + if (xlinkp->href->type == XMLRI_STRING) { + if (!xlinkp->href->string) { + fprintf(stderr, "Error: IRI not initialized\n"); + return; + } else { + GF_Node *n; + + n = (GF_Node*)gf_sg_find_node_by_name(gf_node_get_graph(node), xlinkp->href->string); + if (n) { + xlinkp->href->type = XMLRI_ELEMENTID; + xlinkp->href->target = n; + gf_node_register_iri(node->sgprivate->scenegraph, xlinkp->href); + } else { + return; + } + } + } + if (!xlinkp->href->target) return; + + gf_smil_timing_init_runtime_info(node); + gf_smil_anim_init_runtime_info(node); + gf_smil_anim_set_anim_runtime_in_timing(node); +} + + + +#endif /*GPAC_DISABLE_SVG*/ diff --git a/src/scenegraph/smil_timing.c b/src/scenegraph/smil_timing.c new file mode 100644 index 0000000..42a0d97 --- /dev/null +++ b/src/scenegraph/smil_timing.c @@ -0,0 +1,1041 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean Le Feuvre + * Copyright (c)2004-200X ENST - All rights reserved + * + * This file is part of GPAC / SVG Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +#ifndef GPAC_DISABLE_SVG + +static void gf_smil_timing_null_timed_function(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, u32 state) +{ +} + +static void gf_smil_timing_print_interval(SMIL_Timing_RTI *rti, Bool current, SMIL_Interval *interval) +{ + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - ", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, (current ? "Current " : " Next ")); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("Interval - ")); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("begin: %.2f", interval->begin)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, (" - end: %.2f", interval->end)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, (" - simple dur: %.2f - active dur: %.2f\n",interval->simple_duration, interval->active_duration)); +} + +/* Computes the active duration for the given interval, + assumes that the values of begin and end have been set (>0 for real duration or -1 if infinite) + and that begin is defined (i.e. a positive value, not infinite)*/ +static void gf_smil_timing_compute_active_duration(SMIL_Timing_RTI *rti, SMIL_Interval *interval) +{ + Bool clamp_active_duration; + Bool isDurDefined, isRepeatCountDefined, isRepeatDurDefined, isMinDefined, isMaxDefined, isRepeatDurIndefinite, isRepeatCountIndefinite, isMediaDuration; + SMILTimingAttributesPointers *timingp = rti->timingp; + + /* TODO: check if the test on begin is right and needed */ + if (!timingp/* || interval->begin == -1*/) return; + + switch (gf_node_get_tag((GF_Node *)rti->timed_elt)) { + case TAG_SVG_discard: + interval->active_duration = -1; + return; + } + + isDurDefined = (timingp->dur && timingp->dur->type == SMIL_DURATION_DEFINED); + isMediaDuration = (timingp->dur && (timingp->dur->type == SMIL_DURATION_MEDIA) && (rti->media_duration>=0) ); + isRepeatCountDefined = (timingp->repeatCount && timingp->repeatCount->type == SMIL_REPEATCOUNT_DEFINED); + isRepeatCountIndefinite = (timingp->repeatCount && timingp->repeatCount->type == SMIL_REPEATCOUNT_INDEFINITE); + isRepeatDurDefined = (timingp->repeatDur && timingp->repeatDur->type == SMIL_DURATION_DEFINED); + isRepeatDurIndefinite = (timingp->repeatDur && timingp->repeatDur->type == SMIL_DURATION_INDEFINITE); + + /* Step 1: Computing active duration using repeatDur and repeatCount */ + if (isDurDefined || isMediaDuration) { + interval->simple_duration = isMediaDuration ? rti->media_duration : timingp->dur->clock_value; + + if (isRepeatCountDefined && !isRepeatDurDefined) { + interval->repeat_duration = FIX2FLT(timingp->repeatCount->count) * interval->simple_duration; + } else if (!isRepeatCountDefined && isRepeatDurDefined) { + interval->repeat_duration = timingp->repeatDur->clock_value; + } else if (!isRepeatCountDefined && !isRepeatDurDefined) { + if (isRepeatDurIndefinite || isRepeatCountIndefinite) { + interval->repeat_duration = -1; + } else { + interval->repeat_duration = interval->simple_duration; + } + } else { + interval->repeat_duration = MIN(timingp->repeatDur->clock_value, + FIX2FLT(timingp->repeatCount->count) * interval->simple_duration); + } + } else { + + /* simple_duration is indefinite */ + interval->simple_duration = -1; + + /* we can ignore repeatCount to compute active_duration */ + if (!isRepeatDurDefined) { + interval->repeat_duration = -1; + } else { + interval->repeat_duration = timingp->repeatDur->clock_value; + } + } + + interval->active_duration = interval->repeat_duration; + /* Step 2: if end is defined in the document, clamp active duration to end-begin + otherwise return*/ + if (interval->end < 0) { + /* interval->active_duration stays as is */ + } else { + if (interval->active_duration >= 0) + interval->active_duration = MIN(interval->active_duration, interval->end - interval->begin); + else + interval->active_duration = interval->end - interval->begin; + } + + /* min and max check should be checked last, + to ensure that they have greater priority than the end attribute + see (animate-elem-223-t.svg) */ + /* Step 3: clamp the active duration with min and max */ + clamp_active_duration = 1; + /* testing for presence of min and max because some elements may not have them: eg SVG audio */ + isMinDefined = (timingp->min && timingp->min->type == SMIL_DURATION_DEFINED); + isMaxDefined = (timingp->max && timingp->max->type == SMIL_DURATION_DEFINED); + if (isMinDefined && isMaxDefined && + timingp->max->clock_value < timingp->min->clock_value) { + clamp_active_duration = 0; + } + if (clamp_active_duration) { + if (isMinDefined) { + if ((interval->active_duration >= 0) && + (interval->active_duration <= timingp->min->clock_value)) { + /* see http://www.w3.org/TR/2005/REC-SMIL2-20051213/smil-timing.html#Timing-MinMax + - if repeat duration or simple duration is smaller than min, + then the (active ? / simple ?) duration shall be set to min + (cf 6th row in animate-elem-65-t.svg) + - if the min > dur > end, the element is played normally for its simple duration + and then is frozen or not shown depending on the value of the fill attribute. + (cf animate-elem-222-t.svg)*/ + interval->active_duration = timingp->min->clock_value; + interval->min_active = 1; + } + } + if (isMaxDefined) { + if ((interval->active_duration >= 0 && interval->active_duration >= timingp->max->clock_value) || + interval->active_duration == -1) { + interval->active_duration = timingp->max->clock_value; + } + } + } + +} + +/* This should be called when the dur attribute is set to media and when the media duration is known. + The function recomputes the active duration of the current interval according to the given media duration */ +GF_EXPORT +void gf_smil_set_media_duration(SMIL_Timing_RTI *rti, Double media_duration) +{ + rti->media_duration = media_duration; + gf_smil_timing_compute_active_duration(rti, rti->current_interval); +} + +/* the end value of this interval needs to be initialized before computing the active duration + the begin value must be >= 0 + The result can be: + - a positive value meaning that a resolved and non-indefinite value was found + - the value -1 meaning indefinite or unresolved + TODO: we should make a difference between indefinite and unresolved because + if an interval is created with a value of indefinite, this value should not + be replaced by a resolved event. (Not sure ?!!) + - the value -2 meaning that a valid end value (including indefinite) could not be found +*/ +static void gf_smil_timing_get_interval_end(SMIL_Timing_RTI *rti, SMIL_Interval *interval) +{ + u32 end_count, j; + + /* we set the value to indicate that this is an illegal end, + if it stays like that after searching through the values, + then the whole interval must be discarded */ + interval->end = -2; + + end_count = (rti->timingp->end ? gf_list_count(*rti->timingp->end) : 0); + /* trying to find a matching end */ + if (end_count > 0) { + for (j = 0; j < end_count; j++) { + SMIL_Time *end = (SMIL_Time*)gf_list_get(*rti->timingp->end, j); + if ( GF_SMIL_TIME_IS_CLOCK(end->type) ) { + if( end->clock >= interval->begin) { + interval->end = end->clock; + break; + } + } else { + /* an unresolved or indefinite value is always good */ + interval->end = -1; + break; + } + } + } else { + interval->end = -1; + } +} + +static void gf_smil_timing_get_first_interval(SMIL_Timing_RTI *rti) +{ + u32 i, count; + memset(rti->current_interval, 0, sizeof(SMIL_Interval)); + rti->current_interval->begin = -1; + count = (rti->timingp->begin ? gf_list_count(*rti->timingp->begin) : 0); + for (i = 0; i < count; i ++) { + SMIL_Time *begin = (SMIL_Time*)gf_list_get(*rti->timingp->begin, i); + if (GF_SMIL_TIME_IS_CLOCK(begin->type)) { + rti->current_interval->begin = begin->clock; + break; + } + } + /*In SVG, if no 'begin' is specified, the default timing of the time container + is equivalent to an offset value of '0'.*/ + if (rti->current_interval->begin == -1 && count == 0) { + /* except for LASeR Conditional element*/ + if (rti->timed_elt->sgprivate->tag != TAG_LSR_conditional) { + rti->current_interval->begin = 0; + } else { + return; + } + } + + /* this is the first time we check the interval */ + gf_smil_timing_get_interval_end(rti, rti->current_interval); + if (0 && rti->current_interval->end == -2) { + /* TODO: check if the interval can be discarded (i.e. if end is specified with an invalid end value (return -2)), + probably yes, but next time we call the evaluation of interval, we should call get_first_interval */ + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Wrong Interval\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + rti->current_interval->begin = -1; + rti->current_interval->end = -1; + return; + } + + gf_smil_timing_compute_active_duration(rti, rti->current_interval); + gf_smil_timing_print_interval(rti, 1, rti->current_interval); +} + +static Bool gf_smil_timing_get_next_interval(SMIL_Timing_RTI *rti, Bool current, SMIL_Interval *interval, Double scene_time) +{ + u32 i, count; + + memset(interval, 0, sizeof(SMIL_Interval)); + interval->begin = -1; + + count = (rti->timingp->begin ? gf_list_count(*rti->timingp->begin) : 0); + for (i = 0; i < count; i ++) { + SMIL_Time *begin = (SMIL_Time*)gf_list_get(*rti->timingp->begin, i); + if (GF_SMIL_TIME_IS_CLOCK(begin->type)) { + if (rti->current_interval->begin != -1 && begin->clock <= rti->current_interval->begin) continue; +// if (rti->current_interval->begin == -1 || begin->clock <= scene_time) { + interval->begin = begin->clock; + break; +// } + } + } + if (interval->begin != -1) { + gf_smil_timing_get_interval_end(rti, interval); + if (interval->end == -2) { + /* this is a wrong interval see first animation in animate-elem-201-t.svg */ + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Wrong Interval\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + interval->begin = -1; + interval->end = -1; + return 0; + } + gf_smil_timing_compute_active_duration(rti, interval); + gf_smil_timing_print_interval(rti, current, interval); + return 1; + } else { + return 0; + } +} + +/* To reduce the process of notifying the time to all timed elements, we add to the scene graph + only the timed elements which have a resolved current interval, other timed elements will be + added at runtime when an event leads to the creation of a new interval. + We also insert the new timed element in the order of the current_interval begin value, to stop + the notification of time when not necessary */ +static Bool gf_smil_timing_add_to_sg(GF_SceneGraph *sg, SMIL_Timing_RTI *rti) +{ + if (rti->current_interval->begin != -1) { + SMIL_Timing_RTI *cur_rti = NULL; + u32 i; + + for (i = 0; i < gf_list_count(sg->smil_timed_elements); i++) { + cur_rti = (SMIL_Timing_RTI *)gf_list_get(sg->smil_timed_elements, i); + if (cur_rti->current_interval->begin > rti->current_interval->begin) break; + } + gf_list_insert(sg->smil_timed_elements, rti, i); + return 1; + } + return 0; +} + +/* when a timed element restarts, since the list of timed elements in the scene graph, + to which scene time is notified at each rendering cycle, is sorted, we need to remove + and reinsert this timed element as if it was a new one, to make sure the sorting is correct */ +static void gf_smil_mark_modified(SMIL_Timing_RTI *rti, Bool remove) +{ + GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph; + while (sg->parent_scene) sg = sg->parent_scene; + if (remove) { + gf_list_del_item(sg->modified_smil_timed_elements, rti); + } else { + gf_list_add(sg->modified_smil_timed_elements, rti); + } +} + +/* Attributes from the timed elements are not easy to use during runtime, + the runtime info is a set of easy to use structures. + This function initializes them (intervals, status ...). */ +GF_EXPORT +void gf_smil_timing_init_runtime_info(GF_Node *timed_elt) +{ + GF_SceneGraph *sg; + SMIL_Timing_RTI *rti; + SMILTimingAttributesPointers *timingp = NULL; + u32 tag = gf_node_get_tag(timed_elt); + SVGAllAttributes all_atts; + SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)timed_elt; + + gf_svg_flatten_attributes((SVG_Element *)e, &all_atts); + e->timingp = malloc(sizeof(SMILTimingAttributesPointers)); + e->timingp->begin = all_atts.begin; + e->timingp->clipBegin = all_atts.clipBegin; + e->timingp->clipEnd = all_atts.clipEnd; + e->timingp->dur = all_atts.dur; + e->timingp->end = all_atts.end; + e->timingp->fill = all_atts.smil_fill; + e->timingp->max = all_atts.max; + e->timingp->min = all_atts.min; + e->timingp->repeatCount = all_atts.repeatCount; + e->timingp->repeatDur = all_atts.repeatDur; + e->timingp->restart = all_atts.restart; + timingp = e->timingp; + + if (tag == TAG_SVG_audio || tag == TAG_SVG_video) { + /* if the dur attribute is not set, then it should be set to media + as this is the default for media elements see + http://www.w3.org/TR/2005/REC-SMIL2-20051213/smil-timing.html#Timing-DurValueSemantics + "For simple media elements that specify continuous media (i.e. media with an inherent notion of time), + the implicit duration is the intrinsic duration of the media itself - e.g. video and audio files + have a defined duration." + TODO: Check if this should work with the animation element */ + if (!e->timingp->dur) { + SVGAttribute *att = gf_xml_create_attribute((GF_Node *)e, TAG_SVG_ATT_dur); + e->timingp->dur = (SMIL_Duration *)att->data; + e->timingp->dur->type = SMIL_DURATION_MEDIA; + } + } + + if (!timingp) return; + + GF_SAFEALLOC(rti, SMIL_Timing_RTI) + timingp->runtime = rti; + rti->timed_elt = timed_elt; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Initialization\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + + rti->timingp = timingp; + rti->status = SMIL_STATUS_WAITING_TO_BEGIN; + rti->evaluate_status = SMIL_TIMING_EVAL_NONE; + rti->evaluate = gf_smil_timing_null_timed_function; + rti->scene_time = -1; + rti->force_reevaluation = 0; + rti->media_duration = -1; + + GF_SAFEALLOC(rti->current_interval, SMIL_Interval); + gf_smil_timing_get_first_interval(rti); + GF_SAFEALLOC(rti->next_interval, SMIL_Interval); + gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->current_interval->begin); + + /* Now that the runtime info for this timed element is initialized, we can tell the scene graph that it can start + notifying the scene time to this element. Because of the 'animation' element, we can have many scene graphs + sharing the same scene time, we therefore add this timed element to the rootmost scene graph. */ + sg = timed_elt->sgprivate->scenegraph; + while (sg->parent_scene) sg = sg->parent_scene; + gf_smil_timing_add_to_sg(sg, rti); +} + + +static void gf_smil_timing_reset_time_list(GF_List *times) +{ + GF_DOMEventTarget *evt; + u32 i; + for (i=0; ilistener) continue; + + /*detach the listener from the observed node*/ + evt = t->listener->sgprivate->UserPrivate; + t->listener->sgprivate->UserPrivate = NULL; + gf_dom_listener_del(t->listener, evt); + + /*release our listener*/ + gf_node_unregister(t->listener, NULL); + t->listener = NULL; + } +} + +void gf_smil_timing_delete_runtime_info(GF_Node *timed_elt, SMIL_Timing_RTI *rti) +{ + GF_SceneGraph *sg; + + if (!rti || !timed_elt) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Destruction\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + free(rti->current_interval); + free(rti->next_interval); + + /* we inform the rootmost scene graph that this node will not need notification of the scene time anymore */ + sg = timed_elt->sgprivate->scenegraph; + while (sg->parent_scene) sg = sg->parent_scene; + gf_list_del_item(sg->smil_timed_elements, rti); + + /*remove all associated listeners*/ + if (rti->timingp->begin) gf_smil_timing_reset_time_list(* rti->timingp->begin); + if (rti->timingp->end) gf_smil_timing_reset_time_list(* rti->timingp->end); + + free(rti); +} + +GF_EXPORT +Bool gf_smil_timing_is_active(GF_Node *node) +{ + SMILTimingAttributesPointers *timingp = NULL; + timingp = ((SVGTimedAnimBaseElement *)node)->timingp; + if (!timingp || !timingp->runtime) return 0; + return (timingp->runtime->status == SMIL_STATUS_ACTIVE); +} + +/* This function notifies the scene time to all the timed elements from the list in the given scene graph. + It returns the number of active timed elements. If no timed element is active, this means that from the timing + point of view, the scene has not changed and no rendering refresh is needed, even if the time has changed. + It uses an additional list of modified timed elements to insure that no timing + element was modified by the begin/end/repeat of another timed element. +*/ +Bool gf_smil_notify_timed_elements(GF_SceneGraph *sg) +{ + SMIL_Timing_RTI *rti; + u32 active_count, i; + s32 ret; + Bool do_loop; + if (!sg) return 0; + + active_count = 0; + + /* notify the new scene time to the register timed elements + this might modify other timed elements or the element itself + in which case it will be added to the list of modified elements */ + i = 0; + do_loop = 1; + while(do_loop && (rti = (SMIL_Timing_RTI *)gf_list_enum(sg->smil_timed_elements, &i))) { + ret = gf_smil_timing_notify_time(rti, gf_node_get_scene_time((GF_Node*)rti->timed_elt) ); + switch (ret) { + case -1: + /* special case for discard element + when a discard element is executed, it automatically removes itself from the list of timed element + in the scene graph, we need to fix the index i. */ + i--; + break; + case -2: + /* special return value, -2 means that the tested timed element is waiting to begin + Assuming that the timed elements are sorted by begin order, + the next ones don't need to be checked */ + do_loop = 0; + break; + case -3: + /* special case for animation elements which do not need to be notified anymore, + but which require a tree traversal */ + i--; + active_count ++; + break; + case 1: + active_count++; + break; + case 0: + default: + break; + } + } + + /* notify the timed elements which have been modified either since the previous frame (updates, scripts) or + because of the start/end/repeat of the previous notifications */ + while (gf_list_count(sg->modified_smil_timed_elements)) { + /* first remove the modified smil timed element */ + rti = gf_list_get(sg->modified_smil_timed_elements, 0); + gf_list_rem(sg->modified_smil_timed_elements, 0); + + /* then remove it in the list of non modified (if it was there) */ + gf_list_del_item(sg->smil_timed_elements, rti); + + /* then insert it at its right position (in the sorted list of timed elements) */ + gf_smil_timing_add_to_sg(sg, rti); + + /* finally again notify this timed element */ + rti->force_reevaluation = 1; + ret = gf_smil_timing_notify_time(rti, gf_node_get_scene_time((GF_Node*)rti->timed_elt) ); + switch (ret) { + case -1: + break; + case -2: + break; + case -3: + active_count++; + break; + case 1: + active_count++; + break; + case 0: + default: + break; + } + + } + return (active_count>0); +} + +/* evaluation function for the discard element + returns 1 if the discard was executed + 0 otherwise +*/ +static Bool gf_smil_discard(SMIL_Timing_RTI *rti, Fixed scene_time) +{ + u32 nb_inst; + SMIL_Time *begin; + SVGTimedAnimBaseElement *tb = (SVGTimedAnimBaseElement *)rti->timed_elt; + SMILTimingAttributesPointers *timingp = (SMILTimingAttributesPointers *)rti->timingp; + GF_Node *target; + + if (!timingp) return 0; + + target = tb->xlinkp->href ? tb->xlinkp->href->target : NULL; + + begin = (timingp->begin ? (SMIL_Time *)gf_list_get(*timingp->begin, 0) : NULL); + + if (!begin) return 0; + if (!GF_SMIL_TIME_IS_CLOCK(begin->type) ) return 0; + + if (begin->clock > scene_time) return 0; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SVG Composer] discarding element %s at time %f\n", target ? gf_node_get_log_name(target) : "None", scene_time)); + + gf_smil_mark_modified(rti, 1); + + /*this takes care of cases where discard is a child of its target*/ + gf_node_register(rti->timed_elt, NULL); + nb_inst = gf_node_get_num_instances(rti->timed_elt); + if (target) gf_node_replace(target, NULL, 0); + if (nb_inst == gf_node_get_num_instances(rti->timed_elt)) { + gf_node_unregister(rti->timed_elt, NULL); + /*after this the stack may be free'd*/ + gf_node_replace(rti->timed_elt, NULL, 0); + } else { + gf_node_unregister(rti->timed_elt, NULL); + } + return 1; +} + +/* Animations are applied in their begin order first and then in document order. + Whenever an animation (re)starts, it is placed at the end of the queue (potentially after frozen animations) */ +static void gf_smil_reorder_anim(SMIL_Timing_RTI *rti) +{ + SMIL_Anim_RTI *rai = rti->rai; + if (rai) { + gf_list_del_item(rai->owner->anims, rai); + gf_list_add(rai->owner->anims, rai); + gf_smil_anim_reset_variables(rai); + } +} + +/* Notifies the scene time to a timed element, potentially changing its status and triggering its evaluation + Returns: + 0 if no rendering traversal is required, + 1 if a rendering traversal is required, + -1 if the time node is a discard which has been deleted during this notification, + -2 means that the timed element is waiting to begin, + -3 means that the timed element is active but does not need further notifications (set without dur) + but still requires a rendering traversal */ +s32 gf_smil_timing_notify_time(SMIL_Timing_RTI *rti, Double in_scene_time) +{ + s32 ret = 0; + GF_DOM_Event evt; + SMILTimingAttributesPointers *timingp = rti->timingp; + Bool force_end = 0; + + if (!timingp) return 0; + + /* if the scene time is the same as it was during the previous notification, it means that the + animations are paused and we don't need to evaluate it again unless the force_reevaluation flag is set */ + if ((rti->scene_time == in_scene_time) && (rti->force_reevaluation == 0)) return 0; + if (!rti->paused) rti->scene_time = in_scene_time; + rti->force_reevaluation = 0; + + /* for fraction events, in all cases we indicate that the scene needs redraw */ + if (rti->evaluate_status == SMIL_TIMING_EVAL_FRACTION) + return 1; + + if (rti->evaluate_status == SMIL_TIMING_EVAL_DISCARD) { + /* TODO: FIX ME discarding should send a begin event ? */ + /* Since the discard can only be evaluated once, it unregisters itself + from the list of timed elements to be notified, so for this special case + we return -1 when the discard has actually been executed */ + if (gf_smil_discard(rti, FLT2FIX(rti->scene_time))) return -1; + else return 0; + } + + gf_node_register(rti->timed_elt, NULL); + +waiting_to_begin: + if (rti->status == SMIL_STATUS_WAITING_TO_BEGIN) { + if (rti->current_interval->begin != -1 && rti->scene_time >= rti->current_interval->begin) { + /* if there is a computed interval with a definite begin value + and if that value is lesser than the scene time, then the animation becomes active */ + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Activating\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + rti->status = SMIL_STATUS_ACTIVE; + + if (rti->timed_elt->sgprivate->tag==TAG_LSR_conditional) { + SVG_Element *e = (SVG_Element *)rti->timed_elt; + /*activate conditional*/ + if (e->children) gf_node_traverse(e->children->node, NULL); + rti->status = SMIL_STATUS_DONE; + } else { + gf_smil_reorder_anim(rti); + } + + memset(&evt, 0, sizeof(evt)); + evt.type = GF_EVENT_BEGIN_EVENT; + evt.smil_event_time = rti->current_interval->begin; + gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Evaluating (Not starting)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + ret = -2; + goto exit; + } + } + + if (rti->status == SMIL_STATUS_ACTIVE) { + u32 cur_id; + + if (rti->current_interval->active_duration >= 0 + && rti->scene_time >= (rti->current_interval->begin + rti->current_interval->active_duration)) { +force_end: + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Stopping \n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + + rti->normalized_simple_time = gf_smil_timing_get_normalized_simple_time(rti, rti->scene_time, NULL); + ret = rti->postpone; + + if (timingp->fill && *timingp->fill == SMIL_FILL_FREEZE) { + rti->status = SMIL_STATUS_FROZEN; + rti->evaluate_status = SMIL_TIMING_EVAL_FREEZE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Preparing to freeze\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + if (!rti->postpone) { + rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status); + } + } else { + rti->status = SMIL_STATUS_DONE; + rti->evaluate_status = SMIL_TIMING_EVAL_REMOVE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Preparing to remove\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + if (!rti->postpone) { + rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status); + } + } + + memset(&evt, 0, sizeof(evt)); + evt.type = GF_EVENT_END_EVENT; + /* WARNING: begin + active_duration may be greater than 'now' because of force_end cases */ + evt.smil_event_time = rti->current_interval->begin + rti->current_interval->active_duration; + gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt); + + } else { /* the animation is still active */ + + if (!timingp->restart || *timingp->restart == SMIL_RESTART_ALWAYS) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Checking for restart (always)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + + if (rti->next_interval->begin != -1 && rti->next_interval->begin < rti->scene_time) { + *rti->current_interval = *rti->next_interval; + gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->scene_time); + + /* mark that this element has been modified and + need to be reinserted at its proper place in the list of timed elements in the scenegraph */ + gf_smil_mark_modified(rti, 0); + + /* if this is animation, reinserting the animation in the list of animations + that targets this attribute, so that it is the last one */ + gf_smil_reorder_anim(rti); + + memset(&evt, 0, sizeof(evt)); + evt.type = GF_EVENT_BEGIN_EVENT; + evt.smil_event_time = rti->current_interval->begin; + gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt); + } + } + + ret = rti->postpone; + + cur_id = rti->current_interval->nb_iterations; + rti->normalized_simple_time = gf_smil_timing_get_normalized_simple_time(rti, rti->scene_time, &force_end); + if (force_end) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Forcing end (fill or remove)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + goto force_end; + } + if (cur_id < rti->current_interval->nb_iterations) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[SMIL Timing ] Time %f - Timed element %s - Preparing to repeat\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + memset(&evt, 0, sizeof(evt)); + evt.type = GF_EVENT_REPEAT_EVENT; + evt.smil_event_time = rti->current_interval->begin + rti->current_interval->nb_iterations*rti->current_interval->simple_duration; + evt.detail = rti->current_interval->nb_iterations; + gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt); + + rti->evaluate_status = SMIL_TIMING_EVAL_REPEAT; + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Preparing to update\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + rti->evaluate_status = SMIL_TIMING_EVAL_UPDATE; + } + + if (!rti->postpone) { + rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status); + } + + /* special case for animations with unspecified simpleDur (not with media timed elements) + we need to indicate that this anim does not need to be notified anymore and that + it does not require tree traversal */ + if (gf_svg_is_animation_tag(rti->timed_elt->sgprivate->tag) + && (rti->current_interval->simple_duration==-1) + && (rti->current_interval->active_duration==-1) + ) { + /*GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph; + while (sg->parent_scene) sg = sg->parent_scene; + gf_list_del_item(sg->smil_timed_elements, rti); + ret = -3;*/ + ret = 1; + } + } + } + + if ((rti->status == SMIL_STATUS_DONE) || (rti->status == SMIL_STATUS_FROZEN)) { + if (!timingp->restart || *timingp->restart != SMIL_RESTART_NEVER) { + /* Check changes in begin or end attributes */ + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Checking for restart when not active\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + if (rti->next_interval->begin != -1) { + Bool restart_timing = 0; + /*next interval is right now*/ + if (rti->next_interval->begin == rti->current_interval->begin+rti->current_interval->active_duration) + restart_timing = 1; + + /*switch intervals*/ + if (rti->next_interval->begin >= rti->current_interval->begin+rti->current_interval->active_duration) { + *rti->current_interval = *rti->next_interval; + + gf_smil_timing_print_interval(rti, 1, rti->current_interval); + gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->scene_time); + + /* mark that this element has been modified and + need to be reinserted at its proper place in the list of timed elements in the scenegraph */ + gf_smil_mark_modified(rti, 0); + } else { + rti->next_interval->begin = -1; + } + + /*if chaining to new interval, go to wait_for begin right now*/ + if (restart_timing) { + rti->status = SMIL_STATUS_WAITING_TO_BEGIN; + rti->evaluate_status = SMIL_TIMING_EVAL_NONE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Returning to eval none status\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + ret = 0; + goto waiting_to_begin; + } + /*otherwise move state to waiting for begin for next smil_timing evaluation, but + don't change evaluate status for next anim evaluation*/ + else { + rti->status = SMIL_STATUS_WAITING_TO_BEGIN; + } + } else { + /*??? what is this ???*/ + //ret = 0; + } + } else if ((rti->status == SMIL_STATUS_DONE) && + timingp->restart && (*timingp->restart == SMIL_RESTART_NEVER)) { + /* the timed element is done and cannot restart, we don't need to evaluate it anymore */ + GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph; + while (sg->parent_scene) sg = sg->parent_scene; + gf_list_del_item(sg->smil_timed_elements, rti); + ret = -1; + } + } + +exit: + gf_node_unregister(rti->timed_elt, NULL); + return ret; +} + +/* returns a fraction between 0 and 1 of the elapsed time in the simple duration */ +/* WARNING: According to SMIL (http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-Fill, +see "Illustration of animation combining a partial repeat and fill="freeze"") +When a element is frozen, its normalized simple time is not necessarily 1, +an animation can be frozen in the middle of a repeatition */ +Fixed gf_smil_timing_get_normalized_simple_time(SMIL_Timing_RTI *rti, Double scene_time, Bool *force_end) +{ + Double activeTime; + Double simpleTime; + Fixed normalizedSimpleTime; + + if (rti->current_interval->begin == -1) return 0; + + /* we define the active time as the elapsed time from the current activation of the element */ + activeTime = scene_time - rti->current_interval->begin; + + /* Is the animation reaching the end of its active duration ? */ + if (rti->current_interval->active_duration != -1 && activeTime >= rti->current_interval->active_duration) { + + /* we clamp the active time to its maximum value */ + activeTime = rti->current_interval->active_duration; + + /* if the simple duration is defined, then we can take iterations into account */ + if (rti->current_interval->simple_duration>0) { + + if (activeTime == rti->current_interval->simple_duration*(rti->current_interval->nb_iterations+1)) { + return FIX_ONE; + } else { + goto end; + } + } else { + /* If the element does not define its simple duration, but it has an active duration, + and we are past this active duration, we assume it's blocked in final state + We should take into account fill behavior*/ + rti->current_interval->nb_iterations = 0; + if (rti->timingp->fill && *(rti->timingp->fill) == SMIL_FILL_FREEZE) { + if (rti->current_interval->repeat_duration == rti->current_interval->simple_duration) { + return FIX_ONE; + } else { + return rti->normalized_simple_time; + } + } else { + return 0; + } + } + } + +end: + /* if the simple duration is defined, then we can take iterations into account */ + if (rti->current_interval->simple_duration>0) { + + /* if we are active but frozen or done (animate-elem-65-t, animate-elem-222-t) + (see The rule to apply to compute the active duration of an element with min or max specified) */ + if ((activeTime >= rti->current_interval->repeat_duration) && rti->current_interval->min_active) { + /* freeze the normalized simple time */ + if (force_end) *force_end = 1; + if (rti->timingp->fill && *(rti->timingp->fill) == SMIL_FILL_FREEZE) { + if (rti->current_interval->repeat_duration == rti->current_interval->simple_duration) { + return FIX_ONE; + } else { + return rti->normalized_simple_time; + } + } + } + /* we update the number of iterations */ + rti->current_interval->nb_iterations = (u32)floor(activeTime / rti->current_interval->simple_duration); + } else { + /* If the element does not define its simple duration, we assume it's blocked in final state + Is this correct ? */ + rti->current_interval->nb_iterations = 0; + //GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Error Computing Normalized Simple Time while simple duration is indefinite\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + return FIX_ONE; + } + + /* We compute the simple time by removing time taken by previous iterations */ + simpleTime = activeTime - rti->current_interval->simple_duration * rti->current_interval->nb_iterations; + + /* Then we clamp the simple time to be sure it is between 0 and simple duration */ + simpleTime = MAX(0, simpleTime); + simpleTime = MIN(rti->current_interval->simple_duration, simpleTime); + + /* Then we normalize to have a value between 0 and 1 */ + normalizedSimpleTime = FLT2FIX(simpleTime / rti->current_interval->simple_duration); + + return normalizedSimpleTime; +} + +/* This function is called when a modification to the node has been made (scripts, updates or events ...) */ +void gf_smil_timing_modified(GF_Node *node, GF_FieldInfo *field) +{ + SMILTimingAttributesPointers *timingp = NULL; + SMIL_Timing_RTI *rti; + + timingp = ((SVGTimedAnimBaseElement *)node)->timingp; + + if (!timingp) return; + rti = timingp->runtime; + if (!rti) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Modification\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + if (rti->current_interval->begin == -1) { + gf_smil_timing_get_next_interval(rti, 1, rti->current_interval, gf_node_get_scene_time((GF_Node*)rti->timed_elt)); + } else { + /* we don't have the right to modify the end of an element if it's not in unresolved state */ + if (rti->current_interval->end == -1) gf_smil_timing_get_interval_end(rti, rti->current_interval); + if (0 && rti->current_interval->end == -2) { + /* TODO: check if the interval can be discarded if end = -2, + probably no, because the interval is currently running*/ + GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Wrong Interval\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); + rti->current_interval->begin = -1; + rti->current_interval->end = -1; + return; + } + + gf_smil_timing_compute_active_duration(rti, rti->current_interval); + gf_smil_timing_print_interval(rti, 1, rti->current_interval); + } + gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, gf_node_get_scene_time((GF_Node*)rti->timed_elt)); + + /* mark that this element has been modified and + need to be reinserted at its proper place in the list of timed elements in the scenegraph */ + gf_smil_mark_modified(rti, 0); +} + + +/* Tries to resolve event-based or sync-based time values + Used in parsing, to determine if a timed element can be initialized */ +Bool gf_svg_resolve_smil_times(GF_Node *anim, void *event_base_element, + GF_List *smil_times, Bool is_end, const char *node_name) +{ + u32 i, done, count; + + done = 0; + count = gf_list_count(smil_times); + for (i=0; itype != GF_SMIL_TIME_EVENT) { + done++; + continue; + } + if (!t->element_id) { + if (!t->element) t->element = (GF_Node *)event_base_element; + done++; + continue; + } + /*commented out because it breaks regular anims (cf interact-pevents-07-t.svg)*/ +// if (node_name && strcmp(node_name, t->element_id)) continue; + + t->element = gf_sg_find_node_by_name(anim->sgprivate->scenegraph, t->element_id); + if (t->element) { + free(t->element_id); + t->element_id = NULL; + done++; + } + } + /*lacuna value of discard is 0*/ + if (!count && !is_end && (anim->sgprivate->tag==TAG_SVG_discard) ) { + SMIL_Time *t; + GF_SAFEALLOC(t, SMIL_Time); + t->clock = 0; + t->type = GF_SMIL_TIME_CLOCK; + gf_list_add(smil_times, t); + return 1; + } + + if (done!=count) return 0; + return 1; +} + +/* Inserts a new resolved time instant in the begin or end attribute. + The insertion preserves the sorting and removes the previous insertions + which have become obsolete + WARNING: Only used for inserting time when an element, whose target is a timed element, is activated. */ +GF_EXPORT +void gf_smil_timing_insert_clock(GF_Node *elt, Bool is_end, Double clock) +{ + u32 i, count, found; + SVGTimedAnimBaseElement *timed = (SVGTimedAnimBaseElement*)elt; + SMIL_Time *begin; + GF_List *l; + GF_SAFEALLOC(begin, SMIL_Time); + + begin->type = GF_SMIL_TIME_EVENT_RESOLVED; + begin->clock = clock; + + l = is_end ? *timed->timingp->end : *timed->timingp->begin; + + found = 0; + count = gf_list_count(l); + for (i=0; itype==GF_SMIL_TIME_EVENT_RESOLVED) && (first->clock < begin->clock)) { + gf_list_rem(l, i); + free(first); + i--; + count--; + continue; + } + if ( (first->type == GF_SMIL_TIME_INDEFINITE) + || ( (first->type == GF_SMIL_TIME_CLOCK) && (first->clock > begin->clock) ) + ) { + gf_list_insert(l, begin, i); + found = 1; + break; + } + } + if (!found) gf_list_add(l, begin); + + /* call gf_smil_timing_modified */ + gf_node_changed(elt, NULL); +} + +void gf_smil_timing_pause(GF_Node *node) +{ + if (node && ((SVGTimedAnimBaseElement *)node)->timingp && ((SVGTimedAnimBaseElement *)node)->timingp->runtime) { + SMIL_Timing_RTI *rti = ((SVGTimedAnimBaseElement *)node)->timingp->runtime; + if (rti->status<=SMIL_STATUS_ACTIVE) rti->paused = 1; + } +} + +void gf_smil_timing_resume(GF_Node *node) +{ + if (node && ((SVGTimedAnimBaseElement *)node)->timingp && ((SVGTimedAnimBaseElement *)node)->timingp->runtime) { + SMIL_Timing_RTI *rti = ((SVGTimedAnimBaseElement *)node)->timingp->runtime; + rti->paused = 0; + } +} + +void gf_smil_set_evaluation_callback(GF_Node *node, + void (*smil_evaluate)(struct _smil_timing_rti *rti, Fixed normalized_simple_time, u32 state)) +{ + if (node && ((SVGTimedAnimBaseElement *)node)->timingp && ((SVGTimedAnimBaseElement *)node)->timingp->runtime) { + SMIL_Timing_RTI *rti = ((SVGTimedAnimBaseElement *)node)->timingp->runtime; + rti->evaluate = smil_evaluate; + } +} + +GF_Node *gf_smil_get_element(SMIL_Timing_RTI *rti) +{ + return rti->timed_elt; +} + +Double gf_smil_get_media_duration(SMIL_Timing_RTI *rti) +{ + return rti->media_duration; +} + + +#endif /*GPAC_DISABLE_SVG*/ diff --git a/src/scenegraph/svg_attributes.c b/src/scenegraph/svg_attributes.c new file mode 100644 index 0000000..ba7aa5c --- /dev/null +++ b/src/scenegraph/svg_attributes.c @@ -0,0 +1,5996 @@ +/* + * GPAC Multimedia Framework + * + * Authors: Cyril Concolato - Jean le Feuvre - Jean-Claude Moissinac + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / SVG Loader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +#ifndef GPAC_DISABLE_SVG + +#include + +#define DUMP_COORDINATES 1 + + +static const struct dom_event_def {u32 event; const char *name; u32 category; } defined_dom_events [] = +{ + { GF_EVENT_ABORT, "abort", GF_DOM_EVENT_DOM }, + { GF_EVENT_ERROR, "error", GF_DOM_EVENT_DOM }, + { GF_EVENT_LOAD, "load", GF_DOM_EVENT_DOM }, + { GF_EVENT_UNLOAD, "unload", GF_DOM_EVENT_DOM }, + + /*focus - we differentiate from UI/key events to avoid browing focus if no listener is on place*/ + { GF_EVENT_FOCUSIN, "DOMFocusIn", GF_DOM_EVENT_FOCUS }, + { GF_EVENT_FOCUSIN, "focusin", GF_DOM_EVENT_FOCUS }, + { GF_EVENT_FOCUSOUT, "DOMFocusOut", GF_DOM_EVENT_FOCUS }, + { GF_EVENT_FOCUSOUT, "focusout", GF_DOM_EVENT_FOCUS }, + { GF_EVENT_CHANGE, "change", GF_DOM_EVENT_FOCUS }, + { GF_EVENT_FOCUS, "focus", GF_DOM_EVENT_FOCUS }, + { GF_EVENT_BLUR, "blur", GF_DOM_EVENT_FOCUS }, + + /*key events*/ + { GF_EVENT_KEYDOWN, "keydown", GF_DOM_EVENT_KEY }, + { GF_EVENT_KEYDOWN, "accesskey", GF_DOM_EVENT_KEY }, + { GF_EVENT_KEYDOWN, "keypress", GF_DOM_EVENT_KEY }, + { GF_EVENT_KEYUP, "keyup", GF_DOM_EVENT_KEY }, + { GF_EVENT_LONGKEYPRESS, "longaccesskey", GF_DOM_EVENT_KEY }, + + { GF_EVENT_CLICK, "click", GF_DOM_EVENT_MOUSE }, + { GF_EVENT_DBLCLICK, "dblclick", GF_DOM_EVENT_MOUSE }, + { GF_EVENT_MOUSEDOWN, "mousedown", GF_DOM_EVENT_MOUSE }, + { GF_EVENT_MOUSEMOVE, "mousemove", GF_DOM_EVENT_MOUSE }, + { GF_EVENT_MOUSEOUT, "mouseout", GF_DOM_EVENT_MOUSE }, + { GF_EVENT_MOUSEOVER, "mouseover", GF_DOM_EVENT_MOUSE }, + { GF_EVENT_MOUSEUP, "mouseup", GF_DOM_EVENT_MOUSE }, + { GF_EVENT_MOUSEWHEEL, "SVGMousewheel", GF_DOM_EVENT_MOUSE }, + { GF_EVENT_MOUSEWHEEL, "mousewheel", GF_DOM_EVENT_MOUSE }, + + /*activate is not a basic DOM but a MOUSE and KEY event*/ + { GF_EVENT_ACTIVATE, "activate", GF_DOM_EVENT_MOUSE | GF_DOM_EVENT_KEY }, + { GF_EVENT_ACTIVATE, "DOMActivate", GF_DOM_EVENT_MOUSE | GF_DOM_EVENT_KEY }, + + /*text events*/ + { GF_EVENT_TEXTINPUT, "textInput", GF_DOM_EVENT_TEXT }, + { GF_EVENT_TEXTSELECT, "select", GF_DOM_EVENT_TEXT }, + + /*SMIL events*/ + { GF_EVENT_BEGIN, "begin", GF_DOM_EVENT_FAKE }, + { GF_EVENT_BEGIN_EVENT, "beginEvent", GF_DOM_EVENT_SMIL }, + { GF_EVENT_END, "end", GF_DOM_EVENT_FAKE }, + { GF_EVENT_END_EVENT, "endEvent", GF_DOM_EVENT_SMIL }, + { GF_EVENT_REPEAT, "repeat", GF_DOM_EVENT_FAKE }, + { GF_EVENT_REPEAT_EVENT, "repeatEvent", GF_DOM_EVENT_SMIL }, + + /*all SVG/HTML/... UI events*/ + { GF_EVENT_RESIZE, "resize", GF_DOM_EVENT_UI }, + { GF_EVENT_SCROLL, "scroll", GF_DOM_EVENT_UI }, + { GF_EVENT_ZOOM, "zoom", GF_DOM_EVENT_UI }, + + + { GF_EVENT_LOAD, "SVGLoad", GF_DOM_EVENT_DOM }, + { GF_EVENT_RESIZE, "SVGResize", GF_DOM_EVENT_UI }, + { GF_EVENT_SCROLL, "SVGScroll", GF_DOM_EVENT_UI }, + { GF_EVENT_ZOOM, "SVGZoom", GF_DOM_EVENT_UI }, + + /*mutation events and DCCI*/ + { GF_EVENT_TREE_MODIFIED, "DOMSubtreeModified", GF_DOM_EVENT_MUTATION }, + { GF_EVENT_NODE_INSERTED, "DOMNodeInserted", GF_DOM_EVENT_MUTATION }, + { GF_EVENT_NODE_REMOVED, "DOMNodeRemoved", GF_DOM_EVENT_MUTATION }, + { GF_EVENT_NODE_REMOVED_DOC, "DOMNodeRemovedFromDocument", GF_DOM_EVENT_MUTATION }, + { GF_EVENT_NODE_INSERTED_DOC, "DOMNodeInsertedIntoDocument", GF_DOM_EVENT_MUTATION }, + { GF_EVENT_ATTR_MODIFIED, "DOMAttrModified", GF_DOM_EVENT_MUTATION }, + { GF_EVENT_CHAR_DATA_MODIFIED, "DOMCharacterDataModified", GF_DOM_EVENT_MUTATION }, + { GF_EVENT_NODE_NAME_CHANGED, "DOMElementNameChanged", GF_DOM_EVENT_MUTATION }, + { GF_EVENT_ATTR_NAME_CHANGED, "DOMAttributeNameChanged", GF_DOM_EVENT_MUTATION }, + { GF_EVENT_DCCI_PROP_CHANGE, "DCCI-prop-change", GF_DOM_EVENT_MUTATION }, + + /*LASeR events - some events are attached to other categorues*/ + { GF_EVENT_ACTIVATED, "activatedEvent", GF_DOM_EVENT_LASER }, + { GF_EVENT_DEACTIVATED, "deactivatedEvent", GF_DOM_EVENT_LASER }, + { GF_EVENT_EXECUTION_TIME, "executionTime", GF_DOM_EVENT_FAKE }, + { GF_EVENT_PAUSE, "pause", GF_DOM_EVENT_SMIL }, + { GF_EVENT_PAUSED_EVENT, "pausedEvent", GF_DOM_EVENT_SMIL }, + { GF_EVENT_PLAY, "play", GF_DOM_EVENT_SMIL }, + { GF_EVENT_RESUME_EVENT, "resumedEvent", GF_DOM_EVENT_SMIL }, + { GF_EVENT_REPEAT_KEY, "repeatKey", GF_DOM_EVENT_KEY }, + { GF_EVENT_SHORT_ACCESSKEY, "shortAccessKey", GF_DOM_EVENT_KEY }, + + /*LASeR unofficial events*/ + { GF_EVENT_BATTERY, "battery", GF_DOM_EVENT_LASER }, + { GF_EVENT_CPU, "cpu", GF_DOM_EVENT_LASER }, + + /*MediaAccess events*/ + { GF_EVENT_MEDIA_BEGIN_SESSION_SETUP, "BeginSessionSetup", GF_DOM_EVENT_MEDIA_ACCESS }, + { GF_EVENT_MEDIA_END_SESSION_SETUP, "EndSessionSetup", GF_DOM_EVENT_MEDIA_ACCESS }, + { GF_EVENT_MEDIA_DATA_REQUEST, "DataRequest", GF_DOM_EVENT_MEDIA_ACCESS }, + { GF_EVENT_MEDIA_PLAYABLE, "Playable", GF_DOM_EVENT_MEDIA_ACCESS }, + { GF_EVENT_MEDIA_NOT_PLAYABLE, "NotPlayable", GF_DOM_EVENT_MEDIA_ACCESS }, + { GF_EVENT_MEDIA_DATA_PROGRESS, "DataReceptionProgress", GF_DOM_EVENT_MEDIA_ACCESS }, + { GF_EVENT_MEDIA_END_OF_DATA, "EndOfDataReception", GF_DOM_EVENT_MEDIA_ACCESS }, + { GF_EVENT_MEDIA_STOP, "Stop", GF_DOM_EVENT_MEDIA_ACCESS }, + { GF_EVENT_MEDIA_ERROR, "Error", GF_DOM_EVENT_MEDIA_ACCESS }, + +}; + +u32 gf_dom_event_type_by_name(const char *name) +{ + u32 i, count; + count = sizeof(defined_dom_events) / sizeof(struct dom_event_def); + if (!name) return GF_EVENT_UNKNOWN; + if ((name[0]=='o') && (name[1]=='n')) name += 2; + for (i=0;i= 'A' && c[0] <= 'Z') + return (GF_KEY_A + (c[0] - 'A') ); + + if (c[0] >= '0' && c[0] <= '9') + return ( GF_KEY_0 + (c[0] - '0') ); + + switch (c[0]) { + case '@': return GF_KEY_AT; + case '*': return GF_KEY_STAR; + case '#': return GF_KEY_NUMBER; + case ' ': return GF_KEY_SPACE; + case '!': return GF_KEY_EXCLAMATION; + case '"': return GF_KEY_QUOTATION; + case '$': return GF_KEY_DOLLAR; + case '&': return GF_KEY_AMPERSAND; + case '\'': return GF_KEY_APOSTROPHE; + case '(': return GF_KEY_LEFTPARENTHESIS; + case ')': return GF_KEY_RIGHTPARENTHESIS; + case '+': return GF_KEY_PLUS; + case ',': return GF_KEY_COMMA; + case '-': return GF_KEY_HYPHEN; + case '.': return GF_KEY_FULLSTOP; + case '/': return GF_KEY_SLASH; + case ':': return GF_KEY_COLON; + case ';': return GF_KEY_SEMICOLON; + case '<': return GF_KEY_LESSTHAN; + case '=': return GF_KEY_EQUALS; + case '>': return GF_KEY_GREATERTHAN; + case '?': return GF_KEY_QUESTION; + case '[': return GF_KEY_LEFTSQUAREBRACKET; + case '\\': return GF_KEY_BACKSLASH; + case ']': return GF_KEY_RIGHTSQUAREBRACKET; + case '^': return GF_KEY_CIRCUM; + case '_': return GF_KEY_UNDERSCORE; + case '`': return GF_KEY_GRAVEACCENT; + case '{': return GF_KEY_LEFTCURLYBRACKET; + case '|': return GF_KEY_PIPE; + case '}': return GF_KEY_RIGHTCURLYBRACKET; + case '¡': return GF_KEY_INVERTEXCLAMATION; + default: return GF_KEY_UNIDENTIFIED; + } + } else { + u32 i, count; + count = sizeof(predefined_key_identifiers) / sizeof(struct predef_keyid); + for (i=0; ired = INT2FIX(predefined_colors[i].r) / 255; + col->green = INT2FIX(predefined_colors[i].g) / 255; + col->blue = INT2FIX(predefined_colors[i].b) / 255; + col->type = SVG_COLOR_RGBCOLOR; + return; + } + } + count = sizeof(system_colors) / sizeof(struct sys_col); + for (i=0; itype = system_colors[i].type; + return; + } + } +} + +const char *gf_svg_get_system_paint_server_name(u32 paint_type) +{ + u32 i, count; + count = sizeof(system_colors) / sizeof(struct sys_col); + for (i=0; itype = SVG_COLOR_CURRENTCOLOR; + return; + } else if (!strcmp(str, "inherit")) { + col->type = SVG_COLOR_INHERIT; + return; + } else if (str[0]=='#') { + u32 val; + sscanf(str+1, "%x", &val); + if (strlen(str) == 7) { + col->red = INT2FIX((val>>16) & 0xFF) / 255; + col->green = INT2FIX((val>>8) & 0xFF) / 255; + col->blue = INT2FIX(val & 0xFF) / 255; + } else { + col->red = INT2FIX((val>>8) & 0xF) / 15; + col->green = INT2FIX((val>>4) & 0xF) / 15; + col->blue = INT2FIX(val & 0xF) / 15; + } + col->type = SVG_COLOR_RGBCOLOR; + } else if (strstr(str, "rgb(") || strstr(str, "RGB(")) { + Float _val; + u8 is_percentage= 0; + if (strstr(str, "%")) is_percentage = 1; + str = strstr(str, "("); + str++; + sscanf(str, "%f", &_val); col->red = FLT2FIX(_val); + str = strstr(str, ","); + str++; + sscanf(str, "%f", &_val); col->green = FLT2FIX(_val); + str = strstr(str, ","); + str++; + sscanf(str, "%f", &_val); col->blue = FLT2FIX(_val); + if (is_percentage) { + col->red /= 100; + col->green /= 100; + col->blue /= 100; + } else { + col->red /= 255; + col->green /= 255; + col->blue /= 255; + } + col->type = SVG_COLOR_RGBCOLOR; + } else if ((str[0] >= 'a' && str[0] <= 'z') + || (str[0] >= 'A' && str[0] <= 'Z')) { + svg_parse_named_color(col, str); + } else { + Float _r, _g, _b; + sscanf(str, "%f %f %f", &_r, &_g, &_b); + col->red = FLT2FIX(_r); + col->green = FLT2FIX(_g); + col->blue = FLT2FIX(_b); + col->type = SVG_COLOR_RGBCOLOR; + } +} + +/* + Reads a float in scientific notation + trims any space, comma, semi-column before or after + reads an optional + or - + then reads a digit between 0 and 9 + optionally followed by an '.' and digits between 0 and 9 + optionally followed by e or E and digits between 0 and 9 + Returns the number of char read in d +*/ +static u32 svg_parse_float(char *d, Fixed *f, Bool is_angle) +{ + Bool is_negative = 0; + Float _val = 0; + u32 i = 0; + while ((d[i] != 0) && strchr(" ,;\r\n\t", d[i])) i++; + if (!d[i]) goto end; + if (d[i] == '+') i++; + if (d[i] == '-') { + is_negative = 1; + i++; + } + if ((d[i]=='N') && (d[i+1]=='a') && (d[i+2]=='N')) { + i+= 3; + _val = 0; + goto end; + } + while (d[i] >= '0' && d[i] <= '9' && d[i] != 0) { + _val = _val*10 + (d[i]-'0'); + i++; + } + if (!d[i]) goto end; + if (d[i] == '.') { + u32 nb_digit_after = 0; + i++; + while (d[i] >= '0' && d[i] <= '9' && d[i] != 0) { + _val = _val*10 + (d[i]-'0'); + nb_digit_after++; + i++; + } + _val /= (Float)pow(10,nb_digit_after); + if (!d[i]) goto end; + } + if (d[i] == 'e' || d[i] == 'E') { + Bool neg_exp = 0; + s32 exp = 0; + i++; + if (d[i] == '+') i++; + else if (d[i] == '-') { + i++; + neg_exp=1; + } + while (d[i] >= '0' && d[i] <= '9' && d[i] != 0) { + exp = exp*10 + (d[i]-'0'); + i++; + } + _val *= (Float)pow(10, neg_exp ? -exp : exp); + } +end: + if (is_negative) _val *= -1; + //_val = atof(d); + if (is_angle) { + _val/=180; + (*f) = gf_mulfix(FLT2FIX(_val), GF_PI); + } else { + (*f) = FLT2FIX(_val); + } + while (d[i] != 0 && (d[i] == ' ' || d[i] == ',' || d[i] == ';')) i++; + return i; +} + +/* + Parse an Offset Value, i.e +/- Clock Value +*/ +static GF_Err svg_parse_clock_value(char *d, Double *clock_value) +{ + char *tmp; + s32 sign = 1; + + if (!d) return GF_BAD_PARAM; + + if (!d[0]) return GF_BAD_PARAM; + + if (d[0] == '+') d++; + else if (d[0] == '-') { sign = -1; d++; } + + if (!d[0]) return GF_BAD_PARAM; + + /* According to SVG, the following are invalid syntaxes (see animate-elem-225-t.svg) + '+-2s' + '1++s' even though sscanf returns the right values + */ + if (strchr(d, '+') || strchr(d, '-')) return GF_BAD_PARAM; + + /* No embedded white space is allowed in clock values, + although leading and trailing white space characters will be ignored.*/ + while (*d == ' ') d++; + + if ((tmp = strchr(d, ':'))) { + /* Full or Partial Clock value */ + tmp++; + if ((tmp = strchr(tmp, ':'))) { + /* Full Clock value : hh:mm:ss(.frac) */ + u32 hours; + u32 minutes; + Float seconds; + if (sscanf(d, "%d:%d:%f", &hours, &minutes, &seconds) < 3) return GF_BAD_PARAM; + *clock_value = hours*3600 + minutes*60 + seconds; + } else { + /* Partial Clock value : mm:ss(.frac) */ + s32 minutes; + Float seconds; + if (sscanf(d, "%d:%f", &minutes, &seconds) < 2) return GF_BAD_PARAM; + *clock_value = minutes*60 + seconds; + } + } else if ((tmp = strstr(d, "h"))) { + Float f; + if (sscanf(d, "%fh", &f) == 0) return GF_BAD_PARAM; + *clock_value = 3600*f; + } else if (strstr(d, "min")) { + Float f; + if (sscanf(d, "%fmin", &f) == 0) return GF_BAD_PARAM; + *clock_value = 60*f; + } else if ((tmp = strstr(d, "ms"))) { + Float f; + if (sscanf(d, "%fms", &f) == 0) return GF_BAD_PARAM; + *clock_value = f/1000; + } else if (strchr(d, 's')) { + Float f; + if (sscanf(d, "%fs", &f) == 0) return GF_BAD_PARAM; + *clock_value = f; + } else { + Float f; + if (sscanf(d, "%f", &f) == 0) return GF_BAD_PARAM; + *clock_value = f; + } + *clock_value *= sign; + return GF_OK; +} +/* Parses one SVG time value: + indefinite, + element_id.event_name + wallclock, + accessKey, + events, + clock value. + */ +static GF_Err smil_parse_time(GF_Node *elt, SMIL_Time *v, char *d) +{ + GF_Err e = GF_OK; + char *tmp; + + /* Offset Values */ + if ((d[0] >= '0' && d[0] <= '9') || d[0] == '+' || d[0] == '-'){ + v->type = GF_SMIL_TIME_CLOCK; + return svg_parse_clock_value(d, &(v->clock)); + } + + /* Indefinite Values */ + else if (!strcmp(d, "indefinite")) { + v->type = GF_SMIL_TIME_INDEFINITE; + return GF_OK; + } + + /* Wallclock Values */ + else if ((tmp = strstr(d, "wallclock("))) { + u32 year, month, day; + u32 hours, minutes; + u32 nhours, nminutes; + Float seconds; + char *tmp1, *tmp2; + + v->type = GF_SMIL_TIME_WALLCLOCK; + tmp += 10; + if ((tmp1 = strchr(tmp, 'T')) ) { + /* From tmp to wallStartTime, we parse a date */ + sscanf(tmp, "%d-%d-%dT", &year, &month, &day); + tmp1++; + tmp = tmp1; + } + if ((tmp1 = strchr(tmp, ':')) ) { + if ((tmp2 = strchr(tmp1, ':')) ) { + /* HHMMSS */ + sscanf(tmp, "%d:%d:%f", &hours, &minutes, &seconds); + } else { + /* HHMM */ + sscanf(tmp, "%d:%d", &hours, &minutes); + } + } + if (strchr(tmp, 'Z')) { + return GF_OK; + } else { + if ( (tmp1 = strchr(tmp, '+')) ) { + sscanf(tmp1, "%d:%d", &nhours, &nminutes); + } else if ( (tmp1 = strchr(tmp, '-')) ) { + sscanf(tmp1, "%d:%d", &nhours, &nminutes); + } + } + return GF_OK; + } + + /* AccessKey Values */ + else if ((tmp = strstr(d, "accessKey("))) { + char *sep; + v->type = GF_SMIL_TIME_EVENT; + v->event.type = GF_EVENT_KEYDOWN; + v->element = elt->sgprivate->scenegraph->RootNode; + tmp+=10; + sep = strchr(d, ')'); + sep[0] = 0; + v->event.parameter = gf_dom_get_key_type(tmp); + sep++; + if ((tmp = strchr(sep, '+')) || (tmp = strchr(sep, '-'))) { + char c = *tmp; + tmp++; + e = svg_parse_clock_value(tmp, &(v->clock)); + if (c == '-') v->clock *= -1; + } + return e; + } + + else { + Bool had_param = 0; + char *tmp2; + v->type = GF_SMIL_TIME_EVENT; + if ((tmp = strchr(d, '.'))) { + tmp[0] = 0; + if (strlen(d) == 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] expecting an id before '.' in SMIL Time .%s\n", tmp+1)); + return GF_BAD_PARAM; + } + v->element_id = strdup(d); + tmp[0] = '.'; + tmp++; + } else { + tmp = d; + } + if ((tmp2 = strchr(tmp, '('))) { + tmp2[0] = 0; + v->event.type = gf_dom_event_type_by_name(tmp); + tmp2[0] = '('; + tmp2++; + had_param = 1; + v->event.parameter = atoi(tmp2); + tmp = strchr(tmp2, ')'); + if (!tmp) { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] expecting ')' in SMIL Time %s\n", d)); + return GF_BAD_PARAM; + } + tmp++; + } + if ((tmp2 = strchr(tmp, '+')) || (tmp2 = strchr(tmp, '-'))) { + char c = *tmp2; + char *tmp3 = tmp2; + tmp2[0] = 0; + tmp3--; + while (*tmp3==' ') { *tmp3=0; tmp3--; } + if (v->event.type == 0) v->event.type = gf_dom_event_type_by_name(tmp); + if (!had_param && (v->event.type == GF_EVENT_REPEAT || v->event.type == GF_EVENT_REPEAT_EVENT)) + v->event.parameter = 1; + tmp2[0] = c; + tmp2++; + e = svg_parse_clock_value(tmp2, &(v->clock)); + if (c == '-') v->clock *= -1; + return e; + } else { + if (v->event.type == 0) v->event.type = gf_dom_event_type_by_name(tmp); + if (!had_param && (v->event.type == GF_EVENT_REPEAT || v->event.type == GF_EVENT_REPEAT_EVENT)) + v->event.parameter = 1; + } + } + return GF_OK; +} + +/* Parses a list of SVG transformations and collapses them in the given matrix */ +void gf_svg_parse_transformlist(GF_Matrix2D *mat, char *attribute_content) +{ + GF_Matrix2D tmp; + + char *str; + u32 i; + + gf_mx2d_init(*mat); + + str = attribute_content; + i = 0; + while (str[i] != 0) { + while (str[i] == ' ') i++; + if (str[i] == ',') i++; + while (str[i] == ' ') i++; + if (strstr(str+i, "scale")==str+i) { + Fixed sx, sy; + i += 5; + while(str[i] != 0 && str[i] == ' ') i++; + if (str[i] == '(') { + i++; + i+=svg_parse_float(&(str[i]), &sx, 0); + if (str[i] == ')') { + sy = sx; + } else { + i+=svg_parse_float(&(str[i]), &sy, 0); + } + i++; + } + gf_mx2d_init(tmp); + gf_mx2d_add_scale(&tmp, sx, sy); + gf_mx2d_add_matrix(&tmp, mat); + gf_mx2d_copy(*mat, tmp); + while(str[i] != 0 && str[i] == ' ') i++; + } else if (strstr(str+i, "translate")==str+i) { + Fixed tx, ty; + i += 9; + while(str[i] != 0 && str[i] == ' ') i++; + if (str[i] == '(') { + i++; + i+=svg_parse_float(&(str[i]), &tx, 0); + if (str[i] == ')') { + ty = 0; + } else { + i+=svg_parse_float(&(str[i]), &ty, 0); + } + i++; + } + gf_mx2d_init(tmp); + gf_mx2d_add_translation(&tmp, tx, ty); + gf_mx2d_add_matrix(&tmp, mat); + gf_mx2d_copy(*mat, tmp); + while(str[i] != 0 && str[i] == ' ') i++; + } else if (strstr(str+i, "rotate")==str+i) { + Fixed angle, cx, cy; + i += 6; + while(str[i] != 0 && str[i] == ' ') i++; + if (str[i] == '(') { + i++; + i+=svg_parse_float(&(str[i]), &angle, 1); + if (str[i] == ')') { + cx = cy = 0; + } else { + i+=svg_parse_float(&(str[i]), &cx, 0); + i+=svg_parse_float(&(str[i]), &cy, 0); + } + i++; + } + gf_mx2d_init(tmp); + gf_mx2d_add_rotation(&tmp, cx, cy, angle); + gf_mx2d_add_matrix(&tmp, mat); + gf_mx2d_copy(*mat, tmp); + while(str[i] != 0 && str[i] == ' ') i++; + } else if (strstr(str+i, "skewX")==str+i) { + Fixed angle; + i += 5; + while(str[i] != 0 && str[i] == ' ') i++; + if (str[i] == '(') { + i++; + i+=svg_parse_float(&(str[i]), &angle, 1); + i++; + } + gf_mx2d_init(tmp); + gf_mx2d_add_skew_x(&tmp, angle); + gf_mx2d_add_matrix(&tmp, mat); + gf_mx2d_copy(*mat, tmp); + while(str[i] != 0 && str[i] == ' ') i++; + } else if (strstr(str+i, "skewY")==str+i) { + Fixed angle; + i += 5; + while(str[i] != 0 && str[i] == ' ') i++; + if (str[i] == '(') { + i++; + i+=svg_parse_float(&(str[i]), &angle, 1); + i++; + } + gf_mx2d_init(tmp); + gf_mx2d_add_skew_y(&tmp, angle); + gf_mx2d_add_matrix(&tmp, mat); + gf_mx2d_copy(*mat, tmp); + while(str[i] != 0 && str[i] == ' ') i++; + } else if (strstr(str+i, "matrix")==str+i) { + i+=6; + while(str[i] != 0 && str[i] == ' ') i++; + if (str[i] == '(') { + i++; + i+=svg_parse_float(&(str[i]), &(tmp.m[0]), 0); + i+=svg_parse_float(&(str[i]), &(tmp.m[3]), 0); + i+=svg_parse_float(&(str[i]), &(tmp.m[1]), 0); + i+=svg_parse_float(&(str[i]), &(tmp.m[4]), 0); + i+=svg_parse_float(&(str[i]), &(tmp.m[2]), 0); + i+=svg_parse_float(&(str[i]), &(tmp.m[5]), 0); + i++; + } + gf_mx2d_add_matrix(&tmp, mat); + gf_mx2d_copy(*mat, tmp); + while(str[i] != 0 && str[i] == ' ') i++; + if (str[i] == ')') i++; + } + /*for svgView parsing*/ + if (str[i] == ')') i++; + } +} + +/* Parses an SVG transform attribute and collapses all in the given matrix */ +static Bool svg_parse_transform(SVG_Transform *t, char *attribute_content) +{ + char *str; + u32 i; + str = attribute_content; + i = 0; + + if ((str = strstr(attribute_content, "ref("))) { + t->is_ref = 1; + gf_mx2d_init(t->mat); + str+=4; + while(str[i] != 0 && str[i] == ' ') i++; + if (str[i] == 's' && str[i+1] == 'v' && str[i+2] == 'g') { + i+=3; + while(str[i] != 0 && str[i] == ' ') i++; + if (str[i] == ',') i++; + else if (str[i] == ')') { + i++; + return GF_OK; + } + i+=svg_parse_float(&(str[i]), &(t->mat.m[2]), 0); + i+=svg_parse_float(&(str[i]), &(t->mat.m[5]), 0); + while(str[i] != 0 && str[i] == ' ') i++; + if (str[i] == ')') i++; + i++; + return GF_OK; + } else { + while(str[i] != 0 && str[i] != ')') i++; + i++; + return GF_NOT_SUPPORTED; + } + + } else { + gf_svg_parse_transformlist(&t->mat, attribute_content); + } + return GF_OK; +} + +#undef REMOVE_ALLOC + +#if USE_GF_PATH + +//#define PARSE_PATH_ONLY + +static void svg_parse_path(SVG_PathData *path, char *attribute_content) +{ + char *d = attribute_content; + + /* Point used to start a new subpath when the previous subpath is closed */ + SVG_Point prev_m_pt; + /* Point used to convert relative 'lower-case commands' into absolute */ + SVG_Point rel_ref_pt; + /* Points used to convert S, T commands into C, Q */ + SVG_Point orig, ct_orig, ct_end, end; + /* Used by elliptical arcs */ + Fixed x_axis_rotation, large_arc_flag, sweep_flag; + + char c, prev_c; + u32 i; + + if (*d == 0) return; + + i = 0; + prev_c = 'M'; + orig.x = orig.y = ct_orig.x = ct_orig.y = prev_m_pt.x = prev_m_pt.y = rel_ref_pt.x = rel_ref_pt.y = end.x = end.y = 0; + while(1) { + while ( (d[i]==' ') || (d[i] =='\t') || (d[i] =='\r') || (d[i] =='\n') ) i++; + c = d[i]; + if (! c) break; +next_command: + switch (c) { + case 'm': + case 'M': + i++; + i += svg_parse_float(&(d[i]), &(orig.x), 0); + i += svg_parse_float(&(d[i]), &(orig.y), 0); + if (c == 'm') { + orig.x += rel_ref_pt.x; + orig.y += rel_ref_pt.y; + } +#ifndef PARSE_PATH_ONLY + gf_path_add_move_to(path, orig.x, orig.y); +#endif + rel_ref_pt = orig; + prev_m_pt = orig; + /*provision for nextCurveTo when no curve is specified: + "If there is no previous command or if the previous command was not an C, c, S or s, + assume the first control point is coincident with the current point. + */ + ct_orig = orig; + prev_c = c; + break; + case 'L': + case 'l': + i++; + i += svg_parse_float(&(d[i]), &(orig.x), 0); + i += svg_parse_float(&(d[i]), &(orig.y), 0); + if (c == 'l') { + orig.x += rel_ref_pt.x; + orig.y += rel_ref_pt.y; + } +#ifndef PARSE_PATH_ONLY + gf_path_add_line_to(path, orig.x, orig.y); +#endif + rel_ref_pt = orig; + orig = end; + /*cf above*/ + ct_orig = orig; + prev_c = c; + break; + case 'H': + case 'h': + i++; + i += svg_parse_float(&(d[i]), &(orig.x), 0); + if (c == 'h') { + orig.x += rel_ref_pt.x; + } + orig.y = rel_ref_pt.y; +#ifndef PARSE_PATH_ONLY + gf_path_add_line_to(path, orig.x, orig.y); +#endif + rel_ref_pt.x = orig.x; + orig = end; + /*cf above*/ + ct_orig = orig; + prev_c = c; + break; + case 'V': + case 'v': + i++; + i += svg_parse_float(&(d[i]), &(orig.y), 0); + if (c == 'v') { + orig.y += rel_ref_pt.y; + } + orig.x = rel_ref_pt.x; +#ifndef PARSE_PATH_ONLY + gf_path_add_line_to(path, orig.x, orig.y); +#endif + rel_ref_pt.y = orig.y; + orig = end; + /*cf above*/ + ct_orig = orig; + prev_c = c; + break; + case 'C': + case 'c': + i++; + i += svg_parse_float(&(d[i]), &(ct_orig.x), 0); + i += svg_parse_float(&(d[i]), &(ct_orig.y), 0); + if (c == 'c') { + ct_orig.x += rel_ref_pt.x; + ct_orig.y += rel_ref_pt.y; + } + i += svg_parse_float(&(d[i]), &(ct_end.x), 0); + i += svg_parse_float(&(d[i]), &(ct_end.y), 0); + if (c == 'c') { + ct_end.x += rel_ref_pt.x; + ct_end.y += rel_ref_pt.y; + } + i += svg_parse_float(&(d[i]), &(end.x), 0); + i += svg_parse_float(&(d[i]), &(end.y), 0); + if (c == 'c') { + end.x += rel_ref_pt.x; + end.y += rel_ref_pt.y; + } +#ifndef PARSE_PATH_ONLY + gf_path_add_cubic_to(path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y); +#endif + rel_ref_pt = end; + ct_orig = ct_end; + orig = end; + prev_c = c; + break; + case 'S': + case 's': + i++; + ct_orig.x = 2*orig.x - ct_orig.x; + ct_orig.y = 2*orig.y - ct_orig.y; + i += svg_parse_float(&(d[i]), &(ct_end.x), 0); + i += svg_parse_float(&(d[i]), &(ct_end.y), 0); + if (c == 's') { + ct_end.x += rel_ref_pt.x; + ct_end.y += rel_ref_pt.y; + } + i += svg_parse_float(&(d[i]), &(end.x), 0); + i += svg_parse_float(&(d[i]), &(end.y), 0); + if (c == 's') { + end.x += rel_ref_pt.x; + end.y += rel_ref_pt.y; + } +#ifndef PARSE_PATH_ONLY + gf_path_add_cubic_to(path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y); +#endif + rel_ref_pt = end; + ct_orig = ct_end; + orig = end; + prev_c = c; + break; + case 'Q': + case 'q': + i++; + i += svg_parse_float(&(d[i]), &(ct_orig.x), 0); + i += svg_parse_float(&(d[i]), &(ct_orig.y), 0); + if (c == 'q') { + ct_orig.x += rel_ref_pt.x; + ct_orig.y += rel_ref_pt.y; + } + i += svg_parse_float(&(d[i]), &(end.x), 0); + i += svg_parse_float(&(d[i]), &(end.y), 0); + if (c == 'q') { + end.x += rel_ref_pt.x; + end.y += rel_ref_pt.y; + } +#ifndef PARSE_PATH_ONLY + gf_path_add_quadratic_to(path, ct_orig.x, ct_orig.y, end.x, end.y); +#endif + rel_ref_pt = end; + orig = end; + prev_c = c; + break; + case 'T': + case 't': + i++; + ct_orig.x = 2*orig.x - ct_orig.x; + ct_orig.y = 2*orig.y - ct_orig.y; + i += svg_parse_float(&(d[i]), &(end.x), 0); + i += svg_parse_float(&(d[i]), &(end.y), 0); + if (c == 't') { + end.x += rel_ref_pt.x; + end.y += rel_ref_pt.y; + } +#ifndef PARSE_PATH_ONLY + gf_path_add_quadratic_to(path, ct_orig.x, ct_orig.y, end.x, end.y); +#endif + rel_ref_pt = end; + orig = end; + prev_c = c; + break; + case 'A': + case 'a': + i++; + + i += svg_parse_float(&(d[i]), &(orig.x), 0); + i += svg_parse_float(&(d[i]), &(orig.y), 0); + + i += svg_parse_float(&(d[i]), &(x_axis_rotation), 0); + i += svg_parse_float(&(d[i]), &(large_arc_flag), 0); + i += svg_parse_float(&(d[i]), &(sweep_flag), 0); + + i += svg_parse_float(&(d[i]), &(end.x), 0); + i += svg_parse_float(&(d[i]), &(end.y), 0); + if (c == 'a') { + end.x += rel_ref_pt.x; + end.y += rel_ref_pt.y; + } + gf_path_add_svg_arc_to(path, end.x, end.y, orig.x, orig.y, x_axis_rotation , (large_arc_flag == FIX_ONE ? 1 : 0), (sweep_flag == FIX_ONE ? 1 : 0)); + rel_ref_pt = end; + ct_orig = end; + prev_c = c; + break; + case 'Z': + case 'z': + i++; +#ifndef PARSE_PATH_ONLY + gf_path_close(path); +#endif + prev_c = c; + rel_ref_pt = prev_m_pt; + break; + default: + i--; + switch (prev_c) { + case 'M': + c = 'L'; + break; + case 'm': + c = 'l'; + break; + default: + c = prev_c; + } + goto next_command; + } + } +} +#else +/* TODO: Change the function to handle elliptical arcs, requires changing data structure */ +static void svg_parse_path(SVG_PathData *d_attribute, char *attribute_content) +{ + GF_List *d_commands = d_attribute->commands; + GF_List *d_points = d_attribute->points; + char *d = attribute_content; + + if (strlen(d)) { + SVG_Point *pt, cur_pt, prev_m_pt; + u8 *command; + u32 i, k; + char c, prev_c = 'M'; +#ifdef REMOVE_ALLOC + GF_SAFEALLOC(pt, SVG_Point) +#endif + i = 0; + cur_pt.x = cur_pt.y = 0; + prev_m_pt.x = prev_m_pt.y = 0; + while(1) { + while ( (d[i]==' ') || (d[i] =='\t') ) i++; + c = d[i]; + if (! c) break; +next_command: + switch (c) { + case 'm': + case 'M': + i++; +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(command, u8) + gf_list_add(d_commands, command); + *command = SVG_PATHCOMMAND_M; + + GF_SAFEALLOC(pt, SVG_Point) + gf_list_add(d_points, pt); +#endif + i += svg_parse_float(&(d[i]), &(pt->x), 0); + i += svg_parse_float(&(d[i]), &(pt->y), 0); + if (c == 'm') { + pt->x += cur_pt.x; + pt->y += cur_pt.y; + } + cur_pt.x = pt->x; + cur_pt.y = pt->y; + prev_m_pt = cur_pt; + prev_c = c; + break; + case 'L': + case 'l': + i++; +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(command, u8) + gf_list_add(d_commands, command); + *command = SVG_PATHCOMMAND_L; + + GF_SAFEALLOC(pt, SVG_Point) + gf_list_add(d_points, pt); +#endif + i += svg_parse_float(&(d[i]), &(pt->x), 0); + i += svg_parse_float(&(d[i]), &(pt->y), 0); + if (c == 'l') { + pt->x += cur_pt.x; + pt->y += cur_pt.y; + } + cur_pt.x = pt->x; + cur_pt.y = pt->y; + prev_c = c; + break; + case 'H': + case 'h': + i++; +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(command, u8) + gf_list_add(d_commands, command); + *command = SVG_PATHCOMMAND_L; + + GF_SAFEALLOC(pt, SVG_Point) + gf_list_add(d_points, pt); +#endif + i += svg_parse_float(&(d[i]), &(pt->x), 0); + if (c == 'h') { + pt->x += cur_pt.x; + } + pt->y = cur_pt.y; + cur_pt.x = pt->x; + prev_c = c; + break; + case 'V': + case 'v': + i++; +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(command, u8) + gf_list_add(d_commands, command); + *command = SVG_PATHCOMMAND_L; + + GF_SAFEALLOC(pt, SVG_Point) + gf_list_add(d_points, pt); +#endif + i += svg_parse_float(&(d[i]), &(pt->y), 0); + if (c == 'v') { + pt->y += cur_pt.y; + } + pt->x = cur_pt.x; + cur_pt.y = pt->y; + prev_c = c; + break; + case 'C': + case 'c': + i++; +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(command, u8) + gf_list_add(d_commands, command); + *command = SVG_PATHCOMMAND_C; +#endif + + for (k=0; k<3; k++) { +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(pt, SVG_Point) + gf_list_add(d_points, pt); +#endif + i += svg_parse_float(&(d[i]), &(pt->x), 0); + i += svg_parse_float(&(d[i]), &(pt->y), 0); + if (c == 'c') { + pt->x += cur_pt.x; + pt->y += cur_pt.y; + } + } + cur_pt.x = pt->x; + cur_pt.y = pt->y; + prev_c = c; + break; + case 'S': + case 's': + i++; +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(command, u8) + gf_list_add(d_commands, command); + *command = SVG_PATHCOMMAND_S; +#endif + + for (k=0; k<2; k++) { +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(pt, SVG_Point) + gf_list_add(d_points, pt); +#endif + i += svg_parse_float(&(d[i]), &(pt->x), 0); + i += svg_parse_float(&(d[i]), &(pt->y), 0); + if (c == 's') { + pt->x += cur_pt.x; + pt->y += cur_pt.y; + } + } + cur_pt.x = pt->x; + cur_pt.y = pt->y; + prev_c = c; + break; + case 'Q': + case 'q': + i++; +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(command, u8) + gf_list_add(d_commands, command); + *command = SVG_PATHCOMMAND_Q; +#endif + + for (k=0; k<2; k++) { +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(pt, SVG_Point) + gf_list_add(d_points, pt); +#endif + i += svg_parse_float(&(d[i]), &(pt->x), 0); + i += svg_parse_float(&(d[i]), &(pt->y), 0); + if (c == 'q') { + pt->x += cur_pt.x; + pt->y += cur_pt.y; + } + } + cur_pt.x = pt->x; + cur_pt.y = pt->y; + prev_c = c; + break; + case 'T': + case 't': + i++; +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(command, u8) + gf_list_add(d_commands, command); + *command = SVG_PATHCOMMAND_T; + + GF_SAFEALLOC(pt, SVG_Point) + gf_list_add(d_points, pt); +#endif + i += svg_parse_float(&(d[i]), &(pt->x), 0); + i += svg_parse_float(&(d[i]), &(pt->y), 0); + if (c == 't') { + pt->x += cur_pt.x; + pt->y += cur_pt.y; + } + cur_pt.x = pt->x; + cur_pt.y = pt->y; + prev_c = c; + break; + case 'A': + case 'a': + { + Fixed tmp; + i++; +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(command, u8) + gf_list_add(d_commands, command); + *command = SVG_PATHCOMMAND_A; + + GF_SAFEALLOC(pt, SVG_Point) + gf_list_add(d_points, pt); +#endif + i += svg_parse_float(&(d[i]), &(pt->x), 0); + i += svg_parse_float(&(d[i]), &(pt->y), 0); + + i += svg_parse_float(&(d[i]), &(tmp), 0); + i += svg_parse_float(&(d[i]), &(tmp), 0); + i += svg_parse_float(&(d[i]), &(tmp), 0); + +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(pt, SVG_Point) + gf_list_add(d_points, pt); +#endif + i += svg_parse_float(&(d[i]), &(pt->x), 0); + i += svg_parse_float(&(d[i]), &(pt->y), 0); + if (c == 'a') { + pt->x += cur_pt.x; + pt->y += cur_pt.y; + } + cur_pt.x = pt->x; + cur_pt.y = pt->y; + } + prev_c = c; + break; + case 'Z': + case 'z': + i++; +#ifndef REMOVE_ALLOC + GF_SAFEALLOC(command, u8) + gf_list_add(d_commands, command); + *command = SVG_PATHCOMMAND_Z; +#endif + prev_c = c; + cur_pt = prev_m_pt; + break; + default: + i--; + switch (prev_c) { + case 'M': + c = 'L'; + break; + case 'm': + c = 'l'; + break; + default: + c = prev_c; + } + goto next_command; + } + } + } +} +#endif + +static void svg_parse_iri(GF_Node *elt, XMLRI *iri, char *attribute_content) +{ + if (iri->string) { + free(iri->string); + iri->string = NULL; + } + /* TODO: Handle xpointer(id()) syntax */ + if (attribute_content[0] == '#') { + iri->string = strdup(attribute_content); + iri->target = gf_sg_find_node_by_name(elt->sgprivate->scenegraph, attribute_content + 1); + if (!iri->target) { + iri->type = XMLRI_STRING; + } else { + iri->type = XMLRI_ELEMENTID; + gf_node_register_iri(elt->sgprivate->scenegraph, iri); + } + } else { + iri->type = XMLRI_STRING; + iri->string = strdup(attribute_content); + } +} + +static void svg_parse_idref(GF_Node *elt, XML_IDREF *iri, char *attribute_content) +{ + iri->type = XMLRI_ELEMENTID; + iri->target = gf_sg_find_node_by_name(elt->sgprivate->scenegraph, attribute_content); + if (!iri->target) { + iri->string = strdup(attribute_content); + } else { + gf_node_register_iri(elt->sgprivate->scenegraph, iri); + } +} + +/* Parses a paint attribute: none, inherit or color */ +static void svg_parse_paint(GF_Node *n, SVG_Paint *paint, char *attribute_content) +{ + if (!strcmp(attribute_content, "none")) { + paint->type = SVG_PAINT_NONE; + } else if (!strcmp(attribute_content, "inherit")) { + paint->type = SVG_PAINT_INHERIT; + } else if (!strncmp(attribute_content, "url(", 4) ) { + char *ext = strrchr(attribute_content, ')'); + paint->type = SVG_PAINT_URI; + if (ext) ext[0] = 0; + svg_parse_iri(n, &paint->iri, attribute_content+4); + if (ext) ext[0] = ')'; + } else { + paint->type = SVG_PAINT_COLOR; + svg_parse_color(&paint->color, attribute_content); + } +} + + +static u32 svg_parse_number(SVG_Number *number, char *value_string, Bool clamp0to1) +{ + char *unit = NULL; + u32 len = 0; + if (!strcmp(value_string, "inherit")) { + number->type = SVG_NUMBER_INHERIT; + return 7; + } else if (!strcmp(value_string, "auto")) { + number->type = SVG_NUMBER_AUTO; + return 4; + } else if (!strcmp(value_string, "auto-reverse")) { + number->type = SVG_NUMBER_AUTO_REVERSE; + return 12; + } else if ((unit = strstr(value_string, "%")) ) { + number->type = SVG_NUMBER_PERCENTAGE; + } else if ((unit = strstr(value_string, "em"))) { + number->type = SVG_NUMBER_EMS; + } else if ((unit = strstr(value_string, "ex"))) { + number->type = SVG_NUMBER_EXS; + } else if ((unit = strstr(value_string, "px"))) { + number->type = SVG_NUMBER_PX; + } else if ((unit = strstr(value_string, "cm"))) { + number->type = SVG_NUMBER_CM; + } else if ((unit = strstr(value_string, "mm"))) { + number->type = SVG_NUMBER_MM; + } else if ((unit = strstr(value_string, "in"))) { + number->type = SVG_NUMBER_IN; + } else if ((unit = strstr(value_string, "pt"))) { + number->type = SVG_NUMBER_PT; + } else if ((unit = strstr(value_string, "pc"))) { + number->type = SVG_NUMBER_PC; + } else { + number->type = SVG_NUMBER_VALUE; + } + if (unit) len = strlen(unit); + len+=svg_parse_float(value_string, &(number->value), 0); + + if (clamp0to1) number->value = MAX(0, MIN(1, number->value)); + return len; +} + +static void svg_parse_visibility(SVG_Visibility *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_VISIBILITY_INHERIT; + } else if (!strcmp(value_string, "visible")) { + *value = SVG_VISIBILITY_VISIBLE; + } else if (!strcmp(value_string, "hidden")) { + *value = SVG_VISIBILITY_HIDDEN; + } else if (!strcmp(value_string, "collapse")) { + *value = SVG_VISIBILITY_COLLAPSE; + } +} + +static void svg_parse_display(SVG_Display *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_DISPLAY_INHERIT; + } else if (!strcmp(value_string, "none")) { + *value = SVG_DISPLAY_NONE; + } else if (!strcmp(value_string, "inline")) { + *value = SVG_DISPLAY_INLINE; + } else if (!strcmp(value_string, "block")) { + *value = SVG_DISPLAY_BLOCK; + } else if (!strcmp(value_string, "list-item")) { + *value = SVG_DISPLAY_LIST_ITEM; + } else if (!strcmp(value_string, "run-in")) { + *value = SVG_DISPLAY_RUN_IN; + } else if (!strcmp(value_string, "compact")) { + *value = SVG_DISPLAY_COMPACT; + } else if (!strcmp(value_string, "marker")) { + *value = SVG_DISPLAY_MARKER; + } else if (!strcmp(value_string, "table")) { + *value = SVG_DISPLAY_TABLE; + } else if (!strcmp(value_string, "inline-table")) { + *value = SVG_DISPLAY_INLINE_TABLE; + } else if (!strcmp(value_string, "table-row-group")) { + *value = SVG_DISPLAY_TABLE_ROW_GROUP; + } else if (!strcmp(value_string, "table-header-group")) { + *value = SVG_DISPLAY_TABLE_HEADER_GROUP; + } else if (!strcmp(value_string, "table-footer-group")) { + *value = SVG_DISPLAY_TABLE_FOOTER_GROUP; + } else if (!strcmp(value_string, "table-row")) { + *value = SVG_DISPLAY_TABLE_ROW; + } else if (!strcmp(value_string, "table-column-group")) { + *value = SVG_DISPLAY_TABLE_COLUMN_GROUP; + } else if (!strcmp(value_string, "table-column")) { + *value = SVG_DISPLAY_TABLE_COLUMN; + } else if (!strcmp(value_string, "table-cell")) { + *value = SVG_DISPLAY_TABLE_CELL; + } else if (!strcmp(value_string, "table-caption")) { + *value = SVG_DISPLAY_TABLE_CAPTION; + } +} + +static void svg_parse_displayalign(SVG_DisplayAlign *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_DISPLAYALIGN_INHERIT; + } else if (!strcmp(value_string, "auto")) { + *value = SVG_DISPLAYALIGN_AUTO; + } else if (!strcmp(value_string, "before")) { + *value = SVG_DISPLAYALIGN_BEFORE; + } else if (!strcmp(value_string, "center")) { + *value = SVG_DISPLAYALIGN_CENTER; + } else if (!strcmp(value_string, "after")) { + *value = SVG_DISPLAYALIGN_AFTER; + } +} + +static void svg_parse_textalign(SVG_TextAlign *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_TEXTALIGN_INHERIT; + } else if (!strcmp(value_string, "start")) { + *value = SVG_TEXTALIGN_START; + } else if (!strcmp(value_string, "center")) { + *value = SVG_TEXTALIGN_CENTER; + } else if (!strcmp(value_string, "end")) { + *value = SVG_TEXTALIGN_END; + } +} + +static void svg_parse_pointerevents(SVG_PointerEvents *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_POINTEREVENTS_INHERIT; + } else if (!strcmp(value_string, "visiblePainted")) { + *value = SVG_POINTEREVENTS_VISIBLEPAINTED; + } else if (!strcmp(value_string, "visibleFill")) { + *value = SVG_POINTEREVENTS_VISIBLEFILL; + } else if (!strcmp(value_string, "visibleStroke")) { + *value = SVG_POINTEREVENTS_VISIBLESTROKE; + } else if (!strcmp(value_string, "visible")) { + *value = SVG_POINTEREVENTS_VISIBLE; + } else if (!strcmp(value_string, "painted")) { + *value = SVG_POINTEREVENTS_PAINTED; + } else if (!strcmp(value_string, "fill")) { + *value = SVG_POINTEREVENTS_FILL; + } else if (!strcmp(value_string, "stroke")) { + *value = SVG_POINTEREVENTS_STROKE; + } else if (!strcmp(value_string, "all")) { + *value = SVG_POINTEREVENTS_ALL; + } else if (!strcmp(value_string, "boundingBox")) { + *value = SVG_POINTEREVENTS_BOUNDINGBOX; + } else if (!strcmp(value_string, "none")) { + *value = SVG_POINTEREVENTS_NONE; + } +} + +static void svg_parse_renderinghint(SVG_RenderingHint *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_RENDERINGHINT_INHERIT; + } else if (!strcmp(value_string, "auto")) { + *value = SVG_RENDERINGHINT_AUTO; + } else if (!strcmp(value_string, "optimizeQuality")) { + *value = SVG_RENDERINGHINT_OPTIMIZEQUALITY; + } else if (!strcmp(value_string, "optimizeSpeed")) { + *value = SVG_RENDERINGHINT_OPTIMIZESPEED; + } else if (!strcmp(value_string, "optimizeLegibility")) { + *value = SVG_RENDERINGHINT_OPTIMIZELEGIBILITY; + } else if (!strcmp(value_string, "crispEdges")) { + *value = SVG_RENDERINGHINT_CRISPEDGES; + } else if (!strcmp(value_string, "geometricPrecision")) { + *value = SVG_RENDERINGHINT_GEOMETRICPRECISION; + } +} + +static void svg_parse_vectoreffect(SVG_VectorEffect *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_VECTOREFFECT_INHERIT; + } else if (!strcmp(value_string, "none")) { + *value = SVG_VECTOREFFECT_NONE; + } else if (!strcmp(value_string, "non-scaling-stroke")) { + *value = SVG_VECTOREFFECT_NONSCALINGSTROKE; + } +} + +static void svg_parse_playbackorder(SVG_VectorEffect *value, char *value_string) +{ + if (!strcmp(value_string, "forwardOnly")) { + *value = SVG_PLAYBACKORDER_FORWARDONLY; + } else if (!strcmp(value_string, "all")) { + *value = SVG_PLAYBACKORDER_ALL; + } +} + +static void svg_parse_timelinebegin(SVG_TimelineBegin *value, char *value_string) +{ + if (!strcmp(value_string, "onStart")) { + *value = SVG_TIMELINEBEGIN_ONSTART; + } else if (!strcmp(value_string, "onLoad")) { + *value = SVG_TIMELINEBEGIN_ONLOAD; + } +} + +static void svg_parse_xmlspace(XML_Space *value, char *value_string) +{ + if (!strcmp(value_string, "default")) { + *value = XML_SPACE_DEFAULT; + } else if (!strcmp(value_string, "preserve")) { + *value = XML_SPACE_PRESERVE; + } +} + +static void svg_parse_xmlev_propagate(XMLEV_Propagate *value, char *value_string) +{ + if (!strcmp(value_string, "continue")) { + *value = XMLEVENT_PROPAGATE_CONTINUE; + } else if (!strcmp(value_string, "stop")) { + *value = XMLEVENT_PROPAGATE_STOP; + } +} + +static void svg_parse_xmlev_defaultAction(XMLEV_DefaultAction *value, char *value_string) +{ + if (!strcmp(value_string, "cancel")) { + *value = XMLEVENT_DEFAULTACTION_CANCEL; + } else if (!strcmp(value_string, "perform")) { + *value = XMLEVENT_DEFAULTACTION_PERFORM; + } +} + +static void svg_parse_xmlev_phase(XMLEV_Phase *value, char *value_string) +{ + if (!strcmp(value_string, "default")) { + *value = XMLEVENT_PHASE_DEFAULT; + } else if (!strcmp(value_string, "capture")) { + *value = XMLEVENT_PHASE_CAPTURE; + } +} + +static void svg_parse_overflow(SVG_Overflow *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_OVERFLOW_INHERIT; + } else if (!strcmp(value_string, "visible")) { + *value = SVG_OVERFLOW_VISIBLE; + } else if (!strcmp(value_string, "hidden")) { + *value = SVG_OVERFLOW_HIDDEN; + } else if (!strcmp(value_string, "scroll")) { + *value = SVG_OVERFLOW_SCROLL; + } else if (!strcmp(value_string, "auto")) { + *value = SVG_OVERFLOW_AUTO; + } +} + +static void svg_parse_textanchor(SVG_TextAnchor *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_TEXTANCHOR_INHERIT; + } else if (!strcmp(value_string, "start")) { + *value = SVG_TEXTANCHOR_START; + } else if (!strcmp(value_string, "middle")) { + *value = SVG_TEXTANCHOR_MIDDLE; + } else if (!strcmp(value_string, "end")) { + *value = SVG_TEXTANCHOR_END; + } +} + +static void svg_parse_clipfillrule(SVG_FillRule *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_FILLRULE_INHERIT; + } else if (!strcmp(value_string, "nonzero")) { + *value = SVG_FILLRULE_NONZERO; + } else if (!strcmp(value_string, "evenodd")) { + *value = SVG_FILLRULE_EVENODD; + } +} + +static void svg_parse_strokelinejoin(SVG_StrokeLineJoin *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_STROKELINEJOIN_INHERIT; + } else if (!strcmp(value_string, "miter")) { + *value = SVG_STROKELINEJOIN_MITER; + } else if (!strcmp(value_string, "round")) { + *value = SVG_STROKELINEJOIN_ROUND; + } else if (!strcmp(value_string, "bevel")) { + *value = SVG_STROKELINEJOIN_BEVEL; + } +} + +static void svg_parse_strokelinecap(SVG_StrokeLineCap *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_STROKELINECAP_INHERIT; + } else if (!strcmp(value_string, "butt")) { + *value = SVG_STROKELINECAP_BUTT; + } else if (!strcmp(value_string, "round")) { + *value = SVG_STROKELINECAP_ROUND; + } else if (!strcmp(value_string, "square")) { + *value = SVG_STROKELINECAP_SQUARE; + } +} + +static void svg_parse_fontfamily(SVG_FontFamily *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + value->type = SVG_FONTFAMILY_INHERIT; + } else { + value->type = SVG_FONTFAMILY_VALUE; + value->value = strdup(value_string); + } +} + +static void svg_parse_fontstyle(SVG_FontStyle *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_FONTSTYLE_INHERIT; + } else if (!strcmp(value_string, "normal")) { + *value = SVG_FONTSTYLE_NORMAL; + } else if (!strcmp(value_string, "italic")) { + *value = SVG_FONTSTYLE_ITALIC; + } else if (!strcmp(value_string, "oblique")) { + *value = SVG_FONTSTYLE_OBLIQUE; + } +} + +static void svg_parse_fontweight(SVG_FontWeight *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_FONTWEIGHT_INHERIT; + } else if (!strcmp(value_string, "normal")) { + *value = SVG_FONTWEIGHT_NORMAL; + } else if (!strcmp(value_string, "bold")) { + *value = SVG_FONTWEIGHT_BOLD; + } else if (!strcmp(value_string, "bolder")) { + *value = SVG_FONTWEIGHT_BOLDER; + } else if (!strcmp(value_string, "lighter")) { + *value = SVG_FONTWEIGHT_LIGHTER; + } else if (!strcmp(value_string, "100")) { + *value = SVG_FONTWEIGHT_100; + } else if (!strcmp(value_string, "200")) { + *value = SVG_FONTWEIGHT_200; + } else if (!strcmp(value_string, "300")) { + *value = SVG_FONTWEIGHT_300; + } else if (!strcmp(value_string, "400")) { + *value = SVG_FONTWEIGHT_400; + } else if (!strcmp(value_string, "500")) { + *value = SVG_FONTWEIGHT_500; + } else if (!strcmp(value_string, "600")) { + *value = SVG_FONTWEIGHT_600; + } else if (!strcmp(value_string, "700")) { + *value = SVG_FONTWEIGHT_700; + } else if (!strcmp(value_string, "800")) { + *value = SVG_FONTWEIGHT_800; + } else if (!strcmp(value_string, "900")) { + *value = SVG_FONTWEIGHT_900; + } +} + +static void svg_parse_fontvariant(SVG_FontVariant *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SVG_FONTVARIANT_INHERIT; + } else if (!strcmp(value_string, "normal")) { + *value = SVG_FONTVARIANT_NORMAL; + } else if (!strcmp(value_string, "small-caps")) { + *value = SVG_FONTVARIANT_SMALLCAPS; + } +} + +static void svg_parse_boolean(SVG_Boolean *value, char *value_string) +{ + /*simple for text editable*/ + if (!strcmp(value_string, "1") || !strcmp(value_string, "true") || !strcmp(value_string, "simple")) + *value = 1; + else + *value = 0; +} + + +static void smil_parse_time_list(GF_Node *e, GF_List *values, char *begin_or_end_list) +{ + SMIL_Time *value; + char value_string[500]; + char *str = begin_or_end_list, *tmp; + u32 len; + + /* get rid of leading spaces */ + while (*str == ' ') str++; + + while (1) { + tmp = strchr(str, ';'); + if (tmp) len = tmp-str; + else len = strlen(str); + memcpy(value_string, str, len); + while (value_string[len - 1] == ' ' && len > 0) len--; + value_string[len] = 0; + + GF_SAFEALLOC(value, SMIL_Time) + gf_list_add(values, value); + + if (smil_parse_time(e, value, value_string) != GF_OK) goto err; + + if (!tmp) break; + + str = tmp + 1; + while (*str == ' ') str++; + } + + /* sorting timing values */ + if (gf_list_count(values) > 1) { + SMIL_Time *v, *sv; + GF_List *sorted = gf_list_new(); + u32 i, count; + u8 added = 0; + do { + v = (SMIL_Time*)gf_list_get(values, 0); + gf_list_rem(values, 0); + added = 0; + count = gf_list_count(sorted); + for (i=0; itype >= GF_SMIL_TIME_EVENT) { + /* unresolved or indefinite so add at the end of the sorted list */ + gf_list_add(sorted, v); + added = 1; + break; + } else { + if (sv->type >= GF_SMIL_TIME_EVENT) { + gf_list_insert(sorted, v, i); + added = 1; + break; + } else { + if (v->clock <= sv->clock) { + gf_list_insert(sorted, v, i); + added = 1; + break; + } + } + } + } + if (!added) gf_list_add(sorted, v); + } while (gf_list_count(values) > 0); + + count = gf_list_count(sorted); + for (i = 0; i < count; i++) { + gf_list_add(values, gf_list_get(sorted, i)); + } + gf_list_del(sorted); + } + return; + +err: + /* See SVG spec: + "If the 'begin' attribute is + syntactically invalid, in the list itself or in any of the individual + list values, it is equivalent to a single 'begin' value of 'indefinite'."*/ + len = gf_list_count(values); + while (len) { + SMIL_Time *v = (SMIL_Time*)gf_list_get(values, 0); + if (v->element_id) free(v->element_id); + gf_list_rem(values, 0); + free(v); + len--; + } + + GF_SAFEALLOC(value, SMIL_Time) + gf_list_add(values, value); + + switch (e->sgprivate->tag) { + case TAG_SVG_discard: + value->type = GF_SMIL_TIME_CLOCK; + value->clock = 0; + break; + default: + value->type = GF_SMIL_TIME_INDEFINITE; + break; + } + return; +} + +static void smil_parse_attributeType(SMIL_AttributeType *value, char *value_string) +{ + if (!strcmp(value_string, "auto")) { + *value = SMIL_ATTRIBUTETYPE_AUTO; + } else if (!strcmp(value_string, "XML")) { + *value = SMIL_ATTRIBUTETYPE_XML; + } else if (!strcmp(value_string, "CSS")) { + *value = SMIL_ATTRIBUTETYPE_CSS; + } +} + +static void smil_parse_min_max_dur_repeatdur(SMIL_Duration *value, char *value_string) +{ + if (!strcmp(value_string, "indefinite")) { + value->type = SMIL_DURATION_INDEFINITE; + } else if (!strcmp(value_string, "media")) { + value->type = SMIL_DURATION_MEDIA; + } else { + Double ftime; + if ((svg_parse_clock_value(value_string, &ftime) == GF_OK) && (ftime >= 0)) { + value->clock_value = ftime; + value->type = SMIL_DURATION_DEFINED; + } else { + /* WARNING: Should this attribute in error be removed ? */ + value->type = SMIL_DURATION_INDEFINITE; + } + } +} + +static void smil_parse_repeatcount(SMIL_RepeatCount *value, char *value_string) +{ + if (!strcmp(value_string, "indefinite")) { + value->type = SMIL_REPEATCOUNT_INDEFINITE; + } else { + Float _val; + sscanf(value_string, "%f", &_val); + value->type = SMIL_REPEATCOUNT_DEFINED; + value->count = FLT2FIX(_val); + } +} + +static void smil_parse_fill(SMIL_Fill *value, char *value_string) +{ + if (!strcmp(value_string, "freeze")) { + *value = SMIL_FILL_FREEZE; + } else if (!strcmp(value_string, "remove")) { + *value = SMIL_FILL_REMOVE; + } +} + +static void smil_parse_restart(SMIL_Restart *value, char *value_string) +{ + if (!strcmp(value_string, "always")) { + *value = SMIL_RESTART_ALWAYS; + } else if (!strcmp(value_string, "whenNotActive")) { + *value = SMIL_RESTART_WHENNOTACTIVE; + } else if (!strcmp(value_string, "never")) { + *value = SMIL_RESTART_NEVER; + } +} + +static void smil_parse_calcmode(SMIL_CalcMode *value, char *value_string) +{ + if (!strcmp(value_string, "discrete")) { + *value = SMIL_CALCMODE_DISCRETE; + } else if (!strcmp(value_string, "linear")) { + *value = SMIL_CALCMODE_LINEAR; + } else if (!strcmp(value_string, "paced")) { + *value = SMIL_CALCMODE_PACED; + } else if (!strcmp(value_string, "spline")) { + *value = SMIL_CALCMODE_SPLINE; + } +} + +static void smil_parse_additive(SMIL_Additive *value, char *value_string) +{ + if (!strcmp(value_string, "replace")) { + *value = SMIL_ADDITIVE_REPLACE; + } else if (!strcmp(value_string, "sum")) { + *value = SMIL_ADDITIVE_SUM; + } +} + +static void smil_parse_accumulate(SMIL_Accumulate *value, char *value_string) +{ + if (!strcmp(value_string, "none")) { + *value = SMIL_ACCUMULATE_NONE; + } else if (!strcmp(value_string, "sum")) { + *value = SMIL_ACCUMULATE_SUM; + } +} + +static void smil_parse_syncBehaviorOrDefault(SMIL_SyncBehavior *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + *value = SMIL_SYNCBEHAVIOR_INHERIT; + } else if (!strcmp(value_string, "default")) { + *value = SMIL_SYNCBEHAVIOR_DEFAULT; + } else if (!strcmp(value_string, "locked")) { + *value = SMIL_SYNCBEHAVIOR_LOCKED; + } else if (!strcmp(value_string, "canSlip")) { + *value = SMIL_SYNCBEHAVIOR_CANSLIP; + } else if (!strcmp(value_string, "independent")) { + *value = SMIL_SYNCBEHAVIOR_INDEPENDENT; + } +} + +static void smil_parse_syncToleranceOrDefault(SMIL_SyncTolerance *value, char *value_string) +{ + if (!strcmp(value_string, "inherit")) { + value->type = SMIL_SYNCTOLERANCE_INHERIT; + } else if (!strcmp(value_string, "default")) { + value->type = SMIL_SYNCTOLERANCE_DEFAULT; + } else { + value->type = SMIL_SYNCBEHAVIOR_LOCKED; + svg_parse_clock_value(value_string, &(value->value)); + } +} + +static void svg_parse_viewbox(SVG_ViewBox *value, char *value_string) +{ + char *str = value_string; + if (!strcmp(str, "none")) { + value->is_set = 0; + } else { + u32 i = 0; + value->is_set = 1; + i+=svg_parse_float(&(str[i]), &(value->x), 0); + i+=svg_parse_float(&(str[i]), &(value->y), 0); + i+=svg_parse_float(&(str[i]), &(value->width), 0); + i+=svg_parse_float(&(str[i]), &(value->height), 0); + } +} + +static void svg_parse_coordinates(GF_List *values, char *value_string) +{ + SVG_Coordinate *c; + u32 i = 0; + char *str = value_string; + u32 len = strlen(str); + + while (gf_list_count(values)) { + c = (SVG_Coordinate*)gf_list_get(values, 0); + gf_list_rem(values, 0); + free(c); + } + while (i < len) { + u32 sub; + GF_SAFEALLOC(c, SVG_Coordinate) + sub = svg_parse_number(c, &(str[i]), 0); + if (!sub) { + free(c); + return; + } + i+=sub; + gf_list_add(values, c); + } +} + +u32 svg_parse_point(SVG_Point *p, char *value_string) +{ + u32 i = 0, j = 0; + i = svg_parse_float(&(value_string[i]), &(p->x), 0); + j = svg_parse_float(&(value_string[i]), &(p->y), 0); + /* we need to detect an odd number of coordinates in polygon points list + cf. http://www.w3.org/TR/SVGMobile12/shapes.html#PolygonElement + see svg_parse_points */ + if (j == 0) return 0; + else return i+j; +} + +static u32 svg_parse_point_into_matrix(GF_Matrix2D *p, char *value_string) +{ + u32 i = 0; + gf_mx2d_init(*p); + i+=svg_parse_float(&(value_string[i]), &(p->m[2]), 0); + i+=svg_parse_float(&(value_string[i]), &(p->m[5]), 0); + return i; +} + +static void svg_parse_points(GF_List *values, char *value_string) +{ + u32 i = 0, j; + char *str = value_string; + u32 len = strlen(str); + while (i < len) { + SVG_Point *p; + GF_SAFEALLOC(p, SVG_Point) + j = svg_parse_point(p, &str[i]); + if (j == 0) { + /* cf. http://www.w3.org/TR/SVGMobile12/shapes.html#PolygonElement + If an odd number of coordinates is provided, then the element + is treated as if the attribute had not been specified.*/ + while (gf_list_count(values)) { + p = (SVG_Point *)gf_list_get(values, 0); + free(p); + gf_list_rem(values, 0); + } + return; + } + i += j; + gf_list_add(values, p); + } +} + +static void svg_parse_floats(GF_List *values, char *value_string, Bool is_angle) +{ + u32 i = 0; + char *str = value_string; + u32 len = strlen(str); + while (i < len) { + Fixed *f; + GF_SAFEALLOC(f, Fixed) + i+=svg_parse_float(&(str[i]), f, is_angle); + if (!i) { + free(f); + return; + } + gf_list_add(values, f); + } +} + +static void svg_string_list_add(GF_List *values, char *string, u32 string_type) +{ + XMLRI *iri; + switch (string_type) { + case 1: + iri = (XMLRI*)malloc(sizeof(XMLRI)); + iri->type = XMLRI_STRING; + iri->string = strdup(string); + gf_list_add(values, iri); + break; + default: + gf_list_add(values, strdup(string)); + break; + } +} + +static void svg_parse_strings(GF_List *values, char *value_string, u32 string_type) +{ + char *next, *sep = value_string; + + while (gf_list_count(values)) { + next = (char*)gf_list_last(values); + gf_list_rem_last(values); + free(next); + } + + while (1) { + while (sep && sep[0]==' ') sep++; + if (!sep) break; + next = sep+1; + while (next[0]) { + if (strchr(" ;,", next[0])) break; + next++; + } + if (!next[0]) { + svg_string_list_add(values, sep, string_type); + break; + } + next[0]=0; + svg_string_list_add(values, sep, string_type); + next[0]=';'; + sep = next+1; + while (strchr(" ,;", sep[0])) sep++; + } +} + +static void svg_parse_strokedasharray(SVG_StrokeDashArray *value, char *value_string) +{ + if (!strcmp(value_string, "none")) { + value->type = SVG_STROKEDASHARRAY_NONE; + } else if (!strcmp(value_string, "inherit")) { + value->type = SVG_STROKEDASHARRAY_INHERIT; + } else { + Array *vals = &(value->array); + GF_List *values = gf_list_new(); + u32 i = 0; + u32 len = strlen(value_string); + char *str = value_string; + while (i < len) { + Fixed *f; + GF_SAFEALLOC(f, Fixed) + i+=svg_parse_float(&(str[i]), f, 0); + gf_list_add(values, f); + } + vals->count = gf_list_count(values); + vals->vals = (Fixed *) malloc(sizeof(Fixed)*vals->count); + for (i = 0; i < vals->count; i++) { + Fixed *f = (Fixed *)gf_list_get(values, i); + vals->vals[i] = *f; + free(f); + } + gf_list_del(values); + value->type = SVG_STROKEDASHARRAY_ARRAY; + } +} + +static void svg_parse_zoomandpan(SVG_ZoomAndPan *value, char *value_string) +{ + if (!strcmp(value_string, "disable")) { + *value = SVG_ZOOMANDPAN_DISABLE; + } else if (!strcmp(value_string, "magnify")) { + *value = SVG_ZOOMANDPAN_MAGNIFY; + } +} + +static void svg_parse_preserveaspectratio(SVG_PreserveAspectRatio *par, char *attribute_content) +{ + char *content = attribute_content; + while (*content == ' ') content++; + if (strstr(content, "defer")) { + par->defer = 1; + content += 4; + } else { + content = attribute_content; + } + while (*content == ' ') content++; + if (strstr(content, "none")) { + par->align = SVG_PRESERVEASPECTRATIO_NONE; + content+=4; + } else if (strstr(content, "xMinYMin")) { + par->align = SVG_PRESERVEASPECTRATIO_XMINYMIN; + content+=8; + } else if (strstr(content, "xMidYMin")) { + par->align = SVG_PRESERVEASPECTRATIO_XMIDYMIN; + content+=8; + } else if (strstr(content, "xMaxYMin")) { + par->align = SVG_PRESERVEASPECTRATIO_XMAXYMIN; + content+=8; + } else if (strstr(content, "xMinYMid")) { + par->align = SVG_PRESERVEASPECTRATIO_XMINYMID; + content+=8; + } else if (strstr(content, "xMidYMid")) { + par->align = SVG_PRESERVEASPECTRATIO_XMIDYMID; + content+=8; + } else if (strstr(content, "xMaxYMid")) { + par->align = SVG_PRESERVEASPECTRATIO_XMAXYMID; + content+=8; + } else if (strstr(content, "xMinYMax")) { + par->align = SVG_PRESERVEASPECTRATIO_XMINYMAX; + content+=8; + } else if (strstr(content, "xMidYMax")) { + par->align = SVG_PRESERVEASPECTRATIO_XMIDYMAX; + content+=8; + } else if (strstr(content, "xMaxYMax")) { + par->align = SVG_PRESERVEASPECTRATIO_XMAXYMAX; + content+=8; + } + while (*content == ' ') content++; + if (*content == 0) return; + if (strstr(content, "meet")) { + par->meetOrSlice = SVG_MEETORSLICE_MEET; + } else if (strstr(content, "slice")) { + par->meetOrSlice = SVG_MEETORSLICE_SLICE; + } +} + +static void svg_parse_animatetransform_type(SVG_TransformType *anim_transform_type, char *attribute_content) +{ + *anim_transform_type = SVG_TRANSFORM_MATRIX; + if (!strcmp(attribute_content, "scale")) { + *anim_transform_type = SVG_TRANSFORM_SCALE; + } else if (!strcmp(attribute_content, "rotate")) { + *anim_transform_type = SVG_TRANSFORM_ROTATE; + } else if (!strcmp(attribute_content, "translate")) { + *anim_transform_type = SVG_TRANSFORM_TRANSLATE; + } else if (!strcmp(attribute_content, "skewX")) { + *anim_transform_type = SVG_TRANSFORM_SKEWX; + } else if (!strcmp(attribute_content, "skewY")) { + *anim_transform_type = SVG_TRANSFORM_SKEWY; + } +} + +static void svg_parse_focushighlight(SVG_FocusHighlight *fh, char *attribute_content) +{ + if (!strcmp(attribute_content, "auto")) { + *fh = SVG_FOCUSHIGHLIGHT_AUTO; + } else if (!strcmp(attribute_content, "none")) { + *fh = SVG_FOCUSHIGHLIGHT_NONE; + } +} + +static void svg_parse_focusable(SVG_Focusable *f, char *attribute_content) +{ + if (!strcmp(attribute_content, "true")) { + *f = SVG_FOCUSABLE_TRUE; + } else if (!strcmp(attribute_content, "false")) { + *f = SVG_FOCUSABLE_FALSE; + } else { + *f = SVG_FOCUSABLE_AUTO; + } +} + +static void svg_parse_initialvisibility(SVG_InitialVisibility *iv, char *attribute_content) +{ + if (!strcmp(attribute_content, "whenStarted")) { + *iv = SVG_INITIALVISIBILTY_WHENSTARTED; + } else if (!strcmp(attribute_content, "always")) { + *iv = SVG_INITIALVISIBILTY_ALWAYS; + } +} + +static void svg_parse_overlay(SVG_Overlay *o, char *attribute_content) +{ + if (!strcmp(attribute_content, "none")) { + *o = SVG_OVERLAY_NONE; + } else if (!strcmp(attribute_content, "top")) { + *o = SVG_OVERLAY_TOP; + } +} + +static void svg_parse_transformbehavior(SVG_TransformBehavior *tb, char *attribute_content) +{ + if (!strcmp(attribute_content, "geometric")) { + *tb = SVG_TRANSFORMBEHAVIOR_GEOMETRIC; + } else if (!strcmp(attribute_content, "pinned")) { + *tb = SVG_TRANSFORMBEHAVIOR_PINNED; + } else if (!strcmp(attribute_content, "pinned90")) { + *tb = SVG_TRANSFORMBEHAVIOR_PINNED90; + } else if (!strcmp(attribute_content, "pinned180")) { + *tb = SVG_TRANSFORMBEHAVIOR_PINNED180; + } else if (!strcmp(attribute_content, "pinned270")) { + *tb = SVG_TRANSFORMBEHAVIOR_PINNED270; + } +} + +static void svg_parse_focus(GF_Node *e, SVG_Focus *o, char *attribute_content) +{ + if (o->target.string) free(o->target.string); + o->target.string = NULL; + o->target.target = NULL; + + if (!strcmp(attribute_content, "self")) o->type = SVG_FOCUS_SELF; + else if (!strcmp(attribute_content, "auto")) o->type = SVG_FOCUS_AUTO; + else if (!strnicmp(attribute_content, "url(", 4)) { + char *sep = strrchr(attribute_content, ')'); + if (sep) sep[0] = 0; + o->type = SVG_FOCUS_IRI; + svg_parse_iri(e, &o->target, attribute_content+4); + if (sep) sep[0] = ')'; + } +} + +/* end of Basic SVG datatype parsing functions */ + +void svg_parse_one_anim_value(GF_Node *n, SMIL_AnimateValue *anim_value, char *attribute_content, u8 anim_value_type) +{ + GF_FieldInfo info; + info.fieldType = anim_value_type; + info.far_ptr = gf_svg_create_attribute_value(anim_value_type); + if (info.far_ptr) gf_svg_parse_attribute(n, &info, attribute_content, 0); + + anim_value->value = info.far_ptr; + anim_value->type = anim_value_type; +} + +void svg_parse_anim_values(GF_Node *n, SMIL_AnimateValues *anim_values, char *anim_values_string, u8 anim_value_type) +{ + u32 i = 0; + char *str; + s32 psemi = -1; + GF_FieldInfo info; + info.fieldType = anim_value_type; + anim_values->type = anim_value_type; + + str = anim_values_string; + while (1) { + if (str[i] == ';' || str[i] == 0) { + u32 single_value_len = 0; + char c; + single_value_len = i - (psemi+1); + c = str [ (psemi+1) + single_value_len]; + str [ (psemi+1) + single_value_len] = 0; + info.far_ptr = gf_svg_create_attribute_value(anim_value_type); + if (info.far_ptr) { + gf_svg_parse_attribute(n, &info, str + (psemi+1), anim_value_type); + gf_list_add(anim_values->values, info.far_ptr); + } + str [ (psemi+1) + single_value_len] = c; + psemi = i; + if (!str[i]) return; + } + i++; + } +} + +GF_Err laser_parse_choice(LASeR_Choice *choice, char *attribute_content) +{ + if (!strcmp(attribute_content, "none")) { + choice->type = LASeR_CHOICE_NONE; + } else if (!strcmp(attribute_content, "all")) { + choice->type = LASeR_CHOICE_ALL; + } else { + choice->type = LASeR_CHOICE_N; + choice->choice_index = atoi(attribute_content); + } + return GF_OK; +} + +GF_Err laser_parse_size(LASeR_Size *size, char *attribute_content) +{ + char *str = attribute_content; + u32 i = 0; + i+=svg_parse_float(&(str[i]), &(size->width), 0); + i+=svg_parse_float(&(str[i]), &(size->height), 0); + return GF_OK; +} + +GF_Err gf_svg_parse_element_id(GF_Node *n, const char *nodename, Bool warning_if_defined) +{ + GF_SceneGraph *sg = gf_node_get_graph((GF_Node *)n); + u32 id = gf_sg_get_max_node_id(sg) + 1; + gf_node_set_id(n, id, nodename); + return GF_OK; +} + +/* Parse an SVG attribute */ +GF_Err gf_svg_parse_attribute(GF_Node *n, GF_FieldInfo *info, char *attribute_content, u8 anim_value_type) +{ + if (info->fieldType != DOM_String_datatype) { + u32 i, len; + /*remove spaces at the begining*/ + while (strchr("\r\n\t ", attribute_content[0])) + attribute_content++; + + /*change all special chars in spaces*/ + i=0; + len = strlen(attribute_content); + while (ifieldType) { + case SVG_Boolean_datatype: + svg_parse_boolean((SVG_Boolean *)info->far_ptr, attribute_content); + break; + case SVG_Color_datatype: + svg_parse_color((SVG_Color *)info->far_ptr, attribute_content); + break; + case SVG_Paint_datatype: + svg_parse_paint(n, (SVG_Paint *)info->far_ptr, attribute_content); + break; + +/* beginning of keyword type parsing */ + case SVG_FillRule_datatype: + svg_parse_clipfillrule((SVG_FillRule *)info->far_ptr, attribute_content); + break; + case SVG_StrokeLineJoin_datatype: + svg_parse_strokelinejoin((SVG_StrokeLineJoin *)info->far_ptr, attribute_content); + break; + case SVG_StrokeLineCap_datatype: + svg_parse_strokelinecap((SVG_StrokeLineCap *)info->far_ptr, attribute_content); + break; + case SVG_FontStyle_datatype: + svg_parse_fontstyle((SVG_FontStyle *)info->far_ptr, attribute_content); + break; + case SVG_FontWeight_datatype: + svg_parse_fontweight((SVG_FontWeight *)info->far_ptr, attribute_content); + break; + case SVG_FontVariant_datatype: + svg_parse_fontvariant((SVG_FontVariant *)info->far_ptr, attribute_content); + break; + case SVG_TextAnchor_datatype: + svg_parse_textanchor((SVG_TextAnchor *)info->far_ptr, attribute_content); + break; + case SVG_Display_datatype: + svg_parse_display((SVG_Display *)info->far_ptr, attribute_content); + break; + case SVG_Visibility_datatype: + svg_parse_visibility((SVG_Visibility *)info->far_ptr, attribute_content); + break; + case SVG_Overflow_datatype: + svg_parse_overflow((SVG_Overflow *)info->far_ptr, attribute_content); + break; + case SVG_ZoomAndPan_datatype: + svg_parse_zoomandpan((SVG_ZoomAndPan *)info->far_ptr, attribute_content); + break; + case SVG_DisplayAlign_datatype: + svg_parse_displayalign((SVG_DisplayAlign *)info->far_ptr, attribute_content); + break; + case SVG_TextAlign_datatype: + svg_parse_textalign((SVG_TextAlign *)info->far_ptr, attribute_content); + break; + case SVG_PointerEvents_datatype: + svg_parse_pointerevents((SVG_PointerEvents *)info->far_ptr, attribute_content); + break; + case SVG_RenderingHint_datatype: + svg_parse_renderinghint((SVG_RenderingHint *)info->far_ptr, attribute_content); + break; + case SVG_VectorEffect_datatype: + svg_parse_vectoreffect((SVG_VectorEffect *)info->far_ptr, attribute_content); + break; + case SVG_PlaybackOrder_datatype: + svg_parse_playbackorder((SVG_PlaybackOrder *)info->far_ptr, attribute_content); + break; + case SVG_TimelineBegin_datatype: + svg_parse_timelinebegin((SVG_TimelineBegin *)info->far_ptr, attribute_content); + break; + case XML_Space_datatype: + svg_parse_xmlspace((XML_Space *)info->far_ptr, attribute_content); + break; + case XMLEV_Propagate_datatype: + svg_parse_xmlev_propagate((XMLEV_Propagate *)info->far_ptr, attribute_content); + break; + case XMLEV_DefaultAction_datatype: + svg_parse_xmlev_defaultAction((XMLEV_DefaultAction *)info->far_ptr, attribute_content); + break; + case XMLEV_Phase_datatype: + svg_parse_xmlev_phase((XMLEV_Phase *)info->far_ptr, attribute_content); + break; + case SMIL_SyncBehavior_datatype: + smil_parse_syncBehaviorOrDefault((SMIL_SyncBehavior *)info->far_ptr, attribute_content); + break; + case SMIL_SyncTolerance_datatype: + smil_parse_syncToleranceOrDefault((SMIL_SyncTolerance *)info->far_ptr, attribute_content); + break; + case SMIL_AttributeType_datatype: + smil_parse_attributeType((SMIL_AttributeType *)info->far_ptr, attribute_content); + break; + case SMIL_CalcMode_datatype: + smil_parse_calcmode((SMIL_CalcMode *)info->far_ptr, attribute_content); + break; + case SMIL_Additive_datatype: + smil_parse_additive((SMIL_CalcMode *)info->far_ptr, attribute_content); + break; + case SMIL_Accumulate_datatype: + smil_parse_accumulate((SMIL_Accumulate *)info->far_ptr, attribute_content); + break; + case SMIL_Restart_datatype: + smil_parse_restart((SMIL_Restart *)info->far_ptr, attribute_content); + break; + case SMIL_Fill_datatype: + smil_parse_fill((SMIL_Fill *)info->far_ptr, attribute_content); + break; + case SVG_GradientUnit_datatype: + *((SVG_GradientUnit *)info->far_ptr) = !strcmp(attribute_content, "userSpaceOnUse") ? SVG_GRADIENTUNITS_USER : SVG_GRADIENTUNITS_OBJECT; + break; + case SVG_FocusHighlight_datatype: + svg_parse_focushighlight((SVG_FocusHighlight*)info->far_ptr, attribute_content); + break; + case SVG_Focusable_datatype: + svg_parse_focusable((SVG_Focusable*)info->far_ptr, attribute_content); + break; + + case SVG_InitialVisibility_datatype: + svg_parse_initialvisibility((SVG_InitialVisibility*)info->far_ptr, attribute_content); + break; + case SVG_Overlay_datatype: + svg_parse_overlay((SVG_Overlay*)info->far_ptr, attribute_content); + break; + case SVG_TransformBehavior_datatype: + svg_parse_transformbehavior((SVG_TransformBehavior*)info->far_ptr, attribute_content); + break; + case SVG_SpreadMethod_datatype: + if (!strcmp(attribute_content, "reflect")) *(u8*)info->far_ptr = SVG_SPREAD_REFLECT; + else if (!strcmp(attribute_content, "repeat")) *(u8*)info->far_ptr = SVG_SPREAD_REPEAT; + else *(u8*)info->far_ptr = SVG_SPREAD_PAD; + break; +/* end of keyword type parsing */ + + /* keyword | floats */ + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_FontSize_datatype: + case SVG_Rotate_datatype: + case SVG_Number_datatype: + svg_parse_number((SVG_Number*)info->far_ptr, attribute_content, 0); + break; + + case SMIL_AnimateValue_datatype: + svg_parse_one_anim_value(n, (SMIL_AnimateValue*)info->far_ptr, attribute_content, anim_value_type); + break; + case SMIL_AnimateValues_datatype: + svg_parse_anim_values(n, (SMIL_AnimateValues*)info->far_ptr, attribute_content, anim_value_type); + break; + + case XMLRI_datatype: + svg_parse_iri(n, (XMLRI*)info->far_ptr, attribute_content); + break; + case XML_IDREF_datatype: + svg_parse_idref(n, (XMLRI*)info->far_ptr, attribute_content); + break; + case SMIL_AttributeName_datatype: + ((SMIL_AttributeName *)info->far_ptr)->name = strdup(attribute_content); + break; + case SMIL_Times_datatype: + smil_parse_time_list(n, *(GF_List **)info->far_ptr, attribute_content); + break; + case SMIL_Duration_datatype: + smil_parse_min_max_dur_repeatdur((SMIL_Duration*)info->far_ptr, attribute_content); + break; + case SMIL_RepeatCount_datatype: + smil_parse_repeatcount((SMIL_RepeatCount*)info->far_ptr, attribute_content); + break; + case SVG_PathData_datatype: + svg_parse_path((SVG_PathData*)info->far_ptr, attribute_content); + break; + case SVG_Points_datatype: + svg_parse_points(*(GF_List **)(info->far_ptr), attribute_content); + break; + case SMIL_KeyTimes_datatype: + case SMIL_KeyPoints_datatype: + case SMIL_KeySplines_datatype: + svg_parse_floats(*(GF_List **)(info->far_ptr), attribute_content, 0); + break; + case SVG_Numbers_datatype: + case SVG_Coordinates_datatype: + svg_parse_coordinates(*(GF_List **)(info->far_ptr), attribute_content); + break; + case SVG_ViewBox_datatype: + svg_parse_viewbox((SVG_ViewBox*)info->far_ptr, attribute_content); + break; + case SVG_StrokeDashArray_datatype: + svg_parse_strokedasharray((SVG_StrokeDashArray*)info->far_ptr, attribute_content); + break; + case SVG_FontFamily_datatype: + svg_parse_fontfamily((SVG_FontFamily*)info->far_ptr, attribute_content); + break; + case SVG_Motion_datatype: + svg_parse_point_into_matrix((GF_Matrix2D*)info->far_ptr, attribute_content); + break; + case SVG_Transform_datatype: + svg_parse_transform((SVG_Transform*)info->far_ptr, attribute_content); + break; + case SVG_Transform_Translate_datatype: + { + u32 i = 0; + SVG_Point *p = (SVG_Point *)info->far_ptr;; + i+=svg_parse_float(&(attribute_content[i]), &(p->x), 0); + if (attribute_content[i] == 0) { + p->y = 0; + } else { + i+=svg_parse_float(&(attribute_content[i]), &(p->y), 0); + } + } + break; + case SVG_Transform_Scale_datatype: + { + u32 i = 0; + SVG_Point *p = (SVG_Point *)info->far_ptr;; + i+=svg_parse_float(&(attribute_content[i]), &(p->x), 0); + if (attribute_content[i] == 0) { + p->y = p->x; + } else { + i+=svg_parse_float(&(attribute_content[i]), &(p->y), 0); + } + } + break; + case SVG_Transform_SkewX_datatype: + case SVG_Transform_SkewY_datatype: + { + Fixed *p = (Fixed *)info->far_ptr; + svg_parse_float(attribute_content, p, 1); + } + break; + case SVG_Transform_Rotate_datatype: + { + u32 i = 0; + SVG_Point_Angle *p = (SVG_Point_Angle *)info->far_ptr;; + i+=svg_parse_float(&(attribute_content[i]), &(p->angle), 1); + if (attribute_content[i] == 0) { + p->y = p->x = 0; + } else { + i+=svg_parse_float(&(attribute_content[i]), &(p->x), 0); + i+=svg_parse_float(&(attribute_content[i]), &(p->y), 0); + } + } + break; + case SVG_PreserveAspectRatio_datatype: + svg_parse_preserveaspectratio((SVG_PreserveAspectRatio*)info->far_ptr, attribute_content); + break; + case SVG_TransformType_datatype: + svg_parse_animatetransform_type((SVG_TransformType*)info->far_ptr, attribute_content); + break; + + case SVG_ID_datatype: + case DOM_String_datatype: + case SVG_ContentType_datatype: + case SVG_LanguageID_datatype: + if (*(SVG_String *)info->far_ptr) free(*(SVG_String *)info->far_ptr); + + *(SVG_String *)info->far_ptr = strdup(attribute_content); + break; + + case DOM_StringList_datatype: + svg_parse_strings(*(GF_List **)info->far_ptr, attribute_content, 0); + break; + case XMLRI_List_datatype: + svg_parse_strings(*(GF_List **)info->far_ptr, attribute_content, 1); + break; + + case XMLEV_Event_datatype: + { + XMLEV_Event *xml_ev = (XMLEV_Event *)info->far_ptr; + char *sep = strchr(attribute_content, '('); + if (sep) { + sep[0] = 0; + xml_ev->type = gf_dom_event_type_by_name(attribute_content); + sep[0] = '('; + if ((xml_ev->type == GF_EVENT_REPEAT) || (xml_ev->type == GF_EVENT_REPEAT_EVENT)) { + char _v; + sscanf(sep, "(%c)", &_v); + xml_ev->parameter = _v; + } else { /* key events ... */ + char *sep2 = strchr(attribute_content, ')'); + sep2[0] = 0; + xml_ev->parameter = gf_dom_get_key_type(sep+1); + sep2[0] = ')'; + } + } else { + xml_ev->parameter = 0; + xml_ev->type = gf_dom_event_type_by_name(attribute_content); + } + } + break; + + case SVG_Focus_datatype: + svg_parse_focus(n, (SVG_Focus*)info->far_ptr, attribute_content); + break; + case LASeR_Choice_datatype: + laser_parse_choice((LASeR_Choice*)info->far_ptr, attribute_content); + break; + case LASeR_Size_datatype: + laser_parse_size((LASeR_Size*)info->far_ptr, attribute_content); + break; + case SVG_Clock_datatype: + svg_parse_clock_value(attribute_content, (SVG_Clock*)info->far_ptr); + break; + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Parsing] Cannot parse attribute %s\n", info->name, gf_svg_attribute_type_to_string(info->fieldType))); + break; + } + return GF_OK; +} + +void svg_parse_one_style(GF_Node *n, char *one_style) +{ + GF_FieldInfo info; + char *c, sep; + u32 attributeNameLen; + + while (*one_style == ' ') one_style++; + c = strchr(one_style, ':'); + if (!c) return; + attributeNameLen = (c - one_style); + sep = one_style[attributeNameLen]; + one_style[attributeNameLen] = 0; + while (strchr("\r\n\t ", one_style[0])) + one_style++; + if (!gf_node_get_field_by_name(n, one_style, &info)) { + c++; + gf_svg_parse_attribute(n, &info, c, 0); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Attribute %s does not belong to element %s.\n", one_style, gf_node_get_class_name(n))); + } + one_style[attributeNameLen] = sep; +} + +void gf_svg_parse_style(GF_Node *n, char *style) +{ + u32 i = 0; + char *str = style; + s32 psemi = -1; + + while (1) { + if (str[i] == ';' || str[i] == 0) { + u32 single_value_len = 0; + single_value_len = i - (psemi+1); + if (single_value_len) { + char c = str[psemi+1 + single_value_len]; + str[psemi+1 + single_value_len] = 0; + svg_parse_one_style(n, str + psemi+1); + str[psemi+1 + single_value_len] = c; + psemi = i; + } + if (!str[i]) return; + } + i++; + } + +} + +void *gf_svg_create_attribute_value(u32 attribute_type) +{ + switch (attribute_type) { + case SVG_Boolean_datatype: + { + SVG_Boolean *b; + GF_SAFEALLOC(b, SVG_Boolean) + return b; + } + break; + case SVG_Color_datatype: + { + SVG_Color *color; + GF_SAFEALLOC(color, SVG_Color) + return color; + } + break; + case SVG_Paint_datatype: + { + SVG_Paint *paint; + GF_SAFEALLOC(paint, SVG_Paint) + return paint; + } + break; + + /* keyword types */ + case SVG_FillRule_datatype: + case SVG_StrokeLineJoin_datatype: + case SVG_StrokeLineCap_datatype: + case SVG_FontStyle_datatype: + case SVG_FontWeight_datatype: + case SVG_FontVariant_datatype: + case SVG_TextAnchor_datatype: + case SVG_Display_datatype: + case SVG_Visibility_datatype: + case SVG_Overflow_datatype: + case SVG_ZoomAndPan_datatype: + case SVG_DisplayAlign_datatype: + case SVG_TextAlign_datatype: + case SVG_PointerEvents_datatype: + case SVG_RenderingHint_datatype: + case SVG_VectorEffect_datatype: + case SVG_PlaybackOrder_datatype: + case SVG_TimelineBegin_datatype: + case XML_Space_datatype: + case XMLEV_Propagate_datatype: + case XMLEV_DefaultAction_datatype: + case XMLEV_Phase_datatype: + case SMIL_SyncBehavior_datatype: + case SMIL_AttributeType_datatype: + case SMIL_CalcMode_datatype: + case SMIL_Additive_datatype: + case SMIL_Accumulate_datatype: + case SMIL_Restart_datatype: + case SMIL_Fill_datatype: + case SVG_TransformType_datatype: + case SVG_FocusHighlight_datatype: + case SVG_InitialVisibility_datatype: + case SVG_GradientUnit_datatype: + case SVG_Overlay_datatype: + case SVG_TransformBehavior_datatype: + case SVG_SpreadMethod_datatype: + case SVG_Focusable_datatype: + { + u8 *keyword; + GF_SAFEALLOC(keyword, u8) + return keyword; + } + break; + case SMIL_SyncTolerance_datatype: + { + SMIL_SyncTolerance *st; + GF_SAFEALLOC(st, SMIL_SyncTolerance) + return st; + } + break; + + /* inheritable floats */ + case SVG_FontSize_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_Rotate_datatype: + case SVG_Number_datatype: + { + SVG_Number *number; + GF_SAFEALLOC(number, SVG_Number) + return number; + } + break; + + case SVG_StrokeDashArray_datatype: + { + SVG_StrokeDashArray *array; + GF_SAFEALLOC(array, SVG_StrokeDashArray) + return array; + } + break; + + case SVG_Motion_datatype: + { + GF_Matrix2D *p; + GF_SAFEALLOC(p, GF_Matrix2D) + gf_mx2d_init(*p); + return p; + } + break; + + case SVG_Transform_datatype: + { + SVG_Transform *p; + GF_SAFEALLOC(p, SVG_Transform) + gf_mx2d_init(p->mat); + return p; + } + break; + + case SVG_Transform_Translate_datatype: + case SVG_Transform_Scale_datatype: + { + SVG_Point *p; + GF_SAFEALLOC(p, SVG_Point) + return p; + } + break; + + case SVG_Transform_SkewX_datatype: + case SVG_Transform_SkewY_datatype: + { + Fixed *p; + GF_SAFEALLOC(p, Fixed) + return p; + } + break; + + case SVG_Transform_Rotate_datatype: + { + SVG_Point_Angle *p; + GF_SAFEALLOC(p, SVG_Point_Angle) + return p; + } + break; + + case SVG_ViewBox_datatype: + { + SVG_ViewBox *viewbox; + GF_SAFEALLOC(viewbox, SVG_ViewBox) + return viewbox; + } + break; + case XMLRI_datatype: + case XML_IDREF_datatype: + { + XMLRI *iri; + GF_SAFEALLOC(iri, XMLRI) + return iri; + } + break; + case SVG_FontFamily_datatype: + { + SVG_FontFamily *fontfamily; + GF_SAFEALLOC(fontfamily, SVG_FontFamily) + return fontfamily; + } + break; + case DOM_String_datatype: + case SVG_ContentType_datatype: + case SVG_LanguageID_datatype: + case SVG_ID_datatype: + { + SVG_String *string; + GF_SAFEALLOC(string, SVG_String) + return string; + } + break; + case DOM_StringList_datatype: + case XMLRI_List_datatype: + case SVG_Points_datatype: + case SVG_Coordinates_datatype: + case SMIL_Times_datatype: + case SMIL_KeySplines_datatype: + case SMIL_KeyTimes_datatype: + case SMIL_KeyPoints_datatype: + case SVG_Numbers_datatype: + { + ListOfXXX *list; + GF_SAFEALLOC(list, ListOfXXX) + *list = gf_list_new(); + return list; + } + break; + case SVG_PreserveAspectRatio_datatype: + { + SVG_PreserveAspectRatio *par; + GF_SAFEALLOC(par, SVG_PreserveAspectRatio) + return par; + } + break; + case SVG_PathData_datatype: + { + SVG_PathData *path; + GF_SAFEALLOC(path, SVG_PathData); +#if USE_GF_PATH + gf_path_reset(path); + path->fineness = FIX_ONE; +#else + path->commands = gf_list_new(); + path->points = gf_list_new(); +#endif + return path; + } + break; + case LASeR_Choice_datatype: + { + LASeR_Choice *ch; + GF_SAFEALLOC(ch, LASeR_Choice) + return ch; + } + case SVG_Focus_datatype: + { + SVG_Focus *foc; + GF_SAFEALLOC(foc, SVG_Focus) + return foc; + } + case SMIL_AttributeName_datatype: + { + SMIL_AttributeName *an; + GF_SAFEALLOC(an, SMIL_AttributeName) + return an; + } + case SMIL_RepeatCount_datatype: + { + SMIL_RepeatCount *rc; + GF_SAFEALLOC(rc, SMIL_RepeatCount) + return rc; + } + case SMIL_Duration_datatype: + { + SMIL_Duration *sd; + GF_SAFEALLOC(sd, SMIL_Duration) + return sd; + } + case SMIL_AnimateValue_datatype: + { + SMIL_AnimateValue *av; + GF_SAFEALLOC(av, SMIL_AnimateValue) + return av; + } + break; + case SMIL_AnimateValues_datatype: + { + SMIL_AnimateValues *av; + GF_SAFEALLOC(av, SMIL_AnimateValues) + av->values = gf_list_new(); + return av; + } + break; + case SVG_Clock_datatype: + { + SVG_Clock *ck; + GF_SAFEALLOC(ck, SVG_Clock) + return ck; + } + break; + + case XMLEV_Event_datatype: + { + XMLEV_Event *e; + GF_SAFEALLOC(e, XMLEV_Event); + return e; + } + break; + case LASeR_Size_datatype: + { + LASeR_Size *s; + GF_SAFEALLOC(s, LASeR_Size); + return s; + } + break; + + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Attributes] Cannot create attribute value - Type %s not supported.\n", gf_svg_attribute_type_to_string(attribute_type))); + break; + } + return NULL; +} + +static void svg_dump_color(SVG_Color *col, char *attValue) +{ + if (col->type == SVG_COLOR_CURRENTCOLOR) strcpy(attValue, "currentColor"); + else if (col->type == SVG_COLOR_INHERIT) strcpy(attValue, "inherit"); + else if (col->type !=SVG_COLOR_RGBCOLOR) { + u32 i, count; + count = sizeof(system_colors) / sizeof(struct sys_col); + for (i=0; itype == system_colors[i].type) { + strcpy(attValue, system_colors[i].name); + return; + } + } + } else { + u32 i, count = sizeof(predefined_colors) / sizeof(struct predef_col); + u32 r, g, b; + r = FIX2INT(255*col->red); + g = FIX2INT(255*col->green); + b = FIX2INT(255*col->blue); + for (i=0; itype==SVG_NUMBER_INHERIT) strcpy(attValue, "inherit"); + else if (l->type == SVG_NUMBER_AUTO) strcpy(attValue, "auto"); + else if (l->type == SVG_NUMBER_AUTO_REVERSE) strcpy(attValue, "auto-reverse"); + else { + sprintf(attValue, "%g", FIX2FLT(l->value) ); + if (l->type == SVG_NUMBER_PERCENTAGE) strcat(attValue, "%"); + else if (l->type == SVG_NUMBER_EMS) strcat(attValue, "em"); + else if (l->type == SVG_NUMBER_EXS) strcat(attValue, "ex"); + else if (l->type == SVG_NUMBER_PX) strcat(attValue, "px"); + else if (l->type == SVG_NUMBER_CM) strcat(attValue, "cm"); + else if (l->type == SVG_NUMBER_MM) strcat(attValue, "mm"); + else if (l->type == SVG_NUMBER_IN) strcat(attValue, "in"); + else if (l->type == SVG_NUMBER_PT) strcat(attValue, "pt"); + else if (l->type == SVG_NUMBER_PC) strcat(attValue, "pc"); + } +} + +static void svg_dump_iri(XMLRI*iri, char *attValue) +{ + if (iri->type == XMLRI_ELEMENTID) { + const char *name; + name = gf_node_get_name((GF_Node *)iri->target); + if (name) sprintf(attValue, "#%s", gf_node_get_name((GF_Node *)iri->target)); + else sprintf(attValue, "#N%d", gf_node_get_id((GF_Node *)iri->target) - 1); + } + else if ((iri->type == XMLRI_STRING) && iri->string) strcpy(attValue, iri->string); + else strcpy(attValue, ""); +} + +static void svg_dump_idref(XMLRI*iri, char *attValue) +{ + const char *name; + if (iri->target) { + name = gf_node_get_name((GF_Node *)iri->target); + if (name) sprintf(attValue, "%s", gf_node_get_name((GF_Node *)iri->target)); + else sprintf(attValue, "N%d", gf_node_get_id((GF_Node *)iri->target) - 1); + return; + } + if (iri->string) strcpy(attValue, iri->string); +} + +#if USE_GF_PATH +static void svg_dump_path(SVG_PathData *path, char *attValue) +{ + char szT[1000]; + GF_Point2D *pt, last_pt, *ct1, *ct2, *end; + u32 i, *contour; + strcpy(attValue, ""); + + contour = path->contours; + last_pt.x = last_pt.y = 0; + + for (i=0; in_points; ) { + switch (path->tags[i]) { + case GF_PATH_CURVE_ON: + case GF_PATH_CLOSE: + pt = &path->points[i]; + if (!i || (*contour == i-1) ) { + sprintf(szT, "M%g %g", FIX2FLT(pt->x), FIX2FLT(pt->y)); + if (i) contour++; + } else if (path->tags[i]==GF_PATH_CLOSE) { + sprintf(szT, "z"); + } else { + if (i && (last_pt.x==pt->x)) sprintf(szT, "V%g", FIX2FLT(pt->y)); + else if (i && (last_pt.y==pt->y)) sprintf(szT, "H%g", FIX2FLT(pt->x)); + else sprintf(szT, "L%g %g", FIX2FLT(pt->x), FIX2FLT(pt->y)); + } + strcat(attValue, szT); + last_pt = *pt; + i++; + break; + case GF_PATH_CURVE_CONIC: + ct1 = &path->points[i]; + end = &path->points[i+1]; + sprintf(szT, "Q%g %g %g %g", FIX2FLT(ct1->x), FIX2FLT(ct1->y), FIX2FLT(end->x), FIX2FLT(end->y)); + strcat(attValue, szT); + last_pt = *end; + if (path->tags[i+2]==GF_PATH_CLOSE) { + strcat(attValue, "z"); + } + i+=2; + break; + case GF_PATH_CURVE_CUBIC: + ct1 = &path->points[i]; + ct2 = &path->points[i+1]; + end = &path->points[i+2]; + sprintf(szT, "C%g %g %g %g %g %g", FIX2FLT(ct1->x), FIX2FLT(ct1->y), FIX2FLT(ct2->x), FIX2FLT(ct2->y), FIX2FLT(end->x), FIX2FLT(end->y)); + strcat(attValue, szT); + last_pt = *end; + if (path->tags[i+2]==GF_PATH_CLOSE) { + strcat(attValue, "z"); + } + i+=3; + break; + } + } + +} +#else +static void svg_dump_point(SVG_Point *pt, char *attValue) +{ + if (pt) sprintf(attValue, "%g %g ", FIX2FLT(pt->x), FIX2FLT(pt->y) ); +} +static void svg_dump_path(SVG_PathData *path, char *attValue) +{ + char szT[1000]; + u32 i, pt_i, count; + count = gf_list_count(path->commands); + pt_i = 0; + strcpy(attValue, ""); + for (i = 0; i < count; i++) { + u8 command = *(u8 *)gf_list_get(path->commands, i); + switch(command) { + case SVG_PATHCOMMAND_M: + strcat(attValue, "M"); + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + break; + case SVG_PATHCOMMAND_L: + strcat(attValue, "L"); + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + break; + case SVG_PATHCOMMAND_C: + strcat(attValue, "C"); + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + break; + case SVG_PATHCOMMAND_S: + strcat(attValue, "S"); + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + break; + case SVG_PATHCOMMAND_Q: + strcat(attValue, "Q"); + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + break; + case SVG_PATHCOMMAND_T: + strcat(attValue, "T"); + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + break; + case SVG_PATHCOMMAND_A: + strcat(attValue, "A"); + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + strcat(attValue, "0 0 0 "); + svg_dump_point((SVG_Point*)gf_list_get(path->points, pt_i), szT); + strcat(attValue, szT); + pt_i++; + break; + case SVG_PATHCOMMAND_Z: + strcat(attValue, "Z"); + break; + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Dumping] unknown path command %d\n", command)); + break; + } + } +} +#endif + +static void svg_dump_access_key(XMLEV_Event *evt, char *attValue) +{ + u32 i, count; + strcpy(attValue, "accessKey("); + count = sizeof(predefined_key_identifiers) / sizeof(struct predef_keyid); + for (i=0; iparameter == predefined_key_identifiers[i].key_code) { + strcat(attValue, predefined_key_identifiers[i].name); + break; + } + } + /* OLD LASeR CODE + switch (evt->parameter) { + case 0: strcat(attValue, "UP"); break; + case 1: strcat(attValue, "DOWN"); break; + case 2: strcat(attValue, "LEFT"); break; + case 3: strcat(attValue, "RIGHT"); break; + case 4: strcat(attValue, "FIRE"); break; + case 5: strcat(attValue, "NO_KEY"); break; + case 6: strcat(attValue, "ANY_KEY"); break; + case 7: strcat(attValue, "SOFT_KEY_1"); break; + case 8: strcat(attValue, "SOFT_KEY_2"); break; + case 35: strcat(attValue, "#"); break; + case 42: strcat(attValue, "*"); break; + case 48: strcat(attValue, "0"); break; + case 49: strcat(attValue, "1"); break; + case 50: strcat(attValue, "2"); break; + case 51: strcat(attValue, "3"); break; + case 52: strcat(attValue, "4"); break; + case 53: strcat(attValue, "5"); break; + case 54: strcat(attValue, "6"); break; + case 55: strcat(attValue, "7"); break; + case 56: strcat(attValue, "8"); break; + case 57: strcat(attValue, "9"); break; + */ + strcat(attValue, ")"); +} + +static void gf_svg_dump_matrix(GF_Matrix2D *matrix, char *attValue) +{ + /*try to do a simple decomposition...*/ + if (!matrix->m[1] && !matrix->m[3]) { + if (matrix->m[2] != 0 || matrix->m[5] != 0) sprintf(attValue, "translate(%g,%g)", FIX2FLT(matrix->m[2]), FIX2FLT(matrix->m[5]) ); + if ((matrix->m[0]!=FIX_ONE) || (matrix->m[4]!=FIX_ONE)) { + char szT[1024]; + if ((matrix->m[0]==-FIX_ONE) && (matrix->m[4]==-FIX_ONE)) { + strcpy(szT, " rotate(180)"); + } else { + sprintf(szT, " scale(%g,%g)", FIX2FLT(matrix->m[0]), FIX2FLT(matrix->m[4]) ); + } + strcat(attValue, szT); + } + } else if (matrix->m[1] == - matrix->m[3]) { + Fixed angle = gf_asin(matrix->m[3]); + Fixed cos_a = gf_cos(angle); + if (ABS(cos_a)>FIX_EPSILON) { + Fixed sx, sy; + sx = gf_divfix(matrix->m[0], cos_a); + sy = gf_divfix(matrix->m[4], cos_a); + angle = gf_divfix(180*angle, GF_PI); + if ((sx==sy) && ( ABS(FIX_ONE - ABS(sx) ) < FIX_ONE/100)) { + if (matrix->m[2] != 0 || matrix->m[5] != 0) + sprintf(attValue, "translate(%g,%g) rotate(%g)", FIX2FLT(matrix->m[2]), FIX2FLT(matrix->m[5]), FIX2FLT(gf_divfix(angle*180, GF_PI) ) ); + else + sprintf(attValue, "rotate(%g)", FIX2FLT(gf_divfix(angle*180, GF_PI) ) ); + } else { + if (matrix->m[2] != 0 || matrix->m[5] != 0) + sprintf(attValue, "translate(%g,%g) scale(%g,%g) rotate(%g)", FIX2FLT(matrix->m[2]), FIX2FLT(matrix->m[5]), FIX2FLT(sx), FIX2FLT(sy), FIX2FLT(gf_divfix(angle*180, GF_PI) ) ); + else + sprintf(attValue, "scale(%g,%g) rotate(%g)", FIX2FLT(sx), FIX2FLT(sy), FIX2FLT(gf_divfix(angle*180, GF_PI) ) ); + } + } else { + Fixed a = angle; + if (a<0) a += GF_2PI; + if (matrix->m[2] != 0 || matrix->m[5] != 0) + sprintf(attValue, "translate(%g,%g) rotate(%g)", FIX2FLT(matrix->m[2]), FIX2FLT(matrix->m[5]), FIX2FLT(gf_divfix(a*180, GF_PI) ) ); + else + sprintf(attValue, "rotate(%g)", FIX2FLT(gf_divfix(a*180, GF_PI) ) ); + } + } + /*default*/ + if (!strlen(attValue)) + sprintf(attValue, "matrix(%g %g %g %g %g %g)", FIX2FLT(matrix->m[0]), FIX2FLT(matrix->m[3]), FIX2FLT(matrix->m[1]), FIX2FLT(matrix->m[4]), FIX2FLT(matrix->m[2]), FIX2FLT(matrix->m[5]) ); +} + +GF_Err gf_svg_dump_attribute(GF_Node *elt, GF_FieldInfo *info, char *attValue) +{ + u8 intVal; + strcpy(attValue, ""); + if (!info->far_ptr) return GF_OK; + intVal = *(u8 *)info->far_ptr; + + switch (info->fieldType) { + case SVG_Boolean_datatype: + sprintf(attValue, "%s", *(SVG_Boolean *)info->far_ptr ? "true" : "false"); + break; + case SVG_Color_datatype: + { + SVG_Color *col = (SVG_Color *)info->far_ptr; + svg_dump_color(col, attValue); + } + break; + case SVG_Paint_datatype: + { + SVG_Paint *paint = (SVG_Paint *)info->far_ptr; + if (paint->type == SVG_PAINT_NONE) strcpy(attValue, "none"); + else if (paint->type == SVG_PAINT_INHERIT) strcpy(attValue, "inherit"); + else if (paint->type == SVG_PAINT_URI) { + char tmp[1024]; + svg_dump_iri(&paint->iri, tmp); + sprintf(attValue, "url(%s)", tmp); + } else svg_dump_color(&paint->color, attValue); + } + break; + +/* beginning of keyword type parsing */ + case SVG_FillRule_datatype: + if (intVal == SVG_FILLRULE_INHERIT) strcpy(attValue, "inherit"); + else if (intVal == SVG_FILLRULE_NONZERO) strcpy(attValue, "nonzero"); + else strcpy(attValue, "evenodd"); + break; + + case SVG_StrokeLineJoin_datatype: + if (intVal==SVG_STROKELINEJOIN_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_STROKELINEJOIN_MITER) strcpy(attValue, "miter"); + else if (intVal==SVG_STROKELINEJOIN_ROUND) strcpy(attValue, "round"); + else if (intVal==SVG_STROKELINEJOIN_BEVEL) strcpy(attValue, "bevel"); + break; + case SVG_StrokeLineCap_datatype: + if (intVal==SVG_STROKELINECAP_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_STROKELINECAP_BUTT) strcpy(attValue, "butt"); + else if (intVal==SVG_STROKELINECAP_ROUND) strcpy(attValue, "round"); + else if (intVal==SVG_STROKELINECAP_SQUARE) strcpy(attValue, "square"); + break; + case SVG_FontStyle_datatype: + if (intVal==SVG_FONTSTYLE_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_FONTSTYLE_NORMAL) strcpy(attValue, "normal"); + else if (intVal==SVG_FONTSTYLE_ITALIC) strcpy(attValue, "italic"); + else if (intVal==SVG_FONTSTYLE_OBLIQUE) strcpy(attValue, "oblique"); + break; + case SVG_FontWeight_datatype: + if (intVal==SVG_FONTWEIGHT_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_FONTWEIGHT_NORMAL) strcpy(attValue, "normal"); + else if (intVal==SVG_FONTWEIGHT_BOLD) strcpy(attValue, "bold"); + else if (intVal==SVG_FONTWEIGHT_BOLDER) strcpy(attValue, "bolder"); + else if (intVal==SVG_FONTWEIGHT_LIGHTER) strcpy(attValue, "lighter"); + else if (intVal==SVG_FONTWEIGHT_100) strcpy(attValue, "100"); + else if (intVal==SVG_FONTWEIGHT_200) strcpy(attValue, "200"); + else if (intVal==SVG_FONTWEIGHT_300) strcpy(attValue, "300"); + else if (intVal==SVG_FONTWEIGHT_400) strcpy(attValue, "400"); + else if (intVal==SVG_FONTWEIGHT_500) strcpy(attValue, "500"); + else if (intVal==SVG_FONTWEIGHT_600) strcpy(attValue, "600"); + else if (intVal==SVG_FONTWEIGHT_700) strcpy(attValue, "700"); + else if (intVal==SVG_FONTWEIGHT_800) strcpy(attValue, "800"); + else if (intVal==SVG_FONTWEIGHT_900) strcpy(attValue, "900"); + break; + case SVG_FontVariant_datatype: + if (intVal==SVG_FONTVARIANT_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_FONTVARIANT_NORMAL) strcpy(attValue, "normal"); + else if (intVal==SVG_FONTVARIANT_SMALLCAPS) strcpy(attValue, "small-caps"); + break; + case SVG_TextAnchor_datatype: + if (intVal==SVG_TEXTANCHOR_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_TEXTANCHOR_START) strcpy(attValue, "start"); + else if (intVal==SVG_TEXTANCHOR_MIDDLE) strcpy(attValue, "middle"); + else if (intVal==SVG_TEXTANCHOR_END) strcpy(attValue, "end"); + break; + case SVG_Display_datatype: + if (intVal==SVG_DISPLAY_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_DISPLAY_NONE) strcpy(attValue, "none"); + else if (intVal==SVG_DISPLAY_INLINE) strcpy(attValue, "inline"); + else if (intVal==SVG_DISPLAY_BLOCK) strcpy(attValue, "block"); + else if (intVal==SVG_DISPLAY_LIST_ITEM) strcpy(attValue, "list-item"); + else if (intVal==SVG_DISPLAY_RUN_IN) strcpy(attValue, "run-in"); + else if (intVal==SVG_DISPLAY_COMPACT) strcpy(attValue, "compact"); + else if (intVal==SVG_DISPLAY_MARKER) strcpy(attValue, "marker"); + else if (intVal==SVG_DISPLAY_TABLE) strcpy(attValue, "table"); + else if (intVal==SVG_DISPLAY_INLINE_TABLE) strcpy(attValue, "inline-table"); + else if (intVal==SVG_DISPLAY_TABLE_ROW_GROUP) strcpy(attValue, "table-row-group"); + else if (intVal==SVG_DISPLAY_TABLE_HEADER_GROUP) strcpy(attValue, "table-header-group"); + else if (intVal==SVG_DISPLAY_TABLE_FOOTER_GROUP) strcpy(attValue, "table-footer-group"); + else if (intVal==SVG_DISPLAY_TABLE_ROW) strcpy(attValue, "table-row"); + else if (intVal==SVG_DISPLAY_TABLE_COLUMN_GROUP) strcpy(attValue, "table-column-group"); + else if (intVal==SVG_DISPLAY_TABLE_COLUMN) strcpy(attValue, "table-column"); + else if (intVal==SVG_DISPLAY_TABLE_CELL) strcpy(attValue, "table-cell"); + else if (intVal==SVG_DISPLAY_TABLE_CAPTION) strcpy(attValue, "table-caption"); + break; + case SVG_Visibility_datatype: + if (intVal==SVG_VISIBILITY_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_VISIBILITY_VISIBLE) strcpy(attValue, "visible"); + else if (intVal==SVG_VISIBILITY_HIDDEN) strcpy(attValue, "hidden"); + else if (intVal==SVG_VISIBILITY_COLLAPSE) strcpy(attValue, "collapse"); + break; + case SVG_Overflow_datatype: + if (intVal==SVG_OVERFLOW_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_OVERFLOW_VISIBLE) strcpy(attValue, "visible"); + else if (intVal==SVG_OVERFLOW_HIDDEN) strcpy(attValue, "hidden"); + else if (intVal==SVG_OVERFLOW_SCROLL) strcpy(attValue, "scroll"); + else if (intVal==SVG_OVERFLOW_AUTO) strcpy(attValue, "auto"); + break; + case SVG_ZoomAndPan_datatype: + if (intVal==SVG_ZOOMANDPAN_DISABLE) strcpy(attValue, "disable"); + else strcpy(attValue, "magnify"); + break; + case SVG_DisplayAlign_datatype: + if (intVal==SVG_DISPLAYALIGN_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_DISPLAYALIGN_AUTO) strcpy(attValue, "auto"); + else if (intVal==SVG_DISPLAYALIGN_BEFORE) strcpy(attValue, "before"); + else if (intVal==SVG_DISPLAYALIGN_CENTER) strcpy(attValue, "center"); + else if (intVal==SVG_DISPLAYALIGN_AFTER) strcpy(attValue, "after"); + break; + case SVG_TextAlign_datatype: + if (intVal==SVG_TEXTALIGN_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_TEXTALIGN_START) strcpy(attValue, "start"); + else if (intVal==SVG_TEXTALIGN_CENTER) strcpy(attValue, "center"); + else if (intVal==SVG_TEXTALIGN_END) strcpy(attValue, "end"); + break; + case SVG_PointerEvents_datatype: + if (intVal==SVG_POINTEREVENTS_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_POINTEREVENTS_VISIBLEPAINTED) strcpy(attValue, "visiblePainted"); + else if (intVal==SVG_POINTEREVENTS_VISIBLEFILL) strcpy(attValue, "visibleFill"); + else if (intVal==SVG_POINTEREVENTS_VISIBLESTROKE) strcpy(attValue, "visibleStroke"); + else if (intVal==SVG_POINTEREVENTS_VISIBLE) strcpy(attValue, "visible"); + else if (intVal==SVG_POINTEREVENTS_PAINTED) strcpy(attValue, "painted"); + else if (intVal==SVG_POINTEREVENTS_FILL) strcpy(attValue, "fill"); + else if (intVal==SVG_POINTEREVENTS_STROKE) strcpy(attValue, "stroke"); + else if (intVal==SVG_POINTEREVENTS_ALL) strcpy(attValue, "all"); + else if (intVal==SVG_POINTEREVENTS_NONE) strcpy(attValue, "none"); + else if (intVal==SVG_POINTEREVENTS_BOUNDINGBOX) strcpy(attValue, "boundingBox"); + break; + case SVG_RenderingHint_datatype: + if (intVal==SVG_RENDERINGHINT_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_RENDERINGHINT_AUTO) strcpy(attValue, "auto"); + else if (intVal==SVG_RENDERINGHINT_OPTIMIZEQUALITY) strcpy(attValue, "optimizeQuality"); + else if (intVal==SVG_RENDERINGHINT_OPTIMIZESPEED) strcpy(attValue, "optimizeSpeed"); + else if (intVal==SVG_RENDERINGHINT_OPTIMIZELEGIBILITY) strcpy(attValue, "optimizeLegibility"); + else if (intVal==SVG_RENDERINGHINT_CRISPEDGES) strcpy(attValue, "crispEdges"); + else if (intVal==SVG_RENDERINGHINT_GEOMETRICPRECISION) strcpy(attValue, "geometricPrecision"); + break; + case SVG_VectorEffect_datatype: + if (intVal==SVG_VECTOREFFECT_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SVG_VECTOREFFECT_NONE) strcpy(attValue, "none"); + else if (intVal==SVG_VECTOREFFECT_NONSCALINGSTROKE) strcpy(attValue, "non-scaling-stroke"); + break; + case SVG_PlaybackOrder_datatype: + if (intVal== SVG_PLAYBACKORDER_FORWARDONLY) strcpy(attValue, "forwardOnly"); + else if (intVal== SVG_PLAYBACKORDER_ALL) strcpy(attValue, "all"); + break; + case SVG_TimelineBegin_datatype: + if (intVal== SVG_TIMELINEBEGIN_ONSTART) strcpy(attValue, "onStart"); + else if (intVal== SVG_TIMELINEBEGIN_ONLOAD) strcpy(attValue, "onLoad"); + break; + case XML_Space_datatype: + if (intVal==XML_SPACE_DEFAULT) strcpy(attValue, "default"); + else if (intVal==XML_SPACE_PRESERVE) strcpy(attValue, "preserve"); + break; + case XMLEV_Propagate_datatype: + if (intVal==XMLEVENT_PROPAGATE_CONTINUE) strcpy(attValue, "continue"); + else if (intVal==XMLEVENT_PROPAGATE_STOP) strcpy(attValue, "stop"); + break; + case XMLEV_DefaultAction_datatype: + if (intVal==XMLEVENT_DEFAULTACTION_CANCEL) strcpy(attValue, "cancel"); + else if (intVal==XMLEVENT_DEFAULTACTION_PERFORM) strcpy(attValue, "perform"); + break; + case XMLEV_Phase_datatype: + if (intVal==XMLEVENT_PHASE_DEFAULT) strcpy(attValue, "default"); + else if (intVal==XMLEVENT_PHASE_CAPTURE) strcpy(attValue, "capture"); + break; + case SMIL_SyncBehavior_datatype: + if (intVal==SMIL_SYNCBEHAVIOR_INHERIT) strcpy(attValue, "inherit"); + else if (intVal==SMIL_SYNCBEHAVIOR_DEFAULT) strcpy(attValue, "default"); + else if (intVal==SMIL_SYNCBEHAVIOR_LOCKED) strcpy(attValue, "locked"); + else if (intVal==SMIL_SYNCBEHAVIOR_CANSLIP) strcpy(attValue, "canSlip"); + else if (intVal==SMIL_SYNCBEHAVIOR_INDEPENDENT) strcpy(attValue, "independent"); + break; + case SMIL_SyncTolerance_datatype: + if (((SMIL_SyncTolerance*)info->far_ptr)->type==SMIL_SYNCTOLERANCE_INHERIT) strcpy(attValue, "inherit"); + else if (((SMIL_SyncTolerance*)info->far_ptr)->type==SMIL_SYNCTOLERANCE_DEFAULT) strcpy(attValue, "default"); + else if (((SMIL_SyncTolerance*)info->far_ptr)->type==SMIL_SYNCBEHAVIOR_LOCKED) { + sprintf(attValue, "%g", ((SMIL_SyncTolerance*)info->far_ptr)->value); + } + break; + case SMIL_AttributeType_datatype: + if (intVal==SMIL_ATTRIBUTETYPE_AUTO) strcpy(attValue, "auto"); + else if (intVal==SMIL_ATTRIBUTETYPE_XML) strcpy(attValue, "XML"); + else if (intVal==SMIL_ATTRIBUTETYPE_CSS) strcpy(attValue, "CSS"); + break; + case SMIL_CalcMode_datatype: + if (intVal==SMIL_CALCMODE_DISCRETE) strcpy(attValue, "discrete"); + else if (intVal==SMIL_CALCMODE_LINEAR) strcpy(attValue, "linear"); + else if (intVal==SMIL_CALCMODE_PACED) strcpy(attValue, "paced"); + else if (intVal==SMIL_CALCMODE_SPLINE) strcpy(attValue, "spline"); + break; + case SMIL_Additive_datatype: + if (intVal==SMIL_ADDITIVE_REPLACE) strcpy(attValue, "replace"); + else if (intVal==SMIL_ADDITIVE_SUM) strcpy(attValue, "sum"); + break; + case SMIL_Accumulate_datatype: + if (intVal==SMIL_ACCUMULATE_NONE) strcpy(attValue, "none"); + else if (intVal==SMIL_ACCUMULATE_SUM) strcpy(attValue, "sum"); + break; + case SMIL_Restart_datatype: + if (intVal==SMIL_RESTART_ALWAYS) strcpy(attValue, "always"); + else if (intVal==SMIL_RESTART_WHENNOTACTIVE) strcpy(attValue, "whenNotActive"); + else if (intVal==SMIL_RESTART_NEVER) strcpy(attValue, "never"); + break; + case SMIL_Fill_datatype: + if (intVal==SMIL_FILL_FREEZE) strcpy(attValue, "freeze"); + else if (intVal==SMIL_FILL_REMOVE) strcpy(attValue, "remove"); + break; + + case SVG_GradientUnit_datatype: + if (intVal==SVG_GRADIENTUNITS_USER) strcpy(attValue, "userSpaceOnUse"); + else if (intVal==SVG_GRADIENTUNITS_OBJECT) strcpy(attValue, "objectBoundingBox"); + break; + case SVG_InitialVisibility_datatype: + if (intVal==SVG_INITIALVISIBILTY_WHENSTARTED) strcpy(attValue, "whenStarted"); + else if (intVal==SVG_INITIALVISIBILTY_ALWAYS) strcpy(attValue, "always"); + break; + case SVG_FocusHighlight_datatype: + if (intVal==SVG_FOCUSHIGHLIGHT_AUTO) strcpy(attValue, "auto"); + else if (intVal==SVG_FOCUSHIGHLIGHT_NONE) strcpy(attValue, "none"); + break; + case SVG_Overlay_datatype: + if (intVal==SVG_OVERLAY_NONE) strcpy(attValue, "none"); + else if (intVal==SVG_OVERLAY_TOP) strcpy(attValue, "top"); + break; + case SVG_TransformBehavior_datatype: + if (intVal==SVG_TRANSFORMBEHAVIOR_GEOMETRIC) strcpy(attValue, "geometric"); + else if (intVal==SVG_TRANSFORMBEHAVIOR_PINNED) strcpy(attValue, "pinned"); + else if (intVal==SVG_TRANSFORMBEHAVIOR_PINNED90) strcpy(attValue, "pinned90"); + else if (intVal==SVG_TRANSFORMBEHAVIOR_PINNED180) strcpy(attValue, "pinned180"); + else if (intVal==SVG_TRANSFORMBEHAVIOR_PINNED270) strcpy(attValue, "pinned270"); + break; + case SVG_SpreadMethod_datatype: + if (intVal==SVG_SPREAD_REFLECT) strcpy(attValue, "reflect"); + else if (intVal==SVG_SPREAD_REFLECT) strcpy(attValue, "repeat"); + else strcpy(attValue, "pad"); + break; + + case LASeR_Choice_datatype: + if (intVal==LASeR_CHOICE_ALL) strcpy(attValue, "all"); + else if (intVal==LASeR_CHOICE_NONE) strcpy(attValue, "none"); + else if (intVal==LASeR_CHOICE_N) { + char szT[1000]; + sprintf(szT, "%d", ((LASeR_Choice *)info->far_ptr)->choice_index); + strcat(attValue, szT); + } + break; + case LASeR_Size_datatype: + { + char szT[1000]; + sprintf(szT, "%g %g", FIX2FLT(((LASeR_Size *)info->far_ptr)->width), FIX2FLT(((LASeR_Size *)info->far_ptr)->height)); + strcat(attValue, szT); + } + break; +/* end of keyword type parsing */ + + /* inheritable floats */ + case SVG_FontSize_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_Rotate_datatype: + case SVG_Number_datatype: +#if DUMP_COORDINATES + svg_dump_number((SVG_Number *)info->far_ptr, attValue); +#endif + break; + + case XMLRI_datatype: + svg_dump_iri((XMLRI*)info->far_ptr, attValue); + break; + case XML_IDREF_datatype: + svg_dump_idref((XMLRI*)info->far_ptr, attValue); + break; + case XMLRI_List_datatype: + { + GF_List *l = *(GF_List **)info->far_ptr; + u32 i, count = gf_list_count(l); + for (i=0; ifar_ptr, attValue); +#endif + break; + case SVG_Points_datatype: + { +#if DUMP_COORDINATES + GF_List *l = *(GF_List **) info->far_ptr; + u32 i = 0; + u32 count = gf_list_count(l); + for (i=0; ix), FIX2FLT(p->y)); + if (i) strcat(attValue, " "); + strcat(attValue, szT); + } +#endif + } + break; + case SMIL_KeyTimes_datatype: + case SMIL_KeyPoints_datatype: + case SMIL_KeySplines_datatype: + { + GF_List *l = *(GF_List **) info->far_ptr; + u32 i = 0; + u32 count = gf_list_count(l); + for (i=0; ifar_ptr; + u32 i = 0; + u32 count = gf_list_count(l); + for (i=0; ifar_ptr; + if (v->is_set) + sprintf(attValue, "%g %g %g %g", FIX2FLT(v->x), FIX2FLT(v->y), FIX2FLT(v->width), FIX2FLT(v->height) ); + else + strcat(attValue, "none"); + } + break; + case SVG_StrokeDashArray_datatype: + { + SVG_StrokeDashArray *p = (SVG_StrokeDashArray *)info->far_ptr; + if (p->type==SVG_STROKEDASHARRAY_NONE) strcpy(attValue, "none"); + else if (p->type==SVG_STROKEDASHARRAY_INHERIT) strcpy(attValue, "inherit"); + else if (p->type==SVG_STROKEDASHARRAY_ARRAY) { + u32 i = 0; + for (i=0; iarray.count; i++) { + char szT[1000]; + sprintf(szT, "%g", FIX2FLT(p->array.vals[i])); + if (i) strcat(attValue, " "); + strcat(attValue, szT); + } + } + } + break; + case SVG_FontFamily_datatype: + { + SVG_FontFamily *f = (SVG_FontFamily *)info->far_ptr; + strcpy(attValue, (f->type==SVG_FONTFAMILY_INHERIT) ? "inherit" : (const char *) f->value); + } + break; + case SVG_PreserveAspectRatio_datatype: + { + SVG_PreserveAspectRatio *par = (SVG_PreserveAspectRatio *)info->far_ptr; + if (par->defer) strcat(attValue, "defer "); + if (par->align == SVG_PRESERVEASPECTRATIO_NONE) strcat(attValue, "none"); + else if (par->align == SVG_PRESERVEASPECTRATIO_XMINYMIN) strcat(attValue, "xMinYMin"); + else if (par->align == SVG_PRESERVEASPECTRATIO_XMIDYMIN) strcat(attValue, "xMidYMin"); + else if (par->align == SVG_PRESERVEASPECTRATIO_XMAXYMIN) strcat(attValue, "xMaxYMin"); + else if (par->align == SVG_PRESERVEASPECTRATIO_XMINYMID) strcat(attValue, "xMinYMid"); + else if (par->align == SVG_PRESERVEASPECTRATIO_XMIDYMID) strcat(attValue, "xMidYMid"); + else if (par->align == SVG_PRESERVEASPECTRATIO_XMAXYMID) strcat(attValue, "xMaxYMid"); + else if (par->align == SVG_PRESERVEASPECTRATIO_XMINYMAX) strcat(attValue, "xMinYMax"); + else if (par->align == SVG_PRESERVEASPECTRATIO_XMIDYMAX) strcat(attValue, "xMidYMax"); + else if (par->align == SVG_PRESERVEASPECTRATIO_XMAXYMAX) strcat(attValue, "xMaxYMax"); + if (par->meetOrSlice== SVG_MEETORSLICE_SLICE) strcat(attValue, " slice"); + } + break; + case SVG_Clock_datatype: + sprintf(attValue, "%g", * (SVG_Clock *)info->far_ptr ); + break; + + case SVG_ID_datatype: + case SVG_LanguageID_datatype: + case SVG_GradientOffset_datatype: + case DOM_String_datatype: + case SVG_ContentType_datatype: + if (*(SVG_String *)info->far_ptr) + strcpy(attValue, *(SVG_String *)info->far_ptr ); + break; + case SVG_Focus_datatype: + { + SVG_Focus *foc = (SVG_Focus *)info->far_ptr; + if (foc->type==SVG_FOCUS_SELF) strcpy(attValue, "self"); + else if (foc->type==SVG_FOCUS_AUTO) strcpy(attValue, "auto"); + else sprintf(attValue, "#%s", foc->target.string); + } + break; + case SVG_Focusable_datatype: + { + SVG_Focusable *f = (SVG_Focusable *)info->far_ptr; + if (*f == SVG_FOCUSABLE_TRUE) strcpy(attValue, "true"); + else if (*f == SVG_FOCUSABLE_FALSE) strcpy(attValue, "false"); + else strcpy(attValue, "auto"); + } + break; + + case DOM_StringList_datatype: + { + GF_List *l1 = *(GF_List **) info->far_ptr; + u32 i = 0; + u32 count = gf_list_count(l1); + for (i=0; ifar_ptr; + u32 i = 0; + u32 count = gf_list_count(l1); + for (i=0; ifar_ptr; + sprintf(attValue, "%g %g", FIX2FLT(m->m[2]), FIX2FLT(m->m[5])); +#endif + } + break; + + case SVG_Transform_datatype: + { + SVG_Transform *t= (SVG_Transform *)info->far_ptr; + if (t->is_ref) { + sprintf(attValue, "ref(svg,%g,%g)", FIX2FLT(t->mat.m[2]), FIX2FLT(t->mat.m[5]) ); + } else { + gf_svg_dump_matrix(&t->mat, attValue); + } + } + break; + + case SVG_Transform_Translate_datatype: + { + SVG_Point *pt = (SVG_Point *)info->far_ptr; +#if DUMP_COORDINATES + sprintf(attValue, "%g %g", FIX2FLT(pt->x), FIX2FLT(pt->y) ); +#endif + } + break; + + case SVG_Transform_Scale_datatype: + { + SVG_Point *pt = (SVG_Point *)info->far_ptr; +#if DUMP_COORDINATES + if (pt->x == pt->y) { + sprintf(attValue, "%g", FIX2FLT(pt->x)); + } else { + sprintf(attValue, "%g %g", FIX2FLT(pt->x), FIX2FLT(pt->y) ); + } +#endif + } + break; + + case SVG_Transform_SkewX_datatype: + case SVG_Transform_SkewY_datatype: + { + Fixed *f = (Fixed *)info->far_ptr; +#if DUMP_COORDINATES + sprintf(attValue, "%g", FIX2FLT( 180 * gf_divfix(*f, GF_PI) )); +#endif + } + break; + + case SVG_Transform_Rotate_datatype: + { + SVG_Point_Angle *pt = (SVG_Point_Angle *)info->far_ptr; +#if DUMP_COORDINATES + if (pt->x || pt->y) { + sprintf(attValue, "%g %g %g", FIX2FLT( 180 * gf_divfix(pt->angle, GF_PI) ), FIX2FLT(pt->x), FIX2FLT(pt->y) ); + } else { + sprintf(attValue, "%g", FIX2FLT(gf_divfix(180 * pt->angle, GF_PI) )); + } +#endif + } + break; + + case SMIL_AttributeName_datatype: + { + SMIL_AttributeName *att_name = (SMIL_AttributeName *) info->far_ptr; + if (att_name->name) { + strcpy(attValue, att_name->name); + return GF_OK; + } + if (att_name->tag) { + strcpy(attValue, gf_svg_get_attribute_name(elt, att_name->tag)); + return GF_OK; + } + +#if 0 + GF_Node *t=NULL; + u32 i, count; + if (!elt->xlink) break; + t = (GF_Node *) elt->xlink->href.target; + if (!t) break; + count = gf_node_get_field_count(t); + for (i=0; ifield_ptr) { + sprintf(attValue, fi.name); + return GF_OK; + } + } +#endif + } + break; + case SMIL_Times_datatype: + { + u32 i, count; + GF_Node *par = gf_node_get_parent((GF_Node *)elt, 0); + GF_List *l = *(GF_List **) info->far_ptr; + count = gf_list_count(l); + for (i=0; itype == GF_SMIL_TIME_CLOCK) { + sprintf(szBuf, "%gs", t->clock); + strcat(attValue, szBuf); + } else if (t->type==GF_SMIL_TIME_INDEFINITE) { + strcat(attValue, "indefinite"); + } else if (t->type==GF_SMIL_TIME_WALLCLOCK) { + u32 h, m, s; + /*TODO - day month and year*/ + h = (u32) t->clock * 3600; + m = (u32) (t->clock * 60 - 60*h); + s = (u32) (t->clock - 3600*h - 60*m); + sprintf(szBuf, "wallclock(%d:%d:%d)", h, m, s); + strcat(attValue, szBuf); + } + else if (t->type==GF_SMIL_TIME_EVENT) { + if (t->event.type == GF_EVENT_KEYDOWN) { + svg_dump_access_key(&t->event, szBuf); + strcat(attValue, szBuf); + } else { + if (t->element_id) { + strcat(attValue, t->element_id); + strcat(attValue, "."); + } else if (t->element && (t->element!=par) && gf_node_get_id(t->element) ) { + const char *name = gf_node_get_name(t->element); + if (name) { + strcat(attValue, name); + } else { + sprintf(szBuf, "N%d", gf_node_get_id(t->element)-1 ); + strcat(attValue, szBuf); + } + strcat(attValue, "."); + } + strcat(attValue, gf_dom_event_get_name(t->event.type)); + } + if (t->clock) { + sprintf(szBuf, "%gs", t->clock); + strcat(attValue, "+"); + strcat(attValue, szBuf); + } + } + } + } + break; + case SMIL_Duration_datatype: + { + SMIL_Duration *dur = (SMIL_Duration *)info->far_ptr; + if (dur->type == SMIL_DURATION_INDEFINITE) strcpy(attValue, "indefinite"); + else if (dur->type == SMIL_DURATION_MEDIA) strcpy(attValue, "media"); + else if (dur->type == SMIL_DURATION_DEFINED) sprintf(attValue, "%gs", dur->clock_value); + else { + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Dumping] smil duration not assigned\n")); + } + } + break; + case SMIL_RepeatCount_datatype: + { + SMIL_RepeatCount *rep = (SMIL_RepeatCount *)info->far_ptr; + if (rep->type == SMIL_REPEATCOUNT_INDEFINITE) strcpy(attValue, "indefinite"); + else if (rep->type == SMIL_REPEATCOUNT_DEFINED) sprintf(attValue, "%g", FIX2FLT(rep->count) ); + else { + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Dumping] smil repeat count not assigned\n")); + } + } + break; + case SVG_TransformType_datatype: + { + SVG_TransformType tr = *(SVG_TransformType *)info->far_ptr; + if (tr == SVG_TRANSFORM_MATRIX) strcpy(attValue, "matrix"); + else if (tr == SVG_TRANSFORM_SCALE) strcpy(attValue, "scale"); + else if (tr == SVG_TRANSFORM_ROTATE) strcpy(attValue, "rotate"); + else if (tr == SVG_TRANSFORM_TRANSLATE) strcpy(attValue, "translate"); + else if (tr == SVG_TRANSFORM_SKEWX) strcpy(attValue, "skewX"); + else if (tr == SVG_TRANSFORM_SKEWY) strcpy(attValue, "skewY"); + } + break; + + case SMIL_AnimateValue_datatype: + { + GF_FieldInfo a_fi; + SMIL_AnimateValue*av = (SMIL_AnimateValue*)info->far_ptr; + a_fi.fieldIndex = 0; + a_fi.fieldType = av->type; + a_fi.name = info->name; + a_fi.far_ptr = av->value; + gf_svg_dump_attribute(elt, &a_fi, attValue); + } + break; + case SMIL_AnimateValues_datatype: + { + GF_FieldInfo a_fi; + u32 i, count; + SMIL_AnimateValues *av = (SMIL_AnimateValues*)info->far_ptr; + if (av->type) { + count = gf_list_count(av->values); + a_fi.fieldIndex = 0; + a_fi.fieldType = av->type; + a_fi.name = info->name; + for (i=0; ivalues, i); + gf_svg_dump_attribute(elt, &a_fi, szBuf); + if (i) strcat(attValue, ";"); + strcat(attValue, szBuf); + } + } + } + break; + + case XMLEV_Event_datatype: + { + XMLEV_Event *d = (XMLEV_Event *)info->far_ptr; + if (d->parameter) { + svg_dump_access_key(d, attValue); + } else { + strcpy(attValue, gf_dom_event_get_name(d->type)); + } + break; + } + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Dumping] field %s of type %s not supported\n", info->name, gf_svg_attribute_type_to_string(info->fieldType))); + break; + } + return GF_OK; +} + +GF_Err gf_svg_dump_attribute_indexed(GF_Node *elt, GF_FieldInfo *info, char *attValue) +{ + strcpy(attValue, ""); + switch (info->fieldType) { + case SVG_PointerEvents_datatype: + break; + case XMLRI_List_datatype: + strcpy(attValue, (char *) info->far_ptr); + break; + + case SVG_Points_datatype: + { +#if DUMP_COORDINATES + SVG_Point *p = (SVG_Point *)info->far_ptr; + sprintf(attValue, "%g %g", FIX2FLT(p->x), FIX2FLT(p->y)); +#endif + } + break; + case SMIL_KeyPoints_datatype: + case SMIL_KeyTimes_datatype: + case SMIL_KeySplines_datatype: + { + Fixed *p = (Fixed *)info->far_ptr; + sprintf(attValue, "%g", FIX2FLT(*p)); + } + break; + case SVG_Coordinates_datatype: + { +#if DUMP_COORDINATES + SVG_Coordinate *p = (SVG_Coordinate *)info->far_ptr; + svg_dump_number((SVG_Length *)p, attValue); + if (strstr(attValue, "pt")) { + fprintf(stderr, "found pt in output\n"); + } +#endif + } + break; + case SVG_ViewBox_datatype: + { + Fixed *v = (Fixed *)info->far_ptr; + sprintf(attValue, "%g", FIX2FLT(*v)); + } + break; + case SVG_StrokeDashArray_datatype: + { + Fixed *p = (Fixed *)info->far_ptr; + sprintf(attValue, "%g", FIX2FLT(*p)); + } + break; + case SMIL_Times_datatype: + { + SMIL_Time *t = (SMIL_Time *)info->far_ptr; + if (t->type == GF_SMIL_TIME_CLOCK) { + sprintf(attValue, "%gs", t->clock); + } else if (t->type==GF_SMIL_TIME_INDEFINITE) { + strcpy(attValue, "indefinite"); + } else if (t->type==GF_SMIL_TIME_WALLCLOCK) { + u32 h, m, s; + /*TODO - day month and year*/ + h = (u32) t->clock * 3600; + m = (u32) (t->clock * 60 - 60*h); + s = (u32) (t->clock - 3600*h - 60*m); + sprintf(attValue, "wallclock(%d:%d:%d)", h, m, s); + } + else if (t->type==GF_SMIL_TIME_EVENT) { + GF_Node *par = gf_node_get_parent((GF_Node *)elt, 0); + if (t->event.type == GF_EVENT_KEYDOWN) { + svg_dump_access_key(&t->event, attValue); + } else { + strcpy(attValue, ""); + if (t->element_id) { + strcat(attValue, t->element_id); + strcat(attValue, "."); + } else if (t->element && (t->element!=par) && gf_node_get_id(t->element) ) { + const char *name = gf_node_get_name(t->element); + if (name) { + strcat(attValue, name); + } else { + sprintf(attValue, "N%d", gf_node_get_id(t->element)-1 ); + } + strcat(attValue, "."); + } + strcat(attValue, gf_dom_event_get_name(t->event.type)); + } + if (t->clock) { + char szBuf[100]; + sprintf(szBuf, "%gs", t->clock); + strcpy(attValue, "+"); + strcat(attValue, szBuf); + } + } + } + break; + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[SVG Dumping] indexed field %s of type %s not supported\n", info->name, gf_svg_attribute_type_to_string(info->fieldType))); + break; + } + return GF_OK; +} + +static Bool svg_viewbox_equal(SVG_ViewBox *v1, SVG_ViewBox *v2) +{ + if (v1->is_set != v2->is_set) return 0; + if (!v1->is_set) + return 1; + else { + if ( (v1->x == v2->x) && (v1->y == v2->y) && (v1->width == v2->width) && (v1->height == v2->height) ) + return 1; + else + return 0; + } +} + +static Bool svg_colors_equal(SVG_Color *c1, SVG_Color *c2) +{ + if (c1->type != c2->type) return 0; + if (c1->red != c2->red) return 0; + if (c1->green != c2->green) return 0; + if (c1->blue != c2->blue) return 0; + return 1; +} +static Bool svg_numbers_equal(SVG_Length *l1, SVG_Length *l2) +{ + if (l1->type!=l2->type) return 0; + if (l1->type >= SVG_NUMBER_INHERIT) return 1; + return (l1->value==l2->value) ? 1 : 0; +} +static Bool svg_iris_equal(XMLRI*iri1, XMLRI*iri2) +{ + u32 type1, type2; + type1 = iri1->type; + type2 = iri2->type; + /*ignore undef hrefs, these are internall ones*/ + if ((iri1->type == XMLRI_ELEMENTID) && iri1->target) { + if (!gf_node_get_id((GF_Node *)iri1->target)) type1 = 0; + } + if ((iri2->type == XMLRI_ELEMENTID) && iri2->target) { + if (!gf_node_get_id((GF_Node *)iri2->target)) type2 = 0; + } + if (type1 != type2) return 0; + if ((type1 == XMLRI_ELEMENTID) && (iri1->target == iri2->target) ) return 1; + if (iri1->string && iri2->string && !strcmp(iri1->string, iri2->string)) return 1; + if (!iri1->string && !iri2->string) return 1; + return 0; +} +static Bool svg_matrices_equal(GF_Matrix2D *m1, GF_Matrix2D *m2) +{ + if (m1->m[0] != m2->m[0]) return 0; + if (m1->m[1] != m2->m[1]) return 0; + if (m1->m[2] != m2->m[2]) return 0; + if (m1->m[3] != m2->m[3]) return 0; + if (m1->m[4] != m2->m[4]) return 0; + if (m1->m[5] != m2->m[5]) return 0; + return 1; +} + +Bool gf_svg_attributes_equal(GF_FieldInfo *f1, GF_FieldInfo *f2) +{ + u32 v1, v2; + if (f1->fieldType!=f2->fieldType) return 0; + if (f1->far_ptr && !f2->far_ptr) return 0; + if (f2->far_ptr && !f1->far_ptr) return 0; + if (!f1->far_ptr) return 1; + v1 = *(u8 *)f1->far_ptr; + v2 = *(u8 *)f2->far_ptr; + + switch (f1->fieldType) { + case SVG_Boolean_datatype: + case SVG_FillRule_datatype: + case SVG_StrokeLineJoin_datatype: + case SVG_StrokeLineCap_datatype: + case SVG_FontStyle_datatype: + case SVG_FontWeight_datatype: + case SVG_FontVariant_datatype: + case SVG_TextAnchor_datatype: + case SVG_Display_datatype: + case SVG_Visibility_datatype: + case SVG_GradientUnit_datatype: + case SVG_PreserveAspectRatio_datatype: + case XML_Space_datatype: + case XMLEV_Propagate_datatype: + case XMLEV_DefaultAction_datatype: + case XMLEV_Phase_datatype: + case SMIL_SyncBehavior_datatype: + case SMIL_AttributeType_datatype: + case SMIL_CalcMode_datatype: + case SMIL_Additive_datatype: + case SMIL_Accumulate_datatype: + case SMIL_Restart_datatype: + case SMIL_Fill_datatype: + case SVG_Overflow_datatype: + case SVG_ZoomAndPan_datatype: + case SVG_DisplayAlign_datatype: + case SVG_TextAlign_datatype: + case SVG_PointerEvents_datatype: + case SVG_RenderingHint_datatype: + case SVG_VectorEffect_datatype: + case SVG_PlaybackOrder_datatype: + case SVG_TimelineBegin_datatype: + case SVG_FocusHighlight_datatype: + case SVG_TransformType_datatype: + case SVG_Overlay_datatype: + case SVG_TransformBehavior_datatype: + case SVG_SpreadMethod_datatype: + case SVG_InitialVisibility_datatype: + case LASeR_Choice_datatype: + return (v1==v2) ? 1 : 0; + case SVG_Color_datatype: + return svg_colors_equal((SVG_Color *)f1->far_ptr, (SVG_Color *)f2->far_ptr); + case SMIL_SyncTolerance_datatype: + { + SMIL_SyncTolerance *st1 = (SMIL_SyncTolerance*)f1->far_ptr; + SMIL_SyncTolerance *st2 = (SMIL_SyncTolerance*)f2->far_ptr; + if (st1->type!=st2->type) return 0; + if ((st1->type==SMIL_SYNCTOLERANCE_VALUE) && (st1->value!=st2->value)) return 0; + return 1; + } + + case SVG_Paint_datatype: + { + SVG_Paint *p1 = (SVG_Paint *)f1->far_ptr; + SVG_Paint *p2 = (SVG_Paint *)f2->far_ptr; + if (p1->type != p2->type) return 0; + if (p1->type==SVG_PAINT_COLOR) return svg_colors_equal(&p1->color, &p2->color); + else if (p1->type==SVG_PAINT_URI) return svg_iris_equal(&p1->iri, &p2->iri); + return 1; + } + break; + + case SVG_FontSize_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_Rotate_datatype: + case SVG_Number_datatype: + return svg_numbers_equal((SVG_Number *)f1->far_ptr, (SVG_Number *)f2->far_ptr); + case XMLRI_datatype: + return svg_iris_equal((XMLRI*)f1->far_ptr, (XMLRI*)f2->far_ptr); + case XMLRI_List_datatype: + { + GF_List *l1 = *(GF_List **)f1->far_ptr; + GF_List *l2 = *(GF_List **)f2->far_ptr; + u32 i, count = gf_list_count(l1); + if (gf_list_count(l2)!=count) return 0; + for (i=0; ifar_ptr; + SVG_PathData *d2 = (SVG_PathData *)f2->far_ptr; + u32 i; + /*FIXME - be less lazy..*/ +#if USE_GF_PATH + if (d1->n_points != d2->n_points) return 0; + if (d1->n_contours != d2->n_contours) return 0; + for (i=0; in_points; i++) { + if (d1->points[i].x != d2->points[i].x) return 0; + if (d1->points[i].y != d2->points[i].y) return 0; + } + for (i=0; in_points; i++) { + if (d1->tags[i] != d2->tags[i]) return 0; + } + for (i=0; in_contours; i++) { + if (d1->contours[i] != d2->contours[i]) return 0; + } + return 1; +#else + if (!gf_list_count(d1->commands) && !gf_list_count(d2->commands)) return 1; +#endif + return 0; + } + case SVG_Points_datatype: + { + GF_List *l1 = *(GF_List **) f1->far_ptr; + GF_List *l2 = *(GF_List **) f2->far_ptr; + u32 i = 0; + u32 count = gf_list_count(l1); + if (gf_list_count(l2)!=count) return 0; + for (i=0; ix != p2->x) return 0; + if (p1->y != p2->y) return 0; + } + return 1; + } + case SMIL_KeyTimes_datatype: + case SMIL_KeyPoints_datatype: + case SMIL_KeySplines_datatype: + { + GF_List *l1 = *(GF_List **) f1->far_ptr; + GF_List *l2 = *(GF_List **) f2->far_ptr; + u32 i = 0; + u32 count = gf_list_count(l1); + if (gf_list_count(l2)!=count) return 0; + for (i=0; ifar_ptr; + GF_List *l2 = *(GF_List **) f2->far_ptr; + u32 i = 0; + u32 count = gf_list_count(l1); + if (gf_list_count(l2) != count) return 0; + for (i=0; ifar_ptr; + SVG_ViewBox *v2 = (SVG_ViewBox *)f2->far_ptr; + return svg_viewbox_equal(v1, v2); + } + case SVG_StrokeDashArray_datatype: + { + SVG_StrokeDashArray *p1 = (SVG_StrokeDashArray *)f1->far_ptr; + SVG_StrokeDashArray *p2 = (SVG_StrokeDashArray *)f2->far_ptr; + if (p1->type!=p2->type) return 0; + if (p1->type==SVG_STROKEDASHARRAY_ARRAY) { + u32 i = 0; + if (p1->array.count != p2->array.count) return 0; + for (i=0; iarray.count; i++) { + if (p1->array.vals[i] != p2->array.vals[i]) return 0; + } + } + return 1; + } + case SVG_FontFamily_datatype: + { + SVG_FontFamily *ff1 = (SVG_FontFamily *)f1->far_ptr; + SVG_FontFamily *ff2 = (SVG_FontFamily *)f2->far_ptr; + if (ff1->type!=ff2->type) return 0; + if (ff1->type==SVG_FONTFAMILY_INHERIT) return 1; + return (ff1->value && ff2->value && !strcmp(ff1->value, ff2->value)) ? 1 : 0; + } + + case SVG_Clock_datatype: + return (* (SVG_Clock *)f1->far_ptr == * (SVG_Clock *)f2->far_ptr) ? 1 : 0; + + /* required for animateMotion */ + case SVG_Motion_datatype: + return svg_matrices_equal((GF_Matrix2D*)f1->far_ptr, (GF_Matrix2D*)f2->far_ptr); + + case SVG_Transform_datatype: + { + SVG_Transform *t1 = (SVG_Transform *)f1->far_ptr; + SVG_Transform *t2 = (SVG_Transform *)f2->far_ptr; + if (t1->is_ref == t2->is_ref) + return svg_matrices_equal(&t1->mat, &t2->mat); + else + return 0; + } + + case SVG_Transform_Translate_datatype: + case SVG_Transform_Scale_datatype: + { + SVG_Point *p1 = (SVG_Point *)f1->far_ptr; + SVG_Point *p2 = (SVG_Point *)f2->far_ptr; + if (p1->x != p2->x) return 0; + if (p1->y != p2->y) return 0; + return 1; + } + + case SVG_Transform_SkewX_datatype: + case SVG_Transform_SkewY_datatype: + { + Fixed *p1 = (Fixed *)f1->far_ptr; + Fixed *p2 = (Fixed *)f2->far_ptr; + return (*p1 == *p2); + } + + case SVG_Transform_Rotate_datatype: + { + SVG_Point_Angle *p1 = (SVG_Point_Angle *)f1->far_ptr; + SVG_Point_Angle *p2 = (SVG_Point_Angle *)f2->far_ptr; + if (p1->x != p2->x) return 0; + if (p1->y != p2->y) return 0; + if (p1->angle != p2->angle) return 0; + return 1; + } + + + case SVG_ID_datatype: + case SVG_LanguageID_datatype: + case SVG_GradientOffset_datatype: + case DOM_String_datatype: + case SVG_ContentType_datatype: + { + char *str1 = *(SVG_String *)f1->far_ptr; + char *str2 = *(SVG_String *)f2->far_ptr; + if (!str1 && !str2) return 1; + return (str1 && str2 && !strcmp(str1, str2)) ? 1 : 0; + } + + case SVG_Focus_datatype: + { + SVG_Focus *foc1 = (SVG_Focus *) f1->far_ptr; + SVG_Focus *foc2 = (SVG_Focus *)f2->far_ptr; + if (foc1->type!=foc2->type) return 0; + if (foc1->type != SVG_FOCUS_IRI) return 1; + return (foc1->target.string && foc2->target.string && !strcmp(foc1->target.string, foc2->target.string)) ? 1 : 0; + } + break; + + case DOM_StringList_datatype: + { + GF_List *l1 = *(GF_List **) f1->far_ptr; + GF_List *l2 = *(GF_List **) f2->far_ptr; + u32 i = 0; + u32 count = gf_list_count(l1); + if (gf_list_count(l2) != count) return 0; + for (i=0; ifar_ptr; + GF_List *l2 = *(GF_List **) f2->far_ptr; + u32 i = 0; + u32 count = gf_list_count(l1); + if (gf_list_count(l2) != count) return 0; + for (i=0; ifar_ptr; + GF_List *l2 = *(GF_List **) f2->far_ptr; + u32 i = 0; + u32 count = gf_list_count(l1); + if (gf_list_count(l2) != count) return 0; + for (i=0; itype != p2->type) return 0; + if (p1->clock != p2->clock) return 0; + if (p1->type==GF_SMIL_TIME_EVENT) { + if (p1->event.type != p2->event.type) return 0; + if (p1->event.parameter != p2->event.parameter) return 0; + } + } + return 1; + } + case SMIL_Duration_datatype: + { + SMIL_Duration *d1 = (SMIL_Duration *)f1->far_ptr; + SMIL_Duration *d2 = (SMIL_Duration *)f2->far_ptr; + if (d1->type != d2->type) return 0; + if (d1->clock_value != d2->clock_value) return 0; + return 1; + } + case SMIL_RepeatCount_datatype: + { + SMIL_RepeatCount *d1 = (SMIL_RepeatCount *)f1->far_ptr; + SMIL_RepeatCount *d2 = (SMIL_RepeatCount *)f2->far_ptr; + if (d1->type != d2->type) return 0; + if (d1->count != d2->count) return 0; + return 1; + } + + case SMIL_AttributeName_datatype: + { + SMIL_AttributeName *att1 = (SMIL_AttributeName *) f1->far_ptr; + SMIL_AttributeName *att2 = (SMIL_AttributeName *) f2->far_ptr; + /*TODO check me...*/ + if (att2->field_ptr == att1->field_ptr) return 1; + return 0; + } + + case SMIL_AnimateValue_datatype: + { + SMIL_AnimateValue *av1 = (SMIL_AnimateValue*)f1->far_ptr; + SMIL_AnimateValue *av2 = (SMIL_AnimateValue*)f2->far_ptr; + if (av1->value != av2->value) return 0; + return 1; + } + break; + + case SMIL_AnimateValues_datatype: + { + u32 count; + SMIL_AnimateValues *av1 = (SMIL_AnimateValues*)f1->far_ptr; + SMIL_AnimateValues *av2 = (SMIL_AnimateValues*)f2->far_ptr; + if (av1->type != av2->type) return 0; + if ( (count = gf_list_count(av1->values) ) != gf_list_count(av1->values)) return 0; + return count ? 0 : 1; + } + case XMLEV_Event_datatype: + { + XMLEV_Event *d1 = (XMLEV_Event *)f1->far_ptr; + XMLEV_Event *d2 = (XMLEV_Event *)f2->far_ptr; + if (d1->type != d2->type) return 0; + if (d1->parameter != d2->parameter) return 0; + return 1; + } + case LASeR_Size_datatype: + { + LASeR_Size *sz1 = (LASeR_Size *)f1->far_ptr; + LASeR_Size *sz2 = (LASeR_Size *)f2->far_ptr; + if (sz1->width != sz2->width) return 0; + if (sz1->height != sz2->height) return 0; + return 1; + } + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING|GF_LOG_INTERACT, ("[SVG Attributes] comparaison for field %s of type %s not supported\n", f1->name, gf_svg_attribute_type_to_string(f1->fieldType))); + return 0; + } +} + +static void svg_color_clamp(SVG_Color *a) +{ + a->red = MAX(0, MIN(FIX_ONE, a->red)); + a->green = MAX(0, MIN(FIX_ONE, a->green)); + a->blue = MAX(0, MIN(FIX_ONE, a->blue)); +} + +static GF_Err svg_color_muladd(Fixed alpha, SVG_Color *a, Fixed beta, SVG_Color *b, SVG_Color *c, Bool clamp) +{ + if (a->type != SVG_COLOR_RGBCOLOR || b->type != SVG_COLOR_RGBCOLOR) { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] only RGB colors are additive\n")); + return GF_BAD_PARAM; + } + c->type = SVG_COLOR_RGBCOLOR; + c->red = gf_mulfix(alpha, a->red) + gf_mulfix(beta, b->red); + c->green = gf_mulfix(alpha, a->green) + gf_mulfix(beta, b->green); + c->blue = gf_mulfix(alpha, a->blue) + gf_mulfix(beta, b->blue); + if (clamp) svg_color_clamp(c); + return GF_OK; +} + +static GF_Err svg_number_muladd(Fixed alpha, SVG_Number *a, Fixed beta, SVG_Number *b, SVG_Number *c) +{ + if (a->type != b->type) { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] cannot add lengths of mismatching types\n")); + return GF_BAD_PARAM; + } + if (a->type == SVG_NUMBER_INHERIT || a->type == SVG_NUMBER_AUTO) { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] cannot add lengths\n")); + return GF_BAD_PARAM; + } + c->value = gf_mulfix(alpha, a->value) + gf_mulfix(beta, b->value); + return GF_OK; +} + +static GF_Err svg_viewbox_muladd(Fixed alpha, SVG_ViewBox *a, Fixed beta, SVG_ViewBox *b, SVG_ViewBox *c) +{ + c->is_set = 1; + c->x = gf_mulfix(alpha, a->x) + gf_mulfix(beta, b->x); + c->y = gf_mulfix(alpha, a->y) + gf_mulfix(beta, b->y); + c->width = gf_mulfix(alpha, a->width) + gf_mulfix(beta, b->width); + c->height= gf_mulfix(alpha, a->height) + gf_mulfix(beta, b->height); + return GF_OK; +} + +static GF_Err svg_point_muladd(Fixed alpha, SVG_Point *pta, Fixed beta, SVG_Point *ptb, SVG_Point *ptc) +{ + ptc->x = gf_mulfix(alpha, pta->x) + gf_mulfix(beta, ptb->x); + ptc->y = gf_mulfix(alpha, pta->y) + gf_mulfix(beta, ptb->y); + return GF_OK; +} + +static GF_Err svg_point_angle_muladd(Fixed alpha, SVG_Point_Angle *pta, Fixed beta, SVG_Point_Angle *ptb, SVG_Point_Angle *ptc) +{ + ptc->x = gf_mulfix(alpha, pta->x) + gf_mulfix(beta, ptb->x); + ptc->y = gf_mulfix(alpha, pta->y) + gf_mulfix(beta, ptb->y); + ptc->angle = gf_mulfix(alpha, pta->angle) + gf_mulfix(beta, ptb->angle); + return GF_OK; +} + +static GF_Err svg_points_muladd(Fixed alpha, SVG_Points *a, Fixed beta, SVG_Points *b, SVG_Points *c) +{ + u32 a_count = gf_list_count(*a); + u32 i; + + if (a_count != gf_list_count(*b)) return GF_BAD_PARAM; + + while (gf_list_count(*c)) { + SVG_Point *ptc = (SVG_Point *)gf_list_get(*c, 0); + gf_list_rem(*c, 0); + free(ptc); + } + for (i = 0; i < a_count; i ++) { + SVG_Point *ptc; + SVG_Point *pta = (SVG_Point *)gf_list_get(*a, i); + SVG_Point *ptb = (SVG_Point *)gf_list_get(*b, i); + GF_SAFEALLOC(ptc, SVG_Point) + svg_point_muladd(alpha, pta, beta, ptb, ptc); + gf_list_add(*c, ptc); + } + + return GF_OK; +} + +static GF_Err svg_points_copy(SVG_Points *a, SVG_Points *b) +{ + u32 i, count; + + count = gf_list_count(*a); + for (i = 0; i < count; i++) { + SVG_Point *pt = (SVG_Point *)gf_list_get(*a, i); + free(pt); + } + gf_list_reset(*a); + + count = gf_list_count(*b); + for (i = 0; i < count; i ++) { + SVG_Point *ptb = (SVG_Point *)gf_list_get(*b, i); + SVG_Point *pta; + GF_SAFEALLOC(pta, SVG_Point) + *pta = *ptb; + gf_list_add(*a, pta); + } + return GF_OK; + +} + +static GF_Err svg_numbers_muladd(Fixed alpha, SVG_Numbers *a, Fixed beta, SVG_Numbers *b, SVG_Numbers *c) +{ + u32 a_count = gf_list_count(*a); + u32 i; + + if (a_count != gf_list_count(*b)) return GF_BAD_PARAM; + + gf_list_reset(*c); + for (i = 0; i < a_count; i ++) { + SVG_Number *nc; + SVG_Number *na = (SVG_Number *)gf_list_get(*a, i); + SVG_Number *nb = (SVG_Number *)gf_list_get(*b, i); + GF_SAFEALLOC(nc, SVG_Number) + svg_number_muladd(alpha, na, beta, nb, nc); + gf_list_add(*c, nc); + } + return GF_OK; +} + +static GF_Err svg_numbers_copy(SVG_Numbers *a, SVG_Numbers *b) +{ + u32 i, count; + + count = gf_list_count(*a); + for (i = 0; i < count; i++) { + SVG_Coordinate *c = (SVG_Coordinate *)gf_list_get(*a, i); + free(c); + } + gf_list_reset(*a); + + count = gf_list_count(*b); + for (i = 0; i < count; i ++) { + SVG_Number *na; + GF_SAFEALLOC(na, SVG_Number) + *na = *(SVG_Number *)gf_list_get(*b, i); + gf_list_add(*a, na); + } + return GF_OK; +} + +#if USE_GF_PATH +static GF_Err svg_path_copy(SVG_PathData *a, SVG_PathData *b) +{ + if (a->contours) free(a->contours); + if (a->points) free(a->points); + if (a->tags) free(a->tags); + + a->contours = (u32 *)malloc(sizeof(u32)*b->n_contours); + a->points = (GF_Point2D *) malloc(sizeof(GF_Point2D)*b->n_points); + a->tags = (u8 *) malloc(sizeof(u8)*b->n_points); + memcpy(a->contours, b->contours, sizeof(u32)*b->n_contours); + a->n_contours = b->n_contours; + memcpy(a->points, b->points, sizeof(GF_Point2D)*b->n_points); + memcpy(a->tags, b->tags, sizeof(u8)*b->n_points); + a->n_alloc_points = a->n_points = b->n_points; + a->flags = b->flags; + a->bbox = b->bbox; + a->fineness = b->fineness; + return GF_OK; +} +#else +static GF_Err svg_path_copy(SVG_PathData *a, SVG_PathData *b) +{ + u32 i, count; + count = gf_list_count(a->commands); + for (i = 0; i < count; i++) { + u8 *command = (u8 *)gf_list_get(a->commands, i); + free(command); + } + gf_list_reset(a->commands); + count = gf_list_count(a->points); + for (i = 0; i < count; i++) { + SVG_Point *pt = (SVG_Point *)gf_list_get(a->points, i); + free(pt); + } + gf_list_reset(a->points); + + count = gf_list_count(b->commands); + for (i = 0; i < count; i ++) { + u8 *nc = (u8 *)malloc(sizeof(u8)); + *nc = *(u8*)gf_list_get(b->commands, i); + gf_list_add(a->commands, nc); + } + count = gf_list_count(b->points); + for (i = 0; i < count; i ++) { + SVG_Point *pta; + GF_SAFEALLOC(pta, SVG_Point) + *pta = *(SVG_Point *)gf_list_get(b->points, i); + gf_list_add(a->points, pta); + } + return GF_OK; +} +#endif + +#if USE_GF_PATH +static GF_Err svg_path_muladd(Fixed alpha, SVG_PathData *a, Fixed beta, SVG_PathData *b, SVG_PathData *c) +{ + u32 i; + + if (a->n_points != b->n_points) return GF_BAD_PARAM; + gf_path_reset(c); + svg_path_copy(c, a); + + for (i=0; in_points; i++) { + svg_point_muladd(alpha, (SVG_Point *) &a->points[i], beta, (SVG_Point *) &b->points[i], (SVG_Point *) &c->points[i]); + } + c->flags |= GF_PATH_BBOX_DIRTY; + c->flags &= ~GF_PATH_FLATTENED; + return GF_OK; +} +#else +static GF_Err svg_path_muladd(Fixed alpha, SVG_PathData *a, Fixed beta, SVG_PathData *b, SVG_PathData *c) +{ + u32 i, ccount, pcount; + + ccount = gf_list_count(a->commands); + pcount = gf_list_count(a->points); + + if (pcount != gf_list_count(b->points)) return GF_BAD_PARAM; + +#if 0 + if (ccount != gf_list_count(b->commands)) return GF_BAD_PARAM; + for (i = 0; i < ccount; i++) { + u8 *ac = gf_list_get(a->commands, i); + u8 *bc = gf_list_get(b->commands, i); + if (*ac != *bc) return GF_BAD_PARAM; + } +#endif + + while (gf_list_count(c->commands)) { + u8 *command = (u8 *)gf_list_last(c->commands); + free(command); + gf_list_rem_last(c->commands); + } + while (gf_list_count(c->points)) { + SVG_Point *pt = (SVG_Point *)gf_list_last(c->points); + free(pt); + gf_list_rem_last(c->points); + } + + for (i = 0; i < ccount; i++) { + u8 *nc = (u8 *)malloc(sizeof(u8)); + *nc = *(u8*)gf_list_get(a->commands, i); + gf_list_add(c->commands, nc); + } + for (i = 0; i < pcount; i++) { + SVG_Point *pta = (SVG_Point *)gf_list_get(a->points, i); + SVG_Point *ptb = (SVG_Point *)gf_list_get(b->points, i); + SVG_Point *ptc; + GF_SAFEALLOC(ptc, SVG_Point) + svg_point_muladd(alpha, pta, beta, ptb, ptc); + gf_list_add(c->points, ptc); + } + return GF_OK; +} +#endif + + +static GF_Err svg_dasharray_muladd(Fixed alpha, SVG_StrokeDashArray *a, Fixed beta, SVG_StrokeDashArray *b, SVG_StrokeDashArray *c) +{ + u32 i; + if (a->type != b->type) return GF_BAD_PARAM; + if (a->array.count != b->array.count) return GF_BAD_PARAM; + + c->type = a->type; + c->array.count = a->array.count; + c->array.vals = (Fixed *) malloc(sizeof(Fixed)*c->array.count); + for (i = 0; i < c->array.count; i++) { + c->array.vals[i] = gf_mulfix(alpha, a->array.vals[i]) + gf_mulfix(beta, b->array.vals[i]); + } + return GF_OK; +} + +static GF_Err svg_dasharray_copy(SVG_StrokeDashArray *a, SVG_StrokeDashArray *b) +{ + a->type = b->type; + a->array.count = b->array.count; + a->array.vals = (Fixed*)malloc(sizeof(Fixed)*a->array.count); + memcpy(a->array.vals, b->array.vals, sizeof(Fixed)*a->array.count); + return GF_OK; +} + +static GF_Err svg_matrix_muladd(Fixed alpha, GF_Matrix2D *a, Fixed beta, GF_Matrix2D *b, GF_Matrix2D *c) +{ + /* + if ((alpha == beta) && (alpha == FIX_ONE) ) { + GF_Matrix2D tmp; + gf_mx2d_copy(tmp, *b); + gf_mx2d_add_matrix(&tmp, a); + gf_mx2d_copy(*c, tmp); + } else */ + if (alpha <= FIX_ONE) { + /* This case should happen only when using animateMotion and accumulation + see animate-elem-202-t.svg + we only add and multiply the translation component; */ + /* + c->m[0] = gf_mulfix(alpha, a->m[0]) + gf_mulfix(beta, b->m[0]); + c->m[1] = gf_mulfix(alpha, a->m[1]) + gf_mulfix(beta, b->m[1]); + c->m[2] = gf_mulfix(alpha, a->m[2]) + gf_mulfix(beta, b->m[2]); + c->m[3] = gf_mulfix(alpha, a->m[3]) + gf_mulfix(beta, b->m[3]); + */ + c->m[0] = a->m[0]; + c->m[1] = a->m[1]; + c->m[2] = gf_mulfix(alpha, a->m[2]) + gf_mulfix(beta, b->m[2]); + c->m[3] = a->m[3]; + c->m[4] = a->m[4]; + c->m[5] = gf_mulfix(alpha, a->m[5]) + gf_mulfix(beta, b->m[5]); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] matrix operations not supported\n")); + return GF_BAD_PARAM; + } + return GF_OK; +} + +static GF_Err laser_size_muladd(Fixed alpha, LASeR_Size *sza, Fixed beta, LASeR_Size *szb, LASeR_Size *szc) +{ + szc->width = gf_mulfix(alpha, sza->width) + gf_mulfix(beta, szb->width); + szc->height = gf_mulfix(alpha, sza->height) + gf_mulfix(beta, szb->height); + return GF_OK; +} + +/* c = alpha * a + beta * b */ +GF_Err gf_svg_attributes_muladd(Fixed alpha, GF_FieldInfo *a, + Fixed beta, GF_FieldInfo *b, + GF_FieldInfo *c, + Bool clamp) +{ + if (!a->far_ptr || !b->far_ptr || !c->far_ptr) return GF_BAD_PARAM; + + if (a->fieldType != b->fieldType) { + if (a->fieldType != SVG_Transform_datatype && + a->fieldType != SVG_Transform_Scale_datatype && + a->fieldType != SVG_Transform_Translate_datatype && + a->fieldType != SVG_Transform_Rotate_datatype && + a->fieldType != SVG_Transform_SkewX_datatype && + a->fieldType != SVG_Transform_SkewY_datatype && + a->fieldType != SVG_Motion_datatype) + return GF_BAD_PARAM; + } + + /* by default a and c are of the same type, except for matrix related types */ + c->fieldType = a->fieldType; + + switch (a->fieldType) { + + /* Numeric types */ + case SVG_Color_datatype: + return svg_color_muladd(alpha, (SVG_Color*)a->far_ptr, beta, (SVG_Color*)b->far_ptr, (SVG_Color*)c->far_ptr, clamp); + + case SVG_Paint_datatype: + { + SVG_Paint *pa = (SVG_Paint *)a->far_ptr; + SVG_Paint *pb = (SVG_Paint *)b->far_ptr; + SVG_Paint *pc = (SVG_Paint *)c->far_ptr; + if (pa->type != pb->type || pa->type != SVG_PAINT_COLOR || pb->type != SVG_PAINT_COLOR) { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] only color paints are additive\n")); + return GF_BAD_PARAM; + } + pc->type = SVG_PAINT_COLOR; + return svg_color_muladd(alpha, &pa->color, beta, &pb->color, &pc->color, clamp); + } + + case SVG_Number_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_FontSize_datatype: + return svg_number_muladd(alpha, (SVG_Number*)a->far_ptr, beta, (SVG_Number*)b->far_ptr, (SVG_Number*)c->far_ptr); + + case SVG_ViewBox_datatype: + return svg_viewbox_muladd(alpha, (SVG_ViewBox*)a->far_ptr, beta, (SVG_ViewBox*)b->far_ptr, (SVG_ViewBox*)c->far_ptr); + + case SVG_Points_datatype: + return svg_points_muladd(alpha, (GF_List **)a->far_ptr, beta, (GF_List **)b->far_ptr, (GF_List **)c->far_ptr); + + case SVG_Numbers_datatype: + case SVG_Coordinates_datatype: + return svg_numbers_muladd(alpha, (GF_List **)a->far_ptr, beta, (GF_List **)b->far_ptr, (GF_List **)c->far_ptr); + + case SVG_PathData_datatype: + return svg_path_muladd(alpha, (SVG_PathData*)a->far_ptr, beta, (SVG_PathData*)b->far_ptr, (SVG_PathData*)c->far_ptr); + + case SVG_StrokeDashArray_datatype: + return svg_dasharray_muladd(alpha, (SVG_StrokeDashArray*)a->far_ptr, beta, (SVG_StrokeDashArray*)b->far_ptr, (SVG_StrokeDashArray*)c->far_ptr); + + case SVG_Motion_datatype: + return svg_matrix_muladd(alpha, (GF_Matrix2D*)a->far_ptr, beta, (GF_Matrix2D*)b->far_ptr, (GF_Matrix2D*)c->far_ptr); + + case SVG_Transform_datatype: + if (b->fieldType == SVG_Transform_datatype) { + SVG_Transform *ta = (SVG_Transform *)a->far_ptr; + SVG_Transform *tb = (SVG_Transform *)b->far_ptr; + SVG_Transform *tc = (SVG_Transform *)c->far_ptr; + if (ta->is_ref == tb->is_ref) { + return svg_matrix_muladd(alpha, &ta->mat, beta, &tb->mat, &tc->mat); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] matrix operations not supported\n")); + return GF_NOT_SUPPORTED; + } + } else { + /* a and c are matrices but b is not */ + GF_Matrix2D tmp; + if (alpha != FIX_ONE) { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] matrix operations not supported\n")); + return GF_NOT_SUPPORTED; + } + gf_mx2d_init(tmp); + switch (b->fieldType) { + case SVG_Transform_Translate_datatype: + gf_mx2d_add_translation(&tmp, gf_mulfix(((SVG_Point *)b->far_ptr)->x, beta), gf_mulfix(((SVG_Point *)b->far_ptr)->y, beta)); + break; + case SVG_Transform_Scale_datatype: + gf_mx2d_add_scale(&tmp, gf_mulfix(((SVG_Point *)b->far_ptr)->x, beta), gf_mulfix(((SVG_Point *)b->far_ptr)->y, beta)); + break; + case SVG_Transform_Rotate_datatype: + gf_mx2d_add_rotation(&tmp, gf_mulfix(((SVG_Point_Angle *)b->far_ptr)->x, beta), gf_mulfix(((SVG_Point_Angle *)b->far_ptr)->y, beta), gf_mulfix(((SVG_Point_Angle *)b->far_ptr)->angle, beta)); + break; + case SVG_Transform_SkewX_datatype: + gf_mx2d_add_skew_x(&tmp, gf_mulfix(*(Fixed*)b->far_ptr, beta)); + break; + case SVG_Transform_SkewY_datatype: + gf_mx2d_add_skew_y(&tmp, gf_mulfix(*(Fixed*)b->far_ptr, beta)); + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] copy of attributes %s not supported\n", a->name)); + return GF_NOT_SUPPORTED; + } + gf_mx2d_add_matrix(&tmp, &((SVG_Transform*)a->far_ptr)->mat); + gf_mx2d_copy(((SVG_Transform*)c->far_ptr)->mat, tmp); + return GF_OK; + } + + case SVG_Transform_Translate_datatype: + if (b->fieldType == SVG_Transform_Translate_datatype) { + return svg_point_muladd(alpha, (SVG_Point*)a->far_ptr, beta, (SVG_Point*)b->far_ptr, (SVG_Point*)c->far_ptr); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] matrix operations not supported\n")); + return GF_NOT_SUPPORTED; + } + + case SVG_Transform_Scale_datatype: + if (b->fieldType == SVG_Transform_Scale_datatype) { + if (alpha == FIX_ONE && beta == FIX_ONE) { + /* addition of matrices which represent scales is equivalent + to multiplication of scale coefficients, we assume this only happens if + alpha and beta are set to one */ + ((SVG_Point*)c->far_ptr)->x = gf_mulfix(((SVG_Point*)a->far_ptr)->x,((SVG_Point*)b->far_ptr)->x); + ((SVG_Point*)c->far_ptr)->y = gf_mulfix(((SVG_Point*)a->far_ptr)->y,((SVG_Point*)b->far_ptr)->y); + return GF_OK; + } else { + return svg_point_muladd(alpha, (SVG_Point*)a->far_ptr, beta, (SVG_Point*)b->far_ptr, (SVG_Point*)c->far_ptr); + } + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] matrix operations not supported\n")); + return GF_NOT_SUPPORTED; + } + + case SVG_Transform_Rotate_datatype: + if (b->fieldType == SVG_Transform_Rotate_datatype) { + return svg_point_angle_muladd(alpha, (SVG_Point_Angle*)a->far_ptr, beta, (SVG_Point_Angle*)b->far_ptr, (SVG_Point_Angle*)c->far_ptr); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] matrix operations not supported\n")); + return GF_NOT_SUPPORTED; + } + + case SVG_Transform_SkewX_datatype: + if (b->fieldType == SVG_Transform_SkewX_datatype) { + *(Fixed*)c->far_ptr = gf_mulfix(alpha, *(Fixed*)a->far_ptr) + gf_mulfix(beta, *(Fixed*)b->far_ptr); + return GF_OK; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] matrix operations not supported\n")); + return GF_NOT_SUPPORTED; + } + + case SVG_Transform_SkewY_datatype: + if (b->fieldType == SVG_Transform_SkewY_datatype) { + *(Fixed*)c->far_ptr = gf_mulfix(alpha, *(Fixed*)a->far_ptr) + gf_mulfix(beta, *(Fixed*)b->far_ptr); + return GF_OK; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] matrix operations not supported\n")); + return GF_NOT_SUPPORTED; + } + + case DOM_String_datatype: + { + u32 len; + char *res; + SVG_String *s_a = (SVG_String *)a->far_ptr; + SVG_String *s_b = (SVG_String *)b->far_ptr; + u32 len_a = strlen(*s_a); + u32 len_b = strlen(*s_b); + len_a = FIX2INT(alpha * len_a); + len_b = FIX2INT(beta * len_b); + len = len_a + len_b + 1; + res = (char*)malloc(sizeof(char) * len); + memcpy(res, *s_a, len_a); + memcpy(res+len_a, *s_b, len_b); + res[len-1] = 0; + s_a = (SVG_String*)c->far_ptr; + if (*s_a) free(*s_a); + *s_a = res; + } + break; + case LASeR_Size_datatype: + laser_size_muladd(alpha, (LASeR_Size*)a->far_ptr, beta, (LASeR_Size*)b->far_ptr, (LASeR_Size*)c->far_ptr); + break; + + /* Keyword types */ + case SVG_Boolean_datatype: + case SVG_FillRule_datatype: + case SVG_StrokeLineJoin_datatype: + case SVG_StrokeLineCap_datatype: + case SVG_FontStyle_datatype: + case SVG_FontWeight_datatype: + case SVG_FontVariant_datatype: + case SVG_TextAnchor_datatype: + case SVG_Display_datatype: + case SVG_Visibility_datatype: + case SVG_GradientUnit_datatype: + case SVG_PreserveAspectRatio_datatype: + case XML_Space_datatype: + case XMLEV_Propagate_datatype: + case XMLEV_DefaultAction_datatype: + case XMLEV_Phase_datatype: + case SMIL_SyncBehavior_datatype: + case SMIL_SyncTolerance_datatype: + case SMIL_AttributeType_datatype: + case SMIL_CalcMode_datatype: + case SMIL_Additive_datatype: + case SMIL_Accumulate_datatype: + case SMIL_Restart_datatype: + case SMIL_Fill_datatype: + case SVG_Overflow_datatype: + case SVG_ZoomAndPan_datatype: + case SVG_DisplayAlign_datatype: + case SVG_TextAlign_datatype: + case SVG_PointerEvents_datatype: + case SVG_RenderingHint_datatype: + case SVG_VectorEffect_datatype: + case SVG_PlaybackOrder_datatype: + case SVG_TimelineBegin_datatype: + case SVG_SpreadMethod_datatype: + case SVG_TransformType_datatype: + + /* Unsupported types */ + case SVG_ContentType_datatype: + case SVG_LanguageID_datatype: + case SVG_FontFamily_datatype: + case XMLRI_datatype: + case XMLRI_List_datatype: + case DOM_StringList_datatype: + case SVG_Clock_datatype: + case SVG_Focus_datatype: + case SVG_ID_datatype: + case SVG_GradientOffset_datatype: + case SMIL_KeyTimes_datatype: + case SMIL_KeyPoints_datatype: + case SMIL_KeySplines_datatype: + case SMIL_AnimateValue_datatype: + case SMIL_AnimateValues_datatype: + case SMIL_AttributeName_datatype: + case SMIL_Times_datatype: + case SMIL_Duration_datatype: + case SMIL_RepeatCount_datatype: + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_INTERACT, ("[SVG Attributes] addition for attributes %s of type %s not supported\n", a->name, gf_svg_attribute_type_to_string(a->fieldType))); + return GF_NOT_SUPPORTED; + } + return GF_OK; +} + +/* *a = *b, copy by value */ +GF_EXPORT +GF_Err gf_svg_attributes_copy(GF_FieldInfo *a, GF_FieldInfo *b, Bool clamp) +{ + if (!a->far_ptr || !b->far_ptr) return GF_BAD_PARAM; + switch (a->fieldType) { + /* Numeric types */ + case SVG_Color_datatype: + *((SVG_Color *)a->far_ptr) = *((SVG_Color *)b->far_ptr); + if (clamp) svg_color_clamp((SVG_Color *)a->far_ptr); + break; + + case SVG_Paint_datatype: + { + SVG_Paint *pa = (SVG_Paint *)a->far_ptr; + SVG_Paint *pb = (SVG_Paint *)b->far_ptr; + pa->type = pb->type; + if (pb->type == SVG_PAINT_URI) { + GF_FieldInfo tmp_a, tmp_b; + tmp_a.fieldType = tmp_b.fieldType = XMLRI_datatype; + tmp_a.far_ptr = &pa->iri; + tmp_b.far_ptr = &pb->iri; + gf_svg_attributes_copy(&tmp_a, &tmp_b, 0); + } else { + pa->color = pb->color; + } + return GF_OK; + } + break; + + case SVG_Number_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_FontSize_datatype: + *((SVG_Number *)a->far_ptr) = *((SVG_Number *)b->far_ptr); + break; + + case SVG_ViewBox_datatype: + *((SVG_ViewBox *)a->far_ptr) = *((SVG_ViewBox *)b->far_ptr); + break; + + case SVG_Points_datatype: + return svg_points_copy((GF_List**)a->far_ptr, (GF_List**)b->far_ptr); + + case SVG_Numbers_datatype: + case SVG_Coordinates_datatype: + return svg_numbers_copy((GF_List**)a->far_ptr, (GF_List**)b->far_ptr); + + case SVG_PathData_datatype: + return svg_path_copy((SVG_PathData*)a->far_ptr, (SVG_PathData*)b->far_ptr); + + case SVG_StrokeDashArray_datatype: + return svg_dasharray_copy((SVG_StrokeDashArray*)a->far_ptr, (SVG_StrokeDashArray*)b->far_ptr); + + case SVG_Motion_datatype: + gf_mx2d_copy(*(GF_Matrix2D *)a->far_ptr, *(GF_Matrix2D *)b->far_ptr); + return GF_OK; + + case SVG_Transform_datatype: + switch (b->fieldType) { + case SVG_Transform_Translate_datatype: + gf_mx2d_init(((SVG_Transform *)a->far_ptr)->mat); + gf_mx2d_add_translation(&((SVG_Transform *)a->far_ptr)->mat, ((SVG_Point*)b->far_ptr)->x, ((SVG_Point*)b->far_ptr)->y); + break; + case SVG_Transform_Scale_datatype: + gf_mx2d_init(((SVG_Transform *)a->far_ptr)->mat); + gf_mx2d_add_scale(&((SVG_Transform *)a->far_ptr)->mat, ((SVG_Point*)b->far_ptr)->x, ((SVG_Point*)b->far_ptr)->y); + break; + case SVG_Transform_Rotate_datatype: + gf_mx2d_init(((SVG_Transform *)a->far_ptr)->mat); + gf_mx2d_add_rotation(&((SVG_Transform *)a->far_ptr)->mat, ((SVG_Point_Angle*)b->far_ptr)->x, ((SVG_Point_Angle*)b->far_ptr)->y, ((SVG_Point_Angle*)b->far_ptr)->angle); + break; + case SVG_Transform_SkewX_datatype: + gf_mx2d_init(((SVG_Transform *)a->far_ptr)->mat); + gf_mx2d_add_skew_x(&((SVG_Transform *)a->far_ptr)->mat, *(Fixed *)b->far_ptr); + break; + case SVG_Transform_SkewY_datatype: + gf_mx2d_init(((SVG_Transform *)a->far_ptr)->mat); + gf_mx2d_add_skew_y(&((SVG_Transform *)a->far_ptr)->mat, *(Fixed *)b->far_ptr); + break; + case SVG_Transform_datatype: + gf_mx2d_copy(((SVG_Transform *)a->far_ptr)->mat, ((SVG_Transform *)b->far_ptr)->mat); + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[SVG Attributes] forbidden type of transform\n")); + return GF_NOT_SUPPORTED; + } + return GF_OK; + + /* Keyword types */ + case SVG_Boolean_datatype: + case SVG_FillRule_datatype: + case SVG_StrokeLineJoin_datatype: + case SVG_StrokeLineCap_datatype: + case SVG_FontStyle_datatype: + case SVG_FontWeight_datatype: + case SVG_FontVariant_datatype: + case SVG_TextAnchor_datatype: + case SVG_Display_datatype: + case SVG_Visibility_datatype: + case SVG_GradientUnit_datatype: + case SVG_PreserveAspectRatio_datatype: + case XML_Space_datatype: + case XMLEV_Propagate_datatype: + case XMLEV_DefaultAction_datatype: + case XMLEV_Phase_datatype: + case SMIL_SyncBehavior_datatype: + case SMIL_AttributeType_datatype: + case SMIL_CalcMode_datatype: + case SMIL_Additive_datatype: + case SMIL_Accumulate_datatype: + case SMIL_Restart_datatype: + case SMIL_Fill_datatype: + case SVG_Overflow_datatype: + case SVG_ZoomAndPan_datatype: + case SVG_DisplayAlign_datatype: + case SVG_TextAlign_datatype: + case SVG_PointerEvents_datatype: + case SVG_RenderingHint_datatype: + case SVG_VectorEffect_datatype: + case SVG_PlaybackOrder_datatype: + case SVG_TimelineBegin_datatype: + case SVG_TransformType_datatype: + case SVG_Focusable_datatype: + case SVG_FocusHighlight_datatype: + *(u8 *)a->far_ptr = *(u8 *)b->far_ptr; + return GF_OK; + + case SMIL_SyncTolerance_datatype: + *(SMIL_SyncTolerance*)a->far_ptr = *(SMIL_SyncTolerance*)b->far_ptr; + return GF_OK; + /* Other types */ + case SVG_ID_datatype: + case SVG_LanguageID_datatype: + case SVG_ContentType_datatype: + case DOM_String_datatype: + if (* (SVG_String *)a->far_ptr) free(* (SVG_String *)a->far_ptr); + * (SVG_String *)a->far_ptr = strdup(*(SVG_String *)b->far_ptr); + return GF_OK; + + case SVG_FontFamily_datatype: + ((SVG_FontFamily *)a->far_ptr)->type = ((SVG_FontFamily *)b->far_ptr)->type; + if ( ((SVG_FontFamily *)a->far_ptr)->value) free( ((SVG_FontFamily *)a->far_ptr)->value ); + ((SVG_FontFamily *)a->far_ptr)->value = (((SVG_FontFamily *)b->far_ptr)->value ? strdup(((SVG_FontFamily *)b->far_ptr)->value) : NULL ); + return GF_OK; + + case XMLRI_datatype: + case XML_IDREF_datatype: + ((XMLRI *)a->far_ptr)->type = ((XMLRI *)b->far_ptr)->type; + if (((XMLRI *)a->far_ptr)->string) free(((XMLRI *)a->far_ptr)->string); + if (((XMLRI *)b->far_ptr)->string) { + ((XMLRI *)a->far_ptr)->string = strdup(((XMLRI *)b->far_ptr)->string); + } else { + ((XMLRI *)a->far_ptr)->string = strdup(""); + } + ((XMLRI *)a->far_ptr)->target = ((XMLRI *)b->far_ptr)->target; + if (((XMLRI *)a->far_ptr)->type == XMLRI_ELEMENTID) { + GF_Node *n = (GF_Node *) ((XMLRI *)b->far_ptr)->target; + /*TODO Check if assigning IRI from # scenegraph can happen*/ + if (n) gf_node_register_iri(gf_node_get_graph(n), (XMLRI*)a->far_ptr); + } + return GF_OK; + + case SVG_Focus_datatype: + { + ((SVG_Focus *)a->far_ptr)->type = ((SVG_Focus *)b->far_ptr)->type; + if ( ((SVG_Focus *)b->far_ptr)->target.string) + ((SVG_Focus *)a->far_ptr)->target.string = strdup( ((SVG_Focus *)b->far_ptr)->target.string); + } + return GF_OK; + + case SMIL_Times_datatype: + { + u32 i, count; + GF_List *dst = *(GF_List **)a->far_ptr; + GF_List *src = *(GF_List **)b->far_ptr; + while (gf_list_count(dst)) { + SMIL_Time *t = gf_list_get(dst, 0); + gf_list_rem(dst, 0); + free(t); + } + count = gf_list_count(src); + for (i=0;ifar_ptr; + SMIL_AttributeName *sab = (SMIL_AttributeName *)b->far_ptr; + saa->tag = sab->tag; + saa->type = sab->type; + saa->name = sab->name ? strdup(sab->name) : NULL; + } + break; + case SMIL_Duration_datatype: + { + SMIL_Duration *da = (SMIL_Duration*)a->far_ptr; + SMIL_Duration *db = (SMIL_Duration*)b->far_ptr; + da->type = db->type; + da->clock_value = db->clock_value; + } + break; + case SMIL_AnimateValue_datatype: + { + SMIL_AnimateValue *sa = (SMIL_AnimateValue*)a->far_ptr; + SMIL_AnimateValue *sb = (SMIL_AnimateValue*)b->far_ptr; + sa->type = sb->type; + if (sb->value) { + GF_FieldInfo ava, avb; + sa->value = gf_svg_create_attribute_value(sa->type); + ava.fieldIndex = avb.fieldIndex = 0; + ava.fieldType = avb.fieldType = sb->type; + ava.far_ptr = sa->value; + avb.far_ptr = sb->value; + gf_svg_attributes_copy(&ava, &avb, 0); + } + } + break; + + /* Unsupported types */ + case XMLRI_List_datatype: + case DOM_StringList_datatype: + case SVG_GradientOffset_datatype: + case SVG_Clock_datatype: + case SMIL_KeyTimes_datatype: + case SMIL_KeyPoints_datatype: + case SMIL_KeySplines_datatype: + case SMIL_AnimateValues_datatype: + case SMIL_RepeatCount_datatype: + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_INTERACT, ("[SVG Attributes] copy of attributes %s of type %s not supported\n", a->name, gf_svg_attribute_type_to_string(a->fieldType))); + return GF_OK; + } + return GF_OK; +} + +/* c = a + b */ +GF_Err gf_svg_attributes_add(GF_FieldInfo *a, GF_FieldInfo *b, GF_FieldInfo *c, Bool clamp) +{ + return gf_svg_attributes_muladd(FIX_ONE, a, FIX_ONE, b, c, clamp); +} + +Bool gf_svg_attribute_is_interpolatable(u32 type) +{ + switch (type) { + /* additive types which can really be interpolated */ + case SVG_Color_datatype: + case SVG_Paint_datatype: + case SVG_Number_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_FontSize_datatype: + case SVG_ViewBox_datatype: + case SVG_Points_datatype: + case SVG_Numbers_datatype: + case SVG_Coordinates_datatype: + case SVG_PathData_datatype: + case SVG_Motion_datatype: + case SVG_Transform_datatype: + case SVG_Transform_Translate_datatype: + case SVG_Transform_Scale_datatype: + case SVG_Transform_Rotate_datatype: + case SVG_Transform_SkewX_datatype: + case SVG_Transform_SkewY_datatype: + case LASeR_Size_datatype: + return 1; + } + return 0; +} + +GF_Err gf_svg_attributes_interpolate(GF_FieldInfo *a, GF_FieldInfo *b, GF_FieldInfo *c, Fixed coef, Bool clamp) +{ + if (!a->far_ptr || !b->far_ptr || !c->far_ptr) return GF_BAD_PARAM; + + c->fieldType = a->fieldType; + + switch (a->fieldType) { + + /* additive types which can really be interpolated */ + case SVG_Color_datatype: + case SVG_Paint_datatype: + case SVG_Number_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_FontSize_datatype: + case SVG_ViewBox_datatype: + case SVG_Points_datatype: + case SVG_Numbers_datatype: + case SVG_Coordinates_datatype: + case SVG_PathData_datatype: + case SVG_Motion_datatype: + case SVG_Transform_datatype: + case SVG_Transform_Translate_datatype: + case SVG_Transform_Scale_datatype: + case SVG_Transform_Rotate_datatype: + case SVG_Transform_SkewX_datatype: + case SVG_Transform_SkewY_datatype: + case LASeR_Size_datatype: + return gf_svg_attributes_muladd(FIX_ONE-coef, a, coef, b, c, clamp); + + /* discrete types: interpolation is the selection of one of the 2 values + using the coeff and a the 0.5 threshold */ + /* Keyword types */ + case SVG_Boolean_datatype: + case SVG_FillRule_datatype: + case SVG_StrokeLineJoin_datatype: + case SVG_StrokeLineCap_datatype: + case SVG_FontStyle_datatype: + case SVG_FontWeight_datatype: + case SVG_FontVariant_datatype: + case SVG_TextAnchor_datatype: + case SVG_Display_datatype: + case SVG_Visibility_datatype: + case SVG_GradientUnit_datatype: + case SVG_PreserveAspectRatio_datatype: + case SVG_TransformType_datatype: + case XML_Space_datatype: + case XMLEV_Propagate_datatype: + case XMLEV_DefaultAction_datatype: + case XMLEV_Phase_datatype: + case SMIL_SyncBehavior_datatype: + case SMIL_SyncTolerance_datatype: + case SMIL_AttributeType_datatype: + case SMIL_CalcMode_datatype: + case SMIL_Additive_datatype: + case SMIL_Accumulate_datatype: + case SMIL_Restart_datatype: + case SMIL_Fill_datatype: + case SVG_Overflow_datatype: + case SVG_ZoomAndPan_datatype: + case SVG_DisplayAlign_datatype: + case SVG_TextAlign_datatype: + case SVG_PointerEvents_datatype: + case SVG_RenderingHint_datatype: + case SVG_VectorEffect_datatype: + case SVG_PlaybackOrder_datatype: + case SVG_TimelineBegin_datatype: + + /* Other non keyword types but which can still be discretely interpolated */ + case DOM_String_datatype: + case SVG_ContentType_datatype: + case SVG_LanguageID_datatype: + case SVG_FontFamily_datatype: + case XMLRI_datatype: + case XMLRI_List_datatype: + case DOM_StringList_datatype: + case SVG_Clock_datatype: + case SVG_ID_datatype: + case SVG_GradientOffset_datatype: + case LASeR_Choice_datatype: + if (coef < FIX_ONE/2) { + gf_svg_attributes_copy(c, a, clamp); + } else { + gf_svg_attributes_copy(c, b, clamp); + } + return GF_OK; + + /* Unsupported types */ + case SMIL_KeyTimes_datatype: + case SMIL_KeyPoints_datatype: + case SMIL_KeySplines_datatype: + case SMIL_AnimateValue_datatype: + case SMIL_AnimateValues_datatype: + case SMIL_AttributeName_datatype: + case SMIL_Times_datatype: + case SMIL_Duration_datatype: + case SMIL_RepeatCount_datatype: + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_INTERACT, ("[SVG Attributes] interpolation for attributes %s of type %s not supported\n", a->name, gf_svg_attribute_type_to_string(a->fieldType))); + return GF_OK; + } + return GF_OK; + +} + +Bool gf_svg_is_current_color(GF_FieldInfo *a) +{ + switch (a->fieldType) { + case SVG_Color_datatype: + return (((SVG_Color *)a->far_ptr)->type == SVG_COLOR_CURRENTCOLOR); + break; + case SVG_Paint_datatype: + if ( ((SVG_Paint *)a->far_ptr)->type == SVG_PAINT_COLOR) { + return (((SVG_Paint *)a->far_ptr)->color.type == SVG_COLOR_CURRENTCOLOR); + } else { + return 0; + } + break; + } + return 0; +} + + + +char *gf_svg_attribute_type_to_string(u32 att_type) +{ + switch (att_type) { + case SVG_FillRule_datatype: return "FillRule"; + case SVG_StrokeLineJoin_datatype: return "StrokeLineJoin"; + case SVG_StrokeLineCap_datatype: return "StrokeLineCap"; + case SVG_FontStyle_datatype: return "FontStyle"; + case SVG_FontWeight_datatype: return "FontWeight"; + case SVG_FontVariant_datatype: return "FontVariant"; + case SVG_TextAnchor_datatype: return "TextAnchor"; + case SVG_TransformType_datatype: return "TransformType"; + case SVG_Display_datatype: return "Display"; + case SVG_Visibility_datatype: return "Visibility"; + case SVG_Overflow_datatype: return "Overflow"; + case SVG_ZoomAndPan_datatype: return "ZoomAndPan"; + case SVG_DisplayAlign_datatype: return "DisplayAlign"; + case SVG_PointerEvents_datatype: return "PointerEvents"; + case SVG_RenderingHint_datatype: return "RenderingHint"; + case SVG_VectorEffect_datatype: return "VectorEffect"; + case SVG_PlaybackOrder_datatype: return "PlaybackOrder"; + case SVG_TimelineBegin_datatype: return "TimelineBegin"; + case XML_Space_datatype: return "XML_Space"; + case XMLEV_Propagate_datatype: return "XMLEV_Propagate"; + case XMLEV_DefaultAction_datatype: return "XMLEV_DefaultAction"; + case XMLEV_Phase_datatype: return "XMLEV_Phase"; + case SMIL_SyncBehavior_datatype: return "SMIL_SyncBehavior"; + case SMIL_SyncTolerance_datatype: return "SMIL_SyncTolerance"; + case SMIL_AttributeType_datatype: return "SMIL_AttributeType"; + case SMIL_CalcMode_datatype: return "SMIL_CalcMode"; + case SMIL_Additive_datatype: return "SMIL_Additive"; + case SMIL_Accumulate_datatype: return "SMIL_Accumulate"; + case SMIL_Restart_datatype: return "SMIL_Restart"; + case SMIL_Fill_datatype: return "SMIL_Fill"; + case SVG_GradientUnit_datatype: return "GradientUnit"; + case SVG_InitialVisibility_datatype:return "InitialVisibility"; + case SVG_FocusHighlight_datatype: return "FocusHighlight"; + case SVG_Overlay_datatype: return "Overlay"; + case SVG_TransformBehavior_datatype:return "TransformBehavior"; + case SVG_SpreadMethod_datatype: return "SpreadMethod"; + case SVG_TextAlign_datatype: return "TextAlign"; + case SVG_Number_datatype: return "Number"; + case SVG_FontSize_datatype: return "FontSize"; + case SVG_Length_datatype: return "Length"; + case SVG_Coordinate_datatype: return "Coordinate"; + case SVG_Rotate_datatype: return "Rotate"; + case SVG_Numbers_datatype: return "Numbers"; + case SVG_Points_datatype: return "Points"; + case SVG_Coordinates_datatype: return "Coordinates"; + case DOM_StringList_datatype: return "StringList"; + case XMLRI_List_datatype: return "ListOfIRI"; + case SMIL_KeyTimes_datatype: return "SMIL_KeyTimes"; + case SMIL_KeySplines_datatype: return "SMIL_KeySplines"; + case SMIL_KeyPoints_datatype: return "SMIL_KeyPoints"; + case SMIL_Times_datatype: return "SMIL_Times"; + case SMIL_AnimateValue_datatype: return "SMIL_AnimateValue"; + case SMIL_AnimateValues_datatype: return "SMIL_AnimateValues"; + case SMIL_Duration_datatype: return "SMIL_Duration"; + case SMIL_RepeatCount_datatype: return "SMIL_RepeatCount"; + case SMIL_AttributeName_datatype: return "SMIL_AttributeName"; + case SVG_Boolean_datatype: return "Boolean"; + case SVG_Color_datatype: return "Color"; + case SVG_Paint_datatype: return "Paint"; + case SVG_PathData_datatype: return "PathData"; + case SVG_FontFamily_datatype: return "FontFamily"; + case SVG_ID_datatype: return "ID"; + case XMLRI_datatype: return "IRI"; + case XML_IDREF_datatype: return "IDREF"; + case SVG_StrokeDashArray_datatype: return "StrokeDashArray"; + case SVG_PreserveAspectRatio_datatype:return "PreserveAspectRatio"; + case SVG_ViewBox_datatype: return "ViewBox"; + case SVG_GradientOffset_datatype: return "GradientOffset"; + case SVG_Focus_datatype : return "Focus"; + case SVG_Clock_datatype : return "Clock"; + case DOM_String_datatype : return "String"; + case SVG_ContentType_datatype: return "ContentType"; + case SVG_LanguageID_datatype: return "LanguageID"; + case XMLEV_Event_datatype: return "XMLEV_Event"; + case SVG_Motion_datatype: return "Motion"; + case SVG_Transform_datatype: return "Transform"; + case SVG_Transform_Translate_datatype:return "Translate"; + case SVG_Transform_Scale_datatype: return "Scale"; + case SVG_Transform_SkewX_datatype: return "SkewX"; + case SVG_Transform_SkewY_datatype: return "SkewY"; + case SVG_Transform_Rotate_datatype: return "Rotate"; + case LASeR_Choice_datatype: return "LASeR_Choice"; + case LASeR_Size_datatype: return "LASeR_Size"; + default: return "UnknownType"; + } +} + + +#endif /*GPAC_DISABLE_SVG*/ diff --git a/src/scenegraph/svg_properties.c b/src/scenegraph/svg_properties.c new file mode 100644 index 0000000..d9592fb --- /dev/null +++ b/src/scenegraph/svg_properties.c @@ -0,0 +1,971 @@ +/* + * GPAC Multimedia Framework + * + * Authors: Cyril Concolato - Jean le Feuvre - Jean-Claude Moissinac + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / SVG Loader module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_SVG + +#include +#include +/* + Initialization of properties at the top level before any rendering + The value shall not use the 'inherit' value, it uses the initial value. + The property values are then updated when going down the tree using svg_properties_apply +*/ +GF_EXPORT +void gf_svg_properties_init_pointers(SVGPropertiesPointers *svg_props) +{ + if (!svg_props) return; + + GF_SAFEALLOC(svg_props->audio_level, SVG_Number); + svg_props->audio_level->type = SVG_NUMBER_VALUE; + svg_props->audio_level->value = FIX_ONE; + svg_props->computed_audio_level = FIX_ONE; + + GF_SAFEALLOC(svg_props->color, SVG_Paint); + svg_props->color->type = SVG_PAINT_COLOR; + svg_props->color->color.type = SVG_COLOR_RGBCOLOR; + /* svg_props->color->red, green, blue set to zero, so initial value for color property is black */ + + GF_SAFEALLOC(svg_props->color_rendering, SVG_RenderingHint); + *svg_props->color_rendering = SVG_RENDERINGHINT_AUTO; + + GF_SAFEALLOC(svg_props->display, SVG_Display); + *svg_props->display = SVG_DISPLAY_INLINE; + + GF_SAFEALLOC(svg_props->display_align, SVG_DisplayAlign); + *svg_props->display_align = SVG_DISPLAYALIGN_AUTO; + + GF_SAFEALLOC(svg_props->fill, SVG_Paint); + svg_props->fill->type = SVG_PAINT_COLOR; + svg_props->fill->color.type = SVG_COLOR_RGBCOLOR; + /* svg_props->fill->color.red, green, blue set to zero, so initial value for fill color is black */ + + GF_SAFEALLOC(svg_props->fill_opacity, SVG_Number); + svg_props->fill_opacity->type = SVG_NUMBER_VALUE; + svg_props->fill_opacity->value = FIX_ONE; + + GF_SAFEALLOC(svg_props->fill_rule, SVG_FillRule); + *svg_props->fill_rule = SVG_FILLRULE_NONZERO; + + GF_SAFEALLOC(svg_props->font_family, SVG_FontFamily); + svg_props->font_family->type = SVG_FONTFAMILY_VALUE; + svg_props->font_family->value = strdup("Arial"); + + GF_SAFEALLOC(svg_props->font_size, SVG_FontSize); + svg_props->font_size->type = SVG_NUMBER_VALUE; + svg_props->font_size->value = 12*FIX_ONE; + + GF_SAFEALLOC(svg_props->font_style, SVG_FontStyle); + *svg_props->font_style = SVG_FONTSTYLE_NORMAL; + + GF_SAFEALLOC(svg_props->font_variant, SVG_FontVariant); + *svg_props->font_variant = SVG_FONTVARIANT_NORMAL; + + GF_SAFEALLOC(svg_props->font_weight, SVG_FontWeight); + *svg_props->font_weight = SVG_FONTWEIGHT_NORMAL; + + GF_SAFEALLOC(svg_props->image_rendering, SVG_RenderingHint); + *svg_props->image_rendering = SVG_RENDERINGHINT_AUTO; + + GF_SAFEALLOC(svg_props->line_increment, SVG_Number); + svg_props->line_increment->type = SVG_NUMBER_AUTO; + + GF_SAFEALLOC(svg_props->opacity, SVG_Number); + svg_props->opacity->type = SVG_NUMBER_VALUE; + svg_props->opacity->value = FIX_ONE; + + GF_SAFEALLOC(svg_props->pointer_events, SVG_PointerEvents); + *svg_props->pointer_events = SVG_POINTEREVENTS_VISIBLEPAINTED; + + GF_SAFEALLOC(svg_props->shape_rendering, SVG_RenderingHint); + *svg_props->shape_rendering = SVG_RENDERINGHINT_AUTO; + + GF_SAFEALLOC(svg_props->solid_color, SVG_Paint); + svg_props->solid_color->type = SVG_PAINT_COLOR; + svg_props->solid_color->color.type = SVG_COLOR_RGBCOLOR; + /* svg_props->solid_color->color.red, green, blue set to zero, so initial value for solid_color is black */ + + GF_SAFEALLOC(svg_props->solid_opacity, SVG_Number); + svg_props->solid_opacity->type = SVG_NUMBER_VALUE; + svg_props->solid_opacity->value = FIX_ONE; + + GF_SAFEALLOC(svg_props->stop_color, SVG_Paint); + svg_props->stop_color->type = SVG_PAINT_COLOR; + svg_props->stop_color->color.type = SVG_COLOR_RGBCOLOR; + /* svg_props->stop_color->color.red, green, blue set to zero, so initial value for stop_color is black */ + + GF_SAFEALLOC(svg_props->stop_opacity, SVG_Number); + svg_props->stop_opacity->type = SVG_NUMBER_VALUE; + svg_props->stop_opacity->value = FIX_ONE; + + GF_SAFEALLOC(svg_props->stroke, SVG_Paint); + svg_props->stroke->type = SVG_PAINT_NONE; + svg_props->stroke->color.type = SVG_COLOR_RGBCOLOR; + /* svg_props->stroke->color.red, green, blue set to zero, so initial value for stroke color is black */ + + GF_SAFEALLOC(svg_props->stroke_dasharray, SVG_StrokeDashArray); + svg_props->stroke_dasharray->type = SVG_STROKEDASHARRAY_NONE; + + GF_SAFEALLOC(svg_props->stroke_dashoffset , SVG_Length); + svg_props->stroke_dashoffset->type = SVG_NUMBER_VALUE; + svg_props->stroke_dashoffset->value = 0; + + GF_SAFEALLOC(svg_props->stroke_linecap, SVG_StrokeLineCap); + *svg_props->stroke_linecap = SVG_STROKELINECAP_BUTT; + + GF_SAFEALLOC(svg_props->stroke_linejoin, SVG_StrokeLineJoin); + *svg_props->stroke_linejoin = SVG_STROKELINEJOIN_MITER; + + GF_SAFEALLOC(svg_props->stroke_miterlimit, SVG_Number); + svg_props->stroke_miterlimit->type = SVG_NUMBER_VALUE; + svg_props->stroke_miterlimit->value = 4*FIX_ONE; + + GF_SAFEALLOC(svg_props->stroke_opacity, SVG_Number); + svg_props->stroke_opacity->type = SVG_NUMBER_VALUE; + svg_props->stroke_opacity->value = FIX_ONE; + + GF_SAFEALLOC(svg_props->stroke_width, SVG_Length); + svg_props->stroke_width->type = SVG_NUMBER_VALUE; + svg_props->stroke_width->value = FIX_ONE; + + GF_SAFEALLOC(svg_props->text_align, SVG_TextAlign); + *svg_props->text_align = SVG_TEXTALIGN_START; + + GF_SAFEALLOC(svg_props->text_anchor, SVG_TextAnchor); + *svg_props->text_anchor = SVG_TEXTANCHOR_START; + + GF_SAFEALLOC(svg_props->text_rendering, SVG_RenderingHint); + *svg_props->text_rendering = SVG_RENDERINGHINT_AUTO; + + GF_SAFEALLOC(svg_props->vector_effect, SVG_VectorEffect); + *svg_props->vector_effect = SVG_VECTOREFFECT_NONE; + + GF_SAFEALLOC(svg_props->viewport_fill, SVG_Paint); + svg_props->viewport_fill->type = SVG_PAINT_NONE; + + GF_SAFEALLOC(svg_props->viewport_fill_opacity, SVG_Number); + svg_props->viewport_fill_opacity->type = SVG_NUMBER_VALUE; + svg_props->viewport_fill_opacity->value = FIX_ONE; + + GF_SAFEALLOC(svg_props->visibility, SVG_Visibility); + *svg_props->visibility = SVG_VISIBILITY_VISIBLE; + +} + +GF_EXPORT +void gf_svg_properties_reset_pointers(SVGPropertiesPointers *svg_props) +{ + if (!svg_props) return; + if(svg_props->audio_level) free(svg_props->audio_level); + gf_svg_delete_paint(NULL, svg_props->color); + if(svg_props->color_rendering) free(svg_props->color_rendering); + if(svg_props->display) free(svg_props->display); + if(svg_props->display_align) free(svg_props->display_align); + gf_svg_delete_paint(NULL, svg_props->fill); + if(svg_props->fill_opacity) free(svg_props->fill_opacity); + if(svg_props->fill_rule) free(svg_props->fill_rule); + if(svg_props->font_family) { + if (svg_props->font_family->value) free(svg_props->font_family->value); + free(svg_props->font_family); + } + if(svg_props->font_size) free(svg_props->font_size); + if(svg_props->font_style) free(svg_props->font_style); + if(svg_props->font_variant) free(svg_props->font_variant); + if(svg_props->font_weight) free(svg_props->font_weight); + if(svg_props->image_rendering) free(svg_props->image_rendering); + if(svg_props->line_increment) free(svg_props->line_increment); + if(svg_props->opacity) free(svg_props->opacity); + if(svg_props->pointer_events) free(svg_props->pointer_events); + if(svg_props->shape_rendering) free(svg_props->shape_rendering); + gf_svg_delete_paint(NULL, svg_props->solid_color); + if(svg_props->solid_opacity) free(svg_props->solid_opacity); + gf_svg_delete_paint(NULL, svg_props->stop_color); + if(svg_props->stop_opacity) free(svg_props->stop_opacity); + gf_svg_delete_paint(NULL, svg_props->stroke); + if(svg_props->stroke_dasharray) { + if (svg_props->stroke_dasharray->array.count) free(svg_props->stroke_dasharray->array.vals); + free(svg_props->stroke_dasharray); + } + if(svg_props->stroke_dashoffset) free(svg_props->stroke_dashoffset); + if(svg_props->stroke_linecap) free(svg_props->stroke_linecap); + if(svg_props->stroke_linejoin) free(svg_props->stroke_linejoin); + if(svg_props->stroke_miterlimit) free(svg_props->stroke_miterlimit); + if(svg_props->stroke_opacity) free(svg_props->stroke_opacity); + if(svg_props->stroke_width) free(svg_props->stroke_width); + if(svg_props->text_align) free(svg_props->text_align); + if(svg_props->text_anchor) free(svg_props->text_anchor); + if(svg_props->text_rendering) free(svg_props->text_rendering); + if(svg_props->vector_effect) free(svg_props->vector_effect); + gf_svg_delete_paint(NULL, svg_props->viewport_fill); + if(svg_props->viewport_fill_opacity) free(svg_props->viewport_fill_opacity); + if(svg_props->visibility) free(svg_props->visibility); + memset(svg_props, 0, sizeof(SVGPropertiesPointers)); +} + + +void *gf_svg_get_property_pointer_from_tag(SVGPropertiesPointers *output_property_context, u32 prop_tag) +{ + switch (prop_tag) { + case TAG_SVG_ATT_audio_level: return output_property_context->audio_level; + case TAG_SVG_ATT_color: return output_property_context->color; + case TAG_SVG_ATT_color_rendering: return output_property_context->color_rendering; + case TAG_SVG_ATT_display: return output_property_context->display; + case TAG_SVG_ATT_display_align: return output_property_context->display_align; + case TAG_SVG_ATT_fill: return output_property_context->fill; + case TAG_SVG_ATT_fill_opacity: return output_property_context->fill_opacity; + case TAG_SVG_ATT_fill_rule: return output_property_context->fill_rule; + case TAG_SVG_ATT_font_family: return output_property_context->font_family; + case TAG_SVG_ATT_font_size: return output_property_context->font_size; + case TAG_SVG_ATT_font_style: return output_property_context->font_style; + case TAG_SVG_ATT_font_variant: return output_property_context->font_variant; + case TAG_SVG_ATT_font_weight: return output_property_context->font_weight; + case TAG_SVG_ATT_image_rendering: return output_property_context->image_rendering; + case TAG_SVG_ATT_line_increment: return output_property_context->line_increment; + case TAG_SVG_ATT_opacity: return output_property_context->opacity; + case TAG_SVG_ATT_pointer_events: return output_property_context->pointer_events; + case TAG_SVG_ATT_shape_rendering: return output_property_context->shape_rendering; + case TAG_SVG_ATT_solid_color: return output_property_context->solid_color; + case TAG_SVG_ATT_solid_opacity: return output_property_context->solid_opacity; + case TAG_SVG_ATT_stop_color: return output_property_context->stop_color; + case TAG_SVG_ATT_stop_opacity: return output_property_context->stop_opacity; + case TAG_SVG_ATT_stroke: return output_property_context->stroke; + case TAG_SVG_ATT_stroke_dasharray: return output_property_context->stroke_dasharray; + case TAG_SVG_ATT_stroke_dashoffset: return output_property_context->stroke_dashoffset; + case TAG_SVG_ATT_stroke_linecap: return output_property_context->stroke_linecap; + case TAG_SVG_ATT_stroke_linejoin: return output_property_context->stroke_linejoin; + case TAG_SVG_ATT_stroke_miterlimit: return output_property_context->stroke_miterlimit; + case TAG_SVG_ATT_stroke_opacity: return output_property_context->stroke_opacity; + case TAG_SVG_ATT_stroke_width: return output_property_context->stroke_width; + case TAG_SVG_ATT_text_align: return output_property_context->text_align; + case TAG_SVG_ATT_text_anchor: return output_property_context->text_anchor; + case TAG_SVG_ATT_text_rendering: return output_property_context->text_rendering; + case TAG_SVG_ATT_vector_effect: return output_property_context->vector_effect; + case TAG_SVG_ATT_viewport_fill: return output_property_context->viewport_fill; + case TAG_SVG_ATT_viewport_fill_opacity: return output_property_context->viewport_fill_opacity; + case TAG_SVG_ATT_visibility: return output_property_context->visibility; + default:return NULL; + } +} + +void *gf_svg_get_property_pointer(SVG_Element *elt, void *input_attribute, + SVGPropertiesPointers *output_property_context) +{ + SVGAttribute *att = elt->attributes; + while (att) { + if (att->data == input_attribute) break; + att = att->next; + } + if (!att) return NULL; + return gf_svg_get_property_pointer_from_tag(output_property_context, att->tag); +} + +Bool gf_svg_is_property(GF_Node *node, GF_FieldInfo *target_attribute) +{ + u32 tag = gf_node_get_tag(node); + + if (tag > GF_NODE_RANGE_LAST_VRML) { + SVG_Element *e = (SVG_Element *)node; + SVGAttribute *att = e->attributes; + while (att) { + if (att->data == target_attribute->far_ptr) break; + att = att->next; + } + if (!att) return 0; + switch (att->tag) { + case TAG_SVG_ATT_audio_level: + case TAG_SVG_ATT_color: + case TAG_SVG_ATT_color_rendering: + case TAG_SVG_ATT_display: + case TAG_SVG_ATT_display_align: + case TAG_SVG_ATT_fill: + case TAG_SVG_ATT_fill_opacity: + case TAG_SVG_ATT_fill_rule: + case TAG_SVG_ATT_font_family: + case TAG_SVG_ATT_font_size: + case TAG_SVG_ATT_font_style: + case TAG_SVG_ATT_font_variant: + case TAG_SVG_ATT_font_weight: + case TAG_SVG_ATT_image_rendering: + case TAG_SVG_ATT_line_increment: + case TAG_SVG_ATT_opacity: + case TAG_SVG_ATT_pointer_events: + case TAG_SVG_ATT_shape_rendering: + case TAG_SVG_ATT_solid_color: + case TAG_SVG_ATT_solid_opacity: + case TAG_SVG_ATT_stop_color: + case TAG_SVG_ATT_stop_opacity: + case TAG_SVG_ATT_stroke: + case TAG_SVG_ATT_stroke_dasharray: + case TAG_SVG_ATT_stroke_dashoffset: + case TAG_SVG_ATT_stroke_linecap: + case TAG_SVG_ATT_stroke_linejoin: + case TAG_SVG_ATT_stroke_miterlimit: + case TAG_SVG_ATT_stroke_opacity: + case TAG_SVG_ATT_stroke_width: + case TAG_SVG_ATT_text_align: + case TAG_SVG_ATT_text_anchor: + case TAG_SVG_ATT_text_rendering: + case TAG_SVG_ATT_vector_effect: + case TAG_SVG_ATT_viewport_fill: + case TAG_SVG_ATT_viewport_fill_opacity: + case TAG_SVG_ATT_visibility: + return 1; + default: + return 0; + } + } + else { + return 0; + } +} + +/* TODO: Check that all possibly inherited types are treated */ +Bool gf_svg_is_inherit(GF_FieldInfo *a) +{ + if (!a->far_ptr) return 1; + + switch (a->fieldType) { + case SVG_Color_datatype: + return (((SVG_Color *)a->far_ptr)->type == SVG_COLOR_INHERIT); + break; + case SVG_Paint_datatype: + return (((SVG_Paint *)a->far_ptr)->type == SVG_PAINT_INHERIT); + break; + case SVG_FontSize_datatype: + case SVG_Number_datatype: + return (((SVG_Number *)a->far_ptr)->type == SVG_NUMBER_INHERIT); + break; + case SVG_RenderingHint_datatype: + return (*((SVG_RenderingHint *)a->far_ptr) == SVG_RENDERINGHINT_INHERIT); + break; + case SVG_Display_datatype: + return (*((SVG_Display *)a->far_ptr) == SVG_DISPLAY_INHERIT); + break; + case SVG_DisplayAlign_datatype: + return (*((SVG_DisplayAlign *)a->far_ptr) == SVG_DISPLAYALIGN_INHERIT); + break; + case SVG_TextAlign_datatype: + return (*((SVG_TextAlign *)a->far_ptr) == SVG_TEXTALIGN_INHERIT); + break; + case SVG_FillRule_datatype: + return (*((SVG_FillRule *)a->far_ptr) == SVG_FILLRULE_INHERIT); + break; + case SVG_FontFamily_datatype: + return (((SVG_FontFamily *)a->far_ptr)->type == SVG_FONTFAMILY_INHERIT); + break; + case SVG_FontStyle_datatype: + return (*((SVG_FontStyle *)a->far_ptr) == SVG_FONTSTYLE_INHERIT); + break; + case SVG_FontWeight_datatype: + return (*((SVG_FontWeight *)a->far_ptr) == SVG_FONTWEIGHT_INHERIT); + break; + case SVG_PointerEvents_datatype: + return (*((SVG_PointerEvents *)a->far_ptr) == SVG_POINTEREVENTS_INHERIT); + break; + case SVG_StrokeDashArray_datatype: + return (((SVG_StrokeDashArray *)a->far_ptr)->type == SVG_STROKEDASHARRAY_INHERIT); + break; + case SVG_StrokeLineCap_datatype: + return (*((SVG_StrokeLineCap *)a->far_ptr) == SVG_STROKELINECAP_INHERIT); + break; + case SVG_StrokeLineJoin_datatype: + return (*((SVG_StrokeLineJoin *)a->far_ptr) == SVG_STROKELINEJOIN_INHERIT); + break; + case SVG_TextAnchor_datatype: + return (*((SVG_TextAnchor *)a->far_ptr) == SVG_TEXTANCHOR_INHERIT); + break; + case SVG_VectorEffect_datatype: + return (*((SVG_VectorEffect *)a->far_ptr) == SVG_VECTOREFFECT_INHERIT); + break; + case SVG_Visibility_datatype: + return (*((SVG_Visibility *)a->far_ptr) == SVG_VISIBILITY_INHERIT); + break; + case SVG_Overflow_datatype: + return (*((SVG_Overflow *)a->far_ptr) == SVG_OVERFLOW_INHERIT); + break; + default: + return 0; + } +} + + +u32 gf_svg_get_modification_flags(SVG_Element *n, GF_FieldInfo *info) +{ +// return 0xFFFFFFFF; + switch (info->fieldType) { + case SVG_Paint_datatype: + if (info->fieldIndex == TAG_SVG_ATT_fill) return GF_SG_SVG_FILL_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_stroke) return GF_SG_SVG_STROKE_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_solid_color) return GF_SG_SVG_SOLIDCOLOR_OR_OPACITY_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_stop_color) return GF_SG_SVG_STOPCOLOR_OR_OPACITY_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_color) return GF_SG_SVG_COLOR_DIRTY; + break; + case SVG_Number_datatype: + if (info->fieldIndex == TAG_SVG_ATT_opacity) return GF_SG_SVG_OPACITY_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_fill_opacity) return GF_SG_SVG_FILLOPACITY_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_stroke_opacity) return GF_SG_SVG_STROKEOPACITY_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_solid_opacity) return GF_SG_SVG_SOLIDCOLOR_OR_OPACITY_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_stop_opacity) return GF_SG_SVG_STOPCOLOR_OR_OPACITY_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_line_increment) return GF_SG_SVG_LINEINCREMENT_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_stroke_miterlimit) return GF_SG_SVG_STROKEMITERLIMIT_DIRTY; + break; + case SVG_Length_datatype: + if (info->fieldIndex == TAG_SVG_ATT_stroke_dashoffset) return GF_SG_SVG_STROKEDASHOFFSET_DIRTY; + if (info->fieldIndex == TAG_SVG_ATT_stroke_width) return GF_SG_SVG_STROKEWIDTH_DIRTY; + break; + case SVG_DisplayAlign_datatype: + return GF_SG_SVG_DISPLAYALIGN_DIRTY; + case SVG_FillRule_datatype: + return GF_SG_SVG_FILLRULE_DIRTY; + case SVG_FontFamily_datatype: + return GF_SG_SVG_FONTFAMILY_DIRTY; + case SVG_FontSize_datatype: + return GF_SG_SVG_FONTSIZE_DIRTY; + case SVG_FontStyle_datatype: + return GF_SG_SVG_FONTSTYLE_DIRTY; + case SVG_FontVariant_datatype: + return GF_SG_SVG_FONTVARIANT_DIRTY; + case SVG_FontWeight_datatype: + return GF_SG_SVG_FONTWEIGHT_DIRTY; + case SVG_StrokeDashArray_datatype: + return GF_SG_SVG_STROKEDASHARRAY_DIRTY; + case SVG_StrokeLineCap_datatype: + return GF_SG_SVG_STROKELINECAP_DIRTY; + case SVG_StrokeLineJoin_datatype: + return GF_SG_SVG_STROKELINEJOIN_DIRTY; + case SVG_TextAlign_datatype: + return GF_SG_SVG_TEXTPOSITION_DIRTY; + case SVG_TextAnchor_datatype: + return GF_SG_SVG_TEXTPOSITION_DIRTY; + case SVG_Display_datatype: + return GF_SG_SVG_DISPLAY_DIRTY; + case SVG_VectorEffect_datatype: + return GF_SG_SVG_VECTOREFFECT_DIRTY; + } + + /* this is not a property but a regular attribute, the animatable attributes are at the moment: + focusable, focusHighlight, gradientUnits, nav-*, target, xlink:href, xlink:type, + + + the following affect the geometry of the element (some affect the positioning): + cx, cy, d, height, offset, pathLength, points, r, rx, ry, width, x, x1, x2, y, y1, y2, rotate + + the following affect the positioning and are computed at each frame: + transform, motion + */ + switch (info->fieldType) { + case SVG_Number_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_Numbers_datatype: + case SVG_Points_datatype: + case SVG_Coordinates_datatype: + case SVG_PathData_datatype: + case SVG_Rotate_datatype: + return GF_SG_SVG_GEOMETRY_DIRTY; + + case XMLRI_datatype: + return GF_SG_SVG_XLINK_HREF_DIRTY; + /*for viewbox & PAR, use node dirty to force recomputing of the viewbox*/ + case SVG_PreserveAspectRatio_datatype: + case SVG_ViewBox_datatype: + return GF_SG_NODE_DIRTY; + + //case SVG_Matrix_datatype: + //case SVG_Motion_datatype: + + default: + return 0; + } +} + +/* NOTE: Some properties (audio-level, display, opacity, solid*, stop*, vector-effect, viewport*) + are inherited only when they are specified with the value 'inherit' + otherwise they default to their initial value + which for the function below means NULL, the compositor will take care of the rest + */ +GF_EXPORT +u32 gf_svg_apply_inheritance(SVGAllAttributes *all_atts, SVGPropertiesPointers *render_svg_props) +{ + u32 inherited_flags_mask = GF_SG_NODE_DIRTY | GF_SG_CHILD_DIRTY; + if(!all_atts || !render_svg_props) return ~inherited_flags_mask; + + if (!all_atts->audio_level) { + render_svg_props->audio_level = NULL; + } else { + Fixed par_val = render_svg_props->computed_audio_level; + Fixed val = par_val; + if (all_atts->audio_level->type != SVG_NUMBER_INHERIT) { + render_svg_props->audio_level = all_atts->audio_level; + val = all_atts->audio_level->value; + } else if (render_svg_props->audio_level) { + val = render_svg_props->audio_level->value; + } else { + val = FIX_ONE; + } + par_val = MIN(FIX_ONE, MAX( par_val, 0)); + val = MIN(FIX_ONE, MAX( val, 0)); + render_svg_props->computed_audio_level = gf_mulfix(val, par_val); + } + + if (all_atts->color && all_atts->color->type == SVG_PAINT_COLOR + && all_atts->color->color.type != SVG_COLOR_INHERIT) { + render_svg_props->color = all_atts->color; + } else { + inherited_flags_mask |= GF_SG_SVG_COLOR_DIRTY; + } + if (all_atts->color_rendering && *(all_atts->color_rendering) != SVG_RENDERINGHINT_INHERIT) { + render_svg_props->color_rendering = all_atts->color_rendering; + } + if (all_atts->display && *(all_atts->display) != SVG_DISPLAY_INHERIT) { + render_svg_props->display = all_atts->display; + } else if (!all_atts->display) { + render_svg_props->display = NULL; + } else { + inherited_flags_mask |= GF_SG_SVG_DISPLAY_DIRTY; + } + + if (all_atts->display_align && *(all_atts->display_align) != SVG_DISPLAYALIGN_INHERIT) { + render_svg_props->display_align = all_atts->display_align; + } else { + inherited_flags_mask |= GF_SG_SVG_DISPLAYALIGN_DIRTY; + } + if (all_atts->fill && all_atts->fill->type != SVG_PAINT_INHERIT) { + render_svg_props->fill = all_atts->fill; + if (all_atts->fill->type == SVG_PAINT_COLOR && + all_atts->fill->color.type == SVG_COLOR_CURRENTCOLOR) { + render_svg_props->fill = render_svg_props->color; + if (inherited_flags_mask & GF_SG_SVG_COLOR_DIRTY) { + inherited_flags_mask |= GF_SG_SVG_FILL_DIRTY; + } + } + } else { + inherited_flags_mask |= GF_SG_SVG_FILL_DIRTY; + } + if (all_atts->fill_opacity && all_atts->fill_opacity->type != SVG_NUMBER_INHERIT) { + render_svg_props->fill_opacity = all_atts->fill_opacity; + } else { + inherited_flags_mask |= GF_SG_SVG_FILLOPACITY_DIRTY; + } + if (all_atts->fill_rule && *(all_atts->fill_rule) != SVG_FILLRULE_INHERIT) { + render_svg_props->fill_rule = all_atts->fill_rule; + } else { + inherited_flags_mask |= GF_SG_SVG_FILLRULE_DIRTY; + } + if (all_atts->font_family && all_atts->font_family->type != SVG_FONTFAMILY_INHERIT) { + render_svg_props->font_family = all_atts->font_family; + } else { + inherited_flags_mask |= GF_SG_SVG_FONTFAMILY_DIRTY; + } + if (all_atts->font_size && all_atts->font_size->type != SVG_NUMBER_INHERIT) { + render_svg_props->font_size = all_atts->font_size; + } else { + inherited_flags_mask |= GF_SG_SVG_FONTSIZE_DIRTY; + } + if (all_atts->font_style && *(all_atts->font_style) != SVG_FONTSTYLE_INHERIT) { + render_svg_props->font_style = all_atts->font_style; + } else { + inherited_flags_mask |= GF_SG_SVG_FONTSTYLE_DIRTY; + } + if (all_atts->font_variant && *(all_atts->font_variant) != SVG_FONTVARIANT_INHERIT) { + render_svg_props->font_variant = all_atts->font_variant; + } else { + inherited_flags_mask |= GF_SG_SVG_FONTVARIANT_DIRTY; + } + if (all_atts->font_weight && *(all_atts->font_weight) != SVG_FONTWEIGHT_INHERIT) { + render_svg_props->font_weight = all_atts->font_weight; + } else { + inherited_flags_mask |= GF_SG_SVG_FONTWEIGHT_DIRTY; + } + if (all_atts->image_rendering && *(all_atts->image_rendering) != SVG_RENDERINGHINT_INHERIT) { + render_svg_props->image_rendering = all_atts->image_rendering; + } + if (all_atts->line_increment && all_atts->line_increment->type != SVG_NUMBER_INHERIT) { + render_svg_props->line_increment = all_atts->line_increment; + } else { + inherited_flags_mask |= GF_SG_SVG_LINEINCREMENT_DIRTY; + } + if (all_atts->opacity && all_atts->opacity->type != SVG_NUMBER_INHERIT) { + render_svg_props->opacity = all_atts->opacity; + } else if (!all_atts->opacity) { + render_svg_props->opacity = NULL; + } else { + inherited_flags_mask |= GF_SG_SVG_OPACITY_DIRTY; + } + + if (all_atts->pointer_events && *(all_atts->pointer_events) != SVG_POINTEREVENTS_INHERIT) { + render_svg_props->pointer_events = all_atts->pointer_events; + } + if (all_atts->shape_rendering && *(all_atts->shape_rendering) != SVG_RENDERINGHINT_INHERIT) { + render_svg_props->shape_rendering = all_atts->shape_rendering; + } + if (all_atts->solid_color && all_atts->solid_color->type != SVG_PAINT_INHERIT) { + render_svg_props->solid_color = all_atts->solid_color; + if (all_atts->solid_color->type == SVG_PAINT_COLOR && + all_atts->solid_color->color.type == SVG_COLOR_CURRENTCOLOR) { + render_svg_props->solid_color = render_svg_props->color; + if (inherited_flags_mask & GF_SG_SVG_COLOR_DIRTY) { + inherited_flags_mask |= GF_SG_SVG_SOLIDCOLOR_OR_OPACITY_DIRTY; + } + } + } else if (!all_atts->solid_color) { + render_svg_props->solid_color = NULL; + } else { + inherited_flags_mask |= GF_SG_SVG_SOLIDCOLOR_OR_OPACITY_DIRTY; + } + if (all_atts->solid_opacity && all_atts->solid_opacity->type != SVG_NUMBER_INHERIT) { + render_svg_props->solid_opacity = all_atts->solid_opacity; + } else if (!all_atts->solid_opacity) { + render_svg_props->solid_opacity = NULL; + } else { + inherited_flags_mask |= GF_SG_SVG_SOLIDCOLOR_OR_OPACITY_DIRTY; + } + if (all_atts->stop_color && all_atts->stop_color->type != SVG_PAINT_INHERIT) { + render_svg_props->stop_color = all_atts->stop_color; + if (all_atts->stop_color->type == SVG_PAINT_COLOR && + all_atts->stop_color->color.type == SVG_COLOR_CURRENTCOLOR) { + render_svg_props->stop_color = render_svg_props->color; + if (inherited_flags_mask & GF_SG_SVG_COLOR_DIRTY) { + inherited_flags_mask |= GF_SG_SVG_STOPCOLOR_OR_OPACITY_DIRTY; + } + } + } else if (!all_atts->stop_color) { + render_svg_props->stop_color = NULL; + } else { + inherited_flags_mask |= GF_SG_SVG_STOPCOLOR_OR_OPACITY_DIRTY; + } + if (all_atts->stop_opacity && all_atts->stop_opacity->type != SVG_NUMBER_INHERIT) { + render_svg_props->stop_opacity = all_atts->stop_opacity; + } else if (!all_atts->stop_opacity) { + render_svg_props->stop_opacity = NULL; + } else { + inherited_flags_mask |= GF_SG_SVG_STOPCOLOR_OR_OPACITY_DIRTY; + } + if (all_atts->stroke && all_atts->stroke->type != SVG_PAINT_INHERIT) { + render_svg_props->stroke = all_atts->stroke; + if (all_atts->stroke->type == SVG_PAINT_COLOR && + all_atts->stroke->color.type == SVG_COLOR_CURRENTCOLOR) { + render_svg_props->stroke = render_svg_props->color; + if (inherited_flags_mask & GF_SG_SVG_COLOR_DIRTY) { + inherited_flags_mask |= GF_SG_SVG_STROKE_DIRTY; + } + } + } else { + inherited_flags_mask |= GF_SG_SVG_STROKE_DIRTY; + } + if (all_atts->stroke_dasharray && all_atts->stroke_dasharray->type != SVG_STROKEDASHARRAY_INHERIT) { + render_svg_props->stroke_dasharray = all_atts->stroke_dasharray; + } else { + inherited_flags_mask |= GF_SG_SVG_STROKEDASHARRAY_DIRTY; + } + if (all_atts->stroke_dashoffset && all_atts->stroke_dashoffset->type != SVG_NUMBER_INHERIT) { + render_svg_props->stroke_dashoffset = all_atts->stroke_dashoffset; + } else { + inherited_flags_mask |= GF_SG_SVG_STROKEDASHOFFSET_DIRTY; + } + if (all_atts->stroke_linecap && *(all_atts->stroke_linecap) != SVG_STROKELINECAP_INHERIT) { + render_svg_props->stroke_linecap = all_atts->stroke_linecap; + } else { + inherited_flags_mask |= GF_SG_SVG_STROKELINECAP_DIRTY; + } + if (all_atts->stroke_linejoin && *(all_atts->stroke_linejoin) != SVG_STROKELINEJOIN_INHERIT) { + render_svg_props->stroke_linejoin = all_atts->stroke_linejoin; + } else { + inherited_flags_mask |= GF_SG_SVG_STROKELINEJOIN_DIRTY; + } + if (all_atts->stroke_miterlimit && all_atts->stroke_miterlimit->type != SVG_NUMBER_INHERIT) { + render_svg_props->stroke_miterlimit = all_atts->stroke_miterlimit; + } else { + inherited_flags_mask |= GF_SG_SVG_STROKEMITERLIMIT_DIRTY; + } + if (all_atts->stroke_opacity && all_atts->stroke_opacity->type != SVG_NUMBER_INHERIT) { + render_svg_props->stroke_opacity = all_atts->stroke_opacity; + } else { + inherited_flags_mask |= GF_SG_SVG_STROKEOPACITY_DIRTY; + } + if (all_atts->stroke_width && all_atts->stroke_width->type != SVG_NUMBER_INHERIT) { + render_svg_props->stroke_width = all_atts->stroke_width; + } else { + inherited_flags_mask |= GF_SG_SVG_STROKEWIDTH_DIRTY; + } + if (all_atts->text_align && *(all_atts->text_align) != SVG_TEXTALIGN_INHERIT) { + render_svg_props->text_align = all_atts->text_align; + } else { + inherited_flags_mask |= GF_SG_SVG_TEXTPOSITION_DIRTY; + } + if (all_atts->text_anchor && *(all_atts->text_anchor) != SVG_TEXTANCHOR_INHERIT) { + render_svg_props->text_anchor = all_atts->text_anchor; + } else { + inherited_flags_mask |= GF_SG_SVG_TEXTPOSITION_DIRTY; + } + if (all_atts->text_rendering && *(all_atts->text_rendering) != SVG_RENDERINGHINT_INHERIT) { + render_svg_props->text_rendering = all_atts->text_rendering; + } + if (all_atts->vector_effect && *(all_atts->vector_effect) != SVG_VECTOREFFECT_INHERIT) { + render_svg_props->vector_effect = all_atts->vector_effect; + } else if (!all_atts->vector_effect) { + render_svg_props->vector_effect = NULL; + } else { + inherited_flags_mask |= GF_SG_SVG_VECTOREFFECT_DIRTY; + } + if (all_atts->viewport_fill && all_atts->viewport_fill->type != SVG_PAINT_INHERIT) { + render_svg_props->viewport_fill = all_atts->viewport_fill; + } else if (!all_atts->viewport_fill) { + render_svg_props->viewport_fill = NULL; + } + if (all_atts->viewport_fill_opacity && all_atts->viewport_fill_opacity->type != SVG_NUMBER_INHERIT) { + render_svg_props->viewport_fill_opacity = all_atts->viewport_fill_opacity; + } else if (!all_atts->viewport_fill_opacity) { + render_svg_props->viewport_fill_opacity = NULL; + } + if (all_atts->visibility && *(all_atts->visibility) != SVG_VISIBILITY_INHERIT) { + render_svg_props->visibility = all_atts->visibility; + } + return inherited_flags_mask; +} + + + +GF_EXPORT +void gf_svg_flatten_attributes(SVG_Element *e, SVGAllAttributes *all_atts) +{ + SVGAttribute *att; + memset(all_atts, 0, sizeof(SVGAllAttributes)); + if (e->sgprivate->tag <= GF_NODE_FIRST_DOM_NODE_TAG) return; + att = e->attributes; + while (att) { + switch(att->tag) { + case TAG_XML_ATT_id: all_atts->xml_id = (SVG_ID *)att->data; break; + case TAG_XML_ATT_base: all_atts->xml_base = (XMLRI *)att->data; break; + case TAG_XML_ATT_lang: all_atts->xml_lang = (SVG_LanguageID *)att->data; break; + case TAG_XML_ATT_space: all_atts->xml_space = (XML_Space *)att->data; break; + + case TAG_XLINK_ATT_type: all_atts->xlink_type = (SVG_String *)att->data; break; + case TAG_XLINK_ATT_role: all_atts->xlink_role = (XMLRI *)att->data; break; + case TAG_XLINK_ATT_arcrole: all_atts->xlink_arcrole = (XMLRI *)att->data; break; + case TAG_XLINK_ATT_title: all_atts->xlink_title = (SVG_String *)att->data; break; + case TAG_XLINK_ATT_href: all_atts->xlink_href = (XMLRI *)att->data; break; + case TAG_XLINK_ATT_show: all_atts->xlink_show = (SVG_String *)att->data; break; + case TAG_XLINK_ATT_actuate: all_atts->xlink_actuate = (SVG_String *)att->data; break; + + case TAG_XMLEV_ATT_event: all_atts->event = (XMLEV_Event *)att->data; break; + case TAG_XMLEV_ATT_phase: all_atts->phase = (XMLEV_Phase *)att->data; break; + case TAG_XMLEV_ATT_propagate: all_atts->propagate = (XMLEV_Propagate *)att->data; break; + case TAG_XMLEV_ATT_defaultAction: all_atts->defaultAction = (XMLEV_DefaultAction *)att->data; break; + case TAG_XMLEV_ATT_observer: all_atts->observer = (XML_IDREF *)att->data; break; + case TAG_XMLEV_ATT_target: all_atts->listener_target = (XML_IDREF *)att->data; break; + case TAG_XMLEV_ATT_handler: all_atts->handler = (XMLRI *)att->data; break; + + case TAG_LSR_ATT_enabled: all_atts->lsr_enabled = (SVG_Boolean *)att->data; break; + + case TAG_SVG_ATT_id: all_atts->id = (SVG_ID *)att->data; break; + case TAG_SVG_ATT__class: all_atts->_class = (SVG_String *)att->data; break; + case TAG_SVG_ATT_requiredFeatures: all_atts->requiredFeatures = (SVG_ListOfIRI *)att->data; break; + case TAG_SVG_ATT_requiredExtensions: all_atts->requiredExtensions = (SVG_ListOfIRI *)att->data; break; + case TAG_SVG_ATT_requiredFormats: all_atts->requiredFormats = (SVG_FormatList *)att->data; break; + case TAG_SVG_ATT_requiredFonts: all_atts->requiredFonts = (SVG_FontList *)att->data; break; + case TAG_SVG_ATT_systemLanguage: all_atts->systemLanguage = (SVG_LanguageIDs *)att->data; break; + case TAG_SVG_ATT_display: all_atts->display = (SVG_Display *)att->data; break; + case TAG_SVG_ATT_visibility: all_atts->visibility = (SVG_Visibility *)att->data; break; + case TAG_SVG_ATT_image_rendering: all_atts->image_rendering = (SVG_RenderingHint *)att->data; break; + case TAG_SVG_ATT_pointer_events: all_atts->pointer_events = (SVG_PointerEvents *)att->data; break; + case TAG_SVG_ATT_shape_rendering: all_atts->shape_rendering = (SVG_RenderingHint *)att->data; break; + case TAG_SVG_ATT_text_rendering: all_atts->text_rendering = (SVG_RenderingHint *)att->data; break; + case TAG_SVG_ATT_audio_level: all_atts->audio_level = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_viewport_fill: all_atts->viewport_fill = (SVG_Paint *)att->data; break; + case TAG_SVG_ATT_viewport_fill_opacity: all_atts->viewport_fill_opacity = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_overflow: all_atts->overflow = (SVG_String *)att->data; break; + case TAG_SVG_ATT_fill_opacity: all_atts->fill_opacity = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_stroke_opacity: all_atts->stroke_opacity = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_fill: all_atts->fill = (SVG_Paint *)att->data; break; + case TAG_SVG_ATT_fill_rule: all_atts->fill_rule = (SVG_FillRule *)att->data; break; + case TAG_SVG_ATT_stroke: all_atts->stroke = (SVG_Paint *)att->data; break; + case TAG_SVG_ATT_stroke_dasharray: all_atts->stroke_dasharray = (SVG_StrokeDashArray *)att->data; break; + case TAG_SVG_ATT_stroke_dashoffset: all_atts->stroke_dashoffset = (SVG_Length *)att->data; break; + case TAG_SVG_ATT_stroke_linecap: all_atts->stroke_linecap = (SVG_StrokeLineCap *)att->data; break; + case TAG_SVG_ATT_stroke_linejoin: all_atts->stroke_linejoin = (SVG_StrokeLineJoin *)att->data; break; + case TAG_SVG_ATT_stroke_miterlimit: all_atts->stroke_miterlimit = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_stroke_width: all_atts->stroke_width = (SVG_Length *)att->data; break; + case TAG_SVG_ATT_color: all_atts->color = (SVG_Paint *)att->data; break; + case TAG_SVG_ATT_color_rendering: all_atts->color_rendering = (SVG_RenderingHint *)att->data; break; + case TAG_SVG_ATT_vector_effect: all_atts->vector_effect = (SVG_VectorEffect *)att->data; break; + case TAG_SVG_ATT_solid_color: all_atts->solid_color = (SVG_SVGColor *)att->data; break; + case TAG_SVG_ATT_solid_opacity: all_atts->solid_opacity = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_display_align: all_atts->display_align = (SVG_DisplayAlign *)att->data; break; + case TAG_SVG_ATT_line_increment: all_atts->line_increment = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_stop_color: all_atts->stop_color = (SVG_SVGColor *)att->data; break; + case TAG_SVG_ATT_stop_opacity: all_atts->stop_opacity = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_font_family: all_atts->font_family = (SVG_FontFamily *)att->data; break; + case TAG_SVG_ATT_font_size: all_atts->font_size = (SVG_FontSize *)att->data; break; + case TAG_SVG_ATT_font_style: all_atts->font_style = (SVG_FontStyle *)att->data; break; + case TAG_SVG_ATT_font_variant: all_atts->font_variant = (SVG_FontVariant *)att->data; break; + case TAG_SVG_ATT_font_weight: all_atts->font_weight = (SVG_FontWeight *)att->data; break; + case TAG_SVG_ATT_text_anchor: all_atts->text_anchor = (SVG_TextAnchor *)att->data; break; + case TAG_SVG_ATT_text_align: all_atts->text_align = (SVG_TextAlign *)att->data; break; + case TAG_SVG_ATT_text_decoration: all_atts->text_decoration = (SVG_String *)att->data; break; + case TAG_SVG_ATT_focusHighlight: all_atts->focusHighlight = (SVG_FocusHighlight *)att->data; break; + case TAG_SVG_ATT_externalResourcesRequired: all_atts->externalResourcesRequired = (SVG_Boolean *)att->data; break; + case TAG_SVG_ATT_focusable: all_atts->focusable = (SVG_Focusable *)att->data; break; + case TAG_SVG_ATT_nav_next: all_atts->nav_next = (SVG_Focus *)att->data; break; + case TAG_SVG_ATT_nav_prev: all_atts->nav_prev = (SVG_Focus *)att->data; break; + case TAG_SVG_ATT_nav_up: all_atts->nav_up = (SVG_Focus *)att->data; break; + case TAG_SVG_ATT_nav_up_right: all_atts->nav_up_right = (SVG_Focus *)att->data; break; + case TAG_SVG_ATT_nav_right: all_atts->nav_right = (SVG_Focus *)att->data; break; + case TAG_SVG_ATT_nav_down_right: all_atts->nav_down_right = (SVG_Focus *)att->data; break; + case TAG_SVG_ATT_nav_down: all_atts->nav_down = (SVG_Focus *)att->data; break; + case TAG_SVG_ATT_nav_down_left: all_atts->nav_down_left = (SVG_Focus *)att->data; break; + case TAG_SVG_ATT_nav_left: all_atts->nav_left = (SVG_Focus *)att->data; break; + case TAG_SVG_ATT_nav_up_left: all_atts->nav_up_left = (SVG_Focus *)att->data; break; + case TAG_SVG_ATT_transform: all_atts->transform = (SVG_Transform *)att->data; break; + case TAG_SVG_ATT_target: all_atts->target = (SVG_String *)att->data; break; + case TAG_SVG_ATT_attributeName: all_atts->attributeName = (SMIL_AttributeName *)att->data; break; + case TAG_SVG_ATT_attributeType: all_atts->attributeType = (SMIL_AttributeType *)att->data; break; + case TAG_SVG_ATT_begin: all_atts->begin = (SMIL_Times *)att->data; break; + case TAG_SVG_ATT_dur: all_atts->dur = (SMIL_Duration *)att->data; break; + case TAG_SVG_ATT_end: all_atts->end = (SMIL_Times *)att->data; break; + case TAG_SVG_ATT_repeatCount: all_atts->repeatCount = (SMIL_RepeatCount *)att->data; break; + case TAG_SVG_ATT_repeatDur: all_atts->repeatDur = (SMIL_Duration *)att->data; break; + case TAG_SVG_ATT_restart: all_atts->restart = (SMIL_Restart *)att->data; break; + case TAG_SVG_ATT_smil_fill: all_atts->smil_fill = (SMIL_Fill *)att->data; break; + case TAG_SVG_ATT_min: all_atts->min = (SMIL_Duration *)att->data; break; + case TAG_SVG_ATT_max: all_atts->max = (SMIL_Duration *)att->data; break; + case TAG_SVG_ATT_to: all_atts->to = (SMIL_AnimateValue *)att->data; break; + case TAG_SVG_ATT_calcMode: all_atts->calcMode = (SMIL_CalcMode *)att->data; break; + case TAG_SVG_ATT_values: all_atts->values = (SMIL_AnimateValues *)att->data; break; + case TAG_SVG_ATT_keyTimes: all_atts->keyTimes = (SMIL_KeyTimes *)att->data; break; + case TAG_SVG_ATT_keySplines: all_atts->keySplines = (SMIL_KeySplines *)att->data; break; + case TAG_SVG_ATT_from: all_atts->from = (SMIL_AnimateValue *)att->data; break; + case TAG_SVG_ATT_by: all_atts->by = (SMIL_AnimateValue *)att->data; break; + case TAG_SVG_ATT_additive: all_atts->additive = (SMIL_Additive *)att->data; break; + case TAG_SVG_ATT_accumulate: all_atts->accumulate = (SMIL_Accumulate *)att->data; break; + case TAG_SVG_ATT_path: all_atts->path = (SVG_PathData *)att->data; break; + case TAG_SVG_ATT_keyPoints: all_atts->keyPoints = (SMIL_KeyPoints *)att->data; break; + case TAG_SVG_ATT_rotate: all_atts->rotate = (SVG_Rotate *)att->data; break; + case TAG_SVG_ATT_origin: all_atts->origin = (SVG_String *)att->data; break; + case TAG_SVG_ATT_transform_type: all_atts->transform_type = (SVG_TransformType *)att->data; break; + case TAG_SVG_ATT_clipBegin: all_atts->clipBegin = (SVG_Clock *)att->data; break; + case TAG_SVG_ATT_clipEnd: all_atts->clipEnd = (SVG_Clock *)att->data; break; + case TAG_SVG_ATT_syncBehavior: all_atts->syncBehavior = (SMIL_SyncBehavior *)att->data; break; + case TAG_SVG_ATT_syncTolerance: all_atts->syncTolerance = (SMIL_SyncTolerance *)att->data; break; + case TAG_SVG_ATT_syncMaster: all_atts->syncMaster = (SVG_Boolean *)att->data; break; + case TAG_SVG_ATT_syncReference: all_atts->syncReference = (XMLRI *)att->data; break; + case TAG_SVG_ATT_x: all_atts->x = (SVG_Coordinate *)att->data; break; + case TAG_SVG_ATT_y: all_atts->y = (SVG_Coordinate *)att->data; break; + case TAG_SVG_ATT_width: all_atts->width = (SVG_Length *)att->data; break; + case TAG_SVG_ATT_height: all_atts->height = (SVG_Length *)att->data; break; + case TAG_SVG_ATT_preserveAspectRatio: all_atts->preserveAspectRatio = (SVG_PreserveAspectRatio *)att->data; break; + case TAG_SVG_ATT_initialVisibility: all_atts->initialVisibility = (SVG_InitialVisibility *)att->data; break; + case TAG_SVG_ATT_type: all_atts->type = (SVG_ContentType *)att->data; break; + case TAG_SVG_ATT_cx: all_atts->cx = (SVG_Coordinate *)att->data; break; + case TAG_SVG_ATT_cy: all_atts->cy = (SVG_Coordinate *)att->data; break; + case TAG_SVG_ATT_r: all_atts->r = (SVG_Length *)att->data; break; + case TAG_SVG_ATT_cursorManager_x: all_atts->cursorManager_x = (SVG_Length *)att->data; break; + case TAG_SVG_ATT_cursorManager_y: all_atts->cursorManager_y = (SVG_Length *)att->data; break; + case TAG_SVG_ATT_rx: all_atts->rx = (SVG_Length *)att->data; break; + case TAG_SVG_ATT_ry: all_atts->ry = (SVG_Length *)att->data; break; + case TAG_SVG_ATT_horiz_adv_x: all_atts->horiz_adv_x = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_horiz_origin_x: all_atts->horiz_origin_x = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_font_stretch: all_atts->font_stretch = (SVG_String *)att->data; break; + case TAG_SVG_ATT_unicode_range: all_atts->unicode_range = (SVG_String *)att->data; break; + case TAG_SVG_ATT_panose_1: all_atts->panose_1 = (SVG_String *)att->data; break; + case TAG_SVG_ATT_widths: all_atts->widths = (SVG_String *)att->data; break; + case TAG_SVG_ATT_bbox: all_atts->bbox = (SVG_String *)att->data; break; + case TAG_SVG_ATT_units_per_em: all_atts->units_per_em = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_stemv: all_atts->stemv = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_stemh: all_atts->stemh = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_slope: all_atts->slope = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_cap_height: all_atts->cap_height = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_x_height: all_atts->x_height = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_accent_height: all_atts->accent_height = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_ascent: all_atts->ascent = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_descent: all_atts->descent = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_ideographic: all_atts->ideographic = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_alphabetic: all_atts->alphabetic = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_mathematical: all_atts->mathematical = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_hanging: all_atts->hanging = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_underline_position: all_atts->underline_position = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_underline_thickness: all_atts->underline_thickness = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_strikethrough_position: all_atts->strikethrough_position = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_strikethrough_thickness: all_atts->strikethrough_thickness = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_overline_position: all_atts->overline_position = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_overline_thickness: all_atts->overline_thickness = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_d: all_atts->d = (SVG_PathData *)att->data; break; + case TAG_SVG_ATT_unicode: all_atts->unicode = (SVG_String *)att->data; break; + case TAG_SVG_ATT_glyph_name: all_atts->glyph_name = (SVG_String *)att->data; break; + case TAG_SVG_ATT_arabic_form: all_atts->arabic_form = (SVG_String *)att->data; break; + case TAG_SVG_ATT_lang: all_atts->lang = (SVG_LanguageIDs *)att->data; break; + case TAG_SVG_ATT_u1: all_atts->u1 = (SVG_String *)att->data; break; + case TAG_SVG_ATT_g1: all_atts->g1 = (SVG_String *)att->data; break; + case TAG_SVG_ATT_u2: all_atts->u2 = (SVG_String *)att->data; break; + case TAG_SVG_ATT_g2: all_atts->g2 = (SVG_String *)att->data; break; + case TAG_SVG_ATT_k: all_atts->k = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_opacity: all_atts->opacity = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_x1: all_atts->x1 = (SVG_Coordinate *)att->data; break; + case TAG_SVG_ATT_y1: all_atts->y1 = (SVG_Coordinate *)att->data; break; + case TAG_SVG_ATT_x2: all_atts->x2 = (SVG_Coordinate *)att->data; break; + case TAG_SVG_ATT_y2: all_atts->y2 = (SVG_Coordinate *)att->data; break; + case TAG_SVG_ATT_gradientUnits: all_atts->gradientUnits = (SVG_GradientUnit *)att->data; break; + case TAG_SVG_ATT_spreadMethod: all_atts->spreadMethod = (SVG_SpreadMethod *)att->data; break; + case TAG_SVG_ATT_gradientTransform: all_atts->gradientTransform = (SVG_Transform *)att->data; break; + case TAG_SVG_ATT_pathLength: all_atts->pathLength = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_points: all_atts->points = (SVG_Points *)att->data; break; + case TAG_SVG_ATT_mediaSize: all_atts->mediaSize = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_mediaTime: all_atts->mediaTime = (SVG_String *)att->data; break; + case TAG_SVG_ATT_mediaCharacterEncoding: all_atts->mediaCharacterEncoding = (SVG_String *)att->data; break; + case TAG_SVG_ATT_mediaContentEncodings: all_atts->mediaContentEncodings = (SVG_String *)att->data; break; + case TAG_SVG_ATT_bandwidth: all_atts->bandwidth = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_fx: all_atts->fx = (SVG_Coordinate *)att->data; break; + case TAG_SVG_ATT_fy: all_atts->fy = (SVG_Coordinate *)att->data; break; + case TAG_SVG_ATT_size: all_atts->size = (LASeR_Size *)att->data; break; + case TAG_SVG_ATT_choice: all_atts->choice = (LASeR_Choice *)att->data; break; + case TAG_SVG_ATT_delta: all_atts->delta = (LASeR_Size *)att->data; break; + case TAG_SVG_ATT_offset: all_atts->offset = (SVG_Number *)att->data; break; + case TAG_SVG_ATT_syncBehaviorDefault: all_atts->syncBehaviorDefault = (SMIL_SyncBehavior *)att->data; break; + case TAG_SVG_ATT_syncToleranceDefault: all_atts->syncToleranceDefault = (SMIL_SyncTolerance *)att->data; break; + case TAG_SVG_ATT_viewBox: all_atts->viewBox = (SVG_ViewBox *)att->data; break; + case TAG_SVG_ATT_zoomAndPan: all_atts->zoomAndPan = (SVG_ZoomAndPan *)att->data; break; + case TAG_SVG_ATT_version: all_atts->version = (SVG_String *)att->data; break; + case TAG_SVG_ATT_baseProfile: all_atts->baseProfile = (SVG_String *)att->data; break; + case TAG_SVG_ATT_contentScriptType: all_atts->contentScriptType = (SVG_ContentType *)att->data; break; + case TAG_SVG_ATT_snapshotTime: all_atts->snapshotTime = (SVG_Clock *)att->data; break; + case TAG_SVG_ATT_timelineBegin: all_atts->timelineBegin = (SVG_TimelineBegin *)att->data; break; + case TAG_SVG_ATT_playbackOrder: all_atts->playbackOrder = (SVG_PlaybackOrder *)att->data; break; + case TAG_SVG_ATT_editable: all_atts->editable = (SVG_Boolean *)att->data; break; + case TAG_SVG_ATT_text_x: all_atts->text_x = (SVG_Coordinates *)att->data; break; + case TAG_SVG_ATT_text_y: all_atts->text_y = (SVG_Coordinates *)att->data; break; + case TAG_SVG_ATT_text_rotate: all_atts->text_rotate = (SVG_Numbers *)att->data; break; + case TAG_SVG_ATT_transformBehavior: all_atts->transformBehavior = (SVG_TransformBehavior *)att->data; break; + case TAG_SVG_ATT_overlay: all_atts->overlay = (SVG_Overlay *)att->data; break; + case TAG_SVG_ATT_fullscreen: all_atts->fullscreen = (SVG_Boolean *)att->data; break; + case TAG_SVG_ATT_motionTransform: all_atts->motionTransform = (SVG_Motion *)att->data; break; + } + + att = att->next; + } +} + +#endif + diff --git a/src/scenegraph/svg_smjs.c b/src/scenegraph/svg_smjs.c new file mode 100644 index 0000000..e18ce89 --- /dev/null +++ b/src/scenegraph/svg_smjs.c @@ -0,0 +1,2533 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_SVG +#include +#include + + +#ifdef GPAC_HAS_SPIDERMONKEY + +#include + +#define JSVAL_CHECK_STRING(_v) (JSVAL_IS_STRING(_v) || JSVAL_IS_NULL(_v)) +#define JSVAL_GET_STRING(_v) (JSVAL_IS_NULL(_v) ? NULL : JS_GetStringBytes(JSVAL_TO_STRING(_v)) ) + +static Bool svg_script_execute_handler(GF_Node *node, GF_DOM_Event *event, GF_Node *observer); + + +jsval dom_element_construct(JSContext *c, GF_Node *n); +void dom_element_finalize(JSContext *c, JSObject *obj); +void dom_document_finalize(JSContext *c, JSObject *obj); + +GF_Node *dom_get_element(JSContext *c, JSObject *obj); +GF_SceneGraph *dom_get_doc(JSContext *c, JSObject *obj); + +JSBool js_has_instance(JSContext *c, JSObject *obj, jsval val, JSBool *vp); +JSBool dom_event_add_listener(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval); +JSBool dom_event_remove_listener(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +char *js_get_utf8(jsval val); + +void dom_node_set_textContent(GF_Node *n, char *text); +char *dom_node_flatten_text(GF_Node *n); + +jsval dom_node_get_sibling(JSContext *c, GF_Node *n, Bool is_prev, Bool elt_only); + + + + +#define _ScriptMessage(_sg, _e, _msg) {\ + GF_JSAPIParam par; \ + par.info.e = _e; \ + par.info.msg = _msg; \ + _sg->script_action(_sg->script_action_cbck, GF_JSAPI_OP_MESSAGE, NULL, &par);\ + } + +static GFINLINE Bool ScriptAction(GF_SceneGraph *scene, u32 type, GF_Node *node, GF_JSAPIParam *param) +{ + if (scene->script_action) + return scene->script_action(scene->script_action_cbck, type, node, param); + return 0; +} + +typedef struct +{ + u32 nb_inst; + /*SVG uDOM classes*/ + JSClass svgElement; + JSClass svgDocument; + JSClass globalClass; + JSClass connectionClass; + JSClass rgbClass; + JSClass rectClass; + JSClass pointClass; + JSClass pathClass; + JSClass matrixClass; +} GF_SVGuDOM; +static GF_SVGuDOM *svg_rt = NULL; + +static JSObject *svg_new_path_object(JSContext *c, SVG_PathData *d); + + +static void svg_node_changed(GF_Node *n, GF_FieldInfo *info) +{ + if (!info) { + gf_node_changed(n, NULL); + } else { + u32 flag = gf_svg_get_modification_flags((SVG_Element *)n, info); + gf_node_dirty_set(n, flag, 0); + } + /*trigger rendering*/ + if (n->sgprivate->scenegraph->NodeCallback) + n->sgprivate->scenegraph->NodeCallback(n->sgprivate->scenegraph->userpriv, GF_SG_CALLBACK_MODIFIED, n, info); +} + + +/*note we are using float to avoid conversions fixed to/from JS native */ +typedef struct +{ + u32 r, g, b; +} rgbCI; + + +typedef struct +{ + Float x, y, w, h; + /*if set, this is the svg.viewport uDOM object, its values are updated upon query*/ + GF_SceneGraph *sg; +} rectCI; + +typedef struct +{ + Float x, y; + /*if set, this is the svg.currentTranslate uDOM object, its values are updated upon query*/ + GF_SceneGraph *sg; +} pointCI; + +typedef struct +{ + Float x, y; +} ptCI; + +typedef struct +{ + u32 nb_coms; + u8 *tags; + ptCI *pts; +} pathCI; + +static JSBool svg_connection_create(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +static JSBool svg_nav_to_location(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_JSAPIParam par; + GF_SceneGraph *sg; + if ((argc!=1) || !JS_InstanceOf(c, obj, &svg_rt->globalClass, NULL)) return JS_TRUE; + sg = JS_GetContextPrivate(c); + par.uri.url = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + par.uri.nb_params = 0; + ScriptAction(sg, GF_JSAPI_OP_LOAD_URL, sg->RootNode, &par); + return JS_TRUE; +} + +static JSBool svg_parse_xml(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_SceneGraph *sg; + JSObject *doc_obj; + GF_Node *node; + char *str; + GF_Node *gf_sm_load_svg_from_string(GF_SceneGraph *sg, char *svg_str); + + doc_obj = JSVAL_TO_OBJECT(argv[1]); + if (!doc_obj) { + dom_throw_exception(c, GF_DOM_EXC_WRONG_DOCUMENT_ERR); + return JS_FALSE; + } + str = js_get_utf8(argv[0]); + if (!str) return JS_TRUE; + + sg = dom_get_doc(c, doc_obj); + + node = gf_sm_load_svg_from_string(sg, str); + free(str); + *rval = dom_element_construct(c, node); + + return JS_TRUE; +} + +static void svg_script_error(JSContext *c, const char *msg, JSErrorReport *jserr) +{ + GF_SceneGraph *sg = JS_GetContextPrivate(c); + _ScriptMessage(sg, GF_SCRIPT_ERROR, msg); +} + +static JSBool svg_echo(JSContext *c, JSObject *p, uintN argc, jsval *argv, jsval *rval) +{ + u32 i; + char buf[5000]; + GF_SceneGraph *sg = JS_GetContextPrivate(c); + if (!sg) return JS_TRUE; + + strcpy(buf, ""); + for (i = 0; i < argc; i++) { + jschar*utf; + JSString *str = JS_ValueToString(c, argv[i]); + if (!str) return JS_TRUE; + if (i) strcat(buf, " "); + utf = JS_GetStringChars(str); + strcat(buf, JS_GetStringBytes(str)); + } + _ScriptMessage(sg, GF_SCRIPT_INFO, buf); + return JS_TRUE; +} + +static void svg_define_udom_exception(JSContext *c, JSObject *global) +{ + JSObject *obj = JS_DefineObject(c, global, "GlobalException", NULL, 0, 0); + JS_DefineProperty(c, obj, "NOT_CONNECTED_ERR", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "ENCODING_ERR", INT_TO_JSVAL(2), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "DENIED_ERR", INT_TO_JSVAL(3), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "UNKNOWN_ERR", INT_TO_JSVAL(4), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + + obj = JS_DefineObject(c, global, "SVGException", NULL, 0, 0); + JS_DefineProperty(c, obj, "SVG_WRONG_TYPE_ERR", INT_TO_JSVAL(0), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "SVG_INVALID_VALUE_ERR", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "SVG_MATRIX_NOT_INVERTABLE", INT_TO_JSVAL(2), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + + obj = JS_DefineObject(c, global, "SVGSVGElement", NULL, 0, 0); + JS_DefineProperty(c, obj, "NAV_AUTO", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "NAV_NEXT", INT_TO_JSVAL(2), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "NAV_PREV", INT_TO_JSVAL(3), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "NAV_UP", INT_TO_JSVAL(4), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "NAV_UP_RIGHT", INT_TO_JSVAL(5), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "NAV_RIGHT", INT_TO_JSVAL(6), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "NAV_DOWN_RIGHT", INT_TO_JSVAL(7), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "NAV_DOWN", INT_TO_JSVAL(8), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "NAV_DOWN_LEFT", INT_TO_JSVAL(9), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "NAV_LEFT", INT_TO_JSVAL(10), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(c, obj, "NAV_UP_LEFT", INT_TO_JSVAL(11), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); +} + +static JSBool global_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_SceneGraph *sg; + if (!JS_InstanceOf(c, obj, &svg_rt->globalClass, NULL) ) + return JS_TRUE; + + sg = JS_GetContextPrivate(c); + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + /*namespaceURI*/ + case 0: + return JS_TRUE; + /*parent*/ + case 1: + *vp = JSVAL_VOID; + if (sg->parent_scene && sg->parent_scene->svg_js) *vp = OBJECT_TO_JSVAL(sg->parent_scene->svg_js->global); + return JS_TRUE; + default: + return JS_TRUE; + } + } + return JS_TRUE; +} + +/*TODO - try to be more precise...*/ +static JSBool dom_imp_has_feature(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + *rval = BOOLEAN_TO_JSVAL(JS_FALSE); + if (argc) { + u32 len; + char sep; + char *fname = JS_GetStringBytes(JS_ValueToString(c, argv[0])); + if (!fname) return JS_TRUE; + while (strchr(" \t\n\r", fname[0])) fname++; + len = strlen(fname); + while (len && strchr(" \t\n\r", fname[len-1])) len--; + sep = fname[len]; + fname[len] = 0; + if (!stricmp(fname, "xml")) *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + else if (!stricmp(fname, "core")) *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + else if (!stricmp(fname, "traversal")) *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + else if (!stricmp(fname, "uievents")) *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + else if (!stricmp(fname, "mouseevents")) *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + else if (!stricmp(fname, "mutationevents")) *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + else if (!stricmp(fname, "events")) *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + + fname[len] = sep; + } + return JS_TRUE; +} + +static GF_Node *get_corresponding_use(GF_Node *n) +{ + GF_Node *t; + u32 i, count; + if (!n || !n->sgprivate->scenegraph->use_stack) return NULL; + + /*find current node in the use stack - if found, return the use element*/ + count = gf_list_count(n->sgprivate->scenegraph->use_stack); + for (i=count; i>0; i-=2) { + t = gf_list_get(n->sgprivate->scenegraph->use_stack, i-2); + if (t==n) { + GF_Node *use = gf_list_get(n->sgprivate->scenegraph->use_stack, i-1); + GF_Node *par_use = get_corresponding_use(use); + return par_use ? par_use : use; + } + } + /*otherwise recursively get up the tree*/ + return get_corresponding_use(gf_node_get_parent(n, 0)); +} +static JSBool svg_doc_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + u32 prop_id; + GF_SceneGraph *sg = dom_get_doc(c, obj); + if (!sg) return JS_TRUE; + + if (!JSVAL_IS_INT(id)) return JS_TRUE; + prop_id = JSVAL_TO_INT(id); + switch (prop_id) { + case 0:/*global*/ + *vp = OBJECT_TO_JSVAL( JS_GetGlobalObject(c) ); + return JS_TRUE; + } + return JS_TRUE; +} + +static JSBool svg_element_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + u32 prop_id; + GF_JSAPIParam par; + jsdouble *d; + JSString *s; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (!JSVAL_IS_INT(id)) return JS_TRUE; + prop_id = JSVAL_TO_INT(id); + + + switch (prop_id) { + case 0: /*id*/ + { + const char *node_name = gf_node_get_name((GF_Node*)n); + if (node_name) { + s = JS_NewStringCopyZ(c, node_name); + *vp = STRING_TO_JSVAL( s ); + return JS_TRUE; + } + return JS_TRUE; + } + case 1:/*firstElementChild*/ + *vp = JSVAL_NULL; + if (n->sgprivate->tag!=TAG_DOMText) { + GF_ChildNodeItem *child = ((GF_ParentNode*)n)->children; + while (child) { + if (child->node->sgprivate->tag != TAG_DOMText) { + *vp = dom_element_construct(c, child->node); + break; + } + child = child->next; + } + } + return JS_TRUE; + case 2:/*lastElementChild*/ + *vp = JSVAL_NULL; + if (n->sgprivate->tag!=TAG_DOMText) { + GF_Node *last = NULL; + GF_ChildNodeItem *child = ((GF_ParentNode*)n)->children; + while (child) { + if (child->node->sgprivate->tag != TAG_DOMText) { + last = child->node; + } + child = child->next; + } + if (last) *vp = dom_element_construct(c, last); + } + return JS_TRUE; + case 3:/*previousElementSibling*/ + *vp = dom_node_get_sibling(c, n, 1, 1); + return JS_TRUE; + case 4:/*nextElementSibling*/ + *vp = dom_node_get_sibling(c, n, 0, 1); + return JS_TRUE; + + case 5:/*currentScale*/ + if (n->sgprivate->tag!=TAG_SVG_svg) return JS_TRUE; + if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_SCALE, (GF_Node *)n, &par)) { + d = JS_NewDouble(c, FIX2FLT(par.val) ); + *vp = DOUBLE_TO_JSVAL(d); + return JS_TRUE; + } + return JS_TRUE; + case 6:/*currentRotate*/ + if (n->sgprivate->tag!=TAG_SVG_svg) return JS_TRUE; + if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_ROTATION, (GF_Node *)n, &par)) { + d = JS_NewDouble(c, FIX2FLT(par.val) ); + *vp = DOUBLE_TO_JSVAL(d); + return JS_TRUE; + } + return JS_TRUE; + case 7:/*currentTranslate*/ + if (n->sgprivate->tag!=TAG_SVG_svg) return JS_TRUE; + if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_TRANSLATE, (GF_Node *)n, &par)) { + JSObject *r = JS_NewObject(c, &svg_rt->pointClass, 0, 0); + pointCI *rc = malloc(sizeof(pointCI)); + rc->x = FIX2FLT(par.pt.x); + rc->y = FIX2FLT(par.pt.y); + rc->sg = n->sgprivate->scenegraph; + JS_SetPrivate(c, r, rc); + *vp = OBJECT_TO_JSVAL(r); + return JS_TRUE; + } + return JS_TRUE; + case 8:/*viewport*/ + if (n->sgprivate->tag!=TAG_SVG_svg) return JS_TRUE; + if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_VIEWPORT, (GF_Node *)n, &par)) { + JSObject *r = JS_NewObject(c, &svg_rt->rectClass, 0, 0); + rectCI *rc = malloc(sizeof(rectCI)); + rc->x = FIX2FLT(par.rc.x); + rc->y = FIX2FLT(par.rc.y); + rc->w = FIX2FLT(par.rc.width); + rc->h = FIX2FLT(par.rc.height); + rc->sg = n->sgprivate->scenegraph; + JS_SetPrivate(c, r, rc); + *vp = OBJECT_TO_JSVAL(r); + return JS_TRUE; + } + return JS_TRUE; + case 9:/*currentTime*/ + d = JS_NewDouble(c, gf_node_get_scene_time((GF_Node *)n) ); + *vp = DOUBLE_TO_JSVAL(d); + return JS_TRUE; + case 10:/*isPaused*/ + *vp = BOOLEAN_TO_JSVAL(JS_FALSE); + return JS_TRUE; + case 11:/*ownerSVGElement*/ + while (1) { + GF_Node *par = gf_node_get_parent(n, 0); + if (!par) return JS_TRUE; + if (par->sgprivate->tag==TAG_SVG_svg) { + *vp = dom_element_construct(c, par); + return JS_TRUE; + } + n = par; + } + return JS_TRUE; + case 12:/*correspondingElement*/ + /*if we can find a corresponding element for this node, then this is an SVGElementInstance*/ + if (get_corresponding_use(n)) { + *vp = dom_element_construct(c, n); + } else { + *vp = dom_element_construct(c, NULL); + } + return JS_TRUE; + case 13:/*correspondingUseElement*/ + *vp = dom_element_construct(c, get_corresponding_use(n)); + return JS_TRUE; + default: + return JS_TRUE; + } + return JS_TRUE; +} + +static JSBool svg_element_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_JSAPIParam par; + jsdouble d; + u32 prop_id; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (!JSVAL_IS_INT(id)) return JS_TRUE; + prop_id = JSVAL_TO_INT(id); + + switch (prop_id) { + case 0:/*id*/ + if (JSVAL_CHECK_STRING(*vp)) { + char *id = JSVAL_GET_STRING(*vp); + if (id) { + GF_FieldInfo info; + u32 nid = gf_node_get_id(n); + if (!nid) nid = gf_sg_get_next_available_node_id(n->sgprivate->scenegraph); + gf_node_set_id(n, nid, id); + if (gf_node_get_attribute_by_tag(n, TAG_XML_ATT_id, 1, 0, &info)==GF_OK) { + if (*(DOM_String *)info.far_ptr) free(*(DOM_String *)info.far_ptr); + *(DOM_String *)info.far_ptr = strdup(id); + } + if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_id, 1, 0, &info)==GF_OK) { + if (*(DOM_String *)info.far_ptr) free(*(DOM_String *)info.far_ptr); + *(DOM_String *)info.far_ptr = strdup(id); + } + } + } + return JS_TRUE; + /*currentScale*/ + case 5: + if (!JSVAL_IS_NUMBER(*vp) || (n->sgprivate->tag!=TAG_SVG_svg)) return JS_TRUE; + JS_ValueToNumber(c, *vp, &d); + par.val = FLT2FIX(d); + if (!par.val) { + return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR); + } + if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_SCALE, (GF_Node *)n, &par)) { + return JS_TRUE; + } + return JS_TRUE; + /*currentRotate*/ + case 6: + if (!JSVAL_IS_NUMBER(*vp) || (n->sgprivate->tag!=TAG_SVG_svg)) return JS_TRUE; + JS_ValueToNumber(c, *vp, &d); + par.val = FLT2FIX(d); + if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_ROTATION, (GF_Node *)n, &par)) { + return JS_TRUE; + } + return JS_TRUE; + /*currentTime*/ + case 9: + if (!JSVAL_IS_NUMBER(*vp) || (n->sgprivate->tag!=TAG_SVG_svg)) return JS_TRUE; + JS_ValueToNumber(c, *vp, &d); + par.time = d; + if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_TIME, (GF_Node *)n, &par)) { + return JS_TRUE; + } + return JS_TRUE; + default: + return JS_TRUE; + } + return JS_TRUE; +} + + +static GF_Node *svg_udom_smil_check_instance(JSContext *c, JSObject *obj) +{ + GF_Node *n = dom_get_element(c, obj); + if (!n) return NULL; + switch (n->sgprivate->tag) { + case TAG_SVG_animate: + case TAG_SVG_animateColor: + case TAG_SVG_animateMotion: + case TAG_SVG_animateTransform: + case TAG_SVG_animation: + case TAG_SVG_audio: + case TAG_SVG_video: + case TAG_SVG_set: + /*not sure about this one...*/ + case TAG_SVG_discard: + return n; + } + return NULL; +} + + +/*TODO*/ +JSBool svg_udom_smil_time_insert(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, Bool is_end) +{ + GF_FieldInfo info; + u32 i, count; + Double offset; + GF_List *times; + SMIL_Time *newtime; + GF_Node *n = svg_udom_smil_check_instance(c, obj); + if (!n) return JS_TRUE; + + + if (is_end) { + info.far_ptr = ((SVGTimedAnimBaseElement *)n)->timingp->end; + } else { + info.far_ptr = ((SVGTimedAnimBaseElement *)n)->timingp->begin; + } + + + times = *((GF_List **)info.far_ptr); + GF_SAFEALLOC(newtime, SMIL_Time); + newtime->type = GF_SMIL_TIME_EVENT_RESOLVED; + + offset = 0; + if (argc && JSVAL_IS_NUMBER(argv[0]) ) { + jsdouble d; + JS_ValueToNumber(c, argv[0], &d); + offset = d; + } + newtime->clock = gf_node_get_scene_time(n) + offset; + + /*insert in sorted order*/ + count = gf_list_count(times); + for (i=0; itype) ) { + if (t->clock > newtime->clock) break; + } else { + break; + } + } + gf_list_insert(times, newtime, i); + + info.fieldType = SMIL_Times_datatype; + gf_node_changed(n, &info); + return JS_TRUE; +} + +JSBool svg_udom_smil_begin(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return svg_udom_smil_time_insert(c, obj, argc, argv, rval, 0); +} + +JSBool svg_udom_smil_end(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return svg_udom_smil_time_insert(c, obj, argc, argv, rval, 1); +} + +/*TODO*/ +JSBool svg_udom_smil_pause(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 tag; + GF_Node *n = dom_get_element(c, obj); + + tag = gf_node_get_tag(n); + if (gf_svg_is_animation_tag(tag)) { + /* pausing an animation element (set, animate ...) should pause the main time line ? */ + gf_smil_timing_pause(n); + } else if (gf_svg_is_timing_tag(tag)) { + ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_PAUSE_SVG, n, NULL); + } else { + return JS_TRUE; + } + return JS_TRUE; +} +/*TODO*/ +JSBool svg_udom_smil_resume(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 tag; + GF_Node *n = dom_get_element(c, obj); + + tag = gf_node_get_tag(n); + if (gf_svg_is_animation_tag(tag)) { + /* resuming an animation element (set, animate ...) should resume the main time line ? */ + gf_smil_timing_resume(n); + } else if (gf_svg_is_timing_tag(tag)) { + ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESUME_SVG, n, NULL); + } else { + return JS_TRUE; + } + return JS_TRUE; +} + +JSBool svg_udom_get_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char attValue[1024], *ns, *name; + GF_Err e; + GF_FieldInfo info; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + ns = name =NULL; + if (! JSVAL_CHECK_STRING(argv[0]) ) return JS_TRUE; + if (argc==2) { + ns = JSVAL_GET_STRING(argv[0]); + name = JSVAL_GET_STRING(argv[1]); + } else if (argc==1) { + name = JSVAL_GET_STRING(argv[0]); + } else return JS_TRUE; + + if (!name) return JS_TRUE; + if (!strcmp(name, "#text")) { + char *res = dom_node_flatten_text(n); + *rval = STRING_TO_JSVAL( JS_NewStringCopyZ(c, res) ); + free(res); + return JS_TRUE; + } + e = gf_node_get_field_by_name(n, name, &info); + + if (e!=GF_OK) return JS_TRUE; + *rval = JSVAL_VOID; + + switch (info.fieldType) { + /* inheritable floats */ + case SVG_FontSize_datatype: + case SVG_Color_datatype: + case SVG_Paint_datatype: + /* inheritable float and unit */ + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + /*Number*/ + case SVG_Number_datatype: + +/*all string traits*/ + case SVG_Boolean_datatype: + case SVG_FillRule_datatype: + case SVG_StrokeLineJoin_datatype: + case SVG_StrokeLineCap_datatype: + case SVG_FontStyle_datatype: + case SVG_FontWeight_datatype: + case SVG_FontVariant_datatype: + case SVG_TextAnchor_datatype: + case SVG_Display_datatype: + case SVG_Visibility_datatype: + case SVG_GradientUnit_datatype: + case SVG_PreserveAspectRatio_datatype: + case XML_Space_datatype: + case XMLEV_Propagate_datatype: + case XMLEV_DefaultAction_datatype: + case XMLEV_Phase_datatype: + case SMIL_SyncBehavior_datatype: + case SMIL_SyncTolerance_datatype: + case SMIL_AttributeType_datatype: + case SMIL_CalcMode_datatype: + case SMIL_Additive_datatype: + case SMIL_Accumulate_datatype: + case SMIL_Restart_datatype: + case SMIL_Fill_datatype: + case SVG_Overflow_datatype: + case SVG_ZoomAndPan_datatype: + case SVG_DisplayAlign_datatype: + case SVG_TextAlign_datatype: + case SVG_PointerEvents_datatype: + case SVG_RenderingHint_datatype: + case SVG_VectorEffect_datatype: + case SVG_PlaybackOrder_datatype: + case SVG_TimelineBegin_datatype: +/*end of string traits*/ +/*DOM string traits*/ + case SVG_FontFamily_datatype: + case XMLRI_datatype: + case DOM_String_datatype: + case SVG_ContentType_datatype: + case SVG_LanguageID_datatype: + case SVG_Focus_datatype: + case SVG_ID_datatype: + case SVG_GradientOffset_datatype: +/*end of DOM string traits*/ + gf_svg_dump_attribute(n, &info, attValue); + *rval = STRING_TO_JSVAL( JS_NewStringCopyZ(c, attValue) ); + return JS_TRUE; + /*dump to trait*/ + break; + +#if 0 +/*SVGT 1.2 default traits*/ + case SMIL_KeyTimes_datatype: + case SMIL_KeyPoints_datatype: + case SMIL_KeySplines_datatype: + case SVG_Coordinates_datatype: + case SVG_StrokeDashArray_datatype: + case SVG_Points_datatype: + case SVG_Motion_datatype: +/*end SVGT 1.2 default traits*/ + +/*unimplemented/unnkown/FIXME traits*/ + case SMIL_SyncTolerance_datatype: + case SVG_TransformType_datatype: + case SVG_TransformList_datatype: + case SMIL_AnimateValue_datatype: + case SMIL_AnimateValues_datatype: + case SMIL_AttributeName_datatype: + case SMIL_Times_datatype: + case SMIL_Duration_datatype: + case SMIL_RepeatCount_datatype: + default: +/*end unimplemented/unnkown/FIXME traits*/ + return JS_TRUE; +#endif + } + return JS_TRUE; +} + +JSBool svg_udom_get_float_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *szName; + GF_FieldInfo info; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (argc!=1) return JS_TRUE; + if (!JSVAL_IS_STRING(argv[0])) return JS_TRUE; + szName = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + *rval = JSVAL_VOID; + if (gf_node_get_attribute_by_name(n, szName, 0, 1, 1, &info) != GF_OK) return JS_TRUE; + + switch (info.fieldType) { + /* inheritable floats */ + case SVG_Number_datatype: + case SVG_FontSize_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + { + SVG_Number *l = (SVG_Number *)info.far_ptr; + if (l->type==SVG_NUMBER_AUTO || l->type==SVG_NUMBER_INHERIT) return JS_TRUE; + *rval = DOUBLE_TO_JSVAL( JS_NewDouble(c, FIX2FLT(l->value) ) ); + return JS_TRUE; + } + case SVG_Clock_datatype: + *rval = DOUBLE_TO_JSVAL( JS_NewDouble(c, *(SVG_Clock*)info.far_ptr ) ); + return JS_TRUE; + default: + return JS_TRUE; + } + return JS_TRUE; +} + +JSBool svg_udom_get_matrix_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *szName; + JSObject *mO; + GF_FieldInfo info; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (argc!=1) return JS_TRUE; + if (!JSVAL_IS_STRING(argv[0])) return JS_TRUE; + szName = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + *rval = JSVAL_VOID; + if (gf_node_get_field_by_name(n, szName, &info) != GF_OK) return JS_TRUE; + + if (info.fieldType==SVG_Transform_datatype) { + GF_Matrix2D *mx = malloc(sizeof(GF_Matrix2D)); + mO = JS_NewObject(c, &svg_rt->matrixClass, 0, 0); + gf_mx2d_init(*mx); + gf_mx2d_copy(*mx, ((SVG_Transform*)info.far_ptr)->mat); + + JS_SetPrivate(c, mO, mx); + *rval = OBJECT_TO_JSVAL(mO); + return JS_TRUE; + } + return JS_TRUE; +} +JSBool svg_udom_get_rect_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *newObj; + char *szName; + GF_FieldInfo info; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (argc!=1) return JS_TRUE; + if (!JSVAL_IS_STRING(argv[0])) return JS_TRUE; + szName = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + *rval = JSVAL_VOID; + if (gf_node_get_field_by_name(n, szName, &info) != GF_OK) return JS_TRUE; + + if (info.fieldType==SVG_ViewBox_datatype) { + rectCI *rc; + SVG_ViewBox *v = (SVG_ViewBox *)info.far_ptr; + newObj = JS_NewObject(c, &svg_rt->rectClass, 0, 0); + GF_SAFEALLOC(rc, rectCI); + rc->x = FIX2FLT(v->x); + rc->y = FIX2FLT(v->y); + rc->w = FIX2FLT(v->width); + rc->h = FIX2FLT(v->height); + JS_SetPrivate(c, newObj, rc); + *rval = OBJECT_TO_JSVAL(newObj); + return JS_TRUE; + } + return JS_TRUE; +} +JSBool svg_udom_get_path_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *szName; + GF_FieldInfo info; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (argc!=1) return JS_TRUE; + if (!JSVAL_IS_STRING(argv[0])) return JS_TRUE; + szName = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + *rval = JSVAL_VOID; + if (gf_node_get_field_by_name(n, szName, &info) != GF_OK) return JS_TRUE; + + if (info.fieldType==SVG_PathData_datatype) { + *rval = OBJECT_TO_JSVAL( svg_new_path_object(c, info.far_ptr) ); + return JS_TRUE; + } + return JS_TRUE; +} +JSBool svg_udom_get_rgb_color_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *szName; + GF_FieldInfo info; + rgbCI *rgb; + JSObject *newObj; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (argc!=1) return JS_TRUE; + if (!JSVAL_IS_STRING(argv[0])) return JS_TRUE; + szName = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + *rval = JSVAL_VOID; + if (gf_node_get_field_by_name(n, szName, &info) != GF_OK) return JS_TRUE; + + switch (info.fieldType) { + case SVG_Color_datatype: + { + SVG_Color *col = (SVG_Color *)info.far_ptr; + if (col->type == SVG_COLOR_CURRENTCOLOR) return JS_TRUE; + if (col->type == SVG_COLOR_INHERIT) return JS_TRUE; + newObj = JS_NewObject(c, &svg_rt->rgbClass, 0, 0); + GF_SAFEALLOC(rgb, rgbCI); + rgb->r = (u8) (255*FIX2FLT(col->red)) ; + rgb->g = (u8) (255*FIX2FLT(col->green)) ; + rgb->b = (u8) (255*FIX2FLT(col->blue)) ; + JS_SetPrivate(c, newObj, rgb); + *rval = OBJECT_TO_JSVAL(newObj); + return JS_TRUE; + } + break; + case SVG_Paint_datatype: + { + SVG_Paint *paint = (SVG_Paint *)info.far_ptr; + if (1 || paint->type==SVG_PAINT_COLOR) { + newObj = JS_NewObject(c, &svg_rt->rgbClass, 0, 0); + GF_SAFEALLOC(rgb, rgbCI); + rgb->r = (u8) (255*FIX2FLT(paint->color.red) ); + rgb->g = (u8) (255*FIX2FLT(paint->color.green) ); + rgb->b = (u8) (255*FIX2FLT(paint->color.blue) ); + JS_SetPrivate(c, newObj, rgb); + *rval = OBJECT_TO_JSVAL(newObj); + return JS_TRUE; + } + return JS_TRUE; + } + } + return JS_TRUE; +} + +JSBool svg_udom_set_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *ns, *name, *val; + GF_Err e; + GF_FieldInfo info; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + val = ns = name = NULL; + if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + if (argc==3) { + if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + if (!JSVAL_CHECK_STRING(argv[2])) return JS_TRUE; + ns = JSVAL_GET_STRING(argv[0]); + name = JSVAL_GET_STRING(argv[1]); + val = JS_GetStringBytes(JSVAL_TO_STRING(argv[2])); + } else if (argc==2) { + name = JSVAL_GET_STRING(argv[0]); + val = JS_GetStringBytes(JSVAL_TO_STRING(argv[1])); + } else + return JS_TRUE; + if (!name) return JS_TRUE; + if (!strcmp(name, "#text")) { + if (!JSVAL_IS_STRING(argv[1])) return JS_TRUE; + dom_node_set_textContent(n, val); + return JS_TRUE; + } + e = gf_node_get_field_by_name(n, name, &info); + + + if (!val || (e!=GF_OK)) return JS_TRUE; + *rval = JSVAL_VOID; + + e = gf_svg_parse_attribute(n, &info, val, 0); + if (e) return dom_throw_exception(c, GF_DOM_EXC_INVALID_ACCESS_ERR); + + svg_node_changed(n, &info); + + return JS_TRUE; +} + +JSBool svg_udom_set_float_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_FieldInfo info; + jsdouble d; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (argc!=2) return JS_TRUE; + if (!JSVAL_IS_STRING(argv[0])) return JS_TRUE; + if (!JSVAL_IS_NUMBER(argv[1])) return JS_TRUE; + JS_ValueToNumber(c, argv[1], &d); + *rval = JSVAL_VOID; + if (gf_node_get_field_by_name(n, JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), &info) != GF_OK) return JS_TRUE; + + switch (info.fieldType) { + /* inheritable floats */ + case SVG_FontSize_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_Number_datatype: + { + SVG_Number *l = (SVG_Number *)info.far_ptr; + l->type=SVG_NUMBER_VALUE; + l->value = FLT2FIX(d); + break; + } + case SVG_Numbers_datatype: + case SVG_Coordinates_datatype: + { + SVG_Number *val; + SVG_Coordinates *l = (SVG_Coordinates *)info.far_ptr; + while (gf_list_count(*l)) { + val = gf_list_get(*l, 0); + gf_list_rem(*l, 0); + free(val); + } + GF_SAFEALLOC(val, SVG_Coordinate); + val->type=SVG_NUMBER_VALUE; + val->value = FLT2FIX(d); + gf_list_add(*l, val); + break; + } + default: + return JS_TRUE; + } + svg_node_changed(n, &info); + return JS_TRUE; +} +JSBool svg_udom_set_matrix_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *mO; + char *szName; + GF_FieldInfo info; + GF_Matrix2D *mx; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (argc!=2) return JS_TRUE; + if (!JSVAL_IS_STRING(argv[0])) return JS_TRUE; + szName = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + if (JSVAL_IS_NULL(argv[1]) || !JSVAL_IS_OBJECT(argv[1])) return JS_TRUE; + mO = JSVAL_TO_OBJECT(argv[1]); + if (!JS_InstanceOf(c, mO, &svg_rt->matrixClass, NULL) ) return JS_TRUE; + mx = JS_GetPrivate(c, mO); + if (!mx) return JS_TRUE; + + if (gf_node_get_field_by_name(n, szName, &info) != GF_OK) return JS_TRUE; + + if (info.fieldType==SVG_Transform_datatype) { + gf_mx2d_copy(((SVG_Transform*)info.far_ptr)->mat, *mx); + svg_node_changed(n, NULL); + return JS_TRUE; + } + return JS_TRUE; +} +JSBool svg_udom_set_rect_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *rO; + char *szName; + GF_FieldInfo info; + rectCI *rc; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (argc!=2) return JS_TRUE; + if (!JSVAL_IS_STRING(argv[0])) return JS_TRUE; + szName = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + if (JSVAL_IS_NULL(argv[1]) || !JSVAL_IS_OBJECT(argv[1])) return JS_TRUE; + rO = JSVAL_TO_OBJECT(argv[1]); + if (!JS_InstanceOf(c, rO, &svg_rt->rectClass, NULL) ) return JS_TRUE; + rc = JS_GetPrivate(c, rO); + if (!rc) return JS_TRUE; + + if (gf_node_get_field_by_name(n, szName, &info) != GF_OK) return JS_TRUE; + + if (info.fieldType==SVG_ViewBox_datatype) { + SVG_ViewBox *v = (SVG_ViewBox *)info.far_ptr; + v->x = FLT2FIX(rc->x); + v->y = FLT2FIX(rc->y); + v->width = FLT2FIX(rc->w); + v->height = FLT2FIX(rc->h); + svg_node_changed(n, NULL); + return JS_TRUE; + } + return JS_TRUE; +} +JSBool svg_udom_set_path_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + pathCI *path; + GF_FieldInfo info; + JSObject *pO; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (argc!=2) return JS_TRUE; + if (!JSVAL_IS_STRING(argv[0])) return JS_TRUE; + if (JSVAL_IS_NULL(argv[1]) || !JSVAL_IS_OBJECT(argv[1])) return JS_TRUE; + pO = JSVAL_TO_OBJECT(argv[1]); + if (!JS_InstanceOf(c, pO, &svg_rt->pathClass, NULL) ) return JS_TRUE; + path = JS_GetPrivate(c, pO); + if (!path) return JS_TRUE; + if (gf_node_get_field_by_name(n, JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), &info) != GF_OK) return JS_TRUE; + + if (info.fieldType==SVG_PathData_datatype) { +#if USE_GF_PATH +#else + u32 i; + u32 nb_pts; + SVG_PathData *d = (SVG_PathData *)info.far_ptr; + while (gf_list_count(d->commands)) { + u8 *t = gf_list_get(d->commands, 0); + gf_list_rem(d->commands, 0); + free(t); + } + while (gf_list_count(d->points)) { + SVG_Point *t = gf_list_get(d->points, 0); + gf_list_rem(d->points, 0); + free(t); + } + nb_pts = 0; + for (i=0; inb_coms; i++) { + u8 *t = malloc(sizeof(u8)); + *t = path->tags[i]; + gf_list_add(d->commands, t); + switch (*t) { + case 0: + case 1: nb_pts++; break; + case 2: nb_pts+=3; break; + case 4: nb_pts+=2; break; + } + } + for (i=0; ix = FLT2FIX(path->pts[i].x); + t->y = FLT2FIX(path->pts[i].y); + gf_list_add(d->points, t); + } + svg_node_changed(n, NULL); + return JS_TRUE; +#endif + } + return JS_TRUE; +} + +JSBool svg_udom_set_rgb_color_trait(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_FieldInfo info; + rgbCI *rgb; + JSObject *colO; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + if (argc!=2) return JS_TRUE; + if (!JSVAL_IS_STRING(argv[0])) return JS_TRUE; + if (!JSVAL_IS_OBJECT(argv[1])) return JS_TRUE; + colO = JSVAL_TO_OBJECT(argv[1]); + if (!colO) return JS_TRUE; + if (!JS_InstanceOf(c, colO, &svg_rt->rgbClass, NULL) ) return JS_TRUE; + rgb = JS_GetPrivate(c, colO); + if (!rgb) return JS_TRUE; + + *rval = JSVAL_VOID; + if (gf_node_get_field_by_name(n, JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), &info) != GF_OK) return JS_TRUE; + + switch (info.fieldType) { + case SVG_Color_datatype: + { + SVG_Color *col = (SVG_Color *)info.far_ptr; + col->type = SVG_COLOR_RGBCOLOR; + col->red = FLT2FIX(rgb->r / 255.0f); + col->green = FLT2FIX(rgb->g / 255.0f); + col->blue = FLT2FIX(rgb->b / 255.0f); + svg_node_changed(n, &info); + return JS_TRUE; + } + case SVG_Paint_datatype: + { + SVG_Paint *paint = (SVG_Paint *)info.far_ptr; + paint->type = SVG_PAINT_COLOR; + paint->color.type = SVG_COLOR_RGBCOLOR; + paint->color.red = FLT2FIX(rgb->r / 255.0f); + paint->color.green = FLT2FIX(rgb->g / 255.0f); + paint->color.blue = FLT2FIX(rgb->b / 255.0f); + svg_node_changed(n, &info); + return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool svg_get_bbox(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, Bool get_screen) +{ + GF_JSAPIParam par; + GF_Node *n = dom_get_element(c, obj); + if (!n || argc) return JS_TRUE; + + par.bbox.is_set = 0; + if (ScriptAction(n->sgprivate->scenegraph, get_screen ? GF_JSAPI_OP_GET_SCREEN_BBOX : GF_JSAPI_OP_GET_LOCAL_BBOX, (GF_Node *)n, &par) ) { + if (par.bbox.is_set) { + JSObject *rO = JS_NewObject(c, &svg_rt->rectClass, 0, 0); + rectCI *rc = malloc(sizeof(rectCI)); + rc->sg = NULL; + rc->x = FIX2FLT(par.bbox.min_edge.x); + /*BBox is in 3D coord system style*/ + rc->y = FIX2FLT(par.bbox.min_edge.y); + rc->w = FIX2FLT(par.bbox.max_edge.x - par.bbox.min_edge.x); + rc->h = FIX2FLT(par.bbox.max_edge.y - par.bbox.min_edge.y); + JS_SetPrivate(c, rO, rc); + *rval = OBJECT_TO_JSVAL(rO); + } else { + *rval = JSVAL_VOID; + } + return JS_TRUE; + } + return JS_TRUE; +} +JSBool svg_udom_get_local_bbox(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return svg_get_bbox(c, obj, argc, argv, rval, 0); +} +JSBool svg_udom_get_screen_bbox(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return svg_get_bbox(c, obj, argc, argv, rval, 1); +} + +JSBool svg_udom_get_screen_ctm(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_JSAPIParam par; + GF_Node *n = dom_get_element(c, obj); + if (!n || argc) return JS_TRUE; + + if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_TRANSFORM, (GF_Node *)n, &par)) { + JSObject *mO = JS_NewObject(c, &svg_rt->matrixClass, 0, 0); + GF_Matrix2D *mx = malloc(sizeof(GF_Matrix2D)); + gf_mx2d_from_mx(mx, &par.mx); + JS_SetPrivate(c, mO, mx); + *rval = OBJECT_TO_JSVAL(mO); + return JS_TRUE; + } + return JS_TRUE; +} + +JSBool svg_udom_create_matrix_components(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_Matrix2D *mx; + JSObject *mat; + jsdouble v; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + if (argc!=6) return JS_TRUE; + + GF_SAFEALLOC(mx, GF_Matrix2D) + JS_ValueToNumber(c, argv[0], &v); + mx->m[0] = FLT2FIX(v); + JS_ValueToNumber(c, argv[1], &v); + mx->m[3] = FLT2FIX(v); + JS_ValueToNumber(c, argv[2], &v); + mx->m[1] = FLT2FIX(v); + JS_ValueToNumber(c, argv[3], &v); + mx->m[4] = FLT2FIX(v); + JS_ValueToNumber(c, argv[4], &v); + mx->m[2] = FLT2FIX(v); + JS_ValueToNumber(c, argv[5], &v); + mx->m[5] = FLT2FIX(v); + mat = JS_NewObject(c, &svg_rt->matrixClass, 0, 0); + JS_SetPrivate(c, mat, mx); + *rval = OBJECT_TO_JSVAL(mat); + return JS_TRUE; +} +JSBool svg_udom_create_rect(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + rectCI *rc; + JSObject *r; + GF_Node *n = dom_get_element(c, obj); + if (!n || argc) return JS_TRUE; + + GF_SAFEALLOC(rc, rectCI); + r = JS_NewObject(c, &svg_rt->rectClass, 0, 0); + JS_SetPrivate(c, r, rc); + *rval = OBJECT_TO_JSVAL(r); + return JS_TRUE; +} +JSBool svg_udom_create_point(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + pointCI *pt; + JSObject *r; + GF_Node *n = dom_get_element(c, obj); + if (!n || argc) return JS_TRUE; + + GF_SAFEALLOC(pt, pointCI); + r = JS_NewObject(c, &svg_rt->pointClass, 0, 0); + JS_SetPrivate(c, r, pt); + *rval = OBJECT_TO_JSVAL(r); + return JS_TRUE; +} +JSBool svg_udom_create_path(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + pathCI *path; + JSObject *p; + GF_Node *n = dom_get_element(c, obj); + if (!n || argc) return JS_TRUE; + + GF_SAFEALLOC(path, pathCI); + p = JS_NewObject(c, &svg_rt->pathClass, 0, 0); + JS_SetPrivate(c, p, path); + *rval = OBJECT_TO_JSVAL(p); + return JS_TRUE; +} +JSBool svg_udom_create_color(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + rgbCI *col; + JSObject *p; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + if (argc!=3) return JS_TRUE; + + GF_SAFEALLOC(col, rgbCI); + col->r = JSVAL_TO_INT(argv[0]); + col->g = JSVAL_TO_INT(argv[1]); + col->b = JSVAL_TO_INT(argv[2]); + p = JS_NewObject(c, &svg_rt->rgbClass, 0, 0); + JS_SetPrivate(c, p, col); + *rval = OBJECT_TO_JSVAL(p); + return JS_TRUE; +} + +JSBool svg_udom_move_focus(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_JSAPIParam par; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + if ((argc!=1) || !JSVAL_IS_OBJECT(argv[0])) return JS_TRUE; + + par.opt = JSVAL_TO_INT(argv[1]); + if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_FOCUS, (GF_Node *)n, &par)) + return JS_TRUE; + return JS_TRUE; +} +JSBool svg_udom_set_focus(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_JSAPIParam par; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + if ((argc!=1) || !JSVAL_IS_OBJECT(argv[0])) return JS_TRUE; + + par.node = dom_get_element(c, JSVAL_TO_OBJECT(argv[0])); + + /*NOT IN THE GRAPH*/ + if (!par.node || !par.node->sgprivate->num_instances) return JS_TRUE; + if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_FOCUS, (GF_Node *)n, &par)) + return JS_TRUE; + return JS_TRUE; +} +JSBool svg_udom_get_focus(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_JSAPIParam par; + GF_Node *n = dom_get_element(c, obj); + if (!n || argc) return JS_TRUE; + + *rval = JSVAL_VOID; + if (!ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_FOCUS, (GF_Node *)n, &par)) + return JS_TRUE; + + if (par.node) { + *rval = dom_element_construct(c, par.node); + } + return JS_TRUE; +} + +JSBool svg_udom_get_time(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble *d; + GF_Node *n = dom_get_element(c, obj); + if (!n) return JS_TRUE; + + d = JS_NewDouble(c, gf_node_get_scene_time(n)); + *rval = DOUBLE_TO_JSVAL(d); + return JS_TRUE; +} + + + +static JSBool svg_connection_set_encoding(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return JS_TRUE; +} +static JSBool svg_connection_create(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return JS_TRUE; +} +static JSBool svg_connection_connect(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return JS_TRUE; +} +static JSBool svg_connection_send(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return JS_TRUE; +} +static JSBool svg_connection_close(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return JS_TRUE; +} +static JSPropertySpec connectionProps[] = { + {"connected", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {0} +}; +static JSFunctionSpec connectionFuncs[] = { + /*eventTarget interface*/ + {"addEventListenerNS", dom_event_add_listener, 4}, + {"removeEventListenerNS", dom_event_remove_listener, 4}, + {"addEventListenerNS", dom_event_add_listener, 3}, + {"removeEventListenerNS", dom_event_remove_listener, 3}, + /*connection interface*/ + {"setEncoding", svg_connection_set_encoding, 1}, + {"connect", svg_connection_connect, 1}, + {"send", svg_connection_send, 1}, + {"close", svg_connection_close, 0}, + {0} +}; + +static void baseCI_finalize(JSContext *c, JSObject *obj) +{ + void *data = JS_GetPrivate(c, obj); + if (data) free(data); +} + +static JSBool rgb_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + if (!JS_InstanceOf(c, obj, &svg_rt->rgbClass, NULL) ) return JS_TRUE; + if (JSVAL_IS_INT(id)) { + rgbCI *col = JS_GetPrivate(c, obj); + if (!col) return JS_TRUE; + switch (JSVAL_TO_INT(id)) { + case 0: *vp = INT_TO_JSVAL(col->r); return JS_TRUE; + case 1: *vp = INT_TO_JSVAL(col->g); return JS_TRUE; + case 2: *vp = INT_TO_JSVAL(col->b); return JS_TRUE; + default: + return JS_TRUE; + } + } + return JS_TRUE; +} +static JSBool rgb_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + if (!JS_InstanceOf(c, obj, &svg_rt->rgbClass, NULL) ) return JS_TRUE; + if (JSVAL_IS_INT(id)) { + rgbCI *col = JS_GetPrivate(c, obj); + if (!col) return JS_TRUE; + switch (JSVAL_TO_INT(id)) { + case 0: col->r = JSVAL_TO_INT(*vp); return JS_TRUE; + case 1: col->g = JSVAL_TO_INT(*vp); return JS_TRUE; + case 2: col->b = JSVAL_TO_INT(*vp); return JS_TRUE; + default: return JS_TRUE; + } + } + return JS_TRUE; +} + + +static JSBool rect_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + if (!JS_InstanceOf(c, obj, &svg_rt->rectClass, NULL) ) return JS_TRUE; + if (JSVAL_IS_INT(id)) { + jsdouble *d; + rectCI *rc = JS_GetPrivate(c, obj); + if (!rc) return JS_TRUE; + if (rc->sg) { + GF_JSAPIParam par; + ScriptAction(rc->sg, GF_JSAPI_OP_GET_VIEWPORT, rc->sg->RootNode, &par); + rc->x = FIX2FLT(par.rc.x); + rc->y = FIX2FLT(par.rc.y); + rc->w = FIX2FLT(par.rc.width); + rc->h = FIX2FLT(par.rc.height); + } + switch (JSVAL_TO_INT(id)) { + case 0: d = JS_NewDouble(c, rc->x); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 1: d = JS_NewDouble(c, rc->y); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 2: d = JS_NewDouble(c, rc->w); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 3: d = JS_NewDouble(c, rc->h); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + default: return JS_TRUE; + } + } + return JS_TRUE; +} +static JSBool rect_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + if (!JS_InstanceOf(c, obj, &svg_rt->rectClass, NULL) ) return JS_TRUE; + if (JSVAL_IS_INT(id)) { + jsdouble d; + rectCI *rc = JS_GetPrivate(c, obj); + if (!rc) return JS_TRUE; + JS_ValueToNumber(c, *vp, &d); + switch (JSVAL_TO_INT(id)) { + case 0: rc->x = (Float) d; return JS_TRUE; + case 1: rc->y = (Float) d; return JS_TRUE; + case 2: rc->w = (Float) d; return JS_TRUE; + case 3: rc->h = (Float) d; return JS_TRUE; + default: return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool point_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + if (!JS_InstanceOf(c, obj, &svg_rt->pointClass, NULL) ) return JS_TRUE; + if (JSVAL_IS_INT(id)) { + jsdouble *d; + pointCI *pt = JS_GetPrivate(c, obj); + if (!pt) return JS_TRUE; + if (pt->sg) { + GF_JSAPIParam par; + ScriptAction(pt->sg, GF_JSAPI_OP_GET_TRANSLATE, pt->sg->RootNode, &par); + pt->x = FIX2FLT(par.pt.x); + pt->y = FIX2FLT(par.pt.y); + } + switch (JSVAL_TO_INT(id)) { + case 0: d = JS_NewDouble(c, pt->x); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 1: d = JS_NewDouble(c, pt->y); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + default: return JS_TRUE; + } + } + return JS_TRUE; +} +static JSBool point_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + if (!JS_InstanceOf(c, obj, &svg_rt->pointClass, NULL) ) return JS_TRUE; + if (JSVAL_IS_INT(id)) { + jsdouble d; + pointCI *pt = JS_GetPrivate(c, obj); + if (!pt) return JS_TRUE; + JS_ValueToNumber(c, *vp, &d); + switch (JSVAL_TO_INT(id)) { + case 0: pt->x = (Float) d; break; + case 1: pt->y = (Float) d; break; + default: return JS_TRUE; + } + if (pt->sg) { + GF_JSAPIParam par; + par.pt.x = FLT2FIX(pt->x); + par.pt.y = FLT2FIX(pt->y); + ScriptAction(pt->sg, GF_JSAPI_OP_SET_TRANSLATE, pt->sg->RootNode, &par); + } + return JS_TRUE; + } + return JS_TRUE; +} + +static JSObject *svg_new_path_object(JSContext *c, SVG_PathData *d) +{ +#if USE_GF_PATH + return NULL; +#else + JSObject *obj; + pathCI *p; + GF_SAFEALLOC(p, pathCI); + if (d) { + u32 i, count; + p->nb_coms = gf_list_count(d->commands); + p->tags = malloc(sizeof(u8) * p->nb_coms); + for (i=0; inb_coms; i++) p->tags[i] = * (u8 *) gf_list_get(d->commands, i); + count = gf_list_count(d->points); + p->pts = malloc(sizeof(pointCI) * count); + for (i=0; icommands, i); + p->pts[i].x = FIX2FLT(pt->x); + p->pts[i].y = FIX2FLT(pt->y); + } + } + obj = JS_NewObject(c, &svg_rt->pathClass, 0, 0); + JS_SetPrivate(c, obj, p); + return obj; +#endif +} + +static JSBool pathCI_constructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + pathCI *p; + GF_SAFEALLOC(p, pathCI); + JS_SetPrivate(c, obj, p); + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static void pathCI_finalize(JSContext *c, JSObject *obj) +{ + pathCI *p = JS_GetPrivate(c, obj); + if (p) { + if (p->pts) free(p->pts); + if (p->tags) free(p->tags); + free(p); + } +} + +static JSBool path_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + if (!JS_InstanceOf(c, obj, &svg_rt->pathClass, NULL) ) return JS_TRUE; + if (JSVAL_IS_INT(id)) { + pathCI *p = JS_GetPrivate(c, obj); + if (!p) return JS_TRUE; + switch (JSVAL_TO_INT(id)) { + case 0: *vp = INT_TO_JSVAL(p->nb_coms); return JS_TRUE; + default: return JS_TRUE; + } + } + return JS_TRUE; +} +static JSBool svg_path_get_segment(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + pathCI *p; + u32 idx; + if (!JS_InstanceOf(c, obj, &svg_rt->pathClass, NULL) ) return JS_TRUE; + p = JS_GetPrivate(c, obj); + if (!p) return JS_TRUE; + if ((argc!=1) || !JSVAL_IS_INT(argv[0])) return JS_TRUE; + idx = JSVAL_TO_INT(argv[0]); + if (idx>=p->nb_coms) return JS_TRUE; + switch (p->tags[idx]) { + case 0: *vp = INT_TO_JSVAL(77); return JS_TRUE; /* Move To */ + case 1: *vp = INT_TO_JSVAL(76); return JS_TRUE; /* Line To */ + case 2:/* Curve To */ + case 3:/* next Curve To */ + *vp = INT_TO_JSVAL(67); return JS_TRUE; + case 4:/* Quad To */ + case 5:/* next Quad To */ + *vp = INT_TO_JSVAL(81); return JS_TRUE; + case 6: *vp = INT_TO_JSVAL(90); return JS_TRUE; /* Close */ + } + return JS_TRUE; +} + +static JSBool svg_path_get_segment_param(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + jsdouble *d; + pathCI *p; + ptCI *pt; + u32 i, idx, param_idx, pt_idx; + if (!JS_InstanceOf(c, obj, &svg_rt->pathClass, NULL) ) return JS_TRUE; + p = JS_GetPrivate(c, obj); + if (!p) return JS_TRUE; + if ((argc!=2) || !JSVAL_IS_INT(argv[0]) || !JSVAL_IS_INT(argv[1])) return JS_TRUE; + idx = JSVAL_TO_INT(argv[0]); + param_idx = JSVAL_TO_INT(argv[1]); + if (idx>=p->nb_coms) return JS_TRUE; + pt_idx = 0; + for (i=0; itags[i]) { + case 0: pt_idx++; break; + case 1: pt_idx++; break; + case 2: pt_idx+=3; break; + case 3: pt_idx+=2; break; + case 4: pt_idx+=2; break; + case 5: pt_idx+=1; break; + } + } + switch (p->tags[idx]) { + case 0: + case 1: + if (param_idx>1) return JS_TRUE; + pt = &p->pts[pt_idx]; + d = JS_NewDouble(c, param_idx ? pt->y : pt->x); + *vp = DOUBLE_TO_JSVAL(d); + return JS_TRUE; + case 2:/* Curve To */ + if (param_idx>5) return JS_TRUE; + pt = &p->pts[pt_idx + (param_idx/2) ]; + d = JS_NewDouble(c, (param_idx%2) ? pt->y : pt->x); + *vp = DOUBLE_TO_JSVAL(d); + return JS_TRUE; + case 3:/* Next Curve To */ + if (param_idx>5) return JS_TRUE; + if (param_idx<2) { + pt = &p->pts[pt_idx - 1]; + d = JS_NewDouble(c, param_idx ? pt->y : pt->x); + } else { + param_idx-=2; + pt = &p->pts[pt_idx + (param_idx/2)]; + d = JS_NewDouble(c, (param_idx%2) ? pt->y : pt->x); + } + *vp = DOUBLE_TO_JSVAL(d); + return JS_TRUE; + case 4:/* Quad To */ + if (param_idx>3) return JS_TRUE; + pt = &p->pts[pt_idx + (param_idx/2) ]; + d = JS_NewDouble(c, (param_idx%2) ? pt->y : pt->x); + *vp = DOUBLE_TO_JSVAL(d); + return JS_TRUE; + case 5:/* Next Quad To */ + if (param_idx>3) return JS_TRUE; + if (param_idx<2) { + pt = &p->pts[pt_idx - 1]; + d = JS_NewDouble(c, param_idx ? pt->y : pt->x); + } else { + param_idx-=2; + pt = &p->pts[pt_idx]; + d = JS_NewDouble(c, param_idx ? pt->y : pt->x); + } + *vp = DOUBLE_TO_JSVAL(d); + return JS_TRUE; + /*spec is quite obscur here*/ + case 6: + d = JS_NewDouble(c, 0); + *vp = DOUBLE_TO_JSVAL(d); + return JS_TRUE; + } + return JS_TRUE; +} + +static u32 svg_path_realloc_pts(pathCI *p, u32 nb_pts) +{ + u32 i, orig_pts; + orig_pts = 0; + for (i=0; inb_coms; i++) { + switch (p->tags[i]) { + case 0: orig_pts++; break; + case 1: orig_pts++; break; + case 2: orig_pts+=3; break; + case 3: orig_pts+=2; break; + case 4: orig_pts+=2; break; + case 5: orig_pts+=1; break; + } + } + p->pts = realloc(p->pts, sizeof(ptCI)*(nb_pts+orig_pts)); + return orig_pts; +} +static JSBool svg_path_move_to(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + pathCI *p; + jsdouble x, y; + u32 nb_pts; + if (!JS_InstanceOf(c, obj, &svg_rt->pathClass, NULL) ) return JS_TRUE; + p = JS_GetPrivate(c, obj); + if (!p) return JS_TRUE; + if ((argc!=2) || !JSVAL_IS_NUMBER(argv[0]) || !JSVAL_IS_NUMBER(argv[1])) return JS_TRUE; + JS_ValueToNumber(c, argv[0], &x); + JS_ValueToNumber(c, argv[1], &y); + nb_pts = svg_path_realloc_pts(p, 1); + p->pts[nb_pts].x = (Float) x; + p->pts[nb_pts].y = (Float) y; + p->tags = realloc(p->tags, sizeof(u8)*(p->nb_coms+1) ); + p->tags[p->nb_coms] = 0; + p->nb_coms++; + return JS_TRUE; +} +static JSBool svg_path_line_to(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + pathCI *p; + jsdouble x, y; + u32 nb_pts; + if (!JS_InstanceOf(c, obj, &svg_rt->pathClass, NULL) ) return JS_TRUE; + p = JS_GetPrivate(c, obj); + if (!p) return JS_TRUE; + if ((argc!=2) || !JSVAL_IS_NUMBER(argv[0]) || !JSVAL_IS_NUMBER(argv[1])) return JS_TRUE; + JS_ValueToNumber(c, argv[0], &x); + JS_ValueToNumber(c, argv[1], &y); + nb_pts = svg_path_realloc_pts(p, 1); + p->pts[nb_pts].x = (Float) x; + p->pts[nb_pts].y = (Float) y; + p->tags = realloc(p->tags, sizeof(u8)*(p->nb_coms+1) ); + p->tags[p->nb_coms] = 1; + p->nb_coms++; + return JS_TRUE; +} + +static JSBool svg_path_quad_to(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + pathCI *p; + jsdouble x1, y1, x2, y2; + u32 nb_pts; + if (!JS_InstanceOf(c, obj, &svg_rt->pathClass, NULL) ) return JS_TRUE; + p = JS_GetPrivate(c, obj); + if (!p) return JS_TRUE; + if ((argc!=4) || !JSVAL_IS_NUMBER(argv[0]) || !JSVAL_IS_NUMBER(argv[1]) || !JSVAL_IS_NUMBER(argv[2]) || !JSVAL_IS_NUMBER(argv[3])) return JS_TRUE; + JS_ValueToNumber(c, argv[0], &x1); + JS_ValueToNumber(c, argv[1], &y1); + JS_ValueToNumber(c, argv[2], &x2); + JS_ValueToNumber(c, argv[3], &y2); + nb_pts = svg_path_realloc_pts(p, 2); + p->pts[nb_pts].x = (Float) x1; p->pts[nb_pts].y = (Float) y1; + p->pts[nb_pts+1].x = (Float) x2; p->pts[nb_pts+1].y = (Float) y2; + p->tags = realloc(p->tags, sizeof(u8)*(p->nb_coms+1) ); + p->tags[p->nb_coms] = 4; + p->nb_coms++; + return JS_TRUE; +} +static JSBool svg_path_curve_to(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + pathCI *p; + jsdouble x1, y1, x2, y2, x, y; + u32 nb_pts; + if (!JS_InstanceOf(c, obj, &svg_rt->pathClass, NULL) ) return JS_TRUE; + p = JS_GetPrivate(c, obj); + if (!p) return JS_TRUE; + if ((argc!=6) || !JSVAL_IS_NUMBER(argv[0]) || !JSVAL_IS_NUMBER(argv[1]) || !JSVAL_IS_NUMBER(argv[2]) || !JSVAL_IS_NUMBER(argv[3]) || !JSVAL_IS_NUMBER(argv[4]) || !JSVAL_IS_NUMBER(argv[5])) return JS_TRUE; + JS_ValueToNumber(c, argv[0], &x1); + JS_ValueToNumber(c, argv[1], &y1); + JS_ValueToNumber(c, argv[2], &x2); + JS_ValueToNumber(c, argv[3], &y2); + JS_ValueToNumber(c, argv[4], &x); + JS_ValueToNumber(c, argv[5], &y); + nb_pts = svg_path_realloc_pts(p, 3); + p->pts[nb_pts].x = (Float) x1; p->pts[nb_pts].y = (Float) y1; + p->pts[nb_pts+1].x = (Float) x2; p->pts[nb_pts+1].y = (Float) y2; + p->pts[nb_pts+2].x = (Float) x; p->pts[nb_pts+2].y = (Float) y; + p->tags = realloc(p->tags, sizeof(u8)*(p->nb_coms+1) ); + p->tags[p->nb_coms] = 2; + p->nb_coms++; + return JS_TRUE; +} +static JSBool svg_path_close(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + pathCI *p; + if (!JS_InstanceOf(c, obj, &svg_rt->pathClass, NULL) ) return JS_TRUE; + p = JS_GetPrivate(c, obj); + if (!p) return JS_TRUE; + if (argc) return JS_TRUE; + p->tags = realloc(p->tags, sizeof(u8)*(p->nb_coms+1) ); + p->tags[p->nb_coms] = 6; + p->nb_coms++; + return JS_TRUE; +} + +static JSBool matrix_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + jsdouble *d; + GF_Matrix2D *mx; + if (!JS_InstanceOf(c, obj, &svg_rt->matrixClass, NULL) ) return JS_TRUE; + mx = JS_GetPrivate(c, obj); + if (!JSVAL_IS_INT(id)) return JS_TRUE; + + if (!mx) return JS_TRUE; + switch (JSVAL_TO_INT(id)) { + case 0: d = JS_NewDouble(c, FIX2FLT(mx->m[0])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 1: d = JS_NewDouble(c, FIX2FLT(mx->m[3])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 2: d = JS_NewDouble(c, FIX2FLT(mx->m[1])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 3: d = JS_NewDouble(c, FIX2FLT(mx->m[4])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 4: d = JS_NewDouble(c, FIX2FLT(mx->m[2])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 5: d = JS_NewDouble(c, FIX2FLT(mx->m[5])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + default: return JS_TRUE; + } + return JS_TRUE; +} +static JSBool matrix_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + jsdouble d; + GF_Matrix2D *mx; + if (!JS_InstanceOf(c, obj, &svg_rt->matrixClass, NULL) ) return JS_TRUE; + mx = JS_GetPrivate(c, obj); + if (!JSVAL_IS_INT(id)) return JS_TRUE; + + JS_ValueToNumber(c, *vp, &d); + switch (JSVAL_TO_INT(id)) { + case 0: mx->m[0] = FLT2FIX(d); break; + case 1: mx->m[3] = FLT2FIX(d); break; + case 2: mx->m[1] = FLT2FIX(d); break; + case 3: mx->m[4] = FLT2FIX(d); break; + case 4: mx->m[2] = FLT2FIX(d); break; + case 5: mx->m[5] = FLT2FIX(d); break; + default: return JS_TRUE; + } + return JS_TRUE; +} +static JSBool svg_mx2d_get_component(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + jsdouble *d; + GF_Matrix2D *mx; + if (!JS_InstanceOf(c, obj, &svg_rt->matrixClass, NULL) ) return JS_TRUE; + mx = JS_GetPrivate(c, obj); + if (!mx || (argc!=1)) return JS_TRUE; + if (!JSVAL_IS_INT(argv[0])) return JS_TRUE; + switch (JSVAL_TO_INT(argv[0])) { + case 0: d = JS_NewDouble(c, FIX2FLT(mx->m[0])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 1: d = JS_NewDouble(c, FIX2FLT(mx->m[3])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 2: d = JS_NewDouble(c, FIX2FLT(mx->m[1])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 3: d = JS_NewDouble(c, FIX2FLT(mx->m[4])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 4: d = JS_NewDouble(c, FIX2FLT(mx->m[2])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + case 5: d = JS_NewDouble(c, FIX2FLT(mx->m[5])); *vp = DOUBLE_TO_JSVAL(d); return JS_TRUE; + } + return JS_TRUE; +} + +static JSBool svg_mx2d_multiply(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + JSObject *mat; + GF_Matrix2D *mx1, *mx2; + if (!JS_InstanceOf(c, obj, &svg_rt->matrixClass, NULL) ) return JS_TRUE; + mx1 = JS_GetPrivate(c, obj); + if (!mx1 || (argc!=1)) return JS_TRUE; + if (!JSVAL_IS_OBJECT(argv[0])) return JS_TRUE; + mat = JSVAL_TO_OBJECT(argv[0]); + if (!JS_InstanceOf(c, mat, &svg_rt->matrixClass, NULL) ) return JS_TRUE; + mx2 = JS_GetPrivate(c, mat); + if (!mx2) return JS_TRUE; + gf_mx2d_add_matrix(mx1, mx2); + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool svg_mx2d_inverse(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + GF_Matrix2D *mx1; + if (!JS_InstanceOf(c, obj, &svg_rt->matrixClass, NULL) ) return JS_TRUE; + mx1 = JS_GetPrivate(c, obj); + if (!mx1) return JS_TRUE; + gf_mx2d_inverse(mx1); + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool svg_mx2d_translate(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + jsdouble x, y; + GF_Matrix2D *mx1, mx2; + if (!JS_InstanceOf(c, obj, &svg_rt->matrixClass, NULL) ) return JS_TRUE; + mx1 = JS_GetPrivate(c, obj); + if (!mx1 || (argc!=2)) return JS_TRUE; + JS_ValueToNumber(c, argv[0], &x); + JS_ValueToNumber(c, argv[1], &y); + + gf_mx2d_init(mx2); + mx2.m[2] = FLT2FIX(x); + mx2.m[5] = FLT2FIX(y); + gf_mx2d_pre_multiply(mx1, &mx2); + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool svg_mx2d_scale(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + jsdouble scale; + GF_Matrix2D *mx1, mx2; + if (!JS_InstanceOf(c, obj, &svg_rt->matrixClass, NULL) ) return JS_TRUE; + mx1 = JS_GetPrivate(c, obj); + if (!mx1 || (argc!=1)) return JS_TRUE; + JS_ValueToNumber(c, argv[0], &scale); + gf_mx2d_init(mx2); + mx2.m[0] = mx2.m[4] = FLT2FIX(scale); + gf_mx2d_pre_multiply(mx1, &mx2); + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool svg_mx2d_rotate(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *vp) +{ + jsdouble angle; + GF_Matrix2D *mx1, mx2; + if (!JS_InstanceOf(c, obj, &svg_rt->matrixClass, NULL) ) return JS_TRUE; + mx1 = JS_GetPrivate(c, obj); + if (!mx1 || (argc!=1)) return JS_TRUE; + JS_ValueToNumber(c, argv[0], &angle); + gf_mx2d_init(mx2); + gf_mx2d_add_rotation(&mx2, 0, 0, gf_mulfix(FLT2FIX(angle/180), GF_PI)); + gf_mx2d_pre_multiply(mx1, &mx2); + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +jsval svg_udom_new_rect(JSContext *c, Fixed x, Fixed y, Fixed width, Fixed height) +{ + JSObject *r = JS_NewObject(c, &svg_rt->rectClass, 0, 0); + rectCI *rc = malloc(sizeof(rectCI)); + rc->x = FIX2FLT(x); + rc->y = FIX2FLT(y); + rc->w = FIX2FLT(width); + rc->h = FIX2FLT(height); + rc->sg = NULL; + JS_SetPrivate(c, r, rc); + return OBJECT_TO_JSVAL(r); +} + +jsval svg_udom_new_point(JSContext *c, Fixed x, Fixed y) +{ + JSObject *p = JS_NewObject(c, &svg_rt->pointClass, 0, 0); + pointCI *pt = malloc(sizeof(pointCI)); + pt->x = FIX2FLT(x); + pt->y = FIX2FLT(y); + pt->sg = NULL; + JS_SetPrivate(c, p, pt); + return OBJECT_TO_JSVAL(p); +} + +void *svg_get_element_class(GF_Node *n) +{ + return &svg_rt->svgElement; +} +void *svg_get_document_class(GF_SceneGraph *n) +{ + return &svg_rt->svgDocument; +} + + +static void svg_init_js_api(GF_SceneGraph *scene) +{ + JSObject *proto; + JS_SetContextPrivate(scene->svg_js->js_ctx, scene); + JS_SetErrorReporter(scene->svg_js->js_ctx, svg_script_error); + + /*init global object*/ + scene->svg_js->global = JS_NewObject(scene->svg_js->js_ctx, &svg_rt->globalClass, 0, 0 ); + JS_InitStandardClasses(scene->svg_js->js_ctx, scene->svg_js->global); + /*remember pointer to scene graph!!*/ + JS_SetPrivate(scene->svg_js->js_ctx, scene->svg_js->global, scene); + { + JSPropertySpec globalClassProps[] = { + {"connected", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"parent", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {0} + }; + JSFunctionSpec globalClassFuncs[] = { + {"createConnection", svg_connection_create, 0}, + {"gotoLocation", svg_nav_to_location, 1}, + {"alert", svg_echo, 0}, + /*technically, this is part of Implementation interface, not global, but let's try not to complicate things too much*/ + {"hasFeature", dom_imp_has_feature, 2}, + {"parseXML", svg_parse_xml, 0}, + {0} + }; + JS_DefineFunctions(scene->svg_js->js_ctx, scene->svg_js->global, globalClassFuncs); + JS_DefineProperties(scene->svg_js->js_ctx, scene->svg_js->global, globalClassProps); + } + + /*initialize DOM core */ + dom_js_load(scene, scene->svg_js->js_ctx, scene->svg_js->global); + + /*user-defined extensions*/ + gf_sg_load_script_extensions(scene, scene->svg_js->js_ctx, scene->svg_js->global, 0); + + svg_define_udom_exception(scene->svg_js->js_ctx, scene->svg_js->global); + + /*SVGDocument class*/ + { + + JSPropertySpec svgDocumentProps[] = { + {"global", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + /*in our implementation, defaultView is just an alias to the global object*/ + {"defaultView", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + + {0} + }; + JSObject *doc_proto = dom_js_get_document_proto(scene->svg_js->js_ctx); + JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, doc_proto, &svg_rt->svgDocument, 0, 0, svgDocumentProps, 0, 0, 0); + } + + /*SVGElement class*/ + { + JSFunctionSpec svgElementFuncs[] = { + /*trait access interface*/ + {"getTrait", svg_udom_get_trait, 1}, + {"getTraitNS", svg_udom_get_trait, 2}, + {"getFloatTrait", svg_udom_get_float_trait, 1}, + {"getMatrixTrait", svg_udom_get_matrix_trait, 1}, + {"getRectTrait", svg_udom_get_rect_trait, 1}, + {"getPathTrait", svg_udom_get_path_trait, 1}, + {"getRGBColorTrait", svg_udom_get_rgb_color_trait, 1}, + /*FALLBACK TO BASE-VALUE FOR NOW - WILL NEED EITHER DOM TREE-CLONING OR A FAKE RENDER + PASS FOR EACH PRESENTATION VALUE ACCESS*/ + {"getPresentationTrait", svg_udom_get_trait, 1}, + {"getPresentationTraitNS", svg_udom_get_trait, 2}, + {"getFloatPresentationTrait", svg_udom_get_float_trait, 1}, + {"getMatrixPresentationTrait", svg_udom_get_matrix_trait, 1}, + {"getRectPresentationTrait", svg_udom_get_rect_trait, 1}, + {"getPathPresentationTrait", svg_udom_get_path_trait, 1}, + {"getRGBColorPresentationTrait", svg_udom_get_rgb_color_trait, 1}, + {"setTrait", svg_udom_set_trait, 2}, + {"setTraitNS", svg_udom_set_trait, 3}, + {"setFloatTrait", svg_udom_set_float_trait, 2}, + {"setMatrixTrait", svg_udom_set_matrix_trait, 2}, + {"setRectTrait", svg_udom_set_rect_trait, 2}, + {"setPathTrait", svg_udom_set_path_trait, 2}, + {"setRGBColorTrait", svg_udom_set_rgb_color_trait, 2}, + /*locatable interface*/ + {"getBBox", svg_udom_get_local_bbox, 0}, + {"getScreenCTM", svg_udom_get_screen_ctm, 0}, + {"getScreenBBox", svg_udom_get_screen_bbox, 0}, + /*svgSVGElement interface*/ + {"createSVGMatrixComponents", svg_udom_create_matrix_components, 0}, + {"createSVGRect", svg_udom_create_rect, 0}, + {"createSVGPath", svg_udom_create_path, 0}, + {"createSVGRGBColor", svg_udom_create_color, 0}, + {"createSVGPoint", svg_udom_create_point, 0}, + + {"moveFocus", svg_udom_move_focus, 0}, + {"setFocus", svg_udom_set_focus, 0}, + {"getCurrentFocusedObject", svg_udom_get_focus, 0}, + {"getCurrentTime", svg_udom_get_time, 0}, + + /*timeControl interface*/ + {"beginElementAt", svg_udom_smil_begin, 1}, + {"beginElement", svg_udom_smil_begin, 0}, + {"endElementAt", svg_udom_smil_end, 1}, + {"endElement", svg_udom_smil_end, 0}, + {"pauseElement", svg_udom_smil_pause, 0}, + {"resumeElement", svg_udom_smil_resume, 0}, + {0} + }; + + JSPropertySpec svgElementProps[] = { + /*svgElement interface*/ + {"id", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + /*elementTraversal interface - all SVGElement implement this*/ + {"firstElementChild", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"lastElementChild", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"previousElementSibling", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"nextElementSibling", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + /*svgSVGElement interface*/ + {"currentScale", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"currentRotate", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"currentTranslate", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"viewport", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"currentTime", 9, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + /*timeControl interface*/ + {"isPaused", 10, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + /*old SVG1.1 stuff*/ + {"ownerSVGElement", 11, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + /*SVGElementInstance*/ + {"correspondingElement", 12, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {"correspondingUseElement", 13, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {0} + }; + JSObject *elt_proto = dom_js_get_element_proto(scene->svg_js->js_ctx); + JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, elt_proto, &svg_rt->svgElement, 0, 0, svgElementProps, svgElementFuncs, 0, 0); + + } + + /*RGBColor class*/ + { + JSPropertySpec rgbClassProps[] = { + {"red", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"green", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"blue", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {0} + }; + JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &svg_rt->rgbClass, 0, 0, rgbClassProps, 0, 0, 0); + } + /*SVGRect class*/ + { + JSPropertySpec rectClassProps[] = { + {"x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"width", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"height", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {0} + }; + JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &svg_rt->rectClass, 0, 0, rectClassProps, 0, 0, 0); + } + /*SVGPoint class*/ + { + JSPropertySpec pointClassProps[] = { + {"x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {0} + }; + JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &svg_rt->pointClass, 0, 0, pointClassProps, 0, 0, 0); + } + /*SVGMatrix class*/ + { + JSFunctionSpec matrixClassFuncs[] = { + {"getComponent", svg_mx2d_get_component, 1}, + {"mMultiply", svg_mx2d_multiply, 1}, + {"inverse", svg_mx2d_inverse, 0}, + {"mTranslate", svg_mx2d_translate, 2}, + {"mScale", svg_mx2d_scale, 1}, + {"mRotate", svg_mx2d_rotate, 1}, + {0} + }; + JSPropertySpec matrixClassProps[] = { + {"a", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"b", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"c", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"d", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"e", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {"f", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + {0} + }; + JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &svg_rt->matrixClass, 0, 0, matrixClassProps, matrixClassFuncs, 0, 0); + } + /*SVGPath class*/ + { + JSFunctionSpec pathClassFuncs[] = { + {"getSegment", svg_path_get_segment, 1}, + {"getSegmentParam", svg_path_get_segment_param, 2}, + {"moveTo", svg_path_move_to, 2}, + {"lineTo", svg_path_line_to, 2}, + {"quadTo", svg_path_quad_to, 4}, + {"curveTo", svg_path_curve_to, 6}, + {"close", svg_path_close, 0}, + {0} + }; + JSPropertySpec pathClassProps[] = { + {"numberOfSegments", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY}, + {0} + }; + proto = JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &svg_rt->pathClass, 0, 0, pathClassProps, pathClassFuncs, 0, 0); + JS_DefineProperty(scene->svg_js->js_ctx, proto, "MOVE_TO", INT_TO_JSVAL(77), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(scene->svg_js->js_ctx, proto, "LINE_TO", INT_TO_JSVAL(76), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(scene->svg_js->js_ctx, proto, "CURVE_TO", INT_TO_JSVAL(67), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(scene->svg_js->js_ctx, proto, "QUAD_TO", INT_TO_JSVAL(81), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + JS_DefineProperty(scene->svg_js->js_ctx, proto, "CLOSE", INT_TO_JSVAL(90), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); + + } + /*we have our own constructors*/ + dom_set_class_selector(scene->svg_js->js_ctx, svg_get_element_class, svg_get_document_class); + + /*create document object*/ + dom_js_define_document(scene->svg_js->js_ctx, scene->svg_js->global, scene); + /*create event object, and remember it*/ + scene->svg_js->event = dom_js_define_event(scene->svg_js->js_ctx, scene->svg_js->global); +} + +Bool svg_script_execute(GF_SceneGraph *sg, char *utf8_script, GF_DOM_Event *event) +{ + char szFuncName[1024]; + JSBool ret; + jsval rval; + GF_DOM_Event *prev_event = NULL; + char *sep = strchr(utf8_script, '('); + + if (!sep) { + strcpy(szFuncName, utf8_script); + strcat(szFuncName, "(evt)"); + utf8_script = szFuncName; + } + prev_event = JS_GetPrivate(sg->svg_js->js_ctx, sg->svg_js->event); + JS_SetPrivate(sg->svg_js->js_ctx, sg->svg_js->event, event); + ret = JS_EvaluateScript(sg->svg_js->js_ctx, sg->svg_js->global, utf8_script, strlen(utf8_script), 0, 0, &rval); + JS_SetPrivate(sg->svg_js->js_ctx, sg->svg_js->event, prev_event); + + /*clean-up*/ + JS_GC(sg->svg_js->js_ctx); + + if (ret==JS_FALSE) { + char *sep = strchr(utf8_script, '('); + if (!sep) return 0; + sep[0] = 0; + JS_LookupProperty(sg->svg_js->js_ctx, sg->svg_js->global, utf8_script, &rval); + sep[0] = '('; + if (JSVAL_IS_VOID(rval)) return 0; + } + return 1; +} + +static void svg_script_predestroy(GF_Node *n, void *eff, Bool is_destroy) +{ + if (is_destroy) { + GF_SVGJS *svg_js = n->sgprivate->scenegraph->svg_js; + /*unregister script from parent scene (cf base_scenegraph::sg_reset) */ + gf_list_del_item(n->sgprivate->scenegraph->scripts, n); + + if (svg_js->nb_scripts) { + svg_js->nb_scripts--; + if (!svg_js->nb_scripts) { + /*user-defined extensions*/ + gf_sg_load_script_extensions(n->sgprivate->scenegraph, svg_js->js_ctx, svg_js->global, 1); + gf_sg_ecmascript_del(svg_js->js_ctx); + dom_js_unload(svg_js->js_ctx, svg_js->global); + free(svg_js); + n->sgprivate->scenegraph->svg_js = NULL; + assert(svg_rt); + svg_rt->nb_inst--; + if (!svg_rt->nb_inst) { + free(svg_rt); + svg_rt = NULL; + } + } else { + u32 i, count; + /*detach this script from our object cache*/ + count = gf_list_count(n->sgprivate->scenegraph->objects); + for (i=0; isgprivate->scenegraph->objects, i); + GF_Node *a_node = JS_GetPrivate(svg_js->js_ctx, obj); + if (n==a_node) + JS_SetPrivate(svg_js->js_ctx, obj, NULL); + } + + } + } + } +} + +static GF_Err JSScript_CreateSVGContext(GF_SceneGraph *sg) +{ + GF_SVGJS *svg_js; + GF_SAFEALLOC(svg_js, GF_SVGJS); + /*create new ecmascript context*/ + svg_js->js_ctx = gf_sg_ecmascript_new(sg); + if (!svg_js->js_ctx) { + free(svg_js); + return GF_SCRIPT_ERROR; + } + if (!svg_rt) { + GF_SAFEALLOC(svg_rt, GF_SVGuDOM); + JS_SETUP_CLASS(svg_rt->svgElement, "SVGElement", JSCLASS_HAS_PRIVATE, svg_element_getProperty, svg_element_setProperty, dom_element_finalize); + JS_SETUP_CLASS(svg_rt->svgDocument, "SVGDocument", JSCLASS_HAS_PRIVATE, svg_doc_getProperty, JS_PropertyStub, dom_document_finalize); + JS_SETUP_CLASS(svg_rt->globalClass, "global", JSCLASS_HAS_PRIVATE, global_getProperty, JS_PropertyStub, JS_FinalizeStub); + JS_SETUP_CLASS(svg_rt->rgbClass, "SVGRGBColor", JSCLASS_HAS_PRIVATE, rgb_getProperty, rgb_setProperty, baseCI_finalize); + JS_SETUP_CLASS(svg_rt->rectClass, "SVGRect", JSCLASS_HAS_PRIVATE, rect_getProperty, rect_setProperty, baseCI_finalize); + JS_SETUP_CLASS(svg_rt->pointClass, "SVGPoint", JSCLASS_HAS_PRIVATE, point_getProperty, point_setProperty, baseCI_finalize); + JS_SETUP_CLASS(svg_rt->matrixClass, "SVGMatrix", JSCLASS_HAS_PRIVATE, matrix_getProperty, matrix_setProperty, baseCI_finalize); + JS_SETUP_CLASS(svg_rt->pathClass, "SVGPath", JSCLASS_HAS_PRIVATE, path_getProperty, JS_PropertyStub, pathCI_finalize); + } + svg_rt->nb_inst++; + + sg->svg_js = svg_js; + /*load SVG & DOM APIs*/ + svg_init_js_api(sg); + + svg_js->script_execute = svg_script_execute; + svg_js->handler_execute = svg_script_execute_handler; + return GF_OK; +} + +GF_DOMText *svg_get_text_child(GF_Node *node) +{ + GF_ChildNodeItem *child; + GF_DOMText *txt; + txt = NULL; + child = ((SVG_Element*)node)->children; + if (! child) return NULL; + while (child) { + txt = (GF_DOMText*)child->node; + if ((txt->sgprivate->tag==TAG_DOMText) && txt->textContent) return txt; + txt = NULL; + child = child->next; + } + return NULL; +} + +static Bool svg_js_load_script(GF_Node *script, char *file) +{ + FILE *jsf; + char *jsscript; + u32 fsize; + Bool success = 1; + JSBool ret; + jsval rval; + GF_SVGJS *svg_js; + + svg_js = script->sgprivate->scenegraph->svg_js; + jsf = fopen(file, "rb"); + if (!jsf) { + GF_JSAPIParam par; + GF_SceneGraph *scene = script->sgprivate->scenegraph; + char *abs_url = NULL; + par.uri.url = file; + if (scene->script_action && scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_RESOLVE_XLINK, script, &par)) + abs_url = (char *) par.uri.url; + + if (abs_url) { + jsf = fopen(abs_url, "rb"); + free(abs_url); + } + } + if (!jsf) return 0; + + fseek(jsf, 0, SEEK_END); + fsize = ftell(jsf); + fseek(jsf, 0, SEEK_SET); + jsscript = malloc(sizeof(char)*(fsize+1)); + fread(jsscript, sizeof(char)*fsize, 1, jsf); + fclose(jsf); + jsscript[fsize] = 0; + + /*for handler, only load code*/ + if (script->sgprivate->tag==TAG_SVG_handler) { + GF_DOMText *txt = gf_dom_add_text_node(script, jsscript); + txt->type = GF_DOM_TEXT_INSERTED; + return 1; + } + + ret = JS_EvaluateScript(svg_js->js_ctx, svg_js->global, jsscript, sizeof(char)*fsize, 0, 0, &rval); + if (ret==JS_FALSE) success = 0; + gf_dom_listener_process_add(script->sgprivate->scenegraph); + + free(jsscript); + return success; +} + +#include +#include + +/* Download is performed asynchronously, so the script loading must be performed in this function */ +static void JS_SVG_NetIO(void *cbck, GF_NETIO_Parameter *param) +{ + GF_Err e; + JSFileDownload *jsdnload = (JSFileDownload *)cbck; + GF_Node *node = jsdnload->node; + + e = param->error; + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + const char *szCache = gf_dm_sess_get_cache_name(jsdnload->sess); + if (!svg_js_load_script(jsdnload->node, (char *) szCache)) + e = GF_SCRIPT_ERROR; + else + e = GF_OK; + + } else { + if (!e) { + /* the download is going on */ + return; + } else { + /* there was an error during the download */ + } + } + + /*destroy current download session (ie, destroy ourselves)*/ + gf_dm_sess_del(jsdnload->sess); + free(jsdnload); + + if (e) { + GF_JSAPIParam par; + par.info.e = e; + par.info.msg = "Cannot fetch script"; + ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_MESSAGE, NULL, &par); + } +} + +void JSScript_LoadSVG(GF_Node *node) +{ + GF_DOMText *txt; + GF_SVGJS *svg_js; + JSBool ret; + jsval rval; + GF_FieldInfo href_info; + + if (!node->sgprivate->scenegraph->svg_js) { + if (JSScript_CreateSVGContext(node->sgprivate->scenegraph) != GF_OK) return; + } + + /*register script with parent scene (cf base_scenegraph::sg_reset) */ + gf_list_add(node->sgprivate->scenegraph->scripts, node); + + svg_js = node->sgprivate->scenegraph->svg_js; + if (!node->sgprivate->UserCallback) { + svg_js->nb_scripts++; + node->sgprivate->UserCallback = svg_script_predestroy; + } + /*if href download the script file*/ + if (gf_node_get_attribute_by_tag(node, TAG_XLINK_ATT_href, 0, 0, &href_info) == GF_OK) { + GF_JSAPIParam par; + char *url; + GF_Err e; + JSFileDownload *jsdnload; + XMLRI *xmlri = (XMLRI *)href_info.far_ptr; + + /* getting the base uri of the scene and concatenating */ + ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_GET_SCENE_URI, node, &par); + url = NULL; + if (par.uri.url) url = gf_url_concatenate(par.uri.url, xmlri->string); + if (!url) url = strdup(xmlri->string); + + /* if the file is local, we don't need to download it */ + if (!strstr(url, "://") || !strnicmp(url, "file://", 7)) { + svg_js_load_script(node, url); + } else { + + /* getting a download manager */ + par.dnld_man = NULL; + ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, NULL, &par); + if (par.dnld_man) { + + GF_SAFEALLOC(jsdnload, JSFileDownload); + jsdnload->node = node; + jsdnload->sess = gf_dm_sess_new(par.dnld_man, url, 0, JS_SVG_NetIO, jsdnload, &e); + if (!jsdnload->sess) { + free(jsdnload); + } + } + } + free(url); + } + /*for scripts only, execute*/ + else if (node->sgprivate->tag == TAG_SVG_script) { + txt = svg_get_text_child(node); + if (!txt) return; + ret = JS_EvaluateScript(svg_js->js_ctx, svg_js->global, txt->textContent, strlen(txt->textContent), 0, 0, &rval); + if (ret==JS_FALSE) { + _ScriptMessage(node->sgprivate->scenegraph, GF_SCRIPT_ERROR, "SVG: Invalid script"); + } + gf_dom_listener_process_add(node->sgprivate->scenegraph); + } +} + +static Bool svg_script_execute_handler(GF_Node *node, GF_DOM_Event *event, GF_Node *observer) +{ + GF_DOMText *txt = NULL; + GF_SVGJS *svg_js; + JSObject *__this; + JSBool ret = JS_TRUE; + GF_DOM_Event *prev_event = NULL; + GF_DOMHandler *hdl = (GF_DOMHandler *)node; + jsval fval, rval; + + if (!hdl->js_fun && !hdl->js_fun_val && !hdl->evt_listen_obj) { + txt = svg_get_text_child(node); + if (!txt) return 0; + } + /*not sure about this (cf test struct-use-205-t.svg)*/ + if (!node->sgprivate->parents) return 0; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[DOM Events ] Executing script code from handler\n")); + + svg_js = node->sgprivate->scenegraph->svg_js; + + prev_event = JS_GetPrivate(svg_js->js_ctx, svg_js->event); + /*break loops*/ + if (prev_event && (prev_event->type==event->type) && (prev_event->target==event->target)) + return 0; + JS_SetPrivate(svg_js->js_ctx, svg_js->event, event); + + /*if an observer is being specified, use it*/ + if (hdl->evt_listen_obj) __this = hdl->evt_listen_obj; + /*compile the jsfun if any - 'this' is the associated observer*/ + else __this = observer ? JSVAL_TO_OBJECT( dom_element_construct(svg_js->js_ctx, observer) ) : svg_js->global; + if (txt && !hdl->js_fun) { + char *argn = "evt"; + hdl->js_fun = JS_CompileFunction(svg_js->js_ctx, __this, NULL, 1, &argn, txt->textContent, strlen(txt->textContent), NULL, 0); + } + + if (hdl->js_fun || hdl->js_fun_val || hdl->evt_listen_obj) { + JSObject *evt; + jsval argv[1]; + evt = gf_dom_new_event(svg_js->js_ctx); + JS_SetPrivate(svg_js->js_ctx, evt, event); + argv[0] = OBJECT_TO_JSVAL(evt); + + if (hdl->js_fun) { + ret = JS_CallFunction(svg_js->js_ctx, __this, (JSFunction *)hdl->js_fun, 1, argv, &rval); + } + else if (hdl->js_fun_val) { + jsval funval = (JSWord) hdl->js_fun_val; + ret = JS_CallFunctionValue(svg_js->js_ctx, __this, funval, 1, argv, &rval); + } else { + ret = JS_CallFunctionName(svg_js->js_ctx, hdl->evt_listen_obj, "handleEvent", 1, argv, &rval); + } + } + else if (JS_LookupProperty(svg_js->js_ctx, svg_js->global, txt->textContent, &fval) && !JSVAL_IS_VOID(fval) ) { + if (svg_script_execute(node->sgprivate->scenegraph, txt->textContent, event)) + ret = JS_FALSE; + } + else { + ret = JS_EvaluateScript(svg_js->js_ctx, __this, txt->textContent, strlen(txt->textContent), 0, 0, &rval); + } + JS_SetPrivate(svg_js->js_ctx, svg_js->event, prev_event); + + if (ret==JS_FALSE) { + _ScriptMessage(node->sgprivate->scenegraph, GF_SCRIPT_ERROR, "SVG: Invalid handler textContent"); + return 0; + } + return 1; +} + +#endif + +#endif diff --git a/src/scenegraph/svg_types.c b/src/scenegraph/svg_types.c new file mode 100644 index 0000000..160dba1 --- /dev/null +++ b/src/scenegraph/svg_types.c @@ -0,0 +1,484 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2004 + * All rights reserved + * + * This file is part of GPAC / SVG Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_SVG +#include + +Bool gf_svg_is_animation_tag(u32 tag) +{ + return (tag == TAG_SVG_set || + tag == TAG_SVG_animate || + tag == TAG_SVG_animateColor || + tag == TAG_SVG_animateTransform || + tag == TAG_SVG_animateMotion || + tag == TAG_SVG_discard + ) ? 1 : 0; +} + +Bool gf_svg_is_timing_tag(u32 tag) +{ + if (gf_svg_is_animation_tag(tag)) return 1; + else return (tag == TAG_SVG_animation || + tag == TAG_SVG_audio || + tag == TAG_LSR_conditional || + tag == TAG_SVG_video)?1:0; +} + +SVG_Element *gf_svg_create_node(u32 ElementTag) +{ + SVG_Element *p; + if (gf_svg_is_timing_tag(ElementTag)) { + SVGTimedAnimBaseElement *tap; + GF_SAFEALLOC(tap, SVGTimedAnimBaseElement); + p = (SVG_Element *)tap; + } else if (ElementTag == TAG_SVG_handler) { + SVG_handlerElement *hdl; + GF_SAFEALLOC(hdl, SVG_handlerElement); + p = (SVG_Element *)hdl; + } else { + GF_SAFEALLOC(p, SVG_Element); + } + gf_node_setup((GF_Node *)p, ElementTag); + gf_sg_parent_setup((GF_Node *) p); + return p; +} + +void gf_svg_node_del(GF_Node *node) +{ + SVG_Element *p = (SVG_Element *)node; + + if (p->sgprivate->interact && p->sgprivate->interact->animations) { + gf_smil_anim_delete_animations((GF_Node *)p); + } + if (p->sgprivate->tag==TAG_SVG_listener) { + /*remove from parent's listener list*/ + GF_DOMEventTarget *evt = node->sgprivate->UserPrivate; + node->sgprivate->UserPrivate = NULL; + gf_dom_listener_del(node, evt); + } + /*remove this node from associated listeners*/ + else if (node->sgprivate->interact && node->sgprivate->interact->dom_evt) { + u32 i, count; + count = gf_dom_listener_count(node); + for (i=0; isgprivate->interact->dom_evt->evt_list, i); + listener->sgprivate->UserPrivate = NULL; + } + } + + if (gf_svg_is_timing_tag(node->sgprivate->tag)) { + SVGTimedAnimBaseElement *tap = (SVGTimedAnimBaseElement *)node; + if (tap->animp) { + free(tap->animp); + gf_smil_anim_remove_from_target((GF_Node *)tap, (GF_Node *)tap->xlinkp->href->target); + } + if (tap->timingp) { + gf_smil_timing_delete_runtime_info((GF_Node *)tap, tap->timingp->runtime); + free(tap->timingp); + } + if (tap->xlinkp) free(tap->xlinkp); + } + + gf_node_delete_attributes(node); + gf_sg_parent_reset(node); + gf_node_free(node); +} + +Bool gf_svg_node_init(GF_Node *node) +{ + switch (node->sgprivate->tag) { + case TAG_SVG_script: + if (node->sgprivate->scenegraph->script_load) + node->sgprivate->scenegraph->script_load(node); + return 1; + + case TAG_SVG_handler: + if (node->sgprivate->scenegraph->script_load) + node->sgprivate->scenegraph->script_load(node); + if (node->sgprivate->scenegraph->script_action) + ((SVG_handlerElement*)node)->handle_event = gf_sg_handle_dom_event; + return 1; + case TAG_LSR_conditional: + gf_smil_timing_init_runtime_info(node); + gf_smil_setup_events(node); + return 1; + case TAG_SVG_animateMotion: + case TAG_SVG_set: + case TAG_SVG_animate: + case TAG_SVG_animateColor: + case TAG_SVG_animateTransform: + gf_smil_anim_init_node(node); + gf_smil_setup_events(node); + /*we may get called several times depending on xlink:href resoling for events*/ + return (node->sgprivate->UserPrivate || node->sgprivate->UserCallback) ? 1 : 0; + case TAG_SVG_audio: + case TAG_SVG_video: + gf_smil_timing_init_runtime_info(node); + gf_smil_setup_events(node); + /*we may get called several times depending on xlink:href resoling for events*/ + return (node->sgprivate->UserPrivate || node->sgprivate->UserCallback) ? 1 : 0; + case TAG_SVG_animation: + gf_smil_timing_init_runtime_info(node); + gf_smil_setup_events(node); + return 0; + /*discard is implemented as a special animation element */ + case TAG_SVG_discard: + gf_smil_anim_init_discard(node); + gf_smil_setup_events(node); + return 1; + default: + return 0; + } + return 0; +} + +Bool gf_svg_node_changed(GF_Node *node, GF_FieldInfo *field) +{ + switch (node->sgprivate->tag) { + case TAG_SVG_animateMotion: + case TAG_SVG_discard: + case TAG_SVG_set: + case TAG_SVG_animate: + case TAG_SVG_animateColor: + case TAG_SVG_animateTransform: + case TAG_LSR_conditional: + gf_smil_timing_modified(node, field); + return 1; + case TAG_SVG_animation: + case TAG_SVG_audio: + case TAG_SVG_video: + gf_smil_timing_modified(node, field); + /*used by compositors*/ + return 0; + } + return 0; +} + + +void gf_svg_reset_path(SVG_PathData d) +{ +#if USE_GF_PATH + gf_path_reset(&d); +#else + u32 i, count; + count = gf_list_count(d.commands); + for (i = 0; i < count; i++) { + u8 *command = (u8 *)gf_list_get(d.commands, i); + free(command); + } + gf_list_del(d.commands); + count = gf_list_count(d.points); + for (i = 0; i < count; i++) { + SVG_Point *pt = (SVG_Point *)gf_list_get(d.points, i); + free(pt); + } + gf_list_del(d.points); +#endif +} + +/* TODO: update for elliptical arcs */ +GF_EXPORT +void gf_svg_path_build(GF_Path *path, GF_List *commands, GF_List *points) +{ + u32 i, j, command_count, points_count; + SVG_Point orig, ct_orig, ct_end, end, *tmp; + command_count = gf_list_count(commands); + points_count = gf_list_count(points); + orig.x = orig.y = ct_orig.x = ct_orig.y = 0; + + for (i=0, j=0; ielement_id) free(v->element_id); + free(v); + } + gf_list_del(list); +} + +void gf_svg_delete_points(GF_List *list) +{ + u32 i, count = gf_list_count(list); + for (i = 0; i < count; i++) { + SVG_Point *p = (SVG_Point *)gf_list_get(list, i); + free(p); + } + gf_list_del(list); +} + +void gf_svg_delete_coordinates(GF_List *list) +{ + u32 i, count = gf_list_count(list); + for (i = 0; i < count; i++) { + SVG_Coordinate *c = (SVG_Coordinate *)gf_list_get(list, i); + free(c); + } + gf_list_del(list); +} + +void gf_svg_reset_iri(GF_SceneGraph *sg, XMLRI *iri) +{ + if (!iri) return; + if (iri->string) free(iri->string); + gf_node_unregister_iri(sg, iri); +} + +void gf_svg_delete_paint(GF_SceneGraph *sg, SVG_Paint *paint) +{ + if (!paint) return; + if (paint->type == SVG_PAINT_URI && sg) gf_svg_reset_iri(sg, &paint->iri); + free(paint); +} + +static void svg_delete_one_anim_value(u8 anim_datatype, void *anim_value, GF_SceneGraph *sg) +{ + /* TODO: handle specific animation types : Motion, else ? */ + gf_svg_delete_attribute_value(anim_datatype, anim_value, sg); +} + +void gf_svg_reset_animate_values(SMIL_AnimateValues anim_values, GF_SceneGraph *sg) +{ + u32 i, count; + count = gf_list_count(anim_values.values); + for (i = 0; i < count; i++) { + void *value = gf_list_get(anim_values.values, i); + svg_delete_one_anim_value(anim_values.type, value, sg); + } + gf_list_del(anim_values.values); + anim_values.values = NULL; +} + +void gf_svg_reset_animate_value(SMIL_AnimateValue anim_value, GF_SceneGraph *sg) +{ + svg_delete_one_anim_value(anim_value.type, anim_value.value, sg); + anim_value.value = NULL; +} + +void gf_svg_delete_attribute_value(u32 type, void *value, GF_SceneGraph *sg) +{ + GF_List *l; + switch (type) { + case SVG_Paint_datatype: + gf_svg_delete_paint(sg, (SVG_Paint *)value); + break; + case XMLRI_datatype: + case XML_IDREF_datatype: + gf_svg_reset_iri(sg, (XMLRI *)value); + free(value); + break; + case SVG_Focus_datatype: + gf_svg_reset_iri(sg, & ((SVG_Focus*)value)->target); + free(value); + break; + case SVG_PathData_datatype: +#if USE_GF_PATH + gf_path_del((GF_Path *)value); +#else + free(value); +#endif + break; + case SVG_ID_datatype: + case DOM_String_datatype: + case SVG_ContentType_datatype: + case SVG_LanguageID_datatype: + if (*(SVG_String *)value) free(*(SVG_String *)value); + free(value); + break; + case SVG_StrokeDashArray_datatype: + if (((SVG_StrokeDashArray*)value)->array.vals) free(((SVG_StrokeDashArray*)value)->array.vals); + free(value); + break; + case SVG_Numbers_datatype: + case SVG_Coordinates_datatype: + case SVG_Points_datatype: + l = *(GF_List**)value; + while (gf_list_count(l)) { + void *n = gf_list_last(l); + gf_list_rem_last(l); + free(n); + } + gf_list_del(l); + free(value); + break; + case SVG_FontFamily_datatype: + { + SVG_FontFamily *ff = (SVG_FontFamily *)value; + if (ff->value) free(ff->value); + free(value); + } + break; + case SMIL_AttributeName_datatype: + { + SMIL_AttributeName *an = (SMIL_AttributeName *)value; + if (an->name) free(an->name); + free(value); + } + break; + case SMIL_Times_datatype: + gf_smil_delete_times(*(SMIL_Times *)value); + free(value); + break; + case SMIL_AnimateValue_datatype: + svg_delete_one_anim_value(((SMIL_AnimateValue *)value)->type, ((SMIL_AnimateValue *)value)->value, sg); + free(value); + break; + case SMIL_AnimateValues_datatype: + gf_svg_reset_animate_values(*((SMIL_AnimateValues *)value), sg); + free(value); + break; + case DOM_StringList_datatype: + l = *(GF_List**)value; + while (gf_list_count(l)) { + char *n = gf_list_last(l); + gf_list_rem_last(l); + free(n); + } + gf_list_del(l); + free(value); + break; + case XMLRI_List_datatype: + l = *(GF_List**)value; + while (gf_list_count(l)) { + XMLRI *r = gf_list_last(l); + gf_list_rem_last(l); + if (r->string) free(r->string); + free(r); + } + gf_list_del(l); + free(value); + break; + case SMIL_KeyTimes_datatype: + case SMIL_KeySplines_datatype: + l = *(GF_List**)value; + while (gf_list_count(l)) { + Fixed *f = gf_list_last(l); + gf_list_rem_last(l); + free(f); + } + gf_list_del(l); + free(value); + break; + + case SMIL_RepeatCount_datatype: + case SMIL_Duration_datatype: + case SVG_Length_datatype: + case SVG_Coordinate_datatype: + case SVG_Visibility_datatype: + case SVG_Display_datatype: + default: + free(value); + } +} + + +void gf_smil_delete_key_types(GF_List *l) +{ + while (gf_list_count(l)) { + Fixed *t = (Fixed *)gf_list_get(l, 0); + gf_list_rem(l, 0); + free(t); + } + gf_list_del(l); +} + + +#endif /*GPAC_DISABLE_SVG*/ diff --git a/src/scenegraph/vrml_interpolators.c b/src/scenegraph/vrml_interpolators.c new file mode 100644 index 0000000..cb42f1b --- /dev/null +++ b/src/scenegraph/vrml_interpolators.c @@ -0,0 +1,709 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include + + +static Fixed Interpolate(Fixed keyValue1, Fixed keyValue2, Fixed fraction) +{ + return gf_mulfix(keyValue2 - keyValue1, fraction) + keyValue1; +} + +static Fixed GetInterpolateFraction(Fixed key1, Fixed key2, Fixed fraction) +{ + Fixed keyDiff = key2 - key1; + assert((fraction >= key1) && (fraction <= key2)); + if (ABS(keyDiff) < FIX_EPSILON) return 0; + return gf_divfix(fraction - key1, keyDiff); +} + +static void CI2D_SetFraction(GF_Node *n) +{ + Fixed frac; + u32 numElemPerKey, i, j; + M_CoordinateInterpolator2D *_this = (M_CoordinateInterpolator2D *) n; + + if (! _this->key.count) return; + if (_this->keyValue.count % _this->key.count) return; + + numElemPerKey = _this->keyValue.count / _this->key.count; + //set size + if (_this->value_changed.count != numElemPerKey) + gf_sg_vrml_mf_alloc(&_this->value_changed, GF_SG_VRML_MFVEC2F, numElemPerKey); + + + if (_this->set_fraction < _this->key.vals[0]) { + for (i=0; ivalue_changed.vals[i] = _this->keyValue.vals[i]; + } else if (_this->set_fraction > _this->key.vals[_this->key.count - 1]) { + for (i=0; ivalue_changed.vals[i] = _this->keyValue.vals[(_this->keyValue.count) - numElemPerKey + i]; + } else { + for (j = 1; j < _this->key.count; j++) { + // Find the key values the fraction lies between + if ( _this->set_fraction < _this->key.vals[j-1]) continue; + if (_this->set_fraction >= _this->key.vals[j]) continue; + + frac = GetInterpolateFraction(_this->key.vals[j-1], _this->key.vals[j], _this->set_fraction); + for (i=0; ivalue_changed.vals[i].x = Interpolate(_this->keyValue.vals[(j-1)*numElemPerKey + i].x, + _this->keyValue.vals[(j)*numElemPerKey + i].x, + frac); + _this->value_changed.vals[i].y = Interpolate(_this->keyValue.vals[(j-1)*numElemPerKey + i].y, + _this->keyValue.vals[(j)*numElemPerKey + i].y, + frac); + } + break; + } + } + //invalidate + gf_node_event_out(n, 3);//"value_changed" +} + +Bool InitCoordinateInterpolator2D(M_CoordinateInterpolator2D *node) +{ + node->on_set_fraction = CI2D_SetFraction; + + if (node->key.count && !(node->keyValue.count % node->key.count) ) { + u32 numElemPerKey, i; + numElemPerKey = node->keyValue.count / node->key.count; + gf_sg_vrml_mf_alloc(&node->value_changed, GF_SG_VRML_MFVEC2F, numElemPerKey); + for (i=0; ivalue_changed.vals[i] = node->keyValue.vals[i]; + } + return 1; +} + +static Bool CI_SetFraction(Fixed fraction, MFVec3f *vals, MFFloat *key, MFVec3f *keyValue) +{ + Fixed frac; + u32 numElemPerKey, i, j; + + if (! key->count) return 0; + if (keyValue->count % key->count) return 0; + + numElemPerKey = keyValue->count / key->count; + + if (vals->count != numElemPerKey) gf_sg_vrml_mf_alloc(vals, GF_SG_VRML_MFVEC3F, numElemPerKey); + + if (fraction < key->vals[0]) { + for (i=0; ivals[i] = keyValue->vals[i]; + } else if (fraction > key->vals[key->count - 1]) { + for (i=0; ivals[i] = keyValue->vals[(keyValue->count) - numElemPerKey + i]; + } else { + for (j = 1; j < key->count; j++) { + // Find the key values the fraction lies between + if (fraction < key->vals[j-1]) continue; + if (fraction >= key->vals[j]) continue; + + frac = GetInterpolateFraction(key->vals[j-1], key->vals[j], fraction); + for (i=0; ivals[i].x = Interpolate(keyValue->vals[(j-1)*numElemPerKey + i].x, + keyValue->vals[(j)*numElemPerKey + i].x, + frac); + vals->vals[i].y = Interpolate(keyValue->vals[(j-1)*numElemPerKey + i].y, + keyValue->vals[(j)*numElemPerKey + i].y, + frac); + vals->vals[i].z = Interpolate(keyValue->vals[(j-1)*numElemPerKey + i].z, + keyValue->vals[(j)*numElemPerKey + i].z, + frac); + } + break; + } + } + return 1; +} + + +static void CoordInt_SetFraction(GF_Node *n) +{ + M_CoordinateInterpolator *_this = (M_CoordinateInterpolator *) n; + + if (CI_SetFraction(_this->set_fraction, &_this->value_changed, &_this->key, &_this->keyValue)) + gf_node_event_out(n, 3);//"value_changed" +} + +Bool InitCoordinateInterpolator(M_CoordinateInterpolator *n) +{ + n->on_set_fraction = CoordInt_SetFraction; + CI_SetFraction(0, &n->value_changed, &n->key, &n->keyValue); + return 1; +} + +static void NormInt_SetFraction(GF_Node *n) +{ + u32 i; + M_NormalInterpolator *_this = (M_NormalInterpolator *) n; + + if (!CI_SetFraction(_this->set_fraction, &_this->value_changed, &_this->key, &_this->keyValue)) return; + /*renorm*/ + for (i=0; i<_this->value_changed.count; i++) { + gf_vec_norm(&_this->value_changed.vals[i]); + } + gf_node_event_out(n, 3);//"value_changed" +} + +Bool InitNormalInterpolator(M_NormalInterpolator *n) +{ + n->on_set_fraction = NormInt_SetFraction; + CI_SetFraction(0, &n->value_changed, &n->key, &n->keyValue); + return 1; +} + +static void ColorInt_SetFraction(GF_Node *node) +{ + u32 i; + Fixed frac; + M_ColorInterpolator *_this = (M_ColorInterpolator *)node; + + + if (! _this->key.count) return; + if (_this->keyValue.count != _this->key.count) return; + + // The given fraction is less than the specified range + if (_this->set_fraction < _this->key.vals[0]) { + _this->value_changed = _this->keyValue.vals[0]; + } else if (_this->set_fraction >= _this->key.vals[_this->key.count-1]) { + _this->value_changed = _this->keyValue.vals[_this->keyValue.count-1]; + } else { + for (i=1; i<_this->key.count; i++) { + // Find the key values the fraction lies between + if (_this->set_fraction < _this->key.vals[i-1]) continue; + if (_this->set_fraction >= _this->key.vals[i]) continue; + + frac = GetInterpolateFraction(_this->key.vals[i-1], _this->key.vals[i], _this->set_fraction); + _this->value_changed.red = Interpolate(_this->keyValue.vals[i-1].red, + _this->keyValue.vals[i].red, + frac); + _this->value_changed.green = Interpolate(_this->keyValue.vals[i-1].green, + _this->keyValue.vals[i].green, + frac); + _this->value_changed.blue = Interpolate(_this->keyValue.vals[i-1].blue, + _this->keyValue.vals[i].blue, + frac); + break; + } + } + gf_node_event_out(node, 3);//"value_changed" +} + +Bool InitColorInterpolator(M_ColorInterpolator *node) +{ + node->on_set_fraction = ColorInt_SetFraction; + if (node->keyValue.count) node->value_changed = node->keyValue.vals[0]; + return 1; +} + + +static void PosInt2D_SetFraction(GF_Node *node) +{ + M_PositionInterpolator2D *_this = (M_PositionInterpolator2D *)node; + u32 i; + Fixed frac; + + if (! _this->key.count) return; + if (_this->keyValue.count != _this->key.count) return; + + // The given fraction is less than the specified range + if (_this->set_fraction < _this->key.vals[0]) { + _this->value_changed = _this->keyValue.vals[0]; + } else if (_this->set_fraction >= _this->key.vals[_this->key.count-1]) { + _this->value_changed = _this->keyValue.vals[_this->keyValue.count-1]; + } else { + for (i=1; i<_this->key.count; i++) { + // Find the key values the fraction lies between + if (_this->set_fraction < _this->key.vals[i-1]) continue; + if (_this->set_fraction >= _this->key.vals[i]) continue; + + frac = GetInterpolateFraction(_this->key.vals[i-1], _this->key.vals[i], _this->set_fraction); + _this->value_changed.x = Interpolate(_this->keyValue.vals[i-1].x, _this->keyValue.vals[i].x, frac); + _this->value_changed.y = Interpolate(_this->keyValue.vals[i-1].y, _this->keyValue.vals[i].y, frac); + break; + } + } + gf_node_event_out(node, 3);//"value_changed" +} + +Bool InitPositionInterpolator2D(M_PositionInterpolator2D *node) +{ + node->on_set_fraction = PosInt2D_SetFraction; + if (node->keyValue.count) node->value_changed = node->keyValue.vals[0]; + return 1; +} + +static void PosInt_SetFraction(GF_Node *node) +{ + u32 i; + Fixed frac; + M_PositionInterpolator *_this = (M_PositionInterpolator *)node; + + if (! _this->key.count) return; + if (_this->keyValue.count != _this->key.count) return; + + // The given fraction is less than the specified range + if (_this->set_fraction < _this->key.vals[0]) { + _this->value_changed = _this->keyValue.vals[0]; + } else if (_this->set_fraction >= _this->key.vals[_this->key.count-1]) { + _this->value_changed = _this->keyValue.vals[_this->keyValue.count-1]; + } else { + for (i=1; i<_this->key.count; i++) { + // Find the key values the fraction lies between + if (_this->set_fraction < _this->key.vals[i-1]) continue; + if (_this->set_fraction >= _this->key.vals[i]) continue; + + frac = GetInterpolateFraction(_this->key.vals[i-1], _this->key.vals[i], _this->set_fraction); + _this->value_changed.x = Interpolate(_this->keyValue.vals[i-1].x, _this->keyValue.vals[i].x, frac); + _this->value_changed.y = Interpolate(_this->keyValue.vals[i-1].y, _this->keyValue.vals[i].y, frac); + _this->value_changed.z = Interpolate(_this->keyValue.vals[i-1].z, _this->keyValue.vals[i].z, frac); + break; + } + } + gf_node_event_out(node, 3);//"value_changed" +} + +Bool InitPositionInterpolator(M_PositionInterpolator *node) +{ + node->on_set_fraction = PosInt_SetFraction; + if (node->keyValue.count) node->value_changed = node->keyValue.vals[0]; + return 1; +} + +static void ScalarInt_SetFraction(GF_Node *node) +{ + M_ScalarInterpolator *_this = (M_ScalarInterpolator *)node; + u32 i; + Fixed frac; + + if (! _this->key.count) return; + if (_this->keyValue.count != _this->key.count) return; + + // The given fraction is less than the specified range + if (_this->set_fraction < _this->key.vals[0]) { + _this->value_changed = _this->keyValue.vals[0]; + } else if (_this->set_fraction >= _this->key.vals[_this->key.count-1]) { + _this->value_changed = _this->keyValue.vals[_this->keyValue.count-1]; + } else { + for (i=1; i<_this->key.count; i++) { + // Find the key values the fraction lies between + if (_this->set_fraction < _this->key.vals[i-1]) continue; + if (_this->set_fraction >= _this->key.vals[i]) continue; + + frac = GetInterpolateFraction(_this->key.vals[i-1], _this->key.vals[i], _this->set_fraction); + _this->value_changed = Interpolate(_this->keyValue.vals[i-1], _this->keyValue.vals[i], frac); + break; + } + } + gf_node_event_out(node, 3);//"value_changed" +} +Bool InitScalarInterpolator(M_ScalarInterpolator *node) +{ + node->on_set_fraction = ScalarInt_SetFraction; + if (node->keyValue.count) node->value_changed = node->keyValue.vals[0]; + return 1; +} + +GF_EXPORT +SFRotation gf_sg_sfrotation_interpolate(SFRotation kv1, SFRotation kv2, Fixed fraction) +{ + SFRotation res; + Fixed newa, olda; + Bool stzero = ( ABS(kv1.q) < FIX_EPSILON) ? 1 : 0; + Bool endzero = ( ABS(kv2.q) < FIX_EPSILON) ? 1 : 0; + Fixed testa = gf_mulfix(kv1.x, kv2.x) + gf_mulfix(kv1.y, kv2.y) + gf_mulfix(kv1.y, kv2.y); + + if (testa>= 0) { + res.x = kv1.x + gf_mulfix(fraction, kv2.x-kv1.x); + res.y = kv1.y + gf_mulfix(fraction, kv2.y-kv1.y); + res.z = kv1.z + gf_mulfix(fraction, kv2.z-kv1.z); + newa = kv2.q; + } else { + res.x = kv1.x + gf_mulfix(fraction, -kv2.x -kv1.x); + res.y = kv1.y + gf_mulfix(fraction, -kv2.y-kv1.y); + res.z = kv1.z + gf_mulfix(fraction, -kv2.z-kv1.z); + newa = -kv2.q; + } + olda = kv1.q; + if (stzero || endzero) { + res.x = stzero ? kv2.x : kv1.x; + res.y = stzero ? kv2.y : kv1.y; + res.z = stzero ? kv2.z : kv1.z; + } + res.q = olda + gf_mulfix(fraction, newa - olda); + if (res.q > GF_2PI) { + res.q -= GF_2PI; + } else if (res.q < GF_2PI) { + res.q += GF_2PI; + } + return res; +} + +static void OrientInt_SetFraction(GF_Node *node) +{ + u32 i; + Fixed frac; + M_OrientationInterpolator *_this = (M_OrientationInterpolator *)node; + + if (! _this->key.count) return; + if (_this->keyValue.count != _this->key.count) return; + + // The given fraction is less than the specified range + if (_this->set_fraction < _this->key.vals[0]) { + _this->value_changed = _this->keyValue.vals[0]; + } else if (_this->set_fraction >= _this->key.vals[_this->key.count-1]) { + _this->value_changed = _this->keyValue.vals[_this->keyValue.count-1]; + } else { + for (i=1; i<_this->key.count; i++) { + // Find the key values the fraction lies between + if (_this->set_fraction < _this->key.vals[i-1]) continue; + if (_this->set_fraction >= _this->key.vals[i]) continue; + + frac = GetInterpolateFraction(_this->key.vals[i-1], _this->key.vals[i], _this->set_fraction); + _this->value_changed = gf_sg_sfrotation_interpolate(_this->keyValue.vals[i-1], _this->keyValue.vals[i], frac); + break; + } + } + gf_node_event_out(node, 3);//"value_changed" +} + +Bool InitOrientationInterpolator(M_OrientationInterpolator *node) +{ + node->on_set_fraction = OrientInt_SetFraction; + if (node->keyValue.count) node->value_changed = node->keyValue.vals[0]; + return 1; +} + +static void CI4D_SetFraction(GF_Node *n) +{ + Fixed frac; + u32 numElemPerKey, i, j; + M_CoordinateInterpolator4D *_this = (M_CoordinateInterpolator4D *) n; + + if (! _this->key.count) return; + if (_this->keyValue.count % _this->key.count) return; + + numElemPerKey = _this->keyValue.count / _this->key.count; + //set size + if (_this->value_changed.count != numElemPerKey) + gf_sg_vrml_mf_alloc(&_this->value_changed, GF_SG_VRML_MFVEC4F, numElemPerKey); + + + if (_this->set_fraction < _this->key.vals[0]) { + for (i=0; ivalue_changed.vals[i] = _this->keyValue.vals[i]; + } else if (_this->set_fraction > _this->key.vals[_this->key.count - 1]) { + for (i=0; ivalue_changed.vals[i] = _this->keyValue.vals[(_this->keyValue.count) - numElemPerKey + i]; + } else { + for (j = 1; j < _this->key.count; j++) { + // Find the key values the fraction lies between + if ( _this->set_fraction < _this->key.vals[j-1]) continue; + if (_this->set_fraction >= _this->key.vals[j]) continue; + + frac = GetInterpolateFraction(_this->key.vals[j-1], _this->key.vals[j], _this->set_fraction); + for (i=0; ivalue_changed.vals[i].x = Interpolate(_this->keyValue.vals[(j-1)*numElemPerKey + i].x, + _this->keyValue.vals[(j)*numElemPerKey + i].x, + frac); + _this->value_changed.vals[i].y = Interpolate(_this->keyValue.vals[(j-1)*numElemPerKey + i].y, + _this->keyValue.vals[(j)*numElemPerKey + i].y, + frac); + _this->value_changed.vals[i].z = Interpolate(_this->keyValue.vals[(j-1)*numElemPerKey + i].z, + _this->keyValue.vals[(j)*numElemPerKey + i].z, + frac); + _this->value_changed.vals[i].q = Interpolate(_this->keyValue.vals[(j-1)*numElemPerKey + i].q, + _this->keyValue.vals[(j)*numElemPerKey + i].q, + frac); + } + break; + } + } + //invalidate + gf_node_event_out(n, 3);//"value_changed" +} + +Bool InitCoordinateInterpolator4D(M_CoordinateInterpolator4D *node) +{ + node->on_set_fraction = CI4D_SetFraction; + + if (node->key.count && !(node->keyValue.count % node->key.count)) { + u32 i, numElemPerKey = node->keyValue.count / node->key.count; + gf_sg_vrml_mf_alloc(&node->value_changed, GF_SG_VRML_MFVEC4F, numElemPerKey); + for (i=0; ivalue_changed.vals[i] = node->keyValue.vals[i]; + } + return 1; +} + +static void PI4D_SetFraction(GF_Node *node) +{ + u32 i; + Fixed frac; + M_PositionInterpolator4D *_this = (M_PositionInterpolator4D *)node; + + if (! _this->key.count) return; + if (_this->keyValue.count != _this->key.count) return; + + // The given fraction is less than the specified range + if (_this->set_fraction < _this->key.vals[0]) { + _this->value_changed = _this->keyValue.vals[0]; + } else if (_this->set_fraction >= _this->key.vals[_this->key.count-1]) { + _this->value_changed = _this->keyValue.vals[_this->keyValue.count-1]; + } else { + for (i=1; i<_this->key.count; i++) { + // Find the key values the fraction lies between + if (_this->set_fraction < _this->key.vals[i-1]) continue; + if (_this->set_fraction >= _this->key.vals[i]) continue; + + frac = GetInterpolateFraction(_this->key.vals[i-1], _this->key.vals[i], _this->set_fraction); + _this->value_changed.x = Interpolate(_this->keyValue.vals[i-1].x, _this->keyValue.vals[i].x, frac); + _this->value_changed.y = Interpolate(_this->keyValue.vals[i-1].y, _this->keyValue.vals[i].y, frac); + _this->value_changed.z = Interpolate(_this->keyValue.vals[i-1].z, _this->keyValue.vals[i].z, frac); + _this->value_changed.q = Interpolate(_this->keyValue.vals[i-1].q, _this->keyValue.vals[i].q, frac); + break; + } + } + gf_node_event_out(node, 3);//"value_changed" +} + +Bool InitPositionInterpolator4D(M_PositionInterpolator4D *node) +{ + node->on_set_fraction = PI4D_SetFraction; + if (node->keyValue.count) node->value_changed = node->keyValue.vals[0]; + return 1; +} + + + +static void BooleanFilter_setValue(GF_Node *n) +{ + X_BooleanFilter *bf = (X_BooleanFilter *)n; + if (!bf->set_boolean) { + bf->inputFalse = 1; + gf_node_event_out_str(n, "inputFalse"); + } + if (bf->set_boolean) { + bf->inputTrue = 1; + gf_node_event_out_str(n, "inputTrue"); + } + bf->inputNegate = bf->set_boolean ? 0 : 1; + gf_node_event_out_str(n, "inputNegate"); +} + +void InitBooleanFilter(GF_Node *n) +{ + X_BooleanFilter *bf = (X_BooleanFilter *)n; + bf->on_set_boolean = BooleanFilter_setValue; +} + +static void BooleanSequencer_setFraction(GF_Node *n) +{ + u32 i; + X_BooleanSequencer *bs = (X_BooleanSequencer*)n; + if (! bs->key.count) return; + if (bs->keyValue.count != bs->key.count) return; + + if (bs->set_fraction < bs->key.vals[0]) { + bs->value_changed = bs->keyValue.vals[0]; + } else if (bs->set_fraction >= bs->key.vals[bs->key.count-1]) { + bs->value_changed = bs->keyValue.vals[bs->keyValue.count-1]; + } else { + for (i=1; ikey.count; i++) { + if (bs->set_fraction < bs->key.vals[i-1]) continue; + if (bs->set_fraction >= bs->key.vals[i]) continue; + bs->value_changed = bs->keyValue.vals[i-1]; + break; + } + } + gf_node_event_out(n, 3);//"value_changed" +} + +static void BooleanSequencer_setNext(GF_Node *n) +{ + s32 *prev_val, val; + X_BooleanSequencer *bs = (X_BooleanSequencer*)n; + if (!bs->next) return; + + prev_val = (s32 *)n->sgprivate->UserPrivate; + val = (*prev_val + 1) % bs->keyValue.count; + *prev_val = val; + bs->value_changed = bs->keyValue.vals[val]; + gf_node_event_out(n, 3);//"value_changed" +} + +static void BooleanSequencer_setPrevious(GF_Node *n) +{ + s32 *prev_val, val; + X_BooleanSequencer *bs = (X_BooleanSequencer*)n; + if (!bs->previous) return; + + prev_val = (s32 *)n->sgprivate->UserPrivate; + val = (*prev_val - 1); + if (val<0) val += bs->keyValue.count; + val %= bs->keyValue.count; + *prev_val = val; + bs->value_changed = bs->keyValue.vals[val]; + gf_node_event_out(n, 3);//"value_changed" +} +static void DestroyBooleanSequencer(GF_Node *n, void *eff, Bool is_destroy) +{ + if (is_destroy) { + s32 *st = (s32 *) gf_node_get_private(n); + free(st); + } +} +void InitBooleanSequencer(GF_Node *n) +{ + X_BooleanSequencer *bs = (X_BooleanSequencer*)n; + bs->on_next = BooleanSequencer_setNext; + bs->on_previous = BooleanSequencer_setPrevious; + bs->on_set_fraction = BooleanSequencer_setFraction; + n->sgprivate->UserPrivate = malloc(sizeof(s32)); + *(s32 *)n->sgprivate->UserPrivate = 0; + n->sgprivate->UserCallback = DestroyBooleanSequencer; +} + +static void BooleanToggle_setValue(GF_Node *n) +{ + X_BooleanToggle *bt = (X_BooleanToggle *)n; + if (bt->set_boolean) { + bt->toggle = !bt->toggle; + gf_node_event_out_str(n, "toggle"); + } +} +void InitBooleanToggle(GF_Node *n) +{ + X_BooleanToggle *bt = (X_BooleanToggle *)n; + bt->on_set_boolean = BooleanToggle_setValue; +} + +static void BooleanTrigger_setTime(GF_Node *n) +{ + X_BooleanTrigger *bt = (X_BooleanTrigger *)n; + bt->triggerTrue = 1; + gf_node_event_out_str(n, "triggerTrue"); +} +void InitBooleanTrigger(GF_Node *n) +{ + X_BooleanTrigger *bt = (X_BooleanTrigger *)n; + bt->on_set_triggerTime = BooleanTrigger_setTime; +} + +static void IntegerSequencer_setFraction(GF_Node *n) +{ + u32 i; + X_IntegerSequencer *is = (X_IntegerSequencer *)n; + if (! is->key.count) return; + if (is->keyValue.count != is->key.count) return; + + if (is->set_fraction < is->key.vals[0]) { + is->value_changed = is->keyValue.vals[0]; + } else if (is->set_fraction >= is->key.vals[is->key.count-1]) { + is->value_changed = is->keyValue.vals[is->keyValue.count-1]; + } else { + for (i=1; ikey.count; i++) { + if (is->set_fraction < is->key.vals[i-1]) continue; + if (is->set_fraction >= is->key.vals[i]) continue; + is->value_changed = is->keyValue.vals[i-1]; + break; + } + } + gf_node_event_out(n, 3);//"value_changed" +} + +static void IntegerSequencer_setNext(GF_Node *n) +{ + s32 *prev_val, val; + X_IntegerSequencer *is = (X_IntegerSequencer*)n; + if (!is->next) return; + + prev_val = (s32 *)n->sgprivate->UserPrivate; + val = (*prev_val + 1) % is->keyValue.count; + *prev_val = val; + is->value_changed = is->keyValue.vals[val]; + gf_node_event_out(n, 3);//"value_changed" +} + +static void IntegerSequencer_setPrevious(GF_Node *n) +{ + s32 *prev_val, val; + X_IntegerSequencer *is = (X_IntegerSequencer *)n; + if (!is->previous) return; + + prev_val = (s32 *)n->sgprivate->UserPrivate; + val = (*prev_val - 1); + if (val<0) val += is->keyValue.count; + val %= is->keyValue.count; + *prev_val = val; + is->value_changed = is->keyValue.vals[val]; + gf_node_event_out(n, 3);//"value_changed" +} +static void DestroyIntegerSequencer(GF_Node *n, void *eff, Bool is_destroy) +{ + if (is_destroy) { + s32 *st = (s32 *)gf_node_get_private(n); + free(st); + } +} +void InitIntegerSequencer(GF_Node *n) +{ + X_IntegerSequencer *bs = (X_IntegerSequencer *)n; + bs->on_next = IntegerSequencer_setNext; + bs->on_previous = IntegerSequencer_setPrevious; + bs->on_set_fraction = IntegerSequencer_setFraction; + n->sgprivate->UserPrivate = malloc(sizeof(s32)); + *(s32 *)n->sgprivate->UserPrivate = 0; + n->sgprivate->UserCallback = DestroyIntegerSequencer; +} + +static void IntegerTrigger_setTrigger(GF_Node *n) +{ + X_IntegerTrigger *it = (X_IntegerTrigger *)n; + if (it->set_boolean) { + it->triggerValue = it->integerKey; + gf_node_event_out_str(n, "triggerValue"); + } +} +void InitIntegerTrigger(GF_Node *n) +{ + X_IntegerTrigger *it = (X_IntegerTrigger *)n; + it->on_set_boolean = IntegerTrigger_setTrigger; +} + +static void TimeTrigger_setTrigger(GF_Node *n) +{ + X_TimeTrigger *tt = (X_TimeTrigger *)n; + tt->triggerTime = gf_node_get_scene_time(n); + gf_node_event_out_str(n, "triggerTime"); +} +void InitTimeTrigger(GF_Node *n) +{ + X_TimeTrigger *tt = (X_TimeTrigger*)n; + tt->on_set_boolean = TimeTrigger_setTrigger; +} + diff --git a/src/scenegraph/vrml_proto.c b/src/scenegraph/vrml_proto.c new file mode 100644 index 0000000..c4e8751 --- /dev/null +++ b/src/scenegraph/vrml_proto.c @@ -0,0 +1,1231 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +/*MPEG4 & X3D tags (for node tables & script handling)*/ +#include +#include + + +GF_Proto *gf_sg_proto_new(GF_SceneGraph *inScene, u32 ProtoID, char *name, Bool unregistered) +{ + GF_Proto *tmp; + if (!inScene) return NULL; + + /*make sure we don't define a proto already defined in this scope*/ + if (!unregistered) { + tmp = gf_sg_find_proto(inScene, ProtoID, name); + if (tmp) return NULL; + } + + GF_SAFEALLOC(tmp, GF_Proto) + if (!tmp) return NULL; + + tmp->proto_fields = gf_list_new(); + tmp->node_code = gf_list_new(); + tmp->parent_graph = inScene; + tmp->sub_graph = gf_sg_new_subscene(inScene); + tmp->instances = gf_list_new(); + + if (name) + tmp->Name = strdup(name); + else + tmp->Name = strdup("Unnamed Proto"); + tmp->ID = ProtoID; + if (!unregistered) { + gf_list_add(inScene->protos, tmp); + } else { + gf_list_add(inScene->unregistered_protos, tmp); + } + return tmp; +} + + +GF_Err gf_sg_proto_set_in_graph(GF_Proto *proto, GF_SceneGraph *inScene, Bool set_in) +{ + u32 i; + GF_Proto *tmp; + GF_List *removeFrom; + GF_List *insertIn; + + if (set_in) { + removeFrom = proto->parent_graph->unregistered_protos; + insertIn = proto->parent_graph->protos; + } else { + insertIn = proto->parent_graph->unregistered_protos; + removeFrom = proto->parent_graph->protos; + } + + gf_list_del_item(removeFrom, proto); + + i=0; + while ((tmp = (GF_Proto*)gf_list_enum(insertIn, &i))) { + if (tmp==proto) return GF_OK; + if (!set_in) continue; + /*if registering, make sure no other proto has the same ID/name*/ + if (tmp->ID==proto->ID) return GF_BAD_PARAM; + if (!stricmp(tmp->Name, proto->Name)) return GF_BAD_PARAM; + } + return gf_list_add(insertIn, proto); +} + + +GF_Err gf_sg_proto_del(GF_Proto *proto) +{ + GF_Node *node; + GF_ProtoFieldInterface *field; + s32 i; + + if (!proto) return GF_OK; + i = gf_list_del_item(proto->parent_graph->protos, proto); + if (i<0) i = gf_list_del_item(proto->parent_graph->unregistered_protos, proto); + + if (proto->userpriv && proto->OnDelete) proto->OnDelete(proto->userpriv); + + /*first destroy the code*/ + while (gf_list_count(proto->node_code)) { + node = (GF_Node*)gf_list_get(proto->node_code, 0); + gf_node_unregister(node, NULL); + gf_list_rem(proto->node_code, 0); + } + gf_list_del(proto->node_code); + + /*delete interface*/ + while (gf_list_count(proto->proto_fields)) { + field = (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, 0); + if (field->userpriv && field->OnDelete) field->OnDelete(field->userpriv); + + if (field->FieldType==GF_SG_VRML_SFNODE) { + if (field->def_sfnode_value) + gf_node_unregister(field->def_sfnode_value, NULL); + } + else if (field->FieldType==GF_SG_VRML_MFNODE) { + if (field->def_mfnode_value) + gf_node_unregister_children(NULL, field->def_mfnode_value); + } + else if (field->def_value) + gf_sg_vrml_field_pointer_del(field->def_value, field->FieldType); + + if (field->FieldName) free(field->FieldName); + + /*QP fields are SF fields, we can safely free() them*/ + if (field->qp_max_value) free(field->qp_max_value); + if (field->qp_min_value) free(field->qp_min_value); + free(field); + gf_list_rem(proto->proto_fields, 0); + } + gf_list_del(proto->proto_fields); + + while (gf_list_count(proto->instances)) { + GF_ProtoInstance *p = (GF_ProtoInstance *)gf_list_get(proto->instances, 0); + gf_list_rem(proto->instances, 0); + p->proto_interface = NULL; + } + + /*delete sub graph*/ + gf_sg_del(proto->sub_graph); + + + if (proto->Name) free(proto->Name); + gf_sg_mfurl_del(proto->ExternProto); + gf_list_del(proto->instances); + free(proto); + return GF_OK; +} + +GF_SceneGraph *gf_sg_proto_get_graph(GF_Proto *proto) +{ + return proto ? proto->sub_graph : NULL; +} + +void gf_sg_proto_set_private(GF_Proto *p, void *ptr, void (*OnDelete)(void *ptr) ) +{ + if (p) { + p->userpriv = ptr; + p->OnDelete = OnDelete; + } +} +void *gf_sg_proto_get_private(GF_Proto *p) +{ + return p ? p->userpriv : NULL; +} + +GF_EXPORT +MFURL *gf_sg_proto_get_extern_url(GF_Proto *proto) +{ + return proto ? &proto->ExternProto : NULL; +} + +GF_Err gf_sg_proto_add_node_code(GF_Proto *proto, GF_Node *pNode) +{ + if (!proto) return GF_BAD_PARAM; + return gf_list_add(proto->node_code, pNode); +} + +GF_ProtoFieldInterface *gf_sg_proto_field_find_by_name(GF_Proto *proto, char *fieldName) +{ + GF_ProtoFieldInterface *ptr; + u32 i=0; + while ((ptr = (GF_ProtoFieldInterface*)gf_list_enum(proto->proto_fields, &i))) { + if (ptr->FieldName && !strcmp(ptr->FieldName, fieldName)) return ptr; + } + return NULL; +} + +GF_ProtoFieldInterface *gf_sg_proto_field_new(GF_Proto *proto, u32 fieldType, u32 eventType, char *fieldName) +{ + GF_ProtoFieldInterface *tmp; + + if (fieldName) { + tmp = gf_sg_proto_field_find_by_name(proto, fieldName); + if (tmp) return NULL; + } + GF_SAFEALLOC(tmp, GF_ProtoFieldInterface) + if (!tmp) return NULL; + + tmp->FieldType = fieldType; + tmp->EventType = eventType; + + /*create container - can be NULL if SF node*/ + if ( fieldType == GF_SG_VRML_SFNODE) { + tmp->def_sfnode_value = NULL; + tmp->def_value = &tmp->def_sfnode_value; + } else if ( fieldType == GF_SG_VRML_MFNODE) { + tmp->def_mfnode_value = NULL; + tmp->def_value = &tmp->def_mfnode_value; + } else { + tmp->def_value = gf_sg_vrml_field_pointer_new(fieldType); + } + + if (fieldName) tmp->FieldName = strdup(fieldName); + + tmp->ALL_index = gf_list_count(proto->proto_fields); + tmp->OUT_index = tmp->DEF_index = tmp->IN_index = (u32) -1; + + switch (eventType) { + case GF_SG_EVENT_EXPOSED_FIELD: + tmp->IN_index = proto->NumIn; + proto->NumIn ++; + tmp->OUT_index = proto->NumOut; + proto->NumOut ++; + case GF_SG_EVENT_FIELD: + tmp->DEF_index = proto->NumDef; + proto->NumDef ++; + break; + case GF_SG_EVENT_IN: + tmp->IN_index = proto->NumIn; + proto->NumIn ++; + break; + case GF_SG_EVENT_OUT: + tmp->OUT_index = proto->NumOut; + proto->NumOut ++; + break; + } + + gf_list_add(proto->proto_fields, tmp); + return tmp; +} + +void gf_sg_proto_field_set_private(GF_ProtoFieldInterface *field, void *ptr, void (*OnDelete)(void *ptr)) +{ + if (field) { + field->userpriv = ptr; + field->OnDelete = OnDelete; + } +} + +void *gf_sg_proto_field_get_private(GF_ProtoFieldInterface *field) +{ + return field ? field->userpriv : NULL; +} + + + +GF_Err gf_sg_proto_field_get_field(GF_ProtoFieldInterface *field, GF_FieldInfo *info) +{ + if (!field || !info) return GF_BAD_PARAM; + memset(info, 0, sizeof(GF_FieldInfo)); + info->fieldIndex = field->ALL_index; + info->fieldType = field->FieldType; + info->eventType = field->EventType; + info->far_ptr = field->def_value; + info->name = field->FieldName; + info->NDTtype = NDT_SFWorldNode; + return GF_OK; +} + +GF_Err gf_sg_proto_get_field(GF_Proto *proto, GF_Node *node, GF_FieldInfo *info) +{ + GF_ProtoFieldInterface *proto_field; + GF_ProtoInstance *inst; + GF_ProtoField *field; + + if (!proto && !node) return GF_BAD_PARAM; + + if (proto) { + proto_field = (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, info->fieldIndex); + if (!proto_field) return GF_BAD_PARAM; + + info->fieldType = proto_field->FieldType; + info->eventType = proto_field->EventType; + info->fieldIndex = proto_field->ALL_index; + info->NDTtype = NDT_SFWorldNode; + info->far_ptr = proto_field->def_value; + info->name = proto_field->FieldName; + return GF_OK; + } + + /*otherwise this is an instanciated proto*/ + if (node->sgprivate->tag!=TAG_ProtoNode) return GF_BAD_PARAM; + + inst = (GF_ProtoInstance *) node; + field = (GF_ProtoField*)gf_list_get(inst->fields, info->fieldIndex); + if (!field) return GF_BAD_PARAM; + + info->fieldType = field->FieldType; + info->eventType = field->EventType; + /*SF/MF nodes need pointers to field object - cf gf_sg_proto_create_node*/ + if (gf_sg_vrml_get_sf_type(field->FieldType) == GF_SG_VRML_SFNODE) { + info->far_ptr = &field->field_pointer; + } else { + info->far_ptr = field->field_pointer; + } + /*set the name - watchout for deletion case*/ + if (inst->proto_interface) { + proto_field = (GF_ProtoFieldInterface*)gf_list_get(inst->proto_interface->proto_fields, info->fieldIndex); + info->name = proto_field->FieldName; + } else { + info->name = "ProtoFieldDeleted"; + } + info->NDTtype = NDT_SFWorldNode; + return GF_OK; +} + +s32 gf_sg_proto_get_field_index_by_name(GF_Proto *proto, GF_Node *node, char *name) +{ + u32 i; + GF_ProtoFieldInterface *proto_field; + GF_Proto *__proto; + + if (node && (node->sgprivate->tag!=TAG_ProtoNode)) return -1; + + __proto = proto ? proto : ((GF_ProtoInstance *) node)->proto_interface; + if (!__proto ) return -1; + + for (i=0; iproto_fields); i++) { + proto_field = (GF_ProtoFieldInterface*)gf_list_get(__proto->proto_fields, i); + if (proto_field->FieldName && !strcmp(proto_field->FieldName, name)) return i; + } + return -1; +} + + +GF_Node *gf_vrml_node_clone(GF_SceneGraph *inScene, GF_Node *orig, GF_Node *cloned_parent, char *inst_id_suffix) +{ + u32 i, count, id; + char *szNodeName; + Bool is_script; + GF_Node *node, *child; + GF_ChildNodeItem *list, *last; + GF_Route *r1, *r2; + void BIFS_SetupConditionalClone(GF_Node *node, GF_Node *orig); + GF_ProtoInstance *proto; + GF_Proto *proto_node; + GF_FieldInfo field_orig, field; + + /*this is not a mistake*/ + if (!orig) return NULL; + + /*check for DEF/USE*/ + szNodeName = NULL; + if (!inst_id_suffix) id = 0; + else { + const char *orig_name = gf_node_get_name_and_id(orig, &id); + /*generate clone IDs based on original one*/ + if (inst_id_suffix[0] && id) { + id = gf_sg_get_next_available_node_id(inScene); + if (orig_name) { + szNodeName = malloc(sizeof(char)*(strlen(orig_name)+strlen(inst_id_suffix)+1)); + strcpy(szNodeName, orig_name); + strcat(szNodeName, inst_id_suffix); + } + } + } + + if (id) { + node = szNodeName ? gf_sg_find_node_by_name(inScene, szNodeName) : gf_sg_find_node(inScene, id); + /*node already created, USE*/ + if (node) { + gf_node_register(node, cloned_parent); + if (inst_id_suffix[0] && szNodeName) free(szNodeName); + return node; + } + } + /*create a node*/ + if (orig->sgprivate->tag == TAG_ProtoNode) { + proto_node = ((GF_ProtoInstance *)orig)->proto_interface; + /*create the instance but don't load the code -c we MUST wait for ISed routes to be cloned before*/ + node = gf_sg_proto_create_node(inScene, proto_node, (GF_ProtoInstance *) orig); + } else { + node = gf_node_new(inScene, orig->sgprivate->tag); + } + + count = gf_node_get_field_count(orig); + + is_script = 0; + if ((orig->sgprivate->tag==TAG_MPEG4_Script) || (orig->sgprivate->tag==TAG_X3D_Script)) is_script = 1; + if (is_script) gf_sg_script_prepare_clone(node, orig); + + /*copy each field*/ + for (i=0; inode, node, inst_id_suffix, 1); + gf_node_list_add_child_last((GF_ChildNodeItem **) field.far_ptr, child, &last); + list = list->next; + } + break; + case GF_SG_VRML_SFTIME: + gf_sg_vrml_field_copy(field.far_ptr, field_orig.far_ptr, field.fieldType); + if (!inScene->GetSceneTime) break; + /*update SFTime that must be updated when cloning the node*/ + if (orig->sgprivate->tag == TAG_ProtoNode) { + if (gf_sg_proto_field_is_sftime_offset(orig, &field_orig)) + *((SFTime *)field.far_ptr) += inScene->GetSceneTime(inScene->userpriv); + } else if (!stricmp(field_orig.name, "startTime") || !stricmp(field_orig.name, "startTime") ) { + *((SFTime *)field.far_ptr) += inScene->GetSceneTime(inScene->userpriv); + } + break; + default: + gf_sg_vrml_field_copy(field.far_ptr, field_orig.far_ptr, field.fieldType); + break; + } + } + + if (node->sgprivate->tag==TAG_MPEG4_InputSensor) { + GF_Command *com_o, *com_f; + u32 k = 0; + M_InputSensor *clone_is = (M_InputSensor *)node; + M_InputSensor *orig_is = (M_InputSensor *)orig; + while ( (com_o = (GF_Command *)gf_list_enum(orig_is->buffer.commandList, &k) ) ) { + com_f = gf_sg_command_clone(com_o, node->sgprivate->scenegraph, 1); + gf_list_add(clone_is->buffer.commandList, com_f); + } + } + + /*register node*/ + if (id) { + gf_node_set_id(node, id, szNodeName); + if (inst_id_suffix[0] && szNodeName) free(szNodeName); + } + gf_node_register(node, cloned_parent); + +#ifndef GPAC_READ_ONLY + /*init node before creating ISed routes so the eventIn handler are in place*/ + if (node->sgprivate->tag == TAG_MPEG4_Conditional) BIFS_SetupConditionalClone(node, orig); + else +#endif + if (node->sgprivate->tag != TAG_ProtoNode) gf_node_init(node); + + if (!inScene->pOwningProto) return node; + proto = inScene->pOwningProto; + + /*create Routes for ISed fields*/ + i=0; + while ((r1 = (GF_Route*)gf_list_enum(proto->proto_interface->sub_graph->Routes, &i))) { + r2 = NULL; + /*locate only ISed routes*/ + if (!r1->IS_route) continue; + + /*eventOut*/ + if (r1->FromNode == orig) { + r2 = gf_sg_route_new(inScene, node, r1->FromField.fieldIndex, (GF_Node *) proto, r1->ToField.fieldIndex); + r2->IS_route = 1; + } + /*eventIn or exposedField*/ + else if (r1->ToNode == orig) { + r2 = gf_sg_route_new(inScene, (GF_Node *) proto, r1->FromField.fieldIndex, node, r1->ToField.fieldIndex); + r2->IS_route = 1; + + /*activate the route now so that proto instanciation works properly, otherwise we may load scripts with wrong field values + Note: we don't activate eventOut routes upon instanciation since no event has been triggered yet*/ + gf_sg_route_activate(r2); + } + } + + /*remember scripts*/ + if (is_script) gf_list_add(proto->scripts_to_load, node); + + /*this is a proto node, init our internal stuff*/ + if (node->sgprivate->tag == TAG_ProtoNode) { + node->sgprivate->UserCallback = NULL; + node->sgprivate->UserPrivate = NULL; + /*NO RENDER, this is filtered at the generic gf_node_traverse to cope with instanciations and externProto*/ + /*load code*/ + gf_sg_proto_instanciate((GF_ProtoInstance *)node); + } + return node; +} + +GF_Err gf_sg_proto_get_field_ind_static(GF_Node *Node, u32 inField, u8 IndexMode, u32 *allField) +{ + return gf_sg_proto_get_field_index((GF_ProtoInstance *)Node, inField, IndexMode, allField); +} + + +static Bool is_same_proto(GF_Proto *p1, GF_Proto *p2) +{ + u32 i, count; + if (gf_list_count(p1->proto_fields) != gf_list_count(p2->proto_fields)) return 0; + count = gf_list_count(p1->proto_fields); + for (i=0; iproto_fields, i); + GF_ProtoFieldInterface *pf2 = (GF_ProtoFieldInterface*)gf_list_get(p2->proto_fields, i); + if (pf1->EventType != pf2->EventType) return 0; + if (pf1->FieldType != pf2->FieldType) return 0; + /*note we don't check names since we're not sure both protos use name coding (MPEG4 only)*/ + } + return 1; +} + +static GF_Proto *SG_FindProtoByInterface(GF_SceneGraph *sg, GF_Proto *the_proto) +{ + GF_Proto *proto; + u32 i, count; + + assert(sg); + + /*browse all top-level */ + i=0; + while ((proto = (GF_Proto*)gf_list_enum(sg->protos, &i))) { + if (is_same_proto(proto, the_proto)) return proto; + } + /*browse all top-level unregistered in reverse order*/ + count = gf_list_count(sg->unregistered_protos); + for (i=count; i>0; i--) { + proto = (GF_Proto*)gf_list_get(sg->unregistered_protos, i-1); + if (is_same_proto(proto, the_proto)) return proto; + } + return NULL; +} +/*performs common initialization of routes ISed fields and protos once everything is loaded*/ +void gf_sg_proto_instanciate(GF_ProtoInstance *proto_node) +{ + GF_Node *node, *orig; + GF_Route *route, *r2; + u32 i, count; + GF_Proto *proto = proto_node->proto_interface; + GF_Proto *owner = proto; + + if (!proto) return; + + if (owner->ExternProto.count) { + GF_ProtoFieldInterface *pfi; + GF_SceneGraph *extern_lib; + if (!owner->parent_graph->GetExternProtoLib) return; + extern_lib = owner->parent_graph->GetExternProtoLib(proto->parent_graph->userpriv, &owner->ExternProto); + if (!extern_lib) return; + + /*this is an hardcoded proto - all routes, node modifications and co are handled internally*/ + if (extern_lib == GF_SG_INTERNAL_PROTO) { + proto_node->sgprivate->flags |= GF_SG_NODE_DIRTY; + owner->parent_graph->NodeCallback(owner->parent_graph->userpriv, GF_SG_CALLBACK_INIT, (GF_Node *) proto_node, NULL); + return; + } + /*not loaded yet*/ + if (!gf_list_count(extern_lib->protos)) return; + + /*overwrite this proto by external one*/ + proto = NULL; + /*start with proto v2 addressing*/ + if (owner->ExternProto.vals[0].url) { + u32 ID = (u32) -1; + char *szName = strrchr(owner->ExternProto.vals[0].url, '#'); + if (szName) { + szName++; + if (sscanf(szName, "%d", &ID)) ID = (u32) -1; + } + /*if we have the proto name, use it*/ + if (owner->Name) szName = owner->Name; + proto = gf_sg_find_proto(extern_lib, ID, szName); + } + if (!proto) proto = gf_sg_find_proto(extern_lib, owner->ID, owner->Name); + if (!proto) proto = SG_FindProtoByInterface(extern_lib, owner); + /*couldn't find proto in the given lib, consider the proto as loaded (give up)*/ + if (!proto) { + proto_node->is_loaded = 1; + return; + } + /*cf VRML: once an external proto is loaded, copy back the default field values of the external proto*/ + count = gf_list_count(owner->proto_fields); + for (i=0; ifields, i); + if (!pf->has_been_accessed) { + pfi = (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, i); + gf_sg_vrml_field_copy(pf->field_pointer, pfi->def_value, pfi->FieldType); + } + } + + /*unregister from prev and reg with real proto*/ + gf_list_del_item(owner->instances, proto_node); + gf_list_add(proto->instances, proto_node); + } + + /*OVERRIDE the proto instance (eg don't instanciate an empty externproto...)*/ + proto_node->proto_interface = proto; + + /*clone all nodes*/ + i=0; + while ((orig = (GF_Node*)gf_list_enum(proto->node_code, &i))) { + /*node is cloned in the new scenegraph and its parent is NULL */ + node = gf_node_clone(proto_node->sgprivate->scenegraph, orig, NULL, "", 1); + assert(node); + + /*assign first rendering node*/ + if (i==1) proto_node->RenderingNode = node; + gf_list_add(proto_node->node_code, node); + } + + /*instantiate routes (not ISed ones)*/ + i=0; + while ((route = (GF_Route*)gf_list_enum(proto->sub_graph->Routes, &i))) { + if (route->IS_route) continue; + + r2 = gf_sg_route_new(proto_node->sgprivate->scenegraph, + gf_sg_find_node(proto_node->sgprivate->scenegraph, gf_node_get_id(route->FromNode) ), + route->FromField.fieldIndex, + gf_sg_find_node(proto_node->sgprivate->scenegraph, gf_node_get_id(route->ToNode) ), + route->ToField.fieldIndex); + + if (route->ID) gf_sg_route_set_id(r2, route->ID); + if (route->name) gf_sg_route_set_name(r2, route->name); + } + /*activate all ISed fields so that inits on events is properly done*/ + i=0; + while ((route = (GF_Route*)gf_list_enum(proto_node->sgprivate->scenegraph->Routes, &i))) { + if (!route->IS_route) continue; + /*do not activate eventIn to eventIn routes*/ + if (route->is_setup) { + if ((route->ToField.eventType == GF_SG_EVENT_IN) && (route->FromField.eventType == GF_SG_EVENT_IN) ) continue; + } + gf_sg_route_activate(route); + } + /*and load all scripts (this must be done once all fields are routed for the "initialize" method)*/ + while (gf_list_count(proto_node->scripts_to_load)) { + node = (GF_Node*)gf_list_get(proto_node->scripts_to_load, 0); + gf_list_rem(proto_node->scripts_to_load, 0); + gf_sg_script_load(node); + } + /*re-activate all ISed fields pointing to scripts once scripts are loaded (eventIns)*/ + i=0; + while ((route = (GF_Route*)gf_list_enum(proto_node->sgprivate->scenegraph->Routes, &i))) { + if (!route->IS_route || !route->ToNode) continue; +/* assert(route->is_setup); + if ((route->FromField.eventType == GF_SG_EVENT_OUT) || (route->FromField.eventType == GF_SG_EVENT_IN) ) continue; +*/ if ((route->ToNode->sgprivate->tag==TAG_MPEG4_Script) || (route->ToNode->sgprivate->tag==TAG_X3D_Script) ) + gf_sg_route_activate(route); + } + proto_node->is_loaded = 1; +} + +void gf_sg_proto_mark_field_loaded(GF_Node *proto_inst, GF_FieldInfo *info) +{ + GF_ProtoInstance *inst= (proto_inst->sgprivate->tag==TAG_ProtoNode) ? (GF_ProtoInstance *)proto_inst : NULL; + GF_ProtoField *pf = inst ? (GF_ProtoField *)gf_list_get(inst->fields, info->fieldIndex) : NULL; + if (pf) pf->has_been_accessed = 1; +} + +GF_Node *gf_sg_proto_create_node(GF_SceneGraph *scene, GF_Proto *proto, GF_ProtoInstance *from_inst) +{ + u32 i; + GF_ProtoField *inst, *from_field; + GF_ProtoFieldInterface *field; + + GF_ProtoInstance *proto_node; + GF_SAFEALLOC(proto_node, GF_ProtoInstance) + if (!proto_node) return NULL; + + gf_node_setup((GF_Node *)proto_node, TAG_ProtoNode); + proto_node->node_code = gf_list_new(); + proto_node->fields = gf_list_new(); + proto_node->scripts_to_load = gf_list_new(); + + proto_node->proto_interface = proto; + gf_list_add(proto->instances, proto_node); + + proto_node->proto_name = strdup(proto->Name); + + /*create the namespace*/ + proto_node->sgprivate->scenegraph = gf_sg_new_subscene(scene); + /*set this proto as owner of the new graph*/ + proto_node->sgprivate->scenegraph->pOwningProto = proto_node; + + /*instanciate fields*/ + i=0; + while ((field = (GF_ProtoFieldInterface*)gf_list_enum(proto->proto_fields, &i))) { + GF_SAFEALLOC(inst, GF_ProtoField); + inst->EventType = field->EventType; + inst->FieldType = field->FieldType; + + /*this is OK to call on GF_Node (returns NULL) and MFNode (returns gf_list_new() )*/ + inst->field_pointer = gf_sg_vrml_field_pointer_new(inst->FieldType); + + /*regular field, duplicate from default value or instanciated one if specified (since + a proto may be partially instanciated when used in another proto)*/ + if (gf_sg_vrml_get_sf_type(inst->FieldType) != GF_SG_VRML_SFNODE) { + if (from_inst) { + from_field = (GF_ProtoField *)gf_list_get(from_inst->fields, i-1); + gf_sg_vrml_field_copy(inst->field_pointer, from_field->field_pointer, inst->FieldType); + inst->has_been_accessed = from_field->has_been_accessed; + } else { + gf_sg_vrml_field_copy(inst->field_pointer, field->def_value, inst->FieldType); + } + } + /*No default values for SFNodes as interfaces ...*/ + gf_list_add(proto_node->fields, inst); + } + return (GF_Node *) proto_node; +} + + + +GF_Node *gf_sg_proto_create_instance(GF_SceneGraph *sg, GF_Proto *proto) +{ + return gf_sg_proto_create_node(sg, proto, NULL); +} + +GF_Err gf_sg_proto_load_code(GF_Node *node) +{ + GF_ProtoInstance *inst; + if (node->sgprivate->tag != TAG_ProtoNode) return GF_BAD_PARAM; + inst = (GF_ProtoInstance *) node; + if (!inst->proto_interface) return GF_BAD_PARAM; + if (inst->is_loaded) return GF_OK; + gf_sg_proto_instanciate(inst); + return GF_OK; +} + + +u32 gf_sg_proto_get_num_fields(GF_Node *node, u8 code_mode) +{ + GF_ProtoInstance *proto; + if (!node) return 0; + + proto = (GF_ProtoInstance *)node; + /*watchout for deletion case*/ + switch (code_mode) { + case GF_SG_FIELD_CODING_IN: return proto->proto_interface ? proto->proto_interface->NumIn : 0; + case GF_SG_FIELD_CODING_OUT: return proto->proto_interface ? proto->proto_interface->NumOut : 0; + case GF_SG_FIELD_CODING_DEF: return proto->proto_interface ? proto->proto_interface->NumDef : 0; + case GF_SG_FIELD_CODING_ALL: return gf_list_count(proto->proto_interface ? proto->proto_interface->proto_fields : proto->fields); + /*BIFS-ANIM not supported*/ + case GF_SG_FIELD_CODING_DYN: + default: + return 0; + } +} + + +void gf_sg_proto_del_instance(GF_ProtoInstance *inst) +{ + GF_SceneGraph *sg; + GF_ProtoField *field; + GF_Node *node; + u32 index; + + index = 0; + while (gf_list_count(inst->fields)) { + field = (GF_ProtoField *)gf_list_get(inst->fields, 0); + gf_list_rem(inst->fields, 0); + + /*regular type*/ + if ( (field->FieldType!=GF_SG_VRML_SFNODE) && (field->FieldType!=GF_SG_VRML_MFNODE)) { + gf_sg_vrml_field_pointer_del(field->field_pointer, field->FieldType); + } + /*node types: delete instances*/ + else if (field->field_pointer) { + if (field->FieldType == GF_SG_VRML_SFNODE) { + gf_node_unregister((GF_Node *) field->field_pointer, (GF_Node *) inst); + } else { + GF_ChildNodeItem *list = (GF_ChildNodeItem *)field->field_pointer; + while (list) { + GF_ChildNodeItem *cur = list; + gf_node_unregister(list->node, (GF_Node *) inst); + list = list->next; + free(cur); + } + } + } + + free(field); + index++; + } + gf_list_del(inst->fields); + + /*destroy the code*/ + while (gf_list_count(inst->node_code)) { + node = (GF_Node*)gf_list_get(inst->node_code, 0); + gf_node_unregister(node, (GF_Node*) inst); + gf_list_rem(inst->node_code, 0); + } + gf_list_del(inst->node_code); + + assert(!gf_list_count(inst->scripts_to_load)); + gf_list_del(inst->scripts_to_load); + + if (inst->proto_interface) gf_list_del_item(inst->proto_interface->instances, inst); + + sg = inst->sgprivate->scenegraph; + + free((char *) inst->proto_name); + + sg->pOwningProto = NULL; + /*and finally destroy the node. If the proto is a hardcoded one (UserCallback set), destroy the node first + since the hardcoded proto may need the scene graph when being destroyed*/ + if (inst->sgprivate->UserCallback) { + gf_node_free((GF_Node *)inst); + gf_sg_del(sg); + } else { + gf_sg_del(sg); + gf_node_free((GF_Node *)inst); + } +} + +/*Note on ISed fields: we cannot support fan-in on proto, eg we assume only one eventIn field can receive events +thus situations where a proto receives eventIn from outside and the node with ISed eventIn receives event +from inside the proto are undefined*/ +GF_Err gf_sg_proto_field_set_ised(GF_Proto *proto, u32 protoFieldIndex, GF_Node *node, u32 nodeFieldIndex) +{ + GF_Err e; + GF_Route *r; + GF_FieldInfo field, nodeField; + field.fieldIndex = protoFieldIndex; + e = gf_sg_proto_get_field(proto, NULL, &field); + if (e) return e; + e = gf_node_get_field(node, nodeFieldIndex, &nodeField); + if (e) return e; + if (field.fieldType != nodeField.fieldType) { + if ((gf_sg_vrml_get_sf_type(field.fieldType)==GF_SG_VRML_SFSTRING) && (gf_sg_vrml_get_sf_type(nodeField.fieldType) == GF_SG_VRML_SFURL)) { + e = GF_OK; + } else if ((gf_sg_vrml_get_sf_type(field.fieldType)==GF_SG_VRML_SFURL) && (gf_sg_vrml_get_sf_type(nodeField.fieldType) == GF_SG_VRML_SFSTRING)) { + e = GF_OK; + } else { +// printf("error in IS - node field %s.%s - inType %s - outType %s\n", gf_node_get_class_name(node) , nodeField.name, gf_sg_vrml_get_field_type_by_name(field.fieldType), gf_sg_vrml_get_field_type_by_name(nodeField.fieldType)); + return GF_SG_INVALID_PROTO; + } + } + + GF_SAFEALLOC(r, GF_Route) + if (!r) return GF_OUT_OF_MEM; + r->IS_route = 1; + + if (nodeField.eventType==GF_SG_EVENT_OUT) { + r->FromField.fieldIndex = nodeFieldIndex; + r->FromNode = node; + r->ToField.fieldIndex = protoFieldIndex; + r->ToNode = NULL; + if (!node->sgprivate->interact) GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); + if (!node->sgprivate->interact->routes) node->sgprivate->interact->routes = gf_list_new(); + gf_list_add(node->sgprivate->interact->routes, r); + } else { + switch (field.eventType) { + case GF_SG_EVENT_FIELD: + case GF_SG_EVENT_EXPOSED_FIELD: + case GF_SG_EVENT_IN: + r->FromField.fieldIndex = protoFieldIndex; + r->FromNode = NULL; + r->ToField.fieldIndex = nodeFieldIndex; + r->ToNode = node; + break; + case GF_SG_EVENT_OUT: + r->FromField.fieldIndex = nodeFieldIndex; + r->FromNode = node; + r->ToField.fieldIndex = protoFieldIndex; + r->ToNode = NULL; + if (!node->sgprivate->interact) GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); + if (!node->sgprivate->interact->routes) node->sgprivate->interact->routes = gf_list_new(); + break; + default: + free(r); + return GF_BAD_PARAM; + } + } + r->graph = proto->sub_graph; + return gf_list_add(proto->sub_graph->Routes, r); +} + +GF_Err gf_sg_proto_instance_set_ised(GF_Node *protoinst, u32 protoFieldIndex, GF_Node *node, u32 nodeFieldIndex) +{ + GF_Err e; + GF_Route *r; + GF_FieldInfo field, nodeField; + if (protoinst->sgprivate->tag != TAG_ProtoNode) return GF_BAD_PARAM; + + e = gf_node_get_field(protoinst, protoFieldIndex, &field); + if (e) return e; + e = gf_node_get_field(node, nodeFieldIndex, &nodeField); + if (e) return e; + if (field.fieldType != nodeField.fieldType) { + if ((gf_sg_vrml_get_sf_type(field.fieldType)==GF_SG_VRML_SFSTRING) && (gf_sg_vrml_get_sf_type(nodeField.fieldType) == GF_SG_VRML_SFURL)) { + e = GF_OK; + } else if ((gf_sg_vrml_get_sf_type(field.fieldType)==GF_SG_VRML_SFURL) && (gf_sg_vrml_get_sf_type(nodeField.fieldType) == GF_SG_VRML_SFSTRING)) { + e = GF_OK; + } else { +// printf("error in IS - node field %s.%s - inType %s - outType %s\n", gf_node_get_class_name(node) , nodeField.name, gf_sg_vrml_get_field_type_by_name(field.fieldType), gf_sg_vrml_get_field_type_by_name(nodeField.fieldType)); + return GF_SG_INVALID_PROTO; + } + } + + GF_SAFEALLOC(r, GF_Route) + if (!r) return GF_OUT_OF_MEM; + r->IS_route = 1; + + if (nodeField.eventType==GF_SG_EVENT_OUT) { + r->FromField.fieldIndex = nodeFieldIndex; + r->FromNode = node; + r->ToField.fieldIndex = protoFieldIndex; + r->ToNode = protoinst; + if (!node->sgprivate->interact) GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); + if (!node->sgprivate->interact->routes) node->sgprivate->interact->routes = gf_list_new(); + gf_list_add(node->sgprivate->interact->routes, r); + } else { + switch (field.eventType) { + case GF_SG_EVENT_FIELD: + case GF_SG_EVENT_EXPOSED_FIELD: + case GF_SG_EVENT_IN: + r->FromField.fieldIndex = protoFieldIndex; + r->FromNode = protoinst; + r->ToField.fieldIndex = nodeFieldIndex; + r->ToNode = node; + break; + case GF_SG_EVENT_OUT: + r->FromField.fieldIndex = nodeFieldIndex; + r->FromNode = node; + r->ToField.fieldIndex = protoFieldIndex; + r->ToNode = protoinst; + if (!node->sgprivate->interact) GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); + if (!node->sgprivate->interact->routes) node->sgprivate->interact->routes = gf_list_new(); + gf_list_add(node->sgprivate->interact->routes, r); + break; + default: + free(r); + return GF_BAD_PARAM; + } + } + r->graph = node->sgprivate->scenegraph; + gf_sg_route_activate(r); + return gf_list_add(r->graph->Routes, r); +} + + +GF_Err gf_bifs_proto_field_set_aq_info(GF_ProtoFieldInterface *field, + u32 QP_Type, + u32 hasMinMax, + u32 QPSFType, + void *qp_min_value, + void *qp_max_value, + u32 QP13_NumBits) +{ + + if (!field) return GF_BAD_PARAM; + if (!QP_Type) return GF_OK; + if (!gf_sg_vrml_is_sf_field(QPSFType)) return GF_BAD_PARAM; + + field->QP_Type = QP_Type; + field->hasMinMax = hasMinMax; + if (hasMinMax) { + if (qp_min_value) { + field->qp_min_value = gf_sg_vrml_field_pointer_new(QPSFType); + gf_sg_vrml_field_copy(field->qp_min_value, qp_min_value, QPSFType); + } + if (qp_max_value) { + field->qp_max_value = gf_sg_vrml_field_pointer_new(QPSFType); + gf_sg_vrml_field_copy(field->qp_max_value, qp_max_value, QPSFType); + } + } + field->NumBits = QP13_NumBits; + return GF_OK; +} + + +GF_Err gf_sg_proto_get_field_index(GF_ProtoInstance *proto, u32 index, u32 code_mode, u32 *all_index) +{ + u32 i; + GF_ProtoFieldInterface *proto_field; + + i=0; + while ((proto_field = (GF_ProtoFieldInterface*)gf_list_enum(proto->proto_interface->proto_fields, &i))) { + assert(proto_field); + switch (code_mode) { + case GF_SG_FIELD_CODING_IN: + if (proto_field->IN_index == index) { + *all_index = proto_field->ALL_index; + return GF_OK; + } + break; + case GF_SG_FIELD_CODING_OUT: + if (proto_field->OUT_index == index) { + *all_index = proto_field->ALL_index; + return GF_OK; + } + break; + case GF_SG_FIELD_CODING_DEF: + if (proto_field->DEF_index == index) { + *all_index = proto_field->ALL_index; + return GF_OK; + } + break; + case GF_SG_FIELD_CODING_ALL: + if (proto_field->ALL_index == index) { + *all_index = proto_field->ALL_index; + return GF_OK; + } + break; + /*BIFS-ANIM not supported*/ + case GF_SG_FIELD_CODING_DYN: + default: + return GF_BAD_PARAM; + } + } + return GF_BAD_PARAM; +} + +u32 gf_sg_proto_get_field_count(GF_Proto *proto) +{ + if (!proto) return 0; + return gf_list_count(proto->proto_fields); +} + +GF_ProtoFieldInterface *gf_sg_proto_field_find(GF_Proto *proto, u32 fieldIndex) +{ + if (!proto) return NULL; + return (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, fieldIndex); +} + +void gf_sg_proto_check_field_change(GF_Node *node, u32 fieldIndex) +{ + u32 i; + + GF_Route *r; + if (!node) return; + + if ((node->sgprivate->tag == TAG_ProtoNode) && node->sgprivate->interact && node->sgprivate->interact->routes){ + i=0; + while ((r = (GF_Route*)gf_list_enum(node->sgprivate->interact->routes, &i))) { + if (!r->IS_route) continue; + /*eventIn or exposedField*/ + if ((r->FromNode == node) && (r->FromField.fieldIndex == fieldIndex) ) { + if (gf_sg_route_activate(r)) + gf_node_changed(r->ToNode, &r->FromField); + } + /*eventOut*/ + if ((r->ToNode == node) && (r->ToField.fieldIndex == fieldIndex) ) { + gf_sg_route_activate(r); + } + } + /*no notification for proto changes*/ + return; + } + /*the node has to belong to a proto graph*/ + if (! node->sgprivate->scenegraph->pOwningProto) return; + /*no routes defined*/ + if (!node->sgprivate->interact) return; + + /*search for IS routes_events in the node and activate them. Field can also be an eventOut !!*/ + i=0; + while ((r = (GF_Route*)gf_list_enum(node->sgprivate->interact->routes, &i))) { + if (!r->IS_route) continue; + /*activate eventOuts*/ + if ((r->FromNode == node) && (r->FromField.fieldIndex == fieldIndex)) { + gf_sg_route_activate(r); + } + /*or eventIns / (exposed)Fields*/ + else if ((r->ToNode == node) && (r->ToField.fieldIndex == fieldIndex)) { + /*don't forget to notify the node it has changed*/ + if (gf_sg_route_activate(r)) + gf_node_changed(node, &r->ToField); + } + } +} + + +Bool gf_sg_proto_get_aq_info(GF_Node *Node, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + GF_Proto *proto; + u32 i; + GF_ProtoFieldInterface *proto_field; + + proto = ((GF_ProtoInstance *)Node)->proto_interface; + + i=0; + while ((proto_field = (GF_ProtoFieldInterface*)gf_list_enum(proto->proto_fields, &i))) { + if (proto_field->ALL_index!=FieldIndex) continue; + + *QType = proto_field->QP_Type; + *AType = proto_field->Anim_Type; + *b_min = FIX_MIN; + *b_max = FIX_MAX; + + if (proto_field->hasMinMax) { + + /*translate our bounds*/ + switch (gf_sg_vrml_get_sf_type(proto_field->FieldType)) { + case GF_SG_VRML_SFINT32: + *b_min = (SFFloat) * ( (SFInt32 *) proto_field->qp_min_value); + *b_max = (SFFloat) * ( (SFInt32 *) proto_field->qp_max_value); + break; + /*TO DO EVERYWHERE: check on field translation from double to float + during quant bounds*/ + case GF_SG_VRML_SFTIME: + *b_min = (SFFloat) * ( (SFTime *) proto_field->qp_min_value); + *b_max = (SFFloat) * ( (SFTime *) proto_field->qp_max_value); + break; + default: + if (proto_field->qp_min_value) + *b_min = (SFFloat) * ( (SFFloat *) proto_field->qp_min_value); + if (proto_field->qp_max_value) + *b_max = (SFFloat) * ( (SFFloat *) proto_field->qp_max_value); + break; + } + + } + *QT13_bits = proto_field->NumBits; + return 1; + } + return 0; +} + + +GF_EXPORT +GF_Proto *gf_node_get_proto(GF_Node *node) +{ + GF_ProtoInstance *inst; + if (node->sgprivate->tag != TAG_ProtoNode) return NULL; + inst = (GF_ProtoInstance *) node; + return inst->proto_interface; +} + +/*returns the ID of the proto*/ +u32 gf_sg_proto_get_id(GF_Proto *proto) +{ + return proto->ID; +} + +const char *gf_sg_proto_get_class_name(GF_Proto *proto) +{ + return (const char *) proto->Name; +} + +u32 gf_sg_proto_get_root_tag(GF_Proto *proto) +{ + GF_Node *n; + if (!proto) return TAG_UndefinedNode; + n = (GF_Node*)gf_list_get(proto->node_code, 0); + if (!n) return TAG_UndefinedNode; + if (n->sgprivate->tag == TAG_ProtoNode) return gf_sg_proto_get_root_tag(((GF_ProtoInstance *)n)->proto_interface); + return n->sgprivate->tag; +} + +Bool gf_sg_proto_field_is_sftime_offset(GF_Node *node, GF_FieldInfo *field) +{ + u32 i; + GF_Route *r; + GF_ProtoInstance *inst; + GF_FieldInfo inf; + if (node->sgprivate->tag != TAG_ProtoNode) return 0; + if (field->fieldType != GF_SG_VRML_SFTIME) return 0; + + inst = (GF_ProtoInstance *) node; + /*check in interface if this is ISed */ + i=0; + while ((r = (GF_Route*)gf_list_enum(inst->proto_interface->sub_graph->Routes, &i))) { + if (!r->IS_route) continue; + /*only check eventIn/field/exposedField*/ + if (r->FromNode || (r->FromField.fieldIndex != field->fieldIndex)) continue; + + gf_node_get_field(r->ToNode, r->ToField.fieldIndex, &inf); + /*IS to another proto*/ + if (r->ToNode->sgprivate->tag == TAG_ProtoNode) return gf_sg_proto_field_is_sftime_offset(r->ToNode, &inf); + /*IS to a startTime/stopTime field*/ + if (!stricmp(inf.name, "startTime") || !stricmp(inf.name, "stopTime")) return 1; + } + return 0; +} + +GF_SceneGraph *Node_GetExternProtoScene(GF_Node *node) +{ + GF_SceneGraph *sg; + sg = node->sgprivate->scenegraph; + if (!sg->pOwningProto) return NULL; + if (!sg->pOwningProto->proto_interface->ExternProto.count) return NULL; + sg = sg->pOwningProto->proto_interface->parent_graph; + while (sg->parent_scene) sg = sg->parent_scene; + return sg; +} + + +GF_EXPORT +GF_Node *gf_node_get_proto_root(GF_Node *node) +{ + if (!node || (node->sgprivate->tag!=TAG_ProtoNode)) return NULL; + return ((GF_ProtoInstance *)node)->RenderingNode; +} + +GF_EXPORT +GF_Node *gf_node_get_proto_parent(GF_Node *node) +{ + if (!node) return NULL; + if (node->sgprivate->scenegraph->pOwningProto) { + GF_Node *the_node = (GF_Node *) node->sgprivate->scenegraph->pOwningProto; + if (the_node != node) return the_node; + } + return NULL; +} + +GF_EXPORT +Bool gf_node_is_proto_root(GF_Node *node) +{ + if (!node) return 0; + if (!node->sgprivate->scenegraph->pOwningProto) return 0; + + if (gf_list_find(node->sgprivate->scenegraph->pOwningProto->node_code, node)>=0) return 1; + return 0; +} diff --git a/src/scenegraph/vrml_route.c b/src/scenegraph/vrml_route.c new file mode 100644 index 0000000..215342e --- /dev/null +++ b/src/scenegraph/vrml_route.c @@ -0,0 +1,360 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +/*MPEG4 & X3D tags (for node tables & script handling)*/ +#include +#include + + +GF_EXPORT +GF_Route *gf_sg_route_new(GF_SceneGraph *sg, GF_Node *fromNode, u32 fromField, GF_Node *toNode, u32 toField) +{ + GF_Route *r; + if (!sg || !toNode || !fromNode) return NULL; + + GF_SAFEALLOC(r, GF_Route) + if (!r) return NULL; + r->FromNode = fromNode; + r->FromField.fieldIndex = fromField; + r->ToNode = toNode; + r->ToField.fieldIndex = toField; + r->graph = sg; + + if (!fromNode->sgprivate->interact) GF_SAFEALLOC(fromNode->sgprivate->interact, struct _node_interactive_ext); + if (!fromNode->sgprivate->interact->routes) fromNode->sgprivate->interact->routes = gf_list_new(); + gf_list_add(fromNode->sgprivate->interact->routes, r); + gf_list_add(sg->Routes, r); + return r; +} + + +void gf_sg_route_del(GF_Route *r) +{ + GF_SceneGraph *sg; + s32 ind; + + gf_sg_route_unqueue(r->graph, r); + + /*remove declared routes*/ + ind = gf_list_del_item(r->graph->Routes, r); + /*remove route from node - do this regardless of setup state since the route is registered upon creation*/ + if (r->FromNode && r->FromNode->sgprivate->interact && r->FromNode->sgprivate->interact->routes) { + gf_list_del_item(r->FromNode->sgprivate->interact->routes, r); + if (!gf_list_count(r->FromNode->sgprivate->interact->routes)) { + gf_list_del(r->FromNode->sgprivate->interact->routes); + r->FromNode->sgprivate->interact->routes = NULL; + } + } + r->is_setup = 0; + sg = r->graph; + while (sg->parent_scene) sg = sg->parent_scene; + gf_list_add(sg->routes_to_destroy, r); +} + + +GF_Err gf_sg_route_del_by_id(GF_SceneGraph *sg,u32 routeID) +{ + GF_Route *r; + if(!sg) return GF_BAD_PARAM; + r = gf_sg_route_find(sg, routeID); + if (!r) return GF_BAD_PARAM; + gf_sg_route_del(r); + return GF_OK; +} + +void gf_sg_destroy_routes(GF_SceneGraph *sg) +{ + while (gf_list_count(sg->routes_to_destroy) ) { + GF_Route *r = (GF_Route *)gf_list_get(sg->routes_to_destroy, 0); + gf_list_rem(sg->routes_to_destroy, 0); + gf_sg_route_unqueue(sg, r); + if (r->name) free(r->name); + free(r); + } +} + + +void gf_sg_route_queue(GF_SceneGraph *sg, GF_Route *r) +{ + u32 now; + if (!sg) return; + + /*get the top level scene (that's the only reliable one regarding simulatioin tick)*/ + while (sg->parent_scene) sg = sg->parent_scene; + /*a single route may not be activated more than once in a simulation tick*/ + now = 1 + sg->simulation_tick; + if (r->lastActivateTime >= now) return; + r->lastActivateTime = now; + gf_list_add(sg->routes_to_activate, r); +} + +/*activate all routes in the order they where triggered*/ +GF_EXPORT +void gf_sg_activate_routes(GF_SceneGraph *sg) +{ + GF_Route *r; + GF_Node *targ; + if (!sg) return; + + sg->simulation_tick++; + + while (gf_list_count(sg->routes_to_activate)) { + r = (GF_Route *)gf_list_get(sg->routes_to_activate, 0); + gf_list_rem(sg->routes_to_activate, 0); + if (r) { + targ = r->ToNode; + if (gf_sg_route_activate(r)) { +#ifdef GF_SELF_REPLACE_ENABLE + if (sg->graph_has_been_reset) { + sg->graph_has_been_reset = 0; + return; + } +#endif + if (r->is_setup) gf_node_changed(targ, &r->ToField); + } + } + } + gf_sg_destroy_routes(sg); +} + +void gf_sg_route_unqueue(GF_SceneGraph *sg, GF_Route *r) +{ + /*get the top level scene*/ + while (sg->parent_scene) sg = sg->parent_scene; + /*remove route from queue list*/ + gf_list_del_item(sg->routes_to_activate, r); +} + + +GF_Route *gf_sg_route_find(GF_SceneGraph *sg, u32 RouteID) +{ + GF_Route *r; + u32 i=0; + while ((r = (GF_Route*)gf_list_enum(sg->Routes, &i))) { + if (r->ID == RouteID) return r; + } + return NULL; +} + +GF_Route *gf_sg_route_find_by_name(GF_SceneGraph *sg, char *name) +{ + GF_Route *r; + u32 i; + if (!sg || !name) return NULL; + + i=0; + while ((r = (GF_Route*)gf_list_enum(sg->Routes, &i))) { + if (r->name && !strcmp(r->name, name)) return r; + } + return NULL; +} + + +GF_Err gf_sg_route_set_id(GF_Route *route, u32 ID) +{ + GF_Route *ptr; + if (!route || !ID) return GF_BAD_PARAM; + + ptr = gf_sg_route_find(route->graph, ID); + if (ptr) return GF_BAD_PARAM; + route->ID = ID; + return GF_OK; +} +u32 gf_sg_route_get_id(GF_Route *route) +{ + return route->ID; +} + +GF_Err gf_sg_route_set_name(GF_Route *route, char *name) +{ + GF_Route *ptr; + if (!name || !route) return GF_BAD_PARAM; + ptr = gf_sg_route_find_by_name(route->graph, name); + if (ptr) return GF_BAD_PARAM; + if (route->name) free(route->name); + route->name = strdup(name); + return GF_OK; +} +char *gf_sg_route_get_name(GF_Route *route) +{ + return route->name; +} + +static void gf_sg_route_setup(GF_Route *r) +{ + gf_node_get_field(r->FromNode, r->FromField.fieldIndex, &r->FromField); + gf_node_get_field(r->ToNode, r->ToField.fieldIndex, &r->ToField); + r->is_setup = 1; +} + +Bool gf_sg_route_activate(GF_Route *r) +{ + Bool ret; + /*URL/String conversion clone*/ + void VRML_FieldCopyCast(void *dest, u32 dst_field_type, void *orig, u32 ori_field_type); + + if (!r->is_setup) { + gf_sg_route_setup(r); + /*special case when initing ISed routes on eventOuts: skip*/ + if (r->IS_route) { + if (r->FromField.eventType == GF_SG_EVENT_OUT) return 0; + if (r->ToField.eventType == GF_SG_EVENT_OUT) return 0; + } + } +#ifndef GPAC_DISABLE_LOG + if (r->IS_route) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[VRML Event] executing %s.%s IS %s.%s\n", gf_node_get_name(r->FromNode), r->FromField.name, gf_node_get_name(r->ToNode), r->ToField.name)); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[VRML Event] executing ROUTE %s.%s TO %s.%s\n", gf_node_get_name(r->FromNode), r->FromField.name, gf_node_get_name(r->ToNode), r->ToField.name)); + } +#endif + + ret = 1; + switch (r->FromField.fieldType) { + case GF_SG_VRML_SFNODE: + if (* (GF_Node **) r->ToField.far_ptr != * (GF_Node **) r->FromField.far_ptr) { + GF_Node *n = * (GF_Node **) r->ToField.far_ptr; + /*delete instance*/ + if (n) gf_node_unregister(n, r->ToNode); + /*and use the node*/ + * (GF_Node **) r->ToField.far_ptr = * (GF_Node **) r->FromField.far_ptr; + n = * (GF_Node **) r->FromField.far_ptr; + gf_node_register(n, r->ToNode); + } + break; + + /*move all pointers to dest*/ + case GF_SG_VRML_MFNODE: + { + GF_ChildNodeItem *last = NULL; + GF_ChildNodeItem *orig = *(GF_ChildNodeItem **)r->FromField.far_ptr; + + /*empty list*/ + gf_node_unregister_children(r->ToNode, *(GF_ChildNodeItem **)r->ToField.far_ptr ); + *(GF_ChildNodeItem **)r->ToField.far_ptr = NULL; + + while (orig) { + gf_node_list_add_child_last( (GF_ChildNodeItem **)r->ToField.far_ptr, orig->node, &last); + gf_node_register(orig->node, r->ToNode); + orig = orig->next; + } + } + break; + + default: + if (r->ToField.fieldType==r->FromField.fieldType) { + /*if unchanged don't invalidate dst node*/ + if (gf_sg_vrml_field_equal(r->ToField.far_ptr, r->FromField.far_ptr, r->FromField.fieldType)) { + ret = 0; + } else { + gf_sg_vrml_field_copy(r->ToField.far_ptr, r->FromField.far_ptr, r->FromField.fieldType); + } + } + /*typecast URL <-> string if needed*/ + else { + VRML_FieldCopyCast(r->ToField.far_ptr, r->ToField.fieldType, r->FromField.far_ptr, r->FromField.fieldType); + } + break; + } + + //if this is a supported eventIn call watcher + if (r->ToField.on_event_in) { + r->ToField.on_event_in(r->ToNode); + } + //if this is a script eventIn call directly script + else if (((r->ToNode->sgprivate->tag==TAG_MPEG4_Script) || (r->ToNode->sgprivate->tag==TAG_X3D_Script) ) + && ((r->ToField.eventType==GF_SG_EVENT_IN) /*|| (r->ToField.eventType==GF_SG_EVENT_FIELD)*/) ) { + gf_sg_script_event_in(r->ToNode, &r->ToField); + } + //check if ISed or not - this will notify the node of any changes + else { + gf_sg_proto_check_field_change(r->ToNode, r->ToField.fieldIndex); + /*only happen on proto, an eventOut may route to an eventOut*/ + if (r->IS_route && r->ToField.eventType==GF_SG_EVENT_OUT) + gf_node_event_out(r->ToNode, r->ToField.fieldIndex); + } + /*and signal routes on exposed fields if field changed*/ + if (r->ToField.eventType == GF_SG_EVENT_EXPOSED_FIELD) + gf_node_event_out(r->ToNode, r->ToField.fieldIndex); + + return ret; +} + + +void gf_node_event_out(GF_Node *node, u32 FieldIndex) +{ + u32 i; + GF_Route *r; + if (!node) return; + + if (!node->sgprivate->interact) return; + + //search for routes to activate in the order they where declared + i=0; + while ((r = (GF_Route*)gf_list_enum(node->sgprivate->interact->routes, &i))) { + if (r->FromNode != node) continue; + if (r->FromField.fieldIndex != FieldIndex) continue; + + /*no postpone for IS routes*/ + if (r->IS_route) { + if (gf_sg_route_activate(r)) + gf_node_changed(r->ToNode, &r->ToField); + } + //queue + else { + gf_sg_route_queue(node->sgprivate->scenegraph, r); + } + } +} + +GF_EXPORT +void gf_node_event_out_str(GF_Node *node, const char *eventName) +{ + u32 i; + GF_Route *r; + + /*node is being deleted ignore event*/ + if (!node->sgprivate->interact) return; + + //this is not an ISed + if (!(node->sgprivate->flags & GF_NODE_IS_DEF) && !node->sgprivate->scenegraph->pOwningProto) return; + + //search for routes to activate in the order they where declared + i=0; + while ((r = (GF_Route*)gf_list_enum(node->sgprivate->interact->routes, &i))) { + if (!r->is_setup) gf_sg_route_setup(r); + if (stricmp(r->FromField.name, eventName)) continue; + + //no postpone + if (r->IS_route) { + gf_sg_route_activate(r); + } + //queue + else { + gf_sg_route_queue(node->sgprivate->scenegraph, r); + } + } +} + diff --git a/src/scenegraph/vrml_script.c b/src/scenegraph/vrml_script.c new file mode 100644 index 0000000..c7cb845 --- /dev/null +++ b/src/scenegraph/vrml_script.c @@ -0,0 +1,302 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +/*MPEG4 & X3D tags (for node tables & script handling)*/ +#include +#include + +#if defined(GPAC_HAS_SPIDERMONKEY) && !defined(__GNUC__) +# if defined(_WIN32_WCE) +# pragma comment(lib, "js") +# elif defined (WIN32) +# pragma comment(lib, "js32") +# endif +#endif + +static u32 script_get_nb_static_field(GF_Node *node) +{ + return (node->sgprivate->tag==TAG_MPEG4_Script) ? 3 : 4; +} + +Bool gf_sg_has_scripting() +{ +#ifdef GPAC_HAS_SPIDERMONKEY + return 1; +#else + return 0; +#endif +} + +void Script_PreDestroy(GF_Node *node, void *eff, Bool is_destroy) +{ + GF_ScriptPriv *priv; + GF_ScriptField *field; + + if (!is_destroy) return; + + priv = (GF_ScriptPriv *)node->sgprivate->UserPrivate; + + if (priv->JS_PreDestroy) priv->JS_PreDestroy(node); + + //destroy extra fields + while (gf_list_count(priv->fields)) { + field = (GF_ScriptField *)gf_list_get(priv->fields, 0); + gf_list_rem(priv->fields, 0); + if (field->pField) { + //if Node unregister + switch (field->fieldType) { + //specific case for GF_Node in script + case GF_SG_VRML_SFNODE: + gf_node_unregister((GF_Node *) field->pField, node); + break; + case GF_SG_VRML_MFNODE: + gf_node_unregister_children(node, (GF_ChildNodeItem*) field->pField); + break; + default: + gf_sg_vrml_field_pointer_del(field->pField, field->fieldType); + break; + } + } + if (field->name) free(field->name); + free(field); + } + gf_list_del(priv->fields); + free(priv); +} + +u32 gf_sg_script_get_num_fields(GF_Node *node, u8 IndexMode) +{ + u32 nb_static; + GF_ScriptPriv *priv = (GF_ScriptPriv *)node->sgprivate->UserPrivate; + switch (IndexMode) { + case GF_SG_FIELD_CODING_IN: + return priv->numIn; + case GF_SG_FIELD_CODING_OUT: + return priv->numOut; + case GF_SG_FIELD_CODING_DEF: + return priv->numDef; + case GF_SG_FIELD_CODING_DYN: + return 0; + default: + nb_static = script_get_nb_static_field(node); + return priv ? gf_list_count(priv->fields) + nb_static : nb_static; + } +} + +GF_Err gf_sg_script_get_field_index(GF_Node *node, u32 inField, u8 IndexMode, u32 *allField) +{ + u32 i; + GF_ScriptField *sf; + u32 nb_static = script_get_nb_static_field(node); + GF_ScriptPriv *priv = (GF_ScriptPriv *)node->sgprivate->UserPrivate; + i=0; + while ((sf = (GF_ScriptField *)gf_list_enum(priv->fields, &i))) { + *allField = i-1+nb_static; + switch (IndexMode) { + case GF_SG_FIELD_CODING_IN: + if ((u32)sf->IN_index==inField) return GF_OK; + break; + case GF_SG_FIELD_CODING_DEF: + if ((u32)sf->DEF_index==inField) return GF_OK; + break; + case GF_SG_FIELD_CODING_OUT: + if ((u32)sf->OUT_index==inField) return GF_OK; + break; + case GF_SG_FIELD_CODING_DYN: + return GF_BAD_PARAM; + default: + if (inField==i-1+nb_static) return GF_OK; + break; + } + } + /*try with default*/ + return gf_sg_mpeg4_node_get_field_index(node, inField, IndexMode, allField); +} + + +GF_Err gf_sg_script_get_field(GF_Node *node, GF_FieldInfo *info) +{ + GF_ScriptField *field; + GF_ScriptPriv *priv; + u32 nb_static; + + if (!info || !node) return GF_BAD_PARAM; + + priv = (GF_ScriptPriv *)gf_node_get_private(node); + nb_static = script_get_nb_static_field(node); + + //static fields + if (info->fieldIndex < nb_static) { + if (nb_static==3) return gf_sg_mpeg4_node_get_field(node, info); + return gf_sg_x3d_node_get_field(node, info); + } + + //dyn fields + field = (GF_ScriptField *)gf_list_get(priv->fields, info->fieldIndex - nb_static); + if (!field) return GF_BAD_PARAM; + + info->eventType = field->eventType; + info->fieldType = field->fieldType; + info->name = field->name; + //we need the eventIn name to activate the function... + info->on_event_in = NULL; + + //setup pointer (special cases for nodes) + switch (field->fieldType) { + case GF_SG_VRML_SFNODE: + case GF_SG_VRML_MFNODE: + info->far_ptr = &field->pField; + info->NDTtype = NDT_SFWorldNode; + break; + default: + info->far_ptr = field->pField; + break; + } + return GF_OK; +} + +void gf_sg_script_init(GF_Node *node) +{ + GF_ScriptPriv *priv; + + + GF_SAFEALLOC(priv, GF_ScriptPriv) + priv->fields = gf_list_new(); + + gf_node_set_private(node, priv); + node->sgprivate->UserCallback = Script_PreDestroy; + + //URL is exposedField (in, out Def) + priv->numDef = priv->numIn = priv->numOut = script_get_nb_static_field(node) - 2; + //directOutput and mustEvaluate are fields (def) + priv->numDef += 2; +} + + +GF_ScriptField *gf_sg_script_field_new(GF_Node *node, u32 eventType, u32 fieldType, const char *name) +{ + GF_ScriptPriv *priv; + GF_ScriptField *field; + if (!name || ((node->sgprivate->tag != TAG_MPEG4_Script) && (node->sgprivate->tag != TAG_X3D_Script))) + return NULL; + + if (eventType > GF_SG_SCRIPT_TYPE_EVENT_OUT) return NULL; + priv = (GF_ScriptPriv *)gf_node_get_private(node); + + GF_SAFEALLOC(field, GF_ScriptField) + field->fieldType = fieldType; + field->name = strdup(name); + + field->DEF_index = field->IN_index = field->OUT_index = -1; + switch (eventType) { + case GF_SG_SCRIPT_TYPE_FIELD: + field->DEF_index = priv->numDef; + priv->numDef++; + field->eventType = GF_SG_EVENT_FIELD; + break; + case GF_SG_SCRIPT_TYPE_EVENT_IN: + field->IN_index = priv->numIn; + priv->numIn++; + field->eventType = GF_SG_EVENT_IN; + break; + case GF_SG_SCRIPT_TYPE_EVENT_OUT: + field->OUT_index = priv->numOut; + field->eventType = GF_SG_EVENT_OUT; + priv->numOut++; + break; + } + //+ static fields + field->ALL_index = script_get_nb_static_field(node) + gf_list_count(priv->fields); + gf_list_add(priv->fields, field); + + //create field entry + if ((fieldType != GF_SG_VRML_SFNODE) && (fieldType != GF_SG_VRML_MFNODE) ) { + field->pField = gf_sg_vrml_field_pointer_new(fieldType); + } + + return field; +} + + +GF_Err gf_sg_script_prepare_clone(GF_Node *dest, GF_Node *orig) +{ + u32 i, type; + GF_ScriptField *sf; + GF_ScriptPriv *dest_priv, *orig_priv; + orig_priv = (GF_ScriptPriv *)orig->sgprivate->UserPrivate; + dest_priv = (GF_ScriptPriv *)dest->sgprivate->UserPrivate; + if (!orig_priv || !dest_priv) return GF_BAD_PARAM; + + i=0; + while ((sf = (GF_ScriptField *)gf_list_enum(orig_priv->fields, &i))) { + switch (sf->eventType) { + case GF_SG_EVENT_IN: + type = GF_SG_SCRIPT_TYPE_EVENT_IN; + break; + case GF_SG_EVENT_OUT: + type = GF_SG_SCRIPT_TYPE_EVENT_OUT; + break; + case GF_SG_EVENT_FIELD: + type = GF_SG_SCRIPT_TYPE_FIELD; + break; + default: + return GF_BAD_PARAM; + } + gf_sg_script_field_new(dest, type, sf->fieldType, sf->name); + } + return GF_OK; +} + +GF_Err gf_sg_script_field_get_info(GF_ScriptField *field, GF_FieldInfo *info) +{ + if (!field || !info) return GF_BAD_PARAM; + memset(info, 0, sizeof(GF_FieldInfo)); + + info->fieldIndex = field->ALL_index; + info->eventType = field->eventType; + info->fieldType = field->fieldType; + info->name = field->name; + + //setup pointer (special cases for nodes) + switch (field->fieldType) { + case GF_SG_VRML_SFNODE: + case GF_SG_VRML_MFNODE: + info->far_ptr = &field->pField; + info->NDTtype = NDT_SFWorldNode; + break; + default: + info->far_ptr = field->pField; + break; + } + return GF_OK; +} + +void gf_sg_script_event_in(GF_Node *node, GF_FieldInfo *in_field) +{ + GF_ScriptPriv *priv = (GF_ScriptPriv *)node->sgprivate->UserPrivate; + if (priv->JS_EventIn) priv->JS_EventIn(node, in_field); +} + + diff --git a/src/scenegraph/vrml_smjs.c b/src/scenegraph/vrml_smjs.c new file mode 100644 index 0000000..9d37cfa --- /dev/null +++ b/src/scenegraph/vrml_smjs.c @@ -0,0 +1,3814 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/*MPEG4 & X3D tags (for node tables & script handling)*/ +#include +#include + +#ifdef GPAC_HAS_SPIDERMONKEY + +#ifndef GPAC_DISABLE_SVG +/*SVG tags for script handling*/ +#include +#endif + +#include +#include + +#include + +void gf_sg_script_to_node_field(struct JSContext *c, jsval v, GF_FieldInfo *field, GF_Node *owner, GF_JSField *parent); +jsval gf_sg_script_to_smjs_field(GF_ScriptPriv *priv, GF_FieldInfo *field, GF_Node *parent, Bool no_cache, Bool force_evaluate); + +JSBool js_has_instance(JSContext *c, JSObject *obj, jsval val, JSBool *vp); + +#define _ScriptMessage(_c, _e, _msg) { \ + GF_Node *_n = (GF_Node *) JS_GetContextPrivate(_c); \ + if (_n->sgprivate->scenegraph->script_action) {\ + GF_JSAPIParam par; \ + par.info.e = (_e); \ + par.info.msg = (_msg); \ + _n->sgprivate->scenegraph->script_action(_n->sgprivate->scenegraph->script_action_cbck, GF_JSAPI_OP_MESSAGE, NULL, &par);\ + } \ + } + +Bool ScriptAction(JSContext *c, GF_SceneGraph *scene, u32 type, GF_Node *node, GF_JSAPIParam *param) +{ + if (!scene) { + GF_Node *n = (GF_Node *) JS_GetContextPrivate(c); + scene = n->sgprivate->scenegraph; + } + if (scene->script_action) + return scene->script_action(scene->script_action_cbck, type, node, param); + return 0; +} + + + +typedef struct +{ + JSRuntime *js_runtime; + u32 nb_inst; + JSClass globalClass; + JSClass browserClass; + JSClass SFNodeClass; + JSClass SFVec2fClass; + JSClass SFVec3fClass; + JSClass SFRotationClass; + JSClass SFColorClass; + JSClass SFImageClass; + JSClass MFInt32Class; + JSClass MFBoolClass; + JSClass MFFloatClass; + JSClass MFTimeClass; + JSClass MFVec2fClass; + JSClass MFVec3fClass; + JSClass MFRotationClass; + JSClass MFColorClass; + JSClass MFStringClass; + JSClass MFUrlClass; + JSClass MFNodeClass; + + /*extensions are loaded for the lifetime of the runtime NOT of the context - this avoids nasty + crashes with multiple contexts in SpiderMonkey (root'ing bug with InitStandardClasses)*/ + GF_List *extensions; +} GF_JSRuntime; + +static GF_JSRuntime *js_rt = NULL; + + + + +void gf_sg_load_script_extensions(GF_SceneGraph *sg, JSContext *c, JSObject *obj, Bool unload) +{ + u32 i, count; + count = gf_list_count(js_rt->extensions); + for (i=0; iextensions, i); + ext->load(ext, sg, c, obj, unload); + } +} + + + +void SFColor_fromHSV(SFColor *col) +{ + Fixed f, q, t, p, hue, sat, val; + u32 i; + hue = col->red; + sat = col->green; + val = col->blue; + if (sat==0) { + col->red = col->green = col->blue = val; + return; + } + if (hue == FIX_ONE) hue = 0; + else hue *= 6; + i = FIX2INT( gf_floor(hue) ); + f = hue-i; + p = gf_mulfix(val, FIX_ONE - sat); + q = gf_mulfix(val, FIX_ONE - gf_mulfix(sat,f)); + t = gf_mulfix(val, FIX_ONE - gf_mulfix(sat, FIX_ONE - f)); + switch (i) { + case 0: col->red = val; col->green = t; col->blue = p; break; + case 1: col->red = q; col->green = val; col->blue = p; break; + case 2: col->red = p; col->green = val; col->blue = t; break; + case 3: col->red = p; col->green = q; col->blue = val; break; + case 4: col->red = t; col->green = p; col->blue = val; break; + case 5: col->red = val; col->green = p; col->blue = q; break; + } +} + +void SFColor_toHSV(SFColor *col) +{ + Fixed h, s; + Fixed _max = MAX(col->red, MAX(col->green, col->blue)); + Fixed _min = MIN(col->red, MAX(col->green, col->blue)); + + s = (_max == 0) ? 0 : gf_divfix(_max - _min, _max); + if (s != 0) { + Fixed rl = gf_divfix(_max - col->red, _max - _min); + Fixed gl = gf_divfix(_max - col->green, _max - _min); + Fixed bl = gf_divfix(_max - col->blue, _max - _min); + if (_max == col->red) { + if (_min == col->green) h = 60*(5+bl); + else h = 60*(1-gl); + } else if (_max == col->green) { + if (_min == col->blue) h = 60*(1+rl); + else h = 60*(3-bl); + } else { + if (_min == col->red) h = 60*(3+gl); + else h = 60*(5-rl); + } + } else { + h = 0; + } + col->red = h; + col->green = s; + col->blue = _max; +} + +static void vrml_node_register(GF_Node *node, GF_Node *parent) +{ + if (node) { + node->sgprivate->flags |= GF_NODE_HAS_BINDING; + gf_node_register(node, parent); + } +} + +static GFINLINE GF_JSField *NewJSField() +{ + GF_JSField *ptr; + GF_SAFEALLOC(ptr, GF_JSField) + return ptr; +} + +static GFINLINE M_Script *JS_GetScript(JSContext *c) +{ + return (M_Script *) JS_GetContextPrivate(c); +} +static GFINLINE GF_ScriptPriv *JS_GetScriptStack(JSContext *c) +{ + M_Script *script = (M_Script *) JS_GetContextPrivate(c); + return script->sgprivate->UserPrivate; +} + +static void script_error(JSContext *c, const char *msg, JSErrorReport *jserr) +{ +// GF_JSInterface *ifce = JS_GetInterface(c); +// if (ifce) ifce->ScriptMessage(ifce->callback, GF_SCRIPT_ERROR, msg); + GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[JavaScript] Error: %s", msg)); +} + +static JSBool JSPrint(JSContext *c, JSObject *p, uintN argc, jsval *argv, jsval *rval) +{ + u32 i; + char buf[5000]; + + strcpy(buf, ""); + for (i = 0; i < argc; i++) { + JSString *str = JS_ValueToString(c, argv[i]); + if (!str) return JS_FALSE; + if (i) strcat(buf, " "); + strcat(buf, JS_GetStringBytes(str)); + } + _ScriptMessage(c, GF_SCRIPT_INFO, buf); + return JS_TRUE; +} + +static JSBool getName(JSContext *c, JSObject *obj, uintN n, jsval *v, jsval *rval) +{ + JSString *s = JS_NewStringCopyZ(c, "GPAC RichMediaEngine"); + if (!s) return JS_FALSE; + *rval = STRING_TO_JSVAL(s); + return JS_TRUE; +} +static JSBool getVersion(JSContext*c, JSObject*obj, uintN n, jsval *v, jsval *rval) +{ + JSString *s = JS_NewStringCopyZ(c, GPAC_FULL_VERSION); + if (!s) return JS_FALSE; + *rval = STRING_TO_JSVAL(s); + return JS_TRUE; +} +static JSBool getCurrentSpeed(JSContext *c, JSObject *o, uintN n, jsval *v, jsval *rval) +{ + GF_JSAPIParam par; + GF_Node *node = JS_GetContextPrivate(c); + par.time = 0; + ScriptAction(c, NULL, GF_JSAPI_OP_GET_SPEED, node->sgprivate->scenegraph->RootNode, &par); + *rval = DOUBLE_TO_JSVAL(JS_NewDouble( c, par.time)); + return JS_TRUE; +} +static JSBool getCurrentFrameRate(JSContext *c, JSObject*o, uintN n, jsval *v, jsval*rval) +{ + GF_JSAPIParam par; + GF_Node *node = JS_GetContextPrivate(c); + par.time = 0; + ScriptAction(c, NULL, GF_JSAPI_OP_GET_FPS, node->sgprivate->scenegraph->RootNode, &par); + *rval = DOUBLE_TO_JSVAL(JS_NewDouble( c, par.time)); + return JS_TRUE; +} +static JSBool getWorldURL(JSContext*c, JSObject*obj, uintN n, jsval *v, jsval *rval) +{ + GF_JSAPIParam par; + GF_Node *node = JS_GetContextPrivate(c); + if (ScriptAction(c, NULL, GF_JSAPI_OP_GET_SCENE_URI, node->sgprivate->scenegraph->RootNode, &par)) { + JSString *s = JS_NewStringCopyZ(c, par.uri.url); + if (!s) return JS_FALSE; + *rval = STRING_TO_JSVAL(s); + return JS_TRUE; + } + return JS_FALSE; +} +static JSBool getScript(JSContext*c, JSObject*obj, uintN n, jsval *v, jsval *rval) +{ + JSObject *an_obj; + GF_JSField *field; + GF_ScriptPriv *priv = JS_GetScriptStack(c); + GF_Node *node = JS_GetContextPrivate(c); + + vrml_node_register(node, NULL); + field = NewJSField(); + field->field.fieldType = GF_SG_VRML_SFNODE; + field->temp_node = node; + field->field.far_ptr = &field->temp_node; + + an_obj = JS_NewObject(priv->js_ctx, &js_rt->SFNodeClass, 0, priv->js_obj); + field->obj = an_obj; + JS_SetPrivate(c, an_obj, field); + *rval = OBJECT_TO_JSVAL(an_obj); + return JS_TRUE; +} +static JSBool getProto(JSContext*c, JSObject*obj, uintN n, jsval *v, jsval *rval) +{ + JSObject *an_obj; + GF_JSField *field; + GF_ScriptPriv *priv = JS_GetScriptStack(c); + GF_Node *node = JS_GetContextPrivate(c); + if (!node->sgprivate->scenegraph->pOwningProto) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + node = (GF_Node *) node->sgprivate->scenegraph->pOwningProto; + vrml_node_register(node, NULL); + field = NewJSField(); + field->field.fieldType = GF_SG_VRML_SFNODE; + field->temp_node = node; + field->field.far_ptr = &field->temp_node; + + an_obj = JS_NewObject(priv->js_ctx, &js_rt->SFNodeClass, 0, priv->js_obj); + field->obj = an_obj; + JS_SetPrivate(c, an_obj, field); + *rval = OBJECT_TO_JSVAL(an_obj); + return JS_TRUE; +} +static JSBool getElementById(JSContext*c, JSObject*obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_Node *elt; + JSObject *an_obj; + GF_JSField *field; + char *name = NULL; + u32 ID = 0; + GF_ScriptPriv *priv = JS_GetScriptStack(c); + GF_Node *sc = JS_GetContextPrivate(c); + if (JSVAL_IS_STRING(argv[0])) name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + else if (JSVAL_IS_INT(argv[0])) ID = JSVAL_TO_INT(argv[0]); + + if (!ID && !name) return JS_FALSE; + + elt = NULL; + if (ID) elt = gf_sg_find_node(sc->sgprivate->scenegraph, ID); + else elt = gf_sg_find_node_by_name(sc->sgprivate->scenegraph, name); + if (!elt) return JS_TRUE; + + vrml_node_register(elt, NULL); + field = NewJSField(); + field->field.fieldType = GF_SG_VRML_SFNODE; + field->temp_node = elt; + field->field.far_ptr = &field->temp_node; + + an_obj = JS_NewObject(c, &js_rt->SFNodeClass, 0, priv->js_obj); + field->obj = an_obj; + JS_SetPrivate(c, an_obj, field); + *rval = OBJECT_TO_JSVAL(an_obj); + return JS_TRUE; +} + +static JSBool replaceWorld(JSContext*c, JSObject*o, uintN n, jsval *v, jsval *rv) +{ + return JS_TRUE; +} +static JSBool addRoute(JSContext*c, JSObject*o, uintN argc, jsval *argv, jsval *rv) +{ + GF_JSField *ptr; + GF_Node *n1, *n2; + char *f1, *f2; + GF_FieldInfo info; + GF_Route *r; + u32 f_id1, f_id2; + if (argc!=4) return JS_FALSE; + if (!JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + if (!JSVAL_IS_OBJECT(argv[2]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[2]), &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + if (!JSVAL_IS_STRING(argv[1]) || !JSVAL_IS_STRING(argv[3])) return JS_FALSE; + + ptr = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])); + assert(ptr->field.fieldType==GF_SG_VRML_SFNODE); + n1 = * ((GF_Node **)ptr->field.far_ptr); + ptr = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[2])); + assert(ptr->field.fieldType==GF_SG_VRML_SFNODE); + n2 = * ((GF_Node **)ptr->field.far_ptr); + + if (!n1 || !n2) return JS_FALSE; + f1 = JS_GetStringBytes(JSVAL_TO_STRING(argv[1])); + f2 = JS_GetStringBytes(JSVAL_TO_STRING(argv[3])); + if (!f1 || !f2) return JS_FALSE; + + if (!strnicmp(f1, "_field", 6)) { + f_id1 = atoi(f1+6); + if (gf_node_get_field(n1, f_id1, &info) != GF_OK) return JS_FALSE; + } else { + if (gf_node_get_field_by_name(n1, f1, &info) != GF_OK) return JS_FALSE; + f_id1 = info.fieldIndex; + } + if (!strnicmp(f2, "_field", 6)) { + f_id2 = atoi(f2+6); + if (gf_node_get_field(n2, f_id2, &info) != GF_OK) return JS_FALSE; + } else { + if ((n2->sgprivate->tag==TAG_MPEG4_Script) || (n2->sgprivate->tag==TAG_X3D_Script)) { + GF_FieldInfo src = info; + if (gf_node_get_field_by_name(n2, f2, &info) != GF_OK) { + gf_sg_script_field_new(n2, GF_SG_SCRIPT_TYPE_EVENT_IN, src.fieldType, f2); + } + } + if (gf_node_get_field_by_name(n2, f2, &info) != GF_OK) return JS_FALSE; + f_id2 = info.fieldIndex; + } + r = gf_sg_route_new(n1->sgprivate->scenegraph, n1, f_id1, n2, f_id2); + if (!r) return JS_FALSE; + return JS_TRUE; +} +static JSBool deleteRoute(JSContext*c, JSObject*o, uintN argc, jsval *argv, jsval *rv) +{ + GF_JSField *ptr; + GF_Node *n1, *n2; + char *f1, *f2; + GF_FieldInfo info; + GF_Route *r; + u32 f_id1, f_id2, i; + if (argc!=4) return JS_FALSE; + if (!JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + if (!JSVAL_IS_OBJECT(argv[2]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[2]), &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + if (!JSVAL_IS_STRING(argv[1]) || !JSVAL_IS_STRING(argv[3])) return JS_FALSE; + + ptr = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])); + assert(ptr->field.fieldType==GF_SG_VRML_SFNODE); + n1 = * ((GF_Node **)ptr->field.far_ptr); + ptr = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[2])); + assert(ptr->field.fieldType==GF_SG_VRML_SFNODE); + n2 = * ((GF_Node **)ptr->field.far_ptr); + + if (!n1 || !n2) return JS_FALSE; + if (!n1->sgprivate->interact) return JS_TRUE; + + f1 = JS_GetStringBytes(JSVAL_TO_STRING(argv[1])); + f2 = JS_GetStringBytes(JSVAL_TO_STRING(argv[3])); + if (!f1 || !f2) return JS_FALSE; + + if (!strnicmp(f1, "_field", 6)) { + f_id1 = atoi(f1+6); + if (gf_node_get_field(n1, f_id1, &info) != GF_OK) return JS_FALSE; + } else { + if (gf_node_get_field_by_name(n1, f1, &info) != GF_OK) return JS_FALSE; + f_id1 = info.fieldIndex; + } + if (!strnicmp(f2, "_field", 6)) { + f_id2 = atoi(f2+6); + if (gf_node_get_field(n2, f_id2, &info) != GF_OK) return JS_FALSE; + } else { + if (gf_node_get_field_by_name(n2, f2, &info) != GF_OK) return JS_FALSE; + f_id2 = info.fieldIndex; + } + + i=0; + while ((r = gf_list_enum(n1->sgprivate->interact->routes, &i))) { + if (r->FromField.fieldIndex != f_id1) continue; + if (r->ToNode != n2) continue; + if (r->ToField.fieldIndex != f_id2) continue; + gf_sg_route_del(r); + return JS_TRUE; + } + return JS_TRUE; +} + +static JSBool loadURL(JSContext*c, JSObject*obj, uintN argc, jsval *argv, jsval *rval) +{ + u32 i; + GF_JSAPIParam par; + jsval item; + jsuint len; + JSObject *p; + GF_JSField *f; + M_Script *script = (M_Script *) JS_GetContextPrivate(c); + + if (argc < 1) return JS_FALSE; + + if (JSVAL_IS_STRING(argv[0])) { + JSString *str = JSVAL_TO_STRING(argv[0]); + par.uri.url = JS_GetStringBytes(str); + /*TODO add support for params*/ + par.uri.nb_params = 0; + if (ScriptAction(c, NULL, GF_JSAPI_OP_LOAD_URL, (GF_Node *)script, &par)) + return JS_TRUE; + return JS_FALSE; + } + if (!JSVAL_IS_OBJECT(argv[0])) return JS_FALSE; + + JS_ValueToObject(c, argv[0], &p); + + f = (GF_JSField *) JS_GetPrivate(c, p); + if (!f || !f->js_list) return JS_FALSE; + JS_GetArrayLength(c, f->js_list, &len); + + for (i=0; ijs_list, (jsint) i, &item); + + if (JSVAL_IS_STRING(item)) { + JSString *str = JSVAL_TO_STRING(item); + par.uri.url = JS_GetStringBytes(str); + /*TODO add support for params*/ + par.uri.nb_params = 0; + if (ScriptAction(c, NULL, GF_JSAPI_OP_LOAD_URL, (GF_Node*)script, &par) ) + return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool setDescription(JSContext*c, JSObject*o, uintN argc, jsval *argv, jsval *rv) +{ + GF_JSAPIParam par; + GF_Node *node = JS_GetContextPrivate(c); + if (!argc || !JSVAL_IS_STRING(argv[0])) return JS_FALSE; + par.uri.url = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + ScriptAction(c, NULL, GF_JSAPI_OP_SET_TITLE, node->sgprivate->scenegraph->RootNode, &par); + return JS_TRUE; +} + +static JSBool createVrmlFromString(JSContext*c, JSObject*obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_ScriptPriv *priv; + GF_FieldInfo field; + /*BT/VRML from string*/ + GF_List *gf_sm_load_bt_from_string(GF_SceneGraph *in_scene, char *node_str); + JSString *js_str; + char *str; + GF_List *nlist; + GF_Node *sc_node = JS_GetContextPrivate(c); + if (argc < 1) return JS_FALSE; + + if (!JSVAL_IS_STRING(argv[0])) return JS_FALSE; + js_str = JSVAL_TO_STRING(argv[0]); + str = JS_GetStringBytes(js_str); + nlist = gf_sm_load_bt_from_string(sc_node->sgprivate->scenegraph, str); + if (!nlist) return JSVAL_NULL; + + priv = JS_GetScriptStack(c); + memset(&field, 0, sizeof(GF_FieldInfo)); + field.fieldType = GF_SG_VRML_MFNODE; + field.far_ptr = &nlist; + *rval = gf_sg_script_to_smjs_field(priv, &field, NULL, 0, 0); + + /*don't forget to unregister all this stuff*/ + while (gf_list_count(nlist)) { + GF_Node *n = gf_list_get(nlist, 0); + gf_list_rem(nlist, 0); + gf_node_unregister(n, NULL); + } + gf_list_del(nlist); + return JS_TRUE; +} + +static JSBool getOption(JSContext*c, JSObject*obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *s; + GF_JSAPIParam par; + GF_Node *sc_node = JS_GetContextPrivate(c); + JSString *js_sec_name, *js_key_name; + if (argc < 2) return JSVAL_FALSE; + + if (!JSVAL_IS_STRING(argv[0])) return JSVAL_FALSE; + js_sec_name = JSVAL_TO_STRING(argv[0]); + par.gpac_cfg.section = JS_GetStringBytes(js_sec_name); + + if (!JSVAL_IS_STRING(argv[1])) return JSVAL_FALSE; + js_key_name = JSVAL_TO_STRING(argv[1]); + par.gpac_cfg.key = JS_GetStringBytes(js_key_name); + par.gpac_cfg.key_val = NULL; + + if (!ScriptAction(c, NULL, GF_JSAPI_OP_GET_OPT, sc_node->sgprivate->scenegraph->RootNode, &par)) + return JSVAL_FALSE; + + s = JS_NewStringCopyZ(c, par.gpac_cfg.key_val ? (const char *)par.gpac_cfg.key_val : ""); + if (!s) return JS_FALSE; + *rval = STRING_TO_JSVAL(s); + return JS_TRUE; +} + +static JSBool setOption(JSContext*c, JSObject*obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_JSAPIParam par; + GF_Node *sc_node = JS_GetContextPrivate(c); + JSString *js_sec_name, *js_key_name, *js_key_val; + if (argc < 3) return JSVAL_FALSE; + + if (!JSVAL_IS_STRING(argv[0])) return JSVAL_FALSE; + js_sec_name = JSVAL_TO_STRING(argv[0]); + par.gpac_cfg.section = JS_GetStringBytes(js_sec_name); + + if (!JSVAL_IS_STRING(argv[1])) return JSVAL_FALSE; + js_key_name = JSVAL_TO_STRING(argv[1]); + par.gpac_cfg.key = JS_GetStringBytes(js_key_name); + + if (!JSVAL_IS_STRING(argv[2])) return JSVAL_FALSE; + js_key_val = JSVAL_TO_STRING(argv[2]); + par.gpac_cfg.key_val = JS_GetStringBytes(js_key_val); + + if (!ScriptAction(c, NULL, GF_JSAPI_OP_SET_OPT, sc_node->sgprivate->scenegraph->RootNode, &par)) + return JSVAL_FALSE; + + return JSVAL_TRUE; +} + +void Script_FieldChanged(JSContext *c, GF_Node *parent, GF_JSField *parent_owner, GF_FieldInfo *field) +{ + GF_ScriptPriv *priv; + u32 script_field; + u32 i; + GF_ScriptField *sf; + + + if (!parent) { + parent = parent_owner->owner; + field = &parent_owner->field; + } + if (!parent) return; + + script_field = 0; + if ((parent->sgprivate->tag == TAG_MPEG4_Script) || (parent->sgprivate->tag == TAG_X3D_Script) ) { + script_field = 1; + if ( (GF_Node *) JS_GetContextPrivate(c) == parent) script_field = 2; + } + + if (script_field!=2) { + if (field->on_event_in) field->on_event_in(parent); + else if (script_field && (field->eventType==GF_SG_EVENT_IN) ) { + gf_sg_script_event_in(parent, field); + gf_node_changed_internal(parent, field, 0); + return; + } + /*field has changed, set routes...*/ + if (parent->sgprivate->tag == TAG_ProtoNode)gf_sg_proto_check_field_change(parent, field->fieldIndex); + else { + gf_node_event_out(parent, field->fieldIndex); + gf_node_changed_internal(parent, field, 0); + } + return; + } + /*otherwise mark field if eventOut*/ + if (parent_owner || parent) { + priv = parent ? parent->sgprivate->UserPrivate : parent_owner->owner->sgprivate->UserPrivate; + i=0; + while ((sf = gf_list_enum(priv->fields, &i))) { + if (sf->ALL_index == field->fieldIndex) { + /*queue eventOut*/ + if (sf->eventType == GF_SG_EVENT_OUT) { + sf->activate_event_out = 1; + } + } + } + } +} + +JSBool gf_sg_script_eventout_set_prop(JSContext *c, JSObject *obj, jsval id, jsval *val) +{ + u32 i; + const char *eventName; + GF_ScriptPriv *script; + GF_Node *n; + GF_ScriptField *sf; + GF_FieldInfo info; + JSString *str = JS_ValueToString(c, id); + if (!str) return JS_FALSE; + + eventName = JS_GetStringBytes(str); + + script = JS_GetScriptStack(c); + if (!script) return JS_FALSE; + n = (GF_Node *) JS_GetScript(c); + + i=0; + while ((sf = gf_list_enum(script->fields, &i))) { + if (!stricmp(sf->name, eventName)) { + gf_node_get_field(n, sf->ALL_index, &info); + gf_sg_script_to_node_field(c, *val, &info, n, NULL); + sf->activate_event_out = 1; + return JS_TRUE; + } + } + return JS_FALSE; +} + + +/*generic ToString method*/ +static GFINLINE void sffield_toString(char *str, void *f_ptr, u32 fieldType) +{ + char temp[1000]; + + switch (fieldType) { + case GF_SG_VRML_SFVEC2F: + { + SFVec2f val = * ((SFVec2f *) f_ptr); + sprintf(temp, "%f %f", FIX2FLT(val.x), FIX2FLT(val.y)); + strcat(str, temp); + break; + } + case GF_SG_VRML_SFVEC3F: + { + SFVec3f val = * ((SFVec3f *) f_ptr); + sprintf(temp, "%f %f %f", FIX2FLT(val.x), FIX2FLT(val.y), FIX2FLT(val.z)); + strcat(str, temp); + break; + } + case GF_SG_VRML_SFVEC4F: + { + SFVec4f val = * ((SFVec4f *) f_ptr); + sprintf(temp, "%f %f %f %f", FIX2FLT(val.x), FIX2FLT(val.y), FIX2FLT(val.z), FIX2FLT(val.q)); + strcat(str, temp); + break; + } + case GF_SG_VRML_SFROTATION: + { + SFRotation val = * ((SFRotation *) f_ptr); + sprintf(temp, "%f %f %f %f", FIX2FLT(val.x), FIX2FLT(val.y), FIX2FLT(val.z), FIX2FLT(val.q)); + strcat(str, temp); + break; + } + case GF_SG_VRML_SFCOLOR: + { + SFColor val = * ((SFColor *) f_ptr); + sprintf(temp, "%f %f %f", val.red, val.green, val.blue); + strcat(str, temp); + break; + } + case GF_SG_VRML_SFIMAGE: + { + SFImage *val = ((SFImage *)f_ptr); + sprintf(temp, "%dx%dx%d", val->width, val->height, val->numComponents); + strcat(str, temp); + break; + } + + } +} + +static void JS_ObjectDestroyed(JSContext *c, JSObject *obj) +{ + GF_ScriptPriv *priv = JS_GetScriptStack(c); + + JS_SetPrivate(c, obj, 0); + if (priv->js_cache) + gf_list_del_item(priv->js_cache, obj); +} + + +static JSBool field_toString(JSContext *c, JSObject *obj, uintN n, jsval *v, jsval *rval) +{ + u32 i, len; + jsdouble d; + char temp[1000]; + char str[5000]; + JSString *s; + jsval item; + GF_JSField *f = (GF_JSField *) JS_GetPrivate(c, obj); + if (!f) return JS_FALSE; + + strcpy(str, ""); + + if (gf_sg_vrml_is_sf_field(f->field.fieldType)) { + sffield_toString(str, f->field.far_ptr, f->field.fieldType); + } else { + if (f->field.fieldType == GF_SG_VRML_MFNODE) return JS_TRUE; + + strcat(str, "["); + JS_GetArrayLength(c, f->js_list, &len); + for (i=0; ijs_list, (jsint) i, &item); + switch (f->field.fieldType) { + case GF_SG_VRML_MFBOOL: + sprintf(temp, "%s", JSVAL_TO_BOOLEAN(item) ? "TRUE" : "FALSE"); + strcat(str, temp); + break; + case GF_SG_VRML_MFINT32: + sprintf(temp, "%d", JSVAL_TO_INT(item)); + strcat(str, temp); + break; + case GF_SG_VRML_MFFLOAT: + case GF_SG_VRML_MFTIME: + JS_ValueToNumber(c, item, &d); + sprintf(temp, "%g", d); + strcat(str, temp); + break; + case GF_SG_VRML_MFSTRING: + case GF_SG_VRML_MFURL: + { + JSString *j_str = JSVAL_TO_STRING(item); + char *str_val = JS_GetStringBytes(j_str); + strcat(str, str_val); + } + break; + default: + if (JSVAL_IS_OBJECT(item)) { + GF_JSField *sf = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(item)); + sffield_toString(str, sf->field.far_ptr, sf->field.fieldType); + } + break; + } + if (ifield.fieldType = GF_SG_VRML_SFNODE; + field->temp_node = NULL; + field->field.far_ptr = &field->temp_node; + JS_SetPrivate(c, obj, field); + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + if (!JS_InstanceOf(c, obj, &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + + + str = JS_ValueToString(c, argv[0]); + if (!str) return JS_FALSE; + + ID = 0; + node_name = JS_GetStringBytes(str); + if (!strnicmp(node_name, "_proto", 6)) { + ID = atoi(node_name+6); + node_name = NULL; + +locate_proto: + + /*locate proto in current graph and all parents*/ + sg = sc->sgprivate->scenegraph; + while (1) { + proto = gf_sg_find_proto(sg, ID, node_name); + if (proto) break; + if (!sg->parent_scene) break; + sg = sg->parent_scene; + } + if (!proto) return JS_FALSE; + /* create interface and load code in current graph*/ + new_node = gf_sg_proto_create_instance(sc->sgprivate->scenegraph, proto); + if (!new_node) return JS_FALSE; + /*OK, instanciate proto code*/ + if (gf_sg_proto_load_code(new_node) != GF_OK) { + gf_node_unregister(new_node, NULL); + return JS_FALSE; + } + } else { + if (sc->sgprivate->tag==TAG_MPEG4_Script) { + tag = gf_node_mpeg4_type_by_class_name(node_name); + } else { + tag = gf_node_x3d_type_by_class_name(node_name); + } + if (!tag) goto locate_proto; + new_node = gf_node_new(sc->sgprivate->scenegraph, tag); + if (!new_node) return JS_FALSE; + gf_node_init(new_node); + } + + vrml_node_register(new_node, NULL); + field = NewJSField(); + field->field.fieldType = GF_SG_VRML_SFNODE; + field->temp_node = new_node; + field->field.far_ptr = &field->temp_node; + + field->obj = obj; + JS_AddRoot(c, &field->obj); + if (priv->js_cache) gf_list_add(priv->js_cache, obj); + + JS_SetPrivate(c, obj, field); + *rval = OBJECT_TO_JSVAL(obj); + + return JS_TRUE; +} +static void node_finalize_ex(JSContext *c, JSObject *obj, Bool is_js_call) +{ + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + + if (is_js_call) + JS_ObjectDestroyed(c, obj); + + if (ptr) { + /*num_instances may be 0 if the node is the script being destroyed*/ + if (ptr->temp_node && ptr->temp_node->sgprivate->num_instances) { + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] unregistering node %s (%s)\n", gf_node_get_name(ptr->temp_node), gf_node_get_class_name(ptr->temp_node))); + gf_node_unregister(ptr->temp_node, ptr->owner); + } + free(ptr); + } +} +static void node_finalize(JSContext *c, JSObject *obj) +{ + node_finalize_ex(c, obj, 1); +} + +static JSBool node_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_Node *n; + u32 index; + JSString *str; + GF_FieldInfo info; + GF_JSField *ptr; + GF_ScriptPriv *priv; + + if (! JS_InstanceOf(c, obj, &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + ptr = (GF_JSField *) JS_GetPrivate(c, obj); + assert(ptr->field.fieldType==GF_SG_VRML_SFNODE); + n = * ((GF_Node **)ptr->field.far_ptr); + priv = JS_GetScriptStack(c); + + if (n && JSVAL_IS_STRING(id) && ( (str = JSVAL_TO_STRING(id)) != 0) ) { + char *fieldName = JS_GetStringBytes(str); + if (!strnicmp(fieldName, "toString", 8)) { + return JS_TRUE; + } + /*fieldID indexing*/ + if (!strnicmp(fieldName, "_field", 6)) { + index = atoi(fieldName+6); + if ( gf_node_get_field(n, index, &info) == GF_OK) { + *vp = gf_sg_script_to_smjs_field(priv, &info, n, 0, 0); + return JS_TRUE; + } + } else if ( gf_node_get_field_by_name(n, fieldName, &info) == GF_OK) { + *vp = gf_sg_script_to_smjs_field(priv, &info, n, 0, 0); + return JS_TRUE; + } + return JS_TRUE; + } + return JS_FALSE; +} + +static JSBool node_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_Node *n; + GF_FieldInfo info; + u32 index; + char *fieldname; + GF_JSField *ptr; + if (! JS_InstanceOf(c, obj, &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + ptr = (GF_JSField *) JS_GetPrivate(c, obj); + + /*this is the prototype*/ + if (!ptr) { + if (! JSVAL_IS_STRING(id)) return JS_FALSE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] setting new prop/func %s for object prototype\n", JS_GetStringBytes(JSVAL_TO_STRING(id)) )); + return JS_TRUE; + } + + assert(ptr->field.fieldType==GF_SG_VRML_SFNODE); + n = * ((GF_Node **)ptr->field.far_ptr); + + if (n && JSVAL_IS_STRING(id)) { + JSString *str = JSVAL_TO_STRING(id); + fieldname = JS_GetStringBytes(str); + + /*fieldID indexing*/ + if (!strnicmp(fieldname, "_field", 6)) { + index = atoi(fieldname+6); + if ( gf_node_get_field(n, index, &info) != GF_OK) return JS_TRUE; + } else { + if (gf_node_get_field_by_name(n, fieldname, &info) != GF_OK) { + /*VRML style*/ + if (!strnicmp(fieldname, "set_", 4)) { + fieldname+=4; + if (gf_node_get_field_by_name(n, fieldname, &info) != GF_OK) return JS_TRUE; + } else + return JS_TRUE; + } + } + if (gf_node_get_tag(n)==TAG_ProtoNode) + gf_sg_proto_mark_field_loaded(n, &info); + + gf_sg_script_to_node_field(c, *vp, &info, n, ptr); + } + return JS_TRUE; +} +static JSBool node_toString(JSContext *c, JSObject *obj, uintN i, jsval *v, jsval *rval) +{ + char str[1000]; + u32 id; + GF_Node *n; + JSString *s; + GF_JSField *f; + const char *name; + if (! JS_InstanceOf(c, obj, &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + f = (GF_JSField *) JS_GetPrivate(c, obj); + if (!f) return JS_FALSE; + + str[0] = 0; + n = * ((GF_Node **)f->field.far_ptr); + + name = gf_node_get_name_and_id(n, &id); + if (id) { + if (name) { + sprintf(str , "DEF %s ", name); + } else { + sprintf(str , "DEF %d ", id - 1); + } + } + strcat(str, gf_node_get_class_name(n)); + s = JS_NewStringCopyZ(c, (const char *) str); + if (!s) return JS_FALSE; + *rval = STRING_TO_JSVAL(s); + return JS_TRUE; +} + +/* Generic field destructor */ +static void field_finalize(JSContext *c, JSObject *obj) +{ + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + JS_ObjectDestroyed(c, obj); + if (!ptr) return; + + if (ptr->field_ptr) gf_sg_vrml_field_pointer_del(ptr->field_ptr, ptr->field.fieldType); + free(ptr); +} + + + + + +/*SFImage class functions */ +static GFINLINE GF_JSField *SFImage_Create(JSContext *c, JSObject *obj, u32 w, u32 h, u32 nbComp, MFInt32 *pixels) +{ + u32 i, len; + GF_JSField *field; + SFImage *v; + field = NewJSField(); + v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFIMAGE); + field->field_ptr = field->field.far_ptr = v; + field->field.fieldType = GF_SG_VRML_SFIMAGE; + v->width = w; + v->height = h; + v->numComponents = nbComp; + v->pixels = (u8 *) malloc(sizeof(u8) * nbComp * w * h); + len = MIN(nbComp * w * h, pixels->count); + for (i=0; ipixels[i] = (u8) pixels->vals[i]; + JS_SetPrivate(c, obj, field); + return field; +} +static JSBool SFImageConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rv) +{ + u32 w, h, nbComp; + MFInt32 *pixels; + if (argc<4) return 0; + if (!JSVAL_IS_INT(argv[0]) || !JSVAL_IS_INT(argv[1]) || !JSVAL_IS_INT(argv[2])) return JS_FALSE; + if (!JSVAL_IS_OBJECT(argv[3]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[3]), &js_rt->MFInt32Class, NULL)) return JS_FALSE; + w = JSVAL_TO_INT(argv[0]); + h = JSVAL_TO_INT(argv[1]); + nbComp = JSVAL_TO_INT(argv[2]); + pixels = (MFInt32 *) ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[3])))->field.far_ptr; + SFImage_Create(c, obj, w, h, nbComp, pixels); + return JS_TRUE; +} +static JSBool image_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_ScriptPriv *priv = JS_GetScriptStack(c); + GF_JSField *val = (GF_JSField *) JS_GetPrivate(c, obj); + SFImage *sfi; + if (!val) return JS_FALSE; + sfi = (SFImage*)val->field.far_ptr; + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + case 0: *vp = INT_TO_JSVAL( sfi->width ); break; + case 1: *vp = INT_TO_JSVAL( sfi->height); break; + case 2: *vp = INT_TO_JSVAL( sfi->numComponents ); break; + case 3: + { + u32 i, len; + JSObject *an_obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFInt32Class, 0, priv->js_obj); + len = sfi->width*sfi->height*sfi->numComponents; + for (i=0; ipixels[i]); + JS_SetElement(priv->js_ctx, an_obj, (jsint) i, &newVal); + } + } + break; + default: + return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool image_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + u32 ival; + Bool changed = 0; + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + SFImage *sfi; + + /*this is the prototype*/ + if (!ptr) { + if (! JSVAL_IS_STRING(id)) return JS_FALSE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] setting new prop/func %s for object prototype\n", JS_GetStringBytes(JSVAL_TO_STRING(id)) )); + return JS_TRUE; + } + sfi = (SFImage*)ptr->field.far_ptr; + + if (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0 && JSVAL_TO_INT(id) < 4) { + switch (JSVAL_TO_INT(id)) { + case 0: + ival = JSVAL_TO_INT(vp); + changed = ! (sfi->width == ival); + sfi->width = ival; + if (changed && sfi->pixels) { free(sfi->pixels); sfi->pixels = NULL; } + break; + case 1: + ival = JSVAL_TO_INT(vp); + changed = ! (sfi->height == ival); + sfi->height = ival; + if (changed && sfi->pixels) { free(sfi->pixels); sfi->pixels = NULL; } + break; + case 2: + ival = JSVAL_TO_INT(vp); + changed = ! (sfi->numComponents == ival); + sfi->numComponents = ival; + if (changed && sfi->pixels) { free(sfi->pixels); sfi->pixels = NULL; } + break; + case 3: + { + MFInt32 *pixels; + u32 len, i; + if (!JSVAL_IS_OBJECT(*vp) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(*vp), &js_rt->MFInt32Class, NULL)) return JS_FALSE; + pixels = (MFInt32 *) ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(*vp)))->field.far_ptr; + if (sfi->pixels) free(sfi->pixels); + len = sfi->width*sfi->height*sfi->numComponents; + sfi->pixels = (char *) malloc(sizeof(char)*len); + len = MAX(len, pixels->count); + for (i=0; ipixels[i] = (u8) pixels->vals[i]; + changed = 1; + break; + } + default: return JS_FALSE; + } + if (changed) Script_FieldChanged(c, NULL, ptr, NULL); + return JS_TRUE; + } + return JS_FALSE; +} + +/*SFVec2f class functions */ +static GFINLINE GF_JSField *SFVec2f_Create(JSContext *c, JSObject *obj, Fixed x, Fixed y) +{ + GF_JSField *field; + SFVec2f *v; + field = NewJSField(); + v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFVEC2F); + field->field_ptr = field->field.far_ptr = v; + field->field.fieldType = GF_SG_VRML_SFVEC2F; + v->x = x; + v->y = y; + JS_SetPrivate(c, obj, field); + return field; +} +static JSBool SFVec2fConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rv) +{ + jsdouble x = 0.0, y = 0.0; + if (argc > 0) JS_ValueToNumber(c, argv[0], &x); + if (argc > 1) JS_ValueToNumber(c, argv[1], &y); + SFVec2f_Create(c, obj, FLT2FIX( x), FLT2FIX( y)); + return JS_TRUE; +} +static JSBool vec2f_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_JSField *val = (GF_JSField *) JS_GetPrivate(c, obj); + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + case 0: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFVec2f*)val->field.far_ptr)->x) )); break; + case 1: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFVec2f*)val->field.far_ptr)->y) )); break; + default: return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool vec2f_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + jsdouble d; + Fixed v; + Bool changed = 0; + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + + /*this is the prototype*/ + if (!ptr) { + if (! JSVAL_IS_STRING(id)) return JS_FALSE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] setting new prop/func %s for object prototype\n", JS_GetStringBytes(JSVAL_TO_STRING(id)) )); + return JS_TRUE; + } + + if (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0 && JSVAL_TO_INT(id) < 2 && JS_ValueToNumber(c, *vp, &d)) { + switch (JSVAL_TO_INT(id)) { + case 0: + v = FLT2FIX( d); + changed = ! ( ((SFVec2f*)ptr->field.far_ptr)->x == v); + ((SFVec2f*)ptr->field.far_ptr)->x = v; + break; + case 1: + v = FLT2FIX( d); + changed = ! ( ((SFVec2f*)ptr->field.far_ptr)->y == v); + ((SFVec2f*)ptr->field.far_ptr)->y = v; + break; + default: + return JS_TRUE; + } + if (changed) Script_FieldChanged(c, NULL, ptr, NULL); + return JS_TRUE; + } + return JS_FALSE; +} + +static JSBool vec2f_add(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec2f *v1, *v2; + JSObject *pNew; + if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFVec2fClass, NULL)) + return JS_FALSE; + + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + v2 = ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec2fClass, 0, JS_GetParent(c, obj)); + SFVec2f_Create(c, pNew, v1->x + v2->x, v1->y + v2->y); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec2f_subtract(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec2f *v1, *v2; + JSObject *pNew; + if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFVec2fClass, NULL)) + return JS_FALSE; + + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + v2 = ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec2fClass, 0, JS_GetParent(c, obj)); + SFVec2f_Create(c, pNew, v1->x - v2->x, v1->y - v2->y); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec2f_negate(JSContext *c, JSObject *obj, uintN n, jsval *v, jsval *rval) +{ + SFVec2f *v1; + JSObject *pNew; + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec2fClass, 0, JS_GetParent(c, obj)); + SFVec2f_Create(c, pNew, -v1->x , -v1->y ); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec2f_multiply(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec2f *v1; + JSObject *pNew; + jsdouble d; + Fixed v; + if (argc<=0) return JS_FALSE; + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec2fClass, 0, JS_GetParent(c, obj)); + JS_ValueToNumber(c, argv[0], &d ); + v = FLT2FIX( d); + SFVec2f_Create(c, pNew, gf_mulfix(v1->x , v), gf_mulfix(v1->y, v) ); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec2f_divide(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec2f *v1; + JSObject *pNew; + jsdouble d; + Fixed v; + if (argc<=0) return JS_FALSE; + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec2fClass, 0, JS_GetParent(c, obj)); + JS_ValueToNumber(c, argv[0], &d ); + v = FLT2FIX(d); + SFVec2f_Create(c, pNew, gf_divfix(v1->x, v), gf_divfix(v1->y, v)); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec2f_length(JSContext *c, JSObject *obj, uintN n, jsval *val, jsval *rval) +{ + Double res; + SFVec2f *v1; + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + res = FIX2FLT(gf_v2d_len(v1)); + *rval = DOUBLE_TO_JSVAL(JS_NewDouble(c, res) ); + return JS_TRUE; +} +static JSBool vec2f_normalize(JSContext *c, JSObject *obj, uintN n, jsval *val, jsval *rval) +{ + SFVec2f *v1; + Fixed res; + JSObject *pNew; + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + res = gf_v2d_len(v1); + pNew = JS_NewObject(c, &js_rt->SFVec2fClass, 0, JS_GetParent(c, obj)); + SFVec2f_Create(c, pNew, gf_divfix(v1->x, res), gf_divfix(v1->y, res) ); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec2f_dot(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec2f *v1, *v2; + if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFVec2fClass, NULL)) + return JS_FALSE; + + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + v2 = ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + *rval = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( gf_mulfix(v1->x, v2->x) + gf_mulfix(v1->y, v2->y) ) ) ); + return JS_TRUE; +} + + +/*SFVec3f class functions */ +static GFINLINE GF_JSField *SFVec3f_Create(JSContext *c, JSObject *obj, Fixed x, Fixed y, Fixed z) +{ + GF_JSField *field; + SFVec3f *v; + field = NewJSField(); + v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFVEC3F); + field->field_ptr = field->field.far_ptr = v; + field->field.fieldType = GF_SG_VRML_SFVEC3F; + v->x = x; + v->y = y; + v->z = z; + JS_SetPrivate(c, obj, field); + return field; +} +static JSBool SFVec3fConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rv) +{ + jsdouble x = 0.0, y = 0.0, z = 0.0; + if (argc > 0) JS_ValueToNumber(c, argv[0], &x); + if (argc > 1) JS_ValueToNumber(c, argv[1], &y); + if (argc > 2) JS_ValueToNumber(c, argv[2], &z); + SFVec3f_Create(c, obj, FLT2FIX( x), FLT2FIX( y), FLT2FIX( z)); + return JS_TRUE; +} +static JSBool vec3f_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_JSField *val = (GF_JSField *) JS_GetPrivate(c, obj); + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + case 0: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFVec3f*)val->field.far_ptr)->x) )); break; + case 1: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFVec3f*)val->field.far_ptr)->y) )); break; + case 2: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFVec3f*)val->field.far_ptr)->z) )); break; + default: return JS_TRUE; + } + } + return JS_TRUE; +} +static JSBool vec3f_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + jsdouble d; + Fixed v; + Bool changed = 0; + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + + /*this is the prototype*/ + if (!ptr) { + if (! JSVAL_IS_STRING(id)) return JS_FALSE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] setting new prop/func %s for object prototype\n", JS_GetStringBytes(JSVAL_TO_STRING(id)) )); + return JS_TRUE; + } + + if (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0 && JSVAL_TO_INT(id) < 3 && JS_ValueToNumber(c, *vp, &d)) { + switch (JSVAL_TO_INT(id)) { + case 0: + v = FLT2FIX( d); + changed = ! ( ((SFVec3f*)ptr->field.far_ptr)->x == v); + ((SFVec3f*)ptr->field.far_ptr)->x = v; + break; + case 1: + v = FLT2FIX( d); + changed = ! ( ((SFVec3f*)ptr->field.far_ptr)->y == v); + ((SFVec3f*)ptr->field.far_ptr)->y = v; + break; + case 2: + v = FLT2FIX( d); + changed = ! ( ((SFVec3f*)ptr->field.far_ptr)->z == v); + ((SFVec3f*)ptr->field.far_ptr)->z = v; + break; + default: return JS_TRUE; + } + if (changed) Script_FieldChanged(c, NULL, ptr, NULL); + return JS_TRUE; + } + return JS_FALSE; +} +static JSBool vec3f_add(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec3f *v1, *v2; + JSObject *pNew; + if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFVec3fClass, NULL)) + return JS_FALSE; + + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + v2 = ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec3fClass, 0, JS_GetParent(c, obj)); + SFVec3f_Create(c, pNew, v1->x + v2->x, v1->y + v2->y, v1->z + v2->z); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec3f_subtract(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec3f *v1, *v2; + JSObject *pNew; + if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFVec3fClass, NULL)) + return JS_FALSE; + + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + v2 = ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec3fClass, 0, JS_GetParent(c, obj)); + SFVec3f_Create(c, pNew, v1->x - v2->x, v1->y - v2->y, v1->z - v2->z); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec3f_negate(JSContext *c, JSObject *obj, uintN n, jsval *v, jsval *rval) +{ + SFVec3f *v1; + JSObject *pNew; + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec3fClass, 0, JS_GetParent(c, obj)); + SFVec3f_Create(c, pNew, -v1->x , -v1->y , -v1->z ); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec3f_multiply(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec3f *v1; + JSObject *pNew; + jsdouble d; + Fixed v; + if (argc<=0) return JS_FALSE; + + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec3fClass, 0, JS_GetParent(c, obj)); + JS_ValueToNumber(c, argv[0], &d ); + v = FLT2FIX(d); + SFVec3f_Create(c, pNew, gf_mulfix(v1->x, v), gf_mulfix(v1->y, v), gf_mulfix(v1->z, v) ); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec3f_divide(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec3f *v1; + JSObject *pNew; + jsdouble d; + Fixed v; + if (argc<=0) return JS_FALSE; + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec3fClass, 0, JS_GetParent(c, obj)); + JS_ValueToNumber(c, argv[0], &d ); + v = FLT2FIX(d); + SFVec3f_Create(c, pNew, gf_divfix(v1->x, v), gf_divfix(v1->y, v), gf_divfix(v1->z, v)); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec3f_length(JSContext *c, JSObject *obj, uintN n, jsval *val, jsval *rval) +{ + Fixed res; + SFVec3f *v1; + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + res = gf_vec_len(*v1); + *rval = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT(res)) ); + return JS_TRUE; +} +static JSBool vec3f_normalize(JSContext *c, JSObject *obj, uintN n, jsval *val, jsval *rval) +{ + SFVec3f v1; + JSObject *pNew; + v1 = * (SFVec3f *) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + gf_vec_norm(&v1); + pNew = JS_NewObject(c, &js_rt->SFVec3fClass, 0, JS_GetParent(c, obj)); + SFVec3f_Create(c, pNew, v1.x, v1.y, v1.z); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool vec3f_dot(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec3f v1, v2; + if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFVec3fClass, NULL)) + return JS_FALSE; + + v1 = *(SFVec3f *) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + v2 = *(SFVec3f *) ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + *rval = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT(gf_vec_dot(v1, v2))) ); + return JS_TRUE; +} +static JSBool vec3f_cross(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec3f v1, v2, v3; + JSObject *pNew; + + if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFVec3fClass, NULL)) + return JS_FALSE; + + v1 = * (SFVec3f *) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + v2 = * (SFVec3f *) ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec3fClass, 0, JS_GetParent(c, obj)); + v3 = gf_vec_cross(v1, v2); + SFVec3f_Create(c, pNew, v3.x, v3.y, v3.z); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} + + + +/*SFRotation class*/ +static GFINLINE GF_JSField *SFRotation_Create(JSContext *c, JSObject *obj, Fixed x, Fixed y, Fixed z, Fixed q) +{ + GF_JSField *field; + SFRotation *v; + field = NewJSField(); + v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFROTATION); + field->field_ptr = field->field.far_ptr = v; + field->field.fieldType = GF_SG_VRML_SFROTATION; + v->x = x; + v->y = y; + v->z = z; + v->q = q; + JS_SetPrivate(c, obj, field); + return field; +} +static JSBool SFRotationConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rv) +{ + JSObject *an_obj; + SFVec3f v1, v2; + Fixed l1, l2, dot; + jsdouble x = 0.0, y = 0.0, z = 0.0, a = 0.0; + if (argc == 0) { + SFRotation_Create(c, obj, FLT2FIX(x), FLT2FIX(y), FIX_ONE, FLT2FIX(a) ); + return JS_TRUE; + } + if ((argc>0) && JSVAL_IS_NUMBER(argv[0])) { + if (argc > 0) JS_ValueToNumber(c, argv[0], &x); + if (argc > 1) JS_ValueToNumber(c, argv[1], &y); + if (argc > 2) JS_ValueToNumber(c, argv[2], &z); + if (argc > 3) JS_ValueToNumber(c, argv[3], &a); + SFRotation_Create(c, obj, FLT2FIX(x), FLT2FIX(y), FLT2FIX(z), FLT2FIX(a)); + return JS_TRUE; + } + if (argc!=2) return JS_FALSE; + if (!JSVAL_IS_OBJECT(argv[0])) return JS_FALSE; + an_obj = JSVAL_TO_OBJECT(argv[0]); + if (! JS_InstanceOf(c, an_obj, &js_rt->SFVec3fClass, NULL)) return JS_FALSE; + v1 = * (SFVec3f *) ((GF_JSField *) JS_GetPrivate(c, an_obj))->field.far_ptr; + if (JSVAL_IS_DOUBLE(argv[1])) { + JS_ValueToNumber(c, argv[1], &a); + SFRotation_Create(c, obj, v1.x, v1.y, v1.z, FLT2FIX(a)); + return JS_TRUE; + } + + if (!JSVAL_IS_OBJECT(argv[1])) return JS_FALSE; + an_obj = JSVAL_TO_OBJECT(argv[1]); + if (!JS_InstanceOf(c, an_obj, &js_rt->SFVec3fClass, NULL)) return JS_FALSE; + v2 = * (SFVec3f *) ((GF_JSField *) JS_GetPrivate(c, an_obj))->field.far_ptr; + l1 = gf_vec_len(v1); + l2 = gf_vec_len(v2); + dot = gf_divfix(gf_vec_dot(v1, v2), gf_mulfix(l1, l2) ); + a = gf_atan2(gf_sqrt(FIX_ONE - gf_mulfix(dot, dot)), dot); + SFRotation_Create(c, obj, gf_mulfix(v1.y, v2.z) - gf_mulfix(v2.y, v1.z), + gf_mulfix(v1.z, v2.x) - gf_mulfix(v2.z, v1.x), + gf_mulfix(v1.x, v2.y) - gf_mulfix(v2.x, v1.y), + FLT2FIX(a)); + return JS_TRUE; +} + +static JSBool rot_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_JSField *val = (GF_JSField *) JS_GetPrivate(c, obj); + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + case 0: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->x)) ); break; + case 1: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->y)) ); break; + case 2: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->z)) ); break; + case 3: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->q)) ); break; + default: return JS_TRUE; + } + } + return JS_TRUE; +} +static JSBool rot_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + jsdouble d; + Fixed v; + Bool changed = 0; + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + + /*this is the prototype*/ + if (!ptr) { + if (! JSVAL_IS_STRING(id)) return JS_FALSE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] setting new prop/func %s for object prototype\n", JS_GetStringBytes(JSVAL_TO_STRING(id)) )); + return JS_TRUE; + } + + if (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0 && JSVAL_TO_INT(id) < 4 && JS_ValueToNumber(c, *vp, &d)) { + switch (JSVAL_TO_INT(id)) { + case 0: + v = FLT2FIX(d); + changed = ! ( ((SFRotation*)ptr->field.far_ptr)->x == v); + ((SFRotation*)ptr->field.far_ptr)->x = v; + break; + case 1: + v = FLT2FIX(d); + changed = ! ( ((SFRotation*)ptr->field.far_ptr)->y == v); + ((SFRotation*)ptr->field.far_ptr)->y = v; + break; + case 2: + v = FLT2FIX(d); + changed = ! ( ((SFRotation*)ptr->field.far_ptr)->z == v); + ((SFRotation*)ptr->field.far_ptr)->z = v; + break; + case 3: + v = FLT2FIX(d); + changed = ! ( ((SFRotation*)ptr->field.far_ptr)->q == v); + ((SFRotation*)ptr->field.far_ptr)->q = v; + break; + default: return JS_TRUE; + } + if (changed) Script_FieldChanged(c, NULL, ptr, NULL); + return JS_TRUE; + } + return JS_FALSE; +} +static JSBool rot_getAxis(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFRotation r; + JSObject *pNew; + r = * (SFRotation *) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFVec3fClass, 0, JS_GetParent(c, obj)); + SFVec3f_Create(c, pNew, r.x, r.y, r.z); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool rot_inverse(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFRotation r; + JSObject *pNew; + r = * (SFRotation *) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + pNew = JS_NewObject(c, &js_rt->SFRotationClass, 0, JS_GetParent(c, obj)); + SFRotation_Create(c, pNew, r.x, r.y, r.z, r.q-GF_PI); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} + +static JSBool rot_multiply(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFRotation r1, r2; + SFVec4f q1, q2; + JSObject *pNew; + + if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFRotationClass, NULL)) + return JS_FALSE; + + r1 = * (SFRotation *) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + r2 = * (SFRotation *) ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + q1 = gf_quat_from_rotation(r1); + q2 = gf_quat_from_rotation(r2); + q1 = gf_quat_multiply(&q1, &q2); + r1 = gf_quat_to_rotation(&q1); + + pNew = JS_NewObject(c, &js_rt->SFRotationClass, 0, JS_GetParent(c, obj)); + SFRotation_Create(c, pNew, r1.x, r1.y, r1.z, r1.q); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool rot_multVec(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec3f v; + SFRotation r; + GF_Matrix mx; + JSObject *pNew; + if (argc<=0) return JS_FALSE; + + if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFVec3fClass, NULL)) + return JS_FALSE; + + r = *(SFRotation *) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + v = *(SFVec3f *) ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + gf_mx_init(mx); + gf_mx_add_rotation(&mx, r.q, r.x, r.y, r.z); + gf_mx_apply_vec(&mx, &v); + pNew = JS_NewObject(c, &js_rt->SFVec3fClass, 0, JS_GetParent(c, obj)); + SFVec3f_Create(c, pNew, v.x, v.y, v.z); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} +static JSBool rot_setAxis(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFVec3f v; + SFRotation r; + if (argc<=0) return JS_FALSE; + + if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFVec3fClass, NULL)) + return JS_FALSE; + + r = *(SFRotation *) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + v = *(SFVec3f *) ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + r.x = v.x; + r.y = v.y; + r.z = v.z; + return JS_TRUE; +} +static JSBool rot_slerp(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + SFRotation v1, v2, res; + SFVec4f q1, q2; + JSObject *pNew; + jsdouble d; + if (argc<=1) return JS_FALSE; + + if (!JSVAL_IS_DOUBLE(argv[1]) || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFRotationClass, NULL)) return JS_FALSE; + + v1 = *(SFRotation *) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + v2 = *(SFRotation *) ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; + JS_ValueToNumber(c, argv[1], &d ); + q1 = gf_quat_from_rotation(v1); + q2 = gf_quat_from_rotation(v2); + q1 = gf_quat_slerp(q1, q2, FLT2FIX( d)); + res = gf_quat_to_rotation(&q1); + pNew = JS_NewObject(c, &js_rt->SFRotationClass, 0, JS_GetParent(c, obj)); + SFRotation_Create(c, pNew, res.x, res.y, res.z, res.q); + *rval = OBJECT_TO_JSVAL(pNew); + return JS_TRUE; +} + +/* SFColor class functions */ +static GFINLINE GF_JSField *SFColor_Create(JSContext *c, JSObject *obj, Fixed r, Fixed g, Fixed b) +{ + GF_JSField *field; + SFColor *v; + field = NewJSField(); + v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFCOLOR); + field->field_ptr = field->field.far_ptr = v; + field->field.fieldType = GF_SG_VRML_SFCOLOR; + v->red = r; + v->green = g; + v->blue = b; + JS_SetPrivate(c, obj, field); + return field; +} +static JSBool SFColorConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rv) +{ + jsdouble r = 0.0, g = 0.0, b = 0.0; + if (argc > 0) JS_ValueToNumber(c, argv[0], &r); + if (argc > 1) JS_ValueToNumber(c, argv[1], &g); + if (argc > 2) JS_ValueToNumber(c, argv[2], &b); + SFColor_Create(c, obj, FLT2FIX( r), FLT2FIX( g), FLT2FIX( b)); + return JS_TRUE; +} +static JSBool color_getProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + GF_JSField *val = (GF_JSField *) JS_GetPrivate(c, obj); + if (JSVAL_IS_INT(id)) { + switch (JSVAL_TO_INT(id)) { + case 0: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFColor*)val->field.far_ptr)->red)) ); break; + case 1: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFColor*)val->field.far_ptr)->green)) ); break; + case 2: *vp = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT( ((SFColor*)val->field.far_ptr)->blue)) ); break; + default: return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool color_setProperty(JSContext *c, JSObject *obj, jsval id, jsval *vp) +{ + jsdouble d; + Fixed v; + Bool changed = 0; + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + /*this is the prototype*/ + if (!ptr) { + if (! JSVAL_IS_STRING(id)) return JS_FALSE; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] setting new prop/func %s for object prototype\n", JS_GetStringBytes(JSVAL_TO_STRING(id)) )); + return JS_TRUE; + } + + if (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0 && JSVAL_TO_INT(id) < 3 && JS_ValueToNumber(c, *vp, &d)) { + switch (JSVAL_TO_INT(id)) { + case 0: + v = FLT2FIX(d); + changed = ! ( ((SFColor*)ptr->field.far_ptr)->red == v); + ((SFColor*)ptr->field.far_ptr)->red = v; + break; + case 1: + v = FLT2FIX(d); + changed = ! ( ((SFColor*)ptr->field.far_ptr)->green == v); + ((SFColor*)ptr->field.far_ptr)->green = v; + break; + case 2: + v = FLT2FIX(d); + changed = ! ( ((SFColor*)ptr->field.far_ptr)->blue == v); + ((SFColor*)ptr->field.far_ptr)->blue = v; + break; + default: + return JS_TRUE; + } + if (changed) Script_FieldChanged(c, NULL, ptr, NULL); + return JS_TRUE; + } + return JS_FALSE; +} +static JSBool color_setHSV(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rv) +{ + SFColor *v1, hsv; + jsdouble h, s, v; + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + if (argc != 3) return JS_FALSE; + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + JS_ValueToNumber( c, argv[0], &h); + JS_ValueToNumber( c, argv[1], &s); + JS_ValueToNumber( c, argv[2], &v); + hsv.red = FLT2FIX( h); + hsv.green = FLT2FIX( s); + hsv.blue = FLT2FIX( v); + SFColor_fromHSV(&hsv); + gf_sg_vrml_field_copy(v1, &hsv, GF_SG_VRML_SFCOLOR); + Script_FieldChanged(c, NULL, ptr, NULL); + return JS_TRUE; +} + +static JSBool color_getHSV(JSContext *c, JSObject *obj, uintN n, jsval *va, jsval *rval) +{ + SFColor *v1, hsv; + jsval vec[3]; + JSObject *arr; + + v1 = ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + hsv = *v1; + SFColor_toHSV(&hsv); + vec[0] = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT(hsv.red))); + vec[1] = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT(hsv.green))); + vec[2] = DOUBLE_TO_JSVAL(JS_NewDouble(c, FIX2FLT(hsv.blue))); + arr = JS_NewArrayObject(c, 3, vec); + *rval = OBJECT_TO_JSVAL(arr); + return JS_TRUE; +} + + +static JSBool MFArrayConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, u32 fieldType) +{ + GF_ScriptPriv *priv = JS_GetScriptStack(c); + GF_JSField *ptr = NewJSField(); + ptr->field.fieldType = fieldType; + ptr->js_list = JS_NewArrayObject(c, (jsint) argc, argv); + JS_AddRoot(c, &ptr->js_list); + gf_list_add(priv->js_cache, obj); + + JS_SetPrivate(c, obj, ptr); + *rval = OBJECT_TO_JSVAL(obj); + return obj == 0 ? JS_FALSE : JS_TRUE; +} + +static JSBool MFBoolConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return MFArrayConstructor(c, obj, argc, argv, rval, GF_SG_VRML_MFBOOL); +} +static JSBool MFInt32Constructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return MFArrayConstructor(c, obj, argc, argv, rval, GF_SG_VRML_MFINT32); +} +static JSBool MFFloatConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return MFArrayConstructor(c, obj, argc, argv, rval, GF_SG_VRML_MFFLOAT); +} +static JSBool MFTimeConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return MFArrayConstructor(c, obj, argc, argv, rval, GF_SG_VRML_MFTIME); +} +static JSBool MFStringConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return MFArrayConstructor(c, obj, argc, argv, rval, GF_SG_VRML_MFSTRING); +} +static JSBool MFURLConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return MFArrayConstructor(c, obj, argc, argv, rval, GF_SG_VRML_MFURL); +} +static JSBool MFNodeConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return MFArrayConstructor(c, obj, argc, argv, rval, GF_SG_VRML_MFNODE); +} + + +static void array_finalize_ex(JSContext *c, JSObject *obj, Bool is_js_call) +{ + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + if (is_js_call) + JS_ObjectDestroyed(c, obj); + if (!ptr) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] unregistering MFField %s\n", ptr->field.name)); + + if (ptr->js_list) JS_RemoveRoot(c, &ptr->js_list); + + /*MFNode*/ + if (ptr->temp_list) { + gf_node_unregister_children(ptr->owner, ptr->temp_list); + } + if (ptr->field_ptr) { + gf_sg_vrml_field_pointer_del(ptr->field_ptr, ptr->field.fieldType); + } + free(ptr); +} +static void array_finalize(JSContext *c, JSObject *obj) +{ + array_finalize_ex(c, obj, 1); +} + +JSBool array_getElement(JSContext *c, JSObject *obj, jsval id, jsval *rval) +{ + u32 i; + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + if (JSVAL_IS_INT(id)) { + i = JSVAL_TO_INT(id); + JS_GetElement(c, ptr->js_list, (jsint) i, rval); + } + return JS_TRUE; +} + + +//this could be overloaded for each MF type... +JSBool array_setElement(JSContext *c, JSObject *obj, jsval id, jsval *rval) +{ + u32 ind, len; + jsdouble d; + GF_JSField *from; + JSBool ret; + JSClass *the_sf_class = NULL; + JSString *str; + char *str_val; + void *sf_slot; + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + ind = JSVAL_TO_INT(id); + + ret = JS_GetArrayLength(c, ptr->js_list, &len); + if (ret==JS_FALSE) return JS_FALSE; + + if (gf_sg_vrml_is_sf_field(ptr->field.fieldType)) return JS_FALSE; + + switch (ptr->field.fieldType) { + case GF_SG_VRML_MFVEC2F: the_sf_class = &js_rt->SFVec2fClass; break; + case GF_SG_VRML_MFVEC3F: the_sf_class = &js_rt->SFVec3fClass; break; + case GF_SG_VRML_MFCOLOR: the_sf_class = &js_rt->SFColorClass; break; + case GF_SG_VRML_MFROTATION: the_sf_class = &js_rt->SFRotationClass; break; + } + /*dynamic expend*/ + if (ind>=len) { + ret = JS_SetArrayLength(c, ptr->js_list, len+1); + if (ret==JS_FALSE) return JS_FALSE; + while (lenfield.fieldType) { + case GF_SG_VRML_MFBOOL: + a_val = BOOLEAN_TO_JSVAL(0); + break; + case GF_SG_VRML_MFINT32: + a_val = INT_TO_JSVAL(0); + break; + case GF_SG_VRML_MFFLOAT: + case GF_SG_VRML_MFTIME: + a_val = DOUBLE_TO_JSVAL( JS_NewDouble(c, 0) ); + break; + case GF_SG_VRML_MFSTRING: + case GF_SG_VRML_MFURL: + a_val = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "") ); + break; + case GF_SG_VRML_MFVEC2F: + case GF_SG_VRML_MFVEC3F: + case GF_SG_VRML_MFCOLOR: + case GF_SG_VRML_MFROTATION: + a_val = OBJECT_TO_JSVAL( JS_ConstructObject(c, the_sf_class, 0, obj) ); + break; + default: + a_val = INT_TO_JSVAL(0); + break; + } + + if (ptr->field.fieldType!=GF_SG_VRML_MFNODE) + gf_sg_vrml_mf_insert(ptr->field.far_ptr, ptr->field.fieldType, &sf_slot, len); + + JS_SetElement(c, ptr->js_list, len, &a_val); + len++; + } + if (ptr->field.fieldType!=GF_SG_VRML_MFNODE) + gf_sg_vrml_mf_insert(ptr->field.far_ptr, ptr->field.fieldType, &sf_slot, ind); + } + + /*assign object*/ + if (ptr->field.fieldType==GF_SG_VRML_MFNODE) { + if (JSVAL_IS_VOID(*rval) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(*rval), &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + } else if (the_sf_class) { + if (JSVAL_IS_VOID(*rval)) return JS_FALSE; + if (!JS_InstanceOf(c, JSVAL_TO_OBJECT(*rval), the_sf_class, NULL) ) return JS_FALSE; + } else if (ptr->field.fieldType==GF_SG_VRML_MFBOOL) { + if (!JSVAL_IS_BOOLEAN(*rval)) return JS_FALSE; + } else if (ptr->field.fieldType==GF_SG_VRML_MFINT32) { + if (!JSVAL_IS_INT(*rval)) return JS_FALSE; + } else if (ptr->field.fieldType==GF_SG_VRML_MFFLOAT) { + if (!JSVAL_IS_NUMBER(*rval)) return JS_FALSE; + } else if (ptr->field.fieldType==GF_SG_VRML_MFTIME) { + if (!JSVAL_IS_NUMBER(*rval)) return JS_FALSE; + } else if (ptr->field.fieldType==GF_SG_VRML_MFSTRING) { + if (!JSVAL_IS_STRING(*rval)) return JS_FALSE; + } else if (ptr->field.fieldType==GF_SG_VRML_MFURL) { + if (!JSVAL_IS_STRING(*rval)) return JS_FALSE; + } + ret = JS_SetElement(c, ptr->js_list, ind, rval); + if (ret==JS_FALSE) return JS_FALSE; + + if (!ptr->owner) return JS_TRUE; + + /*rewrite MFNode entry*/ + if (ptr->field.fieldType==GF_SG_VRML_MFNODE) { + GF_Node *prev_n, *new_n; + /*get and delete previous node if any, but unregister later*/ + prev_n = gf_node_list_del_child_idx( (GF_ChildNodeItem **)ptr->field.far_ptr, ind); + + /*get new node*/ + from = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(*rval)); + new_n = *(GF_Node**)from->field.far_ptr; + if (new_n) { + gf_node_list_insert_child( (GF_ChildNodeItem **)ptr->field.far_ptr , new_n, ind); + vrml_node_register(new_n, ptr->owner); + } + /*unregister previous node*/ + if (prev_n) gf_node_unregister(prev_n, ptr->owner); + + Script_FieldChanged(c, NULL, ptr, NULL); + return JS_TRUE; + } + + /*rewrite MF slot*/ + switch (ptr->field.fieldType) { + case GF_SG_VRML_MFBOOL: + ((MFBool *)ptr->field.far_ptr)->vals[ind] = (Bool) JSVAL_TO_BOOLEAN(*rval); + break; + case GF_SG_VRML_MFINT32: + ((MFInt32 *)ptr->field.far_ptr)->vals[ind] = (s32) JSVAL_TO_INT(*rval); + break; + case GF_SG_VRML_MFFLOAT: + JS_ValueToNumber(c, *rval, &d); + ((MFFloat *)ptr->field.far_ptr)->vals[ind] = FLT2FIX(d); + break; + case GF_SG_VRML_MFTIME: + JS_ValueToNumber(c, *rval, &d); + ((MFTime *)ptr->field.far_ptr)->vals[ind] = d; + break; + case GF_SG_VRML_MFSTRING: + if (((MFString *)ptr->field.far_ptr)->vals[ind]) { + free(((MFString *)ptr->field.far_ptr)->vals[ind]); + ((MFString *)ptr->field.far_ptr)->vals[ind] = NULL; + } + str = JSVAL_IS_STRING(*rval) ? JSVAL_TO_STRING(*rval) : JS_ValueToString(c, *rval); + str_val = JS_GetStringBytes(str); + ((MFString *)ptr->field.far_ptr)->vals[ind] = strdup(str_val); + break; + + case GF_SG_VRML_MFURL: + if (((MFURL *)ptr->field.far_ptr)->vals[ind].url) { + free(((MFURL *)ptr->field.far_ptr)->vals[ind].url); + ((MFURL *)ptr->field.far_ptr)->vals[ind].url = NULL; + } + str = JSVAL_IS_STRING(*rval) ? JSVAL_TO_STRING(*rval) : JS_ValueToString(c, *rval); + str_val = JS_GetStringBytes(str); + ((MFURL *)ptr->field.far_ptr)->vals[ind].url = strdup(str_val); + ((MFURL *)ptr->field.far_ptr)->vals[ind].OD_ID = 0; + break; + + case GF_SG_VRML_MFVEC2F: + case GF_SG_VRML_MFVEC3F: + case GF_SG_VRML_MFROTATION: + case GF_SG_VRML_MFCOLOR: + from = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(*rval)); + gf_sg_vrml_field_copy(& ((GenMFField *)ptr->field.far_ptr)->array[ind], from->field.far_ptr, from->field.fieldType); + break; + } + + Script_FieldChanged(c, NULL, ptr, NULL); + return JS_TRUE; +} + +JSBool array_setLength(JSContext *c, JSObject *obj, jsval v, jsval *val) +{ + u32 len, i, sftype; + JSBool ret; + JSClass *the_sf_class; + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + if (!JSVAL_IS_INT(*val) || JSVAL_TO_INT(*val) < 0) return JS_FALSE; + len = JSVAL_TO_INT(*val); + ret = JS_SetArrayLength(c, ptr->js_list, len); + if (ret==JS_FALSE) return ret; + + if (!len) { + if (ptr->field.fieldType==GF_SG_VRML_MFNODE) { + gf_node_unregister_children(ptr->owner, *(GF_ChildNodeItem**)ptr->field.far_ptr); + *(GF_ChildNodeItem**)ptr->field.far_ptr = NULL; + } else { + gf_sg_vrml_mf_reset(ptr->field.far_ptr, ptr->field.fieldType); + } + return JS_TRUE; + } + +#if 0 + /*insert till index if needed*/ + if (ptr->field.fieldType != GF_SG_VRML_MFNODE) { + if (!ptr->field.far_ptr) ptr->field_ptr = ptr->field.far_ptr = gf_sg_vrml_field_pointer_new(ptr->field.fieldType); + gf_sg_vrml_mf_reset(ptr->field.far_ptr, ptr->field.fieldType); + gf_sg_vrml_mf_alloc(ptr->field.far_ptr, ptr->field.fieldType, len); + if (ptr->field_ptr) ptr->field_ptr = ptr->field.far_ptr; + } +#endif + + the_sf_class = NULL; + switch (ptr->field.fieldType) { + case GF_SG_VRML_MFVEC2F: the_sf_class = &js_rt->SFVec2fClass; break; + case GF_SG_VRML_MFVEC3F: the_sf_class = &js_rt->SFVec3fClass; break; + case GF_SG_VRML_MFCOLOR: the_sf_class = &js_rt->SFColorClass; break; + case GF_SG_VRML_MFROTATION: the_sf_class = &js_rt->SFRotationClass; break; + case GF_SG_VRML_MFNODE: the_sf_class = &js_rt->SFNodeClass; break; + } + sftype = gf_sg_vrml_get_sf_type(ptr->field.fieldType); + for (i=0; ijs_list, i, &a_val); + } + return JS_TRUE; +} + +JSBool array_getLength(JSContext *c, JSObject *obj, jsval v, jsval *val) +{ + jsuint len; + GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); + JSBool ret = JS_GetArrayLength(c, ptr->js_list, &len); + *val = INT_TO_JSVAL(len); + return ret; +} + + +/* MFVec2f class constructor */ +static JSBool MFVec2fConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval val; + JSObject *item; + u32 i; + GF_JSField *ptr = NewJSField(); + ptr->js_list = JS_NewArrayObject(c, 0, 0); + JS_SetArrayLength(c, ptr->js_list, argc); + JS_SetPrivate(c, obj, ptr); + + for (i=0; iSFVec2fClass, NULL) ) { + item = JS_ConstructObject(c, &js_rt->SFVec2fClass, 0, obj); + val = OBJECT_TO_JSVAL(item); + JS_SetElement(c, ptr->js_list, i, &val); + } else { + JS_SetElement(c, ptr->js_list, i, &argv[i]); + } + } + *rval = OBJECT_TO_JSVAL(obj); + return obj == 0 ? JS_FALSE : JS_TRUE; +} + +/* MFVec3f class constructor */ +static JSBool MFVec3fConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval val; + JSObject *item; + u32 i; + GF_JSField *ptr = NewJSField(); + ptr->field.fieldType = GF_SG_VRML_MFVEC3F; + ptr->js_list = JS_NewArrayObject(c, (jsint) argc, argv); + JS_SetArrayLength(c, ptr->js_list, argc); + JS_SetPrivate(c, obj, ptr); + + for (i=0; iSFVec3fClass, NULL) ) { + item = JS_ConstructObject(c, &js_rt->SFVec3fClass, 0, obj); + val = OBJECT_TO_JSVAL(item); + JS_SetElement(c, ptr->js_list, i, &val); + } else { + JS_SetElement(c, ptr->js_list, i, &argv[i]); + } + } + *rval = OBJECT_TO_JSVAL(obj); + return obj == 0 ? JS_FALSE : JS_TRUE; +} + +/* MFRotation class constructor */ +static JSBool MFRotationConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval val; + JSObject *item; + u32 i; + GF_JSField *ptr = NewJSField(); + ptr->field.fieldType = GF_SG_VRML_MFROTATION; + ptr->js_list = JS_NewArrayObject(c, 0, 0); + JS_SetArrayLength(c, ptr->js_list, argc); + JS_SetPrivate(c, obj, ptr); + + for (i=0; iSFRotationClass, NULL) ) { + item = JS_ConstructObject(c, &js_rt->SFVec3fClass, 0, obj); + val = OBJECT_TO_JSVAL(item); + JS_SetElement(c, ptr->js_list, i, &val); + } else { + JS_SetElement(c, ptr->js_list, i, &argv[i]); + } + } + *rval = OBJECT_TO_JSVAL(obj); + return obj == 0 ? JS_FALSE : JS_TRUE; +} + +/*MFColor class constructor */ +static JSBool MFColorConstructor(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval val; + JSObject *item; + u32 i; + GF_JSField *ptr = NewJSField(); + ptr->field.fieldType = GF_SG_VRML_MFCOLOR; + ptr->js_list = JS_NewArrayObject(c, 0, 0); + JS_SetArrayLength(c, ptr->js_list, argc); + JS_SetPrivate(c, obj, ptr); + + for (i=0; iSFColorClass, NULL) ) { + item = JS_ConstructObject(c, &js_rt->SFColorClass, 0, obj); + val = OBJECT_TO_JSVAL(item); + JS_SetElement(c, ptr->js_list, i, &val); + } else { + JS_SetElement(c, ptr->js_list, i, &argv[i]); + } + } + *rval = OBJECT_TO_JSVAL(obj); + return obj == 0 ? JS_FALSE : JS_TRUE; +} + +#ifndef GPAC_DISABLE_SVG +JSBool dom_event_add_listener_ex(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, GF_Node *vrml_node); +JSBool dom_event_remove_listener_ex(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, GF_Node *vrml_node); + +JSBool vrml_event_add_listener(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_Node *node; + GF_JSField *ptr; + if (! JS_InstanceOf(c, obj, &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + ptr = (GF_JSField *) JS_GetPrivate(c, obj); + assert(ptr->field.fieldType==GF_SG_VRML_SFNODE); + node = * ((GF_Node **)ptr->field.far_ptr); + + return dom_event_add_listener_ex(c, obj, argc, argv, rval, node); +} +JSBool vrml_event_remove_listener(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GF_Node *node; + GF_JSField *ptr; + if (! JS_InstanceOf(c, obj, &js_rt->SFNodeClass, NULL) ) return JS_FALSE; + ptr = (GF_JSField *) JS_GetPrivate(c, obj); + assert(ptr->field.fieldType==GF_SG_VRML_SFNODE); + node = * ((GF_Node **)ptr->field.far_ptr); + + return dom_event_remove_listener_ex(c, obj, argc, argv, rval, node); +} +#endif + +static JSBool vrml_dom3_not_implemented(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return JS_FALSE; +} + + +void gf_sg_script_init_sm_api(GF_ScriptPriv *sc, GF_Node *script) +{ + /*GCC port: classes are declared within code since JS_PropertyStub and co are exported symbols + from JS runtime lib, so with non-constant addresses*/ + JS_SETUP_CLASS(js_rt->globalClass, "global", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_FinalizeStub); + + JS_SETUP_CLASS(js_rt->browserClass , "Browser", 0, + JS_PropertyStub, JS_PropertyStub, JS_FinalizeStub); + + JS_SETUP_CLASS(js_rt->SFNodeClass, "SFNode", JSCLASS_HAS_PRIVATE, + node_getProperty, node_setProperty, node_finalize); + + JS_SETUP_CLASS(js_rt->SFVec2fClass , "SFVec2f", JSCLASS_HAS_PRIVATE, + vec2f_getProperty, vec2f_setProperty, field_finalize); + + JS_SETUP_CLASS(js_rt->SFVec3fClass , "SFVec3f", JSCLASS_HAS_PRIVATE, + vec3f_getProperty, vec3f_setProperty, field_finalize); + + JS_SETUP_CLASS(js_rt->SFRotationClass , "SFRotation", JSCLASS_HAS_PRIVATE, + rot_getProperty, rot_setProperty, field_finalize); + + JS_SETUP_CLASS(js_rt->SFColorClass , "SFColor", JSCLASS_HAS_PRIVATE, + color_getProperty, color_setProperty, field_finalize); + + JS_SETUP_CLASS(js_rt->SFImageClass , "SFImage", JSCLASS_HAS_PRIVATE, + image_getProperty, image_setProperty, field_finalize); + + JS_SETUP_CLASS(js_rt->MFInt32Class , "MFInt32", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SETUP_CLASS(js_rt->MFBoolClass , "MFBool", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SETUP_CLASS(js_rt->MFTimeClass , "MFTime", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SETUP_CLASS(js_rt->MFFloatClass , "MFFloat", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SETUP_CLASS(js_rt->MFUrlClass , "MFUrl", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SETUP_CLASS(js_rt->MFStringClass , "MFString", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SETUP_CLASS(js_rt->MFNodeClass , "MFNode", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SETUP_CLASS(js_rt->MFVec2fClass , "MFVec2f", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SETUP_CLASS(js_rt->MFVec3fClass , "MFVec3f", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SETUP_CLASS(js_rt->MFRotationClass , "MFRotation", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SETUP_CLASS(js_rt->MFColorClass , "MFColor", JSCLASS_HAS_PRIVATE, + array_getElement, array_setElement, array_finalize); + + JS_SetErrorReporter(sc->js_ctx, script_error); + + sc->js_obj = JS_NewObject(sc->js_ctx, &js_rt->globalClass, 0, 0 ); + JS_InitStandardClasses(sc->js_ctx, sc->js_obj); + { + JSFunctionSpec globalFunctions[] = { + {"print", JSPrint, 0}, + { 0 } + }; + JS_DefineFunctions(sc->js_ctx, sc->js_obj, globalFunctions ); + } + /*remember pointer to scene graph!!*/ + JS_SetPrivate(sc->js_ctx, sc->js_obj, script->sgprivate->scenegraph); + + + JS_DefineProperty(sc->js_ctx, sc->js_obj, "FALSE", BOOLEAN_TO_JSVAL(0), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT ); + JS_DefineProperty(sc->js_ctx, sc->js_obj, "TRUE", BOOLEAN_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT ); + JS_DefineProperty(sc->js_ctx, sc->js_obj, "_this", PRIVATE_TO_JSVAL(script), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT ); + + sc->js_browser = JS_DefineObject(sc->js_ctx, sc->js_obj, "Browser", &js_rt->browserClass, 0, 0 ); + JS_DefineProperty(sc->js_ctx, sc->js_browser, "_this", PRIVATE_TO_JSVAL(script), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT ); + { + JSFunctionSpec browserFunctions[] = { + {"getName", getName, 0}, + {"getVersion", getVersion, 0}, + {"getCurrentSpeed", getCurrentSpeed, 0}, + {"getCurrentFrameRate", getCurrentFrameRate, 0}, + {"getWorldURL", getWorldURL, 0}, + {"replaceWorld", replaceWorld, 0}, + {"addRoute", addRoute, 0}, + {"deleteRoute", deleteRoute, 0}, + {"loadURL", loadURL, 0}, + {"createVrmlFromString", createVrmlFromString, 0}, + {"setDescription", setDescription, 0}, + {"print", JSPrint, 0}, + {"getOption", getOption, 0}, + {"setOption", setOption, 0}, + {"getScript", getScript, 0}, + {"getProto", getProto, 0}, + {"getElementById", getElementById, 0}, + {0} + }; + JS_DefineFunctions(sc->js_ctx, sc->js_browser, browserFunctions); + } + + { + JSFunctionSpec SFNodeMethods[] = { + {"toString", node_toString, 0}, +#ifndef GPAC_DISABLE_SVG + {"addEventListenerNS", vrml_event_add_listener, 4}, + {"removeEventListenerNS", vrml_event_remove_listener, 4}, + {"addEventListener", vrml_event_add_listener, 3}, + {"removeEventListener", vrml_event_remove_listener, 3}, + {"dispatchEvent", vrml_dom3_not_implemented, 1}, +#endif + {0} + }; + JSPropertySpec SFNodeProps[] = { + {"__dummy", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {0} + }; + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->SFNodeClass, SFNodeConstructor, 1, SFNodeProps, SFNodeMethods, 0, 0); + } + { + JSPropertySpec SFVec2fProps[] = { + {"x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {0} + }; + JSFunctionSpec SFVec2fMethods[] = { + {"add", vec2f_add, 1}, + {"divide", vec2f_divide, 1}, + {"dot", vec2f_dot, 1}, + {"length", vec2f_length, 0}, + {"multiply", vec2f_multiply, 1}, + {"normalize", vec2f_normalize,0}, + {"subtract", vec2f_subtract, 1}, + {"negate", vec2f_negate, 0}, + {"toString", field_toString, 0}, + {0} + }; + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->SFVec2fClass, SFVec2fConstructor, 0, SFVec2fProps, SFVec2fMethods, 0, 0); + } + { + JSPropertySpec SFVec3fProps[] = { + {"x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"z", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {0} + }; + JSFunctionSpec SFVec3fMethods[] = { + {"add", vec3f_add, 1}, + {"divide", vec3f_divide, 1}, + {"dot", vec3f_dot, 1}, + {"length", vec3f_length, 0}, + {"multiply", vec3f_multiply, 1}, + {"normalize", vec3f_normalize,0}, + {"subtract", vec3f_subtract, 1}, + {"cross", vec3f_cross, 1}, + {"negate", vec3f_negate, 0}, + {"toString", field_toString, 0}, + {0} + }; + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->SFVec3fClass, SFVec3fConstructor, 0, SFVec3fProps, SFVec3fMethods, 0, 0); + } + { + JSPropertySpec SFRotationProps[] = { + {"xAxis", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"yAxis", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"zAxis", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"angle", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {0} + }; + JSFunctionSpec SFRotationMethods[] = { + {"getAxis", rot_getAxis, 1}, + {"inverse", rot_inverse, 1}, + {"multiply", rot_multiply, 1}, + {"multVec", rot_multVec, 0}, + {"setAxis", rot_setAxis, 1}, + {"slerp", rot_slerp,0}, + {"toString", field_toString, 0}, + {0} + }; + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->SFRotationClass, SFRotationConstructor, 0, SFRotationProps, SFRotationMethods, 0, 0); + } + { + JSPropertySpec SFColorProps[] = { + {"r", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"g", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"b", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {0} + }; + JSFunctionSpec SFColorMethods[] = { + {"setHSV", color_setHSV, 3, 0, 0}, + {"getHSV", color_getHSV, 0, 0, 0}, + {"toString", field_toString, 0, 0, 0}, + {0, 0, 0, 0, 0} + }; + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->SFColorClass, SFColorConstructor, 0, SFColorProps, SFColorMethods, 0, 0); + } + { + JSPropertySpec SFImageProps[] = { + {"x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"comp", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {"array", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT}, + {0} + }; + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->SFImageClass, SFImageConstructor, 0, SFImageProps, 0, 0, 0); + } + + { + JSPropertySpec MFArrayProp[] = { + { "length", 0, JSPROP_PERMANENT, array_getLength, array_setLength }, + { "assign", 0, JSPROP_PERMANENT, array_getElement, array_setElement}, + { 0, 0, 0, 0, 0 } + }; + JSFunctionSpec MFArrayMethods[] = { + {"toString", field_toString, 0}, + {0} + }; + + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFInt32Class, MFInt32Constructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFBoolClass, MFBoolConstructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFFloatClass, MFFloatConstructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFTimeClass, MFTimeConstructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFStringClass, MFStringConstructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFUrlClass, MFURLConstructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFVec2fClass, MFVec2fConstructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFVec3fClass, MFVec3fConstructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFRotationClass, MFRotationConstructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFColorClass, MFColorConstructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->MFNodeClass, MFNodeConstructor, 0, MFArrayProp, MFArrayMethods, 0, 0); + } + +/* + cant get any doc specifying if these are supposed to be supported in MPEG4Script... + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &SFVec4fClass, SFVec4fConstructor, 0, SFVec4fProps, SFVec4fMethods, 0, 0); + JS_InitClass(sc->js_ctx, sc->js_obj, 0, &MFVec4fClass, MFVec4fCons, 0, MFArrayProp, 0, 0, 0); +*/ + +} + + + +void gf_sg_script_to_node_field(JSContext *c, jsval val, GF_FieldInfo *field, GF_Node *owner, GF_JSField *parent) +{ + jsdouble d; + Bool changed; + JSObject *obj; + GF_JSField *p, *from; + jsuint len; + jsval item; + u32 i; + + if (JSVAL_IS_VOID(val)) return; + if ((field->fieldType != GF_SG_VRML_SFNODE) && JSVAL_IS_NULL(val)) return; + + + switch (field->fieldType) { + case GF_SG_VRML_SFBOOL: + { + if (JSVAL_IS_BOOLEAN(val)) { + *((SFBool *) field->far_ptr) = JSVAL_TO_BOOLEAN(val); + Script_FieldChanged(c, owner, parent, field); + } + return; + } + case GF_SG_VRML_SFINT32: + { + if (JSVAL_IS_INT(val) ) { + * ((SFInt32 *) field->far_ptr) = JSVAL_TO_INT(val); + Script_FieldChanged(c, owner, parent, field); + } else if (JSVAL_IS_NUMBER(val) ) { + JS_ValueToNumber(c, val, &d ); + *((SFInt32 *) field->far_ptr) = (s32) d; + Script_FieldChanged(c, owner, parent, field); + } + return; + } + case GF_SG_VRML_SFFLOAT: + { + if (JSVAL_IS_NUMBER(val) ) { + JS_ValueToNumber(c, val, &d ); + *((SFFloat *) field->far_ptr) = FLT2FIX( d); + Script_FieldChanged(c, owner, parent, field); + } + return; + } + case GF_SG_VRML_SFTIME: + { + if (JSVAL_IS_NUMBER(val) ) { + JS_ValueToNumber(c, val, &d ); + *((SFTime *) field->far_ptr) = (Double) d; + Script_FieldChanged(c, owner, parent, field); + } + return; + } + case GF_SG_VRML_SFSTRING: + { + SFString *s = (SFString*)field->far_ptr; + JSString *str = JSVAL_IS_STRING(val) ? JSVAL_TO_STRING(val) : JS_ValueToString(c, val); + char *str_val = JS_GetStringBytes(str); + /*we do filter strings since rebuilding a text is quite slow, so let's avoid killing the compositors*/ + if (!s->buffer || strcmp(str_val, s->buffer)) { + if ( s->buffer) free(s->buffer); + s->buffer = strdup(str_val); + Script_FieldChanged(c, owner, parent, field); + } + return; + } + case GF_SG_VRML_SFURL: + { + JSString *str = JSVAL_IS_STRING(val) ? JSVAL_TO_STRING(val) : JS_ValueToString(c, val); + if (((SFURL*)field->far_ptr)->url) free(((SFURL*)field->far_ptr)->url); + ((SFURL*)field->far_ptr)->url = strdup(JS_GetStringBytes(str)); + ((SFURL*)field->far_ptr)->OD_ID = 0; + Script_FieldChanged(c, owner, parent, field); + return; + } + case GF_SG_VRML_MFSTRING: + if (JSVAL_IS_STRING(val)) { + JSString *str = JSVAL_TO_STRING(val); + gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType); + gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, 1); + ((MFString*)field->far_ptr)->vals[0] = strdup(JS_GetStringBytes(str)); + Script_FieldChanged(c, owner, parent, field); + return; + } + case GF_SG_VRML_MFURL: + if (JSVAL_IS_STRING(val)) { + JSString *str = JSVAL_TO_STRING(val); + gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType); + gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, 1); + ((MFURL*)field->far_ptr)->vals[0].url = strdup(JS_GetStringBytes(str)); + ((MFURL*)field->far_ptr)->vals[0].OD_ID = 0; + Script_FieldChanged(c, owner, parent, field); + return; + } + + default: + break; + } + + //from here we must have an object + if (! JSVAL_IS_OBJECT(val)) return; + obj = JSVAL_TO_OBJECT(val) ; + + switch (field->fieldType) { + case GF_SG_VRML_SFVEC2F: + { + if (JS_InstanceOf(c, obj, &js_rt->SFVec2fClass, NULL) ) { + p = (GF_JSField *) JS_GetPrivate(c, obj); + gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFVEC2F); + Script_FieldChanged(c, owner, parent, field); + } + return; + } + case GF_SG_VRML_SFVEC3F: + { + if (JS_InstanceOf(c, obj, &js_rt->SFVec3fClass, NULL) ) { + p = (GF_JSField *) JS_GetPrivate(c, obj); + gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFVEC3F); + Script_FieldChanged(c, owner, parent, field); + } + return; + } + case GF_SG_VRML_SFROTATION: + { + if ( JS_InstanceOf(c, obj, &js_rt->SFRotationClass, NULL) ) { + p = (GF_JSField *) JS_GetPrivate(c, obj); + gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFROTATION); + Script_FieldChanged(c, owner, parent, field); + } + return; + } + case GF_SG_VRML_SFCOLOR: + { + if (JS_InstanceOf(c, obj, &js_rt->SFColorClass, NULL) ) { + p = (GF_JSField *) JS_GetPrivate(c, obj); + gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFCOLOR); + Script_FieldChanged(c, owner, parent, field); + } + return; + } + case GF_SG_VRML_SFNODE: + { + /*replace object*/ + if (*((GF_Node**)field->far_ptr)) + gf_node_unregister(*((GF_Node**)field->far_ptr), owner); + + if (JSVAL_IS_NULL(val)) { + field->far_ptr = NULL; + Script_FieldChanged(c, owner, parent, field); + } else if (JS_InstanceOf(c, obj, &js_rt->SFNodeClass, NULL) ) { + GF_Node *n = * (GF_Node**) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + * ((GF_Node **)field->far_ptr) = n; + vrml_node_register(n, owner); + Script_FieldChanged(c, owner, parent, field); + } + return; + } + case GF_SG_VRML_SFIMAGE: + { + if ( JS_InstanceOf(c, obj, &js_rt->SFImageClass, NULL) ) { + p = (GF_JSField *) JS_GetPrivate(c, obj); + gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFIMAGE); + Script_FieldChanged(c, owner, parent, field); + } + return; + } + default: + break; + } + + //from here we handle only MF fields + if ( !JS_InstanceOf(c, obj, &js_rt->MFBoolClass, NULL) + && !JS_InstanceOf(c, obj, &js_rt->MFInt32Class, NULL) + && !JS_InstanceOf(c, obj, &js_rt->MFFloatClass, NULL) + && !JS_InstanceOf(c, obj, &js_rt->MFTimeClass, NULL) + && !JS_InstanceOf(c, obj, &js_rt->MFStringClass, NULL) + && !JS_InstanceOf(c, obj, &js_rt->MFUrlClass, NULL) + && !JS_InstanceOf(c, obj, &js_rt->MFVec2fClass, NULL) + && !JS_InstanceOf(c, obj, &js_rt->MFVec3fClass, NULL) + && !JS_InstanceOf(c, obj, &js_rt->MFRotationClass, NULL) + && !JS_InstanceOf(c, obj, &js_rt->MFColorClass, NULL) + && !JS_InstanceOf(c, obj, &js_rt->MFNodeClass, NULL) +/* + && !JS_InstanceOf(c, obj, &MFVec4fClass, NULL) +*/ + ) return; + + + p = (GF_JSField *) JS_GetPrivate(c, obj); + JS_GetArrayLength(c, p->js_list, &len); + + /*special handling for MF node, reset list first*/ + if (JS_InstanceOf(c, obj, &js_rt->MFNodeClass, NULL)) { + GF_Node *child; + GF_ChildNodeItem *last = NULL; + gf_node_unregister_children(owner, * (GF_ChildNodeItem **) field->far_ptr); + * (GF_ChildNodeItem **) field->far_ptr = NULL; + + for (i=0; ijs_list, (jsint) i, &item); + if (JSVAL_IS_NULL(item)) break; + if (!JSVAL_IS_OBJECT(item)) break; + node_obj = JSVAL_TO_OBJECT(item); + if ( !JS_InstanceOf(c, node_obj, &js_rt->SFNodeClass, NULL)) break; + from = (GF_JSField *) JS_GetPrivate(c, node_obj); + + child = * ((GF_Node**)from->field.far_ptr); + + gf_node_list_add_child_last( (GF_ChildNodeItem **) field->far_ptr , child, &last); + vrml_node_register(child, owner); + } + Script_FieldChanged(c, owner, parent, field); + return; + } + + /*again, check text changes*/ + changed = (field->fieldType == GF_SG_VRML_MFSTRING) ? 0 : 1; + /*realloc*/ + if (len != ((GenMFField *)field->far_ptr)->count) { + gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType); + gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, len); + changed = 1; + } + /*assign each slot*/ + for (i=0; ijs_list, (jsint) i, &item); + + switch (field->fieldType) { + case GF_SG_VRML_MFBOOL: + if (JSVAL_IS_BOOLEAN(item)) { + ((MFBool*)field->far_ptr)->vals[i] = (Bool) JSVAL_TO_BOOLEAN(item); + } + break; + case GF_SG_VRML_MFINT32: + if (JSVAL_IS_INT(item)) { + ((MFInt32 *)field->far_ptr)->vals[i] = (s32) JSVAL_TO_INT(item); + } + break; + case GF_SG_VRML_MFFLOAT: + if (JSVAL_IS_NUMBER(item)) { + JS_ValueToNumber(c, item, &d); + ((MFFloat *)field->far_ptr)->vals[i] = FLT2FIX( d); + } + break; + case GF_SG_VRML_MFTIME: + if (JSVAL_IS_NUMBER(item)) { + JS_ValueToNumber(c, item, &d); + ((MFTime *)field->far_ptr)->vals[i] = d; + } + break; + case GF_SG_VRML_MFSTRING: + { + MFString *mfs = (MFString *) field->far_ptr; + JSString *str = JSVAL_IS_STRING(item) ? JSVAL_TO_STRING(item) : JS_ValueToString(c, item); + char *str_val = JS_GetStringBytes(str); + if (!mfs->vals[i] || strcmp(str_val, mfs->vals[i]) ) { + if (mfs->vals[i]) free(mfs->vals[i]); + mfs->vals[i] = strdup(str_val); + changed = 1; + } + } + break; + case GF_SG_VRML_MFURL: + { + MFURL *mfu = (MFURL *) field->far_ptr; + JSString *str = JSVAL_IS_STRING(item) ? JSVAL_TO_STRING(item) : JS_ValueToString(c, item); + if (mfu->vals[i].url) free(mfu->vals[i].url); + mfu->vals[i].url = strdup(JS_GetStringBytes(str)); + mfu->vals[i].OD_ID = 0; + } + break; + + case GF_SG_VRML_MFVEC2F: + if ( JSVAL_IS_OBJECT(item) && JS_InstanceOf(c, JSVAL_TO_OBJECT(item), &js_rt->SFVec2fClass, NULL) ) { + from = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(item)); + gf_sg_vrml_field_copy(& ((MFVec2f*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFVEC2F); + } + break; + case GF_SG_VRML_MFVEC3F: + if ( JSVAL_IS_OBJECT(item) && JS_InstanceOf(c, JSVAL_TO_OBJECT(item), &js_rt->SFVec3fClass, NULL) ) { + from = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(item)); + gf_sg_vrml_field_copy(& ((MFVec3f*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFVEC3F); + } + break; + case GF_SG_VRML_MFROTATION: + if ( JSVAL_IS_OBJECT(item) && JS_InstanceOf(c, JSVAL_TO_OBJECT(item), &js_rt->SFRotationClass, NULL) ) { + from = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(item)); + gf_sg_vrml_field_copy(& ((MFRotation*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFROTATION); + } + break; + case GF_SG_VRML_MFCOLOR: + if ( JSVAL_IS_OBJECT(item) && JS_InstanceOf(c, JSVAL_TO_OBJECT(item), &js_rt->SFColorClass, NULL) ) { + SFColor *col; + from = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(item)); + col = (SFColor *)from->field.far_ptr; + gf_sg_vrml_field_copy(& ((MFColor*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFCOLOR); + } + break; + + default: + return; + } + } + if (changed) Script_FieldChanged(c, owner, parent, field); +} + +#define SETUP_FIELD \ + jsf = NewJSField(); \ + jsf->owner = parent; \ + if(parent) gf_node_get_field(parent, field->fieldIndex, &jsf->field); \ + +#define SETUP_MF_FIELD \ + jsf = (GF_JSField *) JS_GetPrivate(priv->js_ctx, obj); \ + jsf->owner = parent; \ + if (parent) gf_node_get_field(parent, field->fieldIndex, &jsf->field); \ + + + +jsval gf_sg_script_to_smjs_field(GF_ScriptPriv *priv, GF_FieldInfo *field, GF_Node *parent, Bool skip_cache, Bool force_evaluate) +{ + u32 i; + JSObject *obj = NULL; + GF_JSField *jsf = NULL; + GF_JSField *slot = NULL; + GF_Node *n; + jsdouble *d; + jsval newVal; + JSString *s; + + /*native types*/ + switch (field->fieldType) { + case GF_SG_VRML_SFBOOL: + return BOOLEAN_TO_JSVAL( * ((SFBool *) field->far_ptr) ); + case GF_SG_VRML_SFINT32: + return INT_TO_JSVAL( * ((SFInt32 *) field->far_ptr)); + case GF_SG_VRML_SFFLOAT: + d = JS_NewDouble(priv->js_ctx, FIX2FLT(* ((SFFloat *) field->far_ptr) )); + return DOUBLE_TO_JSVAL(d); + case GF_SG_VRML_SFTIME: + d = JS_NewDouble(priv->js_ctx, * ((SFTime *) field->far_ptr)); + return DOUBLE_TO_JSVAL(d); + case GF_SG_VRML_SFSTRING: + { + s = JS_NewStringCopyZ(priv->js_ctx, ((SFString *) field->far_ptr)->buffer); + return STRING_TO_JSVAL( s ); + } + case GF_SG_VRML_SFURL: + { + SFURL *url = (SFURL *)field->far_ptr; + if (url->OD_ID > 0) { + char msg[30]; + sprintf(msg, "od:%d", url->OD_ID); + s = JS_NewStringCopyZ(priv->js_ctx, (const char *) msg); + } else { + s = JS_NewStringCopyZ(priv->js_ctx, (const char *) url->url); + } + return STRING_TO_JSVAL( s ); + } + } + + + /*look into object bank in case we already have this object*/ + if (parent && !skip_cache && priv->js_cache) { + i=0; + while ((obj = gf_list_enum(priv->js_cache, &i))) { + jsf = (GF_JSField *) JS_GetPrivate(priv->js_ctx, obj); + if (jsf + && (jsf->owner==parent) + && (jsf->field.fieldIndex == field->fieldIndex) + /*type check needed for MFNode entries*/ + && (jsf->field.fieldType==field->fieldType) + ) { + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] found cached jsobj 0x%08x (field %s) in script bank 0x%08x\n", obj, field->name, priv->js_cache) ); + if (!force_evaluate && !jsf->reevaluate) return OBJECT_TO_JSVAL(obj); + + /*we need to rebuild MF types where SF is a native type.*/ + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] Recomputing cached jsobj\n") ); + switch (jsf->field.fieldType) { + case GF_SG_VRML_MFBOOL: + { + MFBool *f = (MFBool *) field->far_ptr; + JS_SetArrayLength(priv->js_ctx, jsf->js_list, 0); + JS_SetArrayLength(priv->js_ctx, jsf->js_list, f->count); + for (i=0; icount; i++) { + newVal = BOOLEAN_TO_JSVAL(f->vals[i]); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + } + break; + case GF_SG_VRML_MFINT32: + { + MFInt32 *f = (MFInt32 *) field->far_ptr; + JS_SetArrayLength(priv->js_ctx, jsf->js_list, 0); + JS_SetArrayLength(priv->js_ctx, jsf->js_list, f->count); + for (i=0; icount; i++) { + newVal = INT_TO_JSVAL(f->vals[i]); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + } + break; + case GF_SG_VRML_MFFLOAT: + { + MFFloat *f = (MFFloat *) field->far_ptr; + JS_SetArrayLength(priv->js_ctx, jsf->js_list, 0); + JS_SetArrayLength(priv->js_ctx, jsf->js_list, f->count); + for (i=0; icount; i++) { + newVal = DOUBLE_TO_JSVAL(JS_NewDouble(priv->js_ctx, FIX2FLT(f->vals[i]))); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + } + break; + case GF_SG_VRML_MFTIME: + { + MFTime *f = (MFTime *) field->far_ptr; + JS_SetArrayLength(priv->js_ctx, jsf->js_list, 0); + JS_SetArrayLength(priv->js_ctx, jsf->js_list, f->count); + for (i=0; icount; i++) { + newVal = DOUBLE_TO_JSVAL(JS_NewDouble(priv->js_ctx, f->vals[i])); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + } + break; + case GF_SG_VRML_MFSTRING: + { + MFString *f = (MFString *) field->far_ptr; + JS_SetArrayLength(priv->js_ctx, jsf->js_list, 0); + JS_SetArrayLength(priv->js_ctx, jsf->js_list, f->count); + for (i=0; icount; i++) { + s = JS_NewStringCopyZ(priv->js_ctx, f->vals[i]); + newVal = STRING_TO_JSVAL( s ); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + } + break; + case GF_SG_VRML_MFURL: + { + MFURL *f = (MFURL *) field->far_ptr; + JS_SetArrayLength(priv->js_ctx, jsf->js_list, 0); + JS_SetArrayLength(priv->js_ctx, jsf->js_list, f->count); + for (i=0; icount; i++) { + if (f->vals[i].OD_ID > 0) { + char msg[30]; + sprintf(msg, "od:%d", f->vals[i].OD_ID); + s = JS_NewStringCopyZ(priv->js_ctx, (const char *) msg); + } else { + s = JS_NewStringCopyZ(priv->js_ctx, f->vals[i].url); + } + newVal = STRING_TO_JSVAL(s); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + } + break; + case GF_SG_VRML_MFNODE: + { + GF_ChildNodeItem *f = *(GF_ChildNodeItem **) field->far_ptr; + u32 count = 0; + while (f) { + count++; + f = f->next; + } + /*this will destroy the different objects*/ + JS_SetArrayLength(priv->js_ctx, jsf->js_list, 0); + if (JS_SetArrayLength(priv->js_ctx, jsf->js_list, count) != JS_TRUE) return JSVAL_NULL; + + count = 0; + f = *(GF_ChildNodeItem **) field->far_ptr; + while (f) { + JSObject *pf = JS_NewObject(priv->js_ctx, &js_rt->SFNodeClass, 0, obj); + slot = NewJSField(); + slot->owner = parent; + slot->temp_node = f->node; + slot->field.far_ptr = & slot->temp_node; + slot->field.fieldType = GF_SG_VRML_SFNODE; + vrml_node_register(f->node, parent); + JS_SetPrivate(priv->js_ctx, pf, slot); + + newVal = OBJECT_TO_JSVAL(pf); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) count, &newVal); + count++; + + f = f->next; + } + } + break; + } + jsf->reevaluate = 0; + return OBJECT_TO_JSVAL(obj); + } + } + } + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] creating jsobj %s.%s\n", gf_node_get_name(parent), field->name) ); + + switch (field->fieldType) { + case GF_SG_VRML_SFVEC2F: + SETUP_FIELD + obj = JS_NewObject(priv->js_ctx, &js_rt->SFVec2fClass, 0, priv->js_obj); + break; + case GF_SG_VRML_SFVEC3F: + SETUP_FIELD + obj = JS_NewObject(priv->js_ctx, &js_rt->SFVec3fClass, 0, priv->js_obj); + break; + case GF_SG_VRML_SFROTATION: + SETUP_FIELD + obj = JS_NewObject(priv->js_ctx, &js_rt->SFRotationClass, 0, priv->js_obj); + break; + case GF_SG_VRML_SFCOLOR: + SETUP_FIELD + obj = JS_NewObject(priv->js_ctx, &js_rt->SFColorClass, 0, priv->js_obj); + break; + case GF_SG_VRML_SFIMAGE: + SETUP_FIELD + obj = JS_NewObject(priv->js_ctx, &js_rt->SFImageClass, 0, priv->js_obj); + break; + case GF_SG_VRML_SFNODE: + SETUP_FIELD + obj = JS_NewObject(priv->js_ctx, &js_rt->SFNodeClass, 0, priv->js_obj); + break; + + + case GF_SG_VRML_MFBOOL: + { + MFBool *f = (MFBool *) field->far_ptr; + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFBoolClass, 0, priv->js_obj); + SETUP_MF_FIELD + for (i = 0; icount; i++) { + jsval newVal = BOOLEAN_TO_JSVAL(f->vals[i]); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + break; + } + case GF_SG_VRML_MFINT32: + { + MFInt32 *f = (MFInt32 *) field->far_ptr; + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFInt32Class, 0, priv->js_obj); + SETUP_MF_FIELD + for (i=0; icount; i++) { + jsval newVal = INT_TO_JSVAL(f->vals[i]); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + break; + } + case GF_SG_VRML_MFFLOAT: + { + MFFloat *f = (MFFloat *) field->far_ptr; + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFFloatClass, 0, priv->js_obj); + SETUP_MF_FIELD + for (i=0; icount; i++) { + jsval newVal = DOUBLE_TO_JSVAL(JS_NewDouble(priv->js_ctx, FIX2FLT(f->vals[i]))); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + break; + } + case GF_SG_VRML_MFTIME: + { + MFTime *f = (MFTime *) field->far_ptr; + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFTimeClass, 0, priv->js_obj); + SETUP_MF_FIELD + for (i=0; icount; i++) { + jsval newVal = DOUBLE_TO_JSVAL( JS_NewDouble(priv->js_ctx, f->vals[i]) ); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + break; + } + case GF_SG_VRML_MFSTRING: + { + MFString *f = (MFString *) field->far_ptr; + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFStringClass, 0, priv->js_obj); + SETUP_MF_FIELD + for (i=0; icount; i++) { + s = JS_NewStringCopyZ(priv->js_ctx, f->vals[i]); + newVal = STRING_TO_JSVAL( s ); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + break; + } + case GF_SG_VRML_MFURL: + { + MFURL *f = (MFURL *) field->far_ptr; + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFUrlClass, 0, priv->js_obj); + SETUP_MF_FIELD + for (i=0; icount; i++) { + if (f->vals[i].OD_ID > 0) { + char msg[30]; + sprintf(msg, "od:%d", f->vals[i].OD_ID); + s = JS_NewStringCopyZ(priv->js_ctx, (const char *) msg); + } else { + s = JS_NewStringCopyZ(priv->js_ctx, f->vals[i].url); + } + newVal = STRING_TO_JSVAL( s ); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + break; + } + + case GF_SG_VRML_MFVEC2F: + { + MFVec2f *f = (MFVec2f *) field->far_ptr; + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFVec2fClass, 0, priv->js_obj); + SETUP_MF_FIELD + for (i=0; icount; i++) { + JSObject *pf = JS_NewObject(priv->js_ctx, &js_rt->SFVec2fClass, 0, obj); + newVal = OBJECT_TO_JSVAL(pf); + slot = SFVec2f_Create(priv->js_ctx, pf, f->vals[i].x, f->vals[i].y); + slot->owner = parent; + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + break; + } + case GF_SG_VRML_MFVEC3F: + { + MFVec3f *f = (MFVec3f *) field->far_ptr; + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFVec3fClass, 0, priv->js_obj); + SETUP_MF_FIELD + for (i=0; icount; i++) { + JSObject *pf = JS_NewObject(priv->js_ctx, &js_rt->SFVec3fClass, 0, obj); + newVal = OBJECT_TO_JSVAL(pf); + slot = SFVec3f_Create(priv->js_ctx, pf, f->vals[i].x, f->vals[i].y, f->vals[i].z); + slot->owner = parent; + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + break; + } + case GF_SG_VRML_MFROTATION: + { + MFRotation *f = (MFRotation*) field->far_ptr; + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFRotationClass, 0, priv->js_obj); + SETUP_MF_FIELD + for (i=0; icount; i++) { + JSObject *pf = JS_NewObject(priv->js_ctx, &js_rt->SFRotationClass, 0, obj); + newVal = OBJECT_TO_JSVAL(pf); + slot = SFRotation_Create(priv->js_ctx, pf, f->vals[i].x, f->vals[i].y, f->vals[i].z, f->vals[i].q); + slot->owner = parent; + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + break; + } + case GF_SG_VRML_MFCOLOR: + { + MFColor *f = (MFColor *) field->far_ptr; + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFColorClass, 0, priv->js_obj); + SETUP_MF_FIELD + for (i=0; icount; i++) { + JSObject *pf = JS_NewObject(priv->js_ctx, &js_rt->SFColorClass, 0, obj); + newVal = OBJECT_TO_JSVAL(pf); + slot = SFColor_Create(priv->js_ctx, pf, f->vals[i].red, f->vals[i].green, f->vals[i].blue); + slot->owner = parent; + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + } + break; + } + + case GF_SG_VRML_MFNODE: + { + u32 size; + GF_ChildNodeItem *f = * ((GF_ChildNodeItem **)field->far_ptr); + obj = JS_ConstructObject(priv->js_ctx, &js_rt->MFNodeClass, 0, priv->js_obj); + SETUP_MF_FIELD + size = gf_node_list_get_count(f); + + if (JS_SetArrayLength(priv->js_ctx, jsf->js_list, size) != JS_TRUE) return JSVAL_NULL; + i=0; + while (f) { + JSObject *pf = JS_NewObject(priv->js_ctx, &js_rt->SFNodeClass, 0, obj); + n = f->node; + if (n->sgprivate->tag == TAG_ProtoNode) { + GF_ProtoInstance *proto_inst = (GF_ProtoInstance *) n; + if (!proto_inst->RenderingNode) + gf_sg_proto_instanciate(proto_inst); + } + slot = NewJSField(); + slot->owner = parent; + slot->temp_node = n; + slot->field.far_ptr = & slot->temp_node; + slot->field.fieldType = GF_SG_VRML_SFNODE; + slot->field.fieldIndex = jsf->field.fieldIndex; + + vrml_node_register(n, parent); + JS_SetPrivate(priv->js_ctx, pf, slot); + + /*if this is the obj corresponding to an existing field/node, store it and prevent GC on object*/ + if (!skip_cache && priv->js_cache) { + slot->obj = pf; + JS_AddRoot(priv->js_ctx, &slot->obj); + gf_list_add(priv->js_cache, pf); + } + + + newVal = OBJECT_TO_JSVAL(pf); + JS_SetElement(priv->js_ctx, jsf->js_list, (jsint) i, &newVal); + f = f->next; + i++; + } + break; + } + + //not supported + default: + return JSVAL_NULL; + } + + if (!obj) return JSVAL_NULL; + //store field associated with object if needed + if (jsf) { + if (parent) parent->sgprivate->flags |= GF_NODE_HAS_BINDING; + + JS_SetPrivate(priv->js_ctx, obj, jsf); + /*if this is the obj corresponding to an existing field/node, store it and prevent GC on object*/ + if (parent && !skip_cache && priv->js_cache) { + jsf->obj = obj; + JS_AddRoot(priv->js_ctx, &jsf->obj); + gf_list_add(priv->js_cache, obj); + + if (jsf->js_list) + JS_AddRoot(priv->js_ctx, &jsf->js_list); + } + } + return OBJECT_TO_JSVAL(obj); + +} + +static void JS_ReleaseRootObjects(GF_ScriptPriv *priv) +{ + if (priv->js_cache) { + u32 i, count = gf_list_count(priv->js_cache); + for (i=0; ijs_cache, i); + jsf = (GF_JSField *) JS_GetPrivate(priv->js_ctx, obj); + + /* !!! WARNING !!! + + SpiderMonkey GC is handled at the JSRuntime level, not at the JSContext level. + Objects may not be finalized until the runtime is destroyed/GC'ed, which is not what we want. + We therefore destroy by hand all SFNode/MFNode + */ + if (jsf->obj) { + JS_RemoveRoot(priv->js_ctx, &jsf->obj); + jsf->obj = NULL; + + if (jsf->temp_node) { + node_finalize_ex(priv->js_ctx, obj, 0); + JS_SetPrivate(priv->js_ctx, obj, NULL); + } + } + if (jsf->js_list) { + JS_RemoveRoot(priv->js_ctx, &jsf->js_list); + jsf->js_list = NULL; + if (jsf->temp_list) { + array_finalize_ex(priv->js_ctx, obj, 0); + JS_SetPrivate(priv->js_ctx, obj, NULL); + } + } + } + } +} + +static void JS_PreDestroy(GF_Node *node) +{ + jsval fval, rval; + GF_ScriptPriv *priv = node->sgprivate->UserPrivate; + if (!priv) return; + + if (JS_LookupProperty(priv->js_ctx, priv->js_obj, "shutdown", &fval)) + if (! JSVAL_IS_VOID(fval)) + JS_CallFunctionValue(priv->js_ctx, priv->js_obj, fval, 0, NULL, &rval); + + /*unprotect all cached objects from GC*/ + JS_ReleaseRootObjects(priv); + + gf_sg_load_script_extensions(node->sgprivate->scenegraph, priv->js_ctx, priv->js_obj, 1); + gf_sg_ecmascript_del(priv->js_ctx); + +#ifndef GPAC_DISABLE_SVG + dom_js_unload(); +#endif + + if (priv->js_cache) gf_list_del(priv->js_cache); + priv->js_ctx = NULL; + /*unregister script from parent scene (cf base_scenegraph::sg_reset) */ + gf_list_del_item(node->sgprivate->scenegraph->scripts, node); +} + + +static void JS_InitScriptFields(GF_ScriptPriv *priv, GF_Node *sc) +{ + u32 i; + GF_ScriptField *sf; + GF_FieldInfo info; + jsval val; + + i=0; + while ((sf = gf_list_enum(priv->fields, &i))) { + + switch (sf->eventType) { + case GF_SG_EVENT_IN: + gf_node_get_field(sc, sf->ALL_index, &info); + val = gf_sg_script_to_smjs_field(priv, &info, sc, 0, 0); + break; + case GF_SG_EVENT_OUT: + gf_node_get_field(sc, sf->ALL_index, &info); + val = gf_sg_script_to_smjs_field(priv, &info, sc, 0, 0); + /*for native types directly modified*/ + JS_DefineProperty(priv->js_ctx, priv->js_obj, (const char *) sf->name, val, 0, gf_sg_script_eventout_set_prop, JSPROP_PERMANENT ); + break; + default: + gf_node_get_field(sc, sf->ALL_index, &info); + val = gf_sg_script_to_smjs_field(priv, &info, sc, 0, 0); + JS_DefineProperty(priv->js_ctx, priv->js_obj, (const char *) sf->name, val, 0, 0, JSPROP_PERMANENT); + break; + } + } +} + +static void flush_event_out(GF_Node *node, GF_ScriptPriv *priv) +{ + u32 i; + GF_ScriptField *sf; + + /*flush event out*/ + i=0; + while ((sf = gf_list_enum(priv->fields, &i))) { + if (sf->activate_event_out) { + sf->activate_event_out = 0; + gf_node_event_out(node, sf->ALL_index); + } + } +} + +static void JS_EventIn(GF_Node *node, GF_FieldInfo *in_field) +{ + jsval fval, rval; + Double time; + jsval argv[2]; + GF_ScriptField *sf; + GF_FieldInfo t_info; + GF_ScriptPriv *priv; + u32 i; + priv = node->sgprivate->UserPrivate; + + /*no support for change of static fields*/ + if (in_field->fieldIndex<3) return; + + i = (node->sgprivate->tag==TAG_MPEG4_Script) ? 3 : 4; + sf = gf_list_get(priv->fields, in_field->fieldIndex - i); + time = gf_node_get_scene_time(node); + + /* + if (sf->last_route_time == time) return; + */ + sf->last_route_time = time; + + //locate function + if (! JS_LookupProperty(priv->js_ctx, priv->js_obj, sf->name, &fval)) return; + if (JSVAL_IS_VOID(fval)) return; + + argv[0] = gf_sg_script_to_smjs_field(priv, in_field, node, 0, 1); + + memset(&t_info, 0, sizeof(GF_FieldInfo)); + t_info.far_ptr = &sf->last_route_time; + t_info.fieldType = GF_SG_VRML_SFTIME; + t_info.fieldIndex = -1; + t_info.name = "timestamp"; + argv[1] = gf_sg_script_to_smjs_field(priv, &t_info, node, 0, 1); + + /*protect args*/ + if (JSVAL_IS_GCTHING(argv[0])) JS_AddRoot(priv->js_ctx, &argv[0]); + if (JSVAL_IS_GCTHING(argv[1])) JS_AddRoot(priv->js_ctx, &argv[1]); + + JS_CallFunctionValue(priv->js_ctx, priv->js_obj, fval, 2, argv, &rval); + + /*release args*/ + if (JSVAL_IS_GCTHING(argv[0])) JS_RemoveRoot(priv->js_ctx, &argv[0]); + if (JSVAL_IS_GCTHING(argv[1])) JS_RemoveRoot(priv->js_ctx, &argv[1]); + + flush_event_out(node, priv); + /*perform JS cleanup*/ + JS_MaybeGC(priv->js_ctx); +} + +void JSScriptFromFile(GF_Node *node); + +static Bool vrml_js_load_script(M_Script *script, char *file) +{ + FILE *jsf; + char *jsscript; + u32 fsize; + Bool success = 1; + JSBool ret; + jsval rval, fval; + GF_ScriptPriv *priv = (GF_ScriptPriv *) script->sgprivate->UserPrivate; + + jsf = fopen(file, "rb"); + if (!jsf) return 0; + + fseek(jsf, 0, SEEK_END); + fsize = ftell(jsf); + fseek(jsf, 0, SEEK_SET); + jsscript = malloc(sizeof(char)*(fsize+1)); + fread(jsscript, sizeof(char)*fsize, 1, jsf); + fclose(jsf); + jsscript[fsize] = 0; + + ret = JS_EvaluateScript(priv->js_ctx, priv->js_obj, jsscript, sizeof(char)*fsize, 0, 0, &rval); + if (ret==JS_FALSE) success = 0; + + if (success && JS_LookupProperty(priv->js_ctx, priv->js_obj, "initialize", &fval)) { + if (! JSVAL_IS_VOID(fval)) { + JS_CallFunctionValue(priv->js_ctx, priv->js_obj, fval, 0, NULL, &rval); + flush_event_out((GF_Node *)script, priv); + } + } + free(jsscript); + return success; +} + + +static void JS_NetIO(void *cbck, GF_NETIO_Parameter *param) +{ + GF_Err e; + JSFileDownload *jsdnload = (JSFileDownload *)cbck; + M_Script *script = (M_Script *)jsdnload->node; + + e = param->error; + if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { + const char *szCache = gf_dm_sess_get_cache_name(jsdnload->sess); + if (!vrml_js_load_script(script, (char *) szCache)) + e = GF_SCRIPT_ERROR; + else + e = GF_OK; + } else if (!e) return; + + /*destroy current download session (ie, destroy ourselves)*/ + gf_dm_sess_del(jsdnload->sess); + free(jsdnload); + + if (e) { + u32 i; + if (script->url.count<=1) { + GF_JSAPIParam par; + par.info.e = e; + par.info.msg = "Cannot fetch script"; + ScriptAction(NULL, script->sgprivate->scenegraph, GF_JSAPI_OP_MESSAGE, NULL, &par); + return; + } + free(script->url.vals[0].script_text); + for (i=0; iurl.count-1; i++) + script->url.vals[i].script_text = script->url.vals[i+1].script_text; + script->url.vals[script->url.count-1].script_text = NULL; + script->url.count -= 1; + JSScriptFromFile((GF_Node *)script); + } +} + +void JSScriptFromFile(GF_Node *node) +{ + u32 i; + GF_Err e; + char szExt[50], *ext; + M_Script *script = (M_Script *)node; + Bool can_dnload = 0; + + for (i=0; iurl.count; i++) { + ext = strrchr(script->url.vals[i].script_text, '.'); + if (!ext) break; + strcpy(szExt, ext); + strlwr(szExt); + if (strcmp(szExt, ".js")) continue; + can_dnload = 1; + break; + } + if (!can_dnload) return; + + e = GF_SCRIPT_ERROR; + + if (!vrml_js_load_script(script, script->url.vals[0].script_text)) { + GF_JSAPIParam par; + char *url; + GF_Err e; + JSFileDownload *jsdnload; + + ScriptAction(NULL, node->sgprivate->scenegraph, GF_JSAPI_OP_GET_SCENE_URI, node, &par); + url = NULL; + if (par.uri.url) url = gf_url_concatenate(par.uri.url, script->url.vals[0].script_text); + if (!url) url = strdup(script->url.vals[0].script_text); + + par.dnld_man = NULL; + ScriptAction(NULL, node->sgprivate->scenegraph, GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, NULL, &par); + if (!par.dnld_man) return; + + if (!strstr(url, "://") || !strnicmp(url, "file://", 7)) { + free(url); + +retry: + if (script->url.count<=1) { + GF_JSAPIParam par; + par.info.e = e; + par.info.msg = "Cannot fetch script"; + ScriptAction(NULL, script->sgprivate->scenegraph, GF_JSAPI_OP_GET_DCCI, NULL, &par); + return; + } + free(script->url.vals[0].script_text); + for (i=0; iurl.count-1; i++) + script->url.vals[i].script_text = script->url.vals[i+1].script_text; + script->url.vals[script->url.count-1].script_text = NULL; + script->url.count -= 1; + JSScriptFromFile((GF_Node *)script); + } else { + GF_SAFEALLOC(jsdnload, JSFileDownload); + jsdnload->node = node; + jsdnload->sess = gf_dm_sess_new(par.dnld_man, script->url.vals[0].script_text, 0, JS_NetIO, jsdnload, &e); + free(url); + if (!jsdnload->sess) { + free(jsdnload); + goto retry; + } + } + } +} + +static void JSScript_LoadVRML(GF_Node *node) +{ + char *str; + JSBool ret; + u32 i; + Bool local_script; + jsval rval, fval; + M_Script *script = (M_Script *)node; + GF_ScriptPriv *priv = (GF_ScriptPriv *) node->sgprivate->UserPrivate; + + if (!priv || priv->is_loaded) return; + if (!script->url.count) return; + priv->is_loaded = 1; + + /*register script width parent scene (cf base_scenegraph::sg_reset) */ + gf_list_add(node->sgprivate->scenegraph->scripts, node); + + str = NULL; + for (i=0; iurl.count; i++) { + str = script->url.vals[i].script_text; + while (strchr("\n\t ", str[0])) str++; + + if (!strnicmp(str, "javascript:", 11)) str += 11; + else if (!strnicmp(str, "vrmlscript:", 11)) str += 11; + else if (!strnicmp(str, "ecmascript:", 11)) str += 11; + else if (!strnicmp(str, "mpeg4script:", 12)) str += 12; + else str = NULL; + if (str) break; + } + local_script = str ? 1 : 0; + + priv->js_ctx = gf_sg_ecmascript_new(node->sgprivate->scenegraph); + if (!priv->js_ctx) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Cannot allocate ECMAScript context for node\n")); + return; + } + + JS_SetContextPrivate(priv->js_ctx, node); + gf_sg_script_init_sm_api(priv, node); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] Built-in classes initialized\n")); +#ifndef GPAC_DISABLE_SVG + /*initialize DOM*/ + dom_js_load(node->sgprivate->scenegraph, priv->js_ctx, priv->js_obj); + /*create event object, and remember it*/ + priv->event = dom_js_define_event(priv->js_ctx, priv->js_obj); +#endif + + gf_sg_load_script_extensions(node->sgprivate->scenegraph, priv->js_ctx, priv->js_obj, 0); + + priv->js_cache = gf_list_new(); + + /*setup fields interfaces*/ + JS_InitScriptFields(priv, node); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] script fields initialized\n")); + + priv->JS_PreDestroy = JS_PreDestroy; + priv->JS_EventIn = JS_EventIn; + + if (!local_script) { + JSScriptFromFile(node); + return; + } + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] Evaluating script %s\n", str)); + + ret = JS_EvaluateScript(priv->js_ctx, priv->js_obj, str, strlen(str), 0, 0, &rval); + if (ret==JS_FALSE) { + return; + } + + /*call initialize if present*/ + if (! JS_LookupProperty(priv->js_ctx, priv->js_obj, "initialize", &fval)) return; + if (JSVAL_IS_VOID(fval)) return; + JS_CallFunctionValue(priv->js_ctx, priv->js_obj, fval, 0, NULL, &rval); + + flush_event_out(node, priv); + /*perform JS cleanup*/ + JS_MaybeGC(priv->js_ctx); +} + +static void JSScript_Load(GF_Node *node) +{ + switch (node->sgprivate->tag) { + case TAG_MPEG4_Script: + case TAG_X3D_Script: + JSScript_LoadVRML(node); + break; +#ifndef GPAC_DISABLE_SVG + case TAG_SVG_script: + case TAG_SVG_handler: + JSScript_LoadSVG(node); + break; +#endif + default: + break; + } +} + + + + +static void gf_sg_load_script_modules(GF_SceneGraph *sg) +{ + GF_Terminal *term; + u32 i, count; + GF_JSAPIParam par; + + js_rt->extensions = gf_list_new(); + + if (!sg->script_action) return; + if (!sg->script_action(sg->script_action_cbck, GF_JSAPI_OP_GET_TERM, sg->RootNode, &par)) + return; + + term = par.term; + + count = gf_modules_get_count(term->user->modules); + for (i=0; iuser->modules, i, GF_JS_USER_EXT_INTERFACE); + if (!ext) continue; + gf_list_add(js_rt->extensions, ext); + } +} + +static void gf_sg_unload_script_modules() +{ + while (gf_list_count(js_rt->extensions)) { + GF_JSUserExtension *ext = gf_list_last(js_rt->extensions); + gf_list_rem_last(js_rt->extensions); + gf_modules_close_interface((GF_BaseInterface *) ext); + } + gf_list_del(js_rt->extensions); +} + + +#ifdef __SYMBIAN32__ +const long MAX_HEAP_BYTES = 256 * 1024L; +#else +const long MAX_HEAP_BYTES = 4*1024 * 1024L; +#endif +const long STACK_CHUNK_BYTES = 8*1024L; + +JSContext *gf_sg_ecmascript_new(GF_SceneGraph *sg) +{ + if (!js_rt) { + JSRuntime *js_runtime = JS_NewRuntime(MAX_HEAP_BYTES); + if (!js_runtime) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[ECMAScript] Cannot allocate ECMAScript runtime\n")); + return NULL; + } + GF_SAFEALLOC(js_rt, GF_JSRuntime); + js_rt->js_runtime = js_runtime; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[ECMAScript] ECMAScript runtime allocated 0x%08x\n", js_runtime)); + gf_sg_load_script_modules(sg); + } + js_rt->nb_inst++; + return JS_NewContext(js_rt->js_runtime, STACK_CHUNK_BYTES); +} + +void gf_sg_ecmascript_del(JSContext *ctx) +{ + JS_SetGlobalObject(ctx, NULL); + JS_DestroyContext(ctx); + if (js_rt) { + js_rt->nb_inst --; + if (js_rt->nb_inst == 0) { + JS_DestroyRuntime(js_rt->js_runtime); + JS_ShutDown(); + gf_sg_unload_script_modules(); + free(js_rt); + js_rt = NULL; + } + } +} + +static void JSScript_NodeModified(GF_SceneGraph *sg, GF_Node *node, GF_FieldInfo *info) +{ + JSObject *obj = NULL; + GF_ScriptPriv *priv; + u32 i, count, j; + + count = gf_list_count(sg->scripts); + for (i=0; iscripts, i); + if ((p->sgprivate->tag!=TAG_MPEG4_Script) && (p->sgprivate->tag!=TAG_X3D_Script)) + continue; + + priv = (GF_ScriptPriv *)gf_node_get_private(p); + if (!priv->js_cache) continue; + + j=0; + while ((obj = gf_list_enum(priv->js_cache, &j))) { + GF_JSField *jsf = (GF_JSField *) JS_GetPrivate(priv->js_ctx, obj); + if (jsf + && (jsf->owner==node) + && (jsf->field.fieldIndex == info->fieldIndex) + && (jsf->field.fieldType == info->fieldType)) { + jsf->reevaluate = 1; + } + } + } +} + +void gf_sg_handle_dom_event_for_vrml(GF_Node *node, GF_DOM_Event *event, GF_Node *observer) +{ + GF_ScriptPriv *priv; + Bool prev_type; + JSBool ret = JS_FALSE; + GF_DOM_Event *prev_event = NULL; + SVG_handlerElement *hdl; + jsval rval; + + hdl = (SVG_handlerElement *) node; + if (!hdl->js_fun_val && !hdl->evt_listen_obj) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[DOM Events] Executing script code from VRML handler\n")); + + priv = JS_GetScriptStack(hdl->js_context); + prev_event = JS_GetPrivate(priv->js_ctx, priv->event); + /*break loops*/ + if (prev_event && (prev_event->type==event->type) && (prev_event->target==event->target)) + return; + + prev_type = event->is_vrml; + event->is_vrml = 1; + JS_SetPrivate(priv->js_ctx, priv->event, event); + + + { + JSObject *evt; + jsval argv[1]; + evt = gf_dom_new_event(priv->js_ctx); + JS_SetPrivate(priv->js_ctx, evt, event); + argv[0] = OBJECT_TO_JSVAL(evt); + + if (hdl->js_fun_val) { + jsval funval = (JSWord) hdl->js_fun_val; + ret = JS_CallFunctionValue(priv->js_ctx, hdl->evt_listen_obj ? (JSObject *)hdl->evt_listen_obj: priv->js_obj, funval, 1, argv, &rval); + } else { + ret = JS_CallFunctionName(priv->js_ctx, hdl->evt_listen_obj, "handleEvent", 1, argv, &rval); + } + } + + event->is_vrml = prev_type; + JS_SetPrivate(priv->js_ctx, priv->event, prev_event); + +} + +#endif + + +/*set JavaScript interface*/ +void gf_sg_set_script_action(GF_SceneGraph *scene, gf_sg_script_action script_act, void *cbk) +{ + if (!scene) return; + scene->script_action = script_act; + scene->script_action_cbck = cbk; + +#ifdef GPAC_HAS_SPIDERMONKEY + scene->script_load = JSScript_Load; + scene->on_node_modified = JSScript_NodeModified; +#endif + +} + diff --git a/src/scenegraph/vrml_tools.c b/src/scenegraph/vrml_tools.c new file mode 100644 index 0000000..630dbba --- /dev/null +++ b/src/scenegraph/vrml_tools.c @@ -0,0 +1,1489 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/*MPEG4 & X3D tags (for node tables & script handling)*/ +#include +#include + +/*this is not a NodeReplace, thus only the given container is updated - pos is 0-based*/ +GF_Err gf_node_replace_child(GF_Node *node, GF_ChildNodeItem **container, s32 pos, GF_Node *newNode) +{ + GF_ChildNodeItem *child, *prev; + u32 tag; + u32 cur_pos = 0; + + child = *container; + prev = NULL; + while (child->next) { + if ((pos<0) || (cur_pos!=(u32)pos)) { + prev = child; + child = child->next; + cur_pos++; + continue; + } + break; + } + tag = child->node->sgprivate->tag; + gf_node_unregister(child->node, node); + if (newNode) { + child->node = newNode; + if (tag==TAG_MPEG4_ColorTransform) + node->sgprivate->flags |= GF_SG_VRML_COLOR_DIRTY; + } else { + if (prev) prev->next = child->next; + else *container = child->next; + free(child); + } + return GF_OK; +} + + +static void Node_on_add_children(GF_Node *node) +{ + GF_ChildNodeItem *list; + GF_FieldInfo field; + GF_VRMLParent *n = (GF_VRMLParent *)node; + + if (n->children) { + list = n->children; + while (list->next) list = list->next; + list->next = n->addChildren; + } else { + n->children = n->addChildren; + } + n->addChildren = NULL; + + /*signal children field is modified*/ + field.name = "children"; + field.eventType = GF_SG_EVENT_EXPOSED_FIELD; + field.fieldType = GF_SG_VRML_MFNODE; + field.NDTtype = 0; + field.fieldIndex = 2; + field.far_ptr = & n->children; + gf_node_changed(node, &field); +} + +static void Node_on_remove_children(GF_Node *node) +{ + GF_ChildNodeItem *list; + GF_FieldInfo field; + GF_VRMLParent *n = (GF_VRMLParent *)node; + + if (!n->removeChildren) return; + + list = n->removeChildren; + while (list) { + if (gf_node_list_del_child(& n->children, list->node)) { + gf_node_unregister(list->node, node); + } + list = list->next; + } + gf_node_unregister_children(node, n->removeChildren); + n->removeChildren = NULL; + + /*signal children field is modified*/ + field.name = "children"; + field.eventType = GF_SG_EVENT_EXPOSED_FIELD; + field.fieldType = GF_SG_VRML_MFNODE; + field.NDTtype = 0; + field.fieldIndex = 2; + field.far_ptr = & n->children; + gf_node_changed(node, &field); +} + +void gf_sg_vrml_parent_setup(GF_Node *pNode) +{ + GF_VRMLParent *par = (GF_VRMLParent *)pNode; + par->children = NULL; + par->addChildren = NULL; + par->on_addChildren = Node_on_add_children; + par->removeChildren = NULL; + par->on_removeChildren = Node_on_remove_children; + pNode->sgprivate->flags |= GF_SG_CHILD_DIRTY; +} + +void gf_sg_vrml_parent_destroy(GF_Node *pNode) +{ + GF_VRMLParent *par = (GF_VRMLParent *)pNode; + gf_node_unregister_children(pNode, par->children); + gf_node_unregister_children(pNode, par->addChildren); + gf_node_unregister_children(pNode, par->removeChildren); +} + +GF_Err gf_sg_delete_all_protos(GF_SceneGraph *scene) +{ + if (!scene) return GF_BAD_PARAM; + while (gf_list_count(scene->protos)) { + GF_Proto *p = (GF_Proto *)gf_list_get(scene->protos, 0); + gf_sg_proto_del(p); + } + return GF_OK; +} + +GF_EXPORT +void gf_sg_set_proto_loader(GF_SceneGraph *scene, GF_SceneGraph *(*GetExternProtoLib)(void *SceneCallback, MFURL *lib_url)) +{ + if (!scene) return; + scene->GetExternProtoLib = GetExternProtoLib; +} + + +u32 gf_sg_get_next_available_route_id(GF_SceneGraph *sg) +{ + u32 i, count; + u32 ID = 0; + + if (!sg->max_defined_route_id) { + count = gf_list_count(sg->Routes); + /*routes are not sorted*/ + for (i=0; iRoutes, i); + if (ID<=r->ID) ID = r->ID; + } + return ID+1; + } else { + sg->max_defined_route_id++; + return sg->max_defined_route_id; + } +} + +void gf_sg_set_max_defined_route_id(GF_SceneGraph *sg, u32 ID) +{ + sg->max_defined_route_id = MAX(sg->max_defined_route_id, ID); +} + +u32 gf_sg_get_next_available_proto_id(GF_SceneGraph *sg) +{ + u32 i, count; + u32 ID = 0; + count = gf_list_count(sg->protos); + /*protos are not sorted*/ + for (i=0; iprotos, i); + if (ID<=p->ID) ID = p->ID; + } + count = gf_list_count(sg->unregistered_protos); + for (i=0; iunregistered_protos, i); + if (ID<=p->ID) ID = p->ID; + } + return ID+1; +} + +//adds a child in the children list +GF_Err gf_node_insert_child(GF_Node *parent, GF_Node *new_child, s32 Position) +{ + GF_ParentNode *node = (GF_ParentNode *) parent; + if (Position == -1) { + gf_node_list_add_child(& node->children, new_child); + } else { + gf_node_list_insert_child(& node->children, new_child, Position); + } + return GF_OK; +} + +/*for V4Studio...*/ +GF_Err gf_node_remove_child(GF_Node *parent, GF_Node *toremove_child) +{ + if (!gf_node_list_del_child(& ((GF_ParentNode *) parent)->children, toremove_child)) return GF_BAD_PARAM; + /*V4Studio doesn't handle DEF/USE properly yet...*/ + /*gf_node_unregister(toremove_child, parent);*/ + return GF_OK; +} + +void gf_sg_script_load(GF_Node *n) +{ + if (n && n->sgprivate->scenegraph->script_load) n->sgprivate->scenegraph->script_load(n); +} + +GF_Proto *gf_sg_find_proto(GF_SceneGraph *sg, u32 ProtoID, char *name) +{ + GF_Proto *proto; + u32 i; + + assert(sg); + + /*browse all top-level */ + i=0; + while ((proto = (GF_Proto *)gf_list_enum(sg->protos, &i))) { + /*first check on name if given, since parsers use this with ID=0*/ + if (name) { + if (proto->Name && !stricmp(name, proto->Name)) return proto; + } else if (proto->ID == ProtoID) return proto; + } + /*browse all top-level unregistered in reverse order*/ + for (i=gf_list_count(sg->unregistered_protos); i>0; i--) { + proto = (GF_Proto *)gf_list_get(sg->unregistered_protos, i-1); + if (name) { + if (proto->Name && !stricmp(name, proto->Name)) return proto; + } else if (proto->ID == ProtoID) return proto; + } + return NULL; +} + + + +u32 gf_bifs_get_child_table(GF_Node *Node) +{ + assert(Node); + return gf_sg_mpeg4_node_get_child_ndt(Node); +} + + +GF_Err gf_bifs_get_field_index(GF_Node *Node, u32 inField, u8 IndexMode, u32 *allField) +{ + assert(Node); + switch (Node->sgprivate->tag) { + case TAG_ProtoNode: + return gf_sg_proto_get_field_ind_static(Node, inField, IndexMode, allField); + case TAG_MPEG4_Script: + case TAG_X3D_Script: + return gf_sg_script_get_field_index(Node, inField, IndexMode, allField); + default: + return gf_sg_mpeg4_node_get_field_index(Node, inField, IndexMode, allField); + } +} + + +/* QUANTIZATION AND BIFS_Anim Info */ +GF_EXPORT +Bool gf_bifs_get_aq_info(GF_Node *Node, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) +{ + switch (Node->sgprivate->tag) { + case TAG_ProtoNode: return gf_sg_proto_get_aq_info(Node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + default: return gf_sg_mpeg4_node_get_aq_info(Node, FieldIndex, QType, AType, b_min, b_max, QT13_bits); + } +} + +static SFBool *NewSFBool() +{ + SFBool *tmp = (SFBool *)malloc(sizeof(SFBool)); + memset(tmp, 0, sizeof(SFBool)); + return tmp; +} +static SFFloat *NewSFFloat() +{ + SFFloat *tmp = (SFFloat *)malloc(sizeof(SFFloat)); + memset(tmp, 0, sizeof(SFFloat)); + return tmp; +} +static SFDouble *NewSFDouble() +{ + SFDouble *tmp = (SFDouble *)malloc(sizeof(SFDouble)); + memset(tmp, 0, sizeof(SFDouble)); + return tmp; +} +static SFTime *NewSFTime() +{ + SFTime *tmp = (SFTime *)malloc(sizeof(SFTime)); + memset(tmp, 0, sizeof(SFTime)); + return tmp; +} +static SFInt32 *NewSFInt32() +{ + SFInt32 *tmp = (SFInt32 *)malloc(sizeof(SFInt32)); + memset(tmp, 0, sizeof(SFInt32)); + return tmp; +} +static SFString *NewSFString() +{ + SFString *tmp = (SFString *)malloc(sizeof(SFString)); + memset(tmp, 0, sizeof(SFString)); + return tmp; +} +static SFVec3f *NewSFVec3f() +{ + SFVec3f *tmp = (SFVec3f *)malloc(sizeof(SFVec3f)); + memset(tmp, 0, sizeof(SFVec3f)); + return tmp; +} +static SFVec3d *NewSFVec3d() +{ + SFVec3d *tmp = (SFVec3d *)malloc(sizeof(SFVec3d)); + memset(tmp, 0, sizeof(SFVec3d)); + return tmp; +} +static SFVec2f *NewSFVec2f() +{ + SFVec2f *tmp = (SFVec2f *)malloc(sizeof(SFVec2f)); + memset(tmp, 0, sizeof(SFVec2f)); + return tmp; +} +static SFVec2d *NewSFVec2d() +{ + SFVec2d *tmp = (SFVec2d *)malloc(sizeof(SFVec2d)); + memset(tmp, 0, sizeof(SFVec2d)); + return tmp; +} +static SFColor *NewSFColor() +{ + SFColor *tmp = (SFColor *)malloc(sizeof(SFColor)); + memset(tmp, 0, sizeof(SFColor)); + return tmp; +} +static SFColorRGBA *NewSFColorRGBA() +{ + SFColorRGBA *tmp = (SFColorRGBA *)malloc(sizeof(SFColorRGBA)); + memset(tmp, 0, sizeof(SFColorRGBA)); + return tmp; +} +static SFRotation *NewSFRotation() +{ + SFRotation *tmp = (SFRotation *)malloc(sizeof(SFRotation)); + memset(tmp, 0, sizeof(SFRotation)); + return tmp; +} +static SFImage *NewSFImage() +{ + SFImage *tmp = (SFImage *)malloc(sizeof(SFImage)); + memset(tmp, 0, sizeof(SFImage)); + return tmp; +} +static SFURL *NewSFURL() +{ + SFURL *tmp = (SFURL *)malloc(sizeof(SFURL)); + memset(tmp, 0, sizeof(SFURL)); + return tmp; +} +static SFCommandBuffer *NewSFCommandBuffer() +{ + SFCommandBuffer *tmp = (SFCommandBuffer *)malloc(sizeof(SFCommandBuffer)); + memset(tmp, 0, sizeof(SFCommandBuffer)); + tmp->commandList = gf_list_new(); + return tmp; +} +static SFScript *NewSFScript() +{ + SFScript *tmp = (SFScript *)malloc(sizeof(SFScript)); + memset(tmp, 0, sizeof(SFScript)); + return tmp; +} +static MFBool *NewMFBool() +{ + MFBool *tmp = (MFBool *)malloc(sizeof(MFBool)); + memset(tmp, 0, sizeof(MFBool)); + return tmp; +} +static MFFloat *NewMFFloat() +{ + MFFloat *tmp = (MFFloat *)malloc(sizeof(MFFloat)); + memset(tmp, 0, sizeof(MFFloat)); + return tmp; +} +static MFTime *NewMFTime() +{ + MFTime *tmp = (MFTime *)malloc(sizeof(MFTime)); + memset(tmp, 0, sizeof(MFTime)); + return tmp; +} +static MFInt32 *NewMFInt32() +{ + MFInt32 *tmp = (MFInt32 *)malloc(sizeof(MFInt32)); + memset(tmp, 0, sizeof(MFInt32)); + return tmp; +} +static MFString *NewMFString() +{ + MFString *tmp = (MFString *)malloc(sizeof(MFString)); + memset(tmp, 0, sizeof(MFString)); + return tmp; +} +static MFVec3f *NewMFVec3f() +{ + MFVec3f *tmp = (MFVec3f *)malloc(sizeof(MFVec3f)); + memset(tmp, 0, sizeof(MFVec3f)); + return tmp; +} +static MFVec3d *NewMFVec3d() +{ + MFVec3d *tmp = (MFVec3d *)malloc(sizeof(MFVec3d)); + memset(tmp, 0, sizeof(MFVec3d)); + return tmp; +} +static MFVec2f *NewMFVec2f() +{ + MFVec2f *tmp = (MFVec2f *)malloc(sizeof(MFVec2f)); + memset(tmp, 0, sizeof(MFVec2f)); + return tmp; +} +static MFVec2d *NewMFVec2d() +{ + MFVec2d *tmp = (MFVec2d *)malloc(sizeof(MFVec2d)); + memset(tmp, 0, sizeof(MFVec2d)); + return tmp; +} +static MFColor *NewMFColor() +{ + MFColor *tmp = (MFColor *)malloc(sizeof(MFColor)); + memset(tmp, 0, sizeof(MFColor)); + return tmp; +} +static MFColorRGBA *NewMFColorRGBA() +{ + MFColorRGBA *tmp = (MFColorRGBA *)malloc(sizeof(MFColorRGBA)); + memset(tmp, 0, sizeof(MFColorRGBA)); + return tmp; +} +static MFRotation *NewMFRotation() +{ + MFRotation *tmp = (MFRotation *)malloc(sizeof(MFRotation)); + memset(tmp, 0, sizeof(MFRotation)); + return tmp; +} +static MFURL *NewMFURL() +{ + MFURL *tmp = (MFURL *)malloc(sizeof(MFURL)); + memset(tmp, 0, sizeof(MFURL)); + return tmp; +} +static MFScript *NewMFScript() +{ + MFScript *tmp = (MFScript *)malloc(sizeof(MFScript)); + memset(tmp, 0, sizeof(MFScript)); + return tmp; +} + +void *gf_sg_vrml_field_pointer_new(u32 FieldType) +{ + switch (FieldType) { + case GF_SG_VRML_SFBOOL: return NewSFBool(); + case GF_SG_VRML_SFFLOAT: return NewSFFloat(); + case GF_SG_VRML_SFDOUBLE: return NewSFDouble(); + case GF_SG_VRML_SFTIME: return NewSFTime(); + case GF_SG_VRML_SFINT32: return NewSFInt32(); + case GF_SG_VRML_SFSTRING: return NewSFString(); + case GF_SG_VRML_SFVEC3F: return NewSFVec3f(); + case GF_SG_VRML_SFVEC2F: return NewSFVec2f(); + case GF_SG_VRML_SFVEC3D: return NewSFVec3d(); + case GF_SG_VRML_SFVEC2D: return NewSFVec2d(); + case GF_SG_VRML_SFCOLOR: return NewSFColor(); + case GF_SG_VRML_SFCOLORRGBA: return NewSFColorRGBA(); + case GF_SG_VRML_SFROTATION: return NewSFRotation(); + case GF_SG_VRML_SFIMAGE: return NewSFImage(); + case GF_SG_VRML_MFBOOL: return NewMFBool(); + case GF_SG_VRML_MFFLOAT: return NewMFFloat(); + case GF_SG_VRML_MFTIME: return NewMFTime(); + case GF_SG_VRML_MFINT32: return NewMFInt32(); + case GF_SG_VRML_MFSTRING: return NewMFString(); + case GF_SG_VRML_MFVEC3F: return NewMFVec3f(); + case GF_SG_VRML_MFVEC2F: return NewMFVec2f(); + case GF_SG_VRML_MFVEC3D: return NewMFVec3d(); + case GF_SG_VRML_MFVEC2D: return NewMFVec2d(); + case GF_SG_VRML_MFCOLOR: return NewMFColor(); + case GF_SG_VRML_MFCOLORRGBA: return NewMFColorRGBA(); + case GF_SG_VRML_MFROTATION: return NewMFRotation(); + + //used in commands + case GF_SG_VRML_SFCOMMANDBUFFER: + return NewSFCommandBuffer(); + + case GF_SG_VRML_SFURL: + return NewSFURL(); + case GF_SG_VRML_MFURL: + return NewMFURL(); + + case GF_SG_VRML_SFSCRIPT: + return NewSFScript(); + case GF_SG_VRML_MFSCRIPT: + return NewMFScript(); + } + return NULL; +} + +void gf_sg_mfint32_del(MFInt32 par) { free(par.vals); } +void gf_sg_mffloat_del(MFFloat par) { free(par.vals); } +void gf_sg_mfdouble_del(MFDouble par) { free(par.vals); } +void gf_sg_mfbool_del(MFBool par) { free(par.vals); } +void gf_sg_mfcolor_del(MFColor par) { free(par.vals); } +void gf_sg_mfcolor_rgba_del(MFColorRGBA par) { free(par.vals); } +void gf_sg_mfrotation_del(MFRotation par) { free(par.vals); } +void gf_sg_mftime_del(MFTime par) { free(par.vals); } +void gf_sg_mfvec2f_del(MFVec2f par) { free(par.vals); } +void gf_sg_mfvec2d_del(MFVec2d par) { free(par.vals); } +void gf_sg_mfvec3f_del(MFVec3f par) { free(par.vals); } +void gf_sg_mfvec3d_del(MFVec3d par) { free(par.vals); } +void gf_sg_mfvec4f_del(MFVec4f par) { free(par.vals); } +void gf_sg_sfimage_del(SFImage im) { free(im.pixels); } +void gf_sg_sfurl_del(SFURL url) { if (url.url) free(url.url); } +void gf_sg_sfstring_del(SFString par) { if (par.buffer) free(par.buffer); } +void gf_sg_sfscript_del(SFScript par) { if (par.script_text) free(par.script_text); } + +void gf_sg_mfstring_del(MFString par) +{ + u32 i; + for (i=0; i0; i--) { + GF_Command *com = (GF_Command *)gf_list_get(cb.commandList, i-1); + gf_sg_command_del(com); + } + gf_list_del(cb.commandList); + if (cb.buffer) free(cb.buffer); +} + +GF_EXPORT +void gf_sg_mfurl_del(MFURL url) +{ + u32 i; + for (i=0; ibuffer) free(((SFString *)field)->buffer); + break; + case GF_SG_VRML_SFIMAGE: + gf_sg_sfimage_del(* ((SFImage *)field)); + break; + + case GF_SG_VRML_SFNODE: + node = *(GF_Node **) field; + if (node) gf_node_del(node); + return; + case GF_SG_VRML_SFCOMMANDBUFFER: + gf_sg_sfcommand_del(*(SFCommandBuffer *)field); + break; + + case GF_SG_VRML_MFBOOL: + gf_sg_mfbool_del( * ((MFBool *) field)); + break; + case GF_SG_VRML_MFFLOAT: + gf_sg_mffloat_del( * ((MFFloat *) field)); + break; + case GF_SG_VRML_MFDOUBLE: + gf_sg_mfdouble_del( * ((MFDouble *) field)); + break; + case GF_SG_VRML_MFTIME: + gf_sg_mftime_del( * ((MFTime *)field)); + break; + case GF_SG_VRML_MFINT32: + gf_sg_mfint32_del( * ((MFInt32 *)field)); + break; + case GF_SG_VRML_MFSTRING: + gf_sg_mfstring_del( *((MFString *)field)); + break; + case GF_SG_VRML_MFVEC3F: + gf_sg_mfvec3f_del( * ((MFVec3f *)field)); + break; + case GF_SG_VRML_MFVEC2F: + gf_sg_mfvec2f_del( * ((MFVec2f *)field)); + break; + case GF_SG_VRML_MFVEC3D: + gf_sg_mfvec3d_del( * ((MFVec3d *)field)); + break; + case GF_SG_VRML_MFVEC2D: + gf_sg_mfvec2d_del( * ((MFVec2d *)field)); + break; + case GF_SG_VRML_MFCOLOR: + gf_sg_mfcolor_del( * ((MFColor *)field)); + break; + case GF_SG_VRML_MFCOLORRGBA: + gf_sg_mfcolor_rgba_del( * ((MFColorRGBA *)field)); + break; + case GF_SG_VRML_MFROTATION: + gf_sg_mfrotation_del( * ((MFRotation *)field)); + break; + case GF_SG_VRML_SFURL: + gf_sg_sfurl_del( * ((SFURL *) field)); + break; + case GF_SG_VRML_MFURL: + gf_sg_mfurl_del( * ((MFURL *) field)); + break; + //used only in proto since this field is created by default for regular nodes + case GF_SG_VRML_MFNODE: + assert(0); + return; + case GF_SG_VRML_MFSCRIPT: + gf_sg_mfscript_del( * ((MFScript *) field)); + break; + + default: + assert(0); + return; + } + //free pointer + free(field); +} + + +Bool gf_sg_vrml_is_sf_field(u32 FieldType) +{ + return (FieldTypecount || !mffield->array) { + if (mffield->array) free(mffield->array); + mffield->array = (char*)malloc(sizeof(char)*FieldSize); + memset(mffield->array, 0, sizeof(char)*FieldSize); + mffield->count = 1; + if (new_ptr) *new_ptr = mffield->array; + return GF_OK; + } + + //append at the end + if (InsertAt >= mffield->count) { + mffield->array = (char*)realloc(mffield->array, sizeof(char)*(1+mffield->count)*FieldSize); + memset(mffield->array + mffield->count * FieldSize, 0, FieldSize); + if (new_ptr) *new_ptr = mffield->array + mffield->count * FieldSize; + mffield->count += 1; + return GF_OK; + } + //alloc 1+itemCount + buffer = (char*)malloc(sizeof(char)*(1+mffield->count)*FieldSize); + + //insert in the array + k=0; + for (i=0; i < mffield->count; i++) { + if (InsertAt == i) { + if (new_ptr) { + *new_ptr = buffer + i*FieldSize; + memset(*new_ptr, 0, sizeof(char)*FieldSize); + } + k = 1; + } + memcpy(buffer + (k+i) * FieldSize , mffield->array + i*FieldSize, FieldSize); + } + free(mffield->array); + mffield->array = buffer; + mffield->count += 1; + return GF_OK; +} + +#define MAX_MFFIELD_ALLOC 5000000 +GF_EXPORT +GF_Err gf_sg_vrml_mf_alloc(void *mf, u32 FieldType, u32 NbItems) +{ + u32 FieldSize; + GenMFField *mffield = (GenMFField *)mf; + + if (gf_sg_vrml_is_sf_field(FieldType)) return GF_BAD_PARAM; + if (FieldType == GF_SG_VRML_MFNODE) return GF_BAD_PARAM; + + FieldSize = gf_sg_vrml_get_sf_size(FieldType); + + //field we can't copy + if (!FieldSize) return GF_BAD_PARAM; + if (NbItems>MAX_MFFIELD_ALLOC) return GF_IO_ERR; + + if (mffield->count==NbItems) return GF_OK; + gf_sg_vrml_mf_reset(mf, FieldType); + if (NbItems) { + mffield->array = (char*)malloc(sizeof(char)*FieldSize*NbItems); + memset(mffield->array, 0, sizeof(char)*FieldSize*NbItems); + } + mffield->count = NbItems; + return GF_OK; +} + +GF_Err gf_sg_vrml_mf_get_item(void *mf, u32 FieldType, void **new_ptr, u32 ItemPos) +{ + u32 FieldSize; + GenMFField *mffield = (GenMFField *)mf; + + *new_ptr = NULL; + if (gf_sg_vrml_is_sf_field(FieldType)) return GF_BAD_PARAM; + if (FieldType == GF_SG_VRML_MFNODE) return GF_BAD_PARAM; + + FieldSize = gf_sg_vrml_get_sf_size(FieldType); + + //field we can't copy + if (!FieldSize) return GF_BAD_PARAM; + if (ItemPos >= mffield->count) return GF_BAD_PARAM; + *new_ptr = mffield->array + ItemPos * FieldSize; + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_sg_vrml_mf_append(void *mf, u32 FieldType, void **new_ptr) +{ + GenMFField *mffield = (GenMFField *)mf; + return gf_sg_vrml_mf_insert(mf, FieldType, new_ptr, mffield->count+2); +} + + +//remove the specified item (0-based index) +GF_Err gf_sg_vrml_mf_remove(void *mf, u32 FieldType, u32 RemoveFrom) +{ + char *buffer; + u32 FieldSize, i, k; + GenMFField *mffield = (GenMFField *)mf; + + FieldSize = gf_sg_vrml_get_sf_size(FieldType); + + //field we can't copy + if (!FieldSize) return GF_BAD_PARAM; + + if (!mffield->count || RemoveFrom >= mffield->count) return GF_BAD_PARAM; + + if (mffield->count == 1) { + free(mffield->array); + mffield->array = NULL; + mffield->count = 0; + return GF_OK; + } + k=0; + buffer = (char*)malloc(sizeof(char)*(mffield->count-1)*FieldSize); + for (i=0; icount; i++) { + if (RemoveFrom == i) { + k = 1; + } else { + memcpy(buffer + (i-k)*FieldSize, mffield->array + i*FieldSize, FieldSize); + } + } + free(mffield->array); + mffield->array = buffer; + mffield->count -= 1; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_sg_vrml_mf_reset(void *mf, u32 FieldType) +{ + GenMFField *mffield = (GenMFField *)mf; + if (!mffield->array) return GF_OK; + + //field we can't copy + if (gf_sg_vrml_is_sf_field(FieldType)) return GF_BAD_PARAM; + if (!gf_sg_vrml_get_sf_size(FieldType)) return GF_BAD_PARAM; + + switch (FieldType) { + case GF_SG_VRML_MFSTRING: + gf_sg_mfstring_del( * ((MFString *) mf)); + break; + case GF_SG_VRML_MFURL: + gf_sg_mfurl_del( * ((MFURL *) mf)); + break; + case GF_SG_VRML_MFSCRIPT: + gf_sg_mfscript_del( * ((MFScript *) mf)); + break; + default: + if (mffield->array) free(mffield->array); + break; + } + + mffield->array = NULL; + mffield->count = 0; + return GF_OK; +} + +/*special cloning with type-casting from SF/MF strings to URL conversion since proto URL doesn't exist +as a field type (it's just a stupid encoding trick) */ +void VRML_FieldCopyCast(void *dest, u32 dst_field_type, void *orig, u32 ori_field_type) +{ + SFURL *url; + char tmp[50]; + u32 size, i, sf_type_ori, sf_type_dst; + void *dst_field, *orig_field; + if (!dest || !orig) return; + + switch (dst_field_type) { + case GF_SG_VRML_SFSTRING: + if (ori_field_type == GF_SG_VRML_SFURL) { + url = ((SFURL *)orig); + if (url->OD_ID>0) { + sprintf(tmp, "%d", url->OD_ID); + if ( ((SFString*)dest)->buffer) free(((SFString*)dest)->buffer); + ((SFString*)dest)->buffer = strdup(tmp); + } else { + if ( ((SFString*)dest)->buffer) free(((SFString*)dest)->buffer); + ((SFString*)dest)->buffer = strdup(url->url); + } + } + /*for SFString to MFString cast*/ + else if (ori_field_type == GF_SG_VRML_SFSTRING) { + if ( ((SFString*)dest)->buffer) free(((SFString*)dest)->buffer); + ((SFString*)dest)->buffer = strdup(((SFString*)orig)->buffer); + } + return; + case GF_SG_VRML_SFURL: + if (ori_field_type != GF_SG_VRML_SFSTRING) return; + url = ((SFURL *)dest); + url->OD_ID = 0; + if (url->url) free(url->url); + if ( ((SFString*)orig)->buffer) + url->url = strdup(((SFString*)orig)->buffer); + else + url->url = NULL; + return; + case GF_SG_VRML_MFSTRING: + case GF_SG_VRML_MFURL: + break; + default: + return; + } + + sf_type_dst = gf_sg_vrml_get_sf_type(dst_field_type); + + if (gf_sg_vrml_is_sf_field(ori_field_type)) { + size = 1; + gf_sg_vrml_mf_alloc(dest, dst_field_type, size); + gf_sg_vrml_mf_get_item(dest, dst_field_type, &dst_field, 0); + VRML_FieldCopyCast(dst_field, sf_type_dst, orig, ori_field_type); + return; + } + + size = ((GenMFField *)orig)->count; + if (size != ((GenMFField *)dest)->count) gf_sg_vrml_mf_alloc(dest, dst_field_type, size); + + sf_type_ori = gf_sg_vrml_get_sf_type(ori_field_type); + //duplicate all items + for (i=0; ibuffer) free(((SFString*)dest)->buffer); + if ( ((SFString*)orig)->buffer ) + ((SFString*)dest)->buffer = strdup(((SFString*)orig)->buffer); + else + ((SFString*)dest)->buffer = NULL; + break; + case GF_SG_VRML_SFURL: + if ( ((SFURL *)dest)->url ) free( ((SFURL *)dest)->url ); + ((SFURL *)dest)->OD_ID = ((SFURL *)orig)->OD_ID; + if (((SFURL *)orig)->url) + ((SFURL *)dest)->url = strdup(((SFURL *)orig)->url); + else + ((SFURL *)dest)->url = NULL; + break; + case GF_SG_VRML_SFIMAGE: + if (((SFImage *)dest)->pixels) free(((SFImage *)dest)->pixels); + ((SFImage *)dest)->width = ((SFImage *)orig)->width; + ((SFImage *)dest)->height = ((SFImage *)orig)->height; + ((SFImage *)dest)->numComponents = ((SFImage *)orig)->numComponents; + size = ((SFImage *)dest)->width * ((SFImage *)dest)->height * ((SFImage *)dest)->numComponents; + ((SFImage *)dest)->pixels = (u8*)malloc(sizeof(char)*size); + memcpy(((SFImage *)dest)->pixels, ((SFImage *)orig)->pixels, sizeof(char)*size); + break; + case GF_SG_VRML_SFCOMMANDBUFFER: + gf_sg_sfcommand_del( *(SFCommandBuffer *)dest); + ((SFCommandBuffer *)dest)->commandList = gf_list_new(); + ((SFCommandBuffer *)dest)->bufferSize = ((SFCommandBuffer *)orig)->bufferSize; + if (((SFCommandBuffer *)dest)->bufferSize) { + ((SFCommandBuffer *)dest)->buffer = (u8*)malloc(sizeof(char)*((SFCommandBuffer *)orig)->bufferSize); + memcpy(((SFCommandBuffer *)dest)->buffer, + ((SFCommandBuffer *)orig)->buffer, + sizeof(char)*((SFCommandBuffer *)orig)->bufferSize); + } else { + ((SFCommandBuffer *)dest)->buffer = NULL; + } + break; + + /*simply copy text string*/ + case GF_SG_VRML_SFSCRIPT: + if (((SFScript*)dest)->script_text) free(((SFScript*)dest)->script_text); + ((SFScript*)dest)->script_text = NULL; + if ( ((SFScript*)orig)->script_text) + ((SFScript *)dest)->script_text = (u8*)strdup( (char*) ((SFScript*)orig)->script_text ); + break; + + + //simple MFFields, do a memcpy + case GF_SG_VRML_MFBOOL: + case GF_SG_VRML_MFFLOAT: + case GF_SG_VRML_MFTIME: + case GF_SG_VRML_MFINT32: + case GF_SG_VRML_MFVEC3F: + case GF_SG_VRML_MFVEC2F: + case GF_SG_VRML_MFCOLOR: + case GF_SG_VRML_MFROTATION: + size = gf_sg_vrml_get_sf_size(field_type) * ((GenMFField *)orig)->count; + if (((GenMFField *)orig)->count != ((GenMFField *)dest)->count) { + ((GenMFField *)dest)->array = realloc(((GenMFField *)dest)->array, size); + ((GenMFField *)dest)->count = ((GenMFField *)orig)->count; + } + memcpy(((GenMFField *)dest)->array, ((GenMFField *)orig)->array, size); + break; + //complex MFFields + case GF_SG_VRML_MFSTRING: + case GF_SG_VRML_MFIMAGE: + case GF_SG_VRML_MFURL: + case GF_SG_VRML_MFSCRIPT: + size = ((GenMFField *)orig)->count; + gf_sg_vrml_mf_reset(dest, field_type); + gf_sg_vrml_mf_alloc(dest, field_type, size); + sf_type = gf_sg_vrml_get_sf_type(field_type); + //duplicate all items + for (i=0; ired != ((SFColor *)orig)->red) changed = 1; + else if (((SFColor *)dest)->green != ((SFColor *)orig)->green) changed = 1; + else if (((SFColor *)dest)->blue != ((SFColor *)orig)->blue) changed = 1; + break; + case GF_SG_VRML_SFFLOAT: + if ( (*(SFFloat *)dest) != (*(SFFloat *)orig) ) changed = 1; + break; + case GF_SG_VRML_SFINT32: + changed = memcmp(dest, orig, sizeof(SFInt32)); + break; + case GF_SG_VRML_SFROTATION: + if (((SFRotation *)dest)->x != ((SFRotation *)orig)->x) changed = 1; + else if (((SFRotation *)dest)->y != ((SFRotation *)orig)->y) changed = 1; + else if (((SFRotation *)dest)->z != ((SFRotation *)orig)->z) changed = 1; + else if (((SFRotation *)dest)->q != ((SFRotation *)orig)->q) changed = 1; + break; + case GF_SG_VRML_SFTIME: + if ( (*(SFTime *)dest) != (*(SFTime*)orig) ) changed = 1; + break; + case GF_SG_VRML_SFVEC2F: + if (((SFVec2f *)dest)->x != ((SFVec2f *)orig)->x) changed = 1; + else if (((SFVec2f *)dest)->y != ((SFVec2f *)orig)->y) changed = 1; + break; + case GF_SG_VRML_SFVEC3F: + if (((SFVec3f *)dest)->x != ((SFVec3f *)orig)->x) changed = 1; + else if (((SFVec3f *)dest)->y != ((SFVec3f *)orig)->y) changed = 1; + else if (((SFVec3f *)dest)->z != ((SFVec3f *)orig)->z) changed = 1; + break; + case GF_SG_VRML_SFSTRING: + if ( ((SFString*)dest)->buffer && ((SFString*)orig)->buffer) { + changed = strcmp(((SFString*)dest)->buffer, ((SFString*)orig)->buffer); + } else { + changed = ( !((SFString*)dest)->buffer && !((SFString*)orig)->buffer) ? 0 : 1; + } + break; + case GF_SG_VRML_SFURL: + if (((SFURL *)dest)->OD_ID > 0 || ((SFURL *)orig)->OD_ID > 0) { + if ( ((SFURL *)orig)->OD_ID != ((SFURL *)dest)->OD_ID) changed = 1; + } else { + if ( ((SFURL *)orig)->url && ! ((SFURL *)dest)->url) changed = 1; + else if ( ! ((SFURL *)orig)->url && ((SFURL *)dest)->url) changed = 1; + else if ( strcmp( ((SFURL *)orig)->url , ((SFURL *)dest)->url) ) changed = 1; + } + break; + case GF_SG_VRML_SFIMAGE: + case GF_SG_VRML_SFSCRIPT: + case GF_SG_VRML_SFCOMMANDBUFFER: + changed = 1; + break; + + //MFFields + case GF_SG_VRML_MFBOOL: + case GF_SG_VRML_MFFLOAT: + case GF_SG_VRML_MFTIME: + case GF_SG_VRML_MFINT32: + case GF_SG_VRML_MFSTRING: + case GF_SG_VRML_MFVEC3F: + case GF_SG_VRML_MFVEC2F: + case GF_SG_VRML_MFCOLOR: + case GF_SG_VRML_MFROTATION: + case GF_SG_VRML_MFIMAGE: + case GF_SG_VRML_MFURL: + case GF_SG_VRML_MFSCRIPT: + if ( ((GenMFField *)orig)->count != ((GenMFField *)dest)->count) changed = 1; + else { + size = ((GenMFField *)orig)->count; + sf_type = gf_sg_vrml_get_sf_type(field_type); + for (i=0; isgprivate->tag == TAG_ProtoNode) return gf_sg_proto_get_num_fields(Node, IndexMode); + else if ((Node->sgprivate->tag == TAG_MPEG4_Script) || (Node->sgprivate->tag == TAG_X3D_Script) ) + return gf_sg_script_get_num_fields(Node, IndexMode); + else if (Node->sgprivate->tag <= GF_NODE_RANGE_LAST_MPEG4) return gf_sg_mpeg4_node_get_field_count(Node, IndexMode); + else if (Node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) return gf_sg_x3d_node_get_field_count(Node); + else return 0; +} + + + +/*all our internally handled nodes*/ +Bool InitColorInterpolator(M_ColorInterpolator *node); +Bool InitCoordinateInterpolator2D(M_CoordinateInterpolator2D *node); +Bool InitCoordinateInterpolator(M_CoordinateInterpolator *n); +Bool InitNormalInterpolator(M_NormalInterpolator *n); +Bool InitPositionInterpolator2D(M_PositionInterpolator2D *node); +Bool InitPositionInterpolator(M_PositionInterpolator *node); +Bool InitScalarInterpolator(M_ScalarInterpolator *node); +Bool InitOrientationInterpolator(M_OrientationInterpolator *node); +Bool InitValuator(M_Valuator *node); +Bool InitCoordinateInterpolator4D(M_CoordinateInterpolator4D *node); +Bool InitPositionInterpolator4D(M_PositionInterpolator4D *node); + +void PA_Init(GF_Node *n); +void PA_Modified(GF_Node *n, GF_FieldInfo *field); +void PA2D_Init(GF_Node *n); +void PA2D_Modified(GF_Node *n, GF_FieldInfo *field); +void SA_Init(GF_Node *n); +void SA_Modified(GF_Node *n, GF_FieldInfo *field); +/*X3D tools*/ +void InitBooleanFilter(GF_Node *n); +void InitBooleanSequencer(GF_Node *n); +void InitBooleanToggle(GF_Node *n); +void InitBooleanTrigger(GF_Node *n); +void InitIntegerSequencer(GF_Node *n); +void InitIntegerTrigger(GF_Node *n); +void InitTimeTrigger(GF_Node *n); + +Bool gf_sg_vrml_node_init(GF_Node *node) +{ + switch (node->sgprivate->tag) { + case TAG_MPEG4_ColorInterpolator: + case TAG_X3D_ColorInterpolator: + return InitColorInterpolator((M_ColorInterpolator *)node); + case TAG_MPEG4_CoordinateInterpolator: + case TAG_X3D_CoordinateInterpolator: + return InitCoordinateInterpolator((M_CoordinateInterpolator *)node); + case TAG_MPEG4_CoordinateInterpolator2D: + return InitCoordinateInterpolator2D((M_CoordinateInterpolator2D *)node); + case TAG_MPEG4_NormalInterpolator: + case TAG_X3D_NormalInterpolator: + return InitNormalInterpolator((M_NormalInterpolator*)node); + case TAG_MPEG4_OrientationInterpolator: + case TAG_X3D_OrientationInterpolator: + return InitOrientationInterpolator((M_OrientationInterpolator*)node); + case TAG_MPEG4_PositionInterpolator: + case TAG_X3D_PositionInterpolator: + return InitPositionInterpolator((M_PositionInterpolator *)node); + case TAG_MPEG4_PositionInterpolator2D: + case TAG_X3D_PositionInterpolator2D: + return InitPositionInterpolator2D((M_PositionInterpolator2D *)node); + case TAG_MPEG4_ScalarInterpolator: + case TAG_X3D_ScalarInterpolator: + return InitScalarInterpolator((M_ScalarInterpolator *)node); + case TAG_MPEG4_Valuator: + return InitValuator((M_Valuator *)node); + case TAG_MPEG4_PositionAnimator: + PA_Init(node); return 1; + case TAG_MPEG4_PositionAnimator2D: + PA2D_Init(node); return 1; + case TAG_MPEG4_ScalarAnimator: + SA_Init(node); return 1; + case TAG_MPEG4_PositionInterpolator4D: + return InitPositionInterpolator4D((M_PositionInterpolator4D *)node); + case TAG_MPEG4_CoordinateInterpolator4D: + return InitCoordinateInterpolator4D((M_CoordinateInterpolator4D *)node); + case TAG_MPEG4_Script: + case TAG_X3D_Script: + return 1; + + case TAG_X3D_BooleanFilter: InitBooleanFilter(node); return 1; + case TAG_X3D_BooleanSequencer: InitBooleanSequencer(node); return 1; + case TAG_X3D_BooleanToggle: InitBooleanToggle(node); return 1; + case TAG_X3D_BooleanTrigger: InitBooleanTrigger(node); return 1; + case TAG_X3D_IntegerSequencer: InitIntegerSequencer(node); return 1; + case TAG_X3D_IntegerTrigger: InitIntegerTrigger(node); return 1; + case TAG_X3D_TimeTrigger: InitTimeTrigger(node); return 1; + } + return 0; +} + +Bool gf_sg_vrml_node_changed(GF_Node *node, GF_FieldInfo *field) +{ + switch (node->sgprivate->tag) { + case TAG_ProtoNode: + /*hardcoded protos need modification notifs*/ + if (node->sgprivate->UserCallback) return 0; + case TAG_MPEG4_ColorInterpolator: + case TAG_X3D_ColorInterpolator: + case TAG_MPEG4_CoordinateInterpolator: + case TAG_X3D_CoordinateInterpolator: + case TAG_MPEG4_CoordinateInterpolator2D: + case TAG_MPEG4_NormalInterpolator: + case TAG_X3D_NormalInterpolator: + case TAG_MPEG4_OrientationInterpolator: + case TAG_X3D_OrientationInterpolator: + case TAG_MPEG4_PositionInterpolator: + case TAG_X3D_PositionInterpolator: + case TAG_MPEG4_PositionInterpolator2D: + case TAG_MPEG4_ScalarInterpolator: + case TAG_X3D_ScalarInterpolator: + case TAG_MPEG4_Valuator: + case TAG_MPEG4_PositionInterpolator4D: + case TAG_MPEG4_CoordinateInterpolator4D: + case TAG_MPEG4_Script: + case TAG_X3D_Script: + case TAG_X3D_BooleanFilter: + case TAG_X3D_BooleanSequencer: + case TAG_X3D_BooleanToggle: + case TAG_X3D_BooleanTrigger: + case TAG_X3D_IntegerSequencer: + case TAG_X3D_IntegerTrigger: + case TAG_X3D_TimeTrigger: + return 1; + case TAG_MPEG4_PositionAnimator: PA_Modified(node, field); return 1; + case TAG_MPEG4_PositionAnimator2D: PA2D_Modified(node, field); return 1; + case TAG_MPEG4_ScalarAnimator: SA_Modified(node, field); return 1; + } + return 0; +} + + diff --git a/src/scenegraph/x3d_nodes.c b/src/scenegraph/x3d_nodes.c new file mode 100644 index 0000000..b27e4e4 --- /dev/null +++ b/src/scenegraph/x3d_nodes.c @@ -0,0 +1,10628 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / X3D Scene Graph sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + DO NOT MOFIFY - File generated on GMT Thu Aug 07 11:44:22 2008 + + BY X3DGen for GPAC Version 0.4.5-DEV +*/ + + +#include + +#include + +/*for NDT tag definitions*/ +#include + +/* + Anchor Node deletion +*/ + +static void Anchor_Del(GF_Node *node) +{ + X_Anchor *p = (X_Anchor *) node; + gf_sg_sfstring_del(p->description); + gf_sg_mfstring_del(p->parameter); + gf_sg_mfurl_del(p->url); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_vrml_parent_destroy(node); + gf_node_free((GF_Node *)p); +} + + +static u32 Anchor_get_field_count(GF_Node *node, u8 dummy) +{ + return 7; +} + +static GF_Err Anchor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Anchor *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Anchor *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Anchor *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Anchor *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Anchor *)node)->children; + return GF_OK; + case 3: + info->name = "description"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_Anchor *) node)->description; + return GF_OK; + case 4: + info->name = "parameter"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_Anchor *) node)->parameter; + return GF_OK; + case 5: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_Anchor *) node)->url; + return GF_OK; + case 6: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Anchor *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Anchor_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("description", name)) return 3; + if (!strcmp("parameter", name)) return 4; + if (!strcmp("url", name)) return 5; + if (!strcmp("metadata", name)) return 6; + return -1; + } + + +static GF_Node *Anchor_Create() +{ + X_Anchor *p; + GF_SAFEALLOC(p, X_Anchor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Anchor); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Appearance Node deletion +*/ + +static void Appearance_Del(GF_Node *node) +{ + X_Appearance *p = (X_Appearance *) node; + gf_node_unregister((GF_Node *) p->material, node); + gf_node_unregister((GF_Node *) p->texture, node); + gf_node_unregister((GF_Node *) p->textureTransform, node); + gf_node_unregister((GF_Node *) p->fillProperties, node); + gf_node_unregister((GF_Node *) p->lineProperties, node); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Appearance_get_field_count(GF_Node *node, u8 dummy) +{ + return 6; +} + +static GF_Err Appearance_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "material"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMaterialNode; + info->far_ptr = & ((X_Appearance *)node)->material; + return GF_OK; + case 1: + info->name = "texture"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((X_Appearance *)node)->texture; + return GF_OK; + case 2: + info->name = "textureTransform"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureTransformNode; + info->far_ptr = & ((X_Appearance *)node)->textureTransform; + return GF_OK; + case 3: + info->name = "fillProperties"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFFillPropertiesNode; + info->far_ptr = & ((X_Appearance *)node)->fillProperties; + return GF_OK; + case 4: + info->name = "lineProperties"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFX3DLinePropertiesNode; + info->far_ptr = & ((X_Appearance *)node)->lineProperties; + return GF_OK; + case 5: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Appearance *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Appearance_get_field_index_by_name(char *name) +{ + if (!strcmp("material", name)) return 0; + if (!strcmp("texture", name)) return 1; + if (!strcmp("textureTransform", name)) return 2; + if (!strcmp("fillProperties", name)) return 3; + if (!strcmp("lineProperties", name)) return 4; + if (!strcmp("metadata", name)) return 5; + return -1; + } + + +static GF_Node *Appearance_Create() +{ + X_Appearance *p; + GF_SAFEALLOC(p, X_Appearance); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Appearance); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Arc2D Node deletion +*/ + +static void Arc2D_Del(GF_Node *node) +{ + X_Arc2D *p = (X_Arc2D *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Arc2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err Arc2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "endAngle"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Arc2D *) node)->endAngle; + return GF_OK; + case 1: + info->name = "radius"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Arc2D *) node)->radius; + return GF_OK; + case 2: + info->name = "startAngle"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Arc2D *) node)->startAngle; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Arc2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Arc2D_get_field_index_by_name(char *name) +{ + if (!strcmp("endAngle", name)) return 0; + if (!strcmp("radius", name)) return 1; + if (!strcmp("startAngle", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *Arc2D_Create() +{ + X_Arc2D *p; + GF_SAFEALLOC(p, X_Arc2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Arc2D); + + /*default field values*/ + p->endAngle = FLT2FIX(1.5707963); + p->radius = FLT2FIX(1); + p->startAngle = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + ArcClose2D Node deletion +*/ + +static void ArcClose2D_Del(GF_Node *node) +{ + X_ArcClose2D *p = (X_ArcClose2D *) node; + gf_sg_sfstring_del(p->closureType); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 ArcClose2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err ArcClose2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "closureType"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_ArcClose2D *) node)->closureType; + return GF_OK; + case 1: + info->name = "endAngle"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_ArcClose2D *) node)->endAngle; + return GF_OK; + case 2: + info->name = "radius"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_ArcClose2D *) node)->radius; + return GF_OK; + case 3: + info->name = "startAngle"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_ArcClose2D *) node)->startAngle; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_ArcClose2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ArcClose2D_get_field_index_by_name(char *name) +{ + if (!strcmp("closureType", name)) return 0; + if (!strcmp("endAngle", name)) return 1; + if (!strcmp("radius", name)) return 2; + if (!strcmp("startAngle", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *ArcClose2D_Create() +{ + X_ArcClose2D *p; + GF_SAFEALLOC(p, X_ArcClose2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_ArcClose2D); + + /*default field values*/ + p->closureType.buffer = (char*) malloc(sizeof(char) * 4); + strcpy(p->closureType.buffer, "PIE"); + p->endAngle = FLT2FIX(1.5707963); + p->radius = FLT2FIX(1); + p->startAngle = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + AudioClip Node deletion +*/ + +static void AudioClip_Del(GF_Node *node) +{ + X_AudioClip *p = (X_AudioClip *) node; + gf_sg_sfstring_del(p->description); + gf_sg_mfurl_del(p->url); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 AudioClip_get_field_count(GF_Node *node, u8 dummy) +{ + return 13; +} + +static GF_Err AudioClip_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "description"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_AudioClip *) node)->description; + return GF_OK; + case 1: + info->name = "loop"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_AudioClip *) node)->loop; + return GF_OK; + case 2: + info->name = "pitch"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_AudioClip *) node)->pitch; + return GF_OK; + case 3: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_AudioClip *) node)->startTime; + return GF_OK; + case 4: + info->name = "stopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_AudioClip *) node)->stopTime; + return GF_OK; + case 5: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_AudioClip *) node)->url; + return GF_OK; + case 6: + info->name = "duration_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_AudioClip *) node)->duration_changed; + return GF_OK; + case 7: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_AudioClip *) node)->isActive; + return GF_OK; + case 8: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_AudioClip *)node)->metadata; + return GF_OK; + case 9: + info->name = "pauseTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_AudioClip *) node)->pauseTime; + return GF_OK; + case 10: + info->name = "resumeTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_AudioClip *) node)->resumeTime; + return GF_OK; + case 11: + info->name = "elapsedTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_AudioClip *) node)->elapsedTime; + return GF_OK; + case 12: + info->name = "isPaused"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_AudioClip *) node)->isPaused; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 AudioClip_get_field_index_by_name(char *name) +{ + if (!strcmp("description", name)) return 0; + if (!strcmp("loop", name)) return 1; + if (!strcmp("pitch", name)) return 2; + if (!strcmp("startTime", name)) return 3; + if (!strcmp("stopTime", name)) return 4; + if (!strcmp("url", name)) return 5; + if (!strcmp("duration_changed", name)) return 6; + if (!strcmp("isActive", name)) return 7; + if (!strcmp("metadata", name)) return 8; + if (!strcmp("pauseTime", name)) return 9; + if (!strcmp("resumeTime", name)) return 10; + if (!strcmp("elapsedTime", name)) return 11; + if (!strcmp("isPaused", name)) return 12; + return -1; + } + + +static GF_Node *AudioClip_Create() +{ + X_AudioClip *p; + GF_SAFEALLOC(p, X_AudioClip); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_AudioClip); + + /*default field values*/ + p->pitch = FLT2FIX(1.0); + p->startTime = 0; + p->stopTime = 0; + p->pauseTime = 0; + p->resumeTime = 0; + return (GF_Node *)p; +} + + +/* + Background Node deletion +*/ + +static void Background_Del(GF_Node *node) +{ + X_Background *p = (X_Background *) node; + gf_sg_mffloat_del(p->groundAngle); + gf_sg_mfcolor_del(p->groundColor); + gf_sg_mfurl_del(p->backUrl); + gf_sg_mfurl_del(p->bottomUrl); + gf_sg_mfurl_del(p->frontUrl); + gf_sg_mfurl_del(p->leftUrl); + gf_sg_mfurl_del(p->rightUrl); + gf_sg_mfurl_del(p->topUrl); + gf_sg_mffloat_del(p->skyAngle); + gf_sg_mfcolor_del(p->skyColor); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Background_get_field_count(GF_Node *node, u8 dummy) +{ + return 14; +} + +static GF_Err Background_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Background *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Background *) node)->set_bind; + return GF_OK; + case 1: + info->name = "groundAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_Background *) node)->groundAngle; + return GF_OK; + case 2: + info->name = "groundColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((X_Background *) node)->groundColor; + return GF_OK; + case 3: + info->name = "backUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_Background *) node)->backUrl; + return GF_OK; + case 4: + info->name = "bottomUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_Background *) node)->bottomUrl; + return GF_OK; + case 5: + info->name = "frontUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_Background *) node)->frontUrl; + return GF_OK; + case 6: + info->name = "leftUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_Background *) node)->leftUrl; + return GF_OK; + case 7: + info->name = "rightUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_Background *) node)->rightUrl; + return GF_OK; + case 8: + info->name = "topUrl"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_Background *) node)->topUrl; + return GF_OK; + case 9: + info->name = "skyAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_Background *) node)->skyAngle; + return GF_OK; + case 10: + info->name = "skyColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((X_Background *) node)->skyColor; + return GF_OK; + case 11: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Background *) node)->isBound; + return GF_OK; + case 12: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Background *)node)->metadata; + return GF_OK; + case 13: + info->name = "bindTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_Background *) node)->bindTime; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Background_get_field_index_by_name(char *name) +{ + if (!strcmp("set_bind", name)) return 0; + if (!strcmp("groundAngle", name)) return 1; + if (!strcmp("groundColor", name)) return 2; + if (!strcmp("backUrl", name)) return 3; + if (!strcmp("bottomUrl", name)) return 4; + if (!strcmp("frontUrl", name)) return 5; + if (!strcmp("leftUrl", name)) return 6; + if (!strcmp("rightUrl", name)) return 7; + if (!strcmp("topUrl", name)) return 8; + if (!strcmp("skyAngle", name)) return 9; + if (!strcmp("skyColor", name)) return 10; + if (!strcmp("isBound", name)) return 11; + if (!strcmp("metadata", name)) return 12; + if (!strcmp("bindTime", name)) return 13; + return -1; + } + + +static GF_Node *Background_Create() +{ + X_Background *p; + GF_SAFEALLOC(p, X_Background); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Background); + + /*default field values*/ + p->skyColor.vals = (SFColor*)malloc(sizeof(SFColor)*1); + p->skyColor.count = 1; + p->skyColor.vals[0].red = FLT2FIX(0); + p->skyColor.vals[0].green = FLT2FIX(0); + p->skyColor.vals[0].blue = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Billboard Node deletion +*/ + +static void Billboard_Del(GF_Node *node) +{ + X_Billboard *p = (X_Billboard *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_vrml_parent_destroy(node); + gf_node_free((GF_Node *)p); +} + + +static u32 Billboard_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err Billboard_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Billboard *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Billboard *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Billboard *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Billboard *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Billboard *)node)->children; + return GF_OK; + case 3: + info->name = "axisOfRotation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_Billboard *) node)->axisOfRotation; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Billboard *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Billboard_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("axisOfRotation", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *Billboard_Create() +{ + X_Billboard *p; + GF_SAFEALLOC(p, X_Billboard); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Billboard); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->axisOfRotation.x = FLT2FIX(0); + p->axisOfRotation.y = FLT2FIX(1); + p->axisOfRotation.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + BooleanFilter Node deletion +*/ + +static void BooleanFilter_Del(GF_Node *node) +{ + X_BooleanFilter *p = (X_BooleanFilter *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 BooleanFilter_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err BooleanFilter_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_boolean"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_BooleanFilter *)node)->on_set_boolean; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_BooleanFilter *) node)->set_boolean; + return GF_OK; + case 1: + info->name = "inputFalse"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_BooleanFilter *) node)->inputFalse; + return GF_OK; + case 2: + info->name = "inputNegate"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_BooleanFilter *) node)->inputNegate; + return GF_OK; + case 3: + info->name = "inputTrue"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_BooleanFilter *) node)->inputTrue; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_BooleanFilter *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 BooleanFilter_get_field_index_by_name(char *name) +{ + if (!strcmp("set_boolean", name)) return 0; + if (!strcmp("inputFalse", name)) return 1; + if (!strcmp("inputNegate", name)) return 2; + if (!strcmp("inputTrue", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *BooleanFilter_Create() +{ + X_BooleanFilter *p; + GF_SAFEALLOC(p, X_BooleanFilter); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_BooleanFilter); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + BooleanSequencer Node deletion +*/ + +static void BooleanSequencer_Del(GF_Node *node) +{ + X_BooleanSequencer *p = (X_BooleanSequencer *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfbool_del(p->keyValue); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 BooleanSequencer_get_field_count(GF_Node *node, u8 dummy) +{ + return 7; +} + +static GF_Err BooleanSequencer_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "next"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_BooleanSequencer *)node)->on_next; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_BooleanSequencer *) node)->next; + return GF_OK; + case 1: + info->name = "previous"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_BooleanSequencer *)node)->on_previous; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_BooleanSequencer *) node)->previous; + return GF_OK; + case 2: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_BooleanSequencer *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_BooleanSequencer *) node)->set_fraction; + return GF_OK; + case 3: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_BooleanSequencer *) node)->key; + return GF_OK; + case 4: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFBOOL; + info->far_ptr = & ((X_BooleanSequencer *) node)->keyValue; + return GF_OK; + case 5: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_BooleanSequencer *) node)->value_changed; + return GF_OK; + case 6: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_BooleanSequencer *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 BooleanSequencer_get_field_index_by_name(char *name) +{ + if (!strcmp("next", name)) return 0; + if (!strcmp("previous", name)) return 1; + if (!strcmp("set_fraction", name)) return 2; + if (!strcmp("key", name)) return 3; + if (!strcmp("keyValue", name)) return 4; + if (!strcmp("value_changed", name)) return 5; + if (!strcmp("metadata", name)) return 6; + return -1; + } + + +static GF_Node *BooleanSequencer_Create() +{ + X_BooleanSequencer *p; + GF_SAFEALLOC(p, X_BooleanSequencer); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_BooleanSequencer); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + BooleanToggle Node deletion +*/ + +static void BooleanToggle_Del(GF_Node *node) +{ + X_BooleanToggle *p = (X_BooleanToggle *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 BooleanToggle_get_field_count(GF_Node *node, u8 dummy) +{ + return 3; +} + +static GF_Err BooleanToggle_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_boolean"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_BooleanToggle *)node)->on_set_boolean; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_BooleanToggle *) node)->set_boolean; + return GF_OK; + case 1: + info->name = "toggle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_BooleanToggle *) node)->toggle; + return GF_OK; + case 2: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_BooleanToggle *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 BooleanToggle_get_field_index_by_name(char *name) +{ + if (!strcmp("set_boolean", name)) return 0; + if (!strcmp("toggle", name)) return 1; + if (!strcmp("metadata", name)) return 2; + return -1; + } + + +static GF_Node *BooleanToggle_Create() +{ + X_BooleanToggle *p; + GF_SAFEALLOC(p, X_BooleanToggle); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_BooleanToggle); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + BooleanTrigger Node deletion +*/ + +static void BooleanTrigger_Del(GF_Node *node) +{ + X_BooleanTrigger *p = (X_BooleanTrigger *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 BooleanTrigger_get_field_count(GF_Node *node, u8 dummy) +{ + return 3; +} + +static GF_Err BooleanTrigger_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_triggerTime"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_BooleanTrigger *)node)->on_set_triggerTime; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_BooleanTrigger *) node)->set_triggerTime; + return GF_OK; + case 1: + info->name = "triggerTrue"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_BooleanTrigger *) node)->triggerTrue; + return GF_OK; + case 2: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_BooleanTrigger *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 BooleanTrigger_get_field_index_by_name(char *name) +{ + if (!strcmp("set_triggerTime", name)) return 0; + if (!strcmp("triggerTrue", name)) return 1; + if (!strcmp("metadata", name)) return 2; + return -1; + } + + +static GF_Node *BooleanTrigger_Create() +{ + X_BooleanTrigger *p; + GF_SAFEALLOC(p, X_BooleanTrigger); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_BooleanTrigger); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Box Node deletion +*/ + +static void Box_Del(GF_Node *node) +{ + X_Box *p = (X_Box *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Box_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err Box_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "size"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_Box *) node)->size; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Box *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Box_get_field_index_by_name(char *name) +{ + if (!strcmp("size", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *Box_Create() +{ + X_Box *p; + GF_SAFEALLOC(p, X_Box); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Box); + + /*default field values*/ + p->size.x = FLT2FIX(2); + p->size.y = FLT2FIX(2); + p->size.z = FLT2FIX(2); + return (GF_Node *)p; +} + + +/* + Circle2D Node deletion +*/ + +static void Circle2D_Del(GF_Node *node) +{ + X_Circle2D *p = (X_Circle2D *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Circle2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err Circle2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "radius"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Circle2D *) node)->radius; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Circle2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Circle2D_get_field_index_by_name(char *name) +{ + if (!strcmp("radius", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *Circle2D_Create() +{ + X_Circle2D *p; + GF_SAFEALLOC(p, X_Circle2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Circle2D); + + /*default field values*/ + p->radius = FLT2FIX(1); + return (GF_Node *)p; +} + + +/* + Collision Node deletion +*/ + +static void Collision_Del(GF_Node *node) +{ + X_Collision *p = (X_Collision *) node; + gf_node_unregister((GF_Node *) p->proxy, node); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_vrml_parent_destroy(node); + gf_node_free((GF_Node *)p); +} + + +static u32 Collision_get_field_count(GF_Node *node, u8 dummy) +{ + return 8; +} + +static GF_Err Collision_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Collision *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Collision *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Collision *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Collision *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Collision *)node)->children; + return GF_OK; + case 3: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Collision *) node)->enabled; + return GF_OK; + case 4: + info->name = "proxy"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Collision *)node)->proxy; + return GF_OK; + case 5: + info->name = "collideTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_Collision *) node)->collideTime; + return GF_OK; + case 6: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Collision *) node)->isActive; + return GF_OK; + case 7: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Collision *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Collision_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("enabled", name)) return 3; + if (!strcmp("proxy", name)) return 4; + if (!strcmp("collideTime", name)) return 5; + if (!strcmp("isActive", name)) return 6; + if (!strcmp("metadata", name)) return 7; + return -1; + } + + +static GF_Node *Collision_Create() +{ + X_Collision *p; + GF_SAFEALLOC(p, X_Collision); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Collision); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + Color Node deletion +*/ + +static void Color_Del(GF_Node *node) +{ + X_Color *p = (X_Color *) node; + gf_sg_mfcolor_del(p->color); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Color_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err Color_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((X_Color *) node)->color; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Color *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Color_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *Color_Create() +{ + X_Color *p; + GF_SAFEALLOC(p, X_Color); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Color); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + ColorInterpolator Node deletion +*/ + +static void ColorInterpolator_Del(GF_Node *node) +{ + X_ColorInterpolator *p = (X_ColorInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfcolor_del(p->keyValue); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 ColorInterpolator_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err ColorInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_ColorInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_ColorInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_ColorInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((X_ColorInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((X_ColorInterpolator *) node)->value_changed; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_ColorInterpolator *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ColorInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *ColorInterpolator_Create() +{ + X_ColorInterpolator *p; + GF_SAFEALLOC(p, X_ColorInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_ColorInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + ColorRGBA Node deletion +*/ + +static void ColorRGBA_Del(GF_Node *node) +{ + X_ColorRGBA *p = (X_ColorRGBA *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 ColorRGBA_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err ColorRGBA_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLORRGBA; + info->far_ptr = & ((X_ColorRGBA *) node)->color; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_ColorRGBA *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ColorRGBA_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *ColorRGBA_Create() +{ + X_ColorRGBA *p; + GF_SAFEALLOC(p, X_ColorRGBA); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_ColorRGBA); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Cone Node deletion +*/ + +static void Cone_Del(GF_Node *node) +{ + X_Cone *p = (X_Cone *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Cone_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err Cone_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "bottomRadius"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Cone *) node)->bottomRadius; + return GF_OK; + case 1: + info->name = "height"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Cone *) node)->height; + return GF_OK; + case 2: + info->name = "side"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Cone *) node)->side; + return GF_OK; + case 3: + info->name = "bottom"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Cone *) node)->bottom; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Cone *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Cone_get_field_index_by_name(char *name) +{ + if (!strcmp("bottomRadius", name)) return 0; + if (!strcmp("height", name)) return 1; + if (!strcmp("side", name)) return 2; + if (!strcmp("bottom", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *Cone_Create() +{ + X_Cone *p; + GF_SAFEALLOC(p, X_Cone); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Cone); + + /*default field values*/ + p->bottomRadius = FLT2FIX(1); + p->height = FLT2FIX(2); + p->side = 1; + p->bottom = 1; + return (GF_Node *)p; +} + + +/* + Contour2D Node deletion +*/ + +static void Contour2D_Del(GF_Node *node) +{ + X_Contour2D *p = (X_Contour2D *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_vrml_parent_destroy(node); + gf_node_free((GF_Node *)p); +} + + +static u32 Contour2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err Contour2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Contour2D *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFNurbsControlCurveNode; + info->far_ptr = & ((X_Contour2D *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Contour2D *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFNurbsControlCurveNode; + info->far_ptr = & ((X_Contour2D *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFNurbsControlCurveNode; + info->far_ptr = & ((X_Contour2D *)node)->children; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Contour2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Contour2D_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *Contour2D_Create() +{ + X_Contour2D *p; + GF_SAFEALLOC(p, X_Contour2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Contour2D); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + ContourPolyline2D Node deletion +*/ + +static void ContourPolyline2D_Del(GF_Node *node) +{ + X_ContourPolyline2D *p = (X_ContourPolyline2D *) node; + gf_sg_mfvec2f_del(p->point); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 ContourPolyline2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err ContourPolyline2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_ContourPolyline2D *) node)->point; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_ContourPolyline2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ContourPolyline2D_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *ContourPolyline2D_Create() +{ + X_ContourPolyline2D *p; + GF_SAFEALLOC(p, X_ContourPolyline2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_ContourPolyline2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Coordinate Node deletion +*/ + +static void Coordinate_Del(GF_Node *node) +{ + X_Coordinate *p = (X_Coordinate *) node; + gf_sg_mfvec3f_del(p->point); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Coordinate_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err Coordinate_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((X_Coordinate *) node)->point; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Coordinate *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Coordinate_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *Coordinate_Create() +{ + X_Coordinate *p; + GF_SAFEALLOC(p, X_Coordinate); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Coordinate); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + CoordinateDouble Node deletion +*/ + +static void CoordinateDouble_Del(GF_Node *node) +{ + X_CoordinateDouble *p = (X_CoordinateDouble *) node; + gf_sg_mfvec3d_del(p->point); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 CoordinateDouble_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err CoordinateDouble_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3D; + info->far_ptr = & ((X_CoordinateDouble *) node)->point; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_CoordinateDouble *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 CoordinateDouble_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *CoordinateDouble_Create() +{ + X_CoordinateDouble *p; + GF_SAFEALLOC(p, X_CoordinateDouble); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_CoordinateDouble); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Coordinate2D Node deletion +*/ + +static void Coordinate2D_Del(GF_Node *node) +{ + X_Coordinate2D *p = (X_Coordinate2D *) node; + gf_sg_mfvec2f_del(p->point); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Coordinate2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err Coordinate2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_Coordinate2D *) node)->point; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Coordinate2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Coordinate2D_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *Coordinate2D_Create() +{ + X_Coordinate2D *p; + GF_SAFEALLOC(p, X_Coordinate2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Coordinate2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + CoordinateInterpolator Node deletion +*/ + +static void CoordinateInterpolator_Del(GF_Node *node) +{ + X_CoordinateInterpolator *p = (X_CoordinateInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec3f_del(p->keyValue); + gf_sg_mfvec3f_del(p->value_changed); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 CoordinateInterpolator_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err CoordinateInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_CoordinateInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_CoordinateInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_CoordinateInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((X_CoordinateInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((X_CoordinateInterpolator *) node)->value_changed; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_CoordinateInterpolator *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 CoordinateInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *CoordinateInterpolator_Create() +{ + X_CoordinateInterpolator *p; + GF_SAFEALLOC(p, X_CoordinateInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_CoordinateInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + CoordinateInterpolator2D Node deletion +*/ + +static void CoordinateInterpolator2D_Del(GF_Node *node) +{ + X_CoordinateInterpolator2D *p = (X_CoordinateInterpolator2D *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec2f_del(p->keyValue); + gf_sg_mfvec2f_del(p->value_changed); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 CoordinateInterpolator2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err CoordinateInterpolator2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_CoordinateInterpolator2D *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_CoordinateInterpolator2D *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_CoordinateInterpolator2D *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_CoordinateInterpolator2D *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_CoordinateInterpolator2D *) node)->value_changed; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_CoordinateInterpolator2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 CoordinateInterpolator2D_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *CoordinateInterpolator2D_Create() +{ + X_CoordinateInterpolator2D *p; + GF_SAFEALLOC(p, X_CoordinateInterpolator2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_CoordinateInterpolator2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Cylinder Node deletion +*/ + +static void Cylinder_Del(GF_Node *node) +{ + X_Cylinder *p = (X_Cylinder *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Cylinder_get_field_count(GF_Node *node, u8 dummy) +{ + return 6; +} + +static GF_Err Cylinder_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "bottom"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Cylinder *) node)->bottom; + return GF_OK; + case 1: + info->name = "height"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Cylinder *) node)->height; + return GF_OK; + case 2: + info->name = "radius"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Cylinder *) node)->radius; + return GF_OK; + case 3: + info->name = "side"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Cylinder *) node)->side; + return GF_OK; + case 4: + info->name = "top"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Cylinder *) node)->top; + return GF_OK; + case 5: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Cylinder *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Cylinder_get_field_index_by_name(char *name) +{ + if (!strcmp("bottom", name)) return 0; + if (!strcmp("height", name)) return 1; + if (!strcmp("radius", name)) return 2; + if (!strcmp("side", name)) return 3; + if (!strcmp("top", name)) return 4; + if (!strcmp("metadata", name)) return 5; + return -1; + } + + +static GF_Node *Cylinder_Create() +{ + X_Cylinder *p; + GF_SAFEALLOC(p, X_Cylinder); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Cylinder); + + /*default field values*/ + p->bottom = 1; + p->height = FLT2FIX(2); + p->radius = FLT2FIX(1); + p->side = 1; + p->top = 1; + return (GF_Node *)p; +} + + +/* + CylinderSensor Node deletion +*/ + +static void CylinderSensor_Del(GF_Node *node) +{ + X_CylinderSensor *p = (X_CylinderSensor *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_sfstring_del(p->description); + gf_node_free((GF_Node *)p); +} + + +static u32 CylinderSensor_get_field_count(GF_Node *node, u8 dummy) +{ + return 12; +} + +static GF_Err CylinderSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "autoOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_CylinderSensor *) node)->autoOffset; + return GF_OK; + case 1: + info->name = "diskAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_CylinderSensor *) node)->diskAngle; + return GF_OK; + case 2: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_CylinderSensor *) node)->enabled; + return GF_OK; + case 3: + info->name = "maxAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_CylinderSensor *) node)->maxAngle; + return GF_OK; + case 4: + info->name = "minAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_CylinderSensor *) node)->minAngle; + return GF_OK; + case 5: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_CylinderSensor *) node)->offset; + return GF_OK; + case 6: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_CylinderSensor *) node)->isActive; + return GF_OK; + case 7: + info->name = "rotation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((X_CylinderSensor *) node)->rotation_changed; + return GF_OK; + case 8: + info->name = "trackPoint_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_CylinderSensor *) node)->trackPoint_changed; + return GF_OK; + case 9: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_CylinderSensor *)node)->metadata; + return GF_OK; + case 10: + info->name = "description"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_CylinderSensor *) node)->description; + return GF_OK; + case 11: + info->name = "isOver"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_CylinderSensor *) node)->isOver; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 CylinderSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("autoOffset", name)) return 0; + if (!strcmp("diskAngle", name)) return 1; + if (!strcmp("enabled", name)) return 2; + if (!strcmp("maxAngle", name)) return 3; + if (!strcmp("minAngle", name)) return 4; + if (!strcmp("offset", name)) return 5; + if (!strcmp("isActive", name)) return 6; + if (!strcmp("rotation_changed", name)) return 7; + if (!strcmp("trackPoint_changed", name)) return 8; + if (!strcmp("metadata", name)) return 9; + if (!strcmp("description", name)) return 10; + if (!strcmp("isOver", name)) return 11; + return -1; + } + + +static GF_Node *CylinderSensor_Create() +{ + X_CylinderSensor *p; + GF_SAFEALLOC(p, X_CylinderSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_CylinderSensor); + + /*default field values*/ + p->autoOffset = 1; + p->diskAngle = FLT2FIX(0.2617); + p->enabled = 1; + p->maxAngle = FLT2FIX(-1); + p->minAngle = FLT2FIX(0); + p->offset = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + DirectionalLight Node deletion +*/ + +static void DirectionalLight_Del(GF_Node *node) +{ + X_DirectionalLight *p = (X_DirectionalLight *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 DirectionalLight_get_field_count(GF_Node *node, u8 dummy) +{ + return 6; +} + +static GF_Err DirectionalLight_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "ambientIntensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_DirectionalLight *) node)->ambientIntensity; + return GF_OK; + case 1: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((X_DirectionalLight *) node)->color; + return GF_OK; + case 2: + info->name = "direction"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_DirectionalLight *) node)->direction; + return GF_OK; + case 3: + info->name = "intensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_DirectionalLight *) node)->intensity; + return GF_OK; + case 4: + info->name = "on"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_DirectionalLight *) node)->on; + return GF_OK; + case 5: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_DirectionalLight *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 DirectionalLight_get_field_index_by_name(char *name) +{ + if (!strcmp("ambientIntensity", name)) return 0; + if (!strcmp("color", name)) return 1; + if (!strcmp("direction", name)) return 2; + if (!strcmp("intensity", name)) return 3; + if (!strcmp("on", name)) return 4; + if (!strcmp("metadata", name)) return 5; + return -1; + } + + +static GF_Node *DirectionalLight_Create() +{ + X_DirectionalLight *p; + GF_SAFEALLOC(p, X_DirectionalLight); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_DirectionalLight); + + /*default field values*/ + p->ambientIntensity = FLT2FIX(0); + p->color.red = FLT2FIX(1); + p->color.green = FLT2FIX(1); + p->color.blue = FLT2FIX(1); + p->direction.x = FLT2FIX(0); + p->direction.y = FLT2FIX(0); + p->direction.z = FLT2FIX(-1); + p->intensity = FLT2FIX(1); + p->on = 1; + return (GF_Node *)p; +} + + +/* + Disk2D Node deletion +*/ + +static void Disk2D_Del(GF_Node *node) +{ + X_Disk2D *p = (X_Disk2D *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Disk2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 3; +} + +static GF_Err Disk2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "innerRadius"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Disk2D *) node)->innerRadius; + return GF_OK; + case 1: + info->name = "outerRadius"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Disk2D *) node)->outerRadius; + return GF_OK; + case 2: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Disk2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Disk2D_get_field_index_by_name(char *name) +{ + if (!strcmp("innerRadius", name)) return 0; + if (!strcmp("outerRadius", name)) return 1; + if (!strcmp("metadata", name)) return 2; + return -1; + } + + +static GF_Node *Disk2D_Create() +{ + X_Disk2D *p; + GF_SAFEALLOC(p, X_Disk2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Disk2D); + + /*default field values*/ + p->innerRadius = FLT2FIX(0); + p->outerRadius = FLT2FIX(1); + return (GF_Node *)p; +} + + +/* + ElevationGrid Node deletion +*/ + +static void ElevationGrid_Del(GF_Node *node) +{ + X_ElevationGrid *p = (X_ElevationGrid *) node; + gf_sg_mffloat_del(p->set_height); + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->normal, node); + gf_node_unregister((GF_Node *) p->texCoord, node); + gf_sg_mffloat_del(p->height); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 ElevationGrid_get_field_count(GF_Node *node, u8 dummy) +{ + return 15; +} + +static GF_Err ElevationGrid_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_height"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_ElevationGrid *)node)->on_set_height; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_ElevationGrid *) node)->set_height; + return GF_OK; + case 1: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_ElevationGrid *)node)->color; + return GF_OK; + case 2: + info->name = "normal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNormalNode; + info->far_ptr = & ((X_ElevationGrid *)node)->normal; + return GF_OK; + case 3: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((X_ElevationGrid *)node)->texCoord; + return GF_OK; + case 4: + info->name = "height"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_ElevationGrid *) node)->height; + return GF_OK; + case 5: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_ElevationGrid *) node)->ccw; + return GF_OK; + case 6: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_ElevationGrid *) node)->colorPerVertex; + return GF_OK; + case 7: + info->name = "creaseAngle"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_ElevationGrid *) node)->creaseAngle; + return GF_OK; + case 8: + info->name = "normalPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_ElevationGrid *) node)->normalPerVertex; + return GF_OK; + case 9: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_ElevationGrid *) node)->solid; + return GF_OK; + case 10: + info->name = "xDimension"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((X_ElevationGrid *) node)->xDimension; + return GF_OK; + case 11: + info->name = "xSpacing"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_ElevationGrid *) node)->xSpacing; + return GF_OK; + case 12: + info->name = "zDimension"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((X_ElevationGrid *) node)->zDimension; + return GF_OK; + case 13: + info->name = "zSpacing"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_ElevationGrid *) node)->zSpacing; + return GF_OK; + case 14: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_ElevationGrid *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ElevationGrid_get_field_index_by_name(char *name) +{ + if (!strcmp("set_height", name)) return 0; + if (!strcmp("color", name)) return 1; + if (!strcmp("normal", name)) return 2; + if (!strcmp("texCoord", name)) return 3; + if (!strcmp("height", name)) return 4; + if (!strcmp("ccw", name)) return 5; + if (!strcmp("colorPerVertex", name)) return 6; + if (!strcmp("creaseAngle", name)) return 7; + if (!strcmp("normalPerVertex", name)) return 8; + if (!strcmp("solid", name)) return 9; + if (!strcmp("xDimension", name)) return 10; + if (!strcmp("xSpacing", name)) return 11; + if (!strcmp("zDimension", name)) return 12; + if (!strcmp("zSpacing", name)) return 13; + if (!strcmp("metadata", name)) return 14; + return -1; + } + + +static GF_Node *ElevationGrid_Create() +{ + X_ElevationGrid *p; + GF_SAFEALLOC(p, X_ElevationGrid); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_ElevationGrid); + + /*default field values*/ + p->ccw = 1; + p->colorPerVertex = 1; + p->creaseAngle = FLT2FIX(0.0); + p->normalPerVertex = 1; + p->solid = 1; + p->xDimension = 0; + p->xSpacing = FLT2FIX(1.0); + p->zDimension = 0; + p->zSpacing = FLT2FIX(1.0); + return (GF_Node *)p; +} + + +/* + Extrusion Node deletion +*/ + +static void Extrusion_Del(GF_Node *node) +{ + X_Extrusion *p = (X_Extrusion *) node; + gf_sg_mfvec2f_del(p->set_crossSection); + gf_sg_mfrotation_del(p->set_orientation); + gf_sg_mfvec2f_del(p->set_scale); + gf_sg_mfvec3f_del(p->set_spine); + gf_sg_mfvec2f_del(p->crossSection); + gf_sg_mfrotation_del(p->orientation); + gf_sg_mfvec2f_del(p->scale); + gf_sg_mfvec3f_del(p->spine); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Extrusion_get_field_count(GF_Node *node, u8 dummy) +{ + return 15; +} + +static GF_Err Extrusion_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_crossSection"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Extrusion *)node)->on_set_crossSection; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_Extrusion *) node)->set_crossSection; + return GF_OK; + case 1: + info->name = "set_orientation"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Extrusion *)node)->on_set_orientation; + info->fieldType = GF_SG_VRML_MFROTATION; + info->far_ptr = & ((X_Extrusion *) node)->set_orientation; + return GF_OK; + case 2: + info->name = "set_scale"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Extrusion *)node)->on_set_scale; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_Extrusion *) node)->set_scale; + return GF_OK; + case 3: + info->name = "set_spine"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Extrusion *)node)->on_set_spine; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((X_Extrusion *) node)->set_spine; + return GF_OK; + case 4: + info->name = "beginCap"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Extrusion *) node)->beginCap; + return GF_OK; + case 5: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Extrusion *) node)->ccw; + return GF_OK; + case 6: + info->name = "convex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Extrusion *) node)->convex; + return GF_OK; + case 7: + info->name = "creaseAngle"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Extrusion *) node)->creaseAngle; + return GF_OK; + case 8: + info->name = "crossSection"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_Extrusion *) node)->crossSection; + return GF_OK; + case 9: + info->name = "endCap"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Extrusion *) node)->endCap; + return GF_OK; + case 10: + info->name = "orientation"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFROTATION; + info->far_ptr = & ((X_Extrusion *) node)->orientation; + return GF_OK; + case 11: + info->name = "scale"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_Extrusion *) node)->scale; + return GF_OK; + case 12: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Extrusion *) node)->solid; + return GF_OK; + case 13: + info->name = "spine"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((X_Extrusion *) node)->spine; + return GF_OK; + case 14: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Extrusion *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Extrusion_get_field_index_by_name(char *name) +{ + if (!strcmp("set_crossSection", name)) return 0; + if (!strcmp("set_orientation", name)) return 1; + if (!strcmp("set_scale", name)) return 2; + if (!strcmp("set_spine", name)) return 3; + if (!strcmp("beginCap", name)) return 4; + if (!strcmp("ccw", name)) return 5; + if (!strcmp("convex", name)) return 6; + if (!strcmp("creaseAngle", name)) return 7; + if (!strcmp("crossSection", name)) return 8; + if (!strcmp("endCap", name)) return 9; + if (!strcmp("orientation", name)) return 10; + if (!strcmp("scale", name)) return 11; + if (!strcmp("solid", name)) return 12; + if (!strcmp("spine", name)) return 13; + if (!strcmp("metadata", name)) return 14; + return -1; + } + + +static GF_Node *Extrusion_Create() +{ + X_Extrusion *p; + GF_SAFEALLOC(p, X_Extrusion); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Extrusion); + + /*default field values*/ + p->beginCap = 1; + p->ccw = 1; + p->convex = 1; + p->creaseAngle = FLT2FIX(0.0); + p->crossSection.vals = (SFVec2f*) malloc(sizeof(SFVec2f)*5); + p->crossSection.count = 5; + p->crossSection.vals[0].x = FLT2FIX(1); + p->crossSection.vals[0].y = FLT2FIX(1); + p->crossSection.vals[1].x = FLT2FIX(1); + p->crossSection.vals[1].y = FLT2FIX(-1); + p->crossSection.vals[2].x = FLT2FIX(-1); + p->crossSection.vals[2].y = FLT2FIX(-1); + p->crossSection.vals[3].x = FLT2FIX(-1); + p->crossSection.vals[3].y = FLT2FIX(1); + p->crossSection.vals[4].x = FLT2FIX(1); + p->crossSection.vals[4].y = FLT2FIX(1); + p->endCap = 1; + p->orientation.vals = (GF_Vec4*)malloc(sizeof(GF_Vec4)*1); + p->orientation.count = 1; + p->orientation.vals[0].x = FLT2FIX(0); + p->orientation.vals[0].y = FLT2FIX(0); + p->orientation.vals[0].z = FLT2FIX(1); + p->orientation.vals[0].q = FLT2FIX(0); + p->scale.vals = (SFVec2f*) malloc(sizeof(SFVec2f)*1); + p->scale.count = 1; + p->scale.vals[0].x = FLT2FIX(1); + p->scale.vals[0].y = FLT2FIX(1); + p->solid = 1; + p->spine.vals = (SFVec3f*)malloc(sizeof(SFVec3f)*2); + p->spine.count = 2; + p->spine.vals[0].x = FLT2FIX(0); + p->spine.vals[0].y = FLT2FIX(0); + p->spine.vals[0].z = FLT2FIX(0); + p->spine.vals[1].x = FLT2FIX(0); + p->spine.vals[1].y = FLT2FIX(1); + p->spine.vals[1].z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + FillProperties Node deletion +*/ + +static void FillProperties_Del(GF_Node *node) +{ + X_FillProperties *p = (X_FillProperties *) node; + gf_node_free((GF_Node *)p); +} + + +static u32 FillProperties_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err FillProperties_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "filled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_FillProperties *) node)->filled; + return GF_OK; + case 1: + info->name = "hatchColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((X_FillProperties *) node)->hatchColor; + return GF_OK; + case 2: + info->name = "hatched"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_FillProperties *) node)->hatched; + return GF_OK; + case 3: + info->name = "hatchStyle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((X_FillProperties *) node)->hatchStyle; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 FillProperties_get_field_index_by_name(char *name) +{ + if (!strcmp("filled", name)) return 0; + if (!strcmp("hatchColor", name)) return 1; + if (!strcmp("hatched", name)) return 2; + if (!strcmp("hatchStyle", name)) return 3; + return -1; + } + + +static GF_Node *FillProperties_Create() +{ + X_FillProperties *p; + GF_SAFEALLOC(p, X_FillProperties); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_FillProperties); + + /*default field values*/ + p->filled = 1; + p->hatchColor.red = FLT2FIX(1); + p->hatchColor.green = FLT2FIX(1); + p->hatchColor.blue = FLT2FIX(1); + p->hatched = 1; + p->hatchStyle = 1; + return (GF_Node *)p; +} + + +/* + Fog Node deletion +*/ + +static void Fog_Del(GF_Node *node) +{ + X_Fog *p = (X_Fog *) node; + gf_sg_sfstring_del(p->fogType); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Fog_get_field_count(GF_Node *node, u8 dummy) +{ + return 7; +} + +static GF_Err Fog_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((X_Fog *) node)->color; + return GF_OK; + case 1: + info->name = "fogType"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_Fog *) node)->fogType; + return GF_OK; + case 2: + info->name = "visibilityRange"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Fog *) node)->visibilityRange; + return GF_OK; + case 3: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Fog *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Fog *) node)->set_bind; + return GF_OK; + case 4: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Fog *) node)->isBound; + return GF_OK; + case 5: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Fog *)node)->metadata; + return GF_OK; + case 6: + info->name = "bindTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_Fog *) node)->bindTime; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Fog_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("fogType", name)) return 1; + if (!strcmp("visibilityRange", name)) return 2; + if (!strcmp("set_bind", name)) return 3; + if (!strcmp("isBound", name)) return 4; + if (!strcmp("metadata", name)) return 5; + if (!strcmp("bindTime", name)) return 6; + return -1; + } + + +static GF_Node *Fog_Create() +{ + X_Fog *p; + GF_SAFEALLOC(p, X_Fog); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Fog); + + /*default field values*/ + p->color.red = FLT2FIX(1); + p->color.green = FLT2FIX(1); + p->color.blue = FLT2FIX(1); + p->fogType.buffer = (char*) malloc(sizeof(char) * 7); + strcpy(p->fogType.buffer, "LINEAR"); + p->visibilityRange = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + FontStyle Node deletion +*/ + +static void FontStyle_Del(GF_Node *node) +{ + X_FontStyle *p = (X_FontStyle *) node; + gf_sg_mfstring_del(p->family); + gf_sg_mfstring_del(p->justify); + gf_sg_sfstring_del(p->language); + gf_sg_sfstring_del(p->style); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 FontStyle_get_field_count(GF_Node *node, u8 dummy) +{ + return 10; +} + +static GF_Err FontStyle_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "family"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_FontStyle *) node)->family; + return GF_OK; + case 1: + info->name = "horizontal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_FontStyle *) node)->horizontal; + return GF_OK; + case 2: + info->name = "justify"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_FontStyle *) node)->justify; + return GF_OK; + case 3: + info->name = "language"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_FontStyle *) node)->language; + return GF_OK; + case 4: + info->name = "leftToRight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_FontStyle *) node)->leftToRight; + return GF_OK; + case 5: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_FontStyle *) node)->size; + return GF_OK; + case 6: + info->name = "spacing"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_FontStyle *) node)->spacing; + return GF_OK; + case 7: + info->name = "style"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_FontStyle *) node)->style; + return GF_OK; + case 8: + info->name = "topToBottom"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_FontStyle *) node)->topToBottom; + return GF_OK; + case 9: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_FontStyle *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 FontStyle_get_field_index_by_name(char *name) +{ + if (!strcmp("family", name)) return 0; + if (!strcmp("horizontal", name)) return 1; + if (!strcmp("justify", name)) return 2; + if (!strcmp("language", name)) return 3; + if (!strcmp("leftToRight", name)) return 4; + if (!strcmp("size", name)) return 5; + if (!strcmp("spacing", name)) return 6; + if (!strcmp("style", name)) return 7; + if (!strcmp("topToBottom", name)) return 8; + if (!strcmp("metadata", name)) return 9; + return -1; + } + + +static GF_Node *FontStyle_Create() +{ + X_FontStyle *p; + GF_SAFEALLOC(p, X_FontStyle); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_FontStyle); + + /*default field values*/ + p->family.vals = (char**)malloc(sizeof(SFString)*1); + p->family.count = 1; + p->family.vals[0] = (char*)malloc(sizeof(char) * 6); + strcpy(p->family.vals[0], "SERIF"); + p->horizontal = 1; + p->justify.vals = (char**)malloc(sizeof(SFString)*1); + p->justify.count = 1; + p->justify.vals[0] = (char*)malloc(sizeof(char) * 6); + strcpy(p->justify.vals[0], "BEGIN"); + p->leftToRight = 1; + p->size = FLT2FIX(1.0); + p->spacing = FLT2FIX(1.0); + p->style.buffer = (char*) malloc(sizeof(char) * 6); + strcpy(p->style.buffer, "PLAIN"); + p->topToBottom = 1; + return (GF_Node *)p; +} + + +/* + Group Node deletion +*/ + +static void Group_Del(GF_Node *node) +{ + X_Group *p = (X_Group *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_vrml_parent_destroy(node); + gf_node_free((GF_Node *)p); +} + + +static u32 Group_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err Group_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Group *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Group *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Group *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Group *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Group *)node)->children; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Group *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Group_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *Group_Create() +{ + X_Group *p; + GF_SAFEALLOC(p, X_Group); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Group); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + ImageTexture Node deletion +*/ + +static void ImageTexture_Del(GF_Node *node) +{ + X_ImageTexture *p = (X_ImageTexture *) node; + gf_sg_mfurl_del(p->url); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 ImageTexture_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err ImageTexture_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_ImageTexture *) node)->url; + return GF_OK; + case 1: + info->name = "repeatS"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_ImageTexture *) node)->repeatS; + return GF_OK; + case 2: + info->name = "repeatT"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_ImageTexture *) node)->repeatT; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_ImageTexture *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ImageTexture_get_field_index_by_name(char *name) +{ + if (!strcmp("url", name)) return 0; + if (!strcmp("repeatS", name)) return 1; + if (!strcmp("repeatT", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *ImageTexture_Create() +{ + X_ImageTexture *p; + GF_SAFEALLOC(p, X_ImageTexture); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_ImageTexture); + + /*default field values*/ + p->repeatS = 1; + p->repeatT = 1; + return (GF_Node *)p; +} + + +/* + IndexedFaceSet Node deletion +*/ + +static void IndexedFaceSet_Del(GF_Node *node) +{ + X_IndexedFaceSet *p = (X_IndexedFaceSet *) node; + gf_sg_mfint32_del(p->set_colorIndex); + gf_sg_mfint32_del(p->set_coordIndex); + gf_sg_mfint32_del(p->set_normalIndex); + gf_sg_mfint32_del(p->set_texCoordIndex); + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->coord, node); + gf_node_unregister((GF_Node *) p->normal, node); + gf_node_unregister((GF_Node *) p->texCoord, node); + gf_sg_mfint32_del(p->colorIndex); + gf_sg_mfint32_del(p->coordIndex); + gf_sg_mfint32_del(p->normalIndex); + gf_sg_mfint32_del(p->texCoordIndex); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 IndexedFaceSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 19; +} + +static GF_Err IndexedFaceSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_colorIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IndexedFaceSet *)node)->on_set_colorIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedFaceSet *) node)->set_colorIndex; + return GF_OK; + case 1: + info->name = "set_coordIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IndexedFaceSet *)node)->on_set_coordIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedFaceSet *) node)->set_coordIndex; + return GF_OK; + case 2: + info->name = "set_normalIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IndexedFaceSet *)node)->on_set_normalIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedFaceSet *) node)->set_normalIndex; + return GF_OK; + case 3: + info->name = "set_texCoordIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IndexedFaceSet *)node)->on_set_texCoordIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedFaceSet *) node)->set_texCoordIndex; + return GF_OK; + case 4: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_IndexedFaceSet *)node)->color; + return GF_OK; + case 5: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((X_IndexedFaceSet *)node)->coord; + return GF_OK; + case 6: + info->name = "normal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNormalNode; + info->far_ptr = & ((X_IndexedFaceSet *)node)->normal; + return GF_OK; + case 7: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((X_IndexedFaceSet *)node)->texCoord; + return GF_OK; + case 8: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedFaceSet *) node)->ccw; + return GF_OK; + case 9: + info->name = "colorIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedFaceSet *) node)->colorIndex; + return GF_OK; + case 10: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedFaceSet *) node)->colorPerVertex; + return GF_OK; + case 11: + info->name = "convex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedFaceSet *) node)->convex; + return GF_OK; + case 12: + info->name = "coordIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedFaceSet *) node)->coordIndex; + return GF_OK; + case 13: + info->name = "creaseAngle"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_IndexedFaceSet *) node)->creaseAngle; + return GF_OK; + case 14: + info->name = "normalIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedFaceSet *) node)->normalIndex; + return GF_OK; + case 15: + info->name = "normalPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedFaceSet *) node)->normalPerVertex; + return GF_OK; + case 16: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedFaceSet *) node)->solid; + return GF_OK; + case 17: + info->name = "texCoordIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedFaceSet *) node)->texCoordIndex; + return GF_OK; + case 18: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_IndexedFaceSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IndexedFaceSet_get_field_index_by_name(char *name) +{ + if (!strcmp("set_colorIndex", name)) return 0; + if (!strcmp("set_coordIndex", name)) return 1; + if (!strcmp("set_normalIndex", name)) return 2; + if (!strcmp("set_texCoordIndex", name)) return 3; + if (!strcmp("color", name)) return 4; + if (!strcmp("coord", name)) return 5; + if (!strcmp("normal", name)) return 6; + if (!strcmp("texCoord", name)) return 7; + if (!strcmp("ccw", name)) return 8; + if (!strcmp("colorIndex", name)) return 9; + if (!strcmp("colorPerVertex", name)) return 10; + if (!strcmp("convex", name)) return 11; + if (!strcmp("coordIndex", name)) return 12; + if (!strcmp("creaseAngle", name)) return 13; + if (!strcmp("normalIndex", name)) return 14; + if (!strcmp("normalPerVertex", name)) return 15; + if (!strcmp("solid", name)) return 16; + if (!strcmp("texCoordIndex", name)) return 17; + if (!strcmp("metadata", name)) return 18; + return -1; + } + + +static GF_Node *IndexedFaceSet_Create() +{ + X_IndexedFaceSet *p; + GF_SAFEALLOC(p, X_IndexedFaceSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_IndexedFaceSet); + + /*default field values*/ + p->ccw = 1; + p->colorPerVertex = 1; + p->convex = 1; + p->creaseAngle = FLT2FIX(0.0); + p->normalPerVertex = 1; + p->solid = 1; + return (GF_Node *)p; +} + + +/* + IndexedLineSet Node deletion +*/ + +static void IndexedLineSet_Del(GF_Node *node) +{ + X_IndexedLineSet *p = (X_IndexedLineSet *) node; + gf_sg_mfint32_del(p->set_colorIndex); + gf_sg_mfint32_del(p->set_coordIndex); + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->coord, node); + gf_sg_mfint32_del(p->colorIndex); + gf_sg_mfint32_del(p->coordIndex); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 IndexedLineSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 8; +} + +static GF_Err IndexedLineSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_colorIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IndexedLineSet *)node)->on_set_colorIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedLineSet *) node)->set_colorIndex; + return GF_OK; + case 1: + info->name = "set_coordIndex"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IndexedLineSet *)node)->on_set_coordIndex; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedLineSet *) node)->set_coordIndex; + return GF_OK; + case 2: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_IndexedLineSet *)node)->color; + return GF_OK; + case 3: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((X_IndexedLineSet *)node)->coord; + return GF_OK; + case 4: + info->name = "colorIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedLineSet *) node)->colorIndex; + return GF_OK; + case 5: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedLineSet *) node)->colorPerVertex; + return GF_OK; + case 6: + info->name = "coordIndex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedLineSet *) node)->coordIndex; + return GF_OK; + case 7: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_IndexedLineSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IndexedLineSet_get_field_index_by_name(char *name) +{ + if (!strcmp("set_colorIndex", name)) return 0; + if (!strcmp("set_coordIndex", name)) return 1; + if (!strcmp("color", name)) return 2; + if (!strcmp("coord", name)) return 3; + if (!strcmp("colorIndex", name)) return 4; + if (!strcmp("colorPerVertex", name)) return 5; + if (!strcmp("coordIndex", name)) return 6; + if (!strcmp("metadata", name)) return 7; + return -1; + } + + +static GF_Node *IndexedLineSet_Create() +{ + X_IndexedLineSet *p; + GF_SAFEALLOC(p, X_IndexedLineSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_IndexedLineSet); + + /*default field values*/ + p->colorPerVertex = 1; + return (GF_Node *)p; +} + + +/* + IndexedTriangleFanSet Node deletion +*/ + +static void IndexedTriangleFanSet_Del(GF_Node *node) +{ + X_IndexedTriangleFanSet *p = (X_IndexedTriangleFanSet *) node; + gf_sg_mfint32_del(p->set_index); + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->coord, node); + gf_node_unregister((GF_Node *) p->normal, node); + gf_node_unregister((GF_Node *) p->texCoord, node); + gf_sg_mfint32_del(p->index); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 IndexedTriangleFanSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 11; +} + +static GF_Err IndexedTriangleFanSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_index"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IndexedTriangleFanSet *)node)->on_set_index; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedTriangleFanSet *) node)->set_index; + return GF_OK; + case 1: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_IndexedTriangleFanSet *)node)->color; + return GF_OK; + case 2: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((X_IndexedTriangleFanSet *)node)->coord; + return GF_OK; + case 3: + info->name = "normal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNormalNode; + info->far_ptr = & ((X_IndexedTriangleFanSet *)node)->normal; + return GF_OK; + case 4: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((X_IndexedTriangleFanSet *)node)->texCoord; + return GF_OK; + case 5: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleFanSet *) node)->ccw; + return GF_OK; + case 6: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleFanSet *) node)->colorPerVertex; + return GF_OK; + case 7: + info->name = "normalPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleFanSet *) node)->normalPerVertex; + return GF_OK; + case 8: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleFanSet *) node)->solid; + return GF_OK; + case 9: + info->name = "index"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedTriangleFanSet *) node)->index; + return GF_OK; + case 10: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_IndexedTriangleFanSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IndexedTriangleFanSet_get_field_index_by_name(char *name) +{ + if (!strcmp("set_index", name)) return 0; + if (!strcmp("color", name)) return 1; + if (!strcmp("coord", name)) return 2; + if (!strcmp("normal", name)) return 3; + if (!strcmp("texCoord", name)) return 4; + if (!strcmp("ccw", name)) return 5; + if (!strcmp("colorPerVertex", name)) return 6; + if (!strcmp("normalPerVertex", name)) return 7; + if (!strcmp("solid", name)) return 8; + if (!strcmp("index", name)) return 9; + if (!strcmp("metadata", name)) return 10; + return -1; + } + + +static GF_Node *IndexedTriangleFanSet_Create() +{ + X_IndexedTriangleFanSet *p; + GF_SAFEALLOC(p, X_IndexedTriangleFanSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_IndexedTriangleFanSet); + + /*default field values*/ + p->ccw = 1; + p->colorPerVertex = 1; + p->normalPerVertex = 1; + p->solid = 1; + return (GF_Node *)p; +} + + +/* + IndexedTriangleSet Node deletion +*/ + +static void IndexedTriangleSet_Del(GF_Node *node) +{ + X_IndexedTriangleSet *p = (X_IndexedTriangleSet *) node; + gf_sg_mfint32_del(p->set_index); + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->coord, node); + gf_node_unregister((GF_Node *) p->normal, node); + gf_node_unregister((GF_Node *) p->texCoord, node); + gf_sg_mfint32_del(p->index); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 IndexedTriangleSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 11; +} + +static GF_Err IndexedTriangleSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_index"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IndexedTriangleSet *)node)->on_set_index; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedTriangleSet *) node)->set_index; + return GF_OK; + case 1: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_IndexedTriangleSet *)node)->color; + return GF_OK; + case 2: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((X_IndexedTriangleSet *)node)->coord; + return GF_OK; + case 3: + info->name = "normal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNormalNode; + info->far_ptr = & ((X_IndexedTriangleSet *)node)->normal; + return GF_OK; + case 4: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((X_IndexedTriangleSet *)node)->texCoord; + return GF_OK; + case 5: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleSet *) node)->ccw; + return GF_OK; + case 6: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleSet *) node)->colorPerVertex; + return GF_OK; + case 7: + info->name = "normalPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleSet *) node)->normalPerVertex; + return GF_OK; + case 8: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleSet *) node)->solid; + return GF_OK; + case 9: + info->name = "index"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedTriangleSet *) node)->index; + return GF_OK; + case 10: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_IndexedTriangleSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IndexedTriangleSet_get_field_index_by_name(char *name) +{ + if (!strcmp("set_index", name)) return 0; + if (!strcmp("color", name)) return 1; + if (!strcmp("coord", name)) return 2; + if (!strcmp("normal", name)) return 3; + if (!strcmp("texCoord", name)) return 4; + if (!strcmp("ccw", name)) return 5; + if (!strcmp("colorPerVertex", name)) return 6; + if (!strcmp("normalPerVertex", name)) return 7; + if (!strcmp("solid", name)) return 8; + if (!strcmp("index", name)) return 9; + if (!strcmp("metadata", name)) return 10; + return -1; + } + + +static GF_Node *IndexedTriangleSet_Create() +{ + X_IndexedTriangleSet *p; + GF_SAFEALLOC(p, X_IndexedTriangleSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_IndexedTriangleSet); + + /*default field values*/ + p->ccw = 1; + p->colorPerVertex = 1; + p->normalPerVertex = 1; + p->solid = 1; + return (GF_Node *)p; +} + + +/* + IndexedTriangleStripSet Node deletion +*/ + +static void IndexedTriangleStripSet_Del(GF_Node *node) +{ + X_IndexedTriangleStripSet *p = (X_IndexedTriangleStripSet *) node; + gf_sg_mfint32_del(p->set_index); + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->coord, node); + gf_node_unregister((GF_Node *) p->normal, node); + gf_node_unregister((GF_Node *) p->texCoord, node); + gf_sg_mfint32_del(p->index); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 IndexedTriangleStripSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 11; +} + +static GF_Err IndexedTriangleStripSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_index"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IndexedTriangleStripSet *)node)->on_set_index; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedTriangleStripSet *) node)->set_index; + return GF_OK; + case 1: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_IndexedTriangleStripSet *)node)->color; + return GF_OK; + case 2: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((X_IndexedTriangleStripSet *)node)->coord; + return GF_OK; + case 3: + info->name = "creaseAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_IndexedTriangleStripSet *) node)->creaseAngle; + return GF_OK; + case 4: + info->name = "normal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNormalNode; + info->far_ptr = & ((X_IndexedTriangleStripSet *)node)->normal; + return GF_OK; + case 5: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((X_IndexedTriangleStripSet *)node)->texCoord; + return GF_OK; + case 6: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleStripSet *) node)->ccw; + return GF_OK; + case 7: + info->name = "normalPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleStripSet *) node)->normalPerVertex; + return GF_OK; + case 8: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IndexedTriangleStripSet *) node)->solid; + return GF_OK; + case 9: + info->name = "index"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IndexedTriangleStripSet *) node)->index; + return GF_OK; + case 10: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_IndexedTriangleStripSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IndexedTriangleStripSet_get_field_index_by_name(char *name) +{ + if (!strcmp("set_index", name)) return 0; + if (!strcmp("color", name)) return 1; + if (!strcmp("coord", name)) return 2; + if (!strcmp("creaseAngle", name)) return 3; + if (!strcmp("normal", name)) return 4; + if (!strcmp("texCoord", name)) return 5; + if (!strcmp("ccw", name)) return 6; + if (!strcmp("normalPerVertex", name)) return 7; + if (!strcmp("solid", name)) return 8; + if (!strcmp("index", name)) return 9; + if (!strcmp("metadata", name)) return 10; + return -1; + } + + +static GF_Node *IndexedTriangleStripSet_Create() +{ + X_IndexedTriangleStripSet *p; + GF_SAFEALLOC(p, X_IndexedTriangleStripSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_IndexedTriangleStripSet); + + /*default field values*/ + p->creaseAngle = FLT2FIX(0); + p->ccw = 1; + p->normalPerVertex = 1; + p->solid = 1; + return (GF_Node *)p; +} + + +/* + Inline Node deletion +*/ + +static void Inline_Del(GF_Node *node) +{ + X_Inline *p = (X_Inline *) node; + gf_sg_mfurl_del(p->url); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Inline_get_field_count(GF_Node *node, u8 dummy) +{ + return 3; +} + +static GF_Err Inline_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_Inline *) node)->url; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Inline *)node)->metadata; + return GF_OK; + case 2: + info->name = "load"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Inline *) node)->load; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Inline_get_field_index_by_name(char *name) +{ + if (!strcmp("url", name)) return 0; + if (!strcmp("metadata", name)) return 1; + if (!strcmp("load", name)) return 2; + return -1; + } + + +static GF_Node *Inline_Create() +{ + X_Inline *p; + GF_SAFEALLOC(p, X_Inline); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Inline); + + /*default field values*/ + p->load = 1; + return (GF_Node *)p; +} + + +/* + IntegerSequencer Node deletion +*/ + +static void IntegerSequencer_Del(GF_Node *node) +{ + X_IntegerSequencer *p = (X_IntegerSequencer *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfint32_del(p->keyValue); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 IntegerSequencer_get_field_count(GF_Node *node, u8 dummy) +{ + return 7; +} + +static GF_Err IntegerSequencer_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "next"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IntegerSequencer *)node)->on_next; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IntegerSequencer *) node)->next; + return GF_OK; + case 1: + info->name = "previous"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IntegerSequencer *)node)->on_previous; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IntegerSequencer *) node)->previous; + return GF_OK; + case 2: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IntegerSequencer *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_IntegerSequencer *) node)->set_fraction; + return GF_OK; + case 3: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_IntegerSequencer *) node)->key; + return GF_OK; + case 4: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_IntegerSequencer *) node)->keyValue; + return GF_OK; + case 5: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((X_IntegerSequencer *) node)->value_changed; + return GF_OK; + case 6: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_IntegerSequencer *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IntegerSequencer_get_field_index_by_name(char *name) +{ + if (!strcmp("next", name)) return 0; + if (!strcmp("previous", name)) return 1; + if (!strcmp("set_fraction", name)) return 2; + if (!strcmp("key", name)) return 3; + if (!strcmp("keyValue", name)) return 4; + if (!strcmp("value_changed", name)) return 5; + if (!strcmp("metadata", name)) return 6; + return -1; + } + + +static GF_Node *IntegerSequencer_Create() +{ + X_IntegerSequencer *p; + GF_SAFEALLOC(p, X_IntegerSequencer); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_IntegerSequencer); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + IntegerTrigger Node deletion +*/ + +static void IntegerTrigger_Del(GF_Node *node) +{ + X_IntegerTrigger *p = (X_IntegerTrigger *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 IntegerTrigger_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err IntegerTrigger_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_boolean"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_IntegerTrigger *)node)->on_set_boolean; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_IntegerTrigger *) node)->set_boolean; + return GF_OK; + case 1: + info->name = "integerKey"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((X_IntegerTrigger *) node)->integerKey; + return GF_OK; + case 2: + info->name = "triggerValue"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((X_IntegerTrigger *) node)->triggerValue; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_IntegerTrigger *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 IntegerTrigger_get_field_index_by_name(char *name) +{ + if (!strcmp("set_boolean", name)) return 0; + if (!strcmp("integerKey", name)) return 1; + if (!strcmp("triggerValue", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *IntegerTrigger_Create() +{ + X_IntegerTrigger *p; + GF_SAFEALLOC(p, X_IntegerTrigger); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_IntegerTrigger); + + /*default field values*/ + p->integerKey = -1; + return (GF_Node *)p; +} + + +/* + KeySensor Node deletion +*/ + +static void KeySensor_Del(GF_Node *node) +{ + X_KeySensor *p = (X_KeySensor *) node; + gf_sg_sfstring_del(p->keyPress); + gf_sg_sfstring_del(p->keyRelease); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 KeySensor_get_field_count(GF_Node *node, u8 dummy) +{ + return 10; +} + +static GF_Err KeySensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_KeySensor *) node)->enabled; + return GF_OK; + case 1: + info->name = "actionKeyPress"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((X_KeySensor *) node)->actionKeyPress; + return GF_OK; + case 2: + info->name = "actionKeyRelease"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((X_KeySensor *) node)->actionKeyRelease; + return GF_OK; + case 3: + info->name = "altKey"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_KeySensor *) node)->altKey; + return GF_OK; + case 4: + info->name = "controlKey"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_KeySensor *) node)->controlKey; + return GF_OK; + case 5: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_KeySensor *) node)->isActive; + return GF_OK; + case 6: + info->name = "keyPress"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_KeySensor *) node)->keyPress; + return GF_OK; + case 7: + info->name = "keyRelease"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_KeySensor *) node)->keyRelease; + return GF_OK; + case 8: + info->name = "shiftKey"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_KeySensor *) node)->shiftKey; + return GF_OK; + case 9: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_KeySensor *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 KeySensor_get_field_index_by_name(char *name) +{ + if (!strcmp("enabled", name)) return 0; + if (!strcmp("actionKeyPress", name)) return 1; + if (!strcmp("actionKeyRelease", name)) return 2; + if (!strcmp("altKey", name)) return 3; + if (!strcmp("controlKey", name)) return 4; + if (!strcmp("isActive", name)) return 5; + if (!strcmp("keyPress", name)) return 6; + if (!strcmp("keyRelease", name)) return 7; + if (!strcmp("shiftKey", name)) return 8; + if (!strcmp("metadata", name)) return 9; + return -1; + } + + +static GF_Node *KeySensor_Create() +{ + X_KeySensor *p; + GF_SAFEALLOC(p, X_KeySensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_KeySensor); + + /*default field values*/ + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + LineProperties Node deletion +*/ + +static void LineProperties_Del(GF_Node *node) +{ + X_LineProperties *p = (X_LineProperties *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 LineProperties_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err LineProperties_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "applied"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_LineProperties *) node)->applied; + return GF_OK; + case 1: + info->name = "linetype"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((X_LineProperties *) node)->linetype; + return GF_OK; + case 2: + info->name = "linewidthScaleFactor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_LineProperties *) node)->linewidthScaleFactor; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_LineProperties *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 LineProperties_get_field_index_by_name(char *name) +{ + if (!strcmp("applied", name)) return 0; + if (!strcmp("linetype", name)) return 1; + if (!strcmp("linewidthScaleFactor", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *LineProperties_Create() +{ + X_LineProperties *p; + GF_SAFEALLOC(p, X_LineProperties); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_LineProperties); + + /*default field values*/ + p->applied = 1; + p->linetype = 1; + p->linewidthScaleFactor = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + LineSet Node deletion +*/ + +static void LineSet_Del(GF_Node *node) +{ + X_LineSet *p = (X_LineSet *) node; + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->coord, node); + gf_sg_mfint32_del(p->vertexCount); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 LineSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err LineSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_LineSet *)node)->color; + return GF_OK; + case 1: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((X_LineSet *)node)->coord; + return GF_OK; + case 2: + info->name = "vertexCount"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_LineSet *) node)->vertexCount; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_LineSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 LineSet_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("coord", name)) return 1; + if (!strcmp("vertexCount", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *LineSet_Create() +{ + X_LineSet *p; + GF_SAFEALLOC(p, X_LineSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_LineSet); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + LOD Node deletion +*/ + +static void LOD_Del(GF_Node *node) +{ + X_LOD *p = (X_LOD *) node; + gf_sg_mffloat_del(p->range); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_vrml_parent_destroy(node); + gf_node_free((GF_Node *)p); +} + + +static u32 LOD_get_field_count(GF_Node *node, u8 dummy) +{ + return 6; +} + +static GF_Err LOD_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_LOD *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_LOD *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_LOD *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_LOD *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_LOD *)node)->children; + return GF_OK; + case 3: + info->name = "center"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_LOD *) node)->center; + return GF_OK; + case 4: + info->name = "range"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_LOD *) node)->range; + return GF_OK; + case 5: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_LOD *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 LOD_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("center", name)) return 3; + if (!strcmp("range", name)) return 4; + if (!strcmp("metadata", name)) return 5; + return -1; + } + + +static GF_Node *LOD_Create() +{ + X_LOD *p; + GF_SAFEALLOC(p, X_LOD); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_LOD); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->center.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + Material Node deletion +*/ + +static void Material_Del(GF_Node *node) +{ + X_Material *p = (X_Material *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Material_get_field_count(GF_Node *node, u8 dummy) +{ + return 7; +} + +static GF_Err Material_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "ambientIntensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Material *) node)->ambientIntensity; + return GF_OK; + case 1: + info->name = "diffuseColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((X_Material *) node)->diffuseColor; + return GF_OK; + case 2: + info->name = "emissiveColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((X_Material *) node)->emissiveColor; + return GF_OK; + case 3: + info->name = "shininess"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Material *) node)->shininess; + return GF_OK; + case 4: + info->name = "specularColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((X_Material *) node)->specularColor; + return GF_OK; + case 5: + info->name = "transparency"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Material *) node)->transparency; + return GF_OK; + case 6: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Material *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Material_get_field_index_by_name(char *name) +{ + if (!strcmp("ambientIntensity", name)) return 0; + if (!strcmp("diffuseColor", name)) return 1; + if (!strcmp("emissiveColor", name)) return 2; + if (!strcmp("shininess", name)) return 3; + if (!strcmp("specularColor", name)) return 4; + if (!strcmp("transparency", name)) return 5; + if (!strcmp("metadata", name)) return 6; + return -1; + } + + +static GF_Node *Material_Create() +{ + X_Material *p; + GF_SAFEALLOC(p, X_Material); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Material); + + /*default field values*/ + p->ambientIntensity = FLT2FIX(0.2); + p->diffuseColor.red = FLT2FIX(0.8); + p->diffuseColor.green = FLT2FIX(0.8); + p->diffuseColor.blue = FLT2FIX(0.8); + p->emissiveColor.red = FLT2FIX(0); + p->emissiveColor.green = FLT2FIX(0); + p->emissiveColor.blue = FLT2FIX(0); + p->shininess = FLT2FIX(0.2); + p->specularColor.red = FLT2FIX(0); + p->specularColor.green = FLT2FIX(0); + p->specularColor.blue = FLT2FIX(0); + p->transparency = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + MetadataDouble Node deletion +*/ + +static void MetadataDouble_Del(GF_Node *node) +{ + X_MetadataDouble *p = (X_MetadataDouble *) node; + gf_sg_sfstring_del(p->name); + gf_sg_sfstring_del(p->reference); + gf_sg_mfdouble_del(p->value); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 MetadataDouble_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err MetadataDouble_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "name"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_MetadataDouble *) node)->name; + return GF_OK; + case 1: + info->name = "reference"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_MetadataDouble *) node)->reference; + return GF_OK; + case 2: + info->name = "value"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFDOUBLE; + info->far_ptr = & ((X_MetadataDouble *) node)->value; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_MetadataDouble *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MetadataDouble_get_field_index_by_name(char *name) +{ + if (!strcmp("name", name)) return 0; + if (!strcmp("reference", name)) return 1; + if (!strcmp("value", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *MetadataDouble_Create() +{ + X_MetadataDouble *p; + GF_SAFEALLOC(p, X_MetadataDouble); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_MetadataDouble); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + MetadataFloat Node deletion +*/ + +static void MetadataFloat_Del(GF_Node *node) +{ + X_MetadataFloat *p = (X_MetadataFloat *) node; + gf_sg_sfstring_del(p->name); + gf_sg_sfstring_del(p->reference); + gf_sg_mffloat_del(p->value); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 MetadataFloat_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err MetadataFloat_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "name"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_MetadataFloat *) node)->name; + return GF_OK; + case 1: + info->name = "reference"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_MetadataFloat *) node)->reference; + return GF_OK; + case 2: + info->name = "value"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_MetadataFloat *) node)->value; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_MetadataFloat *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MetadataFloat_get_field_index_by_name(char *name) +{ + if (!strcmp("name", name)) return 0; + if (!strcmp("reference", name)) return 1; + if (!strcmp("value", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *MetadataFloat_Create() +{ + X_MetadataFloat *p; + GF_SAFEALLOC(p, X_MetadataFloat); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_MetadataFloat); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + MetadataInteger Node deletion +*/ + +static void MetadataInteger_Del(GF_Node *node) +{ + X_MetadataInteger *p = (X_MetadataInteger *) node; + gf_sg_sfstring_del(p->name); + gf_sg_sfstring_del(p->reference); + gf_sg_mfint32_del(p->value); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 MetadataInteger_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err MetadataInteger_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "name"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_MetadataInteger *) node)->name; + return GF_OK; + case 1: + info->name = "reference"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_MetadataInteger *) node)->reference; + return GF_OK; + case 2: + info->name = "value"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_MetadataInteger *) node)->value; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_MetadataInteger *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MetadataInteger_get_field_index_by_name(char *name) +{ + if (!strcmp("name", name)) return 0; + if (!strcmp("reference", name)) return 1; + if (!strcmp("value", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *MetadataInteger_Create() +{ + X_MetadataInteger *p; + GF_SAFEALLOC(p, X_MetadataInteger); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_MetadataInteger); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + MetadataSet Node deletion +*/ + +static void MetadataSet_Del(GF_Node *node) +{ + X_MetadataSet *p = (X_MetadataSet *) node; + gf_sg_sfstring_del(p->name); + gf_sg_sfstring_del(p->reference); + gf_node_unregister_children(node, p->value); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 MetadataSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err MetadataSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "name"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_MetadataSet *) node)->name; + return GF_OK; + case 1: + info->name = "reference"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_MetadataSet *) node)->reference; + return GF_OK; + case 2: + info->name = "value"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_MetadataSet *)node)->value; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_MetadataSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MetadataSet_get_field_index_by_name(char *name) +{ + if (!strcmp("name", name)) return 0; + if (!strcmp("reference", name)) return 1; + if (!strcmp("value", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *MetadataSet_Create() +{ + X_MetadataSet *p; + GF_SAFEALLOC(p, X_MetadataSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_MetadataSet); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + MetadataString Node deletion +*/ + +static void MetadataString_Del(GF_Node *node) +{ + X_MetadataString *p = (X_MetadataString *) node; + gf_sg_sfstring_del(p->name); + gf_sg_sfstring_del(p->reference); + gf_sg_mfstring_del(p->value); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 MetadataString_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err MetadataString_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "name"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_MetadataString *) node)->name; + return GF_OK; + case 1: + info->name = "reference"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_MetadataString *) node)->reference; + return GF_OK; + case 2: + info->name = "value"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_MetadataString *) node)->value; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_MetadataString *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MetadataString_get_field_index_by_name(char *name) +{ + if (!strcmp("name", name)) return 0; + if (!strcmp("reference", name)) return 1; + if (!strcmp("value", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *MetadataString_Create() +{ + X_MetadataString *p; + GF_SAFEALLOC(p, X_MetadataString); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_MetadataString); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + MovieTexture Node deletion +*/ + +static void MovieTexture_Del(GF_Node *node) +{ + X_MovieTexture *p = (X_MovieTexture *) node; + gf_sg_mfurl_del(p->url); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 MovieTexture_get_field_count(GF_Node *node, u8 dummy) +{ + return 14; +} + +static GF_Err MovieTexture_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "loop"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_MovieTexture *) node)->loop; + return GF_OK; + case 1: + info->name = "speed"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_MovieTexture *) node)->speed; + return GF_OK; + case 2: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_MovieTexture *) node)->startTime; + return GF_OK; + case 3: + info->name = "stopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_MovieTexture *) node)->stopTime; + return GF_OK; + case 4: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFURL; + info->far_ptr = & ((X_MovieTexture *) node)->url; + return GF_OK; + case 5: + info->name = "repeatS"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_MovieTexture *) node)->repeatS; + return GF_OK; + case 6: + info->name = "repeatT"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_MovieTexture *) node)->repeatT; + return GF_OK; + case 7: + info->name = "duration_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_MovieTexture *) node)->duration_changed; + return GF_OK; + case 8: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_MovieTexture *) node)->isActive; + return GF_OK; + case 9: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_MovieTexture *)node)->metadata; + return GF_OK; + case 10: + info->name = "resumeTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_MovieTexture *) node)->resumeTime; + return GF_OK; + case 11: + info->name = "pauseTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_MovieTexture *) node)->pauseTime; + return GF_OK; + case 12: + info->name = "elapsedTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_MovieTexture *) node)->elapsedTime; + return GF_OK; + case 13: + info->name = "isPaused"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_MovieTexture *) node)->isPaused; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MovieTexture_get_field_index_by_name(char *name) +{ + if (!strcmp("loop", name)) return 0; + if (!strcmp("speed", name)) return 1; + if (!strcmp("startTime", name)) return 2; + if (!strcmp("stopTime", name)) return 3; + if (!strcmp("url", name)) return 4; + if (!strcmp("repeatS", name)) return 5; + if (!strcmp("repeatT", name)) return 6; + if (!strcmp("duration_changed", name)) return 7; + if (!strcmp("isActive", name)) return 8; + if (!strcmp("metadata", name)) return 9; + if (!strcmp("resumeTime", name)) return 10; + if (!strcmp("pauseTime", name)) return 11; + if (!strcmp("elapsedTime", name)) return 12; + if (!strcmp("isPaused", name)) return 13; + return -1; + } + + +static GF_Node *MovieTexture_Create() +{ + X_MovieTexture *p; + GF_SAFEALLOC(p, X_MovieTexture); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_MovieTexture); + + /*default field values*/ + p->speed = FLT2FIX(1.0); + p->startTime = 0; + p->stopTime = 0; + p->repeatS = 1; + p->repeatT = 1; + p->resumeTime = 0; + p->pauseTime = 0; + return (GF_Node *)p; +} + + +/* + MultiTexture Node deletion +*/ + +static void MultiTexture_Del(GF_Node *node) +{ + X_MultiTexture *p = (X_MultiTexture *) node; + gf_sg_mfstring_del(p->function); + gf_sg_mfstring_del(p->mode); + gf_sg_mfstring_del(p->source); + gf_node_unregister_children(node, p->texture); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 MultiTexture_get_field_count(GF_Node *node, u8 dummy) +{ + return 7; +} + +static GF_Err MultiTexture_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "alpha"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_MultiTexture *) node)->alpha; + return GF_OK; + case 1: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((X_MultiTexture *) node)->color; + return GF_OK; + case 2: + info->name = "function"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_MultiTexture *) node)->function; + return GF_OK; + case 3: + info->name = "mode"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_MultiTexture *) node)->mode; + return GF_OK; + case 4: + info->name = "source"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_MultiTexture *) node)->source; + return GF_OK; + case 5: + info->name = "texture"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((X_MultiTexture *)node)->texture; + return GF_OK; + case 6: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_MultiTexture *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MultiTexture_get_field_index_by_name(char *name) +{ + if (!strcmp("alpha", name)) return 0; + if (!strcmp("color", name)) return 1; + if (!strcmp("function", name)) return 2; + if (!strcmp("mode", name)) return 3; + if (!strcmp("source", name)) return 4; + if (!strcmp("texture", name)) return 5; + if (!strcmp("metadata", name)) return 6; + return -1; + } + + +static GF_Node *MultiTexture_Create() +{ + X_MultiTexture *p; + GF_SAFEALLOC(p, X_MultiTexture); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_MultiTexture); + + /*default field values*/ + p->alpha = FLT2FIX(1); + p->color.red = FLT2FIX(1); + p->color.green = FLT2FIX(1); + p->color.blue = FLT2FIX(1); + return (GF_Node *)p; +} + + +/* + MultiTextureCoordinate Node deletion +*/ + +static void MultiTextureCoordinate_Del(GF_Node *node) +{ + X_MultiTextureCoordinate *p = (X_MultiTextureCoordinate *) node; + gf_node_unregister_children(node, p->texCoord); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 MultiTextureCoordinate_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err MultiTextureCoordinate_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((X_MultiTextureCoordinate *)node)->texCoord; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_MultiTextureCoordinate *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MultiTextureCoordinate_get_field_index_by_name(char *name) +{ + if (!strcmp("texCoord", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *MultiTextureCoordinate_Create() +{ + X_MultiTextureCoordinate *p; + GF_SAFEALLOC(p, X_MultiTextureCoordinate); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_MultiTextureCoordinate); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + MultiTextureTransform Node deletion +*/ + +static void MultiTextureTransform_Del(GF_Node *node) +{ + X_MultiTextureTransform *p = (X_MultiTextureTransform *) node; + gf_node_unregister_children(node, p->textureTransform); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 MultiTextureTransform_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err MultiTextureTransform_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "textureTransform"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SFTextureTransformNode; + info->far_ptr = & ((X_MultiTextureTransform *)node)->textureTransform; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_MultiTextureTransform *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 MultiTextureTransform_get_field_index_by_name(char *name) +{ + if (!strcmp("textureTransform", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *MultiTextureTransform_Create() +{ + X_MultiTextureTransform *p; + GF_SAFEALLOC(p, X_MultiTextureTransform); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_MultiTextureTransform); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + NavigationInfo Node deletion +*/ + +static void NavigationInfo_Del(GF_Node *node) +{ + X_NavigationInfo *p = (X_NavigationInfo *) node; + gf_sg_mffloat_del(p->avatarSize); + gf_sg_mfstring_del(p->type); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_mfstring_del(p->transitionType); + gf_node_free((GF_Node *)p); +} + + +static u32 NavigationInfo_get_field_count(GF_Node *node, u8 dummy) +{ + return 10; +} + +static GF_Err NavigationInfo_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_NavigationInfo *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_NavigationInfo *) node)->set_bind; + return GF_OK; + case 1: + info->name = "avatarSize"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_NavigationInfo *) node)->avatarSize; + return GF_OK; + case 2: + info->name = "headlight"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_NavigationInfo *) node)->headlight; + return GF_OK; + case 3: + info->name = "speed"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_NavigationInfo *) node)->speed; + return GF_OK; + case 4: + info->name = "type"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_NavigationInfo *) node)->type; + return GF_OK; + case 5: + info->name = "visibilityLimit"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_NavigationInfo *) node)->visibilityLimit; + return GF_OK; + case 6: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_NavigationInfo *) node)->isBound; + return GF_OK; + case 7: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_NavigationInfo *)node)->metadata; + return GF_OK; + case 8: + info->name = "transitionType"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_NavigationInfo *) node)->transitionType; + return GF_OK; + case 9: + info->name = "bindTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_NavigationInfo *) node)->bindTime; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 NavigationInfo_get_field_index_by_name(char *name) +{ + if (!strcmp("set_bind", name)) return 0; + if (!strcmp("avatarSize", name)) return 1; + if (!strcmp("headlight", name)) return 2; + if (!strcmp("speed", name)) return 3; + if (!strcmp("type", name)) return 4; + if (!strcmp("visibilityLimit", name)) return 5; + if (!strcmp("isBound", name)) return 6; + if (!strcmp("metadata", name)) return 7; + if (!strcmp("transitionType", name)) return 8; + if (!strcmp("bindTime", name)) return 9; + return -1; + } + + +static GF_Node *NavigationInfo_Create() +{ + X_NavigationInfo *p; + GF_SAFEALLOC(p, X_NavigationInfo); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_NavigationInfo); + + /*default field values*/ + p->avatarSize.vals = (SFFloat *)malloc(sizeof(SFFloat)*3); + p->avatarSize.count = 3; + p->avatarSize.vals[0] = FLT2FIX(0.25); + p->avatarSize.vals[1] = FLT2FIX(1.6); + p->avatarSize.vals[2] = FLT2FIX(0.75); + p->headlight = 1; + p->speed = FLT2FIX(1.0); + p->type.vals = (char**)malloc(sizeof(SFString)*2); + p->type.count = 2; + p->type.vals[0] = (char*)malloc(sizeof(char) * 5); + strcpy(p->type.vals[0], "WALK"); + p->type.vals[1] = (char*)malloc(sizeof(char) * 4); + strcpy(p->type.vals[1], "ANY"); + p->visibilityLimit = FLT2FIX(0.0); + p->transitionType.vals = (char**)malloc(sizeof(SFString)*2); + p->transitionType.count = 2; + p->transitionType.vals[0] = (char*)malloc(sizeof(char) * 5); + strcpy(p->transitionType.vals[0], "WALK"); + p->transitionType.vals[1] = (char*)malloc(sizeof(char) * 4); + strcpy(p->transitionType.vals[1], "ANY"); + return (GF_Node *)p; +} + + +/* + Normal Node deletion +*/ + +static void Normal_Del(GF_Node *node) +{ + X_Normal *p = (X_Normal *) node; + gf_sg_mfvec3f_del(p->vector); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Normal_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err Normal_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "vector"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((X_Normal *) node)->vector; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Normal *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Normal_get_field_index_by_name(char *name) +{ + if (!strcmp("vector", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *Normal_Create() +{ + X_Normal *p; + GF_SAFEALLOC(p, X_Normal); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Normal); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + NormalInterpolator Node deletion +*/ + +static void NormalInterpolator_Del(GF_Node *node) +{ + X_NormalInterpolator *p = (X_NormalInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec3f_del(p->keyValue); + gf_sg_mfvec3f_del(p->value_changed); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 NormalInterpolator_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err NormalInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_NormalInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_NormalInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_NormalInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((X_NormalInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((X_NormalInterpolator *) node)->value_changed; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_NormalInterpolator *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 NormalInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *NormalInterpolator_Create() +{ + X_NormalInterpolator *p; + GF_SAFEALLOC(p, X_NormalInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_NormalInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + OrientationInterpolator Node deletion +*/ + +static void OrientationInterpolator_Del(GF_Node *node) +{ + X_OrientationInterpolator *p = (X_OrientationInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfrotation_del(p->keyValue); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 OrientationInterpolator_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err OrientationInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_OrientationInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_OrientationInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_OrientationInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFROTATION; + info->far_ptr = & ((X_OrientationInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((X_OrientationInterpolator *) node)->value_changed; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_OrientationInterpolator *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 OrientationInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *OrientationInterpolator_Create() +{ + X_OrientationInterpolator *p; + GF_SAFEALLOC(p, X_OrientationInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_OrientationInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + PixelTexture Node deletion +*/ + +static void PixelTexture_Del(GF_Node *node) +{ + X_PixelTexture *p = (X_PixelTexture *) node; + gf_sg_sfimage_del(p->image); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 PixelTexture_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err PixelTexture_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "image"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFIMAGE; + info->far_ptr = & ((X_PixelTexture *) node)->image; + return GF_OK; + case 1: + info->name = "repeatS"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_PixelTexture *) node)->repeatS; + return GF_OK; + case 2: + info->name = "repeatT"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_PixelTexture *) node)->repeatT; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_PixelTexture *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PixelTexture_get_field_index_by_name(char *name) +{ + if (!strcmp("image", name)) return 0; + if (!strcmp("repeatS", name)) return 1; + if (!strcmp("repeatT", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *PixelTexture_Create() +{ + X_PixelTexture *p; + GF_SAFEALLOC(p, X_PixelTexture); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_PixelTexture); + + /*default field values*/ + p->repeatS = 1; + p->repeatT = 1; + return (GF_Node *)p; +} + + +/* + PlaneSensor Node deletion +*/ + +static void PlaneSensor_Del(GF_Node *node) +{ + X_PlaneSensor *p = (X_PlaneSensor *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_sfstring_del(p->description); + gf_node_free((GF_Node *)p); +} + + +static u32 PlaneSensor_get_field_count(GF_Node *node, u8 dummy) +{ + return 11; +} + +static GF_Err PlaneSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "autoOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_PlaneSensor *) node)->autoOffset; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_PlaneSensor *) node)->enabled; + return GF_OK; + case 2: + info->name = "maxPosition"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((X_PlaneSensor *) node)->maxPosition; + return GF_OK; + case 3: + info->name = "minPosition"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((X_PlaneSensor *) node)->minPosition; + return GF_OK; + case 4: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_PlaneSensor *) node)->offset; + return GF_OK; + case 5: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_PlaneSensor *) node)->isActive; + return GF_OK; + case 6: + info->name = "trackPoint_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_PlaneSensor *) node)->trackPoint_changed; + return GF_OK; + case 7: + info->name = "translation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_PlaneSensor *) node)->translation_changed; + return GF_OK; + case 8: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_PlaneSensor *)node)->metadata; + return GF_OK; + case 9: + info->name = "description"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_PlaneSensor *) node)->description; + return GF_OK; + case 10: + info->name = "isOver"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_PlaneSensor *) node)->isOver; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PlaneSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("autoOffset", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("maxPosition", name)) return 2; + if (!strcmp("minPosition", name)) return 3; + if (!strcmp("offset", name)) return 4; + if (!strcmp("isActive", name)) return 5; + if (!strcmp("trackPoint_changed", name)) return 6; + if (!strcmp("translation_changed", name)) return 7; + if (!strcmp("metadata", name)) return 8; + if (!strcmp("description", name)) return 9; + if (!strcmp("isOver", name)) return 10; + return -1; + } + + +static GF_Node *PlaneSensor_Create() +{ + X_PlaneSensor *p; + GF_SAFEALLOC(p, X_PlaneSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_PlaneSensor); + + /*default field values*/ + p->autoOffset = 1; + p->enabled = 1; + p->maxPosition.x = FLT2FIX(-1); + p->maxPosition.y = FLT2FIX(-1); + p->minPosition.x = FLT2FIX(0); + p->minPosition.y = FLT2FIX(0); + p->offset.x = FLT2FIX(0); + p->offset.y = FLT2FIX(0); + p->offset.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + PointLight Node deletion +*/ + +static void PointLight_Del(GF_Node *node) +{ + X_PointLight *p = (X_PointLight *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 PointLight_get_field_count(GF_Node *node, u8 dummy) +{ + return 8; +} + +static GF_Err PointLight_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "ambientIntensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_PointLight *) node)->ambientIntensity; + return GF_OK; + case 1: + info->name = "attenuation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_PointLight *) node)->attenuation; + return GF_OK; + case 2: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((X_PointLight *) node)->color; + return GF_OK; + case 3: + info->name = "intensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_PointLight *) node)->intensity; + return GF_OK; + case 4: + info->name = "location"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_PointLight *) node)->location; + return GF_OK; + case 5: + info->name = "on"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_PointLight *) node)->on; + return GF_OK; + case 6: + info->name = "radius"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_PointLight *) node)->radius; + return GF_OK; + case 7: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_PointLight *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PointLight_get_field_index_by_name(char *name) +{ + if (!strcmp("ambientIntensity", name)) return 0; + if (!strcmp("attenuation", name)) return 1; + if (!strcmp("color", name)) return 2; + if (!strcmp("intensity", name)) return 3; + if (!strcmp("location", name)) return 4; + if (!strcmp("on", name)) return 5; + if (!strcmp("radius", name)) return 6; + if (!strcmp("metadata", name)) return 7; + return -1; + } + + +static GF_Node *PointLight_Create() +{ + X_PointLight *p; + GF_SAFEALLOC(p, X_PointLight); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_PointLight); + + /*default field values*/ + p->ambientIntensity = FLT2FIX(0); + p->attenuation.x = FLT2FIX(1); + p->attenuation.y = FLT2FIX(0); + p->attenuation.z = FLT2FIX(0); + p->color.red = FLT2FIX(1); + p->color.green = FLT2FIX(1); + p->color.blue = FLT2FIX(1); + p->intensity = FLT2FIX(1); + p->location.x = FLT2FIX(0); + p->location.y = FLT2FIX(0); + p->location.z = FLT2FIX(0); + p->on = 1; + p->radius = FLT2FIX(100); + return (GF_Node *)p; +} + + +/* + PointSet Node deletion +*/ + +static void PointSet_Del(GF_Node *node) +{ + X_PointSet *p = (X_PointSet *) node; + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->coord, node); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 PointSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 3; +} + +static GF_Err PointSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_PointSet *)node)->color; + return GF_OK; + case 1: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((X_PointSet *)node)->coord; + return GF_OK; + case 2: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_PointSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PointSet_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("coord", name)) return 1; + if (!strcmp("metadata", name)) return 2; + return -1; + } + + +static GF_Node *PointSet_Create() +{ + X_PointSet *p; + GF_SAFEALLOC(p, X_PointSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_PointSet); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Polyline2D Node deletion +*/ + +static void Polyline2D_Del(GF_Node *node) +{ + X_Polyline2D *p = (X_Polyline2D *) node; + gf_sg_mfvec2f_del(p->lineSegments); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Polyline2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err Polyline2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "lineSegments"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_Polyline2D *) node)->lineSegments; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Polyline2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Polyline2D_get_field_index_by_name(char *name) +{ + if (!strcmp("lineSegments", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *Polyline2D_Create() +{ + X_Polyline2D *p; + GF_SAFEALLOC(p, X_Polyline2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Polyline2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Polypoint2D Node deletion +*/ + +static void Polypoint2D_Del(GF_Node *node) +{ + X_Polypoint2D *p = (X_Polypoint2D *) node; + gf_sg_mfvec2f_del(p->point); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Polypoint2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err Polypoint2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_Polypoint2D *) node)->point; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Polypoint2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Polypoint2D_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *Polypoint2D_Create() +{ + X_Polypoint2D *p; + GF_SAFEALLOC(p, X_Polypoint2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Polypoint2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + PositionInterpolator Node deletion +*/ + +static void PositionInterpolator_Del(GF_Node *node) +{ + X_PositionInterpolator *p = (X_PositionInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec3f_del(p->keyValue); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 PositionInterpolator_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err PositionInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_PositionInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_PositionInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_PositionInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC3F; + info->far_ptr = & ((X_PositionInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_PositionInterpolator *) node)->value_changed; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_PositionInterpolator *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PositionInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *PositionInterpolator_Create() +{ + X_PositionInterpolator *p; + GF_SAFEALLOC(p, X_PositionInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_PositionInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + PositionInterpolator2D Node deletion +*/ + +static void PositionInterpolator2D_Del(GF_Node *node) +{ + X_PositionInterpolator2D *p = (X_PositionInterpolator2D *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mfvec2f_del(p->keyValue); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 PositionInterpolator2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err PositionInterpolator2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_PositionInterpolator2D *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_PositionInterpolator2D *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_PositionInterpolator2D *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_PositionInterpolator2D *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((X_PositionInterpolator2D *) node)->value_changed; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_PositionInterpolator2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 PositionInterpolator2D_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *PositionInterpolator2D_Create() +{ + X_PositionInterpolator2D *p; + GF_SAFEALLOC(p, X_PositionInterpolator2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_PositionInterpolator2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + ProximitySensor Node deletion +*/ + +static void ProximitySensor_Del(GF_Node *node) +{ + X_ProximitySensor *p = (X_ProximitySensor *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 ProximitySensor_get_field_count(GF_Node *node, u8 dummy) +{ + return 10; +} + +static GF_Err ProximitySensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_ProximitySensor *) node)->center; + return GF_OK; + case 1: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_ProximitySensor *) node)->size; + return GF_OK; + case 2: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_ProximitySensor *) node)->enabled; + return GF_OK; + case 3: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_ProximitySensor *) node)->isActive; + return GF_OK; + case 4: + info->name = "position_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_ProximitySensor *) node)->position_changed; + return GF_OK; + case 5: + info->name = "orientation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((X_ProximitySensor *) node)->orientation_changed; + return GF_OK; + case 6: + info->name = "enterTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_ProximitySensor *) node)->enterTime; + return GF_OK; + case 7: + info->name = "exitTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_ProximitySensor *) node)->exitTime; + return GF_OK; + case 8: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_ProximitySensor *)node)->metadata; + return GF_OK; + case 9: + info->name = "centerOfRotation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_ProximitySensor *) node)->centerOfRotation_changed; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ProximitySensor_get_field_index_by_name(char *name) +{ + if (!strcmp("center", name)) return 0; + if (!strcmp("size", name)) return 1; + if (!strcmp("enabled", name)) return 2; + if (!strcmp("isActive", name)) return 3; + if (!strcmp("position_changed", name)) return 4; + if (!strcmp("orientation_changed", name)) return 5; + if (!strcmp("enterTime", name)) return 6; + if (!strcmp("exitTime", name)) return 7; + if (!strcmp("metadata", name)) return 8; + if (!strcmp("centerOfRotation_changed", name)) return 9; + return -1; + } + + +static GF_Node *ProximitySensor_Create() +{ + X_ProximitySensor *p; + GF_SAFEALLOC(p, X_ProximitySensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_ProximitySensor); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->center.z = FLT2FIX(0); + p->size.x = FLT2FIX(0); + p->size.y = FLT2FIX(0); + p->size.z = FLT2FIX(0); + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + Rectangle2D Node deletion +*/ + +static void Rectangle2D_Del(GF_Node *node) +{ + X_Rectangle2D *p = (X_Rectangle2D *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Rectangle2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err Rectangle2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "size"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((X_Rectangle2D *) node)->size; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Rectangle2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Rectangle2D_get_field_index_by_name(char *name) +{ + if (!strcmp("size", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *Rectangle2D_Create() +{ + X_Rectangle2D *p; + GF_SAFEALLOC(p, X_Rectangle2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Rectangle2D); + + /*default field values*/ + p->size.x = FLT2FIX(2); + p->size.y = FLT2FIX(2); + return (GF_Node *)p; +} + + +/* + ScalarInterpolator Node deletion +*/ + +static void ScalarInterpolator_Del(GF_Node *node) +{ + X_ScalarInterpolator *p = (X_ScalarInterpolator *) node; + gf_sg_mffloat_del(p->key); + gf_sg_mffloat_del(p->keyValue); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 ScalarInterpolator_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err ScalarInterpolator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_fraction"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_ScalarInterpolator *)node)->on_set_fraction; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_ScalarInterpolator *) node)->set_fraction; + return GF_OK; + case 1: + info->name = "key"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_ScalarInterpolator *) node)->key; + return GF_OK; + case 2: + info->name = "keyValue"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_ScalarInterpolator *) node)->keyValue; + return GF_OK; + case 3: + info->name = "value_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_ScalarInterpolator *) node)->value_changed; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_ScalarInterpolator *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 ScalarInterpolator_get_field_index_by_name(char *name) +{ + if (!strcmp("set_fraction", name)) return 0; + if (!strcmp("key", name)) return 1; + if (!strcmp("keyValue", name)) return 2; + if (!strcmp("value_changed", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *ScalarInterpolator_Create() +{ + X_ScalarInterpolator *p; + GF_SAFEALLOC(p, X_ScalarInterpolator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_ScalarInterpolator); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Script Node deletion +*/ + +static void Script_Del(GF_Node *node) +{ + X_Script *p = (X_Script *) node; + gf_sg_mfscript_del(p->url); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Script_get_field_count(GF_Node *node, u8 dummy) +{ + return 4; +} + +static GF_Err Script_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "url"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSCRIPT; + info->far_ptr = & ((X_Script *) node)->url; + return GF_OK; + case 1: + info->name = "directOutput"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Script *) node)->directOutput; + return GF_OK; + case 2: + info->name = "mustEvaluate"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Script *) node)->mustEvaluate; + return GF_OK; + case 3: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Script *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Script_get_field_index_by_name(char *name) +{ + if (!strcmp("url", name)) return 0; + if (!strcmp("directOutput", name)) return 1; + if (!strcmp("mustEvaluate", name)) return 2; + if (!strcmp("metadata", name)) return 3; + return -1; + } + + +static GF_Node *Script_Create() +{ + X_Script *p; + GF_SAFEALLOC(p, X_Script); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Script); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Shape Node deletion +*/ + +static void Shape_Del(GF_Node *node) +{ + X_Shape *p = (X_Shape *) node; + gf_node_unregister((GF_Node *) p->appearance, node); + gf_node_unregister((GF_Node *) p->geometry, node); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Shape_get_field_count(GF_Node *node, u8 dummy) +{ + return 3; +} + +static GF_Err Shape_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "appearance"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFAppearanceNode; + info->far_ptr = & ((X_Shape *)node)->appearance; + return GF_OK; + case 1: + info->name = "geometry"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFGeometryNode; + info->far_ptr = & ((X_Shape *)node)->geometry; + return GF_OK; + case 2: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Shape *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Shape_get_field_index_by_name(char *name) +{ + if (!strcmp("appearance", name)) return 0; + if (!strcmp("geometry", name)) return 1; + if (!strcmp("metadata", name)) return 2; + return -1; + } + + +static GF_Node *Shape_Create() +{ + X_Shape *p; + GF_SAFEALLOC(p, X_Shape); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Shape); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + Sound Node deletion +*/ + +static void Sound_Del(GF_Node *node) +{ + X_Sound *p = (X_Sound *) node; + gf_node_unregister((GF_Node *) p->source, node); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Sound_get_field_count(GF_Node *node, u8 dummy) +{ + return 11; +} + +static GF_Err Sound_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "direction"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_Sound *) node)->direction; + return GF_OK; + case 1: + info->name = "intensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Sound *) node)->intensity; + return GF_OK; + case 2: + info->name = "location"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_Sound *) node)->location; + return GF_OK; + case 3: + info->name = "maxBack"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Sound *) node)->maxBack; + return GF_OK; + case 4: + info->name = "maxFront"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Sound *) node)->maxFront; + return GF_OK; + case 5: + info->name = "minBack"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Sound *) node)->minBack; + return GF_OK; + case 6: + info->name = "minFront"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Sound *) node)->minFront; + return GF_OK; + case 7: + info->name = "priority"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Sound *) node)->priority; + return GF_OK; + case 8: + info->name = "source"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFAudioNode; + info->far_ptr = & ((X_Sound *)node)->source; + return GF_OK; + case 9: + info->name = "spatialize"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Sound *) node)->spatialize; + return GF_OK; + case 10: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Sound *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Sound_get_field_index_by_name(char *name) +{ + if (!strcmp("direction", name)) return 0; + if (!strcmp("intensity", name)) return 1; + if (!strcmp("location", name)) return 2; + if (!strcmp("maxBack", name)) return 3; + if (!strcmp("maxFront", name)) return 4; + if (!strcmp("minBack", name)) return 5; + if (!strcmp("minFront", name)) return 6; + if (!strcmp("priority", name)) return 7; + if (!strcmp("source", name)) return 8; + if (!strcmp("spatialize", name)) return 9; + if (!strcmp("metadata", name)) return 10; + return -1; + } + + +static GF_Node *Sound_Create() +{ + X_Sound *p; + GF_SAFEALLOC(p, X_Sound); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Sound); + + /*default field values*/ + p->direction.x = FLT2FIX(0); + p->direction.y = FLT2FIX(0); + p->direction.z = FLT2FIX(1); + p->intensity = FLT2FIX(1); + p->location.x = FLT2FIX(0); + p->location.y = FLT2FIX(0); + p->location.z = FLT2FIX(0); + p->maxBack = FLT2FIX(10); + p->maxFront = FLT2FIX(10); + p->minBack = FLT2FIX(1); + p->minFront = FLT2FIX(1); + p->priority = FLT2FIX(0); + p->spatialize = 1; + return (GF_Node *)p; +} + + +/* + Sphere Node deletion +*/ + +static void Sphere_Del(GF_Node *node) +{ + X_Sphere *p = (X_Sphere *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Sphere_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err Sphere_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "radius"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Sphere *) node)->radius; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Sphere *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Sphere_get_field_index_by_name(char *name) +{ + if (!strcmp("radius", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *Sphere_Create() +{ + X_Sphere *p; + GF_SAFEALLOC(p, X_Sphere); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Sphere); + + /*default field values*/ + p->radius = FLT2FIX(1); + return (GF_Node *)p; +} + + +/* + SphereSensor Node deletion +*/ + +static void SphereSensor_Del(GF_Node *node) +{ + X_SphereSensor *p = (X_SphereSensor *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_sfstring_del(p->description); + gf_node_free((GF_Node *)p); +} + + +static u32 SphereSensor_get_field_count(GF_Node *node, u8 dummy) +{ + return 9; +} + +static GF_Err SphereSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "autoOffset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_SphereSensor *) node)->autoOffset; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_SphereSensor *) node)->enabled; + return GF_OK; + case 2: + info->name = "offset"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((X_SphereSensor *) node)->offset; + return GF_OK; + case 3: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_SphereSensor *) node)->isActive; + return GF_OK; + case 4: + info->name = "rotation_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((X_SphereSensor *) node)->rotation_changed; + return GF_OK; + case 5: + info->name = "trackPoint_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_SphereSensor *) node)->trackPoint_changed; + return GF_OK; + case 6: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_SphereSensor *)node)->metadata; + return GF_OK; + case 7: + info->name = "description"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_SphereSensor *) node)->description; + return GF_OK; + case 8: + info->name = "isOver"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_SphereSensor *) node)->isOver; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 SphereSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("autoOffset", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("offset", name)) return 2; + if (!strcmp("isActive", name)) return 3; + if (!strcmp("rotation_changed", name)) return 4; + if (!strcmp("trackPoint_changed", name)) return 5; + if (!strcmp("metadata", name)) return 6; + if (!strcmp("description", name)) return 7; + if (!strcmp("isOver", name)) return 8; + return -1; + } + + +static GF_Node *SphereSensor_Create() +{ + X_SphereSensor *p; + GF_SAFEALLOC(p, X_SphereSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_SphereSensor); + + /*default field values*/ + p->autoOffset = 1; + p->enabled = 1; + p->offset.x = FLT2FIX(0); + p->offset.y = FLT2FIX(1); + p->offset.z = FLT2FIX(0); + p->offset.q = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + SpotLight Node deletion +*/ + +static void SpotLight_Del(GF_Node *node) +{ + X_SpotLight *p = (X_SpotLight *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 SpotLight_get_field_count(GF_Node *node, u8 dummy) +{ + return 11; +} + +static GF_Err SpotLight_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "ambientIntensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_SpotLight *) node)->ambientIntensity; + return GF_OK; + case 1: + info->name = "attenuation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_SpotLight *) node)->attenuation; + return GF_OK; + case 2: + info->name = "beamWidth"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_SpotLight *) node)->beamWidth; + return GF_OK; + case 3: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFCOLOR; + info->far_ptr = & ((X_SpotLight *) node)->color; + return GF_OK; + case 4: + info->name = "cutOffAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_SpotLight *) node)->cutOffAngle; + return GF_OK; + case 5: + info->name = "direction"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_SpotLight *) node)->direction; + return GF_OK; + case 6: + info->name = "intensity"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_SpotLight *) node)->intensity; + return GF_OK; + case 7: + info->name = "location"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_SpotLight *) node)->location; + return GF_OK; + case 8: + info->name = "on"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_SpotLight *) node)->on; + return GF_OK; + case 9: + info->name = "radius"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_SpotLight *) node)->radius; + return GF_OK; + case 10: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_SpotLight *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 SpotLight_get_field_index_by_name(char *name) +{ + if (!strcmp("ambientIntensity", name)) return 0; + if (!strcmp("attenuation", name)) return 1; + if (!strcmp("beamWidth", name)) return 2; + if (!strcmp("color", name)) return 3; + if (!strcmp("cutOffAngle", name)) return 4; + if (!strcmp("direction", name)) return 5; + if (!strcmp("intensity", name)) return 6; + if (!strcmp("location", name)) return 7; + if (!strcmp("on", name)) return 8; + if (!strcmp("radius", name)) return 9; + if (!strcmp("metadata", name)) return 10; + return -1; + } + + +static GF_Node *SpotLight_Create() +{ + X_SpotLight *p; + GF_SAFEALLOC(p, X_SpotLight); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_SpotLight); + + /*default field values*/ + p->ambientIntensity = FLT2FIX(0); + p->attenuation.x = FLT2FIX(1); + p->attenuation.y = FLT2FIX(0); + p->attenuation.z = FLT2FIX(0); + p->beamWidth = FLT2FIX(1.570796); + p->color.red = FLT2FIX(1); + p->color.green = FLT2FIX(1); + p->color.blue = FLT2FIX(1); + p->cutOffAngle = FLT2FIX(0.785398); + p->direction.x = FLT2FIX(0); + p->direction.y = FLT2FIX(0); + p->direction.z = FLT2FIX(-1); + p->intensity = FLT2FIX(1); + p->location.x = FLT2FIX(0); + p->location.y = FLT2FIX(0); + p->location.z = FLT2FIX(0); + p->on = 1; + p->radius = FLT2FIX(100); + return (GF_Node *)p; +} + + +/* + StaticGroup Node deletion +*/ + +static void StaticGroup_Del(GF_Node *node) +{ + X_StaticGroup *p = (X_StaticGroup *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_vrml_parent_destroy(node); + gf_node_free((GF_Node *)p); +} + + +static u32 StaticGroup_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err StaticGroup_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "children"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_StaticGroup *)node)->children; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_StaticGroup *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 StaticGroup_get_field_index_by_name(char *name) +{ + if (!strcmp("children", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *StaticGroup_Create() +{ + X_StaticGroup *p; + GF_SAFEALLOC(p, X_StaticGroup); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_StaticGroup); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + StringSensor Node deletion +*/ + +static void StringSensor_Del(GF_Node *node) +{ + X_StringSensor *p = (X_StringSensor *) node; + gf_sg_sfstring_del(p->enteredText); + gf_sg_sfstring_del(p->finalText); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 StringSensor_get_field_count(GF_Node *node, u8 dummy) +{ + return 6; +} + +static GF_Err StringSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "deletionAllowed"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_StringSensor *) node)->deletionAllowed; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_StringSensor *) node)->enabled; + return GF_OK; + case 2: + info->name = "enteredText"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_StringSensor *) node)->enteredText; + return GF_OK; + case 3: + info->name = "finalText"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_StringSensor *) node)->finalText; + return GF_OK; + case 4: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_StringSensor *) node)->isActive; + return GF_OK; + case 5: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_StringSensor *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 StringSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("deletionAllowed", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("enteredText", name)) return 2; + if (!strcmp("finalText", name)) return 3; + if (!strcmp("isActive", name)) return 4; + if (!strcmp("metadata", name)) return 5; + return -1; + } + + +static GF_Node *StringSensor_Create() +{ + X_StringSensor *p; + GF_SAFEALLOC(p, X_StringSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_StringSensor); + + /*default field values*/ + p->deletionAllowed = 1; + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + Switch Node deletion +*/ + +static void Switch_Del(GF_Node *node) +{ + X_Switch *p = (X_Switch *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_vrml_parent_destroy(node); + gf_node_free((GF_Node *)p); +} + + +static u32 Switch_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err Switch_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Switch *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Switch *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Switch *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Switch *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Switch *)node)->children; + return GF_OK; + case 3: + info->name = "whichChoice"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFINT32; + info->far_ptr = & ((X_Switch *) node)->whichChoice; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Switch *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Switch_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("children", name)) return 2; + if (!strcmp("whichChoice", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *Switch_Create() +{ + X_Switch *p; + GF_SAFEALLOC(p, X_Switch); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Switch); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->whichChoice = -1; + return (GF_Node *)p; +} + + +/* + Text Node deletion +*/ + +static void Text_Del(GF_Node *node) +{ + X_Text *p = (X_Text *) node; + gf_sg_mfstring_del(p->string); + gf_sg_mffloat_del(p->length); + gf_node_unregister((GF_Node *) p->fontStyle, node); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Text_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err Text_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "string"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_Text *) node)->string; + return GF_OK; + case 1: + info->name = "length"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_Text *) node)->length; + return GF_OK; + case 2: + info->name = "fontStyle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFFontStyleNode; + info->far_ptr = & ((X_Text *)node)->fontStyle; + return GF_OK; + case 3: + info->name = "maxExtent"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Text *) node)->maxExtent; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Text *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Text_get_field_index_by_name(char *name) +{ + if (!strcmp("string", name)) return 0; + if (!strcmp("length", name)) return 1; + if (!strcmp("fontStyle", name)) return 2; + if (!strcmp("maxExtent", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *Text_Create() +{ + X_Text *p; + GF_SAFEALLOC(p, X_Text); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Text); + + /*default field values*/ + p->maxExtent = FLT2FIX(0.0); + return (GF_Node *)p; +} + + +/* + TextureBackground Node deletion +*/ + +static void TextureBackground_Del(GF_Node *node) +{ + X_TextureBackground *p = (X_TextureBackground *) node; + gf_sg_mffloat_del(p->groundAngle); + gf_sg_mfcolor_del(p->groundColor); + gf_node_unregister((GF_Node *) p->backTexture, node); + gf_node_unregister((GF_Node *) p->bottomTexture, node); + gf_node_unregister((GF_Node *) p->frontTexture, node); + gf_node_unregister((GF_Node *) p->leftTexture, node); + gf_node_unregister((GF_Node *) p->rightTexture, node); + gf_node_unregister((GF_Node *) p->topTexture, node); + gf_sg_mffloat_del(p->skyAngle); + gf_sg_mfcolor_del(p->skyColor); + gf_sg_mffloat_del(p->transparency); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 TextureBackground_get_field_count(GF_Node *node, u8 dummy) +{ + return 15; +} + +static GF_Err TextureBackground_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_TextureBackground *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TextureBackground *) node)->set_bind; + return GF_OK; + case 1: + info->name = "groundAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_TextureBackground *) node)->groundAngle; + return GF_OK; + case 2: + info->name = "groundColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((X_TextureBackground *) node)->groundColor; + return GF_OK; + case 3: + info->name = "backTexture"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((X_TextureBackground *)node)->backTexture; + return GF_OK; + case 4: + info->name = "bottomTexture"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((X_TextureBackground *)node)->bottomTexture; + return GF_OK; + case 5: + info->name = "frontTexture"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((X_TextureBackground *)node)->frontTexture; + return GF_OK; + case 6: + info->name = "leftTexture"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((X_TextureBackground *)node)->leftTexture; + return GF_OK; + case 7: + info->name = "rightTexture"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((X_TextureBackground *)node)->rightTexture; + return GF_OK; + case 8: + info->name = "topTexture"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureNode; + info->far_ptr = & ((X_TextureBackground *)node)->topTexture; + return GF_OK; + case 9: + info->name = "skyAngle"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_TextureBackground *) node)->skyAngle; + return GF_OK; + case 10: + info->name = "skyColor"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFCOLOR; + info->far_ptr = & ((X_TextureBackground *) node)->skyColor; + return GF_OK; + case 11: + info->name = "transparency"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_TextureBackground *) node)->transparency; + return GF_OK; + case 12: + info->name = "bindTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TextureBackground *) node)->bindTime; + return GF_OK; + case 13: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TextureBackground *) node)->isBound; + return GF_OK; + case 14: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TextureBackground *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TextureBackground_get_field_index_by_name(char *name) +{ + if (!strcmp("set_bind", name)) return 0; + if (!strcmp("groundAngle", name)) return 1; + if (!strcmp("groundColor", name)) return 2; + if (!strcmp("backTexture", name)) return 3; + if (!strcmp("bottomTexture", name)) return 4; + if (!strcmp("frontTexture", name)) return 5; + if (!strcmp("leftTexture", name)) return 6; + if (!strcmp("rightTexture", name)) return 7; + if (!strcmp("topTexture", name)) return 8; + if (!strcmp("skyAngle", name)) return 9; + if (!strcmp("skyColor", name)) return 10; + if (!strcmp("transparency", name)) return 11; + if (!strcmp("bindTime", name)) return 12; + if (!strcmp("isBound", name)) return 13; + if (!strcmp("metadata", name)) return 14; + return -1; + } + + +static GF_Node *TextureBackground_Create() +{ + X_TextureBackground *p; + GF_SAFEALLOC(p, X_TextureBackground); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TextureBackground); + + /*default field values*/ + p->skyColor.vals = (SFColor*)malloc(sizeof(SFColor)*1); + p->skyColor.count = 1; + p->skyColor.vals[0].red = FLT2FIX(0); + p->skyColor.vals[0].green = FLT2FIX(0); + p->skyColor.vals[0].blue = FLT2FIX(0); + p->transparency.vals = (SFFloat *)malloc(sizeof(SFFloat)*1); + p->transparency.count = 1; + p->transparency.vals[0] = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + TextureCoordinate Node deletion +*/ + +static void TextureCoordinate_Del(GF_Node *node) +{ + X_TextureCoordinate *p = (X_TextureCoordinate *) node; + gf_sg_mfvec2f_del(p->point); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 TextureCoordinate_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err TextureCoordinate_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "point"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_TextureCoordinate *) node)->point; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TextureCoordinate *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TextureCoordinate_get_field_index_by_name(char *name) +{ + if (!strcmp("point", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *TextureCoordinate_Create() +{ + X_TextureCoordinate *p; + GF_SAFEALLOC(p, X_TextureCoordinate); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TextureCoordinate); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + TextureCoordinateGenerator Node deletion +*/ + +static void TextureCoordinateGenerator_Del(GF_Node *node) +{ + X_TextureCoordinateGenerator *p = (X_TextureCoordinateGenerator *) node; + gf_sg_sfstring_del(p->mode); + gf_sg_mffloat_del(p->parameter); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 TextureCoordinateGenerator_get_field_count(GF_Node *node, u8 dummy) +{ + return 3; +} + +static GF_Err TextureCoordinateGenerator_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "mode"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_TextureCoordinateGenerator *) node)->mode; + return GF_OK; + case 1: + info->name = "parameter"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFFLOAT; + info->far_ptr = & ((X_TextureCoordinateGenerator *) node)->parameter; + return GF_OK; + case 2: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TextureCoordinateGenerator *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TextureCoordinateGenerator_get_field_index_by_name(char *name) +{ + if (!strcmp("mode", name)) return 0; + if (!strcmp("parameter", name)) return 1; + if (!strcmp("metadata", name)) return 2; + return -1; + } + + +static GF_Node *TextureCoordinateGenerator_Create() +{ + X_TextureCoordinateGenerator *p; + GF_SAFEALLOC(p, X_TextureCoordinateGenerator); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TextureCoordinateGenerator); + + /*default field values*/ + p->mode.buffer = (char*) malloc(sizeof(char) * 7); + strcpy(p->mode.buffer, "SPHERE"); + return (GF_Node *)p; +} + + +/* + TextureTransform Node deletion +*/ + +static void TextureTransform_Del(GF_Node *node) +{ + X_TextureTransform *p = (X_TextureTransform *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 TextureTransform_get_field_count(GF_Node *node, u8 dummy) +{ + return 5; +} + +static GF_Err TextureTransform_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((X_TextureTransform *) node)->center; + return GF_OK; + case 1: + info->name = "rotation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_TextureTransform *) node)->rotation; + return GF_OK; + case 2: + info->name = "scale"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((X_TextureTransform *) node)->scale; + return GF_OK; + case 3: + info->name = "translation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((X_TextureTransform *) node)->translation; + return GF_OK; + case 4: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TextureTransform *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TextureTransform_get_field_index_by_name(char *name) +{ + if (!strcmp("center", name)) return 0; + if (!strcmp("rotation", name)) return 1; + if (!strcmp("scale", name)) return 2; + if (!strcmp("translation", name)) return 3; + if (!strcmp("metadata", name)) return 4; + return -1; + } + + +static GF_Node *TextureTransform_Create() +{ + X_TextureTransform *p; + GF_SAFEALLOC(p, X_TextureTransform); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TextureTransform); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->rotation = FLT2FIX(0); + p->scale.x = FLT2FIX(1); + p->scale.y = FLT2FIX(1); + p->translation.x = FLT2FIX(0); + p->translation.y = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + TimeSensor Node deletion +*/ + +static void TimeSensor_Del(GF_Node *node) +{ + X_TimeSensor *p = (X_TimeSensor *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 TimeSensor_get_field_count(GF_Node *node, u8 dummy) +{ + return 14; +} + +static GF_Err TimeSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "cycleInterval"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TimeSensor *) node)->cycleInterval; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TimeSensor *) node)->enabled; + return GF_OK; + case 2: + info->name = "loop"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TimeSensor *) node)->loop; + return GF_OK; + case 3: + info->name = "startTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TimeSensor *) node)->startTime; + return GF_OK; + case 4: + info->name = "stopTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TimeSensor *) node)->stopTime; + return GF_OK; + case 5: + info->name = "cycleTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TimeSensor *) node)->cycleTime; + return GF_OK; + case 6: + info->name = "fraction_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_TimeSensor *) node)->fraction_changed; + return GF_OK; + case 7: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TimeSensor *) node)->isActive; + return GF_OK; + case 8: + info->name = "time"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TimeSensor *) node)->time; + return GF_OK; + case 9: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TimeSensor *)node)->metadata; + return GF_OK; + case 10: + info->name = "pauseTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TimeSensor *) node)->pauseTime; + return GF_OK; + case 11: + info->name = "resumeTime"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TimeSensor *) node)->resumeTime; + return GF_OK; + case 12: + info->name = "elapsedTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TimeSensor *) node)->elapsedTime; + return GF_OK; + case 13: + info->name = "isPaused"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TimeSensor *) node)->isPaused; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TimeSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("cycleInterval", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("loop", name)) return 2; + if (!strcmp("startTime", name)) return 3; + if (!strcmp("stopTime", name)) return 4; + if (!strcmp("cycleTime", name)) return 5; + if (!strcmp("fraction_changed", name)) return 6; + if (!strcmp("isActive", name)) return 7; + if (!strcmp("time", name)) return 8; + if (!strcmp("metadata", name)) return 9; + if (!strcmp("pauseTime", name)) return 10; + if (!strcmp("resumeTime", name)) return 11; + if (!strcmp("elapsedTime", name)) return 12; + if (!strcmp("isPaused", name)) return 13; + return -1; + } + + +static GF_Node *TimeSensor_Create() +{ + X_TimeSensor *p; + GF_SAFEALLOC(p, X_TimeSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TimeSensor); + + /*default field values*/ + p->cycleInterval = 1; + p->enabled = 1; + p->startTime = 0; + p->stopTime = 0; + p->pauseTime = 0; + p->resumeTime = 0; + return (GF_Node *)p; +} + + +/* + TimeTrigger Node deletion +*/ + +static void TimeTrigger_Del(GF_Node *node) +{ + X_TimeTrigger *p = (X_TimeTrigger *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 TimeTrigger_get_field_count(GF_Node *node, u8 dummy) +{ + return 3; +} + +static GF_Err TimeTrigger_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_boolean"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_TimeTrigger *)node)->on_set_boolean; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TimeTrigger *) node)->set_boolean; + return GF_OK; + case 1: + info->name = "triggerTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TimeTrigger *) node)->triggerTime; + return GF_OK; + case 2: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TimeTrigger *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TimeTrigger_get_field_index_by_name(char *name) +{ + if (!strcmp("set_boolean", name)) return 0; + if (!strcmp("triggerTime", name)) return 1; + if (!strcmp("metadata", name)) return 2; + return -1; + } + + +static GF_Node *TimeTrigger_Create() +{ + X_TimeTrigger *p; + GF_SAFEALLOC(p, X_TimeTrigger); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TimeTrigger); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + TouchSensor Node deletion +*/ + +static void TouchSensor_Del(GF_Node *node) +{ + X_TouchSensor *p = (X_TouchSensor *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_sfstring_del(p->description); + gf_node_free((GF_Node *)p); +} + + +static u32 TouchSensor_get_field_count(GF_Node *node, u8 dummy) +{ + return 9; +} + +static GF_Err TouchSensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TouchSensor *) node)->enabled; + return GF_OK; + case 1: + info->name = "hitNormal_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_TouchSensor *) node)->hitNormal_changed; + return GF_OK; + case 2: + info->name = "hitPoint_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_TouchSensor *) node)->hitPoint_changed; + return GF_OK; + case 3: + info->name = "hitTexCoord_changed"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFVEC2F; + info->far_ptr = & ((X_TouchSensor *) node)->hitTexCoord_changed; + return GF_OK; + case 4: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TouchSensor *) node)->isActive; + return GF_OK; + case 5: + info->name = "isOver"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TouchSensor *) node)->isOver; + return GF_OK; + case 6: + info->name = "touchTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_TouchSensor *) node)->touchTime; + return GF_OK; + case 7: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TouchSensor *)node)->metadata; + return GF_OK; + case 8: + info->name = "description"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_TouchSensor *) node)->description; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TouchSensor_get_field_index_by_name(char *name) +{ + if (!strcmp("enabled", name)) return 0; + if (!strcmp("hitNormal_changed", name)) return 1; + if (!strcmp("hitPoint_changed", name)) return 2; + if (!strcmp("hitTexCoord_changed", name)) return 3; + if (!strcmp("isActive", name)) return 4; + if (!strcmp("isOver", name)) return 5; + if (!strcmp("touchTime", name)) return 6; + if (!strcmp("metadata", name)) return 7; + if (!strcmp("description", name)) return 8; + return -1; + } + + +static GF_Node *TouchSensor_Create() +{ + X_TouchSensor *p; + GF_SAFEALLOC(p, X_TouchSensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TouchSensor); + + /*default field values*/ + p->enabled = 1; + return (GF_Node *)p; +} + + +/* + Transform Node deletion +*/ + +static void Transform_Del(GF_Node *node) +{ + X_Transform *p = (X_Transform *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_sg_vrml_parent_destroy(node); + gf_node_free((GF_Node *)p); +} + + +static u32 Transform_get_field_count(GF_Node *node, u8 dummy) +{ + return 9; +} + +static GF_Err Transform_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "addChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Transform *)node)->on_addChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Transform *)node)->addChildren; + return GF_OK; + case 1: + info->name = "removeChildren"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Transform *)node)->on_removeChildren; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Transform *)node)->removeChildren; + return GF_OK; + case 2: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_Transform *) node)->center; + return GF_OK; + case 3: + info->name = "children"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFNODE; + info->NDTtype = NDT_SF3DNode; + info->far_ptr = & ((X_Transform *)node)->children; + return GF_OK; + case 4: + info->name = "rotation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((X_Transform *) node)->rotation; + return GF_OK; + case 5: + info->name = "scale"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_Transform *) node)->scale; + return GF_OK; + case 6: + info->name = "scaleOrientation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((X_Transform *) node)->scaleOrientation; + return GF_OK; + case 7: + info->name = "translation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_Transform *) node)->translation; + return GF_OK; + case 8: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Transform *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Transform_get_field_index_by_name(char *name) +{ + if (!strcmp("addChildren", name)) return 0; + if (!strcmp("removeChildren", name)) return 1; + if (!strcmp("center", name)) return 2; + if (!strcmp("children", name)) return 3; + if (!strcmp("rotation", name)) return 4; + if (!strcmp("scale", name)) return 5; + if (!strcmp("scaleOrientation", name)) return 6; + if (!strcmp("translation", name)) return 7; + if (!strcmp("metadata", name)) return 8; + return -1; + } + + +static GF_Node *Transform_Create() +{ + X_Transform *p; + GF_SAFEALLOC(p, X_Transform); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Transform); + gf_sg_vrml_parent_setup((GF_Node *) p); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->center.z = FLT2FIX(0); + p->rotation.x = FLT2FIX(0); + p->rotation.y = FLT2FIX(0); + p->rotation.z = FLT2FIX(1); + p->rotation.q = FLT2FIX(0); + p->scale.x = FLT2FIX(1); + p->scale.y = FLT2FIX(1); + p->scale.z = FLT2FIX(1); + p->scaleOrientation.x = FLT2FIX(0); + p->scaleOrientation.y = FLT2FIX(0); + p->scaleOrientation.z = FLT2FIX(1); + p->scaleOrientation.q = FLT2FIX(0); + p->translation.x = FLT2FIX(0); + p->translation.y = FLT2FIX(0); + p->translation.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + TriangleFanSet Node deletion +*/ + +static void TriangleFanSet_Del(GF_Node *node) +{ + X_TriangleFanSet *p = (X_TriangleFanSet *) node; + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->coord, node); + gf_sg_mfint32_del(p->fanCount); + gf_node_unregister((GF_Node *) p->normal, node); + gf_node_unregister((GF_Node *) p->texCoord, node); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 TriangleFanSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 10; +} + +static GF_Err TriangleFanSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_TriangleFanSet *)node)->color; + return GF_OK; + case 1: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((X_TriangleFanSet *)node)->coord; + return GF_OK; + case 2: + info->name = "fanCount"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_TriangleFanSet *) node)->fanCount; + return GF_OK; + case 3: + info->name = "normal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNormalNode; + info->far_ptr = & ((X_TriangleFanSet *)node)->normal; + return GF_OK; + case 4: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((X_TriangleFanSet *)node)->texCoord; + return GF_OK; + case 5: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleFanSet *) node)->ccw; + return GF_OK; + case 6: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleFanSet *) node)->colorPerVertex; + return GF_OK; + case 7: + info->name = "normalPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleFanSet *) node)->normalPerVertex; + return GF_OK; + case 8: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleFanSet *) node)->solid; + return GF_OK; + case 9: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TriangleFanSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TriangleFanSet_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("coord", name)) return 1; + if (!strcmp("fanCount", name)) return 2; + if (!strcmp("normal", name)) return 3; + if (!strcmp("texCoord", name)) return 4; + if (!strcmp("ccw", name)) return 5; + if (!strcmp("colorPerVertex", name)) return 6; + if (!strcmp("normalPerVertex", name)) return 7; + if (!strcmp("solid", name)) return 8; + if (!strcmp("metadata", name)) return 9; + return -1; + } + + +static GF_Node *TriangleFanSet_Create() +{ + X_TriangleFanSet *p; + GF_SAFEALLOC(p, X_TriangleFanSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TriangleFanSet); + + /*default field values*/ + p->ccw = 1; + p->colorPerVertex = 1; + p->normalPerVertex = 1; + p->solid = 1; + return (GF_Node *)p; +} + + +/* + TriangleSet Node deletion +*/ + +static void TriangleSet_Del(GF_Node *node) +{ + X_TriangleSet *p = (X_TriangleSet *) node; + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->coord, node); + gf_node_unregister((GF_Node *) p->normal, node); + gf_node_unregister((GF_Node *) p->texCoord, node); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 TriangleSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 9; +} + +static GF_Err TriangleSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_TriangleSet *)node)->color; + return GF_OK; + case 1: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((X_TriangleSet *)node)->coord; + return GF_OK; + case 2: + info->name = "normal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNormalNode; + info->far_ptr = & ((X_TriangleSet *)node)->normal; + return GF_OK; + case 3: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((X_TriangleSet *)node)->texCoord; + return GF_OK; + case 4: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleSet *) node)->ccw; + return GF_OK; + case 5: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleSet *) node)->colorPerVertex; + return GF_OK; + case 6: + info->name = "normalPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleSet *) node)->normalPerVertex; + return GF_OK; + case 7: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleSet *) node)->solid; + return GF_OK; + case 8: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TriangleSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TriangleSet_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("coord", name)) return 1; + if (!strcmp("normal", name)) return 2; + if (!strcmp("texCoord", name)) return 3; + if (!strcmp("ccw", name)) return 4; + if (!strcmp("colorPerVertex", name)) return 5; + if (!strcmp("normalPerVertex", name)) return 6; + if (!strcmp("solid", name)) return 7; + if (!strcmp("metadata", name)) return 8; + return -1; + } + + +static GF_Node *TriangleSet_Create() +{ + X_TriangleSet *p; + GF_SAFEALLOC(p, X_TriangleSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TriangleSet); + + /*default field values*/ + p->ccw = 1; + p->colorPerVertex = 1; + p->normalPerVertex = 1; + p->solid = 1; + return (GF_Node *)p; +} + + +/* + TriangleSet2D Node deletion +*/ + +static void TriangleSet2D_Del(GF_Node *node) +{ + X_TriangleSet2D *p = (X_TriangleSet2D *) node; + gf_sg_mfvec2f_del(p->vertices); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 TriangleSet2D_get_field_count(GF_Node *node, u8 dummy) +{ + return 2; +} + +static GF_Err TriangleSet2D_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "vertices"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFVEC2F; + info->far_ptr = & ((X_TriangleSet2D *) node)->vertices; + return GF_OK; + case 1: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TriangleSet2D *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TriangleSet2D_get_field_index_by_name(char *name) +{ + if (!strcmp("vertices", name)) return 0; + if (!strcmp("metadata", name)) return 1; + return -1; + } + + +static GF_Node *TriangleSet2D_Create() +{ + X_TriangleSet2D *p; + GF_SAFEALLOC(p, X_TriangleSet2D); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TriangleSet2D); + + /*default field values*/ + return (GF_Node *)p; +} + + +/* + TriangleStripSet Node deletion +*/ + +static void TriangleStripSet_Del(GF_Node *node) +{ + X_TriangleStripSet *p = (X_TriangleStripSet *) node; + gf_node_unregister((GF_Node *) p->color, node); + gf_node_unregister((GF_Node *) p->coord, node); + gf_node_unregister((GF_Node *) p->normal, node); + gf_sg_mfint32_del(p->stripCount); + gf_node_unregister((GF_Node *) p->texCoord, node); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 TriangleStripSet_get_field_count(GF_Node *node, u8 dummy) +{ + return 10; +} + +static GF_Err TriangleStripSet_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "color"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFColorNode; + info->far_ptr = & ((X_TriangleStripSet *)node)->color; + return GF_OK; + case 1: + info->name = "coord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFCoordinateNode; + info->far_ptr = & ((X_TriangleStripSet *)node)->coord; + return GF_OK; + case 2: + info->name = "normal"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFNormalNode; + info->far_ptr = & ((X_TriangleStripSet *)node)->normal; + return GF_OK; + case 3: + info->name = "stripCount"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_MFINT32; + info->far_ptr = & ((X_TriangleStripSet *) node)->stripCount; + return GF_OK; + case 4: + info->name = "texCoord"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFTextureCoordinateNode; + info->far_ptr = & ((X_TriangleStripSet *)node)->texCoord; + return GF_OK; + case 5: + info->name = "ccw"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleStripSet *) node)->ccw; + return GF_OK; + case 6: + info->name = "colorPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleStripSet *) node)->colorPerVertex; + return GF_OK; + case 7: + info->name = "normalPerVertex"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleStripSet *) node)->normalPerVertex; + return GF_OK; + case 8: + info->name = "solid"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_TriangleStripSet *) node)->solid; + return GF_OK; + case 9: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_TriangleStripSet *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 TriangleStripSet_get_field_index_by_name(char *name) +{ + if (!strcmp("color", name)) return 0; + if (!strcmp("coord", name)) return 1; + if (!strcmp("normal", name)) return 2; + if (!strcmp("stripCount", name)) return 3; + if (!strcmp("texCoord", name)) return 4; + if (!strcmp("ccw", name)) return 5; + if (!strcmp("colorPerVertex", name)) return 6; + if (!strcmp("normalPerVertex", name)) return 7; + if (!strcmp("solid", name)) return 8; + if (!strcmp("metadata", name)) return 9; + return -1; + } + + +static GF_Node *TriangleStripSet_Create() +{ + X_TriangleStripSet *p; + GF_SAFEALLOC(p, X_TriangleStripSet); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_TriangleStripSet); + + /*default field values*/ + p->ccw = 1; + p->colorPerVertex = 1; + p->normalPerVertex = 1; + p->solid = 1; + return (GF_Node *)p; +} + + +/* + Viewpoint Node deletion +*/ + +static void Viewpoint_Del(GF_Node *node) +{ + X_Viewpoint *p = (X_Viewpoint *) node; + gf_sg_sfstring_del(p->description); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 Viewpoint_get_field_count(GF_Node *node, u8 dummy) +{ + return 10; +} + +static GF_Err Viewpoint_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "set_bind"; + info->eventType = GF_SG_EVENT_IN; + info->on_event_in = ((X_Viewpoint *)node)->on_set_bind; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Viewpoint *) node)->set_bind; + return GF_OK; + case 1: + info->name = "fieldOfView"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFFLOAT; + info->far_ptr = & ((X_Viewpoint *) node)->fieldOfView; + return GF_OK; + case 2: + info->name = "jump"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Viewpoint *) node)->jump; + return GF_OK; + case 3: + info->name = "orientation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFROTATION; + info->far_ptr = & ((X_Viewpoint *) node)->orientation; + return GF_OK; + case 4: + info->name = "position"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_Viewpoint *) node)->position; + return GF_OK; + case 5: + info->name = "description"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_Viewpoint *) node)->description; + return GF_OK; + case 6: + info->name = "bindTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_Viewpoint *) node)->bindTime; + return GF_OK; + case 7: + info->name = "isBound"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_Viewpoint *) node)->isBound; + return GF_OK; + case 8: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_Viewpoint *)node)->metadata; + return GF_OK; + case 9: + info->name = "centerOfRotation"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_Viewpoint *) node)->centerOfRotation; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 Viewpoint_get_field_index_by_name(char *name) +{ + if (!strcmp("set_bind", name)) return 0; + if (!strcmp("fieldOfView", name)) return 1; + if (!strcmp("jump", name)) return 2; + if (!strcmp("orientation", name)) return 3; + if (!strcmp("position", name)) return 4; + if (!strcmp("description", name)) return 5; + if (!strcmp("bindTime", name)) return 6; + if (!strcmp("isBound", name)) return 7; + if (!strcmp("metadata", name)) return 8; + if (!strcmp("centerOfRotation", name)) return 9; + return -1; + } + + +static GF_Node *Viewpoint_Create() +{ + X_Viewpoint *p; + GF_SAFEALLOC(p, X_Viewpoint); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_Viewpoint); + + /*default field values*/ + p->fieldOfView = FLT2FIX(0.785398); + p->jump = 1; + p->orientation.x = FLT2FIX(0); + p->orientation.y = FLT2FIX(0); + p->orientation.z = FLT2FIX(1); + p->orientation.q = FLT2FIX(0); + p->position.x = FLT2FIX(0); + p->position.y = FLT2FIX(0); + p->position.z = FLT2FIX(10); + p->centerOfRotation.x = FLT2FIX(0); + p->centerOfRotation.y = FLT2FIX(0); + p->centerOfRotation.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + VisibilitySensor Node deletion +*/ + +static void VisibilitySensor_Del(GF_Node *node) +{ + X_VisibilitySensor *p = (X_VisibilitySensor *) node; + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 VisibilitySensor_get_field_count(GF_Node *node, u8 dummy) +{ + return 7; +} + +static GF_Err VisibilitySensor_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "center"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_VisibilitySensor *) node)->center; + return GF_OK; + case 1: + info->name = "enabled"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_VisibilitySensor *) node)->enabled; + return GF_OK; + case 2: + info->name = "size"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFVEC3F; + info->far_ptr = & ((X_VisibilitySensor *) node)->size; + return GF_OK; + case 3: + info->name = "enterTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_VisibilitySensor *) node)->enterTime; + return GF_OK; + case 4: + info->name = "exitTime"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFTIME; + info->far_ptr = & ((X_VisibilitySensor *) node)->exitTime; + return GF_OK; + case 5: + info->name = "isActive"; + info->eventType = GF_SG_EVENT_OUT; + info->fieldType = GF_SG_VRML_SFBOOL; + info->far_ptr = & ((X_VisibilitySensor *) node)->isActive; + return GF_OK; + case 6: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_VisibilitySensor *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 VisibilitySensor_get_field_index_by_name(char *name) +{ + if (!strcmp("center", name)) return 0; + if (!strcmp("enabled", name)) return 1; + if (!strcmp("size", name)) return 2; + if (!strcmp("enterTime", name)) return 3; + if (!strcmp("exitTime", name)) return 4; + if (!strcmp("isActive", name)) return 5; + if (!strcmp("metadata", name)) return 6; + return -1; + } + + +static GF_Node *VisibilitySensor_Create() +{ + X_VisibilitySensor *p; + GF_SAFEALLOC(p, X_VisibilitySensor); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_VisibilitySensor); + + /*default field values*/ + p->center.x = FLT2FIX(0); + p->center.y = FLT2FIX(0); + p->center.z = FLT2FIX(0); + p->enabled = 1; + p->size.x = FLT2FIX(0); + p->size.y = FLT2FIX(0); + p->size.z = FLT2FIX(0); + return (GF_Node *)p; +} + + +/* + WorldInfo Node deletion +*/ + +static void WorldInfo_Del(GF_Node *node) +{ + X_WorldInfo *p = (X_WorldInfo *) node; + gf_sg_mfstring_del(p->info); + gf_sg_sfstring_del(p->title); + gf_node_unregister((GF_Node *) p->metadata, node); + gf_node_free((GF_Node *)p); +} + + +static u32 WorldInfo_get_field_count(GF_Node *node, u8 dummy) +{ + return 3; +} + +static GF_Err WorldInfo_get_field(GF_Node *node, GF_FieldInfo *info) +{ + switch (info->fieldIndex) { + case 0: + info->name = "info"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_MFSTRING; + info->far_ptr = & ((X_WorldInfo *) node)->info; + return GF_OK; + case 1: + info->name = "title"; + info->eventType = GF_SG_EVENT_FIELD; + info->fieldType = GF_SG_VRML_SFSTRING; + info->far_ptr = & ((X_WorldInfo *) node)->title; + return GF_OK; + case 2: + info->name = "metadata"; + info->eventType = GF_SG_EVENT_EXPOSED_FIELD; + info->fieldType = GF_SG_VRML_SFNODE; + info->NDTtype = NDT_SFMetadataNode; + info->far_ptr = & ((X_WorldInfo *)node)->metadata; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +static s32 WorldInfo_get_field_index_by_name(char *name) +{ + if (!strcmp("info", name)) return 0; + if (!strcmp("title", name)) return 1; + if (!strcmp("metadata", name)) return 2; + return -1; + } + + +static GF_Node *WorldInfo_Create() +{ + X_WorldInfo *p; + GF_SAFEALLOC(p, X_WorldInfo); + if(!p) return NULL; + gf_node_setup((GF_Node *)p, TAG_X3D_WorldInfo); + + /*default field values*/ + return (GF_Node *)p; +} + + + + +GF_Node *gf_sg_x3d_node_new(u32 NodeTag) +{ + switch (NodeTag) { + case TAG_X3D_Anchor: + return Anchor_Create(); + case TAG_X3D_Appearance: + return Appearance_Create(); + case TAG_X3D_Arc2D: + return Arc2D_Create(); + case TAG_X3D_ArcClose2D: + return ArcClose2D_Create(); + case TAG_X3D_AudioClip: + return AudioClip_Create(); + case TAG_X3D_Background: + return Background_Create(); + case TAG_X3D_Billboard: + return Billboard_Create(); + case TAG_X3D_BooleanFilter: + return BooleanFilter_Create(); + case TAG_X3D_BooleanSequencer: + return BooleanSequencer_Create(); + case TAG_X3D_BooleanToggle: + return BooleanToggle_Create(); + case TAG_X3D_BooleanTrigger: + return BooleanTrigger_Create(); + case TAG_X3D_Box: + return Box_Create(); + case TAG_X3D_Circle2D: + return Circle2D_Create(); + case TAG_X3D_Collision: + return Collision_Create(); + case TAG_X3D_Color: + return Color_Create(); + case TAG_X3D_ColorInterpolator: + return ColorInterpolator_Create(); + case TAG_X3D_ColorRGBA: + return ColorRGBA_Create(); + case TAG_X3D_Cone: + return Cone_Create(); + case TAG_X3D_Contour2D: + return Contour2D_Create(); + case TAG_X3D_ContourPolyline2D: + return ContourPolyline2D_Create(); + case TAG_X3D_Coordinate: + return Coordinate_Create(); + case TAG_X3D_CoordinateDouble: + return CoordinateDouble_Create(); + case TAG_X3D_Coordinate2D: + return Coordinate2D_Create(); + case TAG_X3D_CoordinateInterpolator: + return CoordinateInterpolator_Create(); + case TAG_X3D_CoordinateInterpolator2D: + return CoordinateInterpolator2D_Create(); + case TAG_X3D_Cylinder: + return Cylinder_Create(); + case TAG_X3D_CylinderSensor: + return CylinderSensor_Create(); + case TAG_X3D_DirectionalLight: + return DirectionalLight_Create(); + case TAG_X3D_Disk2D: + return Disk2D_Create(); + case TAG_X3D_ElevationGrid: + return ElevationGrid_Create(); + case TAG_X3D_Extrusion: + return Extrusion_Create(); + case TAG_X3D_FillProperties: + return FillProperties_Create(); + case TAG_X3D_Fog: + return Fog_Create(); + case TAG_X3D_FontStyle: + return FontStyle_Create(); + case TAG_X3D_Group: + return Group_Create(); + case TAG_X3D_ImageTexture: + return ImageTexture_Create(); + case TAG_X3D_IndexedFaceSet: + return IndexedFaceSet_Create(); + case TAG_X3D_IndexedLineSet: + return IndexedLineSet_Create(); + case TAG_X3D_IndexedTriangleFanSet: + return IndexedTriangleFanSet_Create(); + case TAG_X3D_IndexedTriangleSet: + return IndexedTriangleSet_Create(); + case TAG_X3D_IndexedTriangleStripSet: + return IndexedTriangleStripSet_Create(); + case TAG_X3D_Inline: + return Inline_Create(); + case TAG_X3D_IntegerSequencer: + return IntegerSequencer_Create(); + case TAG_X3D_IntegerTrigger: + return IntegerTrigger_Create(); + case TAG_X3D_KeySensor: + return KeySensor_Create(); + case TAG_X3D_LineProperties: + return LineProperties_Create(); + case TAG_X3D_LineSet: + return LineSet_Create(); + case TAG_X3D_LOD: + return LOD_Create(); + case TAG_X3D_Material: + return Material_Create(); + case TAG_X3D_MetadataDouble: + return MetadataDouble_Create(); + case TAG_X3D_MetadataFloat: + return MetadataFloat_Create(); + case TAG_X3D_MetadataInteger: + return MetadataInteger_Create(); + case TAG_X3D_MetadataSet: + return MetadataSet_Create(); + case TAG_X3D_MetadataString: + return MetadataString_Create(); + case TAG_X3D_MovieTexture: + return MovieTexture_Create(); + case TAG_X3D_MultiTexture: + return MultiTexture_Create(); + case TAG_X3D_MultiTextureCoordinate: + return MultiTextureCoordinate_Create(); + case TAG_X3D_MultiTextureTransform: + return MultiTextureTransform_Create(); + case TAG_X3D_NavigationInfo: + return NavigationInfo_Create(); + case TAG_X3D_Normal: + return Normal_Create(); + case TAG_X3D_NormalInterpolator: + return NormalInterpolator_Create(); + case TAG_X3D_OrientationInterpolator: + return OrientationInterpolator_Create(); + case TAG_X3D_PixelTexture: + return PixelTexture_Create(); + case TAG_X3D_PlaneSensor: + return PlaneSensor_Create(); + case TAG_X3D_PointLight: + return PointLight_Create(); + case TAG_X3D_PointSet: + return PointSet_Create(); + case TAG_X3D_Polyline2D: + return Polyline2D_Create(); + case TAG_X3D_Polypoint2D: + return Polypoint2D_Create(); + case TAG_X3D_PositionInterpolator: + return PositionInterpolator_Create(); + case TAG_X3D_PositionInterpolator2D: + return PositionInterpolator2D_Create(); + case TAG_X3D_ProximitySensor: + return ProximitySensor_Create(); + case TAG_X3D_Rectangle2D: + return Rectangle2D_Create(); + case TAG_X3D_ScalarInterpolator: + return ScalarInterpolator_Create(); + case TAG_X3D_Script: + return Script_Create(); + case TAG_X3D_Shape: + return Shape_Create(); + case TAG_X3D_Sound: + return Sound_Create(); + case TAG_X3D_Sphere: + return Sphere_Create(); + case TAG_X3D_SphereSensor: + return SphereSensor_Create(); + case TAG_X3D_SpotLight: + return SpotLight_Create(); + case TAG_X3D_StaticGroup: + return StaticGroup_Create(); + case TAG_X3D_StringSensor: + return StringSensor_Create(); + case TAG_X3D_Switch: + return Switch_Create(); + case TAG_X3D_Text: + return Text_Create(); + case TAG_X3D_TextureBackground: + return TextureBackground_Create(); + case TAG_X3D_TextureCoordinate: + return TextureCoordinate_Create(); + case TAG_X3D_TextureCoordinateGenerator: + return TextureCoordinateGenerator_Create(); + case TAG_X3D_TextureTransform: + return TextureTransform_Create(); + case TAG_X3D_TimeSensor: + return TimeSensor_Create(); + case TAG_X3D_TimeTrigger: + return TimeTrigger_Create(); + case TAG_X3D_TouchSensor: + return TouchSensor_Create(); + case TAG_X3D_Transform: + return Transform_Create(); + case TAG_X3D_TriangleFanSet: + return TriangleFanSet_Create(); + case TAG_X3D_TriangleSet: + return TriangleSet_Create(); + case TAG_X3D_TriangleSet2D: + return TriangleSet2D_Create(); + case TAG_X3D_TriangleStripSet: + return TriangleStripSet_Create(); + case TAG_X3D_Viewpoint: + return Viewpoint_Create(); + case TAG_X3D_VisibilitySensor: + return VisibilitySensor_Create(); + case TAG_X3D_WorldInfo: + return WorldInfo_Create(); + default: + return NULL; + } +} + +const char *gf_sg_x3d_node_get_class_name(u32 NodeTag) +{ + switch (NodeTag) { + case TAG_X3D_Anchor: + return "Anchor"; + case TAG_X3D_Appearance: + return "Appearance"; + case TAG_X3D_Arc2D: + return "Arc2D"; + case TAG_X3D_ArcClose2D: + return "ArcClose2D"; + case TAG_X3D_AudioClip: + return "AudioClip"; + case TAG_X3D_Background: + return "Background"; + case TAG_X3D_Billboard: + return "Billboard"; + case TAG_X3D_BooleanFilter: + return "BooleanFilter"; + case TAG_X3D_BooleanSequencer: + return "BooleanSequencer"; + case TAG_X3D_BooleanToggle: + return "BooleanToggle"; + case TAG_X3D_BooleanTrigger: + return "BooleanTrigger"; + case TAG_X3D_Box: + return "Box"; + case TAG_X3D_Circle2D: + return "Circle2D"; + case TAG_X3D_Collision: + return "Collision"; + case TAG_X3D_Color: + return "Color"; + case TAG_X3D_ColorInterpolator: + return "ColorInterpolator"; + case TAG_X3D_ColorRGBA: + return "ColorRGBA"; + case TAG_X3D_Cone: + return "Cone"; + case TAG_X3D_Contour2D: + return "Contour2D"; + case TAG_X3D_ContourPolyline2D: + return "ContourPolyline2D"; + case TAG_X3D_Coordinate: + return "Coordinate"; + case TAG_X3D_CoordinateDouble: + return "CoordinateDouble"; + case TAG_X3D_Coordinate2D: + return "Coordinate2D"; + case TAG_X3D_CoordinateInterpolator: + return "CoordinateInterpolator"; + case TAG_X3D_CoordinateInterpolator2D: + return "CoordinateInterpolator2D"; + case TAG_X3D_Cylinder: + return "Cylinder"; + case TAG_X3D_CylinderSensor: + return "CylinderSensor"; + case TAG_X3D_DirectionalLight: + return "DirectionalLight"; + case TAG_X3D_Disk2D: + return "Disk2D"; + case TAG_X3D_ElevationGrid: + return "ElevationGrid"; + case TAG_X3D_Extrusion: + return "Extrusion"; + case TAG_X3D_FillProperties: + return "FillProperties"; + case TAG_X3D_Fog: + return "Fog"; + case TAG_X3D_FontStyle: + return "FontStyle"; + case TAG_X3D_Group: + return "Group"; + case TAG_X3D_ImageTexture: + return "ImageTexture"; + case TAG_X3D_IndexedFaceSet: + return "IndexedFaceSet"; + case TAG_X3D_IndexedLineSet: + return "IndexedLineSet"; + case TAG_X3D_IndexedTriangleFanSet: + return "IndexedTriangleFanSet"; + case TAG_X3D_IndexedTriangleSet: + return "IndexedTriangleSet"; + case TAG_X3D_IndexedTriangleStripSet: + return "IndexedTriangleStripSet"; + case TAG_X3D_Inline: + return "Inline"; + case TAG_X3D_IntegerSequencer: + return "IntegerSequencer"; + case TAG_X3D_IntegerTrigger: + return "IntegerTrigger"; + case TAG_X3D_KeySensor: + return "KeySensor"; + case TAG_X3D_LineProperties: + return "LineProperties"; + case TAG_X3D_LineSet: + return "LineSet"; + case TAG_X3D_LOD: + return "LOD"; + case TAG_X3D_Material: + return "Material"; + case TAG_X3D_MetadataDouble: + return "MetadataDouble"; + case TAG_X3D_MetadataFloat: + return "MetadataFloat"; + case TAG_X3D_MetadataInteger: + return "MetadataInteger"; + case TAG_X3D_MetadataSet: + return "MetadataSet"; + case TAG_X3D_MetadataString: + return "MetadataString"; + case TAG_X3D_MovieTexture: + return "MovieTexture"; + case TAG_X3D_MultiTexture: + return "MultiTexture"; + case TAG_X3D_MultiTextureCoordinate: + return "MultiTextureCoordinate"; + case TAG_X3D_MultiTextureTransform: + return "MultiTextureTransform"; + case TAG_X3D_NavigationInfo: + return "NavigationInfo"; + case TAG_X3D_Normal: + return "Normal"; + case TAG_X3D_NormalInterpolator: + return "NormalInterpolator"; + case TAG_X3D_OrientationInterpolator: + return "OrientationInterpolator"; + case TAG_X3D_PixelTexture: + return "PixelTexture"; + case TAG_X3D_PlaneSensor: + return "PlaneSensor"; + case TAG_X3D_PointLight: + return "PointLight"; + case TAG_X3D_PointSet: + return "PointSet"; + case TAG_X3D_Polyline2D: + return "Polyline2D"; + case TAG_X3D_Polypoint2D: + return "Polypoint2D"; + case TAG_X3D_PositionInterpolator: + return "PositionInterpolator"; + case TAG_X3D_PositionInterpolator2D: + return "PositionInterpolator2D"; + case TAG_X3D_ProximitySensor: + return "ProximitySensor"; + case TAG_X3D_Rectangle2D: + return "Rectangle2D"; + case TAG_X3D_ScalarInterpolator: + return "ScalarInterpolator"; + case TAG_X3D_Script: + return "Script"; + case TAG_X3D_Shape: + return "Shape"; + case TAG_X3D_Sound: + return "Sound"; + case TAG_X3D_Sphere: + return "Sphere"; + case TAG_X3D_SphereSensor: + return "SphereSensor"; + case TAG_X3D_SpotLight: + return "SpotLight"; + case TAG_X3D_StaticGroup: + return "StaticGroup"; + case TAG_X3D_StringSensor: + return "StringSensor"; + case TAG_X3D_Switch: + return "Switch"; + case TAG_X3D_Text: + return "Text"; + case TAG_X3D_TextureBackground: + return "TextureBackground"; + case TAG_X3D_TextureCoordinate: + return "TextureCoordinate"; + case TAG_X3D_TextureCoordinateGenerator: + return "TextureCoordinateGenerator"; + case TAG_X3D_TextureTransform: + return "TextureTransform"; + case TAG_X3D_TimeSensor: + return "TimeSensor"; + case TAG_X3D_TimeTrigger: + return "TimeTrigger"; + case TAG_X3D_TouchSensor: + return "TouchSensor"; + case TAG_X3D_Transform: + return "Transform"; + case TAG_X3D_TriangleFanSet: + return "TriangleFanSet"; + case TAG_X3D_TriangleSet: + return "TriangleSet"; + case TAG_X3D_TriangleSet2D: + return "TriangleSet2D"; + case TAG_X3D_TriangleStripSet: + return "TriangleStripSet"; + case TAG_X3D_Viewpoint: + return "Viewpoint"; + case TAG_X3D_VisibilitySensor: + return "VisibilitySensor"; + case TAG_X3D_WorldInfo: + return "WorldInfo"; + default: + return "Unknown Node"; + } +} + +void gf_sg_x3d_node_del(GF_Node *node) +{ + switch (node->sgprivate->tag) { + case TAG_X3D_Anchor: + Anchor_Del(node); return; + case TAG_X3D_Appearance: + Appearance_Del(node); return; + case TAG_X3D_Arc2D: + Arc2D_Del(node); return; + case TAG_X3D_ArcClose2D: + ArcClose2D_Del(node); return; + case TAG_X3D_AudioClip: + AudioClip_Del(node); return; + case TAG_X3D_Background: + Background_Del(node); return; + case TAG_X3D_Billboard: + Billboard_Del(node); return; + case TAG_X3D_BooleanFilter: + BooleanFilter_Del(node); return; + case TAG_X3D_BooleanSequencer: + BooleanSequencer_Del(node); return; + case TAG_X3D_BooleanToggle: + BooleanToggle_Del(node); return; + case TAG_X3D_BooleanTrigger: + BooleanTrigger_Del(node); return; + case TAG_X3D_Box: + Box_Del(node); return; + case TAG_X3D_Circle2D: + Circle2D_Del(node); return; + case TAG_X3D_Collision: + Collision_Del(node); return; + case TAG_X3D_Color: + Color_Del(node); return; + case TAG_X3D_ColorInterpolator: + ColorInterpolator_Del(node); return; + case TAG_X3D_ColorRGBA: + ColorRGBA_Del(node); return; + case TAG_X3D_Cone: + Cone_Del(node); return; + case TAG_X3D_Contour2D: + Contour2D_Del(node); return; + case TAG_X3D_ContourPolyline2D: + ContourPolyline2D_Del(node); return; + case TAG_X3D_Coordinate: + Coordinate_Del(node); return; + case TAG_X3D_CoordinateDouble: + CoordinateDouble_Del(node); return; + case TAG_X3D_Coordinate2D: + Coordinate2D_Del(node); return; + case TAG_X3D_CoordinateInterpolator: + CoordinateInterpolator_Del(node); return; + case TAG_X3D_CoordinateInterpolator2D: + CoordinateInterpolator2D_Del(node); return; + case TAG_X3D_Cylinder: + Cylinder_Del(node); return; + case TAG_X3D_CylinderSensor: + CylinderSensor_Del(node); return; + case TAG_X3D_DirectionalLight: + DirectionalLight_Del(node); return; + case TAG_X3D_Disk2D: + Disk2D_Del(node); return; + case TAG_X3D_ElevationGrid: + ElevationGrid_Del(node); return; + case TAG_X3D_Extrusion: + Extrusion_Del(node); return; + case TAG_X3D_FillProperties: + FillProperties_Del(node); return; + case TAG_X3D_Fog: + Fog_Del(node); return; + case TAG_X3D_FontStyle: + FontStyle_Del(node); return; + case TAG_X3D_Group: + Group_Del(node); return; + case TAG_X3D_ImageTexture: + ImageTexture_Del(node); return; + case TAG_X3D_IndexedFaceSet: + IndexedFaceSet_Del(node); return; + case TAG_X3D_IndexedLineSet: + IndexedLineSet_Del(node); return; + case TAG_X3D_IndexedTriangleFanSet: + IndexedTriangleFanSet_Del(node); return; + case TAG_X3D_IndexedTriangleSet: + IndexedTriangleSet_Del(node); return; + case TAG_X3D_IndexedTriangleStripSet: + IndexedTriangleStripSet_Del(node); return; + case TAG_X3D_Inline: + Inline_Del(node); return; + case TAG_X3D_IntegerSequencer: + IntegerSequencer_Del(node); return; + case TAG_X3D_IntegerTrigger: + IntegerTrigger_Del(node); return; + case TAG_X3D_KeySensor: + KeySensor_Del(node); return; + case TAG_X3D_LineProperties: + LineProperties_Del(node); return; + case TAG_X3D_LineSet: + LineSet_Del(node); return; + case TAG_X3D_LOD: + LOD_Del(node); return; + case TAG_X3D_Material: + Material_Del(node); return; + case TAG_X3D_MetadataDouble: + MetadataDouble_Del(node); return; + case TAG_X3D_MetadataFloat: + MetadataFloat_Del(node); return; + case TAG_X3D_MetadataInteger: + MetadataInteger_Del(node); return; + case TAG_X3D_MetadataSet: + MetadataSet_Del(node); return; + case TAG_X3D_MetadataString: + MetadataString_Del(node); return; + case TAG_X3D_MovieTexture: + MovieTexture_Del(node); return; + case TAG_X3D_MultiTexture: + MultiTexture_Del(node); return; + case TAG_X3D_MultiTextureCoordinate: + MultiTextureCoordinate_Del(node); return; + case TAG_X3D_MultiTextureTransform: + MultiTextureTransform_Del(node); return; + case TAG_X3D_NavigationInfo: + NavigationInfo_Del(node); return; + case TAG_X3D_Normal: + Normal_Del(node); return; + case TAG_X3D_NormalInterpolator: + NormalInterpolator_Del(node); return; + case TAG_X3D_OrientationInterpolator: + OrientationInterpolator_Del(node); return; + case TAG_X3D_PixelTexture: + PixelTexture_Del(node); return; + case TAG_X3D_PlaneSensor: + PlaneSensor_Del(node); return; + case TAG_X3D_PointLight: + PointLight_Del(node); return; + case TAG_X3D_PointSet: + PointSet_Del(node); return; + case TAG_X3D_Polyline2D: + Polyline2D_Del(node); return; + case TAG_X3D_Polypoint2D: + Polypoint2D_Del(node); return; + case TAG_X3D_PositionInterpolator: + PositionInterpolator_Del(node); return; + case TAG_X3D_PositionInterpolator2D: + PositionInterpolator2D_Del(node); return; + case TAG_X3D_ProximitySensor: + ProximitySensor_Del(node); return; + case TAG_X3D_Rectangle2D: + Rectangle2D_Del(node); return; + case TAG_X3D_ScalarInterpolator: + ScalarInterpolator_Del(node); return; + case TAG_X3D_Script: + Script_Del(node); return; + case TAG_X3D_Shape: + Shape_Del(node); return; + case TAG_X3D_Sound: + Sound_Del(node); return; + case TAG_X3D_Sphere: + Sphere_Del(node); return; + case TAG_X3D_SphereSensor: + SphereSensor_Del(node); return; + case TAG_X3D_SpotLight: + SpotLight_Del(node); return; + case TAG_X3D_StaticGroup: + StaticGroup_Del(node); return; + case TAG_X3D_StringSensor: + StringSensor_Del(node); return; + case TAG_X3D_Switch: + Switch_Del(node); return; + case TAG_X3D_Text: + Text_Del(node); return; + case TAG_X3D_TextureBackground: + TextureBackground_Del(node); return; + case TAG_X3D_TextureCoordinate: + TextureCoordinate_Del(node); return; + case TAG_X3D_TextureCoordinateGenerator: + TextureCoordinateGenerator_Del(node); return; + case TAG_X3D_TextureTransform: + TextureTransform_Del(node); return; + case TAG_X3D_TimeSensor: + TimeSensor_Del(node); return; + case TAG_X3D_TimeTrigger: + TimeTrigger_Del(node); return; + case TAG_X3D_TouchSensor: + TouchSensor_Del(node); return; + case TAG_X3D_Transform: + Transform_Del(node); return; + case TAG_X3D_TriangleFanSet: + TriangleFanSet_Del(node); return; + case TAG_X3D_TriangleSet: + TriangleSet_Del(node); return; + case TAG_X3D_TriangleSet2D: + TriangleSet2D_Del(node); return; + case TAG_X3D_TriangleStripSet: + TriangleStripSet_Del(node); return; + case TAG_X3D_Viewpoint: + Viewpoint_Del(node); return; + case TAG_X3D_VisibilitySensor: + VisibilitySensor_Del(node); return; + case TAG_X3D_WorldInfo: + WorldInfo_Del(node); return; + default: + return; + } +} + +u32 gf_sg_x3d_node_get_field_count(GF_Node *node) +{ + switch (node->sgprivate->tag) { + case TAG_X3D_Anchor:return Anchor_get_field_count(node, 0); + case TAG_X3D_Appearance:return Appearance_get_field_count(node, 0); + case TAG_X3D_Arc2D:return Arc2D_get_field_count(node, 0); + case TAG_X3D_ArcClose2D:return ArcClose2D_get_field_count(node, 0); + case TAG_X3D_AudioClip:return AudioClip_get_field_count(node, 0); + case TAG_X3D_Background:return Background_get_field_count(node, 0); + case TAG_X3D_Billboard:return Billboard_get_field_count(node, 0); + case TAG_X3D_BooleanFilter:return BooleanFilter_get_field_count(node, 0); + case TAG_X3D_BooleanSequencer:return BooleanSequencer_get_field_count(node, 0); + case TAG_X3D_BooleanToggle:return BooleanToggle_get_field_count(node, 0); + case TAG_X3D_BooleanTrigger:return BooleanTrigger_get_field_count(node, 0); + case TAG_X3D_Box:return Box_get_field_count(node, 0); + case TAG_X3D_Circle2D:return Circle2D_get_field_count(node, 0); + case TAG_X3D_Collision:return Collision_get_field_count(node, 0); + case TAG_X3D_Color:return Color_get_field_count(node, 0); + case TAG_X3D_ColorInterpolator:return ColorInterpolator_get_field_count(node, 0); + case TAG_X3D_ColorRGBA:return ColorRGBA_get_field_count(node, 0); + case TAG_X3D_Cone:return Cone_get_field_count(node, 0); + case TAG_X3D_Contour2D:return Contour2D_get_field_count(node, 0); + case TAG_X3D_ContourPolyline2D:return ContourPolyline2D_get_field_count(node, 0); + case TAG_X3D_Coordinate:return Coordinate_get_field_count(node, 0); + case TAG_X3D_CoordinateDouble:return CoordinateDouble_get_field_count(node, 0); + case TAG_X3D_Coordinate2D:return Coordinate2D_get_field_count(node, 0); + case TAG_X3D_CoordinateInterpolator:return CoordinateInterpolator_get_field_count(node, 0); + case TAG_X3D_CoordinateInterpolator2D:return CoordinateInterpolator2D_get_field_count(node, 0); + case TAG_X3D_Cylinder:return Cylinder_get_field_count(node, 0); + case TAG_X3D_CylinderSensor:return CylinderSensor_get_field_count(node, 0); + case TAG_X3D_DirectionalLight:return DirectionalLight_get_field_count(node, 0); + case TAG_X3D_Disk2D:return Disk2D_get_field_count(node, 0); + case TAG_X3D_ElevationGrid:return ElevationGrid_get_field_count(node, 0); + case TAG_X3D_Extrusion:return Extrusion_get_field_count(node, 0); + case TAG_X3D_FillProperties:return FillProperties_get_field_count(node, 0); + case TAG_X3D_Fog:return Fog_get_field_count(node, 0); + case TAG_X3D_FontStyle:return FontStyle_get_field_count(node, 0); + case TAG_X3D_Group:return Group_get_field_count(node, 0); + case TAG_X3D_ImageTexture:return ImageTexture_get_field_count(node, 0); + case TAG_X3D_IndexedFaceSet:return IndexedFaceSet_get_field_count(node, 0); + case TAG_X3D_IndexedLineSet:return IndexedLineSet_get_field_count(node, 0); + case TAG_X3D_IndexedTriangleFanSet:return IndexedTriangleFanSet_get_field_count(node, 0); + case TAG_X3D_IndexedTriangleSet:return IndexedTriangleSet_get_field_count(node, 0); + case TAG_X3D_IndexedTriangleStripSet:return IndexedTriangleStripSet_get_field_count(node, 0); + case TAG_X3D_Inline:return Inline_get_field_count(node, 0); + case TAG_X3D_IntegerSequencer:return IntegerSequencer_get_field_count(node, 0); + case TAG_X3D_IntegerTrigger:return IntegerTrigger_get_field_count(node, 0); + case TAG_X3D_KeySensor:return KeySensor_get_field_count(node, 0); + case TAG_X3D_LineProperties:return LineProperties_get_field_count(node, 0); + case TAG_X3D_LineSet:return LineSet_get_field_count(node, 0); + case TAG_X3D_LOD:return LOD_get_field_count(node, 0); + case TAG_X3D_Material:return Material_get_field_count(node, 0); + case TAG_X3D_MetadataDouble:return MetadataDouble_get_field_count(node, 0); + case TAG_X3D_MetadataFloat:return MetadataFloat_get_field_count(node, 0); + case TAG_X3D_MetadataInteger:return MetadataInteger_get_field_count(node, 0); + case TAG_X3D_MetadataSet:return MetadataSet_get_field_count(node, 0); + case TAG_X3D_MetadataString:return MetadataString_get_field_count(node, 0); + case TAG_X3D_MovieTexture:return MovieTexture_get_field_count(node, 0); + case TAG_X3D_MultiTexture:return MultiTexture_get_field_count(node, 0); + case TAG_X3D_MultiTextureCoordinate:return MultiTextureCoordinate_get_field_count(node, 0); + case TAG_X3D_MultiTextureTransform:return MultiTextureTransform_get_field_count(node, 0); + case TAG_X3D_NavigationInfo:return NavigationInfo_get_field_count(node, 0); + case TAG_X3D_Normal:return Normal_get_field_count(node, 0); + case TAG_X3D_NormalInterpolator:return NormalInterpolator_get_field_count(node, 0); + case TAG_X3D_OrientationInterpolator:return OrientationInterpolator_get_field_count(node, 0); + case TAG_X3D_PixelTexture:return PixelTexture_get_field_count(node, 0); + case TAG_X3D_PlaneSensor:return PlaneSensor_get_field_count(node, 0); + case TAG_X3D_PointLight:return PointLight_get_field_count(node, 0); + case TAG_X3D_PointSet:return PointSet_get_field_count(node, 0); + case TAG_X3D_Polyline2D:return Polyline2D_get_field_count(node, 0); + case TAG_X3D_Polypoint2D:return Polypoint2D_get_field_count(node, 0); + case TAG_X3D_PositionInterpolator:return PositionInterpolator_get_field_count(node, 0); + case TAG_X3D_PositionInterpolator2D:return PositionInterpolator2D_get_field_count(node, 0); + case TAG_X3D_ProximitySensor:return ProximitySensor_get_field_count(node, 0); + case TAG_X3D_Rectangle2D:return Rectangle2D_get_field_count(node, 0); + case TAG_X3D_ScalarInterpolator:return ScalarInterpolator_get_field_count(node, 0); + case TAG_X3D_Script:return Script_get_field_count(node, 0); + case TAG_X3D_Shape:return Shape_get_field_count(node, 0); + case TAG_X3D_Sound:return Sound_get_field_count(node, 0); + case TAG_X3D_Sphere:return Sphere_get_field_count(node, 0); + case TAG_X3D_SphereSensor:return SphereSensor_get_field_count(node, 0); + case TAG_X3D_SpotLight:return SpotLight_get_field_count(node, 0); + case TAG_X3D_StaticGroup:return StaticGroup_get_field_count(node, 0); + case TAG_X3D_StringSensor:return StringSensor_get_field_count(node, 0); + case TAG_X3D_Switch:return Switch_get_field_count(node, 0); + case TAG_X3D_Text:return Text_get_field_count(node, 0); + case TAG_X3D_TextureBackground:return TextureBackground_get_field_count(node, 0); + case TAG_X3D_TextureCoordinate:return TextureCoordinate_get_field_count(node, 0); + case TAG_X3D_TextureCoordinateGenerator:return TextureCoordinateGenerator_get_field_count(node, 0); + case TAG_X3D_TextureTransform:return TextureTransform_get_field_count(node, 0); + case TAG_X3D_TimeSensor:return TimeSensor_get_field_count(node, 0); + case TAG_X3D_TimeTrigger:return TimeTrigger_get_field_count(node, 0); + case TAG_X3D_TouchSensor:return TouchSensor_get_field_count(node, 0); + case TAG_X3D_Transform:return Transform_get_field_count(node, 0); + case TAG_X3D_TriangleFanSet:return TriangleFanSet_get_field_count(node, 0); + case TAG_X3D_TriangleSet:return TriangleSet_get_field_count(node, 0); + case TAG_X3D_TriangleSet2D:return TriangleSet2D_get_field_count(node, 0); + case TAG_X3D_TriangleStripSet:return TriangleStripSet_get_field_count(node, 0); + case TAG_X3D_Viewpoint:return Viewpoint_get_field_count(node, 0); + case TAG_X3D_VisibilitySensor:return VisibilitySensor_get_field_count(node, 0); + case TAG_X3D_WorldInfo:return WorldInfo_get_field_count(node, 0); + default: + return 0; + } +} + +GF_Err gf_sg_x3d_node_get_field(GF_Node *node, GF_FieldInfo *field) +{ + switch (node->sgprivate->tag) { + case TAG_X3D_Anchor: return Anchor_get_field(node, field); + case TAG_X3D_Appearance: return Appearance_get_field(node, field); + case TAG_X3D_Arc2D: return Arc2D_get_field(node, field); + case TAG_X3D_ArcClose2D: return ArcClose2D_get_field(node, field); + case TAG_X3D_AudioClip: return AudioClip_get_field(node, field); + case TAG_X3D_Background: return Background_get_field(node, field); + case TAG_X3D_Billboard: return Billboard_get_field(node, field); + case TAG_X3D_BooleanFilter: return BooleanFilter_get_field(node, field); + case TAG_X3D_BooleanSequencer: return BooleanSequencer_get_field(node, field); + case TAG_X3D_BooleanToggle: return BooleanToggle_get_field(node, field); + case TAG_X3D_BooleanTrigger: return BooleanTrigger_get_field(node, field); + case TAG_X3D_Box: return Box_get_field(node, field); + case TAG_X3D_Circle2D: return Circle2D_get_field(node, field); + case TAG_X3D_Collision: return Collision_get_field(node, field); + case TAG_X3D_Color: return Color_get_field(node, field); + case TAG_X3D_ColorInterpolator: return ColorInterpolator_get_field(node, field); + case TAG_X3D_ColorRGBA: return ColorRGBA_get_field(node, field); + case TAG_X3D_Cone: return Cone_get_field(node, field); + case TAG_X3D_Contour2D: return Contour2D_get_field(node, field); + case TAG_X3D_ContourPolyline2D: return ContourPolyline2D_get_field(node, field); + case TAG_X3D_Coordinate: return Coordinate_get_field(node, field); + case TAG_X3D_CoordinateDouble: return CoordinateDouble_get_field(node, field); + case TAG_X3D_Coordinate2D: return Coordinate2D_get_field(node, field); + case TAG_X3D_CoordinateInterpolator: return CoordinateInterpolator_get_field(node, field); + case TAG_X3D_CoordinateInterpolator2D: return CoordinateInterpolator2D_get_field(node, field); + case TAG_X3D_Cylinder: return Cylinder_get_field(node, field); + case TAG_X3D_CylinderSensor: return CylinderSensor_get_field(node, field); + case TAG_X3D_DirectionalLight: return DirectionalLight_get_field(node, field); + case TAG_X3D_Disk2D: return Disk2D_get_field(node, field); + case TAG_X3D_ElevationGrid: return ElevationGrid_get_field(node, field); + case TAG_X3D_Extrusion: return Extrusion_get_field(node, field); + case TAG_X3D_FillProperties: return FillProperties_get_field(node, field); + case TAG_X3D_Fog: return Fog_get_field(node, field); + case TAG_X3D_FontStyle: return FontStyle_get_field(node, field); + case TAG_X3D_Group: return Group_get_field(node, field); + case TAG_X3D_ImageTexture: return ImageTexture_get_field(node, field); + case TAG_X3D_IndexedFaceSet: return IndexedFaceSet_get_field(node, field); + case TAG_X3D_IndexedLineSet: return IndexedLineSet_get_field(node, field); + case TAG_X3D_IndexedTriangleFanSet: return IndexedTriangleFanSet_get_field(node, field); + case TAG_X3D_IndexedTriangleSet: return IndexedTriangleSet_get_field(node, field); + case TAG_X3D_IndexedTriangleStripSet: return IndexedTriangleStripSet_get_field(node, field); + case TAG_X3D_Inline: return Inline_get_field(node, field); + case TAG_X3D_IntegerSequencer: return IntegerSequencer_get_field(node, field); + case TAG_X3D_IntegerTrigger: return IntegerTrigger_get_field(node, field); + case TAG_X3D_KeySensor: return KeySensor_get_field(node, field); + case TAG_X3D_LineProperties: return LineProperties_get_field(node, field); + case TAG_X3D_LineSet: return LineSet_get_field(node, field); + case TAG_X3D_LOD: return LOD_get_field(node, field); + case TAG_X3D_Material: return Material_get_field(node, field); + case TAG_X3D_MetadataDouble: return MetadataDouble_get_field(node, field); + case TAG_X3D_MetadataFloat: return MetadataFloat_get_field(node, field); + case TAG_X3D_MetadataInteger: return MetadataInteger_get_field(node, field); + case TAG_X3D_MetadataSet: return MetadataSet_get_field(node, field); + case TAG_X3D_MetadataString: return MetadataString_get_field(node, field); + case TAG_X3D_MovieTexture: return MovieTexture_get_field(node, field); + case TAG_X3D_MultiTexture: return MultiTexture_get_field(node, field); + case TAG_X3D_MultiTextureCoordinate: return MultiTextureCoordinate_get_field(node, field); + case TAG_X3D_MultiTextureTransform: return MultiTextureTransform_get_field(node, field); + case TAG_X3D_NavigationInfo: return NavigationInfo_get_field(node, field); + case TAG_X3D_Normal: return Normal_get_field(node, field); + case TAG_X3D_NormalInterpolator: return NormalInterpolator_get_field(node, field); + case TAG_X3D_OrientationInterpolator: return OrientationInterpolator_get_field(node, field); + case TAG_X3D_PixelTexture: return PixelTexture_get_field(node, field); + case TAG_X3D_PlaneSensor: return PlaneSensor_get_field(node, field); + case TAG_X3D_PointLight: return PointLight_get_field(node, field); + case TAG_X3D_PointSet: return PointSet_get_field(node, field); + case TAG_X3D_Polyline2D: return Polyline2D_get_field(node, field); + case TAG_X3D_Polypoint2D: return Polypoint2D_get_field(node, field); + case TAG_X3D_PositionInterpolator: return PositionInterpolator_get_field(node, field); + case TAG_X3D_PositionInterpolator2D: return PositionInterpolator2D_get_field(node, field); + case TAG_X3D_ProximitySensor: return ProximitySensor_get_field(node, field); + case TAG_X3D_Rectangle2D: return Rectangle2D_get_field(node, field); + case TAG_X3D_ScalarInterpolator: return ScalarInterpolator_get_field(node, field); + case TAG_X3D_Script: return Script_get_field(node, field); + case TAG_X3D_Shape: return Shape_get_field(node, field); + case TAG_X3D_Sound: return Sound_get_field(node, field); + case TAG_X3D_Sphere: return Sphere_get_field(node, field); + case TAG_X3D_SphereSensor: return SphereSensor_get_field(node, field); + case TAG_X3D_SpotLight: return SpotLight_get_field(node, field); + case TAG_X3D_StaticGroup: return StaticGroup_get_field(node, field); + case TAG_X3D_StringSensor: return StringSensor_get_field(node, field); + case TAG_X3D_Switch: return Switch_get_field(node, field); + case TAG_X3D_Text: return Text_get_field(node, field); + case TAG_X3D_TextureBackground: return TextureBackground_get_field(node, field); + case TAG_X3D_TextureCoordinate: return TextureCoordinate_get_field(node, field); + case TAG_X3D_TextureCoordinateGenerator: return TextureCoordinateGenerator_get_field(node, field); + case TAG_X3D_TextureTransform: return TextureTransform_get_field(node, field); + case TAG_X3D_TimeSensor: return TimeSensor_get_field(node, field); + case TAG_X3D_TimeTrigger: return TimeTrigger_get_field(node, field); + case TAG_X3D_TouchSensor: return TouchSensor_get_field(node, field); + case TAG_X3D_Transform: return Transform_get_field(node, field); + case TAG_X3D_TriangleFanSet: return TriangleFanSet_get_field(node, field); + case TAG_X3D_TriangleSet: return TriangleSet_get_field(node, field); + case TAG_X3D_TriangleSet2D: return TriangleSet2D_get_field(node, field); + case TAG_X3D_TriangleStripSet: return TriangleStripSet_get_field(node, field); + case TAG_X3D_Viewpoint: return Viewpoint_get_field(node, field); + case TAG_X3D_VisibilitySensor: return VisibilitySensor_get_field(node, field); + case TAG_X3D_WorldInfo: return WorldInfo_get_field(node, field); + default: + return GF_BAD_PARAM; + } +} + + +u32 gf_node_x3d_type_by_class_name(const char *node_name) +{ + if(!node_name) return 0; + if (!strcmp(node_name, "Anchor")) return TAG_X3D_Anchor; + if (!strcmp(node_name, "Appearance")) return TAG_X3D_Appearance; + if (!strcmp(node_name, "Arc2D")) return TAG_X3D_Arc2D; + if (!strcmp(node_name, "ArcClose2D")) return TAG_X3D_ArcClose2D; + if (!strcmp(node_name, "AudioClip")) return TAG_X3D_AudioClip; + if (!strcmp(node_name, "Background")) return TAG_X3D_Background; + if (!strcmp(node_name, "Billboard")) return TAG_X3D_Billboard; + if (!strcmp(node_name, "BooleanFilter")) return TAG_X3D_BooleanFilter; + if (!strcmp(node_name, "BooleanSequencer")) return TAG_X3D_BooleanSequencer; + if (!strcmp(node_name, "BooleanToggle")) return TAG_X3D_BooleanToggle; + if (!strcmp(node_name, "BooleanTrigger")) return TAG_X3D_BooleanTrigger; + if (!strcmp(node_name, "Box")) return TAG_X3D_Box; + if (!strcmp(node_name, "Circle2D")) return TAG_X3D_Circle2D; + if (!strcmp(node_name, "Collision")) return TAG_X3D_Collision; + if (!strcmp(node_name, "Color")) return TAG_X3D_Color; + if (!strcmp(node_name, "ColorInterpolator")) return TAG_X3D_ColorInterpolator; + if (!strcmp(node_name, "ColorRGBA")) return TAG_X3D_ColorRGBA; + if (!strcmp(node_name, "Cone")) return TAG_X3D_Cone; + if (!strcmp(node_name, "Contour2D")) return TAG_X3D_Contour2D; + if (!strcmp(node_name, "ContourPolyline2D")) return TAG_X3D_ContourPolyline2D; + if (!strcmp(node_name, "Coordinate")) return TAG_X3D_Coordinate; + if (!strcmp(node_name, "CoordinateDouble")) return TAG_X3D_CoordinateDouble; + if (!strcmp(node_name, "Coordinate2D")) return TAG_X3D_Coordinate2D; + if (!strcmp(node_name, "CoordinateInterpolator")) return TAG_X3D_CoordinateInterpolator; + if (!strcmp(node_name, "CoordinateInterpolator2D")) return TAG_X3D_CoordinateInterpolator2D; + if (!strcmp(node_name, "Cylinder")) return TAG_X3D_Cylinder; + if (!strcmp(node_name, "CylinderSensor")) return TAG_X3D_CylinderSensor; + if (!strcmp(node_name, "DirectionalLight")) return TAG_X3D_DirectionalLight; + if (!strcmp(node_name, "Disk2D")) return TAG_X3D_Disk2D; + if (!strcmp(node_name, "ElevationGrid")) return TAG_X3D_ElevationGrid; + if (!strcmp(node_name, "Extrusion")) return TAG_X3D_Extrusion; + if (!strcmp(node_name, "FillProperties")) return TAG_X3D_FillProperties; + if (!strcmp(node_name, "Fog")) return TAG_X3D_Fog; + if (!strcmp(node_name, "FontStyle")) return TAG_X3D_FontStyle; + if (!strcmp(node_name, "Group")) return TAG_X3D_Group; + if (!strcmp(node_name, "ImageTexture")) return TAG_X3D_ImageTexture; + if (!strcmp(node_name, "IndexedFaceSet")) return TAG_X3D_IndexedFaceSet; + if (!strcmp(node_name, "IndexedLineSet")) return TAG_X3D_IndexedLineSet; + if (!strcmp(node_name, "IndexedTriangleFanSet")) return TAG_X3D_IndexedTriangleFanSet; + if (!strcmp(node_name, "IndexedTriangleSet")) return TAG_X3D_IndexedTriangleSet; + if (!strcmp(node_name, "IndexedTriangleStripSet")) return TAG_X3D_IndexedTriangleStripSet; + if (!strcmp(node_name, "Inline")) return TAG_X3D_Inline; + if (!strcmp(node_name, "IntegerSequencer")) return TAG_X3D_IntegerSequencer; + if (!strcmp(node_name, "IntegerTrigger")) return TAG_X3D_IntegerTrigger; + if (!strcmp(node_name, "KeySensor")) return TAG_X3D_KeySensor; + if (!strcmp(node_name, "LineProperties")) return TAG_X3D_LineProperties; + if (!strcmp(node_name, "LineSet")) return TAG_X3D_LineSet; + if (!strcmp(node_name, "LOD")) return TAG_X3D_LOD; + if (!strcmp(node_name, "Material")) return TAG_X3D_Material; + if (!strcmp(node_name, "MetadataDouble")) return TAG_X3D_MetadataDouble; + if (!strcmp(node_name, "MetadataFloat")) return TAG_X3D_MetadataFloat; + if (!strcmp(node_name, "MetadataInteger")) return TAG_X3D_MetadataInteger; + if (!strcmp(node_name, "MetadataSet")) return TAG_X3D_MetadataSet; + if (!strcmp(node_name, "MetadataString")) return TAG_X3D_MetadataString; + if (!strcmp(node_name, "MovieTexture")) return TAG_X3D_MovieTexture; + if (!strcmp(node_name, "MultiTexture")) return TAG_X3D_MultiTexture; + if (!strcmp(node_name, "MultiTextureCoordinate")) return TAG_X3D_MultiTextureCoordinate; + if (!strcmp(node_name, "MultiTextureTransform")) return TAG_X3D_MultiTextureTransform; + if (!strcmp(node_name, "NavigationInfo")) return TAG_X3D_NavigationInfo; + if (!strcmp(node_name, "Normal")) return TAG_X3D_Normal; + if (!strcmp(node_name, "NormalInterpolator")) return TAG_X3D_NormalInterpolator; + if (!strcmp(node_name, "OrientationInterpolator")) return TAG_X3D_OrientationInterpolator; + if (!strcmp(node_name, "PixelTexture")) return TAG_X3D_PixelTexture; + if (!strcmp(node_name, "PlaneSensor")) return TAG_X3D_PlaneSensor; + if (!strcmp(node_name, "PointLight")) return TAG_X3D_PointLight; + if (!strcmp(node_name, "PointSet")) return TAG_X3D_PointSet; + if (!strcmp(node_name, "Polyline2D")) return TAG_X3D_Polyline2D; + if (!strcmp(node_name, "Polypoint2D")) return TAG_X3D_Polypoint2D; + if (!strcmp(node_name, "PositionInterpolator")) return TAG_X3D_PositionInterpolator; + if (!strcmp(node_name, "PositionInterpolator2D")) return TAG_X3D_PositionInterpolator2D; + if (!strcmp(node_name, "ProximitySensor")) return TAG_X3D_ProximitySensor; + if (!strcmp(node_name, "Rectangle2D")) return TAG_X3D_Rectangle2D; + if (!strcmp(node_name, "ScalarInterpolator")) return TAG_X3D_ScalarInterpolator; + if (!strcmp(node_name, "Script")) return TAG_X3D_Script; + if (!strcmp(node_name, "Shape")) return TAG_X3D_Shape; + if (!strcmp(node_name, "Sound")) return TAG_X3D_Sound; + if (!strcmp(node_name, "Sphere")) return TAG_X3D_Sphere; + if (!strcmp(node_name, "SphereSensor")) return TAG_X3D_SphereSensor; + if (!strcmp(node_name, "SpotLight")) return TAG_X3D_SpotLight; + if (!strcmp(node_name, "StaticGroup")) return TAG_X3D_StaticGroup; + if (!strcmp(node_name, "StringSensor")) return TAG_X3D_StringSensor; + if (!strcmp(node_name, "Switch")) return TAG_X3D_Switch; + if (!strcmp(node_name, "Text")) return TAG_X3D_Text; + if (!strcmp(node_name, "TextureBackground")) return TAG_X3D_TextureBackground; + if (!strcmp(node_name, "TextureCoordinate")) return TAG_X3D_TextureCoordinate; + if (!strcmp(node_name, "TextureCoordinateGenerator")) return TAG_X3D_TextureCoordinateGenerator; + if (!strcmp(node_name, "TextureTransform")) return TAG_X3D_TextureTransform; + if (!strcmp(node_name, "TimeSensor")) return TAG_X3D_TimeSensor; + if (!strcmp(node_name, "TimeTrigger")) return TAG_X3D_TimeTrigger; + if (!strcmp(node_name, "TouchSensor")) return TAG_X3D_TouchSensor; + if (!strcmp(node_name, "Transform")) return TAG_X3D_Transform; + if (!strcmp(node_name, "TriangleFanSet")) return TAG_X3D_TriangleFanSet; + if (!strcmp(node_name, "TriangleSet")) return TAG_X3D_TriangleSet; + if (!strcmp(node_name, "TriangleSet2D")) return TAG_X3D_TriangleSet2D; + if (!strcmp(node_name, "TriangleStripSet")) return TAG_X3D_TriangleStripSet; + if (!strcmp(node_name, "Viewpoint")) return TAG_X3D_Viewpoint; + if (!strcmp(node_name, "VisibilitySensor")) return TAG_X3D_VisibilitySensor; + if (!strcmp(node_name, "WorldInfo")) return TAG_X3D_WorldInfo; + return 0; +} + +s32 gf_sg_x3d_node_get_field_index_by_name(GF_Node *node, char *name) +{ + switch (node->sgprivate->tag) { + case TAG_X3D_Anchor: return Anchor_get_field_index_by_name(name); + case TAG_X3D_Appearance: return Appearance_get_field_index_by_name(name); + case TAG_X3D_Arc2D: return Arc2D_get_field_index_by_name(name); + case TAG_X3D_ArcClose2D: return ArcClose2D_get_field_index_by_name(name); + case TAG_X3D_AudioClip: return AudioClip_get_field_index_by_name(name); + case TAG_X3D_Background: return Background_get_field_index_by_name(name); + case TAG_X3D_Billboard: return Billboard_get_field_index_by_name(name); + case TAG_X3D_BooleanFilter: return BooleanFilter_get_field_index_by_name(name); + case TAG_X3D_BooleanSequencer: return BooleanSequencer_get_field_index_by_name(name); + case TAG_X3D_BooleanToggle: return BooleanToggle_get_field_index_by_name(name); + case TAG_X3D_BooleanTrigger: return BooleanTrigger_get_field_index_by_name(name); + case TAG_X3D_Box: return Box_get_field_index_by_name(name); + case TAG_X3D_Circle2D: return Circle2D_get_field_index_by_name(name); + case TAG_X3D_Collision: return Collision_get_field_index_by_name(name); + case TAG_X3D_Color: return Color_get_field_index_by_name(name); + case TAG_X3D_ColorInterpolator: return ColorInterpolator_get_field_index_by_name(name); + case TAG_X3D_ColorRGBA: return ColorRGBA_get_field_index_by_name(name); + case TAG_X3D_Cone: return Cone_get_field_index_by_name(name); + case TAG_X3D_Contour2D: return Contour2D_get_field_index_by_name(name); + case TAG_X3D_ContourPolyline2D: return ContourPolyline2D_get_field_index_by_name(name); + case TAG_X3D_Coordinate: return Coordinate_get_field_index_by_name(name); + case TAG_X3D_CoordinateDouble: return CoordinateDouble_get_field_index_by_name(name); + case TAG_X3D_Coordinate2D: return Coordinate2D_get_field_index_by_name(name); + case TAG_X3D_CoordinateInterpolator: return CoordinateInterpolator_get_field_index_by_name(name); + case TAG_X3D_CoordinateInterpolator2D: return CoordinateInterpolator2D_get_field_index_by_name(name); + case TAG_X3D_Cylinder: return Cylinder_get_field_index_by_name(name); + case TAG_X3D_CylinderSensor: return CylinderSensor_get_field_index_by_name(name); + case TAG_X3D_DirectionalLight: return DirectionalLight_get_field_index_by_name(name); + case TAG_X3D_Disk2D: return Disk2D_get_field_index_by_name(name); + case TAG_X3D_ElevationGrid: return ElevationGrid_get_field_index_by_name(name); + case TAG_X3D_Extrusion: return Extrusion_get_field_index_by_name(name); + case TAG_X3D_FillProperties: return FillProperties_get_field_index_by_name(name); + case TAG_X3D_Fog: return Fog_get_field_index_by_name(name); + case TAG_X3D_FontStyle: return FontStyle_get_field_index_by_name(name); + case TAG_X3D_Group: return Group_get_field_index_by_name(name); + case TAG_X3D_ImageTexture: return ImageTexture_get_field_index_by_name(name); + case TAG_X3D_IndexedFaceSet: return IndexedFaceSet_get_field_index_by_name(name); + case TAG_X3D_IndexedLineSet: return IndexedLineSet_get_field_index_by_name(name); + case TAG_X3D_IndexedTriangleFanSet: return IndexedTriangleFanSet_get_field_index_by_name(name); + case TAG_X3D_IndexedTriangleSet: return IndexedTriangleSet_get_field_index_by_name(name); + case TAG_X3D_IndexedTriangleStripSet: return IndexedTriangleStripSet_get_field_index_by_name(name); + case TAG_X3D_Inline: return Inline_get_field_index_by_name(name); + case TAG_X3D_IntegerSequencer: return IntegerSequencer_get_field_index_by_name(name); + case TAG_X3D_IntegerTrigger: return IntegerTrigger_get_field_index_by_name(name); + case TAG_X3D_KeySensor: return KeySensor_get_field_index_by_name(name); + case TAG_X3D_LineProperties: return LineProperties_get_field_index_by_name(name); + case TAG_X3D_LineSet: return LineSet_get_field_index_by_name(name); + case TAG_X3D_LOD: return LOD_get_field_index_by_name(name); + case TAG_X3D_Material: return Material_get_field_index_by_name(name); + case TAG_X3D_MetadataDouble: return MetadataDouble_get_field_index_by_name(name); + case TAG_X3D_MetadataFloat: return MetadataFloat_get_field_index_by_name(name); + case TAG_X3D_MetadataInteger: return MetadataInteger_get_field_index_by_name(name); + case TAG_X3D_MetadataSet: return MetadataSet_get_field_index_by_name(name); + case TAG_X3D_MetadataString: return MetadataString_get_field_index_by_name(name); + case TAG_X3D_MovieTexture: return MovieTexture_get_field_index_by_name(name); + case TAG_X3D_MultiTexture: return MultiTexture_get_field_index_by_name(name); + case TAG_X3D_MultiTextureCoordinate: return MultiTextureCoordinate_get_field_index_by_name(name); + case TAG_X3D_MultiTextureTransform: return MultiTextureTransform_get_field_index_by_name(name); + case TAG_X3D_NavigationInfo: return NavigationInfo_get_field_index_by_name(name); + case TAG_X3D_Normal: return Normal_get_field_index_by_name(name); + case TAG_X3D_NormalInterpolator: return NormalInterpolator_get_field_index_by_name(name); + case TAG_X3D_OrientationInterpolator: return OrientationInterpolator_get_field_index_by_name(name); + case TAG_X3D_PixelTexture: return PixelTexture_get_field_index_by_name(name); + case TAG_X3D_PlaneSensor: return PlaneSensor_get_field_index_by_name(name); + case TAG_X3D_PointLight: return PointLight_get_field_index_by_name(name); + case TAG_X3D_PointSet: return PointSet_get_field_index_by_name(name); + case TAG_X3D_Polyline2D: return Polyline2D_get_field_index_by_name(name); + case TAG_X3D_Polypoint2D: return Polypoint2D_get_field_index_by_name(name); + case TAG_X3D_PositionInterpolator: return PositionInterpolator_get_field_index_by_name(name); + case TAG_X3D_PositionInterpolator2D: return PositionInterpolator2D_get_field_index_by_name(name); + case TAG_X3D_ProximitySensor: return ProximitySensor_get_field_index_by_name(name); + case TAG_X3D_Rectangle2D: return Rectangle2D_get_field_index_by_name(name); + case TAG_X3D_ScalarInterpolator: return ScalarInterpolator_get_field_index_by_name(name); + case TAG_X3D_Script: return Script_get_field_index_by_name(name); + case TAG_X3D_Shape: return Shape_get_field_index_by_name(name); + case TAG_X3D_Sound: return Sound_get_field_index_by_name(name); + case TAG_X3D_Sphere: return Sphere_get_field_index_by_name(name); + case TAG_X3D_SphereSensor: return SphereSensor_get_field_index_by_name(name); + case TAG_X3D_SpotLight: return SpotLight_get_field_index_by_name(name); + case TAG_X3D_StaticGroup: return StaticGroup_get_field_index_by_name(name); + case TAG_X3D_StringSensor: return StringSensor_get_field_index_by_name(name); + case TAG_X3D_Switch: return Switch_get_field_index_by_name(name); + case TAG_X3D_Text: return Text_get_field_index_by_name(name); + case TAG_X3D_TextureBackground: return TextureBackground_get_field_index_by_name(name); + case TAG_X3D_TextureCoordinate: return TextureCoordinate_get_field_index_by_name(name); + case TAG_X3D_TextureCoordinateGenerator: return TextureCoordinateGenerator_get_field_index_by_name(name); + case TAG_X3D_TextureTransform: return TextureTransform_get_field_index_by_name(name); + case TAG_X3D_TimeSensor: return TimeSensor_get_field_index_by_name(name); + case TAG_X3D_TimeTrigger: return TimeTrigger_get_field_index_by_name(name); + case TAG_X3D_TouchSensor: return TouchSensor_get_field_index_by_name(name); + case TAG_X3D_Transform: return Transform_get_field_index_by_name(name); + case TAG_X3D_TriangleFanSet: return TriangleFanSet_get_field_index_by_name(name); + case TAG_X3D_TriangleSet: return TriangleSet_get_field_index_by_name(name); + case TAG_X3D_TriangleSet2D: return TriangleSet2D_get_field_index_by_name(name); + case TAG_X3D_TriangleStripSet: return TriangleStripSet_get_field_index_by_name(name); + case TAG_X3D_Viewpoint: return Viewpoint_get_field_index_by_name(name); + case TAG_X3D_VisibilitySensor: return VisibilitySensor_get_field_index_by_name(name); + case TAG_X3D_WorldInfo: return WorldInfo_get_field_index_by_name(name); + default: + return -1; + } +} + + + +/* NDT X3D */ + +#define SFWorldNode_X3D_Count 127 +static const u32 SFWorldNode_X3D_TypeToTag[127] = { + TAG_X3D_Anchor, TAG_X3D_Appearance, TAG_X3D_Arc2D, TAG_X3D_ArcClose2D, TAG_X3D_AudioClip, TAG_X3D_Background, TAG_X3D_Billboard, TAG_X3D_BooleanFilter, TAG_X3D_BooleanSequencer, TAG_X3D_BooleanToggle, TAG_X3D_BooleanTrigger, TAG_X3D_Box, TAG_X3D_Circle2D, TAG_X3D_Collision, TAG_X3D_Color, TAG_X3D_ColorInterpolator, TAG_X3D_ColorRGBA, TAG_X3D_Cone, TAG_X3D_Contour2D, TAG_X3D_ContourPolyline2D, TAG_X3D_Coordinate, TAG_X3D_CoordinateDouble, TAG_X3D_Coordinate2D, TAG_X3D_CoordinateInterpolator, TAG_X3D_CoordinateInterpolator2D, TAG_X3D_Cylinder, TAG_X3D_CylinderSensor, TAG_X3D_DirectionalLight, TAG_X3D_Disk2D, TAG_X3D_ElevationGrid, TAG_X3D_EspduTransform, TAG_X3D_Extrusion, TAG_X3D_FillProperties, TAG_X3D_Fog, TAG_X3D_FontStyle, TAG_X3D_GeoCoordinate, TAG_X3D_GeoElevationGrid, TAG_X3D_GeoLocation, TAG_X3D_GeoLOD, TAG_X3D_GeoMetadata, TAG_X3D_GeoPositionInterpolator, TAG_X3D_GeoTouchSensor, TAG_X3D_GeoViewpoint, TAG_X3D_Group, TAG_X3D_HAnimDisplacer, TAG_X3D_HAnimHumanoid, TAG_X3D_HAnimJoint, TAG_X3D_HAnimSegment, TAG_X3D_HAnimSite, TAG_X3D_ImageTexture, TAG_X3D_IndexedFaceSet, TAG_X3D_IndexedLineSet, TAG_X3D_IndexedTriangleFanSet, TAG_X3D_IndexedTriangleSet, TAG_X3D_IndexedTriangleStripSet, TAG_X3D_Inline, TAG_X3D_IntegerSequencer, TAG_X3D_IntegerTrigger, TAG_X3D_KeySensor, TAG_X3D_LineProperties, TAG_X3D_LineSet, TAG_X3D_LoadSensor, TAG_X3D_LOD, TAG_X3D_Material, TAG_X3D_MetadataDouble, TAG_X3D_MetadataFloat, TAG_X3D_MetadataInteger, TAG_X3D_MetadataSet, TAG_X3D_MetadataString, TAG_X3D_MovieTexture, TAG_X3D_MultiTexture, TAG_X3D_MultiTextureCoordinate, TAG_X3D_MultiTextureTransform, TAG_X3D_NavigationInfo, TAG_X3D_Normal, TAG_X3D_NormalInterpolator, TAG_X3D_NurbsCurve, TAG_X3D_NurbsCurve2D, TAG_X3D_NurbsOrientationInterpolator, TAG_X3D_NurbsPatchSurface, TAG_X3D_NurbsPositionInterpolator, TAG_X3D_NurbsSet, TAG_X3D_NurbsSurfaceInterpolator, TAG_X3D_NurbsSweptSurface, TAG_X3D_NurbsSwungSurface, TAG_X3D_NurbsTextureCoordinate, TAG_X3D_NurbsTrimmedSurface, TAG_X3D_OrientationInterpolator, TAG_X3D_PixelTexture, TAG_X3D_PlaneSensor, TAG_X3D_PointLight, TAG_X3D_PointSet, TAG_X3D_Polyline2D, TAG_X3D_Polypoint2D, TAG_X3D_PositionInterpolator, TAG_X3D_PositionInterpolator2D, TAG_X3D_ProximitySensor, TAG_X3D_ReceiverPdu, TAG_X3D_Rectangle2D, TAG_X3D_ScalarInterpolator, TAG_X3D_Script, TAG_X3D_Shape, TAG_X3D_SignalPdu, TAG_X3D_Sound, TAG_X3D_Sphere, TAG_X3D_SphereSensor, TAG_X3D_SpotLight, TAG_X3D_StaticGroup, TAG_X3D_StringSensor, TAG_X3D_Switch, TAG_X3D_Text, TAG_X3D_TextureBackground, TAG_X3D_TextureCoordinate, TAG_X3D_TextureCoordinateGenerator, TAG_X3D_TextureTransform, TAG_X3D_TimeSensor, TAG_X3D_TimeTrigger, TAG_X3D_TouchSensor, TAG_X3D_Transform, TAG_X3D_TransmitterPdu, TAG_X3D_TriangleFanSet, TAG_X3D_TriangleSet, TAG_X3D_TriangleSet2D, TAG_X3D_TriangleStripSet, TAG_X3D_Viewpoint, TAG_X3D_VisibilitySensor, TAG_X3D_WorldInfo +}; + +#define SF3DNode_X3D_Count 60 +static const u32 SF3DNode_X3D_TypeToTag[60] = { + TAG_X3D_Anchor, TAG_X3D_Background, TAG_X3D_Billboard, TAG_X3D_BooleanFilter, TAG_X3D_BooleanSequencer, TAG_X3D_BooleanToggle, TAG_X3D_BooleanTrigger, TAG_X3D_Collision, TAG_X3D_ColorInterpolator, TAG_X3D_CoordinateInterpolator, TAG_X3D_CoordinateInterpolator2D, TAG_X3D_CylinderSensor, TAG_X3D_DirectionalLight, TAG_X3D_EspduTransform, TAG_X3D_Fog, TAG_X3D_GeoLocation, TAG_X3D_GeoLOD, TAG_X3D_GeoMetadata, TAG_X3D_GeoPositionInterpolator, TAG_X3D_GeoTouchSensor, TAG_X3D_GeoViewpoint, TAG_X3D_Group, TAG_X3D_HAnimHumanoid, TAG_X3D_Inline, TAG_X3D_IntegerSequencer, TAG_X3D_IntegerTrigger, TAG_X3D_KeySensor, TAG_X3D_LOD, TAG_X3D_NavigationInfo, TAG_X3D_NormalInterpolator, TAG_X3D_NurbsOrientationInterpolator, TAG_X3D_NurbsPositionInterpolator, TAG_X3D_NurbsSet, TAG_X3D_NurbsSurfaceInterpolator, TAG_X3D_OrientationInterpolator, TAG_X3D_PlaneSensor, TAG_X3D_PointLight, TAG_X3D_PositionInterpolator, TAG_X3D_PositionInterpolator2D, TAG_X3D_ProximitySensor, TAG_X3D_ReceiverPdu, TAG_X3D_ScalarInterpolator, TAG_X3D_Script, TAG_X3D_Shape, TAG_X3D_SignalPdu, TAG_X3D_Sound, TAG_X3D_SphereSensor, TAG_X3D_SpotLight, TAG_X3D_StaticGroup, TAG_X3D_StringSensor, TAG_X3D_Switch, TAG_X3D_TextureBackground, TAG_X3D_TimeSensor, TAG_X3D_TimeTrigger, TAG_X3D_TouchSensor, TAG_X3D_Transform, TAG_X3D_TransmitterPdu, TAG_X3D_Viewpoint, TAG_X3D_VisibilitySensor, TAG_X3D_WorldInfo +}; + +#define SF2DNode_X3D_Count 34 +static const u32 SF2DNode_X3D_TypeToTag[34] = { + TAG_X3D_Anchor, TAG_X3D_BooleanFilter, TAG_X3D_BooleanSequencer, TAG_X3D_BooleanToggle, TAG_X3D_BooleanTrigger, TAG_X3D_ColorInterpolator, TAG_X3D_CoordinateInterpolator2D, TAG_X3D_EspduTransform, TAG_X3D_GeoMetadata, TAG_X3D_GeoTouchSensor, TAG_X3D_Group, TAG_X3D_Inline, TAG_X3D_IntegerSequencer, TAG_X3D_IntegerTrigger, TAG_X3D_KeySensor, TAG_X3D_LOD, TAG_X3D_NurbsOrientationInterpolator, TAG_X3D_NurbsPositionInterpolator, TAG_X3D_NurbsSet, TAG_X3D_NurbsSurfaceInterpolator, TAG_X3D_PositionInterpolator2D, TAG_X3D_ReceiverPdu, TAG_X3D_ScalarInterpolator, TAG_X3D_Script, TAG_X3D_Shape, TAG_X3D_SignalPdu, TAG_X3D_StaticGroup, TAG_X3D_StringSensor, TAG_X3D_Switch, TAG_X3D_TimeSensor, TAG_X3D_TimeTrigger, TAG_X3D_TouchSensor, TAG_X3D_TransmitterPdu, TAG_X3D_WorldInfo +}; + +#define SFAppearanceNode_X3D_Count 1 +static const u32 SFAppearanceNode_X3D_TypeToTag[1] = { + TAG_X3D_Appearance +}; + +#define SFGeometryNode_X3D_Count 31 +static const u32 SFGeometryNode_X3D_TypeToTag[31] = { + TAG_X3D_Arc2D, TAG_X3D_ArcClose2D, TAG_X3D_Box, TAG_X3D_Circle2D, TAG_X3D_Cone, TAG_X3D_Cylinder, TAG_X3D_Disk2D, TAG_X3D_ElevationGrid, TAG_X3D_Extrusion, TAG_X3D_GeoElevationGrid, TAG_X3D_IndexedFaceSet, TAG_X3D_IndexedLineSet, TAG_X3D_IndexedTriangleFanSet, TAG_X3D_IndexedTriangleSet, TAG_X3D_IndexedTriangleStripSet, TAG_X3D_LineSet, TAG_X3D_NurbsCurve, TAG_X3D_NurbsPatchSurface, TAG_X3D_NurbsSweptSurface, TAG_X3D_NurbsSwungSurface, TAG_X3D_NurbsTrimmedSurface, TAG_X3D_PointSet, TAG_X3D_Polyline2D, TAG_X3D_Polypoint2D, TAG_X3D_Rectangle2D, TAG_X3D_Sphere, TAG_X3D_Text, TAG_X3D_TriangleFanSet, TAG_X3D_TriangleSet, TAG_X3D_TriangleSet2D, TAG_X3D_TriangleStripSet +}; + +#define SFAudioNode_X3D_Count 1 +static const u32 SFAudioNode_X3D_TypeToTag[1] = { + TAG_X3D_AudioClip +}; + +#define SFStreamingNode_X3D_Count 4 +static const u32 SFStreamingNode_X3D_TypeToTag[4] = { + TAG_X3D_AudioClip, TAG_X3D_Inline, TAG_X3D_LoadSensor, TAG_X3D_MovieTexture +}; + +#define SFBackground3DNode_X3D_Count 2 +static const u32 SFBackground3DNode_X3D_TypeToTag[2] = { + TAG_X3D_Background, TAG_X3D_TextureBackground +}; + +#define SFColorNode_X3D_Count 2 +static const u32 SFColorNode_X3D_TypeToTag[2] = { + TAG_X3D_Color, TAG_X3D_ColorRGBA +}; + +#define SFNurbsControlCurveNode_X3D_Count 3 +static const u32 SFNurbsControlCurveNode_X3D_TypeToTag[3] = { + TAG_X3D_Contour2D, TAG_X3D_ContourPolyline2D, TAG_X3D_NurbsCurve2D +}; + +#define SFCoordinateNode_X3D_Count 3 +static const u32 SFCoordinateNode_X3D_TypeToTag[3] = { + TAG_X3D_Coordinate, TAG_X3D_CoordinateDouble, TAG_X3D_GeoCoordinate +}; + +#define SFCoordinate2DNode_X3D_Count 1 +static const u32 SFCoordinate2DNode_X3D_TypeToTag[1] = { + TAG_X3D_Coordinate2D +}; + +#define SFFillPropertiesNode_X3D_Count 1 +static const u32 SFFillPropertiesNode_X3D_TypeToTag[1] = { + TAG_X3D_FillProperties +}; + +#define SFFogNode_X3D_Count 1 +static const u32 SFFogNode_X3D_TypeToTag[1] = { + TAG_X3D_Fog +}; + +#define SFFontStyleNode_X3D_Count 1 +static const u32 SFFontStyleNode_X3D_TypeToTag[1] = { + TAG_X3D_FontStyle +}; + +#define SFGeoOriginNode_X3D_Count 1 +static const u32 SFGeoOriginNode_X3D_TypeToTag[1] = { + TAG_X3D_GeoOrigin +}; + +#define SFViewpointNode_X3D_Count 2 +static const u32 SFViewpointNode_X3D_TypeToTag[2] = { + TAG_X3D_GeoViewpoint, TAG_X3D_Viewpoint +}; + +#define SFTopNode_X3D_Count 1 +static const u32 SFTopNode_X3D_TypeToTag[1] = { + TAG_X3D_Group +}; + +#define SFHAnimDisplacerNode_X3D_Count 1 +static const u32 SFHAnimDisplacerNode_X3D_TypeToTag[1] = { + TAG_X3D_HAnimDisplacer +}; + +#define SFHAnimNode_X3D_Count 3 +static const u32 SFHAnimNode_X3D_TypeToTag[3] = { + TAG_X3D_HAnimJoint, TAG_X3D_HAnimSegment, TAG_X3D_HAnimSite +}; + +#define SFTextureNode_X3D_Count 4 +static const u32 SFTextureNode_X3D_TypeToTag[4] = { + TAG_X3D_ImageTexture, TAG_X3D_MovieTexture, TAG_X3D_MultiTexture, TAG_X3D_PixelTexture +}; + +#define SFX3DLinePropertiesNode_X3D_Count 1 +static const u32 SFX3DLinePropertiesNode_X3D_TypeToTag[1] = { + TAG_X3D_LineProperties +}; + +#define SFMaterialNode_X3D_Count 1 +static const u32 SFMaterialNode_X3D_TypeToTag[1] = { + TAG_X3D_Material +}; + +#define SFMetadataNode_X3D_Count 5 +static const u32 SFMetadataNode_X3D_TypeToTag[5] = { + TAG_X3D_MetadataDouble, TAG_X3D_MetadataFloat, TAG_X3D_MetadataInteger, TAG_X3D_MetadataSet, TAG_X3D_MetadataString +}; + +#define SFTextureCoordinateNode_X3D_Count 4 +static const u32 SFTextureCoordinateNode_X3D_TypeToTag[4] = { + TAG_X3D_MultiTextureCoordinate, TAG_X3D_NurbsTextureCoordinate, TAG_X3D_TextureCoordinate, TAG_X3D_TextureCoordinateGenerator +}; + +#define SFTextureTransformNode_X3D_Count 2 +static const u32 SFTextureTransformNode_X3D_TypeToTag[2] = { + TAG_X3D_MultiTextureTransform, TAG_X3D_TextureTransform +}; + +#define SFNavigationInfoNode_X3D_Count 1 +static const u32 SFNavigationInfoNode_X3D_TypeToTag[1] = { + TAG_X3D_NavigationInfo +}; + +#define SFNormalNode_X3D_Count 1 +static const u32 SFNormalNode_X3D_TypeToTag[1] = { + TAG_X3D_Normal +}; + +#define SFNurbsCurveNode_X3D_Count 1 +static const u32 SFNurbsCurveNode_X3D_TypeToTag[1] = { + TAG_X3D_NurbsCurve +}; + +#define SFNurbsSurfaceNode_X3D_Count 4 +static const u32 SFNurbsSurfaceNode_X3D_TypeToTag[4] = { + TAG_X3D_NurbsPatchSurface, TAG_X3D_NurbsSweptSurface, TAG_X3D_NurbsSwungSurface, TAG_X3D_NurbsTrimmedSurface +}; + + + + +Bool gf_x3d_get_node_type(u32 NDT_Tag, u32 NodeTag) +{ + const u32 *types; + u32 count, i; + if (!NodeTag) return 0; + types = NULL; count = 0; + switch (NDT_Tag) { + case NDT_SFWorldNode: + types = SFWorldNode_X3D_TypeToTag; count = SFWorldNode_X3D_Count; break; + case NDT_SF3DNode: + types = SF3DNode_X3D_TypeToTag; count = SF3DNode_X3D_Count; break; + case NDT_SF2DNode: + types = SF2DNode_X3D_TypeToTag; count = SF2DNode_X3D_Count; break; + case NDT_SFAppearanceNode: + types = SFAppearanceNode_X3D_TypeToTag; count = SFAppearanceNode_X3D_Count; break; + case NDT_SFGeometryNode: + types = SFGeometryNode_X3D_TypeToTag; count = SFGeometryNode_X3D_Count; break; + case NDT_SFAudioNode: + types = SFAudioNode_X3D_TypeToTag; count = SFAudioNode_X3D_Count; break; + case NDT_SFStreamingNode: + types = SFStreamingNode_X3D_TypeToTag; count = SFStreamingNode_X3D_Count; break; + case NDT_SFBackground3DNode: + types = SFBackground3DNode_X3D_TypeToTag; count = SFBackground3DNode_X3D_Count; break; + case NDT_SFColorNode: + types = SFColorNode_X3D_TypeToTag; count = SFColorNode_X3D_Count; break; + case NDT_SFNurbsControlCurveNode: + types = SFNurbsControlCurveNode_X3D_TypeToTag; count = SFNurbsControlCurveNode_X3D_Count; break; + case NDT_SFCoordinateNode: + types = SFCoordinateNode_X3D_TypeToTag; count = SFCoordinateNode_X3D_Count; break; + case NDT_SFCoordinate2DNode: + types = SFCoordinate2DNode_X3D_TypeToTag; count = SFCoordinate2DNode_X3D_Count; break; + case NDT_SFFillPropertiesNode: + types = SFFillPropertiesNode_X3D_TypeToTag; count = SFFillPropertiesNode_X3D_Count; break; + case NDT_SFFogNode: + types = SFFogNode_X3D_TypeToTag; count = SFFogNode_X3D_Count; break; + case NDT_SFFontStyleNode: + types = SFFontStyleNode_X3D_TypeToTag; count = SFFontStyleNode_X3D_Count; break; + case NDT_SFGeoOriginNode: + types = SFGeoOriginNode_X3D_TypeToTag; count = SFGeoOriginNode_X3D_Count; break; + case NDT_SFViewpointNode: + types = SFViewpointNode_X3D_TypeToTag; count = SFViewpointNode_X3D_Count; break; + case NDT_SFTopNode: + types = SFTopNode_X3D_TypeToTag; count = SFTopNode_X3D_Count; break; + case NDT_SFHAnimDisplacerNode: + types = SFHAnimDisplacerNode_X3D_TypeToTag; count = SFHAnimDisplacerNode_X3D_Count; break; + case NDT_SFHAnimNode: + types = SFHAnimNode_X3D_TypeToTag; count = SFHAnimNode_X3D_Count; break; + case NDT_SFTextureNode: + types = SFTextureNode_X3D_TypeToTag; count = SFTextureNode_X3D_Count; break; + case NDT_SFX3DLinePropertiesNode: + types = SFX3DLinePropertiesNode_X3D_TypeToTag; count = SFX3DLinePropertiesNode_X3D_Count; break; + case NDT_SFMaterialNode: + types = SFMaterialNode_X3D_TypeToTag; count = SFMaterialNode_X3D_Count; break; + case NDT_SFMetadataNode: + types = SFMetadataNode_X3D_TypeToTag; count = SFMetadataNode_X3D_Count; break; + case NDT_SFTextureCoordinateNode: + types = SFTextureCoordinateNode_X3D_TypeToTag; count = SFTextureCoordinateNode_X3D_Count; break; + case NDT_SFTextureTransformNode: + types = SFTextureTransformNode_X3D_TypeToTag; count = SFTextureTransformNode_X3D_Count; break; + case NDT_SFNavigationInfoNode: + types = SFNavigationInfoNode_X3D_TypeToTag; count = SFNavigationInfoNode_X3D_Count; break; + case NDT_SFNormalNode: + types = SFNormalNode_X3D_TypeToTag; count = SFNormalNode_X3D_Count; break; + case NDT_SFNurbsCurveNode: + types = SFNurbsCurveNode_X3D_TypeToTag; count = SFNurbsCurveNode_X3D_Count; break; + case NDT_SFNurbsSurfaceNode: + types = SFNurbsSurfaceNode_X3D_TypeToTag; count = SFNurbsSurfaceNode_X3D_Count; break; + default: + return 0; + } + for(i=0; i +#include +#include +#include +#include + +#ifndef GPAC_DISABLE_SVG + +#include + +typedef struct +{ + GF_SceneLoader *load; + GF_Err last_error; + GF_SAXParser *sax_parser; + XBL_Element *root; + + /* stack of XBL nodes*/ + GF_List *node_stack; +} GF_XBL_Parser; + + +typedef struct +{ + /*top of parsed sub-tree*/ + XBL_Element *node; + /*depth of unknown elements being skipped*/ + u32 unknown_depth; + /*last child added, used to speed-up parsing*/ + GF_ChildNodeItem *last_child; +} XBL_NodeStack; + +static GF_Err xbl_parse_report(GF_XBL_Parser *parser, GF_Err e, char *format, ...) +{ +#ifndef GPAC_DISABLE_LOG + if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + char szMsg[2048]; + va_list args; + va_start(args, format); + vsprintf(szMsg, format, args); + va_end(args); + GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_PARSER, ("[XBL Parsing] line %d - %s\n", gf_xml_sax_get_line(parser->sax_parser), szMsg)); + } +#endif + if (e) parser->last_error = e; + return e; +} + +static void xbl_parse_progress(void *cbk, u32 done, u32 total) +{ + gf_set_progress("XBL Parsing", done, total); +} + +XBL_Element *gf_xbl_create_node(u32 ElementTag) +{ + XBL_Element *p; + GF_SAFEALLOC(p, SVG_Element); + gf_node_setup((GF_Node *)p, ElementTag); + gf_sg_parent_setup((GF_Node *) p); + return p; +} + +static XBL_Element *xbl_parse_element(GF_XBL_Parser *parser, const char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes, XBL_NodeStack *parent) +{ + u32 tag, attribute_tag, i; + XBL_Element *elt = NULL; + +// if (name_space && strlen(name_space)) return NULL; + + tag = gf_sg_node_get_tag_by_class_name(name, 0); + if (tag != TAG_UndefinedNode) { + elt = (XBL_Element *)gf_node_new(parser->load->scene_graph, tag); + } else { + elt = (XBL_Element *)gf_node_new(parser->load->scene_graph, TAG_DOMFullNode); + } + gf_node_register((GF_Node *)elt, (parent ? (GF_Node *)parent->node : NULL)); + if (parent && elt) gf_node_list_add_child_last( & parent->node->children, (GF_Node*)elt, & parent->last_child); + + for (i=0; ivalue || !strlen(att->value)) continue; + attribute_tag = gf_xml_get_attribute_tag((GF_Node *)elt, att->name, 0); + if (attribute_tag!=TAG_DOM_ATT_any) { + /*FIXME do we need to check if the attribute is specified several times*/ + GF_DOMAttribute *dom_att = gf_xml_create_attribute((GF_Node*)elt, attribute_tag); + dom_att->data = strdup(att->value); + } else { + xbl_parse_report(parser, GF_OK, "Skipping attribute %s on node %s", att->name, name); + } + } + + return elt; +} + +static void xbl_node_start(void *sax_cbck, const char *name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + GF_XBL_Parser *parser = (GF_XBL_Parser *)sax_cbck; + XBL_NodeStack *stack, *parent; + XBL_Element *elt; + + parent = (XBL_NodeStack *)gf_list_last(parser->node_stack); + + elt = xbl_parse_element(parser, name, name_space, attributes, nb_attributes, parent); + if (!elt) { + if (parent) parent->unknown_depth++; + xbl_parse_report(parser, GF_OK, "Ignoring unknown element %s", name); + return; + } + if (!parser->root) parser->root = elt; + + GF_SAFEALLOC(stack, XBL_NodeStack); + stack->node = elt; + gf_list_add(parser->node_stack, stack); + +} + +static void xbl_text_content(void *sax_cbck, const char *text_content, Bool is_cdata) +{ + GF_XBL_Parser *parser = (GF_XBL_Parser *)sax_cbck; + XBL_NodeStack *top = (XBL_NodeStack *)gf_list_last(parser->node_stack); + + /* TODO */ +} + +static void xbl_node_end(void *sax_cbck, const char *name, const char *name_space) +{ + GF_XBL_Parser *parser = (GF_XBL_Parser *)sax_cbck; + XBL_NodeStack *top = (XBL_NodeStack *)gf_list_last(parser->node_stack); + + if (!top) return; + if (/*!name_space && */gf_sg_node_get_tag_by_class_name(name, 0) != TAG_UndefinedNode) { + const char *the_name; + XBL_Element *node = top->node; + /*check node name...*/ + the_name = gf_node_get_class_name((GF_Node *) node); + if (strcmp(the_name, name)) { + if (top->unknown_depth) { + top->unknown_depth--; + return; + } else { + xbl_parse_report(parser, GF_BAD_PARAM, "depth mismatch"); + return; + } + } + free(top); + gf_list_rem_last(parser->node_stack); + } else if (top) { + if (top->unknown_depth) { + top->unknown_depth--; + } else { + xbl_parse_report(parser, GF_BAD_PARAM, "depth mismatch"); + } + } + +} + +static GF_XBL_Parser *xbl_new_parser(GF_SceneLoader *load) +{ + GF_XBL_Parser *parser; + if (load->type==GF_SM_LOAD_XBL) { + if (!load->ctx) return NULL; + } else if (load->type!=GF_SM_LOAD_XBL) return NULL; + + GF_SAFEALLOC(parser, GF_XBL_Parser); + parser->node_stack = gf_list_new(); + parser->sax_parser = gf_xml_sax_new(xbl_node_start, xbl_node_end, xbl_text_content, parser); + parser->load = load; + load->loader_priv = parser; + + return parser; +} + + +GF_Err gf_sm_load_init_xbl(GF_SceneLoader *load) +{ + GF_Err e; + GF_XBL_Parser *parser; + + if (!load->fileName) return GF_BAD_PARAM; + parser = xbl_new_parser(load); + GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ( "[Parser] XBL Parsing\n") ); + e = gf_xml_sax_parse_file(parser->sax_parser, (const char *)load->fileName, xbl_parse_progress); + if (e<0) return xbl_parse_report(parser, e, "Unable to parse file %s: %s", load->fileName, gf_xml_sax_get_error(parser->sax_parser) ); + return parser->last_error; +} + +GF_Err gf_sm_load_run_xbl(GF_SceneLoader *load) +{ + return GF_OK; +} + +GF_Err gf_sm_load_done_xbl(GF_SceneLoader *load) +{ + GF_XBL_Parser *parser = (GF_XBL_Parser *)load->loader_priv; + if (!parser) return GF_OK; + while (gf_list_count(parser->node_stack)) { + XBL_NodeStack *st = (XBL_NodeStack *)gf_list_last(parser->node_stack); + gf_list_rem_last(parser->node_stack); + free(st); + } + gf_list_del(parser->node_stack); + if (parser->sax_parser) gf_xml_sax_del(parser->sax_parser); + free(parser); + load->loader_priv = NULL; + return GF_OK; +} + +void apply(GF_Node *bound_doc, GF_Node *binding_doc) +{ + +} + +#endif diff --git a/src/scenegraph/xml_ns.c b/src/scenegraph/xml_ns.c new file mode 100644 index 0000000..00044c3 --- /dev/null +++ b/src/scenegraph/xml_ns.c @@ -0,0 +1,1032 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean Le Feuvre + * Copyright (c)2008-200X ENST - All rights reserved + * + * This file is part of GPAC / SVG Scene Graph sub-project + * + * GPAC is free software, TYPE, 0 }, you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, TYPE, 0 }, either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, TYPE, 0 }, without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library, TYPE, 0 }, see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include + +#ifndef GPAC_DISABLE_SVG + +#include + +enum +{ + /*no specific criteria*/ + GF_SVG_ATTOPT_NONE = 0, + /*attribute only valid for SMIL anims*/ + GF_SVG_ATTOPT_SMIL = 1, + /*attribute only valid for TEXT*/ + GF_SVG_ATTOPT_TEXT = 2, + /*attribute only valid for cursor*/ + GF_SVG_ATTOPT_CURSOR = 3, + /*attribute only valid for listener*/ + GF_SVG_ATTOPT_LISTENER = 4, +} GF_SVGAttOption; + +static const struct xml_att_def { const char *name; u32 tag; u32 type; u32 opts; u32 xmlns; } xml_attributes [] = +{ + /*XML base*/ + { "id", TAG_XML_ATT_id, SVG_ID_datatype, 0, GF_XMLNS_XML } , + { "base", TAG_XML_ATT_base, XMLRI_datatype, 0, GF_XMLNS_XML } , + { "lang", TAG_XML_ATT_lang, SVG_LanguageID_datatype, 0, GF_XMLNS_XML } , + { "space", TAG_XML_ATT_space, XML_Space_datatype, 0, GF_XMLNS_XML } , + + /*XLINK*/ + { "type", TAG_XLINK_ATT_type, DOM_String_datatype, 0, GF_XMLNS_XLINK }, + { "role", TAG_XLINK_ATT_role, XMLRI_datatype, 0, GF_XMLNS_XLINK }, + { "arcrole", TAG_XLINK_ATT_arcrole, XMLRI_datatype, 0, GF_XMLNS_XLINK }, + { "title", TAG_XLINK_ATT_title, DOM_String_datatype, 0, GF_XMLNS_XLINK }, + { "href", TAG_XLINK_ATT_href, XMLRI_datatype, 0, GF_XMLNS_XLINK }, + { "show", TAG_XLINK_ATT_show, DOM_String_datatype, 0, GF_XMLNS_XLINK }, + { "actuate", TAG_XLINK_ATT_actuate, DOM_String_datatype, 0, GF_XMLNS_XLINK }, + + /*XML Events*/ + { "event", TAG_XMLEV_ATT_event, XMLEV_Event_datatype, 0, GF_XMLNS_XMLEV }, + { "phase", TAG_XMLEV_ATT_phase, XMLEV_Phase_datatype, 0, GF_XMLNS_XMLEV }, + { "propagate", TAG_XMLEV_ATT_propagate, XMLEV_Propagate_datatype, 0, GF_XMLNS_XMLEV }, + { "defaultAction", TAG_XMLEV_ATT_defaultAction, XMLEV_DefaultAction_datatype, 0, GF_XMLNS_XMLEV }, + { "observer", TAG_XMLEV_ATT_observer, XML_IDREF_datatype, 0, GF_XMLNS_XMLEV }, + { "target", TAG_XMLEV_ATT_target, XML_IDREF_datatype, 0, GF_XMLNS_XMLEV }, + { "handler", TAG_XMLEV_ATT_handler, XMLRI_datatype, 0, GF_XMLNS_XMLEV }, + + + { "id", TAG_SVG_ATT_id, SVG_ID_datatype, 0, GF_XMLNS_SVG } , + { "class", TAG_SVG_ATT__class, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "requiredFeatures", TAG_SVG_ATT_requiredFeatures, XMLRI_List_datatype, 0, GF_XMLNS_SVG }, + { "requiredExtensions", TAG_SVG_ATT_requiredExtensions, XMLRI_List_datatype, 0, GF_XMLNS_SVG }, + { "requiredFormats", TAG_SVG_ATT_requiredFormats, DOM_StringList_datatype, 0, GF_XMLNS_SVG }, + { "requiredFonts", TAG_SVG_ATT_requiredFonts, DOM_StringList_datatype, 0, GF_XMLNS_SVG }, + { "systemLanguage", TAG_SVG_ATT_systemLanguage, DOM_StringList_datatype, 0, GF_XMLNS_SVG }, + { "display", TAG_SVG_ATT_display, SVG_Display_datatype, 0, GF_XMLNS_SVG }, + { "visibility", TAG_SVG_ATT_visibility, SVG_Visibility_datatype, 0, GF_XMLNS_SVG }, + { "image-rendering", TAG_SVG_ATT_image_rendering, SVG_RenderingHint_datatype, 0, GF_XMLNS_SVG }, + { "pointer-events", TAG_SVG_ATT_pointer_events, SVG_PointerEvents_datatype, 0, GF_XMLNS_SVG }, + { "shape-rendering", TAG_SVG_ATT_shape_rendering, SVG_RenderingHint_datatype, 0, GF_XMLNS_SVG }, + { "text-rendering", TAG_SVG_ATT_text_rendering, SVG_RenderingHint_datatype, 0, GF_XMLNS_SVG }, + { "audio-level", TAG_SVG_ATT_audio_level, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "viewport-fill", TAG_SVG_ATT_viewport_fill, SVG_Paint_datatype, 0, GF_XMLNS_SVG }, + { "viewport-fill-opacity", TAG_SVG_ATT_viewport_fill_opacity, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "overflow", TAG_SVG_ATT_overflow, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "fill-opacity", TAG_SVG_ATT_fill_opacity, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "stroke-opacity", TAG_SVG_ATT_stroke_opacity, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "fill-rule", TAG_SVG_ATT_fill_rule, SVG_FillRule_datatype, 0, GF_XMLNS_SVG }, + { "stroke", TAG_SVG_ATT_stroke, SVG_Paint_datatype, 0, GF_XMLNS_SVG }, + { "stroke-dasharray", TAG_SVG_ATT_stroke_dasharray, SVG_StrokeDashArray_datatype, 0, GF_XMLNS_SVG }, + { "stroke-dashoffset", TAG_SVG_ATT_stroke_dashoffset, SVG_Length_datatype, 0, GF_XMLNS_SVG }, + { "stroke-linecap", TAG_SVG_ATT_stroke_linecap, SVG_StrokeLineCap_datatype, 0, GF_XMLNS_SVG }, + { "stroke-linejoin", TAG_SVG_ATT_stroke_linejoin, SVG_StrokeLineJoin_datatype, 0, GF_XMLNS_SVG }, + { "stroke-miterlimit", TAG_SVG_ATT_stroke_miterlimit, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "stroke-width", TAG_SVG_ATT_stroke_width, SVG_Length_datatype, 0, GF_XMLNS_SVG }, + { "color", TAG_SVG_ATT_color, SVG_Paint_datatype, 0, GF_XMLNS_SVG }, + { "color-rendering", TAG_SVG_ATT_color_rendering, SVG_RenderingHint_datatype, 0, GF_XMLNS_SVG }, + { "vector-effect", TAG_SVG_ATT_vector_effect, SVG_VectorEffect_datatype, 0, GF_XMLNS_SVG }, + { "solid-color", TAG_SVG_ATT_solid_color, SVG_Paint_datatype, 0, GF_XMLNS_SVG }, + { "solid-opacity", TAG_SVG_ATT_solid_opacity, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "display-align", TAG_SVG_ATT_display_align, SVG_DisplayAlign_datatype, 0, GF_XMLNS_SVG }, + { "line-increment", TAG_SVG_ATT_line_increment, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "stop-color", TAG_SVG_ATT_stop_color, SVG_Paint_datatype, 0, GF_XMLNS_SVG }, + { "stop-opacity", TAG_SVG_ATT_stop_opacity, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "font-family", TAG_SVG_ATT_font_family, SVG_FontFamily_datatype, 0, GF_XMLNS_SVG }, + { "font-size", TAG_SVG_ATT_font_size, SVG_FontSize_datatype, 0, GF_XMLNS_SVG }, + { "font-style", TAG_SVG_ATT_font_style, SVG_FontStyle_datatype, 0, GF_XMLNS_SVG }, + { "font-variant", TAG_SVG_ATT_font_variant, SVG_FontVariant_datatype, 0, GF_XMLNS_SVG }, + { "font-weight", TAG_SVG_ATT_font_weight, SVG_FontWeight_datatype, 0, GF_XMLNS_SVG }, + { "text-anchor", TAG_SVG_ATT_text_anchor, SVG_TextAnchor_datatype, 0, GF_XMLNS_SVG }, + { "text-align", TAG_SVG_ATT_text_align, SVG_TextAlign_datatype, 0, GF_XMLNS_SVG }, + { "text-decoration", TAG_SVG_ATT_text_decoration, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "focusHighlight", TAG_SVG_ATT_focusHighlight, SVG_FocusHighlight_datatype, 0, GF_XMLNS_SVG }, + { "externalResourcesRequired", TAG_SVG_ATT_externalResourcesRequired, SVG_Boolean_datatype, 0, GF_XMLNS_SVG }, + { "focusable", TAG_SVG_ATT_focusable, SVG_Focusable_datatype, 0, GF_XMLNS_SVG }, + { "nav-next", TAG_SVG_ATT_nav_next, SVG_Focus_datatype, 0, GF_XMLNS_SVG }, + { "nav-prev", TAG_SVG_ATT_nav_prev, SVG_Focus_datatype, 0, GF_XMLNS_SVG }, + { "nav-up", TAG_SVG_ATT_nav_up, SVG_Focus_datatype, 0, GF_XMLNS_SVG }, + { "nav-up-right", TAG_SVG_ATT_nav_up_right, SVG_Focus_datatype, 0, GF_XMLNS_SVG }, + { "nav-right", TAG_SVG_ATT_nav_right, SVG_Focus_datatype, 0, GF_XMLNS_SVG }, + { "nav-down-right", TAG_SVG_ATT_nav_down_right, SVG_Focus_datatype, 0, GF_XMLNS_SVG }, + { "nav-down", TAG_SVG_ATT_nav_down, SVG_Focus_datatype, 0, GF_XMLNS_SVG }, + { "nav-down-left", TAG_SVG_ATT_nav_down_left, SVG_Focus_datatype, 0, GF_XMLNS_SVG }, + { "nav-left", TAG_SVG_ATT_nav_left, SVG_Focus_datatype, 0, GF_XMLNS_SVG }, + { "nav-up-left", TAG_SVG_ATT_nav_up_left, SVG_Focus_datatype, 0, GF_XMLNS_SVG }, + { "transform", TAG_SVG_ATT_transform, SVG_Transform_datatype, 0, GF_XMLNS_SVG }, + { "target", TAG_SVG_ATT_target, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "attributeName", TAG_SVG_ATT_attributeName, SMIL_AttributeName_datatype, 0, GF_XMLNS_SVG }, + { "attributeType", TAG_SVG_ATT_attributeType, SMIL_AttributeType_datatype, 0, GF_XMLNS_SVG }, + { "begin", TAG_SVG_ATT_begin, SMIL_Times_datatype, 0, GF_XMLNS_SVG }, + { "dur", TAG_SVG_ATT_dur, SMIL_Duration_datatype, 0, GF_XMLNS_SVG }, + { "end", TAG_SVG_ATT_end, SMIL_Times_datatype, 0, GF_XMLNS_SVG }, + { "repeatCount", TAG_SVG_ATT_repeatCount, SMIL_RepeatCount_datatype, 0, GF_XMLNS_SVG }, + { "repeatDur", TAG_SVG_ATT_repeatDur, SMIL_Duration_datatype, 0, GF_XMLNS_SVG }, + { "restart", TAG_SVG_ATT_restart, SMIL_Restart_datatype, 0, GF_XMLNS_SVG }, + { "min", TAG_SVG_ATT_min, SMIL_Duration_datatype, 0, GF_XMLNS_SVG }, + { "max", TAG_SVG_ATT_max, SMIL_Duration_datatype, 0, GF_XMLNS_SVG }, + { "to", TAG_SVG_ATT_to, SMIL_AnimateValue_datatype, 0, GF_XMLNS_SVG }, + { "calcMode", TAG_SVG_ATT_calcMode, SMIL_CalcMode_datatype, 0, GF_XMLNS_SVG }, + { "values", TAG_SVG_ATT_values, SMIL_AnimateValues_datatype, 0, GF_XMLNS_SVG }, + { "keyTimes", TAG_SVG_ATT_keyTimes, SMIL_KeyTimes_datatype, 0, GF_XMLNS_SVG }, + { "keySplines", TAG_SVG_ATT_keySplines, SMIL_KeySplines_datatype, 0, GF_XMLNS_SVG }, + { "from", TAG_SVG_ATT_from, SMIL_AnimateValue_datatype, 0, GF_XMLNS_SVG }, + { "by", TAG_SVG_ATT_by, SMIL_AnimateValue_datatype, 0, GF_XMLNS_SVG }, + { "additive", TAG_SVG_ATT_additive, SMIL_Additive_datatype, 0, GF_XMLNS_SVG }, + { "accumulate", TAG_SVG_ATT_accumulate, SMIL_Accumulate_datatype, 0, GF_XMLNS_SVG }, + { "path", TAG_SVG_ATT_path, SVG_PathData_datatype, 0, GF_XMLNS_SVG }, + { "keyPoints", TAG_SVG_ATT_keyPoints, SMIL_KeyPoints_datatype, 0, GF_XMLNS_SVG }, + { "origin", TAG_SVG_ATT_origin, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "clipBegin", TAG_SVG_ATT_clipBegin, SVG_Clock_datatype, 0, GF_XMLNS_SVG }, + { "clipEnd", TAG_SVG_ATT_clipEnd, SVG_Clock_datatype, 0, GF_XMLNS_SVG }, + { "syncBehavior", TAG_SVG_ATT_syncBehavior, SMIL_SyncBehavior_datatype, 0, GF_XMLNS_SVG }, + { "syncTolerance", TAG_SVG_ATT_syncTolerance, SMIL_SyncTolerance_datatype, 0, GF_XMLNS_SVG }, + { "syncMaster", TAG_SVG_ATT_syncMaster, SVG_Boolean_datatype, 0, GF_XMLNS_SVG }, + { "syncReference", TAG_SVG_ATT_syncReference, XMLRI_datatype, 0, GF_XMLNS_SVG }, + { "width", TAG_SVG_ATT_width, SVG_Length_datatype, 0, GF_XMLNS_SVG }, + { "height", TAG_SVG_ATT_height, SVG_Length_datatype, 0, GF_XMLNS_SVG }, + { "preserveAspectRatio", TAG_SVG_ATT_preserveAspectRatio, SVG_PreserveAspectRatio_datatype, 0, GF_XMLNS_SVG }, + { "initialVisibility", TAG_SVG_ATT_initialVisibility, SVG_InitialVisibility_datatype, 0, GF_XMLNS_SVG }, + { "cx", TAG_SVG_ATT_cx, SVG_Coordinate_datatype, 0, GF_XMLNS_SVG }, + { "cy", TAG_SVG_ATT_cy, SVG_Coordinate_datatype, 0, GF_XMLNS_SVG }, + { "r", TAG_SVG_ATT_r, SVG_Length_datatype, 0, GF_XMLNS_SVG }, + { "rx", TAG_SVG_ATT_rx, SVG_Length_datatype, 0, GF_XMLNS_SVG }, + { "ry", TAG_SVG_ATT_ry, SVG_Length_datatype, 0, GF_XMLNS_SVG }, + { "horiz-adv-x", TAG_SVG_ATT_horiz_adv_x, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "horiz-origin-x", TAG_SVG_ATT_horiz_origin_x, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "font-stretch", TAG_SVG_ATT_font_stretch, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "unicode-range", TAG_SVG_ATT_unicode_range, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "panose-1", TAG_SVG_ATT_panose_1, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "widths", TAG_SVG_ATT_widths, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "bbox", TAG_SVG_ATT_bbox, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "units-per-em", TAG_SVG_ATT_units_per_em, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "stemv", TAG_SVG_ATT_stemv, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "stemh", TAG_SVG_ATT_stemh, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "slope", TAG_SVG_ATT_slope, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "cap-height", TAG_SVG_ATT_cap_height, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "x-height", TAG_SVG_ATT_x_height, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "accent-height", TAG_SVG_ATT_accent_height, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "ascent", TAG_SVG_ATT_ascent, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "descent", TAG_SVG_ATT_descent, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "ideographic", TAG_SVG_ATT_ideographic, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "alphabetic", TAG_SVG_ATT_alphabetic, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "mathematical", TAG_SVG_ATT_mathematical, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "hanging", TAG_SVG_ATT_hanging, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "underline-position", TAG_SVG_ATT_underline_position, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "underline-thickness", TAG_SVG_ATT_underline_thickness, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "strikethrough-position", TAG_SVG_ATT_strikethrough_position, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "strikethrough-thickness", TAG_SVG_ATT_strikethrough_thickness, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "overline-position", TAG_SVG_ATT_overline_position, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "overline-thickness", TAG_SVG_ATT_overline_thickness, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "d", TAG_SVG_ATT_d, SVG_PathData_datatype, 0, GF_XMLNS_SVG }, + { "unicode", TAG_SVG_ATT_unicode, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "glyph-name", TAG_SVG_ATT_glyph_name, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "arabic-form", TAG_SVG_ATT_arabic_form, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "lang", TAG_SVG_ATT_lang, DOM_StringList_datatype, 0, GF_XMLNS_SVG }, + { "u1", TAG_SVG_ATT_u1, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "g1", TAG_SVG_ATT_g1, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "u2", TAG_SVG_ATT_u2, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "g2", TAG_SVG_ATT_g2, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "k", TAG_SVG_ATT_k, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "opacity", TAG_SVG_ATT_opacity, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "x1", TAG_SVG_ATT_x1, SVG_Coordinate_datatype, 0, GF_XMLNS_SVG }, + { "y1", TAG_SVG_ATT_y1, SVG_Coordinate_datatype, 0, GF_XMLNS_SVG }, + { "x2", TAG_SVG_ATT_x2, SVG_Coordinate_datatype, 0, GF_XMLNS_SVG }, + { "y2", TAG_SVG_ATT_y2, SVG_Coordinate_datatype, 0, GF_XMLNS_SVG }, + { "gradientUnits", TAG_SVG_ATT_gradientUnits, SVG_GradientUnit_datatype, 0, GF_XMLNS_SVG }, + { "spreadMethod", TAG_SVG_ATT_spreadMethod, SVG_SpreadMethod_datatype, 0, GF_XMLNS_SVG }, + { "gradientTransform", TAG_SVG_ATT_gradientTransform, SVG_Transform_datatype, 0, GF_XMLNS_SVG }, + { "pathLength", TAG_SVG_ATT_pathLength, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "points", TAG_SVG_ATT_points, SVG_Points_datatype, 0, GF_XMLNS_SVG }, + { "mediaSize", TAG_SVG_ATT_mediaSize, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "mediaTime", TAG_SVG_ATT_mediaTime, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "mediaCharacterEncoding", TAG_SVG_ATT_mediaCharacterEncoding, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "mediaContentEncodings", TAG_SVG_ATT_mediaContentEncodings, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "bandwidth", TAG_SVG_ATT_bandwidth, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "fx", TAG_SVG_ATT_fx, SVG_Coordinate_datatype, 0, GF_XMLNS_SVG }, + { "fy", TAG_SVG_ATT_fy, SVG_Coordinate_datatype, 0, GF_XMLNS_SVG }, + { "size", TAG_SVG_ATT_size, LASeR_Size_datatype, 0, GF_XMLNS_SVG }, + { "choice", TAG_SVG_ATT_choice, LASeR_Choice_datatype, 0, GF_XMLNS_SVG }, + { "delta", TAG_SVG_ATT_delta, LASeR_Size_datatype, 0, GF_XMLNS_SVG }, + { "offset", TAG_SVG_ATT_offset, SVG_Number_datatype, 0, GF_XMLNS_SVG }, + { "syncBehaviorDefault", TAG_SVG_ATT_syncBehaviorDefault, SMIL_SyncBehavior_datatype, 0, GF_XMLNS_SVG }, + { "syncToleranceDefault", TAG_SVG_ATT_syncToleranceDefault, SMIL_SyncTolerance_datatype, 0, GF_XMLNS_SVG }, + { "viewBox", TAG_SVG_ATT_viewBox, SVG_ViewBox_datatype, 0, GF_XMLNS_SVG }, + { "zoomAndPan", TAG_SVG_ATT_zoomAndPan, SVG_ZoomAndPan_datatype, 0, GF_XMLNS_SVG }, + { "version", TAG_SVG_ATT_version, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "baseProfile", TAG_SVG_ATT_baseProfile, DOM_String_datatype, 0, GF_XMLNS_SVG }, + { "snapshotTime", TAG_SVG_ATT_snapshotTime, SVG_Clock_datatype, 0, GF_XMLNS_SVG }, + { "timelineBegin", TAG_SVG_ATT_timelineBegin, SVG_TimelineBegin_datatype, 0, GF_XMLNS_SVG }, + { "playbackOrder", TAG_SVG_ATT_playbackOrder, SVG_PlaybackOrder_datatype, 0, GF_XMLNS_SVG }, + { "editable", TAG_SVG_ATT_editable, SVG_Boolean_datatype, 0, GF_XMLNS_SVG }, + { "transformBehavior", TAG_SVG_ATT_transformBehavior, SVG_TransformBehavior_datatype, 0, GF_XMLNS_SVG }, + { "overlay", TAG_SVG_ATT_overlay, SVG_Overlay_datatype, 0, GF_XMLNS_SVG }, + { "fullscreen", TAG_SVG_ATT_fullscreen, SVG_Boolean_datatype, 0, GF_XMLNS_SVG }, + { "motionTransform", TAG_SVG_ATT_motionTransform, SVG_Motion_datatype, 0, GF_XMLNS_SVG }, + /*SMIL anim fill*/ + { "fill", TAG_SVG_ATT_smil_fill, SMIL_Fill_datatype, GF_SVG_ATTOPT_SMIL, GF_XMLNS_SVG }, + /*regular paint fill*/ + { "fill", TAG_SVG_ATT_fill, SVG_Paint_datatype, 0, GF_XMLNS_SVG }, + /*text rotate*/ + { "rotate", TAG_SVG_ATT_text_rotate, SVG_Numbers_datatype, GF_SVG_ATTOPT_TEXT, GF_XMLNS_SVG }, + /*regular matrix rotate*/ + { "rotate", TAG_SVG_ATT_rotate, SVG_Rotate_datatype, 0, GF_XMLNS_SVG }, + /*SMIL anim type*/ + { "type", TAG_SVG_ATT_transform_type, SVG_TransformType_datatype, GF_SVG_ATTOPT_SMIL, GF_XMLNS_SVG }, + /*regular content type*/ + { "type", TAG_SVG_ATT_type, SVG_ContentType_datatype, 0, GF_XMLNS_SVG }, + /*text x*/ + { "x", TAG_SVG_ATT_text_x, SVG_Coordinates_datatype, GF_SVG_ATTOPT_TEXT, GF_XMLNS_SVG }, + /*regular x position*/ + { "x", TAG_SVG_ATT_x, SVG_Coordinate_datatype, 0, GF_XMLNS_SVG }, + /*text y*/ + { "y", TAG_SVG_ATT_text_y, SVG_Coordinates_datatype, GF_SVG_ATTOPT_TEXT, GF_XMLNS_SVG }, + /*regular y position*/ + { "y", TAG_SVG_ATT_y, SVG_Coordinate_datatype, 0, GF_XMLNS_SVG }, + + /*LASeR*/ + { "enabled", TAG_LSR_ATT_enabled, SVG_Boolean_datatype, 0, GF_XMLNS_LASER }, + /*cursor x*/ + { "x", TAG_SVG_ATT_cursorManager_x, SVG_Length_datatype, GF_SVG_ATTOPT_CURSOR, GF_XMLNS_LASER }, + /*cursor y*/ + { "y", TAG_SVG_ATT_cursorManager_y, SVG_Length_datatype, GF_SVG_ATTOPT_CURSOR, GF_XMLNS_LASER }, + + /*XBL*/ + { "id", TAG_XBL_ATT_id, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "extends", TAG_XBL_ATT_extends, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "display", TAG_XBL_ATT_display, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "inheritstyle", TAG_XBL_ATT_inheritstyle, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "includes", TAG_XBL_ATT_includes, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "name", TAG_XBL_ATT_name, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "implements", TAG_XBL_ATT_implements, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "type", TAG_XBL_ATT_type, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "readonly", TAG_XBL_ATT_readonly, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "onget", TAG_XBL_ATT_onget, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "onset", TAG_XBL_ATT_onset, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "event", TAG_XBL_ATT_event, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "action", TAG_XBL_ATT_action, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "phase", TAG_XBL_ATT_phase, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "button", TAG_XBL_ATT_button, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "modifiers", TAG_XBL_ATT_modifiers, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "keycode", TAG_XBL_ATT_keycode, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "key", TAG_XBL_ATT_key, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "charcode", TAG_XBL_ATT_charcode, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "clickcount", TAG_XBL_ATT_clickcount, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "command", TAG_XBL_ATT_command, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "preventdefault", TAG_XBL_ATT_preventdefault, DOM_String_datatype, 0, GF_XMLNS_XBL }, + { "src", TAG_XBL_ATT_src, DOM_String_datatype, 0, GF_XMLNS_XBL }, +}; + +void gf_xml_push_namespaces(GF_DOMNode *elt) +{ + GF_DOMAttribute *att = elt->attributes; + while (att) { + if (att->tag==TAG_DOM_ATT_any) { + GF_DOMFullAttribute *datt = (GF_DOMFullAttribute*)att; + if (datt->name && !strncmp(datt->name, "xmlns", 5)) { + char *qname = datt->name+5; + gf_sg_add_namespace(elt->sgprivate->scenegraph, *(DOM_String *) datt->data, qname[0] ? qname+1 : NULL); + } + } + att = att->next; + } +} + +void gf_xml_pop_namespaces(GF_DOMNode *elt) +{ + GF_DOMAttribute *att = elt->attributes; + while (att) { + if (att->tag==TAG_DOM_ATT_any) { + GF_DOMFullAttribute *datt = (GF_DOMFullAttribute*)att; + if (datt->name && !strncmp(datt->name, "xmlns", 5)) { + char *qname = datt->name+5; + gf_sg_remove_namespace(elt->sgprivate->scenegraph, *(DOM_String *) datt->data, qname[0] ? qname+1 : NULL); + } + } + att = att->next; + } +} + + +u32 gf_xml_get_attribute_tag(GF_Node *elt, char *attribute_name, u32 ns) +{ + char *ns_sep; + u32 i, count; + count = sizeof(xml_attributes) / sizeof(struct xml_att_def); + + if (!ns) { + ns_sep = strchr(attribute_name, ':'); + ns = 0; + if (ns_sep) { + ns_sep[0] = 0; + ns = gf_sg_get_namespace_code(elt->sgprivate->scenegraph, attribute_name); + ns_sep[0] = ':'; + attribute_name = ++ns_sep; + } else { + ns = gf_xml_get_element_namespace(elt); + if (!ns) ns = gf_sg_get_namespace_code(elt->sgprivate->scenegraph, NULL); + } + } + + for (i=0; isgprivate->tag) { + case TAG_SVG_animate: + case TAG_SVG_animateColor: + case TAG_SVG_animateMotion: + case TAG_SVG_animateTransform: + case TAG_SVG_animation: + case TAG_SVG_audio: + case TAG_SVG_video: + case TAG_SVG_set: + return xml_attributes[i].tag; + default: + break; + } + break; + case GF_SVG_ATTOPT_TEXT: + if (elt->sgprivate->tag == TAG_SVG_text) + return xml_attributes[i].tag; + break; + case GF_SVG_ATTOPT_CURSOR: + if (elt->sgprivate->tag == TAG_LSR_cursorManager) + return xml_attributes[i].tag; + break; + case GF_SVG_ATTOPT_LISTENER: + if (elt->sgprivate->tag == TAG_SVG_listener) + return xml_attributes[i].tag; + break; + default: + return xml_attributes[i].tag; + } + } + return TAG_DOM_ATT_any; +} + +u32 gf_xml_get_attribute_type(u32 tag) +{ + u32 i, count; + count = sizeof(xml_attributes) / sizeof(struct xml_att_def); + for (i=0; isgprivate->scenegraph, NULL); + ns = gf_xml_get_element_namespace(node); + count = sizeof(xml_attributes) / sizeof(struct xml_att_def); + for (i=0; isgprivate->scenegraph, xml_attributes[i].xmlns); + if (xmlns) { + sprintf(node->sgprivate->scenegraph->szNameBuffer, "%s:%s", xmlns, xml_attributes[i].name); + return node->sgprivate->scenegraph->szNameBuffer; + } + return xml_attributes[i].name; + } + } + return 0; +} +GF_DOMAttribute *gf_xml_create_attribute(GF_Node *node, u32 tag) +{ + u32 type = gf_xml_get_attribute_type(tag); + return gf_node_create_attribute_from_datatype(type, tag); +} + +static const struct xml_elt_def { const char *name; u32 tag; u32 xmlns; } xml_elements [] = +{ + { "listener", TAG_SVG_listener, GF_XMLNS_XMLEV}, + /*SVG*/ + { "a", TAG_SVG_a, GF_XMLNS_SVG }, + { "animate", TAG_SVG_animate, GF_XMLNS_SVG }, + { "animateColor", TAG_SVG_animateColor, GF_XMLNS_SVG }, + { "animateMotion", TAG_SVG_animateMotion, GF_XMLNS_SVG }, + { "animateTransform", TAG_SVG_animateTransform, GF_XMLNS_SVG }, + { "animation", TAG_SVG_animation, GF_XMLNS_SVG }, + { "audio", TAG_SVG_audio, GF_XMLNS_SVG }, + { "circle", TAG_SVG_circle, GF_XMLNS_SVG }, + { "defs", TAG_SVG_defs, GF_XMLNS_SVG }, + { "desc", TAG_SVG_desc, GF_XMLNS_SVG }, + { "discard", TAG_SVG_discard, GF_XMLNS_SVG }, + { "ellipse", TAG_SVG_ellipse, GF_XMLNS_SVG }, + { "font", TAG_SVG_font, GF_XMLNS_SVG }, + { "font-face", TAG_SVG_font_face, GF_XMLNS_SVG }, + { "font-face-src", TAG_SVG_font_face_src, GF_XMLNS_SVG }, + { "font-face-uri", TAG_SVG_font_face_uri, GF_XMLNS_SVG }, + { "foreignObject", TAG_SVG_foreignObject, GF_XMLNS_SVG }, + { "g", TAG_SVG_g, GF_XMLNS_SVG }, + { "glyph", TAG_SVG_glyph, GF_XMLNS_SVG }, + { "handler", TAG_SVG_handler, GF_XMLNS_SVG }, + { "hkern", TAG_SVG_hkern, GF_XMLNS_SVG }, + { "image", TAG_SVG_image, GF_XMLNS_SVG }, + { "line", TAG_SVG_line, GF_XMLNS_SVG }, + { "linearGradient", TAG_SVG_linearGradient, GF_XMLNS_SVG }, + { "metadata", TAG_SVG_metadata, GF_XMLNS_SVG }, + { "missing-glyph", TAG_SVG_missing_glyph, GF_XMLNS_SVG }, + { "mpath", TAG_SVG_mpath, GF_XMLNS_SVG }, + { "path", TAG_SVG_path, GF_XMLNS_SVG }, + { "polygon", TAG_SVG_polygon, GF_XMLNS_SVG }, + { "polyline", TAG_SVG_polyline, GF_XMLNS_SVG }, + { "prefetch", TAG_SVG_prefetch, GF_XMLNS_SVG }, + { "radialGradient", TAG_SVG_radialGradient, GF_XMLNS_SVG }, + { "rect", TAG_SVG_rect, GF_XMLNS_SVG }, + { "script", TAG_SVG_script, GF_XMLNS_SVG }, + { "set", TAG_SVG_set, GF_XMLNS_SVG }, + { "solidColor", TAG_SVG_solidColor, GF_XMLNS_SVG }, + { "stop", TAG_SVG_stop, GF_XMLNS_SVG }, + { "svg", TAG_SVG_svg, GF_XMLNS_SVG }, + { "switch", TAG_SVG_switch, GF_XMLNS_SVG }, + { "tbreak", TAG_SVG_tbreak, GF_XMLNS_SVG }, + { "text", TAG_SVG_text, GF_XMLNS_SVG }, + { "textArea", TAG_SVG_textArea, GF_XMLNS_SVG }, + { "title", TAG_SVG_title, GF_XMLNS_SVG }, + { "tspan", TAG_SVG_tspan, GF_XMLNS_SVG }, + { "use", TAG_SVG_use, GF_XMLNS_SVG }, + { "video", TAG_SVG_video, GF_XMLNS_SVG }, + + /*LASeR*/ + { "conditional", TAG_LSR_conditional, GF_XMLNS_LASER }, + { "rectClip", TAG_LSR_rectClip, GF_XMLNS_LASER }, + { "cursorManager", TAG_LSR_cursorManager, GF_XMLNS_LASER }, + { "selector", TAG_LSR_selector, GF_XMLNS_LASER }, + { "simpleLayout", TAG_LSR_simpleLayout, GF_XMLNS_LASER }, + + /*XBL*/ + { "bindings", TAG_XBL_bindings, GF_XMLNS_XBL }, + { "binding", TAG_XBL_binding, GF_XMLNS_XBL }, + { "content", TAG_XBL_content, GF_XMLNS_XBL }, + { "children", TAG_XBL_children, GF_XMLNS_XBL }, + { "implementation", TAG_XBL_implementation, GF_XMLNS_XBL }, + { "constructor", TAG_XBL_constructor, GF_XMLNS_XBL }, + { "destructor", TAG_XBL_destructor, GF_XMLNS_XBL }, + { "field", TAG_XBL_field, GF_XMLNS_XBL }, + { "property", TAG_XBL_property, GF_XMLNS_XBL }, + { "getter", TAG_XBL_getter, GF_XMLNS_XBL }, + { "setter", TAG_XBL_setter, GF_XMLNS_XBL }, + { "method", TAG_XBL_method, GF_XMLNS_XBL }, + { "parameter", TAG_XBL_parameter, GF_XMLNS_XBL }, + { "body", TAG_XBL_body, GF_XMLNS_XBL }, + { "handlers", TAG_XBL_handlers, GF_XMLNS_XBL }, + { "handler", TAG_XBL_handler, GF_XMLNS_XBL }, + { "resources", TAG_XBL_resources, GF_XMLNS_XBL }, + { "stylesheet", TAG_XBL_stylesheet, GF_XMLNS_XBL }, + { "image", TAG_XBL_image, GF_XMLNS_XBL }, +}; + +u32 gf_xml_get_element_tag(const char *element_name, u32 ns) +{ + u32 i, count; + if (!element_name) return TAG_UndefinedNode; + count = sizeof(xml_elements) / sizeof(struct xml_elt_def); + for (i=0; isgprivate->scenegraph, NULL) : 0; + count = sizeof(xml_elements) / sizeof(struct xml_elt_def); + for (i=0; isgprivate->tag==xml_elements[i].tag) { + char *xmlns; + if (!n || (ns == xml_elements[i].xmlns)) + return xml_elements[i].name; + + xmlns = (char *) gf_sg_get_namespace_qname(n->sgprivate->scenegraph, xml_elements[i].xmlns); + if (xmlns) { + sprintf(n->sgprivate->scenegraph->szNameBuffer, "%s:%s", xmlns, xml_elements[i].name); + return n->sgprivate->scenegraph->szNameBuffer; + } + return xml_elements[i].name; + } + } + return "UndefinedNode"; +} + +u32 gf_xml_get_element_namespace(GF_Node *n) +{ + u32 i, count; + if (n->sgprivate->tag==TAG_DOMFullNode) { + GF_DOMFullNode *elt = (GF_DOMFullNode *)n; + return elt->ns; + } + + count = sizeof(xml_elements) / sizeof(struct xml_elt_def); + for (i=0; isgprivate->tag==xml_elements[i].tag) return xml_elements[i].xmlns; + } + return GF_XMLNS_NONE; +} + + +u32 gf_node_get_attribute_count(GF_Node *node) +{ + u32 count = 0; + GF_DOMNode *dom = (GF_DOMNode *)node; + GF_DOMAttribute *atts = dom->attributes; + while (atts) { + count++; + atts = atts->next; + } + return count; +} + +GF_Err gf_node_get_attribute_info(GF_Node *node, GF_FieldInfo *info) +{ + GF_DOMNode *dom = (GF_DOMNode *)node; + GF_DOMAttribute *atts = dom->attributes; + while (atts) { + if (atts->tag == info->fieldIndex) { + info->fieldType = atts->data_type; + info->far_ptr = atts->data; + return GF_OK; + } + atts = atts->next; + } + info->fieldType = 0; + info->far_ptr = NULL; + return GF_NOT_SUPPORTED; +} + +void gf_node_delete_attributes(GF_Node *node) +{ + GF_DOMAttribute *tmp; + GF_DOMAttribute *att = ((GF_DOMNode*)node)->attributes; + while(att) { + gf_svg_delete_attribute_value(att->data_type, att->data, node->sgprivate->scenegraph); + tmp = att; + att = att->next; + if (tmp->tag==TAG_DOM_ATT_any) { + free( ((GF_DOMFullAttribute*)tmp)->name); + } + free(tmp); + } +} + +SVGAttribute *gf_node_create_attribute_from_datatype(u32 data_type, u32 attribute_tag) +{ + SVGAttribute *att; + if (!data_type) return NULL; + + GF_SAFEALLOC(att, SVGAttribute); + att->data_type = (u16) data_type; + att->tag = (u16) attribute_tag; + att->data = gf_svg_create_attribute_value(att->data_type); + return att; +} + +GF_Err gf_node_get_attribute_by_name(GF_Node *node, char *name, u32 xmlns_code, Bool create_if_not_found, Bool set_default, GF_FieldInfo *field) +{ + u32 attribute_tag = gf_xml_get_attribute_tag(node, name, xmlns_code); + if (attribute_tag == TAG_DOM_ATT_any) { + u32 len = 0; + const char *ns = NULL; + SVGAttribute *last_att = NULL; + GF_DOMFullAttribute *att = (GF_DOMFullAttribute *) ((SVG_Element*)node)->attributes; + if (xmlns_code) ns = gf_sg_get_namespace_qname(node->sgprivate->scenegraph, xmlns_code); + if (ns) len = strlen(ns); + + while (att) { + if (((u32) att->tag == TAG_DOM_ATT_any) && + ((!ns && !strcmp(name, att->name)) || (!strncmp(att->name, ns, len) && !strcmp(att->name+len+1, name))) + ) { + field->fieldIndex = att->tag; + field->fieldType = att->data_type; + field->far_ptr = att->data; + return GF_OK; + } + last_att = (SVGAttribute *) att; + att = (GF_DOMFullAttribute *) att->next; + } + if (create_if_not_found) { + GF_SAFEALLOC(att, GF_DOMFullAttribute); + att->data_type = (u16) DOM_String_datatype; + att->tag = (u16) TAG_DOM_ATT_any; + att->data = gf_svg_create_attribute_value(att->data_type); + + att->name = strdup(name); + if (!xmlns_code) + att->xmlns = gf_xml_get_element_namespace(node); + else + att->xmlns = xmlns_code; + + if (last_att) last_att->next = (SVGAttribute *)att; + else ((SVG_Element*)node)->attributes = (SVGAttribute *)att; + + field->far_ptr = att->data; + field->fieldType = att->data_type; + field->fieldIndex = att->tag; + return GF_OK; + } + return GF_NOT_SUPPORTED; + } + return gf_node_get_attribute_by_tag(node, attribute_tag, create_if_not_found, set_default, field); +} + + +static void attributes_set_default_value(u32 node_tag, SVGAttribute *att) +{ + switch (att->tag) { + case TAG_SVG_ATT_width: + case TAG_SVG_ATT_height: + if (node_tag == TAG_SVG_svg) { + SVG_Length *len = att->data; + len->type = SVG_NUMBER_PERCENTAGE; + len->value = INT2FIX(100); + } + break; + case TAG_SVG_ATT_x2: + if (node_tag == TAG_SVG_linearGradient) { + ((SVG_Number *)att->data)->value = FIX_ONE; + } + break; + case TAG_SVG_ATT_cx: + case TAG_SVG_ATT_cy: + case TAG_SVG_ATT_fx: + case TAG_SVG_ATT_fy: + case TAG_SVG_ATT_r: + if (node_tag == TAG_SVG_radialGradient) { + ((SVG_Number *)att->data)->value = FIX_ONE/2; + } + break; + case TAG_SVG_ATT_dur: + if (node_tag == TAG_SVG_video || + node_tag == TAG_SVG_audio || + node_tag == TAG_SVG_animation) + { + ((SMIL_Duration *)att->data)->type = SMIL_DURATION_MEDIA; + } else { + /*is this correct?*/ + ((SMIL_Duration *)att->data)->type = SMIL_DURATION_INDEFINITE; + } + break; + case TAG_SVG_ATT_min: + ((SMIL_Duration *)att->data)->type = SMIL_DURATION_DEFINED; + break; + case TAG_SVG_ATT_repeatDur: + ((SMIL_Duration *)att->data)->type = SMIL_DURATION_INDEFINITE; + break; + case TAG_SVG_ATT_calcMode: + if (node_tag == TAG_SVG_animateMotion) + *((SMIL_CalcMode *)att->data) = SMIL_CALCMODE_PACED; + else + *((SMIL_CalcMode *)att->data) = SMIL_CALCMODE_LINEAR; + break; + case TAG_SVG_ATT_color: + ((SVG_Paint *)att->data)->type = SVG_PAINT_COLOR; + ((SVG_Paint *)att->data)->color.type = SVG_COLOR_INHERIT; + break; + case TAG_SVG_ATT_solid_color: + case TAG_SVG_ATT_stop_color: + ((SVG_Paint *)att->data)->type = SVG_PAINT_COLOR; + ((SVG_Paint *)att->data)->color.type = SVG_COLOR_RGBCOLOR; + break; + case TAG_SVG_ATT_opacity: + case TAG_SVG_ATT_solid_opacity: + case TAG_SVG_ATT_stop_opacity: + case TAG_SVG_ATT_audio_level: + case TAG_SVG_ATT_viewport_fill_opacity: + ((SVG_Number *)att->data)->value = FIX_ONE; + break; + case TAG_SVG_ATT_display: + *((SVG_Display *)att->data) = SVG_DISPLAY_INLINE; + break; + case TAG_SVG_ATT_fill: + case TAG_SVG_ATT_stroke: + ((SVG_Paint *)att->data)->type = SVG_PAINT_INHERIT; + break; + case TAG_SVG_ATT_stroke_dasharray: + ((SVG_StrokeDashArray *)att->data)->type = SVG_STROKEDASHARRAY_INHERIT; + break; + case TAG_SVG_ATT_fill_opacity: + case TAG_SVG_ATT_stroke_opacity: + case TAG_SVG_ATT_stroke_width: + case TAG_SVG_ATT_font_size: + case TAG_SVG_ATT_line_increment: + case TAG_SVG_ATT_stroke_dashoffset: + case TAG_SVG_ATT_stroke_miterlimit: + ((SVG_Number *)att->data)->type = SVG_NUMBER_INHERIT; + break; + case TAG_SVG_ATT_vector_effect: + *((SVG_VectorEffect *)att->data) = SVG_VECTOREFFECT_NONE; + break; + case TAG_SVG_ATT_fill_rule: + *((SVG_FillRule *)att->data) = SVG_FILLRULE_INHERIT; + break; + case TAG_SVG_ATT_font_weight: + *((SVG_FontWeight *)att->data) = SVG_FONTWEIGHT_INHERIT; + break; + case TAG_SVG_ATT_visibility: + *((SVG_Visibility *)att->data) = SVG_VISIBILITY_INHERIT; + break; + case TAG_SVG_ATT_smil_fill: + *((SMIL_Fill *)att->data) = SMIL_FILL_REMOVE; + break; + case TAG_XMLEV_ATT_defaultAction: + *((XMLEV_DefaultAction *)att->data) = XMLEVENT_DEFAULTACTION_PERFORM; + break; + case TAG_SVG_ATT_zoomAndPan: + *((SVG_ZoomAndPan *)att->data) = SVG_ZOOMANDPAN_MAGNIFY; + break; + case TAG_SVG_ATT_stroke_linecap: + *(SVG_StrokeLineCap*)att->data = SVG_STROKELINECAP_INHERIT; + break; + case TAG_SVG_ATT_stroke_linejoin: + *(SVG_StrokeLineJoin*)att->data = SVG_STROKELINEJOIN_INHERIT; + break; + + case TAG_SVG_ATT_transform: + gf_mx2d_init(((SVG_Transform*)att->data)->mat); + break; + + + /*all default=0 values (don't need init)*/ + case TAG_SVG_ATT_font_family: + case TAG_SVG_ATT_font_style: + case TAG_SVG_ATT_text_anchor: + case TAG_SVG_ATT_x: + case TAG_SVG_ATT_y: + break; + + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scene] Cannot create default value for SVG attribute %s\n", gf_svg_get_attribute_name(NULL, att->tag))); + } +} + +GF_EXPORT +GF_Err gf_node_get_attribute_by_tag(GF_Node *node, u32 attribute_tag, Bool create_if_not_found, Bool set_default, GF_FieldInfo *field) +{ + SVG_Element *elt = (SVG_Element *)node; + SVGAttribute *att = elt->attributes; + SVGAttribute *last_att = NULL; + + while (att) { + if ((u32) att->tag == attribute_tag) { + field->fieldIndex = att->tag; + field->fieldType = att->data_type; + field->far_ptr = att->data; + return GF_OK; + } + last_att = att; + att = att->next; + } + + if (create_if_not_found) { + /* field not found create one */ + att = gf_xml_create_attribute(node, attribute_tag); + if (att) { + if (!elt->attributes) elt->attributes = att; + else last_att->next = att; + field->far_ptr = att->data; + field->fieldType = att->data_type; + field->fieldIndex = att->tag; + /* attribute name should not be called, if needed use gf_svg_get_attribute_name(att->tag);*/ + field->name = NULL; + if (set_default) attributes_set_default_value(node->sgprivate->tag, att); + return GF_OK; + } + } + + return GF_NOT_SUPPORTED; +} + +GF_EXPORT +void gf_node_register_iri(GF_SceneGraph *sg, XMLRI *target) +{ +#ifndef GPAC_DISABLE_SVG + if (gf_list_find(sg->xlink_hrefs, target)<0) { + gf_list_add(sg->xlink_hrefs, target); + } +#endif +} + +void gf_node_unregister_iri(GF_SceneGraph *sg, XMLRI *target) +{ +#ifndef GPAC_DISABLE_SVG + gf_list_del_item(sg->xlink_hrefs, target); +#endif +} + +GF_Node *gf_xml_node_clone(GF_SceneGraph *inScene, GF_Node *orig, GF_Node *cloned_parent, char *inst_id, Bool deep) +{ + GF_DOMAttribute *att; + GF_Node *clone = gf_node_new(inScene, orig->sgprivate->tag); + if (!clone) return NULL; + + if (orig->sgprivate->tag == TAG_DOMText) { + GF_DOMText *n_src,*n_dst; + n_src = (GF_DOMText *)orig; + n_dst = (GF_DOMText *)clone; + n_dst->type = n_src->type; + n_dst->textContent = strdup(n_src->textContent); + } else { + if (orig->sgprivate->tag == TAG_DOMFullNode) { + GF_DOMFullNode *n_src,*n_dst; + n_src = (GF_DOMFullNode *)orig; + n_dst = (GF_DOMFullNode *)clone; + n_dst->ns = n_src->ns; + n_dst->name = strdup(n_dst->name); + } + + att = ((GF_DOMNode *)orig)->attributes; + while (att) { + GF_FieldInfo dst, src; + /*create by name*/ + if (att->tag==TAG_DOM_ATT_any) { + gf_node_get_attribute_by_name(clone, ((GF_DOMFullAttribute*)att)->name, 0, 1, 0, &dst); + } else { + gf_node_get_attribute_by_tag(clone, att->tag, 1, 0, &dst); + } + src.far_ptr = att->data; + src.fieldType = att->data_type; + src.fieldIndex = att->tag; + gf_svg_attributes_copy(&dst, &src, 0); + if (att->tag==TAG_XLINK_ATT_href) { + XMLRI *iri = (XMLRI *)att->data; + if (iri->target == gf_node_get_parent(orig, 0)) { + ((XMLRI *)dst.far_ptr)->target = cloned_parent; + } else { + ((XMLRI *)dst.far_ptr)->target = NULL; + } + } + att = att->next; + } + } + if (cloned_parent) { + gf_node_list_add_child( & ((GF_ParentNode*)cloned_parent)->children, clone); + gf_node_register(clone, cloned_parent); + /*TO CLARIFY: can we init the node right now or should we wait for insertion in the scene tree ?*/ + gf_node_init(clone); + } + if (deep) { + GF_ChildNodeItem *child = ((GF_ParentNode *)orig)->children; + while (child) { + gf_node_clone(inScene, child->node, clone, inst_id, 1); + child = child->next; + } + } + return clone; +} + +/*TODO FIXME, this is ugly, add proper cache system*/ +#include + + +static u32 check_existing_file(char *base_file, char *ext, char *data, u32 data_size, u32 idx) +{ + char szFile[GF_MAX_PATH]; + u32 fsize; + FILE *f; + + sprintf(szFile, "%s%04X%s", base_file, idx, ext); + + f = fopen(szFile, "rb"); + if (!f) return 0; + + fseek(f, 0, SEEK_END); + fsize = ftell(f); + if (fsize==data_size) { + u32 offset=0; + char cache[1024]; + fseek(f, 0, SEEK_SET); + while (fsize) { + u32 read = fread(cache, 1, 1024, f); + fsize -= read; + if (memcmp(cache, data+offset, sizeof(char)*read)) break; + offset+=read; + } + fclose(f); + /*same file*/ + if (!fsize) return 2; + } + fclose(f); + return 1; +} + + +GF_EXPORT +GF_Err gf_node_store_embedded_data(XMLRI *iri, const char *cache_dir, const char *base_filename) +{ + char szFile[GF_MAX_PATH], buf[20], *sep, *data, *ext; + u32 data_size, idx; + Bool existing; + FILE *f; + + if (!cache_dir || !base_filename || !iri || !iri->string || strncmp(iri->string, "data:", 5)) return GF_OK; + + /*handle "data:" scheme when cache is specified*/ + strcpy(szFile, cache_dir); + data_size = strlen(szFile); + if (szFile[data_size-1] != GF_PATH_SEPARATOR) { + szFile[data_size] = GF_PATH_SEPARATOR; + szFile[data_size+1] = 0; + } + if (base_filename) { + sep = strrchr(base_filename, GF_PATH_SEPARATOR); +#ifdef WIN32 + if (!sep) sep = strrchr(base_filename, '/'); +#endif + if (!sep) sep = (char *) base_filename; + else sep += 1; + strcat(szFile, sep); + } + sep = strrchr(szFile, '.'); + if (sep) sep[0] = 0; + strcat(szFile, "_img_"); + + /*get mime type*/ + sep = (char *)iri->string + 5; + if (!strncmp(sep, "image/jpg", 9) || !strncmp(sep, "image/jpeg", 10)) ext = ".jpg"; + else if (!strncmp(sep, "image/png", 9)) ext = ".png"; + else return GF_OK; + + + data = NULL; + sep = strchr(iri->string, ';'); + if (!strncmp(sep, ";base64,", 8)) { + sep += 8; + data_size = 2*strlen(sep); + data = (char*)malloc(sizeof(char)*data_size); + if (!data) return GF_OUT_OF_MEM; + data_size = gf_base64_decode(sep, strlen(sep), data, data_size); + } + else if (!strncmp(sep, ";base16,", 8)) { + data_size = 2*strlen(sep); + data = (char*)malloc(sizeof(char)*data_size); + if (!data) return GF_OUT_OF_MEM; + sep += 8; + data_size = gf_base16_decode(sep, strlen(sep), data, data_size); + } + if (!data_size) return GF_OK; + + iri->type = XMLRI_STRING; + + existing = 0; + idx = 0; + while (1) { + u32 res = check_existing_file(szFile, ext, data, data_size, idx); + if (!res) break; + if (res==2) { + existing = 1; + break; + } + idx++; + } + sprintf(buf, "%04X", idx); + strcat(szFile, buf); + strcat(szFile, ext); + + if (!existing) { + f = fopen(szFile, "wb"); + if (!f) { + free(data); + free(iri->string); + iri->string = NULL; + return GF_IO_ERR; + } + fwrite(data, data_size, 1, f); + fclose(f); + } + free(data); + free(iri->string); + iri->string = strdup(szFile); + return GF_OK; +} + + +#endif /*GPAC_DISABLE_SVG*/ + diff --git a/src/terminal/channel.c b/src/terminal/channel.c new file mode 100644 index 0000000..6606d29 --- /dev/null +++ b/src/terminal/channel.c @@ -0,0 +1,1298 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include +#include + +#include "media_memory.h" +#include "media_control.h" + +static void ch_buffer_off(GF_Channel *ch) +{ + /*just in case*/ + if (ch->BufferOn) { + ch->BufferOn = 0; + gf_clock_buffer_off(ch->clock); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: buffering off at %d (nb buffering on clock: %d)\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), ch->clock->Buffering)); + } +} + + +static void ch_buffer_on(GF_Channel *ch) +{ + /*don't buffer on an already running clock*/ + if (ch->clock->no_time_ctrl && ch->clock->clock_init && (ch->esd->ESID!=ch->clock->clockID) ) return; + if (ch->dispatch_after_db) return; + + /*just in case*/ + if (!ch->BufferOn) { + ch->BufferOn = 1; + gf_clock_buffer_on(ch->clock); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: buffering on at %d (nb buffering on clock: %d)\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), ch->clock->Buffering)); + } +} + +/*reset channel*/ +static void Channel_Reset(GF_Channel *ch, Bool for_start) +{ + gf_es_lock(ch, 1); + + ch->IsClockInit = 0; + ch->au_sn = 0; + ch->pck_sn = 0; + ch->stream_state = 1; + ch->IsRap = 0; + ch->IsEndOfStream = 0; + ch->skip_carousel_au = 0; + + if (for_start) { + gf_es_lock(ch, 0); + return; + } + + ch->ts_offset = 0; + ch->seed_ts = 0; + + ch_buffer_off(ch); + + if (ch->buffer) free(ch->buffer); + ch->buffer = NULL; + ch->len = ch->allocSize = 0; + + gf_db_unit_del(ch->AU_buffer_first); + ch->AU_buffer_first = ch->AU_buffer_last = NULL; + ch->AU_Count = 0; + ch->BufferTime = 0; + ch->NextIsAUStart = 1; + + ch->first_au_fetched = 0; + if (ch->AU_buffer_pull) { + ch->AU_buffer_pull->data = NULL; + gf_db_unit_del(ch->AU_buffer_pull); + ch->AU_buffer_pull = NULL; + } + gf_es_lock(ch, 0); +} + +GF_Channel *gf_es_new(GF_ESD *esd) +{ + u32 nbBits; + GF_Channel *tmp; + GF_SAFEALLOC(tmp, GF_Channel); + if (!tmp) return NULL; + + tmp->mx = gf_mx_new("Channel"); + tmp->esd = esd; + tmp->chan_id = (u32) tmp; + tmp->es_state = GF_ESM_ES_SETUP; + + nbBits = sizeof(u32) * 8 - esd->slConfig->AUSeqNumLength; + tmp->max_au_sn = 0xFFFFFFFF >> nbBits; + nbBits = sizeof(u32) * 8 - esd->slConfig->packetSeqNumLength; + tmp->max_pck_sn = 0xFFFFFFFF >> nbBits; + + tmp->skip_sl = (esd->slConfig->predefined == SLPredef_SkipSL) ? 1 : 0; + + /*take care of dummy streams*/ + if (!esd->slConfig->timestampResolution) esd->slConfig->timestampResolution = esd->slConfig->timeScale ? esd->slConfig->timeScale : 1000; + if (!esd->slConfig->OCRResolution) esd->slConfig->OCRResolution = esd->slConfig->timestampResolution; + + tmp->ts_res = esd->slConfig->timestampResolution; + + tmp->ocr_scale = 0; + if (esd->slConfig->OCRResolution) { + tmp->ocr_scale = 1000; + tmp->ocr_scale /= esd->slConfig->OCRResolution; + } + + + Channel_Reset(tmp, 0); + return tmp; +} + + +/*reconfig SL settings for this channel - this is needed by some net services*/ +void gf_es_reconfig_sl(GF_Channel *ch, GF_SLConfig *slc) +{ + u32 nbBits; + + memcpy(ch->esd->slConfig, slc, sizeof(GF_SLConfig)); + + nbBits = sizeof(u32) * 8 - ch->esd->slConfig->AUSeqNumLength; + ch->max_au_sn = 0xFFFFFFFF >> nbBits; + nbBits = sizeof(u32) * 8 - ch->esd->slConfig->packetSeqNumLength; + ch->max_pck_sn = 0xFFFFFFFF >> nbBits; + + ch->skip_sl = (slc->predefined == SLPredef_SkipSL) ? 1 : 0; + + /*take care of dummy streams*/ + if (!ch->esd->slConfig->timestampResolution) ch->esd->slConfig->timestampResolution = 1000; + if (!ch->esd->slConfig->OCRResolution) ch->esd->slConfig->OCRResolution = ch->esd->slConfig->timestampResolution; + + ch->ts_res = ch->esd->slConfig->timestampResolution; + ch->ocr_scale = 0; + if (ch->esd->slConfig->OCRResolution) { + ch->ocr_scale = 1000; + ch->ocr_scale /= ch->esd->slConfig->OCRResolution; + } +} + + +/*destroy channel*/ +void gf_es_del(GF_Channel *ch) +{ + Channel_Reset(ch, 0); + if (ch->AU_buffer_pull) { + ch->AU_buffer_pull->data = NULL; + gf_db_unit_del(ch->AU_buffer_pull); + } + if (ch->ipmp_tool) + gf_modules_close_interface((GF_BaseInterface *) ch->ipmp_tool); + + if (ch->mx) gf_mx_del(ch->mx); + free(ch); +} + +Bool gf_es_owns_clock(GF_Channel *ch) +{ + /*if the clock is not in the same namespace (used with dynamic scenes), it's not ours*/ + if (gf_list_find(ch->odm->net_service->Clocks, ch->clock)<0) return 0; + return (ch->esd->ESID==ch->clock->clockID) ? 1 : 0; +} + +GF_Err gf_es_start(GF_Channel *ch) +{ + if (!ch) return GF_BAD_PARAM; + + switch (ch->es_state) { + case GF_ESM_ES_UNAVAILABLE: + case GF_ESM_ES_SETUP: + return GF_BAD_PARAM; + /*if the channel is already running, don't reset its settings. This only happens in the case of broadcast + objects started several times by the scene but not stoped at the ODManager level (cf gf_odm_stop)*/ + case GF_ESM_ES_RUNNING: + return GF_OK; + default: + break; + } + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] Starting ES %d\n", ch->esd->ESID)); + + /*reset clock if we own it*/ + if (gf_es_owns_clock(ch) && !ch->clock->no_time_ctrl) + gf_clock_reset(ch->clock); + + /*reset channel*/ + Channel_Reset(ch, 1); + /*create pull buffer if needed*/ + if (ch->is_pulling && !ch->AU_buffer_pull) ch->AU_buffer_pull = gf_db_unit_new(); + + /*and start buffering - pull channels always turn off buffering immediately, otherwise + buffering size is setup by the network service - except InputSensor*/ + if ((ch->esd->decoderConfig->streamType != GF_STREAM_INTERACT) || ch->esd->URLString) { + ch_buffer_on(ch); + } + ch->last_au_time = gf_term_get_time(ch->odm->term); + ch->es_state = GF_ESM_ES_RUNNING; + return GF_OK; +} + +GF_Err gf_es_stop(GF_Channel *ch) +{ + if (!ch) return GF_BAD_PARAM; + + switch (ch->es_state) { + case GF_ESM_ES_UNAVAILABLE: + case GF_ESM_ES_SETUP: + return GF_BAD_PARAM; + default: + break; + } + + ch_buffer_off(ch); + + ch->es_state = GF_ESM_ES_CONNECTED; + Channel_Reset(ch, 0); + return GF_OK; +} + + +void Channel_WaitRAP(GF_Channel *ch) +{ + ch->pck_sn = 0; + + /*if using RAP signal and codec not resilient, wait for rap. If RAP isn't signaled DON'T wait for it :)*/ + if (!ch->codec_resilient) + ch->stream_state = 2; + if (ch->buffer) free(ch->buffer); + ch->buffer = NULL; + ch->AULength = 0; + ch->au_sn = 0; +} + +void gf_es_map_time(GF_Channel *ch, Bool reset) +{ + gf_mx_p(ch->mx); + if (ch->buffer) free(ch->buffer); + ch->buffer = NULL; + ch->len = ch->allocSize = 0; + + if (reset) { + gf_db_unit_del(ch->AU_buffer_first); + ch->AU_buffer_first = ch->AU_buffer_last = NULL; + ch->AU_Count = 0; + } else { + GF_DBUnit *au = ch->AU_buffer_first; + while (au) { + au->DTS = au->CTS = ch->ts_offset; + au = au->next; + } + } + ch->BufferTime = 0; + gf_mx_v(ch->mx); +} + +static Bool Channel_NeedsBuffering(GF_Channel *ch, u32 ForRebuffering) +{ + if (!ch->MaxBuffer || ch->IsEndOfStream) return 0; + + /*for rebuffering, check we're not below min buffer*/ + if (ForRebuffering) { + if (ch->MinBuffer && (ch->BufferTime <= (s32) ch->MinBuffer)) { + return 1; + } + return 0; + } + /*we're in a broadcast scenario and one of the stream running on this clock has completed its buffering: + abort all buffering*/ + if (ch->clock->no_time_ctrl == 2) return 0; + + /*nothing received, buffer needed*/ + if (!ch->first_au_fetched && !ch->AU_buffer_first) { + u32 now = gf_term_get_time(ch->odm->term); + /*data timeout (no data sent)*/ + if (now > ch->last_au_time + ch->odm->term->net_data_timeout) { + gf_term_message(ch->odm->term, ch->service->url, "Data timeout - aborting buffering", GF_OK); + ch->MinBuffer = ch->MaxBuffer = 0; + ch->au_duration = 0; + gf_inline_buffering_info(ch->odm->parentscene ? ch->odm->parentscene : ch->odm->subscene); + return 0; + } else { + now = ch->odm->term->net_data_timeout + ch->last_au_time - now; + now /= 1000; + if (now != ch->au_duration) { + char szMsg[500]; + ch->au_duration = now; + sprintf(szMsg, "Buffering - Waiting for data (%d s)", now); + gf_term_message(ch->odm->term, ch->service->url, szMsg, GF_OK); + } + return 1; + } + } + + /*buffer not filled yet*/ + if (ch->BufferTime < (s32) ch->MaxBuffer) { + /*check last AU time*/ + u32 now = gf_term_get_time(ch->odm->term); + /*if more than half sec since last AU don't buffer and prevent rebuffering on short streams + this will also work for channels ignoring timing*/ + if (now>ch->last_au_time + MAX(ch->BufferTime, 500) ) { + /*this can be safely seen as a stream with very few updates (likely only one)*/ + if (!ch->AU_buffer_first && ch->first_au_fetched) ch->MinBuffer = 0; + return 0; + } + return 1; + } + return 0; +} + +static void Channel_UpdateBuffering(GF_Channel *ch, Bool update_info) +{ + if (update_info && ch->MaxBuffer) gf_inline_buffering_info(ch->odm->parentscene ? ch->odm->parentscene : ch->odm->subscene); + + gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_DATA_PROGRESS); + + if (!Channel_NeedsBuffering(ch, 0)) { + ch_buffer_off(ch); + if (ch->MaxBuffer && update_info) gf_inline_buffering_info(ch->odm->parentscene ? ch->odm->parentscene : ch->odm->subscene); + if (ch->clock->no_time_ctrl) ch->clock->no_time_ctrl = 2; + + gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_PLAYABLE); + } +} + +static void Channel_UpdateBufferTime(GF_Channel *ch) +{ + if (!ch->AU_buffer_first) { + ch->BufferTime = 0; + } + else if (ch->skip_sl) { + GF_DBUnit *au; + /*compute buffer size from avg bitrate*/ + u32 avg_rate = ch->esd->decoderConfig->avgBitrate; + if (!avg_rate && ch->odm->codec) avg_rate = ch->odm->codec->avg_bit_rate; + if (avg_rate) { + u32 bsize = 0; + au = ch->AU_buffer_first; + while (1) { + bsize += au->dataLength*8; + if (!au->next) break; + au = au->next; + } + ch->BufferTime = 1000*bsize/avg_rate; + } else { + /*we're in the dark, so don't buffer too much (assume 50ms per unit) so that we start decoding asap*/ + ch->BufferTime = 50*ch->AU_Count; + } + } else { + s32 bt = ch->AU_buffer_last->DTS - gf_clock_time(ch->clock); + ch->BufferTime = 0; + if (bt>0) ch->BufferTime = (u32) bt; + } + ch->BufferTime += ch->au_duration; +} + +/*dispatch the AU in the DB*/ +static void Channel_DispatchAU(GF_Channel *ch, u32 duration) +{ + u32 time; + GF_DBUnit *au; + + if (!ch->buffer || !ch->len) { + if (ch->buffer) { + free(ch->buffer); + ch->buffer = NULL; + } + return; + } + + au = gf_db_unit_new(); + if (!au) { + free(ch->buffer); + ch->buffer = NULL; + ch->len = 0; + return; + } + + au->CTS = ch->CTS; + au->DTS = ch->DTS; + au->RAP = ch->IsRap; + au->data = ch->buffer; + au->dataLength = ch->len; + au->PaddingBits = ch->padingBits; + + ch->IsRap = 0; + ch->padingBits = 0; + au->next = NULL; + ch->buffer = NULL; + + if (ch->len + ch->media_padding_bytes != ch->allocSize) { + au->data = (char*)realloc(au->data, sizeof(char) * (au->dataLength + ch->media_padding_bytes)); + } + if (ch->media_padding_bytes) memset(au->data + au->dataLength, 0, sizeof(char)*ch->media_padding_bytes); + + ch->len = ch->allocSize = 0; + + gf_es_lock(ch, 1); + + if (ch->service && ch->service->cache) { + GF_SLHeader slh; + memset(&slh, 0, sizeof(GF_SLHeader)); + slh.accessUnitEndFlag = slh.accessUnitStartFlag = 1; + slh.compositionTimeStampFlag = slh.decodingTimeStampFlag = 1; + slh.decodingTimeStamp = ch->net_dts; + slh.compositionTimeStamp = ch->net_cts; + slh.randomAccessPointFlag = au->RAP; + ch->service->cache->Write(ch->service->cache, ch, au->data, au->dataLength, &slh); + } + + if (!ch->AU_buffer_first) { + ch->AU_buffer_first = au; + ch->AU_buffer_last = au; + ch->AU_Count = 1; + } else { + if (ch->AU_buffer_last->DTS<=au->DTS) { + ch->AU_buffer_last->next = au; + ch->AU_buffer_last = ch->AU_buffer_last->next; + } + /*enable deinterleaving only for audio channels (some video transport may not be able to compute DTS, cf MPEG1-2/RTP) + HOWEVER, we must recompute a monotone increasing DTS in case the decoder does perform frame reordering + in which case the DTS is used for presentation time!!*/ + else if (ch->odm->codec && (ch->odm->codec->type!=GF_STREAM_AUDIO)) { + GF_DBUnit *au_prev, *ins_au; + u32 DTS; + + /*append AU*/ + ch->AU_buffer_last->next = au; + ch->AU_buffer_last = ch->AU_buffer_last->next; + + DTS = au->DTS; + au_prev = ch->AU_buffer_first; + /*locate first AU in buffer with DTS greater than new unit CTS*/ + while (au_prev->next && (au_prev->DTS < DTS) ) au_prev = au_prev->next; + /*remember insertion point*/ + ins_au = au_prev; + /*shift all following frames DTS*/ + while (au_prev->next) { + au_prev->next->DTS = au_prev->DTS; + au_prev = au_prev->next; + } + /*and apply*/ + ins_au->DTS = DTS; + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] Audio deinterleaving OD %d ch %d\n", ch->esd->ESID, ch->odm->OD->objectDescriptorID)); + /*de-interleaving of AUs*/ + if (ch->AU_buffer_first->DTS > au->DTS) { + au->next = ch->AU_buffer_first; + ch->AU_buffer_first = au; + } else { + GF_DBUnit *au_prev = ch->AU_buffer_first; + while (au_prev->next && au_prev->next->DTSDTS) { + au_prev = au_prev->next; + } + assert(au_prev); + if (au_prev->next->DTS==au->DTS) { + free(au->data); + free(au); + } else { + au->next = au_prev->next; + au_prev->next = au; + } + } + } + ch->AU_Count += 1; + } + + Channel_UpdateBufferTime(ch); + ch->au_duration = 0; + if (duration) ch->au_duration = (u32) ((u64)1000 * duration / ch->ts_res); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d - Dispatch AU CTS %d time %d Buffer %d Nb AUs %d\n", ch->esd->ESID, au->CTS, gf_clock_time(ch->clock), ch->BufferTime, ch->AU_Count)); + + /*little optimisation: if direct dispatching is possible, try to decode the AU + we must lock the media scheduler to avoid deadlocks with other codecs accessing the scene or + media resources*/ + if (ch->dispatch_after_db) { + u32 retry = 100; + u32 current_frame; + GF_Terminal *term = ch->odm->term; + ch_buffer_off(ch); + if (gf_mx_try_lock(term->mm_mx)) { + switch (ch->esd->decoderConfig->streamType) { + case GF_STREAM_OD: + gf_codec_process(ch->odm->subscene->od_codec, 100); + break; + case GF_STREAM_SCENE: + if (ch->odm->codec) + gf_codec_process(ch->odm->codec, 100); + else + gf_codec_process(ch->odm->subscene->scene_codec, 100); + break; + } + gf_mx_v(term->mm_mx); + } + + current_frame = term->compositor->frame_number; + /*wait for initial setup to complete before giving back the hand to the caller service*/ + while (retry) { + /*Scene bootstrap: if the scene is attached, wait for first frame to complete so that initial PLAY on + objects can be evaluated*/ + if (term->compositor->scene && (term->compositor->frame_number==current_frame) ) { + retry--; + gf_sleep(1); + continue; + } + /*Media bootstrap: wait for all pending requests on media objects are processed*/ + if (gf_list_count(term->media_queue)) { + retry--; + gf_sleep(1); + continue; + } + break; + } + } + + time = gf_term_get_time(ch->odm->term); + if (ch->BufferOn) { + ch->last_au_time = time; + Channel_UpdateBuffering(ch, 1); + } else { + /*trigger the data progress every 500 ms*/ + if (ch->last_au_time + 500 > time) { + gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_DATA_PROGRESS); + ch->last_au_time = time; + } + } + + gf_es_lock(ch, 0); +} + +void Channel_ReceiveSkipSL(GF_ClientService *serv, GF_Channel *ch, char *StreamBuf, u32 StreamLength) +{ + GF_DBUnit *au; + if (!StreamLength) return; + + gf_es_lock(ch, 1); + au = gf_db_unit_new(); + au->RAP = 1; + au->DTS = gf_clock_time(ch->clock); + au->data = (char*)malloc(sizeof(char) * (ch->media_padding_bytes + StreamLength)); + memcpy(au->data, StreamBuf, sizeof(char) * StreamLength); + if (ch->media_padding_bytes) memset(au->data + StreamLength, 0, sizeof(char)*ch->media_padding_bytes); + au->dataLength = StreamLength; + au->next = NULL; + + /*if channel owns the clock, start it*/ + if (ch->clock && !ch->IsClockInit) { + if (gf_es_owns_clock(ch)) { + gf_clock_set_time(ch->clock, 0); + ch->IsClockInit = 1; + ch->seed_ts = 0; + } + if (ch->clock->clock_init && !ch->IsClockInit) { + ch->IsClockInit = 1; + ch->seed_ts = gf_clock_time(ch->clock); + } + } + + if (!ch->AU_buffer_first) { + ch->AU_buffer_first = au; + ch->AU_buffer_last = au; + ch->AU_Count = 1; + } else { + ch->AU_buffer_last->next = au; + ch->AU_buffer_last = ch->AU_buffer_last->next; + ch->AU_Count += 1; + } + + Channel_UpdateBufferTime(ch); + + if (ch->BufferOn) { + ch->last_au_time = gf_term_get_time(ch->odm->term); + Channel_UpdateBuffering(ch, 1); + } + gf_es_lock(ch, 0); +} + + +/*handles reception of an SL-PDU, logical or physical*/ +void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *StreamBuf, u32 StreamLength, GF_SLHeader *header, GF_Err reception_status) +{ + GF_SLHeader hdr; + u32 nbAU, OldLength, size, AUSeqNum, SLHdrLen; + Bool EndAU, NewAU; + char *payload; + + if (ch->bypass_sl_and_db) { + GF_SceneDecoder *sdec; + ch->IsClockInit = 1; + if (ch->odm->subscene) { + sdec = (GF_SceneDecoder *)ch->odm->subscene->scene_codec->decio; + } else { + sdec = (GF_SceneDecoder *)ch->odm->codec->decio; + } + gf_mx_p(ch->mx); + sdec->ProcessData(sdec, StreamBuf, StreamLength, ch->esd->ESID, 0, 0); + gf_mx_v(ch->mx); + return; + } + + if (ch->es_state != GF_ESM_ES_RUNNING) return; + + /*physical SL-PDU - depacketize*/ + if (!header) { + if (!StreamLength) return; + gf_sl_depacketize(ch->esd->slConfig, &hdr, StreamBuf, StreamLength, &SLHdrLen); + StreamLength -= SLHdrLen; + } else { + hdr = *header; + SLHdrLen = 0; + } + payload = StreamBuf + SLHdrLen; + + if (ch->skip_sl) { + Channel_ReceiveSkipSL(serv, ch, payload, StreamLength); + return; + } + + /*check state*/ + if (!ch->codec_resilient && (reception_status==GF_CORRUPTED_DATA)) { + Channel_WaitRAP(ch); + return; + } + + if (!ch->esd->slConfig->useAccessUnitStartFlag) { + /*no AU signaling - each packet is an AU*/ + if (!ch->esd->slConfig->useAccessUnitEndFlag) + hdr.accessUnitEndFlag = hdr.accessUnitStartFlag = 1; + /*otherwise AU are signaled by end of previous packet*/ + else + hdr.accessUnitStartFlag = ch->NextIsAUStart; + } + + /*get RAP*/ + if (ch->esd->slConfig->hasRandomAccessUnitsOnlyFlag) { + hdr.randomAccessPointFlag = 1; + } else if (!ch->esd->slConfig->useRandomAccessPointFlag || ch->codec_resilient) { + ch->stream_state = 0; + } + + if (ch->esd->slConfig->packetSeqNumLength) { + if (ch->pck_sn && hdr.packetSequenceNumber) { + /*repeated -> drop*/ + if (ch->pck_sn == hdr.packetSequenceNumber) { + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: repeated packet, droping\n", ch->esd->ESID)); + return; + } + /*if codec has no resiliency check packet drops*/ + if (!ch->codec_resilient && !hdr.accessUnitStartFlag) { + if (ch->pck_sn == (u32) (1<esd->slConfig->packetSeqNumLength) ) { + if (hdr.packetSequenceNumber) { + GF_LOG(GF_LOG_WARNING, GF_LOG_SYNC, ("[SyncLayer] ES%d: packet loss, droping & wait RAP\n", ch->esd->ESID)); + Channel_WaitRAP(ch); + return; + } + } else if (ch->pck_sn + 1 != hdr.packetSequenceNumber) { + GF_LOG(GF_LOG_WARNING, GF_LOG_SYNC, ("[SyncLayer] ES%d: packet loss, droping & wait RAP\n", ch->esd->ESID)); + Channel_WaitRAP(ch); + return; + } + } + } + ch->pck_sn = hdr.packetSequenceNumber; + } + + /*if empty, skip the packet*/ + if (hdr.paddingFlag && !hdr.paddingBits) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: Empty packet - skipping\n", ch->esd->ESID)); + return; + } + /*IDLE stream shall be processed*/ + + + NewAU = 0; + if (hdr.accessUnitStartFlag) { + NewAU = 1; + ch->NextIsAUStart = 0; + ch->skip_carousel_au = 0; + + /*if we have a pending AU, add it*/ + if (ch->buffer) { + if (ch->esd->slConfig->useAccessUnitEndFlag) { + GF_LOG(GF_LOG_WARNING, GF_LOG_SYNC, ("[SyncLayer] ES%d: missed end of AU (DTS %d)\n", ch->esd->ESID, ch->DTS)); + } + if (ch->codec_resilient) { + Channel_DispatchAU(ch, 0); + } else { + free(ch->buffer); + ch->buffer = NULL; + ch->AULength = 0; + ch->len = ch->allocSize = 0; + } + } + AUSeqNum = hdr.AU_sequenceNumber; + /*Get CTS */ + if (hdr.compositionTimeStampFlag) { + ch->net_dts = ch->net_cts = hdr.compositionTimeStamp; + /*get DTS */ + if (hdr.decodingTimeStampFlag) ch->net_dts = hdr.decodingTimeStamp; + + /*until clock is not init check seed ts*/ + if (!ch->IsClockInit && (ch->net_dts < ch->seed_ts)) + ch->seed_ts = ch->net_dts; + + ch->net_dts -= ch->seed_ts; + ch->net_cts -= ch->seed_ts; + /*TS Wraping not tested*/ + ch->CTS = (u32) (ch->ts_offset + (s64) (ch->net_cts) * 1000 / ch->ts_res); + ch->DTS = (u32) (ch->ts_offset + (s64) (ch->net_dts) * 1000 / ch->ts_res); + } else { + /*use CU duration*/ + if (!ch->IsClockInit) ch->DTS = ch->CTS = ch->ts_offset; + + if (!ch->esd->slConfig->AUSeqNumLength) { + if (!ch->au_sn) { + ch->CTS = ch->ts_offset; + ch->au_sn = 1; + } else { + ch->CTS += ch->esd->slConfig->CUDuration; + } + } else { + //use the sequence number to get the TS + if (AUSeqNum < ch->au_sn) { + nbAU = ( (1<esd->slConfig->AUSeqNumLength) - ch->au_sn) + AUSeqNum; + } else { + nbAU = AUSeqNum - ch->au_sn; + } + ch->CTS += nbAU * ch->esd->slConfig->CUDuration; + } + } + + if (!ch->IsClockInit && (hdr.compositionTimeStampFlag || !ch->esd->slConfig->useTimestampsFlag) ) { + /*the first data received inits the clock - this is needed to handle clock dependencies on non-initialized + streams (eg, bifs/od depends on audio/video clock)*/ + if (!ch->clock->clock_init) { + gf_clock_set_time(ch->clock, ch->CTS); + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: Forcing clock initialization at STB %d - AU DTS %d\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), ch->DTS)); + } + /*channel is the OCR, force a re-init of the clock since we cannot assume the AU used to init the clock was + not sent ahead of time*/ + else if (gf_es_owns_clock(ch)) { + ch->clock->clock_init = 0; + gf_clock_set_time(ch->clock, ch->DTS); + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: initializing clock at STB %d - AU DTS %d - %d buffering\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), ch->DTS, ch->clock->Buffering)); + } + if (ch->clock->clock_init) ch->IsClockInit = 1; + } + /*if the AU Length is carried in SL, get its size*/ + if (ch->esd->slConfig->AULength > 0) { + ch->AULength = hdr.accessUnitLength; + } else { + ch->AULength = 0; + } + /*carousel for repeated AUs.*/ + if (ch->esd->slConfig->AUSeqNumLength) { + if (hdr.randomAccessPointFlag) { + /*initial tune-in*/ + if (ch->stream_state==1) { + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: RAP Carousel found - tuning in\n", ch->esd->ESID)); + ch->au_sn = AUSeqNum; + ch->stream_state = 0; + } + /*carousel RAP*/ + else if (AUSeqNum == ch->au_sn) { + /*error recovery*/ + if (ch->stream_state==2) { + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: RAP Carousel found - recovering\n", ch->esd->ESID)); + ch->stream_state = 0; + } + else { + ch->skip_carousel_au = 1; + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: RAP Carousel found - skipping\n", ch->esd->ESID)); + return; + } + } + /*regular RAP*/ + else { + ch->au_sn = AUSeqNum; + ch->stream_state = 0; + } + } + /*regular AU but waiting for RAP*/ + else if (ch->stream_state) { + ch->skip_carousel_au = 1; + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: Waiting for RAP Carousel - skipping\n", ch->esd->ESID)); + return; + } else { + ch->au_sn = AUSeqNum; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: NON-RAP AU received\n", ch->esd->ESID)); + } + } + /*no carousel signaling, tune-in at first RAP*/ + else if (hdr.randomAccessPointFlag) { + ch->stream_state = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: RAP AU received\n", ch->esd->ESID)); + } + /*waiting for RAP, return*/ + else if (ch->stream_state) { + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: Waiting for RAP - skipping AU (DTS %d)\n", ch->esd->ESID, ch->DTS)); + return; + } + } + + /*update the RAP marker on a packet base (to cope with AVC/H264 NALU->AU reconstruction)*/ + if (hdr.randomAccessPointFlag) ch->IsRap = 1; + /* we need to skip all the packets of the current AU in the carousel scenario */ + if (ch->skip_carousel_au == 1) return; + + /*we ignore OCRs for the moment*/ +#if 0 + /*Apply OCR*/ + if (!ch->BufferOn && ch->IsClockInit && hdr.OCRflag) { + u32 OCR_TS = (u32) (s64) (((s64) hdr.objectClockReference) * ch->ocr_scale); + u32 ck = gf_clock_time(ch->clock); + OCR_TS += ch->BufferTime; + fprintf(stdout, "OCR %d OTB %d Diff %d Buffer %d (%d AUs) \n", OCR_TS, ck, OCR_TS - ck, ch->BufferTime, ch->AU_Count); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: At OTB %d adjusting OCR to %d\n", ch->esd->ESID, gf_clock_real_time(ch->clock), OCR_TS)); + gf_clock_set_time(ch->clock, OCR_TS); + } +#endif + + /*get AU end state*/ + OldLength = ch->buffer ? ch->len : 0; + EndAU = hdr.accessUnitEndFlag; + if (ch->AULength == OldLength + StreamLength) EndAU = 1; + if (EndAU) ch->NextIsAUStart = 1; + + if (!StreamLength && EndAU && ch->buffer) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: Empty packet, flushing buffer\n", ch->esd->ESID)); + Channel_DispatchAU(ch, 0); + return; + } + if (!StreamLength) return; + + /*missed begining, unusable*/ + if (!ch->buffer && !NewAU) { + if (ch->esd->slConfig->useAccessUnitStartFlag) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SYNC, ("[SyncLayer] ES%d: missed begin of AU\n", ch->esd->ESID)); + } + if (ch->codec_resilient) NewAU = 1; + else return; + } + + /*Write the Packet payload to the buffer*/ + if (NewAU) { + /*we should NEVER have a bitstream at this stage*/ + assert(!ch->buffer); + /*ignore length fields*/ + size = StreamLength + ch->media_padding_bytes; + ch->buffer = (char*)malloc(sizeof(char) * size); + if (!ch->buffer) { + assert(0); + return; + } + + ch->allocSize = size; + memset(ch->buffer, 0, sizeof(char) * size); + ch->len = 0; + } + if (!ch->esd->slConfig->usePaddingFlag) hdr.paddingFlag = 0; + + if (ch->ipmp_tool) { + GF_Err e; + GF_IPMPEvent evt; + memset(&evt, 0, sizeof(evt)); + evt.event_type=GF_IPMP_TOOL_PROCESS_DATA; + evt.channel = ch; + evt.data = payload; + evt.data_size = StreamLength; + evt.is_encrypted = hdr.isma_encrypted; + evt.isma_BSO = hdr.isma_BSO; + e = ch->ipmp_tool->process(ch->ipmp_tool, &evt); + + /*we discard undecrypted AU*/ + if (e) { + if (e==GF_EOS) { + gf_es_on_eos(ch); + /*restart*/ + if (evt.restart_requested) { + if (ch->odm->parentscene->is_dynamic_scene) { + gf_inline_restart_dynamic(ch->odm->parentscene, 0); + } else { + MC_Restart(ch->odm); + } + } + } + return; + } + } + + if (hdr.paddingFlag && !EndAU) { + /*to do - this shouldn't happen anyway */ + + } else { + /*check if enough space*/ + size = ch->allocSize; + if (size && (StreamLength + ch->len <= size)) { + memcpy(ch->buffer+ch->len, payload, StreamLength); + ch->len += StreamLength; + } else { + size = StreamLength + ch->len + ch->media_padding_bytes; + ch->buffer = (char*)realloc(ch->buffer, sizeof(char) * size); + memcpy(ch->buffer+ch->len, payload, StreamLength); + ch->allocSize = size; + ch->len += StreamLength; + } + if (hdr.paddingFlag) ch->padingBits = hdr.paddingBits; + } + + if (EndAU) Channel_DispatchAU(ch, hdr.au_duration); +} + + +/*notification of End of stream on this channel*/ +void gf_es_on_eos(GF_Channel *ch) +{ + if (!ch || ch->IsEndOfStream) return; + ch->IsEndOfStream = 1; + + /*flush buffer*/ + ch_buffer_off(ch); + + ch->clock->has_seen_eos = 1; + gf_odm_on_eos(ch->odm, ch); +} + + + +GF_DBUnit *gf_es_get_au(GF_Channel *ch) +{ + Bool comp, is_new_data; + GF_Err e, state; + GF_SLHeader slh; + + if (ch->es_state != GF_ESM_ES_RUNNING) return NULL; + + if (!ch->is_pulling) { + /*we must update buffering before fetching in order to stop buffering for streams with very few + updates (especially streams with one update, like most of OD streams)*/ + if (ch->BufferOn) Channel_UpdateBuffering(ch, 0); + if (ch->first_au_fetched && ch->BufferOn) return NULL; + return ch->AU_buffer_first; + } + + /*pull from stream - resume clock if needed*/ + ch_buffer_off(ch); + + e = gf_term_channel_get_sl_packet(ch->service, ch, (char **) &ch->AU_buffer_pull->data, &ch->AU_buffer_pull->dataLength, &slh, &comp, &state, &is_new_data); + if (e) state = e; + switch (state) { + case GF_EOS: + gf_es_on_eos(ch); + return NULL; + case GF_OK: + break; + default: + gf_term_message(ch->odm->term, ch->service->url , "Data reception failure", state); + return NULL; + } + assert(!comp); + /*update timing if new stream data but send no data*/ + if (is_new_data) { + gf_es_receive_sl_packet(ch->service, ch, NULL, 0, &slh, GF_OK); + if (ch->ipmp_tool) { + GF_IPMPEvent evt; + memset(&evt, 0, sizeof(evt)); + evt.event_type=GF_IPMP_TOOL_PROCESS_DATA; + evt.data = ch->AU_buffer_pull->data; + evt.data_size = ch->AU_buffer_pull->dataLength; + evt.is_encrypted = slh.isma_encrypted; + evt.isma_BSO = slh.isma_BSO; + evt.channel = ch; + e = ch->ipmp_tool->process(ch->ipmp_tool, &evt); + + /*we discard undecrypted AU*/ + if (e) { + if (e==GF_EOS) { + gf_es_on_eos(ch); + /*restart*/ + if (evt.restart_requested) { + if (ch->odm->parentscene->is_dynamic_scene) { + gf_inline_restart_dynamic(ch->odm->parentscene, 0); + } else { + MC_Restart(ch->odm); + } + } + } + gf_term_channel_release_sl_packet(ch->service, ch); + return NULL; + } + } + } + + /*this may happen in file streaming when data has not arrived yet, in which case we discard the AU*/ + if (!ch->AU_buffer_pull->data) { + gf_term_channel_release_sl_packet(ch->service, ch); + return NULL; + } + ch->AU_buffer_pull->CTS = (u32) ch->CTS; + ch->AU_buffer_pull->DTS = (u32) ch->DTS; + ch->AU_buffer_pull->PaddingBits = ch->padingBits; + ch->AU_buffer_pull->RAP = ch->IsRap; + return ch->AU_buffer_pull; +} + +void gf_es_init_dummy(GF_Channel *ch) +{ + GF_SLHeader slh; + Bool comp, is_new_data; + GF_Err e, state; + /*pull from stream - resume clock if needed*/ + ch_buffer_off(ch); + + ch->ts_res = 1000; + if (ch->is_pulling) { + e = gf_term_channel_get_sl_packet(ch->service, ch, (char **) &ch->AU_buffer_pull->data, &ch->AU_buffer_pull->dataLength, &slh, &comp, &state, &is_new_data); + if (e) state = e; + if ((state==GF_OK) && is_new_data) gf_es_receive_sl_packet(ch->service, ch, NULL, 0, &slh, GF_OK); + gf_term_channel_release_sl_packet(ch->service, ch); + } else { + memset(&slh, 0, sizeof(GF_SLHeader)); + slh.accessUnitStartFlag = 1; + slh.compositionTimeStampFlag = 1; + gf_es_receive_sl_packet(ch->service, ch, NULL, 0, &slh, GF_OK); + } +} + +void gf_es_drop_au(GF_Channel *ch) +{ + GF_DBUnit *au; + + if (ch->is_pulling) { + if (ch->AU_buffer_pull) { + gf_term_channel_release_sl_packet(ch->service, ch); + ch->AU_buffer_pull->data = NULL; + ch->AU_buffer_pull->dataLength = 0; + } + ch->first_au_fetched = 1; + return; + } + + /*lock the channel before touching the queue*/ + gf_es_lock(ch, 1); + if (!ch->AU_buffer_first) { + gf_es_lock(ch, 0); + return; + } + ch->first_au_fetched = 1; + + au = ch->AU_buffer_first; + ch->AU_buffer_first = au->next; + au->next = NULL; + gf_db_unit_del(au); + ch->AU_Count -= 1; + + if (!ch->AU_Count && ch->AU_buffer_first) { + ch->AU_buffer_first = NULL; + } + if (!ch->AU_buffer_first) ch->AU_buffer_last = NULL; + + Channel_UpdateBufferTime(ch); + + /*if we get under our limit, rebuffer EXCEPT WHEN EOS is signaled*/ + if (!ch->IsEndOfStream && Channel_NeedsBuffering(ch, 1)) { + ch_buffer_on(ch); + gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_NOT_PLAYABLE); + } + + /*unlock the channel*/ + gf_es_lock(ch, 0); +} + +/*(un)locks channel*/ +void gf_es_lock(GF_Channel *ch, u32 LockIt) +{ + if (LockIt) { + gf_mx_p(ch->mx); + } else { + gf_mx_v(ch->mx); + } +} + +/*refresh all ODs when an non-interactive stream is found*/ +static void refresh_non_interactive_clocks(GF_ObjectManager *odm) +{ + u32 i, j; + GF_Channel *ch; + GF_ObjectManager *test_od; + GF_InlineScene *in_scene; + + /*check for inline*/ + in_scene = odm->subscene ? odm->subscene : odm->parentscene; + i=0; + while ((ch = (GF_Channel*)gf_list_enum(in_scene->root_od->channels, &i)) ) { + if (ch->clock->no_time_ctrl) { + in_scene->root_od->flags |= GF_ODM_NO_TIME_CTRL; + //if (ch->clock->use_ocr && ch->BufferOn) ch_buffer_off(ch); + } + } + + i=0; + while ((test_od = (GF_ObjectManager *)gf_list_enum(in_scene->ODlist, &i)) ) { + j=0; + while ((ch = (GF_Channel*)gf_list_enum(test_od->channels, &j)) ) { + if (ch->clock->no_time_ctrl) { + test_od->flags |= GF_ODM_NO_TIME_CTRL; + //if (ch->clock->use_ocr && ch->BufferOn) ch_buffer_off(ch); + } + } + } +} +/*performs final setup upon connection confirm*/ +void gf_es_on_connect(GF_Channel *ch) +{ + Bool can_buffer; + GF_NetworkCommand com; + + /*check whether we can work in pull mode or not*/ + can_buffer = 1; + /*if local interaction streams no buffer nor pull*/ + if ((ch->esd->decoderConfig->streamType == GF_STREAM_INTERACT) && !ch->esd->URLString) can_buffer = 0; + + com.base.on_channel = ch; + + ch->is_pulling = 0; + if (can_buffer) { + /*request padding*/ + com.command_type = GF_NET_CHAN_SET_PADDING; + com.pad.padding_bytes = ch->media_padding_bytes; + if (!com.pad.padding_bytes || (gf_term_service_command(ch->service, &com) == GF_OK)) { + /*request pull if possible*/ + if (ch->service->ifce->ChannelGetSLP && ch->service->ifce->ChannelReleaseSLP) { + com.command_type = GF_NET_CHAN_SET_PULL; + if (gf_term_service_command(ch->service, &com) == GF_OK) { + ch->is_pulling = 1; + can_buffer = 0; + } + } + } + } + /*checks whether the stream is interactive or not*/ + com.command_type = GF_NET_CHAN_INTERACTIVE; + if (gf_term_service_command(ch->service, &com)!=GF_OK) { + ch->clock->no_time_ctrl = 1; + ch->odm->flags |= GF_ODM_NO_TIME_CTRL; + refresh_non_interactive_clocks(ch->odm); + } + + /*signal channel state*/ + if (ch->es_state == GF_ESM_ES_WAIT_FOR_ACK) + ch->es_state = GF_ESM_ES_CONNECTED; + /*signal only once connected to prevent PLAY trigger on connection callback*/ + ch->odm->pending_channels--; + + /*remember channels connected on service*/ + if (ch->esd->URLString) ch->service->nb_ch_users++; + + /*turn off buffering for JPEG and PNG*/ + switch (ch->esd->decoderConfig->objectTypeIndication) { + case 0x6C: + case 0x6D: + can_buffer = 0; + break; + } + /*buffer setup*/ + ch->MinBuffer = ch->MaxBuffer = 0; + if (can_buffer) { + const char *sOpt; + com.command_type = GF_NET_CHAN_BUFFER; + com.base.on_channel = ch; + + /*set default values*/ + com.buffer.max = 1000; + sOpt = gf_cfg_get_key(ch->odm->term->user->config, "Network", "BufferLength"); + if (sOpt) com.buffer.max = atoi(sOpt); + com.buffer.min = 0; + sOpt = gf_cfg_get_key(ch->odm->term->user->config, "Network", "RebufferLength"); + if (sOpt) com.buffer.min = atoi(sOpt); + + if (gf_term_service_command(ch->service, &com) == GF_OK) { + ch->MinBuffer = com.buffer.min; + ch->MaxBuffer = com.buffer.max; + } + } + if (ch->esd->decoderConfig->streamType == GF_STREAM_PRIVATE_SCENE && + ch->esd->decoderConfig->objectTypeIndication == GPAC_OTI_PRIVATE_SCENE_EPG) { + ch->bypass_sl_and_db = 1; + } + if (ch->clock->no_time_ctrl) { + switch (ch->esd->decoderConfig->streamType) { + case GF_STREAM_AUDIO: + case GF_STREAM_VISUAL: + break; + default: + ch->dispatch_after_db = 1; + break; + } + } + + /*get duration*/ + com.command_type = GF_NET_CHAN_DURATION; + com.base.on_channel = ch; + if (gf_term_service_command(ch->service, &com) == GF_OK) + gf_odm_set_duration(ch->odm, ch, (u64) (1000*com.duration.duration)); +} + +void gf_es_config_drm(GF_Channel *ch, GF_NetComDRMConfig *drm_cfg) +{ + GF_Terminal *term = ch->odm->term; + u32 i, count; + GF_Err e; + GF_IPMPEvent evt; + GF_OMADRM2Config cfg; + GF_OMADRM2Config isma_cfg; + + /*always buffer when fetching keys*/ + ch_buffer_on(ch); + ch->is_protected = 1; + + memset(&evt, 0, sizeof(GF_IPMPEvent)); + evt.event_type = GF_IPMP_TOOL_SETUP; + evt.channel = ch; + + /*push all cfg data*/ + if (drm_cfg->contentID) { + evt.config_data_code = GF_4CC('o','d','r','m'); + memset(&cfg, 0, sizeof(cfg)); + cfg.scheme_version = drm_cfg->scheme_version; + cfg.scheme_type = drm_cfg->scheme_type; + cfg.scheme_uri = drm_cfg->scheme_uri; + cfg.kms_uri = drm_cfg->kms_uri; + memcpy(cfg.hash, drm_cfg->hash, sizeof(char)*20); + cfg.contentID = drm_cfg->contentID; + cfg.oma_drm_crypt_type = drm_cfg->oma_drm_crypt_type; + cfg.oma_drm_use_pad = drm_cfg->oma_drm_use_pad; + cfg.oma_drm_use_hdr = drm_cfg->oma_drm_use_hdr; + cfg.oma_drm_textual_headers = drm_cfg->oma_drm_textual_headers; + cfg.oma_drm_textual_headers_len = drm_cfg->oma_drm_textual_headers_len; + evt.config_data = &cfg; + } else { + evt.config_data_code = GF_4CC('i','s','m','a'); + memset(&isma_cfg, 0, sizeof(isma_cfg)); + isma_cfg.scheme_version = drm_cfg->scheme_version; + isma_cfg.scheme_type = drm_cfg->scheme_type; + isma_cfg.scheme_uri = drm_cfg->scheme_uri; + isma_cfg.kms_uri = drm_cfg->kms_uri; + evt.config_data = &isma_cfg; + } + + if (ch->ipmp_tool) { + e = ch->ipmp_tool->process(ch->ipmp_tool, &evt); + if (e) gf_term_message(ch->odm->term, ch->service->url, "Error setting up DRM tool", e); + ch_buffer_off(ch); + return; + } + + /*browse all available tools*/ + count = gf_modules_get_count(term->user->modules); + for (i=0; i< count; i++) { + ch->ipmp_tool = (GF_IPMPTool *) gf_modules_load_interface(term->user->modules, i, GF_IPMP_TOOL_INTERFACE); + if (!ch->ipmp_tool) continue; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[IPMP] Checking if IPMP tool %s can handle channel protection scheme\n", ch->ipmp_tool->module_name)); + e = ch->ipmp_tool->process(ch->ipmp_tool, &evt); + if (e==GF_OK) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[IPMP] Associating IPMP tool %s to channel %d\n", ch->ipmp_tool->module_name, ch->esd->ESID)); + ch_buffer_off(ch); + return; + } + gf_modules_close_interface((GF_BaseInterface *) ch->ipmp_tool); + ch->ipmp_tool = NULL; + } + gf_term_message(ch->odm->term, ch->service->url, "No IPMP tool suitable to handle channel protection", GF_NOT_SUPPORTED); + ch_buffer_off(ch); +} + diff --git a/src/terminal/clock.c b/src/terminal/clock.c new file mode 100644 index 0000000..3787f51 --- /dev/null +++ b/src/terminal/clock.c @@ -0,0 +1,275 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +GF_Clock *NewClock(GF_Terminal *term) +{ + GF_Clock *tmp; + GF_SAFEALLOC(tmp, GF_Clock); + if (!tmp) return NULL; + tmp->mx = gf_mx_new("Clock"); + tmp->term = term; + tmp->speed = FIX_ONE; + if (term->play_state) tmp->Paused = 1; + return tmp; +} + +void gf_clock_del(GF_Clock *ck) +{ + gf_mx_del(ck->mx); + free(ck); +} + +GF_Clock *gf_clock_find(GF_List *Clocks, u16 clockID, u16 ES_ID) +{ + u32 i; + GF_Clock *tmp; + i=0; + while ((tmp = (GF_Clock *)gf_list_enum(Clocks, &i))) { + //first check the clock ID + if (tmp->clockID == clockID) return tmp; + //then check the ES ID + if (ES_ID && (tmp->clockID == ES_ID)) return tmp; + } + //no clocks found... + return NULL; +} + +GF_Clock *CK_LookForClockDep(struct _inline_scene *is, u16 clockID) +{ + u32 i, j; + GF_Channel *ch; + GF_ObjectManager *odm; + + /*check in top OD*/ + i=0; + while ((ch = (GF_Channel*)gf_list_enum(is->root_od->channels, &i))) { + if (ch->esd->ESID == clockID) return ch->clock; + } + /*check in sub ODs*/ + j=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &j))) { + i=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i))) { + if (ch->esd->ESID == clockID) return ch->clock; + } + } + return NULL; +} + +/*remove clocks created due to out-of-order OCR dependencies*/ +void CK_ResolveClockDep(GF_List *clocks, struct _inline_scene *is, GF_Clock *ck, u16 Clock_ESID) +{ + u32 i, j; + GF_Clock *clock; + GF_Channel *ch; + GF_ObjectManager *odm; + + /*check all channels - if any is using a clock which ID is the clock_ESID then + this clock shall be removed*/ + i=0; + while ((ch = (GF_Channel*)gf_list_enum(is->root_od->channels, &i))) { + if (ch->clock->clockID == Clock_ESID) { + if (is->scene_codec && is->scene_codec->ck == ch->clock) is->scene_codec->ck = ck; + if (is->od_codec && is->od_codec->ck == ch->clock) is->od_codec->ck = ck; + if (is->root_od->oci_codec && is->root_od->oci_codec->ck == ch->clock) is->root_od->oci_codec->ck = ck; + ch->clock = ck; + if (ch->esd) ch->esd->OCRESID = ck->clockID; + } + } + j=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &j))) { + i=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i))) { + if (ch->clock->clockID == Clock_ESID) { + if (odm->codec && (odm->codec->ck==ch->clock)) odm->codec->ck = ck; + if (odm->oci_codec && (odm->oci_codec->ck==ch->clock)) odm->oci_codec->ck = ck; + ch->clock = ck; + if (ch->esd) ch->esd->OCRESID = ck->clockID; + } + } + } + /*destroy clock*/ + i=0; + while ((clock = (GF_Clock*)gf_list_enum(clocks, &i))) { + if (clock->clockID == Clock_ESID) { + gf_list_rem(clocks, i-1); + gf_clock_del(clock); + return; + } + } +} + +GF_Clock *gf_clock_attach(GF_List *clocks, struct _inline_scene *is, u16 clockID, u16 ES_ID, s32 hasOCR) +{ + Bool check_dep; + GF_Clock *tmp = gf_clock_find(clocks, clockID, ES_ID); + /*ck dep can only be solved if in the main service*/ + check_dep = (is->root_od->net_service && is->root_od->net_service->Clocks==clocks) ? 1 : 0; + /*this partly solves a->b->c*/ + if (!tmp && check_dep) tmp = CK_LookForClockDep(is, clockID); + if (!tmp) { + tmp = NewClock(is->root_od->term); + tmp->clockID = clockID; + gf_list_add(clocks, tmp); + } else { + if (tmp->clockID == ES_ID) tmp->clockID = clockID; + /*this finally solves a->b->c*/ + if (check_dep && (tmp->clockID != ES_ID)) CK_ResolveClockDep(clocks, is, tmp, ES_ID); + } + if (hasOCR >= 0) tmp->use_ocr = hasOCR; + return tmp; +} + +void gf_clock_reset(GF_Clock *ck) +{ + ck->clock_init = 0; + ck->drift = 0; + ck->discontinuity_time = 0; + //do NOT reset buffering flag, because RESET is called only + //for the stream owning the clock, and other streams may + //have signaled buffering on this clock + ck->init_time = 0; + ck->StartTime = 0; + ck->has_seen_eos = 0; + if (ck->no_time_ctrl==2) ck->no_time_ctrl = 1; +} + +void gf_clock_stop(GF_Clock *ck) +{ + ck->StartTime = gf_clock_time(ck); + ck->clock_init = 0; +} + +void gf_clock_set_time(GF_Clock *ck, u32 TS) +{ + if (!ck->clock_init) { + ck->init_time = TS; + ck->clock_init = 1; + ck->drift = 0; + /*update starttime and pausetime even in pause mode*/ + ck->PauseTime = ck->StartTime = gf_term_get_time(ck->term); + if (ck->term->play_state) ck->Paused ++; + } + /*TODO: test with pure OCR streams*/ + else if (ck->use_ocr) { + /*just update the drift - we could also apply a drift algo*/ + u32 now = gf_clock_real_time(ck); + s32 drift = now - (u32) TS; + ck->drift += drift; + } +} + + + +void gf_clock_pause(GF_Clock *ck) +{ + gf_mx_p(ck->mx); + if (!ck->Paused) ck->PauseTime = gf_term_get_time(ck->term); + ck->Paused += 1; + gf_mx_v(ck->mx); +} + +void gf_clock_resume(GF_Clock *ck) +{ + gf_mx_p(ck->mx); +// assert(ck->Paused); + ck->Paused -= 1; + if (!ck->Paused) + ck->StartTime += gf_term_get_time(ck->term) - ck->PauseTime; + gf_mx_v(ck->mx); +} + + +u32 gf_clock_real_time(GF_Clock *ck) +{ + u32 time; + assert(ck); + if (!ck->clock_init) return ck->StartTime; + time = ck->Paused > 0 ? ck->PauseTime : gf_term_get_time(ck->term); +#ifdef GPAC_FIXED_POINT + time = ck->discontinuity_time + ck->init_time + (time - ck->StartTime) * FIX2INT(100*ck->speed) / 100; +#else + time = ck->discontinuity_time + ck->init_time + (u32) (ck->speed * (time - ck->StartTime) ); +#endif + return time; +} + +u32 gf_clock_time(GF_Clock *ck) +{ + u32 time = gf_clock_real_time(ck); + if ((s32) time < ck->drift) return 0; + return time - ck->drift; +} + +u32 gf_clock_ellapse_time(GF_Clock *ck) +{ + if (ck->no_time_ctrl) return gf_clock_time(ck) - ck->init_time; + return gf_clock_time(ck); +} + + +Bool gf_clock_is_started(GF_Clock *ck) +{ + if (/*!ck->StartTime || */ck->Buffering || ck->Paused) return 0; + return 1; +} + +/*buffering is protected by a mutex because it may be triggered by composition memory (audio or visual threads)*/ +void gf_clock_buffer_on(GF_Clock *ck) +{ + gf_mx_p(ck->mx); + if (!ck->Buffering) gf_clock_pause(ck); + ck->Buffering += 1; + gf_mx_v(ck->mx); +} + +void gf_clock_buffer_off(GF_Clock *ck) +{ + gf_mx_p(ck->mx); + //assert(ck->Buffering); + if (ck->Buffering) { + ck->Buffering -= 1; + if (!ck->Buffering) gf_clock_resume(ck); + } + gf_mx_v(ck->mx); +} + + +void gf_clock_set_speed(GF_Clock *ck, Fixed speed) +{ + u32 time; + if (speed==ck->speed) return; + time = gf_term_get_time(ck->term); + /*adjust start time*/ + ck->discontinuity_time = gf_clock_time(ck); + ck->PauseTime = ck->StartTime = time; + ck->speed = speed; +} + +void gf_clock_adjust_drift(GF_Clock *ck, s32 ms_drift) +{ + if (ck) ck->drift = ms_drift; +} diff --git a/src/terminal/decoder.c b/src/terminal/decoder.c new file mode 100644 index 0000000..f8e3d8a --- /dev/null +++ b/src/terminal/decoder.c @@ -0,0 +1,1025 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include +#include +#include "media_memory.h" +#include "media_control.h" +#include "input_sensor.h" + +#define TIME_CHECK 3 + + +GF_Err Codec_Load(GF_Codec *codec, GF_ESD *esd, u32 PL); + +GF_Codec *gf_codec_new(GF_ObjectManager *odm, GF_ESD *base_layer, s32 PL, GF_Err *e) +{ + GF_Codec *tmp; + GF_SAFEALLOC(tmp, GF_Codec); + if (! tmp) { + *e = GF_OUT_OF_MEM; + return NULL; + } + tmp->odm = odm; + + if (PL<0) PL = 0xFF; + *e = Codec_Load(tmp, base_layer, PL); + + if (*e) { + free(tmp); + return NULL; + } + /*remember codec type*/ + tmp->type = base_layer->decoderConfig->streamType; + tmp->inChannels = gf_list_new(); + tmp->Status = GF_ESM_CODEC_STOP; + + return tmp; +} + +GF_Codec *gf_codec_use_codec(GF_Codec *codec, GF_ObjectManager *odm) +{ + GF_Codec *tmp; + if (!codec->decio) return NULL; + GF_SAFEALLOC(tmp, GF_Codec); + tmp->type = codec->type; + tmp->inChannels = gf_list_new(); + tmp->Status = GF_ESM_CODEC_STOP; + tmp->odm = odm; + tmp->flags = codec->flags | GF_ESM_CODEC_IS_USE; + tmp->decio = codec->decio; + return tmp; +} + +GF_Err gf_codec_add_channel(GF_Codec *codec, GF_Channel *ch) +{ + GF_Err e; + GF_NetworkCommand com; + GF_Channel *a_ch; + char *dsi; + u32 dsiSize, CUsize, i; + GF_CodecCapability cap; + u32 min, max; + + + /*only for valid codecs (eg not OCR)*/ + if (codec->decio) { + com.get_dsi.dsi = NULL; + dsi = NULL; + dsiSize = 0; + if (ch->esd->decoderConfig->upstream) codec->flags |= GF_ESM_CODEC_HAS_UPSTREAM; + if (ch->esd->decoderConfig->decoderSpecificInfo) { + dsi = ch->esd->decoderConfig->decoderSpecificInfo->data; + dsiSize = ch->esd->decoderConfig->decoderSpecificInfo->dataLength; + } + /*For objects declared in OD stream, override with network DSI if any*/ + if (ch->service && !(ch->odm->flags & GF_ODM_NOT_IN_OD_STREAM) ) { + com.command_type = GF_NET_CHAN_GET_DSI; + com.base.on_channel = ch; + e = gf_term_service_command(ch->service, &com); + if (!e && com.get_dsi.dsi) { + dsi = com.get_dsi.dsi; + dsiSize = com.get_dsi.dsi_len; + + if (ch->esd->decoderConfig->decoderSpecificInfo->data) free(ch->esd->decoderConfig->decoderSpecificInfo->data); + ch->esd->decoderConfig->decoderSpecificInfo->data = com.get_dsi.dsi; + ch->esd->decoderConfig->decoderSpecificInfo->dataLength = com.get_dsi.dsi_len; + } + } + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[Codec] Attaching stream %d to codec %s\n", ch->esd->ESID, codec->decio->module_name)); + + /*lock the channel before setup in case we are using direct_decode */ + gf_mx_p(ch->mx); + e = codec->decio->AttachStream(codec->decio, ch->esd); + gf_mx_v(ch->mx); + + + if (e) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[Codec] Attach Stream failed %s\n", gf_error_to_string(e) )); + return e; + } + + /*ask codec for desired output capacity - note this may be 0 if stream is not yet configured*/ + cap.CapCode = GF_CODEC_OUTPUT_SIZE; + gf_codec_get_capability(codec, &cap); + if (codec->CB && (cap.cap.valueInt != codec->CB->UnitSize)) { + gf_cm_del(codec->CB); + codec->CB = NULL; + } + CUsize = cap.cap.valueInt; + + /*get desired amount of units and minimal fullness (used for scheduling)*/ + switch(codec->type) { + case GF_STREAM_VISUAL: + case GF_STREAM_AUDIO: + cap.CapCode = GF_CODEC_BUFFER_MIN; + gf_codec_get_capability(codec, &cap); + min = cap.cap.valueInt; + cap.CapCode = GF_CODEC_BUFFER_MAX; + gf_codec_get_capability(codec, &cap); + max = cap.cap.valueInt; + break; + case GF_STREAM_ND_SUBPIC: + max = 1; + min = 0; + break; + default: + min = max = 0; + } + if ((codec->type==GF_STREAM_AUDIO) && (max<2)) max = 2; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[ODM] Creating composition buffer for codec %s - %d units %d bytes each\n", codec->decio->module_name, max, CUsize)); + /*setup CB*/ + if (!codec->CB && max) { + codec->CB = gf_cm_new(CUsize, max); + codec->CB->Min = min; + codec->CB->odm = codec->odm; + } + + /*check re-ordering - set by default on all codecs*/ + codec->is_reordering = 1; + cap.CapCode = GF_CODEC_REORDER; + if (gf_codec_get_capability(codec, &cap) == GF_OK); + codec->is_reordering = cap.cap.valueInt; + + /*setup net channel config*/ + if (ch->service) { + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.command_type = GF_NET_CHAN_CONFIG; + com.base.on_channel = ch; + + com.cfg.priority = ch->esd->streamPriority; + com.cfg.sync_id = (u32) ch->clock; + memcpy(&com.cfg.sl_config, ch->esd->slConfig, sizeof(GF_SLConfig)); + /*get the frame duration if audio (used by some network stack)*/ + if (ch->odm->codec && (ch->odm->codec->type==GF_STREAM_AUDIO) ) { + cap.CapCode = GF_CODEC_SAMPLERATE; + gf_codec_get_capability(ch->odm->codec, &cap); + com.cfg.sample_rate = cap.cap.valueInt; + cap.CapCode = GF_CODEC_CU_DURATION; + gf_codec_get_capability(ch->odm->codec, &cap); + com.cfg.frame_duration = cap.cap.valueInt; + } + gf_term_service_command(ch->service, &com); + } + } + + /*assign the first base layer as the codec clock by default, or current channel clock if no clock set + Also assign codec priority here*/ + if (!ch->esd->dependsOnESID || !codec->ck) { + codec->ck = ch->clock; + codec->Priority = ch->esd->streamPriority; + /*insert base layer first - note we are sure this is a stream of the same type + as the codec (other streams - OCI, MPEG7, MPEGJ - are not added that way)*/ + return gf_list_insert(codec->inChannels, ch, 0); + } + else { + /*make sure all channels are in order*/ + i=0; + while ((a_ch = (GF_Channel*)gf_list_enum(codec->inChannels, &i))) { + if (ch->esd->dependsOnESID == a_ch->esd->ESID) { + return gf_list_insert(codec->inChannels, ch, i); + } + if (a_ch->esd->dependsOnESID == ch->esd->ESID) { + return gf_list_insert(codec->inChannels, ch, i-1); + } + } + /*by default append*/ + return gf_list_add(codec->inChannels, ch); + } +} + +Bool gf_codec_remove_channel(GF_Codec *codec, struct _es_channel *ch) +{ + s32 i; + i = gf_list_find(codec->inChannels, ch); + if (i>=0) { + if (codec->decio) codec->decio->DetachStream(codec->decio, ch->esd->ESID); + gf_list_rem(codec->inChannels, (u32) i); + return 1; + } + return 0; +} + + +static void codec_update_stats(GF_Codec *codec, u32 dataLength, u32 dec_time) +{ + codec->total_dec_time += dec_time; + codec->nb_dec_frames++; + if (dec_time>codec->max_dec_time) codec->max_dec_time = dec_time; + + if (dataLength) { + u32 now = gf_clock_time(codec->ck); + if (codec->last_stat_start + 1000 <= now) { + if (!codec->cur_bit_size) { + codec->last_stat_start = now; + } else { + codec->avg_bit_rate = codec->cur_bit_size; + if (codec->avg_bit_rate > codec->max_bit_rate) codec->max_bit_rate = codec->avg_bit_rate; + codec->last_stat_start = now; + codec->cur_bit_size = 0; + } + } + codec->cur_bit_size += 8*dataLength; + } +} + +/*scalable browsing of input channels: find the AU with the lowest DTS on all input channels*/ +static void Decoder_GetNextAU(GF_Codec *codec, GF_Channel **activeChannel, GF_DBUnit **nextAU) +{ + GF_Channel *ch; + GF_DBUnit *AU; + u32 count, minDTS, i; + count = gf_list_count(codec->inChannels); + *nextAU = NULL; + *activeChannel = NULL; + + if (!count) return; + + minDTS = (u32) -1; + /*reverse browsing to make sure we fill enhancement before base layer*/ + for (i=count;i>0;i--) { + ch = (GF_Channel*)gf_list_get(codec->inChannels, i-1); + + if ((codec->type==GF_STREAM_OCR) && ch->IsClockInit) { + /*check duration - we assume that scalable OCR streams are just pure nonsense...*/ + if (ch->is_pulling && codec->odm->duration) { + if (gf_clock_time(codec->ck) > codec->odm->duration) + gf_es_on_eos(ch); + } + return; + } + + AU = gf_es_get_au(ch); + if (!AU) { + if (! (*activeChannel)) *activeChannel = ch; + continue; + } + + /*we use <= to make sure we first fetch data on base layer if same + DTS (which is possible in spatial scalability)*/ + if (AU->DTS <= minDTS) { + minDTS = AU->DTS; + *activeChannel = ch; + *nextAU = AU; + } + } + + /*FIXME - we're breaking sync (couple of frames delay)*/ + if (*nextAU && codec->is_reordering) + (*nextAU)->CTS = (*nextAU)->DTS; +} + + +static GF_Err SystemCodec_Process(GF_Codec *codec, u32 TimeAvailable) +{ + GF_DBUnit *AU; + GF_Channel *ch; + u32 now, obj_time, mm_level, au_time; + Bool scene_locked; + Bool check_next_unit; + GF_SceneDecoder *sdec = (GF_SceneDecoder *)codec->decio; + GF_Err e = GF_OK; + + scene_locked = 0; + + /*for resync, if needed - the logic behind this is that there is no composition memory on sytems codecs so + "frame dropping" is done by preventing the compositor from redrawing after an update and decoding following AU + so that the compositor is always woken up once all late systems AUs are decoded. This flag is overriden when + seeking*/ + check_next_unit = (codec->odm->term->flags & GF_TERM_SYSDEC_RESYNC) ? 1 : 0; + +check_unit: + + /*muting systems codec means we don't decode until mute is off - likely there will be visible however + there is no other way to decode system AUs without modifying the content, which is what mute is about on visual...*/ + if (codec->Muted) goto exit; + + /*fetch next AU in DTS order for this codec*/ + Decoder_GetNextAU(codec, &ch, &AU); + + /*no active channel return*/ + if (!AU || !ch) { + /*if the codec is in EOS state, move to STOP*/ + if (codec->Status == GF_ESM_CODEC_EOS) { + GF_CodecCapability cap; + cap.CapCode = GF_CODEC_MEDIA_NOT_OVER; + cap.cap.valueInt = 0; + sdec->GetCapabilities(codec->decio, &cap); + if (!cap.cap.valueInt) { + gf_term_stop_codec(codec); + if ((codec->type==GF_STREAM_OD) && (codec->nb_dec_frames==1)) { + /*this is just by safety, since seeking is only allowed when a single clock is present + in the scene*/ + if (gf_list_count(codec->odm->net_service->Clocks)==1) + codec->odm->subscene->static_media_ressources=1; + } + } + } + goto exit; + } + + if (ch && ch->odm->media_ctrl && !ch->odm->media_ctrl->media_speed) + goto exit; + + /*get the object time*/ + obj_time = gf_clock_time(codec->ck); + + /*clock is not started*/ +// if (ch->first_au_fetched && !gf_clock_is_started(ch->clock)) goto exit; + + /*check timing based on the input channel and main FPS*/ + if (AU->DTS > obj_time /*+ codec->odm->term->half_frame_duration*/) goto exit; + + /*check seeking and update timing - do NOT use the base layer, since BIFS streams may depend on other + streams not on the same clock*/ + if (codec->last_unit_cts == AU->CTS ) { + /*hack for RTSP streaming of systems streams, except InputSensor*/ + if (!ch->is_pulling && (codec->type != GF_STREAM_INTERACT) && (AU->dataLength == codec->prev_au_size)) { + gf_es_drop_au(ch); + GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[SysDec] Same MPEG-4 Systems AU detected - dropping\n")); + goto check_unit; + } + /*seeking for systems is done by not releasing the graph until seek is done*/ + check_next_unit = 1; + mm_level = GF_CODEC_LEVEL_SEEK; + au_time = AU->DTS; + + } + /*set system stream timing*/ + else { + codec->last_unit_cts = AU->CTS; + /*we're droping the frame*/ + if (scene_locked) codec->nb_droped ++; + mm_level = GF_CODEC_LEVEL_NORMAL; + au_time = AU->DTS; + } + + /*lock scene*/ + if (!scene_locked) { + if (!gf_mx_try_lock(codec->odm->term->compositor->mx)) return GF_OK; + //gf_term_lock_compositor(codec->odm->term, 1); + scene_locked = 1; + /*if terminal is paused, force step-mode: it won't hurt in regular pause/play and ensures proper frame dumping*/ + if (codec->odm->term->play_state) codec->odm->term->compositor->step_mode=1; + } + + /*current media time for system objects is the clock time, since the media is likely to have random + updates in time*/ + codec->odm->current_time = gf_clock_time(codec->ck); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[SysDec] Codec %s AU CTS %d channel %d OTB %d\n", sdec->module_name , AU->CTS, ch->esd->ESID, codec->odm->current_time)); + now = gf_term_get_time(codec->odm->term); + e = sdec->ProcessData(sdec, AU->data, AU->dataLength, ch->esd->ESID, au_time, mm_level); + now = gf_term_get_time(codec->odm->term) - now; + + codec_update_stats(codec, AU->dataLength, now); + codec->prev_au_size = AU->dataLength; + + /*destroy this AU*/ + gf_es_drop_au(ch); + + if (e) { + if (e<0) ch->stream_state = 2; + goto exit; + } + + /*in broadcast mode, generate a scene if none is available*/ + if (codec->ck->no_time_ctrl) { + GF_InlineScene *is = codec->odm->subscene ? codec->odm->subscene : codec->odm->parentscene; + + /*static OD resources (embedded in ESD) in broadcast mode, reset time*/ + if (codec->flags & GF_ESM_CODEC_IS_STATIC_OD) gf_clock_reset(codec->ck); + /*generate a temp scene if none is in place*/ + if (is->graph_attached != 1) { + Bool prev_dyn = is->is_dynamic_scene; + is->is_dynamic_scene = 1; + gf_inline_regenerate(is); + is->graph_attached = 2; + is->is_dynamic_scene = prev_dyn; + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[Decoder] Got OD resources before scene - generating temporary scene\n")); + } + } + + /*if no release restart*/ + if (check_next_unit) goto check_unit; + +exit: + if (scene_locked) gf_term_lock_compositor(codec->odm->term, 0); + return e; +} + + + +/*special handling of decoders not using ESM*/ +static GF_Err PrivateScene_Process(GF_Codec *codec, u32 TimeAvailable) +{ + Bool resume_clock; + u32 now; + GF_Channel *ch; + GF_SceneDecoder *sdec = (GF_SceneDecoder *)codec->decio; + GF_Err e = GF_OK; + + /*muting systems codec means we don't decode until mute is off - likely there will be visible however + there is no other way to decode system AUs without modifying the content, which is what mute is about on visual...*/ + if (codec->Muted) return GF_OK; + + if (codec->Status == GF_ESM_CODEC_EOS) { + gf_term_stop_codec(codec); + return GF_OK; + } + + ch = (GF_Channel*)gf_list_get(codec->inChannels, 0); + if (!ch) return GF_OK; + resume_clock = 0; + /*init channel clock*/ + if (!ch->IsClockInit) { + Bool started; + /*signal seek*/ + if (!gf_mx_try_lock(codec->odm->term->compositor->mx)) return GF_OK; + //gf_term_lock_compositor(codec->odm->term, 1); + gf_es_init_dummy(ch); + + sdec->ProcessData(sdec, NULL, 0, ch->esd->ESID, -1, GF_CODEC_LEVEL_NORMAL); + gf_term_lock_compositor(codec->odm->term, 0); + started = gf_clock_is_started(ch->clock); + /*let's be nice to the scene loader (that usually involves quite some parsing), pause clock while + parsing*/ + gf_clock_pause(ch->clock); + codec->last_unit_dts = 0; + if (!started) return GF_OK; + } + + codec->odm->current_time = codec->last_unit_cts = gf_clock_time(codec->ck); + + /*lock scene*/ + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[PrivateDec] Codec %s Processing at %d\n", sdec->module_name , codec->odm->current_time)); + + //gf_term_lock_compositor(codec->odm->term, 1); + if (!gf_mx_try_lock(codec->odm->term->compositor->mx)) return GF_OK; + now = gf_term_get_time(codec->odm->term); + e = sdec->ProcessData(sdec, NULL, 0, ch->esd->ESID, codec->odm->current_time, GF_CODEC_LEVEL_NORMAL); + now = gf_term_get_time(codec->odm->term) - now; + codec->last_unit_dts ++; + /*resume on error*/ + if (e && (codec->last_unit_dts<2) ) { + gf_clock_resume(ch->clock); + codec->last_unit_dts = 2; + } + /*resume clock on 2nd decode (we assume parsing is done in 2 steps, one for first frame display, one for complete parse)*/ + else if (codec->last_unit_dts==2) { + gf_clock_resume(ch->clock); + } + + codec_update_stats(codec, 0, now); + + gf_term_lock_compositor(codec->odm->term, 0); + + if (e==GF_EOS) { + /*first end of stream, evaluate duration*/ + if (!codec->odm->duration) gf_odm_set_duration(codec->odm, ch, codec->odm->current_time); + gf_es_on_eos(ch); + return GF_OK; + } + return e; +} +/*Get a pointer to the next CU buffer*/ +static GFINLINE GF_Err LockCompositionUnit(GF_Codec *dec, u32 CU_TS, GF_CMUnit **cu, u32 *cu_size) +{ + if (!dec->CB) return GF_BAD_PARAM; + + *cu = gf_cm_lock_input(dec->CB, CU_TS, dec->is_reordering); + if (! *cu ) return GF_OUT_OF_MEM; + *cu_size = dec->CB->UnitSize; + return GF_OK; +} + + +static GFINLINE GF_Err UnlockCompositionUnit(GF_Codec *dec, GF_CMUnit *CU, u32 cu_size) +{ + /*temporal scalability disabling: if we already rendered this, no point getting further*/ + if (CU->TS < dec->CB->LastRenderedTS) { + GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("[ODM] CU (TS %d) later than last frame drawn (TS %d) - droping\n", CU->TS, dec->CB->LastRenderedTS)); + cu_size = 0; + } + + /*unlock the CB*/ + gf_cm_unlock_input(dec->CB, CU, cu_size, dec->is_reordering); + return GF_OK; +} + + +static GF_Err ResizeCompositionBuffer(GF_Codec *dec, u32 NewSize) +{ + if (!dec || !dec->CB) return GF_BAD_PARAM; + + /*update config*/ + gf_mo_update_caps(dec->odm->mo); + + /*bytes per sec not available: either video or audio not configured*/ + if (!dec->bytes_per_sec) { + if (NewSize && (NewSize != dec->CB->UnitSize) ) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[ODM] Resizing composition buffer for codec %s - %d bytes per unit\n", dec->decio->module_name, NewSize)); + gf_cm_resize(dec->CB, NewSize); + } + } + /*audio: make sure we have enough data in CM to entirely fill the HW audio buffer...*/ + else { + u32 unit_size, audio_buf_len, unit_count; + GF_CodecCapability cap; + unit_size = NewSize; + /*a bit ugly, make some extra provision for speed >1. this is the drawback of working with pre-allocated memory + for composition, we may get into cases where there will never be enough data for high speeds... + FIXME - WE WILL NEED TO MOVE TO DYNAMIC CU BLOCKS IN ORDER TO SUPPORT ANY SPEED, BUT WHAT IS THE IMPACT + FOR LOW RESOURCES DEVICES ??*/ +// audio_buf_len = 1000; + audio_buf_len = 200; + + cap.CapCode = GF_CODEC_BUFFER_MAX; + gf_codec_get_capability(dec, &cap); + unit_count = cap.cap.valueInt; + /*at least 2 units for dec and render ...*/ + if (unit_count<2) unit_count = 2; + while (unit_size*unit_count*1000 < dec->bytes_per_sec*audio_buf_len) unit_count++; +#ifdef __SYMBIAN32__ + /*FIXME - symbian tests*/ + unit_count = 10; +#endif + gf_cm_reinit(dec->CB, unit_size, unit_count); + dec->CB->Min = unit_count/3; + if (!dec->CB->Min) dec->CB->Min = 1; + } + if ((dec->type==GF_STREAM_VISUAL) && dec->odm->parentscene->is_dynamic_scene) { + gf_inline_force_scene_size_video(dec->odm->parentscene, dec->odm->mo); + } + return GF_OK; +} + +static GF_Err MediaCodec_Process(GF_Codec *codec, u32 TimeAvailable) +{ + GF_CMUnit *CU; + GF_DBUnit *AU; + GF_Channel *ch, *prev_ch; + u32 mmlevel, cts; + u32 first, entryTime, now, obj_time, unit_size; + GF_MediaDecoder *mdec = (GF_MediaDecoder*)codec->decio; + GF_Err e = GF_OK; + + /*if video codec muted don't decode (try to saves ressources) + if audio codec muted we dispatch to keep sync in place*/ + if (codec->Muted && (codec->type==GF_STREAM_VISUAL) ) return GF_OK; + + entryTime = gf_term_get_time(codec->odm->term); + + /*fetch next AU in DTS order for this codec*/ + Decoder_GetNextAU(codec, &ch, &AU); + /*no active channel return*/ + if (!AU || !ch) { + /*if the codec is in EOS state, assume we're done*/ + if (codec->Status == GF_ESM_CODEC_EOS) { + /*if codec is reordering, try to flush it*/ + if (codec->is_reordering) { + if ( LockCompositionUnit(codec, codec->last_unit_cts+1, &CU, &unit_size) == GF_OUT_OF_MEM) + return GF_OK; + e = mdec->ProcessData(mdec, NULL, 0, 0, CU->data, &unit_size, 0, 0); + if (e==GF_OK) e = UnlockCompositionUnit(codec, CU, unit_size); + } + gf_term_stop_codec(codec); + if (codec->CB) gf_cm_set_eos(codec->CB); + } + /*if no data, and channel not buffering, ABORT CB buffer (data timeout or EOS not detectable)*/ + else if (ch && !ch->BufferOn) + gf_cm_abort_buffering(codec->CB); + return GF_OK; + } + + /*get the object time*/ + obj_time = gf_clock_time(codec->ck); + /*Media Time for media codecs is updated in the CB*/ + + if (!codec->CB) { + gf_es_drop_au(ch); + return GF_BAD_PARAM; + } + /*image codecs - usually only one image is tolerated in the stream, but just in case force reset of CB*/ + if ((codec->CB->Capacity==1) && codec->CB->UnitCount && (obj_time>=AU->CTS)) { + codec->CB->output->dataLength = 0; + codec->CB->UnitCount = 0; + } + + /*try to refill the full buffer*/ + first = 1; + while (codec->CB->Capacity > codec->CB->UnitCount) { + /*set media processing level*/ + mmlevel = GF_CODEC_LEVEL_NORMAL; + /*SEEK: if the last frame had the same TS, we are seeking. Ask the codec to drop*/ + if (!ch->skip_sl && codec->last_unit_cts && (codec->last_unit_cts == AU->CTS) && !ch->esd->dependsOnESID) { + mmlevel = GF_CODEC_LEVEL_SEEK; + /*object clock is paused by media control or terminal is paused: exact frame seek*/ + if ((codec->ck->mc && codec->ck->mc->paused) || (codec->odm->term->play_state)) { + gf_cm_rewind_input(codec->CB); + mmlevel = GF_CODEC_LEVEL_NORMAL; + /*force staying in step-mode*/ + codec->odm->term->compositor->step_mode=1; + } + } + /*only perform drop in normal playback*/ + else if (codec->CB->Status == CB_PLAY) { + /*extremely late, set the level to drop + NOTE: the 100 ms safety gard is to avoid discarding audio*/ + if (!ch->skip_sl && (AU->CTS + 100 < obj_time) ) { + mmlevel = GF_CODEC_LEVEL_DROP; + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[Decoder] ODM%d: frame too late (%d vs %d) - using drop level\n", codec->odm->OD->objectDescriptorID, AU->CTS, obj_time)); + } + /*we are late according to the media manager*/ + else if (codec->PriorityBoost) { + mmlevel = GF_CODEC_LEVEL_VERY_LATE; + } + /*otherwise we must have an idea of the load in order to set the right level + use the composition buffer for that, only on the first frame*/ + else if (first) { + //if the CB is almost empty set to very late + if (codec->CB->UnitCount <= codec->CB->Min+1) { + mmlevel = GF_CODEC_LEVEL_VERY_LATE; + } else if (codec->CB->UnitCount * 2 <= codec->CB->Capacity) { + mmlevel = GF_CODEC_LEVEL_LATE; + } + first = 0; + } + } + + /*when using temporal scalability make sure we can decode*/ + if (ch->esd->dependsOnESID && (codec->last_unit_dts > AU->DTS)){ +// printf("SCALABLE STREAM DEAD!!\n"); + goto drop; + } + + if (ch->skip_sl) { + if (codec->bytes_per_sec) { + AU->CTS = codec->last_unit_cts + ch->ts_offset + codec->cur_audio_bytes * 1000 / codec->bytes_per_sec; + } else if (codec->fps) { + AU->CTS = codec->last_unit_cts + ch->ts_offset + (u32) (codec->cur_video_frames * 1000 / codec->fps); + } + } + if ( LockCompositionUnit(codec, AU->CTS, &CU, &unit_size) == GF_OUT_OF_MEM) + return GF_OK; + +scalable_retry: + + now = gf_term_get_time(codec->odm->term); + + e = mdec->ProcessData(mdec, AU->data, AU->dataLength, ch->esd->ESID, CU->data, &unit_size, AU->PaddingBits, mmlevel); + now = gf_term_get_time(codec->odm->term) - now; + if (codec->Status == GF_ESM_CODEC_STOP) return GF_OK; + + /*input is too small, resize composition memory*/ + switch (e) { + case GF_BUFFER_TOO_SMALL: + /*release but no dispatch*/ + UnlockCompositionUnit(codec, CU, 0); + ResizeCompositionBuffer(codec, unit_size); + continue; + + /*this happens a lot when using non-MPEG-4 streams (ex: ffmpeg demuxer)*/ + case GF_PACKED_FRAMES: + /*in seek don't dispatch any output*/ + if (mmlevel == GF_CODEC_LEVEL_SEEK) + unit_size = 0; + e = UnlockCompositionUnit(codec, CU, unit_size); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI|GF_LOG_CODEC, ("[%s] ODM%d at %d decoded packed frame TS %d in %d ms\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, obj_time, AU->CTS, now)); + if (ch->skip_sl) { + if (codec->bytes_per_sec) { + codec->cur_audio_bytes += unit_size; + } else if (codec->fps && unit_size) { + codec->cur_video_frames += 1; + } + } else { + u32 deltaTS = 0; + if (codec->bytes_per_sec) { + deltaTS = unit_size * 1000 / codec->bytes_per_sec; + } /*else if (0 && codec->fps && unit_size) { + deltaTS = (u32) (1000.0f / codec->fps); + } */else { + deltaTS = (AU->DTS - codec->last_unit_dts); + } + AU->CTS += deltaTS; + } + codec_update_stats(codec, 0, now); + continue; + + /*for all cases below, don't release the composition buffer until we are sure we are not + processing a scalable stream*/ + case GF_OK: + if (unit_size) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI|GF_LOG_CODEC, ("[%s] ODM%d at %d decoded frame TS %d in %d ms (DTS %d)\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, obj_time, AU->CTS, now, AU->DTS)); + } + /*in seek don't dispatch any output*/ + if (mmlevel == GF_CODEC_LEVEL_SEEK) + unit_size = 0; + + codec_update_stats(codec, AU->dataLength, now); + if (ch->skip_sl) { + if (codec->bytes_per_sec) { + codec->cur_audio_bytes += unit_size; + while (codec->cur_audio_bytes>codec->bytes_per_sec) { + codec->cur_audio_bytes -= codec->bytes_per_sec; + codec->last_unit_cts += 1000; + } + } else if (codec->fps && unit_size) { + codec->cur_video_frames += 1; + } + } + break; + default: + unit_size = 0; + /*error - if the object is in intitial buffering resume it!!*/ + gf_cm_abort_buffering(codec->CB); + break; + } + + codec->last_unit_dts = AU->DTS; + /*remember base layer timing*/ + if (!ch->esd->dependsOnESID && !ch->skip_sl) codec->last_unit_cts = AU->CTS; + + +drop: + /*store current CTS*/ + cts = AU->CTS; + prev_ch = ch; + + gf_es_drop_au(ch); + if (e) { + UnlockCompositionUnit(codec, CU, unit_size); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Decoder %s] ODM%d: decoded error %s\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, gf_error_to_string(e) )); + return e; + } + + Decoder_GetNextAU(codec, &ch, &AU); + /*same CTS: same output, likely scalable stream so don't release the CB*/ + if (AU && (AU->CTS == cts) && (ch !=prev_ch) ) + goto scalable_retry; + + UnlockCompositionUnit(codec, CU, unit_size); + if (!ch || !AU) return GF_OK; + + /*escape from decoding loop only if above critical limit - this is to avoid starvation on audio*/ + if (!ch->esd->dependsOnESID && (codec->CB->UnitCount > codec->CB->Min)) { + now = gf_term_get_time(codec->odm->term); + if (0 && now - entryTime + TIME_CHECK >= TimeAvailable) { + return GF_OK; + } + } + Decoder_GetNextAU(codec, &ch, &AU); + if (!ch || !AU) return GF_OK; + } + return GF_OK; +} + + + +GF_Err gf_codec_process(GF_Codec *codec, u32 TimeAvailable) +{ + if (codec->Status == GF_ESM_CODEC_STOP) return GF_OK; + codec->Muted = (codec->odm->media_ctrl && codec->odm->media_ctrl->control->mute) ? 1 : 0; + + /*OCR: needed for OCR in pull mode (dummy streams used to sync various sources)*/ + if (codec->type==GF_STREAM_OCR) { + GF_DBUnit *AU; + GF_Channel *ch; + /*fetch next AU on OCR (empty AUs)*/ + Decoder_GetNextAU(codec, &ch, &AU); + + /*no active channel return*/ + if (!AU || !ch) { + /*if the codec is in EOS state, move to STOP*/ + if (codec->Status == GF_ESM_CODEC_EOS) { + gf_term_stop_codec(codec); + /*if a mediacontrol is ruling this OCR*/ + if (codec->odm->media_ctrl && codec->odm->media_ctrl->control->loop) MC_Restart(codec->odm); + } + } + } + /*special case here (we tweak a bit the timing)*/ + else if (codec->type==GF_STREAM_PRIVATE_SCENE) { + return PrivateScene_Process(codec, TimeAvailable); + } else if (codec->decio->InterfaceType==GF_MEDIA_DECODER_INTERFACE) { + return MediaCodec_Process(codec, TimeAvailable); + } else if (codec->decio->InterfaceType==GF_SCENE_DECODER_INTERFACE) { + return SystemCodec_Process(codec, TimeAvailable); + } + return GF_OK; +} + + +GF_Err gf_codec_get_capability(GF_Codec *codec, GF_CodecCapability *cap) +{ + cap->cap.valueInt = 0; + if (!codec->decio) return GF_OK; + return codec->decio->GetCapabilities(codec->decio, cap); +} + +GF_Err gf_codec_set_capability(GF_Codec *codec, GF_CodecCapability cap) +{ + if (!codec->decio) return GF_OK; + return codec->decio->SetCapabilities(codec->decio, cap); +} + + +void gf_codec_set_status(GF_Codec *codec, u32 Status) +{ + if (!codec) return; + + if (Status == GF_ESM_CODEC_PAUSE) codec->Status = GF_ESM_CODEC_STOP; + else if (Status == GF_ESM_CODEC_BUFFER) codec->Status = GF_ESM_CODEC_PLAY; + else if (Status == GF_ESM_CODEC_PLAY) { + codec->last_unit_cts = 0; + codec->prev_au_size = 0; + codec->Status = Status; + codec->last_stat_start = codec->cur_bit_size = codec->max_bit_rate = codec->avg_bit_rate = 0; + codec->nb_dec_frames = codec->total_dec_time = codec->max_dec_time = 0; + codec->cur_audio_bytes = codec->cur_video_frames = 0; + codec->nb_droped = 0; + } + else codec->Status = Status; + + if (!codec->CB) return; + + /*notify CB*/ + switch (Status) { + case GF_ESM_CODEC_PLAY: + gf_cm_set_status(codec->CB, CB_PLAY); + return; + case GF_ESM_CODEC_PAUSE: + gf_cm_set_status(codec->CB, CB_PAUSE); + return; + case GF_ESM_CODEC_STOP: + gf_cm_set_status(codec->CB, CB_STOP); + return; + case GF_ESM_CODEC_EOS: + /*do NOT notify CB yet, wait till last AU is decoded*/ + return; + case GF_ESM_CODEC_BUFFER: + default: + return; + } +} + +static GF_Err Codec_LoadModule(GF_Codec *codec, GF_ESD *esd, u32 PL) +{ + char szPrefDec[500]; + const char *sOpt; + GF_BaseDecoder *ifce; + u32 i, plugCount; + u32 ifce_type; + char *cfg; + u32 cfg_size; + GF_Terminal *term = codec->odm->term; + + if (esd->decoderConfig->decoderSpecificInfo) { + cfg = esd->decoderConfig->decoderSpecificInfo->data; + cfg_size = esd->decoderConfig->decoderSpecificInfo->dataLength; + } else { + cfg = NULL; + cfg_size = 0; + } + + ifce_type = GF_SCENE_DECODER_INTERFACE; + if ((esd->decoderConfig->streamType==GF_STREAM_AUDIO) + || (esd->decoderConfig->streamType==GF_STREAM_VISUAL) + || (esd->decoderConfig->streamType==GF_STREAM_ND_SUBPIC) + ) + ifce_type = GF_MEDIA_DECODER_INTERFACE; + + /*a bit dirty, if FFMPEG is used for demuxer load it for decoder too*/ + if (0 && !stricmp(codec->odm->net_service->ifce->module_name, "FFMPEG demuxer")) { + sOpt = "FFMPEG decoder"; + } else { + /*use user-defined module if any*/ + sOpt = NULL; + switch (esd->decoderConfig->streamType) { + case GF_STREAM_VISUAL: + if ((esd->decoderConfig->objectTypeIndication==0x6C) || (esd->decoderConfig->objectTypeIndication==0x6D)) + sOpt = gf_cfg_get_key(term->user->config, "Systems", "DefImageDec"); + else + sOpt = gf_cfg_get_key(term->user->config, "Systems", "DefVideoDec"); + break; + case GF_STREAM_AUDIO: + sOpt = gf_cfg_get_key(term->user->config, "Systems", "DefAudioDec"); + break; + default: + break; + } + } + + if (sOpt) { + ifce = (GF_BaseDecoder *) gf_modules_load_interface_by_name(term->user->modules, sOpt, ifce_type); + if (ifce) { + if (ifce->CanHandleStream && ifce->CanHandleStream(ifce, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication, cfg, cfg_size, PL) ) { + codec->decio = ifce; + return GF_OK; + } + gf_modules_close_interface((GF_BaseInterface *) ifce); + } + } + + /*prefered codec module per streamType/objectType from config*/ + sprintf(szPrefDec, "codec_%02x_%02x", esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); + sOpt = gf_cfg_get_key(term->user->config, "Systems", szPrefDec); + if (sOpt) { + ifce = (GF_BaseDecoder *) gf_modules_load_interface_by_name(term->user->modules, sOpt, ifce_type); + if (ifce) { + if (ifce->CanHandleStream && ifce->CanHandleStream(ifce, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication, cfg, cfg_size, PL) ) { + codec->decio = ifce; + return GF_OK; + } + gf_modules_close_interface((GF_BaseInterface *) ifce); + } + } + /*not found, check all modules*/ + plugCount = gf_modules_get_count(term->user->modules); + for (i = 0; i < plugCount ; i++) { + ifce = (GF_BaseDecoder *) gf_modules_load_interface(term->user->modules, i, ifce_type); + if (!ifce) continue; + if (ifce->CanHandleStream && ifce->CanHandleStream(ifce, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication, cfg, cfg_size, PL) ) { + codec->decio = ifce; + return GF_OK; + } + gf_modules_close_interface((GF_BaseInterface *) ifce); + } + return GF_CODEC_NOT_FOUND; +} + +GF_Err Codec_Load(GF_Codec *codec, GF_ESD *esd, u32 PL) +{ + switch (esd->decoderConfig->streamType) { + /*OCR has no codec, just a channel*/ + case GF_STREAM_OCR: + codec->decio = NULL; + return GF_OK; + /*InteractionStream is currently hardcoded*/ + case GF_STREAM_INTERACT: + codec->decio = (GF_BaseDecoder *) NewISCodec(PL); + assert(codec->decio->InterfaceType == GF_SCENE_DECODER_INTERFACE); + return GF_OK; + + /*load decoder module*/ + case GF_STREAM_VISUAL: + case GF_STREAM_AUDIO: + if (!esd->decoderConfig->objectTypeIndication) + return GF_NON_COMPLIANT_BITSTREAM; + default: + return Codec_LoadModule(codec, esd, PL); + } +} + + +void gf_codec_del(GF_Codec *codec) +{ + if (gf_list_count(codec->inChannels)) return; + + if (!(codec->flags & GF_ESM_CODEC_IS_USE)) { + switch (codec->type) { + /*input sensor streams are handled internally for now*/ + case GF_STREAM_INTERACT: + gf_mx_p(codec->odm->term->net_mx); + ISDec_Delete(codec->decio); + gf_list_del_item(codec->odm->term->input_streams, codec); + gf_mx_v(codec->odm->term->net_mx); + break; + default: + gf_modules_close_interface((GF_BaseInterface *) codec->decio); + break; + } + } + if (codec->CB) gf_cm_del(codec->CB); + gf_list_del(codec->inChannels); + free(codec); +} + + diff --git a/src/terminal/inline.c b/src/terminal/inline.c new file mode 100644 index 0000000..2e96602 --- /dev/null +++ b/src/terminal/inline.c @@ -0,0 +1,1719 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +/*for OD service types*/ +#include +#include +#include "media_control.h" +#include +#include + +/*SVG properties*/ +#ifndef GPAC_DISABLE_SVG +#include +#endif + +/*extern proto fetcher*/ +typedef struct +{ + MFURL *url; + GF_MediaObject *mo; +} ProtoLink; + +GF_EXPORT +Double gf_inline_get_time(void *_is) +{ + GF_InlineScene *is = (GF_InlineScene *)_is; +#if 0 + u32 ret; + GF_Clock *ck; + assert(is); + ck = is->scene_codec ? is->scene_codec->ck : is->dyn_ck; + if (!ck) return 0.0; + ret = gf_clock_time(ck); + if (is->root_od->media_stop_time && (is->root_od->media_stop_timeroot_od->media_stop_time; + return ret/1000.0; +#else + return is->simulation_time; +#endif +} + +void gf_inline_sample_time(GF_InlineScene *is) +{ + u32 ret; + GF_Clock *ck; + ck = is->scene_codec ? is->scene_codec->ck : is->dyn_ck; + if (!ck) + is->simulation_time = 0; + else { + ret = gf_clock_time(ck); + if (is->root_od->media_stop_time && (is->root_od->media_stop_timeroot_od->media_stop_time; + is->simulation_time = ((Double) ret) / 1000.0; + } +} + +GF_InlineScene *gf_inline_new(GF_InlineScene *parentScene) +{ + GF_InlineScene *tmp; + GF_SAFEALLOC(tmp, GF_InlineScene); + if (! tmp) return NULL; + + tmp->ODlist = gf_list_new(); + tmp->media_objects = gf_list_new(); + tmp->extern_protos = gf_list_new(); + tmp->inline_nodes = gf_list_new(); + tmp->extra_scenes = gf_list_new(); + /*init inline scene*/ + if (parentScene) { + tmp->graph = gf_sg_new_subscene(parentScene->graph); + } else { + tmp->graph = gf_sg_new(); + } + + gf_sg_set_private(tmp->graph, tmp); + gf_sg_set_node_callback(tmp->graph, gf_term_node_callback); + gf_sg_set_scene_time_callback(tmp->graph, gf_inline_get_time); + + gf_sg_set_proto_loader(tmp->graph, gf_inline_get_proto_lib); + return tmp; +} + +void gf_inline_del(GF_InlineScene *is) +{ + gf_list_del(is->ODlist); + gf_list_del(is->inline_nodes); + assert(!gf_list_count(is->extra_scenes) ); + gf_list_del(is->extra_scenes); + + while (gf_list_count(is->extern_protos)) { + ProtoLink *pl = (ProtoLink *)gf_list_get(is->extern_protos, 0); + gf_list_rem(is->extern_protos, 0); + free(pl); + } + gf_list_del(is->extern_protos); + + /*delete scene decoder */ + if (is->scene_codec) { + GF_SceneDecoder *dec = (GF_SceneDecoder *)is->scene_codec->decio; + /*make sure the scene codec doesn't have anything left in the scene graph*/ + if (dec->ReleaseScene) dec->ReleaseScene(dec); + + gf_term_remove_codec(is->root_od->term, is->scene_codec); + gf_codec_del(is->scene_codec); + /*reset pointer to NULL in case nodes try to access scene time*/ + is->scene_codec = NULL; + } + + /*delete the scene graph*/ + gf_sg_del(is->graph); + + if (is->od_codec) { + gf_term_remove_codec(is->root_od->term, is->od_codec); + gf_codec_del(is->od_codec); + is->od_codec = NULL; + } + /*don't touch the root_od, will be deleted by the parent scene*/ + + /*clean all remaining associations*/ + while (gf_list_count(is->media_objects)) { + GF_MediaObject *obj = (GF_MediaObject *)gf_list_get(is->media_objects, 0); + if (obj->odm) obj->odm->mo = NULL; + gf_list_rem(is->media_objects, 0); + gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); + gf_list_del(obj->nodes); + free(obj); + } + gf_list_del(is->media_objects); + + if (is->audio_url.url) free(is->audio_url.url); + if (is->visual_url.url) free(is->visual_url.url); + if (is->text_url.url) free(is->text_url.url); + if (is->fragment_uri) free(is->fragment_uri); + if (is->redirect_xml_base) free(is->redirect_xml_base); + free(is); +} + +GF_EXPORT +GF_ObjectManager *gf_inline_find_odm(GF_InlineScene *is, u16 OD_ID) +{ + GF_ObjectManager *odm; + u32 i=0; + while ((odm = (GF_ObjectManager *)gf_list_enum(is->ODlist, &i))) { + if (odm->OD && odm->OD->objectDescriptorID == OD_ID) return odm; + } + return NULL; +} + +GF_EXPORT +void gf_inline_disconnect(GF_InlineScene *is, Bool for_shutdown) +{ + u32 i; + GF_MediaObject *obj; + GF_Node *root_node; + GF_ObjectManager *odm; + GF_SceneDecoder *dec = NULL; + if (is->scene_codec) dec = (GF_SceneDecoder *)is->scene_codec->decio; + + gf_term_lock_compositor(is->root_od->term, 1); + + /*disconnect / kill all objects BEFORE reseting the scene graph since we have + potentially registered Inline nodes of the graph with the sub-scene*/ + if (!for_shutdown && is->static_media_ressources) { + i=0; + /*stop all objects but DON'T DESTROY THEM*/ + while ((odm = (GF_ObjectManager *)gf_list_enum(is->ODlist, &i))) { + if (odm->state) gf_odm_disconnect(odm, 0); + } + /*reset all stream associations*/ + i=0; + while ((obj = (GF_MediaObject*)gf_list_enum(is->media_objects, &i))) { + gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); + gf_list_reset(obj->nodes); + } + } else { + while (gf_list_count(is->ODlist)) { + odm = (GF_ObjectManager *)gf_list_get(is->ODlist, 0); + gf_odm_disconnect(odm, (for_shutdown || !is->static_media_ressources) ? 1 : 0); + } + } + + root_node = gf_sg_get_root_node(is->graph); + /*reset private stack of all inline nodes still registered*/ + while (gf_list_count(is->inline_nodes)) { + GF_Node *n = (GF_Node *)gf_list_get(is->inline_nodes, 0); + gf_list_rem(is->inline_nodes, 0); + switch (gf_node_get_tag(n)) { + case TAG_MPEG4_Inline: + case TAG_X3D_Inline: + gf_node_set_private(n, NULL); + break; + } + } + + /*remove all associated eventTargets - THIS ENEDS CLEANUP*/ + i=0; + while ((obj = (GF_MediaObject *)gf_list_enum(is->ODlist, &i))) { + if (obj->nodes) gf_list_reset(obj->nodes); + } + + if (is->graph_attached && (is->root_od->term->root_scene == is)) { + gf_sc_set_scene(is->root_od->term->compositor, NULL); + } + /*release the scene*/ + if (dec && dec->ReleaseScene) dec->ReleaseScene(dec); + gf_sg_reset(is->graph); + is->graph_attached = 0; + + gf_term_lock_compositor(is->root_od->term, 0); + + + assert(!gf_list_count(is->extra_scenes) ); + /*reset statc ressource flag since we destroyed scene objects*/ + is->static_media_ressources = 0; + + /*remove stream associations*/ + while (gf_list_count(is->media_objects)) { + obj = (GF_MediaObject*)gf_list_get(is->media_objects, 0); + gf_list_rem(is->media_objects, 0); + if (obj->odm) obj->odm->mo = NULL; + gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); + gf_list_del(obj->nodes); + free(obj); + } +} + +static void IS_InsertObject(GF_InlineScene *is, GF_MediaObject *mo, Bool lock_timelines, GF_MediaObject *sync_ref, Bool keep_fragment) +{ + GF_ObjectManager *root_od; + GF_ObjectManager *odm; + char *url; + if (!mo || !is) return; + + odm = gf_odm_new(); + /*remember OD*/ + odm->mo = mo; + mo->odm = odm; + odm->parentscene = is; + odm->OD = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + odm->OD->objectDescriptorID = GF_ESM_DYNAMIC_OD_ID; + odm->parentscene = is; + odm->term = is->root_od->term; + root_od = is->root_od; + + url = mo->URLs.vals[0].url; + if (!stricmp(url, "KeySensor")) { + GF_ESD *esd = gf_odf_desc_esd_new(0); + esd->decoderConfig->streamType = GF_STREAM_INTERACT; + esd->decoderConfig->objectTypeIndication = 1; + free(esd->decoderConfig->decoderSpecificInfo->data); + esd->decoderConfig->decoderSpecificInfo->data = strdup(" KeySensor"); + esd->decoderConfig->decoderSpecificInfo->data[0] = 9; + esd->decoderConfig->decoderSpecificInfo->dataLength = 10; + esd->ESID = esd->OCRESID = 65534; + gf_list_add(odm->OD->ESDescriptors, esd); + } else if (!stricmp(url, "StringSensor")) { + GF_ESD *esd = gf_odf_desc_esd_new(0); + esd->decoderConfig->streamType = GF_STREAM_INTERACT; + esd->decoderConfig->objectTypeIndication = 1; + free(esd->decoderConfig->decoderSpecificInfo->data); + esd->decoderConfig->decoderSpecificInfo->data = strdup(" StringSensor"); + esd->decoderConfig->decoderSpecificInfo->data[0] = 12; + esd->decoderConfig->decoderSpecificInfo->dataLength = 13; + esd->ESID = esd->OCRESID = 65534; + gf_list_add(odm->OD->ESDescriptors, esd); + } else if (!stricmp(url, "Mouse")) { + GF_ESD *esd = gf_odf_desc_esd_new(0); + esd->decoderConfig->streamType = GF_STREAM_INTERACT; + esd->decoderConfig->objectTypeIndication = 1; + free(esd->decoderConfig->decoderSpecificInfo->data); + esd->decoderConfig->decoderSpecificInfo->data = strdup(" Mouse"); + esd->decoderConfig->decoderSpecificInfo->data[0] = 5; + esd->decoderConfig->decoderSpecificInfo->dataLength = 6; + esd->ESID = esd->OCRESID = 65534; + gf_list_add(odm->OD->ESDescriptors, esd); + } else { + if (!keep_fragment) { + char *frag = strrchr(mo->URLs.vals[0].url, '#'); + if (frag) frag[0] = 0; + odm->OD->URLString = strdup(mo->URLs.vals[0].url); + if (frag) frag[0] = '#'; + } else { + odm->OD->URLString = strdup(mo->URLs.vals[0].url); + } + if (lock_timelines) odm->flags |= GF_ODM_INHERIT_TIMELINE; + } + + /*HACK - temp storage of sync ref*/ + if (sync_ref) odm->ocr_codec = (struct _generic_codec *)sync_ref; + + gf_list_add(is->ODlist, odm); + gf_odm_setup_object(odm, root_od->net_service); +} + +static void IS_ReinsertObject(GF_InlineScene *is, GF_MediaObject *mo) +{ + u32 i; + free(mo->URLs.vals[0].url); + mo->URLs.vals[0].url = NULL; + for (i=0; iURLs.count-1; i++) mo->URLs.vals[i].url = mo->URLs.vals[i+1].url; + mo->URLs.vals[mo->URLs.count-1].url = NULL; + mo->URLs.count-=1; + /*FIXME - we should re-ananlyse whether the fragment is important or not ...*/ + IS_InsertObject(is, mo, 0, NULL, 0); +} + + +void gf_inline_remove_object(GF_InlineScene *is, GF_ObjectManager *odm, Bool for_shutdown) +{ + u32 i; + GF_MediaObject *obj; + + gf_list_del_item(is->ODlist, odm); + + + i=0; + while ((obj = (GF_MediaObject*)gf_list_enum(is->media_objects, &i))) { + if ( + /*assigned object*/ + (obj->odm==odm) || + /*remote OD*/ + ((obj->OD_ID!=GF_ESM_DYNAMIC_OD_ID) && odm->OD && (obj->OD_ID == odm->OD->objectDescriptorID) ) || + /*dynamic OD*/ + (obj->URLs.count && odm->OD && odm->OD->URLString && !stricmp(obj->URLs.vals[0].url, odm->OD->URLString)) + ) { + gf_odm_lock(odm, 1); + obj->flags = 0; + if (obj->odm) obj->odm->mo = NULL; + odm->mo = NULL; + obj->odm = NULL; + + obj->frame = NULL; + obj->framesize = obj->timestamp = 0; + + gf_odm_lock(odm, 0); + + /*if graph not attached we can remove the link (this is likely scene shutdown for some error)*/ + if (!is->graph_attached) { + ProtoLink *pl; + u32 j=0; + while ((pl = (ProtoLink *)gf_list_enum(is->extern_protos, &j))) { + if (pl->mo==obj) { + pl->mo = NULL; + break; + } + } + gf_list_rem(is->media_objects, i-1); + gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); + gf_list_del(obj->nodes); + free(obj); + } else if (!for_shutdown) { + /*if dynamic OD and more than 1 URLs resetup*/ + if ((obj->OD_ID==GF_ESM_DYNAMIC_OD_ID) && (obj->URLs.count>1)) IS_ReinsertObject(is, obj); + } + return; + } + } +} + +u32 URL_GetODID(MFURL *url) +{ + u32 i, j, tmpid; + char *str, *s_url; + u32 id = 0; + + if (!url) return 0; + + for (i=0; icount; i++) { + if (url->vals[i].OD_ID) { + /*works because OD ID 0 is forbidden in MPEG4*/ + if (!id) { + id = url->vals[i].OD_ID; + } + /*bad url, only one object can be described in MPEG4 urls*/ + else if (id != url->vals[i].OD_ID) return 0; + } else if (url->vals[i].url && strlen(url->vals[i].url)) { + /*format: od:ID or od:ID#segment - also check for "ID" in case...*/ + str = url->vals[i].url; + if (!strnicmp(str, "od:", 3)) str += 3; + /*remove segment info*/ + s_url = strdup(str); + j = 0; + while (jroot_od->channels, &j))) { + /*count only re-buffering channels*/ + if (!ch->BufferOn) continue; + + max_buffer += ch->MaxBuffer; + cur_buffer += (ch->BufferTime>0) ? ch->BufferTime : 1; + } + + /*get buffering on all ODs*/ + i=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &i))) { + if (!odm->codec) continue; + j=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &j))) { + /*count only re-buffering channels*/ + if (!ch->BufferOn) continue; + + max_buffer += ch->MaxBuffer; + cur_buffer += (ch->BufferTime>0) ? ch->BufferTime : 1; + } + } + + evt.type = GF_EVENT_PROGRESS; + evt.progress.progress_type = 0; + evt.progress.service = is->root_od->net_service->url; + if (!max_buffer || !cur_buffer || (max_buffer <= cur_buffer)) { + evt.progress.done = evt.progress.total = max_buffer; + } else { + evt.progress.done = cur_buffer; + evt.progress.total = max_buffer; + } + GF_USER_SENDEVENT(is->root_od->term->user, &evt); +} + + +static Bool Inline_SetScene(M_Inline *root) +{ + GF_MediaObject *mo; + GF_InlineScene *parent; + GF_SceneGraph *graph = gf_node_get_graph((GF_Node *) root); + parent = (GF_InlineScene *)gf_sg_get_private(graph); + if (!parent) return 0; + + mo = gf_inline_get_media_object_ex(parent, &root->url, GF_MEDIA_OBJECT_SCENE, 0, NULL, 0, (GF_Node*)root); + if (!mo || !mo->odm) return 0; + + if (!mo->odm->subscene) { + gf_term_invalidate_compositor(parent->root_od->term); + return 0; + } + /*assign inline scene as private stack of inline node, and remember inline node for event propagation*/ + gf_node_set_private((GF_Node *)root, mo->odm->subscene); + gf_list_add(mo->odm->subscene->inline_nodes, root); + /*play*/ + gf_mo_play(mo, 0, -1, 0); + return 1; +} + +static Bool gf_mo_is_same_url_ex(GF_MediaObject *obj, MFURL *an_url, Bool *keep_fragment, u32 obj_hint_type) +{ + Bool include_sub_url = 0; + u32 i; + char szURL1[GF_MAX_PATH], szURL2[GF_MAX_PATH], *ext; + + if (keep_fragment) *keep_fragment = 0; + if (obj->OD_ID==GF_ESM_DYNAMIC_OD_ID) { + if (!obj->URLs.count) { + if (!obj->odm) return 0; + strcpy(szURL1, obj->odm->net_service->url); + } else { + strcpy(szURL1, obj->URLs.vals[0].url); + } + } else { + if (!obj->URLs.count) return 0; + strcpy(szURL1, obj->URLs.vals[0].url); + } + + /*don't analyse audio/video to locate segments or viewports*/ + if (obj->type==GF_MEDIA_OBJECT_AUDIO) + include_sub_url = 1; + else if (obj->type==GF_MEDIA_OBJECT_VIDEO) + include_sub_url = 1; + else if ((obj->type==GF_MEDIA_OBJECT_SCENE) && keep_fragment && obj->odm) { + GF_ClientService *ns; + u32 j; + /*for remoteODs/dynamic ODs, check if one of the running service cannot be used*/ + for (i=0; icount; i++) { + char *frag = strrchr(an_url->vals[i].url, '#'); + j=0; + /*this is the same object (may need some refinement)*/ + if (!stricmp(szURL1, an_url->vals[i].url)) return 1; + + /*fragment is a media segment, same URL*/ + if (frag ) { + Bool same_res = 0; + frag[0] = 0; + same_res = !strncmp(an_url->vals[i].url, szURL1, strlen(an_url->vals[i].url)) ? 1 : 0; + frag[0] = '#'; + + /*if we're talking about the same resource, check if the fragment can be matched*/ + if (same_res) { + /*if the expected type is a segment (undefined media type) + and the fragment is a media segment, same URL + */ + if (obj->odm->subscene && (gf_sg_find_node_by_name(obj->odm->subscene->graph, frag+1)!=NULL) ) + return 1; + + /*if the expected type is a segment (undefined media type) + and the fragment is a media segment, same URL + */ + if (!obj_hint_type && gf_odm_find_segment(obj->odm, frag+1)) + return 1; + } + } + + while ( (ns = (GF_ClientService*)gf_list_enum(obj->odm->term->net_services, &j)) ) { + /*sub-service of an existing service - don't touch any fragment*/ + if (gf_term_service_can_handle_url(ns, an_url->vals[i].url)) { + *keep_fragment = 1; + return 0; + } + } + } + } + + /*check on full URL without removing fragment IDs*/ + if (include_sub_url) { + for (i=0; icount; i++) { + if (!stricmp(szURL1, an_url->vals[i].url)) return 1; + } + return 0; + } + ext = strrchr(szURL1, '#'); + if (ext) ext[0] = 0; + for (i=0; icount; i++) { + if (!an_url->vals[i].url) return 0; + strcpy(szURL2, an_url->vals[i].url); + ext = strrchr(szURL2, '#'); + if (ext) ext[0] = 0; + if (!stricmp(szURL1, szURL2)) return 1; + } + return 0; +} + +Bool gf_mo_is_same_url(GF_MediaObject *obj, MFURL *an_url) +{ + return gf_mo_is_same_url_ex(obj, an_url, NULL, 0); +} + +void gf_inline_on_modified(GF_Node *node) +{ + u32 ODID; + GF_MediaObject *mo; + M_Inline *pInline = (M_Inline *) node; + GF_InlineScene *pIS = (GF_InlineScene *)gf_node_get_private(node); + + ODID = URL_GetODID(&pInline->url); + if (pIS) { + mo = (pIS->root_od) ? pIS->root_od->mo : NULL; + + /*disconnect current inline if we're the last one using it (same as regular OD session leave/join)*/ + if (mo) { + Bool changed = 1; + if (ODID != GF_ESM_DYNAMIC_OD_ID) { + if (ODID && (ODID==pIS->root_od->OD->objectDescriptorID)) changed = 0; + } else { + if (gf_mo_is_same_url(mo, &pInline->url) ) changed = 0; + } + if (mo->num_open) { + if (!changed) return; + mo->num_open --; + if (!mo->num_open) { + gf_odm_stop(pIS->root_od, 1); + gf_inline_disconnect(pIS, 1); + assert(gf_list_count(pIS->ODlist) == 0); + } + } + } + } + if (ODID) Inline_SetScene(pInline); +} + + +static void IS_CheckMediaRestart(GF_InlineScene *is) +{ + /*no ctrl if no duration*/ + if (!is->duration) return; + if (!is->needs_restart) gf_odm_check_segment_switch(is->root_od); + if (is->needs_restart) return; + + if (is->root_od->media_ctrl && is->root_od->media_ctrl->control->loop) { + GF_Clock *ck = gf_odm_get_media_clock(is->root_od); + if (ck->has_seen_eos) { + u32 now = gf_clock_time(ck); + u64 dur = is->duration; + if (is->root_od->media_ctrl->current_seg) { + /*only process when all segments are played*/ + if (gf_list_count(is->root_od->media_ctrl->seg) <= is->root_od->media_ctrl->current_seg) { + is->needs_restart = 1; + is->root_od->media_ctrl->current_seg = 0; + } + } + else { + Double s, e; + s = now; s/=1000; + e = -1; + MC_GetRange(is->root_od->media_ctrl, &s, &e); + if ((e>=0) && (eneeds_restart = 1; + is->root_od->media_ctrl->current_seg = 0; + } + } + } else { + /*trigger render until to watch for restart...*/ + gf_term_invalidate_compositor(is->root_od->term); + } + } +} + +static void gf_inline_traverse(GF_Node *n, void *rs, Bool is_destroy) +{ + GF_InlineScene *is = (GF_InlineScene *)gf_node_get_private(n); + + if (is_destroy) { + GF_MediaObject *mo = (is && is->root_od) ? is->root_od->mo : NULL; + + if (is) gf_list_del_item(is->inline_nodes, n); + + if (!mo) return; + /*disconnect current inline if we're the last one using it (same as regular OD session leave/join)*/ + if (mo->num_open) { + mo->num_open --; + if (!mo->num_open) { + /*this is unspecified in the spec: whenever an inline not using the + OD framework is destroyed, destroy the associated resource*/ + if (mo->OD_ID == GF_ESM_DYNAMIC_OD_ID) { + gf_odm_disconnect(is->root_od, 1); + + /*get parent scene and remove MediaObject in case the ressource + gets re-requested later on*/ + is = (GF_InlineScene *)gf_sg_get_private(gf_node_get_graph((GF_Node *) n) ); + gf_list_del_item(is->media_objects, mo); + gf_sg_vrml_mf_reset(&mo->URLs, GF_SG_VRML_MFURL); + gf_list_del(mo->nodes); + free(mo); + } else { + gf_odm_stop(is->root_od, 1); + gf_inline_disconnect(is, 1); + assert(gf_list_count(is->ODlist) == 0); + } + } + } + return; + } + + + //if no private scene is associated get the node parent graph, retrieve the IS and find the OD + if (!is) { + M_Inline *inl = (M_Inline *)n; + Inline_SetScene(inl); + is = (GF_InlineScene *)gf_node_get_private(n); + if (!is) { + /*just like protos, we must invalidate parent graph until attached*/ + if (inl->url.count) + gf_node_dirty_set(n, 0, 1); + return; + } + } + + IS_CheckMediaRestart(is); + + /*if we need to restart, shutdown graph and do it*/ + if (is->needs_restart) { + u32 current_seg = 0; + /*special case: scene change*/ + if (is->needs_restart==2) { + is->needs_restart = 0; + gf_inline_on_modified(n); + return; + } + + if (is->root_od->media_ctrl) current_seg = is->root_od->media_ctrl->current_seg; + is->needs_restart = 0; + + if (is->is_dynamic_scene) { + if (is->root_od->media_ctrl) is->root_od->media_ctrl->current_seg = current_seg; + gf_inline_restart_dynamic(is, 0); + } else { + /*we cannot use gf_mo_restart since it only sets the needs_restart for inline scenes. + The rational is that gf_mo_restart can be called from the parent scene (OK) or from the scene itself, in + which case shutting down the graph would crash the compositor. We therefore need two render passes to + safely restart an inline scene*/ + + /*1- stop main object from playing but don't disconnect channels*/ + gf_odm_stop(is->root_od, 1); + /*2- close all ODs inside the scene and reset the graph*/ + gf_inline_disconnect(is, 0); + if (is->root_od->media_ctrl) is->root_od->media_ctrl->current_seg = current_seg; + /*3- restart the scene*/ + gf_odm_start(is->root_od); + } + gf_node_dirty_set(n, 0, 1); + return; + } + + /*if not attached return (attaching the graph cannot be done in render since render is not called while unattached :) */ + if (!is->graph_attached) { + /*just like protos, we must invalidate parent graph until attached*/ + gf_node_dirty_set(n, 0, 1); + return; + } + /*clear dirty flags for any sub-inlines, bitmaps or protos*/ + gf_node_dirty_clear(n, 0); + + gf_sc_traverse_subscene(is->root_od->term->compositor, n, is->graph, rs); +} + + +static void gf_is_resize_event(GF_InlineScene *is) +{ + /*fire resize event*/ +#ifndef GPAC_DISABLE_SVG + u32 i, count; + u32 w, h; + GF_DOM_Event evt; + memset(&evt, 0, sizeof(GF_DOM_Event)); + w = h = 0; + gf_sg_get_scene_size_info(is->graph, &w, &h); + evt.type = GF_EVENT_RESIZE; + evt.screen_rect.width = INT2FIX(w); + evt.screen_rect.height = INT2FIX(h); + gf_dom_event_fire(gf_sg_get_root_node(is->graph), &evt); + + count=gf_list_count(is->inline_nodes); + for (i=0;iinline_nodes, i), &evt ); + } +#endif +} + +GF_EXPORT +void gf_inline_attach_to_compositor(GF_InlineScene *is) +{ + char *url; + if ((is->graph_attached==1) || (gf_sg_get_root_node(is->graph)==NULL) ) { + gf_term_invalidate_compositor(is->root_od->term); + return; + } + is->graph_attached = 1; + + /*locate fragment IRI*/ + if (!is->root_od || !is->root_od->net_service || !is->root_od->net_service->url) return; + if (is->fragment_uri) { + free(is->fragment_uri); + is->fragment_uri = NULL; + } + url = strchr(is->root_od->net_service->url, '#'); + if (url) is->fragment_uri = strdup(url+1); + + /*main display scene, setup compositor*/ + if (is->root_od->term->root_scene == is) { + gf_sc_set_scene(is->root_od->term->compositor, is->graph); + } + else { + u32 i, count=gf_list_count(is->inline_nodes); + for (i=0;iinline_nodes, i) ); + gf_term_invalidate_compositor(is->root_od->term); + + if (is->root_od->parentscene->is_dynamic_scene) { + u32 w, h; + gf_sg_get_scene_size_info(is->graph, &w, &h); + gf_sc_set_size(is->root_od->term->compositor, w, h); + } + gf_is_resize_event(is); + } +} + +static GF_MediaObject *IS_CheckExistingObject(GF_InlineScene *is, MFURL *urls, u32 type) +{ + GF_MediaObject *obj; + u32 i = 0; + while ((obj = (GF_MediaObject *)gf_list_enum(is->media_objects, &i))) { + if (type && (type != obj->type)) continue; + if ((obj->OD_ID == GF_ESM_DYNAMIC_OD_ID) && gf_mo_is_same_url(obj, urls)) return obj; + else if ((obj->OD_ID != GF_ESM_DYNAMIC_OD_ID) && (obj->OD_ID == urls->vals[0].OD_ID)) return obj; + } + return NULL; +} + +static GFINLINE Bool is_match_obj_type(u32 type, u32 hint_type) +{ + if (!hint_type) return 1; + if (type==hint_type) return 1; + /*TEXT are used by animation stream*/ + if ((type==GF_MEDIA_OBJECT_TEXT) && (hint_type==GF_MEDIA_OBJECT_UPDATES)) return 1; + return 0; +} + +GF_MediaObject *gf_inline_get_media_object_ex(GF_InlineScene *is, MFURL *url, u32 obj_type_hint, Bool lock_timelines, GF_MediaObject *sync_ref, Bool always_load_new, GF_Node *node) +{ + GF_MediaObject *obj; + Bool keep_fragment = 1; + u32 i, OD_ID; + + OD_ID = URL_GetODID(url); + if (!OD_ID) return NULL; + + if (!always_load_new) { + obj = NULL; + i=0; + while ((obj = (GF_MediaObject *)gf_list_enum(is->media_objects, &i))) { + if ( + /*regular OD scheme*/ + (OD_ID != GF_ESM_DYNAMIC_OD_ID && (obj->OD_ID==OD_ID)) + || + /*dynamic OD scheme*/ + ((OD_ID == GF_ESM_DYNAMIC_OD_ID) && (obj->OD_ID==GF_ESM_DYNAMIC_OD_ID) + /*if object type unknown (media control, media sensor), return first obj matching URL + otherwise check types*/ + && is_match_obj_type(obj->type, obj_type_hint) + /*locate sub-url in given one and handle fragments (viewpoint/segments/...)*/ + && gf_mo_is_same_url_ex(obj, url, &keep_fragment, obj_type_hint) + ) + ) { + + if (node && (gf_list_find(obj->nodes, node)<0)) + gf_list_add(obj->nodes, node); + return obj; + } + } + } + /*we cannot create an OD manager at this point*/ + if (obj_type_hint==GF_MEDIA_OBJECT_UNDEF) return NULL; + + /*create a new object identification*/ + obj = gf_mo_new(); + obj->OD_ID = OD_ID; + obj->type = obj_type_hint; + + /*register node with object*/ + if (node) + gf_list_add(obj->nodes, node); + + /*if animation stream object, remember originating node + !! FIXME - this should be cleaned up !! + */ + if (obj->type == GF_MEDIA_OBJECT_UPDATES) + obj->node_ptr = node; + + gf_list_add(is->media_objects, obj); + if (OD_ID == GF_ESM_DYNAMIC_OD_ID) { + gf_sg_vrml_field_copy(&obj->URLs, url, GF_SG_VRML_MFURL); + IS_InsertObject(is, obj, lock_timelines, sync_ref, keep_fragment); + /*safety check!!!*/ + if (gf_list_find(is->media_objects, obj)<0) + return NULL; + } + return obj; +} + +GF_MediaObject *gf_inline_get_media_object(GF_InlineScene *is, MFURL *url, u32 obj_type_hint, Bool lock_timelines) +{ + return gf_inline_get_media_object_ex(is, url, obj_type_hint, lock_timelines, NULL, 0, NULL); +} + +GF_EXPORT +void gf_inline_setup_object(GF_InlineScene *is, GF_ObjectManager *odm) +{ + GF_MediaObject *obj; + u32 i; + + /*an object may already be assigned (when using ESD URLs, setup is performed twice)*/ + if (odm->mo != NULL) goto existing; + + i=0; + while ((obj = (GF_MediaObject*)gf_list_enum(is->media_objects, &i))) { + if (obj->OD_ID==GF_ESM_DYNAMIC_OD_ID) { + //assert(obj->odm); + if (obj->odm == odm) { + /*assign FINAL OD, not parent*/ + obj->odm = odm; + odm->mo = obj; + goto existing; + } + } + else if (obj->OD_ID == odm->OD->objectDescriptorID) { + assert(obj->odm==NULL); + obj->odm = odm; + odm->mo = obj; + goto existing; + } + } + /*newly created OD*/ + odm->mo = gf_mo_new(); + gf_list_add(is->media_objects, odm->mo); + odm->mo->odm = odm; + odm->mo->OD_ID = odm->OD->objectDescriptorID; + +existing: + /*setup object type*/ + if (!odm->codec) odm->mo->type = GF_MEDIA_OBJECT_SCENE; + else if (odm->codec->type == GF_STREAM_VISUAL) odm->mo->type = GF_MEDIA_OBJECT_VIDEO; + else if (odm->codec->type == GF_STREAM_AUDIO) odm->mo->type = GF_MEDIA_OBJECT_AUDIO; + else if (odm->codec->type == GF_STREAM_TEXT) odm->mo->type = GF_MEDIA_OBJECT_TEXT; + else if (odm->codec->type == GF_STREAM_SCENE) odm->mo->type = GF_MEDIA_OBJECT_UPDATES; + + /*update info*/ + gf_mo_update_caps(odm->mo); + /*media object playback has already been requested by the scene, trigger media start*/ + if (odm->mo->num_open && !odm->state) { + gf_odm_start(odm); + if (odm->mo->speed != FIX_ONE) gf_odm_set_speed(odm, odm->mo->speed); + } + if ((odm->mo->type==GF_MEDIA_OBJECT_VIDEO) && is->is_dynamic_scene) { + gf_inline_force_scene_size_video(is, odm->mo); + } + /*invalidate scene for all nodes using the OD*/ + gf_term_invalidate_compositor(odm->term); +} + +void gf_inline_restart(GF_InlineScene *is) +{ + is->needs_restart = 1; + gf_term_invalidate_compositor(is->root_od->term); +} + + +GF_EXPORT +void gf_inline_set_duration(GF_InlineScene *is) +{ + Double dur; + u32 i; + u64 max_dur; + GF_ObjectManager *odm; + MediaSensorStack *media_sens; + GF_Clock *ck; + + /*this is not normative but works in so many cases... set the duration to the max duration + of all streams sharing the clock*/ + ck = gf_odm_get_media_clock(is->root_od); + max_dur = is->root_od->duration; + i=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &i))) { + if (!odm->codec) continue; + if (!ck || gf_odm_shares_clock(odm, ck)) { + if (odm->duration>max_dur) max_dur = odm->duration; + } + } + if (is->duration == max_dur) return; + + is->duration = max_dur; + dur = (Double) (s64) is->duration; + dur /= 1000; + + i=0; + while ((media_sens = (MediaSensorStack*)gf_list_enum(is->root_od->ms_stack, &i))) { + if (media_sens->sensor->isActive) { + media_sens->sensor->mediaDuration = dur; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "mediaDuration"); + } + } + + if ((is == is->root_od->term->root_scene) && is->root_od->term->user->EventProc) { + GF_Event evt; + evt.type = GF_EVENT_DURATION; + evt.duration.duration = dur; + evt.duration.can_seek = !(is->root_od->flags & GF_ODM_NO_TIME_CTRL); + if (dur<2.0) evt.duration.can_seek = 0; + GF_USER_SENDEVENT(is->root_od->term->user,&evt); + } + +} + + +static Bool IS_IsHardcodedProto(MFURL *url, GF_Config *cfg) +{ + u32 i; + const char *sOpt = gf_cfg_get_key(cfg, "Systems", "hardcoded_protos"); + for (i=0; icount; i++) { + if (!url->vals[i].url) continue; + if (strstr(url->vals[i].url, "urn:inet:gpac:builtin")) return 1; + if (sOpt && strstr(sOpt, url->vals[i].url)) return 1; + } + return 0; +} + +void IS_LoadExternProto(GF_InlineScene *is, MFURL *url) +{ + u32 i; + ProtoLink *pl; + if (!url || !url->count) return; + + /*internal, don't waste ressources*/ + if (IS_IsHardcodedProto(url, is->root_od->term->user->config)) return; + + i=0; + while ((pl = (ProtoLink*)gf_list_enum(is->extern_protos, &i)) ) { + if (pl->url == url) return; + if (pl->url->vals[0].OD_ID && (pl->url->vals[0].OD_ID == url->vals[0].OD_ID)) return; + if (pl->url->vals[0].url && url->vals[0].url && !stricmp(pl->url->vals[0].url, url->vals[0].url) ) return; + } + pl = (ProtoLink*)malloc(sizeof(ProtoLink)); + pl->url = url; + gf_list_add(is->extern_protos, pl); + pl->mo = gf_inline_get_media_object(is, url, GF_MEDIA_OBJECT_SCENE, 0); + /*this may already be destroyed*/ + if (pl->mo) gf_mo_play(pl->mo, 0, -1, 0); +} + +GF_EXPORT +GF_SceneGraph *gf_inline_get_proto_lib(void *_is, MFURL *lib_url) +{ + ProtoLink *pl; + u32 i; + GF_InlineScene *is = (GF_InlineScene *) _is; + if (!is || !lib_url->count) return NULL; + + if (IS_IsHardcodedProto(lib_url, is->root_od->term->user->config)) return GF_SG_INTERNAL_PROTO; + + i=0; + while ((pl = (ProtoLink*)gf_list_enum(is->extern_protos, &i))) { + if (!pl->mo) continue; + if (URL_GetODID(pl->url) != GF_ESM_DYNAMIC_OD_ID) { + if (URL_GetODID(pl->url) == URL_GetODID(lib_url)) { + if (!pl->mo->odm || !pl->mo->odm->subscene) return NULL; + return pl->mo->odm->subscene->graph; + } + } else if (lib_url->vals[0].url) { + if (gf_mo_is_same_url(pl->mo, lib_url)) { + if (!pl->mo->odm || !pl->mo->odm->subscene) return NULL; + return pl->mo->odm->subscene->graph; + } + } + } + + /*not found, create loader*/ + IS_LoadExternProto(is, lib_url); + + /*and return NULL*/ + return NULL; +} + +GF_ObjectManager *IS_GetProtoSceneByGraph(void *_is, GF_SceneGraph *sg) +{ + u32 i; + ProtoLink *pl; + GF_InlineScene *is = (GF_InlineScene *) _is; + if (!is) return NULL; + i=0; + while ((pl = (ProtoLink*)gf_list_enum(is->extern_protos, &i))) { + if (pl->mo->odm && pl->mo->odm->subscene && (pl->mo->odm->subscene->graph==sg)) return pl->mo->odm; + } + return NULL; +} + + +Bool IS_IsProtoLibObject(GF_InlineScene *is, GF_ObjectManager *odm) +{ + u32 i; + ProtoLink *pl; + i=0; + while ((pl = (ProtoLink*)gf_list_enum(is->extern_protos, &i))) { + if (pl->mo->odm == odm) return 1; + } + return 0; +} + + +GF_MediaObject *gf_inline_find_object(GF_InlineScene *is, u16 ODID, char *url) +{ + u32 i; + GF_MediaObject *mo; + if (!url && !ODID) return NULL; + i=0; + while ((mo = (GF_MediaObject *)gf_list_enum(is->media_objects, &i))) { + if (ODID==GF_ESM_DYNAMIC_OD_ID) { + if (mo->URLs.count && !stricmp(mo->URLs.vals[0].url, url)) return mo; + } else if (mo->OD_ID==ODID) return mo; + } + return NULL; +} + + +const char *IS_GetSceneViewName(GF_InlineScene *is) +{ + char *seg_name; + /*check any viewpoint*/ + seg_name = strrchr(is->root_od->net_service->url, '#'); + if (!seg_name) return NULL; + seg_name += 1; + /*look for a media segment with this name - if none found, this is a viewpoint name*/ + if (gf_odm_find_segment(is->root_od, seg_name) != NULL) return NULL; + return seg_name; +} + +GF_EXPORT +Bool gf_inline_default_scene_viewpoint(GF_Node *node) +{ + const char *nname, *sname; + GF_SceneGraph *sg = gf_node_get_graph(node); + GF_InlineScene *is = sg ? (GF_InlineScene *) gf_sg_get_private(sg) : NULL; + if (!is) return 0; + + nname = gf_node_get_name(node); + if (!nname) return 0; + sname = IS_GetSceneViewName(is); + if (!sname) return 0; + return (!strcmp(nname, sname)); +} + +GF_EXPORT +void gf_inline_register_extra_graph(GF_InlineScene *is, GF_SceneGraph *extra_scene, Bool do_remove) +{ + if (do_remove) { + if (gf_list_find(is->extra_scenes, extra_scene)<0) return; + gf_list_del_item(is->extra_scenes, extra_scene); + /*for root scene*/ + if (is->root_od->term->root_scene == is) { + gf_sc_register_extra_graph(is->root_od->term->compositor, extra_scene, 1); + } + } else { + if (gf_list_find(is->extra_scenes, extra_scene)>=0) return; + gf_list_add(is->extra_scenes, extra_scene); + /*for root scene*/ + if (is->root_od->term->root_scene == is) { + gf_sc_register_extra_graph(is->root_od->term->compositor, extra_scene, 0); + } + } +} + + +static void gf_inline_get_video_size(GF_MediaObject *mo, u32 *w, u32 *h) +{ + u32 pixel_ar; + if (!gf_mo_get_visual_info(mo, w, h, NULL, &pixel_ar, NULL)) return; + if (pixel_ar) { + u32 n, d; + n = (pixel_ar>>16) & 0xFF; + d = (pixel_ar) & 0xFF; + *w = (*w * n) / d; + } +} + +static void IS_UpdateVideoPos(GF_InlineScene *is) +{ + MFURL url; + M_Transform2D *tr; + GF_MediaObject *mo; + u32 w, h, v_w, v_h; + if (!is->visual_url.OD_ID && !is->visual_url.url) return; + + url.count = 1; + url.vals = &is->visual_url; + mo = IS_CheckExistingObject(is, &url, GF_MEDIA_OBJECT_VIDEO); + if (!mo) return; + tr = (M_Transform2D *) gf_sg_find_node_by_name(is->graph, "DYN_TRANS"); + if (!tr) return; + + gf_sg_get_scene_size_info(is->graph, &w, &h); + if (!w || !h) return; + + gf_inline_get_video_size(mo, &v_w, &v_h); + tr->translation.x = INT2FIX((s32) (w - v_w)) / 2; + tr->translation.y = INT2FIX((s32) (h - v_h)) / 2; + gf_node_dirty_set((GF_Node *)tr, 0, 0); + + if (is->root_od->term->root_scene == is) { + //if (is->graph_attached) gf_sc_set_scene(is->root_od->term->compositor, NULL); + gf_sc_set_scene(is->root_od->term->compositor, is->graph); + } +} + +static GF_Node *is_create_node(GF_SceneGraph *sg, u32 tag, const char *def_name) +{ + GF_Node *n = gf_node_new(sg, tag); + if (n) { + if (def_name) gf_node_set_id(n, gf_sg_get_next_available_node_id(sg), def_name); + gf_node_init(n); + } + return n; +} + +static Bool is_odm_url(SFURL *url, GF_ObjectManager *odm) +{ + if (!url->OD_ID && !url->url) return 0; + if (odm->OD->objectDescriptorID != GF_ESM_DYNAMIC_OD_ID) return (url->OD_ID==odm->OD->objectDescriptorID) ? 1 : 0; + if (!url->url || !odm->OD->URLString) return 0; + return !stricmp(url->url, odm->OD->URLString); +} + +void gf_inline_force_scene_size_video(GF_InlineScene *is, GF_MediaObject *mo) +{ + u32 w, h; + gf_inline_get_video_size(mo, &w, &h); + gf_inline_force_scene_size(is, w, h); +} + + +/*regenerates the scene graph for dynamic scene. +This will also try to reload any previously presented streams. Note that in the usual case the scene is generated +just once when receiving the first OD AU (ressources are NOT destroyed when seeking), but since the network may need +to update the OD ressources, we still kake care of it*/ +void gf_inline_regenerate(GF_InlineScene *is) +{ + u32 i, nb_obj, w, h; + GF_Node *n1, *n2; + SFURL *sfu; + GF_Event evt; + GF_ObjectManager *first_odm, *odm; + M_AudioClip *ac; + M_MovieTexture *mt; + M_AnimationStream *as; + M_Inline *dims; + + if (!is->is_dynamic_scene) return; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Inline] Regenerating scene graph for service %s\n", is->root_od->net_service->url)); + + gf_sc_lock(is->root_od->term->compositor, 1); + + if (is->root_od->term->root_scene == is) + gf_sc_set_scene(is->root_od->term->compositor, NULL); + + gf_sg_reset(is->graph); + gf_sg_get_scene_size_info(is->graph, &w, &h); + gf_sg_set_scene_size_info(is->graph, w, h, 1); + + n1 = is_create_node(is->graph, TAG_MPEG4_OrderedGroup, NULL); + gf_sg_set_root_node(is->graph, n1); + gf_node_register(n1, NULL); + + n2 = is_create_node(is->graph, TAG_MPEG4_Sound2D, NULL); + gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); + gf_node_register(n2, n1); + + ac = (M_AudioClip *) is_create_node(is->graph, TAG_MPEG4_AudioClip, "DYN_AUDIO"); + ac->startTime = gf_inline_get_time(is); + ((M_Sound2D *)n2)->source = (GF_Node *)ac; + gf_node_register((GF_Node *)ac, n2); + + nb_obj = 0; + first_odm = NULL; + i=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &i))) { + if (!odm->codec || (odm->codec->type!=GF_STREAM_AUDIO)) continue; + + if (is_odm_url(&is->audio_url, odm)) { + gf_sg_vrml_mf_append(&ac->url, GF_SG_VRML_MFURL, (void **) &sfu); + sfu->OD_ID = is->audio_url.OD_ID; + if (is->audio_url.url) sfu->url = strdup(is->audio_url.url); + first_odm = NULL; + nb_obj++; + break; + } + if (!first_odm) first_odm = odm; + } + if (first_odm) { + if (is->audio_url.url) free(is->audio_url.url); + is->audio_url.url = NULL; + is->audio_url.OD_ID = first_odm->OD->objectDescriptorID; + if (is->audio_url.OD_ID==GF_ESM_DYNAMIC_OD_ID) is->audio_url.url = strdup(first_odm->net_service->url); + gf_sg_vrml_mf_append(&ac->url, GF_SG_VRML_MFURL, (void **) &sfu); + sfu->OD_ID = is->audio_url.OD_ID; + if (is->audio_url.url) sfu->url = strdup(is->audio_url.url); + nb_obj++; + + if (!is->dyn_ck) is->dyn_ck = first_odm->codec->ck; + } + + /*transform for any translation due to scene resize (3GPP)*/ + n2 = is_create_node(is->graph, TAG_MPEG4_Transform2D, "DYN_TRANS"); + gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); + gf_node_register(n2, n1); + n1 = n2; + + n2 = is_create_node(is->graph, TAG_MPEG4_Shape, NULL); + gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); + gf_node_register(n2, n1); + n1 = n2; + n2 = is_create_node(is->graph, TAG_MPEG4_Appearance, NULL); + ((M_Shape *)n1)->appearance = n2; + gf_node_register(n2, n1); + + /*note we create a movie texture even for images...*/ + mt = (M_MovieTexture *) is_create_node(is->graph, TAG_MPEG4_MovieTexture, "DYN_VIDEO"); + mt->startTime = gf_inline_get_time(is); + ((M_Appearance *)n2)->texture = (GF_Node *)mt; + gf_node_register((GF_Node *)mt, n2); + + first_odm = NULL; + i=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &i))) { + if (!odm->codec || (odm->codec->type!=GF_STREAM_VISUAL)) continue; + + if (is_odm_url(&is->visual_url, odm)) { + gf_sg_vrml_mf_append(&mt->url, GF_SG_VRML_MFURL, (void **) &sfu); + sfu->OD_ID = is->visual_url.OD_ID; + if (is->visual_url.url) sfu->url = strdup(is->visual_url.url); + if (odm->mo) { + gf_inline_get_video_size(odm->mo, &w, &h); + gf_sg_set_scene_size_info(is->graph, w, h, 1); + } + first_odm = NULL; + nb_obj++; + break; + } + if (!first_odm) + first_odm = odm; + } + if (first_odm) { + if (is->visual_url.url) free(is->visual_url.url); + is->visual_url.url = NULL; + is->visual_url.OD_ID = first_odm->OD->objectDescriptorID; + if (is->visual_url.OD_ID==GF_ESM_DYNAMIC_OD_ID) is->visual_url.url = strdup(first_odm->net_service->url); + + gf_sg_vrml_mf_append(&mt->url, GF_SG_VRML_MFURL, (void **) &sfu); + sfu->OD_ID = is->visual_url.OD_ID; + if (is->visual_url.url) sfu->url = strdup(is->visual_url.url); + + if (first_odm->mo) { + gf_inline_get_video_size(first_odm->mo, &w, &h); + gf_sg_set_scene_size_info(is->graph, w, h, 1); + } + nb_obj++; + if (!is->dyn_ck) is->dyn_ck = first_odm->codec->ck; + } + + n2 = is_create_node(is->graph, TAG_MPEG4_Bitmap, NULL); + ((M_Shape *)n1)->geometry = n2; + gf_node_register(n2, n1); + + + /*text streams controlled through AnimationStream*/ + n1 = gf_sg_get_root_node(is->graph); + as = (M_AnimationStream *) is_create_node(is->graph, TAG_MPEG4_AnimationStream, "DYN_TEXT"); + gf_node_list_add_child( &((GF_ParentNode *)n1)->children, (GF_Node*)as); + gf_node_register((GF_Node *)as, n1); + + first_odm = NULL; + i=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &i))) { + if (!odm->codec || ((odm->codec->type!=GF_STREAM_TEXT) && (odm->codec->type!=GF_STREAM_ND_SUBPIC)) ) continue; + + if (!nb_obj || is_odm_url(&is->text_url, odm)) { + if (is->text_url.url) free(is->text_url.url); + is->text_url.url = NULL; + + gf_sg_vrml_mf_append(&as->url, GF_SG_VRML_MFURL, (void **) &sfu); + sfu->OD_ID = is->text_url.OD_ID = odm->OD->objectDescriptorID; + if (odm->OD->objectDescriptorID == GF_ESM_DYNAMIC_OD_ID) { + sfu->url = strdup(odm->net_service->url); + is->text_url.url = strdup(odm->net_service->url); + } + first_odm = NULL; + if (!is->dyn_ck) is->dyn_ck = odm->codec->ck; + break; + } + if (!first_odm) first_odm = odm; + } + if (first_odm) { + if (is->text_url.url) free(is->text_url.url); + is->text_url.url = NULL; + gf_sg_vrml_mf_append(&as->url, GF_SG_VRML_MFURL, (void **) &sfu); + sfu->OD_ID = is->text_url.OD_ID = first_odm->OD->objectDescriptorID; + if (is->text_url.OD_ID==GF_ESM_DYNAMIC_OD_ID) { + is->text_url.url = strdup(first_odm->net_service->url); + sfu->url = strdup(first_odm->net_service->url); + } + if (!is->dyn_ck) is->dyn_ck = first_odm->codec->ck; + } + + + /*3GPP DIMS streams controlled */ + n1 = gf_sg_get_root_node(is->graph); + dims = (M_Inline *) is_create_node(is->graph, TAG_MPEG4_Inline, "DYN_SCENE"); + gf_node_list_add_child( &((GF_ParentNode *)n1)->children, (GF_Node*)dims); + gf_node_register((GF_Node *)dims, n1); + + i=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &i))) { + if (!odm->subscene || !odm->subscene->scene_codec) continue; + + gf_sg_vrml_mf_append(&dims->url, GF_SG_VRML_MFURL, (void **) &sfu); + sfu->OD_ID = odm->OD->objectDescriptorID; + if (odm->OD->objectDescriptorID == GF_ESM_DYNAMIC_OD_ID) { + sfu->url = strdup(odm->net_service->url); + } + if (!is->dyn_ck) is->dyn_ck = odm->subscene->scene_codec->ck; + break; + } + + gf_sc_lock(is->root_od->term->compositor, 0); + + /*disconnect to force resize*/ + if (is->root_od->term->root_scene == is) { + if (is->graph_attached) gf_sc_set_scene(is->root_od->term->compositor, NULL); + gf_sc_set_scene(is->root_od->term->compositor, is->graph); + is->graph_attached = 1; + evt.type = GF_EVENT_STREAMLIST; + GF_USER_SENDEVENT(is->root_od->term->user,&evt); + IS_UpdateVideoPos(is); + } else { + is->graph_attached = 1; + gf_term_invalidate_compositor(is->root_od->term); + } +} + +static Bool check_odm_deactivate(SFURL *url, GF_ObjectManager *odm, GF_Node *n) +{ + GF_FieldInfo info; + if (!is_odm_url(url, odm) || !n) return 0; + + if (url->url) free(url->url); + url->url = NULL; + url->OD_ID = 0; + + gf_node_get_field_by_name(n, "url", &info); + gf_sg_vrml_mf_reset(info.far_ptr, GF_SG_VRML_MFURL); + gf_node_get_field_by_name(n, "stopTime", &info); + *((SFTime *)info.far_ptr) = gf_node_get_scene_time(n); + gf_node_changed(n, NULL); + return 1; +} + +void gf_inline_select_object(GF_InlineScene *is, GF_ObjectManager *odm) +{ + char *url; + if (!is->is_dynamic_scene || !is->graph_attached || !odm) return; + + if (!odm->codec) return; + + if (odm->state) { + if (check_odm_deactivate(&is->audio_url, odm, gf_sg_find_node_by_name(is->graph, "DYN_AUDIO")) ) return; + if (check_odm_deactivate(&is->visual_url, odm, gf_sg_find_node_by_name(is->graph, "DYN_VIDEO") )) return; + if (check_odm_deactivate(&is->text_url, odm, gf_sg_find_node_by_name(is->graph, "DYN_TEXT") )) return; + } + + if (odm->codec->type == GF_STREAM_AUDIO) { + M_AudioClip *ac = (M_AudioClip *) gf_sg_find_node_by_name(is->graph, "DYN_AUDIO"); + if (!ac) return; + if (is->audio_url.url) free(is->audio_url.url); + is->audio_url.url = NULL; + is->audio_url.OD_ID = odm->OD->objectDescriptorID; + if (!ac->url.count) gf_sg_vrml_mf_alloc(&ac->url, GF_SG_VRML_MFURL, 1); + ac->url.vals[0].OD_ID = odm->OD->objectDescriptorID; + if (ac->url.vals[0].url) { + free(ac->url.vals[0].url); + ac->url.vals[0].url = NULL; + } + url = odm->mo->URLs.count ? odm->mo->URLs.vals[0].url : NULL; + if (url) { + is->audio_url.url = strdup(url); + ac->url.vals[0].url = strdup(url); + } + ac->startTime = gf_inline_get_time(is); + gf_node_changed((GF_Node *)ac, NULL); + return; + } + + if (odm->codec->type == GF_STREAM_VISUAL) { + M_MovieTexture *mt = (M_MovieTexture*) gf_sg_find_node_by_name(is->graph, "DYN_VIDEO"); + if (!mt) return; + if (is->visual_url.url) free(is->visual_url.url); + is->visual_url.url = NULL; + is->visual_url.OD_ID = odm->OD->objectDescriptorID; + if (!mt->url.count) gf_sg_vrml_mf_alloc(&mt->url, GF_SG_VRML_MFURL, 1); + mt->url.vals[0].OD_ID = odm->OD->objectDescriptorID; + if (mt->url.vals[0].url) free(mt->url.vals[0].url); + url = odm->mo->URLs.count ? odm->mo->URLs.vals[0].url : NULL; + if (url) { + is->visual_url.url = strdup(url); + mt->url.vals[0].url = strdup(url); + } + mt->startTime = gf_inline_get_time(is); + gf_node_changed((GF_Node *)mt, NULL); + if (odm->mo) gf_inline_force_scene_size_video(is, odm->mo); + return; + } + + + if (odm->codec->type == GF_STREAM_TEXT) { + M_AnimationStream *as = (M_AnimationStream*) gf_sg_find_node_by_name(is->graph, "DYN_TEXT"); + if (!as) return; + if (is->text_url.url) free(is->text_url.url); + is->text_url.url = NULL; + is->text_url.OD_ID = odm->OD->objectDescriptorID; + if (!as->url.count) gf_sg_vrml_mf_alloc(&as->url, GF_SG_VRML_MFURL, 1); + as->url.vals[0].OD_ID = odm->OD->objectDescriptorID; + if (as->url.vals[0].url) free(as->url.vals[0].url); + url = odm->mo->URLs.count ? odm->mo->URLs.vals[0].url : NULL; + if (url) { + is->text_url.url = strdup(url); + as->url.vals[0].url = strdup(url); + } + as->startTime = gf_inline_get_time(is); + gf_node_changed((GF_Node *)as, NULL); + return; + } +} + + +GF_EXPORT +void gf_inline_force_scene_size(GF_InlineScene *is, u32 width, u32 height) +{ + /*for now only allowed when no scene info*/ + if (!is->is_dynamic_scene) return; + gf_sg_set_scene_size_info(is->graph, width, height, gf_sg_use_pixel_metrics(is->graph)); + + if (is->root_od->term->root_scene == is) + gf_sc_set_scene(is->root_od->term->compositor, is->graph); + + gf_is_resize_event(is); + + IS_UpdateVideoPos(is); +} + +void gf_inline_restart_dynamic(GF_InlineScene *is, u64 from_time) +{ + u32 i; + GF_List *to_restart; + GF_ObjectManager *odm; + + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[InlineScene] Restarting from "LLD"\n", LLD_CAST from_time)); + to_restart = gf_list_new(); + i=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &i))) { + if (odm->state) { + gf_list_add(to_restart, odm); + gf_odm_stop(odm, 1); + } + } + + /*reset clock*/ + if (is->dyn_ck) gf_clock_reset(is->dyn_ck); + + /*restart objects*/ + i=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(to_restart, &i))) { + odm->media_start_time = from_time; + gf_odm_start(odm); + } + gf_list_del(to_restart); + + /*also check nodes if no media control since they may be deactivated (end of stream)*/ + if (!is->root_od->media_ctrl) { + M_AudioClip *ac = (M_AudioClip *) gf_sg_find_node_by_name(is->graph, "DYN_AUDIO"); + M_MovieTexture *mt = (M_MovieTexture *) gf_sg_find_node_by_name(is->graph, "DYN_VIDEO"); + M_AnimationStream *as = (M_AnimationStream *) gf_sg_find_node_by_name(is->graph, "DYN_TEXT"); + if (ac) { + ac->startTime = gf_inline_get_time(is); + gf_node_changed((GF_Node *)ac, NULL); + } + if (mt) { + mt->startTime = gf_inline_get_time(is); + gf_node_changed((GF_Node *)mt, NULL); + } + if (as) { + as->startTime = gf_inline_get_time(is); + gf_node_changed((GF_Node *)as, NULL); + } + } +} + + +GF_EXPORT +Bool gf_inline_process_anchor(GF_Node *caller, GF_Event *evt) +{ + u32 i; + GF_Terminal *term; + M_Inline *inl; + GF_InlineScene *is; + GF_SceneGraph *sg = gf_node_get_graph(caller); + if (!sg) return 1; + is = (GF_InlineScene *)gf_sg_get_private(sg); + if (!is) return 1; + term = is->root_od->term; + + /*if main scene forward to user. If no params or first one not "self" forward to user*/ + if ((term->root_scene==is) || !evt->navigate.parameters || !evt->navigate.param_count || (stricmp(evt->navigate.parameters[0], "self") && stricmp(evt->navigate.parameters[0], "_self"))) { + if (term->user->EventProc) return term->user->EventProc(term->user->opaque, evt); + return 1; + } + /*FIXME this is too restrictive, we assume the navigate URL is really a presentation one...*/ + i=0; + while ((inl = (M_Inline*)gf_list_enum(is->inline_nodes, &i))) { + switch (gf_node_get_tag((GF_Node *)inl)) { + case TAG_MPEG4_Inline: + case TAG_X3D_Inline: + gf_sg_vrml_mf_reset(&inl->url, GF_SG_VRML_MFURL); + gf_sg_vrml_mf_alloc(&inl->url, GF_SG_VRML_MFURL, 1); + inl->url.vals[0].url = strdup(evt->navigate.to_url ? evt->navigate.to_url : ""); + /*signal URL change but don't destroy inline scene now since we got this event from inside the scene, + this could crash compositors*/ + is->needs_restart = 2; + break; + } + } + return 1; +} + +GF_EXPORT +GF_Compositor *gf_sc_get_compositor(GF_Node *node) +{ + GF_InlineScene *is; + GF_SceneGraph *sg = gf_node_get_graph(node); + if (!sg) return NULL; + is = (GF_InlineScene *)gf_sg_get_private(sg); + if (!is) return NULL; + return is->root_od->term->compositor; +} + +const char *gf_inline_get_fragment_uri(GF_Node *node) +{ + GF_SceneGraph *sg = gf_node_get_graph(node); + GF_InlineScene *is = sg ? (GF_InlineScene *) gf_sg_get_private(sg) : NULL; + if (!is) return NULL; + return is->fragment_uri; +} +void gf_inline_set_fragment_uri(GF_Node *node, const char *uri) +{ + GF_SceneGraph *sg = gf_node_get_graph(node); + GF_InlineScene *is = sg ? (GF_InlineScene *) gf_sg_get_private(sg) : NULL; + if (!is) return; + if (is->fragment_uri) { + free(is->fragment_uri); + is->fragment_uri = NULL; + } + if (uri) is->fragment_uri = strdup(uri); +} + +GF_Node *gf_inline_get_subscene_root(GF_Node *node) +{ + GF_InlineScene *is; + if (!node) return NULL; + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_Inline: case TAG_X3D_Inline: + break; + default: + return NULL; + } + is = (GF_InlineScene *)gf_node_get_private(node); + if (!is) return NULL; + return gf_sg_get_root_node(is->graph); +} + +GF_Node *gf_inline_get_parent_node(GF_Node *node, u32 idx) +{ + GF_InlineScene *is; + if (!node) return NULL; + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_Inline: case TAG_X3D_Inline: + is = (GF_InlineScene *)gf_node_get_private(node); + break; + default: + is = (GF_InlineScene *)gf_sg_get_private(gf_node_get_graph(node)); + break; + } + if (!is) return NULL; + return (GF_Node *) gf_list_get(is->inline_nodes, idx); +} + +void InitInline(GF_InlineScene *is, GF_Node *node) +{ + gf_node_set_callback_function(node, gf_inline_traverse); +} + + diff --git a/src/terminal/input_sensor.c b/src/terminal/input_sensor.c new file mode 100644 index 0000000..7a1fc1f --- /dev/null +++ b/src/terminal/input_sensor.c @@ -0,0 +1,988 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +#include "input_sensor.h" + +#if GPAC_HTK_DEMO + +void HTK_Init(const char *htk_path); +void HTK_Close(void); +void HTK_SetDictionary(char *dicofile); +void HTK_DoDetection(void); +const char *HTK_GetWord(); +int HTK_GetWordIndex(); +float HTK_GetWordScore(); +void StartHTK(ISPriv *is_dec); + +static u32 htk_num_users = 0; +#endif + + +/* + input sensor decoder(s) handling +*/ + +GF_Err IS_Configure(GF_BaseDecoder *plug, GF_InlineScene *scene, Bool is_remote) +{ + ISPriv *is = (ISPriv *)plug->privateStack; + /*we can only deal with encoded content (for now)*/ + if (!scene->scene_codec) return GF_NOT_SUPPORTED; + is->scene = scene; + is->is_local = !is_remote; + return GF_OK; +} + + +static void add_field(ISPriv *priv, u32 fieldType, const char *fieldName) +{ + GF_FieldInfo *field = (GF_FieldInfo *) malloc(sizeof(GF_FieldInfo)); + memset(field, 0, sizeof(GF_FieldInfo)); + field->fieldType = fieldType; + field->far_ptr = gf_sg_vrml_field_pointer_new(fieldType); + field->name = (const char *) fieldName; + field->fieldIndex = gf_list_count(priv->ddf); + gf_list_add(priv->ddf, field); +} + +static GF_Err IS_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) +{ + GF_BitStream *bs; + u32 len, size, i; + char devName[255]; + u16 termSeq[20]; + + ISPriv *is = (ISPriv *)plug->privateStack; + if (esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; + if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->dataLength) return GF_NON_COMPLIANT_BITSTREAM; + + /*no more than one UI stream per object*/ + if (is->ES_ID) return GF_NOT_SUPPORTED; + is->ES_ID = esd->ESID; + /*parse config*/ + bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); + len = gf_bs_read_int(bs, 8); + for (i=0; itype = 0; + if (!stricmp(devName, "KeySensor")) { + is->type = IS_KeySensor; + add_field(is, GF_SG_VRML_SFINT32, "keyPressed"); + add_field(is, GF_SG_VRML_SFINT32, "keyReleased"); + add_field(is, GF_SG_VRML_SFINT32, "actionKeyPressed"); + add_field(is, GF_SG_VRML_SFINT32, "actionKeyReleased"); + add_field(is, GF_SG_VRML_SFBOOL, "shiftKeyPressed"); + add_field(is, GF_SG_VRML_SFBOOL, "controlKeyPressed"); + add_field(is, GF_SG_VRML_SFBOOL, "altKeyPressed"); + + } else if (!stricmp(devName, "StringSensor")) { + is->type = IS_StringSensor; + add_field(is, GF_SG_VRML_SFSTRING, "enteredText"); + add_field(is, GF_SG_VRML_SFSTRING, "finalText"); + + is->termChar = '\r'; + is->delChar = '\b'; + + /*get escape chars if any specified*/ + if (sizedecoderConfig->decoderSpecificInfo->dataLength) { + const char *src = esd->decoderConfig->decoderSpecificInfo->data + size; + gf_utf8_mbstowcs(termSeq, esd->decoderConfig->decoderSpecificInfo->dataLength - size, &src); + is->termChar = termSeq[0]; + is->delChar = termSeq[1]; + } + } else if (!stricmp(devName, "Mouse")) { + is->type = IS_Mouse; + add_field(is, GF_SG_VRML_SFVEC2F, "position"); + add_field(is, GF_SG_VRML_SFBOOL, "leftButtonDown"); + add_field(is, GF_SG_VRML_SFBOOL, "middleButtonDown"); + add_field(is, GF_SG_VRML_SFBOOL, "rightButtonDown"); + add_field(is, GF_SG_VRML_SFFLOAT, "wheel"); + +#if GPAC_HTK_DEMO + } else if (!stricmp(devName, "HTKSensor")) { + FILE *f; + u32 nb_word, nbPhone, c, j; + char szPh[3]; + char szName[1024]; + char *szPath = gf_cfg_get_key(is->scene->root_od->term->user->config, "HTK", "HTKDirectory"); + if (!szPath) szPath = gf_cfg_get_key(is->scene->root_od->term->user->config, "General", "ModulesDirectory"); + strcpy(is->szHTKPath, szPath); + if (szPath[strlen(szPath)-1] != GF_PATH_SEPARATOR) is->szHTKPath[strlen(szPath)] = GF_PATH_SEPARATOR; + + add_field(is, GF_SG_VRML_SFSTRING, "word"); + add_field(is, GF_SG_VRML_SFINT32, "wordIndex"); + add_field(is, GF_SG_VRML_SFFLOAT, "wordScore"); + + if (!htk_num_users) { + HTK_Init(is->szHTKPath); + htk_num_users++; + } + + sprintf(szName, "HTKD_%d", (u32) is); + strcat(is->szHTKPath, szName); + + f = fopen(is->szHTKPath, "wt"); + szPh[2] = 0; + nb_word = gf_bs_read_int(bs, 8); + for (i=0; itype = IS_HTKSensor; + + StartHTK(is); +#endif + + } + gf_bs_del(bs); + return GF_OK; +} + +static GF_Err IS_DetachStream(GF_BaseDecoder *plug, u16 ES_ID) +{ + ISPriv *is = (ISPriv *)plug->privateStack; + is->ES_ID = 0; +#if GPAC_HTK_DEMO + if (htk_num_users) { + htk_num_users--; + if (!htk_num_users) { + while (is->htk_running) gf_sleep(10); + HTK_Close(); + } + } +#endif + return GF_OK; +} + +static GF_Err IS_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *capability) +{ + capability->cap.valueInt = 0; + return GF_OK; +} + +static GF_Err IS_SetCapabilities(GF_BaseDecoder *plug, const GF_CodecCapability capability) +{ + return GF_OK; +} + +static GF_Err IS_ProcessData(GF_SceneDecoder *plug, char *inBuffer, u32 inBufferLength, + u16 ES_ID, u32 AU_time, u32 mmlevel) +{ + u32 i, j, count; + Double scene_time; + GF_BitStream *bs; + GF_FieldInfo *field; + ISStack *st; + ISPriv *priv = (ISPriv *)plug->privateStack; + GF_Err e = GF_OK; + + /*decode data frame except if local stringSensor*/ + bs = gf_bs_new(inBuffer, inBufferLength, GF_BITSTREAM_READ); + i=0; + while ((field = (GF_FieldInfo *)gf_list_enum(priv->ddf, &i))) { + /*store present flag in eventIn for command skip - this is an ugly hack but it works since DDF don't have event types*/ + field->eventType = gf_bs_read_int(bs, 1); + /*parse val ourselves (we don't want to depend on bifs codec)*/ + if (field->eventType) { + switch (field->fieldType) { + case GF_SG_VRML_SFBOOL: * ((SFBool *) field->far_ptr) = (SFBool) gf_bs_read_int(bs, 1); break; + case GF_SG_VRML_SFFLOAT: *((SFFloat *)field->far_ptr) = FLT2FIX( gf_bs_read_float(bs) ); break; + case GF_SG_VRML_SFINT32: *((SFInt32 *)field->far_ptr) = (s32) gf_bs_read_int(bs, 32); break; + case GF_SG_VRML_SFTIME: *((SFTime *)field->far_ptr) = gf_bs_read_double(bs); break; + case GF_SG_VRML_SFVEC2F: + ((SFVec2f *)field->far_ptr)->x = FLT2FIX( gf_bs_read_float(bs) ); + ((SFVec2f *)field->far_ptr)->y = FLT2FIX( gf_bs_read_float(bs) ); + break; + case GF_SG_VRML_SFVEC3F: + ((SFVec3f *)field->far_ptr)->x = FLT2FIX( gf_bs_read_float(bs) ); + ((SFVec3f *)field->far_ptr)->y = FLT2FIX( gf_bs_read_float(bs) ); + ((SFVec3f *)field->far_ptr)->z = FLT2FIX( gf_bs_read_float(bs) ); + break; + case GF_SG_VRML_SFCOLOR: + ((SFColor *)field->far_ptr)->red = FLT2FIX( gf_bs_read_float(bs) ); + ((SFColor *)field->far_ptr)->green = FLT2FIX( gf_bs_read_float(bs) ); + ((SFColor *)field->far_ptr)->blue = FLT2FIX( gf_bs_read_float(bs) ); + break; + case GF_SG_VRML_SFVEC4F: + case GF_SG_VRML_SFROTATION: + ((SFRotation *)field->far_ptr)->x = FLT2FIX( gf_bs_read_float(bs) ); + ((SFRotation *)field->far_ptr)->y = FLT2FIX( gf_bs_read_float(bs) ); + ((SFRotation *)field->far_ptr)->z = FLT2FIX( gf_bs_read_float(bs) ); + ((SFRotation *)field->far_ptr)->q = FLT2FIX( gf_bs_read_float(bs) ); + break; + + case GF_SG_VRML_SFSTRING: + { + u32 size, length; + size = gf_bs_read_int(bs, 5); + length = gf_bs_read_int(bs, size); + if (gf_bs_available(bs) < length) return GF_NON_COMPLIANT_BITSTREAM; + + if ( ((SFString *)field->far_ptr)->buffer ) free( ((SFString *)field->far_ptr)->buffer); + ((SFString *)field->far_ptr)->buffer = (char*)malloc(sizeof(char)*(length+1)); + memset(((SFString *)field->far_ptr)->buffer , 0, length+1); + for (j=0; jfar_ptr)->buffer[j] = gf_bs_read_int(bs, 8); + } + } + break; + } + } + } + gf_bs_del(bs); + + /*special case for StringSensor in local mode: lookup for special chars*/ + if ((priv->type == IS_StringSensor) && priv->is_local) { + char tmp_utf8[5000]; + const unsigned short *ptr; + u32 len; + GF_FieldInfo *field1 = (GF_FieldInfo *)gf_list_get(priv->ddf, 0); + GF_FieldInfo *field2 = (GF_FieldInfo *)gf_list_get(priv->ddf, 1); + SFString *inText = (SFString *) field1->far_ptr; + SFString *outText = (SFString *) field2->far_ptr; + + field1->eventType = field2->eventType = 0; + priv->enteredText[priv->text_len] = (short) '\0'; + + len = gf_utf8_wcslen(priv->enteredText); + if (len && (priv->enteredText[len-1] == priv->termChar)) { + ptr = priv->enteredText; + len = gf_utf8_wcstombs(tmp_utf8, 5000, &ptr); + if (outText->buffer) free(outText->buffer); + outText->buffer = (char*)malloc(sizeof(char) * (len+1)); + memcpy(outText->buffer, tmp_utf8, sizeof(char) * len); + outText->buffer[len] = 0; + if (inText->buffer) free(inText->buffer); + inText->buffer = NULL; + priv->text_len = 0; + + field1->eventType = field2->eventType = 1; + } else { + if (priv->delChar) { + /*remove chars*/ + if ((len>1) && (priv->enteredText[len-1] == priv->delChar)) { + priv->enteredText[len-1] = (short) '\0'; + len--; + if (len) { + priv->enteredText[len-1] = (short) '\0'; + len--; + } + } + } + priv->text_len = len; + ptr = priv->enteredText; + len = gf_utf8_wcstombs(tmp_utf8, 5000, &ptr); + if (inText->buffer) free(inText->buffer); + inText->buffer = (char*)malloc(sizeof(char) * (len+1)); + memcpy(inText->buffer, tmp_utf8, sizeof(char) * len); + inText->buffer[len] = 0; + field1->eventType = 1; + } + } + + gf_term_lock_compositor(priv->scene->root_od->term, 1); + + /*apply it*/ + i=0; + while ((st = (ISStack*)gf_list_enum(priv->is_nodes, &i))) { + assert(st->is); + assert(st->mo); + if (!st->is->enabled) continue; + + count = gf_list_count(st->is->buffer.commandList); + scene_time = gf_inline_get_time(priv->scene); + for (j=0; jis->buffer.commandList, j); + GF_FieldInfo *field = (GF_FieldInfo *)gf_list_get(priv->ddf, j); + GF_CommandField *info = (GF_CommandField *)gf_list_get(com->command_fields, 0); + if (info && field && field->eventType) { + gf_sg_vrml_field_copy(info->field_ptr, field->far_ptr, field->fieldType); + gf_sg_command_apply(priv->scene->graph, com, scene_time); + } + } + } + gf_term_lock_compositor(priv->scene->root_od->term, 0); + return e; +} + +void ISDec_Delete(GF_BaseDecoder *plug) +{ + ISPriv *priv = (ISPriv *)plug->privateStack; + gf_list_del(priv->is_nodes); + + while (gf_list_count(priv->ddf)) { + GF_FieldInfo *fi = (GF_FieldInfo *)gf_list_get(priv->ddf, 0); + gf_list_rem(priv->ddf, 0); + gf_sg_vrml_field_pointer_del(fi->far_ptr, fi->fieldType); + free(fi); + } + gf_list_del(priv->ddf); +#if GPAC_HTK_DEMO + gf_th_del(priv->th); +#endif + free(priv); + free(plug); +} + + +GF_BaseDecoder *NewISCodec(u32 PL) +{ + ISPriv *priv; + GF_SceneDecoder *tmp; + + tmp = (GF_SceneDecoder*) malloc(sizeof(GF_SceneDecoder)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_SceneDecoder)); + + priv = (ISPriv *) malloc(sizeof(ISPriv)); + memset(priv, 0, sizeof(ISPriv)); + + priv->is_nodes = gf_list_new(); + priv->ddf = gf_list_new(); + + tmp->privateStack = priv; + + tmp->AttachStream = IS_AttachStream; + tmp->DetachStream = IS_DetachStream; + tmp->GetCapabilities = IS_GetCapabilities; + tmp->SetCapabilities = IS_SetCapabilities; + tmp->ProcessData = IS_ProcessData; + /*we don't use this...*/ + tmp->AttachScene = NULL; + + GF_REGISTER_MODULE_INTERFACE(tmp, GF_SCENE_DECODER_INTERFACE, "GPAC InputSensor Decoder", "gpac distribution") + +#if GPAC_HTK_DEMO + priv->th = gf_th_new("HTKDecoder"); +#endif + return (GF_BaseDecoder *) tmp; +} + +/* + input sensor node handling +*/ +static void IS_Unregister(GF_Node *node, ISStack *st) +{ + u32 i; + GF_ObjectManager *odm; + ISPriv *is_dec; + + gf_mo_unregister(node, st->mo); + + odm = st->mo->odm; + if (!odm) return; + + assert(odm->codec && (odm->codec->type == GF_STREAM_INTERACT)); + + /*get IS dec*/ + is_dec = (ISPriv*)odm->codec->decio->privateStack; + for (i=0; iis_nodes); i++) { + ISStack *tmp = (ISStack *)gf_list_get(is_dec->is_nodes, i); + if (tmp == st) { + gf_list_rem(is_dec->is_nodes, i); + i--; + } + } + /*stop stream*/ + if (st->mo->num_open) gf_mo_stop(st->mo); + st->mo = NULL; + st->registered = 0; +} + +static void IS_Register(GF_Node *n) +{ + GF_ObjectManager *odm; + ISPriv *is_dec; + ISStack *st = (ISStack *)gf_node_get_private(n); + odm = st->mo->odm; + if (!odm) return; + + assert(odm->codec && (odm->codec->type == GF_STREAM_INTERACT)); + + /*get IS dec*/ + is_dec = (ISPriv*)odm->codec->decio->privateStack; + gf_list_add(is_dec->is_nodes, st); + st->registered = 1; +#if GPAC_HTK_DEMO + StartHTK(is_dec); +#endif + /*start stream*/ + gf_mo_play(st->mo, 0, -1, 0); + + gf_term_unqueue_node_traverse(odm->term, n); +} + +static void TraverseInputSensor(GF_Node *node, void *rs, Bool is_destroy) +{ + ISStack *st = (ISStack*)gf_node_get_private(node); + M_InputSensor *is = (M_InputSensor *)node; + + if (is_destroy) { + GF_InlineScene *is; + if (st->registered) IS_Unregister(node, st); + is = (GF_InlineScene*)gf_sg_get_private(gf_node_get_graph(node)); + gf_term_unqueue_node_traverse(is->root_od->term, node); + free(st); + } else { + /*get decoder object */ + if (!st->mo) st->mo = gf_mo_register(node, &is->url, 0); + /*register with decoder*/ + if (st->mo && !st->registered) IS_Register(node); + } +} + + +void InitInputSensor(GF_InlineScene *is, GF_Node *node) +{ + ISStack *stack; + GF_SAFEALLOC(stack, ISStack); + stack->is = (M_InputSensor *) node; + gf_node_set_private(node, stack); + gf_node_set_callback_function(node, TraverseInputSensor); + gf_term_queue_node_traverse(is->root_od->term, node); +} + +/*check only URL changes*/ +void InputSensorModified(GF_Node *node) +{ +#if GPAC_HTK_DEMO + GF_ObjectManager *odm; + ISPriv *is_dec; +#endif + GF_MediaObject *mo; + ISStack *st = (ISStack *)gf_node_get_private(node); + + mo = gf_mo_register(node, &st->is->url, 0); + if ((mo!=st->mo) || !st->registered){ + if (mo!=st->mo) { + if (st->mo) IS_Unregister(node, st); + st->mo = mo; + } + if (st->is->enabled) + IS_Register(node); + else + return; + } else if (!st->is->enabled) { + IS_Unregister(node, st); + return; + } + +#if GPAC_HTK_DEMO + /*turn audio analyse on/off*/ + if (!st->is_dec || !st->is_dec->od_man) return; + odm = st->is_dec->od_man; + assert(odm->codec && (odm->codec->type == GF_STREAM_INTERACT)); + /*get IS dec*/ + is_dec = odm->codec->decio->privateStack; + StartHTK(is_dec); +#endif +} + + + +/* + input sensor DDF generations (user interface) +*/ + +void gf_term_mouse_input(GF_Terminal *term, GF_EventMouse *event) +{ + s32 X, Y; + u32 left_but_down, middle_but_down, right_but_down; + SFFloat wheel_pos; + u32 i; + GF_Codec *cod; + GF_BitStream *bs; + GF_SLHeader slh; + char *buf; + u32 buf_size; + Fixed bX, bY; + + if (!term || !gf_list_count(term->input_streams)) return; + + X = event->x; + Y = event->y; + left_but_down = middle_but_down = right_but_down = 0; + wheel_pos = 0; + switch (event->type) { + case GF_EVENT_MOUSEDOWN: + if (event->button==GF_MOUSE_RIGHT) right_but_down = 2; + else if (event->button==GF_MOUSE_MIDDLE) middle_but_down = 2; + else if (event->button==GF_MOUSE_LEFT) left_but_down = 2; + break; + case GF_EVENT_MOUSEUP: + if (event->button==GF_MOUSE_RIGHT) right_but_down = 1; + else if (event->button==GF_MOUSE_MIDDLE) middle_but_down = 1; + else if (event->button==GF_MOUSE_LEFT) left_but_down = 1; + break; + case GF_EVENT_MOUSEWHEEL: wheel_pos = event->wheel_pos; break; + case GF_EVENT_MOUSEMOVE: break; + default: return; + } + + /*get BIFS coordinates*/ + gf_sc_map_point(term->compositor, X, Y, &bX, &bY); + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + /*If wheel is specified disable X and Y (bug from MS wheel handling)*/ + if (wheel_pos) { + gf_bs_write_int(bs, 0, 1); + } else { + gf_bs_write_int(bs, 1, 1); + gf_bs_write_float(bs, FIX2FLT(bX)); + gf_bs_write_float(bs, FIX2FLT(bY)); + } + gf_bs_write_int(bs, left_but_down ? 1 : 0, 1); + if (left_but_down) gf_bs_write_int(bs, left_but_down-1, 1); + gf_bs_write_int(bs, middle_but_down ? 1 : 0, 1); + if (middle_but_down) gf_bs_write_int(bs, middle_but_down-1, 1); + gf_bs_write_int(bs, right_but_down ? 1 : 0, 1); + if (right_but_down) gf_bs_write_int(bs, right_but_down-1, 1); + if (wheel_pos==0) { + gf_bs_write_int(bs, 0, 1); + } else { + gf_bs_write_int(bs, 1, 1); + gf_bs_write_float(bs, FIX2FLT(wheel_pos) ); + } + + gf_bs_align(bs); + gf_bs_get_content(bs, &buf, &buf_size); + gf_bs_del(bs); + + memset(&slh, 0, sizeof(GF_SLHeader)); + slh.accessUnitStartFlag = slh.accessUnitEndFlag = 1; + slh.compositionTimeStampFlag = 1; + /*note we could use an exact TS but it's not needed: since the input is generated locally + we want it to be decoded as soon as possible, thus using 0 emulates permanent seeking on + InputSensor stream, hence forces input frame resync*/ + slh.compositionTimeStamp = 0; + + /*get all IS Mouse decoders and send frame*/ + i=0; + while ((cod = (GF_Codec*)gf_list_enum(term->input_streams, &i))) { + ISPriv *is = (ISPriv *)cod->decio->privateStack; + if (is->type==IS_Mouse) { + GF_Channel *ch = (GF_Channel *)gf_list_get(cod->inChannels, 0); + gf_es_receive_sl_packet(ch->service, ch, buf, buf_size, &slh, GF_OK); + } + } + free(buf); +} + +void gf_term_keyboard_input(GF_Terminal *term, u32 key_code, u32 hw_code, Bool isKeyUp) +{ + u32 i; + GF_BitStream *bs; + GF_SLHeader slh; + char *buf; + X_KeySensor *n; + u32 buf_size; + u32 actionKey = 0; + u32 shiftKeyDown, controlKeyDown, altKeyDown; + GF_Codec *cod; + s32 keyPressed, keyReleased, actionKeyPressed, actionKeyReleased; + + if (!term || (!gf_list_count(term->input_streams) && !gf_list_count(term->x3d_sensors)) ) return; + + memset(&slh, 0, sizeof(GF_SLHeader)); + slh.accessUnitStartFlag = slh.accessUnitEndFlag = 1; + slh.compositionTimeStampFlag = 1; + /*cf above*/ + slh.compositionTimeStamp = 0; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + shiftKeyDown = controlKeyDown = altKeyDown = 0; + keyPressed = keyReleased = actionKeyPressed = actionKeyReleased = 0; + /*key-sensor codes*/ + switch (key_code) { + case GF_KEY_F1: actionKey = 1; break; + case GF_KEY_F2: actionKey = 2; break; + case GF_KEY_F3: actionKey = 3; break; + case GF_KEY_F4: actionKey = 4; break; + case GF_KEY_F5: actionKey = 5; break; + case GF_KEY_F6: actionKey = 6; break; + case GF_KEY_F7: actionKey = 7; break; + case GF_KEY_F8: actionKey = 8; break; + case GF_KEY_F9: actionKey = 9; break; + case GF_KEY_F10: actionKey = 10; break; + case GF_KEY_F11: actionKey = 11; break; + case GF_KEY_F12: actionKey = 12; break; + case GF_KEY_HOME: actionKey = 13; break; + case GF_KEY_END: actionKey = 14; break; + case GF_KEY_PAGEUP: actionKey = 15; break; + case GF_KEY_PAGEDOWN: actionKey = 16; break; + case GF_KEY_UP: actionKey = 17; break; + case GF_KEY_DOWN: actionKey = 18; break; + case GF_KEY_LEFT: actionKey = 19; break; + case GF_KEY_RIGHT: actionKey = 20; break; + case GF_KEY_SHIFT: + actionKey = 0; + shiftKeyDown = isKeyUp ? 1 : 2; + break; + case GF_KEY_CONTROL: + actionKey = 0; + controlKeyDown = isKeyUp ? 1 : 2; + break; + case GF_KEY_ALT: + actionKey = 0; + altKeyDown = isKeyUp ? 1 : 2; + break; + + default: actionKey = 0; break; + } + if (actionKey) { + if (isKeyUp) + actionKeyReleased = actionKey; + else + actionKeyPressed = actionKey; + } else { + /*handle numeric pad*/ + if ((key_code>=GF_KEY_0) && (key_code<=GF_KEY_9) ) { + key_code = key_code + 0x30 - GF_KEY_0; + } + else + key_code = hw_code; + + if (isKeyUp) keyReleased = key_code; + else keyPressed = key_code; + } + + gf_bs_write_int(bs, keyPressed ? 1 : 0, 1); + if (keyPressed) gf_bs_write_int(bs, keyPressed, 32); + gf_bs_write_int(bs, keyReleased ? 1 : 0, 1); + if (keyReleased) gf_bs_write_int(bs, keyReleased, 32); + gf_bs_write_int(bs, actionKeyPressed ? 1 : 0, 1); + if (actionKeyPressed) gf_bs_write_int(bs, actionKeyPressed, 32); + gf_bs_write_int(bs, actionKeyReleased ? 1 : 0, 1); + if (actionKeyReleased) gf_bs_write_int(bs, actionKeyReleased, 32); + gf_bs_write_int(bs, shiftKeyDown ? 1 : 0 , 1); + if (shiftKeyDown) gf_bs_write_int(bs, shiftKeyDown-1, 1); + gf_bs_write_int(bs, controlKeyDown ? 1 : 0 , 1); + if (controlKeyDown) gf_bs_write_int(bs, controlKeyDown-1, 1); + gf_bs_write_int(bs, altKeyDown ? 1 : 0 , 1); + if (altKeyDown) gf_bs_write_int(bs, altKeyDown, 1); + + gf_bs_align(bs); + gf_bs_get_content(bs, &buf, &buf_size); + gf_bs_del(bs); + + /*get all IS keySensor decoders and send frame*/ + i=0; + while ((cod = (GF_Codec*)gf_list_enum(term->input_streams, &i))) { + ISPriv *is = (ISPriv *)cod->decio->privateStack; + if (is->type==IS_KeySensor) { +// GF_Channel *ch = gf_list_get(cod->inChannels, 0); +// gf_es_receive_sl_packet(ch->service, ch, buf, buf_size, &slh, GF_OK); + + IS_ProcessData((GF_SceneDecoder*)cod->decio, buf, buf_size, 0, 0, 0); + } + } + free(buf); + + i=0; + while ((n = (X_KeySensor*)gf_list_enum(term->x3d_sensors, &i))) { + u16 tc[2]; + u32 len; + char szStr[10]; + const unsigned short *ptr; + if (gf_node_get_tag((GF_Node*)n) != TAG_X3D_KeySensor) continue; + if (!n->enabled) return; + + if (keyPressed) { + if (n->keyPress.buffer) free(n->keyPress.buffer); + tc[0] = keyPressed; tc[1] = 0; + ptr = tc; + len = gf_utf8_wcstombs(szStr, 10, &ptr); + n->keyPress.buffer = (char*)malloc(sizeof(char) * (len+1)); + memcpy(n->keyPress.buffer, szStr, sizeof(char) * len); + n->keyPress.buffer[len] = 0; + gf_node_event_out_str((GF_Node *)n, "keyPress"); + } + if (keyReleased) { + if (n->keyRelease.buffer) free(n->keyRelease.buffer); + tc[0] = keyReleased; tc[1] = 0; + ptr = tc; + len = gf_utf8_wcstombs(szStr, 10, &ptr); + n->keyRelease.buffer = (char*)malloc(sizeof(char) * (len+1)); + memcpy(n->keyRelease.buffer, szStr, sizeof(char) * len); + n->keyRelease.buffer[len] = 0; + gf_node_event_out_str((GF_Node *)n, "keyRelease"); + } + if (actionKeyPressed) { + n->actionKeyPress = actionKeyPressed; + gf_node_event_out_str((GF_Node *)n, "actionKeyPress"); + } + if (actionKeyReleased) { + n->actionKeyRelease = actionKeyReleased; + gf_node_event_out_str((GF_Node *)n, "actionKeyRelease"); + } + if (shiftKeyDown) { + n->shiftKey = (shiftKeyDown-1) ? 1 : 0; + gf_node_event_out_str((GF_Node *)n, "shiftKey"); + } + if (controlKeyDown) { + n->controlKey = (controlKeyDown-1) ? 1 : 0; + gf_node_event_out_str((GF_Node *)n, "controlKey"); + } + if (altKeyDown) { + n->altKey= (altKeyDown-1) ? 1 : 0; + gf_node_event_out_str((GF_Node *)n, "altKey"); + } + if (keyPressed || actionKeyPressed || (shiftKeyDown-1) || (controlKeyDown-1) || (altKeyDown-1)) { + if (!n->isActive) { + n->isActive = 1; + gf_node_event_out_str((GF_Node *)n, "isActive"); + } + } else if (n->isActive) { + n->isActive = 0; + gf_node_event_out_str((GF_Node *)n, "isActive"); + } + } +} + +void gf_term_string_input(GF_Terminal *term, u32 character) +{ + u32 i; + GF_BitStream *bs; + GF_SLHeader slh; + X_StringSensor *n; + GF_Codec *cod; + char *buf; + u32 buf_size; + + if (!character || !term) return; + if (!gf_list_count(term->input_streams) && !gf_list_count(term->x3d_sensors)) return; + + memset(&slh, 0, sizeof(GF_SLHeader)); + slh.accessUnitStartFlag = slh.accessUnitEndFlag = 1; + slh.compositionTimeStampFlag = 1; + /*cf above*/ + slh.compositionTimeStamp = 0; + + /*get all IS StringSensor decoders and send frame*/ + i=0; + while ((cod = (GF_Codec*)gf_list_enum(term->input_streams, &i))) { + ISPriv *is = (ISPriv *)cod->decio->privateStack; + if (is->type==IS_StringSensor) { + + GF_Channel *ch = (GF_Channel *)gf_list_get(cod->inChannels, 0); + is->enteredText[is->text_len] = character; + is->text_len += 1; + + /*write empty DDF*/ + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 0, 1); + gf_bs_align(bs); + gf_bs_get_content(bs, &buf, &buf_size); + gf_bs_del(bs); + + gf_es_receive_sl_packet(ch->service, ch, buf, buf_size, &slh, GF_OK); + + free(buf); + } + } + + + /*get all X3D StringSensors*/ + i=0; + while ((n = (X_StringSensor*)gf_list_enum(term->x3d_sensors, &i))) { + StringSensorStack *st; + char szStr[5000]; + const unsigned short *ptr; + u32 len; + if (gf_node_get_tag((GF_Node*)n) != TAG_X3D_StringSensor) continue; + if (!n->enabled) continue; + + st = (StringSensorStack *) gf_node_get_private((GF_Node *)n); + + if (character=='\b') { + if (n->deletionAllowed && st->text_len) { + st->text_len -= 1; + st->enteredText[st->text_len] = 0; + ptr = st->enteredText; + len = gf_utf8_wcstombs(szStr, 10, &ptr); + if (n->enteredText.buffer) free(n->enteredText.buffer); + szStr[len] = 0; + n->enteredText.buffer = strdup(szStr); + gf_node_event_out_str((GF_Node *)n, "enteredText"); + } + } else if (character=='\r') { + if (n->finalText.buffer) free(n->finalText.buffer); + n->finalText.buffer = n->enteredText.buffer; + n->enteredText.buffer = strdup(""); + st->text_len = 0; + gf_node_event_out_str((GF_Node *)n, "enteredText"); + gf_node_event_out_str((GF_Node *)n, "finalText"); + } else { + st->enteredText[st->text_len] = character; + st->text_len += 1; + st->enteredText[st->text_len] = 0; + ptr = st->enteredText; + len = gf_utf8_wcstombs(szStr, 10, &ptr); + if (n->enteredText.buffer) free(n->enteredText.buffer); + szStr[len] = 0; + n->enteredText.buffer = strdup(szStr); + gf_node_event_out_str((GF_Node *)n, "enteredText"); + } + } +} + +void DestroyKeySensor(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + GF_Terminal *term = (GF_Terminal *) gf_node_get_private(node); + gf_list_del_item(term->x3d_sensors, node); + } +} +void InitKeySensor(GF_InlineScene *is, GF_Node *node) +{ + gf_node_set_private(node, is->root_od->term); + gf_node_set_callback_function(node, DestroyKeySensor); + gf_list_add(is->root_od->term->x3d_sensors, node); +} + +void DestroyStringSensor(GF_Node *node, void *rs, Bool is_destroy) +{ + if (is_destroy) { + StringSensorStack *st = (StringSensorStack *) gf_node_get_private(node); + gf_list_del_item(st->term->x3d_sensors, node); + free(st); + } +} +void InitStringSensor(GF_InlineScene *is, GF_Node *node) +{ + StringSensorStack*st; + GF_SAFEALLOC(st, StringSensorStack) + st->term = is->root_od->term; + gf_node_set_private(node, st); + gf_node_set_callback_function(node, DestroyStringSensor); + gf_list_add(is->root_od->term->x3d_sensors, node); +} + +#if GPAC_HTK_DEMO +u32 RunHTKDec(void *par) +{ + GF_BitStream *bs; + char *szWord; + s32 word_index; + u32 len, val, i; + Float word_score; + GF_SLHeader slh; + GF_Codec *cod; + unsigned char *buf; + u32 buf_size; + + + ISPriv *is_dec = (ISPriv *)par; +// while (is_dec->htk_running) + + HTK_DoDetection(); + szWord = HTK_GetWord(); + word_index = HTK_GetWordIndex(); + word_score = HTK_GetWordScore(); + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + /*HTK sensor buffer format: SFString - SFInt32 - SFFloat*/ + gf_bs_write_int(bs, 1, 1); + len = strlen(szWord); + val = gf_get_bit_size(len); + gf_bs_write_int(bs, val, 5); + gf_bs_write_int(bs, len, val); + for (i=0; iscene->root_od->term->input_streams, &i))) { + ISPriv *is = cod->decio->privateStack; + if (is != is_dec) continue; + if (is->type==IS_HTKSensor) { + GF_Channel *ch = gf_list_get(cod->inChannels, 0); + gf_es_receive_sl_packet(ch->service, ch, buf, buf_size, &slh, GF_OK); + } + } + free(buf); + + is_dec->htk_running = 0; + return 0; +} + +void StartHTK(ISPriv *is_dec) +{ + u32 j; + Bool run; + ISStack *st; + run = 0; + j=0; + while ((st = gf_list_enum(is_dec->is_nodes, &j))) { + if (st->is->enabled) { + run = 1; + break; + } + } + if (is_dec->htk_running && run) return; + if (!is_dec->htk_running && !run) return; + + is_dec->htk_running = run; + if (run) { + HTK_SetDictionary(is_dec->szHTKPath); + gf_th_run(is_dec->th, RunHTKDec, is_dec); + } +} +#endif + diff --git a/src/terminal/input_sensor.h b/src/terminal/input_sensor.h new file mode 100644 index 0000000..aa88726 --- /dev/null +++ b/src/terminal/input_sensor.h @@ -0,0 +1,101 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _INPUT_SENSOR_H_ +#define _INPUT_SENSOR_H_ + +#include +/*input sensor defs*/ +#include + +enum +{ + IS_KeySensor = 1, + IS_StringSensor, + IS_Mouse, + IS_HTKSensor, +}; + +#define GPAC_HTK_DEMO 0 + +typedef struct +{ + /*parent scene*/ + GF_InlineScene *scene; + /*list of attached nodes*/ + GF_List *is_nodes; + /*stream ID*/ + u16 ES_ID; + /*uncompressed data frame*/ + GF_List *ddf; + u32 type; + + /*string sensor sep char */ + s16 termChar, delChar; + /*current typed text in UTF-8*/ + unsigned short enteredText[5000]; + u32 text_len; + Bool is_local; + +#if GPAC_HTK_DEMO + GF_Thread *th; + Bool htk_running; + char szHTKPath[GF_MAX_PATH]; +#endif + +} ISPriv; + + +GF_BaseDecoder *NewISCodec(u32 PL); +void ISDec_Delete(GF_BaseDecoder *plug); +GF_Err IS_Configure(GF_BaseDecoder *plug, GF_InlineScene *scene, Bool is_remote); + + +typedef struct +{ + /*stream context*/ + u16 ES_ID; + Bool registered; + GF_MediaObject *mo; + M_InputSensor *is; +} ISStack; + + +void InitInputSensor(GF_InlineScene *is, GF_Node *node); +void InputSensorModified(GF_Node *n); + +void InitKeySensor(GF_InlineScene *is, GF_Node *node); + + +typedef struct +{ + u16 enteredText[5000]; + u32 text_len; + GF_Terminal *term; +} StringSensorStack; + +void InitStringSensor(GF_InlineScene *is, GF_Node *node); + +#endif /*_INPUT_SENSOR_H_*/ + diff --git a/src/terminal/media_control.c b/src/terminal/media_control.c new file mode 100644 index 0000000..6a2c521 --- /dev/null +++ b/src/terminal/media_control.c @@ -0,0 +1,443 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "media_control.h" +#include + +Bool MC_URLChanged(MFURL *old_url, MFURL *new_url) +{ + u32 i; + if (URL_GetODID(old_url) != URL_GetODID(new_url)) return 1; + if (old_url->count != new_url->count) return 1; + + for (i=0; icount; i++) { + if (old_url->vals[i].url || new_url->vals[i].url) { + if (!old_url->vals[i].url || !new_url->vals[i].url) return 1; + if (strcmp(old_url->vals[i].url, new_url->vals[i].url)) return 1; + } + } + return 0; +} + + +void MC_Restart(GF_ObjectManager *odm) +{ + GF_List *to_restart; + GF_ObjectManager *ctrl_od; + GF_Clock *ck, *scene_ck; + u32 i; + u32 current_seg; + MediaControlStack *ctrl; + if (!odm || (odm->flags & GF_ODM_NO_TIME_CTRL) ) return; + + ctrl = ODM_GetMediaControl(odm); + if (ctrl) { + /*we have a control - filter calls to only handle objects owning media control*/ + ctrl_od = ctrl->stream->odm; + /*if media control owns the scene this OD refers to the scene is always restarted - TODO make that an option*/ + if (!ctrl_od->subscene) { + if (ctrl->stream->odm != odm) return; + } + odm = ctrl->stream->odm; + + /*this is inline restart - only possible through media control*/ + if (odm->subscene && odm->subscene->root_od==ctrl->stream->odm) { + gf_inline_restart(odm->subscene); + return; + } + } + + /*if clock is main scene clock do nothing*/ + scene_ck = gf_odm_get_media_clock(odm->parentscene->root_od); + if (gf_odm_shares_clock(odm, scene_ck)) { + if (odm->parentscene->is_dynamic_scene) + gf_inline_restart_dynamic(odm->parentscene, 0); + return; + } + + /*otherwise locate all objects sharing the clock*/ + ck = gf_odm_get_media_clock(odm); + if (!ck) return; + + current_seg = 0; + /*store current segment idx*/ + if (ctrl) { + current_seg = ctrl->current_seg; + /*if last segment is passed restart*/ + if (gf_list_count(ctrl->seg) == current_seg) current_seg = 0; + } + + to_restart = gf_list_new(); + /*do stop/start in 2 pass, it's much cleaner for servers*/ + i=0; + while ((ctrl_od = (GF_ObjectManager*)gf_list_enum(odm->parentscene->ODlist, &i))) { + if (!gf_odm_shares_clock(ctrl_od, ck)) continue; + /*if running, stop and collect for restart*/ + if (ctrl_od->state) { + gf_odm_stop(ctrl_od, 1); + gf_list_add(to_restart, ctrl_od); + } + } + /*force clock reset since we don't know how OD ordering is done*/ + gf_clock_reset(ck); + if (ctrl) ctrl->current_seg = current_seg; + + /*play on all ODs collected for restart*/ + i=0; + while ((ctrl_od = (GF_ObjectManager*)gf_list_enum(to_restart, &i))) { + gf_odm_start(ctrl_od); + } + gf_list_del(to_restart); +} + + +/*resume all objects*/ +void MC_Resume(GF_ObjectManager *odm) +{ + u32 i; + GF_ObjectManager *ctrl_od; + GF_InlineScene *in_scene; + GF_Clock *ck; + + if (odm->flags & GF_ODM_NO_TIME_CTRL) return; + + /*otherwise locate all objects sharing the clock*/ + ck = gf_odm_get_media_clock(odm); + if (!ck) return; + + in_scene = odm->parentscene; + if (odm->subscene) { + assert(odm->subscene->root_od==odm); + assert(odm->subscene->is_dynamic_scene || gf_odm_shares_clock(odm, ck) ); + /*resume root*/ + gf_odm_resume(odm); + in_scene = odm->subscene; + } + + i=0; + while ((ctrl_od = (GF_ObjectManager*)gf_list_enum(in_scene->ODlist, &i))) { + if (!odm->subscene && !gf_odm_shares_clock(ctrl_od, ck)) continue; + gf_odm_resume(ctrl_od); + } +} + + +/*pause all objects*/ +void MC_Pause(GF_ObjectManager *odm) +{ + u32 i; + GF_ObjectManager *ctrl_od; + GF_InlineScene *in_scene; + GF_Clock *ck; + + if (odm->flags & GF_ODM_NO_TIME_CTRL) return; + + /*otherwise locate all objects sharing the clock*/ + ck = gf_odm_get_media_clock(odm); + if (!ck) return; + + in_scene = odm->parentscene; + if (odm->subscene) { + assert(odm->subscene->root_od==odm); + assert(odm->subscene->is_dynamic_scene || gf_odm_shares_clock(odm, ck) ); + /*pause root*/ + gf_odm_pause(odm); + in_scene = odm->subscene; + } + + i=0; + while ((ctrl_od = (GF_ObjectManager*)gf_list_enum(in_scene->ODlist, &i))) { + if (!odm->subscene && !gf_odm_shares_clock(ctrl_od, ck)) continue; + gf_odm_pause(ctrl_od); + } +} + + +/*pause all objects*/ +void MC_SetSpeed(GF_ObjectManager *odm, Fixed speed) +{ + u32 i; + GF_ObjectManager *ctrl_od; + GF_InlineScene *in_scene; + GF_Clock *ck; + + if (odm->flags & GF_ODM_NO_TIME_CTRL) return; + + /*otherwise locate all objects sharing the clock*/ + ck = gf_odm_get_media_clock(odm); + if (!ck) return; + + in_scene = odm->parentscene; + if (odm->subscene) { + assert(odm->subscene->root_od==odm); + assert( gf_odm_shares_clock(odm, ck) ); + gf_odm_set_speed(odm, speed); + in_scene = odm->subscene; + } + + i=0; + while ((ctrl_od = (GF_ObjectManager*)gf_list_enum(in_scene->ODlist, &i))) { + if (!gf_odm_shares_clock(ctrl_od, ck)) continue; + gf_odm_set_speed(ctrl_od, speed); + } +} + +void MC_GetRange(MediaControlStack *ctrl, Double *start_range, Double *end_range) +{ + u32 i; + Double duration; + GF_Segment *last_seg, *prev_seg; + if (gf_list_count(ctrl->seg)) { + GF_Segment *desc = (GF_Segment *)gf_list_get(ctrl->seg, ctrl->current_seg); + if (!desc) { + *start_range = 0; + *end_range = 0; + return; + } + /*get last segment in consecutive range so that we never issue stop/play between consecutive segments*/ + prev_seg = desc; + last_seg = NULL; + duration = desc->Duration; + i=1+ctrl->current_seg; + while ((last_seg = (GF_Segment *)gf_list_enum(ctrl->seg, &i))) { + if (prev_seg->startTime + prev_seg->Duration != last_seg->startTime) { + last_seg = NULL; + break; + } + prev_seg = last_seg; + duration += last_seg->Duration; + } + if (!last_seg) last_seg = desc; + + *start_range = desc->startTime; + if (ctrl->control->mediaStartTime>=0) *start_range += ctrl->control->mediaStartTime; + + *end_range = desc->startTime; + if ((ctrl->control->mediaStopTime>=0) && ctrl->control->mediaStopTimecontrol->mediaStopTime; + } else { + *end_range += duration; + } + } else { + if (ctrl->control->mediaStartTime>=0) *start_range = ctrl->control->mediaStartTime; + if (ctrl->control->mediaStopTime>=0) *end_range = ctrl->control->mediaStopTime; + } +} + + +void RenderMediaControl(GF_Node *node, void *rs, Bool is_destroy) +{ + Bool shall_restart, need_restart; + GF_MediaObject *prev; + GF_ObjectManager *odm; + MediaControlStack *stack =(MediaControlStack *) gf_node_get_private(node); + + if (is_destroy) { + GF_ObjectManager *odm; + MediaControlStack *stack = (MediaControlStack *) gf_node_get_private(node); + + /*reset ODM using this control*/ + if (stack->stream && stack->stream->odm) { + odm = stack->stream->odm; + ODM_RemoveMediaControl(odm, stack); + } + /*also removes the association ck<->MC if the object has been destroyed before the node*/ + if (stack->ck) stack->ck->mc = NULL; + + gf_list_del(stack->seg); + gf_sg_vrml_mf_reset(&stack->url, GF_SG_VRML_MFURL); + free(stack); + return; + } + + /*not changed nothing to do - note we need to register with stream yet for control switching...*/ + if (stack->stream && (!stack->changed || !stack->control->enabled)) return; + + need_restart = (stack->changed==2) ? 1 : 0; + shall_restart = (stack->control->mediaStartTime>=0) ? 1 : 0; + + /*check url target*/ + if (stack->stream) { + if (MC_URLChanged(&stack->url, &stack->control->url)) { + gf_sg_vrml_mf_reset(&stack->url, GF_SG_VRML_MFURL); + + prev = stack->stream; + stack->stream = gf_inline_get_media_object(stack->parent, &stack->control->url, GF_MEDIA_OBJECT_UNDEF, 0); + if (stack->stream) { + if (!stack->stream->odm) return; + /*MediaControl on inline: if dynamic scene, make sure it is connected before attaching...*/ + if (stack->stream->odm->subscene) { + if (stack->stream->odm->subscene->is_dynamic_scene && !stack->stream->odm->subscene->dyn_ck) return; + } + gf_sg_vrml_field_copy(&stack->url, &stack->control->url, GF_SG_VRML_MFURL); + + /*remove from prev*/ + if (prev && prev->odm && (prev != stack->stream)) ODM_RemoveMediaControl(prev->odm, stack); + /*register with new*/ + ODM_SetMediaControl((GF_ObjectManager *) stack->stream->odm, stack); + + while (gf_list_count(stack->seg)) gf_list_rem(stack->seg, 0); + gf_odm_init_segments((GF_ObjectManager *) stack->stream->odm, stack->seg, &stack->control->url); + + stack->current_seg = 0; + shall_restart = need_restart = 1; + stack->ck = gf_odm_get_media_clock(stack->stream->odm); + } + /*control has been removed and we were paused, resume*/ + else if (stack->paused) { + MC_Resume((GF_ObjectManager *) prev->odm); + stack->paused = 0; + } + /*MediaControl has been detached*/ + else { + ODM_RemoveMediaControl(prev->odm, stack); + return; + } + } + } else { + stack->stream = gf_inline_get_media_object(stack->parent, &stack->control->url, GF_MEDIA_OBJECT_UNDEF, 0); + if (!stack->stream || !stack->stream->odm) { + if (stack->control->url.count) gf_term_invalidate_compositor(stack->parent->root_od->term); + return; + } + stack->ck = gf_odm_get_media_clock(stack->stream->odm); + gf_sg_vrml_field_copy(&stack->url, &stack->control->url, GF_SG_VRML_MFURL); + ODM_SetMediaControl((GF_ObjectManager *) stack->stream->odm, stack); + + while (gf_list_count(stack->seg)) gf_list_rem(stack->seg, 0); + gf_odm_init_segments((GF_ObjectManager *) stack->stream->odm, stack->seg, &stack->control->url); + stack->current_seg = 0; + } + + if (!stack->changed || !stack->control->enabled) return; + + + /*if not previously enabled and now enabled, switch all other controls off and reactivate*/ + if (!stack->enabled) { + stack->enabled = 1; + need_restart = ODM_SwitchMediaControl(stack->stream->odm, stack); + } + + stack->changed = 0; + + if (!stack->control->mediaSpeed) shall_restart = 0; + + odm = (GF_ObjectManager *)stack->stream->odm; + + /*check for changes*/ + if (!stack->is_init) { + /*not linked yet*/ + if (!odm) return; + stack->media_speed = stack->control->mediaSpeed; + stack->enabled = stack->control->enabled; + stack->media_start = stack->control->mediaStartTime; + stack->media_stop = stack->control->mediaStopTime; + stack->is_init = 1; + /*the object has already been started, and media start time is not 0, restart*/ + if (stack->stream->num_open && (stack->media_start > 0) ) MC_Restart(odm); + return; + } + + if (stack->media_speed != stack->control->mediaSpeed) { + /*if no speed pause*/ + if (!stack->control->mediaSpeed && !stack->paused) { + MC_Pause(odm); + stack->paused = 1; + } + /*else resume if paused*/ + else if (stack->control->mediaSpeed && stack->paused) { + MC_Resume(odm); + stack->paused = 0; + need_restart += shall_restart; + } + /*else set speed*/ + else if (stack->media_speed && stack->control->mediaSpeed) { + /*don't set speed if we have to restart the media ...*/ + if (!shall_restart) MC_SetSpeed(odm, stack->control->mediaSpeed); + need_restart += shall_restart; + } + /*init state was paused*/ + else if (!stack->media_speed) { + need_restart ++; + } + stack->media_speed = stack->control->mediaSpeed; + } + /*check start/stop changes*/ + if (stack->media_start != stack->control->mediaStartTime) { + stack->media_start = stack->control->mediaStartTime; + need_restart += shall_restart; + } + /*stop change triggers restart no matter what (new range) if playing*/ + if (stack->media_stop != stack->control->mediaStopTime) { + stack->media_stop = stack->control->mediaStopTime; + if (stack->control->mediaSpeed) need_restart = 1; + } + + if (need_restart) { + MC_Restart(odm); + } + + /*handle preroll*/ + +} + +void InitMediaControl(GF_InlineScene *is, GF_Node *node) +{ + MediaControlStack *stack; + GF_SAFEALLOC(stack, MediaControlStack); + + stack->changed = 1; + stack->parent = is; + stack->control = (M_MediaControl *)node; + stack->seg = gf_list_new(); + + /*default values are stored on first render*/ + gf_node_set_callback_function(node, RenderMediaControl); + gf_node_set_private(node, stack); +} + + +void MC_Modified(GF_Node *node) +{ + MediaControlStack *stack =(MediaControlStack *) gf_node_get_private(node); + if (!stack) return; + if (stack->changed!=2) { + /*check URL*/ + if (MC_URLChanged(&stack->url, &stack->control->url)) stack->changed = 2; + /*check speed (play/pause)*/ + else if (stack->media_speed != stack->control->mediaSpeed) + stack->changed = 1; + /*check mediaStartTime (seek)*/ + else if (stack->media_start != stack->control->mediaStartTime) { + /*do not reevaluate if mediaStartTime is reset to -1 (current time)*/ + if (stack->control->mediaStartTime!=-1.0) + stack->changed = 2; + } +// else stack->changed = 1; + } + + /*invalidate scene, we recompute MC state in render*/ + gf_term_invalidate_compositor(stack->parent->root_od->term); +} diff --git a/src/terminal/media_control.h b/src/terminal/media_control.h new file mode 100644 index 0000000..7d767c1 --- /dev/null +++ b/src/terminal/media_control.h @@ -0,0 +1,108 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#ifndef _MEDIA_CONTROL_H_ +#define _MEDIA_CONTROL_H_ + +#include +/*MediaControl definition*/ +#include + + +/*to do: add preroll support*/ +typedef struct _media_control +{ + M_MediaControl *control; + + /*store current values to detect changes*/ + Double media_start, media_stop; + Fixed media_speed; + Bool enabled; + MFURL url; + + GF_InlineScene *parent; + /*stream owner*/ + GF_MediaObject *stream; + /*stream owner's clock*/ + GF_Clock *ck; + + Bool changed; + Bool is_init; + Bool paused; + u32 prev_active; + + /*stream object list (segments)*/ + GF_List *seg; + /*current active segment index (ie, controling the PLAY range of the media)*/ + u32 current_seg; + +} MediaControlStack; +void InitMediaControl(GF_InlineScene *is, GF_Node *node); +void MC_Modified(GF_Node *node); + +void MC_GetRange(MediaControlStack *ctrl, Double *start_range, Double *end_range); + +/*assign mediaControl for this object*/ +void ODM_SetMediaControl(GF_ObjectManager *odm, struct _media_control *ctrl); +/*get media control ruling the clock the media is running on*/ +struct _media_control *ODM_GetMediaControl(GF_ObjectManager *odm); +/*get mediaControl controling and owned by the OD, or NULL if none*/ +struct _media_control *ODM_GetObjectMediaControl(GF_ObjectManager *odm); +/*removes control from OD context*/ +void ODM_RemoveMediaControl(GF_ObjectManager *odm, struct _media_control *ctrl); +/*switches control (propagates enable=FALSE), returns 1 if control associated with OD has changed to new one*/ +Bool ODM_SwitchMediaControl(GF_ObjectManager *odm, struct _media_control *ctrl); + +/*restart object and takes care of media control/clock dependencies*/ +void MC_Restart(GF_ObjectManager *odm); +void MC_Pause(GF_ObjectManager *odm); +void MC_Resume(GF_ObjectManager *odm); + +Bool MC_URLChanged(MFURL *old_url, MFURL *new_url); + +typedef struct _media_sensor +{ + M_MediaSensor *sensor; + + GF_InlineScene *parent; + + GF_List *seg; + Bool is_init; + /*stream owner*/ + GF_MediaObject *stream; + + /*private cache (avoids browsing all sensor*/ + u32 active_seg; +} MediaSensorStack; + +void InitMediaSensor(GF_InlineScene *is, GF_Node *node); +void MS_Modified(GF_Node *node); + +void MS_UpdateTiming(GF_ObjectManager *odm, Bool is_eos); +void MS_Stop(MediaSensorStack *st); + + +#endif /*_MEDIA_CONTROL_H_*/ diff --git a/src/terminal/media_manager.c b/src/terminal/media_manager.c new file mode 100644 index 0000000..d4de80c --- /dev/null +++ b/src/terminal/media_manager.c @@ -0,0 +1,591 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include "media_memory.h" +#include + + +u32 MM_Loop(void *par); + + +enum +{ + GF_MM_CE_RUNNING= 1, + GF_MM_CE_HAS_ERROR = 1<<1, + GF_MM_CE_THREADED = 1<<2, + GF_MM_CE_REQ_THREAD = 1<<3, + /*only used by threaded decs to signal end of thread*/ + GF_MM_CE_DEAD = 1<<4 +}; + +typedef struct +{ + u32 flags; + GF_Codec *dec; + /*for threaded decoders*/ + GF_Thread *thread; + GF_Mutex *mx; +} CodecEntry; + +GF_Err gf_term_init_scheduler(GF_Terminal *term, u32 threading_mode) +{ + term->mm_mx = gf_mx_new("MediaManager"); + term->codecs = gf_list_new(); + + term->frame_duration = 33; + switch (threading_mode) { + case GF_TERM_THREAD_SINGLE: term->flags |= GF_TERM_SINGLE_THREAD; + break; + case GF_TERM_THREAD_MULTI: term->flags |= GF_TERM_MULTI_THREAD; + break; + default: + break; + } + + if (term->user->init_flags & GF_TERM_NO_VISUAL_THREAD) return GF_OK; + + term->mm_thread = gf_th_new("MediaManager"); + term->flags |= GF_TERM_RUNNING; + term->priority = GF_THREAD_PRIORITY_NORMAL; + gf_th_run(term->mm_thread, MM_Loop, term); + return GF_OK; +} + +void gf_term_stop_scheduler(GF_Terminal *term) +{ + if (term->mm_thread) { + term->flags &= ~GF_TERM_RUNNING; + while (!(term->flags & GF_TERM_DEAD) ) + gf_sleep(2); + + assert(! gf_list_count(term->codecs)); + gf_th_del(term->mm_thread); + } + gf_list_del(term->codecs); + gf_mx_del(term->mm_mx); +} + +static CodecEntry *mm_get_codec(GF_List *list, GF_Codec *codec) +{ + CodecEntry *ce; + u32 i = 0; + while ((ce = (CodecEntry*)gf_list_enum(list, &i))) { + if (ce->dec==codec) return ce; + } + return NULL; +} + + +void gf_term_add_codec(GF_Terminal *term, GF_Codec *codec) +{ + u32 i, count; + Bool locked; + Bool threaded; + CodecEntry *cd; + CodecEntry *ptr, *next; + GF_CodecCapability cap; + assert(codec); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Registering codec %s\n", codec->decio ? codec->decio->module_name : "Unknown")); + + /*caution: the mutex can be grabbed by a decoder waiting for a mutex owned by the calling thread + this happens when several scene codecs are running concurently and triggering play/pause on media*/ + locked = gf_mx_try_lock(term->mm_mx); + + cd = mm_get_codec(term->codecs, codec); + if (cd) goto exit; + + GF_SAFEALLOC(cd, CodecEntry); + cd->dec = codec; + + cap.CapCode = GF_CODEC_WANTS_THREAD; + cap.cap.valueInt = 0; + gf_codec_get_capability(codec, &cap); + threaded = cap.cap.valueInt; + if (threaded) cd->flags |= GF_MM_CE_REQ_THREAD; + + if (term->flags & GF_TERM_MULTI_THREAD) { + if ((codec->type==0x04) || (codec->type==0x05)) threaded = 1; + } else if (term->flags & GF_TERM_SINGLE_THREAD) { + threaded = 0; + } + + if (threaded) { + cd->thread = gf_th_new(cd->dec->decio->module_name); + cd->mx = gf_mx_new(cd->dec->decio->module_name); + cd->flags |= GF_MM_CE_THREADED; + gf_list_add(term->codecs, cd); + goto exit; + } + + //add codec 1- per priority 2- per type, audio being first + //priorities inherits from Systems (5bits) so range from 0 to 31 + //we sort from MAX to MIN + count = gf_list_count(term->codecs); + for (i=0; icodecs, i); + if (ptr->flags & GF_MM_CE_THREADED) continue; + + //higher priority, continue + if (ptr->dec->Priority > codec->Priority) continue; + + //same priority, put audio first + if (ptr->dec->Priority == codec->Priority) { + //we insert audio (0x05) before video (0x04) + if (ptr->dec->type < codec->type) { + gf_list_insert(term->codecs, cd, i); + goto exit; + } + //same prior, same type: insert after + if (ptr->dec->type == codec->type) { + if (i+1==count) { + gf_list_add(term->codecs, cd); + } else { + gf_list_insert(term->codecs, cd, i+1); + } + goto exit; + } + //we insert video (0x04) after audio (0x05) if next is not audio + //last one + if (i+1 == count) { + gf_list_add(term->codecs, cd); + goto exit; + } + next = (CodecEntry*)gf_list_get(term->codecs, i+1); + //# priority level, insert + if ((next->flags & GF_MM_CE_THREADED) || (next->dec->Priority != codec->Priority)) { + gf_list_insert(term->codecs, cd, i+1); + goto exit; + } + //same priority level and at least one after : continue + continue; + } + gf_list_insert(term->codecs, cd, i); + goto exit; + } + //if we got here, first in list + gf_list_add(term->codecs, cd); + +exit: + if (locked) gf_mx_v(term->mm_mx); + return; +} + +void gf_term_remove_codec(GF_Terminal *term, GF_Codec *codec) +{ + u32 i; + Bool locked; + CodecEntry *ce; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Unregistering codec %s\n", codec->decio ? codec->decio->module_name : "Unknown")); + + /*cf note above*/ + locked = gf_mx_try_lock(term->mm_mx); + + i=0; + while ((ce = (CodecEntry*)gf_list_enum(term->codecs, &i))) { + if (ce->dec != codec) continue; + + if (ce->thread) { + if (ce->flags & GF_MM_CE_RUNNING) { + ce->flags &= ~GF_MM_CE_RUNNING; + while (! (ce->flags & GF_MM_CE_DEAD)) gf_sleep(10); + ce->flags &= ~GF_MM_CE_DEAD; + } + gf_th_del(ce->thread); + gf_mx_del(ce->mx); + } + free(ce); + gf_list_rem(term->codecs, i-1); + break; + } + if (locked) gf_mx_v(term->mm_mx); + return; +} + +Bool gf_term_find_codec(GF_Terminal *term, GF_Codec *codec) +{ + CodecEntry *ce; + u32 i=0; + while ((ce = (CodecEntry*)gf_list_enum(term->codecs, &i))) { + if (ce->dec == codec) return 1; + } + return 0; +} + +static u32 MM_SimulationStep(GF_Terminal *term) +{ + CodecEntry *ce; + GF_Err e; + u32 count, remain; + u32 time_taken, time_slice, time_left; + +#ifndef GF_DISABLE_LOG + term->compositor->networks_time = gf_sys_clock(); +#endif + + gf_term_handle_services(term); + +#ifndef GF_DISABLE_LOG + term->compositor->networks_time = gf_sys_clock() - term->compositor->networks_time; +#endif + +#ifndef GF_DISABLE_LOG + term->compositor->decoders_time = gf_sys_clock(); +#endif + gf_mx_p(term->mm_mx); + + count = gf_list_count(term->codecs); + time_left = term->frame_duration; + + if (term->last_codec >= count) term->last_codec = 0; + remain = count; + + /*this is ultra basic a nice scheduling system would be much better*/ + while (remain) { + ce = (CodecEntry*)gf_list_get(term->codecs, term->last_codec); + if (!ce) break; + + if (!(ce->flags & GF_MM_CE_RUNNING) || (ce->flags & GF_MM_CE_THREADED) ) { + remain--; + if (!remain) break; + term->last_codec = (term->last_codec + 1) % count; + continue; + } + time_slice = ce->dec->Priority * time_left / term->cumulated_priority; + if (ce->dec->PriorityBoost) time_slice *= 2; + time_taken = gf_sys_clock(); + + e = gf_codec_process(ce->dec, time_slice); + + /*avoid signaling errors too often...*/ +#ifndef GPAC_DISABLE_LOG + if (e) GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM%d] Decoding Error %s\n", ce->dec->odm->OD->objectDescriptorID, gf_error_to_string(e) )); +#endif + + time_taken = gf_sys_clock() - time_taken; + + if (ce->dec->CB && (ce->dec->CB->UnitCount >= ce->dec->CB->Min)) ce->dec->PriorityBoost = 0; + + remain -= 1; + if (!remain) break; + + term->last_codec = (term->last_codec + 1) % count; + + if (time_left > time_taken) { + time_left -= time_taken; + } else { + break; + } + } + gf_mx_v(term->mm_mx); +#ifndef GF_DISABLE_LOG + term->compositor->decoders_time = gf_sys_clock() - term->compositor->decoders_time; +#endif + + if (term->flags & GF_TERM_DRAW_FRAME) { + time_taken = gf_sys_clock(); + gf_sc_draw_frame(term->compositor); + time_taken = gf_sys_clock() - time_taken; + if (time_left>time_taken) + time_left -= time_taken; + else + time_left = 0; + } /*else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI, ("(RTI] Terminal Cycle Log\t%d\t%d\t%d\n", term->compositor->networks_time, term->compositor->decoders_time, 0)); + }*/ + if (!(term->user->init_flags & GF_TERM_NO_REGULATION)) { + gf_sleep(time_left); + } +// GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Simulation step done in %d / %d ms\n", term->frame_duration-time_left, term->frame_duration)); + return time_left; +} + +u32 MM_Loop(void *par) +{ + GF_Terminal *term = (GF_Terminal *) par; + + gf_th_set_priority(term->mm_thread, term->priority); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[MediaManager] Entering thread ID %d\n", gf_th_id() )); +// GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI, ("(RTI] Terminal Cycle Log\tServices\tDecoders\tCompositor\tSleep\n")); + + while (term->flags & GF_TERM_RUNNING) { + MM_SimulationStep(term); + } + term->flags |= GF_TERM_DEAD; + return 0; +} + +u32 RunSingleDec(void *ptr) +{ + GF_Err e; + u32 time_left; + CodecEntry *ce = (CodecEntry *) ptr; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[MediaDecoder %d] Entering thread ID %d\n", ce->dec->odm->OD->objectDescriptorID, gf_th_id() )); + + while (ce->flags & GF_MM_CE_RUNNING) { + time_left = gf_sys_clock(); + gf_mx_p(ce->mx); + e = gf_codec_process(ce->dec, ce->dec->odm->term->frame_duration); + if (e) gf_term_message(ce->dec->odm->term, ce->dec->odm->net_service->url, "Decoding Error", e); + gf_mx_v(ce->mx); + time_left = gf_sys_clock() - time_left; + + + /*no priority boost this way for systems codecs, priority is dynamically set by not releasing the + graph when late and moving on*/ + if (!ce->dec->CB || (ce->dec->CB->UnitCount == ce->dec->CB->Capacity)) + ce->dec->PriorityBoost = 0; + + /*while on don't sleep*/ + if (ce->dec->PriorityBoost) continue; + + if (time_left) { + while (time_left > ce->dec->odm->term->frame_duration) time_left -= ce->dec->odm->term->frame_duration; + gf_sleep(time_left); + } else { + gf_sleep(ce->dec->odm->term->frame_duration); + } + } + ce->flags |= GF_MM_CE_DEAD; + return 0; +} + +/*NOTE: when starting/stoping a decoder we only lock the decoder mutex, NOT the media manager. This +avoids deadlocking in case a system codec waits for the scene graph and the compositor requests +a stop/start on a media*/ +void gf_term_start_codec(GF_Codec *codec) +{ + GF_CodecCapability cap; + CodecEntry *ce; + GF_Terminal *term = codec->odm->term; + ce = mm_get_codec(term->codecs, codec); + if (!ce) return; + + /*lock dec*/ + if (ce->mx) gf_mx_p(ce->mx); + + /*clean decoder memory and wait for RAP*/ + if (codec->CB) gf_cm_reset(codec->CB); + + cap.CapCode = GF_CODEC_WAIT_RAP; + gf_codec_set_capability(codec, cap); + + if (codec->decio && (codec->decio->InterfaceType == GF_SCENE_DECODER_INTERFACE)) { + cap.CapCode = GF_CODEC_SHOW_SCENE; + cap.cap.valueInt = 1; + gf_codec_set_capability(codec, cap); + } + + gf_codec_set_status(codec, GF_ESM_CODEC_PLAY); + + if (!(ce->flags & GF_MM_CE_RUNNING)) { + ce->flags |= GF_MM_CE_RUNNING; + if (ce->thread) { + gf_th_run(ce->thread, RunSingleDec, ce); + gf_th_set_priority(ce->thread, term->priority); + } else { + term->cumulated_priority += ce->dec->Priority+1; + } + } + + /*unlock dec*/ + if (ce->mx) + gf_mx_v(ce->mx); +} + +void gf_term_stop_codec(GF_Codec *codec) +{ + Bool locked = 0; + CodecEntry *ce; + GF_Terminal *term = codec->odm->term; + ce = mm_get_codec(term->codecs, codec); + if (!ce) return; + + if (ce->mx) gf_mx_p(ce->mx); + /*We must make sure: + 1- media codecs are synchrounously stop otherwise we could destroy the composition memory while + the codec writes to it + 2- prevent deadlock for other codecs waiting for the scene graph + */ + else if (codec->CB) { + locked = 1; + gf_mx_p(term->mm_mx); + } else { + locked = gf_mx_try_lock(term->mm_mx); + } + + if (codec->decio && codec->odm->mo && (codec->odm->mo->flags & GF_MO_DISPLAY_REMOVE) ) { + GF_CodecCapability cap; + cap.CapCode = GF_CODEC_SHOW_SCENE; + cap.cap.valueInt = 0; + gf_codec_set_capability(codec, cap); + codec->odm->mo->flags &= ~GF_MO_DISPLAY_REMOVE; + } + + /*set status directly and don't touch CB state*/ + codec->Status = GF_ESM_CODEC_STOP; + /*don't wait for end of thread since this can be triggered within the decoding thread*/ + if (ce->flags & GF_MM_CE_RUNNING) { + ce->flags &= ~GF_MM_CE_RUNNING; + if (!ce->thread) + term->cumulated_priority -= codec->Priority+1; + } + + if (ce->mx) gf_mx_v(ce->mx); + /*cf note above*/ + else if (locked) gf_mx_v(term->mm_mx); +} + +void gf_term_set_threading(GF_Terminal *term, u32 mode) +{ + u32 i; + Bool thread_it, restart_it; + CodecEntry *ce; + + switch (mode) { + case GF_TERM_THREAD_SINGLE: + if (term->flags & GF_TERM_SINGLE_THREAD) return; + term->flags &= ~GF_TERM_MULTI_THREAD; + term->flags |= GF_TERM_SINGLE_THREAD; + break; + case GF_TERM_THREAD_MULTI: + if (term->flags & GF_TERM_MULTI_THREAD) return; + term->flags &= ~GF_TERM_SINGLE_THREAD; + term->flags |= GF_TERM_MULTI_THREAD; + break; + default: + if (!(term->flags & (GF_TERM_MULTI_THREAD | GF_TERM_SINGLE_THREAD) ) ) return; + term->flags &= ~GF_TERM_SINGLE_THREAD; + term->flags &= ~GF_TERM_MULTI_THREAD; + break; + } + + gf_mx_p(term->mm_mx); + + + i=0; + while ((ce = (CodecEntry*)gf_list_enum(term->codecs, &i))) { + thread_it = 0; + /*free mode, decoder wants threading - do */ + if ((mode == GF_TERM_THREAD_FREE) && (ce->flags & GF_MM_CE_REQ_THREAD)) thread_it = 1; + else if (mode == GF_TERM_THREAD_MULTI) thread_it = 1; + + if (thread_it && (ce->flags & GF_MM_CE_THREADED)) continue; + if (!thread_it && !(ce->flags & GF_MM_CE_THREADED)) continue; + + restart_it = 0; + if (ce->flags & GF_MM_CE_RUNNING) { + restart_it = 1; + ce->flags &= ~GF_MM_CE_RUNNING; + } + + if (ce->flags & GF_MM_CE_THREADED) { + /*wait for thread to die*/ + while (!(ce->flags & GF_MM_CE_DEAD)) gf_sleep(0); + ce->flags &= ~GF_MM_CE_DEAD; + gf_th_del(ce->thread); + ce->thread = NULL; + gf_mx_del(ce->mx); + ce->mx = NULL; + ce->flags &= ~GF_MM_CE_THREADED; + } else { + term->cumulated_priority -= ce->dec->Priority+1; + } + + if (thread_it) { + ce->flags |= GF_MM_CE_THREADED; + ce->thread = gf_th_new(ce->dec->decio->module_name); + ce->mx = gf_mx_new(ce->dec->decio->module_name); + } + + if (restart_it) { + ce->flags |= GF_MM_CE_RUNNING; + if (ce->thread) { + gf_th_run(ce->thread, RunSingleDec, ce); + gf_th_set_priority(ce->thread, term->priority); + } else { + term->cumulated_priority += ce->dec->Priority+1; + } + } + } + gf_mx_v(term->mm_mx); +} + +void gf_term_set_priority(GF_Terminal *term, s32 Priority) +{ + u32 i; + CodecEntry *ce; + gf_mx_p(term->mm_mx); + + gf_th_set_priority(term->mm_thread, Priority); + + i=0; + while ((ce = (CodecEntry*)gf_list_enum(term->codecs, &i))) { + if (ce->flags & GF_MM_CE_THREADED) + gf_th_set_priority(ce->thread, Priority); + } + term->priority = Priority; + gf_mx_v(term->mm_mx); +} + +GF_EXPORT +GF_Err gf_term_process_step(GF_Terminal *term) +{ + if (!(term->flags & GF_TERM_DRAW_FRAME)) return GF_BAD_PARAM; + MM_SimulationStep(term); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_term_process_flush(GF_Terminal *term) +{ + u32 i; + CodecEntry *ce; + if (!(term->flags & GF_TERM_DRAW_FRAME) ) return GF_BAD_PARAM; + + /*update till frame mature*/ + while (1) { + gf_term_handle_services(term); + gf_mx_p(term->mm_mx); + + i=0; + while ((ce = (CodecEntry*)gf_list_enum(term->codecs, &i))) { + gf_codec_process(ce->dec, 10000); + } + gf_mx_v(term->mm_mx); + + if (!gf_sc_draw_frame(term->compositor)) + break; + + if (! (term->user->init_flags & GF_TERM_NO_REGULATION)) + break; + } + return GF_OK; +} + diff --git a/src/terminal/media_memory.c b/src/terminal/media_memory.c new file mode 100644 index 0000000..c1ec90c --- /dev/null +++ b/src/terminal/media_memory.c @@ -0,0 +1,586 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include +#include +#include "media_memory.h" +#include "media_control.h" + +#define NO_TEMPORAL_SCALABLE 1 + + +GF_DBUnit *gf_db_unit_new() +{ + GF_DBUnit *tmp; + GF_SAFEALLOC(tmp, GF_DBUnit) + return tmp; +} + +void gf_db_unit_del(GF_DBUnit *db) +{ + if (!db) return; + if (db->next) gf_db_unit_del(db->next); + if (db->data) free(db->data); + free(db); +} + +static GF_CMUnit *gf_cm_unit_new() +{ + GF_CMUnit *tmp; + GF_SAFEALLOC(tmp, GF_CMUnit) + return tmp; +} + +static void gf_cm_unit_del(GF_CMUnit *cb) +{ + if (cb->next) gf_cm_unit_del(cb->next); + cb->next = NULL; + if (cb->data) { + free(cb->data); + cb->data = NULL; + } + free(cb); +} + + +GF_CompositionMemory *gf_cm_new(u32 UnitSize, u32 capacity) +{ + GF_CompositionMemory *tmp; + GF_CMUnit *cu, *prev; + u32 i; + if (!capacity) return NULL; + + GF_SAFEALLOC(tmp, GF_CompositionMemory) + + tmp->Capacity = capacity; + tmp->UnitSize = UnitSize; + + prev = NULL; + i = 1; + while (capacity) { + cu = gf_cm_unit_new(); + if (!prev) { + tmp->input = cu; + } else { + prev->next = cu; + cu->prev = prev; + } + cu->dataLength = 0; + cu->data = UnitSize ? (char*)malloc(sizeof(char)*UnitSize) : NULL; + if (cu->data) memset(cu->data, 0, sizeof(char)*UnitSize); + prev = cu; + capacity --; + i++; + } + cu->next = tmp->input; + tmp->input->prev = cu; + + /*close the loop. The output is the input as the first item + that will be ready for composition will be filled in the input*/ + tmp->output = tmp->input; + + tmp->Status = CB_STOP; + return tmp; +} + +void gf_cm_del(GF_CompositionMemory *cb) +{ + gf_odm_lock(cb->odm, 1); + /*may happen when CB is destroyed right after creation in case*/ + if (cb->Status == CB_BUFFER) { + gf_clock_buffer_off(cb->odm->codec->ck); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ODM%d: buffering off at %d (nb buffering on clock: %d)\n", cb->odm->OD->objectDescriptorID, gf_term_get_time(cb->odm->term), cb->odm->codec->ck->Buffering)); + } + + /*break the loop and destroy*/ + cb->input->prev->next = NULL; + gf_cm_unit_del(cb->input); + gf_odm_lock(cb->odm, 0); + free(cb); +} + +void gf_cm_rewind_input(GF_CompositionMemory *cb) +{ + if (cb->UnitCount) { + cb->UnitCount--; + cb->input = cb->input->prev; + cb->input->dataLength = 0; + } +} + +/*access to the input buffer - return NULL if no input is available (buffer full)*/ +GF_CMUnit *gf_cm_lock_input(GF_CompositionMemory *cb, u32 TS, Bool codec_reordering) +{ +#if !NO_TEMPORAL_SCALABLE + GF_CMUnit *cu; + if (codec_reordering) { +#endif + /*there is still something in the input buffer*/ + if (cb->input->dataLength) { + if (cb->input->TS==TS) + return cb->input; + return NULL; + } + cb->input->TS = TS; + return cb->input; + +#if !NO_TEMPORAL_SCALABLE + } + + /*spatial scalable, go backward to fetch same TS*/ + cu = cb->input; + while (1) { + if (cu->TS == TS) + return cu; + cu = cu->prev; + if (cu == cb->input) break; + } + /*temporal scalable (find empty slot)*/ + cu = cb->input; + while (1) { + if (!cu->dataLength) { + assert(!cu->TS || (cb->Capacity==1)); + cu->TS = TS; + return cu; + } + cu = cu->next; + if (cu == cb->input) return NULL; + } + return NULL; +#endif +} + +#if 0 +static void check_temporal(GF_CompositionMemory *cb) +{ + GF_CMUnit *cu; + cu = cb->output; + while (1) { + if (cu->next==cb->output) break; + assert(!cu->next->dataLength || (cu->TS < cu->next->TS)); + assert(!cu(>TS || (cu->TS >= cb->LastRenderedTS)); + cu = cu->next; + } +} +#endif + +/*re-orders units in case of temporal scalability. Blocking call as it may change the output unit too*/ +static GF_CMUnit *gf_cm_reorder_unit(GF_CompositionMemory *cb, GF_CMUnit *unit, Bool codec_reordering) +{ + GF_CMUnit *cu; +#if NO_TEMPORAL_SCALABLE + cu = cb->input; + cb->input = cb->input->next; + return cu; +#else + /*lock the buffer since we may move pointers*/ + gf_odm_lock(cb->odm, 1); + + /*1- unit is next on time axis (no temporal scalability)*/ + if (!cb->input->dataLength || cb->input->TS < unit->TS) { + + if (unit != cb->input) { + /*remove unit from the list*/ + unit->prev->next = unit->next; + unit->next->prev = unit->prev; + + /*insert unit after input*/ + unit->prev = cb->input; + unit->next = cb->input->next; + unit->next->prev = unit; + unit->prev->next = unit; + } + /*set input to our unit*/ + cb->input = unit; + goto exit; + } + /*2- unit is before our last delivered input - temporal scalability*/ + cu = cb->input; + while (1) { + + if (cu->TS > unit->TS) { + /*we have a unit after this one that has been consumed - discard our unit*/ + if (!cu->dataLength) { + unit = NULL; + goto exit; + } + /*previous unit is the active one - check one further*/ + if (cu->prev == unit) { + if (!unit->prev->dataLength || (unit->prev->TS < unit->TS)) + break; + } + /*no previous unit or our unit is just after the previous unit*/ + else if (!cu->prev->dataLength || (cu->prev->TS < unit->TS)) { + break; + } + /*otherwise need to go one further*/ + } + + /*go on*/ + cu = cu->prev; + /*done (should never happen)*/ + if (cu == cb->input) + goto exit; + } + + /*remove unit from the list*/ + unit->prev->next = unit->next; + unit->next->prev = unit->prev; + + /*insert active before cu*/ + unit->next = cu; + unit->prev = cu->prev; + unit->next->prev = unit; + unit->prev->next = unit; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("Swapping CU buffer\n")); + +exit: + + /*perform sanity check on output ordering*/ + if (unit) { + /*if no more output reset to our min*/ + if (!cb->output->dataLength) { + cb->output = cb->input; + while (1) { + if (!cb->output->prev->dataLength) break; + cb->output = cb->output->prev; + } + } + /*otherwise we just inserted a CU that should be the next one to be consumed*/ + else if (cb->output->TS > unit->TS) { + cb->output = unit; + } + + } + + //check_temporal(cb); + /*unlock the buffer*/ + gf_odm_lock(cb->odm, 0); + return unit; +#endif +} + +void gf_cm_unlock_input(GF_CompositionMemory *cb, GF_CMUnit *cu, u32 cu_size, Bool codec_reordering) +{ + /*nothing dispatched, ignore*/ + if (!cu_size) { + cu->dataLength = 0; + cu->TS = 0; + return; + } + gf_odm_lock(cb->odm, 1); + + if (cu->TS < cb->input->TS) + cu = cu; + + + if (codec_reordering) { + cb->input = cb->input->next; + } else { + cu = gf_cm_reorder_unit(cb, cu, codec_reordering); + assert(cu); + } + + if (cu) { + /*FIXME - if the CU already has data, this is spatial scalability so same num buffers*/ + if (!cu->dataLength) cb->UnitCount += 1; + cu->dataLength = cu_size; + cu->RenderedLength = 0; + + /*turn off buffering - this must be done now rather than when fetching first output frame since we're not + sure output is fetched (Switch node, ...)*/ + if ( (cb->Status == CB_BUFFER) && (cb->UnitCount >= cb->Capacity) ) { + /*done with buffering, signal to the clock (ONLY ONCE !)*/ + cb->Status = CB_BUFFER_DONE; + gf_clock_buffer_off(cb->odm->codec->ck); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ODM%d: buffering off at %d (nb buffering on clock: %d)\n", cb->odm->OD->objectDescriptorID, gf_term_get_time(cb->odm->term), cb->odm->codec->ck->Buffering)); + } + + /*since a new CU is here notify the compositor*/ + if ((cb->odm->codec->type==GF_STREAM_VISUAL) && cb->odm->mo && cb->odm->mo->num_open) { + gf_term_invalidate_compositor(cb->odm->term); + } + } + gf_odm_lock(cb->odm, 0); +} + + +/*Reset composition memory. Note we don't reset the content of each frame since it would lead to green frames +when using bitmap (visual), where data is not cached*/ +void gf_cm_reset(GF_CompositionMemory *cb) +{ + GF_CMUnit *cu; + + gf_odm_lock(cb->odm, 1); + + cu = cb->input; + cu->RenderedLength = 0; + cu->dataLength = 0; + cu->TS = 0; + cu = cu->next; + while (cu != cb->input) { + cu->RenderedLength = 0; + cu->TS = 0; + cu->dataLength = 0; + cu = cu->next; + } + cb->output = cb->input; + cb->UnitCount = 0; + cb->HasSeenEOS = 0; + + if (cb->odm->mo) cb->odm->mo->timestamp = 0; + gf_odm_lock(cb->odm, 0); +} + +/*resize buffers (blocking)*/ +void gf_cm_resize(GF_CompositionMemory *cb, u32 newCapacity) +{ + GF_CMUnit *cu; + if (!newCapacity) return; + + /*lock buffer*/ + gf_odm_lock(cb->odm, 1); + cu = cb->input; + + cb->UnitSize = newCapacity; + /*don't touch any existing memory (it may happen on the fly with audio)*/ + cu->data = (char*)realloc(cu->data, sizeof(char)*newCapacity); + cu = cu->next; + while (cu != cb->input) { + cu->data = (char*)realloc(cu->data, sizeof(char)*newCapacity); + cu = cu->next; + } + + gf_odm_lock(cb->odm, 0); +} + + + +/*resize buffers (blocking)*/ +void gf_cm_reinit(GF_CompositionMemory *cb, u32 UnitSize, u32 Capacity) +{ + GF_CMUnit *cu, *prev; + u32 i; + if (!Capacity || !UnitSize) return; + + gf_odm_lock(cb->odm, 1); + /*break the loop and destroy*/ + cb->input->prev->next = NULL; + gf_cm_unit_del(cb->input); + + cu = NULL; + cb->Capacity = Capacity; + cb->UnitSize = UnitSize; + + prev = NULL; + i = 1; + while (Capacity) { + cu = gf_cm_unit_new(); + if (!prev) { + cb->input = cu; + } else { + prev->next = cu; + cu->prev = prev; + } + cu->dataLength = 0; + cu->data = (char*)malloc(sizeof(char)*UnitSize); + prev = cu; + Capacity --; + i++; + } + cu->next = cb->input; + cb->input->prev = cu; + cb->output = cb->input; + gf_odm_lock(cb->odm, 0); +} + +/*access to the first available CU for rendering +this is a blocking call since input may change the output (temporal scalability)*/ +GF_CMUnit *gf_cm_get_output(GF_CompositionMemory *cb) +{ + GF_CMUnit *out = NULL; + + /*if paused or stop or buffering, do nothing*/ + switch (cb->Status) { + case CB_BUFFER: + return NULL; + case CB_STOP: + case CB_PAUSE: + /*only visual buffers deliver data when paused*/ + if (cb->odm->codec->type != GF_STREAM_VISUAL) goto exit; + break; + case CB_BUFFER_DONE: + cb->Status = CB_PLAY; + break; + } + + /*no output*/ + if (!cb->output->dataLength) { + if ((cb->Status != CB_STOP) && cb->HasSeenEOS && (cb->odm && cb->odm->codec)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] Switching composition memory to stop state - time %d\n", cb->odm->OD->objectDescriptorID, (u32) cb->odm->media_stop_time)); + + cb->Status = CB_STOP; + cb->odm->current_time = (u32) cb->odm->media_stop_time; + /*force update of media time*/ + MS_UpdateTiming(cb->odm, 1); + } + goto exit; + } + + /*update the timing*/ + if ((cb->Status != CB_STOP) && cb->odm && cb->odm->codec) { + cb->odm->current_time = cb->output->TS; + + /*handle visual object - EOS if no more data (we keep the last CU for rendering, so check next one)*/ + if (cb->HasSeenEOS && (!cb->output->next->dataLength || (cb->Capacity==1))) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] Switching composition memory to stop state - time %d\n", cb->odm->OD->objectDescriptorID, (u32) cb->odm->media_stop_time)); + cb->Status = CB_STOP; + cb->odm->current_time = (u32) cb->odm->media_stop_time; + /*force update of media time*/ + MS_UpdateTiming(cb->odm, 1); + } + } + out = cb->output; + + assert(out->TS >= cb->LastRenderedTS); + +exit: + return out; +} + + + +/*drop the output CU*/ +void gf_cm_drop_output(GF_CompositionMemory *cb) +{ + assert(cb->UnitCount); + /*this allows reuse of the CU*/ + cb->output->RenderedLength = 0; + cb->LastRenderedTS = cb->output->TS; + + /*on visual streams, always keep the last AU*/ + if (cb->output->dataLength && (cb->odm->codec->type == GF_STREAM_VISUAL) ) { + if ( !cb->output->next->dataLength || (cb->Capacity == 1) ) { + return; + } + } + + /*reset the output*/ + cb->output->dataLength = 0; + cb->output->TS = 0; + cb->output = cb->output->next; + cb->UnitCount -= 1; + + if (!cb->HasSeenEOS && cb->UnitCount <= cb->Min) { + cb->odm->codec->PriorityBoost = 1; + } +} + +void gf_cm_set_status(GF_CompositionMemory *cb, u32 Status) +{ + gf_odm_lock(cb->odm, 1); + /*if we're asked for play, trigger on buffering*/ + if (Status == CB_PLAY) { + switch (cb->Status) { + case CB_STOP: + cb->Status = CB_BUFFER; + gf_clock_buffer_on(cb->odm->codec->ck); + break; + case CB_PAUSE: + cb->Status = CB_PLAY; + break; + /*this should never happen (calling play while already buffering ...)*/ + case CB_BUFFER: + cb->LastRenderedTS = 0; + break; + default: + cb->Status = Status; + break; + } + } else { + cb->LastRenderedTS = 0; + if (cb->Status == CB_BUFFER) { + gf_clock_buffer_off(cb->odm->codec->ck); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ODM%d: buffering off at %d (nb buffering on clock: %d)\n", cb->odm->OD->objectDescriptorID, gf_term_get_time(cb->odm->term), cb->odm->codec->ck->Buffering)); + } + if (Status == CB_STOP) { + gf_cm_reset(cb); + } + cb->Status = Status; + } + + gf_odm_lock(cb->odm, 0); + +} + +void gf_cm_set_eos(GF_CompositionMemory *cb) +{ + gf_odm_lock(cb->odm, 1); + /*we may have a pb if the stream is so short that the EOS is signaled + while we're buffering. In this case we shall turn the clock on and + keep a trace of the EOS notif*/ + if (cb->Status == CB_BUFFER) { + cb->Status = CB_BUFFER_DONE; + gf_clock_buffer_off(cb->odm->codec->ck); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ODM%d: buffering off at %d (nb buffering on clock: %d)\n", cb->odm->OD->objectDescriptorID, gf_term_get_time(cb->odm->term), cb->odm->codec->ck->Buffering)); + } + cb->HasSeenEOS = 1; + gf_term_invalidate_compositor(cb->odm->term); + gf_odm_lock(cb->odm, 0); +} + + +Bool gf_cm_is_running(GF_CompositionMemory *cb) +{ + if (cb->Status == CB_PLAY) + return !cb->odm->codec->ck->Paused; + + if ((cb->Status == CB_BUFFER_DONE) && (gf_clock_is_started(cb->odm->codec->ck) || cb->odm->term->play_state) ) { + cb->Status = CB_PLAY; + return 1; + } + + if ((cb->odm->codec->type == GF_STREAM_VISUAL) + && (cb->Status == CB_STOP) + && cb->output->dataLength) return 1; + + return 0; +} + + +Bool gf_cm_is_eos(GF_CompositionMemory *cb) +{ + return ( (cb->Status == CB_STOP) && cb->HasSeenEOS); +} + +void gf_cm_abort_buffering(GF_CompositionMemory *cb) +{ + if (cb->Status == CB_BUFFER) { + cb->Status = CB_BUFFER_DONE; + gf_clock_buffer_off(cb->odm->codec->ck); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ODM%d: buffering off at %d (nb buffering on clock: %d)\n", cb->odm->OD->objectDescriptorID, gf_term_get_time(cb->odm->term), cb->odm->codec->ck->Buffering)); + } +} diff --git a/src/terminal/media_memory.h b/src/terminal/media_memory.h new file mode 100644 index 0000000..29dce9b --- /dev/null +++ b/src/terminal/media_memory.h @@ -0,0 +1,163 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _MEDIA_MEMORY_H_ +#define _MEDIA_MEMORY_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*compressed media unit*/ +typedef struct _decoding_buffer +{ + struct _decoding_buffer *next; + + /*decoding time stamp in ms*/ + u32 DTS; + /*composition time stamp in ms*/ + u32 CTS; + /*random access point flag*/ + Bool RAP; + /*amount of padding bits*/ + u8 PaddingBits; + + u32 dataLength; + char *data; +} GF_DBUnit; + +GF_DBUnit *gf_db_unit_new(); +void gf_db_unit_del(GF_DBUnit *db); + + +/*composition memory (composition buffer) status*/ +enum +{ + /*CB stoped (contains invalid data)*/ + CB_STOP = 0, + /*CB playing (contains valid data)*/ + CB_PLAY, + /*CB is not playing but contains VALID data (prevents output droping)*/ + CB_PAUSE, + /*CB is in buffering stage (internal only)*/ + CB_BUFFER, + /*CB is done buffering and waits for data (internal only)*/ + CB_BUFFER_DONE, +}; + +/*Composition Unit structure*/ +typedef struct _composition_unit { + /*next and previous buffers. previous is used for temporal scalability*/ + struct _composition_unit *next, *prev; + /*TS: composition time stamp in ms*/ + u32 TS; + + /*this is for audio CU in case the whole CU is not consumed in one shot*/ + u32 RenderedLength; + + u32 dataLength; + char* data; +} GF_CMUnit; + + +/*composition buffer (circular buffer of CUs)*/ +struct _composition_memory +{ + /*input is used by the decoder to deliver CUs. + if temporal scalability is enabled, this is the LAST DELIVERED CU + otherwise this is the next available CU slot*/ + GF_CMUnit *input; + /*output is the next available frme for rendering*/ + GF_CMUnit *output; + /*capacity is the number of allocated buffers*/ + u32 Capacity; + /*Min is the triggering of the media manager*/ + u32 Min; + /*Unit size is the size of each buffer*/ + u32 UnitSize; + + /*Status of the buffer*/ + u32 Status; + /*Number of active units*/ + u32 UnitCount; + + /*OD manager ruling this CB*/ + struct _od_manager *odm; + + /*set when EOS is received by decoder*/ + Bool HasSeenEOS; + + /*trick for temporal scalability: this is the last rendered CTS*/ + u32 LastRenderedTS; +}; + +/*a composition buffer only has fixed-size unit*/ +GF_CompositionMemory *gf_cm_new(u32 UnitSize, u32 capacity); +void gf_cm_del(GF_CompositionMemory *cb); +/*re-inits complete cb*/ +void gf_cm_reinit(GF_CompositionMemory *cb, u32 UnitSize, u32 Capacity); + +/*locks available input for desired TS (needed for scalability) - return NULL if no +input is available (buffer full)*/ +GF_CMUnit *gf_cm_lock_input(GF_CompositionMemory *cb, u32 TS, Bool codec_reordering); +/*dispatch data in input. If NbBytes is 0, no data is dispatched. TS is needed for re-ordering +of buffers*/ +void gf_cm_unlock_input(GF_CompositionMemory *cb, GF_CMUnit *cu, u32 cu_size, Bool codec_reordering); +/*rewind input of 1 unit - used when doing precise seeking*/ +void gf_cm_rewind_input(GF_CompositionMemory *cb); + +/*fetch output buffer, NULL if output is empty*/ +GF_CMUnit *gf_cm_get_output(GF_CompositionMemory *cb); +/*release the output buffer once rendered - if renderedLength is not equal to dataLength the +output is NOT droped*/ +void gf_cm_drop_output(GF_CompositionMemory *cb); + +/*reset the entire memory*/ +void gf_cm_reset(GF_CompositionMemory *cb); + +/*resize the buffers*/ +void gf_cm_resize(GF_CompositionMemory *cb, u32 newCapacity); + +/*set the status of the buffer. Buffering is done automatically*/ +void gf_cm_set_status(GF_CompositionMemory *cb, u32 Status); + +/*return 1 if object is running (clock started), 0 otherwise*/ +Bool gf_cm_is_running(GF_CompositionMemory *cb); +/*signals end of stream*/ +void gf_cm_set_eos(GF_CompositionMemory *cb); +/*returns 1 if end of stream has been reached (eg, signaled and no more data)*/ +Bool gf_cm_is_eos(GF_CompositionMemory *cb); +/*aborts buffering if any*/ +void gf_cm_abort_buffering(GF_CompositionMemory *cb); + +#ifdef __cplusplus +} +#endif + + +#endif /*_MEDIA_MEMORY_H_*/ + diff --git a/src/terminal/media_object.c b/src/terminal/media_object.c new file mode 100644 index 0000000..8622715 --- /dev/null +++ b/src/terminal/media_object.c @@ -0,0 +1,798 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include +#include +#include "media_memory.h" +#include "media_control.h" +#include + + +static GF_MediaObject *get_sync_reference(GF_InlineScene *is, XMLRI *iri, u32 o_type, GF_Node *orig_ref, Bool *post_pone) +{ + MFURL mfurl; + SFURL sfurl; + GF_MediaObject *res; + GF_Node *ref = NULL; + + u32 stream_id = 0; + if (iri->type==XMLRI_STREAMID) { + stream_id = iri->lsr_stream_id; + } else if (!iri->string) { + return NULL; + } else { + if (iri->target) ref = iri->target; + else if (iri->string[0]=='#') ref = gf_sg_find_node_by_name(is->graph, iri->string+1); + else ref = gf_sg_find_node_by_name(is->graph, iri->string); + + if (ref) { +#ifndef GPAC_DISABLE_SVG + GF_FieldInfo info; +#endif + /*safety check, break cyclic references*/ + if (ref==orig_ref) return NULL; + + switch (ref->sgprivate->tag) { +#ifndef GPAC_DISABLE_SVG + case TAG_SVG_audio: + o_type = GF_MEDIA_OBJECT_AUDIO; + if (gf_node_get_attribute_by_tag(ref, TAG_XLINK_ATT_href, 0, 0, &info)==GF_OK) { + return get_sync_reference(is, info.far_ptr, o_type, orig_ref ? orig_ref : ref, post_pone); + } + return NULL; + case TAG_SVG_video: + o_type = GF_MEDIA_OBJECT_VIDEO; + if (gf_node_get_attribute_by_tag(ref, TAG_XLINK_ATT_href, 0, 0, &info)==GF_OK) { + return get_sync_reference(is, info.far_ptr, o_type, orig_ref ? orig_ref : ref, post_pone); + } + return NULL; +#endif + default: + return NULL; + } + } + } + *post_pone = 0; + mfurl.count = 1; + mfurl.vals = &sfurl; + mfurl.vals[0].OD_ID = stream_id; + mfurl.vals[0].url = iri->string; + + res = gf_inline_get_media_object(is, &mfurl, o_type, 0); + if (!res) *post_pone = 1; + return res; +} + +GF_EXPORT +GF_MediaObject *gf_mo_register(GF_Node *node, MFURL *url, Bool lock_timelines) +{ + u32 obj_type; +#ifndef GPAC_DISABLE_SVG + Bool post_pone; + GF_FieldInfo info; +#endif + GF_InlineScene *is; + GF_MediaObject *res, *syncRef; + GF_SceneGraph *sg = gf_node_get_graph(node); + if (!sg) return NULL; + is = (GF_InlineScene*)gf_sg_get_private(sg); + if (!is) return NULL; + + syncRef = NULL; + + /*keep track of the kind of object expected if URL is not using OD scheme*/ + switch (gf_node_get_tag(node)) { + /*MPEG4 only*/ + case TAG_MPEG4_AudioSource: obj_type = GF_MEDIA_OBJECT_AUDIO; break; + case TAG_MPEG4_AnimationStream: + obj_type = GF_MEDIA_OBJECT_UPDATES; + break; + + case TAG_MPEG4_InputSensor: obj_type = GF_MEDIA_OBJECT_INTERACT; break; + + /*MPEG4/X3D*/ + case TAG_MPEG4_MovieTexture: case TAG_X3D_MovieTexture: obj_type = GF_MEDIA_OBJECT_VIDEO; break; + case TAG_MPEG4_Background2D: obj_type = GF_MEDIA_OBJECT_VIDEO; break; + case TAG_MPEG4_Background: case TAG_X3D_Background: obj_type = GF_MEDIA_OBJECT_VIDEO; break; + case TAG_MPEG4_ImageTexture: case TAG_X3D_ImageTexture: obj_type = GF_MEDIA_OBJECT_VIDEO; break; + case TAG_MPEG4_AudioClip: case TAG_X3D_AudioClip: obj_type = GF_MEDIA_OBJECT_AUDIO; break; + case TAG_MPEG4_Inline: case TAG_X3D_Inline: obj_type = GF_MEDIA_OBJECT_SCENE; break; + + /*SVG*/ +#ifndef GPAC_DISABLE_SVG + case TAG_SVG_audio: + obj_type = GF_MEDIA_OBJECT_AUDIO; + if (gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_syncReference, 0, 0, &info)==GF_OK) { + syncRef = get_sync_reference(is, info.far_ptr, GF_MEDIA_OBJECT_UNDEF, node, &post_pone); + /*syncRef is specified but doesn't exist yet, post-pone*/ + if (post_pone) return NULL; + } + break; + case TAG_SVG_video: + obj_type = GF_MEDIA_OBJECT_VIDEO; + if (gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_syncReference, 0, 0, &info)==GF_OK) { + syncRef = get_sync_reference(is, info.far_ptr, GF_MEDIA_OBJECT_UNDEF, node, &post_pone); + /*syncRef is specified but doesn't exist yet, post-pone*/ + if (post_pone) return NULL; + } + break; + case TAG_SVG_image: + obj_type = GF_MEDIA_OBJECT_VIDEO; + break; +#endif + + default: obj_type = GF_MEDIA_OBJECT_UNDEF; break; + } + + /*move to primary resource handler*/ + while (is->secondary_resource && is->root_od->parentscene) + is = is->root_od->parentscene; + + res = gf_inline_get_media_object_ex(is, url, obj_type, lock_timelines, syncRef, 0, node); + + if (res) { + } + return res; +} + +void gf_mo_unregister(GF_Node *node, GF_MediaObject *mo) +{ + if (mo && node) { + gf_list_del_item(mo->nodes, node); + } +} + +GF_MediaObject *gf_mo_new() +{ + GF_MediaObject *mo; + mo = (GF_MediaObject *) malloc(sizeof(GF_MediaObject)); + memset(mo, 0, sizeof(GF_MediaObject)); + mo->speed = FIX_ONE; + mo->URLs.count = 0; + mo->URLs.vals = NULL; + mo->nodes = gf_list_new(); + return mo; +} + + +GF_EXPORT +Bool gf_mo_get_visual_info(GF_MediaObject *mo, u32 *width, u32 *height, u32 *stride, u32 *pixel_ar, u32 *pixelFormat) +{ + GF_CodecCapability cap; + if ((mo->type != GF_MEDIA_OBJECT_VIDEO) && (mo->type!=GF_MEDIA_OBJECT_TEXT)) return 0; + + if (width) { + cap.CapCode = GF_CODEC_WIDTH; + gf_codec_get_capability(mo->odm->codec, &cap); + *width = cap.cap.valueInt; + } + if (height) { + cap.CapCode = GF_CODEC_HEIGHT; + gf_codec_get_capability(mo->odm->codec, &cap); + *height = cap.cap.valueInt; + } + if (mo->type==GF_MEDIA_OBJECT_TEXT) return 1; + + if (stride) { + cap.CapCode = GF_CODEC_STRIDE; + gf_codec_get_capability(mo->odm->codec, &cap); + *stride = cap.cap.valueInt; + } + if (pixelFormat) { + cap.CapCode = GF_CODEC_PIXEL_FORMAT; + gf_codec_get_capability(mo->odm->codec, &cap); + *pixelFormat = cap.cap.valueInt; + } + /*get PAR settings*/ + if (pixel_ar) { + cap.CapCode = GF_CODEC_PAR; + gf_codec_get_capability(mo->odm->codec, &cap); + *pixel_ar = cap.cap.valueInt; + if (! (*pixel_ar & 0x0000FFFF)) *pixel_ar = 0; + if (! (*pixel_ar & 0xFFFF0000)) *pixel_ar = 0; + + /**/ + if (! *pixel_ar) { + GF_Channel *ch; + GF_NetworkCommand com; + com.base.command_type = GF_NET_CHAN_GET_PIXEL_AR; + ch = gf_list_get(mo->odm->channels, 0); + if (!ch) return 0; + + com.base.on_channel = ch; + com.par.hSpacing = com.par.vSpacing = 0; + if (gf_term_service_command(ch->service, &com) == GF_OK) { + if ((com.par.hSpacing>65535) || (com.par.vSpacing>65535)) { + com.par.hSpacing>>=16; + com.par.vSpacing>>=16; + } + if (com.par.hSpacing|| com.par.vSpacing) + *pixel_ar = (com.par.hSpacing<<16) | com.par.vSpacing; + } + } + } + return 1; +} + +GF_EXPORT +Bool gf_mo_get_audio_info(GF_MediaObject *mo, u32 *sample_rate, u32 *bits_per_sample, u32 *num_channels, u32 *channel_config) +{ + GF_CodecCapability cap; + if (!mo->odm || !mo->odm->codec || (mo->type != GF_MEDIA_OBJECT_AUDIO)) return 0; + + if (sample_rate) { + cap.CapCode = GF_CODEC_SAMPLERATE; + gf_codec_get_capability(mo->odm->codec, &cap); + *sample_rate = cap.cap.valueInt; + } + if (num_channels) { + cap.CapCode = GF_CODEC_NB_CHAN; + gf_codec_get_capability(mo->odm->codec, &cap); + *num_channels = cap.cap.valueInt; + } + if (bits_per_sample) { + cap.CapCode = GF_CODEC_BITS_PER_SAMPLE; + gf_codec_get_capability(mo->odm->codec, &cap); + *bits_per_sample = cap.cap.valueInt; + } + if (channel_config) { + cap.CapCode = GF_CODEC_CHANNEL_CONFIG; + gf_codec_get_capability(mo->odm->codec, &cap); + *channel_config = cap.cap.valueInt; + } + return 1; +} + +void gf_mo_update_caps(GF_MediaObject *mo) +{ + GF_CodecCapability cap; + + mo->flags &= ~GF_MO_IS_INIT; + + if (mo->type == GF_MEDIA_OBJECT_VIDEO) { + cap.CapCode = GF_CODEC_FPS; + gf_codec_get_capability(mo->odm->codec, &cap); + mo->odm->codec->fps = cap.cap.valueFloat; + } + else if (mo->type == GF_MEDIA_OBJECT_AUDIO) { + u32 sr, nb_ch, bps; + sr = nb_ch = bps = 0; + gf_mo_get_audio_info(mo, &sr, &bps, &nb_ch, NULL); + mo->odm->codec->bytes_per_sec = sr * nb_ch * bps / 8; + } +} + +GF_EXPORT +char *gf_mo_fetch_data(GF_MediaObject *mo, Bool resync, Bool *eos, u32 *timestamp, u32 *size) +{ + u32 obj_time; + GF_CMUnit *CU; + *eos = 0; + + if (!gf_odm_lock_mo(mo)) return NULL; + + if (!mo->odm->codec || !mo->odm->codec->CB) { + gf_odm_lock(mo->odm, 0); + return NULL; + } + + /*if frame locked return it*/ + if (mo->nb_fetch) { + *eos = 0; + *timestamp = mo->timestamp; + *size = mo->framesize; + mo->nb_fetch ++; + gf_odm_lock(mo->odm, 0); + return mo->frame; + } + + /*end of stream */ + *eos = gf_cm_is_eos(mo->odm->codec->CB); + + /*not running and no resync (ie audio)*/ + if (!resync && !gf_cm_is_running(mo->odm->codec->CB)) { + gf_odm_lock(mo->odm, 0); + return NULL; + } + + /*new frame to fetch, lock*/ + CU = gf_cm_get_output(mo->odm->codec->CB); + /*no output*/ + if (!CU || (CU->RenderedLength == CU->dataLength)) { + gf_odm_lock(mo->odm, 0); + return NULL; + } + + /*note this assert is NOT true when recomputing DTS from CTS on the fly (MPEG1/2 RTP and H264/AVC RTP)*/ + //assert(CU->TS >= mo->odm->codec->CB->LastRenderedTS); + + if (mo->odm->codec->CB->UnitCount==1) resync = 0; + + /*resync*/ + if (resync) { + u32 nb_droped = 0; + obj_time = gf_clock_time(mo->odm->codec->ck); + while (CU->TS < obj_time) { + if (!CU->next->dataLength) break; + /*figure out closest time*/ + if (CU->next->TS > obj_time) { + *eos = 0; + break; + } + nb_droped ++; + if (nb_droped>1) { + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[ODM%d] At OTB %d dropped frame TS %d\n", mo->odm->OD->objectDescriptorID, obj_time, CU->TS)); + mo->odm->codec->nb_droped++; + } + /*discard*/ + CU->RenderedLength = CU->dataLength = 0; + gf_cm_drop_output(mo->odm->codec->CB); + /*get next*/ + CU = gf_cm_get_output(mo->odm->codec->CB); + *eos = gf_cm_is_eos(mo->odm->codec->CB); + } + } + mo->framesize = CU->dataLength - CU->RenderedLength; + mo->frame = CU->data + CU->RenderedLength; + if (mo->timestamp != CU->TS) { + MS_UpdateTiming(mo->odm, *eos); + mo->timestamp = CU->TS; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] At OTB %d fetch frame TS %d size %d - %d unit in CB\n", mo->odm->OD->objectDescriptorID, gf_clock_time(mo->odm->codec->ck), mo->timestamp, mo->framesize, mo->odm->codec->CB->UnitCount)); + /*signal EOS after rendering last frame, not while rendering it*/ + *eos = 0; + } + + /*also adjust CU time based on consummed bytes in input, since some codecs output very large audio chunks*/ + if (mo->odm->codec->bytes_per_sec) mo->timestamp += CU->RenderedLength * 1000 / mo->odm->codec->bytes_per_sec; + + mo->nb_fetch ++; + *timestamp = mo->timestamp; + *size = mo->framesize; + + gf_odm_lock(mo->odm, 0); + return mo->frame; +} + +GF_EXPORT +void gf_mo_release_data(GF_MediaObject *mo, u32 nb_bytes, s32 forceDrop) +{ + u32 obj_time; + if (!gf_odm_lock_mo(mo)) return; + + if (!mo->nb_fetch || !mo->odm->codec) { + gf_odm_lock(mo->odm, 0); + return; + } + mo->nb_fetch--; + if (mo->nb_fetch) { + gf_odm_lock(mo->odm, 0); + return; + } + + /*perform a sanity check on TS since the CB may have changed status - this may happen in + temporal scalability only*/ + if (mo->odm->codec->CB->output->dataLength ) { + if (nb_bytes==0xFFFFFFFF) { + mo->odm->codec->CB->output->RenderedLength = mo->odm->codec->CB->output->dataLength; + } else { + assert(mo->odm->codec->CB->output->RenderedLength + nb_bytes <= mo->odm->codec->CB->output->dataLength); + mo->odm->codec->CB->output->RenderedLength += nb_bytes; + } + + if (forceDrop<0) { + /*only allow for explicit last frame keeping if only one node is using the resource + otherwise this would block the composition memory*/ + if (mo->num_open>1) forceDrop=0; + else { + gf_odm_lock(mo->odm, 0); + return; + } + } + + /*discard frame*/ + if (mo->odm->codec->CB->output->RenderedLength == mo->odm->codec->CB->output->dataLength) { + if (forceDrop) { + gf_cm_drop_output(mo->odm->codec->CB); + forceDrop--; +// if (forceDrop) mo->odm->codec->nb_droped++; + } else { + obj_time = gf_clock_time(mo->odm->codec->ck); + if (mo->odm->codec->CB->output->next->dataLength) { + if (2*obj_time < mo->timestamp + mo->odm->codec->CB->output->next->TS ) { + mo->odm->codec->CB->output->RenderedLength = 0; + } else { + gf_cm_drop_output(mo->odm->codec->CB); + } + } else { + gf_cm_drop_output(mo->odm->codec->CB); + } + } + } + } + gf_odm_lock(mo->odm, 0); +} + +GF_EXPORT +void gf_mo_get_object_time(GF_MediaObject *mo, u32 *obj_time) +{ + if (!gf_odm_lock_mo(mo)) return; + + /*regular media codec...*/ + if (mo->odm->codec) { + /*get absolute clock (without drift) for audio*/ + if (mo->odm->codec->type==GF_STREAM_AUDIO) + *obj_time = gf_clock_real_time(mo->odm->codec->ck); + else + *obj_time = gf_clock_time(mo->odm->codec->ck); + } + /*BIFS object */ + else if (mo->odm->subscene && mo->odm->subscene->scene_codec) { + *obj_time = gf_clock_time(mo->odm->subscene->scene_codec->ck); + } + /*unknown / unsupported object*/ + else { + *obj_time = 0; + } + gf_odm_lock(mo->odm, 0); +} + + +GF_EXPORT +void gf_mo_play(GF_MediaObject *mo, Double clipBegin, Double clipEnd, Bool can_loop) +{ + if (!mo) return; + + if (!mo->num_open && mo->odm) { + Bool is_restart = 0; + + /*remove object from media queue*/ + gf_term_lock_net(mo->odm->term, 1); + gf_list_del_item(mo->odm->term->media_queue, mo->odm); + gf_term_lock_net(mo->odm->term, 0); + + if (mo->odm->media_start_time == (u64) -1) is_restart = 1; + + if (mo->odm->flags & GF_ODM_NO_TIME_CTRL) { + mo->odm->media_start_time = 0; + } else { + mo->odm->media_start_time = (u64) (clipBegin*1000); + if (mo->odm->duration && (mo->odm->media_start_time > mo->odm->duration)) { + if (can_loop) { + mo->odm->media_start_time %= mo->odm->duration; + } else { + mo->odm->media_start_time = mo->odm->duration; + } + } + if (clipEnd>=clipBegin) { + mo->odm->media_stop_time = (u64) (clipEnd*1000); + if (mo->odm->duration && (mo->odm->media_stop_time > mo->odm->duration)) { + mo->odm->media_stop_time = 0; + } + } else { + mo->odm->media_stop_time = 0; + } + } + if (is_restart) { + MC_Restart(mo->odm); + } else { + if (mo->odm->subscene && mo->odm->subscene->is_dynamic_scene) + mo->odm->flags |= GF_ODM_REGENERATE_SCENE; + + gf_odm_start(mo->odm); + } + } else if (mo->odm) { + if (mo->num_to_restart) mo->num_restart--; + if (!mo->num_restart && (mo->num_to_restart==mo->num_open+1) ) { + MC_Restart(mo->odm); + mo->num_to_restart = mo->num_restart = 0; + } + } + mo->num_open++; +} + +GF_EXPORT +void gf_mo_stop(GF_MediaObject *mo) +{ + if (!mo || !mo->num_open) return; + + mo->num_open--; + if (!mo->num_open && mo->odm) { + /*do not stop directly, this can delete channel data currently being decoded (BIFS anim & co)*/ + gf_mx_p(mo->odm->term->net_mx); + /*if object not in media queue, add it*/ + if (gf_list_find(mo->odm->term->media_queue, mo->odm)<0) + gf_list_add(mo->odm->term->media_queue, mo->odm); + + /*signal STOP request*/ + mo->odm->media_start_time = (u64)-1; + + gf_mx_v(mo->odm->term->net_mx); + } else { + if (!mo->num_to_restart) { + mo->num_restart = mo->num_to_restart = mo->num_open + 1; + } + } +} + +GF_EXPORT +void gf_mo_restart(GF_MediaObject *mo) +{ + MediaControlStack *ctrl; + if (!gf_odm_lock_mo(mo)) return; + + ctrl = ODM_GetMediaControl(mo->odm); + /*if no control and not root of a scene, check timelines are unlocked*/ + if (!ctrl && !mo->odm->subscene) { + /*don't restart if sharing parent scene clock*/ + if (gf_odm_shares_clock(mo->odm, gf_odm_get_media_clock(mo->odm->parentscene->root_od))) { + gf_odm_lock(mo->odm, 0); + return; + } + } + /*all other cases, call restart to take into account clock references*/ + MC_Restart(mo->odm); + gf_odm_lock(mo->odm, 0); +} + +GF_EXPORT +Bool gf_mo_url_changed(GF_MediaObject *mo, MFURL *url) +{ + u32 od_id; + Bool ret = 0; + if (!mo) return (url ? 1 : 0); + od_id = URL_GetODID(url); + if ( (mo->OD_ID == GF_ESM_DYNAMIC_OD_ID) && (od_id == GF_ESM_DYNAMIC_OD_ID)) { + ret = !gf_mo_is_same_url(mo, url); + } else { + ret = (mo->OD_ID == od_id) ? 0 : 1; + } + /*special case for 3GPP text: if not playing and user node changed, force removing it*/ + if (ret && mo->odm && !mo->num_open && (mo->type == GF_MEDIA_OBJECT_TEXT)) { + mo->flags |= GF_MO_DISPLAY_REMOVE; + gf_term_stop_codec(mo->odm->codec); + } + return ret; +} + +GF_EXPORT +void gf_mo_pause(GF_MediaObject *mo) +{ + if (!mo || !mo->num_open || !mo->odm) return; + + MC_Pause(mo->odm); +} + +GF_EXPORT +void gf_mo_resume(GF_MediaObject *mo) +{ + if (!mo || !mo->num_open || !mo->odm) return; + + MC_Resume(mo->odm); +} + +GF_EXPORT +void gf_mo_set_speed(GF_MediaObject *mo, Fixed speed) +{ + MediaControlStack *ctrl; + + if (!mo) return; + if (!mo->odm) { + mo->speed = speed; + return; + } + /*if media control forbidd that*/ + ctrl = ODM_GetMediaControl(mo->odm); + if (ctrl) return; + gf_odm_set_speed(mo->odm, speed); +} + +GF_EXPORT +Fixed gf_mo_get_speed(GF_MediaObject *mo, Fixed in_speed) +{ + Fixed res; + MediaControlStack *ctrl; + if (!gf_odm_lock_mo(mo)) return in_speed; + /*get control*/ + ctrl = ODM_GetMediaControl(mo->odm); + res = ctrl ? ctrl->control->mediaSpeed : in_speed; + gf_odm_lock(mo->odm, 0); + return res; +} + +GF_EXPORT +Bool gf_mo_get_loop(GF_MediaObject *mo, Bool in_loop) +{ + GF_Clock *ck; + MediaControlStack *ctrl; + if (!gf_odm_lock_mo(mo)) return in_loop; + + /*get control*/ + ctrl = ODM_GetMediaControl(mo->odm); + if (ctrl) in_loop = ctrl->control->loop; + + /*otherwise looping is only accepted if not sharing parent scene clock*/ + ck = gf_odm_get_media_clock(mo->odm->parentscene->root_od); + if (gf_odm_shares_clock(mo->odm, ck)) in_loop = 0; + gf_odm_lock(mo->odm, 0); + return in_loop; +} + +GF_EXPORT +Double gf_mo_get_duration(GF_MediaObject *mo) +{ + Double dur; + if (!gf_odm_lock_mo(mo)) return -1.0; + dur = ((Double) (s64)mo->odm->duration)/1000.0; + gf_odm_lock(mo->odm, 0); + return dur; +} + +GF_EXPORT +Bool gf_mo_should_deactivate(GF_MediaObject *mo) +{ + Bool res = 0; + MediaControlStack *ctrl; + + if (!gf_odm_lock_mo(mo)) return 0; + + if (!mo->odm->state) { + gf_odm_lock(mo->odm, 0); + return 0; + } + + /*get media control and see if object owning control is running*/ + ctrl = ODM_GetMediaControl(mo->odm); + if (!ctrl) res = 1; + /*if ctrl and ctrl not ruling this mediaObject, deny deactivation*/ + else if (ctrl->stream->odm != mo->odm) res = 0; + /*this is currently under discussion in MPEG. for now we deny deactivation as soon as a mediaControl is here*/ + else if (ctrl->stream->odm->state) res = 0; + /*otherwise allow*/ + else res = 1; + + gf_odm_lock(mo->odm, 0); + return res; +} + +GF_EXPORT +Bool gf_mo_is_muted(GF_MediaObject *mo) +{ + Bool res = 0; + if (!gf_odm_lock_mo(mo)) return 0; + res = mo->odm->media_ctrl ? mo->odm->media_ctrl->control->mute : 0; + gf_odm_lock(mo->odm, 0); + return res; +} + +GF_EXPORT +Bool gf_mo_is_done(GF_MediaObject *mo) +{ + Bool res = 0; + GF_Codec *codec; + u64 dur; + if (!gf_odm_lock_mo(mo)) return 0; + + /*for natural media use composition buffer*/ + if (mo->odm->codec && mo->odm->codec->CB) + res = (mo->odm->codec->CB->Status==CB_STOP) ? 1 : 0; + + /*otherwise check EOS and time*/ + else { + codec = mo->odm->codec; + dur = mo->odm->duration; + if (!mo->odm->codec) { + if (!mo->odm->subscene) res = 0; + else { + codec = mo->odm->subscene->scene_codec; + dur = mo->odm->subscene->duration; + } + } + if (codec && (codec->Status==GF_ESM_CODEC_STOP)) { + /*codec is done, check by duration*/ + GF_Clock *ck = gf_odm_get_media_clock(mo->odm); + if (gf_clock_time(ck) > dur) res = 1; + } + } + gf_odm_lock(mo->odm, 0); + return res; +} + +/*resyncs clock - only audio objects are allowed to use this*/ +GF_EXPORT +void gf_mo_adjust_clock(GF_MediaObject *mo, s32 ms_drift) +{ + if (!mo || !mo->odm) return; + if (!mo->odm->codec || (mo->odm->codec->type != GF_STREAM_AUDIO) ) return; + gf_clock_adjust_drift(mo->odm->codec->ck, ms_drift); +} + +GF_EXPORT +u32 gf_mo_get_flags(GF_MediaObject *mo) +{ + return mo ? mo->flags : 0; +} + +GF_EXPORT +void gf_mo_set_flag(GF_MediaObject *mo, u32 flag, Bool set_on) +{ + if (mo) { + if (set_on) + mo->flags |= flag; + else + mo->flags &= ~flag; + } +} + +GF_EXPORT +u32 gf_mo_get_last_frame_time(GF_MediaObject *mo) +{ + return mo ? mo->timestamp : 0; +} + + +GF_EXPORT +Bool gf_mo_has_audio(GF_MediaObject *mo) +{ + char *sub_url, *ext; + u32 i; + GF_NetworkCommand com; + GF_ClientService *ns; + GF_InlineScene *is; + if (!mo || !mo->odm) return 0; + if (mo->type != GF_MEDIA_OBJECT_VIDEO) return 0; + + ns = mo->odm->net_service; + is = mo->odm->parentscene; + sub_url = strchr(ns->url, '#'); + for (i=0; iODlist); i++) { + GF_ObjectManager *odm = gf_list_get(is->ODlist, i); + if (odm->net_service != ns) continue; + if (!odm->mo) continue; + + if (sub_url) { + ext = odm->mo->URLs.count ? odm->mo->URLs.vals[0].url : NULL; + if (ext) ext = strchr(ext, '#'); + if (!ext || strcmp(sub_url, ext)) continue; + } + /*there is already an audio object in this service, do not recreate one*/ + if (odm->mo->type == GF_MEDIA_OBJECT_AUDIO) return 0; + } + memset(&com, 0, sizeof(GF_NetworkCommand) ); + com.command_type = GF_NET_SERVICE_HAS_AUDIO; + com.audio.base_url = mo->URLs.count ? mo->URLs.vals[0].url : NULL; + if (!com.audio.base_url) com.audio.base_url = ns->url; + if (gf_term_service_command(ns, &com) == GF_OK) return 1; + return 0; +} + +GF_EXPORT +GF_SceneGraph *gf_mo_get_scenegraph(GF_MediaObject *mo) +{ + if (!mo || !mo->odm || !mo->odm->subscene) return NULL; + return mo->odm->subscene->graph; +} + diff --git a/src/terminal/media_sensor.c b/src/terminal/media_sensor.c new file mode 100644 index 0000000..f11fb14 --- /dev/null +++ b/src/terminal/media_sensor.c @@ -0,0 +1,232 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "media_control.h" +#include + +/*render : setup media sensor and update timing in case of inline scenes*/ +void RenderMediaSensor(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_Clock *ck; + MediaSensorStack *st = (MediaSensorStack *)gf_node_get_private(node); + + if (is_destroy) { + /*unlink from OD*/ + if (st->stream && st->stream->odm) + gf_list_del_item(st->stream->odm->ms_stack, st); + + gf_list_del(st->seg); + free(st); + return; + } + + if (!st->stream) st->stream = gf_mo_register(node, &st->sensor->url, 0); + if (!st->stream || !st->stream->odm) return; + + if (!st->is_init) { + gf_list_add(st->stream->odm->ms_stack, st); + gf_odm_init_segments(st->stream->odm, st->seg, &st->sensor->url); + st->is_init = 1; + st->active_seg = 0; + + } + /*media sensor bound to natural media (audio, video) is updated when fetching the stream + data for rendering.*/ + + ck = NULL; + /*check inline scenes - if the scene is set to restart DON'T MODIFY SENSOR: since we need a 2 render + passes to restart inline, scene is considered as not running*/ + if (st->stream->odm->subscene && !st->stream->odm->subscene->needs_restart) { + if (st->stream->odm->subscene->scene_codec) ck = st->stream->odm->subscene->scene_codec->ck; + /*dynamic scene*/ + else ck = st->stream->odm->subscene->dyn_ck; + /*since audio may be used alone through an inline scene, we need to refresh the graph*/ + if (st->stream->odm->state) gf_term_invalidate_compositor(st->stream->odm->term); + } + /*check anim streams*/ + else if (st->stream->odm->codec && (st->stream->odm->codec->type==GF_STREAM_SCENE)) ck = st->stream->odm->codec->ck; + /*check OCR streams*/ + else if (st->stream->odm->ocr_codec) ck = st->stream->odm->ocr_codec->ck; + + if (ck && gf_clock_is_started(ck) ) { + st->stream->odm->current_time = gf_clock_time(ck); + MS_UpdateTiming(st->stream->odm, 0); + } +} + +void InitMediaSensor(GF_InlineScene *is, GF_Node *node) +{ + MediaSensorStack *st; + GF_SAFEALLOC(st, MediaSensorStack); + + st->parent = is; + st->sensor = (M_MediaSensor *)node; + st->seg = gf_list_new(); + gf_node_set_callback_function(node, RenderMediaSensor); + gf_node_set_private(node, st); + +} + +/*only URL can be changed, so reset and get new URL*/ +void MS_Modified(GF_Node *node) +{ + MediaSensorStack *st = (MediaSensorStack *)gf_node_get_private(node); + if (!st) return; + + while (gf_list_count(st->seg)) gf_list_rem(st->seg, 0); + + /*unlink from OD*/ + if (st->stream && st->stream->odm) + gf_list_del_item(st->stream->odm->ms_stack, st); + + gf_mo_unregister(node, st->stream); + st->stream = gf_mo_register(node, &st->sensor->url, 0); + st->is_init = 0; + gf_term_invalidate_compositor(st->parent->root_od->term); +} + +void MS_UpdateTiming(GF_ObjectManager *odm, Bool is_eos) +{ + GF_Segment *desc; + u32 i, count, j, ms_count; + Double time; + ms_count = gf_list_count(odm->ms_stack); + if (!ms_count) return; + + time = odm->current_time / 1000.0; + + for (j=0; jms_stack, j); + if (!media_sens->is_init) continue; + count = gf_list_count(media_sens->seg); + + /*full object controled*/ + if (!media_sens->active_seg && !count) { + if (!is_eos && !media_sens->sensor->isActive) { + media_sens->sensor->isActive = 1; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "isActive"); + if (odm->subscene) { + media_sens->sensor->mediaDuration = (Double) (s64)odm->subscene->duration; + } else { + media_sens->sensor->mediaDuration = (Double) (s64)odm->duration; + } + if (media_sens->sensor->mediaDuration) + media_sens->sensor->mediaDuration /= 1000; + else + media_sens->sensor->mediaDuration = -FIX_ONE; + + gf_node_event_out_str((GF_Node *) media_sens->sensor, "mediaDuration"); + } + if (media_sens->sensor->isActive && (media_sens->sensor->mediaCurrentTime != time)) { + media_sens->sensor->mediaCurrentTime = time; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "mediaCurrentTime"); + } + /*check for end of scene (MediaSensor on inline)*/ + if (odm->subscene && odm->subscene->duration) { + GF_Clock *ck = gf_odm_get_media_clock(odm); + if (ck->has_seen_eos && media_sens->sensor->isActive && (1000*time>=(Double) (s64)odm->subscene->duration)) { + media_sens->sensor->isActive = 0; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "isActive"); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[ODM%d] Deactivating media sensor\n", odm->OD->objectDescriptorID)); + } + } + if (is_eos && media_sens->sensor->isActive) { + media_sens->sensor->isActive = 0; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "isActive"); + } + continue; + } + + /*locate segment*/ + for (i=media_sens->active_seg; iseg, i); + /*not controled*/ + if (desc->startTime > time) { + if (media_sens->sensor->isActive) { + media_sens->sensor->isActive = 0; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "isActive"); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[ODM%d] Deactivating media sensor at time %g - segment %s\n", odm->OD->objectDescriptorID, time, desc->SegmentName)); + } + break; + } + if (desc->startTime + desc->Duration < time) continue; + if (desc->startTime + desc->Duration == time) { + continue; + } + /*segment switch, force activation (isActive TRUE send at each seg)*/ + if (media_sens->active_seg != i) { + media_sens->active_seg = i; + media_sens->sensor->isActive = 0; + } + + if (!media_sens->sensor->isActive) { + media_sens->sensor->isActive = 1; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "isActive"); + /*set info*/ + gf_sg_vrml_mf_reset(& media_sens->sensor->info, GF_SG_VRML_MFSTRING); + gf_sg_vrml_mf_alloc(& media_sens->sensor->info, GF_SG_VRML_MFSTRING, 1); + media_sens->sensor->info.vals[0] = desc->SegmentName ? strdup(desc->SegmentName) : NULL; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "info"); + /*set duration*/ + media_sens->sensor->mediaDuration = desc->Duration; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "mediaDuration"); + /*set seg start time*/ + media_sens->sensor->streamObjectStartTime = desc->startTime; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "streamObjectStartTime"); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[ODM%d] Activating media sensor time %g - segment %s\n", odm->OD->objectDescriptorID, time, desc->SegmentName)); + } + /*set media time - relative to segment start time*/ + time -= desc->startTime; + if (media_sens->sensor->mediaCurrentTime != time) { + media_sens->sensor->mediaCurrentTime = time; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "mediaCurrentTime"); + } + break; + } + if (i==count) { + /*we're after last segment, deactivate*/ + if (media_sens->sensor->isActive) { + media_sens->sensor->isActive = 0; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "isActive"); + media_sens->active_seg = count; + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[ODM%d] Deactivating media sensor at time %g: no more segments\n", odm->OD->objectDescriptorID, time)); + } + } + } +} + +void MS_Stop(MediaSensorStack *st) +{ + if (st->sensor->isActive) { + st->sensor->isActive = 0; + gf_node_event_out_str((GF_Node *) st->sensor, "isActive"); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[ODM%d] Deactivating media sensor\n", st->stream->odm->OD->objectDescriptorID)); + } + st->active_seg = 0; +} diff --git a/src/terminal/network_service.c b/src/terminal/network_service.c new file mode 100644 index 0000000..18a3337 --- /dev/null +++ b/src/terminal/network_service.c @@ -0,0 +1,1024 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + + + +#define GET_TERM() GF_Terminal *term = (GF_Terminal *) user_priv; if (!term) return; + +static GFINLINE GF_Channel *gf_term_get_channel(GF_ClientService *service, LPNETCHANNEL ns) +{ + GF_Channel *ch = (GF_Channel *)ns; + if (!service || !ch) return NULL; + if (ch->chan_id != (u32) ch) return NULL; + if (ch->service != service) return NULL; + return ch; +} + +static void term_on_message(void *user_priv, GF_ClientService *service, GF_Err error, const char *message) +{ + GET_TERM(); + + /*check for UDP timeout*/ + if (error==GF_IP_UDP_TIMEOUT) { + const char *sOpt = gf_cfg_get_key(term->user->config, "Network", "AutoReconfigUDP"); + if (sOpt && !stricmp(sOpt, "yes")) { + char szMsg[1024]; + sprintf(szMsg, "!! UDP down (%s) - Retrying with TCP !!\n", message); + gf_term_message(term, service->url, szMsg, GF_OK); + + /*reload scene*/ + if (term->reload_url) free(term->reload_url); + term->reload_state = 1; + term->reload_url = strdup(term->root_scene->root_od->net_service->url); + gf_cfg_set_key(term->user->config, "Network", "UDPNotAvailable", "yes"); + return; + } + } + gf_term_message(term, service->url, message, error); +} + +static void term_on_connect(void *user_priv, GF_ClientService *service, LPNETCHANNEL netch, GF_Err err) +{ + GF_Channel *ch; + GF_ObjectManager *root; + GET_TERM(); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Connection ACK received from %s (channel %d) - %s\n", service->url, (u32) netch, gf_error_to_string(err) )); + + root = service->owner; + if (root && (root->net_service != service)) { + gf_term_message(term, service->url, "Incomaptible module type", GF_SERVICE_ERROR); + return; + } + /*this is service connection*/ + if (!netch) { + gf_term_service_media_event(service->owner, GF_EVENT_MEDIA_END_SESSION_SETUP); + if (err) { + char msg[5000]; + sprintf(msg, "Cannot open %s", service->url); + gf_term_message(term, service->url, msg, err); + + gf_term_service_media_event(service->owner, GF_EVENT_MEDIA_ERROR); + + /*destroy service only if attached*/ + if (root) { + gf_term_lock_net(term, 1); + service->ifce->CloseService(service->ifce); + root->net_service = NULL; + service->owner = NULL; + /*depends on module: some module could forget to call gf_term_on_disconnect */ + if ( gf_list_del_item(term->net_services, service) >= 0) { + /*and queue for destroy*/ + gf_list_add(term->net_services_to_remove, service); + } + gf_term_lock_net(term, 0); + if (!root->parentscene) { + GF_Event evt; + evt.type = GF_EVENT_CONNECT; + evt.connect.is_connected = 0; + GF_USER_SENDEVENT(term->user, &evt); + } else { + /*try to reinsert OD for VRML/X3D with multiple URLs: + 1- first remove from parent scene without destroying object, this will trigger a re-setup + if other URLs are present + 2- then destroy object*/ + gf_inline_remove_object(root->parentscene, root, 0); + gf_odm_disconnect(root, 1); + } + return; + } + } + + if (!root) { + /*channel service connect*/ + u32 i; + GF_ChannelSetup *cs; + GF_List *ODs = gf_list_new(); + gf_term_lock_net(term, 1); + i=0; + while ((cs = (GF_ChannelSetup*)gf_list_enum(term->channels_pending, &i))) { + if (cs->ch->service != service) continue; + gf_list_rem(term->channels_pending, i-1); + i--; + /*even if error do setup (channel needs to be deleted)*/ + if (gf_odm_post_es_setup(cs->ch, cs->dec, err) == GF_OK) { + if (cs->ch->odm && (gf_list_find(ODs, cs->ch->odm)==-1) ) gf_list_add(ODs, cs->ch->odm); + } + free(cs); + } + gf_term_lock_net(term, 0); + /*finally setup all ODs concerned (we do this later in case of scalability)*/ + while (gf_list_count(ODs)) { + GF_ObjectManager *odm = (GF_ObjectManager*)gf_list_get(ODs, 0); + gf_list_rem(ODs, 0); + /*force re-setup*/ + gf_inline_setup_object(odm->parentscene, odm); + } + gf_list_del(ODs); + } else { + /*setup od*/ + gf_odm_setup_entry_point(root, service->url); + } + /*load cache if requested*/ + if (!err && term->enable_cache) { + err = gf_term_service_cache_load(service); + /*not a fatal error*/ + if (err) gf_term_message(term, "GPAC Cache", "Cannot load cache", err); + } + } + + /*this is channel connection*/ + ch = gf_term_get_channel(service, netch); + if (!ch) return; + + /*confirm channel connection even if error - this allow playback of objects even if not all streams are setup + */ + gf_term_lock_net(term, 1); + gf_es_on_connect(ch); + gf_term_lock_net(term, 0); + + if (err) { + gf_term_message(term, service->url, "Channel Connection Failed", err); + ch->es_state = GF_ESM_ES_UNAVAILABLE; +// return; + } + + /*Plays request are skiped until all channels are connected. We send a PLAY on the objecy in case + 1-OD user has requested a play + 2-this is a channel of the root OD + */ + if ( (ch->odm->mo && ch->odm->mo->num_open) + || !ch->odm->parentscene + ) { + gf_odm_start(ch->odm); + } +#if 0 + else if (ch->odm->codec && ch->odm->codec->ck && ch->odm->codec->ck->no_time_ctrl) { + gf_odm_play(ch->odm); + } +#endif +} + +static void term_on_disconnect(void *user_priv, GF_ClientService *service, LPNETCHANNEL netch, GF_Err response) +{ + GF_ObjectManager *root; + GF_Channel *ch; + GET_TERM(); + + /*may be null upon destroy*/ + root = service->owner; + if (root && (root->net_service != service)) { + if (root->net_service) gf_term_message(term, service->url, "Incompatible module type", GF_SERVICE_ERROR); + return; + } + /*this is service disconnect*/ + if (!netch) { + gf_term_lock_net(term, 1); + /*unregister from valid services*/ + if (gf_list_del_item(term->net_services, service)>=0) { + /*and queue for destroy*/ + gf_list_add(term->net_services_to_remove, service); + } + gf_term_lock_net(term, 0); + return; + } + /*this is channel disconnect*/ + + /*no notif in case of failure for disconnection*/ + ch = gf_term_get_channel(service, netch); + if (!ch) return; + /*signal channel state*/ + ch->es_state = GF_ESM_ES_DISCONNECTED; +} + +static void term_on_slp_received(void *user_priv, GF_ClientService *service, LPNETCHANNEL netch, char *data, u32 data_size, GF_SLHeader *hdr, GF_Err reception_status) +{ + GF_Channel *ch; + GET_TERM(); + + ch = gf_term_get_channel(service, netch); + if (!ch) + return; + + if (reception_status==GF_EOS) { + gf_es_on_eos(ch); + return; + } + /*otherwise dispatch with error code*/ + gf_es_receive_sl_packet(service, ch, data, data_size, hdr, reception_status); +} + +static Bool is_same_od(GF_ObjectDescriptor *od1, GF_ObjectDescriptor *od2) +{ + GF_ESD *esd1, *esd2; + if (gf_list_count(od1->ESDescriptors) != gf_list_count(od2->ESDescriptors)) return 0; + esd1 = gf_list_get(od1->ESDescriptors, 0); + if (!esd1) return 0; + esd2 = gf_list_get(od2->ESDescriptors, 0); + if (!esd2) return 0; + if (esd1->ESID==esd2->ESID) return 1; + return 0; +} + +static void term_on_media_add(void *user_priv, GF_ClientService *service, GF_Descriptor *media_desc, Bool no_scene_check) +{ + u32 i; + GF_MediaObject *the_mo; + GF_InlineScene *is; + GF_ObjectManager *odm, *root; + GF_ObjectDescriptor *od; + GET_TERM(); + + root = service->owner; + is = root->subscene ? root->subscene : root->parentscene; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[Service %s] %s\n", service->url, media_desc ? "Adding new media object" : "Regenerating scene graph")); + if (!media_desc) { + if (!no_scene_check) gf_inline_regenerate(is); + return; + } + + switch (media_desc->tag) { + case GF_ODF_OD_TAG: + case GF_ODF_IOD_TAG: + if (root && (root->net_service == service)) { + od = (GF_ObjectDescriptor *) media_desc; + break; + } + default: + gf_odf_desc_del(media_desc); + return; + } + + gf_term_lock_net(term, 1); + /*object declared this way are not part of an OD stream and are considered as dynamic*/ +// od->objectDescriptorID = GF_ESM_DYNAMIC_OD_ID; + + /*check if we have a mediaObject in the scene not attached and matching this object*/ + the_mo = NULL; + odm = NULL; + for (i=0; imedia_objects); i++) { + char *frag, *ext; + GF_ESD *esd; + GF_MediaObject *mo = gf_list_get(is->media_objects, i); + if (!mo->odm) continue; + /*already assigned object - this may happen since the compositor has no control on when objects are declared by the service, + therefore opening file#video and file#audio may result in the objects being declared twice if the service doesn't + keep track of declared objects*/ + if (mo->odm->OD) { + if (is_same_od(mo->odm->OD, od)) { + /*reassign OD ID*/ + mo->OD_ID = od->objectDescriptorID; + gf_odf_desc_del(media_desc); + gf_term_lock_net(term, 0); + return; + } + continue; + } + if (mo->OD_ID != GF_ESM_DYNAMIC_OD_ID) { + if (mo->OD_ID == od->objectDescriptorID) { + the_mo = mo; + odm = mo->odm; + break; + } + continue; + } + if (!mo->URLs.count || !mo->URLs.vals[0].url) continue; + + frag = NULL; + ext = strrchr(mo->URLs.vals[0].url, '#'); + if (ext) { + frag = strchr(ext, '='); + ext[0] = 0; + } + if (!strstr(service->url, mo->URLs.vals[0].url)) { + if (ext) ext[0] = '#'; + continue; + } + if (ext) ext[0] = '#'; + + esd = gf_list_get(od->ESDescriptors, 0); + /*match type*/ + switch (esd->decoderConfig->streamType) { + case GF_STREAM_VISUAL: + if (mo->type != GF_MEDIA_OBJECT_VIDEO) continue; + break; + case GF_STREAM_AUDIO: + if (mo->type != GF_MEDIA_OBJECT_AUDIO) continue; + break; + default: + continue; + } + if (frag) { + u32 frag_id = 0; + u32 ID = od->objectDescriptorID; + if (ID==GF_ESM_DYNAMIC_OD_ID) ID = esd->ESID; + frag++; + frag_id = atoi(frag); + if (ID!=frag_id) continue; + } + the_mo = mo; + odm = mo->odm; + break; + } + + if (!odm) { + odm = gf_odm_new(); + odm->term = term; + odm->parentscene = is; + gf_list_add(is->ODlist, odm); + } + odm->OD = od; + odm->mo = the_mo; + if (the_mo) the_mo->OD_ID = od->objectDescriptorID; + odm->flags |= GF_ODM_NOT_IN_OD_STREAM; + gf_term_lock_net(term, 0); + + gf_odm_setup_object(odm, service); + + /*OD inserted by service: resetup scene*/ + if (!no_scene_check && is->is_dynamic_scene) gf_inline_regenerate(is); +} + +static void term_on_command(void *user_priv, GF_ClientService *service, GF_NetworkCommand *com, GF_Err response) +{ + GF_Channel *ch; + GET_TERM(); + + if (com->command_type==GF_NET_BUFFER_QUERY) { + GF_List *od_list; + u32 i; + GF_ObjectManager *odm; + com->buffer.max = 0; + com->buffer.min = com->buffer.occupancy = (u32) -1; + if (!service->owner) { + com->buffer.occupancy = 0; + return; + } + + /*browse all channels in the scene, running on this service, and get buffer info*/ + od_list = NULL; + if (service->owner->parentscene) { + od_list = service->owner->parentscene->ODlist; + } else if (service->owner->subscene) { + od_list = service->owner->subscene->ODlist; + } + if (!od_list) { + com->buffer.occupancy = 0; + return; + } + /*get exclusive access to media scheduler, to make sure ODs are not being + manipulated*/ + gf_mx_p(term->mm_mx); + i=0; + while ((odm = (GF_ObjectManager*)gf_list_enum(od_list, &i))) { + u32 j, count; + count = gf_list_count(odm->channels); + for (j=0; jchannels, j); + if (ch->service != service) continue; + if (ch->es_state != GF_ESM_ES_RUNNING) continue; + if (!ch->MaxBuffer || ch->dispatch_after_db || ch->bypass_sl_and_db || ch->IsEndOfStream) continue; + if (ch->MaxBuffer>com->buffer.max) com->buffer.max = ch->MaxBuffer; + if (ch->MinBufferbuffer.min) com->buffer.min = ch->MinBuffer; + if ((ch->AU_Count > 2) && ((u32) ch->BufferTimebuffer.occupancy)) + com->buffer.occupancy = ch->BufferTime; + } + } + gf_mx_v(term->mm_mx); + if (com->buffer.occupancy==(u32) -1) com->buffer.occupancy = 0; + return; + } + if (com->command_type==GF_NET_SERVICE_INFO) { + GF_Event evt; + evt.type = GF_EVENT_METADATA; + GF_USER_SENDEVENT(term->user, &evt); + return; + } + + + if (!com->base.on_channel) return; + + + ch = gf_term_get_channel(service, com->base.on_channel); + if (!ch) return; + + switch (com->command_type) { + /*SL reconfiguration*/ + case GF_NET_CHAN_RECONFIG: + gf_term_lock_net(term, 1); + gf_es_reconfig_sl(ch, &com->cfg.sl_config); + gf_term_lock_net(term, 0); + return; + /*time mapping (TS to media-time)*/ + case GF_NET_CHAN_MAP_TIME: + ch->seed_ts = com->map_time.timestamp; + ch->ts_offset = (u32) (com->map_time.media_time*1000); + /* + if (gf_es_owns_clock(ch)) { + ch->ts_offset = (u32) (com->map_time.media_time*1000); + } else { + ch->ts_offset = gf_clock_time(ch->clock); + } + */ + gf_es_map_time(ch, com->map_time.reset_buffers); + break; + /*duration changed*/ + case GF_NET_CHAN_DURATION: + gf_odm_set_duration(ch->odm, ch, (u32) (1000*com->duration.duration)); + break; + case GF_NET_CHAN_BUFFER_QUERY: + if (ch->IsEndOfStream) { + com->buffer.max = com->buffer.min = com->buffer.occupancy = 0; + } else { + com->buffer.max = ch->MaxBuffer; + com->buffer.min = ch->MinBuffer; + com->buffer.occupancy = ch->BufferTime; + } + break; + case GF_NET_CHAN_DRM_CFG: + gf_term_lock_net(term, 1); + gf_es_config_drm(ch, &com->drm_cfg); + gf_term_lock_net(term, 0); + return; + case GF_NET_CHAN_GET_ESD: + gf_term_lock_net(term, 1); + com->cache_esd.esd = ch->esd; + com->cache_esd.is_iod_stream = (ch->odm->subscene /*&& (ch->odm->subscene->root_od==ch->odm)*/) ? 1 : 0; + gf_term_lock_net(term, 0); + return; + default: + return; + } +} + + +Bool net_check_interface(GF_InputService *ifce) +{ + if (!ifce->CanHandleURL) return 0; + if (!ifce->ConnectService) return 0; + if (!ifce->CloseService) return 0; + if (!ifce->ConnectChannel) return 0; + if (!ifce->DisconnectChannel) return 0; + if (!ifce->GetServiceDescriptor) return 0; + if (!ifce->ServiceCommand) return 0; + return 1; +} + +static void fetch_mime_io(void *dnld, GF_NETIO_Parameter *parameter) +{ + /*this is correct, however some shoutcast servers don't understand HEAD and don't reply at all + so don't use HEAD*/ +#if 0 + /*only get the content type*/ + if (parameter->msg_type==GF_NETIO_GET_METHOD) parameter->name = "HEAD"; +#endif + + +} + +static char *get_mime_type(GF_Terminal *term, const char *url, GF_Err *ret_code) +{ + char *mime_type; + GF_DownloadSession * sess; + + (*ret_code) = GF_OK; + if (strnicmp(url, "http", 4)) return NULL; + + sess = gf_dm_sess_new(term->downloader, (char *) url, GF_NETIO_SESSION_NOT_THREADED, fetch_mime_io, NULL, ret_code); + if (!sess) { + if (strstr(url, "rtsp://") || strstr(url, "rtp://") || strstr(url, "udp://") || strstr(url, "tcp://") ) (*ret_code) = GF_OK; + return NULL; + } + mime_type = (char *) gf_dm_sess_mime_type(sess); + if (mime_type) mime_type = strdup(mime_type); + else *ret_code = gf_dm_sess_last_error(sess); + gf_dm_sess_del(sess); + return mime_type; +} + + +static Bool check_extension(const char *szExtList, char *szExt) +{ + char szExt2[500]; + if (szExtList[0] != '"') return 0; + szExtList += 1; + + while (1) { + u32 i = 0; + while ((szExtList[0] != ' ') && (szExtList[0] != '"')) { + szExt2[i] = szExtList[0]; + i++; + szExtList++; + } + szExt2[i] = 0; + if (!strncmp(szExt, szExt2, strlen(szExt2))) return 1; + if (szExtList[0]=='"') break; + else szExtList++; + } + return 0; +} + + +static GF_InputService *gf_term_can_handle_service(GF_Terminal *term, const char *url, const char *parent_url, Bool no_mime_check, char **out_url, GF_Err *ret_code) +{ + u32 i; + GF_Err e; + char *sURL, *ext, *mime_type; + char szExt[500]; + GF_InputService *ifce; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Looking for plugin for URL %s\n", url)); + *out_url = NULL; + if (!url) { + (*ret_code) = GF_URL_ERROR; + return NULL; + } + + sURL = NULL; + if (parent_url) sURL = gf_url_concatenate(parent_url, url); + + /*path absolute*/ + if (!sURL) sURL = strdup(url); + + if (gf_url_is_local(sURL)) + gf_url_to_fs_path(sURL); + + if (no_mime_check) { + mime_type = NULL; + } else { + /*fetch a mime type if any. If error don't even attempt to open the service + TRYTOFIXME: it would be nice to reuse the downloader created while fetching the mime type, however + we don't know if the plugin will want it threaded or not.... + */ + mime_type = get_mime_type(term, sURL, &e); + if (e) { + free(sURL); + (*ret_code) = e; + return NULL; + } + } + + if (mime_type && + (!stricmp(mime_type, "text/plain") + || !stricmp(mime_type, "video/quicktime") + || !stricmp(mime_type, "application/octet-stream") + ) + ) { + free(mime_type); + mime_type = NULL; + } + + ifce = NULL; + + /*load from mime type*/ + if (mime_type) { + const char *sPlug = gf_cfg_get_key(term->user->config, "MimeTypes", mime_type); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Mime type found: %s\n", mime_type)); + if (sPlug) sPlug = strrchr(sPlug, '"'); + if (sPlug) { + sPlug += 2; + ifce = (GF_InputService *) gf_modules_load_interface_by_name(term->user->modules, sPlug, GF_NET_CLIENT_INTERFACE); + if (ifce && !net_check_interface(ifce) ) { + gf_modules_close_interface((GF_BaseInterface *) ifce); + ifce = NULL; + } + } + } + + + ext = strchr(sURL, '#'); + if (ext) { + char *anext; + ext[0] = 0; + anext = strrchr(sURL, '.'); + ext[0] = '#'; + ext = anext; + } else { + ext = strrchr(sURL, '.'); + } + if (ext && !stricmp(ext, ".gz")) { + char *anext; + ext[0] = 0; + anext = strrchr(sURL, '.'); + ext[0] = '.'; + ext = anext; + } + /*no mime type: either local or streaming. If streaming discard extension checking*/ + if (!ifce && !mime_type && strstr(sURL, "://") && strnicmp(sURL, "file://", 7)) ext = NULL; + + if (mime_type) free(mime_type); + + /*browse extensions for prefered module*/ + if (!ifce && ext) { + u32 keyCount; + strcpy(szExt, &ext[1]); + ext = strrchr(szExt, '#'); + if (ext) ext[0] = 0; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] No mime type found - checking by extension %s\n", szExt)); + keyCount = gf_cfg_get_key_count(term->user->config, "MimeTypes"); + for (i=0; iuser->config, "MimeTypes", i); + if (!sMime) continue; + sKey = gf_cfg_get_key(term->user->config, "MimeTypes", sMime); + if (!sKey) continue; + if (!check_extension(sKey, szExt)) continue; + sPlug = strrchr(sKey, '"'); + if (!sPlug) continue; /*bad format entry*/ + sPlug += 2; + + ifce = (GF_InputService *) gf_modules_load_interface_by_name(term->user->modules, sPlug, GF_NET_CLIENT_INTERFACE); + if (!ifce) continue; + if (!net_check_interface(ifce)) { + gf_modules_close_interface((GF_BaseInterface *) ifce); + ifce = NULL; + continue; + } + break; + } + } + + /*browse all modules*/ + if (!ifce) { + for (i=0; i< gf_modules_get_count(term->user->modules); i++) { + ifce = (GF_InputService *) gf_modules_load_interface(term->user->modules, i, GF_NET_CLIENT_INTERFACE); + if (!ifce) continue; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Checking if module %s supports URL %s\n", ifce->module_name, sURL)); + if (net_check_interface(ifce) && ifce->CanHandleURL(ifce, sURL)) break; + gf_modules_close_interface((GF_BaseInterface *) ifce); + ifce = NULL; + } + } + + if (!ifce) { + free(sURL); + (*ret_code) = GF_NOT_SUPPORTED; + return NULL; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Found input plugin %s for URL %s\n", ifce->module_name, sURL)); + *out_url = sURL; + return ifce; +} + +GF_ClientService *gf_term_service_new(GF_Terminal *term, struct _od_manager *owner, const char *url, GF_ClientService *parent_service, GF_Err *ret_code) +{ + char *sURL; + GF_ClientService *serv; + GF_InputService *ifce = gf_term_can_handle_service(term, url, parent_service ? parent_service->url : NULL, 0, &sURL, ret_code); + if (!ifce) return NULL; + + GF_SAFEALLOC(serv, GF_ClientService); + serv->term = term; + serv->owner = owner; + serv->ifce = ifce; + serv->url = sURL; + serv->Clocks = gf_list_new(); + serv->dnloads = gf_list_new(); + gf_list_add(term->net_services, serv); + + return serv; +} + +Bool gf_term_is_supported_url(GF_Terminal *term, const char *fileName, Bool use_parent_url, Bool no_mime_check) +{ + GF_InputService *ifce; + GF_Err e; + char *sURL; + char *parent_url = NULL; + if (use_parent_url && term->root_scene) parent_url = term->root_scene->root_od->net_service->url; + + ifce = gf_term_can_handle_service(term, fileName, parent_url, no_mime_check, &sURL, &e); + if (!ifce) return 0; + gf_modules_close_interface((GF_BaseInterface *) ifce); + free(sURL); + return 1; +} + + +Bool gf_term_service_can_handle_url(GF_ClientService *ns, char *url) +{ + /*if no owner attached the service is being deleted, don't query it*/ + if (!ns->owner || !ns->ifce->CanHandleURLInService) return 0; + return ns->ifce->CanHandleURLInService(ns->ifce, url); +} + +GF_Err gf_term_service_command(GF_ClientService *ns, GF_NetworkCommand *com) +{ + return ns->ifce->ServiceCommand(ns->ifce, com); +} +GF_Err gf_term_channel_get_sl_packet(GF_ClientService *ns, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) +{ + if (!ns->ifce->ChannelGetSLP) return GF_NOT_SUPPORTED; + return ns->ifce->ChannelGetSLP(ns->ifce, channel, out_data_ptr, out_data_size, out_sl_hdr, sl_compressed, out_reception_status, is_new_data); +} +GF_Err gf_term_channel_release_sl_packet(GF_ClientService *ns, LPNETCHANNEL channel) +{ + if (!ns->ifce->ChannelGetSLP) return GF_NOT_SUPPORTED; + return ns->ifce->ChannelReleaseSLP(ns->ifce, channel); +} + +GF_EXPORT +void gf_term_on_message(GF_ClientService *service, GF_Err error, const char *message) +{ + assert(service); + term_on_message(service->term, service, error, message); +} +GF_EXPORT +void gf_term_on_connect(GF_ClientService *service, LPNETCHANNEL ns, GF_Err response) +{ + assert(service); + term_on_connect(service->term, service, ns, response); +} +GF_EXPORT +void gf_term_on_disconnect(GF_ClientService *service, LPNETCHANNEL ns, GF_Err response) +{ + assert(service); + term_on_disconnect(service->term, service, ns, response); +} +GF_EXPORT +void gf_term_on_command(GF_ClientService *service, GF_NetworkCommand *com, GF_Err response) +{ + assert(service); + term_on_command(service->term, service, com, response); +} +GF_EXPORT +void gf_term_on_sl_packet(GF_ClientService *service, LPNETCHANNEL ns, char *data, u32 data_size, GF_SLHeader *hdr, GF_Err reception_status) +{ + assert(service); + term_on_slp_received(service->term, service, ns, data, data_size, hdr, reception_status); +} + +GF_EXPORT +void gf_term_add_media(GF_ClientService *service, GF_Descriptor *media_desc, Bool no_scene_check) +{ + term_on_media_add(service->term, service, media_desc, no_scene_check); +} + +GF_EXPORT +const char *gf_term_get_service_url(GF_ClientService *service) +{ + if (!service) return NULL; + return service->url; +} + +void NM_DeleteService(GF_ClientService *ns) +{ + const char *sOpt = gf_cfg_get_key(ns->term->user->config, "StreamingCache", "AutoSave"); + if (ns->cache) gf_term_service_cache_close(ns, (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + gf_modules_close_interface((GF_BaseInterface *)ns->ifce); + free(ns->url); + + assert(!ns->nb_odm_users); + assert(!ns->nb_ch_users); + assert(!ns->owner); + + /*delete all the clocks*/ + while (gf_list_count(ns->Clocks)) { + GF_Clock *ck = (GF_Clock *)gf_list_get(ns->Clocks, 0); + gf_list_rem(ns->Clocks, 0); + gf_clock_del(ck); + } + gf_list_del(ns->Clocks); + + assert(!gf_list_count(ns->dnloads)); + gf_list_del(ns->dnloads); + free(ns); +} + +GF_EXPORT +GF_InputService *gf_term_get_service_interface(GF_ClientService *serv) +{ + return serv ? serv->ifce : NULL; +} + +GF_EXPORT +GF_DownloadSession *gf_term_download_new(GF_ClientService *service, const char *url, u32 flags, gf_dm_user_io user_io, void *cbk) +{ + GF_Err e; + GF_DownloadSession * sess; + char *sURL; + if (!service || !user_io) return NULL; + + sURL = gf_url_concatenate(service->url, url); + /*path was absolute*/ + if (!sURL) sURL = strdup(url); + sess = gf_dm_sess_new(service->term->downloader, sURL, flags, user_io, cbk, &e); + free(sURL); + if (!sess) return NULL; + gf_dm_sess_set_private(sess, service); + gf_list_add(service->dnloads, sess); + return sess; +} + +GF_EXPORT +void gf_term_download_del(GF_DownloadSession * sess) +{ + GF_ClientService *serv; + if (!sess) return; + serv = (GF_ClientService *)gf_dm_sess_get_private(sess); + + /*avoid sending data back to user*/ + gf_dm_sess_abort(sess); + /*unregister from service*/ + gf_list_del_item(serv->dnloads, sess); + + /*same as service: this may be called in the downloader thread (typically when download fails) + so we must queue the downloader and let the term delete it later on*/ + gf_list_add(serv->term->net_services_to_remove, sess); +} + +GF_EXPORT +void gf_term_download_update_stats(GF_DownloadSession * sess) +{ + GF_ClientService *serv; + const char *szURI; + u32 total_size, bytes_done, net_status, bytes_per_sec; + + if (!sess) return; + + gf_dm_sess_get_stats(sess, NULL, &szURI, &total_size, &bytes_done, &bytes_per_sec, &net_status); + serv = (GF_ClientService *)gf_dm_sess_get_private(sess); + switch (net_status) { + case GF_NETIO_SETUP: + gf_term_on_message(serv, GF_OK, "Connecting"); + break; + case GF_NETIO_CONNECTED: + gf_term_on_message(serv, GF_OK, "Connected"); + break; + case GF_NETIO_WAIT_FOR_REPLY: + gf_term_on_message(serv, GF_OK, "Waiting for reply..."); + break; + case GF_NETIO_DATA_EXCHANGE: + /*notify some connection / ...*/ + if (total_size) { + GF_Event evt; + evt.type = GF_EVENT_PROGRESS; + evt.progress.progress_type = 1; + evt.progress.service = szURI; + evt.progress.total = total_size; + evt.progress.done = bytes_done; + GF_USER_SENDEVENT(serv->term->user, &evt); + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] %s received %d / %d\n", szURI, bytes_done, total_size)); + break; + } +} + +void gf_term_service_del(GF_ClientService *ns) +{ + /*this is a downloader session*/ + if (! * (u32 *) ns) { + gf_dm_sess_del((GF_DownloadSession * ) ns); + } else { + NM_DeleteService(ns); + } +} + +GF_EXPORT +void gf_term_register_mime_type(GF_InputService *ifce, const char *mimeType, const char *extList, const char *description) +{ + u32 len; + char *buf; + if (!ifce || !mimeType || !extList || !description) return; + + len = strlen(extList) + 3 + strlen(description) + 3 + strlen(ifce->module_name) + 1; + buf = (char*)malloc(sizeof(char)*len); + sprintf(buf, "\"%s\" ", extList); + strlwr(buf); + strcat(buf, "\""); + strcat(buf, description); + strcat(buf, "\" "); + strcat(buf, ifce->module_name); + gf_modules_set_option((GF_BaseInterface *)(GF_BaseInterface *)ifce, "MimeTypes", mimeType, buf); + free(buf); +} + +GF_EXPORT +Bool gf_term_check_extension(GF_InputService *ifce, const char *mimeType, const char *extList, const char *description, const char *fileExt) +{ + const char *szExtList; + char *ext, szExt[500]; + if (!ifce || !mimeType || !extList || !description || !fileExt) return 0; + /*this is a URL*/ + if ( (strlen(fileExt)>20) || strchr(fileExt, '/')) return 0; + + if (fileExt[0]=='.') fileExt++; + strcpy(szExt, fileExt); + strlwr(szExt); + ext = strchr(szExt, '#'); + if (ext) ext[0]=0; + + szExtList = gf_modules_get_option((GF_BaseInterface *)(GF_BaseInterface *)ifce, "MimeTypes", mimeType); + if (!szExtList) { + gf_term_register_mime_type(ifce, mimeType, extList, description); + szExtList = gf_modules_get_option((GF_BaseInterface *)(GF_BaseInterface *)ifce, "MimeTypes", mimeType); + } + if (!strstr(szExtList, ifce->module_name)) return 0; + return check_extension((char *)szExtList, szExt); +} + +GF_Err gf_term_service_cache_load(GF_ClientService *ns) +{ + GF_Err e; + const char *sOpt; + char szName[GF_MAX_PATH], szURL[1024]; + GF_NetworkCommand com; + u32 i; + GF_StreamingCache *mcache = NULL; + + /*is service cachable*/ + com.base.on_channel = NULL; + com.base.command_type = GF_NET_IS_CACHABLE; + if (ns->ifce->ServiceCommand(ns->ifce, &com) != GF_OK) return GF_OK; + + /*locate a cache*/ + for (i=0; i< gf_modules_get_count(ns->term->user->modules); i++) { + mcache = (GF_StreamingCache *) gf_modules_load_interface(ns->term->user->modules, i, GF_STREAMING_MEDIA_CACHE); + if (mcache && mcache->Open && mcache->Close && mcache->Write && mcache->ChannelGetSLP && mcache->ChannelReleaseSLP && mcache->ServiceCommand) break; + if (mcache) gf_modules_close_interface((GF_BaseInterface *)mcache); + mcache = NULL; + } + if (!mcache) return GF_NOT_SUPPORTED; + + sOpt = gf_cfg_get_key(ns->term->user->config, "StreamingCache", "RecordDirectory"); + if (!sOpt) sOpt = gf_cfg_get_key(ns->term->user->config, "General", "CacheDirectory"); + if (sOpt) { + strcpy(szName, sOpt); + if (szName[strlen(szName)-1]!='\\') strcat(szName, "\\"); + } else { + strcpy(szName, ""); + } + sOpt = gf_cfg_get_key(ns->term->user->config, "StreamingCache", "BaseFileName"); + if (sOpt) { + strcat(szName, sOpt); + } else { + char *sep; + strcat(szName, "rec_"); + sOpt = strrchr(ns->url, '/'); + if (!sOpt) sOpt = strrchr(ns->url, '\\'); + if (sOpt) sOpt += 1; + else { + sOpt = strstr(ns->url, "://"); + if (sOpt) sOpt += 3; + else sOpt = ns->url; + } + strcpy(szURL, sOpt); + sep = strrchr(szURL, '.'); + if (sep) sep[0] = 0; + for (i=0; iterm->user->config, "StreamingCache", "KeepExistingFiles"); + e = mcache->Open(mcache, ns, szName, (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + if (e) { + gf_modules_close_interface((GF_BaseInterface *)mcache); + return e; + } + ns->cache = mcache; + return GF_OK; +} + +GF_Err gf_term_service_cache_close(GF_ClientService *ns, Bool no_save) +{ + GF_Err e; + if (!ns->cache) return GF_OK; + e = ns->cache->Close(ns->cache, no_save); + + gf_modules_close_interface((GF_BaseInterface *)ns->cache); + ns->cache = NULL; + return e; +} diff --git a/src/terminal/object_browser.c b/src/terminal/object_browser.c new file mode 100644 index 0000000..0791388 --- /dev/null +++ b/src/terminal/object_browser.c @@ -0,0 +1,376 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include "media_memory.h" + + +/*WorldInfo node*/ +#include +/*title node*/ +#include + + +static Bool check_in_scene(GF_InlineScene *scene, GF_ObjectManager *odm) +{ + u32 i; + GF_ObjectManager *ptr, *root; + if (!scene) return 0; + root = scene->root_od; + if (odm == root) return 1; + scene = root->subscene; + + i=0; + while ((ptr = (GF_ObjectManager *)gf_list_enum(scene->ODlist, &i))) { + if (ptr == odm) return 1; + if (check_in_scene(ptr->subscene, odm)) return 1; + } + return 0; +} + +static Bool gf_term_check_odm(GF_Terminal *term, GF_ObjectManager *odm) +{ + if (!term->root_scene) return 0; + return check_in_scene(term->root_scene, odm); +} + + +/*returns top-level OD of the presentation*/ +GF_ObjectManager *gf_term_get_root_object(GF_Terminal *term) +{ + if (!term) return NULL; + if (!term->root_scene) return NULL; + return term->root_scene->root_od; +} + +/*returns number of sub-ODs in the current root. scene_od must be an inline OD*/ +u32 gf_term_get_object_count(GF_Terminal *term, GF_ObjectManager *scene_od) +{ + if (!term || !scene_od) return 0; + if (!gf_term_check_odm(term, scene_od)) return 0; + if (!scene_od->subscene) return 0; + return gf_list_count(scene_od->subscene->ODlist); +} + +/*returns indexed (0-based) OD manager in the scene*/ +GF_ObjectManager *gf_term_get_object(GF_Terminal *term, GF_ObjectManager *scene_od, u32 index) +{ + if (!term || !scene_od) return NULL; + if (!gf_term_check_odm(term, scene_od)) return NULL; + if (!scene_od->subscene) return NULL; + return (GF_ObjectManager *) gf_list_get(scene_od->subscene->ODlist, index); +} + +u32 gf_term_object_subscene_type(GF_Terminal *term, GF_ObjectManager *odm) +{ + Bool IS_IsProtoLibObject(GF_InlineScene *is, GF_ObjectManager *odm); + + if (!term || !odm) return 0; + if (!gf_term_check_odm(term, odm)) return 0; + + if (!odm->subscene) return 0; + if (odm->parentscene) return IS_IsProtoLibObject(odm->parentscene, odm) ? 3 : 2; + return 1; +} + +/*select given object when stream selection is available*/ +void gf_term_select_object(GF_Terminal *term, GF_ObjectManager *odm) +{ + if (!term || !odm) return; + if (!gf_term_check_odm(term, odm)) return; + + gf_inline_select_object(term->root_scene, odm); +} + + + +static void get_codec_stats(GF_Codec *dec, ODInfo *info) +{ + info->avg_bitrate = dec->avg_bit_rate; + info->max_bitrate = dec->max_bit_rate; + info->nb_dec_frames = dec->nb_dec_frames; + info->max_dec_time = dec->max_dec_time; + info->total_dec_time = dec->total_dec_time; +} + +GF_Err gf_term_get_object_info(GF_Terminal *term, GF_ObjectManager *odm, ODInfo *info) +{ + GF_Channel *ch; + + if (!term || !odm || !odm->OD || !info) return GF_BAD_PARAM; + if (!gf_term_check_odm(term, odm)) return GF_BAD_PARAM; + + memset(info, 0, sizeof(ODInfo)); + info->od = odm->OD; + + info->duration = (Double) (s64)odm->duration; + info->duration /= 1000; + if (odm->codec) { + /*since we don't remove ODs that failed setup, check for clock*/ + if (odm->codec->ck) info->current_time = odm->codec->CB ? odm->current_time : gf_clock_time(odm->codec->ck); + info->current_time /= 1000; + info->nb_droped = odm->codec->nb_droped; + } else if (odm->subscene && odm->subscene->scene_codec) { + if (odm->subscene->scene_codec->ck) { + info->current_time = gf_clock_time(odm->subscene->scene_codec->ck); + info->current_time /= 1000; + } + info->duration = (Double) (s64)odm->subscene->duration; + info->duration /= 1000; + info->nb_droped = odm->subscene->scene_codec->nb_droped; + } + + info->buffer = -2; + info->db_unit_count = 0; + + /*Warning: is_open==2 means object setup, don't check then*/ + if (odm->state==GF_ODM_STATE_IN_SETUP) { + info->status = 3; + } else if (odm->state==GF_ODM_STATE_BLOCKED) { + info->status = 0; + info->protection = 2; + } else if (odm->state) { + u32 i, buf; + GF_Clock *ck; + + ck = gf_odm_get_media_clock(odm); + /*no clock means setup failed*/ + if (!ck) { + info->status = 4; + } else { + info->status = gf_clock_is_started(ck) ? 1 : 2; + info->clock_drift = ck->drift; + + info->buffer = -1; + buf = 0; + i=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i))) { + info->db_unit_count += ch->AU_Count; + if (!ch->is_pulling) { + if (ch->MaxBuffer) info->buffer = 0; + buf += ch->BufferTime; + } + if (ch->is_protected) info->protection = ch->ipmp_tool ? 1 : 2; + + } + if (buf) info->buffer = (s32) buf; + } + } + + info->has_profiles = (odm->flags & GF_ODM_HAS_PROFILES) ? 1 : 0; + if (info->has_profiles) { + info->inline_pl = (odm->flags & GF_ODM_INLINE_PROFILES) ? 1 : 0; + info->OD_pl = odm->OD_PL; + info->scene_pl = odm->Scene_PL; + info->audio_pl = odm->Audio_PL; + info->visual_pl = odm->Visual_PL; + info->graphics_pl = odm->Graphics_PL; + } + + if (odm->net_service) { + info->service_handler = odm->net_service->ifce->module_name; + info->service_url = odm->net_service->url; + if (odm->net_service->owner == odm) info->owns_service = 1; + } else { + info->service_url = "Service not found or error"; + } + + if (odm->codec && odm->codec->decio) { + if (!odm->codec->decio->GetName) { + info->codec_name = odm->codec->decio->module_name; + } else { + info->codec_name = odm->codec->decio->GetName(odm->codec->decio); + } + info->od_type = odm->codec->type; + if (odm->codec->CB) { + info->cb_max_count = odm->codec->CB->Capacity; + info->cb_unit_count = odm->codec->CB->UnitCount; + } + } + + if (odm->subscene && odm->subscene->scene_codec) { + GF_BaseDecoder *dec = odm->subscene->scene_codec->decio; + assert(odm->subscene->root_od==odm) ; + info->od_type = odm->subscene->scene_codec->type; + if (!dec->GetName) { + info->codec_name = dec->module_name; + } else { + info->codec_name = dec->GetName(dec); + } + gf_sg_get_scene_size_info(odm->subscene->graph, &info->width, &info->height); + } else if (odm->mo) { + switch (info->od_type) { + case GF_STREAM_VISUAL: + gf_mo_get_visual_info(odm->mo, &info->width, &info->height, NULL, &info->par, &info->pixelFormat); + break; + case GF_STREAM_AUDIO: + gf_mo_get_audio_info(odm->mo, &info->sample_rate, &info->bits_per_sample, &info->num_channels, NULL); + info->clock_drift = 0; + break; + case GF_STREAM_TEXT: + gf_mo_get_visual_info(odm->mo, &info->width, &info->height, NULL, NULL, NULL); + break; + } + } + if (odm->subscene && odm->subscene->scene_codec) get_codec_stats(odm->subscene->scene_codec, info); + else if (odm->codec) get_codec_stats(odm->codec, info); + + ch = (GF_Channel*)gf_list_get(odm->channels, 0); + if (ch && ch->esd->langDesc) info->lang = ch->esd->langDesc->langCode; + return GF_OK; +} + + +Bool gf_term_get_download_info(GF_Terminal *term, GF_ObjectManager *odm, u32 *d_enum, const char **server, const char **path, u32 *bytes_done, u32 *total_bytes, u32 *bytes_per_sec) +{ + GF_DownloadSession * sess; + if (!term || !odm || !gf_term_check_odm(term, odm)) return 0; + if (odm->net_service->owner != odm) return 0; + + if (*d_enum >= gf_list_count(odm->net_service->dnloads)) return 0; + sess = (GF_DownloadSession*)gf_list_get(odm->net_service->dnloads, *d_enum); + if (!sess) return 0; + (*d_enum) ++; + gf_dm_sess_get_stats(sess, server, path, bytes_done, total_bytes, bytes_per_sec, NULL); + return 1; +} + +Bool gf_term_get_channel_net_info(GF_Terminal *term, GF_ObjectManager *odm, u32 *d_enum, u32 *chid, NetStatCommand *netcom, GF_Err *ret_code) +{ + GF_Channel *ch; + GF_NetworkCommand com; + if (!term || !odm || !gf_term_check_odm(term, odm)) return 0; + if (*d_enum >= gf_list_count(odm->channels)) return 0; + ch = (GF_Channel*)gf_list_get(odm->channels, *d_enum); + if (!ch) return 0; + (*d_enum) ++; + if (ch->is_pulling) { + (*ret_code) = GF_NOT_SUPPORTED; + return 1; + } + (*chid) = ch->esd->ESID; + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.base.on_channel = ch; + com.command_type = GF_NET_GET_STATS; + (*ret_code) = gf_term_service_command(ch->service, &com); + memcpy(netcom, &com.net_stats, sizeof(NetStatCommand)); + return 1; +} + +GF_Err gf_term_get_service_info(GF_Terminal *term, GF_ObjectManager *odm, NetInfoCommand *netinfo) +{ + GF_Err e; + GF_NetworkCommand com; + if (!term || !odm || !netinfo || !gf_term_check_odm(term, odm)) return GF_BAD_PARAM; + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.command_type = GF_NET_SERVICE_INFO; + e = gf_term_service_command(odm->net_service, &com); + memcpy(netinfo, &com.info, sizeof(NetInfoCommand)); + return e; +} + + +const char *gf_term_get_world_info(GF_Terminal *term, GF_ObjectManager *scene_od, GF_List *descriptions) +{ + GF_Node *info; + u32 i; + if (!term) return NULL; + info = NULL; + if (!scene_od) { + if (!term->root_scene) return NULL; + info = (GF_Node*)term->root_scene->world_info; + } else { + if (!gf_term_check_odm(term, scene_od)) return NULL; + info = (GF_Node*) (scene_od->subscene ? scene_od->subscene->world_info : scene_od->parentscene->world_info); + } + if (!info) return NULL; + + if (gf_node_get_tag(info) == TAG_SVG_title) { + /*FIXME*/ + //return ((SVG_titleElement *) info)->textContent; + return "TO FIX IN GPAC!!"; + } else { + M_WorldInfo *wi = (M_WorldInfo *) info; + if (descriptions) { + for (i=0; iinfo.count; i++) { + gf_list_add(descriptions, wi->info.vals[i]); + } + } + return wi->title.buffer; + } +} + + +GF_Err gf_term_dump_scene(GF_Terminal *term, char *rad_name, Bool xml_dump, Bool skip_protos, GF_ObjectManager *scene_od) +{ +#ifndef GPAC_READ_ONLY + GF_SceneGraph *sg; + GF_ObjectManager *odm; + GF_SceneDumper *dumper; + u32 mode; + char szExt[20], *ext; + GF_Err e; + + if (!term || !term->root_scene) return GF_BAD_PARAM; + if (!scene_od) { + if (!term->root_scene) return GF_BAD_PARAM; + odm = term->root_scene->root_od; + } else { + odm = scene_od; + if (!gf_term_check_odm(term, scene_od)) + odm = term->root_scene->root_od; + } + + if (odm->subscene) { + if (!odm->subscene->graph) return GF_IO_ERR; + sg = odm->subscene->graph; + } else { + if (!odm->parentscene->graph) return GF_IO_ERR; + sg = odm->parentscene->graph; + } + + mode = xml_dump ? GF_SM_DUMP_AUTO_XML : GF_SM_DUMP_AUTO_TXT; + /*figure out best dump format based on extension*/ + ext = strrchr(odm->net_service->url, '.'); + if (ext) { + strcpy(szExt, ext); + strlwr(szExt); + if (!strcmp(szExt, ".wrl")) mode = xml_dump ? GF_SM_DUMP_X3D_XML : GF_SM_DUMP_VRML; + else if(!strncmp(szExt, ".x3d", 4) || !strncmp(szExt, ".x3dv", 5) ) mode = xml_dump ? GF_SM_DUMP_X3D_XML : GF_SM_DUMP_X3D_VRML; + else if(!strncmp(szExt, ".bt", 3) || !strncmp(szExt, ".xmt", 4) || !strncmp(szExt, ".mp4", 4) ) mode = xml_dump ? GF_SM_DUMP_XMTA : GF_SM_DUMP_BT; + } + + dumper = gf_sm_dumper_new(sg, rad_name, ' ', mode); + if (!dumper) return GF_IO_ERR; + e = gf_sm_dump_graph(dumper, skip_protos, 0); + gf_sm_dumper_del(dumper); + return e; +#else + return GF_NOT_SUPPORTED; +#endif +} + diff --git a/src/terminal/object_manager.c b/src/terminal/object_manager.c new file mode 100644 index 0000000..60e99b0 --- /dev/null +++ b/src/terminal/object_manager.c @@ -0,0 +1,1788 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include "media_memory.h" +#include "media_control.h" + +#include "input_sensor.h" + +/*removes the channel ressources and destroy it*/ +void ODM_DeleteChannel(GF_ObjectManager *odm, struct _es_channel *ch); + +GF_EXPORT +GF_ObjectManager *gf_odm_new() +{ + GF_ObjectManager *tmp; + GF_SAFEALLOC(tmp, GF_ObjectManager); + if (!tmp) return NULL; + tmp->channels = gf_list_new(); + + tmp->Audio_PL = (u8) -1; + tmp->Graphics_PL = (u8) -1; + tmp->OD_PL = (u8) -1; + tmp->Scene_PL = (u8) -1; + tmp->Visual_PL = (u8) -1; + tmp->ms_stack = gf_list_new(); + tmp->mc_stack = gf_list_new(); + tmp->mx = gf_mx_new("ODM"); + return tmp; +} + +void gf_odm_del(GF_ObjectManager *odm) +{ + Bool lock; + u32 i; + MediaSensorStack *media_sens; + + /*make sure we are not in the media queue*/ + gf_mx_p(odm->term->net_mx); + gf_list_del_item(odm->term->media_queue, odm); + gf_mx_v(odm->term->net_mx); + + lock = gf_mx_try_lock(odm->mx); + i=0; + while ((media_sens = (MediaSensorStack *)gf_list_enum(odm->ms_stack, &i))) { + MS_Stop(media_sens); + /*and detach from stream object*/ + media_sens->is_init = 0; + } + if (odm->mo) odm->mo->odm = NULL; + + + gf_list_del(odm->channels); + gf_list_del(odm->ms_stack); + gf_list_del(odm->mc_stack); + gf_odf_desc_del((GF_Descriptor *)odm->OD); + assert (!odm->net_service); + if (lock) gf_mx_v(odm->mx); + gf_mx_del(odm->mx); + free(odm); +} + + +void gf_odm_lock(GF_ObjectManager *odm, u32 LockIt) +{ + assert(odm); + if (LockIt) + gf_mx_p(odm->mx); + else + gf_mx_v(odm->mx); +} + +Bool gf_odm_lock_mo(GF_MediaObject *mo) +{ + if (!mo || !mo->odm) return 0; + gf_odm_lock(mo->odm, 1); + /*the ODM may have been destroyed here !!*/ + if (!mo->odm) return 0; + return 1; +} + +GF_EXPORT +void gf_odm_disconnect(GF_ObjectManager *odm, Bool do_remove) +{ + GF_Channel *ch; + + gf_odm_stop(odm, 1); + + /*disconnect sub-scene*/ + if (odm->subscene) gf_inline_disconnect(odm->subscene, do_remove); + + /*no destroy*/ + if (!do_remove) return; + + gf_odm_lock(odm, 1); + + /*unload the decoders before deleting the channels to prevent any access fault*/ + if (odm->codec) gf_term_remove_codec(odm->term, odm->codec); + if (odm->ocr_codec) gf_term_remove_codec(odm->term, odm->ocr_codec); + if (odm->oci_codec) gf_term_remove_codec(odm->term, odm->oci_codec); + + /*then delete all the channels in this OD */ + while (gf_list_count(odm->channels)) { + ch = (GF_Channel*)gf_list_get(odm->channels, 0); +#if 0 + if (ch->clock->mc && ch->clock->mc->stream && ch->clock->mc->stream->odm==odm) { + ch->clock->mc->stream = NULL; + ch->clock->mc = NULL; + } +#endif + ODM_DeleteChannel(odm, ch); + } + + /*delete the decoders*/ + if (odm->codec) { + gf_codec_del(odm->codec); + odm->codec = NULL; + } + if (odm->ocr_codec) { + gf_codec_del(odm->ocr_codec); + odm->ocr_codec = NULL; + } + if (odm->oci_codec) { + gf_codec_del(odm->oci_codec); + odm->oci_codec = NULL; + } + + /*detach from network service */ + if (odm->net_service) { + GF_ClientService *ns = odm->net_service; + if (ns->owner == odm) { + if (ns->nb_odm_users) ns->nb_odm_users--; + /*detach it!!*/ + ns->owner = NULL; + /*try to assign a new root in case this is not scene shutdown*/ + if (ns->nb_odm_users && odm->parentscene) { + GF_ObjectManager *new_root; + u32 i = 0; + while ((new_root = (GF_ObjectManager *)gf_list_enum(odm->parentscene->ODlist, &i)) ) { + if (new_root == odm) continue; + if (new_root->net_service != ns) continue; + ns->owner = new_root; + break; + } + } + } + odm->net_service = NULL; + if (!ns->nb_odm_users) gf_term_close_services(odm->term, ns); + } + + gf_odm_lock(odm, 0); + + /*delete from the parent scene.*/ + if (odm->parentscene) { + gf_inline_remove_object(odm->parentscene, odm, do_remove); + if (odm->subscene) gf_inline_del(odm->subscene); + gf_odm_del(odm); + return; + } + + /*this is the scene root OD (may be a remote OD ..) */ + if (odm->term->root_scene) { + GF_Event evt; + assert(odm->term->root_scene == odm->subscene); + gf_inline_del(odm->subscene); + /*reset main pointer*/ + odm->term->root_scene = NULL; + + evt.type = GF_EVENT_CONNECT; + evt.connect.is_connected = 0; + GF_USER_SENDEVENT(odm->term->user, &evt); + } + + /*delete the ODMan*/ + gf_odm_del(odm); +} + +/*setup service for OD (extract IOD and go)*/ +void gf_odm_setup_entry_point(GF_ObjectManager *odm, const char *service_sub_url) +{ + u32 od_type; + char *ext; + char *sub_url = (char *) service_sub_url; + GF_Terminal *term; + GF_Descriptor *desc; + GF_IPMP_ToolList *toolList; + +// assert(odm->OD==NULL); + + term = odm->term; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM] Setting up root object for %s\n", odm->net_service->url)); + + if (odm->subscene) od_type = GF_MEDIA_OBJECT_SCENE; + else if (odm->mo) { + od_type = odm->mo->type; + if (!sub_url && odm->mo->URLs.count && odm->mo->URLs.vals[0].url) { + sub_url = odm->mo->URLs.vals[0].url; + } + } + else od_type = GF_MEDIA_OBJECT_UNDEF; + + /*for remote ODs, get expected OD type in case the service needs to generate the IOD on the fly*/ + if (odm->parentscene && odm->OD && odm->OD->URLString) { + GF_MediaObject *mo; + mo = gf_inline_find_object(odm->parentscene, odm->OD->objectDescriptorID, odm->OD->URLString); + if (mo) od_type = mo->type; + ext = strchr(odm->OD->URLString, '#'); + if (ext) sub_url = ext; + } + + desc = odm->net_service->ifce->GetServiceDescriptor(odm->net_service->ifce, od_type, sub_url); + if (odm->OD) return; + + if (!desc) { + /*if desc is NULL for a media, the media will be declared later by the service (gf_term_media_add)*/ + if (od_type != GF_MEDIA_OBJECT_SCENE) + return; + /*create empty service descriptor, this will automatically create a dynamic scene*/ + desc = gf_odf_desc_new(GF_ODF_OD_TAG); + } + odm->net_service->nb_odm_users++; + + if (!gf_list_count( ((GF_ObjectDescriptor*)desc)->ESDescriptors)) { + /*new subscene*/ + if (!odm->subscene) { + assert(odm->parentscene); + odm->subscene = gf_inline_new(odm->parentscene); + odm->subscene->root_od = odm; + } + } + + toolList = NULL; + switch (desc->tag) { + case GF_ODF_IOD_TAG: + { + GF_InitialObjectDescriptor *the_iod = (GF_InitialObjectDescriptor *)desc; + odm->OD = (GF_ObjectDescriptor*)malloc(sizeof(GF_ObjectDescriptor)); + memcpy(odm->OD, the_iod, sizeof(GF_ObjectDescriptor)); + odm->OD->tag = GF_ODF_OD_TAG; + /*Check P&Ls of this IOD*/ + odm->Audio_PL = the_iod->audio_profileAndLevel; + odm->Graphics_PL = the_iod->graphics_profileAndLevel; + odm->OD_PL = the_iod->OD_profileAndLevel; + odm->Scene_PL = the_iod->scene_profileAndLevel; + odm->Visual_PL = the_iod->visual_profileAndLevel; + odm->flags |= GF_ODM_HAS_PROFILES; + if (the_iod->inlineProfileFlag) odm->flags |= GF_ODM_INLINE_PROFILES; + toolList = the_iod->IPMPToolList; + free(the_iod); + } + break; + case GF_ODF_OD_TAG: + odm->Audio_PL = odm->Graphics_PL = odm->OD_PL = odm->Scene_PL = odm->Visual_PL = (u8) -1; + odm->OD = (GF_ObjectDescriptor *)desc; + break; + default: + gf_term_message(odm->term, odm->net_service->url, "MPEG4 Service Setup Failure", GF_ODF_INVALID_DESCRIPTOR); + goto err_exit; + } + + if (toolList) { + Bool ipmp_failed = 0; +/* + GF_IPMP_Tool *ipmpt; + i=0; + while ((ipmpt = gf_list_enum(toolList->ipmp_tools, &i))) { + if (!Term_CheckIPMPTool(odm->term, ipmpt)) { + ipmp_failed = 1; + break; + } + } +*/ + gf_odf_desc_del((GF_Descriptor *)toolList); + if (ipmp_failed) { + gf_term_message(odm->term, odm->net_service->url, "MPEG4 IPMP Setup Failure - cannot process content", GF_SERVICE_ERROR); + goto err_exit; + } + } + + gf_term_lock_net(term, 1); + gf_odm_setup_object(odm, odm->net_service); + gf_term_lock_net(term, 0); + + return; + +err_exit: + if (!odm->parentscene) { + GF_Event evt; + evt.type = GF_EVENT_CONNECT; + evt.connect.is_connected = 0; + GF_USER_SENDEVENT(odm->term->user, &evt); + } + +} + + +/*locate ESD by ID*/ +static GF_ESD *od_get_esd(GF_ObjectDescriptor *OD, u16 ESID) +{ + GF_ESD *esd; + u32 i = 0; + while ((esd = (GF_ESD *)gf_list_enum(OD->ESDescriptors, &i)) ) { + if (esd->ESID==ESID) return esd; + } + return NULL; +} + +static void ODM_SelectAlternateStream(GF_ObjectManager *odm, u32 lang_code, u8 stream_type) +{ + u32 i; + GF_ESD *esd; + u16 def_id, es_id; + + def_id = 0; + i=0; + while ( (esd = (GF_ESD *)gf_list_enum(odm->OD->ESDescriptors, &i)) ) { + if (esd->decoderConfig->streamType != stream_type) continue; + + if (!esd->langDesc) { + if (!def_id) def_id = esd->ESID; + continue; + } + if (esd->langDesc->langCode==lang_code) { + def_id = esd->ESID; + break; + } else if (!def_id) { + def_id = esd->ESID; + } + } + + /*remove all other media streams*/ + i=0; + while ((esd = (GF_ESD *)gf_list_enum(odm->OD->ESDescriptors, &i)) ) { + if (esd->decoderConfig->streamType != stream_type) continue; + + /*get base stream ID for this stream*/ + es_id = esd->ESID; + if (esd->dependsOnESID && (esd->dependsOnESID != es_id)) { + es_id = esd->dependsOnESID; + while (es_id) { + GF_ESD *base = od_get_esd(odm->OD, es_id); + if (!base) break; + /*forbidden except for BIFS->OD*/ + if (base->decoderConfig->streamType != stream_type) break; + if (!base->dependsOnESID) break; + es_id = base->dependsOnESID; + } + } + /*not part of same object as base, remove*/ + if (es_id != def_id) { + gf_list_del_item(odm->OD->ESDescriptors, esd); + gf_odf_desc_del((GF_Descriptor*) esd); + i--; + } + } +} + + +/*Validate the streams in this OD, and check if we have to setup an inline scene*/ +GF_Err ODM_ValidateOD(GF_ObjectManager *odm, Bool *hasInline) +{ + u32 i; + u16 es_id; + GF_ESD *esd, *base_scene; + const char *sOpt; + u32 lang, nb_od, nb_ocr, nb_scene, nb_mp7, nb_ipmp, nb_oci, nb_mpj, nb_other, prev_st; + + nb_od = nb_ocr = nb_scene = nb_mp7 = nb_ipmp = nb_oci = nb_mpj = nb_other = 0; + prev_st = 0; + + *hasInline = 0; + + /*step 1: validate OD*/ + i=0; + while ((esd = (GF_ESD *)gf_list_enum(odm->OD->ESDescriptors, &i))) { + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: nb_od++; break; + case GF_STREAM_OCR: nb_ocr++; break; + case GF_STREAM_SCENE: nb_scene++; break; + case GF_STREAM_MPEG7: nb_mp7++; break; + case GF_STREAM_IPMP: nb_ipmp++; break; + case GF_STREAM_OCI: nb_oci++; break; + case GF_STREAM_MPEGJ: nb_mpj++; break; + case GF_STREAM_PRIVATE_SCENE: nb_scene++; break; + /*all other streams shall not be mixed: text, video, audio, interaction, font*/ + default: + if (esd->decoderConfig->streamType!=prev_st) nb_other++; + prev_st = esd->decoderConfig->streamType; + break; + } + } + /*cf spec on stream aggregation*/ + if (nb_other>1) return GF_ODF_INVALID_DESCRIPTOR; /*no more than one base media type*/ + if (nb_od && !nb_scene) return GF_ODF_INVALID_DESCRIPTOR; /*if OD we must have scene description*/ + if (nb_other && nb_scene) return GF_ODF_INVALID_DESCRIPTOR; /*scene OR media*/ + if (nb_ocr>1) return GF_ODF_INVALID_DESCRIPTOR; /*only ONE OCR*/ + if (nb_oci>1) return GF_ODF_INVALID_DESCRIPTOR; /*only ONE OCI*/ + if (nb_mp7>1) return GF_ODF_INVALID_DESCRIPTOR; /*only ONE MPEG-7 - this is not in the spec, but since MPEG-7 = OCI++ this seems reasonable*/ + if (nb_mpj>1) return GF_ODF_INVALID_DESCRIPTOR; /*only ONE MPEG-J - this is not in the spec but well...*/ + + /*the rest should be OK*/ + + /*select independant streams - check language and (TODO) bitrate & term caps*/ + sOpt = gf_cfg_get_key(odm->term->user->config, "Systems", "Language3CC"); + if (!sOpt) { + gf_cfg_set_key(odm->term->user->config, "Systems", "Language3CC", "und"); + sOpt = "und"; + } + lang = (sOpt[0]<<16) | (sOpt[1]<<8) | sOpt[2]; +#if 0 + if (gf_list_count(odm->OD->ESDescriptors)>1) { + ODM_SelectAlternateStream(odm, lang, GF_STREAM_SCENE); + ODM_SelectAlternateStream(odm, lang, GF_STREAM_OD); + ODM_SelectAlternateStream(odm, lang, GF_STREAM_VISUAL); + ODM_SelectAlternateStream(odm, lang, GF_STREAM_AUDIO); + ODM_SelectAlternateStream(odm, lang, GF_STREAM_IPMP); + ODM_SelectAlternateStream(odm, lang, GF_STREAM_INTERACT); + ODM_SelectAlternateStream(odm, lang, GF_STREAM_TEXT); + } +#endif + /*no scene, OK*/ + if (!nb_scene) return GF_OK; + + /*check if inline or animation stream*/ + *hasInline = 1; + base_scene = NULL; + i=0; + while ( (esd = (GF_ESD *)gf_list_enum(odm->OD->ESDescriptors, &i)) ) { + switch (esd->decoderConfig->streamType) { + case GF_STREAM_PRIVATE_SCENE: + case GF_STREAM_SCENE: + base_scene = esd; + break; + default: + break; + } + if (base_scene) break; + } + + /*we have a scene stream without dependancies, this is an inline*/ + if (!base_scene || !base_scene->dependsOnESID) return GF_OK; + + /*if the stream the base scene depends on is in this OD, this is in inline*/ + es_id = base_scene->dependsOnESID; + while (es_id) { + esd = od_get_esd(odm->OD, es_id); + /*the stream this stream depends on is not in this OD, this is some anim stream*/ + if (!esd) { + *hasInline = 0; + return GF_OK; + } + es_id = esd->dependsOnESID; + /*should be forbidden (circular reference), we assume this describes inline (usually wrong BIFS->OD setup)*/ + if (es_id==base_scene->ESID) break; + } + /*no dependency to external stream, this is an inline*/ + return GF_OK; +} + + + +/*connection of OD and setup of streams. The streams are not requested if the OD +is in an unexecuted state +the ODM is created either because of IOD / remoteOD, or by the OD codec. In the later +case, the GF_InlineScene pointer will be set by the OD codec.*/ +GF_EXPORT +void gf_odm_setup_object(GF_ObjectManager *odm, GF_ClientService *serv) +{ + Bool hasInline; + u32 i, numOK; + GF_Err e; + GF_ESD *esd; + GF_MediaObject *syncRef; + + if (!odm->net_service) odm->net_service = serv; + + /*if this is a remote OD, we need a new manager and a new service...*/ + if (odm->OD->URLString) { + GF_ClientService *parent = odm->net_service; + char *url = odm->OD->URLString; + odm->OD->URLString = NULL; + /*store original OD ID */ + if (!odm->current_time) odm->current_time = odm->OD->objectDescriptorID; + + gf_odf_desc_del((GF_Descriptor *)odm->OD); + odm->OD = NULL; + odm->net_service = NULL; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Object redirection to %s\n", url)); + + /*if object is a scene, create the inline before connecting the object. + This is needed in irder to register the nodes using the resource for event + propagation (stored at the inline level) + */ + if (odm->mo && (odm->mo->type==GF_MEDIA_OBJECT_SCENE)) { + odm->subscene = gf_inline_new(odm->parentscene); + odm->subscene->root_od = odm; + } + gf_term_connect_object(odm->term, odm, url, parent); + free(url); + return; + } + /*restore OD ID */ + if (odm->current_time) { + odm->OD->objectDescriptorID = odm->current_time; + odm->current_time = 0; + odm->flags |= GF_ODM_REMOTE_OD; + } + + /*HACK - temp storage of sync ref*/ + syncRef = (GF_MediaObject*)odm->ocr_codec; + odm->ocr_codec = NULL; + + e = ODM_ValidateOD(odm, &hasInline); + if (e) { + gf_term_message(odm->term, odm->net_service->url, "MPEG-4 Service Error", e); + gf_odm_disconnect(odm, 1); + return; + } + + if (odm->mo && (odm->mo->type == GF_MEDIA_OBJECT_UPDATES)) { + hasInline = 0; + } + + /*if there is a BIFS stream in the OD, we need an GF_InlineScene (except if we already + have one, which means this is the first IOD)*/ + if (hasInline && !odm->subscene) { + odm->subscene = gf_inline_new(odm->parentscene); + odm->subscene->root_od = odm; + } + + numOK = odm->pending_channels = 0; + /*empty IOD, use a dynamic scene*/ + if (!gf_list_count(odm->OD->ESDescriptors) && odm->subscene) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] No streams in object - taking over scene graph generation\n",odm->OD->objectDescriptorID)); + assert(odm->subscene->root_od==odm); + odm->subscene->is_dynamic_scene = 1; + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Setting up object streams\n")); + /*avoid channels PLAY request when confirming connection (sync network service)*/ + odm->state = GF_ODM_STATE_IN_SETUP; + + i=0; + while ((esd = (GF_ESD *)gf_list_enum(odm->OD->ESDescriptors, &i)) ) { + e = gf_odm_setup_es(odm, esd, serv, syncRef); + /*notify error but still go on, all streams are not so usefull*/ + if (e==GF_OK) { + numOK++; + } else { + gf_term_message(odm->term, odm->net_service->url, "Stream Setup Failure", e); + } + } + odm->state = GF_ODM_STATE_STOP; + } + + /*special case for ODs only having OCRs: force a START since they're never refered to by media nodes*/ + if (odm->ocr_codec) gf_odm_start(odm); + +#if 0 + /*clean up - note that this will not be performed if one of the stream is using ESD URL*/ + if (!numOK) { + gf_odm_disconnect(odm, 1); + return; + } +#endif + + /*setup mediaobject info except for top-level OD*/ + if (odm->parentscene) { + gf_inline_setup_object(odm->parentscene, odm); + } else { + /*othewise send a connect ack for top level*/ + GF_Event evt; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM] Root object connected !\n", odm->net_service->url)); + + evt.type = GF_EVENT_CONNECT; + evt.connect.is_connected = 1; + GF_USER_SENDEVENT(odm->term->user, &evt); + } + + /* and connect ONLY if main scene - inlines are connected when attached to Inline nodes*/ + if (!odm->parentscene) { + assert(odm->subscene == odm->term->root_scene); + assert(odm->subscene->root_od==odm); + gf_odm_start(odm); + } + + /*for objects inserted by user (subs & co), auto select*/ + if (odm->term->root_scene->is_dynamic_scene && (odm->OD->objectDescriptorID==GF_ESM_DYNAMIC_OD_ID) && (odm->flags & GF_ODM_REMOTE_OD)) { + GF_Event evt; + if (odm->OD_PL) { + gf_inline_select_object(odm->term->root_scene, odm); + odm->OD_PL = 0; + } + evt.type = GF_EVENT_STREAMLIST; + GF_USER_SENDEVENT(odm->term->user,&evt); + } +} + + + +void ODM_CheckChannelService(GF_Channel *ch) +{ + if (ch->service == ch->odm->net_service) return; + /*if the stream has created a service check if close is needed or not*/ + if (ch->esd->URLString && !ch->service->nb_ch_users) + gf_term_close_services(ch->odm->term, ch->service); +} + +/*setup channel, clock and query caps*/ +GF_EXPORT +GF_Err gf_odm_setup_es(GF_ObjectManager *odm, GF_ESD *esd, GF_ClientService *serv, GF_MediaObject *sync_ref) +{ + GF_CodecCapability cap; + GF_Channel *ch; + GF_Clock *ck; + GF_List *ck_namespace; + GF_Codec *dec; + s8 flag; + u16 clockID; + Bool emulated_od = 0; + GF_Err e; + GF_InlineScene *is; + + /*find the clock for this new channel*/ + ck = NULL; + flag = (s8) -1; + + /*sync reference*/ + if (sync_ref && sync_ref->odm && sync_ref->odm->codec) { + ck = sync_ref->odm->codec->ck; + goto clock_setup; + } + /*timeline override*/ + if (odm->flags & GF_ODM_INHERIT_TIMELINE) { + if (odm->parentscene->root_od->subscene->scene_codec) + ck = odm->parentscene->root_od->subscene->scene_codec->ck; + else + ck = odm->parentscene->root_od->subscene->dyn_ck; + goto clock_setup; + } + + /*get clocks namespace (eg, parent scene)*/ + is = odm->subscene ? odm->subscene : odm->parentscene; + if (is->force_sub_clock_id) esd->OCRESID = is->force_sub_clock_id; + + ck_namespace = odm->net_service->Clocks; + /*little trick for non-OD addressing: if object is a remote one, and service owner already has clocks, + override OCR. This will solve addressing like file.avi#audio and file.avi#video*/ + if (!esd->OCRESID && (odm->flags & GF_ODM_REMOTE_OD) && (gf_list_count(ck_namespace)==1) ) { + ck = (GF_Clock*)gf_list_get(ck_namespace, 0); + esd->OCRESID = ck->clockID; + } + /*for dynamic scene, force all streams to be sync on main OD stream (one timeline, no need to reload ressources)*/ + else if (odm->term->root_scene->is_dynamic_scene) { + GF_ObjectManager *root_od = odm->term->root_scene->root_od; + if (gf_list_count(root_od->net_service->Clocks)==1) { + ck = (GF_Clock*)gf_list_get(root_od->net_service->Clocks, 0); + esd->OCRESID = ck->clockID; + goto clock_setup; + } + } + + /*do we have an OCR specified*/ + clockID = esd->OCRESID; + /*if OCR stream force self-synchro !!*/ + if (esd->decoderConfig->streamType==GF_STREAM_OCR) clockID = esd->ESID; + if (!clockID) { + /*if no clock ID but depandancy, force the clock to be the base layer for AV but not systems (animation streams, ..)*/ + if ((esd->decoderConfig->streamType==GF_STREAM_VISUAL) || (esd->decoderConfig->streamType==GF_STREAM_AUDIO)) clockID = esd->dependsOnESID; + if (!clockID) clockID = esd->ESID; + } + + /*override clock dependencies if specified*/ + if (odm->term->flags & GF_TERM_SINGLE_CLOCK) { + if (is->scene_codec) { + clockID = is->scene_codec->ck->clockID; + } else if (is->od_codec) { + clockID = is->od_codec->ck->clockID; + } + ck_namespace = odm->term->root_scene->root_od->net_service->Clocks; + } + /*if the GF_Clock is the stream, check if we have embedded OCR in the stream...*/ + if (clockID == esd->ESID) { + flag = (esd->slConfig && esd->slConfig->OCRLength > 0); + } + + if (!esd->slConfig) { + esd->slConfig = (GF_SLConfig *)gf_odf_desc_new(GF_ODF_SLC_TAG); + esd->slConfig->timestampResolution = 1000; + } + + /*attach clock in namespace*/ + ck = gf_clock_attach(ck_namespace, is, clockID, esd->ESID, flag); + if (!ck) return GF_OUT_OF_MEM; + esd->OCRESID = ck->clockID; + +clock_setup: + /*create a channel for this stream*/ + ch = gf_es_new(esd); + if (!ch) return GF_OUT_OF_MEM; + ch->clock = ck; + ch->service = serv; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM] Creating codec for stream %d\n", ch->esd->ESID)); + + /*setup the decoder for this stream or find the existing one.*/ + e = GF_OK; + dec = NULL; + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: + //OD - MUST be in inline + if (!odm->subscene) { + e = GF_NON_COMPLIANT_BITSTREAM; + break; + } + /*OD codec acts as main scene codec when used to generate scene graph*/ + if (! odm->subscene->od_codec) { + odm->subscene->od_codec = gf_codec_new(odm, esd, odm->OD_PL, &e); + if (e) return e; + gf_term_add_codec(odm->term, odm->subscene->od_codec); + } + dec = odm->subscene->od_codec; + break; + case GF_STREAM_OCR: + /*OD codec acts as main scene codec when used to generate scene graph*/ + dec = odm->ocr_codec = gf_codec_new(odm, esd, odm->OD_PL, &e); + gf_term_add_codec(odm->term, odm->ocr_codec); + break; + case GF_STREAM_SCENE: + /*animationStream */ + if (!odm->subscene) { + if (!odm->codec) { + odm->codec = gf_codec_new(odm, esd, odm->Scene_PL, &e); + gf_term_add_codec(odm->term, odm->codec); + } + dec = odm->codec; + } + /*inline scene*/ + else { + if (! odm->subscene->scene_codec) { + odm->subscene->scene_codec = gf_codec_new(odm, esd, odm->Scene_PL, &e); + if (!e) gf_term_add_codec(odm->term, odm->subscene->scene_codec); + } + dec = odm->subscene->scene_codec; + } + break; + case GF_STREAM_OCI: + /*OCI - only one per OD */ + if (odm->oci_codec) { + e = GF_NON_COMPLIANT_BITSTREAM; + } else { + odm->oci_codec = gf_codec_new(odm, esd, odm->OD_PL, &e); + odm->oci_codec->odm = odm; + gf_term_add_codec(odm->term, odm->oci_codec); + } + break; + + case GF_STREAM_AUDIO: + case GF_STREAM_VISUAL: + /*we have a media or user-specific codec...*/ + if (!odm->codec) { + odm->codec = gf_codec_new(odm, esd, (esd->decoderConfig->streamType==GF_STREAM_VISUAL) ? odm->Visual_PL : odm->Audio_PL, &e); + if (!e) gf_term_add_codec(odm->term, odm->codec); + } + dec = odm->codec; + break; + + /*interaction stream*/ + case GF_STREAM_INTERACT: + if (!odm->codec) { + odm->codec = gf_codec_new(odm, esd, odm->OD_PL, &e); + if (!e) { + IS_Configure(odm->codec->decio, odm->parentscene, esd->URLString ? 1 : 0); + gf_term_add_codec(odm->term, odm->codec); + /*register it*/ + gf_list_add(odm->term->input_streams, odm->codec); + } + } + dec = odm->codec; + if ((esd->ESID==esd->OCRESID) &&(esd->ESID>=65530)) { + emulated_od = 1; + } + break; + + case GF_STREAM_PRIVATE_SCENE: + if (odm->subscene) { + assert(!odm->subscene->scene_codec); + odm->subscene->scene_codec = gf_codec_new(odm, esd, odm->Scene_PL, &e); + if (odm->subscene->scene_codec) { + gf_term_add_codec(odm->term, odm->subscene->scene_codec); + } + dec = odm->subscene->scene_codec; + } else { + /*this is a bit tricky: the scene decoder needs to ba called with the dummy streams of this + object, so we associate the main decoder to this object*/ + odm->codec = dec = gf_codec_use_codec(odm->parentscene->scene_codec, odm); + gf_term_add_codec(odm->term, odm->codec); + } + break; + /*all other cases*/ + default: + if (!odm->codec) { + odm->codec = gf_codec_new(odm, esd, odm->OD_PL, &e); + if (!e) gf_term_add_codec(odm->term, odm->codec); + + } + dec = odm->codec; + break; + } + + /*if we have a decoder, set up the channel and co.*/ + if (!dec) { + if (e) { + gf_es_del(ch); + return e; + } + } + + /*setup scene decoder*/ + if (dec->decio && (dec->decio->InterfaceType==GF_SCENE_DECODER_INTERFACE) ) { + GF_SceneDecoder *sdec = (GF_SceneDecoder *) dec->decio; + is = odm->subscene ? odm->subscene : odm->parentscene; + if (sdec->AttachScene) { + /*if a node asked for this media object, use the scene graph of the node (AnimationStream in PROTO)*/ + if (odm->mo && odm->mo->node_ptr) { + GF_SceneGraph *sg = is->graph; + /*FIXME - this MUST be cleaned up*/ + is->graph = gf_node_get_graph((GF_Node*)odm->mo->node_ptr); + sdec->AttachScene(sdec, is, (is->scene_codec==dec) ? 1: 0); + is->graph = sg; + odm->mo->node_ptr = NULL; + } else { + sdec->AttachScene(sdec, is, (is->scene_codec==dec) ? 1: 0); + } + } + } + + ch->es_state = GF_ESM_ES_SETUP; + ch->odm = odm; + + /*get media padding BEFORE channel setup, since we use it on channel connect ack*/ + if (dec) { + cap.CapCode = GF_CODEC_PADDING_BYTES; + gf_codec_get_capability(dec, &cap); + ch->media_padding_bytes = cap.cap.valueInt; + + cap.CapCode = GF_CODEC_RESILIENT; + gf_codec_get_capability(dec, &cap); + ch->codec_resilient = cap.cap.valueInt; + } + + if (emulated_od) { + ch->service = NULL; + } + + /*one more channel to wait for*/ + odm->pending_channels++; + + /*service redirection*/ + if (esd->URLString) { + GF_ChannelSetup *cs; + /*here we have a pb with the MPEG4 model: streams are supposed to be attachable as soon as the OD + update is received, but this is not true with ESD URLs, where service setup may take some time (file + downloading, authentification, etc...). We therefore need to wait for the service connect response before + setting up the channel...*/ + cs = (GF_ChannelSetup*)malloc(sizeof(GF_ChannelSetup)); + cs->ch = ch; + cs->dec = dec; + + /*HACK: special case when OD resources are statically described in the ESD itself (ISMA streaming)*/ + if ((ch->esd->decoderConfig->streamType==GF_STREAM_OD) && strstr(ch->esd->URLString, "data:application/mpeg4-od-au;") ) + dec->flags |= GF_ESM_CODEC_IS_STATIC_OD; + + gf_term_lock_net(odm->term, 1); + gf_list_add(odm->term->channels_pending, cs); + e = gf_term_connect_remote_channel(odm->term, ch, esd->URLString); + if (e) { + s32 i = gf_list_find(odm->term->channels_pending, cs); + if (i>=0) { + gf_list_rem(odm->term->channels_pending, (u32) i); + free(cs); + odm->pending_channels--; + ODM_CheckChannelService(ch); + gf_es_del(ch); + } + } + gf_term_lock_net(odm->term, 0); + if (ch->service->owner) { + gf_list_del_item(odm->term->channels_pending, cs); + free(cs); + return gf_odm_post_es_setup(ch, dec, GF_OK); + } + return e; + } + + /*regular setup*/ + return gf_odm_post_es_setup(ch, dec, GF_OK); +} + +GF_Err gf_odm_post_es_setup(GF_Channel *ch, GF_Codec *dec, GF_Err had_err) +{ + char szURL[2048]; + GF_Err e; + GF_NetworkCommand com; + + e = had_err; + if (e) { + ch->odm->pending_channels--; + goto err_exit; + } + + /*insert channel*/ + if (dec) gf_list_insert(ch->odm->channels, ch, 0); + + if (ch->service) { + ch->es_state = GF_ESM_ES_WAIT_FOR_ACK; + if (ch->esd->URLString) { + strcpy(szURL, ch->esd->URLString); + } else { + sprintf(szURL, "ES_ID=%d", ch->esd->ESID); + } + + /*connect before setup: this is needed in case the decoder cfg is wrong, we may need to get it from + network config...*/ + e = ch->service->ifce->ConnectChannel(ch->service->ifce, ch, szURL, ch->esd->decoderConfig->upstream); + + /*special case (not really specified in specs ...): if the stream is not found and is an Interaction + one (ie, used by an InputSensor), consider this means the stream shall be generated by the IS device*/ + if ((e==GF_STREAM_NOT_FOUND) && (ch->esd->decoderConfig->streamType==GF_STREAM_INTERACT)) e = GF_OK; + } else { + ch->es_state = GF_ESM_ES_CONNECTED; + ch->odm->pending_channels--; + } + + if (e) { + if (dec) gf_list_rem(ch->odm->channels, 0); + goto err_exit; + } + /*add to decoder*/ + if (dec) { + e = gf_codec_add_channel(dec, ch); + if (e) { + switch (ch->esd->decoderConfig->streamType) { + case GF_STREAM_VISUAL: + gf_term_message(ch->odm->term, ch->service->url, "Video Setup failed", e); + break; + case GF_STREAM_AUDIO: + gf_term_message(ch->odm->term, ch->service->url, "Audio Setup failed", e); + break; + } + gf_list_rem(ch->odm->channels, 0); + /*disconnect*/ + ch->service->ifce->DisconnectChannel(ch->service->ifce, ch); + if (ch->esd->URLString) { + assert(ch->service->nb_ch_users); + ch->service->nb_ch_users--; + } + goto err_exit; + } + } + + /*in case a channel is inserted in a running OD, open and play if not in queue*/ + if ( (ch->odm->state==GF_ODM_STATE_PLAY) + /*HACK: special case when OD resources are statically described in the ESD itself (ISMA streaming)*/ +// || (dec && (dec->flags & GF_ESM_CODEC_IS_STATIC_OD)) + ) { + + gf_term_lock_net(ch->odm->term, 1); + gf_list_del_item(ch->odm->term->media_queue, ch->odm); + + gf_es_start(ch); + com.command_type = GF_NET_CHAN_PLAY; + com.base.on_channel = ch; + com.play.speed = FIX2FLT(ch->clock->speed); + com.play.start_range = gf_clock_time(ch->clock); + com.play.start_range /= 1000; + com.play.end_range = -1.0; + gf_term_service_command(ch->service, &com); + if (dec && (dec->Status!=GF_ESM_CODEC_PLAY)) gf_term_start_codec(dec); + gf_term_lock_net(ch->odm->term, 0); + } + return GF_OK; + +err_exit: + ODM_CheckChannelService(ch); + gf_es_del(ch); + return e; +} + +/*confirmation of channel delete from net*/ +void ODM_DeleteChannel(GF_ObjectManager *odm, GF_Channel *ch) +{ + u32 i, count, ch_pos; + GF_Channel *ch2; + GF_Clock *ck; + + if (!ch) return; + + //find a clock with this stream ES_ID + ck = gf_clock_find(odm->net_service->Clocks, ch->esd->ESID, 0); + + count = gf_list_count(odm->channels); + ch_pos = count+1; + + for (i=0; ichannels, i); + if (ch2 == ch) { + ch_pos = i; + if (ck) continue; + break; + } + //note that when a stream is added, we need to update clocks info ... + if (ck && ch->clock && (ch2->clock->clockID == ck->clockID)) gf_es_stop(ch2); + } + /*remove channel*/ + if (ch_pos != count+1) gf_list_rem(odm->channels, ch_pos); + + /*remove from the codec*/ + count = 0; + if (!count && odm->codec) + count = gf_codec_remove_channel(odm->codec, ch); + if (!count && odm->ocr_codec) + count = gf_codec_remove_channel(odm->ocr_codec, ch); + if (!count && odm->oci_codec) + count = gf_codec_remove_channel(odm->oci_codec, ch); + if (!count && odm->subscene) { + if (odm->subscene->scene_codec) count = gf_codec_remove_channel(odm->subscene->scene_codec, ch); + if (!count) count = gf_codec_remove_channel(odm->subscene->od_codec, ch); + } + assert(count); + + if (ch->service) { + ch->service->ifce->DisconnectChannel(ch->service->ifce, ch); + if (ch->esd->URLString) { + assert(ch->service->nb_ch_users); + ch->service->nb_ch_users--; + } + ODM_CheckChannelService(ch); + } + + //and delete + gf_es_del(ch); +} + +GF_EXPORT +void gf_odm_remove_es(GF_ObjectManager *odm, u16 ES_ID) +{ + GF_ESD *esd; + GF_Channel *ch; + u32 i = 0; + while ((esd = (GF_ESD *)gf_list_enum(odm->OD->ESDescriptors, &i)) ) { + if (esd->ESID==ES_ID) goto esd_found; + } + return; + +esd_found: + /*remove esd*/ + gf_list_rem(odm->OD->ESDescriptors, i-1); + /*locate channel*/ + ch = NULL; + i=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i)) ) { + if (ch->esd->ESID == ES_ID) break; + ch = NULL; + } + /*remove channel*/ + if (ch) ODM_DeleteChannel(odm, ch); + /*destroy ESD*/ + gf_odf_desc_del((GF_Descriptor *) esd); +} + +/*this is the tricky part: make sure the net is locked before doing anything since an async service +reply could destroy the object we're queuing for play*/ +void gf_odm_start(GF_ObjectManager *odm) +{ + gf_term_lock_net(odm->term, 1); + + /*only if not open & ready (not waiting for ACK on channel setup)*/ + if (!odm->state && !odm->pending_channels && odm->OD) { + GF_Channel *ch; + u32 i = 0; + odm->state = GF_ODM_STATE_PLAY; + + /*look for a given segment name to play*/ + if (odm->subscene) { + char *url, *frag; + assert(odm->subscene->root_od==odm); + + url = (odm->mo && odm->mo->URLs.count) ? odm->mo->URLs.vals[0].url : odm->net_service->url; + frag = strrchr(url, '#'); + if (frag) { + GF_Segment *seg = gf_odm_find_segment(odm, frag+1); + if (seg) { + odm->media_start_time = (u64) ((s64) seg->startTime*1000); + odm->media_stop_time = (u64) ((s64) (seg->startTime + seg->Duration)*1000); + } + } + } + + /*start all channels and postpone play - this assures that all channels of a multiplexed are setup + before one starts playing*/ + while ( (ch = (GF_Channel*)gf_list_enum(odm->channels, &i)) ) { + gf_es_start(ch); + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[ODM%d] CH%d: At OTB %d starting channel\n", odm->OD->objectDescriptorID, ch->esd->ESID, gf_clock_time(ch->clock))); + } + if (gf_list_find(odm->term->media_queue, odm)<0) gf_list_add(odm->term->media_queue, odm); + } + gf_term_lock_net(odm->term, 0); +} + +void gf_odm_play(GF_ObjectManager *odm) +{ + GF_Channel *ch; + u32 i; + u32 nb_failure; + u64 range_end; + Bool skip_od_st; + GF_NetworkCommand com; + MediaControlStack *ctrl; + GF_Clock *parent_ck = NULL; + + if (odm->parentscene) { + parent_ck = gf_odm_get_media_clock(odm->parentscene->root_od); + if (!gf_odm_shares_clock(odm, parent_ck)) parent_ck = NULL; + } + + skip_od_st = (odm->subscene && odm->subscene->static_media_ressources) ? 1 : 0; + range_end = odm->media_stop_time; +// odm->media_stop_time = 0; + + nb_failure = gf_list_count(odm->channels); + + /*send play command*/ + com.command_type = GF_NET_CHAN_PLAY; + i=0; + while ( (ch = (GF_Channel*)gf_list_enum(odm->channels, &i)) ) { + Double ck_time; + + if (ch->ipmp_tool) { + GF_IPMPEvent evt; + GF_Err e; + memset(&evt, 0, sizeof(evt)); + evt.event_type=GF_IPMP_TOOL_GRANT_ACCESS; + evt.channel = ch; + e = ch->ipmp_tool->process(ch->ipmp_tool, &evt); + if (e) { + gf_term_message(odm->term, NULL, "PLAY access is not granted on channel - please check your license", e); + gf_es_stop(ch); + continue; + } + } + nb_failure --; + + com.base.on_channel = ch; + com.play.speed = 1.0; + /*play from requested time (seeking or non-mpeg4 media control)*/ + if (odm->media_start_time && !ch->clock->clock_init) { + ck_time = (Double) (s64) odm->media_start_time; + ck_time /= 1000; + } + /*play from current time*/ + else { + ck_time = gf_clock_time(ch->clock); + ck_time /= 1000; + /*handle initial start - MPEG-4 is a bit annoying here, streams are not started through OD but through + scene nodes. If the stream runs on the BIFS/OD clock, the clock is already started at this point and we're + sure to get at least a one-frame delay in PLAY, so just remove it - note we're generous but this shouldn't hurt*/ + if (ck_time<=0.5) ck_time = 0; + } + com.play.start_range = ck_time; + + if (range_end) { + com.play.end_range = (s64) range_end / 1000.0; + } else { + if (!odm->subscene && gf_odm_shares_clock(odm->parentscene->root_od, ch->clock) + && (odm->parentscene->root_od->media_stop_time != odm->parentscene->root_od->duration) + ) { + com.play.end_range = (s64) odm->parentscene->root_od->media_stop_time / 1000.0; + } else { + com.play.end_range = -1; + } + } + + /*if object shares parent scene clock, do not use media control*/ + ctrl = parent_ck ? NULL : ODM_GetMediaControl(odm); + /*override range and speed with MC*/ + if (ctrl) { + MC_GetRange(ctrl, &com.play.start_range, &com.play.end_range); + com.play.speed = FIX2FLT(ctrl->control->mediaSpeed); + /*if the channel doesn't control the clock, jump to current time in the controled range, not just the begining*/ + if ((ch->esd->ESID!=ch->clock->clockID) && (ck_time>com.play.start_range) && (com.play.end_range>com.play.start_range) && (ck_timeclock, ctrl->control->mediaSpeed); + /*if requested seek time AND media control, adjust start range to current play time*/ + if (odm->media_start_time) { + if ((com.play.start_range>=0) && (com.play.end_range>com.play.start_range)) { + if (ctrl->control->loop) { + Double active_dur = com.play.end_range - com.play.start_range; + while (ck_time>active_dur) ck_time -= active_dur; + } else { + ck_time = 0; + //com.play.start_range = com.play.end_range; + } + } + com.play.start_range += ck_time; + } + } + /*full object playback*/ + if (com.play.end_range<=0) { + odm->media_stop_time = odm->subscene ? 0 : odm->duration; + } else { + /*segment playback - since our timing is in ms whereas segment ranges are double precision, + make sure we have a LARGER range in ms, otherwise media sensors won't deactivate properly*/ + odm->media_stop_time = (u64) ceil(1000 * com.play.end_range); + } + + /*don't replay OD channel, only init clock if needed*/ + if (!ch->service || (skip_od_st && (ch->esd->decoderConfig->streamType==GF_STREAM_OD))) { + Bool gf_es_owns_clock(GF_Channel *ch); + + if (gf_es_owns_clock(ch) ) + gf_clock_set_time(ch->clock, (u32) (com.play.start_range*1000)); + + ch->IsClockInit = 1; + if (ch->BufferOn) { + ch->BufferOn = 0; + gf_clock_buffer_off(ch->clock); + } + } else { + gf_term_service_command(ch->service, &com); + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[ODM%d] CH%d: At OTB %d requesting PLAY from %g to %g (clock init %d)\n", odm->OD->objectDescriptorID, ch->esd->ESID, gf_clock_time(ch->clock), com.play.start_range, com.play.end_range, ch->clock->clock_init)); + } + } +// odm->media_start_time = 0; + + if (nb_failure) { + odm->state = GF_ODM_STATE_BLOCKED; + return; + } + + gf_term_service_media_event(odm, GF_EVENT_MEDIA_DATA_REQUEST); + + + /*start codecs last (otherwise we end up pulling data from channels not yet connected->pbs when seeking)*/ + if (odm->codec) { + /*reset*/ + if (odm->codec->CB) { + gf_cm_set_status(odm->codec->CB, CB_STOP); + odm->codec->CB->HasSeenEOS = 0; + } + gf_term_start_codec(odm->codec); + } else if (odm->subscene) { + if (odm->subscene->scene_codec) gf_term_start_codec(odm->subscene->scene_codec); + if (!skip_od_st && odm->subscene->od_codec) gf_term_start_codec(odm->subscene->od_codec); + + if (odm->flags & GF_ODM_REGENERATE_SCENE) { + odm->flags &= ~GF_ODM_REGENERATE_SCENE; + gf_inline_regenerate(odm->subscene); + } + } + if (odm->ocr_codec) gf_term_start_codec(odm->ocr_codec); + if (odm->oci_codec) gf_term_start_codec(odm->oci_codec); +} + + +void gf_odm_stop(GF_ObjectManager *odm, Bool force_close) +{ + GF_Channel *ch; + u32 i; + MediaControlStack *ctrl; + MediaSensorStack *media_sens; + GF_NetworkCommand com; + + if (!odm->state) return; + +#if 0 + /*Handle broadcast environment, do not stop the object if no time control and instruction + comes from the scene*/ + if (odm->no_time_ctrl && !force_close) { + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[ODM%d] - broadcast detected, ignoring Stop from scene\n", odm->OD->objectDescriptorID); + return; + } +#endif + + + gf_term_lock_net(odm->term, 1); + gf_list_del_item(odm->term->media_queue, odm); + gf_term_lock_net(odm->term, 0); + + /*little opt for image codecs: don't actually stop the OD*/ + if (!force_close && odm->codec && odm->codec->CB) { + if (odm->codec->CB->Capacity==1) return; + } + + /*object was not unlocked, decoders were not started*/ + if (odm->state==GF_ODM_STATE_BLOCKED) { + odm->current_time = 0; + return; + } + + /*stop codecs*/ + if (odm->codec) { + gf_term_stop_codec(odm->codec); + } else if (odm->subscene) { + u32 i=0; + GF_ObjectManager *sub_odm; + if (odm->subscene->scene_codec) gf_term_stop_codec(odm->subscene->scene_codec); + if (odm->subscene->od_codec) gf_term_stop_codec(odm->subscene->od_codec); + + /*stops all resources of the subscene as well*/ + while ((sub_odm=(GF_ObjectManager *)gf_list_enum(odm->subscene->ODlist, &i))) { + gf_odm_stop(sub_odm, force_close); + } + } + if (odm->ocr_codec) gf_term_stop_codec(odm->ocr_codec); + if (odm->oci_codec) gf_term_stop_codec(odm->oci_codec); + + gf_term_lock_net(odm->term, 1); + + /*send stop command*/ + com.command_type = GF_NET_CHAN_STOP; + i=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i)) ) { + + if (ch->ipmp_tool) { + GF_IPMPEvent evt; + memset(&evt, 0, sizeof(evt)); + evt.event_type=GF_IPMP_TOOL_RELEASE_ACCESS; + evt.channel = ch; + ch->ipmp_tool->process(ch->ipmp_tool, &evt); + } + + if (ch->service) { + com.base.on_channel = ch; + gf_term_service_command(ch->service, &com); + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[ODM%d] CH %d At OTB %d requesting STOP\n", odm->OD->objectDescriptorID, ch->esd->ESID, gf_clock_time(ch->clock))); + } + } + gf_term_service_media_event(odm, GF_EVENT_MEDIA_STOP); + + /*stop channels*/ + i=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i)) ) { + /*stops clock if this is a scene stop*/ + if (!(odm->flags & GF_ODM_INHERIT_TIMELINE) && odm->subscene) gf_clock_stop(ch->clock); + gf_es_stop(ch); + } + + gf_term_lock_net(odm->term, 0); + + odm->state = GF_ODM_STATE_STOP; + odm->current_time = 0; + + /*reset media sensor(s)*/ + if (force_close!=2) { + i = 0; + while ((media_sens = (MediaSensorStack *)gf_list_enum(odm->ms_stack, &i))){ + MS_Stop(media_sens); + } + } + /*reset media control state*/ + ctrl = ODM_GetMediaControl(odm); + if (ctrl) ctrl->current_seg = 0; +} + +void gf_odm_on_eos(GF_ObjectManager *odm, GF_Channel *on_channel) +{ + if (gf_odm_check_segment_switch(odm)) return; + + gf_term_service_media_event(odm, GF_EVENT_MEDIA_END_OF_DATA); + + if (odm->codec && (on_channel->esd->decoderConfig->streamType==odm->codec->type)) { + gf_codec_set_status(odm->codec, GF_ESM_CODEC_EOS); + return; + } + if (on_channel->esd->decoderConfig->streamType==GF_STREAM_OCR) { + gf_codec_set_status(odm->ocr_codec, GF_ESM_CODEC_EOS); + return; + } + if (on_channel->esd->decoderConfig->streamType==GF_STREAM_OCI) { + gf_codec_set_status(odm->oci_codec, GF_ESM_CODEC_EOS); + return; + } + if (!odm->subscene) return; + + if (odm->subscene->scene_codec && (gf_list_find(odm->subscene->scene_codec->inChannels, on_channel)>=0) ) { + gf_codec_set_status(odm->subscene->scene_codec, GF_ESM_CODEC_EOS); + return; + } + + if (on_channel->esd->decoderConfig->streamType==GF_STREAM_OD) { + gf_codec_set_status(odm->subscene->od_codec, GF_ESM_CODEC_EOS); + return; + } +} + +void gf_odm_set_duration(GF_ObjectManager *odm, GF_Channel *ch, u64 stream_duration) +{ + if (odm->codec) { + if (ch->esd->decoderConfig->streamType == odm->codec->type) + if (odm->duration < stream_duration) + odm->duration = stream_duration; + } else if (odm->ocr_codec) { + if (ch->esd->decoderConfig->streamType == odm->ocr_codec->type) + if (odm->duration < stream_duration) + odm->duration = stream_duration; + } else if (odm->subscene && odm->subscene->scene_codec) { + //if (gf_list_find(odm->subscene->scene_codec->inChannels, ch) >= 0) { + if (odm->duration < stream_duration) odm->duration = stream_duration; + //} + } + + /*update scene duration*/ + gf_inline_set_duration(odm->subscene ? odm->subscene : (odm->parentscene ? odm->parentscene : odm->term->root_scene)); +} + + +GF_Clock *gf_odm_get_media_clock(GF_ObjectManager *odm) +{ + if (odm->codec) return odm->codec->ck; + if (odm->ocr_codec) return odm->ocr_codec->ck; + if (odm->subscene && odm->subscene->scene_codec) return odm->subscene->scene_codec->ck; + if (odm->subscene && odm->subscene->dyn_ck) return odm->subscene->dyn_ck; + return NULL; +} + + +void ODM_SetMediaControl(GF_ObjectManager *odm, MediaControlStack *ctrl) +{ + u32 i; + GF_Channel *ch; + + /*keep track of it*/ + if (ctrl && (gf_list_find(odm->mc_stack, ctrl) < 0)) gf_list_add(odm->mc_stack, ctrl); + if (ctrl && !ctrl->control->enabled) return; + + if (odm->subscene && odm->subscene->is_dynamic_scene) { + if (odm->subscene->dyn_ck) { + /*deactivate current control*/ + if (ctrl && odm->subscene->dyn_ck->mc) { + odm->subscene->dyn_ck->mc->control->enabled = 0; + gf_node_event_out_str((GF_Node *)odm->subscene->dyn_ck->mc->control, "enabled"); + } + odm->subscene->dyn_ck->mc = ctrl; + } + } else { + /*for each clock in the controled OD*/ + i=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i))) { + if (ch->clock->mc != ctrl) { + /*deactivate current control*/ + if (ctrl && ch->clock->mc) { + ch->clock->mc->control->enabled = 0; + gf_node_event_out_str((GF_Node *)ch->clock->mc->control, "enabled"); + } + /*and attach this control to the clock*/ + ch->clock->mc = ctrl; + } + } + } + /*store active control on media*/ + odm->media_ctrl = ODM_GetMediaControl(odm); +} + +MediaControlStack *ODM_GetMediaControl(GF_ObjectManager *odm) +{ + GF_Clock *ck; + ck = gf_odm_get_media_clock(odm); + if (!ck) return NULL; + return ck->mc; +} + +MediaControlStack *ODM_GetObjectMediaControl(GF_ObjectManager *odm) +{ + MediaControlStack *ctrl; + ctrl = ODM_GetMediaControl(odm); + if (!ctrl) return NULL; + /*inline scene control*/ + if (odm->subscene && (ctrl->stream->odm == odm->subscene->root_od) ) return ctrl; + if (ctrl->stream->OD_ID != odm->OD->objectDescriptorID) return NULL; + return ctrl; +} + +void ODM_RemoveMediaControl(GF_ObjectManager *odm, MediaControlStack *ctrl) +{ + gf_list_del_item(odm->mc_stack, ctrl); + /*removed. Note the spec doesn't say what to do in this case...*/ + if (odm->media_ctrl == ctrl) ODM_SetMediaControl(odm, NULL); +} + +Bool ODM_SwitchMediaControl(GF_ObjectManager *odm, MediaControlStack *ctrl) +{ + u32 i; + MediaControlStack *st2; + if (!ctrl->control->enabled) return 0; + + /*for all media controls other than this one force enable to false*/ + i=0; + while ((st2 = (MediaControlStack *)gf_list_enum(odm->mc_stack, &i))) { + if (st2 == ctrl) continue; + if (st2->control->enabled) { + st2->control->enabled = 0; + gf_node_event_out_str((GF_Node *) st2->control, "enabled"); + } + st2->enabled = 0; + } + if (ctrl == odm->media_ctrl) return 0; + ODM_SetMediaControl(odm, ctrl); + return 1; +} + +Bool gf_odm_shares_clock(GF_ObjectManager *odm, GF_Clock *ck) +{ + u32 i = 0; + GF_Channel *ch; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i))) { + if (ch->clock == ck) return 1; + } + return 0; +} + + + +void gf_odm_pause(GF_ObjectManager *odm) +{ + u32 i; + GF_NetworkCommand com; + MediaSensorStack *media_sens; + GF_Channel *ch; + + if (odm->flags & GF_ODM_NO_TIME_CTRL) return; + + + /*stop codecs, and update status for media codecs*/ + if (odm->codec) { + gf_term_stop_codec(odm->codec); + gf_codec_set_status(odm->codec, GF_ESM_CODEC_PAUSE); + } else if (odm->subscene) { + if (odm->subscene->scene_codec) { + gf_codec_set_status(odm->subscene->scene_codec, GF_ESM_CODEC_PAUSE); + gf_term_stop_codec(odm->subscene->scene_codec); + } + if (odm->subscene->od_codec) gf_term_stop_codec(odm->subscene->od_codec); + } + if (odm->ocr_codec) gf_term_stop_codec(odm->ocr_codec); + if (odm->oci_codec) gf_term_stop_codec(odm->oci_codec); + + com.command_type = GF_NET_CHAN_PAUSE; + i=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i))) { + gf_clock_pause(ch->clock); + com.base.on_channel = ch; + gf_term_service_command(ch->service, &com); + } + + /*mediaSensor shall generate isActive false when paused*/ + i=0; + while ((media_sens = (MediaSensorStack *)gf_list_enum(odm->ms_stack, &i)) ) { + if (media_sens && media_sens->sensor->isActive) { + media_sens->sensor->isActive = 0; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "isActive"); + } + } +} + +void gf_odm_resume(GF_ObjectManager *odm) +{ + u32 i; + GF_NetworkCommand com; + GF_Channel *ch; + MediaSensorStack *media_sens; + + if (odm->flags & GF_ODM_NO_TIME_CTRL) return; + + + /*start codecs, and update status for media codecs*/ + if (odm->codec) { + gf_term_start_codec(odm->codec); + gf_codec_set_status(odm->codec, GF_ESM_CODEC_PLAY); + } else if (odm->subscene) { + if (odm->subscene->scene_codec) { + gf_codec_set_status(odm->subscene->scene_codec, GF_ESM_CODEC_PLAY); + gf_term_start_codec(odm->subscene->scene_codec); + } + if (odm->subscene->od_codec) gf_term_start_codec(odm->subscene->od_codec); + } + if (odm->ocr_codec) gf_term_start_codec(odm->ocr_codec); + if (odm->oci_codec) gf_term_start_codec(odm->oci_codec); + + com.command_type = GF_NET_CHAN_RESUME; + i=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i)) ){ + gf_clock_resume(ch->clock); + com.base.on_channel = ch; + gf_term_service_command(ch->service, &com); + } + + /*mediaSensor shall generate isActive TRUE when resumed*/ + i=0; + while ((media_sens = (MediaSensorStack *)gf_list_enum(odm->ms_stack, &i)) ){ + if (media_sens && !media_sens->sensor->isActive) { + media_sens->sensor->isActive = 1; + gf_node_event_out_str((GF_Node *) media_sens->sensor, "isActive"); + } + } +} + +void gf_odm_set_speed(GF_ObjectManager *odm, Fixed speed) +{ + u32 i; + GF_NetworkCommand com; + GF_Channel *ch; + + if (odm->flags & GF_ODM_NO_TIME_CTRL) return; + + com.command_type = GF_NET_CHAN_SET_SPEED; + com.play.speed = FIX2FLT(speed); + i=0; + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i)) ) { + gf_clock_set_speed(ch->clock, speed); + com.play.on_channel = ch; + gf_term_service_command(ch->service, &com); + } +} + +GF_Segment *gf_odm_find_segment(GF_ObjectManager *odm, char *descName) +{ + GF_Segment *desc; + u32 i = 0; + while ( (desc = (GF_Segment *)gf_list_enum(odm->OD->OCIDescriptors, &i)) ){ + if (desc->tag != GF_ODF_SEGMENT_TAG) continue; + if (!stricmp(desc->SegmentName, descName)) return desc; + } + return NULL; +} + +static void gf_odm_insert_segment(GF_ObjectManager *odm, GF_Segment *seg, GF_List *list) +{ + /*this reorders segments when inserting into list - I believe this is not compliant*/ +#if 0 + GF_Segment *desc; + u32 i = 0; + while ((desc = gf_list_enum(list, &i))) { + if (desc == seg) return; + if (seg->startTime + seg->Duration <= desc->startTime) { + gf_list_insert(list, seg, i); + return; + } + } +#endif + gf_list_add(list, seg); +} + +/*add segment descriptor and sort them*/ +void gf_odm_init_segments(GF_ObjectManager *odm, GF_List *list, MFURL *url) +{ + char *str, *sep; + char seg1[1024], seg2[1024], seg_url[4096]; + GF_Segment *first_seg, *last_seg, *seg; + u32 i, j; + + /*browse all URLs*/ + for (i=0; icount; i++) { + if (!url->vals[i].url) continue; + str = strstr(url->vals[i].url, "#"); + if (!str) continue; + str++; + strcpy(seg_url, str); + /*segment closed range*/ + if ((sep = strstr(seg_url, "-")) ) { + strcpy(seg2, sep+1); + sep[0] = 0; + strcpy(seg1, seg_url); + first_seg = gf_odm_find_segment(odm, seg1); + if (!first_seg) continue; + last_seg = gf_odm_find_segment(odm, seg2); + } + /*segment open range*/ + else if ((sep = strstr(seg_url, "+")) ) { + sep[0] = 0; + strcpy(seg1, seg_url); + first_seg = gf_odm_find_segment(odm, seg_url); + if (!first_seg) continue; + last_seg = NULL; + } + /*single segment*/ + else { + first_seg = gf_odm_find_segment(odm, seg_url); + if (!first_seg) continue; + gf_odm_insert_segment(odm, first_seg, list); + continue; + } + /*segment range process*/ + gf_odm_insert_segment(odm, first_seg, list); + j=0; + while ( (seg = (GF_Segment *)gf_list_enum(odm->OD->OCIDescriptors, &j)) ) { + if (seg->tag != GF_ODF_SEGMENT_TAG) continue; + if (seg==first_seg) continue; + if (seg->startTime + seg->Duration <= first_seg->startTime) continue; + /*this also includes last_seg insertion !!*/ + if (last_seg && (seg->startTime + seg->Duration > last_seg->startTime + last_seg->Duration) ) continue; + gf_odm_insert_segment(odm, seg, list); + } + } +} + +Bool gf_odm_check_segment_switch(GF_ObjectManager *odm) +{ + u32 count, i; + GF_Segment *cur, *next; + MediaControlStack *ctrl = ODM_GetMediaControl(odm); + + /*if no control or control not on this object ignore segment switch*/ + if (!ctrl || (ctrl->stream->odm != odm)) return 0; + + count = gf_list_count(ctrl->seg); + /*reached end of controled stream (no more segments)*/ + if (ctrl->current_seg>=count) return 0; + + /*synth media, trigger if end of segment run-time*/ + if (!odm->codec || ((odm->codec->type!=GF_STREAM_VISUAL) && (odm->codec->type!=GF_STREAM_AUDIO))) { + GF_Clock *ck = gf_odm_get_media_clock(odm); + u32 now = gf_clock_time(ck); + u64 dur = odm->subscene ? odm->subscene->duration : odm->duration; + cur = (GF_Segment *)gf_list_get(ctrl->seg, ctrl->current_seg); + if (odm->subscene && odm->subscene->needs_restart) return 0; + if (cur) dur = (u32) ((cur->Duration+cur->startTime)*1000); + if (now<=dur) return 0; + } else { + /*FIXME - for natural media with scalability, we should only process when all streams of the object are done*/ + } + + /*get current segment and move to next one*/ + cur = (GF_Segment *)gf_list_get(ctrl->seg, ctrl->current_seg); + ctrl->current_seg ++; + + /*resync in case we have been issuing a play range over several segments*/ + for (i=ctrl->current_seg; iseg, i); + if ( + /*if next seg start is after cur seg start*/ + (cur->startTime < next->startTime) + /*if next seg start is before cur seg end*/ + && (cur->startTime + cur->Duration > next->startTime) + /*if next seg start is already passed*/ + && (1000*next->startTime < odm->current_time) + /*then next segment was taken into account when requesting play*/ + ) { + cur = next; + ctrl->current_seg ++; + } + } + /*if last segment in ctrl is done, end of stream*/ + if (ctrl->current_seg >= count) return 0; + next = (GF_Segment *)gf_list_get(ctrl->seg, ctrl->current_seg); + + /*if next seg start is not in current seg, media needs restart*/ + if ((next->startTime < cur->startTime) || (cur->startTime + cur->Duration < next->startTime)) + MC_Restart(odm); + + return 1; +} + diff --git a/src/terminal/svg_external.c b/src/terminal/svg_external.c new file mode 100644 index 0000000..49a4d35 --- /dev/null +++ b/src/terminal/svg_external.c @@ -0,0 +1,228 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / SVG Rendering sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +#ifndef GPAC_DISABLE_SVG + +#include +#include +#include +#include + + +char *gf_term_resolve_xlink(GF_Node *node, char *the_url) +{ + char *url; + GF_InlineScene *is = gf_sg_get_private(gf_node_get_graph(node)); + if (!is) return NULL; + + url = strdup(the_url); + /*apply XML:base*/ + while (node) { + GF_FieldInfo info; + if (gf_node_get_attribute_by_tag(node, TAG_XML_ATT_base, 0, 0, &info)==GF_OK) { + char *new_url = gf_url_concatenate( ((XMLRI*)info.far_ptr)->string, url); + if (new_url) { + free(url); + url = new_url; + } + } + node = gf_node_get_parent(node, 0); + } + + /*if this is a fragment and no XML:BASE was found, this is a fragment of the current document*/ + if (url[0]=='#') return url; + + if (is) { + char *the_url; + if (is->redirect_xml_base) { + the_url = gf_url_concatenate(is->redirect_xml_base, url); + } else { + the_url = gf_url_concatenate(is->root_od->net_service->url, url); + } + free(url); + return the_url; + } + return url; +} + +GF_EXPORT +GF_Err gf_term_get_mfurl_from_xlink(GF_Node *node, MFURL *mfurl) +{ + u32 stream_id = 0; + GF_Err e = GF_OK; + SFURL *sfurl = NULL; + GF_FieldInfo info; + XMLRI *iri; + GF_InlineScene *is = gf_sg_get_private(gf_node_get_graph(node)); + if (!is) return GF_BAD_PARAM; + + gf_sg_vrml_mf_reset(mfurl, GF_SG_VRML_MFURL); + + e = gf_node_get_attribute_by_tag(node, TAG_XLINK_ATT_href, 0, 0, &info); + if (e) return e; + + iri = (XMLRI*)info.far_ptr; + + if (iri->type==XMLRI_STREAMID) { + stream_id = iri->lsr_stream_id; + } else if (!iri->string) return GF_OK; + + mfurl->count = 1; + GF_SAFEALLOC(mfurl->vals, SFURL) + sfurl = mfurl->vals; + sfurl->OD_ID = stream_id; + if (stream_id) return GF_OK; + + if (!strncmp(iri->string, "data:", 5)) { + const char *cache_dir = gf_cfg_get_key(is->root_od->term->user->config, "General", "CacheDirectory"); + return gf_node_store_embedded_data(iri, cache_dir, "embedded_"); + } + sfurl->url = gf_term_resolve_xlink(node, iri->string); + return e; +} + + +/* Creates a subscene from the xlink:href */ +static GF_InlineScene *gf_svg_get_subscene(GF_Node *elt, XLinkAttributesPointers *xlinkp, SMILSyncAttributesPointers *syncp, Bool use_sync, Bool primary_resource) +{ + MFURL url; + Bool lock_timelines = 0; + GF_MediaObject *mo; + GF_SceneGraph *graph = gf_node_get_graph(elt); + GF_InlineScene *is = (GF_InlineScene *)gf_sg_get_private(graph); + if (!is) return NULL; + + if (use_sync && syncp) { + switch ((syncp->syncBehavior?*syncp->syncBehavior:SMIL_SYNCBEHAVIOR_DEFAULT)) { + case SMIL_SYNCBEHAVIOR_LOCKED: + case SMIL_SYNCBEHAVIOR_CANSLIP: + lock_timelines = 1; + break; + case SMIL_SYNCBEHAVIOR_DEFAULT: + { +#if 0 + if (svg && syncp) { + switch ((syncp->syncBehaviorDefault ? *syncp->syncBehaviorDefault : SMIL_SYNCBEHAVIOR_LOCKED)) { + case SMIL_SYNCBEHAVIOR_LOCKED: + case SMIL_SYNCBEHAVIOR_CANSLIP: + lock_timelines = 1; + break; + default: + break; + } + } +#endif + } + default: + break; + } + } + memset(&url, 0, sizeof(MFURL)); + if (!xlinkp->href) return NULL; + + gf_term_get_mfurl_from_xlink(elt, &url); + + while (is->secondary_resource && is->root_od->parentscene) + is = is->root_od->parentscene; + + mo = gf_inline_get_media_object_ex(is, &url, GF_MEDIA_OBJECT_SCENE, lock_timelines, NULL, primary_resource, elt); + gf_sg_vrml_mf_reset(&url, GF_SG_VRML_MFURL); + + if (!mo || !mo->odm) return NULL; + mo->odm->subscene->secondary_resource = primary_resource ? 0 : 1; + return mo->odm->subscene; +} + + +GF_MediaObject *gf_mo_load_xlink_resource(GF_Node *node, Bool primary_resource, Double clipBegin, Double clipEnd) +{ + GF_InlineScene *new_resource; + SVGAllAttributes all_atts; + XLinkAttributesPointers xlinkp; + SMILSyncAttributesPointers syncp; + GF_InlineScene *is = gf_sg_get_private(gf_node_get_graph(node)); + if (!is) return NULL; + + gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); + xlinkp.actuate = all_atts.xlink_actuate; + xlinkp.arcrole = all_atts.xlink_arcrole; + xlinkp.href = all_atts.xlink_href; + xlinkp.role = all_atts.xlink_role; + xlinkp.show = all_atts.xlink_show; + xlinkp.title = all_atts.xlink_title; + xlinkp.type = all_atts.xlink_type; + syncp.syncBehavior = all_atts.syncBehavior; + syncp.syncBehaviorDefault = all_atts.syncBehaviorDefault; + syncp.syncMaster = all_atts.syncMaster; + syncp.syncReference = all_atts.syncReference; + syncp.syncTolerance = all_atts.syncTolerance; + syncp.syncToleranceDefault = all_atts.syncToleranceDefault; + + if (!xlinkp.href) return NULL; + + if (xlinkp.href->type == XMLRI_ELEMENTID) return NULL; +// else if (xlinkp.href->string && (xlinkp.href->string[0]=='#')) return NULL; + + new_resource = gf_svg_get_subscene(node, &xlinkp, &syncp, primary_resource ? 1 : 0, primary_resource); + if (!new_resource) return NULL; + + /*remember parent node for event propagation*/ + gf_list_add(new_resource->inline_nodes, node); + + /*play*/ + gf_mo_play(new_resource->root_od->mo, 0, -1, 0); + + return new_resource->root_od->mo; +} + +void gf_mo_unload_xlink_resource(GF_Node *node, GF_MediaObject *mo) +{ + if (!mo) return; + if (!gf_odm_lock_mo(mo)) return; + if (!mo->odm->subscene) { + gf_odm_lock(mo->odm, 0); + return; + } + if (mo->num_open) { + mo->num_open--; + gf_list_del_item(mo->odm->subscene->inline_nodes, node); + if (!mo->num_open) { + + /*do we simply stop the associated document or unload it??? to check*/ +// gf_mo_stop(mo); + gf_odm_disconnect(mo->odm, 1); + } + } + + /*ODM may be destroyed at this point !!*/ + if (mo->odm) gf_odm_lock(mo->odm, 0); +} + +#endif //GPAC_DISABLE_SVG + diff --git a/src/terminal/term_node_init.c b/src/terminal/term_node_init.c new file mode 100644 index 0000000..596e0f1 --- /dev/null +++ b/src/terminal/term_node_init.c @@ -0,0 +1,254 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +/*includes MPEG4 nodes + input sensor stack*/ +#include "input_sensor.h" +/*includes X3D nodes for WorldInfo, Inline and Key/String sensors*/ +#include +#include + +void InitMediaControl(GF_InlineScene *is, GF_Node *node); +void MC_Modified(GF_Node *node); +void InitMediaSensor(GF_InlineScene *is, GF_Node *node); +void MS_Modified(GF_Node *node); +void InitInline(GF_InlineScene *is, GF_Node *node); + + +void TraverseWorldInfo(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_InlineScene *is = (GF_InlineScene *)gf_node_get_private(node); + is->world_info = is_destroy ? NULL : (M_WorldInfo *) node; +} + +void svg_traverse_title(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_InlineScene *is = (GF_InlineScene *)gf_node_get_private(node); + is->world_info = is_destroy ? NULL : (M_WorldInfo *) node; +} + +void evaluate_term_cap(GF_Node *node) +{ + GF_SystemRTInfo rti; + Double fps; + u32 height; + Bool b_on; + u32 b_charge, b_level; + + M_TermCap *tc = (M_TermCap *)node; + GF_InlineScene *is = gf_node_get_private(node); + tc->value = 0; + switch (tc->capability) { + case 0: /*framerate*/ + fps = gf_sc_get_fps(is->root_od->term->compositor, 1); + if (fps<=5.0) tc->value = 1; + else if (fps<=10.0) tc->value = 2; + else if (fps<=20.0) tc->value = 3; + else if (fps<=40.0) tc->value = 4; + else tc->value = 5; + break; + case 1: /*colordepth*/ + return; + case 2: /*screensize*/ + height = is->root_od->term->compositor->display_height; + if (height<200) tc->value = 1; + else if (height<400) tc->value = 2; + else if (height<800) tc->value = 3; + else if (height<1600) tc->value = 4; + else tc->value = 4; + break; + case 3: /*graphics hardware*/ + return; + case 32:/*audio out format*/ + return; + case 33:/*audio out format*/ + return; + case 34:/*spatial audio cap*/ + return; + case 64:/*CPU load*/ + if (!gf_sys_get_rti(200, &rti, 0) ) return; + if (rti.total_cpu_usage<20) tc->value = 1; + else if (rti.total_cpu_usage<40) tc->value = 2; + else if (rti.total_cpu_usage<60) tc->value = 3; + else if (rti.total_cpu_usage<80) tc->value = 4; + else tc->value = 5; + break; + case 65:/*mem load*/ + if (!gf_sys_get_rti(200, &rti, GF_RTI_SYSTEM_MEMORY_ONLY) ) return; + rti.physical_memory_avail /= 1024; + if (rti.physical_memory_avail < 100) tc->value = 1; + else if (rti.physical_memory_avail < 512) tc->value = 2; + else if (rti.physical_memory_avail < 2048) tc->value = 3; + else if (rti.physical_memory_avail < 8192) tc->value = 4; + else if (rti.physical_memory_avail < 32768) tc->value = 5; + else if (rti.physical_memory_avail < 204800) tc->value = 6; + else tc->value = 7; + break; + + /*GPAC extensions*/ + case 100: /*display width*/ + tc->value = is->root_od->term->compositor->display_width; + break; + case 101: /*display height*/ + tc->value = is->root_od->term->compositor->display_height; + break; + case 102: /*frame rate*/ + tc->value = (u32) gf_sc_get_fps(is->root_od->term->compositor, 1); + break; + case 103: /*total CPU*/ + if (!gf_sys_get_rti(200, &rti, 0) ) return; + tc->value = rti.total_cpu_usage; + break; + case 104: /*process CPU*/ + if (!gf_sys_get_rti(200, &rti, 0) ) return; + tc->value = rti.process_cpu_usage; + break; + case 106: /*total memory in kB*/ + if (!gf_sys_get_rti(200, &rti, GF_RTI_SYSTEM_MEMORY_ONLY) ) return; + tc->value = (u32) (rti.physical_memory/1024); + break; + case 107: /*total memory available in kB*/ + if (!gf_sys_get_rti(200, &rti, GF_RTI_SYSTEM_MEMORY_ONLY) ) return; + tc->value = (u32) (rti.physical_memory_avail/1024); + break; + case 108: /*process memory in kB*/ + if (!gf_sys_get_rti(200, &rti, 0) ) return; + tc->value = (u32) (rti.process_memory/1024); + break; + case 109: /*battery on/off*/ + gf_sys_get_battery_state(&b_on, &b_charge, &b_level); + tc->value = b_on; + break; + case 110: /*battery charging*/ + gf_sys_get_battery_state(&b_on, &b_charge, &b_level); + tc->value = b_charge; + break; + case 111: /*battery level*/ + gf_sys_get_battery_state(&b_on, &b_charge, &b_level); + tc->value = b_level; + break; + + case 112: /*audio vol*/ + tc->value = gf_sys_get_battery_state(&b_on, &b_charge, &b_level); + break; + case 113: /*audio pan*/ + tc->value = gf_sys_get_battery_state(&b_on, &b_charge, &b_level); + break; + default: + return; + } + gf_node_event_out(node, 2); +} + +static void InitTermCap(GF_InlineScene *is, GF_Node *node) +{ + M_TermCap *tc = (M_TermCap *)node; + tc->on_evaluate = evaluate_term_cap; + gf_node_set_private(node, is); + /*evaluate upon init (cf BIFS spec)*/ + evaluate_term_cap(node); +} + +void gf_term_on_node_init(void *_is, GF_Node *node) +{ + GF_InlineScene *is = (GF_InlineScene *)_is; + if (!node || !is) return; + + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_Inline: + case TAG_X3D_Inline: + InitInline(is, node); break; + case TAG_MPEG4_MediaBuffer: break; + case TAG_MPEG4_MediaControl: InitMediaControl(is, node); break; + case TAG_MPEG4_MediaSensor: InitMediaSensor(is, node); break; + case TAG_MPEG4_InputSensor: InitInputSensor(is, node); break; + + /*BIFS nodes, get back to codec, but filter externProtos*/ + case TAG_MPEG4_Conditional: break; + case TAG_MPEG4_QuantizationParameter: break; + /*world info is stored at the inline scene level*/ + case TAG_MPEG4_WorldInfo: + case TAG_X3D_WorldInfo: + gf_node_set_callback_function(node, TraverseWorldInfo); + gf_node_set_private(node, is); + break; + + case TAG_X3D_KeySensor: InitKeySensor(is, node); break; + case TAG_X3D_StringSensor: InitStringSensor(is, node); break; + + case TAG_MPEG4_TermCap: + InitTermCap(is, node); break; + +#ifndef GPAC_DISABLE_SVG + + case TAG_SVG_title: + gf_node_set_callback_function(node, svg_traverse_title); + gf_node_set_private(node, is); + break; +#endif + + default: gf_sc_on_node_init(is->root_od->term->compositor, node); break; + } +} + +void gf_term_on_node_modified(void *_is, GF_Node *node) +{ + GF_InlineScene *is = (GF_InlineScene *)_is; + if (!is) return; + if (!node) { + gf_sc_invalidate(is->root_od->term->compositor, NULL); + return; + } + + switch (gf_node_get_tag(node)) { + case TAG_MPEG4_Inline: + case TAG_X3D_Inline: + gf_inline_on_modified(node); break; + case TAG_MPEG4_MediaBuffer: break; + case TAG_MPEG4_MediaControl: MC_Modified(node); break; + case TAG_MPEG4_MediaSensor: MS_Modified(node); break; + case TAG_MPEG4_InputSensor: InputSensorModified(node); break; + case TAG_MPEG4_Conditional: break; + default: gf_sc_invalidate(is->root_od->term->compositor, node); break; + } +} + +GF_EXPORT +void gf_term_node_callback(void *_is, u32 type, GF_Node *n, void *param) +{ + if (type==GF_SG_CALLBACK_MODIFIED) gf_term_on_node_modified(_is, n); + else if (type==GF_SG_CALLBACK_INIT) gf_term_on_node_init(_is, n); + /*get all inline nodes using this subscene and bubble up...*/ + else if (type==GF_SG_CALLBACK_GRAPH_DIRTY) { + u32 i=0; + GF_Node *root; + GF_InlineScene *is = (GF_InlineScene *)_is; + + while ((root=(GF_Node*)gf_list_enum(is->inline_nodes, &i))) { + gf_node_dirty_set(root, GF_SG_CHILD_DIRTY, 1); + } + } +} diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c new file mode 100644 index 0000000..e4a0d27 --- /dev/null +++ b/src/terminal/terminal.c @@ -0,0 +1,1323 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media terminal sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +/*textual command processing*/ +#include + +u32 gf_term_get_time(GF_Terminal *term) +{ + assert(term); + return gf_sc_get_clock(term->compositor); +} + +void gf_term_invalidate_compositor(GF_Terminal *term) +{ + gf_sc_invalidate(term->compositor, NULL); +} + +static Bool check_user(GF_User *user) +{ + if (!user->config) return 0; + if (!user->modules) return 0; + if (!user->opaque) return 0; + return 1; +} + +static Bool term_script_action(void *opaque, u32 type, GF_Node *n, GF_JSAPIParam *param) +{ + Bool ret; + GF_Terminal *term = (GF_Terminal *) opaque; + + if (type==GF_JSAPI_OP_MESSAGE) { + gf_term_message(term, term->root_scene->root_od->net_service->url, param->info.msg, param->info.e); + return 1; + } + if (type==GF_JSAPI_OP_GET_TERM) { + param->term = term; + return 1; + } + if (type==GF_JSAPI_OP_RESOLVE_XLINK) { +#ifndef GPAC_DISABLE_SVG + param->uri.url = (char *) gf_term_resolve_xlink(n, (char *) param->uri.url); + return 1; +#else + return 0; +#endif + } + if (type==GF_JSAPI_OP_GET_OPT) { + param->gpac_cfg.key_val = gf_cfg_get_key(term->user->config, param->gpac_cfg.section, param->gpac_cfg.key); + return 1; + } + if (type==GF_JSAPI_OP_SET_OPT) { + gf_cfg_set_key(term->user->config, param->gpac_cfg.section, param->gpac_cfg.key, param->gpac_cfg.key_val); + return 1; + } + if (type==GF_JSAPI_OP_GET_DOWNLOAD_MANAGER) { + param->dnld_man = term->downloader; + return 1; + } + if (type==GF_JSAPI_OP_SET_TITLE) { + GF_Event evt; + if (!term->user->EventProc) return 0; + evt.type = GF_EVENT_SET_CAPTION; + evt.caption.caption = param->uri.url; + term->user->EventProc(term->user->opaque, &evt); + return 1; + } + if (type==GF_JSAPI_OP_GET_DCCI) { + param->scene = term->dcci_doc; + return 1; + } + if (type==GF_JSAPI_OP_GET_SUBSCENE) { + GF_InlineScene *is = (GF_InlineScene *)gf_node_get_private(n); + param->scene = is->graph; + return 1; + } + if (type==GF_JSAPI_OP_EVAL_IRI) { + GF_InlineScene *is; + if (!n) return 0; + is = (GF_InlineScene *)gf_sg_get_private(gf_node_get_graph(n)); + if (!param->uri.url) { + /*wait for compositor to be completely setup*/ + if (is->graph_attached && term->compositor->scene && !term->compositor->msg_type) return 1; + return 0; + } + /*todo*/ + return 0; + } + + + if (type==GF_JSAPI_OP_GET_SCENE_URI) { + GF_InlineScene *is = (GF_InlineScene *)gf_sg_get_private(gf_node_get_graph(n)); + param->uri.url = is->root_od->net_service->url; + param->uri.nb_params = 0; + return 1; + } + ret = gf_sc_script_action(term->compositor, type, n, param); + if (ret) return ret; + + if (type==GF_JSAPI_OP_LOAD_URL) { + if (gf_sg_get_private(gf_node_get_graph(n)) == term->root_scene) { + GF_Event evt; + if (!term->user->EventProc) return 0; + evt.type = GF_EVENT_NAVIGATE; + evt.navigate.to_url = param->uri.url; + evt.navigate.parameters = param->uri.params; + evt.navigate.param_count = param->uri.nb_params; + return term->user->EventProc(term->user->opaque, &evt); + } else { + /*TODO*/ + return 0; + } + } + return 0; +} + + +static void gf_term_reload_cfg(GF_Terminal *term) +{ + const char *sOpt; + Double fps; + u32 mode; + s32 prio; + + if (!term) return; + + /*reload term part*/ + + sOpt = gf_cfg_get_key(term->user->config, "Systems", "AlwaysDrawBIFS"); + if (sOpt && !stricmp(sOpt, "yes")) + term->flags &= ~GF_TERM_SYSDEC_RESYNC; + else + term->flags |= GF_TERM_SYSDEC_RESYNC; + + sOpt = gf_cfg_get_key(term->user->config, "Systems", "ForceSingleClock"); + if (sOpt && !stricmp(sOpt, "yes")) + term->flags |= GF_TERM_SINGLE_CLOCK; + else + term->flags &= ~GF_TERM_SINGLE_CLOCK; + + sOpt = gf_cfg_get_key(term->user->config, "Compositor", "FrameRate"); + if (sOpt) { + fps = atof(sOpt); + term->frame_duration = (u32) (1000/fps); + gf_sc_set_fps(term->compositor, fps); + } + + if (term->user->init_flags & GF_TERM_NO_VISUAL_THREAD){ + //gf_term_set_threading(term->mediaman, 1); + } else { + prio = GF_THREAD_PRIORITY_NORMAL; + sOpt = gf_cfg_get_key(term->user->config, "Systems", "Priority"); + if (sOpt) { + if (!stricmp(sOpt, "low")) prio = GF_THREAD_PRIORITY_LOWEST; + else if (!stricmp(sOpt, "normal")) prio = GF_THREAD_PRIORITY_NORMAL; + else if (!stricmp(sOpt, "high")) prio = GF_THREAD_PRIORITY_HIGHEST; + else if (!stricmp(sOpt, "real-time")) prio = GF_THREAD_PRIORITY_REALTIME; + } else { + gf_cfg_set_key(term->user->config, "Systems", "Priority", "normal"); + } + gf_term_set_priority(term, prio); + + sOpt = gf_cfg_get_key(term->user->config, "Systems", "ThreadingPolicy"); + if (sOpt) { + mode = GF_TERM_THREAD_FREE; + if (!stricmp(sOpt, "Single")) mode = GF_TERM_THREAD_SINGLE; + else if (!stricmp(sOpt, "Multi")) mode = GF_TERM_THREAD_MULTI; + gf_term_set_threading(term, mode); + } + } + + /*default data timeout is 20 sec*/ + term->net_data_timeout = 20000; + sOpt = gf_cfg_get_key(term->user->config, "Network", "DataTimeout"); + if (sOpt) term->net_data_timeout = atoi(sOpt); + + if (term->root_scene) gf_inline_set_duration(term->root_scene); + +#ifndef GPAC_DISABLE_SVG + if (term->dcci_doc) { + sOpt = gf_cfg_get_key(term->user->config, "General", "EnvironmentFile"); + gf_sg_reload_xml_doc(sOpt, term->dcci_doc); + } +#endif + + /*reload compositor config*/ + gf_sc_set_option(term->compositor, GF_OPT_RELOAD_CONFIG, 1); +} + +static Bool gf_term_get_user_pass(void *usr_cbk, const char *site_url, char *usr_name, char *password) +{ + GF_Event evt; + GF_Terminal *term = (GF_Terminal *)usr_cbk; + evt.auth.site_url = site_url; + evt.auth.user = usr_name; + evt.auth.password = password; + return term->user->EventProc(term->user->opaque, &evt); +} + + +void gf_term_pause_all_clocks(GF_Terminal *term, Bool pause) +{ + u32 i, j; + GF_ClientService *ns; + /*pause all clocks on all services*/ + i=0; + while ( (ns = (GF_ClientService*)gf_list_enum(term->net_services, &i)) ) { + GF_Clock *ck; + j=0; + while ( (ck = (GF_Clock *)gf_list_enum(ns->Clocks, &j)) ) { + if (pause) gf_clock_pause(ck); + else gf_clock_resume(ck); + } + } +} + +static void gf_term_set_play_state(GF_Terminal *term, u32 PlayState, Bool reset_audio, Bool pause_clocks) +{ + /*only play/pause if connected*/ + if (!term || !term->root_scene) return; + /*and if not already paused/playing*/ + if (!term->play_state && !PlayState) return; + if (term->play_state && (PlayState==GF_STATE_PAUSED)) return; + + /*pause compositor*/ + if ((PlayState==GF_STATE_PLAYING) && reset_audio) + gf_sc_set_option(term->compositor, GF_OPT_PLAY_STATE, 0xFF); + else + gf_sc_set_option(term->compositor, GF_OPT_PLAY_STATE, PlayState); + + if (PlayState==GF_STATE_STEP_PAUSE) PlayState = term->play_state ? GF_STATE_PLAYING : GF_STATE_PAUSED; + if (term->play_state == PlayState) return; + term->play_state = PlayState; + + if (!pause_clocks) return; + + gf_term_pause_all_clocks(term, PlayState ? 1 : 0); +} + +static void gf_term_connect_from_time_ex(GF_Terminal * term, const char *URL, u64 startTime, Bool pause_at_first_frame, Bool secondary_scene) +{ + GF_InlineScene *is; + GF_ObjectManager *odm; + const char *main_url; + if (!URL || !strlen(URL)) return; + + if (term->root_scene) { + if (term->root_scene->root_od && term->root_scene->root_od->net_service) { + main_url = term->root_scene->root_od->net_service->url; + if (main_url && !strcmp(main_url, URL)) { + gf_term_play_from_time(term, 0, pause_at_first_frame); + return; + } + } + /*disconnect*/ + gf_term_disconnect(term); + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Connecting to %s\n", URL)); + + gf_term_lock_net(term, 1); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Creating new root scene\n", URL)); + /*create a new scene*/ + is = gf_inline_new(NULL); + gf_sg_set_script_action(is->graph, term_script_action, term); + odm = gf_odm_new(); + + is->root_od = odm; + term->root_scene = is; + odm->parentscene = NULL; + odm->subscene = is; + odm->term = term; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] root scene created\n", URL)); + gf_term_lock_net(term, 0); + + odm->media_start_time = startTime; + /*render first visual frame and pause*/ + if (pause_at_first_frame) + gf_term_set_play_state(term, GF_STATE_STEP_PAUSE, 0, 0); + /*connect - we don't have any parentID */ + gf_term_connect_object(term, odm, (char *) URL, NULL); +} + +GF_EXPORT +GF_Terminal *gf_term_new(GF_User *user) +{ + u32 i; + GF_Terminal *tmp; + const char *cf; + + if (!check_user(user)) return NULL; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Creating terminal\n")); + + tmp = (GF_Terminal*)malloc(sizeof(GF_Terminal)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_Terminal)); + + /*just for safety*/ + gf_sys_init(); + + tmp->user = user; + + /*this is not changeable at runtime*/ + if (user->init_flags & GF_TERM_NO_VISUAL_THREAD) { + tmp->flags |= GF_TERM_DRAW_FRAME; + } else { + cf = gf_cfg_get_key(user->config, "Systems", "NoVisualThread"); + if (!cf || !stricmp(cf, "no")) { + tmp->flags &= ~GF_TERM_DRAW_FRAME; + } else { + tmp->flags |= GF_TERM_DRAW_FRAME; + } + } + + /*setup scene compositor*/ + tmp->compositor = gf_sc_new(user, !(tmp->flags & GF_TERM_DRAW_FRAME) , tmp); + if (!tmp->compositor) { + free(tmp); + return NULL; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] compositor loaded\n")); + gf_sc_set_fps(tmp->compositor, 30.0); + tmp->frame_duration = (u32) (1000/30); + + tmp->downloader = gf_dm_new(user->config); + gf_dm_set_auth_callback(tmp->downloader, gf_term_get_user_pass, tmp); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] downloader loaded\n")); + + tmp->net_services = gf_list_new(); + tmp->net_services_to_remove = gf_list_new(); + tmp->channels_pending = gf_list_new(); + tmp->media_queue = gf_list_new(); + + tmp->net_mx = gf_mx_new("GlobalNetwork"); + tmp->input_streams = gf_list_new(); + tmp->x3d_sensors = gf_list_new(); + + /*mode is changed when reloading cfg*/ + gf_term_init_scheduler(tmp, GF_TERM_THREAD_FREE); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Terminal created - loading config\n")); + gf_term_reload_cfg(tmp); + +#ifndef GPAC_DISABLE_SVG + cf = gf_cfg_get_key(user->config, "General", "EnvironmentFile"); + if (cf) { + GF_Err e = gf_sg_new_from_xml_doc(cf, &tmp->dcci_doc); + if (e!=GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Terminal] Error %s while loading file %s - user environment disabled\n", gf_error_to_string(e), cf)); + } else { + gf_sg_set_script_action(tmp->dcci_doc, term_script_action, tmp); + } + } +#endif + + /*load extensions*/ + tmp->extensions = gf_list_new(); + for (i=0; i< gf_modules_get_count(user->modules); i++) { + GF_TermExt *ifce = (GF_TermExt *) gf_modules_load_interface(user->modules, i, GF_TERM_EXT_INTERFACE); + if (ifce) gf_list_add(tmp->extensions, ifce); + } + tmp->unthreaded_extensions = gf_list_new(); + for (i=0; i< gf_list_count(tmp->extensions); i++) { + GF_TermExt *ifce = gf_list_get(tmp->extensions, i); + if (!ifce->process(ifce, tmp, GF_TERM_EXT_START)) { + gf_list_rem(tmp->extensions, i); + i--; + } else if (ifce->caps & GF_TERM_EXT_CAP_NOT_THREADED) { + gf_list_add(tmp->unthreaded_extensions, ifce); + } + } + if (!gf_list_count(tmp->unthreaded_extensions)) { + gf_list_del(tmp->unthreaded_extensions); + tmp->unthreaded_extensions = NULL; + } + + cf = gf_cfg_get_key(user->config, "General", "GUIFile"); + if (cf) { + gf_term_connect_from_time_ex(tmp, cf, 0, 0, 1); + } + return tmp; +} + +GF_EXPORT +GF_Err gf_term_del(GF_Terminal * term) +{ + GF_Err e; + u32 timeout, i; + + if (!term) return GF_BAD_PARAM; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Destroying terminal\n")); + /*close main service*/ + gf_term_disconnect(term); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] main service disconnected\n")); + + /*wait for destroy*/ + e = GF_IO_ERR; + timeout = 1000; + while (term->root_scene || gf_list_count(term->net_services) || gf_list_count(term->net_services_to_remove)) { + gf_sleep(30); + /*this shouldn't be needed but unfortunately there's a bug hanging around there...*/ + timeout--; + if (!timeout) break; + } + if (timeout) { + assert(!gf_list_count(term->net_services)); + assert(!gf_list_count(term->net_services_to_remove)); + e = GF_OK; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] All network services deleted\n")); + + /*unload extensions*/ + for (i=0; i< gf_list_count(term->extensions); i++) { + GF_TermExt *ifce = gf_list_get(term->extensions, i); + ifce->process(ifce, term, GF_TERM_EXT_STOP); + } + + /*stop the media manager */ + gf_term_stop_scheduler(term); + + /*unload extensions*/ + for (i=0; i< gf_list_count(term->extensions); i++) { + GF_TermExt *ifce = gf_list_get(term->extensions, i); + gf_modules_close_interface((GF_BaseInterface *) ifce); + } + gf_list_del(term->extensions); + if (term->unthreaded_extensions) gf_list_del(term->unthreaded_extensions); + + /*delete compositor before the input sensor stacks to avoid receiving events from the compositor + when destroying these stacks*/ + gf_sc_del(term->compositor); + + gf_list_del(term->net_services); + gf_list_del(term->net_services_to_remove); + gf_list_del(term->input_streams); + gf_list_del(term->x3d_sensors); + assert(!gf_list_count(term->channels_pending)); + gf_list_del(term->channels_pending); + assert(!gf_list_count(term->media_queue)); + assert(!term->nodes_pending); + gf_list_del(term->media_queue); + if (term->downloader) gf_dm_del(term->downloader); + + if (term->dcci_doc) { + if (term->dcci_doc->modified) { + char *pref_file = (char *)gf_cfg_get_key(term->user->config, "General", "EnvironmentFile"); + GF_SceneDumper *dumper = gf_sm_dumper_new(term->dcci_doc, pref_file, ' ', GF_SM_DUMP_AUTO_XML); + if (!dumper) return GF_IO_ERR; + e = gf_sm_dump_graph(dumper, 1, 0); + gf_sm_dumper_del(dumper); + } + gf_sg_del(term->dcci_doc); + } + gf_mx_del(term->net_mx); + + gf_sys_close(); + free(term); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Terminal destroyed\n")); + return e; +} + + +void gf_term_message(GF_Terminal *term, const char *service, const char *message, GF_Err error) +{ + if (!term || !term->user) return; + GF_USER_MESSAGE(term->user, service, message, error); +} + +GF_Err gf_term_step_clocks(GF_Terminal * term, u32 ms_diff) +{ + u32 i, j; + GF_ClientService *ns; + /*only play/pause if connected*/ + if (!term || !term->root_scene || !term->root_scene->root_od) return GF_BAD_PARAM; + if (!term->play_state) return GF_BAD_PARAM; + + gf_sc_lock(term->compositor, 1); + i=0; + while ( (ns = (GF_ClientService*)gf_list_enum(term->net_services, &i)) ) { + GF_Clock *ck; + j=0; + while ( (ck = (GF_Clock *)gf_list_enum(ns->Clocks, &j)) ) { + ck->init_time += ms_diff; + } + } + term->compositor->step_mode = 1; + term->compositor->draw_next_frame = 1; + gf_sc_lock(term->compositor, 0); + return GF_OK; +} + +GF_EXPORT +void gf_term_connect_from_time(GF_Terminal * term, const char *URL, u64 startTime, Bool pause_at_first_frame) +{ + gf_term_connect_from_time_ex(term, URL, startTime, pause_at_first_frame, 0); +} + +GF_EXPORT +void gf_term_connect(GF_Terminal * term, const char *URL) +{ + gf_term_connect_from_time(term, URL, 0, 0); +} + +GF_EXPORT +void gf_term_disconnect(GF_Terminal *term) +{ + if (!term->root_scene) return; + /*resume*/ + if (term->play_state) gf_term_set_play_state(term, GF_STATE_PLAYING, 1, 1); + + if (term->root_scene->root_od) { + gf_odm_disconnect(term->root_scene->root_od, 1); + } else { + gf_inline_del(term->root_scene); + term->root_scene = NULL; + } + while (term->root_scene || gf_list_count(term->net_services_to_remove)) { + gf_term_handle_services(term); + gf_sleep(10); + } +} + +GF_EXPORT +const char *gf_term_get_url(GF_Terminal *term) +{ + if (!term || !term->root_scene) return NULL; + return term->root_scene->root_od->net_service->url; +} + +static GF_Err gf_term_set_cache_state(GF_Terminal *term, u32 state) +{ + GF_Err e; + if (!term) return GF_BAD_PARAM; + if (term->enable_cache && (state==GF_MEDIA_CACHE_ENABLED)) return GF_OK; + if (!term->enable_cache && (state!=GF_MEDIA_CACHE_ENABLED)) return GF_OK; + + term->enable_cache = !term->enable_cache; + /*not connected, nothing to do*/ + if (!term->root_scene) return GF_OK; + gf_term_lock_net(term, 1); + if (term->enable_cache) { + /*otherwise start cache*/ + e = gf_term_service_cache_load(term->root_scene->root_od->net_service); + } else { + e = gf_term_service_cache_close(term->root_scene->root_od->net_service, (state==GF_MEDIA_CACHE_DISCARD) ? 1 : 0); + } + gf_term_lock_net(term, 0); + return e; +} + + +/*set rendering option*/ +GF_EXPORT +GF_Err gf_term_set_option(GF_Terminal * term, u32 type, u32 value) +{ + if (!term) return GF_BAD_PARAM; + switch (type) { + case GF_OPT_PLAY_STATE: + gf_term_set_play_state(term, value, 0, 1); + return GF_OK; + case GF_OPT_RELOAD_CONFIG: + gf_term_reload_cfg(term); + return GF_OK; + case GF_OPT_MEDIA_CACHE: + gf_term_set_cache_state(term, value); + return GF_OK; + default: + return gf_sc_set_option(term->compositor, type, value); + } +} + +GF_EXPORT +GF_Err gf_term_set_simulation_frame_rate(GF_Terminal * term, Double frame_rate) +{ + if (!term) return GF_BAD_PARAM; + term->frame_duration = (u32) (1000.0 / frame_rate); + gf_sc_set_fps(term->compositor, frame_rate); + return GF_OK; +} + +GF_EXPORT +Double gf_term_get_simulation_frame_rate(GF_Terminal *term) +{ + return term ? term->compositor->frame_rate : 0.0; +} + + +/*returns 0 if any of the clock still hasn't seen EOS*/ +u32 Term_CheckClocks(GF_ClientService *ns, GF_InlineScene *is) +{ + GF_Clock *ck; + u32 i; + if (is) { + GF_ObjectManager *odm; + if (is->root_od->net_service != ns) { + if (!Term_CheckClocks(is->root_od->net_service, is)) return 0; + } + i=0; + while ( (odm = (GF_ObjectManager*)gf_list_enum(is->ODlist, &i)) ) { + if (odm->net_service != ns) { + if (!Term_CheckClocks(odm->net_service, NULL)) return 0; + } + } + } + i=0; + while ( (ck = (GF_Clock *)gf_list_enum(ns->Clocks, &i) ) ) { + if (!ck->has_seen_eos) return 0; + } + return 1; +} + +u32 Term_CheckIsOver(GF_Terminal *term) +{ + if (!term->root_scene) return 1; + /*if input sensors consider the scene runs forever*/ + if (gf_list_count(term->input_streams)) return 0; + if (gf_list_count(term->x3d_sensors)) return 0; + + /*check no clocks are still running*/ + if (!Term_CheckClocks(term->root_scene->root_od->net_service, term->root_scene)) return 0; + if (term->root_scene->is_dynamic_scene) return 1; + /*ask compositor if there are sensors*/ + return gf_sc_get_option(term->compositor, GF_OPT_IS_FINISHED); +} + +/*get rendering option*/ +GF_EXPORT +u32 gf_term_get_option(GF_Terminal * term, u32 type) +{ + if (!term) return 0; + switch (type) { + case GF_OPT_HAS_JAVASCRIPT: return gf_sg_has_scripting(); + case GF_OPT_IS_FINISHED: return Term_CheckIsOver(term); + case GF_OPT_PLAY_STATE: + if (term->compositor->step_mode) return GF_STATE_STEP_PAUSE; + if (term->root_scene) { + GF_Clock *ck = term->root_scene->dyn_ck; + if (!ck) { + if (!term->root_scene->scene_codec) return GF_STATE_PAUSED; + ck = term->root_scene->scene_codec->ck; + if (!ck) return GF_STATE_PAUSED; + } + if (ck->Buffering) + return GF_STATE_STEP_PAUSE; + } + if (term->play_state) return GF_STATE_PAUSED; + return GF_STATE_PLAYING; + case GF_OPT_MEDIA_CACHE: + if (!term->enable_cache) return GF_MEDIA_CACHE_DISABLED; + else if (term->root_scene && term->root_scene->root_od->net_service->cache) return GF_MEDIA_CACHE_RUNNING; + else return GF_MEDIA_CACHE_ENABLED; + case GF_OPT_CAN_SELECT_STREAMS: return (term->root_scene && term->root_scene->is_dynamic_scene) ? 1 : 0; + default: return gf_sc_get_option(term->compositor, type); + } +} + + +GF_EXPORT +GF_Err gf_term_set_size(GF_Terminal * term, u32 NewWidth, u32 NewHeight) +{ + if (!term) return GF_BAD_PARAM; + return gf_sc_set_size(term->compositor, NewWidth, NewHeight); +} + +void gf_term_handle_services(GF_Terminal *term) +{ + GF_ClientService *ns; + + /*we could run into a deadlock if some thread has requested opening of a URL. If we cannot + grab the network now, we'll do our management at the next cycle*/ + if (!gf_mx_try_lock(term->net_mx)) + return; + + /*play ODs that need it*/ + while (gf_list_count(term->media_queue)) { + GF_ObjectManager *odm = (GF_ObjectManager *)gf_list_get(term->media_queue, 0); + gf_list_rem(term->media_queue, 0); + /*unlock net before sending play/pause*/ + gf_mx_v(term->net_mx); + /*this is a stop*/ + if (odm->media_start_time == (u64)-1) { + odm->media_start_time = 0; + gf_odm_stop(odm, 0); + } + /*this is a play*/ + else { + gf_odm_play(odm); + } + + /*relock net before sending play/pause*/ + gf_mx_p(term->net_mx); + } + gf_mx_v(term->net_mx); + + /*lock to avoid any start attemps from compositor*/ + if (!gf_mx_try_lock(term->compositor->mx)) + return; + while (gf_list_count(term->net_services_to_remove)) { + gf_mx_p(term->net_mx); + ns = (GF_ClientService*)gf_list_get(term->net_services_to_remove, 0); + if (ns) gf_list_rem(term->net_services_to_remove, 0); + gf_mx_v(term->net_mx); + if (!ns) break; + gf_term_service_del(ns); + } + + if (term->nodes_pending) { + u32 i, count, n_count; + i=0; + count = gf_list_count(term->nodes_pending); + while (inodes_pending, i); + gf_node_traverse(n, NULL); + if (!term->nodes_pending) break; + n_count = gf_list_count(term->nodes_pending); + if (n_count==count) i++; + else count=n_count; + } + } + gf_sc_lock(term->compositor, 0); + + /*extensions*/ + if (!term->reload_state && term->unthreaded_extensions) { + u32 i, count; + count = gf_list_count(term->unthreaded_extensions); + for (i=0; iunthreaded_extensions, i); + ifce->process(ifce, term, GF_TERM_EXT_PROCESS); + } + } + + + /*need to reload*/ + if (term->reload_state == 1) { + term->reload_state = 0; + gf_term_disconnect(term); + term->reload_state = 2; + } + if (term->reload_state == 2) { + if (gf_list_count(term->net_services)) return; + term->reload_state = 0; + gf_term_connect(term, term->reload_url); + free(term->reload_url); + term->reload_url = NULL; + } +} + +void gf_term_queue_node_traverse(GF_Terminal *term, GF_Node *node) +{ + gf_sc_lock(term->compositor, 1); + if (!term->nodes_pending) term->nodes_pending = gf_list_new(); + gf_list_add(term->nodes_pending, node); + gf_sc_lock(term->compositor, 0); +} +void gf_term_unqueue_node_traverse(GF_Terminal *term, GF_Node *node) +{ + gf_sc_lock(term->compositor, 1); + if (term->nodes_pending) { + gf_list_del_item(term->nodes_pending, node); + if (!gf_list_count(term->nodes_pending)) { + gf_list_del(term->nodes_pending); + term->nodes_pending = NULL; + } + } + gf_sc_lock(term->compositor, 0); +} + +void gf_term_close_services(GF_Terminal *term, GF_ClientService *ns) +{ + GF_Err e; + + /*prevent the media manager / term to access the list of services to destroy, otherwise + we could unload the module while poping its CloseService() call stack which can lead to + random crashes (return adresses no longer valid) - cf any "stress mode" playback of a playlist*/ + gf_mx_p(term->net_mx); + ns->owner = NULL; + e = ns->ifce->CloseService(ns->ifce); + /*if error don't wait for ACK to remove from main list*/ + if (e) { + gf_list_del_item(term->net_services, ns); + if (gf_list_find(term->net_services_to_remove, ns)<0) + gf_list_add(term->net_services_to_remove, ns); + } + gf_mx_v(term->net_mx); +} + +void gf_term_lock_compositor(GF_Terminal *term, Bool LockIt) +{ + gf_sc_lock(term->compositor, LockIt); +} + + +void gf_term_lock_net(GF_Terminal *term, Bool LockIt) +{ + if (LockIt) { + gf_mx_p(term->net_mx); + } else { + gf_mx_v(term->net_mx); + } +} + +static void mae_collect_info(GF_ClientService *net, GF_ObjectManager *odm, GF_DOMMediaAccessEvent *mae, u32 transport, u32 *min_time, u32 *min_buffer) +{ + u32 i=0; + GF_Channel *ch; + + while ((ch = (GF_Channel*)gf_list_enum(odm->channels, &i))) { + u32 val; + if (ch->service != net) continue; + + mae->bufferValid = 1; + if (ch->BufferTime>0) { + if (ch->MaxBuffer) { + val = (ch->BufferTime * 100) / ch->MaxBuffer; + if (*min_buffer > val) *min_buffer = val; + } else { + if (*min_buffer > 100) *min_buffer = 100; + } + if (*min_time > (u32) ch->BufferTime) + *min_time = ch->BufferTime; + } else { + *min_time = 0; + *min_buffer = 0; + } + if (mae->nb_streams<20) { + mae->streams[mae->nb_streams].streamType = ch->esd->decoderConfig->streamType; + mae->streams[mae->nb_streams].mediaType = ch->esd->decoderConfig->objectTypeIndication; + mae->streams[mae->nb_streams].transport = transport; + mae->nb_streams ++; + } + } +} + +void gf_term_service_media_event(GF_ObjectManager *odm, u32 event_type) +{ +#ifndef GPAC_DISABLE_SVG + u32 i, count, min_buffer, min_time, transport; + Bool locked; + GF_DOMMediaAccessEvent mae; + GF_DOM_Event evt; + GF_ObjectManager *an_od; + GF_InlineScene *is; + + if (!odm) return; + if (odm->mo) { + count = gf_list_count(odm->mo->nodes); + if (!count) return; + if (!(gf_node_get_dom_event_filter(gf_list_get(odm->mo->nodes, 0)) & GF_DOM_EVENT_MEDIA_ACCESS)) + return; + } else { + count = 0; + } + + + memset(&mae, 0, sizeof(GF_DOMMediaAccessEvent)); + transport = 0; + mae.bufferValid = 0; + mae.session_name = odm->net_service->url; + if (!strnicmp(mae.session_name, "rtsp:", 5) + || !strnicmp(mae.session_name, "sdp:", 4) + || !strnicmp(mae.session_name, "rtp:", 4) + ) + transport = 1; + else if (!strnicmp(mae.session_name, "dvb:", 4)) + transport = 2; + + min_time = min_buffer = (u32) -1; + is = odm->subscene ? odm->subscene : odm->parentscene; + /*get buffering on root OD*/ + mae_collect_info(odm->net_service, is->root_od, &mae, transport, &min_time, &min_buffer); + /*get buffering on all ODs*/ + i=0; + while ((an_od = (GF_ObjectManager*)gf_list_enum(is->ODlist, &i))) { + mae_collect_info(odm->net_service, an_od, &mae, transport, &min_time, &min_buffer); + } + + mae.level = min_buffer; + mae.remaining_time = INT2FIX(min_time) / 60; + mae.status = 0; + + memset(&evt, 0, sizeof(GF_DOM_Event)); + evt.mae = &mae; + evt.type = event_type; + evt.bubbles = 0; /*the spec says yes but we force it to NO*/ + + /*lock scene to prevent concurrent access of scene data*/ + locked = gf_mx_try_lock(odm->term->compositor->mx); + for (i=0; imo->nodes, i); + gf_dom_event_fire(node, &evt); + } + if (!count) { + GF_Node *root = gf_sg_get_root_node(is->graph); + if (root) gf_dom_event_fire(root, &evt); + } + if (locked) gf_sc_lock(odm->term->compositor, 0); +#endif +} + + +/*connects given OD manager to its URL*/ +void gf_term_connect_object(GF_Terminal *term, GF_ObjectManager *odm, char *serviceURL, GF_ClientService *ParentService) +{ + GF_ClientService *ns; + u32 i; + GF_Err e; + gf_term_lock_net(term, 1); + + /*for remoteODs/dynamic ODs, check if one of the running service cannot be used*/ + i=0; + while ( (ns = (GF_ClientService*)gf_list_enum(term->net_services, &i)) ) { + if (gf_term_service_can_handle_url(ns, serviceURL)) { + gf_term_lock_net(term, 0); + + /*wait for service to setup - service may become destroyed if not available*/ + while (1) { + gf_term_lock_net(term, 1); + if (!ns->owner) { + gf_term_lock_net(term, 0); + return; + } + gf_term_lock_net(term, 0); + if (ns->owner->OD) break; + gf_sleep(5); + } + odm->net_service = ns; + gf_odm_setup_entry_point(odm, serviceURL); + return; + } + } + + odm->net_service = gf_term_service_new(term, odm, serviceURL, ParentService, &e); + if (!odm->net_service) { + gf_term_lock_net(term, 0); + gf_term_message(term, serviceURL, "Cannot open service", e); + gf_odm_disconnect(odm, 1); + return; + } + gf_term_lock_net(term, 0); + + /*OK connect*/ + gf_term_service_media_event(odm, GF_EVENT_MEDIA_BEGIN_SESSION_SETUP); + odm->net_service->ifce->ConnectService(odm->net_service->ifce, odm->net_service, odm->net_service->url); +} + +/*connects given channel to its URL if needed*/ +GF_Err gf_term_connect_remote_channel(GF_Terminal *term, GF_Channel *ch, char *URL) +{ + GF_Err e; + u32 i; + GF_ClientService *ns; + + gf_term_lock_net(term, 1); + + /*if service is handled by current service don't create new one*/ + if (gf_term_service_can_handle_url(ch->service, URL)) { + gf_term_lock_net(term, 0); + return GF_OK; + } + i=0; + while ( (ns = (GF_ClientService*)gf_list_enum(term->net_services, &i)) ) { + if (gf_term_service_can_handle_url(ns, URL)) { + ch->service = ns; + gf_term_lock_net(term, 0); + return GF_OK; + } + } + /*use parent OD for parent service*/ + ns = gf_term_service_new(term, NULL, URL, ch->odm->net_service, &e); + if (!ns) return e; + ch->service = ns; + ns->ifce->ConnectService(ns->ifce, ns, ns->url); + + gf_term_lock_net(term, 0); + return GF_OK; +} + +GF_EXPORT +u32 gf_term_play_from_time(GF_Terminal *term, u64 from_time, Bool pause_at_first_frame) +{ + if (!term || !term->root_scene || !term->root_scene->root_od) return 0; + if (term->root_scene->root_od->flags & GF_ODM_NO_TIME_CTRL) return 1; + + /*for dynamic scene OD ressources are static and all object use the same clock, so don't restart the root + OD, just act as a mediaControl on all playing streams*/ + if (term->root_scene->is_dynamic_scene) { + /*exit pause mode*/ + gf_term_set_play_state(term, GF_STATE_PLAYING, 1, 1); + + if (pause_at_first_frame) + gf_term_set_play_state(term, GF_STATE_STEP_PAUSE, 0, 0); + + gf_sc_lock(term->compositor, 1); + gf_inline_restart_dynamic(term->root_scene, from_time); + gf_sc_lock(term->compositor, 0); + return 2; + } + + /*pause everything*/ + gf_term_set_play_state(term, GF_STATE_PAUSED, 0, 1); + /*stop root*/ + gf_odm_stop(term->root_scene->root_od, 1); + gf_inline_disconnect(term->root_scene, 0); + /*make sure we don't have OD queued*/ + while (gf_list_count(term->media_queue)) gf_list_rem(term->media_queue, 0); + term->root_scene->root_od->media_start_time = from_time; + + gf_odm_start(term->root_scene->root_od); + gf_term_set_play_state(term, pause_at_first_frame ? GF_STATE_STEP_PAUSE : GF_STATE_PLAYING, 0, 1); + return 2; +} + + +GF_EXPORT +Bool gf_term_user_event(GF_Terminal * term, GF_Event *evt) +{ + if (term) return gf_sc_user_event(term->compositor, evt); + return 0; +} + + +GF_EXPORT +Double gf_term_get_framerate(GF_Terminal *term, Bool absoluteFPS) +{ + if (!term || !term->compositor) return 0; + return gf_sc_get_fps(term->compositor, absoluteFPS); +} + +/*get main scene current time in sec*/ +GF_EXPORT +u32 gf_term_get_time_in_ms(GF_Terminal *term) +{ + if (!term || !term->root_scene) return 0; + if (term->root_scene->scene_codec && term->root_scene->scene_codec->ck) return gf_clock_ellapse_time(term->root_scene->scene_codec->ck); + else if (term->root_scene->dyn_ck) return gf_clock_ellapse_time(term->root_scene->dyn_ck); + return 0; +} + +GF_Node *gf_term_pick_node(GF_Terminal *term, s32 X, s32 Y) +{ + if (!term || !term->compositor) return NULL; + return gf_sc_pick_node(term->compositor, X, Y); +} + +GF_EXPORT +void gf_term_navigate_to(GF_Terminal *term, const char *toURL) +{ + if (!toURL && !term->root_scene) return; + if (term->reload_url) free(term->reload_url); + term->reload_url = NULL; + if (term->root_scene) + term->reload_url = gf_url_concatenate(term->root_scene->root_od->net_service->url, toURL); + if (!term->reload_url) term->reload_url = strdup(toURL); + term->reload_state = 1; +} + +GF_EXPORT +GF_Err gf_term_get_viewpoint(GF_Terminal *term, u32 viewpoint_idx, const char **outName, Bool *is_bound) +{ + return gf_sc_get_viewpoint(term->compositor, viewpoint_idx, outName, is_bound); +} + +GF_EXPORT +GF_Err gf_term_set_viewpoint(GF_Terminal *term, u32 viewpoint_idx, const char *viewpoint_name) +{ + return gf_sc_set_viewpoint(term->compositor, viewpoint_idx, viewpoint_name); +} + +GF_EXPORT +GF_Err gf_term_add_object(GF_Terminal *term, const char *url, Bool auto_play) +{ + GF_MediaObject *mo; + SFURL sfurl; + MFURL mfurl; + if (!url || !term || !term->root_scene || !term->root_scene->is_dynamic_scene) return GF_BAD_PARAM; + + sfurl.OD_ID = GF_ESM_DYNAMIC_OD_ID; + sfurl.url = (char *) url; + mfurl.count = 1; + mfurl.vals = &sfurl; + /*only text tracks are supported for now...*/ + mo = gf_inline_get_media_object(term->root_scene, &mfurl, GF_MEDIA_OBJECT_TEXT, 1); + if (mo) { + /*check if we must deactivate it*/ + if (mo->odm) { + if (mo->num_open && !auto_play) { + gf_inline_select_object(term->root_scene, mo->odm); + } else { + mo->odm->OD_PL = auto_play ? 1 : 0; + } + } else { + gf_list_del_item(term->root_scene->media_objects, mo); + gf_sg_vrml_mf_reset(&mo->URLs, GF_SG_VRML_MFURL); + free(mo); + mo = NULL; + } + } + return mo ? GF_OK : GF_NOT_SUPPORTED; +} + + +void gf_term_attach_service(GF_Terminal *term, GF_InputService *service_hdl) +{ + Bool net_check_interface(GF_InputService *ifce); + GF_InlineScene *is; + GF_ObjectManager *odm; + if (!net_check_interface(service_hdl)) return; + + if (term->root_scene) gf_term_disconnect(term); + + gf_term_lock_net(term, 1); + + /*create a new scene*/ + is = gf_inline_new(NULL); + odm = gf_odm_new(); + gf_sg_set_script_action(is->graph, term_script_action, term); + + is->root_od = odm; + term->root_scene = is; + odm->parentscene = NULL; + odm->subscene = is; + odm->term = term; + + GF_SAFEALLOC(odm->net_service , GF_ClientService); + odm->net_service->term = term; + odm->net_service->owner = odm; + odm->net_service->ifce = service_hdl; + odm->net_service->url = strdup("Internal Service Handler"); + odm->net_service->Clocks = gf_list_new(); + gf_list_add(term->net_services, odm->net_service); + + gf_term_lock_net(term, 0); + + /*OK connect*/ + odm->net_service->ifce->ConnectService(odm->net_service->ifce, odm->net_service, odm->net_service->url); +} + +GF_EXPORT +GF_Err gf_term_scene_update(GF_Terminal *term, char *type, char *com) +{ + GF_Err e; + GF_StreamContext *sc; + GF_ESD *esd; + Bool is_xml = 0; + Double time = 0; + u32 i, tag; + GF_SceneLoader load; + + if (!term) return GF_BAD_PARAM; + + memset(&load, 0, sizeof(GF_SceneLoader)); + load.localPath = gf_cfg_get_key(term->user->config, "General", "CacheDirectory"); + load.flags = GF_SM_LOAD_FOR_PLAYBACK | GF_SM_LOAD_CONTEXT_READY; + load.type = GF_SM_LOAD_BT; + + if (!term->root_scene) { + gf_term_lock_net(term, 1); + /*create a new scene*/ + term->root_scene = gf_inline_new(NULL); + gf_sg_set_script_action(term->root_scene->graph, term_script_action, term); + term->root_scene->root_od = gf_odm_new(); + term->root_scene->root_od->parentscene = NULL; + term->root_scene->root_od->subscene = term->root_scene; + term->root_scene->root_od->term = term; + gf_term_lock_net(term, 0); + load.ctx = gf_sm_new(term->root_scene->graph); + } else if (term->root_scene->root_od->OD) { + load.ctx = gf_sm_new(term->root_scene->graph); + /*restore streams*/ + i=0; + while ((esd = (GF_ESD*)gf_list_enum(term->root_scene->root_od->OD->ESDescriptors, &i)) ) { + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: + case GF_STREAM_SCENE: + case GF_STREAM_PRIVATE_SCENE: + sc = gf_sm_stream_new(load.ctx, esd->ESID, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); + if (esd->decoderConfig->streamType==GF_STREAM_PRIVATE_SCENE) sc->streamType = GF_STREAM_SCENE; + sc->timeScale = esd->slConfig->timestampResolution; + break; + } + } + } else { + return GF_BAD_PARAM; + } + load.ctx->max_node_id = gf_sg_get_max_node_id(term->root_scene->graph); + + i=0; + while ((com[i] == ' ') || (com[i] == '\r') || (com[i] == '\n') || (com[i] == '\t')) i++; + if (com[i]=='<') is_xml = 1; + + load.type = is_xml ? GF_SM_LOAD_XMTA : GF_SM_LOAD_BT; + time = gf_inline_get_time(term->root_scene); + + + if (type && (!stricmp(type, "application/x-laser+xml") || !stricmp(type, "laser"))) { + load.type = GF_SM_LOAD_XSR; + time = gf_inline_get_time(term->root_scene); + } + else if (type && (!stricmp(type, "image/svg+xml") || !stricmp(type, "svg")) ) { + load.type = GF_SM_LOAD_XSR; + time = gf_inline_get_time(term->root_scene); + } + else if (type && (!stricmp(type, "model/x3d+xml") || !stricmp(type, "x3d")) ) load.type = GF_SM_LOAD_X3D; + else if (type && (!stricmp(type, "model/x3d+vrml") || !stricmp(type, "x3dv")) ) load.type = GF_SM_LOAD_X3DV; + else if (type && (!stricmp(type, "model/vrml") || !stricmp(type, "vrml")) ) load.type = GF_SM_LOAD_VRML; + else if (type && (!stricmp(type, "application/x-xmt") || !stricmp(type, "xmt")) ) load.type = GF_SM_LOAD_XMTA; + else if (type && (!stricmp(type, "application/x-bt") || !stricmp(type, "bt")) ) load.type = GF_SM_LOAD_BT; + else if (gf_sg_get_root_node(term->root_scene->graph)) { + tag = gf_node_get_tag(gf_sg_get_root_node(term->root_scene->graph)); + if (tag >= GF_NODE_RANGE_FIRST_SVG) { + load.type = GF_SM_LOAD_XSR; + time = gf_inline_get_time(term->root_scene); + } else if (tag>=GF_NODE_RANGE_FIRST_X3D) { + load.type = is_xml ? GF_SM_LOAD_X3D : GF_SM_LOAD_X3DV; + } else { + load.type = is_xml ? GF_SM_LOAD_XMTA : GF_SM_LOAD_BT; + time = gf_inline_get_time(term->root_scene); + } + } + + e = gf_sm_load_string(&load, com, 1); + if (!e) { + u32 j, au_count, st_count; + st_count = gf_list_count(load.ctx->streams); + for (i=0; istreams, i); + au_count = gf_list_count(sc->AUs); + for (j=0; jAUs, j); + e = gf_sg_command_apply_list(term->root_scene->graph, au->commands, time); + if (e) break; + } + } + } + if (!term->root_scene->graph_attached) { + if (!load.ctx->scene_width || !load.ctx->scene_height) { +// load.ctx->scene_width = term->compositor->width; +// load.ctx->scene_height = term->compositor->height; + } + gf_sg_set_scene_size_info(term->root_scene->graph, load.ctx->scene_width, load.ctx->scene_height, load.ctx->is_pixel_metrics); + gf_inline_attach_to_compositor(term->root_scene); + } + gf_sm_del(load.ctx); + return e; +} + +GF_Err gf_term_get_screen_buffer(GF_Terminal *term, GF_VideoSurface *framebuffer) +{ + if (!term) return GF_BAD_PARAM; + return gf_sc_get_screen_buffer(term->compositor, framebuffer, 0); +} + +GF_Err gf_term_release_screen_buffer(GF_Terminal *term, GF_VideoSurface *framebuffer) +{ + if (!term) return GF_BAD_PARAM; + return gf_sc_release_screen_buffer(term->compositor, framebuffer); +} + + +static void gf_term_sample_scenetime(GF_InlineScene *scene) +{ + u32 i, count; + gf_inline_sample_time(scene); + count = gf_list_count(scene->ODlist); + for (i=0; iODlist, i); + if (odm->subscene) gf_term_sample_scenetime(odm->subscene); + } +} + +void gf_term_sample_clocks(GF_Terminal *term) +{ + gf_term_sample_scenetime(term->root_scene); +} + +const char *gf_term_get_text_selection(GF_Terminal *term, Bool probe_only) +{ + Bool has_text; + if (!term) return NULL; + has_text = gf_sc_has_text_selection(term->compositor); + if (!has_text) return NULL; + if (probe_only) return ""; + return gf_sc_get_selected_text(term->compositor); +} + + +GF_Err gf_term_paste_text(GF_Terminal *term, const char *txt, Bool probe_only) +{ + if (!term) return GF_BAD_PARAM; + if (probe_only) return gf_sc_paste_text(term->compositor, NULL); + return gf_sc_paste_text(term->compositor, txt); +} + + diff --git a/src/utils/base_encoding.c b/src/utils/base_encoding.c new file mode 100644 index 0000000..736fff2 --- /dev/null +++ b/src/utils/base_encoding.c @@ -0,0 +1,179 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +static const char base_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +GF_EXPORT +u32 gf_base64_encode(char *_in, u32 inSize, char *_out, u32 outSize) +{ + s32 padding; + u32 i = 0, j = 0; + unsigned char *in = (unsigned char *)_in; + unsigned char *out = (unsigned char *)_out; + + if (outSize < (inSize * 4 / 3)) return 0; + + while (i < inSize) { + padding = 3 - (inSize - i); + if (padding == 2) { + out[j] = base_64[in[i]>>2]; + out[j+1] = base_64[(in[i] & 0x03) << 4]; + out[j+2] = '='; + out[j+3] = '='; + } else if (padding == 1) { + out[j] = base_64[in[i]>>2]; + out[j+1] = base_64[((in[i] & 0x03) << 4) | ((in[i+1] & 0xf0) >> 4)]; + out[j+2] = base_64[(in[i+1] & 0x0f) << 2]; + out[j+3] = '='; + } else{ + out[j] = base_64[in[i]>>2]; + out[j+1] = base_64[((in[i] & 0x03) << 4) | ((in[i+1] & 0xf0) >> 4)]; + out[j+2] = base_64[((in[i+1] & 0x0f) << 2) | ((in[i+2] & 0xc0) >> 6)]; + out[j+3] = base_64[in[i+2] & 0x3f]; + } + i += 3; + j += 4; + } + return j; +} + +static const unsigned char index_64[128] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +#define char64(c) ((c > 127) ? (char) 0xff : index_64[(c)]) + +/*denoise input*/ +u32 load_block(char *in, u32 size, u32 pos, char *out) +{ + u32 i, len; + u8 c; + len = i = 0; + while ((len<4) && ((pos+i)='A') && (c<='Z')) + || ((c>='a') && (c<='z')) + || ((c>='0') && (c<='9')) + || (c=='=') || (c=='+') || (c=='/') + ) { + out[len] = c; + len++; + } + i++; + } + while (len<4) { out[len] = (char) 0xFF; len++; } + return pos+i; +} + +GF_EXPORT +u32 gf_base64_decode(char *in_buf, u32 inSize, char *out, u32 outSize) +{ + u32 i = 0, j = 0, padding; + unsigned char c[4], in[4]; + + if (outSize < (inSize * 3 / 4)) return 0; + + while ((i + 3) < inSize) { + padding = 0; + i = load_block(in_buf, inSize, i, (char*)in); + c[0] = char64(in[0]); + padding += (c[0] == 0xff); + c[1] = char64(in[1]); + padding += (c[1] == 0xff); + c[2] = char64(in[2]); + padding += (c[2] == 0xff); + c[3] = char64(in[3]); + padding += (c[3] == 0xff); + if (padding == 2) { + out[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); + out[j] = (c[1] & 0x0f) << 4; + } else if (padding == 1) { + out[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); + out[j++] = ((c[1] & 0x0f) << 4) | ((c[2] & 0x3c) >> 2); + out[j] = (c[2] & 0x03) << 6; + } else { + out[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); + out[j++] = ((c[1] & 0x0f) << 4) | ((c[2] & 0x3c) >> 2); + out[j++] = ((c[2] & 0x03) << 6) | (c[3] & 0x3f); + } + //i += 4; + } + return j; +} + + +/* + * Copyright (c) ENST 2004 - Philippe de Cuetos + */ + +static const char base_16[] = "0123456789abcdef"; + +GF_EXPORT +u32 gf_base16_encode(char *_in, u32 inSize, char *_out, u32 outSize) +{ + u32 i = 0; + unsigned char *in = (unsigned char *)_in; + unsigned char *out = (unsigned char *)_out; + + if (outSize < (inSize * 2)+1) return 0; + + for (i=0;i> 4)]; + out[2*i+1] = base_16[(in[i] & 0x0f)]; + } + out[(inSize * 2)] = 0; + + return i; +} + +#define char16(nb) (((nb) < 97) ? ((nb)-48) : ((nb)-87)) + +GF_EXPORT +u32 gf_base16_decode(char *in, u32 inSize, char *out, u32 outSize) +{ + u32 j=0; + u32 c[2] = {0,0}; + + if (outSize < (inSize / 2)) return 0; + if ((inSize % 2) != 0) return 0; + + for (j=0;j + +/*the default size for new streams allocation...*/ +#define BS_MEM_BLOCK_ALLOC_SIZE 250 + +/*private types*/ +enum +{ + GF_BITSTREAM_FILE_READ = GF_BITSTREAM_WRITE + 1, + GF_BITSTREAM_FILE_WRITE, + /*private mode if we own the buffer*/ + GF_BITSTREAM_WRITE_DYN +}; + +struct __tag_bitstream +{ + /*original stream data*/ + FILE *stream; + + /*original data*/ + char *original; + /*the size of our buffer*/ + u64 size; + /*current position in BYTES*/ + u64 position; + /*the byte readen/written */ + u32 current; + /*the number of bits in the current byte*/ + u32 nbBits; + /*the bitstream mode*/ + u32 bsmode; + + void (*EndOfStream)(void *par); + void *par; +}; + + +GF_EXPORT +GF_BitStream *gf_bs_new(char *buffer, u64 BufferSize, u32 mode) +{ + GF_BitStream *tmp; + if ( (buffer && ! BufferSize)) return NULL; + + tmp = (GF_BitStream *)malloc(sizeof(GF_BitStream)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_BitStream)); + + tmp->original = (char*)buffer; + tmp->size = BufferSize; + + tmp->position = 0; + tmp->current = 0; + tmp->bsmode = mode; + tmp->stream = NULL; + + switch (tmp->bsmode) { + case GF_BITSTREAM_READ: + tmp->nbBits = 8; + tmp->current = 0; + break; + case GF_BITSTREAM_WRITE: + tmp->nbBits = 0; + if (! buffer) { + /*if BufferSize is specified, use it. This is typically used when AvgSize of + some buffers is known, but some exceed it.*/ + if (BufferSize) { + tmp->size = BufferSize; + } else { + tmp->size = BS_MEM_BLOCK_ALLOC_SIZE; + } + tmp->original = (char *) malloc(sizeof(char) * ((u32) tmp->size)); + if (! tmp->original) { + free(tmp); + return NULL; + } + tmp->bsmode = GF_BITSTREAM_WRITE_DYN; + } else { + tmp->original = (char*)buffer; + tmp->size = BufferSize; + } + break; + default: + /*the stream constructor is not the same...*/ + free(tmp); + return NULL; + } + return tmp; +} + +GF_EXPORT +GF_BitStream *gf_bs_from_file(FILE *f, u32 mode) +{ + GF_BitStream *tmp; + if (!f) return NULL; + + tmp = (GF_BitStream *)malloc(sizeof(GF_BitStream)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_BitStream)); + /*switch to internal mode*/ + mode = (mode==GF_BITSTREAM_READ) ? GF_BITSTREAM_FILE_READ : GF_BITSTREAM_FILE_WRITE; + tmp->bsmode = mode; + tmp->current = 0; + tmp->nbBits = (mode == GF_BITSTREAM_FILE_READ) ? 8 : 0; + tmp->original = NULL; + tmp->position = 0; + tmp->stream = f; + + /*get the size of this file (for read streams)*/ + tmp->position = gf_f64_tell(f); + gf_f64_seek(f, 0, SEEK_END); + tmp->size = gf_f64_tell(f); + gf_f64_seek(f, tmp->position, SEEK_SET); + return tmp; +} + +GF_EXPORT +void gf_bs_del(GF_BitStream *bs) +{ + if (!bs) return; + /*if we are in dynamic mode (alloc done by the bitstream), free the buffer if still present*/ + if ((bs->bsmode == GF_BITSTREAM_WRITE_DYN) && bs->original) free(bs->original); + free(bs); +} + + +/*returns 1 if aligned wrt current mode, 0 otherwise*/ +static Bool BS_IsAlign(GF_BitStream *bs) +{ + switch (bs->bsmode) { + case GF_BITSTREAM_READ: + case GF_BITSTREAM_FILE_READ: + return ( (8 == bs->nbBits) ? 1 : 0); + default: + return !bs->nbBits; + } +} + + +/*fetch a new byte in the bitstream switch between packets*/ +static u8 BS_ReadByte(GF_BitStream *bs) +{ + if (bs->bsmode == GF_BITSTREAM_READ) { + if (bs->position >= bs->size) { + if (bs->EndOfStream) bs->EndOfStream(bs->par); + return 0; + } + return (u32) bs->original[bs->position++]; + } + /*we are in FILE mode, test for end of file*/ + if (!feof(bs->stream)) { + bs->position++; + return (u32) fgetc(bs->stream); + } + if (bs->EndOfStream) bs->EndOfStream(bs->par); + return 0; +} + +#define NO_OPTS + +#ifndef NO_OPTS +static u32 bit_mask[] = {0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1}; +static u32 bits_mask[] = {0x0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F}; +#endif + +GF_EXPORT +u8 gf_bs_read_bit(GF_BitStream *bs) +{ + if (bs->nbBits == 8) { + bs->current = BS_ReadByte(bs); + bs->nbBits = 0; + } +#ifdef NO_OPTS + { + s32 ret; + bs->current <<= 1; + bs->nbBits++; + ret = (bs->current & 0x100) >> 8; + return (u8) ret; + } +#else + return (u8) (bs->current & bit_mask[bs->nbBits++]) ? 1 : 0; +#endif + +} + +GF_EXPORT +u32 gf_bs_read_int(GF_BitStream *bs, u32 nBits) +{ + u32 ret; + +#ifndef NO_OPTS + if (nBits + bs->nbBits <= 8) { + bs->nbBits += nBits; + ret = (bs->current >> (8 - bs->nbBits) ) & bits_mask[nBits]; + return ret; + } +#endif + ret = 0; + while (nBits-- > 0) { + ret <<= 1; + ret |= gf_bs_read_bit(bs); + } + return ret; +} + +GF_EXPORT +u32 gf_bs_read_u8(GF_BitStream *bs) +{ + assert(bs->nbBits==8); + return (u32) BS_ReadByte(bs); +} + +GF_EXPORT +u32 gf_bs_read_u16(GF_BitStream *bs) +{ + u32 ret; + assert(bs->nbBits==8); + ret = BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); + return ret; +} + + +GF_EXPORT +u32 gf_bs_read_u24(GF_BitStream *bs) +{ + u32 ret; + assert(bs->nbBits==8); + ret = BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); + return ret; +} + +GF_EXPORT +u32 gf_bs_read_u32(GF_BitStream *bs) +{ + u32 ret; + assert(bs->nbBits==8); + ret = BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); + return ret; +} + +GF_EXPORT +u64 gf_bs_read_u64(GF_BitStream *bs) +{ + u64 ret; + ret = gf_bs_read_u32(bs); ret<<=32; + ret |= gf_bs_read_u32(bs); + return ret; +} + +GF_EXPORT +u64 gf_bs_read_long_int(GF_BitStream *bs, u32 nBits) +{ + u64 ret = 0; + if (nBits>64) { + gf_bs_read_long_int(bs, nBits-64); + ret = gf_bs_read_long_int(bs, 64); + } else { + while (nBits-- > 0) { + ret <<= 1; + ret |= gf_bs_read_bit(bs); + } + } + return ret; +} + + +GF_EXPORT +Float gf_bs_read_float(GF_BitStream *bs) +{ + char buf [4] = "\0\0\0"; +#ifdef NO_OPTS + s32 i; + for (i = 0; i < 32; i++) + buf[3-i/8] |= gf_bs_read_bit(bs) << (7 - i%8); +#else + buf[3] = gf_bs_read_int(bs, 8); + buf[2] = gf_bs_read_int(bs, 8); + buf[1] = gf_bs_read_int(bs, 8); + buf[0] = gf_bs_read_int(bs, 8); +#endif + return (* (Float *) buf); +} + +GF_EXPORT +Double gf_bs_read_double(GF_BitStream *bs) +{ + char buf [8] = "\0\0\0\0\0\0\0"; + s32 i; + for (i = 0; i < 64; i++) + buf[7-i/8] |= gf_bs_read_bit(bs) << (7 - i%8); + return (* (Double *) buf); +} + +GF_EXPORT +u32 gf_bs_read_data(GF_BitStream *bs, char *data, u32 nbBytes) +{ + u64 orig = bs->position; + + if (bs->position+nbBytes > bs->size) return 0; + + if (BS_IsAlign(bs)) { + switch (bs->bsmode) { + case GF_BITSTREAM_READ: + memcpy(data, bs->original + bs->position, nbBytes); + bs->position += nbBytes; + return nbBytes; + case GF_BITSTREAM_FILE_READ: + case GF_BITSTREAM_FILE_WRITE: + fread(data, nbBytes, 1, bs->stream); + bs->position += nbBytes; + return nbBytes; + default: + return 0; + } + } + + while (nbBytes-- > 0) { + *data++ = gf_bs_read_int(bs, 8); + } + return (u32) (bs->position - orig); + +} + + + +static void BS_WriteByte(GF_BitStream *bs, u8 val) +{ + /*we don't allow write on READ buffers*/ + if ( (bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_FILE_READ) ) return; + if (!bs->original && !bs->stream) return; + + /*we are in MEM mode*/ + if ( (bs->bsmode == GF_BITSTREAM_WRITE) || (bs->bsmode == GF_BITSTREAM_WRITE_DYN) ) { + if (bs->position == bs->size) { + /*no more space...*/ + if (bs->bsmode != GF_BITSTREAM_WRITE_DYN) return; + /*realloc if enough space...*/ + if (bs->size > 0xFFFFFFFF) return; + bs->original = (char*)realloc(bs->original, (u32) (bs->size + BS_MEM_BLOCK_ALLOC_SIZE)); + if (!bs->original) return; + bs->size += BS_MEM_BLOCK_ALLOC_SIZE; + } + bs->original[bs->position] = val; + bs->position++; + return; + } + /*we are in FILE mode, no pb for any realloc...*/ + fputc(val, bs->stream); + /*check we didn't rewind the stream*/ + if (bs->size == bs->position) bs->size++; + bs->position += 1; +} + +static void BS_WriteBit(GF_BitStream *bs, u32 bit) +{ + bs->current <<= 1; + bs->current |= bit; + if (++ bs->nbBits == 8) { + bs->nbBits = 0; + BS_WriteByte(bs, (u8) bs->current); + bs->current = 0; + } +} + +GF_EXPORT +void gf_bs_write_int(GF_BitStream *bs, s32 value, s32 nBits) +{ + value <<= sizeof (s32) * 8 - nBits; + while (--nBits >= 0) { + BS_WriteBit (bs, value < 0); + value <<= 1; + } +} + +GF_EXPORT +void gf_bs_write_long_int(GF_BitStream *bs, s64 value, s32 nBits) +{ + if (nBits>64) { + gf_bs_write_int(bs, 0, nBits-64); + gf_bs_write_long_int(bs, value, 64); + } else { + value <<= sizeof (s64) * 8 - nBits; + while (--nBits >= 0) { + BS_WriteBit (bs, value < 0); + value <<= 1; + } + } +} + +GF_EXPORT +void gf_bs_write_u8(GF_BitStream *bs, u32 value) +{ + assert(!bs->nbBits); + BS_WriteByte(bs, (u8) value); +} + +GF_EXPORT +void gf_bs_write_u16(GF_BitStream *bs, u32 value) +{ + assert(!bs->nbBits); + BS_WriteByte(bs, (u8) ((value>>8)&0xff)); + BS_WriteByte(bs, (u8) ((value)&0xff)); +} + +GF_EXPORT +void gf_bs_write_u24(GF_BitStream *bs, u32 value) +{ + assert(!bs->nbBits); + BS_WriteByte(bs, (u8) ((value>>16)&0xff)); + BS_WriteByte(bs, (u8) ((value>>8)&0xff)); + BS_WriteByte(bs, (u8) ((value)&0xff)); +} + +GF_EXPORT +void gf_bs_write_u32(GF_BitStream *bs, u32 value) +{ + assert(!bs->nbBits); + BS_WriteByte(bs, (u8) ((value>>24)&0xff)); + BS_WriteByte(bs, (u8) ((value>>16)&0xff)); + BS_WriteByte(bs, (u8) ((value>>8)&0xff)); + BS_WriteByte(bs, (u8) ((value)&0xff)); +} + +GF_EXPORT +void gf_bs_write_u64(GF_BitStream *bs, u64 value) +{ + assert(!bs->nbBits); + gf_bs_write_u32(bs, (u32) ((value>>32)&0xffffffff)); + gf_bs_write_u32(bs, (u32) (value&0xffffffff)); +} + +GF_EXPORT +void gf_bs_write_float(GF_BitStream *bs, Float value) +{ + u32 i; + union + { float f; + char sz [4]; + } float_value; + float_value.f = value; + + for (i = 0; i < 32; i++) + BS_WriteBit(bs, (float_value.sz [3 - i / 8] & 1 << (7 - i % 8)) != 0); + +} + +GF_EXPORT +void gf_bs_write_double (GF_BitStream *bs, Double value) +{ + u32 i; + union + { Double d; + char sz [8]; + } double_value; + double_value.d = value; + for (i = 0; i < 64; i++) { + BS_WriteBit(bs, (double_value.sz [7 - i / 8] & 1 << (7 - i % 8)) != 0); + } +} + + +GF_EXPORT +u32 gf_bs_write_data(GF_BitStream *bs, char *data, u32 nbBytes) +{ + /*we need some feedback for this guy...*/ + u64 begin = bs->position; + if (!nbBytes) return 0; + + if (BS_IsAlign(bs)) { + switch (bs->bsmode) { + case GF_BITSTREAM_WRITE: + if (bs->position+nbBytes > bs->size) + return 0; + memcpy(bs->original + bs->position, data, nbBytes); + bs->position += nbBytes; + return nbBytes; + case GF_BITSTREAM_WRITE_DYN: + /*need to realloc ...*/ + if (bs->position+nbBytes > bs->size) { + if (bs->size + nbBytes > 0xFFFFFFFF) + return 0; + bs->original = (char*)realloc(bs->original, sizeof(u32)*((u32) bs->size + nbBytes)); + if (!bs->original) + return 0; + bs->size += nbBytes; + } + memcpy(bs->original + bs->position, data, nbBytes); + bs->position += nbBytes; + return nbBytes; + case GF_BITSTREAM_FILE_READ: + case GF_BITSTREAM_FILE_WRITE: + if (fwrite(data, nbBytes, 1, bs->stream) != 1) return 0; + if (bs->size == bs->position) bs->size += nbBytes; + bs->position += nbBytes; + return nbBytes; + default: + return 0; + } + } + + while (nbBytes) { + gf_bs_write_int(bs, (s32) *data, 8); + data++; + nbBytes--; + } + return (u32) (bs->position - begin); +} + +/*align return the num of bits read in READ mode, 0 in WRITE*/ +GF_EXPORT +u8 gf_bs_align(GF_BitStream *bs) +{ + u8 res = 8 - bs->nbBits; + if ( (bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_FILE_READ)) { + if (res > 0) { + gf_bs_read_int(bs, res); + } + return res; + } + if (bs->nbBits > 0) { + gf_bs_write_int (bs, 0, res); + return res; + } + return 0; +} + + +/*size available in the bitstream*/ +GF_EXPORT +u64 gf_bs_available(GF_BitStream *bs) +{ + s64 cur, end; + + /*in WRITE mode only, this should not be called, but return something big in case ...*/ + if ( (bs->bsmode == GF_BITSTREAM_WRITE) + || (bs->bsmode == GF_BITSTREAM_WRITE_DYN) + ) + return (u64) -1; + + /*we are in MEM mode*/ + if (bs->bsmode == GF_BITSTREAM_READ) { + return (bs->size - bs->position); + } + /*FILE READ: assume size hasn't changed, otherwise the user shall call gf_bs_get_refreshed_size*/ + if (bs->bsmode==GF_BITSTREAM_FILE_READ) return (bs->size - bs->position); + + cur = gf_f64_tell(bs->stream); + gf_f64_seek(bs->stream, 0, SEEK_END); + end = gf_f64_tell(bs->stream); + gf_f64_seek(bs->stream, cur, SEEK_SET); + return (u64) (end - cur); +} + +/*call this funct to set the buffer size to the nb of bytes written +Used only in WRITE mode, as we don't know the real size during allocation... +return -1 for bad param or malloc failed +return nbBytes cut*/ +static u32 BS_CutBuffer(GF_BitStream *bs) +{ + u32 nbBytes; + if ( (bs->bsmode != GF_BITSTREAM_WRITE_DYN) && (bs->bsmode != GF_BITSTREAM_WRITE)) return (u32) -1; + /*Align our buffer or we're dead!*/ + gf_bs_align(bs); + + nbBytes = (u32) (bs->size - bs->position); + if (!nbBytes || (nbBytes == 0xFFFFFFFF) || (bs->position >= 0xFFFFFFFF)) return 0; + bs->original = (char*)realloc(bs->original, (u32) bs->position); + if (! bs->original) return (u32) -1; + /*just in case, re-adjust..*/ + bs->size = bs->position; + return nbBytes; +} + +/*For DYN mode, this gets the content out*/ +GF_EXPORT +void gf_bs_get_content(GF_BitStream *bs, char **output, u32 *outSize) +{ + /*only in WRITE MEM mode*/ + if (bs->bsmode != GF_BITSTREAM_WRITE_DYN) return; + if (!bs->position && !bs->nbBits) { + *output = NULL; + *outSize = 0; + free(bs->original); + } else { + BS_CutBuffer(bs); + *output = bs->original; + *outSize = (u32) bs->size; + } + bs->original = NULL; + bs->size = 0; + bs->position = 0; +} + +/* Skip nbytes. + Align + If READ (MEM or FILE) mode, just read n times 8 bit + If WRITE (MEM or FILE) mode, write n times 0 on 8 bit +*/ +GF_EXPORT +void gf_bs_skip_bytes(GF_BitStream *bs, u64 nbBytes) +{ + if (!bs || !nbBytes) return; + + gf_bs_align(bs); + + /*special case for file skipping...*/ + if ((bs->bsmode == GF_BITSTREAM_FILE_WRITE) || (bs->bsmode == GF_BITSTREAM_FILE_READ)) { + gf_f64_seek(bs->stream, nbBytes, SEEK_CUR); + bs->position += nbBytes; + return; + } + + /*special case for reading*/ + if (bs->bsmode == GF_BITSTREAM_READ) { + bs->position += nbBytes; + return; + } + /*for writing we must do it this way, otherwise pb in dynamic buffers*/ + while (nbBytes) { + gf_bs_write_int(bs, 0, 8); + nbBytes--; + } +} + +/*Only valid for READ MEMORY*/ +GF_EXPORT +void gf_bs_rewind_bits(GF_BitStream *bs, u64 nbBits) +{ + u64 nbBytes; + if (bs->bsmode != GF_BITSTREAM_READ) return; + + nbBits -= (bs->nbBits); + nbBytes = (nbBits+8)>>3; + nbBits = nbBytes*8 - nbBits; + gf_bs_align(bs); + assert(bs->position >= nbBytes); + bs->position -= nbBytes + 1; + gf_bs_read_int(bs, (u32)nbBits); + return; +} + +/*seek from begining of stream: use internally even when non aligned!*/ +static GF_Err BS_SeekIntern(GF_BitStream *bs, u64 offset) +{ + u32 i; + /*if mem, do it */ + if ((bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_WRITE) || (bs->bsmode == GF_BITSTREAM_WRITE_DYN)) { + if (offset > 0xFFFFFFFF) return GF_IO_ERR; + /*0 for write, read will be done automatically*/ + if (offset >= bs->size) { + if ( (bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_WRITE) ) return GF_BAD_PARAM; + /*in DYN, realloc ...*/ + bs->original = (char*)realloc(bs->original, (u32) (offset + 1)); + for (i = 0; i < (u32) (offset + 1 - bs->size); i++) { + bs->original[bs->size + i] = 0; + } + bs->size = offset + 1; + } + bs->current = bs->original[offset]; + bs->position = offset; + bs->nbBits = (bs->bsmode == GF_BITSTREAM_READ) ? 8 : 0; + return GF_OK; + } + + gf_f64_seek(bs->stream, offset, SEEK_SET); + + bs->position = offset; + bs->current = 0; + /*setup NbBits so that next acccess to the buffer will trigger read/write*/ + bs->nbBits = (bs->bsmode == GF_BITSTREAM_FILE_READ) ? 8 : 0; + return GF_OK; +} + +/*seek from begining of stream: align before anything else*/ +GF_EXPORT +GF_Err gf_bs_seek(GF_BitStream *bs, u64 offset) +{ + /*warning: we allow offset = bs->size for WRITE buffers*/ + if (offset > bs->size) return GF_BAD_PARAM; + + gf_bs_align(bs); + return BS_SeekIntern(bs, offset); +} + +/*peek bits (as int!!) from orig position (ON BYTE BOUNDARIES, from 0) - only for read ...*/ +GF_EXPORT +u32 gf_bs_peek_bits(GF_BitStream *bs, u32 numBits, u32 byte_offset) +{ + u64 curPos; + u32 curBits, ret, current; + + if ( (bs->bsmode != GF_BITSTREAM_READ) && (bs->bsmode != GF_BITSTREAM_FILE_READ)) return 0; + if (!numBits || (bs->size < bs->position + byte_offset)) return 0; + + /*store our state*/ + curPos = bs->position; + curBits = bs->nbBits; + current = bs->current; + + if (byte_offset) gf_bs_seek(bs, bs->position + byte_offset); + ret = gf_bs_read_int(bs, numBits); + + /*restore our cache - position*/ + gf_bs_seek(bs, curPos); + /*to avoid re-reading our bits ...*/ + bs->nbBits = curBits; + bs->current = current; + return ret; +} + +GF_EXPORT +u64 gf_bs_get_refreshed_size(GF_BitStream *bs) +{ + s64 offset; + + switch (bs->bsmode) { + case GF_BITSTREAM_READ: + case GF_BITSTREAM_WRITE: + return bs->size; + + default: + offset = gf_f64_tell(bs->stream); + gf_f64_seek(bs->stream, 0, SEEK_END); + bs->size = gf_f64_tell(bs->stream); + gf_f64_seek(bs->stream, offset, SEEK_SET); + return bs->size; + } +} + +GF_EXPORT +u64 gf_bs_get_size(GF_BitStream *bs) +{ + return bs->size; +} + +GF_EXPORT +u64 gf_bs_get_position(GF_BitStream *bs) +{ + return bs->position; +} + +GF_EXPORT +u8 gf_bs_bits_available(GF_BitStream *bs) +{ + if (bs->size > bs->position) return 8; + if (bs->nbBits < 8) return (8-bs->nbBits); + return 0; +} + +GF_EXPORT +void gf_bs_set_eos_callback(GF_BitStream *bs, void (*EndOfStream)(void *par), void *par) +{ + bs->EndOfStream = EndOfStream; + bs->par = par; +} + + +GF_EXPORT +u32 gf_bs_read_u32_le(GF_BitStream *bs) +{ + u32 ret, v; + ret = gf_bs_read_int(bs, 8); + v = gf_bs_read_int(bs, 8); v<<=8; ret |= v; + v = gf_bs_read_int(bs, 8); v<<=16; ret |= v; + v = gf_bs_read_int(bs, 8); v<<=24; ret |= v; + return ret; +} + +GF_EXPORT +u16 gf_bs_read_u16_le(GF_BitStream *bs) +{ + u32 ret, v; + ret = gf_bs_read_int(bs, 8); + v = gf_bs_read_int(bs, 8); v<<=8; ret |= v; + return ret; +} + +GF_EXPORT +void gf_bs_write_u32_le(GF_BitStream *bs, u32 val) +{ + gf_bs_write_int(bs, val & 0xFF, 8); + gf_bs_write_int(bs, val>>8, 8); + gf_bs_write_int(bs, val>>16, 8); + gf_bs_write_int(bs, val>>24, 8); +} + +GF_EXPORT +void gf_bs_write_u16_le(GF_BitStream *bs, u32 val) +{ + gf_bs_write_int(bs, val & 0xFF, 8); + gf_bs_write_int(bs, val>>8, 8); +} + +GF_EXPORT +u32 gf_bs_get_bit_offset(GF_BitStream *bs) +{ + if (bs->stream) return 0; + if (bs->bsmode==GF_BITSTREAM_READ) return (u32) ( (bs->position - 1) * 8 + bs->nbBits); + return (u32) ( (bs->position ) * 8 + bs->nbBits); +} + +GF_EXPORT +u32 gf_bs_get_bit_position(GF_BitStream *bs) +{ + if (bs->stream) return 0; + return bs->nbBits; +} + +u32 gf_bs_read_vluimsbf5(GF_BitStream *bs) +{ + u32 nb_words = 0; + while (gf_bs_read_int(bs, 1)) nb_words++; + nb_words++; + return gf_bs_read_int(bs, 4*nb_words); +} + diff --git a/src/utils/color.c b/src/utils/color.c new file mode 100644 index 0000000..7d0e60f --- /dev/null +++ b/src/utils/color.c @@ -0,0 +1,1005 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include + + +/* original YUV table code from XviD colorspace module */ + +/***************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC + * - colorspace conversion module - + * + * Copyright(C) 2002 Peter Ross + * 2002 Michael Militzer + * + * follows the usual GPL license terms + ****************************************************************************/ + + + +#define col_clip(a) MAX(0, MIN(255, a)) + +static s32 RGB_Y[256]; +static s32 B_U[256]; +static s32 G_U[256]; +static s32 G_V[256]; +static s32 R_V[256]; + +#define SCALEBITS_OUT 13 +#define FIX_OUT(x) ((unsigned short) ((x) * (1L<> SCALEBITS_OUT); + dst[1] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT); + dst[2] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT); + dst[3] = 0xFF; + y_src++; + + rgb_y = RGB_Y[*y_src]; + dst[4] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT); + dst[5] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT); + dst[6] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT); + dst[7] = 0xFF; + y_src++; + + rgb_y = RGB_Y[*y_src2]; + dst2[0] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT); + dst2[1] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT); + dst2[2] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT); + dst2[3] = 0xFF; + y_src2++; + + rgb_y = RGB_Y[*y_src2]; + dst2[4] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT); + dst2[5] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT); + dst2[6] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT); + dst2[7] = 0xFF; + y_src2++; + + dst += 8; + dst2 += 8; + } +} + + + +static void gf_yuva_load_lines(unsigned char *dst, s32 dststride, unsigned char *y_src, unsigned char *u_src, unsigned char *v_src, unsigned char *a_src, + s32 y_stride, s32 uv_stride, s32 width) +{ + u32 x, hw; + unsigned char *dst2 = dst + dststride; + unsigned char *y_src2 = y_src + y_stride; + unsigned char *a_src2 = a_src + y_stride; + + yuv2rgb_init(); + + hw = width / 2; + for (x = 0; x < hw; x++) { + s32 u, v; + s32 b_u, g_uv, r_v, rgb_y; + + u = u_src[x]; + v = v_src[x]; + + b_u = B_U[u]; + g_uv = G_U[u] + G_V[v]; + r_v = R_V[v]; + + rgb_y = RGB_Y[*y_src]; + dst[0] = col_clip ( (rgb_y + r_v) >> SCALEBITS_OUT ); + dst[1] = col_clip ( (rgb_y - g_uv) >> SCALEBITS_OUT ); + dst[2] = col_clip ( (rgb_y + b_u) >> SCALEBITS_OUT ); + dst[3] = *a_src; + y_src++; + a_src++; + + rgb_y = RGB_Y[*y_src]; + dst[4] = col_clip ( (rgb_y + r_v) >> SCALEBITS_OUT ); + dst[5] = col_clip ( (rgb_y - g_uv) >> SCALEBITS_OUT ); + dst[6] = col_clip ( (rgb_y + b_u) >> SCALEBITS_OUT ); + dst[7] = *a_src; + y_src++; + a_src++; + + rgb_y = RGB_Y[*y_src2]; + dst2[0] = col_clip ( (rgb_y + r_v) >> SCALEBITS_OUT ); + dst2[1] = col_clip ( (rgb_y - g_uv) >> SCALEBITS_OUT ); + dst2[2] = col_clip ( (rgb_y + b_u) >> SCALEBITS_OUT ); + dst2[3] = *a_src2; + y_src2++; + a_src2++; + + rgb_y = RGB_Y[*y_src2]; + dst2[4] = col_clip ( (rgb_y + r_v) >> SCALEBITS_OUT ); + dst2[5] = col_clip ( (rgb_y - g_uv) >> SCALEBITS_OUT ); + dst2[6] = col_clip ( (rgb_y + b_u) >> SCALEBITS_OUT ); + dst2[7] = *a_src2; + y_src2++; + a_src2++; + + dst += 8; + dst2 += 8; + } + +} + +static s32 mul255(s32 a, s32 b) +{ + return ((a+1) * b) >> 8; +} + +typedef void (*copy_row_proto)(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha); +typedef void (*load_line_proto)(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits); + +static void copy_row_rgb_555(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + s32 pos; + u16 *dst = (u16 *)_dst; + u8 a, r, g, b; + pos = 0x10000; + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + } + if (a) *dst = GF_COL_555(r, g, b); + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + +static void copy_row_rgb_565(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + s32 pos; + u16 *dst = (u16 *)_dst; + u8 a, r, g, b; + pos = 0x10000; + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + } + if (a) *dst = GF_COL_565(r, g, b); + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + + +static void copy_row_rgb_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + s32 pos; + u8 a, r, g, b; + + pos = 0x10000; + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + } + if (a) { dst[0] = r; dst[1] = g; dst[2] = b; } + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + +static void copy_row_bgr_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + s32 pos; + u8 a, r, g, b; + + pos = 0x10000; + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + } + if (a) { dst[0] = b; dst[1] = g; dst[2] = r; } + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + +static void copy_row_rgb_32(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + u8 a, r, g, b; + s32 pos = 0x10000L; + u32 *dst = (u32 *)_dst; + + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + } + if (a) *dst = GF_COL_ARGB(a, r, g, b); + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + +static void copy_row_bgr_32(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + u8 a, r, g, b; + s32 pos = 0x10000L; + u32 *dst = (u32 *)_dst; + + while ( dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + } + if (a) *dst = GF_COL_ARGB(b, g, r, a); + dst+=x_pitch; + pos += h_inc; + dst_w--; + } +} + + +static void merge_row_rgb_555(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + u32 _r, _g, _b, a, r, g, b; + s32 pos; + u16 col, *dst = (u16 *)_dst; + + + pos = 0x10000; + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + a = mul255(a, alpha); + } + if (a && alpha) { + col = *dst; + _r = (col >> 7) & 0xf8; + _g = (col >> 2) & 0xf8; + _b = (col << 3) & 0xf8; + _r = mul255(a, r - _r) + _r; + _g = mul255(a, g - _g) + _g; + _b = mul255(a, b - _b) + _b; + *dst = GF_COL_555(_r, _g, _b); + } + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + +static void merge_row_rgb_565(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + u32 _r, _g, _b, a, r, g, b; + s32 pos; + u16 col, *dst = (u16 *)_dst; + + + pos = 0x10000; + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + a = mul255(a, alpha); + } + if (a) { + col = *dst; + _r = (col >> 8) & 0xf8; + _g = (col >> 3) & 0xfc; + _b = (col << 3) & 0xf8; + _r = mul255(a, r - _r) + _r; + _g = mul255(a, g - _g) + _g; + _b = mul255(a, b - _b) + _b; + *dst = GF_COL_565(_r, _g, _b); + } + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + + +static void merge_row_rgb_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + u32 _r, _g, _b, a, r, g, b; + s32 pos; + + pos = 0x10000; + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + a = mul255(a, alpha); + } + if (a) { + _r = dst[0]; _g = dst[0]; _b = dst[0]; + dst[0] = mul255(a, r - _r) + _r; + dst[1] = mul255(a, g - _g) + _g; + dst[2] = mul255(a, b - _b) + _b; + } + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + +static void merge_row_bgr_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + u32 _r, _g, _b, a, r, g, b; + s32 pos; + + pos = 0x10000; + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + } + + if (a && alpha) { + _r = dst[0]; _g = dst[0]; _b = dst[0]; + a = mul255(a, alpha); + dst[2] = mul255(a, r - _r) + _r; + dst[1] = mul255(a, g - _g) + _g; + dst[0] = mul255(a, b - _b) + _b; + + } + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + + +static void merge_row_rgb_32(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + u32 _r, _g, _b, a, r, g, b, col; + s32 pos; + u32 *dst = (u32 *)_dst; + + pos = 0x10000; + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + a = mul255(a, alpha); + pos -= 0x10000L; + } + + if (a) { + col = *dst; + _r = GF_COL_R(col); + _g = GF_COL_G(col); + _b = GF_COL_B(col); + _r = mul255(a, r - _r) + _r; + _g = mul255(a, g - _g) + _g; + _b = mul255(a, b - _b) + _b; + *dst = GF_COL_ARGB(0xFF, _r, _g, _b); + } + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + + +static void merge_row_argb_32(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) +{ + u32 _a, _r, _g, _b, a, r, g, b, col; + s32 pos; + u32 *dst = (u32 *)_dst; + + pos = 0x10000; + while (dst_w) { + while ( pos >= 0x10000L ) { + r = *src++; g = *src++; b = *src++; a = *src++; + pos -= 0x10000L; + a = mul255(a, alpha); + } + + if (a) { + col = *dst; + _r = GF_COL_R(col); + _g = GF_COL_G(col); + _b = GF_COL_B(col); + if (GF_COL_A(col)) { + _a = mul255(a, a) + mul255(0xFF-a, 0xFF); + _r = mul255(a, r - _r) + _r; + _g = mul255(a, g - _g) + _g; + _b = mul255(a, b - _b) + _b; + *dst = GF_COL_ARGB(_a, _r, _g, _b); + } else { + *dst = GF_COL_ARGB(a, r, g, b); + } + } + dst += x_pitch; + pos += h_inc; + dst_w--; + } +} + +static GFINLINE u8 colmask(s32 a, s32 n) +{ + s32 mask = (1 << n) - 1; + return (u8) (a & (0xff & ~mask)) | ((-((a >> n) & 1)) & mask); +} + +static void load_line_rgb_555(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +{ + u32 i; + src_bits += x_offset*3 + y_offset*y_pitch; + for (i=0; i> (10 - 3), 3); + dst_bits[1] = colmask(c >> (5 - 3), 3); + dst_bits[2] = colmask(c << 3, 3); + dst_bits[3] = 0xFF; + dst_bits+=4; + } +} + +static void load_line_rgb_565(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +{ + u32 i; + src_bits += x_offset*3 + y_offset*y_pitch; + for (i=0; i> (11 - 3), 3); + dst_bits[1] = colmask(c >> (5 - 2), 2); + dst_bits[2] = colmask(c << 3, 3); + dst_bits[3] = 0xFF; + dst_bits+=4; + } +} + +static void load_line_rgb_24(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +{ + u32 i; + src_bits += x_offset*3 + y_offset*y_pitch; + for (i=0; im[15] || cmat->m[16] || cmat->m[17] || (cmat->m[18]!=FIX_ONE) || cmat->m[19] )) has_alpha = 1; + else if (key && (key->alpha<0xFF)) has_alpha = 1; + + switch (src->pixel_format) { + case GF_PIXEL_RGB_555: + load_line = load_line_rgb_555; + break; + case GF_PIXEL_RGB_565: + load_line = load_line_rgb_565; + break; + case GF_PIXEL_RGB_24: + load_line = load_line_rgb_24; + break; + case GF_PIXEL_BGR_24: + load_line = load_line_bgr_24; + break; + case GF_PIXEL_ARGB: + has_alpha = 1; + load_line = load_line_argb; + break; + case GF_PIXEL_RGBA: + has_alpha = 1; + case GF_PIXEL_RGB_32: + load_line = load_line_rgb_32; + break; + case GF_PIXEL_BGR_32: + load_line = load_line_bgr_32; + break; + case GF_PIXEL_YV12: + case GF_PIXEL_IYUV: + case GF_PIXEL_I420: + yuv2rgb_init(); + yuv_type = 1; + break; + case GF_PIXEL_YUVA: + has_alpha = 1; + yuv_type = 2; + break; + default: + return GF_NOT_SUPPORTED; + } + + /*only RGB output supported*/ + switch (dst->pixel_format) { + case GF_PIXEL_RGB_555: + dst_bpp = sizeof(unsigned char)*2; + copy_row = has_alpha ? merge_row_rgb_555 : copy_row_rgb_555; + break; + case GF_PIXEL_RGB_565: + dst_bpp = sizeof(unsigned char)*2; + copy_row = has_alpha ? merge_row_rgb_565 : copy_row_rgb_565; + break; + case GF_PIXEL_RGB_24: + dst_bpp = sizeof(unsigned char)*3; + copy_row = has_alpha ? merge_row_rgb_24 : copy_row_rgb_24; + break; + case GF_PIXEL_BGR_24: + dst_bpp = sizeof(unsigned char)*3; + copy_row = has_alpha ? merge_row_bgr_24 : copy_row_bgr_24; + break; + case GF_PIXEL_RGB_32: + dst_bpp = sizeof(unsigned char)*4; + copy_row = has_alpha ? merge_row_rgb_32: copy_row_rgb_32; + break; + case GF_PIXEL_ARGB: + dst_bpp = sizeof(unsigned char)*4; + copy_row = has_alpha ? merge_row_argb_32: copy_row_rgb_32; + break; + case GF_PIXEL_RGBA: + dst_bpp = sizeof(unsigned char)*4; + copy_row = has_alpha ? merge_row_argb_32: copy_row_bgr_32; + break; + case GF_PIXEL_BGR_32: + dst_bpp = sizeof(unsigned char)*4; + copy_row = has_alpha ? merge_row_rgb_32: copy_row_bgr_32; + break; + default: + return GF_NOT_SUPPORTED; + } + /*x_pitch 0 means linear framebuffer*/ + if (!dst_x_pitch) dst_x_pitch = dst_bpp; + + + src_w = src_wnd ? src_wnd->w : src->width; + src_h = src_wnd ? src_wnd->h : src->height; + dst_w = dst_wnd ? dst_wnd->w : dst->width; + dst_h = dst_wnd ? dst_wnd->h : dst->height; + + if (yuv_type && (src_w%2)) src_w++; + + tmp = (u8 *) malloc(sizeof(u8) * src_w * (yuv_type ? 8 : 4) ); + rows = tmp; + + src_bits = (u8 *) src->video_buffer; + dst_bits = (u8 *) dst->video_buffer; + + pos_y = 0x10000; + inc_y = (src_h << 16) / dst_h; + inc_x = (src_w << 16) / dst_w; + x_off = src_wnd ? src_wnd->x : 0; + src_row = src_wnd ? src_wnd->y : 0; + + prev_row = -1; + + dst_bits = (u8 *) dst->video_buffer; + if (dst_wnd) dst_bits += ((s32)dst_wnd->x) * dst_x_pitch + ((s32)dst_wnd->y) * dst->pitch; + + dst_w_size = dst_bpp*dst_w; + + /*small opt here: if we need to fetch data from destination, and if destination is + hardware memory, we work on a copy of the destination line*/ + if (has_alpha && dst->is_hardware_memory) + dst_temp_bits = (u8 *) malloc(sizeof(u8) * dst_bpp * dst_w); + + + /*for 2 and 4 bytes colors, precompute pitch for u16 and u32 type casting*/ + if ((dst_bpp==2) || (dst_bpp==4) ) dst_x_pitch /= (s32) dst_bpp; + + kl = kh = ka = kr = kg = kb = 0; + if (key) { + ka = key->alpha; + kr = key->r; + kg = key->g; + kb = key->b; + kl = key->low; + kh = key->high; + if (kh==kl) kh++; + } + + while (dst_h) { + while ( pos_y >= 0x10000L ) { + src_row++; + pos_y -= 0x10000L; + } + /*new row, check if conversion is needed*/ + if (prev_row != src_row) { + u32 the_row = src_row - 1; + if (yuv_type) { + if ( the_row % 2) { + if (!yuv_init) { + yuv_init = 1; + the_row --; + if (flip) the_row = src->height-2 - the_row; + if (yuv_type==1) { + load_line_yv12(src->video_buffer, x_off, the_row, src->pitch, src_w, src->height, tmp); + } else { + load_line_yuva(src->video_buffer, x_off, the_row, src->pitch, src_w, src->height, tmp); + } + the_row = src_row - 1; + } + rows = flip ? tmp : tmp + src_w * 4; + } else { + if (flip) the_row = src->height-2 - the_row; + if (yuv_type==1) { + load_line_yv12(src->video_buffer, x_off, the_row, src->pitch, src_w, src->height, tmp); + } else { + load_line_yuva(src->video_buffer, x_off, the_row, src->pitch, src_w, src->height, tmp); + } + yuv_init = 1; + rows = flip ? tmp + src_w * 4 : tmp; + + if (cmat) { + for (i=0; i<2*src_w; i++) { + u32 idx = 4*i; + gf_cmx_apply_argb(cmat, &tmp[idx+3], &tmp[idx], &tmp[idx+1], &tmp[idx+2]); + } + } + if (key) { + for (i=0; i<2*src_w; i++) { + u32 idx = 4*i; + s32 thres, v; + v = tmp[idx]-kr; thres = ABS(v); + v = tmp[idx+1]-kg; thres += ABS(v); + v = tmp[idx+2]-kb; thres += ABS(v); + thres/=3; +#if 0 + if (thres < kl) tmp[idx+3] = 0; + else if (thres <= kh) tmp[idx+3] = (thres-kl)*ka / (kh-kl); +#else + if (thres < kh) tmp[idx+3] = 0; +#endif + else tmp[idx+3] = ka; + } + } + } + } else { + if (flip) the_row = src->height-1 - the_row; + load_line((u8*)src->video_buffer, x_off, the_row, src->pitch, src_w, tmp); + rows = tmp; + if (cmat) { + for (i=0; i 3*0xFE) tmp[idx+3] = ka; +// if ( (tmp[idx]==kr) && (tmp[idx+1]==kg) && (tmp[idx+2]==kb)) tmp[idx+3] = ka; + } + } + } + if (dst_temp_bits) { + /*load from video memory*/ + memcpy(dst_temp_bits, dst_bits, dst_w_size); + /*merge*/ + copy_row(rows, src_w, dst_temp_bits, dst_w, inc_x, dst_x_pitch, alpha); + /*copy to video memory*/ + memcpy(dst_bits, dst_temp_bits, dst_w_size); + } else { + copy_row(rows, src_w, dst_bits, dst_w, inc_x, dst_x_pitch, alpha); + } + } + /*do NOT use memcpy if the target buffer is not in systems memory*/ + else if (has_alpha || dst->is_hardware_memory) { + copy_row(rows, src_w, dst_bits, dst_w, inc_x, dst_x_pitch, alpha); + } else { + memcpy(dst_bits, dst_bits_prev, dst_w_size); + } + + pos_y += inc_y; + prev_row = src_row; + + dst_bits_prev = dst_bits; + dst_bits += dst->pitch; + dst_h--; + } + if (dst_temp_bits) free(dst_temp_bits); + free(tmp); + return GF_OK; +} + + + +/* + COLOR MATRIX TOOLS + */ + +GF_EXPORT +void gf_cmx_init(GF_ColorMatrix *_this) +{ + if (!_this) return; + memset(_this->m, 0, sizeof(Fixed)*20); + _this->m[0] = _this->m[6] = _this->m[12] = _this->m[18] = FIX_ONE; + _this->identity = 1; +} + + +static void gf_cmx_identity(GF_ColorMatrix *_this) +{ + GF_ColorMatrix mat; + gf_cmx_init(&mat); + _this->identity = memcmp(_this->m, mat.m, sizeof(Fixed)*20) ? 0 : 1; +} + +GF_EXPORT +void gf_cmx_set_all(GF_ColorMatrix *_this, Fixed *coefs) +{ + if (!_this || !coefs) return; +} + +GF_EXPORT +void gf_cmx_set(GF_ColorMatrix *_this, + Fixed c1, Fixed c2, Fixed c3, Fixed c4, Fixed c5, + Fixed c6, Fixed c7, Fixed c8, Fixed c9, Fixed c10, + Fixed c11, Fixed c12, Fixed c13, Fixed c14, Fixed c15, + Fixed c16, Fixed c17, Fixed c18, Fixed c19, Fixed c20) +{ + if (!_this) return; + _this->m[0] = c1; _this->m[1] = c2; _this->m[2] = c3; _this->m[3] = c4; _this->m[4] = c5; + _this->m[5] = c6; _this->m[6] = c7; _this->m[7] = c8; _this->m[8] = c9; _this->m[9] = c10; + _this->m[10] = c11; _this->m[11] = c12; _this->m[12] = c13; _this->m[13] = c14; _this->m[14] = c15; + _this->m[15] = c16; _this->m[16] = c17; _this->m[17] = c18; _this->m[18] = c19; _this->m[19] = c20; + gf_cmx_identity(_this); +} + +GF_EXPORT +void gf_cmx_copy(GF_ColorMatrix *_this, GF_ColorMatrix *from) +{ + if (!_this || !from) return; + memcpy(_this->m, from->m, sizeof(Fixed)*20); + gf_cmx_identity(_this); +} + + +GF_EXPORT +void gf_cmx_multiply(GF_ColorMatrix *_this, GF_ColorMatrix *w) +{ + Fixed res[20]; + if (!_this || !w || w->identity) return; + if (_this->identity) { + gf_cmx_copy(_this, w); + return; + } + + res[0] = gf_mulfix(_this->m[0], w->m[0]) + gf_mulfix(_this->m[1], w->m[5]) + gf_mulfix(_this->m[2], w->m[10]) + gf_mulfix(_this->m[3], w->m[15]); + res[1] = gf_mulfix(_this->m[0], w->m[1]) + gf_mulfix(_this->m[1], w->m[6]) + gf_mulfix(_this->m[2], w->m[11]) + gf_mulfix(_this->m[3], w->m[16]); + res[2] = gf_mulfix(_this->m[0], w->m[2]) + gf_mulfix(_this->m[1], w->m[7]) + gf_mulfix(_this->m[2], w->m[12]) + gf_mulfix(_this->m[3], w->m[17]); + res[3] = gf_mulfix(_this->m[0], w->m[3]) + gf_mulfix(_this->m[1], w->m[8]) + gf_mulfix(_this->m[2], w->m[13]) + gf_mulfix(_this->m[3], w->m[18]); + res[4] = gf_mulfix(_this->m[0], w->m[4]) + gf_mulfix(_this->m[1], w->m[9]) + gf_mulfix(_this->m[2], w->m[14]) + gf_mulfix(_this->m[3], w->m[19]) + _this->m[4]; + + res[5] = gf_mulfix(_this->m[5], w->m[0]) + gf_mulfix(_this->m[6], w->m[5]) + gf_mulfix(_this->m[7], w->m[10]) + gf_mulfix(_this->m[8], w->m[15]); + res[6] = gf_mulfix(_this->m[5], w->m[1]) + gf_mulfix(_this->m[6], w->m[6]) + gf_mulfix(_this->m[7], w->m[11]) + gf_mulfix(_this->m[8], w->m[16]); + res[7] = gf_mulfix(_this->m[5], w->m[2]) + gf_mulfix(_this->m[6], w->m[7]) + gf_mulfix(_this->m[7], w->m[12]) + gf_mulfix(_this->m[8], w->m[17]); + res[8] = gf_mulfix(_this->m[5], w->m[3]) + gf_mulfix(_this->m[6], w->m[8]) + gf_mulfix(_this->m[7], w->m[13]) + gf_mulfix(_this->m[8], w->m[18]); + res[9] = gf_mulfix(_this->m[5], w->m[4]) + gf_mulfix(_this->m[6], w->m[9]) + gf_mulfix(_this->m[7], w->m[14]) + gf_mulfix(_this->m[8], w->m[19]) + _this->m[9]; + + res[10] = gf_mulfix(_this->m[10], w->m[0]) + gf_mulfix(_this->m[11], w->m[5]) + gf_mulfix(_this->m[12], w->m[10]) + gf_mulfix(_this->m[13], w->m[15]); + res[11] = gf_mulfix(_this->m[10], w->m[1]) + gf_mulfix(_this->m[11], w->m[6]) + gf_mulfix(_this->m[12], w->m[11]) + gf_mulfix(_this->m[13], w->m[16]); + res[12] = gf_mulfix(_this->m[10], w->m[2]) + gf_mulfix(_this->m[11], w->m[7]) + gf_mulfix(_this->m[12], w->m[12]) + gf_mulfix(_this->m[13], w->m[17]); + res[13] = gf_mulfix(_this->m[10], w->m[3]) + gf_mulfix(_this->m[11], w->m[8]) + gf_mulfix(_this->m[12], w->m[13]) + gf_mulfix(_this->m[13], w->m[18]); + res[14] = gf_mulfix(_this->m[10], w->m[4]) + gf_mulfix(_this->m[11], w->m[9]) + gf_mulfix(_this->m[12], w->m[14]) + gf_mulfix(_this->m[13], w->m[19]) + _this->m[14]; + + res[15] = gf_mulfix(_this->m[15], w->m[0]) + gf_mulfix(_this->m[16], w->m[5]) + gf_mulfix(_this->m[17], w->m[10]) + gf_mulfix(_this->m[18], w->m[15]); + res[16] = gf_mulfix(_this->m[15], w->m[1]) + gf_mulfix(_this->m[16], w->m[6]) + gf_mulfix(_this->m[17], w->m[11]) + gf_mulfix(_this->m[18], w->m[16]); + res[17] = gf_mulfix(_this->m[15], w->m[2]) + gf_mulfix(_this->m[16], w->m[7]) + gf_mulfix(_this->m[17], w->m[12]) + gf_mulfix(_this->m[18], w->m[17]); + res[18] = gf_mulfix(_this->m[15], w->m[3]) + gf_mulfix(_this->m[16], w->m[8]) + gf_mulfix(_this->m[17], w->m[13]) + gf_mulfix(_this->m[18], w->m[18]); + res[19] = gf_mulfix(_this->m[15], w->m[4]) + gf_mulfix(_this->m[16], w->m[9]) + gf_mulfix(_this->m[17], w->m[14]) + gf_mulfix(_this->m[18], w->m[19]) + _this->m[19]; + memcpy(_this->m, res, sizeof(Fixed)*20); + gf_cmx_identity(_this); +} + +#define CLIP_COMP(val) { if (val<0) { val=0; } else if (val>FIX_ONE) { val=FIX_ONE;} } + +static void gf_cmx_apply_argb(GF_ColorMatrix *_this, u8 *a_, u8 *r_, u8 *g_, u8 *b_) +{ + Fixed _a, _r, _g, _b, a, r, g, b; + if (!_this || _this->identity) return; + + a = INT2FIX(*a_)/255; + r = INT2FIX(*r_)/255; + g = INT2FIX(*g_)/255; + b = INT2FIX(*b_)/255; + _r = gf_mulfix(r, _this->m[0]) + gf_mulfix(g, _this->m[1]) + gf_mulfix(b, _this->m[2]) + gf_mulfix(a, _this->m[3]) + _this->m[4]; + _g = gf_mulfix(r, _this->m[5]) + gf_mulfix(g, _this->m[6]) + gf_mulfix(b, _this->m[7]) + gf_mulfix(a, _this->m[8]) + _this->m[9]; + _b = gf_mulfix(r, _this->m[10]) + gf_mulfix(g, _this->m[11]) + gf_mulfix(b, _this->m[12]) + gf_mulfix(a, _this->m[13]) + _this->m[14]; + _a = gf_mulfix(r, _this->m[15]) + gf_mulfix(g, _this->m[16]) + gf_mulfix(b, _this->m[17]) + gf_mulfix(a, _this->m[18]) + _this->m[19]; + CLIP_COMP(_a); + CLIP_COMP(_r); + CLIP_COMP(_g); + CLIP_COMP(_b); + + *a_ = FIX2INT(_a*255); + *r_ = FIX2INT(_r*255); + *g_ = FIX2INT(_g*255); + *b_ = FIX2INT(_b*255); +} + + +GF_EXPORT +GF_Color gf_cmx_apply(GF_ColorMatrix *_this, GF_Color col) +{ + Fixed _a, _r, _g, _b, a, r, g, b; + if (!_this || _this->identity) return col; + + a = INT2FIX(col>>24); a /= 255; + r = INT2FIX((col>>16)&0xFF); r /= 255; + g = INT2FIX((col>>8)&0xFF); g /= 255; + b = INT2FIX((col)&0xFF); b /= 255; + _r = gf_mulfix(r, _this->m[0]) + gf_mulfix(g, _this->m[1]) + gf_mulfix(b, _this->m[2]) + gf_mulfix(a, _this->m[3]) + _this->m[4]; + _g = gf_mulfix(r, _this->m[5]) + gf_mulfix(g, _this->m[6]) + gf_mulfix(b, _this->m[7]) + gf_mulfix(a, _this->m[8]) + _this->m[9]; + _b = gf_mulfix(r, _this->m[10]) + gf_mulfix(g, _this->m[11]) + gf_mulfix(b, _this->m[12]) + gf_mulfix(a, _this->m[13]) + _this->m[14]; + _a = gf_mulfix(r, _this->m[15]) + gf_mulfix(g, _this->m[16]) + gf_mulfix(b, _this->m[17]) + gf_mulfix(a, _this->m[18]) + _this->m[19]; + CLIP_COMP(_a); + CLIP_COMP(_r); + CLIP_COMP(_g); + CLIP_COMP(_b); + return GF_COL_ARGB(FIX2INT(_a*255),FIX2INT(_r*255),FIX2INT(_g*255),FIX2INT(_b*255)); +} + +GF_EXPORT +void gf_cmx_apply_fixed(GF_ColorMatrix *_this, Fixed *a, Fixed *r, Fixed *g, Fixed *b) +{ + u32 col = GF_COL_ARGB_FIXED(*a, *r, *g, *b); + col = gf_cmx_apply(_this, col); + *a = INT2FIX(GF_COL_A(col)) / 255; + *r = INT2FIX(GF_COL_R(col)) / 255; + *g = INT2FIX(GF_COL_G(col)) / 255; + *b = INT2FIX(GF_COL_B(col)) / 255; +} diff --git a/src/utils/configfile.c b/src/utils/configfile.c new file mode 100644 index 0000000..dff49d6 --- /dev/null +++ b/src/utils/configfile.c @@ -0,0 +1,361 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#define MAX_SECTION_NAME 500 +#define MAX_INI_LINE 2046 + +typedef struct +{ + char *name; + char *value; +} IniKey; + +typedef struct +{ + char section_name[MAX_SECTION_NAME]; + GF_List *keys; +} IniSection; + +struct __tag_config +{ + char *fileName; + char *filePath; + GF_List *sections; + Bool hasChanged; +}; + +GF_EXPORT +GF_Config *gf_cfg_new(const char *filePath, const char* file_name) +{ + IniSection *p; + IniKey *k; + FILE *file; + char *ret; + char fileName[GF_MAX_PATH]; + char line[MAX_INI_LINE]; + GF_Config *tmp; + + if (filePath) { + if (filePath[strlen(filePath)-1] == GF_PATH_SEPARATOR) { + strcpy(fileName,filePath); + strcat(fileName, file_name); + } else { + sprintf(fileName, "%s%c%s", filePath, GF_PATH_SEPARATOR, file_name); + } + } else { + strcpy(fileName,file_name); + } + file = fopen(fileName, "rt"); + if (!file) return NULL; + + tmp = (GF_Config *)malloc(sizeof(GF_Config)); + memset((void *)tmp, 0, sizeof(GF_Config)); + + tmp->filePath = strdup(filePath); + tmp->fileName = strdup(fileName); + tmp->sections = gf_list_new(); + + //load the file + p = NULL; + + while (!feof(file)) { + ret = fgets(line, MAX_INI_LINE, file); + + if (!ret) continue; + + //get rid of the end of line stuff + while (1) { + u32 len = strlen(line); + if (!len) break; + if ((line[len-1] != '\n') && (line[len-1] != '\r')) break; + line[len-1] = 0; + } + if (!strlen(line)) continue; + if (line[0] == '#') continue; + + + //new section + if (line[0] == '[') { + p = (IniSection *) malloc(sizeof(IniSection)); + p->keys = gf_list_new(); + strcpy(p->section_name, line + 1); + p->section_name[strlen(line) - 2] = 0; + while (p->section_name[strlen(p->section_name) - 1] == ']' || p->section_name[strlen(p->section_name) - 1] == ' ') p->section_name[strlen(p->section_name) - 1] = 0; + gf_list_add(tmp->sections, p); + } + else if (strlen(line) && (strchr(line, '=') != NULL) ) { + if (!p) { + gf_list_del(tmp->sections); + free(tmp->fileName); + free(tmp->filePath); + free(tmp); + fclose(file); + return NULL; + } +// GF_SAFEALLOC(k, IniKey) + k = (IniKey *) malloc(sizeof(IniKey)); + memset((void *)k, 0, sizeof(IniKey)); + ret = strchr(line, '='); + if (ret) { + ret[0] = 0; + k->name = strdup(line); + ret[0] = '='; + ret += 1; + while (ret[0] == ' ') ret++; + k->value = strdup(ret); + while (k->name[strlen(k->name) - 1] == ' ') k->name[strlen(k->name) - 1] = 0; + while (k->value[strlen(k->value) - 1] == ' ') k->value[strlen(k->value) - 1] = 0; + } + gf_list_add(p->keys, k); + } + } + fclose(file); + return tmp; +} + +static void DelSection(IniSection *ptr) +{ + IniKey *k; + if (!ptr) return; + if (ptr->keys) { + while (gf_list_count(ptr->keys)) { + k = (IniKey *) gf_list_get(ptr->keys, 0); + if (k->value) free(k->value); + if (k->name) free(k->name); + free(k); + gf_list_rem(ptr->keys, 0); + } + gf_list_del(ptr->keys); + } + free(ptr); +} + + +GF_EXPORT +GF_Err gf_cfg_save(GF_Config *iniFile) +{ + u32 i, j; + IniSection *sec; + IniKey *key; + FILE *file; + + if (!iniFile->hasChanged) return GF_OK; + + file = fopen(iniFile->fileName, "wt"); + if (!file) return GF_IO_ERR; + + i=0; + while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i)) ) { + fprintf(file, "[%s]\n", sec->section_name); + j=0; + while ( (key = (IniKey *) gf_list_enum(sec->keys, &j)) ) { + fprintf(file, "%s=%s\n", key->name, key->value); + } + //end of section + fprintf(file, "\n"); + } + fclose(file); + return GF_OK; +} + +GF_EXPORT +void gf_cfg_del(GF_Config *iniFile) +{ + IniSection *p; + if (!iniFile) return; + + gf_cfg_save(iniFile); + while (gf_list_count(iniFile->sections)) { + p = (IniSection *) gf_list_get(iniFile->sections, 0); + DelSection(p); + gf_list_rem(iniFile->sections, 0); + } + gf_list_del(iniFile->sections); + free(iniFile->fileName); + free(iniFile->filePath); + free(iniFile); +} + + +GF_EXPORT +const char *gf_cfg_get_key(GF_Config *iniFile, const char *secName, const char *keyName) +{ + u32 i; + IniSection *sec; + IniKey *key; + + i=0; + while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i)) ) { + if (!strcmp(secName, sec->section_name)) goto get_key; + } + return NULL; + +get_key: + i=0; + while ( (key = (IniKey *) gf_list_enum(sec->keys, &i)) ) { + if (!strcmp(key->name, keyName)) return key->value; + } + return NULL; +} + + + +GF_EXPORT +GF_Err gf_cfg_set_key(GF_Config *iniFile, const char *secName, const char *keyName, const char *keyValue) +{ + u32 i; + IniSection *sec; + IniKey *key; + + if (!iniFile || !secName || !keyName) return GF_BAD_PARAM; + + i=0; + while ((sec = (IniSection *) gf_list_enum(iniFile->sections, &i)) ) { + if (!strcmp(secName, sec->section_name)) goto get_key; + } + //need a new key + sec = (IniSection *) malloc(sizeof(IniSection)); + strcpy(sec->section_name, secName); + sec->keys = gf_list_new(); + iniFile->hasChanged = 1; + gf_list_add(iniFile->sections, sec); + +get_key: + i=0; + while ( (key = (IniKey *) gf_list_enum(sec->keys, &i) ) ) { + if (!strcmp(key->name, keyName)) goto set_value; + } + if (!keyValue) return GF_OK; + //need a new key + key = (IniKey *) malloc(sizeof(IniKey)); + key->name = strdup(keyName); + key->value = strdup(""); + iniFile->hasChanged = 1; + gf_list_add(sec->keys, key); + +set_value: + if (!keyValue) { + gf_list_del_item(sec->keys, key); + if (key->name) free(key->name); + if (key->value) free(key->value); + free(key); + iniFile->hasChanged = 1; + return GF_OK; + } + //same value, don't update + if (!strcmp(key->value, keyValue)) return GF_OK; + + if (key->value) free(key->value); + key->value = strdup(keyValue); + iniFile->hasChanged = 1; + return GF_OK; +} + +GF_EXPORT +u32 gf_cfg_get_section_count(GF_Config *iniFile) +{ + return gf_list_count(iniFile->sections); +} + +GF_EXPORT +const char *gf_cfg_get_section_name(GF_Config *iniFile, u32 secIndex) +{ + IniSection *is = (IniSection *) gf_list_get(iniFile->sections, secIndex); + if (!is) return NULL; + return is->section_name; +} + +GF_EXPORT +u32 gf_cfg_get_key_count(GF_Config *iniFile, const char *secName) +{ + u32 i = 0; + IniSection *sec; + while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i)) ) { + if (!strcmp(secName, sec->section_name)) return gf_list_count(sec->keys); + } + return 0; +} + +GF_EXPORT +const char *gf_cfg_get_key_name(GF_Config *iniFile, const char *secName, u32 keyIndex) +{ + u32 i = 0; + IniSection *sec; + while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i) ) ) { + if (!strcmp(secName, sec->section_name)) { + IniKey *key = (IniKey *) gf_list_get(sec->keys, keyIndex); + return key ? key->name : NULL; + } + } + return NULL; +} + +GF_EXPORT +void gf_cfg_del_section(GF_Config *iniFile, const char *secName) +{ + u32 i; + IniSection *p; + if (!iniFile) return; + + i = 0; + while ((p = gf_list_enum(iniFile->sections, &i))) { + if (!strcmp(secName, p->section_name)) { + DelSection(p); + gf_list_rem(iniFile->sections, i-1); + return; + } + } +} + +GF_EXPORT +GF_Err gf_cfg_insert_key(GF_Config *iniFile, const char *secName, const char *keyName, const char *keyValue, u32 index) +{ + u32 i; + IniSection *sec; + IniKey *key; + + if (!iniFile || !secName || !keyName|| !keyValue) return GF_BAD_PARAM; + + i=0; + while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i) ) ) { + if (!strcmp(secName, sec->section_name)) break; + } + if (!sec) return GF_BAD_PARAM; + + i=0; + while ( (key = (IniKey *) gf_list_enum(sec->keys, &i) ) ) { + if (!strcmp(key->name, keyName)) return GF_BAD_PARAM; + } + + key = (IniKey *) malloc(sizeof(IniKey)); + key->name = strdup(keyName); + key->value = strdup(keyValue); + gf_list_insert(sec->keys, key, index); + iniFile->hasChanged = 1; + return GF_OK; +} + diff --git a/src/utils/downloader.c b/src/utils/downloader.c new file mode 100644 index 0000000..dcb8b13 --- /dev/null +++ b/src/utils/downloader.c @@ -0,0 +1,1546 @@ +/* + * GPAC Multimedia Framework + * + * Authors: Jean Le Feuvre + * Copyright (c) 2005-2005 ENST + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include + + +#ifdef GPAC_HAS_SSL +#include +#include +#include +#include +#endif + + +static void gf_dm_connect(GF_DownloadSession *sess); + +#define GF_DOWNLOAD_AGENT_NAME "GPAC/" GPAC_FULL_VERSION +#define GF_DOWNLOAD_BUFFER_SIZE 8192 + +/*internal flags*/ +enum +{ + GF_DOWNLOAD_SESSION_USE_SSL = 1<<10, + GF_DOWNLOAD_SESSION_THREAD_DEAD = 1<<11, + GF_DOWNLOAD_IS_ICY = 1<<12, +}; + + +struct __gf_download_session +{ + /*this is always 0 and helps differenciating downloads from other interfaces (interfaceType != 0)*/ + u32 reserved; + + struct __gf_download_manager *dm; + GF_Thread *th; + GF_Mutex *mx; + + Bool in_callback, destroy; + + char *server_name; + u16 port; + + char *remote_path; + char *user; + char *passwd; + char cookie[GF_MAX_PATH]; + + FILE *cache; + char *cache_name; + /*cache size if existing*/ + u32 cache_start_size; + + GF_Socket *sock; + u32 num_retry, status; + + char *mime_type; + u32 flags; + + u32 total_size, bytes_done, start_time, icy_metaint, icy_count, icy_bytes; + u32 bytes_per_sec, window_start, bytes_in_wnd; + u32 limit_data_rate; + + /*0: GET + 1: HEAD + 2: all the rest + */ + u32 http_read_type; + + GF_Err last_error; + char *init_data; + u32 init_data_size; + +#ifdef GPAC_HAS_SSL + SSL *ssl; +#endif + + void (*do_requests)(struct __gf_download_session *); + + /*callback for data reception - may not be NULL*/ + gf_dm_user_io user_proc; + void *usr_cbk; + + /*private extension*/ + void *ext; +}; + +struct __gf_download_manager +{ + char *cache_directory; + char szCookieDir[GF_MAX_PATH]; + + Bool (*GetUserPassword)(void *usr_cbk, const char *site_url, char *usr_name, char *password); + void *usr_cbk; + + GF_Config *cfg; + GF_List *sessions; + +#ifdef GPAC_HAS_SSL + SSL_CTX *ssl_ctx; +#endif + +}; + +#ifdef GPAC_HAS_SSL + +static void init_prng (void) +{ + char namebuf[256]; + const char *random_file; + + if (RAND_status ()) return; + + namebuf[0] = '\0'; + random_file = RAND_file_name (namebuf, sizeof (namebuf)); + + if (random_file && *random_file) + RAND_load_file(random_file, 16384); + + if (RAND_status ()) return; + +#ifdef WIN32 + RAND_screen (); + if (RAND_status ()) + return; +#endif +} + +static int ssl_init(GF_DownloadManager *dm, u32 mode) +{ + SSL_METHOD *meth; + + if (!dm) return 0; + /* The SSL has already been initialized. */ + if (dm->ssl_ctx) return 1; + /* Init the PRNG. If that fails, bail out. */ + init_prng(); + if (RAND_status() != 1) goto error; + SSL_library_init(); + SSL_load_error_strings(); + SSLeay_add_all_algorithms(); + SSLeay_add_ssl_algorithms(); + + switch (mode) { + case 0: + meth = SSLv23_client_method(); + break; + case 1: + meth = SSLv2_client_method(); + break; + case 2: + meth = SSLv3_client_method(); + break; + case 3: + meth = TLSv1_client_method(); + break; + default: + goto error; + } + + dm->ssl_ctx = SSL_CTX_new(meth); + if (!dm->ssl_ctx) goto error; + SSL_CTX_set_default_verify_paths(dm->ssl_ctx); + SSL_CTX_load_verify_locations (dm->ssl_ctx, NULL, NULL); + /* SSL_VERIFY_NONE instructs OpenSSL not to abort SSL_connect if the + certificate is invalid. We verify the certificate separately in + ssl_check_certificate, which provides much better diagnostics + than examining the error stack after a failed SSL_connect. */ + SSL_CTX_set_verify(dm->ssl_ctx, SSL_VERIFY_NONE, NULL); + + /* Since fd_write unconditionally assumes partial writes (and handles them correctly), + allow them in OpenSSL. */ + SSL_CTX_set_mode(dm->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); + return 1; +error: + if (dm->ssl_ctx) SSL_CTX_free(dm->ssl_ctx); + dm->ssl_ctx = NULL; + return 0; +} + +#endif + + +static Bool gf_dm_is_local(GF_DownloadManager *dm, const char *url) +{ + if (!strnicmp(url, "file://", 7)) return 1; + if (!strnicmp(url, "file:///", 8)) return 1; + if (!strstr(url, "://")) return 1; + return 0; +} + +static Bool gf_dm_can_handle_url(GF_DownloadManager *dm, const char *url) +{ + if (!strnicmp(url, "http://", 7)) return 1; +#ifdef GPAC_HAS_SSL + if (!strnicmp(url, "https://", 8)) return 1; +#endif + return 0; +} + + +void gf_dm_configure_cache(GF_DownloadSession *sess) +{ + u32 i, len; + char *tmp, *ext; + u8 hash[20]; + const char *opt; + + if (!sess->dm->cache_directory) return; + if (sess->flags & GF_NETIO_SESSION_NOT_CACHED) return; + + len = strlen(sess->server_name) + strlen(sess->remote_path) + 1; + if (len<50) len = 50; + tmp = malloc(sizeof(char) * len); + tmp[0] = 0; + + /*generate hash of the full url*/ + strcpy(tmp, sess->server_name); + strcat(tmp, sess->remote_path); + gf_sha1_csum(tmp, strlen(tmp), hash); + tmp[0] = 0; + for (i=0; i<20; i++) { + char t[3]; + t[2] = 0; + sprintf(t, "%02X", hash[i]); + strcat(tmp, t); + } + + len += strlen(sess->dm->cache_directory) + 6; + sess->cache_name = malloc(sizeof(char)*len); + sess->cache_name[0] = 0; + + strcpy(sess->cache_name, sess->dm->cache_directory); + strcat(sess->cache_name, tmp); + + /*try to locate an extension*/ + strcpy(tmp, sess->remote_path); + ext = strchr(tmp, '?'); + if (ext) ext[0] = 0; + ext = strchr(tmp, '.'); + if (ext && (strlen(ext)<6) ) strcat(sess->cache_name, ext); + free(tmp); + + /*first try, check cached file*/ + if (!sess->cache_start_size) { + /*if file present figure out how much of the file is downloaded - we assume 2^31 byte file max*/ + FILE *the_cache = fopen(sess->cache_name, "rb"); + if (the_cache) { + fseek(the_cache, 0, SEEK_END); + sess->cache_start_size = ftell(the_cache); + fclose(the_cache); + } + } + /*second try, disable cached file*/ + else { + sess->cache_start_size = 0; + } + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[HTTP] Cache setup to %s\n", sess->cache_name)); + + /*are we using existing cached files ?*/ + opt = gf_cfg_get_key(sess->dm->cfg, "Downloader", "RestartFiles"); + if (opt && !stricmp(opt, "yes")) sess->cache_start_size = 0; +} + +static void gf_dm_disconnect(GF_DownloadSession *sess) +{ +#ifdef GPAC_HAS_SSL + if (sess->ssl) { + SSL_shutdown(sess->ssl); + SSL_free(sess->ssl); + sess->ssl = NULL; + } +#endif + if (sess->sock) { + gf_sk_del(sess->sock); + sess->sock = NULL; + } + if (sess->cache) fclose(sess->cache); + sess->cache = NULL; + sess->status = GF_NETIO_DISCONNECTED; + if (sess->num_retry) sess->num_retry--; +} + +void gf_dm_sess_del(GF_DownloadSession *sess) +{ + const char *opt; + + /*self-destruction, let the download manager destroy us*/ + if (sess->th && sess->in_callback) { + sess->destroy = 1; + return; + } + gf_dm_disconnect(sess); + + /*if threaded wait for thread exit*/ + if (sess->th) { + while (!(sess->flags & GF_DOWNLOAD_SESSION_THREAD_DEAD)) + gf_sleep(1); + gf_th_del(sess->th); + gf_mx_del(sess->mx); + } + + gf_list_del_item(sess->dm->sessions, sess); + + if (sess->cache_name) { + opt = gf_cfg_get_key(sess->dm->cfg, "Downloader", "CleanCache"); + if (opt && !stricmp(opt, "yes")) gf_delete_file(sess->cache_name); + free(sess->cache_name); + } + + if (sess->server_name) free(sess->server_name); + if (sess->remote_path) free(sess->remote_path); + if (sess->user) free(sess->user); + if (sess->passwd) free(sess->passwd); + if (sess->mime_type) free(sess->mime_type); + if (sess->cache) fclose(sess->cache); + if (sess->init_data) free(sess->init_data); + free(sess); +} + +void http_do_requests(GF_DownloadSession *sess); + +static void gf_dm_sess_notify_state(GF_DownloadSession *sess, u32 dnload_status, GF_Err error) +{ + GF_NETIO_Parameter par; + sess->in_callback = 1; + memset(&par, 0, sizeof(GF_NETIO_Parameter)); + par.msg_type = dnload_status; + par.error = error; + sess->user_proc(sess->usr_cbk, &par); + sess->in_callback = 0; +} + +static void gf_dm_sess_user_io(GF_DownloadSession *sess, GF_NETIO_Parameter *par) +{ + sess->in_callback = 1; + sess->user_proc(sess->usr_cbk, par); + sess->in_callback = 0; +} + +GF_Err gf_dm_sess_last_error(GF_DownloadSession *sess) +{ + if (!sess) return GF_BAD_PARAM; + return sess->last_error; +} + + +static GF_Err gf_dm_setup_from_url(GF_DownloadSession *sess, char *url) +{ + char *tmp, *tmp_url; + const char *opt; + if (!strnicmp(url, "http://", 7)) { + url += 7; + sess->port = 80; + sess->do_requests = http_do_requests; + } + else if (!strnicmp(url, "https://", 8)) { + url += 8; + sess->port = 443; +#ifndef GPAC_HAS_SSL + return GF_NOT_SUPPORTED; +#endif + sess->flags |= GF_DOWNLOAD_SESSION_USE_SSL; + sess->do_requests = http_do_requests; + } + else if (!strnicmp(url, "ftp://", 6)) { + url += 6; + sess->port = 21; + sess->do_requests = NULL; + return GF_NOT_SUPPORTED; + } + /*relative URL*/ + else if (!strstr(url, "://")) { + u32 i; + if (!sess->remote_path) return GF_BAD_PARAM; + tmp = gf_url_concatenate(sess->remote_path, url); + free(sess->remote_path); + sess->remote_path = tmp; + for (i=0; iremote_path); i++) + if (sess->remote_path[i]=='\\') sess->remote_path[i]='/'; + + } else { + return GF_BAD_PARAM; + } + + + tmp = strchr(url, '/'); + sess->remote_path = strdup(tmp ? tmp : "/"); + if (tmp) { + tmp[0] = 0; + tmp_url = strdup(url); + tmp[0] = '/'; + } else { + tmp_url = strdup(url); + } + + tmp = strrchr(tmp_url, ':'); + if (tmp) { + sess->port = atoi(tmp+1); + tmp[0] = 0; + } + tmp = strrchr(tmp_url, '@'); + if (tmp) { + sess->server_name = strdup(tmp+1); + tmp[0] = 0; + tmp = strchr(tmp_url, ':'); + if (sess->user) free(sess->user); + sess->user = NULL; + if (sess->passwd) free(sess->passwd); + sess->passwd = NULL; + + if (tmp) { + sess->passwd = strdup(tmp+1); + tmp[0] = 0; + } + sess->user = strdup(tmp_url); + } else { + sess->server_name = strdup(tmp_url); + } + free(tmp_url); + + /*setup BW limiter*/ + sess->limit_data_rate = 0; + opt = gf_cfg_get_key(sess->dm->cfg, "Downloader", "MaxRate"); + if (opt) { + /*use it in in BYTES per second*/ + sess->limit_data_rate = 1024 * atoi(opt) / 8; + } + + return GF_OK; +} + + +#define GF_WAIT_REPLY_SLEEP 20 +static u32 gf_dm_session_thread(void *par) +{ + GF_DownloadSession *sess = (GF_DownloadSession *)par; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] Entering thread ID %d\n", gf_th_id() )); + + sess->flags &= ~GF_DOWNLOAD_SESSION_THREAD_DEAD; + while (!sess->destroy) { + gf_mx_p(sess->mx); + if (sess->status >= GF_NETIO_DISCONNECTED) { + gf_mx_v(sess->mx); + break; + } + + if (sess->status < GF_NETIO_CONNECTED) { + gf_dm_connect(sess); + } else { + if (sess->status == GF_NETIO_WAIT_FOR_REPLY) gf_sleep(GF_WAIT_REPLY_SLEEP); + sess->do_requests(sess); + } + gf_mx_v(sess->mx); + gf_sleep(2); + } + /*destroy all sessions*/ + gf_dm_disconnect(sess); + sess->status = GF_NETIO_STATE_ERROR; + sess->last_error = 0; + sess->flags |= GF_DOWNLOAD_SESSION_THREAD_DEAD; + return 1; +} + +#define SESSION_RETRY_COUNT 20 + +GF_DownloadSession *gf_dm_sess_new(GF_DownloadManager *dm, char *url, u32 dl_flags, + gf_dm_user_io user_io, + void *usr_cbk, + GF_Err *e) +{ + GF_DownloadSession *sess; + + *e = GF_OK; + if (gf_dm_is_local(dm, url)) return NULL; + + if (!gf_dm_can_handle_url(dm, url)) { + *e = GF_NOT_SUPPORTED; + return NULL; + } + if (!user_io) { + *e = GF_BAD_PARAM; + return NULL; + } + + + sess = (GF_DownloadSession *)malloc(sizeof(GF_DownloadSession)); + memset((void *)sess, 0, sizeof(GF_DownloadSession)); + sess->flags = dl_flags; + sess->user_proc = user_io; + sess->usr_cbk = usr_cbk; + sess->dm = dm; + gf_list_add(dm->sessions, sess); + + *e = gf_dm_setup_from_url(sess, url); + if (*e) { + gf_dm_sess_del(sess); + return NULL; + } + if (!(sess->flags & GF_NETIO_SESSION_NOT_THREADED) ) { + sess->th = gf_th_new(url); + sess->mx = gf_mx_new(url); + gf_th_run(sess->th, gf_dm_session_thread, sess); + } + sess->num_retry = SESSION_RETRY_COUNT; + return sess; +} + +static GF_Err gf_dm_read_data(GF_DownloadSession *sess, char *data, u32 data_size, u32 *out_read) +{ + GF_Err e; + +#ifdef GPAC_HAS_SSL + if (sess->ssl) { + u32 size = SSL_read(sess->ssl, data, data_size); + e = GF_OK; + data[size] = 0; + if (!size) e = GF_IP_NETWORK_EMPTY; + *out_read = size; + } else +#endif + e = gf_sk_receive(sess->sock, data, data_size, 0, out_read); + + return e; +} + + +#ifdef GPAC_HAS_SSL +/*pattern comp taken from wget*/ +#define ASTERISK_EXCLUDES_DOT /* mandated by rfc2818 */ + +#define TOLOWER(x) ('A' <= (x) && (x) <= 'Z' ? (x) - 32 : (x)) + +static Bool pattern_match(const char *pattern, const char *string) +{ + const char *p = pattern, *n = string; + char c; + for (; (c = TOLOWER (*p++)) != '\0'; n++) { + if (c == '*') { + for (c = TOLOWER (*p); c == '*'; c = TOLOWER (*++p)) + ; + for (; *n != '\0'; n++) + if (TOLOWER (*n) == c && pattern_match (p, n)) + return 1; +#ifdef ASTERISK_EXCLUDES_DOT + else if (*n == '.') + return 0; +#endif + return c == '\0'; + } else { + if (c != TOLOWER (*n)) + return 0; + } + } + return *n == '\0'; +} +#undef TOLOWER + +#endif + +static void gf_dm_connect(GF_DownloadSession *sess) +{ + GF_Err e; + u16 proxy_port = 0; + const char *proxy, *ip; + if (!sess->sock) { + sess->num_retry = 40; + sess->sock = gf_sk_new(GF_SOCK_TYPE_TCP); + //gf_sk_set_block_mode(sess->sock, 1); + } + + /*connect*/ + sess->status = GF_NETIO_SETUP; + gf_dm_sess_notify_state(sess, sess->status, GF_OK); + + /*PROXY setup*/ + proxy = gf_cfg_get_key(sess->dm->cfg, "HTTPProxy", "Enabled"); + if (proxy && !strcmp(proxy, "yes")) { + proxy = gf_cfg_get_key(sess->dm->cfg, "HTTPProxy", "Port"); + proxy_port = proxy ? atoi(proxy) : 80; + + proxy = gf_cfg_get_key(sess->dm->cfg, "HTTPProxy", "Name"); + } else { + proxy = NULL; + } + + + ip = gf_cfg_get_key(sess->dm->cfg, "Network", "MobileIPEnabled"); + if (ip && !strcmp(ip, "yes")) { + ip = gf_cfg_get_key(sess->dm->cfg, "Network", "MobileIP"); + } else { + ip = NULL; + } + + if (!proxy) { + proxy = sess->server_name; + proxy_port = sess->port; + } + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[HTTP] Connecting to %s:%d\n", proxy, proxy_port)); + + e = gf_sk_connect(sess->sock, (char *) proxy, proxy_port, (char *)ip); + + /*retry*/ + if ((e == GF_IP_SOCK_WOULD_BLOCK) && sess->num_retry) { + sess->status = GF_NETIO_SETUP; + sess->num_retry--; + return; + } + + /*failed*/ + if (e) { + sess->status = GF_NETIO_STATE_ERROR; + sess->last_error = e; + gf_dm_sess_notify_state(sess, sess->status, e); + return; + } + + sess->status = GF_NETIO_CONNECTED; + gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK); + //gf_sk_set_block_mode(sess->sock, 1); + gf_sk_set_buffer_size(sess->sock, 0, GF_DOWNLOAD_BUFFER_SIZE); + gf_dm_configure_cache(sess); + +#ifdef GPAC_HAS_SSL + /*socket is connected, configure SSL layer*/ + if (!sess->ssl && sess->dm->ssl_ctx && (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL)) { + int ret; + long vresult; + char common_name[256]; + X509 *cert; + Bool success = 1; + + sess->ssl = SSL_new(sess->dm->ssl_ctx); + SSL_set_fd(sess->ssl, gf_sk_get_handle(sess->sock)); + SSL_set_connect_state(sess->ssl); + ret = SSL_connect(sess->ssl); + assert(ret>0); + + cert = SSL_get_peer_certificate(sess->ssl); + /*if we have a cert, check it*/ + if (cert) { + vresult = SSL_get_verify_result(sess->ssl); + if (vresult != X509_V_OK) success = 0; + else { + common_name[0] = 0; + X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, common_name, sizeof (common_name)); + if (!pattern_match(common_name, sess->server_name)) success = 0; + } + X509_free(cert); + + if (!success) { + gf_dm_disconnect(sess); + sess->status = GF_NETIO_STATE_ERROR; + sess->last_error = GF_AUTHENTICATION_FAILURE; + gf_dm_sess_notify_state(sess, sess->status, sess->last_error); + } + } + } +#endif +} + +const char *gf_dm_sess_mime_type(GF_DownloadSession *sess) +{ + Bool go; + u32 flags = sess->flags; + sess->flags |= GF_NETIO_SESSION_NOT_CACHED; + + go = 1; + while (go) { + switch (sess->status) { + /*setup download*/ + case GF_NETIO_SETUP: + gf_dm_connect(sess); + if (sess->status == GF_NETIO_SETUP) + gf_sleep(200); + break; + case GF_NETIO_WAIT_FOR_REPLY: + gf_sleep(20); + case GF_NETIO_CONNECTED: + sess->do_requests(sess); + break; + case GF_NETIO_DATA_EXCHANGE: + case GF_NETIO_DISCONNECTED: + case GF_NETIO_STATE_ERROR: + go = 0; + break; + } + } + sess->flags = flags; + if (sess->status==GF_NETIO_STATE_ERROR) return NULL; + return sess->mime_type; +} + + + + +GF_DownloadManager *gf_dm_new(GF_Config *cfg) +{ + const char *opt; + GF_DownloadManager *dm; + if (!cfg) return NULL; + GF_SAFEALLOC(dm, GF_DownloadManager); + dm->sessions = gf_list_new(); + dm->cfg = cfg; + + opt = gf_cfg_get_key(cfg, "General", "CacheDirectory"); + if (opt) { + if (opt[strlen(opt)-1] != GF_PATH_SEPARATOR) { + dm->cache_directory = (char *) malloc(sizeof(char)* (strlen(opt)+2)); + sprintf(dm->cache_directory, "%s%c", opt, GF_PATH_SEPARATOR); + } else { + dm->cache_directory = strdup(opt); + } + } +#ifdef GPAC_HAS_SSL + ssl_init(dm, 0); +#endif + return dm; +} + +void gf_dm_set_auth_callback(GF_DownloadManager *dm, + Bool (*GetUserPassword)(void *usr_cbk, const char *site_url, char *usr_name, char *password), + void *usr_cbk) +{ + if (dm) { + dm->GetUserPassword = GetUserPassword; + dm->usr_cbk = usr_cbk; + } +} + +void gf_dm_del(GF_DownloadManager *dm) +{ + /*destroy all pending sessions*/ + while (gf_list_count(dm->sessions)) { + GF_DownloadSession *sess = (GF_DownloadSession *) gf_list_get(dm->sessions, 0); + gf_dm_sess_del(sess); + } + gf_list_del(dm->sessions); + + free(dm->cache_directory); + +#ifdef GPAC_HAS_SSL + if (dm->ssl_ctx) SSL_CTX_free(dm->ssl_ctx); +#endif + + free(dm); +} + + +static GFINLINE void gf_dm_data_received(GF_DownloadSession *sess, char *data, u32 nbBytes) +{ + GF_NETIO_Parameter par; + u32 runtime, rcv; + + rcv = nbBytes; + + if (! (sess->flags & GF_NETIO_SESSION_NOT_CACHED)) { + if (sess->cache) { + fwrite(data, nbBytes, 1, sess->cache); + fflush(sess->cache); + } + sess->bytes_done += nbBytes; + /*if not threaded don't signal data to user*/ + if (sess->th) { + par.msg_type = GF_NETIO_DATA_EXCHANGE; + par.error = GF_OK; + par.data = NULL; + par.size = nbBytes; + gf_dm_sess_user_io(sess, &par); + } + } else if (sess->icy_metaint) { + while (nbBytes) { + if (sess->icy_bytes == sess->icy_metaint) { + sess->icy_count = 1 + 16* (u8) data[0]; + + /*skip icy metadata*/ + if (sess->icy_count >= nbBytes) { + sess->icy_count -= nbBytes; + nbBytes = 0; + } else { + if (sess->icy_count > 1) { + GF_NETIO_Parameter par; + char szData[4096]; + memcpy(szData, data+1, sess->icy_count-1); + szData[sess->icy_count] = 0; + + par.error = 0; + par.msg_type = GF_NETIO_PARSE_HEADER; + par.name = "icy-meta"; + par.value = szData; + gf_dm_sess_user_io(sess, &par); + } + nbBytes -= sess->icy_count; + data += sess->icy_count; + sess->icy_count = 0; + sess->icy_bytes = 0; + } + } else { + u32 left = sess->icy_metaint - sess->icy_bytes; + if (left > nbBytes) { + left = nbBytes; + sess->icy_bytes += left; + nbBytes = 0; + } else { + sess->icy_bytes = sess->icy_metaint; + nbBytes -= left; + } + + par.msg_type = GF_NETIO_DATA_EXCHANGE; + par.error = GF_OK; + par.data = data; + par.size = left; + gf_dm_sess_user_io(sess, &par); + + data += left; + } + } + } else { + sess->bytes_done += nbBytes; + /*if not threaded don't signal data to user*/ + if (sess->th) { + par.msg_type = GF_NETIO_DATA_EXCHANGE; + par.error = GF_OK; + par.data = data; + par.size = nbBytes; + gf_dm_sess_user_io(sess, &par); + } + } + + if (sess->total_size && (sess->bytes_done == sess->total_size)) { + gf_dm_disconnect(sess); + par.msg_type = GF_NETIO_DATA_TRANSFERED; + par.error = GF_OK; + gf_dm_sess_user_io(sess, &par); + return; + } + /*update state if not done*/ + if (rcv) { + sess->bytes_in_wnd += rcv; + runtime = gf_sys_clock() - sess->window_start; + if (!runtime) { + sess->bytes_per_sec = 0; + } else { + sess->bytes_per_sec = (1000 * (sess->bytes_in_wnd)) / runtime; + if (runtime>1000) { + sess->window_start += runtime/2; + sess->bytes_in_wnd = sess->bytes_per_sec / 2; + } + } + } +} + + +GF_EXPORT +GF_Err gf_dm_sess_fetch_data(GF_DownloadSession *sess, char *buffer, u32 buffer_size, u32 *read_size) +{ + GF_Err e; +// if (sess->cache || !buffer || !buffer_size) return GF_BAD_PARAM; + if (/*sess->cache || */ !buffer || !buffer_size) return GF_BAD_PARAM; + if (sess->th) return GF_BAD_PARAM; + if (sess->status == GF_NETIO_DISCONNECTED) return GF_EOS; + if (sess->status > GF_NETIO_DATA_TRANSFERED) return GF_BAD_PARAM; + + *read_size = 0; + if (sess->status == GF_NETIO_DATA_TRANSFERED) return GF_EOS; + + if (sess->status == GF_NETIO_SETUP) { + gf_dm_connect(sess); + return GF_OK; + } else if (sess->status < GF_NETIO_DATA_EXCHANGE) { + sess->do_requests(sess); + if (sess->status > GF_NETIO_DATA_TRANSFERED) return GF_SERVICE_ERROR; + return GF_OK; + } + /*we're running but we had data previously*/ + if (sess->init_data) { + if (sess->init_data_size<=buffer_size) { + memcpy(buffer, sess->init_data, sizeof(char)*sess->init_data_size); + *read_size = sess->init_data_size; + free(sess->init_data); + sess->init_data = NULL; + sess->init_data_size = 0; + } else { + memcpy(buffer, sess->init_data, sizeof(char)*buffer_size); + *read_size = buffer_size; + sess->init_data_size -= buffer_size; + memcpy(sess->init_data, sess->init_data+buffer_size, sizeof(char)*sess->init_data_size); + } + return GF_OK; + } + + e = gf_dm_read_data(sess, buffer, buffer_size, read_size); + if (e) return e; + gf_dm_data_received(sess, buffer, *read_size); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_dm_sess_get_stats(GF_DownloadSession * sess, const char **server, const char **path, u32 *total_size, u32 *bytes_done, u32 *bytes_per_sec, u32 *net_status) +{ + if (!sess) return GF_BAD_PARAM; + if (server) *server = sess->server_name; + if (path) *path = sess->remote_path; + if (total_size) *total_size = sess->total_size; + if (bytes_done) *bytes_done = sess->bytes_done; + if (bytes_per_sec) *bytes_per_sec = sess->bytes_per_sec; + if (net_status) *net_status = sess->status; + if (sess->status == GF_NETIO_DISCONNECTED) return GF_EOS; + else if (sess->status == GF_NETIO_STATE_ERROR) return GF_SERVICE_ERROR; + return GF_OK; +} + +GF_EXPORT +const char *gf_dm_sess_get_cache_name(GF_DownloadSession * sess) +{ + if (!sess) return NULL; + return sess->cache_name; +} + +void gf_dm_sess_abort(GF_DownloadSession * sess) +{ + if (sess->mx) { + gf_mx_p(sess->mx); + gf_dm_disconnect(sess); + sess->status = GF_NETIO_STATE_ERROR; + gf_mx_v(sess->mx); + } else { + gf_dm_disconnect(sess); + } +} +void *gf_dm_sess_get_private(GF_DownloadSession * sess) +{ + return sess ? sess->ext : NULL; +} + +void gf_dm_sess_set_private(GF_DownloadSession * sess, void *private_data) +{ + if (sess) sess->ext = private_data; +} + +/* HTTP(S) stuff*/ +static GFINLINE u32 http_skip_space(char *val) +{ + u32 ret = 0; + while (val[ret] == ' ') ret+=1; + return ret; +} + +static GFINLINE char *http_is_header(char *line, char *header_name) +{ + char *res; + if (strnicmp(line, header_name, strlen(header_name))) return NULL; + res = line + strlen(header_name); + while ((res[0] == ':') || (res[0] == ' ')) res+=1; + return res; +} + +void http_do_requests(GF_DownloadSession *sess) +{ + GF_Err e; + Bool is_ice; + GF_NETIO_Parameter par; + char sHTTP[GF_DOWNLOAD_BUFFER_SIZE]; + char buf[1024]; + char comp[400]; + char *new_location; + char *hdr, *hdr_val; + u32 bytesRead, res; + s32 LinePos, Pos; + u32 rsp_code, ContentLength, first_byte, last_byte, total_size, range, no_range; + s32 BodyStart; + + /*sent HTTP request*/ + if (sess->status==GF_NETIO_CONNECTED) { + char range_buf[1024]; + char pass_buf[1024]; + const char *user_agent; + const char *user_profile; + const char *param_string; + u32 size; + Bool has_accept, has_connection, has_range, has_agent, send_profile; + + /*setup authentification*/ + strcpy(pass_buf, ""); + if (sess->user) { + if (!sess->passwd) { + char szUSR[50], szPASS[50]; + strcpy(szUSR, sess->user); + strcpy(szPASS, ""); + /*failed getting pass*/ + if (!sess->dm->GetUserPassword || !sess->dm->GetUserPassword(sess->dm->usr_cbk, sess->server_name, szUSR, szPASS)) { + sess->status = GF_NETIO_STATE_ERROR; + return; + } + sess->passwd = strdup(szPASS); + } + sprintf(pass_buf, "%s:%s", sess->user, sess->passwd); + size = gf_base64_encode(pass_buf, strlen(pass_buf), range_buf, 1024); + range_buf[size] = 0; + sprintf(pass_buf, "Authorization: Basic %s", range_buf); + } + + + /*MIX2005 KMS project*/ +#if 0 + if (strstr(sess->remote_path, "getKey.php?")) { + char *sLogin, *sPass; + sLogin = gf_cfg_get_key(sess->dm->cfg, "General", "KMS_User"); + sPass = gf_cfg_get_key(sess->dm->cfg, "General", "KMS_Password"); + if (!sLogin) sLogin = "mix"; + if (!sPass) sPass = "mix"; + sprintf(https_get_buffer, "%s&login=%s&password=%s", sess->remote_path, sLogin, sPass); + } +#endif + + user_agent = gf_cfg_get_key(sess->dm->cfg, "Downloader", "UserAgent"); + if (!user_agent) user_agent = GF_DOWNLOAD_AGENT_NAME; + + + par.error = 0; + par.msg_type = GF_NETIO_GET_METHOD; + par.name = NULL; + gf_dm_sess_user_io(sess, &par); + + if (par.name) { + if (!strcmp(par.name, "GET")) sess->http_read_type = 0; + else if (!strcmp(par.name, "HEAD")) sess->http_read_type = 1; + else sess->http_read_type = 2; + } else { + sess->http_read_type = 0; + } + + param_string = gf_cfg_get_key(sess->dm->cfg, "Downloader", "ParamString"); + if (param_string) { + if (strchr(sess->remote_path, '?')) { + sprintf(sHTTP, "%s %s&%s HTTP/1.0\r\nHost: %s\r\n" , + par.name ? par.name : "GET", sess->remote_path, param_string, sess->server_name); + } else { + sprintf(sHTTP, "%s %s?%s HTTP/1.0\r\nHost: %s\r\n" , + par.name ? par.name : "GET", sess->remote_path, param_string, sess->server_name); + } + } else { + sprintf(sHTTP, "%s %s HTTP/1.0\r\nHost: %s\r\n" , + par.name ? par.name : "GET", sess->remote_path, sess->server_name); + } + + /*signal we support title streaming*/ + if (!strcmp(sess->remote_path, "/")) strcat(sHTTP, "icy-metadata:1\r\n"); + + /*get all headers*/ + has_agent = has_accept = has_connection = has_range = 0; + while (1) { + par.msg_type = GF_NETIO_GET_HEADER; + par.name = NULL; + par.value = NULL; + gf_dm_sess_user_io(sess, &par); + if (!par.name) break; + strcat(sHTTP, par.name); + strcat(sHTTP, ": "); + strcat(sHTTP, par.value); + strcat(sHTTP, "\r\n"); + if (!strcmp(par.name, "Accept")) has_accept = 1; + else if (!strcmp(par.name, "Connection")) has_connection = 1; + else if (!strcmp(par.name, "Range")) has_range = 1; + else if (!strcmp(par.name, "User-Agent")) has_agent = 1; + } + if (!has_agent) { + strcat(sHTTP, "User-Agent: "); + strcat(sHTTP, user_agent); + strcat(sHTTP, "\r\n"); + } + if (!has_accept) strcat(sHTTP, "Accept: */*\r\n"); + if (!has_connection) strcat(sHTTP, "Connection: Keep-Alive\r\n"); + if (!has_range && sess->cache_start_size) { + sprintf(range_buf, "Range: bytes=%d-\r\n", sess->cache_start_size); + strcat(sHTTP, range_buf); + } + if (strlen(pass_buf)) { + strcat(sHTTP, pass_buf); + strcat(sHTTP, "\r\n"); + } + if (sess->flags & GF_DOWNLOAD_IS_ICY) strcat(sHTTP, "Icy-Metadata: 1\r\n"); + + par.msg_type = GF_NETIO_GET_CONTENT; + par.data = NULL; + par.size = 0; + + /*check if we have personalization info*/ + send_profile = 0; + user_profile = gf_cfg_get_key(sess->dm->cfg, "Downloader", "UserProfileID"); + if (user_profile) { + strcat(sHTTP, "X-UserProfileID: "); + strcat(sHTTP, user_profile); + strcat(sHTTP, "\r\n"); + } else { + user_profile = gf_cfg_get_key(sess->dm->cfg, "Downloader", "UserProfile"); + if (user_profile) { + FILE *profile = fopen(user_profile, "rt"); + if (profile) { + fseek(profile, 0, SEEK_END); + par.size = ftell(profile); + fclose(profile); + sprintf(range_buf, "Content-Length: %d\r\n", par.size); + strcat(sHTTP, range_buf); + strcat(sHTTP, "Content-Type: text/xml\r\n"); + send_profile = 1; + } + } + } + + + if (!send_profile) { + gf_dm_sess_user_io(sess, &par); + if (par.data && par.size) { + sprintf(range_buf, "Content-Length: %d\r\n", par.size); + strcat(sHTTP, range_buf); + } + } + strcat(sHTTP, "\r\n"); + + if (send_profile || par.data) { + u32 len = strlen(sHTTP); + char *tmp_buf = malloc(sizeof(char)*(len+par.size)); + strcpy(tmp_buf, sHTTP); + if (par.data) { + memcpy(tmp_buf+len, par.data, par.size); + } else { + FILE *profile; + user_profile = gf_cfg_get_key(sess->dm->cfg, "Downloader", "UserProfile"); + assert (user_profile); + profile = fopen(user_profile, "rt"); + fread(tmp_buf+len, 1, par.size, profile); + fclose(profile); + } + +#ifdef GPAC_HAS_SSL + if (sess->ssl) { + e = GF_IP_NETWORK_FAILURE; + if (!SSL_write(sess->ssl, tmp_buf, len+par.size)) e = GF_OK; + } else +#endif + e = gf_sk_send(sess->sock, tmp_buf, len+par.size); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] %s\n\n", tmp_buf)); + free(tmp_buf); + } else { + +#ifdef GPAC_HAS_SSL + if (sess->ssl) { + e = GF_IP_NETWORK_FAILURE; + if (!SSL_write(sess->ssl, sHTTP, strlen(sHTTP))) e = GF_OK; + } else +#endif + e = gf_sk_send(sess->sock, sHTTP, strlen(sHTTP)); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] %s\n\n", sHTTP)); + } + + if (e) { + sess->status = GF_NETIO_STATE_ERROR; + sess->last_error = e; + gf_dm_sess_notify_state(sess, GF_NETIO_STATE_ERROR, e); + return; + } + + sess->status = GF_NETIO_WAIT_FOR_REPLY; + gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK); + return; + } + + /*process HTTP request*/ + if (sess->status == GF_NETIO_WAIT_FOR_REPLY) { + bytesRead = res = 0; + new_location = NULL; + while (1) { + e = gf_dm_read_data(sess, sHTTP + bytesRead, GF_DOWNLOAD_BUFFER_SIZE - bytesRead, &res); + + switch (e) { + case GF_IP_NETWORK_EMPTY: + if (!bytesRead) return; + continue; + /*socket has been closed while configuring, retry (not sure if the server got the GET)*/ + case GF_IP_CONNECTION_CLOSED: + gf_dm_disconnect(sess); + if (sess->num_retry) + sess->status = GF_NETIO_SETUP; + else { + sess->last_error = e; + sess->status = GF_NETIO_STATE_ERROR; + } + return; + case GF_OK: + if (!res) return; + break; + default: + goto exit; + } + bytesRead += res; + + /*locate body start*/ + BodyStart = gf_token_find(sHTTP, 0, bytesRead, "\r\n\r\n"); + if (BodyStart <= 0) { + BodyStart=0; + continue; + } + BodyStart += 4; + break; + } + if (bytesRead < 0) { + e = GF_REMOTE_SERVICE_ERROR; + goto exit; + } + if (!BodyStart) + BodyStart = bytesRead; + + sHTTP[BodyStart-1] = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] %s\n\n", sHTTP)); + + LinePos = gf_token_get_line(sHTTP, 0, bytesRead, buf, 1024); + Pos = gf_token_get(buf, 0, " \t\r\n", comp, 400); + + if (sess->mime_type) free(sess->mime_type); + sess->mime_type = NULL; + + is_ice = 0; + if (!strncmp("ICY", comp, 4)) { + is_ice = 1; + /*be prepared not to receive any mime type from ShoutCast servers*/ + sess->mime_type = strdup("audio/mpeg"); + } else if ((strncmp("HTTP", comp, 4) != 0)) { + e = GF_REMOTE_SERVICE_ERROR; + goto exit; + } + Pos = gf_token_get(buf, Pos, " ", comp, 400); + if (Pos <= 0) { + e = GF_REMOTE_SERVICE_ERROR; + goto exit; + } + rsp_code = (u32) atoi(comp); + Pos = gf_token_get(buf, Pos, " \r\n", comp, 400); + + no_range = range = ContentLength = first_byte = last_byte = total_size = 0; + //parse header + while (1) { + char *sep, *hdr_sep; + if ( (s32) LinePos + 4 > BodyStart) break; + LinePos = gf_token_get_line(sHTTP, LinePos , bytesRead, buf, 1024); + if (LinePos < 0) break; + + hdr_sep = NULL; + hdr_val = NULL; + hdr = buf; + sep = strchr(buf, ':'); + if (sep) { + sep[0]=0; + hdr_val = sep+1; + while (hdr_val[0]==' ') hdr_val++; + hdr_sep = strrchr(hdr_val, '\r'); + if (hdr_sep) hdr_sep[0] = 0; + } + + par.error = 0; + par.msg_type = GF_NETIO_PARSE_HEADER; + par.name = hdr; + par.value = hdr_val; + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[HTTP] Processing header %s: %s\n", hdr, hdr_val)); + gf_dm_sess_user_io(sess, &par); + + if (!stricmp(hdr, "Content-Length") ) ContentLength = (u32) atoi(hdr_val); + else if (!stricmp(hdr, "Content-Type")) { + if (sess->mime_type) free(sess->mime_type); + sess->mime_type = strdup(hdr_val); + while (1) { + u32 len = strlen(sess->mime_type); + char c = len ? sess->mime_type[len-1] : 0; + if ((c=='\r') || (c=='\n')) { + sess->mime_type[len-1] = 0; + } else { + break; + } + } + hdr = strchr(sess->mime_type, ';'); + if (hdr) hdr[0] = 0; + } + else if (!stricmp(hdr, "Content-Range")) { + range = 1; + if (!strncmp(hdr_val, "bytes", 5)) { + hdr_val += 5; + if (hdr_val[0] == ':') hdr_val += 1; + hdr_val += http_skip_space(hdr_val); + if (hdr_val[0] == '*') { + sscanf(hdr_val, "*/%d", &total_size); + } else { + sscanf(hdr_val, "%d-%d/%d", &first_byte, &last_byte, &total_size); + } + } + } + else if (!stricmp(hdr, "Accept-Ranges")) { + if (strstr(hdr_val, "none")) no_range = 1; + } + else if (!stricmp(hdr, "Location")) + new_location = strdup(hdr_val); + else if (!stricmp(hdr, "icy-metaint")) + sess->icy_metaint = atoi(hdr_val); + else if (!stricmp(hdr, "ice") || !stricmp(hdr, "icy") ) + is_ice = 1; + else if (!stricmp(hdr, "X-UserProfileID") ) + gf_cfg_set_key(sess->dm->cfg, "Downloader", "UserProfileID", hdr_val); +/* else if (!stricmp(hdr, "Connection") ) + if (strstr(hdr_val, "close")) sess->http_read_type = 1; */ + + + if (sep) sep[0]=':'; + if (hdr_sep) hdr_sep[0] = '\r'; + } + if (no_range) first_byte = 0; + + if (sess->cache_start_size) { + if (total_size && (sess->cache_start_size >= total_size) ) { + rsp_code = 200; + ContentLength = total_size; + } + if (ContentLength && (sess->cache_start_size == ContentLength) ) rsp_code = 200; + } + + par.msg_type = GF_NETIO_PARSE_REPLY; + par.error = GF_OK; + par.reply = rsp_code; + par.value = comp; + + switch (rsp_code) { + case 200: + case 201: + case 202: + case 206: + gf_dm_sess_user_io(sess, &par); + e = GF_OK; + break; + /*redirection: extract the new location*/ + case 301: + case 302: + if (!new_location || !strlen(new_location) ) { + gf_dm_sess_user_io(sess, &par); + e = GF_URL_ERROR; + goto exit; + } + while ( + (new_location[strlen(new_location)-1] == '\n') + || (new_location[strlen(new_location)-1] == '\r') ) + new_location[strlen(new_location)-1] = 0; + + /*reset and reconnect*/ + gf_dm_disconnect(sess); + sess->status = GF_NETIO_SETUP; + e = gf_dm_setup_from_url(sess, new_location); + if (e) { + sess->status = GF_NETIO_STATE_ERROR; + sess->last_error = e; + gf_dm_sess_notify_state(sess, sess->status, e); + return; + } + return; + case 404: + case 416: + /*try without cache (some servers screw up when content-length is specified)*/ + if (sess->cache_start_size) { + gf_dm_disconnect(sess); + sess->status = GF_NETIO_SETUP; + return; + } else if (is_ice && !(sess->flags & GF_DOWNLOAD_IS_ICY)) { + gf_dm_disconnect(sess); + sess->status = GF_NETIO_SETUP; + sess->flags |= GF_DOWNLOAD_IS_ICY; + return; + } + gf_dm_sess_user_io(sess, &par); + e = GF_URL_ERROR; + goto exit; + case 503: + default: + gf_dm_sess_user_io(sess, &par); + e = GF_REMOTE_SERVICE_ERROR; + goto exit; + } + + /*head*/ + if (sess->http_read_type==1) { + gf_dm_disconnect(sess); + gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); + sess->status = GF_NETIO_DISCONNECTED; + sess->http_read_type = 0; + return; + } + + if (!ContentLength && sess->mime_type && strstr(sess->mime_type, "ogg")) is_ice = 1; + + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[HTTP] Connected to %s - error %s\n", sess->server_name, gf_error_to_string(e) )); + + /*some servers may reply without content length, but we MUST have it*/ +// if (!is_ice && !ContentLength) e = GF_REMOTE_SERVICE_ERROR; + if (e) goto exit; + + /*force disabling cache (no content length)*/ + if (is_ice) { + sess->flags |= GF_NETIO_SESSION_NOT_CACHED; + if (sess->mime_type && !stricmp(sess->mime_type, "video/nsv")) { + free(sess->mime_type); + sess->mime_type = strdup("audio/aac"); + } + } + + /*done*/ + if (sess->cache_start_size + && ( (total_size && sess->cache_start_size >= total_size) || (sess->cache_start_size == ContentLength)) ) { + sess->total_size = sess->bytes_done = sess->cache_start_size; + /*disconnect*/ + gf_dm_disconnect(sess); + BodyStart = bytesRead; + gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); + } + else if (sess->flags & GF_DOWNLOAD_IS_ICY) { + sess->icy_bytes = 0; + sess->status = GF_NETIO_DATA_EXCHANGE; + } + /*we don't expect anything*/ + else if (!ContentLength && sess->http_read_type) { + gf_dm_disconnect(sess); + gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); + sess->status = GF_NETIO_DISCONNECTED; + sess->http_read_type = 0; + } + /*no range header, Accept-Ranges deny or dumb server : restart*/ + else if (!range || !first_byte || (first_byte != sess->cache_start_size) ) { + sess->cache_start_size = sess->bytes_done = 0; + sess->total_size = ContentLength; + if (! (sess->flags & GF_NETIO_SESSION_NOT_CACHED) ) { + sess->cache = fopen(sess->cache_name, "wb"); + if (!sess->cache) { + e = GF_IO_ERR; + goto exit; + } + } + sess->status = GF_NETIO_DATA_EXCHANGE; + } + /*resume*/ + else { + sess->total_size = ContentLength + sess->cache_start_size; + if (! (sess->flags & GF_NETIO_SESSION_NOT_CACHED) ) { + sess->cache = fopen(sess->cache_name, "ab"); + if (!sess->cache) { + e = GF_IO_ERR; + goto exit; + } + } + sess->status = GF_NETIO_DATA_EXCHANGE; + sess->bytes_done = sess->cache_start_size; + } + + sess->window_start = sess->start_time = gf_sys_clock(); + sess->bytes_in_wnd = 0; + + + //we may have existing data in this buffer ... + if (!e && (BodyStart < (s32) bytesRead)) { + gf_dm_data_received(sess, sHTTP + BodyStart, bytesRead - BodyStart); + /*store data if no callbacks or cache*/ +// if (sess->flags & GF_NETIO_SESSION_NOT_CACHED) { + if (sess->init_data) free(sess->init_data); + sess->init_data_size = bytesRead - BodyStart; + sess->init_data = (char *) malloc(sizeof(char) * sess->init_data_size); + memcpy(sess->init_data, sHTTP+BodyStart, sess->init_data_size); +// } + } +exit: + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[HTTP] Error parsing reply: %s\n", gf_error_to_string(e) )); + gf_dm_disconnect(sess); + sess->status = GF_NETIO_STATE_ERROR; + sess->last_error = e; + gf_dm_sess_notify_state(sess, sess->status, e); + } + return; + } + /*fetch data*/ + while (1) { + u32 size; +#if 1 + if (sess->limit_data_rate && sess->bytes_per_sec) { + if (sess->bytes_per_sec>sess->limit_data_rate) { + /*update state*/ + u32 runtime = gf_sys_clock() - sess->window_start; + sess->bytes_per_sec = (1000 * (sess->bytes_in_wnd)) / runtime; + if (sess->bytes_per_sec > sess->limit_data_rate) return; + } + } +#endif + e = gf_dm_read_data(sess, sHTTP, GF_DOWNLOAD_BUFFER_SIZE, &size); + if (!size || e == GF_IP_NETWORK_EMPTY) { + if (!sess->total_size && (gf_sys_clock() - sess->window_start > 1000)) { + sess->total_size = sess->bytes_done; + gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); + } + return; + } + + if (e) { + gf_dm_disconnect(sess); + sess->last_error = e; + gf_dm_sess_notify_state(sess, sess->status, e); + return; + } + gf_dm_data_received(sess, sHTTP, size); + /*socket empty*/ + if (size < GF_DOWNLOAD_BUFFER_SIZE) return; + } +} + diff --git a/src/utils/error.c b/src/utils/error.c new file mode 100644 index 0000000..6f94278 --- /dev/null +++ b/src/utils/error.c @@ -0,0 +1,434 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#ifndef _WIN32_WCE +#include +#endif + +/*GPAC memory tracking*/ +size_t gpac_allocated_memory = 0; +size_t gpac_nb_alloc_blocs = 0; +void *gf_malloc(size_t size) +{ + void *ptr; + size_t size_g = size + sizeof(size_t); + ptr = malloc(size_g); + *(size_t *)ptr = size; + gpac_allocated_memory += size; + gpac_nb_alloc_blocs++; + return (void *) ( (char *)ptr + sizeof(size_t) ); +} +void *gf_realloc(void *ptr, size_t size) +{ + size_t prev_size; + char *ptr_g = (char *)ptr; + if (!ptr) return gf_malloc(size); + ptr_g -= sizeof(size_t); + prev_size = *(size_t *)ptr_g; +#ifndef _WIN32_WCE + assert(gpac_allocated_memory >= prev_size); +#endif + gpac_allocated_memory -= prev_size; + ptr_g = (char *) realloc(ptr_g, size+sizeof(size_t)); + *(size_t *)ptr_g = size; + gpac_allocated_memory += size; + return ptr_g + sizeof(size_t); +} +void gf_free(void *ptr) +{ + if (ptr) { + char *ptr_g = (char *)ptr - sizeof(size_t); + size_t size_g = *(size_t *)ptr_g; +#ifndef _WIN32_WCE + assert(gpac_allocated_memory >= size_g); +#endif + gpac_allocated_memory -= size_g; + gpac_nb_alloc_blocs--; + free(ptr_g); + } +} +char *gf_strdup(const char *str) +{ + size_t len = strlen(str) + 1; + char *ptr = (char *) gf_malloc(len); + memcpy(ptr, str, len); + return ptr; +} + + +#include + +static char szTYPE[5]; + +GF_EXPORT +const char *gf_4cc_to_str(u32 type) +{ + u32 ch, i; + char *ptr, *name = (char *)szTYPE; + ptr = name; + for (i = 0; i < 4; i++, name++) { + ch = type >> (8 * (3-i) ) & 0xff; + if ( ch >= 0x20 && ch <= 0x7E ) { + *name = ch; + } else { + *name = '.'; + } + } + *name = 0; + return (const char *) ptr; +} + + +static const char *szProg[] = +{ + " ", + "= ", + "== ", + "=== ", + "==== ", + "===== ", + "====== ", + "======= ", + "======== ", + "========= ", + "========== ", + "=========== ", + "============ ", + "============= ", + "============== ", + "=============== ", + "================ ", + "================= ", + "================== ", + "=================== ", + "====================", +}; + +static u32 prev_pos = 0; +static u32 prev_pc = 0; +static void gf_on_progress_stdout(char *_title, u32 done, u32 total) +{ + Double prog; + u32 pos; + char *szT = _title ? (char *)_title : (char *) ""; + prog = done; + prog /= total; + pos = MIN((u32) (20 * prog), 20); + + if (pos>prev_pos) { + prev_pos = 0; + prev_pc = 0; + } + if (done==total) { + u32 len = strlen(szT) + 40; + while (len) { fprintf(stdout, " "); len--; }; + fprintf(stdout, "\r"); + } + else { + u32 pc = (u32) ( 100 * prog); + if ((pos!=prev_pos) || (pc!=prev_pc)) { + prev_pos = pos; + prev_pc = pc; + fprintf(stdout, "%s: |%s| (%02d/100)\r", szT, szProg[pos], pc); + fflush(stdout); + } + } +} + +static gf_on_progress_cbk prog_cbk = NULL; +static void *user_cbk; + +GF_EXPORT +void gf_set_progress(char *title, u32 done, u32 total) +{ + if (prog_cbk) { + prog_cbk(user_cbk, title, done, total); + } +#ifndef _WIN32_WCE + else { + gf_on_progress_stdout(title, done, total); + } +#endif +} + +GF_EXPORT +void gf_set_progress_callback(void *_user_cbk, gf_on_progress_cbk _prog_cbk) +{ + prog_cbk = _prog_cbk; + user_cbk = _user_cbk; +} + + +u32 global_log_level = 0; +u32 global_log_tools = 0; + + +#ifndef GPAC_DISABLE_LOG +u32 call_lev = 0; +u32 call_tool = 0; + +GF_EXPORT +u32 gf_log_get_level() { return global_log_level; } + +GF_EXPORT +u32 gf_log_get_tools() { return global_log_tools; } + +void default_log_callback(void *cbck, u32 level, u32 tool, const char* fmt, va_list vlist) +{ +#ifndef _WIN32_WCE + vfprintf(stdout, fmt, vlist); +#endif +} + + +static void *user_log_cbk = NULL; +static gf_log_cbk log_cbk = default_log_callback; + +GF_EXPORT +void gf_log(const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + log_cbk(user_log_cbk, call_lev, call_tool, fmt, vl); + va_end(vl); +} + +GF_EXPORT +void gf_log_set_level(u32 level) +{ + global_log_level = level; +} +GF_EXPORT +void gf_log_set_tools(u32 modules) +{ + global_log_tools = modules; +} +GF_EXPORT +void gf_log_lt(u32 ll, u32 lt) +{ + call_lev = ll; + call_tool = lt; +} + +GF_EXPORT +gf_log_cbk gf_log_set_callback(void *usr_cbk, gf_log_cbk cbk) +{ + gf_log_cbk prev_cbk = log_cbk; + log_cbk = cbk; + if (!log_cbk) log_cbk = default_log_callback; + user_log_cbk = usr_cbk; + return prev_cbk; +} +#else +GF_EXPORT +void gf_log(const char *fmt, ...) +{ +} +GF_EXPORT +void gf_log_lt(u32 ll, u32 lt) +{ +} +GF_EXPORT +void gf_log_set_level(u32 level) +{ +} +GF_EXPORT +void gf_log_set_tools(u32 modules) +{ +} +GF_EXPORT +gf_log_cbk gf_log_set_callback(void *usr_cbk, gf_log_cbk cbk) +{ + return NULL; +} +GF_EXPORT +u32 gf_log_get_level() { return 0; } + +GF_EXPORT +u32 gf_log_get_tools() { return 0; } + +#endif + + +GF_EXPORT +const char *gf_error_to_string(GF_Err e) +{ + switch (e) { + case GF_SCRIPT_INFO: + return "Script message"; + case GF_EOS: + return "End Of Stream / File"; + case GF_OK: + return "No Error"; + + /*General errors */ + case GF_BAD_PARAM: + return "Bad Parameter"; + case GF_OUT_OF_MEM: + return "Out Of Memory"; + case GF_IO_ERR: + return "I/O Error"; + case GF_NOT_SUPPORTED: + return "Feature Not Supported"; + case GF_CORRUPTED_DATA: + return "Corrupted Data in file/stream"; + + /*File Format Errors */ + case GF_ISOM_INVALID_FILE: + return "Invalid IsoMedia File"; + case GF_ISOM_INCOMPLETE_FILE: + return "IsoMedia File is truncated"; + case GF_ISOM_INVALID_MEDIA: + return "Invalid IsoMedia Media"; + case GF_ISOM_INVALID_MODE: + return "Invalid Mode while accessing the file"; + case GF_ISOM_UNKNOWN_DATA_REF: + return "Media Data Reference not found"; + + /*Object Descriptor Errors */ + case GF_ODF_INVALID_DESCRIPTOR: + return "Invalid MPEG-4 Descriptor"; + case GF_ODF_FORBIDDEN_DESCRIPTOR: + return "MPEG-4 Descriptor Not Allowed"; + case GF_ODF_INVALID_COMMAND: + return "Read OD Command Failed"; + + /*BIFS Errors */ + case GF_SG_UNKNOWN_NODE: + return "Unknown BIFS Node"; + case GF_SG_INVALID_PROTO: + return "Invalid Proto Interface"; + case GF_BIFS_UNKNOWN_VERSION: + return "Invalid BIFS version"; + case GF_SCRIPT_ERROR: + return "Invalid Script"; + + /*MPEG-4 Errors */ + case GF_BUFFER_TOO_SMALL: + return "Bad Buffer size (too small)"; + case GF_NON_COMPLIANT_BITSTREAM: + return "BitStream Not Compliant"; + case GF_CODEC_NOT_FOUND: + return "Media Codec not found"; + + /*DMIF errors - local and control plane */ + case GF_URL_ERROR: + return "Requested URL is not valid or cannot be found"; + + case GF_SERVICE_ERROR: + return "Internal Service Error"; + case GF_REMOTE_SERVICE_ERROR: + return "Dialog Failure with remote peer"; + + case GF_STREAM_NOT_FOUND: + return "Media Channel couldn't be found"; + + case GF_IP_ADDRESS_NOT_FOUND: + return "IP Address Not Found"; + case GF_IP_CONNECTION_FAILURE: + return "IP Connection Failed"; + case GF_IP_NETWORK_FAILURE: + return "Network Unreachable"; + + case GF_IP_NETWORK_EMPTY: + return "Network Timeout"; + case GF_IP_SOCK_WOULD_BLOCK: + return "Socket Would Block"; + case GF_IP_CONNECTION_CLOSED: + return "Connection to server closed"; + case GF_IP_UDP_TIMEOUT: + return "UDP traffic timeout"; + case GF_AUTHENTICATION_FAILURE: + return "Authentication failure"; + case GF_SCRIPT_NOT_READY: + return "Script not ready for playback"; + + default: + return "Unknown Error"; + } +} + + +/* crc32 from ffmpeg*/ +static const u32 gf_crc_table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +GF_EXPORT +u32 gf_crc_32(char *data, u32 len) +{ + register u32 i; + u32 crc = 0xffffffff; + if (!data) return 0; + for (i=0; i> 24) ^ *data++) & 0xff]; + + return crc; +} + diff --git a/src/utils/gzio.cpp b/src/utils/gzio.cpp new file mode 100644 index 0000000..053f39f --- /dev/null +++ b/src/utils/gzio.cpp @@ -0,0 +1,1006 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id: gzio.cpp,v 1.2 2007/05/23 15:51:18 jeanlf Exp $ */ + +#include + +// Benoit: ZLIB extentions of symbian ZLIB support +#include "zlib_symbian_ext.h" + +#include "zutil.h" +typedef voidp gzFile; + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +#ifndef __SYMBIAN32__ +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +//extern voidp malloc OF((uInt size)); +//extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + +#ifdef __SYMBIAN32__ +#define GZ_EXPORT EXPORT_C +#else +#define GZ_EXPORT +#endif + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +//local gzFile gz_open OF((const char *path, const char *mode, int fd)); +//local int do_flush OF((gzFile file, int flush)); +//local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +//local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (const char *path, const char *mode, int fd) +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + //} else if (*p == 'R') { + // strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +GZ_EXPORT +gzFile gzopen (const char *path, const char *mode) +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +GZ_EXPORT +gzFile gzdopen (int fd, const char *mode) +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int gzsetparams (gzFile file, int level, int strategy) +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(gz_stream *s) +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(gz_stream *s) +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (gz_stream *s) +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +GZ_EXPORT +int gzread (gzFile file, voidp buf, unsigned len) +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int gzgetc(gzFile file) +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int gzungetc(int c, gzFile file) +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * gzgets(gzFile file, char *buf, int len) +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int gzwrite (gzFile file, void *const buf, unsigned len) +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len = 0; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + /*Benoit: to be checked if needed*/ + //len = vsnprintf(buf, sizeof(buf), format, va); + //va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int gzprintf (gzFile file, const char *format, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, + int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18, int a19, int a20) +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int gzputc(gzFile file, int c) +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int gzputs(gzFile file, const char *s) +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (gzFile file, int flush) +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int gzflush (gzFile file, int flush) +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Rewinds input file. +*/ +int gzrewind (gzFile file) +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t gzseek (gzFile file, z_off_t offset, int whence) +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t gztell (gzFile file) +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +GZ_EXPORT +int gzeof (gzFile file) +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int gzdirect (gzFile file) +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (FILE *file, uLong x) +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (gz_stream *s) +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +GZ_EXPORT +int gzclose (gzFile file) +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * gzerror (gzFile file, int *errnum) +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void gzclearerr (gzFile file) +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/src/utils/list.c b/src/utils/list.c new file mode 100644 index 0000000..c22cfb4 --- /dev/null +++ b/src/utils/list.c @@ -0,0 +1,730 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/* GF_List modes, ONLY ONE CAN BE DEFINED + + linked-list + #define GF_LIST_LINKED + + double navigation linked-list + #define GF_LIST_DOUBLE_LINKED + + single step memory array + #define GF_LIST_ARRAY + + multi-step memory array withou realloc on remove, using the GF_LIST_REALLOC macro + GF_LIST_ARRAY_GROW +*/ + +/*after some tuning, this seems to be the fastest mode on WINCE*/ +#ifdef _WIN32_WCE +#define GF_LIST_LINKED +#else +#define GF_LIST_ARRAY_GROW +#endif + +//#define GF_LIST_REALLOC(a) (a ? (3*a/2) : 10) +#define GF_LIST_REALLOC(a) (a++) + + +#if defined(GF_LIST_LINKED) + +typedef struct tagIS +{ + struct tagIS *next; + void *data; +} ItemSlot; + +struct _tag_array +{ + struct tagIS *head; + struct tagIS *tail; + u32 entryCount; + s32 foundEntryNumber; + struct tagIS *foundEntry; +}; + + +GF_EXPORT +GF_List * gf_list_new() +{ + GF_List *nlist = (GF_List *) malloc(sizeof(GF_List)); + if (! nlist) return NULL; + nlist->head = nlist->foundEntry = NULL; + nlist->tail = NULL; + nlist->foundEntryNumber = -1; + nlist->entryCount = 0; + return nlist; +} + +GF_EXPORT +void gf_list_del(GF_List *ptr) +{ + if (!ptr) return; + while (ptr->entryCount) gf_list_rem(ptr, 0); + free(ptr); +} + +GF_EXPORT +void gf_list_reset(GF_List *ptr) +{ + while (ptr && ptr->entryCount) gf_list_rem(ptr, 0); +} + +GF_EXPORT +GF_Err gf_list_add(GF_List *ptr, void* item) +{ + ItemSlot *entry; + if (! ptr) return GF_BAD_PARAM; + entry = (ItemSlot *) malloc(sizeof(ItemSlot)); + if (!entry) return GF_OUT_OF_MEM; + entry->data = item; + entry->next = NULL; + if (! ptr->head) { + ptr->head = entry; + ptr->entryCount = 1; + } else { + ptr->entryCount += 1; + ptr->tail->next = entry; + } + ptr->tail = entry; + ptr->foundEntryNumber = ptr->entryCount - 1; + ptr->foundEntry = entry; + return GF_OK; +} + +GF_EXPORT +u32 gf_list_count(GF_List *ptr) +{ + if (! ptr) return 0; + return ptr->entryCount; +} + +GF_EXPORT +void *gf_list_get(GF_List *ptr, u32 itemNumber) +{ + ItemSlot *entry; + u32 i; + + if (!ptr || (itemNumber >= ptr->entryCount) ) return NULL; + + if (!ptr->foundEntry || (itemNumber < (u32) ptr->foundEntryNumber) ) { + ptr->foundEntryNumber = 0; + ptr->foundEntry = ptr->head; + } + entry = ptr->foundEntry; + for (i = ptr->foundEntryNumber; i < itemNumber; i++ ) { + entry = entry->next; + } + ptr->foundEntryNumber = itemNumber; + ptr->foundEntry = entry; + return (void *) entry->data; +} + +GF_EXPORT +void *gf_list_last(GF_List *ptr) +{ + ItemSlot *entry; + if (!ptr || !ptr->entryCount) return NULL; + entry = ptr->head; + while (entry->next) entry = entry->next; + return entry->data; +} + +GF_EXPORT +GF_Err gf_list_rem(GF_List *ptr, u32 itemNumber) +{ + ItemSlot *tmp, *tmp2; + u32 i; + + /* !! if head is null (empty list)*/ + if ( (! ptr) || (! ptr->head) || (ptr->head && !ptr->entryCount) || (itemNumber >= ptr->entryCount) ) + return GF_BAD_PARAM; + + /*we delete the head*/ + if (! itemNumber) { + tmp = ptr->head; + ptr->head = ptr->head->next; + ptr->entryCount --; + ptr->foundEntry = ptr->head; + ptr->foundEntryNumber = 0; + free(tmp); + /*that was the last entry, reset the tail*/ + if (!ptr->entryCount) { + ptr->tail = ptr->head = ptr->foundEntry = NULL; + ptr->foundEntryNumber = -1; + } + return GF_OK; + } + + tmp = ptr->head; + i = 0; + while (i < itemNumber - 1) { + tmp = tmp->next; + i++; + } + tmp2 = tmp->next; + tmp->next = tmp2->next; + /*if we deleted the last entry, update the tail !!!*/ + if (! tmp->next || (ptr->tail == tmp2) ) { + ptr->tail = tmp; + tmp->next = NULL; + } + + free(tmp2); + ptr->entryCount --; + ptr->foundEntry = ptr->head; + ptr->foundEntryNumber = 0; + + return GF_OK; +} + +GF_EXPORT +GF_Err gf_list_rem_last(GF_List *ptr) +{ + return gf_list_rem(ptr, ptr->entryCount-1); +} + +GF_EXPORT +GF_Err gf_list_insert(GF_List *ptr, void *item, u32 position) +{ + u32 i; + ItemSlot *tmp, *tmp2; + + if (!ptr || !item) return GF_BAD_PARAM; + /*if last entry or first of an empty array...*/ + if (position >= ptr->entryCount) return gf_list_add(ptr, item); + + tmp2 = (ItemSlot *) malloc(sizeof(ItemSlot)); + tmp2->data = item; + tmp2->next = NULL; + /*special case for the head*/ + if (position == 0) { + tmp2->next = ptr->head; + ptr->head = tmp2; + ptr->entryCount ++; + ptr->foundEntry = tmp2; + ptr->foundEntryNumber = 0; + return GF_OK; + } + tmp = ptr->head; + for (i = 1; i < position; i++) { + tmp = tmp->next; + if (!tmp) + break; + } + tmp2->next = tmp->next; + tmp->next = tmp2; + ptr->entryCount ++; + ptr->foundEntry = tmp2; + ptr->foundEntryNumber = i; + return GF_OK; +} + +#elif defined(GF_LIST_DOUBLE_LINKED) + + +typedef struct tagIS +{ + struct tagIS *next; + struct tagIS *prev; + void *data; +} ItemSlot; + +struct _tag_array +{ + struct tagIS *head; + struct tagIS *tail; + u32 entryCount; + s32 foundEntryNumber; + struct tagIS *foundEntry; +}; + + +GF_EXPORT +GF_List * gf_list_new() +{ + GF_List *nlist = (GF_List *) malloc(sizeof(GF_List)); + if (! nlist) return NULL; + nlist->head = nlist->foundEntry = NULL; + nlist->tail = NULL; + nlist->foundEntryNumber = -1; + nlist->entryCount = 0; + return nlist; +} + +GF_EXPORT +void gf_list_del(GF_List *ptr) +{ + if (!ptr) return; + while (ptr->entryCount) { + gf_list_rem(ptr, 0); + } + free(ptr); +} + +GF_EXPORT +void gf_list_reset(GF_List *ptr) +{ + while (ptr && ptr->entryCount) gf_list_rem(ptr, 0); +} + +GF_EXPORT +GF_Err gf_list_add(GF_List *ptr, void* item) +{ + ItemSlot *entry; + if (! ptr) return GF_BAD_PARAM; + entry = (ItemSlot *) malloc(sizeof(ItemSlot)); + if (!entry) return GF_OUT_OF_MEM; + entry->data = item; + entry->next = entry->prev = NULL; + + if (! ptr->head) { + ptr->head = entry; + ptr->entryCount = 1; + } else { + ptr->entryCount += 1; + entry->prev = ptr->tail; + ptr->tail->next = entry; + } + ptr->tail = entry; + ptr->foundEntryNumber = ptr->entryCount - 1; + ptr->foundEntry = entry; + return GF_OK; +} + + +GF_EXPORT +u32 gf_list_count(GF_List *ptr) +{ + if (! ptr) return 0; + return ptr->entryCount; +} + +GF_EXPORT +void *gf_list_get(GF_List *ptr, u32 itemNumber) +{ + ItemSlot *entry; + u32 i; + + if (!ptr || !ptr->head || (itemNumber >= ptr->entryCount) ) return NULL; + + if (!itemNumber) { + ptr->foundEntry = ptr->head; + ptr->foundEntryNumber = 0; + return ptr->head->data; + } + + entry = ptr->foundEntry; + if ( itemNumber < (u32) ptr->foundEntryNumber ) { + for (i = ptr->foundEntryNumber; i > itemNumber; i-- ) { + entry = entry->prev; + } + } else { + for (i = ptr->foundEntryNumber; i < itemNumber; i++ ) { + entry = entry->next; + } + } + ptr->foundEntryNumber = itemNumber; + ptr->foundEntry = entry; + return (void *) entry->data; +} + +GF_EXPORT +void *gf_list_last(GF_List *ptr) +{ + if(!ptr || !ptr->tail) return NULL; + return ptr->tail->data; +} + +GF_EXPORT +GF_Err gf_list_rem(GF_List *ptr, u32 itemNumber) +{ + ItemSlot *tmp; + u32 i; + + /* !! if head is null (empty list)*/ + if ( (! ptr) || (! ptr->head) || (ptr->head && !ptr->entryCount) || (itemNumber >= ptr->entryCount) ) + return GF_BAD_PARAM; + + /*we delete the head*/ + if (! itemNumber) { + tmp = ptr->head; + ptr->head = ptr->head->next; + + ptr->entryCount --; + ptr->foundEntry = ptr->head; + ptr->foundEntryNumber = 0; + free(tmp); + + /*that was the last entry, reset the tail*/ + if (!ptr->entryCount) { + ptr->tail = ptr->head = ptr->foundEntry = NULL; + ptr->foundEntryNumber = -1; + } else { + ptr->head->prev = NULL; + } + return GF_OK; + } + else if (itemNumber==ptr->entryCount-1) { + tmp = ptr->tail; + ptr->tail = tmp->prev; + ptr->tail->next = NULL; + ptr->entryCount--; + if (ptr->foundEntry==tmp) { + ptr->foundEntry = ptr->tail; + ptr->foundEntryNumber = ptr->entryCount-1; + } + free(tmp); + return GF_OK; + } + + tmp = ptr->foundEntry; + if ( itemNumber < (u32) ptr->foundEntryNumber ) { + for (i = ptr->foundEntryNumber; i > itemNumber; i-- ) { + tmp = tmp->prev; + } + } else { + for (i = ptr->foundEntryNumber; i < itemNumber; i++ ) { + tmp = tmp->next; + } + } + tmp->prev->next = tmp->next; + tmp->next->prev = tmp->prev; + if (tmp==ptr->foundEntry) ptr->foundEntry = tmp->next; + free(tmp); + ptr->entryCount--; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_list_rem_last(GF_List *ptr) +{ + return gf_list_rem(ptr, ptr->entryCount-1); +} + +GF_EXPORT +GF_Err gf_list_insert(GF_List *ptr, void *item, u32 position) +{ + u32 i; + ItemSlot *tmp, *tmp2; + + if (!ptr || !item) return GF_BAD_PARAM; + /*if last entry or first of an empty array...*/ + if (position >= ptr->entryCount) return gf_list_add(ptr, item); + tmp2 = (ItemSlot *) malloc(sizeof(ItemSlot)); + tmp2->data = item; + tmp2->next = tmp2->prev = NULL; + /*special case for the head*/ + if (position == 0) { + ptr->head->prev = tmp2; + tmp2->next = ptr->head; + ptr->head = tmp2; + ptr->entryCount ++; + ptr->foundEntry = tmp2; + ptr->foundEntryNumber = 0; + return GF_OK; + } + + tmp = ptr->foundEntry; + if ( position < (u32) ptr->foundEntryNumber ) { + for (i = ptr->foundEntryNumber; i >= position; i-- ) { + tmp = tmp->prev; + } + tmp = tmp->prev; + } else { + for (i = ptr->foundEntryNumber; i < position; i++ ) { + tmp = tmp->next; + } + } + tmp2->next = tmp->next; + tmp2->next->prev = tmp2; + tmp2->prev = tmp; + tmp2->prev->next = tmp2; + ptr->entryCount ++; + ptr->foundEntry = tmp2; + ptr->foundEntryNumber = position; + return GF_OK; +} + +#elif defined(GF_LIST_ARRAY) + +struct _tag_array +{ + void **slots; + u32 entryCount; +}; + + +GF_EXPORT +GF_List * gf_list_new() +{ + GF_List *nlist = (GF_List *) malloc(sizeof(GF_List)); + if (! nlist) return NULL; + nlist->slots = NULL; + nlist->entryCount = 0; + return nlist; +} + +GF_EXPORT +void gf_list_del(GF_List *ptr) +{ + if (!ptr) return; + free(ptr->slots); + free(ptr); +} + +GF_EXPORT +GF_Err gf_list_add(GF_List *ptr, void* item) +{ + if (! ptr) return GF_BAD_PARAM; + + ptr->entryCount ++; + ptr->slots = (void **) realloc(ptr->slots, ptr->entryCount*sizeof(void*)); + if (!ptr->slots) { + ptr->entryCount = 0; + return GF_OUT_OF_MEM; + } + ptr->slots[ptr->entryCount-1] = item; + return GF_OK; +} + +GF_EXPORT +u32 gf_list_count(GF_List *ptr) +{ + return ptr ? ptr->entryCount : 0; +} + +GF_EXPORT +void *gf_list_get(GF_List *ptr, u32 itemNumber) +{ + if(!ptr || (itemNumber >= ptr->entryCount)) return NULL; + return ptr->slots[itemNumber]; +} + +GF_EXPORT +void *gf_list_last(GF_List *ptr) +{ + if(!ptr || !ptr->entryCount) return NULL; + return ptr->slots[ptr->entryCount-1]; +} + + +/*WARNING: itemNumber is from 0 to entryCount - 1*/ +GF_EXPORT +GF_Err gf_list_rem(GF_List *ptr, u32 itemNumber) +{ + u32 i; + if ( !ptr || !ptr->slots || !ptr->entryCount) return GF_BAD_PARAM; + i = ptr->entryCount - itemNumber - 1; + if (i) memmove(&ptr->slots[itemNumber], & ptr->slots[itemNumber +1], sizeof(void *)*i); + ptr->slots[ptr->entryCount-1] = NULL; + ptr->entryCount -= 1; + ptr->slots = (void **) realloc(ptr->slots, sizeof(void*)*ptr->entryCount); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_list_rem_last(GF_List *ptr) +{ + if ( !ptr || !ptr->slots || !ptr->entryCount) return GF_BAD_PARAM; + ptr->entryCount -= 1; + ptr->slots = (void **) realloc(ptr->slots, sizeof(void*)*ptr->entryCount); + return GF_OK; +} + + +/*WARNING: position is from 0 to entryCount - 1*/ +GF_EXPORT +GF_Err gf_list_insert(GF_List *ptr, void *item, u32 position) +{ + u32 i; + if (!ptr || !item) return GF_BAD_PARAM; + /*if last entry or first of an empty array...*/ + if (position >= ptr->entryCount) return gf_list_add(ptr, item); + ptr->slots = (void **) realloc(ptr->slots, (ptr->entryCount+1)*sizeof(void*)); + i = ptr->entryCount - position; + memmove(&ptr->slots[position + 1], &ptr->slots[position], sizeof(void *)*i); + ptr->entryCount++; + ptr->slots[position] = item; + return GF_OK; +} + +GF_EXPORT +void gf_list_reset(GF_List *ptr) +{ + if (ptr) { + ptr->entryCount = 0; + free(ptr->slots); + ptr->slots = NULL; + } +} + +#else /*GF_LIST_ARRAY_GROW*/ + + +struct _tag_array +{ + void **slots; + u32 entryCount; + u32 allocSize; +}; + +GF_EXPORT +GF_List * gf_list_new() +{ + GF_List *nlist; + + nlist = (GF_List *) malloc(sizeof(GF_List)); + if (! nlist) return NULL; + + nlist->slots = NULL; + nlist->entryCount = 0; + nlist->allocSize = 0; + return nlist; +} + +GF_EXPORT +void gf_list_del(GF_List *ptr) +{ + if (!ptr) return; + free(ptr->slots); + free(ptr); +} + +static void realloc_chain(GF_List *ptr) +{ + GF_LIST_REALLOC(ptr->allocSize); + ptr->slots = realloc(ptr->slots, ptr->allocSize*sizeof(void*)); +} + +GF_EXPORT +GF_Err gf_list_add(GF_List *ptr, void* item) +{ + if (! ptr) return GF_BAD_PARAM; + if (ptr->allocSize==ptr->entryCount) realloc_chain(ptr); + if (!ptr->slots) return GF_OUT_OF_MEM; + + ptr->slots[ptr->entryCount] = item; + ptr->entryCount ++; + return GF_OK; +} + +GF_EXPORT +u32 gf_list_count(GF_List *ptr) +{ + if (!ptr) return 0; + return ptr->entryCount; +} + +GF_EXPORT +void *gf_list_get(GF_List *ptr, u32 itemNumber) +{ + if(!ptr || (itemNumber >= ptr->entryCount)) return NULL; + return ptr->slots[itemNumber]; +} + +GF_EXPORT +void *gf_list_last(GF_List *ptr) +{ + if(!ptr || !ptr->entryCount) return NULL; + return ptr->slots[ptr->entryCount-1]; +} + + +/*WARNING: itemNumber is from 0 to entryCount - 1*/ +GF_EXPORT +GF_Err gf_list_rem(GF_List *ptr, u32 itemNumber) +{ + u32 i; + if ( !ptr || !ptr->slots || !ptr->entryCount) return GF_BAD_PARAM; + i = ptr->entryCount - itemNumber - 1; + if (i) memmove(&ptr->slots[itemNumber], & ptr->slots[itemNumber +1], sizeof(void *)*i); + ptr->slots[ptr->entryCount-1] = NULL; + ptr->entryCount -= 1; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_list_rem_last(GF_List *ptr) +{ + if ( !ptr || !ptr->slots || !ptr->entryCount) return GF_BAD_PARAM; + ptr->slots[ptr->entryCount-1] = NULL; + ptr->entryCount -= 1; + return GF_OK; +} + +/*WARNING: position is from 0 to entryCount - 1*/ +GF_EXPORT +GF_Err gf_list_insert(GF_List *ptr, void *item, u32 position) +{ + u32 i; + if (!ptr || !item) return GF_BAD_PARAM; + /*if last entry or first of an empty array...*/ + if (position >= ptr->entryCount) return gf_list_add(ptr, item); + if (ptr->allocSize==ptr->entryCount) realloc_chain(ptr); + + i = ptr->entryCount - position; + memmove(&ptr->slots[position + 1], &ptr->slots[position], sizeof(void *)*i); + ptr->entryCount++; + ptr->slots[position] = item; + return GF_OK; +} + +GF_EXPORT +void gf_list_reset(GF_List *ptr) +{ + if (ptr) ptr->entryCount = 0; +} + +#endif + +GF_EXPORT +s32 gf_list_find(GF_List *ptr, void *item) +{ + u32 i, count; + count = gf_list_count(ptr); + for (i=0; i=0) gf_list_rem(ptr, (u32) i); + return i; +} + +GF_EXPORT +void *gf_list_enum(GF_List *ptr, u32 *pos) +{ + void *res = gf_list_get(ptr, *pos); + (*pos)++; + return res; +} diff --git a/src/utils/math.c b/src/utils/math.c new file mode 100644 index 0000000..baf05e9 --- /dev/null +++ b/src/utils/math.c @@ -0,0 +1,2353 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * fixed-point trigo routines taken from freetype + * Copyright 1996-2001, 2002, 2004 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * License: FTL or GPL + * + */ + +#include + +u32 gf_get_bit_size(u32 MaxVal) +{ + u32 k=0; + while ((s32) MaxVal > ((1<x; + y = vec->y; + z = ( ( x >= 0 ) ? x : - x ) | ( (y >= 0) ? y : -y ); + shift = 0; + if ( z < ( 1L << 27 ) ) { + do { + shift++; + z <<= 1; + } + while ( z < ( 1L << 27 ) ); + + vec->x = x << shift; + vec->y = y << shift; + } + else if ( z > ( 1L << 28 ) ) { + do { + shift++; + z >>= 1; + } while ( z > ( 1L << 28 ) ); + + vec->x = x >> shift; + vec->y = y >> shift; + shift = -shift; + } + return shift; +} + +#define ANGLE_RAD_TO_DEG(_th) ((s32) ( (((fix_s64)_th)*5729582)/100000)) +#define ANGLE_DEG_TO_RAD(_th) ((s32) ( (((fix_s64)_th)*100000)/5729582)) + +static GFINLINE void gf_trig_pseudo_polarize(GF_Point2D *vec) +{ + Fixed theta; + Fixed yi, i; + Fixed x, y; + const Fixed *arctanptr; + + x = vec->x; + y = vec->y; + + /* Get the vector into the right half plane */ + theta = 0; + if ( x < 0 ) { + x = -x; + y = -y; + theta = 2 * GF_ANGLE_PI2; + } + + if ( y > 0 ) + theta = - theta; + + arctanptr = gf_trig_arctan_table; + + if ( y < 0 ) { + /* Rotate positive */ + yi = y + ( x << 1 ); + x = x - ( y << 1 ); + y = yi; + theta -= *arctanptr++; /* Subtract angle */ + } else { + /* Rotate negative */ + yi = y - ( x << 1 ); + x = x + ( y << 1 ); + y = yi; + theta += *arctanptr++; /* Add angle */ + } + + i = 0; + do { + if ( y < 0 ) { + /* Rotate positive */ + yi = y + ( x >> i ); + x = x - ( y >> i ); + y = yi; + theta -= *arctanptr++; + } else { + /* Rotate negative */ + yi = y - ( x >> i ); + x = x + ( y >> i ); + y = yi; + theta += *arctanptr++; + } + } + while ( ++i < GF_TRIG_MAX_ITERS ); + + /* round theta */ + if ( theta >= 0 ) + theta = GF_PAD_ROUND( theta, 32 ); + else + theta = - GF_PAD_ROUND( -theta, 32 ); + + vec->x = x; + vec->y = ANGLE_DEG_TO_RAD(theta); +} + +GF_EXPORT +Fixed gf_atan2(Fixed dy, Fixed dx) +{ + GF_Point2D v; + if ( dx == 0 && dy == 0 ) return 0; + v.x = dx; + v.y = dy; + gf_trig_prenorm( &v ); + gf_trig_pseudo_polarize( &v ); + return v.y; +} + +/*define one of these to enable IntelGPP support and link to gpp_WMMX40_d.lib (debug) or gpp_WMMX40_r.lib (release) +this is not configured by default for lack of real perf increase.*/ +#if defined(GPAC_USE_IGPP_HP) || defined(GPAC_USE_IGPP) +# pragma message("Using IntelGPP math library") +#ifdef _DEBUG +# pragma comment(lib, "gpp_WMMX40_d") +#else +# pragma comment(lib, "gpp_WMMX40_r") +#endif + +#include + +GF_EXPORT +Fixed gf_invfix(Fixed a) +{ + Fixed res; + if (!a) return (s32)0x7FFFFFFFL; + gppInv_16_32s(a, &res); + return res; +} +GF_EXPORT +Fixed gf_divfix(Fixed a, Fixed b) +{ + Fixed res; + if (!a) return 0; + else if (!b) return (a<0) ? -(s32)0x7FFFFFFFL : (s32)0x7FFFFFFFL; + else if (a==FIX_ONE) gppInv_16_32s(b, &res); + else gppDiv_16_32s(a, b, &res); + return res; +} + +GF_EXPORT +Fixed gf_mulfix(Fixed a, Fixed b) +{ + Fixed res; + if (!a || !b) return 0; + else if (b == FIX_ONE) return a; + else gppMul_16_32s(a, b, &res); + return res; +} + +GF_EXPORT +Fixed gf_sqrt(Fixed x) +{ + Fixed res; +#ifdef GPAC_USE_IGPP_HP + gppSqrtHP_16_32s(x, &res); +#else + gppSqrtLP_16_32s(x, &res); +#endif + return res; +} + +GF_EXPORT +Fixed gf_cos(Fixed angle) +{ + Fixed res; +#ifdef GPAC_USE_IGPP_HP + gppCosHP_16_32s(angle, &res); +#else + gppCosLP_16_32s(angle, &res); +#endif + return res; +} + +GF_EXPORT +Fixed gf_sin(Fixed angle) +{ + Fixed res; +#ifdef GPAC_USE_IGPP_HP + gppSinHP_16_32s (angle, &res); +#else + gppSinLP_16_32s (angle, &res); +#endif + return res; +} + + +GF_EXPORT +Fixed gf_tan(Fixed angle) +{ + Fixed cosa, sina; +#ifdef GPAC_USE_IGPP_HP + gppSinCosHP_16_32s(angle, &sina, &cosa); +#else + gppSinCosLP_16_32s(angle, &sina, &cosa); +#endif + if (!cosa) return (sina<0) ? -GF_PI2 : GF_PI2; + return gf_divfix(sina, cosa); +} + +GF_EXPORT +GF_Point2D gf_v2d_from_polar(Fixed length, Fixed angle) +{ + GF_Point2D vec; + Fixed cosa, sina; +#ifdef GPAC_USE_IGPP_HP + gppSinCosHP_16_32s(angle, &sina, &cosa); +#else + gppSinCosLP_16_32s(angle, &sina, &cosa); +#endif + vec.x = gf_mulfix(length, cosa); + vec.y = gf_mulfix(length, sina); + return vec; +} + +GF_EXPORT +Fixed gf_v2d_len(GF_Point2D *vec) +{ + Fixed a, b, res; + if (!vec->x) return ABS(vec->y); + if (!vec->y) return ABS(vec->x); + gppSquare_16_32s(vec->x, &a); + gppSquare_16_32s(vec->y, &b); +#ifdef GPAC_USE_IGPP_HP + gppSqrtHP_16_32s(a+b, &res); +#else + gppSqrtLP_16_32s(a+b, &res); +#endif + return res; +} + +#else + +GF_EXPORT +Fixed gf_invfix(Fixed a) +{ + if (!a) return (s32)0x7FFFFFFFL; + return gf_divfix(FIX_ONE, a); +} + +GF_EXPORT +Fixed gf_divfix(Fixed a, Fixed b) +{ + s32 s; + u32 q; + if (!a) return 0; + s = 1; + if ( a < 0 ) { a = -a; s = -1; } + if ( b < 0 ) { b = -b; s = -s; } + + if ( b == 0 ) + /* check for division by 0 */ + q = 0x7FFFFFFFL; + else { + /* compute result directly */ + q = (u32)( ( ( (fix_s64)a << 16 ) + ( b >> 1 ) ) / b ); + } + return ( s < 0 ? -(s32)q : (s32)q ); +} + +GF_EXPORT +Fixed gf_mulfix(Fixed a, Fixed b) +{ + Fixed s; + u32 ua, ub; + if ( !a || (b == 0x10000L)) return a; + if (!b) return 0; + + s = a; a = ABS(a); + s ^= b; b = ABS(b); + + ua = (u32)a; + ub = (u32)b; + + if ((ua <= 2048) && (ub <= 1048576L)) { + ua = ( ua * ub + 0x8000L ) >> 16; + } else { + u32 al = ua & 0xFFFFL; + ua = ( ua >> 16 ) * ub + al * ( ub >> 16 ) + ( ( al * ( ub & 0xFFFFL ) + 0x8000L ) >> 16 ); + } + if (ua & 0x80000000) s=-s; + return ( s < 0 ? -(Fixed)(s32)ua : (Fixed)ua ); +} + + +GF_EXPORT +Fixed gf_sqrt(Fixed x) +{ + u32 root, rem_hi, rem_lo, test_div; + s32 count; + root = 0; + if ( x > 0 ) { + rem_hi = 0; + rem_lo = x; + count = 24; + do { + rem_hi = ( rem_hi << 2 ) | ( rem_lo >> 30 ); + rem_lo <<= 2; + root <<= 1; + test_div = ( root << 1 ) + 1; + + if ( rem_hi >= test_div ) { + rem_hi -= test_div; + root += 1; + } + } while ( --count ); + } + + return (Fixed) root; +} + + +/* multiply a given value by the CORDIC shrink factor */ +static Fixed gf_trig_downscale(Fixed val) +{ + Fixed s; + fix_s64 v; + s = val; + val = ( val >= 0 ) ? val : -val; +#ifdef _MSC_VER + v = ( val * (fix_s64)GF_TRIG_SCALE ) + 0x100000000; +#else + v = ( val * (fix_s64)GF_TRIG_SCALE ) + 0x100000000ULL; +#endif + val = (Fixed)( v >> 32 ); + return ( s >= 0 ) ? val : -val; +} + +static void gf_trig_pseudo_rotate(GF_Point2D* vec, Fixed theta) +{ + s32 i; + Fixed x, y, xtemp; + const Fixed *arctanptr; + + /*trig funcs are in degrees*/ + theta = ANGLE_RAD_TO_DEG(theta); + + x = vec->x; + y = vec->y; + + /* Get angle between -90 and 90 degrees */ + while ( theta <= -GF_ANGLE_PI2 ) { + x = -x; + y = -y; + theta += GF_ANGLE_PI; + } + + while ( theta > GF_ANGLE_PI2 ) { + x = -x; + y = -y; + theta -= GF_ANGLE_PI; + } + + /* Initial pseudorotation, with left shift */ + arctanptr = gf_trig_arctan_table; + if ( theta < 0 ) { + xtemp = x + ( y << 1 ); + y = y - ( x << 1 ); + x = xtemp; + theta += *arctanptr++; + } else { + xtemp = x - ( y << 1 ); + y = y + ( x << 1 ); + x = xtemp; + theta -= *arctanptr++; + } + /* Subsequent pseudorotations, with right shifts */ + i = 0; + do { + if ( theta < 0 ) { + xtemp = x + ( y >> i ); + y = y - ( x >> i ); + x = xtemp; + theta += *arctanptr++; + } else { + xtemp = x - ( y >> i ); + y = y + ( x >> i ); + x = xtemp; + theta -= *arctanptr++; + } + } while ( ++i < GF_TRIG_MAX_ITERS ); + + vec->x = x; + vec->y = y; +} + +/* these macros return 0 for positive numbers, and -1 for negative ones */ +#define GF_SIGN_LONG( x ) ( (x) >> ( 32 - 1 ) ) +#define GF_SIGN_INT( x ) ( (x) >> ( 32 - 1 ) ) +#define GF_SIGN_INT32( x ) ( (x) >> 31 ) +#define GF_SIGN_INT16( x ) ( (x) >> 15 ) + +static void gf_v2d_rotate(GF_Point2D *vec, Fixed angle) +{ + s32 shift; + GF_Point2D v; + + v.x = vec->x; + v.y = vec->y; + + if ( angle && ( v.x != 0 || v.y != 0 ) ) { + shift = gf_trig_prenorm( &v ); + gf_trig_pseudo_rotate( &v, angle ); + v.x = gf_trig_downscale( v.x ); + v.y = gf_trig_downscale( v.y ); + + if ( shift > 0 ) { + s32 half = 1L << ( shift - 1 ); + + vec->x = ( v.x + half + GF_SIGN_LONG( v.x ) ) >> shift; + vec->y = ( v.y + half + GF_SIGN_LONG( v.y ) ) >> shift; + } else { + shift = -shift; + vec->x = v.x << shift; + vec->y = v.y << shift; + } + } +} + +GF_EXPORT +Fixed gf_v2d_len(GF_Point2D *vec) +{ + s32 shift; + GF_Point2D v; + v = *vec; + + /* handle trivial cases */ + if ( v.x == 0 ) { + return ( v.y >= 0 ) ? v.y : -v.y; + } else if ( v.y == 0 ) { + return ( v.x >= 0 ) ? v.x : -v.x; + } + + /* general case */ + shift = gf_trig_prenorm( &v ); + gf_trig_pseudo_polarize( &v ); + v.x = gf_trig_downscale( v.x ); + if ( shift > 0 ) + return ( v.x + ( 1 << ( shift - 1 ) ) ) >> shift; + + return v.x << -shift; +} + + +GF_EXPORT +void gf_v2d_polarize(GF_Point2D *vec, Fixed *length, Fixed *angle) +{ + s32 shift; + GF_Point2D v; + v = *vec; + if ( v.x == 0 && v.y == 0 ) + return; + + shift = gf_trig_prenorm( &v ); + gf_trig_pseudo_polarize( &v ); + v.x = gf_trig_downscale( v.x ); + *length = ( shift >= 0 ) ? ( v.x >> shift ) : ( v.x << -shift ); + *angle = v.y; +} + +GF_EXPORT +GF_Point2D gf_v2d_from_polar(Fixed length, Fixed angle) +{ + GF_Point2D vec; + vec.x = length; + vec.y = 0; + gf_v2d_rotate(&vec, angle); + return vec; +} + +GF_EXPORT +Fixed gf_cos(Fixed angle) +{ + GF_Point2D v; + v.x = GF_TRIG_COSCALE >> 2; + v.y = 0; + gf_trig_pseudo_rotate( &v, angle ); + return v.x / ( 1 << 12 ); +} +GF_EXPORT +Fixed gf_sin(Fixed angle) +{ + return gf_cos( GF_PI2 - angle ); +} +GF_EXPORT +Fixed gf_tan(Fixed angle) +{ + GF_Point2D v; + v.x = GF_TRIG_COSCALE >> 2; + v.y = 0; + gf_trig_pseudo_rotate( &v, angle ); + return gf_divfix( v.y, v.x ); +} + +#endif + +/*common parts*/ +GF_EXPORT +Fixed gf_muldiv(Fixed a, Fixed b, Fixed c) +{ + s32 s, d; + if (!b || !a) return 0; + s = 1; + if ( a < 0 ) { a = -a; s = -1; } + if ( b < 0 ) { b = -b; s = -s; } + if ( c < 0 ) { c = -c; s = -s; } + + d = (s32)( c > 0 ? ( (fix_s64)a * b + ( c >> 1 ) ) / c : 0x7FFFFFFFL); + return (Fixed) (( s > 0 ) ? d : -d); +} + + +GF_EXPORT +Fixed gf_ceil(Fixed a) +{ + return (a >= 0) ? (a + 0xFFFFL) & ~0xFFFFL : -((-a + 0xFFFFL) & ~0xFFFFL); +} + +GF_EXPORT +Fixed gf_floor(Fixed a) +{ + return (a >= 0) ? (a & ~0xFFFFL) : -((-a) & ~0xFFFFL); +} + + +/*FIXME*/ +GF_EXPORT +Fixed gf_acos(Fixed angle) +{ + return FLT2FIX( (Float) acos(FIX2FLT(angle))); +} + +/*FIXME*/ +GF_EXPORT +Fixed gf_asin(Fixed angle) +{ + return FLT2FIX( (Float) asin(FIX2FLT(angle))); +} + + +#else + + +GF_EXPORT +GF_Point2D gf_v2d_from_polar(Fixed length, Fixed angle) +{ + GF_Point2D vec; + vec.x = length*(Float) cos(angle); + vec.y = length*(Float) sin(angle); + return vec; +} + +GF_EXPORT +Fixed gf_v2d_len(GF_Point2D *vec) +{ + if (!vec->x) return ABS(vec->y); + if (!vec->y) return ABS(vec->x); + return (Fixed) sqrt(vec->x*vec->x + vec->y*vec->y); +} + +/* + +Fixed gf_cos(_a) { return (Float) cos(_a); } +Fixed gf_sin(_a) { return (Float) sin(_a); } +Fixed gf_tan(_a) { return (Float) tan(_a); } +Fixed gf_atan2(_y, _x) { return (Float) atan2(_y, _x); } +Fixed gf_sqrt(_x) { return (Float) sqrt(_x); } +Fixed gf_ceil(_a) { return (Float) ceil(_a); } +Fixed gf_floor(_a) { return (Float) floor(_a); } +Fixed gf_acos(_a) { return (Float) acos(_a); } +Fixed gf_asin(_a) { return (Float) asin(_a); } + +*/ + +#endif + + +GF_EXPORT +Fixed gf_angle_diff(Fixed angle1, Fixed angle2) +{ + Fixed delta = angle2 - angle1; +#ifdef GPAC_FIXED_POINT + delta %= GF_2PI; + if (delta < 0) delta += GF_2PI; + if (delta > GF_PI) delta -= GF_2PI; +#else + while (delta < 0) delta += GF_2PI; + while (delta > GF_PI) delta -= GF_2PI; +#endif + return delta; +} + + + +/* + 2D MATRIX TOOLS + */ + +GF_EXPORT +void gf_mx2d_add_matrix(GF_Matrix2D *_this, GF_Matrix2D *from) +{ + GF_Matrix2D bck; + if (!_this || !from) return; + + if (gf_mx2d_is_identity(*from)) return; + else if (gf_mx2d_is_identity(*_this)) { + gf_mx2d_copy(*_this, *from); + return; + } + gf_mx2d_copy(bck, *_this); + _this->m[0] = gf_mulfix(from->m[0], bck.m[0]) + gf_mulfix(from->m[1], bck.m[3]); + _this->m[1] = gf_mulfix(from->m[0], bck.m[1]) + gf_mulfix(from->m[1], bck.m[4]); + _this->m[2] = gf_mulfix(from->m[0], bck.m[2]) + gf_mulfix(from->m[1], bck.m[5]) + from->m[2]; + _this->m[3] = gf_mulfix(from->m[3], bck.m[0]) + gf_mulfix(from->m[4], bck.m[3]); + _this->m[4] = gf_mulfix(from->m[3], bck.m[1]) + gf_mulfix(from->m[4], bck.m[4]); + _this->m[5] = gf_mulfix(from->m[3], bck.m[2]) + gf_mulfix(from->m[4], bck.m[5]) + from->m[5]; +} + + +GF_EXPORT +void gf_mx2d_pre_multiply(GF_Matrix2D *_this, GF_Matrix2D *with) +{ + GF_Matrix2D bck; + if (!_this || !with) return; + + if (gf_mx2d_is_identity(*with)) return; + else if (gf_mx2d_is_identity(*_this)) { + gf_mx2d_copy(*_this, *with); + return; + } + gf_mx2d_copy(bck, *_this); + _this->m[0] = gf_mulfix(bck.m[0], with->m[0]) + gf_mulfix(bck.m[1], with->m[3]); + _this->m[1] = gf_mulfix(bck.m[0], with->m[1]) + gf_mulfix(bck.m[1], with->m[4]); + _this->m[2] = gf_mulfix(bck.m[0], with->m[2]) + gf_mulfix(bck.m[1], with->m[5]) + bck.m[2]; + _this->m[3] = gf_mulfix(bck.m[3], with->m[0]) + gf_mulfix(bck.m[4], with->m[3]); + _this->m[4] = gf_mulfix(bck.m[3], with->m[1]) + gf_mulfix(bck.m[4], with->m[4]); + _this->m[5] = gf_mulfix(bck.m[3], with->m[2]) + gf_mulfix(bck.m[4], with->m[5]) + bck.m[5]; +} + + +GF_EXPORT +void gf_mx2d_add_translation(GF_Matrix2D *_this, Fixed cx, Fixed cy) +{ + GF_Matrix2D tmp; + if (!_this || (!cx && !cy) ) return; + gf_mx2d_init(tmp); + tmp.m[2] = cx; + tmp.m[5] = cy; + gf_mx2d_add_matrix(_this, &tmp); +} + + +GF_EXPORT +void gf_mx2d_add_rotation(GF_Matrix2D *_this, Fixed cx, Fixed cy, Fixed angle) +{ + GF_Matrix2D tmp; + if (!_this) return; + gf_mx2d_init(tmp); + + gf_mx2d_add_translation(_this, -cx, -cy); + + tmp.m[0] = gf_cos(angle); + tmp.m[4] = tmp.m[0]; + tmp.m[3] = gf_sin(angle); + tmp.m[1] = -1 * tmp.m[3]; + gf_mx2d_add_matrix(_this, &tmp); + gf_mx2d_add_translation(_this, cx, cy); +} + +GF_EXPORT +void gf_mx2d_add_scale(GF_Matrix2D *_this, Fixed scale_x, Fixed scale_y) +{ + GF_Matrix2D tmp; + if (!_this || ((scale_x==FIX_ONE) && (scale_y==FIX_ONE)) ) return; + gf_mx2d_init(tmp); + tmp.m[0] = scale_x; + tmp.m[4] = scale_y; + gf_mx2d_add_matrix(_this, &tmp); +} + +GF_EXPORT +void gf_mx2d_add_scale_at(GF_Matrix2D *_this, Fixed scale_x, Fixed scale_y, Fixed cx, Fixed cy, Fixed angle) +{ + GF_Matrix2D tmp; + if (!_this) return; + gf_mx2d_init(tmp); + if (angle) { + gf_mx2d_add_rotation(_this, cx, cy, -angle); + } + tmp.m[0] = scale_x; + tmp.m[4] = scale_y; + gf_mx2d_add_matrix(_this, &tmp); + if (angle) gf_mx2d_add_rotation(_this, cx, cy, angle); +} + +GF_EXPORT +void gf_mx2d_add_skew(GF_Matrix2D *_this, Fixed skew_x, Fixed skew_y) +{ + GF_Matrix2D tmp; + if (!_this || (!skew_x && !skew_y) ) return; + gf_mx2d_init(tmp); + tmp.m[1] = skew_x; + tmp.m[3] = skew_y; + gf_mx2d_add_matrix(_this, &tmp); +} + +GF_EXPORT +void gf_mx2d_add_skew_x(GF_Matrix2D *_this, Fixed angle) +{ + GF_Matrix2D tmp; + if (!_this) return; + gf_mx2d_init(tmp); + tmp.m[1] = gf_tan(angle); + tmp.m[3] = 0; + gf_mx2d_add_matrix(_this, &tmp); +} + +GF_EXPORT +void gf_mx2d_add_skew_y(GF_Matrix2D *_this, Fixed angle) +{ + GF_Matrix2D tmp; + if (!_this) return; + gf_mx2d_init(tmp); + tmp.m[1] = 0; + tmp.m[3] = gf_tan(angle); + gf_mx2d_add_matrix(_this, &tmp); +} + +static Fixed gf_mx2d_get_determinent(GF_Matrix2D *_this) +{ + if (_this) + return gf_mulfix(_this->m[0], _this->m[4]) - gf_mulfix(_this->m[1], _this->m[3]); + return 0; +} + + +GF_EXPORT +void gf_mx2d_inverse(GF_Matrix2D *_this) +{ + Fixed det; + GF_Matrix2D tmp; + if(!_this) return; + if (gf_mx2d_is_identity(*_this)) return; + det = gf_mx2d_get_determinent(_this); + if (!det) { + gf_mx2d_init(*_this); + return; + } + tmp.m[0] = gf_divfix(_this->m[4], det); + tmp.m[1] = -1 * gf_divfix(_this->m[1], det); + tmp.m[2] = gf_mulfix(_this->m[1], _this->m[5]) - gf_mulfix(_this->m[4], _this->m[2]); + tmp.m[2] = gf_divfix(tmp.m[2], det); + tmp.m[3] = -1 * gf_divfix(_this->m[3], det); + tmp.m[4] = gf_divfix(_this->m[0], det); + tmp.m[5] = gf_mulfix(_this->m[3], _this->m[2]) - gf_mulfix(_this->m[0],_this->m[5]); + tmp.m[5] = gf_divfix(tmp.m[5], det); + gf_mx2d_copy(*_this, tmp); +} + +Bool gf_mx2d_decompose(GF_Matrix2D *mx, GF_Point2D *scale, Fixed *rotate, GF_Point2D *translate) +{ + Fixed det, angle; + Fixed tmp[6]; + if(!mx) return 0; + + memcpy(tmp, mx->m, sizeof(Fixed)*6); + translate->x = tmp[2]; + translate->y = tmp[5]; + + /*check ac+bd=0*/ + det = gf_mulfix(tmp[0], tmp[3]) + gf_mulfix(tmp[1], tmp[4]); + if (ABS(det) > FIX_EPSILON) { + scale->x = scale->y = 0; + *rotate = 0; + return 0; + } + angle = gf_atan2(tmp[3], tmp[4]); + if (angle < FIX_EPSILON) { + scale->x = tmp[0]; + scale->y = tmp[4]; + } else { + det = gf_cos(angle); + scale->x = gf_divfix(tmp[0], det); + scale->y = gf_divfix(tmp[4], det); + } + *rotate = angle; + return 1; +} + +GF_EXPORT +void gf_mx2d_apply_coords(GF_Matrix2D *_this, Fixed *x, Fixed *y) +{ + Fixed _x, _y; + if (!_this || !x || !y) return; + _x = gf_mulfix(*x, _this->m[0]) + gf_mulfix(*y, _this->m[1]) + _this->m[2]; + _y = gf_mulfix(*x, _this->m[3]) + gf_mulfix(*y, _this->m[4]) + _this->m[5]; + *x = _x; + *y = _y; +} + +GF_EXPORT +void gf_mx2d_apply_point(GF_Matrix2D *_this, GF_Point2D *pt) +{ + gf_mx2d_apply_coords(_this, &pt->x, &pt->y); +} + +GF_EXPORT +void gf_mx2d_apply_rect(GF_Matrix2D *_this, GF_Rect *rc) +{ + GF_Point2D c1, c2, c3, c4; + c1.x = c2.x = rc->x; + c3.x = c4.x = rc->x + rc->width; + c1.y = c3.y = rc->y; + c2.y = c4.y = rc->y - rc->height; + gf_mx2d_apply_point(_this, &c1); + gf_mx2d_apply_point(_this, &c2); + gf_mx2d_apply_point(_this, &c3); + gf_mx2d_apply_point(_this, &c4); + rc->x = MIN(c1.x, MIN(c2.x, MIN(c3.x, c4.x))); + rc->width = MAX(c1.x, MAX(c2.x, MAX(c3.x, c4.x))) - rc->x; + rc->height = MIN(c1.y, MIN(c2.y, MIN(c3.y, c4.y))); + rc->y = MAX(c1.y, MAX(c2.y, MAX(c3.y, c4.y))); + rc->height = rc->y - rc->height; + assert(rc->height>=0); + assert(rc->width>=0); +} + +/* + RECTANGLE TOOLS + */ + +/*transform rect to smallest covering integer pixels rect - this is needed to make sure clearing +of screen is correctly handled, otherwise we have troubles with bitmap hardware blitting (always integer)*/ +GF_EXPORT +GF_IRect gf_rect_pixelize(GF_Rect *r) +{ + GF_IRect rc; + rc.x = FIX2INT(gf_floor(r->x)); + rc.y = FIX2INT(gf_ceil(r->y)); + rc.width = FIX2INT(gf_ceil(r->x + r->width)) - rc.x; + rc.height = rc.y - FIX2INT(gf_floor(r->y - r->height)); + return rc; +} + + +/*adds @rc2 to @rc1 - the new @rc1 contains the old @rc1 and @rc2*/ +GF_EXPORT +void gf_rect_union(GF_Rect *rc1, GF_Rect *rc2) +{ + if (!rc1->width || !rc1->height) {*rc1=*rc2; return;} + if (!rc2->width || !rc2->height) return; + if (rc2->x < rc1->x) { rc1->width += rc1->x - rc2->x; rc1->x = rc2->x; } + if (rc2->x + rc2->width > rc1->x+rc1->width) rc1->width = rc2->x + rc2->width - rc1->x; + if (rc2->y > rc1->y) { rc1->height += rc2->y - rc1->y; rc1->y = rc2->y; } + if (rc2->y - rc2->height < rc1->y - rc1->height) rc1->height = rc1->y - rc2->y + rc2->height; +} + +GF_EXPORT +GF_Rect gf_rect_center(Fixed w, Fixed h) +{ + GF_Rect rc; + rc.x=-w/2; rc.y=h/2; rc.width=w; rc.height=h; + return rc; +} + +GF_EXPORT +Bool gf_rect_overlaps(GF_Rect rc1, GF_Rect rc2) +{ + if (! rc2.height || !rc2.width || !rc1.height || !rc1.width) return 0; + if (rc2.x+rc2.width<=rc1.x) return 0; + if (rc2.x>=rc1.x+rc1.width) return 0; + if (rc2.y-rc2.height>=rc1.y) return 0; + if (rc2.y<=rc1.y-rc1.height) return 0; + return 1; +} + +GF_EXPORT +Bool gf_rect_equal(GF_Rect rc1, GF_Rect rc2) +{ + if ( (rc1.x == rc2.x) && (rc1.y == rc2.y) && (rc1.width == rc2.width) && (rc1.height == rc2.height) ) + return 1; + return 0; +} + +#ifdef GPAC_FIXED_POINT + +/* check if dimension is larger than FIX_ONE*/ +#define IS_HIGH_DIM(_v) ((_v > FIX_ONE) || (_v < (s32)0xFFFF0000)) +/* check if any vector dimension is larger than FIX_ONE*/ +#define VEC_HIGH_MAG(_v) (IS_HIGH_DIM(_v.x) || IS_HIGH_DIM(_v.y) || IS_HIGH_DIM(_v.z) ) + +//#define FLOAT_COMPUTE + +GF_EXPORT +Fixed gf_vec_len(GF_Vec v) +{ + /*commented out atm - weird results (not enough precision?)...*/ +//#if defined(GPAC_USE_IGPP_HP) || defined (GPAC_USE_IGPP) +#if 0 + Fixed res; + gppVec3DLength_16_32s((GPP_VEC3D *) &v, &res); + return res; +#elif defined(FLOAT_COMPUTE) + return FLT2FIX( sqrt( FIX2FLT(v.x) * FIX2FLT(v.x) + FIX2FLT(v.y)*FIX2FLT(v.y) + FIX2FLT(v.z)*FIX2FLT(v.z) ) ); +#else + + /*high-magnitude vector, use low precision on frac part to avoid overflow*/ + if (VEC_HIGH_MAG(v)) { + v.x>>=8; + v.y>>=8; + v.z>>=8; + return gf_sqrt( gf_mulfix(v.x, v.x) + gf_mulfix(v.y, v.y) + gf_mulfix(v.z, v.z) ) << 8; + } + /*low-res vector*/ + return gf_sqrt( gf_mulfix(v.x, v.x) + gf_mulfix(v.y, v.y) + gf_mulfix(v.z, v.z) ); +#endif +} + +GF_EXPORT +Fixed gf_vec_lensq(GF_Vec v) +{ + /*commented out atm - weird results (not enough precision?)...*/ +//#if defined(GPAC_USE_IGPP_HP) || defined (GPAC_USE_IGPP) +#if 0 + Fixed res; + gppVec3DLengthSq_16_32s((GPP_VEC3D *) &v, &res); + return res; +#elif defined(FLOAT_COMPUTE) + return FLT2FIX( FIX2FLT(v.x) * FIX2FLT(v.x) + FIX2FLT(v.y)*FIX2FLT(v.y) + FIX2FLT(v.z)*FIX2FLT(v.z) ); +#else + + /*high-magnitude vector, use low precision on frac part to avoid overflow*/ + if (VEC_HIGH_MAG(v)) { + v.x>>=8; + v.y>>=8; + v.z>>=8; + return ( gf_mulfix(v.x, v.x) + gf_mulfix(v.y, v.y) + gf_mulfix(v.z, v.z) ) << 16; + } + return gf_mulfix(v.x, v.x) + gf_mulfix(v.y, v.y) + gf_mulfix(v.z, v.z); + +#endif +} + +GF_EXPORT +Fixed gf_vec_dot(GF_Vec v1, GF_Vec v2) +{ + /*commented out atm - weird results (not enough precision?)...*/ +//#if defined(GPAC_USE_IGPP_HP) || defined (GPAC_USE_IGPP) +#if 0 + Fixed res; + gppVec3DDot_16_32s((GPP_VEC3D *) &v1, (GPP_VEC3D *) &v2, &res); + return res; +#elif defined(FLOAT_COMPUTE) + Float fr = FIX2FLT(v1.x)*FIX2FLT(v2.x) + FIX2FLT(v1.y)*FIX2FLT(v2.y) + FIX2FLT(v1.z)*FIX2FLT(v2.z); + return FLT2FIX(fr); +#else + /*both high-magnitude vectors, use low precision on frac part to avoid overflow + if only one is, the dot product should still be in proper range*/ + if (0&&VEC_HIGH_MAG(v1) && VEC_HIGH_MAG(v2)) { + v1.x>>=4; v1.y>>=4; v1.z>>=4; + v2.x>>=4; v2.y>>=4; v2.z>>=4; + return ( gf_mulfix(v1.x, v2.x) + gf_mulfix(v1.y, v2.y) + gf_mulfix(v1.z, v2.z) ) << 8; + } + return gf_mulfix(v1.x, v2.x) + gf_mulfix(v1.y, v2.y) + gf_mulfix(v1.z, v2.z); + +#endif +} + +GF_EXPORT +void gf_vec_norm(GF_Vec *v) +{ + /*commented out atm - weird results (not enough precision?)...*/ +//#if defined(GPAC_USE_IGPP_HP) || defined (GPAC_USE_IGPP) +#if 0 + gppVec3DNormalize_16_32s((GPP_VEC3D *) &v); +#else + Fixed __res = gf_vec_len(*v); + v->x = gf_divfix(v->x, __res); + v->y = gf_divfix(v->y, __res); + v->z = gf_divfix(v->z, __res); +#endif +} + +GF_EXPORT +GF_Vec gf_vec_scale(GF_Vec v, Fixed f) +{ + GF_Vec res; + res.x = gf_mulfix(v.x, f); + res.y = gf_mulfix(v.y, f); + res.z = gf_mulfix(v.z, f); + return res; +} + +GF_EXPORT +GF_Vec gf_vec_cross(GF_Vec v1, GF_Vec v2) +{ + GF_Vec res; + /*commented out atm - weird results (not enough precision?)...*/ +//#if defined(GPAC_USE_IGPP_HP) || defined (GPAC_USE_IGPP) +#if 0 + gppVec3DCross_16_32s((GPP_VEC3D *) &v1, (GPP_VEC3D *) &v2, (GPP_VEC3D *) &res); + return res; +#elif defined(FLOAT_COMPUTE) + res.x = FLT2FIX( FIX2FLT(v1.y)*FIX2FLT(v2.z) - FIX2FLT(v2.y)*FIX2FLT(v1.z)); + res.y = FLT2FIX( FIX2FLT(v2.x)*FIX2FLT(v1.z) - FIX2FLT(v1.x)*FIX2FLT(v2.z)); + res.z = FLT2FIX( FIX2FLT(v1.x)*FIX2FLT(v2.y) - FIX2FLT(v2.x)*FIX2FLT(v1.y)); + return res; +#else + + /*both high-magnitude vectors, use low precision on frac part to avoid overflow + if only one is, the cross product should still be in proper range*/ + if (VEC_HIGH_MAG(v1) && VEC_HIGH_MAG(v2)) { + v1.x>>=8; v1.y>>=8; v1.z>>=8; + v2.x>>=8; v2.y>>=8; v2.z>>=8; + res.x = gf_mulfix(v1.y, v2.z) - gf_mulfix(v2.y, v1.z); + res.y = gf_mulfix(v2.x, v1.z) - gf_mulfix(v1.x, v2.z); + res.z = gf_mulfix(v1.x, v2.y) - gf_mulfix(v2.x, v1.y); + res.x<<=16; + res.y<<=16; + res.z<<=16; + return res; + } + res.x = gf_mulfix(v1.y, v2.z) - gf_mulfix(v2.y, v1.z); + res.y = gf_mulfix(v2.x, v1.z) - gf_mulfix(v1.x, v2.z); + res.z = gf_mulfix(v1.x, v2.y) - gf_mulfix(v2.x, v1.y); + +#endif + return res; +} + +#else + +GF_EXPORT +Fixed gf_vec_len(GF_Vec v) { return gf_sqrt(v.x*v.x + v.y*v.y + v.z*v.z); } +GF_EXPORT +Fixed gf_vec_lensq(GF_Vec v) { return v.x*v.x + v.y*v.y + v.z*v.z; } +GF_EXPORT +Fixed gf_vec_dot(GF_Vec v1, GF_Vec v2) { return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; } +GF_EXPORT +void gf_vec_norm(GF_Vec *v) +{ + Fixed __res = gf_vec_len(*v); + if (__res ) __res = 1.0f/__res ; + v->x *= __res; + v->y *= __res; + v->z *= __res; +} + +GF_EXPORT +GF_Vec gf_vec_scale(GF_Vec v, Fixed f) +{ + GF_Vec res = v; + res.x *= f; + res.y *= f; + res.z *= f; + return res; +} + +GF_EXPORT +GF_Vec gf_vec_cross(GF_Vec v1, GF_Vec v2) +{ + GF_Vec res; + res.x = v1.y*v2.z - v2.y*v1.z; + res.y = v2.x*v1.z - v1.x*v2.z; + res.z = v1.x*v2.y - v2.x*v1.y; + return res; +} + +#endif + + +GF_EXPORT +void gf_mx2d_from_mx(GF_Matrix2D *mat2D, GF_Matrix *mat) +{ + gf_mx2d_init(*mat2D); + mat2D->m[0] = mat->m[0]; + mat2D->m[1] = mat->m[4]; + mat2D->m[2] = mat->m[12]; + mat2D->m[3] = mat->m[1]; + mat2D->m[4] = mat->m[5]; + mat2D->m[5] = mat->m[13]; +} + +GF_EXPORT +void gf_mx_apply_rect(GF_Matrix *mat, GF_Rect *rc) +{ + GF_Matrix2D mat2D; + gf_mx2d_from_mx(&mat2D, mat); + gf_mx2d_apply_rect(&mat2D, rc); +} + +GF_EXPORT +void gf_mx_add_matrix(GF_Matrix *mat, GF_Matrix *mul) +{ + GF_Matrix tmp; + gf_mx_init(tmp); + + tmp.m[0] = gf_mulfix(mat->m[0],mul->m[0]) + gf_mulfix(mat->m[4],mul->m[1]) + gf_mulfix(mat->m[8],mul->m[2]); + tmp.m[4] = gf_mulfix(mat->m[0],mul->m[4]) + gf_mulfix(mat->m[4],mul->m[5]) + gf_mulfix(mat->m[8],mul->m[6]); + tmp.m[8] = gf_mulfix(mat->m[0],mul->m[8]) + gf_mulfix(mat->m[4],mul->m[9]) + gf_mulfix(mat->m[8],mul->m[10]); + tmp.m[12]= gf_mulfix(mat->m[0],mul->m[12]) + gf_mulfix(mat->m[4],mul->m[13]) + gf_mulfix(mat->m[8],mul->m[14]) + mat->m[12]; + tmp.m[1] = gf_mulfix(mat->m[1],mul->m[0]) + gf_mulfix(mat->m[5],mul->m[1]) + gf_mulfix(mat->m[9],mul->m[2]); + tmp.m[5] = gf_mulfix(mat->m[1],mul->m[4]) + gf_mulfix(mat->m[5],mul->m[5]) + gf_mulfix(mat->m[9],mul->m[6]); + tmp.m[9] = gf_mulfix(mat->m[1],mul->m[8]) + gf_mulfix(mat->m[5],mul->m[9]) + gf_mulfix(mat->m[9],mul->m[10]); + tmp.m[13]= gf_mulfix(mat->m[1],mul->m[12]) + gf_mulfix(mat->m[5],mul->m[13]) + gf_mulfix(mat->m[9],mul->m[14]) + mat->m[13]; + tmp.m[2] = gf_mulfix(mat->m[2],mul->m[0]) + gf_mulfix(mat->m[6],mul->m[1]) + gf_mulfix(mat->m[10],mul->m[2]); + tmp.m[6] = gf_mulfix(mat->m[2],mul->m[4]) + gf_mulfix(mat->m[6],mul->m[5]) + gf_mulfix(mat->m[10],mul->m[6]); + tmp.m[10]= gf_mulfix(mat->m[2],mul->m[8]) + gf_mulfix(mat->m[6],mul->m[9]) + gf_mulfix(mat->m[10],mul->m[10]); + tmp.m[14]= gf_mulfix(mat->m[2],mul->m[12]) + gf_mulfix(mat->m[6],mul->m[13]) + gf_mulfix(mat->m[10],mul->m[14]) + mat->m[14]; + memcpy(mat->m, tmp.m, sizeof(Fixed)*16); +} + +GF_EXPORT +void gf_mx_add_matrix_2d(GF_Matrix *mat, GF_Matrix2D *mat2D) +{ + GF_Matrix tmp; + gf_mx_init(tmp); + + tmp.m[0] = gf_mulfix(mat->m[0],mat2D->m[0]) + gf_mulfix(mat->m[4],mat2D->m[3]); + tmp.m[4] = gf_mulfix(mat->m[0],mat2D->m[1]) + gf_mulfix(mat->m[4],mat2D->m[4]); + tmp.m[8] = mat->m[8]; + tmp.m[12]= gf_mulfix(mat->m[0],mat2D->m[2]) + gf_mulfix(mat->m[4],mat2D->m[5]) + mat->m[12]; + tmp.m[1] = gf_mulfix(mat->m[1],mat2D->m[0]) + gf_mulfix(mat->m[5],mat2D->m[3]); + tmp.m[5] = gf_mulfix(mat->m[1],mat2D->m[1]) + gf_mulfix(mat->m[5],mat2D->m[4]); + tmp.m[9] = mat->m[9]; + tmp.m[13]= gf_mulfix(mat->m[1],mat2D->m[2]) + gf_mulfix(mat->m[5],mat2D->m[5]) + mat->m[13]; + tmp.m[2] = gf_mulfix(mat->m[2],mat2D->m[0]) + gf_mulfix(mat->m[6],mat2D->m[3]); + tmp.m[6] = gf_mulfix(mat->m[2],mat2D->m[1]) + gf_mulfix(mat->m[6],mat2D->m[4]); + tmp.m[10]= mat->m[10]; + tmp.m[14]= gf_mulfix(mat->m[2],mat2D->m[2]) + gf_mulfix(mat->m[6],mat2D->m[5]) + mat->m[14]; + memcpy(mat->m, tmp.m, sizeof(Fixed)*16); +} + + + +GF_EXPORT +void gf_mx_add_translation(GF_Matrix *mat, Fixed tx, Fixed ty, Fixed tz) +{ + Fixed tmp[3]; + u32 i; + tmp[0] = mat->m[12]; + tmp[1] = mat->m[13]; + tmp[2] = mat->m[14]; + for (i=0; i<3; i++) + tmp[i] += (gf_mulfix(tx,mat->m[i]) + gf_mulfix(ty,mat->m[i+4]) + gf_mulfix(tz, mat->m[i + 8])); + mat->m[12] = tmp[0]; + mat->m[13] = tmp[1]; + mat->m[14] = tmp[2]; +} + +GF_EXPORT +void gf_mx_add_scale(GF_Matrix *mat, Fixed sx, Fixed sy, Fixed sz) +{ + Fixed tmp[3]; + u32 i, j; + + tmp[0] = sx; + tmp[1] = sy; + tmp[2] = sz; + + for (i=0; i<3; i++) { + for (j=0; j<3; j++) { + mat->m[i*4 + j] = gf_mulfix(mat->m[j+4 * i], tmp[i]); + } + } +} + +GF_EXPORT +void gf_mx_add_rotation(GF_Matrix *mat, Fixed angle, Fixed x, Fixed y, Fixed z) +{ + GF_Matrix tmp; + Fixed xx, yy, zz, xy, xz, yz; + Fixed nor = gf_sqrt(gf_mulfix(x,x) + gf_mulfix(y,y) + gf_mulfix(z,z)); + Fixed cos_a = gf_cos(angle); + Fixed sin_a = gf_sin(angle); + Fixed icos_a = FIX_ONE - cos_a; + + if (nor && (nor!=FIX_ONE)) { + x = gf_divfix(x, nor); + y = gf_divfix(y, nor); + z = gf_divfix(z, nor); + } + xx = gf_mulfix(x,x); yy = gf_mulfix(y,y); zz = gf_mulfix(z,z); xy = gf_mulfix(x,y); xz = gf_mulfix(x,z); yz = gf_mulfix(y,z); + gf_mx_init(tmp); + tmp.m[0] = gf_mulfix(icos_a,xx) + cos_a; + tmp.m[1] = gf_mulfix(xy,icos_a) + gf_mulfix(z,sin_a); + tmp.m[2] = gf_mulfix(xz,icos_a) - gf_mulfix(y,sin_a); + + tmp.m[4] = gf_mulfix(xy,icos_a) - gf_mulfix(z,sin_a); + tmp.m[5] = gf_mulfix(icos_a,yy) + cos_a; + tmp.m[6] = gf_mulfix(yz,icos_a) + gf_mulfix(x,sin_a); + + tmp.m[8] = gf_mulfix(xz,icos_a) + gf_mulfix(y,sin_a); + tmp.m[9] = gf_mulfix(yz,icos_a) - gf_mulfix(x,sin_a); + tmp.m[10]= gf_mulfix(icos_a,zz) + cos_a; + + gf_mx_add_matrix(mat, &tmp); +} + +GF_EXPORT +void gf_mx_from_mx2d(GF_Matrix *mat, GF_Matrix2D *mat2D) +{ + gf_mx_init(*mat); + mat->m[0] = mat2D->m[0]; + mat->m[4] = mat2D->m[1]; + mat->m[12] = mat2D->m[2]; + mat->m[1] = mat2D->m[3]; + mat->m[5] = mat2D->m[4]; + mat->m[13] = mat2D->m[5]; +} + +GF_EXPORT +Bool gf_mx_equal(GF_Matrix *mx1, GF_Matrix *mx2) +{ + if (mx1->m[0] != mx2->m[0]) return 0; + if (mx1->m[1] != mx2->m[1]) return 0; + if (mx1->m[2] != mx2->m[2]) return 0; + if (mx1->m[4] != mx2->m[4]) return 0; + if (mx1->m[5] != mx2->m[5]) return 0; + if (mx1->m[6] != mx2->m[6]) return 0; + if (mx1->m[8] != mx2->m[8]) return 0; + if (mx1->m[9] != mx2->m[9]) return 0; + if (mx1->m[10] != mx2->m[10]) return 0; + if (mx1->m[12] != mx2->m[12]) return 0; + if (mx1->m[13] != mx2->m[13]) return 0; + if (mx1->m[14] != mx2->m[14]) return 0; + return 1; +} + + +GF_EXPORT +void gf_mx_inverse(GF_Matrix *mx) +{ + Fixed det; + GF_Matrix rev; + gf_mx_init(rev); + + assert(! ((mx->m[3] != 0) || (mx->m[7] != 0) || (mx->m[11] != 0) || (mx->m[15] != FIX_ONE)) ); + + det = gf_mulfix(gf_mulfix(mx->m[0], mx->m[5]) , mx->m[10]); + det += gf_mulfix(gf_mulfix(mx->m[1], mx->m[6]) , mx->m[8]); + det += gf_mulfix(gf_mulfix(mx->m[2], mx->m[4]) , mx->m[9]); + det -= gf_mulfix(gf_mulfix(mx->m[2], mx->m[5]) , mx->m[8]); + det -= gf_mulfix(gf_mulfix(mx->m[1], mx->m[4]) , mx->m[10]); + det -= gf_mulfix(gf_mulfix(mx->m[0], mx->m[6]) , mx->m[9]); + +// if (gf_mulfix(det) < FIX_EPSILON) return; + + /* Calculate inverse(A) = adj(A) / det(A) */ + rev.m[0] = gf_muldiv(mx->m[5], mx->m[10], det) - gf_muldiv(mx->m[6], mx->m[9], det); + rev.m[4] = -gf_muldiv(mx->m[4], mx->m[10], det) + gf_muldiv(mx->m[6], mx->m[8], det); + rev.m[8] = gf_muldiv(mx->m[4], mx->m[9], det) - gf_muldiv(mx->m[5], mx->m[8], det); + rev.m[1] = -gf_muldiv(mx->m[1], mx->m[10], det) + gf_muldiv(mx->m[2], mx->m[9], det); + rev.m[5] = gf_muldiv(mx->m[0], mx->m[10], det) - gf_muldiv(mx->m[2], mx->m[8], det); + rev.m[9] = -gf_muldiv(mx->m[0], mx->m[9], det) + gf_muldiv(mx->m[1], mx->m[8], det); + rev.m[2] = gf_muldiv(mx->m[1], mx->m[6], det) - gf_muldiv(mx->m[2], mx->m[5], det); + rev.m[6] = -gf_muldiv(mx->m[0], mx->m[6], det) + gf_muldiv(mx->m[2], mx->m[4], det); + rev.m[10] = gf_muldiv(mx->m[0], mx->m[5], det) - gf_muldiv(mx->m[1], mx->m[4], det); + + /* do translation part*/ + rev.m[12] = -( gf_mulfix(mx->m[12], rev.m[0]) + gf_mulfix(mx->m[13], rev.m[4]) + gf_mulfix(mx->m[14], rev.m[8]) ); + rev.m[13] = -( gf_mulfix(mx->m[12], rev.m[1]) + gf_mulfix(mx->m[13], rev.m[5]) + gf_mulfix(mx->m[14], rev.m[9]) ); + rev.m[14] = -( gf_mulfix(mx->m[12], rev.m[2]) + gf_mulfix(mx->m[13], rev.m[6]) + gf_mulfix(mx->m[14], rev.m[10]) ); + gf_mx_copy(*mx, rev); +} + + +GF_EXPORT +void gf_mx_apply_vec(GF_Matrix *mx, GF_Vec *pt) +{ + GF_Vec res; + res.x = gf_mulfix(pt->x, mx->m[0]) + gf_mulfix(pt->y, mx->m[4]) + gf_mulfix(pt->z, mx->m[8]) + mx->m[12]; + res.y = gf_mulfix(pt->x, mx->m[1]) + gf_mulfix(pt->y, mx->m[5]) + gf_mulfix(pt->z, mx->m[9]) + mx->m[13]; + res.z = gf_mulfix(pt->x, mx->m[2]) + gf_mulfix(pt->y, mx->m[6]) + gf_mulfix(pt->z, mx->m[10]) + mx->m[14]; + *pt = res; +} + +GF_EXPORT +void gf_mx_ortho(GF_Matrix *mx, Fixed left, Fixed right, Fixed bottom, Fixed top, Fixed z_near, Fixed z_far) +{ + gf_mx_init(*mx); + mx->m[0] = gf_divfix(2*FIX_ONE, right-left); + mx->m[5] = gf_divfix(2*FIX_ONE, top-bottom); + mx->m[10] = gf_divfix(-2*FIX_ONE, z_far-z_near); + mx->m[12] = gf_divfix(right+left, right-left); + mx->m[13] = gf_divfix(top+bottom, top-bottom); + mx->m[14] = gf_divfix(z_far+z_near, z_far-z_near); + mx->m[15] = FIX_ONE; +} + +GF_EXPORT +void gf_mx_perspective(GF_Matrix *mx, Fixed fieldOfView, Fixed aspectRatio, Fixed z_near, Fixed z_far) +{ + Fixed f = gf_divfix(gf_cos(fieldOfView/2), gf_sin(fieldOfView/2)); + gf_mx_init(*mx); + mx->m[0] = gf_divfix(f, aspectRatio); + mx->m[5] = f; + mx->m[10] = gf_divfix(z_far+z_near, z_near-z_far); + + mx->m[11] = -FIX_ONE; + mx->m[14] = 2*gf_muldiv(z_near, z_far, z_near-z_far); + + mx->m[15] = 0; +} + +GF_EXPORT +void gf_mx_lookat(GF_Matrix *mx, GF_Vec eye, GF_Vec center, GF_Vec upVector) +{ + GF_Vec f, s, u; + + gf_vec_diff(f, center, eye); + gf_vec_norm(&f); + gf_vec_norm(&upVector); + + s = gf_vec_cross(f, upVector); + u = gf_vec_cross(s, f); + gf_mx_init(*mx); + + mx->m[0] = s.x; + mx->m[1] = u.x; + mx->m[2] = -f.x; + mx->m[4] = s.y; + mx->m[5] = u.y; + mx->m[6] = -f.y; + mx->m[8] = s.z; + mx->m[9] = u.z; + mx->m[10] = -f.z; + + gf_mx_add_translation(mx, -eye.x, -eye.y, -eye.z); +} + +GF_Vec4 gf_quat_from_matrix(GF_Matrix *mx); + +GF_EXPORT +void gf_mx_decompose(GF_Matrix *mx, GF_Vec *translate, GF_Vec *scale, GF_Vec4 *rotate, GF_Vec *shear) +{ + u32 i, j; + GF_Vec4 quat; + Fixed locmat[16]; + GF_Matrix tmp; + GF_Vec row0, row1, row2; + Fixed shear_xy, shear_xz, shear_yz; + assert(mx->m[15]); + + memcpy(locmat, mx->m, sizeof(Fixed)*16); + /*no perspective*/ + locmat[3] = locmat[7] = locmat[11] = 0; + /*normalize*/ + for (i=0; i<4; i++) { + for (j=0; j<4; j++) { + locmat[4*i+j] = gf_divfix(locmat[4*i+j], locmat[15]); + } + } + translate->x = locmat[12]; + translate->y = locmat[13]; + translate->z = locmat[14]; + locmat[12] = locmat[13] = locmat[14] = 0; + row0.x = locmat[0]; row0.y = locmat[1]; row0.z = locmat[2]; + row1.x = locmat[4]; row1.y = locmat[5]; row1.z = locmat[6]; + row2.x = locmat[8]; row2.y = locmat[9]; row2.z = locmat[10]; + + scale->x = gf_vec_len(row0); + gf_vec_norm(&row0); + shear_xy = gf_vec_dot(row0, row1); + row1.x -= gf_mulfix(row0.x, shear_xy); + row1.y -= gf_mulfix(row0.y, shear_xy); + row1.z -= gf_mulfix(row0.z, shear_xy); + + scale->y = gf_vec_len(row1); + gf_vec_norm(&row1); + shear->x = gf_divfix(shear_xy, scale->y); + + shear_xz = gf_vec_dot(row0, row2); + row2.x -= gf_mulfix(row0.x, shear_xz); + row2.y -= gf_mulfix(row0.y, shear_xz); + row2.z -= gf_mulfix(row0.z, shear_xz); + shear_yz = gf_vec_dot(row1, row2); + row2.x -= gf_mulfix(row1.x, shear_yz); + row2.y -= gf_mulfix(row1.y, shear_yz); + row2.z -= gf_mulfix(row1.z, shear_yz); + + scale->z = gf_vec_len(row2); + gf_vec_norm(&row2); + shear->y = gf_divfix(shear_xz, scale->z); + shear->z = gf_divfix(shear_yz, scale->z); + + locmat[0] = row0.x; locmat[4] = row1.x; locmat[8] = row2.x; + locmat[1] = row0.y; locmat[5] = row1.y; locmat[9] = row2.y; + locmat[2] = row0.z; locmat[6] = row1.z; locmat[10] = row2.z; + + memcpy(tmp.m, locmat, sizeof(Fixed)*16); + quat = gf_quat_from_matrix(&tmp); + *rotate = gf_quat_to_rotation(&quat); +} + +GF_EXPORT +void gf_mx_apply_bbox_sphere(GF_Matrix *mx, GF_BBox *box) +{ + Fixed var; + gf_mx_apply_vec(mx, &box->min_edge); + gf_mx_apply_vec(mx, &box->max_edge); + + if (box->min_edge.x > box->max_edge.x) + { + var = box->min_edge.x; box->min_edge.x = box->max_edge.x; box->max_edge.x = var; + } + if (box->min_edge.y > box->max_edge.y) + { + var = box->min_edge.y; box->min_edge.y = box->max_edge.y; box->max_edge.y = var; + } + if (box->min_edge.z > box->max_edge.z) + { + var = box->min_edge.z; box->min_edge.z = box->max_edge.z; box->max_edge.z = var; + } + gf_bbox_refresh(box); +} + +GF_EXPORT +void gf_mx_apply_bbox(GF_Matrix *mx, GF_BBox *box) +{ + u32 i; + GF_Vec v[4]; + v[0] = box->min_edge; + v[1] = box->min_edge; v[1].x = box->max_edge.x; + v[2] = box->min_edge; v[2].y = box->max_edge.y; + v[3] = box->min_edge; v[3].z = box->max_edge.z; + box->max_edge.x = box->max_edge.y = box->max_edge.z = -FIX_MAX; + box->min_edge.x = box->min_edge.y = box->min_edge.z = FIX_MAX; + for (i=0;i<4; i++) { + gf_mx_apply_vec(mx, &v[i]); + if (box->min_edge.x > v[i].x) box->min_edge.x = v[i].x; + if (box->min_edge.y > v[i].y) box->min_edge.y = v[i].y; + if (box->min_edge.z > v[i].z) box->min_edge.z = v[i].z; + if (box->max_edge.x < v[i].x) box->max_edge.x = v[i].x; + if (box->max_edge.y < v[i].y) box->max_edge.y = v[i].y; + if (box->max_edge.z < v[i].z) box->max_edge.z = v[i].z; + } + gf_bbox_refresh(box); +} + +// Apply the rotation portion of a matrix to a vector. +GF_EXPORT +void gf_mx_rotate_vector(GF_Matrix *mx, GF_Vec *pt) +{ + GF_Vec res; + Fixed den; + res.x = gf_mulfix(pt->x, mx->m[0]) + gf_mulfix(pt->y, mx->m[4]) + gf_mulfix(pt->z, mx->m[8]); + res.y = gf_mulfix(pt->x, mx->m[1]) + gf_mulfix(pt->y, mx->m[5]) + gf_mulfix(pt->z, mx->m[9]); + res.z = gf_mulfix(pt->x, mx->m[2]) + gf_mulfix(pt->y, mx->m[6]) + gf_mulfix(pt->z, mx->m[10]); + den = gf_mulfix(pt->x, mx->m[3]) + gf_mulfix(pt->y, mx->m[7]) + gf_mulfix(pt->z, mx->m[11]) + mx->m[15]; + if (den == 0) return; + res.x = gf_divfix(res.x, den); + res.y = gf_divfix(res.y, den); + res.z = gf_divfix(res.z, den); + *pt = res; +} + +GF_EXPORT +void gf_mx_rotation_matrix_from_vectors(GF_Matrix *mx, GF_Vec x, GF_Vec y, GF_Vec z) +{ + mx->m[0] = x.x; mx->m[1] = y.x; mx->m[2] = z.x; mx->m[3] = 0; + mx->m[4] = x.y; mx->m[5] = y.y; mx->m[6] = z.y; mx->m[7] = 0; + mx->m[8] = x.z; mx->m[9] = y.z; mx->m[10] = z.z; mx->m[11] = 0; + mx->m[12] = 0; mx->m[13] = 0; mx->m[14] = 0; mx->m[15] = FIX_ONE; +} + + +/*we should only need a full matrix product for frustrum setup*/ +GF_EXPORT +void gf_mx_add_matrix_4x4(GF_Matrix *mat, GF_Matrix *mul) +{ + GF_Matrix tmp; + gf_mx_init(tmp); + tmp.m[0] = gf_mulfix(mat->m[0],mul->m[0]) + gf_mulfix(mat->m[4],mul->m[1]) + gf_mulfix(mat->m[8],mul->m[2]) + gf_mulfix(mat->m[12],mul->m[3]); + tmp.m[1] = gf_mulfix(mat->m[1],mul->m[0]) + gf_mulfix(mat->m[5],mul->m[1]) + gf_mulfix(mat->m[9],mul->m[2]) + gf_mulfix(mat->m[13],mul->m[3]); + tmp.m[2] = gf_mulfix(mat->m[2],mul->m[0]) + gf_mulfix(mat->m[6],mul->m[1]) + gf_mulfix(mat->m[10],mul->m[2]) + gf_mulfix(mat->m[14],mul->m[3]); + tmp.m[3] = gf_mulfix(mat->m[3],mul->m[0]) + gf_mulfix(mat->m[7],mul->m[1]) + gf_mulfix(mat->m[11],mul->m[2]) + gf_mulfix(mat->m[15],mul->m[3]); + tmp.m[4] = gf_mulfix(mat->m[0],mul->m[4]) + gf_mulfix(mat->m[4],mul->m[5]) + gf_mulfix(mat->m[8],mul->m[6]) + gf_mulfix(mat->m[12],mul->m[7]); + tmp.m[5] = gf_mulfix(mat->m[1],mul->m[4]) + gf_mulfix(mat->m[5],mul->m[5]) + gf_mulfix(mat->m[9],mul->m[6]) + gf_mulfix(mat->m[13],mul->m[7]); + tmp.m[6] = gf_mulfix(mat->m[2],mul->m[4]) + gf_mulfix(mat->m[6],mul->m[5]) + gf_mulfix(mat->m[10],mul->m[6]) + gf_mulfix(mat->m[14],mul->m[7]); + tmp.m[7] = gf_mulfix(mat->m[3],mul->m[4]) + gf_mulfix(mat->m[7],mul->m[5]) + gf_mulfix(mat->m[11],mul->m[6]) + gf_mulfix(mat->m[15],mul->m[7]); + tmp.m[8] = gf_mulfix(mat->m[0],mul->m[8]) + gf_mulfix(mat->m[4],mul->m[9]) + gf_mulfix(mat->m[8],mul->m[10]) + gf_mulfix(mat->m[12],mul->m[11]); + tmp.m[9] = gf_mulfix(mat->m[1],mul->m[8]) + gf_mulfix(mat->m[5],mul->m[9]) + gf_mulfix(mat->m[9],mul->m[10]) + gf_mulfix(mat->m[13],mul->m[11]); + tmp.m[10] = gf_mulfix(mat->m[2],mul->m[8]) + gf_mulfix(mat->m[6],mul->m[9]) + gf_mulfix(mat->m[10],mul->m[10]) + gf_mulfix(mat->m[14],mul->m[11]); + tmp.m[11] = gf_mulfix(mat->m[3],mul->m[8]) + gf_mulfix(mat->m[7],mul->m[9]) + gf_mulfix(mat->m[11],mul->m[10]) + gf_mulfix(mat->m[15],mul->m[11]); + tmp.m[12] = gf_mulfix(mat->m[0],mul->m[12]) + gf_mulfix(mat->m[4],mul->m[13]) + gf_mulfix(mat->m[8],mul->m[14]) + gf_mulfix(mat->m[12],mul->m[15]); + tmp.m[13] = gf_mulfix(mat->m[1],mul->m[12]) + gf_mulfix(mat->m[5],mul->m[13]) + gf_mulfix(mat->m[9],mul->m[14]) + gf_mulfix(mat->m[13],mul->m[15]); + tmp.m[14] = gf_mulfix(mat->m[2],mul->m[12]) + gf_mulfix(mat->m[6],mul->m[13]) + gf_mulfix(mat->m[10],mul->m[14]) + gf_mulfix(mat->m[14],mul->m[15]); + tmp.m[15] = gf_mulfix(mat->m[3],mul->m[12]) + gf_mulfix(mat->m[7],mul->m[13]) + gf_mulfix(mat->m[11],mul->m[14]) + gf_mulfix(mat->m[15],mul->m[15]); + memcpy(mat->m, tmp.m, sizeof(Fixed)*16); +} + + +GF_EXPORT +void gf_mx_apply_vec_4x4(GF_Matrix *mx, GF_Vec4 *vec) +{ + GF_Vec4 res; + res.x = gf_mulfix(mx->m[0], vec->x) + gf_mulfix(mx->m[4], vec->y) + gf_mulfix(mx->m[8], vec->z) + gf_mulfix(mx->m[12], vec->q); + res.y = gf_mulfix(mx->m[1], vec->x) + gf_mulfix(mx->m[5], vec->y) + gf_mulfix(mx->m[9], vec->z) + gf_mulfix(mx->m[13], vec->q); + res.z = gf_mulfix(mx->m[2], vec->x) + gf_mulfix(mx->m[6], vec->y) + gf_mulfix(mx->m[10], vec->z) + gf_mulfix(mx->m[14], vec->q); + res.q = gf_mulfix(mx->m[3], vec->x) + gf_mulfix(mx->m[7], vec->y) + gf_mulfix(mx->m[11], vec->z) + gf_mulfix(mx->m[15], vec->q); + *vec = res; +} + +/* + * Taken from MESA/GLU (LGPL) + * + * Compute inverse of 4x4 transformation matrix. + * Code contributed by Jacques Leroy jle@star.be + * Return 1 for success, 0 for failure (singular matrix) + */ + +GF_EXPORT +Bool gf_mx_inverse_4x4(GF_Matrix *mx) +{ + +#define SWAP_ROWS(a, b) { Fixed *_tmp = a; (a)=(b); (b)=_tmp; } + Fixed wtmp[4][8]; + Fixed m0, m1, m2, m3, s; + Fixed *r0, *r1, *r2, *r3; + GF_Matrix res; + r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; + r0[0] = mx->m[0]; r0[1] = mx->m[4]; r0[2] = mx->m[8]; r0[3] = mx->m[12]; r0[4] = FIX_ONE; r0[5] = r0[6] = r0[7] = 0; + r1[0] = mx->m[1]; r1[1] = mx->m[5]; r1[2] = mx->m[9]; r1[3] = mx->m[13]; r1[5] = FIX_ONE; r1[4] = r1[6] = r1[7] = 0; + r2[0] = mx->m[2]; r2[1] = mx->m[6]; r2[2] = mx->m[10]; r2[3] = mx->m[14]; r2[6] = FIX_ONE; r2[4] = r2[5] = r2[7] = 0; + r3[0] = mx->m[3]; r3[1] = mx->m[7]; r3[2] = mx->m[11]; r3[3] = mx->m[15]; r3[7] = FIX_ONE; r3[4] = r3[5] = r3[6] = 0; + + /* choose pivot - or die */ + if (ABS(r3[0]) > ABS(r2[0])) SWAP_ROWS(r3, r2); + if (ABS(r2[0]) > ABS(r1[0])) SWAP_ROWS(r2, r1); + if (ABS(r1[0]) > ABS(r0[0])) SWAP_ROWS(r1, r0); + if (r0[0]==0) return 0; + + /*eliminate first variable*/ + m1 = gf_divfix(r1[0], r0[0]); + m2 = gf_divfix(r2[0], r0[0]); + m3 = gf_divfix(r3[0], r0[0]); + s = r0[1]; + r1[1] -= gf_mulfix(m1, s); + r2[1] -= gf_mulfix(m2, s); + r3[1] -= gf_mulfix(m3, s); + s = r0[2]; + r1[2] -= gf_mulfix(m1, s); + r2[2] -= gf_mulfix(m2, s); + r3[2] -= gf_mulfix(m3, s); + s = r0[3]; + r1[3] -= gf_mulfix(m1, s); + r2[3] -= gf_mulfix(m2, s); + r3[3] -= gf_mulfix(m3, s); + s = r0[4]; + if (s != 0) { + r1[4] -= gf_mulfix(m1, s); + r2[4] -= gf_mulfix(m2, s); + r3[4] -= gf_mulfix(m3, s); + } + s = r0[5]; + if (s != 0) { + r1[5] -= gf_mulfix(m1, s); + r2[5] -= gf_mulfix(m2, s); + r3[5] -= gf_mulfix(m3, s); + } + s = r0[6]; + if (s != 0) { + r1[6] -= gf_mulfix(m1, s); + r2[6] -= gf_mulfix(m2, s); + r3[6] -= gf_mulfix(m3, s); + } + s = r0[7]; + if (s != 0) { + r1[7] -= gf_mulfix(m1, s); + r2[7] -= gf_mulfix(m2, s); + r3[7] -= gf_mulfix(m3, s); + } + + /* choose pivot - or die */ + if (fabs(r3[1]) > fabs(r2[1])) SWAP_ROWS(r3, r2); + if (fabs(r2[1]) > fabs(r1[1])) SWAP_ROWS(r2, r1); + if (r1[1]==0) return 0; + + /* eliminate second variable */ + m2 = gf_divfix(r2[1], r1[1]); + m3 = gf_divfix(r3[1], r1[1]); + r2[2] -= gf_mulfix(m2, r1[2]); + r3[2] -= gf_mulfix(m3, r1[2]); + r2[3] -= gf_mulfix(m2, r1[3]); + r3[3] -= gf_mulfix(m3, r1[3]); + s = r1[4]; + if (s!=0) { + r2[4] -= gf_mulfix(m2, s); + r3[4] -= gf_mulfix(m3, s); + } + s = r1[5]; + if (s!=0) { + r2[5] -= gf_mulfix(m2, s); + r3[5] -= gf_mulfix(m3, s); + } + s = r1[6]; + if (s!=0) { + r2[6] -= gf_mulfix(m2, s); + r3[6] -= gf_mulfix(m3, s); + } + s = r1[7]; + if (s!=0) { + r2[7] -= gf_mulfix(m2, s); + r3[7] -= gf_mulfix(m3, s); + } + + /* choose pivot - or die */ + if (fabs(r3[2]) > fabs(r2[2])) SWAP_ROWS(r3, r2); + if (r2[2]==0) return 0; + + /* eliminate third variable */ + m3 = gf_divfix(r3[2], r2[2]); + r3[3] -= gf_mulfix(m3, r2[3]); r3[4] -= gf_mulfix(m3, r2[4]); r3[5] -= gf_mulfix(m3, r2[5]); + r3[6] -= gf_mulfix(m3, r2[6]); r3[7] -= gf_mulfix(m3, r2[7]); + /* last check */ + if (r3[3]==0) return 0; + + s = gf_invfix(r3[3]); /* now back substitute row 3 */ + r3[4] = gf_mulfix(r3[4], s); + r3[5] = gf_mulfix(r3[5], s); + r3[6] = gf_mulfix(r3[6], s); + r3[7] = gf_mulfix(r3[7], s); + + m2 = r2[3]; /* now back substitute row 2 */ + s = gf_invfix(r2[2]); + r2[4] = gf_mulfix(s, r2[4] - gf_mulfix(r3[4], m2)); + r2[5] = gf_mulfix(s, r2[5] - gf_mulfix(r3[5], m2)); + r2[6] = gf_mulfix(s, r2[6] - gf_mulfix(r3[6], m2)); + r2[7] = gf_mulfix(s, r2[7] - gf_mulfix(r3[7], m2)); + m1 = r1[3]; + r1[4] -= gf_mulfix(r3[4], m1); r1[5] -= gf_mulfix(r3[5], m1); r1[6] -= gf_mulfix(r3[6], m1); r1[7] -= gf_mulfix(r3[7], m1); + m0 = r0[3]; + r0[4] -= gf_mulfix(r3[4], m0); r0[5] -= gf_mulfix(r3[5], m0); r0[6] -= gf_mulfix(r3[6], m0); r0[7] -= gf_mulfix(r3[7], m0); + + m1 = r1[2]; /* now back substitute row 1 */ + s = gf_invfix(r1[1]); + r1[4] = gf_mulfix(s, r1[4] - gf_mulfix(r2[4], m1)); + r1[5] = gf_mulfix(s, r1[5] - gf_mulfix(r2[5], m1)); + r1[6] = gf_mulfix(s, r1[6] - gf_mulfix(r2[6], m1)); + r1[7] = gf_mulfix(s, r1[7] - gf_mulfix(r2[7], m1)); + m0 = r0[2]; + r0[4] -= gf_mulfix(r2[4], m0); r0[5] -= gf_mulfix(r2[5], m0); r0[6] -= gf_mulfix(r2[6], m0); r0[7] -= gf_mulfix(r2[7], m0); + + m0 = r0[1]; /* now back substitute row 0 */ + s = gf_invfix(r0[0]); + r0[4] = gf_mulfix(s, r0[4] - gf_mulfix(r1[4], m0)); + r0[5] = gf_mulfix(s, r0[5] - gf_mulfix(r1[5], m0)); + r0[6] = gf_mulfix(s, r0[6] - gf_mulfix(r1[6], m0)); + r0[7] = gf_mulfix(s, r0[7] - gf_mulfix(r1[7], m0)); + + gf_mx_init(res) + res.m[0] = r0[4]; res.m[4] = r0[5]; res.m[8] = r0[6]; res.m[12] = r0[7]; + res.m[1] = r1[4]; res.m[5] = r1[5], res.m[9] = r1[6]; res.m[13] = r1[7]; + res.m[2] = r2[4]; res.m[6] = r2[5]; res.m[10] = r2[6]; res.m[14] = r2[7]; + res.m[3] = r3[4]; res.m[7] = r3[5]; res.m[11] = r3[6]; res.m[15] = r3[7]; + gf_mx_copy(*mx, res); + return 1; +#undef SWAP_ROWS + +} + + +GF_EXPORT +Bool gf_plane_exists_intersection(GF_Plane *plane, GF_Plane *with) +{ + GF_Vec cross; + cross = gf_vec_cross(with->normal, plane->normal); + return gf_vec_lensq(cross) > FIX_EPSILON; +} + +GF_EXPORT +Bool gf_plane_intersect_line(GF_Plane *plane, GF_Vec *linepoint, GF_Vec *linevec, GF_Vec *outPoint) +{ + Fixed t, t2; + t2 = gf_vec_dot(plane->normal, *linevec); + if (t2 == 0) return 0; + t = - gf_divfix((gf_vec_dot(plane->normal, *linepoint) + plane->d) , t2); + if (t<0) return 0; + *outPoint = gf_vec_scale(*linevec, t); + gf_vec_add(*outPoint, *linepoint, *outPoint); + return 1; +} + +GF_EXPORT +Bool gf_plane_intersect_plane(GF_Plane *plane, GF_Plane *with, GF_Vec *linepoint, GF_Vec *linevec) +{ + Fixed fn00 = gf_vec_len(plane->normal); + Fixed fn01 = gf_vec_dot(plane->normal, with->normal); + Fixed fn11 = gf_vec_len(with->normal); + Fixed det = gf_mulfix(fn00,fn11) - gf_mulfix(fn01,fn01); + if (fabs(det) > FIX_EPSILON) { + Fixed fc0, fc1; + GF_Vec v1, v2; + fc0 = gf_divfix( gf_mulfix(fn11, -plane->d) + gf_mulfix(fn01, with->d) , det); + fc1 = gf_divfix( gf_mulfix(fn00, -with->d) + gf_mulfix(fn01, plane->d) , det); + *linevec = gf_vec_cross(plane->normal, with->normal); + v1 = gf_vec_scale(plane->normal, fc0); + v2 = gf_vec_scale(with->normal, fc1); + gf_vec_add(*linepoint, v1, v2); + return 1; + } + return 0; +} + +GF_EXPORT +Bool gf_plane_intersect_planes(GF_Plane *plane, GF_Plane *p1, GF_Plane *p2, GF_Vec *outPoint) +{ + GF_Vec lp, lv; + if (gf_plane_intersect_plane(plane, p1, &lp, &lv)) + return gf_plane_intersect_line(p2, &lp, &lv, outPoint); + return 0; +} + + + +GF_EXPORT +GF_Ray gf_ray(GF_Vec start, GF_Vec end) +{ + GF_Ray r; + r.orig = start; + gf_vec_diff(r.dir, end, start); + gf_vec_norm(&r.dir); + return r; +} + +GF_EXPORT +void gf_mx_apply_ray(GF_Matrix *mx, GF_Ray *r) +{ + gf_vec_add(r->dir, r->orig, r->dir); + gf_mx_apply_vec(mx, &r->orig); + gf_mx_apply_vec(mx, &r->dir); + gf_vec_diff(r->dir, r->dir, r->orig); + gf_vec_norm(&r->dir); +} + +#define XPLANE 0 +#define YPLANE 1 +#define ZPLANE 2 + + +GF_EXPORT +Bool gf_ray_hit_box(GF_Ray *ray, GF_Vec box_min, GF_Vec box_max, GF_Vec *outPoint) +{ + Fixed t1, t2, tNEAR=FIX_MIN, tFAR=FIX_MAX; + Fixed temp; + s8 xyorz, sign; + + if (ray->dir.x == 0) { + if ((ray->orig.x < box_min.x) || (ray->orig.x > box_max.x)) + return 0; + } else { + t1 = gf_divfix(box_min.x - ray->orig.x, ray->dir.x); + t2 = gf_divfix(box_max.x - ray->orig.x, ray->dir.x); + if (t1 > t2) { + temp = t1; + t1 = t2; + t2 = temp; + } + if (t1 > tNEAR) { + tNEAR = t1; + xyorz = XPLANE; + sign = (ray->dir.x < 0) ? 1 : -1; + } + if (t2 < tFAR) tFAR = t2; + if (tNEAR > tFAR) return 0; // box missed + if (tFAR < 0) return 0; // box behind the ray + } + + if (ray->dir.y == 0) { + if ((ray->orig.y < box_min.y) || (ray->orig.y > box_max.y)) + return 0; + } else { + tNEAR=FIX_MIN; + tFAR=FIX_MAX; + t1 = gf_divfix(box_min.y - ray->orig.y, ray->dir.y); + t2 = gf_divfix(box_max.y - ray->orig.y, ray->dir.y); + if (t1 > t2) { + temp = t1; + t1 = t2; + t2 = temp; + } + if (t1 > tNEAR) { + tNEAR = t1; + xyorz = YPLANE; + sign = (ray->dir.y < 0) ? 1 : -1; + } + if (t2 < tFAR) tFAR = t2; + if (tNEAR > tFAR) return 0; // box missed + if (tFAR < 0) return 0; // box behind the ray + } + + // Check the Z plane + if (ray->dir.z == 0) { + if ((ray->orig.z < box_min.z) || (ray->orig.z > box_max.z)) + return 0; + } else { + tNEAR=FIX_MIN; + tFAR=FIX_MAX; + t1 = gf_divfix(box_min.z - ray->orig.z, ray->dir.z); + t2 = gf_divfix(box_max.z - ray->orig.z, ray->dir.z); + if (t1 > t2) { + temp = t1; + t1 = t2; + t2 = temp; + } + if (t1 > tNEAR) { + tNEAR = t1; + xyorz = ZPLANE; + sign = (ray->dir.z < 0) ? 1 : -1; + } + if (t2 < tFAR) tFAR = t2; + if (tNEAR>tFAR) return 0; // box missed + if (tFAR < 0) return 0; // box behind the ray + } + if (outPoint) { + *outPoint = gf_vec_scale(ray->dir, tNEAR); + gf_vec_add(*outPoint, *outPoint, ray->orig); + } + return 1; +} + + +GF_EXPORT +Bool gf_ray_hit_sphere(GF_Ray *ray, GF_Vec *center, Fixed radius, GF_Vec *outPoint) +{ + GF_Vec radv; + Fixed dist, center_proj, center_proj_sq, hcord; + if (center) { + gf_vec_diff(radv, *center, ray->orig); + } else { + radv = ray->orig; + gf_vec_rev(radv); + } + dist = gf_vec_len(radv); + center_proj = gf_vec_dot(radv, ray->dir); + if (radius + ABS(center_proj) < dist ) return 0; + + center_proj_sq = gf_mulfix(center_proj, center_proj); + hcord = center_proj_sq - gf_mulfix(dist, dist) + gf_mulfix(radius , radius); + if (hcord < 0) return 0; + if (center_proj_sq < hcord) return 0; + if (outPoint) { + center_proj -= gf_sqrt(hcord); + radv = gf_vec_scale(ray->dir, center_proj); + gf_vec_add(*outPoint, ray->orig, radv); + } + return 1; +} + +/* + * Tomas M�ller and Ben Trumbore. + * Fast, minimum storage ray-triangle intersection. + * Journal of graphics tools, 2(1):21-28, 1997 + * + */ +GF_EXPORT +Bool gf_ray_hit_triangle(GF_Ray *ray, GF_Vec *v0, GF_Vec *v1, GF_Vec *v2, Fixed *dist) +{ + Fixed u, v, det; + GF_Vec edge1, edge2, tvec, pvec, qvec; + /* find vectors for two edges sharing vert0 */ + gf_vec_diff(edge1, *v1, *v0); + gf_vec_diff(edge2, *v2, *v0); + /* begin calculating determinant - also used to calculate U parameter */ + pvec = gf_vec_cross(ray->dir, edge2); + /* if determinant is near zero, ray lies in plane of triangle */ + det = gf_vec_dot(edge1, pvec); + if (ABS(det) < FIX_EPSILON) return 0; + /* calculate distance from vert0 to ray origin */ + gf_vec_diff(tvec, ray->orig, *v0); + /* calculate U parameter and test bounds */ + u = gf_divfix(gf_vec_dot(tvec, pvec), det); + if ((u < 0) || (u > FIX_ONE)) return 0; + /* prepare to test V parameter */ + qvec = gf_vec_cross(tvec, edge1); + /* calculate V parameter and test bounds */ + v = gf_divfix(gf_vec_dot(ray->dir, qvec), det); + if ((v < 0) || (u + v > FIX_ONE)) return 0; +#ifdef GPAC_FIXED_POINT +#define VSCALE 4096 + det /= VSCALE; + qvec.x /= VSCALE; + qvec.y /= VSCALE; + qvec.z /= VSCALE; +#undef VSCALE +#endif + /* calculate t, ray intersects triangle */ + *dist = gf_divfix(gf_vec_dot(edge2, qvec), det); + return 1; +} + +GF_EXPORT +Bool gf_ray_hit_triangle_backcull(GF_Ray *ray, GF_Vec *v0, GF_Vec *v1, GF_Vec *v2, Fixed *dist) +{ + Fixed u, v, det; + GF_Vec edge1, edge2, tvec, pvec, qvec; + /* find vectors for two edges sharing vert0 */ + gf_vec_diff(edge1, *v1, *v0); + gf_vec_diff(edge2, *v2, *v0); + /* begin calculating determinant - also used to calculate U parameter */ + pvec = gf_vec_cross(ray->dir, edge2); + /* if determinant is near zero, ray lies in plane of triangle */ + det = gf_vec_dot(edge1, pvec); + if (det < FIX_EPSILON) return 0; + /* calculate distance from vert0 to ray origin */ + gf_vec_diff(tvec, ray->orig, *v0); + /* calculate U parameter and test bounds */ + u = gf_vec_dot(tvec, pvec); + if ((u < 0) || (u > det)) return 0; + /* prepare to test V parameter */ + qvec = gf_vec_cross(tvec, edge1); + /* calculate V parameter and test bounds */ + v = gf_vec_dot(ray->dir, qvec); + if ((v < 0) || (u + v > det)) return 0; + /* calculate t, scale parameters, ray intersects triangle */ + *dist = gf_divfix(gf_vec_dot(edge2, qvec), det); + return 1; +} + +GF_EXPORT +GF_Vec gf_closest_point_to_line(GF_Vec line_pt, GF_Vec line_vec, GF_Vec pt) +{ + GF_Vec c; + Fixed t; + gf_vec_diff(c, pt, line_pt); + t = gf_vec_dot(line_vec, c); + c = gf_vec_scale(line_vec, t); + gf_vec_add(c, c, line_pt); + return c; +} + + +GF_EXPORT +GF_Vec4 gf_quat_from_matrix(GF_Matrix *mx) +{ + GF_Vec4 res; + Fixed diagonal, s; + diagonal = mx->m[0] + mx->m[5] + mx->m[10]; + + if (diagonal > 0) { + s = gf_sqrt(diagonal + FIX_ONE); + res.q = s / 2; + s = gf_invfix(2*s); + res.x = gf_mulfix(mx->m[6] - mx->m[9], s); + res.y = gf_mulfix(mx->m[8] - mx->m[2], s); + res.z = gf_mulfix(mx->m[1] - mx->m[4], s); + } else { + Fixed q[4]; + u32 i, j, k; + static const u32 next[3] = { 1, 2, 0 }; + i = 0; + if (mx->m[5] > mx->m[0]) { i = 1; } + if (mx->m[10] > mx->m[4*i+i]) { i = 2; } + j = next[i]; + k = next[j]; + s = gf_sqrt(FIX_ONE + mx->m[4*i + i] - (mx->m[4*j+j] + mx->m[4*k+k]) ); + q[i] = s / 2; + if (s != 0) { s = gf_invfix(2*s); } + q[3] = gf_mulfix(mx->m[4*j+k] - mx->m[4*k+j], s); + q[j] = gf_mulfix(mx->m[4*i+j] + mx->m[4*j+i], s); + q[k] = gf_mulfix(mx->m[4*i+k] + mx->m[4*k+i], s); + res.x = q[0]; res.y = q[1]; res.z = q[2]; res.q = q[3]; + } + return res; +} + +GF_EXPORT +GF_Vec4 gf_quat_to_rotation(GF_Vec4 *quat) +{ + GF_Vec4 r; + Fixed val = gf_acos(quat->q); + if (val == 0) { + r.x = r.y = 0; + r.z = FIX_ONE; + r.q = 0; + } else { + GF_Vec axis; + Fixed sin_val = gf_sin(val); + axis.x = gf_divfix(quat->x, sin_val); + axis.y = gf_divfix(quat->y, sin_val); + axis.z = gf_divfix(quat->z, sin_val); + gf_vec_norm(&axis); + r.x = axis.x; + r.y = axis.y; + r.z = axis.z; + r.q= 2 * val; + } + return r; +} + +GF_EXPORT +GF_Vec4 gf_quat_from_rotation(GF_Vec4 rot) +{ + GF_Vec4 res; + Fixed s; + Fixed scale = gf_sqrt( gf_mulfix(rot.x, rot.x) + gf_mulfix(rot.y, rot.y) + gf_mulfix(rot.z, rot.z)); + + /* no rotation - use (multiplication ???) identity quaternion */ + if (scale == 0) { + res.q = FIX_ONE; + res.x = 0; + res.y = 0; + res.z = 0; + } else { + s = gf_sin(rot.q/2); + res.q = gf_cos(rot.q / 2); + res.x = gf_muldiv(s, rot.x, scale); + res.y = gf_muldiv(s, rot.y, scale); + res.z = gf_muldiv(s, rot.z, scale); + gf_quat_norm(res); + } + return res; +} + +GF_EXPORT +GF_Vec4 gf_quat_from_axis_cos(GF_Vec axis, Fixed cos_a) +{ + GF_Vec4 r; + if (cos_a < -FIX_ONE) cos_a = -FIX_ONE; + else if (cos_a > FIX_ONE) cos_a = FIX_ONE; + r.x = axis.x; r.y = axis.y; r.z = axis.z; + r.q = gf_acos(cos_a); + return gf_quat_from_rotation(r); +} + +static void gf_quat_conjugate(GF_Vec4 *quat) +{ + quat->x *= -1; + quat->y *= -1; + quat->z *= -1; +} + +GF_EXPORT +GF_Vec4 gf_quat_get_inv(GF_Vec4 *quat) +{ + GF_Vec4 ret = *quat; + gf_quat_conjugate(&ret); + gf_quat_norm(ret); + return ret; +} + + +GF_EXPORT +GF_Vec4 gf_quat_multiply(GF_Vec4 *q1, GF_Vec4 *q2) +{ + GF_Vec4 ret; + ret.q = gf_mulfix(q1->q, q2->q) - gf_mulfix(q1->x, q2->x) - gf_mulfix(q1->y, q2->y) - gf_mulfix(q1->z, q2->z); + ret.x = gf_mulfix(q1->q, q2->x) + gf_mulfix(q1->x, q2->q) + gf_mulfix(q1->y, q2->z) - gf_mulfix(q1->z, q2->y); + ret.y = gf_mulfix(q1->q, q2->y) + gf_mulfix(q1->y, q2->q) - gf_mulfix(q1->x, q2->z) + gf_mulfix(q1->z, q2->x); + ret.z = gf_mulfix(q1->q, q2->z) + gf_mulfix(q1->z, q2->q) + gf_mulfix(q1->x, q2->y) - gf_mulfix(q1->y, q2->x); + return ret; +} + +GF_EXPORT +GF_Vec gf_quat_rotate(GF_Vec4 *quat, GF_Vec *vec) +{ + GF_Vec ret; + GF_Vec4 q_v, q_i, q_r1, q_r2; + q_v.q = 0; + q_v.x = vec->x; + q_v.y = vec->y; + q_v.z = vec->z; + q_i = gf_quat_get_inv(quat); + q_r1 = gf_quat_multiply(&q_v, &q_i); + q_r2 = gf_quat_multiply(quat, &q_r1); + ret.x = q_r2.x; + ret.y = q_r2.y; + ret.z = q_r2.z; + return ret; +} + +/* + * Code from www.gamasutra.com/features/19980703/quaternions_01.htm, + * Listing 5. + * + * SLERP(p, q, t) = [p sin((1 - t)a) + q sin(ta)] / sin(a) + * + * where a is the arc angle, quaternions pq = cos(q) and 0 <= t <= 1 + */ +GF_EXPORT +GF_Vec4 gf_quat_slerp(GF_Vec4 q1, GF_Vec4 q2, Fixed frac) +{ + GF_Vec4 res; + Fixed omega, cosom, sinom, scale0, scale1, q2_array[4]; + + cosom = gf_mulfix(q1.x, q2.x) + gf_mulfix(q1.y, q2.y) + gf_mulfix(q1.z, q2.z) + gf_mulfix(q1.q, q2.q); + if (cosom < 0) { + cosom = -cosom; + q2_array[0] = -q2.x; + q2_array[1] = -q2.y; + q2_array[2] = -q2.z; + q2_array[3] = -q2.q; + } else { + q2_array[0] = q2.x; + q2_array[1] = q2.y; + q2_array[2] = q2.z; + q2_array[3] = q2.q; + } + + /* calculate coefficients */ + if ((FIX_ONE - cosom) > FIX_EPSILON) { + omega = gf_acos(cosom); + sinom = gf_sin(omega); + scale0 = gf_divfix(gf_sin( gf_mulfix(FIX_ONE - frac, omega)), sinom); + scale1 = gf_divfix(gf_sin( gf_mulfix(frac, omega)), sinom); + } else { + /* q1 & q2 are very close, so do linear interpolation */ + scale0 = FIX_ONE - frac; + scale1 = frac; + } + res.x = gf_mulfix(scale0, q1.x) + gf_mulfix(scale1, q2_array[0]); + res.y = gf_mulfix(scale0, q1.y) + gf_mulfix(scale1, q2_array[1]); + res.z = gf_mulfix(scale0, q1.z) + gf_mulfix(scale1, q2_array[2]); + res.q = gf_mulfix(scale0, q1.q) + gf_mulfix(scale1, q2_array[3]); + return res; +} + + +/*update center & radius & is_set flag*/ +GF_EXPORT +void gf_bbox_refresh(GF_BBox *b) +{ + GF_Vec v; + gf_vec_add(v, b->min_edge, b->max_edge); + b->center = gf_vec_scale(v, FIX_ONE / 2); + gf_vec_diff(v, b->max_edge, b->min_edge); + b->radius = gf_vec_len(v) / 2; + b->is_set = 1; +} + +GF_EXPORT +void gf_bbox_from_rect(GF_BBox *box, GF_Rect *rc) +{ + box->min_edge.x = rc->x; + box->min_edge.y = rc->y - rc->height; + box->min_edge.z = 0; + box->max_edge.x = rc->x + rc->width; + box->max_edge.y = rc->y; + box->max_edge.z = 0; + gf_bbox_refresh(box); +} + +GF_EXPORT +void gf_rect_from_bbox(GF_Rect *rc, GF_BBox *box) +{ + rc->x = box->min_edge.x; + rc->y = box->max_edge.y; + rc->width = box->max_edge.x - box->min_edge.x; + rc->height = box->max_edge.y - box->min_edge.y; +} + +GF_EXPORT +void gf_bbox_grow_point(GF_BBox *box, GF_Vec pt) +{ + if (pt.x > box->max_edge.x) box->max_edge.x = pt.x; + if (pt.y > box->max_edge.y) box->max_edge.y = pt.y; + if (pt.z > box->max_edge.z) box->max_edge.z = pt.z; + if (pt.x < box->min_edge.x) box->min_edge.x = pt.x; + if (pt.y < box->min_edge.y) box->min_edge.y = pt.y; + if (pt.z < box->min_edge.z) box->min_edge.z = pt.z; +} + +GF_EXPORT +void gf_bbox_union(GF_BBox *b1, GF_BBox *b2) +{ + if (b2->is_set) { + if (!b1->is_set) { + *b1 = *b2; + } else { + gf_bbox_grow_point(b1, b2->min_edge); + gf_bbox_grow_point(b1, b2->max_edge); + gf_bbox_refresh(b1); + } + } +} + +GF_EXPORT +Bool gf_bbox_equal(GF_BBox *b1, GF_BBox *b2) +{ + return (gf_vec_equal(b1->min_edge, b2->min_edge) && gf_vec_equal(b1->max_edge, b2->max_edge)); +} + +GF_EXPORT +Bool gf_bbox_point_inside(GF_BBox *box, GF_Vec *p) +{ + return (p->x >= box->min_edge.x && p->x <= box->max_edge.x && + p->y >= box->min_edge.y && p->y <= box->max_edge.y && + p->z >= box->min_edge.z && p->z <= box->max_edge.z); +} + +/*vertices are ordered to respect p vertex indexes (vertex from bbox closer to plane) +and so that n-vertex (vertex from bbox farther from plane) is 7-p_vx_idx*/ +GF_EXPORT +void gf_bbox_get_vertices(GF_Vec bmin, GF_Vec bmax, GF_Vec *vecs) +{ + vecs[0].x = vecs[1].x = vecs[2].x = vecs[3].x = bmax.x; + vecs[4].x = vecs[5].x = vecs[6].x = vecs[7].x = bmin.x; + vecs[0].y = vecs[1].y = vecs[4].y = vecs[5].y = bmax.y; + vecs[2].y = vecs[3].y = vecs[6].y = vecs[7].y = bmin.y; + vecs[0].z = vecs[2].z = vecs[4].z = vecs[6].z = bmax.z; + vecs[1].z = vecs[3].z = vecs[5].z = vecs[7].z = bmin.z; +} + + +GF_EXPORT +void gf_mx_apply_plane(GF_Matrix *mx, GF_Plane *plane) +{ + GF_Vec pt, end; + /*get pt*/ + pt = gf_vec_scale(plane->normal, -plane->d); + gf_vec_add(end, pt, plane->normal); + gf_mx_apply_vec(mx, &pt); + gf_mx_apply_vec(mx, &end); + gf_vec_diff(plane->normal, end, pt); + gf_vec_norm(&plane->normal); + plane->d = - gf_vec_dot(pt, plane->normal); +} + +GF_EXPORT +Fixed gf_plane_get_distance(GF_Plane *plane, GF_Vec *p) +{ + return gf_vec_dot(*p, plane->normal) + plane->d; +} + + +/*return p-vertex index (vertex from bbox closer to plane) - index range from 0 to 8*/ +GF_EXPORT +u32 gf_plane_get_p_vertex_idx(GF_Plane *p) +{ + if (p->normal.x>=0) { + if (p->normal.y>=0) return (p->normal.z>=0) ? 0 : 1; + return (p->normal.z>=0) ? 2 : 3; + } else { + if (p->normal.y>=0) return (p->normal.z>=0) ? 4 : 5; + return (p->normal.z>=0) ? 6 : 7; + } +} + + +GF_EXPORT +u32 gf_bbox_plane_relation(GF_BBox *box, GF_Plane *p) +{ + GF_Vec nearv, farv; + nearv = box->max_edge; + farv = box->min_edge; + if (p->normal.x > 0) { + nearv.x = box->min_edge.x; + farv.x = box->max_edge.x; + } + if (p->normal.y > 0) { + nearv.y = box->min_edge.y; + farv.y = box->max_edge.y; + } + if (p->normal.z > 0) { + nearv.z = box->min_edge.z; + farv.z = box->max_edge.z; + } + if (gf_vec_dot(p->normal, nearv) + p->d > 0) return GF_BBOX_FRONT; + if (gf_vec_dot(p->normal, farv) + p->d > 0) return GF_BBOX_INTER; + return GF_BBOX_BACK; +} + + +GF_EXPORT +u32 gf_get_next_pow2(u32 s) +{ + u32 i; + u32 res = s; + u32 sizes[] = { 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 }; + u32 nSizes = sizeof(sizes) / sizeof(int); + for (i = 0; i < nSizes; ++i) { if (res <= sizes[i]) { res = sizes[i]; break; } } + return res; +} + diff --git a/src/utils/module.c b/src/utils/module.c new file mode 100644 index 0000000..ddb5648 --- /dev/null +++ b/src/utils/module.c @@ -0,0 +1,193 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "module_wrap.h" +#include + +GF_EXPORT +GF_ModuleManager *gf_modules_new(const char *directory, GF_Config *config) +{ + GF_ModuleManager *tmp; + if (!directory || !strlen(directory) || (strlen(directory) > GF_MAX_PATH)) return NULL; + + GF_SAFEALLOC(tmp, GF_ModuleManager); + if (!tmp) return NULL; + strcpy(tmp->dir, directory); + + /*remove the final delimiter*/ + if (tmp->dir[strlen(tmp->dir)-1] == GF_PATH_SEPARATOR) tmp->dir[strlen(tmp->dir)-1] = 0; + + tmp->plug_list = gf_list_new(); + if (!tmp->plug_list) { + free(tmp); + return NULL; + } + tmp->cfg = config; + gf_modules_refresh(tmp); + return tmp; +} + +GF_EXPORT +void gf_modules_del(GF_ModuleManager *pm) +{ + ModuleInstance *inst; + if (!pm) return; + /*unload all modules*/ + while (gf_list_count(pm->plug_list)) { + inst = (ModuleInstance *) gf_list_get(pm->plug_list, 0); + gf_modules_free_module(inst); + gf_list_rem(pm->plug_list, 0); + } + gf_list_del(pm->plug_list); + free(pm); +} + +Bool gf_module_is_loaded(GF_ModuleManager *pm, char *filename) +{ + u32 i = 0; + ModuleInstance *inst; + while ( (inst = (ModuleInstance *) gf_list_enum(pm->plug_list, &i) ) ) { + if (!strcmp(inst->szName, filename)) return 1; + } + return 0; +} + +GF_EXPORT +u32 gf_modules_get_count(GF_ModuleManager *pm) +{ + if (!pm) return 0; + return gf_list_count(pm->plug_list); +} + + +GF_EXPORT +GF_BaseInterface *gf_modules_load_interface(GF_ModuleManager *pm, u32 whichplug, u32 InterfaceFamily) +{ + ModuleInstance *inst; + GF_BaseInterface *ifce; + + if (!pm) return NULL; + inst = (ModuleInstance *) gf_list_get(pm->plug_list, whichplug); + if (!inst) return NULL; + if (!gf_modules_load_library(inst)) return NULL; + + if (!inst->query_func || !inst->query_func(InterfaceFamily) ) goto err_exit; + + ifce = (GF_BaseInterface *) inst->load_func(InterfaceFamily); + /*sanity check*/ + if (!ifce) goto err_exit; + + if (!ifce->module_name || (ifce->InterfaceType != InterfaceFamily)) { + inst->destroy_func(ifce); + goto err_exit; + } + gf_list_add(inst->interfaces, ifce); + /*keep track of parent*/ + ifce->HPLUG = inst; + return ifce; + +err_exit: + /*this slows down loading of GPAC on CE-based devices*/ +#ifndef _WIN32_WCE + gf_modules_unload_library(inst); +#endif + return NULL; +} + + +GF_EXPORT +GF_BaseInterface *gf_modules_load_interface_by_name(GF_ModuleManager *pm, const char *plug_name, u32 InterfaceFamily) +{ + u32 i, count; + GF_BaseInterface *ifce; + count = gf_list_count(pm->plug_list); + for (i=0; imodule_name && !stricmp(ifce->module_name, plug_name)) return ifce; + /*check by file name*/ + if (!stricmp(((ModuleInstance *)ifce->HPLUG)->szName, plug_name)) return ifce; + gf_modules_close_interface(ifce); + } + return NULL; +} + +GF_EXPORT +GF_Err gf_modules_close_interface(GF_BaseInterface *ifce) +{ + ModuleInstance *par; + u32 i; + if (!ifce) return GF_BAD_PARAM; + par = (ModuleInstance *) ifce->HPLUG; + + if (!par || !ifce->InterfaceType) return GF_BAD_PARAM; + + i = gf_list_find(par->plugman->plug_list, par); + if (i<0) return GF_BAD_PARAM; + + i = gf_list_find(par->interfaces, ifce); + if (i<0) return GF_BAD_PARAM; + gf_list_rem(par->interfaces, (u32) i); + par->destroy_func(ifce); + gf_modules_unload_library(par); + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] interface %s unloaded\n", ifce->module_name)); + return GF_OK; +} + +GF_EXPORT +const char *gf_modules_get_option(GF_BaseInterface *ifce, const char *secName, const char *keyName) +{ + GF_Config *cfg; + if (!ifce || !ifce->HPLUG) return NULL; + cfg = ((ModuleInstance *)ifce->HPLUG)->plugman->cfg; + if (!cfg) return NULL; + return gf_cfg_get_key(cfg, secName, keyName); +} + +GF_EXPORT +GF_Err gf_modules_set_option(GF_BaseInterface *ifce, const char *secName, const char *keyName, const char *keyValue) +{ + GF_Config *cfg; + if (!ifce || !ifce->HPLUG) return GF_BAD_PARAM; + cfg = ((ModuleInstance *)ifce->HPLUG)->plugman->cfg; + if (!cfg) return GF_NOT_SUPPORTED; + return gf_cfg_set_key(cfg, secName, keyName, keyValue); +} + +GF_EXPORT +GF_Config *gf_modules_get_config(GF_BaseInterface *ifce) +{ + if (!ifce || !ifce->HPLUG) return NULL; + return ((ModuleInstance *)ifce->HPLUG)->plugman->cfg; +} + +GF_EXPORT +const char *gf_modules_get_file_name(GF_ModuleManager *pm, u32 i) +{ + ModuleInstance *inst = (ModuleInstance *) gf_list_get(pm->plug_list, i); + if (!inst) return NULL; + return inst->szName; +} + diff --git a/src/utils/module_wrap.h b/src/utils/module_wrap.h new file mode 100644 index 0000000..01f19df --- /dev/null +++ b/src/utils/module_wrap.h @@ -0,0 +1,79 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#ifndef _GF_MODULE_WRAP_H_ +#define _GF_MODULE_WRAP_H_ + +/* interface api*/ +typedef Bool (*QueryInterface) (u32 InterfaceType); +typedef void * (*LoadInterface) (u32 InterfaceType); +typedef void (*ShutdownInterface) (void *interface_obj); + + +typedef struct +{ + struct __tag_mod_man *plugman; + char szName[GF_MAX_PATH]; + GF_List *interfaces; + + /*library is loaded only when an interface is attached*/ + void *lib_handle; + QueryInterface query_func; + LoadInterface load_func; + ShutdownInterface destroy_func; +} ModuleInstance; + + +struct __tag_mod_man +{ + /*location of the modules*/ + char dir[GF_MAX_PATH]; + GF_List *plug_list; + GF_Config *cfg; + /*the one and only ssl instance used throughout the client engine*/ + void *ssl_inst; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/*returns 1 if a module with the same filename is already loaded*/ +Bool gf_module_is_loaded(GF_ModuleManager *pm, char *filename); + +/*these are OS specific*/ +void gf_modules_free_module(ModuleInstance *inst); +Bool gf_modules_load_library(ModuleInstance *inst); +void gf_modules_unload_library(ModuleInstance *inst); +u32 gf_modules_refresh(GF_ModuleManager *pm); + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_MODULE_WRAP_H_*/ + diff --git a/src/utils/os_divers.c b/src/utils/os_divers.c new file mode 100644 index 0000000..167fbdc --- /dev/null +++ b/src/utils/os_divers.c @@ -0,0 +1,1239 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#if defined(_WIN32_WCE) + +#include +#include + +#if !defined(__GNUC__) +#pragma comment(lib, "toolhelp") +#endif + +#elif defined(WIN32) + +#include +#include +#include +#include +#include + +#if !defined(__GNUC__) +#pragma comment(lib, "winmm") +#endif + +#else + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __BEOS__ +#include +#endif + + +#define SLEEP_ABS_SELECT 1 + +static u32 sys_start_time = 0; +#endif + + +#ifndef _WIN32_WCE +#include +#endif + + +#ifndef WIN32 +u32 gf_sys_clock() +{ + struct timeval now; + gettimeofday(&now, NULL); + return ( (now.tv_sec)*1000 + (now.tv_usec) / 1000) - sys_start_time; +} +#endif + + +void gf_sleep(u32 ms) +{ +#ifdef WIN32 + Sleep(ms); +#else + s32 sel_err; + struct timeval tv; + +#ifndef SLEEP_ABS_SELECT + u32 prev, now, elapsed; +#endif + +#ifdef SLEEP_ABS_SELECT + tv.tv_sec = ms/1000; + tv.tv_usec = (ms%1000)*1000; +#else + prev = gf_sys_clock(); +#endif + + do { + errno = 0; + +#ifndef SLEEP_ABS_SELECT + now = gf_sys_clock(); + elapsed = (now - prev); + if ( elapsed >= ms ) { + break; + } + prev = now; + ms -= elapsed; + tv.tv_sec = ms/1000; + tv.tv_usec = (ms%1000)*1000; +#endif + + sel_err = select(0, NULL, NULL, NULL, &tv); + } while ( sel_err && (errno == EINTR) ); +#endif +} + +#ifndef gettimeofday +#ifdef _WIN32_WCE + +/* + Conversion code for WinCE from pthreads WinCE + (FILETIME in Win32 is from jan 1, 1601) + time between jan 1, 1601 and jan 1, 1970 in units of 100 nanoseconds +*/ +#define TIMESPEC_TO_FILETIME_OFFSET (((LONGLONG)27111902 << 32) + (LONGLONG)3577643008) + +s32 gettimeofday(struct timeval *tp, void *tz) +{ + FILETIME ft; + SYSTEMTIME st; + s32 val; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + + val = (s32) ((*(LONGLONG *) &ft - TIMESPEC_TO_FILETIME_OFFSET) / 10000000); + tp->tv_sec = (u32) val; + val = (s32 ) ((*(LONGLONG *) &ft - TIMESPEC_TO_FILETIME_OFFSET - ((LONGLONG) val * (LONGLONG) 10000000)) * 100); + tp->tv_usec = val; + return 0; +} + +#elif defined(WIN32) + +s32 gettimeofday(struct timeval *tp, void *tz) +{ + struct _timeb timebuffer; + + _ftime( &timebuffer ); + tp->tv_sec = timebuffer.time; + tp->tv_usec = timebuffer.millitm * 1000; + return 0; +} +#endif + +#endif + +#ifdef _WIN32_WCE + +void CE_Assert(u32 valid, char *file, u32 line) +{ + if (!valid) { + char szBuf[2048]; + u16 wcBuf[2048]; + sprintf(szBuf, "File %s : line %d", file, line); + CE_CharToWide(szBuf, wcBuf); + MessageBox(NULL, wcBuf, _T("GPAC Assertion Failure"), MB_OK); + exit(EXIT_FAILURE); + } +} + +void CE_WideToChar(unsigned short *w_str, char *str) +{ + WideCharToMultiByte(CP_ACP, 0, w_str, -1, str, GF_MAX_PATH, NULL, NULL); +} + +void CE_CharToWide(char *str, unsigned short *w_str) +{ + MultiByteToWideChar(CP_ACP, 0, str, -1, w_str, GF_MAX_PATH); +} + + +#endif + +void gf_delete_file(char *fileName) +{ +#if defined(_WIN32_WCE) + TCHAR swzName[MAX_PATH]; + CE_CharToWide(fileName, swzName); + DeleteFile(swzName); +#elif defined(WIN32) + DeleteFile(fileName); +#else + remove(fileName); +#endif +} + + + +void gf_rand_init(Bool Reset) +{ + if (Reset) { + srand(1); + } else { +#if defined(_WIN32_WCE) + srand( (u32) GetTickCount() ); +#else + srand( (u32) time(NULL) ); +#endif + } +} + +u32 gf_rand() +{ + return rand(); +} + + +#ifndef GPAC_READ_ONLY +FILE *gf_temp_file_new() +{ +#if defined(_WIN32_WCE) + TCHAR pPath[MAX_PATH+1]; + TCHAR pTemp[MAX_PATH+1]; + if (!GetTempPath(MAX_PATH, pPath)) { + pPath[0] = '.'; + pPath[1] = '.'; + } + if (GetTempFileName(pPath, TEXT("git"), 0, pTemp)) + return _wfopen(pTemp, TEXT("w+b")); + + return NULL; +#elif defined(WIN32) + char tmp[MAX_PATH], t_file[100]; + FILE *res = tmpfile(); + if (res) return res; + /*tmpfile() may fail under vista ...*/ + if (!GetEnvironmentVariable("TEMP",tmp,MAX_PATH)) return NULL; + sprintf(t_file, "\\gpac_%08x.tmp", (u32) tmp); + strcat(tmp, t_file); + return fopen(tmp, "w+b"); +#else + return tmpfile(); +#endif +} +#endif + + +void gf_utc_time_since_1970(u32 *sec, u32 *msec) +{ +#if defined (WIN32) && !defined(_WIN32_WCE) + struct _timeb tb; + _ftime( &tb ); + *sec = tb.time; + *msec = tb.millitm; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + *sec = tv.tv_sec; + *msec = tv.tv_usec/1000; +#endif +} + +void gf_get_user_name(char *buf, u32 buf_size) +{ + strcpy(buf, "mpeg4-user"); + +#if 0 + s32 len; + char *t; + strcpy(buf, ""); + len = 1024; + GetUserName(buf, &len); + if (!len) { + t = getenv("USER"); + if (t) strcpy(buf, t); + } +#endif +#if 0 + struct passwd *pw; + pw = getpwuid(getuid()); + strcpy(buf, ""); + if (pw && pw->pw_name) strcpy(name, pw->pw_name); +#endif +} + + +/*enumerate directories*/ +GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item enum_dir_fct, void *cbck, const char *filter) +{ + char item_path[GF_MAX_PATH]; + +#if defined(_WIN32_WCE) + char _path[GF_MAX_PATH]; + unsigned short path[GF_MAX_PATH]; + unsigned short w_filter[GF_MAX_PATH]; + char file[GF_MAX_PATH]; +#else + char path[GF_MAX_PATH], *file; +#endif + +#ifdef WIN32 + WIN32_FIND_DATA FindData; + HANDLE SearchH; +#else + DIR *the_dir; + struct dirent* the_file; + struct stat st; +#endif + + if (!dir || !enum_dir_fct) return GF_BAD_PARAM; +#if defined (_WIN32_WCE) + if (dir[strlen(dir) - 1] == GF_PATH_SEPARATOR) { + sprintf(_path, "%s*", dir); + } else { + sprintf(_path, "%s%c*", dir, GF_PATH_SEPARATOR); + } + CE_CharToWide(_path, path); + CE_CharToWide((char *)filter, w_filter); +#elif defined(WIN32) + if (dir[strlen(dir) - 1] == GF_PATH_SEPARATOR) { + sprintf(path, "%s*", dir); + } else { + sprintf(path, "%s%c*", dir, GF_PATH_SEPARATOR); + } +#else + strcpy(path, dir); + if (path[strlen(path)-1] != '/') strcat(path, "/"); +#endif + +#ifdef WIN32 + SearchH= FindFirstFile(path, &FindData); + if (SearchH == INVALID_HANDLE_VALUE) return GF_IO_ERR; + +#if defined (_WIN32_WCE) + _path[strlen(_path)-1] = 0; +#else + path[strlen(path)-1] = 0; +#endif + + while (SearchH != INVALID_HANDLE_VALUE) { + +#else + + the_dir = opendir(path); + if (the_dir == NULL) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot open directory %s for enumeration\n", path)); + return GF_IO_ERR; + } + the_file = readdir(the_dir); + while (the_file) { + +#endif + +#if defined (_WIN32_WCE) + if (!wcscmp(FindData.cFileName, _T(".") )) goto next; + if (!wcscmp(FindData.cFileName, _T("..") )) goto next; +#elif defined(WIN32) + if (!strcmp(FindData.cFileName, ".")) goto next; + if (!strcmp(FindData.cFileName, "..")) goto next; +#else + if (!strcmp(the_file->d_name, "..")) goto next; + if (the_file->d_name[0] == '.') goto next; +#endif + +#ifdef WIN32 + if (!enum_directory && (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) goto next; + if (enum_directory && !(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) goto next; +#endif + + if (filter) { +#if defined (_WIN32_WCE) + short ext[30]; + short *sep = wcsrchr(FindData.cFileName, (wchar_t) '.'); + if (!sep) goto next; + wcscpy(ext, sep+1); + wcslwr(ext); + if (!wcsstr(w_filter, ext)) goto next; +#elif defined(WIN32) + char ext[30]; + char *sep = strrchr(FindData.cFileName, '.'); + if (!sep) goto next; + strcpy(ext, sep+1); + strlwr(ext); + if (!strstr(filter, ext)) goto next; +#else + char ext[30]; + char *sep = strrchr(the_file->d_name, '.'); + if (!sep) goto next; + strcpy(ext, sep+1); + strlwr(ext); + if (!strstr(filter, sep+1)) goto next; +#endif + } + +#if defined (_WIN32_WCE) + CE_WideToChar(FindData.cFileName, file); + strcpy(item_path, _path); + strcat(item_path, file); +#elif defined(WIN32) + strcpy(item_path, path); + strcat(item_path, FindData.cFileName); + file = FindData.cFileName; +#else + strcpy(item_path, path); + strcat(item_path, the_file->d_name); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Checking file %s for enum\n", item_path)); + + if (stat( item_path, &st ) != 0) goto next; + if (enum_directory && ( (st.st_mode & S_IFMT) != S_IFDIR)) goto next; + if (!enum_directory && ((st.st_mode & S_IFMT) == S_IFDIR)) goto next; + file = the_file->d_name; +#endif + if (enum_dir_fct(cbck, file, item_path)) { +#ifdef WIN32 + FindClose(SearchH); +#endif + break; + } + +next: +#ifdef WIN32 + if (!FindNextFile(SearchH, &FindData)) { + FindClose(SearchH); + break; + } +#else + the_file = readdir(the_dir); +#endif + } +#ifndef WIN32 + closedir(the_dir); +#endif + return GF_OK; +} + + +#ifndef WIN32 +char * my_str_upr(char *str) +{ + u32 i; + for (i=0; i +#include + +Bool gf_prompt_has_input() +{ + return kbhit(); +} +char gf_prompt_get_char() +{ + return getchar(); +} +void gf_prompt_set_echo_off(Bool echo_off) +{ + DWORD flags; + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(hStdin, &flags); + if (echo_off) flags &= ~ENABLE_ECHO_INPUT; + else flags |= ENABLE_ECHO_INPUT; + SetConsoleMode(hStdin, flags); +} +#endif +#else +/*linux kbhit/getchar- borrowed on debian mailing lists, (author Mike Brownlow)*/ +#include + +static struct termios t_orig, t_new; +static s32 ch_peek = -1; + +static void init_keyboard() +{ + tcgetattr(0, &t_orig); + t_new = t_orig; + t_new.c_lflag &= ~ICANON; + t_new.c_lflag &= ~ECHO; + t_new.c_lflag &= ~ISIG; + t_new.c_cc[VMIN] = 1; + t_new.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t_new); +} +static void close_keyboard(Bool new_line) +{ + tcsetattr(0,TCSANOW, &t_orig); + if (new_line) fprintf(stdout, "\n"); +} + +void gf_prompt_set_echo_off(Bool echo_off) +{ + init_keyboard(); + if (echo_off) t_orig.c_lflag &= ~ECHO; + else t_orig.c_lflag |= ECHO; + close_keyboard(0); +} + +Bool gf_prompt_has_input() +{ + u8 ch; + s32 nread; + + init_keyboard(); + if (ch_peek != -1) return 1; + t_new.c_cc[VMIN]=0; + tcsetattr(0, TCSANOW, &t_new); + nread = read(0, &ch, 1); + t_new.c_cc[VMIN]=1; + tcsetattr(0, TCSANOW, &t_new); + if(nread == 1) { + ch_peek = ch; + return 1; + } + close_keyboard(0); + return 0; +} + +char gf_prompt_get_char() +{ + char ch; + if (ch_peek != -1) { + ch = ch_peek; + ch_peek = -1; + close_keyboard(1); + return ch; + } + read(0,&ch,1); + close_keyboard(1); + return ch; +} + +#endif + + + + +static u32 sys_init = 0; +static u32 last_update_time = 0; +static u64 last_process_k_u_time = 0; +GF_SystemRTInfo the_rti; + + +#if defined(_WIN32_WCE) +static LARGE_INTEGER frequency , init_counter; +static u64 last_total_k_u_time = 0; +static u32 mem_usage_at_startup = 0; + + +#ifndef GetCurrentPermissions +DWORD GetCurrentPermissions(); +#endif +#ifndef SetProcPermissions +void SetProcPermissions(DWORD ); +#endif + +#elif defined(WIN32) +static LARGE_INTEGER frequency , init_counter; +static u64 last_proc_idle_time = 0; +static u64 last_proc_k_u_time = 0; + +static HINSTANCE psapi_hinst = NULL; +typedef BOOL(WINAPI* NTGetSystemTimes)(VOID *,VOID *,VOID *); +NTGetSystemTimes MyGetSystemTimes = NULL; +typedef BOOL(WINAPI* NTGetProcessMemoryInfo)(HANDLE,VOID *,DWORD); +NTGetProcessMemoryInfo MyGetProcessMemoryInfo = NULL; +typedef int(WINAPI* NTQuerySystemInfo)(ULONG,PVOID,ULONG,PULONG); +NTQuerySystemInfo MyQuerySystemInfo = NULL; + +#ifndef PROCESS_MEMORY_COUNTERS +typedef struct _PROCESS_MEMORY_COUNTERS +{ + DWORD cb; + DWORD PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; +} PROCESS_MEMORY_COUNTERS; +#endif + +#ifndef SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION +typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION +{ + LARGE_INTEGER IdleTime; + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER Reserved1[2]; + ULONG Reserved2; +} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; +#endif + +#else +static u64 last_cpu_u_k_time = 0; +static u64 last_cpu_idle_time = 0; +static u64 mem_at_startup = 0; + +#endif + +#ifdef WIN32 +static u32 (*OS_GetSysClock)(); + +u32 gf_sys_clock() +{ + return OS_GetSysClock(); +} +#endif + + +#if defined(_WIN32_WCE) +static u32 OS_GetSysClockHIGHRES() +{ + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + now.QuadPart -= init_counter.QuadPart; + return (u32) ((now.QuadPart * 1000) / frequency.QuadPart); +} + +static u32 OS_GetSysClockNORMAL() { return GetTickCount(); } + +#elif defined(WIN32) + +static u32 OS_GetSysClockHIGHRES() +{ + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + now.QuadPart -= init_counter.QuadPart; + return (u32) (now.QuadPart * 1000 / frequency.QuadPart); +} + +u32 OS_GetSysClockNORMAL() { return timeGetTime(); } + +#endif + + +void gf_sys_init() +{ + if (!sys_init) { +#if defined(WIN32) + +#if defined(_WIN32_WCE) + MEMORYSTATUS ms; +#endif + frequency.QuadPart = 0; + /*clock setup*/ + if (QueryPerformanceFrequency(&frequency)) { + QueryPerformanceCounter(&init_counter); + OS_GetSysClock = OS_GetSysClockHIGHRES; + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] using WIN32 performance timer\n")); + } else { +#ifndef _WIN32_WCE + timeBeginPeriod(1); +#endif + OS_GetSysClock = OS_GetSysClockNORMAL; + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] using WIN32 regular timer\n")); + } + + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] checking for run-time info tools")); +#if defined(_WIN32_WCE) + last_total_k_u_time = last_process_k_u_time = 0; + last_update_time = 0; + memset(&the_rti, 0, sizeof(GF_SystemRTInfo)); + the_rti.pid = GetCurrentProcessId(); + GlobalMemoryStatus(&ms); + mem_usage_at_startup = ms.dwAvailPhys; +#else + /*cpu usage tools are buried in win32 dlls...*/ + MyGetSystemTimes = (NTGetSystemTimes) GetProcAddress(GetModuleHandle("kernel32.dll"), "GetSystemTimes"); + if (!MyGetSystemTimes) { + MyQuerySystemInfo = (NTQuerySystemInfo) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation"); + if (MyQuerySystemInfo) { + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, (" - CPU: QuerySystemInformation")); + } + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, (" - CPU: GetSystemsTimes")); + } + psapi_hinst = LoadLibrary("psapi.dll"); + MyGetProcessMemoryInfo = (NTGetProcessMemoryInfo) GetProcAddress(psapi_hinst, "GetProcessMemoryInfo"); + if (MyGetProcessMemoryInfo) { + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, (" - memory: GetProcessMemoryInfo")); + } + last_process_k_u_time = last_proc_idle_time = last_proc_k_u_time = 0; + last_update_time = 0; + memset(&the_rti, 0, sizeof(GF_SystemRTInfo)); + the_rti.pid = GetCurrentProcessId(); +#endif + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("\n")); + +#else + /*linux threads...*/ + last_process_k_u_time = 0; + last_cpu_u_k_time = last_cpu_idle_time = 0; + last_update_time = 0; + memset(&the_rti, 0, sizeof(GF_SystemRTInfo)); + the_rti.pid = getpid(); + sys_start_time = gf_sys_clock(); +#endif + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] process id %d\n", the_rti.pid)); + +#ifndef _WIN32_WCE + setlocale( LC_NUMERIC, "C" ); +#endif + } + sys_init += 1; +} + +void gf_sys_close() +{ + if (sys_init > 0) { + sys_init --; + if (sys_init) return; + /*prevent any call*/ + last_update_time = 0xFFFFFFFF; + +#if defined(WIN32) && !defined(_WIN32_WCE) + if (!frequency.QuadPart) timeEndPeriod(1); + + MyGetSystemTimes = NULL; + MyGetProcessMemoryInfo = NULL; + MyQuerySystemInfo = NULL; + if (psapi_hinst) FreeLibrary(psapi_hinst); + psapi_hinst = NULL; +#endif + } +} + +extern size_t gpac_allocated_memory; +extern size_t gpac_nb_alloc_blocs; + +/*CPU and Memory Usage*/ + +#ifdef WIN32 + +Bool gf_sys_get_rti(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) +{ +#if defined(_WIN32_WCE) + THREADENTRY32 tentry; + u64 total_cpu_time, process_cpu_time; + DWORD orig_perm; +#endif + MEMORYSTATUS ms; + u64 creation, exit, kernel, user, process_k_u_time, proc_idle_time, proc_k_u_time; + u32 nb_threads, entry_time; + HANDLE hSnapShot; + + assert(sys_init); + + if (!rti) return 0; + + proc_idle_time = proc_k_u_time = process_k_u_time = 0; + nb_threads = 0; + + entry_time = gf_sys_clock(); + if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 0; + } + + if (flags & GF_RTI_SYSTEM_MEMORY_ONLY) { + memset(rti, 0, sizeof(GF_SystemRTInfo)); + rti->sampling_instant = last_update_time; + GlobalMemoryStatus(&ms); + rti->physical_memory = ms.dwTotalPhys; + rti->physical_memory_avail = ms.dwAvailPhys; + rti->gpac_memory = (u64) gpac_allocated_memory; + return 1; + } + +#if defined (_WIN32_WCE) + + total_cpu_time = process_cpu_time = 0; + + /*get a snapshot of all running threads*/ + orig_perm = GetCurrentPermissions(); + SetProcPermissions(0xFFFFFFFF); + hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hSnapShot) { + tentry.dwSize = sizeof(THREADENTRY32); + the_rti.thread_count = 0; + /*note we always act as if GF_RTI_ALL_PROCESSES_TIMES flag is set, since there is no other way + to enumerate threads from a process, and GetProcessTimes doesn't exist on CE*/ + if (Thread32First(hSnapShot, &tentry)) { + do { + /*get thread times*/ + if (GetThreadTimes( (HANDLE) tentry.th32ThreadID, (FILETIME *) &creation, (FILETIME *) &exit, (FILETIME *) &kernel, (FILETIME *) &user)) { + total_cpu_time += user + kernel; + if (tentry.th32OwnerProcessID==the_rti.pid) { + process_cpu_time += user + kernel; + the_rti.thread_count ++; + } + } + } while (Thread32Next(hSnapShot, &tentry)); + } + CloseToolhelp32Snapshot(hSnapShot); + } + + if (flags & GF_RTI_PROCESS_MEMORY) { + HEAPLIST32 hlentry; + HEAPENTRY32 hentry; + the_rti.process_memory = 0; + hlentry.dwSize = sizeof(HEAPLIST32); + hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, the_rti.pid); + if (hSnapShot && Heap32ListFirst(hSnapShot, &hlentry)) { + do { + hentry.dwSize = sizeof(hentry); + if (Heap32First(hSnapShot, &hentry, hlentry.th32ProcessID, hlentry.th32HeapID)) { + do { + the_rti.process_memory += hentry.dwBlockSize; + } while (Heap32Next(hSnapShot, &hentry)); + } + } while (Heap32ListNext(hSnapShot, &hlentry)); + } + CloseToolhelp32Snapshot(hSnapShot); + } + SetProcPermissions(orig_perm); + total_cpu_time /= 10; + process_cpu_time /= 10; + +#else + /*XP-SP1 and Win2003 servers only have GetSystemTimes support. This will give a better estimation + of CPU usage since we can take into account the idle time*/ + if (MyGetSystemTimes) { + u64 u_time; + MyGetSystemTimes(&proc_idle_time, &proc_k_u_time, &u_time); + proc_k_u_time += u_time; + proc_idle_time /= 10; + proc_k_u_time /= 10; + } + /*same rq for NtQuerySystemInformation*/ + else if (MyQuerySystemInfo) { + DWORD ret; + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info; + MyQuerySystemInfo(0x8 /*SystemProcessorPerformanceInformation*/, &info, sizeof(info), &ret); + if (ret && (ret<=sizeof(info))) { + proc_idle_time = info.IdleTime.QuadPart / 10; + proc_k_u_time = (info.KernelTime.QuadPart + info.UserTime.QuadPart) / 10; + } + } + /*no special API available, ONLY FETCH TIMES if requested (may eat up some time)*/ + else if (flags & GF_RTI_ALL_PROCESSES_TIMES) { + PROCESSENTRY32 pentry; + /*get a snapshot of all running threads*/ + hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (!hSnapShot) return 0; + pentry.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(hSnapShot, &pentry)) { + do { + HANDLE procH = NULL; + if (pentry.th32ProcessID) procH = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pentry.th32ProcessID); + if (procH && GetProcessTimes(procH, (FILETIME *) &creation, (FILETIME *) &exit, (FILETIME *) &kernel, (FILETIME *) &user) ) { + user += kernel; + proc_k_u_time += user; + if (pentry.th32ProcessID==the_rti.pid) { + process_k_u_time = user; + nb_threads = pentry.cntThreads; + } + } + if (procH) CloseHandle(procH); + } while (Process32Next(hSnapShot, &pentry)); + } + CloseHandle(hSnapShot); + proc_k_u_time /= 10; + } + + + if (!process_k_u_time) { + HANDLE procH = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, the_rti.pid); + if (procH && GetProcessTimes(procH, (FILETIME *) &creation, (FILETIME *) &exit, (FILETIME *) &kernel, (FILETIME *) &user) ) { + process_k_u_time = user + kernel; + } + if (procH) CloseHandle(procH); + if (!process_k_u_time) return 0; + } + process_k_u_time /= 10; + + /*this won't cost a lot*/ + if (MyGetProcessMemoryInfo) { + PROCESS_MEMORY_COUNTERS pmc; + HANDLE procH = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, the_rti.pid); + MyGetProcessMemoryInfo(procH, &pmc, sizeof (pmc)); + the_rti.process_memory = pmc.WorkingSetSize; + if (procH) CloseHandle(procH); + } + /*THIS IS VERY HEAVY (eats up mem and time) - only perform if requested*/ + else if (flags & GF_RTI_PROCESS_MEMORY) { + HEAPLIST32 hlentry; + HEAPENTRY32 hentry; + the_rti.process_memory = 0; + hlentry.dwSize = sizeof(HEAPLIST32); + hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, the_rti.pid); + if (hSnapShot && Heap32ListFirst(hSnapShot, &hlentry)) { + do { + hentry.dwSize = sizeof(hentry); + if (Heap32First(&hentry, hlentry.th32ProcessID, hlentry.th32HeapID)) { + do { + the_rti.process_memory += hentry.dwBlockSize; + } while (Heap32Next(&hentry)); + } + } while (Heap32ListNext(hSnapShot, &hlentry)); + } + CloseHandle(hSnapShot); + } +#endif + + the_rti.sampling_instant = last_update_time; + + if (last_update_time) { + the_rti.sampling_period_duration = entry_time - last_update_time; + the_rti.process_cpu_time_diff = (u32) ((process_k_u_time - last_process_k_u_time)/1000); + +#if defined(_WIN32_WCE) + the_rti.total_cpu_time_diff = (u32) ((total_cpu_time - last_total_k_u_time)/1000); + /*we're not that accurate....*/ + if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) + the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; + + /*rough values*/ + the_rti.cpu_idle_time = the_rti.sampling_period_duration - the_rti.total_cpu_time_diff; + the_rti.total_cpu_usage = (u32) (100 * the_rti.total_cpu_time_diff / the_rti.sampling_period_duration); + the_rti.process_cpu_usage = (u32) (100*the_rti.process_cpu_time_diff / (the_rti.total_cpu_time_diff + the_rti.cpu_idle_time) ); + +#else + /*oops, we have no choice but to assume 100% cpu usage during this period*/ + if (!proc_k_u_time) { + the_rti.total_cpu_time_diff = the_rti.sampling_period_duration; + proc_k_u_time = last_proc_k_u_time + the_rti.sampling_period_duration; + the_rti.cpu_idle_time = 0; + the_rti.total_cpu_usage = 100; + if (the_rti.sampling_period_duration) + the_rti.process_cpu_usage = (u32) (100*the_rti.process_cpu_time_diff / the_rti.sampling_period_duration); + } else { + u64 samp_sys_time, idle; + the_rti.total_cpu_time_diff = (u32) ((proc_k_u_time - last_proc_k_u_time)/1000); + + /*we're not that accurate....*/ + if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) { + the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; + } + + if (!proc_idle_time) + proc_idle_time = last_proc_idle_time + (the_rti.sampling_period_duration - the_rti.total_cpu_time_diff); + + samp_sys_time = proc_k_u_time - last_proc_k_u_time; + idle = proc_idle_time - last_proc_idle_time; + the_rti.cpu_idle_time = (u32) (idle/1000); + if (samp_sys_time) { + the_rti.total_cpu_usage = (u32) ( (samp_sys_time - idle) / (samp_sys_time / 100) ); + the_rti.process_cpu_usage = (u32) (100*the_rti.process_cpu_time_diff / (samp_sys_time/1000)); + } + } +#endif + } + last_update_time = entry_time; + last_process_k_u_time = process_k_u_time; + + GlobalMemoryStatus(&ms); + the_rti.physical_memory = ms.dwTotalPhys; + the_rti.gpac_memory = (u64) gpac_allocated_memory; + the_rti.physical_memory_avail = ms.dwAvailPhys; + +#if defined(_WIN32_WCE) + last_total_k_u_time = total_cpu_time; + if (!the_rti.process_memory) the_rti.process_memory = mem_usage_at_startup - ms.dwAvailPhys; +#else + last_proc_idle_time = proc_idle_time; + last_proc_k_u_time = proc_k_u_time; +#endif + + if (!the_rti.gpac_memory) the_rti.gpac_memory = the_rti.process_memory; + + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 1; +} + + +#else + +Bool gf_sys_get_rti(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) +{ + u32 entry_time; + u64 process_u_k_time; + u32 u_k_time, idle_time; +#if 0 + char szProc[100]; +#endif + char line[2048]; + FILE *f; + + assert(sys_init); + + entry_time = gf_sys_clock(); + if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 0; + } + + u_k_time = idle_time = 0; + f = fopen("/proc/stat", "r"); + if (f) { + u32 k_time, nice_time, u_time; + if (fgets(line, 128, f) != NULL) { + if (sscanf(line, "cpu %u %u %u %u\n", &u_time, &k_time, &nice_time, &idle_time) == 4) { + u_k_time = u_time + k_time + nice_time; + } + } + fclose(f); + } + + process_u_k_time = 0; + the_rti.process_memory = 0; + + /*FIXME? under LinuxThreads this will only fetch stats for the calling thread, we would have to enumerate /proc to get + the complete CPU usage of all therads of the process...*/ +#if 0 + sprintf(szProc, "/proc/%d/stat", the_rti.pid); + f = fopen(szProc, "r"); + if (f) { + fflush(f); + if (fgets(line, 2048, f) != NULL) { + char state; + char *start; + long cutime, cstime, priority, nice, itrealvalue, rss; + int exit_signal, processor; + unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime,starttime, vsize, rlim, startcode, endcode, startstack, kstkesp, kstkeip, signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap, rem; + int ppid, pgrp ,session, tty_nr, tty_pgrp, res; + start = strchr(line, ')'); + if (start) start += 2; + else { + start = strchr(line, ' '); + start++; + } + res = sscanf(start,"%c %d %d %d %d %d %lu %lu %lu %lu \ +%lu %lu %lu %ld %ld %ld %ld %ld %ld %lu \ +%lu %ld %lu %lu %lu %lu %lu %lu %lu %lu \ +%lu %lu %lu %lu %lu %d %d", + &state, &ppid, &pgrp, &session, &tty_nr, &tty_pgrp, &flags, &minflt, &cminflt, &majflt, + &cmajflt, &utime, &stime, &cutime, &cstime, &priority, &nice, &itrealvalue, &rem, &starttime, + &vsize, &rss, &rlim, &startcode, &endcode, &startstack, &kstkesp, &kstkeip, &signal, &blocked, + &sigignore, &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor); + + if (res) process_u_k_time = (u64) (cutime + cstime); + else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] PROC %s parse error\n", szProc)); + } + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] error reading pid/stat\n\n", szProc)); + } + fclose(f); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open %s\n", szProc)); + } + sprintf(szProc, "/proc/%d/status", the_rti.pid); + f = fopen(szProc, "r"); + if (f) { + while (fgets(line, 1024, f) != NULL) { + if (!strnicmp(line, "VmSize:", 7)) { + sscanf(line, "VmSize: %lld kB", &the_rti.process_memory); + the_rti.process_memory *= 1024; + } + } + fclose(f); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open %s\n", szProc)); + } +#endif + + + the_rti.physical_memory = the_rti.physical_memory_avail = 0; + f = fopen("/proc/meminfo", "r"); + if (f) { + while (fgets(line, 1024, f) != NULL) { + if (!strnicmp(line, "MemTotal:", 9)) { + sscanf(line, "MemTotal: %lld kB", &the_rti.physical_memory); + the_rti.physical_memory *= 1024; + }else if (!strnicmp(line, "MemFree:", 8)) { + sscanf(line, "MemFree: %lld kB", &the_rti.physical_memory_avail); + the_rti.physical_memory_avail *= 1024; + break; + } + } + fclose(f); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open /proc/meminfo\n")); + } + + + the_rti.sampling_instant = last_update_time; + + if (last_update_time) { + the_rti.sampling_period_duration = (entry_time - last_update_time); + the_rti.process_cpu_time_diff = (process_u_k_time - last_process_k_u_time) * 10; + + /*oops, we have no choice but to assume 100% cpu usage during this period*/ + if (!u_k_time) { + the_rti.total_cpu_time_diff = the_rti.sampling_period_duration; + u_k_time = last_cpu_u_k_time + the_rti.sampling_period_duration; + the_rti.cpu_idle_time = 0; + the_rti.total_cpu_usage = 100; + if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; + the_rti.process_cpu_usage = (u32) ( 100 * the_rti.process_cpu_time_diff / the_rti.sampling_period_duration); + } else { + u64 samp_sys_time; + /*move to ms (/proc/stat gives times in 100 ms unit*/ + the_rti.total_cpu_time_diff = (u_k_time - last_cpu_u_k_time)*10; + + /*we're not that accurate....*/ + if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) + the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; + + + if (!idle_time) idle_time = (the_rti.sampling_period_duration - the_rti.total_cpu_time_diff)/10; + samp_sys_time = u_k_time - last_cpu_u_k_time; + the_rti.cpu_idle_time = idle_time - last_cpu_idle_time; + the_rti.total_cpu_usage = (u32) ( 100 * samp_sys_time / (the_rti.cpu_idle_time + samp_sys_time ) ); + /*move to ms (/proc/stat gives times in 100 ms unit*/ + the_rti.cpu_idle_time *= 10; + if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; + the_rti.process_cpu_usage = (u32) ( 100 * the_rti.process_cpu_time_diff / (the_rti.cpu_idle_time + 10*samp_sys_time ) ); + } + } else { + mem_at_startup = the_rti.physical_memory_avail; + } + the_rti.process_memory = mem_at_startup - the_rti.physical_memory_avail; + the_rti.gpac_memory = gpac_allocated_memory; + + last_process_k_u_time = process_u_k_time; + last_cpu_idle_time = idle_time; + last_cpu_u_k_time = u_k_time; + last_update_time = entry_time; + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 1; +} + +#endif + + +Bool gf_sys_get_battery_state(Bool *onBattery, u32 *onCharge, u32*level) +{ +#if defined(WIN32) && !defined(_WIN32_WCE) + SYSTEM_POWER_STATUS sps; + GetSystemPowerStatus(&sps); + if (onBattery) *onBattery = sps.ACLineStatus; + if (onCharge) *onCharge = (sps.BatteryFlag & BATTERY_FLAG_CHARGING) ? 1 : 0; + if (level) *level = sps.BatteryLifePercent; +#endif + return 1; +} diff --git a/src/utils/os_module.c b/src/utils/os_module.c new file mode 100644 index 0000000..de8d9bd --- /dev/null +++ b/src/utils/os_module.c @@ -0,0 +1,214 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "module_wrap.h" + +#if defined(WIN32) || defined(_WIN32_WCE) +#include +#else +#include +#include +#include +#endif + + +/*delete all interfaces loaded on object*/ +void gf_modules_free_module(ModuleInstance *inst) +{ + void *objinterface; + while (gf_list_count(inst->interfaces)) { + objinterface = gf_list_get(inst->interfaces, 0); + gf_list_rem(inst->interfaces, 0); + inst->destroy_func(objinterface); + } + +#ifdef WIN32 + if (inst->lib_handle) FreeLibrary(inst->lib_handle); +#else + if (inst->lib_handle) dlclose(inst->lib_handle); +#endif + gf_list_del(inst->interfaces); + free(inst); +} + + +Bool gf_modules_load_library(ModuleInstance *inst) +{ +#ifdef _WIN32_WCE + char s_path[GF_MAX_PATH]; + unsigned short path[GF_MAX_PATH]; +#else + char path[GF_MAX_PATH]; +#ifndef WIN32 + s32 _flags; +#endif + +#endif + + if (inst->lib_handle) return 1; + +#ifdef _WIN32_WCE + sprintf(s_path, "%s%c%s", inst->plugman->dir, GF_PATH_SEPARATOR, inst->szName); + CE_CharToWide(s_path, path); +#else + sprintf(path, "%s%c%s", inst->plugman->dir, GF_PATH_SEPARATOR, inst->szName); +#endif + +#ifdef WIN32 + inst->lib_handle = LoadLibrary(path); + if (!inst->lib_handle) { +#ifdef _WIN32_WCE + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot load module file %s\n", s_path)); +#else + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot load module file %s\n", path)); +#endif + return 0; + } +#if defined(_WIN32_WCE) + inst->query_func = (QueryInterface) GetProcAddress(inst->lib_handle, _T("QueryInterface")); + inst->load_func = (LoadInterface) GetProcAddress(inst->lib_handle, _T("LoadInterface")); + inst->destroy_func = (ShutdownInterface) GetProcAddress(inst->lib_handle, _T("ShutdownInterface")); +#else + inst->query_func = (QueryInterface) GetProcAddress(inst->lib_handle, "QueryInterface"); + inst->load_func = (LoadInterface) GetProcAddress(inst->lib_handle, "LoadInterface"); + inst->destroy_func = (ShutdownInterface) GetProcAddress(inst->lib_handle, "ShutdownInterface"); +#endif + +#else + +#ifdef RTLD_GLOBAL + _flags =RTLD_LAZY | RTLD_GLOBAL; +#else + _flags =RTLD_LAZY; +#endif + inst->lib_handle = dlopen(path, _flags); + if (!inst->lib_handle) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot load module file %s, error is %s\n", path, dlerror())); + return 0; + } + inst->query_func = (QueryInterface) dlsym(inst->lib_handle, "QueryInterface"); + inst->load_func = (LoadInterface) dlsym(inst->lib_handle, "LoadInterface"); + inst->destroy_func = (ShutdownInterface) dlsym(inst->lib_handle, "ShutdownInterface"); +#endif + return 1; +} + +void gf_modules_unload_library(ModuleInstance *inst) +{ + if (!inst->lib_handle || gf_list_count(inst->interfaces)) return; +#ifdef WIN32 + FreeLibrary(inst->lib_handle); +#else + dlclose(inst->lib_handle); +#endif + inst->lib_handle = NULL; + inst->load_func = NULL; + inst->destroy_func = NULL; + inst->query_func = NULL; +} + + +Bool enum_modules(void *cbck, char *item_name, char *item_path) +{ + ModuleInstance *inst; +#if CHECK_MODULE + QueryInterface query_func; + LoadInterface load_func; + ShutdownInterface del_func; +#ifdef WIN32 + HMODULE ModuleLib; +#else + void *ModuleLib; + s32 _flags; +#endif + +#endif + + GF_ModuleManager *pm = cbck; + + if (strstr(item_name, "nposmozilla")) return 0; + if (strncmp(item_name, "gm_", 3)) return 0; + if (gf_module_is_loaded(pm, item_name) ) return 0; + +#if CHECK_MODULE + +#ifdef WIN32 + ModuleLib = LoadLibrary(item_path); + if (!ModuleLib) return 0; + +#ifdef _WIN32_WCE + query_func = (QueryInterface) GetProcAddress(ModuleLib, _T("QueryInterface")); + load_func = (LoadInterface) GetProcAddress(ModuleLib, _T("LoadInterface")); + del_func = (ShutdownInterface) GetProcAddress(ModuleLib, _T("ShutdownInterface")); +#else + query_func = (QueryInterface) GetProcAddress(ModuleLib, "QueryInterface"); + load_func = (LoadInterface) GetProcAddress(ModuleLib, "LoadInterface"); + del_func = (ShutdownInterface) GetProcAddress(ModuleLib, "ShutdownInterface"); +#endif + FreeLibrary(ModuleLib); + +#else + +#ifdef RTLD_GLOBAL + _flags =RTLD_LAZY | RTLD_GLOBAL; +#else + _flags =RTLD_LAZY; +#endif + + ModuleLib = dlopen(file, _flags); + if (!ModuleLib) goto next; + + query_func = (QueryInterface) dlsym(ModuleLib, "QueryInterface"); + load_func = (LoadInterface) dlsym(ModuleLib, "LoadInterface"); + del_func = (ShutdownInterface) dlsym(ModuleLib, "ShutdownInterface"); + dlclose(ModuleLib); +#endif + + if (!load_func || !query_func || !del_func) return 0; +#endif + + + GF_SAFEALLOC(inst, ModuleInstance); + inst->interfaces = gf_list_new(); + inst->plugman = pm; + strcpy(inst->szName, item_name); + gf_list_add(pm->plug_list, inst); + return 0; +} + +/*refresh modules - note we don't check for deleted modules but since we've open them the OS should forbid delete*/ +u32 gf_modules_refresh(GF_ModuleManager *pm) +{ + if (!pm) return 0; + +#ifdef WIN32 + gf_enum_directory(pm->dir, 0, enum_modules, pm, ".dll"); +#elif defined(__APPLE__) + gf_enum_directory(pm->dir, 0, enum_modules, pm, ".dylib"); +#else + gf_enum_directory(pm->dir, 0, enum_modules, pm, ".so"); +#endif + + return gf_list_count(pm->plug_list); +} diff --git a/src/utils/os_net.c b/src/utils/os_net.c new file mode 100644 index 0000000..d8ce560 --- /dev/null +++ b/src/utils/os_net.c @@ -0,0 +1,1446 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#if defined(WIN32) || defined(_WIN32_WCE) + +#ifdef _WIN32_WCE +#include + +#if !defined(__GNUC__) +#pragma comment(lib, "winsock") +#endif + +#else + +#include +#include +#include + +#if !defined(__GNUC__) +#pragma comment(lib, "ws2_32") +#endif + +#endif + +#include + +#if !defined(__GNUC__) + +#if defined(IPV6_MULTICAST_IF___dis) +#define GPAC_HAS_IPV6 1 +#pragma message("Using WinSock IPV6") +#else +#undef GPAC_HAS_IPV6 +#pragma message("Using WinSock IPV4") +#endif + +#endif + +/*common win32 redefs*/ +#define EAGAIN WSAEWOULDBLOCK +#define EISCONN WSAEISCONN +#define ENOTCONN WSAENOTCONN +#define ECONNRESET WSAECONNRESET +#define EMSGSIZE WSAEMSGSIZE +#define ECONNABORTED WSAECONNABORTED +#define ENETDOWN WSAENETDOWN + +#define LASTSOCKERROR WSAGetLastError() + +/*the number of sockets used. This because the WinSock lib needs init*/ +static int wsa_init = 0; + +#include + +/*end-win32*/ + +#else +/*non-win32*/ +#include +#include +#include + +#ifndef __BEOS__ +#include +#endif + +#ifndef __DARWIN__ +#include +#endif + +#include +#include +#include +#include +#include + +#include + +/*not defined on solaris*/ +#if !defined(INADDR_NONE) +# if (defined(sun) && defined(__SVR4)) +# define INADDR_NONE -1 +# else +# define INADDR_NONE ((unsigned long)-1) +# endif +#endif + + +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define LASTSOCKERROR errno + +typedef s32 SOCKET; +#define closesocket(v) close(v) + +#endif + +#ifdef __SYMBIAN32__ +#define SSO_CAST +#else +#define SSO_CAST (const char *) +#endif + + +#define SOCK_MICROSEC_WAIT 500 + +#ifdef GPAC_HAS_IPV6 +static u32 ipv6_check_state = 0; +#endif + +/*internal flags*/ +enum +{ + GF_SOCK_IS_TCP = 1<<9, + GF_SOCK_IS_IPV6 = 1<<10, + GF_SOCK_NON_BLOCKING = 1<<11, + GF_SOCK_IS_MULTICAST = 1<<12, + GF_SOCK_IS_LISTENING = 1<<13, + /*socket is bound to a specific dest (server) or source (client) */ + GF_SOCK_HAS_PEER = 1<<14, + GF_SOCK_IS_MIP = 1<<15 +}; + +struct __tag_socket +{ + u32 flags; + SOCKET socket; + /*destination address for sendto/recvfrom*/ +#ifdef GPAC_HAS_IPV6 + struct sockaddr_storage dest_addr; +#else + struct sockaddr_in dest_addr; +#endif + u32 dest_addr_len; +}; + + + +/* + MobileIP tools +*/ + +static gf_net_mobileip_ctrl_cbk mobip_cbk = NULL; +static const char *MobileIPAdd = NULL; + +GF_EXPORT +void gf_net_mobileip_set_callback(gf_net_mobileip_ctrl_cbk _mobip_cbk, const char *mip) +{ + mobip_cbk = _mobip_cbk; + MobileIPAdd = _mobip_cbk ? mip : NULL; +} + +static GF_Err gf_net_mobileip_ctrl(Bool start) +{ + if (mobip_cbk) return mobip_cbk(start); + return GF_NOT_SUPPORTED; +} + + +/* + NTP tools +*/ +void gf_net_get_ntp(u32 *sec, u32 *frac) +{ + struct timeval now; +#ifdef WIN32 + s32 gettimeofday(struct timeval *tp, void *tz); +#endif + gettimeofday(&now, NULL); + *sec = (u32) (now.tv_sec) + GF_NTP_SEC_1900_TO_1970; + *frac = (u32) ( (now.tv_usec << 12) + (now.tv_usec << 8) - ((now.tv_usec * 3650) >> 6) ); +} + +u32 gf_net_has_ipv6() +{ +#ifdef GPAC_HAS_IPV6 + if (!ipv6_check_state) { + SOCKET s; +#ifdef WIN32 + if (!wsa_init) { + WSADATA Data; + if (WSAStartup(0x0202, &Data)!=0) { + ipv6_check_state = 1; + return 0; + } + } +#endif + s = socket(PF_INET6, SOCK_STREAM, 0); + if (!s) ipv6_check_state = 1; + else { + ipv6_check_state = 2; + closesocket(s); + } +#ifdef WIN32 + if (!wsa_init) WSACleanup(); +#endif + } + return (ipv6_check_state==2); +#else + return 0; +#endif +} + +GF_EXPORT +Bool gf_net_is_ipv6(char *address) +{ + char *sep; + if (!address) return 0; + sep = strchr(address, ':'); + if (sep) sep = strchr(address, ':'); + return sep ? 1 : 0; +} + +#ifdef GPAC_HAS_IPV6 +static struct addrinfo *gf_sk_get_ipv6_addr(char *PeerName, u16 PortNumber, int family, int flags, int sock_type) +{ + struct addrinfo *res=NULL; + struct addrinfo hints; + char node[50], portstring[20], *service, *dest; + +#ifdef WIN32 + if (!wsa_init) { + WSADATA Data; + if (WSAStartup(0x0202, &Data)!=0) return NULL; + wsa_init = 1; + } +#endif + + service = dest = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = sock_type; + hints.ai_family = family; + hints.ai_flags = flags; + + if (PortNumber) { + sprintf (portstring, "%d", PortNumber); + service = (char *)portstring; + } + if (PeerName) { + strcpy(node, PeerName); + if (node[0]=='[') { + node[strlen(node)-1] = 0; + strcpy(node, &node[1]); + } + dest = (char *) node; + } + if (getaddrinfo((const char *)dest, (const char *)service, &hints, &res) != 0) return NULL; + return res; +} + +static Bool gf_sk_ipv6_set_remote_address(GF_Socket *sock, char *address, u16 PortNumber) +{ + struct addrinfo *res = gf_sk_get_ipv6_addr(address, PortNumber, AF_UNSPEC, 0, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM); + if (!res) return 0; + memcpy(&sock->dest_addr, res->ai_addr, res->ai_addrlen); + sock->dest_addr_len = res->ai_addrlen; + freeaddrinfo(res); + return 1; +} +#endif + + +GF_Err gf_sk_get_host_name(char *buffer) +{ + s32 ret = gethostname(buffer, GF_MAX_IP_NAME_LEN); + return (ret == SOCKET_ERROR) ? GF_IP_ADDRESS_NOT_FOUND : GF_OK; +} + +GF_Err gf_sk_get_local_ip(GF_Socket *sock, char *buffer) +{ +#ifdef GPAC_HAS_IPV6 + char clienthost[NI_MAXHOST]; + if (sock->flags & GF_SOCK_HAS_PEER) { + if (getnameinfo((struct sockaddr *)&sock->dest_addr, sock->dest_addr_len, clienthost, sizeof(clienthost), NULL, 0, NI_NUMERICHOST)) + return GF_IP_NETWORK_FAILURE; + } else { + struct sockaddr_storage clientaddr; + socklen_t addrlen = sizeof(clientaddr); + if (getsockname(sock->socket, (struct sockaddr *)&clientaddr, &addrlen)) return GF_IP_NETWORK_FAILURE; + + if (getnameinfo((struct sockaddr *)&clientaddr, addrlen, clienthost, sizeof(clienthost), NULL, 0, NI_NUMERICHOST)) + return GF_IP_NETWORK_FAILURE; + } + strcpy(buffer, clienthost); +#else + char *ip; + if (sock->flags & GF_SOCK_HAS_PEER) { + ip = inet_ntoa(sock->dest_addr.sin_addr); + } else { + struct sockaddr_in name; + u32 len = sizeof(struct sockaddr_in); + if (getsockname(sock->socket, (struct sockaddr*) &name, &len)) return GF_IP_NETWORK_FAILURE; + ip = inet_ntoa(name.sin_addr); + } + if (!ip) return GF_IP_NETWORK_FAILURE; + strcpy(buffer, ip); +#endif + return GF_OK; +} + + +GF_Socket *gf_sk_new(u32 SocketType) +{ + GF_Socket *tmp; + + /*init WinSock*/ +#ifdef WIN32 + WSADATA Data; + if (!wsa_init && (WSAStartup(0x0202, &Data)!=0) ) return NULL; +#endif + if ((SocketType != GF_SOCK_TYPE_UDP) && (SocketType != GF_SOCK_TYPE_TCP)) return NULL; + + GF_SAFEALLOC(tmp, GF_Socket); + if (!tmp) return NULL; + if (SocketType == GF_SOCK_TYPE_TCP) tmp->flags |= GF_SOCK_IS_TCP; + +#ifdef GPAC_HAS_IPV6 + memset(&tmp->dest_addr, 0, sizeof(struct sockaddr_storage)); +#else + memset(&tmp->dest_addr, 0, sizeof(struct sockaddr_in)); + tmp->dest_addr_len = sizeof(struct sockaddr); +#endif + +#ifdef WIN32 + wsa_init ++; +#endif + return tmp; +} + +GF_Err gf_sk_set_buffer_size(GF_Socket *sock, Bool SendBuffer, u32 NewSize) +{ + if (!sock || !sock->socket) return GF_BAD_PARAM; + + if (SendBuffer) { + setsockopt(sock->socket, SOL_SOCKET, SO_SNDBUF, (char *) &NewSize, sizeof(u32) ); + } else { + setsockopt(sock->socket, SOL_SOCKET, SO_RCVBUF, (char *) &NewSize, sizeof(u32) ); + } + return GF_OK; +} + +GF_Err gf_sk_set_block_mode(GF_Socket *sock, u32 NonBlockingOn) +{ + s32 res; +#ifdef WIN32 + long val = NonBlockingOn; + if (sock->socket) { + res = ioctlsocket(sock->socket, FIONBIO, &val); + if (res) return GF_SERVICE_ERROR; + } +#else + s32 flag = fcntl(sock->socket, F_GETFL, 0); + if (sock->socket) { + res = fcntl(sock->socket, F_SETFL, flag | O_NONBLOCK); + if (res) return GF_SERVICE_ERROR; + } +#endif + if (NonBlockingOn) { + sock->flags |= GF_SOCK_NON_BLOCKING; + } else { + sock->flags &= ~GF_SOCK_NON_BLOCKING; + } + return GF_OK; +} + + +static void gf_sk_free(GF_Socket *sock) +{ + /*leave multicast*/ + if (sock->socket && (sock->flags & GF_SOCK_IS_MULTICAST) ) { + struct ip_mreq mreq; +#ifdef GPAC_HAS_IPV6 + struct sockaddr *addr = (struct sockaddr *)&sock->dest_addr; + if (addr->sa_family==AF_INET6) { + struct ipv6_mreq mreq6; + memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); + mreq6.ipv6mr_interface= 0; + setsockopt(sock->socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *) &mreq6, sizeof(mreq6)); + } else { + mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + mreq.imr_interface.s_addr = INADDR_ANY; + setsockopt(sock->socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &mreq, sizeof(mreq)); + } +#else + mreq.imr_multiaddr.s_addr = sock->dest_addr.sin_addr.s_addr; + mreq.imr_interface.s_addr = INADDR_ANY; + setsockopt(sock->socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &mreq, sizeof(mreq)); +#endif + } + if (sock->socket) closesocket(sock->socket); + sock->socket = (SOCKET) 0L; + + /*if MobileIP socket, unregister it*/ + if (sock->flags & GF_SOCK_IS_MIP) { + sock->flags &= ~GF_SOCK_IS_MIP; + gf_net_mobileip_ctrl(0); + } +} + +void gf_sk_del(GF_Socket *sock) +{ + gf_sk_free(sock); +#ifdef WIN32 + wsa_init --; + if (!wsa_init) WSACleanup(); +#endif + free(sock); +} + +void gf_sk_reset(GF_Socket *sock) +{ + u32 clear; + if (sock) setsockopt(sock->socket, SOL_SOCKET, SO_ERROR, (char *) &clear, sizeof(u32) ); +} + +s32 gf_sk_get_handle(GF_Socket *sock) +{ + return sock->socket; +} + + + +//connects a socket to a remote peer on a given port +GF_Err gf_sk_connect(GF_Socket *sock, char *PeerName, u16 PortNumber, char *local_ip) +{ + s32 ret; +#ifdef GPAC_HAS_IPV6 + u32 type = (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM; + struct addrinfo *res, *aip, *lip; + + gf_sk_free(sock); + + res = gf_sk_get_ipv6_addr(PeerName, PortNumber, AF_UNSPEC, AI_PASSIVE, type); + if (!res) return GF_IP_CONNECTION_FAILURE; + + /*turn on MobileIP*/ + if (local_ip && MobileIPAdd && !strcmp(MobileIPAdd, local_ip) ) { + if (gf_net_mobileip_ctrl(1)==GF_OK) { + sock->flags |= GF_SOCK_IS_MIP; + } else { + local_ip = NULL; + } + } + + lip = NULL; + if (local_ip) { + lip = gf_sk_get_ipv6_addr(local_ip, PortNumber, AF_UNSPEC, AI_PASSIVE, type); + if (!lip && local_ip) { + lip = gf_sk_get_ipv6_addr(NULL, PortNumber, AF_UNSPEC, AI_PASSIVE, type); + local_ip = NULL; + } + } + + /*for all interfaces*/ + for (aip=res; aip!=NULL; aip=aip->ai_next) { + if (type != (u32) aip->ai_socktype) continue; + sock->socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (sock->socket == INVALID_SOCKET) { + sock->socket = (SOCKET)NULL; + continue; + } + if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); + if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; + else sock->flags &= ~GF_SOCK_IS_IPV6; + + if (lip) { + ret = bind(sock->socket, lip->ai_addr, lip->ai_addrlen); + if (ret == SOCKET_ERROR) { + closesocket(sock->socket); + sock->socket = (SOCKET)NULL; + continue; + } + } + + ret = connect(sock->socket, aip->ai_addr, aip->ai_addrlen); + if (ret == SOCKET_ERROR) { + closesocket(sock->socket); + sock->socket = (SOCKET)NULL; + continue; + } + + memcpy(&sock->dest_addr, aip->ai_addr, aip->ai_addrlen); + sock->dest_addr_len = aip->ai_addrlen; + freeaddrinfo(res); + if (lip) freeaddrinfo(lip); + return GF_OK; + } + freeaddrinfo(res); + if (lip) freeaddrinfo(lip); + return GF_IP_CONNECTION_FAILURE; + +#else + struct hostent *Host; + + if (local_ip) { + /*this will turn on MobileIP if needed*/ + GF_Err e = gf_sk_bind(sock, local_ip, PortNumber, PeerName, PortNumber, GF_SOCK_REUSE_PORT); + if (e) return e; + } + if (!sock->socket) { + sock->socket = socket(AF_INET, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); + if (sock->flags & GF_SOCK_NON_BLOCKING) + gf_sk_set_block_mode(sock, 1); + } + + /*setup the address*/ + sock->dest_addr.sin_family = AF_INET; + sock->dest_addr.sin_port = htons(PortNumber); + /*get the server IP*/ + sock->dest_addr.sin_addr.s_addr = inet_addr(PeerName); + if (sock->dest_addr.sin_addr.s_addr==INADDR_NONE) { + Host = gethostbyname(PeerName); + if (Host == NULL) { + switch (LASTSOCKERROR) { +#ifndef __SYMBIAN32__ + case ENETDOWN: return GF_IP_NETWORK_FAILURE; + //case ENOHOST: return GF_IP_ADDRESS_NOT_FOUND; +#endif + default: return GF_IP_NETWORK_FAILURE; + } + } + memcpy((char *) &sock->dest_addr.sin_addr, Host->h_addr_list[0], sizeof(u32)); + } + + if (sock->flags & GF_SOCK_IS_TCP) { + ret = connect(sock->socket, (struct sockaddr *) &sock->dest_addr, sizeof(struct sockaddr)); + if (ret == SOCKET_ERROR) { + u32 res = LASTSOCKERROR; + GF_LOG(GF_LOG_CORE, GF_LOG_ERROR, ("[Core] Couldn't connect socket - last sock error %d\n", res)); + switch (res) { + case EAGAIN: return GF_IP_SOCK_WOULD_BLOCK; +#ifdef WIN32 + case WSAEINVAL: + if (sock->flags & GF_SOCK_NON_BLOCKING) + return GF_IP_SOCK_WOULD_BLOCK; +#endif + case EISCONN: return GF_OK; + case ENOTCONN: return GF_IP_CONNECTION_FAILURE; + case ECONNRESET: return GF_IP_CONNECTION_FAILURE; + case EMSGSIZE: return GF_IP_CONNECTION_FAILURE; + case ECONNABORTED: return GF_IP_CONNECTION_FAILURE; + case ENETDOWN: return GF_IP_CONNECTION_FAILURE; + default: return GF_IP_CONNECTION_FAILURE; + } + } + } +#endif + return GF_OK; +} + + +//binds the given socket to the specified port. If ReUse is true +//this will enable reuse of ports on a single machine +GF_Err gf_sk_bind(GF_Socket *sock, char *local_ip, u16 port, char *peer_name, u16 peer_port, u32 options) +{ +#ifdef GPAC_HAS_IPV6 + struct addrinfo *res, *aip; + int af; + u32 type; +#else + u32 ip_add; + size_t addrlen; + struct sockaddr_in LocalAdd; + struct hostent *Host; + char buf[GF_MAX_IP_NAME_LEN]; +#endif + s32 ret; + s32 optval; + + if (!sock || sock->socket) return GF_BAD_PARAM; + +#ifdef GPAC_HAS_IPV6 + type = (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM; + af = (options & GF_SOCK_FORCE_IPV6) ? PF_INET6 : PF_UNSPEC; + if (!gf_net_has_ipv6()) af = PF_INET; + /*probe way to peer: is it V4 or V6? */ + if (peer_name && peer_port) { + res = gf_sk_get_ipv6_addr(peer_name, peer_port, af, AI_PASSIVE, type); + if (!res) return GF_IP_CONNECTION_FAILURE; +#ifdef WIN32 + /*win32 has troubles redirecting IPV4 datagrams to IPV6 sockets, so override + local family type to avoid IPV4(S)->IPV6(C) UDP*/ + af = res->ai_family; +#endif + memcpy(&sock->dest_addr, res->ai_addr, res->ai_addrlen); + sock->dest_addr_len = res->ai_addrlen; + freeaddrinfo(res); + } + + /*turn on MobileIP*/ + if (local_ip && MobileIPAdd && !strcmp(MobileIPAdd, local_ip) ) { + if (gf_net_mobileip_ctrl(1)==GF_OK) { + sock->flags |= GF_SOCK_IS_MIP; + } else { + res = gf_sk_get_ipv6_addr(NULL, port, af, AI_PASSIVE, type); + local_ip = NULL; + } + } + res = gf_sk_get_ipv6_addr(local_ip, port, af, AI_PASSIVE, type); + if (!res) { + if (local_ip) { + res = gf_sk_get_ipv6_addr(NULL, port, af, AI_PASSIVE, type); + local_ip = NULL; + } + if (!res) return GF_IP_CONNECTION_FAILURE; + } + + /*for all interfaces*/ + for (aip=res; aip!=NULL; aip=aip->ai_next) { + if (type != (u32) aip->ai_socktype) continue; + + if (aip->ai_next && (aip->ai_next->ai_family==PF_INET) && !gf_net_is_ipv6(peer_name)) continue; + + sock->socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (sock->socket == INVALID_SOCKET) { + sock->socket = (SOCKET)NULL; + continue; + } + if (options & GF_SOCK_REUSE_PORT) { + optval = 1; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &optval, sizeof(optval)); +#ifdef SO_REUSEPORT + optval = 1; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEPORT, SSO_CAST &optval, sizeof(optval)); +#endif + } + if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); + + ret = bind(sock->socket, aip->ai_addr, aip->ai_addrlen); + if (ret == SOCKET_ERROR) { + closesocket(sock->socket); + sock->socket = (SOCKET)NULL; + continue; + } + + if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; + else sock->flags &= ~GF_SOCK_IS_IPV6; + + if (peer_name && peer_port) + sock->flags |= GF_SOCK_HAS_PEER; + + freeaddrinfo(res); + return GF_OK; + } + freeaddrinfo(res); + return GF_IP_CONNECTION_FAILURE; + +#else + + sock->socket = socket(AF_INET, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); + if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); + sock->flags &= ~GF_SOCK_IS_IPV6; + + memset((void *) &LocalAdd, 0, sizeof(LocalAdd)); + ret = gethostname(buf, GF_MAX_IP_NAME_LEN); + if (ret == SOCKET_ERROR) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[socket] cannot get localhost name - socket error %x\n", LASTSOCKERROR)); + return GF_IP_ADDRESS_NOT_FOUND; + } + /*get the IP address*/ + Host = gethostbyname(buf); + if (Host == NULL) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[socket] cannot resolve localhost name - socket error %x\n", LASTSOCKERROR)); + return GF_IP_ADDRESS_NOT_FOUND; + } + + /*turn on MobileIP*/ + if (local_ip && MobileIPAdd && !strcmp(MobileIPAdd, local_ip) ) { + if (gf_net_mobileip_ctrl(1)==GF_OK) { + sock->flags |= GF_SOCK_IS_MIP; + } else { + local_ip = NULL; + } + } + + /*setup the address*/ + ip_add = 0; + if (local_ip) ip_add = inet_addr(local_ip); + if (!ip_add) { + ip_add = htonl(INADDR_ANY); + local_ip = NULL; + } + memcpy((char *) &LocalAdd.sin_addr, Host->h_addr_list[0], sizeof(LocalAdd.sin_addr)); + LocalAdd.sin_family = AF_INET; + LocalAdd.sin_addr.s_addr = ip_add; + LocalAdd.sin_port = htons(port); + addrlen = sizeof(struct sockaddr_in); + if (options & GF_SOCK_REUSE_PORT) { + optval = 1; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, SSO_CAST &optval, sizeof(optval)); +#ifdef SO_REUSEPORT + optval = 1; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEPORT, SSO_CAST &optval, sizeof(optval)); +#endif + } + /*bind the socket*/ + ret = bind(sock->socket, (struct sockaddr *) &LocalAdd, addrlen); + if (ret == SOCKET_ERROR) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[socket] cannot bind socket - socket error %x\n", LASTSOCKERROR)); + return GF_IP_CONNECTION_FAILURE; + } + + if (peer_name && peer_port) { + sock->dest_addr.sin_port = htons(peer_port); + sock->dest_addr.sin_family = AF_INET; + sock->dest_addr.sin_addr.s_addr = inet_addr(peer_name); + if (sock->dest_addr.sin_addr.s_addr == INADDR_NONE) { + Host = gethostbyname(peer_name); + if (Host == NULL) return GF_IP_ADDRESS_NOT_FOUND; + memcpy((char *) &sock->dest_addr.sin_addr, Host->h_addr_list[0], sizeof(u32)); + } + sock->flags |= GF_SOCK_HAS_PEER; + } +#endif + if (sock->flags & GF_SOCK_HAS_PEER) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[socket] socket bound to port %d - remote peer: %s:%d\n", port, peer_name, peer_port)); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[socket] socket bound to port %d\n", port)); + } + return GF_OK; +} + +//send length bytes of a buffer +GF_Err gf_sk_send(GF_Socket *sock, char *buffer, u32 length) +{ + GF_Err e; + u32 Count, Res; +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + + e = GF_OK; + + //the socket must be bound or connected + if (!sock || !sock->socket) return GF_BAD_PARAM; + +#ifndef __SYMBIAN32__ + //can we write? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = 0; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + ready = select(sock->socket+1, NULL, &Group, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + //should never happen (to check: is writeability is guaranteed for not-connected sockets) + if (!ready || !FD_ISSET(sock->socket, &Group)) { + return GF_IP_NETWORK_EMPTY; + } +#endif + + //direct writing + Count = 0; + while (Count < length) { + if (sock->flags & GF_SOCK_HAS_PEER) { + Res = sendto(sock->socket, (char *) &buffer[Count], length - Count, 0, (struct sockaddr *) &sock->dest_addr, sock->dest_addr_len); + } else { + Res = send(sock->socket, (char *) &buffer[Count], length - Count, 0); + } + if (Res == SOCKET_ERROR) { + switch (Res = LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; +#ifndef __SYMBIAN32__ + case ENOTCONN: + case ECONNRESET: + return GF_IP_CONNECTION_CLOSED; +#endif + default: + return GF_IP_NETWORK_FAILURE; + } + } + Count += Res; + } + return GF_OK; +} + + +u32 gf_sk_is_multicast_address(char *multi_IPAdd) +{ +#ifdef GPAC_HAS_IPV6 + u32 val; + char *sep; + struct addrinfo *res; + if (!multi_IPAdd) return 0; + /*IPV6 multicast address*/ + sep = strchr(multi_IPAdd, ':'); + if (sep) sep = strchr(multi_IPAdd, ':'); + if (sep && !strnicmp(multi_IPAdd, "ff", 2)) return 1; + /*ipv4 multicast address*/ + res = gf_sk_get_ipv6_addr(multi_IPAdd, 7000, AF_UNSPEC, AI_PASSIVE, SOCK_DGRAM); + if (!res) return 0; + val = 0; + if (res->ai_addr->sa_family == AF_INET) { + val = IN_MULTICAST(ntohl(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr)); + } else if (res->ai_addr->sa_family == AF_INET6) { + val = IN6_IS_ADDR_MULTICAST(& ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr); + } + freeaddrinfo(res); + return val; +#else + if (!multi_IPAdd) return 0; + return ((htonl(inet_addr(multi_IPAdd)) >> 8) & 0x00f00000) == 0x00e00000; +#endif +} + +GF_Err gf_sk_setup_multicast(GF_Socket *sock, char *multi_IPAdd, u16 MultiPortNumber, u32 TTL, Bool NoBind, char *local_interface_ip) +{ + s32 ret; + u32 flag; + struct ip_mreq M_req; + u32 optval; +#ifdef GPAC_HAS_IPV6 + struct sockaddr *addr; + struct addrinfo *res, *aip; + u32 type; +#else + unsigned long local_add_id; +#endif + + if (!sock || sock->socket) return GF_BAD_PARAM; + + if (TTL > 255) TTL = 255; + + /*check the address*/ + if (!gf_sk_is_multicast_address(multi_IPAdd)) return GF_BAD_PARAM; + + /*turn on MobileIP*/ + if (local_interface_ip && MobileIPAdd && !strcmp(MobileIPAdd, local_interface_ip) ) { + if (gf_net_mobileip_ctrl(1)==GF_OK) { + sock->flags |= GF_SOCK_IS_MIP; + } else { + local_interface_ip = NULL; + } + } + + +#ifdef GPAC_HAS_IPV6 + type = (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM; + res = gf_sk_get_ipv6_addr(local_interface_ip, MultiPortNumber, AF_UNSPEC, AI_PASSIVE, type); + if (!res) { + if (local_interface_ip) { + res = gf_sk_get_ipv6_addr(NULL, MultiPortNumber, AF_UNSPEC, AI_PASSIVE, type); + local_interface_ip = NULL; + } + if (!res) return GF_IP_CONNECTION_FAILURE; + } + + /*for all interfaces*/ + for (aip=res; aip!=NULL; aip=aip->ai_next) { + if (type != (u32) aip->ai_socktype) continue; + sock->socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (sock->socket == INVALID_SOCKET) { + sock->socket = (SOCKET)NULL; + continue; + } + + if (aip->ai_next && (aip->ai_next->ai_family==PF_INET) && !gf_net_is_ipv6(multi_IPAdd)) continue; + + /*enable address reuse*/ + optval = 1; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &optval, sizeof(optval)); +#ifdef SO_REUSEPORT + optval = 1; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEPORT, SSO_CAST &optval, sizeof(optval)); +#endif + + /*TODO: copy over other properties (recption buffer size & co)*/ + if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); + + memcpy(&sock->dest_addr, aip->ai_addr, aip->ai_addrlen); + sock->dest_addr_len = aip->ai_addrlen; + + if (!NoBind) { + ret = bind(sock->socket, aip->ai_addr, aip->ai_addrlen); + if (ret == SOCKET_ERROR) { + closesocket(sock->socket); + sock->socket = (SOCKET)NULL; + continue; + } + } + if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; + else sock->flags &= ~GF_SOCK_IS_IPV6; + break; + } + freeaddrinfo(res); + if (!sock->socket) return GF_IP_CONNECTION_FAILURE; + + + if (!gf_sk_ipv6_set_remote_address(sock, multi_IPAdd, MultiPortNumber)) + return GF_IP_CONNECTION_FAILURE; + + addr = (struct sockaddr *)&sock->dest_addr; + if (addr->sa_family == AF_INET) { + M_req.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + M_req.imr_interface.s_addr = INADDR_ANY; + ret = setsockopt(sock->socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &M_req, sizeof(M_req)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + /*set TTL*/ + ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &TTL, sizeof(TTL)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + /*Disable loopback*/ + flag = 1; + ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &flag, sizeof(flag)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + } + if (addr->sa_family == AF_INET6) { + struct ipv6_mreq M_reqV6; + + + memcpy(&M_reqV6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); + M_reqV6.ipv6mr_interface = 0; + + /*set TTL*/ + ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &TTL, sizeof(TTL)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + /*Disable loopback*/ + flag = 1; + ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &flag, sizeof(flag)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + + ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *) &M_reqV6, sizeof(M_reqV6)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + } +#else + + sock->socket = socket(AF_INET, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); + if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); + sock->flags &= ~GF_SOCK_IS_IPV6; + + /*enable address reuse*/ + optval = 1; + ret = setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, SSO_CAST &optval, sizeof(optval)); +#ifdef SO_REUSEPORT + optval = 1; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEPORT, SSO_CAST &optval, sizeof(optval)); +#endif + + if (local_interface_ip) local_add_id = inet_addr(local_interface_ip); + else local_add_id = htonl(INADDR_ANY); + + if (!NoBind) { + struct sockaddr_in local_address; + + local_address.sin_family = AF_INET; + local_address.sin_addr.s_addr = local_add_id; + local_address.sin_port = htons( MultiPortNumber); + + ret = bind(sock->socket, (struct sockaddr *) &local_address, sizeof(local_address)); + if (ret == SOCKET_ERROR) { + /*retry without specifying the local add*/ + local_address.sin_addr.s_addr = local_add_id = htonl(INADDR_ANY); + local_interface_ip = NULL; + ret = bind(sock->socket, (struct sockaddr *) &local_address, sizeof(local_address)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + } + /*setup local interface*/ + if (local_interface_ip) { + ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_IF, (char *) &local_add_id, sizeof(local_add_id)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + } + } + /*now join the multicast*/ + M_req.imr_multiaddr.s_addr = inet_addr(multi_IPAdd); + M_req.imr_interface.s_addr = local_add_id; + + ret = setsockopt(sock->socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &M_req, sizeof(M_req)); + if (ret == SOCKET_ERROR) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] cannot join multicast: error %d\n", LASTSOCKERROR)); + return GF_IP_CONNECTION_FAILURE; + } + /*set the Time To Live*/ + ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&TTL, sizeof(TTL)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + /*Disable loopback*/ + flag = 1; + ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &flag, sizeof(flag)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + + sock->dest_addr.sin_family = AF_INET; + sock->dest_addr.sin_addr.s_addr = M_req.imr_multiaddr.s_addr; + sock->dest_addr.sin_port = htons( MultiPortNumber); +#endif + sock->flags |= GF_SOCK_IS_MULTICAST | GF_SOCK_HAS_PEER; + return GF_OK; +} + + + + +//fetch nb bytes on a socket and fill the buffer from startFrom +//length is the allocated size of the receiving buffer +//BytesRead is the number of bytes read from the network +GF_Err gf_sk_receive(GF_Socket *sock, char *buffer, u32 length, u32 startFrom, u32 *BytesRead) +{ + GF_Err e; + u32 res; +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + + e = GF_OK; + + *BytesRead = 0; + if (!sock->socket) return GF_BAD_PARAM; + if (startFrom >= length) return GF_IO_ERR; + +#ifndef __SYMBIAN32__ + //can we read? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = 0; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + res = 0; + ready = select(sock->socket+1, &Group, NULL, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[socket] cannot select (error %d)\n", LASTSOCKERROR)); + return GF_IP_NETWORK_FAILURE; + } + } + if (!FD_ISSET(sock->socket, &Group)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[socket] nothing to be read\n")); + return GF_IP_NETWORK_EMPTY; + } +#endif + + if (sock->flags & GF_SOCK_HAS_PEER) + res = recvfrom(sock->socket, (char *) buffer + startFrom, length - startFrom, 0, (struct sockaddr *)&sock->dest_addr, &sock->dest_addr_len); + else + res = recv(sock->socket, (char *) buffer + startFrom, length - startFrom, 0); + + if (res == SOCKET_ERROR) { + res = LASTSOCKERROR; + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[socket] error reading - socket error %d\n", res)); + switch (res) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; +#ifndef __SYMBIAN32__ + case EMSGSIZE: + return GF_OUT_OF_MEM; + case ENOTCONN: + case ECONNRESET: + case ECONNABORTED: + return GF_IP_CONNECTION_CLOSED; +#endif + default: + return GF_IP_NETWORK_FAILURE; + } + } + if (!res) return GF_IP_NETWORK_EMPTY; + *BytesRead = res; + return GF_OK; +} + + +GF_Err gf_sk_listen(GF_Socket *sock, u32 MaxConnection) +{ + s32 i; + if (!sock || !sock->socket) return GF_BAD_PARAM; + if (MaxConnection >= SOMAXCONN) MaxConnection = SOMAXCONN; + i = listen(sock->socket, MaxConnection); + if (i == SOCKET_ERROR) return GF_IP_NETWORK_FAILURE; + sock->flags |= GF_SOCK_IS_LISTENING; + return GF_OK; +} + +GF_Err gf_sk_accept(GF_Socket *sock, GF_Socket **newConnection) +{ + u32 client_address_size, res; + SOCKET sk; +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + *newConnection = NULL; + if (!sock || !(sock->flags & GF_SOCK_IS_LISTENING) ) return GF_BAD_PARAM; + +#ifndef __SYMBIAN32__ + //can we read? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = 0; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + res = 0; + ready = select(sock->socket+1, &Group, NULL, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + if (!ready || !FD_ISSET(sock->socket, &Group)) return GF_IP_NETWORK_EMPTY; +#endif + +#ifdef GPAC_HAS_IPV6 + client_address_size = sizeof(struct sockaddr_in6); +#else + client_address_size = sizeof(struct sockaddr_in); +#endif + sk = accept(sock->socket, (struct sockaddr *) &sock->dest_addr, &client_address_size); + + //we either have an error or we have no connections + if (sk == INVALID_SOCKET) { +// if (sock->flags & GF_SOCK_NON_BLOCKING) return GF_IP_NETWORK_FAILURE; + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + + (*newConnection) = (GF_Socket *) malloc(sizeof(GF_Socket)); + (*newConnection)->socket = sk; + (*newConnection)->flags = sock->flags & ~GF_SOCK_IS_LISTENING; +#ifdef GPAC_HAS_IPV6 + memcpy( &(*newConnection)->dest_addr, &sock->dest_addr, client_address_size); + memset(&sock->dest_addr, 0, sizeof(struct sockaddr_in6)); +#else + memcpy( &(*newConnection)->dest_addr, &sock->dest_addr, client_address_size); + memset(&sock->dest_addr, 0, sizeof(struct sockaddr_in)); +#endif + +#if defined(WIN32) || defined(_WIN32_WCE) + wsa_init++; +#endif + + (*newConnection)->dest_addr_len = client_address_size; + return GF_OK; +} + +GF_Err gf_sk_get_local_info(GF_Socket *sock, u16 *Port, u32 *Familly) +{ +#ifdef GPAC_HAS_IPV6 + struct sockaddr_in6 the_add; +#else + struct sockaddr_in the_add; +#endif + u32 size; + + if (!sock || !sock->socket) return GF_BAD_PARAM; + + if (Port) { +#ifdef GPAC_HAS_IPV6 + size = sizeof(struct sockaddr_in6); + if (getsockname(sock->socket, (struct sockaddr *) &the_add, &size) == SOCKET_ERROR) return GF_IP_NETWORK_FAILURE; + *Port = (u32) ntohs(the_add.sin6_port); +#else + size = sizeof(struct sockaddr_in); + if (getsockname(sock->socket, (struct sockaddr *) &the_add, &size) == SOCKET_ERROR) return GF_IP_NETWORK_FAILURE; + *Port = (u32) ntohs(the_add.sin_port); +#endif + } + if (Familly) { +/* size = 4; + if (getsockopt(sock->socket, SOL_SOCKET, SO_TYPE, (char *) &fam, &size) == SOCKET_ERROR) + return GF_IP_NETWORK_FAILURE; + *Familly = fam; +*/ + if (sock->flags & GF_SOCK_IS_TCP) *Familly = GF_SOCK_TYPE_TCP; + else *Familly = GF_SOCK_TYPE_UDP; + } + return GF_OK; +} + +//we have to do this for the server sockets as we use only one thread +GF_Err gf_sk_server_mode(GF_Socket *sock, Bool serverOn) +{ + u32 one; + + if (!sock || !(sock->flags & GF_SOCK_IS_TCP) || !sock->socket) + return GF_BAD_PARAM; + + one = serverOn ? 1 : 0; + setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, SSO_CAST &one, sizeof(u32)); +#ifndef __SYMBIAN32__ + setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(u32)); +#endif + return GF_OK; +} + +GF_Err gf_sk_get_remote_address(GF_Socket *sock, char *buf) +{ +#ifdef GPAC_HAS_IPV6 + char clienthost[NI_MAXHOST]; + struct sockaddr_in6 * addrptr = (struct sockaddr_in6 *)(&sock->dest_addr_len); + if (!sock || sock->socket) return GF_BAD_PARAM; + if (getnameinfo((struct sockaddr *)addrptr, sock->dest_addr_len, clienthost, sizeof(clienthost), NULL, 0, NI_NUMERICHOST)) + return GF_IP_ADDRESS_NOT_FOUND; + strcpy(buf, clienthost); +#else + if (!sock || !sock->socket) return GF_BAD_PARAM; + strcpy(buf, inet_ntoa(sock->dest_addr.sin_addr)); +#endif + return GF_OK; +} + + + + +//send length bytes of a buffer +GF_Err gf_sk_send_to(GF_Socket *sock, char *buffer, u32 length, char *remoteHost, u16 remotePort) +{ + u32 Count, Res, remote_add_len; +#ifdef GPAC_HAS_IPV6 + struct sockaddr_storage remote_add; +#else + struct sockaddr_in remote_add; + struct hostent *Host; +#endif +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + + //the socket must be bound or connected + if (!sock || !sock->socket) return GF_BAD_PARAM; + if (remoteHost && !remotePort) return GF_BAD_PARAM; + +#ifndef __SYMBIAN32__ + //can we write? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = 0; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + ready = select(sock->socket+1, NULL, &Group, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + if (!ready || !FD_ISSET(sock->socket, &Group)) return GF_IP_NETWORK_EMPTY; +#endif + + + /*setup the address*/ +#ifdef GPAC_HAS_IPV6 + remote_add.ss_family = AF_INET6; + //if a remote host is specified, use it. Otherwise use the default host + if (remoteHost) { + //setup the address + struct addrinfo *res = gf_sk_get_ipv6_addr(remoteHost, remotePort, AF_UNSPEC, 0, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM); + if (!res) return GF_IP_ADDRESS_NOT_FOUND; + memcpy(&remote_add, res->ai_addr, res->ai_addrlen); + remote_add_len = res->ai_addrlen; + freeaddrinfo(res); + } else { + struct sockaddr_in6 *remotePtr = (struct sockaddr_in6 *)&remote_add; + struct sockaddr_in6 * addrptr = (struct sockaddr_in6 *)(&sock->dest_addr); + remotePtr->sin6_port = addrptr->sin6_port; + remotePtr->sin6_addr = addrptr->sin6_addr; + remote_add_len = sock->dest_addr_len; + } +#else + remote_add_len = sizeof(struct sockaddr_in); + remote_add.sin_family = AF_INET; + //if a remote host is specified, use it. Otherwise use the default host + if (remoteHost) { + //setup the address + remote_add.sin_port = htons(remotePort); + //get the server IP + Host = gethostbyname(remoteHost); + if (Host == NULL) return GF_IP_ADDRESS_NOT_FOUND; + memcpy((char *) &remote_add.sin_addr, Host->h_addr_list[0], sizeof(u32)); + } else { + remote_add.sin_port = sock->dest_addr.sin_port; + remote_add.sin_addr.s_addr = sock->dest_addr.sin_addr.s_addr; + } +#endif + Count = 0; + while (Count < length) { + Res = sendto(sock->socket, (char *) &buffer[Count], length - Count, 0, (struct sockaddr *) &remote_add, remote_add_len); + if (Res == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + Count += Res; + } + return GF_OK; +} + + + + +GF_Err gf_sk_receive_wait(GF_Socket *sock, char *buffer, u32 length, u32 startFrom, u32 *BytesRead, u32 Second ) +{ + GF_Err e; + u32 res; +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + e = GF_OK; + + *BytesRead = 0; + if (startFrom >= length) return GF_OK; + + +#ifndef __SYMBIAN32__ + //can we read? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = Second; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + res = 0; + ready = select(sock->socket+1, &Group, NULL, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + if (!FD_ISSET(sock->socket, &Group)) { + return GF_IP_NETWORK_EMPTY; + } +#endif + + + res = recv(sock->socket, (char *) buffer + startFrom, length - startFrom, 0); + if (res == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + *BytesRead = res; + return GF_OK; +} + + +//send length bytes of a buffer +GF_Err gf_sk_send_wait(GF_Socket *sock, char *buffer, u32 length, u32 Second ) +{ + + GF_Err e; + u32 Count, Res; +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + e = GF_OK; + + //the socket must be bound or connected + if (!sock || !sock->socket) return GF_BAD_PARAM; + +#ifndef __SYMBIAN32__ + //can we write? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = Second; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + ready = select(sock->socket+1, NULL, &Group, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + //should never happen (to check: is writeability is guaranteed for not-connected sockets) + if (!ready || !FD_ISSET(sock->socket, &Group)) { + return GF_IP_NETWORK_EMPTY; + } +#endif + + //direct writing + Count = 0; + while (Count < length) { + Res = send(sock->socket, (char *) &buffer[Count], length - Count, 0); + if (Res == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; +#ifndef __SYMBIAN32__ + case ECONNRESET: + return GF_IP_CONNECTION_CLOSED; +#endif + default: + return GF_IP_NETWORK_FAILURE; + } + } + Count += Res; + } + return GF_OK; +} + diff --git a/src/utils/os_thread.c b/src/utils/os_thread.c new file mode 100644 index 0000000..dd69890 --- /dev/null +++ b/src/utils/os_thread.c @@ -0,0 +1,566 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#if defined(WIN32) || defined(_WIN32_WCE) + +/*win32 threads*/ +#include +typedef HANDLE TH_HANDLE; + +#else +/*pthreads*/ +#include +#include +#include +typedef pthread_t TH_HANDLE ; + +#endif + + + +/********************************************************************* + OS-Specific Thread Object +**********************************************************************/ +struct __tag_thread +{ + + u32 status; + TH_HANDLE threadH; + u32 stackSize; + /* the thread procedure */ + u32 (*Run)(void *param); + void *args; + /* lock for signal */ + GF_Semaphore *_signal; +#ifndef GPAC_DISABLE_LOG + u32 id; + char *log_name; +#endif +}; + + +#ifndef GPAC_DISABLE_LOG +#include +static GF_List *thread_bank = NULL; + +static void log_add_thread(GF_Thread *t) +{ + if (!thread_bank) thread_bank = gf_list_new(); + gf_list_add(thread_bank, t); +} +static void log_del_thread(GF_Thread *t) +{ + gf_list_del_item(thread_bank, t); + if (!gf_list_count(thread_bank)) { + gf_list_del(thread_bank); + thread_bank = NULL; + } +} +static const char *log_th_name(u32 id) +{ + u32 i, count; + + if (!id) id = gf_th_id(); + count = gf_list_count(thread_bank); + for (i=0; iid == id) return t->log_name; + } + return "Main Process"; +} + +#endif + + +GF_Thread *gf_th_new(const char *name) +{ + GF_Thread *tmp = malloc(sizeof(GF_Thread)); + memset(tmp, 0, sizeof(GF_Thread)); + tmp->status = GF_THREAD_STATUS_STOP; + +#ifndef GPAC_DISABLE_LOG + if (name) { + tmp->log_name = strdup(name); + } else { + char szN[20]; + sprintf(szN, "0x%08x", (u32) tmp); + tmp->log_name = strdup(szN); + } + log_add_thread(tmp); +#endif + return tmp; +} + +#ifdef WIN32 +DWORD WINAPI RunThread(void *ptr) +{ + DWORD ret = 0; +#else +void *RunThread(void *ptr) +{ + u32 ret = 0; +#endif + GF_Thread *t = (GF_Thread *)ptr; + + /* Signal the caller */ + if (! t->_signal) goto exit; + + t->status = GF_THREAD_STATUS_RUN; + gf_sema_notify(t->_signal, 1); + +#ifndef GPAC_DISABLE_LOG + t->id = gf_th_id(); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Thread %s] Entering thread proc - thread ID 0x%08x\n", t->log_name, t->id)); +#endif + /* Run our thread */ + ret = t->Run(t->args); + +exit: +#ifndef GPAC_DISABLE_LOG + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Thread %s] Exiting thread proc\n", t->log_name)); +#endif + t->status = GF_THREAD_STATUS_DEAD; + t->Run = NULL; +#ifdef WIN32 + CloseHandle(t->threadH); + t->threadH = NULL; + return ret; +#else + pthread_exit((void *)0); + return (void *)ret; +#endif +} + +GF_Err gf_th_run(GF_Thread *t, u32 (*Run)(void *param), void *param) +{ +#ifdef WIN32 + DWORD id; +#else + pthread_attr_t att; +#endif + if (!t || t->Run || t->_signal) return GF_BAD_PARAM; + t->Run = Run; + t->args = param; + t->_signal = gf_sema_new(1, 0); + +#ifdef WIN32 + t->threadH = CreateThread(NULL, t->stackSize, &(RunThread), (void *)t, 0, &id); + if (t->threadH == NULL) { +#else + if ( pthread_attr_init(&att) != 0 ) return GF_IO_ERR; + pthread_attr_setdetachstate(&att, PTHREAD_CREATE_JOINABLE); + if ( pthread_create(&t->threadH, &att, RunThread, t) != 0 ) { +#endif + t->status = GF_THREAD_STATUS_DEAD; + return GF_IO_ERR; + } + + /*wait for the child function to call us - do NOT return before, otherwise the thread status would + be unknown*/ + gf_sema_wait(t->_signal); + gf_sema_del(t->_signal); + t->_signal = NULL; + return GF_OK; +} + + +/* Stops a thread. If Destroy is not 0, thread is destroyed DANGEROUS as no cleanup */ +void Thread_Stop(GF_Thread *t, Bool Destroy) +{ + if (gf_th_status(t) == GF_THREAD_STATUS_RUN) { +#ifdef WIN32 + if (Destroy) { + DWORD dw = 1; + TerminateThread(t->threadH, dw); + t->threadH = NULL; + } else { + WaitForSingleObject(t->threadH, INFINITE); + } +#else + if (Destroy) { + pthread_cancel(t->threadH); + t->threadH = 0; + } else { + /*gracefully wait for Run to finish*/ + pthread_join(t->threadH, NULL); + } +#endif + } + t->status = GF_THREAD_STATUS_DEAD; +} + +void gf_th_stop(GF_Thread *t) +{ + Thread_Stop(t, 0); +} + +void gf_th_del(GF_Thread *t) +{ + Thread_Stop(t, 0); +#ifdef WIN32 +// if (t->threadH) CloseHandle(t->threadH); +#endif + +#ifndef GPAC_DISABLE_LOG + free(t->log_name); + log_del_thread(t); +#endif + free(t); +} + + +void gf_th_set_priority(GF_Thread *t, s32 priority) +{ +#ifdef WIN32 + /*!! in WinCE, changin thread priority is extremely dangerous, it may freeze threads randomly !!*/ +#ifndef _WIN32_WCE + SetThreadPriority(t ? t->threadH : GetCurrentThread(), priority); +#endif + +#else + + struct sched_param s_par; + if (!t) return; + + /* consider this as real-time priority */ + if (priority > 200) { + s_par.sched_priority = priority - 200; + pthread_setschedparam(t->threadH, SCHED_RR, &s_par); + } else { + s_par.sched_priority = priority; + pthread_setschedparam(t->threadH, SCHED_OTHER, &s_par); + } + +#endif +} + +u32 gf_th_status(GF_Thread *t) +{ + if (!t) return 0; + return t->status; +} + + +u32 gf_th_id() +{ +#ifdef WIN32 + return ((u32) GetCurrentThreadId()); +#else + return ((u32) pthread_self()); +#endif +} + + +/********************************************************************* + OS-Specific Mutex Object +**********************************************************************/ +struct __tag_mutex +{ +#ifdef WIN32 + HANDLE hMutex; +#else + pthread_mutex_t hMutex; +#endif + /* We filter recursive calls (1 thread calling Lock several times in a row only locks + ONCE the mutex. Holder is the current ThreadID of the mutex holder*/ + u32 Holder, HolderCount; +#ifndef GPAC_DISABLE_LOG + char *log_name; +#endif +}; + + +GF_Mutex *gf_mx_new(const char *name) +{ +#ifndef WIN32 + pthread_mutexattr_t attr; +#endif + GF_Mutex *tmp = malloc(sizeof(GF_Mutex)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_Mutex)); + +#ifdef WIN32 + tmp->hMutex = CreateMutex(NULL, FALSE, NULL); + if (!tmp->hMutex) { +#else + pthread_mutexattr_init(&attr); + if ( pthread_mutex_init(&tmp->hMutex, &attr) != 0 ) { +#endif + free(tmp); + return NULL; + } + +#ifndef GPAC_DISABLE_LOG + if (name) { + tmp->log_name = strdup(name); + } else { + char szN[20]; + sprintf(szN, "0x%08x", (u32) tmp); + tmp->log_name = strdup(szN); + } +#endif + + return tmp; +} + +void gf_mx_del(GF_Mutex *mx) +{ +#ifdef WIN32 + CloseHandle(mx->hMutex); +#else + pthread_mutex_destroy(&mx->hMutex); +#endif +#ifndef GPAC_DISABLE_LOG + free(mx->log_name); +#endif + free(mx); +} + +void gf_mx_v(GF_Mutex *mx) +{ + u32 caller; + if (!mx) return; + caller = gf_th_id(); + + /*only if we own*/ + assert(caller == mx->Holder); + if (caller != mx->Holder) return; + assert(mx->HolderCount > 0); + mx->HolderCount -= 1; + + if (mx->HolderCount == 0) { +#ifndef GPAC_DISABLE_LOG + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Mutex %s] Released by thread %s\n", mx->log_name, log_th_name(mx->Holder) )); +#endif + mx->Holder = 0; +#ifdef WIN32 + ReleaseMutex(mx->hMutex); +#else + pthread_mutex_unlock(&mx->hMutex); +#endif + } +} + +u32 gf_mx_p(GF_Mutex *mx) +{ + u32 caller; + if (!mx) return 0; + caller = gf_th_id(); + if (caller == mx->Holder) { + mx->HolderCount += 1; + return 1; + } + +#ifndef GPAC_DISABLE_LOG + if (mx->Holder) + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Mutex %s] Thread %s waiting a release from thread %s\n", mx->log_name, log_th_name(caller), log_th_name(mx->Holder) )); +#endif + +#ifdef WIN32 + switch (WaitForSingleObject(mx->hMutex, INFINITE)) { + case WAIT_ABANDONED: + case WAIT_TIMEOUT: + return 0; + default: + break; + } +#else + if (pthread_mutex_lock(&mx->hMutex) != 0 ) { + assert(0); + return 0; + } +#endif + mx->HolderCount = 1; + mx->Holder = caller; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Mutex %s] Grabbed by thread %s\n", mx->log_name, log_th_name(mx->Holder) )); + return 1; +} + +Bool gf_mx_try_lock(GF_Mutex *mx) +{ + u32 caller; + if (!mx) return 0; + caller = gf_th_id(); + if (caller == mx->Holder) { + mx->HolderCount += 1; + return 1; + } + +#ifdef WIN32 + /*wait for 1 ms (I can't figure out from MS doc if 0 timeout only "tests the state" or also lock the mutex ... */ + switch (WaitForSingleObject(mx->hMutex, 1)) { + case WAIT_ABANDONED: + case WAIT_TIMEOUT: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Mutex %s] Couldn't release it for thread %s (grabbed by thread %s)\n", mx->log_name, log_th_name(caller), log_th_name(mx->Holder) )); + return 0; + default: + break; + } +#else + if (pthread_mutex_trylock(&mx->hMutex) != 0 ) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Mutex %s] Couldn't release it for thread %s (grabbed by thread %s)\n", mx->log_name, log_th_name(caller), log_th_name(mx->Holder) )); + return 0; + } +#endif + mx->Holder = caller; + mx->HolderCount = 1; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Mutex %s] Grabbed by thread %s\n", mx->log_name, log_th_name(mx->Holder) )); + return 1; +} + + +/********************************************************************* + OS-Specific Semaphore Object +**********************************************************************/ +struct __tag_semaphore +{ +#ifdef WIN32 + HANDLE hSemaphore; +#else + sem_t *hSemaphore; + sem_t SemaData; +#if defined(__DARWIN__) || defined(__APPLE__) + const char *SemName; +#endif +#endif +}; + + +GF_Semaphore *gf_sema_new(u32 MaxCount, u32 InitCount) +{ + GF_Semaphore *tmp = malloc(sizeof(GF_Semaphore)); + + if (!tmp) return NULL; +#if defined(WIN32) + tmp->hSemaphore = CreateSemaphore(NULL, InitCount, MaxCount, NULL); +#elif defined(__DARWIN__) || defined(__APPLE__) + /* sem_init isn't supported on Mac OS X 10.3 & 10.4; it returns ENOSYS + To get around this, a NAMED semaphore needs to be used + sem_t *sem_open(const char *name, int oflag, ...); + http://users.evitech.fi/~hannuvl/ke04/reaaliaika_ohj/memmap_named_semaphores.c + */ + { + char semaName[40]; + sprintf(semaName,"GPAC_SEM%d", (u32) tmp); + tmp->SemName = strdup(semaName); + } + tmp->hSemaphore = sem_open(tmp->SemName, O_CREAT, S_IRUSR|S_IWUSR, InitCount); +#else + if (sem_init(&tmp->SemaData, 0, InitCount) < 0 ) { + free(tmp); + return NULL; + } + tmp->hSemaphore = &tmp->SemaData; +#endif + + if (!tmp->hSemaphore) { + free(tmp); + return NULL; + } + return tmp; +} + +void gf_sema_del(GF_Semaphore *sm) +{ +#if defined(WIN32) + CloseHandle(sm->hSemaphore); +#elif defined(__DARWIN__) || defined(__APPLE__) + sem_t *sema = sem_open(sm->SemName, 0); + sem_destroy(sema); + free(sm->SemName); +#else + sem_destroy(sm->hSemaphore); +#endif + free(sm); +} + +u32 gf_sema_notify(GF_Semaphore *sm, u32 NbRelease) +{ + u32 prevCount; +#ifndef WIN32 + sem_t *hSem; +#endif + + if (!sm) return 0; + +#if defined(WIN32) + ReleaseSemaphore(sm->hSemaphore, NbRelease, (LPLONG) &prevCount); +#else + +#if defined(__DARWIN__) || defined(__APPLE__) + hSem = sem_open(sm->SemName, 0); +#else + hSem = sm->hSemaphore; +#endif + sem_getvalue(hSem, (s32 *) &prevCount); + while (NbRelease) { + if (sem_post(hSem) < 0) return 0; + NbRelease -= 1; + } +#endif + return (u32) prevCount; +} + +void gf_sema_wait(GF_Semaphore *sm) +{ +#ifdef WIN32 + WaitForSingleObject(sm->hSemaphore, INFINITE); +#else + sem_t *hSem; +#if defined(__DARWIN__) || defined(__APPLE__) + hSem = sem_open(sm->SemName, 0); +#else + hSem = sm->hSemaphore; +#endif + sem_wait(hSem); +#endif +} + +Bool gf_sema_wait_for(GF_Semaphore *sm, u32 TimeOut) +{ +#ifdef WIN32 + if (WaitForSingleObject(sm->hSemaphore, TimeOut) == WAIT_TIMEOUT) return 0; + return 1; +#else + sem_t *hSem; +#if defined(__DARWIN__) || defined(__APPLE__) + hSem = sem_open(sm->SemName, 0); +#else + hSem = sm->hSemaphore; +#endif + if (!TimeOut) { + if (!sem_trywait(hSem)) return 1; + return 0; + } + TimeOut += gf_sys_clock(); + do { + if (!sem_trywait(hSem)) return 1; + gf_sleep(1); + } while (gf_sys_clock() < TimeOut); + return 0; +#endif +} + diff --git a/src/utils/path2d.c b/src/utils/path2d.c new file mode 100644 index 0000000..bc29d78 --- /dev/null +++ b/src/utils/path2d.c @@ -0,0 +1,1359 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include + +GF_EXPORT +GF_Path *gf_path_new() +{ + GF_Path *gp; + GF_SAFEALLOC(gp, GF_Path); + gp->fineness = FIX_ONE; + return gp; +} + +GF_EXPORT +void gf_path_reset(GF_Path *gp) +{ + Fixed fineness; + u32 flags; + if (!gp) return; + if (gp->contours) free(gp->contours); + if (gp->tags) free(gp->tags); + if (gp->points) free(gp->points); + fineness = gp->fineness ? gp->fineness : FIX_ONE; + flags = gp->flags; + memset(gp, 0, sizeof(GF_Path)); + gp->flags = flags | GF_PATH_FLATTENED | GF_PATH_BBOX_DIRTY; + gp->fineness = fineness; +} + +GF_EXPORT +GF_Path *gf_path_clone(GF_Path *gp) +{ + GF_Path *dst; + GF_SAFEALLOC(dst, GF_Path); + if (!dst) return NULL; + dst->contours = (u32 *)malloc(sizeof(u32)*gp->n_contours); + if (!dst->contours) { free(dst); return NULL; } + dst->points = (GF_Point2D *) malloc(sizeof(GF_Point2D)*gp->n_points); + if (!dst->points) { free(dst->contours); free(dst); return NULL; } + dst->tags = (u8 *) malloc(sizeof(u8)*gp->n_points); + if (!dst->tags) { free(dst->points); free(dst->contours); free(dst); return NULL; } + memcpy(dst->contours, gp->contours, sizeof(u32)*gp->n_contours); + dst->n_contours = gp->n_contours; + memcpy(dst->points, gp->points, sizeof(GF_Point2D)*gp->n_points); + memcpy(dst->tags, gp->tags, sizeof(u8)*gp->n_points); + dst->n_alloc_points = dst->n_points = gp->n_points; + dst->flags = gp->flags; + dst->bbox = gp->bbox; + dst->fineness = gp->fineness; + return dst; +} + +GF_EXPORT +void gf_path_del(GF_Path *gp) +{ + if (!gp) return; + if (gp->contours) free(gp->contours); + if (gp->tags) free(gp->tags); + if (gp->points) free(gp->points); + free(gp); +} + +#define GF_2D_REALLOC(_gp) \ + if (_gp->n_alloc_points < _gp->n_points+3) { \ + _gp->n_alloc_points = (_gp->n_alloc_points<5) ? 10 : (_gp->n_alloc_points*3/2); \ + _gp->points = (GF_Point2D *)realloc(_gp->points, sizeof(GF_Point2D)*(_gp->n_alloc_points)); \ + _gp->tags = (u8 *) realloc(_gp->tags, sizeof(u8)*(_gp->n_alloc_points)); \ + } \ + + +GF_EXPORT +GF_Err gf_path_add_move_to(GF_Path *gp, Fixed x, Fixed y) +{ + if (!gp) return GF_BAD_PARAM; + +#if 0 + /*skip empty paths*/ + if ((gp->n_contours>=2) && (gp->contours[gp->n_contours-2]+1==gp->contours[gp->n_contours-1])) { + gp->points[gp->n_points].x = x; + gp->points[gp->n_points].y = y; + return GF_OK; + } +#endif + + gp->contours = (u32 *) realloc(gp->contours, sizeof(u32)*(gp->n_contours+1)); + GF_2D_REALLOC(gp) + + gp->points[gp->n_points].x = x; + gp->points[gp->n_points].y = y; + gp->tags[gp->n_points] = 1; + /*set end point*/ + gp->contours[gp->n_contours] = gp->n_points; + /*new contour*/ + gp->n_contours++; + gp->n_points++; + gp->flags |= GF_PATH_BBOX_DIRTY; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_path_add_move_to_vec(GF_Path *gp, GF_Point2D *pt) { return gf_path_add_move_to(gp, pt->x, pt->y); } + +GF_EXPORT +GF_Err gf_path_add_line_to(GF_Path *gp, Fixed x, Fixed y) +{ + if (!gp || !gp->n_contours) return GF_BAD_PARAM; + /*we allow line to same point as move (seen in SVG sequences) - striking will make a point*/ + + GF_2D_REALLOC(gp) + gp->points[gp->n_points].x = x; + gp->points[gp->n_points].y = y; + gp->tags[gp->n_points] = 1; + /*set end point*/ + gp->contours[gp->n_contours-1] = gp->n_points; + gp->n_points++; + gp->flags |= GF_PATH_BBOX_DIRTY; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_path_add_line_to_vec(GF_Path *gp, GF_Point2D *pt) { return gf_path_add_line_to(gp, pt->x, pt->y); } + +GF_EXPORT +GF_Err gf_path_close(GF_Path *gp) +{ + Fixed diff; + GF_Point2D start, end; + if (!gp || !gp->n_contours) return GF_BAD_PARAM; + + if (gp->n_contours<=1) start = gp->points[0]; + else start = gp->points[gp->contours[gp->n_contours-2] + 1]; + end = gp->points[gp->n_points-1]; + end.x -= start.x; + end.y -= start.y; + diff = gf_mulfix(end.x, end.x) + gf_mulfix(end.y, end.y); + if (ABS(diff) > FIX_ONE/1000) { + GF_Err e = gf_path_add_line_to(gp, start.x, start.y); + if (e) return e; + } + gp->tags[gp->n_points-1] = GF_PATH_CLOSE; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_path_add_cubic_to(GF_Path *gp, Fixed c1_x, Fixed c1_y, Fixed c2_x, Fixed c2_y, Fixed x, Fixed y) +{ + if (!gp || !gp->n_contours) return GF_BAD_PARAM; + GF_2D_REALLOC(gp) + gp->points[gp->n_points].x = c1_x; + gp->points[gp->n_points].y = c1_y; + gp->tags[gp->n_points] = GF_PATH_CURVE_CUBIC; + gp->n_points++; + gp->points[gp->n_points].x = c2_x; + gp->points[gp->n_points].y = c2_y; + gp->tags[gp->n_points] = GF_PATH_CURVE_CUBIC; + gp->n_points++; + gp->points[gp->n_points].x = x; + gp->points[gp->n_points].y = y; + gp->tags[gp->n_points] = GF_PATH_CURVE_ON; + /*set end point*/ + gp->contours[gp->n_contours-1] = gp->n_points; + gp->n_points++; + gp->flags |= GF_PATH_BBOX_DIRTY; + gp->flags &= ~GF_PATH_FLATTENED; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_path_add_cubic_to_vec(GF_Path *gp, GF_Point2D *c1, GF_Point2D *c2, GF_Point2D *pt) +{ + return gf_path_add_cubic_to(gp, c1->x, c1->y, c2->x, c2->y, pt->x, pt->y); +} + + +GF_EXPORT +GF_Err gf_path_add_quadratic_to(GF_Path *gp, Fixed c_x, Fixed c_y, Fixed x, Fixed y) +{ + if (!gp || !gp->n_contours) return GF_BAD_PARAM; + GF_2D_REALLOC(gp) + gp->points[gp->n_points].x = c_x; + gp->points[gp->n_points].y = c_y; + gp->tags[gp->n_points] = GF_PATH_CURVE_CONIC; + gp->n_points++; + gp->points[gp->n_points].x = x; + gp->points[gp->n_points].y = y; + gp->tags[gp->n_points] = GF_PATH_CURVE_ON; + /*set end point*/ + gp->contours[gp->n_contours-1] = gp->n_points; + gp->n_points++; + gp->flags |= GF_PATH_BBOX_DIRTY; + gp->flags &= ~GF_PATH_FLATTENED; + return GF_OK; +} +GF_EXPORT +GF_Err gf_path_add_quadratic_to_vec(GF_Path *gp, GF_Point2D *c, GF_Point2D *pt) +{ + return gf_path_add_quadratic_to(gp, c->x, c->y, pt->x, pt->y); +} + +/*adds rectangle centered on cx, cy*/ +GF_EXPORT +GF_Err gf_path_add_rect_center(GF_Path *gp, Fixed cx, Fixed cy, Fixed w, Fixed h) +{ + GF_Err e = gf_path_add_move_to(gp, cx - w/2, cy - h/2); + if (e) return e; + e = gf_path_add_line_to(gp, cx + w/2, cy - h/2); + if (e) return e; + e = gf_path_add_line_to(gp, cx + w/2, cy + h/2); + if (e) return e; + e = gf_path_add_line_to(gp, cx - w/2, cy + h/2); + if (e) return e; + return gf_path_close(gp); +} + +GF_EXPORT +GF_Err gf_path_add_rect(GF_Path *gp, Fixed ox, Fixed oy, Fixed w, Fixed h) +{ + GF_Err e = gf_path_add_move_to(gp, ox, oy); + if (e) return e; + e = gf_path_add_line_to(gp, ox + w, oy); + if (e) return e; + e = gf_path_add_line_to(gp, ox+w, oy-h); + if (e) return e; + e = gf_path_add_line_to(gp, ox, oy-h); + if (e) return e; + return gf_path_close(gp); +} + +#define GF_2D_DEFAULT_RES 64 + +GF_EXPORT +GF_Err gf_path_add_ellipse(GF_Path *gp, Fixed cx, Fixed cy, Fixed a_axis, Fixed b_axis) +{ + GF_Err e; + Fixed _vx, _vy, cur; + u32 i; + a_axis /= 2; + b_axis /= 2; + e = gf_path_add_move_to(gp, cx+a_axis, cy); + if (e) return e; + for (i=1; icontours = realloc(gp->contours, sizeof(u32) * (gp->n_contours + src->n_contours)); + if (!gp->contours) return GF_OUT_OF_MEM; + for (i=0; in_contours; i++) { + gp->contours[i+gp->n_contours] = src->contours[i] + gp->n_points; + } + gp->n_contours += src->n_contours; + gp->n_alloc_points += src->n_alloc_points; + gp->points = realloc(gp->points, sizeof(GF_Point2D)*gp->n_alloc_points); + if (!gp->points) return GF_OUT_OF_MEM; + gp->tags = realloc(gp->tags, sizeof(u8)*gp->n_alloc_points); + if (!gp->tags) return GF_OUT_OF_MEM; + memcpy(gp->points + gp->n_points, src->points, sizeof(GF_Point2D)*src->n_points); + if (mx) { + for (i=0;in_points; i++) { + gf_mx2d_apply_coords(mx, &gp->points[i+gp->n_points].x, &gp->points[i+gp->n_points].y); + } + } + memcpy(gp->tags + gp->n_points, src->tags, sizeof(u8)*src->n_points); + gp->n_points += src->n_points; + gf_rect_union(&gp->bbox, &src->bbox); + if (!(src->flags & GF_PATH_FLATTENED)) gp->flags &= ~GF_PATH_FLATTENED; + if (src->flags & GF_PATH_BBOX_DIRTY) gp->flags |= GF_PATH_BBOX_DIRTY; + return GF_OK; +} + +/*generic N-bezier*/ +static void NBezier(GF_Point2D *pts, s32 n, Double mu, GF_Point2D *pt_out) +{ + s32 k,kn,nn,nkn; + Double blend, muk, munk; + pt_out->x = pt_out->y = 0; + + muk = 1; + munk = pow(1-mu,(Double)n); + for (k=0;k<=n;k++) { + nn = n; + kn = k; + nkn = n - k; + blend = muk * munk; + muk *= mu; + munk /= (1-mu); + while (nn >= 1) { + blend *= nn; + nn--; + if (kn > 1) { + blend /= (double)kn; + kn--; + } + if (nkn > 1) { + blend /= (double)nkn; + nkn--; + } + } + pt_out->x += gf_mulfix(pts[k].x, FLT2FIX(blend)); + pt_out->y += gf_mulfix(pts[k].y, FLT2FIX(blend)); + } +} + +static void gf_add_n_bezier(GF_Path *gp, GF_Point2D *newpts, u32 nbPoints) +{ + Double mu; + u32 numPoints, i; + GF_Point2D start, end; + numPoints = (u32) FIX2INT(GF_2D_DEFAULT_RES * gp->fineness); + mu = 0.0; + if (numPoints) mu = 1/(Double)numPoints; + start = newpts[0]; + for (i=1; in_points) return GF_BAD_PARAM; + + newpts = (GF_Point2D *) malloc(sizeof(GF_Point2D) * (nbPoints+1)); + newpts[0] = gp->points[gp->n_points-1]; + memcpy(&newpts[1], pts, sizeof(GF_Point2D) * nbPoints); + + gf_add_n_bezier(gp, newpts, nbPoints + 1); + + free(newpts); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_path_add_arc_to(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed fa_x, Fixed fa_y, Fixed fb_x, Fixed fb_y, Bool cw) +{ + GF_Matrix2D mat, inv; + Fixed angle, start_angle, end_angle, sweep, axis_w, axis_h, tmp, cx, cy, _vx, _vy, start_x, start_y; + s32 i, num_steps; + + if (!gp->n_points) return GF_BAD_PARAM; + + start_x = gp->points[gp->n_points-1].x; + start_y = gp->points[gp->n_points-1].y; + + cx = (fb_x + fa_x)/2; + cy = (fb_y + fa_y)/2; + + angle = gf_atan2(fb_y-fa_y, fb_x-fa_x); + gf_mx2d_init(mat); + gf_mx2d_add_rotation(&mat, 0, 0, angle); + gf_mx2d_add_translation(&mat, cx, cy); + + gf_mx2d_copy(inv, mat); + gf_mx2d_inverse(&inv); + gf_mx2d_apply_coords(&inv, &start_x, &start_y); + gf_mx2d_apply_coords(&inv, &end_x, &end_y); + gf_mx2d_apply_coords(&inv, &fa_x, &fa_y); + gf_mx2d_apply_coords(&inv, &fb_x, &fb_y); + + //start angle and end angle + start_angle = gf_atan2(start_y, start_x); + end_angle = gf_atan2(end_y, end_x); + tmp = gf_mulfix((start_x - fa_x), (start_x - fa_x)) + gf_mulfix((start_y - fa_y), (start_y - fa_y)); + axis_w = gf_sqrt(tmp); + tmp = gf_mulfix((start_x - fb_x) , (start_x - fb_x)) + gf_mulfix((start_y - fb_y), (start_y - fb_y)); + axis_w += gf_sqrt(tmp); + axis_w /= 2; + axis_h = gf_sqrt(gf_mulfix(axis_w, axis_w) - gf_mulfix(fa_x,fa_x)); + sweep = end_angle - start_angle; + + if (cw) { + if (sweep>0) sweep -= 2*GF_PI; + } else { + if (sweep<0) sweep += 2*GF_PI; + } + num_steps = GF_2D_DEFAULT_RES/2; + for (i=1; i<=num_steps; i++) { + angle = start_angle + sweep*i/num_steps; + _vx = gf_mulfix(axis_w, gf_cos(angle)); + _vy = gf_mulfix(axis_h, gf_sin(angle)); + /*re-invert*/ + gf_mx2d_apply_coords(&mat, &_vx, &_vy); + gf_path_add_line_to(gp, _vx, _vy); + } + return GF_OK; +} + + + +GF_EXPORT +GF_Err gf_path_add_svg_arc_to(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed r_x, Fixed r_y, Fixed x_axis_rotation, Bool large_arc_flag, Bool sweep_flag) +{ + Fixed start_x, start_y; + Fixed xmid,ymid; + Fixed xmidp,ymidp; + Fixed xmidpsq,ymidpsq; + Fixed phi, cos_phi, sin_phi; + Fixed c_x, c_y; + Fixed cxp, cyp; + Fixed scale; + Fixed rxsq, rysq; + Fixed start_angle, sweep_angle; + Fixed radius_scale; + Fixed vx, vy, normv; + Fixed ux, uy, normu; + Fixed sign; + u32 i, num_steps; + + if (!gp->n_points) return GF_BAD_PARAM; + + if (!r_x || !r_y) { + gf_path_add_line_to(gp, end_x, end_y); + return GF_OK; + } + + if (r_x < 0) r_x = -r_x; + if (r_y < 0) r_y = -r_y; + + start_x = gp->points[gp->n_points-1].x; + start_y = gp->points[gp->n_points-1].y; + + phi = gf_mulfix(gf_divfix(x_axis_rotation, 180), GF_PI); + cos_phi = gf_cos(phi); + sin_phi = gf_sin(phi); + xmid = (start_x - end_x)/2; + ymid = (start_y - end_y)/2; + if (!xmid && !ymid) { + gf_path_add_line_to(gp, end_x, end_y); + return GF_OK; + } + + xmidp = gf_mulfix(cos_phi, xmid) + gf_mulfix(sin_phi, ymid); + ymidp = gf_mulfix(-sin_phi, xmid) + gf_mulfix(cos_phi, ymid); + xmidpsq = gf_mulfix(xmidp, xmidp); + ymidpsq = gf_mulfix(ymidp, ymidp); + + rxsq = gf_mulfix(r_x, r_x); + rysq = gf_mulfix(r_y, r_y); + assert(rxsq && rxsq); + + radius_scale = gf_divfix(xmidpsq, rxsq) + gf_divfix(ymidpsq, rysq); + if (radius_scale > FIX_ONE) { + r_x = gf_mulfix(gf_sqrt(radius_scale), r_x); + r_y = gf_mulfix(gf_sqrt(radius_scale), r_y); + rxsq = gf_mulfix(r_x, r_x); + rysq = gf_mulfix(r_y, r_y); + } + +#if 0 + /* Old code with overflow problems in fixed point, + sign was sometimes negative (cf tango SVG icons appointment-new.svg)*/ + + sign = gf_mulfix(rxsq,ymidpsq) + gf_mulfix(rysq, xmidpsq); + scale = FIX_ONE; + /*FIXME - what if scale is 0 ??*/ + if (sign) scale = gf_divfix( + (gf_mulfix(rxsq,rysq) - gf_mulfix(rxsq, ymidpsq) - gf_mulfix(rysq,xmidpsq)), + sign + ); +#else + /* New code: the sign variable computation is split into simpler cases and + the expression is divided by rxsq to reduce the range */ + if ((rxsq == 0 || ymidpsq ==0) && (rysq == 0 || xmidpsq == 0)) { + scale = FIX_ONE; + } else if (rxsq == 0 || ymidpsq ==0) { + scale = gf_divfix(rxsq,xmidpsq) - FIX_ONE; + } else if (rysq == 0 || xmidpsq == 0) { + scale = gf_divfix(rysq,ymidpsq) - FIX_ONE; + } else { + Fixed tmp; + tmp = gf_mulfix(gf_divfix(rysq, rxsq), xmidpsq); + sign = ymidpsq + tmp; + scale = gf_divfix((rysq - ymidpsq - tmp),sign); + } +#endif + /* precision problem may lead to negative value around zero, we need to take care of it before sqrt */ + scale = gf_sqrt(ABS(scale)); + + cxp = gf_mulfix(scale, gf_divfix(gf_mulfix(r_x, ymidp),r_y)); + cyp = gf_mulfix(scale, -gf_divfix(gf_mulfix(r_y, xmidp),r_x)); + cxp = (large_arc_flag == sweep_flag ? - cxp : cxp); + cyp = (large_arc_flag == sweep_flag ? - cyp : cyp); + + c_x = gf_mulfix(cos_phi, cxp) - gf_mulfix(sin_phi, cyp) + (start_x + end_x)/2; + c_y = gf_mulfix(sin_phi, cxp) + gf_mulfix(cos_phi, cyp) + (start_y + end_y)/2; + + ux = FIX_ONE; + uy = 0; + normu = FIX_ONE; + + vx = gf_divfix(xmidp-cxp,r_x); + vy = gf_divfix(ymidp-cyp,r_y); + normv = gf_sqrt(gf_mulfix(vx, vx) + gf_mulfix(vy,vy)); + + sign = vy; + start_angle = gf_acos(gf_divfix(vx,normv)); + start_angle = (sign > 0 ? start_angle: -start_angle); + + ux = vx; + uy = vy; + normu = normv; + + vx = gf_divfix(-xmidp-cxp,r_x); + vy = gf_divfix(-ymidp-cyp,r_y); + normu = gf_sqrt(gf_mulfix(ux, ux) + gf_mulfix(uy,uy)); + + sign = gf_mulfix(ux, vy) - gf_mulfix(uy, vx); + sweep_angle = gf_divfix( gf_mulfix(ux,vx) + gf_mulfix(uy, vy), gf_mulfix(normu, normv) ); + /*numerical stability safety*/ + sweep_angle = MAX(-FIX_ONE, MIN(sweep_angle, FIX_ONE)); + sweep_angle = gf_acos(sweep_angle); + sweep_angle = (sign > 0 ? sweep_angle: -sweep_angle); + if (sweep_flag == 0) { + if (sweep_angle > 0) sweep_angle -= GF_2PI; + } else { + if (sweep_angle < 0) sweep_angle += GF_2PI; + } + + num_steps = GF_2D_DEFAULT_RES/2; + for (i=1; i<=num_steps; i++) { + Fixed _vx, _vy; + Fixed _vxp, _vyp; + Fixed angle = start_angle + sweep_angle*i/num_steps; + _vx = gf_mulfix(r_x, gf_cos(angle)); + _vy = gf_mulfix(r_y, gf_sin(angle)); + _vxp = gf_mulfix(cos_phi, _vx) - gf_mulfix(sin_phi, _vy) + c_x; + _vyp = gf_mulfix(sin_phi, _vx) + gf_mulfix(cos_phi, _vy) + c_y; + gf_path_add_line_to(gp, _vxp, _vyp); + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_path_add_arc(GF_Path *gp, Fixed radius, Fixed start_angle, Fixed end_angle, u32 close_type) +{ + GF_Err e; + Fixed _vx, _vy, step, cur; + s32 i, do_run; + + step = (end_angle - start_angle) / (GF_2D_DEFAULT_RES); + radius *= 2; + + /*pie*/ + i=0; + if (close_type==2) { + gf_path_add_move_to(gp, 0, 0); + i=1; + } + do_run = 1; + cur=start_angle; + while (do_run) { + if (cur>=end_angle) { + do_run = 0; + cur = end_angle; + } + _vx = gf_mulfix(radius, gf_cos(cur)); + _vy = gf_mulfix(radius, gf_sin(cur)); + if (!i) { + e = gf_path_add_move_to(gp, _vx, _vy); + i = 1; + } else { + e = gf_path_add_line_to(gp, _vx, _vy); + } + if (e) return e; + cur+=step; + } + if (close_type) e = gf_path_close(gp); + return e; +} + + +GF_EXPORT +GF_Err gf_path_get_control_bounds(GF_Path *gp, GF_Rect *rc) +{ + GF_Point2D *pt, *end; + Fixed xMin, xMax, yMin, yMax; + if (!gp || !rc) return GF_BAD_PARAM; + + if (!gp->n_points) { + rc->x = rc->y = rc->width = rc->height = 0; + return GF_OK; + } + pt = gp->points; + end = pt + gp->n_points; + xMin = xMax = pt->x; + yMin = yMax = pt->y; + pt++; + for ( ; pt < end; pt++ ) { + Fixed v; + v = pt->x; + if (v < xMin) xMin = v; + if (v > xMax) xMax = v; + v = pt->y; + if (v < yMin) yMin = v; + if (v > yMax) yMax = v; + } + rc->x = xMin; + rc->y = yMax; + rc->width = xMax - xMin; + rc->height = yMax - yMin; + +#if 0 + /*take care of straight line path by adding a default width if height and vice-versa*/ + if (rc->height && !rc->width) { + rc->width = 2*FIX_ONE; + rc->x -= FIX_ONE; + } + else if (!rc->height && rc->width) { + rc->height = 2*FIX_ONE; + rc->y += FIX_ONE; + } +#endif + return GF_OK; +} + +/* + * conic bbox computing taken from freetype + * Copyright 1996-2001, 2002, 2004 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * License: FTL or GPL + */ +static void gf_conic_check(Fixed y1, Fixed y2, Fixed y3, Fixed *min, Fixed *max) +{ + /* flat arc */ + if ((y1 <= y3) && (y2 == y1)) goto Suite; + + if ( y1 < y3 ) { + /* ascending arc */ + if ((y2 >= y1) && (y2 <= y3)) goto Suite; + } else { + /* descending arc */ + if ((y2 >= y3) && (y2 <= y1)) { + y2 = y1; + y1 = y3; + y3 = y2; + goto Suite; + } + } + y1 = y3 = y1 - gf_muldiv(y2 - y1, y2 - y1, y1 - 2*y2 + y3); + +Suite: + if ( y1 < *min ) *min = y1; + if ( y3 > *max ) *max = y3; +} + + +/* + * cubic bbox computing taken from freetype + * Copyright 1996-2001, 2002, 2004 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * License: FTL or GPL + + * Note: we're using the decomposition method, not the equation one which is not usable with Floats (only 16.16 fixed) +*/ +static void gf_cubic_check(Fixed p1, Fixed p2, Fixed p3, Fixed p4, Fixed *min, Fixed *max) +{ + Fixed stack[32*3 + 1], *arc; + arc = stack; + arc[0] = p1; + arc[1] = p2; + arc[2] = p3; + arc[3] = p4; + + do { + Fixed y1 = arc[0]; + Fixed y2 = arc[1]; + Fixed y3 = arc[2]; + Fixed y4 = arc[3]; + + if (ABS(y1)= y1) && (y2 <= y4) && (y3 >= y1) && (y3 <= y4)) goto Test; + } else { + /* descending */ + if ((y2 >= y4) && (y2 <= y1) && (y3 >= y4) && (y3 <= y1)) { + y2 = y1; + y1 = y4; + y4 = y2; + goto Test; + } + } + /* unknown direction -- split the arc in two */ + arc[6] = y4; + arc[1] = y1 = ( y1 + y2 ) / 2; + arc[5] = y4 = ( y4 + y3 ) / 2; + y2 = ( y2 + y3 ) / 2; + arc[2] = y1 = ( y1 + y2 ) / 2; + arc[4] = y4 = ( y4 + y2 ) / 2; + arc[3] = ( y1 + y4 ) / 2; + + arc += 3; + goto Suite; + +Test: + if ( y1 < *min ) *min = y1; + if ( y4 > *max ) *max = y4; + arc -= 3; +Suite: + ; + } + while ( arc >= stack ); +} + + +GF_EXPORT +GF_Err gf_path_get_bounds(GF_Path *gp, GF_Rect *rc) +{ + u32 i; + GF_Point2D *pt, *end, *ctrl1, *ctrl2; + Fixed xMin, xMax, yMin, yMax, cxMin, cxMax, cyMin, cyMax; + if (!gp || !rc) return GF_BAD_PARAM; + + if (!(gp->flags & GF_PATH_BBOX_DIRTY)) { + *rc = gp->bbox; + return GF_OK; + } + /*no curves in path*/ + if (gp->flags & GF_PATH_FLATTENED) { + GF_Err e; + gp->flags &= ~GF_PATH_BBOX_DIRTY; + e = gf_path_get_control_bounds(gp, &gp->bbox); + *rc = gp->bbox; + return GF_OK; + } + + gp->flags &= ~GF_PATH_BBOX_DIRTY; + + if (!gp->n_points) { + gp->bbox.x = gp->bbox.y = gp->bbox.width = gp->bbox.height = 0; + *rc = gp->bbox; + return GF_OK; + } + pt = gp->points; + end = pt + gp->n_points; + xMin = xMax = cxMin = cxMax = pt->x; + yMin = yMax = cyMin = cyMax = pt->y; + pt++; + for (i=1 ; i < gp->n_points; i++ ) { + Fixed x, y; + x = pt->x; + y = pt->y; + if (x < cxMin) cxMin = x; + if (x > cxMax) cxMax = x; + if (y < cyMin) cyMin = y; + if (y > cyMax) cyMax = y; + /*point on curve, update*/ + if (gp->tags[i] & GF_PATH_CURVE_ON) { + if (x < xMin) xMin = x; + if (x > xMax) xMax = x; + if (y < yMin) yMin = y; + if (y > yMax) yMax = y; + } + pt++; + } + + /*control box is bigger than box , decompose curves*/ + if ((cxMin < xMin) || (cxMax > xMax) || (cyMin < yMin) || (cyMax > yMax)) { + /*decompose all control points*/ + pt = gp->points; + for (i=1 ; i < gp->n_points; ) { + switch (gp->tags[i]) { + case GF_PATH_CURVE_ON: + case GF_PATH_CLOSE: + pt = &gp->points[i]; + i++; + break; + case GF_PATH_CURVE_CONIC: + /*decompose*/ + ctrl1 = &gp->points[i]; + end = &gp->points[i+1]; + if ((ctrl1->x < xMin) || (ctrl1->x > xMax)) + gf_conic_check(pt->x, ctrl1->x, end->x, &xMin, &xMax); + + if ((ctrl1->y < yMin) || (ctrl1->y > yMax)) + gf_conic_check(pt->y, ctrl1->y, end->y, &yMin, &yMax); + + /*and move*/ + pt = end; + i+=2; + break; + case GF_PATH_CURVE_CUBIC: + /*decompose*/ + ctrl1 = &gp->points[i]; + ctrl2 = &gp->points[i+1]; + end = &gp->points[i+2]; + if ((ctrl1->x < xMin) || (ctrl1->x > xMax) || (ctrl2->x < xMin) || (ctrl2->x > xMax)) + gf_cubic_check(pt->x, ctrl1->x, ctrl2->x, end->x, &xMin, &xMax); + + if ((ctrl1->y < yMin) || (ctrl1->y > yMax) || (ctrl2->y < yMin) || (ctrl2->y > yMax)) + gf_cubic_check(pt->y, ctrl1->y, ctrl2->y, end->y, &yMin, &yMax); + + /*and move*/ + pt = end; + i+=3; + break; + } + } + } + gp->bbox.x = xMin; + gp->bbox.y = yMax; + gp->bbox.width = xMax - xMin; + gp->bbox.height = yMax - yMin; + *rc = gp->bbox; + return GF_OK; +} + +/*flattening algo taken from libart but passed to sqrt tests for line distance to avoid 16.16 fixed overflow*/ +static GF_Err gf_subdivide_cubic(GF_Path *gp, Fixed x0, Fixed y0, Fixed x1, Fixed y1, Fixed x2, Fixed y2, Fixed x3, Fixed y3, Fixed fineness) +{ + GF_Point2D pt; + Fixed x3_0, y3_0, z3_0, z1_0, z1_dot, z2_dot, z1_perp, z2_perp; + Fixed max_perp; + Fixed x_m, y_m, xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2; + GF_Err e; + + pt.x = x3_0 = x3 - x0; + pt.y = y3_0 = y3 - y0; + + /*z3_0 is dist z0-z3*/ + z3_0 = gf_v2d_len(&pt); + + pt.x = x1 - x0; + pt.y = y1 - y0; + z1_0 = gf_v2d_len(&pt); + + if ((z3_0*100 < FIX_ONE) && (z1_0*100 < FIX_ONE)) + goto nosubdivide; + + /* perp is distance from line, multiplied by dist z0-z3 */ + max_perp = gf_mulfix(fineness, z3_0); + + z1_perp = gf_mulfix((y1 - y0), x3_0) - gf_mulfix((x1 - x0), y3_0); + if (ABS(z1_perp) > max_perp) + goto subdivide; + + z2_perp = gf_mulfix((y3 - y2), x3_0) - gf_mulfix((x3 - x2), y3_0); + if (ABS(z2_perp) > max_perp) + goto subdivide; + + z1_dot = gf_mulfix((x1 - x0), x3_0) + gf_mulfix((y1 - y0), y3_0); + if ((z1_dot < 0) && (ABS(z1_dot) > max_perp)) + goto subdivide; + + z2_dot = gf_mulfix((x3 - x2), x3_0) + gf_mulfix((y3 - y2), y3_0); + if ((z2_dot < 0) && (ABS(z2_dot) > max_perp)) + goto subdivide; + + if (gf_divfix(z1_dot + z1_dot, z3_0) > z3_0) + goto subdivide; + + if (gf_divfix(z2_dot + z2_dot, z3_0) > z3_0) + goto subdivide; + +nosubdivide: + /* don't subdivide */ + return gf_path_add_line_to(gp, x3, y3); + +subdivide: + xa1 = (x0 + x1) / 2; + ya1 = (y0 + y1) / 2; + xa2 = (x0 + 2 * x1 + x2) / 4; + ya2 = (y0 + 2 * y1 + y2) / 4; + xb1 = (x1 + 2 * x2 + x3) / 4; + yb1 = (y1 + 2 * y2 + y3) / 4; + xb2 = (x2 + x3) / 2; + yb2 = (y2 + y3) / 2; + x_m = (xa2 + xb1) / 2; + y_m = (ya2 + yb1) / 2; + /*safeguard for numerical stability*/ + if ( (ABS(x_m-x0) < FIX_EPSILON) && (ABS(y_m-y0) < FIX_EPSILON)) + return gf_path_add_line_to(gp, x3, y3); + if ( (ABS(x3-x_m) < FIX_EPSILON) && (ABS(y3-y_m) < FIX_EPSILON)) + return gf_path_add_line_to(gp, x3, y3); + + e = gf_subdivide_cubic(gp, x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, fineness); + if (e) return e; + return gf_subdivide_cubic(gp, x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, fineness); +} + +GF_EXPORT +GF_Path *gf_path_get_flatten(GF_Path *gp) +{ + GF_Path *ngp; + Fixed fineness; + u32 i, *countour; + GF_Point2D *pt; + if (!gp || !gp->n_points) return NULL; + + if (gp->flags & GF_PATH_FLATTENED) return gf_path_clone(gp); + + /*avoid too high precision */ + fineness = MAX(FIX_ONE - gp->fineness, FIX_ONE / 100); + + ngp = gf_path_new(); + pt = &gp->points[0]; + gf_path_add_move_to_vec(ngp, pt); + countour = gp->contours; + for (i=1; in_points; ) { + switch (gp->tags[i]) { + case GF_PATH_CURVE_ON: + case GF_PATH_CLOSE: + pt = &gp->points[i]; + if (*countour == i-1) { + gf_path_add_move_to_vec(ngp, pt); + countour++; + } else { + gf_path_add_line_to_vec(ngp, pt); + } + if (gp->tags[i]==GF_PATH_CLOSE) gf_path_close(ngp); + i++; + break; + case GF_PATH_CURVE_CONIC: + { + GF_Point2D *ctl, *end, c1, c2; + ctl = &gp->points[i]; + end = &gp->points[i+1]; + c1.x = pt->x + 2*(ctl->x - pt->x)/3; + c1.y = pt->y + 2*(ctl->y - pt->y)/3; + c2.x = c1.x + (end->x - pt->x) / 3; + c2.y = c1.y + (end->y - pt->y) / 3; + + gf_subdivide_cubic(ngp, pt->x, pt->y, c1.x, c1.y, c2.x, c2.y, end->x, end->y, fineness); + pt = end; + if (gp->tags[i+1]==GF_PATH_CLOSE) gf_path_close(ngp); + i+=2; + } + break; + case GF_PATH_CURVE_CUBIC: + gf_subdivide_cubic(ngp, pt->x, pt->y, gp->points[i].x, gp->points[i].y, gp->points[i+1].x, gp->points[i+1].y, gp->points[i+2].x, gp->points[i+2].y, fineness); + pt = &gp->points[i+2]; + if (gp->tags[i+2]==GF_PATH_CLOSE) gf_path_close(ngp); + i+=3; + break; + } + } + if (gp->flags & GF_PATH_FILL_ZERO_NONZERO) ngp->flags |= GF_PATH_FILL_ZERO_NONZERO; + ngp->flags |= (GF_PATH_BBOX_DIRTY | GF_PATH_FLATTENED); + return ngp; +} + +GF_EXPORT +void gf_path_flatten(GF_Path *gp) +{ + GF_Path *res; + if (gp->flags & GF_PATH_FLATTENED) return; + if (!gp->n_points) return; + res = gf_path_get_flatten(gp); + if (gp->contours) free(gp->contours); + if (gp->points) free(gp->points); + if (gp->tags) free(gp->tags); + memcpy(gp, res, sizeof(GF_Path)); + free(res); +} + + + +#define isLeft(P0, P1, P2) \ + ( gf_mulfix((P1.x - P0.x), (P2.y - P0.y)) - gf_mulfix((P2.x - P0.x), (P1.y - P0.y)) ) + + +static void gf_subdivide_cubic_hit_test(Fixed h_x, Fixed h_y, Fixed x0, Fixed y0, Fixed x1, Fixed y1, Fixed x2, Fixed y2, Fixed x3, Fixed y3, s32 *wn) +{ + GF_Point2D s, e, pt; + Fixed x_m, y_m, xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2, y_min, y_max; + + /*if hit line doesn't intersects control box abort*/ + y_min = MIN(y0, MIN(y1, MIN(y2, y3))); + y_max = MAX(y0, MAX(y1, MAX(y2, y3))); + if ((h_yy_max) ) return; + + /*if vert diff between end points larger than 1 pixels, subdivide (we need pixel accuracy for is_over)*/ + if (y_max - y_min > FIX_ONE) { + xa1 = (x0 + x1) / 2; + ya1 = (y0 + y1) / 2; + xa2 = (x0 + 2 * x1 + x2) / 4; + ya2 = (y0 + 2 * y1 + y2) / 4; + xb1 = (x1 + 2 * x2 + x3) / 4; + yb1 = (y1 + 2 * y2 + y3) / 4; + xb2 = (x2 + x3) / 2; + yb2 = (y2 + y3) / 2; + x_m = (xa2 + xb1) / 2; + y_m = (ya2 + yb1) / 2; + + gf_subdivide_cubic_hit_test(h_x, h_y, x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, wn); + gf_subdivide_cubic_hit_test(h_x, h_y, x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, wn); + return; + } + + s.x = x0; + s.y = y0; + e.x = x3; + e.y = y3; + pt.x = h_x; + pt.y= h_y; + + if (s.y<=pt.y) { + if (e.y>pt.y) { + if (isLeft(s, e, pt) > 0) + (*wn)++; + } + } + else if (e.y<=pt.y) { + if (isLeft(s, e, pt) < 0) + (*wn)--; + } +} + +GF_EXPORT +Bool gf_path_point_over(GF_Path *gp, Fixed x, Fixed y) +{ + u32 i, *contour, start_idx; + s32 wn; + GF_Point2D start, s, e, pt; + GF_Rect rc; + + /*check if not in bounds*/ + gf_path_get_bounds(gp, &rc); + if ((xrc.y) || (x>rc.x+rc.width) || (yn_points<2)) return 0; + + pt.x = x; + pt.y = y; + wn = 0; + s = start = gp->points[0]; + start_idx = 0; + contour = gp->contours; + for (i=1; in_points; ) { + switch (gp->tags[i]) { + case GF_PATH_CURVE_ON: + case GF_PATH_CLOSE: + e = gp->points[i]; + if (s.y<=pt.y) { + if (e.y>pt.y) { + if (isLeft(s, e, pt) > 0) wn++; + } + } + else if (e.y<=pt.y) { + if (isLeft(s, e, pt) < 0) wn--; + } + s = e; + i++; + break; + case GF_PATH_CURVE_CONIC: + { + GF_Point2D *ctl, *end, c1, c2; + ctl = &gp->points[i]; + end = &gp->points[i+1]; + c1.x = s.x + 2*(ctl->x - s.x) / 3; + c1.y = s.y + 2*(ctl->y - s.y) / 3; + c2.x = c1.x + (end->x - s.x) / 3; + c2.y = c1.y + (end->y - s.y) / 3; + gf_subdivide_cubic_hit_test(x, y, s.x, s.y, c1.x, c1.y, c2.x, c2.y, end->x, end->y, &wn); + s = *end; + } + i+=2; + break; + case GF_PATH_CURVE_CUBIC: + gf_subdivide_cubic_hit_test(x, y, s.x, s.y, gp->points[i].x, gp->points[i].y, gp->points[i+1].x, gp->points[i+1].y, gp->points[i+2].x, gp->points[i+2].y, &wn); + s = gp->points[i+2]; + i+=3; + break; + } + /*end of subpath*/ + if (*contour==i-1) { + /*close path if needed, but don't test for lines...*/ + if ((i-start_idx > 2) && (pt.ypt.y) { + if (isLeft(s, e, pt) > 0) wn++; + } + } + else if (e.y<=pt.y) { + if (isLeft(s, e, pt) < 0) wn--; + } + } + } + s = start = gp->points[i]; + i++; + } + } + if (gp->flags & GF_PATH_FILL_ZERO_NONZERO) return wn ? 1 : 0; + return wn%2 ? 1 : 0; +} + +GF_EXPORT +Bool gf_path_is_empty(GF_Path *gp) +{ + if (gp && gp->contours) return 0; + return 1; +} + +/*iteration info*/ +typedef struct +{ + Fixed len; + Fixed dx, dy; + Fixed start_x, start_y; +} IterInfo; + +struct _path_iterator +{ + u32 num_seg; + IterInfo *seg; + Fixed length; +}; + +GF_EXPORT +GF_PathIterator *gf_path_iterator_new(GF_Path *gp) +{ + GF_Path *flat; + GF_PathIterator *it; + u32 i, j, cur; + GF_Point2D start, end; + + GF_SAFEALLOC(it, GF_PathIterator); + if (!it) return NULL; + flat = gf_path_get_flatten(gp); + if (!flat) { + free(it); + return NULL; + } + it->seg = (IterInfo *) malloc(sizeof(IterInfo) * flat->n_points); + it->num_seg = 0; + it->length = 0; + cur = 0; + for (i=0; in_contours; i++) { + Fixed dx, dy; + u32 nb_pts = 1+flat->contours[i]-cur; + start = flat->points[cur]; + for (j=1; jpoints[cur+j]; + it->seg[it->num_seg].start_x = start.x; + it->seg[it->num_seg].start_y = start.y; + dx = it->seg[it->num_seg].dx = end.x - start.x; + dy = it->seg[it->num_seg].dy = end.y - start.y; + it->seg[it->num_seg].len = gf_sqrt(gf_mulfix(dx, dx) + gf_mulfix(dy, dy)); + it->length += it->seg[it->num_seg].len; + start = end; + it->num_seg++; + } + cur += nb_pts; + } + gf_path_del(flat); + return it; +} + +GF_EXPORT +Fixed gf_path_iterator_get_length(GF_PathIterator *it) +{ + return it ? it->length : 0; +} + +GF_EXPORT +Bool gf_path_iterator_get_transform(GF_PathIterator *path, Fixed offset, Bool follow_tangent, GF_Matrix2D *mat, Bool smooth_edges, Fixed length_after_point) +{ + GF_Matrix2D final, rot; + Bool tang = 0; + Fixed res, angle, angleNext; + u32 i; + Fixed curLen = 0; + if (!path) return 0; + + for (i=0; inum_seg; i++) { + if (curLen + path->seg[i].len >= offset) goto found; + curLen += path->seg[i].len; + } + if (!follow_tangent) return 0; + tang = 1; + i--; + +found: + gf_mx2d_init(final); + + res = gf_divfix(offset - curLen, path->seg[i].len); + if (tang) res += 1; + + /*move to current point*/ + gf_mx2d_add_translation(&final, path->seg[i].start_x + gf_mulfix(path->seg[i].dx, res), path->seg[i].start_y + gf_mulfix(path->seg[i].dy, res)); + + if (!path->seg[i].dx) { + angle = GF_PI2; + } else { + angle = gf_acos( gf_divfix(path->seg[i].dx , path->seg[i].len) ); + } + if (path->seg[i].dy<0) angle *= -1; + + if (smooth_edges) { + if (offset + length_after_point > curLen + path->seg[i].len) { + Fixed ratio = gf_divfix(curLen + path->seg[i].len-offset, length_after_point); + if (i < path->num_seg - 1) { + if (!path->seg[i+1].dx) { + angleNext = GF_PI2; + } else { + angleNext = gf_acos( gf_divfix(path->seg[i+1].dx, path->seg[i+1].len) ); + } + if (path->seg[i+1].dy<0) angleNext *= -1; + + if (angle<0 && angleNext>0) { + angle = gf_mulfix(FIX_ONE-ratio, angleNext) - gf_mulfix(ratio, angle); + } else { + angle = gf_mulfix(ratio, angle) + gf_mulfix(FIX_ONE-ratio, angleNext); + } + } + } + } + /*handle res=0 case for rotation (point on line join)*/ + else if (res==1) { + if (i < path->num_seg - 1) { + if (!path->seg[i+1].dx) { + angleNext = GF_PI2; + } else { + angleNext = gf_acos( gf_divfix(path->seg[i+1].dx, path->seg[i+1].len) ); + } + if (path->seg[i+1].dy<0) angleNext *= -1; + angle = ( angle + angleNext) / 2; + } + } + + gf_mx2d_init(rot); + gf_mx2d_add_rotation(&rot, 0, 0, angle); + gf_mx2d_add_matrix(mat, &rot); + gf_mx2d_add_matrix(mat, &final); + return 1; +} + +GF_EXPORT +void gf_path_iterator_del(GF_PathIterator *it) +{ + if (it->seg) free(it->seg); + free(it); +} + + +#define ConvexCompare(delta) \ + ( (delta.x > 0) ? -1 : \ + (delta.x < 0) ? 1 : \ + (delta.y > 0) ? -1 : \ + (delta.y < 0) ? 1 : \ + 0 ) + +#define ConvexGetPointDelta(delta, pprev, pcur ) \ + /* Given a previous point 'pprev', read a new point into 'pcur' */ \ + /* and return delta in 'delta'. */ \ + pcur = pts[iread++]; \ + delta.x = pcur.x - pprev.x; \ + delta.y = pcur.y - pprev.y; \ + +#define ConvexCross(p, q) gf_mulfix(p.x,q.y) - gf_mulfix(p.y,q.x); + +#define ConvexCheckTriple \ + if ( (thisDir = ConvexCompare(dcur)) == -curDir ) { \ + ++dirChanges; \ + /* if ( dirChanges > 2 ) return NotConvex; */ \ + } \ + curDir = thisDir; \ + cross = ConvexCross(dprev, dcur); \ + if ( cross > 0 ) { \ + if ( angleSign == -1 ) return GF_POLYGON_COMPLEX_CW; \ + angleSign = 1; \ + } \ + else if (cross < 0) { \ + if (angleSign == 1) return GF_POLYGON_COMPLEX_CCW; \ + angleSign = -1; \ + } \ + pSecond = pThird; \ + dprev.x = dcur.x; \ + dprev.y = dcur.y; \ + +GF_EXPORT +u32 gf_polygone2d_get_convexity(GF_Point2D *pts, u32 len) +{ + s32 curDir, thisDir = 0, dirChanges = 0, angleSign = 0; + u32 iread; + Fixed cross; + GF_Point2D pSecond, pThird, pSaveSecond; + GF_Point2D dprev, dcur; + + /* Get different point, return if less than 3 diff points. */ + if (len < 3 ) return GF_POLYGON_CONVEX_LINE; + iread = 1; + ConvexGetPointDelta(dprev, (pts[0]), pSecond); + pSaveSecond = pSecond; + /*initial direction */ + curDir = ConvexCompare(dprev); + while ( iread < len) { + /* Get different point, break if no more points */ + ConvexGetPointDelta(dcur, pSecond, pThird ); + if ( (dcur.x == 0) && (dcur.y == 0) ) continue; + /* Check current three points */ + ConvexCheckTriple; + } + + /* Must check for direction changes from last vertex back to first */ + /* Prepare for 'ConvexCheckTriple' */ + pThird = pts[0]; + dcur.x = pThird.x - pSecond.x; + dcur.y = pThird.y - pSecond.y; + if ( ConvexCompare(dcur) ) ConvexCheckTriple; + + /* and check for direction changes back to second vertex */ + dcur.x = pSaveSecond.x - pSecond.x; + dcur.y = pSaveSecond.y - pSecond.y; + /* Don't care about 'pThird' now */ + ConvexCheckTriple; + + /* Decide on polygon type given accumulated status */ + if ( dirChanges > 2 ) return GF_POLYGON_COMPLEX; + if ( angleSign > 0 ) return GF_POLYGON_CONVEX_CCW; + if ( angleSign < 0 ) return GF_POLYGON_CONVEX_CW; + return GF_POLYGON_CONVEX_LINE; +} diff --git a/src/utils/path2d_stroker.c b/src/utils/path2d_stroker.c new file mode 100644 index 0000000..91bbde8 --- /dev/null +++ b/src/utils/path2d_stroker.c @@ -0,0 +1,1784 @@ +/***************************************************************************/ +/* */ +/* ftstroke.c */ +/* */ +/* FreeType path stroker (body). */ +/* */ +/* Copyright 2002, 2003, 2004 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** BEZIER COMPUTATIONS *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + +#define FT_SMALL_CONIC_THRESHOLD ( GF_PI / 6 ) +#define FT_SMALL_CUBIC_THRESHOLD ( GF_PI / 6 ) + +#define FT_IS_SMALL( x ) ( (x) > -FIX_EPSILON && (x) < FIX_EPSILON ) + +static void ft_conic_split(GF_Point2D* base ) +{ + Fixed a, b; + + base[4].x = base[2].x; + b = base[1].x; + a = base[3].x = ( base[2].x + b ) / 2; + b = base[1].x = ( base[0].x + b ) / 2; + base[2].x = ( a + b ) / 2; + + base[4].y = base[2].y; + b = base[1].y; + a = base[3].y = ( base[2].y + b ) / 2; + b = base[1].y = ( base[0].y + b ) / 2; + base[2].y = ( a + b ) / 2; +} + + + +static Bool ft_conic_is_small_enough( GF_Point2D* base, Fixed *angle_in, Fixed *angle_out) +{ + GF_Point2D d1, d2; + Fixed theta; + s32 close1, close2; + d1.x = base[1].x - base[2].x; + d1.y = base[1].y - base[2].y; + d2.x = base[0].x - base[1].x; + d2.y = base[0].y - base[1].y; + close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); + close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); + + if ( close1 ) { + if ( close2 ) + *angle_in = *angle_out = 0; + else + *angle_in = *angle_out = gf_atan2(d2.y, d2.x); + } + else if ( close2 ) { + *angle_in = *angle_out = gf_atan2(d1.y, d1.x); + } else { + *angle_in = gf_atan2(d1.y, d1.x); + *angle_out = gf_atan2(d2.y, d2.x); + } + theta = ABS( gf_angle_diff(*angle_in, *angle_out)); + return ( theta < FT_SMALL_CONIC_THRESHOLD ) ? 1 : 0; +} + +static void ft_cubic_split( GF_Point2D* base ) +{ + Fixed a, b, c, d; + base[6].x = base[3].x; + c = base[1].x; + d = base[2].x; + base[1].x = a = ( base[0].x + c ) / 2; + base[5].x = b = ( base[3].x + d ) / 2; + c = ( c + d ) / 2; + base[2].x = a = ( a + c ) / 2; + base[4].x = b = ( b + c ) / 2; + base[3].x = ( a + b ) / 2; + + base[6].y = base[3].y; + c = base[1].y; + d = base[2].y; + base[1].y = a = ( base[0].y + c ) / 2; + base[5].y = b = ( base[3].y + d ) / 2; + c = ( c + d ) / 2; + base[2].y = a = ( a + c ) / 2; + base[4].y = b = ( b + c ) / 2; + base[3].y = ( a + b ) / 2; +} + + +static Bool ft_cubic_is_small_enough(GF_Point2D *base, Fixed *angle_in, Fixed *angle_mid, Fixed *angle_out) +{ + GF_Point2D d1, d2, d3; + Fixed theta1, theta2; + s32 close1, close2, close3; + d1.x = base[2].x - base[3].x; + d1.y = base[2].y - base[3].y; + d2.x = base[1].x - base[2].x; + d2.y = base[1].y - base[2].y; + d3.x = base[0].x - base[1].x; + d3.y = base[0].y - base[1].y; + + close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); + close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); + close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y ); + + if ( close1 || close3 ) { + if ( close2 ) { + /* basically a point */ + *angle_in = *angle_out = *angle_mid = 0; + } else if ( close1 ) { + *angle_in = *angle_mid = gf_atan2( d2.y, d2.x); + *angle_out = gf_atan2( d3.y, d3.x); + } + /* close2 */ + else { + *angle_in = gf_atan2(d1.y, d1.x); + *angle_mid = *angle_out = gf_atan2(d2.y, d2.x); + } + } + else if ( close2 ) { + *angle_in = *angle_mid = gf_atan2(d1.y, d1.x); + *angle_out = gf_atan2(d3.y, d3.x); + } else { + *angle_in = gf_atan2(d1.y, d1.x); + *angle_mid = gf_atan2(d2.y, d2.x); + *angle_out = gf_atan2(d3.y, d3.x); + } + theta1 = ABS( gf_angle_diff( *angle_in, *angle_mid ) ); + theta2 = ABS( gf_angle_diff( *angle_mid, *angle_out ) ); + return ((theta1 < FT_SMALL_CUBIC_THRESHOLD) && (theta2 < FT_SMALL_CUBIC_THRESHOLD )) ? 1 : 0; +} + + +/***************************************************************************/ +/***************************************************************************/ +/***** *****/ +/***** STROKE BORDERS *****/ +/***** *****/ +/***************************************************************************/ +/***************************************************************************/ + +typedef enum +{ + FT_STROKE_TAG_ON = 1, /* on-curve point */ + FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */ + FT_STROKE_TAG_BEGIN = 4, /* sub-path start */ + FT_STROKE_TAG_END = 8 /* sub-path end */ +} FT_StrokeTags; + + +typedef struct FT_StrokeBorderRec_ +{ + u32 num_points; + u32 max_points; + GF_Point2D* points; + u8 *tags; + Bool movable; + /* index of current sub-path start point */ + s32 start; + Bool valid; +} FT_StrokeBorderRec, *FT_StrokeBorder; + + +static s32 ft_stroke_border_grow(FT_StrokeBorder border, u32 new_points) +{ + u32 new_max = border->num_points + new_points; + if (new_max > border->max_points) { + u32 cur_max = new_max*2; + border->points = (GF_Point2D *) realloc(border->points, sizeof(GF_Point2D)*cur_max); + border->tags = (u8 *) realloc(border->tags, sizeof(u8)*cur_max); + if (!border->points || !border->tags) return -1; + border->max_points = cur_max; + } + return 0; +} + +static void ft_stroke_border_close( FT_StrokeBorder border ) +{ + /* don't record empty paths! */ + if ((border->start <0) || !border->num_points ) return; + if ( border->num_points > (u32)border->start ) { + border->tags[border->start] |= FT_STROKE_TAG_BEGIN; + border->tags[border->num_points - 1] |= FT_STROKE_TAG_END; + } + border->start = -1; + border->movable = 0; +} + +static s32 ft_stroke_border_lineto( FT_StrokeBorder border, GF_Point2D* to, Bool movable ) +{ + assert(border->start >= 0); + + if ( border->movable ) { + /* move last point */ + border->points[border->num_points - 1] = *to; + } else { + /* add one point */ + if (ft_stroke_border_grow( border, 1 )==0) { + GF_Point2D* vec = border->points + border->num_points; + u8 *tag = border->tags + border->num_points; + + vec[0] = *to; + tag[0] = FT_STROKE_TAG_ON; + border->num_points += 1; + } else { + return -1; + } + } + border->movable = movable; + return 0; +} + + +static s32 ft_stroke_border_conicto( FT_StrokeBorder border, GF_Point2D* control, GF_Point2D* to ) +{ + assert( border->start >= 0 ); + if (ft_stroke_border_grow( border, 2 )==0) { + GF_Point2D* vec = border->points + border->num_points; + u8 *tag = border->tags + border->num_points; + + vec[0] = *control; + vec[1] = *to; + + tag[0] = 0; + tag[1] = FT_STROKE_TAG_ON; + + border->num_points += 2; + } else { + return -1; + } + border->movable = 0; + return 0; +} + +static s32 ft_stroke_border_cubicto( FT_StrokeBorder border, + GF_Point2D* control1, + GF_Point2D* control2, + GF_Point2D* to ) +{ + assert( border->start >= 0 ); + + if (!ft_stroke_border_grow( border, 3 )) { + GF_Point2D* vec = border->points + border->num_points; + u8* tag = border->tags + border->num_points; + vec[0] = *control1; + vec[1] = *control2; + vec[2] = *to; + + tag[0] = FT_STROKE_TAG_CUBIC; + tag[1] = FT_STROKE_TAG_CUBIC; + tag[2] = FT_STROKE_TAG_ON; + + border->num_points += 3; + } else { + return -1; + } + border->movable = 0; + return 0; +} + +#define FT_ARC_CUBIC_ANGLE ( GF_PI / 2 ) + + +static s32 ft_stroke_border_arcto( FT_StrokeBorder border, + GF_Point2D* center, + Fixed radius, + Fixed angle_start, + Fixed angle_diff ) +{ + Fixed total, angle, step, rotate, next, theta; + GF_Point2D a, b, a2, b2; + Fixed length; + /* compute start point */ + a = gf_v2d_from_polar(radius, angle_start ); + a.x += center->x; + a.y += center->y; + + total = angle_diff; + angle = angle_start; + rotate = ( angle_diff >= 0 ) ? GF_PI2 : -GF_PI2; + + while ( total != 0 ) { + step = total; + if ( step > FT_ARC_CUBIC_ANGLE ) + step = FT_ARC_CUBIC_ANGLE; + else if ( step < -FT_ARC_CUBIC_ANGLE ) + step = -FT_ARC_CUBIC_ANGLE; + + next = angle + step; + theta = step; + if ( theta < 0 ) + theta = -theta; + +#ifdef GPAC_FIXED_POINT + theta >>= 1; +#else + theta /= 2; +#endif + + /* compute end point */ + b = gf_v2d_from_polar(radius, next ); + b.x += center->x; + b.y += center->y; + + /* compute first and second control points */ + length = gf_muldiv( radius, gf_sin( theta ) * 4, ( FIX_ONE + gf_cos( theta ) ) * 3 ); + + a2 = gf_v2d_from_polar(length, angle + rotate ); + a2.x += a.x; + a2.y += a.y; + + b2 = gf_v2d_from_polar(length, next - rotate ); + b2.x += b.x; + b2.y += b.y; + + /* add cubic arc */ + if (ft_stroke_border_cubicto( border, &a2, &b2, &b ) != 0) return -1; + + /* process the rest of the arc ?? */ + a = b; + total -= step; + angle = next; + } + return 0; +} + + +static s32 ft_stroke_border_moveto(FT_StrokeBorder border, GF_Point2D* to ) +{ + /* close current open path if any ? */ + if ( border->start >= 0 ) + ft_stroke_border_close( border ); + + border->start = border->num_points; + border->movable = 0; + return ft_stroke_border_lineto( border, to, 0 ); +} + + +static s32 ft_stroke_border_get_counts(FT_StrokeBorder border, + u32 *anum_points, + u32 *anum_contours ) +{ + s32 error = 0; + u32 num_points = 0; + u32 num_contours = 0; + u32 count = border->num_points; + GF_Point2D *point = border->points; + u8 *tags = border->tags; + s32 in_contour = 0; + + for ( ; count > 0; count--, num_points++, point++, tags++ ) { + if ( tags[0] & FT_STROKE_TAG_BEGIN ) { + if ( in_contour != 0 ) goto Fail; + + in_contour = 1; + } else if ( in_contour == 0 ) + goto Fail; + + if ( tags[0] & FT_STROKE_TAG_END ) { + if ( in_contour == 0 ) + goto Fail; + in_contour = 0; + num_contours++; + } + } + + if ( in_contour != 0 ) + goto Fail; + + border->valid = 1; + +Exit: + *anum_points = num_points; + *anum_contours = num_contours; + return error; + +Fail: + num_points = 0; + num_contours = 0; + error = -1; + goto Exit; +} + + +static void ft_stroke_border_export( FT_StrokeBorder border, GF_Path* outline ) +{ + if (!border->num_points) return; + + /* copy point locations */ + memcpy(outline->points + outline->n_points, border->points, sizeof(GF_Point2D)*border->num_points); + + /* copy tags */ + { + u32 count = border->num_points; + u8* read = border->tags; + u8* write = (u8*)outline->tags + outline->n_points; + + for ( ; count > 0; count--, read++, write++ ) { + if ( *read & FT_STROKE_TAG_ON ) + *write = GF_PATH_CURVE_ON; + else if ( *read & FT_STROKE_TAG_CUBIC ) + *write = GF_PATH_CURVE_CUBIC; + else + *write = GF_PATH_CURVE_CONIC; + } + } + + /* copy contours */ + { + u32 count = border->num_points; + u8 *tags = border->tags; + u32 *write = outline->contours + outline->n_contours; + u32 idx = outline->n_points; + + for ( ; count > 0; count--, tags++, idx++ ) { + if ( *tags & FT_STROKE_TAG_END ) { + *write++ = idx; + outline->n_contours++; + } + } + } + outline->n_points = outline->n_points + border->num_points; +} + + + /***************************************************************************/ + /***************************************************************************/ + /***** *****/ + /***** STROKER *****/ + /***** *****/ + /***************************************************************************/ + /***************************************************************************/ + +#define FT_SIDE_TO_ROTATE( s ) ( GF_PI2 - (s) * GF_PI ) + +typedef struct FT_StrokerRec_ +{ + Fixed angle_in; + Fixed angle_out; + GF_Point2D center; + Bool first_point; + Fixed subpath_angle; + GF_Point2D subpath_start; + + u32 line_cap; + u32 line_join; + Fixed miter_limit; + Fixed radius; + Bool valid; + Bool closing; + FT_StrokeBorderRec borders[2]; +} FT_StrokerRec, FT_Stroker; + + + /* creates a circular arc at a corner or cap */ +static s32 ft_stroker_arcto( FT_Stroker *stroker, s32 side ) +{ + Fixed total, rotate; + Fixed radius = stroker->radius; + s32 error = 0; + FT_StrokeBorder border = stroker->borders + side; + rotate = FT_SIDE_TO_ROTATE( side ); + total = gf_angle_diff( stroker->angle_in, stroker->angle_out); + if ( total == GF_PI ) total = -rotate * 2; + error = ft_stroke_border_arcto( border, + &stroker->center, + radius, + stroker->angle_in + rotate, + total ); + border->movable = 0; + return error; +} + + /* adds a cap at the end of an opened path */ +static s32 ft_stroker_cap(FT_Stroker *stroker, Fixed angle, s32 side) +{ + s32 error = 0; + if ( stroker->line_cap == GF_LINE_CAP_ROUND) { + /* OK we cheat a bit here compared to FT original code, and use a rough cubic cap instead of + a circle to deal with arbitrary orientation of regular paths where arc cap is not always properly oriented. + Rather than computing orientation we simply approximate to conic - btw this takes less memory than + exact circle cap since cubics are natively supported - we don't use conic since result is not so good looking*/ + GF_Point2D delta, delta2, ctl1, ctl2, end; + Fixed rotate = FT_SIDE_TO_ROTATE( side ); + Fixed radius = stroker->radius; + FT_StrokeBorder border = stroker->borders + side; + + + delta = gf_v2d_from_polar(radius, angle); + delta.x = 4*delta.x/3; + delta.y = 4*delta.y/3; + + delta2 = gf_v2d_from_polar(radius, angle + rotate); + ctl1.x = delta.x + stroker->center.x + delta2.x; + ctl1.y = delta.y + stroker->center.y + delta2.y; + + delta2 = gf_v2d_from_polar(radius, angle - rotate); + ctl2.x = delta.x + delta2.x + stroker->center.x; + ctl2.y = delta.y + delta2.y + stroker->center.y; + + end.x = delta2.x + stroker->center.x; + end.y = delta2.y + stroker->center.y; + + error = ft_stroke_border_cubicto( border, &ctl1, &ctl2, &end); + } else if ( stroker->line_cap == GF_LINE_CAP_SQUARE) { + /* add a square cap */ + GF_Point2D delta, delta2; + Fixed rotate = FT_SIDE_TO_ROTATE( side ); + Fixed radius = stroker->radius; + FT_StrokeBorder border = stroker->borders + side; + + + delta2 = gf_v2d_from_polar(radius, angle + rotate); + delta = gf_v2d_from_polar(radius, angle); + + delta.x += stroker->center.x + delta2.x; + delta.y += stroker->center.y + delta2.y; + + error = ft_stroke_border_lineto( border, &delta, 0 ); + if ( error ) + goto Exit; + + delta2 = gf_v2d_from_polar(radius, angle - rotate); + delta = gf_v2d_from_polar(radius, angle); + + delta.x += delta2.x + stroker->center.x; + delta.y += delta2.y + stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, 0 ); + } else if ( stroker->line_cap == GF_LINE_CAP_TRIANGLE) { + /* add a triangle cap */ + GF_Point2D delta; + Fixed radius = stroker->radius; + FT_StrokeBorder border = stroker->borders + side; + border->movable = 0; + delta = gf_v2d_from_polar(radius, angle); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + error = ft_stroke_border_lineto(border, &delta, 0); + } + +Exit: + return error; +} + + +/* process an inside corner, i.e. compute intersection */ +static s32 ft_stroker_inside(FT_Stroker *stroker, s32 side) +{ + FT_StrokeBorder border = stroker->borders + side; + Fixed phi, theta, rotate; + Fixed length, thcos, sigma; + GF_Point2D delta; + s32 error = 0; + + rotate = FT_SIDE_TO_ROTATE( side ); + + /* compute median angle */ + theta = gf_angle_diff( stroker->angle_in, stroker->angle_out ); + if ( theta == GF_PI ) + theta = rotate; + else + theta = theta / 2; + + phi = stroker->angle_in + theta; + + thcos = gf_cos( theta ); + sigma = gf_mulfix( stroker->miter_limit, thcos ); + + if ( sigma < FIX_ONE ) { + delta = gf_v2d_from_polar(stroker->radius, stroker->angle_out + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + if (!stroker->closing) border->movable = 0; + } else { + length = gf_divfix( stroker->radius, thcos ); + delta = gf_v2d_from_polar(length, phi + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + } + error = ft_stroke_border_lineto( border, &delta, 0 ); + return error; +} + + + /* process an outside corner, i.e. compute bevel/miter/round */ +static s32 ft_stroker_outside( FT_Stroker *stroker, s32 side ) +{ + FT_StrokeBorder border = stroker->borders + side; + s32 error; + Fixed rotate; + u32 join = stroker->line_join; + + if ( join == GF_LINE_JOIN_MITER_SVG ) { + Fixed sin_theta, inv_sin_theta; + join = GF_LINE_JOIN_MITER; + sin_theta = gf_sin(gf_angle_diff( stroker->angle_out - GF_PI, stroker->angle_in) / 2 ); + if (sin_theta) { + inv_sin_theta = gf_invfix(sin_theta); + if (inv_sin_theta > stroker->miter_limit) join = GF_LINE_JOIN_BEVEL; + } else { + join = GF_LINE_JOIN_BEVEL; + } + } + + if ( join == GF_LINE_JOIN_ROUND ) { + error = ft_stroker_arcto( stroker, side ); + } else if (join == GF_LINE_JOIN_BEVEL) { + GF_Point2D delta; + rotate = FT_SIDE_TO_ROTATE( side ); + delta = gf_v2d_from_polar(stroker->radius, stroker->angle_out + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + /*prevent moving current point*/ + border->movable = 0; + /*and add un-movable end point*/ + error = ft_stroke_border_lineto( border, &delta, 0); + } else { + /* this is a mitered or beveled corner */ + Fixed sigma, radius = stroker->radius; + Fixed theta, phi; + Fixed thcos; + Bool miter = 1; + + rotate = FT_SIDE_TO_ROTATE( side ); + + theta = gf_angle_diff( stroker->angle_in, stroker->angle_out ); + if ( theta == GF_PI ) { + theta = rotate; + phi = stroker->angle_in; + } else { + theta = theta / 2; + phi = stroker->angle_in + theta + rotate; + } + + thcos = gf_cos( theta ); + sigma = gf_mulfix( stroker->miter_limit, thcos ); + + if ( sigma >= FIX_ONE ) { + miter = 0; + } + + /* this is a miter (broken angle) */ + if ( miter ) { + GF_Point2D middle, delta; + Fixed length; + + /* compute middle point */ + middle = gf_v2d_from_polar(gf_mulfix(radius, stroker->miter_limit), phi); + middle.x += stroker->center.x; + middle.y += stroker->center.y; + + /* compute first angle point */ + length = gf_mulfix(radius, gf_divfix( FIX_ONE - sigma, ABS( gf_sin( theta ) ) ) ); + + delta = gf_v2d_from_polar(length, phi + rotate ); + delta.x += middle.x; + delta.y += middle.y; + + error = ft_stroke_border_lineto( border, &delta, 0 ); + if ( error ) + goto Exit; + + /* compute second angle point */ + delta = gf_v2d_from_polar(length, phi - rotate); + delta.x += middle.x; + delta.y += middle.y; + + error = ft_stroke_border_lineto( border, &delta, 0 ); + if ( error ) + goto Exit; + + /* finally, add a movable end point */ + delta = gf_v2d_from_polar(radius, stroker->angle_out + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, 1); + } + /* this is a bevel (intersection) */ + else { + Fixed length; + GF_Point2D delta; + + + length = gf_divfix( stroker->radius, thcos ); + + delta = gf_v2d_from_polar(length, phi ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, 0 ); + if (error) goto Exit; + + /* now add end point */ + delta = gf_v2d_from_polar(stroker->radius, stroker->angle_out + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, 1 ); + } + } +Exit: + return error; +} + + + +static s32 ft_stroker_process_corner(FT_Stroker *stroker ) +{ + s32 error = 0; + Fixed turn; + s32 inside_side; + turn = gf_angle_diff( stroker->angle_in, stroker->angle_out ); + + /* no specific corner processing is required if the turn is 0 */ + if ( turn == 0 ) + goto Exit; + + /* when we turn to the right, the inside side is 0 */ + inside_side = 0; + /* otherwise, the inside side is 1 */ + if (turn < 0 ) + inside_side = 1; + + /* process the inside side */ + error = ft_stroker_inside( stroker, inside_side ); + if ( error ) goto Exit; + + /* process the outside side */ + error = ft_stroker_outside( stroker, 1 - inside_side ); + +Exit: + return error; +} + + + /* add two points to the left and right borders corresponding to the */ + /* start of the subpath.. */ +static s32 ft_stroker_subpath_start( FT_Stroker *stroker, Fixed start_angle ) +{ + GF_Point2D delta; + GF_Point2D point; + s32 error; + FT_StrokeBorder border; + + delta = gf_v2d_from_polar(stroker->radius, start_angle + GF_PI2 ); + + point.x = stroker->center.x + delta.x; + point.y = stroker->center.y + delta.y; + + border = stroker->borders; + error = ft_stroke_border_moveto( border, &point ); + if ( error ) + goto Exit; + + point.x = stroker->center.x - delta.x; + point.y = stroker->center.y - delta.y; + + border++; + error = ft_stroke_border_moveto( border, &point ); + + /* save angle for last cap */ + stroker->subpath_angle = start_angle; + stroker->first_point = 0; + +Exit: + return error; +} + + +static s32 FT_Stroker_LineTo( FT_Stroker *stroker, GF_Point2D* to, Bool is_last) +{ + s32 error = 0; + FT_StrokeBorder border; + GF_Point2D delta; + Fixed angle; + s32 side; + + delta.x = to->x - stroker->center.x; + delta.y = to->y - stroker->center.y; + if (!is_last && !delta.x && !delta.y) return 0; + + angle = gf_atan2( delta.y, delta.x); + delta = gf_v2d_from_polar(stroker->radius, angle + GF_PI2 ); + + /* process corner if necessary */ + if ( stroker->first_point ) { + /* This is the first segment of a subpath. We need to */ + /* add a point to each border at their respective starting */ + /* point locations. */ + error = ft_stroker_subpath_start( stroker, angle ); + if ( error ) + goto Exit; + } else { + /* process the current corner */ + stroker->angle_out = angle; + error = ft_stroker_process_corner( stroker ); + if ( error ) + goto Exit; + } + + /* now add a line segment to both the "inside" and "outside" paths */ + for ( border = stroker->borders, side = 1; side >= 0; side--, border++ ) { + GF_Point2D point; + point.x = to->x + delta.x; + point.y = to->y + delta.y; + + error = ft_stroke_border_lineto( border, &point, 1 ); + if ( error ) + goto Exit; + + delta.x = -delta.x; + delta.y = -delta.y; + } + stroker->angle_in = angle; + stroker->center = *to; + +Exit: + return error; +} + + +static s32 FT_Stroker_ConicTo(FT_Stroker *stroker, GF_Point2D* control, GF_Point2D * to) +{ + s32 error = 0; + GF_Point2D bez_stack[34]; + GF_Point2D* arc; + GF_Point2D* limit = bez_stack + 30; + Fixed start_angle; + Bool first_arc = 1; + + + arc = bez_stack; + arc[0] = *to; + arc[1] = *control; + arc[2] = stroker->center; + + while ( arc >= bez_stack ) { + Fixed angle_in, angle_out; + angle_in = angle_out = 0; /* remove compiler warnings */ + + if ( arc < limit && + !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) ) + { + ft_conic_split( arc ); + arc += 2; + continue; + } + + if ( first_arc ) { + first_arc = 0; + + start_angle = angle_in; + + /* process corner if necessary */ + if ( stroker->first_point ) + error = ft_stroker_subpath_start( stroker, start_angle ); + else { + stroker->angle_out = start_angle; + error = ft_stroker_process_corner( stroker ); + } + } + + /* the arc's angle is small enough; we can add it directly to each */ + /* border */ + { + GF_Point2D ctrl, end; + Fixed theta, phi, rotate; + Fixed length; + s32 side; + + theta = gf_angle_diff( angle_in, angle_out ) / 2; + phi = angle_in + theta; + length = gf_divfix( stroker->radius, gf_cos( theta ) ); + + for ( side = 0; side <= 1; side++ ) { + rotate = FT_SIDE_TO_ROTATE( side ); + + /* compute control point */ + ctrl = gf_v2d_from_polar(length, phi + rotate ); + ctrl.x += arc[1].x; + ctrl.y += arc[1].y; + + /* compute end point */ + end = gf_v2d_from_polar(stroker->radius, angle_out + rotate ); + end.x += arc[0].x; + end.y += arc[0].y; + + error = ft_stroke_border_conicto( stroker->borders + side, &ctrl, &end ); + if ( error ) + goto Exit; + } + } + + arc -= 2; + + if ( arc < bez_stack ) + stroker->angle_in = angle_out; + } + + stroker->center = *to; +Exit: + return error; +} + + +static s32 FT_Stroker_CubicTo(FT_Stroker *stroker, + GF_Point2D* control1, + GF_Point2D* control2, + GF_Point2D* to ) +{ + s32 error = 0; + GF_Point2D bez_stack[37]; + GF_Point2D* arc; + GF_Point2D* limit = bez_stack + 32; + Fixed start_angle; + Bool first_arc = 1; + + arc = bez_stack; + arc[0] = *to; + arc[1] = *control2; + arc[2] = *control1; + arc[3] = stroker->center; + + while ( arc >= bez_stack ) { + Fixed angle_in, angle_mid, angle_out; + /* remove compiler warnings */ + angle_in = angle_out = angle_mid = 0; + + if (arc < limit && + !ft_cubic_is_small_enough( arc, &angle_in, &angle_mid, &angle_out ) ) + { + ft_cubic_split( arc ); + arc += 3; + continue; + } + + if ( first_arc ) { + first_arc = 0; + + /* process corner if necessary */ + start_angle = angle_in; + + if ( stroker->first_point ) + error = ft_stroker_subpath_start( stroker, start_angle ); + else { + stroker->angle_out = start_angle; + error = ft_stroker_process_corner( stroker ); + } + if ( error ) + goto Exit; + } + + /* the arc's angle is small enough; we can add it directly to each */ + /* border */ + { + GF_Point2D ctrl1, ctrl2, end; + Fixed theta1, phi1, theta2, phi2, rotate; + Fixed length1, length2; + s32 side; + + + theta1 = ABS( angle_mid - angle_in ) / 2; + theta2 = ABS( angle_out - angle_mid ) / 2; + phi1 = (angle_mid + angle_in ) / 2; + phi2 = (angle_mid + angle_out ) / 2; + length1 = gf_divfix( stroker->radius, gf_cos( theta1 ) ); + length2 = gf_divfix( stroker->radius, gf_cos(theta2) ); + + for ( side = 0; side <= 1; side++ ) { + rotate = FT_SIDE_TO_ROTATE( side ); + + /* compute control points */ + ctrl1 = gf_v2d_from_polar(length1, phi1 + rotate ); + ctrl1.x += arc[2].x; + ctrl1.y += arc[2].y; + + ctrl2 = gf_v2d_from_polar(length2, phi2 + rotate ); + ctrl2.x += arc[1].x; + ctrl2.y += arc[1].y; + + /* compute end point */ + end = gf_v2d_from_polar(stroker->radius, angle_out + rotate ); + end.x += arc[0].x; + end.y += arc[0].y; + + error = ft_stroke_border_cubicto( stroker->borders + side, + &ctrl1, &ctrl2, &end ); + if ( error ) + goto Exit; + } + } + + arc -= 3; + if ( arc < bez_stack ) + stroker->angle_in = angle_out; + } + + stroker->center = *to; + +Exit: + return error; +} + + +static s32 FT_Stroker_BeginSubPath(FT_Stroker *stroker, GF_Point2D* to) +{ + /* We cannot process the first point, because there is not enough */ + /* information regarding its corner/cap. The latter will be processed */ + /* in the "end_subpath" routine. */ + /* */ + stroker->first_point = 1; + stroker->center = *to; + + /* record the subpath start point index for each border */ + stroker->subpath_start = *to; + return 0; +} + +static s32 ft_stroker_add_reverse_left( FT_Stroker *stroker, Bool open ) +{ + FT_StrokeBorder right = stroker->borders + 0; + FT_StrokeBorder left = stroker->borders + 1; + s32 new_points; + s32 error = 0; + + if (!left->num_points) return 0; + + assert( left->start >= 0 ); + new_points = left->num_points - left->start; + if ( new_points > 0 ) { + error = ft_stroke_border_grow( right, (u32)new_points ); + if ( error ) + goto Exit; + + { + GF_Point2D* dst_point = right->points + right->num_points; + u8* dst_tag = right->tags + right->num_points; + GF_Point2D* src_point = left->points + left->num_points - 1; + u8* src_tag = left->tags + left->num_points - 1; + + while ( src_point >= left->points + left->start ) { + *dst_point = *src_point; + *dst_tag = *src_tag; + + if ( open ) + dst_tag[0] &= ~( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END ); + else { + /* switch begin/end tags if necessary.. */ + if ( dst_tag[0] & ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END ) ) + dst_tag[0] ^= ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END ); + } + + src_point--; + src_tag--; + dst_point++; + dst_tag++; + } + } + + left->num_points = left->start; + right->num_points += new_points; + + right->movable = 0; + left->movable = 0; + } + +Exit: + return error; +} + +/* there's a lot of magic in this function! */ +static s32 FT_Stroker_EndSubPath( FT_Stroker *stroker, Bool do_close) +{ + s32 error = 0; + FT_StrokeBorder right = stroker->borders; + if (do_close) { + Fixed turn; + s32 inside_side; + + /* process the corner */ + stroker->angle_out = stroker->subpath_angle; + turn = gf_angle_diff(stroker->angle_in, stroker->angle_out ); + + /* no specific corner processing is required if the turn is 0 */ + if ( turn != 0 ) { + /* when we turn to the right, the inside side is 0 */ + inside_side = 0; + + /* otherwise, the inside side is 1 */ + if ( turn < 0 ) inside_side = 1; + + /* IMPORTANT: WE DO NOT PROCESS THE INSIDE BORDER HERE! */ + /* process the inside side */ + /* error = ft_stroker_inside( stroker, inside_side ); */ + /* if ( error ) */ + /* goto Exit; */ + + /* process the outside side */ + error = ft_stroker_outside( stroker, 1 - inside_side ); + if ( error ) + goto Exit; + } + + ft_stroker_add_reverse_left(stroker, 0); + /* then end our two subpaths */ + ft_stroke_border_close( stroker->borders + 0 ); + ft_stroke_border_close( stroker->borders + 1 ); + } else { + /* All right, this is an opened path, we need to add a cap between */ + /* right & left, add the reverse of left, then add a final cap */ + /* between left & right. */ + error = ft_stroker_cap( stroker, stroker->angle_in, 0 ); + if ( error ) goto Exit; + + /* add reversed points from "left" to "right" */ + error = ft_stroker_add_reverse_left( stroker, 1 ); + if ( error ) goto Exit; + + /* now add the final cap */ + stroker->center = stroker->subpath_start; + error = ft_stroker_cap( stroker, + stroker->subpath_angle + GF_PI, 0 ); + if ( error ) + goto Exit; + + /* Now end the right subpath accordingly. The left one is */ + /* rewind and doesn't need further processing. */ + ft_stroke_border_close( right ); + } + +Exit: + return error; +} + + +static s32 FT_Stroker_GetCounts( FT_Stroker *stroker, u32 *anum_points, u32 *anum_contours ) +{ + u32 count1, count2, num_points = 0; + u32 count3, count4, num_contours = 0; + s32 error; + + error = ft_stroke_border_get_counts( stroker->borders + 0, &count1, &count2 ); + if ( error ) goto Exit; + error = ft_stroke_border_get_counts( stroker->borders + 1, &count3, &count4 ); + if ( error ) goto Exit; + num_points = count1 + count3; + num_contours = count2 + count4; + +Exit: + *anum_points = num_points; + *anum_contours = num_contours; + return error; +} + +/* +* The following is very similar to FT_Outline_Decompose, except +* that we do support opened paths, and do not scale the outline. +*/ +static s32 FT_Stroker_ParseOutline(FT_Stroker *stroker, GF_Path* outline) +{ + GF_Point2D v_last; + GF_Point2D v_control; + GF_Point2D v_start; + GF_Point2D* point; + GF_Point2D* limit; + u8 *tags; + s32 error; + u32 n; /* index of contour in outline */ + u32 first; /* index of first point in contour */ + s32 tag; /* current point's state */ + + if ( !outline || !stroker ) + return -1; + + first = 0; + + for ( n = 0; n < outline->n_contours; n++ ) { + s32 closed_subpath; + s32 last; /* index of last point in contour */ + + last = outline->contours[n]; + limit = outline->points + last; + + v_start = outline->points[first]; + v_last = outline->points[last]; + + v_control = v_start; + + point = outline->points + first; + tags = outline->tags + first; + tag = tags[0]; + + /* A contour cannot start with a cubic control point! */ + if ( tag == GF_PATH_CURVE_CUBIC ) + goto Invalid_Outline; + + /* check first point to determine origin */ + if ( tag == GF_PATH_CURVE_CONIC ) { + /* First point is conic control. Yes, this happens. */ + if ( outline->tags[last] & GF_PATH_CURVE_ON ) { + /* start at last point if it is on the curve */ + v_start = v_last; + limit--; + } else { + /* if both first and last points are conic, */ + /* start at their middle and record its position */ + /* for closure */ + v_start.x = ( v_start.x + v_last.x ) / 2; + v_start.y = ( v_start.y + v_last.y ) / 2; + + v_last = v_start; + } + point--; + tags--; + } + closed_subpath = (outline->tags[outline->contours[n]]==GF_PATH_CLOSE) ? 1 : 0; + + error = FT_Stroker_BeginSubPath(stroker, &v_start); + if ( error ) + goto Exit; + + /*subpath is a single point, force a lineTo to start for the stroker to compute lineCap*/ + if (point==limit) { + error = FT_Stroker_LineTo(stroker, &v_start, 1); + closed_subpath = 0; + goto Close; + } + + while ( point < limit ) { + point++; + tags++; + + tag = tags[0]; + switch ( tag ) { + case GF_PATH_CURVE_ON: /* emit a single line_to */ + case GF_PATH_CLOSE: /* emit a single line_to */ + { + GF_Point2D vec; + vec.x = point->x; + vec.y = point->y; + + error = FT_Stroker_LineTo( stroker, &vec, (point == limit) ? 1 : 0 ); + if ( error ) + goto Exit; + continue; + } + + case GF_PATH_CURVE_CONIC: /* consume conic arcs */ + v_control.x = point->x; + v_control.y = point->y; + + Do_Conic: + if ( point < limit ) { + GF_Point2D vec; + GF_Point2D v_middle; + + + point++; + tags++; + tag = tags[0]; + + vec = point[0]; + + if ( tag & GF_PATH_CURVE_ON) { + + error = FT_Stroker_ConicTo( stroker, &v_control, &vec ); + if ( error ) + goto Exit; + continue; + } + + if ( tag != GF_PATH_CURVE_CONIC ) + goto Invalid_Outline; + + v_middle.x = ( v_control.x + vec.x ) / 2; + v_middle.y = ( v_control.y + vec.y ) / 2; + + error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle ); + if ( error ) + goto Exit; + + v_control = vec; + goto Do_Conic; + } + error = FT_Stroker_ConicTo( stroker, &v_control, &v_start ); + goto Close; + + default: /* GF_PATH_CURVE_CUBIC */ + { + GF_Point2D vec1, vec2; + + if ( point + 1 > limit || + tags[1] != GF_PATH_CURVE_CUBIC ) + goto Invalid_Outline; + + point += 2; + tags += 2; + + vec1 = point[-2]; + vec2 = point[-1]; + + if ( point <= limit ) { + GF_Point2D vec; + vec = point[0]; + + error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec ); + if ( error ) + goto Exit; + continue; + } + error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start ); + goto Close; + } + break; + } + } + +Close: + if ( error ) goto Exit; + + error = FT_Stroker_EndSubPath(stroker, closed_subpath); + if ( error ) + goto Exit; + + first = last + 1; + } + return 0; + +Exit: + return error; + +Invalid_Outline: + return -1; +} + + + + + + +#define GF_PATH_DOT_LEN 1 +#define GF_PATH_DOT_SPACE 2 +#define GF_PATH_DASH_LEN 3 + +static Fixed gf_path_get_dash(GF_PenSettings *pen, u32 dash_slot, u32 *next_slot) +{ + Fixed ret = 0; + switch (pen->dash) { + case GF_DASH_STYLE_DOT: + if (dash_slot==0) ret = GF_PATH_DOT_LEN; + else if (dash_slot==1) ret = GF_PATH_DOT_SPACE; + *next_slot = (dash_slot + 1) % 2; + return ret * pen->width; + case GF_DASH_STYLE_DASH: + if (dash_slot==0) ret = GF_PATH_DASH_LEN; + else if (dash_slot==1) ret = GF_PATH_DOT_SPACE; + *next_slot = (dash_slot + 1) % 2; + return ret * pen->width; + case GF_DASH_STYLE_DASH_DOT: + if (dash_slot==0) ret = GF_PATH_DASH_LEN; + else if (dash_slot==1) ret = GF_PATH_DOT_SPACE; + else if (dash_slot==2) ret = GF_PATH_DOT_LEN; + else if (dash_slot==3) ret = GF_PATH_DOT_SPACE; + *next_slot = (dash_slot + 1) % 4; + return ret * pen->width; + case GF_DASH_STYLE_DASH_DASH_DOT: + if (dash_slot==0) ret = GF_PATH_DASH_LEN; + else if (dash_slot==1) ret = GF_PATH_DOT_SPACE; + else if (dash_slot==2) ret = GF_PATH_DASH_LEN; + else if (dash_slot==3) ret = GF_PATH_DOT_SPACE; + else if (dash_slot==4) ret = GF_PATH_DOT_LEN; + else if (dash_slot==5) ret = GF_PATH_DOT_SPACE; + *next_slot = (dash_slot + 1) % 6; + return ret * pen->width; + case GF_DASH_STYLE_DASH_DOT_DOT: + if (dash_slot==0) ret = GF_PATH_DASH_LEN; + else if (dash_slot==1) ret = GF_PATH_DOT_SPACE; + else if (dash_slot==2) ret = GF_PATH_DOT_LEN; + else if (dash_slot==3) ret = GF_PATH_DOT_SPACE; + else if (dash_slot==4) ret = GF_PATH_DOT_LEN; + else if (dash_slot==5) ret = GF_PATH_DOT_SPACE; + *next_slot = (dash_slot + 1) % 6; + return ret * pen->width; + case GF_DASH_STYLE_CUSTOM: + case GF_DASH_STYLE_SVG: + if (!pen->dash_set || !pen->dash_set->num_dash) return 0; + if (dash_slot>=pen->dash_set->num_dash) dash_slot = 0; + ret = pen->dash_set->dashes[dash_slot]; + *next_slot = (1 + dash_slot) % pen->dash_set->num_dash; + if (pen->dash==GF_DASH_STYLE_SVG) return ret; + /*custom dashes are of type Fixed !!*/ + return gf_mulfix(ret, pen->width); + + default: + case GF_DASH_STYLE_PLAIN: + *next_slot = 0; + return 0; + } +} + + +/* Credits go to Raph Levien for libart / art_vpath_dash */ + +/* FIXEME - NOT DONE - Merge first and last subpaths when first and last dash segment are joined a closepath. */ +static GF_Err gf_path_mergedashes(GF_Path *gp, u32 start_contour_index) +{ + u32 i, dash_first_pt, dash_nb_pts; + if (start_contour_index) { + dash_nb_pts = gp->contours[start_contour_index] - gp->contours[start_contour_index-1]; + dash_first_pt = gp->contours[start_contour_index-1]+1; + } else { + dash_nb_pts = gp->contours[start_contour_index]+1; + dash_first_pt = 0; + } + /*skip first point of first dash in subpath (same as last point of last dash)*/ + for (i=1;ipoints[dash_first_pt + i]); + if (e) return e; + } + /*remove initial dash*/ + gp->n_points -= dash_nb_pts; + memmove(gp->points + dash_first_pt, gp->points + dash_first_pt + dash_nb_pts, sizeof(GF_Point2D)*(gp->n_points - dash_first_pt)); + memmove(gp->tags + dash_first_pt, gp->tags + dash_first_pt + dash_nb_pts, sizeof(u8)*(gp->n_points - dash_first_pt)); + + for (i=start_contour_index; in_contours-1; i++) { + gp->contours[i] = gp->contours[i+1] - dash_nb_pts; + } + gp->n_contours--; + gp->contours = (u32 *)realloc(gp->contours, sizeof(u32)*gp->n_contours); + +/* + gp->points = realloc(gp->points, sizeof(GF_Point2D)*gp->n_points); + gp->tags = realloc(gp->tags, sizeof(u8)*gp->n_points); + gp->n_alloc_points = gp->n_points; +*/ + return GF_OK; +} + +static GF_Err evg_dash_subpath(GF_Path *dashed, GF_Point2D *pts, u32 nb_pts, GF_PenSettings *pen, Fixed length_scale) +{ + Fixed *dists; + Fixed totaldist; + Fixed dash; + Fixed dist; + Fixed dash_dist; + s32 offsetinit; + u32 next_offset; + s32 toggleinit; + s32 firstindex; + Bool toggle_check; + GF_Err e; + u32 i, start_ind; + Fixed phase; + s32 offset, toggle; + + dists = (Fixed *)malloc(sizeof (Fixed) * nb_pts); + if (dists == NULL) return GF_OUT_OF_MEM; + + /* initial values */ + toggleinit = 1; + offsetinit = 0; + dash_dist = 0; + + dash = gf_path_get_dash(pen, offsetinit, &next_offset); + if (length_scale) dash = gf_mulfix(dash, length_scale); + firstindex = -1; + toggle_check = 0; + + start_ind = 0; + dist = 0; + + /*SVG dashing is different from BIFS one*/ + if (pen->dash==GF_DASH_STYLE_SVG) { + while (pen->dash_offset>0) { + if (pen->dash_offset - dash < 0) { + dash -= pen->dash_offset; + pen->dash_offset = 0; + break; + } + pen->dash_offset -= dash; + offsetinit = next_offset; + toggleinit = !toggleinit; + dash = gf_path_get_dash(pen, offsetinit, &next_offset); + if (length_scale) dash = gf_mulfix(dash, length_scale); + } + if (pen->dash_offset<0) { + offsetinit = pen->dash_set->num_dash-1; + dash = gf_path_get_dash(pen, offsetinit, &next_offset); + if (length_scale) dash = gf_mulfix(dash, length_scale); + while (pen->dash_offset<0) { + toggleinit = !toggleinit; + if (pen->dash_offset + dash > 0) { + dash_dist = dash+pen->dash_offset; + pen->dash_offset = 0; + break; + } + pen->dash_offset += dash; + if (offsetinit) offsetinit --; + else offsetinit = pen->dash_set->num_dash-1; + dash = gf_path_get_dash(pen, offsetinit, &next_offset); + if (length_scale) dash = gf_mulfix(dash, length_scale); + } + } + } + + /* calculate line lengths and update offset*/ + totaldist = 0; + for (i = 0; i < nb_pts - 1; i++) { + GF_Point2D diff; + diff.x = pts[i+1].x - pts[i].x; + diff.y = pts[i+1].y - pts[i].y; + dists[i] = gf_v2d_len(&diff); + + if (pen->dash_offset > dists[i]) { + pen->dash_offset -= dists[i]; + dists[i] = 0; + } + else if (pen->dash_offset) { + Fixed a, x, y, dx, dy; + + a = gf_divfix(pen->dash_offset, dists[i]); + dx = pts[i + 1].x - pts[i].x; + dy = pts[i + 1].y - pts[i].y; + x = pts[i].x + gf_mulfix(a, dx); + y = pts[i].y + gf_mulfix(a, dy); + e = gf_path_add_move_to(dashed, x, y); + if (e) goto err_exit; + totaldist += dists[i]; + dist = pen->dash_offset; + pen->dash_offset = 0; + start_ind = i; + } else { + totaldist += dists[i]; + } + } + dash -= dash_dist; + dash_dist = 0; + + /* subpath fits within first dash and no offset*/ + if (!dist && totaldist <= dash) { + if (toggleinit) { + gf_path_add_move_to_vec(dashed, &pts[0]); + for (i=1; in_contours - 1; + } + + while (i < nb_pts - 1) { + /* dash boundary is next */ + if (dists[i] - dist > dash - phase) { + Fixed a, x, y, dx, dy; + dist += dash - phase; + a = gf_divfix(dist, dists[i]); + dx = pts[i + 1].x - pts[i].x; + dy = pts[i + 1].y - pts[i].y; + x = pts[i].x + gf_mulfix(a, dx); + y = pts[i].y + gf_mulfix(a, dy); + + if (!toggle_check || ((x != pts[i].x) || (y != pts[i].y))) { + if (toggle) { + e = gf_path_add_line_to(dashed, x, y); + if (e) goto err_exit; + } + else { + e = gf_path_add_move_to(dashed, x, y); + if (e) goto err_exit; + } + } + + /* advance to next dash */ + toggle = !toggle; + phase = 0; + offset = next_offset; + dash = gf_path_get_dash(pen, offset, &next_offset); + if (length_scale) dash = gf_mulfix(dash, length_scale); + } + /* end of line in subpath is next */ + else { + phase += dists[i] - dist; + i ++; + toggle_check = 0; + dist = 0; + if (toggle) { + e = gf_path_add_line_to_vec(dashed, &pts[i]); + if (e) goto err_exit; + toggle_check = 1; + + if ( (firstindex>=0) && (i == (nb_pts - 1) && ((firstindex + 1) != (s32) start_ind ) )) { + /*merge if closed path*/ + if ((pts[0].x==pts[nb_pts-1].x) && (pts[0].y==pts[nb_pts-1].y)) { + e = gf_path_mergedashes(dashed, firstindex); + if (e) goto err_exit; + } + } + } + } + } + +err_exit: +// pen->dash_offset = dist; + free(dists); + return GF_OK; +} + +static GF_Path *gf_path_dash(GF_Path *path, GF_PenSettings *pen) +{ + u32 i, j, nb_pts; + GF_Point2D *pts; + Fixed length_scale = 0; + Fixed dash_off = pen->dash_offset; + GF_Path *dashed = gf_path_new(); + + + /* calculate line lengths and update offset*/ + if (pen->path_length) { + Fixed totaldist = 0; + nb_pts = 0; + for (i=0; in_contours; i++) { + pts = &path->points[nb_pts]; + nb_pts = 1+path->contours[i] - nb_pts; + + for (j=0; jcontours[i]; + } + length_scale = gf_divfix(totaldist, pen->path_length); + pen->dash_offset = gf_mulfix(pen->dash_offset, length_scale); + } + + nb_pts = 0; + for (i=0; in_contours; i++) { + pts = &path->points[nb_pts]; + nb_pts = 1+path->contours[i] - nb_pts; + evg_dash_subpath(dashed, pts, nb_pts, pen, length_scale); + nb_pts = 1+path->contours[i]; +// if (length_scale) pen->dash_offset = gf_mulfix(pen->dash_offset, length_scale); + } + pen->dash_offset = dash_off; + dashed->flags |= GF_PATH_FILL_ZERO_NONZERO; + return dashed; +} + +GF_EXPORT +GF_Path *gf_path_get_outline(GF_Path *path, GF_PenSettings pen) +{ + s32 error; + GF_Path *outline; + GF_Path *dashed; + GF_Path *scaled; + FT_Stroker stroker; + if (!path || !pen.width) return NULL; + + memset(&stroker, 0, sizeof(stroker)); + stroker.borders[0].start = -1; + stroker.borders[1].start = -1; + stroker.line_cap = pen.cap; + stroker.line_join = pen.join; + stroker.miter_limit = pen.miterLimit; + stroker.radius = pen.width/2; + + gf_path_flatten(path); + + scaled = NULL; + /*if not centered, simply scale path...*/ + if (pen.align) { + Fixed sx, sy; + GF_Rect bounds; + gf_path_get_bounds(path, &bounds); + if (pen.align==GF_PATH_LINE_OUTSIDE) { + sx = gf_divfix(bounds.width+pen.width, bounds.width); + sy = gf_divfix(bounds.height+pen.width, bounds.height); + } else { + /*note: this may result in negative scaling, not our pb but the author's one*/ + sx = gf_divfix(bounds.width-pen.width, bounds.width); + sy = gf_divfix(bounds.height-pen.width, bounds.height); + } + if (sx && sy) { + u32 i; + scaled = gf_path_clone(path); + for (i=0; in_points; i++) { + scaled->points[i].x = gf_mulfix(scaled->points[i].x, sx); + scaled->points[i].y = gf_mulfix(scaled->points[i].y, sy); + } + path = scaled; + } + } + + /*if dashing, first flatten path then dash all segments*/ + dashed = NULL; + /*security, seen in some SVG files*/ + if (pen.dash_set && (pen.dash_set->num_dash==1) && (pen.dash_set->dashes[0]==0)) pen.dash = GF_DASH_STYLE_PLAIN; + if (pen.dash) { + GF_Path *flat; + flat = gf_path_get_flatten(path); + if (!flat) return NULL; + dashed = gf_path_dash(flat, &pen); + gf_path_del(flat); + if (!dashed) return NULL; + path = dashed; + } + + outline = NULL; + error = FT_Stroker_ParseOutline(&stroker, path); + if (!error) { + u32 nb_pt, nb_cnt; + error = FT_Stroker_GetCounts(&stroker, &nb_pt, &nb_cnt); + if (!error) { + FT_StrokeBorder sborder; + outline = gf_path_new(); + if (nb_pt) { + outline->points = (GF_Point2D *) malloc(sizeof(GF_Point2D)*nb_pt); + outline->tags = (u8 *) malloc(sizeof(u8)*nb_pt); + outline->contours = (u32 *) malloc(sizeof(u32)*nb_cnt); + outline->n_alloc_points = nb_pt; + sborder = &stroker.borders[0]; + if (sborder->valid ) ft_stroke_border_export(sborder, outline); + sborder = &stroker.borders[1]; + /*if left border is valid this is a closed path, used odd/even rule - we will have issues at recovering + segments...*/ + if (sborder->valid && sborder->num_points) { + ft_stroke_border_export(sborder, outline); + } + /*otherwise this is an open path, use zero/non-zero*/ + else { + outline->flags |= GF_PATH_FILL_ZERO_NONZERO; + } + } + outline->flags |= GF_PATH_BBOX_DIRTY; + + /*our caps are cubic bezier!!*/ + if ( (path->flags & GF_PATH_FLATTENED) && (pen.cap!=GF_LINE_CAP_ROUND) && (pen.join!=GF_LINE_JOIN_ROUND) ) + outline->flags |= GF_PATH_FLATTENED; + } + } + + if (stroker.borders[0].points) free(stroker.borders[0].points); + if (stroker.borders[0].tags) free(stroker.borders[0].tags); + if (stroker.borders[1].points) free(stroker.borders[1].points); + if (stroker.borders[1].tags) free(stroker.borders[1].tags); + + if (dashed) gf_path_del(dashed); + if (scaled) gf_path_del(scaled); + + return outline; +} + diff --git a/src/utils/symbian_net.cpp b/src/utils/symbian_net.cpp new file mode 100644 index 0000000..2e456b4 --- /dev/null +++ b/src/utils/symbian_net.cpp @@ -0,0 +1,1316 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#if defined(WIN32) || defined(_WIN32_WCE) + +#ifdef _WIN32_WCE +#include +#else +#include +#include +#include +#endif + +#include + +#if !defined(__GNUC__) + +#if defined(IPV6_MULTICAST_IF) +#define GPAC_HAS_IPV6 1 +#pragma message("Using WinSock IPV6") +#else +#undef GPAC_HAS_IPV6 +#pragma message("Using WinSock IPV4") +#endif + +#endif + +/*common win32 redefs*/ +#define EAGAIN WSAEWOULDBLOCK +#define EISCONN WSAEISCONN +#define ENOTCONN WSAENOTCONN +#define ECONNRESET WSAECONNRESET +#define EMSGSIZE WSAEMSGSIZE +#define ECONNABORTED WSAECONNABORTED +#define ENETDOWN WSAENETDOWN + +#define LASTSOCKERROR WSAGetLastError() + +/*the number of sockets used. This because the WinSock lib needs init*/ +static int wsa_init = 0; + +#include + +/*end-win32*/ + +#else +/*non-win32*/ +#include +#include +#include + +#ifndef __BEOS__ +#include +#endif + +#ifndef __DARWIN__ +#include +#endif + +#include +#include +#include +#include + +#include + +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define LASTSOCKERROR errno + +typedef s32 SOCKET; +#define closesocket(v) close(v) + +#endif + +#ifdef __SYMBIAN32__ +#define SSO_CAST +#else +#define SSO_CAST (const char *) +#endif + + +#define SOCK_MICROSEC_WAIT 500 + +#ifdef GPAC_HAS_IPV6 +static u32 ipv6_check_state = 0; +#endif + +/*internal flags*/ +enum +{ + GF_SOCK_IS_TCP = 1<<9, + GF_SOCK_IS_IPV6 = 1<<10, + GF_SOCK_NON_BLOCKING = 1<<11, + GF_SOCK_IS_MULTICAST = 1<<12, + GF_SOCK_IS_LISTENING = 1<<13, + GF_SOCK_HAS_SOURCE = 1<<14 +}; + +struct __tag_socket +{ + u32 flags; + SOCKET socket; + /*destination address for sendto/recvfrom*/ +#ifdef GPAC_HAS_IPV6 + struct sockaddr_storage dest_addr; +#else + struct sockaddr_in dest_addr; +#endif + u32 dest_addr_len; +}; + +/* + Some NTP tools +*/ + +void gf_net_get_ntp(u32 *sec, u32 *frac) +{ + struct timeval now; +#ifdef WIN32 + s32 gettimeofday(struct timeval *tp, void *tz); +#endif + gettimeofday(&now, NULL); + *sec = (u32) (now.tv_sec) + GF_NTP_SEC_1900_TO_1970; + *frac = (u32) ( (now.tv_usec << 12) + (now.tv_usec << 8) - ((now.tv_usec * 3650) >> 6) ); +} + +u32 gf_net_has_ipv6() +{ +#ifdef GPAC_HAS_IPV6 + if (!ipv6_check_state) { + SOCKET s; +#ifdef WIN32 + if (!wsa_init) { + WSADATA Data; + if (WSAStartup(0x0202, &Data)!=0) { + ipv6_check_state = 1; + return 0; + } + } +#endif + s = socket(PF_INET6, SOCK_STREAM, 0); + if (!s) ipv6_check_state = 1; + else { + ipv6_check_state = 2; + closesocket(s); + } +#ifdef WIN32 + if (!wsa_init) WSACleanup(); +#endif + } + return (ipv6_check_state==2); +#else + return 0; +#endif +} + +#ifdef GPAC_HAS_IPV6 +static struct addrinfo *gf_sk_get_ipv6_addr(char *PeerName, u16 PortNumber, int family, int flags, int sock_type) +{ + struct addrinfo *res=NULL; + struct addrinfo hints; + char node[50], portstring[20], *service, *dest; + + service = dest = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = sock_type; + hints.ai_family = family; + hints.ai_flags = flags; + + if (PortNumber) { + sprintf (portstring, "%d", PortNumber); + service = (char *)portstring; + } + if (PeerName) { + strcpy(node, PeerName); + if (node[0]=='[') { + node[strlen(node)-1] = 0; + strcpy(node, &node[1]); + } + dest = (char *) node; + } + if (getaddrinfo((const char *)dest, (const char *)service, &hints, &res) != 0) return NULL; + return res; +} + +static Bool gf_sk_ipv6_set_remote_address(GF_Socket *sock, char *address, u16 PortNumber) +{ + struct addrinfo *res = gf_sk_get_ipv6_addr(address, PortNumber, AF_UNSPEC, 0, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM); + if (!res) return 0; + memcpy(&sock->dest_addr, res->ai_addr, res->ai_addrlen); + sock->dest_addr_len = res->ai_addrlen; + freeaddrinfo(res); + return 1; +} +#endif + + +GF_Err gf_sk_get_host_name(char *buffer) +{ + s32 ret = gethostname(buffer, GF_MAX_IP_NAME_LEN); + return (ret == SOCKET_ERROR) ? GF_IP_ADDRESS_NOT_FOUND : GF_OK; +} + +GF_Err gf_sk_get_local_ip(GF_Socket *sock, char *buffer) +{ +#ifdef GPAC_HAS_IPV6 + char clienthost[NI_MAXHOST]; + struct sockaddr_storage clientaddr; + socklen_t addrlen = sizeof(clientaddr); + if (getsockname(sock->socket, (struct sockaddr *)&clientaddr, &addrlen)) return GF_IP_NETWORK_FAILURE; + + if (getnameinfo((struct sockaddr *)&clientaddr, addrlen, clienthost, sizeof(clienthost), NULL, 0, NI_NUMERICHOST)) + return GF_IP_NETWORK_FAILURE; + strcpy(buffer, clienthost); +#else + char *ip; + struct sockaddr_in name; + u32 len = sizeof(struct sockaddr_in); + if (getsockname(sock->socket, (struct sockaddr*) &name, &len)) return GF_IP_NETWORK_FAILURE; + ip = inet_ntoa(name.sin_addr); + if (!ip) return GF_IP_NETWORK_FAILURE; + strcpy(buffer, ip); +#endif + return GF_OK; +} + +GF_Socket *gf_sk_new(u32 SocketType) +{ + GF_Socket *tmp; + + /*init WinSock*/ +#ifdef WIN32 + WSADATA Data; + if (!wsa_init && (WSAStartup(0x0202, &Data)!=0) ) return NULL; +#endif + if ((SocketType != GF_SOCK_TYPE_UDP) && (SocketType != GF_SOCK_TYPE_TCP)) return NULL; + + GF_SAFEALLOC(tmp, GF_Socket); + if (!tmp) return NULL; + if (SocketType == GF_SOCK_TYPE_TCP) tmp->flags |= GF_SOCK_IS_TCP; + +#ifdef GPAC_HAS_IPV6 + memset(&tmp->dest_addr, 0, sizeof(struct sockaddr_storage)); +#else + memset(&tmp->dest_addr, 0, sizeof(struct sockaddr_in)); + tmp->dest_addr_len = sizeof(struct sockaddr); +#endif + +#ifdef WIN32 + wsa_init ++; +#endif + return tmp; +} + +GF_Err gf_sk_set_buffer_size(GF_Socket *sock, Bool SendBuffer, u32 NewSize) +{ + if (!sock || !sock->socket) return GF_BAD_PARAM; + + if (SendBuffer) { + setsockopt(sock->socket, SOL_SOCKET, SO_SNDBUF, (char *) &NewSize, sizeof(u32) ); + } else { + setsockopt(sock->socket, SOL_SOCKET, SO_RCVBUF, (char *) &NewSize, sizeof(u32) ); + } + return GF_OK; +} + +GF_Err gf_sk_set_block_mode(GF_Socket *sock, u32 NonBlockingOn) +{ +#ifdef WIN32 + s32 res; + u_long val = NonBlockingOn; + res = ioctlsocket(sock->socket, FIONBIO, &val); + if (res) return GF_SERVICE_ERROR; +#else +// s32 flag = fcntl(sock->socket, F_GETFL, 0); +// res = fcntl(sock->socket, F_SETFL, flag | O_NONBLOCK); +// if (res) return GF_SERVICE_ERROR; +#endif + if (NonBlockingOn) { + sock->flags |= GF_SOCK_NON_BLOCKING; + } else { + sock->flags &= ~GF_SOCK_NON_BLOCKING; + } + return GF_OK; +} + + +static void gf_sk_free(GF_Socket *sock) +{ + /*leave multicast*/ + if (sock->socket && (sock->flags & GF_SOCK_IS_MULTICAST) ) { + struct ip_mreq mreq; +#ifdef GPAC_HAS_IPV6 + struct sockaddr *addr = (struct sockaddr *)&sock->dest_addr; + if (addr->sa_family==AF_INET6) { + struct ipv6_mreq mreq6; + memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); + mreq6.ipv6mr_interface= 0; + setsockopt(sock->socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *) &mreq6, sizeof(mreq6)); + } else { + mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + mreq.imr_interface.s_addr = INADDR_ANY; + setsockopt(sock->socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &mreq, sizeof(mreq)); + } +#else + mreq.imr_multiaddr.s_addr = sock->dest_addr.sin_addr.s_addr; + mreq.imr_interface.s_addr = INADDR_ANY; + setsockopt(sock->socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &mreq, sizeof(mreq)); +#endif + } + if (sock->socket) closesocket(sock->socket); + sock->socket = (SOCKET) 0L; +} + +void gf_sk_del(GF_Socket *sock) +{ + gf_sk_free(sock); +#ifdef WIN32 + wsa_init --; + if (!wsa_init) WSACleanup(); +#endif + free(sock); +} + +void gf_sk_reset(GF_Socket *sock) +{ + u32 clear; + if (sock) setsockopt(sock->socket, SOL_SOCKET, SO_ERROR, (char *) &clear, sizeof(u32) ); +} + +s32 gf_sk_get_handle(GF_Socket *sock) +{ + return sock->socket; +} + + + +//connects a socket to a remote peer on a given port +GF_Err gf_sk_connect(GF_Socket *sock, char *PeerName, u16 PortNumber, char *local_ip) +{ + s32 ret; +#ifdef GPAC_HAS_IPV6 + u32 type = (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM; + struct addrinfo *res, *aip; + + gf_sk_free(sock); + + res = gf_sk_get_ipv6_addr(PeerName, PortNumber, AF_UNSPEC, AI_PASSIVE, type); + if (!res) return GF_IP_CONNECTION_FAILURE; + + /*for all interfaces*/ + for (aip=res; aip!=NULL; aip=aip->ai_next) { + if (type != (u32) aip->ai_socktype) continue; + sock->socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (sock->socket == INVALID_SOCKET) { + sock->socket = (SOCKET)NULL; + continue; + } + if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); + if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; + else sock->flags &= ~GF_SOCK_IS_IPV6; + + ret = connect(sock->socket, aip->ai_addr, aip->ai_addrlen); + if (ret == SOCKET_ERROR) { + closesocket(sock->socket); + sock->socket = (SOCKET)NULL; + continue; + } + + memcpy(&sock->dest_addr, aip->ai_addr, aip->ai_addrlen); + sock->dest_addr_len = aip->ai_addrlen; + freeaddrinfo(res); + return GF_OK; + } + freeaddrinfo(res); + return GF_IP_CONNECTION_FAILURE; + +#else + struct hostent *Host; + + if (!sock->socket) + sock->socket = socket(AF_INET, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); + + /*setup the address*/ + sock->dest_addr.sin_family = AF_INET; + sock->dest_addr.sin_port = htons(PortNumber); + /*get the server IP*/ + sock->dest_addr.sin_addr.s_addr = inet_addr(PeerName); + if (sock->dest_addr.sin_addr.s_addr==INADDR_NONE) { + Host = gethostbyname(PeerName); + if (Host == NULL) { + switch (LASTSOCKERROR) { +#ifndef __SYMBIAN32__ + case ENETDOWN: return GF_IP_NETWORK_FAILURE; + //case ENOHOST: return GF_IP_ADDRESS_NOT_FOUND; +#endif + default: return GF_IP_NETWORK_FAILURE; + } + } + memcpy((char *) &sock->dest_addr.sin_addr, Host->h_addr_list[0], sizeof(u32)); + } + + if (sock->flags & GF_SOCK_IS_TCP) { + ret = connect(sock->socket, (struct sockaddr *) &sock->dest_addr, sizeof(struct sockaddr)); + if (ret == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: return GF_IP_SOCK_WOULD_BLOCK; + case EISCONN: return GF_OK; + default: return GF_IP_CONNECTION_FAILURE; + } + } + } +#endif + return GF_OK; +} + + +//binds the given socket to the specified port. If ReUse is true +//this will enable reuse of ports on a single machine +GF_Err gf_sk_bind(GF_Socket *sock, char *local_ip, u16 port, char *peer_name, u16 peer_port, u32 options) +{ +#ifdef GPAC_HAS_IPV6 + struct addrinfo *res, *aip; + int af; + u32 type; +#else + size_t addrlen; + struct sockaddr_in LocalAdd; + struct hostent *Host; + char buf[GF_MAX_IP_NAME_LEN]; +#endif + s32 ret; + s32 optval; + + if (!sock || sock->socket) return GF_BAD_PARAM; + +#ifdef GPAC_HAS_IPV6 + type = (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM; + af = (options & GF_SOCK_FORCE_IPV6) ? PF_INET6 : PF_UNSPEC; + if (!gf_net_has_ipv6()) af = PF_INET; + /*probe way to peer: is it V4 or V6? */ + if (peer_name && peer_port) { + res = gf_sk_get_ipv6_addr(peer_name, peer_port, af, AI_PASSIVE, type); + if (!res) return GF_IP_CONNECTION_FAILURE; +#ifdef WIN32 + /*win32 has troubles redirecting IPV4 datagrams to IPV6 sockets, so override + local family type to avoid IPV4(S)->IPV6(C) UDP*/ + af = res->ai_family; +#endif + memcpy(&sock->dest_addr, res->ai_addr, res->ai_addrlen); + sock->dest_addr_len = res->ai_addrlen; + freeaddrinfo(res); + } + + res = gf_sk_get_ipv6_addr(NULL, port, af, AI_PASSIVE, type); + if (!res) return GF_IP_CONNECTION_FAILURE; + + /*for all interfaces*/ + for (aip=res; aip!=NULL; aip=aip->ai_next) { + if (type != (u32) aip->ai_socktype) continue; + +#ifdef WIN32 + /*recurrent pb with win32: this is not a true dual-stack, listening with a v6 socket for v4 source is + most of the time failing. On win32, only move for connection-less V6 sockets if no other way available*/ + if (aip->ai_next && (aip->ai_next->ai_family==PF_INET)) continue; +#endif + + sock->socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (sock->socket == INVALID_SOCKET) { + sock->socket = (SOCKET)NULL; + continue; + } + if (options & GF_SOCK_REUSE_PORT) { + optval = 1; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &optval, sizeof(optval)); + } + if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); + + ret = bind(sock->socket, aip->ai_addr, aip->ai_addrlen); + if (ret == SOCKET_ERROR) { + closesocket(sock->socket); + sock->socket = (SOCKET)NULL; + continue; + } + + if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; + else sock->flags &= ~GF_SOCK_IS_IPV6; + + if (peer_name && peer_port) + sock->flags |= GF_SOCK_HAS_SOURCE; + + freeaddrinfo(res); + return GF_OK; + } + freeaddrinfo(res); + return GF_IP_CONNECTION_FAILURE; + +#else + + if (!sock->socket) { + sock->socket = socket(AF_INET, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); + if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); + sock->flags &= ~GF_SOCK_IS_IPV6; + } + + memset((void *) &LocalAdd, 0, sizeof(LocalAdd)); + ret = gethostname(buf, GF_MAX_IP_NAME_LEN); + if (ret == SOCKET_ERROR) return GF_IP_ADDRESS_NOT_FOUND; + /*get the IP address*/ + Host = gethostbyname(buf); + if (Host == NULL) return GF_IP_ADDRESS_NOT_FOUND; + /*setup the address*/ + memcpy((char *) &LocalAdd.sin_addr, Host->h_addr_list[0], sizeof(LocalAdd.sin_addr)); + LocalAdd.sin_family = AF_INET; + LocalAdd.sin_addr.s_addr = INADDR_ANY; + LocalAdd.sin_port = htons(port); + addrlen = sizeof(struct sockaddr_in); + if (options & GF_SOCK_REUSE_PORT) { + optval = 1; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, SSO_CAST &optval, sizeof(optval)); + } + /*bind the socket*/ + ret = bind(sock->socket, (struct sockaddr *) &LocalAdd, addrlen); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + + if (peer_name && peer_port) { + sock->dest_addr.sin_port = htons(peer_port); + sock->dest_addr.sin_family = AF_INET; + sock->dest_addr.sin_addr.s_addr = inet_addr(peer_name); + if (sock->dest_addr.sin_addr.s_addr == INADDR_NONE) { + Host = gethostbyname(peer_name); + if (Host == NULL) return GF_IP_ADDRESS_NOT_FOUND; + memcpy((char *) &sock->dest_addr.sin_addr, Host->h_addr_list[0], sizeof(u32)); + } + sock->flags |= GF_SOCK_HAS_SOURCE; + } +#endif + return GF_OK; +} + +//send length bytes of a buffer +GF_Err gf_sk_send(GF_Socket *sock, char *buffer, u32 length) +{ + GF_Err e; + u32 Count; + s32 Res; +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + + e = GF_OK; + + //the socket must be bound or connected + if (!sock || !sock->socket) return GF_BAD_PARAM; + +#ifndef __SYMBIAN32__ + //can we write? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = 0; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + ready = select(sock->socket+1, NULL, &Group, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + //should never happen (to check: is writeability is guaranteed for not-connected sockets) + if (!ready || !FD_ISSET(sock->socket, &Group)) { + return GF_IP_NETWORK_EMPTY; + } +#endif + + //direct writing + Count = 0; + while (Count < length) { + if (sock->flags & GF_SOCK_IS_TCP) { + Res = send(sock->socket, (char *) &buffer[Count], length - Count, 0); + } else { + Res = sendto(sock->socket, (char *) &buffer[Count], length - Count, 0, (struct sockaddr *) &sock->dest_addr, sock->dest_addr_len); + } + if (Res == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; +#ifndef __SYMBIAN32__ + case ENOTCONN: + case ECONNRESET: + return GF_IP_CONNECTION_CLOSED; +#endif + default: + return GF_IP_NETWORK_FAILURE; + } + } + Count += Res; + } + return GF_OK; +} + + +GF_EXPORT +u32 gf_sk_is_multicast_address(char *multi_IPAdd) +{ +#ifdef GPAC_HAS_IPV6 + u32 val; + struct addrinfo *res; + if (!multi_IPAdd) return 0; + res = gf_sk_get_ipv6_addr(multi_IPAdd, 0, AF_UNSPEC, AI_PASSIVE, SOCK_STREAM); + if (!res) return 0; + val = 0; + if (res->ai_addr->sa_family == AF_INET) { + val = IN_MULTICAST(ntohl(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr)); + } else if (res->ai_addr->sa_family == AF_INET6) { + val = IN6_IS_ADDR_MULTICAST(& ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr); + } + freeaddrinfo(res); + return val; +#else + if (!multi_IPAdd) return 0; + return ((htonl(inet_addr(multi_IPAdd)) >> 8) & 0x00f00000) == 0x00e00000; +#endif +} + +GF_Err gf_sk_setup_multicast(GF_Socket *sock, char *multi_IPAdd, u16 MultiPortNumber, u32 TTL, Bool NoBind, char *local_interface_ip) +{ + s32 ret; + u32 flag; + struct ip_mreq M_req; + u32 optval; +#ifdef GPAC_HAS_IPV6 + struct sockaddr *addr; + struct addrinfo *res, *aip; + u32 type; +#else + u_long local_add_id; +#endif + + if (!sock || sock->socket) return GF_BAD_PARAM; + + if (TTL > 255) TTL = 255; + + /*check the address*/ + if (!gf_sk_is_multicast_address(multi_IPAdd)) return GF_BAD_PARAM; + +#ifdef GPAC_HAS_IPV6 + type = (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM; + res = gf_sk_get_ipv6_addr(local_interface_ip, MultiPortNumber, AF_UNSPEC, AI_PASSIVE, type); + if (!res) { + if (local_interface_ip) { + res = gf_sk_get_ipv6_addr(NULL, MultiPortNumber, AF_UNSPEC, AI_PASSIVE, type); + local_interface_ip = NULL; + } + if (!res) return GF_IP_CONNECTION_FAILURE; + } + + /*for all interfaces*/ + for (aip=res; aip!=NULL; aip=aip->ai_next) { + if (type != (u32) aip->ai_socktype) continue; + sock->socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (sock->socket == INVALID_SOCKET) { + sock->socket = (SOCKET)NULL; + continue; + } + /*enable address reuse*/ + optval = SO_REUSEADDR; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &optval, sizeof(optval)); + + /*TODO: copy over other properties (recption buffer size & co)*/ + if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); + + memcpy(&sock->dest_addr, aip->ai_addr, aip->ai_addrlen); + sock->dest_addr_len = aip->ai_addrlen; + + if (!NoBind) { + ret = bind(sock->socket, aip->ai_addr, aip->ai_addrlen); + if (ret == SOCKET_ERROR) { + closesocket(sock->socket); + sock->socket = (SOCKET)NULL; + continue; + } + } + if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; + else sock->flags &= ~GF_SOCK_IS_IPV6; + break; + } + freeaddrinfo(res); + if (!sock->socket) return GF_IP_CONNECTION_FAILURE; + + if (!gf_sk_ipv6_set_remote_address(sock, multi_IPAdd, MultiPortNumber)) + return GF_IP_CONNECTION_FAILURE; + + addr = (struct sockaddr *)&sock->dest_addr; + if (addr->sa_family == AF_INET) { + M_req.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + M_req.imr_interface.s_addr = INADDR_ANY; + ret = setsockopt(sock->socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &M_req, sizeof(M_req)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + /*set TTL*/ + ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &TTL, sizeof(TTL)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + /*Disable loopback*/ + flag = 1; + ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &flag, sizeof(flag)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + } + if (addr->sa_family == AF_INET6) { + struct ipv6_mreq M_reqV6; + memcpy(&M_reqV6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); + M_reqV6.ipv6mr_interface = 0; + ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *) &M_reqV6, sizeof(M_reqV6)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + /*set TTL*/ + ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &TTL, sizeof(TTL)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + /*Disable loopback*/ + flag = 1; + ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &flag, sizeof(flag)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + } +#else + + /*enable address reuse*/ + optval = SO_REUSEADDR; + setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, SSO_CAST &optval, sizeof(optval)); + + if (local_interface_ip) local_add_id = inet_addr(local_interface_ip); + else local_add_id = htonl(INADDR_ANY); + + if (!NoBind) { + struct sockaddr_in local_address; + + local_address.sin_family = AF_INET; + local_address.sin_addr.s_addr = local_add_id; + local_address.sin_port = htons( MultiPortNumber); + + ret = bind(sock->socket, (struct sockaddr *) &local_address, sizeof(local_address)); + if (ret == SOCKET_ERROR) { + /*retry without specifying the local add*/ + local_address.sin_addr.s_addr = local_add_id = htonl(INADDR_ANY); + local_interface_ip = NULL; + ret = bind(sock->socket, (struct sockaddr *) &local_address, sizeof(local_address)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + } + /*setup local interface*/ + if (local_interface_ip) { + ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_IF, (char *) &local_add_id, sizeof(local_add_id)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + } + } + /*now join the multicast*/ + M_req.imr_multiaddr.s_addr = inet_addr(multi_IPAdd); + M_req.imr_interface.s_addr = local_add_id; + + ret = setsockopt(sock->socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &M_req, sizeof(M_req)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + /*set the Time To Live*/ + ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&TTL, sizeof(TTL)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + /*Disable loopback*/ + flag = 1; + ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &flag, sizeof(flag)); + if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; + + sock->dest_addr.sin_family = AF_INET; + sock->dest_addr.sin_addr.s_addr = M_req.imr_multiaddr.s_addr; + sock->dest_addr.sin_port = htons( MultiPortNumber); +#endif + sock->flags |= GF_SOCK_IS_MULTICAST; + return GF_OK; +} + + + + +//fetch nb bytes on a socket and fill the buffer from startFrom +//length is the allocated size of the receiving buffer +//BytesRead is the number of bytes read from the network +GF_Err gf_sk_receive(GF_Socket *sock, char *buffer, u32 length, u32 startFrom, u32 *BytesRead) +{ + GF_Err e; + s32 res; +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + + e = GF_OK; + + *BytesRead = 0; + if (!sock->socket) return GF_BAD_PARAM; + if (startFrom >= length) return GF_IO_ERR; + +#ifndef __SYMBIAN32__ + //can we read? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = 0; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + res = 0; + ready = select(sock->socket+1, &Group, NULL, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[socket] cannot select (error %d)\n", LASTSOCKERROR)); + return GF_IP_NETWORK_FAILURE; + } + } + if (!FD_ISSET(sock->socket, &Group)) { + return GF_IP_NETWORK_EMPTY; + } +#endif + + if (sock->flags & GF_SOCK_HAS_SOURCE) + res = recvfrom(sock->socket, (char *) buffer + startFrom, length - startFrom, 0, (struct sockaddr *)&sock->dest_addr, &sock->dest_addr_len); + else + res = recv(sock->socket, (char *) buffer + startFrom, length - startFrom, 0); + + if (res == SOCKET_ERROR) { + res = LASTSOCKERROR; + switch (res) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; +#ifndef __SYMBIAN32__ + case EMSGSIZE: + return GF_OUT_OF_MEM; + case ENOTCONN: + case ECONNRESET: + case ECONNABORTED: + return GF_IP_CONNECTION_CLOSED; +#endif + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[socket] cannot read from network (%d)\n", LASTSOCKERROR)); + return GF_IP_NETWORK_FAILURE; + } + } + *BytesRead = res; + return GF_OK; +} + + +GF_Err gf_sk_listen(GF_Socket *sock, u32 MaxConnection) +{ + s32 i; + if (!sock || !sock->socket) return GF_BAD_PARAM; + if (MaxConnection >= SOMAXCONN) MaxConnection = SOMAXCONN; + i = listen(sock->socket, MaxConnection); + if (i == SOCKET_ERROR) return GF_IP_NETWORK_FAILURE; + sock->flags |= GF_SOCK_IS_LISTENING; + return GF_OK; +} + +GF_Err gf_sk_accept(GF_Socket *sock, GF_Socket **newConnection) +{ + u32 client_address_size; + SOCKET sk; +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif +#ifndef __SYMBIAN32__ + u32 res; + #endif + *newConnection = NULL; + if (!sock || !(sock->flags & GF_SOCK_IS_LISTENING) ) return GF_BAD_PARAM; + +#ifndef __SYMBIAN32__ + //can we read? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = 0; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + res = 0; + ready = select(sock->socket, &Group, NULL, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + if (!ready || !FD_ISSET(sock->socket, &Group)) return GF_IP_NETWORK_EMPTY; +#endif + +#ifdef GPAC_HAS_IPV6 + client_address_size = sizeof(struct sockaddr_in6); +#else + client_address_size = sizeof(struct sockaddr_in); +#endif + sk = accept(sock->socket, (struct sockaddr *) &sock->dest_addr, &client_address_size); + + //we either have an error or we have no connections + if (sk == INVALID_SOCKET) { +// if (sock->flags & GF_SOCK_NON_BLOCKING) return GF_IP_NETWORK_FAILURE; + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + + (*newConnection) = (GF_Socket *) malloc(sizeof(GF_Socket)); + (*newConnection)->socket = sk; + (*newConnection)->flags = sock->flags & ~GF_SOCK_IS_LISTENING; +#ifdef GPAC_HAS_IPV6 + memcpy( &(*newConnection)->dest_addr, &sock->dest_addr, client_address_size); + memset(&sock->dest_addr, 0, sizeof(struct sockaddr_in6)); +#else + memcpy( &(*newConnection)->dest_addr, &sock->dest_addr, client_address_size); + memset(&sock->dest_addr, 0, sizeof(struct sockaddr_in)); +#endif + (*newConnection)->dest_addr_len = client_address_size; + return GF_OK; +} + +GF_Err gf_sk_get_local_info(GF_Socket *sock, u16 *Port, u32 *Familly) +{ +#ifdef GPAC_HAS_IPV6 + struct sockaddr_in6 the_add; +#else + struct sockaddr_in the_add; +#endif + u32 size; + + if (!sock || !sock->socket) return GF_BAD_PARAM; + + if (Port) { +#ifdef GPAC_HAS_IPV6 + size = sizeof(struct sockaddr_in6); + if (getsockname(sock->socket, (struct sockaddr *) &the_add, &size) == SOCKET_ERROR) return GF_IP_NETWORK_FAILURE; + *Port = (u32) ntohs(the_add.sin6_port); +#else + size = sizeof(struct sockaddr_in); + if (getsockname(sock->socket, (struct sockaddr *) &the_add, &size) == SOCKET_ERROR) return GF_IP_NETWORK_FAILURE; + *Port = (u32) ntohs(the_add.sin_port); +#endif + } + if (Familly) { +/* size = 4; + if (getsockopt(sock->socket, SOL_SOCKET, SO_TYPE, (char *) &fam, &size) == SOCKET_ERROR) + return GF_IP_NETWORK_FAILURE; + *Familly = fam; +*/ + if (sock->flags & GF_SOCK_IS_TCP) *Familly = GF_SOCK_TYPE_TCP; + else *Familly = GF_SOCK_TYPE_UDP; + } + return GF_OK; +} + +//we have to do this for the server sockets as we use only one thread +GF_Err gf_sk_server_mode(GF_Socket *sock, Bool serverOn) +{ + u32 one; + + if (!sock || !(sock->flags & GF_SOCK_IS_TCP) || !sock->socket) + return GF_BAD_PARAM; + + one = serverOn ? 1 : 0; + setsockopt(sock->socket, IPPROTO_TCP, TCP_NODELAY, SSO_CAST &one, sizeof(u32)); +#ifndef __SYMBIAN32__ + setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(u32)); +#endif + return GF_OK; +} + +GF_Err gf_sk_get_remote_address(GF_Socket *sock, char *buf) +{ +#ifdef GPAC_HAS_IPV6 + char clienthost[NI_MAXHOST]; + struct sockaddr_in6 * addrptr = (struct sockaddr_in6 *)(&sock->dest_addr_len); + if (!sock || sock->socket) return GF_BAD_PARAM; + if (getnameinfo((struct sockaddr *)addrptr, sock->dest_addr_len, clienthost, sizeof(clienthost), NULL, 0, NI_NUMERICHOST)) + return GF_IP_ADDRESS_NOT_FOUND; + strcpy(buf, clienthost); +#else + if (!sock || !sock->socket) return GF_BAD_PARAM; + strcpy(buf, inet_ntoa(sock->dest_addr.sin_addr)); +#endif + return GF_OK; +} + + + + +//send length bytes of a buffer +GF_Err gf_sk_send_to(GF_Socket *sock, char *buffer, u32 length, char *remoteHost, u16 remotePort) +{ + u32 Count, remote_add_len; + s32 Res; +#ifdef GPAC_HAS_IPV6 + struct sockaddr_storage remote_add; +#else + struct sockaddr_in remote_add; + struct hostent *Host; +#endif +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + + //the socket must be bound or connected + if (!sock || !sock->socket) return GF_BAD_PARAM; + if (remoteHost && !remotePort) return GF_BAD_PARAM; + +#ifndef __SYMBIAN32__ + //can we write? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = 0; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + ready = select(sock->socket+1, NULL, &Group, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + if (!ready || !FD_ISSET(sock->socket, &Group)) return GF_IP_NETWORK_EMPTY; +#endif + + + /*setup the address*/ +#ifdef GPAC_HAS_IPV6 + remote_add.ss_family = AF_INET6; + //if a remote host is specified, use it. Otherwise use the default host + if (remoteHost) { + //setup the address + struct addrinfo *res = gf_sk_get_ipv6_addr(remoteHost, remotePort, AF_UNSPEC, 0, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM); + if (!res) return GF_IP_ADDRESS_NOT_FOUND; + memcpy(&remote_add, res->ai_addr, res->ai_addrlen); + remote_add_len = res->ai_addrlen; + freeaddrinfo(res); + } else { + struct sockaddr_in6 *remotePtr = (struct sockaddr_in6 *)&remote_add; + struct sockaddr_in6 * addrptr = (struct sockaddr_in6 *)(&sock->dest_addr); + remotePtr->sin6_port = addrptr->sin6_port; + remotePtr->sin6_addr = addrptr->sin6_addr; + remote_add_len = sock->dest_addr_len; + } +#else + remote_add_len = sizeof(struct sockaddr_in); + remote_add.sin_family = AF_INET; + //if a remote host is specified, use it. Otherwise use the default host + if (remoteHost) { + //setup the address + remote_add.sin_port = htons(remotePort); + //get the server IP + Host = gethostbyname(remoteHost); + if (Host == NULL) return GF_IP_ADDRESS_NOT_FOUND; + memcpy((char *) &remote_add.sin_addr, Host->h_addr_list[0], sizeof(u32)); + } else { + remote_add.sin_port = sock->dest_addr.sin_port; + remote_add.sin_addr.s_addr = sock->dest_addr.sin_addr.s_addr; + } +#endif + Count = 0; + while (Count < length) { + Res = sendto(sock->socket, (char *) &buffer[Count], length - Count, 0, (struct sockaddr *) &remote_add, remote_add_len); + if (Res == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + Count += Res; + } + return GF_OK; +} + + + + +GF_Err gf_sk_receive_wait(GF_Socket *sock, char *buffer, u32 length, u32 startFrom, u32 *BytesRead, u32 Second ) +{ + GF_Err e; + s32 res; +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + e = GF_OK; + + *BytesRead = 0; + if (startFrom >= length) return GF_OK; + + +#ifndef __SYMBIAN32__ + //can we read? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = Second; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + res = 0; + ready = select(sock->socket+1, &Group, NULL, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + if (!FD_ISSET(sock->socket, &Group)) { + return GF_IP_NETWORK_EMPTY; + } +#endif + + + res = recv(sock->socket, (char *) buffer + startFrom, length - startFrom, 0); + if (res == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + *BytesRead = res; + return GF_OK; +} + + +//send length bytes of a buffer +GF_Err gf_sk_send_wait(GF_Socket *sock, char *buffer, u32 length, u32 Second ) +{ + + GF_Err e; + u32 Count; + s32 Res; +#ifndef __SYMBIAN32__ + u32 ready; + struct timeval timeout; + fd_set Group; +#endif + e = GF_OK; + + //the socket must be bound or connected + if (!sock || !sock->socket) return GF_BAD_PARAM; + +#ifndef __SYMBIAN32__ + //can we write? + FD_ZERO(&Group); + FD_SET(sock->socket, &Group); + timeout.tv_sec = Second; + timeout.tv_usec = SOCK_MICROSEC_WAIT; + + ready = select(sock->socket+1, NULL, &Group, NULL, &timeout); + if (ready == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; + default: + return GF_IP_NETWORK_FAILURE; + } + } + //should never happen (to check: is writeability is guaranteed for not-connected sockets) + if (!ready || !FD_ISSET(sock->socket, &Group)) { + return GF_IP_NETWORK_EMPTY; + } +#endif + + //direct writing + Count = 0; + while (Count < length) { + Res = send(sock->socket, (char *) &buffer[Count], length - Count, 0); + if (Res == SOCKET_ERROR) { + switch (LASTSOCKERROR) { + case EAGAIN: + return GF_IP_SOCK_WOULD_BLOCK; +#ifndef __SYMBIAN32__ + case ECONNRESET: + return GF_IP_CONNECTION_CLOSED; +#endif + default: + return GF_IP_NETWORK_FAILURE; + } + } + Count += Res; + } + return GF_OK; +} + + +#if 0 + +//Socket Group for select(). The group is a collection of sockets ready for reading / writing +typedef struct __tag_sock_group +{ + //the max time value before a select returns + struct timeval timeout; + fd_set ReadGroup; + fd_set WriteGroup; +} GF_SocketGroup; + + +#define GF_SOCK_GROUP_READ 0 +#define GF_SOCK_GROUP_WRITE 1 + +GF_SocketGroup *NewSockGroup() +{ + GF_SocketGroup *tmp = (GF_SocketGroup*)malloc(sizeof(GF_SocketGroup)); + if (!tmp) return NULL; + FD_ZERO(&tmp->ReadGroup); + FD_ZERO(&tmp->WriteGroup); + return tmp; +} + +void SKG_Delete(GF_SocketGroup *group) +{ + free(group); +} + +void SKG_SetWatchTime(GF_SocketGroup *group, u32 DelayInS, u32 DelayInMicroS) +{ + group->timeout.tv_sec = DelayInS; + group->timeout.tv_usec = DelayInMicroS; +} + +void SKG_AddSocket(GF_SocketGroup *group, GF_Socket *sock, u32 GroupType) +{ + + switch (GroupType) { + case GF_SOCK_GROUP_READ: + FD_SET(sock->socket, &group->ReadGroup); + return; + case GF_SOCK_GROUP_WRITE: + FD_SET(sock->socket, &group->WriteGroup); + return; + default: + return; + } +} + +void SKG_RemoveSocket(GF_SocketGroup *group, GF_Socket *sock, u32 GroupType) +{ + switch (GroupType) { + case GF_SOCK_GROUP_READ: + FD_CLR(sock->socket, &group->ReadGroup); + return; + case GF_SOCK_GROUP_WRITE: + FD_CLR(sock->socket, &group->WriteGroup); + return; + default: + return; + } +} + + +Bool SKG_IsSocketIN(GF_SocketGroup *group, GF_Socket *sock, u32 GroupType) +{ + + switch (GroupType) { + case GF_SOCK_GROUP_READ: + if (FD_ISSET(sock->socket, &group->ReadGroup)) return 1; + return 0; + case GF_SOCK_GROUP_WRITE: + if (FD_ISSET(sock->socket, &group->WriteGroup)) return 1; + return 0; + default: + return 0; + } +} + +u32 SKG_Select(GF_SocketGroup *group) +{ + u32 ready, rien = 0; + ready = select(rien, &group->ReadGroup, &group->WriteGroup, NULL, &group->timeout); + if (ready == SOCKET_ERROR) return 0; + return ready; +} + +#endif diff --git a/src/utils/symbian_os.cpp b/src/utils/symbian_os.cpp new file mode 100644 index 0000000..dda6dca --- /dev/null +++ b/src/utils/symbian_os.cpp @@ -0,0 +1,839 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#ifdef __SYMBIAN32__ + +#include +#include +#include +#include +#include +#include +#include +/*symbian stdlib*/ +#include +/*symbian core (for scheduler & trap cleanup)*/ +#include +/*hardware abstraction layer*/ +#include + +/*gpac module internals*/ +#include "module_wrap.h" +#include + + +// Exported Functions (DLL entry point) +#ifndef EKA2 // for EKA1 only +GLDEF_C TInt E32Dll(TDllReason /*aReason*/) +// Called when the DLL is loaded and unloaded. Note: have to define +// epoccalldllentrypoints in MMP file to get this called in THUMB. +{ + return KErrNone; +} + +#endif + + + +#define SLEEP_ABS_SELECT 1 + +static u32 sys_start_time = 0; + +GF_EXPORT +u32 gf_sys_clock() +{ + struct timeval now; + gettimeofday(&now, NULL); + return ( (now.tv_sec)*1000 + (now.tv_usec) / 1000) - sys_start_time; +} + + +GF_EXPORT +void gf_sleep(u32 ms) +{ + TTimeIntervalMicroSeconds32 inter; + inter = (TInt) (1000*ms); +#ifdef __SERIES60_3X__ + User::AfterHighRes(inter); +#else + User::After(inter); +#endif + +#if 0 + TInt error; + CActiveScheduler::RunIfReady(error, CActive::EPriorityIdle); + + RTimer timer; + TRequestStatus timerStatus; + timer.CreateLocal(); + + timer.After(timerStatus,ms*1000); + User::WaitForRequest(timerStatus); +#endif +} + +GF_EXPORT +void gf_delete_file(char *fileName) +{ + remove(fileName); +} + + +GF_EXPORT +void gf_rand_init(Bool Reset) +{ + if (Reset) { + srand(1); + } else { + srand( (u32) time(NULL) ); + } +} + +GF_EXPORT +u32 gf_rand() +{ + return rand(); +} + + +#ifndef GPAC_READ_ONLY +GF_EXPORT +FILE *gf_temp_file_new() +{ + return tmpfile(); +} +#endif + + +GF_EXPORT +void gf_utc_time_since_1970(u32 *sec, u32 *msec) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + *sec = tv.tv_sec; + *msec = tv.tv_usec/1000; +} + +GF_EXPORT +void gf_get_user_name(char *buf, u32 buf_size) +{ + strcpy(buf, "mpeg4-user"); + +#if 0 + s32 len; + char *t; + strcpy(buf, ""); + len = 1024; + GetUserName(buf, &len); + if (!len) { + t = getenv("USER"); + if (t) strcpy(buf, t); + } +#endif +#if 0 + struct passwd *pw; + pw = getpwuid(getuid()); + strcpy(buf, ""); + if (pw && pw->pw_name) strcpy(name, pw->pw_name); +#endif +} + +GF_EXPORT +char * my_str_upr(char *str) +{ + u32 i; + for (i=0; id_name, "..")) goto next; + if (the_file->d_name[0] == '.') goto next; + + if (filter) { + char ext[30]; + char *sep = strrchr(the_file->d_name, '.'); + if (!sep) goto next; + strcpy(ext, sep+1); + strlwr(ext); + if (!strstr(filter, sep+1)) goto next; + } + + strcpy((char*)item_path, (const char*)path); + strcat((char*)item_path, the_file->d_name); + if (stat( (const char*)item_path, &st ) != 0) goto next; + if (enum_directory && ( (st.st_mode & S_IFMT) != S_IFDIR)) goto next; + if (!enum_directory && ((st.st_mode & S_IFMT) == S_IFDIR)) goto next; + file = (unsigned char*)the_file->d_name; + + if (enum_dir_fct(cbck, (char *)file, (char *)item_path)) { + break; + } + +next: + the_file = readdir(the_dir); + } + return GF_OK; +} + + +GF_EXPORT +u64 gf_f64_tell(FILE *fp) +{ + return (u64) ftell(fp); +} + +GF_EXPORT +u64 gf_f64_seek(FILE *fp, s64 offset, s32 whence) +{ + return fseek(fp, (s32) offset, whence); +} + +GF_EXPORT +FILE *gf_f64_open(const char *file_name, const char *mode) +{ + return fopen(file_name, mode); +} + + +/*symbian thread*/ +typedef RThread* TH_HANDLE; + +/********************************************************************* + OS-Specific Thread Object +**********************************************************************/ +struct __tag_thread +{ + + u32 status; + TH_HANDLE threadH; + u32 stackSize; + /* the thread procedure */ + u32 (*Run)(void *param); + void *args; +#ifndef GPAC_DISABLE_LOG + char *log_name; +#endif + /* lock for signal */ + GF_Semaphore *_signal; +}; + +GF_EXPORT +GF_Thread *gf_th_new(const char *name) +{ + GF_Thread *tmp = (GF_Thread *) malloc(sizeof(GF_Thread)); + memset((void *)tmp, 0, sizeof(GF_Thread)); + tmp->status = GF_THREAD_STATUS_STOP; +#ifndef GPAC_DISABLE_LOG + if (name) { + tmp->log_name = strdup(name); + } else { + char szN[20]; + sprintf(szN, "0x%08x", (u32) tmp); + tmp->log_name = strdup(szN); + } +#endif + + return tmp; +} + +static void *RunThread(void *ptr) +{ + TInt err; + u32 ret = 0; + CTrapCleanup * cleanup = NULL; + GF_Thread *t = (GF_Thread *)ptr; + + + CActiveScheduler * scheduler = new CActiveScheduler(); + if (scheduler == NULL) { + t->status = GF_THREAD_STATUS_DEAD; + gf_sema_notify(t->_signal, 1); + goto exit; + } +#ifndef GPAC_DISABLE_LOG + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Thread %s] Installing ActiveScheduler\n", t->log_name)); +#endif + CActiveScheduler::Install(scheduler); + +#ifndef GPAC_DISABLE_LOG + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Thread %s] Creating cleanup trap\n", t->log_name)); +#endif + cleanup = CTrapCleanup::New(); + if( cleanup == NULL ) { + t->status = GF_THREAD_STATUS_DEAD; + gf_sema_notify(t->_signal, 1); + delete CActiveScheduler::Current(); + goto exit; + } +#if 0 + CActiveScheduler::Start(); +#endif + /* OK , signal the caller */ + t->status = GF_THREAD_STATUS_RUN; + gf_sema_notify(t->_signal, 1); + /* Run our thread */ +#ifndef GPAC_DISABLE_LOG + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Thread %s] Entering thread proc\n", t->log_name)); +#endif + TRAP(err, ret=t->Run(t->args) ); + //ret = t->Run(t->args); + + delete CActiveScheduler::Current(); + delete cleanup; + +exit: +#ifndef GPAC_DISABLE_LOG + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Thread %s] Exit\n", t->log_name )); +#endif + t->status = GF_THREAD_STATUS_DEAD; + t->Run = NULL; + t->threadH->Close(); + t->threadH = NULL; + return (void *)ret; +} + +GF_EXPORT +GF_Err gf_th_run(GF_Thread *t, u32 (*Run)(void *param), void *param) +{ + TBuf<32> threadName; + + const TUint KThreadMinHeapSize = 0x1000; + const TUint KThreadMaxHeapSize = 0x10000; + + if (!t || t->Run || t->_signal) return GF_BAD_PARAM; + t->Run = Run; + t->args = param; + t->_signal = gf_sema_new(1, 0); + if (! t->_signal) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Unable to create thread start-up semaphore\n")); + t->status = GF_THREAD_STATUS_DEAD; + return GF_IO_ERR; + } + + threadName.Format(_L("GTH%d"), (u32) t); + t->threadH = new RThread(); + if ( t->threadH->Create(threadName, (TThreadFunction)RunThread, KDefaultStackSize, KThreadMinHeapSize, KThreadMaxHeapSize, (void *)t, EOwnerProcess) != KErrNone){ + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Unable to create thread\n")); + t->status = GF_THREAD_STATUS_DEAD; + return GF_IO_ERR; + } + t->threadH->Resume(); + + /*wait for the child function to call us - do NOT return before, otherwise the thread status would + be unknown*/ + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Waiting for thread to start\n")); + gf_sema_wait(t->_signal); + gf_sema_del(t->_signal); + t->_signal = NULL; + return GF_OK; +} + + +/* Stops a thread. If Destroy is not 0, thread is destroyed DANGEROUS as no cleanup */ +static void Thread_Stop(GF_Thread *t, Bool Destroy) +{ + if (gf_th_status(t) == GF_THREAD_STATUS_RUN) { + if (Destroy){ + t->threadH->Terminate(0); + t->threadH = NULL; + } + else{ + t->threadH->Suspend(); + } + } + t->status = GF_THREAD_STATUS_DEAD; +} + +GF_EXPORT +void gf_th_stop(GF_Thread *t) +{ + Thread_Stop(t, 0); +} + +GF_EXPORT +void gf_th_del(GF_Thread *t) +{ + Thread_Stop(t, 0); +#ifndef GPAC_DISABLE_LOG + free(t->log_name); +#endif + free(t); +} + + +GF_EXPORT +void gf_th_set_priority(GF_Thread *t, s32 priority) +{ + /*FIXEME: tune priorities on symbian*/ +#if 0 + if (priority > 200) + t->threadH->SetPriority(EPriorityRealTime); + else + t->threadH->SetPriority(EPriorityNormal); +#endif +} + +GF_EXPORT +u32 gf_th_status(GF_Thread *t) +{ + if (!t) return 0; + return t->status; +} + + +GF_EXPORT +u32 gf_th_id() +{ + return RThread().Id(); +} + + + +/********************************************************************* + OS-Specific Mutex Object +**********************************************************************/ +struct __tag_mutex +{ + RMutex *hMutex; + /* We filter recursive calls (1 thread calling Lock several times in a row only locks + ONCE the mutex. Holder is the current ThreadID of the mutex holder*/ + u32 Holder, HolderCount; +#ifndef GPAC_DISABLE_LOG + char *log_name; +#endif +}; + + +GF_EXPORT +GF_Mutex *gf_mx_new(const char *name) +{ + GF_Mutex *tmp = (GF_Mutex *)malloc(sizeof(GF_Mutex)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_Mutex)); + + tmp->hMutex = new RMutex(); + if( tmp->hMutex->CreateLocal() != KErrNone){ + free(tmp); + return NULL; + } +#ifndef GPAC_DISABLE_LOG + if (name) { + tmp->log_name = strdup(name); + } else { + char szN[20]; + sprintf(szN, "0x%08x", (u32) tmp); + tmp->log_name = strdup(szN); + } +#endif + return tmp; +} + +GF_EXPORT +void gf_mx_del(GF_Mutex *mx) +{ + mx->hMutex->Close(); + free(mx); +} + +GF_EXPORT +void gf_mx_v(GF_Mutex *mx) +{ + u32 caller; + if (!mx) return; + caller = gf_th_id(); + + /*only if we own*/ + if (caller != mx->Holder) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Invalid mutex release - owner PID %d - caller PID %d\n", mx->Holder, caller)); + return; + } + if (!mx->HolderCount) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Invalid mutex release - mutex not locked\n")); + return; + } + mx->HolderCount -= 1; + + if (mx->HolderCount == 0) { + mx->Holder = 0; + mx->hMutex->Signal(); + } +} + +GF_EXPORT +u32 gf_mx_p(GF_Mutex *mx) +{ + u32 caller; + if (!mx) return 0; + caller = gf_th_id(); + if (caller == mx->Holder) { + mx->HolderCount += 1; + return 1; + } + mx->hMutex->Wait(); + mx->Holder = caller; + mx->HolderCount = 1; + return 1; +} + +GF_EXPORT +Bool gf_mx_try_lock(GF_Mutex *mx) +{ + u32 caller; + if (!mx) return 0; + caller = gf_th_id(); + if (caller == mx->Holder) { + mx->HolderCount += 1; + return 1; + } + /*FIXME !! WE MUST HAVE tryLock*/ + gf_mx_p(mx); + return 1; +} + + + + +/********************************************************************* + OS-Specific Semaphore Object +**********************************************************************/ +struct __tag_semaphore +{ + RSemaphore *hSemaphore; +}; + + +GF_EXPORT +GF_Semaphore *gf_sema_new(u32 MaxCount, u32 InitCount) +{ + GF_Semaphore *tmp = (GF_Semaphore *) malloc(sizeof(GF_Semaphore)); + + if (!tmp) return NULL; + tmp->hSemaphore = new RSemaphore(); + if (!tmp->hSemaphore) { + free(tmp); + return NULL; + } + TBuf<32> semaName; + semaName.Format(_L("GPAC_SEM%d"), (u32) tmp); + tmp->hSemaphore->CreateGlobal(semaName, InitCount); + + return tmp; +} + +GF_EXPORT +void gf_sema_del(GF_Semaphore *sm) +{ + sm->hSemaphore->Close(); + free(sm); +} + +GF_EXPORT +u32 gf_sema_notify(GF_Semaphore *sm, u32 NbRelease) +{ + u32 prevCount; + if (!sm) return 0; + sm->hSemaphore->Signal(NbRelease); +#ifdef __SERIES60_3X__ + prevCount = 0; +#else + prevCount = sm->hSemaphore->Count(); +#endif + return (u32) prevCount; +} + +GF_EXPORT +void gf_sema_wait(GF_Semaphore *sm) +{ + sm->hSemaphore->Wait(); +} + +GF_EXPORT +Bool gf_sema_wait_for(GF_Semaphore *sm, u32 TimeOut) +{ + return 0; +} + + + + + +static u32 sys_init = 0; +GF_SystemRTInfo the_rti; + +GF_EXPORT +void gf_sys_init() +{ + if (!sys_init) { + memset(&the_rti, 0, sizeof(GF_SystemRTInfo)); + the_rti.pid = getpid(); + sys_start_time = gf_sys_clock(); + } + sys_init += 1; +} + +GF_EXPORT +void gf_sys_close() +{ + if (sys_init > 0) { + sys_init --; + if (sys_init) return; + } +} + +#if GPAC_MEMORY_TRACKING +extern size_t gpac_allocated_memory; +#endif + +/*CPU and Memory Usage*/ +GF_EXPORT +Bool gf_sys_get_rti(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) +{ + TInt ram, ram_free; + u32 now, time; +#ifdef __SERIES60_3X__ + TModuleMemoryInfo mi; +#endif + TTimeIntervalMicroSeconds tims; + RProcess cur_process; + RThread cur_th; + + now = gf_sys_clock(); + if (!rti->sampling_instant) { + rti->sampling_instant = now; + if (cur_th.GetCpuTime(tims) != KErrNone) { + return 0; + } +#ifdef __SERIES60_3X__ + rti->process_cpu_time = (u32) (tims.Int64() / 1000); +#else + rti->process_cpu_time = (u32) ( TInt64(tims.Int64() / 1000).GetTInt() ); +#endif + return 0; + } + if (rti->sampling_instant + refresh_time_ms > now) return 0; + rti->sampling_period_duration = now - rti->sampling_instant; + rti->sampling_instant = now; + + if (cur_th.Process(cur_process) != KErrNone) { + return 0; + } +#ifdef __SERIES60_3X__ + if (cur_process.GetMemoryInfo(mi) != KErrNone) { + return 0; + } + rti->process_memory = mi.iCodeSize + mi.iConstDataSize + mi.iInitialisedDataSize + mi.iUninitialisedDataSize; +#endif + if (cur_th.GetCpuTime(tims) != KErrNone) { + return 0; + } +#ifdef __SERIES60_3X__ + time = (u32) (tims.Int64() / 1000); +#else + time = (u32) ( TInt64(tims.Int64() / 1000).GetTInt() ); +#endif + rti->process_cpu_time_diff = time - rti->process_cpu_time; + rti->process_cpu_time = time; + rti->process_cpu_usage = 100*rti->process_cpu_time_diff / rti->sampling_period_duration; + if (rti->process_cpu_usage > 100) rti->process_cpu_usage = 100; + + HAL::Get(HALData::EMemoryRAM, ram); + HAL::Get(HALData::EMemoryRAMFree, ram_free); + rti->physical_memory = ram; + rti->physical_memory_avail = ram_free; +#if GPAC_MEMORY_TRACKING + rti->gpac_memory = gpac_allocated_memory; +#endif + return 1; +} + +GF_EXPORT +Bool gf_sys_get_battery_state(Bool *onBattery, u32 *state, u32*level) +{ + return 1; +} + + +/*delete all interfaces loaded on object*/ +void gf_modules_free_module(ModuleInstance *inst) +{ + void *objinterface; + while (gf_list_count(inst->interfaces)) { + objinterface = gf_list_get(inst->interfaces, 0); + gf_list_rem(inst->interfaces, 0); + inst->destroy_func(objinterface); + } + if (inst->lib_handle){ + RLibrary* pLibrary = (RLibrary *) inst->lib_handle; + pLibrary->Close(); + } + gf_list_del(inst->interfaces); + free(inst); +} + +Bool gf_modules_load_library(ModuleInstance *inst) +{ + const TUid KGPACModuleUid={0x10000080}; + char s_path[GF_MAX_PATH]; + HBufC *path; + TInt e; + + if (inst->lib_handle) return 1; + + sprintf(s_path, "%s%c%s", inst->plugman->dir, GF_PATH_SEPARATOR, inst->szName); + + path = HBufC::NewL( User::StringLength( ( TUint8* ) s_path) + 1 ); + path->Des().Copy( TPtrC8(( TText8* ) s_path) ); + + RLibrary* pLibrary = new RLibrary(); + e = pLibrary->Load(*path); + delete path; + + if (e != KErrNone) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Cannot load library %s: %d", s_path, e)); + delete pLibrary; + goto err_exit; + } + /*check UID 2 is GPAC's identifier*/ + if (pLibrary->Type()[1] != KGPACModuleUid) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Invalid library UID %x", (u32) pLibrary->Type()[1].iUid)); + pLibrary->Close(); + delete pLibrary; + goto err_exit; + } + inst->query_func = (QueryInterface) pLibrary->Lookup(1); + inst->load_func = (LoadInterface) pLibrary->Lookup(2); + inst->destroy_func = (ShutdownInterface) pLibrary->Lookup(3); + + if ((inst->query_func==NULL) || (inst->load_func==NULL) || (inst->destroy_func==NULL) ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Library %s has invalid interfaces", inst->szName)); + pLibrary->Close(); + delete pLibrary; + goto err_exit; + } + + //store library handle + inst->lib_handle = (void*) pLibrary; + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Module %s loaded\n", inst->szName)); + return 1; + +err_exit: + gf_cfg_set_key(inst->plugman->cfg, "SymbianDLLs", inst->szName, "no"); + return 0; +} + +void gf_modules_unload_library(ModuleInstance *inst) +{ + if (!inst->lib_handle || gf_list_count(inst->interfaces)) return; + + RLibrary* pLibrary = (RLibrary *) inst->lib_handle; + pLibrary->Close(); + delete pLibrary; + + inst->lib_handle = NULL; + inst->load_func = NULL; + inst->destroy_func = NULL; + inst->query_func = NULL; + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Module %s unloaded\n", inst->szName)); +} + + +static Bool enum_modules(void *cbck, char *item_name, char *item_path) +{ + ModuleInstance *inst; + + GF_ModuleManager *pm = (GF_ModuleManager *) cbck; + + if (strstr(item_name, "nposmozilla")) return 0; + if (strncmp(item_name, "gm_", 3)) return 0; + if (gf_module_is_loaded(pm, item_name) ) return 0; + + /*TODO FIXME: add module check for symbian */ + + GF_SAFEALLOC(inst, ModuleInstance); + inst->interfaces = gf_list_new(); + inst->plugman = pm; + strcpy((char*)inst->szName, item_name); + gf_list_add(pm->plug_list, inst); + return 0; +} + +/*refresh modules - note we don't check for deleted modules but since we've open them the OS should forbid delete*/ +GF_EXPORT +u32 gf_modules_refresh(GF_ModuleManager *pm) +{ + if (!pm) return 0; + //!! symbian 9.1 doesn't allow for DLL browsing in /sys/bin, and can only load DLLs in /sys/bin !! +#if 0 + gf_enum_directory((char*)pm->dir, 0, enum_modules, pm, ".dll"); +#else + u32 i, mod_count; + + mod_count = gf_cfg_get_key_count(pm->cfg, "SymbianDLLs"); + for (i=0; icfg, "SymbianDLLs", i); + if (stricmp(gf_cfg_get_key(pm->cfg, "SymbianDLLs", mod), "yes")) continue; + enum_modules(pm, (char*)mod, NULL); + } +#endif + return gf_list_count(pm->plug_list); +} + +#endif + diff --git a/src/utils/token.c b/src/utils/token.c new file mode 100644 index 0000000..18f3624 --- /dev/null +++ b/src/utils/token.c @@ -0,0 +1,132 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +static GFINLINE s32 gf_tok_is_char_in_set(char TestChar, char *TestSet) +{ + u32 i, Len; + Len = strlen(TestSet); + for (i=0; i= Size) return -1; + + offset = 2; + End = gf_token_find(Buffer, Start, Size, "\r\n"); + if (End<0) { + End = gf_token_find(Buffer, Start, Size, "\r"); + if (End<0) End = gf_token_find(Buffer, Start, Size, "\n"); + if (End < 0) return -1; + offset = 1; + } + + Total = End - Start + offset; + if ((u32) Total >= LineBufferSize) Total = LineBufferSize; + for (i=0; i= Size) return -1; + + Len = strlen(Pattern); + if ( Len <= 0 ) return -1; + if (Size - Start < (u32) Len) return -1; + + for (i=Start; i<= Size-Len; i++) { + flag = 0; + for (j=0; j< (u32) Len; j++) { + if (Buffer[i+j] != Pattern[j]) { + flag = 1; + break; + } + } + //found + if (!flag) return i; + } + return -1; +} + diff --git a/src/utils/uni_bidi.c b/src/utils/uni_bidi.c new file mode 100644 index 0000000..4cc5d68 --- /dev/null +++ b/src/utils/uni_bidi.c @@ -0,0 +1,1484 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + + +/*------------------------------------------------------------------------ + Bidirectional Character Types + + as defined by the Unicode Bidirectional Algorithm Table 3-7. + +------------------------------------------------------------------------*/ +enum +{ + // input types + // ON MUST be zero, code relies on ON = N = 0 + ON = 0, // Other Neutral + L, // Left Letter + R, // Right Letter + AN, // Arabic Number + EN, // European Number + AL, // Arabic Letter (Right-to-left) + NSM, // Non-spacing Mark + CS, // Common Separator + ES, // European Separator + ET, // European Terminator (post/prefix e.g. $ and %) + + // resolved types + BN, // Boundary neutral (type of RLE etc after explicit levels) + + // input types, + S, // Segment Separator (TAB) // used only in L1 + WS, // White space // used only in L1 + B, // Paragraph Separator (aka as PS) + + // types for explicit controls + RLO, // these are used only in X1-X9 + RLE, + LRO, + LRE, + PDF, + + // resolved types, also resolved directions + N = ON, // alias, where ON, WS and S are treated the same +}; + +/*---------------------------------------------------------------------- + The following array maps character codes to types for the purpose of + this sample implementation. The legend string gives a human readable + explanation of the pseudo alphabet. + + For simplicity, characters entered by buttons are given a 1:1 mapping + between their type and pseudo character value. Pseudo characters that + can be typed from the keyboard are explained in the legend string. + + Use the Unicode Character Database for the real values in real use. + ---------------------------------------------------------------------*/ + +#define LRM 4 +#define RLM 5 +#define LS 0x13 + +static int bidi_get_class(u32 val); + + + +GF_EXPORT +Bool gf_utf8_is_right_to_left(u16 *utf_string) +{ + u32 i = 0; + while (1) { + u32 c = utf_string[i]; + if (!c) return 0; + switch (bidi_get_class(c)) { + case L: return 0; + case R: return 1; + case AN: return 1; + case EN: return 0; + case AL: return 1; + default: break; + } + i++; + } + return 0; +} + + +/*a very VERY basic bidi reorderer */ +GF_EXPORT +Bool gf_utf8_reorder_bidi(u16 *utf_string, u32 len) +{ + u32 i, j, start, stop, cur_dir, slen, main_dir; + Bool is_start; + + /*get main direction of the layour*/ + Bool rev = gf_utf8_is_right_to_left(utf_string); + + if (rev) { + for (i=0; i=0x0041) && (val<=0x005A)) return L; //# L& [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z + if ((val>=0x0061) && (val<=0x007A)) return L; //# L& [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z + if (val==0x000000AA) return L; //# L& FEMININE ORDINAL INDICATOR + if (val==0x000000B5) return L; //# L& MICRO SIGN + if (val==0x000000BA) return L; //# L& MASCULINE ORDINAL INDICATOR + if ((val>=0x00C0) && (val<=0x00D6)) return L; //# L& [23] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER O WITH DIAERESIS + if ((val>=0x00D8) && (val<=0x00F6)) return L; //# L& [31] LATIN CAPITAL LETTER O WITH STROKE..LATIN SMALL LETTER O WITH DIAERESIS + if ((val>=0x00F8) && (val<=0x01BA)) return L; //# L& [195] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER EZH WITH TAIL + if (val==0x000001BB) return L; //# Lo LATIN LETTER TWO WITH STROKE + if ((val>=0x01BC) && (val<=0x01BF)) return L; //# L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN + if ((val>=0x01C0) && (val<=0x01C3)) return L; //# Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK + if ((val>=0x01C4) && (val<=0x0293)) return L; //# L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL + if (val==0x00000294) return L; //# Lo LATIN LETTER GLOTTAL STOP + if ((val>=0x0295) && (val<=0x02AF)) return L; //# L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL + if ((val>=0x02B0) && (val<=0x02B8)) return L; //# Lm [9] MODIFIER LETTER SMALL H..MODIFIER LETTER SMALL Y + if ((val>=0x02BB) && (val<=0x02C1)) return L; //# Lm [7] MODIFIER LETTER TURNED COMMA..MODIFIER LETTER REVERSED GLOTTAL STOP + if ((val>=0x02D0) && (val<=0x02D1)) return L; //# Lm [2] MODIFIER LETTER TRIANGULAR COLON..MODIFIER LETTER HALF TRIANGULAR COLON + if ((val>=0x02E0) && (val<=0x02E4)) return L; //# Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP + if (val==0x000002EE) return L; //# Lm MODIFIER LETTER DOUBLE APOSTROPHE + if (val==0x0000037A) return L; //# Lm GREEK YPOGEGRAMMENI + if ((val>=0x037B) && (val<=0x037D)) return L; //# L& [3] GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL + if (val==0x00000386) return L; //# L& GREEK CAPITAL LETTER ALPHA WITH TONOS + if ((val>=0x0388) && (val<=0x038A)) return L; //# L& [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS + if (val==0x0000038C) return L; //# L& GREEK CAPITAL LETTER OMICRON WITH TONOS + if ((val>=0x038E) && (val<=0x03A1)) return L; //# L& [20] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK CAPITAL LETTER RHO + if ((val>=0x03A3) && (val<=0x03CE)) return L; //# L& [44] GREEK CAPITAL LETTER SIGMA..GREEK SMALL LETTER OMEGA WITH TONOS + if ((val>=0x03D0) && (val<=0x03F5)) return L; //# L& [38] GREEK BETA SYMBOL..GREEK LUNATE EPSILON SYMBOL + if ((val>=0x03F7) && (val<=0x0481)) return L; //# L& [139] GREEK CAPITAL LETTER SHO..CYRILLIC SMALL LETTER KOPPA + if (val==0x00000482) return L; //# So CYRILLIC THOUSANDS SIGN + if ((val>=0x048A) && (val<=0x0513)) return L; //# L& [138] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EL WITH HOOK + if ((val>=0x0531) && (val<=0x0556)) return L; //# L& [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH + if (val==0x00000559) return L; //# Lm ARMENIAN MODIFIER LETTER LEFT HALF RING + if ((val>=0x055A) && (val<=0x055F)) return L; //# Po [6] ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK + if ((val>=0x0561) && (val<=0x0587)) return L; //# L& [39] ARMENIAN SMALL LETTER AYB..ARMENIAN SMALL LIGATURE ECH YIWN + if (val==0x00000589) return L; //# Po ARMENIAN FULL STOP + if (val==0x00000903) return L; //# Mc DEVANAGARI SIGN VISARGA + if ((val>=0x0904) && (val<=0x0939)) return L; //# Lo [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA + if (val==0x0000093D) return L; //# Lo DEVANAGARI SIGN AVAGRAHA + if ((val>=0x093E) && (val<=0x0940)) return L; //# Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II + if ((val>=0x0949) && (val<=0x094C)) return L; //# Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU + if (val==0x00000950) return L; //# Lo DEVANAGARI OM + if ((val>=0x0958) && (val<=0x0961)) return L; //# Lo [10] DEVANAGARI LETTER QA..DEVANAGARI LETTER VOCALIC LL + if ((val>=0x0964) && (val<=0x0965)) return L; //# Po [2] DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA + if ((val>=0x0966) && (val<=0x096F)) return L; //# Nd [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE + if (val==0x00000970) return L; //# Po DEVANAGARI ABBREVIATION SIGN + if ((val>=0x097B) && (val<=0x097F)) return L; //# Lo [5] DEVANAGARI LETTER GGA..DEVANAGARI LETTER BBA + if ((val>=0x0982) && (val<=0x0983)) return L; //# Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA + if ((val>=0x0985) && (val<=0x098C)) return L; //# Lo [8] BENGALI LETTER A..BENGALI LETTER VOCALIC L + if ((val>=0x098F) && (val<=0x0990)) return L; //# Lo [2] BENGALI LETTER E..BENGALI LETTER AI + if ((val>=0x0993) && (val<=0x09A8)) return L; //# Lo [22] BENGALI LETTER O..BENGALI LETTER NA + if ((val>=0x09AA) && (val<=0x09B0)) return L; //# Lo [7] BENGALI LETTER PA..BENGALI LETTER RA + if (val==0x000009B2) return L; //# Lo BENGALI LETTER LA + if ((val>=0x09B6) && (val<=0x09B9)) return L; //# Lo [4] BENGALI LETTER SHA..BENGALI LETTER HA + if (val==0x000009BD) return L; //# Lo BENGALI SIGN AVAGRAHA + if ((val>=0x09BE) && (val<=0x09C0)) return L; //# Mc [3] BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN II + if ((val>=0x09C7) && (val<=0x09C8)) return L; //# Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI + if ((val>=0x09CB) && (val<=0x09CC)) return L; //# Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU + if (val==0x000009CE) return L; //# Lo BENGALI LETTER KHANDA TA + if (val==0x000009D7) return L; //# Mc BENGALI AU LENGTH MARK + if ((val>=0x09DC) && (val<=0x09DD)) return L; //# Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA + if ((val>=0x09DF) && (val<=0x09E1)) return L; //# Lo [3] BENGALI LETTER YYA..BENGALI LETTER VOCALIC LL + if ((val>=0x09E6) && (val<=0x09EF)) return L; //# Nd [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE + if ((val>=0x09F0) && (val<=0x09F1)) return L; //# Lo [2] BENGALI LETTER RA WITH MIDDLE DIAGONAL..BENGALI LETTER RA WITH LOWER DIAGONAL + if ((val>=0x09F4) && (val<=0x09F9)) return L; //# No [6] BENGALI CURRENCY NUMERATOR ONE..BENGALI CURRENCY DENOMINATOR SIXTEEN + if (val==0x000009FA) return L; //# So BENGALI ISSHAR + if (val==0x00000A03) return L; //# Mc GURMUKHI SIGN VISARGA + if ((val>=0x0A05) && (val<=0x0A0A)) return L; //# Lo [6] GURMUKHI LETTER A..GURMUKHI LETTER UU + if ((val>=0x0A0F) && (val<=0x0A10)) return L; //# Lo [2] GURMUKHI LETTER EE..GURMUKHI LETTER AI + if ((val>=0x0A13) && (val<=0x0A28)) return L; //# Lo [22] GURMUKHI LETTER OO..GURMUKHI LETTER NA + if ((val>=0x0A2A) && (val<=0x0A30)) return L; //# Lo [7] GURMUKHI LETTER PA..GURMUKHI LETTER RA + if ((val>=0x0A32) && (val<=0x0A33)) return L; //# Lo [2] GURMUKHI LETTER LA..GURMUKHI LETTER LLA + if ((val>=0x0A35) && (val<=0x0A36)) return L; //# Lo [2] GURMUKHI LETTER VA..GURMUKHI LETTER SHA + if ((val>=0x0A38) && (val<=0x0A39)) return L; //# Lo [2] GURMUKHI LETTER SA..GURMUKHI LETTER HA + if ((val>=0x0A3E) && (val<=0x0A40)) return L; //# Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II + if ((val>=0x0A59) && (val<=0x0A5C)) return L; //# Lo [4] GURMUKHI LETTER KHHA..GURMUKHI LETTER RRA + if (val==0x00000A5E) return L; //# Lo GURMUKHI LETTER FA + if ((val>=0x0A66) && (val<=0x0A6F)) return L; //# Nd [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE + if ((val>=0x0A72) && (val<=0x0A74)) return L; //# Lo [3] GURMUKHI IRI..GURMUKHI EK ONKAR + if (val==0x00000A83) return L; //# Mc GUJARATI SIGN VISARGA + if ((val>=0x0A85) && (val<=0x0A8D)) return L; //# Lo [9] GUJARATI LETTER A..GUJARATI VOWEL CANDRA E + if ((val>=0x0A8F) && (val<=0x0A91)) return L; //# Lo [3] GUJARATI LETTER E..GUJARATI VOWEL CANDRA O + if ((val>=0x0A93) && (val<=0x0AA8)) return L; //# Lo [22] GUJARATI LETTER O..GUJARATI LETTER NA + if ((val>=0x0AAA) && (val<=0x0AB0)) return L; //# Lo [7] GUJARATI LETTER PA..GUJARATI LETTER RA + if ((val>=0x0AB2) && (val<=0x0AB3)) return L; //# Lo [2] GUJARATI LETTER LA..GUJARATI LETTER LLA + if ((val>=0x0AB5) && (val<=0x0AB9)) return L; //# Lo [5] GUJARATI LETTER VA..GUJARATI LETTER HA + if (val==0x00000ABD) return L; //# Lo GUJARATI SIGN AVAGRAHA + if ((val>=0x0ABE) && (val<=0x0AC0)) return L; //# Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II + if (val==0x00000AC9) return L; //# Mc GUJARATI VOWEL SIGN CANDRA O + if ((val>=0x0ACB) && (val<=0x0ACC)) return L; //# Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU + if (val==0x00000AD0) return L; //# Lo GUJARATI OM + if ((val>=0x0AE0) && (val<=0x0AE1)) return L; //# Lo [2] GUJARATI LETTER VOCALIC RR..GUJARATI LETTER VOCALIC LL + if ((val>=0x0AE6) && (val<=0x0AEF)) return L; //# Nd [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE + if ((val>=0x0B02) && (val<=0x0B03)) return L; //# Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA + if ((val>=0x0B05) && (val<=0x0B0C)) return L; //# Lo [8] ORIYA LETTER A..ORIYA LETTER VOCALIC L + if ((val>=0x0B0F) && (val<=0x0B10)) return L; //# Lo [2] ORIYA LETTER E..ORIYA LETTER AI + if ((val>=0x0B13) && (val<=0x0B28)) return L; //# Lo [22] ORIYA LETTER O..ORIYA LETTER NA + if ((val>=0x0B2A) && (val<=0x0B30)) return L; //# Lo [7] ORIYA LETTER PA..ORIYA LETTER RA + if ((val>=0x0B32) && (val<=0x0B33)) return L; //# Lo [2] ORIYA LETTER LA..ORIYA LETTER LLA + if ((val>=0x0B35) && (val<=0x0B39)) return L; //# Lo [5] ORIYA LETTER VA..ORIYA LETTER HA + if (val==0x00000B3D) return L; //# Lo ORIYA SIGN AVAGRAHA + if (val==0x00000B3E) return L; //# Mc ORIYA VOWEL SIGN AA + if (val==0x00000B40) return L; //# Mc ORIYA VOWEL SIGN II + if ((val>=0x0B47) && (val<=0x0B48)) return L; //# Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI + if ((val>=0x0B4B) && (val<=0x0B4C)) return L; //# Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU + if (val==0x00000B57) return L; //# Mc ORIYA AU LENGTH MARK + if ((val>=0x0B5C) && (val<=0x0B5D)) return L; //# Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA + if ((val>=0x0B5F) && (val<=0x0B61)) return L; //# Lo [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL + if ((val>=0x0B66) && (val<=0x0B6F)) return L; //# Nd [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE + if (val==0x00000B70) return L; //# So ORIYA ISSHAR + if (val==0x00000B71) return L; //# Lo ORIYA LETTER WA + if (val==0x00000B83) return L; //# Lo TAMIL SIGN VISARGA + if ((val>=0x0B85) && (val<=0x0B8A)) return L; //# Lo [6] TAMIL LETTER A..TAMIL LETTER UU + if ((val>=0x0B8E) && (val<=0x0B90)) return L; //# Lo [3] TAMIL LETTER E..TAMIL LETTER AI + if ((val>=0x0B92) && (val<=0x0B95)) return L; //# Lo [4] TAMIL LETTER O..TAMIL LETTER KA + if ((val>=0x0B99) && (val<=0x0B9A)) return L; //# Lo [2] TAMIL LETTER NGA..TAMIL LETTER CA + if (val==0x00000B9C) return L; //# Lo TAMIL LETTER JA + if ((val>=0x0B9E) && (val<=0x0B9F)) return L; //# Lo [2] TAMIL LETTER NYA..TAMIL LETTER TTA + if ((val>=0x0BA3) && (val<=0x0BA4)) return L; //# Lo [2] TAMIL LETTER NNA..TAMIL LETTER TA + if ((val>=0x0BA8) && (val<=0x0BAA)) return L; //# Lo [3] TAMIL LETTER NA..TAMIL LETTER PA + if ((val>=0x0BAE) && (val<=0x0BB9)) return L; //# Lo [12] TAMIL LETTER MA..TAMIL LETTER HA + if ((val>=0x0BBE) && (val<=0x0BBF)) return L; //# Mc [2] TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN I + if ((val>=0x0BC1) && (val<=0x0BC2)) return L; //# Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU + if ((val>=0x0BC6) && (val<=0x0BC8)) return L; //# Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI + if ((val>=0x0BCA) && (val<=0x0BCC)) return L; //# Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU + if (val==0x00000BD7) return L; //# Mc TAMIL AU LENGTH MARK + if ((val>=0x0BE6) && (val<=0x0BEF)) return L; //# Nd [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE + if ((val>=0x0BF0) && (val<=0x0BF2)) return L; //# No [3] TAMIL NUMBER TEN..TAMIL NUMBER ONE THOUSAND + if ((val>=0x0C01) && (val<=0x0C03)) return L; //# Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA + if ((val>=0x0C05) && (val<=0x0C0C)) return L; //# Lo [8] TELUGU LETTER A..TELUGU LETTER VOCALIC L + if ((val>=0x0C0E) && (val<=0x0C10)) return L; //# Lo [3] TELUGU LETTER E..TELUGU LETTER AI + if ((val>=0x0C12) && (val<=0x0C28)) return L; //# Lo [23] TELUGU LETTER O..TELUGU LETTER NA + if ((val>=0x0C2A) && (val<=0x0C33)) return L; //# Lo [10] TELUGU LETTER PA..TELUGU LETTER LLA + if ((val>=0x0C35) && (val<=0x0C39)) return L; //# Lo [5] TELUGU LETTER VA..TELUGU LETTER HA + if ((val>=0x0C41) && (val<=0x0C44)) return L; //# Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR + if ((val>=0x0C60) && (val<=0x0C61)) return L; //# Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL + if ((val>=0x0C66) && (val<=0x0C6F)) return L; //# Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE + if ((val>=0x0C82) && (val<=0x0C83)) return L; //# Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA + if ((val>=0x0C85) && (val<=0x0C8C)) return L; //# Lo [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L + if ((val>=0x0C8E) && (val<=0x0C90)) return L; //# Lo [3] KANNADA LETTER E..KANNADA LETTER AI + if ((val>=0x0C92) && (val<=0x0CA8)) return L; //# Lo [23] KANNADA LETTER O..KANNADA LETTER NA + if ((val>=0x0CAA) && (val<=0x0CB3)) return L; //# Lo [10] KANNADA LETTER PA..KANNADA LETTER LLA + if ((val>=0x0CB5) && (val<=0x0CB9)) return L; //# Lo [5] KANNADA LETTER VA..KANNADA LETTER HA + if (val==0x00000CBD) return L; //# Lo KANNADA SIGN AVAGRAHA + if (val==0x00000CBE) return L; //# Mc KANNADA VOWEL SIGN AA + if (val==0x00000CBF) return L; //# Mn KANNADA VOWEL SIGN I + if ((val>=0x0CC0) && (val<=0x0CC4)) return L; //# Mc [5] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN VOCALIC RR + if (val==0x00000CC6) return L; //# Mn KANNADA VOWEL SIGN E + if ((val>=0x0CC7) && (val<=0x0CC8)) return L; //# Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI + if ((val>=0x0CCA) && (val<=0x0CCB)) return L; //# Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO + if ((val>=0x0CD5) && (val<=0x0CD6)) return L; //# Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK + if (val==0x00000CDE) return L; //# Lo KANNADA LETTER FA + if ((val>=0x0CE0) && (val<=0x0CE1)) return L; //# Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL + if ((val>=0x0CE6) && (val<=0x0CEF)) return L; //# Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE + if ((val>=0x0D02) && (val<=0x0D03)) return L; //# Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA + if ((val>=0x0D05) && (val<=0x0D0C)) return L; //# Lo [8] MALAYALAM LETTER A..MALAYALAM LETTER VOCALIC L + if ((val>=0x0D0E) && (val<=0x0D10)) return L; //# Lo [3] MALAYALAM LETTER E..MALAYALAM LETTER AI + if ((val>=0x0D12) && (val<=0x0D28)) return L; //# Lo [23] MALAYALAM LETTER O..MALAYALAM LETTER NA + if ((val>=0x0D2A) && (val<=0x0D39)) return L; //# Lo [16] MALAYALAM LETTER PA..MALAYALAM LETTER HA + if ((val>=0x0D3E) && (val<=0x0D40)) return L; //# Mc [3] MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN II + if ((val>=0x0D46) && (val<=0x0D48)) return L; //# Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI + if ((val>=0x0D4A) && (val<=0x0D4C)) return L; //# Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU + if (val==0x00000D57) return L; //# Mc MALAYALAM AU LENGTH MARK + if ((val>=0x0D60) && (val<=0x0D61)) return L; //# Lo [2] MALAYALAM LETTER VOCALIC RR..MALAYALAM LETTER VOCALIC LL + if ((val>=0x0D66) && (val<=0x0D6F)) return L; //# Nd [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE + if ((val>=0x0D82) && (val<=0x0D83)) return L; //# Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA + if ((val>=0x0D85) && (val<=0x0D96)) return L; //# Lo [18] SINHALA LETTER AYANNA..SINHALA LETTER AUYANNA + if ((val>=0x0D9A) && (val<=0x0DB1)) return L; //# Lo [24] SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA LETTER DANTAJA NAYANNA + if ((val>=0x0DB3) && (val<=0x0DBB)) return L; //# Lo [9] SINHALA LETTER SANYAKA DAYANNA..SINHALA LETTER RAYANNA + if (val==0x00000DBD) return L; //# Lo SINHALA LETTER DANTAJA LAYANNA + if ((val>=0x0DC0) && (val<=0x0DC6)) return L; //# Lo [7] SINHALA LETTER VAYANNA..SINHALA LETTER FAYANNA + if ((val>=0x0DCF) && (val<=0x0DD1)) return L; //# Mc [3] SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA + if ((val>=0x0DD8) && (val<=0x0DDF)) return L; //# Mc [8] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA + if ((val>=0x0DF2) && (val<=0x0DF3)) return L; //# Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA + if (val==0x00000DF4) return L; //# Po SINHALA PUNCTUATION KUNDDALIYA + if ((val>=0x0E01) && (val<=0x0E30)) return L; //# Lo [48] THAI CHARACTER KO KAI..THAI CHARACTER SARA A + if ((val>=0x0E32) && (val<=0x0E33)) return L; //# Lo [2] THAI CHARACTER SARA AA..THAI CHARACTER SARA AM + if ((val>=0x0E40) && (val<=0x0E45)) return L; //# Lo [6] THAI CHARACTER SARA E..THAI CHARACTER LAKKHANGYAO + if (val==0x00000E46) return L; //# Lm THAI CHARACTER MAIYAMOK + if (val==0x00000E4F) return L; //# Po THAI CHARACTER FONGMAN + if ((val>=0x0E50) && (val<=0x0E59)) return L; //# Nd [10] THAI DIGIT ZERO..THAI DIGIT NINE + if ((val>=0x0E5A) && (val<=0x0E5B)) return L; //# Po [2] THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT + if ((val>=0x0E81) && (val<=0x0E82)) return L; //# Lo [2] LAO LETTER KO..LAO LETTER KHO SUNG + if (val==0x00000E84) return L; //# Lo LAO LETTER KHO TAM + if ((val>=0x0E87) && (val<=0x0E88)) return L; //# Lo [2] LAO LETTER NGO..LAO LETTER CO + if (val==0x00000E8A) return L; //# Lo LAO LETTER SO TAM + if (val==0x00000E8D) return L; //# Lo LAO LETTER NYO + if ((val>=0x0E94) && (val<=0x0E97)) return L; //# Lo [4] LAO LETTER DO..LAO LETTER THO TAM + if ((val>=0x0E99) && (val<=0x0E9F)) return L; //# Lo [7] LAO LETTER NO..LAO LETTER FO SUNG + if ((val>=0x0EA1) && (val<=0x0EA3)) return L; //# Lo [3] LAO LETTER MO..LAO LETTER LO LING + if (val==0x00000EA5) return L; //# Lo LAO LETTER LO LOOT + if (val==0x00000EA7) return L; //# Lo LAO LETTER WO + if ((val>=0x0EAA) && (val<=0x0EAB)) return L; //# Lo [2] LAO LETTER SO SUNG..LAO LETTER HO SUNG + if ((val>=0x0EAD) && (val<=0x0EB0)) return L; //# Lo [4] LAO LETTER O..LAO VOWEL SIGN A + if ((val>=0x0EB2) && (val<=0x0EB3)) return L; //# Lo [2] LAO VOWEL SIGN AA..LAO VOWEL SIGN AM + if (val==0x00000EBD) return L; //# Lo LAO SEMIVOWEL SIGN NYO + if ((val>=0x0EC0) && (val<=0x0EC4)) return L; //# Lo [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI + if (val==0x00000EC6) return L; //# Lm LAO KO LA + if ((val>=0x0ED0) && (val<=0x0ED9)) return L; //# Nd [10] LAO DIGIT ZERO..LAO DIGIT NINE + if ((val>=0x0EDC) && (val<=0x0EDD)) return L; //# Lo [2] LAO HO NO..LAO HO MO + if (val==0x00000F00) return L; //# Lo TIBETAN SYLLABLE OM + if ((val>=0x0F01) && (val<=0x0F03)) return L; //# So [3] TIBETAN MARK GTER YIG MGO TRUNCATED A..TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA + if ((val>=0x0F04) && (val<=0x0F12)) return L; //# Po [15] TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK RGYA GRAM SHAD + if ((val>=0x0F13) && (val<=0x0F17)) return L; //# So [5] TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN..TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS + if ((val>=0x0F1A) && (val<=0x0F1F)) return L; //# So [6] TIBETAN SIGN RDEL DKAR GCIG..TIBETAN SIGN RDEL DKAR RDEL NAG + if ((val>=0x0F20) && (val<=0x0F29)) return L; //# Nd [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE + if ((val>=0x0F2A) && (val<=0x0F33)) return L; //# No [10] TIBETAN DIGIT HALF ONE..TIBETAN DIGIT HALF ZERO + if (val==0x00000F34) return L; //# So TIBETAN MARK BSDUS RTAGS + if (val==0x00000F36) return L; //# So TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN + if (val==0x00000F38) return L; //# So TIBETAN MARK CHE MGO + if ((val>=0x0F3E) && (val<=0x0F3F)) return L; //# Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES + if ((val>=0x0F40) && (val<=0x0F47)) return L; //# Lo [8] TIBETAN LETTER KA..TIBETAN LETTER JA + if ((val>=0x0F49) && (val<=0x0F6A)) return L; //# Lo [34] TIBETAN LETTER NYA..TIBETAN LETTER FIXED-FORM RA + if (val==0x00000F7F) return L; //# Mc TIBETAN SIGN RNAM BCAD + if (val==0x00000F85) return L; //# Po TIBETAN MARK PALUTA + if ((val>=0x0F88) && (val<=0x0F8B)) return L; //# Lo [4] TIBETAN SIGN LCE TSA CAN..TIBETAN SIGN GRU MED RGYINGS + if ((val>=0x0FBE) && (val<=0x0FC5)) return L; //# So [8] TIBETAN KU RU KHA..TIBETAN SYMBOL RDO RJE + if ((val>=0x0FC7) && (val<=0x0FCC)) return L; //# So [6] TIBETAN SYMBOL RDO RJE RGYA GRAM..TIBETAN SYMBOL NOR BU BZHI -KHYIL + if (val==0x00000FCF) return L; //# So TIBETAN SIGN RDEL NAG GSUM + if ((val>=0x0FD0) && (val<=0x0FD1)) return L; //# Po [2] TIBETAN MARK BSKA- SHOG GI MGO RGYAN..TIBETAN MARK MNYAM YIG GI MGO RGYAN + if ((val>=0x1000) && (val<=0x1021)) return L; //# Lo [34] MYANMAR LETTER KA..MYANMAR LETTER A + if ((val>=0x1023) && (val<=0x1027)) return L; //# Lo [5] MYANMAR LETTER I..MYANMAR LETTER E + if ((val>=0x1029) && (val<=0x102A)) return L; //# Lo [2] MYANMAR LETTER O..MYANMAR LETTER AU + if (val==0x0000102C) return L; //# Mc MYANMAR VOWEL SIGN AA + if (val==0x00001031) return L; //# Mc MYANMAR VOWEL SIGN E + if (val==0x00001038) return L; //# Mc MYANMAR SIGN VISARGA + if ((val>=0x1040) && (val<=0x1049)) return L; //# Nd [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE + if ((val>=0x104A) && (val<=0x104F)) return L; //# Po [6] MYANMAR SIGN LITTLE SECTION..MYANMAR SYMBOL GENITIVE + if ((val>=0x1050) && (val<=0x1055)) return L; //# Lo [6] MYANMAR LETTER SHA..MYANMAR LETTER VOCALIC LL + if ((val>=0x1056) && (val<=0x1057)) return L; //# Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR + if ((val>=0x10A0) && (val<=0x10C5)) return L; //# L& [38] GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE + if ((val>=0x10D0) && (val<=0x10FA)) return L; //# Lo [43] GEORGIAN LETTER AN..GEORGIAN LETTER AIN + if (val==0x000010FB) return L; //# Po GEORGIAN PARAGRAPH SEPARATOR + if (val==0x000010FC) return L; //# Lm MODIFIER LETTER GEORGIAN NAR + if ((val>=0x1100) && (val<=0x1159)) return L; //# Lo [90] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG YEORINHIEUH + if ((val>=0x115F) && (val<=0x11A2)) return L; //# Lo [68] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG SSANGARAEA + if ((val>=0x11A8) && (val<=0x11F9)) return L; //# Lo [82] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG YEORINHIEUH + if ((val>=0x1200) && (val<=0x1248)) return L; //# Lo [73] ETHIOPIC SYLLABLE HA..ETHIOPIC SYLLABLE QWA + if ((val>=0x124A) && (val<=0x124D)) return L; //# Lo [4] ETHIOPIC SYLLABLE QWI..ETHIOPIC SYLLABLE QWE + if ((val>=0x1250) && (val<=0x1256)) return L; //# Lo [7] ETHIOPIC SYLLABLE QHA..ETHIOPIC SYLLABLE QHO + if (val==0x00001258) return L; //# Lo ETHIOPIC SYLLABLE QHWA + if ((val>=0x125A) && (val<=0x125D)) return L; //# Lo [4] ETHIOPIC SYLLABLE QHWI..ETHIOPIC SYLLABLE QHWE + if ((val>=0x1260) && (val<=0x1288)) return L; //# Lo [41] ETHIOPIC SYLLABLE BA..ETHIOPIC SYLLABLE XWA + if ((val>=0x128A) && (val<=0x128D)) return L; //# Lo [4] ETHIOPIC SYLLABLE XWI..ETHIOPIC SYLLABLE XWE + if ((val>=0x1290) && (val<=0x12B0)) return L; //# Lo [33] ETHIOPIC SYLLABLE NA..ETHIOPIC SYLLABLE KWA + if ((val>=0x12B2) && (val<=0x12B5)) return L; //# Lo [4] ETHIOPIC SYLLABLE KWI..ETHIOPIC SYLLABLE KWE + if ((val>=0x12B8) && (val<=0x12BE)) return L; //# Lo [7] ETHIOPIC SYLLABLE KXA..ETHIOPIC SYLLABLE KXO + if (val==0x000012C0) return L; //# Lo ETHIOPIC SYLLABLE KXWA + if ((val>=0x12C2) && (val<=0x12C5)) return L; //# Lo [4] ETHIOPIC SYLLABLE KXWI..ETHIOPIC SYLLABLE KXWE + if ((val>=0x12C8) && (val<=0x12D6)) return L; //# Lo [15] ETHIOPIC SYLLABLE WA..ETHIOPIC SYLLABLE PHARYNGEAL O + if ((val>=0x12D8) && (val<=0x1310)) return L; //# Lo [57] ETHIOPIC SYLLABLE ZA..ETHIOPIC SYLLABLE GWA + if ((val>=0x1312) && (val<=0x1315)) return L; //# Lo [4] ETHIOPIC SYLLABLE GWI..ETHIOPIC SYLLABLE GWE + if ((val>=0x1318) && (val<=0x135A)) return L; //# Lo [67] ETHIOPIC SYLLABLE GGA..ETHIOPIC SYLLABLE FYA + if (val==0x00001360) return L; //# So ETHIOPIC SECTION MARK + if ((val>=0x1361) && (val<=0x1368)) return L; //# Po [8] ETHIOPIC WORDSPACE..ETHIOPIC PARAGRAPH SEPARATOR + if ((val>=0x1369) && (val<=0x137C)) return L; //# No [20] ETHIOPIC DIGIT ONE..ETHIOPIC NUMBER TEN THOUSAND + if ((val>=0x1380) && (val<=0x138F)) return L; //# Lo [16] ETHIOPIC SYLLABLE SEBATBEIT MWA..ETHIOPIC SYLLABLE PWE + if ((val>=0x13A0) && (val<=0x13F4)) return L; //# Lo [85] CHEROKEE LETTER A..CHEROKEE LETTER YV + if ((val>=0x1401) && (val<=0x166C)) return L; //# Lo [620] CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA + if ((val>=0x166D) && (val<=0x166E)) return L; //# Po [2] CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP + if ((val>=0x166F) && (val<=0x1676)) return L; //# Lo [8] CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS NNGAA + if ((val>=0x1681) && (val<=0x169A)) return L; //# Lo [26] OGHAM LETTER BEITH..OGHAM LETTER PEITH + if ((val>=0x16A0) && (val<=0x16EA)) return L; //# Lo [75] RUNIC LETTER FEHU FEOH FE F..RUNIC LETTER X + if ((val>=0x16EB) && (val<=0x16ED)) return L; //# Po [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION + if ((val>=0x16EE) && (val<=0x16F0)) return L; //# Nl [3] RUNIC ARLAUG SYMBOL..RUNIC BELGTHOR SYMBOL + if ((val>=0x1700) && (val<=0x170C)) return L; //# Lo [13] TAGALOG LETTER A..TAGALOG LETTER YA + if ((val>=0x170E) && (val<=0x1711)) return L; //# Lo [4] TAGALOG LETTER LA..TAGALOG LETTER HA + if ((val>=0x1720) && (val<=0x1731)) return L; //# Lo [18] HANUNOO LETTER A..HANUNOO LETTER HA + if ((val>=0x1735) && (val<=0x1736)) return L; //# Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION + if ((val>=0x1740) && (val<=0x1751)) return L; //# Lo [18] BUHID LETTER A..BUHID LETTER HA + if ((val>=0x1760) && (val<=0x176C)) return L; //# Lo [13] TAGBANWA LETTER A..TAGBANWA LETTER YA + if ((val>=0x176E) && (val<=0x1770)) return L; //# Lo [3] TAGBANWA LETTER LA..TAGBANWA LETTER SA + if ((val>=0x1780) && (val<=0x17B3)) return L; //# Lo [52] KHMER LETTER KA..KHMER INDEPENDENT VOWEL QAU + if ((val>=0x17B4) && (val<=0x17B5)) return L; //# Cf [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA + if (val==0x000017B6) return L; //# Mc KHMER VOWEL SIGN AA + if ((val>=0x17BE) && (val<=0x17C5)) return L; //# Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU + if ((val>=0x17C7) && (val<=0x17C8)) return L; //# Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU + if ((val>=0x17D4) && (val<=0x17D6)) return L; //# Po [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH + if (val==0x000017D7) return L; //# Lm KHMER SIGN LEK TOO + if ((val>=0x17D8) && (val<=0x17DA)) return L; //# Po [3] KHMER SIGN BEYYAL..KHMER SIGN KOOMUUT + if (val==0x000017DC) return L; //# Lo KHMER SIGN AVAKRAHASANYA + if ((val>=0x17E0) && (val<=0x17E9)) return L; //# Nd [10] KHMER DIGIT ZERO..KHMER DIGIT NINE + if ((val>=0x1810) && (val<=0x1819)) return L; //# Nd [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE + if ((val>=0x1820) && (val<=0x1842)) return L; //# Lo [35] MONGOLIAN LETTER A..MONGOLIAN LETTER CHI + if (val==0x00001843) return L; //# Lm MONGOLIAN LETTER TODO LONG VOWEL SIGN + if ((val>=0x1844) && (val<=0x1877)) return L; //# Lo [52] MONGOLIAN LETTER TODO E..MONGOLIAN LETTER MANCHU ZHA + if ((val>=0x1880) && (val<=0x18A8)) return L; //# Lo [41] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER MANCHU ALI GALI BHA + if ((val>=0x1900) && (val<=0x191C)) return L; //# Lo [29] LIMBU VOWEL-CARRIER LETTER..LIMBU LETTER HA + if ((val>=0x1923) && (val<=0x1926)) return L; //# Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU + if ((val>=0x1930) && (val<=0x1931)) return L; //# Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA + if ((val>=0x1933) && (val<=0x1938)) return L; //# Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA + if ((val>=0x1946) && (val<=0x194F)) return L; //# Nd [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE + if ((val>=0x1950) && (val<=0x196D)) return L; //# Lo [30] TAI LE LETTER KA..TAI LE LETTER AI + if ((val>=0x1970) && (val<=0x1974)) return L; //# Lo [5] TAI LE LETTER TONE-2..TAI LE LETTER TONE-6 + if ((val>=0x1980) && (val<=0x19A9)) return L; //# Lo [42] NEW TAI LUE LETTER HIGH QA..NEW TAI LUE LETTER LOW XVA + if ((val>=0x19B0) && (val<=0x19C0)) return L; //# Mc [17] NEW TAI LUE VOWEL SIGN VOWEL SHORTENER..NEW TAI LUE VOWEL SIGN IY + if ((val>=0x19C1) && (val<=0x19C7)) return L; //# Lo [7] NEW TAI LUE LETTER FINAL V..NEW TAI LUE LETTER FINAL B + if ((val>=0x19C8) && (val<=0x19C9)) return L; //# Mc [2] NEW TAI LUE TONE MARK-1..NEW TAI LUE TONE MARK-2 + if ((val>=0x19D0) && (val<=0x19D9)) return L; //# Nd [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE + if ((val>=0x1A00) && (val<=0x1A16)) return L; //# Lo [23] BUGINESE LETTER KA..BUGINESE LETTER HA + if ((val>=0x1A19) && (val<=0x1A1B)) return L; //# Mc [3] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN AE + if ((val>=0x1A1E) && (val<=0x1A1F)) return L; //# Po [2] BUGINESE PALLAWA..BUGINESE END OF SECTION + if (val==0x00001B04) return L; //# Mc BALINESE SIGN BISAH + if ((val>=0x1B05) && (val<=0x1B33)) return L; //# Lo [47] BALINESE LETTER AKARA..BALINESE LETTER HA + if (val==0x00001B35) return L; //# Mc BALINESE VOWEL SIGN TEDUNG + if (val==0x00001B3B) return L; //# Mc BALINESE VOWEL SIGN RA REPA TEDUNG + if ((val>=0x1B3D) && (val<=0x1B41)) return L; //# Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG + if ((val>=0x1B43) && (val<=0x1B44)) return L; //# Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG + if ((val>=0x1B45) && (val<=0x1B4B)) return L; //# Lo [7] BALINESE LETTER KAF SASAK..BALINESE LETTER ASYURA SASAK + if ((val>=0x1B50) && (val<=0x1B59)) return L; //# Nd [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE + if ((val>=0x1B5A) && (val<=0x1B60)) return L; //# Po [7] BALINESE PANTI..BALINESE PAMENENG + if ((val>=0x1B61) && (val<=0x1B6A)) return L; //# So [10] BALINESE MUSICAL SYMBOL DONG..BALINESE MUSICAL SYMBOL DANG GEDE + if ((val>=0x1B74) && (val<=0x1B7C)) return L; //# So [9] BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG..BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING + if ((val>=0x1D00) && (val<=0x1D2B)) return L; //# L& [44] LATIN LETTER SMALL CAPITAL A..CYRILLIC LETTER SMALL CAPITAL EL + if ((val>=0x1D2C) && (val<=0x1D61)) return L; //# Lm [54] MODIFIER LETTER CAPITAL A..MODIFIER LETTER SMALL CHI + if ((val>=0x1D62) && (val<=0x1D77)) return L; //# L& [22] LATIN SUBSCRIPT SMALL LETTER I..LATIN SMALL LETTER TURNED G + if (val==0x00001D78) return L; //# Lm MODIFIER LETTER CYRILLIC EN + if ((val>=0x1D79) && (val<=0x1D9A)) return L; //# L& [34] LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK + if ((val>=0x1D9B) && (val<=0x1DBF)) return L; //# Lm [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA + if ((val>=0x1E00) && (val<=0x1E9B)) return L; //# L& [156] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER LONG S WITH DOT ABOVE + if ((val>=0x1EA0) && (val<=0x1EF9)) return L; //# L& [90] LATIN CAPITAL LETTER A WITH DOT BELOW..LATIN SMALL LETTER Y WITH TILDE + if ((val>=0x1F00) && (val<=0x1F15)) return L; //# L& [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA + if ((val>=0x1F18) && (val<=0x1F1D)) return L; //# L& [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA + if ((val>=0x1F20) && (val<=0x1F45)) return L; //# L& [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA + if ((val>=0x1F48) && (val<=0x1F4D)) return L; //# L& [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA + if ((val>=0x1F50) && (val<=0x1F57)) return L; //# L& [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI + if (val==0x00001F59) return L; //# L& GREEK CAPITAL LETTER UPSILON WITH DASIA + if (val==0x00001F5B) return L; //# L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA + if (val==0x00001F5D) return L; //# L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA + if ((val>=0x1F5F) && (val<=0x1F7D)) return L; //# L& [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA + if ((val>=0x1F80) && (val<=0x1FB4)) return L; //# L& [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI + if ((val>=0x1FB6) && (val<=0x1FBC)) return L; //# L& [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI + if (val==0x00001FBE) return L; //# L& GREEK PROSGEGRAMMENI + if ((val>=0x1FC2) && (val<=0x1FC4)) return L; //# L& [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI + if ((val>=0x1FC6) && (val<=0x1FCC)) return L; //# L& [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI + if ((val>=0x1FD0) && (val<=0x1FD3)) return L; //# L& [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA + if ((val>=0x1FD6) && (val<=0x1FDB)) return L; //# L& [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA + if ((val>=0x1FE0) && (val<=0x1FEC)) return L; //# L& [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA + if ((val>=0x1FF2) && (val<=0x1FF4)) return L; //# L& [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI + if ((val>=0x1FF6) && (val<=0x1FFC)) return L; //# L& [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI + if (val==0x0000200E) return L; //# Cf LEFT-TO-RIGHT MARK + if (val==0x00002071) return L; //# L& SUPERSCRIPT LATIN SMALL LETTER I + if (val==0x0000207F) return L; //# L& SUPERSCRIPT LATIN SMALL LETTER N + if ((val>=0x2090) && (val<=0x2094)) return L; //# Lm [5] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER SCHWA + if (val==0x00002102) return L; //# L& DOUBLE-STRUCK CAPITAL C + if (val==0x00002107) return L; //# L& EULER CONSTANT + if ((val>=0x210A) && (val<=0x2113)) return L; //# L& [10] SCRIPT SMALL G..SCRIPT SMALL L + if (val==0x00002115) return L; //# L& DOUBLE-STRUCK CAPITAL N + if ((val>=0x2119) && (val<=0x211D)) return L; //# L& [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R + if (val==0x00002124) return L; //# L& DOUBLE-STRUCK CAPITAL Z + if (val==0x00002126) return L; //# L& OHM SIGN + if (val==0x00002128) return L; //# L& BLACK-LETTER CAPITAL Z + if ((val>=0x212A) && (val<=0x212D)) return L; //# L& [4] KELVIN SIGN..BLACK-LETTER CAPITAL C + if ((val>=0x212F) && (val<=0x2134)) return L; //# L& [6] SCRIPT SMALL E..SCRIPT SMALL O + if ((val>=0x2135) && (val<=0x2138)) return L; //# Lo [4] ALEF SYMBOL..DALET SYMBOL + if (val==0x00002139) return L; //# L& INFORMATION SOURCE + if ((val>=0x213C) && (val<=0x213F)) return L; //# L& [4] DOUBLE-STRUCK SMALL PI..DOUBLE-STRUCK CAPITAL PI + if ((val>=0x2145) && (val<=0x2149)) return L; //# L& [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J + if (val==0x0000214E) return L; //# L& TURNED SMALL F + if ((val>=0x2160) && (val<=0x2182)) return L; //# Nl [35] ROMAN NUMERAL ONE..ROMAN NUMERAL TEN THOUSAND + if ((val>=0x2183) && (val<=0x2184)) return L; //# L& [2] ROMAN NUMERAL REVERSED ONE HUNDRED..LATIN SMALL LETTER REVERSED C + if ((val>=0x2336) && (val<=0x237A)) return L; //# So [69] APL FUNCTIONAL SYMBOL I-BEAM..APL FUNCTIONAL SYMBOL ALPHA + if (val==0x00002395) return L; //# So APL FUNCTIONAL SYMBOL QUAD + if ((val>=0x249C) && (val<=0x24E9)) return L; //# So [78] PARENTHESIZED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z + if (val==0x000026AC) return L; //# So MEDIUM SMALL WHITE CIRCLE + if ((val>=0x2800) && (val<=0x28FF)) return L; //# So [256] BRAILLE PATTERN BLANK..BRAILLE PATTERN DOTS-12345678 + if ((val>=0x2C00) && (val<=0x2C2E)) return L; //# L& [47] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE + if ((val>=0x2C30) && (val<=0x2C5E)) return L; //# L& [47] GLAGOLITIC SMALL LETTER AZU..GLAGOLITIC SMALL LETTER LATINATE MYSLITE + if ((val>=0x2C60) && (val<=0x2C6C)) return L; //# L& [13] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN SMALL LETTER Z WITH DESCENDER + if ((val>=0x2C74) && (val<=0x2C77)) return L; //# L& [4] LATIN SMALL LETTER V WITH CURL..LATIN SMALL LETTER TAILLESS PHI + if ((val>=0x2C80) && (val<=0x2CE4)) return L; //# L& [101] COPTIC CAPITAL LETTER ALFA..COPTIC SYMBOL KAI + if ((val>=0x2D00) && (val<=0x2D25)) return L; //# L& [38] GEORGIAN SMALL LETTER AN..GEORGIAN SMALL LETTER HOE + if ((val>=0x2D30) && (val<=0x2D65)) return L; //# Lo [54] TIFINAGH LETTER YA..TIFINAGH LETTER YAZZ + if (val==0x00002D6F) return L; //# Lm TIFINAGH MODIFIER LETTER LABIALIZATION MARK + if ((val>=0x2D80) && (val<=0x2D96)) return L; //# Lo [23] ETHIOPIC SYLLABLE LOA..ETHIOPIC SYLLABLE GGWE + if ((val>=0x2DA0) && (val<=0x2DA6)) return L; //# Lo [7] ETHIOPIC SYLLABLE SSA..ETHIOPIC SYLLABLE SSO + if ((val>=0x2DA8) && (val<=0x2DAE)) return L; //# Lo [7] ETHIOPIC SYLLABLE CCA..ETHIOPIC SYLLABLE CCO + if ((val>=0x2DB0) && (val<=0x2DB6)) return L; //# Lo [7] ETHIOPIC SYLLABLE ZZA..ETHIOPIC SYLLABLE ZZO + if ((val>=0x2DB8) && (val<=0x2DBE)) return L; //# Lo [7] ETHIOPIC SYLLABLE CCHA..ETHIOPIC SYLLABLE CCHO + if ((val>=0x2DC0) && (val<=0x2DC6)) return L; //# Lo [7] ETHIOPIC SYLLABLE QYA..ETHIOPIC SYLLABLE QYO + if ((val>=0x2DC8) && (val<=0x2DCE)) return L; //# Lo [7] ETHIOPIC SYLLABLE KYA..ETHIOPIC SYLLABLE KYO + if ((val>=0x2DD0) && (val<=0x2DD6)) return L; //# Lo [7] ETHIOPIC SYLLABLE XYA..ETHIOPIC SYLLABLE XYO + if ((val>=0x2DD8) && (val<=0x2DDE)) return L; //# Lo [7] ETHIOPIC SYLLABLE GYA..ETHIOPIC SYLLABLE GYO + if (val==0x00003005) return L; //# Lm IDEOGRAPHIC ITERATION MARK + if (val==0x00003006) return L; //# Lo IDEOGRAPHIC CLOSING MARK + if (val==0x00003007) return L; //# Nl IDEOGRAPHIC NUMBER ZERO + if ((val>=0x3021) && (val<=0x3029)) return L; //# Nl [9] HANGZHOU NUMERAL ONE..HANGZHOU NUMERAL NINE + if ((val>=0x3031) && (val<=0x3035)) return L; //# Lm [5] VERTICAL KANA REPEAT MARK..VERTICAL KANA REPEAT MARK LOWER HALF + if ((val>=0x3038) && (val<=0x303A)) return L; //# Nl [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY + if (val==0x0000303B) return L; //# Lm VERTICAL IDEOGRAPHIC ITERATION MARK + if (val==0x0000303C) return L; //# Lo MASU MARK + if ((val>=0x3041) && (val<=0x3096)) return L; //# Lo [86] HIRAGANA LETTER SMALL A..HIRAGANA LETTER SMALL KE + if ((val>=0x309D) && (val<=0x309E)) return L; //# Lm [2] HIRAGANA ITERATION MARK..HIRAGANA VOICED ITERATION MARK + if (val==0x0000309F) return L; //# Lo HIRAGANA DIGRAPH YORI + if ((val>=0x30A1) && (val<=0x30FA)) return L; //# Lo [90] KATAKANA LETTER SMALL A..KATAKANA LETTER VO + if ((val>=0x30FC) && (val<=0x30FE)) return L; //# Lm [3] KATAKANA-HIRAGANA PROLONGED SOUND MARK..KATAKANA VOICED ITERATION MARK + if (val==0x000030FF) return L; //# Lo KATAKANA DIGRAPH KOTO + if ((val>=0x3105) && (val<=0x312C)) return L; //# Lo [40] BOPOMOFO LETTER B..BOPOMOFO LETTER GN + if ((val>=0x3131) && (val<=0x318E)) return L; //# Lo [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE + if ((val>=0x3190) && (val<=0x3191)) return L; //# So [2] IDEOGRAPHIC ANNOTATION LINKING MARK..IDEOGRAPHIC ANNOTATION REVERSE MARK + if ((val>=0x3192) && (val<=0x3195)) return L; //# No [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC ANNOTATION FOUR MARK + if ((val>=0x3196) && (val<=0x319F)) return L; //# So [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC ANNOTATION MAN MARK + if ((val>=0x31A0) && (val<=0x31B7)) return L; //# Lo [24] BOPOMOFO LETTER BU..BOPOMOFO FINAL LETTER H + if ((val>=0x31F0) && (val<=0x31FF)) return L; //# Lo [16] KATAKANA LETTER SMALL KU..KATAKANA LETTER SMALL RO + if ((val>=0x3200) && (val<=0x321C)) return L; //# So [29] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED HANGUL CIEUC U + if ((val>=0x3220) && (val<=0x3229)) return L; //# No [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN + if ((val>=0x322A) && (val<=0x3243)) return L; //# So [26] PARENTHESIZED IDEOGRAPH MOON..PARENTHESIZED IDEOGRAPH REACH + if ((val>=0x3260) && (val<=0x327B)) return L; //# So [28] CIRCLED HANGUL KIYEOK..CIRCLED HANGUL HIEUH A + if (val==0x0000327F) return L; //# So KOREAN STANDARD SYMBOL + if ((val>=0x3280) && (val<=0x3289)) return L; //# No [10] CIRCLED IDEOGRAPH ONE..CIRCLED IDEOGRAPH TEN + if ((val>=0x328A) && (val<=0x32B0)) return L; //# So [39] CIRCLED IDEOGRAPH MOON..CIRCLED IDEOGRAPH NIGHT + if ((val>=0x32C0) && (val<=0x32CB)) return L; //# So [12] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER + if ((val>=0x32D0) && (val<=0x32FE)) return L; //# So [47] CIRCLED KATAKANA A..CIRCLED KATAKANA WO + if ((val>=0x3300) && (val<=0x3376)) return L; //# So [119] SQUARE APAATO..SQUARE PC + if ((val>=0x337B) && (val<=0x33DD)) return L; //# So [99] SQUARE ERA NAME HEISEI..SQUARE WB + if ((val>=0x33E0) && (val<=0x33FE)) return L; //# So [31] IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE..IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE + if ((val>=0x3400) && (val<=0x4DB5)) return L; //# Lo [6582] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DB5 + if ((val>=0x4E00) && (val<=0x9FBB)) return L; //# Lo [20924] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FBB + if ((val>=0xA000) && (val<=0xA014)) return L; //# Lo [21] YI SYLLABLE IT..YI SYLLABLE E + if (val==0x0000A015) return L; //# Lm YI SYLLABLE WU + if ((val>=0xA016) && (val<=0xA48C)) return L; //# Lo [1143] YI SYLLABLE BIT..YI SYLLABLE YYR + if ((val>=0xA800) && (val<=0xA801)) return L; //# Lo [2] SYLOTI NAGRI LETTER A..SYLOTI NAGRI LETTER I + if ((val>=0xA803) && (val<=0xA805)) return L; //# Lo [3] SYLOTI NAGRI LETTER U..SYLOTI NAGRI LETTER O + if ((val>=0xA807) && (val<=0xA80A)) return L; //# Lo [4] SYLOTI NAGRI LETTER KO..SYLOTI NAGRI LETTER GHO + if ((val>=0xA80C) && (val<=0xA822)) return L; //# Lo [23] SYLOTI NAGRI LETTER CO..SYLOTI NAGRI LETTER HO + if ((val>=0xA823) && (val<=0xA824)) return L; //# Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I + if (val==0x0000A827) return L; //# Mc SYLOTI NAGRI VOWEL SIGN OO + if ((val>=0xA840) && (val<=0xA873)) return L; //# Lo [52] PHAGS-PA LETTER KA..PHAGS-PA LETTER CANDRABINDU + if ((val>=0xAC00) && (val<=0xD7A3)) return L; //# Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH + if ((val>=0xE000) && (val<=0xF8FF)) return L; //# Co [6400] .. + if ((val>=0xF900) && (val<=0xFA2D)) return L; //# Lo [302] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA2D + if ((val>=0xFA30) && (val<=0xFA6A)) return L; //# Lo [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A + if ((val>=0xFA70) && (val<=0xFAD9)) return L; //# Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9 + if ((val>=0xFB00) && (val<=0xFB06)) return L; //# L& [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST + if ((val>=0xFB13) && (val<=0xFB17)) return L; //# L& [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH + if ((val>=0xFF21) && (val<=0xFF3A)) return L; //# L& [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z + if ((val>=0xFF41) && (val<=0xFF5A)) return L; //# L& [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z + if ((val>=0xFF66) && (val<=0xFF6F)) return L; //# Lo [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU + if (val==0x0000FF70) return L; //# Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK + if ((val>=0xFF71) && (val<=0xFF9D)) return L; //# Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N + if ((val>=0xFF9E) && (val<=0xFF9F)) return L; //# Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK + if ((val>=0xFFA0) && (val<=0xFFBE)) return L; //# Lo [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH + if ((val>=0xFFC2) && (val<=0xFFC7)) return L; //# Lo [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E + if ((val>=0xFFCA) && (val<=0xFFCF)) return L; //# Lo [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE + if ((val>=0xFFD2) && (val<=0xFFD7)) return L; //# Lo [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU + if ((val>=0xFFDA) && (val<=0xFFDC)) return L; //# Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I + if ((val>=0x10000) && (val<=0x1000B)) return L; //# Lo [12] LINEAR B SYLLABLE B008 A..LINEAR B SYLLABLE B046 JE + if ((val>=0x1000D) && (val<=0x10026)) return L; //# Lo [26] LINEAR B SYLLABLE B036 JO..LINEAR B SYLLABLE B032 QO + if ((val>=0x10028) && (val<=0x1003A)) return L; //# Lo [19] LINEAR B SYLLABLE B060 RA..LINEAR B SYLLABLE B042 WO + if ((val>=0x1003C) && (val<=0x1003D)) return L; //# Lo [2] LINEAR B SYLLABLE B017 ZA..LINEAR B SYLLABLE B074 ZE + if ((val>=0x1003F) && (val<=0x1004D)) return L; //# Lo [15] LINEAR B SYLLABLE B020 ZO..LINEAR B SYLLABLE B091 TWO + if ((val>=0x10050) && (val<=0x1005D)) return L; //# Lo [14] LINEAR B SYMBOL B018..LINEAR B SYMBOL B089 + if ((val>=0x10080) && (val<=0x100FA)) return L; //# Lo [123] LINEAR B IDEOGRAM B100 MAN..LINEAR B IDEOGRAM VESSEL B305 + if (val==0x00010100) return L; //# Po AEGEAN WORD SEPARATOR LINE + if (val==0x00010102) return L; //# So AEGEAN CHECK MARK + if ((val>=0x10107) && (val<=0x10133)) return L; //# No [45] AEGEAN NUMBER ONE..AEGEAN NUMBER NINETY THOUSAND + if ((val>=0x10137) && (val<=0x1013F)) return L; //# So [9] AEGEAN WEIGHT BASE UNIT..AEGEAN MEASURE THIRD SUBUNIT + if ((val>=0x10300) && (val<=0x1031E)) return L; //# Lo [31] OLD ITALIC LETTER A..OLD ITALIC LETTER UU + if ((val>=0x10320) && (val<=0x10323)) return L; //# No [4] OLD ITALIC NUMERAL ONE..OLD ITALIC NUMERAL FIFTY + if ((val>=0x10330) && (val<=0x10340)) return L; //# Lo [17] GOTHIC LETTER AHSA..GOTHIC LETTER PAIRTHRA + if (val==0x00010341) return L; //# Nl GOTHIC LETTER NINETY + if ((val>=0x10342) && (val<=0x10349)) return L; //# Lo [8] GOTHIC LETTER RAIDA..GOTHIC LETTER OTHAL + if (val==0x0001034A) return L; //# Nl GOTHIC LETTER NINE HUNDRED + if ((val>=0x10380) && (val<=0x1039D)) return L; //# Lo [30] UGARITIC LETTER ALPA..UGARITIC LETTER SSU + if (val==0x0001039F) return L; //# Po UGARITIC WORD DIVIDER + if ((val>=0x103A0) && (val<=0x103C3)) return L; //# Lo [36] OLD PERSIAN SIGN A..OLD PERSIAN SIGN HA + if ((val>=0x103C8) && (val<=0x103CF)) return L; //# Lo [8] OLD PERSIAN SIGN AURAMAZDAA..OLD PERSIAN SIGN BUUMISH + if (val==0x000103D0) return L; //# Po OLD PERSIAN WORD DIVIDER + if ((val>=0x103D1) && (val<=0x103D5)) return L; //# Nl [5] OLD PERSIAN NUMBER ONE..OLD PERSIAN NUMBER HUNDRED + if ((val>=0x10400) && (val<=0x1044F)) return L; //# L& [80] DESERET CAPITAL LETTER LONG I..DESERET SMALL LETTER EW + if ((val>=0x10450) && (val<=0x1049D)) return L; //# Lo [78] SHAVIAN LETTER PEEP..OSMANYA LETTER OO + if ((val>=0x104A0) && (val<=0x104A9)) return L; //# Nd [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE + if ((val>=0x12000) && (val<=0x1236E)) return L; //# Lo [879] CUNEIFORM SIGN A..CUNEIFORM SIGN ZUM + if ((val>=0x12400) && (val<=0x12462)) return L; //# Nl [99] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE QUARTER + if ((val>=0x12470) && (val<=0x12473)) return L; //# Po [4] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL TRICOLON + if ((val>=0x1D000) && (val<=0x1D0F5)) return L; //# So [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO + if ((val>=0x1D100) && (val<=0x1D126)) return L; //# So [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2 + if ((val>=0x1D12A) && (val<=0x1D164)) return L; //# So [59] MUSICAL SYMBOL DOUBLE SHARP..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE + if ((val>=0x1D165) && (val<=0x1D166)) return L; //# Mc [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM + if ((val>=0x1D16A) && (val<=0x1D16C)) return L; //# So [3] MUSICAL SYMBOL FINGERED TREMOLO-1..MUSICAL SYMBOL FINGERED TREMOLO-3 + if ((val>=0x1D16D) && (val<=0x1D172)) return L; //# Mc [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5 + if ((val>=0x1D183) && (val<=0x1D184)) return L; //# So [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL ARPEGGIATO DOWN + if ((val>=0x1D18C) && (val<=0x1D1A9)) return L; //# So [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH + if ((val>=0x1D1AE) && (val<=0x1D1DD)) return L; //# So [48] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL PES SUBPUNCTIS + if ((val>=0x1D360) && (val<=0x1D371)) return L; //# No [18] COUNTING ROD UNIT DIGIT ONE..COUNTING ROD TENS DIGIT NINE + if ((val>=0x1D400) && (val<=0x1D454)) return L; //# L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G + if ((val>=0x1D456) && (val<=0x1D49C)) return L; //# L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A + if ((val>=0x1D49E) && (val<=0x1D49F)) return L; //# L& [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D + if (val==0x0001D4A2) return L; //# L& MATHEMATICAL SCRIPT CAPITAL G + if ((val>=0x1D4A5) && (val<=0x1D4A6)) return L; //# L& [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K + if ((val>=0x1D4A9) && (val<=0x1D4AC)) return L; //# L& [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q + if ((val>=0x1D4AE) && (val<=0x1D4B9)) return L; //# L& [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D + if (val==0x0001D4BB) return L; //# L& MATHEMATICAL SCRIPT SMALL F + if ((val>=0x1D4BD) && (val<=0x1D4C3)) return L; //# L& [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N + if ((val>=0x1D4C5) && (val<=0x1D505)) return L; //# L& [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B + if ((val>=0x1D507) && (val<=0x1D50A)) return L; //# L& [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G + if ((val>=0x1D50D) && (val<=0x1D514)) return L; //# L& [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q + if ((val>=0x1D516) && (val<=0x1D51C)) return L; //# L& [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y + if ((val>=0x1D51E) && (val<=0x1D539)) return L; //# L& [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B + if ((val>=0x1D53B) && (val<=0x1D53E)) return L; //# L& [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G + if ((val>=0x1D540) && (val<=0x1D544)) return L; //# L& [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M + if (val==0x0001D546) return L; //# L& MATHEMATICAL DOUBLE-STRUCK CAPITAL O + if ((val>=0x1D54A) && (val<=0x1D550)) return L; //# L& [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y + if ((val>=0x1D552) && (val<=0x1D6A5)) return L; //# L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J + if ((val>=0x1D6A8) && (val<=0x1D6C0)) return L; //# L& [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA + if (val==0x0001D6C1) return L; //# Sm MATHEMATICAL BOLD NABLA + if ((val>=0x1D6C2) && (val<=0x1D6DA)) return L; //# L& [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA + if (val==0x0001D6DB) return L; //# Sm MATHEMATICAL BOLD PARTIAL DIFFERENTIAL + if ((val>=0x1D6DC) && (val<=0x1D6FA)) return L; //# L& [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA + if (val==0x0001D6FB) return L; //# Sm MATHEMATICAL ITALIC NABLA + if ((val>=0x1D6FC) && (val<=0x1D714)) return L; //# L& [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA + if (val==0x0001D715) return L; //# Sm MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL + if ((val>=0x1D716) && (val<=0x1D734)) return L; //# L& [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA + if (val==0x0001D735) return L; //# Sm MATHEMATICAL BOLD ITALIC NABLA + if ((val>=0x1D736) && (val<=0x1D74E)) return L; //# L& [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA + if (val==0x0001D74F) return L; //# Sm MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL + if ((val>=0x1D750) && (val<=0x1D76E)) return L; //# L& [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA + if (val==0x0001D76F) return L; //# Sm MATHEMATICAL SANS-SERIF BOLD NABLA + if ((val>=0x1D770) && (val<=0x1D788)) return L; //# L& [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA + if (val==0x0001D789) return L; //# Sm MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL + if ((val>=0x1D78A) && (val<=0x1D7A8)) return L; //# L& [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA + if (val==0x0001D7A9) return L; //# Sm MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA + if ((val>=0x1D7AA) && (val<=0x1D7C2)) return L; //# L& [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA + if (val==0x0001D7C3) return L; //# Sm MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL + if ((val>=0x1D7C4) && (val<=0x1D7CB)) return L; //# L& [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA + if ((val>=0x20000) && (val<=0x2A6D6)) return L; //# Lo [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6 + if ((val>=0x2F800) && (val<=0x2FA1D)) return L; //# Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D + if ((val>=0xF0000) && (val<=0xFFFFD)) return L; //# Co [65534] .. + if ((val>=0x100000) && (val<=0x10FFFD)) return L; //# Co [65534] .. + if (val==0x00000590) return R; //# Cn + if (val==0x000005BE) return R; //# Po HEBREW PUNCTUATION MAQAF + if (val==0x000005C0) return R; //# Po HEBREW PUNCTUATION PASEQ + if (val==0x000005C3) return R; //# Po HEBREW PUNCTUATION SOF PASUQ + if (val==0x000005C6) return R; //# Po HEBREW PUNCTUATION NUN HAFUKHA + if ((val>=0x05C8) && (val<=0x05CF)) return R; //# Cn [8] .. + if ((val>=0x05D0) && (val<=0x05EA)) return R; //# Lo [27] HEBREW LETTER ALEF..HEBREW LETTER TAV + if ((val>=0x05EB) && (val<=0x05EF)) return R; //# Cn [5] .. + if ((val>=0x05F0) && (val<=0x05F2)) return R; //# Lo [3] HEBREW LIGATURE YIDDISH DOUBLE VAV..HEBREW LIGATURE YIDDISH DOUBLE YOD + if ((val>=0x05F3) && (val<=0x05F4)) return R; //# Po [2] HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATION GERSHAYIM + if ((val>=0x05F5) && (val<=0x05FF)) return R; //# Cn [11] .. + if ((val>=0x07C0) && (val<=0x07C9)) return R; //# Nd [10] NKO DIGIT ZERO..NKO DIGIT NINE + if ((val>=0x07CA) && (val<=0x07EA)) return R; //# Lo [33] NKO LETTER A..NKO LETTER JONA RA + if ((val>=0x07F4) && (val<=0x07F5)) return R; //# Lm [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE + if (val==0x000007FA) return R; //# Lm NKO LAJANYALAN + if ((val>=0x07FB) && (val<=0x08FF)) return R; //# Cn [261] .. + if (val==0x0000200F) return R; //# Cf RIGHT-TO-LEFT MARK + if (val==0x0000FB1D) return R; //# Lo HEBREW LETTER YOD WITH HIRIQ + if ((val>=0xFB1F) && (val<=0xFB28)) return R; //# Lo [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV + if ((val>=0xFB2A) && (val<=0xFB36)) return R; //# Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH + if (val==0x0000FB37) return R; //# Cn + if ((val>=0xFB38) && (val<=0xFB3C)) return R; //# Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH + if (val==0x0000FB3D) return R; //# Cn + if (val==0x0000FB3E) return R; //# Lo HEBREW LETTER MEM WITH DAGESH + if (val==0x0000FB3F) return R; //# Cn + if ((val>=0xFB40) && (val<=0xFB41)) return R; //# Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH + if (val==0x0000FB42) return R; //# Cn + if ((val>=0xFB43) && (val<=0xFB44)) return R; //# Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH + if (val==0x0000FB45) return R; //# Cn + if ((val>=0xFB46) && (val<=0xFB4F)) return R; //# Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED + if ((val>=0x10800) && (val<=0x10805)) return R; //# Lo [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA + if ((val>=0x10806) && (val<=0x10807)) return R; //# Cn [2] .. + if (val==0x00010808) return R; //# Lo CYPRIOT SYLLABLE JO + if (val==0x00010809) return R; //# Cn + if ((val>=0x1080A) && (val<=0x10835)) return R; //# Lo [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO + if (val==0x00010836) return R; //# Cn + if ((val>=0x10837) && (val<=0x10838)) return R; //# Lo [2] CYPRIOT SYLLABLE XA..CYPRIOT SYLLABLE XE + if ((val>=0x10839) && (val<=0x1083B)) return R; //# Cn [3] .. + if (val==0x0001083C) return R; //# Lo CYPRIOT SYLLABLE ZA + if ((val>=0x1083D) && (val<=0x1083E)) return R; //# Cn [2] .. + if (val==0x0001083F) return R; //# Lo CYPRIOT SYLLABLE ZO + if ((val>=0x10840) && (val<=0x108FF)) return R; //# Cn [192] .. + if ((val>=0x10900) && (val<=0x10915)) return R; //# Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU + if ((val>=0x10916) && (val<=0x10919)) return R; //# No [4] PHOENICIAN NUMBER ONE..PHOENICIAN NUMBER ONE HUNDRED + if ((val>=0x1091A) && (val<=0x1091E)) return R; //# Cn [5] .. + if ((val>=0x10920) && (val<=0x109FF)) return R; //# Cn [224] .. + if (val==0x00010A00) return R; //# Lo KHAROSHTHI LETTER A + if (val==0x00010A04) return R; //# Cn + if ((val>=0x10A07) && (val<=0x10A0B)) return R; //# Cn [5] .. + if ((val>=0x10A10) && (val<=0x10A13)) return R; //# Lo [4] KHAROSHTHI LETTER KA..KHAROSHTHI LETTER GHA + if (val==0x00010A14) return R; //# Cn + if ((val>=0x10A15) && (val<=0x10A17)) return R; //# Lo [3] KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA + if (val==0x00010A18) return R; //# Cn + if ((val>=0x10A19) && (val<=0x10A33)) return R; //# Lo [27] KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER TTTHA + if ((val>=0x10A34) && (val<=0x10A37)) return R; //# Cn [4] .. + if ((val>=0x10A3B) && (val<=0x10A3E)) return R; //# Cn [4] .. + if ((val>=0x10A40) && (val<=0x10A47)) return R; //# No [8] KHAROSHTHI DIGIT ONE..KHAROSHTHI NUMBER ONE THOUSAND + if ((val>=0x10A48) && (val<=0x10A4F)) return R; //# Cn [8] .. + if ((val>=0x10A50) && (val<=0x10A58)) return R; //# Po [9] KHAROSHTHI PUNCTUATION DOT..KHAROSHTHI PUNCTUATION LINES + if ((val>=0x10A59) && (val<=0x10FFF)) return R; //# Cn [1447] .. + if ((val>=0x0030) && (val<=0x0039)) return EN; //# Nd [10] DIGIT ZERO..DIGIT NINE + if ((val>=0x00B2) && (val<=0x00B3)) return EN; //# No [2] SUPERSCRIPT TWO..SUPERSCRIPT THREE + if (val==0x000000B9) return EN; //# No SUPERSCRIPT ONE + if ((val>=0x06F0) && (val<=0x06F9)) return EN; //# Nd [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE + if (val==0x00002070) return EN; //# No SUPERSCRIPT ZERO + if ((val>=0x2074) && (val<=0x2079)) return EN; //# No [6] SUPERSCRIPT FOUR..SUPERSCRIPT NINE + if ((val>=0x2080) && (val<=0x2089)) return EN; //# No [10] SUBSCRIPT ZERO..SUBSCRIPT NINE + if ((val>=0x2488) && (val<=0x249B)) return EN; //# No [20] DIGIT ONE FULL STOP..NUMBER TWENTY FULL STOP + if ((val>=0xFF10) && (val<=0xFF19)) return EN; //# Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE + if ((val>=0x1D7CE) && (val<=0x1D7FF)) return EN; //# Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE + if (val==0x0000002B) return ES; //# Sm PLUS SIGN + if (val==0x0000002D) return ES; //# Pd HYPHEN-MINUS + if ((val>=0x207A) && (val<=0x207B)) return ES; //# Sm [2] SUPERSCRIPT PLUS SIGN..SUPERSCRIPT MINUS + if ((val>=0x208A) && (val<=0x208B)) return ES; //# Sm [2] SUBSCRIPT PLUS SIGN..SUBSCRIPT MINUS + if (val==0x00002212) return ES; //# Sm MINUS SIGN + if (val==0x0000FB29) return ES; //# Sm HEBREW LETTER ALTERNATIVE PLUS SIGN + if (val==0x0000FE62) return ES; //# Sm SMALL PLUS SIGN + if (val==0x0000FE63) return ES; //# Pd SMALL HYPHEN-MINUS + if (val==0x0000FF0B) return ES; //# Sm FULLWIDTH PLUS SIGN + if (val==0x0000FF0D) return ES; //# Pd FULLWIDTH HYPHEN-MINUS + if (val==0x00000023) return ET; //# Po NUMBER SIGN + if (val==0x00000024) return ET; //# Sc DOLLAR SIGN + if (val==0x00000025) return ET; //# Po PERCENT SIGN + if ((val>=0x00A2) && (val<=0x00A5)) return ET; //# Sc [4] CENT SIGN..YEN SIGN + if (val==0x000000B0) return ET; //# So DEGREE SIGN + if (val==0x000000B1) return ET; //# Sm PLUS-MINUS SIGN + if (val==0x0000066A) return ET; //# Po ARABIC PERCENT SIGN + if ((val>=0x09F2) && (val<=0x09F3)) return ET; //# Sc [2] BENGALI RUPEE MARK..BENGALI RUPEE SIGN + if (val==0x00000AF1) return ET; //# Sc GUJARATI RUPEE SIGN + if (val==0x00000BF9) return ET; //# Sc TAMIL RUPEE SIGN + if (val==0x00000E3F) return ET; //# Sc THAI CURRENCY SYMBOL BAHT + if (val==0x000017DB) return ET; //# Sc KHMER CURRENCY SYMBOL RIEL + if ((val>=0x2030) && (val<=0x2034)) return ET; //# Po [5] PER MILLE SIGN..TRIPLE PRIME + if ((val>=0x20A0) && (val<=0x20B5)) return ET; //# Sc [22] EURO-CURRENCY SIGN..CEDI SIGN + if (val==0x0000212E) return ET; //# So ESTIMATED SYMBOL + if (val==0x00002213) return ET; //# Sm MINUS-OR-PLUS SIGN + if (val==0x0000FE5F) return ET; //# Po SMALL NUMBER SIGN + if (val==0x0000FE69) return ET; //# Sc SMALL DOLLAR SIGN + if (val==0x0000FE6A) return ET; //# Po SMALL PERCENT SIGN + if (val==0x0000FF03) return ET; //# Po FULLWIDTH NUMBER SIGN + if (val==0x0000FF04) return ET; //# Sc FULLWIDTH DOLLAR SIGN + if (val==0x0000FF05) return ET; //# Po FULLWIDTH PERCENT SIGN + if ((val>=0xFFE0) && (val<=0xFFE1)) return ET; //# Sc [2] FULLWIDTH CENT SIGN..FULLWIDTH POUND SIGN + if ((val>=0xFFE5) && (val<=0xFFE6)) return ET; //# Sc [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN + if ((val>=0x0660) && (val<=0x0669)) return AN; //# Nd [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE + if ((val>=0x066B) && (val<=0x066C)) return AN; //# Po [2] ARABIC DECIMAL SEPARATOR..ARABIC THOUSANDS SEPARATOR + if (val==0x0000002C) return CS; //# Po COMMA + if ((val>=0x002E) && (val<=0x002F)) return CS; //# Po [2] FULL STOP..SOLIDUS + if (val==0x0000003A) return CS; //# Po COLON + if (val==0x000000A0) return CS; //# Zs NO-BREAK SPACE + if (val==0x0000060C) return CS; //# Po ARABIC COMMA + if (val==0x0000202F) return CS; //# Zs NARROW NO-BREAK SPACE + if (val==0x00002044) return CS; //# Sm FRACTION SLASH + if (val==0x0000FE50) return CS; //# Po SMALL COMMA + if (val==0x0000FE52) return CS; //# Po SMALL FULL STOP + if (val==0x0000FE55) return CS; //# Po SMALL COLON + if (val==0x0000FF0C) return CS; //# Po FULLWIDTH COMMA + if ((val>=0xFF0E) && (val<=0xFF0F)) return CS; //# Po [2] FULLWIDTH FULL STOP..FULLWIDTH SOLIDUS + if (val==0x0000FF1A) return CS; //# Po FULLWIDTH COLON + if (val==0x0000000A) return B; //# Cc + if (val==0x0000000D) return B; //# Cc + if ((val>=0x001C) && (val<=0x001E)) return B; //# Cc [3] .. + if (val==0x00000085) return B; //# Cc + if (val==0x00002029) return B; //# Zp PARAGRAPH SEPARATOR + if (val==0x00000009) return S; //# Cc + if (val==0x0000000B) return S; //# Cc + if (val==0x0000001F) return S; //# Cc + if (val==0x0000000C) return WS; //# Cc + if (val==0x00000020) return WS; //# Zs SPACE + if (val==0x00001680) return WS; //# Zs OGHAM SPACE MARK + if (val==0x0000180E) return WS; //# Zs MONGOLIAN VOWEL SEPARATOR + if ((val>=0x2000) && (val<=0x200A)) return WS; //# Zs [11] EN QUAD..HAIR SPACE + if (val==0x00002028) return WS; //# Zl LINE SEPARATOR + if (val==0x0000205F) return WS; //# Zs MEDIUM MATHEMATICAL SPACE + if (val==0x00003000) return WS; //# Zs IDEOGRAPHIC SPACE + if ((val>=0x0021) && (val<=0x0022)) return ON; //# Po [2] EXCLAMATION MARK..QUOTATION MARK + if ((val>=0x0026) && (val<=0x0027)) return ON; //# Po [2] AMPERSAND..APOSTROPHE + if (val==0x00000028) return ON; //# Ps LEFT PARENTHESIS + if (val==0x00000029) return ON; //# Pe RIGHT PARENTHESIS + if (val==0x0000002A) return ON; //# Po ASTERISK + if (val==0x0000003B) return ON; //# Po SEMICOLON + if ((val>=0x003C) && (val<=0x003E)) return ON; //# Sm [3] LESS-THAN SIGN..GREATER-THAN SIGN + if ((val>=0x003F) && (val<=0x0040)) return ON; //# Po [2] QUESTION MARK..COMMERCIAL AT + if (val==0x0000005B) return ON; //# Ps LEFT SQUARE BRACKET + if (val==0x0000005C) return ON; //# Po REVERSE SOLIDUS + if (val==0x0000005D) return ON; //# Pe RIGHT SQUARE BRACKET + if (val==0x0000005E) return ON; //# Sk CIRCUMFLEX ACCENT + if (val==0x0000005F) return ON; //# Pc LOW LINE + if (val==0x00000060) return ON; //# Sk GRAVE ACCENT + if (val==0x0000007B) return ON; //# Ps LEFT CURLY BRACKET + if (val==0x0000007C) return ON; //# Sm VERTICAL LINE + if (val==0x0000007D) return ON; //# Pe RIGHT CURLY BRACKET + if (val==0x0000007E) return ON; //# Sm TILDE + if (val==0x000000A1) return ON; //# Po INVERTED EXCLAMATION MARK + if ((val>=0x00A6) && (val<=0x00A7)) return ON; //# So [2] BROKEN BAR..SECTION SIGN + if (val==0x000000A8) return ON; //# Sk DIAERESIS + if (val==0x000000A9) return ON; //# So COPYRIGHT SIGN + if (val==0x000000AB) return ON; //# Pi LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + if (val==0x000000AC) return ON; //# Sm NOT SIGN + if (val==0x000000AE) return ON; //# So REGISTERED SIGN + if (val==0x000000AF) return ON; //# Sk MACRON + if (val==0x000000B4) return ON; //# Sk ACUTE ACCENT + if (val==0x000000B6) return ON; //# So PILCROW SIGN + if (val==0x000000B7) return ON; //# Po MIDDLE DOT + if (val==0x000000B8) return ON; //# Sk CEDILLA + if (val==0x000000BB) return ON; //# Pf RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + if ((val>=0x00BC) && (val<=0x00BE)) return ON; //# No [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS + if (val==0x000000BF) return ON; //# Po INVERTED QUESTION MARK + if (val==0x000000D7) return ON; //# Sm MULTIPLICATION SIGN + if (val==0x000000F7) return ON; //# Sm DIVISION SIGN + if ((val>=0x02B9) && (val<=0x02BA)) return ON; //# Lm [2] MODIFIER LETTER PRIME..MODIFIER LETTER DOUBLE PRIME + if ((val>=0x02C2) && (val<=0x02C5)) return ON; //# Sk [4] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER DOWN ARROWHEAD + if ((val>=0x02C6) && (val<=0x02CF)) return ON; //# Lm [10] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER LOW ACUTE ACCENT + if ((val>=0x02D2) && (val<=0x02DF)) return ON; //# Sk [14] MODIFIER LETTER CENTRED RIGHT HALF RING..MODIFIER LETTER CROSS ACCENT + if ((val>=0x02E5) && (val<=0x02ED)) return ON; //# Sk [9] MODIFIER LETTER EXTRA-HIGH TONE BAR..MODIFIER LETTER UNASPIRATED + if ((val>=0x02EF) && (val<=0x02FF)) return ON; //# Sk [17] MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER LETTER LOW LEFT ARROW + if ((val>=0x0374) && (val<=0x0375)) return ON; //# Sk [2] GREEK NUMERAL SIGN..GREEK LOWER NUMERAL SIGN + if (val==0x0000037E) return ON; //# Po GREEK QUESTION MARK + if ((val>=0x0384) && (val<=0x0385)) return ON; //# Sk [2] GREEK TONOS..GREEK DIALYTIKA TONOS + if (val==0x00000387) return ON; //# Po GREEK ANO TELEIA + if (val==0x000003F6) return ON; //# Sm GREEK REVERSED LUNATE EPSILON SYMBOL + if (val==0x0000058A) return ON; //# Pd ARMENIAN HYPHEN + if ((val>=0x060E) && (val<=0x060F)) return ON; //# So [2] ARABIC POETIC VERSE SIGN..ARABIC SIGN MISRA + if (val==0x000006E9) return ON; //# So ARABIC PLACE OF SAJDAH + if (val==0x000007F6) return ON; //# So NKO SYMBOL OO DENNEN + if ((val>=0x07F7) && (val<=0x07F9)) return ON; //# Po [3] NKO SYMBOL GBAKURUNEN..NKO EXCLAMATION MARK + if ((val>=0x0BF3) && (val<=0x0BF8)) return ON; //# So [6] TAMIL DAY SIGN..TAMIL AS ABOVE SIGN + if (val==0x00000BFA) return ON; //# So TAMIL NUMBER SIGN + if ((val>=0x0CF1) && (val<=0x0CF2)) return ON; //# So [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA + if (val==0x00000F3A) return ON; //# Ps TIBETAN MARK GUG RTAGS GYON + if (val==0x00000F3B) return ON; //# Pe TIBETAN MARK GUG RTAGS GYAS + if (val==0x00000F3C) return ON; //# Ps TIBETAN MARK ANG KHANG GYON + if (val==0x00000F3D) return ON; //# Pe TIBETAN MARK ANG KHANG GYAS + if ((val>=0x1390) && (val<=0x1399)) return ON; //# So [10] ETHIOPIC TONAL MARK YIZET..ETHIOPIC TONAL MARK KURT + if (val==0x0000169B) return ON; //# Ps OGHAM FEATHER MARK + if (val==0x0000169C) return ON; //# Pe OGHAM REVERSED FEATHER MARK + if ((val>=0x17F0) && (val<=0x17F9)) return ON; //# No [10] KHMER SYMBOL LEK ATTAK SON..KHMER SYMBOL LEK ATTAK PRAM-BUON + if ((val>=0x1800) && (val<=0x1805)) return ON; //# Po [6] MONGOLIAN BIRGA..MONGOLIAN FOUR DOTS + if (val==0x00001806) return ON; //# Pd MONGOLIAN TODO SOFT HYPHEN + if ((val>=0x1807) && (val<=0x180A)) return ON; //# Po [4] MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER..MONGOLIAN NIRUGU + if (val==0x00001940) return ON; //# So LIMBU SIGN LOO + if ((val>=0x1944) && (val<=0x1945)) return ON; //# Po [2] LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK + if ((val>=0x19DE) && (val<=0x19DF)) return ON; //# Po [2] NEW TAI LUE SIGN LAE..NEW TAI LUE SIGN LAEV + if ((val>=0x19E0) && (val<=0x19FF)) return ON; //# So [32] KHMER SYMBOL PATHAMASAT..KHMER SYMBOL DAP-PRAM ROC + if (val==0x00001FBD) return ON; //# Sk GREEK KORONIS + if ((val>=0x1FBF) && (val<=0x1FC1)) return ON; //# Sk [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI + if ((val>=0x1FCD) && (val<=0x1FCF)) return ON; //# Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI + if ((val>=0x1FDD) && (val<=0x1FDF)) return ON; //# Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI + if ((val>=0x1FED) && (val<=0x1FEF)) return ON; //# Sk [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA + if ((val>=0x1FFD) && (val<=0x1FFE)) return ON; //# Sk [2] GREEK OXIA..GREEK DASIA + if ((val>=0x2010) && (val<=0x2015)) return ON; //# Pd [6] HYPHEN..HORIZONTAL BAR + if ((val>=0x2016) && (val<=0x2017)) return ON; //# Po [2] DOUBLE VERTICAL LINE..DOUBLE LOW LINE + if (val==0x00002018) return ON; //# Pi LEFT SINGLE QUOTATION MARK + if (val==0x00002019) return ON; //# Pf RIGHT SINGLE QUOTATION MARK + if (val==0x0000201A) return ON; //# Ps SINGLE LOW-9 QUOTATION MARK + if ((val>=0x201B) && (val<=0x201C)) return ON; //# Pi [2] SINGLE HIGH-REVERSED-9 QUOTATION MARK..LEFT DOUBLE QUOTATION MARK + if (val==0x0000201D) return ON; //# Pf RIGHT DOUBLE QUOTATION MARK + if (val==0x0000201E) return ON; //# Ps DOUBLE LOW-9 QUOTATION MARK + if (val==0x0000201F) return ON; //# Pi DOUBLE HIGH-REVERSED-9 QUOTATION MARK + if ((val>=0x2020) && (val<=0x2027)) return ON; //# Po [8] DAGGER..HYPHENATION POINT + if ((val>=0x2035) && (val<=0x2038)) return ON; //# Po [4] REVERSED PRIME..CARET + if (val==0x00002039) return ON; //# Pi SINGLE LEFT-POINTING ANGLE QUOTATION MARK + if (val==0x0000203A) return ON; //# Pf SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + if ((val>=0x203B) && (val<=0x203E)) return ON; //# Po [4] REFERENCE MARK..OVERLINE + if ((val>=0x203F) && (val<=0x2040)) return ON; //# Pc [2] UNDERTIE..CHARACTER TIE + if ((val>=0x2041) && (val<=0x2043)) return ON; //# Po [3] CARET INSERTION POINT..HYPHEN BULLET + if (val==0x00002045) return ON; //# Ps LEFT SQUARE BRACKET WITH QUILL + if (val==0x00002046) return ON; //# Pe RIGHT SQUARE BRACKET WITH QUILL + if ((val>=0x2047) && (val<=0x2051)) return ON; //# Po [11] DOUBLE QUESTION MARK..TWO ASTERISKS ALIGNED VERTICALLY + if (val==0x00002052) return ON; //# Sm COMMERCIAL MINUS SIGN + if (val==0x00002053) return ON; //# Po SWUNG DASH + if (val==0x00002054) return ON; //# Pc INVERTED UNDERTIE + if ((val>=0x2055) && (val<=0x205E)) return ON; //# Po [10] FLOWER PUNCTUATION MARK..VERTICAL FOUR DOTS + if (val==0x0000207C) return ON; //# Sm SUPERSCRIPT EQUALS SIGN + if (val==0x0000207D) return ON; //# Ps SUPERSCRIPT LEFT PARENTHESIS + if (val==0x0000207E) return ON; //# Pe SUPERSCRIPT RIGHT PARENTHESIS + if (val==0x0000208C) return ON; //# Sm SUBSCRIPT EQUALS SIGN + if (val==0x0000208D) return ON; //# Ps SUBSCRIPT LEFT PARENTHESIS + if (val==0x0000208E) return ON; //# Pe SUBSCRIPT RIGHT PARENTHESIS + if ((val>=0x2100) && (val<=0x2101)) return ON; //# So [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT + if ((val>=0x2103) && (val<=0x2106)) return ON; //# So [4] DEGREE CELSIUS..CADA UNA + if ((val>=0x2108) && (val<=0x2109)) return ON; //# So [2] SCRUPLE..DEGREE FAHRENHEIT + if (val==0x00002114) return ON; //# So L B BAR SYMBOL + if ((val>=0x2116) && (val<=0x2118)) return ON; //# So [3] NUMERO SIGN..SCRIPT CAPITAL P + if ((val>=0x211E) && (val<=0x2123)) return ON; //# So [6] PRESCRIPTION TAKE..VERSICLE + if (val==0x00002125) return ON; //# So OUNCE SIGN + if (val==0x00002127) return ON; //# So INVERTED OHM SIGN + if (val==0x00002129) return ON; //# So TURNED GREEK SMALL LETTER IOTA + if ((val>=0x213A) && (val<=0x213B)) return ON; //# So [2] ROTATED CAPITAL Q..FACSIMILE SIGN + if ((val>=0x2140) && (val<=0x2144)) return ON; //# Sm [5] DOUBLE-STRUCK N-ARY SUMMATION..TURNED SANS-SERIF CAPITAL Y + if (val==0x0000214A) return ON; //# So PROPERTY LINE + if (val==0x0000214B) return ON; //# Sm TURNED AMPERSAND + if ((val>=0x214C) && (val<=0x214D)) return ON; //# So [2] PER SIGN..AKTIESELSKAB + if ((val>=0x2153) && (val<=0x215F)) return ON; //# No [13] VULGAR FRACTION ONE THIRD..FRACTION NUMERATOR ONE + if ((val>=0x2190) && (val<=0x2194)) return ON; //# Sm [5] LEFTWARDS ARROW..LEFT RIGHT ARROW + if ((val>=0x2195) && (val<=0x2199)) return ON; //# So [5] UP DOWN ARROW..SOUTH WEST ARROW + if ((val>=0x219A) && (val<=0x219B)) return ON; //# Sm [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE + if ((val>=0x219C) && (val<=0x219F)) return ON; //# So [4] LEFTWARDS WAVE ARROW..UPWARDS TWO HEADED ARROW + if (val==0x000021A0) return ON; //# Sm RIGHTWARDS TWO HEADED ARROW + if ((val>=0x21A1) && (val<=0x21A2)) return ON; //# So [2] DOWNWARDS TWO HEADED ARROW..LEFTWARDS ARROW WITH TAIL + if (val==0x000021A3) return ON; //# Sm RIGHTWARDS ARROW WITH TAIL + if ((val>=0x21A4) && (val<=0x21A5)) return ON; //# So [2] LEFTWARDS ARROW FROM BAR..UPWARDS ARROW FROM BAR + if (val==0x000021A6) return ON; //# Sm RIGHTWARDS ARROW FROM BAR + if ((val>=0x21A7) && (val<=0x21AD)) return ON; //# So [7] DOWNWARDS ARROW FROM BAR..LEFT RIGHT WAVE ARROW + if (val==0x000021AE) return ON; //# Sm LEFT RIGHT ARROW WITH STROKE + if ((val>=0x21AF) && (val<=0x21CD)) return ON; //# So [31] DOWNWARDS ZIGZAG ARROW..LEFTWARDS DOUBLE ARROW WITH STROKE + if ((val>=0x21CE) && (val<=0x21CF)) return ON; //# Sm [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE + if ((val>=0x21D0) && (val<=0x21D1)) return ON; //# So [2] LEFTWARDS DOUBLE ARROW..UPWARDS DOUBLE ARROW + if (val==0x000021D2) return ON; //# Sm RIGHTWARDS DOUBLE ARROW + if (val==0x000021D3) return ON; //# So DOWNWARDS DOUBLE ARROW + if (val==0x000021D4) return ON; //# Sm LEFT RIGHT DOUBLE ARROW + if ((val>=0x21D5) && (val<=0x21F3)) return ON; //# So [31] UP DOWN DOUBLE ARROW..UP DOWN WHITE ARROW + if ((val>=0x21F4) && (val<=0x2211)) return ON; //# Sm [30] RIGHT ARROW WITH SMALL CIRCLE..N-ARY SUMMATION + if ((val>=0x2214) && (val<=0x22FF)) return ON; //# Sm [236] DOT PLUS..Z NOTATION BAG MEMBERSHIP + if ((val>=0x2300) && (val<=0x2307)) return ON; //# So [8] DIAMETER SIGN..WAVY LINE + if ((val>=0x2308) && (val<=0x230B)) return ON; //# Sm [4] LEFT CEILING..RIGHT FLOOR + if ((val>=0x230C) && (val<=0x231F)) return ON; //# So [20] BOTTOM RIGHT CROP..BOTTOM RIGHT CORNER + if ((val>=0x2320) && (val<=0x2321)) return ON; //# Sm [2] TOP HALF INTEGRAL..BOTTOM HALF INTEGRAL + if ((val>=0x2322) && (val<=0x2328)) return ON; //# So [7] FROWN..KEYBOARD + if (val==0x00002329) return ON; //# Ps LEFT-POINTING ANGLE BRACKET + if (val==0x0000232A) return ON; //# Pe RIGHT-POINTING ANGLE BRACKET + if ((val>=0x232B) && (val<=0x2335)) return ON; //# So [11] ERASE TO THE LEFT..COUNTERSINK + if (val==0x0000237B) return ON; //# So NOT CHECK MARK + if (val==0x0000237C) return ON; //# Sm RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW + if ((val>=0x237D) && (val<=0x2394)) return ON; //# So [24] SHOULDERED OPEN BOX..SOFTWARE-FUNCTION SYMBOL + if ((val>=0x2396) && (val<=0x239A)) return ON; //# So [5] DECIMAL SEPARATOR KEY SYMBOL..CLEAR SCREEN SYMBOL + if ((val>=0x239B) && (val<=0x23B3)) return ON; //# Sm [25] LEFT PARENTHESIS UPPER HOOK..SUMMATION BOTTOM + if ((val>=0x23B4) && (val<=0x23DB)) return ON; //# So [40] TOP SQUARE BRACKET..FUSE + if ((val>=0x23DC) && (val<=0x23E1)) return ON; //# Sm [6] TOP PARENTHESIS..BOTTOM TORTOISE SHELL BRACKET + if ((val>=0x23E2) && (val<=0x23E7)) return ON; //# So [6] WHITE TRAPEZIUM..ELECTRICAL INTERSECTION + if ((val>=0x2400) && (val<=0x2426)) return ON; //# So [39] SYMBOL FOR NULL..SYMBOL FOR SUBSTITUTE FORM TWO + if ((val>=0x2440) && (val<=0x244A)) return ON; //# So [11] OCR HOOK..OCR DOUBLE BACKSLASH + if ((val>=0x2460) && (val<=0x2487)) return ON; //# No [40] CIRCLED DIGIT ONE..PARENTHESIZED NUMBER TWENTY + if ((val>=0x24EA) && (val<=0x24FF)) return ON; //# No [22] CIRCLED DIGIT ZERO..NEGATIVE CIRCLED DIGIT ZERO + if ((val>=0x2500) && (val<=0x25B6)) return ON; //# So [183] BOX DRAWINGS LIGHT HORIZONTAL..BLACK RIGHT-POINTING TRIANGLE + if (val==0x000025B7) return ON; //# Sm WHITE RIGHT-POINTING TRIANGLE + if ((val>=0x25B8) && (val<=0x25C0)) return ON; //# So [9] BLACK RIGHT-POINTING SMALL TRIANGLE..BLACK LEFT-POINTING TRIANGLE + if (val==0x000025C1) return ON; //# Sm WHITE LEFT-POINTING TRIANGLE + if ((val>=0x25C2) && (val<=0x25F7)) return ON; //# So [54] BLACK LEFT-POINTING SMALL TRIANGLE..WHITE CIRCLE WITH UPPER RIGHT QUADRANT + if ((val>=0x25F8) && (val<=0x25FF)) return ON; //# Sm [8] UPPER LEFT TRIANGLE..LOWER RIGHT TRIANGLE + if ((val>=0x2600) && (val<=0x266E)) return ON; //# So [111] BLACK SUN WITH RAYS..MUSIC NATURAL SIGN + if (val==0x0000266F) return ON; //# Sm MUSIC SHARP SIGN + if ((val>=0x2670) && (val<=0x269C)) return ON; //# So [45] WEST SYRIAC CROSS..FLEUR-DE-LIS + if ((val>=0x26A0) && (val<=0x26AB)) return ON; //# So [12] WARNING SIGN..MEDIUM BLACK CIRCLE + if ((val>=0x26AD) && (val<=0x26B2)) return ON; //# So [6] MARRIAGE SYMBOL..NEUTER + if ((val>=0x2701) && (val<=0x2704)) return ON; //# So [4] UPPER BLADE SCISSORS..WHITE SCISSORS + if ((val>=0x2706) && (val<=0x2709)) return ON; //# So [4] TELEPHONE LOCATION SIGN..ENVELOPE + if ((val>=0x270C) && (val<=0x2727)) return ON; //# So [28] VICTORY HAND..WHITE FOUR POINTED STAR + if ((val>=0x2729) && (val<=0x274B)) return ON; //# So [35] STRESS OUTLINED WHITE STAR..HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK + if (val==0x0000274D) return ON; //# So SHADOWED WHITE CIRCLE + if ((val>=0x274F) && (val<=0x2752)) return ON; //# So [4] LOWER RIGHT DROP-SHADOWED WHITE SQUARE..UPPER RIGHT SHADOWED WHITE SQUARE + if (val==0x00002756) return ON; //# So BLACK DIAMOND MINUS WHITE X + if ((val>=0x2758) && (val<=0x275E)) return ON; //# So [7] LIGHT VERTICAL BAR..HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT + if ((val>=0x2761) && (val<=0x2767)) return ON; //# So [7] CURVED STEM PARAGRAPH SIGN ORNAMENT..ROTATED FLORAL HEART BULLET + if (val==0x00002768) return ON; //# Ps MEDIUM LEFT PARENTHESIS ORNAMENT + if (val==0x00002769) return ON; //# Pe MEDIUM RIGHT PARENTHESIS ORNAMENT + if (val==0x0000276A) return ON; //# Ps MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT + if (val==0x0000276B) return ON; //# Pe MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT + if (val==0x0000276C) return ON; //# Ps MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT + if (val==0x0000276D) return ON; //# Pe MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT + if (val==0x0000276E) return ON; //# Ps HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT + if (val==0x0000276F) return ON; //# Pe HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT + if (val==0x00002770) return ON; //# Ps HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT + if (val==0x00002771) return ON; //# Pe HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT + if (val==0x00002772) return ON; //# Ps LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT + if (val==0x00002773) return ON; //# Pe LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT + if (val==0x00002774) return ON; //# Ps MEDIUM LEFT CURLY BRACKET ORNAMENT + if (val==0x00002775) return ON; //# Pe MEDIUM RIGHT CURLY BRACKET ORNAMENT + if ((val>=0x2776) && (val<=0x2793)) return ON; //# No [30] DINGBAT NEGATIVE CIRCLED DIGIT ONE..DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN + if (val==0x00002794) return ON; //# So HEAVY WIDE-HEADED RIGHTWARDS ARROW + if ((val>=0x2798) && (val<=0x27AF)) return ON; //# So [24] HEAVY SOUTH EAST ARROW..NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW + if ((val>=0x27B1) && (val<=0x27BE)) return ON; //# So [14] NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW..OPEN-OUTLINED RIGHTWARDS ARROW + if ((val>=0x27C0) && (val<=0x27C4)) return ON; //# Sm [5] THREE DIMENSIONAL ANGLE..OPEN SUPERSET + if (val==0x000027C5) return ON; //# Ps LEFT S-SHAPED BAG DELIMITER + if (val==0x000027C6) return ON; //# Pe RIGHT S-SHAPED BAG DELIMITER + if ((val>=0x27C7) && (val<=0x27CA)) return ON; //# Sm [4] OR WITH DOT INSIDE..VERTICAL BAR WITH HORIZONTAL STROKE + if ((val>=0x27D0) && (val<=0x27E5)) return ON; //# Sm [22] WHITE DIAMOND WITH CENTRED DOT..WHITE SQUARE WITH RIGHTWARDS TICK + if (val==0x000027E6) return ON; //# Ps MATHEMATICAL LEFT WHITE SQUARE BRACKET + if (val==0x000027E7) return ON; //# Pe MATHEMATICAL RIGHT WHITE SQUARE BRACKET + if (val==0x000027E8) return ON; //# Ps MATHEMATICAL LEFT ANGLE BRACKET + if (val==0x000027E9) return ON; //# Pe MATHEMATICAL RIGHT ANGLE BRACKET + if (val==0x000027EA) return ON; //# Ps MATHEMATICAL LEFT DOUBLE ANGLE BRACKET + if (val==0x000027EB) return ON; //# Pe MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET + if ((val>=0x27F0) && (val<=0x27FF)) return ON; //# Sm [16] UPWARDS QUADRUPLE ARROW..LONG RIGHTWARDS SQUIGGLE ARROW + if ((val>=0x2900) && (val<=0x2982)) return ON; //# Sm [131] RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE..Z NOTATION TYPE COLON + if (val==0x00002983) return ON; //# Ps LEFT WHITE CURLY BRACKET + if (val==0x00002984) return ON; //# Pe RIGHT WHITE CURLY BRACKET + if (val==0x00002985) return ON; //# Ps LEFT WHITE PARENTHESIS + if (val==0x00002986) return ON; //# Pe RIGHT WHITE PARENTHESIS + if (val==0x00002987) return ON; //# Ps Z NOTATION LEFT IMAGE BRACKET + if (val==0x00002988) return ON; //# Pe Z NOTATION RIGHT IMAGE BRACKET + if (val==0x00002989) return ON; //# Ps Z NOTATION LEFT BINDING BRACKET + if (val==0x0000298A) return ON; //# Pe Z NOTATION RIGHT BINDING BRACKET + if (val==0x0000298B) return ON; //# Ps LEFT SQUARE BRACKET WITH UNDERBAR + if (val==0x0000298C) return ON; //# Pe RIGHT SQUARE BRACKET WITH UNDERBAR + if (val==0x0000298D) return ON; //# Ps LEFT SQUARE BRACKET WITH TICK IN TOP CORNER + if (val==0x0000298E) return ON; //# Pe RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER + if (val==0x0000298F) return ON; //# Ps LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER + if (val==0x00002990) return ON; //# Pe RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER + if (val==0x00002991) return ON; //# Ps LEFT ANGLE BRACKET WITH DOT + if (val==0x00002992) return ON; //# Pe RIGHT ANGLE BRACKET WITH DOT + if (val==0x00002993) return ON; //# Ps LEFT ARC LESS-THAN BRACKET + if (val==0x00002994) return ON; //# Pe RIGHT ARC GREATER-THAN BRACKET + if (val==0x00002995) return ON; //# Ps DOUBLE LEFT ARC GREATER-THAN BRACKET + if (val==0x00002996) return ON; //# Pe DOUBLE RIGHT ARC LESS-THAN BRACKET + if (val==0x00002997) return ON; //# Ps LEFT BLACK TORTOISE SHELL BRACKET + if (val==0x00002998) return ON; //# Pe RIGHT BLACK TORTOISE SHELL BRACKET + if ((val>=0x2999) && (val<=0x29D7)) return ON; //# Sm [63] DOTTED FENCE..BLACK HOURGLASS + if (val==0x000029D8) return ON; //# Ps LEFT WIGGLY FENCE + if (val==0x000029D9) return ON; //# Pe RIGHT WIGGLY FENCE + if (val==0x000029DA) return ON; //# Ps LEFT DOUBLE WIGGLY FENCE + if (val==0x000029DB) return ON; //# Pe RIGHT DOUBLE WIGGLY FENCE + if ((val>=0x29DC) && (val<=0x29FB)) return ON; //# Sm [32] INCOMPLETE INFINITY..TRIPLE PLUS + if (val==0x000029FC) return ON; //# Ps LEFT-POINTING CURVED ANGLE BRACKET + if (val==0x000029FD) return ON; //# Pe RIGHT-POINTING CURVED ANGLE BRACKET + if ((val>=0x29FE) && (val<=0x2AFF)) return ON; //# Sm [258] TINY..N-ARY WHITE VERTICAL BAR + if ((val>=0x2B00) && (val<=0x2B1A)) return ON; //# So [27] NORTH EAST WHITE ARROW..DOTTED SQUARE + if ((val>=0x2B20) && (val<=0x2B23)) return ON; //# So [4] WHITE PENTAGON..HORIZONTAL BLACK HEXAGON + if ((val>=0x2CE5) && (val<=0x2CEA)) return ON; //# So [6] COPTIC SYMBOL MI RO..COPTIC SYMBOL SHIMA SIMA + if ((val>=0x2CF9) && (val<=0x2CFC)) return ON; //# Po [4] COPTIC OLD NUBIAN FULL STOP..COPTIC OLD NUBIAN VERSE DIVIDER + if (val==0x00002CFD) return ON; //# No COPTIC FRACTION ONE HALF + if ((val>=0x2CFE) && (val<=0x2CFF)) return ON; //# Po [2] COPTIC FULL STOP..COPTIC MORPHOLOGICAL DIVIDER + if ((val>=0x2E00) && (val<=0x2E01)) return ON; //# Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER + if (val==0x00002E02) return ON; //# Pi LEFT SUBSTITUTION BRACKET + if (val==0x00002E03) return ON; //# Pf RIGHT SUBSTITUTION BRACKET + if (val==0x00002E04) return ON; //# Pi LEFT DOTTED SUBSTITUTION BRACKET + if (val==0x00002E05) return ON; //# Pf RIGHT DOTTED SUBSTITUTION BRACKET + if ((val>=0x2E06) && (val<=0x2E08)) return ON; //# Po [3] RAISED INTERPOLATION MARKER..DOTTED TRANSPOSITION MARKER + if (val==0x00002E09) return ON; //# Pi LEFT TRANSPOSITION BRACKET + if (val==0x00002E0A) return ON; //# Pf RIGHT TRANSPOSITION BRACKET + if (val==0x00002E0B) return ON; //# Po RAISED SQUARE + if (val==0x00002E0C) return ON; //# Pi LEFT RAISED OMISSION BRACKET + if (val==0x00002E0D) return ON; //# Pf RIGHT RAISED OMISSION BRACKET + if ((val>=0x2E0E) && (val<=0x2E16)) return ON; //# Po [9] EDITORIAL CORONIS..DOTTED RIGHT-POINTING ANGLE + if (val==0x00002E17) return ON; //# Pd DOUBLE OBLIQUE HYPHEN + if (val==0x00002E1C) return ON; //# Pi LEFT LOW PARAPHRASE BRACKET + if (val==0x00002E1D) return ON; //# Pf RIGHT LOW PARAPHRASE BRACKET + if ((val>=0x2E80) && (val<=0x2E99)) return ON; //# So [26] CJK RADICAL REPEAT..CJK RADICAL RAP + if ((val>=0x2E9B) && (val<=0x2EF3)) return ON; //# So [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE + if ((val>=0x2F00) && (val<=0x2FD5)) return ON; //# So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE + if ((val>=0x2FF0) && (val<=0x2FFB)) return ON; //# So [12] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID + if ((val>=0x3001) && (val<=0x3003)) return ON; //# Po [3] IDEOGRAPHIC COMMA..DITTO MARK + if (val==0x00003004) return ON; //# So JAPANESE INDUSTRIAL STANDARD SYMBOL + if (val==0x00003008) return ON; //# Ps LEFT ANGLE BRACKET + if (val==0x00003009) return ON; //# Pe RIGHT ANGLE BRACKET + if (val==0x0000300A) return ON; //# Ps LEFT DOUBLE ANGLE BRACKET + if (val==0x0000300B) return ON; //# Pe RIGHT DOUBLE ANGLE BRACKET + if (val==0x0000300C) return ON; //# Ps LEFT CORNER BRACKET + if (val==0x0000300D) return ON; //# Pe RIGHT CORNER BRACKET + if (val==0x0000300E) return ON; //# Ps LEFT WHITE CORNER BRACKET + if (val==0x0000300F) return ON; //# Pe RIGHT WHITE CORNER BRACKET + if (val==0x00003010) return ON; //# Ps LEFT BLACK LENTICULAR BRACKET + if (val==0x00003011) return ON; //# Pe RIGHT BLACK LENTICULAR BRACKET + if ((val>=0x3012) && (val<=0x3013)) return ON; //# So [2] POSTAL MARK..GETA MARK + if (val==0x00003014) return ON; //# Ps LEFT TORTOISE SHELL BRACKET + if (val==0x00003015) return ON; //# Pe RIGHT TORTOISE SHELL BRACKET + if (val==0x00003016) return ON; //# Ps LEFT WHITE LENTICULAR BRACKET + if (val==0x00003017) return ON; //# Pe RIGHT WHITE LENTICULAR BRACKET + if (val==0x00003018) return ON; //# Ps LEFT WHITE TORTOISE SHELL BRACKET + if (val==0x00003019) return ON; //# Pe RIGHT WHITE TORTOISE SHELL BRACKET + if (val==0x0000301A) return ON; //# Ps LEFT WHITE SQUARE BRACKET + if (val==0x0000301B) return ON; //# Pe RIGHT WHITE SQUARE BRACKET + if (val==0x0000301C) return ON; //# Pd WAVE DASH + if (val==0x0000301D) return ON; //# Ps REVERSED DOUBLE PRIME QUOTATION MARK + if ((val>=0x301E) && (val<=0x301F)) return ON; //# Pe [2] DOUBLE PRIME QUOTATION MARK..LOW DOUBLE PRIME QUOTATION MARK + if (val==0x00003020) return ON; //# So POSTAL MARK FACE + if (val==0x00003030) return ON; //# Pd WAVY DASH + if ((val>=0x3036) && (val<=0x3037)) return ON; //# So [2] CIRCLED POSTAL MARK..IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL + if (val==0x0000303D) return ON; //# Po PART ALTERNATION MARK + if ((val>=0x303E) && (val<=0x303F)) return ON; //# So [2] IDEOGRAPHIC VARIATION INDICATOR..IDEOGRAPHIC HALF FILL SPACE + if ((val>=0x309B) && (val<=0x309C)) return ON; //# Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK + if (val==0x000030A0) return ON; //# Pd KATAKANA-HIRAGANA DOUBLE HYPHEN + if (val==0x000030FB) return ON; //# Po KATAKANA MIDDLE DOT + if ((val>=0x31C0) && (val<=0x31CF)) return ON; //# So [16] CJK STROKE T..CJK STROKE N + if ((val>=0x321D) && (val<=0x321E)) return ON; //# So [2] PARENTHESIZED KOREAN CHARACTER OJEON..PARENTHESIZED KOREAN CHARACTER O HU + if (val==0x00003250) return ON; //# So PARTNERSHIP SIGN + if ((val>=0x3251) && (val<=0x325F)) return ON; //# No [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE + if ((val>=0x327C) && (val<=0x327E)) return ON; //# So [3] CIRCLED KOREAN CHARACTER CHAMKO..CIRCLED HANGUL IEUNG U + if ((val>=0x32B1) && (val<=0x32BF)) return ON; //# No [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY + if ((val>=0x32CC) && (val<=0x32CF)) return ON; //# So [4] SQUARE HG..LIMITED LIABILITY SIGN + if ((val>=0x3377) && (val<=0x337A)) return ON; //# So [4] SQUARE DM..SQUARE IU + if ((val>=0x33DE) && (val<=0x33DF)) return ON; //# So [2] SQUARE V OVER M..SQUARE A OVER M + if (val==0x000033FF) return ON; //# So SQUARE GAL + if ((val>=0x4DC0) && (val<=0x4DFF)) return ON; //# So [64] HEXAGRAM FOR THE CREATIVE HEAVEN..HEXAGRAM FOR BEFORE COMPLETION + if ((val>=0xA490) && (val<=0xA4C6)) return ON; //# So [55] YI RADICAL QOT..YI RADICAL KE + if ((val>=0xA700) && (val<=0xA716)) return ON; //# Sk [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR + if ((val>=0xA717) && (val<=0xA71A)) return ON; //# Lm [4] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOWER RIGHT CORNER ANGLE + if ((val>=0xA720) && (val<=0xA721)) return ON; //# Sk [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE + if ((val>=0xA828) && (val<=0xA82B)) return ON; //# So [4] SYLOTI NAGRI POETRY MARK-1..SYLOTI NAGRI POETRY MARK-4 + if ((val>=0xA874) && (val<=0xA877)) return ON; //# Po [4] PHAGS-PA SINGLE HEAD MARK..PHAGS-PA MARK DOUBLE SHAD + if (val==0x0000FD3E) return ON; //# Ps ORNATE LEFT PARENTHESIS + if (val==0x0000FD3F) return ON; //# Pe ORNATE RIGHT PARENTHESIS + if (val==0x0000FDFD) return ON; //# So ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM + if ((val>=0xFE10) && (val<=0xFE16)) return ON; //# Po [7] PRESENTATION FORM FOR VERTICAL COMMA..PRESENTATION FORM FOR VERTICAL QUESTION MARK + if (val==0x0000FE17) return ON; //# Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET + if (val==0x0000FE18) return ON; //# Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET + if (val==0x0000FE19) return ON; //# Po PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS + if (val==0x0000FE30) return ON; //# Po PRESENTATION FORM FOR VERTICAL TWO DOT LEADER + if ((val>=0xFE31) && (val<=0xFE32)) return ON; //# Pd [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH + if ((val>=0xFE33) && (val<=0xFE34)) return ON; //# Pc [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE + if (val==0x0000FE35) return ON; //# Ps PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS + if (val==0x0000FE36) return ON; //# Pe PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS + if (val==0x0000FE37) return ON; //# Ps PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET + if (val==0x0000FE38) return ON; //# Pe PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET + if (val==0x0000FE39) return ON; //# Ps PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET + if (val==0x0000FE3A) return ON; //# Pe PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET + if (val==0x0000FE3B) return ON; //# Ps PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET + if (val==0x0000FE3C) return ON; //# Pe PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET + if (val==0x0000FE3D) return ON; //# Ps PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET + if (val==0x0000FE3E) return ON; //# Pe PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET + if (val==0x0000FE3F) return ON; //# Ps PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET + if (val==0x0000FE40) return ON; //# Pe PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET + if (val==0x0000FE41) return ON; //# Ps PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET + if (val==0x0000FE42) return ON; //# Pe PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET + if (val==0x0000FE43) return ON; //# Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET + if (val==0x0000FE44) return ON; //# Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET + if ((val>=0xFE45) && (val<=0xFE46)) return ON; //# Po [2] SESAME DOT..WHITE SESAME DOT + if (val==0x0000FE47) return ON; //# Ps PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET + if (val==0x0000FE48) return ON; //# Pe PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET + if ((val>=0xFE49) && (val<=0xFE4C)) return ON; //# Po [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE + if ((val>=0xFE4D) && (val<=0xFE4F)) return ON; //# Pc [3] DASHED LOW LINE..WAVY LOW LINE + if (val==0x0000FE51) return ON; //# Po SMALL IDEOGRAPHIC COMMA + if (val==0x0000FE54) return ON; //# Po SMALL SEMICOLON + if ((val>=0xFE56) && (val<=0xFE57)) return ON; //# Po [2] SMALL QUESTION MARK..SMALL EXCLAMATION MARK + if (val==0x0000FE58) return ON; //# Pd SMALL EM DASH + if (val==0x0000FE59) return ON; //# Ps SMALL LEFT PARENTHESIS + if (val==0x0000FE5A) return ON; //# Pe SMALL RIGHT PARENTHESIS + if (val==0x0000FE5B) return ON; //# Ps SMALL LEFT CURLY BRACKET + if (val==0x0000FE5C) return ON; //# Pe SMALL RIGHT CURLY BRACKET + if (val==0x0000FE5D) return ON; //# Ps SMALL LEFT TORTOISE SHELL BRACKET + if (val==0x0000FE5E) return ON; //# Pe SMALL RIGHT TORTOISE SHELL BRACKET + if ((val>=0xFE60) && (val<=0xFE61)) return ON; //# Po [2] SMALL AMPERSAND..SMALL ASTERISK + if ((val>=0xFE64) && (val<=0xFE66)) return ON; //# Sm [3] SMALL LESS-THAN SIGN..SMALL EQUALS SIGN + if (val==0x0000FE68) return ON; //# Po SMALL REVERSE SOLIDUS + if (val==0x0000FE6B) return ON; //# Po SMALL COMMERCIAL AT + if ((val>=0xFF01) && (val<=0xFF02)) return ON; //# Po [2] FULLWIDTH EXCLAMATION MARK..FULLWIDTH QUOTATION MARK + if ((val>=0xFF06) && (val<=0xFF07)) return ON; //# Po [2] FULLWIDTH AMPERSAND..FULLWIDTH APOSTROPHE + if (val==0x0000FF08) return ON; //# Ps FULLWIDTH LEFT PARENTHESIS + if (val==0x0000FF09) return ON; //# Pe FULLWIDTH RIGHT PARENTHESIS + if (val==0x0000FF0A) return ON; //# Po FULLWIDTH ASTERISK + if (val==0x0000FF1B) return ON; //# Po FULLWIDTH SEMICOLON + if ((val>=0xFF1C) && (val<=0xFF1E)) return ON; //# Sm [3] FULLWIDTH LESS-THAN SIGN..FULLWIDTH GREATER-THAN SIGN + if ((val>=0xFF1F) && (val<=0xFF20)) return ON; //# Po [2] FULLWIDTH QUESTION MARK..FULLWIDTH COMMERCIAL AT + if (val==0x0000FF3B) return ON; //# Ps FULLWIDTH LEFT SQUARE BRACKET + if (val==0x0000FF3C) return ON; //# Po FULLWIDTH REVERSE SOLIDUS + if (val==0x0000FF3D) return ON; //# Pe FULLWIDTH RIGHT SQUARE BRACKET + if (val==0x0000FF3E) return ON; //# Sk FULLWIDTH CIRCUMFLEX ACCENT + if (val==0x0000FF3F) return ON; //# Pc FULLWIDTH LOW LINE + if (val==0x0000FF40) return ON; //# Sk FULLWIDTH GRAVE ACCENT + if (val==0x0000FF5B) return ON; //# Ps FULLWIDTH LEFT CURLY BRACKET + if (val==0x0000FF5C) return ON; //# Sm FULLWIDTH VERTICAL LINE + if (val==0x0000FF5D) return ON; //# Pe FULLWIDTH RIGHT CURLY BRACKET + if (val==0x0000FF5E) return ON; //# Sm FULLWIDTH TILDE + if (val==0x0000FF5F) return ON; //# Ps FULLWIDTH LEFT WHITE PARENTHESIS + if (val==0x0000FF60) return ON; //# Pe FULLWIDTH RIGHT WHITE PARENTHESIS + if (val==0x0000FF61) return ON; //# Po HALFWIDTH IDEOGRAPHIC FULL STOP + if (val==0x0000FF62) return ON; //# Ps HALFWIDTH LEFT CORNER BRACKET + if (val==0x0000FF63) return ON; //# Pe HALFWIDTH RIGHT CORNER BRACKET + if ((val>=0xFF64) && (val<=0xFF65)) return ON; //# Po [2] HALFWIDTH IDEOGRAPHIC COMMA..HALFWIDTH KATAKANA MIDDLE DOT + if (val==0x0000FFE2) return ON; //# Sm FULLWIDTH NOT SIGN + if (val==0x0000FFE3) return ON; //# Sk FULLWIDTH MACRON + if (val==0x0000FFE4) return ON; //# So FULLWIDTH BROKEN BAR + if (val==0x0000FFE8) return ON; //# So HALFWIDTH FORMS LIGHT VERTICAL + if ((val>=0xFFE9) && (val<=0xFFEC)) return ON; //# Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW + if ((val>=0xFFED) && (val<=0xFFEE)) return ON; //# So [2] HALFWIDTH BLACK SQUARE..HALFWIDTH WHITE CIRCLE + if ((val>=0xFFF9) && (val<=0xFFFB)) return ON; //# Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR + if ((val>=0xFFFC) && (val<=0xFFFD)) return ON; //# So [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHARACTER + if (val==0x00010101) return ON; //# Po AEGEAN WORD SEPARATOR DOT + if ((val>=0x10140) && (val<=0x10174)) return ON; //# Nl [53] GREEK ACROPHONIC ATTIC ONE QUARTER..GREEK ACROPHONIC STRATIAN FIFTY MNAS + if ((val>=0x10175) && (val<=0x10178)) return ON; //# No [4] GREEK ONE HALF SIGN..GREEK THREE QUARTERS SIGN + if ((val>=0x10179) && (val<=0x10189)) return ON; //# So [17] GREEK YEAR SIGN..GREEK TRYBLION BASE SIGN + if (val==0x0001018A) return ON; //# No GREEK ZERO SIGN + if (val==0x0001091F) return ON; //# Po PHOENICIAN WORD SEPARATOR + if ((val>=0x1D200) && (val<=0x1D241)) return ON; //# So [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54 + if (val==0x0001D245) return ON; //# So GREEK MUSICAL LEIMMA + if ((val>=0x1D300) && (val<=0x1D356)) return ON; //# So [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING + if ((val>=0x0000) && (val<=0x0008)) return BN; //# Cc [9] .. + if ((val>=0x000E) && (val<=0x001B)) return BN; //# Cc [14] .. + if ((val>=0x007F) && (val<=0x0084)) return BN; //# Cc [6] .. + if ((val>=0x0086) && (val<=0x009F)) return BN; //# Cc [26] .. + if (val==0x000000AD) return BN; //# Cf SOFT HYPHEN + if (val==0x0000070F) return BN; //# Cf SYRIAC ABBREVIATION MARK + if ((val>=0x200B) && (val<=0x200D)) return BN; //# Cf [3] ZERO WIDTH SPACE..ZERO WIDTH JOINER + if ((val>=0x2060) && (val<=0x2063)) return BN; //# Cf [4] WORD JOINER..INVISIBLE SEPARATOR + if ((val>=0x2064) && (val<=0x2069)) return BN; //# Cn [6] .. + if ((val>=0x206A) && (val<=0x206F)) return BN; //# Cf [6] INHIBIT SYMMETRIC SWAPPING..NOMINAL DIGIT SHAPES + if ((val>=0xFDD0) && (val<=0xFDEF)) return BN; //# Cn [32] .. + if (val==0x0000FEFF) return BN; //# Cf ZERO WIDTH NO-BREAK SPACE + if ((val>=0xFFF0) && (val<=0xFFF8)) return BN; //# Cn [9] .. + if ((val>=0xFFFE) && (val<=0xFFFF)) return BN; //# Cn [2] .. + if ((val>=0x1D173) && (val<=0x1D17A)) return BN; //# Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE + if ((val>=0x1FFFE) && (val<=0x1FFFF)) return BN; //# Cn [2] .. + if ((val>=0x2FFFE) && (val<=0x2FFFF)) return BN; //# Cn [2] .. + if ((val>=0x3FFFE) && (val<=0x3FFFF)) return BN; //# Cn [2] .. + if ((val>=0x4FFFE) && (val<=0x4FFFF)) return BN; //# Cn [2] .. + if ((val>=0x5FFFE) && (val<=0x5FFFF)) return BN; //# Cn [2] .. + if ((val>=0x6FFFE) && (val<=0x6FFFF)) return BN; //# Cn [2] .. + if ((val>=0x7FFFE) && (val<=0x7FFFF)) return BN; //# Cn [2] .. + if ((val>=0x8FFFE) && (val<=0x8FFFF)) return BN; //# Cn [2] .. + if ((val>=0x9FFFE) && (val<=0x9FFFF)) return BN; //# Cn [2] .. + if ((val>=0xAFFFE) && (val<=0xAFFFF)) return BN; //# Cn [2] .. + if ((val>=0xBFFFE) && (val<=0xBFFFF)) return BN; //# Cn [2] .. + if ((val>=0xCFFFE) && (val<=0xCFFFF)) return BN; //# Cn [2] .. + if ((val>=0xDFFFE) && (val<=0xE0000)) return BN; //# Cn [3] .. + if (val==0x000E0001) return BN; //# Cf LANGUAGE TAG + if ((val>=0xE0002) && (val<=0xE001F)) return BN; //# Cn [30] .. + if ((val>=0xE0020) && (val<=0xE007F)) return BN; //# Cf [96] TAG SPACE..CANCEL TAG + if ((val>=0xE0080) && (val<=0xE00FF)) return BN; //# Cn [128] .. + if ((val>=0xE01F0) && (val<=0xE0FFF)) return BN; //# Cn [3600] .. + if ((val>=0xEFFFE) && (val<=0xEFFFF)) return BN; //# Cn [2] .. + if ((val>=0xFFFFE) && (val<=0xFFFFF)) return BN; //# Cn [2] .. + if ((val>=0x10FFFE) && (val<=0x10FFFF)) return BN; //# Cn [2] .. + if ((val>=0x0300) && (val<=0x036F)) return NSM; //# Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X + if ((val>=0x0483) && (val<=0x0486)) return NSM; //# Mn [4] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC PSILI PNEUMATA + if ((val>=0x0488) && (val<=0x0489)) return NSM; //# Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN + if ((val>=0x0591) && (val<=0x05BD)) return NSM; //# Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG + if (val==0x000005BF) return NSM; //# Mn HEBREW POINT RAFE + if ((val>=0x05C1) && (val<=0x05C2)) return NSM; //# Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT + if ((val>=0x05C4) && (val<=0x05C5)) return NSM; //# Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT + if (val==0x000005C7) return NSM; //# Mn HEBREW POINT QAMATS QATAN + if ((val>=0x0610) && (val<=0x0615)) return NSM; //# Mn [6] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL HIGH TAH + if ((val>=0x064B) && (val<=0x065E)) return NSM; //# Mn [20] ARABIC FATHATAN..ARABIC FATHA WITH TWO DOTS + if (val==0x00000670) return NSM; //# Mn ARABIC LETTER SUPERSCRIPT ALEF + if ((val>=0x06D6) && (val<=0x06DC)) return NSM; //# Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN + if (val==0x000006DE) return NSM; //# Me ARABIC START OF RUB EL HIZB + if ((val>=0x06DF) && (val<=0x06E4)) return NSM; //# Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA + if ((val>=0x06E7) && (val<=0x06E8)) return NSM; //# Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON + if ((val>=0x06EA) && (val<=0x06ED)) return NSM; //# Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM + if (val==0x00000711) return NSM; //# Mn SYRIAC LETTER SUPERSCRIPT ALAPH + if ((val>=0x0730) && (val<=0x074A)) return NSM; //# Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH + if ((val>=0x07A6) && (val<=0x07B0)) return NSM; //# Mn [11] THAANA ABAFILI..THAANA SUKUN + if ((val>=0x07EB) && (val<=0x07F3)) return NSM; //# Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE + if ((val>=0x0901) && (val<=0x0902)) return NSM; //# Mn [2] DEVANAGARI SIGN CANDRABINDU..DEVANAGARI SIGN ANUSVARA + if (val==0x0000093C) return NSM; //# Mn DEVANAGARI SIGN NUKTA + if ((val>=0x0941) && (val<=0x0948)) return NSM; //# Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI + if (val==0x0000094D) return NSM; //# Mn DEVANAGARI SIGN VIRAMA + if ((val>=0x0951) && (val<=0x0954)) return NSM; //# Mn [4] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI ACUTE ACCENT + if ((val>=0x0962) && (val<=0x0963)) return NSM; //# Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL + if (val==0x00000981) return NSM; //# Mn BENGALI SIGN CANDRABINDU + if (val==0x000009BC) return NSM; //# Mn BENGALI SIGN NUKTA + if ((val>=0x09C1) && (val<=0x09C4)) return NSM; //# Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR + if (val==0x000009CD) return NSM; //# Mn BENGALI SIGN VIRAMA + if ((val>=0x09E2) && (val<=0x09E3)) return NSM; //# Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL + if ((val>=0x0A01) && (val<=0x0A02)) return NSM; //# Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI + if (val==0x00000A3C) return NSM; //# Mn GURMUKHI SIGN NUKTA + if ((val>=0x0A41) && (val<=0x0A42)) return NSM; //# Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU + if ((val>=0x0A47) && (val<=0x0A48)) return NSM; //# Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI + if ((val>=0x0A4B) && (val<=0x0A4D)) return NSM; //# Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA + if ((val>=0x0A70) && (val<=0x0A71)) return NSM; //# Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK + if ((val>=0x0A81) && (val<=0x0A82)) return NSM; //# Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA + if (val==0x00000ABC) return NSM; //# Mn GUJARATI SIGN NUKTA + if ((val>=0x0AC1) && (val<=0x0AC5)) return NSM; //# Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E + if ((val>=0x0AC7) && (val<=0x0AC8)) return NSM; //# Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI + if (val==0x00000ACD) return NSM; //# Mn GUJARATI SIGN VIRAMA + if ((val>=0x0AE2) && (val<=0x0AE3)) return NSM; //# Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL + if (val==0x00000B01) return NSM; //# Mn ORIYA SIGN CANDRABINDU + if (val==0x00000B3C) return NSM; //# Mn ORIYA SIGN NUKTA + if (val==0x00000B3F) return NSM; //# Mn ORIYA VOWEL SIGN I + if ((val>=0x0B41) && (val<=0x0B43)) return NSM; //# Mn [3] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC R + if (val==0x00000B4D) return NSM; //# Mn ORIYA SIGN VIRAMA + if (val==0x00000B56) return NSM; //# Mn ORIYA AI LENGTH MARK + if (val==0x00000B82) return NSM; //# Mn TAMIL SIGN ANUSVARA + if (val==0x00000BC0) return NSM; //# Mn TAMIL VOWEL SIGN II + if (val==0x00000BCD) return NSM; //# Mn TAMIL SIGN VIRAMA + if ((val>=0x0C3E) && (val<=0x0C40)) return NSM; //# Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II + if ((val>=0x0C46) && (val<=0x0C48)) return NSM; //# Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI + if ((val>=0x0C4A) && (val<=0x0C4D)) return NSM; //# Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA + if ((val>=0x0C55) && (val<=0x0C56)) return NSM; //# Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK + if (val==0x00000CBC) return NSM; //# Mn KANNADA SIGN NUKTA + if ((val>=0x0CCC) && (val<=0x0CCD)) return NSM; //# Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA + if ((val>=0x0CE2) && (val<=0x0CE3)) return NSM; //# Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL + if ((val>=0x0D41) && (val<=0x0D43)) return NSM; //# Mn [3] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC R + if (val==0x00000D4D) return NSM; //# Mn MALAYALAM SIGN VIRAMA + if (val==0x00000DCA) return NSM; //# Mn SINHALA SIGN AL-LAKUNA + if ((val>=0x0DD2) && (val<=0x0DD4)) return NSM; //# Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA + if (val==0x00000DD6) return NSM; //# Mn SINHALA VOWEL SIGN DIGA PAA-PILLA + if (val==0x00000E31) return NSM; //# Mn THAI CHARACTER MAI HAN-AKAT + if ((val>=0x0E34) && (val<=0x0E3A)) return NSM; //# Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU + if ((val>=0x0E47) && (val<=0x0E4E)) return NSM; //# Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN + if (val==0x00000EB1) return NSM; //# Mn LAO VOWEL SIGN MAI KAN + if ((val>=0x0EB4) && (val<=0x0EB9)) return NSM; //# Mn [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU + if ((val>=0x0EBB) && (val<=0x0EBC)) return NSM; //# Mn [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO + if ((val>=0x0EC8) && (val<=0x0ECD)) return NSM; //# Mn [6] LAO TONE MAI EK..LAO NIGGAHITA + if ((val>=0x0F18) && (val<=0x0F19)) return NSM; //# Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS + if (val==0x00000F35) return NSM; //# Mn TIBETAN MARK NGAS BZUNG NYI ZLA + if (val==0x00000F37) return NSM; //# Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS + if (val==0x00000F39) return NSM; //# Mn TIBETAN MARK TSA -PHRU + if ((val>=0x0F71) && (val<=0x0F7E)) return NSM; //# Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO + if ((val>=0x0F80) && (val<=0x0F84)) return NSM; //# Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA + if ((val>=0x0F86) && (val<=0x0F87)) return NSM; //# Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS + if ((val>=0x0F90) && (val<=0x0F97)) return NSM; //# Mn [8] TIBETAN SUBJOINED LETTER KA..TIBETAN SUBJOINED LETTER JA + if ((val>=0x0F99) && (val<=0x0FBC)) return NSM; //# Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA + if (val==0x00000FC6) return NSM; //# Mn TIBETAN SYMBOL PADMA GDAN + if ((val>=0x102D) && (val<=0x1030)) return NSM; //# Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU + if (val==0x00001032) return NSM; //# Mn MYANMAR VOWEL SIGN AI + if ((val>=0x1036) && (val<=0x1037)) return NSM; //# Mn [2] MYANMAR SIGN ANUSVARA..MYANMAR SIGN DOT BELOW + if (val==0x00001039) return NSM; //# Mn MYANMAR SIGN VIRAMA + if ((val>=0x1058) && (val<=0x1059)) return NSM; //# Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL + if (val==0x0000135F) return NSM; //# Mn ETHIOPIC COMBINING GEMINATION MARK + if ((val>=0x1712) && (val<=0x1714)) return NSM; //# Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA + if ((val>=0x1732) && (val<=0x1734)) return NSM; //# Mn [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD + if ((val>=0x1752) && (val<=0x1753)) return NSM; //# Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U + if ((val>=0x1772) && (val<=0x1773)) return NSM; //# Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U + if ((val>=0x17B7) && (val<=0x17BD)) return NSM; //# Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA + if (val==0x000017C6) return NSM; //# Mn KHMER SIGN NIKAHIT + if ((val>=0x17C9) && (val<=0x17D3)) return NSM; //# Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT + if (val==0x000017DD) return NSM; //# Mn KHMER SIGN ATTHACAN + if ((val>=0x180B) && (val<=0x180D)) return NSM; //# Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE + if (val==0x000018A9) return NSM; //# Mn MONGOLIAN LETTER ALI GALI DAGALGA + if ((val>=0x1920) && (val<=0x1922)) return NSM; //# Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U + if ((val>=0x1927) && (val<=0x1928)) return NSM; //# Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O + if ((val>=0x1929) && (val<=0x192B)) return NSM; //# Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA + if (val==0x00001932) return NSM; //# Mn LIMBU SMALL LETTER ANUSVARA + if ((val>=0x1939) && (val<=0x193B)) return NSM; //# Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I + if ((val>=0x1A17) && (val<=0x1A18)) return NSM; //# Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U + if ((val>=0x1B00) && (val<=0x1B03)) return NSM; //# Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG + if (val==0x00001B34) return NSM; //# Mn BALINESE SIGN REREKAN + if ((val>=0x1B36) && (val<=0x1B3A)) return NSM; //# Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA + if (val==0x00001B3C) return NSM; //# Mn BALINESE VOWEL SIGN LA LENGA + if (val==0x00001B42) return NSM; //# Mn BALINESE VOWEL SIGN PEPET + if ((val>=0x1B6B) && (val<=0x1B73)) return NSM; //# Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG + if ((val>=0x1DC0) && (val<=0x1DCA)) return NSM; //# Mn [11] COMBINING DOTTED GRAVE ACCENT..COMBINING LATIN SMALL LETTER R BELOW + if ((val>=0x1DFE) && (val<=0x1DFF)) return NSM; //# Mn [2] COMBINING LEFT ARROWHEAD ABOVE..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW + if ((val>=0x20D0) && (val<=0x20DC)) return NSM; //# Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE + if ((val>=0x20DD) && (val<=0x20E0)) return NSM; //# Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH + if (val==0x000020E1) return NSM; //# Mn COMBINING LEFT RIGHT ARROW ABOVE + if ((val>=0x20E2) && (val<=0x20E4)) return NSM; //# Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE + if ((val>=0x20E5) && (val<=0x20EF)) return NSM; //# Mn [11] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING RIGHT ARROW BELOW + if ((val>=0x302A) && (val<=0x302F)) return NSM; //# Mn [6] IDEOGRAPHIC LEVEL TONE MARK..HANGUL DOUBLE DOT TONE MARK + if ((val>=0x3099) && (val<=0x309A)) return NSM; //# Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK + if (val==0x0000A802) return NSM; //# Mc SYLOTI NAGRI SIGN DVISVARA + if (val==0x0000A806) return NSM; //# Mn SYLOTI NAGRI SIGN HASANTA + if (val==0x0000A80B) return NSM; //# Mn SYLOTI NAGRI SIGN ANUSVARA + if ((val>=0xA825) && (val<=0xA826)) return NSM; //# Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E + if (val==0x0000FB1E) return NSM; //# Mn HEBREW POINT JUDEO-SPANISH VARIKA + if ((val>=0xFE00) && (val<=0xFE0F)) return NSM; //# Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 + if ((val>=0xFE20) && (val<=0xFE23)) return NSM; //# Mn [4] COMBINING LIGATURE LEFT HALF..COMBINING DOUBLE TILDE RIGHT HALF + if ((val>=0x10A01) && (val<=0x10A03)) return NSM; //# Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R + if ((val>=0x10A05) && (val<=0x10A06)) return NSM; //# Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O + if ((val>=0x10A0C) && (val<=0x10A0F)) return NSM; //# Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA + if ((val>=0x10A38) && (val<=0x10A3A)) return NSM; //# Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW + if (val==0x00010A3F) return NSM; //# Mn KHAROSHTHI VIRAMA + if ((val>=0x1D167) && (val<=0x1D169)) return NSM; //# Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 + if ((val>=0x1D17B) && (val<=0x1D182)) return NSM; //# Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE + if ((val>=0x1D185) && (val<=0x1D18B)) return NSM; //# Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE + if ((val>=0x1D1AA) && (val<=0x1D1AD)) return NSM; //# Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO + if ((val>=0x1D242) && (val<=0x1D244)) return NSM; //# Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME + if ((val>=0xE0100) && (val<=0xE01EF)) return NSM; //# Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 + if ((val>=0x0600) && (val<=0x0603)) return AL; //# Cf [4] ARABIC NUMBER SIGN..ARABIC SIGN SAFHA + if ((val>=0x0604) && (val<=0x060A)) return AL; //# Cn [7] .. + if (val==0x0000060B) return AL; //# Sc AFGHANI SIGN + if (val==0x0000060D) return AL; //# Po ARABIC DATE SEPARATOR + if ((val>=0x0616) && (val<=0x061A)) return AL; //# Cn [5] .. + if (val==0x0000061B) return AL; //# Po ARABIC SEMICOLON + if ((val>=0x061C) && (val<=0x061D)) return AL; //# Cn [2] .. + if ((val>=0x061E) && (val<=0x061F)) return AL; //# Po [2] ARABIC TRIPLE DOT PUNCTUATION MARK..ARABIC QUESTION MARK + if (val==0x00000620) return AL; //# Cn + if ((val>=0x0621) && (val<=0x063A)) return AL; //# Lo [26] ARABIC LETTER HAMZA..ARABIC LETTER GHAIN + if ((val>=0x063B) && (val<=0x063F)) return AL; //# Cn [5] .. + if (val==0x00000640) return AL; //# Lm ARABIC TATWEEL + if ((val>=0x0641) && (val<=0x064A)) return AL; //# Lo [10] ARABIC LETTER FEH..ARABIC LETTER YEH + if (val==0x0000065F) return AL; //# Cn + if (val==0x0000066D) return AL; //# Po ARABIC FIVE POINTED STAR + if ((val>=0x066E) && (val<=0x066F)) return AL; //# Lo [2] ARABIC LETTER DOTLESS BEH..ARABIC LETTER DOTLESS QAF + if ((val>=0x0671) && (val<=0x06D3)) return AL; //# Lo [99] ARABIC LETTER ALEF WASLA..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE + if (val==0x000006D4) return AL; //# Po ARABIC FULL STOP + if (val==0x000006D5) return AL; //# Lo ARABIC LETTER AE + if (val==0x000006DD) return AL; //# Cf ARABIC END OF AYAH + if ((val>=0x06E5) && (val<=0x06E6)) return AL; //# Lm [2] ARABIC SMALL WAW..ARABIC SMALL YEH + if ((val>=0x06EE) && (val<=0x06EF)) return AL; //# Lo [2] ARABIC LETTER DAL WITH INVERTED V..ARABIC LETTER REH WITH INVERTED V + if ((val>=0x06FA) && (val<=0x06FC)) return AL; //# Lo [3] ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC LETTER GHAIN WITH DOT BELOW + if ((val>=0x06FD) && (val<=0x06FE)) return AL; //# So [2] ARABIC SIGN SINDHI AMPERSAND..ARABIC SIGN SINDHI POSTPOSITION MEN + if (val==0x000006FF) return AL; //# Lo ARABIC LETTER HEH WITH INVERTED V + if ((val>=0x0700) && (val<=0x070D)) return AL; //# Po [14] SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN ASTERISCUS + if (val==0x0000070E) return AL; //# Cn + if (val==0x00000710) return AL; //# Lo SYRIAC LETTER ALAPH + if ((val>=0x0712) && (val<=0x072F)) return AL; //# Lo [30] SYRIAC LETTER BETH..SYRIAC LETTER PERSIAN DHALATH + if ((val>=0x074B) && (val<=0x074C)) return AL; //# Cn [2] .. + if ((val>=0x074D) && (val<=0x076D)) return AL; //# Lo [33] SYRIAC LETTER SOGDIAN ZHAIN..ARABIC LETTER SEEN WITH TWO DOTS VERTICALLY ABOVE + if ((val>=0x076E) && (val<=0x077F)) return AL; //# Cn [18] .. + if ((val>=0x0780) && (val<=0x07A5)) return AL; //# Lo [38] THAANA LETTER HAA..THAANA LETTER WAAVU + if (val==0x000007B1) return AL; //# Lo THAANA LETTER NAA + if ((val>=0x07B2) && (val<=0x07BF)) return AL; //# Cn [14] .. + if ((val>=0xFB50) && (val<=0xFBB1)) return AL; //# Lo [98] ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM + if ((val>=0xFBB2) && (val<=0xFBD2)) return AL; //# Cn [33] .. + if ((val>=0xFBD3) && (val<=0xFD3D)) return AL; //# Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM + if ((val>=0xFD40) && (val<=0xFD4F)) return AL; //# Cn [16] .. + if ((val>=0xFD50) && (val<=0xFD8F)) return AL; //# Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM + if ((val>=0xFD90) && (val<=0xFD91)) return AL; //# Cn [2] .. + if ((val>=0xFD92) && (val<=0xFDC7)) return AL; //# Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM + if ((val>=0xFDC8) && (val<=0xFDCF)) return AL; //# Cn [8] .. + if ((val>=0xFDF0) && (val<=0xFDFB)) return AL; //# Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU + if (val==0x0000FDFC) return AL; //# Sc RIAL SIGN + if ((val>=0xFDFE) && (val<=0xFDFF)) return AL; //# Cn [2] .. + if ((val>=0xFE70) && (val<=0xFE74)) return AL; //# Lo [5] ARABIC FATHATAN ISOLATED FORM..ARABIC KASRATAN ISOLATED FORM + if (val==0x0000FE75) return AL; //# Cn + if ((val>=0xFE76) && (val<=0xFEFC)) return AL; //# Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM + if ((val>=0xFEFD) && (val<=0xFEFE)) return AL; //# Cn [2] .. + if (val==0x0000202D) return LRO; //# Cf LEFT-TO-RIGHT OVERRIDE + if (val==0x0000202E) return RLO; //# Cf RIGHT-TO-LEFT OVERRIDE + if (val==0x0000202A) return LRE; //# Cf LEFT-TO-RIGHT EMBEDDING + if (val==0x0000202B) return RLE; //# Cf RIGHT-TO-LEFT EMBEDDING + if (val==0x0000202C) return PDF; //# Cf POP DIRECTIONAL FORMATTING + return L; +} diff --git a/src/utils/url.c b/src/utils/url.c new file mode 100644 index 0000000..a53b910 --- /dev/null +++ b/src/utils/url.c @@ -0,0 +1,195 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/* the length of the URL separator ("://" || "|//") */ +#define URL_SEP_LENGTH 3 + +/* our supported protocol types */ +enum +{ + /*absolute path to file*/ + GF_URL_TYPE_FILE = 0, + /*relative URL*/ + GF_URL_TYPE_RELATIVE , + /*any other URL*/ + GF_URL_TYPE_ANY +}; + +/*resolve the protocol type, for a std URL: http:// or ftp:// ...*/ +static u32 URL_GetProtocolType(const char *pathName) +{ + char *begin; + if (!pathName) return GF_URL_TYPE_ANY; + + if ((pathName[0] == '/') || (pathName[0] == '\\') + || (pathName[1] == ':') + || ((pathName[0] == ':') && (pathName[1] == ':')) + ) return GF_URL_TYPE_FILE; + + begin = strstr(pathName, "://"); + if (!begin) begin = strstr(pathName, "|//"); + if (!begin) return GF_URL_TYPE_RELATIVE; + if (!strnicmp(pathName, "file", 4)) return GF_URL_TYPE_FILE; + return GF_URL_TYPE_ANY; +} + +/*gets protocol type*/ +Bool gf_url_is_local(const char *pathName) +{ + u32 mode = URL_GetProtocolType(pathName); + return (mode==GF_URL_TYPE_ANY) ? 0 : 1; +} + +char *gf_url_get_absolute_path(const char *pathName, const char *parentPath) +{ + u32 prot_type = URL_GetProtocolType(pathName); + + /*abs path name*/ + if (prot_type == GF_URL_TYPE_FILE) { + /*abs path*/ + if (!strstr(pathName, "://") && !strstr(pathName, "|//")) return strdup(pathName); + pathName += 6; + /*not sure if "file:///C:\..." is std, but let's handle it anyway*/ + if ((pathName[0]=='/') && (pathName[2]==':')) pathName += 1; + return strdup(pathName); + } + if (prot_type==GF_URL_TYPE_ANY) return NULL; + if (!parentPath) return strdup(pathName); + + /*try with the parent URL*/ + prot_type = URL_GetProtocolType(parentPath); + /*if abs parent path concatenate*/ + if (prot_type == GF_URL_TYPE_FILE) return gf_url_concatenate(parentPath, pathName); + if (prot_type != GF_URL_TYPE_RELATIVE) return NULL; + /*if we are here, parentPath is also relative... return the original PathName*/ + return strdup(pathName); +} + + +char *gf_url_concatenate(const char *parentName, const char *pathName) +{ + u32 pathSepCount, i, prot_type; + char psep; + char *outPath, *name; + char tmp[GF_MAX_PATH]; + + if (!pathName || !parentName) return NULL; + + if ( (strlen(parentName) > GF_MAX_PATH) || (strlen(pathName) > GF_MAX_PATH) ) return NULL; + + prot_type = URL_GetProtocolType(pathName); + if (prot_type != GF_URL_TYPE_RELATIVE) { + outPath = strdup(pathName); + goto check_spaces; + } + + pathSepCount = 0; + name = NULL; + if (pathName[0] == '.') { + if (!strcmp(pathName, "..")) { + pathSepCount = 1; + name = ""; + } + for (i = 0; i< strlen(pathName) - 2; i++) { + /*current dir*/ + if ( (pathName[i] == '.') + && ( (pathName[i+1] == GF_PATH_SEPARATOR) || (pathName[i+1] == '/') ) ) { + i++; + continue; + } + /*parent dir*/ + if ( (pathName[i] == '.') && (pathName[i+1] == '.') + && ( (pathName[i+2] == GF_PATH_SEPARATOR) || (pathName[i+2] == '/') ) + ) { + pathSepCount ++; + i+=2; + name = (char *) &pathName[i+1]; + } else { + name = (char *) &pathName[i]; + break; + } + } + } + if (!name) name = (char *) pathName; + + strcpy(tmp, parentName); + for (i = strlen(parentName); i > 0; i--) { + //break our path at each separator + if ((parentName[i-1] == GF_PATH_SEPARATOR) || (parentName[i-1] == '/')) { + tmp[i-1] = 0; + if (!pathSepCount) break; + pathSepCount--; + } + } + //if i==0, the parent path was relative, just return the pathName + if (!i) { + outPath = strdup(pathName); + goto check_spaces; + } + + prot_type = URL_GetProtocolType(parentName); + psep = (prot_type == GF_URL_TYPE_FILE) ? GF_PATH_SEPARATOR : '/'; + + outPath = (char *) malloc(strlen(tmp) + strlen(name) + 2); + sprintf(outPath, "%s%c%s", tmp, psep, name); + + /*cleanup paths sep for win32*/ +// if ((prot_type == GF_URL_TYPE_FILE) && (GF_PATH_SEPARATOR != '/')) { + for (i = 0; i + + + /* + * Original code from the GNU UTF-8 Library + */ + +GF_EXPORT +size_t gf_utf8_wcstombs(char* dest, size_t len, const unsigned short** srcp) +{ + size_t count; + const unsigned short * src = *srcp; + + if (dest != NULL) { + char* destptr = dest; + for (;; src++) { + unsigned char c; + unsigned short wc = *src; + if (wc < 0x80) { + if (wc == (wchar_t)'\0') { + if (len == 0) { + *srcp = src; + break; + } + *destptr = '\0'; + *srcp = NULL; + break; + } + count = 0; + c = (unsigned char) wc; + } else if (wc < 0x800) { + count = 1; + c = (unsigned char) ((wc >> 6) | 0xC0); + } else { + count = 2; + c = (unsigned char) ((wc >> 12) | 0xE0); + } + if (len <= count) { + *srcp = src; + break; + } + len -= count+1; + *destptr++ = c; + if (count > 0) + do { + *destptr++ = (unsigned char)(((wc >> (6 * --count)) & 0x3F) | 0x80); + } while (count > 0); + } + return destptr - dest; + } else { + /* Ignore dest and len. */ + size_t totalcount = 0; + for (;; src++) { + unsigned short wc = *src; + size_t count; + if (wc < 0x80) { + if (wc == (wchar_t)'\0') { + *srcp = NULL; + break; + } + count = 1; + } else if (wc < 0x800) { + count = 2; + } else { + count = 3; + } + totalcount += count; + } + return totalcount; + } +} + + +typedef struct +{ + u32 count : 16; /* number of bytes remaining to be processed */ + u32 value : 16; /* if count > 0: partial wide character */ +/* + If WCHAR_T_BITS == 16, need 2 bits for count, + 12 bits for value (10 for mbstowcs direction, 12 for wcstombs direction). +*/ +} gf_utf8_mbstate_t; + +static gf_utf8_mbstate_t internal; + +GF_EXPORT +size_t gf_utf8_mbstowcs(unsigned short* dest, size_t len, const char** srcp) +{ + gf_utf8_mbstate_t* ps = &internal; + const char *src = *srcp; + + unsigned short* destptr = dest; + for (; len > 0; destptr++, len--) { + const char* backup_src = src; + unsigned char c; + unsigned short wc; + size_t count; + if (ps->count == 0) { + c = (unsigned char) *src; + if (c < 0x80) { + *destptr = (wchar_t) c; + if (c == 0) { + src = NULL; + break; + } + src++; + continue; + } else if (c < 0xC0) { + /* Spurious 10XXXXXX byte is invalid. */ + goto bad_input; + } + if (c < 0xE0) { + wc = (wchar_t)(c & 0x1F) << 6; + count = 1; + if (c < 0xC2) goto bad_input; + } else if (c < 0xF0) { + wc = (wchar_t)(c & 0x0F) << 12; + count = 2; + } + else goto bad_input; + src++; + } else { + wc = ps->value << 6; + count = ps->count; + } + for (;;) { + c = (unsigned char) *src++ ^ 0x80; + if (!(c < 0x40)) goto bad_input_backup; + wc |= (unsigned short) c << (6 * --count); + if (count == 0) + break; + /* The following test is only necessary once for every character, + but it would be too complicated to perform it once only, on + the first pass through this loop. */ + if ((unsigned short) wc < ((unsigned short) 1 << (5 * count + 6))) + goto bad_input_backup; + } + *destptr = wc; + ps->count = 0; + continue; + +bad_input_backup: + src = backup_src; + goto bad_input; + } + *srcp = src; + return destptr-dest; + +bad_input: + *srcp = src; + return (size_t)(-1); +} + + +GF_EXPORT +size_t gf_utf8_wcslen (const unsigned short *s) +{ + const unsigned short* ptr; + for (ptr = s; *ptr != (unsigned short)'\0'; ptr++) { + } + return ptr - s; +} + + diff --git a/src/utils/xml_parser.c b/src/utils/xml_parser.c new file mode 100644 index 0000000..e4b1a88 --- /dev/null +++ b/src/utils/xml_parser.c @@ -0,0 +1,1695 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +/*since 0.2.2, we use zlib for xmt/x3d reading to handle gz files*/ +#include + +#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__) +#pragma comment(lib, "zlib") +#endif + + +static GF_Err gf_xml_sax_parse_intern(GF_SAXParser *parser, char *current); + +static char *xml_translate_xml_string(char *str) +{ + char *value; + u32 size, i, j; + if (!str || !strlen(str)) return NULL; + value = (char *)malloc(sizeof(char) * 500); + size = 500; + i = j = 0; + while (str[i]) { + if (j+20 >= size) { + size += 500; + value = (char *)realloc(value, sizeof(char)*size); + } + if (str[i] == '&') { + if (str[i+1]=='#') { + char szChar[20], *end; + u16 wchar[2]; + u32 val; + const unsigned short *srcp; + strncpy(szChar, str+i, 10); + end = strchr(szChar, ';'); + if (!end) break; + end[1] = 0; + i+=strlen(szChar); + wchar[1] = 0; + if (szChar[2]=='x') + sscanf(szChar, "&#x%x;", &val); + else + sscanf(szChar, "&#%d;", &val); + wchar[0] = val; + srcp = wchar; + j += gf_utf8_wcstombs(&value[j], 20, &srcp); + } + else if (!strnicmp(&str[i], "&", sizeof(char)*5)) { + value[j] = '&'; + j++; + i+= 5; + } + else if (!strnicmp(&str[i], "<", sizeof(char)*4)) { + value[j] = '<'; + j++; + i+= 4; + } + else if (!strnicmp(&str[i], ">", sizeof(char)*4)) { + value[j] = '>'; + j++; + i+= 4; + } + else if (!strnicmp(&str[i], "'", sizeof(char)*6)) { + value[j] = '\''; + j++; + i+= 6; + } + else if (!strnicmp(&str[i], """, sizeof(char)*6)) { + value[j] = '\"'; + j++; + i+= 6; + } else { + value[j] = str[i]; + j++; i++; + } + } else { + value[j] = str[i]; + j++; i++; + } + } + value[j] = 0; + return value; +} + + +enum +{ + SAX_STATE_ATT_NAME, + SAX_STATE_ATT_VALUE, + SAX_STATE_ELEMENT, + SAX_STATE_COMMENT, + SAX_STATE_TEXT_CONTENT, + SAX_STATE_ENTITY, + SAX_STATE_SKIP_DOCTYPE, + SAX_STATE_CDATA, + SAX_STATE_DONE, + SAX_STATE_XML_PROC, + SAX_STATE_SYNTAX_ERROR, +}; + +typedef struct +{ + u32 name_start, name_end; + u32 val_start, val_end; + Bool has_entities; +} GF_XMLSaxAttribute; + + +//#define NO_GZIP + + +struct _tag_sax_parser +{ + /*0: UTF-8, 1: UTF-16 BE, 2: UTF-16 LE. String input is always converted back to utf8*/ + s32 unicode_type; + char *buffer; + /*alloc size, line size and current position*/ + u32 alloc_size, line_size, current_pos; + /*current node depth*/ + u32 node_depth; + + /*gz input file*/ +#ifdef NO_GZIP + FILE *f_in; +#else + gzFile gz_in; +#endif + /*current line , file size and pos for user notif*/ + u32 line, file_size, file_pos; + + /*SAX callbacks*/ + gf_xml_sax_node_start sax_node_start; + gf_xml_sax_node_end sax_node_end; + gf_xml_sax_text_content sax_text_content; + void *sax_cbck; + gf_xml_sax_progress on_progress; + + u32 sax_state; + u32 init_state; + GF_List *entities; + char att_sep; + Bool in_entity, suspended; + u32 in_quote; + + u32 elt_start_pos, elt_end_pos; + + /*last error found*/ + char err_msg[1000]; + + u32 att_name_start, elt_name_start, elt_name_end, text_start, text_end; + + GF_XMLAttribute *attrs; + GF_XMLSaxAttribute *sax_attrs; + u32 nb_attrs, nb_alloc_attrs; +}; + +static GF_XMLSaxAttribute *xml_get_sax_attribute(GF_SAXParser *parser) +{ + if (parser->nb_attrs==parser->nb_alloc_attrs) { + parser->nb_alloc_attrs++; + parser->sax_attrs = (GF_XMLSaxAttribute *)realloc(parser->sax_attrs, sizeof(GF_XMLSaxAttribute)*parser->nb_alloc_attrs); + parser->attrs = (GF_XMLAttribute *)realloc(parser->attrs, sizeof(GF_XMLAttribute)*parser->nb_alloc_attrs); + } + return &parser->sax_attrs[parser->nb_attrs++]; +} + +static void xml_sax_swap(GF_SAXParser *parser) +{ + if (parser->current_pos && ((parser->sax_state==SAX_STATE_TEXT_CONTENT) || (parser->sax_state==SAX_STATE_COMMENT) ) ) { + assert(parser->line_size >= parser->current_pos); + parser->line_size -= parser->current_pos; + parser->file_pos += parser->current_pos; + if (parser->line_size) memmove(parser->buffer, parser->buffer + parser->current_pos, sizeof(char)*parser->line_size); + parser->buffer[parser->line_size] = 0; + parser->current_pos = 0; + } +} + +static void xml_sax_node_end(GF_SAXParser *parser, Bool had_children) +{ + char *name, *sep, c; + + assert(parser->elt_name_start); + assert(parser->elt_name_end); + if (!parser->node_depth) { + parser->sax_state = SAX_STATE_SYNTAX_ERROR; + sprintf(parser->err_msg, "Markup error"); + return; + } + c = parser->buffer[parser->elt_name_end - 1]; + parser->buffer[parser->elt_name_end - 1] = 0; + name = parser->buffer + parser->elt_name_start - 1; + + if (parser->sax_node_end) { + sep = strchr(name, ':'); + if (sep) { + sep[0] = 0; + parser->sax_node_end(parser->sax_cbck, sep+1, name); + sep[0] = ':'; + } else { + parser->sax_node_end(parser->sax_cbck, name, NULL); + } + } + parser->buffer[parser->elt_name_end - 1] = c; + parser->node_depth--; + if (!parser->init_state && !parser->node_depth) parser->sax_state = SAX_STATE_DONE; + xml_sax_swap(parser); + parser->text_start = parser->text_end = 0; +} + +static void xml_sax_node_start(GF_SAXParser *parser) +{ + Bool has_entities = 0; + u32 i; + char *sep, c, *name; + + assert(parser->elt_name_start && parser->elt_name_end); + c = parser->buffer[parser->elt_name_end - 1]; + parser->buffer[parser->elt_name_end - 1] = 0; + name = parser->buffer + parser->elt_name_start - 1; + + for (i=0;inb_attrs; i++) { + parser->attrs[i].name = parser->buffer + parser->sax_attrs[i].name_start - 1; + parser->buffer[parser->sax_attrs[i].name_end-1] = 0; + parser->attrs[i].value = parser->buffer + parser->sax_attrs[i].val_start - 1; + parser->buffer[parser->sax_attrs[i].val_end-1] = 0; + + if (strchr(parser->attrs[i].value, '&')) { + parser->sax_attrs[i].has_entities = 1; + has_entities = 1; + parser->attrs[i].value = xml_translate_xml_string(parser->attrs[i].value); + } + /*store first char pos after current attrib for node peeking*/ + parser->att_name_start = parser->sax_attrs[i].val_end; + } + + if (parser->sax_node_start) { + sep = strchr(name, ':'); + if (sep) { + sep[0] = 0; + parser->sax_node_start(parser->sax_cbck, sep+1, name, parser->attrs, parser->nb_attrs); + sep[0] = ':'; + } else { + parser->sax_node_start(parser->sax_cbck, name, NULL, parser->attrs, parser->nb_attrs); + } + } + parser->att_name_start = 0; + parser->buffer[parser->elt_name_end - 1] = c; + parser->node_depth++; + if (has_entities) { + for (i=0;inb_attrs; i++) { + if (parser->sax_attrs[i].has_entities) { + parser->sax_attrs[i].has_entities = 0; + free(parser->attrs[i].value); + } + } + } + parser->nb_attrs = 0; + xml_sax_swap(parser); + parser->text_start = parser->text_end = 0; +} + +static Bool xml_sax_parse_attribute(GF_SAXParser *parser) +{ + char *sep; + GF_XMLSaxAttribute *att = NULL; + + /*looking for attribute name*/ + if (parser->sax_state==SAX_STATE_ATT_NAME) { + /*looking for start*/ + if (!parser->att_name_start) { + while (parser->current_pos < parser->line_size) { + u8 c = parser->buffer[parser->current_pos]; + switch (c) { + case '\n': + parser->line++; + case ' ': + case '\r': + case '\t': + parser->current_pos++; + continue; + /*end of element*/ + case '?': + if (parser->init_state!=1) break; + case '/': + /*not enough data*/ + if (parser->current_pos+1 == parser->line_size) return 1; + if (parser->buffer[parser->current_pos+1]=='>') { + parser->current_pos+=2; + parser->elt_end_pos = parser->file_pos + parser->current_pos - 1; + /*done parsing attr AND elements*/ + if (!parser->init_state) { + xml_sax_node_start(parser); + /*move to SAX_STATE_TEXT_CONTENT to force text flush*/ + parser->sax_state = SAX_STATE_TEXT_CONTENT; + xml_sax_node_end(parser, 0); + } else { + parser->nb_attrs = 0; + } + parser->sax_state = (parser->init_state) ? SAX_STATE_ELEMENT : SAX_STATE_TEXT_CONTENT; + parser->text_start = parser->text_end = 0; + return 0; + } + if (!parser->in_quote && (c=='/')) { + if (!parser->init_state) { + parser->sax_state = SAX_STATE_SYNTAX_ERROR; + sprintf(parser->err_msg, "Markup error"); + return 1; + } + } + break; + case '"': + if (parser->sax_state==SAX_STATE_ATT_VALUE) break; + if (parser->in_quote && (parser->in_quote!=c) ) { + parser->sax_state = SAX_STATE_SYNTAX_ERROR; + sprintf(parser->err_msg, "Markup error"); + return 1; + } + if (parser->in_quote) parser->in_quote = 0; + else parser->in_quote = c; + break; + case '>': + parser->current_pos+=1; + /*end of */ + if (parser->init_state) { + if (parser->init_state==1) { + parser->sax_state = SAX_STATE_SYNTAX_ERROR; + sprintf(parser->err_msg, "Invalid DOCTYPE"); + return 1; + } + parser->sax_state = SAX_STATE_ELEMENT; + return 0; + } + /*done parsing attr*/ + parser->sax_state = SAX_STATE_TEXT_CONTENT; + xml_sax_node_start(parser); + return 0; + case '[': + if (parser->init_state) { + parser->current_pos+=1; + if (parser->init_state==1) { + parser->sax_state = SAX_STATE_SYNTAX_ERROR; + sprintf(parser->err_msg, "Invalid DOCTYPE"); + return 1; + } + parser->sax_state = SAX_STATE_ELEMENT; + return 0; + } + break; + case '<': + parser->sax_state = SAX_STATE_SYNTAX_ERROR; + sprintf(parser->err_msg, "Invalid character"); + return 0; + /*first char of attr name*/ + default: + parser->att_name_start = parser->current_pos + 1; + break; + } + parser->current_pos++; + if (parser->att_name_start) break; + } + if (parser->current_pos == parser->line_size) return 1; + } + + if (parser->init_state==2) { + sep = strchr(parser->buffer + parser->att_name_start - 1, parser->in_quote ? parser->in_quote : ' '); + /*not enough data*/ + if (!sep) return 1; + parser->current_pos = sep - parser->buffer; + parser->att_name_start = 0; + if (parser->in_quote) { + parser->current_pos++; + parser->in_quote = 0; + } + return 0; + } + + /*looking for '"'*/ + if (parser->att_name_start) { + sep = strchr(parser->buffer + parser->att_name_start - 1, '='); + /*not enough data*/ + if (!sep) return 1; + + parser->current_pos = sep - parser->buffer; + att = xml_get_sax_attribute(parser); + att->name_start = parser->att_name_start; + att->name_end = parser->current_pos + 1; + while (strchr(" \n\t", parser->buffer[att->name_end - 2])) { + assert(att->name_end); + att->name_end --; + } + att->has_entities = 0; + parser->att_name_start = 0; + parser->current_pos++; + parser->sax_state = SAX_STATE_ATT_VALUE; + + } + } + + if (parser->sax_state == SAX_STATE_ATT_VALUE) { + att = &parser->sax_attrs[parser->nb_attrs-1]; + /*looking for first delimiter*/ + if (!parser->att_sep) { + while (parser->current_pos < parser->line_size) { + u8 c = parser->buffer[parser->current_pos]; + switch (c) { + case '\n': + parser->line++; + case ' ': + case '\r': + case '\t': + parser->current_pos++; + continue; + case '\'': + case '"': + parser->att_sep = c; + att->val_start = parser->current_pos + 2; + break; + default: + break; + } + parser->current_pos++; + if (parser->att_sep) break; + } + if (parser->current_pos == parser->line_size) return 1; + } + +att_retry: + + assert(parser->att_sep); + sep = strchr(parser->buffer + parser->current_pos, parser->att_sep); + if (!sep || !sep[1]) return 1; + + if (!parser->init_state && (strchr(" />\n\t\r", sep[1])==NULL)) { + parser->current_pos = sep - parser->buffer + 1; + goto att_retry; + } + + parser->current_pos = sep - parser->buffer; + att->val_end = parser->current_pos + 1; + parser->current_pos++; + + /*"style" always at the begining of the attributes for ease of parsing*/ + if (!strncmp(parser->buffer + att->name_start-1, "style", 5)) { + GF_XMLSaxAttribute prev = parser->sax_attrs[0]; + parser->sax_attrs[0] = *att; + *att = prev; + } + parser->att_sep = 0; + parser->sax_state = SAX_STATE_ATT_NAME; + parser->att_name_start = 0; + return 0; + } + return 1; +} + + +typedef struct +{ + char *name; + char *value; + u32 namelen; + u8 sep; +} XML_Entity; + +static void xml_sax_flush_text(GF_SAXParser *parser) +{ + char *text, c; + if (!parser->text_start || parser->init_state || !parser->sax_text_content) return; + + /* This optimization should be done at the application level + generic XML parsing should not try to remove any character !!*/ +#if 0 + u32 offset; + offset = 0; + while (parser->text_start+offsettext_end) { + c = parser->buffer[parser->text_start-1+offset]; + if (c=='\r') offset++; + else if (c==' ') offset++; + else if (c=='\n') { + parser->line++; + offset++; + } else { + break; + } + } + parser->text_start+=offset; + if (parser->text_start == parser->text_end) { + parser->text_start = parser->text_end = 0; + return; + } + + offset = 0; + while (offsettext_end) { + c = parser->buffer[parser->text_end-2-offset]; + if (c=='\r') offset++; + else if (c==' ') offset++; + else if (c=='\n') { + parser->line++; + offset++; + } else { + break; + } + } + parser->text_end-=offset; +#endif + + assert(parser->text_start < parser->text_end); + + c = parser->buffer[parser->text_end-1]; + parser->buffer[parser->text_end-1] = 0; + text = parser->buffer + parser->text_start-1; + + /*solve XML built-in entities*/ + if (strchr(text, '&') && strchr(text, ';')) { + char *xml_text = xml_translate_xml_string(text); + if (xml_text) { + parser->sax_text_content(parser->sax_cbck, xml_text, (parser->sax_state==SAX_STATE_CDATA) ? 1 : 0); + free(xml_text); + } + } else { + parser->sax_text_content(parser->sax_cbck, text, (parser->sax_state==SAX_STATE_CDATA) ? 1 : 0); + } + parser->buffer[parser->text_end-1] = c; + parser->text_start = parser->text_end = 0; +} + +static void xml_sax_store_text(GF_SAXParser *parser, u32 txt_len) +{ + if (!txt_len) return; + + if (!parser->text_start) { + parser->text_start = parser->current_pos + 1; + parser->text_end = parser->text_start + txt_len; + parser->current_pos += txt_len; + assert(parser->current_pos <= parser->line_size); + return; + } + /*contiguous text*/ + if (parser->text_end && (parser->text_end-1 == parser->current_pos)) { + parser->text_end += txt_len; + parser->current_pos += txt_len; + assert(parser->current_pos <= parser->line_size); + return; + } + /*need to flush*/ + xml_sax_flush_text(parser); + + parser->text_start = parser->current_pos + 1; + parser->text_end = parser->text_start + txt_len; + parser->current_pos += txt_len; + assert(parser->current_pos <= parser->line_size); +} + +static char *xml_get_current_text(GF_SAXParser *parser) +{ + char *text, c; + if (!parser->text_start) return NULL; + + c = parser->buffer[parser->text_end-1]; + parser->buffer[parser->text_end-1] = 0; + text = strdup(parser->buffer + parser->text_start-1); + parser->buffer[parser->text_end-1] = c; + parser->text_start = parser->text_end = 0; + return text; +} + +static void xml_sax_skip_doctype(GF_SAXParser *parser) +{ + while (parser->current_pos < parser->line_size) { + if (parser->buffer[parser->current_pos]=='>') { + parser->sax_state = SAX_STATE_ELEMENT; + parser->current_pos++; + xml_sax_swap(parser); + return; + } + parser->current_pos++; + } +} + +static void xml_sax_skip_xml_proc(GF_SAXParser *parser) +{ + while (parser->current_pos + 1 < parser->line_size) { + if ((parser->buffer[parser->current_pos]=='?') && (parser->buffer[parser->current_pos+1]=='>')) { + parser->sax_state = SAX_STATE_ELEMENT; + parser->current_pos++; + xml_sax_swap(parser); + return; + } + parser->current_pos++; + } +} + + +static void xml_sax_parse_entity(GF_SAXParser *parser) +{ + char szName[1024]; + u32 i = 0; + XML_Entity *ent = (XML_Entity *)gf_list_last(parser->entities); + char *skip_chars = " \t\n\r"; + i=0; + if (ent && ent->value) ent = NULL; + if (ent) skip_chars = NULL; + + while (parser->current_pos+i < parser->line_size) { + u8 c = parser->buffer[parser->current_pos+i]; + if (skip_chars && strchr(skip_chars, c)) { + if (c=='\n') parser->line++; + parser->current_pos++; + continue; + } + if (!ent && (c=='%')) { + parser->current_pos+=i+1; + parser->sax_state = SAX_STATE_SKIP_DOCTYPE; + return; + } + else if (!ent && ((c=='\"') || (c=='\'')) ) { + szName[i] = 0; + GF_SAFEALLOC(ent, XML_Entity); + ent->name = strdup(szName); + ent->namelen = strlen(ent->name); + ent->sep = c; + parser->current_pos += 1+i; + assert(parser->current_pos < parser->line_size); + xml_sax_swap(parser); + i=0; + gf_list_add(parser->entities, ent); + skip_chars = NULL; + } else if (ent && c==ent->sep) { + xml_sax_store_text(parser, i); + + ent->value = xml_get_current_text(parser); + if (!ent->value) ent->value = strdup(""); + + parser->current_pos += 1; + assert(parser->current_pos < parser->line_size); + xml_sax_swap(parser); + parser->sax_state = SAX_STATE_SKIP_DOCTYPE; + return; + } else if (!ent) { + szName[i] = c; + i++; + } else { + i++; + } + } + xml_sax_store_text(parser, i); +} + +static void xml_sax_cdata(GF_SAXParser *parser) +{ + char *cd_end = strstr(parser->buffer + parser->current_pos, "]]>"); + if (!cd_end) { + xml_sax_store_text(parser, parser->line_size - parser->current_pos); + } else { + u32 size = cd_end - (parser->buffer + parser->current_pos); + xml_sax_store_text(parser, size); + xml_sax_flush_text(parser); + parser->current_pos += 3; + assert(parser->current_pos <= parser->line_size); + parser->sax_state = SAX_STATE_TEXT_CONTENT; + } +} + +static Bool xml_sax_parse_comments(GF_SAXParser *parser) +{ + char *end = strstr(parser->buffer + parser->current_pos, "-->"); + if (!end) { + if (parser->line_size>3) + parser->current_pos = parser->line_size-3; + xml_sax_swap(parser); + return 0; + } + + parser->current_pos += 3 + (u32) (end - (parser->buffer + parser->current_pos) ); + assert(parser->current_pos <= parser->line_size); + parser->sax_state = SAX_STATE_TEXT_CONTENT; + parser->text_start = parser->text_end = 0; + xml_sax_swap(parser); + return 1; +} + + + +static GF_Err xml_sax_parse(GF_SAXParser *parser, Bool force_parse) +{ + u32 i = 0; + Bool is_text, is_end; + u8 c; + char *elt, sep; + + is_text = 0; + while (parser->current_posline_size) { + if (!force_parse && parser->suspended) goto exit; + +restart: + is_text = 0; + switch (parser->sax_state) { + /*load an XML element*/ + case SAX_STATE_TEXT_CONTENT: + is_text = 1; + case SAX_STATE_ELEMENT: + elt = NULL; + i=0; + while ((c = parser->buffer[parser->current_pos+i]) !='<') { + if ((parser->init_state==2) && (c ==']')) { + parser->sax_state = SAX_STATE_ATT_NAME; + parser->current_pos+=i+1; + goto restart; + } + i++; + if (!is_text && (c=='\n')) parser->line++; + if (parser->current_pos+i==parser->line_size) goto exit; + } + if (is_text && i) { + xml_sax_store_text(parser, i); + is_text = 0; + parser->sax_state = SAX_STATE_ELEMENT; + } else if (i) { + parser->current_pos += i; + assert(parser->current_pos < parser->line_size); + } + is_end = 0; + i = 0; + while (1) { + char c = parser->buffer[parser->current_pos+1+i]; + if (!c) { + i = 0; + goto exit; + } + if ((c=='\t') || (c=='\r') || (c==' ') ) { + if (i) break; + else parser->current_pos++; + } + else if (c=='\n') { + parser->line++; + if (i) break; + else parser->current_pos++; + } + else if (c=='>') break; + else if (c=='=') break; + else if (c=='/') { + is_end = !i ? 1 : 2; + i++; + } else { + i++; + } +// if ((c=='[') && (parser->buffer[parser->elt_name_start-1 + i-2]=='A') ) break; + if (parser->current_pos+1+i==parser->line_size) { + i=0; + goto exit; + } + } + if (i) { + parser->elt_name_start = parser->current_pos+1 + 1; + if (is_end==1) parser->elt_name_start ++; + if (is_end==2) parser->elt_name_end = parser->current_pos+1+i; + else parser->elt_name_end = parser->current_pos+1+i + 1; + } + if (is_end) { + xml_sax_flush_text(parser); + parser->elt_end_pos = parser->file_pos + parser->current_pos + i; + if (is_end==2) { + parser->sax_state = SAX_STATE_ELEMENT; + xml_sax_node_start(parser); + xml_sax_node_end(parser, 0); + } else { + parser->elt_end_pos += parser->elt_name_end - parser->elt_name_start; + xml_sax_node_end(parser, 1); + } + if (parser->sax_state == SAX_STATE_SYNTAX_ERROR) break; + parser->current_pos+=2+i; + parser->sax_state = SAX_STATE_TEXT_CONTENT; + break; + } + sep = parser->buffer[parser->elt_name_end-1]; + parser->buffer[parser->elt_name_end-1] = 0; + elt = parser->buffer + parser->elt_name_start-1; + + parser->sax_state = SAX_STATE_ATT_NAME; + assert(parser->elt_start_pos <= parser->file_pos + parser->current_pos); + parser->elt_start_pos = parser->file_pos + parser->current_pos; + + if (!strncmp(elt, "!--", 3)) { + xml_sax_flush_text(parser); + parser->sax_state = SAX_STATE_COMMENT; + if (i>3) parser->current_pos -= (i-3); + } + else if (!strcmp(elt, "?xml")) parser->init_state = 1; + else if (!strcmp(elt, "!DOCTYPE")) parser->init_state = 2; + else if (!strcmp(elt, "!ENTITY")) parser->sax_state = SAX_STATE_ENTITY; + else if (!strcmp(elt, "!ATTLIST") || !strcmp(elt, "!ELEMENT")) parser->sax_state = SAX_STATE_SKIP_DOCTYPE; + else if (!strcmp(elt, "![CDATA[")) + parser->sax_state = SAX_STATE_CDATA; + else if (elt[0]=='?') parser->sax_state = SAX_STATE_XML_PROC; + /*node found*/ + else { + xml_sax_flush_text(parser); + if (parser->init_state) { + parser->init_state = 0; + /*that's a bit ugly: since we solve entities when appending text, we need to + reparse the current buffer*/ + if (gf_list_count(parser->entities)) { + char *orig_buf; + GF_Err e; + parser->buffer[parser->elt_name_end-1] = sep; + orig_buf = strdup(parser->buffer + parser->current_pos); + parser->current_pos = 0; + parser->line_size = 0; + parser->elt_start_pos = 0; + parser->sax_state = SAX_STATE_TEXT_CONTENT; + e = gf_xml_sax_parse_intern(parser, orig_buf); + free(orig_buf); + return e; + } + } + } + parser->current_pos+=1+i; + parser->buffer[parser->elt_name_end-1] = sep; + break; + case SAX_STATE_COMMENT: + if (!xml_sax_parse_comments(parser)) { + xml_sax_swap(parser); + return GF_OK; + } + break; + case SAX_STATE_ATT_NAME: + case SAX_STATE_ATT_VALUE: + if (xml_sax_parse_attribute(parser)) + goto exit; + break; + case SAX_STATE_ENTITY: + xml_sax_parse_entity(parser); + break; + case SAX_STATE_SKIP_DOCTYPE: + xml_sax_skip_doctype(parser); + break; + case SAX_STATE_XML_PROC: + xml_sax_skip_xml_proc(parser); + break; + case SAX_STATE_CDATA: + xml_sax_cdata(parser); + break; + case SAX_STATE_SYNTAX_ERROR: + return GF_CORRUPTED_DATA; + case SAX_STATE_DONE: + return GF_EOS; + } + } +exit: +#if 0 + if (is_text) { + if (i) xml_sax_store_text(parser, i); + /*DON'T FLUSH TEXT YET, wait for next '<' to do so otherwise we may corrupt xml base entities (', ...)*/ + } +#endif + xml_sax_swap(parser); + return GF_OK; +} + +static GF_Err xml_sax_append_string(GF_SAXParser *parser, char *string) +{ + u32 size = parser->line_size; + u32 nl_size = strlen(string); + + if (!nl_size) return GF_OK; + + if ( (parser->alloc_size < size+nl_size+1) + || (parser->alloc_size / 2 ) > size+nl_size+1) + { + parser->buffer = realloc(parser->buffer, sizeof(char) * (size+nl_size+1) ); + if (!parser->buffer ) return GF_OUT_OF_MEM; + parser->alloc_size = size+nl_size+1; + } + memcpy(parser->buffer+size, string, sizeof(char)*nl_size); + parser->buffer[size+nl_size] = 0; + parser->line_size = size+nl_size; + return GF_OK; +} + +static XML_Entity *gf_xml_locate_entity(GF_SAXParser *parser, char *ent_start, Bool *needs_text) +{ + u32 i, count; + u32 len = strlen(ent_start); + + *needs_text = 0; + count = gf_list_count(parser->entities); + + for (i=0; ientities, i); + if (len < ent->namelen + 1) { + *needs_text = 1; + return NULL; + } + if (!strncmp(ent->name, ent_start, ent->namelen) && (ent_start[ent->namelen]==';')) { + return ent; + } + } + return NULL; +} + + +static GF_Err gf_xml_sax_parse_intern(GF_SAXParser *parser, char *current) +{ + u32 count; + /*solve entities*/ + count = gf_list_count(parser->entities); + while (count) { + char *entityEnd; + XML_Entity *ent; + char *entityStart = strstr(current, "&"); + Bool needs_text; + u32 line_num; + + /*if in entity, the start of the entity is in the buffer !!*/ + if (parser->in_entity) { + u32 len; + char *name; + entityEnd = strstr(current, ";"); + if (!entityEnd) return xml_sax_append_string(parser, current); + entityStart = strrchr(parser->buffer, '&'); + + entityEnd[0] = 0; + len = strlen(entityStart) + strlen(current) + 1; + name = malloc(sizeof(char)*len); + sprintf(name, "%s%s;", entityStart+1, current); + + ent = gf_xml_locate_entity(parser, name, &needs_text); + free(name); + + if (!ent && !needs_text) { + xml_sax_append_string(parser, current); + xml_sax_parse(parser, 1); + entityEnd[0] = ';'; + current = entityEnd; + continue; + } + assert(ent); + /*truncate input buffer*/ + parser->line_size -= strlen(entityStart); + entityStart[0] = 0; + + parser->in_entity = 0; + entityEnd[0] = ';'; + current = entityEnd+1; + } else { + if (!entityStart) break; + + ent = gf_xml_locate_entity(parser, entityStart+1, &needs_text); + + /*store current string before entity start*/ + entityStart[0] = 0; + xml_sax_append_string(parser, current); + xml_sax_parse(parser, 1); + entityStart[0] = '&'; + + /*this is not an entitiy*/ + if (!ent && !needs_text) { + xml_sax_append_string(parser, "&"); + current = entityStart+1; + continue; + } + + if (!ent) { + parser->in_entity = 1; + /*store entity start*/ + return xml_sax_append_string(parser, entityStart); + } + current = entityStart + ent->namelen + 2; + } + /*append entity*/ + line_num = parser->line; + xml_sax_append_string(parser, ent->value); + xml_sax_parse(parser, 1); + parser->line = line_num; + + } + xml_sax_append_string(parser, current); + return xml_sax_parse(parser, 0); +} + +GF_EXPORT +GF_Err gf_xml_sax_parse(GF_SAXParser *parser, void *string) +{ + GF_Err e; + char *current; + char *utf_conv = NULL; + + if (parser->unicode_type < 0) return GF_BAD_PARAM; + + if (parser->unicode_type>1) { + const u16 *sptr = (const u16 *)string; + u32 len = 2*gf_utf8_wcslen(sptr); + utf_conv = (char *)malloc(sizeof(char)*(len+1)); + len = gf_utf8_wcstombs(utf_conv, len, &sptr); + if (len==(u32) -1) { + parser->sax_state = SAX_STATE_SYNTAX_ERROR; + free(utf_conv); + return GF_CORRUPTED_DATA; + } + utf_conv[len] = 0; + current = utf_conv; + } else { + current = (char *)string; + } + + e = gf_xml_sax_parse_intern(parser, current); + if (utf_conv) free(utf_conv); + return e; +} + + +GF_EXPORT +GF_Err gf_xml_sax_init(GF_SAXParser *parser, unsigned char *BOM) +{ + u32 offset; + if (!BOM) { + parser->unicode_type = 0; + parser->sax_state = SAX_STATE_ELEMENT; + return GF_OK; + } + + if (parser->unicode_type >= 0) return gf_xml_sax_parse(parser, BOM); + + offset = 0; + if ((BOM[0]==0xFF) && (BOM[1]==0xFE)) { + if (!BOM[2] && !BOM[3]) return GF_NOT_SUPPORTED; + parser->unicode_type = 2; + offset = 2; + } else if ((BOM[0]==0xFE) && (BOM[1]==0xFF)) { + if (!BOM[2] && !BOM[3]) return GF_NOT_SUPPORTED; + parser->unicode_type = 1; + offset = 2; + } else if ((BOM[0]==0xEF) && (BOM[1]==0xBB) && (BOM[2]==0xBF)) { + /*we handle UTF8 as asci*/ + parser->unicode_type = 0; + offset = 3; + } else { + parser->unicode_type = 0; + offset = 0; + } + parser->sax_state = SAX_STATE_ELEMENT; + return gf_xml_sax_parse(parser, BOM + offset); +} + +static void xml_sax_reset(GF_SAXParser *parser) +{ + while (1) { + XML_Entity *ent = (XML_Entity *)gf_list_last(parser->entities); + if (!ent) break; + gf_list_rem_last(parser->entities); + if (ent->name) free(ent->name); + if (ent->value) free(ent->value); + free(ent); + } + if (parser->buffer) free(parser->buffer); + parser->buffer = NULL; + parser->current_pos = 0; + free(parser->attrs); + parser->attrs = NULL; + free(parser->sax_attrs); + parser->sax_attrs = NULL; + parser->nb_alloc_attrs = parser->nb_attrs = 0; +} + +#define XML_INPUT_SIZE 4096 + +static GF_Err xml_sax_read_file(GF_SAXParser *parser) +{ + GF_Err e = GF_EOS; + unsigned char szLine[XML_INPUT_SIZE+2]; + +#ifdef NO_GZIP + if (!parser->f_in) return GF_BAD_PARAM; +#else + if (!parser->gz_in) return GF_BAD_PARAM; +#endif + + + while (!parser->suspended) { +#ifdef NO_GZIP + s32 read = fread(szLine, 1, XML_INPUT_SIZE, parser->f_in); +#else + s32 read = gzread(parser->gz_in, szLine, XML_INPUT_SIZE); +#endif + if ((read<=0) && !parser->node_depth) break; + szLine[read] = 0; + szLine[read+1] = 0; + e = gf_xml_sax_parse(parser, szLine); + if (e) break; + if (parser->file_pos > parser->file_size) parser->file_size = parser->file_pos + 1; + if (parser->on_progress) parser->on_progress(parser->sax_cbck, parser->file_pos, parser->file_size); + } + +#ifdef NO_GZIP + if (feof(parser->f_in)) { +#else + if (gzeof(parser->gz_in)) { +#endif + if (!e) e = GF_EOS; + if (parser->on_progress) parser->on_progress(parser->sax_cbck, parser->file_size, parser->file_size); + +#ifdef NO_GZIP + fclose(parser->f_in); + parser->f_in = NULL; +#else + gzclose(parser->gz_in); + parser->gz_in = 0; +#endif + } + return e; +} + +GF_EXPORT +GF_Err gf_xml_sax_parse_file(GF_SAXParser *parser, const char *fileName, gf_xml_sax_progress OnProgress) +{ + FILE *test; + GF_Err e; +#ifndef NO_GZIP + gzFile gzInput; +#endif + unsigned char szLine[6]; + + /*check file exists and gets its size (zlib doesn't support SEEK_END)*/ + test = fopen(fileName, "rb"); + if (!test) return GF_URL_ERROR; + fseek(test, 0, SEEK_END); + parser->file_size = ftell(test); + fclose(test); + + parser->on_progress = OnProgress; + +#ifdef NO_GZIP + parser->f_in = fopen(fileName, "rt"); + fread(szLine, 1, 4, parser->f_in); +#else + gzInput = gzopen(fileName, "rb"); + if (!gzInput) return GF_IO_ERR; + parser->gz_in = gzInput; + /*init SAX parser (unicode setup)*/ + gzread(gzInput, szLine, 4); +#endif + szLine[4] = szLine[5] = 0; + e = gf_xml_sax_init(parser, szLine); + if (e) return e; + parser->file_pos = 4; + return xml_sax_read_file(parser); +} + +GF_EXPORT +Bool gf_xml_sax_binary_file(GF_SAXParser *parser) +{ + if (!parser) return 0; +#ifdef NO_GZIP + return 0; +#else + if (!parser->gz_in) return 0; + return (((z_stream*)parser->gz_in)->data_type==Z_BINARY) ? 1 : 0; +#endif +} + +GF_EXPORT +GF_SAXParser *gf_xml_sax_new(gf_xml_sax_node_start on_node_start, + gf_xml_sax_node_end on_node_end, + gf_xml_sax_text_content on_text_content, + void *cbck) +{ + GF_SAXParser *parser; + GF_SAFEALLOC(parser, GF_SAXParser); + + parser->entities = gf_list_new(); + parser->unicode_type = -1; + parser->sax_node_start = on_node_start; + parser->sax_node_end = on_node_end; + parser->sax_text_content = on_text_content; + parser->sax_cbck = cbck; + return parser; +} + +GF_EXPORT +void gf_xml_sax_del(GF_SAXParser *parser) +{ + xml_sax_reset(parser); + gf_list_del(parser->entities); +#ifdef NO_GZIP + if (parser->f_in) fclose(parser->f_in); +#else + if (parser->gz_in) gzclose(parser->gz_in); +#endif + free(parser); +} + +GF_EXPORT +GF_Err gf_xml_sax_suspend(GF_SAXParser *parser, Bool do_suspend) +{ + parser->suspended = do_suspend; + if (!do_suspend) { +#ifdef NO_GZIP + if (parser->f_in) return xml_sax_read_file(parser); +#else + if (parser->gz_in) return xml_sax_read_file(parser); +#endif + return xml_sax_parse(parser, 0); + } + return GF_OK; +} + +GF_EXPORT +u32 gf_xml_sax_get_line(GF_SAXParser *parser) { return parser->line + 1 ; } + +GF_EXPORT +u32 gf_xml_sax_get_file_size(GF_SAXParser *parser) +{ +#ifdef NO_GZIP + return parser->f_in ? parser->file_size : 0; +#else + return parser->gz_in ? parser->file_size : 0; +#endif +} + +GF_EXPORT +u32 gf_xml_sax_get_file_pos(GF_SAXParser *parser) +{ +#ifdef NO_GZIP + return parser->f_in ? parser->file_pos : 0; +#else + return parser->gz_in ? parser->file_pos : 0; +#endif +} + +GF_EXPORT +char *gf_xml_sax_peek_node(GF_SAXParser *parser, char *att_name, char *att_value, char *substitute, char *get_attr, char *end_pattern, Bool *is_substitute) +{ + u32 state, att_len, alloc_size; + z_off_t pos; + char szLine1[XML_INPUT_SIZE+2], szLine2[XML_INPUT_SIZE+2], *szLine, *cur_line, *sep, *start, first_c, *result; +#ifdef NO_GZIP + if (!parser->f_in) return NULL; +#else + if (!parser->gz_in) return NULL; +#endif + +#define CPYCAT_ALLOC(__str, __is_copy) if ( strlen(__str) + (__is_copy ? 0 : strlen(szLine))>=alloc_size) {\ + alloc_size = 1+strlen(__str); \ + if (!__is_copy) alloc_size += strlen(szLine); \ + szLine = realloc(szLine, alloc_size); \ + }\ + if (__is_copy) strcpy(szLine, __str); \ + else strcat(szLine, __str); \ + + result = NULL; + + szLine1[0] = szLine2[0] = 0; +#ifdef NO_GZIP + pos = ftell(parser->f_in); +#else + pos = gztell(parser->gz_in); +#endif + att_len = strlen(parser->buffer + parser->att_name_start); + if (att_len<2*XML_INPUT_SIZE) att_len = 2*XML_INPUT_SIZE; + alloc_size = att_len; + szLine = (char *) malloc(sizeof(char)*alloc_size); + strcpy(szLine, parser->buffer + parser->att_name_start); + cur_line = szLine; + att_len = strlen(att_value); + state = 0; + goto retry; + +#ifdef NO_GZIP + while (!feof(parser->f_in)) { +#else + while (!gzeof(parser->gz_in)) { +#endif + u32 read; + if (cur_line == szLine2) { + cur_line = szLine1; + } else { + cur_line = szLine2; + } +#ifdef NO_GZIP + read = fread(cur_line, 1, XML_INPUT_SIZE, parser->f_in); +#else + read = gzread(parser->gz_in, cur_line, XML_INPUT_SIZE); +#endif + cur_line[read] = cur_line[read+1] = 0; + + CPYCAT_ALLOC(cur_line, 0); +retry: + if (state == 2) goto fetch_attr; + sep = strstr(szLine, att_name); + if (!sep && !state) { + state = 0; + CPYCAT_ALLOC(cur_line, 1); + if (end_pattern && strstr(szLine, end_pattern)) goto exit; + continue; + } + if (!state) { + state = 1; + /*load next line*/ + first_c = sep[0]; + sep[0] = 0; + start = strrchr(szLine, '<'); + if (!start) goto exit; + sep[0] = first_c; + CPYCAT_ALLOC(start, 1); + sep = strstr(szLine, att_name); + } + sep = strchr(sep, '='); + if (!sep) { + state = 0; + CPYCAT_ALLOC(cur_line, 1); + continue; + } + while (sep[0] && (sep[0] != '\"') ) sep++; + if (!sep[0]) continue; + sep++; + while (sep[0] && strchr(" \n\r\t", sep[0]) ) sep++; + if (!sep[0]) continue; + + /*found*/ + if (!strncmp(sep, att_value, att_len)) { + u32 pos; + sep = szLine + 1; + while (strchr(" \t\r\n", sep[0])) sep++; + pos = 0; + while (!strchr(" \t\r\n", sep[pos])) pos++; + first_c = sep[pos]; + sep[pos] = 0; + state = 2; + if (!substitute || !get_attr || strcmp(sep, substitute) ) { + if (is_substitute) *is_substitute = 0; + result = strdup(sep); + goto exit; + } + sep[pos] = first_c; +fetch_attr: + sep = strstr(szLine + 1, get_attr); + if (!sep) { + CPYCAT_ALLOC(cur_line, 1); + continue; + } + sep += strlen(get_attr); + while (strchr("= \t\r\n", sep[0])) sep++; + sep++; + pos = 0; + while (!strchr(" \t\r\n/>", sep[pos])) pos++; + sep[pos-1] = 0; + result = strdup(sep); + if (is_substitute) *is_substitute = 1; + goto exit; + } + state = 0; + CPYCAT_ALLOC(sep, 1); + goto retry; + } +exit: + free(szLine); +#ifdef NO_GZIP + fseek(parser->f_in, pos, SEEK_SET); +#else + gzrewind(parser->gz_in); + gzseek(parser->gz_in, pos, SEEK_SET); +#endif + return result; +} + +GF_EXPORT +const char *gf_xml_sax_get_error(GF_SAXParser *parser) +{ + return parser->err_msg; +} + + +struct _peek_type +{ + GF_SAXParser *parser; + char *res; +}; + +static void on_peek_node_start(void *cbk, const char *name, const char *ns, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + struct _peek_type *pt = (struct _peek_type*)cbk; + pt->res = strdup(name); + pt->parser->suspended = 1; +} + +GF_EXPORT +char *gf_xml_get_root_type(const char *file, GF_Err *ret) +{ + GF_Err e; + struct _peek_type pt; + pt.res = NULL; + pt.parser = gf_xml_sax_new(on_peek_node_start, NULL, NULL, &pt); + e = gf_xml_sax_parse_file(pt.parser, file, NULL); + if (ret) *ret = e; + gf_xml_sax_del(pt.parser); + return pt.res; +} + + +GF_EXPORT +u32 gf_xml_sax_get_node_start_pos(GF_SAXParser *parser) +{ + return parser->elt_start_pos; +} + +GF_EXPORT +u32 gf_xml_sax_get_node_end_pos(GF_SAXParser *parser) +{ + return parser->elt_end_pos; +} + +struct _tag_dom_parser +{ + GF_SAXParser *parser; + GF_List *stack; + GF_XMLNode *root; + u32 depth; + + void (*OnProgress)(void *cbck, u32 done, u32 tot); + void *cbk; +}; + + +GF_EXPORT +void gf_xml_dom_node_del(GF_XMLNode *node) +{ + if (node->attributes) { + while (gf_list_count(node->attributes)) { + GF_XMLAttribute *att = (GF_XMLAttribute *)gf_list_last(node->attributes); + gf_list_rem_last(node->attributes); + if (att->name) free(att->name); + if (att->value) free(att->value); + free(att); + } + gf_list_del(node->attributes); + } + if (node->content) { + while (gf_list_count(node->content)) { + GF_XMLNode *child = (GF_XMLNode *)gf_list_last(node->content); + gf_list_rem_last(node->content); + gf_xml_dom_node_del(child); + } + gf_list_del(node->content); + } + if (node->ns) free(node->ns); + if (node->name) free(node->name); + free(node); +} + +static void on_dom_node_start(void *cbk, const char *name, const char *ns, const GF_XMLAttribute *attributes, u32 nb_attributes) +{ + u32 i; + GF_DOMParser *par = (GF_DOMParser *) cbk; + GF_XMLNode *node; + + if (par->root && !gf_list_count(par->stack)) { + par->parser->suspended = 1; + return; + } + + GF_SAFEALLOC(node, GF_XMLNode); + node->attributes = gf_list_new(); + for (i=0; iname = strdup(attributes[i].name); + att->value = strdup(attributes[i].value); + gf_list_add(node->attributes, att); + } + node->content = gf_list_new(); + node->name = strdup(name); + if (ns) node->ns = strdup(ns); + gf_list_add(par->stack, node); + if (!par->root) par->root = node; +} +static void on_dom_node_end(void *cbk, const char *name, const char *ns) +{ + GF_DOMParser *par = (GF_DOMParser *)cbk; + GF_XMLNode *last = (GF_XMLNode *)gf_list_last(par->stack); + gf_list_rem_last(par->stack); + + if (!last || strcmp(last->name, name) || (!ns && last->ns) || (ns && !last->ns) || (ns && strcmp(last->ns, ns) ) ) { + par->parser->suspended = 1; + gf_xml_dom_node_del(last); + return; + } + + if (last != par->root) { + GF_XMLNode *node = (GF_XMLNode *)gf_list_last(par->stack); + assert(node->content); + assert(gf_list_find(node->content, last) == -1); + gf_list_add(node->content, last); + } +} + +static void on_dom_text_content(void *cbk, const char *content, Bool is_cdata) +{ + GF_DOMParser *par = (GF_DOMParser *)cbk; + GF_XMLNode *node; + GF_XMLNode *last = (GF_XMLNode *)gf_list_last(par->stack); + if (!last) return; + assert(last->content); + + GF_SAFEALLOC(node, GF_XMLNode); + node->type = is_cdata ? GF_XML_CDATA_TYPE : GF_XML_TEXT_TYPE; + node->name = strdup(content); + gf_list_add(last->content, node); +} + +GF_EXPORT +GF_DOMParser *gf_xml_dom_new() +{ + GF_DOMParser *dom; + GF_SAFEALLOC(dom, GF_DOMParser); + return dom; +} + +static void gf_xml_dom_reset(GF_DOMParser *dom, Bool full_reset) +{ + if (full_reset && dom->parser) { + gf_xml_sax_del(dom->parser); + dom->parser = NULL; + } + + if (dom->stack) { + while (gf_list_count(dom->stack)) { + GF_XMLNode *n = (GF_XMLNode *)gf_list_last(dom->stack); + gf_list_rem_last(dom->stack); + if (dom->root==n) dom->root = NULL; + gf_xml_dom_node_del(n); + } + gf_list_del(dom->stack); + dom->stack = NULL; + } + if (full_reset && dom->root) { + gf_xml_dom_node_del(dom->root); + dom->root = NULL; + } +} + +GF_EXPORT +void gf_xml_dom_del(GF_DOMParser *parser) +{ + gf_xml_dom_reset(parser, 1); + free(parser); +} + +GF_EXPORT +GF_XMLNode *gf_xml_dom_detach_root(GF_DOMParser *parser) +{ + GF_XMLNode *root = parser->root; + parser->root = NULL; + return root; +} + +static void dom_on_progress(void *cbck, u32 done, u32 tot) +{ + GF_DOMParser *dom = (GF_DOMParser *)cbck; + dom->OnProgress(dom->cbk, done, tot); +} + +GF_EXPORT +GF_Err gf_xml_dom_parse(GF_DOMParser *dom, const char *file, gf_xml_sax_progress OnProgress, void *cbk) +{ + GF_Err e; + gf_xml_dom_reset(dom, 1); + dom->stack = gf_list_new(); + dom->parser = gf_xml_sax_new(on_dom_node_start, on_dom_node_end, on_dom_text_content, dom); + dom->OnProgress = OnProgress; + dom->cbk = cbk; + e = gf_xml_sax_parse_file(dom->parser, file, OnProgress ? dom_on_progress : NULL); + gf_xml_dom_reset(dom, 0); + return e<0 ? e : GF_OK; +} + +GF_EXPORT +GF_XMLNode *gf_xml_dom_get_root(GF_DOMParser *parser) +{ + return parser->root; +} +GF_EXPORT +const char *gf_xml_dom_get_error(GF_DOMParser *parser) +{ + return gf_xml_sax_get_error(parser->parser); +} +GF_EXPORT +u32 gf_xml_dom_get_line(GF_DOMParser *parser) +{ + return gf_xml_sax_get_line(parser->parser); +} + + +static void gf_xml_dom_node_serialize(GF_XMLNode *node, Bool content_only, char **str, u32 *alloc_size, u32 *size) +{ + u32 i, count, vlen; + char *name; + +#define SET_STRING(v) \ + vlen = strlen(v); \ + if (vlen+ (*size) >= (*alloc_size)) { \ + (*alloc_size) += 1024; \ + (*str) = realloc((*str), (*alloc_size)); \ + (*str)[(*size)] = 0; \ + } \ + strcat((*str), v); \ + *size += vlen; \ + + switch (node->type) { + case GF_XML_CDATA_TYPE: + SET_STRING("![CDATA["); + SET_STRING(node->name); + SET_STRING("]]>"); + return; + case GF_XML_TEXT_TYPE: + name = node->name; + if ((name[0]=='\r') && (name[1]=='\n')) + name++; + SET_STRING(name); + return; + } + + if (!content_only) { + SET_STRING("<"); + if (node->ns) { + SET_STRING(node->ns); + SET_STRING(":"); + } + SET_STRING(node->name); + SET_STRING(" "); + count = gf_list_count(node->attributes); + for (i=0; iattributes, i); + SET_STRING(att->name); + SET_STRING("=\""); + SET_STRING(att->value); + SET_STRING("\" "); + } + + if (!gf_list_count(node->content)) { + SET_STRING("/>"); + return; + } + SET_STRING(">"); + } + + count = gf_list_count(node->content); + for (i=0; icontent, i); + gf_xml_dom_node_serialize(child, 0, str, alloc_size, size); + } + if (!content_only) { + SET_STRING("ns) { + SET_STRING(node->ns); + SET_STRING(":"); + } + SET_STRING(node->name); + SET_STRING(">"); + } +} + +GF_EXPORT +char *gf_xml_dom_serialize(GF_XMLNode *node, Bool content_only) +{ + u32 alloc_size = 0; + u32 size = 0; + char *str = NULL; + gf_xml_dom_node_serialize(node, content_only, &str, &alloc_size, &size); + return str; +} diff --git a/src/utils/zlib_symbian_ext.h b/src/utils/zlib_symbian_ext.h new file mode 100644 index 0000000..049c20a --- /dev/null +++ b/src/utils/zlib_symbian_ext.h @@ -0,0 +1,207 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.1.3, July 9th, 1998 + + Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +/* +This is an extended version of the zlib library designed to run on EPOC +Symbian Markr 5/11/99 +*/ + +#ifndef _ZLIB_SYMBIAN_EXT_H +#define _ZLIB_SYMBIAN_EXT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +//#include + +typedef voidp gzFile; + +gzFile gzopen OF((const char *path, const char *mode)); + +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h". (See the description + of deflateInit2 for more information about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +gzFile gzdopen OF((int fd, const char *mode)); + +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +int gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +int gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +int gzwrite OF((gzFile file, const voidp buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +int gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ + +int gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +char * gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +int gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +int gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +int gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +z_off_t gzseek OF((gzFile file, z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +int gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +z_off_t gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +int gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +int gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +const char * gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + + +#ifdef __cplusplus +} +#endif + +#endif /* _ZLIB_H */ diff --git a/src/utils/zutil.c b/src/utils/zutil.c new file mode 100644 index 0000000..884630f --- /dev/null +++ b/src/utils/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zutil.c,v 1.1 2006/12/13 15:51:06 jeanlf Exp $ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/src/utils/zutil.h b/src/utils/zutil.h new file mode 100644 index 0000000..4b61258 --- /dev/null +++ b/src/utils/zutil.h @@ -0,0 +1,277 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h,v 1.1 2006/12/13 15:51:06 jeanlf Exp $ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#ifdef __cplusplus +} +#endif + +#endif /* ZUTIL_H */ -- 2.30.2

    7;_GlLo7p*NPJoo;Q55+c#%`ILYcRMd${8 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�%!@3#mN2@Ij3_ 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 EALgBPy8r+H literal 0 HcmV?d00001 diff --git a/regression_tests/auxiliary_files/count_german.mp3 b/regression_tests/auxiliary_files/count_german.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2ac2224f95ca4478cde36ef683580c2275ae9113 GIT binary patch literal 57365 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 pIGG25F-%kt=pP!QCYg+zFNh2vk$= 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�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 a28RcMD1fs+c~Al8aS-U={G0#S{eJ-V9n*LK literal 0 HcmV?d00001 diff --git a/regression_tests/auxiliary_files/count_video.cmp b/regression_tests/auxiliary_files/count_video.cmp new file mode 100644 index 0000000000000000000000000000000000000000..05115902b1b90376a5ef3ebde4d9459618da40a6 GIT binary patch literal 146688 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 + + + + + +@ECHO OFF +SET XALAN="" + + + + +IF NOT EXIST .mp4 MP4Box -mp4 . + +C:\Users\Cyril\sourceforge\gpac\bin\w32_deb\mp4client -bmp - .mp4 -2d + + +@echo Creating HTML for +IF NOT EXIST .html java -jar %XALAN% -IN .svg -OUT .html -XSL /svg2html.xslt -PARAM filename -PARAM next -PARAM previous -PARAM snapshot +@echo done. + + + +IF NOT EXIST .mp4 MP4Box -mp4 . +IF NOT EXIST .xmt MP4Box -xmt . + +C:\Users\Cyril\sourceforge\gpac\bin\w32_deb\mp4client -bmp - .mp4 -3d -2d + + +@echo Creating HTML for +IF NOT EXIST .html java -jar %XALAN% -IN .xmt -OUT .html -XSL /xmt2html.xslt -PARAM filename -PARAM use3d true -PARAM next -PARAM previous -PARAM snapshot +@echo done. + + + +IF NOT EXIST .x3d MP4Box -x3d . + +@echo Creating HTML for +IF NOT EXIST .html java -jar %XALAN% -IN .x3d -OUT .html -XSL /x3d2html.xslt -PARAM filename -PARAM use3d true -PARAM next -PARAM previous +@echo done. + + + + diff --git a/regression_tests/auxiliary_files/index2html.xslt b/regression_tests/auxiliary_files/index2html.xslt new file mode 100644 index 0000000..617eb42 --- /dev/null +++ b/regression_tests/auxiliary_files/index2html.xslt @@ -0,0 +1,62 @@ + + + + + + + + + GPAC Non-regression Test Suite Navigator + + + + +

    kLD((Pmu$p3xLPaC9#g! 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

x(iRmCjEYhEK+JvOS!@3Y(aC!>eW% z)A4v>e);jL%+86?b2?eQFFxlQxW$97(g||DrlKQQB7cf;(UNgFL*il?5~F{NUZMu( z#OMe?!n?>2Bcq~tQ5g^EDZkbHhk{VbGbpL0P5*lql>96zOYx-#Q24d<=jZ91;cYWv z6t)R9DfRfx)z$Z>g3L{fkRV5Pq8YO)+TI?l{xGvVi~2q6QgPp`bK-3pSNqo>n^8XX z3tE{_$zge7x#@(Z(F;~Tt;%%Cunk*I$NRC&V8y73ek5a9HO7o>hkt|_8_ONm4Q)8= zv#NMu2FnTgKc7AHti>Pkmwsa^D0MyiF}kGBkDP;^3d(z8V#k+8eeH|#kW5r+?QiKM z^Z7urFP-u$XFIQjnNPT}U(69`wG zpYn?u;aP26e!=moeCl{+XQf=M8l?xR=P=0@Df*`B-1E8hQwVvPTThs`Zfc1$8I3s< z%329}%F}q|(1{b3H&+`D75Z7lz`|)sSASaD+i54$Pi4@pSkTO!$0`c))i3V*(aX)# zD%#&1rjKQYDheVZz>o2*JT@U%-&c@l0Qxs3gE{jnarNKoqoUhx9?pICuPf`*EZa@HTq@VX-q6exUt*HjBm==}LHRo9}3$#o%xHm}i}6=JYWO zMu}9xnm=b|N@A4^`mF;A+kP6Yj7BY|^XlAmub9c*SEg(kt(7UNWPW*l48nndzFeQM zP;>Ew`K=)@WRBh;r7<9D5y&LVEv$ARizfgyw%3W_7gJ$_gPXz5^7QKJlyH+CVieq# zje*-}y^VvLbAvc5=xvaP#OYx;T0t`|49}v<_&ScUYA?Y2GKk3fFVlCu4LF~?pGjT@ z*dQnI^oE?-1F$0DRR+tp8-EG?!1%axO3lDH_&sV;R{X2z2W@?%Bw-PdGZbDGv~NYmO;olo6>?y}l+ms;WI1GHYfN?`o8eYZ5Od7K{hL1^Jvl$SO0l!sjV`Of@hdog~R z?Nz4!#%hS3C|vxV<48-Fn;GMkD4bh?`Er$%=Pg}0mc=b=<#gQoV4Pkh;~vpPItPZ+ zbq^Pk!(hy~+6M#^^PV@ZruXJsHxOnp+QOGl_a%78C8n?_{h(V;@GaK#swmw>m9j#w zR^rX{hA`cqOK%L*7UGbGbA2LI2D8m!dOy((iK zqgCMzDaUhMKWZ6?GL?22vf|1QTIL|Lr6=v793awO(}2n`K^`3xc|E7W)#!ri@E+IO zd*VIFn|Hk|LKO+JUtWP)6qRjwZLQgQGUz8W*G`+Lb-leJ^DplVvO1G}l%3eX=Xtd4 zJQRF?Q#20%7yeoWQ|b?u9pc(+vtx<43dV9$|D2@$YU(F|AFWTfP31nxjw@@z8{zGg z>i9m#IDf1nH;`MnS*qYfeG6N&!`AQRO;Xan5>;X^s|jbPa&OHqsp2Sf&$nwvwJq1y z3vdR?_?*R>*7;Y&E1%~$tmo}GEDpjNI>7lrcRn93?3bU$TJVHL*0Aq}>pY?V_eU9Y zW@UfHq1J(*>kghTb*OTvb??q}ANLaq6ZrLV^S0I2cm`5# zQQlAvEKez&Tq$$1G>YcL@ns>+b*b&`Yhr*?tE!R-T z_NNl+v^Y9>c}4CY!jIO1!HJQ{3ybUOfp*v@}Lb{Hq*`g-0vL^m!-lPVTG2I zG>o}{@OMSx|CJZjtYlG%TQ5PTHGvGNY)z@bV#RBq!DVvqUsMKL{A*Q#0 zOVNVEDHig~9~}Nx@Zd&Ce?+B0JrUM96i}M{r$_$dnDr-l;S~I9`-Qf5j^Z-KX^pnu zjP1!0C+YLPM5 zOS8_pt2OmO=cJ&^^hxPDr%vd4fW>ONjaMyw!PiG`<4gX4l6>mB$D#R{mC&1@Ji$@; z$8NjAu^+|52)kC%GqdX_A^;?^8JVnK`l}6bfC^qK+tv6cnlwEaTdy`CTAUJnaG6Nd z`3FU2ah=)iPvs_kq!Ju5k9IzHY$-o=H$$bC7}b$DG-)mDQW$thx2RX>oB7LdZpSXm zbJ?sXX9EbjK2jyvY$EF&sfQmUBlp5blyX_8UiL9y z4p$~y`f&V=j6sJEKiYAf#lvhb%bWVgq`VEXz5GTC$!%ThbBtb1f#h}a0&tbA5M+wD zrOv^MY@5hjWoDvXMeu13|4Oq&JiZSh=z36d)G@)ICM|1;*Si@0CxF+RUxQftHy>@k z*e{D9>{T%l9sWJ9_wDrdkDzNA{qzwmQnM!SbzQ0Q?T3GcpE!WpCPl8r6<$XQj=KH_s1$z8?*-SKW-rKL78Uov z=_QG1Gq3t&TPdpoV=1*jVG;kOdPC|PQ8^5#OO17KJ|as~mro8qR`d~J8P0=q_}H|G z>mKOWBC~zk_x~c%okY7IWY6Q4`3QO@+PEAH>@#AZr2|c2Up2JFWDi2t;_}p5TUd=k z+15*)?&}I&+zYAjIaR2YUt)4MpA>{&W0yGmV)UEZXA^#{&6UISD28=3n0JDLFBN}k5CHSma z3%dSM1ZV}YJR6CWX_G{HrfFNDX@g`W5~&f58y0ykW#K@=w)Q7%t4%AxEJIrCWNEi^ z>C=x8uUv?5!<%vpCHPVp-beV$*%k{QQ^51)vp|Cc3ABC_{xJm&aX8^Oy?X=0i^2nL zKC!wa)X~a2{rYTsk6%-c;^4nGix(7cr+8_gDCFy|L$3r{pL9Rc!CiUy=d-X|Fs#` z+y{!y73DV=Vyf+R)w&fZS>DM4$Q|_jkl&;iO7&fYsq&-wY);+>GdghR+GNH^S>fV) z%X*c5*!Ipu|1TQ7kjb|-x!=}gM5Nq{vCk;jtr&Umz~y`ix_?`U5*HLN&-$h(MKyHy zZPv^{B8UtW7E@sOr#*%vliTYG^17G)dSg5k-PT75D$K%1L1$lYgUYu=?QCBA3yPNW z<$I#`hYN$e?C&=wZ|^O9ic-Hn5a*S=y{B+hc{yuxgIXOb{Dmkdj2!fC3n-$$eTDyS zg#)@7D0Ip!nC{n2e?d{MP(orS!Fyc@_sL*w+xST=LHP=>WZ*25-Gx7bm*LetC3_P$ zmKXRQF$22~JY@{W*|&P&tM-C(&V8Cb5iM3Ym#+{w{R%BQRCuQFBsaG2_Y7DUY(Jc& z$U-CT-XB@CJ>oe#>fu)%c`r;c8{{T&F^8YSBet6Rpl9>T36*fPf~33EQ(>Z%g!cf5 zt0cTB-KXj=;*ae&`o8Il8eYZ&c7ZgSvR>3i&t+EDP4qgwR(6Bxs4s5Cqg8fA&UYw$ z57hSanmw=?K;h!ANRYErexjEw_W&I&{r|GuGs~h(S-O9;a#5<%@jS}1edg!W{`-ne zI9JP>gncM!TG7`WG?lEjVaN5}1yE`m&~Y4O&*8BoYB*JJtte4v2?t}}04X6uTpVGA z1YOUAo`~^pHuxk5t*$fs%h7WC%UFL{_2&(Rf@5LExVe@j31Eo)B==va6rhENe`uD5rUOy~opX9n2WTiO}=9YA?1to(YZGZTYy7`3Jd9nv= z3WKiCLLbKBozfa4nvzjYh}c-}ZXAVuEB>Ga>$2BQBeKy3Qd`5IeLq*77`qn0HA*w@ z6i(x6=?i^O-BAKtrl|ORilIS~r1~7=tGQJJMDZ(pI!W=nRS>a>E5#En$?ZV)Tc>>~ zJvHBYEV`{qr>ZGuNPTo$wGuGl*jVKGV2En0%6_X-YgJear7GuCi>*YRKgibO+4?o} z;Q}40F-8A0Eg^A^Lq*OpsoFh5Rq-&zKV!X3^GJi)zu5vUpVQd$;?*qPX*Mifi%zMN zeG$>9=%JICXebp$*c7JFGk8006f=#;PYE}*QMz7dm!M}#Sds07N>jHAnYH9h8+1#y zaM=K~*E>(xE?C_i!d6;Tuwk+-2i@z&t=)E|;QQRDn)i=eGss$gfb=xZaZ%j$Vxq1G z`6cR_`&zaP3$tcmwM9j+MO-s_e;^x!FfF;s4~)lceemxn*ESP*4Tm%QqT4x91;9?LnJ{CGC~yR5T4@GdjSbwM;jm(<%scC;&c=j5^t4iJZc9ei=H)W zhWrD0omlOTiwiGMR>BVrM*nbm$tph84b14SzA06!@A+&1$thtg_#f_|Qerg3=7EQ^b^w zF6iFKlY)*kvF{GKaTfiod_DwMmn;S?0tNFow)``vC|~Kz?SOTg)e8U_*iG&X=2h8| z3bRF39G#7m@26BY&UeK5Q69cs+lBPo=6QTJVmjH$C8m>}JQ35I@pN+h@|X(Ow={TzL2^2@+ea<>^T^9h)9^{Y zS#+HY6F=gg~2eN)Y<`>v`u zC+M=c`<5uPV`8plzxupd90ky*XiK3tk?v$t;ah-r)vFV2Ty3|ndR6{SLH07S9E&gs z{hX0ZsMsrKAWn2@J|X8X*{-qiWlu=;NaYXuU8odC&WhE#p|zk(SyXUqEu^jReV|RW=Q#gG>PDkc}RBIkcoY*+v9#-2!auDrz&r>|d z_H}&tOR(xTdq>~{R^9i3iS(fodKb1F$?T}CZ0QFW=2L|Ib)g2q9KdtxSam$xHMGg) zp%fgmn=As9Qy;4$s=#Ub$Fht+;2XcZ)@!It>vhnUQ&HIGjvdj&qS?y zIuqL{1an-5Q7uAIPNiHmu4A+GZ$NYaP^fp zJD?V_`pUR9?(qFki0(;Wc^8Gnetb_DG={%-{znM%ib}O9Ana#58MU#qGvnF@_?@kY zEdF4)0KZDd=u$TgwYiU)xt$n{^Ql$rmqkL;a%3m#gBKg^MS162+De4Q7}qbT(Nu2b<%RWsX<1#9kQyRk)!*62UCnH#5@vIJ8kB99RfOh-3M1ITdKo-X&S848}TjfjAD77BLK@M3)hw7kJ#8XcB8O93j zMIeom*iQ-&t#|yRhE+<2K$m zOmjt#IC@mwsXbM{##P&l?%w~>0xav^wWCxUr%|k-+I6)1biNT0piF{g5JR}vCI!8{Dvqw2e25a8UtHQ6dagLlX=g9b+Bc>i1jsz&3<&i$BTv<9ZycU7n zqunUDnO2fj?Yua{ixQBDds;mP=T5sP(^1J#=Edn;j?X-=1Llu*)Onp+m+9!3pD>yKM9HfjoDgpP#;7jpo{q3pG4=4{1JSwFiVH775Vb6_i;ye8Y_rq$W4yUD<~*@ zU3OplLGt+wXZJa_gL8xUf_P`g$2xs^=Cz5%`=8qL%HsWxxhIQG0Ybl5zZ&Thr&FDv>jrdp_lh^4|$a_`&ea2#E3IeC^Xr@`TE3t1Iz2Y)r+M?x?89OsW`pQr4&c0{mos`ZiT$ zefme;r|N&f9~+vRU(=`Lz6VX8l4C=}@vog2P{$()?z!z!^31OyhF!tvD2o)uu4wi| zG(Io9SEt*BH>VifefcSM4f{r_*X>AMRH1Bp-)VaXAz>G9sA&I5W@uvOc*P}suk7V& zZR#R>-2NQAY6I5nqv7x5Rt}RnID0}o?2=lNwA`F(yF8y--LMaNedRHd?7W=1xC&OQ zO8KKES^F)+__*@&&8ZOH@;p2*i@i6t-HmStoXwlB zQKxn`e^P%k{jOEv-7b%J7b5_b;a$z=J>H3|egQ0Et`b4H-46ea z=4mhr!&1+eRFTSGU2a3LyzK6j0W8DLd<<_9$!RWCAMt6ggtJA3;+$+qQpt0m@nF9quv;eeP`yy?Ji>}dLApNBgg3Fs`cn9LyI;gjf7aI2MoIA%4Nc4mq_q zx7WEN8(zoFSKx^-Shx#F>bdqy;E*T5ChL_XL%6xpF#*aEvYV&7zDX1)_AaoqjQM2p zK}*GF^cF+LfXm-DchU^O?9Y+%jRL_4m`ww{DjyFQ%}5ZmX?EqJxi*L$6UiPjqoVMB zDyV0|i(iHw?fA^LDc*lFX9qgx@0&CuY_887uMD~h+D4C~a6JZwWsakjybPWZfW(%V z?tpm{Mx$`%aYb;C#+lLB5@eOiG3b$dK7gF$9Ujw<=TT9%4Cq}@M|96mGfdnb@CVPs z-C$6HbiwNT=!!;6BV{J5Y{`G8h(zY1kVm=dj`;RilnAAtu~8WAG%YZc!qBrfZYK9g z+*}>3-H0eJ$>5s9+p>L!w*_6_w}SCes@T@fCPS+@{Ww@6{1B!HE?MPWOV(4^w17iE z_^75?-?T5z9s9)(!coP8r~6{VpzsQiPQd>ZkU;l@#{=bfoET~*k&_x<9Lv~OP+m^A zKZTRrx>hfv&za>0mCOmhvJ)d}3Dhk6DUrF#Yj(WjZ1)df5R<(84nF{VIf|5lqs&#m zGzq{#lhC`IZwny3I{F(R#l1TE8~8ZS*l^JT4`S^sQvs0MzmE@p5`PoRt*SlX6^6&U zX}~E~uSe!0d(tYhwHetV<8d7;%C~F_>+1=xL9*csjm3cD_4(V6^jKbBCz~*%K_lpywC-dX#t0YxbSk0x_J| zuO?RbZ6}^{H5T6pJ#Haxh=dVGVmmqAdNSm}FN3)qqYfceY+lO|5EL-3-Gb?c6`&2} z+DWG(ryloPhY}L!yL8)`x&%|=Iz?pGk=(Q89n&cw)+2SN^~msKoPW^kZ4I(-Vz47Nv+1A3hKzhdqL*`mnLC(s_oFi>+gJcmq z7c57pTD$zd=zOlyUEWc-{rt}JVxPMuZtaRDDY>Ijf&9(5D?DiPm0qR3CuG6Vgd#nozorF=4&W?LWD&qD<*4fy|Q?CrGVXebuY_c;n0 zD5` z7(vCM&c1}@YeRIKd%;c2$%|rMttbw*HoX$8{Ue4RUW^*->UpVP&07SB^hQrJ@Z_wV zS5>(b<=Q5>)DrTP?$bt0oEYAEbqBX&17B8sP$|(lk7F>t4)3b?x|$smC+=vh;6JwO z$(1|j$t*{+U3dzD6J_%#{{VZ0V*|7DFw^I!8-#M6CBF?tA|Me{CO-LE><)QO2a9o_ ze$G|GhI6LtmkD&R(HuS!oqvUxz=f-Dxl?v3FLvQa%SWF#my0cKR{%AH6yV0G5Th_$ zqL*+@UHCH^s*L=-qhqKZis!4dZ8Z#fN|cb0(k6!A0C`h5cf&i|bi5-b{p#UY3$MT} z86V~FyLD~hN1!MEokJr!=-r$oZEtWS7~^H*i++c2qh{KT>;~Cw$AK(rtkdvW4^qX^ zf3!WdCL6S@Gpc}7w+O-*Vvj8;O#2E&^FLOo>>$>p@(_$5`vc}(Q1NqO@5AEaz>ze=d3R*>HyA!|TFhfW;6b0ag3E&j6;Mc_&R9L}?PW zfxH9S5dt%eaTgbKgq1p(Y~FwI&n9;dg!2&zk0fIEqS=-6=gL6C#x+rrw>aJhF~d8^ zl=7o4LB7sy&m6rkJBS~^tyQx7-&)5S>E~+bvVfW zCF5hY;Q;ABVy1Y%(I>c3z=_HSFRU#P3fc4pcmqL{xT9 z6D$ynkuA0cU2mZu9rRF}E`)S*JOrMry{AyK>oTp!pPX>|F^L2k+fiCB@;jcqX zf#tR!`=3PW@10S&7%)rk(lo>PsDixnn2dtby_B_Aii15X%GCO>BjT-Vq2Q}Vv;{t_ z4SQ{ny`B%AfgX9M(^4bD|9(^xN@=4e7N0cBnqSSXIq9B6Z=g20fd`Hr1)9119Sksh zQ@(vbdXs#lyK{5dgZfKKpZt(6$XH=da@ixgMgRHj+}G>5y!t8q=GL1h)}qBZi_gwC z&B$dRvGOPMo1e<>PCM*M^B1iC5)y%*A1KHAdw7gEz;9@kL~r}48Qxu^ELj!s?l9#% z_VmgVD=1YyJ3w)f0nP5=PE@P+VJ>2O823J;YbZqT<92~4CT)_Z6B}tfp%Zm8zZ0yf zL?}BwS@?Yf-O$Eob36Q(1xFX;rzg6Q)yJc(K2n^QMDvkVXQHh2WE>jNyOGBt6TO?b z@RZ_3dc++6T9o4JY9p)JaDD#yu=?CSM|2{qZ$)2LuZ#Uns6|#CioR?hOmNCm42M;2 zVoHSLB)?!g(M-FAuj%=4FQB_=-5U3<;yXniy_}i;8KOhb^AqzU^Xj$UE>$*lpF@B+ ztJm`@s^Bg`jyP)-oM=KQD$m@OR>F@ywczoiU)S@%k1e`D@6o&2p6b0Q>Ar1`Ct)0V zyEthSqUIW$9K(UH{Vq5zo~&`=%%)kvYUSySvufDGILfLIl|`LcR$q^@)|oo6W+qU%|8Aj(>I#*W;Lb}^&< zB$Q*q)nA`V-Min1rhvcf0@*wG>k=!-W~`kcNpk7<4@Y+NZtfLFtb)v1+}bJ9vfn-HCW|$?kZc%Qk!H5aR88HRev>c^mst5^-FX-}In; zX}9P>(6tz%qkvP<%wb)#^^K;f9#$u# z20CVfXOrwwjyQXOC@hwzWq`>w%?|VShIxCG>{g3pw`!W}h?N5zQmI}x&;?n=eil2L za+fO#bO`UM6XY|n1ix#w|f^1a>|>Yg z`C7K05f`W6gx$Hjmq9xVyS2(-J!b;#v*9w;h~g1kBVkq$W9!K^%_`(d;-=4MBmZ~{ zKw2OPiHyA~`@%Z0e!bd{!b@_t1<7%`cR&U3f_wB=yx?A4EP)qP)m(PJZn608-kGiA z66ymx*#{2g)<@jum%tAm)I~G=;4&_9>qX>qPbu-E$Fr+@brYt|>~cI;O7 z4;;<8`Aw8BfkjkrB0Fd`VY!-TS1vK*SXRA5t*@>bi-_XA88Om*#}P#{D6CbVuQg%v zDfM|tIg5C$36sqxOg5V^xn%Lq=0uouR$2A=j-Bp0So?t5uwB^odEeoZJUNHPvu@X;G*1|M? z^Y_+7S>3v$g7Xe-jl;9gIL_Ag4*#r#=byAzHt4ta5&bsRIDrR_#*08%4a|MOv5nS| zvPMHYp*Suzx~iav=dyg()nR?sosZE9T+W1CiICR}B(!^_Fmp4+I6)ASvda2yR z!P#%*L$SX}4$|Hy<#^w@4MjvU9ZtjaOd zwPL2F(M|*heY~jMZk|!;7g+RF&47c;8G*e{RXtI8NY|6&-YWeStgCfl(;I!&P{}6n zjIh3=tj*8rdP3Y=qrU=mZCRTF_-Q~5i#|T6w}XVNXPF9`VbP!GHqNTEMNn&4xRbE9 z^Y;!Li>^UMCcFyT>SC0&z6jdtWt6o(o&@a@gck#0$HYISJtD|F#|9MJVvFByNyW4a zEs;it48%o^j;9ZKP8rHB;6wR)>p+vXd{UsZ5M3iGZ{H!9))d2;m7AZO=7LN90&EjU%0jIEV%h zZqu*M`t~x)qN)Dk9bw*ZX^!IWpiLZ&y<6wPgJ#>vRJP~}y#jXambr{aH*jcxos+$& z-sG8EQdc_!U;SROLjO{@gkG#zqU!`n&IQ^ z4e{|O`=7;Gdp&EfXYJ*zUJkO~0$Ci&g4k!n$B)(b3iCHOVc;ZL&JfhCHY3fprV@lT z?$aZCuV7v{07T$RJlSQHj6(3C!tc;y&$o=TiiToaAcbdXg?W_A@Lu%`+1<->h16^m4uz5z|;g?P&eV)m>NPOC?qCD5^@EWO!#I?s;{7QJ#C?Xapk3oA`TjY2T@2-?OLU0oV2c^M3mRf)o7s@yhlK zIS=G-{EG7PHO8HI-3{R_SKw8Nw@0QwCDvceS$33hV zF_MyNg0+7m-Q)Zj#Eo2y%5_bfHkxzf74VtwO(AblK4tFE5f<=^D;sB5espdz-kLNB zn>qbtw|va{#PD!_hIvLr{p=>oh#J|Wzf~gq@iOvO>=7@CXT%)+hwD}2HB7;TOyi77 zjyK%^=8kermG2C`px!}sV?6H)^B@6G8wDX}HQW^{fORac}$|K1GleKWTg|I zDBvTvsI;m^^QwSVr0j|q+8&9>mWBfo{!q@nv;joO&z(F&*_fS|sbuPL4R^Ga`L&v z9BO^@u%Tyi9Ax2#^F^=)B-C^FfZsdg@ zKr6NTj8=7>$x)T9*SlDlUuf7gyBw)UrN%VBi)2cr?IK||+V-$%995Ta-C_F4`7nd* zkF=N2RrfDBKT3DDBNl4?P@HW4@-s!*KFGcbjb+)K$#bC~>V?nSR) zk1)qNGL~r2>XA#G9b5ZI!vP|ZdFGE-#lN6hif~iv;~N^~*UzPKPQ&{CH>QZHu5Y7< zKl>}EOxT%T$HNoBlbrRjuxe*|qwaF%qe@MQi74F0N>7fsk{wmLlxvVSdI5{Ek2 z(E`gP({WVn;c!AOTSKo%T!igBmz7=+WnOpzX(mFuBgaA5qI}d6DEZ3%T+5JR8#uN0 zrt09y59hwexi>-A#Zb@6yk6g#m7Q51-I<->uX6bmSJ@No?+@*BFQ+Eb%{3Aw2Qy#Q zGdwbI&Dal|G9y)-^-Zd8J9h_+r0nWm)d}a+hIus}rmBb~=Ukcn#6j#d_c-PV=u63# z%U)-{a;eR&U(PQF*#W?YKIJ!fsL!>Pvm{wT<}*AYRylZbQvTj5oq2aI;#{!$L7L;7 zvM$--^7CtETL0n0g4H+hrB(M=OboK}RR}N6Y_CIRm}wo%wd@h(_DUT}c8K&qu)0NH zugtI9L)>6~A!SeGMbS3rUSl2Sv%;R?Kl!n3_}N$ZC@D5v2ZPn(?+Q8}j_-z(XFeqm z2=W{*NyvBi%XE#NXSQ=l(j<8qR)XO=$7chFBK8N_*LV*0+c5}v5}lcNPe(?6NA0Ei zSqmN%JZu>XGMkbH0q%GW>N!T>xdbWXUd^{0biFSVp2&tmD8mrCu-3i9+kT)ywf*r( zy>NEq?fM086ML7+?9@7k*E&hFi|VB~AW??$+s8)VK0fO9sZA`q?1{EFhhNuKF`oPD zPpBNHfDf_Uok2SxpM`f*w2l>_`GANf5!fubiOwd^anVvMrMGxzydZmnaHD4dzePnJ z`+vEruik3B-D;Hb;ZntUX32wfCf~*IcMfzP`P=6QE#>2_caMpyBzwH~Oz2&!LHPsC za<=429qxMR^sLzfZ69pFZ!qlDh&}6)*;B7qN<9aFmu)V=`Lf?0$B|ma7QIM5DFM?)dup(S9 zYIyYpLB>LCt&k}WdsQ6fYxq^>2BV%FK+ELiryB>#nX;Zp-{^>RHC1z8ui`hK`fWBX zXV4a%R(7B;tLL2a3fVrHW}_>ces1vOM~rAIJ5-ogLzTVOs8O-}l&Zq@644|_O>P); zlqWBO)*s@l@X%H$n+NJ=zb}9XS!K6HcQFTS2)`~sat1BXUaPe?05}>38z>sN5;-*)3A5;mfHY^!pPASm;(`Cci2;p`-^h4TjcADOsiwU`}Z=oAG^U=W2&Y-WR0)y@=)ft(| zdbjb@jvIen`S|Hj#u^k1`YL9chX-~nnh(F;NAo~qlA!T-Kx1oR7xfvx4I3S3aWlEp z{3L_yS9or##6zZWr<LwhRxa!}o2?r%YMiQd%` zLIE$__RA>fdMkHlxs(b&X8OfPMFm{1pQpv-dK1_GhqG^ikE*!--%XYfWZ^CnH9SOA zKwb(_R6-I6d4WO{g#Zc>tcuTC47-5}!EAQ3aJ_Dg4{WQVw6+$Nwy`RpAOwP0d`Ccx zLLmxj)=-Q#U{c>o;^GDJ*6QIgcSW4dZLVX% zEd|E1EutxtabWs!X*hD(kC9u6-mEi>iSohY}(BDXhvTUscShf{^jWZzXU;#Gq@E{{kFyUL_ z2}wGJQ#(KwrN)Y!1Y9mSJi`2Fbgp~TzBqk(olP)Yf7By`KJiXS*(LS@jrUX69t=0? zZr#%?mtp1Xalv^Ua>1mHyrv$jvrbY;@aBYvos+A>Dg6{@OaRoFXlkzjBFxVX}G5+~mW2*Q7@xYA_Rb%0~FMl}OGAi=d6= zeWeCpyq|;{QR#e5JPihFqcu|TO-N4*UWCCQ`ZGL_ zn7_Z_=8Y)9@&*u4_2iI!HD)Deep)Wxb=-hAgJ3q$5qJH|fcs#CyX?Znr_1*XpUcc_ zg%`Zi5l(N`myLj13(gePIhb-3_4(b^z*w&3ORqHjXV^)%pgu#fYqtn#Qv_4wkMvPU%_dBEG#h`~p4w$_R zDL*2P>)bl#g3HA{s7cHnK7w%`4!b^+p<38=NLf3B76%W9V@odFyQlNepeC;9A-J+$ zhIvFa9>b-y@_Hm#Jl0q)zvhV@G>%Q5$C;yde5@ZRZwAR}xXuV{vY65F3421I+JyvC zNiF)wtTsJP+KMcLWVK$f$Aai%lNxb;N4TGpWKXRK|9q3We+5Hhj4C*-Vp!@vuS{A`v>$5AT9-opcuBU7 z$(jvFDY4w$SuUdn?LqTaS5r~LuD!UA+7X_zH&F(2adltR$(@b(0>X?*#J~a=^|3=3n?;M(DDBAit^%T2O|Xra;*SWI@*Mc)XtI zyC|>-&nibsm0W`O=5F{pV2m)Ww$AS4x*N8>L6d}aomz<`FPGnsu^yTpK5mk~mSIAl zgvWj@yKsXD)NJLhKbm5%i{h?dY|OH?YfI#AWGRFZmRw;m_H4pHXtYS!lw{*8W<2!P z^6-+W%ywa{c7fKyTd<=s=)2rQx7VC@h~j_Bd6s!nL8*o{y^=*LgwV>R-!PVm;+Ah) zfOQG6YTiDd9^_3fR*k;hYGI>dT19SV93w2YP;RPtN9R!_Em$N8E|*}qfny@9lGC1a zO#?}L@H+gJ0|MA!RI{ zPQmsacvD(7o|AULVuaKGi;m2EOs?@vh!=OarLpO2f&DV_rH#~^F|mMRh|ny%=-nRu z2jaKwgj&lFoqh|cE*ux#fFC)h!=4E*N>DGfC9lI9(!mlhR@MrWrDI#VR#wz3<+3%) z>gq7;QR{E>|AlS$gKV6|TGag19<7~IS_ER>7Clb}1F{d=F5mfAF;B_R>`aqRohLO| z&h|)bG4gia|G7rKW5f{X@=Bj8O_tr$Sp8_Am)K$jaB0^t4YsK zjLtfM;OrAk4Gg=!Z&gjDj$AF3IxZh+*NG7ao2b+DM1$hXJY_#h#R_n=fr_pZY0OGA z@9blRVg*S|b)ed|)TtbW%&(8Wgus?X&Pz%;YTzYEWFsmQ-7HF%+Z(lIIKtCg^v`sR z`P>a3tcH|KIZ$#UE^|vMkf-}Ka*6JN-95N@iEgiw?#5NB$;+^WnHS-%Isxn zd%uvG)hW+E4|cjR+51;4UN){o&;RCD;gkc&=o=WZW4+oM92od+u=Rc^oMpj`CLyJ& zup09@Tv$`(J#07%jZzoRy;Yy;eO^8RC>Ra3~=rbQ-HG20ma1>QPP$=)An)!caIE+fkek~d{*3Z13 z=>@wFcFvgR1{?eNT-zR&C82%MMTw1&@T`Qv#?#)XcfXb=k|cdVdcx|^PU7Et zww1ii?5hKyO%4djbhL)Y{ZJ?}ihv8EAl0+5oK=Ur6y;Jyk*OKnC~(V`Q+u8_JLcyz#(9wA3QSE?_rh7sFIEK8J_H5u@b^4+CPU>vOyqh*c?E_~S|S zcs;0Vvyg08jn$_R!#sEN2FO_Ye{=r>w>8PTay~bn7qD(4v-qZ1m$cO}Fn3m<%4NL} zYH{xSq|({`qNG;&;O3nqEVH6&uV}m(8`c;np?3R?UHTUOPWd-b!-{t>rj)^vBgXQWYl_@WZ>q(sDON?bmdARp8Y_oPMrDj-bDOl_yAWo9Of#hxQ-+GcZ921|>iw&uBoQPf=+Us1N|>n&)L%jF4_ z3hxtUqeOn~b^QO-E`{YK@(O%K$}Xst+I)k}H0=f2Kq~mb)n4wWR~{;}?(T<73kGr5m!{(tnQ#_|eo#5i#S?Ywu*b;`TIJ(6nt++yS> z^#@z*la@@yLcV%oQ)(S<|I&Wf6_Ws^ifEScCD>-uKt=^!3~svOaVJ@fVLqeWXY*GJ zZibg|WZo-3|0_waut6_8uy{h7#GHH0a-e2cYu;X?x=NCbohwv2xR(!UY_2Fe7|1<< zA)wPJIFMS<@W6*kp1uR<-%|H1N;h(wjDqCwB2P=|-o=+-5e`3*1HB0$YYMg86v{?3`YUT9;d%mRt?#%CVW0`_ix< z>VbaR-D$hEHq^w@|3Id=&KEJTMz`PaB_ome+=kSqd2xD8`O%1ZXY%6yW-SFybkcnX z=8w>SNZBT(-Q+Y1ldOyBc0TYQxuUHg39lO{oKqAvK|ZN_ytvdI z-8?R^>y8|qo1C9jn9>JJOXDQuo(B$|%ue(kh*bnLxFy3usX!;CQ*D3OG^FyZi~#v| z58iwIC=iWH&39`{R>D`dGa#V?cj>4uzVl1(cb?(vC3dL+Ld2BBq3Lqbkn}setr6o% zVPAr!aik7TT3w+}Yc{aW}h(lo3ici8xd_d%_?LKK71E;^%K$RxmO7u~IP zaU;AW7u7D%DE049n|l6%)J-Mh?9|xMDpb4ZY_|(Dom01q{jKey@BoIesfF$bE`tP& z{NCY3i7lzkCH+wiXeQ20jpn<7r~zT-`^9VzHO|z-W_w76;t%n;Yh0H)jQk|Mut8R5 zrNXg~w6t96-R=@_31p-(9^sArWNm}12RnG28nDf>Z(sRQG#c;FcwaIaB8O)+2@bSd z+|uVgTicKTt&mu%qe7RuH)UJu-lAl@V~`B{)?=v)m-#h;({S&&)V=ef@UC%n6U9}n zRkiP2w-dGx`j%R-U)pTf=zXFHK6k2bKdu&sPPKTq8u{+1ydqFhp>GanZY&5i_t3x8 zLJyBeH=Fe=kQNHS?T@`EN)nt;_iJ!hXu-P(-T)B2!B)ORD$7K=f#$Qd&;&xxs+;?0 zp>)DTP&33JYM~*Fz79%&b08$AoLZp<2+-VC5Kb z%Q3uA5lJDE=8&{Nk*1OK5J?kADk7;n%1>BGI7ACwOgNmdzZU94*aK^CzKJp8dT z3}x`$Jc-4aKxW8VsTOL=lQMsZjgLSxB7Y9(kKKUfLM`|)Nzaqi0p%xrmT&;d&yYGp zzC?FCPC?*(Vg@^o*-xPPaxGLrQU*yuk~Wis!utr<5Td5$FythLypg1pBy~dl6FyA{ zS*H^|si+q-;#@{NpQLh;oa2^SJ})IvvcrR?`9>X(GSCOk_E?IfH^i29BI z`eSmyMhmVZ>1vX|`6`m$BxwanStOy1j}WF3g7Y%M%N3l*h#xTG?R0-a_Y4N>r-Dr+ zJYS`mLD-WJ!nuUdqXM5t=q5z1c>w*f7?X9;9WNUo!R8z(`!P&e1I-8)CH#&Me11mw zA3})YUBaD&5XE}J4^-g46TYk9GlW|ee3)=EAp}xN_%}j`?mj?&>?X2w8%a-+gmO(K z{GEz8jv)gKnMTq=lF%}T65gjmo=f;^LNMQza4I2~Z^@Q|UqgtJHW5xBgu;9d=#Rb0 zTs|f#izIMfL(D~x8^sNjaT$ZWg%pl_c;zo$v|;uOb|x;3!7i!-$uV)K`(tBkV~CE*grb!YK1m1;?Z5H0p)M$GFf#r!MX*V6q*2CHN+l<`5r z=T#yL2_I1K9!3l@;%t(Bt4P<8^dL#Obcg9qWw3h~3`#$k@ODC!y%*suLX_Rjh<{~7 z2i_|l}H&w zIy4biF-a{=mPNRo@Hk;t=s)3q2$98AgkKRt?4tw7EIxRHM3`{s)3*Ab%8h5sF>QX>hNeHFR1N6tH zpD86CMbh&s)C2lsRm^1vNuyQB zw+KfPLPJ**4kR22`%icf`&(rG4}=YbSz1U3^v7m*7v@VydX^+;*}a6nC&c7t7GWtN zjP4}D*@RPI{|H-f<5|YtaY!sQoF0$irWV@w^9dgzMA~N(K0t{1XFTC-!mCmLsUnH9 z2w^6^Bs^2WorDfTH|n485GK1U-&(?YLYV0n2;U`yc0Ng1rKkqs3PL2ci11NDwU45>dQF(y{## z@)9ldG)b3}R6%zd-9ZMcW-xH>BYcw(3CKFH2p)Qc9Dq+ z9Hm0;A{BC!GtKtn}mG{(VG83*p(1TJxh27Au_E1^vAmPm4XE6zK`w&bUy&M z^iy{+(%iiwDrm+G664O7NY~Qck?w2;JGw`LK_^C#bQVbiN&1>3q|k$;ek66FdjQ=| zx(C8d`5hV|V%W)8DCj=Ij|frF9fa>H>RW*RSSr(boun;_^djLpLgfAwLtev>50kWl zBv_DA!V*F#{e6V*?3NPcXrW&*;<5{+;8RJub(fIx(f^aQYJiZ^NOIIc5nHH;gH4Ye z5*7`x7h&yo3%Utc5Te3!nuUV zB8Tu^LR8%4gm)35H4Gq}O^BJ&IfNcUj3W{W9fatQen=4s)W0wJBJMYYI|(sP*hRR4 zuov1tAO^ljK?_!qRH`DrOjt}fKnwkuFq05V3y%>FCxluC2_1yRu>XYR?+FjTg8e7# zO9)#w%?u2f@mde$9Lh62{soV9*NMmFrpIiFpM>%Q`eSFU5H5R?^vx`UEp|!RPG;EY z5*Ct-LHOaaAEyaKjW-hhKnTI^COkq2#eSdg--L*}0ni`2Xr<)yFOsTA!WiH=LdO(j z``i%8_EFQLFFab3#lvrUbcDynPVtyakJ=8x$tW#!Ghqecom%L6GdLmIR5sy%ToM?1 zJA!bpLm+CcKjB6N&nA455LGMh#ItZZ;0m8#ACrpODgmr{xV*W$8g%E5^CEP@a;r2Mf zO2RIf{}7fE-mirQ0s3POrxT?+m+m;ayD``gCnQIdG>-6XLfC=Bmxvr3KMEBk-A`D5 zTp-loW5QZO45exS{jss}lEoV&Z6FD~^-CmOP13U@m6C+fS_MhZk`yFqHc1yk{|QH` zMCLGLEkoW&(lC-x(Gv(Agx6yIpRn?n@QI$`V#3*ksOUasV4U8}xHn!@tc&TfTY{sV zoq+KUmsRYtO8G6JV>p8Umwm||(_@&>(WE~l+=+WAsi0MaPZ6S$UM75k5cU0M!p8`q zD~}PDsK7x&FCj$ZBfOpv3oUa9R}7VmFr%18=pe+zXFTDHLxh^Ag;EKBMF=(q1Nvis z-XV3)lVXhk}Qq+KL+CaE7u(DUQ4O_;lr)C709%%x&|PSRPQNP-`e z^vyuY3$bcQx}2mpNP34Pu<;T}*OK%sNtG&A1xddlDM(UHxX3>nftXb6W7@IPr%`_k=!c;V~-LJC&b2T zfbc9r)W4U|Lx?tX8(|khF@uDi3D1BTB+P6Un~%mmlJE+`zA%G?ZbHQE35d9WMKvoeXl=Nw|{`gU1L%UdfQ_NZP8pqg5nj zkhFsC(R4pf_ZYaX{d8;tGiDWIg3ls`$YSU@B*}N+?5U@bluObClHMQ*oTU>wa2lNR ztBVO^cqfVx`!Hf+qG&myUjBorfoq-2s}7fFG?CJB1kNcf&g zWH;ek3cgQRMF^eVK)6=He-c&_qGw$}_&30e=cWCG9u|+RS}N_ABwoYQ4XFK{fd1I) zQrzG>B+0#A47rY^xztfJhle;vMBifOP)Z18c!UubaJGb1v@*gb4m?1eM|dd>MG6)g z0RyoiT5vKQ7i+<*86d>ez)~7vv4TSZ{joDI5Dxp&J%|dKME9>w*!y|$^tc5crd>FU z>6$;*8K%a>`-r>Pcsp<)cA?!a))Du!@k_-0ZTu8)xa_7D5f7^QAwYkul9DT>`(?Uu zh6Mh1CD?r30uO9=s6Fy2qtb;tnVW0tHF#39=Sk z0CymUZv$_qqYKwHW=Qy~{g~X4zt99mD7X1fBOYY)H-vFJ*?QKUSiR?@1>=d&v+0Mi zLJ^3$ZM=)PuZ`aY_Qy)kR`OU!+!a$GQ(i9tW8hscRe(v0Bfxa>yS18D6;l2?MP1vBY>f$kA>zXZ1>#|!DP%1%exsXz7)8^;s>)5eF< zrv0&3ZM=_I8naoS+lklMxSDvajb9;NXXEF9!(}<-8-3E_ByR*67uM7L3Ehk6{vK|N z@4FdqoH}$ilTbbsqdCkc97PDDKbr6oLd+P45%wZP57n2j8=)+e6F$U4WRPSWpg*=a zUN}11SD2M+B&Zi(1Nvi2NZLjBQMx~*`@s$prJ7L=DHHQ5Nl};!T)b#T=_u}}8LShB zYY;=2unUh&pod$`kTF{3B9gSj77w?OG)j|1rqP{7_XN7r;Wq0fjX1-`Lx96&bGe%Y z3H2uVDKt|p=%M>*x?K$BQF|i)Jx^je2*KA^fc{v5tY-v2Bk2cjbD%?dm!wQtuL*7> zX}#KOs3g2V!9Nl9B|Hx!WJY`wf3;wUq_H#(kji|*%M`qW@InP;i9B4^UBRmf;|WoX zX@pJepuqW1!fyycJr~d)OGaU}U^lvZ(A|OVo+1QMaZnHUkzBr15%&Z7W9Kq(9o>EC zevj_+;I{O29Xzni#&{Srts;Dv5Yk%C;HixI2;HOTE@!Z}RIp;gO)B%-2{#f#JkuF5 zmk}q@okw>jgT2mRFtI5lT~AVfx+l}!gYGGCi+$_NNG~xG^y4@-Mf|Zv4A?|>3ElhY zUJSQY&h7Mg2p*>1RTER6d7kwZ;@{cy=YXM6sz{F${>Vjf$fcaHS*?*5GjbAIBsL02 z`i3MFeg@$egs9(%4B4L{GwHsF?h$kkf?L@?#;Z{|BoS5-Vp!9G!LMf2A9{-jCeZyK zxWi?ys#tpn|4xXR(T9X{RU%b{cPscZ;T#qB&xE%UqQs8@Vtm0|mXI_{gsgR`SNxF`t8%V+eL>5W^An6K{t|JNa_=^ZHRf!}M4p*=fBW`2FAA1Q`V%sr8 zqli}1j|G1Y8zAadwcdN|+_Xht*mD`7J?Jy7^32#*kA z=)Q!oo)C?60pT7(6zonwe{2x?X)Rbt_h7oOW3Z1I4C2hoo6DATu%%ce% zg!iKVW5nNJ$gTx1B&p(4NfXPWNrbZru_)Goa55nZdKBh7T-H)2fl*yw6UG#*BRoQg zrLMOLKOww83%vp8kL6=pqy_&$(#tC1vm{L==`oU4l7vbQ5oKmzc9n z=D&{EYtvsM_SyI;;BeW4EGQbzLnJ>AvKA~Q=?;>hsrM0HONhbDuLv^X%m^2dH><3{3#ZM=)P!p83sKVsu`#E;teCE~|y zEJH7RePLsRc$tkC59NaBCmuLl){p66bavz{Ve>fSHqb4Lhgxtq+~Kk~1k1o~3+&dr^l*GE`C~z8 zJz+h!rJ;U*C+x+oW!RNx2s;vj`Y@nB_9K?6wP1jxIFir@cnKZc@ddn%A>%HQkT=jB zPj^1uF1VH6rqQQ^_)ss00Ef$B9|?aLKJ+HJ6C-=*K7(!--JRjK?7vJ z3e(*VjQKg!g^k`q@;OXvE#1B8UPbr0t?53+c%_Vo9lVDKZzG(JgR+DUz>JUCZ0@0l zr&id9oh~G7QTxi*0OCXrX3MyEknTjfub{gV-1a>3LVBGcUgW-@J)UCO+53$IcW!zfgQ00QATDf~*DW=^jq^4hDNi z1$&F643b_aX|p1|NO+rqPch^QhJ2XrV{``?EK_aXdkOb(<6QKgAtzl*{U_<4Bw?5I zYQi8P6g-XaT16d7ID!x@?p#8hJMMs82`?Z-E-l@puq~HKE{EX`mt|t_JNJE zX9&Ck8>NJf8w6s%yoNA3T_BQOLs(CUWLFZ_amyUkKN3Eq;Ddnv*ytZ6)5Ua;p}UCg zAK~Wu)-8-B(H~tI;lz>2~^zw9~C7PPOqX#G`Ed9C4bBA0tk;aez3(#`B0r z+xQmZF*cq|oN421;;}Zqj5y227ZPXNxI1xI}$?Lmk_!X^*lg-%*kUS-AStAxey$jbP*baNcO*O$z=&4mI}WD^v51##LwtH zuaj)geZXK2o@W?~gEA!LZWGcgCh20?t(%I2qJV)I1`3aff7YQuGluj_==O6jb0Ndu z-y)iaalt)+{@CAXYG%`|bre}mVX*&l83hH*C0s!Wwyz+3nh?XSiwK`kFqv?8oMeF^ zUME0UdsFYRXH?IjGen3!eOT2yP>b&~~uG~3L-EO^|6&sp%82_Ls$ z%!K6@{K1697Hl@*?H2srgwri})Pz@A@V_P;Wx*pR9Ad%4ChTp&Lx7N57eHu&qbo$} zBMs^5$WLUr>oP`6Tgr$TJ;D9Kh=LC)uXlOB7sG1&e4v7tm@u~%zO!!v| zerv)f2pwF3{*4xTfUxA4R0wKsF5v<~By}_4Z(>5ljQx5*e{3TcoAXE-LlQz>NtmwS zK!%JkWN(t>>RD=87sA&FvG8)DlcYJ9tJH_!4wnrk2_5V{lJ<}!pXi3m&R2F$8P;j%F**3m@C>wS{GfjeB*N2Rcf zutj~``W{23LyR~WNzz6|dYSNb1^-O=8X+|9F~SN(4H7<}pbyX=ThCKa>23w%r;j;I> zlhmN{O@yx#Ld!k}^v7Oi$d5^yND>SyuAM-{Tte`_hH#{!t|V+wA9?)|5MxS=GH~?; z-B;4>r#lsHSzn#UNDng-x`tZ`O9`O_1%&sjB=ZR85`xn!32!I7PzwzN^v7zLi99G9 zF5CXC6cK{&Leixe>%s1k^bScV#39%VoOLJZE4agDD;4Pzl4fD1go~I+I;g&@Sx@+Z zf`2Fc2O-jYhVXX^K1_HOA>>^O2tKg@qXoSrT}cv5!)+w}n8UW)ZTE!csih$6|G6XCL``q1TTLI)v)RtJcW z3$aqH1>Ym-o&6$D)XSSBrIYkB-Ledg4?`Jj8H2&JJwcesuS!t5C4_N=kkSIe;q0cd zt8gdbg{n`#5zrs|ysspC4M}}SLV3p!_98^G!wGkCrU@lIpYV1<pUKNh!A(n+8@ zf$mtm_;-ez^Q=a|aM>=-&Jb%iseO(~tXh&B96w-*ZxdlXht!bLzX)4+90hc-@Aqg}KtaH&V%?0IVux}U)b-szDUL?In z(tgzyK2P|Cg1=|Tz6@!Qw3j4|?-mihrPBO0LuNAM%_MCk2|IGvlQfGYxd7E4`!(I8 z80=A%*AT*o34eukE{5E}kRH0lglR!s(}WcM4Yy_24`UTCT-Hk^v!C!x1#v+yvrNyxnqVWmr2>CIY5GpU3~)humk>KJuMmzTgem?Dp@$Ibb;|&;jLHQKouoK3o#Mqf zkj_$cx8kTu=u8MSv={^;>rHl#eH#UB}}u!I||3G1fW}TcrW(H z1`EI#@fX7i{=%}z?nkeXkw1tUP^M}2Yn2|Ryu8!o3xIg0xN<;Fu?>Q_Jid0aV}33>uMrxd*(1PAfg!4)>K&05(mz6x2s?^p*Fa;g)c@ z@Vx?0hm`MwLqB1zXGC{O0Nk@?*5`N><)edM+;%7TyW!g3aPP4x@{SO0pwrgGYip9O zouj+1#VuoV^zMPixYX~8zO*i|Lsf|&qmz)}oQjoaYULy0iuT7J>n>XP!F_sDabc$i zm#g6}xI|H6xs>yR*=Y`Siy$;EzjSfOVcxry4}Jp;2|B`u^E%?kycxRjBJfq?=&LR! z3pB_r?TO)Rxkla<&UPw-WRQ+$7gcg{Bc-n(#4%d`5smwn4#o^3=>;3 zT+RD#C+pR?aTKj47neAu5A#k;pXnW6y3jdF+N*c8V;oVA&nu{bqZG4IS2IT2KFc1bwUfGn}bDQzz zCbacDXgw}%<;}6y#cjNGwD=q|-Je`gsxn|Gy=ML%y{<-&53go>|{{Kdo=O2J{+VO|&yggKD~j za`T-PDVKuW>_~TP6um8780YZ1<&h2Xh6nDmi}Pls;2z5<@y_sgXK8Ghw|8mm5?}w) zMe%XN!sFw7y}~*1@vsMYJ_B*WW1YCT(Nnspv-1+~+hG@8yQ8T17v`%xBJE*4{)Cr{ zKnxfHSGc0(#EBE%*Pg%G|3xYOd86NNE-i&UO4#&`2ZjRg2najd0=wcFHhrAE8G0C!*rRi7B^tozWR}O=DQa72V z))ajmT@6O9`<-!rGL$P3nq|Q9`I@0$6SURw(EaJq{VAgRTJTXT`}Vr8*Wk4rlk#&d zU;u8(Mfc)swyw%${YXLDrX<&4dao{Bm@q7yo#5?(JD%lkT@{DdJu_bEg1Aw>&%@ar z8+ZRqI*qHPou1sTV08YBdwv^Vu;lVnI_wz~2yejE>CJO08V+N1sJyDecMk5X1|K@E zH&&PZk{c+yR(=6uG_Eu?H@p#Ia1`Jv$1m+0i@m=##dtHsSSVtw@J>Cs^BE<=QO%>`%+y$U2wz8(Q%`Js0t*MY%QYfl9gRydVzJ&0L^-_|(#T37oV9I=u^+{7B=*=A*c{*Z zg&kROYspF77fX5OyN%&^tqV78UIG)NtvPGNvC;VHa0PzkLR(+c9KGP+ruqcrszY8u z8Kr7#@(=2Va1Zp(aQYdzS2ZC}Rhm9VTe2B6X```0hfv*N_w~-Vx7T;7mU>wOQh4H{ z3FKQ_Q@#C*)Y_7_B%wKa7s(TMZBo8#A%_;lG24ffZ}sNjwmCW;vyvu^ETrUtU%Q=i zBDn`K9J&>JYOBuD2xI*??0yyQx0hS<+qGYYFElcHW27hd-!nfnoU8HHnm69a z>U&c7lj;3VqMPXjs%BTSK1_a){`8aZ%vOxtvJxX%GSENOeeu$a@lp*e#Mbl#>bTJs53^!a>yY*J;6lt>Wa%&XXAEo%U%d=0 zpMoMFb=<~^7XzITP-?9fxEd211FwoEt1f*`#D&X{UsewlxZou{JP&BK< z)+3UNC+r$638Z3>$custb{lqwhdl69h*QTUwTZ~c=q4feo+_(RCue29#MwTZI7B|Z zY*wP^KPgdP$tDm7e)bz<<&txCp?mHDERUfXShI`hCe+%Tic{v)WEUvTEYR7Q+_lRI zDi?{*xS%E*mpns&JuyPidm8yo#(gd|c1880U~$f4snuFwIckI#r~ka{tgMjYDYCkt zua#9GE)He^rNhkVK=FNOt}_0^vnmFjYq1Ke!^+sTjCwJ;NiEgHiyD;4=nG3gqAXz0 zd`}6=WDnYmg55eg4Y$#)2G`Y#!Ag#PB4edj2Z)|CC;Pf)nG6<*Y^+RdW&8K3ss{G2 zwQAa=fyWU>;#E1GBQWb@zubD9Wd{-VCrT;h?GjADQnoc5IF5dA&-_}WuwGu^?0|}G z%S4>0;lPubV6CMOQ?&-QkNah{&q6Av)RLd5{iKuglNNTmYM+f7IlXM6Co7|5qhzZt z8#k*MJvp_bU4z{%jn%5(=p*Qtr9bE(8gW|cFxM58r-&?lINf|UpuQxZww079l@$1? zzDMHEpOKvt$##vMGImBycA{!xiPFUO|6CJ``{gDUFRi)gqSNHLU4w^+3kxkF$J;{2 zd|b?ot8`=+Ehx)k7%o`0lTD81Q-?W0wu4W<)tyQYoGB_-YrJ4KYiV&%_Wz&M+m(w= zV%X?5*2TK5-m2fy1XhhBt~VC7v(u!xiw-M^nb|56;HGAp^E-JD36FkZ{A#{uk}FS> zqCJN5k3@2hOIH@jZ8m?7$WMdZC2r&%dD|Y3;XNh134?7l^(Kt>%#>{rdBX)GuFbAM zEV<~rinpy%sf^ELJ?%K6+yhgpMjh4O&ad}E=Y-)W-jJbhBp$7(MbFLwH#(lR#mgkw zSm^WU*~9S|gq}SDD`I#FWtKUSRO4yRR`E!KTNVIRN1yF7;v<=pkUmzfT$p;ce-DRg zb0x|s!JAG;WMF^+U1po7UC{S>jZ6ZS`317>3OQw>Jo3YVZX{C{!huXV zv^aZI!2;>=7%8-63$N{9KAz_>R|hZ%#^XEYV^G#OqRpy7HRjarW?Y0RbL;QXiQ?Cl zgT*#*Gz~wL+E3Td)qFY8n45@2iSF&ng?mkf%l1Wzcu%89%uOE$IVWQ*JPT{a(yow_ z6u-4vyR;Jy9$GM3pUH$YfO8JHWS-bsS2XM6>|t7}FM0k-D>sj5feJa!g-If2Faauqvt`zeEl0oK_%Lz-wIT>*-A%*kJfTHiU7<$?eDOHQP^P`}T92YCqD(RG0(p!(`%x9O@xEko6Zq z1~UJgs@I4`YaC(fMUE=QX{v%9p3D3jPpU5?G!s@Ol4qKhJQ@=_gV1cDF@*4<{3G-i zpGfFdMikc_maF|ff;nGK+2ublvoESt8cMP((L93CHRT8PVlB6({Gcr9V!c6SpfB42 zf^<^lwc^Nf#g~mo1VztiIZuXoIPEQ-N@FE>UAy2i=BAg5*rp*HZ%SBw`Frv=hL_|e zW-hidjND}H@pAbvmdhR~M-vIcWL7VK6@=)+D7)Fq(Gb{-%c}NoFfB-XQ-W#1ofr#; z-;`1!oLE(PhfI{($g<<$tum*h#?{fGR-0v|n!H(d$XS6XSWm{Z~Hiq?wpN8?wfyo1}!U)*0WlVOaAS7$ndRlakradPx-@h+Bk(NV4- zQoRu#+J-8bfT8o4r`jRR$u7gJby#=2*NEDY7Xz)HMJpd5(>o7YY-~aclvbG)RXois(gq|(MO0NB zy-dd7uxDn8<>67b3~-==n5`?i5CQRy@0-$Sqy0GsM}LJ{YK_ref6aJ}A@V7ei%YR& z8-^lK`m-MVn0eFYTfHq_4TYiaA&-ZWFA<1h2Nl>6ySNK6p-n+ewrw%(8>L}d6PgmS zzR1aen#MCx+_P)b6#ZMoNLjDnS?&85<&i*n{9^f|eOgW~b@7EJ+bKue_H)q}FcVnk z03*@!;rhi2X?z0~OEshLi1@~i(ofj>x!@=Ix!9Sqt5uKDPB!H95glFJKBRYlP0o2O zw*J5><@S|U`Uq;zDCirBlhJN8Aa!Y3U`xe%m@K1I(&t(=8NC-}`9&c$Zeb?gPRs!5 z+k4b{byOarmHl#bhAtU=*EQ_L+|0lHsvT)Hj?|Eph#{l8O+@p&fTHsRyFn# zxtGQUc&|k|OOu8hCAG47fbGx{ERSu8FRay8cNhI%fY{-@q}1H^=GXeo#EgRX5J~Kh zs!2U}D}p0A-^)nI$lYQT)FQ3YMIPsc-c;#JFnHhy{ZXkms|ut8`dms61K^rWrz2dr zb}qnP2?qXJa2346xd+6$T6sJq?5eH{PxO`4O3pIlnS*&z7}J;BhA@^^_evZH#c9uH zoHfyxMF*rDQUV!pNZdcP6M%0fBGtHtr>nxdq6-nrvfHL$9MNv#g>=ka<>*+5hv|aX zFhqpZCFOESS!wXK5~gjw%iHIBmYMeG)_miUiA2CkIvM@*$q~rtMJhrTt~UI9x{dz;Q|_CsVR;fdK4H?D*509p+-=ThVZ8#|k9bWO5<9N%^bR*l zDY7cxrAD?#Y#12{yW(_{)!O)MPg*us4?NnEzmvUYyhkjr+|H>m9+T2wYM2+!YDvrX z_(q3aXIt@_?0CiR36Fy<9MoeH;f$MGIyxMBt+v$7d zM5%zITJT<&U88i8AarAL-qV2^bw-1G< z)_QvZrqJw7+k}x0wN03+wqZ`c{-^1mm{Vpq6!daKR*#0E+58J%A^eZQg4KaW62Jpi zU$Cg*>)JTELUpWd))ZjV2DO3h)-JIA;CnBUM|m~!efQRN4QrB(WrefNH55&CgI0uq zbld@zE9Ns z8JWi$*Wg8Wi4(~cmBv%3ID~=2miWLN2q#)nT8!UuNx(d8+L#FYhVo%4$CWCZu20BX zf>F3Toa1c4_TZ*{@w)3BgR2F`RF{!6(p8fahnc?VV}{NdDaR_ojGogI*-P?fShp3H z>l09%0pP*NNwx<_sD=S%_(;x34_dP${u$<5_zZ!(^c zG@-gvQg-2CagSNOm_~x_)a~;|W1HWU(s)^${MuRoHuFaiBd5-Vz)H2u)Ka$>ZMEhz zQ$4MVHZ{=k|?;lF;s(%S(;F7rVh#CG28j2-S}7G>laN!`~3*E5C4IoD4~&QPabg25i^vJVRB&9%MZZq?Qi;t7|iF|lOiOD zM{96nswbsIU$!4Hbi8?-)G=+Ud*1aZvbMUn$6%_`-#mv%+UjbQ1Zz@~m8u~jhCnPm zhKn=bf+LXUaUxN@)=bn)vFIPL8&ixYI1<6&O}c9132SVj$=IS?IKY+TC>BOsF4;xN z;~;@`8FqElS-nu7JWm=Hi1RR7S%@_b{}z#idEgrL5|*N)SA4w6*QxYae|)|s9py6m zZu5@Bib^@|7REA^_>7$H)`BU3n3))4Ad0Ksx&D&Z+Znx7`UtasDm`|Aw?pZ%i+meu zf*D5;RaWw47{E?IMBV^TOYrBLYx)NLBAHah_S+sfLMy@5GQl!NZaoY`y!1x5o|Pzj zAJ@3UQ#v|R-_fUfw7?jY8H%qNO7Zb|77HY4peCh)@=j6<@CmErQMDXBTM9|7TKY6g z)293~1cw}{5Rts}1Vjzzoa2PN+eo?swRxHG3~Lj|CO9;q{A76fvlTXfmhn4Ad0Q)& z9^9;!NR}xeFphGFT;+#yH3R5buIP4n*=akQf2s?iBj()09) zEOKoxM+I?dfqb)Uu7Gk za4FVTQjP|;%+#Aq<4C!*^eZn#5i2C@H6A^Gm7H?Va~Fq<#V)<1NRFOW zc#<>iIyVkMLEDYODt(h)TbkZ^jBihAdi-c#9nukduQx$OApu9jQ=&bKJ$lKi=rGg< zSJGRVsMvsN!iiPIRD~?~+#yMWpJ!w+gKu!|HWs_}lJ(++zr~)^6MB>GTQx5Yl9J8Y zT%6@h8XnI2KK*)pCMJ<7I57`?NkXQ3Mqwr9v&K_qlHONRbJyeBLws{3@_dq^Og+i3 z4E#0|#~vSmp_5&O{V=aaNu@F0CABF_{NbEF?K-(TIta>T@a6wdfHG9=WOaG=%47{@LRqs3|**$1DJ0&f7o`ujtl9#bT@(sI6!>)2U-3^BT^WC#!+p74F?4}G*HMI9Xu z4(7Li@%`p*j#7uO_xEC(vb6FsAO*gF5U+s)DFrfnJEcwvpy5tYqjrfGJ;%zB>j038 zwtAk)QZNBuyvcN|pelN=ux{3mM73(&DDvplQY#4%i}SFW+QBB!Uu6j!(legmEAyOWhSzMzSbqXi)T0 zaHRAK^(V#+DQSV8@Heh@>G?PX?;Pz*ioTD+KyOQcJC4?Z>sU5XoHQJ}9qTFjva!-k zr@`@GM1`!1J+KDRk8o0FP6gyGiVt}^q6RvL(vq)HnJ>Zx@dvV@hlQ)6Pf4#~j^of_ zYr#)M#1PaB9A*&{8r@)@`jPQ}omN&YGSF^eKu8O(=g`zZQD%Bjh4Xv>PK} zALlVi0{doA``pS%CXHd+HXt(TBVkNFa2g$^coj^s;u8^JOVM8PJW;0z&(!}yRP%eV z63d{7T2Ma64dVMwqu^Dki>^`^Xd#n4R*rhXDr39|7p1ZeC|m3NjgzP!>0P`xr}y<1 zrk`P^k9W6V@X{gpmr%dy85rox?=alq0Dn0mZ1I6lGGN(qE_0&FaoxfXl5tgdvNIlE zm#of-#~voGSA}nSC*M<;fQnteQ#8g`sc*-)wQ+-)j{cGHs5R>EjFC$!_GL>%q9{za zZ;+-)7K&vLBIB(AOsT17#1l&3FA`A~YzUZgojYe?2 zm>m5(Z3EWH);3;e$%WgpTCkrm2RnIPCd6Mp2r_Biiyf*aj#5~D0iP|6en>7>3q3Tm z!Am82;ab_JkT#QB6+g~Cs)9*FweR;(E>R6BlQDJ*R&C-c`-&7`DSWcP>^iyok%RhR z8@WVyr~umUKm!%UyLyT&2d@1dNtqo4#tD(JQy2nT{;WwbN>-uEb7F$8m47TdP`Rt5 zwMhrrBn`tS%2T?ij}uC9mKb!jlnIh@`QH#4pUr9I&q%a|ndL27`D1XZB|CEg4<8R; zsfderdFJ9>p4(w8Wwo%p&jokW(ti@Db=cidcr zBWE|?;=OGSmK=&4#xxHaOVp5gCU}ALre>P0PukfG$>|&9FV{4fh7CT_x}!J9+Kg~1+|nDy8-)k->_p=}w=ouG zZaoSGu1X&m?+`m1EI>JV-jFVoLE$uy1CvemqCkpf%J<)urNJw(58@H1bCt!&i zrW8`c7GMllfk< zgKLMrM=#uhPe|j7T!)|cT_+8)Ohzuo0+%t~V@!ABRGwZ^XOwI)P+`0Ekw(b@W135! zXQpw3}?sbWwH<&D5-@G%k>*@VSFZ^kKlW9G&gV%&KVV_`(-`P@HOZKP1u!_2Hjo4 zpha+{PtAC-&iaW=daq&Ma+Ak+g6ph#l&D?@Nyg{(OU;|EEm0(*;69p+&4_h#fkup^haVq(OMp0j@3X?Qx91+wH~RrsVRHg zOTyGzVOIT27}zk~FOvui*f-UiU(@(GaZpPOn9l^*E&>v4Q$0VDHUhF@tXATT$2iHL zpVT1dQ_;ImQmEza73vIIp`e1YD;GZmTiCE_5$RE|JL;?A{bG6H~->3K6jZk=zw3zL@Jo~UwfjU(Fy6EDQ!D?w@JTg6ZHbyh@0Z@$ZsF5pCkBJVI#_IDT0dh}CTw#! z$J2rhhjBCUJe=q;;+INw$Uj64+@I;h_j?hJtHlq;o$KS|#B)}9grjcp6#MW4&LtVM zWMmpmffX0+>1}CGw7vG&YZYq`ND*WS+H7Lzso`c3(#6I~5!OhLfEwkTOt^tr_Fe%| zS`&AvfyKzWZGQdHHrr@)roA?jWj2~Dv(e<&N5-0sW~|w0#+r@hMi$~}SXgMng}4$5 z_0)_mTBOp@9WRPU_hAUO_DFmA`_42O$3%sUpFp3p_Q&>Nyt~&f#?gW{wWeZWFu_<` z5vw$947@ojk?IvGyli%04S(GWX+Rb4!w|&RL#!{_i#Z*@z-zB4eMo|$v5(7%jLSrH zyP08(h7(=pw3Ndx3?L&iYn>u7@-d@@ZMx278mgr;v0*yaCB39-$s%%d_K0zjq+6|T zj^s_JQTyC=Q%n0q_csj@|F>%7`)*ZF1oCvujqpf0tU zbsmAXE`s{C6Dbf%32hyw-!Ix4Ki9<)LU=e%dvcxEouba4ii)1Dt%*y+kS^L0m21wo za^I7-J_Eu)vXM+#cS0juZ!D1G)zl;&bID!dy-Qf~tw1Alp1@&XTSYJW`Ns7IvT592J+c>Fm+a>|h0Pf71aK4+5-_k1I?waUh(2c6e56DgKve>Ko8nUI!i$|&>%Q>kMW+RCK{=#_ zL*1L=k+x%i5WGWOd4k77a6rt8>LXqF>#_^JhG(5!W%<_0H1V%Z!cbdEmO zp|@>OX*RxNt*c4@$oqe$;+U8JKyP%kya+-om%BKkS3~XD9>V-+qfLhwPIt(W2dk^$ zS)o%ft_6D{1&jH*hI=v10P~Sn=4A#YIT@4trA_WzoFD`H?7qf?kvQwn-N^3C#vmro zJT~|d&iopaCzWsW%?U4)6X~!!=0_INXzYIPfsQ=tD{MZM?)n0~t{E*eR6=87;t3e}wlK$qk81J+|y;mSWxbToZNxn0Y)FoN5 zPu3_Zu_pI>Y$F_{pR#69$MRbdA8{j@WCX5;KR~;q`S3>h)p)%3aSjvliJ(!4wcmyj zbuBGS8M}Je$h!x*5y}{*$=g|vSYPv6-|}K`7C7eAi$+9nizSpLD<_gQaQ16wqT?G{ zx<-D6m%e(YLqA)`+6%s|$xG@{^?hxB2i`z22T;Vq-oK=O5$LFR2`B2>a6q_8{`lR^5eOPGG$u#sy5wS5iInBoW|JZx`_^7IDZ# zFhKM~i4ZjfR8&yfG7hywu;y(5k+i8W646}k8}+u@>xG$sUL)YlK#qrlX-g`#;HCZ9 zTUu$QeE_iqn+Yi)kwQd@8D3(f)Mh$}5xj`hE6?}4_Bm%}5~4m&d!PIJqkKN(oY`mZ zwb$ETd+oQiTM&x_|MUg9wjF@<%G|*jX+N9voZwB+UTkBdYGb^{K3O3TOswBFMnFCN zAS>1rGqAPrrpY^({6jDPiT>YB@xVm2$Igy=w-(1Jb^j)9C3hkj75O-Yt%tf>{9B22 zERl_s0nF6IUeDG8$MuT!v{%9)D4`a)9h9QLxF#Lmwa>LPy6@33;j;s^PBrPw{jH)^0i0>*+K0Q=KP2Y6}4dK#&Io0uVRrzdm_OMi2DOKzR1;r z#g~;|L!TF}Z3J3>z&Gzch0}1ssd4e!a2qhjbWQUV`BBjiAQG?%5W-0prd_nUi70O7 zUWRIfaW{QHwylEVQBI0)fPf{0_Y80_U-*V&*qG2z+J%pbb#g3?tixIO+Kt!OLdP17 zi=*ev+>vlE9xV)?1B#rYgd7Uy1F{1$kPqw@3W!_9Yl8`G>VrtJs!tk2P+ zCpiwE>MrtkFT4_G#=$Sdiq{g8QjO4_q`@25(_jLDn$%wZ3 zZx+>V)BlPY85Rk211Z-*_!C=P+-e(hO~_`p>TQePOjj)ZC(K>zNwl(h@+0ZO3d5oT zZA-TPGV~g}imt;qmHrTBV?AK1zBjR&1>oQ88hnQ6@8!>F?xNVjp=OD@s09(*k%FZ^ zAbuj-%->AJv0j59F&wM}N85Vue$wmyHc>k{{}i@b7gcxR+Cl=$ztqef)d&Bs#cw7S z5%Msq4H`9U0Z%8`eTkvw!EI4R&ly7>y$XU^c!!EFsM^^@c00>od_uK;EVHq-9g@~I zf)ya47Om|vM`;i?+G=Ns)9`kza**Gy^*Ny*l4^f9mJCAcDJ=w!_=K_pkkhxlBBaTI z4fEeJs@E4)=LesFh+J{k!QS%82YnBsMJax3b-Xn@C5zCK9P++!sO@iz<#S zemn6tNaGBY#%B-J2>jKeh2$+X3EE#Jrl8TgkGfwdy02GvU@;H;r3%r`y1(ahKdHOF zry4-cHF@i#`Z^oOV_%raYdwk4Uyt$F%4(@=nuW zHM1sdSv}J}0G21R5$nt??To$E{@|5L|9YT*KKFNZyOq6ZRPQf(4c9%RAQ~CYIk@6| z?8Iy6f5c)XZbLRSHtZp)&9^jw4NjTH%*JL64cBfQxDfl~A!;-+WuZ4h%`l&iaF}<( zN3g9Q_|9gtpUr0H!a?MDtY#<7D(uBuuov5vy=cdDrPzy6>HPk-CGWHIA@BC0>W#s* z;I%7G{pn@zHp1T7wI`RL)^;-TUua-TS+htRY^-LJuRW$}&sDWo?82O_w-u`PiF(7m z(&v6$cdw-S?p=s_cTKK7rbQ&K6iuy#ALL1U+vJ)+qgzyc4085h@f!^cXX0T{!|%bO zeup|PUQ*&oydscZ33_ISzaP@|Xqn8-Cv(w@;{;Y(8%9`6w%-)D(`yeMPH1v5I&EJUM<$H+mAkMlwV;uRhUjWk{XB{D*(^5C7;N{?R}Dqks5E|L~9g;UE3O zKl+D%ls1Ei`7y3%z<0*{Q$;=}yDG2ymeSwKbKab`Y=!g%AA67i^@dO}#sgy%BbzV~5B9qx9bD9n4EV^h5Fk-t@lkFrUg1aCI)&a-~s%%`CyoG5=Ej z?Mlpy;5WyG?F--H-7AET$(PLdmnnPz|GvKPQ{S_V?CKqMJa|Q)=fm;?-h&_UJ@^sd z@BfJJ?vMDM+xNQ&Rw=36AmcMiCt>!az$l%FM?SU;y%momArjS zYg#F@+K#&?nUqVHoUyOV2o6~|R57jDL0jj94uhksf;u&x)EhCtKv+Q*7jhA4Lgr}n3?MU824Hg z1wW09xLe1XK<>RX&2v-7IX|F9@8aalYdFW!?d)|39Y_1$&Hc8e_f0OnKQzmjF-CtC zdi$(#JI?9{pE0J5hx)EHrcJ`*d&abhcsyZDn}Sx-V2v*mj{q*(H70SrhXg5W9Qm(S zdVa6;JDCo}x5sbZR$1Y|!hLn(2~0qouYHD(PqLO{Ib)Aqd+&fDImWc6r>4I!GukTcON%@u6HR9IF{AuRwm~+)mlvM&HWr1Bg@z}%;{^B@O$=N4 zxde~lYSAwFJsMom?jNjP9Zf{}F@F))Y9LoRrM z(6CQ0aTLFVzaqq%I2Te{{9S~FL;Cor@`3A#=kW{cC7{4w-ANr$Ny8-h7^ZSDKfu$( zc206KUyH^BUV(_h{=`!R7QBQXsCyeNz2%k!RF;uBaTOBP_tbh5Rtayz{sXWjwK`aq zXKh2sV4(`fgRnG6B^Bppjm8hV`j#?bVOZiCr^R3UJHyWmX@Y>$Ry-4|JN`*-O}WE+ znqg24EFcfU9GmFK=Mg+q0wFrM+j3M`epp4fK&e*g#dIKs~Nf7VN6m94qrEl zXL?VAb;H<=eiu#nNn?R@qbD(Y{r3|<3b|bSL^AYO`qvEp1U%h;#(b5{p9lY6@h43_ zUr(!l`u{-ve?F6<|Gxi$`oEr5fBFAF{eQ;pC0qXY|997q`-OVz-`CUXhsXVQvq7mQ zV71TGeO!#pM)>_srZZiVwFiorn)*G`t;j=DnWtbgm^(MV~V~hV8yko_69d=!< zT+Y{k$J=W7*XysCk32TTnb&r&H1ebOp2G!KTgoo+k}mrdVU=$1Cn9?dKb=JBBDuP-QrcUjO_^nv7*2;wHN3; zJmBHqn5be`fpZq24cJf06(%lrU_Fi4dd%SJDf$lZ$Nhx1rT$*rG9`Ep5AP(N%TfOl zCZ^-L93geB#0)%_XFKVFCpH0Fcw!@F(XPcmz^dBK7(#jXp`XNoVfT0IT(NFXU5Wae zE$=zu7&iKR7Z}1-6Zz(QUS&H9B2`7MIsy^uB(&M|4Q|;jAusfwyc{vA~WNs5bkd0R4sew)N8YLPQ5s1=hW+PTMkIL1V2BCIzEl% zaqT&5w2Jb6JL9w1IJ$Wv-X5*QUAxs-Rm_FQg?mTqXcgYZ-r_{jX4ass^2Ygr-iz!+iXzfT(1W8A%b ziXk~-tsY-t^xi?YVDm<-8#_J3TJk*)Q3!0)=kza-i5N+3%kPQYQn8}!X}qr(4Xh;m z!9e})C#PsDu|pl#M{+DDbU%74YBdFo*~_QkmcN5M-ZuM*V(asD<9dAZ0cQ-6Ty7-~ z1Dz-+`1@uroubeF{&e*VU0l&x()%bv+IhyEGPVownOmp-UlW45gw?*;-$z9+%nW}p z;L&Lc>2Pd7ktwy^_!}x#@)FuTRi~Ld+4^=F^jzPICEs!H8s(2q@|FL3N>Qg?{`i!| ze{hM+jVO_{X#1zINWhh-#t=#DefR^{qX+B?T^7*a{e}$_0bw9Vp)pn8&-$+~2^;T0 zFNy#q4bxF4_EtRl87Q0@H-|ii@XnaMa*FA`6pD3yRC2A7E9{N!{86+QTd)?tqHO_2(nu9e*WFeeX{5u6)HUU&8=LU z(dLO|ZDWgno2Y6;bt#YhdWw#&-M=ZZ308=H&MT2aJN@6qo}o2bgMU2&Z6JMTbP=HP zpupSOmVJ>{J+K-dagz5Ez}*>LARo`_?^z#L;o~!iEr*ZVmdEb}mRpxDS+XR@ByR5} zTy3$lgRoDn%QLQh9MoLD_rE@uRcC$^AD7hr6q|p4mWBDAz&a^p0mv~)V}E5UeukTr zYu9Jtg6+VX%wl9p-(oMY$|be`fy#a&m95S!^r&)Z z{8-WU^YhVmek84eKlh&Qhqm)$`O6e-8wABn+CBuu4T`X51;tFlKAio}D%zeqA8qGH z(xUA-?;)XWTyMFZ56kbT2>ZqPg!Z8t@(ZCY%6n9^f;Sj}UgUk41WXpvwgi5L$h$w$ zw)lU1L-ljsSG{|^E%@Yg zCD2$`0d2c~gLe;LKkMD;-NP0Z6WfR=k4E(-j4X!10EhkCTt;YH*uT+Tv{~#2_JFSJ zL{r8dPF&x)1JkuYXICugiJi!cy;Trt)GOAZ9sX2{jKvlcwlb$3_GT^`{kT}xyly-doUb8Z;XyYFMl#um05grX*2yQ zYeT5v-wMAx+&B>NJ`#sK!UuYo~!N?K^IW@~L-vd+L@GE>1SW zmsom^b=B413+SOA%Yg`QYZ61XYqDJXI5KR~-KIDq?dXph&kPE8T^@dKWay~2&%&}o|Jn!@Mp7yr3_?tmQ*3eM%;5~zP z>Jzu$i%|w}QV3}gUhAoC5lj&fsO~88niH`_Q9tF~0ld9$>rJtv@C-1();p}m5n~Hk zPEz#z?HmWA^)(uZ{$&|B}WXImf%j~sOZAq)ixDKZEXggxBd$cWK$}Vw|v$Q*2xLgq$K6SbGRALz5 zwG5!oPH!V|+dF73vfhVN8c5*C039bE9@ka=uj3el{_OAHLa>|<4*z;}OL2b9>>Chf zs-WlfeLJ|gwuuLVt@AHSkKYG}RNFEmM>lt4XPvfX2Ni8TC134S9j{JIrNqXj?SagN+q2!7U&s|K6a z+m(W!`Y{*7?HO06;diazca4Q#kHT*(GL1{a?|Q-SItxF>P1UwMJRX@onTFq|1;0;O z_?=NGO%VLvS3kUhpY_8x*tFhm5d74Sxf22PGimsJR`9#g!jBi}Xj|q_LZ+fL{3Z*2 zH(B_(CDX%Gkm=?${BF^<-2S=2d(mtMzp8KdAd|5u-GGVa0&m+D zC%tE5d$P?n!@mE}V{`SB-nOeMJvnQ4g9dM#xow1`9?aQJU5m9{Zsw0L7J2lBDFoO+ z-HPqaHdkCE+0J@TUUATS()F?--(xRjqXd6Zz06I+6)f-BtK_rytm}Z@#vBK%{B5}3 zs#pLYW&u^XAxYkYSG0K#x?a^!vH)&XqHe`nZM>~apvEWJL6Pef5J`lG8EdIydc$h` zRT<4Sm)X=_an{@BI%urrN4+6eYUex1QuyacqTWz0pQwmwipqJk= z=vA3PFHaI14b()3UMBUSmui?!uVfN>`PPfxGstYy%g_DNnv#FPaSL_r9zjjJ1wp zgAo|!y+|}ycnR9r%ogC=O#HxUlpr7*v}AAis(_|4fRnm;A3>t6@oAX({B`guDdyc1 z+XI|GCxEvT5ZW3*5`ez>e*7h0wm*m$V>=09Jrh9!)dQihiAe+7thWu;q-_*NzQ|;* zxa^0?__vG(mh(xqK84d-ipAJY@UE9tlSXZ9TRzpW_hx(1CP^-zL|C9M!V7dc1yvV@ zdck@$cd;dwJfI-@=0}j-*i2RMZ6=N+p2YA;oItd`c?n^z8cvvc!z$!58dN#H19BUz z)Irb-7Sd2rF1opf`AjN+&m`KE7)j=nrF{*|0oj?`iU>KwbdmGA8Npw}bmT4yi_#>~ z`!Y)EW*u_~ICIrSLbFL(;;uUUHKYcg;NXSP&5=}+-B3vQw0$}1wPA$-qTGld3Fgzn zP&ez5VAY^Ysv-2DVH|`s_hwTQ2m%oK`aWv+H4t&|3&}=<$jN#qSzdXMnfyA+kRO)R z*hqW=EKObtHV|Je(6aE?*Ptj}BPk-Y2K7#+c;!82G6nGYFgM+xw1P1DN{zCyrRK)F zPc)ZYAn|;%9JsCoXGu*r*CEv~Df0{oE<%FOB%Ya3YB|T;` z1zhDLLuv$;CN%{cQrm=dr31bOi&9Cc!(VU1R(U5o)O*Zi3gGkMNR2Skq{fCuq5B$$ zHjP)qHoREUvfjxCue`@heg#ZE%xyO*O%RwVr-7c!_8>Co4UPD#WCrp1w%?1NUbqyb zm`@gr?H#3WZ^nzcY6QXQ4fPb?cK+25!pxmy7lh1&MHb_A5ng?Qx8AS|?@BYh);sA{ z?=h2Kfi55BiXj3Yjm;;(U=XPVY-tKMh~F*Hmf){X%#7a9E-CBq*K56#cJ&@J`4#Z_ zaLgrPc;W1c2;tncT@JUO8Q>sor+kdH)CkY1y}C(wMGi4V{f zcAyBWh|xzph8Ig*P!v+>Js`HcTRsR?nHF72Y!-`-9?G)l)Us75;cF0t+4CP409y&s zdM8EdJ!bMN*vbdQc6i(6Pe|W|fEtKe-7p5p`oSyS8;tIn^+C4=x51R+HAg=B#M0^w zJxEcyN|QnQ`II6w`|ltD4O_s!jpjv-J9$NX@WCt25*~k3_fUj@z+TjEh^5jS&Issc z{Pl_TC15hevK;TrXJ&$hV+H8L@WT=jU$1z3FnoB{!(SBaGkC%(G8>(UER2#3QD84J zkrjLrr1a2ke1^&iY{6z|14~>b<*qnMz#EV^O{y&F6ZA#49>P~L1@7d9wP~OkR)yd@`%_v3Yp0nGG3kl;*8uNU)X+ znQS>K6m~;CJ}Jo=V$OU*6akZdKBX|FS&&u%q)Dr7Nck(`ASFi<^oFs(OFwCgcpHA@ zNSaURP`$d;sf1}XkaE^TOoWiptj88AQz=JC6RhAbO0rP$%9sqls3f{M25Cx+KC#FY zCV9fAFXsZ0`6RRvj9gm;3FpUPX#8Su!@`0Z% z@fs$7P?`koNVA=Al^;f-Qj9*66p#lbollk!UQEPGno!Aew4nFW_&pv)^@FU2)mc)e zs^m2AlTQ>}Z^{r6Dt`poyAIK*D-4#%}y#>I6J}V!0u3B28-f zWG#aHeRy%&CsPMwv}geX;!MHp#zEEs0aGtTp!efN3D2jD7xI!16eUZG(R~mvc84UL zB|fsC-Y@~g!!5Nh95q=ijfbW6Wm)kHPfZsz1 zX#+t85Ya=HSUMr->4AtPFTRjwZy*w@h>S7<5hyaD8G$9e#)6as&1b02*eufDT)=`* zHWGzrpw1UqLgyj0!rb&Z0kT~&SnLhEJlYS?XPA^S^^{cj%+Un!o>995at0K8JOv>W ziua%{&SKh>)kGA~E9w<{!F!(=R!FiUqSzByWmMEzj8Wq=juBiI3-yMHRAmY{M_x%+ zV6|#^K4m-rYXwz9p*#vpMmC>iFbAh0uo{A+mRT*#CwmsK&&+1&IwMf;4Xle*tOH}$ zS&Wr&TVQp{{RbF$Vu5FrM10ctxt~t46)%BR-oW};#d-i)58YAHc_z)rH}i)Zn<*aN z$sjDH%ea{eM6s;lpJzlc8*RbVf=?KS;ll>7k19tPl$MVB9s=upn`g502S)8PUUU|4 zB$1J#-D*SFPR%BOO+HyRAtMAFiP>fZR>1!ctmmI?7y+0oG~$gt08rAvCffs;auF{V zX`otTfP%klsTIveMLlJ?(URpZOn)es|cQ&Ndo2Y-?bk!xz{Oez&0`6gwhXC5+FJ6dW=@ z+cPTG`;^!LVm}5(1ai#WB<bSQ0PGneu*jS}dxq!(J@+bk$yOCt_~1UkGQkOA9- z0Xn4ZLm*p-qnp&?iE@zCanaET!d=1y;Iu=!AsD83aGcWbPWhRHfRa6wG1^(6QT`so z-&n;eDCsK9a;lO&zQA$Ls3~KN7E=%2djo55{WJc0Lu+CcYepHB9wW5IH9gnS?H+ye z3K)u<5DHUCWjYE9N+Vn8aaHBHF#lfL<62a(w3(|xvf+{IOR~SQ5a&u{ACw21qx{E% z_aJm%G1iZ;%6Rk^(>-w9D@AhdXHbnhJk5=J4SH}0rZoJ)Ej3<*xxl(AA}yU6cQBkR z?$!}g+;scGXKq=TK(aX%M~1>(jo~+LF=uBNgLyc0Gu;jsR@C{(uW_<4IkCTdVsJZF7ynXFhl-nGuOlU|F7T$_ROf$7wlUQqIwtpG z)%oAnchyy#V!6L~xTiF5qRxLZ{Q527_dcsvAd2!>aP>u=g1SC*7!?Trew!7V%GVxPjqS_aPowNC)!aIt)OOK&-_F#h@`IyecM+V8g z9Bn8{ig4I28%IYhVKsUnuW^B{@R>onYoSzylS~gGij-&Z0^~KTkH5&^USKzFF1ZNo zW^`qqr;)=;1juc%h21+4|J$hO8n*}NnNx<=j0c9?Zv_^gRdW6za@cb2gJ1A!@4n=a zdG%(kst;A$F~L}^efUENz|vsp&^nj)eCZJE*)>Xqxh2_nsVl*byi%>M#Ep*^)|KFF zVy^7w9f+5@l0o=5tggg^j~CaK48~bG`G|vzd37a2@o_|5NiIGPuPeC#hvnqsg?One z(eUx|x{^G6ysWO|BBQiGK3`+*p7YRU?mmJTy90;ST= zi^~4bN3awOSr7?{*JvMri>|;H2xBe(ZsjsE?=SlXURwOCiAaloje4w;C1n3IvV`nk zFH6Y&4YGvn-^e9o3r|A;nhlJph{`-!sF#%zwhBL!D~f|%@wh5T_MP}w_q7}i5*Ek5 z8p`iq<1$SC*YWtV9_3h}#lP-G{KE!t^;oYS z8`NW?dTdgUt?IE&JsQ=cSv_{C$8Pm#SC9Sb(V-rP>QS#AtJGt)daU7N@!;eq5RdI_ad0>`hYy=j ze#yECkxMwn;11iiNB(3+-S}Y!*0n@Ec%jT&qPchzRgUK2O|&_hk2g{1XaU|tr=x{< zqguBJkYbe8B|2;Op-yPx>EI$mKAcB$vj|r{)(i?u&A5t|i9DJ3bBL8{$4*e}x21QI zzk4xY+a~;tJ|049DxF6SF=o z#HarG5WE&$1^gJ_1f)Gw+}x03rrazW;DO;Y&1A&%uVyBo;9{}KJS-#eB-eG zmm^L+yR@0DPx(7EHn$;NO~Jv;fGkCnUFrE~+j3Z0bCEmhfk7V%Ac@c7kJWF>l%9=o zFIZy2RX*DvxgYTvvHvX7EJtx+&_^2P;Emw29$H;9(|c%Mz111h&|L&1)BEElxLrrZ z?RsQHacHDEJMKnKo1ch{uFMqrbrUCFtj@>&oculFC;;Eg^ZATFz%z;n<}2qHQ@^L! zzY({u5qHodu(!vM@CZYU;)#ezir@tG@e=^2TFP(9NWcd$8pcWw^zKSI(Gwb$#Qcru zL(vSk_<4YLaTMo!LMOb9)zB}yf6{kV%Xj;?f;Y>kKo|xK=5X;_cv2`hplGfq=piJ+ zxvP3!eV_OSqz4`qZt07*E*gx=RGb3RQVhC6`3n2wpDelav~V1Uh%9_VC&G=nglqj) z_#n?f9(t~v*ylV)DxN_Oj{NtH51{xH$OVYYz@Sl}3@6kJ^vdZx{!uxTK`o|NI$&|m zglugop3X<|^;CR>(4eG-{ol8j!^Z&~r;XCe^Us5@ zVnleb)W`a6^G8YN!sIn+a=EOi(&NYlNB7d?0>LW1>3!Z_!K?Hr?`dD~Qe%cEIkX;! zTJTb|YyM!-o*w)LCWtRB{DSlLMWQ$a7rKEK=sWNf_G z{I{duL#W^GtNE40ZnF1pqJ2~wS=#f(**q_9jn~~x(dgQ+CPv#?XX@wb&CU{3${Vl=iaKIVRBXnP~Q1ru3PCr6&U*qqZ zYZV=Wn1Ha0>pQ8r!99|7;cF?vrJd3`r%gK`<5b9i)QjfA-tuO6dB)bp zvlTuv4x={N3Q2ik}F84xZ$}M@UEYBI9yg4e&?6P5#c#r~2#U^S$-SmghH9 zq~c~?dw5C7yMSg?uU;H5aJ;WFkjda{hBanw@6 zVQ~L^QtTk@XGic-z0DXBZ4JGR!EUo#?_lgCWhW3S`%br>jUe>`0C!n9_9J{8_J}cq zSTs1gsKgz*Bzc4Ny{OdkuP(&Rzd(n6QLFSry358!y292S214T7@U0}#ccnk7hWVI2 z42~$m^|BWyXOQfoGTVRCJB^Vb^@OEhHr_emf6m}_C_Nj#WaGTc{M_U(A)V6PUB2BWEF!*Y~MvKUgZ&+QeLBS1X}OUcu+S7+dDsDV%TDr-uttuZ5}CF{xLEB$8q+Wnb-@It+j&A_bg5qvUm2 zr)~XUd>3i))v%`2RyS_cs@@@G!KS9*n|c+Lze+EyGRp4OOYb(y?$t~0HE{n>=|rP! z3htjY%8K>UVxw%5UOEZ)bSuTR;lm%$_wq8O@NwM5*8{eZ<%*)6n5Nes^+4fZZ^U&U z3jMInmOeEX;U}PiUqRcOyvVa~^5lm-3ocW7mn>0Fq>G&A-vQ4r8#ecZegp1>P@3$h zD(vHgKB>42Wr5Q(;12r|9FT*fQgc z5XFAa2@^#~X8^WvOm_od+0j7&bBnRKT$N8ega)2z4-juK6)z3iPOL)+?uUzIu*8J> zSG34~Q1uS{c0vuV1-%;Ya=sWh;6B-;eW!6!lNOc83H}~q(fthRbq`bvg3YxB3%zLx z=J!4ngs+(H4Sr3(2gbhJp=ywU6`@9OaVVEo><$LEFNb;#k9m3 zjMNdiQ0y+oBh~LH)-hya+I?TVlGwf@DDCPP+@iEZ_>J$sts;3>l01e!NFy(D1(?Ph z#h5>T;S>(WEvKMqW+LbiHoFua1X^!~)RnULOp=0f!1`5@3b1q#<2@g6Jkqx*1w z3T%6}_I%d7n{(U?FEt9s7;upcWVCsx^km-61JEbp8kCF-JL+{@z=9DxVp75Z1>B6! z>h_jC?VSdlNT3;f1--_D>~jYcJz5a_^1N_yHrD3YMc!(RQW3Xq7kNOlW!wfea=Is_ zK=ht+cwILL%PxA*gV<;AM~$gYfG#vo+hv6Pt|?D`KOKl(hPzFSVoMwG3NXd_fS3FQ zn}ur6Xi2a$gY9XRE4kjpRfK0D835}*>;n(!(8E)8qg`pw#hThUg>J!*SD!05|_oaU5n1l21EAGfEACud$5X56iWhL~M%-E=y z@q0Xk8G^sXKba!Q#grO^RS9vl?jVGd7KDGzgmB|$ZdMRf{||eLdcmV1R{1@Rsob(G_0z930 z8_bqaZ-T=yU4V4VZ{RXnH4By7x}zQojhrc#(mR=wrav zNBB4Zrq*B8w95oX?~e7-yZxzo7Z2h;_0hMTM5fY0=9>PHNyVR&{!YmU{DmP={JqD5 zFtQ&AY4!=)%bw|#!2F)0&4N;V zZXOI_uo!>87`%Pn&G%(1dV^Ev-JG30ufFE4Z?VjBp7_dKQ)(xgHr0b&+fjqL##v+|1CgY!6WhWB0Uq>K0Y1^9lpHU1W^?1uBh znX+P>dtc-L;BRyKBGw1d>boFs+;^Egh4!cQs^7RRHz^whQvNCJkF>XP)lq^yxnxJp zFbaR+)o6UFc4XskJ1Ta&)B~PYQ9Clmv=~#I$rwi~h-iBK^v6Wr;#I<>JQTbdL@q)P zszrVUZXteZga1_MO6lZN`T)Q49Y*w2j*r+wHnDxdS5FrY8XbI@ceh#2BlM(67EmyD zgd=%w&R3JaA{vB44)ao@4-)n&s+ySV*ELWw|tr?ATg)O2)KQSJUS6| zoZBb25WyeI;S_FiAKCC@iw$oL$Y>l--efC2;KTkdJ-!}raJeHags42bUJqe&plf?^ z;K|U`6_54CK=Elj?wY^MyZHh4!eeHXOTsPkcNEI*j6Q(hxK6N}{;~}vxvlSfp7`@< zX$Zl6#u%t(m2OivuBP2DhlydQ@qr&UK=Ad?q!l%imulAq-Z?%3TV_p4?&9YcEmj=L zWW`y#siekZY4ZVEpz$62HJ9AilbAjG%eN*ie2NtnH6YqnL-E2^x67Fee zq+KGzxPZUPYLj&@S*)9r$+{10*7X^0ax@F4jXV;=iFXeCb!SYQ6TGO;(BH_J$9~%x z_S-XD&`+5^8E@q2F>K9&QSu)Se!{3YjBCvdtf#owXXCPH|6#hX_!Rn6^7F>+9%Jq# zC~R)gj4>8^L9l~yAiY-H;?Q%dA4`rVji{O3hjdPq5PL+dwt+e4Ua>d(gm-0Q_Jb}# zKDJ_1ba7s!3;v3Y#4XqjWek>2p+6N8%zi4AA6==Vl;5PMMFuS$LGo;Mlod)+F+$pH= z?grea9Ha*UX{^UwZs9x9#`hAzH!%y3kHjI-7YB@P17I)WZl~e!75)_3j)QlMedC}3 zH-8N6pN+?9fE2#Hi5I;oG3w*s!3f8_c+nFoO#Bc=&e9KDtEHIGqecFO^&b)pm@&T~@qKU{cKJpO!uA<=oF`vZJ|=!H zNM5brVZt2waD5l=?K|C+9eOj2eW71;cb|k4lI-fk7Z?%h^}mBB(qDW}Z{jogA0H{4 ze@99G88^@{}cGkh?gG;@&x$oU^oyUQ~L7*TW;S`a^v}@kAyd`FTC3< zeQd+NIKeN57vOhQde#A`IylwJStvOzIct#%RQ_+`@sag^?c-$H2EkIpA*#*H4z6<*i5@WF=4K9+Bw@a<}4aR`D6M7DNr{-hDVZ2p_k7dRT_q#Eu zQE_Mz_BEmlD4G|($rZZZD9ghpBe{VZ^IkJ>rGggyBh`&xFTjwVQ^86v>^WJ1lNah) z+KPO6`V4XTFiECG7DJ`QUL#DTw(LcGranl@>1Ma;;J`4q?C?u7@90V%bi+ZadmBZ( zr)|erI%D^kM=;0ZOqeCVS-A|CBUnQSL$F6ki@D_-qOmehOWQWzjh(#sm%g+jEt0Uaf5e7Dp_- zu;~oaUFbujjTXAH^%(@wTu!b+*m?$z#XA7 zWaK0yOnh;9;%i_J@`^`Ch*HLKViOEIpfGjr|5f_ONlDHVU1wCep}BGzEy5xn3es}jO;5wq?P@c7WuMdKbD^TmW=E@Dd^}TuafND>Djp! zkV59q?7tK;=*^Ci?dZPJkco*TL_L8!Z~V2gqjUMOWba}2oOw$pk<`bSROx=V2~Xtb zojW>u4&uFnf}{ zQmENL9~Sx^dpzlad(>qZsV*6Jd_sljHOTjFEh=-Xjys}7pC&{IM80=v(R=Wl^IGGb z{bNfxtS#m=Vh#olw))WSR1-EP35&KlT{mMeA$Hh*0vB|`mgOX4xkUAEzikh)pkzxI_IhFPxwMh zhxMMmj9iV6A4%E*CzDm#m;0@3`iE8aK03XRtn5`MlU3PHB%Gg*8}OxH%Ca3eAHv2X z=v6a>wi^lOuk2U&(r;yqAo!7#?Qt?$lx^>~vj6YHDx2fL`4Gx(a57nyUD0o4yqUr> z=gxc<#!s}bQ;fV8|1Ky`mSr-tXqQUeRwDb-VlBEfWjS|aevoFyzguiui@!asuox*8 zmPD&CFNwD--F_|dxD?)R74AqYJeOZsSQ4$mUrQA}tVKR6g%4YWyV42|vI|S1RrtA- zjfKHiCRq%APK&==6&6#w3GXnER-$Ebqwf-=W6lRylG3+|*@V#vKm*a-{L!lm5s7K83Z()XAu(Zy2 zNQ?5OAQ)d4>}#6IEtJ$Q7mh1AgI_J$N|62zm%rWc?^ot{zZR{L)OMGD7lkQCdbbu8 zkJ-N~+&o2DX_*EUZ}hi)?R03!i=Z?sF`3nVnEkZUmsC9d?^>~f*#S%1U&Wt6dz6Z6`p=|A=~1B7OUAs9vdiaX&FX%HqAGt67OX!E zzY1F=70*Sc|6^^?BIRItKb0$jZu!51YkSz#@Lrmx@ckJs>rrMI{%+cQE0-Abj_!Bg zQFxIyr#wb1JC(;zk7?e`m}(u!sn!}?zmql4 z^$vXxX6^3g4B7@QVgL_WYHIOs1V*w9G{W-#Wn%sYPRwh|X}xjO+4OgisYIR?+Ea|U z%{tAl82$|oW!8k}WdFvRMifRMVwA(4gbf^`*Q?nt^rRO}(@0;ZJ z1NLuOr}7?JdX63g*8gT=RYU38j^xC7^%yB5J#zwhXKD&~U}&l_P|f%bv^IKSjN*a! zk3BV&p76OZ20cs9@fb3e1+1}Z@7vR)zv?rQocxK}MgE~$Y+M-QdW9cd2@RXErNZ?%gA#f10mr!*9M+rFK zUWGm=gwxuuz2CzaWUbkLgY7dIl;>zOf)lPC^9>||t5a8kN&xn6M8{0L`9u!3L98NP^2MsQb( zv4A^!1{Y1m{1f*kqIutH%)+t`^fq_}ZjyjJ5WQREs5cco3}yHOgp0kzFJlhzO=A#u z^IQx=Ce|mlp$BcyxJ-M#j4Rc+m=HyL$Y2L}~ZLN7v{wmiZQ^thlWdlbG>m0Al#&)_L4 z8xx#^G}|U)YQf%fq`EUqwNBtBX*)w=q>>}{BPEW3(WEkMA6jA#J|llwe)9J~V4m9F z)lxPF_-7@5NR#`NHIwx@7$G)mYa(v9JrS3lXIf^S<943Zet%2{%a*J|IA(Pv`SRiI zz|wxX&llsjrN?D?5kki_ucxS_K;NrH{)$SyjTp?i-P)6lQ2XfJAWDmtBHbt%qeo|x z#uBU!Xb~$I06SQm2eMgCi9HL&0drd9lrP@1Pz9ISw1^MC>apx5Aw+o5p}^-C?l;6z z2D;U2P;_#s8~21>k2KJwe&arn$dyRIo^hU~`wKPqT=1K+g-n*!gM%l}f!0n%o_XAp z1{JEn)*a|HZ-LYz|A`OEj`zDSO@Gj>=V0UaBGKVo(c#p^n=((C(iaFzI#vpjBDR;0 zQ_I+&l4b0%>WniNuuVDbyfeyW5-ejMV+D3GjNc@z3((F3#Wu^Ld#GtpZwarYMZN?o z5G57>Uy4kT#e0U!HidZ5fa`G^}81Gruwu|?@I#_=L7 z(vhe{n)pGB%M-s+wPT60inp3#o)(Kkj#P4-_hn(TDo zkA9Lps`4Z--s7gj)b2sPLhgwHr1YWk#D8%BLUL_t?B-8imW@d)CPdYlCWf*#WMi*$ za;5iBeexn@XNcP=E&LUH?3ac^Rxcku;Y#HRC!HQ8`D-YCul~4Dwj^tjvvBl;7_6$) zJcRpKAB6s~o#47HOBapDxLMrhblL~3ab$dghcpON3v4eas~itopDiDW%jo#*NZ0OL z0ksz_98O?hk<=nZyXA1MFvTxcUVO3yi1yNj8Z3+vhJlF zqwSpbQ*^;eWr;K;WM!oj$erY@jEd6^Iy$;ebx5V>qw z*+0tVXe)k88DuZJp;**05-nv9%1_%6$-x;%T5@(K^(%dO#|ZNTP3jvD(WHK5m_|86 z<9sV~jDu0;=#Ek5D1}j$vOZfEa$k1_8Ev+9xoiuR(Vvh4zT!vdfGz5KD{v zRr~KaZ;Wky$Hj56#Dcx$)Oubz2>KZtg5rw`2=?$+zvK* z4*?tL=A>>r*41&N6y8@4RL7}+{tu@o#c!D3S}(Wqu$_XMa+xZisB$flNgoYj;KyX3 znY!OSg`aA_i2#7ZSa6hV848qF2qn0^rtkhq*&jEPCJ4uMc+^CDz8#kaV*=%@8%?wZ zL+P0^6NlkkeefhD0XZ%EBsf87sCsJ-0)Xv3>HbC|#3Mn!vu^!MUdCy-s_>Z$waC-t zA8!>4G!_N6E%=d%g@ynpusBX5pPZKFM{Keoq2FqFof~qfC&4YaD4|})VwZqQd@SG-+5wBk8*l9 zc&7^8D)bo-b3?>uaDK=sF0)-UD^q0dCRjlF&}FL}-SY{YyBv-yFdT`hXtJSSsgaJ| zD1x01T~pK?CQ*2VC^UOdCpF>QPS<1!1i9dsYPy!;>bR3z%#Pv5aj_x6>!=2Q0DGy; z!QGko93=|{pX9G-=b9CsMppMOd`UhnlPw&F7{W9MC5zQWi2=5>$SwG*>^vo#94Y(> zi{VG)Dds7XA6l4OHBCe^l%G&zk77AB-EQh=I>20u973Zc_RrhfIAnAXTyLmRf?R7Z zOAW!I3=wkEw}uz_KJ1bSRoj-2>Fq)#zIYC$B!4pBH-g2BvwnLXZ@XjZP z>K8O4q#Lo#RHA37M4u#%SkzrFN@Ry?N%8Y3j(OAc2r+HhprA)f@Fn?lx@LU~DT$Y* z?RFWHc$#59u|FTB*9xUtSx;yUAi~IBA)yL!h zf_QaL++P^4ekbl99j`tU_m7GD$HuGQkDrKlCqH5N9Y+ox>4@)+w_>qL=3S4(`70jl zh;>}h^{vTSaesG5Ylr_h|Ga}g&G7-n|1vqNBX;D^uXR1WaQczM$lVryDc%&{d*t=k zx{k!3UifUhwc~2$I+9?X4M#eT9LE2Scc+&kicm|?R%43 z((Q8$J4`{}EMtOESr~VJPk;u$VB9%ce;M&O^Do5>yWOMA{JfXX@+)w7df^rE^6$Ch zZAXp*p@UD)zd<2#0ZusD`gx>7f0=rL^OQpO!_OV*K#ZJv1e;NO0KPcbj>sVvf@%Jz z-hv$}uW*M-G1v~_X_0pzC3ZbaZZW2fW)6S!D_jFYPm$K5w{wl4UzT1+rP9dY;SaeI*15rYD>TCxg0 zukS+fcvk1z$=}3Jzz$BsiQtZSVO|)B;?zR;fi8r&JmxO?!e}kbJ2;PYB(uRPYZC^j zjaTn7-BF>??qvaE+UP!sx#vrSy6!@qiQl5pQyxm6ak2(Wg70Ir0iUu8{E`-jE~P0r z^o8O0jVME}o_bcix56(W?4QSmzrgTcwB6jjmBfBSvIMtDJ18# zT3qB=Sfa8hjd%9b_6vVJU;zF^JW}up81Tm^_{Dzr4P53`OS8Dd|63%BFx#SuJIg)MFwaG7d-ZEE&}G9p9wv zu|JO%=;`?ts=Ylu|BD?*(;A<)j2>ouKo8n5E!62|Kf)SC8bfo_GKH!izKg~a=2IGb z>kjpcG2GBz)DP%%5W*c~c6J72x$MBHWld@|7b;3S}7;o)7+S>Wvj`)Bx zwDz`LzyeKz_Z6q&bG?rlMBKi7AiN06H<8V##3-9+{@eI;Zhbf}!yjaymz`Q<4xPCV za@6@!%vuL8vL+%@=O_^Cgz;IOpTlD&N6aw6 z(OZ$PrEIp+b32}u%zU}ySsnL!;sd6mkAmgwKHP*Z>@Ebia>t9^9rxyTw2C3U3oA?R z_<$_rLV?zfVowLMYotx%=IGGQL4Y)t&F(TRgJYN~(3a_EaB z07u5Z=tc|!N!f zKI0B;O1zlDAIkxvZ=t7^9woTv7~M4Nsf_3E=TOYD|KM@F)UCI~PpE!(SiIX&teSb~ zK<3V?$KTm0{Y_4%9w$pBeTqs~^&~%K>#g;BWUt>BX8yKgfW}L6Rb0*}U7E!-(E;n0?T!^|LQY?i*$Xpz+58dqDcl)f@#*Ya%d*XrJovm?eAn)B5 zuYE&%-v7oXgf;RXi$8GeP<2=Qfj2PDy1i26?+EQS?-?J*ltu^W?!FUieTY)k1j>;B zFdaxB556$bxM!wOIUWmx7462t@^KxQzluHub{e&~vEeaqBkWu@f+=<6E@wQj9i6cS zZ&7*XmM2b8y+8*K_e2J<2)KXS*aY{lGu@b`)*a4`iRQuZ!EYI3%oz)j6v8POZA>dP z=8VR#eEcdfrsW%R3h;|3!}GAifxea6ZH5Hp&3pHmW#{76`)wNI)g5vF{*DSE2x2|g zFJCIUZvUdq7nbRK9i>3NImM)e&50)dW*JrEap>zpVlVAfkH@EZ!U&y+&v+@(;as|2_TS7YiBb? zGK1lA7roNi3cYM*I+hAAoZJHUFQYgq4>{o@_;5vvY4WRFwIkDjk9`SA{q288wQhg41ES*yPWx-l! zqifpuj{DO-c7Ilbe(M|3Z+(^RPRTJ@#b@$%NuR0GmEI>mm)T$aefl?D^>2?mEM2&~ zPyaSL)xZ4~28aFI^LRS_+j!~U%8gkQ!DIAv#bh=6w=qut_SS!9^l#(gN~3?%q6=Z4 zrH@-qM+BxBR(TApGW6{KZ+1E0F$;fB6Jol5FPb%fg8p*cJuvPb+3|V@JK;NL$IA!0 zvUae+zd*advfax&(~A8Fy+U_|1{Y}l&Q z2wsc+Go+|J{uTVSjEEvoDY;E%g(oo_vZ?RY?*FEK;W_kXDK8?UU+BMA_}2-rs)fI! z2yk6ZVyV@ivp-vN}=_>q&WemzI!HD;Y&)SM6l%kjLL@SHa z0V02~g-m`b|K~LN3Gsh^%$L8n|MO;V{|5=|e=PrJLZ<)2_$;b#ncd_DUCLjJyXgYO zOW#M=vZG^2xXB~#5FF>^K&3D26z9&4&#g*4fDQ!|GrkFYqVb6`P@2{+9`c^H`o;0s zM|}-GvKS-z?Bz7je^6i#Fsg3w3o0NdXfjI2 zFy5ycm>qy6WyiG#&z7myz-)qaTr-VX9Fd{pnr?So+)knQbQh6ALgS9qIAUtPaYt$#fpDXDdj42R0q5-WY#i3+}m-VT4(JYYv8F9ii1`?yY9N zct5e%v*Z3%^u<>TsS`0V9Czng{ncIf$ICtFukz4eNe6b|T+xpu{~VP!BEtqctYlBFKZyl|Sk z`WU2xL8WNA{}@FlV-wgE{~Aoc_#=*YUh@>(X8#(!)9U(cuN~KoK@YUYpTu8weP(%0 zj@9ca#+mLKGgpl~c3AyPX8$MGBz9D!iSFY`M^;vQ38)?dG9Q65CFY>6RKJ)8z8~QJ zjp!0*7w4GnVk`#O7+(Y^^XiM@TC|Zp8TymTf;bcbZBbf^wyY6de*DC*bJ29Yt%=vs zD`20*itiwI`nL=9wTm9&~Y_a2ebxl*|fE(DK!XUyA>pJ6GbJJJ3Ju?%dJ&Qv9XPChai>0+a=#R>S{{QM=yE&l%Tdx^Fk# z%Z=>>5zm4n6-R&z?#G!r0fRoQQB`BmhUJQQ_3k()0&u#CqY3{mDj!ao;*o125z{$q zxlWACYpeKNJ6--x!e7x*BIj?$G&~kB;{M%0G%l;ST-RC=uU-L?;!);~N2O#`p!^== z^F6?#l`CwDP!OIdKt8NDqCc059d4VDp!qg7lL~BUxrX+4g_8K=cfx(qqE+}CZ?o*K zbzUOg2A0OF?g#X$2jf-2#9)XS_PJ%M8ZXy+luxjD0(^p(FzWFhn$Viesn_3AZC0Fv z^fudWx7q!6n{DTu$W%|fx=vc|e#N{FGEcbD;jfGTlNxwWP`<;D5??}1G9UGKu&Yq5 z#5yRsz{_mMfpt)_k6~ofRdrp*1NCwLI+Oxj#twk1m4n7bDy#R$tJ}eLGtk~~^Knj$ z-%WjKS0s~i(Al9j^{D&e;@CVyeb^uOw*v-TnZ!5gF8Cv~O7IyRi`4nH-NHW&=ETb% ze;>H3zVB<^({LFQuHdzpZ}^p+JU=g9{wsjC8$Rx}+UY9VGykyuE)vaqEVtn)v+yp# z&;4u2r+dMl=^50R`DzW)GK1URLu&c0|7D|7e(PBan|OEP-|^;87V2@Otu zTIAdJyh%StunyQ$-3~mSC&c>UD?F>ZQ$!!0Knd6#N8^^-RF+)H z{v5j%w8(NoodbNaFEW?e?AsGc59vIJKme!ZZN3hlEyQvK5=@ehk!m zF(CPEj!WcR+;Za{Y(*n}xcgM(k>GAneAiD2e-ttOr~vP0OO&Yms0IhQ($KJW%h~=Y zn+7Z+y=BqMQfK=uHnScTB%Ks?QnG)1g6!?M3}3?!xrZW# z$;bFzBH?Qh?tmdtE=+X0A!)eB&VI+l!}w^ghrfe!x3c7p^Zf2HcZ0Z8+a8TYq!CEr zEd}}lbeJAJN4|zX7^auy)(q=?(iQc~cfD+M@{iU4F*V)@AD=`J2!=0JVv{*^cSMW- zII7^j)QljdMzJFf@Zlh7!NIf=H(HO=PO+E%QuHJN7>*}IYQT3>Is|b@75yR zK-=O!M#fldq-Ke?jHd16Hh>+dQ2{HlADU+&a*TiT=-J%7i31S`K9Gw&M0!bXaE#s7 zxy2WusAsvk1_E_K)l?GII%rQ2kPcz~vEz6w(W}VVFHBytIon!=?aofa0$B8wL?Yan z$0`;eu%H&Ub#F-4-&_bE!LIoFsb6WF>5S${*;U{ zZX@4?{WC50C*ESS^}{%5(TRM8&`|OR^Ds>|I*`kLuT)DV@Swzgn*8$ z<+3jJ(}8p3_HI0RSgjOBdUVa;7ikldGw0P~7`&S6X*So5sm@UaI|=MdB|5!WDhcg0 zR_{H%w3#^QJu>W1!DF3HleR%ZIPA}ybTQ3Sd;N3};lA-{&PP=Dqh$Sy!2euEl@9Ew zoWK^9O6I5Vm^#)@HBzsD)hW=c(D<*VUM1L9g1sqd&MA&mc_#_w4_N)zg&;n?+>N zVz@}y(tjIrx{BDAAwxxMJ~1$wD57IQ=x%I>QahkXm-7{=V!?rWvCO`Ryqta(9v_2D z_C0b3rH}qIpeFtR-eBLLzsrl;NxL9j+zGEo8_6-+5m+=(m{y^p>8p_+x1A4HsNsAk%Ht;cvtUsVM6UDi!I4~=&!(+E&Micleu)5s@_u5ai`HD za2UC5K2hn@_=Hmk(LZr;8`>KF0Dhv4%?JxCV*nDrCVTQYLB)1*V)1{D>msS3+p?hr%kv!Q3BSY zKjr9k6F0cwR0fmI-&(au6Frs&fzWTGrRr1xAY3EJr*0@4lOV+@NjO<-i9pI}f{Ng4 zP(l=3aL4y98|j3^C$7y+e4RX@BpsVY`6tixXVGZMM6F@Dq)wXZUajUilx8APN;^lW ztWhenV=_}oi9eCXKIq(?iH=#jDb3{R+l6(!^atS#q|)=WC+71J@jsZ(BNyZ zGmz&%N8%~{M5SdyunVZ%8psnz+u@uxp2mDVHiZetFa$sVRFFv)pfyQ0X$t?Fy)%!u zx!V8#F~lMINF|97l}a*IGL#HmY0{viNh(rEoMS3EIeXiV<8+0hK}mDeZItmO3F%fU zl|-S*hXzT7!teclt^!maJ3u|5# zQLORcAEsNdhSvnTH9;zzU*$iE=X0qEBPQ8}FHy2q@|BWH_W6w%AtCw4k?Fl-R93k2 zV7h15o3oh1NDClowqI&Tl2RAa@S>MWJWk7%8O7c3+*}8`xF|NDEA;2-h>$U(-STgk}PdYu)`6Au4cruHw~!Mdv-7^iueXF$qHL4zzq zoc3H=J@`+)r4<*_SFkFg7wd%ecpAKsZ3Y)oeZE#fuVUfK9GNBEI6&2e!n2inft=rs z%!Vq~3|zF#fBF}_u2s`_4V<>w)AvtQxz9vO+^)<;#miX)N>;hWO?e~qG~vzYibF83 z&_0re*q6M#<6Im5APT>F*o%#1hn5dFsr1{FZw3uwvzq@p&lokg&Q}_v4M?JWb4fL_ zSLcQC--_r(;7wT1h`0R1x%f(YpY_6`>8ojYZEZ^LCnISf_t`R*w3!`W(GThBZ)vs1 z8)=F$PZ!aZQ#56btKxgPtD^WSjAx^I(Q+?VNIs-DRPp+l4|7Wx1^dr^XfhMC(jHmz1VYH3wFqxr7H48~AMh8=7`6 zIwWlCSYGvq(zFQ^(vg{7AX5PHNH zPFY|*Gh@Hrk!NB4)eHKi5?%#z-rRg2T?`3wka`bZ@s)JmoOd}5{K`eA6(3pjJN6*Or3!1eZ;cuikha2cL80Y0)Y!BV$5WE*GM*GR4ALwruq<}(+G~VH@X32_~K}z34lM{5u#V~UTY=AMqk)y9Z&_KK}7Y7}Mx0i3^#V;cbis{h33SE{WOV_4$ltMIp z#aShq2H8|?)zF3ktTh^q9aVwXEU2Mw#HV9N+b~!HT~Fbqx9B`_l#@qt*SI`Jk6K@n zi{}y!g@5Ua^j3-vzKJi!FI?vI)FrsCjhiWWvZmj8qhO*1D1{OWSYk}gN?(52JT**5 zn#|B9=#rtV=uo_5N7<|aZ5rBi{r0Mo z>348aqnqWqVS8^y$%j)%UcPo7V=8sn%Xo382d>|CGj-K5m+tF6qPS_^ z&GFx`j>)@#Y@oWN-3YltrOV`OLCtM_kvdgt!bMPuI;5Axs5)4JbjjUXqPlMOn`#no zGo`w8fsefC)7$iEUfx8n2IC(d$^VSRp<-D|(o2-p zuZzwYL2GBzVB?rjQ>1?i*Ck1-@s_Fk8u~FfzmOA@6s;=iyxJecMWoCkY_z-P7C~x% zCogks@>PyBDMEXA2Lbx@Ib+d0s_$25%y&fQS1Gyrd%8Td{^~Ws<=2S_0SV5$!`F9S zvpxtdtqr;0GySF{em<10y|$Z8Lv84RbGy**n5y?c(RHMpf40e)J8X0tDw*3~eO#|T zda92u>f?0v(L{aJRUe0|k6iV!_fE9(lloYrKHgFvuc(ii>Z3?~j8Y$i)W;3#<8t+J zuKH-FK2BC2b<{_$`uKAc`t#MtTJ^C)eau%M&!~?_)W_ZGBc?uXS0A^kkG%D0?0WTA z5A`uX6<(%3J{@%iTdAA+ZsbT*I9vU7y839YK2A^{k5xm3T=ntH4XALq`mU<_`27wP zey2V@Rv)G6;~Dkwu===HeT-Khcc_m+>f?I#ajyC}Lw%g6J{qcz!_-Hv`q*Skd)W=%&@saviqCQ?$A2Zd*Wc4vteLS=tJsfi|OfWz-Jw|;u zTz%ZEKCV?C7pRXj)yJvo;|5i)j{2*D`q-r`f3@mihx%)?`uJFVEKwgXsE?OUgGNuP zzaCK^{Z-*K_1E3%<1Y0vM>Uf?O%(NTSzral_0kJ{>^@J}`Y&&z83 zd2?tjP48@MH9Kc$)n*?St&-PP(Cf8mD86zJJ&c2WLcuQe9IuynmWFJ>uCl>C)hp!p zZWs7vg1_@mRs4LK9CNrms>uD(bt=@;zXz6g57*)KIo`uPaXud{D89Vn`NiaYCeG*O zKXE?4`=wx4nZY%<@1Z$EuPNBYzl)uBU(g@V+RU{AM=QdSs81B;A9?<`1{D)Wt)QIS zE645Kr`n_IAe8W^U%pqsu!nBPiyw`4eSQTl+9{*jIZDapjs1``=eE>7p3Z?)dpx7D zeADPpLcKASm7oDP^zNYl_;&FA0&f4Ty>0dT$`e63ugOVd+E1Z=*iTRHH}u2Z$}av_ z;LCVp*d8onAlKMhN{@?FG1&|K(%r#iNw$fWzd0HR`}vqA7P1eoU|F9P^e2h##aE&} z9XSYz(XN*mNs&N~*8Pt;TDAK*xH5xl zYdb%Sa`8*79Rc&{5`ucqr{9$RgnxqfjK;eveP^K^lDD<``x5;(KlnlKnJh;iu_czf0VfvR}^ zA&U3neW}8HPxR`c>gNqqcJvY_!-1i_daHJ3fh#k3thN`+XT)XvW~Kjw#KBG~jy>4h zh?B&8B|q)`ApaU#EhzcXfyf`@UxqU6vljO78z^~c{L?=;9&V=g4Ev0E3Eb9)Rs81% z*Q!d)4 zd~`De59O_8>l^P%e^WnS5pRvF52=944@5g8XLI~L>{9kI<@YgV1Nrbh**}-^3+wR+ z=NZF(TNL*Zx+VLqR28(n$JWB%Vy`&=)(rLgIjx4(cn$0`1Nm3%Q@9!B^XV=L#BWXT z+&=yh=EKTpPx49pS^A#Z|CScaWKPOws{OIkAcxo~UM-MA%e@KThwZjj?aV_l_1lBm zF~--}naZv$P)z>Iv*jv$i3A`y_%RKd59T)_J=&J#ysFC2hJ4iSd?l#ZE9mzRRqT!F zYRf&gmXfPJ6)eg0uW zYX43;G({XwqIQgY7jLTa^fmaN=B+WxKL@fr=KA0PrROh-n}4v1ce-hX3aI^~=)>a2 ztMUrqIliH%m*9)Yru}*=ZU{KC_tE%AD1VOnzLYLc(2)1W4954NU0SQ}AH(>d`fP6= z|NHE5I;OqSs|BjlIN&%m#z|o<)lPlnIgB{6pDD!8!Oox=2ELzjU}4-$DPJe;9q|Z{fmS6`(!_C`c!b0->J^UUxHk2t2JpN{pg-!7Ufby(us7Ead0b#5x!Jzde%?|ZBQ(Fn zUxFR{Gq}EA#*0^1 zZ;ZnG;qllP9iZ_k7w_NZk|OeZ+iL%I)08$jg{}F0`NK9Am*wiTsjEGe3)PPhPaO81 zly{@@;X^m?!k{F@o{QJz<|%R5x##r)P- zVe4EkF_eTTE=%1^K{vTtkHppbi@E4Wzi%kMX{gnHUO&3qKDmAF#>b1Rlzb1}MbEPp zRvE>eSNX935>YzgtMeoj%U0B(rWV@YOco3j`?!!RHXdnp`BR~(5^rq$SE)*2>jS*R zQ>aVotb;lgyu`h_WJjQ&${&eQy1d#nwr>T!KEF|yx1@52q4u|3NqUO&!uM?b7e|F@ zgckVn&=K@6trltCBW4PI3=94zWr;x73NhC8SJIWx$>WoZg#E2yq&KM|g^HJW6)lj) z-JnNBgcaq#nYuj2dQ>i?TpxRubO`h;95TW(YRWkAui%DM{UVApuW&X+ST=sF7gqdo zzbbx5b5C(uHXtv44FlRj9?J$?AT0k>Z%P)%{wG+Bm>y%i(vy=+rFfhp_ao z;wnn{y{K{uRUzeP97FF?>%iBwf*|HfW)Sl}s<3rsc*9E^hMvhuj~h<%5_@TpBjb4$ z{KW4i+r(?Ea0uH&>^SPke~So;_n`IH&qkaR=&j(#MU1^|z< zH-@2~>k`I5s6uXGlbeQ~k9Ig%Mh>VuY4qB4ptE zv*R~XnPL2QRkosPR9(eH7t}%+-G`XyJKXB{4;4m1$Nb?Rh8`(DRn$Gsg&jzSUunLA z9ad=>kZ5HPn644p z1LpW$&`6EIv%GJC{D(eZ<il|uE}ww%O1k`qok2UZZJytKf%aEZxsi`s zlF>O%?i^wn|K(}zcwh!f^ZAlLjan#d-6)WEyl!uY-Om7BKJ7u)BNsU3(Vn_|F3QWJ z+)Lp8ld}9A%{r_6MBrS$4dn-D{i^DA%0Gnmb@{Kf-hqC#Q69XX*-Na~uEn&H z3o8DT6!Hr9+?ri#N>D}Qpb=<+WL^Q;D)Kt^MqRd9%cbW3Vblf3TMqr6Ojh|Xlx`=b zOt0|KEGzo7z@#w>|9Oee=`XlD_hB}-n%=#{QWWDku7>y`_`M7-@r16>iOP-fauEr^ z@}EL^B^&vBLkvn;m+A(4prc%@|Gh*zT|5ZI61T_b@+o$CRb9RU<+9%Tl@|U`@J}i? zES>ri+Cf&nn5ue(OO(A6DxlR&%@c?*6JQ?6#ZuWHO!YA-+@J-j`3MUX zyk{yttG_i)#?!+#G)_2Hsm+EQkGzEo)(ZV8Inh#)s*tHKPG0bGC36b z4g;;eJ{^I@Mv&9Ew!bv2G9J-2NSW#E3C48Ei|gdRc;BMdR(H_9jM>5-U>Nw<5`o#` zB2=Pr(mpT=&6Dly>hD?m0L&BhP`y$R$klZlSD_6!>K{DE1>=3=088gB%58zp!F+cF ztNu3atitiJfc`@G#HiV@_;D0te9XWXdaYE9FE~#vM7bVNL!^8)l`9MNq%y-oGsl>z z5h}JrE$A{4Dd_B5;PPyPZV%lQsrven?ar(onYbZ;v zqkD*MaW#5UdltPOO!U;>Zo{_{H|=zJ?U^jR$ajn`Z)KNP)#cq#F6+Tx`CeH*fVhdu z=>_)s;9}i(ruBLI>%UJZy@Fm7Ej&{-9xDEGAZlykgc5VaaxU?ULDba5Ue~f3tH#~{ z`N4LaqcpIZs?SZe2O%ddM_B}h>`@>@rP&1#hjj@uv;QZ(Ly+F#uX`C)0V)w7#99y{ z#~{^`8k8*edtfm!lEh%WKFAgS5Q#Y7}eX0^=;M%mOq0WFC} zAB>5(AQyya^tMItgynxrPrn#r*UmjS@L38%EbG{E%>*HG{H!zz<`a0Q0EAe@(aSZq z92=}QV7Im)8fn`HtAfD8NEx+2NYHK^9f^URTUlEP+G@(fK^xexegZ;t*{vpdlXwM$ z#DE_qCV>zSJEr?VAVkvYDq4dWXuy8C0EAe*vPG~seq#_ayy_?-usU?9Y|HWcALcL* z@)Oej#w`fA$;x1A{dOn?oWWobr2@%+N#Ut7F*j*0_X-Iw=_rDpZ3zlxuLB_lOD$-t z{S&m({t3F)XF=0>WiE(xUb)Y1<5(+>c@n3Sk$UXD0d`YIxrRV|b7=9x_$Pj*C9nx|;6l@6D%Rz{N z--;5afRF)dn*+(_5D=2|983D2>ERV4>Ddb*wDCR&(d7hnMr;nQ=RnYlZPcW7ltmz6 z6@!p)Iz$nqENfezKKyu`8noR2$CKHLYyDy67&M190&=Zx1y46 zr1uAw&`s!pE((Pu@uAfQh|}yg zHmO`=%hx3SP7klg!%kcfBW%+4TWY?hy3uEfzs0}x^lhc0D6h&}GL z<{IeobNg!m*FIR1tBk4d85;=>agulVc z3a?oN#>sLJ5;-qf1T6Lh2pQ2cEdpDNLc5LBX|yMS`;kUZRCu;&^v^F@SqV?aK4vWl zF{vGwNMCb7$WSY`5*&v7eIUeOjx?GFLehp~Yke^Y3Bsw-UYgp3V|Q>62noV7t=X+U z2lDq{usy_ar&t7!e19bf(Z%UF>>nN_FpcA6L!h#{KUm?Z94EN4v)>V7h&k>nHOCWT zus#TJEk7+8URVhd3N+feo^=+h47AP>(9|a,!|EuI6I-ZM}rU#2mWJ0i9ia@0U-mVQ-5;^A#Z)L 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